xref: /linux/drivers/iio/pressure/hp206c.c (revision fa4c9c93e93f429bb8a8b01c53d54d6bd53a3028)
1*fa4c9c93SCrestez Dan Leonard /*
2*fa4c9c93SCrestez Dan Leonard  * hp206c.c - HOPERF HP206C precision barometer and altimeter sensor
3*fa4c9c93SCrestez Dan Leonard  *
4*fa4c9c93SCrestez Dan Leonard  * Copyright (c) 2016, Intel Corporation.
5*fa4c9c93SCrestez Dan Leonard  *
6*fa4c9c93SCrestez Dan Leonard  * This file is subject to the terms and conditions of version 2 of
7*fa4c9c93SCrestez Dan Leonard  * the GNU General Public License.  See the file COPYING in the main
8*fa4c9c93SCrestez Dan Leonard  * directory of this archive for more details.
9*fa4c9c93SCrestez Dan Leonard  *
10*fa4c9c93SCrestez Dan Leonard  * (7-bit I2C slave address 0x76)
11*fa4c9c93SCrestez Dan Leonard  *
12*fa4c9c93SCrestez Dan Leonard  * Datasheet:
13*fa4c9c93SCrestez Dan Leonard  *  http://www.hoperf.com/upload/sensor/HP206C_DataSheet_EN_V2.0.pdf
14*fa4c9c93SCrestez Dan Leonard  */
15*fa4c9c93SCrestez Dan Leonard 
16*fa4c9c93SCrestez Dan Leonard #include <linux/module.h>
17*fa4c9c93SCrestez Dan Leonard #include <linux/i2c.h>
18*fa4c9c93SCrestez Dan Leonard #include <linux/iio/iio.h>
19*fa4c9c93SCrestez Dan Leonard #include <linux/iio/sysfs.h>
20*fa4c9c93SCrestez Dan Leonard #include <linux/delay.h>
21*fa4c9c93SCrestez Dan Leonard #include <linux/util_macros.h>
22*fa4c9c93SCrestez Dan Leonard #include <linux/acpi.h>
23*fa4c9c93SCrestez Dan Leonard 
24*fa4c9c93SCrestez Dan Leonard /* I2C commands: */
25*fa4c9c93SCrestez Dan Leonard #define HP206C_CMD_SOFT_RST	0x06
26*fa4c9c93SCrestez Dan Leonard 
27*fa4c9c93SCrestez Dan Leonard #define HP206C_CMD_ADC_CVT	0x40
28*fa4c9c93SCrestez Dan Leonard 
29*fa4c9c93SCrestez Dan Leonard #define HP206C_CMD_ADC_CVT_OSR_4096	0x00
30*fa4c9c93SCrestez Dan Leonard #define HP206C_CMD_ADC_CVT_OSR_2048	0x04
31*fa4c9c93SCrestez Dan Leonard #define HP206C_CMD_ADC_CVT_OSR_1024	0x08
32*fa4c9c93SCrestez Dan Leonard #define HP206C_CMD_ADC_CVT_OSR_512	0x0c
33*fa4c9c93SCrestez Dan Leonard #define HP206C_CMD_ADC_CVT_OSR_256	0x10
34*fa4c9c93SCrestez Dan Leonard #define HP206C_CMD_ADC_CVT_OSR_128	0x14
35*fa4c9c93SCrestez Dan Leonard 
36*fa4c9c93SCrestez Dan Leonard #define HP206C_CMD_ADC_CVT_CHNL_PT	0x00
37*fa4c9c93SCrestez Dan Leonard #define HP206C_CMD_ADC_CVT_CHNL_T	0x02
38*fa4c9c93SCrestez Dan Leonard 
39*fa4c9c93SCrestez Dan Leonard #define HP206C_CMD_READ_P	0x30
40*fa4c9c93SCrestez Dan Leonard #define HP206C_CMD_READ_T	0x32
41*fa4c9c93SCrestez Dan Leonard 
42*fa4c9c93SCrestez Dan Leonard #define HP206C_CMD_READ_REG	0x80
43*fa4c9c93SCrestez Dan Leonard #define HP206C_CMD_WRITE_REG	0xc0
44*fa4c9c93SCrestez Dan Leonard 
45*fa4c9c93SCrestez Dan Leonard #define HP206C_REG_INT_EN	0x0b
46*fa4c9c93SCrestez Dan Leonard #define HP206C_REG_INT_CFG	0x0c
47*fa4c9c93SCrestez Dan Leonard 
48*fa4c9c93SCrestez Dan Leonard #define HP206C_REG_INT_SRC	0x0d
49*fa4c9c93SCrestez Dan Leonard #define HP206C_FLAG_DEV_RDY	0x40
50*fa4c9c93SCrestez Dan Leonard 
51*fa4c9c93SCrestez Dan Leonard #define HP206C_REG_PARA		0x0f
52*fa4c9c93SCrestez Dan Leonard #define HP206C_FLAG_CMPS_EN	0x80
53*fa4c9c93SCrestez Dan Leonard 
54*fa4c9c93SCrestez Dan Leonard /* Maximum spin for DEV_RDY */
55*fa4c9c93SCrestez Dan Leonard #define HP206C_MAX_DEV_RDY_WAIT_COUNT 20
56*fa4c9c93SCrestez Dan Leonard #define HP206C_DEV_RDY_WAIT_US    20000
57*fa4c9c93SCrestez Dan Leonard 
58*fa4c9c93SCrestez Dan Leonard struct hp206c_data {
59*fa4c9c93SCrestez Dan Leonard 	struct mutex mutex;
60*fa4c9c93SCrestez Dan Leonard 	struct i2c_client *client;
61*fa4c9c93SCrestez Dan Leonard 	int temp_osr_index;
62*fa4c9c93SCrestez Dan Leonard 	int pres_osr_index;
63*fa4c9c93SCrestez Dan Leonard };
64*fa4c9c93SCrestez Dan Leonard 
65*fa4c9c93SCrestez Dan Leonard struct hp206c_osr_setting {
66*fa4c9c93SCrestez Dan Leonard 	u8 osr_mask;
67*fa4c9c93SCrestez Dan Leonard 	unsigned int temp_conv_time_us;
68*fa4c9c93SCrestez Dan Leonard 	unsigned int pres_conv_time_us;
69*fa4c9c93SCrestez Dan Leonard };
70*fa4c9c93SCrestez Dan Leonard 
71*fa4c9c93SCrestez Dan Leonard /* Data from Table 5 in datasheet. */
72*fa4c9c93SCrestez Dan Leonard static const struct hp206c_osr_setting hp206c_osr_settings[] = {
73*fa4c9c93SCrestez Dan Leonard 	{ HP206C_CMD_ADC_CVT_OSR_4096,	65600,	131100	},
74*fa4c9c93SCrestez Dan Leonard 	{ HP206C_CMD_ADC_CVT_OSR_2048,	32800,	65600	},
75*fa4c9c93SCrestez Dan Leonard 	{ HP206C_CMD_ADC_CVT_OSR_1024,	16400,	32800	},
76*fa4c9c93SCrestez Dan Leonard 	{ HP206C_CMD_ADC_CVT_OSR_512,	8200,	16400	},
77*fa4c9c93SCrestez Dan Leonard 	{ HP206C_CMD_ADC_CVT_OSR_256,	4100,	8200	},
78*fa4c9c93SCrestez Dan Leonard 	{ HP206C_CMD_ADC_CVT_OSR_128,	2100,	4100	},
79*fa4c9c93SCrestez Dan Leonard };
80*fa4c9c93SCrestez Dan Leonard static const int hp206c_osr_rates[] = { 4096, 2048, 1024, 512, 256, 128 };
81*fa4c9c93SCrestez Dan Leonard static const char hp206c_osr_rates_str[] = "4096 2048 1024 512 256 128";
82*fa4c9c93SCrestez Dan Leonard 
83*fa4c9c93SCrestez Dan Leonard static inline int hp206c_read_reg(struct i2c_client *client, u8 reg)
84*fa4c9c93SCrestez Dan Leonard {
85*fa4c9c93SCrestez Dan Leonard 	return i2c_smbus_read_byte_data(client, HP206C_CMD_READ_REG | reg);
86*fa4c9c93SCrestez Dan Leonard }
87*fa4c9c93SCrestez Dan Leonard 
88*fa4c9c93SCrestez Dan Leonard static inline int hp206c_write_reg(struct i2c_client *client, u8 reg, u8 val)
89*fa4c9c93SCrestez Dan Leonard {
90*fa4c9c93SCrestez Dan Leonard 	return i2c_smbus_write_byte_data(client,
91*fa4c9c93SCrestez Dan Leonard 			HP206C_CMD_WRITE_REG | reg, val);
92*fa4c9c93SCrestez Dan Leonard }
93*fa4c9c93SCrestez Dan Leonard 
94*fa4c9c93SCrestez Dan Leonard static int hp206c_read_20bit(struct i2c_client *client, u8 cmd)
95*fa4c9c93SCrestez Dan Leonard {
96*fa4c9c93SCrestez Dan Leonard 	int ret;
97*fa4c9c93SCrestez Dan Leonard 	u8 values[3];
98*fa4c9c93SCrestez Dan Leonard 
99*fa4c9c93SCrestez Dan Leonard 	ret = i2c_smbus_read_i2c_block_data(client, cmd, 3, values);
100*fa4c9c93SCrestez Dan Leonard 	if (ret < 0)
101*fa4c9c93SCrestez Dan Leonard 		return ret;
102*fa4c9c93SCrestez Dan Leonard 	if (ret != 3)
103*fa4c9c93SCrestez Dan Leonard 		return -EIO;
104*fa4c9c93SCrestez Dan Leonard 	return ((values[0] & 0xF) << 16) | (values[1] << 8) | (values[2]);
105*fa4c9c93SCrestez Dan Leonard }
106*fa4c9c93SCrestez Dan Leonard 
107*fa4c9c93SCrestez Dan Leonard /* Spin for max 160ms until DEV_RDY is 1, or return error. */
108*fa4c9c93SCrestez Dan Leonard static int hp206c_wait_dev_rdy(struct iio_dev *indio_dev)
109*fa4c9c93SCrestez Dan Leonard {
110*fa4c9c93SCrestez Dan Leonard 	int ret;
111*fa4c9c93SCrestez Dan Leonard 	int count = 0;
112*fa4c9c93SCrestez Dan Leonard 	struct hp206c_data *data = iio_priv(indio_dev);
113*fa4c9c93SCrestez Dan Leonard 	struct i2c_client *client = data->client;
114*fa4c9c93SCrestez Dan Leonard 
115*fa4c9c93SCrestez Dan Leonard 	while (++count <= HP206C_MAX_DEV_RDY_WAIT_COUNT) {
116*fa4c9c93SCrestez Dan Leonard 		ret = hp206c_read_reg(client, HP206C_REG_INT_SRC);
117*fa4c9c93SCrestez Dan Leonard 		if (ret < 0) {
118*fa4c9c93SCrestez Dan Leonard 			dev_err(&indio_dev->dev, "Failed READ_REG INT_SRC: %d\n", ret);
119*fa4c9c93SCrestez Dan Leonard 			return ret;
120*fa4c9c93SCrestez Dan Leonard 		}
121*fa4c9c93SCrestez Dan Leonard 		if (ret & HP206C_FLAG_DEV_RDY)
122*fa4c9c93SCrestez Dan Leonard 			return 0;
123*fa4c9c93SCrestez Dan Leonard 		usleep_range(HP206C_DEV_RDY_WAIT_US, HP206C_DEV_RDY_WAIT_US * 3 / 2);
124*fa4c9c93SCrestez Dan Leonard 	}
125*fa4c9c93SCrestez Dan Leonard 	return -ETIMEDOUT;
126*fa4c9c93SCrestez Dan Leonard }
127*fa4c9c93SCrestez Dan Leonard 
128*fa4c9c93SCrestez Dan Leonard static int hp206c_set_compensation(struct i2c_client *client, bool enabled)
129*fa4c9c93SCrestez Dan Leonard {
130*fa4c9c93SCrestez Dan Leonard 	int val;
131*fa4c9c93SCrestez Dan Leonard 
132*fa4c9c93SCrestez Dan Leonard 	val = hp206c_read_reg(client, HP206C_REG_PARA);
133*fa4c9c93SCrestez Dan Leonard 	if (val < 0)
134*fa4c9c93SCrestez Dan Leonard 		return val;
135*fa4c9c93SCrestez Dan Leonard 	if (enabled)
136*fa4c9c93SCrestez Dan Leonard 		val |= HP206C_FLAG_CMPS_EN;
137*fa4c9c93SCrestez Dan Leonard 	else
138*fa4c9c93SCrestez Dan Leonard 		val &= ~HP206C_FLAG_CMPS_EN;
139*fa4c9c93SCrestez Dan Leonard 
140*fa4c9c93SCrestez Dan Leonard 	return hp206c_write_reg(client, HP206C_REG_PARA, val);
141*fa4c9c93SCrestez Dan Leonard }
142*fa4c9c93SCrestez Dan Leonard 
143*fa4c9c93SCrestez Dan Leonard /* Do a soft reset */
144*fa4c9c93SCrestez Dan Leonard static int hp206c_soft_reset(struct iio_dev *indio_dev)
145*fa4c9c93SCrestez Dan Leonard {
146*fa4c9c93SCrestez Dan Leonard 	int ret;
147*fa4c9c93SCrestez Dan Leonard 	struct hp206c_data *data = iio_priv(indio_dev);
148*fa4c9c93SCrestez Dan Leonard 	struct i2c_client *client = data->client;
149*fa4c9c93SCrestez Dan Leonard 
150*fa4c9c93SCrestez Dan Leonard 	ret = i2c_smbus_write_byte(client, HP206C_CMD_SOFT_RST);
151*fa4c9c93SCrestez Dan Leonard 	if (ret) {
152*fa4c9c93SCrestez Dan Leonard 		dev_err(&client->dev, "Failed to reset device: %d\n", ret);
153*fa4c9c93SCrestez Dan Leonard 		return ret;
154*fa4c9c93SCrestez Dan Leonard 	}
155*fa4c9c93SCrestez Dan Leonard 
156*fa4c9c93SCrestez Dan Leonard 	usleep_range(400, 600);
157*fa4c9c93SCrestez Dan Leonard 
158*fa4c9c93SCrestez Dan Leonard 	ret = hp206c_wait_dev_rdy(indio_dev);
159*fa4c9c93SCrestez Dan Leonard 	if (ret) {
160*fa4c9c93SCrestez Dan Leonard 		dev_err(&client->dev, "Device not ready after soft reset: %d\n", ret);
161*fa4c9c93SCrestez Dan Leonard 		return ret;
162*fa4c9c93SCrestez Dan Leonard 	}
163*fa4c9c93SCrestez Dan Leonard 
164*fa4c9c93SCrestez Dan Leonard 	ret = hp206c_set_compensation(client, true);
165*fa4c9c93SCrestez Dan Leonard 	if (ret)
166*fa4c9c93SCrestez Dan Leonard 		dev_err(&client->dev, "Failed to enable compensation: %d\n", ret);
167*fa4c9c93SCrestez Dan Leonard 	return ret;
168*fa4c9c93SCrestez Dan Leonard }
169*fa4c9c93SCrestez Dan Leonard 
170*fa4c9c93SCrestez Dan Leonard static int hp206c_conv_and_read(struct iio_dev *indio_dev,
171*fa4c9c93SCrestez Dan Leonard 				u8 conv_cmd, u8 read_cmd,
172*fa4c9c93SCrestez Dan Leonard 				unsigned int sleep_us)
173*fa4c9c93SCrestez Dan Leonard {
174*fa4c9c93SCrestez Dan Leonard 	int ret;
175*fa4c9c93SCrestez Dan Leonard 	struct hp206c_data *data = iio_priv(indio_dev);
176*fa4c9c93SCrestez Dan Leonard 	struct i2c_client *client = data->client;
177*fa4c9c93SCrestez Dan Leonard 
178*fa4c9c93SCrestez Dan Leonard 	ret = hp206c_wait_dev_rdy(indio_dev);
179*fa4c9c93SCrestez Dan Leonard 	if (ret < 0) {
180*fa4c9c93SCrestez Dan Leonard 		dev_err(&indio_dev->dev, "Device not ready: %d\n", ret);
181*fa4c9c93SCrestez Dan Leonard 		return ret;
182*fa4c9c93SCrestez Dan Leonard 	}
183*fa4c9c93SCrestez Dan Leonard 
184*fa4c9c93SCrestez Dan Leonard 	ret = i2c_smbus_write_byte(client, conv_cmd);
185*fa4c9c93SCrestez Dan Leonard 	if (ret < 0) {
186*fa4c9c93SCrestez Dan Leonard 		dev_err(&indio_dev->dev, "Failed convert: %d\n", ret);
187*fa4c9c93SCrestez Dan Leonard 		return ret;
188*fa4c9c93SCrestez Dan Leonard 	}
189*fa4c9c93SCrestez Dan Leonard 
190*fa4c9c93SCrestez Dan Leonard 	usleep_range(sleep_us, sleep_us * 3 / 2);
191*fa4c9c93SCrestez Dan Leonard 
192*fa4c9c93SCrestez Dan Leonard 	ret = hp206c_wait_dev_rdy(indio_dev);
193*fa4c9c93SCrestez Dan Leonard 	if (ret < 0) {
194*fa4c9c93SCrestez Dan Leonard 		dev_err(&indio_dev->dev, "Device not ready: %d\n", ret);
195*fa4c9c93SCrestez Dan Leonard 		return ret;
196*fa4c9c93SCrestez Dan Leonard 	}
197*fa4c9c93SCrestez Dan Leonard 
198*fa4c9c93SCrestez Dan Leonard 	ret = hp206c_read_20bit(client, read_cmd);
199*fa4c9c93SCrestez Dan Leonard 	if (ret < 0)
200*fa4c9c93SCrestez Dan Leonard 		dev_err(&indio_dev->dev, "Failed read: %d\n", ret);
201*fa4c9c93SCrestez Dan Leonard 
202*fa4c9c93SCrestez Dan Leonard 	return ret;
203*fa4c9c93SCrestez Dan Leonard }
204*fa4c9c93SCrestez Dan Leonard 
205*fa4c9c93SCrestez Dan Leonard static int hp206c_read_raw(struct iio_dev *indio_dev,
206*fa4c9c93SCrestez Dan Leonard 			   struct iio_chan_spec const *chan, int *val,
207*fa4c9c93SCrestez Dan Leonard 			   int *val2, long mask)
208*fa4c9c93SCrestez Dan Leonard {
209*fa4c9c93SCrestez Dan Leonard 	int ret;
210*fa4c9c93SCrestez Dan Leonard 	struct hp206c_data *data = iio_priv(indio_dev);
211*fa4c9c93SCrestez Dan Leonard 	const struct hp206c_osr_setting *osr_setting;
212*fa4c9c93SCrestez Dan Leonard 	u8 conv_cmd;
213*fa4c9c93SCrestez Dan Leonard 
214*fa4c9c93SCrestez Dan Leonard 	mutex_lock(&data->mutex);
215*fa4c9c93SCrestez Dan Leonard 
216*fa4c9c93SCrestez Dan Leonard 	switch (mask) {
217*fa4c9c93SCrestez Dan Leonard 	case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
218*fa4c9c93SCrestez Dan Leonard 		switch (chan->type) {
219*fa4c9c93SCrestez Dan Leonard 		case IIO_TEMP:
220*fa4c9c93SCrestez Dan Leonard 			*val = hp206c_osr_rates[data->temp_osr_index];
221*fa4c9c93SCrestez Dan Leonard 			ret = IIO_VAL_INT;
222*fa4c9c93SCrestez Dan Leonard 			break;
223*fa4c9c93SCrestez Dan Leonard 
224*fa4c9c93SCrestez Dan Leonard 		case IIO_PRESSURE:
225*fa4c9c93SCrestez Dan Leonard 			*val = hp206c_osr_rates[data->pres_osr_index];
226*fa4c9c93SCrestez Dan Leonard 			ret = IIO_VAL_INT;
227*fa4c9c93SCrestez Dan Leonard 			break;
228*fa4c9c93SCrestez Dan Leonard 		default:
229*fa4c9c93SCrestez Dan Leonard 			ret = -EINVAL;
230*fa4c9c93SCrestez Dan Leonard 		}
231*fa4c9c93SCrestez Dan Leonard 		break;
232*fa4c9c93SCrestez Dan Leonard 
233*fa4c9c93SCrestez Dan Leonard 	case IIO_CHAN_INFO_RAW:
234*fa4c9c93SCrestez Dan Leonard 		switch (chan->type) {
235*fa4c9c93SCrestez Dan Leonard 		case IIO_TEMP:
236*fa4c9c93SCrestez Dan Leonard 			osr_setting = &hp206c_osr_settings[data->temp_osr_index];
237*fa4c9c93SCrestez Dan Leonard 			conv_cmd = HP206C_CMD_ADC_CVT |
238*fa4c9c93SCrestez Dan Leonard 					osr_setting->osr_mask |
239*fa4c9c93SCrestez Dan Leonard 					HP206C_CMD_ADC_CVT_CHNL_T;
240*fa4c9c93SCrestez Dan Leonard 			ret = hp206c_conv_and_read(indio_dev,
241*fa4c9c93SCrestez Dan Leonard 					conv_cmd,
242*fa4c9c93SCrestez Dan Leonard 					HP206C_CMD_READ_T,
243*fa4c9c93SCrestez Dan Leonard 					osr_setting->temp_conv_time_us);
244*fa4c9c93SCrestez Dan Leonard 			if (ret >= 0) {
245*fa4c9c93SCrestez Dan Leonard 				/* 20 significant bits are provided.
246*fa4c9c93SCrestez Dan Leonard 				 * Extend sign over the rest.
247*fa4c9c93SCrestez Dan Leonard 				 */
248*fa4c9c93SCrestez Dan Leonard 				*val = sign_extend32(ret, 19);
249*fa4c9c93SCrestez Dan Leonard 				ret = IIO_VAL_INT;
250*fa4c9c93SCrestez Dan Leonard 			}
251*fa4c9c93SCrestez Dan Leonard 			break;
252*fa4c9c93SCrestez Dan Leonard 
253*fa4c9c93SCrestez Dan Leonard 		case IIO_PRESSURE:
254*fa4c9c93SCrestez Dan Leonard 			osr_setting = &hp206c_osr_settings[data->pres_osr_index];
255*fa4c9c93SCrestez Dan Leonard 			conv_cmd = HP206C_CMD_ADC_CVT |
256*fa4c9c93SCrestez Dan Leonard 					osr_setting->osr_mask |
257*fa4c9c93SCrestez Dan Leonard 					HP206C_CMD_ADC_CVT_CHNL_PT;
258*fa4c9c93SCrestez Dan Leonard 			ret = hp206c_conv_and_read(indio_dev,
259*fa4c9c93SCrestez Dan Leonard 					conv_cmd,
260*fa4c9c93SCrestez Dan Leonard 					HP206C_CMD_READ_P,
261*fa4c9c93SCrestez Dan Leonard 					osr_setting->pres_conv_time_us);
262*fa4c9c93SCrestez Dan Leonard 			if (ret >= 0) {
263*fa4c9c93SCrestez Dan Leonard 				*val = ret;
264*fa4c9c93SCrestez Dan Leonard 				ret = IIO_VAL_INT;
265*fa4c9c93SCrestez Dan Leonard 			}
266*fa4c9c93SCrestez Dan Leonard 			break;
267*fa4c9c93SCrestez Dan Leonard 		default:
268*fa4c9c93SCrestez Dan Leonard 			ret = -EINVAL;
269*fa4c9c93SCrestez Dan Leonard 		}
270*fa4c9c93SCrestez Dan Leonard 		break;
271*fa4c9c93SCrestez Dan Leonard 
272*fa4c9c93SCrestez Dan Leonard 	case IIO_CHAN_INFO_SCALE:
273*fa4c9c93SCrestez Dan Leonard 		switch (chan->type) {
274*fa4c9c93SCrestez Dan Leonard 		case IIO_TEMP:
275*fa4c9c93SCrestez Dan Leonard 			*val = 0;
276*fa4c9c93SCrestez Dan Leonard 			*val2 = 10000;
277*fa4c9c93SCrestez Dan Leonard 			ret = IIO_VAL_INT_PLUS_MICRO;
278*fa4c9c93SCrestez Dan Leonard 			break;
279*fa4c9c93SCrestez Dan Leonard 
280*fa4c9c93SCrestez Dan Leonard 		case IIO_PRESSURE:
281*fa4c9c93SCrestez Dan Leonard 			*val = 0;
282*fa4c9c93SCrestez Dan Leonard 			*val2 = 1000;
283*fa4c9c93SCrestez Dan Leonard 			ret = IIO_VAL_INT_PLUS_MICRO;
284*fa4c9c93SCrestez Dan Leonard 			break;
285*fa4c9c93SCrestez Dan Leonard 		default:
286*fa4c9c93SCrestez Dan Leonard 			ret = -EINVAL;
287*fa4c9c93SCrestez Dan Leonard 		}
288*fa4c9c93SCrestez Dan Leonard 		break;
289*fa4c9c93SCrestez Dan Leonard 
290*fa4c9c93SCrestez Dan Leonard 	default:
291*fa4c9c93SCrestez Dan Leonard 		ret = -EINVAL;
292*fa4c9c93SCrestez Dan Leonard 	}
293*fa4c9c93SCrestez Dan Leonard 
294*fa4c9c93SCrestez Dan Leonard 	mutex_unlock(&data->mutex);
295*fa4c9c93SCrestez Dan Leonard 	return ret;
296*fa4c9c93SCrestez Dan Leonard }
297*fa4c9c93SCrestez Dan Leonard 
298*fa4c9c93SCrestez Dan Leonard static int hp206c_write_raw(struct iio_dev *indio_dev,
299*fa4c9c93SCrestez Dan Leonard 			    struct iio_chan_spec const *chan,
300*fa4c9c93SCrestez Dan Leonard 			    int val, int val2, long mask)
301*fa4c9c93SCrestez Dan Leonard {
302*fa4c9c93SCrestez Dan Leonard 	int ret = 0;
303*fa4c9c93SCrestez Dan Leonard 	struct hp206c_data *data = iio_priv(indio_dev);
304*fa4c9c93SCrestez Dan Leonard 
305*fa4c9c93SCrestez Dan Leonard 	if (mask != IIO_CHAN_INFO_OVERSAMPLING_RATIO)
306*fa4c9c93SCrestez Dan Leonard 		return -EINVAL;
307*fa4c9c93SCrestez Dan Leonard 	mutex_lock(&data->mutex);
308*fa4c9c93SCrestez Dan Leonard 	switch (chan->type) {
309*fa4c9c93SCrestez Dan Leonard 	case IIO_TEMP:
310*fa4c9c93SCrestez Dan Leonard 		data->temp_osr_index = find_closest_descending(val,
311*fa4c9c93SCrestez Dan Leonard 			hp206c_osr_rates, ARRAY_SIZE(hp206c_osr_rates));
312*fa4c9c93SCrestez Dan Leonard 		break;
313*fa4c9c93SCrestez Dan Leonard 	case IIO_PRESSURE:
314*fa4c9c93SCrestez Dan Leonard 		data->pres_osr_index = find_closest_descending(val,
315*fa4c9c93SCrestez Dan Leonard 			hp206c_osr_rates, ARRAY_SIZE(hp206c_osr_rates));
316*fa4c9c93SCrestez Dan Leonard 		break;
317*fa4c9c93SCrestez Dan Leonard 	default:
318*fa4c9c93SCrestez Dan Leonard 		ret = -EINVAL;
319*fa4c9c93SCrestez Dan Leonard 	}
320*fa4c9c93SCrestez Dan Leonard 	mutex_unlock(&data->mutex);
321*fa4c9c93SCrestez Dan Leonard 	return ret;
322*fa4c9c93SCrestez Dan Leonard }
323*fa4c9c93SCrestez Dan Leonard 
324*fa4c9c93SCrestez Dan Leonard static const struct iio_chan_spec hp206c_channels[] = {
325*fa4c9c93SCrestez Dan Leonard 	{
326*fa4c9c93SCrestez Dan Leonard 		.type = IIO_TEMP,
327*fa4c9c93SCrestez Dan Leonard 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
328*fa4c9c93SCrestez Dan Leonard 				      BIT(IIO_CHAN_INFO_SCALE) |
329*fa4c9c93SCrestez Dan Leonard 				      BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
330*fa4c9c93SCrestez Dan Leonard 	},
331*fa4c9c93SCrestez Dan Leonard 	{
332*fa4c9c93SCrestez Dan Leonard 		.type = IIO_PRESSURE,
333*fa4c9c93SCrestez Dan Leonard 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
334*fa4c9c93SCrestez Dan Leonard 				      BIT(IIO_CHAN_INFO_SCALE) |
335*fa4c9c93SCrestez Dan Leonard 				      BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
336*fa4c9c93SCrestez Dan Leonard 	}
337*fa4c9c93SCrestez Dan Leonard };
338*fa4c9c93SCrestez Dan Leonard 
339*fa4c9c93SCrestez Dan Leonard static IIO_CONST_ATTR_SAMP_FREQ_AVAIL(hp206c_osr_rates_str);
340*fa4c9c93SCrestez Dan Leonard 
341*fa4c9c93SCrestez Dan Leonard static struct attribute *hp206c_attributes[] = {
342*fa4c9c93SCrestez Dan Leonard 	&iio_const_attr_sampling_frequency_available.dev_attr.attr,
343*fa4c9c93SCrestez Dan Leonard 	NULL,
344*fa4c9c93SCrestez Dan Leonard };
345*fa4c9c93SCrestez Dan Leonard 
346*fa4c9c93SCrestez Dan Leonard static const struct attribute_group hp206c_attribute_group = {
347*fa4c9c93SCrestez Dan Leonard 	.attrs = hp206c_attributes,
348*fa4c9c93SCrestez Dan Leonard };
349*fa4c9c93SCrestez Dan Leonard 
350*fa4c9c93SCrestez Dan Leonard static const struct iio_info hp206c_info = {
351*fa4c9c93SCrestez Dan Leonard 	.attrs = &hp206c_attribute_group,
352*fa4c9c93SCrestez Dan Leonard 	.read_raw = hp206c_read_raw,
353*fa4c9c93SCrestez Dan Leonard 	.write_raw = hp206c_write_raw,
354*fa4c9c93SCrestez Dan Leonard 	.driver_module = THIS_MODULE,
355*fa4c9c93SCrestez Dan Leonard };
356*fa4c9c93SCrestez Dan Leonard 
357*fa4c9c93SCrestez Dan Leonard static int hp206c_probe(struct i2c_client *client,
358*fa4c9c93SCrestez Dan Leonard 			const struct i2c_device_id *id)
359*fa4c9c93SCrestez Dan Leonard {
360*fa4c9c93SCrestez Dan Leonard 	struct iio_dev *indio_dev;
361*fa4c9c93SCrestez Dan Leonard 	struct hp206c_data *data;
362*fa4c9c93SCrestez Dan Leonard 	int ret;
363*fa4c9c93SCrestez Dan Leonard 
364*fa4c9c93SCrestez Dan Leonard 	if (!i2c_check_functionality(client->adapter,
365*fa4c9c93SCrestez Dan Leonard 				     I2C_FUNC_SMBUS_BYTE |
366*fa4c9c93SCrestez Dan Leonard 				     I2C_FUNC_SMBUS_BYTE_DATA |
367*fa4c9c93SCrestez Dan Leonard 				     I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {
368*fa4c9c93SCrestez Dan Leonard 		dev_err(&client->dev, "Adapter does not support "
369*fa4c9c93SCrestez Dan Leonard 				"all required i2c functionality\n");
370*fa4c9c93SCrestez Dan Leonard 		return -ENODEV;
371*fa4c9c93SCrestez Dan Leonard 	}
372*fa4c9c93SCrestez Dan Leonard 
373*fa4c9c93SCrestez Dan Leonard 	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
374*fa4c9c93SCrestez Dan Leonard 	if (!indio_dev)
375*fa4c9c93SCrestez Dan Leonard 		return -ENOMEM;
376*fa4c9c93SCrestez Dan Leonard 
377*fa4c9c93SCrestez Dan Leonard 	data = iio_priv(indio_dev);
378*fa4c9c93SCrestez Dan Leonard 	data->client = client;
379*fa4c9c93SCrestez Dan Leonard 	mutex_init(&data->mutex);
380*fa4c9c93SCrestez Dan Leonard 
381*fa4c9c93SCrestez Dan Leonard 	indio_dev->info = &hp206c_info;
382*fa4c9c93SCrestez Dan Leonard 	indio_dev->name = id->name;
383*fa4c9c93SCrestez Dan Leonard 	indio_dev->dev.parent = &client->dev;
384*fa4c9c93SCrestez Dan Leonard 	indio_dev->modes = INDIO_DIRECT_MODE;
385*fa4c9c93SCrestez Dan Leonard 	indio_dev->channels = hp206c_channels;
386*fa4c9c93SCrestez Dan Leonard 	indio_dev->num_channels = ARRAY_SIZE(hp206c_channels);
387*fa4c9c93SCrestez Dan Leonard 
388*fa4c9c93SCrestez Dan Leonard 	i2c_set_clientdata(client, indio_dev);
389*fa4c9c93SCrestez Dan Leonard 
390*fa4c9c93SCrestez Dan Leonard 	/* Do a soft reset on probe */
391*fa4c9c93SCrestez Dan Leonard 	ret = hp206c_soft_reset(indio_dev);
392*fa4c9c93SCrestez Dan Leonard 	if (ret) {
393*fa4c9c93SCrestez Dan Leonard 		dev_err(&client->dev, "Failed to reset on startup: %d\n", ret);
394*fa4c9c93SCrestez Dan Leonard 		return -ENODEV;
395*fa4c9c93SCrestez Dan Leonard 	}
396*fa4c9c93SCrestez Dan Leonard 
397*fa4c9c93SCrestez Dan Leonard 	return devm_iio_device_register(&client->dev, indio_dev);
398*fa4c9c93SCrestez Dan Leonard }
399*fa4c9c93SCrestez Dan Leonard 
400*fa4c9c93SCrestez Dan Leonard static const struct i2c_device_id hp206c_id[] = {
401*fa4c9c93SCrestez Dan Leonard 	{"hp206c"},
402*fa4c9c93SCrestez Dan Leonard 	{}
403*fa4c9c93SCrestez Dan Leonard };
404*fa4c9c93SCrestez Dan Leonard 
405*fa4c9c93SCrestez Dan Leonard #ifdef CONFIG_ACPI
406*fa4c9c93SCrestez Dan Leonard static const struct acpi_device_id hp206c_acpi_match[] = {
407*fa4c9c93SCrestez Dan Leonard 	{"HOP206C", 0},
408*fa4c9c93SCrestez Dan Leonard 	{ },
409*fa4c9c93SCrestez Dan Leonard };
410*fa4c9c93SCrestez Dan Leonard MODULE_DEVICE_TABLE(acpi, hp206c_acpi_match);
411*fa4c9c93SCrestez Dan Leonard #endif
412*fa4c9c93SCrestez Dan Leonard 
413*fa4c9c93SCrestez Dan Leonard static struct i2c_driver hp206c_driver = {
414*fa4c9c93SCrestez Dan Leonard 	.probe = hp206c_probe,
415*fa4c9c93SCrestez Dan Leonard 	.id_table = hp206c_id,
416*fa4c9c93SCrestez Dan Leonard 	.driver = {
417*fa4c9c93SCrestez Dan Leonard 		.name = "hp206c",
418*fa4c9c93SCrestez Dan Leonard 		.acpi_match_table = ACPI_PTR(hp206c_acpi_match),
419*fa4c9c93SCrestez Dan Leonard 	},
420*fa4c9c93SCrestez Dan Leonard };
421*fa4c9c93SCrestez Dan Leonard 
422*fa4c9c93SCrestez Dan Leonard module_i2c_driver(hp206c_driver);
423*fa4c9c93SCrestez Dan Leonard 
424*fa4c9c93SCrestez Dan Leonard MODULE_DESCRIPTION("HOPERF HP206C precision barometer and altimeter sensor");
425*fa4c9c93SCrestez Dan Leonard MODULE_AUTHOR("Leonard Crestez <leonard.crestez@intel.com>");
426*fa4c9c93SCrestez Dan Leonard MODULE_LICENSE("GPL v2");
427