xref: /openbmc/linux/drivers/iio/cdc/ad7150.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
1646d67b5SJonathan Cameron // SPDX-License-Identifier: GPL-2.0+
2646d67b5SJonathan Cameron /*
3646d67b5SJonathan Cameron  * AD7150 capacitive sensor driver supporting AD7150/1/6
4646d67b5SJonathan Cameron  *
5646d67b5SJonathan Cameron  * Copyright 2010-2011 Analog Devices Inc.
6646d67b5SJonathan Cameron  * Copyright 2021 Jonathan Cameron <Jonathan.Cameron@huawei.com>
7646d67b5SJonathan Cameron  */
8646d67b5SJonathan Cameron 
9646d67b5SJonathan Cameron #include <linux/bitfield.h>
10646d67b5SJonathan Cameron #include <linux/device.h>
11646d67b5SJonathan Cameron #include <linux/interrupt.h>
12646d67b5SJonathan Cameron #include <linux/irq.h>
13646d67b5SJonathan Cameron #include <linux/i2c.h>
14646d67b5SJonathan Cameron #include <linux/kernel.h>
15646d67b5SJonathan Cameron #include <linux/module.h>
16646d67b5SJonathan Cameron #include <linux/mod_devicetable.h>
17646d67b5SJonathan Cameron #include <linux/regulator/consumer.h>
18646d67b5SJonathan Cameron #include <linux/slab.h>
19646d67b5SJonathan Cameron 
20646d67b5SJonathan Cameron #include <linux/iio/iio.h>
21646d67b5SJonathan Cameron #include <linux/iio/sysfs.h>
22646d67b5SJonathan Cameron #include <linux/iio/events.h>
23646d67b5SJonathan Cameron 
24646d67b5SJonathan Cameron #define AD7150_STATUS_REG		0
25646d67b5SJonathan Cameron #define   AD7150_STATUS_OUT1		BIT(3)
26646d67b5SJonathan Cameron #define   AD7150_STATUS_OUT2		BIT(5)
27646d67b5SJonathan Cameron #define AD7150_CH1_DATA_HIGH_REG	1
28646d67b5SJonathan Cameron #define AD7150_CH2_DATA_HIGH_REG	3
29646d67b5SJonathan Cameron #define AD7150_CH1_AVG_HIGH_REG		5
30646d67b5SJonathan Cameron #define AD7150_CH2_AVG_HIGH_REG		7
31646d67b5SJonathan Cameron #define AD7150_CH1_SENSITIVITY_REG	9
32646d67b5SJonathan Cameron #define AD7150_CH1_THR_HOLD_H_REG	9
33646d67b5SJonathan Cameron #define AD7150_CH1_TIMEOUT_REG		10
34646d67b5SJonathan Cameron #define   AD7150_CH_TIMEOUT_RECEDING	GENMASK(3, 0)
35646d67b5SJonathan Cameron #define   AD7150_CH_TIMEOUT_APPROACHING	GENMASK(7, 4)
36646d67b5SJonathan Cameron #define AD7150_CH1_SETUP_REG		11
37646d67b5SJonathan Cameron #define AD7150_CH2_SENSITIVITY_REG	12
38646d67b5SJonathan Cameron #define AD7150_CH2_THR_HOLD_H_REG	12
39646d67b5SJonathan Cameron #define AD7150_CH2_TIMEOUT_REG		13
40646d67b5SJonathan Cameron #define AD7150_CH2_SETUP_REG		14
41646d67b5SJonathan Cameron #define AD7150_CFG_REG			15
42646d67b5SJonathan Cameron #define   AD7150_CFG_FIX		BIT(7)
43646d67b5SJonathan Cameron #define   AD7150_CFG_THRESHTYPE_MSK	GENMASK(6, 5)
44646d67b5SJonathan Cameron #define   AD7150_CFG_TT_NEG		0x0
45646d67b5SJonathan Cameron #define   AD7150_CFG_TT_POS		0x1
46646d67b5SJonathan Cameron #define   AD7150_CFG_TT_IN_WINDOW	0x2
47646d67b5SJonathan Cameron #define   AD7150_CFG_TT_OUT_WINDOW	0x3
48646d67b5SJonathan Cameron #define AD7150_PD_TIMER_REG		16
49646d67b5SJonathan Cameron #define AD7150_CH1_CAPDAC_REG		17
50646d67b5SJonathan Cameron #define AD7150_CH2_CAPDAC_REG		18
51646d67b5SJonathan Cameron #define AD7150_SN3_REG			19
52646d67b5SJonathan Cameron #define AD7150_SN2_REG			20
53646d67b5SJonathan Cameron #define AD7150_SN1_REG			21
54646d67b5SJonathan Cameron #define AD7150_SN0_REG			22
55646d67b5SJonathan Cameron #define AD7150_ID_REG			23
56646d67b5SJonathan Cameron 
57646d67b5SJonathan Cameron enum {
58646d67b5SJonathan Cameron 	AD7150,
59646d67b5SJonathan Cameron 	AD7151,
60646d67b5SJonathan Cameron };
61646d67b5SJonathan Cameron 
62646d67b5SJonathan Cameron /**
63646d67b5SJonathan Cameron  * struct ad7150_chip_info - instance specific chip data
64646d67b5SJonathan Cameron  * @client: i2c client for this device
65646d67b5SJonathan Cameron  * @threshold: thresholds for simple capacitance value events
66646d67b5SJonathan Cameron  * @thresh_sensitivity: threshold for simple capacitance offset
67646d67b5SJonathan Cameron  *	from 'average' value.
68646d67b5SJonathan Cameron  * @thresh_timeout: a timeout, in samples from the moment an
69646d67b5SJonathan Cameron  *	adaptive threshold event occurs to when the average
70646d67b5SJonathan Cameron  *	value jumps to current value.  Note made up of two fields,
71646d67b5SJonathan Cameron  *      3:0 are for timeout receding - applies if below lower threshold
72646d67b5SJonathan Cameron  *      7:4 are for timeout approaching - applies if above upper threshold
73646d67b5SJonathan Cameron  * @state_lock: ensure consistent state of this structure wrt the
74646d67b5SJonathan Cameron  *	hardware.
75646d67b5SJonathan Cameron  * @interrupts: one or two interrupt numbers depending on device type.
76646d67b5SJonathan Cameron  * @int_enabled: is a given interrupt currently enabled.
77646d67b5SJonathan Cameron  * @type: threshold type
78646d67b5SJonathan Cameron  * @dir: threshold direction
79646d67b5SJonathan Cameron  */
80646d67b5SJonathan Cameron struct ad7150_chip_info {
81646d67b5SJonathan Cameron 	struct i2c_client *client;
82646d67b5SJonathan Cameron 	u16 threshold[2][2];
83646d67b5SJonathan Cameron 	u8 thresh_sensitivity[2][2];
84646d67b5SJonathan Cameron 	u8 thresh_timeout[2][2];
85646d67b5SJonathan Cameron 	struct mutex state_lock;
86646d67b5SJonathan Cameron 	int interrupts[2];
87646d67b5SJonathan Cameron 	bool int_enabled[2];
88646d67b5SJonathan Cameron 	enum iio_event_type type;
89646d67b5SJonathan Cameron 	enum iio_event_direction dir;
90646d67b5SJonathan Cameron };
91646d67b5SJonathan Cameron 
92646d67b5SJonathan Cameron static const u8 ad7150_addresses[][6] = {
93646d67b5SJonathan Cameron 	{ AD7150_CH1_DATA_HIGH_REG, AD7150_CH1_AVG_HIGH_REG,
94646d67b5SJonathan Cameron 	  AD7150_CH1_SETUP_REG, AD7150_CH1_THR_HOLD_H_REG,
95646d67b5SJonathan Cameron 	  AD7150_CH1_SENSITIVITY_REG, AD7150_CH1_TIMEOUT_REG },
96646d67b5SJonathan Cameron 	{ AD7150_CH2_DATA_HIGH_REG, AD7150_CH2_AVG_HIGH_REG,
97646d67b5SJonathan Cameron 	  AD7150_CH2_SETUP_REG, AD7150_CH2_THR_HOLD_H_REG,
98646d67b5SJonathan Cameron 	  AD7150_CH2_SENSITIVITY_REG, AD7150_CH2_TIMEOUT_REG },
99646d67b5SJonathan Cameron };
100646d67b5SJonathan Cameron 
ad7150_read_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,int * val,int * val2,long mask)101646d67b5SJonathan Cameron static int ad7150_read_raw(struct iio_dev *indio_dev,
102646d67b5SJonathan Cameron 			   struct iio_chan_spec const *chan,
103646d67b5SJonathan Cameron 			   int *val,
104646d67b5SJonathan Cameron 			   int *val2,
105646d67b5SJonathan Cameron 			   long mask)
106646d67b5SJonathan Cameron {
107646d67b5SJonathan Cameron 	struct ad7150_chip_info *chip = iio_priv(indio_dev);
108646d67b5SJonathan Cameron 	int channel = chan->channel;
109646d67b5SJonathan Cameron 	int ret;
110646d67b5SJonathan Cameron 
111646d67b5SJonathan Cameron 	switch (mask) {
112646d67b5SJonathan Cameron 	case IIO_CHAN_INFO_RAW:
113646d67b5SJonathan Cameron 		ret = i2c_smbus_read_word_swapped(chip->client,
114646d67b5SJonathan Cameron 						  ad7150_addresses[channel][0]);
115646d67b5SJonathan Cameron 		if (ret < 0)
116646d67b5SJonathan Cameron 			return ret;
117646d67b5SJonathan Cameron 		*val = ret >> 4;
118646d67b5SJonathan Cameron 
119646d67b5SJonathan Cameron 		return IIO_VAL_INT;
120646d67b5SJonathan Cameron 	case IIO_CHAN_INFO_AVERAGE_RAW:
121646d67b5SJonathan Cameron 		ret = i2c_smbus_read_word_swapped(chip->client,
122646d67b5SJonathan Cameron 						  ad7150_addresses[channel][1]);
123646d67b5SJonathan Cameron 		if (ret < 0)
124646d67b5SJonathan Cameron 			return ret;
125646d67b5SJonathan Cameron 		*val = ret;
126646d67b5SJonathan Cameron 
127646d67b5SJonathan Cameron 		return IIO_VAL_INT;
128646d67b5SJonathan Cameron 	case IIO_CHAN_INFO_SCALE:
129646d67b5SJonathan Cameron 		/*
130646d67b5SJonathan Cameron 		 * Base units for capacitance are nano farads and the value
131646d67b5SJonathan Cameron 		 * calculated from the datasheet formula is in picofarad
132646d67b5SJonathan Cameron 		 * so multiply by 1000
133646d67b5SJonathan Cameron 		 */
134646d67b5SJonathan Cameron 		*val = 1000;
135646d67b5SJonathan Cameron 		*val2 = 40944 >> 4; /* To match shift in _RAW */
136646d67b5SJonathan Cameron 		return IIO_VAL_FRACTIONAL;
137646d67b5SJonathan Cameron 	case IIO_CHAN_INFO_OFFSET:
138646d67b5SJonathan Cameron 		*val = -(12288 >> 4); /* To match shift in _RAW */
139646d67b5SJonathan Cameron 		return IIO_VAL_INT;
140646d67b5SJonathan Cameron 	case IIO_CHAN_INFO_SAMP_FREQ:
141646d67b5SJonathan Cameron 		/* Strangely same for both 1 and 2 chan parts */
142646d67b5SJonathan Cameron 		*val = 100;
143646d67b5SJonathan Cameron 		return IIO_VAL_INT;
144646d67b5SJonathan Cameron 	default:
145646d67b5SJonathan Cameron 		return -EINVAL;
146646d67b5SJonathan Cameron 	}
147646d67b5SJonathan Cameron }
148646d67b5SJonathan Cameron 
ad7150_read_event_config(struct iio_dev * indio_dev,const struct iio_chan_spec * chan,enum iio_event_type type,enum iio_event_direction dir)149646d67b5SJonathan Cameron static int ad7150_read_event_config(struct iio_dev *indio_dev,
150646d67b5SJonathan Cameron 				    const struct iio_chan_spec *chan,
151646d67b5SJonathan Cameron 				    enum iio_event_type type,
152646d67b5SJonathan Cameron 				    enum iio_event_direction dir)
153646d67b5SJonathan Cameron {
154646d67b5SJonathan Cameron 	struct ad7150_chip_info *chip = iio_priv(indio_dev);
155646d67b5SJonathan Cameron 	u8 threshtype;
156646d67b5SJonathan Cameron 	bool thrfixed;
157646d67b5SJonathan Cameron 	int ret;
158646d67b5SJonathan Cameron 
159646d67b5SJonathan Cameron 	ret = i2c_smbus_read_byte_data(chip->client, AD7150_CFG_REG);
160646d67b5SJonathan Cameron 	if (ret < 0)
161646d67b5SJonathan Cameron 		return ret;
162646d67b5SJonathan Cameron 
163646d67b5SJonathan Cameron 	threshtype = FIELD_GET(AD7150_CFG_THRESHTYPE_MSK, ret);
164646d67b5SJonathan Cameron 
165646d67b5SJonathan Cameron 	/*check if threshold mode is fixed or adaptive*/
166646d67b5SJonathan Cameron 	thrfixed = FIELD_GET(AD7150_CFG_FIX, ret);
167646d67b5SJonathan Cameron 
168646d67b5SJonathan Cameron 	switch (type) {
169646d67b5SJonathan Cameron 	case IIO_EV_TYPE_THRESH_ADAPTIVE:
170646d67b5SJonathan Cameron 		if (dir == IIO_EV_DIR_RISING)
171646d67b5SJonathan Cameron 			return !thrfixed && (threshtype == AD7150_CFG_TT_POS);
172646d67b5SJonathan Cameron 		return !thrfixed && (threshtype == AD7150_CFG_TT_NEG);
173646d67b5SJonathan Cameron 	case IIO_EV_TYPE_THRESH:
174646d67b5SJonathan Cameron 		if (dir == IIO_EV_DIR_RISING)
175646d67b5SJonathan Cameron 			return thrfixed && (threshtype == AD7150_CFG_TT_POS);
176646d67b5SJonathan Cameron 		return thrfixed && (threshtype == AD7150_CFG_TT_NEG);
177646d67b5SJonathan Cameron 	default:
178646d67b5SJonathan Cameron 		break;
179646d67b5SJonathan Cameron 	}
180646d67b5SJonathan Cameron 	return -EINVAL;
181646d67b5SJonathan Cameron }
182646d67b5SJonathan Cameron 
183646d67b5SJonathan Cameron /* state_lock should be held to ensure consistent state */
ad7150_write_event_params(struct iio_dev * indio_dev,unsigned int chan,enum iio_event_type type,enum iio_event_direction dir)184646d67b5SJonathan Cameron static int ad7150_write_event_params(struct iio_dev *indio_dev,
185646d67b5SJonathan Cameron 				     unsigned int chan,
186646d67b5SJonathan Cameron 				     enum iio_event_type type,
187646d67b5SJonathan Cameron 				     enum iio_event_direction dir)
188646d67b5SJonathan Cameron {
189646d67b5SJonathan Cameron 	struct ad7150_chip_info *chip = iio_priv(indio_dev);
190646d67b5SJonathan Cameron 	int rising = (dir == IIO_EV_DIR_RISING);
191646d67b5SJonathan Cameron 
192646d67b5SJonathan Cameron 	/* Only update value live, if parameter is in use */
193646d67b5SJonathan Cameron 	if ((type != chip->type) || (dir != chip->dir))
194646d67b5SJonathan Cameron 		return 0;
195646d67b5SJonathan Cameron 
196646d67b5SJonathan Cameron 	switch (type) {
197646d67b5SJonathan Cameron 		/* Note completely different from the adaptive versions */
198646d67b5SJonathan Cameron 	case IIO_EV_TYPE_THRESH: {
199646d67b5SJonathan Cameron 		u16 value = chip->threshold[rising][chan];
200646d67b5SJonathan Cameron 		return i2c_smbus_write_word_swapped(chip->client,
201646d67b5SJonathan Cameron 						    ad7150_addresses[chan][3],
202646d67b5SJonathan Cameron 						    value);
203646d67b5SJonathan Cameron 	}
204646d67b5SJonathan Cameron 	case IIO_EV_TYPE_THRESH_ADAPTIVE: {
205646d67b5SJonathan Cameron 		int ret;
206646d67b5SJonathan Cameron 		u8 sens, timeout;
207646d67b5SJonathan Cameron 
208646d67b5SJonathan Cameron 		sens = chip->thresh_sensitivity[rising][chan];
209646d67b5SJonathan Cameron 		ret = i2c_smbus_write_byte_data(chip->client,
210646d67b5SJonathan Cameron 						ad7150_addresses[chan][4],
211646d67b5SJonathan Cameron 						sens);
212646d67b5SJonathan Cameron 		if (ret)
213646d67b5SJonathan Cameron 			return ret;
214646d67b5SJonathan Cameron 
215646d67b5SJonathan Cameron 		/*
216646d67b5SJonathan Cameron 		 * Single timeout register contains timeouts for both
217646d67b5SJonathan Cameron 		 * directions.
218646d67b5SJonathan Cameron 		 */
219646d67b5SJonathan Cameron 		timeout = FIELD_PREP(AD7150_CH_TIMEOUT_APPROACHING,
220646d67b5SJonathan Cameron 				     chip->thresh_timeout[1][chan]);
221646d67b5SJonathan Cameron 		timeout |= FIELD_PREP(AD7150_CH_TIMEOUT_RECEDING,
222646d67b5SJonathan Cameron 				      chip->thresh_timeout[0][chan]);
223646d67b5SJonathan Cameron 		return i2c_smbus_write_byte_data(chip->client,
224646d67b5SJonathan Cameron 						 ad7150_addresses[chan][5],
225646d67b5SJonathan Cameron 						 timeout);
226646d67b5SJonathan Cameron 	}
227646d67b5SJonathan Cameron 	default:
228646d67b5SJonathan Cameron 		return -EINVAL;
229646d67b5SJonathan Cameron 	}
230646d67b5SJonathan Cameron }
231646d67b5SJonathan Cameron 
ad7150_write_event_config(struct iio_dev * indio_dev,const struct iio_chan_spec * chan,enum iio_event_type type,enum iio_event_direction dir,int state)232646d67b5SJonathan Cameron static int ad7150_write_event_config(struct iio_dev *indio_dev,
233646d67b5SJonathan Cameron 				     const struct iio_chan_spec *chan,
234646d67b5SJonathan Cameron 				     enum iio_event_type type,
235646d67b5SJonathan Cameron 				     enum iio_event_direction dir, int state)
236646d67b5SJonathan Cameron {
237646d67b5SJonathan Cameron 	struct ad7150_chip_info *chip = iio_priv(indio_dev);
238032aec33SJonathan Cameron 	int ret = 0;
239646d67b5SJonathan Cameron 
240646d67b5SJonathan Cameron 	/*
241646d67b5SJonathan Cameron 	 * There is only a single shared control and no on chip
242646d67b5SJonathan Cameron 	 * interrupt disables for the two interrupt lines.
243646d67b5SJonathan Cameron 	 * So, enabling will switch the events configured to enable
244646d67b5SJonathan Cameron 	 * whatever was most recently requested and if necessary enable_irq()
245646d67b5SJonathan Cameron 	 * the interrupt and any disable will disable_irq() for that
246646d67b5SJonathan Cameron 	 * channels interrupt.
247646d67b5SJonathan Cameron 	 */
248646d67b5SJonathan Cameron 	if (!state) {
249646d67b5SJonathan Cameron 		if ((chip->int_enabled[chan->channel]) &&
250646d67b5SJonathan Cameron 		    (type == chip->type) && (dir == chip->dir)) {
251646d67b5SJonathan Cameron 			disable_irq(chip->interrupts[chan->channel]);
252646d67b5SJonathan Cameron 			chip->int_enabled[chan->channel] = false;
253646d67b5SJonathan Cameron 		}
254646d67b5SJonathan Cameron 		return 0;
255646d67b5SJonathan Cameron 	}
256646d67b5SJonathan Cameron 
257646d67b5SJonathan Cameron 	mutex_lock(&chip->state_lock);
258646d67b5SJonathan Cameron 	if ((type != chip->type) || (dir != chip->dir)) {
259646d67b5SJonathan Cameron 		int rising = (dir == IIO_EV_DIR_RISING);
260646d67b5SJonathan Cameron 		u8 thresh_type, cfg, fixed;
261646d67b5SJonathan Cameron 
262646d67b5SJonathan Cameron 		/*
263646d67b5SJonathan Cameron 		 * Need to temporarily disable both interrupts if
264646d67b5SJonathan Cameron 		 * enabled - this is to avoid races around changing
265646d67b5SJonathan Cameron 		 * config and thresholds.
266646d67b5SJonathan Cameron 		 * Note enable/disable_irq() are reference counted so
267646d67b5SJonathan Cameron 		 * no need to check if already enabled.
268646d67b5SJonathan Cameron 		 */
269646d67b5SJonathan Cameron 		disable_irq(chip->interrupts[0]);
270646d67b5SJonathan Cameron 		disable_irq(chip->interrupts[1]);
271646d67b5SJonathan Cameron 
272646d67b5SJonathan Cameron 		ret = i2c_smbus_read_byte_data(chip->client, AD7150_CFG_REG);
273646d67b5SJonathan Cameron 		if (ret < 0)
274646d67b5SJonathan Cameron 			goto error_ret;
275646d67b5SJonathan Cameron 
276646d67b5SJonathan Cameron 		cfg = ret & ~(AD7150_CFG_THRESHTYPE_MSK | AD7150_CFG_FIX);
277646d67b5SJonathan Cameron 
278646d67b5SJonathan Cameron 		if (type == IIO_EV_TYPE_THRESH_ADAPTIVE)
279646d67b5SJonathan Cameron 			fixed = 0;
280646d67b5SJonathan Cameron 		else
281646d67b5SJonathan Cameron 			fixed = 1;
282646d67b5SJonathan Cameron 
283646d67b5SJonathan Cameron 		if (rising)
284646d67b5SJonathan Cameron 			thresh_type = AD7150_CFG_TT_POS;
285646d67b5SJonathan Cameron 		else
286646d67b5SJonathan Cameron 			thresh_type = AD7150_CFG_TT_NEG;
287646d67b5SJonathan Cameron 
288646d67b5SJonathan Cameron 		cfg |= FIELD_PREP(AD7150_CFG_FIX, fixed) |
289646d67b5SJonathan Cameron 			FIELD_PREP(AD7150_CFG_THRESHTYPE_MSK, thresh_type);
290646d67b5SJonathan Cameron 
291646d67b5SJonathan Cameron 		ret = i2c_smbus_write_byte_data(chip->client, AD7150_CFG_REG,
292646d67b5SJonathan Cameron 						cfg);
293646d67b5SJonathan Cameron 		if (ret < 0)
294646d67b5SJonathan Cameron 			goto error_ret;
295646d67b5SJonathan Cameron 
296646d67b5SJonathan Cameron 		/*
297646d67b5SJonathan Cameron 		 * There is a potential race condition here, but not easy
298646d67b5SJonathan Cameron 		 * to close given we can't disable the interrupt at the
299646d67b5SJonathan Cameron 		 * chip side of things. Rely on the status bit.
300646d67b5SJonathan Cameron 		 */
301646d67b5SJonathan Cameron 		chip->type = type;
302646d67b5SJonathan Cameron 		chip->dir = dir;
303646d67b5SJonathan Cameron 
304646d67b5SJonathan Cameron 		/* update control attributes */
305646d67b5SJonathan Cameron 		ret = ad7150_write_event_params(indio_dev, chan->channel, type,
306646d67b5SJonathan Cameron 						dir);
307646d67b5SJonathan Cameron 		if (ret)
308646d67b5SJonathan Cameron 			goto error_ret;
309646d67b5SJonathan Cameron 		/* reenable any irq's we disabled whilst changing mode */
310646d67b5SJonathan Cameron 		enable_irq(chip->interrupts[0]);
311646d67b5SJonathan Cameron 		enable_irq(chip->interrupts[1]);
312646d67b5SJonathan Cameron 	}
313646d67b5SJonathan Cameron 	if (!chip->int_enabled[chan->channel]) {
314646d67b5SJonathan Cameron 		enable_irq(chip->interrupts[chan->channel]);
315646d67b5SJonathan Cameron 		chip->int_enabled[chan->channel] = true;
316646d67b5SJonathan Cameron 	}
317646d67b5SJonathan Cameron 
318646d67b5SJonathan Cameron error_ret:
319646d67b5SJonathan Cameron 	mutex_unlock(&chip->state_lock);
320646d67b5SJonathan Cameron 
321646d67b5SJonathan Cameron 	return ret;
322646d67b5SJonathan Cameron }
323646d67b5SJonathan Cameron 
ad7150_read_event_value(struct iio_dev * indio_dev,const struct iio_chan_spec * chan,enum iio_event_type type,enum iio_event_direction dir,enum iio_event_info info,int * val,int * val2)324646d67b5SJonathan Cameron static int ad7150_read_event_value(struct iio_dev *indio_dev,
325646d67b5SJonathan Cameron 				   const struct iio_chan_spec *chan,
326646d67b5SJonathan Cameron 				   enum iio_event_type type,
327646d67b5SJonathan Cameron 				   enum iio_event_direction dir,
328646d67b5SJonathan Cameron 				   enum iio_event_info info,
329646d67b5SJonathan Cameron 				   int *val, int *val2)
330646d67b5SJonathan Cameron {
331646d67b5SJonathan Cameron 	struct ad7150_chip_info *chip = iio_priv(indio_dev);
332646d67b5SJonathan Cameron 	int rising = (dir == IIO_EV_DIR_RISING);
333646d67b5SJonathan Cameron 
334646d67b5SJonathan Cameron 	/* Complex register sharing going on here */
335646d67b5SJonathan Cameron 	switch (info) {
336646d67b5SJonathan Cameron 	case IIO_EV_INFO_VALUE:
337646d67b5SJonathan Cameron 		switch (type) {
338646d67b5SJonathan Cameron 		case IIO_EV_TYPE_THRESH_ADAPTIVE:
339646d67b5SJonathan Cameron 			*val = chip->thresh_sensitivity[rising][chan->channel];
340646d67b5SJonathan Cameron 			return IIO_VAL_INT;
341646d67b5SJonathan Cameron 		case IIO_EV_TYPE_THRESH:
342646d67b5SJonathan Cameron 			*val = chip->threshold[rising][chan->channel];
343646d67b5SJonathan Cameron 			return IIO_VAL_INT;
344646d67b5SJonathan Cameron 		default:
345646d67b5SJonathan Cameron 			return -EINVAL;
346646d67b5SJonathan Cameron 		}
347646d67b5SJonathan Cameron 	case IIO_EV_INFO_TIMEOUT:
348646d67b5SJonathan Cameron 		*val = 0;
349646d67b5SJonathan Cameron 		*val2 = chip->thresh_timeout[rising][chan->channel] * 10000;
350646d67b5SJonathan Cameron 		return IIO_VAL_INT_PLUS_MICRO;
351646d67b5SJonathan Cameron 	default:
352646d67b5SJonathan Cameron 		return -EINVAL;
353646d67b5SJonathan Cameron 	}
354646d67b5SJonathan Cameron }
355646d67b5SJonathan Cameron 
ad7150_write_event_value(struct iio_dev * indio_dev,const struct iio_chan_spec * chan,enum iio_event_type type,enum iio_event_direction dir,enum iio_event_info info,int val,int val2)356646d67b5SJonathan Cameron static int ad7150_write_event_value(struct iio_dev *indio_dev,
357646d67b5SJonathan Cameron 				    const struct iio_chan_spec *chan,
358646d67b5SJonathan Cameron 				    enum iio_event_type type,
359646d67b5SJonathan Cameron 				    enum iio_event_direction dir,
360646d67b5SJonathan Cameron 				    enum iio_event_info info,
361646d67b5SJonathan Cameron 				    int val, int val2)
362646d67b5SJonathan Cameron {
363646d67b5SJonathan Cameron 	int ret;
364646d67b5SJonathan Cameron 	struct ad7150_chip_info *chip = iio_priv(indio_dev);
365646d67b5SJonathan Cameron 	int rising = (dir == IIO_EV_DIR_RISING);
366646d67b5SJonathan Cameron 
367646d67b5SJonathan Cameron 	mutex_lock(&chip->state_lock);
368646d67b5SJonathan Cameron 	switch (info) {
369646d67b5SJonathan Cameron 	case IIO_EV_INFO_VALUE:
370646d67b5SJonathan Cameron 		switch (type) {
371646d67b5SJonathan Cameron 		case IIO_EV_TYPE_THRESH_ADAPTIVE:
372646d67b5SJonathan Cameron 			chip->thresh_sensitivity[rising][chan->channel] = val;
373646d67b5SJonathan Cameron 			break;
374646d67b5SJonathan Cameron 		case IIO_EV_TYPE_THRESH:
375646d67b5SJonathan Cameron 			chip->threshold[rising][chan->channel] = val;
376646d67b5SJonathan Cameron 			break;
377646d67b5SJonathan Cameron 		default:
378646d67b5SJonathan Cameron 			ret = -EINVAL;
379646d67b5SJonathan Cameron 			goto error_ret;
380646d67b5SJonathan Cameron 		}
381646d67b5SJonathan Cameron 		break;
382646d67b5SJonathan Cameron 	case IIO_EV_INFO_TIMEOUT: {
383646d67b5SJonathan Cameron 		/*
384646d67b5SJonathan Cameron 		 * Raw timeout is in cycles of 10 msecs as long as both
385646d67b5SJonathan Cameron 		 * channels are enabled.
386646d67b5SJonathan Cameron 		 * In terms of INT_PLUS_MICRO, that is in units of 10,000
387646d67b5SJonathan Cameron 		 */
388646d67b5SJonathan Cameron 		int timeout = val2 / 10000;
389646d67b5SJonathan Cameron 
390646d67b5SJonathan Cameron 		if (val != 0 || timeout < 0 || timeout > 15 || val2 % 10000) {
391646d67b5SJonathan Cameron 			ret = -EINVAL;
392646d67b5SJonathan Cameron 			goto error_ret;
393646d67b5SJonathan Cameron 		}
394646d67b5SJonathan Cameron 
395646d67b5SJonathan Cameron 		chip->thresh_timeout[rising][chan->channel] = timeout;
396646d67b5SJonathan Cameron 		break;
397646d67b5SJonathan Cameron 	}
398646d67b5SJonathan Cameron 	default:
399646d67b5SJonathan Cameron 		ret = -EINVAL;
400646d67b5SJonathan Cameron 		goto error_ret;
401646d67b5SJonathan Cameron 	}
402646d67b5SJonathan Cameron 
403646d67b5SJonathan Cameron 	/* write back if active */
404646d67b5SJonathan Cameron 	ret = ad7150_write_event_params(indio_dev, chan->channel, type, dir);
405646d67b5SJonathan Cameron 
406646d67b5SJonathan Cameron error_ret:
407646d67b5SJonathan Cameron 	mutex_unlock(&chip->state_lock);
408646d67b5SJonathan Cameron 	return ret;
409646d67b5SJonathan Cameron }
410646d67b5SJonathan Cameron 
411646d67b5SJonathan Cameron static const struct iio_event_spec ad7150_events[] = {
412646d67b5SJonathan Cameron 	{
413646d67b5SJonathan Cameron 		.type = IIO_EV_TYPE_THRESH,
414646d67b5SJonathan Cameron 		.dir = IIO_EV_DIR_RISING,
415646d67b5SJonathan Cameron 		.mask_separate = BIT(IIO_EV_INFO_VALUE) |
416646d67b5SJonathan Cameron 			BIT(IIO_EV_INFO_ENABLE),
417646d67b5SJonathan Cameron 	}, {
418646d67b5SJonathan Cameron 		.type = IIO_EV_TYPE_THRESH,
419646d67b5SJonathan Cameron 		.dir = IIO_EV_DIR_FALLING,
420646d67b5SJonathan Cameron 		.mask_separate = BIT(IIO_EV_INFO_VALUE) |
421646d67b5SJonathan Cameron 			BIT(IIO_EV_INFO_ENABLE),
422646d67b5SJonathan Cameron 	}, {
423646d67b5SJonathan Cameron 		.type = IIO_EV_TYPE_THRESH_ADAPTIVE,
424646d67b5SJonathan Cameron 		.dir = IIO_EV_DIR_RISING,
425646d67b5SJonathan Cameron 		.mask_separate = BIT(IIO_EV_INFO_VALUE) |
426646d67b5SJonathan Cameron 			BIT(IIO_EV_INFO_ENABLE) |
427646d67b5SJonathan Cameron 			BIT(IIO_EV_INFO_TIMEOUT),
428646d67b5SJonathan Cameron 	}, {
429646d67b5SJonathan Cameron 		.type = IIO_EV_TYPE_THRESH_ADAPTIVE,
430646d67b5SJonathan Cameron 		.dir = IIO_EV_DIR_FALLING,
431646d67b5SJonathan Cameron 		.mask_separate = BIT(IIO_EV_INFO_VALUE) |
432646d67b5SJonathan Cameron 			BIT(IIO_EV_INFO_ENABLE) |
433646d67b5SJonathan Cameron 			BIT(IIO_EV_INFO_TIMEOUT),
434646d67b5SJonathan Cameron 	},
435646d67b5SJonathan Cameron };
436646d67b5SJonathan Cameron 
437646d67b5SJonathan Cameron #define AD7150_CAPACITANCE_CHAN(_chan)	{			\
438646d67b5SJonathan Cameron 		.type = IIO_CAPACITANCE,			\
439646d67b5SJonathan Cameron 		.indexed = 1,					\
440646d67b5SJonathan Cameron 		.channel = _chan,				\
441646d67b5SJonathan Cameron 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |	\
442646d67b5SJonathan Cameron 		BIT(IIO_CHAN_INFO_AVERAGE_RAW),			\
443646d67b5SJonathan Cameron 		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
444646d67b5SJonathan Cameron 			BIT(IIO_CHAN_INFO_OFFSET),		\
445646d67b5SJonathan Cameron 		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),\
446646d67b5SJonathan Cameron 		.event_spec = ad7150_events,			\
447646d67b5SJonathan Cameron 		.num_event_specs = ARRAY_SIZE(ad7150_events),	\
448646d67b5SJonathan Cameron 	}
449646d67b5SJonathan Cameron 
450646d67b5SJonathan Cameron #define AD7150_CAPACITANCE_CHAN_NO_IRQ(_chan)	{		\
451646d67b5SJonathan Cameron 		.type = IIO_CAPACITANCE,			\
452646d67b5SJonathan Cameron 		.indexed = 1,					\
453646d67b5SJonathan Cameron 		.channel = _chan,				\
454646d67b5SJonathan Cameron 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |	\
455646d67b5SJonathan Cameron 		BIT(IIO_CHAN_INFO_AVERAGE_RAW),			\
456646d67b5SJonathan Cameron 		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
457646d67b5SJonathan Cameron 			BIT(IIO_CHAN_INFO_OFFSET),		\
458646d67b5SJonathan Cameron 		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),\
459646d67b5SJonathan Cameron 	}
460646d67b5SJonathan Cameron 
461646d67b5SJonathan Cameron static const struct iio_chan_spec ad7150_channels[] = {
462646d67b5SJonathan Cameron 	AD7150_CAPACITANCE_CHAN(0),
463646d67b5SJonathan Cameron 	AD7150_CAPACITANCE_CHAN(1),
464646d67b5SJonathan Cameron };
465646d67b5SJonathan Cameron 
466646d67b5SJonathan Cameron static const struct iio_chan_spec ad7150_channels_no_irq[] = {
467646d67b5SJonathan Cameron 	AD7150_CAPACITANCE_CHAN_NO_IRQ(0),
468646d67b5SJonathan Cameron 	AD7150_CAPACITANCE_CHAN_NO_IRQ(1),
469646d67b5SJonathan Cameron };
470646d67b5SJonathan Cameron 
471646d67b5SJonathan Cameron static const struct iio_chan_spec ad7151_channels[] = {
472646d67b5SJonathan Cameron 	AD7150_CAPACITANCE_CHAN(0),
473646d67b5SJonathan Cameron };
474646d67b5SJonathan Cameron 
475646d67b5SJonathan Cameron static const struct iio_chan_spec ad7151_channels_no_irq[] = {
476646d67b5SJonathan Cameron 	AD7150_CAPACITANCE_CHAN_NO_IRQ(0),
477646d67b5SJonathan Cameron };
478646d67b5SJonathan Cameron 
__ad7150_event_handler(void * private,u8 status_mask,int channel)479646d67b5SJonathan Cameron static irqreturn_t __ad7150_event_handler(void *private, u8 status_mask,
480646d67b5SJonathan Cameron 					  int channel)
481646d67b5SJonathan Cameron {
482646d67b5SJonathan Cameron 	struct iio_dev *indio_dev = private;
483646d67b5SJonathan Cameron 	struct ad7150_chip_info *chip = iio_priv(indio_dev);
484646d67b5SJonathan Cameron 	s64 timestamp = iio_get_time_ns(indio_dev);
485646d67b5SJonathan Cameron 	int int_status;
486646d67b5SJonathan Cameron 
487646d67b5SJonathan Cameron 	int_status = i2c_smbus_read_byte_data(chip->client, AD7150_STATUS_REG);
488646d67b5SJonathan Cameron 	if (int_status < 0)
489646d67b5SJonathan Cameron 		return IRQ_HANDLED;
490646d67b5SJonathan Cameron 
491646d67b5SJonathan Cameron 	if (!(int_status & status_mask))
492646d67b5SJonathan Cameron 		return IRQ_HANDLED;
493646d67b5SJonathan Cameron 
494646d67b5SJonathan Cameron 	iio_push_event(indio_dev,
495646d67b5SJonathan Cameron 		       IIO_UNMOD_EVENT_CODE(IIO_CAPACITANCE, channel,
496646d67b5SJonathan Cameron 					    chip->type, chip->dir),
497646d67b5SJonathan Cameron 		       timestamp);
498646d67b5SJonathan Cameron 
499646d67b5SJonathan Cameron 	return IRQ_HANDLED;
500646d67b5SJonathan Cameron }
501646d67b5SJonathan Cameron 
ad7150_event_handler_ch1(int irq,void * private)502646d67b5SJonathan Cameron static irqreturn_t ad7150_event_handler_ch1(int irq, void *private)
503646d67b5SJonathan Cameron {
504646d67b5SJonathan Cameron 	return __ad7150_event_handler(private, AD7150_STATUS_OUT1, 0);
505646d67b5SJonathan Cameron }
506646d67b5SJonathan Cameron 
ad7150_event_handler_ch2(int irq,void * private)507646d67b5SJonathan Cameron static irqreturn_t ad7150_event_handler_ch2(int irq, void *private)
508646d67b5SJonathan Cameron {
509646d67b5SJonathan Cameron 	return __ad7150_event_handler(private, AD7150_STATUS_OUT2, 1);
510646d67b5SJonathan Cameron }
511646d67b5SJonathan Cameron 
512646d67b5SJonathan Cameron static IIO_CONST_ATTR(in_capacitance_thresh_adaptive_timeout_available,
513646d67b5SJonathan Cameron 		      "[0 0.01 0.15]");
514646d67b5SJonathan Cameron 
515646d67b5SJonathan Cameron static struct attribute *ad7150_event_attributes[] = {
516646d67b5SJonathan Cameron 	&iio_const_attr_in_capacitance_thresh_adaptive_timeout_available
517646d67b5SJonathan Cameron 	.dev_attr.attr,
518646d67b5SJonathan Cameron 	NULL,
519646d67b5SJonathan Cameron };
520646d67b5SJonathan Cameron 
521646d67b5SJonathan Cameron static const struct attribute_group ad7150_event_attribute_group = {
522646d67b5SJonathan Cameron 	.attrs = ad7150_event_attributes,
523646d67b5SJonathan Cameron 	.name = "events",
524646d67b5SJonathan Cameron };
525646d67b5SJonathan Cameron 
526646d67b5SJonathan Cameron static const struct iio_info ad7150_info = {
527646d67b5SJonathan Cameron 	.event_attrs = &ad7150_event_attribute_group,
528646d67b5SJonathan Cameron 	.read_raw = &ad7150_read_raw,
529646d67b5SJonathan Cameron 	.read_event_config = &ad7150_read_event_config,
530646d67b5SJonathan Cameron 	.write_event_config = &ad7150_write_event_config,
531646d67b5SJonathan Cameron 	.read_event_value = &ad7150_read_event_value,
532646d67b5SJonathan Cameron 	.write_event_value = &ad7150_write_event_value,
533646d67b5SJonathan Cameron };
534646d67b5SJonathan Cameron 
535646d67b5SJonathan Cameron static const struct iio_info ad7150_info_no_irq = {
536646d67b5SJonathan Cameron 	.read_raw = &ad7150_read_raw,
537646d67b5SJonathan Cameron };
538646d67b5SJonathan Cameron 
ad7150_probe(struct i2c_client * client)5397558eaa9SUwe Kleine-König static int ad7150_probe(struct i2c_client *client)
540646d67b5SJonathan Cameron {
5417558eaa9SUwe Kleine-König 	const struct i2c_device_id *id = i2c_client_get_device_id(client);
542646d67b5SJonathan Cameron 	struct ad7150_chip_info *chip;
543646d67b5SJonathan Cameron 	struct iio_dev *indio_dev;
544*b20f5801SMatti Vaittinen 	bool use_irq = true;
545646d67b5SJonathan Cameron 	int ret;
546646d67b5SJonathan Cameron 
547646d67b5SJonathan Cameron 	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip));
548646d67b5SJonathan Cameron 	if (!indio_dev)
549646d67b5SJonathan Cameron 		return -ENOMEM;
550646d67b5SJonathan Cameron 
551646d67b5SJonathan Cameron 	chip = iio_priv(indio_dev);
552646d67b5SJonathan Cameron 	mutex_init(&chip->state_lock);
553646d67b5SJonathan Cameron 	chip->client = client;
554646d67b5SJonathan Cameron 
555646d67b5SJonathan Cameron 	indio_dev->name = id->name;
556646d67b5SJonathan Cameron 
557646d67b5SJonathan Cameron 	indio_dev->modes = INDIO_DIRECT_MODE;
558646d67b5SJonathan Cameron 
55972ce527cSJonathan Cameron 	ret = devm_regulator_get_enable(&client->dev, "vdd");
560646d67b5SJonathan Cameron 	if (ret)
561646d67b5SJonathan Cameron 		return ret;
562646d67b5SJonathan Cameron 
563646d67b5SJonathan Cameron 	chip->interrupts[0] = fwnode_irq_get(dev_fwnode(&client->dev), 0);
564646d67b5SJonathan Cameron 	if (chip->interrupts[0] < 0)
565*b20f5801SMatti Vaittinen 		use_irq = false;
566*b20f5801SMatti Vaittinen 	else if (id->driver_data == AD7150) {
567646d67b5SJonathan Cameron 		chip->interrupts[1] = fwnode_irq_get(dev_fwnode(&client->dev), 1);
568646d67b5SJonathan Cameron 		if (chip->interrupts[1] < 0)
569*b20f5801SMatti Vaittinen 			use_irq = false;
570646d67b5SJonathan Cameron 	}
571*b20f5801SMatti Vaittinen 	if (use_irq) {
572646d67b5SJonathan Cameron 		irq_set_status_flags(chip->interrupts[0], IRQ_NOAUTOEN);
573646d67b5SJonathan Cameron 		ret = devm_request_threaded_irq(&client->dev,
574646d67b5SJonathan Cameron 						chip->interrupts[0],
575646d67b5SJonathan Cameron 						NULL,
576646d67b5SJonathan Cameron 						&ad7150_event_handler_ch1,
577646d67b5SJonathan Cameron 						IRQF_TRIGGER_RISING |
578646d67b5SJonathan Cameron 						IRQF_ONESHOT,
579646d67b5SJonathan Cameron 						"ad7150_irq1",
580646d67b5SJonathan Cameron 						indio_dev);
581646d67b5SJonathan Cameron 		if (ret)
582646d67b5SJonathan Cameron 			return ret;
583646d67b5SJonathan Cameron 
584646d67b5SJonathan Cameron 		indio_dev->info = &ad7150_info;
585646d67b5SJonathan Cameron 		switch (id->driver_data) {
586646d67b5SJonathan Cameron 		case AD7150:
587646d67b5SJonathan Cameron 			indio_dev->channels = ad7150_channels;
588646d67b5SJonathan Cameron 			indio_dev->num_channels = ARRAY_SIZE(ad7150_channels);
589646d67b5SJonathan Cameron 			irq_set_status_flags(chip->interrupts[1], IRQ_NOAUTOEN);
590646d67b5SJonathan Cameron 			ret = devm_request_threaded_irq(&client->dev,
591646d67b5SJonathan Cameron 							chip->interrupts[1],
592646d67b5SJonathan Cameron 							NULL,
593646d67b5SJonathan Cameron 							&ad7150_event_handler_ch2,
594646d67b5SJonathan Cameron 							IRQF_TRIGGER_RISING |
595646d67b5SJonathan Cameron 							IRQF_ONESHOT,
596646d67b5SJonathan Cameron 							"ad7150_irq2",
597646d67b5SJonathan Cameron 							indio_dev);
598646d67b5SJonathan Cameron 			if (ret)
599646d67b5SJonathan Cameron 				return ret;
600646d67b5SJonathan Cameron 			break;
601646d67b5SJonathan Cameron 		case AD7151:
602646d67b5SJonathan Cameron 			indio_dev->channels = ad7151_channels;
603646d67b5SJonathan Cameron 			indio_dev->num_channels = ARRAY_SIZE(ad7151_channels);
604646d67b5SJonathan Cameron 			break;
605646d67b5SJonathan Cameron 		default:
606646d67b5SJonathan Cameron 			return -EINVAL;
607646d67b5SJonathan Cameron 		}
608646d67b5SJonathan Cameron 
609646d67b5SJonathan Cameron 	} else {
610646d67b5SJonathan Cameron 		indio_dev->info = &ad7150_info_no_irq;
611646d67b5SJonathan Cameron 		switch (id->driver_data) {
612646d67b5SJonathan Cameron 		case AD7150:
613646d67b5SJonathan Cameron 			indio_dev->channels = ad7150_channels_no_irq;
614646d67b5SJonathan Cameron 			indio_dev->num_channels =
615646d67b5SJonathan Cameron 				ARRAY_SIZE(ad7150_channels_no_irq);
616646d67b5SJonathan Cameron 			break;
617646d67b5SJonathan Cameron 		case AD7151:
618646d67b5SJonathan Cameron 			indio_dev->channels = ad7151_channels_no_irq;
619646d67b5SJonathan Cameron 			indio_dev->num_channels =
620646d67b5SJonathan Cameron 				ARRAY_SIZE(ad7151_channels_no_irq);
621646d67b5SJonathan Cameron 			break;
622646d67b5SJonathan Cameron 		default:
623646d67b5SJonathan Cameron 			return -EINVAL;
624646d67b5SJonathan Cameron 		}
625646d67b5SJonathan Cameron 	}
626646d67b5SJonathan Cameron 
627646d67b5SJonathan Cameron 	return devm_iio_device_register(indio_dev->dev.parent, indio_dev);
628646d67b5SJonathan Cameron }
629646d67b5SJonathan Cameron 
630646d67b5SJonathan Cameron static const struct i2c_device_id ad7150_id[] = {
631646d67b5SJonathan Cameron 	{ "ad7150", AD7150 },
632646d67b5SJonathan Cameron 	{ "ad7151", AD7151 },
633646d67b5SJonathan Cameron 	{ "ad7156", AD7150 },
634646d67b5SJonathan Cameron 	{}
635646d67b5SJonathan Cameron };
636646d67b5SJonathan Cameron 
637646d67b5SJonathan Cameron MODULE_DEVICE_TABLE(i2c, ad7150_id);
638646d67b5SJonathan Cameron 
639646d67b5SJonathan Cameron static const struct of_device_id ad7150_of_match[] = {
640646d67b5SJonathan Cameron 	{ "adi,ad7150" },
641646d67b5SJonathan Cameron 	{ "adi,ad7151" },
642646d67b5SJonathan Cameron 	{ "adi,ad7156" },
643646d67b5SJonathan Cameron 	{}
644646d67b5SJonathan Cameron };
645646d67b5SJonathan Cameron static struct i2c_driver ad7150_driver = {
646646d67b5SJonathan Cameron 	.driver = {
647646d67b5SJonathan Cameron 		.name = "ad7150",
648646d67b5SJonathan Cameron 		.of_match_table = ad7150_of_match,
649646d67b5SJonathan Cameron 	},
6507cf15f42SUwe Kleine-König 	.probe = ad7150_probe,
651646d67b5SJonathan Cameron 	.id_table = ad7150_id,
652646d67b5SJonathan Cameron };
653646d67b5SJonathan Cameron module_i2c_driver(ad7150_driver);
654646d67b5SJonathan Cameron 
655646d67b5SJonathan Cameron MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
656646d67b5SJonathan Cameron MODULE_DESCRIPTION("Analog Devices AD7150/1/6 capacitive sensor driver");
657646d67b5SJonathan Cameron MODULE_LICENSE("GPL v2");
658