1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * mpl115.c - Support for Freescale MPL115A pressure/temperature sensor |
4 | * |
5 | * Copyright (c) 2014 Peter Meerwald <pmeerw@pmeerw.net> |
6 | * |
7 | * TODO: synchronization with system suspend |
8 | */ |
9 | |
10 | #include <linux/module.h> |
11 | #include <linux/iio/iio.h> |
12 | #include <linux/delay.h> |
13 | #include <linux/gpio/consumer.h> |
14 | |
15 | #include "mpl115.h" |
16 | |
17 | #define MPL115_PADC 0x00 /* pressure ADC output value, MSB first, 10 bit */ |
18 | #define MPL115_TADC 0x02 /* temperature ADC output value, MSB first, 10 bit */ |
19 | #define MPL115_A0 0x04 /* 12 bit integer, 3 bit fraction */ |
20 | #define MPL115_B1 0x06 /* 2 bit integer, 13 bit fraction */ |
21 | #define MPL115_B2 0x08 /* 1 bit integer, 14 bit fraction */ |
22 | #define MPL115_C12 0x0a /* 0 bit integer, 13 bit fraction */ |
23 | #define MPL115_CONVERT 0x12 /* convert temperature and pressure */ |
24 | |
25 | struct mpl115_data { |
26 | struct device *dev; |
27 | struct mutex lock; |
28 | s16 a0; |
29 | s16 b1, b2; |
30 | s16 c12; |
31 | struct gpio_desc *shutdown; |
32 | const struct mpl115_ops *ops; |
33 | }; |
34 | |
35 | static int mpl115_request(struct mpl115_data *data) |
36 | { |
37 | int ret = data->ops->write(data->dev, MPL115_CONVERT, 0); |
38 | |
39 | if (ret < 0) |
40 | return ret; |
41 | |
42 | usleep_range(min: 3000, max: 4000); |
43 | |
44 | return 0; |
45 | } |
46 | |
47 | static int mpl115_comp_pressure(struct mpl115_data *data, int *val, int *val2) |
48 | { |
49 | int ret; |
50 | u16 padc, tadc; |
51 | int a1, y1, pcomp; |
52 | unsigned kpa; |
53 | |
54 | mutex_lock(&data->lock); |
55 | ret = mpl115_request(data); |
56 | if (ret < 0) |
57 | goto done; |
58 | |
59 | ret = data->ops->read(data->dev, MPL115_PADC); |
60 | if (ret < 0) |
61 | goto done; |
62 | padc = ret >> 6; |
63 | |
64 | ret = data->ops->read(data->dev, MPL115_TADC); |
65 | if (ret < 0) |
66 | goto done; |
67 | tadc = ret >> 6; |
68 | |
69 | /* see Freescale AN3785 */ |
70 | a1 = data->b1 + ((data->c12 * tadc) >> 11); |
71 | y1 = (data->a0 << 10) + a1 * padc; |
72 | |
73 | /* compensated pressure with 4 fractional bits */ |
74 | pcomp = (y1 + ((data->b2 * (int) tadc) >> 1)) >> 9; |
75 | |
76 | kpa = pcomp * (115 - 50) / 1023 + (50 << 4); |
77 | *val = kpa >> 4; |
78 | *val2 = (kpa & 15) * (1000000 >> 4); |
79 | done: |
80 | mutex_unlock(lock: &data->lock); |
81 | return ret; |
82 | } |
83 | |
84 | static int mpl115_read_temp(struct mpl115_data *data) |
85 | { |
86 | int ret; |
87 | |
88 | mutex_lock(&data->lock); |
89 | ret = mpl115_request(data); |
90 | if (ret < 0) |
91 | goto done; |
92 | ret = data->ops->read(data->dev, MPL115_TADC); |
93 | done: |
94 | mutex_unlock(lock: &data->lock); |
95 | return ret; |
96 | } |
97 | |
98 | static int mpl115_read_raw(struct iio_dev *indio_dev, |
99 | struct iio_chan_spec const *chan, |
100 | int *val, int *val2, long mask) |
101 | { |
102 | struct mpl115_data *data = iio_priv(indio_dev); |
103 | int ret; |
104 | |
105 | switch (mask) { |
106 | case IIO_CHAN_INFO_PROCESSED: |
107 | pm_runtime_get_sync(dev: data->dev); |
108 | ret = mpl115_comp_pressure(data, val, val2); |
109 | if (ret < 0) |
110 | return ret; |
111 | pm_runtime_mark_last_busy(dev: data->dev); |
112 | pm_runtime_put_autosuspend(dev: data->dev); |
113 | |
114 | return IIO_VAL_INT_PLUS_MICRO; |
115 | case IIO_CHAN_INFO_RAW: |
116 | pm_runtime_get_sync(dev: data->dev); |
117 | /* temperature -5.35 C / LSB, 472 LSB is 25 C */ |
118 | ret = mpl115_read_temp(data); |
119 | if (ret < 0) |
120 | return ret; |
121 | pm_runtime_mark_last_busy(dev: data->dev); |
122 | pm_runtime_put_autosuspend(dev: data->dev); |
123 | *val = ret >> 6; |
124 | |
125 | return IIO_VAL_INT; |
126 | case IIO_CHAN_INFO_OFFSET: |
127 | *val = -605; |
128 | *val2 = 750000; |
129 | return IIO_VAL_INT_PLUS_MICRO; |
130 | case IIO_CHAN_INFO_SCALE: |
131 | *val = -186; |
132 | *val2 = 915888; |
133 | return IIO_VAL_INT_PLUS_MICRO; |
134 | } |
135 | return -EINVAL; |
136 | } |
137 | |
138 | static const struct iio_chan_spec mpl115_channels[] = { |
139 | { |
140 | .type = IIO_PRESSURE, |
141 | .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), |
142 | }, |
143 | { |
144 | .type = IIO_TEMP, |
145 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), |
146 | .info_mask_shared_by_type = |
147 | BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_SCALE), |
148 | }, |
149 | }; |
150 | |
151 | static const struct iio_info mpl115_info = { |
152 | .read_raw = &mpl115_read_raw, |
153 | }; |
154 | |
155 | int mpl115_probe(struct device *dev, const char *name, |
156 | const struct mpl115_ops *ops) |
157 | { |
158 | struct mpl115_data *data; |
159 | struct iio_dev *indio_dev; |
160 | int ret; |
161 | |
162 | indio_dev = devm_iio_device_alloc(parent: dev, sizeof_priv: sizeof(*data)); |
163 | if (!indio_dev) |
164 | return -ENOMEM; |
165 | |
166 | data = iio_priv(indio_dev); |
167 | data->dev = dev; |
168 | data->ops = ops; |
169 | mutex_init(&data->lock); |
170 | |
171 | indio_dev->info = &mpl115_info; |
172 | indio_dev->name = name; |
173 | indio_dev->modes = INDIO_DIRECT_MODE; |
174 | indio_dev->channels = mpl115_channels; |
175 | indio_dev->num_channels = ARRAY_SIZE(mpl115_channels); |
176 | |
177 | ret = data->ops->init(data->dev); |
178 | if (ret) |
179 | return ret; |
180 | |
181 | dev_set_drvdata(dev, data: indio_dev); |
182 | |
183 | ret = data->ops->read(data->dev, MPL115_A0); |
184 | if (ret < 0) |
185 | return ret; |
186 | data->a0 = ret; |
187 | ret = data->ops->read(data->dev, MPL115_B1); |
188 | if (ret < 0) |
189 | return ret; |
190 | data->b1 = ret; |
191 | ret = data->ops->read(data->dev, MPL115_B2); |
192 | if (ret < 0) |
193 | return ret; |
194 | data->b2 = ret; |
195 | ret = data->ops->read(data->dev, MPL115_C12); |
196 | if (ret < 0) |
197 | return ret; |
198 | data->c12 = ret; |
199 | |
200 | data->shutdown = devm_gpiod_get_optional(dev, con_id: "shutdown" , |
201 | flags: GPIOD_OUT_LOW); |
202 | if (IS_ERR(ptr: data->shutdown)) |
203 | return dev_err_probe(dev, err: PTR_ERR(ptr: data->shutdown), |
204 | fmt: "cannot get shutdown gpio\n" ); |
205 | |
206 | if (data->shutdown) { |
207 | /* Enable runtime PM */ |
208 | pm_runtime_get_noresume(dev); |
209 | pm_runtime_set_active(dev); |
210 | pm_runtime_enable(dev); |
211 | |
212 | /* |
213 | * As the device takes 3 ms to come up with a fresh |
214 | * reading after power-on and 5 ms to actually power-on, |
215 | * do not shut it down unnecessarily. Set autosuspend to |
216 | * 2000 ms. |
217 | */ |
218 | pm_runtime_set_autosuspend_delay(dev, delay: 2000); |
219 | pm_runtime_use_autosuspend(dev); |
220 | pm_runtime_put(dev); |
221 | |
222 | dev_dbg(dev, "low-power mode enabled" ); |
223 | } else |
224 | dev_dbg(dev, "low-power mode disabled" ); |
225 | |
226 | return devm_iio_device_register(dev, indio_dev); |
227 | } |
228 | EXPORT_SYMBOL_NS_GPL(mpl115_probe, IIO_MPL115); |
229 | |
230 | static int mpl115_runtime_suspend(struct device *dev) |
231 | { |
232 | struct mpl115_data *data = iio_priv(indio_dev: dev_get_drvdata(dev)); |
233 | |
234 | gpiod_set_value(desc: data->shutdown, value: 1); |
235 | |
236 | return 0; |
237 | } |
238 | |
239 | static int mpl115_runtime_resume(struct device *dev) |
240 | { |
241 | struct mpl115_data *data = iio_priv(indio_dev: dev_get_drvdata(dev)); |
242 | |
243 | gpiod_set_value(desc: data->shutdown, value: 0); |
244 | usleep_range(min: 5000, max: 6000); |
245 | |
246 | return 0; |
247 | } |
248 | |
249 | EXPORT_NS_RUNTIME_DEV_PM_OPS(mpl115_dev_pm_ops, mpl115_runtime_suspend, |
250 | mpl115_runtime_resume, NULL, IIO_MPL115); |
251 | |
252 | MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>" ); |
253 | MODULE_DESCRIPTION("Freescale MPL115 pressure/temperature driver" ); |
254 | MODULE_LICENSE("GPL" ); |
255 | |