diff options
Diffstat (limited to 'drivers/gnss/sirf.c')
-rw-r--r-- | drivers/gnss/sirf.c | 407 |
1 files changed, 407 insertions, 0 deletions
diff --git a/drivers/gnss/sirf.c b/drivers/gnss/sirf.c new file mode 100644 index 000000000000..5fb0f730db48 --- /dev/null +++ b/drivers/gnss/sirf.c | |||
@@ -0,0 +1,407 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * SiRFstar GNSS receiver driver | ||
4 | * | ||
5 | * Copyright (C) 2018 Johan Hovold <johan@kernel.org> | ||
6 | */ | ||
7 | |||
8 | #include <linux/errno.h> | ||
9 | #include <linux/gnss.h> | ||
10 | #include <linux/gpio/consumer.h> | ||
11 | #include <linux/init.h> | ||
12 | #include <linux/interrupt.h> | ||
13 | #include <linux/kernel.h> | ||
14 | #include <linux/module.h> | ||
15 | #include <linux/of.h> | ||
16 | #include <linux/pm.h> | ||
17 | #include <linux/pm_runtime.h> | ||
18 | #include <linux/regulator/consumer.h> | ||
19 | #include <linux/serdev.h> | ||
20 | #include <linux/slab.h> | ||
21 | #include <linux/wait.h> | ||
22 | |||
23 | #define SIRF_BOOT_DELAY 500 | ||
24 | #define SIRF_ON_OFF_PULSE_TIME 100 | ||
25 | #define SIRF_ACTIVATE_TIMEOUT 200 | ||
26 | #define SIRF_HIBERNATE_TIMEOUT 200 | ||
27 | |||
28 | struct sirf_data { | ||
29 | struct gnss_device *gdev; | ||
30 | struct serdev_device *serdev; | ||
31 | speed_t speed; | ||
32 | struct regulator *vcc; | ||
33 | struct gpio_desc *on_off; | ||
34 | struct gpio_desc *wakeup; | ||
35 | int irq; | ||
36 | bool active; | ||
37 | wait_queue_head_t power_wait; | ||
38 | }; | ||
39 | |||
40 | static int sirf_open(struct gnss_device *gdev) | ||
41 | { | ||
42 | struct sirf_data *data = gnss_get_drvdata(gdev); | ||
43 | struct serdev_device *serdev = data->serdev; | ||
44 | int ret; | ||
45 | |||
46 | ret = serdev_device_open(serdev); | ||
47 | if (ret) | ||
48 | return ret; | ||
49 | |||
50 | serdev_device_set_baudrate(serdev, data->speed); | ||
51 | serdev_device_set_flow_control(serdev, false); | ||
52 | |||
53 | ret = pm_runtime_get_sync(&serdev->dev); | ||
54 | if (ret < 0) { | ||
55 | dev_err(&gdev->dev, "failed to runtime resume: %d\n", ret); | ||
56 | pm_runtime_put_noidle(&serdev->dev); | ||
57 | goto err_close; | ||
58 | } | ||
59 | |||
60 | return 0; | ||
61 | |||
62 | err_close: | ||
63 | serdev_device_close(serdev); | ||
64 | |||
65 | return ret; | ||
66 | } | ||
67 | |||
68 | static void sirf_close(struct gnss_device *gdev) | ||
69 | { | ||
70 | struct sirf_data *data = gnss_get_drvdata(gdev); | ||
71 | struct serdev_device *serdev = data->serdev; | ||
72 | |||
73 | serdev_device_close(serdev); | ||
74 | |||
75 | pm_runtime_put(&serdev->dev); | ||
76 | } | ||
77 | |||
78 | static int sirf_write_raw(struct gnss_device *gdev, const unsigned char *buf, | ||
79 | size_t count) | ||
80 | { | ||
81 | struct sirf_data *data = gnss_get_drvdata(gdev); | ||
82 | struct serdev_device *serdev = data->serdev; | ||
83 | int ret; | ||
84 | |||
85 | /* write is only buffered synchronously */ | ||
86 | ret = serdev_device_write(serdev, buf, count, 0); | ||
87 | if (ret < 0) | ||
88 | return ret; | ||
89 | |||
90 | /* FIXME: determine if interrupted? */ | ||
91 | serdev_device_wait_until_sent(serdev, 0); | ||
92 | |||
93 | return count; | ||
94 | } | ||
95 | |||
96 | static const struct gnss_operations sirf_gnss_ops = { | ||
97 | .open = sirf_open, | ||
98 | .close = sirf_close, | ||
99 | .write_raw = sirf_write_raw, | ||
100 | }; | ||
101 | |||
102 | static int sirf_receive_buf(struct serdev_device *serdev, | ||
103 | const unsigned char *buf, size_t count) | ||
104 | { | ||
105 | struct sirf_data *data = serdev_device_get_drvdata(serdev); | ||
106 | struct gnss_device *gdev = data->gdev; | ||
107 | |||
108 | return gnss_insert_raw(gdev, buf, count); | ||
109 | } | ||
110 | |||
111 | static const struct serdev_device_ops sirf_serdev_ops = { | ||
112 | .receive_buf = sirf_receive_buf, | ||
113 | .write_wakeup = serdev_device_write_wakeup, | ||
114 | }; | ||
115 | |||
116 | static irqreturn_t sirf_wakeup_handler(int irq, void *dev_id) | ||
117 | { | ||
118 | struct sirf_data *data = dev_id; | ||
119 | struct device *dev = &data->serdev->dev; | ||
120 | int ret; | ||
121 | |||
122 | ret = gpiod_get_value_cansleep(data->wakeup); | ||
123 | dev_dbg(dev, "%s - wakeup = %d\n", __func__, ret); | ||
124 | if (ret < 0) | ||
125 | goto out; | ||
126 | |||
127 | data->active = !!ret; | ||
128 | wake_up_interruptible(&data->power_wait); | ||
129 | out: | ||
130 | return IRQ_HANDLED; | ||
131 | } | ||
132 | |||
133 | static int sirf_wait_for_power_state(struct sirf_data *data, bool active, | ||
134 | unsigned long timeout) | ||
135 | { | ||
136 | int ret; | ||
137 | |||
138 | ret = wait_event_interruptible_timeout(data->power_wait, | ||
139 | data->active == active, msecs_to_jiffies(timeout)); | ||
140 | if (ret < 0) | ||
141 | return ret; | ||
142 | |||
143 | if (ret == 0) { | ||
144 | dev_warn(&data->serdev->dev, "timeout waiting for active state = %d\n", | ||
145 | active); | ||
146 | return -ETIMEDOUT; | ||
147 | } | ||
148 | |||
149 | return 0; | ||
150 | } | ||
151 | |||
152 | static void sirf_pulse_on_off(struct sirf_data *data) | ||
153 | { | ||
154 | gpiod_set_value_cansleep(data->on_off, 1); | ||
155 | msleep(SIRF_ON_OFF_PULSE_TIME); | ||
156 | gpiod_set_value_cansleep(data->on_off, 0); | ||
157 | } | ||
158 | |||
159 | static int sirf_set_active(struct sirf_data *data, bool active) | ||
160 | { | ||
161 | unsigned long timeout; | ||
162 | int retries = 3; | ||
163 | int ret; | ||
164 | |||
165 | if (active) | ||
166 | timeout = SIRF_ACTIVATE_TIMEOUT; | ||
167 | else | ||
168 | timeout = SIRF_HIBERNATE_TIMEOUT; | ||
169 | |||
170 | while (retries-- > 0) { | ||
171 | sirf_pulse_on_off(data); | ||
172 | ret = sirf_wait_for_power_state(data, active, timeout); | ||
173 | if (ret < 0) { | ||
174 | if (ret == -ETIMEDOUT) | ||
175 | continue; | ||
176 | |||
177 | return ret; | ||
178 | } | ||
179 | |||
180 | break; | ||
181 | } | ||
182 | |||
183 | if (retries == 0) | ||
184 | return -ETIMEDOUT; | ||
185 | |||
186 | return 0; | ||
187 | } | ||
188 | |||
189 | static int sirf_runtime_suspend(struct device *dev) | ||
190 | { | ||
191 | struct sirf_data *data = dev_get_drvdata(dev); | ||
192 | |||
193 | if (!data->on_off) | ||
194 | return regulator_disable(data->vcc); | ||
195 | |||
196 | return sirf_set_active(data, false); | ||
197 | } | ||
198 | |||
199 | static int sirf_runtime_resume(struct device *dev) | ||
200 | { | ||
201 | struct sirf_data *data = dev_get_drvdata(dev); | ||
202 | |||
203 | if (!data->on_off) | ||
204 | return regulator_enable(data->vcc); | ||
205 | |||
206 | return sirf_set_active(data, true); | ||
207 | } | ||
208 | |||
209 | static int __maybe_unused sirf_suspend(struct device *dev) | ||
210 | { | ||
211 | struct sirf_data *data = dev_get_drvdata(dev); | ||
212 | int ret = 0; | ||
213 | |||
214 | if (!pm_runtime_suspended(dev)) | ||
215 | ret = sirf_runtime_suspend(dev); | ||
216 | |||
217 | if (data->wakeup) | ||
218 | disable_irq(data->irq); | ||
219 | |||
220 | return ret; | ||
221 | } | ||
222 | |||
223 | static int __maybe_unused sirf_resume(struct device *dev) | ||
224 | { | ||
225 | struct sirf_data *data = dev_get_drvdata(dev); | ||
226 | int ret = 0; | ||
227 | |||
228 | if (data->wakeup) | ||
229 | enable_irq(data->irq); | ||
230 | |||
231 | if (!pm_runtime_suspended(dev)) | ||
232 | ret = sirf_runtime_resume(dev); | ||
233 | |||
234 | return ret; | ||
235 | } | ||
236 | |||
237 | static const struct dev_pm_ops sirf_pm_ops = { | ||
238 | SET_SYSTEM_SLEEP_PM_OPS(sirf_suspend, sirf_resume) | ||
239 | SET_RUNTIME_PM_OPS(sirf_runtime_suspend, sirf_runtime_resume, NULL) | ||
240 | }; | ||
241 | |||
242 | static int sirf_parse_dt(struct serdev_device *serdev) | ||
243 | { | ||
244 | struct sirf_data *data = serdev_device_get_drvdata(serdev); | ||
245 | struct device_node *node = serdev->dev.of_node; | ||
246 | u32 speed = 9600; | ||
247 | |||
248 | of_property_read_u32(node, "current-speed", &speed); | ||
249 | |||
250 | data->speed = speed; | ||
251 | |||
252 | return 0; | ||
253 | } | ||
254 | |||
255 | static int sirf_probe(struct serdev_device *serdev) | ||
256 | { | ||
257 | struct device *dev = &serdev->dev; | ||
258 | struct gnss_device *gdev; | ||
259 | struct sirf_data *data; | ||
260 | int ret; | ||
261 | |||
262 | data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); | ||
263 | if (!data) | ||
264 | return -ENOMEM; | ||
265 | |||
266 | gdev = gnss_allocate_device(dev); | ||
267 | if (!gdev) | ||
268 | return -ENOMEM; | ||
269 | |||
270 | gdev->ops = &sirf_gnss_ops; | ||
271 | gnss_set_drvdata(gdev, data); | ||
272 | |||
273 | data->serdev = serdev; | ||
274 | data->gdev = gdev; | ||
275 | |||
276 | init_waitqueue_head(&data->power_wait); | ||
277 | |||
278 | serdev_device_set_drvdata(serdev, data); | ||
279 | serdev_device_set_client_ops(serdev, &sirf_serdev_ops); | ||
280 | |||
281 | ret = sirf_parse_dt(serdev); | ||
282 | if (ret) | ||
283 | goto err_put_device; | ||
284 | |||
285 | data->vcc = devm_regulator_get(dev, "vcc"); | ||
286 | if (IS_ERR(data->vcc)) { | ||
287 | ret = PTR_ERR(data->vcc); | ||
288 | goto err_put_device; | ||
289 | } | ||
290 | |||
291 | data->on_off = devm_gpiod_get_optional(dev, "sirf,onoff", | ||
292 | GPIOD_OUT_LOW); | ||
293 | if (IS_ERR(data->on_off)) | ||
294 | goto err_put_device; | ||
295 | |||
296 | if (data->on_off) { | ||
297 | data->wakeup = devm_gpiod_get_optional(dev, "sirf,wakeup", | ||
298 | GPIOD_IN); | ||
299 | if (IS_ERR(data->wakeup)) | ||
300 | goto err_put_device; | ||
301 | |||
302 | /* | ||
303 | * Configurations where WAKEUP has been left not connected, | ||
304 | * are currently not supported. | ||
305 | */ | ||
306 | if (!data->wakeup) { | ||
307 | dev_err(dev, "no wakeup gpio specified\n"); | ||
308 | ret = -ENODEV; | ||
309 | goto err_put_device; | ||
310 | } | ||
311 | } | ||
312 | |||
313 | if (data->wakeup) { | ||
314 | ret = gpiod_to_irq(data->wakeup); | ||
315 | if (ret < 0) | ||
316 | goto err_put_device; | ||
317 | |||
318 | data->irq = ret; | ||
319 | |||
320 | ret = devm_request_threaded_irq(dev, data->irq, NULL, | ||
321 | sirf_wakeup_handler, | ||
322 | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, | ||
323 | "wakeup", data); | ||
324 | if (ret) | ||
325 | goto err_put_device; | ||
326 | } | ||
327 | |||
328 | if (data->on_off) { | ||
329 | ret = regulator_enable(data->vcc); | ||
330 | if (ret) | ||
331 | goto err_put_device; | ||
332 | |||
333 | /* Wait for chip to boot into hibernate mode */ | ||
334 | msleep(SIRF_BOOT_DELAY); | ||
335 | } | ||
336 | |||
337 | if (IS_ENABLED(CONFIG_PM)) { | ||
338 | pm_runtime_set_suspended(dev); /* clear runtime_error flag */ | ||
339 | pm_runtime_enable(dev); | ||
340 | } else { | ||
341 | ret = sirf_runtime_resume(dev); | ||
342 | if (ret < 0) | ||
343 | goto err_disable_vcc; | ||
344 | } | ||
345 | |||
346 | ret = gnss_register_device(gdev); | ||
347 | if (ret) | ||
348 | goto err_disable_rpm; | ||
349 | |||
350 | return 0; | ||
351 | |||
352 | err_disable_rpm: | ||
353 | if (IS_ENABLED(CONFIG_PM)) | ||
354 | pm_runtime_disable(dev); | ||
355 | else | ||
356 | sirf_runtime_suspend(dev); | ||
357 | err_disable_vcc: | ||
358 | if (data->on_off) | ||
359 | regulator_disable(data->vcc); | ||
360 | err_put_device: | ||
361 | gnss_put_device(data->gdev); | ||
362 | |||
363 | return ret; | ||
364 | } | ||
365 | |||
366 | static void sirf_remove(struct serdev_device *serdev) | ||
367 | { | ||
368 | struct sirf_data *data = serdev_device_get_drvdata(serdev); | ||
369 | |||
370 | gnss_deregister_device(data->gdev); | ||
371 | |||
372 | if (IS_ENABLED(CONFIG_PM)) | ||
373 | pm_runtime_disable(&serdev->dev); | ||
374 | else | ||
375 | sirf_runtime_suspend(&serdev->dev); | ||
376 | |||
377 | if (data->on_off) | ||
378 | regulator_disable(data->vcc); | ||
379 | |||
380 | gnss_put_device(data->gdev); | ||
381 | }; | ||
382 | |||
383 | #ifdef CONFIG_OF | ||
384 | static const struct of_device_id sirf_of_match[] = { | ||
385 | { .compatible = "fastrax,uc430" }, | ||
386 | { .compatible = "linx,r4" }, | ||
387 | { .compatible = "wi2wi,w2sg0008i" }, | ||
388 | { .compatible = "wi2wi,w2sg0084i" }, | ||
389 | {}, | ||
390 | }; | ||
391 | MODULE_DEVICE_TABLE(of, sirf_of_match); | ||
392 | #endif | ||
393 | |||
394 | static struct serdev_device_driver sirf_driver = { | ||
395 | .driver = { | ||
396 | .name = "gnss-sirf", | ||
397 | .of_match_table = of_match_ptr(sirf_of_match), | ||
398 | .pm = &sirf_pm_ops, | ||
399 | }, | ||
400 | .probe = sirf_probe, | ||
401 | .remove = sirf_remove, | ||
402 | }; | ||
403 | module_serdev_device_driver(sirf_driver); | ||
404 | |||
405 | MODULE_AUTHOR("Johan Hovold <johan@kernel.org>"); | ||
406 | MODULE_DESCRIPTION("SiRFstar GNSS receiver driver"); | ||
407 | MODULE_LICENSE("GPL v2"); | ||