On Sat, 28 May 2016, Kevin Tsai wrote: > Added Vishay Capella CM36672 Proximity Sensor IIO driver. Support both > ACPI and Device Tree. comments below > Signed-off-by: Kevin Tsai <capellamicro@xxxxxxxxx> > --- > .../devicetree/bindings/i2c/trivial-devices.txt | 1 + > .../devicetree/bindings/iio/light/cm36672.txt | 54 ++ > MAINTAINERS | 3 +- > drivers/iio/light/Kconfig | 11 + > drivers/iio/light/Makefile | 1 + > drivers/iio/light/cm36672.c | 925 +++++++++++++++++++++ > 6 files changed, 994 insertions(+), 1 deletion(-) > create mode 100644 Documentation/devicetree/bindings/iio/light/cm36672.txt > create mode 100644 drivers/iio/light/cm36672.c > > diff --git a/Documentation/devicetree/bindings/i2c/trivial-devices.txt b/Documentation/devicetree/bindings/i2c/trivial-devices.txt > index 53987449..a66f052 100644 > --- a/Documentation/devicetree/bindings/i2c/trivial-devices.txt > +++ b/Documentation/devicetree/bindings/i2c/trivial-devices.txt > @@ -25,6 +25,7 @@ at,24c08 i2c serial eeprom (24cxx) > atmel,at97sc3204t i2c trusted platform module (TPM) > capella,cm32181 CM32181: Ambient Light Sensor > capella,cm3232 CM3232: Ambient Light Sensor > +capella,cm36672 CM36672: Proximity Sensor > cirrus,cs42l51 Cirrus Logic CS42L51 audio codec > dallas,ds1307 64 x 8, Serial, I2C Real-Time Clock > dallas,ds1338 I2C RTC with 56-Byte NV RAM > diff --git a/Documentation/devicetree/bindings/iio/light/cm36672.txt b/Documentation/devicetree/bindings/iio/light/cm36672.txt > new file mode 100644 > index 0000000..2c29f9f > --- /dev/null > +++ b/Documentation/devicetree/bindings/iio/light/cm36672.txt > @@ -0,0 +1,54 @@ > +* Vishay Capella CM36672 Proximity Sensor > + > +Required properties: > +- compatible: must be "capella,cm36672" > +- reg: the I2C address of the device > +- interrupts: interrupt-specifier for the sole interrupt generated by the device > + > +Optionoal properties: Optional > +- capella,reg_prx_conf: PS_CONF1 and PS_CONF2 registers > +- capella,reg_prx_conf3: PS_CONF3 and PS_MS registers > +- capella,reg_prx_canc: PS cancellation level setting > +- capella,reg_prx_thdl: PS low interrupt threshold setting > +- capella,reg_prx_thdh: PS high interrupt threshold setting > +- capella,name_prx_int_rising: enable/disable rising interrupt > +- capella,name_prx_int_falling: enable/disable falling interrupt > +- capella,name_prx_duty: PS duty ratio setting > +- capella,name_prx_pers: persistence setting > +- capella,name_prx_it: integration time setting > +- capella,name_prx_led_current: LED current selection > +- capella,name_prx_threshold_falling: interrupt falling threshold > +- capella,name_prx_threshold_rising: interrupt rising threshold > + > +Example: > + > + i2c_cm36672: i2c-gpio { > + /* ... */ > + > + cm36672@60 { > + compatible = "capella,cm36672"; > + reg = <0x60>; > + interrupt-parent = <&gpio0>; > + interrupts = <30 0>; > + > + /* Initialized by register */ > + capella,reg_prx_conf = <0x0322>; > + capella,reg_prx_conf3 = <0x0000>; > + capella,reg_prx_canc = <0x0000>; > + capella,reg_prx_thdl = <0x0007>; > + capella,reg_prx_thdh = <0x000A>; > + > + /* Or, initialized by name */ > + capella,name_prx_int_rising = <1>; > + capella,name_prx_int_falling = <1>; > + capella,name_prx_duty = <0>; > + capella,name_prx_pers = <2>; > + capella,name_prx_it = <1>; > + capella,name_prx_led_current = <0>; > + capella,name_prx_threshold_falling = <7>; > + capella,name_prx_threshold_rising = <10>; > + }; > + > + /* ... */ > + }; > + > diff --git a/MAINTAINERS b/MAINTAINERS > index ed1229e..e9962a1 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -2805,10 +2805,11 @@ F: security/commoncap.c > F: kernel/capability.c > > CAPELLA MICROSYSTEMS LIGHT SENSOR DRIVER > -M: Kevin Tsai <ktsai@xxxxxxxxxxxxxxxx> > +M: Kevin Tsai <capellamicro@xxxxxxxxx> > S: Maintained > F: drivers/iio/light/cm* > F: Documentation/devicetree/bindings/i2c/trivial-devices.txt > +F: Documentation/devicetree/bindings/iio/light/cm*.txt > > CAVIUM LIQUIDIO NETWORK DRIVER > M: Derek Chickles <derek.chickles@xxxxxxxxxxxxxxxxxx> > diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig > index 7c566f5..1fa91ae 100644 > --- a/drivers/iio/light/Kconfig > +++ b/drivers/iio/light/Kconfig > @@ -127,6 +127,17 @@ config CM36651 > To compile this driver as a module, choose M here: > the module will be called cm36651. > > +config CM36672 > + depends on I2C > + tristate "CM36672 proximity sensor" > + help > + Say Y here if you use cm36672. > + This option enables proximity sensor using Vishay sensors > + Capella cm36672 device driver. > + > + To compile this driver as a module, choose M here: > + the module will be called cm36672. > + > config GP2AP020A00F > tristate "Sharp GP2AP020A00F Proximity/ALS sensor" > depends on I2C > diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile > index 6f2a3c6..b155425 100644 > --- a/drivers/iio/light/Makefile > +++ b/drivers/iio/light/Makefile > @@ -14,6 +14,7 @@ obj-$(CONFIG_CM32181) += cm32181.o > obj-$(CONFIG_CM3232) += cm3232.o > obj-$(CONFIG_CM3323) += cm3323.o > obj-$(CONFIG_CM36651) += cm36651.o > +obj-$(CONFIG_CM36672) += cm36672.o > obj-$(CONFIG_GP2AP020A00F) += gp2ap020a00f.o > obj-$(CONFIG_HID_SENSOR_ALS) += hid-sensor-als.o > obj-$(CONFIG_HID_SENSOR_PROX) += hid-sensor-prox.o > diff --git a/drivers/iio/light/cm36672.c b/drivers/iio/light/cm36672.c > new file mode 100644 > index 0000000..6dc2633 > --- /dev/null > +++ b/drivers/iio/light/cm36672.c > @@ -0,0 +1,925 @@ > +/* > + * CM36672 Proximity Sensor > + * > + * Copyright (C) 2014-2016 Vishay Capella > + * Author: Kevin Tsai <capellamicro@xxxxxxxxx> > + * > + * This program is free software; you can redistribute it and/or modify it > + * under the terms of the GNU General Public License version 2, as published > + * by the Free Software Foundation. > + * > + * IIO driver for CM36672 (7-bit I2C slave address 0x60). > + */ > +#include <linux/kernel.h> > +#include <linux/i2c.h> > +#include <linux/module.h> > +#include <linux/interrupt.h> > +#include <linux/gpio.h> > + > +#include <linux/iio/iio.h> > +#include <linux/iio/sysfs.h> > +#include <linux/iio/events.h> > + > +#ifdef CONFIG_ACPI > +#include <linux/acpi.h> > +#endif > + > +/* Sensor registers */ > +#define CM36672_ADDR_RESERVED00 0x00 > +#define CM36672_ADDR_RESERVED01 0x01 > +#define CM36672_ADDR_RESERVED02 0x02 no need to define > +#define CM36672_ADDR_PRX_CONF 0x03 > +#define CM36672_ADDR_PRX_CONF3 0x04 > +#define CM36672_ADDR_PRX_CANC 0x05 > +#define CM36672_ADDR_PRX_THDL 0x06 > +#define CM36672_ADDR_PRX_THDH 0x07 > +#define CM36672_REGS_NUM 0x08 > +/* Read only registers */ > +#define CM36672_ADDR_PRX 0x08 > +#define CM36672_ADDR_STATUS 0x0B > + > +/* RESERVED00 */ > +#define CM36672_RESERVED00_DEFAULT 0x0001 > + > +/* RESERVED01 */ > +#define CM36672_RESERVED01_DEFAULT 0x0000 > + > +/* RESERVED02 */ > +#define CM36672_RESERVED02_DEFAULT 0x0000 > + > +/* PRX_CONF */ > +#define CM36672_PRX_DISABLE 0x0001 > +#define CM36672_PRX_INT_RISING BIT(8) > +#define CM36672_PRX_INT_FALLING BIT(9) > +#define CM36672_PRX_INT_MASK (CM36672_PRX_INT_RISING | \ > + CM36672_PRX_INT_FALLING) > + > +/* PRX_CONF: duty ratio */ > +#define CM36672_PRX_DR_MASK (BIT(6) | BIT(7)) > +#define CM36672_PRX_DR_SHIFT 6 > +#define CM36672_PRX_DR1 0x0000 /* Duty ratio 1/40 */ > +#define CM36672_PRX_DR2 0x0040 /* Duty ratio 1/80 */ > +#define CM36672_PRX_DR3 0x0080 /* Duty ratio 1/160 */ > +#define CM36672_PRX_DR4 0x00C0 /* Duty ratio 1/320 */ > + > +/* PRX_CONF: persistence */ > +#define CM36672_PRX_PERS_MASK (BIT(4) | BIT(5)) > +#define CM36672_PRX_PERS_SHIFT 4 > +#define CM36672_PRX_PERS2 0x0010 > +#define CM36672_PRX_PERS3 0x0020 > +#define CM36672_PRX_PERS4 0x0030 > + > +/* PRX_CONF: integration time */ > +#define CM36672_PRX_IT_MASK (BIT(1) | BIT(2) | BIT(3)) > +#define CM36672_PRX_IT_SHIFT 1 > +#define CM36672_PRX_IT0 0x0000 /* 1T */ > +#define CM36672_PRX_IT1 0x0002 /* 1.5T */ > +#define CM36672_PRX_IT2 0x0004 /* 2T */ > +#define CM36672_PRX_IT3 0x0006 /* 2.5T */ > +#define CM36672_PRX_IT4 0x0008 /* 3T */ > +#define CM36672_PRX_IT5 0x000A /* 3.5T */ > +#define CM36672_PRX_IT6 0x000C /* 4T */ > +#define CM36672_PRX_IT7 0x000E /* 8T */ > + > +#define CM36672_PRX_CONF_DEFAULT (CM36672_PRX_IT2 | \ > + CM36672_PRX_PERS3) > + > +/* PRX_CONF3 */ > +#define CM36672_PRX_LED_I_MASK (BIT(8) | BIT(9) | BIT(10)) > +#define CM36672_PRX_LED_I_SHIFT 8 > +#define CM36672_PRX_LED_I0 0x0000 /* 50mA */ > +#define CM36672_PRX_LED_I1 0x0100 /* 75mA */ > +#define CM36672_PRX_LED_I2 0x0200 /* 100mA */ > +#define CM36672_PRX_LED_I3 0x0300 /* 120mA */ > +#define CM36672_PRX_LED_I4 0x0400 /* 140mA */ > +#define CM36672_PRX_LED_I5 0x0500 /* 160mA */ > +#define CM36672_PRX_LED_I6 0x0600 /* 180mA */ > +#define CM36672_PRX_LED_I7 0x0700 /* 200mA */ > + > +#define CM36672_PRX_CONF3_DEFAULT CM36672_PRX_LED_I0 > + > +/* PRX_CANC */ > +#define CM36672_PRX_CANC_DEFAULT 0x0000 > + > +/* PRX_THDL */ > +#define CM36672_PRX_THDL_DEFAULT 0x0005 > + > +/* PRX_THDH */ > +#define CM36672_PRX_THDH_DEFAULT 0x000A > + > +/* INT_FLAG */ > +#define CM36672_INT_PRX_CLOSE BIT(9) > +#define CM36672_INT_PRX_AWAY BIT(8) > + > +struct cm36672_it_scale { > + u8 it; > + int val; > + int val2; > +}; > + > +static const struct cm36672_it_scale cm36672_prx_it_scales[] = { > + {0, 0, 100}, /* 0.00010 */ > + {1, 0, 150}, /* 0.00015 */ > + {2, 0, 200}, /* 0.00020 */ > + {3, 0, 250}, /* 0.00025 */ > + {4, 0, 300}, /* 0.00030 */ > + {5, 0, 350}, /* 0.00035 */ > + {6, 0, 400}, /* 0.00040 */ > + {7, 0, 800}, /* 0.00080 */ > +}; > + > +#define CM36672_PRX_INT_TIME_AVAIL \ > + "0.000100 0.000150 0.000200 0.000250 " \ > + "0.000300 0.000350 0.000400 0.000800" > + > +static const u16 cm36672_regs_default[] = { > + CM36672_RESERVED00_DEFAULT, > + CM36672_RESERVED01_DEFAULT, > + CM36672_RESERVED02_DEFAULT, > + CM36672_PRX_CONF_DEFAULT, > + CM36672_PRX_CONF3_DEFAULT, > + CM36672_PRX_CANC_DEFAULT, > + CM36672_PRX_THDL_DEFAULT, > + CM36672_PRX_THDH_DEFAULT, > +}; > + > +struct cm36672_chip { > + const struct cm36672_platform_data *pdata; > + struct i2c_client *client; > + struct mutex lock; > + u16 regs[CM36672_REGS_NUM]; > +}; > + > +#define CM36672_DRIVER_NAME "cm36672" > + > +#ifdef CONFIG_OF > +void cm36672_mod_u16(u16 *reg, u16 mask, u8 shift, u16 val) static > +{ > + *reg &= ~mask; > + *reg |= (u16)(val << shift); > +} > + > +void cm36672_parse_dt(struct cm36672_chip *chip) static > +{ > + struct device_node *dn = chip->client->dev.of_node; > + u32 temp_val; > + > + /* parse by registers */ > + if (!of_property_read_u32(dn, "capella,reg_prx_conf", &temp_val)) > + chip->regs[CM36672_ADDR_PRX_CONF] = (u16)temp_val; > + if (!of_property_read_u32(dn, "capella,reg_prx_conf3", &temp_val)) > + chip->regs[CM36672_ADDR_PRX_CONF3] = (u16)temp_val; > + if (!of_property_read_u32(dn, "capella,reg_prx_canc", &temp_val)) > + chip->regs[CM36672_ADDR_PRX_CANC] = (u16)temp_val; > + if (!of_property_read_u32(dn, "capella,reg_prx_thdl", &temp_val)) > + chip->regs[CM36672_ADDR_PRX_THDL] = (u16)temp_val; > + if (!of_property_read_u32(dn, "capella,reg_prx_thdh", &temp_val)) > + chip->regs[CM36672_ADDR_PRX_THDH] = (u16)temp_val; > + > + /* parse by name */ > + if (!of_property_read_u32(dn, "capella,name_prx_int_rising", &temp_val)) > + cm36672_mod_u16(&chip->regs[CM36672_ADDR_PRX_CONF], > + CM36672_PRX_INT_RISING, 0, > + temp_val ? CM36672_PRX_INT_RISING : 0); > + if (!of_property_read_u32(dn, "capella,name_prx_int_falling", > + &temp_val)) > + cm36672_mod_u16(&chip->regs[CM36672_ADDR_PRX_CONF], > + CM36672_PRX_INT_FALLING, 0, > + temp_val ? CM36672_PRX_INT_FALLING : 0); > + if (!of_property_read_u32(dn, "capella,name_prx_duty", &temp_val)) > + cm36672_mod_u16(&chip->regs[CM36672_ADDR_PRX_CONF], > + CM36672_PRX_DR_MASK, > + CM36672_PRX_DR_SHIFT, > + (u16)temp_val); > + if (!of_property_read_u32(dn, "capella,name_prx_pers", &temp_val)) > + cm36672_mod_u16(&chip->regs[CM36672_ADDR_PRX_CONF], > + CM36672_PRX_PERS_MASK, > + CM36672_PRX_PERS_SHIFT, > + (u16)temp_val); > + if (!of_property_read_u32(dn, "capella,name_prx_it", &temp_val)) > + cm36672_mod_u16(&chip->regs[CM36672_ADDR_PRX_CONF], > + CM36672_PRX_IT_MASK, > + CM36672_PRX_IT_SHIFT, > + (u16)temp_val); > + if (!of_property_read_u32(dn, "capella,name_prx_led_current", > + &temp_val)) > + cm36672_mod_u16(&chip->regs[CM36672_ADDR_PRX_CONF3], > + CM36672_PRX_LED_I_MASK, > + CM36672_PRX_LED_I_SHIFT, > + (u16)temp_val); > + if (!of_property_read_u32(dn, "capella,name_prx_threshold_rising", > + &temp_val)) > + chip->regs[CM36672_ADDR_PRX_THDH] = (u16)temp_val; > + if (!of_property_read_u32(dn, "capella,name_prx_threshold_falling", > + &temp_val)) > + chip->regs[CM36672_ADDR_PRX_THDL] = (u16)temp_val; > +} > +#endif > + > +#ifdef CONFIG_ACPI > +/** > + * cm36672_acpi_get_cpm_info() - Get CPM object from ACPI > + * @client: pointer of struct i2c_client. > + * @obj_name: pointer of ACPI object name. > + * @count: maximum size of return array. > + * @vals: pointer of array for return elements. > + * > + * Convert ACPI CPM table to array. Special thanks Srinivas Pandruvada's thanks for > + * help to implement this routine. > + * > + * Return: -ENODEV for fail. Otherwise is number of elements. Otherwise the number of elements > + */ > +static int cm36672_acpi_get_cpm_info(struct i2c_client *client, char *obj_name, > + int count, u64 *vals) > +{ > + acpi_handle handle; > + struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; > + int i; > + acpi_status status; > + union acpi_object *cpm; > + > + handle = ACPI_HANDLE(&client->dev); > + if (!handle) > + return -ENODEV; > + > + status = acpi_evaluate_object(handle, obj_name, NULL, &buffer); > + if (ACPI_FAILURE(status)) { > + dev_err(&client->dev, "object %s not found\n", obj_name); > + return -ENODEV; > + } > + > + cpm = buffer.pointer; > + for (i = 0; i < cpm->package.count && i < count; ++i) { > + union acpi_object *elem; > + > + elem = &(cpm->package.elements[i]); > + vals[i] = elem->integer.value; > + } > + > + kfree(buffer.pointer); > + > + return i; > +} > + > +void cm36672_parse_acpi(struct cm36672_chip *chip) static > +{ > + struct i2c_client *client = chip->client; > + int cpm_elem_count, i; > + u64 cpm_elems[20]; > + > + cpm_elem_count = cm36672_acpi_get_cpm_info(client, "CPM0", > + ARRAY_SIZE(cpm_elems), > + cpm_elems); > + > + if (cpm_elem_count > 0) { > + int header_num = 3; > + int regs_bmp = cpm_elems[2]; > + int reg_num = cpm_elem_count - header_num; > + > + if (reg_num > CM36672_REGS_NUM) > + reg_num = CM36672_REGS_NUM; > + for (i = 0; i < reg_num; i++) > + if (regs_bmp & (1 << i)) > + chip->regs[i] = > + cpm_elems[header_num + i]; > + } > + drop newline > +} > + > +#endif > + > +/** > + * cm36672_setup_reg() - Initialize sensor to default values. > + * @chip: pointer of struct cm36672_chip > + * > + * Return: 0 for success; otherwise for error code. otherwise an error code > + */ > +static int cm36672_setup_reg(struct cm36672_chip *chip) > +{ > + int i, ret; > + u16 prx_conf; > + > + memcpy((char *)&chip->regs, (char *)&cm36672_regs_default, > + sizeof(cm36672_regs_default)); > + > +#ifdef CONFIG_OF > + if (chip->client->dev.of_node) > + cm36672_parse_dt(chip); > +#endif > + > +#ifdef CONFIG_ACPI > + if (ACPI_HANDLE(&chip->client->dev)) > + cm36672_parse_acpi(chip); > +#endif > + drop newline > + > + /* Store regs[CM36672_ADDR_PRX_CONF] */ > + prx_conf = chip->regs[CM36672_ADDR_PRX_CONF]; > + > + /* Disable INT when initialize registers */ > + chip->regs[CM36672_ADDR_PRX_CONF] &= ~CM36672_PRX_INT_RISING; > + chip->regs[CM36672_ADDR_PRX_CONF] &= ~CM36672_PRX_INT_FALLING; > + > + for (i = 0; i < CM36672_REGS_NUM; i++) { > + ret = i2c_smbus_write_word_data(chip->client, i, > + chip->regs[i]); > + if (ret < 0) > + return ret; > + } > + > + /* Restore regs[CM36672_ADDR_PRX_CONF] */ > + chip->regs[CM36672_ADDR_PRX_CONF] = prx_conf; > + > + /* Force to clear flags */ > + ret = i2c_smbus_read_word_data(chip->client, CM36672_ADDR_STATUS); > + if (ret < 0) { > + dev_err(&chip->client->dev, > + "%s: Failed to read Status register, err= %d\n", > + __func__, ret); > + return ret; > + } > + > + return 0; > +} > + > +/** > + * cm36672_irq_handler() - Interrupt handling routine. > + * @irq: irq number > + * @private: pointer of void > + * > + * Return: 0 for success; otherwise for error code. > + */ > +static irqreturn_t cm36672_irq_handler(int irq, void *private) > +{ > + struct iio_dev *indio_dev = private; > + struct cm36672_chip *chip = iio_priv(indio_dev); > + struct i2c_client *client = chip->client; > + int ev_dir, ret; > + u64 ev_code; > + > + ret = i2c_smbus_read_word_data(chip->client, CM36672_ADDR_STATUS); > + if (ret < 0) { > + dev_err(&client->dev, > + "%s: Data read failed: %d\n", __func__, ret); 'Status read failed' maybe > + return IRQ_HANDLED; > + } > + ev_dir = 0; use initialization, not assignment > + if (ret & CM36672_INT_PRX_CLOSE) > + ev_dir |= IIO_EV_DIR_RISING; > + if (ret & CM36672_INT_PRX_AWAY) > + ev_dir |= IIO_EV_DIR_FALLING; > + > + ev_code = IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0, > + IIO_EV_TYPE_THRESH, ev_dir); > + > + iio_push_event(indio_dev, ev_code, iio_get_time_ns()); > + > + return IRQ_HANDLED; > +} > + > +/** > + * cm36672_read_prx_it() - Get the current proximity integration time. > + * @chip: point of struct cm36672_chip > + * @val: point of the integer part of integration time > + * @val2: point of the micro part of integration time > + * > + * Return: IIO_VAL_INT_PLUS_MICRO for success; otherwise for error code. 'otherwise an error code', here and elsewhere > + */ > +static int cm36672_read_prx_it(struct cm36672_chip *chip, int *val, int *val2) > +{ > + u16 prx_it; > + int i; > + > + prx_it = chip->regs[CM36672_ADDR_PRX_CONF]; > + prx_it &= CM36672_PRX_IT_MASK; > + prx_it >>= CM36672_PRX_IT_SHIFT; > + for (i = 0; i < ARRAY_SIZE(cm36672_prx_it_scales); i++) { > + if (prx_it == cm36672_prx_it_scales[i].it) { > + *val = cm36672_prx_it_scales[i].val; > + *val2 = cm36672_prx_it_scales[i].val2; > + return IIO_VAL_INT_PLUS_MICRO; > + } > + } > + > + return -EINVAL; > +} > + > +/** > + * cm36672_write_prx_it() - Set the proximity integration time. > + * @chip: point of struct cm36672_chip > + * @val: the integer part of integration time > + * @val2: the micro part of integration time > + * > + * Return: 0 for success; otherwise for error code. > + */ > +static int cm36672_write_prx_it(struct cm36672_chip *chip, int val, int val2) > +{ > + struct i2c_client *client = chip->client; > + u16 prx_it, cmd; > + int i; > + s32 ret; > + > + for (i = 0; i < ARRAY_SIZE(cm36672_prx_it_scales); i++) { > + if (val == cm36672_prx_it_scales[i].val && > + val2 == cm36672_prx_it_scales[i].val2) { > + > + prx_it = cm36672_prx_it_scales[i].it; > + prx_it <<= CM36672_PRX_IT_SHIFT; > + > + cmd = chip->regs[CM36672_ADDR_PRX_CONF]; > + cmd &= ~CM36672_PRX_IT_MASK; > + cmd |= prx_it; > + ret = i2c_smbus_write_byte_data(client, > + CM36672_ADDR_PRX_CONF, > + cmd); > + if (ret < 0) > + return ret; > + chip->regs[CM36672_ADDR_PRX_CONF] = cmd; > + return 0; > + } > + } > + return -ERANGE; > +} > + > +/** > + * cm36672_prx_read() - Read from proximity sensor. > + * @indio_dev: point of struct iio_dev > + * @chan: point of struct iio_chan_spec > + * @val: point of integer > + * @val2: point of integer > + * @mask: iio_chan_info_enum > + * > + * Return: IIO_VAL type for success; otherwise for error code. > + */ > +static int cm36672_prx_read(struct iio_dev *indio_dev, > + struct iio_chan_spec const *chan, > + int *val, int *val2, long mask) > +{ > + struct cm36672_chip *chip = iio_priv(indio_dev); > + int ret; > + > + if (chan->type != IIO_PROXIMITY) > + return -EINVAL; > + > + switch (mask) { > + case IIO_CHAN_INFO_INT_TIME: > + *val = 0; not needed, *val is set by read_prx_it() > + ret = cm36672_read_prx_it(chip, val, val2); return cm36672_read_prx_it(..); > + break; > + case IIO_CHAN_INFO_RAW: > + case IIO_CHAN_INFO_OFFSET: > + ret = i2c_smbus_read_word_data( > + chip->client, > + mask == IIO_CHAN_INFO_RAW ? could use chan_spec's .address to avoid this check > + CM36672_ADDR_PRX : CM36672_ADDR_PRX_CANC); I'd find if (ret < 0) return ret; *val = ret; return IIO_VAL_INT; a bit cleaner > + if (ret >= 0) { > + *val = ret; > + ret = IIO_VAL_INT; > + } > + break; > + default: > + ret = -EINVAL; return -EINVAL; > + } > + > + return ret; > +} > + > +/** > + * cm36672_prx_write() - Write to proximity sensor. > + * @indio_dev: point of struct iio_dev > + * @chan: point of struct iio_chan_spec > + * @val: point of integer for integer part not a pointer, a value for write > + * @val2: point of integer for micro part not a pointer > + * @mask: point of iio_chan_info_enum > + * > + * Return: Equal or great than 0 for success; otherwise for error code. > + */ > +static int cm36672_prx_write(struct iio_dev *indio_dev, > + struct iio_chan_spec const *chan, > + int val, int val2, long mask) > +{ > + struct cm36672_chip *chip = iio_priv(indio_dev); > + > + switch (mask) { > + case IIO_CHAN_INFO_INT_TIME: > + return cm36672_write_prx_it(chip, val, val2); > + case IIO_CHAN_INFO_OFFSET: > + chip->regs[CM36672_ADDR_PRX_CANC] = val; > + return i2c_smbus_write_word_data( > + chip->client, > + CM36672_ADDR_PRX_CANC, > + chip->regs[CM36672_ADDR_PRX_CANC]); default: return -EINVAL; and drop return below > + } > + > + return -EINVAL; > +} > + > +enum cm36672_prx_attribute_type { > + CM36672_ATTR_TYPE_THRESH_FALLING_EN, > + CM36672_ATTR_TYPE_THRESH_FALLING, > + CM36672_ATTR_TYPE_THRESH_RISING_EN, > + CM36672_ATTR_TYPE_THRESH_RISING, > +}; > + > +struct cm36672_prx_attribute { > + struct device_attribute dev_attr; > + enum cm36672_prx_attribute_type type; > +}; > + > +static inline struct cm36672_prx_attribute * no inline, let the compiler figure it out > +to_cm36672_prx_attr(struct device_attribute *attr) cm36672_ prefix, maybe rename to cm36672_to_prx_attr > +{ > + return container_of(attr, struct cm36672_prx_attribute, dev_attr); > +} > + > +/** > + * show_prx_attr() - Show event attribute. cm36672_ prefix missing > + * @dev: point of struct device > + * @attr: point of struct device_attribute > + * @buf: point of buffer > + * > + * Return: length of buffer; Otherwise for error code. > + */ > +static ssize_t show_prx_attr(struct device *dev, > + struct device_attribute *attr, > + char *buf) > +{ > + struct iio_dev *indio_dev = dev_to_iio_dev(dev); > + struct cm36672_chip *chip = iio_priv(indio_dev); > + struct cm36672_prx_attribute *prx_attr = to_cm36672_prx_attr(attr); > + u16 val; > + > + switch (prx_attr->type) { > + case CM36672_ATTR_TYPE_THRESH_FALLING_EN: > + val = (chip->regs[CM36672_ADDR_PRX_CONF] & > + CM36672_PRX_INT_FALLING) ? 1 : 0; > + break; > + case CM36672_ATTR_TYPE_THRESH_RISING_EN: > + val = (chip->regs[CM36672_ADDR_PRX_CONF] & > + CM36672_PRX_INT_RISING) ? 1 : 0; > + break; > + case CM36672_ATTR_TYPE_THRESH_FALLING: > + val = chip->regs[CM36672_ADDR_PRX_THDL]; > + break; > + case CM36672_ATTR_TYPE_THRESH_RISING: > + val = chip->regs[CM36672_ADDR_PRX_THDH]; > + break; > + default: > + return -ENXIO; > + } > + > + return scnprintf(buf, PAGE_SIZE, "%u\n", val); > +} > + > +/** > + * store_prx_attr() - Store to event attribute. cm36672_ prefix > + * @dev: point of struct device > + * @attr: point of struct device_attribute > + * @buf: point of buffer > + * @len: length of buffer > + * > + * Return: length of buffer; otherwise for error code. > + */ > +static ssize_t store_prx_attr(struct device *dev, > + struct device_attribute *attr, > + const char *buf, size_t len) > +{ > + struct iio_dev *indio_dev = dev_to_iio_dev(dev); > + struct cm36672_chip *chip = iio_priv(indio_dev); > + struct cm36672_prx_attribute *prx_attr = to_cm36672_prx_attr(attr); > + u16 val, cmd; > + int ret; > + > + if (kstrtou16(buf, 0, &val)) > + return -EINVAL; > + > + mutex_lock(&chip->lock); > + switch (prx_attr->type) { > + case CM36672_ATTR_TYPE_THRESH_FALLING_EN: > + cmd = chip->regs[CM36672_ADDR_PRX_CONF]; > + if (!val) > + cmd &= ~CM36672_PRX_INT_FALLING; > + else > + cmd |= CM36672_PRX_INT_FALLING; > + ret = i2c_smbus_write_word_data(chip->client, > + CM36672_ADDR_PRX_CONF, > + cmd); > + if (!ret) > + chip->regs[CM36672_ADDR_PRX_CONF] = cmd; > + break; > + case CM36672_ATTR_TYPE_THRESH_RISING_EN: > + cmd = chip->regs[CM36672_ADDR_PRX_CONF]; > + if (!val) > + cmd &= ~CM36672_PRX_INT_RISING; > + else > + cmd |= CM36672_PRX_INT_RISING; > + ret = i2c_smbus_write_word_data(chip->client, > + CM36672_ADDR_PRX_CONF, > + cmd); > + if (!ret) > + chip->regs[CM36672_ADDR_PRX_CONF] = cmd; > + break; > + case CM36672_ATTR_TYPE_THRESH_FALLING: > + if (val > chip->regs[CM36672_ADDR_PRX_THDH]) { > + ret = -ERANGE; > + break; > + } > + ret = i2c_smbus_write_word_data(chip->client, > + CM36672_ADDR_PRX_THDL, > + val); > + if (!ret) > + chip->regs[CM36672_ADDR_PRX_THDL] = val; > + break; > + case CM36672_ATTR_TYPE_THRESH_RISING: > + if (val < chip->regs[CM36672_ADDR_PRX_THDL]) { > + ret = -ERANGE; > + break; > + } > + ret = i2c_smbus_write_word_data(chip->client, > + CM36672_ADDR_PRX_THDH, > + val); > + if (!ret) > + chip->regs[CM36672_ADDR_PRX_THDH] = val; > + break; > + default: > + ret = -ENXIO; > + } > + mutex_unlock(&chip->lock); > + if (ret < 0) > + return ret; > + > + return len; > +} > + > +#define PRX_ATTR(_name, _mode, _show, _store, _type) \ > + { .dev_attr = __ATTR(_name, _mode, _show, _store), \ > + .type = _type } > + > +#define CM36672_PRX_ATTR(_name, _mode, _show, _store, _type) \ > + struct cm36672_prx_attribute cm36672_prx_attr_##_name = \ > + PRX_ATTR(_name, _mode, _show, _store, _type) > + > +static CM36672_PRX_ATTR(in_proximity_thresh_falling_en, > + S_IRUGO | S_IWUSR, > + show_prx_attr, > + store_prx_attr, > + CM36672_ATTR_TYPE_THRESH_FALLING_EN); > + > +static CM36672_PRX_ATTR(in_proximity_thresh_rising_en, > + S_IRUGO | S_IWUSR, > + show_prx_attr, > + store_prx_attr, > + CM36672_ATTR_TYPE_THRESH_RISING_EN); > + > +static CM36672_PRX_ATTR(in_proximity_thresh_falling_value, > + S_IRUGO | S_IWUSR, > + show_prx_attr, > + store_prx_attr, > + CM36672_ATTR_TYPE_THRESH_FALLING); > + > +static CM36672_PRX_ATTR(in_proximity_thresh_rising_value, > + S_IRUGO | S_IWUSR, > + show_prx_attr, > + store_prx_attr, > + CM36672_ATTR_TYPE_THRESH_RISING); > + > +static struct attribute *cm36672_prx_event_attributes[] = { > + &cm36672_prx_attr_in_proximity_thresh_falling_en.dev_attr.attr, > + &cm36672_prx_attr_in_proximity_thresh_rising_en.dev_attr.attr, > + &cm36672_prx_attr_in_proximity_thresh_falling_value.dev_attr.attr, > + &cm36672_prx_attr_in_proximity_thresh_rising_value.dev_attr.attr, > + NULL, > +}; > + > +static struct attribute_group cm36672_prx_event_attribute_group = { > + .attrs = cm36672_prx_event_attributes > +}; > + > +static const struct iio_event_spec cm36672_event_spec[] = { > + { > + .type = IIO_EV_TYPE_THRESH, > + .dir = IIO_EV_DIR_EITHER, > + .mask_separate = BIT(IIO_EV_INFO_ENABLE), > + } > +}; > + > +static const struct iio_chan_spec cm36672_channels[] = { > + { > + .type = IIO_PROXIMITY, > + .info_mask_separate = > + BIT(IIO_CHAN_INFO_RAW), > + BIT(IIO_CHAN_INFO_INT_TIME), > + BIT(IIO_CHAN_INFO_OFFSET), > + }, > +}; > + > +static const struct iio_chan_spec cm36672_channels_no_irq[] = { > + { > + .type = IIO_PROXIMITY, > + .info_mask_separate = > + BIT(IIO_CHAN_INFO_INT_TIME), > + BIT(IIO_CHAN_INFO_RAW), > + BIT(IIO_CHAN_INFO_OFFSET), > + }, > +}; > + > +static IIO_CONST_ATTR(in_proximity_integration_time_available, > + CM36672_PRX_INT_TIME_AVAIL); > + > +static struct attribute *cm36672_attributes[] = { > + &iio_const_attr_in_proximity_integration_time_available.dev_attr.attr, > + NULL, > +}; > + > +static const struct attribute_group cm36672_attribute_group = { > + .attrs = cm36672_attributes > +}; > + > +static const struct iio_info cm36672_info = { > + .driver_module = THIS_MODULE, > + .read_raw = &cm36672_prx_read, > + .write_raw = &cm36672_prx_write, > + .attrs = &cm36672_attribute_group, > + .event_attrs = &cm36672_prx_event_attribute_group, > +}; > + > +static const struct iio_info cm36672_info_no_irq = { > + .driver_module = THIS_MODULE, > + .read_raw = &cm36672_prx_read, > + .write_raw = &cm36672_prx_write, > + .attrs = &cm36672_attribute_group, > +}; > + > +int irq; > +module_param(irq, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); > +MODULE_PARM_DESC(irq, "irq"); not sure if this is acceptible these days... > + > +static int cm36672_probe(struct i2c_client *client, > + const struct i2c_device_id *id) > +{ > + struct cm36672_chip *chip; > + struct iio_dev *indio_dev; > + int ret; > + > + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip)); > + if (!indio_dev) > + return -ENOMEM; > + > + chip = iio_priv(indio_dev); > + i2c_set_clientdata(client, indio_dev); > + chip->client = client; > + > + mutex_init(&chip->lock); > + indio_dev->dev.parent = &client->dev; > + indio_dev->channels = cm36672_channels; > + indio_dev->num_channels = ARRAY_SIZE(cm36672_channels); > + indio_dev->name = CM36672_DRIVER_NAME; > + indio_dev->modes = INDIO_DIRECT_MODE; > + > + ret = cm36672_setup_reg(chip); > + if (ret) { > + dev_err(&client->dev, "%s: register setup failed\n", __func__); > + return ret; > + } > + > + if (irq) > + client->irq = irq; > + indio_dev->info = (client->irq) ? parenthesis not needed > + &cm36672_info : &cm36672_info_no_irq; > + this checks for client-irq three times in different forms, maybe use one check? > + if (client->irq) { > + ret = request_threaded_irq(client->irq, NULL, > + cm36672_irq_handler, > + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, > + "cm36672", indio_dev); > + if (ret) { > + dev_err(&client->dev, > + "%s: request irq failed\n", > + __func__); > + return ret; > + } > + > + /* Enable interrupt if default request*/ > + if (chip->regs[CM36672_ADDR_PRX_CONF] & CM36672_PRX_INT_MASK) { > + ret = i2c_smbus_write_word_data(chip->client, > + CM36672_ADDR_PRX_CONF, > + chip->regs[CM36672_ADDR_PRX_CONF]); > + if (ret) { > + dev_err(&client->dev, > + "%s: enable interrupt failed\n", > + __func__); > + return ret; > + } > + } > + } > + > + ret = iio_device_register(indio_dev); > + if (ret) { > + dev_err(&client->dev, > + "%s: regist device failed\n", registering > + __func__); > + if (client->irq) > + free_irq(client->irq, indio_dev); > + } > + > + return 0; > +} > + > +static int cm36672_remove(struct i2c_client *client) > +{ > + struct iio_dev *indio_dev = i2c_get_clientdata(client); > + struct cm36672_chip *chip = iio_priv(indio_dev); > + > + i2c_smbus_write_word_data( > + chip->client, > + CM36672_ADDR_PRX_CONF, > + CM36672_PRX_DISABLE); > + if (client->irq) > + free_irq(client->irq, indio_dev); > + iio_device_unregister(indio_dev); > + return 0; > +} > + > +static const struct i2c_device_id cm36672_id[] = { > + { "cm36672", 0 }, > + { } > +}; > + > +MODULE_DEVICE_TABLE(i2c, cm36672_id); > + > +static const struct of_device_id cm36672_of_match[] = { > + { .compatible = "capella,cm36672" }, > + { } > +}; > + > +#ifdef CONFIG_ACPI > +static const struct acpi_device_id cm36672_acpi_match[] = { > + { "CPLM6672", 0}, > + {}, > +}; > + > +MODULE_DEVICE_TABLE(acpi, cm36672_acpi_match); > +#endif > + > +#ifdef CONFIG_PM_SLEEP > +static int cm36672_suspend(struct device *dev) > +{ > + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); > + struct cm36672_chip *chip = iio_priv(indio_dev); > + int ret; > + > + chip->regs[CM36672_ADDR_PRX_CONF] |= CM36672_PRX_DISABLE; > + ret = i2c_smbus_write_byte_data( > + chip->client, > + CM36672_ADDR_PRX_CONF, > + chip->regs[CM36672_ADDR_PRX_CONF]); > + return ret; > +} > + > +static int cm36672_resume(struct device *dev) > +{ > + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); > + struct cm36672_chip *chip = iio_priv(indio_dev); > + int i, ret; > + > + chip->regs[CM36672_ADDR_PRX_CONF] &= ~CM36672_PRX_DISABLE; > + > + /* Initialize registers*/ whitespace before */ please > + for (i = 0; i < CM36672_REGS_NUM; i++) { > + ret = i2c_smbus_write_word_data( > + chip->client, > + i, > + chip->regs[i]); > + if (ret < 0) > + return ret; > + } > + > + return ret; > +} > + > +static const struct dev_pm_ops cm36672_pm_ops = { > + SET_SYSTEM_SLEEP_PM_OPS(cm36672_suspend, cm36672_resume)}; > +#endif > + > +static struct i2c_driver cm36672_driver = { > + .driver = { > + .name = CM36672_DRIVER_NAME, > + .of_match_table = cm36672_of_match, > +#ifdef CONFIG_ACPI > + .acpi_match_table = ACPI_PTR(cm36672_acpi_match), > +#endif > + .owner = THIS_MODULE, > +#ifdef CONFIG_PM_SLEEP > + .pm = &cm36672_pm_ops, > +#endif > + }, > + .id_table = cm36672_id, > + .probe = cm36672_probe, > + .remove = cm36672_remove, > +}; > + > +module_i2c_driver(cm36672_driver); > + > +MODULE_AUTHOR("Kevin Tsai <capellamicro@xxxxxxxxx>"); > +MODULE_DESCRIPTION("CM36672 proximity sensor driver"); > +MODULE_LICENSE("GPL v2"); > -- Peter Meerwald-Stadler +43-664-2444418 (mobile) -- To unsubscribe from this list: send the line "unsubscribe linux-iio" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html