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