137768b05SJohan Hovold // SPDX-License-Identifier: GPL-2.0
237768b05SJohan Hovold /*
337768b05SJohan Hovold * Generic serial GNSS receiver driver
437768b05SJohan Hovold *
537768b05SJohan Hovold * Copyright (C) 2018 Johan Hovold <johan@kernel.org>
637768b05SJohan Hovold */
737768b05SJohan Hovold
837768b05SJohan Hovold #include <linux/errno.h>
937768b05SJohan Hovold #include <linux/gnss.h>
1037768b05SJohan Hovold #include <linux/init.h>
1137768b05SJohan Hovold #include <linux/kernel.h>
1237768b05SJohan Hovold #include <linux/module.h>
1337768b05SJohan Hovold #include <linux/of.h>
1437768b05SJohan Hovold #include <linux/pm.h>
1537768b05SJohan Hovold #include <linux/pm_runtime.h>
1656a6c726SJohan Hovold #include <linux/sched.h>
1737768b05SJohan Hovold #include <linux/serdev.h>
1837768b05SJohan Hovold #include <linux/slab.h>
1937768b05SJohan Hovold
2037768b05SJohan Hovold #include "serial.h"
2137768b05SJohan Hovold
gnss_serial_open(struct gnss_device * gdev)2237768b05SJohan Hovold static int gnss_serial_open(struct gnss_device *gdev)
2337768b05SJohan Hovold {
2437768b05SJohan Hovold struct gnss_serial *gserial = gnss_get_drvdata(gdev);
2537768b05SJohan Hovold struct serdev_device *serdev = gserial->serdev;
2637768b05SJohan Hovold int ret;
2737768b05SJohan Hovold
2837768b05SJohan Hovold ret = serdev_device_open(serdev);
2937768b05SJohan Hovold if (ret)
3037768b05SJohan Hovold return ret;
3137768b05SJohan Hovold
3237768b05SJohan Hovold serdev_device_set_baudrate(serdev, gserial->speed);
3337768b05SJohan Hovold serdev_device_set_flow_control(serdev, false);
3437768b05SJohan Hovold
3537768b05SJohan Hovold ret = pm_runtime_get_sync(&serdev->dev);
3637768b05SJohan Hovold if (ret < 0) {
3737768b05SJohan Hovold pm_runtime_put_noidle(&serdev->dev);
3837768b05SJohan Hovold goto err_close;
3937768b05SJohan Hovold }
4037768b05SJohan Hovold
4137768b05SJohan Hovold return 0;
4237768b05SJohan Hovold
4337768b05SJohan Hovold err_close:
4437768b05SJohan Hovold serdev_device_close(serdev);
4537768b05SJohan Hovold
4637768b05SJohan Hovold return ret;
4737768b05SJohan Hovold }
4837768b05SJohan Hovold
gnss_serial_close(struct gnss_device * gdev)4937768b05SJohan Hovold static void gnss_serial_close(struct gnss_device *gdev)
5037768b05SJohan Hovold {
5137768b05SJohan Hovold struct gnss_serial *gserial = gnss_get_drvdata(gdev);
5237768b05SJohan Hovold struct serdev_device *serdev = gserial->serdev;
5337768b05SJohan Hovold
5437768b05SJohan Hovold serdev_device_close(serdev);
5537768b05SJohan Hovold
5637768b05SJohan Hovold pm_runtime_put(&serdev->dev);
5737768b05SJohan Hovold }
5837768b05SJohan Hovold
gnss_serial_write_raw(struct gnss_device * gdev,const unsigned char * buf,size_t count)5937768b05SJohan Hovold static int gnss_serial_write_raw(struct gnss_device *gdev,
6037768b05SJohan Hovold const unsigned char *buf, size_t count)
6137768b05SJohan Hovold {
6237768b05SJohan Hovold struct gnss_serial *gserial = gnss_get_drvdata(gdev);
6337768b05SJohan Hovold struct serdev_device *serdev = gserial->serdev;
6437768b05SJohan Hovold int ret;
6537768b05SJohan Hovold
6637768b05SJohan Hovold /* write is only buffered synchronously */
6756a6c726SJohan Hovold ret = serdev_device_write(serdev, buf, count, MAX_SCHEDULE_TIMEOUT);
680bbf0a88SJohan Hovold if (ret < 0 || ret < count)
6937768b05SJohan Hovold return ret;
7037768b05SJohan Hovold
7137768b05SJohan Hovold /* FIXME: determine if interrupted? */
7237768b05SJohan Hovold serdev_device_wait_until_sent(serdev, 0);
7337768b05SJohan Hovold
7437768b05SJohan Hovold return count;
7537768b05SJohan Hovold }
7637768b05SJohan Hovold
7737768b05SJohan Hovold static const struct gnss_operations gnss_serial_gnss_ops = {
7837768b05SJohan Hovold .open = gnss_serial_open,
7937768b05SJohan Hovold .close = gnss_serial_close,
8037768b05SJohan Hovold .write_raw = gnss_serial_write_raw,
8137768b05SJohan Hovold };
8237768b05SJohan Hovold
gnss_serial_receive_buf(struct serdev_device * serdev,const unsigned char * buf,size_t count)8337768b05SJohan Hovold static int gnss_serial_receive_buf(struct serdev_device *serdev,
8437768b05SJohan Hovold const unsigned char *buf, size_t count)
8537768b05SJohan Hovold {
8637768b05SJohan Hovold struct gnss_serial *gserial = serdev_device_get_drvdata(serdev);
8737768b05SJohan Hovold struct gnss_device *gdev = gserial->gdev;
8837768b05SJohan Hovold
8937768b05SJohan Hovold return gnss_insert_raw(gdev, buf, count);
9037768b05SJohan Hovold }
9137768b05SJohan Hovold
9237768b05SJohan Hovold static const struct serdev_device_ops gnss_serial_serdev_ops = {
9337768b05SJohan Hovold .receive_buf = gnss_serial_receive_buf,
9437768b05SJohan Hovold .write_wakeup = serdev_device_write_wakeup,
9537768b05SJohan Hovold };
9637768b05SJohan Hovold
gnss_serial_set_power(struct gnss_serial * gserial,enum gnss_serial_pm_state state)9737768b05SJohan Hovold static int gnss_serial_set_power(struct gnss_serial *gserial,
9837768b05SJohan Hovold enum gnss_serial_pm_state state)
9937768b05SJohan Hovold {
10037768b05SJohan Hovold if (!gserial->ops || !gserial->ops->set_power)
10137768b05SJohan Hovold return 0;
10237768b05SJohan Hovold
10337768b05SJohan Hovold return gserial->ops->set_power(gserial, state);
10437768b05SJohan Hovold }
10537768b05SJohan Hovold
10637768b05SJohan Hovold /*
10737768b05SJohan Hovold * FIXME: need to provide subdriver defaults or separate dt parsing from
10837768b05SJohan Hovold * allocation.
10937768b05SJohan Hovold */
gnss_serial_parse_dt(struct serdev_device * serdev)11037768b05SJohan Hovold static int gnss_serial_parse_dt(struct serdev_device *serdev)
11137768b05SJohan Hovold {
11237768b05SJohan Hovold struct gnss_serial *gserial = serdev_device_get_drvdata(serdev);
11337768b05SJohan Hovold struct device_node *node = serdev->dev.of_node;
11437768b05SJohan Hovold u32 speed = 4800;
11537768b05SJohan Hovold
11637768b05SJohan Hovold of_property_read_u32(node, "current-speed", &speed);
11737768b05SJohan Hovold
11837768b05SJohan Hovold gserial->speed = speed;
11937768b05SJohan Hovold
12037768b05SJohan Hovold return 0;
12137768b05SJohan Hovold }
12237768b05SJohan Hovold
gnss_serial_allocate(struct serdev_device * serdev,size_t data_size)12337768b05SJohan Hovold struct gnss_serial *gnss_serial_allocate(struct serdev_device *serdev,
12437768b05SJohan Hovold size_t data_size)
12537768b05SJohan Hovold {
12637768b05SJohan Hovold struct gnss_serial *gserial;
12737768b05SJohan Hovold struct gnss_device *gdev;
12837768b05SJohan Hovold int ret;
12937768b05SJohan Hovold
13037768b05SJohan Hovold gserial = kzalloc(sizeof(*gserial) + data_size, GFP_KERNEL);
13137768b05SJohan Hovold if (!gserial)
13237768b05SJohan Hovold return ERR_PTR(-ENOMEM);
13337768b05SJohan Hovold
13437768b05SJohan Hovold gdev = gnss_allocate_device(&serdev->dev);
13537768b05SJohan Hovold if (!gdev) {
13637768b05SJohan Hovold ret = -ENOMEM;
13737768b05SJohan Hovold goto err_free_gserial;
13837768b05SJohan Hovold }
13937768b05SJohan Hovold
14037768b05SJohan Hovold gdev->ops = &gnss_serial_gnss_ops;
14137768b05SJohan Hovold gnss_set_drvdata(gdev, gserial);
14237768b05SJohan Hovold
14337768b05SJohan Hovold gserial->serdev = serdev;
14437768b05SJohan Hovold gserial->gdev = gdev;
14537768b05SJohan Hovold
14637768b05SJohan Hovold serdev_device_set_drvdata(serdev, gserial);
14737768b05SJohan Hovold serdev_device_set_client_ops(serdev, &gnss_serial_serdev_ops);
14837768b05SJohan Hovold
14937768b05SJohan Hovold ret = gnss_serial_parse_dt(serdev);
15037768b05SJohan Hovold if (ret)
15137768b05SJohan Hovold goto err_put_device;
15237768b05SJohan Hovold
15337768b05SJohan Hovold return gserial;
15437768b05SJohan Hovold
15537768b05SJohan Hovold err_put_device:
15637768b05SJohan Hovold gnss_put_device(gserial->gdev);
15737768b05SJohan Hovold err_free_gserial:
15837768b05SJohan Hovold kfree(gserial);
15937768b05SJohan Hovold
16037768b05SJohan Hovold return ERR_PTR(ret);
16137768b05SJohan Hovold }
16237768b05SJohan Hovold EXPORT_SYMBOL_GPL(gnss_serial_allocate);
16337768b05SJohan Hovold
gnss_serial_free(struct gnss_serial * gserial)16437768b05SJohan Hovold void gnss_serial_free(struct gnss_serial *gserial)
16537768b05SJohan Hovold {
16637768b05SJohan Hovold gnss_put_device(gserial->gdev);
16737768b05SJohan Hovold kfree(gserial);
168*b15c9015SJohan Hovold }
16937768b05SJohan Hovold EXPORT_SYMBOL_GPL(gnss_serial_free);
17037768b05SJohan Hovold
gnss_serial_register(struct gnss_serial * gserial)17137768b05SJohan Hovold int gnss_serial_register(struct gnss_serial *gserial)
17237768b05SJohan Hovold {
17337768b05SJohan Hovold struct serdev_device *serdev = gserial->serdev;
17437768b05SJohan Hovold int ret;
17537768b05SJohan Hovold
17637768b05SJohan Hovold if (IS_ENABLED(CONFIG_PM)) {
17737768b05SJohan Hovold pm_runtime_enable(&serdev->dev);
17837768b05SJohan Hovold } else {
17937768b05SJohan Hovold ret = gnss_serial_set_power(gserial, GNSS_SERIAL_ACTIVE);
18037768b05SJohan Hovold if (ret < 0)
18137768b05SJohan Hovold return ret;
18237768b05SJohan Hovold }
18337768b05SJohan Hovold
18437768b05SJohan Hovold ret = gnss_register_device(gserial->gdev);
18537768b05SJohan Hovold if (ret)
18637768b05SJohan Hovold goto err_disable_rpm;
18737768b05SJohan Hovold
18837768b05SJohan Hovold return 0;
18937768b05SJohan Hovold
19037768b05SJohan Hovold err_disable_rpm:
19137768b05SJohan Hovold if (IS_ENABLED(CONFIG_PM))
19237768b05SJohan Hovold pm_runtime_disable(&serdev->dev);
19337768b05SJohan Hovold else
19437768b05SJohan Hovold gnss_serial_set_power(gserial, GNSS_SERIAL_OFF);
19537768b05SJohan Hovold
19637768b05SJohan Hovold return ret;
19737768b05SJohan Hovold }
19837768b05SJohan Hovold EXPORT_SYMBOL_GPL(gnss_serial_register);
19937768b05SJohan Hovold
gnss_serial_deregister(struct gnss_serial * gserial)20037768b05SJohan Hovold void gnss_serial_deregister(struct gnss_serial *gserial)
20137768b05SJohan Hovold {
20237768b05SJohan Hovold struct serdev_device *serdev = gserial->serdev;
20337768b05SJohan Hovold
20437768b05SJohan Hovold gnss_deregister_device(gserial->gdev);
20537768b05SJohan Hovold
20637768b05SJohan Hovold if (IS_ENABLED(CONFIG_PM))
20737768b05SJohan Hovold pm_runtime_disable(&serdev->dev);
20837768b05SJohan Hovold else
20937768b05SJohan Hovold gnss_serial_set_power(gserial, GNSS_SERIAL_OFF);
21037768b05SJohan Hovold }
21137768b05SJohan Hovold EXPORT_SYMBOL_GPL(gnss_serial_deregister);
21237768b05SJohan Hovold
21337768b05SJohan Hovold #ifdef CONFIG_PM
gnss_serial_runtime_suspend(struct device * dev)21437768b05SJohan Hovold static int gnss_serial_runtime_suspend(struct device *dev)
21537768b05SJohan Hovold {
21637768b05SJohan Hovold struct gnss_serial *gserial = dev_get_drvdata(dev);
21737768b05SJohan Hovold
21837768b05SJohan Hovold return gnss_serial_set_power(gserial, GNSS_SERIAL_STANDBY);
21937768b05SJohan Hovold }
22037768b05SJohan Hovold
gnss_serial_runtime_resume(struct device * dev)22137768b05SJohan Hovold static int gnss_serial_runtime_resume(struct device *dev)
22237768b05SJohan Hovold {
22337768b05SJohan Hovold struct gnss_serial *gserial = dev_get_drvdata(dev);
22437768b05SJohan Hovold
22537768b05SJohan Hovold return gnss_serial_set_power(gserial, GNSS_SERIAL_ACTIVE);
22637768b05SJohan Hovold }
22737768b05SJohan Hovold #endif /* CONFIG_PM */
22837768b05SJohan Hovold
gnss_serial_prepare(struct device * dev)22937768b05SJohan Hovold static int gnss_serial_prepare(struct device *dev)
23037768b05SJohan Hovold {
23137768b05SJohan Hovold if (pm_runtime_suspended(dev))
23237768b05SJohan Hovold return 1;
23337768b05SJohan Hovold
23437768b05SJohan Hovold return 0;
23537768b05SJohan Hovold }
23637768b05SJohan Hovold
23737768b05SJohan Hovold #ifdef CONFIG_PM_SLEEP
gnss_serial_suspend(struct device * dev)23837768b05SJohan Hovold static int gnss_serial_suspend(struct device *dev)
23937768b05SJohan Hovold {
24037768b05SJohan Hovold struct gnss_serial *gserial = dev_get_drvdata(dev);
24137768b05SJohan Hovold int ret = 0;
24237768b05SJohan Hovold
24337768b05SJohan Hovold /*
24437768b05SJohan Hovold * FIXME: serdev currently lacks support for managing the underlying
24537768b05SJohan Hovold * device's wakeup settings. A workaround would be to close the serdev
24637768b05SJohan Hovold * device here if it is open.
24737768b05SJohan Hovold */
24837768b05SJohan Hovold
24937768b05SJohan Hovold if (!pm_runtime_suspended(dev))
25037768b05SJohan Hovold ret = gnss_serial_set_power(gserial, GNSS_SERIAL_STANDBY);
25137768b05SJohan Hovold
25237768b05SJohan Hovold return ret;
25337768b05SJohan Hovold }
25437768b05SJohan Hovold
gnss_serial_resume(struct device * dev)25537768b05SJohan Hovold static int gnss_serial_resume(struct device *dev)
25637768b05SJohan Hovold {
25737768b05SJohan Hovold struct gnss_serial *gserial = dev_get_drvdata(dev);
25837768b05SJohan Hovold int ret = 0;
25937768b05SJohan Hovold
26037768b05SJohan Hovold if (!pm_runtime_suspended(dev))
26137768b05SJohan Hovold ret = gnss_serial_set_power(gserial, GNSS_SERIAL_ACTIVE);
26237768b05SJohan Hovold
26337768b05SJohan Hovold return ret;
26437768b05SJohan Hovold }
26537768b05SJohan Hovold #endif /* CONFIG_PM_SLEEP */
26637768b05SJohan Hovold
26737768b05SJohan Hovold const struct dev_pm_ops gnss_serial_pm_ops = {
26837768b05SJohan Hovold .prepare = gnss_serial_prepare,
26937768b05SJohan Hovold SET_SYSTEM_SLEEP_PM_OPS(gnss_serial_suspend, gnss_serial_resume)
27037768b05SJohan Hovold SET_RUNTIME_PM_OPS(gnss_serial_runtime_suspend, gnss_serial_runtime_resume, NULL)
27137768b05SJohan Hovold };
27237768b05SJohan Hovold EXPORT_SYMBOL_GPL(gnss_serial_pm_ops);
27337768b05SJohan Hovold
27437768b05SJohan Hovold MODULE_AUTHOR("Johan Hovold <johan@kernel.org>");
27537768b05SJohan Hovold MODULE_DESCRIPTION("Generic serial GNSS receiver driver");
27637768b05SJohan Hovold MODULE_LICENSE("GPL v2");
277