162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * lm3533-als.c -- LM3533 Ambient Light Sensor driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2011-2012 Texas Instruments 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Author: Johan Hovold <jhovold@gmail.com> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/atomic.h> 1162306a36Sopenharmony_ci#include <linux/fs.h> 1262306a36Sopenharmony_ci#include <linux/interrupt.h> 1362306a36Sopenharmony_ci#include <linux/io.h> 1462306a36Sopenharmony_ci#include <linux/iio/events.h> 1562306a36Sopenharmony_ci#include <linux/iio/iio.h> 1662306a36Sopenharmony_ci#include <linux/module.h> 1762306a36Sopenharmony_ci#include <linux/mutex.h> 1862306a36Sopenharmony_ci#include <linux/mfd/core.h> 1962306a36Sopenharmony_ci#include <linux/platform_device.h> 2062306a36Sopenharmony_ci#include <linux/slab.h> 2162306a36Sopenharmony_ci#include <linux/uaccess.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include <linux/mfd/lm3533.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define LM3533_ALS_RESISTOR_MIN 1 2762306a36Sopenharmony_ci#define LM3533_ALS_RESISTOR_MAX 127 2862306a36Sopenharmony_ci#define LM3533_ALS_CHANNEL_CURRENT_MAX 2 2962306a36Sopenharmony_ci#define LM3533_ALS_THRESH_MAX 3 3062306a36Sopenharmony_ci#define LM3533_ALS_ZONE_MAX 4 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#define LM3533_REG_ALS_RESISTOR_SELECT 0x30 3362306a36Sopenharmony_ci#define LM3533_REG_ALS_CONF 0x31 3462306a36Sopenharmony_ci#define LM3533_REG_ALS_ZONE_INFO 0x34 3562306a36Sopenharmony_ci#define LM3533_REG_ALS_READ_ADC_RAW 0x37 3662306a36Sopenharmony_ci#define LM3533_REG_ALS_READ_ADC_AVERAGE 0x38 3762306a36Sopenharmony_ci#define LM3533_REG_ALS_BOUNDARY_BASE 0x50 3862306a36Sopenharmony_ci#define LM3533_REG_ALS_TARGET_BASE 0x60 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci#define LM3533_ALS_ENABLE_MASK 0x01 4162306a36Sopenharmony_ci#define LM3533_ALS_INPUT_MODE_MASK 0x02 4262306a36Sopenharmony_ci#define LM3533_ALS_INT_ENABLE_MASK 0x01 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci#define LM3533_ALS_ZONE_SHIFT 2 4562306a36Sopenharmony_ci#define LM3533_ALS_ZONE_MASK 0x1c 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci#define LM3533_ALS_FLAG_INT_ENABLED 1 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistruct lm3533_als { 5162306a36Sopenharmony_ci struct lm3533 *lm3533; 5262306a36Sopenharmony_ci struct platform_device *pdev; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci unsigned long flags; 5562306a36Sopenharmony_ci int irq; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci atomic_t zone; 5862306a36Sopenharmony_ci struct mutex thresh_mutex; 5962306a36Sopenharmony_ci}; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistatic int lm3533_als_get_adc(struct iio_dev *indio_dev, bool average, 6362306a36Sopenharmony_ci int *adc) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci struct lm3533_als *als = iio_priv(indio_dev); 6662306a36Sopenharmony_ci u8 reg; 6762306a36Sopenharmony_ci u8 val; 6862306a36Sopenharmony_ci int ret; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci if (average) 7162306a36Sopenharmony_ci reg = LM3533_REG_ALS_READ_ADC_AVERAGE; 7262306a36Sopenharmony_ci else 7362306a36Sopenharmony_ci reg = LM3533_REG_ALS_READ_ADC_RAW; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci ret = lm3533_read(als->lm3533, reg, &val); 7662306a36Sopenharmony_ci if (ret) { 7762306a36Sopenharmony_ci dev_err(&indio_dev->dev, "failed to read adc\n"); 7862306a36Sopenharmony_ci return ret; 7962306a36Sopenharmony_ci } 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci *adc = val; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci return 0; 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic int _lm3533_als_get_zone(struct iio_dev *indio_dev, u8 *zone) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci struct lm3533_als *als = iio_priv(indio_dev); 8962306a36Sopenharmony_ci u8 val; 9062306a36Sopenharmony_ci int ret; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci ret = lm3533_read(als->lm3533, LM3533_REG_ALS_ZONE_INFO, &val); 9362306a36Sopenharmony_ci if (ret) { 9462306a36Sopenharmony_ci dev_err(&indio_dev->dev, "failed to read zone\n"); 9562306a36Sopenharmony_ci return ret; 9662306a36Sopenharmony_ci } 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci val = (val & LM3533_ALS_ZONE_MASK) >> LM3533_ALS_ZONE_SHIFT; 9962306a36Sopenharmony_ci *zone = min_t(u8, val, LM3533_ALS_ZONE_MAX); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci return 0; 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistatic int lm3533_als_get_zone(struct iio_dev *indio_dev, u8 *zone) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci struct lm3533_als *als = iio_priv(indio_dev); 10762306a36Sopenharmony_ci int ret; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci if (test_bit(LM3533_ALS_FLAG_INT_ENABLED, &als->flags)) { 11062306a36Sopenharmony_ci *zone = atomic_read(&als->zone); 11162306a36Sopenharmony_ci } else { 11262306a36Sopenharmony_ci ret = _lm3533_als_get_zone(indio_dev, zone); 11362306a36Sopenharmony_ci if (ret) 11462306a36Sopenharmony_ci return ret; 11562306a36Sopenharmony_ci } 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci return 0; 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci/* 12162306a36Sopenharmony_ci * channel output channel 0..2 12262306a36Sopenharmony_ci * zone zone 0..4 12362306a36Sopenharmony_ci */ 12462306a36Sopenharmony_cistatic inline u8 lm3533_als_get_target_reg(unsigned channel, unsigned zone) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci return LM3533_REG_ALS_TARGET_BASE + 5 * channel + zone; 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistatic int lm3533_als_get_target(struct iio_dev *indio_dev, unsigned channel, 13062306a36Sopenharmony_ci unsigned zone, u8 *val) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci struct lm3533_als *als = iio_priv(indio_dev); 13362306a36Sopenharmony_ci u8 reg; 13462306a36Sopenharmony_ci int ret; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci if (channel > LM3533_ALS_CHANNEL_CURRENT_MAX) 13762306a36Sopenharmony_ci return -EINVAL; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci if (zone > LM3533_ALS_ZONE_MAX) 14062306a36Sopenharmony_ci return -EINVAL; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci reg = lm3533_als_get_target_reg(channel, zone); 14362306a36Sopenharmony_ci ret = lm3533_read(als->lm3533, reg, val); 14462306a36Sopenharmony_ci if (ret) 14562306a36Sopenharmony_ci dev_err(&indio_dev->dev, "failed to get target current\n"); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci return ret; 14862306a36Sopenharmony_ci} 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cistatic int lm3533_als_set_target(struct iio_dev *indio_dev, unsigned channel, 15162306a36Sopenharmony_ci unsigned zone, u8 val) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci struct lm3533_als *als = iio_priv(indio_dev); 15462306a36Sopenharmony_ci u8 reg; 15562306a36Sopenharmony_ci int ret; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci if (channel > LM3533_ALS_CHANNEL_CURRENT_MAX) 15862306a36Sopenharmony_ci return -EINVAL; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci if (zone > LM3533_ALS_ZONE_MAX) 16162306a36Sopenharmony_ci return -EINVAL; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci reg = lm3533_als_get_target_reg(channel, zone); 16462306a36Sopenharmony_ci ret = lm3533_write(als->lm3533, reg, val); 16562306a36Sopenharmony_ci if (ret) 16662306a36Sopenharmony_ci dev_err(&indio_dev->dev, "failed to set target current\n"); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci return ret; 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic int lm3533_als_get_current(struct iio_dev *indio_dev, unsigned channel, 17262306a36Sopenharmony_ci int *val) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci u8 zone; 17562306a36Sopenharmony_ci u8 target; 17662306a36Sopenharmony_ci int ret; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci ret = lm3533_als_get_zone(indio_dev, &zone); 17962306a36Sopenharmony_ci if (ret) 18062306a36Sopenharmony_ci return ret; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci ret = lm3533_als_get_target(indio_dev, channel, zone, &target); 18362306a36Sopenharmony_ci if (ret) 18462306a36Sopenharmony_ci return ret; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci *val = target; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci return 0; 18962306a36Sopenharmony_ci} 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_cistatic int lm3533_als_read_raw(struct iio_dev *indio_dev, 19262306a36Sopenharmony_ci struct iio_chan_spec const *chan, 19362306a36Sopenharmony_ci int *val, int *val2, long mask) 19462306a36Sopenharmony_ci{ 19562306a36Sopenharmony_ci int ret; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci switch (mask) { 19862306a36Sopenharmony_ci case IIO_CHAN_INFO_RAW: 19962306a36Sopenharmony_ci switch (chan->type) { 20062306a36Sopenharmony_ci case IIO_LIGHT: 20162306a36Sopenharmony_ci ret = lm3533_als_get_adc(indio_dev, false, val); 20262306a36Sopenharmony_ci break; 20362306a36Sopenharmony_ci case IIO_CURRENT: 20462306a36Sopenharmony_ci ret = lm3533_als_get_current(indio_dev, chan->channel, 20562306a36Sopenharmony_ci val); 20662306a36Sopenharmony_ci break; 20762306a36Sopenharmony_ci default: 20862306a36Sopenharmony_ci return -EINVAL; 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci break; 21162306a36Sopenharmony_ci case IIO_CHAN_INFO_AVERAGE_RAW: 21262306a36Sopenharmony_ci ret = lm3533_als_get_adc(indio_dev, true, val); 21362306a36Sopenharmony_ci break; 21462306a36Sopenharmony_ci default: 21562306a36Sopenharmony_ci return -EINVAL; 21662306a36Sopenharmony_ci } 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci if (ret) 21962306a36Sopenharmony_ci return ret; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci return IIO_VAL_INT; 22262306a36Sopenharmony_ci} 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci#define CHANNEL_CURRENT(_channel) \ 22562306a36Sopenharmony_ci { \ 22662306a36Sopenharmony_ci .type = IIO_CURRENT, \ 22762306a36Sopenharmony_ci .channel = _channel, \ 22862306a36Sopenharmony_ci .indexed = true, \ 22962306a36Sopenharmony_ci .output = true, \ 23062306a36Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ 23162306a36Sopenharmony_ci } 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_cistatic const struct iio_chan_spec lm3533_als_channels[] = { 23462306a36Sopenharmony_ci { 23562306a36Sopenharmony_ci .type = IIO_LIGHT, 23662306a36Sopenharmony_ci .channel = 0, 23762306a36Sopenharmony_ci .indexed = true, 23862306a36Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_AVERAGE_RAW) | 23962306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_RAW), 24062306a36Sopenharmony_ci }, 24162306a36Sopenharmony_ci CHANNEL_CURRENT(0), 24262306a36Sopenharmony_ci CHANNEL_CURRENT(1), 24362306a36Sopenharmony_ci CHANNEL_CURRENT(2), 24462306a36Sopenharmony_ci}; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_cistatic irqreturn_t lm3533_als_isr(int irq, void *dev_id) 24762306a36Sopenharmony_ci{ 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci struct iio_dev *indio_dev = dev_id; 25062306a36Sopenharmony_ci struct lm3533_als *als = iio_priv(indio_dev); 25162306a36Sopenharmony_ci u8 zone; 25262306a36Sopenharmony_ci int ret; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci /* Clear interrupt by reading the ALS zone register. */ 25562306a36Sopenharmony_ci ret = _lm3533_als_get_zone(indio_dev, &zone); 25662306a36Sopenharmony_ci if (ret) 25762306a36Sopenharmony_ci goto out; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci atomic_set(&als->zone, zone); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci iio_push_event(indio_dev, 26262306a36Sopenharmony_ci IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 26362306a36Sopenharmony_ci 0, 26462306a36Sopenharmony_ci IIO_EV_TYPE_THRESH, 26562306a36Sopenharmony_ci IIO_EV_DIR_EITHER), 26662306a36Sopenharmony_ci iio_get_time_ns(indio_dev)); 26762306a36Sopenharmony_ciout: 26862306a36Sopenharmony_ci return IRQ_HANDLED; 26962306a36Sopenharmony_ci} 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_cistatic int lm3533_als_set_int_mode(struct iio_dev *indio_dev, int enable) 27262306a36Sopenharmony_ci{ 27362306a36Sopenharmony_ci struct lm3533_als *als = iio_priv(indio_dev); 27462306a36Sopenharmony_ci u8 mask = LM3533_ALS_INT_ENABLE_MASK; 27562306a36Sopenharmony_ci u8 val; 27662306a36Sopenharmony_ci int ret; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci if (enable) 27962306a36Sopenharmony_ci val = mask; 28062306a36Sopenharmony_ci else 28162306a36Sopenharmony_ci val = 0; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci ret = lm3533_update(als->lm3533, LM3533_REG_ALS_ZONE_INFO, val, mask); 28462306a36Sopenharmony_ci if (ret) { 28562306a36Sopenharmony_ci dev_err(&indio_dev->dev, "failed to set int mode %d\n", 28662306a36Sopenharmony_ci enable); 28762306a36Sopenharmony_ci return ret; 28862306a36Sopenharmony_ci } 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci return 0; 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_cistatic int lm3533_als_get_int_mode(struct iio_dev *indio_dev, int *enable) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci struct lm3533_als *als = iio_priv(indio_dev); 29662306a36Sopenharmony_ci u8 mask = LM3533_ALS_INT_ENABLE_MASK; 29762306a36Sopenharmony_ci u8 val; 29862306a36Sopenharmony_ci int ret; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci ret = lm3533_read(als->lm3533, LM3533_REG_ALS_ZONE_INFO, &val); 30162306a36Sopenharmony_ci if (ret) { 30262306a36Sopenharmony_ci dev_err(&indio_dev->dev, "failed to get int mode\n"); 30362306a36Sopenharmony_ci return ret; 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci *enable = !!(val & mask); 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci return 0; 30962306a36Sopenharmony_ci} 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_cistatic inline u8 lm3533_als_get_threshold_reg(unsigned nr, bool raising) 31262306a36Sopenharmony_ci{ 31362306a36Sopenharmony_ci u8 offset = !raising; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci return LM3533_REG_ALS_BOUNDARY_BASE + 2 * nr + offset; 31662306a36Sopenharmony_ci} 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_cistatic int lm3533_als_get_threshold(struct iio_dev *indio_dev, unsigned nr, 31962306a36Sopenharmony_ci bool raising, u8 *val) 32062306a36Sopenharmony_ci{ 32162306a36Sopenharmony_ci struct lm3533_als *als = iio_priv(indio_dev); 32262306a36Sopenharmony_ci u8 reg; 32362306a36Sopenharmony_ci int ret; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci if (nr > LM3533_ALS_THRESH_MAX) 32662306a36Sopenharmony_ci return -EINVAL; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci reg = lm3533_als_get_threshold_reg(nr, raising); 32962306a36Sopenharmony_ci ret = lm3533_read(als->lm3533, reg, val); 33062306a36Sopenharmony_ci if (ret) 33162306a36Sopenharmony_ci dev_err(&indio_dev->dev, "failed to get threshold\n"); 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci return ret; 33462306a36Sopenharmony_ci} 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_cistatic int lm3533_als_set_threshold(struct iio_dev *indio_dev, unsigned nr, 33762306a36Sopenharmony_ci bool raising, u8 val) 33862306a36Sopenharmony_ci{ 33962306a36Sopenharmony_ci struct lm3533_als *als = iio_priv(indio_dev); 34062306a36Sopenharmony_ci u8 val2; 34162306a36Sopenharmony_ci u8 reg, reg2; 34262306a36Sopenharmony_ci int ret; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci if (nr > LM3533_ALS_THRESH_MAX) 34562306a36Sopenharmony_ci return -EINVAL; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci reg = lm3533_als_get_threshold_reg(nr, raising); 34862306a36Sopenharmony_ci reg2 = lm3533_als_get_threshold_reg(nr, !raising); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci mutex_lock(&als->thresh_mutex); 35162306a36Sopenharmony_ci ret = lm3533_read(als->lm3533, reg2, &val2); 35262306a36Sopenharmony_ci if (ret) { 35362306a36Sopenharmony_ci dev_err(&indio_dev->dev, "failed to get threshold\n"); 35462306a36Sopenharmony_ci goto out; 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci /* 35762306a36Sopenharmony_ci * This device does not allow negative hysteresis (in fact, it uses 35862306a36Sopenharmony_ci * whichever value is smaller as the lower bound) so we need to make 35962306a36Sopenharmony_ci * sure that thresh_falling <= thresh_raising. 36062306a36Sopenharmony_ci */ 36162306a36Sopenharmony_ci if ((raising && (val < val2)) || (!raising && (val > val2))) { 36262306a36Sopenharmony_ci ret = -EINVAL; 36362306a36Sopenharmony_ci goto out; 36462306a36Sopenharmony_ci } 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci ret = lm3533_write(als->lm3533, reg, val); 36762306a36Sopenharmony_ci if (ret) { 36862306a36Sopenharmony_ci dev_err(&indio_dev->dev, "failed to set threshold\n"); 36962306a36Sopenharmony_ci goto out; 37062306a36Sopenharmony_ci } 37162306a36Sopenharmony_ciout: 37262306a36Sopenharmony_ci mutex_unlock(&als->thresh_mutex); 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci return ret; 37562306a36Sopenharmony_ci} 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_cistatic int lm3533_als_get_hysteresis(struct iio_dev *indio_dev, unsigned nr, 37862306a36Sopenharmony_ci u8 *val) 37962306a36Sopenharmony_ci{ 38062306a36Sopenharmony_ci struct lm3533_als *als = iio_priv(indio_dev); 38162306a36Sopenharmony_ci u8 falling; 38262306a36Sopenharmony_ci u8 raising; 38362306a36Sopenharmony_ci int ret; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci if (nr > LM3533_ALS_THRESH_MAX) 38662306a36Sopenharmony_ci return -EINVAL; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci mutex_lock(&als->thresh_mutex); 38962306a36Sopenharmony_ci ret = lm3533_als_get_threshold(indio_dev, nr, false, &falling); 39062306a36Sopenharmony_ci if (ret) 39162306a36Sopenharmony_ci goto out; 39262306a36Sopenharmony_ci ret = lm3533_als_get_threshold(indio_dev, nr, true, &raising); 39362306a36Sopenharmony_ci if (ret) 39462306a36Sopenharmony_ci goto out; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci *val = raising - falling; 39762306a36Sopenharmony_ciout: 39862306a36Sopenharmony_ci mutex_unlock(&als->thresh_mutex); 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci return ret; 40162306a36Sopenharmony_ci} 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_cistatic ssize_t show_thresh_either_en(struct device *dev, 40462306a36Sopenharmony_ci struct device_attribute *attr, 40562306a36Sopenharmony_ci char *buf) 40662306a36Sopenharmony_ci{ 40762306a36Sopenharmony_ci struct iio_dev *indio_dev = dev_to_iio_dev(dev); 40862306a36Sopenharmony_ci struct lm3533_als *als = iio_priv(indio_dev); 40962306a36Sopenharmony_ci int enable; 41062306a36Sopenharmony_ci int ret; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci if (als->irq) { 41362306a36Sopenharmony_ci ret = lm3533_als_get_int_mode(indio_dev, &enable); 41462306a36Sopenharmony_ci if (ret) 41562306a36Sopenharmony_ci return ret; 41662306a36Sopenharmony_ci } else { 41762306a36Sopenharmony_ci enable = 0; 41862306a36Sopenharmony_ci } 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", enable); 42162306a36Sopenharmony_ci} 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_cistatic ssize_t store_thresh_either_en(struct device *dev, 42462306a36Sopenharmony_ci struct device_attribute *attr, 42562306a36Sopenharmony_ci const char *buf, size_t len) 42662306a36Sopenharmony_ci{ 42762306a36Sopenharmony_ci struct iio_dev *indio_dev = dev_to_iio_dev(dev); 42862306a36Sopenharmony_ci struct lm3533_als *als = iio_priv(indio_dev); 42962306a36Sopenharmony_ci unsigned long enable; 43062306a36Sopenharmony_ci bool int_enabled; 43162306a36Sopenharmony_ci u8 zone; 43262306a36Sopenharmony_ci int ret; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci if (!als->irq) 43562306a36Sopenharmony_ci return -EBUSY; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci if (kstrtoul(buf, 0, &enable)) 43862306a36Sopenharmony_ci return -EINVAL; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci int_enabled = test_bit(LM3533_ALS_FLAG_INT_ENABLED, &als->flags); 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci if (enable && !int_enabled) { 44362306a36Sopenharmony_ci ret = lm3533_als_get_zone(indio_dev, &zone); 44462306a36Sopenharmony_ci if (ret) 44562306a36Sopenharmony_ci return ret; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci atomic_set(&als->zone, zone); 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci set_bit(LM3533_ALS_FLAG_INT_ENABLED, &als->flags); 45062306a36Sopenharmony_ci } 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci ret = lm3533_als_set_int_mode(indio_dev, enable); 45362306a36Sopenharmony_ci if (ret) { 45462306a36Sopenharmony_ci if (!int_enabled) 45562306a36Sopenharmony_ci clear_bit(LM3533_ALS_FLAG_INT_ENABLED, &als->flags); 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci return ret; 45862306a36Sopenharmony_ci } 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci if (!enable) 46162306a36Sopenharmony_ci clear_bit(LM3533_ALS_FLAG_INT_ENABLED, &als->flags); 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci return len; 46462306a36Sopenharmony_ci} 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_cistatic ssize_t show_zone(struct device *dev, 46762306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 46862306a36Sopenharmony_ci{ 46962306a36Sopenharmony_ci struct iio_dev *indio_dev = dev_to_iio_dev(dev); 47062306a36Sopenharmony_ci u8 zone; 47162306a36Sopenharmony_ci int ret; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci ret = lm3533_als_get_zone(indio_dev, &zone); 47462306a36Sopenharmony_ci if (ret) 47562306a36Sopenharmony_ci return ret; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", zone); 47862306a36Sopenharmony_ci} 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_cienum lm3533_als_attribute_type { 48162306a36Sopenharmony_ci LM3533_ATTR_TYPE_HYSTERESIS, 48262306a36Sopenharmony_ci LM3533_ATTR_TYPE_TARGET, 48362306a36Sopenharmony_ci LM3533_ATTR_TYPE_THRESH_FALLING, 48462306a36Sopenharmony_ci LM3533_ATTR_TYPE_THRESH_RAISING, 48562306a36Sopenharmony_ci}; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_cistruct lm3533_als_attribute { 48862306a36Sopenharmony_ci struct device_attribute dev_attr; 48962306a36Sopenharmony_ci enum lm3533_als_attribute_type type; 49062306a36Sopenharmony_ci u8 val1; 49162306a36Sopenharmony_ci u8 val2; 49262306a36Sopenharmony_ci}; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_cistatic inline struct lm3533_als_attribute * 49562306a36Sopenharmony_cito_lm3533_als_attr(struct device_attribute *attr) 49662306a36Sopenharmony_ci{ 49762306a36Sopenharmony_ci return container_of(attr, struct lm3533_als_attribute, dev_attr); 49862306a36Sopenharmony_ci} 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_cistatic ssize_t show_als_attr(struct device *dev, 50162306a36Sopenharmony_ci struct device_attribute *attr, 50262306a36Sopenharmony_ci char *buf) 50362306a36Sopenharmony_ci{ 50462306a36Sopenharmony_ci struct iio_dev *indio_dev = dev_to_iio_dev(dev); 50562306a36Sopenharmony_ci struct lm3533_als_attribute *als_attr = to_lm3533_als_attr(attr); 50662306a36Sopenharmony_ci u8 val; 50762306a36Sopenharmony_ci int ret; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci switch (als_attr->type) { 51062306a36Sopenharmony_ci case LM3533_ATTR_TYPE_HYSTERESIS: 51162306a36Sopenharmony_ci ret = lm3533_als_get_hysteresis(indio_dev, als_attr->val1, 51262306a36Sopenharmony_ci &val); 51362306a36Sopenharmony_ci break; 51462306a36Sopenharmony_ci case LM3533_ATTR_TYPE_TARGET: 51562306a36Sopenharmony_ci ret = lm3533_als_get_target(indio_dev, als_attr->val1, 51662306a36Sopenharmony_ci als_attr->val2, &val); 51762306a36Sopenharmony_ci break; 51862306a36Sopenharmony_ci case LM3533_ATTR_TYPE_THRESH_FALLING: 51962306a36Sopenharmony_ci ret = lm3533_als_get_threshold(indio_dev, als_attr->val1, 52062306a36Sopenharmony_ci false, &val); 52162306a36Sopenharmony_ci break; 52262306a36Sopenharmony_ci case LM3533_ATTR_TYPE_THRESH_RAISING: 52362306a36Sopenharmony_ci ret = lm3533_als_get_threshold(indio_dev, als_attr->val1, 52462306a36Sopenharmony_ci true, &val); 52562306a36Sopenharmony_ci break; 52662306a36Sopenharmony_ci default: 52762306a36Sopenharmony_ci ret = -ENXIO; 52862306a36Sopenharmony_ci } 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci if (ret) 53162306a36Sopenharmony_ci return ret; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", val); 53462306a36Sopenharmony_ci} 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_cistatic ssize_t store_als_attr(struct device *dev, 53762306a36Sopenharmony_ci struct device_attribute *attr, 53862306a36Sopenharmony_ci const char *buf, size_t len) 53962306a36Sopenharmony_ci{ 54062306a36Sopenharmony_ci struct iio_dev *indio_dev = dev_to_iio_dev(dev); 54162306a36Sopenharmony_ci struct lm3533_als_attribute *als_attr = to_lm3533_als_attr(attr); 54262306a36Sopenharmony_ci u8 val; 54362306a36Sopenharmony_ci int ret; 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci if (kstrtou8(buf, 0, &val)) 54662306a36Sopenharmony_ci return -EINVAL; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci switch (als_attr->type) { 54962306a36Sopenharmony_ci case LM3533_ATTR_TYPE_TARGET: 55062306a36Sopenharmony_ci ret = lm3533_als_set_target(indio_dev, als_attr->val1, 55162306a36Sopenharmony_ci als_attr->val2, val); 55262306a36Sopenharmony_ci break; 55362306a36Sopenharmony_ci case LM3533_ATTR_TYPE_THRESH_FALLING: 55462306a36Sopenharmony_ci ret = lm3533_als_set_threshold(indio_dev, als_attr->val1, 55562306a36Sopenharmony_ci false, val); 55662306a36Sopenharmony_ci break; 55762306a36Sopenharmony_ci case LM3533_ATTR_TYPE_THRESH_RAISING: 55862306a36Sopenharmony_ci ret = lm3533_als_set_threshold(indio_dev, als_attr->val1, 55962306a36Sopenharmony_ci true, val); 56062306a36Sopenharmony_ci break; 56162306a36Sopenharmony_ci default: 56262306a36Sopenharmony_ci ret = -ENXIO; 56362306a36Sopenharmony_ci } 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci if (ret) 56662306a36Sopenharmony_ci return ret; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci return len; 56962306a36Sopenharmony_ci} 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci#define ALS_ATTR(_name, _mode, _show, _store, _type, _val1, _val2) \ 57262306a36Sopenharmony_ci { .dev_attr = __ATTR(_name, _mode, _show, _store), \ 57362306a36Sopenharmony_ci .type = _type, \ 57462306a36Sopenharmony_ci .val1 = _val1, \ 57562306a36Sopenharmony_ci .val2 = _val2 } 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci#define LM3533_ALS_ATTR(_name, _mode, _show, _store, _type, _val1, _val2) \ 57862306a36Sopenharmony_ci struct lm3533_als_attribute lm3533_als_attr_##_name = \ 57962306a36Sopenharmony_ci ALS_ATTR(_name, _mode, _show, _store, _type, _val1, _val2) 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci#define ALS_TARGET_ATTR_RW(_channel, _zone) \ 58262306a36Sopenharmony_ci LM3533_ALS_ATTR(out_current##_channel##_current##_zone##_raw, \ 58362306a36Sopenharmony_ci S_IRUGO | S_IWUSR, \ 58462306a36Sopenharmony_ci show_als_attr, store_als_attr, \ 58562306a36Sopenharmony_ci LM3533_ATTR_TYPE_TARGET, _channel, _zone) 58662306a36Sopenharmony_ci/* 58762306a36Sopenharmony_ci * ALS output current values (ALS mapper targets) 58862306a36Sopenharmony_ci * 58962306a36Sopenharmony_ci * out_current[0-2]_current[0-4]_raw 0-255 59062306a36Sopenharmony_ci */ 59162306a36Sopenharmony_cistatic ALS_TARGET_ATTR_RW(0, 0); 59262306a36Sopenharmony_cistatic ALS_TARGET_ATTR_RW(0, 1); 59362306a36Sopenharmony_cistatic ALS_TARGET_ATTR_RW(0, 2); 59462306a36Sopenharmony_cistatic ALS_TARGET_ATTR_RW(0, 3); 59562306a36Sopenharmony_cistatic ALS_TARGET_ATTR_RW(0, 4); 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_cistatic ALS_TARGET_ATTR_RW(1, 0); 59862306a36Sopenharmony_cistatic ALS_TARGET_ATTR_RW(1, 1); 59962306a36Sopenharmony_cistatic ALS_TARGET_ATTR_RW(1, 2); 60062306a36Sopenharmony_cistatic ALS_TARGET_ATTR_RW(1, 3); 60162306a36Sopenharmony_cistatic ALS_TARGET_ATTR_RW(1, 4); 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_cistatic ALS_TARGET_ATTR_RW(2, 0); 60462306a36Sopenharmony_cistatic ALS_TARGET_ATTR_RW(2, 1); 60562306a36Sopenharmony_cistatic ALS_TARGET_ATTR_RW(2, 2); 60662306a36Sopenharmony_cistatic ALS_TARGET_ATTR_RW(2, 3); 60762306a36Sopenharmony_cistatic ALS_TARGET_ATTR_RW(2, 4); 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci#define ALS_THRESH_FALLING_ATTR_RW(_nr) \ 61062306a36Sopenharmony_ci LM3533_ALS_ATTR(in_illuminance0_thresh##_nr##_falling_value, \ 61162306a36Sopenharmony_ci S_IRUGO | S_IWUSR, \ 61262306a36Sopenharmony_ci show_als_attr, store_als_attr, \ 61362306a36Sopenharmony_ci LM3533_ATTR_TYPE_THRESH_FALLING, _nr, 0) 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci#define ALS_THRESH_RAISING_ATTR_RW(_nr) \ 61662306a36Sopenharmony_ci LM3533_ALS_ATTR(in_illuminance0_thresh##_nr##_raising_value, \ 61762306a36Sopenharmony_ci S_IRUGO | S_IWUSR, \ 61862306a36Sopenharmony_ci show_als_attr, store_als_attr, \ 61962306a36Sopenharmony_ci LM3533_ATTR_TYPE_THRESH_RAISING, _nr, 0) 62062306a36Sopenharmony_ci/* 62162306a36Sopenharmony_ci * ALS Zone thresholds (boundaries) 62262306a36Sopenharmony_ci * 62362306a36Sopenharmony_ci * in_illuminance0_thresh[0-3]_falling_value 0-255 62462306a36Sopenharmony_ci * in_illuminance0_thresh[0-3]_raising_value 0-255 62562306a36Sopenharmony_ci */ 62662306a36Sopenharmony_cistatic ALS_THRESH_FALLING_ATTR_RW(0); 62762306a36Sopenharmony_cistatic ALS_THRESH_FALLING_ATTR_RW(1); 62862306a36Sopenharmony_cistatic ALS_THRESH_FALLING_ATTR_RW(2); 62962306a36Sopenharmony_cistatic ALS_THRESH_FALLING_ATTR_RW(3); 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_cistatic ALS_THRESH_RAISING_ATTR_RW(0); 63262306a36Sopenharmony_cistatic ALS_THRESH_RAISING_ATTR_RW(1); 63362306a36Sopenharmony_cistatic ALS_THRESH_RAISING_ATTR_RW(2); 63462306a36Sopenharmony_cistatic ALS_THRESH_RAISING_ATTR_RW(3); 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci#define ALS_HYSTERESIS_ATTR_RO(_nr) \ 63762306a36Sopenharmony_ci LM3533_ALS_ATTR(in_illuminance0_thresh##_nr##_hysteresis, \ 63862306a36Sopenharmony_ci S_IRUGO, show_als_attr, NULL, \ 63962306a36Sopenharmony_ci LM3533_ATTR_TYPE_HYSTERESIS, _nr, 0) 64062306a36Sopenharmony_ci/* 64162306a36Sopenharmony_ci * ALS Zone threshold hysteresis 64262306a36Sopenharmony_ci * 64362306a36Sopenharmony_ci * threshY_hysteresis = threshY_raising - threshY_falling 64462306a36Sopenharmony_ci * 64562306a36Sopenharmony_ci * in_illuminance0_thresh[0-3]_hysteresis 0-255 64662306a36Sopenharmony_ci * in_illuminance0_thresh[0-3]_hysteresis 0-255 64762306a36Sopenharmony_ci */ 64862306a36Sopenharmony_cistatic ALS_HYSTERESIS_ATTR_RO(0); 64962306a36Sopenharmony_cistatic ALS_HYSTERESIS_ATTR_RO(1); 65062306a36Sopenharmony_cistatic ALS_HYSTERESIS_ATTR_RO(2); 65162306a36Sopenharmony_cistatic ALS_HYSTERESIS_ATTR_RO(3); 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci#define ILLUMINANCE_ATTR_RO(_name) \ 65462306a36Sopenharmony_ci DEVICE_ATTR(in_illuminance0_##_name, S_IRUGO, show_##_name, NULL) 65562306a36Sopenharmony_ci#define ILLUMINANCE_ATTR_RW(_name) \ 65662306a36Sopenharmony_ci DEVICE_ATTR(in_illuminance0_##_name, S_IRUGO | S_IWUSR, \ 65762306a36Sopenharmony_ci show_##_name, store_##_name) 65862306a36Sopenharmony_ci/* 65962306a36Sopenharmony_ci * ALS Zone threshold-event enable 66062306a36Sopenharmony_ci * 66162306a36Sopenharmony_ci * in_illuminance0_thresh_either_en 0,1 66262306a36Sopenharmony_ci */ 66362306a36Sopenharmony_cistatic ILLUMINANCE_ATTR_RW(thresh_either_en); 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci/* 66662306a36Sopenharmony_ci * ALS Current Zone 66762306a36Sopenharmony_ci * 66862306a36Sopenharmony_ci * in_illuminance0_zone 0-4 66962306a36Sopenharmony_ci */ 67062306a36Sopenharmony_cistatic ILLUMINANCE_ATTR_RO(zone); 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_cistatic struct attribute *lm3533_als_event_attributes[] = { 67362306a36Sopenharmony_ci &dev_attr_in_illuminance0_thresh_either_en.attr, 67462306a36Sopenharmony_ci &lm3533_als_attr_in_illuminance0_thresh0_falling_value.dev_attr.attr, 67562306a36Sopenharmony_ci &lm3533_als_attr_in_illuminance0_thresh0_hysteresis.dev_attr.attr, 67662306a36Sopenharmony_ci &lm3533_als_attr_in_illuminance0_thresh0_raising_value.dev_attr.attr, 67762306a36Sopenharmony_ci &lm3533_als_attr_in_illuminance0_thresh1_falling_value.dev_attr.attr, 67862306a36Sopenharmony_ci &lm3533_als_attr_in_illuminance0_thresh1_hysteresis.dev_attr.attr, 67962306a36Sopenharmony_ci &lm3533_als_attr_in_illuminance0_thresh1_raising_value.dev_attr.attr, 68062306a36Sopenharmony_ci &lm3533_als_attr_in_illuminance0_thresh2_falling_value.dev_attr.attr, 68162306a36Sopenharmony_ci &lm3533_als_attr_in_illuminance0_thresh2_hysteresis.dev_attr.attr, 68262306a36Sopenharmony_ci &lm3533_als_attr_in_illuminance0_thresh2_raising_value.dev_attr.attr, 68362306a36Sopenharmony_ci &lm3533_als_attr_in_illuminance0_thresh3_falling_value.dev_attr.attr, 68462306a36Sopenharmony_ci &lm3533_als_attr_in_illuminance0_thresh3_hysteresis.dev_attr.attr, 68562306a36Sopenharmony_ci &lm3533_als_attr_in_illuminance0_thresh3_raising_value.dev_attr.attr, 68662306a36Sopenharmony_ci NULL 68762306a36Sopenharmony_ci}; 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_cistatic const struct attribute_group lm3533_als_event_attribute_group = { 69062306a36Sopenharmony_ci .attrs = lm3533_als_event_attributes 69162306a36Sopenharmony_ci}; 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_cistatic struct attribute *lm3533_als_attributes[] = { 69462306a36Sopenharmony_ci &dev_attr_in_illuminance0_zone.attr, 69562306a36Sopenharmony_ci &lm3533_als_attr_out_current0_current0_raw.dev_attr.attr, 69662306a36Sopenharmony_ci &lm3533_als_attr_out_current0_current1_raw.dev_attr.attr, 69762306a36Sopenharmony_ci &lm3533_als_attr_out_current0_current2_raw.dev_attr.attr, 69862306a36Sopenharmony_ci &lm3533_als_attr_out_current0_current3_raw.dev_attr.attr, 69962306a36Sopenharmony_ci &lm3533_als_attr_out_current0_current4_raw.dev_attr.attr, 70062306a36Sopenharmony_ci &lm3533_als_attr_out_current1_current0_raw.dev_attr.attr, 70162306a36Sopenharmony_ci &lm3533_als_attr_out_current1_current1_raw.dev_attr.attr, 70262306a36Sopenharmony_ci &lm3533_als_attr_out_current1_current2_raw.dev_attr.attr, 70362306a36Sopenharmony_ci &lm3533_als_attr_out_current1_current3_raw.dev_attr.attr, 70462306a36Sopenharmony_ci &lm3533_als_attr_out_current1_current4_raw.dev_attr.attr, 70562306a36Sopenharmony_ci &lm3533_als_attr_out_current2_current0_raw.dev_attr.attr, 70662306a36Sopenharmony_ci &lm3533_als_attr_out_current2_current1_raw.dev_attr.attr, 70762306a36Sopenharmony_ci &lm3533_als_attr_out_current2_current2_raw.dev_attr.attr, 70862306a36Sopenharmony_ci &lm3533_als_attr_out_current2_current3_raw.dev_attr.attr, 70962306a36Sopenharmony_ci &lm3533_als_attr_out_current2_current4_raw.dev_attr.attr, 71062306a36Sopenharmony_ci NULL 71162306a36Sopenharmony_ci}; 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_cistatic const struct attribute_group lm3533_als_attribute_group = { 71462306a36Sopenharmony_ci .attrs = lm3533_als_attributes 71562306a36Sopenharmony_ci}; 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_cistatic int lm3533_als_set_input_mode(struct lm3533_als *als, bool pwm_mode) 71862306a36Sopenharmony_ci{ 71962306a36Sopenharmony_ci u8 mask = LM3533_ALS_INPUT_MODE_MASK; 72062306a36Sopenharmony_ci u8 val; 72162306a36Sopenharmony_ci int ret; 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci if (pwm_mode) 72462306a36Sopenharmony_ci val = mask; /* pwm input */ 72562306a36Sopenharmony_ci else 72662306a36Sopenharmony_ci val = 0; /* analog input */ 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci ret = lm3533_update(als->lm3533, LM3533_REG_ALS_CONF, val, mask); 72962306a36Sopenharmony_ci if (ret) { 73062306a36Sopenharmony_ci dev_err(&als->pdev->dev, "failed to set input mode %d\n", 73162306a36Sopenharmony_ci pwm_mode); 73262306a36Sopenharmony_ci return ret; 73362306a36Sopenharmony_ci } 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci return 0; 73662306a36Sopenharmony_ci} 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_cistatic int lm3533_als_set_resistor(struct lm3533_als *als, u8 val) 73962306a36Sopenharmony_ci{ 74062306a36Sopenharmony_ci int ret; 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci if (val < LM3533_ALS_RESISTOR_MIN || val > LM3533_ALS_RESISTOR_MAX) { 74362306a36Sopenharmony_ci dev_err(&als->pdev->dev, "invalid resistor value\n"); 74462306a36Sopenharmony_ci return -EINVAL; 74562306a36Sopenharmony_ci } 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci ret = lm3533_write(als->lm3533, LM3533_REG_ALS_RESISTOR_SELECT, val); 74862306a36Sopenharmony_ci if (ret) { 74962306a36Sopenharmony_ci dev_err(&als->pdev->dev, "failed to set resistor\n"); 75062306a36Sopenharmony_ci return ret; 75162306a36Sopenharmony_ci } 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci return 0; 75462306a36Sopenharmony_ci} 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_cistatic int lm3533_als_setup(struct lm3533_als *als, 75762306a36Sopenharmony_ci struct lm3533_als_platform_data *pdata) 75862306a36Sopenharmony_ci{ 75962306a36Sopenharmony_ci int ret; 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci ret = lm3533_als_set_input_mode(als, pdata->pwm_mode); 76262306a36Sopenharmony_ci if (ret) 76362306a36Sopenharmony_ci return ret; 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci /* ALS input is always high impedance in PWM-mode. */ 76662306a36Sopenharmony_ci if (!pdata->pwm_mode) { 76762306a36Sopenharmony_ci ret = lm3533_als_set_resistor(als, pdata->r_select); 76862306a36Sopenharmony_ci if (ret) 76962306a36Sopenharmony_ci return ret; 77062306a36Sopenharmony_ci } 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci return 0; 77362306a36Sopenharmony_ci} 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_cistatic int lm3533_als_setup_irq(struct lm3533_als *als, void *dev) 77662306a36Sopenharmony_ci{ 77762306a36Sopenharmony_ci u8 mask = LM3533_ALS_INT_ENABLE_MASK; 77862306a36Sopenharmony_ci int ret; 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci /* Make sure interrupts are disabled. */ 78162306a36Sopenharmony_ci ret = lm3533_update(als->lm3533, LM3533_REG_ALS_ZONE_INFO, 0, mask); 78262306a36Sopenharmony_ci if (ret) { 78362306a36Sopenharmony_ci dev_err(&als->pdev->dev, "failed to disable interrupts\n"); 78462306a36Sopenharmony_ci return ret; 78562306a36Sopenharmony_ci } 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci ret = request_threaded_irq(als->irq, NULL, lm3533_als_isr, 78862306a36Sopenharmony_ci IRQF_TRIGGER_LOW | IRQF_ONESHOT, 78962306a36Sopenharmony_ci dev_name(&als->pdev->dev), dev); 79062306a36Sopenharmony_ci if (ret) { 79162306a36Sopenharmony_ci dev_err(&als->pdev->dev, "failed to request irq %d\n", 79262306a36Sopenharmony_ci als->irq); 79362306a36Sopenharmony_ci return ret; 79462306a36Sopenharmony_ci } 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci return 0; 79762306a36Sopenharmony_ci} 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_cistatic int lm3533_als_enable(struct lm3533_als *als) 80062306a36Sopenharmony_ci{ 80162306a36Sopenharmony_ci u8 mask = LM3533_ALS_ENABLE_MASK; 80262306a36Sopenharmony_ci int ret; 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci ret = lm3533_update(als->lm3533, LM3533_REG_ALS_CONF, mask, mask); 80562306a36Sopenharmony_ci if (ret) 80662306a36Sopenharmony_ci dev_err(&als->pdev->dev, "failed to enable ALS\n"); 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci return ret; 80962306a36Sopenharmony_ci} 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_cistatic int lm3533_als_disable(struct lm3533_als *als) 81262306a36Sopenharmony_ci{ 81362306a36Sopenharmony_ci u8 mask = LM3533_ALS_ENABLE_MASK; 81462306a36Sopenharmony_ci int ret; 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci ret = lm3533_update(als->lm3533, LM3533_REG_ALS_CONF, 0, mask); 81762306a36Sopenharmony_ci if (ret) 81862306a36Sopenharmony_ci dev_err(&als->pdev->dev, "failed to disable ALS\n"); 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci return ret; 82162306a36Sopenharmony_ci} 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_cistatic const struct iio_info lm3533_als_info = { 82462306a36Sopenharmony_ci .attrs = &lm3533_als_attribute_group, 82562306a36Sopenharmony_ci .event_attrs = &lm3533_als_event_attribute_group, 82662306a36Sopenharmony_ci .read_raw = &lm3533_als_read_raw, 82762306a36Sopenharmony_ci}; 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_cistatic int lm3533_als_probe(struct platform_device *pdev) 83062306a36Sopenharmony_ci{ 83162306a36Sopenharmony_ci struct lm3533 *lm3533; 83262306a36Sopenharmony_ci struct lm3533_als_platform_data *pdata; 83362306a36Sopenharmony_ci struct lm3533_als *als; 83462306a36Sopenharmony_ci struct iio_dev *indio_dev; 83562306a36Sopenharmony_ci int ret; 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci lm3533 = dev_get_drvdata(pdev->dev.parent); 83862306a36Sopenharmony_ci if (!lm3533) 83962306a36Sopenharmony_ci return -EINVAL; 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci pdata = pdev->dev.platform_data; 84262306a36Sopenharmony_ci if (!pdata) { 84362306a36Sopenharmony_ci dev_err(&pdev->dev, "no platform data\n"); 84462306a36Sopenharmony_ci return -EINVAL; 84562306a36Sopenharmony_ci } 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*als)); 84862306a36Sopenharmony_ci if (!indio_dev) 84962306a36Sopenharmony_ci return -ENOMEM; 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci indio_dev->info = &lm3533_als_info; 85262306a36Sopenharmony_ci indio_dev->channels = lm3533_als_channels; 85362306a36Sopenharmony_ci indio_dev->num_channels = ARRAY_SIZE(lm3533_als_channels); 85462306a36Sopenharmony_ci indio_dev->name = dev_name(&pdev->dev); 85562306a36Sopenharmony_ci iio_device_set_parent(indio_dev, pdev->dev.parent); 85662306a36Sopenharmony_ci indio_dev->modes = INDIO_DIRECT_MODE; 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci als = iio_priv(indio_dev); 85962306a36Sopenharmony_ci als->lm3533 = lm3533; 86062306a36Sopenharmony_ci als->pdev = pdev; 86162306a36Sopenharmony_ci als->irq = lm3533->irq; 86262306a36Sopenharmony_ci atomic_set(&als->zone, 0); 86362306a36Sopenharmony_ci mutex_init(&als->thresh_mutex); 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci platform_set_drvdata(pdev, indio_dev); 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci if (als->irq) { 86862306a36Sopenharmony_ci ret = lm3533_als_setup_irq(als, indio_dev); 86962306a36Sopenharmony_ci if (ret) 87062306a36Sopenharmony_ci return ret; 87162306a36Sopenharmony_ci } 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci ret = lm3533_als_setup(als, pdata); 87462306a36Sopenharmony_ci if (ret) 87562306a36Sopenharmony_ci goto err_free_irq; 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci ret = lm3533_als_enable(als); 87862306a36Sopenharmony_ci if (ret) 87962306a36Sopenharmony_ci goto err_free_irq; 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci ret = iio_device_register(indio_dev); 88262306a36Sopenharmony_ci if (ret) { 88362306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to register ALS\n"); 88462306a36Sopenharmony_ci goto err_disable; 88562306a36Sopenharmony_ci } 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci return 0; 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_cierr_disable: 89062306a36Sopenharmony_ci lm3533_als_disable(als); 89162306a36Sopenharmony_cierr_free_irq: 89262306a36Sopenharmony_ci if (als->irq) 89362306a36Sopenharmony_ci free_irq(als->irq, indio_dev); 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci return ret; 89662306a36Sopenharmony_ci} 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_cistatic int lm3533_als_remove(struct platform_device *pdev) 89962306a36Sopenharmony_ci{ 90062306a36Sopenharmony_ci struct iio_dev *indio_dev = platform_get_drvdata(pdev); 90162306a36Sopenharmony_ci struct lm3533_als *als = iio_priv(indio_dev); 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci lm3533_als_set_int_mode(indio_dev, false); 90462306a36Sopenharmony_ci iio_device_unregister(indio_dev); 90562306a36Sopenharmony_ci lm3533_als_disable(als); 90662306a36Sopenharmony_ci if (als->irq) 90762306a36Sopenharmony_ci free_irq(als->irq, indio_dev); 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci return 0; 91062306a36Sopenharmony_ci} 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_cistatic struct platform_driver lm3533_als_driver = { 91362306a36Sopenharmony_ci .driver = { 91462306a36Sopenharmony_ci .name = "lm3533-als", 91562306a36Sopenharmony_ci }, 91662306a36Sopenharmony_ci .probe = lm3533_als_probe, 91762306a36Sopenharmony_ci .remove = lm3533_als_remove, 91862306a36Sopenharmony_ci}; 91962306a36Sopenharmony_cimodule_platform_driver(lm3533_als_driver); 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ciMODULE_AUTHOR("Johan Hovold <jhovold@gmail.com>"); 92262306a36Sopenharmony_ciMODULE_DESCRIPTION("LM3533 Ambient Light Sensor driver"); 92362306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 92462306a36Sopenharmony_ciMODULE_ALIAS("platform:lm3533-als"); 925