summaryrefslogtreecommitdiffstats
path: root/drivers/gnss
diff options
context:
space:
mode:
authorJohan Hovold <johan@kernel.org>2018-06-01 04:22:58 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2018-06-28 07:32:51 -0400
commitd2efbbd18b1e26ac79401841089e4a4b97d745c3 (patch)
treefffc7d8fd715ab5d3728fdbfa37aa6f73fd1237b /drivers/gnss
parent176193b7dd6ebcb4bb843f7b08663aeeabbdeacc (diff)
gnss: add driver for sirfstar-based receivers
Add driver for serial-connected SiRFstar-based GNSS receivers. These devices typically boot into hibernate mode from which they can be woken using a pulse on the ON_OFF input pin. Once active, a pulse on the same ON_OFF pin is used to put the device back into hibernate mode. The current state can be determined by sampling the WAKEUP output. Hardware configurations where WAKEUP has been connected to ON_OFF (and where an initial WAKEUP pulse during boot is sufficient to have the device boot into active mode) are also supported. In this case, device power is managed using the main-supply regulator only. Note that configurations where WAKEUP is left not connected, so that the device power state can only indirectly be determined using the I/O interface, is currently not supported. It should be fairly straight-forward to extend the current implementation with such support however (and this this is the main reason for not using the generic serial implementation for this driver). Note that timepulse-support is left unimplemented. Signed-off-by: Johan Hovold <johan@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/gnss')
-rw-r--r--drivers/gnss/Kconfig12
-rw-r--r--drivers/gnss/Makefile3
-rw-r--r--drivers/gnss/sirf.c407
3 files changed, 422 insertions, 0 deletions
diff --git a/drivers/gnss/Kconfig b/drivers/gnss/Kconfig
index 784b8c0367d9..6abc88514512 100644
--- a/drivers/gnss/Kconfig
+++ b/drivers/gnss/Kconfig
@@ -15,6 +15,18 @@ if GNSS
15config GNSS_SERIAL 15config GNSS_SERIAL
16 tristate 16 tristate
17 17
18config GNSS_SIRF_SERIAL
19 tristate "SiRFstar GNSS receiver support"
20 depends on SERIAL_DEV_BUS
21 ---help---
22 Say Y here if you have a SiRFstar-based GNSS receiver which uses a
23 serial interface.
24
25 To compile this driver as a module, choose M here: the module will
26 be called gnss-sirf.
27
28 If unsure, say N.
29
18config GNSS_UBX_SERIAL 30config GNSS_UBX_SERIAL
19 tristate "u-blox GNSS receiver support" 31 tristate "u-blox GNSS receiver support"
20 depends on SERIAL_DEV_BUS 32 depends on SERIAL_DEV_BUS
diff --git a/drivers/gnss/Makefile b/drivers/gnss/Makefile
index d9295b20b7bc..5cf0ebe0330a 100644
--- a/drivers/gnss/Makefile
+++ b/drivers/gnss/Makefile
@@ -9,5 +9,8 @@ gnss-y := core.o
9obj-$(CONFIG_GNSS_SERIAL) += gnss-serial.o 9obj-$(CONFIG_GNSS_SERIAL) += gnss-serial.o
10gnss-serial-y := serial.o 10gnss-serial-y := serial.o
11 11
12obj-$(CONFIG_GNSS_SIRF_SERIAL) += gnss-sirf.o
13gnss-sirf-y := sirf.o
14
12obj-$(CONFIG_GNSS_UBX_SERIAL) += gnss-ubx.o 15obj-$(CONFIG_GNSS_UBX_SERIAL) += gnss-ubx.o
13gnss-ubx-y := ubx.o 16gnss-ubx-y := ubx.o
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
28struct 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
40static 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
62err_close:
63 serdev_device_close(serdev);
64
65 return ret;
66}
67
68static 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
78static 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
96static const struct gnss_operations sirf_gnss_ops = {
97 .open = sirf_open,
98 .close = sirf_close,
99 .write_raw = sirf_write_raw,
100};
101
102static 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
111static const struct serdev_device_ops sirf_serdev_ops = {
112 .receive_buf = sirf_receive_buf,
113 .write_wakeup = serdev_device_write_wakeup,
114};
115
116static 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);
129out:
130 return IRQ_HANDLED;
131}
132
133static 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
152static 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
159static 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
189static 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
199static 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
209static 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
223static 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
237static 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
242static 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
255static 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
352err_disable_rpm:
353 if (IS_ENABLED(CONFIG_PM))
354 pm_runtime_disable(dev);
355 else
356 sirf_runtime_suspend(dev);
357err_disable_vcc:
358 if (data->on_off)
359 regulator_disable(data->vcc);
360err_put_device:
361 gnss_put_device(data->gdev);
362
363 return ret;
364}
365
366static 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
384static 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};
391MODULE_DEVICE_TABLE(of, sirf_of_match);
392#endif
393
394static 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};
403module_serdev_device_driver(sirf_driver);
404
405MODULE_AUTHOR("Johan Hovold <johan@kernel.org>");
406MODULE_DESCRIPTION("SiRFstar GNSS receiver driver");
407MODULE_LICENSE("GPL v2");