162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2015 Intel Corporation 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Driver for UPISEMI us5182d Proximity and Ambient Light Sensor. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * To do: Interrupt support. 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/kernel.h> 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/acpi.h> 1362306a36Sopenharmony_ci#include <linux/delay.h> 1462306a36Sopenharmony_ci#include <linux/i2c.h> 1562306a36Sopenharmony_ci#include <linux/iio/events.h> 1662306a36Sopenharmony_ci#include <linux/iio/iio.h> 1762306a36Sopenharmony_ci#include <linux/interrupt.h> 1862306a36Sopenharmony_ci#include <linux/irq.h> 1962306a36Sopenharmony_ci#include <linux/iio/sysfs.h> 2062306a36Sopenharmony_ci#include <linux/mutex.h> 2162306a36Sopenharmony_ci#include <linux/pm.h> 2262306a36Sopenharmony_ci#include <linux/pm_runtime.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define US5182D_REG_CFG0 0x00 2562306a36Sopenharmony_ci#define US5182D_CFG0_ONESHOT_EN BIT(6) 2662306a36Sopenharmony_ci#define US5182D_CFG0_SHUTDOWN_EN BIT(7) 2762306a36Sopenharmony_ci#define US5182D_CFG0_WORD_ENABLE BIT(0) 2862306a36Sopenharmony_ci#define US5182D_CFG0_PROX BIT(3) 2962306a36Sopenharmony_ci#define US5182D_CFG0_PX_IRQ BIT(2) 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#define US5182D_REG_CFG1 0x01 3262306a36Sopenharmony_ci#define US5182D_CFG1_ALS_RES16 BIT(4) 3362306a36Sopenharmony_ci#define US5182D_CFG1_AGAIN_DEFAULT 0x00 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#define US5182D_REG_CFG2 0x02 3662306a36Sopenharmony_ci#define US5182D_CFG2_PX_RES16 BIT(4) 3762306a36Sopenharmony_ci#define US5182D_CFG2_PXGAIN_DEFAULT BIT(2) 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#define US5182D_REG_CFG3 0x03 4062306a36Sopenharmony_ci#define US5182D_CFG3_LED_CURRENT100 (BIT(4) | BIT(5)) 4162306a36Sopenharmony_ci#define US5182D_CFG3_INT_SOURCE_PX BIT(3) 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci#define US5182D_REG_CFG4 0x10 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci/* 4662306a36Sopenharmony_ci * Registers for tuning the auto dark current cancelling feature. 4762306a36Sopenharmony_ci * DARK_TH(reg 0x27,0x28) - threshold (counts) for auto dark cancelling. 4862306a36Sopenharmony_ci * when ALS > DARK_TH --> ALS_Code = ALS - Upper(0x2A) * Dark 4962306a36Sopenharmony_ci * when ALS < DARK_TH --> ALS_Code = ALS - Lower(0x29) * Dark 5062306a36Sopenharmony_ci */ 5162306a36Sopenharmony_ci#define US5182D_REG_UDARK_TH 0x27 5262306a36Sopenharmony_ci#define US5182D_REG_DARK_AUTO_EN 0x2b 5362306a36Sopenharmony_ci#define US5182D_REG_AUTO_LDARK_GAIN 0x29 5462306a36Sopenharmony_ci#define US5182D_REG_AUTO_HDARK_GAIN 0x2a 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci/* Thresholds for events: px low (0x08-l, 0x09-h), px high (0x0a-l 0x0b-h) */ 5762306a36Sopenharmony_ci#define US5182D_REG_PXL_TH 0x08 5862306a36Sopenharmony_ci#define US5182D_REG_PXH_TH 0x0a 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci#define US5182D_REG_PXL_TH_DEFAULT 1000 6162306a36Sopenharmony_ci#define US5182D_REG_PXH_TH_DEFAULT 30000 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci#define US5182D_OPMODE_ALS 0x01 6462306a36Sopenharmony_ci#define US5182D_OPMODE_PX 0x02 6562306a36Sopenharmony_ci#define US5182D_OPMODE_SHIFT 4 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci#define US5182D_REG_DARK_AUTO_EN_DEFAULT 0x80 6862306a36Sopenharmony_ci#define US5182D_REG_AUTO_LDARK_GAIN_DEFAULT 0x16 6962306a36Sopenharmony_ci#define US5182D_REG_AUTO_HDARK_GAIN_DEFAULT 0x00 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci#define US5182D_REG_ADL 0x0c 7262306a36Sopenharmony_ci#define US5182D_REG_PDL 0x0e 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci#define US5182D_REG_MODE_STORE 0x21 7562306a36Sopenharmony_ci#define US5182D_STORE_MODE 0x01 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci#define US5182D_REG_CHIPID 0xb2 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci#define US5182D_OPMODE_MASK GENMASK(5, 4) 8062306a36Sopenharmony_ci#define US5182D_AGAIN_MASK 0x07 8162306a36Sopenharmony_ci#define US5182D_RESET_CHIP 0x01 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci#define US5182D_CHIPID 0x26 8462306a36Sopenharmony_ci#define US5182D_DRV_NAME "us5182d" 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci#define US5182D_GA_RESOLUTION 1000 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci#define US5182D_READ_BYTE 1 8962306a36Sopenharmony_ci#define US5182D_READ_WORD 2 9062306a36Sopenharmony_ci#define US5182D_OPSTORE_SLEEP_TIME 20 /* ms */ 9162306a36Sopenharmony_ci#define US5182D_SLEEP_MS 3000 /* ms */ 9262306a36Sopenharmony_ci#define US5182D_PXH_TH_DISABLE 0xffff 9362306a36Sopenharmony_ci#define US5182D_PXL_TH_DISABLE 0x0000 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci/* Available ranges: [12354, 7065, 3998, 2202, 1285, 498, 256, 138] lux */ 9662306a36Sopenharmony_cistatic const int us5182d_scales[] = {188500, 107800, 61000, 33600, 19600, 7600, 9762306a36Sopenharmony_ci 3900, 2100}; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci/* 10062306a36Sopenharmony_ci * Experimental thresholds that work with US5182D sensor on evaluation board 10162306a36Sopenharmony_ci * roughly between 12-32 lux 10262306a36Sopenharmony_ci */ 10362306a36Sopenharmony_cistatic u16 us5182d_dark_ths_vals[] = {170, 200, 512, 512, 800, 2000, 4000, 10462306a36Sopenharmony_ci 8000}; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_cienum mode { 10762306a36Sopenharmony_ci US5182D_ALS_PX, 10862306a36Sopenharmony_ci US5182D_ALS_ONLY, 10962306a36Sopenharmony_ci US5182D_PX_ONLY 11062306a36Sopenharmony_ci}; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cienum pmode { 11362306a36Sopenharmony_ci US5182D_CONTINUOUS, 11462306a36Sopenharmony_ci US5182D_ONESHOT 11562306a36Sopenharmony_ci}; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistruct us5182d_data { 11862306a36Sopenharmony_ci struct i2c_client *client; 11962306a36Sopenharmony_ci struct mutex lock; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci /* Glass attenuation factor */ 12262306a36Sopenharmony_ci u32 ga; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci /* Dark gain tuning */ 12562306a36Sopenharmony_ci u8 lower_dark_gain; 12662306a36Sopenharmony_ci u8 upper_dark_gain; 12762306a36Sopenharmony_ci u16 *us5182d_dark_ths; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci u16 px_low_th; 13062306a36Sopenharmony_ci u16 px_high_th; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci int rising_en; 13362306a36Sopenharmony_ci int falling_en; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci u8 opmode; 13662306a36Sopenharmony_ci u8 power_mode; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci bool als_enabled; 13962306a36Sopenharmony_ci bool px_enabled; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci bool default_continuous; 14262306a36Sopenharmony_ci}; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic IIO_CONST_ATTR(in_illuminance_scale_available, 14562306a36Sopenharmony_ci "0.0021 0.0039 0.0076 0.0196 0.0336 0.061 0.1078 0.1885"); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cistatic struct attribute *us5182d_attrs[] = { 14862306a36Sopenharmony_ci &iio_const_attr_in_illuminance_scale_available.dev_attr.attr, 14962306a36Sopenharmony_ci NULL 15062306a36Sopenharmony_ci}; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistatic const struct attribute_group us5182d_attr_group = { 15362306a36Sopenharmony_ci .attrs = us5182d_attrs, 15462306a36Sopenharmony_ci}; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cistatic const struct { 15762306a36Sopenharmony_ci u8 reg; 15862306a36Sopenharmony_ci u8 val; 15962306a36Sopenharmony_ci} us5182d_regvals[] = { 16062306a36Sopenharmony_ci {US5182D_REG_CFG0, US5182D_CFG0_WORD_ENABLE}, 16162306a36Sopenharmony_ci {US5182D_REG_CFG1, US5182D_CFG1_ALS_RES16}, 16262306a36Sopenharmony_ci {US5182D_REG_CFG2, (US5182D_CFG2_PX_RES16 | 16362306a36Sopenharmony_ci US5182D_CFG2_PXGAIN_DEFAULT)}, 16462306a36Sopenharmony_ci {US5182D_REG_CFG3, US5182D_CFG3_LED_CURRENT100 | 16562306a36Sopenharmony_ci US5182D_CFG3_INT_SOURCE_PX}, 16662306a36Sopenharmony_ci {US5182D_REG_CFG4, 0x00}, 16762306a36Sopenharmony_ci}; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistatic const struct iio_event_spec us5182d_events[] = { 17062306a36Sopenharmony_ci { 17162306a36Sopenharmony_ci .type = IIO_EV_TYPE_THRESH, 17262306a36Sopenharmony_ci .dir = IIO_EV_DIR_RISING, 17362306a36Sopenharmony_ci .mask_separate = BIT(IIO_EV_INFO_VALUE) | 17462306a36Sopenharmony_ci BIT(IIO_EV_INFO_ENABLE), 17562306a36Sopenharmony_ci }, 17662306a36Sopenharmony_ci { 17762306a36Sopenharmony_ci .type = IIO_EV_TYPE_THRESH, 17862306a36Sopenharmony_ci .dir = IIO_EV_DIR_FALLING, 17962306a36Sopenharmony_ci .mask_separate = BIT(IIO_EV_INFO_VALUE) | 18062306a36Sopenharmony_ci BIT(IIO_EV_INFO_ENABLE), 18162306a36Sopenharmony_ci }, 18262306a36Sopenharmony_ci}; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cistatic const struct iio_chan_spec us5182d_channels[] = { 18562306a36Sopenharmony_ci { 18662306a36Sopenharmony_ci .type = IIO_LIGHT, 18762306a36Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 18862306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_SCALE), 18962306a36Sopenharmony_ci }, 19062306a36Sopenharmony_ci { 19162306a36Sopenharmony_ci .type = IIO_PROXIMITY, 19262306a36Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 19362306a36Sopenharmony_ci .event_spec = us5182d_events, 19462306a36Sopenharmony_ci .num_event_specs = ARRAY_SIZE(us5182d_events), 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci}; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_cistatic int us5182d_oneshot_en(struct us5182d_data *data) 19962306a36Sopenharmony_ci{ 20062306a36Sopenharmony_ci int ret; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci ret = i2c_smbus_read_byte_data(data->client, US5182D_REG_CFG0); 20362306a36Sopenharmony_ci if (ret < 0) 20462306a36Sopenharmony_ci return ret; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci /* 20762306a36Sopenharmony_ci * In oneshot mode the chip will power itself down after taking the 20862306a36Sopenharmony_ci * required measurement. 20962306a36Sopenharmony_ci */ 21062306a36Sopenharmony_ci ret = ret | US5182D_CFG0_ONESHOT_EN; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci return i2c_smbus_write_byte_data(data->client, US5182D_REG_CFG0, ret); 21362306a36Sopenharmony_ci} 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_cistatic int us5182d_set_opmode(struct us5182d_data *data, u8 mode) 21662306a36Sopenharmony_ci{ 21762306a36Sopenharmony_ci int ret; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci if (mode == data->opmode) 22062306a36Sopenharmony_ci return 0; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci ret = i2c_smbus_read_byte_data(data->client, US5182D_REG_CFG0); 22362306a36Sopenharmony_ci if (ret < 0) 22462306a36Sopenharmony_ci return ret; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci /* update mode */ 22762306a36Sopenharmony_ci ret = ret & ~US5182D_OPMODE_MASK; 22862306a36Sopenharmony_ci ret = ret | (mode << US5182D_OPMODE_SHIFT); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci /* 23162306a36Sopenharmony_ci * After updating the operating mode, the chip requires that 23262306a36Sopenharmony_ci * the operation is stored, by writing 1 in the STORE_MODE 23362306a36Sopenharmony_ci * register (auto-clearing). 23462306a36Sopenharmony_ci */ 23562306a36Sopenharmony_ci ret = i2c_smbus_write_byte_data(data->client, US5182D_REG_CFG0, ret); 23662306a36Sopenharmony_ci if (ret < 0) 23762306a36Sopenharmony_ci return ret; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci ret = i2c_smbus_write_byte_data(data->client, US5182D_REG_MODE_STORE, 24062306a36Sopenharmony_ci US5182D_STORE_MODE); 24162306a36Sopenharmony_ci if (ret < 0) 24262306a36Sopenharmony_ci return ret; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci data->opmode = mode; 24562306a36Sopenharmony_ci msleep(US5182D_OPSTORE_SLEEP_TIME); 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci return 0; 24862306a36Sopenharmony_ci} 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_cistatic int us5182d_als_enable(struct us5182d_data *data) 25162306a36Sopenharmony_ci{ 25262306a36Sopenharmony_ci int ret; 25362306a36Sopenharmony_ci u8 mode; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci if (data->power_mode == US5182D_ONESHOT) { 25662306a36Sopenharmony_ci ret = us5182d_set_opmode(data, US5182D_ALS_ONLY); 25762306a36Sopenharmony_ci if (ret < 0) 25862306a36Sopenharmony_ci return ret; 25962306a36Sopenharmony_ci data->px_enabled = false; 26062306a36Sopenharmony_ci } 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci if (data->als_enabled) 26362306a36Sopenharmony_ci return 0; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci mode = data->px_enabled ? US5182D_ALS_PX : US5182D_ALS_ONLY; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci ret = us5182d_set_opmode(data, mode); 26862306a36Sopenharmony_ci if (ret < 0) 26962306a36Sopenharmony_ci return ret; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci data->als_enabled = true; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci return 0; 27462306a36Sopenharmony_ci} 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_cistatic int us5182d_px_enable(struct us5182d_data *data) 27762306a36Sopenharmony_ci{ 27862306a36Sopenharmony_ci int ret; 27962306a36Sopenharmony_ci u8 mode; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci if (data->power_mode == US5182D_ONESHOT) { 28262306a36Sopenharmony_ci ret = us5182d_set_opmode(data, US5182D_PX_ONLY); 28362306a36Sopenharmony_ci if (ret < 0) 28462306a36Sopenharmony_ci return ret; 28562306a36Sopenharmony_ci data->als_enabled = false; 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci if (data->px_enabled) 28962306a36Sopenharmony_ci return 0; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci mode = data->als_enabled ? US5182D_ALS_PX : US5182D_PX_ONLY; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci ret = us5182d_set_opmode(data, mode); 29462306a36Sopenharmony_ci if (ret < 0) 29562306a36Sopenharmony_ci return ret; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci data->px_enabled = true; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci return 0; 30062306a36Sopenharmony_ci} 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_cistatic int us5182d_get_als(struct us5182d_data *data) 30362306a36Sopenharmony_ci{ 30462306a36Sopenharmony_ci int ret; 30562306a36Sopenharmony_ci unsigned long result; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci ret = us5182d_als_enable(data); 30862306a36Sopenharmony_ci if (ret < 0) 30962306a36Sopenharmony_ci return ret; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci ret = i2c_smbus_read_word_data(data->client, 31262306a36Sopenharmony_ci US5182D_REG_ADL); 31362306a36Sopenharmony_ci if (ret < 0) 31462306a36Sopenharmony_ci return ret; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci result = ret * data->ga / US5182D_GA_RESOLUTION; 31762306a36Sopenharmony_ci if (result > 0xffff) 31862306a36Sopenharmony_ci result = 0xffff; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci return result; 32162306a36Sopenharmony_ci} 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_cistatic int us5182d_get_px(struct us5182d_data *data) 32462306a36Sopenharmony_ci{ 32562306a36Sopenharmony_ci int ret; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci ret = us5182d_px_enable(data); 32862306a36Sopenharmony_ci if (ret < 0) 32962306a36Sopenharmony_ci return ret; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci return i2c_smbus_read_word_data(data->client, 33262306a36Sopenharmony_ci US5182D_REG_PDL); 33362306a36Sopenharmony_ci} 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_cistatic int us5182d_shutdown_en(struct us5182d_data *data, u8 state) 33662306a36Sopenharmony_ci{ 33762306a36Sopenharmony_ci int ret; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci if (data->power_mode == US5182D_ONESHOT) 34062306a36Sopenharmony_ci return 0; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci ret = i2c_smbus_read_byte_data(data->client, US5182D_REG_CFG0); 34362306a36Sopenharmony_ci if (ret < 0) 34462306a36Sopenharmony_ci return ret; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci ret = ret & ~US5182D_CFG0_SHUTDOWN_EN; 34762306a36Sopenharmony_ci ret = ret | state; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci ret = i2c_smbus_write_byte_data(data->client, US5182D_REG_CFG0, ret); 35062306a36Sopenharmony_ci if (ret < 0) 35162306a36Sopenharmony_ci return ret; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci if (state & US5182D_CFG0_SHUTDOWN_EN) { 35462306a36Sopenharmony_ci data->als_enabled = false; 35562306a36Sopenharmony_ci data->px_enabled = false; 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci return ret; 35962306a36Sopenharmony_ci} 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_cistatic int us5182d_set_power_state(struct us5182d_data *data, bool on) 36362306a36Sopenharmony_ci{ 36462306a36Sopenharmony_ci int ret; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci if (data->power_mode == US5182D_ONESHOT) 36762306a36Sopenharmony_ci return 0; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci if (on) { 37062306a36Sopenharmony_ci ret = pm_runtime_resume_and_get(&data->client->dev); 37162306a36Sopenharmony_ci } else { 37262306a36Sopenharmony_ci pm_runtime_mark_last_busy(&data->client->dev); 37362306a36Sopenharmony_ci ret = pm_runtime_put_autosuspend(&data->client->dev); 37462306a36Sopenharmony_ci } 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci return ret; 37762306a36Sopenharmony_ci} 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_cistatic int us5182d_read_value(struct us5182d_data *data, 38062306a36Sopenharmony_ci struct iio_chan_spec const *chan) 38162306a36Sopenharmony_ci{ 38262306a36Sopenharmony_ci int ret, value; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci mutex_lock(&data->lock); 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci if (data->power_mode == US5182D_ONESHOT) { 38762306a36Sopenharmony_ci ret = us5182d_oneshot_en(data); 38862306a36Sopenharmony_ci if (ret < 0) 38962306a36Sopenharmony_ci goto out_err; 39062306a36Sopenharmony_ci } 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci ret = us5182d_set_power_state(data, true); 39362306a36Sopenharmony_ci if (ret < 0) 39462306a36Sopenharmony_ci goto out_err; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci if (chan->type == IIO_LIGHT) 39762306a36Sopenharmony_ci ret = us5182d_get_als(data); 39862306a36Sopenharmony_ci else 39962306a36Sopenharmony_ci ret = us5182d_get_px(data); 40062306a36Sopenharmony_ci if (ret < 0) 40162306a36Sopenharmony_ci goto out_poweroff; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci value = ret; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci ret = us5182d_set_power_state(data, false); 40662306a36Sopenharmony_ci if (ret < 0) 40762306a36Sopenharmony_ci goto out_err; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci mutex_unlock(&data->lock); 41062306a36Sopenharmony_ci return value; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ciout_poweroff: 41362306a36Sopenharmony_ci us5182d_set_power_state(data, false); 41462306a36Sopenharmony_ciout_err: 41562306a36Sopenharmony_ci mutex_unlock(&data->lock); 41662306a36Sopenharmony_ci return ret; 41762306a36Sopenharmony_ci} 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_cistatic int us5182d_read_raw(struct iio_dev *indio_dev, 42062306a36Sopenharmony_ci struct iio_chan_spec const *chan, int *val, 42162306a36Sopenharmony_ci int *val2, long mask) 42262306a36Sopenharmony_ci{ 42362306a36Sopenharmony_ci struct us5182d_data *data = iio_priv(indio_dev); 42462306a36Sopenharmony_ci int ret; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci switch (mask) { 42762306a36Sopenharmony_ci case IIO_CHAN_INFO_RAW: 42862306a36Sopenharmony_ci ret = us5182d_read_value(data, chan); 42962306a36Sopenharmony_ci if (ret < 0) 43062306a36Sopenharmony_ci return ret; 43162306a36Sopenharmony_ci *val = ret; 43262306a36Sopenharmony_ci return IIO_VAL_INT; 43362306a36Sopenharmony_ci case IIO_CHAN_INFO_SCALE: 43462306a36Sopenharmony_ci ret = i2c_smbus_read_byte_data(data->client, US5182D_REG_CFG1); 43562306a36Sopenharmony_ci if (ret < 0) 43662306a36Sopenharmony_ci return ret; 43762306a36Sopenharmony_ci *val = 0; 43862306a36Sopenharmony_ci *val2 = us5182d_scales[ret & US5182D_AGAIN_MASK]; 43962306a36Sopenharmony_ci return IIO_VAL_INT_PLUS_MICRO; 44062306a36Sopenharmony_ci default: 44162306a36Sopenharmony_ci return -EINVAL; 44262306a36Sopenharmony_ci } 44362306a36Sopenharmony_ci} 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci/** 44662306a36Sopenharmony_ci * us5182d_update_dark_th - update Darh_Th registers 44762306a36Sopenharmony_ci * @data: us5182d_data structure 44862306a36Sopenharmony_ci * @index: index in us5182d_dark_ths array to use for the updated value 44962306a36Sopenharmony_ci * 45062306a36Sopenharmony_ci * Function needs to be called with a lock held because it needs two i2c write 45162306a36Sopenharmony_ci * byte operations as these registers (0x27 0x28) don't work in word mode 45262306a36Sopenharmony_ci * accessing. 45362306a36Sopenharmony_ci */ 45462306a36Sopenharmony_cistatic int us5182d_update_dark_th(struct us5182d_data *data, int index) 45562306a36Sopenharmony_ci{ 45662306a36Sopenharmony_ci __be16 dark_th = cpu_to_be16(data->us5182d_dark_ths[index]); 45762306a36Sopenharmony_ci int ret; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci ret = i2c_smbus_write_byte_data(data->client, US5182D_REG_UDARK_TH, 46062306a36Sopenharmony_ci ((u8 *)&dark_th)[0]); 46162306a36Sopenharmony_ci if (ret < 0) 46262306a36Sopenharmony_ci return ret; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci return i2c_smbus_write_byte_data(data->client, US5182D_REG_UDARK_TH + 1, 46562306a36Sopenharmony_ci ((u8 *)&dark_th)[1]); 46662306a36Sopenharmony_ci} 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci/** 46962306a36Sopenharmony_ci * us5182d_apply_scale - update the ALS scale 47062306a36Sopenharmony_ci * @data: us5182d_data structure 47162306a36Sopenharmony_ci * @index: index in us5182d_scales array to use for the updated value 47262306a36Sopenharmony_ci * 47362306a36Sopenharmony_ci * Function needs to be called with a lock held as we're having more than one 47462306a36Sopenharmony_ci * i2c operation. 47562306a36Sopenharmony_ci */ 47662306a36Sopenharmony_cistatic int us5182d_apply_scale(struct us5182d_data *data, int index) 47762306a36Sopenharmony_ci{ 47862306a36Sopenharmony_ci int ret; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci ret = i2c_smbus_read_byte_data(data->client, US5182D_REG_CFG1); 48162306a36Sopenharmony_ci if (ret < 0) 48262306a36Sopenharmony_ci return ret; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci ret = ret & (~US5182D_AGAIN_MASK); 48562306a36Sopenharmony_ci ret |= index; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci ret = i2c_smbus_write_byte_data(data->client, US5182D_REG_CFG1, ret); 48862306a36Sopenharmony_ci if (ret < 0) 48962306a36Sopenharmony_ci return ret; 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci return us5182d_update_dark_th(data, index); 49262306a36Sopenharmony_ci} 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_cistatic int us5182d_write_raw(struct iio_dev *indio_dev, 49562306a36Sopenharmony_ci struct iio_chan_spec const *chan, int val, 49662306a36Sopenharmony_ci int val2, long mask) 49762306a36Sopenharmony_ci{ 49862306a36Sopenharmony_ci struct us5182d_data *data = iio_priv(indio_dev); 49962306a36Sopenharmony_ci int ret, i; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci switch (mask) { 50262306a36Sopenharmony_ci case IIO_CHAN_INFO_SCALE: 50362306a36Sopenharmony_ci if (val != 0) 50462306a36Sopenharmony_ci return -EINVAL; 50562306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(us5182d_scales); i++) 50662306a36Sopenharmony_ci if (val2 == us5182d_scales[i]) { 50762306a36Sopenharmony_ci mutex_lock(&data->lock); 50862306a36Sopenharmony_ci ret = us5182d_apply_scale(data, i); 50962306a36Sopenharmony_ci mutex_unlock(&data->lock); 51062306a36Sopenharmony_ci return ret; 51162306a36Sopenharmony_ci } 51262306a36Sopenharmony_ci break; 51362306a36Sopenharmony_ci default: 51462306a36Sopenharmony_ci return -EINVAL; 51562306a36Sopenharmony_ci } 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci return -EINVAL; 51862306a36Sopenharmony_ci} 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_cistatic int us5182d_setup_prox(struct iio_dev *indio_dev, 52162306a36Sopenharmony_ci enum iio_event_direction dir, u16 val) 52262306a36Sopenharmony_ci{ 52362306a36Sopenharmony_ci struct us5182d_data *data = iio_priv(indio_dev); 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci if (dir == IIO_EV_DIR_FALLING) 52662306a36Sopenharmony_ci return i2c_smbus_write_word_data(data->client, 52762306a36Sopenharmony_ci US5182D_REG_PXL_TH, val); 52862306a36Sopenharmony_ci else if (dir == IIO_EV_DIR_RISING) 52962306a36Sopenharmony_ci return i2c_smbus_write_word_data(data->client, 53062306a36Sopenharmony_ci US5182D_REG_PXH_TH, val); 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci return 0; 53362306a36Sopenharmony_ci} 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_cistatic int us5182d_read_thresh(struct iio_dev *indio_dev, 53662306a36Sopenharmony_ci const struct iio_chan_spec *chan, enum iio_event_type type, 53762306a36Sopenharmony_ci enum iio_event_direction dir, enum iio_event_info info, int *val, 53862306a36Sopenharmony_ci int *val2) 53962306a36Sopenharmony_ci{ 54062306a36Sopenharmony_ci struct us5182d_data *data = iio_priv(indio_dev); 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci switch (dir) { 54362306a36Sopenharmony_ci case IIO_EV_DIR_RISING: 54462306a36Sopenharmony_ci mutex_lock(&data->lock); 54562306a36Sopenharmony_ci *val = data->px_high_th; 54662306a36Sopenharmony_ci mutex_unlock(&data->lock); 54762306a36Sopenharmony_ci break; 54862306a36Sopenharmony_ci case IIO_EV_DIR_FALLING: 54962306a36Sopenharmony_ci mutex_lock(&data->lock); 55062306a36Sopenharmony_ci *val = data->px_low_th; 55162306a36Sopenharmony_ci mutex_unlock(&data->lock); 55262306a36Sopenharmony_ci break; 55362306a36Sopenharmony_ci default: 55462306a36Sopenharmony_ci return -EINVAL; 55562306a36Sopenharmony_ci } 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci return IIO_VAL_INT; 55862306a36Sopenharmony_ci} 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_cistatic int us5182d_write_thresh(struct iio_dev *indio_dev, 56162306a36Sopenharmony_ci const struct iio_chan_spec *chan, enum iio_event_type type, 56262306a36Sopenharmony_ci enum iio_event_direction dir, enum iio_event_info info, int val, 56362306a36Sopenharmony_ci int val2) 56462306a36Sopenharmony_ci{ 56562306a36Sopenharmony_ci struct us5182d_data *data = iio_priv(indio_dev); 56662306a36Sopenharmony_ci int ret; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci if (val < 0 || val > USHRT_MAX || val2 != 0) 56962306a36Sopenharmony_ci return -EINVAL; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci switch (dir) { 57262306a36Sopenharmony_ci case IIO_EV_DIR_RISING: 57362306a36Sopenharmony_ci mutex_lock(&data->lock); 57462306a36Sopenharmony_ci if (data->rising_en) { 57562306a36Sopenharmony_ci ret = us5182d_setup_prox(indio_dev, dir, val); 57662306a36Sopenharmony_ci if (ret < 0) 57762306a36Sopenharmony_ci goto err; 57862306a36Sopenharmony_ci } 57962306a36Sopenharmony_ci data->px_high_th = val; 58062306a36Sopenharmony_ci mutex_unlock(&data->lock); 58162306a36Sopenharmony_ci break; 58262306a36Sopenharmony_ci case IIO_EV_DIR_FALLING: 58362306a36Sopenharmony_ci mutex_lock(&data->lock); 58462306a36Sopenharmony_ci if (data->falling_en) { 58562306a36Sopenharmony_ci ret = us5182d_setup_prox(indio_dev, dir, val); 58662306a36Sopenharmony_ci if (ret < 0) 58762306a36Sopenharmony_ci goto err; 58862306a36Sopenharmony_ci } 58962306a36Sopenharmony_ci data->px_low_th = val; 59062306a36Sopenharmony_ci mutex_unlock(&data->lock); 59162306a36Sopenharmony_ci break; 59262306a36Sopenharmony_ci default: 59362306a36Sopenharmony_ci return -EINVAL; 59462306a36Sopenharmony_ci } 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci return 0; 59762306a36Sopenharmony_cierr: 59862306a36Sopenharmony_ci mutex_unlock(&data->lock); 59962306a36Sopenharmony_ci return ret; 60062306a36Sopenharmony_ci} 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_cistatic int us5182d_read_event_config(struct iio_dev *indio_dev, 60362306a36Sopenharmony_ci const struct iio_chan_spec *chan, enum iio_event_type type, 60462306a36Sopenharmony_ci enum iio_event_direction dir) 60562306a36Sopenharmony_ci{ 60662306a36Sopenharmony_ci struct us5182d_data *data = iio_priv(indio_dev); 60762306a36Sopenharmony_ci int ret; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci switch (dir) { 61062306a36Sopenharmony_ci case IIO_EV_DIR_RISING: 61162306a36Sopenharmony_ci mutex_lock(&data->lock); 61262306a36Sopenharmony_ci ret = data->rising_en; 61362306a36Sopenharmony_ci mutex_unlock(&data->lock); 61462306a36Sopenharmony_ci break; 61562306a36Sopenharmony_ci case IIO_EV_DIR_FALLING: 61662306a36Sopenharmony_ci mutex_lock(&data->lock); 61762306a36Sopenharmony_ci ret = data->falling_en; 61862306a36Sopenharmony_ci mutex_unlock(&data->lock); 61962306a36Sopenharmony_ci break; 62062306a36Sopenharmony_ci default: 62162306a36Sopenharmony_ci ret = -EINVAL; 62262306a36Sopenharmony_ci break; 62362306a36Sopenharmony_ci } 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci return ret; 62662306a36Sopenharmony_ci} 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_cistatic int us5182d_write_event_config(struct iio_dev *indio_dev, 62962306a36Sopenharmony_ci const struct iio_chan_spec *chan, enum iio_event_type type, 63062306a36Sopenharmony_ci enum iio_event_direction dir, int state) 63162306a36Sopenharmony_ci{ 63262306a36Sopenharmony_ci struct us5182d_data *data = iio_priv(indio_dev); 63362306a36Sopenharmony_ci int ret; 63462306a36Sopenharmony_ci u16 new_th; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci mutex_lock(&data->lock); 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci switch (dir) { 63962306a36Sopenharmony_ci case IIO_EV_DIR_RISING: 64062306a36Sopenharmony_ci if (data->rising_en == state) { 64162306a36Sopenharmony_ci mutex_unlock(&data->lock); 64262306a36Sopenharmony_ci return 0; 64362306a36Sopenharmony_ci } 64462306a36Sopenharmony_ci new_th = US5182D_PXH_TH_DISABLE; 64562306a36Sopenharmony_ci if (state) { 64662306a36Sopenharmony_ci data->power_mode = US5182D_CONTINUOUS; 64762306a36Sopenharmony_ci ret = us5182d_set_power_state(data, true); 64862306a36Sopenharmony_ci if (ret < 0) 64962306a36Sopenharmony_ci goto err; 65062306a36Sopenharmony_ci ret = us5182d_px_enable(data); 65162306a36Sopenharmony_ci if (ret < 0) 65262306a36Sopenharmony_ci goto err_poweroff; 65362306a36Sopenharmony_ci new_th = data->px_high_th; 65462306a36Sopenharmony_ci } 65562306a36Sopenharmony_ci ret = us5182d_setup_prox(indio_dev, dir, new_th); 65662306a36Sopenharmony_ci if (ret < 0) 65762306a36Sopenharmony_ci goto err_poweroff; 65862306a36Sopenharmony_ci data->rising_en = state; 65962306a36Sopenharmony_ci break; 66062306a36Sopenharmony_ci case IIO_EV_DIR_FALLING: 66162306a36Sopenharmony_ci if (data->falling_en == state) { 66262306a36Sopenharmony_ci mutex_unlock(&data->lock); 66362306a36Sopenharmony_ci return 0; 66462306a36Sopenharmony_ci } 66562306a36Sopenharmony_ci new_th = US5182D_PXL_TH_DISABLE; 66662306a36Sopenharmony_ci if (state) { 66762306a36Sopenharmony_ci data->power_mode = US5182D_CONTINUOUS; 66862306a36Sopenharmony_ci ret = us5182d_set_power_state(data, true); 66962306a36Sopenharmony_ci if (ret < 0) 67062306a36Sopenharmony_ci goto err; 67162306a36Sopenharmony_ci ret = us5182d_px_enable(data); 67262306a36Sopenharmony_ci if (ret < 0) 67362306a36Sopenharmony_ci goto err_poweroff; 67462306a36Sopenharmony_ci new_th = data->px_low_th; 67562306a36Sopenharmony_ci } 67662306a36Sopenharmony_ci ret = us5182d_setup_prox(indio_dev, dir, new_th); 67762306a36Sopenharmony_ci if (ret < 0) 67862306a36Sopenharmony_ci goto err_poweroff; 67962306a36Sopenharmony_ci data->falling_en = state; 68062306a36Sopenharmony_ci break; 68162306a36Sopenharmony_ci default: 68262306a36Sopenharmony_ci ret = -EINVAL; 68362306a36Sopenharmony_ci goto err; 68462306a36Sopenharmony_ci } 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci if (!state) { 68762306a36Sopenharmony_ci ret = us5182d_set_power_state(data, false); 68862306a36Sopenharmony_ci if (ret < 0) 68962306a36Sopenharmony_ci goto err; 69062306a36Sopenharmony_ci } 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci if (!data->falling_en && !data->rising_en && !data->default_continuous) 69362306a36Sopenharmony_ci data->power_mode = US5182D_ONESHOT; 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci mutex_unlock(&data->lock); 69662306a36Sopenharmony_ci return 0; 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_cierr_poweroff: 69962306a36Sopenharmony_ci if (state) 70062306a36Sopenharmony_ci us5182d_set_power_state(data, false); 70162306a36Sopenharmony_cierr: 70262306a36Sopenharmony_ci mutex_unlock(&data->lock); 70362306a36Sopenharmony_ci return ret; 70462306a36Sopenharmony_ci} 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_cistatic const struct iio_info us5182d_info = { 70762306a36Sopenharmony_ci .read_raw = us5182d_read_raw, 70862306a36Sopenharmony_ci .write_raw = us5182d_write_raw, 70962306a36Sopenharmony_ci .attrs = &us5182d_attr_group, 71062306a36Sopenharmony_ci .read_event_value = &us5182d_read_thresh, 71162306a36Sopenharmony_ci .write_event_value = &us5182d_write_thresh, 71262306a36Sopenharmony_ci .read_event_config = &us5182d_read_event_config, 71362306a36Sopenharmony_ci .write_event_config = &us5182d_write_event_config, 71462306a36Sopenharmony_ci}; 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_cistatic int us5182d_reset(struct iio_dev *indio_dev) 71762306a36Sopenharmony_ci{ 71862306a36Sopenharmony_ci struct us5182d_data *data = iio_priv(indio_dev); 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci return i2c_smbus_write_byte_data(data->client, US5182D_REG_CFG3, 72162306a36Sopenharmony_ci US5182D_RESET_CHIP); 72262306a36Sopenharmony_ci} 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_cistatic int us5182d_init(struct iio_dev *indio_dev) 72562306a36Sopenharmony_ci{ 72662306a36Sopenharmony_ci struct us5182d_data *data = iio_priv(indio_dev); 72762306a36Sopenharmony_ci int i, ret; 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci ret = us5182d_reset(indio_dev); 73062306a36Sopenharmony_ci if (ret < 0) 73162306a36Sopenharmony_ci return ret; 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci data->opmode = 0; 73462306a36Sopenharmony_ci data->power_mode = US5182D_CONTINUOUS; 73562306a36Sopenharmony_ci data->px_low_th = US5182D_REG_PXL_TH_DEFAULT; 73662306a36Sopenharmony_ci data->px_high_th = US5182D_REG_PXH_TH_DEFAULT; 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(us5182d_regvals); i++) { 73962306a36Sopenharmony_ci ret = i2c_smbus_write_byte_data(data->client, 74062306a36Sopenharmony_ci us5182d_regvals[i].reg, 74162306a36Sopenharmony_ci us5182d_regvals[i].val); 74262306a36Sopenharmony_ci if (ret < 0) 74362306a36Sopenharmony_ci return ret; 74462306a36Sopenharmony_ci } 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci data->als_enabled = true; 74762306a36Sopenharmony_ci data->px_enabled = true; 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci if (!data->default_continuous) { 75062306a36Sopenharmony_ci ret = us5182d_shutdown_en(data, US5182D_CFG0_SHUTDOWN_EN); 75162306a36Sopenharmony_ci if (ret < 0) 75262306a36Sopenharmony_ci return ret; 75362306a36Sopenharmony_ci data->power_mode = US5182D_ONESHOT; 75462306a36Sopenharmony_ci } 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci return ret; 75762306a36Sopenharmony_ci} 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_cistatic void us5182d_get_platform_data(struct iio_dev *indio_dev) 76062306a36Sopenharmony_ci{ 76162306a36Sopenharmony_ci struct us5182d_data *data = iio_priv(indio_dev); 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci if (device_property_read_u32(&data->client->dev, "upisemi,glass-coef", 76462306a36Sopenharmony_ci &data->ga)) 76562306a36Sopenharmony_ci data->ga = US5182D_GA_RESOLUTION; 76662306a36Sopenharmony_ci if (device_property_read_u16_array(&data->client->dev, 76762306a36Sopenharmony_ci "upisemi,dark-ths", 76862306a36Sopenharmony_ci data->us5182d_dark_ths, 76962306a36Sopenharmony_ci ARRAY_SIZE(us5182d_dark_ths_vals))) 77062306a36Sopenharmony_ci data->us5182d_dark_ths = us5182d_dark_ths_vals; 77162306a36Sopenharmony_ci if (device_property_read_u8(&data->client->dev, 77262306a36Sopenharmony_ci "upisemi,upper-dark-gain", 77362306a36Sopenharmony_ci &data->upper_dark_gain)) 77462306a36Sopenharmony_ci data->upper_dark_gain = US5182D_REG_AUTO_HDARK_GAIN_DEFAULT; 77562306a36Sopenharmony_ci if (device_property_read_u8(&data->client->dev, 77662306a36Sopenharmony_ci "upisemi,lower-dark-gain", 77762306a36Sopenharmony_ci &data->lower_dark_gain)) 77862306a36Sopenharmony_ci data->lower_dark_gain = US5182D_REG_AUTO_LDARK_GAIN_DEFAULT; 77962306a36Sopenharmony_ci data->default_continuous = device_property_read_bool(&data->client->dev, 78062306a36Sopenharmony_ci "upisemi,continuous"); 78162306a36Sopenharmony_ci} 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_cistatic int us5182d_dark_gain_config(struct iio_dev *indio_dev) 78462306a36Sopenharmony_ci{ 78562306a36Sopenharmony_ci struct us5182d_data *data = iio_priv(indio_dev); 78662306a36Sopenharmony_ci int ret; 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci ret = us5182d_update_dark_th(data, US5182D_CFG1_AGAIN_DEFAULT); 78962306a36Sopenharmony_ci if (ret < 0) 79062306a36Sopenharmony_ci return ret; 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci ret = i2c_smbus_write_byte_data(data->client, 79362306a36Sopenharmony_ci US5182D_REG_AUTO_LDARK_GAIN, 79462306a36Sopenharmony_ci data->lower_dark_gain); 79562306a36Sopenharmony_ci if (ret < 0) 79662306a36Sopenharmony_ci return ret; 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci ret = i2c_smbus_write_byte_data(data->client, 79962306a36Sopenharmony_ci US5182D_REG_AUTO_HDARK_GAIN, 80062306a36Sopenharmony_ci data->upper_dark_gain); 80162306a36Sopenharmony_ci if (ret < 0) 80262306a36Sopenharmony_ci return ret; 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci return i2c_smbus_write_byte_data(data->client, US5182D_REG_DARK_AUTO_EN, 80562306a36Sopenharmony_ci US5182D_REG_DARK_AUTO_EN_DEFAULT); 80662306a36Sopenharmony_ci} 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_cistatic irqreturn_t us5182d_irq_thread_handler(int irq, void *private) 80962306a36Sopenharmony_ci{ 81062306a36Sopenharmony_ci struct iio_dev *indio_dev = private; 81162306a36Sopenharmony_ci struct us5182d_data *data = iio_priv(indio_dev); 81262306a36Sopenharmony_ci enum iio_event_direction dir; 81362306a36Sopenharmony_ci int ret; 81462306a36Sopenharmony_ci u64 ev; 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci ret = i2c_smbus_read_byte_data(data->client, US5182D_REG_CFG0); 81762306a36Sopenharmony_ci if (ret < 0) { 81862306a36Sopenharmony_ci dev_err(&data->client->dev, "i2c transfer error in irq\n"); 81962306a36Sopenharmony_ci return IRQ_HANDLED; 82062306a36Sopenharmony_ci } 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci dir = ret & US5182D_CFG0_PROX ? IIO_EV_DIR_RISING : IIO_EV_DIR_FALLING; 82362306a36Sopenharmony_ci ev = IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 1, IIO_EV_TYPE_THRESH, dir); 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci iio_push_event(indio_dev, ev, iio_get_time_ns(indio_dev)); 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci ret = i2c_smbus_write_byte_data(data->client, US5182D_REG_CFG0, 82862306a36Sopenharmony_ci ret & ~US5182D_CFG0_PX_IRQ); 82962306a36Sopenharmony_ci if (ret < 0) 83062306a36Sopenharmony_ci dev_err(&data->client->dev, "i2c transfer error in irq\n"); 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci return IRQ_HANDLED; 83362306a36Sopenharmony_ci} 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_cistatic int us5182d_probe(struct i2c_client *client) 83662306a36Sopenharmony_ci{ 83762306a36Sopenharmony_ci struct us5182d_data *data; 83862306a36Sopenharmony_ci struct iio_dev *indio_dev; 83962306a36Sopenharmony_ci int ret; 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); 84262306a36Sopenharmony_ci if (!indio_dev) 84362306a36Sopenharmony_ci return -ENOMEM; 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci data = iio_priv(indio_dev); 84662306a36Sopenharmony_ci i2c_set_clientdata(client, indio_dev); 84762306a36Sopenharmony_ci data->client = client; 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci mutex_init(&data->lock); 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci indio_dev->info = &us5182d_info; 85262306a36Sopenharmony_ci indio_dev->name = US5182D_DRV_NAME; 85362306a36Sopenharmony_ci indio_dev->channels = us5182d_channels; 85462306a36Sopenharmony_ci indio_dev->num_channels = ARRAY_SIZE(us5182d_channels); 85562306a36Sopenharmony_ci indio_dev->modes = INDIO_DIRECT_MODE; 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci ret = i2c_smbus_read_byte_data(data->client, US5182D_REG_CHIPID); 85862306a36Sopenharmony_ci if (ret != US5182D_CHIPID) { 85962306a36Sopenharmony_ci dev_err(&data->client->dev, 86062306a36Sopenharmony_ci "Failed to detect US5182 light chip\n"); 86162306a36Sopenharmony_ci return (ret < 0) ? ret : -ENODEV; 86262306a36Sopenharmony_ci } 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci if (client->irq > 0) { 86562306a36Sopenharmony_ci ret = devm_request_threaded_irq(&client->dev, client->irq, NULL, 86662306a36Sopenharmony_ci us5182d_irq_thread_handler, 86762306a36Sopenharmony_ci IRQF_TRIGGER_LOW | IRQF_ONESHOT, 86862306a36Sopenharmony_ci "us5182d-irq", indio_dev); 86962306a36Sopenharmony_ci if (ret < 0) 87062306a36Sopenharmony_ci return ret; 87162306a36Sopenharmony_ci } else 87262306a36Sopenharmony_ci dev_warn(&client->dev, "no valid irq found\n"); 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci us5182d_get_platform_data(indio_dev); 87562306a36Sopenharmony_ci ret = us5182d_init(indio_dev); 87662306a36Sopenharmony_ci if (ret < 0) 87762306a36Sopenharmony_ci return ret; 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci ret = us5182d_dark_gain_config(indio_dev); 88062306a36Sopenharmony_ci if (ret < 0) 88162306a36Sopenharmony_ci goto out_err; 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci if (data->default_continuous) { 88462306a36Sopenharmony_ci ret = pm_runtime_set_active(&client->dev); 88562306a36Sopenharmony_ci if (ret < 0) 88662306a36Sopenharmony_ci goto out_err; 88762306a36Sopenharmony_ci } 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci pm_runtime_enable(&client->dev); 89062306a36Sopenharmony_ci pm_runtime_set_autosuspend_delay(&client->dev, 89162306a36Sopenharmony_ci US5182D_SLEEP_MS); 89262306a36Sopenharmony_ci pm_runtime_use_autosuspend(&client->dev); 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci ret = iio_device_register(indio_dev); 89562306a36Sopenharmony_ci if (ret < 0) 89662306a36Sopenharmony_ci goto out_err; 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci return 0; 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ciout_err: 90162306a36Sopenharmony_ci us5182d_shutdown_en(data, US5182D_CFG0_SHUTDOWN_EN); 90262306a36Sopenharmony_ci return ret; 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci} 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_cistatic void us5182d_remove(struct i2c_client *client) 90762306a36Sopenharmony_ci{ 90862306a36Sopenharmony_ci struct us5182d_data *data = iio_priv(i2c_get_clientdata(client)); 90962306a36Sopenharmony_ci int ret; 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci iio_device_unregister(i2c_get_clientdata(client)); 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci pm_runtime_disable(&client->dev); 91462306a36Sopenharmony_ci pm_runtime_set_suspended(&client->dev); 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci ret = us5182d_shutdown_en(data, US5182D_CFG0_SHUTDOWN_EN); 91762306a36Sopenharmony_ci if (ret) 91862306a36Sopenharmony_ci dev_warn(&client->dev, "Failed to shut down (%pe)\n", 91962306a36Sopenharmony_ci ERR_PTR(ret)); 92062306a36Sopenharmony_ci} 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_cistatic int us5182d_suspend(struct device *dev) 92362306a36Sopenharmony_ci{ 92462306a36Sopenharmony_ci struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); 92562306a36Sopenharmony_ci struct us5182d_data *data = iio_priv(indio_dev); 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci if (data->power_mode == US5182D_CONTINUOUS) 92862306a36Sopenharmony_ci return us5182d_shutdown_en(data, US5182D_CFG0_SHUTDOWN_EN); 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci return 0; 93162306a36Sopenharmony_ci} 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_cistatic int us5182d_resume(struct device *dev) 93462306a36Sopenharmony_ci{ 93562306a36Sopenharmony_ci struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); 93662306a36Sopenharmony_ci struct us5182d_data *data = iio_priv(indio_dev); 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci if (data->power_mode == US5182D_CONTINUOUS) 93962306a36Sopenharmony_ci return us5182d_shutdown_en(data, 94062306a36Sopenharmony_ci ~US5182D_CFG0_SHUTDOWN_EN & 0xff); 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_ci return 0; 94362306a36Sopenharmony_ci} 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_cistatic const struct dev_pm_ops us5182d_pm_ops = { 94662306a36Sopenharmony_ci SYSTEM_SLEEP_PM_OPS(us5182d_suspend, us5182d_resume) 94762306a36Sopenharmony_ci RUNTIME_PM_OPS(us5182d_suspend, us5182d_resume, NULL) 94862306a36Sopenharmony_ci}; 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_cistatic const struct acpi_device_id us5182d_acpi_match[] = { 95162306a36Sopenharmony_ci { "USD5182", 0 }, 95262306a36Sopenharmony_ci {} 95362306a36Sopenharmony_ci}; 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, us5182d_acpi_match); 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_cistatic const struct i2c_device_id us5182d_id[] = { 95862306a36Sopenharmony_ci { "usd5182", 0 }, 95962306a36Sopenharmony_ci {} 96062306a36Sopenharmony_ci}; 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, us5182d_id); 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_cistatic const struct of_device_id us5182d_of_match[] = { 96562306a36Sopenharmony_ci { .compatible = "upisemi,usd5182" }, 96662306a36Sopenharmony_ci {} 96762306a36Sopenharmony_ci}; 96862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, us5182d_of_match); 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_cistatic struct i2c_driver us5182d_driver = { 97162306a36Sopenharmony_ci .driver = { 97262306a36Sopenharmony_ci .name = US5182D_DRV_NAME, 97362306a36Sopenharmony_ci .pm = pm_ptr(&us5182d_pm_ops), 97462306a36Sopenharmony_ci .of_match_table = us5182d_of_match, 97562306a36Sopenharmony_ci .acpi_match_table = ACPI_PTR(us5182d_acpi_match), 97662306a36Sopenharmony_ci }, 97762306a36Sopenharmony_ci .probe = us5182d_probe, 97862306a36Sopenharmony_ci .remove = us5182d_remove, 97962306a36Sopenharmony_ci .id_table = us5182d_id, 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci}; 98262306a36Sopenharmony_cimodule_i2c_driver(us5182d_driver); 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ciMODULE_AUTHOR("Adriana Reus <adriana.reus@intel.com>"); 98562306a36Sopenharmony_ciMODULE_DESCRIPTION("Driver for us5182d Proximity and Light Sensor"); 98662306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 987