xref: /openbmc/linux/drivers/iio/pressure/mpl115.c (revision f3539c12)
1 /*
2  * mpl115.c - Support for Freescale MPL115A pressure/temperature sensor
3  *
4  * Copyright (c) 2014 Peter Meerwald <pmeerw@pmeerw.net>
5  *
6  * This file is subject to the terms and conditions of version 2 of
7  * the GNU General Public License.  See the file COPYING in the main
8  * directory of this archive for more details.
9  *
10  * TODO: shutdown pin
11  *
12  */
13 
14 #include <linux/module.h>
15 #include <linux/iio/iio.h>
16 #include <linux/delay.h>
17 
18 #include "mpl115.h"
19 
20 #define MPL115_PADC 0x00 /* pressure ADC output value, MSB first, 10 bit */
21 #define MPL115_TADC 0x02 /* temperature ADC output value, MSB first, 10 bit */
22 #define MPL115_A0 0x04 /* 12 bit integer, 3 bit fraction */
23 #define MPL115_B1 0x06 /* 2 bit integer, 13 bit fraction */
24 #define MPL115_B2 0x08 /* 1 bit integer, 14 bit fraction */
25 #define MPL115_C12 0x0a /* 0 bit integer, 13 bit fraction */
26 #define MPL115_CONVERT 0x12 /* convert temperature and pressure */
27 
28 struct mpl115_data {
29 	struct device *dev;
30 	struct mutex lock;
31 	s16 a0;
32 	s16 b1, b2;
33 	s16 c12;
34 	const struct mpl115_ops *ops;
35 };
36 
37 static int mpl115_request(struct mpl115_data *data)
38 {
39 	int ret = data->ops->write(data->dev, MPL115_CONVERT, 0);
40 
41 	if (ret < 0)
42 		return ret;
43 
44 	usleep_range(3000, 4000);
45 
46 	return 0;
47 }
48 
49 static int mpl115_comp_pressure(struct mpl115_data *data, int *val, int *val2)
50 {
51 	int ret;
52 	u16 padc, tadc;
53 	int a1, y1, pcomp;
54 	unsigned kpa;
55 
56 	mutex_lock(&data->lock);
57 	ret = mpl115_request(data);
58 	if (ret < 0)
59 		goto done;
60 
61 	ret = data->ops->read(data->dev, MPL115_PADC);
62 	if (ret < 0)
63 		goto done;
64 	padc = ret >> 6;
65 
66 	ret = data->ops->read(data->dev, MPL115_TADC);
67 	if (ret < 0)
68 		goto done;
69 	tadc = ret >> 6;
70 
71 	/* see Freescale AN3785 */
72 	a1 = data->b1 + ((data->c12 * tadc) >> 11);
73 	y1 = (data->a0 << 10) + a1 * padc;
74 
75 	/* compensated pressure with 4 fractional bits */
76 	pcomp = (y1 + ((data->b2 * (int) tadc) >> 1)) >> 9;
77 
78 	kpa = pcomp * (115 - 50) / 1023 + (50 << 4);
79 	*val = kpa >> 4;
80 	*val2 = (kpa & 15) * (1000000 >> 4);
81 done:
82 	mutex_unlock(&data->lock);
83 	return ret;
84 }
85 
86 static int mpl115_read_temp(struct mpl115_data *data)
87 {
88 	int ret;
89 
90 	mutex_lock(&data->lock);
91 	ret = mpl115_request(data);
92 	if (ret < 0)
93 		goto done;
94 	ret = data->ops->read(data->dev, MPL115_TADC);
95 done:
96 	mutex_unlock(&data->lock);
97 	return ret;
98 }
99 
100 static int mpl115_read_raw(struct iio_dev *indio_dev,
101 			    struct iio_chan_spec const *chan,
102 			    int *val, int *val2, long mask)
103 {
104 	struct mpl115_data *data = iio_priv(indio_dev);
105 	int ret;
106 
107 	switch (mask) {
108 	case IIO_CHAN_INFO_PROCESSED:
109 		ret = mpl115_comp_pressure(data, val, val2);
110 		if (ret < 0)
111 			return ret;
112 		return IIO_VAL_INT_PLUS_MICRO;
113 	case IIO_CHAN_INFO_RAW:
114 		/* temperature -5.35 C / LSB, 472 LSB is 25 C */
115 		ret = mpl115_read_temp(data);
116 		if (ret < 0)
117 			return ret;
118 		*val = ret >> 6;
119 		return IIO_VAL_INT;
120 	case IIO_CHAN_INFO_OFFSET:
121 		*val = -605;
122 		*val2 = 750000;
123 		return IIO_VAL_INT_PLUS_MICRO;
124 	case IIO_CHAN_INFO_SCALE:
125 		*val = -186;
126 		*val2 = 915888;
127 		return IIO_VAL_INT_PLUS_MICRO;
128 	}
129 	return -EINVAL;
130 }
131 
132 static const struct iio_chan_spec mpl115_channels[] = {
133 	{
134 		.type = IIO_PRESSURE,
135 		.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
136 	},
137 	{
138 		.type = IIO_TEMP,
139 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
140 			BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_SCALE),
141 	},
142 };
143 
144 static const struct iio_info mpl115_info = {
145 	.read_raw = &mpl115_read_raw,
146 	.driver_module = THIS_MODULE,
147 };
148 
149 int mpl115_probe(struct device *dev, const char *name,
150 			const struct mpl115_ops *ops)
151 {
152 	struct mpl115_data *data;
153 	struct iio_dev *indio_dev;
154 	int ret;
155 
156 	indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
157 	if (!indio_dev)
158 		return -ENOMEM;
159 
160 	data = iio_priv(indio_dev);
161 	data->dev = dev;
162 	data->ops = ops;
163 	mutex_init(&data->lock);
164 
165 	indio_dev->info = &mpl115_info;
166 	indio_dev->name = name;
167 	indio_dev->dev.parent = dev;
168 	indio_dev->modes = INDIO_DIRECT_MODE;
169 	indio_dev->channels = mpl115_channels;
170 	indio_dev->num_channels = ARRAY_SIZE(mpl115_channels);
171 
172 	ret = data->ops->init(data->dev);
173 	if (ret)
174 		return ret;
175 
176 	ret = data->ops->read(data->dev, MPL115_A0);
177 	if (ret < 0)
178 		return ret;
179 	data->a0 = ret;
180 	ret = data->ops->read(data->dev, MPL115_B1);
181 	if (ret < 0)
182 		return ret;
183 	data->b1 = ret;
184 	ret = data->ops->read(data->dev, MPL115_B2);
185 	if (ret < 0)
186 		return ret;
187 	data->b2 = ret;
188 	ret = data->ops->read(data->dev, MPL115_C12);
189 	if (ret < 0)
190 		return ret;
191 	data->c12 = ret;
192 
193 	return devm_iio_device_register(dev, indio_dev);
194 }
195 EXPORT_SYMBOL_GPL(mpl115_probe);
196 
197 MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>");
198 MODULE_DESCRIPTION("Freescale MPL115 pressure/temperature driver");
199 MODULE_LICENSE("GPL");
200