From 4b308e8c396dc9af3e1a97f75d15c055e09a17b2 Mon Sep 17 00:00:00 2001 From: Hartmut Knaack Date: Tue, 28 Jul 2015 00:38:59 +0200 Subject: iio:adc:berlin2-adc: use GENMASK and BIT for masks Make use of GENMASK for consecutive bitmasks and BIT for single bitmasks. Signed-off-by: Hartmut Knaack Acked-by: Antoine Tenart Signed-off-by: Jonathan Cameron --- drivers/iio/adc/berlin2-adc.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/iio/adc/berlin2-adc.c b/drivers/iio/adc/berlin2-adc.c index 4946d9b..6e20c85 100644 --- a/drivers/iio/adc/berlin2-adc.c +++ b/drivers/iio/adc/berlin2-adc.c @@ -27,13 +27,13 @@ #define BERLIN2_SM_CTRL_SM_SOC_INT BIT(1) #define BERLIN2_SM_CTRL_SOC_SM_INT BIT(2) #define BERLIN2_SM_CTRL_ADC_SEL(x) ((x) << 5) /* 0-15 */ -#define BERLIN2_SM_CTRL_ADC_SEL_MASK (0xf << 5) +#define BERLIN2_SM_CTRL_ADC_SEL_MASK GENMASK(8, 5) #define BERLIN2_SM_CTRL_ADC_POWER BIT(9) #define BERLIN2_SM_CTRL_ADC_CLKSEL_DIV2 (0x0 << 10) #define BERLIN2_SM_CTRL_ADC_CLKSEL_DIV3 (0x1 << 10) #define BERLIN2_SM_CTRL_ADC_CLKSEL_DIV4 (0x2 << 10) #define BERLIN2_SM_CTRL_ADC_CLKSEL_DIV8 (0x3 << 10) -#define BERLIN2_SM_CTRL_ADC_CLKSEL_MASK (0x3 << 10) +#define BERLIN2_SM_CTRL_ADC_CLKSEL_MASK GENMASK(11, 10) #define BERLIN2_SM_CTRL_ADC_START BIT(12) #define BERLIN2_SM_CTRL_ADC_RESET BIT(13) #define BERLIN2_SM_CTRL_ADC_BANDGAP_RDY BIT(14) @@ -50,7 +50,7 @@ #define BERLIN2_SM_CTRL_TSEN_MODE_10_50 (0x1 << 22) /* 10-50 C */ #define BERLIN2_SM_CTRL_TSEN_RESET BIT(29) #define BERLIN2_SM_ADC_DATA 0x20 -#define BERLIN2_SM_ADC_MASK 0x3ff +#define BERLIN2_SM_ADC_MASK GENMASK(9, 0) #define BERLIN2_SM_ADC_STATUS 0x1c #define BERLIN2_SM_ADC_STATUS_DATA_RDY(x) BIT(x) /* 0-15 */ #define BERLIN2_SM_ADC_STATUS_DATA_RDY_MASK GENMASK(15, 0) @@ -65,9 +65,9 @@ #define BERLIN2_SM_TSEN_CTRL_START BIT(8) #define BERLIN2_SM_TSEN_CTRL_SETTLING_4 (0x0 << 21) /* 4 us */ #define BERLIN2_SM_TSEN_CTRL_SETTLING_12 (0x1 << 21) /* 12 us */ -#define BERLIN2_SM_TSEN_CTRL_SETTLING_MASK (0x1 << 21) +#define BERLIN2_SM_TSEN_CTRL_SETTLING_MASK BIT(21) #define BERLIN2_SM_TSEN_CTRL_TRIM(x) ((x) << 22) -#define BERLIN2_SM_TSEN_CTRL_TRIM_MASK (0xf << 22) +#define BERLIN2_SM_TSEN_CTRL_TRIM_MASK GENMASK(25, 22) struct berlin2_adc_priv { struct regmap *regmap; -- cgit v1.1 From 3ac065224fa07ec7e60213d67464388ac8a52222 Mon Sep 17 00:00:00 2001 From: Hartmut Knaack Date: Tue, 28 Jul 2015 00:39:03 +0200 Subject: iio:adc:berlin2-adc: enable interrupts with mutex locked Move the call to enable channel interrupts into its _read() function to have it protected by a mutex. This ensures that only one channel is sampled at a time. Signed-off-by: Hartmut Knaack Signed-off-by: Jonathan Cameron --- drivers/iio/adc/berlin2-adc.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/drivers/iio/adc/berlin2-adc.c b/drivers/iio/adc/berlin2-adc.c index 6e20c85..13cfeb4 100644 --- a/drivers/iio/adc/berlin2-adc.c +++ b/drivers/iio/adc/berlin2-adc.c @@ -111,6 +111,10 @@ static int berlin2_adc_read(struct iio_dev *indio_dev, int channel) mutex_lock(&priv->lock); + /* Enable the interrupts */ + regmap_write(priv->regmap, BERLIN2_SM_ADC_STATUS, + BERLIN2_SM_ADC_STATUS_INT_EN(channel)); + /* Configure the ADC */ regmap_update_bits(priv->regmap, BERLIN2_SM_CTRL, BERLIN2_SM_CTRL_ADC_RESET | BERLIN2_SM_CTRL_ADC_SEL_MASK @@ -149,6 +153,10 @@ static int berlin2_adc_tsen_read(struct iio_dev *indio_dev) mutex_lock(&priv->lock); + /* Enable interrupts */ + regmap_write(priv->regmap, BERLIN2_SM_TSEN_STATUS, + BERLIN2_SM_TSEN_STATUS_INT_EN); + /* Configure the ADC */ regmap_update_bits(priv->regmap, BERLIN2_SM_CTRL, BERLIN2_SM_CTRL_TSEN_RESET | BERLIN2_SM_CTRL_ADC_ROTATE, @@ -190,7 +198,6 @@ static int berlin2_adc_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) { - struct berlin2_adc_priv *priv = iio_priv(indio_dev); int temp; switch (mask) { @@ -198,10 +205,6 @@ static int berlin2_adc_read_raw(struct iio_dev *indio_dev, if (chan->type != IIO_VOLTAGE) return -EINVAL; - /* Enable the interrupts */ - regmap_write(priv->regmap, BERLIN2_SM_ADC_STATUS, - BERLIN2_SM_ADC_STATUS_INT_EN(chan->channel)); - *val = berlin2_adc_read(indio_dev, chan->channel); if (*val < 0) return *val; @@ -211,10 +214,6 @@ static int berlin2_adc_read_raw(struct iio_dev *indio_dev, if (chan->type != IIO_TEMP) return -EINVAL; - /* Enable interrupts */ - regmap_write(priv->regmap, BERLIN2_SM_TSEN_STATUS, - BERLIN2_SM_TSEN_STATUS_INT_EN); - temp = berlin2_adc_tsen_read(indio_dev); if (temp < 0) return temp; -- cgit v1.1 From b465fc5499083d0b7086ae590cb852c0052622a0 Mon Sep 17 00:00:00 2001 From: Hartmut Knaack Date: Tue, 28 Jul 2015 00:39:04 +0200 Subject: iio:adc:berlin2-adc: coding style cleanup Some adjustment of indentation to make checkpatch.pl happy in strict mode. Signed-off-by: Hartmut Knaack Acked-by: Antoine Tenart Signed-off-by: Jonathan Cameron --- drivers/iio/adc/berlin2-adc.c | 64 +++++++++++++++++++++++-------------------- 1 file changed, 35 insertions(+), 29 deletions(-) diff --git a/drivers/iio/adc/berlin2-adc.c b/drivers/iio/adc/berlin2-adc.c index 13cfeb4..71c806e 100644 --- a/drivers/iio/adc/berlin2-adc.c +++ b/drivers/iio/adc/berlin2-adc.c @@ -78,13 +78,13 @@ struct berlin2_adc_priv { }; #define BERLIN2_ADC_CHANNEL(n, t) \ - { \ - .channel = n, \ - .datasheet_name = "channel"#n, \ - .type = t, \ - .indexed = 1, \ - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ - } + { \ + .channel = n, \ + .datasheet_name = "channel"#n, \ + .type = t, \ + .indexed = 1, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + } static const struct iio_chan_spec berlin2_adc_channels[] = { BERLIN2_ADC_CHANNEL(0, IIO_VOLTAGE), /* external input */ @@ -117,16 +117,18 @@ static int berlin2_adc_read(struct iio_dev *indio_dev, int channel) /* Configure the ADC */ regmap_update_bits(priv->regmap, BERLIN2_SM_CTRL, - BERLIN2_SM_CTRL_ADC_RESET | BERLIN2_SM_CTRL_ADC_SEL_MASK - | BERLIN2_SM_CTRL_ADC_START, - BERLIN2_SM_CTRL_ADC_SEL(channel) | BERLIN2_SM_CTRL_ADC_START); + BERLIN2_SM_CTRL_ADC_RESET | + BERLIN2_SM_CTRL_ADC_SEL_MASK | + BERLIN2_SM_CTRL_ADC_START, + BERLIN2_SM_CTRL_ADC_SEL(channel) | + BERLIN2_SM_CTRL_ADC_START); ret = wait_event_interruptible_timeout(priv->wq, priv->data_available, - msecs_to_jiffies(1000)); + msecs_to_jiffies(1000)); /* Disable the interrupts */ regmap_update_bits(priv->regmap, BERLIN2_SM_ADC_STATUS, - BERLIN2_SM_ADC_STATUS_INT_EN(channel), 0); + BERLIN2_SM_ADC_STATUS_INT_EN(channel), 0); if (ret == 0) ret = -ETIMEDOUT; @@ -136,7 +138,7 @@ static int berlin2_adc_read(struct iio_dev *indio_dev, int channel) } regmap_update_bits(priv->regmap, BERLIN2_SM_CTRL, - BERLIN2_SM_CTRL_ADC_START, 0); + BERLIN2_SM_CTRL_ADC_START, 0); data = priv->data; priv->data_available = false; @@ -159,22 +161,25 @@ static int berlin2_adc_tsen_read(struct iio_dev *indio_dev) /* Configure the ADC */ regmap_update_bits(priv->regmap, BERLIN2_SM_CTRL, - BERLIN2_SM_CTRL_TSEN_RESET | BERLIN2_SM_CTRL_ADC_ROTATE, - BERLIN2_SM_CTRL_ADC_ROTATE); + BERLIN2_SM_CTRL_TSEN_RESET | + BERLIN2_SM_CTRL_ADC_ROTATE, + BERLIN2_SM_CTRL_ADC_ROTATE); /* Configure the temperature sensor */ regmap_update_bits(priv->regmap, BERLIN2_SM_TSEN_CTRL, - BERLIN2_SM_TSEN_CTRL_TRIM_MASK | BERLIN2_SM_TSEN_CTRL_SETTLING_MASK - | BERLIN2_SM_TSEN_CTRL_START, - BERLIN2_SM_TSEN_CTRL_TRIM(3) | BERLIN2_SM_TSEN_CTRL_SETTLING_12 - | BERLIN2_SM_TSEN_CTRL_START); + BERLIN2_SM_TSEN_CTRL_TRIM_MASK | + BERLIN2_SM_TSEN_CTRL_SETTLING_MASK | + BERLIN2_SM_TSEN_CTRL_START, + BERLIN2_SM_TSEN_CTRL_TRIM(3) | + BERLIN2_SM_TSEN_CTRL_SETTLING_12 | + BERLIN2_SM_TSEN_CTRL_START); ret = wait_event_interruptible_timeout(priv->wq, priv->data_available, - msecs_to_jiffies(1000)); + msecs_to_jiffies(1000)); /* Disable interrupts */ regmap_update_bits(priv->regmap, BERLIN2_SM_TSEN_STATUS, - BERLIN2_SM_TSEN_STATUS_INT_EN, 0); + BERLIN2_SM_TSEN_STATUS_INT_EN, 0); if (ret == 0) ret = -ETIMEDOUT; @@ -184,7 +189,7 @@ static int berlin2_adc_tsen_read(struct iio_dev *indio_dev) } regmap_update_bits(priv->regmap, BERLIN2_SM_TSEN_CTRL, - BERLIN2_SM_TSEN_CTRL_START, 0); + BERLIN2_SM_TSEN_CTRL_START, 0); data = priv->data; priv->data_available = false; @@ -195,8 +200,8 @@ static int berlin2_adc_tsen_read(struct iio_dev *indio_dev) } static int berlin2_adc_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, int *val, int *val2, - long mask) + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) { int temp; @@ -305,12 +310,12 @@ static int berlin2_adc_probe(struct platform_device *pdev) return tsen_irq; ret = devm_request_irq(&pdev->dev, irq, berlin2_adc_irq, 0, - pdev->dev.driver->name, indio_dev); + pdev->dev.driver->name, indio_dev); if (ret) return ret; ret = devm_request_irq(&pdev->dev, tsen_irq, berlin2_adc_tsen_irq, - 0, pdev->dev.driver->name, indio_dev); + 0, pdev->dev.driver->name, indio_dev); if (ret) return ret; @@ -327,13 +332,14 @@ static int berlin2_adc_probe(struct platform_device *pdev) /* Power up the ADC */ regmap_update_bits(priv->regmap, BERLIN2_SM_CTRL, - BERLIN2_SM_CTRL_ADC_POWER, BERLIN2_SM_CTRL_ADC_POWER); + BERLIN2_SM_CTRL_ADC_POWER, + BERLIN2_SM_CTRL_ADC_POWER); ret = iio_device_register(indio_dev); if (ret) { /* Power down the ADC */ regmap_update_bits(priv->regmap, BERLIN2_SM_CTRL, - BERLIN2_SM_CTRL_ADC_POWER, 0); + BERLIN2_SM_CTRL_ADC_POWER, 0); return ret; } @@ -349,7 +355,7 @@ static int berlin2_adc_remove(struct platform_device *pdev) /* Power down the ADC */ regmap_update_bits(priv->regmap, BERLIN2_SM_CTRL, - BERLIN2_SM_CTRL_ADC_POWER, 0); + BERLIN2_SM_CTRL_ADC_POWER, 0); return 0; } -- cgit v1.1 From 7c7a9eeaa335df03d692ad65e0767020ad1be374 Mon Sep 17 00:00:00 2001 From: Hartmut Knaack Date: Thu, 9 Jul 2015 23:51:31 +0200 Subject: iio:light:stk3310: add more error handling Check for the following error cases: * lower boundary for val in _write_event * return value of regmap_(field_)read * possible values for chan->type * return value of stk3310_gpio_probe Also add an error path in _probe to put the sensor back into stand-by mode in case of serious errors. Signed-off-by: Hartmut Knaack Signed-off-by: Jonathan Cameron --- drivers/iio/light/stk3310.c | 59 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 45 insertions(+), 14 deletions(-) diff --git a/drivers/iio/light/stk3310.c b/drivers/iio/light/stk3310.c index f101bb5..09f2f6a 100644 --- a/drivers/iio/light/stk3310.c +++ b/drivers/iio/light/stk3310.c @@ -241,8 +241,11 @@ static int stk3310_write_event(struct iio_dev *indio_dev, struct stk3310_data *data = iio_priv(indio_dev); struct i2c_client *client = data->client; - regmap_field_read(data->reg_ps_gain, &index); - if (val > stk3310_ps_max[index]) + ret = regmap_field_read(data->reg_ps_gain, &index); + if (ret < 0) + return ret; + + if (val < 0 || val > stk3310_ps_max[index]) return -EINVAL; if (dir == IIO_EV_DIR_RISING) @@ -266,9 +269,12 @@ static int stk3310_read_event_config(struct iio_dev *indio_dev, enum iio_event_direction dir) { unsigned int event_val; + int ret; struct stk3310_data *data = iio_priv(indio_dev); - regmap_field_read(data->reg_int_ps, &event_val); + ret = regmap_field_read(data->reg_int_ps, &event_val); + if (ret < 0) + return ret; return event_val; } @@ -307,14 +313,16 @@ static int stk3310_read_raw(struct iio_dev *indio_dev, struct stk3310_data *data = iio_priv(indio_dev); struct i2c_client *client = data->client; + if (chan->type != IIO_LIGHT && chan->type != IIO_PROXIMITY) + return -EINVAL; + switch (mask) { case IIO_CHAN_INFO_RAW: if (chan->type == IIO_LIGHT) reg = STK3310_REG_ALS_DATA_MSB; - else if (chan->type == IIO_PROXIMITY) - reg = STK3310_REG_PS_DATA_MSB; else - return -EINVAL; + reg = STK3310_REG_PS_DATA_MSB; + mutex_lock(&data->lock); ret = regmap_bulk_read(data->regmap, reg, &buf, 2); if (ret < 0) { @@ -327,17 +335,23 @@ static int stk3310_read_raw(struct iio_dev *indio_dev, return IIO_VAL_INT; case IIO_CHAN_INFO_INT_TIME: if (chan->type == IIO_LIGHT) - regmap_field_read(data->reg_als_it, &index); + ret = regmap_field_read(data->reg_als_it, &index); else - regmap_field_read(data->reg_ps_it, &index); + ret = regmap_field_read(data->reg_ps_it, &index); + if (ret < 0) + return ret; + *val = stk3310_it_table[index][0]; *val2 = stk3310_it_table[index][1]; return IIO_VAL_INT_PLUS_MICRO; case IIO_CHAN_INFO_SCALE: if (chan->type == IIO_LIGHT) - regmap_field_read(data->reg_als_gain, &index); + ret = regmap_field_read(data->reg_als_gain, &index); else - regmap_field_read(data->reg_ps_gain, &index); + ret = regmap_field_read(data->reg_ps_gain, &index); + if (ret < 0) + return ret; + *val = stk3310_scale_table[index][0]; *val2 = stk3310_scale_table[index][1]; return IIO_VAL_INT_PLUS_MICRO; @@ -354,6 +368,9 @@ static int stk3310_write_raw(struct iio_dev *indio_dev, int index; struct stk3310_data *data = iio_priv(indio_dev); + if (chan->type != IIO_LIGHT && chan->type != IIO_PROXIMITY) + return -EINVAL; + switch (mask) { case IIO_CHAN_INFO_INT_TIME: index = stk3310_get_index(stk3310_it_table, @@ -435,7 +452,10 @@ static int stk3310_init(struct iio_dev *indio_dev) struct stk3310_data *data = iio_priv(indio_dev); struct i2c_client *client = data->client; - regmap_read(data->regmap, STK3310_REG_ID, &chipid); + ret = regmap_read(data->regmap, STK3310_REG_ID, &chipid); + if (ret < 0) + return ret; + if (chipid != STK3310_CHIP_ID_VAL && chipid != STK3311_CHIP_ID_VAL) { dev_err(&client->dev, "invalid chip id: 0x%x\n", chipid); @@ -608,8 +628,13 @@ static int stk3310_probe(struct i2c_client *client, if (ret < 0) return ret; - if (client->irq < 0) + if (client->irq < 0) { client->irq = stk3310_gpio_probe(client); + if (client->irq < 0) { + ret = client->irq; + goto err_standby; + } + } if (client->irq >= 0) { ret = devm_request_threaded_irq(&client->dev, client->irq, @@ -618,17 +643,23 @@ static int stk3310_probe(struct i2c_client *client, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, STK3310_EVENT, indio_dev); - if (ret < 0) + if (ret < 0) { dev_err(&client->dev, "request irq %d failed\n", client->irq); + goto err_standby; + } } ret = iio_device_register(indio_dev); if (ret < 0) { dev_err(&client->dev, "device_register failed\n"); - stk3310_set_state(data, STK3310_STATE_STANDBY); + goto err_standby; } + return 0; + +err_standby: + stk3310_set_state(data, STK3310_STATE_STANDBY); return ret; } -- cgit v1.1 From 952c3aa3fb5538aa5026980cfdedb3d38829b67e Mon Sep 17 00:00:00 2001 From: Hartmut Knaack Date: Thu, 9 Jul 2015 23:51:32 +0200 Subject: iio:light:stk3310: use correct names and type for state Indicate the bit number of predefined states, make use of these names and change the state type in _resume to u8 to avoid type casting. Signed-off-by: Hartmut Knaack Reviewed-by: Tiberiu Breana Signed-off-by: Jonathan Cameron --- drivers/iio/light/stk3310.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/iio/light/stk3310.c b/drivers/iio/light/stk3310.c index 09f2f6a..e5e0b04 100644 --- a/drivers/iio/light/stk3310.c +++ b/drivers/iio/light/stk3310.c @@ -35,8 +35,8 @@ #define STK3310_REG_ID 0x3E #define STK3310_MAX_REG 0x80 -#define STK3310_STATE_EN_PS 0x01 -#define STK3310_STATE_EN_ALS 0x02 +#define STK3310_STATE_EN_PS BIT(0) +#define STK3310_STATE_EN_ALS BIT(1) #define STK3310_STATE_STANDBY 0x00 #define STK3310_CHIP_ID_VAL 0x13 @@ -436,8 +436,8 @@ static int stk3310_set_state(struct stk3310_data *data, u8 state) dev_err(&client->dev, "failed to change sensor state\n"); } else if (state != STK3310_STATE_STANDBY) { /* Don't reset the 'enabled' flags if we're going in standby */ - data->ps_enabled = !!(state & 0x01); - data->als_enabled = !!(state & 0x02); + data->ps_enabled = !!(state & STK3310_STATE_EN_PS); + data->als_enabled = !!(state & STK3310_STATE_EN_ALS); } mutex_unlock(&data->lock); @@ -683,7 +683,7 @@ static int stk3310_suspend(struct device *dev) static int stk3310_resume(struct device *dev) { - int state = 0; + u8 state = 0; struct stk3310_data *data; data = iio_priv(i2c_get_clientdata(to_i2c_client(dev))); -- cgit v1.1 From 5b958f110f8b11f9bb6c62e713b83768b1375f31 Mon Sep 17 00:00:00 2001 From: Hartmut Knaack Date: Thu, 9 Jul 2015 23:51:33 +0200 Subject: iio:light:stk3310: adjust indentation Adjust some indentation issues as spotted by checkpatch.pl --strict Signed-off-by: Hartmut Knaack Reviewed-by: Tiberiu Breana Signed-off-by: Jonathan Cameron --- drivers/iio/light/stk3310.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/iio/light/stk3310.c b/drivers/iio/light/stk3310.c index e5e0b04..25c6a71 100644 --- a/drivers/iio/light/stk3310.c +++ b/drivers/iio/light/stk3310.c @@ -385,7 +385,7 @@ static int stk3310_write_raw(struct iio_dev *indio_dev, ret = regmap_field_write(data->reg_ps_it, index); if (ret < 0) dev_err(&data->client->dev, - "sensor configuration failed\n"); + "sensor configuration failed\n"); mutex_unlock(&data->lock); return ret; @@ -402,7 +402,7 @@ static int stk3310_write_raw(struct iio_dev *indio_dev, ret = regmap_field_write(data->reg_ps_gain, index); if (ret < 0) dev_err(&data->client->dev, - "sensor configuration failed\n"); + "sensor configuration failed\n"); mutex_unlock(&data->lock); return ret; } @@ -645,7 +645,7 @@ static int stk3310_probe(struct i2c_client *client, STK3310_EVENT, indio_dev); if (ret < 0) { dev_err(&client->dev, "request irq %d failed\n", - client->irq); + client->irq); goto err_standby; } } -- cgit v1.1 From 2427d22de807b40f72175c8c9b1ff8a07275ad82 Mon Sep 17 00:00:00 2001 From: Hartmut Knaack Date: Fri, 17 Jul 2015 10:52:25 +0200 Subject: iio:magnetometer:bmc150_magn: sort entry alphabetically Sort the entry for bmc105_magn in Kconfig and Makefile to its correct position. Also add the minor module information for completeness. Fixes: c91746a2361d ("iio: magn: Add support for BMC150 magnetometer") Signed-off-by: Hartmut Knaack Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/Kconfig | 33 ++++++++++++++++++--------------- drivers/iio/magnetometer/Makefile | 3 +-- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/drivers/iio/magnetometer/Kconfig b/drivers/iio/magnetometer/Kconfig index efb9350..868abad 100644 --- a/drivers/iio/magnetometer/Kconfig +++ b/drivers/iio/magnetometer/Kconfig @@ -24,6 +24,24 @@ config AK09911 help Deprecated: AK09911 is now supported by AK8975 driver. +config BMC150_MAGN + tristate "Bosch BMC150 Magnetometer Driver" + depends on I2C + select REGMAP_I2C + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for the BMC150 magnetometer. + + Currently this only supports the device via an i2c interface. + + This is a combo module with both accelerometer and magnetometer. + This driver is only implementing magnetometer part, which has + its own address and register map. + + To compile this driver as a module, choose M here: the module will be + called bmc150_magn. + config MAG3110 tristate "Freescale MAG3110 3-Axis Magnetometer" depends on I2C @@ -87,19 +105,4 @@ config IIO_ST_MAGN_SPI_3AXIS depends on IIO_ST_MAGN_3AXIS depends on IIO_ST_SENSORS_SPI -config BMC150_MAGN - tristate "Bosch BMC150 Magnetometer Driver" - depends on I2C - select REGMAP_I2C - select IIO_BUFFER - select IIO_TRIGGERED_BUFFER - help - Say yes here to build support for the BMC150 magnetometer. - - Currently this only supports the device via an i2c interface. - - This is a combo module with both accelerometer and magnetometer. - This driver is only implementing magnetometer part, which has - its own address and register map. - endmenu diff --git a/drivers/iio/magnetometer/Makefile b/drivers/iio/magnetometer/Makefile index 33b1d4d..2c72df4 100644 --- a/drivers/iio/magnetometer/Makefile +++ b/drivers/iio/magnetometer/Makefile @@ -4,6 +4,7 @@ # When adding new entries keep the list in alphabetical order obj-$(CONFIG_AK8975) += ak8975.o +obj-$(CONFIG_BMC150_MAGN) += bmc150_magn.o obj-$(CONFIG_MAG3110) += mag3110.o obj-$(CONFIG_HID_SENSOR_MAGNETOMETER_3D) += hid-sensor-magn-3d.o obj-$(CONFIG_MMC35240) += mmc35240.o @@ -14,5 +15,3 @@ st_magn-$(CONFIG_IIO_BUFFER) += st_magn_buffer.o obj-$(CONFIG_IIO_ST_MAGN_I2C_3AXIS) += st_magn_i2c.o obj-$(CONFIG_IIO_ST_MAGN_SPI_3AXIS) += st_magn_spi.o - -obj-$(CONFIG_BMC150_MAGN) += bmc150_magn.o -- cgit v1.1 From 47d5e30ae38ba81600bd2c3292b39d31792470c7 Mon Sep 17 00:00:00 2001 From: Hartmut Knaack Date: Sun, 21 Jun 2015 12:15:51 +0200 Subject: iio:adc:cc10001_adc: resort entry in Kconfig and Makefile Move the entry for the CC 10001 ADC driver in Kconfig and Makefile up to maintain alphabetic order. Signed-off-by: Hartmut Knaack Signed-off-by: Jonathan Cameron --- drivers/iio/adc/Kconfig | 22 +++++++++++----------- drivers/iio/adc/Makefile | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 50c103d..deea62c 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -149,6 +149,17 @@ config BERLIN2_ADC Marvell Berlin2 ADC driver. This ADC has 8 channels, with one used for temperature measurement. +config CC10001_ADC + tristate "Cosmic Circuits 10001 ADC driver" + depends on HAS_IOMEM && HAVE_CLK && REGULATOR + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for Cosmic Circuits 10001 ADC. + + This driver can also be built as a module. If so, the module will be + called cc10001_adc. + config DA9150_GPADC tristate "Dialog DA9150 GPADC driver support" depends on MFD_DA9150 @@ -161,17 +172,6 @@ config DA9150_GPADC To compile this driver as a module, choose M here: the module will be called berlin2-adc. -config CC10001_ADC - tristate "Cosmic Circuits 10001 ADC driver" - depends on HAS_IOMEM && HAVE_CLK && REGULATOR - select IIO_BUFFER - select IIO_TRIGGERED_BUFFER - help - Say yes here to build support for Cosmic Circuits 10001 ADC. - - This driver can also be built as a module. If so, the module will be - called cc10001_adc. - config EXYNOS_ADC tristate "Exynos ADC driver support" depends on ARCH_EXYNOS || ARCH_S3C24XX || ARCH_S3C64XX || (OF && COMPILE_TEST) diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index a096210..fa5723a 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -16,8 +16,8 @@ obj-$(CONFIG_AD799X) += ad799x.o obj-$(CONFIG_AT91_ADC) += at91_adc.o obj-$(CONFIG_AXP288_ADC) += axp288_adc.o obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o -obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o +obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o obj-$(CONFIG_MAX1027) += max1027.o -- cgit v1.1 From 050ee2f16e245b42c9ba30640bb91b309b424c48 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 14 Aug 2015 16:54:54 +0200 Subject: iio: Move callback buffer to its own module Currently the IIO callback buffer implementation is directly built into the IIO core module when enabled. Given that the callback buffer module is standalone functionallity there is really no reason to do this. So move it to its own module. Also rename the source to follow the standard IIO module naming convention as well as add a license notice to the file. Signed-off-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- drivers/iio/Kconfig | 2 +- drivers/iio/Makefile | 2 +- drivers/iio/buffer_cb.c | 126 -------------------------------- drivers/iio/industrialio-buffer-cb.c | 138 +++++++++++++++++++++++++++++++++++ 4 files changed, 140 insertions(+), 128 deletions(-) delete mode 100644 drivers/iio/buffer_cb.c create mode 100644 drivers/iio/industrialio-buffer-cb.c diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig index 4011eff..b52c8a3 100644 --- a/drivers/iio/Kconfig +++ b/drivers/iio/Kconfig @@ -21,7 +21,7 @@ config IIO_BUFFER if IIO_BUFFER config IIO_BUFFER_CB - bool "IIO callback buffer used for push in-kernel interfaces" + tristate "IIO callback buffer used for push in-kernel interfaces" help Should be selected by any drivers that do in-kernel push usage. That is, those where the data is pushed to the consumer. diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile index 698afc2..09d8ec5 100644 --- a/drivers/iio/Makefile +++ b/drivers/iio/Makefile @@ -6,8 +6,8 @@ obj-$(CONFIG_IIO) += industrialio.o industrialio-y := industrialio-core.o industrialio-event.o inkern.o industrialio-$(CONFIG_IIO_BUFFER) += industrialio-buffer.o industrialio-$(CONFIG_IIO_TRIGGER) += industrialio-trigger.o -industrialio-$(CONFIG_IIO_BUFFER_CB) += buffer_cb.o +obj-$(CONFIG_IIO_BUFFER_CB) += industrialio-buffer-cb.o obj-$(CONFIG_IIO_TRIGGERED_BUFFER) += industrialio-triggered-buffer.o obj-$(CONFIG_IIO_KFIFO_BUF) += kfifo_buf.o diff --git a/drivers/iio/buffer_cb.c b/drivers/iio/buffer_cb.c deleted file mode 100644 index 1648e6e..0000000 --- a/drivers/iio/buffer_cb.c +++ /dev/null @@ -1,126 +0,0 @@ -#include -#include -#include -#include -#include -#include - -struct iio_cb_buffer { - struct iio_buffer buffer; - int (*cb)(const void *data, void *private); - void *private; - struct iio_channel *channels; -}; - -static struct iio_cb_buffer *buffer_to_cb_buffer(struct iio_buffer *buffer) -{ - return container_of(buffer, struct iio_cb_buffer, buffer); -} - -static int iio_buffer_cb_store_to(struct iio_buffer *buffer, const void *data) -{ - struct iio_cb_buffer *cb_buff = buffer_to_cb_buffer(buffer); - return cb_buff->cb(data, cb_buff->private); -} - -static void iio_buffer_cb_release(struct iio_buffer *buffer) -{ - struct iio_cb_buffer *cb_buff = buffer_to_cb_buffer(buffer); - kfree(cb_buff->buffer.scan_mask); - kfree(cb_buff); -} - -static const struct iio_buffer_access_funcs iio_cb_access = { - .store_to = &iio_buffer_cb_store_to, - .release = &iio_buffer_cb_release, - - .modes = INDIO_BUFFER_SOFTWARE | INDIO_BUFFER_TRIGGERED, -}; - -struct iio_cb_buffer *iio_channel_get_all_cb(struct device *dev, - int (*cb)(const void *data, - void *private), - void *private) -{ - int ret; - struct iio_cb_buffer *cb_buff; - struct iio_dev *indio_dev; - struct iio_channel *chan; - - cb_buff = kzalloc(sizeof(*cb_buff), GFP_KERNEL); - if (cb_buff == NULL) - return ERR_PTR(-ENOMEM); - - iio_buffer_init(&cb_buff->buffer); - - cb_buff->private = private; - cb_buff->cb = cb; - cb_buff->buffer.access = &iio_cb_access; - INIT_LIST_HEAD(&cb_buff->buffer.demux_list); - - cb_buff->channels = iio_channel_get_all(dev); - if (IS_ERR(cb_buff->channels)) { - ret = PTR_ERR(cb_buff->channels); - goto error_free_cb_buff; - } - - indio_dev = cb_buff->channels[0].indio_dev; - cb_buff->buffer.scan_mask - = kcalloc(BITS_TO_LONGS(indio_dev->masklength), sizeof(long), - GFP_KERNEL); - if (cb_buff->buffer.scan_mask == NULL) { - ret = -ENOMEM; - goto error_release_channels; - } - chan = &cb_buff->channels[0]; - while (chan->indio_dev) { - if (chan->indio_dev != indio_dev) { - ret = -EINVAL; - goto error_free_scan_mask; - } - set_bit(chan->channel->scan_index, - cb_buff->buffer.scan_mask); - chan++; - } - - return cb_buff; - -error_free_scan_mask: - kfree(cb_buff->buffer.scan_mask); -error_release_channels: - iio_channel_release_all(cb_buff->channels); -error_free_cb_buff: - kfree(cb_buff); - return ERR_PTR(ret); -} -EXPORT_SYMBOL_GPL(iio_channel_get_all_cb); - -int iio_channel_start_all_cb(struct iio_cb_buffer *cb_buff) -{ - return iio_update_buffers(cb_buff->channels[0].indio_dev, - &cb_buff->buffer, - NULL); -} -EXPORT_SYMBOL_GPL(iio_channel_start_all_cb); - -void iio_channel_stop_all_cb(struct iio_cb_buffer *cb_buff) -{ - iio_update_buffers(cb_buff->channels[0].indio_dev, - NULL, - &cb_buff->buffer); -} -EXPORT_SYMBOL_GPL(iio_channel_stop_all_cb); - -void iio_channel_release_all_cb(struct iio_cb_buffer *cb_buff) -{ - iio_channel_release_all(cb_buff->channels); - iio_buffer_put(&cb_buff->buffer); -} -EXPORT_SYMBOL_GPL(iio_channel_release_all_cb); - -struct iio_channel -*iio_channel_cb_get_channels(const struct iio_cb_buffer *cb_buffer) -{ - return cb_buffer->channels; -} -EXPORT_SYMBOL_GPL(iio_channel_cb_get_channels); diff --git a/drivers/iio/industrialio-buffer-cb.c b/drivers/iio/industrialio-buffer-cb.c new file mode 100644 index 0000000..323079c --- /dev/null +++ b/drivers/iio/industrialio-buffer-cb.c @@ -0,0 +1,138 @@ +/* The industrial I/O callback buffer + * + * 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 +#include +#include +#include +#include +#include +#include + +struct iio_cb_buffer { + struct iio_buffer buffer; + int (*cb)(const void *data, void *private); + void *private; + struct iio_channel *channels; +}; + +static struct iio_cb_buffer *buffer_to_cb_buffer(struct iio_buffer *buffer) +{ + return container_of(buffer, struct iio_cb_buffer, buffer); +} + +static int iio_buffer_cb_store_to(struct iio_buffer *buffer, const void *data) +{ + struct iio_cb_buffer *cb_buff = buffer_to_cb_buffer(buffer); + return cb_buff->cb(data, cb_buff->private); +} + +static void iio_buffer_cb_release(struct iio_buffer *buffer) +{ + struct iio_cb_buffer *cb_buff = buffer_to_cb_buffer(buffer); + kfree(cb_buff->buffer.scan_mask); + kfree(cb_buff); +} + +static const struct iio_buffer_access_funcs iio_cb_access = { + .store_to = &iio_buffer_cb_store_to, + .release = &iio_buffer_cb_release, + + .modes = INDIO_BUFFER_SOFTWARE | INDIO_BUFFER_TRIGGERED, +}; + +struct iio_cb_buffer *iio_channel_get_all_cb(struct device *dev, + int (*cb)(const void *data, + void *private), + void *private) +{ + int ret; + struct iio_cb_buffer *cb_buff; + struct iio_dev *indio_dev; + struct iio_channel *chan; + + cb_buff = kzalloc(sizeof(*cb_buff), GFP_KERNEL); + if (cb_buff == NULL) + return ERR_PTR(-ENOMEM); + + iio_buffer_init(&cb_buff->buffer); + + cb_buff->private = private; + cb_buff->cb = cb; + cb_buff->buffer.access = &iio_cb_access; + INIT_LIST_HEAD(&cb_buff->buffer.demux_list); + + cb_buff->channels = iio_channel_get_all(dev); + if (IS_ERR(cb_buff->channels)) { + ret = PTR_ERR(cb_buff->channels); + goto error_free_cb_buff; + } + + indio_dev = cb_buff->channels[0].indio_dev; + cb_buff->buffer.scan_mask + = kcalloc(BITS_TO_LONGS(indio_dev->masklength), sizeof(long), + GFP_KERNEL); + if (cb_buff->buffer.scan_mask == NULL) { + ret = -ENOMEM; + goto error_release_channels; + } + chan = &cb_buff->channels[0]; + while (chan->indio_dev) { + if (chan->indio_dev != indio_dev) { + ret = -EINVAL; + goto error_free_scan_mask; + } + set_bit(chan->channel->scan_index, + cb_buff->buffer.scan_mask); + chan++; + } + + return cb_buff; + +error_free_scan_mask: + kfree(cb_buff->buffer.scan_mask); +error_release_channels: + iio_channel_release_all(cb_buff->channels); +error_free_cb_buff: + kfree(cb_buff); + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(iio_channel_get_all_cb); + +int iio_channel_start_all_cb(struct iio_cb_buffer *cb_buff) +{ + return iio_update_buffers(cb_buff->channels[0].indio_dev, + &cb_buff->buffer, + NULL); +} +EXPORT_SYMBOL_GPL(iio_channel_start_all_cb); + +void iio_channel_stop_all_cb(struct iio_cb_buffer *cb_buff) +{ + iio_update_buffers(cb_buff->channels[0].indio_dev, + NULL, + &cb_buff->buffer); +} +EXPORT_SYMBOL_GPL(iio_channel_stop_all_cb); + +void iio_channel_release_all_cb(struct iio_cb_buffer *cb_buff) +{ + iio_channel_release_all(cb_buff->channels); + iio_buffer_put(&cb_buff->buffer); +} +EXPORT_SYMBOL_GPL(iio_channel_release_all_cb); + +struct iio_channel +*iio_channel_cb_get_channels(const struct iio_cb_buffer *cb_buffer) +{ + return cb_buffer->channels; +} +EXPORT_SYMBOL_GPL(iio_channel_cb_get_channels); + +MODULE_AUTHOR("Jonathan Cameron "); +MODULE_DESCRIPTION("Industrial I/O callback buffer"); +MODULE_LICENSE("GPL"); -- cgit v1.1 From 8548a63b37be4891e7972ba058b785d9468e8907 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 14 Aug 2015 16:54:55 +0200 Subject: iio: Move generic buffer implementations to sub-directory For generic IIO trigger implementations we already have a sub-directory, but the generic buffer implementations currently reside in the IIO top-level directory. The main reason is that things have historically grown into this form. With more generic buffer implementations on its way now is the perfect time to clean this up and introduce a sub-directory for generic buffer implementations to avoid too much clutter in the top-level directory. Signed-off-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- Documentation/DocBook/iio.tmpl | 2 +- drivers/iio/Kconfig | 22 +- drivers/iio/Makefile | 5 +- drivers/iio/buffer/Kconfig | 24 +++ drivers/iio/buffer/Makefile | 8 + drivers/iio/buffer/industrialio-buffer-cb.c | 138 +++++++++++++ drivers/iio/buffer/industrialio-triggered-buffer.c | 103 ++++++++++ drivers/iio/buffer/kfifo_buf.c | 221 +++++++++++++++++++++ drivers/iio/industrialio-buffer-cb.c | 138 ------------- drivers/iio/industrialio-triggered-buffer.c | 103 ---------- drivers/iio/kfifo_buf.c | 221 --------------------- 11 files changed, 497 insertions(+), 488 deletions(-) create mode 100644 drivers/iio/buffer/Kconfig create mode 100644 drivers/iio/buffer/Makefile create mode 100644 drivers/iio/buffer/industrialio-buffer-cb.c create mode 100644 drivers/iio/buffer/industrialio-triggered-buffer.c create mode 100644 drivers/iio/buffer/kfifo_buf.c delete mode 100644 drivers/iio/industrialio-buffer-cb.c delete mode 100644 drivers/iio/industrialio-triggered-buffer.c delete mode 100644 drivers/iio/kfifo_buf.c diff --git a/Documentation/DocBook/iio.tmpl b/Documentation/DocBook/iio.tmpl index 06bb53d..98be322 100644 --- a/Documentation/DocBook/iio.tmpl +++ b/Documentation/DocBook/iio.tmpl @@ -578,7 +578,7 @@ work together. IIO triggered buffer setup -!Edrivers/iio/industrialio-triggered-buffer.c +!Edrivers/iio/buffer/industrialio-triggered-buffer.c !Finclude/linux/iio/iio.h iio_buffer_setup_ops diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig index b52c8a3..3c6c6e2 100644 --- a/drivers/iio/Kconfig +++ b/drivers/iio/Kconfig @@ -19,27 +19,7 @@ config IIO_BUFFER acquisition methods. if IIO_BUFFER - -config IIO_BUFFER_CB - tristate "IIO callback buffer used for push in-kernel interfaces" - help - Should be selected by any drivers that do in-kernel push - usage. That is, those where the data is pushed to the consumer. - -config IIO_KFIFO_BUF - tristate "Industrial I/O buffering based on kfifo" - help - A simple fifo based on kfifo. Note that this currently provides - no buffer events so it is up to userspace to work out how - often to read from the buffer. - -config IIO_TRIGGERED_BUFFER - tristate - select IIO_TRIGGER - select IIO_KFIFO_BUF - help - Provides helper functions for setting up triggered buffers. - + source "drivers/iio/buffer/Kconfig" endif # IIO_BUFFER config IIO_TRIGGER diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile index 09d8ec5..7ddb988 100644 --- a/drivers/iio/Makefile +++ b/drivers/iio/Makefile @@ -7,13 +7,10 @@ industrialio-y := industrialio-core.o industrialio-event.o inkern.o industrialio-$(CONFIG_IIO_BUFFER) += industrialio-buffer.o industrialio-$(CONFIG_IIO_TRIGGER) += industrialio-trigger.o -obj-$(CONFIG_IIO_BUFFER_CB) += industrialio-buffer-cb.o -obj-$(CONFIG_IIO_TRIGGERED_BUFFER) += industrialio-triggered-buffer.o -obj-$(CONFIG_IIO_KFIFO_BUF) += kfifo_buf.o - obj-y += accel/ obj-y += adc/ obj-y += amplifiers/ +obj-y += buffer/ obj-y += common/ obj-y += dac/ obj-y += gyro/ diff --git a/drivers/iio/buffer/Kconfig b/drivers/iio/buffer/Kconfig new file mode 100644 index 0000000..0a7b2fd --- /dev/null +++ b/drivers/iio/buffer/Kconfig @@ -0,0 +1,24 @@ +# +# Industrial I/O generic buffer implementations +# +# When adding new entries keep the list in alphabetical order + +config IIO_BUFFER_CB + tristate "IIO callback buffer used for push in-kernel interfaces" + help + Should be selected by any drivers that do in-kernel push + usage. That is, those where the data is pushed to the consumer. + +config IIO_KFIFO_BUF + tristate "Industrial I/O buffering based on kfifo" + help + A simple fifo based on kfifo. Note that this currently provides + no buffer events so it is up to userspace to work out how + often to read from the buffer. + +config IIO_TRIGGERED_BUFFER + tristate + select IIO_TRIGGER + select IIO_KFIFO_BUF + help + Provides helper functions for setting up triggered buffers. diff --git a/drivers/iio/buffer/Makefile b/drivers/iio/buffer/Makefile new file mode 100644 index 0000000..4d193b9 --- /dev/null +++ b/drivers/iio/buffer/Makefile @@ -0,0 +1,8 @@ +# +# Makefile for the industrial I/O buffer implementations +# + +# When adding new entries keep the list in alphabetical order +obj-$(CONFIG_IIO_BUFFER_CB) += industrialio-buffer-cb.o +obj-$(CONFIG_IIO_TRIGGERED_BUFFER) += industrialio-triggered-buffer.o +obj-$(CONFIG_IIO_KFIFO_BUF) += kfifo_buf.o diff --git a/drivers/iio/buffer/industrialio-buffer-cb.c b/drivers/iio/buffer/industrialio-buffer-cb.c new file mode 100644 index 0000000..323079c --- /dev/null +++ b/drivers/iio/buffer/industrialio-buffer-cb.c @@ -0,0 +1,138 @@ +/* The industrial I/O callback buffer + * + * 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 +#include +#include +#include +#include +#include +#include + +struct iio_cb_buffer { + struct iio_buffer buffer; + int (*cb)(const void *data, void *private); + void *private; + struct iio_channel *channels; +}; + +static struct iio_cb_buffer *buffer_to_cb_buffer(struct iio_buffer *buffer) +{ + return container_of(buffer, struct iio_cb_buffer, buffer); +} + +static int iio_buffer_cb_store_to(struct iio_buffer *buffer, const void *data) +{ + struct iio_cb_buffer *cb_buff = buffer_to_cb_buffer(buffer); + return cb_buff->cb(data, cb_buff->private); +} + +static void iio_buffer_cb_release(struct iio_buffer *buffer) +{ + struct iio_cb_buffer *cb_buff = buffer_to_cb_buffer(buffer); + kfree(cb_buff->buffer.scan_mask); + kfree(cb_buff); +} + +static const struct iio_buffer_access_funcs iio_cb_access = { + .store_to = &iio_buffer_cb_store_to, + .release = &iio_buffer_cb_release, + + .modes = INDIO_BUFFER_SOFTWARE | INDIO_BUFFER_TRIGGERED, +}; + +struct iio_cb_buffer *iio_channel_get_all_cb(struct device *dev, + int (*cb)(const void *data, + void *private), + void *private) +{ + int ret; + struct iio_cb_buffer *cb_buff; + struct iio_dev *indio_dev; + struct iio_channel *chan; + + cb_buff = kzalloc(sizeof(*cb_buff), GFP_KERNEL); + if (cb_buff == NULL) + return ERR_PTR(-ENOMEM); + + iio_buffer_init(&cb_buff->buffer); + + cb_buff->private = private; + cb_buff->cb = cb; + cb_buff->buffer.access = &iio_cb_access; + INIT_LIST_HEAD(&cb_buff->buffer.demux_list); + + cb_buff->channels = iio_channel_get_all(dev); + if (IS_ERR(cb_buff->channels)) { + ret = PTR_ERR(cb_buff->channels); + goto error_free_cb_buff; + } + + indio_dev = cb_buff->channels[0].indio_dev; + cb_buff->buffer.scan_mask + = kcalloc(BITS_TO_LONGS(indio_dev->masklength), sizeof(long), + GFP_KERNEL); + if (cb_buff->buffer.scan_mask == NULL) { + ret = -ENOMEM; + goto error_release_channels; + } + chan = &cb_buff->channels[0]; + while (chan->indio_dev) { + if (chan->indio_dev != indio_dev) { + ret = -EINVAL; + goto error_free_scan_mask; + } + set_bit(chan->channel->scan_index, + cb_buff->buffer.scan_mask); + chan++; + } + + return cb_buff; + +error_free_scan_mask: + kfree(cb_buff->buffer.scan_mask); +error_release_channels: + iio_channel_release_all(cb_buff->channels); +error_free_cb_buff: + kfree(cb_buff); + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(iio_channel_get_all_cb); + +int iio_channel_start_all_cb(struct iio_cb_buffer *cb_buff) +{ + return iio_update_buffers(cb_buff->channels[0].indio_dev, + &cb_buff->buffer, + NULL); +} +EXPORT_SYMBOL_GPL(iio_channel_start_all_cb); + +void iio_channel_stop_all_cb(struct iio_cb_buffer *cb_buff) +{ + iio_update_buffers(cb_buff->channels[0].indio_dev, + NULL, + &cb_buff->buffer); +} +EXPORT_SYMBOL_GPL(iio_channel_stop_all_cb); + +void iio_channel_release_all_cb(struct iio_cb_buffer *cb_buff) +{ + iio_channel_release_all(cb_buff->channels); + iio_buffer_put(&cb_buff->buffer); +} +EXPORT_SYMBOL_GPL(iio_channel_release_all_cb); + +struct iio_channel +*iio_channel_cb_get_channels(const struct iio_cb_buffer *cb_buffer) +{ + return cb_buffer->channels; +} +EXPORT_SYMBOL_GPL(iio_channel_cb_get_channels); + +MODULE_AUTHOR("Jonathan Cameron "); +MODULE_DESCRIPTION("Industrial I/O callback buffer"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/buffer/industrialio-triggered-buffer.c b/drivers/iio/buffer/industrialio-triggered-buffer.c new file mode 100644 index 0000000..4b2858b --- /dev/null +++ b/drivers/iio/buffer/industrialio-triggered-buffer.c @@ -0,0 +1,103 @@ + /* + * Copyright (c) 2012 Analog Devices, Inc. + * Author: Lars-Peter Clausen + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +static const struct iio_buffer_setup_ops iio_triggered_buffer_setup_ops = { + .postenable = &iio_triggered_buffer_postenable, + .predisable = &iio_triggered_buffer_predisable, +}; + +/** + * iio_triggered_buffer_setup() - Setup triggered buffer and pollfunc + * @indio_dev: IIO device structure + * @h: Function which will be used as pollfunc top half + * @thread: Function which will be used as pollfunc bottom half + * @setup_ops: Buffer setup functions to use for this device. + * If NULL the default setup functions for triggered + * buffers will be used. + * + * This function combines some common tasks which will normally be performed + * when setting up a triggered buffer. It will allocate the buffer and the + * pollfunc. + * + * Before calling this function the indio_dev structure should already be + * completely initialized, but not yet registered. In practice this means that + * this function should be called right before iio_device_register(). + * + * To free the resources allocated by this function call + * iio_triggered_buffer_cleanup(). + */ +int iio_triggered_buffer_setup(struct iio_dev *indio_dev, + irqreturn_t (*h)(int irq, void *p), + irqreturn_t (*thread)(int irq, void *p), + const struct iio_buffer_setup_ops *setup_ops) +{ + struct iio_buffer *buffer; + int ret; + + buffer = iio_kfifo_allocate(); + if (!buffer) { + ret = -ENOMEM; + goto error_ret; + } + + iio_device_attach_buffer(indio_dev, buffer); + + indio_dev->pollfunc = iio_alloc_pollfunc(h, + thread, + IRQF_ONESHOT, + indio_dev, + "%s_consumer%d", + indio_dev->name, + indio_dev->id); + if (indio_dev->pollfunc == NULL) { + ret = -ENOMEM; + goto error_kfifo_free; + } + + /* Ring buffer functions - here trigger setup related */ + if (setup_ops) + indio_dev->setup_ops = setup_ops; + else + indio_dev->setup_ops = &iio_triggered_buffer_setup_ops; + + /* Flag that polled ring buffering is possible */ + indio_dev->modes |= INDIO_BUFFER_TRIGGERED; + + return 0; + +error_kfifo_free: + iio_kfifo_free(indio_dev->buffer); +error_ret: + return ret; +} +EXPORT_SYMBOL(iio_triggered_buffer_setup); + +/** + * iio_triggered_buffer_cleanup() - Free resources allocated by iio_triggered_buffer_setup() + * @indio_dev: IIO device structure + */ +void iio_triggered_buffer_cleanup(struct iio_dev *indio_dev) +{ + iio_dealloc_pollfunc(indio_dev->pollfunc); + iio_kfifo_free(indio_dev->buffer); +} +EXPORT_SYMBOL(iio_triggered_buffer_cleanup); + +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_DESCRIPTION("IIO helper functions for setting up triggered buffers"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/buffer/kfifo_buf.c b/drivers/iio/buffer/kfifo_buf.c new file mode 100644 index 0000000..c5b999f --- /dev/null +++ b/drivers/iio/buffer/kfifo_buf.c @@ -0,0 +1,221 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct iio_kfifo { + struct iio_buffer buffer; + struct kfifo kf; + struct mutex user_lock; + int update_needed; +}; + +#define iio_to_kfifo(r) container_of(r, struct iio_kfifo, buffer) + +static inline int __iio_allocate_kfifo(struct iio_kfifo *buf, + int bytes_per_datum, int length) +{ + if ((length == 0) || (bytes_per_datum == 0)) + return -EINVAL; + + return __kfifo_alloc((struct __kfifo *)&buf->kf, length, + bytes_per_datum, GFP_KERNEL); +} + +static int iio_request_update_kfifo(struct iio_buffer *r) +{ + int ret = 0; + struct iio_kfifo *buf = iio_to_kfifo(r); + + mutex_lock(&buf->user_lock); + if (buf->update_needed) { + kfifo_free(&buf->kf); + ret = __iio_allocate_kfifo(buf, buf->buffer.bytes_per_datum, + buf->buffer.length); + if (ret >= 0) + buf->update_needed = false; + } else { + kfifo_reset_out(&buf->kf); + } + mutex_unlock(&buf->user_lock); + + return ret; +} + +static int iio_mark_update_needed_kfifo(struct iio_buffer *r) +{ + struct iio_kfifo *kf = iio_to_kfifo(r); + kf->update_needed = true; + return 0; +} + +static int iio_set_bytes_per_datum_kfifo(struct iio_buffer *r, size_t bpd) +{ + if (r->bytes_per_datum != bpd) { + r->bytes_per_datum = bpd; + iio_mark_update_needed_kfifo(r); + } + return 0; +} + +static int iio_set_length_kfifo(struct iio_buffer *r, int length) +{ + /* Avoid an invalid state */ + if (length < 2) + length = 2; + if (r->length != length) { + r->length = length; + iio_mark_update_needed_kfifo(r); + } + return 0; +} + +static int iio_store_to_kfifo(struct iio_buffer *r, + const void *data) +{ + int ret; + struct iio_kfifo *kf = iio_to_kfifo(r); + ret = kfifo_in(&kf->kf, data, 1); + if (ret != 1) + return -EBUSY; + return 0; +} + +static int iio_read_first_n_kfifo(struct iio_buffer *r, + size_t n, char __user *buf) +{ + int ret, copied; + struct iio_kfifo *kf = iio_to_kfifo(r); + + if (mutex_lock_interruptible(&kf->user_lock)) + return -ERESTARTSYS; + + if (!kfifo_initialized(&kf->kf) || n < kfifo_esize(&kf->kf)) + ret = -EINVAL; + else + ret = kfifo_to_user(&kf->kf, buf, n, &copied); + mutex_unlock(&kf->user_lock); + if (ret < 0) + return ret; + + return copied; +} + +static size_t iio_kfifo_buf_data_available(struct iio_buffer *r) +{ + struct iio_kfifo *kf = iio_to_kfifo(r); + size_t samples; + + mutex_lock(&kf->user_lock); + samples = kfifo_len(&kf->kf); + mutex_unlock(&kf->user_lock); + + return samples; +} + +static void iio_kfifo_buffer_release(struct iio_buffer *buffer) +{ + struct iio_kfifo *kf = iio_to_kfifo(buffer); + + mutex_destroy(&kf->user_lock); + kfifo_free(&kf->kf); + kfree(kf); +} + +static const struct iio_buffer_access_funcs kfifo_access_funcs = { + .store_to = &iio_store_to_kfifo, + .read_first_n = &iio_read_first_n_kfifo, + .data_available = iio_kfifo_buf_data_available, + .request_update = &iio_request_update_kfifo, + .set_bytes_per_datum = &iio_set_bytes_per_datum_kfifo, + .set_length = &iio_set_length_kfifo, + .release = &iio_kfifo_buffer_release, + + .modes = INDIO_BUFFER_SOFTWARE | INDIO_BUFFER_TRIGGERED, +}; + +struct iio_buffer *iio_kfifo_allocate(void) +{ + struct iio_kfifo *kf; + + kf = kzalloc(sizeof(*kf), GFP_KERNEL); + if (!kf) + return NULL; + + kf->update_needed = true; + iio_buffer_init(&kf->buffer); + kf->buffer.access = &kfifo_access_funcs; + kf->buffer.length = 2; + mutex_init(&kf->user_lock); + + return &kf->buffer; +} +EXPORT_SYMBOL(iio_kfifo_allocate); + +void iio_kfifo_free(struct iio_buffer *r) +{ + iio_buffer_put(r); +} +EXPORT_SYMBOL(iio_kfifo_free); + +static void devm_iio_kfifo_release(struct device *dev, void *res) +{ + iio_kfifo_free(*(struct iio_buffer **)res); +} + +static int devm_iio_kfifo_match(struct device *dev, void *res, void *data) +{ + struct iio_buffer **r = res; + + if (WARN_ON(!r || !*r)) + return 0; + + return *r == data; +} + +/** + * devm_iio_fifo_allocate - Resource-managed iio_kfifo_allocate() + * @dev: Device to allocate kfifo buffer for + * + * RETURNS: + * Pointer to allocated iio_buffer on success, NULL on failure. + */ +struct iio_buffer *devm_iio_kfifo_allocate(struct device *dev) +{ + struct iio_buffer **ptr, *r; + + ptr = devres_alloc(devm_iio_kfifo_release, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return NULL; + + r = iio_kfifo_allocate(); + if (r) { + *ptr = r; + devres_add(dev, ptr); + } else { + devres_free(ptr); + } + + return r; +} +EXPORT_SYMBOL(devm_iio_kfifo_allocate); + +/** + * devm_iio_fifo_free - Resource-managed iio_kfifo_free() + * @dev: Device the buffer belongs to + * @r: The buffer associated with the device + */ +void devm_iio_kfifo_free(struct device *dev, struct iio_buffer *r) +{ + WARN_ON(devres_release(dev, devm_iio_kfifo_release, + devm_iio_kfifo_match, r)); +} +EXPORT_SYMBOL(devm_iio_kfifo_free); + +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/industrialio-buffer-cb.c b/drivers/iio/industrialio-buffer-cb.c deleted file mode 100644 index 323079c..0000000 --- a/drivers/iio/industrialio-buffer-cb.c +++ /dev/null @@ -1,138 +0,0 @@ -/* The industrial I/O callback buffer - * - * 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 -#include -#include -#include -#include -#include -#include - -struct iio_cb_buffer { - struct iio_buffer buffer; - int (*cb)(const void *data, void *private); - void *private; - struct iio_channel *channels; -}; - -static struct iio_cb_buffer *buffer_to_cb_buffer(struct iio_buffer *buffer) -{ - return container_of(buffer, struct iio_cb_buffer, buffer); -} - -static int iio_buffer_cb_store_to(struct iio_buffer *buffer, const void *data) -{ - struct iio_cb_buffer *cb_buff = buffer_to_cb_buffer(buffer); - return cb_buff->cb(data, cb_buff->private); -} - -static void iio_buffer_cb_release(struct iio_buffer *buffer) -{ - struct iio_cb_buffer *cb_buff = buffer_to_cb_buffer(buffer); - kfree(cb_buff->buffer.scan_mask); - kfree(cb_buff); -} - -static const struct iio_buffer_access_funcs iio_cb_access = { - .store_to = &iio_buffer_cb_store_to, - .release = &iio_buffer_cb_release, - - .modes = INDIO_BUFFER_SOFTWARE | INDIO_BUFFER_TRIGGERED, -}; - -struct iio_cb_buffer *iio_channel_get_all_cb(struct device *dev, - int (*cb)(const void *data, - void *private), - void *private) -{ - int ret; - struct iio_cb_buffer *cb_buff; - struct iio_dev *indio_dev; - struct iio_channel *chan; - - cb_buff = kzalloc(sizeof(*cb_buff), GFP_KERNEL); - if (cb_buff == NULL) - return ERR_PTR(-ENOMEM); - - iio_buffer_init(&cb_buff->buffer); - - cb_buff->private = private; - cb_buff->cb = cb; - cb_buff->buffer.access = &iio_cb_access; - INIT_LIST_HEAD(&cb_buff->buffer.demux_list); - - cb_buff->channels = iio_channel_get_all(dev); - if (IS_ERR(cb_buff->channels)) { - ret = PTR_ERR(cb_buff->channels); - goto error_free_cb_buff; - } - - indio_dev = cb_buff->channels[0].indio_dev; - cb_buff->buffer.scan_mask - = kcalloc(BITS_TO_LONGS(indio_dev->masklength), sizeof(long), - GFP_KERNEL); - if (cb_buff->buffer.scan_mask == NULL) { - ret = -ENOMEM; - goto error_release_channels; - } - chan = &cb_buff->channels[0]; - while (chan->indio_dev) { - if (chan->indio_dev != indio_dev) { - ret = -EINVAL; - goto error_free_scan_mask; - } - set_bit(chan->channel->scan_index, - cb_buff->buffer.scan_mask); - chan++; - } - - return cb_buff; - -error_free_scan_mask: - kfree(cb_buff->buffer.scan_mask); -error_release_channels: - iio_channel_release_all(cb_buff->channels); -error_free_cb_buff: - kfree(cb_buff); - return ERR_PTR(ret); -} -EXPORT_SYMBOL_GPL(iio_channel_get_all_cb); - -int iio_channel_start_all_cb(struct iio_cb_buffer *cb_buff) -{ - return iio_update_buffers(cb_buff->channels[0].indio_dev, - &cb_buff->buffer, - NULL); -} -EXPORT_SYMBOL_GPL(iio_channel_start_all_cb); - -void iio_channel_stop_all_cb(struct iio_cb_buffer *cb_buff) -{ - iio_update_buffers(cb_buff->channels[0].indio_dev, - NULL, - &cb_buff->buffer); -} -EXPORT_SYMBOL_GPL(iio_channel_stop_all_cb); - -void iio_channel_release_all_cb(struct iio_cb_buffer *cb_buff) -{ - iio_channel_release_all(cb_buff->channels); - iio_buffer_put(&cb_buff->buffer); -} -EXPORT_SYMBOL_GPL(iio_channel_release_all_cb); - -struct iio_channel -*iio_channel_cb_get_channels(const struct iio_cb_buffer *cb_buffer) -{ - return cb_buffer->channels; -} -EXPORT_SYMBOL_GPL(iio_channel_cb_get_channels); - -MODULE_AUTHOR("Jonathan Cameron "); -MODULE_DESCRIPTION("Industrial I/O callback buffer"); -MODULE_LICENSE("GPL"); diff --git a/drivers/iio/industrialio-triggered-buffer.c b/drivers/iio/industrialio-triggered-buffer.c deleted file mode 100644 index 4b2858b..0000000 --- a/drivers/iio/industrialio-triggered-buffer.c +++ /dev/null @@ -1,103 +0,0 @@ - /* - * Copyright (c) 2012 Analog Devices, Inc. - * Author: Lars-Peter Clausen - * - * 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 -#include -#include -#include -#include -#include -#include -#include - -static const struct iio_buffer_setup_ops iio_triggered_buffer_setup_ops = { - .postenable = &iio_triggered_buffer_postenable, - .predisable = &iio_triggered_buffer_predisable, -}; - -/** - * iio_triggered_buffer_setup() - Setup triggered buffer and pollfunc - * @indio_dev: IIO device structure - * @h: Function which will be used as pollfunc top half - * @thread: Function which will be used as pollfunc bottom half - * @setup_ops: Buffer setup functions to use for this device. - * If NULL the default setup functions for triggered - * buffers will be used. - * - * This function combines some common tasks which will normally be performed - * when setting up a triggered buffer. It will allocate the buffer and the - * pollfunc. - * - * Before calling this function the indio_dev structure should already be - * completely initialized, but not yet registered. In practice this means that - * this function should be called right before iio_device_register(). - * - * To free the resources allocated by this function call - * iio_triggered_buffer_cleanup(). - */ -int iio_triggered_buffer_setup(struct iio_dev *indio_dev, - irqreturn_t (*h)(int irq, void *p), - irqreturn_t (*thread)(int irq, void *p), - const struct iio_buffer_setup_ops *setup_ops) -{ - struct iio_buffer *buffer; - int ret; - - buffer = iio_kfifo_allocate(); - if (!buffer) { - ret = -ENOMEM; - goto error_ret; - } - - iio_device_attach_buffer(indio_dev, buffer); - - indio_dev->pollfunc = iio_alloc_pollfunc(h, - thread, - IRQF_ONESHOT, - indio_dev, - "%s_consumer%d", - indio_dev->name, - indio_dev->id); - if (indio_dev->pollfunc == NULL) { - ret = -ENOMEM; - goto error_kfifo_free; - } - - /* Ring buffer functions - here trigger setup related */ - if (setup_ops) - indio_dev->setup_ops = setup_ops; - else - indio_dev->setup_ops = &iio_triggered_buffer_setup_ops; - - /* Flag that polled ring buffering is possible */ - indio_dev->modes |= INDIO_BUFFER_TRIGGERED; - - return 0; - -error_kfifo_free: - iio_kfifo_free(indio_dev->buffer); -error_ret: - return ret; -} -EXPORT_SYMBOL(iio_triggered_buffer_setup); - -/** - * iio_triggered_buffer_cleanup() - Free resources allocated by iio_triggered_buffer_setup() - * @indio_dev: IIO device structure - */ -void iio_triggered_buffer_cleanup(struct iio_dev *indio_dev) -{ - iio_dealloc_pollfunc(indio_dev->pollfunc); - iio_kfifo_free(indio_dev->buffer); -} -EXPORT_SYMBOL(iio_triggered_buffer_cleanup); - -MODULE_AUTHOR("Lars-Peter Clausen "); -MODULE_DESCRIPTION("IIO helper functions for setting up triggered buffers"); -MODULE_LICENSE("GPL"); diff --git a/drivers/iio/kfifo_buf.c b/drivers/iio/kfifo_buf.c deleted file mode 100644 index c5b999f..0000000 --- a/drivers/iio/kfifo_buf.c +++ /dev/null @@ -1,221 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct iio_kfifo { - struct iio_buffer buffer; - struct kfifo kf; - struct mutex user_lock; - int update_needed; -}; - -#define iio_to_kfifo(r) container_of(r, struct iio_kfifo, buffer) - -static inline int __iio_allocate_kfifo(struct iio_kfifo *buf, - int bytes_per_datum, int length) -{ - if ((length == 0) || (bytes_per_datum == 0)) - return -EINVAL; - - return __kfifo_alloc((struct __kfifo *)&buf->kf, length, - bytes_per_datum, GFP_KERNEL); -} - -static int iio_request_update_kfifo(struct iio_buffer *r) -{ - int ret = 0; - struct iio_kfifo *buf = iio_to_kfifo(r); - - mutex_lock(&buf->user_lock); - if (buf->update_needed) { - kfifo_free(&buf->kf); - ret = __iio_allocate_kfifo(buf, buf->buffer.bytes_per_datum, - buf->buffer.length); - if (ret >= 0) - buf->update_needed = false; - } else { - kfifo_reset_out(&buf->kf); - } - mutex_unlock(&buf->user_lock); - - return ret; -} - -static int iio_mark_update_needed_kfifo(struct iio_buffer *r) -{ - struct iio_kfifo *kf = iio_to_kfifo(r); - kf->update_needed = true; - return 0; -} - -static int iio_set_bytes_per_datum_kfifo(struct iio_buffer *r, size_t bpd) -{ - if (r->bytes_per_datum != bpd) { - r->bytes_per_datum = bpd; - iio_mark_update_needed_kfifo(r); - } - return 0; -} - -static int iio_set_length_kfifo(struct iio_buffer *r, int length) -{ - /* Avoid an invalid state */ - if (length < 2) - length = 2; - if (r->length != length) { - r->length = length; - iio_mark_update_needed_kfifo(r); - } - return 0; -} - -static int iio_store_to_kfifo(struct iio_buffer *r, - const void *data) -{ - int ret; - struct iio_kfifo *kf = iio_to_kfifo(r); - ret = kfifo_in(&kf->kf, data, 1); - if (ret != 1) - return -EBUSY; - return 0; -} - -static int iio_read_first_n_kfifo(struct iio_buffer *r, - size_t n, char __user *buf) -{ - int ret, copied; - struct iio_kfifo *kf = iio_to_kfifo(r); - - if (mutex_lock_interruptible(&kf->user_lock)) - return -ERESTARTSYS; - - if (!kfifo_initialized(&kf->kf) || n < kfifo_esize(&kf->kf)) - ret = -EINVAL; - else - ret = kfifo_to_user(&kf->kf, buf, n, &copied); - mutex_unlock(&kf->user_lock); - if (ret < 0) - return ret; - - return copied; -} - -static size_t iio_kfifo_buf_data_available(struct iio_buffer *r) -{ - struct iio_kfifo *kf = iio_to_kfifo(r); - size_t samples; - - mutex_lock(&kf->user_lock); - samples = kfifo_len(&kf->kf); - mutex_unlock(&kf->user_lock); - - return samples; -} - -static void iio_kfifo_buffer_release(struct iio_buffer *buffer) -{ - struct iio_kfifo *kf = iio_to_kfifo(buffer); - - mutex_destroy(&kf->user_lock); - kfifo_free(&kf->kf); - kfree(kf); -} - -static const struct iio_buffer_access_funcs kfifo_access_funcs = { - .store_to = &iio_store_to_kfifo, - .read_first_n = &iio_read_first_n_kfifo, - .data_available = iio_kfifo_buf_data_available, - .request_update = &iio_request_update_kfifo, - .set_bytes_per_datum = &iio_set_bytes_per_datum_kfifo, - .set_length = &iio_set_length_kfifo, - .release = &iio_kfifo_buffer_release, - - .modes = INDIO_BUFFER_SOFTWARE | INDIO_BUFFER_TRIGGERED, -}; - -struct iio_buffer *iio_kfifo_allocate(void) -{ - struct iio_kfifo *kf; - - kf = kzalloc(sizeof(*kf), GFP_KERNEL); - if (!kf) - return NULL; - - kf->update_needed = true; - iio_buffer_init(&kf->buffer); - kf->buffer.access = &kfifo_access_funcs; - kf->buffer.length = 2; - mutex_init(&kf->user_lock); - - return &kf->buffer; -} -EXPORT_SYMBOL(iio_kfifo_allocate); - -void iio_kfifo_free(struct iio_buffer *r) -{ - iio_buffer_put(r); -} -EXPORT_SYMBOL(iio_kfifo_free); - -static void devm_iio_kfifo_release(struct device *dev, void *res) -{ - iio_kfifo_free(*(struct iio_buffer **)res); -} - -static int devm_iio_kfifo_match(struct device *dev, void *res, void *data) -{ - struct iio_buffer **r = res; - - if (WARN_ON(!r || !*r)) - return 0; - - return *r == data; -} - -/** - * devm_iio_fifo_allocate - Resource-managed iio_kfifo_allocate() - * @dev: Device to allocate kfifo buffer for - * - * RETURNS: - * Pointer to allocated iio_buffer on success, NULL on failure. - */ -struct iio_buffer *devm_iio_kfifo_allocate(struct device *dev) -{ - struct iio_buffer **ptr, *r; - - ptr = devres_alloc(devm_iio_kfifo_release, sizeof(*ptr), GFP_KERNEL); - if (!ptr) - return NULL; - - r = iio_kfifo_allocate(); - if (r) { - *ptr = r; - devres_add(dev, ptr); - } else { - devres_free(ptr); - } - - return r; -} -EXPORT_SYMBOL(devm_iio_kfifo_allocate); - -/** - * devm_iio_fifo_free - Resource-managed iio_kfifo_free() - * @dev: Device the buffer belongs to - * @r: The buffer associated with the device - */ -void devm_iio_kfifo_free(struct device *dev, struct iio_buffer *r) -{ - WARN_ON(devres_release(dev, devm_iio_kfifo_release, - devm_iio_kfifo_match, r)); -} -EXPORT_SYMBOL(devm_iio_kfifo_free); - -MODULE_LICENSE("GPL"); -- cgit v1.1 From c6c9e995bcd8349f271b39e6abd9bd2f090ec5bd Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Wed, 12 Aug 2015 16:50:04 +0200 Subject: iio: bmg160: Use i2c regmap instead of direct i2c access This patch introduces regmap usage into the driver. This is done to later easily support the SPI interface of this chip. Signed-off-by: Markus Pargmann Reviewed-by: Srinivas Pandruvada Signed-off-by: Jonathan Cameron --- drivers/iio/gyro/Kconfig | 1 + drivers/iio/gyro/bmg160.c | 198 ++++++++++++++++++++-------------------------- 2 files changed, 88 insertions(+), 111 deletions(-) diff --git a/drivers/iio/gyro/Kconfig b/drivers/iio/gyro/Kconfig index 8d24393..cf82d74 100644 --- a/drivers/iio/gyro/Kconfig +++ b/drivers/iio/gyro/Kconfig @@ -55,6 +55,7 @@ config BMG160 depends on I2C select IIO_BUFFER select IIO_TRIGGERED_BUFFER + select REGMAP_I2C help Say yes here to build support for Bosch BMG160 Tri-axis Gyro Sensor driver. This driver also supports BMI055 gyroscope. diff --git a/drivers/iio/gyro/bmg160.c b/drivers/iio/gyro/bmg160.c index 460bf71..d3c5300 100644 --- a/drivers/iio/gyro/bmg160.c +++ b/drivers/iio/gyro/bmg160.c @@ -28,6 +28,7 @@ #include #include #include +#include #define BMG160_DRV_NAME "bmg160" #define BMG160_IRQ_NAME "bmg160_event" @@ -98,6 +99,7 @@ struct bmg160_data { struct i2c_client *client; + struct regmap *regmap; struct iio_trigger *dready_trig; struct iio_trigger *motion_trig; struct mutex mutex; @@ -134,12 +136,17 @@ static const struct { { 133, BMG160_RANGE_250DPS}, { 66, BMG160_RANGE_125DPS} }; +static struct regmap_config bmg160_regmap_i2c_conf = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x3f +}; + static int bmg160_set_mode(struct bmg160_data *data, u8 mode) { int ret; - ret = i2c_smbus_write_byte_data(data->client, - BMG160_REG_PMU_LPW, mode); + ret = regmap_write(data->regmap, BMG160_REG_PMU_LPW, mode); if (ret < 0) { dev_err(&data->client->dev, "Error writing reg_pmu_lpw\n"); return ret; @@ -169,8 +176,7 @@ static int bmg160_set_bw(struct bmg160_data *data, int val) if (bw_bits < 0) return bw_bits; - ret = i2c_smbus_write_byte_data(data->client, BMG160_REG_PMU_BW, - bw_bits); + ret = regmap_write(data->regmap, BMG160_REG_PMU_BW, bw_bits); if (ret < 0) { dev_err(&data->client->dev, "Error writing reg_pmu_bw\n"); return ret; @@ -184,16 +190,17 @@ static int bmg160_set_bw(struct bmg160_data *data, int val) static int bmg160_chip_init(struct bmg160_data *data) { int ret; + unsigned int val; - ret = i2c_smbus_read_byte_data(data->client, BMG160_REG_CHIP_ID); + ret = regmap_read(data->regmap, BMG160_REG_CHIP_ID, &val); if (ret < 0) { dev_err(&data->client->dev, "Error reading reg_chip_id\n"); return ret; } - dev_dbg(&data->client->dev, "Chip Id %x\n", ret); - if (ret != BMG160_CHIP_ID_VAL) { - dev_err(&data->client->dev, "invalid chip %x\n", ret); + dev_dbg(&data->client->dev, "Chip Id %x\n", val); + if (val != BMG160_CHIP_ID_VAL) { + dev_err(&data->client->dev, "invalid chip %x\n", val); return -ENODEV; } @@ -210,40 +217,31 @@ static int bmg160_chip_init(struct bmg160_data *data) return ret; /* Set Default Range */ - ret = i2c_smbus_write_byte_data(data->client, - BMG160_REG_RANGE, - BMG160_RANGE_500DPS); + ret = regmap_write(data->regmap, BMG160_REG_RANGE, BMG160_RANGE_500DPS); if (ret < 0) { dev_err(&data->client->dev, "Error writing reg_range\n"); return ret; } data->dps_range = BMG160_RANGE_500DPS; - ret = i2c_smbus_read_byte_data(data->client, BMG160_REG_SLOPE_THRES); + ret = regmap_read(data->regmap, BMG160_REG_SLOPE_THRES, &val); if (ret < 0) { dev_err(&data->client->dev, "Error reading reg_slope_thres\n"); return ret; } - data->slope_thres = ret; + data->slope_thres = val; /* Set default interrupt mode */ - ret = i2c_smbus_read_byte_data(data->client, BMG160_REG_INT_EN_1); - if (ret < 0) { - dev_err(&data->client->dev, "Error reading reg_int_en_1\n"); - return ret; - } - ret &= ~BMG160_INT1_BIT_OD; - ret = i2c_smbus_write_byte_data(data->client, - BMG160_REG_INT_EN_1, ret); + ret = regmap_update_bits(data->regmap, BMG160_REG_INT_EN_1, + BMG160_INT1_BIT_OD, 0); if (ret < 0) { - dev_err(&data->client->dev, "Error writing reg_int_en_1\n"); + dev_err(&data->client->dev, "Error updating bits in reg_int_en_1\n"); return ret; } - ret = i2c_smbus_write_byte_data(data->client, - BMG160_REG_INT_RST_LATCH, - BMG160_INT_MODE_LATCH_INT | - BMG160_INT_MODE_LATCH_RESET); + ret = regmap_write(data->regmap, BMG160_REG_INT_RST_LATCH, + BMG160_INT_MODE_LATCH_INT | + BMG160_INT_MODE_LATCH_RESET); if (ret < 0) { dev_err(&data->client->dev, "Error writing reg_motion_intr\n"); @@ -284,41 +282,28 @@ static int bmg160_setup_any_motion_interrupt(struct bmg160_data *data, int ret; /* Enable/Disable INT_MAP0 mapping */ - ret = i2c_smbus_read_byte_data(data->client, BMG160_REG_INT_MAP_0); - if (ret < 0) { - dev_err(&data->client->dev, "Error reading reg_int_map0\n"); - return ret; - } - if (status) - ret |= BMG160_INT_MAP_0_BIT_ANY; - else - ret &= ~BMG160_INT_MAP_0_BIT_ANY; - - ret = i2c_smbus_write_byte_data(data->client, - BMG160_REG_INT_MAP_0, - ret); + ret = regmap_update_bits(data->regmap, BMG160_REG_INT_MAP_0, + BMG160_INT_MAP_0_BIT_ANY, + (status ? BMG160_INT_MAP_0_BIT_ANY : 0)); if (ret < 0) { - dev_err(&data->client->dev, "Error writing reg_int_map0\n"); + dev_err(&data->client->dev, "Error updating bits reg_int_map0\n"); return ret; } /* Enable/Disable slope interrupts */ if (status) { /* Update slope thres */ - ret = i2c_smbus_write_byte_data(data->client, - BMG160_REG_SLOPE_THRES, - data->slope_thres); + ret = regmap_write(data->regmap, BMG160_REG_SLOPE_THRES, + data->slope_thres); if (ret < 0) { dev_err(&data->client->dev, "Error writing reg_slope_thres\n"); return ret; } - ret = i2c_smbus_write_byte_data(data->client, - BMG160_REG_MOTION_INTR, - BMG160_INT_MOTION_X | - BMG160_INT_MOTION_Y | - BMG160_INT_MOTION_Z); + ret = regmap_write(data->regmap, BMG160_REG_MOTION_INTR, + BMG160_INT_MOTION_X | BMG160_INT_MOTION_Y | + BMG160_INT_MOTION_Z); if (ret < 0) { dev_err(&data->client->dev, "Error writing reg_motion_intr\n"); @@ -331,10 +316,10 @@ static int bmg160_setup_any_motion_interrupt(struct bmg160_data *data, * to set latched mode, we will be flooded anyway with INTR */ if (!data->dready_trigger_on) { - ret = i2c_smbus_write_byte_data(data->client, - BMG160_REG_INT_RST_LATCH, - BMG160_INT_MODE_LATCH_INT | - BMG160_INT_MODE_LATCH_RESET); + ret = regmap_write(data->regmap, + BMG160_REG_INT_RST_LATCH, + BMG160_INT_MODE_LATCH_INT | + BMG160_INT_MODE_LATCH_RESET); if (ret < 0) { dev_err(&data->client->dev, "Error writing reg_rst_latch\n"); @@ -342,14 +327,12 @@ static int bmg160_setup_any_motion_interrupt(struct bmg160_data *data, } } - ret = i2c_smbus_write_byte_data(data->client, - BMG160_REG_INT_EN_0, - BMG160_DATA_ENABLE_INT); + ret = regmap_write(data->regmap, BMG160_REG_INT_EN_0, + BMG160_DATA_ENABLE_INT); - } else - ret = i2c_smbus_write_byte_data(data->client, - BMG160_REG_INT_EN_0, - 0); + } else { + ret = regmap_write(data->regmap, BMG160_REG_INT_EN_0, 0); + } if (ret < 0) { dev_err(&data->client->dev, "Error writing reg_int_en0\n"); @@ -365,55 +348,39 @@ static int bmg160_setup_new_data_interrupt(struct bmg160_data *data, int ret; /* Enable/Disable INT_MAP1 mapping */ - ret = i2c_smbus_read_byte_data(data->client, BMG160_REG_INT_MAP_1); - if (ret < 0) { - dev_err(&data->client->dev, "Error reading reg_int_map1\n"); - return ret; - } - - if (status) - ret |= BMG160_INT_MAP_1_BIT_NEW_DATA; - else - ret &= ~BMG160_INT_MAP_1_BIT_NEW_DATA; - - ret = i2c_smbus_write_byte_data(data->client, - BMG160_REG_INT_MAP_1, - ret); + ret = regmap_update_bits(data->regmap, BMG160_REG_INT_MAP_1, + BMG160_INT_MAP_1_BIT_NEW_DATA, + (status ? BMG160_INT_MAP_1_BIT_NEW_DATA : 0)); if (ret < 0) { - dev_err(&data->client->dev, "Error writing reg_int_map1\n"); + dev_err(&data->client->dev, "Error updating bits in reg_int_map1\n"); return ret; } if (status) { - ret = i2c_smbus_write_byte_data(data->client, - BMG160_REG_INT_RST_LATCH, - BMG160_INT_MODE_NON_LATCH_INT | - BMG160_INT_MODE_LATCH_RESET); + ret = regmap_write(data->regmap, BMG160_REG_INT_RST_LATCH, + BMG160_INT_MODE_NON_LATCH_INT | + BMG160_INT_MODE_LATCH_RESET); if (ret < 0) { dev_err(&data->client->dev, "Error writing reg_rst_latch\n"); return ret; } - ret = i2c_smbus_write_byte_data(data->client, - BMG160_REG_INT_EN_0, - BMG160_DATA_ENABLE_INT); + ret = regmap_write(data->regmap, BMG160_REG_INT_EN_0, + BMG160_DATA_ENABLE_INT); } else { /* Restore interrupt mode */ - ret = i2c_smbus_write_byte_data(data->client, - BMG160_REG_INT_RST_LATCH, - BMG160_INT_MODE_LATCH_INT | - BMG160_INT_MODE_LATCH_RESET); + ret = regmap_write(data->regmap, BMG160_REG_INT_RST_LATCH, + BMG160_INT_MODE_LATCH_INT | + BMG160_INT_MODE_LATCH_RESET); if (ret < 0) { dev_err(&data->client->dev, "Error writing reg_rst_latch\n"); return ret; } - ret = i2c_smbus_write_byte_data(data->client, - BMG160_REG_INT_EN_0, - 0); + ret = regmap_write(data->regmap, BMG160_REG_INT_EN_0, 0); } if (ret < 0) { @@ -444,10 +411,8 @@ static int bmg160_set_scale(struct bmg160_data *data, int val) for (i = 0; i < ARRAY_SIZE(bmg160_scale_table); ++i) { if (bmg160_scale_table[i].scale == val) { - ret = i2c_smbus_write_byte_data( - data->client, - BMG160_REG_RANGE, - bmg160_scale_table[i].dps_range); + ret = regmap_write(data->regmap, BMG160_REG_RANGE, + bmg160_scale_table[i].dps_range); if (ret < 0) { dev_err(&data->client->dev, "Error writing reg_range\n"); @@ -464,6 +429,7 @@ static int bmg160_set_scale(struct bmg160_data *data, int val) static int bmg160_get_temp(struct bmg160_data *data, int *val) { int ret; + unsigned int raw_val; mutex_lock(&data->mutex); ret = bmg160_set_power_state(data, true); @@ -472,7 +438,7 @@ static int bmg160_get_temp(struct bmg160_data *data, int *val) return ret; } - ret = i2c_smbus_read_byte_data(data->client, BMG160_REG_TEMP); + ret = regmap_read(data->regmap, BMG160_REG_TEMP, &raw_val); if (ret < 0) { dev_err(&data->client->dev, "Error reading reg_temp\n"); bmg160_set_power_state(data, false); @@ -480,7 +446,7 @@ static int bmg160_get_temp(struct bmg160_data *data, int *val) return ret; } - *val = sign_extend32(ret, 7); + *val = sign_extend32(raw_val, 7); ret = bmg160_set_power_state(data, false); mutex_unlock(&data->mutex); if (ret < 0) @@ -492,6 +458,7 @@ static int bmg160_get_temp(struct bmg160_data *data, int *val) static int bmg160_get_axis(struct bmg160_data *data, int axis, int *val) { int ret; + unsigned int raw_val; mutex_lock(&data->mutex); ret = bmg160_set_power_state(data, true); @@ -500,7 +467,8 @@ static int bmg160_get_axis(struct bmg160_data *data, int axis, int *val) return ret; } - ret = i2c_smbus_read_word_data(data->client, BMG160_AXIS_TO_REG(axis)); + ret = regmap_bulk_read(data->regmap, BMG160_AXIS_TO_REG(axis), &raw_val, + 2); if (ret < 0) { dev_err(&data->client->dev, "Error reading axis %d\n", axis); bmg160_set_power_state(data, false); @@ -508,7 +476,7 @@ static int bmg160_get_axis(struct bmg160_data *data, int axis, int *val) return ret; } - *val = sign_extend32(ret, 15); + *val = sign_extend32(raw_val, 15); ret = bmg160_set_power_state(data, false); mutex_unlock(&data->mutex); if (ret < 0) @@ -807,12 +775,13 @@ static irqreturn_t bmg160_trigger_handler(int irq, void *p) struct iio_dev *indio_dev = pf->indio_dev; struct bmg160_data *data = iio_priv(indio_dev); int bit, ret, i = 0; + unsigned int val; mutex_lock(&data->mutex); for_each_set_bit(bit, indio_dev->active_scan_mask, indio_dev->masklength) { - ret = i2c_smbus_read_word_data(data->client, - BMG160_AXIS_TO_REG(bit)); + ret = regmap_bulk_read(data->regmap, BMG160_AXIS_TO_REG(bit), + &val, 2); if (ret < 0) { mutex_unlock(&data->mutex); goto err; @@ -840,10 +809,9 @@ static int bmg160_trig_try_reen(struct iio_trigger *trig) return 0; /* Set latched mode interrupt and clear any latched interrupt */ - ret = i2c_smbus_write_byte_data(data->client, - BMG160_REG_INT_RST_LATCH, - BMG160_INT_MODE_LATCH_INT | - BMG160_INT_MODE_LATCH_RESET); + ret = regmap_write(data->regmap, BMG160_REG_INT_RST_LATCH, + BMG160_INT_MODE_LATCH_INT | + BMG160_INT_MODE_LATCH_RESET); if (ret < 0) { dev_err(&data->client->dev, "Error writing reg_rst_latch\n"); return ret; @@ -907,33 +875,34 @@ static irqreturn_t bmg160_event_handler(int irq, void *private) struct bmg160_data *data = iio_priv(indio_dev); int ret; int dir; + unsigned int val; - ret = i2c_smbus_read_byte_data(data->client, BMG160_REG_INT_STATUS_2); + ret = regmap_read(data->regmap, BMG160_REG_INT_STATUS_2, &val); if (ret < 0) { dev_err(&data->client->dev, "Error reading reg_int_status2\n"); goto ack_intr_status; } - if (ret & 0x08) + if (val & 0x08) dir = IIO_EV_DIR_RISING; else dir = IIO_EV_DIR_FALLING; - if (ret & BMG160_ANY_MOTION_BIT_X) + if (val & BMG160_ANY_MOTION_BIT_X) iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ANGL_VEL, 0, IIO_MOD_X, IIO_EV_TYPE_ROC, dir), iio_get_time_ns()); - if (ret & BMG160_ANY_MOTION_BIT_Y) + if (val & BMG160_ANY_MOTION_BIT_Y) iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ANGL_VEL, 0, IIO_MOD_Y, IIO_EV_TYPE_ROC, dir), iio_get_time_ns()); - if (ret & BMG160_ANY_MOTION_BIT_Z) + if (val & BMG160_ANY_MOTION_BIT_Z) iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ANGL_VEL, 0, IIO_MOD_Z, @@ -943,10 +912,9 @@ static irqreturn_t bmg160_event_handler(int irq, void *private) ack_intr_status: if (!data->dready_trigger_on) { - ret = i2c_smbus_write_byte_data(data->client, - BMG160_REG_INT_RST_LATCH, - BMG160_INT_MODE_LATCH_INT | - BMG160_INT_MODE_LATCH_RESET); + ret = regmap_write(data->regmap, BMG160_REG_INT_RST_LATCH, + BMG160_INT_MODE_LATCH_INT | + BMG160_INT_MODE_LATCH_RESET); if (ret < 0) dev_err(&data->client->dev, "Error writing reg_rst_latch\n"); @@ -1038,6 +1006,14 @@ static int bmg160_probe(struct i2c_client *client, struct iio_dev *indio_dev; int ret; const char *name = NULL; + struct regmap *regmap; + + regmap = devm_regmap_init_i2c(client, &bmg160_regmap_i2c_conf); + if (IS_ERR(regmap)) { + dev_err(&client->dev, "Failed to register i2c regmap %d\n", + (int)PTR_ERR(regmap)); + return PTR_ERR(regmap); + } indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); if (!indio_dev) -- cgit v1.1 From 74e04345dc7e111fd151923d2de32267f1aae321 Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Wed, 12 Aug 2015 16:50:05 +0200 Subject: iio: bmg160: Remove i2c_client from data struct i2c_client variable is not really used anymore in the core driver. It is only used to get the device to make proper outputs. This patch replaces all i2c_client usage through direct usage of the device pointer. Signed-off-by: Markus Pargmann Reviewed-by: Srinivas Pandruvada Signed-off-by: Jonathan Cameron --- drivers/iio/gyro/bmg160.c | 64 +++++++++++++++++++++++------------------------ 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/drivers/iio/gyro/bmg160.c b/drivers/iio/gyro/bmg160.c index d3c5300..e73f7f2 100644 --- a/drivers/iio/gyro/bmg160.c +++ b/drivers/iio/gyro/bmg160.c @@ -98,7 +98,7 @@ #define BMG160_AUTO_SUSPEND_DELAY_MS 2000 struct bmg160_data { - struct i2c_client *client; + struct device *dev; struct regmap *regmap; struct iio_trigger *dready_trig; struct iio_trigger *motion_trig; @@ -148,7 +148,7 @@ static int bmg160_set_mode(struct bmg160_data *data, u8 mode) ret = regmap_write(data->regmap, BMG160_REG_PMU_LPW, mode); if (ret < 0) { - dev_err(&data->client->dev, "Error writing reg_pmu_lpw\n"); + dev_err(data->dev, "Error writing reg_pmu_lpw\n"); return ret; } @@ -178,7 +178,7 @@ static int bmg160_set_bw(struct bmg160_data *data, int val) ret = regmap_write(data->regmap, BMG160_REG_PMU_BW, bw_bits); if (ret < 0) { - dev_err(&data->client->dev, "Error writing reg_pmu_bw\n"); + dev_err(data->dev, "Error writing reg_pmu_bw\n"); return ret; } @@ -194,13 +194,13 @@ static int bmg160_chip_init(struct bmg160_data *data) ret = regmap_read(data->regmap, BMG160_REG_CHIP_ID, &val); if (ret < 0) { - dev_err(&data->client->dev, "Error reading reg_chip_id\n"); + dev_err(data->dev, "Error reading reg_chip_id\n"); return ret; } - dev_dbg(&data->client->dev, "Chip Id %x\n", val); + dev_dbg(data->dev, "Chip Id %x\n", val); if (val != BMG160_CHIP_ID_VAL) { - dev_err(&data->client->dev, "invalid chip %x\n", val); + dev_err(data->dev, "invalid chip %x\n", val); return -ENODEV; } @@ -219,14 +219,14 @@ static int bmg160_chip_init(struct bmg160_data *data) /* Set Default Range */ ret = regmap_write(data->regmap, BMG160_REG_RANGE, BMG160_RANGE_500DPS); if (ret < 0) { - dev_err(&data->client->dev, "Error writing reg_range\n"); + dev_err(data->dev, "Error writing reg_range\n"); return ret; } data->dps_range = BMG160_RANGE_500DPS; ret = regmap_read(data->regmap, BMG160_REG_SLOPE_THRES, &val); if (ret < 0) { - dev_err(&data->client->dev, "Error reading reg_slope_thres\n"); + dev_err(data->dev, "Error reading reg_slope_thres\n"); return ret; } data->slope_thres = val; @@ -235,7 +235,7 @@ static int bmg160_chip_init(struct bmg160_data *data) ret = regmap_update_bits(data->regmap, BMG160_REG_INT_EN_1, BMG160_INT1_BIT_OD, 0); if (ret < 0) { - dev_err(&data->client->dev, "Error updating bits in reg_int_en_1\n"); + dev_err(data->dev, "Error updating bits in reg_int_en_1\n"); return ret; } @@ -243,7 +243,7 @@ static int bmg160_chip_init(struct bmg160_data *data) BMG160_INT_MODE_LATCH_INT | BMG160_INT_MODE_LATCH_RESET); if (ret < 0) { - dev_err(&data->client->dev, + dev_err(data->dev, "Error writing reg_motion_intr\n"); return ret; } @@ -257,17 +257,17 @@ static int bmg160_set_power_state(struct bmg160_data *data, bool on) int ret; if (on) - ret = pm_runtime_get_sync(&data->client->dev); + ret = pm_runtime_get_sync(data->dev); else { - pm_runtime_mark_last_busy(&data->client->dev); - ret = pm_runtime_put_autosuspend(&data->client->dev); + pm_runtime_mark_last_busy(data->dev); + ret = pm_runtime_put_autosuspend(data->dev); } if (ret < 0) { - dev_err(&data->client->dev, + dev_err(data->dev, "Failed: bmg160_set_power_state for %d\n", on); if (on) - pm_runtime_put_noidle(&data->client->dev); + pm_runtime_put_noidle(data->dev); return ret; } @@ -286,7 +286,7 @@ static int bmg160_setup_any_motion_interrupt(struct bmg160_data *data, BMG160_INT_MAP_0_BIT_ANY, (status ? BMG160_INT_MAP_0_BIT_ANY : 0)); if (ret < 0) { - dev_err(&data->client->dev, "Error updating bits reg_int_map0\n"); + dev_err(data->dev, "Error updating bits reg_int_map0\n"); return ret; } @@ -296,7 +296,7 @@ static int bmg160_setup_any_motion_interrupt(struct bmg160_data *data, ret = regmap_write(data->regmap, BMG160_REG_SLOPE_THRES, data->slope_thres); if (ret < 0) { - dev_err(&data->client->dev, + dev_err(data->dev, "Error writing reg_slope_thres\n"); return ret; } @@ -305,7 +305,7 @@ static int bmg160_setup_any_motion_interrupt(struct bmg160_data *data, BMG160_INT_MOTION_X | BMG160_INT_MOTION_Y | BMG160_INT_MOTION_Z); if (ret < 0) { - dev_err(&data->client->dev, + dev_err(data->dev, "Error writing reg_motion_intr\n"); return ret; } @@ -321,7 +321,7 @@ static int bmg160_setup_any_motion_interrupt(struct bmg160_data *data, BMG160_INT_MODE_LATCH_INT | BMG160_INT_MODE_LATCH_RESET); if (ret < 0) { - dev_err(&data->client->dev, + dev_err(data->dev, "Error writing reg_rst_latch\n"); return ret; } @@ -335,7 +335,7 @@ static int bmg160_setup_any_motion_interrupt(struct bmg160_data *data, } if (ret < 0) { - dev_err(&data->client->dev, "Error writing reg_int_en0\n"); + dev_err(data->dev, "Error writing reg_int_en0\n"); return ret; } @@ -352,7 +352,7 @@ static int bmg160_setup_new_data_interrupt(struct bmg160_data *data, BMG160_INT_MAP_1_BIT_NEW_DATA, (status ? BMG160_INT_MAP_1_BIT_NEW_DATA : 0)); if (ret < 0) { - dev_err(&data->client->dev, "Error updating bits in reg_int_map1\n"); + dev_err(data->dev, "Error updating bits in reg_int_map1\n"); return ret; } @@ -361,7 +361,7 @@ static int bmg160_setup_new_data_interrupt(struct bmg160_data *data, BMG160_INT_MODE_NON_LATCH_INT | BMG160_INT_MODE_LATCH_RESET); if (ret < 0) { - dev_err(&data->client->dev, + dev_err(data->dev, "Error writing reg_rst_latch\n"); return ret; } @@ -375,7 +375,7 @@ static int bmg160_setup_new_data_interrupt(struct bmg160_data *data, BMG160_INT_MODE_LATCH_INT | BMG160_INT_MODE_LATCH_RESET); if (ret < 0) { - dev_err(&data->client->dev, + dev_err(data->dev, "Error writing reg_rst_latch\n"); return ret; } @@ -384,7 +384,7 @@ static int bmg160_setup_new_data_interrupt(struct bmg160_data *data, } if (ret < 0) { - dev_err(&data->client->dev, "Error writing reg_int_en0\n"); + dev_err(data->dev, "Error writing reg_int_en0\n"); return ret; } @@ -414,7 +414,7 @@ static int bmg160_set_scale(struct bmg160_data *data, int val) ret = regmap_write(data->regmap, BMG160_REG_RANGE, bmg160_scale_table[i].dps_range); if (ret < 0) { - dev_err(&data->client->dev, + dev_err(data->dev, "Error writing reg_range\n"); return ret; } @@ -440,7 +440,7 @@ static int bmg160_get_temp(struct bmg160_data *data, int *val) ret = regmap_read(data->regmap, BMG160_REG_TEMP, &raw_val); if (ret < 0) { - dev_err(&data->client->dev, "Error reading reg_temp\n"); + dev_err(data->dev, "Error reading reg_temp\n"); bmg160_set_power_state(data, false); mutex_unlock(&data->mutex); return ret; @@ -470,7 +470,7 @@ static int bmg160_get_axis(struct bmg160_data *data, int axis, int *val) ret = regmap_bulk_read(data->regmap, BMG160_AXIS_TO_REG(axis), &raw_val, 2); if (ret < 0) { - dev_err(&data->client->dev, "Error reading axis %d\n", axis); + dev_err(data->dev, "Error reading axis %d\n", axis); bmg160_set_power_state(data, false); mutex_unlock(&data->mutex); return ret; @@ -813,7 +813,7 @@ static int bmg160_trig_try_reen(struct iio_trigger *trig) BMG160_INT_MODE_LATCH_INT | BMG160_INT_MODE_LATCH_RESET); if (ret < 0) { - dev_err(&data->client->dev, "Error writing reg_rst_latch\n"); + dev_err(data->dev, "Error writing reg_rst_latch\n"); return ret; } @@ -879,7 +879,7 @@ static irqreturn_t bmg160_event_handler(int irq, void *private) ret = regmap_read(data->regmap, BMG160_REG_INT_STATUS_2, &val); if (ret < 0) { - dev_err(&data->client->dev, "Error reading reg_int_status2\n"); + dev_err(data->dev, "Error reading reg_int_status2\n"); goto ack_intr_status; } @@ -916,7 +916,7 @@ ack_intr_status: BMG160_INT_MODE_LATCH_INT | BMG160_INT_MODE_LATCH_RESET); if (ret < 0) - dev_err(&data->client->dev, + dev_err(data->dev, "Error writing reg_rst_latch\n"); } @@ -1021,7 +1021,7 @@ static int bmg160_probe(struct i2c_client *client, data = iio_priv(indio_dev); i2c_set_clientdata(client, indio_dev); - data->client = client; + data->dev = &client->dev; ret = bmg160_chip_init(data); if (ret < 0) @@ -1188,7 +1188,7 @@ static int bmg160_runtime_suspend(struct device *dev) ret = bmg160_set_mode(data, BMG160_MODE_SUSPEND); if (ret < 0) { - dev_err(&data->client->dev, "set mode failed\n"); + dev_err(data->dev, "set mode failed\n"); return -EAGAIN; } -- cgit v1.1 From ebc6eb5907acecdebbd0e180a754d98f21045fbc Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Wed, 12 Aug 2015 16:50:06 +0200 Subject: iio: bmg160: Use generic dev_drvdata i2c_get_clientdata() is specifically for i2c. Replace it with the generic dev_get/set_drvdata() to support different protocols. Signed-off-by: Markus Pargmann Reviewed-by: Srinivas Pandruvada Signed-off-by: Jonathan Cameron --- drivers/iio/gyro/bmg160.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/iio/gyro/bmg160.c b/drivers/iio/gyro/bmg160.c index e73f7f2..7492532 100644 --- a/drivers/iio/gyro/bmg160.c +++ b/drivers/iio/gyro/bmg160.c @@ -1020,7 +1020,7 @@ static int bmg160_probe(struct i2c_client *client, return -ENOMEM; data = iio_priv(indio_dev); - i2c_set_clientdata(client, indio_dev); + dev_set_drvdata(&client->dev, indio_dev); data->dev = &client->dev; ret = bmg160_chip_init(data); @@ -1154,7 +1154,7 @@ static int bmg160_remove(struct i2c_client *client) #ifdef CONFIG_PM_SLEEP static int bmg160_suspend(struct device *dev) { - struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); + struct iio_dev *indio_dev = dev_get_drvdata(dev); struct bmg160_data *data = iio_priv(indio_dev); mutex_lock(&data->mutex); @@ -1166,7 +1166,7 @@ static int bmg160_suspend(struct device *dev) static int bmg160_resume(struct device *dev) { - struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); + struct iio_dev *indio_dev = dev_get_drvdata(dev); struct bmg160_data *data = iio_priv(indio_dev); mutex_lock(&data->mutex); @@ -1182,7 +1182,7 @@ static int bmg160_resume(struct device *dev) #ifdef CONFIG_PM static int bmg160_runtime_suspend(struct device *dev) { - struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); + struct iio_dev *indio_dev = dev_get_drvdata(dev); struct bmg160_data *data = iio_priv(indio_dev); int ret; @@ -1197,7 +1197,7 @@ static int bmg160_runtime_suspend(struct device *dev) static int bmg160_runtime_resume(struct device *dev) { - struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); + struct iio_dev *indio_dev = dev_get_drvdata(dev); struct bmg160_data *data = iio_priv(indio_dev); int ret; -- cgit v1.1 From 5d889abbe5c39c0c9ebe468dbcab2688324a33fc Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Wed, 12 Aug 2015 16:50:07 +0200 Subject: iio: bmg160: Remove remaining uses of i2c_client Signed-off-by: Markus Pargmann Signed-off-by: Jonathan Cameron --- drivers/iio/gyro/bmg160.c | 59 +++++++++++++++++++++++------------------------ 1 file changed, 29 insertions(+), 30 deletions(-) diff --git a/drivers/iio/gyro/bmg160.c b/drivers/iio/gyro/bmg160.c index 7492532..ca12402 100644 --- a/drivers/iio/gyro/bmg160.c +++ b/drivers/iio/gyro/bmg160.c @@ -110,6 +110,7 @@ struct bmg160_data { int slope_thres; bool dready_trigger_on; bool motion_trigger_on; + int irq; }; enum bmg160_axis { @@ -961,18 +962,13 @@ static const struct iio_buffer_setup_ops bmg160_buffer_setup_ops = { .postdisable = bmg160_buffer_postdisable, }; -static int bmg160_gpio_probe(struct i2c_client *client, - struct bmg160_data *data) +static int bmg160_gpio_probe(struct bmg160_data *data) { struct device *dev; struct gpio_desc *gpio; - int ret; - - if (!client) - return -EINVAL; - dev = &client->dev; + dev = data->dev; /* data ready gpio interrupt pin */ gpio = devm_gpiod_get_index(dev, BMG160_GPIO_NAME, 0, GPIOD_IN); @@ -981,11 +977,12 @@ static int bmg160_gpio_probe(struct i2c_client *client, return PTR_ERR(gpio); } - ret = gpiod_to_irq(gpio); + data->irq = gpiod_to_irq(gpio); - dev_dbg(dev, "GPIO resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret); + dev_dbg(dev, "GPIO resource, no:%d irq:%d\n", desc_to_gpio(gpio), + data->irq); - return ret; + return 0; } static const char *bmg160_match_acpi_device(struct device *dev) @@ -1007,6 +1004,7 @@ static int bmg160_probe(struct i2c_client *client, int ret; const char *name = NULL; struct regmap *regmap; + struct device *dev = &client->dev; regmap = devm_regmap_init_i2c(client, &bmg160_regmap_i2c_conf); if (IS_ERR(regmap)) { @@ -1020,8 +1018,9 @@ static int bmg160_probe(struct i2c_client *client, return -ENOMEM; data = iio_priv(indio_dev); - dev_set_drvdata(&client->dev, indio_dev); - data->dev = &client->dev; + dev_set_drvdata(dev, indio_dev); + data->dev = dev; + data->irq = client->irq; ret = bmg160_chip_init(data); if (ret < 0) @@ -1032,22 +1031,22 @@ static int bmg160_probe(struct i2c_client *client, if (id) name = id->name; - if (ACPI_HANDLE(&client->dev)) - name = bmg160_match_acpi_device(&client->dev); + if (ACPI_HANDLE(dev)) + name = bmg160_match_acpi_device(dev); - indio_dev->dev.parent = &client->dev; + indio_dev->dev.parent = dev; indio_dev->channels = bmg160_channels; indio_dev->num_channels = ARRAY_SIZE(bmg160_channels); indio_dev->name = name; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->info = &bmg160_info; - if (client->irq <= 0) - client->irq = bmg160_gpio_probe(client, data); + if (data->irq <= 0) + bmg160_gpio_probe(data); - if (client->irq > 0) { - ret = devm_request_threaded_irq(&client->dev, - client->irq, + if (data->irq > 0) { + ret = devm_request_threaded_irq(dev, + data->irq, bmg160_data_rdy_trig_poll, bmg160_event_handler, IRQF_TRIGGER_RISING, @@ -1056,28 +1055,28 @@ static int bmg160_probe(struct i2c_client *client, if (ret) return ret; - data->dready_trig = devm_iio_trigger_alloc(&client->dev, + data->dready_trig = devm_iio_trigger_alloc(dev, "%s-dev%d", indio_dev->name, indio_dev->id); if (!data->dready_trig) return -ENOMEM; - data->motion_trig = devm_iio_trigger_alloc(&client->dev, + data->motion_trig = devm_iio_trigger_alloc(dev, "%s-any-motion-dev%d", indio_dev->name, indio_dev->id); if (!data->motion_trig) return -ENOMEM; - data->dready_trig->dev.parent = &client->dev; + data->dready_trig->dev.parent = dev; data->dready_trig->ops = &bmg160_trigger_ops; iio_trigger_set_drvdata(data->dready_trig, indio_dev); ret = iio_trigger_register(data->dready_trig); if (ret) return ret; - data->motion_trig->dev.parent = &client->dev; + data->motion_trig->dev.parent = dev; data->motion_trig->ops = &bmg160_trigger_ops; iio_trigger_set_drvdata(data->motion_trig, indio_dev); ret = iio_trigger_register(data->motion_trig); @@ -1092,25 +1091,25 @@ static int bmg160_probe(struct i2c_client *client, bmg160_trigger_handler, &bmg160_buffer_setup_ops); if (ret < 0) { - dev_err(&client->dev, + dev_err(dev, "iio triggered buffer setup failed\n"); goto err_trigger_unregister; } ret = iio_device_register(indio_dev); if (ret < 0) { - dev_err(&client->dev, "unable to register iio device\n"); + dev_err(dev, "unable to register iio device\n"); goto err_buffer_cleanup; } - ret = pm_runtime_set_active(&client->dev); + ret = pm_runtime_set_active(dev); if (ret) goto err_iio_unregister; - pm_runtime_enable(&client->dev); - pm_runtime_set_autosuspend_delay(&client->dev, + pm_runtime_enable(dev); + pm_runtime_set_autosuspend_delay(dev, BMG160_AUTO_SUSPEND_DELAY_MS); - pm_runtime_use_autosuspend(&client->dev); + pm_runtime_use_autosuspend(dev); return 0; -- cgit v1.1 From e0950d3b9e88051082f7d5a60a0f8156285cbb6d Mon Sep 17 00:00:00 2001 From: Cristina Opriceana Date: Wed, 12 Aug 2015 14:28:34 +0300 Subject: Documentation: iio-trig-sysfs: Document add_trigger attribute This patch adds the ABI documentation for the add_trigger attribute, which is provided by the iio-trig-sysfs stand-alone driver. Signed-off-by: Cristina Opriceana Signed-off-by: Jonathan Cameron --- Documentation/ABI/testing/sysfs-bus-iio-trigger-sysfs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-bus-iio-trigger-sysfs b/Documentation/ABI/testing/sysfs-bus-iio-trigger-sysfs index bbb0392..76698d2 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio-trigger-sysfs +++ b/Documentation/ABI/testing/sysfs-bus-iio-trigger-sysfs @@ -18,3 +18,14 @@ Description: trigger. In order to associate the trigger with an IIO device one should write this name string to /sys/bus/iio/devices/iio:deviceY/trigger/current_trigger. + +What: /sys/bus/iio/devices/iio_sysfs_trigger/add_trigger +KernelVersion: 2.6.39 +Contact: linux-iio@vger.kernel.org +Description: + This attribute is provided by the iio-trig-sysfs stand-alone + driver and it is used to activate the creation of a new trigger. + In order to achieve this, one should write a positive integer + into the associated file, which will serve as the id of the + trigger. If the trigger with the specified id is already present + in the system, an invalid argument message will be returned. -- cgit v1.1 From 8d96fc276aaec449871bcb86cef41f3187136f0a Mon Sep 17 00:00:00 2001 From: Cristina Opriceana Date: Wed, 12 Aug 2015 14:29:41 +0300 Subject: Documentation: iio-trig-sysfs: Add remove_trigger attribute ABI This patch adds the documentation ABI for the remove_trigger attribute, provided by the iio-trig-sysfs stand-alone driver. Signed-off-by: Cristina Opriceana Signed-off-by: Jonathan Cameron --- Documentation/ABI/testing/sysfs-bus-iio-trigger-sysfs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-bus-iio-trigger-sysfs b/Documentation/ABI/testing/sysfs-bus-iio-trigger-sysfs index 76698d2..04ac623 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio-trigger-sysfs +++ b/Documentation/ABI/testing/sysfs-bus-iio-trigger-sysfs @@ -29,3 +29,14 @@ Description: into the associated file, which will serve as the id of the trigger. If the trigger with the specified id is already present in the system, an invalid argument message will be returned. + +What: /sys/bus/iio/devices/iio_sysfs_trigger/remove_trigger +KernelVersion: 2.6.39 +Contact: linux-iio@vger.kernel.org +Description: + This attribute is used to unregister and delete a previously + created trigger from the list of available triggers. In order to + achieve this, one should write a positive integer into the + associated file, representing the id of the trigger that needs + to be removed. If the trigger can't be found, an invalid + argument message will be returned to the user. -- cgit v1.1 From a0175b9c76f59c1f5706f986d690e27ba06363dd Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 12 Aug 2015 10:22:41 +0200 Subject: iio: st_sensors: add debugfs register read hook This adds a debugfs hook to read/write registers in the ST sensors using debugfs. Proved to be awesome help when trying to debug why IRQs do not arrive. Signed-off-by: Linus Walleij Acked-by: Denis Ciocca Signed-off-by: Jonathan Cameron --- drivers/iio/accel/st_accel_core.c | 1 + drivers/iio/common/st_sensors/st_sensors_core.c | 22 ++++++++++++++++++++++ drivers/iio/gyro/st_gyro_core.c | 1 + drivers/iio/magnetometer/st_magn_core.c | 1 + drivers/iio/pressure/st_pressure_core.c | 1 + include/linux/iio/common/st_sensors.h | 4 ++++ 6 files changed, 30 insertions(+) diff --git a/drivers/iio/accel/st_accel_core.c b/drivers/iio/accel/st_accel_core.c index ff30f88..dab8b76 100644 --- a/drivers/iio/accel/st_accel_core.c +++ b/drivers/iio/accel/st_accel_core.c @@ -618,6 +618,7 @@ static const struct iio_info accel_info = { .attrs = &st_accel_attribute_group, .read_raw = &st_accel_read_raw, .write_raw = &st_accel_write_raw, + .debugfs_reg_access = &st_sensors_debugfs_reg_access, }; #ifdef CONFIG_IIO_TRIGGER diff --git a/drivers/iio/common/st_sensors/st_sensors_core.c b/drivers/iio/common/st_sensors/st_sensors_core.c index 2e7fdb5..25258e2 100644 --- a/drivers/iio/common/st_sensors/st_sensors_core.c +++ b/drivers/iio/common/st_sensors/st_sensors_core.c @@ -44,6 +44,28 @@ st_sensors_write_data_with_mask_error: return err; } +int st_sensors_debugfs_reg_access(struct iio_dev *indio_dev, + unsigned reg, unsigned writeval, + unsigned *readval) +{ + struct st_sensor_data *sdata = iio_priv(indio_dev); + u8 readdata; + int err; + + if (!readval) + return sdata->tf->write_byte(&sdata->tb, sdata->dev, + (u8)reg, (u8)writeval); + + err = sdata->tf->read_byte(&sdata->tb, sdata->dev, (u8)reg, &readdata); + if (err < 0) + return err; + + *readval = (unsigned)readdata; + + return 0; +} +EXPORT_SYMBOL(st_sensors_debugfs_reg_access); + static int st_sensors_match_odr(struct st_sensor_settings *sensor_settings, unsigned int odr, struct st_sensor_odr_avl *odr_out) { diff --git a/drivers/iio/gyro/st_gyro_core.c b/drivers/iio/gyro/st_gyro_core.c index 4b993a5..02eddce 100644 --- a/drivers/iio/gyro/st_gyro_core.c +++ b/drivers/iio/gyro/st_gyro_core.c @@ -383,6 +383,7 @@ static const struct iio_info gyro_info = { .attrs = &st_gyro_attribute_group, .read_raw = &st_gyro_read_raw, .write_raw = &st_gyro_write_raw, + .debugfs_reg_access = &st_sensors_debugfs_reg_access, }; #ifdef CONFIG_IIO_TRIGGER diff --git a/drivers/iio/magnetometer/st_magn_core.c b/drivers/iio/magnetometer/st_magn_core.c index f8dc4b8..b27f014 100644 --- a/drivers/iio/magnetometer/st_magn_core.c +++ b/drivers/iio/magnetometer/st_magn_core.c @@ -560,6 +560,7 @@ static const struct iio_info magn_info = { .attrs = &st_magn_attribute_group, .read_raw = &st_magn_read_raw, .write_raw = &st_magn_write_raw, + .debugfs_reg_access = &st_sensors_debugfs_reg_access, }; #ifdef CONFIG_IIO_TRIGGER diff --git a/drivers/iio/pressure/st_pressure_core.c b/drivers/iio/pressure/st_pressure_core.c index eb41d2b..b39a2fb 100644 --- a/drivers/iio/pressure/st_pressure_core.c +++ b/drivers/iio/pressure/st_pressure_core.c @@ -400,6 +400,7 @@ static const struct iio_info press_info = { .attrs = &st_press_attribute_group, .read_raw = &st_press_read_raw, .write_raw = &st_press_write_raw, + .debugfs_reg_access = &st_sensors_debugfs_reg_access, }; #ifdef CONFIG_IIO_TRIGGER diff --git a/include/linux/iio/common/st_sensors.h b/include/linux/iio/common/st_sensors.h index 3c17cd7..2fe939c 100644 --- a/include/linux/iio/common/st_sensors.h +++ b/include/linux/iio/common/st_sensors.h @@ -271,6 +271,10 @@ void st_sensors_power_enable(struct iio_dev *indio_dev); void st_sensors_power_disable(struct iio_dev *indio_dev); +int st_sensors_debugfs_reg_access(struct iio_dev *indio_dev, + unsigned reg, unsigned writeval, + unsigned *readval); + int st_sensors_set_odr(struct iio_dev *indio_dev, unsigned int odr); int st_sensors_set_dataready_irq(struct iio_dev *indio_dev, bool enable); -- cgit v1.1 From 672f93b6047cc724f002cf50a84f4e8155b86f12 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 11 Aug 2015 14:34:38 +0200 Subject: iio: event_monitor: report unsupported events This makes the event monitor bail out with a helpful error message if a device does not support events, as a related fix to iio core now makes it return -ENODEV properly. Signed-off-by: Linus Walleij Acked-by: Hartmut Knaack Signed-off-by: Jonathan Cameron --- tools/iio/iio_event_monitor.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tools/iio/iio_event_monitor.c b/tools/iio/iio_event_monitor.c index cd3fd41..d51eb04 100644 --- a/tools/iio/iio_event_monitor.c +++ b/tools/iio/iio_event_monitor.c @@ -284,7 +284,11 @@ int main(int argc, char **argv) ret = ioctl(fd, IIO_GET_EVENT_FD_IOCTL, &event_fd); if (ret == -1 || event_fd == -1) { ret = -errno; - fprintf(stderr, "Failed to retrieve event fd\n"); + if (ret == -ENODEV) + fprintf(stderr, + "This device does not support events\n"); + else + fprintf(stderr, "Failed to retrieve event fd\n"); if (close(fd) == -1) perror("Failed to close character device file"); -- cgit v1.1 From 53dabafe1b17411de82b6c4560145cf396cbb564 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 10 Aug 2015 10:55:08 +0200 Subject: iio: generic_buffer: be helpful about enabling channels Currently if generic_buffer is invoked without first enabling any channels in scan_elements/*_en, it will fail unable to enable the buffer because bytes_per_datum inside the kernel will be zero if no channels are available. It is implied that the user of the program should enable channels manually or with a script before executing generic_buffer. Be more helpful by stopping execution if no enabled channels can be found, and print a helptext that will tell you what is wrong and what needs to be done. Signed-off-by: Linus Walleij Acked-by: Hartmut Knaack Signed-off-by: Jonathan Cameron --- tools/iio/generic_buffer.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tools/iio/generic_buffer.c b/tools/iio/generic_buffer.c index 9f7b85b..01c4f67 100644 --- a/tools/iio/generic_buffer.c +++ b/tools/iio/generic_buffer.c @@ -328,6 +328,15 @@ int main(int argc, char **argv) "diag %s\n", dev_dir_name); goto error_free_triggername; } + if (!num_channels) { + fprintf(stderr, + "No channels are enabled, we have nothing to scan.\n"); + fprintf(stderr, "Enable channels manually in " + FORMAT_SCAN_ELEMENTS_DIR + "/*_en and try again.\n", dev_dir_name); + ret = -ENOENT; + goto error_free_triggername; + } /* * Construct the directory name for the associated buffer. -- cgit v1.1 From 3f9059b71791cf0e57a4879594f0066237976943 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 11 Aug 2015 11:56:40 +0200 Subject: iio: percolate error if event fd fails This makes the error from iio_event_getfd() percolate up to userspace properly so we can know for sure there is no events on this device (-ENODEV returned). Before this patch we would bail out looking for the unsupported events on the erroneous (negative) file descriptor. Signed-off-by: Linus Walleij Acked-by: Daniel Baluta Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index b3fcc2c..b347524 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -1153,6 +1153,8 @@ static long iio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) if (cmd == IIO_GET_EVENT_FD_IOCTL) { fd = iio_event_getfd(indio_dev); + if (fd < 0) + return fd; if (copy_to_user(ip, &fd, sizeof(fd))) return -EFAULT; return 0; -- cgit v1.1 From 70b2737e0a948552843ce1b8500e033080677086 Mon Sep 17 00:00:00 2001 From: Yong Li Date: Wed, 12 Aug 2015 21:25:46 +0800 Subject: staging: iio: hmc5843: Set iio name dynamically Load the driver using the below command: echo hmc5983 0x1e > /sys/bus/i2c/devices/i2c-?/new_device In sysfs, the iio name is hmc5843, however the i2c name is hmc5983, they are inconsistent. With this patch, the iio name will be the same as the i2c device name Signed-off-by: Yong Li Reviewed-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- drivers/staging/iio/magnetometer/hmc5843.h | 2 +- drivers/staging/iio/magnetometer/hmc5843_core.c | 4 ++-- drivers/staging/iio/magnetometer/hmc5843_i2c.c | 2 +- drivers/staging/iio/magnetometer/hmc5843_spi.c | 3 ++- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/staging/iio/magnetometer/hmc5843.h b/drivers/staging/iio/magnetometer/hmc5843.h index f3d0da2..06f35d3 100644 --- a/drivers/staging/iio/magnetometer/hmc5843.h +++ b/drivers/staging/iio/magnetometer/hmc5843.h @@ -48,7 +48,7 @@ struct hmc5843_data { }; int hmc5843_common_probe(struct device *dev, struct regmap *regmap, - enum hmc5843_ids id); + enum hmc5843_ids id, const char *name); int hmc5843_common_remove(struct device *dev); int hmc5843_common_suspend(struct device *dev); diff --git a/drivers/staging/iio/magnetometer/hmc5843_core.c b/drivers/staging/iio/magnetometer/hmc5843_core.c index fffca3a..4aab022 100644 --- a/drivers/staging/iio/magnetometer/hmc5843_core.c +++ b/drivers/staging/iio/magnetometer/hmc5843_core.c @@ -577,7 +577,7 @@ int hmc5843_common_resume(struct device *dev) EXPORT_SYMBOL(hmc5843_common_resume); int hmc5843_common_probe(struct device *dev, struct regmap *regmap, - enum hmc5843_ids id) + enum hmc5843_ids id, const char *name) { struct hmc5843_data *data; struct iio_dev *indio_dev; @@ -597,7 +597,7 @@ int hmc5843_common_probe(struct device *dev, struct regmap *regmap, mutex_init(&data->lock); indio_dev->dev.parent = dev; - indio_dev->name = dev->driver->name; + indio_dev->name = name; indio_dev->info = &hmc5843_info; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = data->variant->channels; diff --git a/drivers/staging/iio/magnetometer/hmc5843_i2c.c b/drivers/staging/iio/magnetometer/hmc5843_i2c.c index ff08667..3e06ceb 100644 --- a/drivers/staging/iio/magnetometer/hmc5843_i2c.c +++ b/drivers/staging/iio/magnetometer/hmc5843_i2c.c @@ -61,7 +61,7 @@ static int hmc5843_i2c_probe(struct i2c_client *cli, { return hmc5843_common_probe(&cli->dev, devm_regmap_init_i2c(cli, &hmc5843_i2c_regmap_config), - id->driver_data); + id->driver_data, id->name); } static int hmc5843_i2c_remove(struct i2c_client *client) diff --git a/drivers/staging/iio/magnetometer/hmc5843_spi.c b/drivers/staging/iio/magnetometer/hmc5843_spi.c index 8e658f7..8a80d01 100644 --- a/drivers/staging/iio/magnetometer/hmc5843_spi.c +++ b/drivers/staging/iio/magnetometer/hmc5843_spi.c @@ -59,6 +59,7 @@ static const struct regmap_config hmc5843_spi_regmap_config = { static int hmc5843_spi_probe(struct spi_device *spi) { int ret; + const struct spi_device_id *id = spi_get_device_id(spi); spi->mode = SPI_MODE_3; spi->max_speed_hz = 8000000; @@ -69,7 +70,7 @@ static int hmc5843_spi_probe(struct spi_device *spi) return hmc5843_common_probe(&spi->dev, devm_regmap_init_spi(spi, &hmc5843_spi_regmap_config), - HMC5983_ID); + id->driver_data, id->name); } static int hmc5843_spi_remove(struct spi_device *spi) -- cgit v1.1 From 70581e0ef8634074cc305f9e63e8abec08788753 Mon Sep 17 00:00:00 2001 From: Xander Huff Date: Tue, 11 Aug 2015 18:00:49 -0500 Subject: iio: adc: xilinx-xadc: Push interrupts into hardirq context The driver currently registers a pair of irq handlers using request_threaded_irq(), however the synchronization mechanism between the hardirq and the threadedirq handler is a regular spinlock. Unfortunately, this breaks PREEMPT_RT builds, where a spinlock can sleep, and is thus not able to be acquired from a hardirq handler. This patch gets rid of the threaded handler and pushes all interrupt handling into the hardirq context, and uses request_irq(). To validate that this change has no impact on RT performance, here are cyclictest values with no processes running: $ sudo cyclictest -S -m -p 98 policy: fifo: loadavg: 0.00 0.01 0.05 1/174 2539 T: 0 ( 1405) P:98 I:1000 C:167010520 Min: 9 Act: 12 Avg: 12 Max: 75 T: 1 ( 1862) P:98 I:1500 C:111340339 Min: 9 Act: 12 Avg: 12 Max: 73 Then, all xadc raw handles were accessed in a continuous loop via /sys/bus/iio/devices/iio:device0: $ sudo cyclictest -S -m -p 98 policy: fifo: loadavg: 7.84 7.70 7.63 3/182 4260 T: 0 ( 2559) P:98 I:1000 C:241557018 Min: 11 Act: 18 Avg: 21 Max: 74 T: 1 ( 2560) P:98 I:1500 C:161038006 Min: 10 Act: 21 Avg: 20 Max: 73 Signed-off-by: Xander Huff Acked-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- drivers/iio/adc/xilinx-xadc-core.c | 37 ++++++++++--------------------------- drivers/iio/adc/xilinx-xadc.h | 2 -- 2 files changed, 10 insertions(+), 29 deletions(-) diff --git a/drivers/iio/adc/xilinx-xadc-core.c b/drivers/iio/adc/xilinx-xadc-core.c index ce93bd8..0370624 100644 --- a/drivers/iio/adc/xilinx-xadc-core.c +++ b/drivers/iio/adc/xilinx-xadc-core.c @@ -273,33 +273,13 @@ static void xadc_zynq_unmask_worker(struct work_struct *work) schedule_delayed_work(&xadc->zynq_unmask_work, msecs_to_jiffies(XADC_ZYNQ_UNMASK_TIMEOUT)); } -} - -static irqreturn_t xadc_zynq_threaded_interrupt_handler(int irq, void *devid) -{ - struct iio_dev *indio_dev = devid; - struct xadc *xadc = iio_priv(indio_dev); - unsigned int alarm; - - spin_lock_irq(&xadc->lock); - alarm = xadc->zynq_alarm; - xadc->zynq_alarm = 0; - spin_unlock_irq(&xadc->lock); - - xadc_handle_events(indio_dev, xadc_zynq_transform_alarm(alarm)); - /* unmask the required interrupts in timer. */ - schedule_delayed_work(&xadc->zynq_unmask_work, - msecs_to_jiffies(XADC_ZYNQ_UNMASK_TIMEOUT)); - - return IRQ_HANDLED; } static irqreturn_t xadc_zynq_interrupt_handler(int irq, void *devid) { struct iio_dev *indio_dev = devid; struct xadc *xadc = iio_priv(indio_dev); - irqreturn_t ret = IRQ_HANDLED; uint32_t status; xadc_read_reg(xadc, XADC_ZYNQ_REG_INTSTS, &status); @@ -321,18 +301,23 @@ static irqreturn_t xadc_zynq_interrupt_handler(int irq, void *devid) status &= XADC_ZYNQ_INT_ALARM_MASK; if (status) { - xadc->zynq_alarm |= status; xadc->zynq_masked_alarm |= status; /* * mask the current event interrupt, * unmask it when the interrupt is no more active. */ xadc_zynq_update_intmsk(xadc, 0, 0); - ret = IRQ_WAKE_THREAD; + + xadc_handle_events(indio_dev, + xadc_zynq_transform_alarm(status)); + + /* unmask the required interrupts in timer. */ + schedule_delayed_work(&xadc->zynq_unmask_work, + msecs_to_jiffies(XADC_ZYNQ_UNMASK_TIMEOUT)); } spin_unlock(&xadc->lock); - return ret; + return IRQ_HANDLED; } #define XADC_ZYNQ_TCK_RATE_MAX 50000000 @@ -437,7 +422,6 @@ static const struct xadc_ops xadc_zynq_ops = { .setup = xadc_zynq_setup, .get_dclk_rate = xadc_zynq_get_dclk_rate, .interrupt_handler = xadc_zynq_interrupt_handler, - .threaded_interrupt_handler = xadc_zynq_threaded_interrupt_handler, .update_alarm = xadc_zynq_update_alarm, }; @@ -1225,9 +1209,8 @@ static int xadc_probe(struct platform_device *pdev) if (ret) goto err_free_samplerate_trigger; - ret = request_threaded_irq(irq, xadc->ops->interrupt_handler, - xadc->ops->threaded_interrupt_handler, - 0, dev_name(&pdev->dev), indio_dev); + ret = request_irq(irq, xadc->ops->interrupt_handler, 0, + dev_name(&pdev->dev), indio_dev); if (ret) goto err_clk_disable_unprepare; diff --git a/drivers/iio/adc/xilinx-xadc.h b/drivers/iio/adc/xilinx-xadc.h index 54adc50..f6f0819 100644 --- a/drivers/iio/adc/xilinx-xadc.h +++ b/drivers/iio/adc/xilinx-xadc.h @@ -60,7 +60,6 @@ struct xadc { enum xadc_external_mux_mode external_mux_mode; - unsigned int zynq_alarm; unsigned int zynq_masked_alarm; unsigned int zynq_intmask; struct delayed_work zynq_unmask_work; @@ -79,7 +78,6 @@ struct xadc_ops { void (*update_alarm)(struct xadc *, unsigned int); unsigned long (*get_dclk_rate)(struct xadc *); irqreturn_t (*interrupt_handler)(int, void *); - irqreturn_t (*threaded_interrupt_handler)(int, void *); unsigned int flags; }; -- cgit v1.1 From 8463f6fb78e0798817785e03860f8b1fc4e1b2e8 Mon Sep 17 00:00:00 2001 From: Cristina Opriceana Date: Tue, 11 Aug 2015 13:18:18 +0300 Subject: Staging: iio: trigger: Alignment should match open parenthesis Fix alignment for function parameters as suggested by checkpatch.pl. Signed-off-by: Cristina Opriceana Signed-off-by: Jonathan Cameron --- drivers/staging/iio/trigger/iio-trig-bfin-timer.c | 7 ++++--- drivers/staging/iio/trigger/iio-trig-periodic-rtc.c | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/staging/iio/trigger/iio-trig-bfin-timer.c b/drivers/staging/iio/trigger/iio-trig-bfin-timer.c index 3c1c8c6..9fe48ef 100644 --- a/drivers/staging/iio/trigger/iio-trig-bfin-timer.c +++ b/drivers/staging/iio/trigger/iio-trig-bfin-timer.c @@ -79,7 +79,8 @@ static int iio_bfin_tmr_set_state(struct iio_trigger *trig, bool state) } static ssize_t iio_bfin_tmr_frequency_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) + struct device_attribute *attr, + const char *buf, size_t count) { struct iio_trigger *trig = to_iio_trigger(dev); struct bfin_tmr_state *st = iio_trigger_get_drvdata(trig); @@ -116,8 +117,8 @@ static ssize_t iio_bfin_tmr_frequency_store(struct device *dev, } static ssize_t iio_bfin_tmr_frequency_show(struct device *dev, - struct device_attribute *attr, - char *buf) + struct device_attribute *attr, + char *buf) { struct iio_trigger *trig = to_iio_trigger(dev); struct bfin_tmr_state *st = iio_trigger_get_drvdata(trig); diff --git a/drivers/staging/iio/trigger/iio-trig-periodic-rtc.c b/drivers/staging/iio/trigger/iio-trig-periodic-rtc.c index 0c1976d..a2a42c2 100644 --- a/drivers/staging/iio/trigger/iio-trig-periodic-rtc.c +++ b/drivers/staging/iio/trigger/iio-trig-periodic-rtc.c @@ -37,7 +37,7 @@ static int iio_trig_periodic_rtc_set_state(struct iio_trigger *trig, bool state) if (trig_info->frequency == 0 && state) return -EINVAL; dev_dbg(&trig_info->rtc->dev, "trigger frequency is %u\n", - trig_info->frequency); + trig_info->frequency); ret = rtc_irq_set_state(trig_info->rtc, &trig_info->task, state); if (ret == 0) trig_info->state = state; -- cgit v1.1 From a6d748e3ad927f6f2b93c9747d78da72c83141b6 Mon Sep 17 00:00:00 2001 From: Cristina Opriceana Date: Tue, 11 Aug 2015 13:20:53 +0300 Subject: Staging: iio: trigger: Use braces on both branches of if statement Fix style issue related to missing braces, detected by checkpatch.pl. Signed-off-by: Cristina Opriceana Signed-off-by: Jonathan Cameron --- drivers/staging/iio/trigger/iio-trig-periodic-rtc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/staging/iio/trigger/iio-trig-periodic-rtc.c b/drivers/staging/iio/trigger/iio-trig-periodic-rtc.c index a2a42c2..2db8857 100644 --- a/drivers/staging/iio/trigger/iio-trig-periodic-rtc.c +++ b/drivers/staging/iio/trigger/iio-trig-periodic-rtc.c @@ -74,8 +74,9 @@ static ssize_t iio_trig_periodic_write_freq(struct device *dev, if (ret == 0 && trig_info->state && trig_info->frequency == 0) ret = rtc_irq_set_state(trig_info->rtc, &trig_info->task, 1); - } else + } else { ret = rtc_irq_set_state(trig_info->rtc, &trig_info->task, 0); + } if (ret) goto error_ret; -- cgit v1.1 From 45ef12b6ad6ade7bb8c89db3d3101e99d9552f3a Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Thu, 20 Aug 2015 09:07:15 +0200 Subject: staging: iio: hmc5843: Export missing SPI module alias information The SPI core always reports the MODALIAS uevent as "spi:" regardless of the mechanism that was used to register the device (i.e: OF or board code) and the table that is used later to match the driver with the device (i.e: SPI id table or OF match table). So drivers needs to export the SPI id table and this be built into the module or udev won't have the necessary information to autoload the needed driver module when the device is added. Signed-off-by: Javier Martinez Canillas Signed-off-by: Jonathan Cameron --- drivers/staging/iio/magnetometer/hmc5843_spi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/iio/magnetometer/hmc5843_spi.c b/drivers/staging/iio/magnetometer/hmc5843_spi.c index 8a80d01..1549192 100644 --- a/drivers/staging/iio/magnetometer/hmc5843_spi.c +++ b/drivers/staging/iio/magnetometer/hmc5843_spi.c @@ -82,6 +82,7 @@ static const struct spi_device_id hmc5843_id[] = { { "hmc5983", HMC5983_ID }, { } }; +MODULE_DEVICE_TABLE(spi, hmc5843_id); static struct spi_driver hmc5843_driver = { .driver = { -- cgit v1.1 From d1b895fedae30e2e948dbae4d209509c44564074 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Thu, 20 Aug 2015 09:07:24 +0200 Subject: iio: adc: max1027: Set struct spi_driver .of_match_table The driver has an OF id table but the .of_match_table is not set so the SPI core can't do an OF style match and the table was unused. Signed-off-by: Javier Martinez Canillas Signed-off-by: Jonathan Cameron --- drivers/iio/adc/max1027.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iio/adc/max1027.c b/drivers/iio/adc/max1027.c index 44bf815..54a8302 100644 --- a/drivers/iio/adc/max1027.c +++ b/drivers/iio/adc/max1027.c @@ -508,6 +508,7 @@ static int max1027_remove(struct spi_device *spi) static struct spi_driver max1027_driver = { .driver = { .name = "max1027", + .of_match_table = of_match_ptr(max1027_adc_dt_ids), .owner = THIS_MODULE, }, .probe = max1027_probe, -- cgit v1.1 From 8b7c826d03721ed1d96bd87f138e59dcf80f54e7 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Thu, 20 Aug 2015 09:07:27 +0200 Subject: iio: as3935: Add OF match table The Documentation/devicetree/bindings/iio/proximity/as3935.txt DT binding doc lists "ams,as3935" as a compatible string but the corresponding driver does not have an OF match table. Add the table to the driver so the SPI core can do an OF style match. Signed-off-by: Javier Martinez Canillas Signed-off-by: Jonathan Cameron --- drivers/iio/proximity/as3935.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/iio/proximity/as3935.c b/drivers/iio/proximity/as3935.c index bc0d68e..e950351 100644 --- a/drivers/iio/proximity/as3935.c +++ b/drivers/iio/proximity/as3935.c @@ -434,6 +434,12 @@ static int as3935_remove(struct spi_device *spi) return 0; } +static const struct of_device_id as3935_of_match[] = { + { .compatible = "ams,as3935", }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, as3935_of_match); + static const struct spi_device_id as3935_id[] = { {"as3935", 0}, {}, @@ -443,6 +449,7 @@ MODULE_DEVICE_TABLE(spi, as3935_id); static struct spi_driver as3935_driver = { .driver = { .name = "as3935", + .of_match_table = of_match_ptr(as3935_of_match), .owner = THIS_MODULE, .pm = AS3935_PM_OPS, }, -- cgit v1.1 From 9e611c9e5a20c28f426271a0c2c962795b699069 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Thu, 20 Aug 2015 09:07:28 +0200 Subject: iio: adc128s052: Add OF match table The Documentation/devicetree/bindings/iio/adc/ti-adc128s052.txt DT binding doc lists "ti,adc128s052" or "ti,adc122s021" as compatible strings but the corresponding driver does not have an OF match table. Add the table to the driver so the SPI core can do an OF style match. Signed-off-by: Javier Martinez Canillas Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-adc128s052.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/iio/adc/ti-adc128s052.c b/drivers/iio/adc/ti-adc128s052.c index 915be6b..98c0d2b 100644 --- a/drivers/iio/adc/ti-adc128s052.c +++ b/drivers/iio/adc/ti-adc128s052.c @@ -174,6 +174,13 @@ static int adc128_remove(struct spi_device *spi) return 0; } +static const struct of_device_id adc128_of_match[] = { + { .compatible = "ti,adc128s052", }, + { .compatible = "ti,adc122s021", }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, adc128_of_match); + static const struct spi_device_id adc128_id[] = { { "adc128s052", 0}, /* index into adc128_config */ { "adc122s021", 1}, @@ -184,6 +191,7 @@ MODULE_DEVICE_TABLE(spi, adc128_id); static struct spi_driver adc128_driver = { .driver = { .name = "adc128s052", + .of_match_table = of_match_ptr(adc128_of_match), .owner = THIS_MODULE, }, .probe = adc128_probe, -- cgit v1.1 From 9c68be3ecc74e1ccc085eab6f0cdbaff3093fba2 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Thu, 20 Aug 2015 09:07:29 +0200 Subject: iio: frequency: adf4350: Add OF match table The Documentation/devicetree/bindings/iio/frequency/adf4350.txt DT binding doc lists "adi,adf4350" or "adi,adf4351" as compatible strings but the corresponding driver does not have an OF match table. Add the table to the driver so the SPI core can do an OF style match. Signed-off-by: Javier Martinez Canillas Acked-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- drivers/iio/frequency/adf4350.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/iio/frequency/adf4350.c b/drivers/iio/frequency/adf4350.c index 9890c81..9a6ef3c 100644 --- a/drivers/iio/frequency/adf4350.c +++ b/drivers/iio/frequency/adf4350.c @@ -616,6 +616,13 @@ static int adf4350_remove(struct spi_device *spi) return 0; } +static const struct of_device_id adf4350_of_match[] = { + { .compatible = "adi,adf4350", }, + { .compatible = "adi,adf4351", }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, adf4350_of_match); + static const struct spi_device_id adf4350_id[] = { {"adf4350", 4350}, {"adf4351", 4351}, @@ -625,6 +632,7 @@ static const struct spi_device_id adf4350_id[] = { static struct spi_driver adf4350_driver = { .driver = { .name = "adf4350", + .of_match_table = of_match_ptr(adf4350_of_match), .owner = THIS_MODULE, }, .probe = adf4350_probe, -- cgit v1.1 From 1c00dcd31d8e71670180ff938f05eb9aef49c84e Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Thu, 20 Aug 2015 09:07:23 +0200 Subject: iio: dac: ad7303: Add OF match table The Documentation/devicetree/bindings/iio/dac/ad7303.txt DT binding doc lists "adi,ad7303" as a compatible string but the corresponding driver does not have an OF match table. Add the table to the driver so the SPI core can do an OF style match. Signed-off-by: Javier Martinez Canillas Acked-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad7303.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/iio/dac/ad7303.c b/drivers/iio/dac/ad7303.c index fa28100..18a4ad5 100644 --- a/drivers/iio/dac/ad7303.c +++ b/drivers/iio/dac/ad7303.c @@ -281,6 +281,12 @@ static int ad7303_remove(struct spi_device *spi) return 0; } +static const struct of_device_id ad7303_spi_of_match[] = { + { .compatible = "adi,ad7303", }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, ad7303_spi_of_match); + static const struct spi_device_id ad7303_spi_ids[] = { { "ad7303", 0 }, {} @@ -290,6 +296,7 @@ MODULE_DEVICE_TABLE(spi, ad7303_spi_ids); static struct spi_driver ad7303_driver = { .driver = { .name = "ad7303", + .of_match_table = of_match_ptr(ad7303_spi_of_match), .owner = THIS_MODULE, }, .probe = ad7303_probe, -- cgit v1.1 From ed199a11bdfd2957e46984b77a1e1f0927dc9fa0 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Thu, 20 Aug 2015 09:07:14 +0200 Subject: iio: Export SPI module alias information in missing drivers The SPI core always reports the MODALIAS uevent as "spi:" regardless of the mechanism that was used to register the device (i.e: OF or board code) and the table that is used later to match the driver with the device (i.e: SPI id table or OF match table). So drivers needs to export the SPI id table and this be built into the module or udev won't have the necessary information to autoload the needed driver module when the device is added. Signed-off-by: Javier Martinez Canillas Acked-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- drivers/iio/amplifiers/ad8366.c | 1 + drivers/iio/frequency/adf4350.c | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/iio/amplifiers/ad8366.c b/drivers/iio/amplifiers/ad8366.c index c0d364e..32b82a2 100644 --- a/drivers/iio/amplifiers/ad8366.c +++ b/drivers/iio/amplifiers/ad8366.c @@ -195,6 +195,7 @@ static const struct spi_device_id ad8366_id[] = { {"ad8366", 0}, {} }; +MODULE_DEVICE_TABLE(spi, ad8366_id); static struct spi_driver ad8366_driver = { .driver = { diff --git a/drivers/iio/frequency/adf4350.c b/drivers/iio/frequency/adf4350.c index 9a6ef3c..73f27e0 100644 --- a/drivers/iio/frequency/adf4350.c +++ b/drivers/iio/frequency/adf4350.c @@ -628,6 +628,7 @@ static const struct spi_device_id adf4350_id[] = { {"adf4351", 4351}, {} }; +MODULE_DEVICE_TABLE(spi, adf4350_id); static struct spi_driver adf4350_driver = { .driver = { -- cgit v1.1 From ab6ff6c6ca1b4739b2af07501bc333b85d7381d8 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Thu, 20 Aug 2015 09:07:26 +0200 Subject: iio: adc: mcp320x: Set struct spi_driver .of_match_table The driver has an OF id table but the .of_match_table is not set so the SPI core can't do an OF style match and the table was unused. Signed-off-by: Javier Martinez Canillas Acked-by: Michael Welling Signed-off-by: Jonathan Cameron --- drivers/iio/adc/mcp320x.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iio/adc/mcp320x.c b/drivers/iio/adc/mcp320x.c index b19e4f9..41a21e9 100644 --- a/drivers/iio/adc/mcp320x.c +++ b/drivers/iio/adc/mcp320x.c @@ -404,6 +404,7 @@ MODULE_DEVICE_TABLE(spi, mcp320x_id); static struct spi_driver mcp320x_driver = { .driver = { .name = "mcp320x", + .of_match_table = of_match_ptr(mcp320x_dt_ids), .owner = THIS_MODULE, }, .probe = mcp320x_probe, -- cgit v1.1 From ecf7e207a55d8760734a9de5fba1a974628b92e5 Mon Sep 17 00:00:00 2001 From: Nicola Corna Date: Sun, 23 Aug 2015 23:06:19 +0200 Subject: iio: humidity: si7020: replaced bitmask on humidity values with range check The maximum possible value for the relative humidity is 55575 (100%RH). This value, if shifted right by 2 bits, uses 14 bits and masking it with a 12 bit mask removes 2 meaningful bits. The masking has been replaced with a range check that sets the minimum value at 786 (0%RH) and the maximum at 13893 (99.998%RH). Signed-off-by: Nicola Corna Reviewed-by: Hartmut Knaack Signed-off-by: Jonathan Cameron --- drivers/iio/humidity/si7020.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/iio/humidity/si7020.c b/drivers/iio/humidity/si7020.c index fa3b809..12128d1 100644 --- a/drivers/iio/humidity/si7020.c +++ b/drivers/iio/humidity/si7020.c @@ -57,8 +57,12 @@ static int si7020_read_raw(struct iio_dev *indio_dev, if (ret < 0) return ret; *val = ret >> 2; + /* + * Humidity values can slightly exceed the 0-100%RH + * range and should be corrected by software + */ if (chan->type == IIO_HUMIDITYRELATIVE) - *val &= GENMASK(11, 0); + *val = clamp_val(*val, 786, 13893); return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: if (chan->type == IIO_TEMP) -- cgit v1.1 From 764589b688a1b087b5ff6c1e99f7cd57a5241395 Mon Sep 17 00:00:00 2001 From: Crt Mori Date: Mon, 17 Aug 2015 19:34:33 +0200 Subject: iio: mlx90614: Implement filter configuration Implemented Low pass 3db frequency filter which configures FIR and IIR values within the configuration register of EEPROM. For more standardized interface we have fixed the FIR value to 1024, while changes in IIR value are directly connected to filter responses. The new datasheet version will provide a simplified table (also in reStructured text format below) with this change, to provide quick overview of possible settings. Below sensor timings (bandwidth) are calculated for 3db frequency low pass filter. +--------------------+-----------------+ | Filter setting (%) | Band width (Hz) | | (rounded to 1.0) | | +====================+=================+ | 13 | 0.15 | +--------------------+-----------------+ | 17 | 0.20 | +--------------------+-----------------+ | 25 | 0.31 | +--------------------+-----------------+ | 50 | 0.77 | +--------------------+-----------------+ | 57 | 0.86 | +--------------------+-----------------+ | 67 | 1.10 | +--------------------+-----------------+ | 80 | 1.53 | +--------------------+-----------------+ | 100 | 7.23 | +--------------------+-----------------+ The diff is made towards togreg branch. Added myself to MAINTAINERS and authors as per discussion with Jonathan. Signed-off-by: Crt Mori Signed-off-by: Jonathan Cameron --- MAINTAINERS | 7 +++ drivers/iio/temperature/mlx90614.c | 90 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 94 insertions(+), 3 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index fb7d3b6..d8bf36d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6757,6 +6757,13 @@ S: Supported F: include/linux/mlx5/ F: drivers/infiniband/hw/mlx5/ +MELEXIS MLX90614 DRIVER +M: Crt Mori +L: linux-iio@vger.kernel.org +W: http://www.melexis.com +S: Supported +F: drivers/iio/temperature/mlx90614.c + MN88472 MEDIA DRIVER M: Antti Palosaari L: linux-media@vger.kernel.org diff --git a/drivers/iio/temperature/mlx90614.c b/drivers/iio/temperature/mlx90614.c index 5d033a5..3fd3ba4 100644 --- a/drivers/iio/temperature/mlx90614.c +++ b/drivers/iio/temperature/mlx90614.c @@ -3,6 +3,7 @@ * * Copyright (c) 2014 Peter Meerwald * Copyright (c) 2015 Essensium NV + * Copyright (c) 2015 Melexis * * This file is subject to the terms and conditions of version 2 of * the GNU General Public License. See the file COPYING in the main @@ -20,7 +21,6 @@ * always has a pull-up so we do not need an extra GPIO to drive it high. If * the "wakeup" GPIO is not given, power management will be disabled. * - * TODO: filter configuration */ #include @@ -32,6 +32,7 @@ #include #include +#include #define MLX90614_OP_RAM 0x00 #define MLX90614_OP_EEPROM 0x20 @@ -79,6 +80,20 @@ struct mlx90614_data { unsigned long ready_timestamp; /* in jiffies */ }; +/* Bandwidth values for IIR filtering */ +static const int mlx90614_iir_values[] = {77, 31, 20, 15, 723, 153, 110, 86}; +static IIO_CONST_ATTR(in_temp_object_filter_low_pass_3db_frequency_available, + "0.15 0.20 0.31 0.77 0.86 1.10 1.53 7.23"); + +static struct attribute *mlx90614_attributes[] = { + &iio_const_attr_in_temp_object_filter_low_pass_3db_frequency_available.dev_attr.attr, + NULL, +}; + +static const struct attribute_group mlx90614_attr_group = { + .attrs = mlx90614_attributes, +}; + /* * Erase an address and write word. * The mutex must be locked before calling. @@ -117,6 +132,42 @@ static s32 mlx90614_write_word(const struct i2c_client *client, u8 command, return ret; } +/* + * Find the IIR value inside mlx90614_iir_values array and return its position + * which is equivalent to the bit value in sensor register + */ +static inline s32 mlx90614_iir_search(const struct i2c_client *client, + int value) +{ + int i; + s32 ret; + + for (i = 0; i < ARRAY_SIZE(mlx90614_iir_values); ++i) { + if (value == mlx90614_iir_values[i]) + break; + } + + if (i == ARRAY_SIZE(mlx90614_iir_values)) + return -EINVAL; + + /* + * CONFIG register values must not be changed so + * we must read them before we actually write + * changes + */ + ret = i2c_smbus_read_word_data(client, MLX90614_CONFIG); + if (ret > 0) + return ret; + + /* Write changed values */ + ret = mlx90614_write_word(client, MLX90614_CONFIG, + (i << MLX90614_CONFIG_IIR_SHIFT) | + (((u16) ((0x7 << MLX90614_CONFIG_FIR_SHIFT) | + ((u16) ret & (~((u16) MLX90614_CONFIG_FIR_MASK))))) & + (~(u16) MLX90614_CONFIG_IIR_MASK))); + return ret; +} + #ifdef CONFIG_PM /* * If @startup is true, make sure MLX90614_TIMING_STARTUP ms have elapsed since @@ -236,6 +287,21 @@ static int mlx90614_read_raw(struct iio_dev *indio_dev, *val2 = ret * MLX90614_CONST_EMISSIVITY_RESOLUTION; } return IIO_VAL_INT_PLUS_NANO; + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: /* IIR setting with + FIR = 1024 */ + mlx90614_power_get(data, false); + mutex_lock(&data->lock); + ret = i2c_smbus_read_word_data(data->client, MLX90614_CONFIG); + mutex_unlock(&data->lock); + mlx90614_power_put(data); + + if (ret < 0) + return ret; + + *val = mlx90614_iir_values[ret & MLX90614_CONFIG_IIR_MASK] / 100; + *val2 = (mlx90614_iir_values[ret & MLX90614_CONFIG_IIR_MASK] % 100) * + 10000; + return IIO_VAL_INT_PLUS_MICRO; default: return -EINVAL; } @@ -263,6 +329,18 @@ static int mlx90614_write_raw(struct iio_dev *indio_dev, mlx90614_power_put(data); return ret; + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: /* IIR Filter setting */ + if (val < 0 || val2 < 0) + return -EINVAL; + + mlx90614_power_get(data, false); + mutex_lock(&data->lock); + ret = mlx90614_iir_search(data->client, + val * 100 + val2 / 10000); + mutex_unlock(&data->lock); + mlx90614_power_put(data); + + return ret; default: return -EINVAL; } @@ -275,6 +353,8 @@ static int mlx90614_write_raw_get_fmt(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_CALIBEMISSIVITY: return IIO_VAL_INT_PLUS_NANO; + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: + return IIO_VAL_INT_PLUS_MICRO; default: return -EINVAL; } @@ -294,7 +374,8 @@ static const struct iio_chan_spec mlx90614_channels[] = { .modified = 1, .channel2 = IIO_MOD_TEMP_OBJECT, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | - BIT(IIO_CHAN_INFO_CALIBEMISSIVITY), + BIT(IIO_CHAN_INFO_CALIBEMISSIVITY) | + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_SCALE), }, @@ -305,7 +386,8 @@ static const struct iio_chan_spec mlx90614_channels[] = { .channel = 1, .channel2 = IIO_MOD_TEMP_OBJECT, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | - BIT(IIO_CHAN_INFO_CALIBEMISSIVITY), + BIT(IIO_CHAN_INFO_CALIBEMISSIVITY) | + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_SCALE), }, @@ -315,6 +397,7 @@ static const struct iio_info mlx90614_info = { .read_raw = mlx90614_read_raw, .write_raw = mlx90614_write_raw, .write_raw_get_fmt = mlx90614_write_raw_get_fmt, + .attrs = &mlx90614_attr_group, .driver_module = THIS_MODULE, }; @@ -569,5 +652,6 @@ module_i2c_driver(mlx90614_driver); MODULE_AUTHOR("Peter Meerwald "); MODULE_AUTHOR("Vianney le Clément de Saint-Marcq "); +MODULE_AUTHOR("Crt Mori "); MODULE_DESCRIPTION("Melexis MLX90614 contactless IR temperature sensor driver"); MODULE_LICENSE("GPL"); -- cgit v1.1 From 735ad074ffa72ccc4fdba8e54eb024df95545e7d Mon Sep 17 00:00:00 2001 From: Vladimir Barinov Date: Thu, 20 Aug 2015 22:37:39 +0300 Subject: iio: Support triggered events Support triggered events. This is useful for chips that don't have their own interrupt sources. It allows to use generic/standalone iio triggers for those drivers. Signed-off-by: Vladimir Barinov Signed-off-by: Jonathan Cameron --- drivers/iio/Kconfig | 6 +++ drivers/iio/Makefile | 2 + drivers/iio/industrialio-core.c | 4 +- drivers/iio/industrialio-trigger.c | 12 +++++- drivers/iio/industrialio-triggered-event.c | 68 ++++++++++++++++++++++++++++++ include/linux/iio/iio.h | 3 ++ include/linux/iio/triggered_event.h | 11 +++++ 7 files changed, 102 insertions(+), 4 deletions(-) create mode 100644 drivers/iio/industrialio-triggered-event.c create mode 100644 include/linux/iio/triggered_event.h diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig index 3c6c6e2..6fe0d65 100644 --- a/drivers/iio/Kconfig +++ b/drivers/iio/Kconfig @@ -38,6 +38,12 @@ config IIO_CONSUMERS_PER_TRIGGER This value controls the maximum number of consumers that a given trigger may handle. Default is 2. +config IIO_TRIGGERED_EVENT + tristate + select IIO_TRIGGER + help + Provides helper functions for setting up triggered events. + source "drivers/iio/accel/Kconfig" source "drivers/iio/adc/Kconfig" source "drivers/iio/amplifiers/Kconfig" diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile index 7ddb988..40995f3 100644 --- a/drivers/iio/Makefile +++ b/drivers/iio/Makefile @@ -7,6 +7,8 @@ industrialio-y := industrialio-core.o industrialio-event.o inkern.o industrialio-$(CONFIG_IIO_BUFFER) += industrialio-buffer.o industrialio-$(CONFIG_IIO_TRIGGER) += industrialio-trigger.o +obj-$(CONFIG_IIO_TRIGGERED_EVENT) += industrialio-triggered-event.o + obj-y += accel/ obj-y += adc/ obj-y += amplifiers/ diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index b347524..bef690e 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -962,7 +962,7 @@ static void iio_device_unregister_sysfs(struct iio_dev *indio_dev) static void iio_dev_release(struct device *device) { struct iio_dev *indio_dev = dev_to_iio_dev(device); - if (indio_dev->modes & INDIO_BUFFER_TRIGGERED) + if (indio_dev->modes & (INDIO_BUFFER_TRIGGERED | INDIO_EVENT_TRIGGERED)) iio_device_unregister_trigger_consumer(indio_dev); iio_device_unregister_eventset(indio_dev); iio_device_unregister_sysfs(indio_dev); @@ -1243,7 +1243,7 @@ int iio_device_register(struct iio_dev *indio_dev) "Failed to register event set\n"); goto error_free_sysfs; } - if (indio_dev->modes & INDIO_BUFFER_TRIGGERED) + if (indio_dev->modes & (INDIO_BUFFER_TRIGGERED | INDIO_EVENT_TRIGGERED)) iio_device_register_trigger_consumer(indio_dev); if ((indio_dev->modes & INDIO_ALL_BUFFER_MODES) && diff --git a/drivers/iio/industrialio-trigger.c b/drivers/iio/industrialio-trigger.c index 570606c..ae2806a 100644 --- a/drivers/iio/industrialio-trigger.c +++ b/drivers/iio/industrialio-trigger.c @@ -366,10 +366,18 @@ static ssize_t iio_trigger_write_current(struct device *dev, indio_dev->trig = trig; - if (oldtrig) + if (oldtrig) { + if (indio_dev->modes & INDIO_EVENT_TRIGGERED) + iio_trigger_detach_poll_func(oldtrig, + indio_dev->pollfunc_event); iio_trigger_put(oldtrig); - if (indio_dev->trig) + } + if (indio_dev->trig) { iio_trigger_get(indio_dev->trig); + if (indio_dev->modes & INDIO_EVENT_TRIGGERED) + iio_trigger_attach_poll_func(indio_dev->trig, + indio_dev->pollfunc_event); + } return len; } diff --git a/drivers/iio/industrialio-triggered-event.c b/drivers/iio/industrialio-triggered-event.c new file mode 100644 index 0000000..8cc254f --- /dev/null +++ b/drivers/iio/industrialio-triggered-event.c @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2015 Cogent Embedded, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include + +/** + * iio_triggered_event_setup() - Setup pollfunc_event for triggered event + * @indio_dev: IIO device structure + * @h: Function which will be used as pollfunc_event top half + * @thread: Function which will be used as pollfunc_event bottom half + * + * This function combines some common tasks which will normally be performed + * when setting up a triggered event. It will allocate the pollfunc_event and + * set mode to use it for triggered event. + * + * Before calling this function the indio_dev structure should already be + * completely initialized, but not yet registered. In practice this means that + * this function should be called right before iio_device_register(). + * + * To free the resources allocated by this function call + * iio_triggered_event_cleanup(). + */ +int iio_triggered_event_setup(struct iio_dev *indio_dev, + irqreturn_t (*h)(int irq, void *p), + irqreturn_t (*thread)(int irq, void *p)) +{ + indio_dev->pollfunc_event = iio_alloc_pollfunc(h, + thread, + IRQF_ONESHOT, + indio_dev, + "%s_consumer%d", + indio_dev->name, + indio_dev->id); + if (indio_dev->pollfunc_event == NULL) + return -ENOMEM; + + /* Flag that events polling is possible */ + indio_dev->modes |= INDIO_EVENT_TRIGGERED; + + return 0; +} +EXPORT_SYMBOL(iio_triggered_event_setup); + +/** + * iio_triggered_event_cleanup() - Free resources allocated by iio_triggered_event_setup() + * @indio_dev: IIO device structure + */ +void iio_triggered_event_cleanup(struct iio_dev *indio_dev) +{ + indio_dev->modes &= ~INDIO_EVENT_TRIGGERED; + iio_dealloc_pollfunc(indio_dev->pollfunc_event); +} +EXPORT_SYMBOL(iio_triggered_event_cleanup); + +MODULE_AUTHOR("Vladimir Barinov"); +MODULE_DESCRIPTION("IIO helper functions for setting up triggered events"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index 7bb7f67..19c94c9 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -294,6 +294,7 @@ static inline s64 iio_get_time_ns(void) #define INDIO_BUFFER_TRIGGERED 0x02 #define INDIO_BUFFER_SOFTWARE 0x04 #define INDIO_BUFFER_HARDWARE 0x08 +#define INDIO_EVENT_TRIGGERED 0x10 #define INDIO_ALL_BUFFER_MODES \ (INDIO_BUFFER_TRIGGERED | INDIO_BUFFER_HARDWARE | INDIO_BUFFER_SOFTWARE) @@ -457,6 +458,7 @@ struct iio_buffer_setup_ops { * @scan_index_timestamp:[INTERN] cache of the index to the timestamp * @trig: [INTERN] current device trigger (buffer modes) * @pollfunc: [DRIVER] function run on trigger being received + * @pollfunc_event: [DRIVER] function run on events trigger being received * @channels: [DRIVER] channel specification structure table * @num_channels: [DRIVER] number of channels specified in @channels. * @channel_attr_list: [INTERN] keep track of automatically created channel @@ -495,6 +497,7 @@ struct iio_dev { unsigned scan_index_timestamp; struct iio_trigger *trig; struct iio_poll_func *pollfunc; + struct iio_poll_func *pollfunc_event; struct iio_chan_spec const *channels; int num_channels; diff --git a/include/linux/iio/triggered_event.h b/include/linux/iio/triggered_event.h new file mode 100644 index 0000000..8fe8537 --- /dev/null +++ b/include/linux/iio/triggered_event.h @@ -0,0 +1,11 @@ +#ifndef _LINUX_IIO_TRIGGERED_EVENT_H_ +#define _LINUX_IIO_TRIGGERED_EVENT_H_ + +#include + +int iio_triggered_event_setup(struct iio_dev *indio_dev, + irqreturn_t (*h)(int irq, void *p), + irqreturn_t (*thread)(int irq, void *p)); +void iio_triggered_event_cleanup(struct iio_dev *indio_dev); + +#endif -- cgit v1.1 From 3f4642fc06a51e203b01cf29c418d0833761c4d3 Mon Sep 17 00:00:00 2001 From: Vladimir Barinov Date: Thu, 20 Aug 2015 22:38:08 +0300 Subject: dt: Add vendor prefix 'holt' Add Holt Integrated Circuits, Inc. to the list of device tree vendor prefixes Signed-off-by: Vladimir Barinov Acked-by: Rob Herring Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/vendor-prefixes.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index d444757..bc64cc9 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -99,6 +99,7 @@ himax Himax Technologies, Inc. hisilicon Hisilicon Limited. hit Hitachi Ltd. hitex Hitex Development Tools +holt Holt Integrated Circuits, Inc. honeywell Honeywell hp Hewlett Packard i2se I2SE GmbH -- cgit v1.1 From a8dbc64a2619b82ec0106f2c54174a94d74da620 Mon Sep 17 00:00:00 2001 From: kbuild test robot Date: Sun, 30 Aug 2015 16:46:57 +0800 Subject: iio: light: fix platform_no_drv_owner.cocci warnings drivers/iio/light/opt3001.c:796:3-8: No need to set .owner here. The core will do it. Remove .owner field if calls are used which set it automatically Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci CC: Andreas Dannenberg Signed-off-by: Fengguang Wu Signed-off-by: Jonathan Cameron --- drivers/iio/light/opt3001.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/iio/light/opt3001.c b/drivers/iio/light/opt3001.c index 923aa6a..01e111e 100644 --- a/drivers/iio/light/opt3001.c +++ b/drivers/iio/light/opt3001.c @@ -793,7 +793,6 @@ static struct i2c_driver opt3001_driver = { .driver = { .name = "opt3001", .of_match_table = of_match_ptr(opt3001_of_match), - .owner = THIS_MODULE, }, }; -- cgit v1.1 From f6707ef7a3c9196faee1841f4d12e990239f5dae Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Sun, 30 Aug 2015 16:12:57 +0800 Subject: staging: iio: adc: lpc32xx: use correct reutrn value To lpc32xx_adc driver, when platform_get_resource or platform_get_irq failed, we should use -ENXIO as a return value, but not -EBUSY. Signed-off-by: Peng Fan Cc: Jonathan Cameron Cc: Hartmut Knaack Cc: Lars-Peter Clausen Cc: Peter Meerwald Cc: Greg Kroah-Hartman Cc: Tapasweni Pathak Signed-off-by: Jonathan Cameron --- drivers/staging/iio/adc/lpc32xx_adc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/iio/adc/lpc32xx_adc.c b/drivers/staging/iio/adc/lpc32xx_adc.c index 5331c44..bcf4ebb 100644 --- a/drivers/staging/iio/adc/lpc32xx_adc.c +++ b/drivers/staging/iio/adc/lpc32xx_adc.c @@ -137,7 +137,7 @@ static int lpc32xx_adc_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(&pdev->dev, "failed to get platform I/O memory\n"); - return -EBUSY; + return -ENXIO; } iodev = devm_iio_device_alloc(&pdev->dev, sizeof(*info)); @@ -162,7 +162,7 @@ static int lpc32xx_adc_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq <= 0) { dev_err(&pdev->dev, "failed getting interrupt resource\n"); - return -EINVAL; + return -ENXIO; } retval = devm_request_irq(&pdev->dev, irq, lpc32xx_adc_isr, 0, -- cgit v1.1 From 0010d6b444064029f924b4973f74b87579daddc6 Mon Sep 17 00:00:00 2001 From: Sanchayan Maity Date: Mon, 17 Aug 2015 21:21:40 +0530 Subject: iio: adc: vf610: Add IIO buffer support for Vybrid ADC This patch adds support for IIO buffer to the Vybrid ADC driver. IIO triggered buffer infrastructure along with iio sysfs trigger is used to leverage continuous sampling support provided by the ADC block. Signed-off-by: Sanchayan Maity Acked-by: Fugang Duan Signed-off-by: Jonathan Cameron --- drivers/iio/adc/Kconfig | 2 + drivers/iio/adc/vf610_adc.c | 105 +++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 100 insertions(+), 7 deletions(-) diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index deea62c..2753118 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -361,6 +361,8 @@ config TWL6030_GPADC config VF610_ADC tristate "Freescale vf610 ADC driver" depends on OF + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER help Say yes here to support for Vybrid board analog-to-digital converter. Since the IP is used for i.MX6SLX, the driver also support i.MX6SLX. diff --git a/drivers/iio/adc/vf610_adc.c b/drivers/iio/adc/vf610_adc.c index 6bf4c20..635ccd8 100644 --- a/drivers/iio/adc/vf610_adc.c +++ b/drivers/iio/adc/vf610_adc.c @@ -34,8 +34,11 @@ #include #include +#include #include -#include +#include +#include +#include /* This will be the driver name the kernel reports */ #define DRIVER_NAME "vf610-adc" @@ -170,6 +173,7 @@ struct vf610_adc { u32 sample_freq_avail[5]; struct completion completion; + u16 buffer[8]; }; static const u32 vf610_hw_avgs[] = { 1, 4, 8, 16, 32 }; @@ -505,12 +509,24 @@ static const struct iio_chan_spec_ext_info vf610_ext_info[] = { .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ BIT(IIO_CHAN_INFO_SAMP_FREQ), \ .ext_info = vf610_ext_info, \ + .scan_index = (_idx), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 12, \ + .storagebits = 16, \ + }, \ } #define VF610_ADC_TEMPERATURE_CHAN(_idx, _chan_type) { \ .type = (_chan_type), \ .channel = (_idx), \ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \ + .scan_index = (_idx), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 12, \ + .storagebits = 16, \ + }, \ } static const struct iio_chan_spec vf610_adc_iio_channels[] = { @@ -531,6 +547,7 @@ static const struct iio_chan_spec vf610_adc_iio_channels[] = { VF610_ADC_CHAN(14, IIO_VOLTAGE), VF610_ADC_CHAN(15, IIO_VOLTAGE), VF610_ADC_TEMPERATURE_CHAN(26, IIO_TEMP), + IIO_CHAN_SOFT_TIMESTAMP(32), /* sentinel */ }; @@ -559,13 +576,20 @@ static int vf610_adc_read_data(struct vf610_adc *info) static irqreturn_t vf610_adc_isr(int irq, void *dev_id) { - struct vf610_adc *info = (struct vf610_adc *)dev_id; + struct iio_dev *indio_dev = (struct iio_dev *)dev_id; + struct vf610_adc *info = iio_priv(indio_dev); int coco; coco = readl(info->regs + VF610_REG_ADC_HS); if (coco & VF610_ADC_HS_COCO0) { info->value = vf610_adc_read_data(info); - complete(&info->completion); + if (iio_buffer_enabled(indio_dev)) { + info->buffer[0] = info->value; + iio_push_to_buffers_with_timestamp(indio_dev, + info->buffer, iio_get_time_ns()); + iio_trigger_notify_done(indio_dev->trig); + } else + complete(&info->completion); } return IRQ_HANDLED; @@ -613,8 +637,12 @@ static int vf610_read_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_RAW: case IIO_CHAN_INFO_PROCESSED: mutex_lock(&indio_dev->mlock); - reinit_completion(&info->completion); + if (iio_buffer_enabled(indio_dev)) { + mutex_unlock(&indio_dev->mlock); + return -EBUSY; + } + reinit_completion(&info->completion); hc_cfg = VF610_ADC_ADCHC(chan->channel); hc_cfg |= VF610_ADC_AIEN; writel(hc_cfg, info->regs + VF610_REG_ADC_HC0); @@ -694,6 +722,60 @@ static int vf610_write_raw(struct iio_dev *indio_dev, return -EINVAL; } +static int vf610_adc_buffer_postenable(struct iio_dev *indio_dev) +{ + struct vf610_adc *info = iio_priv(indio_dev); + unsigned int channel; + int ret; + int val; + + ret = iio_triggered_buffer_postenable(indio_dev); + if (ret) + return ret; + + val = readl(info->regs + VF610_REG_ADC_GC); + val |= VF610_ADC_ADCON; + writel(val, info->regs + VF610_REG_ADC_GC); + + channel = find_first_bit(indio_dev->active_scan_mask, + indio_dev->masklength); + + val = VF610_ADC_ADCHC(channel); + val |= VF610_ADC_AIEN; + + writel(val, info->regs + VF610_REG_ADC_HC0); + + return 0; +} + +static int vf610_adc_buffer_predisable(struct iio_dev *indio_dev) +{ + struct vf610_adc *info = iio_priv(indio_dev); + unsigned int hc_cfg = 0; + int val, ret; + + val = readl(info->regs + VF610_REG_ADC_GC); + val &= ~VF610_ADC_ADCON; + writel(val, info->regs + VF610_REG_ADC_GC); + + hc_cfg |= VF610_ADC_CONV_DISABLE; + hc_cfg &= ~VF610_ADC_AIEN; + + writel(hc_cfg, info->regs + VF610_REG_ADC_HC0); + + ret = iio_triggered_buffer_predisable(indio_dev); + if (ret) + return ret; + + return 0; +} + +static const struct iio_buffer_setup_ops iio_triggered_buffer_setup_ops = { + .postenable = &vf610_adc_buffer_postenable, + .predisable = &vf610_adc_buffer_predisable, + .validate_scan_mask = &iio_validate_scan_mask_onehot, +}; + static int vf610_adc_reg_access(struct iio_dev *indio_dev, unsigned reg, unsigned writeval, unsigned *readval) @@ -753,7 +835,7 @@ static int vf610_adc_probe(struct platform_device *pdev) ret = devm_request_irq(info->dev, irq, vf610_adc_isr, 0, - dev_name(&pdev->dev), info); + dev_name(&pdev->dev), indio_dev); if (ret < 0) { dev_err(&pdev->dev, "failed requesting irq, irq = %d\n", irq); return ret; @@ -806,15 +888,23 @@ static int vf610_adc_probe(struct platform_device *pdev) vf610_adc_cfg_init(info); vf610_adc_hw_init(info); + ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time, + NULL, &iio_triggered_buffer_setup_ops); + if (ret < 0) { + dev_err(&pdev->dev, "Couldn't initialise the buffer\n"); + goto error_iio_device_register; + } + ret = iio_device_register(indio_dev); if (ret) { dev_err(&pdev->dev, "Couldn't register the device.\n"); - goto error_iio_device_register; + goto error_adc_buffer_init; } return 0; - +error_adc_buffer_init: + iio_triggered_buffer_cleanup(indio_dev); error_iio_device_register: clk_disable_unprepare(info->clk); error_adc_clk_enable: @@ -829,6 +919,7 @@ static int vf610_adc_remove(struct platform_device *pdev) struct vf610_adc *info = iio_priv(indio_dev); iio_device_unregister(indio_dev); + iio_triggered_buffer_cleanup(indio_dev); regulator_disable(info->vref); clk_disable_unprepare(info->clk); -- cgit v1.1 From 078d02cfdafdd06562286b2f222e6532f369eb27 Mon Sep 17 00:00:00 2001 From: Matt Ranostay Date: Mon, 17 Aug 2015 10:30:15 -0700 Subject: iio: light: DT binding docs for APDS9960 driver Document compatible string, and required DT properties for APDS9960 chipset driver. Reviewed-by: Marek Vasut Signed-off-by: Matt Ranostay Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/light/apds9960.txt | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/light/apds9960.txt diff --git a/Documentation/devicetree/bindings/iio/light/apds9960.txt b/Documentation/devicetree/bindings/iio/light/apds9960.txt new file mode 100644 index 0000000..174b709 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/light/apds9960.txt @@ -0,0 +1,22 @@ +* Avago APDS9960 gesture/RGB/ALS/proximity sensor + +http://www.avagotech.com/docs/AV02-4191EN + +Required properties: + + - compatible: must be "avago,apds9960" + - reg: the I2c address of the sensor + - interrupt-parent: should be the phandle for the interrupt controller + - interrupts : the sole interrupt generated by the device + + Refer to interrupt-controller/interrupts.txt for generic interrupt client + node bindings. + +Example: + +apds9960@39 { + compatible = "avago,apds9960"; + reg = <0x39>; + interrupt-parent = <&gpio1>; + interrupts = <16 1>; +}; -- cgit v1.1 From aff268cd532e99ced3c8f48d01118912eb002bbf Mon Sep 17 00:00:00 2001 From: Matt Ranostay Date: Mon, 17 Aug 2015 10:30:16 -0700 Subject: iio: light: add APDS9960 ALS + promixity driver APDS9960 is a combination of ALS, proximity, and gesture sensors. This patch adds support for these functions along with gain control, integration time, and event thresholds. Signed-off-by: Matt Ranostay Signed-off-by: Jonathan Cameron --- drivers/iio/light/Kconfig | 13 + drivers/iio/light/Makefile | 1 + drivers/iio/light/apds9960.c | 1135 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1149 insertions(+) create mode 100644 drivers/iio/light/apds9960.c diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig index 7ed859a..19b9a17 100644 --- a/drivers/iio/light/Kconfig +++ b/drivers/iio/light/Kconfig @@ -50,6 +50,19 @@ config APDS9300 To compile this driver as a module, choose M here: the module will be called apds9300. +config APDS9960 + tristate "Avago APDS9960 gesture/RGB/ALS/proximity sensor" + select REGMAP_I2C + select IIO_BUFFER + select IIO_KFIFO_BUF + depends on I2C + help + Say Y here to build I2C interface support for the Avago + APDS9960 gesture/RGB/ALS/proximity sensor. + + To compile this driver as a module, choose M here: the + module will be called apds9960 + config BH1750 tristate "ROHM BH1750 ambient light sensor" depends on I2C diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile index 91c74c0..7b22445 100644 --- a/drivers/iio/light/Makefile +++ b/drivers/iio/light/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_ACPI_ALS) += acpi-als.o obj-$(CONFIG_ADJD_S311) += adjd_s311.o obj-$(CONFIG_AL3320A) += al3320a.o obj-$(CONFIG_APDS9300) += apds9300.o +obj-$(CONFIG_APDS9960) += apds9960.o obj-$(CONFIG_BH1750) += bh1750.o obj-$(CONFIG_CM32181) += cm32181.o obj-$(CONFIG_CM3232) += cm3232.o diff --git a/drivers/iio/light/apds9960.c b/drivers/iio/light/apds9960.c new file mode 100644 index 0000000..27f4157 --- /dev/null +++ b/drivers/iio/light/apds9960.c @@ -0,0 +1,1135 @@ +/* + * apds9960.c - Support for Avago APDS9960 gesture/RGB/ALS/proximity sensor + * + * Copyright (C) 2015 Matt Ranostay + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * TODO: gesture + proximity calib offsets + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define APDS9960_REGMAP_NAME "apds9960_regmap" +#define APDS9960_DRV_NAME "apds9960" + +#define APDS9960_REG_RAM_START 0x00 +#define APDS9960_REG_RAM_END 0x7f + +#define APDS9960_REG_ENABLE 0x80 +#define APDS9960_REG_ATIME 0x81 +#define APDS9960_REG_WTIME 0x83 + +#define APDS9960_REG_AILTL 0x84 +#define APDS9960_REG_AILTH 0x85 +#define APDS9960_REG_AIHTL 0x86 +#define APDS9960_REG_AIHTH 0x87 + +#define APDS9960_REG_PILT 0x89 +#define APDS9960_REG_PIHT 0x8b +#define APDS9960_REG_PERS 0x8c + +#define APDS9960_REG_CONFIG_1 0x8d +#define APDS9960_REG_PPULSE 0x8e + +#define APDS9960_REG_CONTROL 0x8f +#define APDS9960_REG_CONTROL_AGAIN_MASK 0x03 +#define APDS9960_REG_CONTROL_PGAIN_MASK 0x0c +#define APDS9960_REG_CONTROL_AGAIN_MASK_SHIFT 0 +#define APDS9960_REG_CONTROL_PGAIN_MASK_SHIFT 2 + +#define APDS9960_REG_CONFIG_2 0x90 +#define APDS9960_REG_CONFIG_2_GGAIN_MASK 0x60 +#define APDS9960_REG_CONFIG_2_GGAIN_MASK_SHIFT 5 + +#define APDS9960_REG_ID 0x92 + +#define APDS9960_REG_STATUS 0x93 +#define APDS9960_REG_STATUS_PS_INT BIT(5) +#define APDS9960_REG_STATUS_ALS_INT BIT(4) +#define APDS9960_REG_STATUS_GINT BIT(2) + +#define APDS9960_REG_PDATA 0x9c +#define APDS9960_REG_POFFSET_UR 0x9d +#define APDS9960_REG_POFFSET_DL 0x9e +#define APDS9960_REG_CONFIG_3 0x9f + +#define APDS9960_REG_GPENTH 0xa0 +#define APDS9960_REG_GEXTH 0xa1 + +#define APDS9960_REG_GCONF_1 0xa2 +#define APDS9960_REG_GCONF_1_GFIFO_THRES_MASK 0xc0 +#define APDS9960_REG_GCONF_1_GFIFO_THRES_MASK_SHIFT 6 + +#define APDS9960_REG_GCONF_2 0xa3 +#define APDS9960_REG_GOFFSET_U 0xa4 +#define APDS9960_REG_GOFFSET_D 0xa5 +#define APDS9960_REG_GPULSE 0xa6 +#define APDS9960_REG_GOFFSET_L 0xa7 +#define APDS9960_REG_GOFFSET_R 0xa9 +#define APDS9960_REG_GCONF_3 0xaa + +#define APDS9960_REG_GCONF_4 0xab +#define APDS9960_REG_GFLVL 0xae +#define APDS9960_REG_GSTATUS 0xaf + +#define APDS9960_REG_IFORCE 0xe4 +#define APDS9960_REG_PICLEAR 0xe5 +#define APDS9960_REG_CICLEAR 0xe6 +#define APDS9960_REG_AICLEAR 0xe7 + +#define APDS9960_DEFAULT_PERS 0x33 +#define APDS9960_DEFAULT_GPENTH 0x50 +#define APDS9960_DEFAULT_GEXTH 0x40 + +#define APDS9960_MAX_PXS_THRES_VAL 255 +#define APDS9960_MAX_ALS_THRES_VAL 0xffff +#define APDS9960_MAX_INT_TIME_IN_US 1000000 + +enum apds9960_als_channel_idx { + IDX_ALS_CLEAR, IDX_ALS_RED, IDX_ALS_GREEN, IDX_ALS_BLUE, +}; + +#define APDS9960_REG_ALS_BASE 0x94 +#define APDS9960_REG_ALS_CHANNEL(_colour) \ + (APDS9960_REG_ALS_BASE + (IDX_ALS_##_colour * 2)) + +enum apds9960_gesture_channel_idx { + IDX_DIR_UP, IDX_DIR_DOWN, IDX_DIR_LEFT, IDX_DIR_RIGHT, +}; + +#define APDS9960_REG_GFIFO_BASE 0xfc +#define APDS9960_REG_GFIFO_DIR(_dir) \ + (APDS9960_REG_GFIFO_BASE + IDX_DIR_##_dir) + +struct apds9960_data { + struct i2c_client *client; + struct iio_dev *indio_dev; + struct mutex lock; + + /* regmap fields */ + struct regmap *regmap; + struct regmap_field *reg_int_als; + struct regmap_field *reg_int_ges; + struct regmap_field *reg_int_pxs; + + struct regmap_field *reg_enable_als; + struct regmap_field *reg_enable_ges; + struct regmap_field *reg_enable_pxs; + + /* state */ + int als_int; + int pxs_int; + int gesture_mode_running; + + /* gain values */ + int als_gain; + int pxs_gain; + + /* integration time value in us */ + int als_adc_int_us; + + /* gesture buffer */ + u8 buffer[4]; /* 4 8-bit channels */ +}; + +static const struct reg_default apds9960_reg_defaults[] = { + /* Default ALS integration time = 2.48ms */ + { APDS9960_REG_ATIME, 0xff }, +}; + +static const struct regmap_range apds9960_volatile_ranges[] = { + regmap_reg_range(APDS9960_REG_STATUS, + APDS9960_REG_PDATA), + regmap_reg_range(APDS9960_REG_GFLVL, + APDS9960_REG_GSTATUS), + regmap_reg_range(APDS9960_REG_GFIFO_DIR(UP), + APDS9960_REG_GFIFO_DIR(RIGHT)), + regmap_reg_range(APDS9960_REG_IFORCE, + APDS9960_REG_AICLEAR), +}; + +static const struct regmap_access_table apds9960_volatile_table = { + .yes_ranges = apds9960_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(apds9960_volatile_ranges), +}; + +static const struct regmap_range apds9960_precious_ranges[] = { + regmap_reg_range(APDS9960_REG_RAM_START, APDS9960_REG_RAM_END), +}; + +static const struct regmap_access_table apds9960_precious_table = { + .yes_ranges = apds9960_precious_ranges, + .n_yes_ranges = ARRAY_SIZE(apds9960_precious_ranges), +}; + +static const struct regmap_range apds9960_readable_ranges[] = { + regmap_reg_range(APDS9960_REG_ENABLE, + APDS9960_REG_GSTATUS), + regmap_reg_range(APDS9960_REG_GFIFO_DIR(UP), + APDS9960_REG_GFIFO_DIR(RIGHT)), +}; + +static const struct regmap_access_table apds9960_readable_table = { + .yes_ranges = apds9960_readable_ranges, + .n_yes_ranges = ARRAY_SIZE(apds9960_readable_ranges), +}; + +static const struct regmap_range apds9960_writeable_ranges[] = { + regmap_reg_range(APDS9960_REG_ENABLE, APDS9960_REG_CONFIG_2), + regmap_reg_range(APDS9960_REG_POFFSET_UR, APDS9960_REG_GCONF_4), + regmap_reg_range(APDS9960_REG_IFORCE, APDS9960_REG_AICLEAR), +}; + +static const struct regmap_access_table apds9960_writeable_table = { + .yes_ranges = apds9960_writeable_ranges, + .n_yes_ranges = ARRAY_SIZE(apds9960_writeable_ranges), +}; + +static const struct regmap_config apds9960_regmap_config = { + .name = APDS9960_REGMAP_NAME, + .reg_bits = 8, + .val_bits = 8, + .use_single_rw = 1, + + .volatile_table = &apds9960_volatile_table, + .precious_table = &apds9960_precious_table, + .rd_table = &apds9960_readable_table, + .wr_table = &apds9960_writeable_table, + + .reg_defaults = apds9960_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(apds9960_reg_defaults), + .max_register = APDS9960_REG_GFIFO_DIR(RIGHT), + .cache_type = REGCACHE_RBTREE, +}; + +static const struct iio_event_spec apds9960_pxs_event_spec[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_ENABLE), + }, + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_separate = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_ENABLE), + }, +}; + +static const struct iio_event_spec apds9960_als_event_spec[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_ENABLE), + }, + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_separate = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_ENABLE), + }, +}; + +#define APDS9960_GESTURE_CHANNEL(_dir, _si) { \ + .type = IIO_PROXIMITY, \ + .channel = _si + 1, \ + .scan_index = _si, \ + .indexed = 1, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 8, \ + .storagebits = 8, \ + }, \ +} + +#define APDS9960_INTENSITY_CHANNEL(_colour) { \ + .type = IIO_INTENSITY, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_INT_TIME), \ + .channel2 = IIO_MOD_LIGHT_##_colour, \ + .address = APDS9960_REG_ALS_CHANNEL(_colour), \ + .modified = 1, \ + .scan_index = -1, \ +} + +static const unsigned long apds9960_scan_masks[] = {0xf, 0}; + +static const struct iio_chan_spec apds9960_channels[] = { + { + .type = IIO_PROXIMITY, + .address = APDS9960_REG_PDATA, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), + .channel = 0, + .indexed = 0, + .scan_index = -1, + + .event_spec = apds9960_pxs_event_spec, + .num_event_specs = ARRAY_SIZE(apds9960_pxs_event_spec), + }, + /* Gesture Sensor */ + APDS9960_GESTURE_CHANNEL(UP, 0), + APDS9960_GESTURE_CHANNEL(DOWN, 1), + APDS9960_GESTURE_CHANNEL(LEFT, 2), + APDS9960_GESTURE_CHANNEL(RIGHT, 3), + /* ALS */ + { + .type = IIO_INTENSITY, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_INT_TIME), + .channel2 = IIO_MOD_LIGHT_CLEAR, + .address = APDS9960_REG_ALS_CHANNEL(CLEAR), + .modified = 1, + .scan_index = -1, + + .event_spec = apds9960_als_event_spec, + .num_event_specs = ARRAY_SIZE(apds9960_als_event_spec), + }, + /* RGB Sensor */ + APDS9960_INTENSITY_CHANNEL(RED), + APDS9960_INTENSITY_CHANNEL(GREEN), + APDS9960_INTENSITY_CHANNEL(BLUE), +}; + +/* integration time in us */ +static const int apds9960_int_time[][2] = + { {28000, 246}, {100000, 219}, {200000, 182}, {700000, 0} }; + +/* gain mapping */ +static const int apds9960_pxs_gain_map[] = {1, 2, 4, 8}; +static const int apds9960_als_gain_map[] = {1, 4, 16, 64}; + +static IIO_CONST_ATTR(proximity_scale_available, "1 2 4 8"); +static IIO_CONST_ATTR(intensity_scale_available, "1 4 16 64"); +static IIO_CONST_ATTR_INT_TIME_AVAIL("0.028 0.1 0.2 0.7"); + +static struct attribute *apds9960_attributes[] = { + &iio_const_attr_proximity_scale_available.dev_attr.attr, + &iio_const_attr_intensity_scale_available.dev_attr.attr, + &iio_const_attr_integration_time_available.dev_attr.attr, + NULL, +}; + +static struct attribute_group apds9960_attribute_group = { + .attrs = apds9960_attributes, +}; + +static const struct reg_field apds9960_reg_field_int_als = + REG_FIELD(APDS9960_REG_ENABLE, 4, 4); + +static const struct reg_field apds9960_reg_field_int_ges = + REG_FIELD(APDS9960_REG_GCONF_4, 1, 1); + +static const struct reg_field apds9960_reg_field_int_pxs = + REG_FIELD(APDS9960_REG_ENABLE, 5, 5); + +static const struct reg_field apds9960_reg_field_enable_als = + REG_FIELD(APDS9960_REG_ENABLE, 1, 1); + +static const struct reg_field apds9960_reg_field_enable_ges = + REG_FIELD(APDS9960_REG_ENABLE, 6, 6); + +static const struct reg_field apds9960_reg_field_enable_pxs = + REG_FIELD(APDS9960_REG_ENABLE, 2, 2); + +static int apds9960_set_it_time(struct apds9960_data *data, int val2) +{ + int ret = -EINVAL; + int idx; + + for (idx = 0; idx < ARRAY_SIZE(apds9960_int_time); idx++) { + if (apds9960_int_time[idx][0] == val2) { + mutex_lock(&data->lock); + ret = regmap_write(data->regmap, APDS9960_REG_ATIME, + apds9960_int_time[idx][1]); + if (!ret) + data->als_adc_int_us = val2; + mutex_unlock(&data->lock); + break; + } + } + + return ret; +} + +static int apds9960_set_pxs_gain(struct apds9960_data *data, int val) +{ + int ret = -EINVAL; + int idx; + + for (idx = 0; idx < ARRAY_SIZE(apds9960_pxs_gain_map); idx++) { + if (apds9960_pxs_gain_map[idx] == val) { + /* pxs + gesture gains are mirrored */ + mutex_lock(&data->lock); + ret = regmap_update_bits(data->regmap, + APDS9960_REG_CONTROL, + APDS9960_REG_CONTROL_PGAIN_MASK, + idx << APDS9960_REG_CONTROL_PGAIN_MASK_SHIFT); + if (ret) { + mutex_unlock(&data->lock); + break; + } + + ret = regmap_update_bits(data->regmap, + APDS9960_REG_CONFIG_2, + APDS9960_REG_CONFIG_2_GGAIN_MASK, + idx << APDS9960_REG_CONFIG_2_GGAIN_MASK_SHIFT); + if (!ret) + data->pxs_gain = idx; + mutex_unlock(&data->lock); + break; + } + } + + return ret; +} + +static int apds9960_set_als_gain(struct apds9960_data *data, int val) +{ + int ret = -EINVAL; + int idx; + + for (idx = 0; idx < ARRAY_SIZE(apds9960_als_gain_map); idx++) { + if (apds9960_als_gain_map[idx] == val) { + mutex_lock(&data->lock); + ret = regmap_update_bits(data->regmap, + APDS9960_REG_CONTROL, + APDS9960_REG_CONTROL_AGAIN_MASK, idx); + if (!ret) + data->als_gain = idx; + mutex_unlock(&data->lock); + break; + } + } + + return ret; +} + +#ifdef CONFIG_PM +static int apds9960_set_power_state(struct apds9960_data *data, bool on) +{ + struct device *dev = &data->client->dev; + int ret = 0; + + mutex_lock(&data->lock); + + if (on) { + int suspended; + + suspended = pm_runtime_suspended(dev); + ret = pm_runtime_get_sync(dev); + + /* Allow one integration cycle before allowing a reading */ + if (suspended) + usleep_range(data->als_adc_int_us, + APDS9960_MAX_INT_TIME_IN_US); + } else { + ret = pm_runtime_put_autosuspend(dev); + } + + mutex_unlock(&data->lock); + + return ret; +} +#else +static int apds9960_set_power_state(struct apds9960_data *data, bool on) +{ + return 0; +} +#endif + +static int apds9960_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct apds9960_data *data = iio_priv(indio_dev); + u16 buf; + int ret = -EINVAL; + + if (data->gesture_mode_running) + return -EBUSY; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + apds9960_set_power_state(data, true); + switch (chan->type) { + case IIO_PROXIMITY: + ret = regmap_read(data->regmap, chan->address, val); + if (!ret) + ret = IIO_VAL_INT; + break; + case IIO_INTENSITY: + ret = regmap_bulk_read(data->regmap, chan->address, + &buf, 2); + if (!ret) + ret = IIO_VAL_INT; + *val = le16_to_cpu(buf); + break; + default: + ret = -EINVAL; + } + apds9960_set_power_state(data, false); + break; + case IIO_CHAN_INFO_INT_TIME: + /* RGB + ALS sensors only have integration time */ + mutex_lock(&data->lock); + switch (chan->type) { + case IIO_INTENSITY: + *val = 0; + *val2 = data->als_adc_int_us; + ret = IIO_VAL_INT_PLUS_MICRO; + break; + default: + ret = -EINVAL; + } + mutex_unlock(&data->lock); + break; + case IIO_CHAN_INFO_SCALE: + mutex_lock(&data->lock); + switch (chan->type) { + case IIO_PROXIMITY: + *val = apds9960_pxs_gain_map[data->pxs_gain]; + ret = IIO_VAL_INT; + break; + case IIO_INTENSITY: + *val = apds9960_als_gain_map[data->als_gain]; + ret = IIO_VAL_INT; + break; + default: + ret = -EINVAL; + } + mutex_unlock(&data->lock); + break; + } + + return ret; +}; + +static int apds9960_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct apds9960_data *data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_INT_TIME: + /* RGB + ALS sensors only have int time */ + switch (chan->type) { + case IIO_INTENSITY: + if (val != 0) + return -EINVAL; + return apds9960_set_it_time(data, val2); + default: + return -EINVAL; + } + case IIO_CHAN_INFO_SCALE: + if (val2 != 0) + return -EINVAL; + switch (chan->type) { + case IIO_PROXIMITY: + return apds9960_set_pxs_gain(data, val); + case IIO_INTENSITY: + return apds9960_set_als_gain(data, val); + default: + return -EINVAL; + } + default: + return -EINVAL; + }; + + return 0; +} + +static inline int apds9960_get_thres_reg(const struct iio_chan_spec *chan, + enum iio_event_direction dir, + u8 *reg) +{ + switch (dir) { + case IIO_EV_DIR_RISING: + switch (chan->type) { + case IIO_PROXIMITY: + *reg = APDS9960_REG_PIHT; + break; + case IIO_INTENSITY: + *reg = APDS9960_REG_AIHTL; + break; + default: + return -EINVAL; + } + break; + case IIO_EV_DIR_FALLING: + switch (chan->type) { + case IIO_PROXIMITY: + *reg = APDS9960_REG_PILT; + break; + case IIO_INTENSITY: + *reg = APDS9960_REG_AILTL; + break; + default: + return -EINVAL; + } + break; + default: + return -EINVAL; + } + + return 0; +} + +static int apds9960_read_event(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int *val, int *val2) +{ + u8 reg; + u16 buf; + int ret = 0; + struct apds9960_data *data = iio_priv(indio_dev); + + if (info != IIO_EV_INFO_VALUE) + return -EINVAL; + + ret = apds9960_get_thres_reg(chan, dir, ®); + if (ret < 0) + return ret; + + if (chan->type == IIO_PROXIMITY) { + ret = regmap_read(data->regmap, reg, val); + if (ret < 0) + return ret; + } else if (chan->type == IIO_INTENSITY) { + ret = regmap_bulk_read(data->regmap, reg, &buf, 2); + if (ret < 0) + return ret; + *val = le16_to_cpu(buf); + } else + return -EINVAL; + + *val2 = 0; + + return IIO_VAL_INT; +} + +static int apds9960_write_event(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int val, int val2) +{ + u8 reg; + u16 buf; + int ret = 0; + struct apds9960_data *data = iio_priv(indio_dev); + + if (info != IIO_EV_INFO_VALUE) + return -EINVAL; + + ret = apds9960_get_thres_reg(chan, dir, ®); + if (ret < 0) + return ret; + + if (chan->type == IIO_PROXIMITY) { + if (val < 0 || val > APDS9960_MAX_PXS_THRES_VAL) + return -EINVAL; + ret = regmap_write(data->regmap, reg, val); + if (ret < 0) + return ret; + } else if (chan->type == IIO_INTENSITY) { + if (val < 0 || val > APDS9960_MAX_ALS_THRES_VAL) + return -EINVAL; + buf = cpu_to_le16(val); + ret = regmap_bulk_write(data->regmap, reg, &buf, 2); + if (ret < 0) + return ret; + } else + return -EINVAL; + + return 0; +} + +static int apds9960_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + struct apds9960_data *data = iio_priv(indio_dev); + + switch (chan->type) { + case IIO_PROXIMITY: + return data->pxs_int; + case IIO_INTENSITY: + return data->als_int; + default: + return -EINVAL; + } + + return 0; +} + +static int apds9960_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + int state) +{ + struct apds9960_data *data = iio_priv(indio_dev); + int ret; + + state = !!state; + + switch (chan->type) { + case IIO_PROXIMITY: + if (data->pxs_int == state) + return -EINVAL; + + ret = regmap_field_write(data->reg_int_pxs, state); + if (ret) + return ret; + data->pxs_int = state; + apds9960_set_power_state(data, state); + break; + case IIO_INTENSITY: + if (data->als_int == state) + return -EINVAL; + + ret = regmap_field_write(data->reg_int_als, state); + if (ret) + return ret; + data->als_int = state; + apds9960_set_power_state(data, state); + break; + default: + return -EINVAL; + } + + return 0; +} + +static const struct iio_info apds9960_info = { + .driver_module = THIS_MODULE, + .attrs = &apds9960_attribute_group, + .read_raw = apds9960_read_raw, + .write_raw = apds9960_write_raw, + .read_event_value = apds9960_read_event, + .write_event_value = apds9960_write_event, + .read_event_config = apds9960_read_event_config, + .write_event_config = apds9960_write_event_config, + +}; + +static inline int apds9660_fifo_is_empty(struct apds9960_data *data) +{ + int cnt; + int ret; + + ret = regmap_read(data->regmap, APDS9960_REG_GFLVL, &cnt); + if (ret) + return ret; + + return cnt; +} + +static void apds9960_read_gesture_fifo(struct apds9960_data *data) +{ + int ret, cnt = 0; + + mutex_lock(&data->lock); + data->gesture_mode_running = 1; + + while (cnt-- || (cnt = apds9660_fifo_is_empty(data) > 0)) { + ret = regmap_bulk_read(data->regmap, APDS9960_REG_GFIFO_BASE, + &data->buffer, 4); + + if (ret) + goto err_read; + + iio_push_to_buffers(data->indio_dev, data->buffer); + } + +err_read: + data->gesture_mode_running = 0; + mutex_unlock(&data->lock); +} + +static irqreturn_t apds9960_interrupt_handler(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + struct apds9960_data *data = iio_priv(indio_dev); + int ret, status; + + ret = regmap_read(data->regmap, APDS9960_REG_STATUS, &status); + if (ret < 0) { + dev_err(&data->client->dev, "irq status reg read failed\n"); + return IRQ_HANDLED; + } + + if ((status & APDS9960_REG_STATUS_ALS_INT) && data->als_int) { + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_INTENSITY, 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_EITHER), + iio_get_time_ns()); + regmap_write(data->regmap, APDS9960_REG_CICLEAR, 1); + } + + if ((status & APDS9960_REG_STATUS_PS_INT) && data->pxs_int) { + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_EITHER), + iio_get_time_ns()); + regmap_write(data->regmap, APDS9960_REG_PICLEAR, 1); + } + + if (status & APDS9960_REG_STATUS_GINT) + apds9960_read_gesture_fifo(data); + + return IRQ_HANDLED; +} + +static int apds9960_set_powermode(struct apds9960_data *data, bool state) +{ + return regmap_update_bits(data->regmap, APDS9960_REG_ENABLE, 1, state); +} + +static int apds9960_buffer_postenable(struct iio_dev *indio_dev) +{ + struct apds9960_data *data = iio_priv(indio_dev); + int ret; + + ret = regmap_field_write(data->reg_int_ges, 1); + if (ret) + return ret; + + ret = regmap_field_write(data->reg_enable_ges, 1); + if (ret) + return ret; + + pm_runtime_get_sync(&data->client->dev); + + return 0; +} + +static int apds9960_buffer_predisable(struct iio_dev *indio_dev) +{ + struct apds9960_data *data = iio_priv(indio_dev); + int ret; + + ret = regmap_field_write(data->reg_enable_ges, 0); + if (ret) + return ret; + + ret = regmap_field_write(data->reg_int_ges, 0); + if (ret) + return ret; + + pm_runtime_put_autosuspend(&data->client->dev); + + return 0; +} + +static const struct iio_buffer_setup_ops apds9960_buffer_setup_ops = { + .postenable = apds9960_buffer_postenable, + .predisable = apds9960_buffer_predisable, +}; + +static int apds9960_regfield_init(struct apds9960_data *data) +{ + struct device *dev = &data->client->dev; + struct regmap *regmap = data->regmap; + + data->reg_int_als = devm_regmap_field_alloc(dev, regmap, + apds9960_reg_field_int_als); + if (IS_ERR(data->reg_int_als)) { + dev_err(dev, "INT ALS reg field init failed\n"); + return PTR_ERR(data->reg_int_als); + } + + data->reg_int_ges = devm_regmap_field_alloc(dev, regmap, + apds9960_reg_field_int_ges); + if (IS_ERR(data->reg_int_ges)) { + dev_err(dev, "INT gesture reg field init failed\n"); + return PTR_ERR(data->reg_int_ges); + } + + data->reg_int_pxs = devm_regmap_field_alloc(dev, regmap, + apds9960_reg_field_int_pxs); + if (IS_ERR(data->reg_int_pxs)) { + dev_err(dev, "INT pxs reg field init failed\n"); + return PTR_ERR(data->reg_int_pxs); + } + + data->reg_enable_als = devm_regmap_field_alloc(dev, regmap, + apds9960_reg_field_enable_als); + if (IS_ERR(data->reg_enable_als)) { + dev_err(dev, "Enable ALS reg field init failed\n"); + return PTR_ERR(data->reg_enable_als); + } + + data->reg_enable_ges = devm_regmap_field_alloc(dev, regmap, + apds9960_reg_field_enable_ges); + if (IS_ERR(data->reg_enable_ges)) { + dev_err(dev, "Enable gesture reg field init failed\n"); + return PTR_ERR(data->reg_enable_ges); + } + + data->reg_enable_pxs = devm_regmap_field_alloc(dev, regmap, + apds9960_reg_field_enable_pxs); + if (IS_ERR(data->reg_enable_pxs)) { + dev_err(dev, "Enable PXS reg field init failed\n"); + return PTR_ERR(data->reg_enable_pxs); + } + + return 0; +} + +static int apds9960_chip_init(struct apds9960_data *data) +{ + int ret; + + /* Default IT for ALS of 28 ms */ + ret = apds9960_set_it_time(data, 28000); + if (ret) + return ret; + + /* Ensure gesture interrupt is OFF */ + ret = regmap_field_write(data->reg_int_ges, 0); + if (ret) + return ret; + + /* Disable gesture sensor, since polling is useless from user-space */ + ret = regmap_field_write(data->reg_enable_ges, 0); + if (ret) + return ret; + + /* Ensure proximity interrupt is OFF */ + ret = regmap_field_write(data->reg_int_pxs, 0); + if (ret) + return ret; + + /* Enable proximity sensor for polling */ + ret = regmap_field_write(data->reg_enable_pxs, 1); + if (ret) + return ret; + + /* Ensure ALS interrupt is OFF */ + ret = regmap_field_write(data->reg_int_als, 0); + if (ret) + return ret; + + /* Enable ALS sensor for polling */ + ret = regmap_field_write(data->reg_enable_als, 1); + if (ret) + return ret; + /* + * When enabled trigger an interrupt after 3 readings + * outside threshold for ALS + PXS + */ + ret = regmap_write(data->regmap, APDS9960_REG_PERS, + APDS9960_DEFAULT_PERS); + if (ret) + return ret; + + /* + * Wait for 4 event outside gesture threshold to prevent interrupt + * flooding. + */ + ret = regmap_update_bits(data->regmap, APDS9960_REG_GCONF_1, + APDS9960_REG_GCONF_1_GFIFO_THRES_MASK, + BIT(0) << APDS9960_REG_GCONF_1_GFIFO_THRES_MASK_SHIFT); + if (ret) + return ret; + + /* Default ENTER and EXIT thresholds for the GESTURE engine. */ + ret = regmap_write(data->regmap, APDS9960_REG_GPENTH, + APDS9960_DEFAULT_GPENTH); + if (ret) + return ret; + + ret = regmap_write(data->regmap, APDS9960_REG_GEXTH, + APDS9960_DEFAULT_GEXTH); + if (ret) + return ret; + + ret = apds9960_set_powermode(data, 1); + if (ret) + return ret; + + return 0; +} + +static int apds9960_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct apds9960_data *data; + struct iio_buffer *buffer; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + buffer = devm_iio_kfifo_allocate(&client->dev); + if (!buffer) + return -ENOMEM; + + iio_device_attach_buffer(indio_dev, buffer); + + indio_dev->info = &apds9960_info; + indio_dev->name = APDS9960_DRV_NAME; + indio_dev->channels = apds9960_channels; + indio_dev->num_channels = ARRAY_SIZE(apds9960_channels); + indio_dev->available_scan_masks = apds9960_scan_masks; + indio_dev->modes = (INDIO_BUFFER_SOFTWARE | INDIO_DIRECT_MODE); + indio_dev->setup_ops = &apds9960_buffer_setup_ops; + + data = iio_priv(indio_dev); + i2c_set_clientdata(client, indio_dev); + + data->regmap = devm_regmap_init_i2c(client, &apds9960_regmap_config); + if (IS_ERR(data->regmap)) { + dev_err(&client->dev, "regmap initialization failed.\n"); + return PTR_ERR(data->regmap); + } + + data->client = client; + data->indio_dev = indio_dev; + mutex_init(&data->lock); + + ret = pm_runtime_set_active(&client->dev); + if (ret) + goto error_power_down; + + pm_runtime_enable(&client->dev); + pm_runtime_set_autosuspend_delay(&client->dev, 5000); + pm_runtime_use_autosuspend(&client->dev); + + apds9960_set_power_state(data, true); + + ret = apds9960_regfield_init(data); + if (ret) + goto error_power_down; + + ret = apds9960_chip_init(data); + if (ret) + goto error_power_down; + + if (client->irq <= 0) { + dev_err(&client->dev, "no valid irq defined\n"); + ret = -EINVAL; + goto error_power_down; + } + ret = devm_request_threaded_irq(&client->dev, client->irq, + NULL, apds9960_interrupt_handler, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + "apds9960_event", + indio_dev); + if (ret) { + dev_err(&client->dev, "request irq (%d) failed\n", client->irq); + goto error_power_down; + } + + ret = iio_device_register(indio_dev); + if (ret) + goto error_power_down; + + apds9960_set_power_state(data, false); + + return 0; + +error_power_down: + apds9960_set_power_state(data, false); + + return ret; +} + +static int apds9960_remove(struct i2c_client *client) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(client); + struct apds9960_data *data = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + pm_runtime_disable(&client->dev); + pm_runtime_set_suspended(&client->dev); + apds9960_set_powermode(data, 0); + + return 0; +} + +#ifdef CONFIG_PM +static int apds9960_runtime_suspend(struct device *dev) +{ + struct apds9960_data *data = + iio_priv(i2c_get_clientdata(to_i2c_client(dev))); + + return apds9960_set_powermode(data, 0); +} + +static int apds9960_runtime_resume(struct device *dev) +{ + struct apds9960_data *data = + iio_priv(i2c_get_clientdata(to_i2c_client(dev))); + + return apds9960_set_powermode(data, 1); +} +#endif + +static const struct dev_pm_ops apds9960_pm_ops = { + SET_RUNTIME_PM_OPS(apds9960_runtime_suspend, + apds9960_runtime_resume, NULL) +}; + +static const struct i2c_device_id apds9960_id[] = { + { "apds9960", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, apds9960_id); + +static struct i2c_driver apds9960_driver = { + .driver = { + .name = APDS9960_DRV_NAME, + .pm = &apds9960_pm_ops, + .owner = THIS_MODULE, + }, + .probe = apds9960_probe, + .remove = apds9960_remove, + .id_table = apds9960_id, +}; +module_i2c_driver(apds9960_driver); + +MODULE_AUTHOR("Matt Ranostay "); +MODULE_DESCRIPTION("ADPS9960 Gesture/RGB/ALS/Proximity sensor"); +MODULE_LICENSE("GPL"); -- cgit v1.1 From 7d87b3c5c8602df4ce6d615b33ae6dc15438fa29 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 18 Aug 2015 12:16:33 +0300 Subject: iio: tsl4531: fix error handling in tsl4531_check_id() The tsl4531_check_id() function returned 1 on "found" and 0 on "not found" and negative error codes on failure. This was non-standard and bug prone. The caller treated all non-zero values including error codes as "found". This patch fixes it by changing the tsl4531_check_id() to return zero on success or a negative error code, and updates the caller. Signed-off-by: Dan Carpenter Acked-by: Peter Meerwald Signed-off-by: Jonathan Cameron --- drivers/iio/light/tsl4531.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/iio/light/tsl4531.c b/drivers/iio/light/tsl4531.c index 2697918..cf94ec7 100644 --- a/drivers/iio/light/tsl4531.c +++ b/drivers/iio/light/tsl4531.c @@ -158,9 +158,9 @@ static int tsl4531_check_id(struct i2c_client *client) case TSL45313_ID: case TSL45315_ID: case TSL45317_ID: - return 1; - default: return 0; + default: + return -ENODEV; } } @@ -180,9 +180,10 @@ static int tsl4531_probe(struct i2c_client *client, data->client = client; mutex_init(&data->lock); - if (!tsl4531_check_id(client)) { + ret = tsl4531_check_id(client); + if (ret) { dev_err(&client->dev, "no TSL4531 sensor\n"); - return -ENODEV; + return ret; } ret = i2c_smbus_write_byte_data(data->client, TSL4531_CONTROL, -- cgit v1.1 From 12280bd3d5d7e1ba1dd60ba0bd4412f4056fc028 Mon Sep 17 00:00:00 2001 From: Matt Ranostay Date: Tue, 18 Aug 2015 07:40:37 -0700 Subject: devicetree: add PulsedLight vendor + device docs Add pulsedlight vendor to vendor-prefixes.txt, and LIDAR device documentation to trivial-devices.txt Signed-off-by: Matt Ranostay Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/i2c/trivial-devices.txt | 1 + Documentation/devicetree/bindings/vendor-prefixes.txt | 1 + 2 files changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/i2c/trivial-devices.txt b/Documentation/devicetree/bindings/i2c/trivial-devices.txt index 00f8652..af1bc50 100644 --- a/Documentation/devicetree/bindings/i2c/trivial-devices.txt +++ b/Documentation/devicetree/bindings/i2c/trivial-devices.txt @@ -80,6 +80,7 @@ oki,ml86v7667 OKI ML86V7667 video decoder ovti,ov5642 OV5642: Color CMOS QSXGA (5-megapixel) Image Sensor with OmniBSI and Embedded TrueFocus pericom,pt7c4338 Real-time Clock Module plx,pex8648 48-Lane, 12-Port PCI Express Gen 2 (5.0 GT/s) Switch +pulsedlight,lidar-lite-v2 Pulsedlight LIDAR range-finding sensor ramtron,24c64 i2c serial eeprom (24cxx) ricoh,r2025sd I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC ricoh,r2221tl I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index bc64cc9..341695b 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -160,6 +160,7 @@ phytec PHYTEC Messtechnik GmbH picochip Picochip Ltd plathome Plat'Home Co., Ltd. pixcir PIXCIR MICROELECTRONICS Co., Ltd +pulsedlight PulsedLight, Inc powervr PowerVR (deprecated, use img) qca Qualcomm Atheros, Inc. qcom Qualcomm Technologies, Inc -- cgit v1.1 From cb119d5350839297bbe8a382dbb2feff545742b5 Mon Sep 17 00:00:00 2001 From: Matt Ranostay Date: Tue, 18 Aug 2015 07:40:38 -0700 Subject: iio: proximity: add support for PulsedLight LIDAR Add support for the PulsedLight LIDAR rangefinder sensor which allows high speed (over 300Hz) distance measurements using Barker Coding within 40 meter range. Support only tested on the "blue label" rev 2, but may work using low sample frequencies on the original version. Signed-off-by: Matt Ranostay Signed-off-by: Jonathan Cameron --- drivers/iio/proximity/Kconfig | 12 + drivers/iio/proximity/Makefile | 1 + drivers/iio/proximity/pulsedlight-lidar-lite-v2.c | 288 ++++++++++++++++++++++ 3 files changed, 301 insertions(+) create mode 100644 drivers/iio/proximity/pulsedlight-lidar-lite-v2.c diff --git a/drivers/iio/proximity/Kconfig b/drivers/iio/proximity/Kconfig index 41a8d8f..ef4c73d 100644 --- a/drivers/iio/proximity/Kconfig +++ b/drivers/iio/proximity/Kconfig @@ -20,6 +20,18 @@ endmenu menu "Proximity sensors" +config LIDAR_LITE_V2 + tristate "PulsedLight LIDAR sensor" + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + depends on I2C + help + Say Y to build a driver for PulsedLight LIDAR range finding + sensor. + + To compile this driver as a module, choose M here: the + module will be called pulsedlight-lite-v2 + config SX9500 tristate "SX9500 Semtech proximity sensor" select IIO_BUFFER diff --git a/drivers/iio/proximity/Makefile b/drivers/iio/proximity/Makefile index 9818dc5..9aadd9a 100644 --- a/drivers/iio/proximity/Makefile +++ b/drivers/iio/proximity/Makefile @@ -4,4 +4,5 @@ # When adding new entries keep the list in alphabetical order obj-$(CONFIG_AS3935) += as3935.o +obj-$(CONFIG_LIDAR_LITE_V2) += pulsedlight-lidar-lite-v2.o obj-$(CONFIG_SX9500) += sx9500.o diff --git a/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c b/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c new file mode 100644 index 0000000..185a7ab --- /dev/null +++ b/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c @@ -0,0 +1,288 @@ +/* + * pulsedlight-lidar-lite-v2.c - Support for PulsedLight LIDAR sensor + * + * Copyright (C) 2015 Matt Ranostay + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * TODO: runtime pm, interrupt mode, and signal strength reporting + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LIDAR_REG_CONTROL 0x00 +#define LIDAR_REG_CONTROL_ACQUIRE BIT(2) + +#define LIDAR_REG_STATUS 0x01 +#define LIDAR_REG_STATUS_INVALID BIT(3) +#define LIDAR_REG_STATUS_READY BIT(0) + +#define LIDAR_REG_DATA_HBYTE 0x0f +#define LIDAR_REG_DATA_LBYTE 0x10 + +#define LIDAR_DRV_NAME "lidar" + +struct lidar_data { + struct iio_dev *indio_dev; + struct i2c_client *client; + + u16 buffer[8]; /* 2 byte distance + 8 byte timestamp */ +}; + +static const struct iio_chan_spec lidar_channels[] = { + { + .type = IIO_DISTANCE, + .info_mask_separate = + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + .scan_index = 0, + .scan_type = { + .sign = 'u', + .realbits = 16, + .storagebits = 16, + }, + }, + IIO_CHAN_SOFT_TIMESTAMP(1), +}; + +static int lidar_read_byte(struct lidar_data *data, int reg) +{ + struct i2c_client *client = data->client; + int ret; + + /* + * Device needs a STOP condition between address write, and data read + * so in turn i2c_smbus_read_byte_data cannot be used + */ + + ret = i2c_smbus_write_byte(client, reg); + if (ret < 0) { + dev_err(&client->dev, "cannot write addr value"); + return ret; + } + + ret = i2c_smbus_read_byte(client); + if (ret < 0) + dev_err(&client->dev, "cannot read data value"); + + return ret; +} + +static inline int lidar_write_control(struct lidar_data *data, int val) +{ + return i2c_smbus_write_byte_data(data->client, LIDAR_REG_CONTROL, val); +} + +static int lidar_read_measurement(struct lidar_data *data, u16 *reg) +{ + int ret; + int val; + + ret = lidar_read_byte(data, LIDAR_REG_DATA_HBYTE); + if (ret < 0) + return ret; + val = ret << 8; + + ret = lidar_read_byte(data, LIDAR_REG_DATA_LBYTE); + if (ret < 0) + return ret; + + val |= ret; + *reg = val; + + return 0; +} + +static int lidar_get_measurement(struct lidar_data *data, u16 *reg) +{ + struct i2c_client *client = data->client; + int tries = 10; + int ret; + + /* start sample */ + ret = lidar_write_control(data, LIDAR_REG_CONTROL_ACQUIRE); + if (ret < 0) { + dev_err(&client->dev, "cannot send start measurement command"); + return ret; + } + + while (tries--) { + usleep_range(1000, 2000); + + ret = lidar_read_byte(data, LIDAR_REG_STATUS); + if (ret < 0) + break; + + /* return 0 since laser is likely pointed out of range */ + if (ret & LIDAR_REG_STATUS_INVALID) { + *reg = 0; + ret = 0; + break; + } + + /* sample ready to read */ + if (!(ret & LIDAR_REG_STATUS_READY)) { + ret = lidar_read_measurement(data, reg); + break; + } + ret = -EIO; + } + + return ret; +} + +static int lidar_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct lidar_data *data = iio_priv(indio_dev); + int ret = -EINVAL; + + mutex_lock(&indio_dev->mlock); + + if (iio_buffer_enabled(indio_dev) && mask == IIO_CHAN_INFO_RAW) { + ret = -EBUSY; + goto error_busy; + } + + switch (mask) { + case IIO_CHAN_INFO_RAW: { + u16 reg; + + ret = lidar_get_measurement(data, ®); + if (!ret) { + *val = reg; + ret = IIO_VAL_INT; + } + break; + } + case IIO_CHAN_INFO_SCALE: + *val = 0; + *val2 = 10000; + ret = IIO_VAL_INT_PLUS_MICRO; + break; + } + +error_busy: + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +static irqreturn_t lidar_trigger_handler(int irq, void *private) +{ + struct iio_poll_func *pf = private; + struct iio_dev *indio_dev = pf->indio_dev; + struct lidar_data *data = iio_priv(indio_dev); + int ret; + + ret = lidar_get_measurement(data, data->buffer); + if (!ret) { + iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, + iio_get_time_ns()); + } else { + dev_err(&data->client->dev, "cannot read LIDAR measurement"); + } + + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static const struct iio_info lidar_info = { + .driver_module = THIS_MODULE, + .read_raw = lidar_read_raw, +}; + +static int lidar_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct lidar_data *data; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + indio_dev->info = &lidar_info; + indio_dev->name = LIDAR_DRV_NAME; + indio_dev->channels = lidar_channels; + indio_dev->num_channels = ARRAY_SIZE(lidar_channels); + indio_dev->modes = INDIO_DIRECT_MODE; + + data = iio_priv(indio_dev); + i2c_set_clientdata(client, indio_dev); + + data->client = client; + data->indio_dev = indio_dev; + + ret = iio_triggered_buffer_setup(indio_dev, NULL, + lidar_trigger_handler, NULL); + if (ret) + return ret; + + ret = iio_device_register(indio_dev); + if (ret) + goto error_unreg_buffer; + + return 0; + +error_unreg_buffer: + iio_triggered_buffer_cleanup(indio_dev); + + return ret; +} + +static int lidar_remove(struct i2c_client *client) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(client); + + iio_device_unregister(indio_dev); + iio_triggered_buffer_cleanup(indio_dev); + + return 0; +} + +static const struct i2c_device_id lidar_id[] = { + {"lidar-lite-v2", 0}, + { }, +}; +MODULE_DEVICE_TABLE(i2c, lidar_id); + +static const struct of_device_id lidar_dt_ids[] = { + { .compatible = "pulsedlight,lidar-lite-v2" }, + { } +}; + +static struct i2c_driver lidar_driver = { + .driver = { + .name = LIDAR_DRV_NAME, + .of_match_table = of_match_ptr(lidar_dt_ids), + }, + .probe = lidar_probe, + .remove = lidar_remove, + .id_table = lidar_id, +}; +module_i2c_driver(lidar_driver); + +MODULE_AUTHOR("Matt Ranostay "); +MODULE_DESCRIPTION("PulsedLight LIDAR sensor"); +MODULE_LICENSE("GPL"); -- cgit v1.1 From 13426454b6493a847cebe276fb1ec3a7f1d48d0e Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Wed, 19 Aug 2015 14:12:45 +0200 Subject: iio: bmg160: Separate i2c and core driver This patch separates the core driver using regmap and the i2c driver which creates the i2c regmap. Also in the Kconfig file BMG160 and BMG160_I2C are separate now. Signed-off-by: Markus Pargmann Reviewed-by: Srinivas Pandruvada Signed-off-by: Jonathan Cameron --- drivers/iio/gyro/Kconfig | 15 +- drivers/iio/gyro/Makefile | 3 +- drivers/iio/gyro/bmg160.c | 1249 ---------------------------------------- drivers/iio/gyro/bmg160.h | 10 + drivers/iio/gyro/bmg160_core.c | 1203 ++++++++++++++++++++++++++++++++++++++ drivers/iio/gyro/bmg160_i2c.c | 71 +++ 6 files changed, 1296 insertions(+), 1255 deletions(-) delete mode 100644 drivers/iio/gyro/bmg160.c create mode 100644 drivers/iio/gyro/bmg160.h create mode 100644 drivers/iio/gyro/bmg160_core.c create mode 100644 drivers/iio/gyro/bmg160_i2c.c diff --git a/drivers/iio/gyro/Kconfig b/drivers/iio/gyro/Kconfig index cf82d74..94526b1 100644 --- a/drivers/iio/gyro/Kconfig +++ b/drivers/iio/gyro/Kconfig @@ -52,16 +52,21 @@ config ADXRS450 config BMG160 tristate "BOSCH BMG160 Gyro Sensor" - depends on I2C + depends on (I2C || SPI_MASTER) select IIO_BUFFER select IIO_TRIGGERED_BUFFER - select REGMAP_I2C + select BMG160_I2C if (I2C) help - Say yes here to build support for Bosch BMG160 Tri-axis Gyro Sensor - driver. This driver also supports BMI055 gyroscope. + Say yes here to build support for BOSCH BMG160 Tri-axis Gyro Sensor + driver connected via I2C or SPI. This driver also supports BMI055 + gyroscope. This driver can also be built as a module. If so, the module - will be called bmg160. + will be called bmg160_i2c. + +config BMG160_I2C + tristate + select REGMAP_I2C config HID_SENSOR_GYRO_3D depends on HID_SENSOR_HUB diff --git a/drivers/iio/gyro/Makefile b/drivers/iio/gyro/Makefile index f46341b..6084010 100644 --- a/drivers/iio/gyro/Makefile +++ b/drivers/iio/gyro/Makefile @@ -8,7 +8,8 @@ obj-$(CONFIG_ADIS16130) += adis16130.o obj-$(CONFIG_ADIS16136) += adis16136.o obj-$(CONFIG_ADIS16260) += adis16260.o obj-$(CONFIG_ADXRS450) += adxrs450.o -obj-$(CONFIG_BMG160) += bmg160.o +obj-$(CONFIG_BMG160) += bmg160_core.o +obj-$(CONFIG_BMG160_I2C) += bmg160_i2c.o obj-$(CONFIG_HID_SENSOR_GYRO_3D) += hid-sensor-gyro-3d.o diff --git a/drivers/iio/gyro/bmg160.c b/drivers/iio/gyro/bmg160.c deleted file mode 100644 index ca12402..0000000 --- a/drivers/iio/gyro/bmg160.c +++ /dev/null @@ -1,1249 +0,0 @@ -/* - * BMG160 Gyro Sensor driver - * Copyright (c) 2014, Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define BMG160_DRV_NAME "bmg160" -#define BMG160_IRQ_NAME "bmg160_event" -#define BMG160_GPIO_NAME "gpio_int" - -#define BMG160_REG_CHIP_ID 0x00 -#define BMG160_CHIP_ID_VAL 0x0F - -#define BMG160_REG_PMU_LPW 0x11 -#define BMG160_MODE_NORMAL 0x00 -#define BMG160_MODE_DEEP_SUSPEND 0x20 -#define BMG160_MODE_SUSPEND 0x80 - -#define BMG160_REG_RANGE 0x0F - -#define BMG160_RANGE_2000DPS 0 -#define BMG160_RANGE_1000DPS 1 -#define BMG160_RANGE_500DPS 2 -#define BMG160_RANGE_250DPS 3 -#define BMG160_RANGE_125DPS 4 - -#define BMG160_REG_PMU_BW 0x10 -#define BMG160_NO_FILTER 0 -#define BMG160_DEF_BW 100 - -#define BMG160_REG_INT_MAP_0 0x17 -#define BMG160_INT_MAP_0_BIT_ANY BIT(1) - -#define BMG160_REG_INT_MAP_1 0x18 -#define BMG160_INT_MAP_1_BIT_NEW_DATA BIT(0) - -#define BMG160_REG_INT_RST_LATCH 0x21 -#define BMG160_INT_MODE_LATCH_RESET 0x80 -#define BMG160_INT_MODE_LATCH_INT 0x0F -#define BMG160_INT_MODE_NON_LATCH_INT 0x00 - -#define BMG160_REG_INT_EN_0 0x15 -#define BMG160_DATA_ENABLE_INT BIT(7) - -#define BMG160_REG_INT_EN_1 0x16 -#define BMG160_INT1_BIT_OD BIT(1) - -#define BMG160_REG_XOUT_L 0x02 -#define BMG160_AXIS_TO_REG(axis) (BMG160_REG_XOUT_L + (axis * 2)) - -#define BMG160_REG_SLOPE_THRES 0x1B -#define BMG160_SLOPE_THRES_MASK 0x0F - -#define BMG160_REG_MOTION_INTR 0x1C -#define BMG160_INT_MOTION_X BIT(0) -#define BMG160_INT_MOTION_Y BIT(1) -#define BMG160_INT_MOTION_Z BIT(2) -#define BMG160_ANY_DUR_MASK 0x30 -#define BMG160_ANY_DUR_SHIFT 4 - -#define BMG160_REG_INT_STATUS_2 0x0B -#define BMG160_ANY_MOTION_MASK 0x07 -#define BMG160_ANY_MOTION_BIT_X BIT(0) -#define BMG160_ANY_MOTION_BIT_Y BIT(1) -#define BMG160_ANY_MOTION_BIT_Z BIT(2) - -#define BMG160_REG_TEMP 0x08 -#define BMG160_TEMP_CENTER_VAL 23 - -#define BMG160_MAX_STARTUP_TIME_MS 80 - -#define BMG160_AUTO_SUSPEND_DELAY_MS 2000 - -struct bmg160_data { - struct device *dev; - struct regmap *regmap; - struct iio_trigger *dready_trig; - struct iio_trigger *motion_trig; - struct mutex mutex; - s16 buffer[8]; - u8 bw_bits; - u32 dps_range; - int ev_enable_state; - int slope_thres; - bool dready_trigger_on; - bool motion_trigger_on; - int irq; -}; - -enum bmg160_axis { - AXIS_X, - AXIS_Y, - AXIS_Z, -}; - -static const struct { - int val; - int bw_bits; -} bmg160_samp_freq_table[] = { {100, 0x07}, - {200, 0x06}, - {400, 0x03}, - {1000, 0x02}, - {2000, 0x01} }; - -static const struct { - int scale; - int dps_range; -} bmg160_scale_table[] = { { 1065, BMG160_RANGE_2000DPS}, - { 532, BMG160_RANGE_1000DPS}, - { 266, BMG160_RANGE_500DPS}, - { 133, BMG160_RANGE_250DPS}, - { 66, BMG160_RANGE_125DPS} }; - -static struct regmap_config bmg160_regmap_i2c_conf = { - .reg_bits = 8, - .val_bits = 8, - .max_register = 0x3f -}; - -static int bmg160_set_mode(struct bmg160_data *data, u8 mode) -{ - int ret; - - ret = regmap_write(data->regmap, BMG160_REG_PMU_LPW, mode); - if (ret < 0) { - dev_err(data->dev, "Error writing reg_pmu_lpw\n"); - return ret; - } - - return 0; -} - -static int bmg160_convert_freq_to_bit(int val) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(bmg160_samp_freq_table); ++i) { - if (bmg160_samp_freq_table[i].val == val) - return bmg160_samp_freq_table[i].bw_bits; - } - - return -EINVAL; -} - -static int bmg160_set_bw(struct bmg160_data *data, int val) -{ - int ret; - int bw_bits; - - bw_bits = bmg160_convert_freq_to_bit(val); - if (bw_bits < 0) - return bw_bits; - - ret = regmap_write(data->regmap, BMG160_REG_PMU_BW, bw_bits); - if (ret < 0) { - dev_err(data->dev, "Error writing reg_pmu_bw\n"); - return ret; - } - - data->bw_bits = bw_bits; - - return 0; -} - -static int bmg160_chip_init(struct bmg160_data *data) -{ - int ret; - unsigned int val; - - ret = regmap_read(data->regmap, BMG160_REG_CHIP_ID, &val); - if (ret < 0) { - dev_err(data->dev, "Error reading reg_chip_id\n"); - return ret; - } - - dev_dbg(data->dev, "Chip Id %x\n", val); - if (val != BMG160_CHIP_ID_VAL) { - dev_err(data->dev, "invalid chip %x\n", val); - return -ENODEV; - } - - ret = bmg160_set_mode(data, BMG160_MODE_NORMAL); - if (ret < 0) - return ret; - - /* Wait upto 500 ms to be ready after changing mode */ - usleep_range(500, 1000); - - /* Set Bandwidth */ - ret = bmg160_set_bw(data, BMG160_DEF_BW); - if (ret < 0) - return ret; - - /* Set Default Range */ - ret = regmap_write(data->regmap, BMG160_REG_RANGE, BMG160_RANGE_500DPS); - if (ret < 0) { - dev_err(data->dev, "Error writing reg_range\n"); - return ret; - } - data->dps_range = BMG160_RANGE_500DPS; - - ret = regmap_read(data->regmap, BMG160_REG_SLOPE_THRES, &val); - if (ret < 0) { - dev_err(data->dev, "Error reading reg_slope_thres\n"); - return ret; - } - data->slope_thres = val; - - /* Set default interrupt mode */ - ret = regmap_update_bits(data->regmap, BMG160_REG_INT_EN_1, - BMG160_INT1_BIT_OD, 0); - if (ret < 0) { - dev_err(data->dev, "Error updating bits in reg_int_en_1\n"); - return ret; - } - - ret = regmap_write(data->regmap, BMG160_REG_INT_RST_LATCH, - BMG160_INT_MODE_LATCH_INT | - BMG160_INT_MODE_LATCH_RESET); - if (ret < 0) { - dev_err(data->dev, - "Error writing reg_motion_intr\n"); - return ret; - } - - return 0; -} - -static int bmg160_set_power_state(struct bmg160_data *data, bool on) -{ -#ifdef CONFIG_PM - int ret; - - if (on) - ret = pm_runtime_get_sync(data->dev); - else { - pm_runtime_mark_last_busy(data->dev); - ret = pm_runtime_put_autosuspend(data->dev); - } - - if (ret < 0) { - dev_err(data->dev, - "Failed: bmg160_set_power_state for %d\n", on); - if (on) - pm_runtime_put_noidle(data->dev); - - return ret; - } -#endif - - return 0; -} - -static int bmg160_setup_any_motion_interrupt(struct bmg160_data *data, - bool status) -{ - int ret; - - /* Enable/Disable INT_MAP0 mapping */ - ret = regmap_update_bits(data->regmap, BMG160_REG_INT_MAP_0, - BMG160_INT_MAP_0_BIT_ANY, - (status ? BMG160_INT_MAP_0_BIT_ANY : 0)); - if (ret < 0) { - dev_err(data->dev, "Error updating bits reg_int_map0\n"); - return ret; - } - - /* Enable/Disable slope interrupts */ - if (status) { - /* Update slope thres */ - ret = regmap_write(data->regmap, BMG160_REG_SLOPE_THRES, - data->slope_thres); - if (ret < 0) { - dev_err(data->dev, - "Error writing reg_slope_thres\n"); - return ret; - } - - ret = regmap_write(data->regmap, BMG160_REG_MOTION_INTR, - BMG160_INT_MOTION_X | BMG160_INT_MOTION_Y | - BMG160_INT_MOTION_Z); - if (ret < 0) { - dev_err(data->dev, - "Error writing reg_motion_intr\n"); - return ret; - } - - /* - * New data interrupt is always non-latched, - * which will have higher priority, so no need - * to set latched mode, we will be flooded anyway with INTR - */ - if (!data->dready_trigger_on) { - ret = regmap_write(data->regmap, - BMG160_REG_INT_RST_LATCH, - BMG160_INT_MODE_LATCH_INT | - BMG160_INT_MODE_LATCH_RESET); - if (ret < 0) { - dev_err(data->dev, - "Error writing reg_rst_latch\n"); - return ret; - } - } - - ret = regmap_write(data->regmap, BMG160_REG_INT_EN_0, - BMG160_DATA_ENABLE_INT); - - } else { - ret = regmap_write(data->regmap, BMG160_REG_INT_EN_0, 0); - } - - if (ret < 0) { - dev_err(data->dev, "Error writing reg_int_en0\n"); - return ret; - } - - return 0; -} - -static int bmg160_setup_new_data_interrupt(struct bmg160_data *data, - bool status) -{ - int ret; - - /* Enable/Disable INT_MAP1 mapping */ - ret = regmap_update_bits(data->regmap, BMG160_REG_INT_MAP_1, - BMG160_INT_MAP_1_BIT_NEW_DATA, - (status ? BMG160_INT_MAP_1_BIT_NEW_DATA : 0)); - if (ret < 0) { - dev_err(data->dev, "Error updating bits in reg_int_map1\n"); - return ret; - } - - if (status) { - ret = regmap_write(data->regmap, BMG160_REG_INT_RST_LATCH, - BMG160_INT_MODE_NON_LATCH_INT | - BMG160_INT_MODE_LATCH_RESET); - if (ret < 0) { - dev_err(data->dev, - "Error writing reg_rst_latch\n"); - return ret; - } - - ret = regmap_write(data->regmap, BMG160_REG_INT_EN_0, - BMG160_DATA_ENABLE_INT); - - } else { - /* Restore interrupt mode */ - ret = regmap_write(data->regmap, BMG160_REG_INT_RST_LATCH, - BMG160_INT_MODE_LATCH_INT | - BMG160_INT_MODE_LATCH_RESET); - if (ret < 0) { - dev_err(data->dev, - "Error writing reg_rst_latch\n"); - return ret; - } - - ret = regmap_write(data->regmap, BMG160_REG_INT_EN_0, 0); - } - - if (ret < 0) { - dev_err(data->dev, "Error writing reg_int_en0\n"); - return ret; - } - - return 0; -} - -static int bmg160_get_bw(struct bmg160_data *data, int *val) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(bmg160_samp_freq_table); ++i) { - if (bmg160_samp_freq_table[i].bw_bits == data->bw_bits) { - *val = bmg160_samp_freq_table[i].val; - return IIO_VAL_INT; - } - } - - return -EINVAL; -} - -static int bmg160_set_scale(struct bmg160_data *data, int val) -{ - int ret, i; - - for (i = 0; i < ARRAY_SIZE(bmg160_scale_table); ++i) { - if (bmg160_scale_table[i].scale == val) { - ret = regmap_write(data->regmap, BMG160_REG_RANGE, - bmg160_scale_table[i].dps_range); - if (ret < 0) { - dev_err(data->dev, - "Error writing reg_range\n"); - return ret; - } - data->dps_range = bmg160_scale_table[i].dps_range; - return 0; - } - } - - return -EINVAL; -} - -static int bmg160_get_temp(struct bmg160_data *data, int *val) -{ - int ret; - unsigned int raw_val; - - mutex_lock(&data->mutex); - ret = bmg160_set_power_state(data, true); - if (ret < 0) { - mutex_unlock(&data->mutex); - return ret; - } - - ret = regmap_read(data->regmap, BMG160_REG_TEMP, &raw_val); - if (ret < 0) { - dev_err(data->dev, "Error reading reg_temp\n"); - bmg160_set_power_state(data, false); - mutex_unlock(&data->mutex); - return ret; - } - - *val = sign_extend32(raw_val, 7); - ret = bmg160_set_power_state(data, false); - mutex_unlock(&data->mutex); - if (ret < 0) - return ret; - - return IIO_VAL_INT; -} - -static int bmg160_get_axis(struct bmg160_data *data, int axis, int *val) -{ - int ret; - unsigned int raw_val; - - mutex_lock(&data->mutex); - ret = bmg160_set_power_state(data, true); - if (ret < 0) { - mutex_unlock(&data->mutex); - return ret; - } - - ret = regmap_bulk_read(data->regmap, BMG160_AXIS_TO_REG(axis), &raw_val, - 2); - if (ret < 0) { - dev_err(data->dev, "Error reading axis %d\n", axis); - bmg160_set_power_state(data, false); - mutex_unlock(&data->mutex); - return ret; - } - - *val = sign_extend32(raw_val, 15); - ret = bmg160_set_power_state(data, false); - mutex_unlock(&data->mutex); - if (ret < 0) - return ret; - - return IIO_VAL_INT; -} - -static int bmg160_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int *val, int *val2, long mask) -{ - struct bmg160_data *data = iio_priv(indio_dev); - int ret; - - switch (mask) { - case IIO_CHAN_INFO_RAW: - switch (chan->type) { - case IIO_TEMP: - return bmg160_get_temp(data, val); - case IIO_ANGL_VEL: - if (iio_buffer_enabled(indio_dev)) - return -EBUSY; - else - return bmg160_get_axis(data, chan->scan_index, - val); - default: - return -EINVAL; - } - case IIO_CHAN_INFO_OFFSET: - if (chan->type == IIO_TEMP) { - *val = BMG160_TEMP_CENTER_VAL; - return IIO_VAL_INT; - } else - return -EINVAL; - case IIO_CHAN_INFO_SCALE: - *val = 0; - switch (chan->type) { - case IIO_TEMP: - *val2 = 500000; - return IIO_VAL_INT_PLUS_MICRO; - case IIO_ANGL_VEL: - { - int i; - - for (i = 0; i < ARRAY_SIZE(bmg160_scale_table); ++i) { - if (bmg160_scale_table[i].dps_range == - data->dps_range) { - *val2 = bmg160_scale_table[i].scale; - return IIO_VAL_INT_PLUS_MICRO; - } - } - return -EINVAL; - } - default: - return -EINVAL; - } - case IIO_CHAN_INFO_SAMP_FREQ: - *val2 = 0; - mutex_lock(&data->mutex); - ret = bmg160_get_bw(data, val); - mutex_unlock(&data->mutex); - return ret; - default: - return -EINVAL; - } -} - -static int bmg160_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int val, int val2, long mask) -{ - struct bmg160_data *data = iio_priv(indio_dev); - int ret; - - switch (mask) { - case IIO_CHAN_INFO_SAMP_FREQ: - mutex_lock(&data->mutex); - /* - * Section 4.2 of spec - * In suspend mode, the only supported operations are reading - * registers as well as writing to the (0x14) softreset - * register. Since we will be in suspend mode by default, change - * mode to power on for other writes. - */ - ret = bmg160_set_power_state(data, true); - if (ret < 0) { - mutex_unlock(&data->mutex); - return ret; - } - ret = bmg160_set_bw(data, val); - if (ret < 0) { - bmg160_set_power_state(data, false); - mutex_unlock(&data->mutex); - return ret; - } - ret = bmg160_set_power_state(data, false); - mutex_unlock(&data->mutex); - return ret; - case IIO_CHAN_INFO_SCALE: - if (val) - return -EINVAL; - - mutex_lock(&data->mutex); - /* Refer to comments above for the suspend mode ops */ - ret = bmg160_set_power_state(data, true); - if (ret < 0) { - mutex_unlock(&data->mutex); - return ret; - } - ret = bmg160_set_scale(data, val2); - if (ret < 0) { - bmg160_set_power_state(data, false); - mutex_unlock(&data->mutex); - return ret; - } - ret = bmg160_set_power_state(data, false); - mutex_unlock(&data->mutex); - return ret; - default: - return -EINVAL; - } - - return -EINVAL; -} - -static int bmg160_read_event(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan, - enum iio_event_type type, - enum iio_event_direction dir, - enum iio_event_info info, - int *val, int *val2) -{ - struct bmg160_data *data = iio_priv(indio_dev); - - *val2 = 0; - switch (info) { - case IIO_EV_INFO_VALUE: - *val = data->slope_thres & BMG160_SLOPE_THRES_MASK; - break; - default: - return -EINVAL; - } - - return IIO_VAL_INT; -} - -static int bmg160_write_event(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan, - enum iio_event_type type, - enum iio_event_direction dir, - enum iio_event_info info, - int val, int val2) -{ - struct bmg160_data *data = iio_priv(indio_dev); - - switch (info) { - case IIO_EV_INFO_VALUE: - if (data->ev_enable_state) - return -EBUSY; - data->slope_thres &= ~BMG160_SLOPE_THRES_MASK; - data->slope_thres |= (val & BMG160_SLOPE_THRES_MASK); - break; - default: - return -EINVAL; - } - - return 0; -} - -static int bmg160_read_event_config(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan, - enum iio_event_type type, - enum iio_event_direction dir) -{ - - struct bmg160_data *data = iio_priv(indio_dev); - - return data->ev_enable_state; -} - -static int bmg160_write_event_config(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan, - enum iio_event_type type, - enum iio_event_direction dir, - int state) -{ - struct bmg160_data *data = iio_priv(indio_dev); - int ret; - - if (state && data->ev_enable_state) - return 0; - - mutex_lock(&data->mutex); - - if (!state && data->motion_trigger_on) { - data->ev_enable_state = 0; - mutex_unlock(&data->mutex); - return 0; - } - /* - * We will expect the enable and disable to do operation in - * in reverse order. This will happen here anyway as our - * resume operation uses sync mode runtime pm calls, the - * suspend operation will be delayed by autosuspend delay - * So the disable operation will still happen in reverse of - * enable operation. When runtime pm is disabled the mode - * is always on so sequence doesn't matter - */ - ret = bmg160_set_power_state(data, state); - if (ret < 0) { - mutex_unlock(&data->mutex); - return ret; - } - - ret = bmg160_setup_any_motion_interrupt(data, state); - if (ret < 0) { - bmg160_set_power_state(data, false); - mutex_unlock(&data->mutex); - return ret; - } - - data->ev_enable_state = state; - mutex_unlock(&data->mutex); - - return 0; -} - -static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("100 200 400 1000 2000"); - -static IIO_CONST_ATTR(in_anglvel_scale_available, - "0.001065 0.000532 0.000266 0.000133 0.000066"); - -static struct attribute *bmg160_attributes[] = { - &iio_const_attr_sampling_frequency_available.dev_attr.attr, - &iio_const_attr_in_anglvel_scale_available.dev_attr.attr, - NULL, -}; - -static const struct attribute_group bmg160_attrs_group = { - .attrs = bmg160_attributes, -}; - -static const struct iio_event_spec bmg160_event = { - .type = IIO_EV_TYPE_ROC, - .dir = IIO_EV_DIR_EITHER, - .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) | - BIT(IIO_EV_INFO_ENABLE) -}; - -#define BMG160_CHANNEL(_axis) { \ - .type = IIO_ANGL_VEL, \ - .modified = 1, \ - .channel2 = IIO_MOD_##_axis, \ - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ - .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ - BIT(IIO_CHAN_INFO_SAMP_FREQ), \ - .scan_index = AXIS_##_axis, \ - .scan_type = { \ - .sign = 's', \ - .realbits = 16, \ - .storagebits = 16, \ - }, \ - .event_spec = &bmg160_event, \ - .num_event_specs = 1 \ -} - -static const struct iio_chan_spec bmg160_channels[] = { - { - .type = IIO_TEMP, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | - BIT(IIO_CHAN_INFO_SCALE) | - BIT(IIO_CHAN_INFO_OFFSET), - .scan_index = -1, - }, - BMG160_CHANNEL(X), - BMG160_CHANNEL(Y), - BMG160_CHANNEL(Z), - IIO_CHAN_SOFT_TIMESTAMP(3), -}; - -static const struct iio_info bmg160_info = { - .attrs = &bmg160_attrs_group, - .read_raw = bmg160_read_raw, - .write_raw = bmg160_write_raw, - .read_event_value = bmg160_read_event, - .write_event_value = bmg160_write_event, - .write_event_config = bmg160_write_event_config, - .read_event_config = bmg160_read_event_config, - .driver_module = THIS_MODULE, -}; - -static irqreturn_t bmg160_trigger_handler(int irq, void *p) -{ - struct iio_poll_func *pf = p; - struct iio_dev *indio_dev = pf->indio_dev; - struct bmg160_data *data = iio_priv(indio_dev); - int bit, ret, i = 0; - unsigned int val; - - mutex_lock(&data->mutex); - for_each_set_bit(bit, indio_dev->active_scan_mask, - indio_dev->masklength) { - ret = regmap_bulk_read(data->regmap, BMG160_AXIS_TO_REG(bit), - &val, 2); - if (ret < 0) { - mutex_unlock(&data->mutex); - goto err; - } - data->buffer[i++] = ret; - } - mutex_unlock(&data->mutex); - - iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, - pf->timestamp); -err: - iio_trigger_notify_done(indio_dev->trig); - - return IRQ_HANDLED; -} - -static int bmg160_trig_try_reen(struct iio_trigger *trig) -{ - struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); - struct bmg160_data *data = iio_priv(indio_dev); - int ret; - - /* new data interrupts don't need ack */ - if (data->dready_trigger_on) - return 0; - - /* Set latched mode interrupt and clear any latched interrupt */ - ret = regmap_write(data->regmap, BMG160_REG_INT_RST_LATCH, - BMG160_INT_MODE_LATCH_INT | - BMG160_INT_MODE_LATCH_RESET); - if (ret < 0) { - dev_err(data->dev, "Error writing reg_rst_latch\n"); - return ret; - } - - return 0; -} - -static int bmg160_data_rdy_trigger_set_state(struct iio_trigger *trig, - bool state) -{ - struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); - struct bmg160_data *data = iio_priv(indio_dev); - int ret; - - mutex_lock(&data->mutex); - - if (!state && data->ev_enable_state && data->motion_trigger_on) { - data->motion_trigger_on = false; - mutex_unlock(&data->mutex); - return 0; - } - - /* - * Refer to comment in bmg160_write_event_config for - * enable/disable operation order - */ - ret = bmg160_set_power_state(data, state); - if (ret < 0) { - mutex_unlock(&data->mutex); - return ret; - } - if (data->motion_trig == trig) - ret = bmg160_setup_any_motion_interrupt(data, state); - else - ret = bmg160_setup_new_data_interrupt(data, state); - if (ret < 0) { - bmg160_set_power_state(data, false); - mutex_unlock(&data->mutex); - return ret; - } - if (data->motion_trig == trig) - data->motion_trigger_on = state; - else - data->dready_trigger_on = state; - - mutex_unlock(&data->mutex); - - return 0; -} - -static const struct iio_trigger_ops bmg160_trigger_ops = { - .set_trigger_state = bmg160_data_rdy_trigger_set_state, - .try_reenable = bmg160_trig_try_reen, - .owner = THIS_MODULE, -}; - -static irqreturn_t bmg160_event_handler(int irq, void *private) -{ - struct iio_dev *indio_dev = private; - struct bmg160_data *data = iio_priv(indio_dev); - int ret; - int dir; - unsigned int val; - - ret = regmap_read(data->regmap, BMG160_REG_INT_STATUS_2, &val); - if (ret < 0) { - dev_err(data->dev, "Error reading reg_int_status2\n"); - goto ack_intr_status; - } - - if (val & 0x08) - dir = IIO_EV_DIR_RISING; - else - dir = IIO_EV_DIR_FALLING; - - if (val & BMG160_ANY_MOTION_BIT_X) - iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ANGL_VEL, - 0, - IIO_MOD_X, - IIO_EV_TYPE_ROC, - dir), - iio_get_time_ns()); - if (val & BMG160_ANY_MOTION_BIT_Y) - iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ANGL_VEL, - 0, - IIO_MOD_Y, - IIO_EV_TYPE_ROC, - dir), - iio_get_time_ns()); - if (val & BMG160_ANY_MOTION_BIT_Z) - iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ANGL_VEL, - 0, - IIO_MOD_Z, - IIO_EV_TYPE_ROC, - dir), - iio_get_time_ns()); - -ack_intr_status: - if (!data->dready_trigger_on) { - ret = regmap_write(data->regmap, BMG160_REG_INT_RST_LATCH, - BMG160_INT_MODE_LATCH_INT | - BMG160_INT_MODE_LATCH_RESET); - if (ret < 0) - dev_err(data->dev, - "Error writing reg_rst_latch\n"); - } - - return IRQ_HANDLED; -} - -static irqreturn_t bmg160_data_rdy_trig_poll(int irq, void *private) -{ - struct iio_dev *indio_dev = private; - struct bmg160_data *data = iio_priv(indio_dev); - - if (data->dready_trigger_on) - iio_trigger_poll(data->dready_trig); - else if (data->motion_trigger_on) - iio_trigger_poll(data->motion_trig); - - if (data->ev_enable_state) - return IRQ_WAKE_THREAD; - else - return IRQ_HANDLED; - -} - -static int bmg160_buffer_preenable(struct iio_dev *indio_dev) -{ - struct bmg160_data *data = iio_priv(indio_dev); - - return bmg160_set_power_state(data, true); -} - -static int bmg160_buffer_postdisable(struct iio_dev *indio_dev) -{ - struct bmg160_data *data = iio_priv(indio_dev); - - return bmg160_set_power_state(data, false); -} - -static const struct iio_buffer_setup_ops bmg160_buffer_setup_ops = { - .preenable = bmg160_buffer_preenable, - .postenable = iio_triggered_buffer_postenable, - .predisable = iio_triggered_buffer_predisable, - .postdisable = bmg160_buffer_postdisable, -}; - -static int bmg160_gpio_probe(struct bmg160_data *data) - -{ - struct device *dev; - struct gpio_desc *gpio; - - dev = data->dev; - - /* data ready gpio interrupt pin */ - gpio = devm_gpiod_get_index(dev, BMG160_GPIO_NAME, 0, GPIOD_IN); - if (IS_ERR(gpio)) { - dev_err(dev, "acpi gpio get index failed\n"); - return PTR_ERR(gpio); - } - - data->irq = gpiod_to_irq(gpio); - - dev_dbg(dev, "GPIO resource, no:%d irq:%d\n", desc_to_gpio(gpio), - data->irq); - - return 0; -} - -static const char *bmg160_match_acpi_device(struct device *dev) -{ - const struct acpi_device_id *id; - - id = acpi_match_device(dev->driver->acpi_match_table, dev); - if (!id) - return NULL; - - return dev_name(dev); -} - -static int bmg160_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct bmg160_data *data; - struct iio_dev *indio_dev; - int ret; - const char *name = NULL; - struct regmap *regmap; - struct device *dev = &client->dev; - - regmap = devm_regmap_init_i2c(client, &bmg160_regmap_i2c_conf); - if (IS_ERR(regmap)) { - dev_err(&client->dev, "Failed to register i2c regmap %d\n", - (int)PTR_ERR(regmap)); - return PTR_ERR(regmap); - } - - indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); - if (!indio_dev) - return -ENOMEM; - - data = iio_priv(indio_dev); - dev_set_drvdata(dev, indio_dev); - data->dev = dev; - data->irq = client->irq; - - ret = bmg160_chip_init(data); - if (ret < 0) - return ret; - - mutex_init(&data->mutex); - - if (id) - name = id->name; - - if (ACPI_HANDLE(dev)) - name = bmg160_match_acpi_device(dev); - - indio_dev->dev.parent = dev; - indio_dev->channels = bmg160_channels; - indio_dev->num_channels = ARRAY_SIZE(bmg160_channels); - indio_dev->name = name; - indio_dev->modes = INDIO_DIRECT_MODE; - indio_dev->info = &bmg160_info; - - if (data->irq <= 0) - bmg160_gpio_probe(data); - - if (data->irq > 0) { - ret = devm_request_threaded_irq(dev, - data->irq, - bmg160_data_rdy_trig_poll, - bmg160_event_handler, - IRQF_TRIGGER_RISING, - BMG160_IRQ_NAME, - indio_dev); - if (ret) - return ret; - - data->dready_trig = devm_iio_trigger_alloc(dev, - "%s-dev%d", - indio_dev->name, - indio_dev->id); - if (!data->dready_trig) - return -ENOMEM; - - data->motion_trig = devm_iio_trigger_alloc(dev, - "%s-any-motion-dev%d", - indio_dev->name, - indio_dev->id); - if (!data->motion_trig) - return -ENOMEM; - - data->dready_trig->dev.parent = dev; - data->dready_trig->ops = &bmg160_trigger_ops; - iio_trigger_set_drvdata(data->dready_trig, indio_dev); - ret = iio_trigger_register(data->dready_trig); - if (ret) - return ret; - - data->motion_trig->dev.parent = dev; - data->motion_trig->ops = &bmg160_trigger_ops; - iio_trigger_set_drvdata(data->motion_trig, indio_dev); - ret = iio_trigger_register(data->motion_trig); - if (ret) { - data->motion_trig = NULL; - goto err_trigger_unregister; - } - } - - ret = iio_triggered_buffer_setup(indio_dev, - iio_pollfunc_store_time, - bmg160_trigger_handler, - &bmg160_buffer_setup_ops); - if (ret < 0) { - dev_err(dev, - "iio triggered buffer setup failed\n"); - goto err_trigger_unregister; - } - - ret = iio_device_register(indio_dev); - if (ret < 0) { - dev_err(dev, "unable to register iio device\n"); - goto err_buffer_cleanup; - } - - ret = pm_runtime_set_active(dev); - if (ret) - goto err_iio_unregister; - - pm_runtime_enable(dev); - pm_runtime_set_autosuspend_delay(dev, - BMG160_AUTO_SUSPEND_DELAY_MS); - pm_runtime_use_autosuspend(dev); - - return 0; - -err_iio_unregister: - iio_device_unregister(indio_dev); -err_buffer_cleanup: - iio_triggered_buffer_cleanup(indio_dev); -err_trigger_unregister: - if (data->dready_trig) - iio_trigger_unregister(data->dready_trig); - if (data->motion_trig) - iio_trigger_unregister(data->motion_trig); - - return ret; -} - -static int bmg160_remove(struct i2c_client *client) -{ - struct iio_dev *indio_dev = i2c_get_clientdata(client); - struct bmg160_data *data = iio_priv(indio_dev); - - pm_runtime_disable(&client->dev); - pm_runtime_set_suspended(&client->dev); - pm_runtime_put_noidle(&client->dev); - - iio_device_unregister(indio_dev); - iio_triggered_buffer_cleanup(indio_dev); - - if (data->dready_trig) { - iio_trigger_unregister(data->dready_trig); - iio_trigger_unregister(data->motion_trig); - } - - mutex_lock(&data->mutex); - bmg160_set_mode(data, BMG160_MODE_DEEP_SUSPEND); - mutex_unlock(&data->mutex); - - return 0; -} - -#ifdef CONFIG_PM_SLEEP -static int bmg160_suspend(struct device *dev) -{ - struct iio_dev *indio_dev = dev_get_drvdata(dev); - struct bmg160_data *data = iio_priv(indio_dev); - - mutex_lock(&data->mutex); - bmg160_set_mode(data, BMG160_MODE_SUSPEND); - mutex_unlock(&data->mutex); - - return 0; -} - -static int bmg160_resume(struct device *dev) -{ - struct iio_dev *indio_dev = dev_get_drvdata(dev); - struct bmg160_data *data = iio_priv(indio_dev); - - mutex_lock(&data->mutex); - if (data->dready_trigger_on || data->motion_trigger_on || - data->ev_enable_state) - bmg160_set_mode(data, BMG160_MODE_NORMAL); - mutex_unlock(&data->mutex); - - return 0; -} -#endif - -#ifdef CONFIG_PM -static int bmg160_runtime_suspend(struct device *dev) -{ - struct iio_dev *indio_dev = dev_get_drvdata(dev); - struct bmg160_data *data = iio_priv(indio_dev); - int ret; - - ret = bmg160_set_mode(data, BMG160_MODE_SUSPEND); - if (ret < 0) { - dev_err(data->dev, "set mode failed\n"); - return -EAGAIN; - } - - return 0; -} - -static int bmg160_runtime_resume(struct device *dev) -{ - struct iio_dev *indio_dev = dev_get_drvdata(dev); - struct bmg160_data *data = iio_priv(indio_dev); - int ret; - - ret = bmg160_set_mode(data, BMG160_MODE_NORMAL); - if (ret < 0) - return ret; - - msleep_interruptible(BMG160_MAX_STARTUP_TIME_MS); - - return 0; -} -#endif - -static const struct dev_pm_ops bmg160_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(bmg160_suspend, bmg160_resume) - SET_RUNTIME_PM_OPS(bmg160_runtime_suspend, - bmg160_runtime_resume, NULL) -}; - -static const struct acpi_device_id bmg160_acpi_match[] = { - {"BMG0160", 0}, - {"BMI055B", 0}, - {}, -}; - -MODULE_DEVICE_TABLE(acpi, bmg160_acpi_match); - -static const struct i2c_device_id bmg160_id[] = { - {"bmg160", 0}, - {"bmi055_gyro", 0}, - {} -}; - -MODULE_DEVICE_TABLE(i2c, bmg160_id); - -static struct i2c_driver bmg160_driver = { - .driver = { - .name = BMG160_DRV_NAME, - .acpi_match_table = ACPI_PTR(bmg160_acpi_match), - .pm = &bmg160_pm_ops, - }, - .probe = bmg160_probe, - .remove = bmg160_remove, - .id_table = bmg160_id, -}; -module_i2c_driver(bmg160_driver); - -MODULE_AUTHOR("Srinivas Pandruvada "); -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("BMG160 Gyro driver"); diff --git a/drivers/iio/gyro/bmg160.h b/drivers/iio/gyro/bmg160.h new file mode 100644 index 0000000..72db723 --- /dev/null +++ b/drivers/iio/gyro/bmg160.h @@ -0,0 +1,10 @@ +#ifndef BMG160_H_ +#define BMG160_H_ + +extern const struct dev_pm_ops bmg160_pm_ops; + +int bmg160_core_probe(struct device *dev, struct regmap *regmap, int irq, + const char *name); +void bmg160_core_remove(struct device *dev); + +#endif /* BMG160_H_ */ diff --git a/drivers/iio/gyro/bmg160_core.c b/drivers/iio/gyro/bmg160_core.c new file mode 100644 index 0000000..02ff789 --- /dev/null +++ b/drivers/iio/gyro/bmg160_core.c @@ -0,0 +1,1203 @@ +/* + * BMG160 Gyro Sensor driver + * Copyright (c) 2014, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "bmg160.h" + +#define BMG160_IRQ_NAME "bmg160_event" +#define BMG160_GPIO_NAME "gpio_int" + +#define BMG160_REG_CHIP_ID 0x00 +#define BMG160_CHIP_ID_VAL 0x0F + +#define BMG160_REG_PMU_LPW 0x11 +#define BMG160_MODE_NORMAL 0x00 +#define BMG160_MODE_DEEP_SUSPEND 0x20 +#define BMG160_MODE_SUSPEND 0x80 + +#define BMG160_REG_RANGE 0x0F + +#define BMG160_RANGE_2000DPS 0 +#define BMG160_RANGE_1000DPS 1 +#define BMG160_RANGE_500DPS 2 +#define BMG160_RANGE_250DPS 3 +#define BMG160_RANGE_125DPS 4 + +#define BMG160_REG_PMU_BW 0x10 +#define BMG160_NO_FILTER 0 +#define BMG160_DEF_BW 100 + +#define BMG160_REG_INT_MAP_0 0x17 +#define BMG160_INT_MAP_0_BIT_ANY BIT(1) + +#define BMG160_REG_INT_MAP_1 0x18 +#define BMG160_INT_MAP_1_BIT_NEW_DATA BIT(0) + +#define BMG160_REG_INT_RST_LATCH 0x21 +#define BMG160_INT_MODE_LATCH_RESET 0x80 +#define BMG160_INT_MODE_LATCH_INT 0x0F +#define BMG160_INT_MODE_NON_LATCH_INT 0x00 + +#define BMG160_REG_INT_EN_0 0x15 +#define BMG160_DATA_ENABLE_INT BIT(7) + +#define BMG160_REG_INT_EN_1 0x16 +#define BMG160_INT1_BIT_OD BIT(1) + +#define BMG160_REG_XOUT_L 0x02 +#define BMG160_AXIS_TO_REG(axis) (BMG160_REG_XOUT_L + (axis * 2)) + +#define BMG160_REG_SLOPE_THRES 0x1B +#define BMG160_SLOPE_THRES_MASK 0x0F + +#define BMG160_REG_MOTION_INTR 0x1C +#define BMG160_INT_MOTION_X BIT(0) +#define BMG160_INT_MOTION_Y BIT(1) +#define BMG160_INT_MOTION_Z BIT(2) +#define BMG160_ANY_DUR_MASK 0x30 +#define BMG160_ANY_DUR_SHIFT 4 + +#define BMG160_REG_INT_STATUS_2 0x0B +#define BMG160_ANY_MOTION_MASK 0x07 +#define BMG160_ANY_MOTION_BIT_X BIT(0) +#define BMG160_ANY_MOTION_BIT_Y BIT(1) +#define BMG160_ANY_MOTION_BIT_Z BIT(2) + +#define BMG160_REG_TEMP 0x08 +#define BMG160_TEMP_CENTER_VAL 23 + +#define BMG160_MAX_STARTUP_TIME_MS 80 + +#define BMG160_AUTO_SUSPEND_DELAY_MS 2000 + +struct bmg160_data { + struct device *dev; + struct regmap *regmap; + struct iio_trigger *dready_trig; + struct iio_trigger *motion_trig; + struct mutex mutex; + s16 buffer[8]; + u8 bw_bits; + u32 dps_range; + int ev_enable_state; + int slope_thres; + bool dready_trigger_on; + bool motion_trigger_on; + int irq; +}; + +enum bmg160_axis { + AXIS_X, + AXIS_Y, + AXIS_Z, +}; + +static const struct { + int val; + int bw_bits; +} bmg160_samp_freq_table[] = { {100, 0x07}, + {200, 0x06}, + {400, 0x03}, + {1000, 0x02}, + {2000, 0x01} }; + +static const struct { + int scale; + int dps_range; +} bmg160_scale_table[] = { { 1065, BMG160_RANGE_2000DPS}, + { 532, BMG160_RANGE_1000DPS}, + { 266, BMG160_RANGE_500DPS}, + { 133, BMG160_RANGE_250DPS}, + { 66, BMG160_RANGE_125DPS} }; + +static int bmg160_set_mode(struct bmg160_data *data, u8 mode) +{ + int ret; + + ret = regmap_write(data->regmap, BMG160_REG_PMU_LPW, mode); + if (ret < 0) { + dev_err(data->dev, "Error writing reg_pmu_lpw\n"); + return ret; + } + + return 0; +} + +static int bmg160_convert_freq_to_bit(int val) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(bmg160_samp_freq_table); ++i) { + if (bmg160_samp_freq_table[i].val == val) + return bmg160_samp_freq_table[i].bw_bits; + } + + return -EINVAL; +} + +static int bmg160_set_bw(struct bmg160_data *data, int val) +{ + int ret; + int bw_bits; + + bw_bits = bmg160_convert_freq_to_bit(val); + if (bw_bits < 0) + return bw_bits; + + ret = regmap_write(data->regmap, BMG160_REG_PMU_BW, bw_bits); + if (ret < 0) { + dev_err(data->dev, "Error writing reg_pmu_bw\n"); + return ret; + } + + data->bw_bits = bw_bits; + + return 0; +} + +static int bmg160_chip_init(struct bmg160_data *data) +{ + int ret; + unsigned int val; + + ret = regmap_read(data->regmap, BMG160_REG_CHIP_ID, &val); + if (ret < 0) { + dev_err(data->dev, "Error reading reg_chip_id\n"); + return ret; + } + + dev_dbg(data->dev, "Chip Id %x\n", val); + if (val != BMG160_CHIP_ID_VAL) { + dev_err(data->dev, "invalid chip %x\n", val); + return -ENODEV; + } + + ret = bmg160_set_mode(data, BMG160_MODE_NORMAL); + if (ret < 0) + return ret; + + /* Wait upto 500 ms to be ready after changing mode */ + usleep_range(500, 1000); + + /* Set Bandwidth */ + ret = bmg160_set_bw(data, BMG160_DEF_BW); + if (ret < 0) + return ret; + + /* Set Default Range */ + ret = regmap_write(data->regmap, BMG160_REG_RANGE, BMG160_RANGE_500DPS); + if (ret < 0) { + dev_err(data->dev, "Error writing reg_range\n"); + return ret; + } + data->dps_range = BMG160_RANGE_500DPS; + + ret = regmap_read(data->regmap, BMG160_REG_SLOPE_THRES, &val); + if (ret < 0) { + dev_err(data->dev, "Error reading reg_slope_thres\n"); + return ret; + } + data->slope_thres = val; + + /* Set default interrupt mode */ + ret = regmap_update_bits(data->regmap, BMG160_REG_INT_EN_1, + BMG160_INT1_BIT_OD, 0); + if (ret < 0) { + dev_err(data->dev, "Error updating bits in reg_int_en_1\n"); + return ret; + } + + ret = regmap_write(data->regmap, BMG160_REG_INT_RST_LATCH, + BMG160_INT_MODE_LATCH_INT | + BMG160_INT_MODE_LATCH_RESET); + if (ret < 0) { + dev_err(data->dev, + "Error writing reg_motion_intr\n"); + return ret; + } + + return 0; +} + +static int bmg160_set_power_state(struct bmg160_data *data, bool on) +{ +#ifdef CONFIG_PM + int ret; + + if (on) + ret = pm_runtime_get_sync(data->dev); + else { + pm_runtime_mark_last_busy(data->dev); + ret = pm_runtime_put_autosuspend(data->dev); + } + + if (ret < 0) { + dev_err(data->dev, + "Failed: bmg160_set_power_state for %d\n", on); + if (on) + pm_runtime_put_noidle(data->dev); + + return ret; + } +#endif + + return 0; +} + +static int bmg160_setup_any_motion_interrupt(struct bmg160_data *data, + bool status) +{ + int ret; + + /* Enable/Disable INT_MAP0 mapping */ + ret = regmap_update_bits(data->regmap, BMG160_REG_INT_MAP_0, + BMG160_INT_MAP_0_BIT_ANY, + (status ? BMG160_INT_MAP_0_BIT_ANY : 0)); + if (ret < 0) { + dev_err(data->dev, "Error updating bits reg_int_map0\n"); + return ret; + } + + /* Enable/Disable slope interrupts */ + if (status) { + /* Update slope thres */ + ret = regmap_write(data->regmap, BMG160_REG_SLOPE_THRES, + data->slope_thres); + if (ret < 0) { + dev_err(data->dev, + "Error writing reg_slope_thres\n"); + return ret; + } + + ret = regmap_write(data->regmap, BMG160_REG_MOTION_INTR, + BMG160_INT_MOTION_X | BMG160_INT_MOTION_Y | + BMG160_INT_MOTION_Z); + if (ret < 0) { + dev_err(data->dev, + "Error writing reg_motion_intr\n"); + return ret; + } + + /* + * New data interrupt is always non-latched, + * which will have higher priority, so no need + * to set latched mode, we will be flooded anyway with INTR + */ + if (!data->dready_trigger_on) { + ret = regmap_write(data->regmap, + BMG160_REG_INT_RST_LATCH, + BMG160_INT_MODE_LATCH_INT | + BMG160_INT_MODE_LATCH_RESET); + if (ret < 0) { + dev_err(data->dev, + "Error writing reg_rst_latch\n"); + return ret; + } + } + + ret = regmap_write(data->regmap, BMG160_REG_INT_EN_0, + BMG160_DATA_ENABLE_INT); + + } else { + ret = regmap_write(data->regmap, BMG160_REG_INT_EN_0, 0); + } + + if (ret < 0) { + dev_err(data->dev, "Error writing reg_int_en0\n"); + return ret; + } + + return 0; +} + +static int bmg160_setup_new_data_interrupt(struct bmg160_data *data, + bool status) +{ + int ret; + + /* Enable/Disable INT_MAP1 mapping */ + ret = regmap_update_bits(data->regmap, BMG160_REG_INT_MAP_1, + BMG160_INT_MAP_1_BIT_NEW_DATA, + (status ? BMG160_INT_MAP_1_BIT_NEW_DATA : 0)); + if (ret < 0) { + dev_err(data->dev, "Error updating bits in reg_int_map1\n"); + return ret; + } + + if (status) { + ret = regmap_write(data->regmap, BMG160_REG_INT_RST_LATCH, + BMG160_INT_MODE_NON_LATCH_INT | + BMG160_INT_MODE_LATCH_RESET); + if (ret < 0) { + dev_err(data->dev, + "Error writing reg_rst_latch\n"); + return ret; + } + + ret = regmap_write(data->regmap, BMG160_REG_INT_EN_0, + BMG160_DATA_ENABLE_INT); + + } else { + /* Restore interrupt mode */ + ret = regmap_write(data->regmap, BMG160_REG_INT_RST_LATCH, + BMG160_INT_MODE_LATCH_INT | + BMG160_INT_MODE_LATCH_RESET); + if (ret < 0) { + dev_err(data->dev, + "Error writing reg_rst_latch\n"); + return ret; + } + + ret = regmap_write(data->regmap, BMG160_REG_INT_EN_0, 0); + } + + if (ret < 0) { + dev_err(data->dev, "Error writing reg_int_en0\n"); + return ret; + } + + return 0; +} + +static int bmg160_get_bw(struct bmg160_data *data, int *val) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(bmg160_samp_freq_table); ++i) { + if (bmg160_samp_freq_table[i].bw_bits == data->bw_bits) { + *val = bmg160_samp_freq_table[i].val; + return IIO_VAL_INT; + } + } + + return -EINVAL; +} + +static int bmg160_set_scale(struct bmg160_data *data, int val) +{ + int ret, i; + + for (i = 0; i < ARRAY_SIZE(bmg160_scale_table); ++i) { + if (bmg160_scale_table[i].scale == val) { + ret = regmap_write(data->regmap, BMG160_REG_RANGE, + bmg160_scale_table[i].dps_range); + if (ret < 0) { + dev_err(data->dev, + "Error writing reg_range\n"); + return ret; + } + data->dps_range = bmg160_scale_table[i].dps_range; + return 0; + } + } + + return -EINVAL; +} + +static int bmg160_get_temp(struct bmg160_data *data, int *val) +{ + int ret; + unsigned int raw_val; + + mutex_lock(&data->mutex); + ret = bmg160_set_power_state(data, true); + if (ret < 0) { + mutex_unlock(&data->mutex); + return ret; + } + + ret = regmap_read(data->regmap, BMG160_REG_TEMP, &raw_val); + if (ret < 0) { + dev_err(data->dev, "Error reading reg_temp\n"); + bmg160_set_power_state(data, false); + mutex_unlock(&data->mutex); + return ret; + } + + *val = sign_extend32(raw_val, 7); + ret = bmg160_set_power_state(data, false); + mutex_unlock(&data->mutex); + if (ret < 0) + return ret; + + return IIO_VAL_INT; +} + +static int bmg160_get_axis(struct bmg160_data *data, int axis, int *val) +{ + int ret; + unsigned int raw_val; + + mutex_lock(&data->mutex); + ret = bmg160_set_power_state(data, true); + if (ret < 0) { + mutex_unlock(&data->mutex); + return ret; + } + + ret = regmap_bulk_read(data->regmap, BMG160_AXIS_TO_REG(axis), &raw_val, + 2); + if (ret < 0) { + dev_err(data->dev, "Error reading axis %d\n", axis); + bmg160_set_power_state(data, false); + mutex_unlock(&data->mutex); + return ret; + } + + *val = sign_extend32(raw_val, 15); + ret = bmg160_set_power_state(data, false); + mutex_unlock(&data->mutex); + if (ret < 0) + return ret; + + return IIO_VAL_INT; +} + +static int bmg160_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct bmg160_data *data = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + switch (chan->type) { + case IIO_TEMP: + return bmg160_get_temp(data, val); + case IIO_ANGL_VEL: + if (iio_buffer_enabled(indio_dev)) + return -EBUSY; + else + return bmg160_get_axis(data, chan->scan_index, + val); + default: + return -EINVAL; + } + case IIO_CHAN_INFO_OFFSET: + if (chan->type == IIO_TEMP) { + *val = BMG160_TEMP_CENTER_VAL; + return IIO_VAL_INT; + } else + return -EINVAL; + case IIO_CHAN_INFO_SCALE: + *val = 0; + switch (chan->type) { + case IIO_TEMP: + *val2 = 500000; + return IIO_VAL_INT_PLUS_MICRO; + case IIO_ANGL_VEL: + { + int i; + + for (i = 0; i < ARRAY_SIZE(bmg160_scale_table); ++i) { + if (bmg160_scale_table[i].dps_range == + data->dps_range) { + *val2 = bmg160_scale_table[i].scale; + return IIO_VAL_INT_PLUS_MICRO; + } + } + return -EINVAL; + } + default: + return -EINVAL; + } + case IIO_CHAN_INFO_SAMP_FREQ: + *val2 = 0; + mutex_lock(&data->mutex); + ret = bmg160_get_bw(data, val); + mutex_unlock(&data->mutex); + return ret; + default: + return -EINVAL; + } +} + +static int bmg160_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct bmg160_data *data = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + mutex_lock(&data->mutex); + /* + * Section 4.2 of spec + * In suspend mode, the only supported operations are reading + * registers as well as writing to the (0x14) softreset + * register. Since we will be in suspend mode by default, change + * mode to power on for other writes. + */ + ret = bmg160_set_power_state(data, true); + if (ret < 0) { + mutex_unlock(&data->mutex); + return ret; + } + ret = bmg160_set_bw(data, val); + if (ret < 0) { + bmg160_set_power_state(data, false); + mutex_unlock(&data->mutex); + return ret; + } + ret = bmg160_set_power_state(data, false); + mutex_unlock(&data->mutex); + return ret; + case IIO_CHAN_INFO_SCALE: + if (val) + return -EINVAL; + + mutex_lock(&data->mutex); + /* Refer to comments above for the suspend mode ops */ + ret = bmg160_set_power_state(data, true); + if (ret < 0) { + mutex_unlock(&data->mutex); + return ret; + } + ret = bmg160_set_scale(data, val2); + if (ret < 0) { + bmg160_set_power_state(data, false); + mutex_unlock(&data->mutex); + return ret; + } + ret = bmg160_set_power_state(data, false); + mutex_unlock(&data->mutex); + return ret; + default: + return -EINVAL; + } + + return -EINVAL; +} + +static int bmg160_read_event(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int *val, int *val2) +{ + struct bmg160_data *data = iio_priv(indio_dev); + + *val2 = 0; + switch (info) { + case IIO_EV_INFO_VALUE: + *val = data->slope_thres & BMG160_SLOPE_THRES_MASK; + break; + default: + return -EINVAL; + } + + return IIO_VAL_INT; +} + +static int bmg160_write_event(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int val, int val2) +{ + struct bmg160_data *data = iio_priv(indio_dev); + + switch (info) { + case IIO_EV_INFO_VALUE: + if (data->ev_enable_state) + return -EBUSY; + data->slope_thres &= ~BMG160_SLOPE_THRES_MASK; + data->slope_thres |= (val & BMG160_SLOPE_THRES_MASK); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int bmg160_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + + struct bmg160_data *data = iio_priv(indio_dev); + + return data->ev_enable_state; +} + +static int bmg160_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + int state) +{ + struct bmg160_data *data = iio_priv(indio_dev); + int ret; + + if (state && data->ev_enable_state) + return 0; + + mutex_lock(&data->mutex); + + if (!state && data->motion_trigger_on) { + data->ev_enable_state = 0; + mutex_unlock(&data->mutex); + return 0; + } + /* + * We will expect the enable and disable to do operation in + * in reverse order. This will happen here anyway as our + * resume operation uses sync mode runtime pm calls, the + * suspend operation will be delayed by autosuspend delay + * So the disable operation will still happen in reverse of + * enable operation. When runtime pm is disabled the mode + * is always on so sequence doesn't matter + */ + ret = bmg160_set_power_state(data, state); + if (ret < 0) { + mutex_unlock(&data->mutex); + return ret; + } + + ret = bmg160_setup_any_motion_interrupt(data, state); + if (ret < 0) { + bmg160_set_power_state(data, false); + mutex_unlock(&data->mutex); + return ret; + } + + data->ev_enable_state = state; + mutex_unlock(&data->mutex); + + return 0; +} + +static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("100 200 400 1000 2000"); + +static IIO_CONST_ATTR(in_anglvel_scale_available, + "0.001065 0.000532 0.000266 0.000133 0.000066"); + +static struct attribute *bmg160_attributes[] = { + &iio_const_attr_sampling_frequency_available.dev_attr.attr, + &iio_const_attr_in_anglvel_scale_available.dev_attr.attr, + NULL, +}; + +static const struct attribute_group bmg160_attrs_group = { + .attrs = bmg160_attributes, +}; + +static const struct iio_event_spec bmg160_event = { + .type = IIO_EV_TYPE_ROC, + .dir = IIO_EV_DIR_EITHER, + .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_ENABLE) +}; + +#define BMG160_CHANNEL(_axis) { \ + .type = IIO_ANGL_VEL, \ + .modified = 1, \ + .channel2 = IIO_MOD_##_axis, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_index = AXIS_##_axis, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 16, \ + .storagebits = 16, \ + }, \ + .event_spec = &bmg160_event, \ + .num_event_specs = 1 \ +} + +static const struct iio_chan_spec bmg160_channels[] = { + { + .type = IIO_TEMP, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET), + .scan_index = -1, + }, + BMG160_CHANNEL(X), + BMG160_CHANNEL(Y), + BMG160_CHANNEL(Z), + IIO_CHAN_SOFT_TIMESTAMP(3), +}; + +static const struct iio_info bmg160_info = { + .attrs = &bmg160_attrs_group, + .read_raw = bmg160_read_raw, + .write_raw = bmg160_write_raw, + .read_event_value = bmg160_read_event, + .write_event_value = bmg160_write_event, + .write_event_config = bmg160_write_event_config, + .read_event_config = bmg160_read_event_config, + .driver_module = THIS_MODULE, +}; + +static irqreturn_t bmg160_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct bmg160_data *data = iio_priv(indio_dev); + int bit, ret, i = 0; + unsigned int val; + + mutex_lock(&data->mutex); + for_each_set_bit(bit, indio_dev->active_scan_mask, + indio_dev->masklength) { + ret = regmap_bulk_read(data->regmap, BMG160_AXIS_TO_REG(bit), + &val, 2); + if (ret < 0) { + mutex_unlock(&data->mutex); + goto err; + } + data->buffer[i++] = ret; + } + mutex_unlock(&data->mutex); + + iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, + pf->timestamp); +err: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static int bmg160_trig_try_reen(struct iio_trigger *trig) +{ + struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); + struct bmg160_data *data = iio_priv(indio_dev); + int ret; + + /* new data interrupts don't need ack */ + if (data->dready_trigger_on) + return 0; + + /* Set latched mode interrupt and clear any latched interrupt */ + ret = regmap_write(data->regmap, BMG160_REG_INT_RST_LATCH, + BMG160_INT_MODE_LATCH_INT | + BMG160_INT_MODE_LATCH_RESET); + if (ret < 0) { + dev_err(data->dev, "Error writing reg_rst_latch\n"); + return ret; + } + + return 0; +} + +static int bmg160_data_rdy_trigger_set_state(struct iio_trigger *trig, + bool state) +{ + struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); + struct bmg160_data *data = iio_priv(indio_dev); + int ret; + + mutex_lock(&data->mutex); + + if (!state && data->ev_enable_state && data->motion_trigger_on) { + data->motion_trigger_on = false; + mutex_unlock(&data->mutex); + return 0; + } + + /* + * Refer to comment in bmg160_write_event_config for + * enable/disable operation order + */ + ret = bmg160_set_power_state(data, state); + if (ret < 0) { + mutex_unlock(&data->mutex); + return ret; + } + if (data->motion_trig == trig) + ret = bmg160_setup_any_motion_interrupt(data, state); + else + ret = bmg160_setup_new_data_interrupt(data, state); + if (ret < 0) { + bmg160_set_power_state(data, false); + mutex_unlock(&data->mutex); + return ret; + } + if (data->motion_trig == trig) + data->motion_trigger_on = state; + else + data->dready_trigger_on = state; + + mutex_unlock(&data->mutex); + + return 0; +} + +static const struct iio_trigger_ops bmg160_trigger_ops = { + .set_trigger_state = bmg160_data_rdy_trigger_set_state, + .try_reenable = bmg160_trig_try_reen, + .owner = THIS_MODULE, +}; + +static irqreturn_t bmg160_event_handler(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + struct bmg160_data *data = iio_priv(indio_dev); + int ret; + int dir; + unsigned int val; + + ret = regmap_read(data->regmap, BMG160_REG_INT_STATUS_2, &val); + if (ret < 0) { + dev_err(data->dev, "Error reading reg_int_status2\n"); + goto ack_intr_status; + } + + if (val & 0x08) + dir = IIO_EV_DIR_RISING; + else + dir = IIO_EV_DIR_FALLING; + + if (val & BMG160_ANY_MOTION_BIT_X) + iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ANGL_VEL, + 0, + IIO_MOD_X, + IIO_EV_TYPE_ROC, + dir), + iio_get_time_ns()); + if (val & BMG160_ANY_MOTION_BIT_Y) + iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ANGL_VEL, + 0, + IIO_MOD_Y, + IIO_EV_TYPE_ROC, + dir), + iio_get_time_ns()); + if (val & BMG160_ANY_MOTION_BIT_Z) + iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ANGL_VEL, + 0, + IIO_MOD_Z, + IIO_EV_TYPE_ROC, + dir), + iio_get_time_ns()); + +ack_intr_status: + if (!data->dready_trigger_on) { + ret = regmap_write(data->regmap, BMG160_REG_INT_RST_LATCH, + BMG160_INT_MODE_LATCH_INT | + BMG160_INT_MODE_LATCH_RESET); + if (ret < 0) + dev_err(data->dev, + "Error writing reg_rst_latch\n"); + } + + return IRQ_HANDLED; +} + +static irqreturn_t bmg160_data_rdy_trig_poll(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + struct bmg160_data *data = iio_priv(indio_dev); + + if (data->dready_trigger_on) + iio_trigger_poll(data->dready_trig); + else if (data->motion_trigger_on) + iio_trigger_poll(data->motion_trig); + + if (data->ev_enable_state) + return IRQ_WAKE_THREAD; + else + return IRQ_HANDLED; + +} + +static int bmg160_buffer_preenable(struct iio_dev *indio_dev) +{ + struct bmg160_data *data = iio_priv(indio_dev); + + return bmg160_set_power_state(data, true); +} + +static int bmg160_buffer_postdisable(struct iio_dev *indio_dev) +{ + struct bmg160_data *data = iio_priv(indio_dev); + + return bmg160_set_power_state(data, false); +} + +static const struct iio_buffer_setup_ops bmg160_buffer_setup_ops = { + .preenable = bmg160_buffer_preenable, + .postenable = iio_triggered_buffer_postenable, + .predisable = iio_triggered_buffer_predisable, + .postdisable = bmg160_buffer_postdisable, +}; + +static int bmg160_gpio_probe(struct bmg160_data *data) + +{ + struct device *dev; + struct gpio_desc *gpio; + + dev = data->dev; + + /* data ready gpio interrupt pin */ + gpio = devm_gpiod_get_index(dev, BMG160_GPIO_NAME, 0, GPIOD_IN); + if (IS_ERR(gpio)) { + dev_err(dev, "acpi gpio get index failed\n"); + return PTR_ERR(gpio); + } + + data->irq = gpiod_to_irq(gpio); + + dev_dbg(dev, "GPIO resource, no:%d irq:%d\n", desc_to_gpio(gpio), + data->irq); + + return 0; +} + +static const char *bmg160_match_acpi_device(struct device *dev) +{ + const struct acpi_device_id *id; + + id = acpi_match_device(dev->driver->acpi_match_table, dev); + if (!id) + return NULL; + + return dev_name(dev); +} + +int bmg160_core_probe(struct device *dev, struct regmap *regmap, int irq, + const char *name) +{ + struct bmg160_data *data; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + dev_set_drvdata(dev, indio_dev); + data->dev = dev; + data->irq = irq; + data->regmap = regmap; + + ret = bmg160_chip_init(data); + if (ret < 0) + return ret; + + mutex_init(&data->mutex); + + if (ACPI_HANDLE(dev)) + name = bmg160_match_acpi_device(dev); + + indio_dev->dev.parent = dev; + indio_dev->channels = bmg160_channels; + indio_dev->num_channels = ARRAY_SIZE(bmg160_channels); + indio_dev->name = name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &bmg160_info; + + if (data->irq <= 0) + bmg160_gpio_probe(data); + + if (data->irq > 0) { + ret = devm_request_threaded_irq(dev, + data->irq, + bmg160_data_rdy_trig_poll, + bmg160_event_handler, + IRQF_TRIGGER_RISING, + BMG160_IRQ_NAME, + indio_dev); + if (ret) + return ret; + + data->dready_trig = devm_iio_trigger_alloc(dev, + "%s-dev%d", + indio_dev->name, + indio_dev->id); + if (!data->dready_trig) + return -ENOMEM; + + data->motion_trig = devm_iio_trigger_alloc(dev, + "%s-any-motion-dev%d", + indio_dev->name, + indio_dev->id); + if (!data->motion_trig) + return -ENOMEM; + + data->dready_trig->dev.parent = dev; + data->dready_trig->ops = &bmg160_trigger_ops; + iio_trigger_set_drvdata(data->dready_trig, indio_dev); + ret = iio_trigger_register(data->dready_trig); + if (ret) + return ret; + + data->motion_trig->dev.parent = dev; + data->motion_trig->ops = &bmg160_trigger_ops; + iio_trigger_set_drvdata(data->motion_trig, indio_dev); + ret = iio_trigger_register(data->motion_trig); + if (ret) { + data->motion_trig = NULL; + goto err_trigger_unregister; + } + } + + ret = iio_triggered_buffer_setup(indio_dev, + iio_pollfunc_store_time, + bmg160_trigger_handler, + &bmg160_buffer_setup_ops); + if (ret < 0) { + dev_err(dev, + "iio triggered buffer setup failed\n"); + goto err_trigger_unregister; + } + + ret = iio_device_register(indio_dev); + if (ret < 0) { + dev_err(dev, "unable to register iio device\n"); + goto err_buffer_cleanup; + } + + ret = pm_runtime_set_active(dev); + if (ret) + goto err_iio_unregister; + + pm_runtime_enable(dev); + pm_runtime_set_autosuspend_delay(dev, + BMG160_AUTO_SUSPEND_DELAY_MS); + pm_runtime_use_autosuspend(dev); + + return 0; + +err_iio_unregister: + iio_device_unregister(indio_dev); +err_buffer_cleanup: + iio_triggered_buffer_cleanup(indio_dev); +err_trigger_unregister: + if (data->dready_trig) + iio_trigger_unregister(data->dready_trig); + if (data->motion_trig) + iio_trigger_unregister(data->motion_trig); + + return ret; +} +EXPORT_SYMBOL_GPL(bmg160_core_probe); + +void bmg160_core_remove(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct bmg160_data *data = iio_priv(indio_dev); + + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + pm_runtime_put_noidle(dev); + + iio_device_unregister(indio_dev); + iio_triggered_buffer_cleanup(indio_dev); + + if (data->dready_trig) { + iio_trigger_unregister(data->dready_trig); + iio_trigger_unregister(data->motion_trig); + } + + mutex_lock(&data->mutex); + bmg160_set_mode(data, BMG160_MODE_DEEP_SUSPEND); + mutex_unlock(&data->mutex); +} +EXPORT_SYMBOL_GPL(bmg160_core_remove); + +#ifdef CONFIG_PM_SLEEP +static int bmg160_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct bmg160_data *data = iio_priv(indio_dev); + + mutex_lock(&data->mutex); + bmg160_set_mode(data, BMG160_MODE_SUSPEND); + mutex_unlock(&data->mutex); + + return 0; +} + +static int bmg160_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct bmg160_data *data = iio_priv(indio_dev); + + mutex_lock(&data->mutex); + if (data->dready_trigger_on || data->motion_trigger_on || + data->ev_enable_state) + bmg160_set_mode(data, BMG160_MODE_NORMAL); + mutex_unlock(&data->mutex); + + return 0; +} +#endif + +#ifdef CONFIG_PM +static int bmg160_runtime_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct bmg160_data *data = iio_priv(indio_dev); + int ret; + + ret = bmg160_set_mode(data, BMG160_MODE_SUSPEND); + if (ret < 0) { + dev_err(data->dev, "set mode failed\n"); + return -EAGAIN; + } + + return 0; +} + +static int bmg160_runtime_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct bmg160_data *data = iio_priv(indio_dev); + int ret; + + ret = bmg160_set_mode(data, BMG160_MODE_NORMAL); + if (ret < 0) + return ret; + + msleep_interruptible(BMG160_MAX_STARTUP_TIME_MS); + + return 0; +} +#endif + +const struct dev_pm_ops bmg160_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(bmg160_suspend, bmg160_resume) + SET_RUNTIME_PM_OPS(bmg160_runtime_suspend, + bmg160_runtime_resume, NULL) +}; +EXPORT_SYMBOL_GPL(bmg160_pm_ops); + +MODULE_AUTHOR("Srinivas Pandruvada "); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("BMG160 Gyro driver"); diff --git a/drivers/iio/gyro/bmg160_i2c.c b/drivers/iio/gyro/bmg160_i2c.c new file mode 100644 index 0000000..90126a5 --- /dev/null +++ b/drivers/iio/gyro/bmg160_i2c.c @@ -0,0 +1,71 @@ +#include +#include +#include +#include +#include + +#include "bmg160.h" + +static const struct regmap_config bmg160_regmap_i2c_conf = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x3f +}; + +static int bmg160_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct regmap *regmap; + const char *name = NULL; + + regmap = devm_regmap_init_i2c(client, &bmg160_regmap_i2c_conf); + if (IS_ERR(regmap)) { + dev_err(&client->dev, "Failed to register i2c regmap %d\n", + (int)PTR_ERR(regmap)); + return PTR_ERR(regmap); + } + + if (id) + name = id->name; + + return bmg160_core_probe(&client->dev, regmap, client->irq, name); +} + +static int bmg160_i2c_remove(struct i2c_client *client) +{ + bmg160_core_remove(&client->dev); + + return 0; +} + +static const struct acpi_device_id bmg160_acpi_match[] = { + {"BMG0160", 0}, + {"BMI055B", 0}, + {}, +}; + +MODULE_DEVICE_TABLE(acpi, bmg160_acpi_match); + +static const struct i2c_device_id bmg160_i2c_id[] = { + {"bmg160", 0}, + {"bmi055_gyro", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, bmg160_i2c_id); + +static struct i2c_driver bmg160_i2c_driver = { + .driver = { + .name = "bmg160_i2c", + .acpi_match_table = ACPI_PTR(bmg160_acpi_match), + .pm = &bmg160_pm_ops, + }, + .probe = bmg160_i2c_probe, + .remove = bmg160_i2c_remove, + .id_table = bmg160_i2c_id, +}; +module_i2c_driver(bmg160_i2c_driver); + +MODULE_AUTHOR("Srinivas Pandruvada "); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("BMG160 I2C Gyro driver"); -- cgit v1.1 From b1d125cc6236399258025b0c5646cafa2b45e043 Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Wed, 19 Aug 2015 14:12:46 +0200 Subject: iio: bmg160: Add SPI driver Signed-off-by: Markus Pargmann Signed-off-by: Jonathan Cameron --- drivers/iio/gyro/Kconfig | 7 +++++- drivers/iio/gyro/Makefile | 1 + drivers/iio/gyro/bmg160_spi.c | 57 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 drivers/iio/gyro/bmg160_spi.c diff --git a/drivers/iio/gyro/Kconfig b/drivers/iio/gyro/Kconfig index 94526b1..e816d29 100644 --- a/drivers/iio/gyro/Kconfig +++ b/drivers/iio/gyro/Kconfig @@ -56,18 +56,23 @@ config BMG160 select IIO_BUFFER select IIO_TRIGGERED_BUFFER select BMG160_I2C if (I2C) + select BMG160_SPI if (SPI) help Say yes here to build support for BOSCH BMG160 Tri-axis Gyro Sensor driver connected via I2C or SPI. This driver also supports BMI055 gyroscope. This driver can also be built as a module. If so, the module - will be called bmg160_i2c. + will be called bmg160_i2c or bmg160_spi. config BMG160_I2C tristate select REGMAP_I2C +config BMG160_SPI + tristate + select REGMAP_SPI + config HID_SENSOR_GYRO_3D depends on HID_SENSOR_HUB select IIO_BUFFER diff --git a/drivers/iio/gyro/Makefile b/drivers/iio/gyro/Makefile index 6084010..f866a4be 100644 --- a/drivers/iio/gyro/Makefile +++ b/drivers/iio/gyro/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_ADIS16260) += adis16260.o obj-$(CONFIG_ADXRS450) += adxrs450.o obj-$(CONFIG_BMG160) += bmg160_core.o obj-$(CONFIG_BMG160_I2C) += bmg160_i2c.o +obj-$(CONFIG_BMG160_SPI) += bmg160_spi.o obj-$(CONFIG_HID_SENSOR_GYRO_3D) += hid-sensor-gyro-3d.o diff --git a/drivers/iio/gyro/bmg160_spi.c b/drivers/iio/gyro/bmg160_spi.c new file mode 100644 index 0000000..021ea5f --- /dev/null +++ b/drivers/iio/gyro/bmg160_spi.c @@ -0,0 +1,57 @@ +#include +#include +#include +#include + +#include "bmg160.h" + +static const struct regmap_config bmg160_regmap_spi_conf = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x3f, +}; + +static int bmg160_spi_probe(struct spi_device *spi) +{ + struct regmap *regmap; + const struct spi_device_id *id = spi_get_device_id(spi); + + regmap = devm_regmap_init_spi(spi, &bmg160_regmap_spi_conf); + if (IS_ERR(regmap)) { + dev_err(&spi->dev, "Failed to register spi regmap %d\n", + (int)PTR_ERR(regmap)); + return PTR_ERR(regmap); + } + + return bmg160_core_probe(&spi->dev, regmap, spi->irq, id->name); +} + +static int bmg160_spi_remove(struct spi_device *spi) +{ + bmg160_core_remove(&spi->dev); + + return 0; +} + +static const struct spi_device_id bmg160_spi_id[] = { + {"bmg160", 0}, + {"bmi055_gyro", 0}, + {} +}; + +MODULE_DEVICE_TABLE(spi, bmg160_spi_id); + +static struct spi_driver bmg160_spi_driver = { + .driver = { + .name = "bmg160_spi", + .pm = &bmg160_pm_ops, + }, + .probe = bmg160_spi_probe, + .remove = bmg160_spi_remove, + .id_table = bmg160_spi_id, +}; +module_spi_driver(bmg160_spi_driver); + +MODULE_AUTHOR("Markus Pargmann "); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("BMG160 SPI Gyro driver"); -- cgit v1.1 From 077377fc4f74899c58e946e47352216412d0bb3a Mon Sep 17 00:00:00 2001 From: Teodora Baluta Date: Thu, 20 Aug 2015 17:37:31 +0300 Subject: iio: accel: add support for mxc4005 accelerometer This patch adds support for Memsic MXC4005XC 3-axis accelerometer. The current implementation is a minimal one as it adds raw readings for the three axes and setting scale from userspace. Signed-off-by: Teodora Baluta Signed-off-by: Jonathan Cameron --- drivers/iio/accel/Kconfig | 11 ++ drivers/iio/accel/Makefile | 2 + drivers/iio/accel/mxc4005.c | 354 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 367 insertions(+) create mode 100644 drivers/iio/accel/mxc4005.c diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig index a59047d..69302be 100644 --- a/drivers/iio/accel/Kconfig +++ b/drivers/iio/accel/Kconfig @@ -137,6 +137,17 @@ config MMA9553 To compile this driver as a module, choose M here: the module will be called mma9553. +config MXC4005 + tristate "Memsic MXC4005XC 3-Axis Accelerometer Driver" + depends on I2C + select REGMAP_I2C + help + Say yes here to build support for the Memsic MXC4005XC 3-axis + accelerometer. + + To compile this driver as a module, choose M. The module will be + called mxc4005. + config STK8312 tristate "Sensortek STK8312 3-Axis Accelerometer Driver" depends on I2C diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile index ebd2675..020dda0 100644 --- a/drivers/iio/accel/Makefile +++ b/drivers/iio/accel/Makefile @@ -14,6 +14,8 @@ obj-$(CONFIG_MMA9551_CORE) += mma9551_core.o obj-$(CONFIG_MMA9551) += mma9551.o obj-$(CONFIG_MMA9553) += mma9553.o +obj-$(CONFIG_MXC4005) += mxc4005.o + obj-$(CONFIG_STK8312) += stk8312.o obj-$(CONFIG_STK8BA50) += stk8ba50.o diff --git a/drivers/iio/accel/mxc4005.c b/drivers/iio/accel/mxc4005.c new file mode 100644 index 0000000..e15c1bd --- /dev/null +++ b/drivers/iio/accel/mxc4005.c @@ -0,0 +1,354 @@ +/* + * 3-axis accelerometer driver for MXC4005XC Memsic sensor + * + * Copyright (c) 2014, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include +#include +#include +#include +#include +#include + +#define MXC4005_DRV_NAME "mxc4005" +#define MXC4005_REGMAP_NAME "mxc4005_regmap" + +#define MXC4005_REG_XOUT_UPPER 0x03 +#define MXC4005_REG_XOUT_LOWER 0x04 +#define MXC4005_REG_YOUT_UPPER 0x05 +#define MXC4005_REG_YOUT_LOWER 0x06 +#define MXC4005_REG_ZOUT_UPPER 0x07 +#define MXC4005_REG_ZOUT_LOWER 0x08 + +#define MXC4005_REG_CONTROL 0x0D +#define MXC4005_REG_CONTROL_MASK_FSR GENMASK(6, 5) +#define MXC4005_CONTROL_FSR_SHIFT 5 + +#define MXC4005_REG_DEVICE_ID 0x0E + +enum mxc4005_axis { + AXIS_X, + AXIS_Y, + AXIS_Z, +}; + +enum mxc4005_range { + MXC4005_RANGE_2G, + MXC4005_RANGE_4G, + MXC4005_RANGE_8G, +}; + +struct mxc4005_data { + struct device *dev; + struct mutex mutex; + struct regmap *regmap; +}; + +/* + * MXC4005 can operate in the following ranges: + * +/- 2G, 4G, 8G (the default +/-2G) + * + * (2 + 2) * 9.81 / (2^12 - 1) = 0.009582 + * (4 + 4) * 9.81 / (2^12 - 1) = 0.019164 + * (8 + 8) * 9.81 / (2^12 - 1) = 0.038329 + */ +static const struct { + u8 range; + int scale; +} mxc4005_scale_table[] = { + {MXC4005_RANGE_2G, 9582}, + {MXC4005_RANGE_4G, 19164}, + {MXC4005_RANGE_8G, 38329}, +}; + + +static IIO_CONST_ATTR(in_accel_scale_available, "0.009582 0.019164 0.038329"); + +static struct attribute *mxc4005_attributes[] = { + &iio_const_attr_in_accel_scale_available.dev_attr.attr, + NULL, +}; + +static const struct attribute_group mxc4005_attrs_group = { + .attrs = mxc4005_attributes, +}; + +static bool mxc4005_is_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MXC4005_REG_XOUT_UPPER: + case MXC4005_REG_XOUT_LOWER: + case MXC4005_REG_YOUT_UPPER: + case MXC4005_REG_YOUT_LOWER: + case MXC4005_REG_ZOUT_UPPER: + case MXC4005_REG_ZOUT_LOWER: + case MXC4005_REG_DEVICE_ID: + case MXC4005_REG_CONTROL: + return true; + default: + return false; + } +} + +static bool mxc4005_is_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MXC4005_REG_CONTROL: + return true; + default: + return false; + } +} + +static const struct regmap_config mxc4005_regmap_config = { + .name = MXC4005_REGMAP_NAME, + + .reg_bits = 8, + .val_bits = 8, + + .max_register = MXC4005_REG_DEVICE_ID, + + .readable_reg = mxc4005_is_readable_reg, + .writeable_reg = mxc4005_is_writeable_reg, +}; + +static int mxc4005_read_axis(struct mxc4005_data *data, + unsigned int addr) +{ + __be16 reg; + int ret; + + ret = regmap_bulk_read(data->regmap, addr, (u8 *) ®, sizeof(reg)); + if (ret < 0) { + dev_err(data->dev, "failed to read reg %02x\n", addr); + return ret; + } + + return be16_to_cpu(reg); +} + +static int mxc4005_read_scale(struct mxc4005_data *data) +{ + unsigned int reg; + int ret; + int i; + + ret = regmap_read(data->regmap, MXC4005_REG_CONTROL, ®); + if (ret < 0) { + dev_err(data->dev, "failed to read reg_control\n"); + return ret; + } + + i = reg >> MXC4005_CONTROL_FSR_SHIFT; + + if (i < 0 || i >= ARRAY_SIZE(mxc4005_scale_table)) + return -EINVAL; + + return mxc4005_scale_table[i].scale; +} + +static int mxc4005_set_scale(struct mxc4005_data *data, int val) +{ + unsigned int reg; + int i; + int ret; + + for (i = 0; i < ARRAY_SIZE(mxc4005_scale_table); i++) { + if (mxc4005_scale_table[i].scale == val) { + reg = i << MXC4005_CONTROL_FSR_SHIFT; + ret = regmap_update_bits(data->regmap, + MXC4005_REG_CONTROL, + MXC4005_REG_CONTROL_MASK_FSR, + reg); + if (ret < 0) + dev_err(data->dev, + "failed to write reg_control\n"); + return ret; + } + } + + return -EINVAL; +} + +static int mxc4005_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct mxc4005_data *data = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + switch (chan->type) { + case IIO_ACCEL: + if (iio_buffer_enabled(indio_dev)) + return -EBUSY; + + ret = mxc4005_read_axis(data, chan->address); + if (ret < 0) + return ret; + *val = sign_extend32(ret >> 4, 11); + return IIO_VAL_INT; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_SCALE: + ret = mxc4005_read_scale(data); + if (ret < 0) + return ret; + + *val = 0; + *val2 = ret; + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +} + +static int mxc4005_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct mxc4005_data *data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + if (val != 0) + return -EINVAL; + + return mxc4005_set_scale(data, val2); + default: + return -EINVAL; + } +} + +static const struct iio_info mxc4005_info = { + .driver_module = THIS_MODULE, + .read_raw = mxc4005_read_raw, + .write_raw = mxc4005_write_raw, + .attrs = &mxc4005_attrs_group, +}; + +#define MXC4005_CHANNEL(_axis, _addr) { \ + .type = IIO_ACCEL, \ + .modified = 1, \ + .channel2 = IIO_MOD_##_axis, \ + .address = _addr, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ +} + +static const struct iio_chan_spec mxc4005_channels[] = { + MXC4005_CHANNEL(X, MXC4005_REG_XOUT_UPPER), + MXC4005_CHANNEL(Y, MXC4005_REG_YOUT_UPPER), + MXC4005_CHANNEL(Z, MXC4005_REG_ZOUT_UPPER), +}; + +static int mxc4005_chip_init(struct mxc4005_data *data) +{ + int ret; + unsigned int reg; + + ret = regmap_read(data->regmap, MXC4005_REG_DEVICE_ID, ®); + if (ret < 0) { + dev_err(data->dev, "failed to read chip id\n"); + return ret; + } + + dev_dbg(data->dev, "MXC4005 chip id %02x\n", reg); + + return 0; +} + +static int mxc4005_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct mxc4005_data *data; + struct iio_dev *indio_dev; + struct regmap *regmap; + int ret; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + regmap = devm_regmap_init_i2c(client, &mxc4005_regmap_config); + if (IS_ERR(regmap)) { + dev_err(&client->dev, "failed to initialize regmap\n"); + return PTR_ERR(regmap); + } + + data = iio_priv(indio_dev); + i2c_set_clientdata(client, indio_dev); + data->dev = &client->dev; + data->regmap = regmap; + + ret = mxc4005_chip_init(data); + if (ret < 0) { + dev_err(&client->dev, "failed to initialize chip\n"); + return ret; + } + + mutex_init(&data->mutex); + + indio_dev->dev.parent = &client->dev; + indio_dev->channels = mxc4005_channels; + indio_dev->num_channels = ARRAY_SIZE(mxc4005_channels); + indio_dev->name = MXC4005_DRV_NAME; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &mxc4005_info; + + ret = iio_device_register(indio_dev); + if (ret < 0) { + dev_err(&client->dev, + "unable to register iio device %d\n", ret); + return ret; + } + + return 0; +} + +static int mxc4005_remove(struct i2c_client *client) +{ + iio_device_unregister(i2c_get_clientdata(client)); + + return 0; +} + +static const struct acpi_device_id mxc4005_acpi_match[] = { + {"MXC4005", 0}, + { }, +}; +MODULE_DEVICE_TABLE(acpi, mxc4005_acpi_match); + +static const struct i2c_device_id mxc4005_id[] = { + {"mxc4005", 0}, + { }, +}; +MODULE_DEVICE_TABLE(i2c, mxc4005_id); + +static struct i2c_driver mxc4005_driver = { + .driver = { + .name = MXC4005_DRV_NAME, + .acpi_match_table = ACPI_PTR(mxc4005_acpi_match), + }, + .probe = mxc4005_probe, + .remove = mxc4005_remove, + .id_table = mxc4005_id, +}; + +module_i2c_driver(mxc4005_driver); + +MODULE_AUTHOR("Teodora Baluta "); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("MXC4005 3-axis accelerometer driver"); -- cgit v1.1 From 1ce0eda0f75747b3131a9047aee19291f59c18c9 Mon Sep 17 00:00:00 2001 From: Teodora Baluta Date: Thu, 20 Aug 2015 17:37:32 +0300 Subject: iio: mxc4005: add triggered buffer mode for mxc4005 This patch adds support for buffered readings for the 3-axis accelerometer mxc4005. Signed-off-by: Teodora Baluta Signed-off-by: Jonathan Cameron --- drivers/iio/accel/Kconfig | 2 ++ drivers/iio/accel/mxc4005.c | 79 +++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 78 insertions(+), 3 deletions(-) diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig index 69302be..cd5cd24 100644 --- a/drivers/iio/accel/Kconfig +++ b/drivers/iio/accel/Kconfig @@ -140,6 +140,8 @@ config MMA9553 config MXC4005 tristate "Memsic MXC4005XC 3-Axis Accelerometer Driver" depends on I2C + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER select REGMAP_I2C help Say yes here to build support for the Memsic MXC4005XC 3-axis diff --git a/drivers/iio/accel/mxc4005.c b/drivers/iio/accel/mxc4005.c index e15c1bd..390eaf8 100644 --- a/drivers/iio/accel/mxc4005.c +++ b/drivers/iio/accel/mxc4005.c @@ -19,6 +19,9 @@ #include #include #include +#include +#include +#include #define MXC4005_DRV_NAME "mxc4005" #define MXC4005_REGMAP_NAME "mxc4005_regmap" @@ -52,6 +55,7 @@ struct mxc4005_data { struct device *dev; struct mutex mutex; struct regmap *regmap; + __be16 buffer[8]; }; /* @@ -122,6 +126,20 @@ static const struct regmap_config mxc4005_regmap_config = { .writeable_reg = mxc4005_is_writeable_reg, }; +static int mxc4005_read_xyz(struct mxc4005_data *data) +{ + int ret; + + ret = regmap_bulk_read(data->regmap, MXC4005_REG_XOUT_UPPER, + (u8 *) data->buffer, sizeof(data->buffer)); + if (ret < 0) { + dev_err(data->dev, "failed to read axes\n"); + return ret; + } + + return 0; +} + static int mxc4005_read_axis(struct mxc4005_data *data, unsigned int addr) { @@ -197,7 +215,8 @@ static int mxc4005_read_raw(struct iio_dev *indio_dev, ret = mxc4005_read_axis(data, chan->address); if (ret < 0) return ret; - *val = sign_extend32(ret >> 4, 11); + *val = sign_extend32(ret >> chan->scan_type.shift, + chan->scan_type.realbits - 1); return IIO_VAL_INT; default: return -EINVAL; @@ -239,6 +258,11 @@ static const struct iio_info mxc4005_info = { .attrs = &mxc4005_attrs_group, }; +static const unsigned long mxc4005_scan_masks[] = { + BIT(AXIS_X) | BIT(AXIS_Y) | BIT(AXIS_Z), + 0 +}; + #define MXC4005_CHANNEL(_axis, _addr) { \ .type = IIO_ACCEL, \ .modified = 1, \ @@ -246,14 +270,43 @@ static const struct iio_info mxc4005_info = { .address = _addr, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .scan_index = AXIS_##_axis, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 12, \ + .storagebits = 16, \ + .shift = 4, \ + .endianness = IIO_BE, \ + }, \ } static const struct iio_chan_spec mxc4005_channels[] = { MXC4005_CHANNEL(X, MXC4005_REG_XOUT_UPPER), MXC4005_CHANNEL(Y, MXC4005_REG_YOUT_UPPER), MXC4005_CHANNEL(Z, MXC4005_REG_ZOUT_UPPER), + IIO_CHAN_SOFT_TIMESTAMP(3), }; +static irqreturn_t mxc4005_trigger_handler(int irq, void *private) +{ + struct iio_poll_func *pf = private; + struct iio_dev *indio_dev = pf->indio_dev; + struct mxc4005_data *data = iio_priv(indio_dev); + int ret; + + ret = mxc4005_read_xyz(data); + if (ret < 0) + goto err; + + iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, + pf->timestamp); + +err: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + static int mxc4005_chip_init(struct mxc4005_data *data) { int ret; @@ -304,23 +357,43 @@ static int mxc4005_probe(struct i2c_client *client, indio_dev->dev.parent = &client->dev; indio_dev->channels = mxc4005_channels; indio_dev->num_channels = ARRAY_SIZE(mxc4005_channels); + indio_dev->available_scan_masks = mxc4005_scan_masks; indio_dev->name = MXC4005_DRV_NAME; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->info = &mxc4005_info; + ret = iio_triggered_buffer_setup(indio_dev, + &iio_pollfunc_store_time, + mxc4005_trigger_handler, + NULL); + if (ret < 0) { + dev_err(&client->dev, + "failed to setup iio triggered buffer\n"); + return ret; + } + ret = iio_device_register(indio_dev); if (ret < 0) { dev_err(&client->dev, "unable to register iio device %d\n", ret); - return ret; + goto err_buffer_cleanup; } return 0; + +err_buffer_cleanup: + iio_triggered_buffer_cleanup(indio_dev); + + return ret; } static int mxc4005_remove(struct i2c_client *client) { - iio_device_unregister(i2c_get_clientdata(client)); + struct iio_dev *indio_dev = i2c_get_clientdata(client); + + iio_device_unregister(indio_dev); + + iio_triggered_buffer_cleanup(indio_dev); return 0; } -- cgit v1.1 From 47196620c82f8d8cef0dc61b87b76f18278537dd Mon Sep 17 00:00:00 2001 From: Teodora Baluta Date: Thu, 20 Aug 2015 17:37:33 +0300 Subject: iio: mxc4005: add data ready trigger for mxc4005 Add iio trigger for the data ready interrupt that signals new measurements for the X, Y and Z axes. Signed-off-by: Teodora Baluta Signed-off-by: Jonathan Cameron --- drivers/iio/accel/mxc4005.c | 142 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 141 insertions(+), 1 deletion(-) diff --git a/drivers/iio/accel/mxc4005.c b/drivers/iio/accel/mxc4005.c index 390eaf8..e72e218 100644 --- a/drivers/iio/accel/mxc4005.c +++ b/drivers/iio/accel/mxc4005.c @@ -17,13 +17,16 @@ #include #include #include +#include #include #include +#include #include #include #include #define MXC4005_DRV_NAME "mxc4005" +#define MXC4005_IRQ_NAME "mxc4005_event" #define MXC4005_REGMAP_NAME "mxc4005_regmap" #define MXC4005_REG_XOUT_UPPER 0x03 @@ -33,6 +36,12 @@ #define MXC4005_REG_ZOUT_UPPER 0x07 #define MXC4005_REG_ZOUT_LOWER 0x08 +#define MXC4005_REG_INT_MASK1 0x0B +#define MXC4005_REG_INT_MASK1_BIT_DRDYE 0x01 + +#define MXC4005_REG_INT_CLR1 0x01 +#define MXC4005_REG_INT_CLR1_BIT_DRDYC 0x01 + #define MXC4005_REG_CONTROL 0x0D #define MXC4005_REG_CONTROL_MASK_FSR GENMASK(6, 5) #define MXC4005_CONTROL_FSR_SHIFT 5 @@ -55,7 +64,9 @@ struct mxc4005_data { struct device *dev; struct mutex mutex; struct regmap *regmap; + struct iio_trigger *dready_trig; __be16 buffer[8]; + bool trigger_enabled; }; /* @@ -107,6 +118,8 @@ static bool mxc4005_is_readable_reg(struct device *dev, unsigned int reg) static bool mxc4005_is_writeable_reg(struct device *dev, unsigned int reg) { switch (reg) { + case MXC4005_REG_INT_CLR1: + case MXC4005_REG_INT_MASK1: case MXC4005_REG_CONTROL: return true; default: @@ -307,6 +320,91 @@ err: return IRQ_HANDLED; } +static int mxc4005_clr_intr(struct mxc4005_data *data) +{ + int ret; + + /* clear interrupt */ + ret = regmap_write(data->regmap, MXC4005_REG_INT_CLR1, + MXC4005_REG_INT_CLR1_BIT_DRDYC); + if (ret < 0) { + dev_err(data->dev, "failed to write to reg_int_clr1\n"); + return ret; + } + + return 0; +} + +static int mxc4005_set_trigger_state(struct iio_trigger *trig, + bool state) +{ + struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); + struct mxc4005_data *data = iio_priv(indio_dev); + int ret; + + mutex_lock(&data->mutex); + if (state) { + ret = regmap_write(data->regmap, MXC4005_REG_INT_MASK1, + MXC4005_REG_INT_MASK1_BIT_DRDYE); + } else { + ret = regmap_write(data->regmap, MXC4005_REG_INT_MASK1, + ~MXC4005_REG_INT_MASK1_BIT_DRDYE); + } + + if (ret < 0) { + mutex_unlock(&data->mutex); + dev_err(data->dev, "failed to update reg_int_mask1"); + return ret; + } + + data->trigger_enabled = state; + mutex_unlock(&data->mutex); + + return 0; +} + +static int mxc4005_trigger_try_reen(struct iio_trigger *trig) +{ + struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); + struct mxc4005_data *data = iio_priv(indio_dev); + + if (!data->dready_trig) + return 0; + + return mxc4005_clr_intr(data); +} + +static const struct iio_trigger_ops mxc4005_trigger_ops = { + .set_trigger_state = mxc4005_set_trigger_state, + .try_reenable = mxc4005_trigger_try_reen, + .owner = THIS_MODULE, +}; + +static int mxc4005_gpio_probe(struct i2c_client *client, + struct mxc4005_data *data) +{ + struct device *dev; + struct gpio_desc *gpio; + int ret; + + if (!client) + return -EINVAL; + + dev = &client->dev; + + gpio = devm_gpiod_get_index(dev, "mxc4005_int", 0, GPIOD_IN); + if (IS_ERR(gpio)) { + dev_err(dev, "failed to get acpi gpio index\n"); + return PTR_ERR(gpio); + } + + ret = gpiod_to_irq(gpio); + + dev_dbg(dev, "GPIO resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret); + + return ret; +} + static int mxc4005_chip_init(struct mxc4005_data *data) { int ret; @@ -363,7 +461,7 @@ static int mxc4005_probe(struct i2c_client *client, indio_dev->info = &mxc4005_info; ret = iio_triggered_buffer_setup(indio_dev, - &iio_pollfunc_store_time, + iio_pollfunc_store_time, mxc4005_trigger_handler, NULL); if (ret < 0) { @@ -372,6 +470,43 @@ static int mxc4005_probe(struct i2c_client *client, return ret; } + if (client->irq < 0) + client->irq = mxc4005_gpio_probe(client, data); + + if (client->irq > 0) { + data->dready_trig = devm_iio_trigger_alloc(&client->dev, + "%s-dev%d", + indio_dev->name, + indio_dev->id); + if (!data->dready_trig) + return -ENOMEM; + + ret = devm_request_threaded_irq(&client->dev, client->irq, + iio_trigger_generic_data_rdy_poll, + NULL, + IRQF_TRIGGER_FALLING | + IRQF_ONESHOT, + MXC4005_IRQ_NAME, + data->dready_trig); + if (ret) { + dev_err(&client->dev, + "failed to init threaded irq\n"); + goto err_buffer_cleanup; + } + + data->dready_trig->dev.parent = &client->dev; + data->dready_trig->ops = &mxc4005_trigger_ops; + iio_trigger_set_drvdata(data->dready_trig, indio_dev); + indio_dev->trig = data->dready_trig; + iio_trigger_get(indio_dev->trig); + ret = iio_trigger_register(data->dready_trig); + if (ret) { + dev_err(&client->dev, + "failed to register trigger\n"); + goto err_trigger_unregister; + } + } + ret = iio_device_register(indio_dev); if (ret < 0) { dev_err(&client->dev, @@ -381,6 +516,8 @@ static int mxc4005_probe(struct i2c_client *client, return 0; +err_trigger_unregister: + iio_trigger_unregister(data->dready_trig); err_buffer_cleanup: iio_triggered_buffer_cleanup(indio_dev); @@ -390,10 +527,13 @@ err_buffer_cleanup: static int mxc4005_remove(struct i2c_client *client) { struct iio_dev *indio_dev = i2c_get_clientdata(client); + struct mxc4005_data *data = iio_priv(indio_dev); iio_device_unregister(indio_dev); iio_triggered_buffer_cleanup(indio_dev); + if (data->dready_trig) + iio_trigger_unregister(data->dready_trig); return 0; } -- cgit v1.1 From 72aa29ce0a59779dcda91e967600a0f8637945a6 Mon Sep 17 00:00:00 2001 From: Vladimir Barinov Date: Fri, 28 Aug 2015 17:27:52 +0300 Subject: iio: adc: hi8435: Holt HI-8435 threshold detector Add Holt threshold detector driver for HI-8435 chip Signed-off-by: Vladimir Barinov Signed-off-by: Jonathan Cameron --- Documentation/ABI/testing/sysfs-bus-iio | 1 + Documentation/ABI/testing/sysfs-bus-iio-adc-hi8435 | 43 ++ drivers/iio/adc/Kconfig | 11 + drivers/iio/adc/Makefile | 1 + drivers/iio/adc/hi8435.c | 534 +++++++++++++++++++++ 5 files changed, 590 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-adc-hi8435 create mode 100644 drivers/iio/adc/hi8435.c diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio index 42d360f..20312a0 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio +++ b/Documentation/ABI/testing/sysfs-bus-iio @@ -581,6 +581,7 @@ What: /sys/.../iio:deviceX/events/in_voltageY_supply_thresh_rising_en What: /sys/.../iio:deviceX/events/in_voltageY_supply_thresh_falling_en What: /sys/.../iio:deviceX/events/in_voltageY_thresh_rising_en What: /sys/.../iio:deviceX/events/in_voltageY_thresh_falling_en +What: /sys/.../iio:deviceX/events/in_voltageY_thresh_either_en What: /sys/.../iio:deviceX/events/in_tempY_thresh_rising_en What: /sys/.../iio:deviceX/events/in_tempY_thresh_falling_en KernelVersion: 2.6.37 diff --git a/Documentation/ABI/testing/sysfs-bus-iio-adc-hi8435 b/Documentation/ABI/testing/sysfs-bus-iio-adc-hi8435 new file mode 100644 index 0000000..f30b4c4 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-iio-adc-hi8435 @@ -0,0 +1,43 @@ +What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_sensing_mode +Date: August 2015 +KernelVersion: 4.2.0 +Contact: source@cogentembedded.com +Description: + Program sensor type for threshold detector inputs. + Could be either "GND-Open" or "Supply-Open" mode. Y is a + threshold detector input channel. Channels 0..7, 8..15, 16..23 + and 24..31 has common sensor types. + +What: /sys/bus/iio/devices/iio:deviceX/events/in_voltageY_thresh_falling_value +Date: August 2015 +KernelVersion: 4.2.0 +Contact: source@cogentembedded.com +Description: + Channel Y low voltage threshold. If sensor input voltage goes lower then + this value then the threshold falling event is pushed. + Depending on in_voltageY_sensing_mode the low voltage threshold + is separately set for "GND-Open" and "Supply-Open" modes. + Channels 0..31 have common low threshold values, but could have different + sensing_modes. + The low voltage threshold range is between 2..21V. + Hysteresis between low and high thresholds can not be lower then 2 and + can not be odd. + If falling threshold results hysteresis to odd value then rising + threshold is automatically subtracted by one. + +What: /sys/bus/iio/devices/iio:deviceX/events/in_voltageY_thresh_rising_value +Date: August 2015 +KernelVersion: 4.2.0 +Contact: source@cogentembedded.com +Description: + Channel Y high voltage threshold. If sensor input voltage goes higher then + this value then the threshold rising event is pushed. + Depending on in_voltageY_sensing_mode the high voltage threshold + is separately set for "GND-Open" and "Supply-Open" modes. + Channels 0..31 have common high threshold values, but could have different + sensing_modes. + The high voltage threshold range is between 3..22V. + Hysteresis between low and high thresholds can not be lower then 2 and + can not be odd. + If rising threshold results hysteresis to odd value then falling + threshold is automatically appended by one. diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 2753118..7868c74 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -183,6 +183,17 @@ config EXYNOS_ADC To compile this driver as a module, choose M here: the module will be called exynos_adc. +config HI8435 + tristate "Holt Integrated Circuits HI-8435 threshold detector" + select IIO_TRIGGERED_EVENT + depends on SPI + help + If you say yes here you get support for Holt Integrated Circuits + HI-8435 chip. + + This driver can also be built as a module. If so, the module will be + called hi8435. + config LP8788_ADC tristate "LP8788 ADC driver" depends on MFD_LP8788 diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index fa5723a..99b37a9 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o +obj-$(CONFIG_HI8435) += hi8435.o obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o obj-$(CONFIG_MAX1027) += max1027.o obj-$(CONFIG_MAX1363) += max1363.o diff --git a/drivers/iio/adc/hi8435.c b/drivers/iio/adc/hi8435.c new file mode 100644 index 0000000..c73c6c6 --- /dev/null +++ b/drivers/iio/adc/hi8435.c @@ -0,0 +1,534 @@ +/* + * Holt Integrated Circuits HI-8435 threshold detector driver + * + * Copyright (C) 2015 Zodiac Inflight Innovations + * Copyright (C) 2015 Cogent Embedded, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "hi8435" + +/* Register offsets for HI-8435 */ +#define HI8435_CTRL_REG 0x02 +#define HI8435_PSEN_REG 0x04 +#define HI8435_TMDATA_REG 0x1E +#define HI8435_GOCENHYS_REG 0x3A +#define HI8435_SOCENHYS_REG 0x3C +#define HI8435_SO7_0_REG 0x10 +#define HI8435_SO15_8_REG 0x12 +#define HI8435_SO23_16_REG 0x14 +#define HI8435_SO31_24_REG 0x16 +#define HI8435_SO31_0_REG 0x78 + +#define HI8435_WRITE_OPCODE 0x00 +#define HI8435_READ_OPCODE 0x80 + +/* CTRL register bits */ +#define HI8435_CTRL_TEST 0x01 +#define HI8435_CTRL_SRST 0x02 + +struct hi8435_priv { + struct spi_device *spi; + struct mutex lock; + + unsigned long event_scan_mask; /* soft mask/unmask channels events */ + unsigned int event_prev_val; + + unsigned threshold_lo[2]; /* GND-Open and Supply-Open thresholds */ + unsigned threshold_hi[2]; /* GND-Open and Supply-Open thresholds */ + u8 reg_buffer[3] ____cacheline_aligned; +}; + +static int hi8435_readb(struct hi8435_priv *priv, u8 reg, u8 *val) +{ + reg |= HI8435_READ_OPCODE; + return spi_write_then_read(priv->spi, ®, 1, val, 1); +} + +static int hi8435_readw(struct hi8435_priv *priv, u8 reg, u16 *val) +{ + int ret; + __be16 be_val; + + reg |= HI8435_READ_OPCODE; + ret = spi_write_then_read(priv->spi, ®, 1, &be_val, 2); + *val = be16_to_cpu(be_val); + + return ret; +} + +static int hi8435_readl(struct hi8435_priv *priv, u8 reg, u32 *val) +{ + int ret; + __be32 be_val; + + reg |= HI8435_READ_OPCODE; + ret = spi_write_then_read(priv->spi, ®, 1, &be_val, 4); + *val = be32_to_cpu(be_val); + + return ret; +} + +static int hi8435_writeb(struct hi8435_priv *priv, u8 reg, u8 val) +{ + priv->reg_buffer[0] = reg | HI8435_WRITE_OPCODE; + priv->reg_buffer[1] = val; + + return spi_write(priv->spi, priv->reg_buffer, 2); +} + +static int hi8435_writew(struct hi8435_priv *priv, u8 reg, u16 val) +{ + priv->reg_buffer[0] = reg | HI8435_WRITE_OPCODE; + priv->reg_buffer[1] = (val >> 8) & 0xff; + priv->reg_buffer[2] = val & 0xff; + + return spi_write(priv->spi, priv->reg_buffer, 3); +} + +static int hi8435_read_event_config(struct iio_dev *idev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + struct hi8435_priv *priv = iio_priv(idev); + + return !!(priv->event_scan_mask & BIT(chan->channel)); +} + +static int hi8435_write_event_config(struct iio_dev *idev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, int state) +{ + struct hi8435_priv *priv = iio_priv(idev); + + priv->event_scan_mask &= ~BIT(chan->channel); + if (state) + priv->event_scan_mask |= BIT(chan->channel); + + return 0; +} + +static int hi8435_read_event_value(struct iio_dev *idev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int *val, int *val2) +{ + struct hi8435_priv *priv = iio_priv(idev); + int ret; + u8 mode, psen; + u16 reg; + + ret = hi8435_readb(priv, HI8435_PSEN_REG, &psen); + if (ret < 0) + return ret; + + /* Supply-Open or GND-Open sensing mode */ + mode = !!(psen & BIT(chan->channel / 8)); + + ret = hi8435_readw(priv, mode ? HI8435_SOCENHYS_REG : + HI8435_GOCENHYS_REG, ®); + if (ret < 0) + return ret; + + if (dir == IIO_EV_DIR_FALLING) + *val = ((reg & 0xff) - (reg >> 8)) / 2; + else if (dir == IIO_EV_DIR_RISING) + *val = ((reg & 0xff) + (reg >> 8)) / 2; + + return IIO_VAL_INT; +} + +static int hi8435_write_event_value(struct iio_dev *idev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int val, int val2) +{ + struct hi8435_priv *priv = iio_priv(idev); + int ret; + u8 mode, psen; + u16 reg; + + ret = hi8435_readb(priv, HI8435_PSEN_REG, &psen); + if (ret < 0) + return ret; + + /* Supply-Open or GND-Open sensing mode */ + mode = !!(psen & BIT(chan->channel / 8)); + + ret = hi8435_readw(priv, mode ? HI8435_SOCENHYS_REG : + HI8435_GOCENHYS_REG, ®); + if (ret < 0) + return ret; + + if (dir == IIO_EV_DIR_FALLING) { + /* falling threshold range 2..21V, hysteresis minimum 2V */ + if (val < 2 || val > 21 || (val + 2) > priv->threshold_hi[mode]) + return -EINVAL; + + if (val == priv->threshold_lo[mode]) + return 0; + + priv->threshold_lo[mode] = val; + + /* hysteresis must not be odd */ + if ((priv->threshold_hi[mode] - priv->threshold_lo[mode]) % 2) + priv->threshold_hi[mode]--; + } else if (dir == IIO_EV_DIR_RISING) { + /* rising threshold range 3..22V, hysteresis minimum 2V */ + if (val < 3 || val > 22 || val < (priv->threshold_lo[mode] + 2)) + return -EINVAL; + + if (val == priv->threshold_hi[mode]) + return 0; + + priv->threshold_hi[mode] = val; + + /* hysteresis must not be odd */ + if ((priv->threshold_hi[mode] - priv->threshold_lo[mode]) % 2) + priv->threshold_lo[mode]++; + } + + /* program thresholds */ + mutex_lock(&priv->lock); + + ret = hi8435_readw(priv, mode ? HI8435_SOCENHYS_REG : + HI8435_GOCENHYS_REG, ®); + if (ret < 0) { + mutex_unlock(&priv->lock); + return ret; + } + + /* hysteresis */ + reg = priv->threshold_hi[mode] - priv->threshold_lo[mode]; + reg <<= 8; + /* threshold center */ + reg |= (priv->threshold_hi[mode] + priv->threshold_lo[mode]); + + ret = hi8435_writew(priv, mode ? HI8435_SOCENHYS_REG : + HI8435_GOCENHYS_REG, reg); + + mutex_unlock(&priv->lock); + + return ret; +} + +static int hi8435_debugfs_reg_access(struct iio_dev *idev, + unsigned reg, unsigned writeval, + unsigned *readval) +{ + struct hi8435_priv *priv = iio_priv(idev); + int ret; + u8 val; + + if (readval != NULL) { + ret = hi8435_readb(priv, reg, &val); + *readval = val; + } else { + val = (u8)writeval; + ret = hi8435_writeb(priv, reg, val); + } + + return ret; +} + +static const struct iio_event_spec hi8435_events[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_VALUE), + }, { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_separate = BIT(IIO_EV_INFO_VALUE), + }, { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_EITHER, + .mask_separate = BIT(IIO_EV_INFO_ENABLE), + }, +}; + +static int hi8435_get_sensing_mode(struct iio_dev *idev, + const struct iio_chan_spec *chan) +{ + struct hi8435_priv *priv = iio_priv(idev); + int ret; + u8 reg; + + ret = hi8435_readb(priv, HI8435_PSEN_REG, ®); + if (ret < 0) + return ret; + + return !!(reg & BIT(chan->channel / 8)); +} + +static int hi8435_set_sensing_mode(struct iio_dev *idev, + const struct iio_chan_spec *chan, + unsigned int mode) +{ + struct hi8435_priv *priv = iio_priv(idev); + int ret; + u8 reg; + + mutex_lock(&priv->lock); + + ret = hi8435_readb(priv, HI8435_PSEN_REG, ®); + if (ret < 0) { + mutex_unlock(&priv->lock); + return ret; + } + + reg &= ~BIT(chan->channel / 8); + if (mode) + reg |= BIT(chan->channel / 8); + + ret = hi8435_writeb(priv, HI8435_PSEN_REG, reg); + + mutex_unlock(&priv->lock); + + return ret; +} + +static const char * const hi8435_sensing_modes[] = { "GND-Open", + "Supply-Open" }; + +static const struct iio_enum hi8435_sensing_mode = { + .items = hi8435_sensing_modes, + .num_items = ARRAY_SIZE(hi8435_sensing_modes), + .get = hi8435_get_sensing_mode, + .set = hi8435_set_sensing_mode, +}; + +static const struct iio_chan_spec_ext_info hi8435_ext_info[] = { + IIO_ENUM("sensing_mode", IIO_SEPARATE, &hi8435_sensing_mode), + {}, +}; + +#define HI8435_VOLTAGE_CHANNEL(num) \ +{ \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = num, \ + .event_spec = hi8435_events, \ + .num_event_specs = ARRAY_SIZE(hi8435_events), \ + .ext_info = hi8435_ext_info, \ +} + +static const struct iio_chan_spec hi8435_channels[] = { + HI8435_VOLTAGE_CHANNEL(0), + HI8435_VOLTAGE_CHANNEL(1), + HI8435_VOLTAGE_CHANNEL(2), + HI8435_VOLTAGE_CHANNEL(3), + HI8435_VOLTAGE_CHANNEL(4), + HI8435_VOLTAGE_CHANNEL(5), + HI8435_VOLTAGE_CHANNEL(6), + HI8435_VOLTAGE_CHANNEL(7), + HI8435_VOLTAGE_CHANNEL(8), + HI8435_VOLTAGE_CHANNEL(9), + HI8435_VOLTAGE_CHANNEL(10), + HI8435_VOLTAGE_CHANNEL(11), + HI8435_VOLTAGE_CHANNEL(12), + HI8435_VOLTAGE_CHANNEL(13), + HI8435_VOLTAGE_CHANNEL(14), + HI8435_VOLTAGE_CHANNEL(15), + HI8435_VOLTAGE_CHANNEL(16), + HI8435_VOLTAGE_CHANNEL(17), + HI8435_VOLTAGE_CHANNEL(18), + HI8435_VOLTAGE_CHANNEL(19), + HI8435_VOLTAGE_CHANNEL(20), + HI8435_VOLTAGE_CHANNEL(21), + HI8435_VOLTAGE_CHANNEL(22), + HI8435_VOLTAGE_CHANNEL(23), + HI8435_VOLTAGE_CHANNEL(24), + HI8435_VOLTAGE_CHANNEL(25), + HI8435_VOLTAGE_CHANNEL(26), + HI8435_VOLTAGE_CHANNEL(27), + HI8435_VOLTAGE_CHANNEL(28), + HI8435_VOLTAGE_CHANNEL(29), + HI8435_VOLTAGE_CHANNEL(30), + HI8435_VOLTAGE_CHANNEL(31), + IIO_CHAN_SOFT_TIMESTAMP(32), +}; + +static const struct iio_info hi8435_info = { + .driver_module = THIS_MODULE, + .read_event_config = &hi8435_read_event_config, + .write_event_config = hi8435_write_event_config, + .read_event_value = &hi8435_read_event_value, + .write_event_value = &hi8435_write_event_value, + .debugfs_reg_access = &hi8435_debugfs_reg_access, +}; + +static void hi8435_iio_push_event(struct iio_dev *idev, unsigned int val) +{ + struct hi8435_priv *priv = iio_priv(idev); + enum iio_event_direction dir; + unsigned int i; + unsigned int status = priv->event_prev_val ^ val; + + if (!status) + return; + + for_each_set_bit(i, &priv->event_scan_mask, 32) { + if (status & BIT(i)) { + dir = val & BIT(i) ? IIO_EV_DIR_RISING : + IIO_EV_DIR_FALLING; + iio_push_event(idev, + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, i, + IIO_EV_TYPE_THRESH, dir), + iio_get_time_ns()); + } + } + + priv->event_prev_val = val; +} + +static irqreturn_t hi8435_trigger_handler(int irq, void *private) +{ + struct iio_poll_func *pf = private; + struct iio_dev *idev = pf->indio_dev; + struct hi8435_priv *priv = iio_priv(idev); + u32 val; + int ret; + + ret = hi8435_readl(priv, HI8435_SO31_0_REG, &val); + if (ret < 0) + goto err_read; + + hi8435_iio_push_event(idev, val); + +err_read: + iio_trigger_notify_done(idev->trig); + + return IRQ_HANDLED; +} + +static int hi8435_probe(struct spi_device *spi) +{ + struct iio_dev *idev; + struct hi8435_priv *priv; + struct gpio_desc *reset_gpio; + int ret; + + idev = devm_iio_device_alloc(&spi->dev, sizeof(*priv)); + if (!idev) + return -ENOMEM; + + priv = iio_priv(idev); + priv->spi = spi; + + reset_gpio = devm_gpiod_get(&spi->dev, NULL, GPIOD_OUT_LOW); + if (IS_ERR(reset_gpio)) { + /* chip s/w reset if h/w reset failed */ + hi8435_writeb(priv, HI8435_CTRL_REG, HI8435_CTRL_SRST); + hi8435_writeb(priv, HI8435_CTRL_REG, 0); + } else { + udelay(5); + gpiod_set_value(reset_gpio, 1); + } + + spi_set_drvdata(spi, idev); + mutex_init(&priv->lock); + + idev->dev.parent = &spi->dev; + idev->name = spi_get_device_id(spi)->name; + idev->modes = INDIO_DIRECT_MODE; + idev->info = &hi8435_info; + idev->channels = hi8435_channels; + idev->num_channels = ARRAY_SIZE(hi8435_channels); + + /* unmask all events */ + priv->event_scan_mask = ~(0); + /* + * There is a restriction in the chip - the hysteresis can not be odd. + * If the hysteresis is set to odd value then chip gets into lock state + * and not functional anymore. + * After chip reset the thresholds are in undefined state, so we need to + * initialize thresholds to some initial values and then prevent + * userspace setting odd hysteresis. + * + * Set threshold low voltage to 2V, threshold high voltage to 4V + * for both GND-Open and Supply-Open sensing modes. + */ + priv->threshold_lo[0] = priv->threshold_lo[1] = 2; + priv->threshold_hi[0] = priv->threshold_hi[1] = 4; + hi8435_writew(priv, HI8435_GOCENHYS_REG, 0x206); + hi8435_writew(priv, HI8435_SOCENHYS_REG, 0x206); + + ret = iio_triggered_event_setup(idev, NULL, hi8435_trigger_handler); + if (ret) + return ret; + + ret = iio_device_register(idev); + if (ret < 0) { + dev_err(&spi->dev, "unable to register device\n"); + goto unregister_triggered_event; + } + + return 0; + +unregister_triggered_event: + iio_triggered_event_cleanup(idev); + return ret; +} + +static int hi8435_remove(struct spi_device *spi) +{ + struct iio_dev *idev = spi_get_drvdata(spi); + + iio_device_unregister(idev); + iio_triggered_event_cleanup(idev); + + return 0; +} + +static const struct of_device_id hi8435_dt_ids[] = { + { .compatible = "holt,hi8435" }, + {}, +}; +MODULE_DEVICE_TABLE(of, hi8435_dt_ids); + +static const struct spi_device_id hi8435_id[] = { + { "hi8435", 0}, + { } +}; +MODULE_DEVICE_TABLE(spi, hi8435_id); + +static struct spi_driver hi8435_driver = { + .driver = { + .name = DRV_NAME, + .of_match_table = of_match_ptr(hi8435_dt_ids), + }, + .probe = hi8435_probe, + .remove = hi8435_remove, + .id_table = hi8435_id, +}; +module_spi_driver(hi8435_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Vladimir Barinov"); +MODULE_DESCRIPTION("HI-8435 threshold detector"); -- cgit v1.1 From 7db75fd615a6d77230315c52f9c79b2102ef2bfe Mon Sep 17 00:00:00 2001 From: Vladimir Barinov Date: Fri, 28 Aug 2015 17:28:20 +0300 Subject: dt: Document Holt HI-8435 bindings These bindings can be used to register Holt HI-8435 threshold detector Signed-off-by: Vladimir Barinov Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/adc/hi8435.txt | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/adc/hi8435.txt diff --git a/Documentation/devicetree/bindings/iio/adc/hi8435.txt b/Documentation/devicetree/bindings/iio/adc/hi8435.txt new file mode 100644 index 0000000..3b0348c --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/hi8435.txt @@ -0,0 +1,21 @@ +Holt Integrated Circuits HI-8435 threshold detector bindings + +Required properties: + - compatible: should be "holt,hi8435" + - reg: spi chip select number for the device + +Recommended properties: + - spi-max-frequency: definition as per + Documentation/devicetree/bindings/spi/spi-bus.txt + +Optional properties: + - gpios: GPIO used for controlling the reset pin + +Example: +sensor@0 { + compatible = "holt,hi8435"; + reg = <0>; + gpios = <&gpio6 1 0>; + + spi-max-frequency = <1000000>; +}; -- cgit v1.1 From 4839367d99e3b067d3c2e9404ae320c100d090c7 Mon Sep 17 00:00:00 2001 From: Matt Ranostay Date: Sun, 6 Sep 2015 13:27:39 -0700 Subject: iio: humidity: add HDC100x support Add support for the HDC100x temperature and humidity sensors including the resistive heater element. Signed-off-by: Matt Ranostay Signed-off-by: Jonathan Cameron --- .../ABI/testing/sysfs-bus-iio-humidity-hdc100x | 9 + drivers/iio/humidity/Kconfig | 10 + drivers/iio/humidity/Makefile | 1 + drivers/iio/humidity/hdc100x.c | 319 +++++++++++++++++++++ 4 files changed, 339 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-humidity-hdc100x create mode 100644 drivers/iio/humidity/hdc100x.c diff --git a/Documentation/ABI/testing/sysfs-bus-iio-humidity-hdc100x b/Documentation/ABI/testing/sysfs-bus-iio-humidity-hdc100x new file mode 100644 index 0000000..b72bb62 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-iio-humidity-hdc100x @@ -0,0 +1,9 @@ +What: /sys/bus/iio/devices/iio:deviceX/out_current_heater_raw +What: /sys/bus/iio/devices/iio:deviceX/out_current_heater_raw_available +KernelVersion: 4.3 +Contact: linux-iio@vger.kernel.org +Description: + Controls the heater device within the humidity sensor to get + rid of excess condensation. + + Valid control values are 0 = OFF, and 1 = ON. diff --git a/drivers/iio/humidity/Kconfig b/drivers/iio/humidity/Kconfig index 688c0d1..353ee9a 100644 --- a/drivers/iio/humidity/Kconfig +++ b/drivers/iio/humidity/Kconfig @@ -12,6 +12,16 @@ config DHT11 Other sensors should work as well as long as they speak the same protocol. +config HDC100X + tristate "TI HDC100x relative humidity and temperature sensor" + depends on I2C + help + Say yes here to build support for the TI HDC100x series of + relative humidity and temperature sensors. + + To compile this driver as a module, choose M here: the module + will be called hdc100x. + config SI7005 tristate "SI7005 relative humidity and temperature sensor" depends on I2C diff --git a/drivers/iio/humidity/Makefile b/drivers/iio/humidity/Makefile index 86e2d26..3e62c0a 100644 --- a/drivers/iio/humidity/Makefile +++ b/drivers/iio/humidity/Makefile @@ -3,5 +3,6 @@ # obj-$(CONFIG_DHT11) += dht11.o +obj-$(CONFIG_HDC100X) += hdc100x.o obj-$(CONFIG_SI7005) += si7005.o obj-$(CONFIG_SI7020) += si7020.o diff --git a/drivers/iio/humidity/hdc100x.c b/drivers/iio/humidity/hdc100x.c new file mode 100644 index 0000000..2824578 --- /dev/null +++ b/drivers/iio/humidity/hdc100x.c @@ -0,0 +1,319 @@ +/* + * hdc100x.c - Support for the TI HDC100x temperature + humidity sensors + * + * Copyright (C) 2015 Matt Ranostay + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include + +#include +#include + +#define HDC100X_REG_TEMP 0x00 +#define HDC100X_REG_HUMIDITY 0x01 + +#define HDC100X_REG_CONFIG 0x02 +#define HDC100X_REG_CONFIG_HEATER_EN BIT(13) + +struct hdc100x_data { + struct i2c_client *client; + struct mutex lock; + u16 config; + + /* integration time of the sensor */ + int adc_int_us[2]; +}; + +/* integration time in us */ +static const int hdc100x_int_time[][3] = { + { 6350, 3650, 0 }, /* IIO_TEMP channel*/ + { 6500, 3850, 2500 }, /* IIO_HUMIDITYRELATIVE channel */ +}; + +/* HDC100X_REG_CONFIG shift and mask values */ +static const struct { + int shift; + int mask; +} hdc100x_resolution_shift[2] = { + { /* IIO_TEMP channel */ + .shift = 10, + .mask = 1 + }, + { /* IIO_HUMIDITYRELATIVE channel */ + .shift = 8, + .mask = 2, + }, +}; + +static IIO_CONST_ATTR(temp_integration_time_available, + "0.00365 0.00635"); + +static IIO_CONST_ATTR(humidityrelative_integration_time_available, + "0.0025 0.00385 0.0065"); + +static IIO_CONST_ATTR(out_current_heater_raw_available, + "0 1"); + +static struct attribute *hdc100x_attributes[] = { + &iio_const_attr_temp_integration_time_available.dev_attr.attr, + &iio_const_attr_humidityrelative_integration_time_available.dev_attr.attr, + &iio_const_attr_out_current_heater_raw_available.dev_attr.attr, + NULL +}; + +static struct attribute_group hdc100x_attribute_group = { + .attrs = hdc100x_attributes, +}; + +static const struct iio_chan_spec hdc100x_channels[] = { + { + .type = IIO_TEMP, + .address = HDC100X_REG_TEMP, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_OFFSET), + }, + { + .type = IIO_HUMIDITYRELATIVE, + .address = HDC100X_REG_HUMIDITY, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_INT_TIME) + }, + { + .type = IIO_CURRENT, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .extend_name = "heater", + .output = 1, + }, +}; + +static int hdc100x_update_config(struct hdc100x_data *data, int mask, int val) +{ + int tmp = (~mask & data->config) | val; + int ret; + + ret = i2c_smbus_write_word_swapped(data->client, + HDC100X_REG_CONFIG, tmp); + if (!ret) + data->config = tmp; + + return ret; +} + +static int hdc100x_set_it_time(struct hdc100x_data *data, int chan, int val2) +{ + int shift = hdc100x_resolution_shift[chan].shift; + int ret = -EINVAL; + int i; + + for (i = 0; i < ARRAY_SIZE(hdc100x_int_time[chan]); i++) { + if (val2 && val2 == hdc100x_int_time[chan][i]) { + ret = hdc100x_update_config(data, + hdc100x_resolution_shift[chan].mask << shift, + i << shift); + if (!ret) + data->adc_int_us[chan] = val2; + break; + } + } + + return ret; +} + +static int hdc100x_get_measurement(struct hdc100x_data *data, + struct iio_chan_spec const *chan) +{ + struct i2c_client *client = data->client; + int delay = data->adc_int_us[chan->address]; + int ret; + int val; + + /* start measurement */ + ret = i2c_smbus_write_byte(client, chan->address); + if (ret < 0) { + dev_err(&client->dev, "cannot start measurement"); + return ret; + } + + /* wait for integration time to pass */ + usleep_range(delay, delay + 1000); + + /* + * i2c_smbus_read_word_data cannot() be used here due to the command + * value not being understood and causes NAKs preventing any reading + * from being accessed. + */ + ret = i2c_smbus_read_byte(client); + if (ret < 0) { + dev_err(&client->dev, "cannot read high byte measurement"); + return ret; + } + val = ret << 6; + + ret = i2c_smbus_read_byte(client); + if (ret < 0) { + dev_err(&client->dev, "cannot read low byte measurement"); + return ret; + } + val |= ret >> 2; + + return val; +} + +static int hdc100x_get_heater_status(struct hdc100x_data *data) +{ + return !!(data->config & HDC100X_REG_CONFIG_HEATER_EN); +} + +static int hdc100x_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + struct hdc100x_data *data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: { + int ret; + + mutex_lock(&data->lock); + if (chan->type == IIO_CURRENT) { + *val = hdc100x_get_heater_status(data); + ret = IIO_VAL_INT; + } else { + ret = hdc100x_get_measurement(data, chan); + if (ret >= 0) { + *val = ret; + ret = IIO_VAL_INT; + } + } + mutex_unlock(&data->lock); + return ret; + } + case IIO_CHAN_INFO_INT_TIME: + *val = 0; + *val2 = data->adc_int_us[chan->address]; + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_SCALE: + if (chan->type == IIO_TEMP) { + *val = 165; + *val2 = 65536 >> 2; + return IIO_VAL_FRACTIONAL; + } else { + *val = 0; + *val2 = 10000; + return IIO_VAL_INT_PLUS_MICRO; + } + break; + case IIO_CHAN_INFO_OFFSET: + *val = -40; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int hdc100x_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct hdc100x_data *data = iio_priv(indio_dev); + int ret = -EINVAL; + + switch (mask) { + case IIO_CHAN_INFO_INT_TIME: + if (val != 0) + return -EINVAL; + + mutex_lock(&data->lock); + ret = hdc100x_set_it_time(data, chan->address, val2); + mutex_unlock(&data->lock); + return ret; + case IIO_CHAN_INFO_RAW: + if (chan->type != IIO_CURRENT || val2 != 0) + return -EINVAL; + + mutex_lock(&data->lock); + ret = hdc100x_update_config(data, HDC100X_REG_CONFIG_HEATER_EN, + val ? HDC100X_REG_CONFIG_HEATER_EN : 0); + mutex_unlock(&data->lock); + return ret; + default: + return -EINVAL; + } +} + +static const struct iio_info hdc100x_info = { + .read_raw = hdc100x_read_raw, + .write_raw = hdc100x_write_raw, + .attrs = &hdc100x_attribute_group, + .driver_module = THIS_MODULE, +}; + +static int hdc100x_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct iio_dev *indio_dev; + struct hdc100x_data *data; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BYTE)) + return -ENODEV; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + i2c_set_clientdata(client, indio_dev); + data->client = client; + mutex_init(&data->lock); + + indio_dev->dev.parent = &client->dev; + indio_dev->name = dev_name(&client->dev); + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &hdc100x_info; + + indio_dev->channels = hdc100x_channels; + indio_dev->num_channels = ARRAY_SIZE(hdc100x_channels); + + /* be sure we are in a known state */ + hdc100x_set_it_time(data, 0, hdc100x_int_time[0][0]); + hdc100x_set_it_time(data, 1, hdc100x_int_time[1][0]); + + return devm_iio_device_register(&client->dev, indio_dev); +} + +static const struct i2c_device_id hdc100x_id[] = { + { "hdc100x", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, hdc100x_id); + +static struct i2c_driver hdc100x_driver = { + .driver = { + .name = "hdc100x", + }, + .probe = hdc100x_probe, + .id_table = hdc100x_id, +}; +module_i2c_driver(hdc100x_driver); + +MODULE_AUTHOR("Matt Ranostay "); +MODULE_DESCRIPTION("TI HDC100x humidity and temperature sensor driver"); +MODULE_LICENSE("GPL"); -- cgit v1.1 From 01b721536b4128c1e17c596650198bfc36ad8c6c Mon Sep 17 00:00:00 2001 From: Shraddha Barke Date: Thu, 10 Sep 2015 22:00:30 +0530 Subject: Staging: iio: meter: Prefer using the BIT macro This patch replaces bit shifting on 1 with the BIT(x) macro This was done with coccinelle: @@ int g; @@ -(1 << g) +BIT(g) Signed-off-by: Shraddha Barke Signed-off-by: Jonathan Cameron --- drivers/staging/iio/meter/ade7753.c | 8 ++++---- drivers/staging/iio/meter/ade7754.c | 6 +++--- drivers/staging/iio/meter/ade7758_core.c | 6 +++--- drivers/staging/iio/meter/ade7759.c | 8 ++++---- drivers/staging/iio/meter/ade7854.c | 6 +++--- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/drivers/staging/iio/meter/ade7753.c b/drivers/staging/iio/meter/ade7753.c index ffc7f0d..3d2e50c 100644 --- a/drivers/staging/iio/meter/ade7753.c +++ b/drivers/staging/iio/meter/ade7753.c @@ -219,7 +219,7 @@ static int ade7753_reset(struct device *dev) u16 val; ade7753_spi_read_reg_16(dev, ADE7753_MODE, &val); - val |= 1 << 6; /* Software Chip Reset */ + val |= BIT(6); /* Software Chip Reset */ return ade7753_spi_write_reg_16(dev, ADE7753_MODE, val); } @@ -328,10 +328,10 @@ static int ade7753_set_irq(struct device *dev, bool enable) goto error_ret; if (enable) - irqen |= 1 << 3; /* Enables an interrupt when a data is + irqen |= BIT(3); /* Enables an interrupt when a data is present in the waveform register */ else - irqen &= ~(1 << 3); + irqen &= ~BIT(3); ret = ade7753_spi_write_reg_8(dev, ADE7753_IRQEN, irqen); @@ -345,7 +345,7 @@ static int ade7753_stop_device(struct device *dev) u16 val; ade7753_spi_read_reg_16(dev, ADE7753_MODE, &val); - val |= 1 << 4; /* AD converters can be turned off */ + val |= BIT(4); /* AD converters can be turned off */ return ade7753_spi_write_reg_16(dev, ADE7753_MODE, val); } diff --git a/drivers/staging/iio/meter/ade7754.c b/drivers/staging/iio/meter/ade7754.c index f12b2e5..8552c76 100644 --- a/drivers/staging/iio/meter/ade7754.c +++ b/drivers/staging/iio/meter/ade7754.c @@ -223,7 +223,7 @@ static int ade7754_reset(struct device *dev) if (ret < 0) return ret; - val |= 1 << 6; /* Software Chip Reset */ + val |= BIT(6); /* Software Chip Reset */ return ade7754_spi_write_reg_8(dev, ADE7754_OPMODE, val); } @@ -350,10 +350,10 @@ static int ade7754_set_irq(struct device *dev, bool enable) goto error_ret; if (enable) - irqen |= 1 << 14; /* Enables an interrupt when a data is + irqen |= BIT(14); /* Enables an interrupt when a data is present in the waveform register */ else - irqen &= ~(1 << 14); + irqen &= ~BIT(14); ret = ade7754_spi_write_reg_16(dev, ADE7754_IRQEN, irqen); if (ret) diff --git a/drivers/staging/iio/meter/ade7758_core.c b/drivers/staging/iio/meter/ade7758_core.c index 77141ae..3883808 100644 --- a/drivers/staging/iio/meter/ade7758_core.c +++ b/drivers/staging/iio/meter/ade7758_core.c @@ -308,7 +308,7 @@ static int ade7758_reset(struct device *dev) dev_err(dev, "Failed to read opmode reg\n"); return ret; } - val |= 1 << 6; /* Software Chip Reset */ + val |= BIT(6); /* Software Chip Reset */ ret = ade7758_spi_write_reg_8(dev, ADE7758_OPMODE, val); if (ret < 0) dev_err(dev, "Failed to write opmode reg\n"); @@ -426,10 +426,10 @@ int ade7758_set_irq(struct device *dev, bool enable) goto error_ret; if (enable) - irqen |= 1 << 16; /* Enables an interrupt when a data is + irqen |= BIT(16); /* Enables an interrupt when a data is present in the waveform register */ else - irqen &= ~(1 << 16); + irqen &= ~BIT(16); ret = ade7758_spi_write_reg_24(dev, ADE7758_MASK, irqen); if (ret) diff --git a/drivers/staging/iio/meter/ade7759.c b/drivers/staging/iio/meter/ade7759.c index dbceda1..23e7392 100644 --- a/drivers/staging/iio/meter/ade7759.c +++ b/drivers/staging/iio/meter/ade7759.c @@ -224,7 +224,7 @@ static int ade7759_reset(struct device *dev) if (ret < 0) return ret; - val |= 1 << 6; /* Software Chip Reset */ + val |= BIT(6); /* Software Chip Reset */ return ade7759_spi_write_reg_16(dev, ADE7759_MODE, val); @@ -288,10 +288,10 @@ static int ade7759_set_irq(struct device *dev, bool enable) goto error_ret; if (enable) - irqen |= 1 << 3; /* Enables an interrupt when a data is + irqen |= BIT(3); /* Enables an interrupt when a data is present in the waveform register */ else - irqen &= ~(1 << 3); + irqen &= ~BIT(3); ret = ade7759_spi_write_reg_8(dev, ADE7759_IRQEN, irqen); @@ -314,7 +314,7 @@ static int ade7759_stop_device(struct device *dev) return ret; } - val |= 1 << 4; /* AD converters can be turned off */ + val |= BIT(4); /* AD converters can be turned off */ return ade7759_spi_write_reg_16(dev, ADE7759_MODE, val); } diff --git a/drivers/staging/iio/meter/ade7854.c b/drivers/staging/iio/meter/ade7854.c index d620bbd..a838835 100644 --- a/drivers/staging/iio/meter/ade7854.c +++ b/drivers/staging/iio/meter/ade7854.c @@ -181,7 +181,7 @@ static int ade7854_reset(struct device *dev) u16 val; st->read_reg_16(dev, ADE7854_CONFIG, &val); - val |= 1 << 7; /* Software Chip Reset */ + val |= BIT(7); /* Software Chip Reset */ return st->write_reg_16(dev, ADE7854_CONFIG, val); } @@ -420,10 +420,10 @@ static int ade7854_set_irq(struct device *dev, bool enable) goto error_ret; if (enable) - irqen |= 1 << 17; /* 1: interrupt enabled when all periodical + irqen |= BIT(17); /* 1: interrupt enabled when all periodical (at 8 kHz rate) DSP computations finish. */ else - irqen &= ~(1 << 17); + irqen &= ~BIT(17); ret = st->write_reg_32(dev, ADE7854_MASK0, irqen); if (ret) -- cgit v1.1 From afc7da6e486c04244766da9fe2574d972125a602 Mon Sep 17 00:00:00 2001 From: Shraddha Barke Date: Thu, 10 Sep 2015 22:00:29 +0530 Subject: Staging: iio: addac: Prefer using the BIT macro This patch replaces bit shifting on 1 with the BIT(x) macro. This was done with coccinelle: @@ int g; @@ -(1 << g) +BIT(g) Signed-off-by: Shraddha Barke Signed-off-by: Jonathan Cameron --- drivers/staging/iio/addac/adt7316.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/staging/iio/addac/adt7316.c b/drivers/staging/iio/addac/adt7316.c index 5b11b42..a1dd745 100644 --- a/drivers/staging/iio/addac/adt7316.c +++ b/drivers/staging/iio/addac/adt7316.c @@ -1756,43 +1756,43 @@ static irqreturn_t adt7316_event_handler(int irq, void *private) stat1 &= 0x1F; time = iio_get_time_ns(); - if (stat1 & (1 << 0)) + if (stat1 & BIT(0)) iio_push_event(indio_dev, IIO_UNMOD_EVENT_CODE(IIO_TEMP, 0, IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING), time); - if (stat1 & (1 << 1)) + if (stat1 & BIT(1)) iio_push_event(indio_dev, IIO_UNMOD_EVENT_CODE(IIO_TEMP, 0, IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING), time); - if (stat1 & (1 << 2)) + if (stat1 & BIT(2)) iio_push_event(indio_dev, IIO_UNMOD_EVENT_CODE(IIO_TEMP, 1, IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING), time); - if (stat1 & (1 << 3)) + if (stat1 & BIT(3)) iio_push_event(indio_dev, IIO_UNMOD_EVENT_CODE(IIO_TEMP, 1, IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING), time); - if (stat1 & (1 << 5)) + if (stat1 & BIT(5)) iio_push_event(indio_dev, IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 1, IIO_EV_TYPE_THRESH, IIO_EV_DIR_EITHER), time); - if (stat1 & (1 << 6)) + if (stat1 & BIT(6)) iio_push_event(indio_dev, IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 2, IIO_EV_TYPE_THRESH, IIO_EV_DIR_EITHER), time); - if (stat1 & (1 << 7)) + if (stat1 & BIT(7)) iio_push_event(indio_dev, IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 3, IIO_EV_TYPE_THRESH, -- cgit v1.1 From c3cdd6e48e35b7a02f28e301ef30a87ff3cd6527 Mon Sep 17 00:00:00 2001 From: Martin Kepplinger Date: Tue, 1 Sep 2015 13:45:08 +0200 Subject: iio: mma8452: refactor for seperating chip specific data This adds a struct mma_chip_info to hold data that will remain specific to the chip in use. It is provided during probe() and linked in struct of_device_id. Also this suggests that the driver is called "mma8452" and now handles the MMA8452Q device, but is not limited to it. Signed-off-by: Martin Kepplinger Signed-off-by: Christoph Muellner Signed-off-by: Jonathan Cameron --- drivers/iio/accel/mma8452.c | 183 ++++++++++++++++++++++++++++++++------------ 1 file changed, 134 insertions(+), 49 deletions(-) diff --git a/drivers/iio/accel/mma8452.c b/drivers/iio/accel/mma8452.c index b921d84..f28428fa 100644 --- a/drivers/iio/accel/mma8452.c +++ b/drivers/iio/accel/mma8452.c @@ -22,6 +22,7 @@ #include #include #include +#include #define MMA8452_STATUS 0x00 #define MMA8452_STATUS_DRDY (BIT(2) | BIT(1) | BIT(0)) @@ -74,6 +75,52 @@ struct mma8452_data { struct mutex lock; u8 ctrl_reg1; u8 data_cfg; + const struct mma_chip_info *chip_info; +}; + +/** + * struct mma_chip_info - chip specific data for Freescale's accelerometers + * @chip_id: WHO_AM_I register's value + * @channels: struct iio_chan_spec matching the device's + * capabilities + * @num_channels: number of channels + * @mma_scales: scale factors for converting register values + * to m/s^2; 3 modes: 2g, 4g, 8g; 2 integers + * per mode: m/s^2 and micro m/s^2 + * @ev_cfg: event config register address + * @ev_cfg_ele: latch bit in event config register + * @ev_cfg_chan_shift: number of the bit to enable events in X + * direction; in event config register + * @ev_src: event source register address + * @ev_src_xe: bit in event source register that indicates + * an event in X direction + * @ev_src_ye: bit in event source register that indicates + * an event in Y direction + * @ev_src_ze: bit in event source register that indicates + * an event in Z direction + * @ev_ths: event threshold register address + * @ev_ths_mask: mask for the threshold value + * @ev_count: event count (period) register address + * + * Since not all chips supported by the driver support comparing high pass + * filtered data for events (interrupts), different interrupt sources are + * used for different chips and the relevant registers are included here. + */ +struct mma_chip_info { + u8 chip_id; + const struct iio_chan_spec *channels; + int num_channels; + const int mma_scales[3][2]; + u8 ev_cfg; + u8 ev_cfg_ele; + u8 ev_cfg_chan_shift; + u8 ev_src; + u8 ev_src_xe; + u8 ev_src_ye; + u8 ev_src_ze; + u8 ev_ths; + u8 ev_ths_mask; + u8 ev_count; }; static int mma8452_drdy(struct mma8452_data *data) @@ -143,16 +190,6 @@ static const int mma8452_samp_freq[8][2] = { {6, 250000}, {1, 560000} }; -/* - * Hardware has fullscale of -2G, -4G, -8G corresponding to raw value -2048 - * The userspace interface uses m/s^2 and we declare micro units - * So scale factor is given by: - * g * N * 1000000 / 2048 for N = 2, 4, 8 and g = 9.80665 - */ -static const int mma8452_scales[3][2] = { - {0, 9577}, {0, 19154}, {0, 38307} -}; - /* Datasheet table 35 (step time vs sample frequency) */ static const int mma8452_transient_time_step_us[8] = { 1250, @@ -189,8 +226,11 @@ static ssize_t mma8452_show_scale_avail(struct device *dev, struct device_attribute *attr, char *buf) { - return mma8452_show_int_plus_micros(buf, mma8452_scales, - ARRAY_SIZE(mma8452_scales)); + struct mma8452_data *data = iio_priv(i2c_get_clientdata( + to_i2c_client(dev))); + + return mma8452_show_int_plus_micros(buf, data->chip_info->mma_scales, + ARRAY_SIZE(data->chip_info->mma_scales)); } static ssize_t mma8452_show_hp_cutoff_avail(struct device *dev, @@ -221,9 +261,8 @@ static int mma8452_get_samp_freq_index(struct mma8452_data *data, static int mma8452_get_scale_index(struct mma8452_data *data, int val, int val2) { - return mma8452_get_int_plus_micros_index(mma8452_scales, - ARRAY_SIZE(mma8452_scales), - val, val2); + return mma8452_get_int_plus_micros_index(data->chip_info->mma_scales, + ARRAY_SIZE(data->chip_info->mma_scales), val, val2); } static int mma8452_get_hp_filter_index(struct mma8452_data *data, @@ -270,14 +309,15 @@ static int mma8452_read_raw(struct iio_dev *indio_dev, if (ret < 0) return ret; - *val = sign_extend32(be16_to_cpu(buffer[chan->scan_index]) >> 4, - 11); + *val = sign_extend32(be16_to_cpu( + buffer[chan->scan_index]) >> chan->scan_type.shift, + chan->scan_type.realbits - 1); return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: i = data->data_cfg & MMA8452_DATA_CFG_FS_MASK; - *val = mma8452_scales[i][0]; - *val2 = mma8452_scales[i][1]; + *val = data->chip_info->mma_scales[i][0]; + *val2 = data->chip_info->mma_scales[i][1]; return IIO_VAL_INT_PLUS_MICRO; case IIO_CHAN_INFO_SAMP_FREQ: @@ -439,17 +479,17 @@ static int mma8452_read_thresh(struct iio_dev *indio_dev, switch (info) { case IIO_EV_INFO_VALUE: ret = i2c_smbus_read_byte_data(data->client, - MMA8452_TRANSIENT_THS); + data->chip_info->ev_ths); if (ret < 0) return ret; - *val = ret & MMA8452_TRANSIENT_THS_MASK; + *val = ret & data->chip_info->ev_ths_mask; return IIO_VAL_INT; case IIO_EV_INFO_PERIOD: ret = i2c_smbus_read_byte_data(data->client, - MMA8452_TRANSIENT_COUNT); + data->chip_info->ev_count); if (ret < 0) return ret; @@ -497,7 +537,8 @@ static int mma8452_write_thresh(struct iio_dev *indio_dev, if (val < 0 || val > MMA8452_TRANSIENT_THS_MASK) return -EINVAL; - return mma8452_change_config(data, MMA8452_TRANSIENT_THS, val); + return mma8452_change_config(data, data->chip_info->ev_ths, + val); case IIO_EV_INFO_PERIOD: steps = (val * USEC_PER_SEC + val2) / @@ -507,7 +548,7 @@ static int mma8452_write_thresh(struct iio_dev *indio_dev, if (steps < 0 || steps > 0xff) return -EINVAL; - return mma8452_change_config(data, MMA8452_TRANSIENT_COUNT, + return mma8452_change_config(data, data->chip_info->ev_count, steps); case IIO_EV_INFO_HIGH_PASS_FILTER_3DB: @@ -538,13 +579,15 @@ static int mma8452_read_event_config(struct iio_dev *indio_dev, enum iio_event_direction dir) { struct mma8452_data *data = iio_priv(indio_dev); + const struct mma_chip_info *chip = data->chip_info; int ret; - ret = i2c_smbus_read_byte_data(data->client, MMA8452_TRANSIENT_CFG); + ret = i2c_smbus_read_byte_data(data->client, + data->chip_info->ev_cfg); if (ret < 0) return ret; - return ret & MMA8452_TRANSIENT_CFG_CHAN(chan->scan_index) ? 1 : 0; + return !!(ret & BIT(chan->scan_index + chip->ev_cfg_chan_shift)); } static int mma8452_write_event_config(struct iio_dev *indio_dev, @@ -554,20 +597,21 @@ static int mma8452_write_event_config(struct iio_dev *indio_dev, int state) { struct mma8452_data *data = iio_priv(indio_dev); + const struct mma_chip_info *chip = data->chip_info; int val; - val = i2c_smbus_read_byte_data(data->client, MMA8452_TRANSIENT_CFG); + val = i2c_smbus_read_byte_data(data->client, chip->ev_cfg); if (val < 0) return val; if (state) - val |= MMA8452_TRANSIENT_CFG_CHAN(chan->scan_index); + val |= BIT(chan->scan_index + chip->ev_cfg_chan_shift); else - val &= ~MMA8452_TRANSIENT_CFG_CHAN(chan->scan_index); + val &= ~BIT(chan->scan_index + chip->ev_cfg_chan_shift); val |= MMA8452_TRANSIENT_CFG_ELE; - return mma8452_change_config(data, MMA8452_TRANSIENT_CFG, val); + return mma8452_change_config(data, chip->ev_cfg, val); } static void mma8452_transient_interrupt(struct iio_dev *indio_dev) @@ -576,25 +620,25 @@ static void mma8452_transient_interrupt(struct iio_dev *indio_dev) s64 ts = iio_get_time_ns(); int src; - src = i2c_smbus_read_byte_data(data->client, MMA8452_TRANSIENT_SRC); + src = i2c_smbus_read_byte_data(data->client, data->chip_info->ev_src); if (src < 0) return; - if (src & MMA8452_TRANSIENT_SRC_XTRANSE) + if (src & data->chip_info->ev_src_xe) iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_X, IIO_EV_TYPE_MAG, IIO_EV_DIR_RISING), ts); - if (src & MMA8452_TRANSIENT_SRC_YTRANSE) + if (src & data->chip_info->ev_src_ye) iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_Y, IIO_EV_TYPE_MAG, IIO_EV_DIR_RISING), ts); - if (src & MMA8452_TRANSIENT_SRC_ZTRANSE) + if (src & data->chip_info->ev_src_ze) iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_Z, IIO_EV_TYPE_MAG, @@ -696,7 +740,7 @@ static struct attribute_group mma8452_event_attribute_group = { .name = "events", }; -#define MMA8452_CHANNEL(axis, idx) { \ +#define MMA8452_CHANNEL(axis, idx, bits) { \ .type = IIO_ACCEL, \ .modified = 1, \ .channel2 = IIO_MOD_##axis, \ @@ -708,9 +752,9 @@ static struct attribute_group mma8452_event_attribute_group = { .scan_index = idx, \ .scan_type = { \ .sign = 's', \ - .realbits = 12, \ + .realbits = (bits), \ .storagebits = 16, \ - .shift = 4, \ + .shift = 16 - (bits), \ .endianness = IIO_BE, \ }, \ .event_spec = mma8452_transient_event, \ @@ -718,12 +762,42 @@ static struct attribute_group mma8452_event_attribute_group = { } static const struct iio_chan_spec mma8452_channels[] = { - MMA8452_CHANNEL(X, 0), - MMA8452_CHANNEL(Y, 1), - MMA8452_CHANNEL(Z, 2), + MMA8452_CHANNEL(X, 0, 12), + MMA8452_CHANNEL(Y, 1, 12), + MMA8452_CHANNEL(Z, 2, 12), IIO_CHAN_SOFT_TIMESTAMP(3), }; +enum { + mma8452, +}; + +static const struct mma_chip_info mma_chip_info_table[] = { + [mma8452] = { + .chip_id = MMA8452_DEVICE_ID, + .channels = mma8452_channels, + .num_channels = ARRAY_SIZE(mma8452_channels), + /* + * Hardware has fullscale of -2G, -4G, -8G corresponding to + * raw value -2048 for 12 bit or -512 for 10 bit. + * The userspace interface uses m/s^2 and we declare micro units + * So scale factor for 12 bit here is given by: + * g * N * 1000000 / 2048 for N = 2, 4, 8 and g=9.80665 + */ + .mma_scales = { {0, 9577}, {0, 19154}, {0, 38307} }, + .ev_cfg = MMA8452_TRANSIENT_CFG, + .ev_cfg_ele = MMA8452_TRANSIENT_CFG_ELE, + .ev_cfg_chan_shift = 1, + .ev_src = MMA8452_TRANSIENT_SRC, + .ev_src_xe = MMA8452_TRANSIENT_SRC_XTRANSE, + .ev_src_ye = MMA8452_TRANSIENT_SRC_YTRANSE, + .ev_src_ze = MMA8452_TRANSIENT_SRC_ZTRANSE, + .ev_ths = MMA8452_TRANSIENT_THS, + .ev_ths_mask = MMA8452_TRANSIENT_THS_MASK, + .ev_count = MMA8452_TRANSIENT_COUNT, + }, +}; + static struct attribute *mma8452_attributes[] = { &iio_dev_attr_sampling_frequency_available.dev_attr.attr, &iio_dev_attr_in_accel_scale_available.dev_attr.attr, @@ -841,12 +915,19 @@ static int mma8452_reset(struct i2c_client *client) return -ETIMEDOUT; } +static const struct of_device_id mma8452_dt_ids[] = { + { .compatible = "fsl,mma8452", .data = &mma_chip_info_table[mma8452] }, + { } +}; +MODULE_DEVICE_TABLE(of, mma8452_dt_ids); + static int mma8452_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct mma8452_data *data; struct iio_dev *indio_dev; int ret; + const struct of_device_id *match; ret = i2c_smbus_read_byte_data(client, MMA8452_WHO_AM_I); if (ret < 0) @@ -854,6 +935,12 @@ static int mma8452_probe(struct i2c_client *client, if (ret != MMA8452_DEVICE_ID) return -ENODEV; + match = of_match_device(mma8452_dt_ids, &client->dev); + if (!match) { + dev_err(&client->dev, "unknown device model\n"); + return -ENODEV; + } + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); if (!indio_dev) return -ENOMEM; @@ -861,14 +948,18 @@ static int mma8452_probe(struct i2c_client *client, data = iio_priv(indio_dev); data->client = client; mutex_init(&data->lock); + data->chip_info = match->data; + + dev_info(&client->dev, "registering %s accelerometer; ID 0x%x\n", + match->compatible, data->chip_info->chip_id); i2c_set_clientdata(client, indio_dev); indio_dev->info = &mma8452_info; indio_dev->name = id->name; indio_dev->dev.parent = &client->dev; indio_dev->modes = INDIO_DIRECT_MODE; - indio_dev->channels = mma8452_channels; - indio_dev->num_channels = ARRAY_SIZE(mma8452_channels); + indio_dev->channels = data->chip_info->channels; + indio_dev->num_channels = data->chip_info->num_channels; indio_dev->available_scan_masks = mma8452_scan_masks; ret = mma8452_reset(client); @@ -987,17 +1078,11 @@ static SIMPLE_DEV_PM_OPS(mma8452_pm_ops, mma8452_suspend, mma8452_resume); #endif static const struct i2c_device_id mma8452_id[] = { - { "mma8452", 0 }, + { "mma8452", mma8452 }, { } }; MODULE_DEVICE_TABLE(i2c, mma8452_id); -static const struct of_device_id mma8452_dt_ids[] = { - { .compatible = "fsl,mma8452" }, - { } -}; -MODULE_DEVICE_TABLE(of, mma8452_dt_ids); - static struct i2c_driver mma8452_driver = { .driver = { .name = "mma8452", -- cgit v1.1 From c5ea1b58e8f51d8cd72e46cc398742988a614054 Mon Sep 17 00:00:00 2001 From: Martin Kepplinger Date: Tue, 1 Sep 2015 13:45:09 +0200 Subject: iio: mma8452: add support for MMA8453Q accelerometer chip This adds support for the 10 bit version if Freescale's accelerometers of this series. The datasheet is available at Freescale's website: http://cache.freescale.com/files/sensors/doc/data_sheet/MMA8453Q.pdf It creates a devicetree bindings file to document the new functionality and removes the driver from the trivial-devices list. Signed-off-by: Martin Kepplinger Signed-off-by: Christoph Muellner Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/i2c/trivial-devices.txt | 1 - .../devicetree/bindings/iio/accel/mma8452.txt | 22 +++++++++++++ drivers/iio/accel/Kconfig | 6 ++-- drivers/iio/accel/mma8452.c | 37 ++++++++++++++++++++-- 4 files changed, 59 insertions(+), 7 deletions(-) create mode 100644 Documentation/devicetree/bindings/iio/accel/mma8452.txt diff --git a/Documentation/devicetree/bindings/i2c/trivial-devices.txt b/Documentation/devicetree/bindings/i2c/trivial-devices.txt index af1bc50..e1c07dd 100644 --- a/Documentation/devicetree/bindings/i2c/trivial-devices.txt +++ b/Documentation/devicetree/bindings/i2c/trivial-devices.txt @@ -54,7 +54,6 @@ epson,rx8581 I2C-BUS INTERFACE REAL TIME CLOCK MODULE fsl,mag3110 MAG3110: Xtrinsic High Accuracy, 3D Magnetometer fsl,mc13892 MC13892: Power Management Integrated Circuit (PMIC) for i.MX35/51 fsl,mma8450 MMA8450Q: Xtrinsic Low-power, 3-axis Xtrinsic Accelerometer -fsl,mma8452 MMA8452Q: 3-axis 12-bit / 8-bit Digital Accelerometer fsl,mpr121 MPR121: Proximity Capacitive Touch Sensor Controller fsl,sgtl5000 SGTL5000: Ultra Low-Power Audio Codec gmt,g751 G751: Digital Temperature Sensor and Thermal Watchdog with Two-Wire Interface diff --git a/Documentation/devicetree/bindings/iio/accel/mma8452.txt b/Documentation/devicetree/bindings/iio/accel/mma8452.txt new file mode 100644 index 0000000..c3bc272 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/accel/mma8452.txt @@ -0,0 +1,22 @@ +Freescale MMA8452Q or MMA8453Q triaxial accelerometer + +Required properties: + + - compatible: should contain one of + * "fsl,mma8452" + * "fsl,mma8453" + - reg: the I2C address of the chip + +Optional properties: + + - interrupt-parent: should be the phandle for the interrupt controller + - interrupts: interrupt mapping for GPIO IRQ + +Example: + + mma8453fc@1d { + compatible = "fsl,mma8453"; + reg = <0x1d>; + interrupt-parent = <&gpio1>; + interrupts = <5 0>; + }; diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig index cd5cd24..fc53c29 100644 --- a/drivers/iio/accel/Kconfig +++ b/drivers/iio/accel/Kconfig @@ -100,13 +100,13 @@ config KXCJK1013 be called kxcjk-1013. config MMA8452 - tristate "Freescale MMA8452Q Accelerometer Driver" + tristate "Freescale MMA8452Q and similar Accelerometers Driver" depends on I2C select IIO_BUFFER select IIO_TRIGGERED_BUFFER help - Say yes here to build support for the Freescale MMA8452Q 3-axis - accelerometer. + Say yes here to build support for the following Freescale 3-axis + accelerometers: MMA8452Q, MMA8453Q. To compile this driver as a module, choose M here: the module will be called mma8452. diff --git a/drivers/iio/accel/mma8452.c b/drivers/iio/accel/mma8452.c index f28428fa..7b2ab17 100644 --- a/drivers/iio/accel/mma8452.c +++ b/drivers/iio/accel/mma8452.c @@ -1,5 +1,8 @@ /* - * mma8452.c - Support for Freescale MMA8452Q 3-axis 12-bit accelerometer + * mma8452.c - Support for following Freescale 3-axis accelerometers: + * + * MMA8452Q (12 bit) + * MMA8453Q (10 bit) * * Copyright 2014 Peter Meerwald * @@ -26,7 +29,7 @@ #define MMA8452_STATUS 0x00 #define MMA8452_STATUS_DRDY (BIT(2) | BIT(1) | BIT(0)) -#define MMA8452_OUT_X 0x01 /* MSB first, 12-bit */ +#define MMA8452_OUT_X 0x01 /* MSB first */ #define MMA8452_OUT_Y 0x03 #define MMA8452_OUT_Z 0x05 #define MMA8452_INT_SRC 0x0c @@ -69,6 +72,7 @@ #define MMA8452_INT_TRANS BIT(5) #define MMA8452_DEVICE_ID 0x2a +#define MMA8453_DEVICE_ID 0x3a struct mma8452_data { struct i2c_client *client; @@ -768,8 +772,16 @@ static const struct iio_chan_spec mma8452_channels[] = { IIO_CHAN_SOFT_TIMESTAMP(3), }; +static const struct iio_chan_spec mma8453_channels[] = { + MMA8452_CHANNEL(X, 0, 10), + MMA8452_CHANNEL(Y, 1, 10), + MMA8452_CHANNEL(Z, 2, 10), + IIO_CHAN_SOFT_TIMESTAMP(3), +}; + enum { mma8452, + mma8453, }; static const struct mma_chip_info mma_chip_info_table[] = { @@ -796,6 +808,22 @@ static const struct mma_chip_info mma_chip_info_table[] = { .ev_ths_mask = MMA8452_TRANSIENT_THS_MASK, .ev_count = MMA8452_TRANSIENT_COUNT, }, + [mma8453] = { + .chip_id = MMA8453_DEVICE_ID, + .channels = mma8453_channels, + .num_channels = ARRAY_SIZE(mma8453_channels), + .mma_scales = { {0, 38307}, {0, 76614}, {0, 153228} }, + .ev_cfg = MMA8452_TRANSIENT_CFG, + .ev_cfg_ele = MMA8452_TRANSIENT_CFG_ELE, + .ev_cfg_chan_shift = 1, + .ev_src = MMA8452_TRANSIENT_SRC, + .ev_src_xe = MMA8452_TRANSIENT_SRC_XTRANSE, + .ev_src_ye = MMA8452_TRANSIENT_SRC_YTRANSE, + .ev_src_ze = MMA8452_TRANSIENT_SRC_ZTRANSE, + .ev_ths = MMA8452_TRANSIENT_THS, + .ev_ths_mask = MMA8452_TRANSIENT_THS_MASK, + .ev_count = MMA8452_TRANSIENT_COUNT, + }, }; static struct attribute *mma8452_attributes[] = { @@ -917,6 +945,7 @@ static int mma8452_reset(struct i2c_client *client) static const struct of_device_id mma8452_dt_ids[] = { { .compatible = "fsl,mma8452", .data = &mma_chip_info_table[mma8452] }, + { .compatible = "fsl,mma8453", .data = &mma_chip_info_table[mma8453] }, { } }; MODULE_DEVICE_TABLE(of, mma8452_dt_ids); @@ -932,7 +961,8 @@ static int mma8452_probe(struct i2c_client *client, ret = i2c_smbus_read_byte_data(client, MMA8452_WHO_AM_I); if (ret < 0) return ret; - if (ret != MMA8452_DEVICE_ID) + + if (ret != MMA8452_DEVICE_ID && ret != MMA8453_DEVICE_ID) return -ENODEV; match = of_match_device(mma8452_dt_ids, &client->dev); @@ -1079,6 +1109,7 @@ static SIMPLE_DEV_PM_OPS(mma8452_pm_ops, mma8452_suspend, mma8452_resume); static const struct i2c_device_id mma8452_id[] = { { "mma8452", mma8452 }, + { "mma8453", mma8453 }, { } }; MODULE_DEVICE_TABLE(i2c, mma8452_id); -- cgit v1.1 From 60f562e74e75716cc121cc7adb2dcb43c17709bf Mon Sep 17 00:00:00 2001 From: Martin Kepplinger Date: Tue, 1 Sep 2015 13:45:10 +0200 Subject: iio: mma8452: add freefall / motion interrupt source This adds the freefall / motion interrupt source definitions to the driver. It is used in this series' next patch, for chips that don't support the transient interrupt source. Signed-off-by: Martin Kepplinger Signed-off-by: Christoph Muellner Signed-off-by: Jonathan Cameron --- drivers/iio/accel/mma8452.c | 44 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/drivers/iio/accel/mma8452.c b/drivers/iio/accel/mma8452.c index 7b2ab17..6b1a862 100644 --- a/drivers/iio/accel/mma8452.c +++ b/drivers/iio/accel/mma8452.c @@ -42,6 +42,16 @@ #define MMA8452_DATA_CFG_HPF_MASK BIT(4) #define MMA8452_HP_FILTER_CUTOFF 0x0f #define MMA8452_HP_FILTER_CUTOFF_SEL_MASK GENMASK(1, 0) +#define MMA8452_FF_MT_CFG 0x15 +#define MMA8452_FF_MT_CFG_OAE BIT(6) +#define MMA8452_FF_MT_CFG_ELE BIT(7) +#define MMA8452_FF_MT_SRC 0x16 +#define MMA8452_FF_MT_SRC_XHE BIT(1) +#define MMA8452_FF_MT_SRC_YHE BIT(3) +#define MMA8452_FF_MT_SRC_ZHE BIT(5) +#define MMA8452_FF_MT_THS 0x17 +#define MMA8452_FF_MT_THS_MASK 0x7f +#define MMA8452_FF_MT_COUNT 0x18 #define MMA8452_TRANSIENT_CFG 0x1d #define MMA8452_TRANSIENT_CFG_HPF_BYP BIT(0) #define MMA8452_TRANSIENT_CFG_CHAN(chan) BIT(chan + 1) @@ -69,6 +79,7 @@ #define MMA8452_MAX_REG 0x31 #define MMA8452_INT_DRDY BIT(0) +#define MMA8452_INT_FF_MT BIT(2) #define MMA8452_INT_TRANS BIT(5) #define MMA8452_DEVICE_ID 0x2a @@ -613,7 +624,8 @@ static int mma8452_write_event_config(struct iio_dev *indio_dev, else val &= ~BIT(chan->scan_index + chip->ev_cfg_chan_shift); - val |= MMA8452_TRANSIENT_CFG_ELE; + val |= chip->ev_cfg_ele; + val |= MMA8452_FF_MT_CFG_OAE; return mma8452_change_config(data, chip->ev_cfg, val); } @@ -654,6 +666,7 @@ static irqreturn_t mma8452_interrupt(int irq, void *p) { struct iio_dev *indio_dev = p; struct mma8452_data *data = iio_priv(indio_dev); + const struct mma_chip_info *chip = data->chip_info; int ret = IRQ_NONE; int src; @@ -666,7 +679,10 @@ static irqreturn_t mma8452_interrupt(int irq, void *p) ret = IRQ_HANDLED; } - if (src & MMA8452_INT_TRANS) { + if ((src & MMA8452_INT_TRANS && + chip->ev_src == MMA8452_TRANSIENT_SRC) || + (src & MMA8452_INT_FF_MT && + chip->ev_src == MMA8452_FF_MT_SRC)) { mma8452_transient_interrupt(indio_dev); ret = IRQ_HANDLED; } @@ -728,6 +744,16 @@ static const struct iio_event_spec mma8452_transient_event[] = { }, }; +static const struct iio_event_spec mma8452_motion_event[] = { + { + .type = IIO_EV_TYPE_MAG, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_ENABLE), + .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_PERIOD) + }, +}; + /* * Threshold is configured in fixed 8G/127 steps regardless of * currently selected scale for measurement. @@ -1013,13 +1039,15 @@ static int mma8452_probe(struct i2c_client *client, if (client->irq) { /* - * Although we enable the transient interrupt source once and - * for all here the transient event detection itself is not - * enabled until userspace asks for it by - * mma8452_write_event_config() + * Although we enable the interrupt sources once and for + * all here the event detection itself is not enabled until + * userspace asks for it by mma8452_write_event_config() */ - int supported_interrupts = MMA8452_INT_DRDY | MMA8452_INT_TRANS; - int enabled_interrupts = MMA8452_INT_TRANS; + int supported_interrupts = MMA8452_INT_DRDY | + MMA8452_INT_TRANS | + MMA8452_INT_FF_MT; + int enabled_interrupts = MMA8452_INT_TRANS | + MMA8452_INT_FF_MT; /* Assume wired to INT1 pin */ ret = i2c_smbus_write_byte_data(client, -- cgit v1.1 From 417e008ba9db7ce4b4e48131c6f69829e9886b3e Mon Sep 17 00:00:00 2001 From: Martin Kepplinger Date: Tue, 1 Sep 2015 13:45:11 +0200 Subject: iio: mma8452: add support for MMA8652FC and MMA8653FC MMA8652FC and MMA8653FC don't provide the transient interrupt source, so the motion interrupt source is used by providing a new iio_chan_spec definition, so that other supported devices are not affected by this. Datasheets for the newly supported devices are available at Freescale's website: http://cache.freescale.com/files/sensors/doc/data_sheet/MMA8652FC.pdf http://cache.freescale.com/files/sensors/doc/data_sheet/MMA8653FC.pdf Signed-off-by: Martin Kepplinger Signed-off-by: Christoph Muellner Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/accel/mma8452.txt | 4 +- drivers/iio/accel/Kconfig | 2 +- drivers/iio/accel/mma8452.c | 98 ++++++++++++++++++++-- 3 files changed, 95 insertions(+), 9 deletions(-) diff --git a/Documentation/devicetree/bindings/iio/accel/mma8452.txt b/Documentation/devicetree/bindings/iio/accel/mma8452.txt index c3bc272..e3c3746 100644 --- a/Documentation/devicetree/bindings/iio/accel/mma8452.txt +++ b/Documentation/devicetree/bindings/iio/accel/mma8452.txt @@ -1,10 +1,12 @@ -Freescale MMA8452Q or MMA8453Q triaxial accelerometer +Freescale MMA8452Q, MMA8453Q, MMA8652FC or MMA8653FC triaxial accelerometer Required properties: - compatible: should contain one of * "fsl,mma8452" * "fsl,mma8453" + * "fsl,mma8652" + * "fsl,mma8653" - reg: the I2C address of the chip Optional properties: diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig index fc53c29..8172ae5 100644 --- a/drivers/iio/accel/Kconfig +++ b/drivers/iio/accel/Kconfig @@ -106,7 +106,7 @@ config MMA8452 select IIO_TRIGGERED_BUFFER help Say yes here to build support for the following Freescale 3-axis - accelerometers: MMA8452Q, MMA8453Q. + accelerometers: MMA8452Q, MMA8453Q, MMA8652FC, MMA8653FC. To compile this driver as a module, choose M here: the module will be called mma8452. diff --git a/drivers/iio/accel/mma8452.c b/drivers/iio/accel/mma8452.c index 6b1a862..59b4455 100644 --- a/drivers/iio/accel/mma8452.c +++ b/drivers/iio/accel/mma8452.c @@ -3,6 +3,8 @@ * * MMA8452Q (12 bit) * MMA8453Q (10 bit) + * MMA8652FC (12 bit) + * MMA8653FC (10 bit) * * Copyright 2014 Peter Meerwald * @@ -84,6 +86,8 @@ #define MMA8452_DEVICE_ID 0x2a #define MMA8453_DEVICE_ID 0x3a +#define MMA8652_DEVICE_ID 0x4a +#define MMA8653_DEVICE_ID 0x5a struct mma8452_data { struct i2c_client *client; @@ -791,6 +795,26 @@ static struct attribute_group mma8452_event_attribute_group = { .num_event_specs = ARRAY_SIZE(mma8452_transient_event), \ } +#define MMA8652_CHANNEL(axis, idx, bits) { \ + .type = IIO_ACCEL, \ + .modified = 1, \ + .channel2 = IIO_MOD_##axis, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_CALIBBIAS), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .scan_index = idx, \ + .scan_type = { \ + .sign = 's', \ + .realbits = (bits), \ + .storagebits = 16, \ + .shift = 16 - (bits), \ + .endianness = IIO_BE, \ + }, \ + .event_spec = mma8452_motion_event, \ + .num_event_specs = ARRAY_SIZE(mma8452_motion_event), \ +} + static const struct iio_chan_spec mma8452_channels[] = { MMA8452_CHANNEL(X, 0, 12), MMA8452_CHANNEL(Y, 1, 12), @@ -805,9 +829,25 @@ static const struct iio_chan_spec mma8453_channels[] = { IIO_CHAN_SOFT_TIMESTAMP(3), }; +static const struct iio_chan_spec mma8652_channels[] = { + MMA8652_CHANNEL(X, 0, 12), + MMA8652_CHANNEL(Y, 1, 12), + MMA8652_CHANNEL(Z, 2, 12), + IIO_CHAN_SOFT_TIMESTAMP(3), +}; + +static const struct iio_chan_spec mma8653_channels[] = { + MMA8652_CHANNEL(X, 0, 10), + MMA8652_CHANNEL(Y, 1, 10), + MMA8652_CHANNEL(Z, 2, 10), + IIO_CHAN_SOFT_TIMESTAMP(3), +}; + enum { mma8452, mma8453, + mma8652, + mma8653, }; static const struct mma_chip_info mma_chip_info_table[] = { @@ -850,6 +890,38 @@ static const struct mma_chip_info mma_chip_info_table[] = { .ev_ths_mask = MMA8452_TRANSIENT_THS_MASK, .ev_count = MMA8452_TRANSIENT_COUNT, }, + [mma8652] = { + .chip_id = MMA8652_DEVICE_ID, + .channels = mma8652_channels, + .num_channels = ARRAY_SIZE(mma8652_channels), + .mma_scales = { {0, 9577}, {0, 19154}, {0, 38307} }, + .ev_cfg = MMA8452_FF_MT_CFG, + .ev_cfg_ele = MMA8452_FF_MT_CFG_ELE, + .ev_cfg_chan_shift = 3, + .ev_src = MMA8452_FF_MT_SRC, + .ev_src_xe = MMA8452_FF_MT_SRC_XHE, + .ev_src_ye = MMA8452_FF_MT_SRC_YHE, + .ev_src_ze = MMA8452_FF_MT_SRC_ZHE, + .ev_ths = MMA8452_FF_MT_THS, + .ev_ths_mask = MMA8452_FF_MT_THS_MASK, + .ev_count = MMA8452_FF_MT_COUNT, + }, + [mma8653] = { + .chip_id = MMA8653_DEVICE_ID, + .channels = mma8653_channels, + .num_channels = ARRAY_SIZE(mma8653_channels), + .mma_scales = { {0, 38307}, {0, 76614}, {0, 153228} }, + .ev_cfg = MMA8452_FF_MT_CFG, + .ev_cfg_ele = MMA8452_FF_MT_CFG_ELE, + .ev_cfg_chan_shift = 3, + .ev_src = MMA8452_FF_MT_SRC, + .ev_src_xe = MMA8452_FF_MT_SRC_XHE, + .ev_src_ye = MMA8452_FF_MT_SRC_YHE, + .ev_src_ze = MMA8452_FF_MT_SRC_ZHE, + .ev_ths = MMA8452_FF_MT_THS, + .ev_ths_mask = MMA8452_FF_MT_THS_MASK, + .ev_count = MMA8452_FF_MT_COUNT, + }, }; static struct attribute *mma8452_attributes[] = { @@ -972,6 +1044,8 @@ static int mma8452_reset(struct i2c_client *client) static const struct of_device_id mma8452_dt_ids[] = { { .compatible = "fsl,mma8452", .data = &mma_chip_info_table[mma8452] }, { .compatible = "fsl,mma8453", .data = &mma_chip_info_table[mma8453] }, + { .compatible = "fsl,mma8652", .data = &mma_chip_info_table[mma8652] }, + { .compatible = "fsl,mma8653", .data = &mma_chip_info_table[mma8653] }, { } }; MODULE_DEVICE_TABLE(of, mma8452_dt_ids); @@ -984,13 +1058,6 @@ static int mma8452_probe(struct i2c_client *client, int ret; const struct of_device_id *match; - ret = i2c_smbus_read_byte_data(client, MMA8452_WHO_AM_I); - if (ret < 0) - return ret; - - if (ret != MMA8452_DEVICE_ID && ret != MMA8453_DEVICE_ID) - return -ENODEV; - match = of_match_device(mma8452_dt_ids, &client->dev); if (!match) { dev_err(&client->dev, "unknown device model\n"); @@ -1006,6 +1073,21 @@ static int mma8452_probe(struct i2c_client *client, mutex_init(&data->lock); data->chip_info = match->data; + ret = i2c_smbus_read_byte_data(client, MMA8452_WHO_AM_I); + if (ret < 0) + return ret; + + switch (ret) { + case MMA8452_DEVICE_ID: + case MMA8453_DEVICE_ID: + case MMA8652_DEVICE_ID: + case MMA8653_DEVICE_ID: + if (ret == data->chip_info->chip_id) + break; + default: + return -ENODEV; + } + dev_info(&client->dev, "registering %s accelerometer; ID 0x%x\n", match->compatible, data->chip_info->chip_id); @@ -1138,6 +1220,8 @@ static SIMPLE_DEV_PM_OPS(mma8452_pm_ops, mma8452_suspend, mma8452_resume); static const struct i2c_device_id mma8452_id[] = { { "mma8452", mma8452 }, { "mma8453", mma8453 }, + { "mma8652", mma8652 }, + { "mma8653", mma8653 }, { } }; MODULE_DEVICE_TABLE(i2c, mma8452_id); -- cgit v1.1 From d6223c3737bd9680f1fb6c058bdfca41ee24e2da Mon Sep 17 00:00:00 2001 From: Martin Kepplinger Date: Tue, 1 Sep 2015 13:45:12 +0200 Subject: iio: mma8452: add copyright notice comment Signed-off-by: Martin Kepplinger Signed-off-by: Christoph Muellner Signed-off-by: Jonathan Cameron --- drivers/iio/accel/mma8452.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iio/accel/mma8452.c b/drivers/iio/accel/mma8452.c index 59b4455..15d50c9 100644 --- a/drivers/iio/accel/mma8452.c +++ b/drivers/iio/accel/mma8452.c @@ -6,6 +6,7 @@ * MMA8652FC (12 bit) * MMA8653FC (10 bit) * + * Copyright 2015 Martin Kepplinger * Copyright 2014 Peter Meerwald * * This file is subject to the terms and conditions of version 2 of -- cgit v1.1 From b2a768949c4906d87fd0c65b39752e599b5a1860 Mon Sep 17 00:00:00 2001 From: Martin Kepplinger Date: Tue, 1 Sep 2015 13:45:13 +0200 Subject: iio: mma8452: leave sysfs namings to the iio core This doesn't actually change anything since the core names the sysfs folder for the iio event attributes "events" anyways. It only leaves the job to the core. Signed-off-by: Martin Kepplinger Signed-off-by: Christoph Muellner Signed-off-by: Jonathan Cameron --- drivers/iio/accel/mma8452.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/iio/accel/mma8452.c b/drivers/iio/accel/mma8452.c index 15d50c9..1eccc2d 100644 --- a/drivers/iio/accel/mma8452.c +++ b/drivers/iio/accel/mma8452.c @@ -772,7 +772,6 @@ static struct attribute *mma8452_event_attributes[] = { static struct attribute_group mma8452_event_attribute_group = { .attrs = mma8452_event_attributes, - .name = "events", }; #define MMA8452_CHANNEL(axis, idx, bits) { \ -- cgit v1.1 From e09f56f3f09638a0f0120be81352378ed1727431 Mon Sep 17 00:00:00 2001 From: Luis de Bethencourt Date: Thu, 17 Sep 2015 19:02:32 +0200 Subject: iio: dac: max5821: Fix module autoload for OF platform driver This platform driver has a OF device ID table but the OF module alias information is not created so module autoloading won't work. Signed-off-by: Luis de Bethencourt Signed-off-by: Jonathan Cameron --- drivers/iio/dac/max5821.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iio/dac/max5821.c b/drivers/iio/dac/max5821.c index 28b8748..86e9e11 100644 --- a/drivers/iio/dac/max5821.c +++ b/drivers/iio/dac/max5821.c @@ -387,6 +387,7 @@ static const struct of_device_id max5821_of_match[] = { { .compatible = "maxim,max5821" }, { } }; +MODULE_DEVICE_TABLE(of, max5821_of_match); static struct i2c_driver max5821_driver = { .driver = { -- cgit v1.1 From 0df5a5488b6d157eabfdc0747c87c08bd7431a56 Mon Sep 17 00:00:00 2001 From: Luis de Bethencourt Date: Thu, 17 Sep 2015 19:02:00 +0200 Subject: iio: adc: twl6030-gpadc: Fix module autoload for OF platform driver This platform driver has a OF device ID table but the OF module alias information is not created so module autoloading won't work. Signed-off-by: Luis de Bethencourt Signed-off-by: Jonathan Cameron --- drivers/iio/adc/twl6030-gpadc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iio/adc/twl6030-gpadc.c b/drivers/iio/adc/twl6030-gpadc.c index df12c57..becbb0a 100644 --- a/drivers/iio/adc/twl6030-gpadc.c +++ b/drivers/iio/adc/twl6030-gpadc.c @@ -875,6 +875,7 @@ static const struct of_device_id of_twl6030_match_tbl[] = { }, { /* end */ } }; +MODULE_DEVICE_TABLE(of, of_twl6030_match_tbl); static int twl6030_gpadc_probe(struct platform_device *pdev) { -- cgit v1.1 From 8ff6b3bc9493089247e012a9fcba7198e194b4a5 Mon Sep 17 00:00:00 2001 From: Matt Ranostay Date: Sun, 13 Sep 2015 20:26:11 -0700 Subject: iio: chemical: Add IIO_CONCENTRATION channel type There are air quality sensors that report data back in parts per million of VOC (Volatile Organic Compounds) which are usually indexed from CO2 or another common pollutant. This patchset adds an IIO_CONCENTRATION type that returns a percentage of substance because no other channels types fit this use case. Modifiers for IIO_MOD_CO2 and IIO_MOD_VOC gas types are defined. Signed-off-by: Matt Ranostay Signed-off-by: Jonathan Cameron --- Documentation/ABI/testing/sysfs-bus-iio | 11 +++++++++++ drivers/iio/industrialio-core.c | 3 +++ include/uapi/linux/iio/types.h | 3 +++ 3 files changed, 17 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio index 20312a0..08903b9 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio +++ b/Documentation/ABI/testing/sysfs-bus-iio @@ -1460,3 +1460,14 @@ Description: measurements and return the average value as output data. Each value resulted from [_name]_oversampling_ratio measurements is considered as one sample for [_name]_sampling_frequency. + +What: /sys/bus/iio/devices/iio:deviceX/in_concentration_raw +What: /sys/bus/iio/devices/iio:deviceX/in_concentrationX_raw +What: /sys/bus/iio/devices/iio:deviceX/in_concentration_co2_raw +What: /sys/bus/iio/devices/iio:deviceX/in_concentrationX_co2_raw +What: /sys/bus/iio/devices/iio:deviceX/in_concentration_voc_raw +What: /sys/bus/iio/devices/iio:deviceX/in_concentrationX_voc_raw +KernelVersion: 4.3 +Contact: linux-iio@vger.kernel.org +Description: + Raw (unscaled no offset etc.) percentage reading of a substance. diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index bef690e..ee09a94 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -75,6 +75,7 @@ static const char * const iio_chan_type_name_spec[] = { [IIO_ENERGY] = "energy", [IIO_DISTANCE] = "distance", [IIO_VELOCITY] = "velocity", + [IIO_CONCENTRATION] = "concentration", }; static const char * const iio_modifier_names[] = { @@ -111,6 +112,8 @@ static const char * const iio_modifier_names[] = { [IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z] = "sqrt(x^2+y^2+z^2)", [IIO_MOD_I] = "i", [IIO_MOD_Q] = "q", + [IIO_MOD_CO2] = "co2", + [IIO_MOD_VOC] = "voc", }; /* relies on pairs of these shared then separate */ diff --git a/include/uapi/linux/iio/types.h b/include/uapi/linux/iio/types.h index 2f8b117..1e4c4e3 100644 --- a/include/uapi/linux/iio/types.h +++ b/include/uapi/linux/iio/types.h @@ -35,6 +35,7 @@ enum iio_chan_type { IIO_ENERGY, IIO_DISTANCE, IIO_VELOCITY, + IIO_CONCENTRATION, }; enum iio_modifier { @@ -72,6 +73,8 @@ enum iio_modifier { IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z, IIO_MOD_I, IIO_MOD_Q, + IIO_MOD_CO2, + IIO_MOD_VOC, }; enum iio_event_type { -- cgit v1.1 From d38d54692d454e4dded125677e39f53514dc4277 Mon Sep 17 00:00:00 2001 From: Matt Ranostay Date: Sun, 13 Sep 2015 20:26:12 -0700 Subject: iio: resistance: add IIO_RESISTANCE channel type Signed-off-by: Matt Ranostay Signed-off-by: Jonathan Cameron --- Documentation/ABI/testing/sysfs-bus-iio | 8 ++++++++ drivers/iio/industrialio-core.c | 1 + include/uapi/linux/iio/types.h | 1 + 3 files changed, 10 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio index 08903b9..8ce14c8 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio +++ b/Documentation/ABI/testing/sysfs-bus-iio @@ -1471,3 +1471,11 @@ KernelVersion: 4.3 Contact: linux-iio@vger.kernel.org Description: Raw (unscaled no offset etc.) percentage reading of a substance. + +What: /sys/bus/iio/devices/iio:deviceX/in_resistance_raw +What: /sys/bus/iio/devices/iio:deviceX/in_resistanceX_raw +KernelVersion: 4.3 +Contact: linux-iio@vger.kernel.org +Description: + Raw (unscaled no offset etc.) resistance reading that can be processed + into an ohm value. diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index ee09a94..208358f 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -76,6 +76,7 @@ static const char * const iio_chan_type_name_spec[] = { [IIO_DISTANCE] = "distance", [IIO_VELOCITY] = "velocity", [IIO_CONCENTRATION] = "concentration", + [IIO_RESISTANCE] = "resistance", }; static const char * const iio_modifier_names[] = { diff --git a/include/uapi/linux/iio/types.h b/include/uapi/linux/iio/types.h index 1e4c4e3..7c63bd6 100644 --- a/include/uapi/linux/iio/types.h +++ b/include/uapi/linux/iio/types.h @@ -36,6 +36,7 @@ enum iio_chan_type { IIO_DISTANCE, IIO_VELOCITY, IIO_CONCENTRATION, + IIO_RESISTANCE, }; enum iio_modifier { -- cgit v1.1 From 9bff3131cfb30ef761adfad08c4a0a1b7faf1e20 Mon Sep 17 00:00:00 2001 From: Matt Ranostay Date: Sun, 13 Sep 2015 20:26:13 -0700 Subject: devicetree: add SGX Sensortech vendor id Signed-off-by: Matt Ranostay Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/vendor-prefixes.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index 341695b..d591561 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -183,6 +183,7 @@ sbs Smart Battery System schindler Schindler seagate Seagate Technology PLC semtech Semtech Corporation +sgx SGX Sensortech sil Silicon Image silabs Silicon Laboratories siliconmitus Silicon Mitus, Inc. -- cgit v1.1 From cd8d97774f23c601d0ed66b5035bcc77dd665b10 Mon Sep 17 00:00:00 2001 From: Matt Ranostay Date: Sun, 13 Sep 2015 20:26:14 -0700 Subject: iio: chemical: add SGX VZ89x VOC sensor support Add support for VZ89X sensors VOC and CO2 reporting channels in percentage which can be converted to part per million. Signed-off-by: Matt Ranostay Signed-off-by: Jonathan Cameron --- .../ABI/testing/sysfs-bus-iio-chemical-vz89x | 7 + .../devicetree/bindings/i2c/trivial-devices.txt | 1 + drivers/iio/Kconfig | 1 + drivers/iio/Makefile | 1 + drivers/iio/chemical/Kconfig | 15 ++ drivers/iio/chemical/Makefile | 6 + drivers/iio/chemical/vz89x.c | 237 +++++++++++++++++++++ 7 files changed, 268 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-chemical-vz89x create mode 100644 drivers/iio/chemical/Kconfig create mode 100644 drivers/iio/chemical/Makefile create mode 100644 drivers/iio/chemical/vz89x.c diff --git a/Documentation/ABI/testing/sysfs-bus-iio-chemical-vz89x b/Documentation/ABI/testing/sysfs-bus-iio-chemical-vz89x new file mode 100644 index 0000000..c0c1ea9 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-iio-chemical-vz89x @@ -0,0 +1,7 @@ +What: /sys/bus/iio/devices/iio:deviceX/in_concentration_VOC_short_raw +Date: September 2015 +KernelVersion: 4.3 +Contact: Matt Ranostay +Description: + Get the raw calibration VOC value from the sensor. + This value has little application outside of calibration. diff --git a/Documentation/devicetree/bindings/i2c/trivial-devices.txt b/Documentation/devicetree/bindings/i2c/trivial-devices.txt index e1c07dd..ed7ef46 100644 --- a/Documentation/devicetree/bindings/i2c/trivial-devices.txt +++ b/Documentation/devicetree/bindings/i2c/trivial-devices.txt @@ -88,6 +88,7 @@ ricoh,rs5c372b I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC ricoh,rv5c386 I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC ricoh,rv5c387a I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC samsung,24ad0xd1 S524AD0XF1 (128K/256K-bit Serial EEPROM for Low Power) +sgx,vz89x SGX Sensortech VZ89X Sensors sii,s35390a 2-wire CMOS real-time clock skyworks,sky81452 Skyworks SKY81452: Six-Channel White LED Driver with Touch Panel Bias Supply st-micro,24c256 i2c serial eeprom (24cxx) diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig index 6fe0d65..119c94d 100644 --- a/drivers/iio/Kconfig +++ b/drivers/iio/Kconfig @@ -47,6 +47,7 @@ config IIO_TRIGGERED_EVENT source "drivers/iio/accel/Kconfig" source "drivers/iio/adc/Kconfig" source "drivers/iio/amplifiers/Kconfig" +source "drivers/iio/chemical/Kconfig" source "drivers/iio/common/Kconfig" source "drivers/iio/dac/Kconfig" source "drivers/iio/frequency/Kconfig" diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile index 40995f3..e210055 100644 --- a/drivers/iio/Makefile +++ b/drivers/iio/Makefile @@ -13,6 +13,7 @@ obj-y += accel/ obj-y += adc/ obj-y += amplifiers/ obj-y += buffer/ +obj-y += chemical/ obj-y += common/ obj-y += dac/ obj-y += gyro/ diff --git a/drivers/iio/chemical/Kconfig b/drivers/iio/chemical/Kconfig new file mode 100644 index 0000000..3061b72 --- /dev/null +++ b/drivers/iio/chemical/Kconfig @@ -0,0 +1,15 @@ +# +# Chemical sensors +# + +menu "Chemical Sensors" + +config VZ89X + tristate "SGX Sensortech MiCS VZ89X VOC sensor" + depends on I2C + help + Say Y here to build I2C interface support for the SGX + Sensortech MiCS VZ89X VOC (Volatile Organic Compounds) + sensors + +endmenu diff --git a/drivers/iio/chemical/Makefile b/drivers/iio/chemical/Makefile new file mode 100644 index 0000000..7292f2d --- /dev/null +++ b/drivers/iio/chemical/Makefile @@ -0,0 +1,6 @@ +# +# Makefile for IIO chemical sensors +# + +# When adding new entries keep the list in alphabetical order +obj-$(CONFIG_VZ89X) += vz89x.o diff --git a/drivers/iio/chemical/vz89x.c b/drivers/iio/chemical/vz89x.c new file mode 100644 index 0000000..b454200 --- /dev/null +++ b/drivers/iio/chemical/vz89x.c @@ -0,0 +1,237 @@ +/* + * vz89x.c - Support for SGX Sensortech MiCS VZ89X VOC sensors + * + * Copyright (C) 2015 Matt Ranostay + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include + +#include +#include + +#define VZ89X_REG_MEASUREMENT 0x09 +#define VZ89X_REG_MEASUREMENT_SIZE 6 + +#define VZ89X_VOC_CO2_IDX 0 +#define VZ89X_VOC_SHORT_IDX 1 +#define VZ89X_VOC_TVOC_IDX 2 +#define VZ89X_VOC_RESISTANCE_IDX 3 + +struct vz89x_data { + struct i2c_client *client; + struct mutex lock; + unsigned long last_update; + + u8 buffer[VZ89X_REG_MEASUREMENT_SIZE]; +}; + +static const struct iio_chan_spec vz89x_channels[] = { + { + .type = IIO_CONCENTRATION, + .channel2 = IIO_MOD_CO2, + .modified = 1, + .info_mask_separate = + BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_RAW), + .address = VZ89X_VOC_CO2_IDX, + }, + { + .type = IIO_CONCENTRATION, + .channel2 = IIO_MOD_VOC, + .modified = 1, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .address = VZ89X_VOC_SHORT_IDX, + .extend_name = "short", + }, + { + .type = IIO_CONCENTRATION, + .channel2 = IIO_MOD_VOC, + .modified = 1, + .info_mask_separate = + BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_RAW), + .address = VZ89X_VOC_TVOC_IDX, + }, + { + .type = IIO_RESISTANCE, + .info_mask_separate = + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + .address = VZ89X_VOC_RESISTANCE_IDX, + }, +}; + +static IIO_CONST_ATTR(in_concentration_co2_scale, "0.00000698689"); +static IIO_CONST_ATTR(in_concentration_voc_scale, "0.00000000436681223"); + +static struct attribute *vz89x_attributes[] = { + &iio_const_attr_in_concentration_co2_scale.dev_attr.attr, + &iio_const_attr_in_concentration_voc_scale.dev_attr.attr, + NULL, +}; + +static const struct attribute_group vz89x_attrs_group = { + .attrs = vz89x_attributes, +}; + +static int vz89x_get_measurement(struct vz89x_data *data) +{ + int ret; + int i; + + /* sensor can only be polled once a second max per datasheet */ + if (!time_after(jiffies, data->last_update + HZ)) + return 0; + + ret = i2c_smbus_write_word_data(data->client, + VZ89X_REG_MEASUREMENT, 0); + if (ret < 0) + return ret; + + for (i = 0; i < VZ89X_REG_MEASUREMENT_SIZE; i++) { + ret = i2c_smbus_read_byte(data->client); + if (ret < 0) + return ret; + data->buffer[i] = ret; + } + + data->last_update = jiffies; + + return 0; +} + +static int vz89x_get_resistance_reading(struct vz89x_data *data) +{ + u8 *buf = &data->buffer[VZ89X_VOC_TVOC_IDX]; + + return buf[0] | (buf[1] << 8) | (buf[2] << 16); +} + +static int vz89x_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + struct vz89x_data *data = iio_priv(indio_dev); + int ret = -EINVAL; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&data->lock); + ret = vz89x_get_measurement(data); + mutex_unlock(&data->lock); + + if (ret) + return ret; + + switch (chan->address) { + case VZ89X_VOC_CO2_IDX: + case VZ89X_VOC_SHORT_IDX: + case VZ89X_VOC_TVOC_IDX: + *val = data->buffer[chan->address]; + return IIO_VAL_INT; + case VZ89X_VOC_RESISTANCE_IDX: + *val = vz89x_get_resistance_reading(data); + return IIO_VAL_INT; + default: + return -EINVAL; + } + break; + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_RESISTANCE: + *val = 10; + return IIO_VAL_INT; + default: + return -EINVAL; + } + break; + case IIO_CHAN_INFO_OFFSET: + switch (chan->address) { + case VZ89X_VOC_CO2_IDX: + *val = 44; + *val2 = 250000; + return IIO_VAL_INT_PLUS_MICRO; + case VZ89X_VOC_TVOC_IDX: + *val = -13; + return IIO_VAL_INT; + default: + return -EINVAL; + } + } + + return ret; +} + +static const struct iio_info vz89x_info = { + .attrs = &vz89x_attrs_group, + .read_raw = vz89x_read_raw, + .driver_module = THIS_MODULE, +}; + +static int vz89x_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct iio_dev *indio_dev; + struct vz89x_data *data; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_BYTE)) + return -ENODEV; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + i2c_set_clientdata(client, indio_dev); + data->client = client; + data->last_update = jiffies - HZ; + mutex_init(&data->lock); + + indio_dev->dev.parent = &client->dev; + indio_dev->info = &vz89x_info, + indio_dev->name = dev_name(&client->dev); + indio_dev->modes = INDIO_DIRECT_MODE; + + indio_dev->channels = vz89x_channels; + indio_dev->num_channels = ARRAY_SIZE(vz89x_channels); + + return devm_iio_device_register(&client->dev, indio_dev); +} + +static const struct i2c_device_id vz89x_id[] = { + { "vz89x", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, vz89x_id); + +static const struct of_device_id vz89x_dt_ids[] = { + { .compatible = "sgx,vz89x" }, + { } +}; +MODULE_DEVICE_TABLE(of, vz89x_dt_ids); + +static struct i2c_driver vz89x_driver = { + .driver = { + .name = "vz89x", + .of_match_table = of_match_ptr(vz89x_dt_ids), + }, + .probe = vz89x_probe, + .id_table = vz89x_id, +}; +module_i2c_driver(vz89x_driver); + +MODULE_AUTHOR("Matt Ranostay "); +MODULE_DESCRIPTION("SGX Sensortech MiCS VZ89X VOC sensors"); +MODULE_LICENSE("GPL v2"); -- cgit v1.1 From fd2bb310ca3d3621a0f201e018e1292dca95df6e Mon Sep 17 00:00:00 2001 From: Cristina Opriceana Date: Fri, 11 Sep 2015 16:59:30 +0300 Subject: Staging: iio: Move evgen interrupt generation to irq_work Enhance interrupt generation in the dummy driver and expand its usage by introducing the irq_work infrastructure to trigger an interrupt. This way, the irq_work_queue() wrapper permits calling both of the top half and threaded part from a hard irq context, unlike handle_nested_irq(), which only calls the threaded part. As an outcome, the driver succeeds in simulating real hardware interrupts, while keeping the normal interrupt flow. Signed-off-by: Cristina Opriceana Acked-by: Daniel Baluta Signed-off-by: Jonathan Cameron --- drivers/staging/iio/iio_dummy_evgen.c | 26 +++++++++++++++++++++++++- drivers/staging/iio/iio_simple_dummy.h | 1 + drivers/staging/iio/iio_simple_dummy_events.c | 19 ++++++++++++++----- 3 files changed, 40 insertions(+), 6 deletions(-) diff --git a/drivers/staging/iio/iio_dummy_evgen.c b/drivers/staging/iio/iio_dummy_evgen.c index 6d38854..86d8447 100644 --- a/drivers/staging/iio/iio_dummy_evgen.c +++ b/drivers/staging/iio/iio_dummy_evgen.c @@ -24,9 +24,21 @@ #include "iio_dummy_evgen.h" #include #include +#include /* Fiddly bit of faking and irq without hardware */ #define IIO_EVENTGEN_NO 10 + +/** + * struct iio_dummy_handle_irq - helper struct to simulate interrupt generation + * @work: irq_work used to run handlers from hardirq context + * @irq: fake irq line number to trigger an interrupt + */ +struct iio_dummy_handle_irq { + struct irq_work work; + int irq; +}; + /** * struct iio_dummy_evgen - evgen state * @chip: irq chip we are faking @@ -35,6 +47,7 @@ * @inuse: mask of which irqs are connected * @regs: irq regs we are faking * @lock: protect the evgen state + * @handler: helper for a 'hardware-like' interrupt simulation */ struct iio_dummy_eventgen { struct irq_chip chip; @@ -43,6 +56,7 @@ struct iio_dummy_eventgen { bool inuse[IIO_EVENTGEN_NO]; struct iio_dummy_regs regs[IIO_EVENTGEN_NO]; struct mutex lock; + struct iio_dummy_handle_irq handler; }; /* We can only ever have one instance of this 'device' */ @@ -67,6 +81,14 @@ static void iio_dummy_event_irqunmask(struct irq_data *d) evgen->enabled[d->irq - evgen->base] = true; } +static void iio_dummy_work_handler(struct irq_work *work) +{ + struct iio_dummy_handle_irq *irq_handler; + + irq_handler = container_of(work, struct iio_dummy_handle_irq, work); + handle_simple_irq(irq_handler->irq, irq_to_desc(irq_handler->irq)); +} + static int iio_dummy_evgen_create(void) { int ret, i; @@ -91,6 +113,7 @@ static int iio_dummy_evgen_create(void) IRQ_NOREQUEST | IRQ_NOAUTOEN, IRQ_NOPROBE); } + init_irq_work(&iio_evgen->handler.work, iio_dummy_work_handler); mutex_init(&iio_evgen->lock); return 0; } @@ -169,8 +192,9 @@ static ssize_t iio_evgen_poke(struct device *dev, iio_evgen->regs[this_attr->address].reg_id = this_attr->address; iio_evgen->regs[this_attr->address].reg_data = event; + iio_evgen->handler.irq = iio_evgen->base + this_attr->address; if (iio_evgen->enabled[this_attr->address]) - handle_nested_irq(iio_evgen->base + this_attr->address); + irq_work_queue(&iio_evgen->handler.work); return len; } diff --git a/drivers/staging/iio/iio_simple_dummy.h b/drivers/staging/iio/iio_simple_dummy.h index 8d00224..5c2f4d0 100644 --- a/drivers/staging/iio/iio_simple_dummy.h +++ b/drivers/staging/iio/iio_simple_dummy.h @@ -46,6 +46,7 @@ struct iio_dummy_state { int event_irq; int event_val; bool event_en; + s64 event_timestamp; #endif /* CONFIG_IIO_SIMPLE_DUMMY_EVENTS */ }; diff --git a/drivers/staging/iio/iio_simple_dummy_events.c b/drivers/staging/iio/iio_simple_dummy_events.c index 73108ba..bfbf1c5 100644 --- a/drivers/staging/iio/iio_simple_dummy_events.c +++ b/drivers/staging/iio/iio_simple_dummy_events.c @@ -153,6 +153,15 @@ int iio_simple_dummy_write_event_value(struct iio_dev *indio_dev, return 0; } +static irqreturn_t iio_simple_dummy_get_timestamp(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + struct iio_dummy_state *st = iio_priv(indio_dev); + + st->event_timestamp = iio_get_time_ns(); + return IRQ_HANDLED; +} + /** * iio_simple_dummy_event_handler() - identify and pass on event * @irq: irq of event line @@ -177,7 +186,7 @@ static irqreturn_t iio_simple_dummy_event_handler(int irq, void *private) IIO_EVENT_CODE(IIO_VOLTAGE, 0, 0, IIO_EV_DIR_RISING, IIO_EV_TYPE_THRESH, 0, 0, 0), - iio_get_time_ns()); + st->event_timestamp); break; case 1: if (st->activity_running > st->event_val) @@ -187,7 +196,7 @@ static irqreturn_t iio_simple_dummy_event_handler(int irq, void *private) IIO_EV_DIR_RISING, IIO_EV_TYPE_THRESH, 0, 0, 0), - iio_get_time_ns()); + st->event_timestamp); break; case 2: if (st->activity_walking < st->event_val) @@ -197,14 +206,14 @@ static irqreturn_t iio_simple_dummy_event_handler(int irq, void *private) IIO_EV_DIR_FALLING, IIO_EV_TYPE_THRESH, 0, 0, 0), - iio_get_time_ns()); + st->event_timestamp); break; case 3: iio_push_event(indio_dev, IIO_EVENT_CODE(IIO_STEPS, 0, IIO_NO_MOD, IIO_EV_DIR_NONE, IIO_EV_TYPE_CHANGE, 0, 0, 0), - iio_get_time_ns()); + st->event_timestamp); break; default: break; @@ -238,7 +247,7 @@ int iio_simple_dummy_events_register(struct iio_dev *indio_dev) st->regs = iio_dummy_evgen_get_regs(st->event_irq); ret = request_threaded_irq(st->event_irq, - NULL, + &iio_simple_dummy_get_timestamp, &iio_simple_dummy_event_handler, IRQF_ONESHOT, "iio_simple_event", -- cgit v1.1 From c14f8abe5304ba46b898cd381a0857fd61f87200 Mon Sep 17 00:00:00 2001 From: Adriana Reus Date: Wed, 16 Sep 2015 11:14:11 +0300 Subject: iio: light: Add support for UPISEMI uS5182d als and proximity sensor Add support for UPISEMI us5182d als and proximity sensor. Supports raw readings. Data sheet for this device can be found here: http://www.upi-semi.com/temp/uS5182D-DS-P0103-temp.pdf Signed-off-by: Adriana Reus Signed-off-by: Jonathan Cameron --- drivers/iio/light/Kconfig | 10 + drivers/iio/light/Makefile | 1 + drivers/iio/light/us5182d.c | 507 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 518 insertions(+) create mode 100644 drivers/iio/light/us5182d.c diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig index 19b9a17..cfd3df8 100644 --- a/drivers/iio/light/Kconfig +++ b/drivers/iio/light/Kconfig @@ -300,6 +300,16 @@ config TSL4531 To compile this driver as a module, choose M here: the module will be called tsl4531. +config US5182D + tristate "UPISEMI light and proximity sensor" + depends on I2C + help + If you say yes here you get support for the UPISEMI US5182D + ambient light and proximity sensor. + + This driver can also be built as a module. If so, the module + will be called us5182d. + config VCNL4000 tristate "VCNL4000 combined ALS and proximity sensor" depends on I2C diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile index 7b22445..b2c3105 100644 --- a/drivers/iio/light/Makefile +++ b/drivers/iio/light/Makefile @@ -28,4 +28,5 @@ obj-$(CONFIG_STK3310) += stk3310.o obj-$(CONFIG_TCS3414) += tcs3414.o obj-$(CONFIG_TCS3472) += tcs3472.o obj-$(CONFIG_TSL4531) += tsl4531.o +obj-$(CONFIG_US5182D) += us5182d.o obj-$(CONFIG_VCNL4000) += vcnl4000.o diff --git a/drivers/iio/light/us5182d.c b/drivers/iio/light/us5182d.c new file mode 100644 index 0000000..49dab3c --- /dev/null +++ b/drivers/iio/light/us5182d.c @@ -0,0 +1,507 @@ +/* + * Copyright (c) 2015 Intel Corporation + * + * Driver for UPISEMI us5182d Proximity and Ambient Light Sensor. + * + * 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. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * To do: Interrupt support. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define US5182D_REG_CFG0 0x00 +#define US5182D_CFG0_ONESHOT_EN BIT(6) +#define US5182D_CFG0_SHUTDOWN_EN BIT(7) +#define US5182D_CFG0_WORD_ENABLE BIT(0) + +#define US5182D_REG_CFG1 0x01 +#define US5182D_CFG1_ALS_RES16 BIT(4) +#define US5182D_CFG1_AGAIN_DEFAULT 0x00 + +#define US5182D_REG_CFG2 0x02 +#define US5182D_CFG2_PX_RES16 BIT(4) +#define US5182D_CFG2_PXGAIN_DEFAULT BIT(2) + +#define US5182D_REG_CFG3 0x03 +#define US5182D_CFG3_LED_CURRENT100 (BIT(4) | BIT(5)) + +#define US5182D_REG_CFG4 0x10 + +/* + * Registers for tuning the auto dark current cancelling feature. + * DARK_TH(reg 0x27,0x28) - threshold (counts) for auto dark cancelling. + * when ALS > DARK_TH --> ALS_Code = ALS - Upper(0x2A) * Dark + * when ALS < DARK_TH --> ALS_Code = ALS - Lower(0x29) * Dark + */ +#define US5182D_REG_UDARK_TH 0x27 +#define US5182D_REG_DARK_AUTO_EN 0x2b +#define US5182D_REG_AUTO_LDARK_GAIN 0x29 +#define US5182D_REG_AUTO_HDARK_GAIN 0x2a + +#define US5182D_OPMODE_ALS 0x01 +#define US5182D_OPMODE_PX 0x02 +#define US5182D_OPMODE_SHIFT 4 + +#define US5182D_REG_DARK_AUTO_EN_DEFAULT 0x80 +#define US5182D_REG_AUTO_LDARK_GAIN_DEFAULT 0x16 +#define US5182D_REG_AUTO_HDARK_GAIN_DEFAULT 0x00 + +#define US5182D_REG_ADL 0x0c +#define US5182D_REG_PDL 0x0e + +#define US5182D_REG_MODE_STORE 0x21 +#define US5182D_STORE_MODE 0x01 + +#define US5182D_REG_CHIPID 0xb2 + +#define US5182D_OPMODE_MASK GENMASK(5, 4) +#define US5182D_AGAIN_MASK 0x07 +#define US5182D_RESET_CHIP 0x01 + +#define US5182D_CHIPID 0x26 +#define US5182D_DRV_NAME "us5182d" + +#define US5182D_GA_RESOLUTION 1000 + +#define US5182D_READ_BYTE 1 +#define US5182D_READ_WORD 2 +#define US5182D_OPSTORE_SLEEP_TIME 20 /* ms */ + +/* Available ranges: [12354, 7065, 3998, 2202, 1285, 498, 256, 138] lux */ +static const int us5182d_scales[] = {188500, 107800, 61000, 33600, 19600, 7600, + 3900, 2100}; + +/* + * Experimental thresholds that work with US5182D sensor on evaluation board + * roughly between 12-32 lux + */ +static u16 us5182d_dark_ths_vals[] = {170, 200, 512, 512, 800, 2000, 4000, + 8000}; + +enum mode { + US5182D_ALS_PX, + US5182D_ALS_ONLY, + US5182D_PX_ONLY +}; + +struct us5182d_data { + struct i2c_client *client; + struct mutex lock; + + /* Glass attenuation factor */ + u32 ga; + + /* Dark gain tuning */ + u8 lower_dark_gain; + u8 upper_dark_gain; + u16 *us5182d_dark_ths; + + u8 opmode; +}; + +static IIO_CONST_ATTR(in_illuminance_scale_available, + "0.0021 0.0039 0.0076 0.0196 0.0336 0.061 0.1078 0.1885"); + +static struct attribute *us5182d_attrs[] = { + &iio_const_attr_in_illuminance_scale_available.dev_attr.attr, + NULL +}; + +static const struct attribute_group us5182d_attr_group = { + .attrs = us5182d_attrs, +}; + +static const struct { + u8 reg; + u8 val; +} us5182d_regvals[] = { + {US5182D_REG_CFG0, (US5182D_CFG0_SHUTDOWN_EN | + US5182D_CFG0_WORD_ENABLE)}, + {US5182D_REG_CFG1, US5182D_CFG1_ALS_RES16}, + {US5182D_REG_CFG2, (US5182D_CFG2_PX_RES16 | + US5182D_CFG2_PXGAIN_DEFAULT)}, + {US5182D_REG_CFG3, US5182D_CFG3_LED_CURRENT100}, + {US5182D_REG_MODE_STORE, US5182D_STORE_MODE}, + {US5182D_REG_CFG4, 0x00}, +}; + +static const struct iio_chan_spec us5182d_channels[] = { + { + .type = IIO_LIGHT, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + }, + { + .type = IIO_PROXIMITY, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + } +}; + +static int us5182d_get_als(struct us5182d_data *data) +{ + int ret; + unsigned long result; + + ret = i2c_smbus_read_word_data(data->client, + US5182D_REG_ADL); + if (ret < 0) + return ret; + + result = ret * data->ga / US5182D_GA_RESOLUTION; + if (result > 0xffff) + result = 0xffff; + + return result; +} + +static int us5182d_set_opmode(struct us5182d_data *data, u8 mode) +{ + int ret; + + ret = i2c_smbus_read_byte_data(data->client, US5182D_REG_CFG0); + if (ret < 0) + return ret; + + /* + * In oneshot mode the chip will power itself down after taking the + * required measurement. + */ + ret = ret | US5182D_CFG0_ONESHOT_EN; + + /* update mode */ + ret = ret & ~US5182D_OPMODE_MASK; + ret = ret | (mode << US5182D_OPMODE_SHIFT); + + /* + * After updating the operating mode, the chip requires that + * the operation is stored, by writing 1 in the STORE_MODE + * register (auto-clearing). + */ + ret = i2c_smbus_write_byte_data(data->client, US5182D_REG_CFG0, ret); + if (ret < 0) + return ret; + + if (mode == data->opmode) + return 0; + + ret = i2c_smbus_write_byte_data(data->client, US5182D_REG_MODE_STORE, + US5182D_STORE_MODE); + if (ret < 0) + return ret; + + data->opmode = mode; + msleep(US5182D_OPSTORE_SLEEP_TIME); + + return 0; +} + +static int us5182d_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + struct us5182d_data *data = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + switch (chan->type) { + case IIO_LIGHT: + mutex_lock(&data->lock); + ret = us5182d_set_opmode(data, US5182D_OPMODE_ALS); + if (ret < 0) + goto out_err; + + ret = us5182d_get_als(data); + if (ret < 0) + goto out_err; + mutex_unlock(&data->lock); + *val = ret; + return IIO_VAL_INT; + case IIO_PROXIMITY: + mutex_lock(&data->lock); + ret = us5182d_set_opmode(data, US5182D_OPMODE_PX); + if (ret < 0) + goto out_err; + + ret = i2c_smbus_read_word_data(data->client, + US5182D_REG_PDL); + if (ret < 0) + goto out_err; + mutex_unlock(&data->lock); + *val = ret; + return IIO_VAL_INT; + default: + return -EINVAL; + } + + case IIO_CHAN_INFO_SCALE: + ret = i2c_smbus_read_byte_data(data->client, US5182D_REG_CFG1); + if (ret < 0) + return ret; + + *val = 0; + *val2 = us5182d_scales[ret & US5182D_AGAIN_MASK]; + + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } + + return -EINVAL; +out_err: + mutex_unlock(&data->lock); + return ret; +} + +/** + * us5182d_update_dark_th - update Darh_Th registers + * @data us5182d_data structure + * @index index in us5182d_dark_ths array to use for the updated value + * + * Function needs to be called with a lock held because it needs two i2c write + * byte operations as these registers (0x27 0x28) don't work in word mode + * accessing. + */ +static int us5182d_update_dark_th(struct us5182d_data *data, int index) +{ + __be16 dark_th = cpu_to_be16(data->us5182d_dark_ths[index]); + int ret; + + ret = i2c_smbus_write_byte_data(data->client, US5182D_REG_UDARK_TH, + ((u8 *)&dark_th)[0]); + if (ret < 0) + return ret; + + return i2c_smbus_write_byte_data(data->client, US5182D_REG_UDARK_TH + 1, + ((u8 *)&dark_th)[1]); +} + +/** + * us5182d_apply_scale - update the ALS scale + * @data us5182d_data structure + * @index index in us5182d_scales array to use for the updated value + * + * Function needs to be called with a lock held as we're having more than one + * i2c operation. + */ +static int us5182d_apply_scale(struct us5182d_data *data, int index) +{ + int ret; + + ret = i2c_smbus_read_byte_data(data->client, US5182D_REG_CFG1); + if (ret < 0) + return ret; + + ret = ret & (~US5182D_AGAIN_MASK); + ret |= index; + + ret = i2c_smbus_write_byte_data(data->client, US5182D_REG_CFG1, ret); + if (ret < 0) + return ret; + + return us5182d_update_dark_th(data, index); +} + +static int us5182d_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, + int val2, long mask) +{ + struct us5182d_data *data = iio_priv(indio_dev); + int ret, i; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + if (val != 0) + return -EINVAL; + for (i = 0; i < ARRAY_SIZE(us5182d_scales); i++) + if (val2 == us5182d_scales[i]) { + mutex_lock(&data->lock); + ret = us5182d_apply_scale(data, i); + mutex_unlock(&data->lock); + return ret; + } + break; + default: + return -EINVAL; + } + + return -EINVAL; +} + +static const struct iio_info us5182d_info = { + .driver_module = THIS_MODULE, + .read_raw = us5182d_read_raw, + .write_raw = us5182d_write_raw, + .attrs = &us5182d_attr_group, +}; + +static int us5182d_reset(struct iio_dev *indio_dev) +{ + struct us5182d_data *data = iio_priv(indio_dev); + + return i2c_smbus_write_byte_data(data->client, US5182D_REG_CFG3, + US5182D_RESET_CHIP); +} + +static int us5182d_init(struct iio_dev *indio_dev) +{ + struct us5182d_data *data = iio_priv(indio_dev); + int i, ret; + + ret = us5182d_reset(indio_dev); + if (ret < 0) + return ret; + + data->opmode = 0; + for (i = 0; i < ARRAY_SIZE(us5182d_regvals); i++) { + ret = i2c_smbus_write_byte_data(data->client, + us5182d_regvals[i].reg, + us5182d_regvals[i].val); + if (ret < 0) + return ret; + } + + return 0; +} + +static void us5182d_get_platform_data(struct iio_dev *indio_dev) +{ + struct us5182d_data *data = iio_priv(indio_dev); + + if (device_property_read_u32(&data->client->dev, "upisemi,glass-coef", + &data->ga)) + data->ga = US5182D_GA_RESOLUTION; + if (device_property_read_u16_array(&data->client->dev, + "upisemi,dark-ths", + data->us5182d_dark_ths, + ARRAY_SIZE(us5182d_dark_ths_vals))) + data->us5182d_dark_ths = us5182d_dark_ths_vals; + if (device_property_read_u8(&data->client->dev, + "upisemi,upper-dark-gain", + &data->upper_dark_gain)) + data->upper_dark_gain = US5182D_REG_AUTO_HDARK_GAIN_DEFAULT; + if (device_property_read_u8(&data->client->dev, + "upisemi,lower-dark-gain", + &data->lower_dark_gain)) + data->lower_dark_gain = US5182D_REG_AUTO_LDARK_GAIN_DEFAULT; +} + +static int us5182d_dark_gain_config(struct iio_dev *indio_dev) +{ + struct us5182d_data *data = iio_priv(indio_dev); + int ret; + + ret = us5182d_update_dark_th(data, US5182D_CFG1_AGAIN_DEFAULT); + if (ret < 0) + return ret; + + ret = i2c_smbus_write_byte_data(data->client, + US5182D_REG_AUTO_LDARK_GAIN, + data->lower_dark_gain); + if (ret < 0) + return ret; + + ret = i2c_smbus_write_byte_data(data->client, + US5182D_REG_AUTO_HDARK_GAIN, + data->upper_dark_gain); + if (ret < 0) + return ret; + + return i2c_smbus_write_byte_data(data->client, US5182D_REG_DARK_AUTO_EN, + US5182D_REG_DARK_AUTO_EN_DEFAULT); +} + +static int us5182d_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct us5182d_data *data; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + i2c_set_clientdata(client, indio_dev); + data->client = client; + + mutex_init(&data->lock); + + indio_dev->dev.parent = &client->dev; + indio_dev->info = &us5182d_info; + indio_dev->name = US5182D_DRV_NAME; + indio_dev->channels = us5182d_channels; + indio_dev->num_channels = ARRAY_SIZE(us5182d_channels); + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = i2c_smbus_read_byte_data(data->client, US5182D_REG_CHIPID); + if (ret != US5182D_CHIPID) { + dev_err(&data->client->dev, + "Failed to detect US5182 light chip\n"); + return (ret < 0) ? ret : -ENODEV; + } + + us5182d_get_platform_data(indio_dev); + ret = us5182d_init(indio_dev); + if (ret < 0) + return ret; + + ret = us5182d_dark_gain_config(indio_dev); + if (ret < 0) + return ret; + + return iio_device_register(indio_dev); +} + +static int us5182d_remove(struct i2c_client *client) +{ + iio_device_unregister(i2c_get_clientdata(client)); + return i2c_smbus_write_byte_data(client, US5182D_REG_CFG0, + US5182D_CFG0_SHUTDOWN_EN); +} + +static const struct acpi_device_id us5182d_acpi_match[] = { + { "USD5182", 0}, + {} +}; + +MODULE_DEVICE_TABLE(acpi, us5182d_acpi_match); + +static const struct i2c_device_id us5182d_id[] = { + {"usd5182", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, us5182d_id); + +static struct i2c_driver us5182d_driver = { + .driver = { + .name = US5182D_DRV_NAME, + .acpi_match_table = ACPI_PTR(us5182d_acpi_match), + }, + .probe = us5182d_probe, + .remove = us5182d_remove, + .id_table = us5182d_id, + +}; +module_i2c_driver(us5182d_driver); + +MODULE_AUTHOR("Adriana Reus "); +MODULE_DESCRIPTION("Driver for us5182d Proximity and Light Sensor"); +MODULE_LICENSE("GPL v2"); -- cgit v1.1 From 9214185b515ed550fe67c912ca446b4524f01603 Mon Sep 17 00:00:00 2001 From: Adriana Reus Date: Wed, 16 Sep 2015 11:14:12 +0300 Subject: devicetree: Add documentation for UPISEMI us5182d ALS and Proximity sensor Added entries in i2c/vendor-prefixes for the us5182d als and proximity sensor. Also added a documentation file for this sensor's properties. Signed-off-by: Adriana Reus Acked-by: Rob Herring Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/light/us5182d.txt | 34 ++++++++++++++++++++++ .../devicetree/bindings/vendor-prefixes.txt | 1 + 2 files changed, 35 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/light/us5182d.txt diff --git a/Documentation/devicetree/bindings/iio/light/us5182d.txt b/Documentation/devicetree/bindings/iio/light/us5182d.txt new file mode 100644 index 0000000..6f0a530 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/light/us5182d.txt @@ -0,0 +1,34 @@ +* UPISEMI us5182d I2C ALS and Proximity sensor + +Required properties: +- compatible: must be "upisemi,usd5182" +- reg: the I2C address of the device + +Optional properties: +- upisemi,glass-coef: glass attenuation factor - compensation factor of + resolution 1000 for material transmittance. +- upisemi,dark-ths: array of 8 elements containing 16-bit thresholds (adc + counts) corresponding to every scale. +- upisemi,upper-dark-gain: 8-bit dark gain compensation factor(4 int and 4 + fractional bits - Q4.4) applied when light > threshold +- upisemi,lower-dark-gain: 8-bit dark gain compensation factor(4 int and 4 + fractional bits - Q4.4) applied when light < threshold + +If the optional properties are not specified these factors will default to the +values in the below example. +The glass-coef defaults to no compensation for the covering material. +The threshold array defaults to experimental values that work with US5182D +sensor on evaluation board - roughly between 12-32 lux. +There will be no dark-gain compensation by default when ALS > thresh +(0 * dark-gain), and a 1.35 compensation factor when ALS < thresh. + +Example: + + usd5182@39 { + compatible = "upisemi,usd5182"; + reg = <0x39>; + upisemi,glass-coef = < 1000 >; + upisemi,dark-ths = /bits/ 16 <170 200 512 512 800 2000 4000 8000>; + upisemi,upper-dark-gain = /bits/ 8 <0x00>; + upisemi,lower-dark-gain = /bits/ 8 <0x16>; + }; diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index d591561..f2b1d6f 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -214,6 +214,7 @@ toshiba Toshiba Corporation toumaz Toumaz tplink TP-LINK Technologies Co., Ltd. truly Truly Semiconductors Limited +upisemi uPI Semiconductor Corp. usi Universal Scientific Industrial Co., Ltd. v3 V3 Semiconductor variscite Variscite Ltd. -- cgit v1.1 From 36736cc66bcedcd25b65faff43e352491409971f Mon Sep 17 00:00:00 2001 From: kbuild test robot Date: Tue, 22 Sep 2015 10:16:48 +0800 Subject: iio: adc: vf610: fix simple_return.cocci warnings drivers/iio/adc/vf610_adc.c:766:1-4: WARNING: end returns can be simpified and declaration on line 755 can be dropped Simplify a trivial if-return sequence. Possibly combine with a preceding function call. Generated by: scripts/coccinelle/misc/simple_return.cocci CC: Sanchayan Maity Signed-off-by: Fengguang Wu Signed-off-by: Jonathan Cameron --- drivers/iio/adc/vf610_adc.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/iio/adc/vf610_adc.c b/drivers/iio/adc/vf610_adc.c index 635ccd8..599cde3 100644 --- a/drivers/iio/adc/vf610_adc.c +++ b/drivers/iio/adc/vf610_adc.c @@ -752,7 +752,7 @@ static int vf610_adc_buffer_predisable(struct iio_dev *indio_dev) { struct vf610_adc *info = iio_priv(indio_dev); unsigned int hc_cfg = 0; - int val, ret; + int val; val = readl(info->regs + VF610_REG_ADC_GC); val &= ~VF610_ADC_ADCON; @@ -763,11 +763,7 @@ static int vf610_adc_buffer_predisable(struct iio_dev *indio_dev) writel(hc_cfg, info->regs + VF610_REG_ADC_HC0); - ret = iio_triggered_buffer_predisable(indio_dev); - if (ret) - return ret; - - return 0; + return iio_triggered_buffer_predisable(indio_dev); } static const struct iio_buffer_setup_ops iio_triggered_buffer_setup_ops = { -- cgit v1.1 From ebe5c543cf8ed3010881a8d863e954b961013fea Mon Sep 17 00:00:00 2001 From: kbuild test robot Date: Tue, 22 Sep 2015 10:21:48 +0800 Subject: iio: apds9960: light: fix simple_return.cocci warnings drivers/iio/light/apds9960.c:986:1-4: WARNING: end returns can be simpified Simplify a trivial if-return sequence. Possibly combine with a preceding function call. Generated by: scripts/coccinelle/misc/simple_return.cocci CC: Matt Ranostay Signed-off-by: Fengguang Wu Signed-off-by: Jonathan Cameron --- drivers/iio/light/apds9960.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/iio/light/apds9960.c b/drivers/iio/light/apds9960.c index 27f4157..8d7ce6a 100644 --- a/drivers/iio/light/apds9960.c +++ b/drivers/iio/light/apds9960.c @@ -983,11 +983,7 @@ static int apds9960_chip_init(struct apds9960_data *data) if (ret) return ret; - ret = apds9960_set_powermode(data, 1); - if (ret) - return ret; - - return 0; + return apds9960_set_powermode(data, 1); } static int apds9960_probe(struct i2c_client *client, -- cgit v1.1 From 1d2f1e084b7386b4082ebc2490ce0ddc8efe5667 Mon Sep 17 00:00:00 2001 From: kbuild test robot Date: Tue, 22 Sep 2015 10:21:48 +0800 Subject: iio: light: apds9960: fix platform_no_drv_owner.cocci warnings drivers/iio/light/apds9960.c:1125:3-8: No need to set .owner here. The core will do it. Remove .owner field if calls are used which set it automatically Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci CC: Matt Ranostay Signed-off-by: Fengguang Wu Signed-off-by: Jonathan Cameron --- drivers/iio/light/apds9960.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/iio/light/apds9960.c b/drivers/iio/light/apds9960.c index 8d7ce6a..bf80ce4 100644 --- a/drivers/iio/light/apds9960.c +++ b/drivers/iio/light/apds9960.c @@ -1118,7 +1118,6 @@ static struct i2c_driver apds9960_driver = { .driver = { .name = APDS9960_DRV_NAME, .pm = &apds9960_pm_ops, - .owner = THIS_MODULE, }, .probe = apds9960_probe, .remove = apds9960_remove, -- cgit v1.1