18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * lm3533-als.c -- LM3533 Ambient Light Sensor driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2011-2012 Texas Instruments 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Author: Johan Hovold <jhovold@gmail.com> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/atomic.h> 118c2ecf20Sopenharmony_ci#include <linux/fs.h> 128c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 138c2ecf20Sopenharmony_ci#include <linux/io.h> 148c2ecf20Sopenharmony_ci#include <linux/iio/events.h> 158c2ecf20Sopenharmony_ci#include <linux/iio/iio.h> 168c2ecf20Sopenharmony_ci#include <linux/module.h> 178c2ecf20Sopenharmony_ci#include <linux/mutex.h> 188c2ecf20Sopenharmony_ci#include <linux/mfd/core.h> 198c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 208c2ecf20Sopenharmony_ci#include <linux/slab.h> 218c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#include <linux/mfd/lm3533.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#define LM3533_ALS_RESISTOR_MIN 1 278c2ecf20Sopenharmony_ci#define LM3533_ALS_RESISTOR_MAX 127 288c2ecf20Sopenharmony_ci#define LM3533_ALS_CHANNEL_CURRENT_MAX 2 298c2ecf20Sopenharmony_ci#define LM3533_ALS_THRESH_MAX 3 308c2ecf20Sopenharmony_ci#define LM3533_ALS_ZONE_MAX 4 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#define LM3533_REG_ALS_RESISTOR_SELECT 0x30 338c2ecf20Sopenharmony_ci#define LM3533_REG_ALS_CONF 0x31 348c2ecf20Sopenharmony_ci#define LM3533_REG_ALS_ZONE_INFO 0x34 358c2ecf20Sopenharmony_ci#define LM3533_REG_ALS_READ_ADC_RAW 0x37 368c2ecf20Sopenharmony_ci#define LM3533_REG_ALS_READ_ADC_AVERAGE 0x38 378c2ecf20Sopenharmony_ci#define LM3533_REG_ALS_BOUNDARY_BASE 0x50 388c2ecf20Sopenharmony_ci#define LM3533_REG_ALS_TARGET_BASE 0x60 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#define LM3533_ALS_ENABLE_MASK 0x01 418c2ecf20Sopenharmony_ci#define LM3533_ALS_INPUT_MODE_MASK 0x02 428c2ecf20Sopenharmony_ci#define LM3533_ALS_INT_ENABLE_MASK 0x01 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci#define LM3533_ALS_ZONE_SHIFT 2 458c2ecf20Sopenharmony_ci#define LM3533_ALS_ZONE_MASK 0x1c 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci#define LM3533_ALS_FLAG_INT_ENABLED 1 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistruct lm3533_als { 518c2ecf20Sopenharmony_ci struct lm3533 *lm3533; 528c2ecf20Sopenharmony_ci struct platform_device *pdev; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci unsigned long flags; 558c2ecf20Sopenharmony_ci int irq; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci atomic_t zone; 588c2ecf20Sopenharmony_ci struct mutex thresh_mutex; 598c2ecf20Sopenharmony_ci}; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic int lm3533_als_get_adc(struct iio_dev *indio_dev, bool average, 638c2ecf20Sopenharmony_ci int *adc) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci struct lm3533_als *als = iio_priv(indio_dev); 668c2ecf20Sopenharmony_ci u8 reg; 678c2ecf20Sopenharmony_ci u8 val; 688c2ecf20Sopenharmony_ci int ret; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci if (average) 718c2ecf20Sopenharmony_ci reg = LM3533_REG_ALS_READ_ADC_AVERAGE; 728c2ecf20Sopenharmony_ci else 738c2ecf20Sopenharmony_ci reg = LM3533_REG_ALS_READ_ADC_RAW; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci ret = lm3533_read(als->lm3533, reg, &val); 768c2ecf20Sopenharmony_ci if (ret) { 778c2ecf20Sopenharmony_ci dev_err(&indio_dev->dev, "failed to read adc\n"); 788c2ecf20Sopenharmony_ci return ret; 798c2ecf20Sopenharmony_ci } 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci *adc = val; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci return 0; 848c2ecf20Sopenharmony_ci} 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistatic int _lm3533_als_get_zone(struct iio_dev *indio_dev, u8 *zone) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci struct lm3533_als *als = iio_priv(indio_dev); 898c2ecf20Sopenharmony_ci u8 val; 908c2ecf20Sopenharmony_ci int ret; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci ret = lm3533_read(als->lm3533, LM3533_REG_ALS_ZONE_INFO, &val); 938c2ecf20Sopenharmony_ci if (ret) { 948c2ecf20Sopenharmony_ci dev_err(&indio_dev->dev, "failed to read zone\n"); 958c2ecf20Sopenharmony_ci return ret; 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci val = (val & LM3533_ALS_ZONE_MASK) >> LM3533_ALS_ZONE_SHIFT; 998c2ecf20Sopenharmony_ci *zone = min_t(u8, val, LM3533_ALS_ZONE_MAX); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci return 0; 1028c2ecf20Sopenharmony_ci} 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic int lm3533_als_get_zone(struct iio_dev *indio_dev, u8 *zone) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci struct lm3533_als *als = iio_priv(indio_dev); 1078c2ecf20Sopenharmony_ci int ret; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci if (test_bit(LM3533_ALS_FLAG_INT_ENABLED, &als->flags)) { 1108c2ecf20Sopenharmony_ci *zone = atomic_read(&als->zone); 1118c2ecf20Sopenharmony_ci } else { 1128c2ecf20Sopenharmony_ci ret = _lm3533_als_get_zone(indio_dev, zone); 1138c2ecf20Sopenharmony_ci if (ret) 1148c2ecf20Sopenharmony_ci return ret; 1158c2ecf20Sopenharmony_ci } 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci return 0; 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci/* 1218c2ecf20Sopenharmony_ci * channel output channel 0..2 1228c2ecf20Sopenharmony_ci * zone zone 0..4 1238c2ecf20Sopenharmony_ci */ 1248c2ecf20Sopenharmony_cistatic inline u8 lm3533_als_get_target_reg(unsigned channel, unsigned zone) 1258c2ecf20Sopenharmony_ci{ 1268c2ecf20Sopenharmony_ci return LM3533_REG_ALS_TARGET_BASE + 5 * channel + zone; 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic int lm3533_als_get_target(struct iio_dev *indio_dev, unsigned channel, 1308c2ecf20Sopenharmony_ci unsigned zone, u8 *val) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci struct lm3533_als *als = iio_priv(indio_dev); 1338c2ecf20Sopenharmony_ci u8 reg; 1348c2ecf20Sopenharmony_ci int ret; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci if (channel > LM3533_ALS_CHANNEL_CURRENT_MAX) 1378c2ecf20Sopenharmony_ci return -EINVAL; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci if (zone > LM3533_ALS_ZONE_MAX) 1408c2ecf20Sopenharmony_ci return -EINVAL; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci reg = lm3533_als_get_target_reg(channel, zone); 1438c2ecf20Sopenharmony_ci ret = lm3533_read(als->lm3533, reg, val); 1448c2ecf20Sopenharmony_ci if (ret) 1458c2ecf20Sopenharmony_ci dev_err(&indio_dev->dev, "failed to get target current\n"); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci return ret; 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic int lm3533_als_set_target(struct iio_dev *indio_dev, unsigned channel, 1518c2ecf20Sopenharmony_ci unsigned zone, u8 val) 1528c2ecf20Sopenharmony_ci{ 1538c2ecf20Sopenharmony_ci struct lm3533_als *als = iio_priv(indio_dev); 1548c2ecf20Sopenharmony_ci u8 reg; 1558c2ecf20Sopenharmony_ci int ret; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci if (channel > LM3533_ALS_CHANNEL_CURRENT_MAX) 1588c2ecf20Sopenharmony_ci return -EINVAL; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci if (zone > LM3533_ALS_ZONE_MAX) 1618c2ecf20Sopenharmony_ci return -EINVAL; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci reg = lm3533_als_get_target_reg(channel, zone); 1648c2ecf20Sopenharmony_ci ret = lm3533_write(als->lm3533, reg, val); 1658c2ecf20Sopenharmony_ci if (ret) 1668c2ecf20Sopenharmony_ci dev_err(&indio_dev->dev, "failed to set target current\n"); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci return ret; 1698c2ecf20Sopenharmony_ci} 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_cistatic int lm3533_als_get_current(struct iio_dev *indio_dev, unsigned channel, 1728c2ecf20Sopenharmony_ci int *val) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci u8 zone; 1758c2ecf20Sopenharmony_ci u8 target; 1768c2ecf20Sopenharmony_ci int ret; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci ret = lm3533_als_get_zone(indio_dev, &zone); 1798c2ecf20Sopenharmony_ci if (ret) 1808c2ecf20Sopenharmony_ci return ret; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci ret = lm3533_als_get_target(indio_dev, channel, zone, &target); 1838c2ecf20Sopenharmony_ci if (ret) 1848c2ecf20Sopenharmony_ci return ret; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci *val = target; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci return 0; 1898c2ecf20Sopenharmony_ci} 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cistatic int lm3533_als_read_raw(struct iio_dev *indio_dev, 1928c2ecf20Sopenharmony_ci struct iio_chan_spec const *chan, 1938c2ecf20Sopenharmony_ci int *val, int *val2, long mask) 1948c2ecf20Sopenharmony_ci{ 1958c2ecf20Sopenharmony_ci int ret; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci switch (mask) { 1988c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_RAW: 1998c2ecf20Sopenharmony_ci switch (chan->type) { 2008c2ecf20Sopenharmony_ci case IIO_LIGHT: 2018c2ecf20Sopenharmony_ci ret = lm3533_als_get_adc(indio_dev, false, val); 2028c2ecf20Sopenharmony_ci break; 2038c2ecf20Sopenharmony_ci case IIO_CURRENT: 2048c2ecf20Sopenharmony_ci ret = lm3533_als_get_current(indio_dev, chan->channel, 2058c2ecf20Sopenharmony_ci val); 2068c2ecf20Sopenharmony_ci break; 2078c2ecf20Sopenharmony_ci default: 2088c2ecf20Sopenharmony_ci return -EINVAL; 2098c2ecf20Sopenharmony_ci } 2108c2ecf20Sopenharmony_ci break; 2118c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_AVERAGE_RAW: 2128c2ecf20Sopenharmony_ci ret = lm3533_als_get_adc(indio_dev, true, val); 2138c2ecf20Sopenharmony_ci break; 2148c2ecf20Sopenharmony_ci default: 2158c2ecf20Sopenharmony_ci return -EINVAL; 2168c2ecf20Sopenharmony_ci } 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci if (ret) 2198c2ecf20Sopenharmony_ci return ret; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci return IIO_VAL_INT; 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci#define CHANNEL_CURRENT(_channel) \ 2258c2ecf20Sopenharmony_ci { \ 2268c2ecf20Sopenharmony_ci .type = IIO_CURRENT, \ 2278c2ecf20Sopenharmony_ci .channel = _channel, \ 2288c2ecf20Sopenharmony_ci .indexed = true, \ 2298c2ecf20Sopenharmony_ci .output = true, \ 2308c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ 2318c2ecf20Sopenharmony_ci } 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_cistatic const struct iio_chan_spec lm3533_als_channels[] = { 2348c2ecf20Sopenharmony_ci { 2358c2ecf20Sopenharmony_ci .type = IIO_LIGHT, 2368c2ecf20Sopenharmony_ci .channel = 0, 2378c2ecf20Sopenharmony_ci .indexed = true, 2388c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_AVERAGE_RAW) | 2398c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_RAW), 2408c2ecf20Sopenharmony_ci }, 2418c2ecf20Sopenharmony_ci CHANNEL_CURRENT(0), 2428c2ecf20Sopenharmony_ci CHANNEL_CURRENT(1), 2438c2ecf20Sopenharmony_ci CHANNEL_CURRENT(2), 2448c2ecf20Sopenharmony_ci}; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_cistatic irqreturn_t lm3533_als_isr(int irq, void *dev_id) 2478c2ecf20Sopenharmony_ci{ 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = dev_id; 2508c2ecf20Sopenharmony_ci struct lm3533_als *als = iio_priv(indio_dev); 2518c2ecf20Sopenharmony_ci u8 zone; 2528c2ecf20Sopenharmony_ci int ret; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci /* Clear interrupt by reading the ALS zone register. */ 2558c2ecf20Sopenharmony_ci ret = _lm3533_als_get_zone(indio_dev, &zone); 2568c2ecf20Sopenharmony_ci if (ret) 2578c2ecf20Sopenharmony_ci goto out; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci atomic_set(&als->zone, zone); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci iio_push_event(indio_dev, 2628c2ecf20Sopenharmony_ci IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 2638c2ecf20Sopenharmony_ci 0, 2648c2ecf20Sopenharmony_ci IIO_EV_TYPE_THRESH, 2658c2ecf20Sopenharmony_ci IIO_EV_DIR_EITHER), 2668c2ecf20Sopenharmony_ci iio_get_time_ns(indio_dev)); 2678c2ecf20Sopenharmony_ciout: 2688c2ecf20Sopenharmony_ci return IRQ_HANDLED; 2698c2ecf20Sopenharmony_ci} 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_cistatic int lm3533_als_set_int_mode(struct iio_dev *indio_dev, int enable) 2728c2ecf20Sopenharmony_ci{ 2738c2ecf20Sopenharmony_ci struct lm3533_als *als = iio_priv(indio_dev); 2748c2ecf20Sopenharmony_ci u8 mask = LM3533_ALS_INT_ENABLE_MASK; 2758c2ecf20Sopenharmony_ci u8 val; 2768c2ecf20Sopenharmony_ci int ret; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci if (enable) 2798c2ecf20Sopenharmony_ci val = mask; 2808c2ecf20Sopenharmony_ci else 2818c2ecf20Sopenharmony_ci val = 0; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci ret = lm3533_update(als->lm3533, LM3533_REG_ALS_ZONE_INFO, val, mask); 2848c2ecf20Sopenharmony_ci if (ret) { 2858c2ecf20Sopenharmony_ci dev_err(&indio_dev->dev, "failed to set int mode %d\n", 2868c2ecf20Sopenharmony_ci enable); 2878c2ecf20Sopenharmony_ci return ret; 2888c2ecf20Sopenharmony_ci } 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci return 0; 2918c2ecf20Sopenharmony_ci} 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_cistatic int lm3533_als_get_int_mode(struct iio_dev *indio_dev, int *enable) 2948c2ecf20Sopenharmony_ci{ 2958c2ecf20Sopenharmony_ci struct lm3533_als *als = iio_priv(indio_dev); 2968c2ecf20Sopenharmony_ci u8 mask = LM3533_ALS_INT_ENABLE_MASK; 2978c2ecf20Sopenharmony_ci u8 val; 2988c2ecf20Sopenharmony_ci int ret; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci ret = lm3533_read(als->lm3533, LM3533_REG_ALS_ZONE_INFO, &val); 3018c2ecf20Sopenharmony_ci if (ret) { 3028c2ecf20Sopenharmony_ci dev_err(&indio_dev->dev, "failed to get int mode\n"); 3038c2ecf20Sopenharmony_ci return ret; 3048c2ecf20Sopenharmony_ci } 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci *enable = !!(val & mask); 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci return 0; 3098c2ecf20Sopenharmony_ci} 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_cistatic inline u8 lm3533_als_get_threshold_reg(unsigned nr, bool raising) 3128c2ecf20Sopenharmony_ci{ 3138c2ecf20Sopenharmony_ci u8 offset = !raising; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci return LM3533_REG_ALS_BOUNDARY_BASE + 2 * nr + offset; 3168c2ecf20Sopenharmony_ci} 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_cistatic int lm3533_als_get_threshold(struct iio_dev *indio_dev, unsigned nr, 3198c2ecf20Sopenharmony_ci bool raising, u8 *val) 3208c2ecf20Sopenharmony_ci{ 3218c2ecf20Sopenharmony_ci struct lm3533_als *als = iio_priv(indio_dev); 3228c2ecf20Sopenharmony_ci u8 reg; 3238c2ecf20Sopenharmony_ci int ret; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci if (nr > LM3533_ALS_THRESH_MAX) 3268c2ecf20Sopenharmony_ci return -EINVAL; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci reg = lm3533_als_get_threshold_reg(nr, raising); 3298c2ecf20Sopenharmony_ci ret = lm3533_read(als->lm3533, reg, val); 3308c2ecf20Sopenharmony_ci if (ret) 3318c2ecf20Sopenharmony_ci dev_err(&indio_dev->dev, "failed to get threshold\n"); 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci return ret; 3348c2ecf20Sopenharmony_ci} 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_cistatic int lm3533_als_set_threshold(struct iio_dev *indio_dev, unsigned nr, 3378c2ecf20Sopenharmony_ci bool raising, u8 val) 3388c2ecf20Sopenharmony_ci{ 3398c2ecf20Sopenharmony_ci struct lm3533_als *als = iio_priv(indio_dev); 3408c2ecf20Sopenharmony_ci u8 val2; 3418c2ecf20Sopenharmony_ci u8 reg, reg2; 3428c2ecf20Sopenharmony_ci int ret; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci if (nr > LM3533_ALS_THRESH_MAX) 3458c2ecf20Sopenharmony_ci return -EINVAL; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci reg = lm3533_als_get_threshold_reg(nr, raising); 3488c2ecf20Sopenharmony_ci reg2 = lm3533_als_get_threshold_reg(nr, !raising); 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci mutex_lock(&als->thresh_mutex); 3518c2ecf20Sopenharmony_ci ret = lm3533_read(als->lm3533, reg2, &val2); 3528c2ecf20Sopenharmony_ci if (ret) { 3538c2ecf20Sopenharmony_ci dev_err(&indio_dev->dev, "failed to get threshold\n"); 3548c2ecf20Sopenharmony_ci goto out; 3558c2ecf20Sopenharmony_ci } 3568c2ecf20Sopenharmony_ci /* 3578c2ecf20Sopenharmony_ci * This device does not allow negative hysteresis (in fact, it uses 3588c2ecf20Sopenharmony_ci * whichever value is smaller as the lower bound) so we need to make 3598c2ecf20Sopenharmony_ci * sure that thresh_falling <= thresh_raising. 3608c2ecf20Sopenharmony_ci */ 3618c2ecf20Sopenharmony_ci if ((raising && (val < val2)) || (!raising && (val > val2))) { 3628c2ecf20Sopenharmony_ci ret = -EINVAL; 3638c2ecf20Sopenharmony_ci goto out; 3648c2ecf20Sopenharmony_ci } 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci ret = lm3533_write(als->lm3533, reg, val); 3678c2ecf20Sopenharmony_ci if (ret) { 3688c2ecf20Sopenharmony_ci dev_err(&indio_dev->dev, "failed to set threshold\n"); 3698c2ecf20Sopenharmony_ci goto out; 3708c2ecf20Sopenharmony_ci } 3718c2ecf20Sopenharmony_ciout: 3728c2ecf20Sopenharmony_ci mutex_unlock(&als->thresh_mutex); 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci return ret; 3758c2ecf20Sopenharmony_ci} 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_cistatic int lm3533_als_get_hysteresis(struct iio_dev *indio_dev, unsigned nr, 3788c2ecf20Sopenharmony_ci u8 *val) 3798c2ecf20Sopenharmony_ci{ 3808c2ecf20Sopenharmony_ci struct lm3533_als *als = iio_priv(indio_dev); 3818c2ecf20Sopenharmony_ci u8 falling; 3828c2ecf20Sopenharmony_ci u8 raising; 3838c2ecf20Sopenharmony_ci int ret; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci if (nr > LM3533_ALS_THRESH_MAX) 3868c2ecf20Sopenharmony_ci return -EINVAL; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci mutex_lock(&als->thresh_mutex); 3898c2ecf20Sopenharmony_ci ret = lm3533_als_get_threshold(indio_dev, nr, false, &falling); 3908c2ecf20Sopenharmony_ci if (ret) 3918c2ecf20Sopenharmony_ci goto out; 3928c2ecf20Sopenharmony_ci ret = lm3533_als_get_threshold(indio_dev, nr, true, &raising); 3938c2ecf20Sopenharmony_ci if (ret) 3948c2ecf20Sopenharmony_ci goto out; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci *val = raising - falling; 3978c2ecf20Sopenharmony_ciout: 3988c2ecf20Sopenharmony_ci mutex_unlock(&als->thresh_mutex); 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci return ret; 4018c2ecf20Sopenharmony_ci} 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_cistatic ssize_t show_thresh_either_en(struct device *dev, 4048c2ecf20Sopenharmony_ci struct device_attribute *attr, 4058c2ecf20Sopenharmony_ci char *buf) 4068c2ecf20Sopenharmony_ci{ 4078c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = dev_to_iio_dev(dev); 4088c2ecf20Sopenharmony_ci struct lm3533_als *als = iio_priv(indio_dev); 4098c2ecf20Sopenharmony_ci int enable; 4108c2ecf20Sopenharmony_ci int ret; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci if (als->irq) { 4138c2ecf20Sopenharmony_ci ret = lm3533_als_get_int_mode(indio_dev, &enable); 4148c2ecf20Sopenharmony_ci if (ret) 4158c2ecf20Sopenharmony_ci return ret; 4168c2ecf20Sopenharmony_ci } else { 4178c2ecf20Sopenharmony_ci enable = 0; 4188c2ecf20Sopenharmony_ci } 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci return scnprintf(buf, PAGE_SIZE, "%u\n", enable); 4218c2ecf20Sopenharmony_ci} 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_cistatic ssize_t store_thresh_either_en(struct device *dev, 4248c2ecf20Sopenharmony_ci struct device_attribute *attr, 4258c2ecf20Sopenharmony_ci const char *buf, size_t len) 4268c2ecf20Sopenharmony_ci{ 4278c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = dev_to_iio_dev(dev); 4288c2ecf20Sopenharmony_ci struct lm3533_als *als = iio_priv(indio_dev); 4298c2ecf20Sopenharmony_ci unsigned long enable; 4308c2ecf20Sopenharmony_ci bool int_enabled; 4318c2ecf20Sopenharmony_ci u8 zone; 4328c2ecf20Sopenharmony_ci int ret; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci if (!als->irq) 4358c2ecf20Sopenharmony_ci return -EBUSY; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci if (kstrtoul(buf, 0, &enable)) 4388c2ecf20Sopenharmony_ci return -EINVAL; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci int_enabled = test_bit(LM3533_ALS_FLAG_INT_ENABLED, &als->flags); 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci if (enable && !int_enabled) { 4438c2ecf20Sopenharmony_ci ret = lm3533_als_get_zone(indio_dev, &zone); 4448c2ecf20Sopenharmony_ci if (ret) 4458c2ecf20Sopenharmony_ci return ret; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci atomic_set(&als->zone, zone); 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci set_bit(LM3533_ALS_FLAG_INT_ENABLED, &als->flags); 4508c2ecf20Sopenharmony_ci } 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci ret = lm3533_als_set_int_mode(indio_dev, enable); 4538c2ecf20Sopenharmony_ci if (ret) { 4548c2ecf20Sopenharmony_ci if (!int_enabled) 4558c2ecf20Sopenharmony_ci clear_bit(LM3533_ALS_FLAG_INT_ENABLED, &als->flags); 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci return ret; 4588c2ecf20Sopenharmony_ci } 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci if (!enable) 4618c2ecf20Sopenharmony_ci clear_bit(LM3533_ALS_FLAG_INT_ENABLED, &als->flags); 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci return len; 4648c2ecf20Sopenharmony_ci} 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_cistatic ssize_t show_zone(struct device *dev, 4678c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 4688c2ecf20Sopenharmony_ci{ 4698c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = dev_to_iio_dev(dev); 4708c2ecf20Sopenharmony_ci u8 zone; 4718c2ecf20Sopenharmony_ci int ret; 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci ret = lm3533_als_get_zone(indio_dev, &zone); 4748c2ecf20Sopenharmony_ci if (ret) 4758c2ecf20Sopenharmony_ci return ret; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci return scnprintf(buf, PAGE_SIZE, "%u\n", zone); 4788c2ecf20Sopenharmony_ci} 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_cienum lm3533_als_attribute_type { 4818c2ecf20Sopenharmony_ci LM3533_ATTR_TYPE_HYSTERESIS, 4828c2ecf20Sopenharmony_ci LM3533_ATTR_TYPE_TARGET, 4838c2ecf20Sopenharmony_ci LM3533_ATTR_TYPE_THRESH_FALLING, 4848c2ecf20Sopenharmony_ci LM3533_ATTR_TYPE_THRESH_RAISING, 4858c2ecf20Sopenharmony_ci}; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_cistruct lm3533_als_attribute { 4888c2ecf20Sopenharmony_ci struct device_attribute dev_attr; 4898c2ecf20Sopenharmony_ci enum lm3533_als_attribute_type type; 4908c2ecf20Sopenharmony_ci u8 val1; 4918c2ecf20Sopenharmony_ci u8 val2; 4928c2ecf20Sopenharmony_ci}; 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_cistatic inline struct lm3533_als_attribute * 4958c2ecf20Sopenharmony_cito_lm3533_als_attr(struct device_attribute *attr) 4968c2ecf20Sopenharmony_ci{ 4978c2ecf20Sopenharmony_ci return container_of(attr, struct lm3533_als_attribute, dev_attr); 4988c2ecf20Sopenharmony_ci} 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_cistatic ssize_t show_als_attr(struct device *dev, 5018c2ecf20Sopenharmony_ci struct device_attribute *attr, 5028c2ecf20Sopenharmony_ci char *buf) 5038c2ecf20Sopenharmony_ci{ 5048c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = dev_to_iio_dev(dev); 5058c2ecf20Sopenharmony_ci struct lm3533_als_attribute *als_attr = to_lm3533_als_attr(attr); 5068c2ecf20Sopenharmony_ci u8 val; 5078c2ecf20Sopenharmony_ci int ret; 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci switch (als_attr->type) { 5108c2ecf20Sopenharmony_ci case LM3533_ATTR_TYPE_HYSTERESIS: 5118c2ecf20Sopenharmony_ci ret = lm3533_als_get_hysteresis(indio_dev, als_attr->val1, 5128c2ecf20Sopenharmony_ci &val); 5138c2ecf20Sopenharmony_ci break; 5148c2ecf20Sopenharmony_ci case LM3533_ATTR_TYPE_TARGET: 5158c2ecf20Sopenharmony_ci ret = lm3533_als_get_target(indio_dev, als_attr->val1, 5168c2ecf20Sopenharmony_ci als_attr->val2, &val); 5178c2ecf20Sopenharmony_ci break; 5188c2ecf20Sopenharmony_ci case LM3533_ATTR_TYPE_THRESH_FALLING: 5198c2ecf20Sopenharmony_ci ret = lm3533_als_get_threshold(indio_dev, als_attr->val1, 5208c2ecf20Sopenharmony_ci false, &val); 5218c2ecf20Sopenharmony_ci break; 5228c2ecf20Sopenharmony_ci case LM3533_ATTR_TYPE_THRESH_RAISING: 5238c2ecf20Sopenharmony_ci ret = lm3533_als_get_threshold(indio_dev, als_attr->val1, 5248c2ecf20Sopenharmony_ci true, &val); 5258c2ecf20Sopenharmony_ci break; 5268c2ecf20Sopenharmony_ci default: 5278c2ecf20Sopenharmony_ci ret = -ENXIO; 5288c2ecf20Sopenharmony_ci } 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci if (ret) 5318c2ecf20Sopenharmony_ci return ret; 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci return scnprintf(buf, PAGE_SIZE, "%u\n", val); 5348c2ecf20Sopenharmony_ci} 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_cistatic ssize_t store_als_attr(struct device *dev, 5378c2ecf20Sopenharmony_ci struct device_attribute *attr, 5388c2ecf20Sopenharmony_ci const char *buf, size_t len) 5398c2ecf20Sopenharmony_ci{ 5408c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = dev_to_iio_dev(dev); 5418c2ecf20Sopenharmony_ci struct lm3533_als_attribute *als_attr = to_lm3533_als_attr(attr); 5428c2ecf20Sopenharmony_ci u8 val; 5438c2ecf20Sopenharmony_ci int ret; 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci if (kstrtou8(buf, 0, &val)) 5468c2ecf20Sopenharmony_ci return -EINVAL; 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci switch (als_attr->type) { 5498c2ecf20Sopenharmony_ci case LM3533_ATTR_TYPE_TARGET: 5508c2ecf20Sopenharmony_ci ret = lm3533_als_set_target(indio_dev, als_attr->val1, 5518c2ecf20Sopenharmony_ci als_attr->val2, val); 5528c2ecf20Sopenharmony_ci break; 5538c2ecf20Sopenharmony_ci case LM3533_ATTR_TYPE_THRESH_FALLING: 5548c2ecf20Sopenharmony_ci ret = lm3533_als_set_threshold(indio_dev, als_attr->val1, 5558c2ecf20Sopenharmony_ci false, val); 5568c2ecf20Sopenharmony_ci break; 5578c2ecf20Sopenharmony_ci case LM3533_ATTR_TYPE_THRESH_RAISING: 5588c2ecf20Sopenharmony_ci ret = lm3533_als_set_threshold(indio_dev, als_attr->val1, 5598c2ecf20Sopenharmony_ci true, val); 5608c2ecf20Sopenharmony_ci break; 5618c2ecf20Sopenharmony_ci default: 5628c2ecf20Sopenharmony_ci ret = -ENXIO; 5638c2ecf20Sopenharmony_ci } 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci if (ret) 5668c2ecf20Sopenharmony_ci return ret; 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci return len; 5698c2ecf20Sopenharmony_ci} 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci#define ALS_ATTR(_name, _mode, _show, _store, _type, _val1, _val2) \ 5728c2ecf20Sopenharmony_ci { .dev_attr = __ATTR(_name, _mode, _show, _store), \ 5738c2ecf20Sopenharmony_ci .type = _type, \ 5748c2ecf20Sopenharmony_ci .val1 = _val1, \ 5758c2ecf20Sopenharmony_ci .val2 = _val2 } 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci#define LM3533_ALS_ATTR(_name, _mode, _show, _store, _type, _val1, _val2) \ 5788c2ecf20Sopenharmony_ci struct lm3533_als_attribute lm3533_als_attr_##_name = \ 5798c2ecf20Sopenharmony_ci ALS_ATTR(_name, _mode, _show, _store, _type, _val1, _val2) 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci#define ALS_TARGET_ATTR_RW(_channel, _zone) \ 5828c2ecf20Sopenharmony_ci LM3533_ALS_ATTR(out_current##_channel##_current##_zone##_raw, \ 5838c2ecf20Sopenharmony_ci S_IRUGO | S_IWUSR, \ 5848c2ecf20Sopenharmony_ci show_als_attr, store_als_attr, \ 5858c2ecf20Sopenharmony_ci LM3533_ATTR_TYPE_TARGET, _channel, _zone) 5868c2ecf20Sopenharmony_ci/* 5878c2ecf20Sopenharmony_ci * ALS output current values (ALS mapper targets) 5888c2ecf20Sopenharmony_ci * 5898c2ecf20Sopenharmony_ci * out_current[0-2]_current[0-4]_raw 0-255 5908c2ecf20Sopenharmony_ci */ 5918c2ecf20Sopenharmony_cistatic ALS_TARGET_ATTR_RW(0, 0); 5928c2ecf20Sopenharmony_cistatic ALS_TARGET_ATTR_RW(0, 1); 5938c2ecf20Sopenharmony_cistatic ALS_TARGET_ATTR_RW(0, 2); 5948c2ecf20Sopenharmony_cistatic ALS_TARGET_ATTR_RW(0, 3); 5958c2ecf20Sopenharmony_cistatic ALS_TARGET_ATTR_RW(0, 4); 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_cistatic ALS_TARGET_ATTR_RW(1, 0); 5988c2ecf20Sopenharmony_cistatic ALS_TARGET_ATTR_RW(1, 1); 5998c2ecf20Sopenharmony_cistatic ALS_TARGET_ATTR_RW(1, 2); 6008c2ecf20Sopenharmony_cistatic ALS_TARGET_ATTR_RW(1, 3); 6018c2ecf20Sopenharmony_cistatic ALS_TARGET_ATTR_RW(1, 4); 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_cistatic ALS_TARGET_ATTR_RW(2, 0); 6048c2ecf20Sopenharmony_cistatic ALS_TARGET_ATTR_RW(2, 1); 6058c2ecf20Sopenharmony_cistatic ALS_TARGET_ATTR_RW(2, 2); 6068c2ecf20Sopenharmony_cistatic ALS_TARGET_ATTR_RW(2, 3); 6078c2ecf20Sopenharmony_cistatic ALS_TARGET_ATTR_RW(2, 4); 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci#define ALS_THRESH_FALLING_ATTR_RW(_nr) \ 6108c2ecf20Sopenharmony_ci LM3533_ALS_ATTR(in_illuminance0_thresh##_nr##_falling_value, \ 6118c2ecf20Sopenharmony_ci S_IRUGO | S_IWUSR, \ 6128c2ecf20Sopenharmony_ci show_als_attr, store_als_attr, \ 6138c2ecf20Sopenharmony_ci LM3533_ATTR_TYPE_THRESH_FALLING, _nr, 0) 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci#define ALS_THRESH_RAISING_ATTR_RW(_nr) \ 6168c2ecf20Sopenharmony_ci LM3533_ALS_ATTR(in_illuminance0_thresh##_nr##_raising_value, \ 6178c2ecf20Sopenharmony_ci S_IRUGO | S_IWUSR, \ 6188c2ecf20Sopenharmony_ci show_als_attr, store_als_attr, \ 6198c2ecf20Sopenharmony_ci LM3533_ATTR_TYPE_THRESH_RAISING, _nr, 0) 6208c2ecf20Sopenharmony_ci/* 6218c2ecf20Sopenharmony_ci * ALS Zone thresholds (boundaries) 6228c2ecf20Sopenharmony_ci * 6238c2ecf20Sopenharmony_ci * in_illuminance0_thresh[0-3]_falling_value 0-255 6248c2ecf20Sopenharmony_ci * in_illuminance0_thresh[0-3]_raising_value 0-255 6258c2ecf20Sopenharmony_ci */ 6268c2ecf20Sopenharmony_cistatic ALS_THRESH_FALLING_ATTR_RW(0); 6278c2ecf20Sopenharmony_cistatic ALS_THRESH_FALLING_ATTR_RW(1); 6288c2ecf20Sopenharmony_cistatic ALS_THRESH_FALLING_ATTR_RW(2); 6298c2ecf20Sopenharmony_cistatic ALS_THRESH_FALLING_ATTR_RW(3); 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_cistatic ALS_THRESH_RAISING_ATTR_RW(0); 6328c2ecf20Sopenharmony_cistatic ALS_THRESH_RAISING_ATTR_RW(1); 6338c2ecf20Sopenharmony_cistatic ALS_THRESH_RAISING_ATTR_RW(2); 6348c2ecf20Sopenharmony_cistatic ALS_THRESH_RAISING_ATTR_RW(3); 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci#define ALS_HYSTERESIS_ATTR_RO(_nr) \ 6378c2ecf20Sopenharmony_ci LM3533_ALS_ATTR(in_illuminance0_thresh##_nr##_hysteresis, \ 6388c2ecf20Sopenharmony_ci S_IRUGO, show_als_attr, NULL, \ 6398c2ecf20Sopenharmony_ci LM3533_ATTR_TYPE_HYSTERESIS, _nr, 0) 6408c2ecf20Sopenharmony_ci/* 6418c2ecf20Sopenharmony_ci * ALS Zone threshold hysteresis 6428c2ecf20Sopenharmony_ci * 6438c2ecf20Sopenharmony_ci * threshY_hysteresis = threshY_raising - threshY_falling 6448c2ecf20Sopenharmony_ci * 6458c2ecf20Sopenharmony_ci * in_illuminance0_thresh[0-3]_hysteresis 0-255 6468c2ecf20Sopenharmony_ci * in_illuminance0_thresh[0-3]_hysteresis 0-255 6478c2ecf20Sopenharmony_ci */ 6488c2ecf20Sopenharmony_cistatic ALS_HYSTERESIS_ATTR_RO(0); 6498c2ecf20Sopenharmony_cistatic ALS_HYSTERESIS_ATTR_RO(1); 6508c2ecf20Sopenharmony_cistatic ALS_HYSTERESIS_ATTR_RO(2); 6518c2ecf20Sopenharmony_cistatic ALS_HYSTERESIS_ATTR_RO(3); 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci#define ILLUMINANCE_ATTR_RO(_name) \ 6548c2ecf20Sopenharmony_ci DEVICE_ATTR(in_illuminance0_##_name, S_IRUGO, show_##_name, NULL) 6558c2ecf20Sopenharmony_ci#define ILLUMINANCE_ATTR_RW(_name) \ 6568c2ecf20Sopenharmony_ci DEVICE_ATTR(in_illuminance0_##_name, S_IRUGO | S_IWUSR, \ 6578c2ecf20Sopenharmony_ci show_##_name, store_##_name) 6588c2ecf20Sopenharmony_ci/* 6598c2ecf20Sopenharmony_ci * ALS Zone threshold-event enable 6608c2ecf20Sopenharmony_ci * 6618c2ecf20Sopenharmony_ci * in_illuminance0_thresh_either_en 0,1 6628c2ecf20Sopenharmony_ci */ 6638c2ecf20Sopenharmony_cistatic ILLUMINANCE_ATTR_RW(thresh_either_en); 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci/* 6668c2ecf20Sopenharmony_ci * ALS Current Zone 6678c2ecf20Sopenharmony_ci * 6688c2ecf20Sopenharmony_ci * in_illuminance0_zone 0-4 6698c2ecf20Sopenharmony_ci */ 6708c2ecf20Sopenharmony_cistatic ILLUMINANCE_ATTR_RO(zone); 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_cistatic struct attribute *lm3533_als_event_attributes[] = { 6738c2ecf20Sopenharmony_ci &dev_attr_in_illuminance0_thresh_either_en.attr, 6748c2ecf20Sopenharmony_ci &lm3533_als_attr_in_illuminance0_thresh0_falling_value.dev_attr.attr, 6758c2ecf20Sopenharmony_ci &lm3533_als_attr_in_illuminance0_thresh0_hysteresis.dev_attr.attr, 6768c2ecf20Sopenharmony_ci &lm3533_als_attr_in_illuminance0_thresh0_raising_value.dev_attr.attr, 6778c2ecf20Sopenharmony_ci &lm3533_als_attr_in_illuminance0_thresh1_falling_value.dev_attr.attr, 6788c2ecf20Sopenharmony_ci &lm3533_als_attr_in_illuminance0_thresh1_hysteresis.dev_attr.attr, 6798c2ecf20Sopenharmony_ci &lm3533_als_attr_in_illuminance0_thresh1_raising_value.dev_attr.attr, 6808c2ecf20Sopenharmony_ci &lm3533_als_attr_in_illuminance0_thresh2_falling_value.dev_attr.attr, 6818c2ecf20Sopenharmony_ci &lm3533_als_attr_in_illuminance0_thresh2_hysteresis.dev_attr.attr, 6828c2ecf20Sopenharmony_ci &lm3533_als_attr_in_illuminance0_thresh2_raising_value.dev_attr.attr, 6838c2ecf20Sopenharmony_ci &lm3533_als_attr_in_illuminance0_thresh3_falling_value.dev_attr.attr, 6848c2ecf20Sopenharmony_ci &lm3533_als_attr_in_illuminance0_thresh3_hysteresis.dev_attr.attr, 6858c2ecf20Sopenharmony_ci &lm3533_als_attr_in_illuminance0_thresh3_raising_value.dev_attr.attr, 6868c2ecf20Sopenharmony_ci NULL 6878c2ecf20Sopenharmony_ci}; 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_cistatic const struct attribute_group lm3533_als_event_attribute_group = { 6908c2ecf20Sopenharmony_ci .attrs = lm3533_als_event_attributes 6918c2ecf20Sopenharmony_ci}; 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_cistatic struct attribute *lm3533_als_attributes[] = { 6948c2ecf20Sopenharmony_ci &dev_attr_in_illuminance0_zone.attr, 6958c2ecf20Sopenharmony_ci &lm3533_als_attr_out_current0_current0_raw.dev_attr.attr, 6968c2ecf20Sopenharmony_ci &lm3533_als_attr_out_current0_current1_raw.dev_attr.attr, 6978c2ecf20Sopenharmony_ci &lm3533_als_attr_out_current0_current2_raw.dev_attr.attr, 6988c2ecf20Sopenharmony_ci &lm3533_als_attr_out_current0_current3_raw.dev_attr.attr, 6998c2ecf20Sopenharmony_ci &lm3533_als_attr_out_current0_current4_raw.dev_attr.attr, 7008c2ecf20Sopenharmony_ci &lm3533_als_attr_out_current1_current0_raw.dev_attr.attr, 7018c2ecf20Sopenharmony_ci &lm3533_als_attr_out_current1_current1_raw.dev_attr.attr, 7028c2ecf20Sopenharmony_ci &lm3533_als_attr_out_current1_current2_raw.dev_attr.attr, 7038c2ecf20Sopenharmony_ci &lm3533_als_attr_out_current1_current3_raw.dev_attr.attr, 7048c2ecf20Sopenharmony_ci &lm3533_als_attr_out_current1_current4_raw.dev_attr.attr, 7058c2ecf20Sopenharmony_ci &lm3533_als_attr_out_current2_current0_raw.dev_attr.attr, 7068c2ecf20Sopenharmony_ci &lm3533_als_attr_out_current2_current1_raw.dev_attr.attr, 7078c2ecf20Sopenharmony_ci &lm3533_als_attr_out_current2_current2_raw.dev_attr.attr, 7088c2ecf20Sopenharmony_ci &lm3533_als_attr_out_current2_current3_raw.dev_attr.attr, 7098c2ecf20Sopenharmony_ci &lm3533_als_attr_out_current2_current4_raw.dev_attr.attr, 7108c2ecf20Sopenharmony_ci NULL 7118c2ecf20Sopenharmony_ci}; 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_cistatic const struct attribute_group lm3533_als_attribute_group = { 7148c2ecf20Sopenharmony_ci .attrs = lm3533_als_attributes 7158c2ecf20Sopenharmony_ci}; 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_cistatic int lm3533_als_set_input_mode(struct lm3533_als *als, bool pwm_mode) 7188c2ecf20Sopenharmony_ci{ 7198c2ecf20Sopenharmony_ci u8 mask = LM3533_ALS_INPUT_MODE_MASK; 7208c2ecf20Sopenharmony_ci u8 val; 7218c2ecf20Sopenharmony_ci int ret; 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci if (pwm_mode) 7248c2ecf20Sopenharmony_ci val = mask; /* pwm input */ 7258c2ecf20Sopenharmony_ci else 7268c2ecf20Sopenharmony_ci val = 0; /* analog input */ 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci ret = lm3533_update(als->lm3533, LM3533_REG_ALS_CONF, val, mask); 7298c2ecf20Sopenharmony_ci if (ret) { 7308c2ecf20Sopenharmony_ci dev_err(&als->pdev->dev, "failed to set input mode %d\n", 7318c2ecf20Sopenharmony_ci pwm_mode); 7328c2ecf20Sopenharmony_ci return ret; 7338c2ecf20Sopenharmony_ci } 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci return 0; 7368c2ecf20Sopenharmony_ci} 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_cistatic int lm3533_als_set_resistor(struct lm3533_als *als, u8 val) 7398c2ecf20Sopenharmony_ci{ 7408c2ecf20Sopenharmony_ci int ret; 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci if (val < LM3533_ALS_RESISTOR_MIN || val > LM3533_ALS_RESISTOR_MAX) { 7438c2ecf20Sopenharmony_ci dev_err(&als->pdev->dev, "invalid resistor value\n"); 7448c2ecf20Sopenharmony_ci return -EINVAL; 7458c2ecf20Sopenharmony_ci } 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci ret = lm3533_write(als->lm3533, LM3533_REG_ALS_RESISTOR_SELECT, val); 7488c2ecf20Sopenharmony_ci if (ret) { 7498c2ecf20Sopenharmony_ci dev_err(&als->pdev->dev, "failed to set resistor\n"); 7508c2ecf20Sopenharmony_ci return ret; 7518c2ecf20Sopenharmony_ci } 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci return 0; 7548c2ecf20Sopenharmony_ci} 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_cistatic int lm3533_als_setup(struct lm3533_als *als, 7578c2ecf20Sopenharmony_ci struct lm3533_als_platform_data *pdata) 7588c2ecf20Sopenharmony_ci{ 7598c2ecf20Sopenharmony_ci int ret; 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci ret = lm3533_als_set_input_mode(als, pdata->pwm_mode); 7628c2ecf20Sopenharmony_ci if (ret) 7638c2ecf20Sopenharmony_ci return ret; 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci /* ALS input is always high impedance in PWM-mode. */ 7668c2ecf20Sopenharmony_ci if (!pdata->pwm_mode) { 7678c2ecf20Sopenharmony_ci ret = lm3533_als_set_resistor(als, pdata->r_select); 7688c2ecf20Sopenharmony_ci if (ret) 7698c2ecf20Sopenharmony_ci return ret; 7708c2ecf20Sopenharmony_ci } 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci return 0; 7738c2ecf20Sopenharmony_ci} 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_cistatic int lm3533_als_setup_irq(struct lm3533_als *als, void *dev) 7768c2ecf20Sopenharmony_ci{ 7778c2ecf20Sopenharmony_ci u8 mask = LM3533_ALS_INT_ENABLE_MASK; 7788c2ecf20Sopenharmony_ci int ret; 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci /* Make sure interrupts are disabled. */ 7818c2ecf20Sopenharmony_ci ret = lm3533_update(als->lm3533, LM3533_REG_ALS_ZONE_INFO, 0, mask); 7828c2ecf20Sopenharmony_ci if (ret) { 7838c2ecf20Sopenharmony_ci dev_err(&als->pdev->dev, "failed to disable interrupts\n"); 7848c2ecf20Sopenharmony_ci return ret; 7858c2ecf20Sopenharmony_ci } 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci ret = request_threaded_irq(als->irq, NULL, lm3533_als_isr, 7888c2ecf20Sopenharmony_ci IRQF_TRIGGER_LOW | IRQF_ONESHOT, 7898c2ecf20Sopenharmony_ci dev_name(&als->pdev->dev), dev); 7908c2ecf20Sopenharmony_ci if (ret) { 7918c2ecf20Sopenharmony_ci dev_err(&als->pdev->dev, "failed to request irq %d\n", 7928c2ecf20Sopenharmony_ci als->irq); 7938c2ecf20Sopenharmony_ci return ret; 7948c2ecf20Sopenharmony_ci } 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci return 0; 7978c2ecf20Sopenharmony_ci} 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_cistatic int lm3533_als_enable(struct lm3533_als *als) 8008c2ecf20Sopenharmony_ci{ 8018c2ecf20Sopenharmony_ci u8 mask = LM3533_ALS_ENABLE_MASK; 8028c2ecf20Sopenharmony_ci int ret; 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci ret = lm3533_update(als->lm3533, LM3533_REG_ALS_CONF, mask, mask); 8058c2ecf20Sopenharmony_ci if (ret) 8068c2ecf20Sopenharmony_ci dev_err(&als->pdev->dev, "failed to enable ALS\n"); 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci return ret; 8098c2ecf20Sopenharmony_ci} 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_cistatic int lm3533_als_disable(struct lm3533_als *als) 8128c2ecf20Sopenharmony_ci{ 8138c2ecf20Sopenharmony_ci u8 mask = LM3533_ALS_ENABLE_MASK; 8148c2ecf20Sopenharmony_ci int ret; 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci ret = lm3533_update(als->lm3533, LM3533_REG_ALS_CONF, 0, mask); 8178c2ecf20Sopenharmony_ci if (ret) 8188c2ecf20Sopenharmony_ci dev_err(&als->pdev->dev, "failed to disable ALS\n"); 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci return ret; 8218c2ecf20Sopenharmony_ci} 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_cistatic const struct iio_info lm3533_als_info = { 8248c2ecf20Sopenharmony_ci .attrs = &lm3533_als_attribute_group, 8258c2ecf20Sopenharmony_ci .event_attrs = &lm3533_als_event_attribute_group, 8268c2ecf20Sopenharmony_ci .read_raw = &lm3533_als_read_raw, 8278c2ecf20Sopenharmony_ci}; 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_cistatic int lm3533_als_probe(struct platform_device *pdev) 8308c2ecf20Sopenharmony_ci{ 8318c2ecf20Sopenharmony_ci struct lm3533 *lm3533; 8328c2ecf20Sopenharmony_ci struct lm3533_als_platform_data *pdata; 8338c2ecf20Sopenharmony_ci struct lm3533_als *als; 8348c2ecf20Sopenharmony_ci struct iio_dev *indio_dev; 8358c2ecf20Sopenharmony_ci int ret; 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci lm3533 = dev_get_drvdata(pdev->dev.parent); 8388c2ecf20Sopenharmony_ci if (!lm3533) 8398c2ecf20Sopenharmony_ci return -EINVAL; 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci pdata = pdev->dev.platform_data; 8428c2ecf20Sopenharmony_ci if (!pdata) { 8438c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "no platform data\n"); 8448c2ecf20Sopenharmony_ci return -EINVAL; 8458c2ecf20Sopenharmony_ci } 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_ci indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*als)); 8488c2ecf20Sopenharmony_ci if (!indio_dev) 8498c2ecf20Sopenharmony_ci return -ENOMEM; 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci indio_dev->info = &lm3533_als_info; 8528c2ecf20Sopenharmony_ci indio_dev->channels = lm3533_als_channels; 8538c2ecf20Sopenharmony_ci indio_dev->num_channels = ARRAY_SIZE(lm3533_als_channels); 8548c2ecf20Sopenharmony_ci indio_dev->name = dev_name(&pdev->dev); 8558c2ecf20Sopenharmony_ci iio_device_set_parent(indio_dev, pdev->dev.parent); 8568c2ecf20Sopenharmony_ci indio_dev->modes = INDIO_DIRECT_MODE; 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci als = iio_priv(indio_dev); 8598c2ecf20Sopenharmony_ci als->lm3533 = lm3533; 8608c2ecf20Sopenharmony_ci als->pdev = pdev; 8618c2ecf20Sopenharmony_ci als->irq = lm3533->irq; 8628c2ecf20Sopenharmony_ci atomic_set(&als->zone, 0); 8638c2ecf20Sopenharmony_ci mutex_init(&als->thresh_mutex); 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, indio_dev); 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci if (als->irq) { 8688c2ecf20Sopenharmony_ci ret = lm3533_als_setup_irq(als, indio_dev); 8698c2ecf20Sopenharmony_ci if (ret) 8708c2ecf20Sopenharmony_ci return ret; 8718c2ecf20Sopenharmony_ci } 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci ret = lm3533_als_setup(als, pdata); 8748c2ecf20Sopenharmony_ci if (ret) 8758c2ecf20Sopenharmony_ci goto err_free_irq; 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci ret = lm3533_als_enable(als); 8788c2ecf20Sopenharmony_ci if (ret) 8798c2ecf20Sopenharmony_ci goto err_free_irq; 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_ci ret = iio_device_register(indio_dev); 8828c2ecf20Sopenharmony_ci if (ret) { 8838c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to register ALS\n"); 8848c2ecf20Sopenharmony_ci goto err_disable; 8858c2ecf20Sopenharmony_ci } 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci return 0; 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_cierr_disable: 8908c2ecf20Sopenharmony_ci lm3533_als_disable(als); 8918c2ecf20Sopenharmony_cierr_free_irq: 8928c2ecf20Sopenharmony_ci if (als->irq) 8938c2ecf20Sopenharmony_ci free_irq(als->irq, indio_dev); 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci return ret; 8968c2ecf20Sopenharmony_ci} 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_cistatic int lm3533_als_remove(struct platform_device *pdev) 8998c2ecf20Sopenharmony_ci{ 9008c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = platform_get_drvdata(pdev); 9018c2ecf20Sopenharmony_ci struct lm3533_als *als = iio_priv(indio_dev); 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci lm3533_als_set_int_mode(indio_dev, false); 9048c2ecf20Sopenharmony_ci iio_device_unregister(indio_dev); 9058c2ecf20Sopenharmony_ci lm3533_als_disable(als); 9068c2ecf20Sopenharmony_ci if (als->irq) 9078c2ecf20Sopenharmony_ci free_irq(als->irq, indio_dev); 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci return 0; 9108c2ecf20Sopenharmony_ci} 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_cistatic struct platform_driver lm3533_als_driver = { 9138c2ecf20Sopenharmony_ci .driver = { 9148c2ecf20Sopenharmony_ci .name = "lm3533-als", 9158c2ecf20Sopenharmony_ci }, 9168c2ecf20Sopenharmony_ci .probe = lm3533_als_probe, 9178c2ecf20Sopenharmony_ci .remove = lm3533_als_remove, 9188c2ecf20Sopenharmony_ci}; 9198c2ecf20Sopenharmony_cimodule_platform_driver(lm3533_als_driver); 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ciMODULE_AUTHOR("Johan Hovold <jhovold@gmail.com>"); 9228c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("LM3533 Ambient Light Sensor driver"); 9238c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 9248c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:lm3533-als"); 925