[PATCH 1/2] i2c: mux: pca9541: add delayed-release support
Joel Stanley
joel at jms.id.au
Mon Jan 31 17:21:01 AEDT 2022
On Mon, 24 Jan 2022 at 21:39, Zev Weiss <zev at bewilderbeest.net> wrote:
>
> For heavily-utilized i2c busses, the overhead of reacquiring bus
> ownership on every transaction can be quite substantial. By delaying
> the release of the bus (in anticipation of another transaction needing
> to re-acquire ownership in the near future), we can reduce the
> overhead significantly -- a subsequent transaction that arrives within
> the delay window can merely verify that we still own it.
>
> The new "release-delay-us" DT property specifies a number of
> microseconds to wait after a transaction before releasing the bus
> (zero by default so as to preserve the existing behavior of releasing
> it immediately).
Sounds like a good idea to me!
>
> Signed-off-by: Zev Weiss <zev at bewilderbeest.net>
Reviewed-by: Joel Stanley <joel at jms.id.au>
> ---
> drivers/i2c/muxes/i2c-mux-pca9541.c | 56 ++++++++++++++++++++++++-----
> 1 file changed, 47 insertions(+), 9 deletions(-)
>
> diff --git a/drivers/i2c/muxes/i2c-mux-pca9541.c b/drivers/i2c/muxes/i2c-mux-pca9541.c
> index 6daec8d3d331..76269bf9f9ca 100644
> --- a/drivers/i2c/muxes/i2c-mux-pca9541.c
> +++ b/drivers/i2c/muxes/i2c-mux-pca9541.c
> @@ -19,6 +19,7 @@
> #include <linux/bitops.h>
> #include <linux/delay.h>
> #include <linux/device.h>
> +#include <linux/devm-helpers.h>
> #include <linux/i2c.h>
> #include <linux/i2c-mux.h>
> #include <linux/jiffies.h>
> @@ -75,6 +76,8 @@ struct pca9541 {
> struct i2c_client *client;
> unsigned long select_timeout;
> unsigned long arb_timeout;
> + unsigned long release_delay;
> + struct delayed_work release_work;
> };
>
> static const struct i2c_device_id pca9541_id[] = {
> @@ -127,8 +130,11 @@ static int pca9541_reg_read(struct i2c_client *client, u8 command)
> * Arbitration management functions
> */
>
> -/* Release bus. Also reset NTESTON and BUSINIT if it was set. */
> -static void pca9541_release_bus(struct i2c_client *client)
> +/*
> + * Release bus. Also reset NTESTON and BUSINIT if it was set.
> + * client->adapter must already be locked.
> + */
> +static void __pca9541_release_bus(struct i2c_client *client)
> {
> int reg;
>
> @@ -138,6 +144,13 @@ static void pca9541_release_bus(struct i2c_client *client)
> (reg & PCA9541_CTL_NBUSON) >> 1);
> }
>
> +static void pca9541_release_bus(struct i2c_client *client)
> +{
> + i2c_lock_bus(client->adapter, I2C_LOCK_SEGMENT);
> + __pca9541_release_bus(client);
> + i2c_unlock_bus(client->adapter, I2C_LOCK_SEGMENT);
> +}
> +
> /*
> * Arbitration is defined as a two-step process. A bus master can only activate
> * the slave bus if it owns it; otherwise it has to request ownership first.
> @@ -254,6 +267,9 @@ static int pca9541_select_chan(struct i2c_mux_core *muxc, u32 chan)
> unsigned long timeout = jiffies + ARB2_TIMEOUT;
> /* give up after this time */
>
> + if (data->release_delay)
> + cancel_delayed_work_sync(&data->release_work);
> +
> data->arb_timeout = jiffies + ARB_TIMEOUT;
> /* force bus ownership after this time */
>
> @@ -276,10 +292,21 @@ static int pca9541_release_chan(struct i2c_mux_core *muxc, u32 chan)
> struct pca9541 *data = i2c_mux_priv(muxc);
> struct i2c_client *client = data->client;
>
> - pca9541_release_bus(client);
> + if (data->release_delay)
> + schedule_delayed_work(&data->release_work, data->release_delay);
> + else
> + __pca9541_release_bus(client);
> +
> return 0;
> }
>
> +static void pca9541_release_workfn(struct work_struct *work)
> +{
> + struct pca9541 *data = container_of(work, struct pca9541, release_work.work);
> +
> + pca9541_release_bus(data->client);
> +}
> +
> /*
> * I2C init/probing/exit functions
> */
> @@ -289,18 +316,13 @@ static int pca9541_probe(struct i2c_client *client,
> struct i2c_adapter *adap = client->adapter;
> struct i2c_mux_core *muxc;
> struct pca9541 *data;
> + u32 release_delay_us;
> int ret;
>
> if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_BYTE_DATA))
> return -ENODEV;
>
> - /*
> - * I2C accesses are unprotected here.
> - * We have to lock the I2C segment before releasing the bus.
> - */
> - i2c_lock_bus(adap, I2C_LOCK_SEGMENT);
> pca9541_release_bus(client);
> - i2c_unlock_bus(adap, I2C_LOCK_SEGMENT);
>
> /* Create mux adapter */
>
> @@ -313,6 +335,14 @@ static int pca9541_probe(struct i2c_client *client,
> data = i2c_mux_priv(muxc);
> data->client = client;
>
> + if (!device_property_read_u32(&client->dev, "release-delay-us", &release_delay_us)) {
> + data->release_delay = usecs_to_jiffies(release_delay_us);
> + ret = devm_delayed_work_autocancel(&client->dev, &data->release_work,
> + pca9541_release_workfn);
> + if (ret)
> + return ret;
> + }
> +
> i2c_set_clientdata(client, muxc);
>
> ret = i2c_mux_add_adapter(muxc, 0, 0, 0);
> @@ -328,6 +358,14 @@ static int pca9541_probe(struct i2c_client *client,
> static int pca9541_remove(struct i2c_client *client)
> {
> struct i2c_mux_core *muxc = i2c_get_clientdata(client);
> + struct pca9541 *data = i2c_mux_priv(muxc);
> +
> + /*
> + * Ensure the bus is released (in case the device gets destroyed
> + * before release_work runs).
> + */
> + if (data->release_delay)
> + pca9541_release_bus(client);
>
> i2c_mux_del_adapters(muxc);
> return 0;
> --
> 2.34.1
>
On Mon, 24 Jan 2022 at 21:39, Zev Weiss <zev at bewilderbeest.net> wrote:
>
> For heavily-utilized i2c busses, the overhead of reacquiring bus
> ownership on every transaction can be quite substantial. By delaying
> the release of the bus (in anticipation of another transaction needing
> to re-acquire ownership in the near future), we can reduce the
> overhead significantly -- a subsequent transaction that arrives within
> the delay window can merely verify that we still own it.
>
> The new "release-delay-us" DT property specifies a number of
> microseconds to wait after a transaction before releasing the bus
> (zero by default so as to preserve the existing behavior of releasing
> it immediately).
>
> Signed-off-by: Zev Weiss <zev at bewilderbeest.net>
> ---
> drivers/i2c/muxes/i2c-mux-pca9541.c | 56 ++++++++++++++++++++++++-----
> 1 file changed, 47 insertions(+), 9 deletions(-)
>
> diff --git a/drivers/i2c/muxes/i2c-mux-pca9541.c b/drivers/i2c/muxes/i2c-mux-pca9541.c
> index 6daec8d3d331..76269bf9f9ca 100644
> --- a/drivers/i2c/muxes/i2c-mux-pca9541.c
> +++ b/drivers/i2c/muxes/i2c-mux-pca9541.c
> @@ -19,6 +19,7 @@
> #include <linux/bitops.h>
> #include <linux/delay.h>
> #include <linux/device.h>
> +#include <linux/devm-helpers.h>
> #include <linux/i2c.h>
> #include <linux/i2c-mux.h>
> #include <linux/jiffies.h>
> @@ -75,6 +76,8 @@ struct pca9541 {
> struct i2c_client *client;
> unsigned long select_timeout;
> unsigned long arb_timeout;
> + unsigned long release_delay;
> + struct delayed_work release_work;
> };
>
> static const struct i2c_device_id pca9541_id[] = {
> @@ -127,8 +130,11 @@ static int pca9541_reg_read(struct i2c_client *client, u8 command)
> * Arbitration management functions
> */
>
> -/* Release bus. Also reset NTESTON and BUSINIT if it was set. */
> -static void pca9541_release_bus(struct i2c_client *client)
> +/*
> + * Release bus. Also reset NTESTON and BUSINIT if it was set.
> + * client->adapter must already be locked.
> + */
> +static void __pca9541_release_bus(struct i2c_client *client)
> {
> int reg;
>
> @@ -138,6 +144,13 @@ static void pca9541_release_bus(struct i2c_client *client)
> (reg & PCA9541_CTL_NBUSON) >> 1);
> }
>
> +static void pca9541_release_bus(struct i2c_client *client)
> +{
> + i2c_lock_bus(client->adapter, I2C_LOCK_SEGMENT);
> + __pca9541_release_bus(client);
> + i2c_unlock_bus(client->adapter, I2C_LOCK_SEGMENT);
> +}
> +
> /*
> * Arbitration is defined as a two-step process. A bus master can only activate
> * the slave bus if it owns it; otherwise it has to request ownership first.
> @@ -254,6 +267,9 @@ static int pca9541_select_chan(struct i2c_mux_core *muxc, u32 chan)
> unsigned long timeout = jiffies + ARB2_TIMEOUT;
> /* give up after this time */
>
> + if (data->release_delay)
> + cancel_delayed_work_sync(&data->release_work);
> +
> data->arb_timeout = jiffies + ARB_TIMEOUT;
> /* force bus ownership after this time */
>
> @@ -276,10 +292,21 @@ static int pca9541_release_chan(struct i2c_mux_core *muxc, u32 chan)
> struct pca9541 *data = i2c_mux_priv(muxc);
> struct i2c_client *client = data->client;
>
> - pca9541_release_bus(client);
> + if (data->release_delay)
> + schedule_delayed_work(&data->release_work, data->release_delay);
> + else
> + __pca9541_release_bus(client);
> +
> return 0;
> }
>
> +static void pca9541_release_workfn(struct work_struct *work)
> +{
> + struct pca9541 *data = container_of(work, struct pca9541, release_work.work);
> +
> + pca9541_release_bus(data->client);
> +}
> +
> /*
> * I2C init/probing/exit functions
> */
> @@ -289,18 +316,13 @@ static int pca9541_probe(struct i2c_client *client,
> struct i2c_adapter *adap = client->adapter;
> struct i2c_mux_core *muxc;
> struct pca9541 *data;
> + u32 release_delay_us;
> int ret;
>
> if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_BYTE_DATA))
> return -ENODEV;
>
> - /*
> - * I2C accesses are unprotected here.
> - * We have to lock the I2C segment before releasing the bus.
> - */
> - i2c_lock_bus(adap, I2C_LOCK_SEGMENT);
> pca9541_release_bus(client);
> - i2c_unlock_bus(adap, I2C_LOCK_SEGMENT);
>
> /* Create mux adapter */
>
> @@ -313,6 +335,14 @@ static int pca9541_probe(struct i2c_client *client,
> data = i2c_mux_priv(muxc);
> data->client = client;
>
> + if (!device_property_read_u32(&client->dev, "release-delay-us", &release_delay_us)) {
> + data->release_delay = usecs_to_jiffies(release_delay_us);
> + ret = devm_delayed_work_autocancel(&client->dev, &data->release_work,
> + pca9541_release_workfn);
> + if (ret)
> + return ret;
> + }
> +
> i2c_set_clientdata(client, muxc);
>
> ret = i2c_mux_add_adapter(muxc, 0, 0, 0);
> @@ -328,6 +358,14 @@ static int pca9541_probe(struct i2c_client *client,
> static int pca9541_remove(struct i2c_client *client)
> {
> struct i2c_mux_core *muxc = i2c_get_clientdata(client);
> + struct pca9541 *data = i2c_mux_priv(muxc);
> +
> + /*
> + * Ensure the bus is released (in case the device gets destroyed
> + * before release_work runs).
> + */
> + if (data->release_delay)
> + pca9541_release_bus(client);
>
> i2c_mux_del_adapters(muxc);
> return 0;
> --
> 2.34.1
>
More information about the openbmc
mailing list