123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929 |
- /*
- * lm3533-als.c -- LM3533 Ambient Light Sensor driver
- *
- * Copyright (C) 2011-2012 Texas Instruments
- *
- * Author: Johan Hovold <jhovold@gmail.com>
- *
- * 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 <linux/atomic.h>
- #include <linux/fs.h>
- #include <linux/interrupt.h>
- #include <linux/io.h>
- #include <linux/iio/events.h>
- #include <linux/iio/iio.h>
- #include <linux/module.h>
- #include <linux/mutex.h>
- #include <linux/mfd/core.h>
- #include <linux/platform_device.h>
- #include <linux/slab.h>
- #include <linux/uaccess.h>
- #include <linux/mfd/lm3533.h>
- #define LM3533_ALS_RESISTOR_MIN 1
- #define LM3533_ALS_RESISTOR_MAX 127
- #define LM3533_ALS_CHANNEL_CURRENT_MAX 2
- #define LM3533_ALS_THRESH_MAX 3
- #define LM3533_ALS_ZONE_MAX 4
- #define LM3533_REG_ALS_RESISTOR_SELECT 0x30
- #define LM3533_REG_ALS_CONF 0x31
- #define LM3533_REG_ALS_ZONE_INFO 0x34
- #define LM3533_REG_ALS_READ_ADC_RAW 0x37
- #define LM3533_REG_ALS_READ_ADC_AVERAGE 0x38
- #define LM3533_REG_ALS_BOUNDARY_BASE 0x50
- #define LM3533_REG_ALS_TARGET_BASE 0x60
- #define LM3533_ALS_ENABLE_MASK 0x01
- #define LM3533_ALS_INPUT_MODE_MASK 0x02
- #define LM3533_ALS_INT_ENABLE_MASK 0x01
- #define LM3533_ALS_ZONE_SHIFT 2
- #define LM3533_ALS_ZONE_MASK 0x1c
- #define LM3533_ALS_FLAG_INT_ENABLED 1
- struct lm3533_als {
- struct lm3533 *lm3533;
- struct platform_device *pdev;
- unsigned long flags;
- int irq;
- atomic_t zone;
- struct mutex thresh_mutex;
- };
- static int lm3533_als_get_adc(struct iio_dev *indio_dev, bool average,
- int *adc)
- {
- struct lm3533_als *als = iio_priv(indio_dev);
- u8 reg;
- u8 val;
- int ret;
- if (average)
- reg = LM3533_REG_ALS_READ_ADC_AVERAGE;
- else
- reg = LM3533_REG_ALS_READ_ADC_RAW;
- ret = lm3533_read(als->lm3533, reg, &val);
- if (ret) {
- dev_err(&indio_dev->dev, "failed to read adc\n");
- return ret;
- }
- *adc = val;
- return 0;
- }
- static int _lm3533_als_get_zone(struct iio_dev *indio_dev, u8 *zone)
- {
- struct lm3533_als *als = iio_priv(indio_dev);
- u8 val;
- int ret;
- ret = lm3533_read(als->lm3533, LM3533_REG_ALS_ZONE_INFO, &val);
- if (ret) {
- dev_err(&indio_dev->dev, "failed to read zone\n");
- return ret;
- }
- val = (val & LM3533_ALS_ZONE_MASK) >> LM3533_ALS_ZONE_SHIFT;
- *zone = min_t(u8, val, LM3533_ALS_ZONE_MAX);
- return 0;
- }
- static int lm3533_als_get_zone(struct iio_dev *indio_dev, u8 *zone)
- {
- struct lm3533_als *als = iio_priv(indio_dev);
- int ret;
- if (test_bit(LM3533_ALS_FLAG_INT_ENABLED, &als->flags)) {
- *zone = atomic_read(&als->zone);
- } else {
- ret = _lm3533_als_get_zone(indio_dev, zone);
- if (ret)
- return ret;
- }
- return 0;
- }
- /*
- * channel output channel 0..2
- * zone zone 0..4
- */
- static inline u8 lm3533_als_get_target_reg(unsigned channel, unsigned zone)
- {
- return LM3533_REG_ALS_TARGET_BASE + 5 * channel + zone;
- }
- static int lm3533_als_get_target(struct iio_dev *indio_dev, unsigned channel,
- unsigned zone, u8 *val)
- {
- struct lm3533_als *als = iio_priv(indio_dev);
- u8 reg;
- int ret;
- if (channel > LM3533_ALS_CHANNEL_CURRENT_MAX)
- return -EINVAL;
- if (zone > LM3533_ALS_ZONE_MAX)
- return -EINVAL;
- reg = lm3533_als_get_target_reg(channel, zone);
- ret = lm3533_read(als->lm3533, reg, val);
- if (ret)
- dev_err(&indio_dev->dev, "failed to get target current\n");
- return ret;
- }
- static int lm3533_als_set_target(struct iio_dev *indio_dev, unsigned channel,
- unsigned zone, u8 val)
- {
- struct lm3533_als *als = iio_priv(indio_dev);
- u8 reg;
- int ret;
- if (channel > LM3533_ALS_CHANNEL_CURRENT_MAX)
- return -EINVAL;
- if (zone > LM3533_ALS_ZONE_MAX)
- return -EINVAL;
- reg = lm3533_als_get_target_reg(channel, zone);
- ret = lm3533_write(als->lm3533, reg, val);
- if (ret)
- dev_err(&indio_dev->dev, "failed to set target current\n");
- return ret;
- }
- static int lm3533_als_get_current(struct iio_dev *indio_dev, unsigned channel,
- int *val)
- {
- u8 zone;
- u8 target;
- int ret;
- ret = lm3533_als_get_zone(indio_dev, &zone);
- if (ret)
- return ret;
- ret = lm3533_als_get_target(indio_dev, channel, zone, &target);
- if (ret)
- return ret;
- *val = target;
- return 0;
- }
- static int lm3533_als_read_raw(struct iio_dev *indio_dev,
- struct iio_chan_spec const *chan,
- int *val, int *val2, long mask)
- {
- int ret;
- switch (mask) {
- case IIO_CHAN_INFO_RAW:
- switch (chan->type) {
- case IIO_LIGHT:
- ret = lm3533_als_get_adc(indio_dev, false, val);
- break;
- case IIO_CURRENT:
- ret = lm3533_als_get_current(indio_dev, chan->channel,
- val);
- break;
- default:
- return -EINVAL;
- }
- break;
- case IIO_CHAN_INFO_AVERAGE_RAW:
- ret = lm3533_als_get_adc(indio_dev, true, val);
- break;
- default:
- return -EINVAL;
- }
- if (ret)
- return ret;
- return IIO_VAL_INT;
- }
- #define CHANNEL_CURRENT(_channel) \
- { \
- .type = IIO_CURRENT, \
- .channel = _channel, \
- .indexed = true, \
- .output = true, \
- .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
- }
- static const struct iio_chan_spec lm3533_als_channels[] = {
- {
- .type = IIO_LIGHT,
- .channel = 0,
- .indexed = true,
- .info_mask_separate = BIT(IIO_CHAN_INFO_AVERAGE_RAW) |
- BIT(IIO_CHAN_INFO_RAW),
- },
- CHANNEL_CURRENT(0),
- CHANNEL_CURRENT(1),
- CHANNEL_CURRENT(2),
- };
- static irqreturn_t lm3533_als_isr(int irq, void *dev_id)
- {
- struct iio_dev *indio_dev = dev_id;
- struct lm3533_als *als = iio_priv(indio_dev);
- u8 zone;
- int ret;
- /* Clear interrupt by reading the ALS zone register. */
- ret = _lm3533_als_get_zone(indio_dev, &zone);
- if (ret)
- goto out;
- atomic_set(&als->zone, zone);
- iio_push_event(indio_dev,
- IIO_UNMOD_EVENT_CODE(IIO_LIGHT,
- 0,
- IIO_EV_TYPE_THRESH,
- IIO_EV_DIR_EITHER),
- iio_get_time_ns(indio_dev));
- out:
- return IRQ_HANDLED;
- }
- static int lm3533_als_set_int_mode(struct iio_dev *indio_dev, int enable)
- {
- struct lm3533_als *als = iio_priv(indio_dev);
- u8 mask = LM3533_ALS_INT_ENABLE_MASK;
- u8 val;
- int ret;
- if (enable)
- val = mask;
- else
- val = 0;
- ret = lm3533_update(als->lm3533, LM3533_REG_ALS_ZONE_INFO, val, mask);
- if (ret) {
- dev_err(&indio_dev->dev, "failed to set int mode %d\n",
- enable);
- return ret;
- }
- return 0;
- }
- static int lm3533_als_get_int_mode(struct iio_dev *indio_dev, int *enable)
- {
- struct lm3533_als *als = iio_priv(indio_dev);
- u8 mask = LM3533_ALS_INT_ENABLE_MASK;
- u8 val;
- int ret;
- ret = lm3533_read(als->lm3533, LM3533_REG_ALS_ZONE_INFO, &val);
- if (ret) {
- dev_err(&indio_dev->dev, "failed to get int mode\n");
- return ret;
- }
- *enable = !!(val & mask);
- return 0;
- }
- static inline u8 lm3533_als_get_threshold_reg(unsigned nr, bool raising)
- {
- u8 offset = !raising;
- return LM3533_REG_ALS_BOUNDARY_BASE + 2 * nr + offset;
- }
- static int lm3533_als_get_threshold(struct iio_dev *indio_dev, unsigned nr,
- bool raising, u8 *val)
- {
- struct lm3533_als *als = iio_priv(indio_dev);
- u8 reg;
- int ret;
- if (nr > LM3533_ALS_THRESH_MAX)
- return -EINVAL;
- reg = lm3533_als_get_threshold_reg(nr, raising);
- ret = lm3533_read(als->lm3533, reg, val);
- if (ret)
- dev_err(&indio_dev->dev, "failed to get threshold\n");
- return ret;
- }
- static int lm3533_als_set_threshold(struct iio_dev *indio_dev, unsigned nr,
- bool raising, u8 val)
- {
- struct lm3533_als *als = iio_priv(indio_dev);
- u8 val2;
- u8 reg, reg2;
- int ret;
- if (nr > LM3533_ALS_THRESH_MAX)
- return -EINVAL;
- reg = lm3533_als_get_threshold_reg(nr, raising);
- reg2 = lm3533_als_get_threshold_reg(nr, !raising);
- mutex_lock(&als->thresh_mutex);
- ret = lm3533_read(als->lm3533, reg2, &val2);
- if (ret) {
- dev_err(&indio_dev->dev, "failed to get threshold\n");
- goto out;
- }
- /*
- * This device does not allow negative hysteresis (in fact, it uses
- * whichever value is smaller as the lower bound) so we need to make
- * sure that thresh_falling <= thresh_raising.
- */
- if ((raising && (val < val2)) || (!raising && (val > val2))) {
- ret = -EINVAL;
- goto out;
- }
- ret = lm3533_write(als->lm3533, reg, val);
- if (ret) {
- dev_err(&indio_dev->dev, "failed to set threshold\n");
- goto out;
- }
- out:
- mutex_unlock(&als->thresh_mutex);
- return ret;
- }
- static int lm3533_als_get_hysteresis(struct iio_dev *indio_dev, unsigned nr,
- u8 *val)
- {
- struct lm3533_als *als = iio_priv(indio_dev);
- u8 falling;
- u8 raising;
- int ret;
- if (nr > LM3533_ALS_THRESH_MAX)
- return -EINVAL;
- mutex_lock(&als->thresh_mutex);
- ret = lm3533_als_get_threshold(indio_dev, nr, false, &falling);
- if (ret)
- goto out;
- ret = lm3533_als_get_threshold(indio_dev, nr, true, &raising);
- if (ret)
- goto out;
- *val = raising - falling;
- out:
- mutex_unlock(&als->thresh_mutex);
- return ret;
- }
- static ssize_t show_thresh_either_en(struct device *dev,
- struct device_attribute *attr,
- char *buf)
- {
- struct iio_dev *indio_dev = dev_to_iio_dev(dev);
- struct lm3533_als *als = iio_priv(indio_dev);
- int enable;
- int ret;
- if (als->irq) {
- ret = lm3533_als_get_int_mode(indio_dev, &enable);
- if (ret)
- return ret;
- } else {
- enable = 0;
- }
- return scnprintf(buf, PAGE_SIZE, "%u\n", enable);
- }
- static ssize_t store_thresh_either_en(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t len)
- {
- struct iio_dev *indio_dev = dev_to_iio_dev(dev);
- struct lm3533_als *als = iio_priv(indio_dev);
- unsigned long enable;
- bool int_enabled;
- u8 zone;
- int ret;
- if (!als->irq)
- return -EBUSY;
- if (kstrtoul(buf, 0, &enable))
- return -EINVAL;
- int_enabled = test_bit(LM3533_ALS_FLAG_INT_ENABLED, &als->flags);
- if (enable && !int_enabled) {
- ret = lm3533_als_get_zone(indio_dev, &zone);
- if (ret)
- return ret;
- atomic_set(&als->zone, zone);
- set_bit(LM3533_ALS_FLAG_INT_ENABLED, &als->flags);
- }
- ret = lm3533_als_set_int_mode(indio_dev, enable);
- if (ret) {
- if (!int_enabled)
- clear_bit(LM3533_ALS_FLAG_INT_ENABLED, &als->flags);
- return ret;
- }
- if (!enable)
- clear_bit(LM3533_ALS_FLAG_INT_ENABLED, &als->flags);
- return len;
- }
- static ssize_t show_zone(struct device *dev,
- struct device_attribute *attr, char *buf)
- {
- struct iio_dev *indio_dev = dev_to_iio_dev(dev);
- u8 zone;
- int ret;
- ret = lm3533_als_get_zone(indio_dev, &zone);
- if (ret)
- return ret;
- return scnprintf(buf, PAGE_SIZE, "%u\n", zone);
- }
- enum lm3533_als_attribute_type {
- LM3533_ATTR_TYPE_HYSTERESIS,
- LM3533_ATTR_TYPE_TARGET,
- LM3533_ATTR_TYPE_THRESH_FALLING,
- LM3533_ATTR_TYPE_THRESH_RAISING,
- };
- struct lm3533_als_attribute {
- struct device_attribute dev_attr;
- enum lm3533_als_attribute_type type;
- u8 val1;
- u8 val2;
- };
- static inline struct lm3533_als_attribute *
- to_lm3533_als_attr(struct device_attribute *attr)
- {
- return container_of(attr, struct lm3533_als_attribute, dev_attr);
- }
- static ssize_t show_als_attr(struct device *dev,
- struct device_attribute *attr,
- char *buf)
- {
- struct iio_dev *indio_dev = dev_to_iio_dev(dev);
- struct lm3533_als_attribute *als_attr = to_lm3533_als_attr(attr);
- u8 val;
- int ret;
- switch (als_attr->type) {
- case LM3533_ATTR_TYPE_HYSTERESIS:
- ret = lm3533_als_get_hysteresis(indio_dev, als_attr->val1,
- &val);
- break;
- case LM3533_ATTR_TYPE_TARGET:
- ret = lm3533_als_get_target(indio_dev, als_attr->val1,
- als_attr->val2, &val);
- break;
- case LM3533_ATTR_TYPE_THRESH_FALLING:
- ret = lm3533_als_get_threshold(indio_dev, als_attr->val1,
- false, &val);
- break;
- case LM3533_ATTR_TYPE_THRESH_RAISING:
- ret = lm3533_als_get_threshold(indio_dev, als_attr->val1,
- true, &val);
- break;
- default:
- ret = -ENXIO;
- }
- if (ret)
- return ret;
- return scnprintf(buf, PAGE_SIZE, "%u\n", val);
- }
- static ssize_t store_als_attr(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t len)
- {
- struct iio_dev *indio_dev = dev_to_iio_dev(dev);
- struct lm3533_als_attribute *als_attr = to_lm3533_als_attr(attr);
- u8 val;
- int ret;
- if (kstrtou8(buf, 0, &val))
- return -EINVAL;
- switch (als_attr->type) {
- case LM3533_ATTR_TYPE_TARGET:
- ret = lm3533_als_set_target(indio_dev, als_attr->val1,
- als_attr->val2, val);
- break;
- case LM3533_ATTR_TYPE_THRESH_FALLING:
- ret = lm3533_als_set_threshold(indio_dev, als_attr->val1,
- false, val);
- break;
- case LM3533_ATTR_TYPE_THRESH_RAISING:
- ret = lm3533_als_set_threshold(indio_dev, als_attr->val1,
- true, val);
- break;
- default:
- ret = -ENXIO;
- }
- if (ret)
- return ret;
- return len;
- }
- #define ALS_ATTR(_name, _mode, _show, _store, _type, _val1, _val2) \
- { .dev_attr = __ATTR(_name, _mode, _show, _store), \
- .type = _type, \
- .val1 = _val1, \
- .val2 = _val2 }
- #define LM3533_ALS_ATTR(_name, _mode, _show, _store, _type, _val1, _val2) \
- struct lm3533_als_attribute lm3533_als_attr_##_name = \
- ALS_ATTR(_name, _mode, _show, _store, _type, _val1, _val2)
- #define ALS_TARGET_ATTR_RW(_channel, _zone) \
- LM3533_ALS_ATTR(out_current##_channel##_current##_zone##_raw, \
- S_IRUGO | S_IWUSR, \
- show_als_attr, store_als_attr, \
- LM3533_ATTR_TYPE_TARGET, _channel, _zone)
- /*
- * ALS output current values (ALS mapper targets)
- *
- * out_current[0-2]_current[0-4]_raw 0-255
- */
- static ALS_TARGET_ATTR_RW(0, 0);
- static ALS_TARGET_ATTR_RW(0, 1);
- static ALS_TARGET_ATTR_RW(0, 2);
- static ALS_TARGET_ATTR_RW(0, 3);
- static ALS_TARGET_ATTR_RW(0, 4);
- static ALS_TARGET_ATTR_RW(1, 0);
- static ALS_TARGET_ATTR_RW(1, 1);
- static ALS_TARGET_ATTR_RW(1, 2);
- static ALS_TARGET_ATTR_RW(1, 3);
- static ALS_TARGET_ATTR_RW(1, 4);
- static ALS_TARGET_ATTR_RW(2, 0);
- static ALS_TARGET_ATTR_RW(2, 1);
- static ALS_TARGET_ATTR_RW(2, 2);
- static ALS_TARGET_ATTR_RW(2, 3);
- static ALS_TARGET_ATTR_RW(2, 4);
- #define ALS_THRESH_FALLING_ATTR_RW(_nr) \
- LM3533_ALS_ATTR(in_illuminance0_thresh##_nr##_falling_value, \
- S_IRUGO | S_IWUSR, \
- show_als_attr, store_als_attr, \
- LM3533_ATTR_TYPE_THRESH_FALLING, _nr, 0)
- #define ALS_THRESH_RAISING_ATTR_RW(_nr) \
- LM3533_ALS_ATTR(in_illuminance0_thresh##_nr##_raising_value, \
- S_IRUGO | S_IWUSR, \
- show_als_attr, store_als_attr, \
- LM3533_ATTR_TYPE_THRESH_RAISING, _nr, 0)
- /*
- * ALS Zone thresholds (boundaries)
- *
- * in_illuminance0_thresh[0-3]_falling_value 0-255
- * in_illuminance0_thresh[0-3]_raising_value 0-255
- */
- static ALS_THRESH_FALLING_ATTR_RW(0);
- static ALS_THRESH_FALLING_ATTR_RW(1);
- static ALS_THRESH_FALLING_ATTR_RW(2);
- static ALS_THRESH_FALLING_ATTR_RW(3);
- static ALS_THRESH_RAISING_ATTR_RW(0);
- static ALS_THRESH_RAISING_ATTR_RW(1);
- static ALS_THRESH_RAISING_ATTR_RW(2);
- static ALS_THRESH_RAISING_ATTR_RW(3);
- #define ALS_HYSTERESIS_ATTR_RO(_nr) \
- LM3533_ALS_ATTR(in_illuminance0_thresh##_nr##_hysteresis, \
- S_IRUGO, show_als_attr, NULL, \
- LM3533_ATTR_TYPE_HYSTERESIS, _nr, 0)
- /*
- * ALS Zone threshold hysteresis
- *
- * threshY_hysteresis = threshY_raising - threshY_falling
- *
- * in_illuminance0_thresh[0-3]_hysteresis 0-255
- * in_illuminance0_thresh[0-3]_hysteresis 0-255
- */
- static ALS_HYSTERESIS_ATTR_RO(0);
- static ALS_HYSTERESIS_ATTR_RO(1);
- static ALS_HYSTERESIS_ATTR_RO(2);
- static ALS_HYSTERESIS_ATTR_RO(3);
- #define ILLUMINANCE_ATTR_RO(_name) \
- DEVICE_ATTR(in_illuminance0_##_name, S_IRUGO, show_##_name, NULL)
- #define ILLUMINANCE_ATTR_RW(_name) \
- DEVICE_ATTR(in_illuminance0_##_name, S_IRUGO | S_IWUSR, \
- show_##_name, store_##_name)
- /*
- * ALS Zone threshold-event enable
- *
- * in_illuminance0_thresh_either_en 0,1
- */
- static ILLUMINANCE_ATTR_RW(thresh_either_en);
- /*
- * ALS Current Zone
- *
- * in_illuminance0_zone 0-4
- */
- static ILLUMINANCE_ATTR_RO(zone);
- static struct attribute *lm3533_als_event_attributes[] = {
- &dev_attr_in_illuminance0_thresh_either_en.attr,
- &lm3533_als_attr_in_illuminance0_thresh0_falling_value.dev_attr.attr,
- &lm3533_als_attr_in_illuminance0_thresh0_hysteresis.dev_attr.attr,
- &lm3533_als_attr_in_illuminance0_thresh0_raising_value.dev_attr.attr,
- &lm3533_als_attr_in_illuminance0_thresh1_falling_value.dev_attr.attr,
- &lm3533_als_attr_in_illuminance0_thresh1_hysteresis.dev_attr.attr,
- &lm3533_als_attr_in_illuminance0_thresh1_raising_value.dev_attr.attr,
- &lm3533_als_attr_in_illuminance0_thresh2_falling_value.dev_attr.attr,
- &lm3533_als_attr_in_illuminance0_thresh2_hysteresis.dev_attr.attr,
- &lm3533_als_attr_in_illuminance0_thresh2_raising_value.dev_attr.attr,
- &lm3533_als_attr_in_illuminance0_thresh3_falling_value.dev_attr.attr,
- &lm3533_als_attr_in_illuminance0_thresh3_hysteresis.dev_attr.attr,
- &lm3533_als_attr_in_illuminance0_thresh3_raising_value.dev_attr.attr,
- NULL
- };
- static const struct attribute_group lm3533_als_event_attribute_group = {
- .attrs = lm3533_als_event_attributes
- };
- static struct attribute *lm3533_als_attributes[] = {
- &dev_attr_in_illuminance0_zone.attr,
- &lm3533_als_attr_out_current0_current0_raw.dev_attr.attr,
- &lm3533_als_attr_out_current0_current1_raw.dev_attr.attr,
- &lm3533_als_attr_out_current0_current2_raw.dev_attr.attr,
- &lm3533_als_attr_out_current0_current3_raw.dev_attr.attr,
- &lm3533_als_attr_out_current0_current4_raw.dev_attr.attr,
- &lm3533_als_attr_out_current1_current0_raw.dev_attr.attr,
- &lm3533_als_attr_out_current1_current1_raw.dev_attr.attr,
- &lm3533_als_attr_out_current1_current2_raw.dev_attr.attr,
- &lm3533_als_attr_out_current1_current3_raw.dev_attr.attr,
- &lm3533_als_attr_out_current1_current4_raw.dev_attr.attr,
- &lm3533_als_attr_out_current2_current0_raw.dev_attr.attr,
- &lm3533_als_attr_out_current2_current1_raw.dev_attr.attr,
- &lm3533_als_attr_out_current2_current2_raw.dev_attr.attr,
- &lm3533_als_attr_out_current2_current3_raw.dev_attr.attr,
- &lm3533_als_attr_out_current2_current4_raw.dev_attr.attr,
- NULL
- };
- static const struct attribute_group lm3533_als_attribute_group = {
- .attrs = lm3533_als_attributes
- };
- static int lm3533_als_set_input_mode(struct lm3533_als *als, bool pwm_mode)
- {
- u8 mask = LM3533_ALS_INPUT_MODE_MASK;
- u8 val;
- int ret;
- if (pwm_mode)
- val = mask; /* pwm input */
- else
- val = 0; /* analog input */
- ret = lm3533_update(als->lm3533, LM3533_REG_ALS_CONF, val, mask);
- if (ret) {
- dev_err(&als->pdev->dev, "failed to set input mode %d\n",
- pwm_mode);
- return ret;
- }
- return 0;
- }
- static int lm3533_als_set_resistor(struct lm3533_als *als, u8 val)
- {
- int ret;
- if (val < LM3533_ALS_RESISTOR_MIN || val > LM3533_ALS_RESISTOR_MAX) {
- dev_err(&als->pdev->dev, "invalid resistor value\n");
- return -EINVAL;
- };
- ret = lm3533_write(als->lm3533, LM3533_REG_ALS_RESISTOR_SELECT, val);
- if (ret) {
- dev_err(&als->pdev->dev, "failed to set resistor\n");
- return ret;
- }
- return 0;
- }
- static int lm3533_als_setup(struct lm3533_als *als,
- struct lm3533_als_platform_data *pdata)
- {
- int ret;
- ret = lm3533_als_set_input_mode(als, pdata->pwm_mode);
- if (ret)
- return ret;
- /* ALS input is always high impedance in PWM-mode. */
- if (!pdata->pwm_mode) {
- ret = lm3533_als_set_resistor(als, pdata->r_select);
- if (ret)
- return ret;
- }
- return 0;
- }
- static int lm3533_als_setup_irq(struct lm3533_als *als, void *dev)
- {
- u8 mask = LM3533_ALS_INT_ENABLE_MASK;
- int ret;
- /* Make sure interrupts are disabled. */
- ret = lm3533_update(als->lm3533, LM3533_REG_ALS_ZONE_INFO, 0, mask);
- if (ret) {
- dev_err(&als->pdev->dev, "failed to disable interrupts\n");
- return ret;
- }
- ret = request_threaded_irq(als->irq, NULL, lm3533_als_isr,
- IRQF_TRIGGER_LOW | IRQF_ONESHOT,
- dev_name(&als->pdev->dev), dev);
- if (ret) {
- dev_err(&als->pdev->dev, "failed to request irq %d\n",
- als->irq);
- return ret;
- }
- return 0;
- }
- static int lm3533_als_enable(struct lm3533_als *als)
- {
- u8 mask = LM3533_ALS_ENABLE_MASK;
- int ret;
- ret = lm3533_update(als->lm3533, LM3533_REG_ALS_CONF, mask, mask);
- if (ret)
- dev_err(&als->pdev->dev, "failed to enable ALS\n");
- return ret;
- }
- static int lm3533_als_disable(struct lm3533_als *als)
- {
- u8 mask = LM3533_ALS_ENABLE_MASK;
- int ret;
- ret = lm3533_update(als->lm3533, LM3533_REG_ALS_CONF, 0, mask);
- if (ret)
- dev_err(&als->pdev->dev, "failed to disable ALS\n");
- return ret;
- }
- static const struct iio_info lm3533_als_info = {
- .attrs = &lm3533_als_attribute_group,
- .event_attrs = &lm3533_als_event_attribute_group,
- .read_raw = &lm3533_als_read_raw,
- };
- static int lm3533_als_probe(struct platform_device *pdev)
- {
- struct lm3533 *lm3533;
- struct lm3533_als_platform_data *pdata;
- struct lm3533_als *als;
- struct iio_dev *indio_dev;
- int ret;
- lm3533 = dev_get_drvdata(pdev->dev.parent);
- if (!lm3533)
- return -EINVAL;
- pdata = pdev->dev.platform_data;
- if (!pdata) {
- dev_err(&pdev->dev, "no platform data\n");
- return -EINVAL;
- }
- indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*als));
- if (!indio_dev)
- return -ENOMEM;
- indio_dev->info = &lm3533_als_info;
- indio_dev->channels = lm3533_als_channels;
- indio_dev->num_channels = ARRAY_SIZE(lm3533_als_channels);
- indio_dev->name = dev_name(&pdev->dev);
- indio_dev->dev.parent = pdev->dev.parent;
- indio_dev->modes = INDIO_DIRECT_MODE;
- als = iio_priv(indio_dev);
- als->lm3533 = lm3533;
- als->pdev = pdev;
- als->irq = lm3533->irq;
- atomic_set(&als->zone, 0);
- mutex_init(&als->thresh_mutex);
- platform_set_drvdata(pdev, indio_dev);
- if (als->irq) {
- ret = lm3533_als_setup_irq(als, indio_dev);
- if (ret)
- return ret;
- }
- ret = lm3533_als_setup(als, pdata);
- if (ret)
- goto err_free_irq;
- ret = lm3533_als_enable(als);
- if (ret)
- goto err_free_irq;
- ret = iio_device_register(indio_dev);
- if (ret) {
- dev_err(&pdev->dev, "failed to register ALS\n");
- goto err_disable;
- }
- return 0;
- err_disable:
- lm3533_als_disable(als);
- err_free_irq:
- if (als->irq)
- free_irq(als->irq, indio_dev);
- return ret;
- }
- static int lm3533_als_remove(struct platform_device *pdev)
- {
- struct iio_dev *indio_dev = platform_get_drvdata(pdev);
- struct lm3533_als *als = iio_priv(indio_dev);
- lm3533_als_set_int_mode(indio_dev, false);
- iio_device_unregister(indio_dev);
- lm3533_als_disable(als);
- if (als->irq)
- free_irq(als->irq, indio_dev);
- return 0;
- }
- static struct platform_driver lm3533_als_driver = {
- .driver = {
- .name = "lm3533-als",
- },
- .probe = lm3533_als_probe,
- .remove = lm3533_als_remove,
- };
- module_platform_driver(lm3533_als_driver);
- MODULE_AUTHOR("Johan Hovold <jhovold@gmail.com>");
- MODULE_DESCRIPTION("LM3533 Ambient Light Sensor driver");
- MODULE_LICENSE("GPL");
- MODULE_ALIAS("platform:lm3533-als");
|