Commit | Line | Data |
---|---|---|
48458b0c GG |
1 | /* |
2 | * cros_ec_light_prox - Driver for light and prox sensors behing CrosEC. | |
3 | * | |
4 | * Copyright (C) 2017 Google, Inc | |
5 | * | |
6 | * This software is licensed under the terms of the GNU General Public | |
7 | * License version 2, as published by the Free Software Foundation, and | |
8 | * may be copied, distributed, and modified under those terms. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | * GNU General Public License for more details. | |
14 | */ | |
15 | ||
16 | #include <linux/delay.h> | |
17 | #include <linux/device.h> | |
18 | #include <linux/iio/buffer.h> | |
5a0b8cb4 | 19 | #include <linux/iio/common/cros_ec_sensors_core.h> |
48458b0c GG |
20 | #include <linux/iio/iio.h> |
21 | #include <linux/iio/kfifo_buf.h> | |
22 | #include <linux/iio/trigger.h> | |
23 | #include <linux/iio/triggered_buffer.h> | |
24 | #include <linux/iio/trigger_consumer.h> | |
25 | #include <linux/kernel.h> | |
26 | #include <linux/mfd/cros_ec.h> | |
27 | #include <linux/mfd/cros_ec_commands.h> | |
28 | #include <linux/module.h> | |
29 | #include <linux/platform_device.h> | |
30 | #include <linux/slab.h> | |
31 | #include <linux/sysfs.h> | |
32 | ||
48458b0c GG |
33 | /* |
34 | * We only represent one entry for light or proximity. EC is merging different | |
35 | * light sensors to return the what the eye would see. For proximity, we | |
36 | * currently support only one light source. | |
37 | */ | |
38 | #define CROS_EC_LIGHT_PROX_MAX_CHANNELS (1 + 1) | |
39 | ||
40 | /* State data for ec_sensors iio driver. */ | |
41 | struct cros_ec_light_prox_state { | |
42 | /* Shared by all sensors */ | |
43 | struct cros_ec_sensors_core_state core; | |
44 | ||
45 | struct iio_chan_spec channels[CROS_EC_LIGHT_PROX_MAX_CHANNELS]; | |
46 | }; | |
47 | ||
48 | static int cros_ec_light_prox_read(struct iio_dev *indio_dev, | |
49 | struct iio_chan_spec const *chan, | |
50 | int *val, int *val2, long mask) | |
51 | { | |
52 | struct cros_ec_light_prox_state *st = iio_priv(indio_dev); | |
53 | u16 data = 0; | |
54 | s64 val64; | |
55 | int ret = IIO_VAL_INT; | |
56 | int idx = chan->scan_index; | |
57 | ||
58 | mutex_lock(&st->core.cmd_lock); | |
59 | ||
60 | switch (mask) { | |
61 | case IIO_CHAN_INFO_RAW: | |
62 | if (chan->type == IIO_PROXIMITY) { | |
63 | if (cros_ec_sensors_read_cmd(indio_dev, 1 << idx, | |
64 | (s16 *)&data) < 0) { | |
65 | ret = -EIO; | |
66 | break; | |
67 | } | |
68 | *val = data; | |
69 | } else { | |
70 | ret = -EINVAL; | |
71 | } | |
72 | break; | |
73 | case IIO_CHAN_INFO_PROCESSED: | |
74 | if (chan->type == IIO_LIGHT) { | |
75 | if (cros_ec_sensors_read_cmd(indio_dev, 1 << idx, | |
76 | (s16 *)&data) < 0) { | |
77 | ret = -EIO; | |
78 | break; | |
79 | } | |
80 | /* | |
81 | * The data coming from the light sensor is | |
82 | * pre-processed and represents the ambient light | |
83 | * illuminance reading expressed in lux. | |
84 | */ | |
85 | *val = data; | |
86 | ret = IIO_VAL_INT; | |
87 | } else { | |
88 | ret = -EINVAL; | |
89 | } | |
90 | break; | |
91 | case IIO_CHAN_INFO_CALIBBIAS: | |
92 | st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_OFFSET; | |
93 | st->core.param.sensor_offset.flags = 0; | |
94 | ||
95 | if (cros_ec_motion_send_host_cmd(&st->core, 0)) { | |
96 | ret = -EIO; | |
97 | break; | |
98 | } | |
99 | ||
100 | /* Save values */ | |
101 | st->core.calib[0] = st->core.resp->sensor_offset.offset[0]; | |
102 | ||
103 | *val = st->core.calib[idx]; | |
104 | break; | |
105 | case IIO_CHAN_INFO_CALIBSCALE: | |
106 | /* | |
107 | * RANGE is used for calibration | |
108 | * scale is a number x.y, where x is coded on 16 bits, | |
109 | * y coded on 16 bits, between 0 and 9999. | |
110 | */ | |
111 | st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_RANGE; | |
112 | st->core.param.sensor_range.data = EC_MOTION_SENSE_NO_VALUE; | |
113 | ||
114 | if (cros_ec_motion_send_host_cmd(&st->core, 0)) { | |
115 | ret = -EIO; | |
116 | break; | |
117 | } | |
118 | ||
119 | val64 = st->core.resp->sensor_range.ret; | |
120 | *val = val64 >> 16; | |
121 | *val2 = (val64 & 0xffff) * 100; | |
122 | ret = IIO_VAL_INT_PLUS_MICRO; | |
123 | break; | |
124 | default: | |
125 | ret = cros_ec_sensors_core_read(&st->core, chan, val, val2, | |
126 | mask); | |
127 | break; | |
128 | } | |
129 | ||
130 | mutex_unlock(&st->core.cmd_lock); | |
131 | ||
132 | return ret; | |
133 | } | |
134 | ||
135 | static int cros_ec_light_prox_write(struct iio_dev *indio_dev, | |
136 | struct iio_chan_spec const *chan, | |
137 | int val, int val2, long mask) | |
138 | { | |
139 | struct cros_ec_light_prox_state *st = iio_priv(indio_dev); | |
140 | int ret = 0; | |
141 | int idx = chan->scan_index; | |
142 | ||
143 | mutex_lock(&st->core.cmd_lock); | |
144 | ||
145 | switch (mask) { | |
146 | case IIO_CHAN_INFO_CALIBBIAS: | |
147 | st->core.calib[idx] = val; | |
148 | /* Send to EC for each axis, even if not complete */ | |
149 | st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_OFFSET; | |
150 | st->core.param.sensor_offset.flags = MOTION_SENSE_SET_OFFSET; | |
151 | st->core.param.sensor_offset.offset[0] = st->core.calib[0]; | |
152 | st->core.param.sensor_offset.temp = | |
153 | EC_MOTION_SENSE_INVALID_CALIB_TEMP; | |
154 | if (cros_ec_motion_send_host_cmd(&st->core, 0)) | |
155 | ret = -EIO; | |
156 | break; | |
157 | case IIO_CHAN_INFO_CALIBSCALE: | |
158 | st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_RANGE; | |
159 | st->core.param.sensor_range.data = (val << 16) | (val2 / 100); | |
160 | if (cros_ec_motion_send_host_cmd(&st->core, 0)) | |
161 | ret = -EIO; | |
162 | break; | |
163 | default: | |
164 | ret = cros_ec_sensors_core_write(&st->core, chan, val, val2, | |
165 | mask); | |
166 | break; | |
167 | } | |
168 | ||
169 | mutex_unlock(&st->core.cmd_lock); | |
170 | ||
171 | return ret; | |
172 | } | |
173 | ||
174 | static const struct iio_info cros_ec_light_prox_info = { | |
175 | .read_raw = &cros_ec_light_prox_read, | |
176 | .write_raw = &cros_ec_light_prox_write, | |
48458b0c GG |
177 | }; |
178 | ||
179 | static int cros_ec_light_prox_probe(struct platform_device *pdev) | |
180 | { | |
181 | struct device *dev = &pdev->dev; | |
182 | struct cros_ec_dev *ec_dev = dev_get_drvdata(dev->parent); | |
48458b0c GG |
183 | struct iio_dev *indio_dev; |
184 | struct cros_ec_light_prox_state *state; | |
185 | struct iio_chan_spec *channel; | |
186 | int ret; | |
187 | ||
188 | if (!ec_dev || !ec_dev->ec_dev) { | |
189 | dev_warn(dev, "No CROS EC device found.\n"); | |
190 | return -EINVAL; | |
191 | } | |
48458b0c GG |
192 | |
193 | indio_dev = devm_iio_device_alloc(dev, sizeof(*state)); | |
194 | if (!indio_dev) | |
195 | return -ENOMEM; | |
196 | ||
197 | ret = cros_ec_sensors_core_init(pdev, indio_dev, true); | |
198 | if (ret) | |
199 | return ret; | |
200 | ||
201 | indio_dev->info = &cros_ec_light_prox_info; | |
202 | state = iio_priv(indio_dev); | |
203 | state->core.type = state->core.resp->info.type; | |
204 | state->core.loc = state->core.resp->info.location; | |
205 | channel = state->channels; | |
206 | ||
207 | /* Common part */ | |
208 | channel->info_mask_shared_by_all = | |
209 | BIT(IIO_CHAN_INFO_SAMP_FREQ) | | |
210 | BIT(IIO_CHAN_INFO_FREQUENCY); | |
211 | channel->scan_type.realbits = CROS_EC_SENSOR_BITS; | |
212 | channel->scan_type.storagebits = CROS_EC_SENSOR_BITS; | |
213 | channel->scan_type.shift = 0; | |
214 | channel->scan_index = 0; | |
215 | channel->ext_info = cros_ec_sensors_ext_info; | |
216 | channel->scan_type.sign = 'u'; | |
217 | ||
218 | state->core.calib[0] = 0; | |
219 | ||
220 | /* Sensor specific */ | |
221 | switch (state->core.type) { | |
222 | case MOTIONSENSE_TYPE_LIGHT: | |
223 | channel->type = IIO_LIGHT; | |
224 | channel->info_mask_separate = | |
225 | BIT(IIO_CHAN_INFO_PROCESSED) | | |
226 | BIT(IIO_CHAN_INFO_CALIBBIAS) | | |
227 | BIT(IIO_CHAN_INFO_CALIBSCALE); | |
228 | break; | |
229 | case MOTIONSENSE_TYPE_PROX: | |
230 | channel->type = IIO_PROXIMITY; | |
231 | channel->info_mask_separate = | |
232 | BIT(IIO_CHAN_INFO_RAW) | | |
233 | BIT(IIO_CHAN_INFO_CALIBBIAS) | | |
234 | BIT(IIO_CHAN_INFO_CALIBSCALE); | |
235 | break; | |
236 | default: | |
237 | dev_warn(dev, "Unknown motion sensor\n"); | |
238 | return -EINVAL; | |
239 | } | |
240 | ||
241 | /* Timestamp */ | |
242 | channel++; | |
243 | channel->type = IIO_TIMESTAMP; | |
244 | channel->channel = -1; | |
245 | channel->scan_index = 1; | |
246 | channel->scan_type.sign = 's'; | |
247 | channel->scan_type.realbits = 64; | |
248 | channel->scan_type.storagebits = 64; | |
249 | ||
250 | indio_dev->channels = state->channels; | |
251 | ||
252 | indio_dev->num_channels = CROS_EC_LIGHT_PROX_MAX_CHANNELS; | |
253 | ||
254 | state->core.read_ec_sensors_data = cros_ec_sensors_read_cmd; | |
255 | ||
256 | ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL, | |
257 | cros_ec_sensors_capture, NULL); | |
258 | if (ret) | |
259 | return ret; | |
260 | ||
261 | return devm_iio_device_register(dev, indio_dev); | |
262 | } | |
263 | ||
264 | static const struct platform_device_id cros_ec_light_prox_ids[] = { | |
265 | { | |
266 | .name = "cros-ec-prox", | |
267 | }, | |
268 | { | |
269 | .name = "cros-ec-light", | |
270 | }, | |
271 | { /* sentinel */ } | |
272 | }; | |
273 | MODULE_DEVICE_TABLE(platform, cros_ec_light_prox_ids); | |
274 | ||
275 | static struct platform_driver cros_ec_light_prox_platform_driver = { | |
276 | .driver = { | |
277 | .name = "cros-ec-light-prox", | |
0dad1ece | 278 | .pm = &cros_ec_sensors_pm_ops, |
48458b0c GG |
279 | }, |
280 | .probe = cros_ec_light_prox_probe, | |
281 | .id_table = cros_ec_light_prox_ids, | |
282 | }; | |
283 | module_platform_driver(cros_ec_light_prox_platform_driver); | |
284 | ||
285 | MODULE_DESCRIPTION("ChromeOS EC light/proximity sensors driver"); | |
286 | MODULE_LICENSE("GPL v2"); |