All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/2] i2c: core: Add mux root adapter operations
@ 2022-05-26 20:53 Eddie James
  2022-05-26 20:53 ` [PATCH 1/2] " Eddie James
  2022-05-26 20:53 ` [PATCH 2/2] iio: si7020: Lock root adapter to wait for reset Eddie James
  0 siblings, 2 replies; 6+ messages in thread
From: Eddie James @ 2022-05-26 20:53 UTC (permalink / raw)
  To: linux-i2c
  Cc: linux-iio, linux-kernel, wsa, peda, jic23, lars, eajames, miltonm, joel

Some I2C clients need the ability to control the root I2C bus even if the
endpoint device is behind a mux. For example, a driver for a chip that
can't handle any I2C traffic on the bus while coming out of reset
(including an I2C-driven mux switching channels) may need to lock the root
bus with the mux selection fixed for the entire time the device is in
reset.
The SI7020 is such a device. This series adds the ability to the I2C core
to lock the root adapter of a client and fix the mux channel selection
until unlocked. The patch to the SI7020 driver then uses the new functions
to do just that and make sure the chip reset is safe.
I included the IIO patch for context, I can split and resubmit to iio list
only if necessary.
Thanks to Milton for the general idea here.

Eddie James (2):
  i2c: core: Add mux root adapter operations
  iio: si7020: Lock root adapter to wait for reset

 drivers/i2c/i2c-core-base.c   | 38 ++++++++++++++++++++++++++
 drivers/i2c/i2c-mux.c         | 50 +++++++++++++++++++++++++++++++++++
 drivers/iio/humidity/si7020.c | 16 +++++++++--
 include/linux/i2c.h           | 42 +++++++++++++++++++++++++++++
 4 files changed, 144 insertions(+), 2 deletions(-)

-- 
2.27.0


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 1/2] i2c: core: Add mux root adapter operations
  2022-05-26 20:53 [PATCH 0/2] i2c: core: Add mux root adapter operations Eddie James
@ 2022-05-26 20:53 ` Eddie James
  2022-05-27 16:50   ` kernel test robot
  2022-06-03  8:01   ` Joel Stanley
  2022-05-26 20:53 ` [PATCH 2/2] iio: si7020: Lock root adapter to wait for reset Eddie James
  1 sibling, 2 replies; 6+ messages in thread
From: Eddie James @ 2022-05-26 20:53 UTC (permalink / raw)
  To: linux-i2c
  Cc: linux-iio, linux-kernel, wsa, peda, jic23, lars, eajames, miltonm, joel

Some I2C clients need the ability to control the root I2C bus even if the
endpoint device is behind a mux. For example, a driver for a chip that
can't handle any I2C traffic on the bus while coming out of reset
(including an I2C-driven mux switching channels) may need to lock the root
bus with the mux selection fixed for the entire time the device is in
reset.
For this purpose, add a new structure containing two function pointers to
the adapter structure. These functions pointers should be defined for
every adapter. The lock_select operation, for a mux adapter, locks the
parent adpaters up to the root and selects the adapter's channel. The
unlock_deselect operation deselects the mux channel and unlocks all the
adapters. For a non-mux adapter, the operations lock and unlock the
adapters up to the root. This scheme should work with multiple levels of
muxes and regular adapters in between.

Signed-off-by: Eddie James <eajames@linux.ibm.com>
---
 drivers/i2c/i2c-core-base.c | 38 ++++++++++++++++++++++++++++
 drivers/i2c/i2c-mux.c       | 50 +++++++++++++++++++++++++++++++++++++
 include/linux/i2c.h         | 42 +++++++++++++++++++++++++++++++
 3 files changed, 130 insertions(+)

diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c
index d43db2c3876e..e2c365348e1f 100644
--- a/drivers/i2c/i2c-core-base.c
+++ b/drivers/i2c/i2c-core-base.c
@@ -1357,6 +1357,41 @@ static const struct i2c_lock_operations i2c_adapter_lock_ops = {
 	.unlock_bus =  i2c_adapter_unlock_bus,
 };
 
+/*
+ * For a non-mux adapter, the lock_select operation locks the chain of
+ * adapters upwards, returning the root. If there's a mux above this adapter
+ * somehow, it should also get locked and the desired channel selected.
+ */
+static struct i2c_adapter *i2c_adapter_lock_select(struct i2c_adapter *adapter)
+{
+	struct i2c_adapter *ret = adapter;
+	struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter);
+
+	if (parent) {
+		ret = parent->mux_root_ops->lock_select(parent);
+		if (IS_ERR(ret))
+			return ret;
+	}
+
+	adapter->lock_ops->lock_bus(adapter, I2C_LOCK_ROOT_ADAPTER);
+	return ret;
+}
+
+static void i2c_adapter_unlock_deselect(struct i2c_adapter *adapter)
+{
+	struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter);
+
+	adapter->lock_ops->unlock_bus(adapter, I2C_LOCK_ROOT_ADAPTER);
+
+	if (parent)
+		parent->mux_root_ops->unlock_deselect(parent);
+}
+
+static const struct i2c_mux_root_operations i2c_adapter_mux_root_ops = {
+	.lock_select = i2c_adapter_lock_select,
+	.unlock_deselect = i2c_adapter_unlock_deselect,
+};
+
 static void i2c_host_notify_irq_teardown(struct i2c_adapter *adap)
 {
 	struct irq_domain *domain = adap->host_notify_domain;
@@ -1452,6 +1487,9 @@ static int i2c_register_adapter(struct i2c_adapter *adap)
 	if (!adap->lock_ops)
 		adap->lock_ops = &i2c_adapter_lock_ops;
 
+	if (!adap->mux_root_ops)
+		adap->mux_root_ops = &i2c_adapter_mux_root_ops;
+
 	adap->locked_flags = 0;
 	rt_mutex_init(&adap->bus_lock);
 	rt_mutex_init(&adap->mux_lock);
diff --git a/drivers/i2c/i2c-mux.c b/drivers/i2c/i2c-mux.c
index 774507b54b57..c7db770e4198 100644
--- a/drivers/i2c/i2c-mux.c
+++ b/drivers/i2c/i2c-mux.c
@@ -210,6 +210,49 @@ static void i2c_parent_unlock_bus(struct i2c_adapter *adapter,
 	rt_mutex_unlock(&parent->mux_lock);
 }
 
+/*
+ * For a mux adapter, the lock_select operation first locks just like the
+ * lock_bus operation. Then it selects the channel for this adapter and
+ * returns the root adapter. If there is another mux above this one, calling
+ * the parent lock_select should ensure that the channel is correctly
+ * selected.
+ */
+static struct i2c_adapter *i2c_mux_lock_select(struct i2c_adapter *adapter)
+{
+	int ret;
+	struct i2c_mux_priv *priv = adapter->algo_data;
+	struct i2c_mux_core *muxc = priv->muxc;
+	struct i2c_adapter *parent = muxc->parent;
+
+	rt_mutex_lock_nested(&parent->mux_lock, i2c_adapter_depth(adapter));
+
+	adapter = parent->mux_root_ops->lock_select(parent);
+	if (IS_ERR(adapter))
+		return adapter;
+
+	ret = muxc->select(muxc, priv->chan_id);
+	if (ret < 0) {
+		parent->mux_root_ops->unlock_deselect(parent);
+		rt_mutex_unlock(&parent->mux_lock);
+		return ERR_PTR(ret);
+	}
+
+	return adapter;
+}
+
+static void i2c_mux_unlock_deselect(struct i2c_adapter *adapter)
+{
+	struct i2c_mux_priv *priv = adapter->algo_data;
+	struct i2c_mux_core *muxc = priv->muxc;
+	struct i2c_adapter *parent = muxc->parent;
+
+	if (muxc->deselect)
+		muxc->deselect(muxc, priv->chan_id);
+
+	parent->mux_root_ops->unlock_deselect(parent);
+	rt_mutex_unlock(&parent->mux_lock);
+}
+
 struct i2c_adapter *i2c_root_adapter(struct device *dev)
 {
 	struct device *i2c;
@@ -279,6 +322,11 @@ static const struct i2c_lock_operations i2c_parent_lock_ops = {
 	.unlock_bus =  i2c_parent_unlock_bus,
 };
 
+static const struct i2c_mux_root_operations i2c_mux_root_ops = {
+	.lock_select = i2c_mux_lock_select,
+	.unlock_deselect = i2c_mux_unlock_deselect,
+};
+
 int i2c_mux_add_adapter(struct i2c_mux_core *muxc,
 			u32 force_nr, u32 chan_id,
 			unsigned int class)
@@ -339,6 +387,8 @@ int i2c_mux_add_adapter(struct i2c_mux_core *muxc,
 	else
 		priv->adap.lock_ops = &i2c_parent_lock_ops;
 
+	priv->adap.mux_root_ops = &i2c_mux_root_ops;
+
 	/* Sanity check on class */
 	if (i2c_mux_parent_classes(parent) & class)
 		dev_err(&parent->dev,
diff --git a/include/linux/i2c.h b/include/linux/i2c.h
index fbda5ada2afc..a3596f61b417 100644
--- a/include/linux/i2c.h
+++ b/include/linux/i2c.h
@@ -583,6 +583,26 @@ struct i2c_lock_operations {
 	void (*unlock_bus)(struct i2c_adapter *adapter, unsigned int flags);
 };
 
+/**
+ * struct i2c_mux_root_operations - represent operations to lock and select
+ * the adapter's mux channel (if a mux is present)
+ * @lock_select: Get exclusive access to the root I2C bus adapter with the
+ *   correct mux channel selected for the adapter
+ * @unlock_deslect: Release exclusive access to the root I2C bus adapter and
+ *   deselect the mux channel for the adapter
+ *
+ * Some I2C clients need the ability to control the root I2C bus even if the
+ * endpoint device is behind a mux. For example, a driver for a chip that
+ * can't handle any I2C traffic on the bus while coming out of reset (including
+ * an I2C-driven mux switching channels) may need to lock the root bus with
+ * the mux selection fixed for the entire time the device is in reset.
+ * These operations are for such a purpose.
+ */
+struct i2c_mux_root_operations {
+	struct i2c_adapter *(*lock_select)(struct i2c_adapter *adapter);
+	void (*unlock_deselect)(struct i2c_adapter *adapter);
+};
+
 /**
  * struct i2c_timings - I2C timing information
  * @bus_freq_hz: the bus frequency in Hz
@@ -725,6 +745,7 @@ struct i2c_adapter {
 
 	/* data fields that are valid for all devices	*/
 	const struct i2c_lock_operations *lock_ops;
+	const struct i2c_mux_root_operations *mux_root_ops;
 	struct rt_mutex bus_lock;
 	struct rt_mutex mux_lock;
 
@@ -817,6 +838,27 @@ i2c_unlock_bus(struct i2c_adapter *adapter, unsigned int flags)
 	adapter->lock_ops->unlock_bus(adapter, flags);
 }
 
+/**
+ * i2c_lock_select_bus - Get exclusive access to the root I2C bus with the
+ *   target's mux channel (if a mux is present) selected.
+ * @adapter: Target I2C bus
+ *
+ * Return the root I2C bus if mux selection succeeds, an ERR_PTR otherwise
+ */
+static inline struct i2c_adapter *i2c_lock_select_bus(struct i2c_adapter *adapter)
+{
+	return adapter->mux_root_ops->lock_select(adapter);
+}
+
+/**
+ * i2c_unlock_deslect_bus - Release exclusive access to the root I2C bus
+ * @adapter: Target I2C bus
+ */
+static inline void i2c_unlock_deselect_bus(struct i2c_adapter *adapter)
+{
+	adapter->mux_root_ops->unlock_deselect(adapter);
+}
+
 /**
  * i2c_mark_adapter_suspended - Report suspended state of the adapter to the core
  * @adap: Adapter to mark as suspended
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 2/2] iio: si7020: Lock root adapter to wait for reset
  2022-05-26 20:53 [PATCH 0/2] i2c: core: Add mux root adapter operations Eddie James
  2022-05-26 20:53 ` [PATCH 1/2] " Eddie James
@ 2022-05-26 20:53 ` Eddie James
  1 sibling, 0 replies; 6+ messages in thread
From: Eddie James @ 2022-05-26 20:53 UTC (permalink / raw)
  To: linux-i2c
  Cc: linux-iio, linux-kernel, wsa, peda, jic23, lars, eajames, miltonm, joel

Use the new mux root operations to lock the root adapter while waiting for
the reset to complete. I2C commands issued after the SI7020 is starting up
or after reset can potentially upset the startup sequence. Therefore, the
host needs to wait for the startup sequence to finish before issuing
further I2C commands.

Signed-off-by: Eddie James <eajames@linux.ibm.com>
---
 drivers/iio/humidity/si7020.c | 16 ++++++++++++++--
 1 file changed, 14 insertions(+), 2 deletions(-)

diff --git a/drivers/iio/humidity/si7020.c b/drivers/iio/humidity/si7020.c
index ab6537f136ba..76ca7863f35b 100644
--- a/drivers/iio/humidity/si7020.c
+++ b/drivers/iio/humidity/si7020.c
@@ -106,6 +106,7 @@ static const struct iio_info si7020_info = {
 static int si7020_probe(struct i2c_client *client,
 			const struct i2c_device_id *id)
 {
+	struct i2c_adapter *root;
 	struct iio_dev *indio_dev;
 	struct i2c_client **data;
 	int ret;
@@ -115,13 +116,24 @@ static int si7020_probe(struct i2c_client *client,
 				     I2C_FUNC_SMBUS_READ_WORD_DATA))
 		return -EOPNOTSUPP;
 
+	root = i2c_lock_select_bus(client->adapter);
+	if (IS_ERR(root))
+		return PTR_ERR(root);
+
 	/* Reset device, loads default settings. */
-	ret = i2c_smbus_write_byte(client, SI7020CMD_RESET);
-	if (ret < 0)
+	ret = __i2c_smbus_xfer(root, client->addr, client->flags,
+			       I2C_SMBUS_WRITE, SI7020CMD_RESET,
+			       I2C_SMBUS_BYTE, NULL);
+	if (ret < 0) {
+		i2c_unlock_deselect_bus(client->adapter);
 		return ret;
+	}
+
 	/* Wait the maximum power-up time after software reset. */
 	msleep(15);
 
+	i2c_unlock_deselect_bus(client->adapter);
+
 	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
 	if (!indio_dev)
 		return -ENOMEM;
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH 1/2] i2c: core: Add mux root adapter operations
  2022-05-26 20:53 ` [PATCH 1/2] " Eddie James
@ 2022-05-27 16:50   ` kernel test robot
  2022-06-03  8:01   ` Joel Stanley
  1 sibling, 0 replies; 6+ messages in thread
From: kernel test robot @ 2022-05-27 16:50 UTC (permalink / raw)
  To: Eddie James, linux-i2c
  Cc: kbuild-all, linux-iio, linux-kernel, wsa, peda, jic23, lars,
	eajames, miltonm, joel

Hi Eddie,

I love your patch! Perhaps something to improve:

[auto build test WARNING on wsa/i2c/for-next]
[also build test WARNING on jic23-iio/togreg linux/master linus/master v5.18 next-20220527]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/intel-lab-lkp/linux/commits/Eddie-James/i2c-core-Add-mux-root-adapter-operations/20220527-050852
base:   https://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux.git i2c/for-next
reproduce: make htmldocs

If you fix the issue, kindly add following tag where applicable
Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

>> include/linux/i2c.h:858: warning: expecting prototype for i2c_unlock_deslect_bus(). Prototype was for i2c_unlock_deselect_bus() instead

vim +858 include/linux/i2c.h

   852	
   853	/**
   854	 * i2c_unlock_deslect_bus - Release exclusive access to the root I2C bus
   855	 * @adapter: Target I2C bus
   856	 */
   857	static inline void i2c_unlock_deselect_bus(struct i2c_adapter *adapter)
 > 858	{
   859		adapter->mux_root_ops->unlock_deselect(adapter);
   860	}
   861	

-- 
0-DAY CI Kernel Test Service
https://01.org/lkp

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH 1/2] i2c: core: Add mux root adapter operations
  2022-05-26 20:53 ` [PATCH 1/2] " Eddie James
  2022-05-27 16:50   ` kernel test robot
@ 2022-06-03  8:01   ` Joel Stanley
  2022-09-06 20:10     ` Eddie James
  1 sibling, 1 reply; 6+ messages in thread
From: Joel Stanley @ 2022-06-03  8:01 UTC (permalink / raw)
  To: Eddie James
  Cc: open list:I2C SUBSYSTEM HOST DRIVERS, linux-iio,
	Linux Kernel Mailing List, Wolfram Sang, Peter Rosin,
	Jonathan Cameron, Lars-Peter Clausen, Milton Miller II

On Thu, 26 May 2022 at 20:53, Eddie James <eajames@linux.ibm.com> wrote:
>
> Some I2C clients need the ability to control the root I2C bus even if the
> endpoint device is behind a mux. For example, a driver for a chip that
> can't handle any I2C traffic on the bus while coming out of reset
> (including an I2C-driven mux switching channels) may need to lock the root
> bus with the mux selection fixed for the entire time the device is in
> reset.
> For this purpose, add a new structure containing two function pointers to
> the adapter structure. These functions pointers should be defined for
> every adapter. The lock_select operation, for a mux adapter, locks the
> parent adpaters up to the root and selects the adapter's channel. The
> unlock_deselect operation deselects the mux channel and unlocks all the
> adapters. For a non-mux adapter, the operations lock and unlock the
> adapters up to the root. This scheme should work with multiple levels of
> muxes and regular adapters in between.
>
> Signed-off-by: Eddie James <eajames@linux.ibm.com>

I think this looks okay. It was hard to understand at first, but makes
more sense with the context of the existing bus locking code.

There's a typo in one of your comments that the 0day bot found.

Have you tested with CONFIG_DEBUG_MUTEXES?

Reviewed-by: Joel Stanley <joel@jms.id.au>

> ---
>  drivers/i2c/i2c-core-base.c | 38 ++++++++++++++++++++++++++++
>  drivers/i2c/i2c-mux.c       | 50 +++++++++++++++++++++++++++++++++++++
>  include/linux/i2c.h         | 42 +++++++++++++++++++++++++++++++
>  3 files changed, 130 insertions(+)
>
> diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c
> index d43db2c3876e..e2c365348e1f 100644
> --- a/drivers/i2c/i2c-core-base.c
> +++ b/drivers/i2c/i2c-core-base.c
> @@ -1357,6 +1357,41 @@ static const struct i2c_lock_operations i2c_adapter_lock_ops = {
>         .unlock_bus =  i2c_adapter_unlock_bus,
>  };
>
> +/*
> + * For a non-mux adapter, the lock_select operation locks the chain of
> + * adapters upwards, returning the root. If there's a mux above this adapter
> + * somehow, it should also get locked and the desired channel selected.
> + */

Recursive lock the set of adaptors.

> +static struct i2c_adapter *i2c_adapter_lock_select(struct i2c_adapter *adapter)
> +{
> +       struct i2c_adapter *ret = adapter;
> +       struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter);
> +
> +       if (parent) {
> +               ret = parent->mux_root_ops->lock_select(parent);
> +               if (IS_ERR(ret))
> +                       return ret;
> +       }
> +
> +       adapter->lock_ops->lock_bus(adapter, I2C_LOCK_ROOT_ADAPTER);
> +       return ret;
> +}
> +
> +static void i2c_adapter_unlock_deselect(struct i2c_adapter *adapter)
> +{
> +       struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter);
> +
> +       adapter->lock_ops->unlock_bus(adapter, I2C_LOCK_ROOT_ADAPTER);
> +
> +       if (parent)
> +               parent->mux_root_ops->unlock_deselect(parent);
> +}
> +
> +static const struct i2c_mux_root_operations i2c_adapter_mux_root_ops = {
> +       .lock_select = i2c_adapter_lock_select,
> +       .unlock_deselect = i2c_adapter_unlock_deselect,
> +};
> +
>  static void i2c_host_notify_irq_teardown(struct i2c_adapter *adap)
>  {
>         struct irq_domain *domain = adap->host_notify_domain;
> @@ -1452,6 +1487,9 @@ static int i2c_register_adapter(struct i2c_adapter *adap)
>         if (!adap->lock_ops)
>                 adap->lock_ops = &i2c_adapter_lock_ops;
>
> +       if (!adap->mux_root_ops)
> +               adap->mux_root_ops = &i2c_adapter_mux_root_ops;
> +
>         adap->locked_flags = 0;
>         rt_mutex_init(&adap->bus_lock);
>         rt_mutex_init(&adap->mux_lock);
> diff --git a/drivers/i2c/i2c-mux.c b/drivers/i2c/i2c-mux.c
> index 774507b54b57..c7db770e4198 100644
> --- a/drivers/i2c/i2c-mux.c
> +++ b/drivers/i2c/i2c-mux.c
> @@ -210,6 +210,49 @@ static void i2c_parent_unlock_bus(struct i2c_adapter *adapter,
>         rt_mutex_unlock(&parent->mux_lock);
>  }
>
> +/*
> + * For a mux adapter, the lock_select operation first locks just like the
> + * lock_bus operation. Then it selects the channel for this adapter and
> + * returns the root adapter. If there is another mux above this one, calling
> + * the parent lock_select should ensure that the channel is correctly
> + * selected.
> + */
> +static struct i2c_adapter *i2c_mux_lock_select(struct i2c_adapter *adapter)
> +{
> +       int ret;
> +       struct i2c_mux_priv *priv = adapter->algo_data;
> +       struct i2c_mux_core *muxc = priv->muxc;
> +       struct i2c_adapter *parent = muxc->parent;
> +
> +       rt_mutex_lock_nested(&parent->mux_lock, i2c_adapter_depth(adapter));
> +
> +       adapter = parent->mux_root_ops->lock_select(parent);
> +       if (IS_ERR(adapter))
> +               return adapter;
> +
> +       ret = muxc->select(muxc, priv->chan_id);
> +       if (ret < 0) {
> +               parent->mux_root_ops->unlock_deselect(parent);
> +               rt_mutex_unlock(&parent->mux_lock);
> +               return ERR_PTR(ret);
> +       }
> +
> +       return adapter;
> +}
> +
> +static void i2c_mux_unlock_deselect(struct i2c_adapter *adapter)
> +{
> +       struct i2c_mux_priv *priv = adapter->algo_data;
> +       struct i2c_mux_core *muxc = priv->muxc;
> +       struct i2c_adapter *parent = muxc->parent;
> +
> +       if (muxc->deselect)
> +               muxc->deselect(muxc, priv->chan_id);
> +
> +       parent->mux_root_ops->unlock_deselect(parent);
> +       rt_mutex_unlock(&parent->mux_lock);
> +}
> +
>  struct i2c_adapter *i2c_root_adapter(struct device *dev)
>  {
>         struct device *i2c;
> @@ -279,6 +322,11 @@ static const struct i2c_lock_operations i2c_parent_lock_ops = {
>         .unlock_bus =  i2c_parent_unlock_bus,
>  };
>
> +static const struct i2c_mux_root_operations i2c_mux_root_ops = {
> +       .lock_select = i2c_mux_lock_select,
> +       .unlock_deselect = i2c_mux_unlock_deselect,
> +};
> +
>  int i2c_mux_add_adapter(struct i2c_mux_core *muxc,
>                         u32 force_nr, u32 chan_id,
>                         unsigned int class)
> @@ -339,6 +387,8 @@ int i2c_mux_add_adapter(struct i2c_mux_core *muxc,
>         else
>                 priv->adap.lock_ops = &i2c_parent_lock_ops;
>
> +       priv->adap.mux_root_ops = &i2c_mux_root_ops;
> +
>         /* Sanity check on class */
>         if (i2c_mux_parent_classes(parent) & class)
>                 dev_err(&parent->dev,
> diff --git a/include/linux/i2c.h b/include/linux/i2c.h
> index fbda5ada2afc..a3596f61b417 100644
> --- a/include/linux/i2c.h
> +++ b/include/linux/i2c.h
> @@ -583,6 +583,26 @@ struct i2c_lock_operations {
>         void (*unlock_bus)(struct i2c_adapter *adapter, unsigned int flags);
>  };
>
> +/**
> + * struct i2c_mux_root_operations - represent operations to lock and select
> + * the adapter's mux channel (if a mux is present)
> + * @lock_select: Get exclusive access to the root I2C bus adapter with the
> + *   correct mux channel selected for the adapter
> + * @unlock_deslect: Release exclusive access to the root I2C bus adapter and
> + *   deselect the mux channel for the adapter
> + *
> + * Some I2C clients need the ability to control the root I2C bus even if the
> + * endpoint device is behind a mux. For example, a driver for a chip that
> + * can't handle any I2C traffic on the bus while coming out of reset (including
> + * an I2C-driven mux switching channels) may need to lock the root bus with
> + * the mux selection fixed for the entire time the device is in reset.
> + * These operations are for such a purpose.
> + */
> +struct i2c_mux_root_operations {
> +       struct i2c_adapter *(*lock_select)(struct i2c_adapter *adapter);
> +       void (*unlock_deselect)(struct i2c_adapter *adapter);
> +};
> +
>  /**
>   * struct i2c_timings - I2C timing information
>   * @bus_freq_hz: the bus frequency in Hz
> @@ -725,6 +745,7 @@ struct i2c_adapter {
>
>         /* data fields that are valid for all devices   */
>         const struct i2c_lock_operations *lock_ops;
> +       const struct i2c_mux_root_operations *mux_root_ops;
>         struct rt_mutex bus_lock;
>         struct rt_mutex mux_lock;
>
> @@ -817,6 +838,27 @@ i2c_unlock_bus(struct i2c_adapter *adapter, unsigned int flags)
>         adapter->lock_ops->unlock_bus(adapter, flags);
>  }
>
> +/**
> + * i2c_lock_select_bus - Get exclusive access to the root I2C bus with the
> + *   target's mux channel (if a mux is present) selected.
> + * @adapter: Target I2C bus
> + *
> + * Return the root I2C bus if mux selection succeeds, an ERR_PTR otherwise
> + */
> +static inline struct i2c_adapter *i2c_lock_select_bus(struct i2c_adapter *adapter)
> +{
> +       return adapter->mux_root_ops->lock_select(adapter);
> +}
> +
> +/**
> + * i2c_unlock_deslect_bus - Release exclusive access to the root I2C bus
> + * @adapter: Target I2C bus
> + */
> +static inline void i2c_unlock_deselect_bus(struct i2c_adapter *adapter)
> +{
> +       adapter->mux_root_ops->unlock_deselect(adapter);
> +}
> +
>  /**
>   * i2c_mark_adapter_suspended - Report suspended state of the adapter to the core
>   * @adap: Adapter to mark as suspended
> --
> 2.27.0
>

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH 1/2] i2c: core: Add mux root adapter operations
  2022-06-03  8:01   ` Joel Stanley
@ 2022-09-06 20:10     ` Eddie James
  0 siblings, 0 replies; 6+ messages in thread
From: Eddie James @ 2022-09-06 20:10 UTC (permalink / raw)
  To: Joel Stanley
  Cc: open list:I2C SUBSYSTEM HOST DRIVERS, linux-iio,
	Linux Kernel Mailing List, Wolfram Sang, Peter Rosin,
	Jonathan Cameron, Lars-Peter Clausen, Milton Miller II


On 6/3/22 03:01, Joel Stanley wrote:
> On Thu, 26 May 2022 at 20:53, Eddie James <eajames@linux.ibm.com> wrote:
>> Some I2C clients need the ability to control the root I2C bus even if the
>> endpoint device is behind a mux. For example, a driver for a chip that
>> can't handle any I2C traffic on the bus while coming out of reset
>> (including an I2C-driven mux switching channels) may need to lock the root
>> bus with the mux selection fixed for the entire time the device is in
>> reset.
>> For this purpose, add a new structure containing two function pointers to
>> the adapter structure. These functions pointers should be defined for
>> every adapter. The lock_select operation, for a mux adapter, locks the
>> parent adpaters up to the root and selects the adapter's channel. The
>> unlock_deselect operation deselects the mux channel and unlocks all the
>> adapters. For a non-mux adapter, the operations lock and unlock the
>> adapters up to the root. This scheme should work with multiple levels of
>> muxes and regular adapters in between.
>>
>> Signed-off-by: Eddie James <eajames@linux.ibm.com>
> I think this looks okay. It was hard to understand at first, but makes
> more sense with the context of the existing bus locking code.
>
> There's a typo in one of your comments that the 0day bot found.
>
> Have you tested with CONFIG_DEBUG_MUTEXES?


Getting back around to this... Yes, I just tested with 
CONFIG_DEBUG_MUTEXES and didn't see any issues.

I will address the comment typo in v2 now.


Thanks,

Eddie


>
> Reviewed-by: Joel Stanley <joel@jms.id.au>
>
>> ---
>>   drivers/i2c/i2c-core-base.c | 38 ++++++++++++++++++++++++++++
>>   drivers/i2c/i2c-mux.c       | 50 +++++++++++++++++++++++++++++++++++++
>>   include/linux/i2c.h         | 42 +++++++++++++++++++++++++++++++
>>   3 files changed, 130 insertions(+)
>>
>> diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c
>> index d43db2c3876e..e2c365348e1f 100644
>> --- a/drivers/i2c/i2c-core-base.c
>> +++ b/drivers/i2c/i2c-core-base.c
>> @@ -1357,6 +1357,41 @@ static const struct i2c_lock_operations i2c_adapter_lock_ops = {
>>          .unlock_bus =  i2c_adapter_unlock_bus,
>>   };
>>
>> +/*
>> + * For a non-mux adapter, the lock_select operation locks the chain of
>> + * adapters upwards, returning the root. If there's a mux above this adapter
>> + * somehow, it should also get locked and the desired channel selected.
>> + */
> Recursive lock the set of adaptors.
>
>> +static struct i2c_adapter *i2c_adapter_lock_select(struct i2c_adapter *adapter)
>> +{
>> +       struct i2c_adapter *ret = adapter;
>> +       struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter);
>> +
>> +       if (parent) {
>> +               ret = parent->mux_root_ops->lock_select(parent);
>> +               if (IS_ERR(ret))
>> +                       return ret;
>> +       }
>> +
>> +       adapter->lock_ops->lock_bus(adapter, I2C_LOCK_ROOT_ADAPTER);
>> +       return ret;
>> +}
>> +
>> +static void i2c_adapter_unlock_deselect(struct i2c_adapter *adapter)
>> +{
>> +       struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter);
>> +
>> +       adapter->lock_ops->unlock_bus(adapter, I2C_LOCK_ROOT_ADAPTER);
>> +
>> +       if (parent)
>> +               parent->mux_root_ops->unlock_deselect(parent);
>> +}
>> +
>> +static const struct i2c_mux_root_operations i2c_adapter_mux_root_ops = {
>> +       .lock_select = i2c_adapter_lock_select,
>> +       .unlock_deselect = i2c_adapter_unlock_deselect,
>> +};
>> +
>>   static void i2c_host_notify_irq_teardown(struct i2c_adapter *adap)
>>   {
>>          struct irq_domain *domain = adap->host_notify_domain;
>> @@ -1452,6 +1487,9 @@ static int i2c_register_adapter(struct i2c_adapter *adap)
>>          if (!adap->lock_ops)
>>                  adap->lock_ops = &i2c_adapter_lock_ops;
>>
>> +       if (!adap->mux_root_ops)
>> +               adap->mux_root_ops = &i2c_adapter_mux_root_ops;
>> +
>>          adap->locked_flags = 0;
>>          rt_mutex_init(&adap->bus_lock);
>>          rt_mutex_init(&adap->mux_lock);
>> diff --git a/drivers/i2c/i2c-mux.c b/drivers/i2c/i2c-mux.c
>> index 774507b54b57..c7db770e4198 100644
>> --- a/drivers/i2c/i2c-mux.c
>> +++ b/drivers/i2c/i2c-mux.c
>> @@ -210,6 +210,49 @@ static void i2c_parent_unlock_bus(struct i2c_adapter *adapter,
>>          rt_mutex_unlock(&parent->mux_lock);
>>   }
>>
>> +/*
>> + * For a mux adapter, the lock_select operation first locks just like the
>> + * lock_bus operation. Then it selects the channel for this adapter and
>> + * returns the root adapter. If there is another mux above this one, calling
>> + * the parent lock_select should ensure that the channel is correctly
>> + * selected.
>> + */
>> +static struct i2c_adapter *i2c_mux_lock_select(struct i2c_adapter *adapter)
>> +{
>> +       int ret;
>> +       struct i2c_mux_priv *priv = adapter->algo_data;
>> +       struct i2c_mux_core *muxc = priv->muxc;
>> +       struct i2c_adapter *parent = muxc->parent;
>> +
>> +       rt_mutex_lock_nested(&parent->mux_lock, i2c_adapter_depth(adapter));
>> +
>> +       adapter = parent->mux_root_ops->lock_select(parent);
>> +       if (IS_ERR(adapter))
>> +               return adapter;
>> +
>> +       ret = muxc->select(muxc, priv->chan_id);
>> +       if (ret < 0) {
>> +               parent->mux_root_ops->unlock_deselect(parent);
>> +               rt_mutex_unlock(&parent->mux_lock);
>> +               return ERR_PTR(ret);
>> +       }
>> +
>> +       return adapter;
>> +}
>> +
>> +static void i2c_mux_unlock_deselect(struct i2c_adapter *adapter)
>> +{
>> +       struct i2c_mux_priv *priv = adapter->algo_data;
>> +       struct i2c_mux_core *muxc = priv->muxc;
>> +       struct i2c_adapter *parent = muxc->parent;
>> +
>> +       if (muxc->deselect)
>> +               muxc->deselect(muxc, priv->chan_id);
>> +
>> +       parent->mux_root_ops->unlock_deselect(parent);
>> +       rt_mutex_unlock(&parent->mux_lock);
>> +}
>> +
>>   struct i2c_adapter *i2c_root_adapter(struct device *dev)
>>   {
>>          struct device *i2c;
>> @@ -279,6 +322,11 @@ static const struct i2c_lock_operations i2c_parent_lock_ops = {
>>          .unlock_bus =  i2c_parent_unlock_bus,
>>   };
>>
>> +static const struct i2c_mux_root_operations i2c_mux_root_ops = {
>> +       .lock_select = i2c_mux_lock_select,
>> +       .unlock_deselect = i2c_mux_unlock_deselect,
>> +};
>> +
>>   int i2c_mux_add_adapter(struct i2c_mux_core *muxc,
>>                          u32 force_nr, u32 chan_id,
>>                          unsigned int class)
>> @@ -339,6 +387,8 @@ int i2c_mux_add_adapter(struct i2c_mux_core *muxc,
>>          else
>>                  priv->adap.lock_ops = &i2c_parent_lock_ops;
>>
>> +       priv->adap.mux_root_ops = &i2c_mux_root_ops;
>> +
>>          /* Sanity check on class */
>>          if (i2c_mux_parent_classes(parent) & class)
>>                  dev_err(&parent->dev,
>> diff --git a/include/linux/i2c.h b/include/linux/i2c.h
>> index fbda5ada2afc..a3596f61b417 100644
>> --- a/include/linux/i2c.h
>> +++ b/include/linux/i2c.h
>> @@ -583,6 +583,26 @@ struct i2c_lock_operations {
>>          void (*unlock_bus)(struct i2c_adapter *adapter, unsigned int flags);
>>   };
>>
>> +/**
>> + * struct i2c_mux_root_operations - represent operations to lock and select
>> + * the adapter's mux channel (if a mux is present)
>> + * @lock_select: Get exclusive access to the root I2C bus adapter with the
>> + *   correct mux channel selected for the adapter
>> + * @unlock_deslect: Release exclusive access to the root I2C bus adapter and
>> + *   deselect the mux channel for the adapter
>> + *
>> + * Some I2C clients need the ability to control the root I2C bus even if the
>> + * endpoint device is behind a mux. For example, a driver for a chip that
>> + * can't handle any I2C traffic on the bus while coming out of reset (including
>> + * an I2C-driven mux switching channels) may need to lock the root bus with
>> + * the mux selection fixed for the entire time the device is in reset.
>> + * These operations are for such a purpose.
>> + */
>> +struct i2c_mux_root_operations {
>> +       struct i2c_adapter *(*lock_select)(struct i2c_adapter *adapter);
>> +       void (*unlock_deselect)(struct i2c_adapter *adapter);
>> +};
>> +
>>   /**
>>    * struct i2c_timings - I2C timing information
>>    * @bus_freq_hz: the bus frequency in Hz
>> @@ -725,6 +745,7 @@ struct i2c_adapter {
>>
>>          /* data fields that are valid for all devices   */
>>          const struct i2c_lock_operations *lock_ops;
>> +       const struct i2c_mux_root_operations *mux_root_ops;
>>          struct rt_mutex bus_lock;
>>          struct rt_mutex mux_lock;
>>
>> @@ -817,6 +838,27 @@ i2c_unlock_bus(struct i2c_adapter *adapter, unsigned int flags)
>>          adapter->lock_ops->unlock_bus(adapter, flags);
>>   }
>>
>> +/**
>> + * i2c_lock_select_bus - Get exclusive access to the root I2C bus with the
>> + *   target's mux channel (if a mux is present) selected.
>> + * @adapter: Target I2C bus
>> + *
>> + * Return the root I2C bus if mux selection succeeds, an ERR_PTR otherwise
>> + */
>> +static inline struct i2c_adapter *i2c_lock_select_bus(struct i2c_adapter *adapter)
>> +{
>> +       return adapter->mux_root_ops->lock_select(adapter);
>> +}
>> +
>> +/**
>> + * i2c_unlock_deslect_bus - Release exclusive access to the root I2C bus
>> + * @adapter: Target I2C bus
>> + */
>> +static inline void i2c_unlock_deselect_bus(struct i2c_adapter *adapter)
>> +{
>> +       adapter->mux_root_ops->unlock_deselect(adapter);
>> +}
>> +
>>   /**
>>    * i2c_mark_adapter_suspended - Report suspended state of the adapter to the core
>>    * @adap: Adapter to mark as suspended
>> --
>> 2.27.0
>>

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2022-09-06 20:16 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-05-26 20:53 [PATCH 0/2] i2c: core: Add mux root adapter operations Eddie James
2022-05-26 20:53 ` [PATCH 1/2] " Eddie James
2022-05-27 16:50   ` kernel test robot
2022-06-03  8:01   ` Joel Stanley
2022-09-06 20:10     ` Eddie James
2022-05-26 20:53 ` [PATCH 2/2] iio: si7020: Lock root adapter to wait for reset Eddie James

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.