Re: [PATCH v7 3/4] iio : Add cm3218 smbus ARA and ACPI support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Sun, 14 Jan 2018 15:39:24 +0100
"Marc CAPDEVILLE" <m.capdeville@xxxxxxxxxx> wrote:

> 
> > On Sat, 13 Jan 2018 14:37:04 +0100
> > Marc CAPDEVILLE <m.capdeville@xxxxxxxxxx> wrote:
> >
> >> On asus T100, Capella cm3218 chip is implemented as ambiant light
> >> sensor. This chip expose an smbus ARA protocol device on standard
> >> address 0x0c. The chip is not functional before all alerts are
> >> acknowledged.
> >>
> >> The driver call i2c_require_smbus_alert() to set the smbus alert
> >> protocol driver on its adapter. If an interrupt resource is given,
> >> we register this irq with the smbus alert driver.
> >> If no irq is given, the driver call i2c_smbus_alert_event() to trigger
> >> the alert process to clear the initial alert event before initializing
> >> cm3218 registers.
> >>
> >> Signed-off-by: Marc CAPDEVILLE <m.capdeville@xxxxxxxxxx>
> >
> > Ultimately I think we can move most of the logic here into the i2c core,
> > but that can be a job for another day.
> 
> This can be done in the core in i2c_device_probe(), but can we suppose
> that client irq is the smbus alert line when the device is flagged with
> I2C_CLIENT_ALERT and/or the driver define an alert callback ?

We'd have to audit the drivers and check this was the case.  Unless
we have have devices with ARA and another interrupt it seems likely
this would be fine.

> >
> > I'm not sure there isn't a nasty mess with this device if we have
> > a bus master created ara.  I raised it below.  Not sure there is
> > much we can do about it though.
> 
> If the adapter has already created the smbus alert device, the
> i2c_require_smbus_alert() do nothing. We just have to register an
> additional handler for the irq line if it differ from the adapter one.
> This is handle by i2c_smbus_alert_add_irq().
> >
> > Jonathan
> >
> >> ---
> >>  drivers/iio/light/cm32181.c | 94
> >> +++++++++++++++++++++++++++++++++++++++++++--
> >>  1 file changed, 91 insertions(+), 3 deletions(-)
> >>
> >> diff --git a/drivers/iio/light/cm32181.c b/drivers/iio/light/cm32181.c
> >> index aebf7dd071af..eae5b7cc6878 100644
> >> --- a/drivers/iio/light/cm32181.c
> >> +++ b/drivers/iio/light/cm32181.c
> >> @@ -18,6 +18,9 @@
> >>  #include <linux/iio/sysfs.h>
> >>  #include <linux/iio/events.h>
> >>  #include <linux/init.h>
> >> +#include <linux/i2c-smbus.h>
> >> +#include <linux/acpi.h>
> >> +#include <linux/of_device.h>
> >>
> >>  /* Registers Address */
> >>  #define CM32181_REG_ADDR_CMD		0x00
> >> @@ -47,6 +50,9 @@
> >>  #define CM32181_CALIBSCALE_RESOLUTION	1000
> >>  #define MLUX_PER_LUX			1000
> >>
> >> +#define CM32181_ID			0x81
> >> +#define CM3218_ID			0x18
> >> +
> >>  static const u8 cm32181_reg[CM32181_CONF_REG_NUM] = {
> >>  	CM32181_REG_ADDR_CMD,
> >>  };
> >> @@ -57,6 +63,7 @@ static const int als_it_value[] = {25000, 50000,
> >> 100000, 200000, 400000,
> >>
> >>  struct cm32181_chip {
> >>  	struct i2c_client *client;
> >> +	int chip_id;
> >>  	struct mutex lock;
> >>  	u16 conf_regs[CM32181_CONF_REG_NUM];
> >>  	int calibscale;
> >> @@ -81,7 +88,7 @@ static int cm32181_reg_init(struct cm32181_chip
> >> *cm32181)
> >>  		return ret;
> >>
> >>  	/* check device ID */
> >> -	if ((ret & 0xFF) != 0x81)
> >> +	if ((ret & 0xFF) != cm32181->chip_id)
> >>  		return -ENODEV;
> >>
> >>  	/* Default Values */
> >> @@ -297,12 +304,23 @@ static const struct iio_info cm32181_info = {
> >>  	.attrs			= &cm32181_attribute_group,
> >>  };
> >>
> >> +static void cm3218_alert(struct i2c_client *client,
> >> +			 enum i2c_alert_protocol type,
> >> +			 unsigned int data)
> >> +{
> >> +	/*
> >> +	 * nothing to do for now.
> >> +	 * This is just here to acknownledge the cm3218 alert.
> >> +	 */
> >> +}
> >> +
> >>  static int cm32181_probe(struct i2c_client *client,
> >>  			const struct i2c_device_id *id)
> >>  {
> >>  	struct cm32181_chip *cm32181;
> >>  	struct iio_dev *indio_dev;
> >>  	int ret;
> >> +	const struct acpi_device_id *a_id;
> >>
> >>  	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*cm32181));
> >>  	if (!indio_dev) {
> >> @@ -322,11 +340,55 @@ static int cm32181_probe(struct i2c_client
> >> *client,
> >>  	indio_dev->name = id->name;
> >>  	indio_dev->modes = INDIO_DIRECT_MODE;
> >>
> >> +	/* Lookup for chip ID from platform, ACPI or of device table */
> >> +	if (id) {
> >> +		cm32181->chip_id = id->driver_data;
> >> +	} else if (ACPI_COMPANION(&client->dev)) {
> >> +		a_id = acpi_match_device(client->dev.driver->acpi_match_table,
> >> +					 &client->dev);
> >> +		if (!a_id)
> >> +			return -ENODEV;
> >> +
> >> +		cm32181->chip_id = (int)a_id->driver_data;
> >> +	} else if (client->dev.of_node) {
> >> +		cm32181->chip_id = (int)of_device_get_match_data(&client->dev);
> >> +		if (!cm32181->chip_id)
> >> +			return -ENODEV;
> >> +	} else {
> >> +		return -ENODEV;
> >> +	}
> >> +
> >> +	if (cm32181->chip_id == CM3218_ID) {
> >> +		/* cm3218 chip require an ARA device on his adapter */
> >> +		ret = i2c_require_smbus_alert(client);
> >> +		if (ret < 0)
> >> +			return ret;
> >
> > This should be apparent to the core as we have an alert callback set.
> 
> Yes, I think that i2c_require_smbus_alert() may be handle by the core if
> the driver have an alert callback defined and/or the client is flagged
> with I2C_CLIENT_ALERT. I think this can be done in i2c_device_probe().
> 
> > I suppose there may be nasty corner cases where the alert can be cleared
> > without acknowledging explicitly (I think some of the Maxim parts do
> > this).
> >
> >> +
> >> +		/* If irq is given, register it with the smbus alert driver */
> >> +		if (client->irq > 0) {
> >> +			ret = i2c_smbus_alert_add_irq(client, client->irq);
> >> +			if (ret < 0)
> >> +				return ret;
> >> +		} else {
> >> +			/*
> >> +			 * if no irq is given, acknownledge initial ARA
> >> +			 * event so cm32181_reg_init() will not fail.
> >
> > Logic here has me a bit confused.  If we don't have an irq then it
> > could be the i2c master already registered the relevant support.
> 
> Yes this code can be removed for now, as all board I have found using this
> chip enumerated by ACPI define an interrupt. (Asus T100, Chuwi Hi8, Lenovo
> Carbon X1, Acer Aspire Switch 10).
> 
> > Now smbalert is IIRC a level interrupt (stays high until all sources
> > have been dealt with) so it should have fired when enabled and be
> > continuously firing until someone deals with it (which is decidedly nasty)
> 
> The smbus_alert driver will always deal with each alert on the bus,
> acknowledging each device until interrupt line is cleared. It don't care
> if there are drivers handling the alert or not.
> >
> > Anyhow to my mind that core irq should be masked until someone says
> > they are ready to handle it.
> >
> > Upshot I think is that any device which can send ARA before driver
> > is loaded is inherently horribly broken if that alert line is shared
> > with other devices as then even enabling only on first
> > device saying it handles an ARA doesn't work. Oh goody.
> 
> This is the case of cm3218. Irq line is stuck low at power-on until the
> first read of alert address on that chip. The driver just can't access
> register until the alert is cleared. So It need the smbus alert process to
> be fired before initializing the device.
If another driver probes first and hence the interrupt is enabled
we are in an interrupt storm territory.  Oh goody.

> 
> There is another problem, the treaded irq hander sometime is not run
> before cm32181_reg_init() is called. So in that case , I think we need to
> defer the probe and retry later.

Be more lazy and sleep a bit?

> >
> > Anyhow, is this path tested?  I.e. did you make your master
> > driver register the ara support?
> 
> No, So I will remove that in next version.
> 
> >
> >> +			 */
> >> +			ret = i2c_smbus_alert_event(client);
> >> +			if (ret)
> >> +				return ret;
> >> +		}
> >> +	}
> >> +
> >>  	ret = cm32181_reg_init(cm32181);
> >>  	if (ret) {
> >>  		dev_err(&client->dev,
> >>  			"%s: register init failed\n",
> >>  			__func__);
> >> +
> > I would use a goto and unify the unwind of this in an erro rblock at
> > the end fo the driver.
> >> +		if (cm32181->chip_id == CM3218_ID && client->irq)
> >> +			i2c_smbus_alert_free_irq(client, client->irq);
> >> +
> >>  		return ret;
> >>  	}
> >>
> >> @@ -335,32 +397,58 @@ static int cm32181_probe(struct i2c_client
> >> *client,
> >>  		dev_err(&client->dev,
> >>  			"%s: regist device failed\n",
> >>  			__func__);
> >> +
> >> +		if (cm32181->chip_id == CM3218_ID && client->irq)
> >> +			i2c_smbus_alert_free_irq(client, client->irq);
> >> +
> >>  		return ret;
> >>  	}
> >>
> >>  	return 0;
> >>  }
> >>
> >> +static int cm32181_remove(struct i2c_client *client)
> >> +{
> >> +	struct cm32181_chip *cm32181 = i2c_get_clientdata(client);
> >> +
> >> +	if (cm32181->chip_id == CM3218_ID && client->irq)
> >> +		i2c_smbus_alert_free_irq(client, client->irq);
> >> +
> >> +	return 0;
> >> +}
> >> +
> >>  static const struct i2c_device_id cm32181_id[] = {
> >> -	{ "cm32181", 0 },
> >> +	{ "cm32181", CM32181_ID },
> >> +	{ "cm3218", CM3218_ID },
> >>  	{ }
> >>  };
> >>
> >>  MODULE_DEVICE_TABLE(i2c, cm32181_id);
> >>
> >>  static const struct of_device_id cm32181_of_match[] = {
> >> -	{ .compatible = "capella,cm32181" },
> >> +	{ .compatible = "capella,cm32181", (void *)CM32181_ID },
> >> +	{ .compatible = "capella,cm3218",  (void *)CM3218_ID },
> >>  	{ }
> >>  };
> >>  MODULE_DEVICE_TABLE(of, cm32181_of_match);
> >>
> >> +static const struct acpi_device_id cm32181_acpi_match[] = {
> >> +	{ "CPLM3218", CM3218_ID },
> >> +	{ }
> >> +};
> >> +
> >> +MODULE_DEVICE_TABLE(acpi, cm32181_acpi_match);
> >> +
> >>  static struct i2c_driver cm32181_driver = {
> >>  	.driver = {
> >>  		.name	= "cm32181",
> >>  		.of_match_table = of_match_ptr(cm32181_of_match),
> >> +		.acpi_match_table = ACPI_PTR(cm32181_acpi_match),
> >>  	},
> >>  	.id_table       = cm32181_id,
> >>  	.probe		= cm32181_probe,
> >> +	.remove		= cm32181_remove,
> >> +	.alert		= cm3218_alert,
> >>  };
> >>
> >>  module_i2c_driver(cm32181_driver);
> >
> >
> 
> 

--
To unsubscribe from this list: send the line "unsubscribe linux-acpi" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Linux IBM ACPI]     [Linux Power Management]     [Linux Kernel]     [Linux Laptop]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Video 4 Linux]     [Device Mapper]     [Linux Resources]

  Powered by Linux