162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Support for AMS AS73211 JENCOLOR(R) Digital XYZ Sensor 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Author: Christian Eggers <ceggers@arri.de> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright (c) 2020 ARRI Lighting 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Color light sensor with 16-bit channels for x, y, z and temperature); 1062306a36Sopenharmony_ci * 7-bit I2C slave address 0x74 .. 0x77. 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * Datasheet: https://ams.com/documents/20143/36005/AS73211_DS000556_3-01.pdf 1362306a36Sopenharmony_ci */ 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <linux/bitfield.h> 1662306a36Sopenharmony_ci#include <linux/completion.h> 1762306a36Sopenharmony_ci#include <linux/delay.h> 1862306a36Sopenharmony_ci#include <linux/i2c.h> 1962306a36Sopenharmony_ci#include <linux/iio/buffer.h> 2062306a36Sopenharmony_ci#include <linux/iio/iio.h> 2162306a36Sopenharmony_ci#include <linux/iio/sysfs.h> 2262306a36Sopenharmony_ci#include <linux/iio/trigger_consumer.h> 2362306a36Sopenharmony_ci#include <linux/iio/triggered_buffer.h> 2462306a36Sopenharmony_ci#include <linux/module.h> 2562306a36Sopenharmony_ci#include <linux/mutex.h> 2662306a36Sopenharmony_ci#include <linux/pm.h> 2762306a36Sopenharmony_ci#include <linux/units.h> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#define AS73211_DRV_NAME "as73211" 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci/* AS73211 configuration registers */ 3262306a36Sopenharmony_ci#define AS73211_REG_OSR 0x0 3362306a36Sopenharmony_ci#define AS73211_REG_AGEN 0x2 3462306a36Sopenharmony_ci#define AS73211_REG_CREG1 0x6 3562306a36Sopenharmony_ci#define AS73211_REG_CREG2 0x7 3662306a36Sopenharmony_ci#define AS73211_REG_CREG3 0x8 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci/* AS73211 output register bank */ 3962306a36Sopenharmony_ci#define AS73211_OUT_OSR_STATUS 0 4062306a36Sopenharmony_ci#define AS73211_OUT_TEMP 1 4162306a36Sopenharmony_ci#define AS73211_OUT_MRES1 2 4262306a36Sopenharmony_ci#define AS73211_OUT_MRES2 3 4362306a36Sopenharmony_ci#define AS73211_OUT_MRES3 4 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci#define AS73211_OSR_SS BIT(7) 4662306a36Sopenharmony_ci#define AS73211_OSR_PD BIT(6) 4762306a36Sopenharmony_ci#define AS73211_OSR_SW_RES BIT(3) 4862306a36Sopenharmony_ci#define AS73211_OSR_DOS_MASK GENMASK(2, 0) 4962306a36Sopenharmony_ci#define AS73211_OSR_DOS_CONFIG FIELD_PREP(AS73211_OSR_DOS_MASK, 0x2) 5062306a36Sopenharmony_ci#define AS73211_OSR_DOS_MEASURE FIELD_PREP(AS73211_OSR_DOS_MASK, 0x3) 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci#define AS73211_AGEN_DEVID_MASK GENMASK(7, 4) 5362306a36Sopenharmony_ci#define AS73211_AGEN_DEVID(x) FIELD_PREP(AS73211_AGEN_DEVID_MASK, (x)) 5462306a36Sopenharmony_ci#define AS73211_AGEN_MUT_MASK GENMASK(3, 0) 5562306a36Sopenharmony_ci#define AS73211_AGEN_MUT(x) FIELD_PREP(AS73211_AGEN_MUT_MASK, (x)) 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci#define AS73211_CREG1_GAIN_MASK GENMASK(7, 4) 5862306a36Sopenharmony_ci#define AS73211_CREG1_GAIN_1 11 5962306a36Sopenharmony_ci#define AS73211_CREG1_TIME_MASK GENMASK(3, 0) 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci#define AS73211_CREG3_CCLK_MASK GENMASK(1, 0) 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci#define AS73211_OSR_STATUS_OUTCONVOF BIT(15) 6462306a36Sopenharmony_ci#define AS73211_OSR_STATUS_MRESOF BIT(14) 6562306a36Sopenharmony_ci#define AS73211_OSR_STATUS_ADCOF BIT(13) 6662306a36Sopenharmony_ci#define AS73211_OSR_STATUS_LDATA BIT(12) 6762306a36Sopenharmony_ci#define AS73211_OSR_STATUS_NDATA BIT(11) 6862306a36Sopenharmony_ci#define AS73211_OSR_STATUS_NOTREADY BIT(10) 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci#define AS73211_SAMPLE_FREQ_BASE 1024000 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci#define AS73211_SAMPLE_TIME_NUM 15 7362306a36Sopenharmony_ci#define AS73211_SAMPLE_TIME_MAX_MS BIT(AS73211_SAMPLE_TIME_NUM - 1) 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci/* Available sample frequencies are 1.024MHz multiplied by powers of two. */ 7662306a36Sopenharmony_cistatic const int as73211_samp_freq_avail[] = { 7762306a36Sopenharmony_ci AS73211_SAMPLE_FREQ_BASE * 1, 7862306a36Sopenharmony_ci AS73211_SAMPLE_FREQ_BASE * 2, 7962306a36Sopenharmony_ci AS73211_SAMPLE_FREQ_BASE * 4, 8062306a36Sopenharmony_ci AS73211_SAMPLE_FREQ_BASE * 8, 8162306a36Sopenharmony_ci}; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic const int as73211_hardwaregain_avail[] = { 8462306a36Sopenharmony_ci 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 8562306a36Sopenharmony_ci}; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci/** 8862306a36Sopenharmony_ci * struct as73211_data - Instance data for one AS73211 8962306a36Sopenharmony_ci * @client: I2C client. 9062306a36Sopenharmony_ci * @osr: Cached Operational State Register. 9162306a36Sopenharmony_ci * @creg1: Cached Configuration Register 1. 9262306a36Sopenharmony_ci * @creg2: Cached Configuration Register 2. 9362306a36Sopenharmony_ci * @creg3: Cached Configuration Register 3. 9462306a36Sopenharmony_ci * @mutex: Keeps cached registers in sync with the device. 9562306a36Sopenharmony_ci * @completion: Completion to wait for interrupt. 9662306a36Sopenharmony_ci * @int_time_avail: Available integration times (depend on sampling frequency). 9762306a36Sopenharmony_ci */ 9862306a36Sopenharmony_cistruct as73211_data { 9962306a36Sopenharmony_ci struct i2c_client *client; 10062306a36Sopenharmony_ci u8 osr; 10162306a36Sopenharmony_ci u8 creg1; 10262306a36Sopenharmony_ci u8 creg2; 10362306a36Sopenharmony_ci u8 creg3; 10462306a36Sopenharmony_ci struct mutex mutex; 10562306a36Sopenharmony_ci struct completion completion; 10662306a36Sopenharmony_ci int int_time_avail[AS73211_SAMPLE_TIME_NUM * 2]; 10762306a36Sopenharmony_ci}; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci#define AS73211_COLOR_CHANNEL(_color, _si, _addr) { \ 11062306a36Sopenharmony_ci .type = IIO_INTENSITY, \ 11162306a36Sopenharmony_ci .modified = 1, \ 11262306a36Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), \ 11362306a36Sopenharmony_ci .info_mask_shared_by_type = \ 11462306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ 11562306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_HARDWAREGAIN) | \ 11662306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_INT_TIME), \ 11762306a36Sopenharmony_ci .info_mask_shared_by_type_available = \ 11862306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ 11962306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_HARDWAREGAIN) | \ 12062306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_INT_TIME), \ 12162306a36Sopenharmony_ci .channel2 = IIO_MOD_##_color, \ 12262306a36Sopenharmony_ci .address = _addr, \ 12362306a36Sopenharmony_ci .scan_index = _si, \ 12462306a36Sopenharmony_ci .scan_type = { \ 12562306a36Sopenharmony_ci .sign = 'u', \ 12662306a36Sopenharmony_ci .realbits = 16, \ 12762306a36Sopenharmony_ci .storagebits = 16, \ 12862306a36Sopenharmony_ci .endianness = IIO_LE, \ 12962306a36Sopenharmony_ci }, \ 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci#define AS73211_OFFSET_TEMP_INT (-66) 13362306a36Sopenharmony_ci#define AS73211_OFFSET_TEMP_MICRO 900000 13462306a36Sopenharmony_ci#define AS73211_SCALE_TEMP_INT 0 13562306a36Sopenharmony_ci#define AS73211_SCALE_TEMP_MICRO 50000 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci#define AS73211_SCALE_X 277071108 /* nW/m^2 */ 13862306a36Sopenharmony_ci#define AS73211_SCALE_Y 298384270 /* nW/m^2 */ 13962306a36Sopenharmony_ci#define AS73211_SCALE_Z 160241927 /* nW/m^2 */ 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci/* Channel order MUST match devices result register order */ 14262306a36Sopenharmony_ci#define AS73211_SCAN_INDEX_TEMP 0 14362306a36Sopenharmony_ci#define AS73211_SCAN_INDEX_X 1 14462306a36Sopenharmony_ci#define AS73211_SCAN_INDEX_Y 2 14562306a36Sopenharmony_ci#define AS73211_SCAN_INDEX_Z 3 14662306a36Sopenharmony_ci#define AS73211_SCAN_INDEX_TS 4 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci#define AS73211_SCAN_MASK_COLOR ( \ 14962306a36Sopenharmony_ci BIT(AS73211_SCAN_INDEX_X) | \ 15062306a36Sopenharmony_ci BIT(AS73211_SCAN_INDEX_Y) | \ 15162306a36Sopenharmony_ci BIT(AS73211_SCAN_INDEX_Z)) 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci#define AS73211_SCAN_MASK_ALL ( \ 15462306a36Sopenharmony_ci BIT(AS73211_SCAN_INDEX_TEMP) | \ 15562306a36Sopenharmony_ci AS73211_SCAN_MASK_COLOR) 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cistatic const struct iio_chan_spec as73211_channels[] = { 15862306a36Sopenharmony_ci { 15962306a36Sopenharmony_ci .type = IIO_TEMP, 16062306a36Sopenharmony_ci .info_mask_separate = 16162306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_RAW) | 16262306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_OFFSET) | 16362306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_SCALE), 16462306a36Sopenharmony_ci .address = AS73211_OUT_TEMP, 16562306a36Sopenharmony_ci .scan_index = AS73211_SCAN_INDEX_TEMP, 16662306a36Sopenharmony_ci .scan_type = { 16762306a36Sopenharmony_ci .sign = 'u', 16862306a36Sopenharmony_ci .realbits = 16, 16962306a36Sopenharmony_ci .storagebits = 16, 17062306a36Sopenharmony_ci .endianness = IIO_LE, 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci }, 17362306a36Sopenharmony_ci AS73211_COLOR_CHANNEL(X, AS73211_SCAN_INDEX_X, AS73211_OUT_MRES1), 17462306a36Sopenharmony_ci AS73211_COLOR_CHANNEL(Y, AS73211_SCAN_INDEX_Y, AS73211_OUT_MRES2), 17562306a36Sopenharmony_ci AS73211_COLOR_CHANNEL(Z, AS73211_SCAN_INDEX_Z, AS73211_OUT_MRES3), 17662306a36Sopenharmony_ci IIO_CHAN_SOFT_TIMESTAMP(AS73211_SCAN_INDEX_TS), 17762306a36Sopenharmony_ci}; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_cistatic unsigned int as73211_integration_time_1024cyc(struct as73211_data *data) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci /* 18262306a36Sopenharmony_ci * Return integration time in units of 1024 clock cycles. Integration time 18362306a36Sopenharmony_ci * in CREG1 is in powers of 2 (x 1024 cycles). 18462306a36Sopenharmony_ci */ 18562306a36Sopenharmony_ci return BIT(FIELD_GET(AS73211_CREG1_TIME_MASK, data->creg1)); 18662306a36Sopenharmony_ci} 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_cistatic unsigned int as73211_integration_time_us(struct as73211_data *data, 18962306a36Sopenharmony_ci unsigned int integration_time_1024cyc) 19062306a36Sopenharmony_ci{ 19162306a36Sopenharmony_ci /* 19262306a36Sopenharmony_ci * f_samp is configured in CREG3 in powers of 2 (x 1.024 MHz) 19362306a36Sopenharmony_ci * t_cycl is configured in CREG1 in powers of 2 (x 1024 cycles) 19462306a36Sopenharmony_ci * t_int_us = 1 / (f_samp) * t_cycl * US_PER_SEC 19562306a36Sopenharmony_ci * = 1 / (2^CREG3_CCLK * 1,024,000) * 2^CREG1_CYCLES * 1,024 * US_PER_SEC 19662306a36Sopenharmony_ci * = 2^(-CREG3_CCLK) * 2^CREG1_CYCLES * 1,000 19762306a36Sopenharmony_ci * In order to get rid of negative exponents, we extend the "fraction" 19862306a36Sopenharmony_ci * by 2^3 (CREG3_CCLK,max = 3) 19962306a36Sopenharmony_ci * t_int_us = 2^(3-CREG3_CCLK) * 2^CREG1_CYCLES * 125 20062306a36Sopenharmony_ci */ 20162306a36Sopenharmony_ci return BIT(3 - FIELD_GET(AS73211_CREG3_CCLK_MASK, data->creg3)) * 20262306a36Sopenharmony_ci integration_time_1024cyc * 125; 20362306a36Sopenharmony_ci} 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_cistatic void as73211_integration_time_calc_avail(struct as73211_data *data) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci int i; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(data->int_time_avail) / 2; i++) { 21062306a36Sopenharmony_ci unsigned int time_us = as73211_integration_time_us(data, BIT(i)); 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci data->int_time_avail[i * 2 + 0] = time_us / USEC_PER_SEC; 21362306a36Sopenharmony_ci data->int_time_avail[i * 2 + 1] = time_us % USEC_PER_SEC; 21462306a36Sopenharmony_ci } 21562306a36Sopenharmony_ci} 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_cistatic unsigned int as73211_gain(struct as73211_data *data) 21862306a36Sopenharmony_ci{ 21962306a36Sopenharmony_ci /* gain can be calculated from CREG1 as 2^(11 - CREG1_GAIN) */ 22062306a36Sopenharmony_ci return BIT(AS73211_CREG1_GAIN_1 - FIELD_GET(AS73211_CREG1_GAIN_MASK, data->creg1)); 22162306a36Sopenharmony_ci} 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci/* must be called with as73211_data::mutex held. */ 22462306a36Sopenharmony_cistatic int as73211_req_data(struct as73211_data *data) 22562306a36Sopenharmony_ci{ 22662306a36Sopenharmony_ci unsigned int time_us = as73211_integration_time_us(data, 22762306a36Sopenharmony_ci as73211_integration_time_1024cyc(data)); 22862306a36Sopenharmony_ci struct device *dev = &data->client->dev; 22962306a36Sopenharmony_ci union i2c_smbus_data smbus_data; 23062306a36Sopenharmony_ci u16 osr_status; 23162306a36Sopenharmony_ci int ret; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci if (data->client->irq) 23462306a36Sopenharmony_ci reinit_completion(&data->completion); 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci /* 23762306a36Sopenharmony_ci * During measurement, there should be no traffic on the i2c bus as the 23862306a36Sopenharmony_ci * electrical noise would disturb the measurement process. 23962306a36Sopenharmony_ci */ 24062306a36Sopenharmony_ci i2c_lock_bus(data->client->adapter, I2C_LOCK_SEGMENT); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci data->osr &= ~AS73211_OSR_DOS_MASK; 24362306a36Sopenharmony_ci data->osr |= AS73211_OSR_DOS_MEASURE | AS73211_OSR_SS; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci smbus_data.byte = data->osr; 24662306a36Sopenharmony_ci ret = __i2c_smbus_xfer(data->client->adapter, data->client->addr, 24762306a36Sopenharmony_ci data->client->flags, I2C_SMBUS_WRITE, 24862306a36Sopenharmony_ci AS73211_REG_OSR, I2C_SMBUS_BYTE_DATA, &smbus_data); 24962306a36Sopenharmony_ci if (ret < 0) { 25062306a36Sopenharmony_ci i2c_unlock_bus(data->client->adapter, I2C_LOCK_SEGMENT); 25162306a36Sopenharmony_ci return ret; 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci /* 25562306a36Sopenharmony_ci * Reset AS73211_OSR_SS (is self clearing) in order to avoid unintentional 25662306a36Sopenharmony_ci * triggering of further measurements later. 25762306a36Sopenharmony_ci */ 25862306a36Sopenharmony_ci data->osr &= ~AS73211_OSR_SS; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci /* 26162306a36Sopenharmony_ci * Add 33% extra margin for the timeout. fclk,min = fclk,typ - 27%. 26262306a36Sopenharmony_ci */ 26362306a36Sopenharmony_ci time_us += time_us / 3; 26462306a36Sopenharmony_ci if (data->client->irq) { 26562306a36Sopenharmony_ci ret = wait_for_completion_timeout(&data->completion, usecs_to_jiffies(time_us)); 26662306a36Sopenharmony_ci if (!ret) { 26762306a36Sopenharmony_ci dev_err(dev, "timeout waiting for READY IRQ\n"); 26862306a36Sopenharmony_ci i2c_unlock_bus(data->client->adapter, I2C_LOCK_SEGMENT); 26962306a36Sopenharmony_ci return -ETIMEDOUT; 27062306a36Sopenharmony_ci } 27162306a36Sopenharmony_ci } else { 27262306a36Sopenharmony_ci /* Wait integration time */ 27362306a36Sopenharmony_ci usleep_range(time_us, 2 * time_us); 27462306a36Sopenharmony_ci } 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci i2c_unlock_bus(data->client->adapter, I2C_LOCK_SEGMENT); 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci ret = i2c_smbus_read_word_data(data->client, AS73211_OUT_OSR_STATUS); 27962306a36Sopenharmony_ci if (ret < 0) 28062306a36Sopenharmony_ci return ret; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci osr_status = ret; 28362306a36Sopenharmony_ci if (osr_status != (AS73211_OSR_DOS_MEASURE | AS73211_OSR_STATUS_NDATA)) { 28462306a36Sopenharmony_ci if (osr_status & AS73211_OSR_SS) { 28562306a36Sopenharmony_ci dev_err(dev, "%s() Measurement has not stopped\n", __func__); 28662306a36Sopenharmony_ci return -ETIME; 28762306a36Sopenharmony_ci } 28862306a36Sopenharmony_ci if (osr_status & AS73211_OSR_STATUS_NOTREADY) { 28962306a36Sopenharmony_ci dev_err(dev, "%s() Data is not ready\n", __func__); 29062306a36Sopenharmony_ci return -ENODATA; 29162306a36Sopenharmony_ci } 29262306a36Sopenharmony_ci if (!(osr_status & AS73211_OSR_STATUS_NDATA)) { 29362306a36Sopenharmony_ci dev_err(dev, "%s() No new data available\n", __func__); 29462306a36Sopenharmony_ci return -ENODATA; 29562306a36Sopenharmony_ci } 29662306a36Sopenharmony_ci if (osr_status & AS73211_OSR_STATUS_LDATA) { 29762306a36Sopenharmony_ci dev_err(dev, "%s() Result buffer overrun\n", __func__); 29862306a36Sopenharmony_ci return -ENOBUFS; 29962306a36Sopenharmony_ci } 30062306a36Sopenharmony_ci if (osr_status & AS73211_OSR_STATUS_ADCOF) { 30162306a36Sopenharmony_ci dev_err(dev, "%s() ADC overflow\n", __func__); 30262306a36Sopenharmony_ci return -EOVERFLOW; 30362306a36Sopenharmony_ci } 30462306a36Sopenharmony_ci if (osr_status & AS73211_OSR_STATUS_MRESOF) { 30562306a36Sopenharmony_ci dev_err(dev, "%s() Measurement result overflow\n", __func__); 30662306a36Sopenharmony_ci return -EOVERFLOW; 30762306a36Sopenharmony_ci } 30862306a36Sopenharmony_ci if (osr_status & AS73211_OSR_STATUS_OUTCONVOF) { 30962306a36Sopenharmony_ci dev_err(dev, "%s() Timer overflow\n", __func__); 31062306a36Sopenharmony_ci return -EOVERFLOW; 31162306a36Sopenharmony_ci } 31262306a36Sopenharmony_ci dev_err(dev, "%s() Unexpected status value\n", __func__); 31362306a36Sopenharmony_ci return -EIO; 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci return 0; 31762306a36Sopenharmony_ci} 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_cistatic int as73211_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, 32062306a36Sopenharmony_ci int *val, int *val2, long mask) 32162306a36Sopenharmony_ci{ 32262306a36Sopenharmony_ci struct as73211_data *data = iio_priv(indio_dev); 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci switch (mask) { 32562306a36Sopenharmony_ci case IIO_CHAN_INFO_RAW: { 32662306a36Sopenharmony_ci int ret; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci ret = iio_device_claim_direct_mode(indio_dev); 32962306a36Sopenharmony_ci if (ret < 0) 33062306a36Sopenharmony_ci return ret; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci ret = as73211_req_data(data); 33362306a36Sopenharmony_ci if (ret < 0) { 33462306a36Sopenharmony_ci iio_device_release_direct_mode(indio_dev); 33562306a36Sopenharmony_ci return ret; 33662306a36Sopenharmony_ci } 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci ret = i2c_smbus_read_word_data(data->client, chan->address); 33962306a36Sopenharmony_ci iio_device_release_direct_mode(indio_dev); 34062306a36Sopenharmony_ci if (ret < 0) 34162306a36Sopenharmony_ci return ret; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci *val = ret; 34462306a36Sopenharmony_ci return IIO_VAL_INT; 34562306a36Sopenharmony_ci } 34662306a36Sopenharmony_ci case IIO_CHAN_INFO_OFFSET: 34762306a36Sopenharmony_ci *val = AS73211_OFFSET_TEMP_INT; 34862306a36Sopenharmony_ci *val2 = AS73211_OFFSET_TEMP_MICRO; 34962306a36Sopenharmony_ci return IIO_VAL_INT_PLUS_MICRO; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci case IIO_CHAN_INFO_SCALE: 35262306a36Sopenharmony_ci switch (chan->type) { 35362306a36Sopenharmony_ci case IIO_TEMP: 35462306a36Sopenharmony_ci *val = AS73211_SCALE_TEMP_INT; 35562306a36Sopenharmony_ci *val2 = AS73211_SCALE_TEMP_MICRO; 35662306a36Sopenharmony_ci return IIO_VAL_INT_PLUS_MICRO; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci case IIO_INTENSITY: { 35962306a36Sopenharmony_ci unsigned int scale; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci switch (chan->channel2) { 36262306a36Sopenharmony_ci case IIO_MOD_X: 36362306a36Sopenharmony_ci scale = AS73211_SCALE_X; 36462306a36Sopenharmony_ci break; 36562306a36Sopenharmony_ci case IIO_MOD_Y: 36662306a36Sopenharmony_ci scale = AS73211_SCALE_Y; 36762306a36Sopenharmony_ci break; 36862306a36Sopenharmony_ci case IIO_MOD_Z: 36962306a36Sopenharmony_ci scale = AS73211_SCALE_Z; 37062306a36Sopenharmony_ci break; 37162306a36Sopenharmony_ci default: 37262306a36Sopenharmony_ci return -EINVAL; 37362306a36Sopenharmony_ci } 37462306a36Sopenharmony_ci scale /= as73211_gain(data); 37562306a36Sopenharmony_ci scale /= as73211_integration_time_1024cyc(data); 37662306a36Sopenharmony_ci *val = scale; 37762306a36Sopenharmony_ci return IIO_VAL_INT; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci default: 38062306a36Sopenharmony_ci return -EINVAL; 38162306a36Sopenharmony_ci }} 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci case IIO_CHAN_INFO_SAMP_FREQ: 38462306a36Sopenharmony_ci /* f_samp is configured in CREG3 in powers of 2 (x 1.024 MHz) */ 38562306a36Sopenharmony_ci *val = BIT(FIELD_GET(AS73211_CREG3_CCLK_MASK, data->creg3)) * 38662306a36Sopenharmony_ci AS73211_SAMPLE_FREQ_BASE; 38762306a36Sopenharmony_ci return IIO_VAL_INT; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci case IIO_CHAN_INFO_HARDWAREGAIN: 39062306a36Sopenharmony_ci *val = as73211_gain(data); 39162306a36Sopenharmony_ci return IIO_VAL_INT; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci case IIO_CHAN_INFO_INT_TIME: { 39462306a36Sopenharmony_ci unsigned int time_us; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci mutex_lock(&data->mutex); 39762306a36Sopenharmony_ci time_us = as73211_integration_time_us(data, as73211_integration_time_1024cyc(data)); 39862306a36Sopenharmony_ci mutex_unlock(&data->mutex); 39962306a36Sopenharmony_ci *val = time_us / USEC_PER_SEC; 40062306a36Sopenharmony_ci *val2 = time_us % USEC_PER_SEC; 40162306a36Sopenharmony_ci return IIO_VAL_INT_PLUS_MICRO; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci default: 40462306a36Sopenharmony_ci return -EINVAL; 40562306a36Sopenharmony_ci }} 40662306a36Sopenharmony_ci} 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_cistatic int as73211_read_avail(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, 40962306a36Sopenharmony_ci const int **vals, int *type, int *length, long mask) 41062306a36Sopenharmony_ci{ 41162306a36Sopenharmony_ci struct as73211_data *data = iio_priv(indio_dev); 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci switch (mask) { 41462306a36Sopenharmony_ci case IIO_CHAN_INFO_SAMP_FREQ: 41562306a36Sopenharmony_ci *length = ARRAY_SIZE(as73211_samp_freq_avail); 41662306a36Sopenharmony_ci *vals = as73211_samp_freq_avail; 41762306a36Sopenharmony_ci *type = IIO_VAL_INT; 41862306a36Sopenharmony_ci return IIO_AVAIL_LIST; 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci case IIO_CHAN_INFO_HARDWAREGAIN: 42162306a36Sopenharmony_ci *length = ARRAY_SIZE(as73211_hardwaregain_avail); 42262306a36Sopenharmony_ci *vals = as73211_hardwaregain_avail; 42362306a36Sopenharmony_ci *type = IIO_VAL_INT; 42462306a36Sopenharmony_ci return IIO_AVAIL_LIST; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci case IIO_CHAN_INFO_INT_TIME: 42762306a36Sopenharmony_ci *length = ARRAY_SIZE(data->int_time_avail); 42862306a36Sopenharmony_ci *vals = data->int_time_avail; 42962306a36Sopenharmony_ci *type = IIO_VAL_INT_PLUS_MICRO; 43062306a36Sopenharmony_ci return IIO_AVAIL_LIST; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci default: 43362306a36Sopenharmony_ci return -EINVAL; 43462306a36Sopenharmony_ci } 43562306a36Sopenharmony_ci} 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_cistatic int _as73211_write_raw(struct iio_dev *indio_dev, 43862306a36Sopenharmony_ci struct iio_chan_spec const *chan __always_unused, 43962306a36Sopenharmony_ci int val, int val2, long mask) 44062306a36Sopenharmony_ci{ 44162306a36Sopenharmony_ci struct as73211_data *data = iio_priv(indio_dev); 44262306a36Sopenharmony_ci int ret; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci switch (mask) { 44562306a36Sopenharmony_ci case IIO_CHAN_INFO_SAMP_FREQ: { 44662306a36Sopenharmony_ci int reg_bits, freq_kHz = val / HZ_PER_KHZ; /* 1024, 2048, ... */ 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci /* val must be 1024 * 2^x */ 44962306a36Sopenharmony_ci if (val < 0 || (freq_kHz * HZ_PER_KHZ) != val || 45062306a36Sopenharmony_ci !is_power_of_2(freq_kHz) || val2) 45162306a36Sopenharmony_ci return -EINVAL; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci /* f_samp is configured in CREG3 in powers of 2 (x 1.024 MHz (=2^10)) */ 45462306a36Sopenharmony_ci reg_bits = ilog2(freq_kHz) - 10; 45562306a36Sopenharmony_ci if (!FIELD_FIT(AS73211_CREG3_CCLK_MASK, reg_bits)) 45662306a36Sopenharmony_ci return -EINVAL; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci data->creg3 &= ~AS73211_CREG3_CCLK_MASK; 45962306a36Sopenharmony_ci data->creg3 |= FIELD_PREP(AS73211_CREG3_CCLK_MASK, reg_bits); 46062306a36Sopenharmony_ci as73211_integration_time_calc_avail(data); 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci ret = i2c_smbus_write_byte_data(data->client, AS73211_REG_CREG3, data->creg3); 46362306a36Sopenharmony_ci if (ret < 0) 46462306a36Sopenharmony_ci return ret; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci return 0; 46762306a36Sopenharmony_ci } 46862306a36Sopenharmony_ci case IIO_CHAN_INFO_HARDWAREGAIN: { 46962306a36Sopenharmony_ci unsigned int reg_bits; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci if (val < 0 || !is_power_of_2(val) || val2) 47262306a36Sopenharmony_ci return -EINVAL; 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci /* gain can be calculated from CREG1 as 2^(11 - CREG1_GAIN) */ 47562306a36Sopenharmony_ci reg_bits = AS73211_CREG1_GAIN_1 - ilog2(val); 47662306a36Sopenharmony_ci if (!FIELD_FIT(AS73211_CREG1_GAIN_MASK, reg_bits)) 47762306a36Sopenharmony_ci return -EINVAL; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci data->creg1 &= ~AS73211_CREG1_GAIN_MASK; 48062306a36Sopenharmony_ci data->creg1 |= FIELD_PREP(AS73211_CREG1_GAIN_MASK, reg_bits); 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci ret = i2c_smbus_write_byte_data(data->client, AS73211_REG_CREG1, data->creg1); 48362306a36Sopenharmony_ci if (ret < 0) 48462306a36Sopenharmony_ci return ret; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci return 0; 48762306a36Sopenharmony_ci } 48862306a36Sopenharmony_ci case IIO_CHAN_INFO_INT_TIME: { 48962306a36Sopenharmony_ci int val_us = val * USEC_PER_SEC + val2; 49062306a36Sopenharmony_ci int time_ms; 49162306a36Sopenharmony_ci int reg_bits; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci /* f_samp is configured in CREG3 in powers of 2 (x 1.024 MHz) */ 49462306a36Sopenharmony_ci int f_samp_1_024mhz = BIT(FIELD_GET(AS73211_CREG3_CCLK_MASK, data->creg3)); 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci /* 49762306a36Sopenharmony_ci * time_ms = time_us * US_PER_MS * f_samp_1_024mhz / MHZ_PER_HZ 49862306a36Sopenharmony_ci * = time_us * f_samp_1_024mhz / 1000 49962306a36Sopenharmony_ci */ 50062306a36Sopenharmony_ci time_ms = (val_us * f_samp_1_024mhz) / 1000; /* 1 ms, 2 ms, ... (power of two) */ 50162306a36Sopenharmony_ci if (time_ms < 0 || !is_power_of_2(time_ms) || time_ms > AS73211_SAMPLE_TIME_MAX_MS) 50262306a36Sopenharmony_ci return -EINVAL; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci reg_bits = ilog2(time_ms); 50562306a36Sopenharmony_ci if (!FIELD_FIT(AS73211_CREG1_TIME_MASK, reg_bits)) 50662306a36Sopenharmony_ci return -EINVAL; /* not possible due to previous tests */ 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci data->creg1 &= ~AS73211_CREG1_TIME_MASK; 50962306a36Sopenharmony_ci data->creg1 |= FIELD_PREP(AS73211_CREG1_TIME_MASK, reg_bits); 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci ret = i2c_smbus_write_byte_data(data->client, AS73211_REG_CREG1, data->creg1); 51262306a36Sopenharmony_ci if (ret < 0) 51362306a36Sopenharmony_ci return ret; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci return 0; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci default: 51862306a36Sopenharmony_ci return -EINVAL; 51962306a36Sopenharmony_ci }} 52062306a36Sopenharmony_ci} 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_cistatic int as73211_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, 52362306a36Sopenharmony_ci int val, int val2, long mask) 52462306a36Sopenharmony_ci{ 52562306a36Sopenharmony_ci struct as73211_data *data = iio_priv(indio_dev); 52662306a36Sopenharmony_ci int ret; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci mutex_lock(&data->mutex); 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci ret = iio_device_claim_direct_mode(indio_dev); 53162306a36Sopenharmony_ci if (ret < 0) 53262306a36Sopenharmony_ci goto error_unlock; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci /* Need to switch to config mode ... */ 53562306a36Sopenharmony_ci if ((data->osr & AS73211_OSR_DOS_MASK) != AS73211_OSR_DOS_CONFIG) { 53662306a36Sopenharmony_ci data->osr &= ~AS73211_OSR_DOS_MASK; 53762306a36Sopenharmony_ci data->osr |= AS73211_OSR_DOS_CONFIG; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci ret = i2c_smbus_write_byte_data(data->client, AS73211_REG_OSR, data->osr); 54062306a36Sopenharmony_ci if (ret < 0) 54162306a36Sopenharmony_ci goto error_release; 54262306a36Sopenharmony_ci } 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci ret = _as73211_write_raw(indio_dev, chan, val, val2, mask); 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_cierror_release: 54762306a36Sopenharmony_ci iio_device_release_direct_mode(indio_dev); 54862306a36Sopenharmony_cierror_unlock: 54962306a36Sopenharmony_ci mutex_unlock(&data->mutex); 55062306a36Sopenharmony_ci return ret; 55162306a36Sopenharmony_ci} 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_cistatic irqreturn_t as73211_ready_handler(int irq __always_unused, void *priv) 55462306a36Sopenharmony_ci{ 55562306a36Sopenharmony_ci struct as73211_data *data = iio_priv(priv); 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci complete(&data->completion); 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci return IRQ_HANDLED; 56062306a36Sopenharmony_ci} 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_cistatic irqreturn_t as73211_trigger_handler(int irq __always_unused, void *p) 56362306a36Sopenharmony_ci{ 56462306a36Sopenharmony_ci struct iio_poll_func *pf = p; 56562306a36Sopenharmony_ci struct iio_dev *indio_dev = pf->indio_dev; 56662306a36Sopenharmony_ci struct as73211_data *data = iio_priv(indio_dev); 56762306a36Sopenharmony_ci struct { 56862306a36Sopenharmony_ci __le16 chan[4]; 56962306a36Sopenharmony_ci s64 ts __aligned(8); 57062306a36Sopenharmony_ci } scan; 57162306a36Sopenharmony_ci int data_result, ret; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci mutex_lock(&data->mutex); 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci data_result = as73211_req_data(data); 57662306a36Sopenharmony_ci if (data_result < 0 && data_result != -EOVERFLOW) 57762306a36Sopenharmony_ci goto done; /* don't push any data for errors other than EOVERFLOW */ 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci if (*indio_dev->active_scan_mask == AS73211_SCAN_MASK_ALL) { 58062306a36Sopenharmony_ci /* Optimization for reading all (color + temperature) channels */ 58162306a36Sopenharmony_ci u8 addr = as73211_channels[0].address; 58262306a36Sopenharmony_ci struct i2c_msg msgs[] = { 58362306a36Sopenharmony_ci { 58462306a36Sopenharmony_ci .addr = data->client->addr, 58562306a36Sopenharmony_ci .flags = 0, 58662306a36Sopenharmony_ci .len = 1, 58762306a36Sopenharmony_ci .buf = &addr, 58862306a36Sopenharmony_ci }, 58962306a36Sopenharmony_ci { 59062306a36Sopenharmony_ci .addr = data->client->addr, 59162306a36Sopenharmony_ci .flags = I2C_M_RD, 59262306a36Sopenharmony_ci .len = sizeof(scan.chan), 59362306a36Sopenharmony_ci .buf = (u8 *)&scan.chan, 59462306a36Sopenharmony_ci }, 59562306a36Sopenharmony_ci }; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci ret = i2c_transfer(data->client->adapter, msgs, ARRAY_SIZE(msgs)); 59862306a36Sopenharmony_ci if (ret < 0) 59962306a36Sopenharmony_ci goto done; 60062306a36Sopenharmony_ci } else { 60162306a36Sopenharmony_ci /* Optimization for reading only color channels */ 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci /* AS73211 starts reading at address 2 */ 60462306a36Sopenharmony_ci ret = i2c_master_recv(data->client, 60562306a36Sopenharmony_ci (char *)&scan.chan[1], 3 * sizeof(scan.chan[1])); 60662306a36Sopenharmony_ci if (ret < 0) 60762306a36Sopenharmony_ci goto done; 60862306a36Sopenharmony_ci } 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci if (data_result) { 61162306a36Sopenharmony_ci /* 61262306a36Sopenharmony_ci * Saturate all channels (in case of overflows). Temperature channel 61362306a36Sopenharmony_ci * is not affected by overflows. 61462306a36Sopenharmony_ci */ 61562306a36Sopenharmony_ci scan.chan[1] = cpu_to_le16(U16_MAX); 61662306a36Sopenharmony_ci scan.chan[2] = cpu_to_le16(U16_MAX); 61762306a36Sopenharmony_ci scan.chan[3] = cpu_to_le16(U16_MAX); 61862306a36Sopenharmony_ci } 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci iio_push_to_buffers_with_timestamp(indio_dev, &scan, iio_get_time_ns(indio_dev)); 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_cidone: 62362306a36Sopenharmony_ci mutex_unlock(&data->mutex); 62462306a36Sopenharmony_ci iio_trigger_notify_done(indio_dev->trig); 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci return IRQ_HANDLED; 62762306a36Sopenharmony_ci} 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_cistatic const struct iio_info as73211_info = { 63062306a36Sopenharmony_ci .read_raw = as73211_read_raw, 63162306a36Sopenharmony_ci .read_avail = as73211_read_avail, 63262306a36Sopenharmony_ci .write_raw = as73211_write_raw, 63362306a36Sopenharmony_ci}; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_cistatic int as73211_power(struct iio_dev *indio_dev, bool state) 63662306a36Sopenharmony_ci{ 63762306a36Sopenharmony_ci struct as73211_data *data = iio_priv(indio_dev); 63862306a36Sopenharmony_ci int ret; 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci mutex_lock(&data->mutex); 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci if (state) 64362306a36Sopenharmony_ci data->osr &= ~AS73211_OSR_PD; 64462306a36Sopenharmony_ci else 64562306a36Sopenharmony_ci data->osr |= AS73211_OSR_PD; 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci ret = i2c_smbus_write_byte_data(data->client, AS73211_REG_OSR, data->osr); 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci mutex_unlock(&data->mutex); 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci if (ret < 0) 65262306a36Sopenharmony_ci return ret; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci return 0; 65562306a36Sopenharmony_ci} 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_cistatic void as73211_power_disable(void *data) 65862306a36Sopenharmony_ci{ 65962306a36Sopenharmony_ci struct iio_dev *indio_dev = data; 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci as73211_power(indio_dev, false); 66262306a36Sopenharmony_ci} 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_cistatic int as73211_probe(struct i2c_client *client) 66562306a36Sopenharmony_ci{ 66662306a36Sopenharmony_ci struct device *dev = &client->dev; 66762306a36Sopenharmony_ci struct as73211_data *data; 66862306a36Sopenharmony_ci struct iio_dev *indio_dev; 66962306a36Sopenharmony_ci int ret; 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); 67262306a36Sopenharmony_ci if (!indio_dev) 67362306a36Sopenharmony_ci return -ENOMEM; 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci data = iio_priv(indio_dev); 67662306a36Sopenharmony_ci i2c_set_clientdata(client, indio_dev); 67762306a36Sopenharmony_ci data->client = client; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci mutex_init(&data->mutex); 68062306a36Sopenharmony_ci init_completion(&data->completion); 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci indio_dev->info = &as73211_info; 68362306a36Sopenharmony_ci indio_dev->name = AS73211_DRV_NAME; 68462306a36Sopenharmony_ci indio_dev->channels = as73211_channels; 68562306a36Sopenharmony_ci indio_dev->num_channels = ARRAY_SIZE(as73211_channels); 68662306a36Sopenharmony_ci indio_dev->modes = INDIO_DIRECT_MODE; 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci ret = i2c_smbus_read_byte_data(data->client, AS73211_REG_OSR); 68962306a36Sopenharmony_ci if (ret < 0) 69062306a36Sopenharmony_ci return ret; 69162306a36Sopenharmony_ci data->osr = ret; 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci /* reset device */ 69462306a36Sopenharmony_ci data->osr |= AS73211_OSR_SW_RES; 69562306a36Sopenharmony_ci ret = i2c_smbus_write_byte_data(data->client, AS73211_REG_OSR, data->osr); 69662306a36Sopenharmony_ci if (ret < 0) 69762306a36Sopenharmony_ci return ret; 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci ret = i2c_smbus_read_byte_data(data->client, AS73211_REG_OSR); 70062306a36Sopenharmony_ci if (ret < 0) 70162306a36Sopenharmony_ci return ret; 70262306a36Sopenharmony_ci data->osr = ret; 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci /* 70562306a36Sopenharmony_ci * Reading AGEN is only possible after reset (AGEN is not available if 70662306a36Sopenharmony_ci * device is in measurement mode). 70762306a36Sopenharmony_ci */ 70862306a36Sopenharmony_ci ret = i2c_smbus_read_byte_data(data->client, AS73211_REG_AGEN); 70962306a36Sopenharmony_ci if (ret < 0) 71062306a36Sopenharmony_ci return ret; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci /* At the time of writing this driver, only DEVID 2 and MUT 1 are known. */ 71362306a36Sopenharmony_ci if ((ret & AS73211_AGEN_DEVID_MASK) != AS73211_AGEN_DEVID(2) || 71462306a36Sopenharmony_ci (ret & AS73211_AGEN_MUT_MASK) != AS73211_AGEN_MUT(1)) 71562306a36Sopenharmony_ci return -ENODEV; 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci ret = i2c_smbus_read_byte_data(data->client, AS73211_REG_CREG1); 71862306a36Sopenharmony_ci if (ret < 0) 71962306a36Sopenharmony_ci return ret; 72062306a36Sopenharmony_ci data->creg1 = ret; 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci ret = i2c_smbus_read_byte_data(data->client, AS73211_REG_CREG2); 72362306a36Sopenharmony_ci if (ret < 0) 72462306a36Sopenharmony_ci return ret; 72562306a36Sopenharmony_ci data->creg2 = ret; 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci ret = i2c_smbus_read_byte_data(data->client, AS73211_REG_CREG3); 72862306a36Sopenharmony_ci if (ret < 0) 72962306a36Sopenharmony_ci return ret; 73062306a36Sopenharmony_ci data->creg3 = ret; 73162306a36Sopenharmony_ci as73211_integration_time_calc_avail(data); 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci ret = as73211_power(indio_dev, true); 73462306a36Sopenharmony_ci if (ret < 0) 73562306a36Sopenharmony_ci return ret; 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci ret = devm_add_action_or_reset(dev, as73211_power_disable, indio_dev); 73862306a36Sopenharmony_ci if (ret) 73962306a36Sopenharmony_ci return ret; 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL, as73211_trigger_handler, NULL); 74262306a36Sopenharmony_ci if (ret) 74362306a36Sopenharmony_ci return ret; 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci if (client->irq) { 74662306a36Sopenharmony_ci ret = devm_request_threaded_irq(&client->dev, client->irq, 74762306a36Sopenharmony_ci NULL, 74862306a36Sopenharmony_ci as73211_ready_handler, 74962306a36Sopenharmony_ci IRQF_ONESHOT, 75062306a36Sopenharmony_ci client->name, indio_dev); 75162306a36Sopenharmony_ci if (ret) 75262306a36Sopenharmony_ci return ret; 75362306a36Sopenharmony_ci } 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci return devm_iio_device_register(dev, indio_dev); 75662306a36Sopenharmony_ci} 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_cistatic int as73211_suspend(struct device *dev) 75962306a36Sopenharmony_ci{ 76062306a36Sopenharmony_ci struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci return as73211_power(indio_dev, false); 76362306a36Sopenharmony_ci} 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_cistatic int as73211_resume(struct device *dev) 76662306a36Sopenharmony_ci{ 76762306a36Sopenharmony_ci struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci return as73211_power(indio_dev, true); 77062306a36Sopenharmony_ci} 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_cistatic DEFINE_SIMPLE_DEV_PM_OPS(as73211_pm_ops, as73211_suspend, 77362306a36Sopenharmony_ci as73211_resume); 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_cistatic const struct of_device_id as73211_of_match[] = { 77662306a36Sopenharmony_ci { .compatible = "ams,as73211" }, 77762306a36Sopenharmony_ci { } 77862306a36Sopenharmony_ci}; 77962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, as73211_of_match); 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_cistatic const struct i2c_device_id as73211_id[] = { 78262306a36Sopenharmony_ci { "as73211", 0 }, 78362306a36Sopenharmony_ci { } 78462306a36Sopenharmony_ci}; 78562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, as73211_id); 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_cistatic struct i2c_driver as73211_driver = { 78862306a36Sopenharmony_ci .driver = { 78962306a36Sopenharmony_ci .name = AS73211_DRV_NAME, 79062306a36Sopenharmony_ci .of_match_table = as73211_of_match, 79162306a36Sopenharmony_ci .pm = pm_sleep_ptr(&as73211_pm_ops), 79262306a36Sopenharmony_ci }, 79362306a36Sopenharmony_ci .probe = as73211_probe, 79462306a36Sopenharmony_ci .id_table = as73211_id, 79562306a36Sopenharmony_ci}; 79662306a36Sopenharmony_cimodule_i2c_driver(as73211_driver); 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ciMODULE_AUTHOR("Christian Eggers <ceggers@arri.de>"); 79962306a36Sopenharmony_ciMODULE_DESCRIPTION("AS73211 XYZ True Color Sensor driver"); 80062306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 801