From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: References: <1395773538-2649-1-git-send-email-ktsai@capellamicro.com> <7781785490BB4EA78BBDC2E5DB00E920@GustyD430> Message-ID: <1395809544.53200.YahooMailNeo@web122006.mail.ne1.yahoo.com> Date: Tue, 25 Mar 2014 21:52:24 -0700 (PDT) From: Kevin Tsai Reply-To: Kevin Tsai Subject: Re: [PATCH V3 1/1] iio: add Capella cm3218x ambient light sensor driver. To: Peter Meerwald Cc: Jonathan Cameron , "linux-iio@vger.kernel.org" In-Reply-To: MIME-Version: 1.0 Content-Type: multipart/alternative; boundary="-1192396609-1770039525-1395809544=:53200" List-ID: ---1192396609-1770039525-1395809544=:53200 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable Hi Peter,=0A=0AThanks for your explanation. =C2=A0I will follow your advise= and resubmit.=0A=0AHave a nice day.=0A=0AKevin Tsai=0A03/25/14=0A=0A=0A___= _____________________________=0A From: Peter Meerwald = =0ATo: Kevin Tsai =0ACc: Jonathan Cameron ; linux-iio@vger.kernel.org =0ASent: Tuesday, March 25, 2014 5:40= PM=0ASubject: Re: [PATCH V3 1/1] iio: add Capella cm3218x ambient light se= nsor driver.=0A =0A=0A=0A> (1). Not all machines use ACPI method.=C2=A0 For= example, one of my machines=0A> loads driver by manually.=0A=0Athe driver = fails to compile if CONFIG_ACPI is not defined, e.g. on ARM =0Aplatform, he= nce I think 'depends on ACPI' is appropriate in Kconfig=0A=0Amaybe you coul= d #ifdef certain function in the driver if ACPI is not =0Aavailable in orde= r to support devices without ACPI=0A=0Adrivers/iio/light/cm3218x.c: In func= tion =E2=80=98cm3218x_acpi_get_cpm_info=E2=80=99:=0Adrivers/iio/light/cm321= 8x.c:195:2: error: unknown type name =E2=80=98acpi_handle=E2=80=99=0Adriver= s/iio/light/cm3218x.c:196:9: error: variable =E2=80=98buffer=E2=80=99 has = =0Ainitializer but incomplete type=0A=C2=A0 structacpi_buffer buffer =3D {A= CPI_ALLOCATE_BUFFER, NULL};=0A=C2=A0 =C2=A0 =C2=A0 =C2=A0 ^=0Adrivers/iio/= light/cm3218x.c:196:31: error: =E2=80=98ACPI_ALLOCATE_BUFFER=E2=80=99 =0Aun= declared (first use in this function)=0A=C2=A0 structacpi_buffer buffer =3D= {ACPI_ALLOCATE_BUFFER, NULL};=0A=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ^=0A...=0A= =0A> (2). About the ARA register, some of sensors use SMBus ARA, others use= I2C=0A> to report interrupt.=C2=A0 I need to call i2c_new_dummy() to read = the ARA=0A> register to clean interrupt.=C2=A0 Otherwise, device may be blo= cked by SMBus=0A> protocol.=C2=A0 In the _remove(), I have to call i2c_unre= gister_device(chip->ara).=0A> Otherwise, I cannot get the handler for ARA w= hen I reload this module.=0A=0Amy concern is that if an error occurs in _pr= obe() after calling =0Ai2c_new_dummy(), the matching i2c_unregister_device(= chip->ara) is missing=0A=0Aregards, p.=0A=0A> ----- Original Message ----- = From: "Peter Meerwald" =0A> To: "Kevin Tsai" =0A> Cc: "Jonathan Cameron" ; =0A> Sent: Tuesday, March 25, 2014 16:39=0A> Subject: Re: [PAT= CH V3 1/1] iio: add Capella cm3218x ambient light sensor=0A> driver.=0A> = =0A> =0A> > =0A> > > Add Capella Microsystem CM3218X family Ambient Light S= ensor IIO driver.=0A> > > This driver will convert raw data to lux value.= =C2=A0 Default parameters are=0A> > > for reference only.=C2=A0 It will det= ect ACPI table to load per-system=0A> > > manufacturing=0A> > > parameters.= =0A> > > =0A> > > V2: Follow Jonathan Cameron's advise to separate device i= d.=0A> > > Also, follow Peter Meerwald's advise to correct whitespace, pref= ix, and,=0A> > > comments.=0A> > > =0A> > > V3: Follow Peter Meerwald's adv= ise to create a separated i2c_client=0A> > > handler to=0A> > > access SMBu= s ARA register.=0A> > =0A> > couple of minor comments...=0A> > =0A> > > Sig= ned-off-by: Kevin Tsai =0A> > > ---=0A> > >=C2=A0 .= ../devicetree/bindings/i2c/trivial-devices.txt=C2=A0 =C2=A0 |=C2=A0 1 +=0A= > > >=C2=A0 drivers/iio/light/Kconfig=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 |=C2=A0 11 +=0A> > >= =C2=A0 drivers/iio/light/Makefile=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 |=C2=A0 1 +=0A> > >=C2=A0 drive= rs/iio/light/cm3218x.c=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 | 745=0A> > > +++++++++++++++++++++=0A> > >= =C2=A0 4 files changed, 758 insertions(+)=0A> > >=C2=A0 create mode 100644 = drivers/iio/light/cm3218x.c=0A> > > =0A> > > diff --git a/Documentation/dev= icetree/bindings/i2c/trivial-devices.txt=0A> > > b/Documentation/devicetree= /bindings/i2c/trivial-devices.txt=0A> > > index 1a1ac2e..c3c869b 100644=0A>= > > --- a/Documentation/devicetree/bindings/i2c/trivial-devices.txt=0A> > = > +++ b/Documentation/devicetree/bindings/i2c/trivial-devices.txt=0A> > > @= @ -17,6 +17,7 @@ at,24c08 i2c serial eeprom=C2=A0 (24cxx)=0A> > >=C2=A0 atm= el,24c02 i2c serial eeprom=C2=A0 (24cxx)=0A> > >=C2=A0 atmel,at97sc3204t i2= c trusted platform module (TPM)=0A> > >=C2=A0 capella,cm32181 CM32181: Ambi= ent Light Sensor=0A> > > +capella,cm3218x CM3218X: Ambient Light Sensor=0A>= > >=C2=A0 catalyst,24c32 i2c serial eeprom=0A> > >=C2=A0 dallas,ds1307 64 = x 8, Serial, I2C Real-Time Clock=0A> > >=C2=A0 dallas,ds1338 I2C RTC with 5= 6-Byte NV RAM=0A> > > diff --git a/drivers/iio/light/Kconfig b/drivers/iio/= light/Kconfig=0A> > > index d12b2a0..97a341a 100644=0A> > > --- a/drivers/i= io/light/Kconfig=0A> > > +++ b/drivers/iio/light/Kconfig=0A> > > @@ -38,6 += 38,17 @@ config CM32181=0A> > >=C2=A0 To compile this driver as a module, c= hoose M here:=0A> > >=C2=A0 the module will be called cm32181.=0A> > > =0A>= > > +config CM3218X=0A> > > + depends on I2C=0A> > =0A> > depends on I2C &= & ACPI ???=0A> > =0A> > > + tristate "CM3218X driver"=0A> > > + help=0A> > = > + Say Y here if you use cm3218x.=0A> > > + This option enables ambient li= ght sensor using=0A> > > + Capella cm3218x device driver.=0A> > > +=0A> > >= + To compile this driver as a module, choose M here:=0A> > > + the module = will be called cm3218x.=0A> > > +=0A> > >=C2=A0 config CM36651=0A> > >=C2= =A0 depends on I2C=0A> > >=C2=A0 tristate "CM36651 driver"=0A> > > diff --g= it a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile=0A> > > index = 60e35ac..a506c23 100644=0A> > > --- a/drivers/iio/light/Makefile=0A> > > ++= + b/drivers/iio/light/Makefile=0A> > > @@ -6,6 +6,7 @@=0A> > >=C2=A0 obj-$(= CONFIG_ADJD_S311) +=3D adjd_s311.o=0A> > >=C2=A0 obj-$(CONFIG_APDS9300) += =3D apds9300.o=0A> > >=C2=A0 obj-$(CONFIG_CM32181) +=3D cm32181.o=0A> > > += obj-$(CONFIG_CM3218X) +=3D cm3218x.o=0A> > >=C2=A0 obj-$(CONFIG_CM36651) += =3D cm36651.o=0A> > >=C2=A0 obj-$(CONFIG_GP2AP020A00F) +=3D gp2ap020a00f.o= =0A> > >=C2=A0 obj-$(CONFIG_HID_SENSOR_ALS) +=3D hid-sensor-als.o=0A> > > d= iff --git a/drivers/iio/light/cm3218x.c b/drivers/iio/light/cm3218x.c=0A> >= > new file mode 100644=0A> > > index 0000000..efd0d0a=0A> > > --- /dev/nul= l=0A> > > +++ b/drivers/iio/light/cm3218x.c=0A> > > @@ -0,0 +1,745 @@=0A> >= > +/*=0A> > > + * Copyright (C) 2014 Capella Microsystems Inc.=0A> > > + *= Author: Kevin Tsai =0A> > > + *=0A> > > + * This p= rogram is free software; you can redistribute it and/or modify=0A> > > it= =0A> > > + * under the terms of the GNU General Public License version 2, a= s=0A> > > published=0A> > > + * by the Free Software Foundation.=0A> > > + = *=0A> > > + * Special thanks SrinivasPandruvada=0A> > > =0A> > > + * help to add ACPI support.=0A> > > + *=0A> > = > + */=0A> > > +=0A> > > +#include =0A> > > +#include =0A> > > +#include =0A> > > +#include = =0A> > > +#include =0A> > > +#include = =0A> > > +#include =0A> > > +#include =0A> > > +#include =0A> > > +#include =0A> > > +#include =0A> > > +#include =0A> > > +=0A> > > +/* Registers Address */=0A> > > +#define CM3218X_REG_A= DDR_CMD 0x00=0A> > > +#define CM3218X_REG_ADDR_WH 0x01=0A> > > +#define CM3= 218X_REG_ADDR_WL 0x02=0A> > > +#define CM3218X_REG_ADDR_TEST 0x03=0A> > > += #define CM3218X_REG_ADDR_ALS 0x04=0A> > > +#define CM3218X_REG_ADDR_STATUS = 0x06=0A> > > +#define CM3218X_REG_ADDR_ID 0x07=0A> > > +=0A> > > +/* Number= of Configurable Registers */=0A> > > +#define CM3218X_CONF_REG_NUM 16=0A> = > > +=0A> > > +/* CMD register */=0A> > > +#define CM3218X_CMD_ALS_DISABLE = BIT(0)=0A> > > +#define CM3218X_CMD_ALS_INT_EN BIT(1)=0A> > > +#define CM32= 18X_CMD_ALS_THRES_WINDOW BIT(2)=0A> > > +=0A> > > +#define CM3218X_CMD_ALS_= PERS_SHIFT 4=0A> > > +#define CM3218X_CMD_ALS_PERS_MASK (0x03 << CM3218X_CM= D_ALS_PERS_SHIFT)=0A> > > +#define CM3218X_CMD_ALS_PERS_DEFAULT (0x01 <<=0A= > > > CM3218X_CMD_ALS_PERS_SHIFT)=0A> > > +=0A> > > +#define CM3218X_CMD_AL= S_IT_SHIFT 6=0A> > > +#define CM3218X_CMD_ALS_IT_MASK (0x0F << CM3218X_CMD_= ALS_IT_SHIFT)=0A> > > +#define CM3218X_CMD_ALS_IT_DEFAULT (0x01 << CM3218X_= CMD_ALS_IT_SHIFT)=0A> > > +=0A> > > +#define CM3218X_CMD_ALS_HS_SHIFT 11=0A= > > > +#define CM3218X_CMD_ALS_HS_MASK (0x01 << CM3218X_CMD_ALS_HS_SHIFT)= =0A> > > +#define CM3218X_CMD_ALS_HS_DEFAULT (0x00 << CM3218X_CMD_ALS_HS_SH= IFT)=0A> > > +=0A> > > +#define CM3218X_CMD_DEFAULT (CM3218X_CMD_ALS_THRES_= WINDOW |\=0A> > > + CM3218X_CMD_ALS_PERS_DEFAULT |\=0A> > > + CM3218X_CMD_A= LS_IT_DEFAULT |\=0A> > > + CM3218X_CMD_ALS_HS_DEFAULT)=0A> > > +=0A> > > +#= define CM3218X_WH_DEFAULT 0xFFFF=0A> > > +#define CM3218X_WL_DEFAULT 0x0000= =0A> > > +=0A> > > +#define CM3218X_CALIBSCALE_DEFAULT 100000=0A> > > +#def= ine CM3218X_CALIBSCALE_RESOLUTION 100000=0A> > > +#define CM3218X_MLUX_PER_= LUX 1000=0A> > > +#define CM3218X_THRESHOLD_PERCENT 10 /* 10 percent */=0A>= > > +=0A> > > +#define CM3218X_ARA 0x0C=0A> > > +=0A> > > +/* CM3218X fami= ly */=0A> > > +enum {=0A> > > + cm3218,=0A> > > + cm32181,=0A> > > + cm3218= 2=0A> > > +};=0A> > > +=0A> > > +/* CM3218 Family */=0A> > > +#define CM321= 8_MLUX_PER_BIT_DEFAULT 5 /* Depend on system */=0A> > > +#define CM3218_MLU= X_PER_BIT_BASE_IT 800000=0A> > > +static const int CM3218_als_it_bits[] =3D= {0, 1, 2, 3};=0A> > > +static const int CM3218_als_it_values[] =3D {100000= , 200000, 400000,=0A> > > 800000};=0A> > > +=0A> > > +/* CM32181 Family */= =0A> > > +#define CM32181_MLUX_PER_BIT_DEFAULT 5=0A> > > +#define CM32181_M= LUX_PER_BIT_BASE_IT 800000=0A> > > +static const int CM32181_als_it_bits[] = =3D {12, 8, 0, 1, 2, 3};=0A> > > +static const int CM32181_als_it_values[] = =3D {=0A> > > + 25000, 50000, 100000, 200000, 400000, 800000};=0A> > > +=0A= > > > +struct cm3218x_als_info {=0A> > > + u32 id;=0A> > > + int int_type;= =0A> > > +#define CM3218X_INT_TYPE_SMBUS 0=0A> > > +#define CM3218X_INT_TYP= E_I2C 1=0A> > > + int regs_bmp;=0A> > > + int calibscale;=0A> > > + int mlu= x_per_bit;=0A> > > + int mlux_per_bit_base_it;=0A> > > + const int *als_it_= bits;=0A> > > + const int *als_it_values;=0A> > > + int num_als_it;=0A> > >= + int als_raw;=0A> > > +};=0A> > > +=0A> > > +static struct cm3218x_als_in= fo cm3218_info =3D {=0A> > > + 3218, CM3218X_INT_TYPE_SMBUS, 0x0F, CM3218X_= CALIBSCALE_DEFAULT,=0A> > > + CM3218_MLUX_PER_BIT_DEFAULT, CM3218_MLUX_PER_= BIT_BASE_IT,=0A> > > + CM3218_als_it_bits, CM3218_als_it_values,=0A> > > + = ARRAY_SIZE(CM3218_als_it_bits), 0};=0A> > =0A> > const?=0A> > =0A> > > +=0A= > > > +static struct cm3218x_als_info cm32181_info =3D {=0A> > > + 32181, C= M3218X_INT_TYPE_I2C, 0x0F, CM3218X_CALIBSCALE_DEFAULT,=0A> > > + CM32181_ML= UX_PER_BIT_DEFAULT, CM32181_MLUX_PER_BIT_BASE_IT,=0A> > > + CM32181_als_it_= bits, CM32181_als_it_values,=0A> > > + ARRAY_SIZE(CM32181_als_it_bits), 0};= =0A> > > +=0A> > > +static struct cm3218x_als_info cm32182_info =3D {=0A> >= > + 32182, CM3218X_INT_TYPE_I2C, 0x0F, CM3218X_CALIBSCALE_DEFAULT,=0A> > >= + CM32181_MLUX_PER_BIT_DEFAULT, CM32181_MLUX_PER_BIT_BASE_IT,=0A> > > + CM= 32181_als_it_bits, CM32181_als_it_values,=0A> > > + ARRAY_SIZE(CM32181_als_= it_bits), 0};=0A> > > +=0A> > > +struct cm3218x_chip {=0A> > > + struct i2c= _client *client;=0A> > > + struct i2c_client *ara;=0A> > > + structmutex lo= ck;=0A> > > + u16 conf_regs[CM3218X_CONF_REG_NUM];=0A> > > + struct cm3218x= _als_info *als_info;=0A> > > +};=0A> > > +=0A> > > +static int cm3218x_get_= lux(struct cm3218x_chip *chip);=0A> > > +static int cm3218x_threshold_updat= e(struct cm3218x_chip *chip, int=0A> > > percent);=0A> > > +static int cm32= 18x_read_als_it(struct cm3218x_chip *chip, int *val2);=0A> > > +=0A> > > +/= **=0A> > > + * cm3218x_interrupt_config() - Enable/Disable CM3218X interrup= t=0A> > > + * @chip: pointer of struct cm3218x.=0A> > > + * @enable: 0 to d= isable; otherwise to enable=0A> > > + *=0A> > > + * Config CM3218X interrup= t control bit.=0A> > > + *=0A> > > + * Return: 0 for success; otherwise for= error code.=0A> > > + */=0A> > > +static int cm3218x_interrupt_config(stru= ct cm3218x_chip *chip, int=0A> > > enable)=0A> > > +{=0A> > > + struct i2c_= client *client =3D chip->client;=0A> > > + struct cm3218x_als_info *als_inf= o =3D chip->als_info;=0A> > > + int status;=0A> > > +=0A> > > + if (!als_in= fo)=0A> > > + return -ENODEV;=0A> > > +=0A> > > + /* Force to clean interru= pt */=0A> > > + if (als_info->int_type =3D=3D CM3218X_INT_TYPE_I2C) {=0A> >= > + status =3D i2c_smbus_read_word_data(client,=0A> > > + CM3218X_REG_ADDR= _STATUS);=0A> > > + if (status < 0)=0A> > > + als_info->int_type =3D CM3218= X_INT_TYPE_SMBUS;=0A> > > + }=0A> > > + if (chip->ara && als_info->int_type= =3D=3D CM3218X_INT_TYPE_SMBUS)=0A> > > + i2c_smbus_read_byte(chip->ara);= =0A> > > +=0A> > > + if (enable)=0A> > > + chip->conf_regs[CM3218X_REG_ADDR= _CMD] |=3D=0A> > > + CM3218X_CMD_ALS_INT_EN;=0A> > > + else=0A> > > + chip-= >conf_regs[CM3218X_REG_ADDR_CMD] &=3D=0A> > > + ~CM3218X_CMD_ALS_INT_EN;=0A= > > > +=0A> > > + status =3D i2c_smbus_write_word_data(client, CM3218X_REG_= ADDR_CMD,=0A> > > + chip->conf_regs[CM3218X_REG_ADDR_CMD]);=0A> > > +=0A> >= > + if (status < 0)=0A> > > + return -ENODEV;=0A> > > +=0A> > > + return s= tatus;=0A> > > +}=0A> > > +=0A> > > +/**=0A> > > + * cm3218x_acpi_get_cpm_i= nfo() - Get CPM object from ACPI=0A> > > + * @client pointer of struct i2c_= client.=0A> > > + * @obj_name pointer of ACPI object name.=0A> > > + * @cou= nt maximum size of return array.=0A> > > + * @vals pointer of array for ret= urn elements.=0A> > > + *=0A> > > + * Convert ACPICPM table to array. Speci= al thanks SrinivasPandruvada's=0A> > > + * help to implement this routine.= =0A> > > + *=0A> > > + * Return: -ENODEV for fail.=C2=A0 Otherwise is numbe= r of elements.=0A> > > + */=0A> > > +static int cm3218x_acpi_get_cpm_info(s= truct i2c_client *client, char=0A> > > *obj_name,=0A> > > + int count, u64 = *vals)=0A> > > +{=0A> > > + acpi_handle handle;=0A> > > + structacpi_buffer= buffer =3D {ACPI_ALLOCATE_BUFFER, NULL};=0A> > > + int i;=0A> > > + acpi_s= tatus status;=0A> > > + union acpi_object *cpm;=0A> > > +=0A> > > + handle = =3D ACPI_HANDLE(&client->dev);=0A> > > + if (!handle)=0A> > > + return -ENO= DEV;=0A> > > +=0A> > > + status =3D acpi_evaluate_object(handle, obj_name, = NULL, &buffer);=0A> > > + if (ACPI_FAILURE(status)) {=0A> > > + dev_err(&cl= ient->dev, "object %s not found\n", obj_name);=0A> > > + return -ENODEV;=0A= > > > + }=0A> > > +=0A> > > + cpm =3D buffer.pointer;=0A> > > + for (i =3D = 0; i < cpm->package.count && i < count; ++i) {=0A> > > + union acpi_object = *elem;=0A> > > + elem =3D &(cpm->package.elements[i]);=0A> > > + vals[i] = =3D elem->integer.value;=0A> > > + }=0A> > > +=0A> > > + kfree(buffer.point= er);=0A> > > +=0A> > > + return cpm->package.count;=0A> > > +}=0A> > > +=0A= > > > +/**=0A> > > + * cm3218x_reg_init() - Initialize CM3218X registers=0A= > > > + * @chip: pointer of struct cm3218x.=0A> > > + *=0A> > > + * Initial= ize CM3218X ambient light sensor register to default values.=0A> > > + *=0A= > > > +=C2=A0 Return: 0 for success; otherwise for error code.=0A> > > + */= =0A> > > +static int cm3218x_reg_init(struct cm3218x_chip *chip)=0A> > > +{= =0A> > > + struct i2c_client *client =3D chip->client;=0A> > > + int i;=0A>= > > + s32 ret;=0A> > > + int cpm_elem_count;=0A> > > + u64 cpm_elems[20];= =0A> > > + struct cm3218x_als_info *als_info;=0A> > > +=0A> > > + /* Defaul= t device */=0A> > > + chip->als_info =3D &cm3218_info;=0A> > > + chip->conf= _regs[CM3218X_REG_ADDR_CMD] =3D CM3218X_CMD_DEFAULT;=0A> > > + chip->conf_r= egs[CM3218X_REG_ADDR_WH] =3D CM3218X_WH_DEFAULT;=0A> > > + chip->conf_regs[= CM3218X_REG_ADDR_WL] =3D CM3218X_WL_DEFAULT;=0A> > > +=0A> > > + /* Disable= interrupt */=0A> > > + cm3218x_interrupt_config(chip, 0);=0A> > > +=0A> > = > + /* Disable Test Mode */=0A> > > + i2c_smbus_write_word_data(client, CM3= 218X_REG_ADDR_TEST, 0x0000);=0A> > > +=0A> > > + /* Disable device */=0A> >= > + i2c_smbus_write_word_data(client, CM3218X_REG_ADDR_CMD,=0A> > > + CM32= 18X_CMD_ALS_DISABLE);=0A> > > +=0A> > > + /* Identify device */=0A> > > + r= et =3D i2c_smbus_read_word_data(client, CM3218X_REG_ADDR_ID);=0A> > > + if = (ret < 0)=0A> > > + return ret;=0A> > > + switch (ret & 0xFF) {=0A> > > + c= ase 0x18:=0A> > > + als_info =3D chip->als_info =3D &cm3218_info;=0A> > > += if (ret & 0x0800)=0A> > > + als_info->int_type =3D CM3218X_INT_TYPE_I2C;= =0A> > > + else=0A> > > + als_info->int_type =3D CM3218X_INT_TYPE_SMBUS;=0A= > > > + break;=0A> > > + case 0x81:=0A> > > + als_info =3D chip->als_info = =3D &cm32181_info;=0A> > > + break;=0A> > > + case 0x82:=0A> > > + als_info= =3D chip->als_info =3D &cm32182_info;=0A> > > + break;=0A> > > + default:= =0A> > > + return -ENODEV;=0A> > > + }=0A> > > +=0A> > > + if (ACPI_HANDLE(= &client->dev)) {=0A> > > + /* Load from ACPI */=0A> > > + cpm_elem_count = =3D cm3218x_acpi_get_cpm_info(client, "CPM0",=0A> > > + ARRAY_SIZE(cpm_elem= s),=0A> > > + cpm_elems);=0A> > > + if (cpm_elem_count > 0) {=0A> > > + int= header_num =3D 3;=0A> > > + int reg_num =3D cpm_elem_count - header_num;= =0A> > > +=0A> > > + als_info->regs_bmp =3D cpm_elems[2];=0A> > > + for (i = =3D 0; i < reg_num; i++)=0A> > > + if (als_info->regs_bmp & (1< > >= + chip->conf_regs[i] =3D=0A> > > + cpm_elems[header_num+i];=0A> > > + }=0A= > > > +=0A> > > + cpm_elem_count =3D cm3218x_acpi_get_cpm_info(client, "CPM= 1",=0A> > > + ARRAY_SIZE(cpm_elems),=0A> > > + cpm_elems);=0A> > > + if (cp= m_elem_count > 0) {=0A> > > + als_info->mlux_per_bit =3D (int)cpm_elems[0] = / 100;=0A> > > + als_info->calibscale =3D (int)cpm_elems[1];=0A> > > + }=0A= > > > + }=0A> > > +=0A> > > + /* Force to disable interrupt */=0A> > > + ch= ip->conf_regs[CM3218X_REG_ADDR_CMD] &=3D ~CM3218X_CMD_ALS_INT_EN;=0A> > > += =0A> > > + /* Initialize registers */=0A> > > + for (i =3D 0; i < CM3218X_C= ONF_REG_NUM; i++) {=0A> > > + if (als_info->regs_bmp & (1< > > + = ret =3D i2c_smbus_write_word_data(client, i,=0A> > > + chip->conf_regs[i]);= =0A> > > + if (ret < 0)=0A> > > + return ret;=0A> > > + }=0A> > > + }=0A> >= > +=0A> > > + return 0;=0A> > > +}=0A> > > +=0A> > > +/**=0A> > > + *=C2= =A0 cm3218x_read_als_it() - Get sensor integration time (ms)=0A> > > + *=C2= =A0 @chip: pointer of struct cm3218x=0A> > > + *=C2=A0 @val2: pointer of in= t to load the als_it value.=0A> > > + *=0A> > > + *=C2=A0 Report the curren= t integartion time in milliseconds.=0A> > =0A> > integration=0A> > =0A> > >= + *=0A> > > + *=C2=A0 Return: IIO_VAL_INT_PLUS_MICRO for success, otherwis= e -EINVAL.=0A> > > + */=0A> > > +static int cm3218x_read_als_it(struct cm32= 18x_chip *chip, int *val2)=0A> > > +{=0A> > > + struct cm3218x_als_info *al= s_info =3D chip->als_info;=0A> > > + u16 als_it;=0A> > > + int i;=0A> > > += =0A> > > + als_it =3D chip->conf_regs[CM3218X_REG_ADDR_CMD];=0A> > > + als_= it &=3D CM3218X_CMD_ALS_IT_MASK;=0A> > > + als_it >>=3D CM3218X_CMD_ALS_IT_= SHIFT;=0A> > > + for (i =3D 0; i < als_info->num_als_it; i++) {=0A> > > + i= f (als_it =3D=3D als_info->als_it_bits[i]) {=0A> > > + *val2 =3D als_info->= als_it_values[i];=0A> > > + return IIO_VAL_INT_PLUS_MICRO;=0A> > > + }=0A> = > > + }=0A> > > +=0A> > > + return -EINVAL;=0A> > > +}=0A> > > +=0A> > > +/= **=0A> > > + * cm3218x_write_als_it() - Write sensor integration time=0A> >= > + * @chip: pointer of struct cm3218x.=0A> > > + * @val: integration time= in milliseconds.=0A> > > + *=0A> > > + * Convert integration time (ms) to = sensor value.=0A> > > + *=0A> > > + * Return: i2c_smbus_write_word_data com= mand return value.=0A> > > + */=0A> > > +static int cm3218x_write_als_it(st= ruct cm3218x_chip *chip, int val)=0A> > > +{=0A> > > + struct i2c_client *c= lient =3D chip->client;=0A> > > + struct cm3218x_als_info *als_info =3D chi= p->als_info;=0A> > > + u16 als_it;=0A> > > + int ret, i;=0A> > > +=0A> > > = + for (i =3D 0; i < als_info->num_als_it; i++)=0A> > > + if (val <=3D als_i= nfo->als_it_values[i])=0A> > > + break;=0A> > > + if (i >=3D als_info->num_= als_it)=0A> > > + i =3D als_info->num_als_it - 1;=0A> > > +=0A> > > + als_i= t =3D als_info->als_it_bits[i];=0A> > > + als_it <<=3D CM3218X_CMD_ALS_IT_S= HIFT;=0A> > > +=0A> > > + mutex_lock(&chip->lock);=0A> > > + chip->conf_reg= s[CM3218X_REG_ADDR_CMD] &=3D=0A> > > + ~CM3218X_CMD_ALS_IT_MASK;=0A> > > + = chip->conf_regs[CM3218X_REG_ADDR_CMD] |=3D=0A> > > + als_it;=0A> > > + ret = =3D i2c_smbus_write_word_data(client, CM3218X_REG_ADDR_CMD,=0A> > > + chip-= >conf_regs[CM3218X_REG_ADDR_CMD]);=0A> > > + mutex_unlock(&chip->lock);=0A>= > > +=0A> > > + return ret;=0A> > > +}=0A> > > +=0A> > > +/**=0A> > > + * = cm3218x_get_lux() - report current lux value=0A> > > + * @chip: pointer of = struct cm3218x.=0A> > > + *=0A> > > + * Convert sensor raw data to lux.=C2= =A0 It depends on integration=0A> > > + * time and calibscale variable.=0A>= > > + *=0A> > > + * Return: Positive value is lux, otherwise is error code= .=0A> > > + */=0A> > > +static int cm3218x_get_lux(struct cm3218x_chip *chi= p)=0A> > > +{=0A> > > + struct i2c_client *client =3D chip->client;=0A> > >= + struct cm3218x_als_info *als_info =3D chip->als_info;=0A> > > + int ret;= =0A> > > + int als_it;=0A> > > + u64 tmp;=0A> > > +=0A> > > + /* Calculate = mlux per bit based on als_it */=0A> > > + ret =3D cm3218x_read_als_it(chip,= &als_it);=0A> > > + if (ret < 0)=0A> > > + return -EINVAL;=0A> > > + tmp = =3D (__force u64)als_info->mlux_per_bit;=0A> > > + tmp *=3D als_info->mlux_= per_bit_base_it;=0A> > > + tmp =3D div_u64(tmp, als_it);=0A> > > +=0A> > > = + /* Get als_raw */=0A> > > + if (!(chip->conf_regs[CM3218X_REG_ADDR_CMD] &= CM3218X_CMD_ALS_INT_EN))=0A> > > + als_info->als_raw =3D i2c_smbus_read_wo= rd_data(=0A> > > + client,=0A> > > + CM3218X_REG_ADDR_ALS);=0A> > > + if (a= ls_info->als_raw < 0)=0A> > > + return als_info->als_raw;=0A> > > +=0A> > >= + tmp *=3D als_info->als_raw;=0A> > > + tmp *=3D als_info->calibscale;=0A>= > > + tmp =3D div_u64(tmp, CM3218X_CALIBSCALE_RESOLUTION);=0A> > > + tmp = =3D div_u64(tmp, CM3218X_MLUX_PER_LUX);=0A> > > +=0A> > > + if (tmp > 0xFFF= F)=0A> > > + tmp =3D 0xFFFF;=0A> > > +=0A> > > + return (int)tmp;=0A> > > += }=0A> > > +=0A> > > +static int cm3218x_read_raw(struct iio_dev *indio_dev,= =0A> > > + struct iio_chan_spec const *chan,=0A> > > + int *val, int *val2,= long mask)=0A> > > +{=0A> > > + struct cm3218x_chip *chip =3D iio_priv(ind= io_dev);=0A> > > + struct cm3218x_als_info *als_info =3D chip->als_info;=0A= > > > + int ret;=0A> > > +=0A> > > + switch (mask) {=0A> > > + case IIO_CHA= N_INFO_PROCESSED:=0A> > > + ret =3D cm3218x_get_lux(chip);=0A> > > + if (re= t < 0)=0A> > > + return ret;=0A> > > + *val =3D ret;=0A> > > + return IIO_V= AL_INT;=0A> > > + case IIO_CHAN_INFO_CALIBSCALE:=0A> > > + *val =3D als_inf= o->calibscale;=0A> > > + return IIO_VAL_INT;=0A> > > + case IIO_CHAN_INFO_I= NT_TIME:=0A> > > + *val =3D 0;=0A> > > + ret =3D cm3218x_read_als_it(chip, = val2);=0A> > > + return ret;=0A> > > + }=0A> > > +=0A> > > + return -EINVAL= ;=0A> > > +}=0A> > > +=0A> > > +static int cm3218x_write_raw(struct iio_dev= *indio_dev,=0A> > > + struct iio_chan_spec const *chan,=0A> > > + int val,= int val2, long mask)=0A> > > +{=0A> > > + struct cm3218x_chip *chip =3D ii= o_priv(indio_dev);=0A> > > + struct cm3218x_als_info *als_info =3D chip->al= s_info;=0A> > > + long ms;=0A> > > +=0A> > > + switch (mask) {=0A> > > + ca= se IIO_CHAN_INFO_CALIBSCALE:=0A> > > + als_info->calibscale =3D val;=0A> > = > + return val;=0A> > > + case IIO_CHAN_INFO_INT_TIME:=0A> > > + ms =3D val= * 1000000 + val2;=0A> > > + return cm3218x_write_als_it(chip, (int)ms);=0A= > > > + }=0A> > > +=0A> > > + return -EINVAL;=0A> > > +}=0A> > > +=0A> > > = +/**=0A> > > + * cm3218x_get_it_available() - Get available ALS IT value=0A= > > > + * @dev: pointer of struct device.=0A> > > + * @attr: pointer of str= uct device_attribute.=0A> > > + * @buf: pointer of return string buffer.=0A= > > > + *=0A> > > + * Display the available integration time in millisecond= s.=0A> > > + *=0A> > > + * Return: string length.=0A> > > + */=0A> > > +sta= tic ssize_t cm3218x_get_it_available(struct device *dev,=0A> > > + struct d= evice_attribute *attr, char *buf)=0A> > > +{=0A> > > + struct cm3218x_chip = *chip =3D iio_priv(dev_to_iio_dev(dev));=0A> > > + struct cm3218x_als_info = *als_info =3D chip->als_info;=0A> > > + int i, len;=0A> > > +=0A> > > + for= (i =3D 0, len =3D 0; i < als_info->num_als_it; i++)=0A> > > + len +=3D scn= printf(buf + len, PAGE_SIZE - len, "%u.%06u ",=0A> > > + als_info->als_it_v= alues[i]/1000000,=0A> > > + als_info->als_it_values[i]%1000000);=0A> > > + = return len + scnprintf(buf + len, PAGE_SIZE - len, "\n");=0A> > > +}=0A> > = > +=0A> > > +/**=0A> > > + * cm3218x_threshold_update() - Update the thresh= old registers.=0A> > > + * @chip: pointer of struct cm3218x_chip.=0A> > > += * @percent: +/- percent.=0A> > > + *=0A> > > + * Based on the current ALS = value, tupdate the hi and low threshold=0A> > > registers.=0A> > =0A> > tup= date?=0A> > =0A> > > + *=0A> > > + * Return: 0 for success; otherwise for e= rror code.=0A> > > + */=0A> > > +static int cm3218x_threshold_update(struct= cm3218x_chip *chip, int=0A> > > percent)=0A> > > +{=0A> > > + struct i2c_c= lient *client =3D chip->client;=0A> > > + struct cm3218x_als_info *als_info= =3D chip->als_info;=0A> > > + int ret;=0A> > > + int wh, wl;=0A> > > +=0A>= > > + ret =3D als_info->als_raw =3D i2c_smbus_read_word_data(client,=0A> >= > + CM3218X_REG_ADDR_ALS);=0A> > > + if (ret < 0)=0A> > > + return ret;=0A= > > > +=0A> > > + wh =3D wl =3D ret;=0A> > > + ret *=3D percent;=0A> > > + = ret /=3D 100;=0A> > > + if (ret < 1)=0A> > > + ret =3D 1;=0A> > > + wh +=3D= ret;=0A> > > + wl -=3D ret;=0A> > > + if (wh > 65535)=0A> > > + wh =3D 655= 35;=0A> > > + if (wl < 0)=0A> > > + wl =3D 0;=0A> > > +=0A> > > + chip->con= f_regs[CM3218X_REG_ADDR_WH] =3D wh;=0A> > > + ret =3D i2c_smbus_write_word_= data(=0A> > > + client,=0A> > > + CM3218X_REG_ADDR_WH,=0A> > > + chip->conf= _regs[CM3218X_REG_ADDR_WH]);=0A> > > + if (ret < 0)=0A> > > + return ret;= =0A> > > +=0A> > > + chip->conf_regs[CM3218X_REG_ADDR_WL] =3D wl;=0A> > > += ret =3D i2c_smbus_write_word_data(=0A> > > + client,=0A> > > + CM3218X_REG= _ADDR_WL,=0A> > > + chip->conf_regs[CM3218X_REG_ADDR_WL]);=0A> > > +=0A> > = > + return ret;=0A> > > +}=0A> > > +=0A> > > +/**=0A> > > + * cm3218x_event= _handler() - Interrupt handling routine.=0A> > > + * @irq: irq number.=0A> = > > + * @private: pointer of void.=0A> > > + *=0A> > > + * Clean interrupt = and reset threshold registers.=0A> > > + *=0A> > > + * Return: IRQ_HANDLED.= =0A> > > + */=0A> > > +static irqreturn_t cm3218x_event_handler(int irq, vo= id *private)=0A> > > +{=0A> > > + struct iio_dev *dev_info =3D private;=0A>= > > + struct cm3218x_chip *chip =3D iio_priv(dev_info);=0A> > > + int ret;= =0A> > > +=0A> > > + mutex_lock(&chip->lock);=0A> > > +=0A> > > + /* Disabl= e interrupt */=0A> > > + ret =3D cm3218x_interrupt_config(chip, 0);=0A> > >= + if (ret < 0)=0A> > > + goto error_handler_unlock;=0A> > > +=0A> > > + /*= Update Hi/Lo windows */=0A> > > + ret =3D cm3218x_threshold_update(chip, C= M3218X_THRESHOLD_PERCENT);=0A> > > + if (ret < 0)=0A> > > + goto error_hand= ler_unlock;=0A> > > +=0A> > > + /* Enable interrupt */=0A> > > + ret =3D cm= 3218x_interrupt_config(chip, 1);=0A> > =0A> > no need to check ret value=0A= > > =0A> > > + if (ret < 0)=0A> > > + goto error_handler_unlock;=0A> > > += =0A> > > +error_handler_unlock:=0A> > > + mutex_unlock(&chip->lock);=0A> > = > + return IRQ_HANDLED;=0A> > > +}=0A> > > +=0A> > > +static conststruct ii= o_chan_spec cm3218x_channels[] =3D {=0A> > > + {=0A> > > + .type =3D IIO_LI= GHT,=0A> > > + .info_mask_separate =3D=0A> > > + BIT(IIO_CHAN_INFO_PROCESSE= D) |=0A> > > + BIT(IIO_CHAN_INFO_CALIBSCALE) |=0A> > > + BIT(IIO_CHAN_INFO_= INT_TIME),=0A> > > + }=0A> > > +};=0A> > > +=0A> > > +static IIO_DEVICE_ATT= R(in_illuminance_integration_time_available,=0A> > > + S_IRUGO, cm3218x_get= _it_available, NULL, 0);=0A> > > +=0A> > > +static struct attribute *cm3218= x_attributes[] =3D {=0A> > > + &iio_dev_attr_in_illuminance_integration_tim= e_available.dev_attr.attr,=0A> > > + NULL,=0A> > > +};=0A> > > +=0A> > > +s= tatic conststruct attribute_group cm3218x_attribute_group =3D {=0A> > > + .= attrs =3D cm3218x_attributes=0A> > > +};=0A> > > +=0A> > > +static conststr= uct iio_info cm3218x_info =3D {=0A> > > + .driver_module =3D THIS_MODULE,= =0A> > > + .read_raw =3D &cm3218x_read_raw,=0A> > > + .write_raw =3D &cm321= 8x_write_raw,=0A> > > + .attrs =3D &cm3218x_attribute_group,=0A> > > +};=0A= > > > +=0A> > > +static int cm3218x_probe(struct i2c_client *client,=0A> > = > + conststruct i2c_device_id *id)=0A> > > +{=0A> > > + struct cm3218x_chip= *chip;=0A> > > + struct iio_dev *indio_dev;=0A> > > + int ret;=0A> > > += =0A> > > + indio_dev =3D devm_iio_device_alloc(&client->dev, sizeof(*chip))= ;=0A> > > + if (!indio_dev) {=0A> > > + dev_err(&client->dev, "devm_iio_dev= ice_alloc failed\n");=0A> > > + return -ENOMEM;=0A> > > + }=0A> > > +=0A> >= > + chip =3D iio_priv(indio_dev);=0A> > > + i2c_set_clientdata(client, ind= io_dev);=0A> > > + chip->client =3D client;=0A> > > + chip->ara =3D i2c_new= _dummy(client->adapter, CM3218X_ARA);=0A> > > +=0A> > =0A> > there is no i2= c_unregister_device(chip->ara) in the error path below; but=0A> > i2c_unreg= ister_device(chip->ara) is called in _remove()=0A> > =0A> > > + mutex_init(= &chip->lock);=0A> > > + indio_dev->dev.parent =3D &client->dev;=0A> > > + i= ndio_dev->channels =3D cm3218x_channels;=0A> > > + indio_dev->num_channels = =3D ARRAY_SIZE(cm3218x_channels);=0A> > > + indio_dev->info =3D &cm3218x_in= fo;=0A> > > + if (id && id->name)=0A> > > + indio_dev->name =3D id->name;= =0A> > > + else=0A> > > + indio_dev->name =3D (char *)dev_name(&client->dev= );=0A> > > + indio_dev->modes =3D INDIO_DIRECT_MODE;=0A> > > +=0A> > > + re= t =3D cm3218x_reg_init(chip);=0A> > > + if (ret) {=0A> > > + dev_err(&clien= t->dev,=0A> > > + "%s: register init failed\n",=0A> > > + __func__);=0A> > = > + return ret;=0A> > > + }=0A> > > +=0A> > > + if (client->irq) {=0A> > > = + ret =3D request_threaded_irq(client->irq,=0A> > > + NULL,=0A> > > + cm321= 8x_event_handler,=0A> > > + IRQF_TRIGGER_FALLING | IRQF_ONESHOT,=0A> > > + = "cm3218x_event",=0A> > > + indio_dev);=0A> > > +=0A> > > + if (ret < 0) {= =0A> > > + dev_err(&client->dev, "irq request error %d\n",=0A> > > + -ret);= =0A> > > + goto error_disable_int;=0A> > > + }=0A> > > + }=0A> > > +=0A> > = > + ret =3D iio_device_register(indio_dev);=0A> > > + if (ret < 0) {=0A> > = > + dev_err(&client->dev,=0A> > > + "%s: regist device failed\n",=0A> > > += __func__);=0A> > > + goto error_free_irq;=0A> > > + }=0A> > > +=0A> > > + = if (client->irq) {=0A> > > + ret =3D cm3218x_threshold_update(chip, CM3218X= _THRESHOLD_PERCENT);=0A> > > + if (ret < 0)=0A> > > + goto error_free_irq;= =0A> > > +=0A> > > + ret =3D cm3218x_interrupt_config(chip, 1);=0A> > > + i= f (ret < 0)=0A> > > + goto error_free_irq;=0A> > > + }=0A> > > +=0A> > > + = return 0;=0A> > > +=0A> > > +error_free_irq:=0A> > > + free_irq(client->irq= , indio_dev);=0A> > =0A> > in _remove() free_irq() is only called when clie= nt->irq !=3D 0=0A> > =0A> > > +error_disable_int:=0A> > > + cm3218x_interru= pt_config(chip, 0);=0A> > > + return ret;=0A> > > +}=0A> > > +=0A> > > +sta= tic int cm3218x_remove(struct i2c_client *client)=0A> > > +{=0A> > > + stru= ct iio_dev *indio_dev =3D i2c_get_clientdata(client);=0A> > > + struct cm32= 18x_chip *chip =3D iio_priv(indio_dev);=0A> > > +=0A> > > + cm3218x_interru= pt_config(chip, 0);=0A> > > + if (chip->ara)=0A> > > + i2c_unregister_devic= e(chip->ara);=0A> > > + if (client->irq)=0A> > > + free_irq(client->irq, in= dio_dev);=0A> > > + iio_device_unregister(indio_dev);=0A> > > + return 0;= =0A> > > +}=0A> > > +=0A> > > +static conststruct i2c_device_id cm3218x_id[= ] =3D {=0A> > > + { "cm3218", cm3218},=0A> > > + { "cm32181", cm32181},=0A>= > > + { "cm32182", cm32182},=0A> > > + { }=0A> > > +};=0A> > > +=0A> > > += MODULE_DEVICE_TABLE(i2c, cm3218x_id);=0A> > > +=0A> > > +static conststruct= of_device_id cm3218x_of_match[] =3D {=0A> > > + { .compatible =3D "capella= ,cm3218" },=0A> > > + { }=0A> > > +};=0A> > > +=0A> > > +static conststruct= acpi_device_id cm3218x_acpi_match[] =3D {=0A> > > + { "CPLM3218", 0},=0A> >= > + {},=0A> > > +};=0A> > > +=0A> > > +MODULE_DEVICE_TABLE(acpi, cm3218x_a= cpi_match);=0A> > > +=0A> > > +static struct i2c_driver cm3218x_driver =3D = {=0A> > > + .driver =3D {=0A> > > + .name =3D "cm3218x",=0A> > > + .acpi_ma= tch_table =3D ACPI_PTR(cm3218x_acpi_match),=0A> > > + .of_match_table =3D o= f_match_ptr(cm3218x_of_match),=0A> > > + .owner =3D THIS_MODULE,=0A> > > + = },=0A> > > + .id_table =3D cm3218x_id,=0A> > > + .probe =3D cm3218x_probe,= =0A> > > + .remove =3D cm3218x_remove,=0A> > > +};=0A> > > +=0A> > > +modul= e_i2c_driver(cm3218x_driver);=0A> > > +=0A> > > +MODULE_AUTHOR("Kevin Tsai = ");=0A> > > +MODULE_DESCRIPTION("CM3218X ambient li= ght sensor driver");=0A> > > +MODULE_LICENSE("GPL");=0A> > > =0A> > =0A> > = -- =0A> > =0A> > Peter Meerwald=0A> > +43-664-2444418 (mobile)=0A> =0A=0A--= =0A=0APeter Meerwald=0A+43-664-2444418 (mobile) ---1192396609-1770039525-1395809544=:53200 Content-Type: text/html; charset=utf-8 Content-Transfer-Encoding: quoted-printable
Hi Peter,

Thanks for your explanation.  I will follow your = advise and resubmit.

Have a nice day.

Kevin Tsai
03/25/14


From: Peter Meerwald <pmeerw@pmeerw.net>
To: Kevin Tsai <ktsai@capellamicro.com>
Cc: Jonathan Cameron <jic23@kernel= .org>; linux-iio@vger.kernel.org
Sent: Tuesday, March 25, 2014 5:40 PM
Subject: Re: [PATCH V3 1/1] iio: add Capella= cm3218x ambient light sensor driver.


> (1). Not all machines use ACPI method.  For example, one of my machines> loads driver by manually.

the driver fails to compile if CONFIG_ACPI is not defined, e.g. on ARM
platform, hence I = think 'depends on ACPI' is ap= propriate in Kconfig

maybe you could #ifdef certain function in the d= river if ACPI is not
ava= ilable in order to support devices without ACPI

drivers/iio/light/cm3218x.c: In function =E2= =80=98cm3218x_acpi_get_cpm_info=E2=80=99:
drivers/iio= /light/cm3218x.c:195:2: error: unknown type name =E2=80=98acpi_handle=E2=80=99
drivers/iio/light/cm3= 218x.c:196:9: error: variable =E2=80=98buffer=E2=80=99 has
initializer but incomplete type
 = ; struct acpi_buffer buffer =3D {ACPI_ALLOCATE_BUFFER, NULL};
     = ;   ^
drivers/iio/light/cm3218x.c:196:31: error: =E2=80=98ACPI_ALLOCATE_BUFFER=E2=80=99
un= declared (first use in this function)
  struct acpi_buffer buffer =3D {ACPI_= ALLOCATE_BUFFER, NULL};
             =                 ^
...

&= gt; (2). About the ARA register, some of sensors use SMBus ARA, others use I2C
> to report interr= upt.  I need to call i2c_new_dummy() to read the ARA
> register = to clean interrupt.  Otherwise, device may be blocked by SMBus
> protocol.  In the _rem= ove(), I have to call i2c_unregiste= r_device(chip->ara).
> Otherwise, I cannot= get the handler for ARA when I reload this module.

my concern is th= at if an error occurs in _probe() after calling
i2c_new_dummy(), the ma= tching i2c_unregister_device= (chip->ara) is missing
regards, p.

> ----- Original Message ----- From: "Peter Meer= wald" <pmeerw@pmeerw.net>
> To: "Kevin Tsai" <k= tsai@capellamicro.com>
> Cc: "Jonathan Cameron" <jic23@kernel.= org>; <linux-iio@vger.kernel.org>
> S= ent: Tuesday, March 25, 2014 16:39
> Subject: Re: [PATCH V3 1/1] iio: add Capella cm3218= x ambient light sensor
> driver.
>
>
> >
&= gt; > > Add Capella Microsyst= em CM3218X family Ambient Light Sensor IIO driver.
> > >= This driver will convert raw data to lux value.  Default parameters are
> > > for ref= erence only.  It will detect A= CPI table to load per-system
> > > manufacturing
>= > > parameters.
> > >
> > > V2: Follow Jona= than Cameron's advise to separate device id.
> > > Also, follow= Peter Meerwald's advise to = correct whitespace, prefix, = and,
> > > comments.
> > >
> > > V3: F= ollow Peter Meerwald's advise to create= a separated i2c_client
> > > handler to
> > > acce= ss SMBus ARA register.
&g= t; >
> > couple of minor comments...
> >
> >= ; > Signed-off-by: Kevin Tsai <ktsai@capellamicro.com>= ;
> > > ---
> > >  .../devicetree/bindings/i2c= /trivial-devices.txt    |  1 +
> > >  driv= ers/iio/light/Kconfig  &nbs= p;                     &n= bsp; |  11 +
> > >  drivers/iio/light/Makefile          =               |  1 +
> >= >  drivers/iio/light/cm3218x.c             = ;           | 745
> > > ++++++++++++++= +++++++
> > >  4 files changed, 758 insertions(+)
> = > >  create mode 100644 drivers/iio/light/cm3218x.c
> >= >
> > > diff --git a/Documentation/devicetree/bindings/i2c/trivial-devices.txt
> > > b/Documentation/<= span id=3D"misspell-53" class=3D"mark">devicetree
/bindings/i2c/trivi= al-devices.txt
> > = > index 1a1ac2e..c3c869b 100644
> > > --- a/Documentation/devicetree/bindings/i2c/trivia= l-devices.txt
> > &= gt; +++ b/Documentation/devicetree/bindings/i2c/trivial-devices.txt
> > > @@ -17,6 +17,7 @@ = at,24c08 i2c serial eeprom&n= bsp; (24cxx)
> > &g= t;  atmel,24c02 i2c ser= ial eeprom  (24cxx)
> > >  atmel,at97sc3204t i2c trusted platform module (TPM)
> > >  capella,cm32181 CM32181: Ambient Light Sens= or
> > > +capella
,cm3218x CM3218X: Ambient Light Sensor
> > >  catalyst,24= c32 i2c serial eeprom
> > >  dallas,ds= 1307 64 x 8, Serial, I2C Real-Time Clock
> > >  dallas,ds1338 I2C R= TC with 56-Byte NV RAM
> > > diff --git a/drivers/iio/li= ght/Kconfig b/drivers/iio/li= ght/Kconfig
> > >= ; index d12b2a0..97a341a 100644
> > > --- a/drivers/iio/light/<= span id=3D"misspell-80" class=3D"mark">Kconfig
> > > +++= b/drivers/iio/light/Kconfig=
> > > @@ -38,6 +38,17 @@ config CM32181
> > >  To compile this driver as = a module, choose M here:
> > >  the module will be called cm32181.
> > >
&= gt; > > +config CM3218= X
> > > + depends on I2C
> >
> > depends on = I2C && ACPI ???
&= gt; >
> > > + trist= ate "CM3218X driver"
> > > + help
> > > + Sa= y Y here if you use cm3218x.
> > > + This option enables ambien= t light sensor using
> > > + Capella cm3218x device driver.
= > > > +
> > > + To compile this driver as a module, ch= oose M here:
> > > + the module will be called cm3218x.
>= > > +
> > >  config CM36651
> > >  depends on I2C
> >= >  tristate "CM3665= 1 driver"
> > > diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile
> > > index 60e35ac..a5= 06c23 100644
> > > --- a/drivers/iio/light/Makefile
> > > +++ b/drivers/iio/lig= ht/Makefile
> > >= ; @@ -6,6 +6,7 @@
> > >  obj-$(CONFIG_ADJD
_S311) +=3D adjd_s311.o> > >  obj-$(CONFI= G_APDS9300) +=3D apds9300.o
> > >&n= bsp; obj-$(CONFIG_CM32181) = +=3D cm32181.o
> > > +obj-$(CONFIG_CM3218X) += =3D cm3218x.o
> > >  obj-$(CONFIG_CM36651) +=3D cm36651.o
> > >  obj= -$(CONFIG_GP2AP020A00F) += =3D gp2ap020a00f.o
> > >  obj-$(CONFIG_HID_SENSOR_ALS) +=3D hid-sensor-als.o
> > > diff --git a/drivers/ii= o/light/cm3218x.c b/drivers/iio/light/cm3218x.c
> > > new file = mode 100644
> > > index 0000000..efd0d0a
> > > --- /dev/null
> > > +++ b/drivers/iio/light/cm= 3218x.c
> > > @@ -0,0 +1,745 @@
> > > +/*
> > > + * Copyright (C) 2014 Capella = Microsystems Inc.
> &= gt; > + * Author: Kevin Tsai <ktsai@capellamicro.com>=
> > > + *
> > > + * This program is free software;= you can redistribute it and/or modify
> > > it
> > &g= t; + * under the terms of the GNU General Public License version 2, as
&= gt; > > published
> > > + * by the Free Software Foundati= on.
> > > + *
> > > + * Special thanks Srinivas Pandruvada
> > > <srinivas.pandruvada@linux.intel.com>
> > > + = * help to add ACPI support.
> >= > + *
> > > + */
> > > +
> > > +#in= clude <linux/delay.h>
> > > +#include <linux/err.h>=
> > > +#include <linux/i2c.h>
> > > +#includ= e <linux/mutex.h>
= > > > +#include <linux/module.h>
> > > +#include= <linux/interrupt.h>
> > > +#include <linux/regulator/= consumer.h>
> > > +#include <linux/iio/iio.h>
> = > > +#include <linux/iio/= sysfs.h>
> > > +#include <linux/iio/events.h>> > > +#include <linux/init.h>
> > > +#include <linux/acpi.h>
> > > +
> >= > +/* Registers Address */
> > > +#define CM3218X_REG_ADDR_CMD 0x00
> > > +#define CM3218X_REG_ADDR_WH 0x01
> > > +#define CM3218X_REG_ADDR_WL 0x02
> > > +#define CM3218X_REG_ADDR_TEST 0x03
> > > +#define CM32= 18X_REG_ADDR_ALS 0x04
> > > +#define CM321= 8X_REG_ADDR_STATUS 0x06
= > > > +#define CM3218X_REG_ADDR_ID 0x07
> > > +
> > > +/* Number of C= onfigurable Registers */
> > > +#define CM3218X_CONF= _REG_NUM 16
> > &g= t; +
> > > +/* CMD
register */
> > > +#define CM3218X_CMD_ALS
_DISABLE BIT(0)
> > > +#define CM3218X_CMD_AL= S_INT_EN BIT(1)
> > > +#define CM3218X_CMD_ALS_THRES_WINDOW BI= T(2)
> > > +
> > > +#define CM3218X_CMD_ALS_PERS_SHIFT 4<= br>> > > +#define CM3218X_CMD_ALS_PERS_MASK (0x03 << CM3218X_CMD<= /span>_ALS_PERS_SHIFT)
> > > +#define CM32= 18X_CMD_ALS_P= ERS_DEFAULT (0x01 <<
> > > CM3218X_CMD_ALS_PERS_SHIFT)> > > +
> > > +#define CM3218X_CMD_ALS= _IT_SHIFT 6
> > > +#define CM3218X_CMD_ALS_IT_MASK (0x0F << CM321= 8X_CMD_ALS_IT_SHIFT)
> > > +#define CM3218= X_CMD_ALS_IT_DEFAULT (0x01 << CM3218X_CMD_ALS_IT_SHIFT)
> > > +
> > > +#defi= ne CM3218X_CMD_ALS_HS_SHIFT 11
> > > +#def= ine CM3218X_CMD_ALS_HS_MASK (0x01 << CM3218X_CMD
_ALS_HS_SHIFT)
> > > +#define CM3218X_CMD
_ALS_HS_DEFAULT (0x00 << CM3218X_CMD_ALS_HS_SHIFT= )
> > > +
> > > +#define CM3218X_CMD_DEFAULT (CM3218X_CMD_ALS_THRES_WINDOW |\
>= ; > > + CM3218X_CMD_<= span id=3D"misspell-203" class=3D"mark">ALS
_PERS_DEFAULT |\
> > > + CM3218X_CMD_ALS_IT_DEFAULT |\
> > > + CM3218X_CMD_ALS_HS_DEFAULT)
> > > +
> > > +#define CM3218X_WH_DEFAULT 0xFFFF
> > > +#define CM3218X_WL_DEFAULT 0x0000
> > > +
> > > +#define = CM3218X_CALIBSCALE_DEFAULT = 100000
> > > +#define CM3218X_CALIBSCALE_RESOLUTION 100000
> > > +#define CM= 3218X_MLUX_PER_LUX 1000
> > > +#define CM3= 218X_THRESHOLD_PERCENT 10 /* 10 percent */
> > > +
> >= > +#define CM3218X_ARA 0x0C
> > > +
> > > +/* C= M3218X family */
> > > +enum {
> > > + cm3218,
> > > + cm32181,> > > + cm32182
> > > +};
> > > +
> > > +/* CM= 3218 Family */
> > > +#define CM3218_MLUX_PER_BIT_DEFAULT 5 /* Depend on system */
>= > > +#define CM3218_MLUX_PER_BIT_BASE_IT 800000
> > > +static const int CM3218_als_it_bits[] =3D {0, 1, 2, 3};
> > > +static = const int CM3218_als_it_values[] =3D {100000, 200000= , 400000,
> > > 800000};
> > > +
> > > = +/* CM32181 Family */
> > > +#define CM32181_MLUX_PER_BIT_DEFAULT 5
> > > +#de= fine CM32181_MLUX_PER_BIT_B= ASE_IT 800000
> > > +static const int CM32181_= als_it_bits[] =3D {12, 8, 0= , 1, 2, 3};
> > > +static const int CM32181_als_it_values[] =3D {
> > > + 25000, 50000, 100000, 200000, 4= 00000, 800000};
> > > +
> > > +struct cm3218x_als_info {
> > > + u32 id;
> > > += int int_type;
> > > +#define CM3218X_INT_TYPE_SMBUS 0
> > > +#define CM3218X_= INT_TYPE_I2C 1
> > > + int regs_bmp;
> > > + int calibscale;
> > > + int mlux_per_bit;
> > > + int mlux_per_bit_base_it;
> > > += const int *als_it_bits;
> > > + const int *als_it_values;
> > > + int num_als_it;
> > > + int als_raw;
> > > +};
> > > +
>= ; > > +static struct = cm3218x_als_info cm3218_inf= o =3D {
> > > + 3218, CM3218X_INT_TYPE_SMBUS, 0x0F, CM3218X_CALIBSCALE_DEFAULT,
> > > + CM3218_MLUX_PE= R_BIT_DEFAULT, CM3218_MLUX_= PER_BIT_BASE_IT,
> > > + CM3218_als_it_bits, CM3218_als_it_values,
> > > + ARRAY_SIZE(CM3218_als_it_bits), 0};
> >
>= > const?
> > <= br>> > > +
> > > +static struct cm3218x_= als_info cm32181_info =3D {
> > > + 32181, CM3218X_INT_T= YPE_I2C, 0x0F, CM3218X_CALIBSCALE<= /span>_DEFAULT,
> > > + CM32181_MLUX_PER_BIT_DEFAULT, CM32181_MLUX_PER_BIT_BASE_IT,
> > > + CM32181_als
_it_bits, CM32181_als_it_values,
> > > + = ARRAY_SIZE(CM32181_als_it_b= its), 0};
> > > +
> > > +static struct cm3218x_als_info cm32182_info =3D {
> > > + 32182, CM3= 218X_INT_TYPE_I2C, 0x0F, CM3218X_C= ALIBSCALE_DEFAULT,
> > > + CM32181_MLUX_PER_BIT_DEFAULT, CM32181_MLUX_PER_BIT_BASE_IT,
> > > + CM3= 2181_als_it_bits, CM32181_<= span id=3D"misspell-267" class=3D"mark">als
_it_values,
> > = > + ARRAY_SIZE(CM32181_als_it_bits), 0};
> > &= gt; +
> > > +struct cm3218x_chip {
> > > + struct i2c_client *client;
> > > + struct i2c_client *ara;
> > > + struct mute= x lock;
> > > + u16 conf_regs[CM3218X_CONF= _REG_NUM];
> &= gt; > + struct cm3218x_<= span id=3D"misspell-279" class=3D"mark">als
_info *als_info;
> > > +};
> > = > +
> > > +static int cm3218x_get_lux(struct cm3218x_chip *chip);
>= > > +static int cm3218x_threshold_update(struct cm3218x_chip *chip, int
> > > perce= nt);
> > > +static int cm3218x_read_als_it(struct= cm3218x_chip *chip, int *val2);
> > > +
> > &g= t; +/**
> > > + * cm3218x_interrupt_config() - Enable/Disable CM3218X interrupt
> &g= t; > + * @chip: pointer of stru= ct cm3218x.
> > > + * @enable: 0 to disable; otherwise t= o enable
> > > + *
> > > + * Config CM3218X interrupt control bit.
> >= ; > + *
> > > + * Return: 0 for success; otherwise for error code.
> > >= ; + */
> > > +static int cm3218x_interrupt_config(struct cm3218x_chip *chip, int
> > > enable)
> &g= t; > +{
> > > + str= uct i2c_client *client =3D chip->client;
> > > + struct
cm3218x_als_info *als_info =3D chip->als_info;
> > > + int status;
> > > +> > > + if (!als= _info)
> > > + return -ENODEV;
> > > +
> > > + /* Force to clean i= nterrupt */
> > > + if (als_info->int_type =3D= =3D CM3218X_INT_TYPE_I2C) {
> > > + status =3D i2c_smbus_read_word_data(client,
> &g= t; > + CM3218X_REG_ADDR_= STATUS);
> > > + if (status < 0)
> > > + als_info->int_type =3D CM3218X_= INT_TYPE_SMBUS;
> >= ; > + }
> > > + if (chip->ara && al= s_info->int_type =3D=3D CM3218X_INT_TYPE_SMBUS)
> > > + i2c_smbus_read_byte(chip->ara);
> > > +
> > > + if = (enable)
> > > + chip->conf_regs[CM3218X_REG_ADDR_CMD] |=3D
> > > + CM3218X_CMD_A= LS_INT_EN;
> > > + else
> > > + chip->conf
_regs[CM3218X_REG_ADDR_CMD] &=3D
> > > + ~CM3218X_CMD_ALS_INT_EN;
> > > +
> > > + status =3D i= 2c_smbus_write_word_data(cl= ient, CM3218X_REG_ADDR_CMD
,
> > > + chip-&= gt;conf_regs[CM3218X_REG_ADDR_CMD]= );
> > > +
> > > + if (status < 0)
> > = > + return -ENODEV;
&= gt; > > +
> > > + return status;
> > > +}
= > > > +
> > > +/**
> > > + * cm3218x_acpi_get_cpm_info() - Get CPM object from A= CPI
> > > + * @client pointer of struct i2c_client.
> > > + * @obj_name= pointer of ACPI object nam= e.
> > > + * @count maximum size of return array.
> > = > + * @vals pointer of array for return elements.
> &= gt; > + *
> > > + * Convert ACPI CPM t= able to array. Special thanks Srin= ivas Pandruvada's> > > + * help to implement this routine.
> > > + *> > > + * Return: -ENOD= EV for fail.  Otherwise is number of elements.
> > >= ; + */
> > > +static int cm3218x_acpi_get_cpm_info(struct i2c_client = *client, char
> > > *obj_name,
> > > + int count, u= 64 *vals)
> > >= +{
> > > + acpi_handle handle;
> >= > + struct acpi_buffer buffer =3D {ACPI_ALLOCATE_BUFFER, NULL};
> > = > + int i;
> > > + = acpi_status status;
> > > + union acpi_object *cpm;
> > > +
> > > + handle =3D ACPI_HANDLE(&client->dev);
> > > + if (!ha= ndle)
> > > + return -= ENODEV;
> > > +
> > > + status =3D acpi_evaluate_object(handle, obj_na= me, NULL, &buffer);
> > > + if (ACPI_FAILURE(status)) = {
> > > + dev_e= rr(&client->dev, "ob= ject %s not found\n", obj_name);
> > > + return -ENODEV;
> > > + }
> >= ; > +
> > > + cpm =3D buffer.pointer;
> > > + for (i =3D 0; i < cpm->package.count && i = < count; ++i) {
> > > + union acpi_object *elem;
> > > + elem =3D &(cpm
->package.elements[i]);<= br>> > > + vals[i]= =3D elem->integer.value;
> > > + }
> > > +
&= gt; > > + kfree(buffer.pointer);
> &= gt; > +
> > > + return cpm->package.count;
> > > +}
> > > +<= br>> > > +/**
> > > + * cm3218x_reg_init() - Initialize CM3218X registers
>= > > + * @chip: pointer of s= truct cm3218x.
> > > + *
> > > + * Initializ= e CM3218X ambient light sensor register to default values.
> > >= ; + *
> > > +  Return: 0 for success; otherwise for error = code.
> > > + */
> > > +static int cm3218x_reg_init
(struct cm3218x_chip *chip)
> > > +{
&g= t; > > + struct i2c_c= lient *client =3D chip->client;
> > > + int i;
> > > + s32 ret;
> > > + int cpm_elem_count;
> > &g= t; + u64 cpm_elems[20];
> > > + struct cm3218x_als_info *als_info;
> > > +
> > > + /* Default dev= ice */
> > > + chip->als_info =3D &cm3218_info;
> > > + chip->conf_regs[CM3218X_REG_ADDR_CMD] =3D CM3218X_= CMD_DEFAULT;
> > > + chip->conf_regs[CM3218X= _REG_ADDR_WH] =3D CM3218X_WH_DEFAULT;
> > > + chip->conf_regs[CM3218X_REG_ADDR_= WL] =3D CM3218X_WL_D= EFAULT;
> > > +
> > > + /* Disable interrupt */
= > > > + cm3218x_interrupt_config(chip, 0);
> > > +
> > > + /* Disable= Test Mode */
> > > + i2c_smbus_write_word_data(client, CM3218X_REG_ADDR_TEST, 0x0000);
> > > +
> &= gt; > + /* Disable device */
> > > + i2c_smbus_writ= e_word_data(client, CM3218X_REG_AD= DR_CMD,
> >= > + CM3218X_CMD_ALS_DISABLE);
> > > +<= br>> > > + /* Identify device */
> > > + ret =3D i2c_smbus_read_word_data(client, CM3218X_REG_ADDR_ID);
> > > + if (ret < 0)
> > > + re= turn ret;
> > >= + switch (ret & 0xFF) {
> > > + case = 0x18:
> > > + als_info =3D chip->als_info =3D &cm3218_info;
&= gt; > > + if (ret &am= p; 0x0800)
> > > + als= _info->int_type =3D CM3218X_INT_TYPE_I2C;
> > > + els= e
> > > + als_i= nfo->int_type =3D CM3218X_INT_TYPE_SMBUS;
> > > + break;
> > > + case 0x81:=
> > > + als_in= fo =3D chip->als_info = =3D &cm32181_info;
> > > + break;
> > > + case = 0x82:
> > > + als
_info =3D chip->als_in= fo =3D &cm32182_info;
> > > + break;
> > > + default:
> > > + return -ENODEV;
> > > + }
&g= t; > > +
> > > + if (ACPI_HANDLE(&client->dev)) {
> > > + /* Load from ACPI */
> > > + cpm_elem_count =3D cm3218x_acpi_get_cpm_info(client, "CPM0",
> > > + ARRAY_SIZE(cpm_elems),
= > > > + cpm_elems);
> > > + if (<= span id=3D"misspell-425" class=3D"mark">cpm
_elem_count > 0) {
> > > + int hea= der_num =3D 3;
> >= > + int reg_num =3D cpm_elem_count - header_num;
> > > +
> = > > + als_info->re= gs_bmp =3D cpm_elems[2];
> > > + for (i =3D 0; i < reg_num; i++)
> > > + if (als_info->regs_bmp & (1<<i))
> > >= ; + chip->conf_regs[i] = =3D
> > > + cpm_elems[header_num+i];
>= ; > > + }
> > > +
> > > + cpm_elem_count =3D cm3218x_acpi_get_cpm_info(client, "CPM1",
> > > + ARRAY_SIZE(cpm_elems),> > > + cpm_elems
);
> > > + if (= cpm_elem_count > 0) {> > > + als_info-= >mlux_per_bit =3D (int)<= span id=3D"misspell-452" class=3D"mark">cpm
_elems[0] / 100;
> > > + als_info->calibscale =3D (int)cpm_elems[1];
> > > + }
= > > > + }
> > > +
> > > + /* Force to disa= ble interrupt */
> > > + chip->conf_regs[CM3218X_REG_ADDR_CMD] &am= p;=3D ~CM3218X_CMD_ALS_INT_EN;
> > > +
= > > > + /* Initialize registers */
> > > + for (i =3D = 0; i < CM3218X_CONF_REG_= NUM; i++) {
> > > + if (als_info->regs_bmp & (1<<i)) {
> > > + ret =3D i2c_smbus_write_word_data(client, i,
> > &g= t; + chip->conf_regs[i])= ;
> > > + if (ret
< 0)
> > > + return ret;
> > > + }
> > > + }
> > &g= t; +
> > > + return 0;
> > > +}
> > > +=
> > > +/**
> > > + *  cm3218x_read_als_it() - Get sensor integration t= ime (ms)
> > > + *  @chip: pointer of struct cm3218x
> > > + *  @val2: pointer of int to load the als_it value.
> > >= + *
> > > + *  Report the current integartion time in milliseconds.
> > > > integration
> >
> > > + *
> > >= ; + *  Return: IIO_VAL_INT_PLUS_MICRO for success, otherwise -EINVAL.
> > > + */
&= gt; > > +static int cm3218x_read_als_it(struct c= m3218x_chip *chip, int *val2)
> > > +{
> > > + struct cm3218x_als_info *als_info =3D chip->als_info;
> > > + u16 als_it;> > > + int i;
> > > +
> > > + als_it =3D chip->conf_regs[CM3218X_REG_ADDR_C= MD];
> > > + al= s_it &=3D CM3218X_CMD_ALS_IT_MASK;
> = > > + als_it >>= =3D CM3218X_CMD_ALS_IT_SHIFT;
> > > + for = (i =3D 0; i < als_info-&= gt;num_als_it; i++) {
> > > + if (als_it =3D=3D <= span id=3D"misspell-499" class=3D"mark">als
_info->als_it_bits[i]) {
> > > + *val= 2 =3D als_info->als_it_values[i];
> > >= + return IIO_VAL_INT_PLUS_MICRO;
> > > + }
> > > += }
> > > +
> > > + return -EINVAL;
> > > +}
> > > +> > > +/**
> > > + * cm3218x_write_als_it() - Write sensor integration time
&= gt; > > + * @chip: pointer of struct cm3218x.
> > > + * @val: integration time in mi= lliseconds.
> > > + *
> > > + * Convert integration= time (ms) to sensor value.
> > > + *
> > > + * Return: i2c_smbus_write_word_data command return v= alue.
> > > + */
> > > +static int cm3218x_write_als_it(struct cm3218x_chip *chip, int val)
> > = > +{
> > > + struct= i2c_client *client =3D chip->client;
> > > + struct cm3218x_als_info *als_info =3D chip->= als_info;
> > > + u16 als_it;
> > > + int ret, i;
> > > +
> > > + for (i = =3D 0; i < als_info->num_als_it; i++)> > > + if (val <=3D als_info->als_it= _values[i])
> > > + break;
> > > + if (i >=3D als_info->num_als_it)
> > > + i =3D als_info->num<= /span>_als_it - 1;
> = > > +
> > > + al= s_it =3D als_info-&g= t;als_it_bits[i];
> &= gt; > + als_it <<=3D CM3218X_CMD_ALS
_IT_SHIFT;
> > > +
> > > + mutex_lock(&chip->lock);
> > >= ; + chip->conf_regs[CM32= 18X_REG_ADDR_CMD] &=3D
> > > + ~CM3218= X_CMD_ALS_IT_MASK;
> > > + chip->conf_regs[CM3218X_REG_ADDR_CMD] |=3D
> > > + als_it;
> > > + ret =3D i2c_smbus_write_word_data(client, CM3218X_REG_ADDR_CMD,
> > > + chip->conf_regs[CM3218X_REG_ADDR_CMD
]);
> > > + mutex_unlock(&chip->lock);
> > > +
> > > + ret= urn ret;
> > > = +}
> > > +
> > > +/**
> > > + * cm3218x= _get_lux() - report current= lux value
> > >= ; + * @chip: pointer of struct cm3218x.
> > > + *
> > > + * Convert sensor raw= data to lux.  It depends on integration
> > &g= t; + * time and calibscale = variable.
> > > + *
> > > + * Return: Positive valu= e is lux, otherwise is erro= r code.
> > > + */
> > > +static int cm3218x_get_lux
(struct cm3218x_chip *chip)
> > > +{
&= gt; > > + struct i2c_= client *client =3D chip->client;
> > > + struct cm3218x_als_info *als_info =3D chip->als_i= nfo;
> > > + int ret;
> > > + int als_it;
> > > + u64 = tmp;
> > > +> > > + /* Calculate mlu= x per bit based on als
_it */
> > > + ret =3D cm3218x_read_als_= it(chip, &als_it);
&= gt; > > + if (ret <= ; 0)
> > > + return -E= INVAL;
> > > + = tmp =3D (__force u64)als_info->mlux_per_bit;=
> > > + tmp *= =3D als_info->mlux_per_bit_base_it;
> > > + tmp =3D div_u64(tmp, als_it);<= br>> > > +
> > > + /* Get als_raw */
> > > + if (!(chip->conf_regs[CM3218X_REG_ADDR_CMD] & CM3218X_CM= D_ALS_INT_EN))
&g= t; > > + als_info->= ;als_raw =3D i2c_smbus_read_word_data(
> > = > + client,
> > > + CM3218X_REG_ADDR_ALS);
> > > + if (als_info->als_raw < 0)
> > > + return als_info->als_raw;
> > > +
> > > + tmp *=3D als_info->als<= /span>_raw;
> > > + tm= p *=3D als_info->= calibscale;
> > &g= t; + tmp =3D div_u64(tmp, CM3218X_CALIBSCALE_RESOLUTION);
> > > + tmp =3D div_u6= 4(tmp, CM3218X_MLUX_PER_LUX);
> > > +
> > > + if (tmp > 0xFFFF)
> > > + tmp =3D 0x= FFFF;
> > > +
> > > + return (int)tmp;
> > > +}
> &= gt; > +
> > > +static int cm3218x_read_raw(struct iio_dev *indio= _dev,
> > > + <= span id=3D"misspell-623" class=3D"mark">struct
iio_chan_spec const *chan,
> > > + int *val, int *val2, long mask)<= br>> > > +{
> > > + struct cm3218x_chip *chip =3D iio_priv(indio_dev);
> > >= ; + struct cm3218x_als_info *als_info =3D chip->als_info;
> > > + int ret;
> > > +
> > > + switc= h (mask) {
> > > + case IIO_CHAN_INFO_PROCESSED:
> > &= gt; + ret =3D cm3218x_get_lux(chip);
> > > + if (ret < 0)
> > > + return ret;
> > > + *val =3D ret
;
> > > + return= IIO_VAL_INT;
> > > + case IIO_CHAN_INFO_CALIBSCALE:
> > > + *val =3D als_info->calibscale;
> > > + return IIO_VAL_IN= T;
> > > + case IIO_CHAN_INFO_INT_TIME:
> > > + *va= l =3D 0;
> > > + ret =3D cm3218x_read_als_= it(chip, val2);
> > > + return ret;
> > > + }
> > > +
> > > + return -EINVAL;
> > > +}
> > > +> > > +static int cm3218x_write_raw(struct iio_dev<= /span> *indio_dev,
> > > + struct iio_chan_spec const *chan,
> > &= gt; + int val, int val2, long mask)
> > > +{
> > > = + struct cm3218x_chip *chip= =3D iio_priv(indio_dev);
> > > + struct cm3218x_als_info *als_info =3D chip->als_info;
> > > + long ms;
> > > +
>= > > + switch (mask) {
> > > + case IIO_CHAN_INFO_CALIBSCALE:
> > > + als
_info->calibscale =3D val;
> > > + ret= urn val;
> > > + case IIO_CHAN_INFO_INT_TIME:
> > >= + ms =3D val * 1000000 + val2;
> > > + return cm3218x_write_als
_it(chip, (int)ms);
>= ; > > + }
> > > +
> > > + return -EINVAL;
> > > +}
> &= gt; > +
> > > +/**
> > > + * cm3218x_get_it_available() - Get available ALS
IT value
> > > = + * @dev: pointer of struct device.
> > > = + * @attr: pointer of struct
device_attribute.
>= > > + * @buf: pointe= r of return string buffer.
> > > + *
> > > + * Disp= lay the available integration time in milliseconds.
> > > + *> > > + * Return: string length.
> > > + */
> = > > +static ssize_t c= m3218x_get_it_available(struct device *dev,
> &g= t; > + struct device_attribute *attr, char *buf)
> > > +{
> > > + struct
cm3218x_chip *chip =3D = iio_priv(dev_to_iio_dev(dev));> > > + struct cm= 3218x_als_info *als_info =3D chip->als_info;
> > > + int i, len;
> > > +
> = > > + for (i =3D 0, len =3D 0; i < als_info->num_als_it; i++)> > > + len +=3D= scnprintf(buf + len, PAGE_SIZE - len, "%u.%06u ",
> > > + als_info->als= _it_values[i]/1000000,
> > > + als_info->als<= /span>_it_values[i]%1000000);
> > > + return len + scnprintf(buf + len
, PAGE_SIZE - len, "\n");> > > +}
> > > +
> > > +/**
> > = > + * cm3218x_threshold_update() - Update the threshold registers.
&g= t; > > + * @chip: pointer of struct cm3218x_chip.
> > > + * @percent: +/- percent.> > > + *
> > > + * Based on the current ALS value, tupdate the hi and low threshold
> > > reg= isters.
> >
> > tupdate?
> >
> > > + *
> > > + * = Return: 0 for success; otherwise for error code.
> > > + */
= > > > +static int cm3218x_threshold_update(struct cm3218x_chip *chip, int
> > > p= ercent)
> > > +{
> > > + = struct i2c_client *client =3D chip->client;
> > > + <= span id=3D"misspell-729" class=3D"mark">struct
cm3218x_als_info *als_info =3D chip->als_info;
> > > + int ret;
> > > + int wh, wl;
> > > +
> > > + ret =3D als_info->als_raw =3D i2c_= smbus_read_word_data(client= ,
> > > + CM3218X_REG_= ADDR_ALS);
> > > + if (ret < 0)
> > > + return ret;
> > > +
> &g= t; > + wh =3D wl =3D ret;
> > > + ret *=3D percent;
> > > + ret /=3D 100;
> > > + if (ret < 1)
> > > + ret
=3D 1;
> > > += wh +=3D ret;
> > > + wl -=3D ret;
> > > + if (wh > 65535)
> > > + wh =3D 65535;
> > > + if (wl < 0)
> > > + wl
=3D 0;
> > > +=
> > > + chip->conf= _regs[CM3218X_REG_ADDR_WH] =3D wh;
> > > + ret =3D i2c_smbus_write_word_data(
> > > + client,
> &g= t; > + CM3218X_REG_ADDR_= WH,
> > > + chi= p->conf_regs[CM3218X_REG= _ADDR_WH]);
> > > + if (ret= < 0)
> > > + return ret;
> > > +
> > > + chip->conf
_regs[CM3218X_REG_ADDR_WL] =3D wl;
> > > + ret = =3D i2c_smbus_write_word_da= ta(
> > > + client,
> > > + CM3218X_REG_ADDR_WL,
> > > + chip->conf_regs[CM3218X_REG_ADDR_WL]);
> > > +
> > > + return= ret;
> > > +}<= br>> > > +
> > > +/**
> > > + * cm3218x_ev= ent_handler() - Interrupt handling routine.
> > > + * @irq: irq number.
> > > + * @private: pointer of vo= id.
> > > + *
> > > + * Clean interrupt and reset t= hreshold registers.
> > > + *
> > > + * Return: IRQ
_HANDLED.
> > >= + */
> > > +static ir= qreturn_t cm3218x_event_handler(int irq, void *private)
> > > +{
> > >= + struct iio_dev *dev_info =3D private;
> > > + struct cm3218x_chip *chip =3D iio_priv(dev_info);
> > > + int ret;
> > > +
> > > + mutex_lock(&chip->lock);
> > &g= t; +
> > > + /* Disable interrupt */
> > > + ret =3D cm3218x_interrupt_config(chip, 0);
> > >= + if (ret < 0)
> = > > + goto error_hand= ler_unlock;
> > > +
> > > + /* Update Hi/Lo windows= */
> > > + ret =3D cm3218x_threshold_update= (chip, CM3218X_THRESHOLD_PERCENT);
> > > + if (ret < 0)
> > > + goto error_handler_unlock;
> >= ; > +
> > > + /* Enable interrupt */
> > > + ret
=3D cm3218x_interrupt_config
(chip, 1);
> > <= br>> > no need to check ret<= /span> value
> >
> > > + if (ret < 0)
> > > + goto error_handler_unlock;
> > > +=
> > > +error_handler_unlock:
> > > + mutex_unlock(&chip->lock);
>= ; > > + return IRQ_HANDLED;
> > > += }
> > > +
> > > +static const struct<= /span> iio_chan_spec cm3218= x_channels[] =3D {
> > > + {
> > > + .type =3D IIO_= LIGHT,
> > > + .info_mask_separate =3D
> > > + BIT(= IIO_CHAN_INFO_PROCESSED) |
> > > + BIT(IIO_CHAN_INFO_CALIBSCALE) |
> > > + B= IT(IIO_CHAN_INFO_INT_TIME),
> > > + }
> > > +};
= > > > +
> > > +static IIO_DEVICE_ATTR(in_illuminance_integration_time_available,
> > > + S_IRUGO
, cm3218x_get_it_available,= NULL, 0);
> > > +
> > > +static struc= t attribute *cm3218x_attributes[] =3D {
> > > + &iio= _dev_attr_in_illuminance_integration_time_available.dev_attr.attr,
> = > > + NULL,
> > > +};
> > > +
> > &g= t; +static const struct attribute_group cm3218x_attr= ibute_group =3D {
> > > + .attrs =3D cm3218x_attributes
> > > +};
> >= > +
> > > +static = const struct iio_inf= o cm3218x_info =3D {
> > > + .driver_module =3D THIS_MODULE,> > > + .read_raw =3D &cm3218x_read_raw,
> > > + = .write_raw =3D &cm3218x_write_raw,
> > > + .attrs =3D &cm3218x_attribute_group,
> >= > +};
> > > +
> > > +static int cm3218x_probe(<= span id=3D"misspell-834" class=3D"mark">struct
i2c_client *client,> > > + const struct
i2c_device_id *id)
= > > > +{
> > > + struct cm3218x_chip *chip;
> > > + struct iio_dev *indio= _dev;
> > > + i= nt ret;
> > > +=
> > > + indio_= dev =3D devm_iio_device_alloc(&client->= dev, sizeof(*chip));=
> > > + if (!indio_dev) {
> > &g= t; + dev_err(&client-&g= t;dev, "devm_iio_device_alloc failed\n");
> > > + return -ENOMEM;
> > > + }
> = > > +
> > > + chip =3D iio_priv(indio_dev);
> > > += i2c_set_clientdata(client, indio_dev)= ;
> > > + chip->client =3D client;
> > > + chip-= >ara =3D i2c_new_dummy(c= lient->adapter, CM3218X_ARA);
> > > +
> >
> = > there is no i2c_unregister_device(chip->ara) i= n the error path below; but
> > i2c_unregister_device(chip->ara) is called in _remove()
> >
> > &g= t; + mutex_init(&chip->lock);
> > >= + indio_dev->dev.parent =3D &client-><= span id=3D"misspell-876" class=3D"mark">dev
;
> > > + indio
_dev->channels =3D cm3218x_channels;
> >= > + indio_dev->num_channels =3D ARRAY_SIZE(cm3218x_channels);
> >= ; > + indio_dev->info =3D &cm3218x_info;
= > > > + if (id && id->name)
> > > + indio_dev->name =3D id->name;
> > > + else<= br>> > > + indio_<= span id=3D"misspell-887" class=3D"mark">dev
->name =3D (char *)dev_name(&client->dev);
> > > + indio_dev= ->modes =3D INDIO_DIRECT= _MODE;
> > > +
> > > + ret =3D cm3218x_reg_init(chip);
> > > + if (ret) {
> > > + dev_err(&client->dev,
> > > + "%s: register init failed\n",
> > > + __func__);
> > > + retu= rn ret;
> > > + }
> > > +
&g= t; > > + if (client->irq<= /span>) {
> > > + ret<= /span> =3D request_threaded_irq(client->irq,
>= ; > > + NULL,
> > > + cm3218x_event_handler,
> >= > + IRQF_TRIGGER_FALLIN= G | IRQF_ONESHOT,
> > > + "cm3218x_event",=
> > > + indio_= dev);
> > > +> > > + if (ret = < 0) {
> > > + dev<= /span>_err(&client->dev, "irq request error %d\n",
> > > + -ret);
> > > + goto error_disable_int;
> >= ; > + }
> > > + }
> > > +
> > > + ret
=3D iio_device_register(<= span id=3D"misspell-918" class=3D"mark">indio
_dev);
> > > + if (ret < 0) {
> > > + dev_err(&client->dev,
> > > + "%s: regist device failed\n",
> > &= gt; + __func__);
> &g= t; > + goto error_free_irq;
> > > + }
> > > +
> > >= ; + if (client->irq) {> > > + ret =3D = cm3218x_threshold_update(chip, CM3218X_THRESHOLD_PERCENT);
> > >= ; + if (ret < 0)
>= > > + goto error_fre= e_irq;
> > > +<= br>> > > + ret =3D= cm3218x_interrupt_config(c= hip, 1);
> > > + if (r= et < 0)
> > > + goto error_free_irq;
> > > + }
> > > +
> > > + return 0;
> > > +
> > >= +error_free_irq:
> &= gt; > + free_irq(client-= >irq, indio_dev);
> >
> > in _remove() free_irq() is only called when client->irq
!=3D 0
> >
>= > > +error_disable_int:
> > > + cm3218x_interrupt_config(chip, 0);
> > >= ; + return ret;
> >= ; > +}
> > > +
> > > +static int cm3218x_remove(= struct i2c_client *client)<= br>> > > +{
> > > + struct iio_dev *indio_dev= =3D i2c_get_clientdata(cli= ent);
> > > + struct cm3218x_chip *chip =3D iio_p= riv(indio_dev);
> > > +
> &= gt; > + cm3218x_interrupt_confi= g(chip, 0);
> > > + if (chip->ara)
> > > + i2c_unregister_device(chip->ara);
> > > + if (client->irq)
> > > + free_irq(clien= t->irq, indio_dev);
> > > + iio_device_unregister(in= dio_dev);
> &g= t; > + return 0;
> > > +}
> > > +
> > &= gt; +static const struct i2c_device_id cm3218x_id[] = =3D {
> > > + { "cm3218", cm3218},
> > > + { "cm321= 81", cm32181},
> > > + { "cm32182", cm32182},
> > >= + { }
> > > +};
> > > +
> > > +MODULE_= DEVICE_TABLE(i2c, cm3218x_id);
> > > +
> > > +stati= c const struc= t of_device_id cm3218x_of_match[] =3D {
> > > + { .compa= tible =3D "capella,cm3218" = },
> > > + { }
> > > +};
> > > +
>= ; > > +static const <= span id=3D"misspell-977" class=3D"mark">struct
acpi_device_id cm3218x_acpi_match[] =3D {
> > > + { "CPLM3218", 0},
> > > + = {},
> > > +};
> > > +
> > > +MODULE_DEV= ICE_TABLE(acpi, cm3218x_acpi
_match);
> > >= +
> > > +static struc= t i2c_driver cm3218x_driver =3D {
> > > + .driver =3D {
> > > + .name =3D "cm3218x= ",
> > > + .acpi_match_table =3D ACPI_PTR(cm3218x_acpi_match),
> > > + .of_match_tab= le =3D of_match_ptr(cm3218x= _of_match),
> > > + .owner =3D THIS_MODULE,
> > > += },
> > > + .id_table =3D cm3218x_id,
> > > + .prob= e =3D cm3218x_probe,
> > > + .remove =3D cm3218x_remove,
>= ; > > +};
> > > +
> > > +module_i2c_driver(cm= 3218x_driver);
> > > +
> > > +MODULE_AUTHOR("Kevin = Tsai <ktsai@capellamicro.com>");
> > > +MODU= LE_DESCRIPTION("CM3218X ambient light sensor driver");
> > > +MODULE_LICENSE("GPL");
> > >
> >
= > > --
> >
> > Peter Meerwald
> > +43-66= 4-2444418 (mobile)
>

--

Peter Meerwald
+43-664-244= 4418 (mobile)

---1192396609-1770039525-1395809544=:53200--