[1/2] i2c: Add new I2C_CLIENT_IGNORE_BUSY flag

Message ID 20180608211113.10238-1-hdegoede@redhat.com
State Superseded
Headers show
Series
  • [1/2] i2c: Add new I2C_CLIENT_IGNORE_BUSY flag
Related show

Commit Message

Hans de Goede June 8, 2018, 9:11 p.m.
Normally i2c_new_device will check that a client at the same address does
not already exists.

On systems with ACPI instantiated i2c-clients, normally there is 1 fw_node
per i2c-device and that fw-node contains 1 I2cSerialBus resource for that 1
i2c-device.

But in some rare cases the manufacturer has decided to describe multiple
i2c-devices in a single ACPI fwnode with multiple I2cSerialBus resources.

After an earlier attempt to fix this in the i2c-core which resulted in a
lot of extra code to support this corner-case it was decided to go for a
different approach. The next patch in this series introduces a new special
i2c-multi-instantiate driver which can be build as a module and which will
bind to the i2c-client which gets instantiated by the core for the first
I2cSerialBus resource in the fwnode.

This driver will instantiate a new i2c-client per I2cSerialBus resource,
using the driver_data from the acpi_device_id it is binding to to tell it
which chip-type (and optional irq-resource) to use when instantiating.

Since it binds to the core instantiated i2c-client and claims the
acpi_device_id of the fwnode, it also needs to instantiate a new i2c-client
for the first I2cSerialBus resource uses the chip-id for the real driver,
so that that can bind to the i2c-device described by the first I2cSerialBus
resource, this requires the new I2C_CLIENT_IGNORE_BUSY flag.

Note this means that we will be instantiating 2 i2c_client-s for the first
I2cSerialBus resource, this is not pretty, but since the multi-instantiate
driver does exactly 0 i2c-transfers this is not a problem.

Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
 drivers/i2c/i2c-core-base.c | 11 +++++++----
 include/linux/i2c.h         |  3 +++
 2 files changed, 10 insertions(+), 4 deletions(-)

Comments

Andy Shevchenko June 11, 2018, 8:43 a.m. | #1
On Fri, 2018-06-08 at 23:11 +0200, Hans de Goede wrote:
> Normally i2c_new_device will check that a client at the same address
> does
> not already exists.
> 
> On systems with ACPI instantiated i2c-clients, normally there is 1
> fw_node
> per i2c-device and that fw-node contains 1 I2cSerialBus resource for
> that 1
> i2c-device.
> 
> But in some rare cases the manufacturer has decided to describe
> multiple
> i2c-devices in a single ACPI fwnode with multiple I2cSerialBus
> resources.
> 
> After an earlier attempt to fix this in the i2c-core which resulted in
> a
> lot of extra code to support this corner-case it was decided to go for
> a
> different approach. The next patch in this series introduces a new
> special
> i2c-multi-instantiate driver which can be build as a module and which
> will
> bind to the i2c-client which gets instantiated by the core for the
> first
> I2cSerialBus resource in the fwnode.
> 
> This driver will instantiate a new i2c-client per I2cSerialBus
> resource,
> using the driver_data from the acpi_device_id it is binding to to tell
> it
> which chip-type (and optional irq-resource) to use when instantiating.
> 
> Since it binds to the core instantiated i2c-client and claims the
> acpi_device_id of the fwnode, it also needs to instantiate a new i2c-
> client
> for the first I2cSerialBus resource uses the chip-id for the real
> driver,
> so that that can bind to the i2c-device described by the first
> I2cSerialBus
> resource, this requires the new I2C_CLIENT_IGNORE_BUSY flag.
> 
> Note this means that we will be instantiating 2 i2c_client-s for the
> first
> I2cSerialBus resource, this is not pretty, but since the multi-
> instantiate
> driver does exactly 0 i2c-transfers this is not a problem.
> 

You forgot to Cc Heikki.

> Signed-off-by: Hans de Goede <hdegoede@redhat.com>
> ---
>  drivers/i2c/i2c-core-base.c | 11 +++++++----
>  include/linux/i2c.h         |  3 +++
>  2 files changed, 10 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c
> index a17f46a95f73..77d70b611bc5 100644
> --- a/drivers/i2c/i2c-core-base.c
> +++ b/drivers/i2c/i2c-core-base.c
> @@ -721,7 +721,7 @@ i2c_new_device(struct i2c_adapter *adap, struct
> i2c_board_info const *info)
>  	if (info->archdata)
>  		client->dev.archdata = *info->archdata;
>  
> -	client->flags = info->flags;
> +	client->flags = info->flags & ~I2C_CLIENT_IGNORE_BUSY;
>  	client->addr = info->addr;
>  
>  	client->irq = info->irq;
> @@ -739,9 +739,12 @@ i2c_new_device(struct i2c_adapter *adap, struct
> i2c_board_info const *info)
>  	}
>  
>  	/* Check for address business */
> -	status = i2c_check_addr_busy(adap,
> i2c_encode_flags_to_addr(client));
> -	if (status)
> -		goto out_err;
> +	if (!(info->flags & I2C_CLIENT_IGNORE_BUSY)) {
> +		status = i2c_check_addr_busy(adap,
> +					     i2c_encode_flags_to_addr
> (client));
> +		if (status)
> +			goto out_err;
> +	}
>  
>  	client->dev.parent = &client->adapter->dev;
>  	client->dev.bus = &i2c_bus_type;
> diff --git a/include/linux/i2c.h b/include/linux/i2c.h
> index 44ad14e016b5..3ad73cf1b187 100644
> --- a/include/linux/i2c.h
> +++ b/include/linux/i2c.h
> @@ -775,6 +775,9 @@ i2c_unlock_adapter(struct i2c_adapter *adapter)
>  #define I2C_CLIENT_SLAVE	0x20	/* we are the slave */
>  #define I2C_CLIENT_HOST_NOTIFY	0x40	/* We want to use
> I2C host notify */
>  #define I2C_CLIENT_WAKE		0x80	/* for board_info;
> true iff can wake */
> +#define I2C_CLIENT_IGNORE_BUSY	0x100	/* For board_info;
> skip busy check */
> +					/* Do NOT use, reserved for
> use by */
> +					/* drv/i2c/i2c-multi-
> instantiate.c */
>  #define I2C_CLIENT_SCCB		0x9000	/* Use
> Omnivision SCCB protocol */
>  					/* Must match
> I2C_M_STOP|IGNORE_NAK */
>

Patch

diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c
index a17f46a95f73..77d70b611bc5 100644
--- a/drivers/i2c/i2c-core-base.c
+++ b/drivers/i2c/i2c-core-base.c
@@ -721,7 +721,7 @@  i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
 	if (info->archdata)
 		client->dev.archdata = *info->archdata;
 
-	client->flags = info->flags;
+	client->flags = info->flags & ~I2C_CLIENT_IGNORE_BUSY;
 	client->addr = info->addr;
 
 	client->irq = info->irq;
@@ -739,9 +739,12 @@  i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
 	}
 
 	/* Check for address business */
-	status = i2c_check_addr_busy(adap, i2c_encode_flags_to_addr(client));
-	if (status)
-		goto out_err;
+	if (!(info->flags & I2C_CLIENT_IGNORE_BUSY)) {
+		status = i2c_check_addr_busy(adap,
+					     i2c_encode_flags_to_addr(client));
+		if (status)
+			goto out_err;
+	}
 
 	client->dev.parent = &client->adapter->dev;
 	client->dev.bus = &i2c_bus_type;
diff --git a/include/linux/i2c.h b/include/linux/i2c.h
index 44ad14e016b5..3ad73cf1b187 100644
--- a/include/linux/i2c.h
+++ b/include/linux/i2c.h
@@ -775,6 +775,9 @@  i2c_unlock_adapter(struct i2c_adapter *adapter)
 #define I2C_CLIENT_SLAVE	0x20	/* we are the slave */
 #define I2C_CLIENT_HOST_NOTIFY	0x40	/* We want to use I2C host notify */
 #define I2C_CLIENT_WAKE		0x80	/* for board_info; true iff can wake */
+#define I2C_CLIENT_IGNORE_BUSY	0x100	/* For board_info; skip busy check */
+					/* Do NOT use, reserved for use by */
+					/* drv/i2c/i2c-multi-instantiate.c */
 #define I2C_CLIENT_SCCB		0x9000	/* Use Omnivision SCCB protocol */
 					/* Must match I2C_M_STOP|IGNORE_NAK */