18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * si1145.c - Support for Silabs SI1132 and SI1141/2/3/5/6/7 combined ambient 48c2ecf20Sopenharmony_ci * light, UV index and proximity sensors 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright 2014-16 Peter Meerwald-Stadler <pmeerw@pmeerw.net> 78c2ecf20Sopenharmony_ci * Copyright 2016 Crestez Dan Leonard <leonard.crestez@intel.com> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * SI1132 (7-bit I2C slave address 0x60) 108c2ecf20Sopenharmony_ci * SI1141/2/3 (7-bit I2C slave address 0x5a) 118c2ecf20Sopenharmony_ci * SI1145/6/6 (7-bit I2C slave address 0x60) 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/i2c.h> 168c2ecf20Sopenharmony_ci#include <linux/err.h> 178c2ecf20Sopenharmony_ci#include <linux/slab.h> 188c2ecf20Sopenharmony_ci#include <linux/delay.h> 198c2ecf20Sopenharmony_ci#include <linux/irq.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include <linux/iio/iio.h> 228c2ecf20Sopenharmony_ci#include <linux/iio/sysfs.h> 238c2ecf20Sopenharmony_ci#include <linux/iio/trigger.h> 248c2ecf20Sopenharmony_ci#include <linux/iio/trigger_consumer.h> 258c2ecf20Sopenharmony_ci#include <linux/iio/triggered_buffer.h> 268c2ecf20Sopenharmony_ci#include <linux/iio/buffer.h> 278c2ecf20Sopenharmony_ci#include <linux/util_macros.h> 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define SI1145_REG_PART_ID 0x00 308c2ecf20Sopenharmony_ci#define SI1145_REG_REV_ID 0x01 318c2ecf20Sopenharmony_ci#define SI1145_REG_SEQ_ID 0x02 328c2ecf20Sopenharmony_ci#define SI1145_REG_INT_CFG 0x03 338c2ecf20Sopenharmony_ci#define SI1145_REG_IRQ_ENABLE 0x04 348c2ecf20Sopenharmony_ci#define SI1145_REG_IRQ_MODE 0x05 358c2ecf20Sopenharmony_ci#define SI1145_REG_HW_KEY 0x07 368c2ecf20Sopenharmony_ci#define SI1145_REG_MEAS_RATE 0x08 378c2ecf20Sopenharmony_ci#define SI1145_REG_PS_LED21 0x0f 388c2ecf20Sopenharmony_ci#define SI1145_REG_PS_LED3 0x10 398c2ecf20Sopenharmony_ci#define SI1145_REG_UCOEF1 0x13 408c2ecf20Sopenharmony_ci#define SI1145_REG_UCOEF2 0x14 418c2ecf20Sopenharmony_ci#define SI1145_REG_UCOEF3 0x15 428c2ecf20Sopenharmony_ci#define SI1145_REG_UCOEF4 0x16 438c2ecf20Sopenharmony_ci#define SI1145_REG_PARAM_WR 0x17 448c2ecf20Sopenharmony_ci#define SI1145_REG_COMMAND 0x18 458c2ecf20Sopenharmony_ci#define SI1145_REG_RESPONSE 0x20 468c2ecf20Sopenharmony_ci#define SI1145_REG_IRQ_STATUS 0x21 478c2ecf20Sopenharmony_ci#define SI1145_REG_ALSVIS_DATA 0x22 488c2ecf20Sopenharmony_ci#define SI1145_REG_ALSIR_DATA 0x24 498c2ecf20Sopenharmony_ci#define SI1145_REG_PS1_DATA 0x26 508c2ecf20Sopenharmony_ci#define SI1145_REG_PS2_DATA 0x28 518c2ecf20Sopenharmony_ci#define SI1145_REG_PS3_DATA 0x2a 528c2ecf20Sopenharmony_ci#define SI1145_REG_AUX_DATA 0x2c 538c2ecf20Sopenharmony_ci#define SI1145_REG_PARAM_RD 0x2e 548c2ecf20Sopenharmony_ci#define SI1145_REG_CHIP_STAT 0x30 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci#define SI1145_UCOEF1_DEFAULT 0x7b 578c2ecf20Sopenharmony_ci#define SI1145_UCOEF2_DEFAULT 0x6b 588c2ecf20Sopenharmony_ci#define SI1145_UCOEF3_DEFAULT 0x01 598c2ecf20Sopenharmony_ci#define SI1145_UCOEF4_DEFAULT 0x00 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci/* Helper to figure out PS_LED register / shift per channel */ 628c2ecf20Sopenharmony_ci#define SI1145_PS_LED_REG(ch) \ 638c2ecf20Sopenharmony_ci (((ch) == 2) ? SI1145_REG_PS_LED3 : SI1145_REG_PS_LED21) 648c2ecf20Sopenharmony_ci#define SI1145_PS_LED_SHIFT(ch) \ 658c2ecf20Sopenharmony_ci (((ch) == 1) ? 4 : 0) 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci/* Parameter offsets */ 688c2ecf20Sopenharmony_ci#define SI1145_PARAM_CHLIST 0x01 698c2ecf20Sopenharmony_ci#define SI1145_PARAM_PSLED12_SELECT 0x02 708c2ecf20Sopenharmony_ci#define SI1145_PARAM_PSLED3_SELECT 0x03 718c2ecf20Sopenharmony_ci#define SI1145_PARAM_PS_ENCODING 0x05 728c2ecf20Sopenharmony_ci#define SI1145_PARAM_ALS_ENCODING 0x06 738c2ecf20Sopenharmony_ci#define SI1145_PARAM_PS1_ADC_MUX 0x07 748c2ecf20Sopenharmony_ci#define SI1145_PARAM_PS2_ADC_MUX 0x08 758c2ecf20Sopenharmony_ci#define SI1145_PARAM_PS3_ADC_MUX 0x09 768c2ecf20Sopenharmony_ci#define SI1145_PARAM_PS_ADC_COUNTER 0x0a 778c2ecf20Sopenharmony_ci#define SI1145_PARAM_PS_ADC_GAIN 0x0b 788c2ecf20Sopenharmony_ci#define SI1145_PARAM_PS_ADC_MISC 0x0c 798c2ecf20Sopenharmony_ci#define SI1145_PARAM_ALS_ADC_MUX 0x0d 808c2ecf20Sopenharmony_ci#define SI1145_PARAM_ALSIR_ADC_MUX 0x0e 818c2ecf20Sopenharmony_ci#define SI1145_PARAM_AUX_ADC_MUX 0x0f 828c2ecf20Sopenharmony_ci#define SI1145_PARAM_ALSVIS_ADC_COUNTER 0x10 838c2ecf20Sopenharmony_ci#define SI1145_PARAM_ALSVIS_ADC_GAIN 0x11 848c2ecf20Sopenharmony_ci#define SI1145_PARAM_ALSVIS_ADC_MISC 0x12 858c2ecf20Sopenharmony_ci#define SI1145_PARAM_LED_RECOVERY 0x1c 868c2ecf20Sopenharmony_ci#define SI1145_PARAM_ALSIR_ADC_COUNTER 0x1d 878c2ecf20Sopenharmony_ci#define SI1145_PARAM_ALSIR_ADC_GAIN 0x1e 888c2ecf20Sopenharmony_ci#define SI1145_PARAM_ALSIR_ADC_MISC 0x1f 898c2ecf20Sopenharmony_ci#define SI1145_PARAM_ADC_OFFSET 0x1a 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci/* Channel enable masks for CHLIST parameter */ 928c2ecf20Sopenharmony_ci#define SI1145_CHLIST_EN_PS1 BIT(0) 938c2ecf20Sopenharmony_ci#define SI1145_CHLIST_EN_PS2 BIT(1) 948c2ecf20Sopenharmony_ci#define SI1145_CHLIST_EN_PS3 BIT(2) 958c2ecf20Sopenharmony_ci#define SI1145_CHLIST_EN_ALSVIS BIT(4) 968c2ecf20Sopenharmony_ci#define SI1145_CHLIST_EN_ALSIR BIT(5) 978c2ecf20Sopenharmony_ci#define SI1145_CHLIST_EN_AUX BIT(6) 988c2ecf20Sopenharmony_ci#define SI1145_CHLIST_EN_UV BIT(7) 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci/* Proximity measurement mode for ADC_MISC parameter */ 1018c2ecf20Sopenharmony_ci#define SI1145_PS_ADC_MODE_NORMAL BIT(2) 1028c2ecf20Sopenharmony_ci/* Signal range mask for ADC_MISC parameter */ 1038c2ecf20Sopenharmony_ci#define SI1145_ADC_MISC_RANGE BIT(5) 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci/* Commands for REG_COMMAND */ 1068c2ecf20Sopenharmony_ci#define SI1145_CMD_NOP 0x00 1078c2ecf20Sopenharmony_ci#define SI1145_CMD_RESET 0x01 1088c2ecf20Sopenharmony_ci#define SI1145_CMD_PS_FORCE 0x05 1098c2ecf20Sopenharmony_ci#define SI1145_CMD_ALS_FORCE 0x06 1108c2ecf20Sopenharmony_ci#define SI1145_CMD_PSALS_FORCE 0x07 1118c2ecf20Sopenharmony_ci#define SI1145_CMD_PS_PAUSE 0x09 1128c2ecf20Sopenharmony_ci#define SI1145_CMD_ALS_PAUSE 0x0a 1138c2ecf20Sopenharmony_ci#define SI1145_CMD_PSALS_PAUSE 0x0b 1148c2ecf20Sopenharmony_ci#define SI1145_CMD_PS_AUTO 0x0d 1158c2ecf20Sopenharmony_ci#define SI1145_CMD_ALS_AUTO 0x0e 1168c2ecf20Sopenharmony_ci#define SI1145_CMD_PSALS_AUTO 0x0f 1178c2ecf20Sopenharmony_ci#define SI1145_CMD_PARAM_QUERY 0x80 1188c2ecf20Sopenharmony_ci#define SI1145_CMD_PARAM_SET 0xa0 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci#define SI1145_RSP_INVALID_SETTING 0x80 1218c2ecf20Sopenharmony_ci#define SI1145_RSP_COUNTER_MASK 0x0F 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci/* Minimum sleep after each command to ensure it's received */ 1248c2ecf20Sopenharmony_ci#define SI1145_COMMAND_MINSLEEP_MS 5 1258c2ecf20Sopenharmony_ci/* Return -ETIMEDOUT after this long */ 1268c2ecf20Sopenharmony_ci#define SI1145_COMMAND_TIMEOUT_MS 25 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci/* Interrupt configuration masks for INT_CFG register */ 1298c2ecf20Sopenharmony_ci#define SI1145_INT_CFG_OE BIT(0) /* enable interrupt */ 1308c2ecf20Sopenharmony_ci#define SI1145_INT_CFG_MODE BIT(1) /* auto reset interrupt pin */ 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci/* Interrupt enable masks for IRQ_ENABLE register */ 1338c2ecf20Sopenharmony_ci#define SI1145_MASK_ALL_IE (BIT(4) | BIT(3) | BIT(2) | BIT(0)) 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci#define SI1145_MUX_TEMP 0x65 1368c2ecf20Sopenharmony_ci#define SI1145_MUX_VDD 0x75 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci/* Proximity LED current; see Table 2 in datasheet */ 1398c2ecf20Sopenharmony_ci#define SI1145_LED_CURRENT_45mA 0x04 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cienum { 1428c2ecf20Sopenharmony_ci SI1132, 1438c2ecf20Sopenharmony_ci SI1141, 1448c2ecf20Sopenharmony_ci SI1142, 1458c2ecf20Sopenharmony_ci SI1143, 1468c2ecf20Sopenharmony_ci SI1145, 1478c2ecf20Sopenharmony_ci SI1146, 1488c2ecf20Sopenharmony_ci SI1147, 1498c2ecf20Sopenharmony_ci}; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistruct si1145_part_info { 1528c2ecf20Sopenharmony_ci u8 part; 1538c2ecf20Sopenharmony_ci const struct iio_info *iio_info; 1548c2ecf20Sopenharmony_ci const struct iio_chan_spec *channels; 1558c2ecf20Sopenharmony_ci unsigned int num_channels; 1568c2ecf20Sopenharmony_ci unsigned int num_leds; 1578c2ecf20Sopenharmony_ci bool uncompressed_meas_rate; 1588c2ecf20Sopenharmony_ci}; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci/** 1618c2ecf20Sopenharmony_ci * struct si1145_data - si1145 chip state data 1628c2ecf20Sopenharmony_ci * @client: I2C client 1638c2ecf20Sopenharmony_ci * @lock: mutex to protect shared state. 1648c2ecf20Sopenharmony_ci * @cmdlock: Low-level mutex to protect command execution only 1658c2ecf20Sopenharmony_ci * @rsp_seq: Next expected response number or -1 if counter reset required 1668c2ecf20Sopenharmony_ci * @scan_mask: Saved scan mask to avoid duplicate set_chlist 1678c2ecf20Sopenharmony_ci * @autonomous: If automatic measurements are active (for buffer support) 1688c2ecf20Sopenharmony_ci * @part_info: Part information 1698c2ecf20Sopenharmony_ci * @trig: Pointer to iio trigger 1708c2ecf20Sopenharmony_ci * @meas_rate: Value of MEAS_RATE register. Only set in HW in auto mode 1718c2ecf20Sopenharmony_ci * @buffer: Used to pack data read from sensor. 1728c2ecf20Sopenharmony_ci */ 1738c2ecf20Sopenharmony_cistruct si1145_data { 1748c2ecf20Sopenharmony_ci struct i2c_client *client; 1758c2ecf20Sopenharmony_ci struct mutex lock; 1768c2ecf20Sopenharmony_ci struct mutex cmdlock; 1778c2ecf20Sopenharmony_ci int rsp_seq; 1788c2ecf20Sopenharmony_ci const struct si1145_part_info *part_info; 1798c2ecf20Sopenharmony_ci unsigned long scan_mask; 1808c2ecf20Sopenharmony_ci bool autonomous; 1818c2ecf20Sopenharmony_ci struct iio_trigger *trig; 1828c2ecf20Sopenharmony_ci int meas_rate; 1838c2ecf20Sopenharmony_ci /* 1848c2ecf20Sopenharmony_ci * Ensure timestamp will be naturally aligned if present. 1858c2ecf20Sopenharmony_ci * Maximum buffer size (may be only partly used if not all 1868c2ecf20Sopenharmony_ci * channels are enabled): 1878c2ecf20Sopenharmony_ci * 6*2 bytes channels data + 4 bytes alignment + 1888c2ecf20Sopenharmony_ci * 8 bytes timestamp 1898c2ecf20Sopenharmony_ci */ 1908c2ecf20Sopenharmony_ci u8 buffer[24] __aligned(8); 1918c2ecf20Sopenharmony_ci}; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci/* 1948c2ecf20Sopenharmony_ci * __si1145_command_reset() - Send CMD_NOP and wait for response 0 1958c2ecf20Sopenharmony_ci * 1968c2ecf20Sopenharmony_ci * Does not modify data->rsp_seq 1978c2ecf20Sopenharmony_ci * 1988c2ecf20Sopenharmony_ci * Return: 0 on success and -errno on error. 1998c2ecf20Sopenharmony_ci */ 2008c2ecf20Sopenharmony_cistatic int __si1145_command_reset(struct si1145_data *data) 2018c2ecf20Sopenharmony_ci{ 2028c2ecf20Sopenharmony_ci struct device *dev = &data->client->dev; 2038c2ecf20Sopenharmony_ci unsigned long stop_jiffies; 2048c2ecf20Sopenharmony_ci int ret; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte_data(data->client, SI1145_REG_COMMAND, 2078c2ecf20Sopenharmony_ci SI1145_CMD_NOP); 2088c2ecf20Sopenharmony_ci if (ret < 0) 2098c2ecf20Sopenharmony_ci return ret; 2108c2ecf20Sopenharmony_ci msleep(SI1145_COMMAND_MINSLEEP_MS); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci stop_jiffies = jiffies + SI1145_COMMAND_TIMEOUT_MS * HZ / 1000; 2138c2ecf20Sopenharmony_ci while (true) { 2148c2ecf20Sopenharmony_ci ret = i2c_smbus_read_byte_data(data->client, 2158c2ecf20Sopenharmony_ci SI1145_REG_RESPONSE); 2168c2ecf20Sopenharmony_ci if (ret <= 0) 2178c2ecf20Sopenharmony_ci return ret; 2188c2ecf20Sopenharmony_ci if (time_after(jiffies, stop_jiffies)) { 2198c2ecf20Sopenharmony_ci dev_warn(dev, "timeout on reset\n"); 2208c2ecf20Sopenharmony_ci return -ETIMEDOUT; 2218c2ecf20Sopenharmony_ci } 2228c2ecf20Sopenharmony_ci msleep(SI1145_COMMAND_MINSLEEP_MS); 2238c2ecf20Sopenharmony_ci continue; 2248c2ecf20Sopenharmony_ci } 2258c2ecf20Sopenharmony_ci} 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci/* 2288c2ecf20Sopenharmony_ci * si1145_command() - Execute a command and poll the response register 2298c2ecf20Sopenharmony_ci * 2308c2ecf20Sopenharmony_ci * All conversion overflows are reported as -EOVERFLOW 2318c2ecf20Sopenharmony_ci * INVALID_SETTING is reported as -EINVAL 2328c2ecf20Sopenharmony_ci * Timeouts are reported as -ETIMEDOUT 2338c2ecf20Sopenharmony_ci * 2348c2ecf20Sopenharmony_ci * Return: 0 on success or -errno on failure 2358c2ecf20Sopenharmony_ci */ 2368c2ecf20Sopenharmony_cistatic int si1145_command(struct si1145_data *data, u8 cmd) 2378c2ecf20Sopenharmony_ci{ 2388c2ecf20Sopenharmony_ci struct device *dev = &data->client->dev; 2398c2ecf20Sopenharmony_ci unsigned long stop_jiffies; 2408c2ecf20Sopenharmony_ci int ret; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci mutex_lock(&data->cmdlock); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci if (data->rsp_seq < 0) { 2458c2ecf20Sopenharmony_ci ret = __si1145_command_reset(data); 2468c2ecf20Sopenharmony_ci if (ret < 0) { 2478c2ecf20Sopenharmony_ci dev_err(dev, "failed to reset command counter, ret=%d\n", 2488c2ecf20Sopenharmony_ci ret); 2498c2ecf20Sopenharmony_ci goto out; 2508c2ecf20Sopenharmony_ci } 2518c2ecf20Sopenharmony_ci data->rsp_seq = 0; 2528c2ecf20Sopenharmony_ci } 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte_data(data->client, SI1145_REG_COMMAND, cmd); 2558c2ecf20Sopenharmony_ci if (ret) { 2568c2ecf20Sopenharmony_ci dev_warn(dev, "failed to write command, ret=%d\n", ret); 2578c2ecf20Sopenharmony_ci goto out; 2588c2ecf20Sopenharmony_ci } 2598c2ecf20Sopenharmony_ci /* Sleep a little to ensure the command is received */ 2608c2ecf20Sopenharmony_ci msleep(SI1145_COMMAND_MINSLEEP_MS); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci stop_jiffies = jiffies + SI1145_COMMAND_TIMEOUT_MS * HZ / 1000; 2638c2ecf20Sopenharmony_ci while (true) { 2648c2ecf20Sopenharmony_ci ret = i2c_smbus_read_byte_data(data->client, 2658c2ecf20Sopenharmony_ci SI1145_REG_RESPONSE); 2668c2ecf20Sopenharmony_ci if (ret < 0) { 2678c2ecf20Sopenharmony_ci dev_warn(dev, "failed to read response, ret=%d\n", ret); 2688c2ecf20Sopenharmony_ci break; 2698c2ecf20Sopenharmony_ci } 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci if ((ret & ~SI1145_RSP_COUNTER_MASK) == 0) { 2728c2ecf20Sopenharmony_ci if (ret == data->rsp_seq) { 2738c2ecf20Sopenharmony_ci if (time_after(jiffies, stop_jiffies)) { 2748c2ecf20Sopenharmony_ci dev_warn(dev, "timeout on command %#02hhx\n", 2758c2ecf20Sopenharmony_ci cmd); 2768c2ecf20Sopenharmony_ci ret = -ETIMEDOUT; 2778c2ecf20Sopenharmony_ci break; 2788c2ecf20Sopenharmony_ci } 2798c2ecf20Sopenharmony_ci msleep(SI1145_COMMAND_MINSLEEP_MS); 2808c2ecf20Sopenharmony_ci continue; 2818c2ecf20Sopenharmony_ci } 2828c2ecf20Sopenharmony_ci if (ret == ((data->rsp_seq + 1) & 2838c2ecf20Sopenharmony_ci SI1145_RSP_COUNTER_MASK)) { 2848c2ecf20Sopenharmony_ci data->rsp_seq = ret; 2858c2ecf20Sopenharmony_ci ret = 0; 2868c2ecf20Sopenharmony_ci break; 2878c2ecf20Sopenharmony_ci } 2888c2ecf20Sopenharmony_ci dev_warn(dev, "unexpected response counter %d instead of %d\n", 2898c2ecf20Sopenharmony_ci ret, (data->rsp_seq + 1) & 2908c2ecf20Sopenharmony_ci SI1145_RSP_COUNTER_MASK); 2918c2ecf20Sopenharmony_ci ret = -EIO; 2928c2ecf20Sopenharmony_ci } else { 2938c2ecf20Sopenharmony_ci if (ret == SI1145_RSP_INVALID_SETTING) { 2948c2ecf20Sopenharmony_ci dev_warn(dev, "INVALID_SETTING error on command %#02hhx\n", 2958c2ecf20Sopenharmony_ci cmd); 2968c2ecf20Sopenharmony_ci ret = -EINVAL; 2978c2ecf20Sopenharmony_ci } else { 2988c2ecf20Sopenharmony_ci /* All overflows are treated identically */ 2998c2ecf20Sopenharmony_ci dev_dbg(dev, "overflow, ret=%d, cmd=%#02hhx\n", 3008c2ecf20Sopenharmony_ci ret, cmd); 3018c2ecf20Sopenharmony_ci ret = -EOVERFLOW; 3028c2ecf20Sopenharmony_ci } 3038c2ecf20Sopenharmony_ci } 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci /* Force a counter reset next time */ 3068c2ecf20Sopenharmony_ci data->rsp_seq = -1; 3078c2ecf20Sopenharmony_ci break; 3088c2ecf20Sopenharmony_ci } 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ciout: 3118c2ecf20Sopenharmony_ci mutex_unlock(&data->cmdlock); 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci return ret; 3148c2ecf20Sopenharmony_ci} 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_cistatic int si1145_param_update(struct si1145_data *data, u8 op, u8 param, 3178c2ecf20Sopenharmony_ci u8 value) 3188c2ecf20Sopenharmony_ci{ 3198c2ecf20Sopenharmony_ci int ret; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte_data(data->client, 3228c2ecf20Sopenharmony_ci SI1145_REG_PARAM_WR, value); 3238c2ecf20Sopenharmony_ci if (ret < 0) 3248c2ecf20Sopenharmony_ci return ret; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci return si1145_command(data, op | (param & 0x1F)); 3278c2ecf20Sopenharmony_ci} 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_cistatic int si1145_param_set(struct si1145_data *data, u8 param, u8 value) 3308c2ecf20Sopenharmony_ci{ 3318c2ecf20Sopenharmony_ci return si1145_param_update(data, SI1145_CMD_PARAM_SET, param, value); 3328c2ecf20Sopenharmony_ci} 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci/* Set param. Returns negative errno or current value */ 3358c2ecf20Sopenharmony_cistatic int si1145_param_query(struct si1145_data *data, u8 param) 3368c2ecf20Sopenharmony_ci{ 3378c2ecf20Sopenharmony_ci int ret; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci ret = si1145_command(data, SI1145_CMD_PARAM_QUERY | (param & 0x1F)); 3408c2ecf20Sopenharmony_ci if (ret < 0) 3418c2ecf20Sopenharmony_ci return ret; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci return i2c_smbus_read_byte_data(data->client, SI1145_REG_PARAM_RD); 3448c2ecf20Sopenharmony_ci} 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci/* Expand 8 bit compressed value to 16 bit, see Silabs AN498 */ 3478c2ecf20Sopenharmony_cistatic u16 si1145_uncompress(u8 x) 3488c2ecf20Sopenharmony_ci{ 3498c2ecf20Sopenharmony_ci u16 result = 0; 3508c2ecf20Sopenharmony_ci u8 exponent = 0; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci if (x < 8) 3538c2ecf20Sopenharmony_ci return 0; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci exponent = (x & 0xf0) >> 4; 3568c2ecf20Sopenharmony_ci result = 0x10 | (x & 0x0f); 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci if (exponent >= 4) 3598c2ecf20Sopenharmony_ci return result << (exponent - 4); 3608c2ecf20Sopenharmony_ci return result >> (4 - exponent); 3618c2ecf20Sopenharmony_ci} 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci/* Compress 16 bit value to 8 bit, see Silabs AN498 */ 3648c2ecf20Sopenharmony_cistatic u8 si1145_compress(u16 x) 3658c2ecf20Sopenharmony_ci{ 3668c2ecf20Sopenharmony_ci u32 exponent = 0; 3678c2ecf20Sopenharmony_ci u32 significand = 0; 3688c2ecf20Sopenharmony_ci u32 tmp = x; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci if (x == 0x0000) 3718c2ecf20Sopenharmony_ci return 0x00; 3728c2ecf20Sopenharmony_ci if (x == 0x0001) 3738c2ecf20Sopenharmony_ci return 0x08; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci while (1) { 3768c2ecf20Sopenharmony_ci tmp >>= 1; 3778c2ecf20Sopenharmony_ci exponent += 1; 3788c2ecf20Sopenharmony_ci if (tmp == 1) 3798c2ecf20Sopenharmony_ci break; 3808c2ecf20Sopenharmony_ci } 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci if (exponent < 5) { 3838c2ecf20Sopenharmony_ci significand = x << (4 - exponent); 3848c2ecf20Sopenharmony_ci return (exponent << 4) | (significand & 0xF); 3858c2ecf20Sopenharmony_ci } 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci significand = x >> (exponent - 5); 3888c2ecf20Sopenharmony_ci if (significand & 1) { 3898c2ecf20Sopenharmony_ci significand += 2; 3908c2ecf20Sopenharmony_ci if (significand & 0x0040) { 3918c2ecf20Sopenharmony_ci exponent += 1; 3928c2ecf20Sopenharmony_ci significand >>= 1; 3938c2ecf20Sopenharmony_ci } 3948c2ecf20Sopenharmony_ci } 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci return (exponent << 4) | ((significand >> 1) & 0xF); 3978c2ecf20Sopenharmony_ci} 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci/* Write meas_rate in hardware */ 4008c2ecf20Sopenharmony_cistatic int si1145_set_meas_rate(struct si1145_data *data, int interval) 4018c2ecf20Sopenharmony_ci{ 4028c2ecf20Sopenharmony_ci if (data->part_info->uncompressed_meas_rate) 4038c2ecf20Sopenharmony_ci return i2c_smbus_write_word_data(data->client, 4048c2ecf20Sopenharmony_ci SI1145_REG_MEAS_RATE, interval); 4058c2ecf20Sopenharmony_ci else 4068c2ecf20Sopenharmony_ci return i2c_smbus_write_byte_data(data->client, 4078c2ecf20Sopenharmony_ci SI1145_REG_MEAS_RATE, interval); 4088c2ecf20Sopenharmony_ci} 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_cistatic int si1145_read_samp_freq(struct si1145_data *data, int *val, int *val2) 4118c2ecf20Sopenharmony_ci{ 4128c2ecf20Sopenharmony_ci *val = 32000; 4138c2ecf20Sopenharmony_ci if (data->part_info->uncompressed_meas_rate) 4148c2ecf20Sopenharmony_ci *val2 = data->meas_rate; 4158c2ecf20Sopenharmony_ci else 4168c2ecf20Sopenharmony_ci *val2 = si1145_uncompress(data->meas_rate); 4178c2ecf20Sopenharmony_ci return IIO_VAL_FRACTIONAL; 4188c2ecf20Sopenharmony_ci} 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci/* Set the samp freq in driver private data */ 4218c2ecf20Sopenharmony_cistatic int si1145_store_samp_freq(struct si1145_data *data, int val) 4228c2ecf20Sopenharmony_ci{ 4238c2ecf20Sopenharmony_ci int ret = 0; 4248c2ecf20Sopenharmony_ci int meas_rate; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci if (val <= 0 || val > 32000) 4278c2ecf20Sopenharmony_ci return -ERANGE; 4288c2ecf20Sopenharmony_ci meas_rate = 32000 / val; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci mutex_lock(&data->lock); 4318c2ecf20Sopenharmony_ci if (data->autonomous) { 4328c2ecf20Sopenharmony_ci ret = si1145_set_meas_rate(data, meas_rate); 4338c2ecf20Sopenharmony_ci if (ret) 4348c2ecf20Sopenharmony_ci goto out; 4358c2ecf20Sopenharmony_ci } 4368c2ecf20Sopenharmony_ci if (data->part_info->uncompressed_meas_rate) 4378c2ecf20Sopenharmony_ci data->meas_rate = meas_rate; 4388c2ecf20Sopenharmony_ci else 4398c2ecf20Sopenharmony_ci data->meas_rate = si1145_compress(meas_rate); 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ciout: 4428c2ecf20Sopenharmony_ci mutex_unlock(&data->lock); 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci return ret; 4458c2ecf20Sopenharmony_ci} 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_cistatic irqreturn_t si1145_trigger_handler(int irq, void *private) 4488c2ecf20Sopenharmony_ci{ 4498c2ecf20Sopenharmony_ci struct iio_poll_func *pf = private; 4508c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = pf->indio_dev; 4518c2ecf20Sopenharmony_ci struct si1145_data *data = iio_priv(indio_dev); 4528c2ecf20Sopenharmony_ci int i, j = 0; 4538c2ecf20Sopenharmony_ci int ret; 4548c2ecf20Sopenharmony_ci u8 irq_status = 0; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci if (!data->autonomous) { 4578c2ecf20Sopenharmony_ci ret = si1145_command(data, SI1145_CMD_PSALS_FORCE); 4588c2ecf20Sopenharmony_ci if (ret < 0 && ret != -EOVERFLOW) 4598c2ecf20Sopenharmony_ci goto done; 4608c2ecf20Sopenharmony_ci } else { 4618c2ecf20Sopenharmony_ci irq_status = ret = i2c_smbus_read_byte_data(data->client, 4628c2ecf20Sopenharmony_ci SI1145_REG_IRQ_STATUS); 4638c2ecf20Sopenharmony_ci if (ret < 0) 4648c2ecf20Sopenharmony_ci goto done; 4658c2ecf20Sopenharmony_ci if (!(irq_status & SI1145_MASK_ALL_IE)) 4668c2ecf20Sopenharmony_ci goto done; 4678c2ecf20Sopenharmony_ci } 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci for_each_set_bit(i, indio_dev->active_scan_mask, 4708c2ecf20Sopenharmony_ci indio_dev->masklength) { 4718c2ecf20Sopenharmony_ci int run = 1; 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci while (i + run < indio_dev->masklength) { 4748c2ecf20Sopenharmony_ci if (!test_bit(i + run, indio_dev->active_scan_mask)) 4758c2ecf20Sopenharmony_ci break; 4768c2ecf20Sopenharmony_ci if (indio_dev->channels[i + run].address != 4778c2ecf20Sopenharmony_ci indio_dev->channels[i].address + 2 * run) 4788c2ecf20Sopenharmony_ci break; 4798c2ecf20Sopenharmony_ci run++; 4808c2ecf20Sopenharmony_ci } 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci ret = i2c_smbus_read_i2c_block_data_or_emulated( 4838c2ecf20Sopenharmony_ci data->client, indio_dev->channels[i].address, 4848c2ecf20Sopenharmony_ci sizeof(u16) * run, &data->buffer[j]); 4858c2ecf20Sopenharmony_ci if (ret < 0) 4868c2ecf20Sopenharmony_ci goto done; 4878c2ecf20Sopenharmony_ci j += run * sizeof(u16); 4888c2ecf20Sopenharmony_ci i += run - 1; 4898c2ecf20Sopenharmony_ci } 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci if (data->autonomous) { 4928c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte_data(data->client, 4938c2ecf20Sopenharmony_ci SI1145_REG_IRQ_STATUS, 4948c2ecf20Sopenharmony_ci irq_status & SI1145_MASK_ALL_IE); 4958c2ecf20Sopenharmony_ci if (ret < 0) 4968c2ecf20Sopenharmony_ci goto done; 4978c2ecf20Sopenharmony_ci } 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, 5008c2ecf20Sopenharmony_ci iio_get_time_ns(indio_dev)); 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_cidone: 5038c2ecf20Sopenharmony_ci iio_trigger_notify_done(indio_dev->trig); 5048c2ecf20Sopenharmony_ci return IRQ_HANDLED; 5058c2ecf20Sopenharmony_ci} 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_cistatic int si1145_set_chlist(struct iio_dev *indio_dev, unsigned long scan_mask) 5088c2ecf20Sopenharmony_ci{ 5098c2ecf20Sopenharmony_ci struct si1145_data *data = iio_priv(indio_dev); 5108c2ecf20Sopenharmony_ci u8 reg = 0, mux; 5118c2ecf20Sopenharmony_ci int ret; 5128c2ecf20Sopenharmony_ci int i; 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci /* channel list already set, no need to reprogram */ 5158c2ecf20Sopenharmony_ci if (data->scan_mask == scan_mask) 5168c2ecf20Sopenharmony_ci return 0; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci for_each_set_bit(i, &scan_mask, indio_dev->masklength) { 5198c2ecf20Sopenharmony_ci switch (indio_dev->channels[i].address) { 5208c2ecf20Sopenharmony_ci case SI1145_REG_ALSVIS_DATA: 5218c2ecf20Sopenharmony_ci reg |= SI1145_CHLIST_EN_ALSVIS; 5228c2ecf20Sopenharmony_ci break; 5238c2ecf20Sopenharmony_ci case SI1145_REG_ALSIR_DATA: 5248c2ecf20Sopenharmony_ci reg |= SI1145_CHLIST_EN_ALSIR; 5258c2ecf20Sopenharmony_ci break; 5268c2ecf20Sopenharmony_ci case SI1145_REG_PS1_DATA: 5278c2ecf20Sopenharmony_ci reg |= SI1145_CHLIST_EN_PS1; 5288c2ecf20Sopenharmony_ci break; 5298c2ecf20Sopenharmony_ci case SI1145_REG_PS2_DATA: 5308c2ecf20Sopenharmony_ci reg |= SI1145_CHLIST_EN_PS2; 5318c2ecf20Sopenharmony_ci break; 5328c2ecf20Sopenharmony_ci case SI1145_REG_PS3_DATA: 5338c2ecf20Sopenharmony_ci reg |= SI1145_CHLIST_EN_PS3; 5348c2ecf20Sopenharmony_ci break; 5358c2ecf20Sopenharmony_ci case SI1145_REG_AUX_DATA: 5368c2ecf20Sopenharmony_ci switch (indio_dev->channels[i].type) { 5378c2ecf20Sopenharmony_ci case IIO_UVINDEX: 5388c2ecf20Sopenharmony_ci reg |= SI1145_CHLIST_EN_UV; 5398c2ecf20Sopenharmony_ci break; 5408c2ecf20Sopenharmony_ci default: 5418c2ecf20Sopenharmony_ci reg |= SI1145_CHLIST_EN_AUX; 5428c2ecf20Sopenharmony_ci if (indio_dev->channels[i].type == IIO_TEMP) 5438c2ecf20Sopenharmony_ci mux = SI1145_MUX_TEMP; 5448c2ecf20Sopenharmony_ci else 5458c2ecf20Sopenharmony_ci mux = SI1145_MUX_VDD; 5468c2ecf20Sopenharmony_ci ret = si1145_param_set(data, 5478c2ecf20Sopenharmony_ci SI1145_PARAM_AUX_ADC_MUX, mux); 5488c2ecf20Sopenharmony_ci if (ret < 0) 5498c2ecf20Sopenharmony_ci return ret; 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci break; 5528c2ecf20Sopenharmony_ci } 5538c2ecf20Sopenharmony_ci } 5548c2ecf20Sopenharmony_ci } 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci data->scan_mask = scan_mask; 5578c2ecf20Sopenharmony_ci ret = si1145_param_set(data, SI1145_PARAM_CHLIST, reg); 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci return ret < 0 ? ret : 0; 5608c2ecf20Sopenharmony_ci} 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_cistatic int si1145_measure(struct iio_dev *indio_dev, 5638c2ecf20Sopenharmony_ci struct iio_chan_spec const *chan) 5648c2ecf20Sopenharmony_ci{ 5658c2ecf20Sopenharmony_ci struct si1145_data *data = iio_priv(indio_dev); 5668c2ecf20Sopenharmony_ci u8 cmd; 5678c2ecf20Sopenharmony_ci int ret; 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci ret = si1145_set_chlist(indio_dev, BIT(chan->scan_index)); 5708c2ecf20Sopenharmony_ci if (ret < 0) 5718c2ecf20Sopenharmony_ci return ret; 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci cmd = (chan->type == IIO_PROXIMITY) ? SI1145_CMD_PS_FORCE : 5748c2ecf20Sopenharmony_ci SI1145_CMD_ALS_FORCE; 5758c2ecf20Sopenharmony_ci ret = si1145_command(data, cmd); 5768c2ecf20Sopenharmony_ci if (ret < 0 && ret != -EOVERFLOW) 5778c2ecf20Sopenharmony_ci return ret; 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci return i2c_smbus_read_word_data(data->client, chan->address); 5808c2ecf20Sopenharmony_ci} 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci/* 5838c2ecf20Sopenharmony_ci * Conversion between iio scale and ADC_GAIN values 5848c2ecf20Sopenharmony_ci * These could be further adjusted but proximity/intensity are dimensionless 5858c2ecf20Sopenharmony_ci */ 5868c2ecf20Sopenharmony_cistatic const int si1145_proximity_scale_available[] = { 5878c2ecf20Sopenharmony_ci 128, 64, 32, 16, 8, 4}; 5888c2ecf20Sopenharmony_cistatic const int si1145_intensity_scale_available[] = { 5898c2ecf20Sopenharmony_ci 128, 64, 32, 16, 8, 4, 2, 1}; 5908c2ecf20Sopenharmony_cistatic IIO_CONST_ATTR(in_proximity_scale_available, 5918c2ecf20Sopenharmony_ci "128 64 32 16 8 4"); 5928c2ecf20Sopenharmony_cistatic IIO_CONST_ATTR(in_intensity_scale_available, 5938c2ecf20Sopenharmony_ci "128 64 32 16 8 4 2 1"); 5948c2ecf20Sopenharmony_cistatic IIO_CONST_ATTR(in_intensity_ir_scale_available, 5958c2ecf20Sopenharmony_ci "128 64 32 16 8 4 2 1"); 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_cistatic int si1145_scale_from_adcgain(int regval) 5988c2ecf20Sopenharmony_ci{ 5998c2ecf20Sopenharmony_ci return 128 >> regval; 6008c2ecf20Sopenharmony_ci} 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_cistatic int si1145_proximity_adcgain_from_scale(int val, int val2) 6038c2ecf20Sopenharmony_ci{ 6048c2ecf20Sopenharmony_ci val = find_closest_descending(val, si1145_proximity_scale_available, 6058c2ecf20Sopenharmony_ci ARRAY_SIZE(si1145_proximity_scale_available)); 6068c2ecf20Sopenharmony_ci if (val < 0 || val > 5 || val2 != 0) 6078c2ecf20Sopenharmony_ci return -EINVAL; 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci return val; 6108c2ecf20Sopenharmony_ci} 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_cistatic int si1145_intensity_adcgain_from_scale(int val, int val2) 6138c2ecf20Sopenharmony_ci{ 6148c2ecf20Sopenharmony_ci val = find_closest_descending(val, si1145_intensity_scale_available, 6158c2ecf20Sopenharmony_ci ARRAY_SIZE(si1145_intensity_scale_available)); 6168c2ecf20Sopenharmony_ci if (val < 0 || val > 7 || val2 != 0) 6178c2ecf20Sopenharmony_ci return -EINVAL; 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci return val; 6208c2ecf20Sopenharmony_ci} 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_cistatic int si1145_read_raw(struct iio_dev *indio_dev, 6238c2ecf20Sopenharmony_ci struct iio_chan_spec const *chan, 6248c2ecf20Sopenharmony_ci int *val, int *val2, long mask) 6258c2ecf20Sopenharmony_ci{ 6268c2ecf20Sopenharmony_ci struct si1145_data *data = iio_priv(indio_dev); 6278c2ecf20Sopenharmony_ci int ret; 6288c2ecf20Sopenharmony_ci u8 reg; 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci switch (mask) { 6318c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_RAW: 6328c2ecf20Sopenharmony_ci switch (chan->type) { 6338c2ecf20Sopenharmony_ci case IIO_INTENSITY: 6348c2ecf20Sopenharmony_ci case IIO_PROXIMITY: 6358c2ecf20Sopenharmony_ci case IIO_VOLTAGE: 6368c2ecf20Sopenharmony_ci case IIO_TEMP: 6378c2ecf20Sopenharmony_ci case IIO_UVINDEX: 6388c2ecf20Sopenharmony_ci ret = iio_device_claim_direct_mode(indio_dev); 6398c2ecf20Sopenharmony_ci if (ret) 6408c2ecf20Sopenharmony_ci return ret; 6418c2ecf20Sopenharmony_ci ret = si1145_measure(indio_dev, chan); 6428c2ecf20Sopenharmony_ci iio_device_release_direct_mode(indio_dev); 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci if (ret < 0) 6458c2ecf20Sopenharmony_ci return ret; 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci *val = ret; 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci return IIO_VAL_INT; 6508c2ecf20Sopenharmony_ci case IIO_CURRENT: 6518c2ecf20Sopenharmony_ci ret = i2c_smbus_read_byte_data(data->client, 6528c2ecf20Sopenharmony_ci SI1145_PS_LED_REG(chan->channel)); 6538c2ecf20Sopenharmony_ci if (ret < 0) 6548c2ecf20Sopenharmony_ci return ret; 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci *val = (ret >> SI1145_PS_LED_SHIFT(chan->channel)) 6578c2ecf20Sopenharmony_ci & 0x0f; 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci return IIO_VAL_INT; 6608c2ecf20Sopenharmony_ci default: 6618c2ecf20Sopenharmony_ci return -EINVAL; 6628c2ecf20Sopenharmony_ci } 6638c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_SCALE: 6648c2ecf20Sopenharmony_ci switch (chan->type) { 6658c2ecf20Sopenharmony_ci case IIO_PROXIMITY: 6668c2ecf20Sopenharmony_ci reg = SI1145_PARAM_PS_ADC_GAIN; 6678c2ecf20Sopenharmony_ci break; 6688c2ecf20Sopenharmony_ci case IIO_INTENSITY: 6698c2ecf20Sopenharmony_ci if (chan->channel2 == IIO_MOD_LIGHT_IR) 6708c2ecf20Sopenharmony_ci reg = SI1145_PARAM_ALSIR_ADC_GAIN; 6718c2ecf20Sopenharmony_ci else 6728c2ecf20Sopenharmony_ci reg = SI1145_PARAM_ALSVIS_ADC_GAIN; 6738c2ecf20Sopenharmony_ci break; 6748c2ecf20Sopenharmony_ci case IIO_TEMP: 6758c2ecf20Sopenharmony_ci *val = 28; 6768c2ecf20Sopenharmony_ci *val2 = 571429; 6778c2ecf20Sopenharmony_ci return IIO_VAL_INT_PLUS_MICRO; 6788c2ecf20Sopenharmony_ci case IIO_UVINDEX: 6798c2ecf20Sopenharmony_ci *val = 0; 6808c2ecf20Sopenharmony_ci *val2 = 10000; 6818c2ecf20Sopenharmony_ci return IIO_VAL_INT_PLUS_MICRO; 6828c2ecf20Sopenharmony_ci default: 6838c2ecf20Sopenharmony_ci return -EINVAL; 6848c2ecf20Sopenharmony_ci } 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci ret = si1145_param_query(data, reg); 6878c2ecf20Sopenharmony_ci if (ret < 0) 6888c2ecf20Sopenharmony_ci return ret; 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci *val = si1145_scale_from_adcgain(ret & 0x07); 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci return IIO_VAL_INT; 6938c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_OFFSET: 6948c2ecf20Sopenharmony_ci switch (chan->type) { 6958c2ecf20Sopenharmony_ci case IIO_TEMP: 6968c2ecf20Sopenharmony_ci /* 6978c2ecf20Sopenharmony_ci * -ADC offset - ADC counts @ 25°C - 6988c2ecf20Sopenharmony_ci * 35 * ADC counts / °C 6998c2ecf20Sopenharmony_ci */ 7008c2ecf20Sopenharmony_ci *val = -256 - 11136 + 25 * 35; 7018c2ecf20Sopenharmony_ci return IIO_VAL_INT; 7028c2ecf20Sopenharmony_ci default: 7038c2ecf20Sopenharmony_ci /* 7048c2ecf20Sopenharmony_ci * All ADC measurements have are by default offset 7058c2ecf20Sopenharmony_ci * by -256 7068c2ecf20Sopenharmony_ci * See AN498 5.6.3 7078c2ecf20Sopenharmony_ci */ 7088c2ecf20Sopenharmony_ci ret = si1145_param_query(data, SI1145_PARAM_ADC_OFFSET); 7098c2ecf20Sopenharmony_ci if (ret < 0) 7108c2ecf20Sopenharmony_ci return ret; 7118c2ecf20Sopenharmony_ci *val = -si1145_uncompress(ret); 7128c2ecf20Sopenharmony_ci return IIO_VAL_INT; 7138c2ecf20Sopenharmony_ci } 7148c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_SAMP_FREQ: 7158c2ecf20Sopenharmony_ci return si1145_read_samp_freq(data, val, val2); 7168c2ecf20Sopenharmony_ci default: 7178c2ecf20Sopenharmony_ci return -EINVAL; 7188c2ecf20Sopenharmony_ci } 7198c2ecf20Sopenharmony_ci} 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_cistatic int si1145_write_raw(struct iio_dev *indio_dev, 7228c2ecf20Sopenharmony_ci struct iio_chan_spec const *chan, 7238c2ecf20Sopenharmony_ci int val, int val2, long mask) 7248c2ecf20Sopenharmony_ci{ 7258c2ecf20Sopenharmony_ci struct si1145_data *data = iio_priv(indio_dev); 7268c2ecf20Sopenharmony_ci u8 reg1, reg2, shift; 7278c2ecf20Sopenharmony_ci int ret; 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci switch (mask) { 7308c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_SCALE: 7318c2ecf20Sopenharmony_ci switch (chan->type) { 7328c2ecf20Sopenharmony_ci case IIO_PROXIMITY: 7338c2ecf20Sopenharmony_ci val = si1145_proximity_adcgain_from_scale(val, val2); 7348c2ecf20Sopenharmony_ci if (val < 0) 7358c2ecf20Sopenharmony_ci return val; 7368c2ecf20Sopenharmony_ci reg1 = SI1145_PARAM_PS_ADC_GAIN; 7378c2ecf20Sopenharmony_ci reg2 = SI1145_PARAM_PS_ADC_COUNTER; 7388c2ecf20Sopenharmony_ci break; 7398c2ecf20Sopenharmony_ci case IIO_INTENSITY: 7408c2ecf20Sopenharmony_ci val = si1145_intensity_adcgain_from_scale(val, val2); 7418c2ecf20Sopenharmony_ci if (val < 0) 7428c2ecf20Sopenharmony_ci return val; 7438c2ecf20Sopenharmony_ci if (chan->channel2 == IIO_MOD_LIGHT_IR) { 7448c2ecf20Sopenharmony_ci reg1 = SI1145_PARAM_ALSIR_ADC_GAIN; 7458c2ecf20Sopenharmony_ci reg2 = SI1145_PARAM_ALSIR_ADC_COUNTER; 7468c2ecf20Sopenharmony_ci } else { 7478c2ecf20Sopenharmony_ci reg1 = SI1145_PARAM_ALSVIS_ADC_GAIN; 7488c2ecf20Sopenharmony_ci reg2 = SI1145_PARAM_ALSVIS_ADC_COUNTER; 7498c2ecf20Sopenharmony_ci } 7508c2ecf20Sopenharmony_ci break; 7518c2ecf20Sopenharmony_ci default: 7528c2ecf20Sopenharmony_ci return -EINVAL; 7538c2ecf20Sopenharmony_ci } 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci ret = iio_device_claim_direct_mode(indio_dev); 7568c2ecf20Sopenharmony_ci if (ret) 7578c2ecf20Sopenharmony_ci return ret; 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci ret = si1145_param_set(data, reg1, val); 7608c2ecf20Sopenharmony_ci if (ret < 0) { 7618c2ecf20Sopenharmony_ci iio_device_release_direct_mode(indio_dev); 7628c2ecf20Sopenharmony_ci return ret; 7638c2ecf20Sopenharmony_ci } 7648c2ecf20Sopenharmony_ci /* Set recovery period to one's complement of gain */ 7658c2ecf20Sopenharmony_ci ret = si1145_param_set(data, reg2, (~val & 0x07) << 4); 7668c2ecf20Sopenharmony_ci iio_device_release_direct_mode(indio_dev); 7678c2ecf20Sopenharmony_ci return ret; 7688c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_RAW: 7698c2ecf20Sopenharmony_ci if (chan->type != IIO_CURRENT) 7708c2ecf20Sopenharmony_ci return -EINVAL; 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci if (val < 0 || val > 15 || val2 != 0) 7738c2ecf20Sopenharmony_ci return -EINVAL; 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci reg1 = SI1145_PS_LED_REG(chan->channel); 7768c2ecf20Sopenharmony_ci shift = SI1145_PS_LED_SHIFT(chan->channel); 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci ret = iio_device_claim_direct_mode(indio_dev); 7798c2ecf20Sopenharmony_ci if (ret) 7808c2ecf20Sopenharmony_ci return ret; 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci ret = i2c_smbus_read_byte_data(data->client, reg1); 7838c2ecf20Sopenharmony_ci if (ret < 0) { 7848c2ecf20Sopenharmony_ci iio_device_release_direct_mode(indio_dev); 7858c2ecf20Sopenharmony_ci return ret; 7868c2ecf20Sopenharmony_ci } 7878c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte_data(data->client, reg1, 7888c2ecf20Sopenharmony_ci (ret & ~(0x0f << shift)) | 7898c2ecf20Sopenharmony_ci ((val & 0x0f) << shift)); 7908c2ecf20Sopenharmony_ci iio_device_release_direct_mode(indio_dev); 7918c2ecf20Sopenharmony_ci return ret; 7928c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_SAMP_FREQ: 7938c2ecf20Sopenharmony_ci return si1145_store_samp_freq(data, val); 7948c2ecf20Sopenharmony_ci default: 7958c2ecf20Sopenharmony_ci return -EINVAL; 7968c2ecf20Sopenharmony_ci } 7978c2ecf20Sopenharmony_ci} 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci#define SI1145_ST { \ 8008c2ecf20Sopenharmony_ci .sign = 'u', \ 8018c2ecf20Sopenharmony_ci .realbits = 16, \ 8028c2ecf20Sopenharmony_ci .storagebits = 16, \ 8038c2ecf20Sopenharmony_ci .endianness = IIO_LE, \ 8048c2ecf20Sopenharmony_ci} 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci#define SI1145_INTENSITY_CHANNEL(_si) { \ 8078c2ecf20Sopenharmony_ci .type = IIO_INTENSITY, \ 8088c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ 8098c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_OFFSET) | \ 8108c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_SCALE), \ 8118c2ecf20Sopenharmony_ci .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ 8128c2ecf20Sopenharmony_ci .scan_type = SI1145_ST, \ 8138c2ecf20Sopenharmony_ci .scan_index = _si, \ 8148c2ecf20Sopenharmony_ci .address = SI1145_REG_ALSVIS_DATA, \ 8158c2ecf20Sopenharmony_ci} 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci#define SI1145_INTENSITY_IR_CHANNEL(_si) { \ 8188c2ecf20Sopenharmony_ci .type = IIO_INTENSITY, \ 8198c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ 8208c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_OFFSET) | \ 8218c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_SCALE), \ 8228c2ecf20Sopenharmony_ci .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ 8238c2ecf20Sopenharmony_ci .modified = 1, \ 8248c2ecf20Sopenharmony_ci .channel2 = IIO_MOD_LIGHT_IR, \ 8258c2ecf20Sopenharmony_ci .scan_type = SI1145_ST, \ 8268c2ecf20Sopenharmony_ci .scan_index = _si, \ 8278c2ecf20Sopenharmony_ci .address = SI1145_REG_ALSIR_DATA, \ 8288c2ecf20Sopenharmony_ci} 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci#define SI1145_TEMP_CHANNEL(_si) { \ 8318c2ecf20Sopenharmony_ci .type = IIO_TEMP, \ 8328c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ 8338c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_OFFSET) | \ 8348c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_SCALE), \ 8358c2ecf20Sopenharmony_ci .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ 8368c2ecf20Sopenharmony_ci .scan_type = SI1145_ST, \ 8378c2ecf20Sopenharmony_ci .scan_index = _si, \ 8388c2ecf20Sopenharmony_ci .address = SI1145_REG_AUX_DATA, \ 8398c2ecf20Sopenharmony_ci} 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci#define SI1145_UV_CHANNEL(_si) { \ 8428c2ecf20Sopenharmony_ci .type = IIO_UVINDEX, \ 8438c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ 8448c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_SCALE), \ 8458c2ecf20Sopenharmony_ci .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ 8468c2ecf20Sopenharmony_ci .scan_type = SI1145_ST, \ 8478c2ecf20Sopenharmony_ci .scan_index = _si, \ 8488c2ecf20Sopenharmony_ci .address = SI1145_REG_AUX_DATA, \ 8498c2ecf20Sopenharmony_ci} 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci#define SI1145_PROXIMITY_CHANNEL(_si, _ch) { \ 8528c2ecf20Sopenharmony_ci .type = IIO_PROXIMITY, \ 8538c2ecf20Sopenharmony_ci .indexed = 1, \ 8548c2ecf20Sopenharmony_ci .channel = _ch, \ 8558c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ 8568c2ecf20Sopenharmony_ci .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ 8578c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_OFFSET), \ 8588c2ecf20Sopenharmony_ci .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ 8598c2ecf20Sopenharmony_ci .scan_type = SI1145_ST, \ 8608c2ecf20Sopenharmony_ci .scan_index = _si, \ 8618c2ecf20Sopenharmony_ci .address = SI1145_REG_PS1_DATA + _ch * 2, \ 8628c2ecf20Sopenharmony_ci} 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci#define SI1145_VOLTAGE_CHANNEL(_si) { \ 8658c2ecf20Sopenharmony_ci .type = IIO_VOLTAGE, \ 8668c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ 8678c2ecf20Sopenharmony_ci .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ 8688c2ecf20Sopenharmony_ci .scan_type = SI1145_ST, \ 8698c2ecf20Sopenharmony_ci .scan_index = _si, \ 8708c2ecf20Sopenharmony_ci .address = SI1145_REG_AUX_DATA, \ 8718c2ecf20Sopenharmony_ci} 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci#define SI1145_CURRENT_CHANNEL(_ch) { \ 8748c2ecf20Sopenharmony_ci .type = IIO_CURRENT, \ 8758c2ecf20Sopenharmony_ci .indexed = 1, \ 8768c2ecf20Sopenharmony_ci .channel = _ch, \ 8778c2ecf20Sopenharmony_ci .output = 1, \ 8788c2ecf20Sopenharmony_ci .scan_index = -1, \ 8798c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ 8808c2ecf20Sopenharmony_ci} 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_cistatic const struct iio_chan_spec si1132_channels[] = { 8838c2ecf20Sopenharmony_ci SI1145_INTENSITY_CHANNEL(0), 8848c2ecf20Sopenharmony_ci SI1145_INTENSITY_IR_CHANNEL(1), 8858c2ecf20Sopenharmony_ci SI1145_TEMP_CHANNEL(2), 8868c2ecf20Sopenharmony_ci SI1145_VOLTAGE_CHANNEL(3), 8878c2ecf20Sopenharmony_ci SI1145_UV_CHANNEL(4), 8888c2ecf20Sopenharmony_ci IIO_CHAN_SOFT_TIMESTAMP(6), 8898c2ecf20Sopenharmony_ci}; 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_cistatic const struct iio_chan_spec si1141_channels[] = { 8928c2ecf20Sopenharmony_ci SI1145_INTENSITY_CHANNEL(0), 8938c2ecf20Sopenharmony_ci SI1145_INTENSITY_IR_CHANNEL(1), 8948c2ecf20Sopenharmony_ci SI1145_PROXIMITY_CHANNEL(2, 0), 8958c2ecf20Sopenharmony_ci SI1145_TEMP_CHANNEL(3), 8968c2ecf20Sopenharmony_ci SI1145_VOLTAGE_CHANNEL(4), 8978c2ecf20Sopenharmony_ci IIO_CHAN_SOFT_TIMESTAMP(5), 8988c2ecf20Sopenharmony_ci SI1145_CURRENT_CHANNEL(0), 8998c2ecf20Sopenharmony_ci}; 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_cistatic const struct iio_chan_spec si1142_channels[] = { 9028c2ecf20Sopenharmony_ci SI1145_INTENSITY_CHANNEL(0), 9038c2ecf20Sopenharmony_ci SI1145_INTENSITY_IR_CHANNEL(1), 9048c2ecf20Sopenharmony_ci SI1145_PROXIMITY_CHANNEL(2, 0), 9058c2ecf20Sopenharmony_ci SI1145_PROXIMITY_CHANNEL(3, 1), 9068c2ecf20Sopenharmony_ci SI1145_TEMP_CHANNEL(4), 9078c2ecf20Sopenharmony_ci SI1145_VOLTAGE_CHANNEL(5), 9088c2ecf20Sopenharmony_ci IIO_CHAN_SOFT_TIMESTAMP(6), 9098c2ecf20Sopenharmony_ci SI1145_CURRENT_CHANNEL(0), 9108c2ecf20Sopenharmony_ci SI1145_CURRENT_CHANNEL(1), 9118c2ecf20Sopenharmony_ci}; 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_cistatic const struct iio_chan_spec si1143_channels[] = { 9148c2ecf20Sopenharmony_ci SI1145_INTENSITY_CHANNEL(0), 9158c2ecf20Sopenharmony_ci SI1145_INTENSITY_IR_CHANNEL(1), 9168c2ecf20Sopenharmony_ci SI1145_PROXIMITY_CHANNEL(2, 0), 9178c2ecf20Sopenharmony_ci SI1145_PROXIMITY_CHANNEL(3, 1), 9188c2ecf20Sopenharmony_ci SI1145_PROXIMITY_CHANNEL(4, 2), 9198c2ecf20Sopenharmony_ci SI1145_TEMP_CHANNEL(5), 9208c2ecf20Sopenharmony_ci SI1145_VOLTAGE_CHANNEL(6), 9218c2ecf20Sopenharmony_ci IIO_CHAN_SOFT_TIMESTAMP(7), 9228c2ecf20Sopenharmony_ci SI1145_CURRENT_CHANNEL(0), 9238c2ecf20Sopenharmony_ci SI1145_CURRENT_CHANNEL(1), 9248c2ecf20Sopenharmony_ci SI1145_CURRENT_CHANNEL(2), 9258c2ecf20Sopenharmony_ci}; 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_cistatic const struct iio_chan_spec si1145_channels[] = { 9288c2ecf20Sopenharmony_ci SI1145_INTENSITY_CHANNEL(0), 9298c2ecf20Sopenharmony_ci SI1145_INTENSITY_IR_CHANNEL(1), 9308c2ecf20Sopenharmony_ci SI1145_PROXIMITY_CHANNEL(2, 0), 9318c2ecf20Sopenharmony_ci SI1145_TEMP_CHANNEL(3), 9328c2ecf20Sopenharmony_ci SI1145_VOLTAGE_CHANNEL(4), 9338c2ecf20Sopenharmony_ci SI1145_UV_CHANNEL(5), 9348c2ecf20Sopenharmony_ci IIO_CHAN_SOFT_TIMESTAMP(6), 9358c2ecf20Sopenharmony_ci SI1145_CURRENT_CHANNEL(0), 9368c2ecf20Sopenharmony_ci}; 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_cistatic const struct iio_chan_spec si1146_channels[] = { 9398c2ecf20Sopenharmony_ci SI1145_INTENSITY_CHANNEL(0), 9408c2ecf20Sopenharmony_ci SI1145_INTENSITY_IR_CHANNEL(1), 9418c2ecf20Sopenharmony_ci SI1145_TEMP_CHANNEL(2), 9428c2ecf20Sopenharmony_ci SI1145_VOLTAGE_CHANNEL(3), 9438c2ecf20Sopenharmony_ci SI1145_UV_CHANNEL(4), 9448c2ecf20Sopenharmony_ci SI1145_PROXIMITY_CHANNEL(5, 0), 9458c2ecf20Sopenharmony_ci SI1145_PROXIMITY_CHANNEL(6, 1), 9468c2ecf20Sopenharmony_ci IIO_CHAN_SOFT_TIMESTAMP(7), 9478c2ecf20Sopenharmony_ci SI1145_CURRENT_CHANNEL(0), 9488c2ecf20Sopenharmony_ci SI1145_CURRENT_CHANNEL(1), 9498c2ecf20Sopenharmony_ci}; 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_cistatic const struct iio_chan_spec si1147_channels[] = { 9528c2ecf20Sopenharmony_ci SI1145_INTENSITY_CHANNEL(0), 9538c2ecf20Sopenharmony_ci SI1145_INTENSITY_IR_CHANNEL(1), 9548c2ecf20Sopenharmony_ci SI1145_PROXIMITY_CHANNEL(2, 0), 9558c2ecf20Sopenharmony_ci SI1145_PROXIMITY_CHANNEL(3, 1), 9568c2ecf20Sopenharmony_ci SI1145_PROXIMITY_CHANNEL(4, 2), 9578c2ecf20Sopenharmony_ci SI1145_TEMP_CHANNEL(5), 9588c2ecf20Sopenharmony_ci SI1145_VOLTAGE_CHANNEL(6), 9598c2ecf20Sopenharmony_ci SI1145_UV_CHANNEL(7), 9608c2ecf20Sopenharmony_ci IIO_CHAN_SOFT_TIMESTAMP(8), 9618c2ecf20Sopenharmony_ci SI1145_CURRENT_CHANNEL(0), 9628c2ecf20Sopenharmony_ci SI1145_CURRENT_CHANNEL(1), 9638c2ecf20Sopenharmony_ci SI1145_CURRENT_CHANNEL(2), 9648c2ecf20Sopenharmony_ci}; 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_cistatic struct attribute *si1132_attributes[] = { 9678c2ecf20Sopenharmony_ci &iio_const_attr_in_intensity_scale_available.dev_attr.attr, 9688c2ecf20Sopenharmony_ci &iio_const_attr_in_intensity_ir_scale_available.dev_attr.attr, 9698c2ecf20Sopenharmony_ci NULL, 9708c2ecf20Sopenharmony_ci}; 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_cistatic struct attribute *si114x_attributes[] = { 9738c2ecf20Sopenharmony_ci &iio_const_attr_in_intensity_scale_available.dev_attr.attr, 9748c2ecf20Sopenharmony_ci &iio_const_attr_in_intensity_ir_scale_available.dev_attr.attr, 9758c2ecf20Sopenharmony_ci &iio_const_attr_in_proximity_scale_available.dev_attr.attr, 9768c2ecf20Sopenharmony_ci NULL, 9778c2ecf20Sopenharmony_ci}; 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_cistatic const struct attribute_group si1132_attribute_group = { 9808c2ecf20Sopenharmony_ci .attrs = si1132_attributes, 9818c2ecf20Sopenharmony_ci}; 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_cistatic const struct attribute_group si114x_attribute_group = { 9848c2ecf20Sopenharmony_ci .attrs = si114x_attributes, 9858c2ecf20Sopenharmony_ci}; 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_cistatic const struct iio_info si1132_info = { 9898c2ecf20Sopenharmony_ci .read_raw = si1145_read_raw, 9908c2ecf20Sopenharmony_ci .write_raw = si1145_write_raw, 9918c2ecf20Sopenharmony_ci .attrs = &si1132_attribute_group, 9928c2ecf20Sopenharmony_ci}; 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_cistatic const struct iio_info si114x_info = { 9958c2ecf20Sopenharmony_ci .read_raw = si1145_read_raw, 9968c2ecf20Sopenharmony_ci .write_raw = si1145_write_raw, 9978c2ecf20Sopenharmony_ci .attrs = &si114x_attribute_group, 9988c2ecf20Sopenharmony_ci}; 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ci#define SI1145_PART(id, iio_info, chans, leds, uncompressed_meas_rate) \ 10018c2ecf20Sopenharmony_ci {id, iio_info, chans, ARRAY_SIZE(chans), leds, uncompressed_meas_rate} 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_cistatic const struct si1145_part_info si1145_part_info[] = { 10048c2ecf20Sopenharmony_ci [SI1132] = SI1145_PART(0x32, &si1132_info, si1132_channels, 0, true), 10058c2ecf20Sopenharmony_ci [SI1141] = SI1145_PART(0x41, &si114x_info, si1141_channels, 1, false), 10068c2ecf20Sopenharmony_ci [SI1142] = SI1145_PART(0x42, &si114x_info, si1142_channels, 2, false), 10078c2ecf20Sopenharmony_ci [SI1143] = SI1145_PART(0x43, &si114x_info, si1143_channels, 3, false), 10088c2ecf20Sopenharmony_ci [SI1145] = SI1145_PART(0x45, &si114x_info, si1145_channels, 1, true), 10098c2ecf20Sopenharmony_ci [SI1146] = SI1145_PART(0x46, &si114x_info, si1146_channels, 2, true), 10108c2ecf20Sopenharmony_ci [SI1147] = SI1145_PART(0x47, &si114x_info, si1147_channels, 3, true), 10118c2ecf20Sopenharmony_ci}; 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_cistatic int si1145_initialize(struct si1145_data *data) 10148c2ecf20Sopenharmony_ci{ 10158c2ecf20Sopenharmony_ci struct i2c_client *client = data->client; 10168c2ecf20Sopenharmony_ci int ret; 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte_data(client, SI1145_REG_COMMAND, 10198c2ecf20Sopenharmony_ci SI1145_CMD_RESET); 10208c2ecf20Sopenharmony_ci if (ret < 0) 10218c2ecf20Sopenharmony_ci return ret; 10228c2ecf20Sopenharmony_ci msleep(SI1145_COMMAND_TIMEOUT_MS); 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_ci /* Hardware key, magic value */ 10258c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte_data(client, SI1145_REG_HW_KEY, 0x17); 10268c2ecf20Sopenharmony_ci if (ret < 0) 10278c2ecf20Sopenharmony_ci return ret; 10288c2ecf20Sopenharmony_ci msleep(SI1145_COMMAND_TIMEOUT_MS); 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_ci /* Turn off autonomous mode */ 10318c2ecf20Sopenharmony_ci ret = si1145_set_meas_rate(data, 0); 10328c2ecf20Sopenharmony_ci if (ret < 0) 10338c2ecf20Sopenharmony_ci return ret; 10348c2ecf20Sopenharmony_ci 10358c2ecf20Sopenharmony_ci /* Initialize sampling freq to 10 Hz */ 10368c2ecf20Sopenharmony_ci ret = si1145_store_samp_freq(data, 10); 10378c2ecf20Sopenharmony_ci if (ret < 0) 10388c2ecf20Sopenharmony_ci return ret; 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_ci /* Set LED currents to 45 mA; have 4 bits, see Table 2 in datasheet */ 10418c2ecf20Sopenharmony_ci switch (data->part_info->num_leds) { 10428c2ecf20Sopenharmony_ci case 3: 10438c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte_data(client, 10448c2ecf20Sopenharmony_ci SI1145_REG_PS_LED3, 10458c2ecf20Sopenharmony_ci SI1145_LED_CURRENT_45mA); 10468c2ecf20Sopenharmony_ci if (ret < 0) 10478c2ecf20Sopenharmony_ci return ret; 10488c2ecf20Sopenharmony_ci fallthrough; 10498c2ecf20Sopenharmony_ci case 2: 10508c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte_data(client, 10518c2ecf20Sopenharmony_ci SI1145_REG_PS_LED21, 10528c2ecf20Sopenharmony_ci (SI1145_LED_CURRENT_45mA << 4) | 10538c2ecf20Sopenharmony_ci SI1145_LED_CURRENT_45mA); 10548c2ecf20Sopenharmony_ci break; 10558c2ecf20Sopenharmony_ci case 1: 10568c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte_data(client, 10578c2ecf20Sopenharmony_ci SI1145_REG_PS_LED21, 10588c2ecf20Sopenharmony_ci SI1145_LED_CURRENT_45mA); 10598c2ecf20Sopenharmony_ci break; 10608c2ecf20Sopenharmony_ci default: 10618c2ecf20Sopenharmony_ci ret = 0; 10628c2ecf20Sopenharmony_ci break; 10638c2ecf20Sopenharmony_ci } 10648c2ecf20Sopenharmony_ci if (ret < 0) 10658c2ecf20Sopenharmony_ci return ret; 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci /* Set normal proximity measurement mode */ 10688c2ecf20Sopenharmony_ci ret = si1145_param_set(data, SI1145_PARAM_PS_ADC_MISC, 10698c2ecf20Sopenharmony_ci SI1145_PS_ADC_MODE_NORMAL); 10708c2ecf20Sopenharmony_ci if (ret < 0) 10718c2ecf20Sopenharmony_ci return ret; 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci ret = si1145_param_set(data, SI1145_PARAM_PS_ADC_GAIN, 0x01); 10748c2ecf20Sopenharmony_ci if (ret < 0) 10758c2ecf20Sopenharmony_ci return ret; 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_ci /* ADC_COUNTER should be one complement of ADC_GAIN */ 10788c2ecf20Sopenharmony_ci ret = si1145_param_set(data, SI1145_PARAM_PS_ADC_COUNTER, 0x06 << 4); 10798c2ecf20Sopenharmony_ci if (ret < 0) 10808c2ecf20Sopenharmony_ci return ret; 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_ci /* Set ALS visible measurement mode */ 10838c2ecf20Sopenharmony_ci ret = si1145_param_set(data, SI1145_PARAM_ALSVIS_ADC_MISC, 10848c2ecf20Sopenharmony_ci SI1145_ADC_MISC_RANGE); 10858c2ecf20Sopenharmony_ci if (ret < 0) 10868c2ecf20Sopenharmony_ci return ret; 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci ret = si1145_param_set(data, SI1145_PARAM_ALSVIS_ADC_GAIN, 0x03); 10898c2ecf20Sopenharmony_ci if (ret < 0) 10908c2ecf20Sopenharmony_ci return ret; 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci ret = si1145_param_set(data, SI1145_PARAM_ALSVIS_ADC_COUNTER, 10938c2ecf20Sopenharmony_ci 0x04 << 4); 10948c2ecf20Sopenharmony_ci if (ret < 0) 10958c2ecf20Sopenharmony_ci return ret; 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_ci /* Set ALS IR measurement mode */ 10988c2ecf20Sopenharmony_ci ret = si1145_param_set(data, SI1145_PARAM_ALSIR_ADC_MISC, 10998c2ecf20Sopenharmony_ci SI1145_ADC_MISC_RANGE); 11008c2ecf20Sopenharmony_ci if (ret < 0) 11018c2ecf20Sopenharmony_ci return ret; 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_ci ret = si1145_param_set(data, SI1145_PARAM_ALSIR_ADC_GAIN, 0x01); 11048c2ecf20Sopenharmony_ci if (ret < 0) 11058c2ecf20Sopenharmony_ci return ret; 11068c2ecf20Sopenharmony_ci 11078c2ecf20Sopenharmony_ci ret = si1145_param_set(data, SI1145_PARAM_ALSIR_ADC_COUNTER, 11088c2ecf20Sopenharmony_ci 0x06 << 4); 11098c2ecf20Sopenharmony_ci if (ret < 0) 11108c2ecf20Sopenharmony_ci return ret; 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci /* 11138c2ecf20Sopenharmony_ci * Initialize UCOEF to default values in datasheet 11148c2ecf20Sopenharmony_ci * These registers are normally zero on reset 11158c2ecf20Sopenharmony_ci */ 11168c2ecf20Sopenharmony_ci if (data->part_info == &si1145_part_info[SI1132] || 11178c2ecf20Sopenharmony_ci data->part_info == &si1145_part_info[SI1145] || 11188c2ecf20Sopenharmony_ci data->part_info == &si1145_part_info[SI1146] || 11198c2ecf20Sopenharmony_ci data->part_info == &si1145_part_info[SI1147]) { 11208c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte_data(data->client, 11218c2ecf20Sopenharmony_ci SI1145_REG_UCOEF1, 11228c2ecf20Sopenharmony_ci SI1145_UCOEF1_DEFAULT); 11238c2ecf20Sopenharmony_ci if (ret < 0) 11248c2ecf20Sopenharmony_ci return ret; 11258c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte_data(data->client, 11268c2ecf20Sopenharmony_ci SI1145_REG_UCOEF2, SI1145_UCOEF2_DEFAULT); 11278c2ecf20Sopenharmony_ci if (ret < 0) 11288c2ecf20Sopenharmony_ci return ret; 11298c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte_data(data->client, 11308c2ecf20Sopenharmony_ci SI1145_REG_UCOEF3, SI1145_UCOEF3_DEFAULT); 11318c2ecf20Sopenharmony_ci if (ret < 0) 11328c2ecf20Sopenharmony_ci return ret; 11338c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte_data(data->client, 11348c2ecf20Sopenharmony_ci SI1145_REG_UCOEF4, SI1145_UCOEF4_DEFAULT); 11358c2ecf20Sopenharmony_ci if (ret < 0) 11368c2ecf20Sopenharmony_ci return ret; 11378c2ecf20Sopenharmony_ci } 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_ci return 0; 11408c2ecf20Sopenharmony_ci} 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_ci/* 11438c2ecf20Sopenharmony_ci * Program the channels we want to measure with CMD_PSALS_AUTO. No need for 11448c2ecf20Sopenharmony_ci * _postdisable as we stop with CMD_PSALS_PAUSE; single measurement (direct) 11458c2ecf20Sopenharmony_ci * mode reprograms the channels list anyway... 11468c2ecf20Sopenharmony_ci */ 11478c2ecf20Sopenharmony_cistatic int si1145_buffer_preenable(struct iio_dev *indio_dev) 11488c2ecf20Sopenharmony_ci{ 11498c2ecf20Sopenharmony_ci struct si1145_data *data = iio_priv(indio_dev); 11508c2ecf20Sopenharmony_ci int ret; 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_ci mutex_lock(&data->lock); 11538c2ecf20Sopenharmony_ci ret = si1145_set_chlist(indio_dev, *indio_dev->active_scan_mask); 11548c2ecf20Sopenharmony_ci mutex_unlock(&data->lock); 11558c2ecf20Sopenharmony_ci 11568c2ecf20Sopenharmony_ci return ret; 11578c2ecf20Sopenharmony_ci} 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_cistatic bool si1145_validate_scan_mask(struct iio_dev *indio_dev, 11608c2ecf20Sopenharmony_ci const unsigned long *scan_mask) 11618c2ecf20Sopenharmony_ci{ 11628c2ecf20Sopenharmony_ci struct si1145_data *data = iio_priv(indio_dev); 11638c2ecf20Sopenharmony_ci unsigned int count = 0; 11648c2ecf20Sopenharmony_ci int i; 11658c2ecf20Sopenharmony_ci 11668c2ecf20Sopenharmony_ci /* Check that at most one AUX channel is enabled */ 11678c2ecf20Sopenharmony_ci for_each_set_bit(i, scan_mask, data->part_info->num_channels) { 11688c2ecf20Sopenharmony_ci if (indio_dev->channels[i].address == SI1145_REG_AUX_DATA) 11698c2ecf20Sopenharmony_ci count++; 11708c2ecf20Sopenharmony_ci } 11718c2ecf20Sopenharmony_ci 11728c2ecf20Sopenharmony_ci return count <= 1; 11738c2ecf20Sopenharmony_ci} 11748c2ecf20Sopenharmony_ci 11758c2ecf20Sopenharmony_cistatic const struct iio_buffer_setup_ops si1145_buffer_setup_ops = { 11768c2ecf20Sopenharmony_ci .preenable = si1145_buffer_preenable, 11778c2ecf20Sopenharmony_ci .validate_scan_mask = si1145_validate_scan_mask, 11788c2ecf20Sopenharmony_ci}; 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_ci/* 11818c2ecf20Sopenharmony_ci * si1145_trigger_set_state() - Set trigger state 11828c2ecf20Sopenharmony_ci * 11838c2ecf20Sopenharmony_ci * When not using triggers interrupts are disabled and measurement rate is 11848c2ecf20Sopenharmony_ci * set to zero in order to minimize power consumption. 11858c2ecf20Sopenharmony_ci */ 11868c2ecf20Sopenharmony_cistatic int si1145_trigger_set_state(struct iio_trigger *trig, bool state) 11878c2ecf20Sopenharmony_ci{ 11888c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); 11898c2ecf20Sopenharmony_ci struct si1145_data *data = iio_priv(indio_dev); 11908c2ecf20Sopenharmony_ci int err = 0, ret; 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_ci mutex_lock(&data->lock); 11938c2ecf20Sopenharmony_ci 11948c2ecf20Sopenharmony_ci if (state) { 11958c2ecf20Sopenharmony_ci data->autonomous = true; 11968c2ecf20Sopenharmony_ci err = i2c_smbus_write_byte_data(data->client, 11978c2ecf20Sopenharmony_ci SI1145_REG_INT_CFG, SI1145_INT_CFG_OE); 11988c2ecf20Sopenharmony_ci if (err < 0) 11998c2ecf20Sopenharmony_ci goto disable; 12008c2ecf20Sopenharmony_ci err = i2c_smbus_write_byte_data(data->client, 12018c2ecf20Sopenharmony_ci SI1145_REG_IRQ_ENABLE, SI1145_MASK_ALL_IE); 12028c2ecf20Sopenharmony_ci if (err < 0) 12038c2ecf20Sopenharmony_ci goto disable; 12048c2ecf20Sopenharmony_ci err = si1145_set_meas_rate(data, data->meas_rate); 12058c2ecf20Sopenharmony_ci if (err < 0) 12068c2ecf20Sopenharmony_ci goto disable; 12078c2ecf20Sopenharmony_ci err = si1145_command(data, SI1145_CMD_PSALS_AUTO); 12088c2ecf20Sopenharmony_ci if (err < 0) 12098c2ecf20Sopenharmony_ci goto disable; 12108c2ecf20Sopenharmony_ci } else { 12118c2ecf20Sopenharmony_cidisable: 12128c2ecf20Sopenharmony_ci /* Disable as much as possible skipping errors */ 12138c2ecf20Sopenharmony_ci ret = si1145_command(data, SI1145_CMD_PSALS_PAUSE); 12148c2ecf20Sopenharmony_ci if (ret < 0 && !err) 12158c2ecf20Sopenharmony_ci err = ret; 12168c2ecf20Sopenharmony_ci ret = si1145_set_meas_rate(data, 0); 12178c2ecf20Sopenharmony_ci if (ret < 0 && !err) 12188c2ecf20Sopenharmony_ci err = ret; 12198c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte_data(data->client, 12208c2ecf20Sopenharmony_ci SI1145_REG_IRQ_ENABLE, 0); 12218c2ecf20Sopenharmony_ci if (ret < 0 && !err) 12228c2ecf20Sopenharmony_ci err = ret; 12238c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte_data(data->client, 12248c2ecf20Sopenharmony_ci SI1145_REG_INT_CFG, 0); 12258c2ecf20Sopenharmony_ci if (ret < 0 && !err) 12268c2ecf20Sopenharmony_ci err = ret; 12278c2ecf20Sopenharmony_ci data->autonomous = false; 12288c2ecf20Sopenharmony_ci } 12298c2ecf20Sopenharmony_ci 12308c2ecf20Sopenharmony_ci mutex_unlock(&data->lock); 12318c2ecf20Sopenharmony_ci return err; 12328c2ecf20Sopenharmony_ci} 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_cistatic const struct iio_trigger_ops si1145_trigger_ops = { 12358c2ecf20Sopenharmony_ci .set_trigger_state = si1145_trigger_set_state, 12368c2ecf20Sopenharmony_ci}; 12378c2ecf20Sopenharmony_ci 12388c2ecf20Sopenharmony_cistatic int si1145_probe_trigger(struct iio_dev *indio_dev) 12398c2ecf20Sopenharmony_ci{ 12408c2ecf20Sopenharmony_ci struct si1145_data *data = iio_priv(indio_dev); 12418c2ecf20Sopenharmony_ci struct i2c_client *client = data->client; 12428c2ecf20Sopenharmony_ci struct iio_trigger *trig; 12438c2ecf20Sopenharmony_ci int ret; 12448c2ecf20Sopenharmony_ci 12458c2ecf20Sopenharmony_ci trig = devm_iio_trigger_alloc(&client->dev, 12468c2ecf20Sopenharmony_ci "%s-dev%d", indio_dev->name, indio_dev->id); 12478c2ecf20Sopenharmony_ci if (!trig) 12488c2ecf20Sopenharmony_ci return -ENOMEM; 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_ci trig->dev.parent = &client->dev; 12518c2ecf20Sopenharmony_ci trig->ops = &si1145_trigger_ops; 12528c2ecf20Sopenharmony_ci iio_trigger_set_drvdata(trig, indio_dev); 12538c2ecf20Sopenharmony_ci 12548c2ecf20Sopenharmony_ci ret = devm_request_irq(&client->dev, client->irq, 12558c2ecf20Sopenharmony_ci iio_trigger_generic_data_rdy_poll, 12568c2ecf20Sopenharmony_ci IRQF_TRIGGER_FALLING, 12578c2ecf20Sopenharmony_ci "si1145_irq", 12588c2ecf20Sopenharmony_ci trig); 12598c2ecf20Sopenharmony_ci if (ret < 0) { 12608c2ecf20Sopenharmony_ci dev_err(&client->dev, "irq request failed\n"); 12618c2ecf20Sopenharmony_ci return ret; 12628c2ecf20Sopenharmony_ci } 12638c2ecf20Sopenharmony_ci 12648c2ecf20Sopenharmony_ci ret = devm_iio_trigger_register(&client->dev, trig); 12658c2ecf20Sopenharmony_ci if (ret) 12668c2ecf20Sopenharmony_ci return ret; 12678c2ecf20Sopenharmony_ci 12688c2ecf20Sopenharmony_ci data->trig = trig; 12698c2ecf20Sopenharmony_ci indio_dev->trig = iio_trigger_get(data->trig); 12708c2ecf20Sopenharmony_ci 12718c2ecf20Sopenharmony_ci return 0; 12728c2ecf20Sopenharmony_ci} 12738c2ecf20Sopenharmony_ci 12748c2ecf20Sopenharmony_cistatic int si1145_probe(struct i2c_client *client, 12758c2ecf20Sopenharmony_ci const struct i2c_device_id *id) 12768c2ecf20Sopenharmony_ci{ 12778c2ecf20Sopenharmony_ci struct si1145_data *data; 12788c2ecf20Sopenharmony_ci struct iio_dev *indio_dev; 12798c2ecf20Sopenharmony_ci u8 part_id, rev_id, seq_id; 12808c2ecf20Sopenharmony_ci int ret; 12818c2ecf20Sopenharmony_ci 12828c2ecf20Sopenharmony_ci indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); 12838c2ecf20Sopenharmony_ci if (!indio_dev) 12848c2ecf20Sopenharmony_ci return -ENOMEM; 12858c2ecf20Sopenharmony_ci 12868c2ecf20Sopenharmony_ci data = iio_priv(indio_dev); 12878c2ecf20Sopenharmony_ci i2c_set_clientdata(client, indio_dev); 12888c2ecf20Sopenharmony_ci data->client = client; 12898c2ecf20Sopenharmony_ci data->part_info = &si1145_part_info[id->driver_data]; 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_ci part_id = ret = i2c_smbus_read_byte_data(data->client, 12928c2ecf20Sopenharmony_ci SI1145_REG_PART_ID); 12938c2ecf20Sopenharmony_ci if (ret < 0) 12948c2ecf20Sopenharmony_ci return ret; 12958c2ecf20Sopenharmony_ci rev_id = ret = i2c_smbus_read_byte_data(data->client, 12968c2ecf20Sopenharmony_ci SI1145_REG_REV_ID); 12978c2ecf20Sopenharmony_ci if (ret < 0) 12988c2ecf20Sopenharmony_ci return ret; 12998c2ecf20Sopenharmony_ci seq_id = ret = i2c_smbus_read_byte_data(data->client, 13008c2ecf20Sopenharmony_ci SI1145_REG_SEQ_ID); 13018c2ecf20Sopenharmony_ci if (ret < 0) 13028c2ecf20Sopenharmony_ci return ret; 13038c2ecf20Sopenharmony_ci dev_info(&client->dev, "device ID part %#02hhx rev %#02hhx seq %#02hhx\n", 13048c2ecf20Sopenharmony_ci part_id, rev_id, seq_id); 13058c2ecf20Sopenharmony_ci if (part_id != data->part_info->part) { 13068c2ecf20Sopenharmony_ci dev_err(&client->dev, "part ID mismatch got %#02hhx, expected %#02x\n", 13078c2ecf20Sopenharmony_ci part_id, data->part_info->part); 13088c2ecf20Sopenharmony_ci return -ENODEV; 13098c2ecf20Sopenharmony_ci } 13108c2ecf20Sopenharmony_ci 13118c2ecf20Sopenharmony_ci indio_dev->name = id->name; 13128c2ecf20Sopenharmony_ci indio_dev->channels = data->part_info->channels; 13138c2ecf20Sopenharmony_ci indio_dev->num_channels = data->part_info->num_channels; 13148c2ecf20Sopenharmony_ci indio_dev->info = data->part_info->iio_info; 13158c2ecf20Sopenharmony_ci indio_dev->modes = INDIO_DIRECT_MODE; 13168c2ecf20Sopenharmony_ci 13178c2ecf20Sopenharmony_ci mutex_init(&data->lock); 13188c2ecf20Sopenharmony_ci mutex_init(&data->cmdlock); 13198c2ecf20Sopenharmony_ci 13208c2ecf20Sopenharmony_ci ret = si1145_initialize(data); 13218c2ecf20Sopenharmony_ci if (ret < 0) 13228c2ecf20Sopenharmony_ci return ret; 13238c2ecf20Sopenharmony_ci 13248c2ecf20Sopenharmony_ci ret = devm_iio_triggered_buffer_setup(&client->dev, 13258c2ecf20Sopenharmony_ci indio_dev, NULL, 13268c2ecf20Sopenharmony_ci si1145_trigger_handler, &si1145_buffer_setup_ops); 13278c2ecf20Sopenharmony_ci if (ret < 0) 13288c2ecf20Sopenharmony_ci return ret; 13298c2ecf20Sopenharmony_ci 13308c2ecf20Sopenharmony_ci if (client->irq) { 13318c2ecf20Sopenharmony_ci ret = si1145_probe_trigger(indio_dev); 13328c2ecf20Sopenharmony_ci if (ret < 0) 13338c2ecf20Sopenharmony_ci return ret; 13348c2ecf20Sopenharmony_ci } else { 13358c2ecf20Sopenharmony_ci dev_info(&client->dev, "no irq, using polling\n"); 13368c2ecf20Sopenharmony_ci } 13378c2ecf20Sopenharmony_ci 13388c2ecf20Sopenharmony_ci return devm_iio_device_register(&client->dev, indio_dev); 13398c2ecf20Sopenharmony_ci} 13408c2ecf20Sopenharmony_ci 13418c2ecf20Sopenharmony_cistatic const struct i2c_device_id si1145_ids[] = { 13428c2ecf20Sopenharmony_ci { "si1132", SI1132 }, 13438c2ecf20Sopenharmony_ci { "si1141", SI1141 }, 13448c2ecf20Sopenharmony_ci { "si1142", SI1142 }, 13458c2ecf20Sopenharmony_ci { "si1143", SI1143 }, 13468c2ecf20Sopenharmony_ci { "si1145", SI1145 }, 13478c2ecf20Sopenharmony_ci { "si1146", SI1146 }, 13488c2ecf20Sopenharmony_ci { "si1147", SI1147 }, 13498c2ecf20Sopenharmony_ci { } 13508c2ecf20Sopenharmony_ci}; 13518c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, si1145_ids); 13528c2ecf20Sopenharmony_ci 13538c2ecf20Sopenharmony_cistatic struct i2c_driver si1145_driver = { 13548c2ecf20Sopenharmony_ci .driver = { 13558c2ecf20Sopenharmony_ci .name = "si1145", 13568c2ecf20Sopenharmony_ci }, 13578c2ecf20Sopenharmony_ci .probe = si1145_probe, 13588c2ecf20Sopenharmony_ci .id_table = si1145_ids, 13598c2ecf20Sopenharmony_ci}; 13608c2ecf20Sopenharmony_ci 13618c2ecf20Sopenharmony_cimodule_i2c_driver(si1145_driver); 13628c2ecf20Sopenharmony_ci 13638c2ecf20Sopenharmony_ciMODULE_AUTHOR("Peter Meerwald-Stadler <pmeerw@pmeerw.net>"); 13648c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Silabs SI1132 and SI1141/2/3/5/6/7 proximity, ambient light and UV index sensor driver"); 13658c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1366