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 22 static 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 43 err_close: 44 serdev_device_close(serdev); 45 46 return ret; 47 } 48 49 static 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 59 static 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) 69 return ret; 70 71 /* FIXME: determine if interrupted? */ 72 serdev_device_wait_until_sent(serdev, 0); 73 74 return count; 75 } 76 77 static 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 83 static 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 92 static 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 97 static 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 */ 110 static 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 123 struct 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 155 err_put_device: 156 gnss_put_device(gserial->gdev); 157 err_free_gserial: 158 kfree(gserial); 159 160 return ERR_PTR(ret); 161 } 162 EXPORT_SYMBOL_GPL(gnss_serial_allocate); 163 164 void gnss_serial_free(struct gnss_serial *gserial) 165 { 166 gnss_put_device(gserial->gdev); 167 kfree(gserial); 168 }; 169 EXPORT_SYMBOL_GPL(gnss_serial_free); 170 171 int 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 190 err_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 } 198 EXPORT_SYMBOL_GPL(gnss_serial_register); 199 200 void 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 } 211 EXPORT_SYMBOL_GPL(gnss_serial_deregister); 212 213 #ifdef CONFIG_PM 214 static 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 221 static 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 229 static 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 238 static 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 255 static 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 267 const 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 }; 272 EXPORT_SYMBOL_GPL(gnss_serial_pm_ops); 273 274 MODULE_AUTHOR("Johan Hovold <johan@kernel.org>"); 275 MODULE_DESCRIPTION("Generic serial GNSS receiver driver"); 276 MODULE_LICENSE("GPL v2"); 277