iio: cm36651: Add CM36651 proximity/light sensor driver
Subsystems (2)
PATCH v2 1/2 iio: cm36651: Add CM36651 proximity/light sensor driver
This patch add a new driver for Capella CM36651 proximity and RGB sensor.
--- drivers/iio/light/Kconfig | 11 + drivers/iio/light/cm36651.c | 591 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 602 insertions(+) create mode 100644 drivers/iio/light/cm36651.c
drivers/iio/light/Kconfig +11 -0
@@ -27,6 +27,17 @@ config APDS9300 To compile this driver as a module, choose M here: the module will be called apds9300. +config CM36651 + depends on I2C + tristate "CM36651 driver" + help + Say Y here if you use cm36651. + This option enables proximity & RGB sensor using + Capella cm36651 device driver. + + To compile this driver as a module, choose M here: + the module will be called cm36651. + config GP2AP020A00F tristate "Sharp GP2AP020A00F Proximity/ALS sensor" depends on I2C
drivers/iio/light/cm36651.c new file +591 -0
591 lines not on this page · raw message
Signature · 6 lines
1.7.9.5 -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Quoted 2 lines
This patch add a new driver for Capella CM36651 proximity and RGB sensor.
moving in the right direction!
couple of comments inline
Quoted 42 lines
drivers/iio/light/Kconfig +11
-0@@ -27,6 +27,17 @@ config APDS9300 To compile this driver as a module, choose M here: the module will be called apds9300. +config CM36651 + depends on I2C + tristate "CM36651 driver" + help + Say Y here if you use cm36651. + This option enables proximity & RGB sensor using + Capella cm36651 device driver. + + To compile this driver as a module, choose M here: + the module will be called cm36651. + config GP2AP020A00F tristate "Sharp GP2AP020A00F Proximity/ALS sensor" depends on I2Cdrivers/iio/light/cm36651.c new file +14
-0@@ -0,0 +1,591 @@ +/* +* Copyright (C) 2013 Samsung Electronics Co., Ltd. +* Author: Beomho Seo <[email protected]> +* +* 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. +*/ + +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/mutex.h> +#include <linux/module.h> +#include <linux/interrupt.h>
drivers/iio/light/cm36651.c line 24
Quoted 10 lines
drivers/iio/light/cm36651.c new file +10
-0+#include <linux/regulator/consumer.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/events.h> + +/* Slave address 0x19 for PS of 7 bit addressing protocol for I2C */ +#define CM36651_I2C_ADDR_PS 0x19 + +/* Ambient light sensor */ +#define CM36651_CS_CONF1 0x00
what does CS stand for?
maybe ALS?
Quoted 15 lines
drivers/iio/light/cm36651.c new file +15
-0+#define CM36651_CS_CONF2 0x01 +#define CM36651_ALS_WH_M 0x02 +#define CM36651_ALS_WH_L 0x03 +#define CM36651_ALS_WL_M 0x04 +#define CM36651_ALS_WL_L 0x05 +#define CM36651_CS_CONF3 0x06 +#define CM36651_CS_REG_NUM 0x07 + +/* Proximity sensor */ +#define CM36651_PS_CONF1 0x00 +#define CM36651_PS_THD 0x01 +#define CM36651_PS_CANC 0x02 +#define CM36651_PS_CONF2 0x03 +#define CM36651_PS_REG_NUM 0x04 +
drivers/iio/light/cm36651.c line 49
Quoted 10 lines
drivers/iio/light/cm36651.c new file +10
-0+/* CS_CONF1 command code */ +#define CM36651_ALS_ENABLE 0x00 +#define CM36651_ALS_DISABLE 0x01 +#define CM36651_ALS_INT_EN 0x02 +#define CM36651_ALS_THRES 0x04 + +/* CS_CONF2 command code */ +#define CM36651_CS_CONF2_DEFAULT_BIT 0x08 + +/* CS_CONF3 channel integration time */
which unit?
this could be made configurable via INT_TIME channel
Quoted 56 lines
drivers/iio/light/cm36651.c new file +56
-0+#define CM36651_IT_80 0x00 +#define CM36651_W_IT_160 0x01 +#define CM36651_W_IT_320 0x02 +#define CM36651_W_IT_640 0x03 +#define CM36651_B_IT_160 0x04 +#define CM36651_B_IT_320 0x08 +#define CM36651_B_IT_640 0x0C +#define CM36651_G_IT_160 0x10 +#define CM36651_G_IT_320 0x20 +#define CM36651_G_IT_640 0x30 +#define CM36651_R_IT_160 0x40 +#define CM36651_R_IT_320 0x80 +#define CM36651_R_IT_640 0xC0 + +/* PS_CONF1 command code */ +#define CM36651_PS_ENABLE 0x00 +#define CM36651_PS_DISABLE 0x01 +#define CM36651_PS_INT_EN 0x02 +#define CM36651_PS_PERS_2 0x04 +#define CM36651_PS_PERS_3 0x08 +#define CM36651_PS_PERS_4 0x0C +#define CM36651_PS_IT_2 0x10 +#define CM36651_PS_IT_3 0x20 +#define CM36651_PS_IT_4 0x30 + +/* PS_THD command code */ +#define CM36651_PS_INITIAL_THD 0x09 + +/* PS_CANC command code */ +#define CM36651_PS_CANC_DEFAULT 0x00 + +/* PS_CONF2 command code */ +#define CM36651_PS_HYS_1 0x00 +#define CM36651_PS_HYS_2 0x01 +#define CM36651_PS_SMART_PERS_EN 0x02 +#define CM36651_PS_MS 0x10 + +#define CM36651_SCAN_MODE_LIGHT 0 +#define CM36651_SCAN_MODE_PROX 1 + +enum cm36651_operation_mode { +CM36651_LIGHT_EN , +CM36651_PROXIMITY_EN , +CM36651_PROXIMITY_EV_EN , +}; + +enum cm36651_light_channel_idx { +CM36651_LIGHT_CHANNEL_IDX_RED , +CM36651_LIGHT_CHANNEL_IDX_GREEN , +CM36651_LIGHT_CHANNEL_IDX_BLUE , +CM36651_LIGHT_CHANNEL_IDX_CLEAR , +}; + +enum cm36651_command { +CM36651_CMD_READ_RAW_LIGHT , +CM36651_CMD_READ_RAW_PROXIMITY ,
drivers/iio/light/cm36651.c line 115
Quoted 10 lines
drivers/iio/light/cm36651.c new file +10
-0+CM36651_CMD_PROX_EV_EN , + CM36651_CMD_PROX_EV_DIS, +}; + +enum cm36651_proximity_event { +CM36651_CLOSE_PROXIMITY , +CM36651_FAR_PROXIMITY , +}; + +static u8 cm36651_als_reg[2] = {
const
drivers/iio/light/cm36651.c line 120
Quoted 5 lines
drivers/iio/light/cm36651.c new file +5
-0+CM36651_CS_CONF1 , + CM36651_CS_CONF2, +}; + +static u8 cm36651_ps_reg[4] = {
const
Quoted 20 lines
drivers/iio/light/cm36651.c new file +20
-0+CM36651_PS_CONF1 , +CM36651_PS_THD , +CM36651_PS_CANC , + CM36651_PS_CONF2, +}; + +struct cm36651_data { +const struct cm36651_platform_data *pdata; +struct i2c_client *client; +struct i2c_client *ps_client; +struct mutex lock; +struct regulator *vled_reg; +unsigned long flags; +u8 cs_ctrl_regs[2 ]; +u8 ps_ctrl_regs[4 ]; +u16 color[4 ]; +}; + +static int cm36651_setup_reg (struct cm36651_data *cm36651) +{
drivers/iio/light/cm36651.c line 150
Quoted 10 lines
drivers/iio/light/cm36651.c new file +10
-0+struct i2c_client *client = cm36651->client; +struct i2c_client *ps_client = cm36651->ps_client; +int i, ret; + +/* ALS initialization */ + cm36651->cs_ctrl_regs[CM36651_CS_CONF1 ] =CM36651_ALS_ENABLE + |CM36651_ALS_THRES ; + cm36651->cs_ctrl_regs[CM36651_CS_CONF2 ] =CM36651_CS_CONF2_DEFAULT_BIT ; + +for (i =0 ; i <2 ; i++) {
use the CS_REG_NUM #defined
Quoted 5 lines
drivers/iio/light/cm36651.c new file +5
-0+ ret =i2c_smbus_write_byte_data (client, cm36651_als_reg[i], + cm36651->cs_ctrl_regs[i]); +if (ret <0 ) + goto err_setup_reg; + }
drivers/iio/light/cm36651.c line 165
Quoted 10 lines
drivers/iio/light/cm36651.c new file +10
-0+ +/* PS initialization */ + cm36651->ps_ctrl_regs[CM36651_PS_CONF1 ] =CM36651_PS_ENABLE + |CM36651_PS_PERS_4 |CM36651_PS_IT_4 ; + cm36651->ps_ctrl_regs[CM36651_PS_THD ] =CM36651_PS_INITIAL_THD ; + cm36651->ps_ctrl_regs[CM36651_PS_CANC ] =CM36651_PS_CANC_DEFAULT ; + cm36651->ps_ctrl_regs[CM36651_PS_CONF2 ] =CM36651_PS_HYS_2 + |CM36651_PS_SMART_PERS_EN |CM36651_PS_MS ; + +for (i =0 ; i <4 ; i++) {
use the PS_REG_NUM #defined
drivers/iio/light/cm36651.c line 177
Quoted 12 lines
drivers/iio/light/cm36651.c new file +12
-0+ ret =i2c_smbus_write_byte_data (ps_client, cm36651_ps_reg[i], + cm36651->ps_ctrl_regs[i]); +if (ret <0 ) + goto err_setup_reg; + } + + ret =i2c_smbus_read_byte (cm36651->ps_client); +if (ret <0 ) + goto err_setup_reg; + +/* Printing the initial proximity value with no contact */ +dev_dbg (&client->dev,"Initial proximity value: %d\n" , ret);
maybe drop the initial read? not needed when not in debug mode
Quoted 18 lines
drivers/iio/light/cm36651.c new file +18
-0+ +/* Device turn off */ + ret =i2c_smbus_write_byte_data (client,CM36651_CS_CONF1 , +CM36651_ALS_DISABLE ); +if (ret <0 ) + goto err_setup_reg; + + ret =i2c_smbus_write_byte_data (cm36651->ps_client, +CM36651_PS_CONF1 ,CM36651_PS_DISABLE ); +if (ret <0 ) + goto err_setup_reg; + +return 0 ; + +err_setup_reg: +dev_err (&client->dev,"Register setup failed: %d\n" , ret); +return ret; +}
drivers/iio/light/cm36651.c line 205
Quoted 10 lines
drivers/iio/light/cm36651.c new file +10
-0+ +static int cm36651_read_output (struct cm36651_data *cm36651, +struct iio_chan_spec const *chan,int *val) +{ +struct i2c_client *client = cm36651->client; +int ret = -EINVAL ; + +switch (chan->scan_index) { +case CM36651_SCAN_MODE_LIGHT : + *val =i2c_smbus_read_word_data (client, chan->channel);
the CM36651 seems to be special here how data is returned; there seems to be no dedicated register to pass the measurement?
Quoted 62 lines
drivers/iio/light/cm36651.c new file +62
-0+if (val <0 ) + goto read_err; + + ret =i2c_smbus_write_byte_data (client,CM36651_CS_CONF1 , +CM36651_ALS_DISABLE ); +if (ret <0 ) + goto write_err; + + ret =IIO_VAL_INT ; +break ; +case CM36651_SCAN_MODE_PROX : + *val =i2c_smbus_read_byte (cm36651->ps_client); +if (val <0 ) + goto read_err; + + ret =i2c_smbus_write_byte_data (cm36651->ps_client, +CM36651_PS_CONF1 ,CM36651_PS_DISABLE ); +if (ret <0 ) + goto write_err; + + ret =IIO_VAL_INT ; +break ; + } + +return ret; + +read_err: +dev_err (&client->dev,"Register read failed\n" ); +return ret; + +write_err: +dev_err (&client->dev,"Register write failed\n" ); +return ret; +} + +static irqreturn_t cm36651_irq_handler (int irq,void *data) +{ +struct iio_dev *indio_dev = data; +struct cm36651_data *cm36651 =iio_priv (indio_dev); +struct i2c_client *client = cm36651->client; +int ev_dir, val, ret; +u64 ev_code; + + ret =i2c_smbus_read_byte (cm36651->ps_client); +if (ret <0 ) { +dev_err (&client->dev, +"%s: Data read failed: %d\n" , __func__, ret); +return IRQ_HANDLED ; + } + +if (ret <CM36651_PS_INITIAL_THD ) { + ev_dir =IIO_EV_DIR_RISING ; + val =CM36651_FAR_PROXIMITY ; + }else { + ev_dir =IIO_EV_DIR_FALLING ; + val =CM36651_CLOSE_PROXIMITY ; + } + + ev_code =IIO_UNMOD_EVENT_CODE (IIO_PROXIMITY , +CM36651_CMD_READ_RAW_PROXIMITY , +IIO_EV_TYPE_THRESH , ev_dir); +
drivers/iio/light/cm36651.c line 277
Quoted 10 lines
drivers/iio/light/cm36651.c new file +10
-0+iio_push_event (indio_dev, ev_code,iio_get_time_ns ()); + +return IRQ_HANDLED ; +} + +static int cm36651_set_operation_mode (struct cm36651_data *cm36651, +enum cm36651_command cmd) +{ +struct i2c_client *client = cm36651->client; +int ret =0 ;
unknown cmd are silently not handled;
probably better to set ret = -EINVAL
Quoted 10 lines
drivers/iio/light/cm36651.c new file +10
-0+int i; + +switch (cmd) { +case CM36651_CMD_READ_RAW_LIGHT : + ret =i2c_smbus_write_byte_data (client,CM36651_CS_CONF1 , + cm36651->cs_ctrl_regs[CM36651_CS_CONF1 ]); +break ; +case CM36651_CMD_READ_RAW_PROXIMITY : + ret =i2c_smbus_write_byte_data (cm36651->ps_client, +CM36651_PS_CONF1 , cm36651->ps_ctrl_regs[CM36651_PS_CONF1 ]);
drivers/iio/light/cm36651.c line 297
Quoted 10 lines
drivers/iio/light/cm36651.c new file +10
-0+break ; +case CM36651_CMD_PROX_EV_EN : +if (test_bit (CM36651_PROXIMITY_EV_EN , &cm36651->flags)) { +dev_err (&client->dev, +"Already proximity event enable state\n" ); +return ret; + } +set_bit (CM36651_PROXIMITY_EV_EN , &cm36651->flags); + +for (i =0 ; i <4 ; i++) {
REG_NUM
Quoted 18 lines
drivers/iio/light/cm36651.c new file +18
-0+ ret =i2c_smbus_write_byte_data (cm36651->ps_client, + cm36651_ps_reg[i], cm36651->ps_ctrl_regs[i]); +if (ret <0 ) + goto write_err; + } + +enable_irq (client->irq); +break ; +case CM36651_CMD_PROX_EV_DIS : +if (!test_bit (CM36651_PROXIMITY_EV_EN , &cm36651->flags)) { +dev_err (&client->dev, +"Already proximity event disable state\n" ); +return ret; + } +clear_bit (CM36651_PROXIMITY_EV_EN , &cm36651->flags); +disable_irq (client->irq); + ret =i2c_smbus_write_byte_data (cm36651->ps_client, +CM36651_PS_CONF1 ,CM36651_PS_DISABLE );
drivers/iio/light/cm36651.c line 325
Quoted 10 lines
drivers/iio/light/cm36651.c new file +10
-0+break ; + } + +if (ret <0 ) +dev_err (&client->dev,"Write register failed\n" ); + +return ret; + +write_err: +dev_err (&client->dev,"Proximity enable event is failed\n" );
remove 'is'
Quoted 4 lines
drivers/iio/light/cm36651.c new file +4
-0+return ret; +} + +static int cm36651_read_channel(struct cm36651_data *cm36651,
drivers/iio/light/cm36651.c line 339
Quoted 10 lines
drivers/iio/light/cm36651.c new file +10
-0+ struct iio_chan_spec const *chan, int *val) +{ + struct i2c_client *client = cm36651->client; +enum cm36651_command cmd =0 ; +int ret; + +if (chan->scan_index ==CM36651_SCAN_MODE_LIGHT ) + cmd =CM36651_CMD_READ_RAW_LIGHT ; +else /* CM36651_SCAN_MODE_PROX */ + cmd =CM36651_CMD_READ_RAW_PROXIMITY ;
chan->type could be used to distinguish between LIGHT and PROXIMITY, so no need for scan_index
drivers/iio/light/cm36651.c line 347
Quoted 8 lines
drivers/iio/light/cm36651.c new file +8
-0+ + ret =cm36651_set_operation_mode (cm36651, cmd); +if (ret <0 ) { +dev_err (&client->dev,"CM36651 set operation mode failed\n" ); +return ret; + } +/* Raw data integration time */ +msleep (50 );
shouldn't this depend on the integration time configured? same for ALS and PS?
Quoted 47 lines
drivers/iio/light/cm36651.c new file +47
-0+ ret =cm36651_read_output (cm36651, chan, val); +if (ret <0 ) { +dev_err (&client->dev,"CM36651 read output failed\n" ); +return ret; + } + +return ret; +} + +static int cm36651_read_raw (struct iio_dev *indio_dev, +struct iio_chan_spec const *chan, +int *val,int *val2,long mask) +{ +struct cm36651_data *cm36651 =iio_priv (indio_dev); +int ret = -EINVAL ; + +mutex_lock (&cm36651->lock); + +if (mask ==IIO_CHAN_INFO_RAW ) + ret =cm36651_read_channel (cm36651, chan, val); + +mutex_unlock (&cm36651->lock); + +return ret; +} + +static int cm36651_read_thresh (struct iio_dev *indio_dev, +u64 event_code,int *val) +{ +struct cm36651_data *cm36651 =iio_priv (indio_dev); +int chan_type =IIO_EVENT_CODE_EXTRACT_CHAN_TYPE (event_code); +int event_type =IIO_EVENT_CODE_EXTRACT_TYPE (event_code); + +if (event_type !=IIO_EV_TYPE_THRESH || chan_type !=IIO_PROXIMITY ) +return -EINVAL ; + + *val = cm36651->ps_ctrl_regs[CM36651_PS_THD ]; + +return 0 ; +} + +static int cm36651_write_thresh (struct iio_dev *indio_dev, +u64 event_code,int val) +{ +struct cm36651_data *cm36651 =iio_priv (indio_dev); +struct i2c_client *client = cm36651->client; +int ret;
drivers/iio/light/cm36651.c line 404
Quoted 10 lines
drivers/iio/light/cm36651.c new file +10
-0+ +if (val <3 || val >255 ) +return -EINVAL ; + + cm36651->ps_ctrl_regs[CM36651_PS_THD ] = val; + ret =i2c_smbus_write_byte_data (cm36651->ps_client,CM36651_PS_THD , + cm36651->ps_ctrl_regs[CM36651_PS_THD ]); + +if (ret <0 ) { +dev_err (&client->dev,"PS register read faied: %d\n" , ret);
that's a write; 'failed', maybe more specific: "PS threshold write failed"
drivers/iio/light/cm36651.c line 417
Quoted 13 lines
drivers/iio/light/cm36651.c new file +13
-0+return ret; + } +dev_dbg (&client->dev,"New threshold is 0x%x\n" , + cm36651->ps_ctrl_regs[CM36651_PS_THD ]); + +return 0 ; +} + +static int cm36651_write_event_config (struct iio_dev *indio_dev, +u64 event_code,int state) +{ +struct cm36651_data *cm36651 =iio_priv (indio_dev); +enum cm36651_command cmd;
could avoid that local variable or move it down
Quoted 26 lines
drivers/iio/light/cm36651.c new file +26
-0+int chan_type =IIO_EVENT_CODE_EXTRACT_CHAN_TYPE (event_code); +int ret = -EINVAL ; + +mutex_lock (&cm36651->lock); + +if (chan_type ==IIO_PROXIMITY ) { + cmd = state ?CM36651_CMD_PROX_EV_EN :CM36651_CMD_PROX_EV_DIS ; + ret =cm36651_set_operation_mode (cm36651, cmd); + } + +mutex_unlock (&cm36651->lock); + +return ret; +} + +static int cm36651_read_event_config (struct iio_dev *indio_dev,u64 event_code) +{ +struct cm36651_data *cm36651 =iio_priv (indio_dev); +int chan_type =IIO_EVENT_CODE_EXTRACT_CHAN_TYPE (event_code); +int event_en = -EINVAL ; + +mutex_lock (&cm36651->lock); + +if (chan_type ==IIO_PROXIMITY ) + event_en =test_bit (CM36651_PROXIMITY_EV_EN , &cm36651->flags); +
drivers/iio/light/cm36651.c line 453
Quoted 10 lines
drivers/iio/light/cm36651.c new file +10
-0+mutex_unlock (&cm36651->lock); + +return event_en; +} + +#define CM36651_LIGHT_CHANNEL (_color, _idx) { \ + .type = IIO_LIGHT, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .scan_type = IIO_ST('u', 16, 16, 0), \ + .scan_index = CM36651_SCAN_MODE_LIGHT, \
no need scan_type and scan_index; driver does not support buffering
drivers/iio/light/cm36651.c line 454
Quoted 1 line
drivers/iio/light/cm36651.c new file +1
-0+ .channel = _idx,
I'd rather use .address than .channel; there is just one light channel
\
Quoted 23 lines
drivers/iio/light/cm36651.c new file +23
-0+ .modified = 1, \ + .channel2 = IIO_MOD_LIGHT_##_color, \ +} + +static const struct iio_chan_spec cm36651_channels[] = { + { + .type =IIO_PROXIMITY , + .info_mask_separate =BIT (IIO_CHAN_INFO_RAW ), + .scan_type =IIO_ST ('u' ,8 ,8 ,0 ), + .scan_index =CM36651_SCAN_MODE_PROX , + .event_mask =IIO_EV_BIT (IIO_EV_TYPE_THRESH ,IIO_EV_DIR_EITHER ) + }, +CM36651_LIGHT_CHANNEL (RED ,CM36651_LIGHT_CHANNEL_IDX_RED ), +CM36651_LIGHT_CHANNEL (GREEN ,CM36651_LIGHT_CHANNEL_IDX_GREEN ), +CM36651_LIGHT_CHANNEL (BLUE ,CM36651_LIGHT_CHANNEL_IDX_BLUE ), +CM36651_LIGHT_CHANNEL (CLEAR ,CM36651_LIGHT_CHANNEL_IDX_CLEAR ), +}; + +static const struct iio_info cm36651_info = { + .driver_module =THIS_MODULE , + .read_raw = &cm36651_read_raw, + .read_event_value = &cm36651_read_thresh, + .write_event_value = &cm36651_write_thresh,
drivers/iio/light/cm36651.c line 487
Quoted 10 lines
drivers/iio/light/cm36651.c new file +10
-0+ .read_event_config = &cm36651_read_event_config, + .write_event_config = &cm36651_write_event_config, +}; + +static int cm36651_probe (struct i2c_client *client, +const struct i2c_device_id *id) +{ +struct cm36651_data *cm36651; +struct iio_dev *indio_dev; +unsigned long irqflag;
could avoid variable irqflag
drivers/iio/light/cm36651.c line 490
Quoted 3 lines
drivers/iio/light/cm36651.c new file +3
-0+int ret; + +dev_dbg (&client->dev,"cm36651 light/proxymity sensor probe\n" );
proximity
Quoted 6 lines
drivers/iio/light/cm36651.c new file +6
-0+ + indio_dev =devm_iio_device_alloc (&client->dev,sizeof (*cm36651)); +if (!indio_dev) +return -ENOMEM ; + + cm36651 =iio_priv (indio_dev);
drivers/iio/light/cm36651.c line 506
Quoted 10 lines
drivers/iio/light/cm36651.c new file +10
-0+ + cm36651->vled_reg =devm_regulator_get (&client->dev,"vled" ); +if (IS_ERR (cm36651->vled_reg)) { +dev_err (&client->dev,"get regulator vled failed\n" ); +return PTR_ERR (cm36651->vled_reg); + } + + ret =regulator_enable (cm36651->vled_reg); +if (ret) { +dev_err (&client->dev,"enable regulator failed\n" );
regulator vled
Quoted 11 lines
drivers/iio/light/cm36651.c new file +11
-0+return ret; + } + +i2c_set_clientdata (client, indio_dev); + + cm36651->client = client; + cm36651->ps_client =i2c_new_dummy (client->adapter, +CM36651_I2C_ADDR_PS ); +mutex_init (&cm36651->lock); + indio_dev->dev.parent = &client->dev; + indio_dev->channels = cm36651_channels;
drivers/iio/light/cm36651.c line 527
Quoted 10 lines
drivers/iio/light/cm36651.c new file +10
-0+ indio_dev->num_channels =ARRAY_SIZE (cm36651_channels); + indio_dev->info = &cm36651_info; + indio_dev->name = id->name; + indio_dev->modes =INDIO_DIRECT_MODE ; + +/* Check if the device is there or not */ + ret =i2c_smbus_write_byte_data (cm36651->ps_client,CM36651_PS_CONF1 , +CM36651_PS_DISABLE ); +if (ret <0 ) { +dev_err (&client->dev,"PS register read faied: %d\n" , ret);
"failed"
isn't there a better way to check? setup_reg() would fail also if the device is not there?!
regulator doesn't get disabled
drivers/iio/light/cm36651.c line 531
Quoted 4 lines
drivers/iio/light/cm36651.c new file +4
-0+return ret; + } + + ret =cm36651_setup_reg (cm36651);
ret not checked
drivers/iio/light/cm36651.c line 537
Quoted 6 lines
drivers/iio/light/cm36651.c new file +6
-0+ + irqflag =IRQF_TRIGGER_RISING |IRQF_TRIGGER_FALLING |IRQF_ONESHOT ; + ret =request_threaded_irq (client->irq,NULL , cm36651_irq_handler, + irqflag,"cm36651_proximity" , indio_dev); +if (ret) { +dev_err (&client->dev,"%s: request irq failed\n" , __func__);
regulator not disabled
drivers/iio/light/cm36651.c line 546
Quoted 9 lines
drivers/iio/light/cm36651.c new file +9
-0+return ret; + } +disable_irq (client->irq); + + ret =iio_device_register (indio_dev); +if (ret) { +regulator_disable (cm36651->vled_reg); +free_irq (client->irq, indio_dev); +return ret;
move 'return ret' down, save 'return 0'
Quoted 46 lines
drivers/iio/light/cm36651.c new file +45
-0+ } + +return 0 ; +} + +static int cm36651_remove (struct i2c_client *client) +{ +struct iio_dev *indio_dev =i2c_get_clientdata (client); +struct cm36651_data *cm36651 =iio_priv (indio_dev); + +iio_device_unregister (indio_dev); +regulator_disable (cm36651->vled_reg); +free_irq (client->irq, indio_dev); + +return 0 ; +} + +static const struct i2c_device_id cm36651_id[] = { + {"cm36651" ,0 }, + { } +}; + +MODULE_DEVICE_TABLE (i2c, cm36651_id); + +static const struct of_device_id cm36651_of_match[] = { + { .compatible ="capella,cm36651" }, + { } +}; + +static struct i2c_driver cm36651_driver = { + .driver = { + .name ="cm36651" , + .of_match_table =of_match_ptr (cm36651_of_match), + .owner =THIS_MODULE , + }, + .probe = cm36651_probe, + .remove = cm36651_remove, + .id_table = cm36651_id, +}; + +module_i2c_driver (cm36651_driver); + +MODULE_AUTHOR ("Beomho Seo <[email protected]>" ); +MODULE_DESCRIPTION ("CM36651 proximity/ambient light sensor driver" ); +MODULE_LICENSE ("GPL v2" );
Signature · 2 lines
Peter Meerwald +43-664-2444418 (mobile)
Hello Peter,
Thank you for your review.
I revised on your advice. v3 patch will be sent in the today.
Reply about your question below
I
Quoted 53 lines
Quoted 2 lines
This patch add a new driver for Capella CM36651 proximity and RGB sensor.
moving in the right direction!
couple of comments inlineQuoted 47 lines
drivers/iio/light/Kconfig +11
-0@@ -27,6 +27,17 @@ config APDS9300 To compile this driver as a module, choose M here: the module will be called apds9300. +config CM36651 + depends on I2C + tristate "CM36651 driver" + help + Say Y here if you use cm36651. + This option enables proximity & RGB sensor using + Capella cm36651 device driver. + + To compile this driver as a module, choose M here: + the module will be called cm36651. + config GP2AP020A00F tristate "Sharp GP2AP020A00F Proximity/ALS sensor" depends on I2Cdrivers/iio/light/cm36651.c new file +19
-0@@ -0,0 +1,591 @@ +/* +* Copyright (C) 2013 Samsung Electronics Co., Ltd. +* Author: Beomho Seo <[email protected]> +* +* 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. +*/ + +#include <linux/delay.h> +#include <linux/i2c.h> +#include <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> +
Quoted 9 lines
drivers/iio/light/cm36651.c line 24
Quoted 5 lines
drivers/iio/light/cm36651.c new file +5
-0+/* Slave address 0x19 for PS of 7 bit addressing protocol for I2C */ +#define CM36651_I2C_ADDR_PS 0x19 + +/* Ambient light sensor */ +#define CM36651_CS_CONF1 0x00what does CS stand for?
maybe ALS?
In order to cm36651 manual, CS stands for COlor sensor(R, G, B and W). Manual uses abbreviation both CS and ALS. I follow register name and command in manual.
Quoted 195 lines
Quoted 15 lines
drivers/iio/light/cm36651.c new file +15
-0+#define CM36651_CS_CONF2 0x01 +#define CM36651_ALS_WH_M 0x02 +#define CM36651_ALS_WH_L 0x03 +#define CM36651_ALS_WL_M 0x04 +#define CM36651_ALS_WL_L 0x05 +#define CM36651_CS_CONF3 0x06 +#define CM36651_CS_REG_NUM 0x07 + +/* Proximity sensor */ +#define CM36651_PS_CONF1 0x00 +#define CM36651_PS_THD 0x01 +#define CM36651_PS_CANC 0x02 +#define CM36651_PS_CONF2 0x03 +#define CM36651_PS_REG_NUM 0x04 +drivers/iio/light/cm36651.c line 49
Quoted 10 lines
drivers/iio/light/cm36651.c new file +10
-0+/* CS_CONF1 command code */ +#define CM36651_ALS_ENABLE 0x00 +#define CM36651_ALS_DISABLE 0x01 +#define CM36651_ALS_INT_EN 0x02 +#define CM36651_ALS_THRES 0x04 + +/* CS_CONF2 command code */ +#define CM36651_CS_CONF2_DEFAULT_BIT 0x08 + +/* CS_CONF3 channel integration time */ which unit?
this could be made configurable viaINT_TIMEchannelQuoted 56 lines
drivers/iio/light/cm36651.c new file +56
-0+#define CM36651_IT_80 0x00 +#define CM36651_W_IT_160 0x01 +#define CM36651_W_IT_320 0x02 +#define CM36651_W_IT_640 0x03 +#define CM36651_B_IT_160 0x04 +#define CM36651_B_IT_320 0x08 +#define CM36651_B_IT_640 0x0C +#define CM36651_G_IT_160 0x10 +#define CM36651_G_IT_320 0x20 +#define CM36651_G_IT_640 0x30 +#define CM36651_R_IT_160 0x40 +#define CM36651_R_IT_320 0x80 +#define CM36651_R_IT_640 0xC0 + +/* PS_CONF1 command code */ +#define CM36651_PS_ENABLE 0x00 +#define CM36651_PS_DISABLE 0x01 +#define CM36651_PS_INT_EN 0x02 +#define CM36651_PS_PERS_2 0x04 +#define CM36651_PS_PERS_3 0x08 +#define CM36651_PS_PERS_4 0x0C +#define CM36651_PS_IT_2 0x10 +#define CM36651_PS_IT_3 0x20 +#define CM36651_PS_IT_4 0x30 + +/* PS_THD command code */ +#define CM36651_PS_INITIAL_THD 0x09 + +/* PS_CANC command code */ +#define CM36651_PS_CANC_DEFAULT 0x00 + +/* PS_CONF2 command code */ +#define CM36651_PS_HYS_1 0x00 +#define CM36651_PS_HYS_2 0x01 +#define CM36651_PS_SMART_PERS_EN 0x02 +#define CM36651_PS_MS 0x10 + +#define CM36651_SCAN_MODE_LIGHT 0 +#define CM36651_SCAN_MODE_PROX 1 + +enum cm36651_operation_mode { +CM36651_LIGHT_EN , +CM36651_PROXIMITY_EN , +CM36651_PROXIMITY_EV_EN , +}; + +enum cm36651_light_channel_idx { +CM36651_LIGHT_CHANNEL_IDX_RED , +CM36651_LIGHT_CHANNEL_IDX_GREEN , +CM36651_LIGHT_CHANNEL_IDX_BLUE , +CM36651_LIGHT_CHANNEL_IDX_CLEAR , +}; + +enum cm36651_command { +CM36651_CMD_READ_RAW_LIGHT , +CM36651_CMD_READ_RAW_PROXIMITY ,drivers/iio/light/cm36651.c line 115
Quoted 10 lines
drivers/iio/light/cm36651.c new file +10
-0+CM36651_CMD_PROX_EV_EN , + CM36651_CMD_PROX_EV_DIS, +}; + +enum cm36651_proximity_event { +CM36651_CLOSE_PROXIMITY , +CM36651_FAR_PROXIMITY , +}; + +static u8 cm36651_als_reg[2] = {const
drivers/iio/light/cm36651.c line 120
Quoted 5 lines
drivers/iio/light/cm36651.c new file +5
-0+CM36651_CS_CONF1 , + CM36651_CS_CONF2, +}; + +static u8 cm36651_ps_reg[4] = {const
Quoted 20 lines
drivers/iio/light/cm36651.c new file +20
-0+CM36651_PS_CONF1 , +CM36651_PS_THD , +CM36651_PS_CANC , + CM36651_PS_CONF2, +}; + +struct cm36651_data { +const struct cm36651_platform_data *pdata; +struct i2c_client *client; +struct i2c_client *ps_client; +struct mutex lock; +struct regulator *vled_reg; +unsigned long flags; +u8 cs_ctrl_regs[2 ]; +u8 ps_ctrl_regs[4 ]; +u16 color[4 ]; +}; + +static int cm36651_setup_reg (struct cm36651_data *cm36651) +{drivers/iio/light/cm36651.c line 150
Quoted 10 lines
drivers/iio/light/cm36651.c new file +10
-0+struct i2c_client *client = cm36651->client; +struct i2c_client *ps_client = cm36651->ps_client; +int i, ret; + +/* ALS initialization */ + cm36651->cs_ctrl_regs[CM36651_CS_CONF1 ] =CM36651_ALS_ENABLE + |CM36651_ALS_THRES ; + cm36651->cs_ctrl_regs[CM36651_CS_CONF2 ] =CM36651_CS_CONF2_DEFAULT_BIT ; + +for (i =0 ; i <2 ; i++) {use the
CS_REG_NUM#definedQuoted 5 lines
drivers/iio/light/cm36651.c new file +5
-0+ ret =i2c_smbus_write_byte_data (client, cm36651_als_reg[i], + cm36651->cs_ctrl_regs[i]); +if (ret <0 ) + goto err_setup_reg; + }drivers/iio/light/cm36651.c line 165
Quoted 10 lines
drivers/iio/light/cm36651.c new file +10
-0+ +/* PS initialization */ + cm36651->ps_ctrl_regs[CM36651_PS_CONF1 ] =CM36651_PS_ENABLE + |CM36651_PS_PERS_4 |CM36651_PS_IT_4 ; + cm36651->ps_ctrl_regs[CM36651_PS_THD ] =CM36651_PS_INITIAL_THD ; + cm36651->ps_ctrl_regs[CM36651_PS_CANC ] =CM36651_PS_CANC_DEFAULT ; + cm36651->ps_ctrl_regs[CM36651_PS_CONF2 ] =CM36651_PS_HYS_2 + |CM36651_PS_SMART_PERS_EN |CM36651_PS_MS ; + +for (i =0 ; i <4 ; i++) {use the
PS_REG_NUM#defineddrivers/iio/light/cm36651.c line 177
Quoted 12 lines
drivers/iio/light/cm36651.c new file +12
-0+ ret =i2c_smbus_write_byte_data (ps_client, cm36651_ps_reg[i], + cm36651->ps_ctrl_regs[i]); +if (ret <0 ) + goto err_setup_reg; + } + + ret =i2c_smbus_read_byte (cm36651->ps_client); +if (ret <0 ) + goto err_setup_reg; + +/* Printing the initial proximity value with no contact */ +dev_dbg (&client->dev,"Initial proximity value: %d\n" , ret);maybe drop the initial read? not needed when not in debug mode
Quoted 23 lines
drivers/iio/light/cm36651.c new file +23
-0+ +/* Device turn off */ + ret =i2c_smbus_write_byte_data (client,CM36651_CS_CONF1 , +CM36651_ALS_DISABLE ); +if (ret <0 ) + goto err_setup_reg; + + ret =i2c_smbus_write_byte_data (cm36651->ps_client, +CM36651_PS_CONF1 ,CM36651_PS_DISABLE ); +if (ret <0 ) + goto err_setup_reg; + +return 0 ; + +err_setup_reg: +dev_err (&client->dev,"Register setup failed: %d\n" , ret); +return ret; +} + +static int cm36651_read_output (struct cm36651_data *cm36651, +struct iio_chan_spec const *chan,int *val) +{ +struct i2c_client *client = cm36651->client;
Quoted 9 lines
drivers/iio/light/cm36651.c line 205
Quoted 5 lines
drivers/iio/light/cm36651.c new file +5
-0+int ret = -EINVAL ; + +switch (chan->scan_index) { +case CM36651_SCAN_MODE_LIGHT : + *val =i2c_smbus_read_word_data (client, chan->channel);the CM36651 seems to be special here how data is returned; there seems to be no dedicated register to pass the measurement?
CM36651 aply slave address 0x18 for CS and 0x19 for PS of 7bit addressing protocol for I2C. CM36651 contains an 8 bit command register. All operation can be controlled by the command register(e.g. read sensor data)
Quoted 443 lines
Quoted 62 lines
drivers/iio/light/cm36651.c new file +62
-0+if (val <0 ) + goto read_err; + + ret =i2c_smbus_write_byte_data (client,CM36651_CS_CONF1 , +CM36651_ALS_DISABLE ); +if (ret <0 ) + goto write_err; + + ret =IIO_VAL_INT ; +break ; +case CM36651_SCAN_MODE_PROX : + *val =i2c_smbus_read_byte (cm36651->ps_client); +if (val <0 ) + goto read_err; + + ret =i2c_smbus_write_byte_data (cm36651->ps_client, +CM36651_PS_CONF1 ,CM36651_PS_DISABLE ); +if (ret <0 ) + goto write_err; + + ret =IIO_VAL_INT ; +break ; + } + +return ret; + +read_err: +dev_err (&client->dev,"Register read failed\n" ); +return ret; + +write_err: +dev_err (&client->dev,"Register write failed\n" ); +return ret; +} + +static irqreturn_t cm36651_irq_handler (int irq,void *data) +{ +struct iio_dev *indio_dev = data; +struct cm36651_data *cm36651 =iio_priv (indio_dev); +struct i2c_client *client = cm36651->client; +int ev_dir, val, ret; +u64 ev_code; + + ret =i2c_smbus_read_byte (cm36651->ps_client); +if (ret <0 ) { +dev_err (&client->dev, +"%s: Data read failed: %d\n" , __func__, ret); +return IRQ_HANDLED ; + } + +if (ret <CM36651_PS_INITIAL_THD ) { + ev_dir =IIO_EV_DIR_RISING ; + val =CM36651_FAR_PROXIMITY ; + }else { + ev_dir =IIO_EV_DIR_FALLING ; + val =CM36651_CLOSE_PROXIMITY ; + } + + ev_code =IIO_UNMOD_EVENT_CODE (IIO_PROXIMITY , +CM36651_CMD_READ_RAW_PROXIMITY , +IIO_EV_TYPE_THRESH , ev_dir); +drivers/iio/light/cm36651.c line 277
Quoted 10 lines
drivers/iio/light/cm36651.c new file +10
-0+iio_push_event (indio_dev, ev_code,iio_get_time_ns ()); + +return IRQ_HANDLED ; +} + +static int cm36651_set_operation_mode (struct cm36651_data *cm36651, +enum cm36651_command cmd) +{ +struct i2c_client *client = cm36651->client; +int ret =0 ;unknown cmd are silently not handled;
probably better to set ret =-EINVALQuoted 10 lines
drivers/iio/light/cm36651.c new file +10
-0+int i; + +switch (cmd) { +case CM36651_CMD_READ_RAW_LIGHT : + ret =i2c_smbus_write_byte_data (client,CM36651_CS_CONF1 , + cm36651->cs_ctrl_regs[CM36651_CS_CONF1 ]); +break ; +case CM36651_CMD_READ_RAW_PROXIMITY : + ret =i2c_smbus_write_byte_data (cm36651->ps_client, +CM36651_PS_CONF1 , cm36651->ps_ctrl_regs[CM36651_PS_CONF1 ]);drivers/iio/light/cm36651.c line 297
Quoted 10 lines
drivers/iio/light/cm36651.c new file +10
-0+break ; +case CM36651_CMD_PROX_EV_EN : +if (test_bit (CM36651_PROXIMITY_EV_EN , &cm36651->flags)) { +dev_err (&client->dev, +"Already proximity event enable state\n" ); +return ret; + } +set_bit (CM36651_PROXIMITY_EV_EN , &cm36651->flags); + +for (i =0 ; i <4 ; i++) {
REG_NUMQuoted 18 lines
drivers/iio/light/cm36651.c new file +18
-0+ ret =i2c_smbus_write_byte_data (cm36651->ps_client, + cm36651_ps_reg[i], cm36651->ps_ctrl_regs[i]); +if (ret <0 ) + goto write_err; + } + +enable_irq (client->irq); +break ; +case CM36651_CMD_PROX_EV_DIS : +if (!test_bit (CM36651_PROXIMITY_EV_EN , &cm36651->flags)) { +dev_err (&client->dev, +"Already proximity event disable state\n" ); +return ret; + } +clear_bit (CM36651_PROXIMITY_EV_EN , &cm36651->flags); +disable_irq (client->irq); + ret =i2c_smbus_write_byte_data (cm36651->ps_client, +CM36651_PS_CONF1 ,CM36651_PS_DISABLE );drivers/iio/light/cm36651.c line 325
Quoted 10 lines
drivers/iio/light/cm36651.c new file +10
-0+break ; + } + +if (ret <0 ) +dev_err (&client->dev,"Write register failed\n" ); + +return ret; + +write_err: +dev_err (&client->dev,"Proximity enable event is failed\n" );remove 'is'
Quoted 4 lines
drivers/iio/light/cm36651.c new file +4
-0+return ret; +} + +static int cm36651_read_channel(struct cm36651_data *cm36651,drivers/iio/light/cm36651.c line 339
Quoted 10 lines
drivers/iio/light/cm36651.c new file +10
-0+ struct iio_chan_spec const *chan, int *val) +{ + struct i2c_client *client = cm36651->client; +enum cm36651_command cmd =0 ; +int ret; + +if (chan->scan_index ==CM36651_SCAN_MODE_LIGHT ) + cmd =CM36651_CMD_READ_RAW_LIGHT ; +else /* CM36651_SCAN_MODE_PROX */ + cmd =CM36651_CMD_READ_RAW_PROXIMITY ;
chan->typecould be used to distinguish between LIGHT and PROXIMITY, so no need forscan_indexdrivers/iio/light/cm36651.c line 347
Quoted 8 lines
drivers/iio/light/cm36651.c new file +8
-0+ + ret =cm36651_set_operation_mode (cm36651, cmd); +if (ret <0 ) { +dev_err (&client->dev,"CM36651 set operation mode failed\n" ); +return ret; + } +/* Raw data integration time */ +msleep (50 );shouldn't this depend on the integration time configured? same for ALS and PS?
Quoted 47 lines
drivers/iio/light/cm36651.c new file +47
-0+ ret =cm36651_read_output (cm36651, chan, val); +if (ret <0 ) { +dev_err (&client->dev,"CM36651 read output failed\n" ); +return ret; + } + +return ret; +} + +static int cm36651_read_raw (struct iio_dev *indio_dev, +struct iio_chan_spec const *chan, +int *val,int *val2,long mask) +{ +struct cm36651_data *cm36651 =iio_priv (indio_dev); +int ret = -EINVAL ; + +mutex_lock (&cm36651->lock); + +if (mask ==IIO_CHAN_INFO_RAW ) + ret =cm36651_read_channel (cm36651, chan, val); + +mutex_unlock (&cm36651->lock); + +return ret; +} + +static int cm36651_read_thresh (struct iio_dev *indio_dev, +u64 event_code,int *val) +{ +struct cm36651_data *cm36651 =iio_priv (indio_dev); +int chan_type =IIO_EVENT_CODE_EXTRACT_CHAN_TYPE (event_code); +int event_type =IIO_EVENT_CODE_EXTRACT_TYPE (event_code); + +if (event_type !=IIO_EV_TYPE_THRESH || chan_type !=IIO_PROXIMITY ) +return -EINVAL ; + + *val = cm36651->ps_ctrl_regs[CM36651_PS_THD ]; + +return 0 ; +} + +static int cm36651_write_thresh (struct iio_dev *indio_dev, +u64 event_code,int val) +{ +struct cm36651_data *cm36651 =iio_priv (indio_dev); +struct i2c_client *client = cm36651->client; +int ret;drivers/iio/light/cm36651.c line 404
Quoted 10 lines
drivers/iio/light/cm36651.c new file +10
-0+ +if (val <3 || val >255 ) +return -EINVAL ; + + cm36651->ps_ctrl_regs[CM36651_PS_THD ] = val; + ret =i2c_smbus_write_byte_data (cm36651->ps_client,CM36651_PS_THD , + cm36651->ps_ctrl_regs[CM36651_PS_THD ]); + +if (ret <0 ) { +dev_err (&client->dev,"PS register read faied: %d\n" , ret);that's a write; 'failed', maybe more specific: "PS threshold write failed"
drivers/iio/light/cm36651.c line 417
Quoted 13 lines
drivers/iio/light/cm36651.c new file +13
-0+return ret; + } +dev_dbg (&client->dev,"New threshold is 0x%x\n" , + cm36651->ps_ctrl_regs[CM36651_PS_THD ]); + +return 0 ; +} + +static int cm36651_write_event_config (struct iio_dev *indio_dev, +u64 event_code,int state) +{ +struct cm36651_data *cm36651 =iio_priv (indio_dev); +enum cm36651_command cmd;could avoid that local variable or move it down
Quoted 26 lines
drivers/iio/light/cm36651.c new file +26
-0+int chan_type =IIO_EVENT_CODE_EXTRACT_CHAN_TYPE (event_code); +int ret = -EINVAL ; + +mutex_lock (&cm36651->lock); + +if (chan_type ==IIO_PROXIMITY ) { + cmd = state ?CM36651_CMD_PROX_EV_EN :CM36651_CMD_PROX_EV_DIS ; + ret =cm36651_set_operation_mode (cm36651, cmd); + } + +mutex_unlock (&cm36651->lock); + +return ret; +} + +static int cm36651_read_event_config (struct iio_dev *indio_dev,u64 event_code) +{ +struct cm36651_data *cm36651 =iio_priv (indio_dev); +int chan_type =IIO_EVENT_CODE_EXTRACT_CHAN_TYPE (event_code); +int event_en = -EINVAL ; + +mutex_lock (&cm36651->lock); + +if (chan_type ==IIO_PROXIMITY ) + event_en =test_bit (CM36651_PROXIMITY_EV_EN , &cm36651->flags); +drivers/iio/light/cm36651.c line 453
Quoted 10 lines
drivers/iio/light/cm36651.c new file +10
-0+mutex_unlock (&cm36651->lock); + +return event_en; +} + +#define CM36651_LIGHT_CHANNEL (_color, _idx) { \ + .type = IIO_LIGHT, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .scan_type = IIO_ST('u', 16, 16, 0), \ + .scan_index = CM36651_SCAN_MODE_LIGHT, \no need
scan_typeandscan_index; driver does not support bufferingdrivers/iio/light/cm36651.c line 454
Quoted 1 line
drivers/iio/light/cm36651.c new file +1
-0+ .channel = _idx,I'd rather use .address than .channel; there is just one light channel
\Quoted 23 lines
drivers/iio/light/cm36651.c new file +23
-0+ .modified = 1, \ + .channel2 = IIO_MOD_LIGHT_##_color, \ +} + +static const struct iio_chan_spec cm36651_channels[] = { + { + .type =IIO_PROXIMITY , + .info_mask_separate =BIT (IIO_CHAN_INFO_RAW ), + .scan_type =IIO_ST ('u' ,8 ,8 ,0 ), + .scan_index =CM36651_SCAN_MODE_PROX , + .event_mask =IIO_EV_BIT (IIO_EV_TYPE_THRESH ,IIO_EV_DIR_EITHER ) + }, +CM36651_LIGHT_CHANNEL (RED ,CM36651_LIGHT_CHANNEL_IDX_RED ), +CM36651_LIGHT_CHANNEL (GREEN ,CM36651_LIGHT_CHANNEL_IDX_GREEN ), +CM36651_LIGHT_CHANNEL (BLUE ,CM36651_LIGHT_CHANNEL_IDX_BLUE ), +CM36651_LIGHT_CHANNEL (CLEAR ,CM36651_LIGHT_CHANNEL_IDX_CLEAR ), +}; + +static const struct iio_info cm36651_info = { + .driver_module =THIS_MODULE , + .read_raw = &cm36651_read_raw, + .read_event_value = &cm36651_read_thresh, + .write_event_value = &cm36651_write_thresh,drivers/iio/light/cm36651.c line 487
Quoted 10 lines
drivers/iio/light/cm36651.c new file +10
-0+ .read_event_config = &cm36651_read_event_config, + .write_event_config = &cm36651_write_event_config, +}; + +static int cm36651_probe (struct i2c_client *client, +const struct i2c_device_id *id) +{ +struct cm36651_data *cm36651; +struct iio_dev *indio_dev; +unsigned long irqflag;could avoid variable irqflag
drivers/iio/light/cm36651.c line 490
Quoted 3 lines
drivers/iio/light/cm36651.c new file +3
-0+int ret; + +dev_dbg (&client->dev,"cm36651 light/proxymity sensor probe\n" );proximity
Quoted 6 lines
drivers/iio/light/cm36651.c new file +6
-0+ + indio_dev =devm_iio_device_alloc (&client->dev,sizeof (*cm36651)); +if (!indio_dev) +return -ENOMEM ; + + cm36651 =iio_priv (indio_dev);drivers/iio/light/cm36651.c line 506
Quoted 10 lines
drivers/iio/light/cm36651.c new file +10
-0+ + cm36651->vled_reg =devm_regulator_get (&client->dev,"vled" ); +if (IS_ERR (cm36651->vled_reg)) { +dev_err (&client->dev,"get regulator vled failed\n" ); +return PTR_ERR (cm36651->vled_reg); + } + + ret =regulator_enable (cm36651->vled_reg); +if (ret) { +dev_err (&client->dev,"enable regulator failed\n" );regulator vled
Quoted 11 lines
drivers/iio/light/cm36651.c new file +11
-0+return ret; + } + +i2c_set_clientdata (client, indio_dev); + + cm36651->client = client; + cm36651->ps_client =i2c_new_dummy (client->adapter, +CM36651_I2C_ADDR_PS ); +mutex_init (&cm36651->lock); + indio_dev->dev.parent = &client->dev; + indio_dev->channels = cm36651_channels;drivers/iio/light/cm36651.c line 527
Quoted 10 lines
drivers/iio/light/cm36651.c new file +10
-0+ indio_dev->num_channels =ARRAY_SIZE (cm36651_channels); + indio_dev->info = &cm36651_info; + indio_dev->name = id->name; + indio_dev->modes =INDIO_DIRECT_MODE ; + +/* Check if the device is there or not */ + ret =i2c_smbus_write_byte_data (cm36651->ps_client,CM36651_PS_CONF1 , +CM36651_PS_DISABLE ); +if (ret <0 ) { +dev_err (&client->dev,"PS register read faied: %d\n" , ret);"failed"
isn't there a better way to check?setup_reg()would fail also if the device is not there?!regulator doesn't get disabled
drivers/iio/light/cm36651.c line 531
Quoted 4 lines
drivers/iio/light/cm36651.c new file +4
-0+return ret; + } + + ret =cm36651_setup_reg (cm36651);ret not checked
drivers/iio/light/cm36651.c line 537
Quoted 6 lines
drivers/iio/light/cm36651.c new file +6
-0+ + irqflag =IRQF_TRIGGER_RISING |IRQF_TRIGGER_FALLING |IRQF_ONESHOT ; + ret =request_threaded_irq (client->irq,NULL , cm36651_irq_handler, + irqflag,"cm36651_proximity" , indio_dev); +if (ret) { +dev_err (&client->dev,"%s: request irq failed\n" , __func__);regulator not disabled
drivers/iio/light/cm36651.c line 546
Quoted 9 lines
drivers/iio/light/cm36651.c new file +9
-0+return ret; + } +disable_irq (client->irq); + + ret =iio_device_register (indio_dev); +if (ret) { +regulator_disable (cm36651->vled_reg); +free_irq (client->irq, indio_dev); +return ret;move 'return ret' down, save 'return 0'
Quoted 46 lines
drivers/iio/light/cm36651.c new file +45
-0+ } + +return 0 ; +} + +static int cm36651_remove (struct i2c_client *client) +{ +struct iio_dev *indio_dev =i2c_get_clientdata (client); +struct cm36651_data *cm36651 =iio_priv (indio_dev); + +iio_device_unregister (indio_dev); +regulator_disable (cm36651->vled_reg); +free_irq (client->irq, indio_dev); + +return 0 ; +} + +static const struct i2c_device_id cm36651_id[] = { + {"cm36651" ,0 }, + { } +}; + +MODULE_DEVICE_TABLE (i2c, cm36651_id); + +static const struct of_device_id cm36651_of_match[] = { + { .compatible ="capella,cm36651" }, + { } +}; + +static struct i2c_driver cm36651_driver = { + .driver = { + .name ="cm36651" , + .of_match_table =of_match_ptr (cm36651_of_match), + .owner =THIS_MODULE , + }, + .probe = cm36651_probe, + .remove = cm36651_remove, + .id_table = cm36651_id, +}; + +module_i2c_driver (cm36651_driver); + +MODULE_AUTHOR ("Beomho Seo <[email protected]>" ); +MODULE_DESCRIPTION ("CM36651 proximity/ambient light sensor driver" ); +MODULE_LICENSE ("GPL v2" );
Signature · 5 lines
Best Regards, Beomho Seo, Assistant Engineer System S/W Lab., S/W Platform Team, Software Center Samsung Electronics