aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarek Vasut <marex@denx.de>2016-04-18 10:05:24 -0400
committerJonathan Cameron <jic23@kernel.org>2016-04-24 04:46:14 -0400
commitd8469e93a4af82110b15b28bbb003a1dbe4258d2 (patch)
treec4744c89ddbec3104a7efe17adbad77016a2e0b6
parent41c128cb25cee72be66397fae8ceb8dc0c2c6984 (diff)
iio: pressure: hp03: Add Hope RF HP03 sensor support
Add support for HopeRF pressure and temperature sensor. This device uses two fixed I2C addresses, one for storing calibration coefficients and another for accessing the ADC. Signed-off-by: Marek Vasut <marex@denx.de> Cc: Matt Ranostay <mranostay@gmail.com> Cc: Jonathan Cameron <jic23@kernel.org> Signed-off-by: Jonathan Cameron <jic23@kernel.org>
-rw-r--r--Documentation/devicetree/bindings/iio/pressure/hp03.txt17
-rw-r--r--drivers/iio/pressure/Kconfig11
-rw-r--r--drivers/iio/pressure/Makefile1
-rw-r--r--drivers/iio/pressure/hp03.c312
4 files changed, 341 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/iio/pressure/hp03.txt b/Documentation/devicetree/bindings/iio/pressure/hp03.txt
new file mode 100644
index 000000000000..54e7e70bcea5
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/pressure/hp03.txt
@@ -0,0 +1,17 @@
1HopeRF HP03 digital pressure/temperature sensors
2
3Required properties:
4- compatible: must be "hoperf,hp03"
5- xclr-gpio: must be device tree identifier of the XCLR pin.
6 The XCLR pin is a reset of the ADC in the chip,
7 it must be pulled HI before the conversion and
8 readout of the value from the ADC registers and
9 pulled LO afterward.
10
11Example:
12
13hp03@0x77 {
14 compatible = "hoperf,hp03";
15 reg = <0x77>;
16 xclr-gpio = <&portc 0 0x0>;
17};
diff --git a/drivers/iio/pressure/Kconfig b/drivers/iio/pressure/Kconfig
index 8de0192adea6..54c616520512 100644
--- a/drivers/iio/pressure/Kconfig
+++ b/drivers/iio/pressure/Kconfig
@@ -30,6 +30,17 @@ config HID_SENSOR_PRESS
30 To compile this driver as a module, choose M here: the module 30 To compile this driver as a module, choose M here: the module
31 will be called hid-sensor-press. 31 will be called hid-sensor-press.
32 32
33config HP03
34 tristate "Hope RF HP03 temperature and pressure sensor driver"
35 depends on I2C
36 select REGMAP_I2C
37 help
38 Say yes here to build support for Hope RF HP03 pressure and
39 temperature sensor.
40
41 To compile this driver as a module, choose M here: the module
42 will be called hp03.
43
33config MPL115 44config MPL115
34 tristate 45 tristate
35 46
diff --git a/drivers/iio/pressure/Makefile b/drivers/iio/pressure/Makefile
index 6e60863c1086..17d6e7afa1ff 100644
--- a/drivers/iio/pressure/Makefile
+++ b/drivers/iio/pressure/Makefile
@@ -5,6 +5,7 @@
5# When adding new entries keep the list in alphabetical order 5# When adding new entries keep the list in alphabetical order
6obj-$(CONFIG_BMP280) += bmp280.o 6obj-$(CONFIG_BMP280) += bmp280.o
7obj-$(CONFIG_HID_SENSOR_PRESS) += hid-sensor-press.o 7obj-$(CONFIG_HID_SENSOR_PRESS) += hid-sensor-press.o
8obj-$(CONFIG_HP03) += hp03.o
8obj-$(CONFIG_MPL115) += mpl115.o 9obj-$(CONFIG_MPL115) += mpl115.o
9obj-$(CONFIG_MPL115_I2C) += mpl115_i2c.o 10obj-$(CONFIG_MPL115_I2C) += mpl115_i2c.o
10obj-$(CONFIG_MPL115_SPI) += mpl115_spi.o 11obj-$(CONFIG_MPL115_SPI) += mpl115_spi.o
diff --git a/drivers/iio/pressure/hp03.c b/drivers/iio/pressure/hp03.c
new file mode 100644
index 000000000000..ac76515d5d49
--- /dev/null
+++ b/drivers/iio/pressure/hp03.c
@@ -0,0 +1,312 @@
1/*
2 * Copyright (c) 2016 Marek Vasut <marex@denx.de>
3 *
4 * Driver for Hope RF HP03 digital temperature and pressure sensor.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 */
10
11#define pr_fmt(fmt) "hp03: " fmt
12
13#include <linux/module.h>
14#include <linux/delay.h>
15#include <linux/gpio/consumer.h>
16#include <linux/i2c.h>
17#include <linux/regmap.h>
18#include <linux/iio/iio.h>
19#include <linux/iio/sysfs.h>
20
21/*
22 * The HP03 sensor occupies two fixed I2C addresses:
23 * 0x50 ... read-only EEPROM with calibration data
24 * 0x77 ... read-write ADC for pressure and temperature
25 */
26#define HP03_EEPROM_ADDR 0x50
27#define HP03_ADC_ADDR 0x77
28
29#define HP03_EEPROM_CX_OFFSET 0x10
30#define HP03_EEPROM_AB_OFFSET 0x1e
31#define HP03_EEPROM_CD_OFFSET 0x20
32
33#define HP03_ADC_WRITE_REG 0xff
34#define HP03_ADC_READ_REG 0xfd
35#define HP03_ADC_READ_PRESSURE 0xf0 /* D1 in datasheet */
36#define HP03_ADC_READ_TEMP 0xe8 /* D2 in datasheet */
37
38struct hp03_priv {
39 struct i2c_client *client;
40 struct mutex lock;
41 struct gpio_desc *xclr_gpio;
42
43 struct i2c_client *eeprom_client;
44 struct regmap *eeprom_regmap;
45
46 s32 pressure; /* kPa */
47 s32 temp; /* Deg. C */
48};
49
50static const struct iio_chan_spec hp03_channels[] = {
51 {
52 .type = IIO_PRESSURE,
53 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
54 .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
55 },
56 {
57 .type = IIO_TEMP,
58 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
59 .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
60 },
61};
62
63static bool hp03_is_writeable_reg(struct device *dev, unsigned int reg)
64{
65 return false;
66}
67
68static bool hp03_is_volatile_reg(struct device *dev, unsigned int reg)
69{
70 return false;
71}
72
73static const struct regmap_config hp03_regmap_config = {
74 .reg_bits = 8,
75 .val_bits = 8,
76
77 .max_register = HP03_EEPROM_CD_OFFSET + 1,
78 .cache_type = REGCACHE_RBTREE,
79
80 .writeable_reg = hp03_is_writeable_reg,
81 .volatile_reg = hp03_is_volatile_reg,
82};
83
84static int hp03_get_temp_pressure(struct hp03_priv *priv, const u8 reg)
85{
86 int ret;
87
88 ret = i2c_smbus_write_byte_data(priv->client, HP03_ADC_WRITE_REG, reg);
89 if (ret < 0)
90 return ret;
91
92 msleep(50); /* Wait for conversion to finish */
93
94 return i2c_smbus_read_word_data(priv->client, HP03_ADC_READ_REG);
95}
96
97static int hp03_update_temp_pressure(struct hp03_priv *priv)
98{
99 struct device *dev = &priv->client->dev;
100 u8 coefs[18];
101 u16 cx_val[7];
102 int ab_val, d1_val, d2_val, diff_val, dut, off, sens, x;
103 int i, ret;
104
105 /* Sample coefficients from EEPROM */
106 ret = regmap_bulk_read(priv->eeprom_regmap, HP03_EEPROM_CX_OFFSET,
107 coefs, sizeof(coefs));
108 if (ret < 0) {
109 dev_err(dev, "Failed to read EEPROM (reg=%02x)\n",
110 HP03_EEPROM_CX_OFFSET);
111 return ret;
112 }
113
114 /* Sample Temperature and Pressure */
115 gpiod_set_value_cansleep(priv->xclr_gpio, 1);
116
117 ret = hp03_get_temp_pressure(priv, HP03_ADC_READ_PRESSURE);
118 if (ret < 0) {
119 dev_err(dev, "Failed to read pressure\n");
120 goto err_adc;
121 }
122 d1_val = ret;
123
124 ret = hp03_get_temp_pressure(priv, HP03_ADC_READ_TEMP);
125 if (ret < 0) {
126 dev_err(dev, "Failed to read temperature\n");
127 goto err_adc;
128 }
129 d2_val = ret;
130
131 gpiod_set_value_cansleep(priv->xclr_gpio, 0);
132
133 /* The Cx coefficients and Temp/Pressure values are MSB first. */
134 for (i = 0; i < 7; i++)
135 cx_val[i] = (coefs[2 * i] << 8) | (coefs[(2 * i) + 1] << 0);
136 d1_val = ((d1_val >> 8) & 0xff) | ((d1_val & 0xff) << 8);
137 d2_val = ((d2_val >> 8) & 0xff) | ((d2_val & 0xff) << 8);
138
139 /* Coefficient voodoo from the HP03 datasheet. */
140 if (d2_val >= cx_val[4])
141 ab_val = coefs[14]; /* A-value */
142 else
143 ab_val = coefs[15]; /* B-value */
144
145 diff_val = d2_val - cx_val[4];
146 dut = (ab_val * (diff_val >> 7) * (diff_val >> 7)) >> coefs[16];
147 dut = diff_val - dut;
148
149 off = (cx_val[1] + (((cx_val[3] - 1024) * dut) >> 14)) * 4;
150 sens = cx_val[0] + ((cx_val[2] * dut) >> 10);
151 x = ((sens * (d1_val - 7168)) >> 14) - off;
152
153 priv->pressure = ((x * 100) >> 5) + (cx_val[6] * 10);
154 priv->temp = 250 + ((dut * cx_val[5]) >> 16) - (dut >> coefs[17]);
155
156 return 0;
157
158err_adc:
159 gpiod_set_value_cansleep(priv->xclr_gpio, 0);
160 return ret;
161}
162
163static int hp03_read_raw(struct iio_dev *indio_dev,
164 struct iio_chan_spec const *chan,
165 int *val, int *val2, long mask)
166{
167 struct hp03_priv *priv = iio_priv(indio_dev);
168 int ret;
169
170 mutex_lock(&priv->lock);
171 ret = hp03_update_temp_pressure(priv);
172 mutex_unlock(&priv->lock);
173
174 if (ret)
175 return ret;
176
177 switch (mask) {
178 case IIO_CHAN_INFO_RAW:
179 switch (chan->type) {
180 case IIO_PRESSURE:
181 *val = priv->pressure;
182 return IIO_VAL_INT;
183 case IIO_TEMP:
184 *val = priv->temp;
185 return IIO_VAL_INT;
186 default:
187 return -EINVAL;
188 }
189 break;
190 case IIO_CHAN_INFO_SCALE:
191 switch (chan->type) {
192 case IIO_PRESSURE:
193 *val = 0;
194 *val2 = 1000;
195 return IIO_VAL_INT_PLUS_MICRO;
196 case IIO_TEMP:
197 *val = 10;
198 return IIO_VAL_INT;
199 default:
200 return -EINVAL;
201 }
202 break;
203 default:
204 return -EINVAL;
205 }
206
207 return -EINVAL;
208}
209
210static const struct iio_info hp03_info = {
211 .driver_module = THIS_MODULE,
212 .read_raw = &hp03_read_raw,
213};
214
215static int hp03_probe(struct i2c_client *client,
216 const struct i2c_device_id *id)
217{
218 struct device *dev = &client->dev;
219 struct iio_dev *indio_dev;
220 struct hp03_priv *priv;
221 int ret;
222
223 indio_dev = devm_iio_device_alloc(dev, sizeof(*priv));
224 if (!indio_dev)
225 return -ENOMEM;
226
227 priv = iio_priv(indio_dev);
228 priv->client = client;
229 mutex_init(&priv->lock);
230
231 indio_dev->dev.parent = dev;
232 indio_dev->name = id->name;
233 indio_dev->channels = hp03_channels;
234 indio_dev->num_channels = ARRAY_SIZE(hp03_channels);
235 indio_dev->info = &hp03_info;
236 indio_dev->modes = INDIO_DIRECT_MODE;
237
238 priv->xclr_gpio = devm_gpiod_get_index(dev, "xclr", 0, GPIOD_OUT_HIGH);
239 if (IS_ERR(priv->xclr_gpio)) {
240 dev_err(dev, "Failed to claim XCLR GPIO\n");
241 ret = PTR_ERR(priv->xclr_gpio);
242 return ret;
243 }
244
245 /*
246 * Allocate another device for the on-sensor EEPROM,
247 * which has it's dedicated I2C address and contains
248 * the calibration constants for the sensor.
249 */
250 priv->eeprom_client = i2c_new_dummy(client->adapter, HP03_EEPROM_ADDR);
251 if (!priv->eeprom_client) {
252 dev_err(dev, "New EEPROM I2C device failed\n");
253 return -ENODEV;
254 }
255
256 priv->eeprom_regmap = regmap_init_i2c(priv->eeprom_client,
257 &hp03_regmap_config);
258 if (IS_ERR(priv->eeprom_regmap)) {
259 dev_err(dev, "Failed to allocate EEPROM regmap\n");
260 ret = PTR_ERR(priv->eeprom_regmap);
261 goto err_cleanup_eeprom_client;
262 }
263
264 ret = iio_device_register(indio_dev);
265 if (ret) {
266 dev_err(dev, "Failed to register IIO device\n");
267 goto err_cleanup_eeprom_regmap;
268 }
269
270 i2c_set_clientdata(client, indio_dev);
271
272 return 0;
273
274err_cleanup_eeprom_regmap:
275 regmap_exit(priv->eeprom_regmap);
276
277err_cleanup_eeprom_client:
278 i2c_unregister_device(priv->eeprom_client);
279 return ret;
280}
281
282static int hp03_remove(struct i2c_client *client)
283{
284 struct iio_dev *indio_dev = i2c_get_clientdata(client);
285 struct hp03_priv *priv = iio_priv(indio_dev);
286
287 iio_device_unregister(indio_dev);
288 regmap_exit(priv->eeprom_regmap);
289 i2c_unregister_device(priv->eeprom_client);
290
291 return 0;
292}
293
294static const struct i2c_device_id hp03_id[] = {
295 { "hp03", 0 },
296 { },
297};
298MODULE_DEVICE_TABLE(i2c, hp03_id);
299
300static struct i2c_driver hp03_driver = {
301 .driver = {
302 .name = "hp03",
303 },
304 .probe = hp03_probe,
305 .remove = hp03_remove,
306 .id_table = hp03_id,
307};
308module_i2c_driver(hp03_driver);
309
310MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
311MODULE_DESCRIPTION("Driver for Hope RF HP03 pressure and temperature sensor");
312MODULE_LICENSE("GPL v2");