Merge tag 'ext4_for_linus_stable' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux-2.6-microblaze.git] / drivers / gnss / serial.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Generic serial 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/init.h>
11 #include <linux/kernel.h>
12 #include <linux/module.h>
13 #include <linux/of.h>
14 #include <linux/pm.h>
15 #include <linux/pm_runtime.h>
16 #include <linux/serdev.h>
17 #include <linux/slab.h>
18
19 #include "serial.h"
20
21 static int gnss_serial_open(struct gnss_device *gdev)
22 {
23         struct gnss_serial *gserial = gnss_get_drvdata(gdev);
24         struct serdev_device *serdev = gserial->serdev;
25         int ret;
26
27         ret = serdev_device_open(serdev);
28         if (ret)
29                 return ret;
30
31         serdev_device_set_baudrate(serdev, gserial->speed);
32         serdev_device_set_flow_control(serdev, false);
33
34         ret = pm_runtime_get_sync(&serdev->dev);
35         if (ret < 0) {
36                 pm_runtime_put_noidle(&serdev->dev);
37                 goto err_close;
38         }
39
40         return 0;
41
42 err_close:
43         serdev_device_close(serdev);
44
45         return ret;
46 }
47
48 static void gnss_serial_close(struct gnss_device *gdev)
49 {
50         struct gnss_serial *gserial = gnss_get_drvdata(gdev);
51         struct serdev_device *serdev = gserial->serdev;
52
53         serdev_device_close(serdev);
54
55         pm_runtime_put(&serdev->dev);
56 }
57
58 static int gnss_serial_write_raw(struct gnss_device *gdev,
59                 const unsigned char *buf, size_t count)
60 {
61         struct gnss_serial *gserial = gnss_get_drvdata(gdev);
62         struct serdev_device *serdev = gserial->serdev;
63         int ret;
64
65         /* write is only buffered synchronously */
66         ret = serdev_device_write(serdev, buf, count, 0);
67         if (ret < 0)
68                 return ret;
69
70         /* FIXME: determine if interrupted? */
71         serdev_device_wait_until_sent(serdev, 0);
72
73         return count;
74 }
75
76 static const struct gnss_operations gnss_serial_gnss_ops = {
77         .open           = gnss_serial_open,
78         .close          = gnss_serial_close,
79         .write_raw      = gnss_serial_write_raw,
80 };
81
82 static int gnss_serial_receive_buf(struct serdev_device *serdev,
83                                         const unsigned char *buf, size_t count)
84 {
85         struct gnss_serial *gserial = serdev_device_get_drvdata(serdev);
86         struct gnss_device *gdev = gserial->gdev;
87
88         return gnss_insert_raw(gdev, buf, count);
89 }
90
91 static const struct serdev_device_ops gnss_serial_serdev_ops = {
92         .receive_buf    = gnss_serial_receive_buf,
93         .write_wakeup   = serdev_device_write_wakeup,
94 };
95
96 static int gnss_serial_set_power(struct gnss_serial *gserial,
97                                         enum gnss_serial_pm_state state)
98 {
99         if (!gserial->ops || !gserial->ops->set_power)
100                 return 0;
101
102         return gserial->ops->set_power(gserial, state);
103 }
104
105 /*
106  * FIXME: need to provide subdriver defaults or separate dt parsing from
107  * allocation.
108  */
109 static int gnss_serial_parse_dt(struct serdev_device *serdev)
110 {
111         struct gnss_serial *gserial = serdev_device_get_drvdata(serdev);
112         struct device_node *node = serdev->dev.of_node;
113         u32 speed = 4800;
114
115         of_property_read_u32(node, "current-speed", &speed);
116
117         gserial->speed = speed;
118
119         return 0;
120 }
121
122 struct gnss_serial *gnss_serial_allocate(struct serdev_device *serdev,
123                                                 size_t data_size)
124 {
125         struct gnss_serial *gserial;
126         struct gnss_device *gdev;
127         int ret;
128
129         gserial = kzalloc(sizeof(*gserial) + data_size, GFP_KERNEL);
130         if (!gserial)
131                 return ERR_PTR(-ENOMEM);
132
133         gdev = gnss_allocate_device(&serdev->dev);
134         if (!gdev) {
135                 ret = -ENOMEM;
136                 goto err_free_gserial;
137         }
138
139         gdev->ops = &gnss_serial_gnss_ops;
140         gnss_set_drvdata(gdev, gserial);
141
142         gserial->serdev = serdev;
143         gserial->gdev = gdev;
144
145         serdev_device_set_drvdata(serdev, gserial);
146         serdev_device_set_client_ops(serdev, &gnss_serial_serdev_ops);
147
148         ret = gnss_serial_parse_dt(serdev);
149         if (ret)
150                 goto err_put_device;
151
152         return gserial;
153
154 err_put_device:
155         gnss_put_device(gserial->gdev);
156 err_free_gserial:
157         kfree(gserial);
158
159         return ERR_PTR(ret);
160 }
161 EXPORT_SYMBOL_GPL(gnss_serial_allocate);
162
163 void gnss_serial_free(struct gnss_serial *gserial)
164 {
165         gnss_put_device(gserial->gdev);
166         kfree(gserial);
167 };
168 EXPORT_SYMBOL_GPL(gnss_serial_free);
169
170 int gnss_serial_register(struct gnss_serial *gserial)
171 {
172         struct serdev_device *serdev = gserial->serdev;
173         int ret;
174
175         if (IS_ENABLED(CONFIG_PM)) {
176                 pm_runtime_enable(&serdev->dev);
177         } else {
178                 ret = gnss_serial_set_power(gserial, GNSS_SERIAL_ACTIVE);
179                 if (ret < 0)
180                         return ret;
181         }
182
183         ret = gnss_register_device(gserial->gdev);
184         if (ret)
185                 goto err_disable_rpm;
186
187         return 0;
188
189 err_disable_rpm:
190         if (IS_ENABLED(CONFIG_PM))
191                 pm_runtime_disable(&serdev->dev);
192         else
193                 gnss_serial_set_power(gserial, GNSS_SERIAL_OFF);
194
195         return ret;
196 }
197 EXPORT_SYMBOL_GPL(gnss_serial_register);
198
199 void gnss_serial_deregister(struct gnss_serial *gserial)
200 {
201         struct serdev_device *serdev = gserial->serdev;
202
203         gnss_deregister_device(gserial->gdev);
204
205         if (IS_ENABLED(CONFIG_PM))
206                 pm_runtime_disable(&serdev->dev);
207         else
208                 gnss_serial_set_power(gserial, GNSS_SERIAL_OFF);
209 }
210 EXPORT_SYMBOL_GPL(gnss_serial_deregister);
211
212 #ifdef CONFIG_PM
213 static int gnss_serial_runtime_suspend(struct device *dev)
214 {
215         struct gnss_serial *gserial = dev_get_drvdata(dev);
216
217         return gnss_serial_set_power(gserial, GNSS_SERIAL_STANDBY);
218 }
219
220 static int gnss_serial_runtime_resume(struct device *dev)
221 {
222         struct gnss_serial *gserial = dev_get_drvdata(dev);
223
224         return gnss_serial_set_power(gserial, GNSS_SERIAL_ACTIVE);
225 }
226 #endif /* CONFIG_PM */
227
228 static int gnss_serial_prepare(struct device *dev)
229 {
230         if (pm_runtime_suspended(dev))
231                 return 1;
232
233         return 0;
234 }
235
236 #ifdef CONFIG_PM_SLEEP
237 static int gnss_serial_suspend(struct device *dev)
238 {
239         struct gnss_serial *gserial = dev_get_drvdata(dev);
240         int ret = 0;
241
242         /*
243          * FIXME: serdev currently lacks support for managing the underlying
244          * device's wakeup settings. A workaround would be to close the serdev
245          * device here if it is open.
246          */
247
248         if (!pm_runtime_suspended(dev))
249                 ret = gnss_serial_set_power(gserial, GNSS_SERIAL_STANDBY);
250
251         return ret;
252 }
253
254 static int gnss_serial_resume(struct device *dev)
255 {
256         struct gnss_serial *gserial = dev_get_drvdata(dev);
257         int ret = 0;
258
259         if (!pm_runtime_suspended(dev))
260                 ret = gnss_serial_set_power(gserial, GNSS_SERIAL_ACTIVE);
261
262         return ret;
263 }
264 #endif /* CONFIG_PM_SLEEP */
265
266 const struct dev_pm_ops gnss_serial_pm_ops = {
267         .prepare        = gnss_serial_prepare,
268         SET_SYSTEM_SLEEP_PM_OPS(gnss_serial_suspend, gnss_serial_resume)
269         SET_RUNTIME_PM_OPS(gnss_serial_runtime_suspend, gnss_serial_runtime_resume, NULL)
270 };
271 EXPORT_SYMBOL_GPL(gnss_serial_pm_ops);
272
273 MODULE_AUTHOR("Johan Hovold <johan@kernel.org>");
274 MODULE_DESCRIPTION("Generic serial GNSS receiver driver");
275 MODULE_LICENSE("GPL v2");