xref: /openbmc/linux/drivers/iio/light/as73211.c (revision 7cf15f42)
1403e5586SChristian Eggers // SPDX-License-Identifier: GPL-2.0-only
2403e5586SChristian Eggers /*
3403e5586SChristian Eggers  * Support for AMS AS73211 JENCOLOR(R) Digital XYZ Sensor
4403e5586SChristian Eggers  *
5403e5586SChristian Eggers  * Author: Christian Eggers <ceggers@arri.de>
6403e5586SChristian Eggers  *
7403e5586SChristian Eggers  * Copyright (c) 2020 ARRI Lighting
8403e5586SChristian Eggers  *
9403e5586SChristian Eggers  * Color light sensor with 16-bit channels for x, y, z and temperature);
10403e5586SChristian Eggers  * 7-bit I2C slave address 0x74 .. 0x77.
11403e5586SChristian Eggers  *
12403e5586SChristian Eggers  * Datasheet: https://ams.com/documents/20143/36005/AS73211_DS000556_3-01.pdf
13403e5586SChristian Eggers  */
14403e5586SChristian Eggers 
15403e5586SChristian Eggers #include <linux/bitfield.h>
16403e5586SChristian Eggers #include <linux/completion.h>
17403e5586SChristian Eggers #include <linux/delay.h>
18403e5586SChristian Eggers #include <linux/i2c.h>
19403e5586SChristian Eggers #include <linux/iio/buffer.h>
20403e5586SChristian Eggers #include <linux/iio/iio.h>
21403e5586SChristian Eggers #include <linux/iio/sysfs.h>
22403e5586SChristian Eggers #include <linux/iio/trigger_consumer.h>
23403e5586SChristian Eggers #include <linux/iio/triggered_buffer.h>
24403e5586SChristian Eggers #include <linux/module.h>
25403e5586SChristian Eggers #include <linux/mutex.h>
26403e5586SChristian Eggers #include <linux/pm.h>
2755c653e0SDaniel Lezcano #include <linux/units.h>
28403e5586SChristian Eggers 
29403e5586SChristian Eggers #define AS73211_DRV_NAME "as73211"
30403e5586SChristian Eggers 
31403e5586SChristian Eggers /* AS73211 configuration registers */
32403e5586SChristian Eggers #define AS73211_REG_OSR    0x0
33403e5586SChristian Eggers #define AS73211_REG_AGEN   0x2
34403e5586SChristian Eggers #define AS73211_REG_CREG1  0x6
35403e5586SChristian Eggers #define AS73211_REG_CREG2  0x7
36403e5586SChristian Eggers #define AS73211_REG_CREG3  0x8
37403e5586SChristian Eggers 
38403e5586SChristian Eggers /* AS73211 output register bank */
39403e5586SChristian Eggers #define AS73211_OUT_OSR_STATUS    0
40403e5586SChristian Eggers #define AS73211_OUT_TEMP          1
41403e5586SChristian Eggers #define AS73211_OUT_MRES1         2
42403e5586SChristian Eggers #define AS73211_OUT_MRES2         3
43403e5586SChristian Eggers #define AS73211_OUT_MRES3         4
44403e5586SChristian Eggers 
45403e5586SChristian Eggers #define AS73211_OSR_SS            BIT(7)
46403e5586SChristian Eggers #define AS73211_OSR_PD            BIT(6)
47403e5586SChristian Eggers #define AS73211_OSR_SW_RES        BIT(3)
48403e5586SChristian Eggers #define AS73211_OSR_DOS_MASK      GENMASK(2, 0)
49403e5586SChristian Eggers #define AS73211_OSR_DOS_CONFIG    FIELD_PREP(AS73211_OSR_DOS_MASK, 0x2)
50403e5586SChristian Eggers #define AS73211_OSR_DOS_MEASURE   FIELD_PREP(AS73211_OSR_DOS_MASK, 0x3)
51403e5586SChristian Eggers 
52403e5586SChristian Eggers #define AS73211_AGEN_DEVID_MASK   GENMASK(7, 4)
53403e5586SChristian Eggers #define AS73211_AGEN_DEVID(x)     FIELD_PREP(AS73211_AGEN_DEVID_MASK, (x))
54403e5586SChristian Eggers #define AS73211_AGEN_MUT_MASK     GENMASK(3, 0)
55403e5586SChristian Eggers #define AS73211_AGEN_MUT(x)       FIELD_PREP(AS73211_AGEN_MUT_MASK, (x))
56403e5586SChristian Eggers 
57403e5586SChristian Eggers #define AS73211_CREG1_GAIN_MASK   GENMASK(7, 4)
58403e5586SChristian Eggers #define AS73211_CREG1_GAIN_1      11
59403e5586SChristian Eggers #define AS73211_CREG1_TIME_MASK   GENMASK(3, 0)
60403e5586SChristian Eggers 
61403e5586SChristian Eggers #define AS73211_CREG3_CCLK_MASK   GENMASK(1, 0)
62403e5586SChristian Eggers 
63403e5586SChristian Eggers #define AS73211_OSR_STATUS_OUTCONVOF  BIT(15)
64403e5586SChristian Eggers #define AS73211_OSR_STATUS_MRESOF     BIT(14)
65403e5586SChristian Eggers #define AS73211_OSR_STATUS_ADCOF      BIT(13)
66403e5586SChristian Eggers #define AS73211_OSR_STATUS_LDATA      BIT(12)
67403e5586SChristian Eggers #define AS73211_OSR_STATUS_NDATA      BIT(11)
68403e5586SChristian Eggers #define AS73211_OSR_STATUS_NOTREADY   BIT(10)
69403e5586SChristian Eggers 
70403e5586SChristian Eggers #define AS73211_SAMPLE_FREQ_BASE      1024000
71403e5586SChristian Eggers 
72403e5586SChristian Eggers #define AS73211_SAMPLE_TIME_NUM       15
73403e5586SChristian Eggers #define AS73211_SAMPLE_TIME_MAX_MS    BIT(AS73211_SAMPLE_TIME_NUM - 1)
74403e5586SChristian Eggers 
75403e5586SChristian Eggers /* Available sample frequencies are 1.024MHz multiplied by powers of two. */
76403e5586SChristian Eggers static const int as73211_samp_freq_avail[] = {
77403e5586SChristian Eggers 	AS73211_SAMPLE_FREQ_BASE * 1,
78403e5586SChristian Eggers 	AS73211_SAMPLE_FREQ_BASE * 2,
79403e5586SChristian Eggers 	AS73211_SAMPLE_FREQ_BASE * 4,
80403e5586SChristian Eggers 	AS73211_SAMPLE_FREQ_BASE * 8,
81403e5586SChristian Eggers };
82403e5586SChristian Eggers 
83403e5586SChristian Eggers static const int as73211_hardwaregain_avail[] = {
84403e5586SChristian Eggers 	1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048,
85403e5586SChristian Eggers };
86403e5586SChristian Eggers 
87403e5586SChristian Eggers /**
88403e5586SChristian Eggers  * struct as73211_data - Instance data for one AS73211
89403e5586SChristian Eggers  * @client: I2C client.
90403e5586SChristian Eggers  * @osr:    Cached Operational State Register.
91403e5586SChristian Eggers  * @creg1:  Cached Configuration Register 1.
92403e5586SChristian Eggers  * @creg2:  Cached Configuration Register 2.
93403e5586SChristian Eggers  * @creg3:  Cached Configuration Register 3.
94403e5586SChristian Eggers  * @mutex:  Keeps cached registers in sync with the device.
95403e5586SChristian Eggers  * @completion: Completion to wait for interrupt.
96403e5586SChristian Eggers  * @int_time_avail: Available integration times (depend on sampling frequency).
97403e5586SChristian Eggers  */
98403e5586SChristian Eggers struct as73211_data {
99403e5586SChristian Eggers 	struct i2c_client *client;
100403e5586SChristian Eggers 	u8 osr;
101403e5586SChristian Eggers 	u8 creg1;
102403e5586SChristian Eggers 	u8 creg2;
103403e5586SChristian Eggers 	u8 creg3;
104403e5586SChristian Eggers 	struct mutex mutex;
105403e5586SChristian Eggers 	struct completion completion;
106403e5586SChristian Eggers 	int int_time_avail[AS73211_SAMPLE_TIME_NUM * 2];
107403e5586SChristian Eggers };
108403e5586SChristian Eggers 
109403e5586SChristian Eggers #define AS73211_COLOR_CHANNEL(_color, _si, _addr) { \
110403e5586SChristian Eggers 	.type = IIO_INTENSITY, \
111403e5586SChristian Eggers 	.modified = 1, \
112403e5586SChristian Eggers 	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), \
113403e5586SChristian Eggers 	.info_mask_shared_by_type = \
114403e5586SChristian Eggers 		BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
115403e5586SChristian Eggers 		BIT(IIO_CHAN_INFO_HARDWAREGAIN) | \
116403e5586SChristian Eggers 		BIT(IIO_CHAN_INFO_INT_TIME), \
117403e5586SChristian Eggers 	.info_mask_shared_by_type_available = \
118403e5586SChristian Eggers 		BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
119403e5586SChristian Eggers 		BIT(IIO_CHAN_INFO_HARDWAREGAIN) | \
120403e5586SChristian Eggers 		BIT(IIO_CHAN_INFO_INT_TIME), \
121403e5586SChristian Eggers 	.channel2 = IIO_MOD_##_color, \
122403e5586SChristian Eggers 	.address = _addr, \
123403e5586SChristian Eggers 	.scan_index = _si, \
124403e5586SChristian Eggers 	.scan_type = { \
125403e5586SChristian Eggers 		.sign = 'u', \
126403e5586SChristian Eggers 		.realbits = 16, \
127403e5586SChristian Eggers 		.storagebits = 16, \
128403e5586SChristian Eggers 		.endianness = IIO_LE, \
129403e5586SChristian Eggers 	}, \
130403e5586SChristian Eggers }
131403e5586SChristian Eggers 
132403e5586SChristian Eggers #define AS73211_OFFSET_TEMP_INT    (-66)
133403e5586SChristian Eggers #define AS73211_OFFSET_TEMP_MICRO  900000
134403e5586SChristian Eggers #define AS73211_SCALE_TEMP_INT     0
135403e5586SChristian Eggers #define AS73211_SCALE_TEMP_MICRO   50000
136403e5586SChristian Eggers 
137403e5586SChristian Eggers #define AS73211_SCALE_X 277071108  /* nW/m^2 */
138403e5586SChristian Eggers #define AS73211_SCALE_Y 298384270  /* nW/m^2 */
139403e5586SChristian Eggers #define AS73211_SCALE_Z 160241927  /* nW/m^2 */
140403e5586SChristian Eggers 
141403e5586SChristian Eggers /* Channel order MUST match devices result register order */
142403e5586SChristian Eggers #define AS73211_SCAN_INDEX_TEMP 0
143403e5586SChristian Eggers #define AS73211_SCAN_INDEX_X    1
144403e5586SChristian Eggers #define AS73211_SCAN_INDEX_Y    2
145403e5586SChristian Eggers #define AS73211_SCAN_INDEX_Z    3
146403e5586SChristian Eggers #define AS73211_SCAN_INDEX_TS   4
147403e5586SChristian Eggers 
148403e5586SChristian Eggers #define AS73211_SCAN_MASK_COLOR ( \
149403e5586SChristian Eggers 	BIT(AS73211_SCAN_INDEX_X) |   \
150403e5586SChristian Eggers 	BIT(AS73211_SCAN_INDEX_Y) |   \
151403e5586SChristian Eggers 	BIT(AS73211_SCAN_INDEX_Z))
152403e5586SChristian Eggers 
153403e5586SChristian Eggers #define AS73211_SCAN_MASK_ALL (    \
154403e5586SChristian Eggers 	BIT(AS73211_SCAN_INDEX_TEMP) | \
155403e5586SChristian Eggers 	AS73211_SCAN_MASK_COLOR)
156403e5586SChristian Eggers 
157403e5586SChristian Eggers static const struct iio_chan_spec as73211_channels[] = {
158403e5586SChristian Eggers 	{
159403e5586SChristian Eggers 		.type = IIO_TEMP,
160403e5586SChristian Eggers 		.info_mask_separate =
161403e5586SChristian Eggers 			BIT(IIO_CHAN_INFO_RAW) |
162403e5586SChristian Eggers 			BIT(IIO_CHAN_INFO_OFFSET) |
163403e5586SChristian Eggers 			BIT(IIO_CHAN_INFO_SCALE),
164403e5586SChristian Eggers 		.address = AS73211_OUT_TEMP,
165403e5586SChristian Eggers 		.scan_index = AS73211_SCAN_INDEX_TEMP,
166403e5586SChristian Eggers 		.scan_type = {
167403e5586SChristian Eggers 			.sign = 'u',
168403e5586SChristian Eggers 			.realbits = 16,
169403e5586SChristian Eggers 			.storagebits = 16,
170403e5586SChristian Eggers 			.endianness = IIO_LE,
171403e5586SChristian Eggers 		}
172403e5586SChristian Eggers 	},
173403e5586SChristian Eggers 	AS73211_COLOR_CHANNEL(X, AS73211_SCAN_INDEX_X, AS73211_OUT_MRES1),
174403e5586SChristian Eggers 	AS73211_COLOR_CHANNEL(Y, AS73211_SCAN_INDEX_Y, AS73211_OUT_MRES2),
175403e5586SChristian Eggers 	AS73211_COLOR_CHANNEL(Z, AS73211_SCAN_INDEX_Z, AS73211_OUT_MRES3),
176403e5586SChristian Eggers 	IIO_CHAN_SOFT_TIMESTAMP(AS73211_SCAN_INDEX_TS),
177403e5586SChristian Eggers };
178403e5586SChristian Eggers 
as73211_integration_time_1024cyc(struct as73211_data * data)179403e5586SChristian Eggers static unsigned int as73211_integration_time_1024cyc(struct as73211_data *data)
180403e5586SChristian Eggers {
181403e5586SChristian Eggers 	/*
182403e5586SChristian Eggers 	 * Return integration time in units of 1024 clock cycles. Integration time
183403e5586SChristian Eggers 	 * in CREG1 is in powers of 2 (x 1024 cycles).
184403e5586SChristian Eggers 	 */
185403e5586SChristian Eggers 	return BIT(FIELD_GET(AS73211_CREG1_TIME_MASK, data->creg1));
186403e5586SChristian Eggers }
187403e5586SChristian Eggers 
as73211_integration_time_us(struct as73211_data * data,unsigned int integration_time_1024cyc)188403e5586SChristian Eggers static unsigned int as73211_integration_time_us(struct as73211_data *data,
189403e5586SChristian Eggers 						 unsigned int integration_time_1024cyc)
190403e5586SChristian Eggers {
191403e5586SChristian Eggers 	/*
192403e5586SChristian Eggers 	 * f_samp is configured in CREG3 in powers of 2 (x 1.024 MHz)
193403e5586SChristian Eggers 	 * t_cycl is configured in CREG1 in powers of 2 (x 1024 cycles)
194403e5586SChristian Eggers 	 * t_int_us = 1 / (f_samp) * t_cycl * US_PER_SEC
195403e5586SChristian Eggers 	 *          = 1 / (2^CREG3_CCLK * 1,024,000) * 2^CREG1_CYCLES * 1,024 * US_PER_SEC
196403e5586SChristian Eggers 	 *          = 2^(-CREG3_CCLK) * 2^CREG1_CYCLES * 1,000
197403e5586SChristian Eggers 	 * In order to get rid of negative exponents, we extend the "fraction"
198403e5586SChristian Eggers 	 * by 2^3 (CREG3_CCLK,max = 3)
199403e5586SChristian Eggers 	 * t_int_us = 2^(3-CREG3_CCLK) * 2^CREG1_CYCLES * 125
200403e5586SChristian Eggers 	 */
201403e5586SChristian Eggers 	return BIT(3 - FIELD_GET(AS73211_CREG3_CCLK_MASK, data->creg3)) *
202403e5586SChristian Eggers 		integration_time_1024cyc * 125;
203403e5586SChristian Eggers }
204403e5586SChristian Eggers 
as73211_integration_time_calc_avail(struct as73211_data * data)205403e5586SChristian Eggers static void as73211_integration_time_calc_avail(struct as73211_data *data)
206403e5586SChristian Eggers {
207403e5586SChristian Eggers 	int i;
208403e5586SChristian Eggers 
209403e5586SChristian Eggers 	for (i = 0; i < ARRAY_SIZE(data->int_time_avail) / 2; i++) {
210403e5586SChristian Eggers 		unsigned int time_us = as73211_integration_time_us(data, BIT(i));
211403e5586SChristian Eggers 
212403e5586SChristian Eggers 		data->int_time_avail[i * 2 + 0] = time_us / USEC_PER_SEC;
213403e5586SChristian Eggers 		data->int_time_avail[i * 2 + 1] = time_us % USEC_PER_SEC;
214403e5586SChristian Eggers 	}
215403e5586SChristian Eggers }
216403e5586SChristian Eggers 
as73211_gain(struct as73211_data * data)217403e5586SChristian Eggers static unsigned int as73211_gain(struct as73211_data *data)
218403e5586SChristian Eggers {
219403e5586SChristian Eggers 	/* gain can be calculated from CREG1 as 2^(11 - CREG1_GAIN) */
220403e5586SChristian Eggers 	return BIT(AS73211_CREG1_GAIN_1 - FIELD_GET(AS73211_CREG1_GAIN_MASK, data->creg1));
221403e5586SChristian Eggers }
222403e5586SChristian Eggers 
223403e5586SChristian Eggers /* must be called with as73211_data::mutex held. */
as73211_req_data(struct as73211_data * data)224403e5586SChristian Eggers static int as73211_req_data(struct as73211_data *data)
225403e5586SChristian Eggers {
226403e5586SChristian Eggers 	unsigned int time_us = as73211_integration_time_us(data,
227403e5586SChristian Eggers 							    as73211_integration_time_1024cyc(data));
228403e5586SChristian Eggers 	struct device *dev = &data->client->dev;
229403e5586SChristian Eggers 	union i2c_smbus_data smbus_data;
230403e5586SChristian Eggers 	u16 osr_status;
231403e5586SChristian Eggers 	int ret;
232403e5586SChristian Eggers 
233403e5586SChristian Eggers 	if (data->client->irq)
234403e5586SChristian Eggers 		reinit_completion(&data->completion);
235403e5586SChristian Eggers 
236403e5586SChristian Eggers 	/*
237403e5586SChristian Eggers 	 * During measurement, there should be no traffic on the i2c bus as the
238403e5586SChristian Eggers 	 * electrical noise would disturb the measurement process.
239403e5586SChristian Eggers 	 */
240403e5586SChristian Eggers 	i2c_lock_bus(data->client->adapter, I2C_LOCK_SEGMENT);
241403e5586SChristian Eggers 
242403e5586SChristian Eggers 	data->osr &= ~AS73211_OSR_DOS_MASK;
243403e5586SChristian Eggers 	data->osr |= AS73211_OSR_DOS_MEASURE | AS73211_OSR_SS;
244403e5586SChristian Eggers 
245403e5586SChristian Eggers 	smbus_data.byte = data->osr;
246403e5586SChristian Eggers 	ret = __i2c_smbus_xfer(data->client->adapter, data->client->addr,
247403e5586SChristian Eggers 			data->client->flags, I2C_SMBUS_WRITE,
248403e5586SChristian Eggers 			AS73211_REG_OSR, I2C_SMBUS_BYTE_DATA, &smbus_data);
249403e5586SChristian Eggers 	if (ret < 0) {
250403e5586SChristian Eggers 		i2c_unlock_bus(data->client->adapter, I2C_LOCK_SEGMENT);
251403e5586SChristian Eggers 		return ret;
252403e5586SChristian Eggers 	}
253403e5586SChristian Eggers 
254403e5586SChristian Eggers 	/*
255403e5586SChristian Eggers 	 * Reset AS73211_OSR_SS (is self clearing) in order to avoid unintentional
256403e5586SChristian Eggers 	 * triggering of further measurements later.
257403e5586SChristian Eggers 	 */
258403e5586SChristian Eggers 	data->osr &= ~AS73211_OSR_SS;
259403e5586SChristian Eggers 
260403e5586SChristian Eggers 	/*
26123e0618cSChristian Eggers 	 * Add 33% extra margin for the timeout. fclk,min = fclk,typ - 27%.
262403e5586SChristian Eggers 	 */
26323e0618cSChristian Eggers 	time_us += time_us / 3;
264403e5586SChristian Eggers 	if (data->client->irq) {
265403e5586SChristian Eggers 		ret = wait_for_completion_timeout(&data->completion, usecs_to_jiffies(time_us));
266403e5586SChristian Eggers 		if (!ret) {
267403e5586SChristian Eggers 			dev_err(dev, "timeout waiting for READY IRQ\n");
268403e5586SChristian Eggers 			i2c_unlock_bus(data->client->adapter, I2C_LOCK_SEGMENT);
269403e5586SChristian Eggers 			return -ETIMEDOUT;
270403e5586SChristian Eggers 		}
271403e5586SChristian Eggers 	} else {
272403e5586SChristian Eggers 		/* Wait integration time */
273403e5586SChristian Eggers 		usleep_range(time_us, 2 * time_us);
274403e5586SChristian Eggers 	}
275403e5586SChristian Eggers 
276403e5586SChristian Eggers 	i2c_unlock_bus(data->client->adapter, I2C_LOCK_SEGMENT);
277403e5586SChristian Eggers 
278403e5586SChristian Eggers 	ret = i2c_smbus_read_word_data(data->client, AS73211_OUT_OSR_STATUS);
279403e5586SChristian Eggers 	if (ret < 0)
280403e5586SChristian Eggers 		return ret;
281403e5586SChristian Eggers 
282403e5586SChristian Eggers 	osr_status = ret;
283403e5586SChristian Eggers 	if (osr_status != (AS73211_OSR_DOS_MEASURE | AS73211_OSR_STATUS_NDATA)) {
284403e5586SChristian Eggers 		if (osr_status & AS73211_OSR_SS) {
285403e5586SChristian Eggers 			dev_err(dev, "%s() Measurement has not stopped\n", __func__);
286403e5586SChristian Eggers 			return -ETIME;
287403e5586SChristian Eggers 		}
288403e5586SChristian Eggers 		if (osr_status & AS73211_OSR_STATUS_NOTREADY) {
289403e5586SChristian Eggers 			dev_err(dev, "%s() Data is not ready\n", __func__);
290403e5586SChristian Eggers 			return -ENODATA;
291403e5586SChristian Eggers 		}
292403e5586SChristian Eggers 		if (!(osr_status & AS73211_OSR_STATUS_NDATA)) {
293403e5586SChristian Eggers 			dev_err(dev, "%s() No new data available\n", __func__);
294403e5586SChristian Eggers 			return -ENODATA;
295403e5586SChristian Eggers 		}
296403e5586SChristian Eggers 		if (osr_status & AS73211_OSR_STATUS_LDATA) {
297403e5586SChristian Eggers 			dev_err(dev, "%s() Result buffer overrun\n", __func__);
298403e5586SChristian Eggers 			return -ENOBUFS;
299403e5586SChristian Eggers 		}
300403e5586SChristian Eggers 		if (osr_status & AS73211_OSR_STATUS_ADCOF) {
301403e5586SChristian Eggers 			dev_err(dev, "%s() ADC overflow\n", __func__);
302403e5586SChristian Eggers 			return -EOVERFLOW;
303403e5586SChristian Eggers 		}
304403e5586SChristian Eggers 		if (osr_status & AS73211_OSR_STATUS_MRESOF) {
305403e5586SChristian Eggers 			dev_err(dev, "%s() Measurement result overflow\n", __func__);
306403e5586SChristian Eggers 			return -EOVERFLOW;
307403e5586SChristian Eggers 		}
308403e5586SChristian Eggers 		if (osr_status & AS73211_OSR_STATUS_OUTCONVOF) {
309403e5586SChristian Eggers 			dev_err(dev, "%s() Timer overflow\n", __func__);
310403e5586SChristian Eggers 			return -EOVERFLOW;
311403e5586SChristian Eggers 		}
312403e5586SChristian Eggers 		dev_err(dev, "%s() Unexpected status value\n", __func__);
313403e5586SChristian Eggers 		return -EIO;
314403e5586SChristian Eggers 	}
315403e5586SChristian Eggers 
316403e5586SChristian Eggers 	return 0;
317403e5586SChristian Eggers }
318403e5586SChristian Eggers 
as73211_read_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,int * val,int * val2,long mask)319403e5586SChristian Eggers static int as73211_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan,
320403e5586SChristian Eggers 			     int *val, int *val2, long mask)
321403e5586SChristian Eggers {
322403e5586SChristian Eggers 	struct as73211_data *data = iio_priv(indio_dev);
323403e5586SChristian Eggers 
324403e5586SChristian Eggers 	switch (mask) {
325403e5586SChristian Eggers 	case IIO_CHAN_INFO_RAW: {
326403e5586SChristian Eggers 		int ret;
327403e5586SChristian Eggers 
328403e5586SChristian Eggers 		ret = iio_device_claim_direct_mode(indio_dev);
329403e5586SChristian Eggers 		if (ret < 0)
330403e5586SChristian Eggers 			return ret;
331403e5586SChristian Eggers 
332403e5586SChristian Eggers 		ret = as73211_req_data(data);
333403e5586SChristian Eggers 		if (ret < 0) {
334403e5586SChristian Eggers 			iio_device_release_direct_mode(indio_dev);
335403e5586SChristian Eggers 			return ret;
336403e5586SChristian Eggers 		}
337403e5586SChristian Eggers 
338403e5586SChristian Eggers 		ret = i2c_smbus_read_word_data(data->client, chan->address);
339403e5586SChristian Eggers 		iio_device_release_direct_mode(indio_dev);
340403e5586SChristian Eggers 		if (ret < 0)
341403e5586SChristian Eggers 			return ret;
342403e5586SChristian Eggers 
343403e5586SChristian Eggers 		*val = ret;
344403e5586SChristian Eggers 		return IIO_VAL_INT;
345403e5586SChristian Eggers 	}
346403e5586SChristian Eggers 	case IIO_CHAN_INFO_OFFSET:
347403e5586SChristian Eggers 		*val = AS73211_OFFSET_TEMP_INT;
348403e5586SChristian Eggers 		*val2 = AS73211_OFFSET_TEMP_MICRO;
349403e5586SChristian Eggers 		return IIO_VAL_INT_PLUS_MICRO;
350403e5586SChristian Eggers 
351403e5586SChristian Eggers 	case IIO_CHAN_INFO_SCALE:
352403e5586SChristian Eggers 		switch (chan->type) {
353403e5586SChristian Eggers 		case IIO_TEMP:
354403e5586SChristian Eggers 			*val = AS73211_SCALE_TEMP_INT;
355403e5586SChristian Eggers 			*val2 = AS73211_SCALE_TEMP_MICRO;
356403e5586SChristian Eggers 			return IIO_VAL_INT_PLUS_MICRO;
357403e5586SChristian Eggers 
358403e5586SChristian Eggers 		case IIO_INTENSITY: {
359403e5586SChristian Eggers 			unsigned int scale;
360403e5586SChristian Eggers 
361403e5586SChristian Eggers 			switch (chan->channel2) {
362403e5586SChristian Eggers 			case IIO_MOD_X:
363403e5586SChristian Eggers 				scale = AS73211_SCALE_X;
364403e5586SChristian Eggers 				break;
365403e5586SChristian Eggers 			case IIO_MOD_Y:
366403e5586SChristian Eggers 				scale = AS73211_SCALE_Y;
367403e5586SChristian Eggers 				break;
368403e5586SChristian Eggers 			case IIO_MOD_Z:
369403e5586SChristian Eggers 				scale = AS73211_SCALE_Z;
370403e5586SChristian Eggers 				break;
371403e5586SChristian Eggers 			default:
372403e5586SChristian Eggers 				return -EINVAL;
373403e5586SChristian Eggers 			}
374403e5586SChristian Eggers 			scale /= as73211_gain(data);
375403e5586SChristian Eggers 			scale /= as73211_integration_time_1024cyc(data);
376403e5586SChristian Eggers 			*val = scale;
377403e5586SChristian Eggers 			return IIO_VAL_INT;
378403e5586SChristian Eggers 
379403e5586SChristian Eggers 		default:
380403e5586SChristian Eggers 			return -EINVAL;
381403e5586SChristian Eggers 		}}
382403e5586SChristian Eggers 
383403e5586SChristian Eggers 	case IIO_CHAN_INFO_SAMP_FREQ:
384403e5586SChristian Eggers 		/* f_samp is configured in CREG3 in powers of 2 (x 1.024 MHz) */
385403e5586SChristian Eggers 		*val = BIT(FIELD_GET(AS73211_CREG3_CCLK_MASK, data->creg3)) *
386403e5586SChristian Eggers 			AS73211_SAMPLE_FREQ_BASE;
387403e5586SChristian Eggers 		return IIO_VAL_INT;
388403e5586SChristian Eggers 
389403e5586SChristian Eggers 	case IIO_CHAN_INFO_HARDWAREGAIN:
390403e5586SChristian Eggers 		*val = as73211_gain(data);
391403e5586SChristian Eggers 		return IIO_VAL_INT;
392403e5586SChristian Eggers 
393403e5586SChristian Eggers 	case IIO_CHAN_INFO_INT_TIME: {
394403e5586SChristian Eggers 		unsigned int time_us;
395403e5586SChristian Eggers 
396403e5586SChristian Eggers 		mutex_lock(&data->mutex);
397403e5586SChristian Eggers 		time_us = as73211_integration_time_us(data, as73211_integration_time_1024cyc(data));
398403e5586SChristian Eggers 		mutex_unlock(&data->mutex);
399403e5586SChristian Eggers 		*val = time_us / USEC_PER_SEC;
400403e5586SChristian Eggers 		*val2 = time_us % USEC_PER_SEC;
401403e5586SChristian Eggers 		return IIO_VAL_INT_PLUS_MICRO;
402403e5586SChristian Eggers 
403403e5586SChristian Eggers 	default:
404403e5586SChristian Eggers 		return -EINVAL;
405403e5586SChristian Eggers 	}}
406403e5586SChristian Eggers }
407403e5586SChristian Eggers 
as73211_read_avail(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,const int ** vals,int * type,int * length,long mask)408403e5586SChristian Eggers static int as73211_read_avail(struct iio_dev *indio_dev, struct iio_chan_spec const *chan,
409403e5586SChristian Eggers 			       const int **vals, int *type, int *length, long mask)
410403e5586SChristian Eggers {
411403e5586SChristian Eggers 	struct as73211_data *data = iio_priv(indio_dev);
412403e5586SChristian Eggers 
413403e5586SChristian Eggers 	switch (mask) {
414403e5586SChristian Eggers 	case IIO_CHAN_INFO_SAMP_FREQ:
415403e5586SChristian Eggers 		*length = ARRAY_SIZE(as73211_samp_freq_avail);
416403e5586SChristian Eggers 		*vals = as73211_samp_freq_avail;
417403e5586SChristian Eggers 		*type = IIO_VAL_INT;
418403e5586SChristian Eggers 		return IIO_AVAIL_LIST;
419403e5586SChristian Eggers 
420403e5586SChristian Eggers 	case IIO_CHAN_INFO_HARDWAREGAIN:
421403e5586SChristian Eggers 		*length = ARRAY_SIZE(as73211_hardwaregain_avail);
422403e5586SChristian Eggers 		*vals = as73211_hardwaregain_avail;
423403e5586SChristian Eggers 		*type = IIO_VAL_INT;
424403e5586SChristian Eggers 		return IIO_AVAIL_LIST;
425403e5586SChristian Eggers 
426403e5586SChristian Eggers 	case IIO_CHAN_INFO_INT_TIME:
427403e5586SChristian Eggers 		*length = ARRAY_SIZE(data->int_time_avail);
428403e5586SChristian Eggers 		*vals = data->int_time_avail;
429403e5586SChristian Eggers 		*type = IIO_VAL_INT_PLUS_MICRO;
430403e5586SChristian Eggers 		return IIO_AVAIL_LIST;
431403e5586SChristian Eggers 
432403e5586SChristian Eggers 	default:
433403e5586SChristian Eggers 		return -EINVAL;
434403e5586SChristian Eggers 	}
435403e5586SChristian Eggers }
436403e5586SChristian Eggers 
_as73211_write_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * chan __always_unused,int val,int val2,long mask)437403e5586SChristian Eggers static int _as73211_write_raw(struct iio_dev *indio_dev,
438403e5586SChristian Eggers 			       struct iio_chan_spec const *chan __always_unused,
439403e5586SChristian Eggers 			       int val, int val2, long mask)
440403e5586SChristian Eggers {
441403e5586SChristian Eggers 	struct as73211_data *data = iio_priv(indio_dev);
442403e5586SChristian Eggers 	int ret;
443403e5586SChristian Eggers 
444403e5586SChristian Eggers 	switch (mask) {
445403e5586SChristian Eggers 	case IIO_CHAN_INFO_SAMP_FREQ: {
446403e5586SChristian Eggers 		int reg_bits, freq_kHz = val / HZ_PER_KHZ;  /* 1024, 2048, ... */
447403e5586SChristian Eggers 
448403e5586SChristian Eggers 		/* val must be 1024 * 2^x */
449403e5586SChristian Eggers 		if (val < 0 || (freq_kHz * HZ_PER_KHZ) != val ||
450403e5586SChristian Eggers 				!is_power_of_2(freq_kHz) || val2)
451403e5586SChristian Eggers 			return -EINVAL;
452403e5586SChristian Eggers 
453403e5586SChristian Eggers 		/* f_samp is configured in CREG3 in powers of 2 (x 1.024 MHz (=2^10)) */
454403e5586SChristian Eggers 		reg_bits = ilog2(freq_kHz) - 10;
455403e5586SChristian Eggers 		if (!FIELD_FIT(AS73211_CREG3_CCLK_MASK, reg_bits))
456403e5586SChristian Eggers 			return -EINVAL;
457403e5586SChristian Eggers 
458403e5586SChristian Eggers 		data->creg3 &= ~AS73211_CREG3_CCLK_MASK;
459403e5586SChristian Eggers 		data->creg3 |= FIELD_PREP(AS73211_CREG3_CCLK_MASK, reg_bits);
460403e5586SChristian Eggers 		as73211_integration_time_calc_avail(data);
461403e5586SChristian Eggers 
462403e5586SChristian Eggers 		ret = i2c_smbus_write_byte_data(data->client, AS73211_REG_CREG3, data->creg3);
463403e5586SChristian Eggers 		if (ret < 0)
464403e5586SChristian Eggers 			return ret;
465403e5586SChristian Eggers 
466403e5586SChristian Eggers 		return 0;
467403e5586SChristian Eggers 	}
468403e5586SChristian Eggers 	case IIO_CHAN_INFO_HARDWAREGAIN: {
469403e5586SChristian Eggers 		unsigned int reg_bits;
470403e5586SChristian Eggers 
471403e5586SChristian Eggers 		if (val < 0 || !is_power_of_2(val) || val2)
472403e5586SChristian Eggers 			return -EINVAL;
473403e5586SChristian Eggers 
474403e5586SChristian Eggers 		/* gain can be calculated from CREG1 as 2^(11 - CREG1_GAIN) */
475403e5586SChristian Eggers 		reg_bits = AS73211_CREG1_GAIN_1 - ilog2(val);
476403e5586SChristian Eggers 		if (!FIELD_FIT(AS73211_CREG1_GAIN_MASK, reg_bits))
477403e5586SChristian Eggers 			return -EINVAL;
478403e5586SChristian Eggers 
479403e5586SChristian Eggers 		data->creg1 &= ~AS73211_CREG1_GAIN_MASK;
480403e5586SChristian Eggers 		data->creg1 |= FIELD_PREP(AS73211_CREG1_GAIN_MASK, reg_bits);
481403e5586SChristian Eggers 
482403e5586SChristian Eggers 		ret = i2c_smbus_write_byte_data(data->client, AS73211_REG_CREG1, data->creg1);
483403e5586SChristian Eggers 		if (ret < 0)
484403e5586SChristian Eggers 			return ret;
485403e5586SChristian Eggers 
486403e5586SChristian Eggers 		return 0;
487403e5586SChristian Eggers 	}
488403e5586SChristian Eggers 	case IIO_CHAN_INFO_INT_TIME: {
489403e5586SChristian Eggers 		int val_us = val * USEC_PER_SEC + val2;
490403e5586SChristian Eggers 		int time_ms;
491403e5586SChristian Eggers 		int reg_bits;
492403e5586SChristian Eggers 
493403e5586SChristian Eggers 		/* f_samp is configured in CREG3 in powers of 2 (x 1.024 MHz) */
494403e5586SChristian Eggers 		int f_samp_1_024mhz = BIT(FIELD_GET(AS73211_CREG3_CCLK_MASK, data->creg3));
495403e5586SChristian Eggers 
496403e5586SChristian Eggers 		/*
497403e5586SChristian Eggers 		 * time_ms = time_us * US_PER_MS * f_samp_1_024mhz / MHZ_PER_HZ
498403e5586SChristian Eggers 		 *         = time_us * f_samp_1_024mhz / 1000
499403e5586SChristian Eggers 		 */
500403e5586SChristian Eggers 		time_ms = (val_us * f_samp_1_024mhz) / 1000;  /* 1 ms, 2 ms, ... (power of two) */
501403e5586SChristian Eggers 		if (time_ms < 0 || !is_power_of_2(time_ms) || time_ms > AS73211_SAMPLE_TIME_MAX_MS)
502403e5586SChristian Eggers 			return -EINVAL;
503403e5586SChristian Eggers 
504403e5586SChristian Eggers 		reg_bits = ilog2(time_ms);
505403e5586SChristian Eggers 		if (!FIELD_FIT(AS73211_CREG1_TIME_MASK, reg_bits))
506403e5586SChristian Eggers 			return -EINVAL;  /* not possible due to previous tests */
507403e5586SChristian Eggers 
508403e5586SChristian Eggers 		data->creg1 &= ~AS73211_CREG1_TIME_MASK;
509403e5586SChristian Eggers 		data->creg1 |= FIELD_PREP(AS73211_CREG1_TIME_MASK, reg_bits);
510403e5586SChristian Eggers 
511403e5586SChristian Eggers 		ret = i2c_smbus_write_byte_data(data->client, AS73211_REG_CREG1, data->creg1);
512403e5586SChristian Eggers 		if (ret < 0)
513403e5586SChristian Eggers 			return ret;
514403e5586SChristian Eggers 
515403e5586SChristian Eggers 		return 0;
516403e5586SChristian Eggers 
517403e5586SChristian Eggers 	default:
518403e5586SChristian Eggers 		return -EINVAL;
519403e5586SChristian Eggers 	}}
520403e5586SChristian Eggers }
521403e5586SChristian Eggers 
as73211_write_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,int val,int val2,long mask)522403e5586SChristian Eggers static int as73211_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan,
523403e5586SChristian Eggers 			      int val, int val2, long mask)
524403e5586SChristian Eggers {
525403e5586SChristian Eggers 	struct as73211_data *data = iio_priv(indio_dev);
526403e5586SChristian Eggers 	int ret;
527403e5586SChristian Eggers 
528403e5586SChristian Eggers 	mutex_lock(&data->mutex);
529403e5586SChristian Eggers 
530403e5586SChristian Eggers 	ret = iio_device_claim_direct_mode(indio_dev);
531403e5586SChristian Eggers 	if (ret < 0)
532403e5586SChristian Eggers 		goto error_unlock;
533403e5586SChristian Eggers 
534403e5586SChristian Eggers 	/* Need to switch to config mode ... */
535403e5586SChristian Eggers 	if ((data->osr & AS73211_OSR_DOS_MASK) != AS73211_OSR_DOS_CONFIG) {
536403e5586SChristian Eggers 		data->osr &= ~AS73211_OSR_DOS_MASK;
537403e5586SChristian Eggers 		data->osr |= AS73211_OSR_DOS_CONFIG;
538403e5586SChristian Eggers 
539403e5586SChristian Eggers 		ret = i2c_smbus_write_byte_data(data->client, AS73211_REG_OSR, data->osr);
540403e5586SChristian Eggers 		if (ret < 0)
541403e5586SChristian Eggers 			goto error_release;
542403e5586SChristian Eggers 	}
543403e5586SChristian Eggers 
544403e5586SChristian Eggers 	ret = _as73211_write_raw(indio_dev, chan, val, val2, mask);
545403e5586SChristian Eggers 
546403e5586SChristian Eggers error_release:
547403e5586SChristian Eggers 	iio_device_release_direct_mode(indio_dev);
548403e5586SChristian Eggers error_unlock:
549403e5586SChristian Eggers 	mutex_unlock(&data->mutex);
550403e5586SChristian Eggers 	return ret;
551403e5586SChristian Eggers }
552403e5586SChristian Eggers 
as73211_ready_handler(int irq __always_unused,void * priv)553403e5586SChristian Eggers static irqreturn_t as73211_ready_handler(int irq __always_unused, void *priv)
554403e5586SChristian Eggers {
555403e5586SChristian Eggers 	struct as73211_data *data = iio_priv(priv);
556403e5586SChristian Eggers 
557403e5586SChristian Eggers 	complete(&data->completion);
558403e5586SChristian Eggers 
559403e5586SChristian Eggers 	return IRQ_HANDLED;
560403e5586SChristian Eggers }
561403e5586SChristian Eggers 
as73211_trigger_handler(int irq __always_unused,void * p)562403e5586SChristian Eggers static irqreturn_t as73211_trigger_handler(int irq __always_unused, void *p)
563403e5586SChristian Eggers {
564403e5586SChristian Eggers 	struct iio_poll_func *pf = p;
565403e5586SChristian Eggers 	struct iio_dev *indio_dev = pf->indio_dev;
566403e5586SChristian Eggers 	struct as73211_data *data = iio_priv(indio_dev);
567403e5586SChristian Eggers 	struct {
568403e5586SChristian Eggers 		__le16 chan[4];
569403e5586SChristian Eggers 		s64 ts __aligned(8);
570403e5586SChristian Eggers 	} scan;
571403e5586SChristian Eggers 	int data_result, ret;
572403e5586SChristian Eggers 
573403e5586SChristian Eggers 	mutex_lock(&data->mutex);
574403e5586SChristian Eggers 
575403e5586SChristian Eggers 	data_result = as73211_req_data(data);
576403e5586SChristian Eggers 	if (data_result < 0 && data_result != -EOVERFLOW)
577403e5586SChristian Eggers 		goto done;  /* don't push any data for errors other than EOVERFLOW */
578403e5586SChristian Eggers 
579403e5586SChristian Eggers 	if (*indio_dev->active_scan_mask == AS73211_SCAN_MASK_ALL) {
580403e5586SChristian Eggers 		/* Optimization for reading all (color + temperature) channels */
581403e5586SChristian Eggers 		u8 addr = as73211_channels[0].address;
582403e5586SChristian Eggers 		struct i2c_msg msgs[] = {
583403e5586SChristian Eggers 			{
584403e5586SChristian Eggers 				.addr = data->client->addr,
585403e5586SChristian Eggers 				.flags = 0,
586403e5586SChristian Eggers 				.len = 1,
587403e5586SChristian Eggers 				.buf = &addr,
588403e5586SChristian Eggers 			},
589403e5586SChristian Eggers 			{
590403e5586SChristian Eggers 				.addr = data->client->addr,
591403e5586SChristian Eggers 				.flags = I2C_M_RD,
592403e5586SChristian Eggers 				.len = sizeof(scan.chan),
593403e5586SChristian Eggers 				.buf = (u8 *)&scan.chan,
594403e5586SChristian Eggers 			},
595403e5586SChristian Eggers 		};
596403e5586SChristian Eggers 
597403e5586SChristian Eggers 		ret = i2c_transfer(data->client->adapter, msgs, ARRAY_SIZE(msgs));
598403e5586SChristian Eggers 		if (ret < 0)
599403e5586SChristian Eggers 			goto done;
600403e5586SChristian Eggers 	} else {
601403e5586SChristian Eggers 		/* Optimization for reading only color channels */
602403e5586SChristian Eggers 
603403e5586SChristian Eggers 		/* AS73211 starts reading at address 2 */
604403e5586SChristian Eggers 		ret = i2c_master_recv(data->client,
605403e5586SChristian Eggers 				(char *)&scan.chan[1], 3 * sizeof(scan.chan[1]));
606403e5586SChristian Eggers 		if (ret < 0)
607403e5586SChristian Eggers 			goto done;
608403e5586SChristian Eggers 	}
609403e5586SChristian Eggers 
610403e5586SChristian Eggers 	if (data_result) {
611403e5586SChristian Eggers 		/*
612403e5586SChristian Eggers 		 * Saturate all channels (in case of overflows). Temperature channel
613403e5586SChristian Eggers 		 * is not affected by overflows.
614403e5586SChristian Eggers 		 */
615403e5586SChristian Eggers 		scan.chan[1] = cpu_to_le16(U16_MAX);
616403e5586SChristian Eggers 		scan.chan[2] = cpu_to_le16(U16_MAX);
617403e5586SChristian Eggers 		scan.chan[3] = cpu_to_le16(U16_MAX);
618403e5586SChristian Eggers 	}
619403e5586SChristian Eggers 
620403e5586SChristian Eggers 	iio_push_to_buffers_with_timestamp(indio_dev, &scan, iio_get_time_ns(indio_dev));
621403e5586SChristian Eggers 
622403e5586SChristian Eggers done:
623403e5586SChristian Eggers 	mutex_unlock(&data->mutex);
624403e5586SChristian Eggers 	iio_trigger_notify_done(indio_dev->trig);
625403e5586SChristian Eggers 
626403e5586SChristian Eggers 	return IRQ_HANDLED;
627403e5586SChristian Eggers }
628403e5586SChristian Eggers 
629403e5586SChristian Eggers static const struct iio_info as73211_info = {
630403e5586SChristian Eggers 	.read_raw = as73211_read_raw,
631403e5586SChristian Eggers 	.read_avail = as73211_read_avail,
632403e5586SChristian Eggers 	.write_raw = as73211_write_raw,
633403e5586SChristian Eggers };
634403e5586SChristian Eggers 
as73211_power(struct iio_dev * indio_dev,bool state)635403e5586SChristian Eggers static int as73211_power(struct iio_dev *indio_dev, bool state)
636403e5586SChristian Eggers {
637403e5586SChristian Eggers 	struct as73211_data *data = iio_priv(indio_dev);
638403e5586SChristian Eggers 	int ret;
639403e5586SChristian Eggers 
640403e5586SChristian Eggers 	mutex_lock(&data->mutex);
641403e5586SChristian Eggers 
642403e5586SChristian Eggers 	if (state)
643403e5586SChristian Eggers 		data->osr &= ~AS73211_OSR_PD;
644403e5586SChristian Eggers 	else
645403e5586SChristian Eggers 		data->osr |= AS73211_OSR_PD;
646403e5586SChristian Eggers 
647403e5586SChristian Eggers 	ret = i2c_smbus_write_byte_data(data->client, AS73211_REG_OSR, data->osr);
648403e5586SChristian Eggers 
649403e5586SChristian Eggers 	mutex_unlock(&data->mutex);
650403e5586SChristian Eggers 
651403e5586SChristian Eggers 	if (ret < 0)
652403e5586SChristian Eggers 		return ret;
653403e5586SChristian Eggers 
654403e5586SChristian Eggers 	return 0;
655403e5586SChristian Eggers }
656403e5586SChristian Eggers 
as73211_power_disable(void * data)657403e5586SChristian Eggers static void as73211_power_disable(void *data)
658403e5586SChristian Eggers {
659403e5586SChristian Eggers 	struct iio_dev *indio_dev = data;
660403e5586SChristian Eggers 
661403e5586SChristian Eggers 	as73211_power(indio_dev, false);
662403e5586SChristian Eggers }
663403e5586SChristian Eggers 
as73211_probe(struct i2c_client * client)664403e5586SChristian Eggers static int as73211_probe(struct i2c_client *client)
665403e5586SChristian Eggers {
666403e5586SChristian Eggers 	struct device *dev = &client->dev;
667403e5586SChristian Eggers 	struct as73211_data *data;
668403e5586SChristian Eggers 	struct iio_dev *indio_dev;
669403e5586SChristian Eggers 	int ret;
670403e5586SChristian Eggers 
671403e5586SChristian Eggers 	indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
672403e5586SChristian Eggers 	if (!indio_dev)
673403e5586SChristian Eggers 		return -ENOMEM;
674403e5586SChristian Eggers 
675403e5586SChristian Eggers 	data = iio_priv(indio_dev);
676403e5586SChristian Eggers 	i2c_set_clientdata(client, indio_dev);
677403e5586SChristian Eggers 	data->client = client;
678403e5586SChristian Eggers 
679403e5586SChristian Eggers 	mutex_init(&data->mutex);
680403e5586SChristian Eggers 	init_completion(&data->completion);
681403e5586SChristian Eggers 
682403e5586SChristian Eggers 	indio_dev->info = &as73211_info;
683403e5586SChristian Eggers 	indio_dev->name = AS73211_DRV_NAME;
684403e5586SChristian Eggers 	indio_dev->channels = as73211_channels;
685403e5586SChristian Eggers 	indio_dev->num_channels = ARRAY_SIZE(as73211_channels);
686403e5586SChristian Eggers 	indio_dev->modes = INDIO_DIRECT_MODE;
687403e5586SChristian Eggers 
688403e5586SChristian Eggers 	ret = i2c_smbus_read_byte_data(data->client, AS73211_REG_OSR);
689403e5586SChristian Eggers 	if (ret < 0)
690403e5586SChristian Eggers 		return ret;
691403e5586SChristian Eggers 	data->osr = ret;
692403e5586SChristian Eggers 
693403e5586SChristian Eggers 	/* reset device */
694403e5586SChristian Eggers 	data->osr |= AS73211_OSR_SW_RES;
695403e5586SChristian Eggers 	ret = i2c_smbus_write_byte_data(data->client, AS73211_REG_OSR, data->osr);
696403e5586SChristian Eggers 	if (ret < 0)
697403e5586SChristian Eggers 		return ret;
698403e5586SChristian Eggers 
699403e5586SChristian Eggers 	ret = i2c_smbus_read_byte_data(data->client, AS73211_REG_OSR);
700403e5586SChristian Eggers 	if (ret < 0)
701403e5586SChristian Eggers 		return ret;
702403e5586SChristian Eggers 	data->osr = ret;
703403e5586SChristian Eggers 
704403e5586SChristian Eggers 	/*
705403e5586SChristian Eggers 	 * Reading AGEN is only possible after reset (AGEN is not available if
706403e5586SChristian Eggers 	 * device is in measurement mode).
707403e5586SChristian Eggers 	 */
708403e5586SChristian Eggers 	ret = i2c_smbus_read_byte_data(data->client, AS73211_REG_AGEN);
709403e5586SChristian Eggers 	if (ret < 0)
710403e5586SChristian Eggers 		return ret;
711403e5586SChristian Eggers 
712403e5586SChristian Eggers 	/* At the time of writing this driver, only DEVID 2 and MUT 1 are known. */
713403e5586SChristian Eggers 	if ((ret & AS73211_AGEN_DEVID_MASK) != AS73211_AGEN_DEVID(2) ||
714403e5586SChristian Eggers 	    (ret & AS73211_AGEN_MUT_MASK) != AS73211_AGEN_MUT(1))
715403e5586SChristian Eggers 		return -ENODEV;
716403e5586SChristian Eggers 
717403e5586SChristian Eggers 	ret = i2c_smbus_read_byte_data(data->client, AS73211_REG_CREG1);
718403e5586SChristian Eggers 	if (ret < 0)
719403e5586SChristian Eggers 		return ret;
720403e5586SChristian Eggers 	data->creg1 = ret;
721403e5586SChristian Eggers 
722403e5586SChristian Eggers 	ret = i2c_smbus_read_byte_data(data->client, AS73211_REG_CREG2);
723403e5586SChristian Eggers 	if (ret < 0)
724403e5586SChristian Eggers 		return ret;
725403e5586SChristian Eggers 	data->creg2 = ret;
726403e5586SChristian Eggers 
727403e5586SChristian Eggers 	ret = i2c_smbus_read_byte_data(data->client, AS73211_REG_CREG3);
728403e5586SChristian Eggers 	if (ret < 0)
729403e5586SChristian Eggers 		return ret;
730403e5586SChristian Eggers 	data->creg3 = ret;
731403e5586SChristian Eggers 	as73211_integration_time_calc_avail(data);
732403e5586SChristian Eggers 
733403e5586SChristian Eggers 	ret = as73211_power(indio_dev, true);
734403e5586SChristian Eggers 	if (ret < 0)
735403e5586SChristian Eggers 		return ret;
736403e5586SChristian Eggers 
737403e5586SChristian Eggers 	ret = devm_add_action_or_reset(dev, as73211_power_disable, indio_dev);
738403e5586SChristian Eggers 	if (ret)
739403e5586SChristian Eggers 		return ret;
740403e5586SChristian Eggers 
741403e5586SChristian Eggers 	ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL, as73211_trigger_handler, NULL);
742403e5586SChristian Eggers 	if (ret)
743403e5586SChristian Eggers 		return ret;
744403e5586SChristian Eggers 
745403e5586SChristian Eggers 	if (client->irq) {
746403e5586SChristian Eggers 		ret = devm_request_threaded_irq(&client->dev, client->irq,
747403e5586SChristian Eggers 				NULL,
748403e5586SChristian Eggers 				as73211_ready_handler,
749403e5586SChristian Eggers 				IRQF_ONESHOT,
750403e5586SChristian Eggers 				client->name, indio_dev);
751403e5586SChristian Eggers 		if (ret)
752403e5586SChristian Eggers 			return ret;
753403e5586SChristian Eggers 	}
754403e5586SChristian Eggers 
755403e5586SChristian Eggers 	return devm_iio_device_register(dev, indio_dev);
756403e5586SChristian Eggers }
757403e5586SChristian Eggers 
as73211_suspend(struct device * dev)758c422aa41SJonathan Cameron static int as73211_suspend(struct device *dev)
759403e5586SChristian Eggers {
760403e5586SChristian Eggers 	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
761403e5586SChristian Eggers 
762403e5586SChristian Eggers 	return as73211_power(indio_dev, false);
763403e5586SChristian Eggers }
764403e5586SChristian Eggers 
as73211_resume(struct device * dev)765c422aa41SJonathan Cameron static int as73211_resume(struct device *dev)
766403e5586SChristian Eggers {
767403e5586SChristian Eggers 	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
768403e5586SChristian Eggers 
769403e5586SChristian Eggers 	return as73211_power(indio_dev, true);
770403e5586SChristian Eggers }
771403e5586SChristian Eggers 
772c422aa41SJonathan Cameron static DEFINE_SIMPLE_DEV_PM_OPS(as73211_pm_ops, as73211_suspend,
773c422aa41SJonathan Cameron 				as73211_resume);
774403e5586SChristian Eggers 
775403e5586SChristian Eggers static const struct of_device_id as73211_of_match[] = {
776403e5586SChristian Eggers 	{ .compatible = "ams,as73211" },
777403e5586SChristian Eggers 	{ }
778403e5586SChristian Eggers };
779403e5586SChristian Eggers MODULE_DEVICE_TABLE(of, as73211_of_match);
780403e5586SChristian Eggers 
781403e5586SChristian Eggers static const struct i2c_device_id as73211_id[] = {
782403e5586SChristian Eggers 	{ "as73211", 0 },
783403e5586SChristian Eggers 	{ }
784403e5586SChristian Eggers };
785403e5586SChristian Eggers MODULE_DEVICE_TABLE(i2c, as73211_id);
786403e5586SChristian Eggers 
787403e5586SChristian Eggers static struct i2c_driver as73211_driver = {
788403e5586SChristian Eggers 	.driver = {
789403e5586SChristian Eggers 		.name           = AS73211_DRV_NAME,
790403e5586SChristian Eggers 		.of_match_table = as73211_of_match,
791c422aa41SJonathan Cameron 		.pm             = pm_sleep_ptr(&as73211_pm_ops),
792403e5586SChristian Eggers 	},
793*7cf15f42SUwe Kleine-König 	.probe      = as73211_probe,
794403e5586SChristian Eggers 	.id_table   = as73211_id,
795403e5586SChristian Eggers };
796403e5586SChristian Eggers module_i2c_driver(as73211_driver);
797403e5586SChristian Eggers 
798403e5586SChristian Eggers MODULE_AUTHOR("Christian Eggers <ceggers@arri.de>");
799403e5586SChristian Eggers MODULE_DESCRIPTION("AS73211 XYZ True Color Sensor driver");
800403e5586SChristian Eggers MODULE_LICENSE("GPL");
801