18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Azoteq IQS621/622 Ambient Light Sensors 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2019 Jeff LaBundy <jeff@labundy.com> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/device.h> 98c2ecf20Sopenharmony_ci#include <linux/iio/events.h> 108c2ecf20Sopenharmony_ci#include <linux/iio/iio.h> 118c2ecf20Sopenharmony_ci#include <linux/kernel.h> 128c2ecf20Sopenharmony_ci#include <linux/mfd/iqs62x.h> 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/mutex.h> 158c2ecf20Sopenharmony_ci#include <linux/notifier.h> 168c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 178c2ecf20Sopenharmony_ci#include <linux/regmap.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#define IQS621_ALS_FLAGS_LIGHT BIT(7) 208c2ecf20Sopenharmony_ci#define IQS621_ALS_FLAGS_RANGE GENMASK(3, 0) 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#define IQS621_ALS_UI_OUT 0x17 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#define IQS621_ALS_THRESH_DARK 0x80 258c2ecf20Sopenharmony_ci#define IQS621_ALS_THRESH_LIGHT 0x81 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define IQS622_IR_RANGE 0x15 288c2ecf20Sopenharmony_ci#define IQS622_IR_FLAGS 0x16 298c2ecf20Sopenharmony_ci#define IQS622_IR_FLAGS_TOUCH BIT(1) 308c2ecf20Sopenharmony_ci#define IQS622_IR_FLAGS_PROX BIT(0) 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#define IQS622_IR_UI_OUT 0x17 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#define IQS622_IR_THRESH_PROX 0x91 358c2ecf20Sopenharmony_ci#define IQS622_IR_THRESH_TOUCH 0x92 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistruct iqs621_als_private { 388c2ecf20Sopenharmony_ci struct iqs62x_core *iqs62x; 398c2ecf20Sopenharmony_ci struct iio_dev *indio_dev; 408c2ecf20Sopenharmony_ci struct notifier_block notifier; 418c2ecf20Sopenharmony_ci struct mutex lock; 428c2ecf20Sopenharmony_ci bool light_en; 438c2ecf20Sopenharmony_ci bool range_en; 448c2ecf20Sopenharmony_ci bool prox_en; 458c2ecf20Sopenharmony_ci u8 als_flags; 468c2ecf20Sopenharmony_ci u8 ir_flags_mask; 478c2ecf20Sopenharmony_ci u8 ir_flags; 488c2ecf20Sopenharmony_ci u8 thresh_light; 498c2ecf20Sopenharmony_ci u8 thresh_dark; 508c2ecf20Sopenharmony_ci u8 thresh_prox; 518c2ecf20Sopenharmony_ci}; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic int iqs621_als_init(struct iqs621_als_private *iqs621_als) 548c2ecf20Sopenharmony_ci{ 558c2ecf20Sopenharmony_ci struct iqs62x_core *iqs62x = iqs621_als->iqs62x; 568c2ecf20Sopenharmony_ci unsigned int event_mask = 0; 578c2ecf20Sopenharmony_ci int ret; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci switch (iqs621_als->ir_flags_mask) { 608c2ecf20Sopenharmony_ci case IQS622_IR_FLAGS_TOUCH: 618c2ecf20Sopenharmony_ci ret = regmap_write(iqs62x->regmap, IQS622_IR_THRESH_TOUCH, 628c2ecf20Sopenharmony_ci iqs621_als->thresh_prox); 638c2ecf20Sopenharmony_ci break; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci case IQS622_IR_FLAGS_PROX: 668c2ecf20Sopenharmony_ci ret = regmap_write(iqs62x->regmap, IQS622_IR_THRESH_PROX, 678c2ecf20Sopenharmony_ci iqs621_als->thresh_prox); 688c2ecf20Sopenharmony_ci break; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci default: 718c2ecf20Sopenharmony_ci ret = regmap_write(iqs62x->regmap, IQS621_ALS_THRESH_LIGHT, 728c2ecf20Sopenharmony_ci iqs621_als->thresh_light); 738c2ecf20Sopenharmony_ci if (ret) 748c2ecf20Sopenharmony_ci return ret; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci ret = regmap_write(iqs62x->regmap, IQS621_ALS_THRESH_DARK, 778c2ecf20Sopenharmony_ci iqs621_als->thresh_dark); 788c2ecf20Sopenharmony_ci } 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci if (ret) 818c2ecf20Sopenharmony_ci return ret; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci if (iqs621_als->light_en || iqs621_als->range_en) 848c2ecf20Sopenharmony_ci event_mask |= iqs62x->dev_desc->als_mask; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci if (iqs621_als->prox_en) 878c2ecf20Sopenharmony_ci event_mask |= iqs62x->dev_desc->ir_mask; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci return regmap_update_bits(iqs62x->regmap, IQS620_GLBL_EVENT_MASK, 908c2ecf20Sopenharmony_ci event_mask, 0); 918c2ecf20Sopenharmony_ci} 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cistatic int iqs621_als_notifier(struct notifier_block *notifier, 948c2ecf20Sopenharmony_ci unsigned long event_flags, void *context) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci struct iqs62x_event_data *event_data = context; 978c2ecf20Sopenharmony_ci struct iqs621_als_private *iqs621_als; 988c2ecf20Sopenharmony_ci struct iio_dev *indio_dev; 998c2ecf20Sopenharmony_ci bool light_new, light_old; 1008c2ecf20Sopenharmony_ci bool prox_new, prox_old; 1018c2ecf20Sopenharmony_ci u8 range_new, range_old; 1028c2ecf20Sopenharmony_ci s64 timestamp; 1038c2ecf20Sopenharmony_ci int ret; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci iqs621_als = container_of(notifier, struct iqs621_als_private, 1068c2ecf20Sopenharmony_ci notifier); 1078c2ecf20Sopenharmony_ci indio_dev = iqs621_als->indio_dev; 1088c2ecf20Sopenharmony_ci timestamp = iio_get_time_ns(indio_dev); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci mutex_lock(&iqs621_als->lock); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci if (event_flags & BIT(IQS62X_EVENT_SYS_RESET)) { 1138c2ecf20Sopenharmony_ci ret = iqs621_als_init(iqs621_als); 1148c2ecf20Sopenharmony_ci if (ret) { 1158c2ecf20Sopenharmony_ci dev_err(indio_dev->dev.parent, 1168c2ecf20Sopenharmony_ci "Failed to re-initialize device: %d\n", ret); 1178c2ecf20Sopenharmony_ci ret = NOTIFY_BAD; 1188c2ecf20Sopenharmony_ci } else { 1198c2ecf20Sopenharmony_ci ret = NOTIFY_OK; 1208c2ecf20Sopenharmony_ci } 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci goto err_mutex; 1238c2ecf20Sopenharmony_ci } 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci if (!iqs621_als->light_en && !iqs621_als->range_en && 1268c2ecf20Sopenharmony_ci !iqs621_als->prox_en) { 1278c2ecf20Sopenharmony_ci ret = NOTIFY_DONE; 1288c2ecf20Sopenharmony_ci goto err_mutex; 1298c2ecf20Sopenharmony_ci } 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci /* IQS621 only */ 1328c2ecf20Sopenharmony_ci light_new = event_data->als_flags & IQS621_ALS_FLAGS_LIGHT; 1338c2ecf20Sopenharmony_ci light_old = iqs621_als->als_flags & IQS621_ALS_FLAGS_LIGHT; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci if (iqs621_als->light_en && light_new && !light_old) 1368c2ecf20Sopenharmony_ci iio_push_event(indio_dev, 1378c2ecf20Sopenharmony_ci IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0, 1388c2ecf20Sopenharmony_ci IIO_EV_TYPE_THRESH, 1398c2ecf20Sopenharmony_ci IIO_EV_DIR_RISING), 1408c2ecf20Sopenharmony_ci timestamp); 1418c2ecf20Sopenharmony_ci else if (iqs621_als->light_en && !light_new && light_old) 1428c2ecf20Sopenharmony_ci iio_push_event(indio_dev, 1438c2ecf20Sopenharmony_ci IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0, 1448c2ecf20Sopenharmony_ci IIO_EV_TYPE_THRESH, 1458c2ecf20Sopenharmony_ci IIO_EV_DIR_FALLING), 1468c2ecf20Sopenharmony_ci timestamp); 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci /* IQS621 and IQS622 */ 1498c2ecf20Sopenharmony_ci range_new = event_data->als_flags & IQS621_ALS_FLAGS_RANGE; 1508c2ecf20Sopenharmony_ci range_old = iqs621_als->als_flags & IQS621_ALS_FLAGS_RANGE; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci if (iqs621_als->range_en && (range_new > range_old)) 1538c2ecf20Sopenharmony_ci iio_push_event(indio_dev, 1548c2ecf20Sopenharmony_ci IIO_UNMOD_EVENT_CODE(IIO_INTENSITY, 0, 1558c2ecf20Sopenharmony_ci IIO_EV_TYPE_CHANGE, 1568c2ecf20Sopenharmony_ci IIO_EV_DIR_RISING), 1578c2ecf20Sopenharmony_ci timestamp); 1588c2ecf20Sopenharmony_ci else if (iqs621_als->range_en && (range_new < range_old)) 1598c2ecf20Sopenharmony_ci iio_push_event(indio_dev, 1608c2ecf20Sopenharmony_ci IIO_UNMOD_EVENT_CODE(IIO_INTENSITY, 0, 1618c2ecf20Sopenharmony_ci IIO_EV_TYPE_CHANGE, 1628c2ecf20Sopenharmony_ci IIO_EV_DIR_FALLING), 1638c2ecf20Sopenharmony_ci timestamp); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci /* IQS622 only */ 1668c2ecf20Sopenharmony_ci prox_new = event_data->ir_flags & iqs621_als->ir_flags_mask; 1678c2ecf20Sopenharmony_ci prox_old = iqs621_als->ir_flags & iqs621_als->ir_flags_mask; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci if (iqs621_als->prox_en && prox_new && !prox_old) 1708c2ecf20Sopenharmony_ci iio_push_event(indio_dev, 1718c2ecf20Sopenharmony_ci IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0, 1728c2ecf20Sopenharmony_ci IIO_EV_TYPE_THRESH, 1738c2ecf20Sopenharmony_ci IIO_EV_DIR_RISING), 1748c2ecf20Sopenharmony_ci timestamp); 1758c2ecf20Sopenharmony_ci else if (iqs621_als->prox_en && !prox_new && prox_old) 1768c2ecf20Sopenharmony_ci iio_push_event(indio_dev, 1778c2ecf20Sopenharmony_ci IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0, 1788c2ecf20Sopenharmony_ci IIO_EV_TYPE_THRESH, 1798c2ecf20Sopenharmony_ci IIO_EV_DIR_FALLING), 1808c2ecf20Sopenharmony_ci timestamp); 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci iqs621_als->als_flags = event_data->als_flags; 1838c2ecf20Sopenharmony_ci iqs621_als->ir_flags = event_data->ir_flags; 1848c2ecf20Sopenharmony_ci ret = NOTIFY_OK; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cierr_mutex: 1878c2ecf20Sopenharmony_ci mutex_unlock(&iqs621_als->lock); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci return ret; 1908c2ecf20Sopenharmony_ci} 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_cistatic void iqs621_als_notifier_unregister(void *context) 1938c2ecf20Sopenharmony_ci{ 1948c2ecf20Sopenharmony_ci struct iqs621_als_private *iqs621_als = context; 1958c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = iqs621_als->indio_dev; 1968c2ecf20Sopenharmony_ci int ret; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci ret = blocking_notifier_chain_unregister(&iqs621_als->iqs62x->nh, 1998c2ecf20Sopenharmony_ci &iqs621_als->notifier); 2008c2ecf20Sopenharmony_ci if (ret) 2018c2ecf20Sopenharmony_ci dev_err(indio_dev->dev.parent, 2028c2ecf20Sopenharmony_ci "Failed to unregister notifier: %d\n", ret); 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_cistatic int iqs621_als_read_raw(struct iio_dev *indio_dev, 2068c2ecf20Sopenharmony_ci struct iio_chan_spec const *chan, 2078c2ecf20Sopenharmony_ci int *val, int *val2, long mask) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci struct iqs621_als_private *iqs621_als = iio_priv(indio_dev); 2108c2ecf20Sopenharmony_ci struct iqs62x_core *iqs62x = iqs621_als->iqs62x; 2118c2ecf20Sopenharmony_ci int ret; 2128c2ecf20Sopenharmony_ci __le16 val_buf; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci switch (chan->type) { 2158c2ecf20Sopenharmony_ci case IIO_INTENSITY: 2168c2ecf20Sopenharmony_ci ret = regmap_read(iqs62x->regmap, chan->address, val); 2178c2ecf20Sopenharmony_ci if (ret) 2188c2ecf20Sopenharmony_ci return ret; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci *val &= IQS621_ALS_FLAGS_RANGE; 2218c2ecf20Sopenharmony_ci return IIO_VAL_INT; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci case IIO_PROXIMITY: 2248c2ecf20Sopenharmony_ci case IIO_LIGHT: 2258c2ecf20Sopenharmony_ci ret = regmap_raw_read(iqs62x->regmap, chan->address, &val_buf, 2268c2ecf20Sopenharmony_ci sizeof(val_buf)); 2278c2ecf20Sopenharmony_ci if (ret) 2288c2ecf20Sopenharmony_ci return ret; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci *val = le16_to_cpu(val_buf); 2318c2ecf20Sopenharmony_ci return IIO_VAL_INT; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci default: 2348c2ecf20Sopenharmony_ci return -EINVAL; 2358c2ecf20Sopenharmony_ci } 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_cistatic int iqs621_als_read_event_config(struct iio_dev *indio_dev, 2398c2ecf20Sopenharmony_ci const struct iio_chan_spec *chan, 2408c2ecf20Sopenharmony_ci enum iio_event_type type, 2418c2ecf20Sopenharmony_ci enum iio_event_direction dir) 2428c2ecf20Sopenharmony_ci{ 2438c2ecf20Sopenharmony_ci struct iqs621_als_private *iqs621_als = iio_priv(indio_dev); 2448c2ecf20Sopenharmony_ci int ret; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci mutex_lock(&iqs621_als->lock); 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci switch (chan->type) { 2498c2ecf20Sopenharmony_ci case IIO_LIGHT: 2508c2ecf20Sopenharmony_ci ret = iqs621_als->light_en; 2518c2ecf20Sopenharmony_ci break; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci case IIO_INTENSITY: 2548c2ecf20Sopenharmony_ci ret = iqs621_als->range_en; 2558c2ecf20Sopenharmony_ci break; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci case IIO_PROXIMITY: 2588c2ecf20Sopenharmony_ci ret = iqs621_als->prox_en; 2598c2ecf20Sopenharmony_ci break; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci default: 2628c2ecf20Sopenharmony_ci ret = -EINVAL; 2638c2ecf20Sopenharmony_ci } 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci mutex_unlock(&iqs621_als->lock); 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci return ret; 2688c2ecf20Sopenharmony_ci} 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_cistatic int iqs621_als_write_event_config(struct iio_dev *indio_dev, 2718c2ecf20Sopenharmony_ci const struct iio_chan_spec *chan, 2728c2ecf20Sopenharmony_ci enum iio_event_type type, 2738c2ecf20Sopenharmony_ci enum iio_event_direction dir, 2748c2ecf20Sopenharmony_ci int state) 2758c2ecf20Sopenharmony_ci{ 2768c2ecf20Sopenharmony_ci struct iqs621_als_private *iqs621_als = iio_priv(indio_dev); 2778c2ecf20Sopenharmony_ci struct iqs62x_core *iqs62x = iqs621_als->iqs62x; 2788c2ecf20Sopenharmony_ci unsigned int val; 2798c2ecf20Sopenharmony_ci int ret; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci mutex_lock(&iqs621_als->lock); 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci ret = regmap_read(iqs62x->regmap, iqs62x->dev_desc->als_flags, &val); 2848c2ecf20Sopenharmony_ci if (ret) 2858c2ecf20Sopenharmony_ci goto err_mutex; 2868c2ecf20Sopenharmony_ci iqs621_als->als_flags = val; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci switch (chan->type) { 2898c2ecf20Sopenharmony_ci case IIO_LIGHT: 2908c2ecf20Sopenharmony_ci ret = regmap_update_bits(iqs62x->regmap, IQS620_GLBL_EVENT_MASK, 2918c2ecf20Sopenharmony_ci iqs62x->dev_desc->als_mask, 2928c2ecf20Sopenharmony_ci iqs621_als->range_en || state ? 0 : 2938c2ecf20Sopenharmony_ci 0xFF); 2948c2ecf20Sopenharmony_ci if (!ret) 2958c2ecf20Sopenharmony_ci iqs621_als->light_en = state; 2968c2ecf20Sopenharmony_ci break; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci case IIO_INTENSITY: 2998c2ecf20Sopenharmony_ci ret = regmap_update_bits(iqs62x->regmap, IQS620_GLBL_EVENT_MASK, 3008c2ecf20Sopenharmony_ci iqs62x->dev_desc->als_mask, 3018c2ecf20Sopenharmony_ci iqs621_als->light_en || state ? 0 : 3028c2ecf20Sopenharmony_ci 0xFF); 3038c2ecf20Sopenharmony_ci if (!ret) 3048c2ecf20Sopenharmony_ci iqs621_als->range_en = state; 3058c2ecf20Sopenharmony_ci break; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci case IIO_PROXIMITY: 3088c2ecf20Sopenharmony_ci ret = regmap_read(iqs62x->regmap, IQS622_IR_FLAGS, &val); 3098c2ecf20Sopenharmony_ci if (ret) 3108c2ecf20Sopenharmony_ci goto err_mutex; 3118c2ecf20Sopenharmony_ci iqs621_als->ir_flags = val; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci ret = regmap_update_bits(iqs62x->regmap, IQS620_GLBL_EVENT_MASK, 3148c2ecf20Sopenharmony_ci iqs62x->dev_desc->ir_mask, 3158c2ecf20Sopenharmony_ci state ? 0 : 0xFF); 3168c2ecf20Sopenharmony_ci if (!ret) 3178c2ecf20Sopenharmony_ci iqs621_als->prox_en = state; 3188c2ecf20Sopenharmony_ci break; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci default: 3218c2ecf20Sopenharmony_ci ret = -EINVAL; 3228c2ecf20Sopenharmony_ci } 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_cierr_mutex: 3258c2ecf20Sopenharmony_ci mutex_unlock(&iqs621_als->lock); 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci return ret; 3288c2ecf20Sopenharmony_ci} 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_cistatic int iqs621_als_read_event_value(struct iio_dev *indio_dev, 3318c2ecf20Sopenharmony_ci const struct iio_chan_spec *chan, 3328c2ecf20Sopenharmony_ci enum iio_event_type type, 3338c2ecf20Sopenharmony_ci enum iio_event_direction dir, 3348c2ecf20Sopenharmony_ci enum iio_event_info info, 3358c2ecf20Sopenharmony_ci int *val, int *val2) 3368c2ecf20Sopenharmony_ci{ 3378c2ecf20Sopenharmony_ci struct iqs621_als_private *iqs621_als = iio_priv(indio_dev); 3388c2ecf20Sopenharmony_ci int ret = IIO_VAL_INT; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci mutex_lock(&iqs621_als->lock); 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci switch (dir) { 3438c2ecf20Sopenharmony_ci case IIO_EV_DIR_RISING: 3448c2ecf20Sopenharmony_ci *val = iqs621_als->thresh_light * 16; 3458c2ecf20Sopenharmony_ci break; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci case IIO_EV_DIR_FALLING: 3488c2ecf20Sopenharmony_ci *val = iqs621_als->thresh_dark * 4; 3498c2ecf20Sopenharmony_ci break; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci case IIO_EV_DIR_EITHER: 3528c2ecf20Sopenharmony_ci if (iqs621_als->ir_flags_mask == IQS622_IR_FLAGS_TOUCH) 3538c2ecf20Sopenharmony_ci *val = iqs621_als->thresh_prox * 4; 3548c2ecf20Sopenharmony_ci else 3558c2ecf20Sopenharmony_ci *val = iqs621_als->thresh_prox; 3568c2ecf20Sopenharmony_ci break; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci default: 3598c2ecf20Sopenharmony_ci ret = -EINVAL; 3608c2ecf20Sopenharmony_ci } 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci mutex_unlock(&iqs621_als->lock); 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci return ret; 3658c2ecf20Sopenharmony_ci} 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_cistatic int iqs621_als_write_event_value(struct iio_dev *indio_dev, 3688c2ecf20Sopenharmony_ci const struct iio_chan_spec *chan, 3698c2ecf20Sopenharmony_ci enum iio_event_type type, 3708c2ecf20Sopenharmony_ci enum iio_event_direction dir, 3718c2ecf20Sopenharmony_ci enum iio_event_info info, 3728c2ecf20Sopenharmony_ci int val, int val2) 3738c2ecf20Sopenharmony_ci{ 3748c2ecf20Sopenharmony_ci struct iqs621_als_private *iqs621_als = iio_priv(indio_dev); 3758c2ecf20Sopenharmony_ci struct iqs62x_core *iqs62x = iqs621_als->iqs62x; 3768c2ecf20Sopenharmony_ci unsigned int thresh_reg, thresh_val; 3778c2ecf20Sopenharmony_ci u8 ir_flags_mask, *thresh_cache; 3788c2ecf20Sopenharmony_ci int ret = -EINVAL; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci mutex_lock(&iqs621_als->lock); 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci switch (dir) { 3838c2ecf20Sopenharmony_ci case IIO_EV_DIR_RISING: 3848c2ecf20Sopenharmony_ci thresh_reg = IQS621_ALS_THRESH_LIGHT; 3858c2ecf20Sopenharmony_ci thresh_val = val / 16; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci thresh_cache = &iqs621_als->thresh_light; 3888c2ecf20Sopenharmony_ci ir_flags_mask = 0; 3898c2ecf20Sopenharmony_ci break; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci case IIO_EV_DIR_FALLING: 3928c2ecf20Sopenharmony_ci thresh_reg = IQS621_ALS_THRESH_DARK; 3938c2ecf20Sopenharmony_ci thresh_val = val / 4; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci thresh_cache = &iqs621_als->thresh_dark; 3968c2ecf20Sopenharmony_ci ir_flags_mask = 0; 3978c2ecf20Sopenharmony_ci break; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci case IIO_EV_DIR_EITHER: 4008c2ecf20Sopenharmony_ci /* 4018c2ecf20Sopenharmony_ci * The IQS622 supports two detection thresholds, both measured 4028c2ecf20Sopenharmony_ci * in the same arbitrary units reported by read_raw: proximity 4038c2ecf20Sopenharmony_ci * (0 through 255 in steps of 1), and touch (0 through 1020 in 4048c2ecf20Sopenharmony_ci * steps of 4). 4058c2ecf20Sopenharmony_ci * 4068c2ecf20Sopenharmony_ci * Based on the single detection threshold chosen by the user, 4078c2ecf20Sopenharmony_ci * select the hardware threshold that gives the best trade-off 4088c2ecf20Sopenharmony_ci * between range and resolution. 4098c2ecf20Sopenharmony_ci * 4108c2ecf20Sopenharmony_ci * By default, the close-range (but coarse) touch threshold is 4118c2ecf20Sopenharmony_ci * chosen during probe. 4128c2ecf20Sopenharmony_ci */ 4138c2ecf20Sopenharmony_ci switch (val) { 4148c2ecf20Sopenharmony_ci case 0 ... 255: 4158c2ecf20Sopenharmony_ci thresh_reg = IQS622_IR_THRESH_PROX; 4168c2ecf20Sopenharmony_ci thresh_val = val; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci ir_flags_mask = IQS622_IR_FLAGS_PROX; 4198c2ecf20Sopenharmony_ci break; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci case 256 ... 1020: 4228c2ecf20Sopenharmony_ci thresh_reg = IQS622_IR_THRESH_TOUCH; 4238c2ecf20Sopenharmony_ci thresh_val = val / 4; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci ir_flags_mask = IQS622_IR_FLAGS_TOUCH; 4268c2ecf20Sopenharmony_ci break; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci default: 4298c2ecf20Sopenharmony_ci goto err_mutex; 4308c2ecf20Sopenharmony_ci } 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci thresh_cache = &iqs621_als->thresh_prox; 4338c2ecf20Sopenharmony_ci break; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci default: 4368c2ecf20Sopenharmony_ci goto err_mutex; 4378c2ecf20Sopenharmony_ci } 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci if (thresh_val > 0xFF) 4408c2ecf20Sopenharmony_ci goto err_mutex; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci ret = regmap_write(iqs62x->regmap, thresh_reg, thresh_val); 4438c2ecf20Sopenharmony_ci if (ret) 4448c2ecf20Sopenharmony_ci goto err_mutex; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci *thresh_cache = thresh_val; 4478c2ecf20Sopenharmony_ci iqs621_als->ir_flags_mask = ir_flags_mask; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_cierr_mutex: 4508c2ecf20Sopenharmony_ci mutex_unlock(&iqs621_als->lock); 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci return ret; 4538c2ecf20Sopenharmony_ci} 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_cistatic const struct iio_info iqs621_als_info = { 4568c2ecf20Sopenharmony_ci .read_raw = &iqs621_als_read_raw, 4578c2ecf20Sopenharmony_ci .read_event_config = iqs621_als_read_event_config, 4588c2ecf20Sopenharmony_ci .write_event_config = iqs621_als_write_event_config, 4598c2ecf20Sopenharmony_ci .read_event_value = iqs621_als_read_event_value, 4608c2ecf20Sopenharmony_ci .write_event_value = iqs621_als_write_event_value, 4618c2ecf20Sopenharmony_ci}; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_cistatic const struct iio_event_spec iqs621_als_range_events[] = { 4648c2ecf20Sopenharmony_ci { 4658c2ecf20Sopenharmony_ci .type = IIO_EV_TYPE_CHANGE, 4668c2ecf20Sopenharmony_ci .dir = IIO_EV_DIR_EITHER, 4678c2ecf20Sopenharmony_ci .mask_separate = BIT(IIO_EV_INFO_ENABLE), 4688c2ecf20Sopenharmony_ci }, 4698c2ecf20Sopenharmony_ci}; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_cistatic const struct iio_event_spec iqs621_als_light_events[] = { 4728c2ecf20Sopenharmony_ci { 4738c2ecf20Sopenharmony_ci .type = IIO_EV_TYPE_THRESH, 4748c2ecf20Sopenharmony_ci .dir = IIO_EV_DIR_EITHER, 4758c2ecf20Sopenharmony_ci .mask_separate = BIT(IIO_EV_INFO_ENABLE), 4768c2ecf20Sopenharmony_ci }, 4778c2ecf20Sopenharmony_ci { 4788c2ecf20Sopenharmony_ci .type = IIO_EV_TYPE_THRESH, 4798c2ecf20Sopenharmony_ci .dir = IIO_EV_DIR_RISING, 4808c2ecf20Sopenharmony_ci .mask_separate = BIT(IIO_EV_INFO_VALUE), 4818c2ecf20Sopenharmony_ci }, 4828c2ecf20Sopenharmony_ci { 4838c2ecf20Sopenharmony_ci .type = IIO_EV_TYPE_THRESH, 4848c2ecf20Sopenharmony_ci .dir = IIO_EV_DIR_FALLING, 4858c2ecf20Sopenharmony_ci .mask_separate = BIT(IIO_EV_INFO_VALUE), 4868c2ecf20Sopenharmony_ci }, 4878c2ecf20Sopenharmony_ci}; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_cistatic const struct iio_chan_spec iqs621_als_channels[] = { 4908c2ecf20Sopenharmony_ci { 4918c2ecf20Sopenharmony_ci .type = IIO_INTENSITY, 4928c2ecf20Sopenharmony_ci .address = IQS621_ALS_FLAGS, 4938c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 4948c2ecf20Sopenharmony_ci .event_spec = iqs621_als_range_events, 4958c2ecf20Sopenharmony_ci .num_event_specs = ARRAY_SIZE(iqs621_als_range_events), 4968c2ecf20Sopenharmony_ci }, 4978c2ecf20Sopenharmony_ci { 4988c2ecf20Sopenharmony_ci .type = IIO_LIGHT, 4998c2ecf20Sopenharmony_ci .address = IQS621_ALS_UI_OUT, 5008c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), 5018c2ecf20Sopenharmony_ci .event_spec = iqs621_als_light_events, 5028c2ecf20Sopenharmony_ci .num_event_specs = ARRAY_SIZE(iqs621_als_light_events), 5038c2ecf20Sopenharmony_ci }, 5048c2ecf20Sopenharmony_ci}; 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_cistatic const struct iio_event_spec iqs622_als_prox_events[] = { 5078c2ecf20Sopenharmony_ci { 5088c2ecf20Sopenharmony_ci .type = IIO_EV_TYPE_THRESH, 5098c2ecf20Sopenharmony_ci .dir = IIO_EV_DIR_EITHER, 5108c2ecf20Sopenharmony_ci .mask_separate = BIT(IIO_EV_INFO_ENABLE) | 5118c2ecf20Sopenharmony_ci BIT(IIO_EV_INFO_VALUE), 5128c2ecf20Sopenharmony_ci }, 5138c2ecf20Sopenharmony_ci}; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_cistatic const struct iio_chan_spec iqs622_als_channels[] = { 5168c2ecf20Sopenharmony_ci { 5178c2ecf20Sopenharmony_ci .type = IIO_INTENSITY, 5188c2ecf20Sopenharmony_ci .channel2 = IIO_MOD_LIGHT_BOTH, 5198c2ecf20Sopenharmony_ci .address = IQS622_ALS_FLAGS, 5208c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 5218c2ecf20Sopenharmony_ci .event_spec = iqs621_als_range_events, 5228c2ecf20Sopenharmony_ci .num_event_specs = ARRAY_SIZE(iqs621_als_range_events), 5238c2ecf20Sopenharmony_ci .modified = true, 5248c2ecf20Sopenharmony_ci }, 5258c2ecf20Sopenharmony_ci { 5268c2ecf20Sopenharmony_ci .type = IIO_INTENSITY, 5278c2ecf20Sopenharmony_ci .channel2 = IIO_MOD_LIGHT_IR, 5288c2ecf20Sopenharmony_ci .address = IQS622_IR_RANGE, 5298c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 5308c2ecf20Sopenharmony_ci .modified = true, 5318c2ecf20Sopenharmony_ci }, 5328c2ecf20Sopenharmony_ci { 5338c2ecf20Sopenharmony_ci .type = IIO_PROXIMITY, 5348c2ecf20Sopenharmony_ci .address = IQS622_IR_UI_OUT, 5358c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 5368c2ecf20Sopenharmony_ci .event_spec = iqs622_als_prox_events, 5378c2ecf20Sopenharmony_ci .num_event_specs = ARRAY_SIZE(iqs622_als_prox_events), 5388c2ecf20Sopenharmony_ci }, 5398c2ecf20Sopenharmony_ci}; 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_cistatic int iqs621_als_probe(struct platform_device *pdev) 5428c2ecf20Sopenharmony_ci{ 5438c2ecf20Sopenharmony_ci struct iqs62x_core *iqs62x = dev_get_drvdata(pdev->dev.parent); 5448c2ecf20Sopenharmony_ci struct iqs621_als_private *iqs621_als; 5458c2ecf20Sopenharmony_ci struct iio_dev *indio_dev; 5468c2ecf20Sopenharmony_ci unsigned int val; 5478c2ecf20Sopenharmony_ci int ret; 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*iqs621_als)); 5508c2ecf20Sopenharmony_ci if (!indio_dev) 5518c2ecf20Sopenharmony_ci return -ENOMEM; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci iqs621_als = iio_priv(indio_dev); 5548c2ecf20Sopenharmony_ci iqs621_als->iqs62x = iqs62x; 5558c2ecf20Sopenharmony_ci iqs621_als->indio_dev = indio_dev; 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci if (iqs62x->dev_desc->prod_num == IQS622_PROD_NUM) { 5588c2ecf20Sopenharmony_ci ret = regmap_read(iqs62x->regmap, IQS622_IR_THRESH_TOUCH, 5598c2ecf20Sopenharmony_ci &val); 5608c2ecf20Sopenharmony_ci if (ret) 5618c2ecf20Sopenharmony_ci return ret; 5628c2ecf20Sopenharmony_ci iqs621_als->thresh_prox = val; 5638c2ecf20Sopenharmony_ci iqs621_als->ir_flags_mask = IQS622_IR_FLAGS_TOUCH; 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci indio_dev->channels = iqs622_als_channels; 5668c2ecf20Sopenharmony_ci indio_dev->num_channels = ARRAY_SIZE(iqs622_als_channels); 5678c2ecf20Sopenharmony_ci } else { 5688c2ecf20Sopenharmony_ci ret = regmap_read(iqs62x->regmap, IQS621_ALS_THRESH_LIGHT, 5698c2ecf20Sopenharmony_ci &val); 5708c2ecf20Sopenharmony_ci if (ret) 5718c2ecf20Sopenharmony_ci return ret; 5728c2ecf20Sopenharmony_ci iqs621_als->thresh_light = val; 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci ret = regmap_read(iqs62x->regmap, IQS621_ALS_THRESH_DARK, 5758c2ecf20Sopenharmony_ci &val); 5768c2ecf20Sopenharmony_ci if (ret) 5778c2ecf20Sopenharmony_ci return ret; 5788c2ecf20Sopenharmony_ci iqs621_als->thresh_dark = val; 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci indio_dev->channels = iqs621_als_channels; 5818c2ecf20Sopenharmony_ci indio_dev->num_channels = ARRAY_SIZE(iqs621_als_channels); 5828c2ecf20Sopenharmony_ci } 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci indio_dev->modes = INDIO_DIRECT_MODE; 5858c2ecf20Sopenharmony_ci indio_dev->name = iqs62x->dev_desc->dev_name; 5868c2ecf20Sopenharmony_ci indio_dev->info = &iqs621_als_info; 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci mutex_init(&iqs621_als->lock); 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci iqs621_als->notifier.notifier_call = iqs621_als_notifier; 5918c2ecf20Sopenharmony_ci ret = blocking_notifier_chain_register(&iqs621_als->iqs62x->nh, 5928c2ecf20Sopenharmony_ci &iqs621_als->notifier); 5938c2ecf20Sopenharmony_ci if (ret) { 5948c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to register notifier: %d\n", ret); 5958c2ecf20Sopenharmony_ci return ret; 5968c2ecf20Sopenharmony_ci } 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci ret = devm_add_action_or_reset(&pdev->dev, 5998c2ecf20Sopenharmony_ci iqs621_als_notifier_unregister, 6008c2ecf20Sopenharmony_ci iqs621_als); 6018c2ecf20Sopenharmony_ci if (ret) 6028c2ecf20Sopenharmony_ci return ret; 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci return devm_iio_device_register(&pdev->dev, indio_dev); 6058c2ecf20Sopenharmony_ci} 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_cistatic struct platform_driver iqs621_als_platform_driver = { 6088c2ecf20Sopenharmony_ci .driver = { 6098c2ecf20Sopenharmony_ci .name = "iqs621-als", 6108c2ecf20Sopenharmony_ci }, 6118c2ecf20Sopenharmony_ci .probe = iqs621_als_probe, 6128c2ecf20Sopenharmony_ci}; 6138c2ecf20Sopenharmony_cimodule_platform_driver(iqs621_als_platform_driver); 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jeff LaBundy <jeff@labundy.com>"); 6168c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Azoteq IQS621/622 Ambient Light Sensors"); 6178c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 6188c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:iqs621-als"); 619