Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > linux.kernel > #1381760 > unrolled thread

[PATCH v3 0/5] Support for max44000 Ambient and Infrared Proximity Sensor

Started byCrestez Dan Leonard <leonard.crestez@intel.com>
First post2016-04-18 16:40 +0200
Last post2016-04-24 11:20 +0200
Articles 9 — 2 participants

Back to article view | Back to linux.kernel


Contents

  [PATCH v3 0/5] Support for max44000 Ambient and Infrared Proximity Sensor Crestez Dan Leonard <leonard.crestez@intel.com> - 2016-04-18 16:40 +0200
    [PATCH v3 2/5] max44000: Initial support for proximity reading Crestez Dan Leonard <leonard.crestez@intel.com> - 2016-04-18 16:40 +0200
      Re: [PATCH v3 2/5] max44000: Initial support for proximity reading Jonathan Cameron <jic23@kernel.org> - 2016-04-24 11:30 +0200
    [PATCH v3 5/5] max44000: Initial triggered buffer support Crestez Dan Leonard <leonard.crestez@intel.com> - 2016-04-18 16:40 +0200
      Re: [PATCH v3 5/5] max44000: Initial triggered buffer support Jonathan Cameron <jic23@kernel.org> - 2016-04-24 11:30 +0200
    [PATCH v3 3/5] max44000: Support controlling LED current output Crestez Dan Leonard <leonard.crestez@intel.com> - 2016-04-18 16:40 +0200
      Re: [PATCH v3 3/5] max44000: Support controlling LED current output Jonathan Cameron <jic23@kernel.org> - 2016-04-24 11:30 +0200
    [PATCH v3 1/5] max44000: Initial support Crestez Dan Leonard <leonard.crestez@intel.com> - 2016-04-18 16:40 +0200
      Re: [PATCH v3 1/5] max44000: Initial support Jonathan Cameron <jic23@kernel.org> - 2016-04-24 11:20 +0200

#1381760 — [PATCH v3 0/5] Support for max44000 Ambient and Infrared Proximity Sensor

FromCrestez Dan Leonard <leonard.crestez@intel.com>
Date2016-04-18 16:40 +0200
Subject[PATCH v3 0/5] Support for max44000 Ambient and Infrared Proximity Sensor
Message-ID<rpivf-79W-3@gated-at.bofh.it>
Changes since v2:
* Drop reg_defaults from regmap_config
* Use REGCACHE_RBTREE
* Implement readable/writable/volatile reg with switches
* Use test_bit when checking the scan mask
* Add _INT_TIME info_mask in patch 4 rather than 2
* Cleaner spacing in max44000_remove

Changes since v1:
* Remove max44000_data.client
* Maintain alphabetical order in kconfig/makefile
* Avoid pointless inlines
* Remove explicit CONFIG_OF because no special handling is done
* Set storagebits = 16 for proximity channel and avoid VLAs
* Expand MAX44000_REGMASK for readability/reviewability
* Clear irq bits at the end of the probe function
* Only write important bits on probe rather than everything

Crestez Dan Leonard (5):
  max44000: Initial support
  max44000: Initial support for proximity reading
  max44000: Support controlling LED current output
  max44000: Expose ambient sensor scaling
  max44000: Initial triggered buffer support

 drivers/iio/light/Kconfig    |  11 +
 drivers/iio/light/Makefile   |   1 +
 drivers/iio/light/max44000.c | 637 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 649 insertions(+)
 create mode 100644 drivers/iio/light/max44000.c

-- 
2.5.5

[toc] | [next] | [standalone]


#1381763 — [PATCH v3 2/5] max44000: Initial support for proximity reading

FromCrestez Dan Leonard <leonard.crestez@intel.com>
Date2016-04-18 16:40 +0200
Subject[PATCH v3 2/5] max44000: Initial support for proximity reading
Message-ID<rpivg-79W-23@gated-at.bofh.it>
In reply to#1381760
The proximity sensor relies on sending pulses to an external IR led and
it is disabled by default on powerup. The driver will enable it with a
default power setting.

Signed-off-by: Crestez Dan Leonard <leonard.crestez@intel.com>
---
 drivers/iio/light/max44000.c | 45 ++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 43 insertions(+), 2 deletions(-)

diff --git a/drivers/iio/light/max44000.c b/drivers/iio/light/max44000.c
index 5ed7934..dc63de2 100644
--- a/drivers/iio/light/max44000.c
+++ b/drivers/iio/light/max44000.c
@@ -59,6 +59,11 @@
  */
 #define MAX44000_REG_CFG_RX_DEFAULT 0xf0
 
+/* REG_TX bits */
+#define MAX44000_LED_CURRENT_MASK	0xf
+#define MAX44000_LED_CURRENT_MAX	11
+#define MAX44000_LED_CURRENT_DEFAULT	6
+
 #define MAX44000_ALSDATA_OVERFLOW	0x4000
 
 struct max44000_data {
@@ -75,6 +80,11 @@ static const struct iio_chan_spec max44000_channels[] = {
 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
 		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
 	},
+	{
+		.type = IIO_PROXIMITY,
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
+	},
 };
 
 static int max44000_read_alsval(struct max44000_data *data)
@@ -104,11 +114,23 @@ static int max44000_read_alsval(struct max44000_data *data)
 	return regval;
 }
 
+static int max44000_write_led_current_raw(struct max44000_data *data, int val)
+{
+	/* Maybe we should clamp the value instead? */
+	if (val < 0 || val > MAX44000_LED_CURRENT_MAX)
+		return -ERANGE;
+	if (val >= 8)
+		val += 4;
+	return regmap_write_bits(data->regmap, MAX44000_REG_CFG_TX,
+				 MAX44000_LED_CURRENT_MASK, val);
+}
+
 static int max44000_read_raw(struct iio_dev *indio_dev,
 			     struct iio_chan_spec const *chan,
 			     int *val, int *val2, long mask)
 {
 	struct max44000_data *data = iio_priv(indio_dev);
+	unsigned int regval;
 	int ret;
 
 	switch (mask) {
@@ -123,6 +145,15 @@ static int max44000_read_raw(struct iio_dev *indio_dev,
 			*val = ret;
 			return IIO_VAL_INT;
 
+		case IIO_PROXIMITY:
+			mutex_lock(&data->lock);
+			ret = regmap_read(data->regmap, MAX44000_REG_PRX_DATA, &regval);
+			mutex_unlock(&data->lock);
+			if (ret < 0)
+				return ret;
+			*val = regval;
+			return IIO_VAL_INT;
+
 		default:
 			return -EINVAL;
 		}
@@ -266,8 +297,18 @@ static int max44000_probe(struct i2c_client *client,
 		return ret;
 	}
 
-	/* Reset CFG bits to ALS-only mode and no interrupts */
-	reg = MAX44000_CFG_TRIM | MAX44000_CFG_MODE_ALS_GIR;
+	/*
+	 * By default the LED pulse used for the proximity sensor is disabled.
+	 * Set a middle value so that we get some sort of valid data by default.
+	 */
+	ret = max44000_write_led_current_raw(data, MAX44000_LED_CURRENT_DEFAULT);
+	if (ret < 0) {
+		dev_err(&client->dev, "failed to write init config: %d\n", ret);
+		return ret;
+	}
+
+	/* Reset CFG bits to ALS_PRX mode which allows easy reading of both values. */
+	reg = MAX44000_CFG_TRIM | MAX44000_CFG_MODE_ALS_PRX;
 	ret = regmap_write(data->regmap, MAX44000_REG_CFG_MAIN, reg);
 	if (ret < 0) {
 		dev_err(&client->dev, "failed to write init config: %d\n", ret);
-- 
2.5.5

[toc] | [prev] | [next] | [standalone]


#1385785 — Re: [PATCH v3 2/5] max44000: Initial support for proximity reading

FromJonathan Cameron <jic23@kernel.org>
Date2016-04-24 11:30 +0200
SubjectRe: [PATCH v3 2/5] max44000: Initial support for proximity reading
Message-ID<rrowx-26r-1@gated-at.bofh.it>
In reply to#1381763
On 18/04/16 15:31, Crestez Dan Leonard wrote:
> The proximity sensor relies on sending pulses to an external IR led and
> it is disabled by default on powerup. The driver will enable it with a
> default power setting.
> 
> Signed-off-by: Crestez Dan Leonard <leonard.crestez@intel.com>
Applied.
> ---
>  drivers/iio/light/max44000.c | 45 ++++++++++++++++++++++++++++++++++++++++++--
>  1 file changed, 43 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/iio/light/max44000.c b/drivers/iio/light/max44000.c
> index 5ed7934..dc63de2 100644
> --- a/drivers/iio/light/max44000.c
> +++ b/drivers/iio/light/max44000.c
> @@ -59,6 +59,11 @@
>   */
>  #define MAX44000_REG_CFG_RX_DEFAULT 0xf0
>  
> +/* REG_TX bits */
> +#define MAX44000_LED_CURRENT_MASK	0xf
> +#define MAX44000_LED_CURRENT_MAX	11
> +#define MAX44000_LED_CURRENT_DEFAULT	6
> +
>  #define MAX44000_ALSDATA_OVERFLOW	0x4000
>  
>  struct max44000_data {
> @@ -75,6 +80,11 @@ static const struct iio_chan_spec max44000_channels[] = {
>  		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
>  		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
>  	},
> +	{
> +		.type = IIO_PROXIMITY,
> +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
> +		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
> +	},
>  };
>  
>  static int max44000_read_alsval(struct max44000_data *data)
> @@ -104,11 +114,23 @@ static int max44000_read_alsval(struct max44000_data *data)
>  	return regval;
>  }
>  
> +static int max44000_write_led_current_raw(struct max44000_data *data, int val)
> +{
> +	/* Maybe we should clamp the value instead? */
> +	if (val < 0 || val > MAX44000_LED_CURRENT_MAX)
> +		return -ERANGE;
> +	if (val >= 8)
> +		val += 4;
> +	return regmap_write_bits(data->regmap, MAX44000_REG_CFG_TX,
> +				 MAX44000_LED_CURRENT_MASK, val);
> +}
> +
>  static int max44000_read_raw(struct iio_dev *indio_dev,
>  			     struct iio_chan_spec const *chan,
>  			     int *val, int *val2, long mask)
>  {
>  	struct max44000_data *data = iio_priv(indio_dev);
> +	unsigned int regval;
>  	int ret;
>  
>  	switch (mask) {
> @@ -123,6 +145,15 @@ static int max44000_read_raw(struct iio_dev *indio_dev,
>  			*val = ret;
>  			return IIO_VAL_INT;
>  
> +		case IIO_PROXIMITY:
> +			mutex_lock(&data->lock);
> +			ret = regmap_read(data->regmap, MAX44000_REG_PRX_DATA, &regval);
> +			mutex_unlock(&data->lock);
> +			if (ret < 0)
> +				return ret;
> +			*val = regval;
> +			return IIO_VAL_INT;
> +
>  		default:
>  			return -EINVAL;
>  		}
> @@ -266,8 +297,18 @@ static int max44000_probe(struct i2c_client *client,
>  		return ret;
>  	}
>  
> -	/* Reset CFG bits to ALS-only mode and no interrupts */
> -	reg = MAX44000_CFG_TRIM | MAX44000_CFG_MODE_ALS_GIR;
> +	/*
> +	 * By default the LED pulse used for the proximity sensor is disabled.
> +	 * Set a middle value so that we get some sort of valid data by default.
> +	 */
> +	ret = max44000_write_led_current_raw(data, MAX44000_LED_CURRENT_DEFAULT);
> +	if (ret < 0) {
> +		dev_err(&client->dev, "failed to write init config: %d\n", ret);
> +		return ret;
> +	}
> +
> +	/* Reset CFG bits to ALS_PRX mode which allows easy reading of both values. */
> +	reg = MAX44000_CFG_TRIM | MAX44000_CFG_MODE_ALS_PRX;
>  	ret = regmap_write(data->regmap, MAX44000_REG_CFG_MAIN, reg);
>  	if (ret < 0) {
>  		dev_err(&client->dev, "failed to write init config: %d\n", ret);
> 

[toc] | [prev] | [next] | [standalone]


#1381764 — [PATCH v3 5/5] max44000: Initial triggered buffer support

FromCrestez Dan Leonard <leonard.crestez@intel.com>
Date2016-04-18 16:40 +0200
Subject[PATCH v3 5/5] max44000: Initial triggered buffer support
Message-ID<rpivg-79W-29@gated-at.bofh.it>
In reply to#1381760
Signed-off-by: Crestez Dan Leonard <leonard.crestez@intel.com>
---
 drivers/iio/light/max44000.c | 62 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 62 insertions(+)

diff --git a/drivers/iio/light/max44000.c b/drivers/iio/light/max44000.c
index c58bb78..b83d47d 100644
--- a/drivers/iio/light/max44000.c
+++ b/drivers/iio/light/max44000.c
@@ -19,6 +19,9 @@
 #include <linux/util_macros.h>
 #include <linux/iio/iio.h>
 #include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
 #include <linux/acpi.h>
 
 #define MAX44000_DRV_NAME		"max44000"
@@ -125,24 +128,41 @@ static const char max44000_scale_avail_str[] =
 	"0.5 "
 	 "4";
 
+#define MAX44000_SCAN_INDEX_ALS 0
+#define MAX44000_SCAN_INDEX_PRX 1
+
 static const struct iio_chan_spec max44000_channels[] = {
 	{
 		.type = IIO_LIGHT,
 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
 		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) |
 					    BIT(IIO_CHAN_INFO_INT_TIME),
+		.scan_index = MAX44000_SCAN_INDEX_ALS,
+		.scan_type = {
+			.sign		= 'u',
+			.realbits	= 14,
+			.storagebits	= 16,
+		}
 	},
 	{
 		.type = IIO_PROXIMITY,
 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
 		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
+		.scan_index = MAX44000_SCAN_INDEX_PRX,
+		.scan_type = {
+			.sign		= 'u',
+			.realbits	= 8,
+			.storagebits	= 16,
+		}
 	},
+	IIO_CHAN_SOFT_TIMESTAMP(2),
 	{
 		.type = IIO_CURRENT,
 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
 				      BIT(IIO_CHAN_INFO_SCALE),
 		.extend_name = "led",
 		.output = 1,
+		.scan_index = -1,
 	},
 };
 
@@ -467,6 +487,41 @@ static const struct regmap_config max44000_regmap_config = {
 	.cache_type	= REGCACHE_RBTREE,
 };
 
+static irqreturn_t max44000_trigger_handler(int irq, void *p)
+{
+	struct iio_poll_func *pf = p;
+	struct iio_dev *indio_dev = pf->indio_dev;
+	struct max44000_data *data = iio_priv(indio_dev);
+	u16 buf[8]; /* 2x u16 + padding + 8 bytes timestamp */
+	int index = 0;
+	unsigned int regval;
+	int ret;
+
+	mutex_lock(&data->lock);
+	if (test_bit(MAX44000_SCAN_INDEX_ALS, indio_dev->active_scan_mask)) {
+		ret = max44000_read_alsval(data);
+		if (ret < 0)
+			goto out_unlock;
+		buf[index++] = ret;
+	}
+	if (test_bit(MAX44000_SCAN_INDEX_PRX, indio_dev->active_scan_mask)) {
+		ret = regmap_read(data->regmap, MAX44000_REG_PRX_DATA, &regval);
+		if (ret < 0)
+			goto out_unlock;
+		buf[index] = regval;
+	}
+	mutex_unlock(&data->lock);
+
+	iio_push_to_buffers_with_timestamp(indio_dev, buf, iio_get_time_ns());
+	iio_trigger_notify_done(indio_dev->trig);
+	return IRQ_HANDLED;
+
+out_unlock:
+	mutex_unlock(&data->lock);
+	iio_trigger_notify_done(indio_dev->trig);
+	return IRQ_HANDLED;
+}
+
 static int max44000_probe(struct i2c_client *client,
 			  const struct i2c_device_id *id)
 {
@@ -532,6 +587,12 @@ static int max44000_probe(struct i2c_client *client,
 		return ret;
 	}
 
+	ret = iio_triggered_buffer_setup(indio_dev, NULL, max44000_trigger_handler, NULL);
+	if (ret < 0) {
+		dev_err(&client->dev, "iio triggered buffer setup failed\n");
+		return ret;
+	}
+
 	return iio_device_register(indio_dev);
 }
 
@@ -540,6 +601,7 @@ static int max44000_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;
 }
-- 
2.5.5

[toc] | [prev] | [next] | [standalone]


#1385788 — Re: [PATCH v3 5/5] max44000: Initial triggered buffer support

FromJonathan Cameron <jic23@kernel.org>
Date2016-04-24 11:30 +0200
SubjectRe: [PATCH v3 5/5] max44000: Initial triggered buffer support
Message-ID<rrowx-26r-11@gated-at.bofh.it>
In reply to#1381764
On 18/04/16 15:31, Crestez Dan Leonard wrote:
> Signed-off-by: Crestez Dan Leonard <leonard.crestez@intel.com>
Applied to the togreg branch of iio.git - initially pushed out as testing
for the autobuilders to play with it.

Thanks,

Jonathan
> ---
>  drivers/iio/light/max44000.c | 62 ++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 62 insertions(+)
> 
> diff --git a/drivers/iio/light/max44000.c b/drivers/iio/light/max44000.c
> index c58bb78..b83d47d 100644
> --- a/drivers/iio/light/max44000.c
> +++ b/drivers/iio/light/max44000.c
> @@ -19,6 +19,9 @@
>  #include <linux/util_macros.h>
>  #include <linux/iio/iio.h>
>  #include <linux/iio/sysfs.h>
> +#include <linux/iio/buffer.h>
> +#include <linux/iio/trigger_consumer.h>
> +#include <linux/iio/triggered_buffer.h>
>  #include <linux/acpi.h>
>  
>  #define MAX44000_DRV_NAME		"max44000"
> @@ -125,24 +128,41 @@ static const char max44000_scale_avail_str[] =
>  	"0.5 "
>  	 "4";
>  
> +#define MAX44000_SCAN_INDEX_ALS 0
> +#define MAX44000_SCAN_INDEX_PRX 1
> +
>  static const struct iio_chan_spec max44000_channels[] = {
>  	{
>  		.type = IIO_LIGHT,
>  		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
>  		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) |
>  					    BIT(IIO_CHAN_INFO_INT_TIME),
> +		.scan_index = MAX44000_SCAN_INDEX_ALS,
> +		.scan_type = {
> +			.sign		= 'u',
> +			.realbits	= 14,
> +			.storagebits	= 16,
> +		}
>  	},
>  	{
>  		.type = IIO_PROXIMITY,
>  		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
>  		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
> +		.scan_index = MAX44000_SCAN_INDEX_PRX,
> +		.scan_type = {
> +			.sign		= 'u',
> +			.realbits	= 8,
> +			.storagebits	= 16,
> +		}
>  	},
> +	IIO_CHAN_SOFT_TIMESTAMP(2),
>  	{
>  		.type = IIO_CURRENT,
>  		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
>  				      BIT(IIO_CHAN_INFO_SCALE),
>  		.extend_name = "led",
>  		.output = 1,
> +		.scan_index = -1,
>  	},
>  };
>  
> @@ -467,6 +487,41 @@ static const struct regmap_config max44000_regmap_config = {
>  	.cache_type	= REGCACHE_RBTREE,
>  };
>  
> +static irqreturn_t max44000_trigger_handler(int irq, void *p)
> +{
> +	struct iio_poll_func *pf = p;
> +	struct iio_dev *indio_dev = pf->indio_dev;
> +	struct max44000_data *data = iio_priv(indio_dev);
> +	u16 buf[8]; /* 2x u16 + padding + 8 bytes timestamp */
> +	int index = 0;
> +	unsigned int regval;
> +	int ret;
> +
> +	mutex_lock(&data->lock);
> +	if (test_bit(MAX44000_SCAN_INDEX_ALS, indio_dev->active_scan_mask)) {
> +		ret = max44000_read_alsval(data);
> +		if (ret < 0)
> +			goto out_unlock;
> +		buf[index++] = ret;
> +	}
> +	if (test_bit(MAX44000_SCAN_INDEX_PRX, indio_dev->active_scan_mask)) {
> +		ret = regmap_read(data->regmap, MAX44000_REG_PRX_DATA, &regval);
> +		if (ret < 0)
> +			goto out_unlock;
> +		buf[index] = regval;
> +	}
> +	mutex_unlock(&data->lock);
> +
> +	iio_push_to_buffers_with_timestamp(indio_dev, buf, iio_get_time_ns());
> +	iio_trigger_notify_done(indio_dev->trig);
> +	return IRQ_HANDLED;
> +
> +out_unlock:
> +	mutex_unlock(&data->lock);
> +	iio_trigger_notify_done(indio_dev->trig);
> +	return IRQ_HANDLED;
> +}
> +
>  static int max44000_probe(struct i2c_client *client,
>  			  const struct i2c_device_id *id)
>  {
> @@ -532,6 +587,12 @@ static int max44000_probe(struct i2c_client *client,
>  		return ret;
>  	}
>  
> +	ret = iio_triggered_buffer_setup(indio_dev, NULL, max44000_trigger_handler, NULL);
> +	if (ret < 0) {
> +		dev_err(&client->dev, "iio triggered buffer setup failed\n");
> +		return ret;
> +	}
> +
>  	return iio_device_register(indio_dev);
>  }
>  
> @@ -540,6 +601,7 @@ static int max44000_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;
>  }
> 

[toc] | [prev] | [next] | [standalone]


#1381765 — [PATCH v3 3/5] max44000: Support controlling LED current output

FromCrestez Dan Leonard <leonard.crestez@intel.com>
Date2016-04-18 16:40 +0200
Subject[PATCH v3 3/5] max44000: Support controlling LED current output
Message-ID<rpivg-79W-31@gated-at.bofh.it>
In reply to#1381760
This is exposed as an output channel with "led" as an extend_name.

Other sensors also have support for controlling an external LED. It's
not clear that simply exposing an undecorated output channel is the
correct approach.

Signed-off-by: Crestez Dan Leonard <leonard.crestez@intel.com>
---
 drivers/iio/light/max44000.c | 53 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 53 insertions(+)

diff --git a/drivers/iio/light/max44000.c b/drivers/iio/light/max44000.c
index dc63de2..0ccd110 100644
--- a/drivers/iio/light/max44000.c
+++ b/drivers/iio/light/max44000.c
@@ -85,6 +85,13 @@ static const struct iio_chan_spec max44000_channels[] = {
 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
 		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
 	},
+	{
+		.type = IIO_CURRENT,
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+				      BIT(IIO_CHAN_INFO_SCALE),
+		.extend_name = "led",
+		.output = 1,
+	},
 };
 
 static int max44000_read_alsval(struct max44000_data *data)
@@ -125,6 +132,20 @@ static int max44000_write_led_current_raw(struct max44000_data *data, int val)
 				 MAX44000_LED_CURRENT_MASK, val);
 }
 
+static int max44000_read_led_current_raw(struct max44000_data *data)
+{
+	unsigned int regval;
+	int ret;
+
+	ret = regmap_read(data->regmap, MAX44000_REG_CFG_TX, &regval);
+	if (ret < 0)
+		return ret;
+	regval &= MAX44000_LED_CURRENT_MASK;
+	if (regval >= 8)
+		regval -= 4;
+	return regval;
+}
+
 static int max44000_read_raw(struct iio_dev *indio_dev,
 			     struct iio_chan_spec const *chan,
 			     int *val, int *val2, long mask)
@@ -154,12 +175,26 @@ static int max44000_read_raw(struct iio_dev *indio_dev,
 			*val = regval;
 			return IIO_VAL_INT;
 
+		case IIO_CURRENT:
+			mutex_lock(&data->lock);
+			ret = max44000_read_led_current_raw(data);
+			mutex_unlock(&data->lock);
+			if (ret < 0)
+				return ret;
+			*val = ret;
+			return IIO_VAL_INT;
+
 		default:
 			return -EINVAL;
 		}
 
 	case IIO_CHAN_INFO_SCALE:
 		switch (chan->type) {
+		case IIO_CURRENT:
+			/* Output register is in 10s of miliamps */
+			*val = 10;
+			return IIO_VAL_INT;
+
 		case IIO_LIGHT:
 			*val = 1;
 			*val2 = MAX44000_ALS_TO_LUX_DEFAULT_FRACTION_LOG2;
@@ -174,9 +209,27 @@ static int max44000_read_raw(struct iio_dev *indio_dev,
 	}
 }
 
+static int max44000_write_raw(struct iio_dev *indio_dev,
+			      struct iio_chan_spec const *chan,
+			      int val, int val2, long mask)
+{
+	struct max44000_data *data = iio_priv(indio_dev);
+	int ret;
+
+	if (mask == IIO_CHAN_INFO_RAW && chan->type == IIO_CURRENT) {
+		mutex_lock(&data->lock);
+		ret = max44000_write_led_current_raw(data, val);
+		mutex_unlock(&data->lock);
+		return ret;
+	}
+
+	return -EINVAL;
+}
+
 static const struct iio_info max44000_info = {
 	.driver_module		= THIS_MODULE,
 	.read_raw		= max44000_read_raw,
+	.write_raw		= max44000_write_raw,
 };
 
 static bool max44000_readable_reg(struct device *dev, unsigned int reg)
-- 
2.5.5

[toc] | [prev] | [next] | [standalone]


#1385786 — Re: [PATCH v3 3/5] max44000: Support controlling LED current output

FromJonathan Cameron <jic23@kernel.org>
Date2016-04-24 11:30 +0200
SubjectRe: [PATCH v3 3/5] max44000: Support controlling LED current output
Message-ID<rrowx-26r-3@gated-at.bofh.it>
In reply to#1381765
On 18/04/16 15:31, Crestez Dan Leonard wrote:
> This is exposed as an output channel with "led" as an extend_name.
> 
> Other sensors also have support for controlling an external LED. It's
> not clear that simply exposing an undecorated output channel is thee
> correct approach.
> 
> Signed-off-by: Crestez Dan Leonard <leonard.crestez@intel.com>
While I suspect we will sometime have a better solution for this common problem,
it won't be a problem to maintain this interface as well

Applied
> ---
>  drivers/iio/light/max44000.c | 53 ++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 53 insertions(+)
> 
> diff --git a/drivers/iio/light/max44000.c b/drivers/iio/light/max44000.c
> index dc63de2..0ccd110 100644
> --- a/drivers/iio/light/max44000.c
> +++ b/drivers/iio/light/max44000.c
> @@ -85,6 +85,13 @@ static const struct iio_chan_spec max44000_channels[] = {
>  		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
>  		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
>  	},
> +	{
> +		.type = IIO_CURRENT,
> +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
> +				      BIT(IIO_CHAN_INFO_SCALE),
> +		.extend_name = "led",
> +		.output = 1,
> +	},
>  };
>  
>  static int max44000_read_alsval(struct max44000_data *data)
> @@ -125,6 +132,20 @@ static int max44000_write_led_current_raw(struct max44000_data *data, int val)
>  				 MAX44000_LED_CURRENT_MASK, val);
>  }
>  
> +static int max44000_read_led_current_raw(struct max44000_data *data)
> +{
> +	unsigned int regval;
> +	int ret;
> +
> +	ret = regmap_read(data->regmap, MAX44000_REG_CFG_TX, &regval);
> +	if (ret < 0)
> +		return ret;
> +	regval &= MAX44000_LED_CURRENT_MASK;
> +	if (regval >= 8)
> +		regval -= 4;
> +	return regval;
> +}
> +
>  static int max44000_read_raw(struct iio_dev *indio_dev,
>  			     struct iio_chan_spec const *chan,
>  			     int *val, int *val2, long mask)
> @@ -154,12 +175,26 @@ static int max44000_read_raw(struct iio_dev *indio_dev,
>  			*val = regval;
>  			return IIO_VAL_INT;
>  
> +		case IIO_CURRENT:
> +			mutex_lock(&data->lock);
> +			ret = max44000_read_led_current_raw(data);
> +			mutex_unlock(&data->lock);
> +			if (ret < 0)
> +				return ret;
> +			*val = ret;
> +			return IIO_VAL_INT;
> +
>  		default:
>  			return -EINVAL;
>  		}
>  
>  	case IIO_CHAN_INFO_SCALE:
>  		switch (chan->type) {
> +		case IIO_CURRENT:
> +			/* Output register is in 10s of miliamps */
> +			*val = 10;
> +			return IIO_VAL_INT;
> +
>  		case IIO_LIGHT:
>  			*val = 1;
>  			*val2 = MAX44000_ALS_TO_LUX_DEFAULT_FRACTION_LOG2;
> @@ -174,9 +209,27 @@ static int max44000_read_raw(struct iio_dev *indio_dev,
>  	}
>  }
>  
> +static int max44000_write_raw(struct iio_dev *indio_dev,
> +			      struct iio_chan_spec const *chan,
> +			      int val, int val2, long mask)
> +{
> +	struct max44000_data *data = iio_priv(indio_dev);
> +	int ret;
> +
> +	if (mask == IIO_CHAN_INFO_RAW && chan->type == IIO_CURRENT) {
> +		mutex_lock(&data->lock);
> +		ret = max44000_write_led_current_raw(data, val);
> +		mutex_unlock(&data->lock);
> +		return ret;
> +	}
> +
> +	return -EINVAL;
> +}
> +
>  static const struct iio_info max44000_info = {
>  	.driver_module		= THIS_MODULE,
>  	.read_raw		= max44000_read_raw,
> +	.write_raw		= max44000_write_raw,
>  };
>  
>  static bool max44000_readable_reg(struct device *dev, unsigned int reg)
> 

[toc] | [prev] | [next] | [standalone]


#1381770 — [PATCH v3 1/5] max44000: Initial support

FromCrestez Dan Leonard <leonard.crestez@intel.com>
Date2016-04-18 16:40 +0200
Subject[PATCH v3 1/5] max44000: Initial support
Message-ID<rpivh-79W-43@gated-at.bofh.it>
In reply to#1381760
This just adds support for reporting illuminance with default settings.

Important default registers are written on probe because the device
otherwise lacks a reset function.

Signed-off-by: Crestez Dan Leonard <leonard.crestez@intel.com>
---
 drivers/iio/light/Kconfig    |  11 ++
 drivers/iio/light/Makefile   |   1 +
 drivers/iio/light/max44000.c | 324 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 336 insertions(+)
 create mode 100644 drivers/iio/light/max44000.c

diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig
index 5ee1d45..d146c2c 100644
--- a/drivers/iio/light/Kconfig
+++ b/drivers/iio/light/Kconfig
@@ -234,6 +234,17 @@ config LTR501
 	 This driver can also be built as a module.  If so, the module
          will be called ltr501.
 
+config MAX44000
+	tristate "MAX44000 Ambient and Infrared Proximity Sensor"
+	depends on I2C
+	select REGMAP_I2C
+	help
+	 Say Y here if you want to build support for Maxim Integrated's
+	 MAX44000 ambient and infrared proximity sensor device.
+
+	 To compile this driver as a module, choose M here:
+	 the module will be called max44000.
+
 config OPT3001
 	tristate "Texas Instruments OPT3001 Light Sensor"
 	depends on I2C
diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile
index 4aeee2b..6e640cf 100644
--- a/drivers/iio/light/Makefile
+++ b/drivers/iio/light/Makefile
@@ -21,6 +21,7 @@ obj-$(CONFIG_ISL29125)		+= isl29125.o
 obj-$(CONFIG_JSA1212)		+= jsa1212.o
 obj-$(CONFIG_SENSORS_LM3533)	+= lm3533-als.o
 obj-$(CONFIG_LTR501)		+= ltr501.o
+obj-$(CONFIG_MAX44000)		+= max44000.o
 obj-$(CONFIG_OPT3001)		+= opt3001.o
 obj-$(CONFIG_PA12203001)	+= pa12203001.o
 obj-$(CONFIG_RPR0521)		+= rpr0521.o
diff --git a/drivers/iio/light/max44000.c b/drivers/iio/light/max44000.c
new file mode 100644
index 0000000..5ed7934
--- /dev/null
+++ b/drivers/iio/light/max44000.c
@@ -0,0 +1,324 @@
+/*
+ * MAX44000 Ambient and Infrared Proximity Sensor
+ *
+ * Copyright (c) 2016, Intel Corporation.
+ *
+ * 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
+ * directory of this archive for more details.
+ *
+ * Data sheet: https://datasheets.maximintegrated.com/en/ds/MAX44000.pdf
+ *
+ * 7-bit I2C slave address 0x4a
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/util_macros.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/acpi.h>
+
+#define MAX44000_DRV_NAME		"max44000"
+
+/* Registers in datasheet order */
+#define MAX44000_REG_STATUS		0x00
+#define MAX44000_REG_CFG_MAIN		0x01
+#define MAX44000_REG_CFG_RX		0x02
+#define MAX44000_REG_CFG_TX		0x03
+#define MAX44000_REG_ALS_DATA_HI	0x04
+#define MAX44000_REG_ALS_DATA_LO	0x05
+#define MAX44000_REG_PRX_DATA		0x16
+#define MAX44000_REG_ALS_UPTHR_HI	0x06
+#define MAX44000_REG_ALS_UPTHR_LO	0x07
+#define MAX44000_REG_ALS_LOTHR_HI	0x08
+#define MAX44000_REG_ALS_LOTHR_LO	0x09
+#define MAX44000_REG_PST		0x0a
+#define MAX44000_REG_PRX_IND		0x0b
+#define MAX44000_REG_PRX_THR		0x0c
+#define MAX44000_REG_TRIM_GAIN_GREEN	0x0f
+#define MAX44000_REG_TRIM_GAIN_IR	0x10
+
+/* REG_CFG bits */
+#define MAX44000_CFG_ALSINTE            0x01
+#define MAX44000_CFG_PRXINTE            0x02
+#define MAX44000_CFG_MASK               0x1c
+#define MAX44000_CFG_MODE_SHUTDOWN      0x00
+#define MAX44000_CFG_MODE_ALS_GIR       0x04
+#define MAX44000_CFG_MODE_ALS_G         0x08
+#define MAX44000_CFG_MODE_ALS_IR        0x0c
+#define MAX44000_CFG_MODE_ALS_PRX       0x10
+#define MAX44000_CFG_MODE_PRX           0x14
+#define MAX44000_CFG_TRIM               0x20
+
+/*
+ * Upper 4 bits are not documented but start as 1 on powerup
+ * Setting them to 0 causes proximity to misbehave so set them to 1
+ */
+#define MAX44000_REG_CFG_RX_DEFAULT 0xf0
+
+#define MAX44000_ALSDATA_OVERFLOW	0x4000
+
+struct max44000_data {
+	struct mutex lock;
+	struct regmap *regmap;
+};
+
+/* Default scale is set to the minimum of 0.03125 or 1 / (1 << 5) lux */
+#define MAX44000_ALS_TO_LUX_DEFAULT_FRACTION_LOG2 5
+
+static const struct iio_chan_spec max44000_channels[] = {
+	{
+		.type = IIO_LIGHT,
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
+	},
+};
+
+static int max44000_read_alsval(struct max44000_data *data)
+{
+	u16 regval;
+	int ret;
+
+	ret = regmap_bulk_read(data->regmap, MAX44000_REG_ALS_DATA_HI,
+			       &regval, sizeof(regval));
+	if (ret < 0)
+		return ret;
+
+	regval = be16_to_cpu(regval);
+
+	/*
+	 * Overflow is explained on datasheet page 17.
+	 *
+	 * It's a warning that either the G or IR channel has become saturated
+	 * and that the value in the register is likely incorrect.
+	 *
+	 * The recommendation is to change the scale (ALSPGA).
+	 * The driver just returns the max representable value.
+	 */
+	if (regval & MAX44000_ALSDATA_OVERFLOW)
+		return 0x3FFF;
+
+	return regval;
+}
+
+static int max44000_read_raw(struct iio_dev *indio_dev,
+			     struct iio_chan_spec const *chan,
+			     int *val, int *val2, long mask)
+{
+	struct max44000_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 = max44000_read_alsval(data);
+			mutex_unlock(&data->lock);
+			if (ret < 0)
+				return ret;
+			*val = ret;
+			return IIO_VAL_INT;
+
+		default:
+			return -EINVAL;
+		}
+
+	case IIO_CHAN_INFO_SCALE:
+		switch (chan->type) {
+		case IIO_LIGHT:
+			*val = 1;
+			*val2 = MAX44000_ALS_TO_LUX_DEFAULT_FRACTION_LOG2;
+			return IIO_VAL_FRACTIONAL_LOG2;
+
+		default:
+			return -EINVAL;
+		}
+
+	default:
+		return -EINVAL;
+	}
+}
+
+static const struct iio_info max44000_info = {
+	.driver_module		= THIS_MODULE,
+	.read_raw		= max44000_read_raw,
+};
+
+static bool max44000_readable_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case MAX44000_REG_STATUS:
+	case MAX44000_REG_CFG_MAIN:
+	case MAX44000_REG_CFG_RX:
+	case MAX44000_REG_CFG_TX:
+	case MAX44000_REG_ALS_DATA_HI:
+	case MAX44000_REG_ALS_DATA_LO:
+	case MAX44000_REG_PRX_DATA:
+	case MAX44000_REG_ALS_UPTHR_HI:
+	case MAX44000_REG_ALS_UPTHR_LO:
+	case MAX44000_REG_ALS_LOTHR_HI:
+	case MAX44000_REG_ALS_LOTHR_LO:
+	case MAX44000_REG_PST:
+	case MAX44000_REG_PRX_IND:
+	case MAX44000_REG_PRX_THR:
+	case MAX44000_REG_TRIM_GAIN_GREEN:
+	case MAX44000_REG_TRIM_GAIN_IR:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool max44000_writeable_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case MAX44000_REG_CFG_MAIN:
+	case MAX44000_REG_CFG_RX:
+	case MAX44000_REG_CFG_TX:
+	case MAX44000_REG_ALS_UPTHR_HI:
+	case MAX44000_REG_ALS_UPTHR_LO:
+	case MAX44000_REG_ALS_LOTHR_HI:
+	case MAX44000_REG_ALS_LOTHR_LO:
+	case MAX44000_REG_PST:
+	case MAX44000_REG_PRX_IND:
+	case MAX44000_REG_PRX_THR:
+	case MAX44000_REG_TRIM_GAIN_GREEN:
+	case MAX44000_REG_TRIM_GAIN_IR:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool max44000_volatile_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case MAX44000_REG_STATUS:
+	case MAX44000_REG_ALS_DATA_HI:
+	case MAX44000_REG_ALS_DATA_LO:
+	case MAX44000_REG_PRX_DATA:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool max44000_precious_reg(struct device *dev, unsigned int reg)
+{
+	return reg == MAX44000_REG_STATUS;
+}
+
+static const struct regmap_config max44000_regmap_config = {
+	.reg_bits	= 8,
+	.val_bits	= 8,
+
+	.max_register	= MAX44000_REG_PRX_DATA,
+	.readable_reg	= max44000_readable_reg,
+	.writeable_reg	= max44000_writeable_reg,
+	.volatile_reg	= max44000_volatile_reg,
+	.precious_reg	= max44000_precious_reg,
+
+	.use_single_rw	= 1,
+	.cache_type	= REGCACHE_RBTREE,
+};
+
+static int max44000_probe(struct i2c_client *client,
+			  const struct i2c_device_id *id)
+{
+	struct max44000_data *data;
+	struct iio_dev *indio_dev;
+	int ret, reg;
+
+	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+	if (!indio_dev)
+		return -ENOMEM;
+	data = iio_priv(indio_dev);
+	data->regmap = devm_regmap_init_i2c(client, &max44000_regmap_config);
+	if (IS_ERR(data->regmap)) {
+		dev_err(&client->dev, "regmap_init failed!\n");
+		return PTR_ERR(data->regmap);
+	}
+
+	i2c_set_clientdata(client, indio_dev);
+	mutex_init(&data->lock);
+	indio_dev->dev.parent = &client->dev;
+	indio_dev->info = &max44000_info;
+	indio_dev->name = MAX44000_DRV_NAME;
+	indio_dev->channels = max44000_channels;
+	indio_dev->num_channels = ARRAY_SIZE(max44000_channels);
+
+	/*
+	 * The device doesn't have a reset function so we just clear some
+	 * important bits at probe time to ensure sane operation.
+	 *
+	 * Since we don't support interrupts/events the threshold values are
+	 * not important. We also don't touch trim values.
+	 */
+
+	/* Reset ALS scaling bits */
+	ret = regmap_write(data->regmap, MAX44000_REG_CFG_RX, MAX44000_REG_CFG_RX_DEFAULT);
+	if (ret < 0) {
+		dev_err(&client->dev, "failed to write default CFG_RX: %d\n", ret);
+		return ret;
+	}
+
+	/* Reset CFG bits to ALS-only mode and no interrupts */
+	reg = MAX44000_CFG_TRIM | MAX44000_CFG_MODE_ALS_GIR;
+	ret = regmap_write(data->regmap, MAX44000_REG_CFG_MAIN, reg);
+	if (ret < 0) {
+		dev_err(&client->dev, "failed to write init config: %d\n", ret);
+		return ret;
+	}
+
+	/* Read status at least once to clear any stale interrupt bits. */
+	ret = regmap_read(data->regmap, MAX44000_REG_STATUS, &reg);
+	if (ret < 0) {
+		dev_err(&client->dev, "failed to read init status: %d\n", ret);
+		return ret;
+	}
+
+	return iio_device_register(indio_dev);
+}
+
+static int max44000_remove(struct i2c_client *client)
+{
+	struct iio_dev *indio_dev = i2c_get_clientdata(client);
+
+	iio_device_unregister(indio_dev);
+
+	return 0;
+}
+
+static const struct i2c_device_id max44000_id[] = {
+	{"max44000", 0},
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, max44000_id);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id max44000_acpi_match[] = {
+	{"MAX44000", 0},
+	{ }
+};
+MODULE_DEVICE_TABLE(acpi, max44000_acpi_match);
+#endif
+
+static struct i2c_driver max44000_driver = {
+	.driver = {
+		.name	= MAX44000_DRV_NAME,
+		.acpi_match_table = ACPI_PTR(max44000_acpi_match),
+	},
+	.probe		= max44000_probe,
+	.remove		= max44000_remove,
+	.id_table	= max44000_id,
+};
+
+module_i2c_driver(max44000_driver);
+
+MODULE_AUTHOR("Crestez Dan Leonard <leonard.crestez@intel.com>");
+MODULE_DESCRIPTION("MAX44000 Ambient and Infrared Proximity Sensor");
+MODULE_LICENSE("GPL v2");
-- 
2.5.5

[toc] | [prev] | [next] | [standalone]


#1385784 — Re: [PATCH v3 1/5] max44000: Initial support

FromJonathan Cameron <jic23@kernel.org>
Date2016-04-24 11:20 +0200
SubjectRe: [PATCH v3 1/5] max44000: Initial support
Message-ID<rromR-20P-3@gated-at.bofh.it>
In reply to#1381770
On 18/04/16 15:31, Crestez Dan Leonard wrote:
> This just adds support for reporting illuminance with default settings.
> 
> Important default registers are written on probe because the device
> otherwise lacks a reset function.
> 
> Signed-off-by: Crestez Dan Leonard <leonard.crestez@intel.com>
Looks good to me - applied to the togreg branch of iio.git
initially pushed out as testing for the autobuilders to play with it.
I did fix up a couple of overly long lines though.  They weren't cases where
breaking them effected readability so checkpatch warnings should have been
fixed.  Otherwise I end up with patches for them a few days after I push
the tree out!  Easier to fix them now.

Jonathan
> ---
>  drivers/iio/light/Kconfig    |  11 ++
>  drivers/iio/light/Makefile   |   1 +
>  drivers/iio/light/max44000.c | 324 +++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 336 insertions(+)
>  create mode 100644 drivers/iio/light/max44000.c
> 
> diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig
> index 5ee1d45..d146c2c 100644
> --- a/drivers/iio/light/Kconfig
> +++ b/drivers/iio/light/Kconfig
> @@ -234,6 +234,17 @@ config LTR501
>  	 This driver can also be built as a module.  If so, the module
>           will be called ltr501.
>  
> +config MAX44000
> +	tristate "MAX44000 Ambient and Infrared Proximity Sensor"
> +	depends on I2C
> +	select REGMAP_I2C
> +	help
> +	 Say Y here if you want to build support for Maxim Integrated's
> +	 MAX44000 ambient and infrared proximity sensor device.
> +
> +	 To compile this driver as a module, choose M here:
> +	 the module will be called max44000.
> +
>  config OPT3001
>  	tristate "Texas Instruments OPT3001 Light Sensor"
>  	depends on I2C
> diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile
> index 4aeee2b..6e640cf 100644
> --- a/drivers/iio/light/Makefile
> +++ b/drivers/iio/light/Makefile
> @@ -21,6 +21,7 @@ obj-$(CONFIG_ISL29125)		+= isl29125.o
>  obj-$(CONFIG_JSA1212)		+= jsa1212.o
>  obj-$(CONFIG_SENSORS_LM3533)	+= lm3533-als.o
>  obj-$(CONFIG_LTR501)		+= ltr501.o
> +obj-$(CONFIG_MAX44000)		+= max44000.o
>  obj-$(CONFIG_OPT3001)		+= opt3001.o
>  obj-$(CONFIG_PA12203001)	+= pa12203001.o
>  obj-$(CONFIG_RPR0521)		+= rpr0521.o
> diff --git a/drivers/iio/light/max44000.c b/drivers/iio/light/max44000.c
> new file mode 100644
> index 0000000..5ed7934
> --- /dev/null
> +++ b/drivers/iio/light/max44000.c
> @@ -0,0 +1,324 @@
> +/*
> + * MAX44000 Ambient and Infrared Proximity Sensor
> + *
> + * Copyright (c) 2016, Intel Corporation.
> + *
> + * 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
> + * directory of this archive for more details.
> + *
> + * Data sheet: https://datasheets.maximintegrated.com/en/ds/MAX44000.pdf
> + *
> + * 7-bit I2C slave address 0x4a
> + */
> +
> +#include <linux/module.h>
> +#include <linux/init.h>
> +#include <linux/i2c.h>
> +#include <linux/regmap.h>
> +#include <linux/util_macros.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/sysfs.h>
> +#include <linux/acpi.h>
> +
> +#define MAX44000_DRV_NAME		"max44000"
> +
> +/* Registers in datasheet order */
> +#define MAX44000_REG_STATUS		0x00
> +#define MAX44000_REG_CFG_MAIN		0x01
> +#define MAX44000_REG_CFG_RX		0x02
> +#define MAX44000_REG_CFG_TX		0x03
> +#define MAX44000_REG_ALS_DATA_HI	0x04
> +#define MAX44000_REG_ALS_DATA_LO	0x05
> +#define MAX44000_REG_PRX_DATA		0x16
> +#define MAX44000_REG_ALS_UPTHR_HI	0x06
> +#define MAX44000_REG_ALS_UPTHR_LO	0x07
> +#define MAX44000_REG_ALS_LOTHR_HI	0x08
> +#define MAX44000_REG_ALS_LOTHR_LO	0x09
> +#define MAX44000_REG_PST		0x0a
> +#define MAX44000_REG_PRX_IND		0x0b
> +#define MAX44000_REG_PRX_THR		0x0c
> +#define MAX44000_REG_TRIM_GAIN_GREEN	0x0f
> +#define MAX44000_REG_TRIM_GAIN_IR	0x10
> +
> +/* REG_CFG bits */
> +#define MAX44000_CFG_ALSINTE            0x01
> +#define MAX44000_CFG_PRXINTE            0x02
> +#define MAX44000_CFG_MASK               0x1c
> +#define MAX44000_CFG_MODE_SHUTDOWN      0x00
> +#define MAX44000_CFG_MODE_ALS_GIR       0x04
> +#define MAX44000_CFG_MODE_ALS_G         0x08
> +#define MAX44000_CFG_MODE_ALS_IR        0x0c
> +#define MAX44000_CFG_MODE_ALS_PRX       0x10
> +#define MAX44000_CFG_MODE_PRX           0x14
> +#define MAX44000_CFG_TRIM               0x20
> +
> +/*
> + * Upper 4 bits are not documented but start as 1 on powerup
> + * Setting them to 0 causes proximity to misbehave so set them to 1
> + */
> +#define MAX44000_REG_CFG_RX_DEFAULT 0xf0
> +
> +#define MAX44000_ALSDATA_OVERFLOW	0x4000
> +
> +struct max44000_data {
> +	struct mutex lock;
> +	struct regmap *regmap;
> +};
> +
> +/* Default scale is set to the minimum of 0.03125 or 1 / (1 << 5) lux */
> +#define MAX44000_ALS_TO_LUX_DEFAULT_FRACTION_LOG2 5
> +
> +static const struct iio_chan_spec max44000_channels[] = {
> +	{
> +		.type = IIO_LIGHT,
> +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
> +		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
> +	},
> +};
> +
> +static int max44000_read_alsval(struct max44000_data *data)
> +{
> +	u16 regval;
> +	int ret;
> +
> +	ret = regmap_bulk_read(data->regmap, MAX44000_REG_ALS_DATA_HI,
> +			       &regval, sizeof(regval));
> +	if (ret < 0)
> +		return ret;
> +
> +	regval = be16_to_cpu(regval);
> +
> +	/*
> +	 * Overflow is explained on datasheet page 17.
> +	 *
> +	 * It's a warning that either the G or IR channel has become saturated
> +	 * and that the value in the register is likely incorrect.
> +	 *
> +	 * The recommendation is to change the scale (ALSPGA).
> +	 * The driver just returns the max representable value.
> +	 */
> +	if (regval & MAX44000_ALSDATA_OVERFLOW)
> +		return 0x3FFF;
> +
> +	return regval;
> +}
> +
> +static int max44000_read_raw(struct iio_dev *indio_dev,
> +			     struct iio_chan_spec const *chan,
> +			     int *val, int *val2, long mask)
> +{
> +	struct max44000_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 = max44000_read_alsval(data);
> +			mutex_unlock(&data->lock);
> +			if (ret < 0)
> +				return ret;
> +			*val = ret;
> +			return IIO_VAL_INT;
> +
> +		default:
> +			return -EINVAL;
> +		}
> +
> +	case IIO_CHAN_INFO_SCALE:
> +		switch (chan->type) {
> +		case IIO_LIGHT:
> +			*val = 1;
> +			*val2 = MAX44000_ALS_TO_LUX_DEFAULT_FRACTION_LOG2;
> +			return IIO_VAL_FRACTIONAL_LOG2;
> +
> +		default:
> +			return -EINVAL;
> +		}
> +
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static const struct iio_info max44000_info = {
> +	.driver_module		= THIS_MODULE,
> +	.read_raw		= max44000_read_raw,
> +};
> +
> +static bool max44000_readable_reg(struct device *dev, unsigned int reg)
> +{
> +	switch (reg) {
> +	case MAX44000_REG_STATUS:
> +	case MAX44000_REG_CFG_MAIN:
> +	case MAX44000_REG_CFG_RX:
> +	case MAX44000_REG_CFG_TX:
> +	case MAX44000_REG_ALS_DATA_HI:
> +	case MAX44000_REG_ALS_DATA_LO:
> +	case MAX44000_REG_PRX_DATA:
> +	case MAX44000_REG_ALS_UPTHR_HI:
> +	case MAX44000_REG_ALS_UPTHR_LO:
> +	case MAX44000_REG_ALS_LOTHR_HI:
> +	case MAX44000_REG_ALS_LOTHR_LO:
> +	case MAX44000_REG_PST:
> +	case MAX44000_REG_PRX_IND:
> +	case MAX44000_REG_PRX_THR:
> +	case MAX44000_REG_TRIM_GAIN_GREEN:
> +	case MAX44000_REG_TRIM_GAIN_IR:
> +		return true;
> +	default:
> +		return false;
> +	}
> +}
> +
> +static bool max44000_writeable_reg(struct device *dev, unsigned int reg)
> +{
> +	switch (reg) {
> +	case MAX44000_REG_CFG_MAIN:
> +	case MAX44000_REG_CFG_RX:
> +	case MAX44000_REG_CFG_TX:
> +	case MAX44000_REG_ALS_UPTHR_HI:
> +	case MAX44000_REG_ALS_UPTHR_LO:
> +	case MAX44000_REG_ALS_LOTHR_HI:
> +	case MAX44000_REG_ALS_LOTHR_LO:
> +	case MAX44000_REG_PST:
> +	case MAX44000_REG_PRX_IND:
> +	case MAX44000_REG_PRX_THR:
> +	case MAX44000_REG_TRIM_GAIN_GREEN:
> +	case MAX44000_REG_TRIM_GAIN_IR:
> +		return true;
> +	default:
> +		return false;
> +	}
> +}
> +
> +static bool max44000_volatile_reg(struct device *dev, unsigned int reg)
> +{
> +	switch (reg) {
> +	case MAX44000_REG_STATUS:
> +	case MAX44000_REG_ALS_DATA_HI:
> +	case MAX44000_REG_ALS_DATA_LO:
> +	case MAX44000_REG_PRX_DATA:
> +		return true;
> +	default:
> +		return false;
> +	}
> +}
> +
> +static bool max44000_precious_reg(struct device *dev, unsigned int reg)
> +{
> +	return reg == MAX44000_REG_STATUS;
> +}
> +
> +static const struct regmap_config max44000_regmap_config = {
> +	.reg_bits	= 8,
> +	.val_bits	= 8,
> +
> +	.max_register	= MAX44000_REG_PRX_DATA,
> +	.readable_reg	= max44000_readable_reg,
> +	.writeable_reg	= max44000_writeable_reg,
> +	.volatile_reg	= max44000_volatile_reg,
> +	.precious_reg	= max44000_precious_reg,
> +
> +	.use_single_rw	= 1,
> +	.cache_type	= REGCACHE_RBTREE,
> +};
> +
> +static int max44000_probe(struct i2c_client *client,
> +			  const struct i2c_device_id *id)
> +{
> +	struct max44000_data *data;
> +	struct iio_dev *indio_dev;
> +	int ret, reg;
> +
> +	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
> +	if (!indio_dev)
> +		return -ENOMEM;
> +	data = iio_priv(indio_dev);
> +	data->regmap = devm_regmap_init_i2c(client, &max44000_regmap_config);
> +	if (IS_ERR(data->regmap)) {
> +		dev_err(&client->dev, "regmap_init failed!\n");
> +		return PTR_ERR(data->regmap);
> +	}
> +
> +	i2c_set_clientdata(client, indio_dev);
> +	mutex_init(&data->lock);
> +	indio_dev->dev.parent = &client->dev;
> +	indio_dev->info = &max44000_info;
> +	indio_dev->name = MAX44000_DRV_NAME;
> +	indio_dev->channels = max44000_channels;
> +	indio_dev->num_channels = ARRAY_SIZE(max44000_channels);
> +
> +	/*
> +	 * The device doesn't have a reset function so we just clear some
> +	 * important bits at probe time to ensure sane operation.
> +	 *
> +	 * Since we don't support interrupts/events the threshold values are
> +	 * not important. We also don't touch trim values.
> +	 */
> +
> +	/* Reset ALS scaling bits */
> +	ret = regmap_write(data->regmap, MAX44000_REG_CFG_RX, MAX44000_REG_CFG_RX_DEFAULT);
> +	if (ret < 0) {
> +		dev_err(&client->dev, "failed to write default CFG_RX: %d\n", ret);
> +		return ret;
> +	}
> +
> +	/* Reset CFG bits to ALS-only mode and no interrupts */
> +	reg = MAX44000_CFG_TRIM | MAX44000_CFG_MODE_ALS_GIR;
> +	ret = regmap_write(data->regmap, MAX44000_REG_CFG_MAIN, reg);
> +	if (ret < 0) {
> +		dev_err(&client->dev, "failed to write init config: %d\n", ret);
> +		return ret;
> +	}
> +
> +	/* Read status at least once to clear any stale interrupt bits. */
> +	ret = regmap_read(data->regmap, MAX44000_REG_STATUS, &reg);
> +	if (ret < 0) {
> +		dev_err(&client->dev, "failed to read init status: %d\n", ret);
> +		return ret;
> +	}
> +
> +	return iio_device_register(indio_dev);
> +}
> +
> +static int max44000_remove(struct i2c_client *client)
> +{
> +	struct iio_dev *indio_dev = i2c_get_clientdata(client);
> +
> +	iio_device_unregister(indio_dev);
> +
> +	return 0;
> +}
> +
> +static const struct i2c_device_id max44000_id[] = {
> +	{"max44000", 0},
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(i2c, max44000_id);
> +
> +#ifdef CONFIG_ACPI
> +static const struct acpi_device_id max44000_acpi_match[] = {
> +	{"MAX44000", 0},
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(acpi, max44000_acpi_match);
> +#endif
> +
> +static struct i2c_driver max44000_driver = {
> +	.driver = {
> +		.name	= MAX44000_DRV_NAME,
> +		.acpi_match_table = ACPI_PTR(max44000_acpi_match),
> +	},
> +	.probe		= max44000_probe,
> +	.remove		= max44000_remove,
> +	.id_table	= max44000_id,
> +};
> +
> +module_i2c_driver(max44000_driver);
> +
> +MODULE_AUTHOR("Crestez Dan Leonard <leonard.crestez@intel.com>");
> +MODULE_DESCRIPTION("MAX44000 Ambient and Infrared Proximity Sensor");
> +MODULE_LICENSE("GPL v2");
> 

[toc] | [prev] | [standalone]


Back to top | Article view | linux.kernel


csiph-web