18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Support for AMS AS73211 JENCOLOR(R) Digital XYZ Sensor 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Author: Christian Eggers <ceggers@arri.de> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (c) 2020 ARRI Lighting 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Color light sensor with 16-bit channels for x, y, z and temperature); 108c2ecf20Sopenharmony_ci * 7-bit I2C slave address 0x74 .. 0x77. 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * Datasheet: https://ams.com/documents/20143/36005/AS73211_DS000556_3-01.pdf 138c2ecf20Sopenharmony_ci */ 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include <linux/bitfield.h> 168c2ecf20Sopenharmony_ci#include <linux/completion.h> 178c2ecf20Sopenharmony_ci#include <linux/delay.h> 188c2ecf20Sopenharmony_ci#include <linux/i2c.h> 198c2ecf20Sopenharmony_ci#include <linux/iio/buffer.h> 208c2ecf20Sopenharmony_ci#include <linux/iio/iio.h> 218c2ecf20Sopenharmony_ci#include <linux/iio/sysfs.h> 228c2ecf20Sopenharmony_ci#include <linux/iio/trigger_consumer.h> 238c2ecf20Sopenharmony_ci#include <linux/iio/triggered_buffer.h> 248c2ecf20Sopenharmony_ci#include <linux/module.h> 258c2ecf20Sopenharmony_ci#include <linux/mutex.h> 268c2ecf20Sopenharmony_ci#include <linux/pm.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#define HZ_PER_KHZ 1000 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#define AS73211_DRV_NAME "as73211" 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci/* AS73211 configuration registers */ 338c2ecf20Sopenharmony_ci#define AS73211_REG_OSR 0x0 348c2ecf20Sopenharmony_ci#define AS73211_REG_AGEN 0x2 358c2ecf20Sopenharmony_ci#define AS73211_REG_CREG1 0x6 368c2ecf20Sopenharmony_ci#define AS73211_REG_CREG2 0x7 378c2ecf20Sopenharmony_ci#define AS73211_REG_CREG3 0x8 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci/* AS73211 output register bank */ 408c2ecf20Sopenharmony_ci#define AS73211_OUT_OSR_STATUS 0 418c2ecf20Sopenharmony_ci#define AS73211_OUT_TEMP 1 428c2ecf20Sopenharmony_ci#define AS73211_OUT_MRES1 2 438c2ecf20Sopenharmony_ci#define AS73211_OUT_MRES2 3 448c2ecf20Sopenharmony_ci#define AS73211_OUT_MRES3 4 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci#define AS73211_OSR_SS BIT(7) 478c2ecf20Sopenharmony_ci#define AS73211_OSR_PD BIT(6) 488c2ecf20Sopenharmony_ci#define AS73211_OSR_SW_RES BIT(3) 498c2ecf20Sopenharmony_ci#define AS73211_OSR_DOS_MASK GENMASK(2, 0) 508c2ecf20Sopenharmony_ci#define AS73211_OSR_DOS_CONFIG FIELD_PREP(AS73211_OSR_DOS_MASK, 0x2) 518c2ecf20Sopenharmony_ci#define AS73211_OSR_DOS_MEASURE FIELD_PREP(AS73211_OSR_DOS_MASK, 0x3) 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci#define AS73211_AGEN_DEVID_MASK GENMASK(7, 4) 548c2ecf20Sopenharmony_ci#define AS73211_AGEN_DEVID(x) FIELD_PREP(AS73211_AGEN_DEVID_MASK, (x)) 558c2ecf20Sopenharmony_ci#define AS73211_AGEN_MUT_MASK GENMASK(3, 0) 568c2ecf20Sopenharmony_ci#define AS73211_AGEN_MUT(x) FIELD_PREP(AS73211_AGEN_MUT_MASK, (x)) 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci#define AS73211_CREG1_GAIN_MASK GENMASK(7, 4) 598c2ecf20Sopenharmony_ci#define AS73211_CREG1_GAIN_1 11 608c2ecf20Sopenharmony_ci#define AS73211_CREG1_TIME_MASK GENMASK(3, 0) 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci#define AS73211_CREG3_CCLK_MASK GENMASK(1, 0) 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci#define AS73211_OSR_STATUS_OUTCONVOF BIT(15) 658c2ecf20Sopenharmony_ci#define AS73211_OSR_STATUS_MRESOF BIT(14) 668c2ecf20Sopenharmony_ci#define AS73211_OSR_STATUS_ADCOF BIT(13) 678c2ecf20Sopenharmony_ci#define AS73211_OSR_STATUS_LDATA BIT(12) 688c2ecf20Sopenharmony_ci#define AS73211_OSR_STATUS_NDATA BIT(11) 698c2ecf20Sopenharmony_ci#define AS73211_OSR_STATUS_NOTREADY BIT(10) 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci#define AS73211_SAMPLE_FREQ_BASE 1024000 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci#define AS73211_SAMPLE_TIME_NUM 15 748c2ecf20Sopenharmony_ci#define AS73211_SAMPLE_TIME_MAX_MS BIT(AS73211_SAMPLE_TIME_NUM - 1) 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci/* Available sample frequencies are 1.024MHz multiplied by powers of two. */ 778c2ecf20Sopenharmony_cistatic const int as73211_samp_freq_avail[] = { 788c2ecf20Sopenharmony_ci AS73211_SAMPLE_FREQ_BASE * 1, 798c2ecf20Sopenharmony_ci AS73211_SAMPLE_FREQ_BASE * 2, 808c2ecf20Sopenharmony_ci AS73211_SAMPLE_FREQ_BASE * 4, 818c2ecf20Sopenharmony_ci AS73211_SAMPLE_FREQ_BASE * 8, 828c2ecf20Sopenharmony_ci}; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistatic const int as73211_hardwaregain_avail[] = { 858c2ecf20Sopenharmony_ci 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 868c2ecf20Sopenharmony_ci}; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci/** 898c2ecf20Sopenharmony_ci * struct as73211_data - Instance data for one AS73211 908c2ecf20Sopenharmony_ci * @client: I2C client. 918c2ecf20Sopenharmony_ci * @osr: Cached Operational State Register. 928c2ecf20Sopenharmony_ci * @creg1: Cached Configuration Register 1. 938c2ecf20Sopenharmony_ci * @creg2: Cached Configuration Register 2. 948c2ecf20Sopenharmony_ci * @creg3: Cached Configuration Register 3. 958c2ecf20Sopenharmony_ci * @mutex: Keeps cached registers in sync with the device. 968c2ecf20Sopenharmony_ci * @completion: Completion to wait for interrupt. 978c2ecf20Sopenharmony_ci * @int_time_avail: Available integration times (depend on sampling frequency). 988c2ecf20Sopenharmony_ci */ 998c2ecf20Sopenharmony_cistruct as73211_data { 1008c2ecf20Sopenharmony_ci struct i2c_client *client; 1018c2ecf20Sopenharmony_ci u8 osr; 1028c2ecf20Sopenharmony_ci u8 creg1; 1038c2ecf20Sopenharmony_ci u8 creg2; 1048c2ecf20Sopenharmony_ci u8 creg3; 1058c2ecf20Sopenharmony_ci struct mutex mutex; 1068c2ecf20Sopenharmony_ci struct completion completion; 1078c2ecf20Sopenharmony_ci int int_time_avail[AS73211_SAMPLE_TIME_NUM * 2]; 1088c2ecf20Sopenharmony_ci}; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci#define AS73211_COLOR_CHANNEL(_color, _si, _addr) { \ 1118c2ecf20Sopenharmony_ci .type = IIO_INTENSITY, \ 1128c2ecf20Sopenharmony_ci .modified = 1, \ 1138c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), \ 1148c2ecf20Sopenharmony_ci .info_mask_shared_by_type = \ 1158c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ 1168c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_HARDWAREGAIN) | \ 1178c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_INT_TIME), \ 1188c2ecf20Sopenharmony_ci .info_mask_shared_by_type_available = \ 1198c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ 1208c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_HARDWAREGAIN) | \ 1218c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_INT_TIME), \ 1228c2ecf20Sopenharmony_ci .channel2 = IIO_MOD_##_color, \ 1238c2ecf20Sopenharmony_ci .address = _addr, \ 1248c2ecf20Sopenharmony_ci .scan_index = _si, \ 1258c2ecf20Sopenharmony_ci .scan_type = { \ 1268c2ecf20Sopenharmony_ci .sign = 'u', \ 1278c2ecf20Sopenharmony_ci .realbits = 16, \ 1288c2ecf20Sopenharmony_ci .storagebits = 16, \ 1298c2ecf20Sopenharmony_ci .endianness = IIO_LE, \ 1308c2ecf20Sopenharmony_ci }, \ 1318c2ecf20Sopenharmony_ci} 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci#define AS73211_OFFSET_TEMP_INT (-66) 1348c2ecf20Sopenharmony_ci#define AS73211_OFFSET_TEMP_MICRO 900000 1358c2ecf20Sopenharmony_ci#define AS73211_SCALE_TEMP_INT 0 1368c2ecf20Sopenharmony_ci#define AS73211_SCALE_TEMP_MICRO 50000 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci#define AS73211_SCALE_X 277071108 /* nW/m^2 */ 1398c2ecf20Sopenharmony_ci#define AS73211_SCALE_Y 298384270 /* nW/m^2 */ 1408c2ecf20Sopenharmony_ci#define AS73211_SCALE_Z 160241927 /* nW/m^2 */ 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci/* Channel order MUST match devices result register order */ 1438c2ecf20Sopenharmony_ci#define AS73211_SCAN_INDEX_TEMP 0 1448c2ecf20Sopenharmony_ci#define AS73211_SCAN_INDEX_X 1 1458c2ecf20Sopenharmony_ci#define AS73211_SCAN_INDEX_Y 2 1468c2ecf20Sopenharmony_ci#define AS73211_SCAN_INDEX_Z 3 1478c2ecf20Sopenharmony_ci#define AS73211_SCAN_INDEX_TS 4 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci#define AS73211_SCAN_MASK_COLOR ( \ 1508c2ecf20Sopenharmony_ci BIT(AS73211_SCAN_INDEX_X) | \ 1518c2ecf20Sopenharmony_ci BIT(AS73211_SCAN_INDEX_Y) | \ 1528c2ecf20Sopenharmony_ci BIT(AS73211_SCAN_INDEX_Z)) 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci#define AS73211_SCAN_MASK_ALL ( \ 1558c2ecf20Sopenharmony_ci BIT(AS73211_SCAN_INDEX_TEMP) | \ 1568c2ecf20Sopenharmony_ci AS73211_SCAN_MASK_COLOR) 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic const struct iio_chan_spec as73211_channels[] = { 1598c2ecf20Sopenharmony_ci { 1608c2ecf20Sopenharmony_ci .type = IIO_TEMP, 1618c2ecf20Sopenharmony_ci .info_mask_separate = 1628c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_RAW) | 1638c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_OFFSET) | 1648c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_SCALE), 1658c2ecf20Sopenharmony_ci .address = AS73211_OUT_TEMP, 1668c2ecf20Sopenharmony_ci .scan_index = AS73211_SCAN_INDEX_TEMP, 1678c2ecf20Sopenharmony_ci .scan_type = { 1688c2ecf20Sopenharmony_ci .sign = 'u', 1698c2ecf20Sopenharmony_ci .realbits = 16, 1708c2ecf20Sopenharmony_ci .storagebits = 16, 1718c2ecf20Sopenharmony_ci .endianness = IIO_LE, 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci }, 1748c2ecf20Sopenharmony_ci AS73211_COLOR_CHANNEL(X, AS73211_SCAN_INDEX_X, AS73211_OUT_MRES1), 1758c2ecf20Sopenharmony_ci AS73211_COLOR_CHANNEL(Y, AS73211_SCAN_INDEX_Y, AS73211_OUT_MRES2), 1768c2ecf20Sopenharmony_ci AS73211_COLOR_CHANNEL(Z, AS73211_SCAN_INDEX_Z, AS73211_OUT_MRES3), 1778c2ecf20Sopenharmony_ci IIO_CHAN_SOFT_TIMESTAMP(AS73211_SCAN_INDEX_TS), 1788c2ecf20Sopenharmony_ci}; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_cistatic unsigned int as73211_integration_time_1024cyc(struct as73211_data *data) 1818c2ecf20Sopenharmony_ci{ 1828c2ecf20Sopenharmony_ci /* 1838c2ecf20Sopenharmony_ci * Return integration time in units of 1024 clock cycles. Integration time 1848c2ecf20Sopenharmony_ci * in CREG1 is in powers of 2 (x 1024 cycles). 1858c2ecf20Sopenharmony_ci */ 1868c2ecf20Sopenharmony_ci return BIT(FIELD_GET(AS73211_CREG1_TIME_MASK, data->creg1)); 1878c2ecf20Sopenharmony_ci} 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_cistatic unsigned int as73211_integration_time_us(struct as73211_data *data, 1908c2ecf20Sopenharmony_ci unsigned int integration_time_1024cyc) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci /* 1938c2ecf20Sopenharmony_ci * f_samp is configured in CREG3 in powers of 2 (x 1.024 MHz) 1948c2ecf20Sopenharmony_ci * t_cycl is configured in CREG1 in powers of 2 (x 1024 cycles) 1958c2ecf20Sopenharmony_ci * t_int_us = 1 / (f_samp) * t_cycl * US_PER_SEC 1968c2ecf20Sopenharmony_ci * = 1 / (2^CREG3_CCLK * 1,024,000) * 2^CREG1_CYCLES * 1,024 * US_PER_SEC 1978c2ecf20Sopenharmony_ci * = 2^(-CREG3_CCLK) * 2^CREG1_CYCLES * 1,000 1988c2ecf20Sopenharmony_ci * In order to get rid of negative exponents, we extend the "fraction" 1998c2ecf20Sopenharmony_ci * by 2^3 (CREG3_CCLK,max = 3) 2008c2ecf20Sopenharmony_ci * t_int_us = 2^(3-CREG3_CCLK) * 2^CREG1_CYCLES * 125 2018c2ecf20Sopenharmony_ci */ 2028c2ecf20Sopenharmony_ci return BIT(3 - FIELD_GET(AS73211_CREG3_CCLK_MASK, data->creg3)) * 2038c2ecf20Sopenharmony_ci integration_time_1024cyc * 125; 2048c2ecf20Sopenharmony_ci} 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_cistatic void as73211_integration_time_calc_avail(struct as73211_data *data) 2078c2ecf20Sopenharmony_ci{ 2088c2ecf20Sopenharmony_ci int i; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(data->int_time_avail) / 2; i++) { 2118c2ecf20Sopenharmony_ci unsigned int time_us = as73211_integration_time_us(data, BIT(i)); 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci data->int_time_avail[i * 2 + 0] = time_us / USEC_PER_SEC; 2148c2ecf20Sopenharmony_ci data->int_time_avail[i * 2 + 1] = time_us % USEC_PER_SEC; 2158c2ecf20Sopenharmony_ci } 2168c2ecf20Sopenharmony_ci} 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_cistatic unsigned int as73211_gain(struct as73211_data *data) 2198c2ecf20Sopenharmony_ci{ 2208c2ecf20Sopenharmony_ci /* gain can be calculated from CREG1 as 2^(11 - CREG1_GAIN) */ 2218c2ecf20Sopenharmony_ci return BIT(AS73211_CREG1_GAIN_1 - FIELD_GET(AS73211_CREG1_GAIN_MASK, data->creg1)); 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci/* must be called with as73211_data::mutex held. */ 2258c2ecf20Sopenharmony_cistatic int as73211_req_data(struct as73211_data *data) 2268c2ecf20Sopenharmony_ci{ 2278c2ecf20Sopenharmony_ci unsigned int time_us = as73211_integration_time_us(data, 2288c2ecf20Sopenharmony_ci as73211_integration_time_1024cyc(data)); 2298c2ecf20Sopenharmony_ci struct device *dev = &data->client->dev; 2308c2ecf20Sopenharmony_ci union i2c_smbus_data smbus_data; 2318c2ecf20Sopenharmony_ci u16 osr_status; 2328c2ecf20Sopenharmony_ci int ret; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci if (data->client->irq) 2358c2ecf20Sopenharmony_ci reinit_completion(&data->completion); 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci /* 2388c2ecf20Sopenharmony_ci * During measurement, there should be no traffic on the i2c bus as the 2398c2ecf20Sopenharmony_ci * electrical noise would disturb the measurement process. 2408c2ecf20Sopenharmony_ci */ 2418c2ecf20Sopenharmony_ci i2c_lock_bus(data->client->adapter, I2C_LOCK_SEGMENT); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci data->osr &= ~AS73211_OSR_DOS_MASK; 2448c2ecf20Sopenharmony_ci data->osr |= AS73211_OSR_DOS_MEASURE | AS73211_OSR_SS; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci smbus_data.byte = data->osr; 2478c2ecf20Sopenharmony_ci ret = __i2c_smbus_xfer(data->client->adapter, data->client->addr, 2488c2ecf20Sopenharmony_ci data->client->flags, I2C_SMBUS_WRITE, 2498c2ecf20Sopenharmony_ci AS73211_REG_OSR, I2C_SMBUS_BYTE_DATA, &smbus_data); 2508c2ecf20Sopenharmony_ci if (ret < 0) { 2518c2ecf20Sopenharmony_ci i2c_unlock_bus(data->client->adapter, I2C_LOCK_SEGMENT); 2528c2ecf20Sopenharmony_ci return ret; 2538c2ecf20Sopenharmony_ci } 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci /* 2568c2ecf20Sopenharmony_ci * Reset AS73211_OSR_SS (is self clearing) in order to avoid unintentional 2578c2ecf20Sopenharmony_ci * triggering of further measurements later. 2588c2ecf20Sopenharmony_ci */ 2598c2ecf20Sopenharmony_ci data->osr &= ~AS73211_OSR_SS; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci /* 2628c2ecf20Sopenharmony_ci * Add 33% extra margin for the timeout. fclk,min = fclk,typ - 27%. 2638c2ecf20Sopenharmony_ci */ 2648c2ecf20Sopenharmony_ci time_us += time_us / 3; 2658c2ecf20Sopenharmony_ci if (data->client->irq) { 2668c2ecf20Sopenharmony_ci ret = wait_for_completion_timeout(&data->completion, usecs_to_jiffies(time_us)); 2678c2ecf20Sopenharmony_ci if (!ret) { 2688c2ecf20Sopenharmony_ci dev_err(dev, "timeout waiting for READY IRQ\n"); 2698c2ecf20Sopenharmony_ci i2c_unlock_bus(data->client->adapter, I2C_LOCK_SEGMENT); 2708c2ecf20Sopenharmony_ci return -ETIMEDOUT; 2718c2ecf20Sopenharmony_ci } 2728c2ecf20Sopenharmony_ci } else { 2738c2ecf20Sopenharmony_ci /* Wait integration time */ 2748c2ecf20Sopenharmony_ci usleep_range(time_us, 2 * time_us); 2758c2ecf20Sopenharmony_ci } 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci i2c_unlock_bus(data->client->adapter, I2C_LOCK_SEGMENT); 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci ret = i2c_smbus_read_word_data(data->client, AS73211_OUT_OSR_STATUS); 2808c2ecf20Sopenharmony_ci if (ret < 0) 2818c2ecf20Sopenharmony_ci return ret; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci osr_status = ret; 2848c2ecf20Sopenharmony_ci if (osr_status != (AS73211_OSR_DOS_MEASURE | AS73211_OSR_STATUS_NDATA)) { 2858c2ecf20Sopenharmony_ci if (osr_status & AS73211_OSR_SS) { 2868c2ecf20Sopenharmony_ci dev_err(dev, "%s() Measurement has not stopped\n", __func__); 2878c2ecf20Sopenharmony_ci return -ETIME; 2888c2ecf20Sopenharmony_ci } 2898c2ecf20Sopenharmony_ci if (osr_status & AS73211_OSR_STATUS_NOTREADY) { 2908c2ecf20Sopenharmony_ci dev_err(dev, "%s() Data is not ready\n", __func__); 2918c2ecf20Sopenharmony_ci return -ENODATA; 2928c2ecf20Sopenharmony_ci } 2938c2ecf20Sopenharmony_ci if (!(osr_status & AS73211_OSR_STATUS_NDATA)) { 2948c2ecf20Sopenharmony_ci dev_err(dev, "%s() No new data available\n", __func__); 2958c2ecf20Sopenharmony_ci return -ENODATA; 2968c2ecf20Sopenharmony_ci } 2978c2ecf20Sopenharmony_ci if (osr_status & AS73211_OSR_STATUS_LDATA) { 2988c2ecf20Sopenharmony_ci dev_err(dev, "%s() Result buffer overrun\n", __func__); 2998c2ecf20Sopenharmony_ci return -ENOBUFS; 3008c2ecf20Sopenharmony_ci } 3018c2ecf20Sopenharmony_ci if (osr_status & AS73211_OSR_STATUS_ADCOF) { 3028c2ecf20Sopenharmony_ci dev_err(dev, "%s() ADC overflow\n", __func__); 3038c2ecf20Sopenharmony_ci return -EOVERFLOW; 3048c2ecf20Sopenharmony_ci } 3058c2ecf20Sopenharmony_ci if (osr_status & AS73211_OSR_STATUS_MRESOF) { 3068c2ecf20Sopenharmony_ci dev_err(dev, "%s() Measurement result overflow\n", __func__); 3078c2ecf20Sopenharmony_ci return -EOVERFLOW; 3088c2ecf20Sopenharmony_ci } 3098c2ecf20Sopenharmony_ci if (osr_status & AS73211_OSR_STATUS_OUTCONVOF) { 3108c2ecf20Sopenharmony_ci dev_err(dev, "%s() Timer overflow\n", __func__); 3118c2ecf20Sopenharmony_ci return -EOVERFLOW; 3128c2ecf20Sopenharmony_ci } 3138c2ecf20Sopenharmony_ci dev_err(dev, "%s() Unexpected status value\n", __func__); 3148c2ecf20Sopenharmony_ci return -EIO; 3158c2ecf20Sopenharmony_ci } 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci return 0; 3188c2ecf20Sopenharmony_ci} 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_cistatic int as73211_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, 3218c2ecf20Sopenharmony_ci int *val, int *val2, long mask) 3228c2ecf20Sopenharmony_ci{ 3238c2ecf20Sopenharmony_ci struct as73211_data *data = iio_priv(indio_dev); 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci switch (mask) { 3268c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_RAW: { 3278c2ecf20Sopenharmony_ci int ret; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci ret = iio_device_claim_direct_mode(indio_dev); 3308c2ecf20Sopenharmony_ci if (ret < 0) 3318c2ecf20Sopenharmony_ci return ret; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci ret = as73211_req_data(data); 3348c2ecf20Sopenharmony_ci if (ret < 0) { 3358c2ecf20Sopenharmony_ci iio_device_release_direct_mode(indio_dev); 3368c2ecf20Sopenharmony_ci return ret; 3378c2ecf20Sopenharmony_ci } 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci ret = i2c_smbus_read_word_data(data->client, chan->address); 3408c2ecf20Sopenharmony_ci iio_device_release_direct_mode(indio_dev); 3418c2ecf20Sopenharmony_ci if (ret < 0) 3428c2ecf20Sopenharmony_ci return ret; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci *val = ret; 3458c2ecf20Sopenharmony_ci return IIO_VAL_INT; 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_OFFSET: 3488c2ecf20Sopenharmony_ci *val = AS73211_OFFSET_TEMP_INT; 3498c2ecf20Sopenharmony_ci *val2 = AS73211_OFFSET_TEMP_MICRO; 3508c2ecf20Sopenharmony_ci return IIO_VAL_INT_PLUS_MICRO; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_SCALE: 3538c2ecf20Sopenharmony_ci switch (chan->type) { 3548c2ecf20Sopenharmony_ci case IIO_TEMP: 3558c2ecf20Sopenharmony_ci *val = AS73211_SCALE_TEMP_INT; 3568c2ecf20Sopenharmony_ci *val2 = AS73211_SCALE_TEMP_MICRO; 3578c2ecf20Sopenharmony_ci return IIO_VAL_INT_PLUS_MICRO; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci case IIO_INTENSITY: { 3608c2ecf20Sopenharmony_ci unsigned int scale; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci switch (chan->channel2) { 3638c2ecf20Sopenharmony_ci case IIO_MOD_X: 3648c2ecf20Sopenharmony_ci scale = AS73211_SCALE_X; 3658c2ecf20Sopenharmony_ci break; 3668c2ecf20Sopenharmony_ci case IIO_MOD_Y: 3678c2ecf20Sopenharmony_ci scale = AS73211_SCALE_Y; 3688c2ecf20Sopenharmony_ci break; 3698c2ecf20Sopenharmony_ci case IIO_MOD_Z: 3708c2ecf20Sopenharmony_ci scale = AS73211_SCALE_Z; 3718c2ecf20Sopenharmony_ci break; 3728c2ecf20Sopenharmony_ci default: 3738c2ecf20Sopenharmony_ci return -EINVAL; 3748c2ecf20Sopenharmony_ci } 3758c2ecf20Sopenharmony_ci scale /= as73211_gain(data); 3768c2ecf20Sopenharmony_ci scale /= as73211_integration_time_1024cyc(data); 3778c2ecf20Sopenharmony_ci *val = scale; 3788c2ecf20Sopenharmony_ci return IIO_VAL_INT; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci default: 3818c2ecf20Sopenharmony_ci return -EINVAL; 3828c2ecf20Sopenharmony_ci }} 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_SAMP_FREQ: 3858c2ecf20Sopenharmony_ci /* f_samp is configured in CREG3 in powers of 2 (x 1.024 MHz) */ 3868c2ecf20Sopenharmony_ci *val = BIT(FIELD_GET(AS73211_CREG3_CCLK_MASK, data->creg3)) * 3878c2ecf20Sopenharmony_ci AS73211_SAMPLE_FREQ_BASE; 3888c2ecf20Sopenharmony_ci return IIO_VAL_INT; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_HARDWAREGAIN: 3918c2ecf20Sopenharmony_ci *val = as73211_gain(data); 3928c2ecf20Sopenharmony_ci return IIO_VAL_INT; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_INT_TIME: { 3958c2ecf20Sopenharmony_ci unsigned int time_us; 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci mutex_lock(&data->mutex); 3988c2ecf20Sopenharmony_ci time_us = as73211_integration_time_us(data, as73211_integration_time_1024cyc(data)); 3998c2ecf20Sopenharmony_ci mutex_unlock(&data->mutex); 4008c2ecf20Sopenharmony_ci *val = time_us / USEC_PER_SEC; 4018c2ecf20Sopenharmony_ci *val2 = time_us % USEC_PER_SEC; 4028c2ecf20Sopenharmony_ci return IIO_VAL_INT_PLUS_MICRO; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci default: 4058c2ecf20Sopenharmony_ci return -EINVAL; 4068c2ecf20Sopenharmony_ci }} 4078c2ecf20Sopenharmony_ci} 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_cistatic int as73211_read_avail(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, 4108c2ecf20Sopenharmony_ci const int **vals, int *type, int *length, long mask) 4118c2ecf20Sopenharmony_ci{ 4128c2ecf20Sopenharmony_ci struct as73211_data *data = iio_priv(indio_dev); 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci switch (mask) { 4158c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_SAMP_FREQ: 4168c2ecf20Sopenharmony_ci *length = ARRAY_SIZE(as73211_samp_freq_avail); 4178c2ecf20Sopenharmony_ci *vals = as73211_samp_freq_avail; 4188c2ecf20Sopenharmony_ci *type = IIO_VAL_INT; 4198c2ecf20Sopenharmony_ci return IIO_AVAIL_LIST; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_HARDWAREGAIN: 4228c2ecf20Sopenharmony_ci *length = ARRAY_SIZE(as73211_hardwaregain_avail); 4238c2ecf20Sopenharmony_ci *vals = as73211_hardwaregain_avail; 4248c2ecf20Sopenharmony_ci *type = IIO_VAL_INT; 4258c2ecf20Sopenharmony_ci return IIO_AVAIL_LIST; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_INT_TIME: 4288c2ecf20Sopenharmony_ci *length = ARRAY_SIZE(data->int_time_avail); 4298c2ecf20Sopenharmony_ci *vals = data->int_time_avail; 4308c2ecf20Sopenharmony_ci *type = IIO_VAL_INT_PLUS_MICRO; 4318c2ecf20Sopenharmony_ci return IIO_AVAIL_LIST; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci default: 4348c2ecf20Sopenharmony_ci return -EINVAL; 4358c2ecf20Sopenharmony_ci } 4368c2ecf20Sopenharmony_ci} 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_cistatic int _as73211_write_raw(struct iio_dev *indio_dev, 4398c2ecf20Sopenharmony_ci struct iio_chan_spec const *chan __always_unused, 4408c2ecf20Sopenharmony_ci int val, int val2, long mask) 4418c2ecf20Sopenharmony_ci{ 4428c2ecf20Sopenharmony_ci struct as73211_data *data = iio_priv(indio_dev); 4438c2ecf20Sopenharmony_ci int ret; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci switch (mask) { 4468c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_SAMP_FREQ: { 4478c2ecf20Sopenharmony_ci int reg_bits, freq_kHz = val / HZ_PER_KHZ; /* 1024, 2048, ... */ 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci /* val must be 1024 * 2^x */ 4508c2ecf20Sopenharmony_ci if (val < 0 || (freq_kHz * HZ_PER_KHZ) != val || 4518c2ecf20Sopenharmony_ci !is_power_of_2(freq_kHz) || val2) 4528c2ecf20Sopenharmony_ci return -EINVAL; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci /* f_samp is configured in CREG3 in powers of 2 (x 1.024 MHz (=2^10)) */ 4558c2ecf20Sopenharmony_ci reg_bits = ilog2(freq_kHz) - 10; 4568c2ecf20Sopenharmony_ci if (!FIELD_FIT(AS73211_CREG3_CCLK_MASK, reg_bits)) 4578c2ecf20Sopenharmony_ci return -EINVAL; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci data->creg3 &= ~AS73211_CREG3_CCLK_MASK; 4608c2ecf20Sopenharmony_ci data->creg3 |= FIELD_PREP(AS73211_CREG3_CCLK_MASK, reg_bits); 4618c2ecf20Sopenharmony_ci as73211_integration_time_calc_avail(data); 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte_data(data->client, AS73211_REG_CREG3, data->creg3); 4648c2ecf20Sopenharmony_ci if (ret < 0) 4658c2ecf20Sopenharmony_ci return ret; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci return 0; 4688c2ecf20Sopenharmony_ci } 4698c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_HARDWAREGAIN: { 4708c2ecf20Sopenharmony_ci unsigned int reg_bits; 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci if (val < 0 || !is_power_of_2(val) || val2) 4738c2ecf20Sopenharmony_ci return -EINVAL; 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci /* gain can be calculated from CREG1 as 2^(11 - CREG1_GAIN) */ 4768c2ecf20Sopenharmony_ci reg_bits = AS73211_CREG1_GAIN_1 - ilog2(val); 4778c2ecf20Sopenharmony_ci if (!FIELD_FIT(AS73211_CREG1_GAIN_MASK, reg_bits)) 4788c2ecf20Sopenharmony_ci return -EINVAL; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci data->creg1 &= ~AS73211_CREG1_GAIN_MASK; 4818c2ecf20Sopenharmony_ci data->creg1 |= FIELD_PREP(AS73211_CREG1_GAIN_MASK, reg_bits); 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte_data(data->client, AS73211_REG_CREG1, data->creg1); 4848c2ecf20Sopenharmony_ci if (ret < 0) 4858c2ecf20Sopenharmony_ci return ret; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci return 0; 4888c2ecf20Sopenharmony_ci } 4898c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_INT_TIME: { 4908c2ecf20Sopenharmony_ci int val_us = val * USEC_PER_SEC + val2; 4918c2ecf20Sopenharmony_ci int time_ms; 4928c2ecf20Sopenharmony_ci int reg_bits; 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci /* f_samp is configured in CREG3 in powers of 2 (x 1.024 MHz) */ 4958c2ecf20Sopenharmony_ci int f_samp_1_024mhz = BIT(FIELD_GET(AS73211_CREG3_CCLK_MASK, data->creg3)); 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci /* 4988c2ecf20Sopenharmony_ci * time_ms = time_us * US_PER_MS * f_samp_1_024mhz / MHZ_PER_HZ 4998c2ecf20Sopenharmony_ci * = time_us * f_samp_1_024mhz / 1000 5008c2ecf20Sopenharmony_ci */ 5018c2ecf20Sopenharmony_ci time_ms = (val_us * f_samp_1_024mhz) / 1000; /* 1 ms, 2 ms, ... (power of two) */ 5028c2ecf20Sopenharmony_ci if (time_ms < 0 || !is_power_of_2(time_ms) || time_ms > AS73211_SAMPLE_TIME_MAX_MS) 5038c2ecf20Sopenharmony_ci return -EINVAL; 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci reg_bits = ilog2(time_ms); 5068c2ecf20Sopenharmony_ci if (!FIELD_FIT(AS73211_CREG1_TIME_MASK, reg_bits)) 5078c2ecf20Sopenharmony_ci return -EINVAL; /* not possible due to previous tests */ 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci data->creg1 &= ~AS73211_CREG1_TIME_MASK; 5108c2ecf20Sopenharmony_ci data->creg1 |= FIELD_PREP(AS73211_CREG1_TIME_MASK, reg_bits); 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte_data(data->client, AS73211_REG_CREG1, data->creg1); 5138c2ecf20Sopenharmony_ci if (ret < 0) 5148c2ecf20Sopenharmony_ci return ret; 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci return 0; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci default: 5198c2ecf20Sopenharmony_ci return -EINVAL; 5208c2ecf20Sopenharmony_ci }} 5218c2ecf20Sopenharmony_ci} 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_cistatic int as73211_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, 5248c2ecf20Sopenharmony_ci int val, int val2, long mask) 5258c2ecf20Sopenharmony_ci{ 5268c2ecf20Sopenharmony_ci struct as73211_data *data = iio_priv(indio_dev); 5278c2ecf20Sopenharmony_ci int ret; 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci mutex_lock(&data->mutex); 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci ret = iio_device_claim_direct_mode(indio_dev); 5328c2ecf20Sopenharmony_ci if (ret < 0) 5338c2ecf20Sopenharmony_ci goto error_unlock; 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci /* Need to switch to config mode ... */ 5368c2ecf20Sopenharmony_ci if ((data->osr & AS73211_OSR_DOS_MASK) != AS73211_OSR_DOS_CONFIG) { 5378c2ecf20Sopenharmony_ci data->osr &= ~AS73211_OSR_DOS_MASK; 5388c2ecf20Sopenharmony_ci data->osr |= AS73211_OSR_DOS_CONFIG; 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte_data(data->client, AS73211_REG_OSR, data->osr); 5418c2ecf20Sopenharmony_ci if (ret < 0) 5428c2ecf20Sopenharmony_ci goto error_release; 5438c2ecf20Sopenharmony_ci } 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci ret = _as73211_write_raw(indio_dev, chan, val, val2, mask); 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_cierror_release: 5488c2ecf20Sopenharmony_ci iio_device_release_direct_mode(indio_dev); 5498c2ecf20Sopenharmony_cierror_unlock: 5508c2ecf20Sopenharmony_ci mutex_unlock(&data->mutex); 5518c2ecf20Sopenharmony_ci return ret; 5528c2ecf20Sopenharmony_ci} 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_cistatic irqreturn_t as73211_ready_handler(int irq __always_unused, void *priv) 5558c2ecf20Sopenharmony_ci{ 5568c2ecf20Sopenharmony_ci struct as73211_data *data = iio_priv(priv); 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci complete(&data->completion); 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci return IRQ_HANDLED; 5618c2ecf20Sopenharmony_ci} 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_cistatic irqreturn_t as73211_trigger_handler(int irq __always_unused, void *p) 5648c2ecf20Sopenharmony_ci{ 5658c2ecf20Sopenharmony_ci struct iio_poll_func *pf = p; 5668c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = pf->indio_dev; 5678c2ecf20Sopenharmony_ci struct as73211_data *data = iio_priv(indio_dev); 5688c2ecf20Sopenharmony_ci struct { 5698c2ecf20Sopenharmony_ci __le16 chan[4]; 5708c2ecf20Sopenharmony_ci s64 ts __aligned(8); 5718c2ecf20Sopenharmony_ci } scan; 5728c2ecf20Sopenharmony_ci int data_result, ret; 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci mutex_lock(&data->mutex); 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci data_result = as73211_req_data(data); 5778c2ecf20Sopenharmony_ci if (data_result < 0 && data_result != -EOVERFLOW) 5788c2ecf20Sopenharmony_ci goto done; /* don't push any data for errors other than EOVERFLOW */ 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci if (*indio_dev->active_scan_mask == AS73211_SCAN_MASK_ALL) { 5818c2ecf20Sopenharmony_ci /* Optimization for reading all (color + temperature) channels */ 5828c2ecf20Sopenharmony_ci u8 addr = as73211_channels[0].address; 5838c2ecf20Sopenharmony_ci struct i2c_msg msgs[] = { 5848c2ecf20Sopenharmony_ci { 5858c2ecf20Sopenharmony_ci .addr = data->client->addr, 5868c2ecf20Sopenharmony_ci .flags = 0, 5878c2ecf20Sopenharmony_ci .len = 1, 5888c2ecf20Sopenharmony_ci .buf = &addr, 5898c2ecf20Sopenharmony_ci }, 5908c2ecf20Sopenharmony_ci { 5918c2ecf20Sopenharmony_ci .addr = data->client->addr, 5928c2ecf20Sopenharmony_ci .flags = I2C_M_RD, 5938c2ecf20Sopenharmony_ci .len = sizeof(scan.chan), 5948c2ecf20Sopenharmony_ci .buf = (u8 *)&scan.chan, 5958c2ecf20Sopenharmony_ci }, 5968c2ecf20Sopenharmony_ci }; 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci ret = i2c_transfer(data->client->adapter, msgs, ARRAY_SIZE(msgs)); 5998c2ecf20Sopenharmony_ci if (ret < 0) 6008c2ecf20Sopenharmony_ci goto done; 6018c2ecf20Sopenharmony_ci } else { 6028c2ecf20Sopenharmony_ci /* Optimization for reading only color channels */ 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci /* AS73211 starts reading at address 2 */ 6058c2ecf20Sopenharmony_ci ret = i2c_master_recv(data->client, 6068c2ecf20Sopenharmony_ci (char *)&scan.chan[1], 3 * sizeof(scan.chan[1])); 6078c2ecf20Sopenharmony_ci if (ret < 0) 6088c2ecf20Sopenharmony_ci goto done; 6098c2ecf20Sopenharmony_ci } 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci if (data_result) { 6128c2ecf20Sopenharmony_ci /* 6138c2ecf20Sopenharmony_ci * Saturate all channels (in case of overflows). Temperature channel 6148c2ecf20Sopenharmony_ci * is not affected by overflows. 6158c2ecf20Sopenharmony_ci */ 6168c2ecf20Sopenharmony_ci scan.chan[1] = cpu_to_le16(U16_MAX); 6178c2ecf20Sopenharmony_ci scan.chan[2] = cpu_to_le16(U16_MAX); 6188c2ecf20Sopenharmony_ci scan.chan[3] = cpu_to_le16(U16_MAX); 6198c2ecf20Sopenharmony_ci } 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci iio_push_to_buffers_with_timestamp(indio_dev, &scan, iio_get_time_ns(indio_dev)); 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_cidone: 6248c2ecf20Sopenharmony_ci mutex_unlock(&data->mutex); 6258c2ecf20Sopenharmony_ci iio_trigger_notify_done(indio_dev->trig); 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci return IRQ_HANDLED; 6288c2ecf20Sopenharmony_ci} 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_cistatic const struct iio_info as73211_info = { 6318c2ecf20Sopenharmony_ci .read_raw = as73211_read_raw, 6328c2ecf20Sopenharmony_ci .read_avail = as73211_read_avail, 6338c2ecf20Sopenharmony_ci .write_raw = as73211_write_raw, 6348c2ecf20Sopenharmony_ci}; 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_cistatic int as73211_power(struct iio_dev *indio_dev, bool state) 6378c2ecf20Sopenharmony_ci{ 6388c2ecf20Sopenharmony_ci struct as73211_data *data = iio_priv(indio_dev); 6398c2ecf20Sopenharmony_ci int ret; 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci mutex_lock(&data->mutex); 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci if (state) 6448c2ecf20Sopenharmony_ci data->osr &= ~AS73211_OSR_PD; 6458c2ecf20Sopenharmony_ci else 6468c2ecf20Sopenharmony_ci data->osr |= AS73211_OSR_PD; 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte_data(data->client, AS73211_REG_OSR, data->osr); 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci mutex_unlock(&data->mutex); 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci if (ret < 0) 6538c2ecf20Sopenharmony_ci return ret; 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci return 0; 6568c2ecf20Sopenharmony_ci} 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_cistatic void as73211_power_disable(void *data) 6598c2ecf20Sopenharmony_ci{ 6608c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = data; 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci as73211_power(indio_dev, false); 6638c2ecf20Sopenharmony_ci} 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_cistatic int as73211_probe(struct i2c_client *client) 6668c2ecf20Sopenharmony_ci{ 6678c2ecf20Sopenharmony_ci struct device *dev = &client->dev; 6688c2ecf20Sopenharmony_ci struct as73211_data *data; 6698c2ecf20Sopenharmony_ci struct iio_dev *indio_dev; 6708c2ecf20Sopenharmony_ci int ret; 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); 6738c2ecf20Sopenharmony_ci if (!indio_dev) 6748c2ecf20Sopenharmony_ci return -ENOMEM; 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci data = iio_priv(indio_dev); 6778c2ecf20Sopenharmony_ci i2c_set_clientdata(client, indio_dev); 6788c2ecf20Sopenharmony_ci data->client = client; 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci mutex_init(&data->mutex); 6818c2ecf20Sopenharmony_ci init_completion(&data->completion); 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci indio_dev->info = &as73211_info; 6848c2ecf20Sopenharmony_ci indio_dev->name = AS73211_DRV_NAME; 6858c2ecf20Sopenharmony_ci indio_dev->channels = as73211_channels; 6868c2ecf20Sopenharmony_ci indio_dev->num_channels = ARRAY_SIZE(as73211_channels); 6878c2ecf20Sopenharmony_ci indio_dev->modes = INDIO_DIRECT_MODE; 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci ret = i2c_smbus_read_byte_data(data->client, AS73211_REG_OSR); 6908c2ecf20Sopenharmony_ci if (ret < 0) 6918c2ecf20Sopenharmony_ci return ret; 6928c2ecf20Sopenharmony_ci data->osr = ret; 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci /* reset device */ 6958c2ecf20Sopenharmony_ci data->osr |= AS73211_OSR_SW_RES; 6968c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte_data(data->client, AS73211_REG_OSR, data->osr); 6978c2ecf20Sopenharmony_ci if (ret < 0) 6988c2ecf20Sopenharmony_ci return ret; 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci ret = i2c_smbus_read_byte_data(data->client, AS73211_REG_OSR); 7018c2ecf20Sopenharmony_ci if (ret < 0) 7028c2ecf20Sopenharmony_ci return ret; 7038c2ecf20Sopenharmony_ci data->osr = ret; 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci /* 7068c2ecf20Sopenharmony_ci * Reading AGEN is only possible after reset (AGEN is not available if 7078c2ecf20Sopenharmony_ci * device is in measurement mode). 7088c2ecf20Sopenharmony_ci */ 7098c2ecf20Sopenharmony_ci ret = i2c_smbus_read_byte_data(data->client, AS73211_REG_AGEN); 7108c2ecf20Sopenharmony_ci if (ret < 0) 7118c2ecf20Sopenharmony_ci return ret; 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci /* At the time of writing this driver, only DEVID 2 and MUT 1 are known. */ 7148c2ecf20Sopenharmony_ci if ((ret & AS73211_AGEN_DEVID_MASK) != AS73211_AGEN_DEVID(2) || 7158c2ecf20Sopenharmony_ci (ret & AS73211_AGEN_MUT_MASK) != AS73211_AGEN_MUT(1)) 7168c2ecf20Sopenharmony_ci return -ENODEV; 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci ret = i2c_smbus_read_byte_data(data->client, AS73211_REG_CREG1); 7198c2ecf20Sopenharmony_ci if (ret < 0) 7208c2ecf20Sopenharmony_ci return ret; 7218c2ecf20Sopenharmony_ci data->creg1 = ret; 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci ret = i2c_smbus_read_byte_data(data->client, AS73211_REG_CREG2); 7248c2ecf20Sopenharmony_ci if (ret < 0) 7258c2ecf20Sopenharmony_ci return ret; 7268c2ecf20Sopenharmony_ci data->creg2 = ret; 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci ret = i2c_smbus_read_byte_data(data->client, AS73211_REG_CREG3); 7298c2ecf20Sopenharmony_ci if (ret < 0) 7308c2ecf20Sopenharmony_ci return ret; 7318c2ecf20Sopenharmony_ci data->creg3 = ret; 7328c2ecf20Sopenharmony_ci as73211_integration_time_calc_avail(data); 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci ret = as73211_power(indio_dev, true); 7358c2ecf20Sopenharmony_ci if (ret < 0) 7368c2ecf20Sopenharmony_ci return ret; 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci ret = devm_add_action_or_reset(dev, as73211_power_disable, indio_dev); 7398c2ecf20Sopenharmony_ci if (ret) 7408c2ecf20Sopenharmony_ci return ret; 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL, as73211_trigger_handler, NULL); 7438c2ecf20Sopenharmony_ci if (ret) 7448c2ecf20Sopenharmony_ci return ret; 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci if (client->irq) { 7478c2ecf20Sopenharmony_ci ret = devm_request_threaded_irq(&client->dev, client->irq, 7488c2ecf20Sopenharmony_ci NULL, 7498c2ecf20Sopenharmony_ci as73211_ready_handler, 7508c2ecf20Sopenharmony_ci IRQF_ONESHOT, 7518c2ecf20Sopenharmony_ci client->name, indio_dev); 7528c2ecf20Sopenharmony_ci if (ret) 7538c2ecf20Sopenharmony_ci return ret; 7548c2ecf20Sopenharmony_ci } 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci return devm_iio_device_register(dev, indio_dev); 7578c2ecf20Sopenharmony_ci} 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_cistatic int __maybe_unused as73211_suspend(struct device *dev) 7608c2ecf20Sopenharmony_ci{ 7618c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci return as73211_power(indio_dev, false); 7648c2ecf20Sopenharmony_ci} 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_cistatic int __maybe_unused as73211_resume(struct device *dev) 7678c2ecf20Sopenharmony_ci{ 7688c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_ci return as73211_power(indio_dev, true); 7718c2ecf20Sopenharmony_ci} 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(as73211_pm_ops, as73211_suspend, as73211_resume); 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_cistatic const struct of_device_id as73211_of_match[] = { 7768c2ecf20Sopenharmony_ci { .compatible = "ams,as73211" }, 7778c2ecf20Sopenharmony_ci { } 7788c2ecf20Sopenharmony_ci}; 7798c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, as73211_of_match); 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_cistatic const struct i2c_device_id as73211_id[] = { 7828c2ecf20Sopenharmony_ci { "as73211", 0 }, 7838c2ecf20Sopenharmony_ci { } 7848c2ecf20Sopenharmony_ci}; 7858c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, as73211_id); 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_cistatic struct i2c_driver as73211_driver = { 7888c2ecf20Sopenharmony_ci .driver = { 7898c2ecf20Sopenharmony_ci .name = AS73211_DRV_NAME, 7908c2ecf20Sopenharmony_ci .of_match_table = as73211_of_match, 7918c2ecf20Sopenharmony_ci .pm = &as73211_pm_ops, 7928c2ecf20Sopenharmony_ci }, 7938c2ecf20Sopenharmony_ci .probe_new = as73211_probe, 7948c2ecf20Sopenharmony_ci .id_table = as73211_id, 7958c2ecf20Sopenharmony_ci}; 7968c2ecf20Sopenharmony_cimodule_i2c_driver(as73211_driver); 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ciMODULE_AUTHOR("Christian Eggers <ceggers@arri.de>"); 7998c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("AS73211 XYZ True Color Sensor driver"); 8008c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 801