xref: /openbmc/linux/drivers/gnss/serial.c (revision 03ab8e6297acd1bc0eedaa050e2a1635c576fd11)
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