Commit | Line | Data |
---|---|---|
3017d90e | 1 | /* |
c984b9cb | 2 | * mpl115.c - Support for Freescale MPL115A pressure/temperature sensor |
3017d90e PM |
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 | * | |
3017d90e PM |
10 | * TODO: shutdown pin |
11 | * | |
12 | */ | |
13 | ||
14 | #include <linux/module.h> | |
3017d90e PM |
15 | #include <linux/iio/iio.h> |
16 | #include <linux/delay.h> | |
17 | ||
c984b9cb AM |
18 | #include "mpl115.h" |
19 | ||
3017d90e PM |
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 { | |
c984b9cb | 29 | struct device *dev; |
3017d90e PM |
30 | struct mutex lock; |
31 | s16 a0; | |
32 | s16 b1, b2; | |
33 | s16 c12; | |
c984b9cb | 34 | const struct mpl115_ops *ops; |
3017d90e PM |
35 | }; |
36 | ||
37 | static int mpl115_request(struct mpl115_data *data) | |
38 | { | |
c984b9cb AM |
39 | int ret = data->ops->write(data->dev, MPL115_CONVERT, 0); |
40 | ||
3017d90e PM |
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 | ||
c984b9cb | 61 | ret = data->ops->read(data->dev, MPL115_PADC); |
3017d90e PM |
62 | if (ret < 0) |
63 | goto done; | |
64 | padc = ret >> 6; | |
65 | ||
c984b9cb | 66 | ret = data->ops->read(data->dev, MPL115_TADC); |
3017d90e PM |
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; | |
c984b9cb | 94 | ret = data->ops->read(data->dev, MPL115_TADC); |
3017d90e PM |
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: | |
431386e7 | 121 | *val = -605; |
3017d90e PM |
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), | |
6a6e1d56 | 140 | .info_mask_shared_by_type = |
3017d90e PM |
141 | BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_SCALE), |
142 | }, | |
143 | }; | |
144 | ||
145 | static const struct iio_info mpl115_info = { | |
146 | .read_raw = &mpl115_read_raw, | |
147 | .driver_module = THIS_MODULE, | |
148 | }; | |
149 | ||
c984b9cb AM |
150 | int mpl115_probe(struct device *dev, const char *name, |
151 | const struct mpl115_ops *ops) | |
3017d90e PM |
152 | { |
153 | struct mpl115_data *data; | |
154 | struct iio_dev *indio_dev; | |
155 | int ret; | |
156 | ||
c984b9cb | 157 | indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); |
3017d90e PM |
158 | if (!indio_dev) |
159 | return -ENOMEM; | |
160 | ||
161 | data = iio_priv(indio_dev); | |
c984b9cb AM |
162 | data->dev = dev; |
163 | data->ops = ops; | |
3017d90e PM |
164 | mutex_init(&data->lock); |
165 | ||
3017d90e | 166 | indio_dev->info = &mpl115_info; |
c984b9cb AM |
167 | indio_dev->name = name; |
168 | indio_dev->dev.parent = dev; | |
3017d90e PM |
169 | indio_dev->modes = INDIO_DIRECT_MODE; |
170 | indio_dev->channels = mpl115_channels; | |
171 | indio_dev->num_channels = ARRAY_SIZE(mpl115_channels); | |
172 | ||
c984b9cb AM |
173 | ret = data->ops->init(data->dev); |
174 | if (ret) | |
175 | return ret; | |
176 | ||
177 | ret = data->ops->read(data->dev, MPL115_A0); | |
3017d90e PM |
178 | if (ret < 0) |
179 | return ret; | |
180 | data->a0 = ret; | |
c984b9cb | 181 | ret = data->ops->read(data->dev, MPL115_B1); |
3017d90e PM |
182 | if (ret < 0) |
183 | return ret; | |
184 | data->b1 = ret; | |
c984b9cb | 185 | ret = data->ops->read(data->dev, MPL115_B2); |
3017d90e PM |
186 | if (ret < 0) |
187 | return ret; | |
188 | data->b2 = ret; | |
c984b9cb | 189 | ret = data->ops->read(data->dev, MPL115_C12); |
3017d90e PM |
190 | if (ret < 0) |
191 | return ret; | |
192 | data->c12 = ret; | |
193 | ||
c984b9cb | 194 | return devm_iio_device_register(dev, indio_dev); |
3017d90e | 195 | } |
c984b9cb | 196 | EXPORT_SYMBOL_GPL(mpl115_probe); |
3017d90e PM |
197 | |
198 | MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>"); | |
199 | MODULE_DESCRIPTION("Freescale MPL115 pressure/temperature driver"); | |
200 | MODULE_LICENSE("GPL"); |