Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Generic serial GNSS receiver driver
0004  *
0005  * Copyright (C) 2018 Johan Hovold <johan@kernel.org>
0006  */
0007 
0008 #include <linux/errno.h>
0009 #include <linux/gnss.h>
0010 #include <linux/init.h>
0011 #include <linux/kernel.h>
0012 #include <linux/module.h>
0013 #include <linux/of.h>
0014 #include <linux/pm.h>
0015 #include <linux/pm_runtime.h>
0016 #include <linux/sched.h>
0017 #include <linux/serdev.h>
0018 #include <linux/slab.h>
0019 
0020 #include "serial.h"
0021 
0022 static int gnss_serial_open(struct gnss_device *gdev)
0023 {
0024     struct gnss_serial *gserial = gnss_get_drvdata(gdev);
0025     struct serdev_device *serdev = gserial->serdev;
0026     int ret;
0027 
0028     ret = serdev_device_open(serdev);
0029     if (ret)
0030         return ret;
0031 
0032     serdev_device_set_baudrate(serdev, gserial->speed);
0033     serdev_device_set_flow_control(serdev, false);
0034 
0035     ret = pm_runtime_get_sync(&serdev->dev);
0036     if (ret < 0) {
0037         pm_runtime_put_noidle(&serdev->dev);
0038         goto err_close;
0039     }
0040 
0041     return 0;
0042 
0043 err_close:
0044     serdev_device_close(serdev);
0045 
0046     return ret;
0047 }
0048 
0049 static void gnss_serial_close(struct gnss_device *gdev)
0050 {
0051     struct gnss_serial *gserial = gnss_get_drvdata(gdev);
0052     struct serdev_device *serdev = gserial->serdev;
0053 
0054     serdev_device_close(serdev);
0055 
0056     pm_runtime_put(&serdev->dev);
0057 }
0058 
0059 static int gnss_serial_write_raw(struct gnss_device *gdev,
0060         const unsigned char *buf, size_t count)
0061 {
0062     struct gnss_serial *gserial = gnss_get_drvdata(gdev);
0063     struct serdev_device *serdev = gserial->serdev;
0064     int ret;
0065 
0066     /* write is only buffered synchronously */
0067     ret = serdev_device_write(serdev, buf, count, MAX_SCHEDULE_TIMEOUT);
0068     if (ret < 0 || ret < count)
0069         return ret;
0070 
0071     /* FIXME: determine if interrupted? */
0072     serdev_device_wait_until_sent(serdev, 0);
0073 
0074     return count;
0075 }
0076 
0077 static const struct gnss_operations gnss_serial_gnss_ops = {
0078     .open       = gnss_serial_open,
0079     .close      = gnss_serial_close,
0080     .write_raw  = gnss_serial_write_raw,
0081 };
0082 
0083 static int gnss_serial_receive_buf(struct serdev_device *serdev,
0084                     const unsigned char *buf, size_t count)
0085 {
0086     struct gnss_serial *gserial = serdev_device_get_drvdata(serdev);
0087     struct gnss_device *gdev = gserial->gdev;
0088 
0089     return gnss_insert_raw(gdev, buf, count);
0090 }
0091 
0092 static const struct serdev_device_ops gnss_serial_serdev_ops = {
0093     .receive_buf    = gnss_serial_receive_buf,
0094     .write_wakeup   = serdev_device_write_wakeup,
0095 };
0096 
0097 static int gnss_serial_set_power(struct gnss_serial *gserial,
0098                     enum gnss_serial_pm_state state)
0099 {
0100     if (!gserial->ops || !gserial->ops->set_power)
0101         return 0;
0102 
0103     return gserial->ops->set_power(gserial, state);
0104 }
0105 
0106 /*
0107  * FIXME: need to provide subdriver defaults or separate dt parsing from
0108  * allocation.
0109  */
0110 static int gnss_serial_parse_dt(struct serdev_device *serdev)
0111 {
0112     struct gnss_serial *gserial = serdev_device_get_drvdata(serdev);
0113     struct device_node *node = serdev->dev.of_node;
0114     u32 speed = 4800;
0115 
0116     of_property_read_u32(node, "current-speed", &speed);
0117 
0118     gserial->speed = speed;
0119 
0120     return 0;
0121 }
0122 
0123 struct gnss_serial *gnss_serial_allocate(struct serdev_device *serdev,
0124                         size_t data_size)
0125 {
0126     struct gnss_serial *gserial;
0127     struct gnss_device *gdev;
0128     int ret;
0129 
0130     gserial = kzalloc(sizeof(*gserial) + data_size, GFP_KERNEL);
0131     if (!gserial)
0132         return ERR_PTR(-ENOMEM);
0133 
0134     gdev = gnss_allocate_device(&serdev->dev);
0135     if (!gdev) {
0136         ret = -ENOMEM;
0137         goto err_free_gserial;
0138     }
0139 
0140     gdev->ops = &gnss_serial_gnss_ops;
0141     gnss_set_drvdata(gdev, gserial);
0142 
0143     gserial->serdev = serdev;
0144     gserial->gdev = gdev;
0145 
0146     serdev_device_set_drvdata(serdev, gserial);
0147     serdev_device_set_client_ops(serdev, &gnss_serial_serdev_ops);
0148 
0149     ret = gnss_serial_parse_dt(serdev);
0150     if (ret)
0151         goto err_put_device;
0152 
0153     return gserial;
0154 
0155 err_put_device:
0156     gnss_put_device(gserial->gdev);
0157 err_free_gserial:
0158     kfree(gserial);
0159 
0160     return ERR_PTR(ret);
0161 }
0162 EXPORT_SYMBOL_GPL(gnss_serial_allocate);
0163 
0164 void gnss_serial_free(struct gnss_serial *gserial)
0165 {
0166     gnss_put_device(gserial->gdev);
0167     kfree(gserial);
0168 }
0169 EXPORT_SYMBOL_GPL(gnss_serial_free);
0170 
0171 int gnss_serial_register(struct gnss_serial *gserial)
0172 {
0173     struct serdev_device *serdev = gserial->serdev;
0174     int ret;
0175 
0176     if (IS_ENABLED(CONFIG_PM)) {
0177         pm_runtime_enable(&serdev->dev);
0178     } else {
0179         ret = gnss_serial_set_power(gserial, GNSS_SERIAL_ACTIVE);
0180         if (ret < 0)
0181             return ret;
0182     }
0183 
0184     ret = gnss_register_device(gserial->gdev);
0185     if (ret)
0186         goto err_disable_rpm;
0187 
0188     return 0;
0189 
0190 err_disable_rpm:
0191     if (IS_ENABLED(CONFIG_PM))
0192         pm_runtime_disable(&serdev->dev);
0193     else
0194         gnss_serial_set_power(gserial, GNSS_SERIAL_OFF);
0195 
0196     return ret;
0197 }
0198 EXPORT_SYMBOL_GPL(gnss_serial_register);
0199 
0200 void gnss_serial_deregister(struct gnss_serial *gserial)
0201 {
0202     struct serdev_device *serdev = gserial->serdev;
0203 
0204     gnss_deregister_device(gserial->gdev);
0205 
0206     if (IS_ENABLED(CONFIG_PM))
0207         pm_runtime_disable(&serdev->dev);
0208     else
0209         gnss_serial_set_power(gserial, GNSS_SERIAL_OFF);
0210 }
0211 EXPORT_SYMBOL_GPL(gnss_serial_deregister);
0212 
0213 #ifdef CONFIG_PM
0214 static int gnss_serial_runtime_suspend(struct device *dev)
0215 {
0216     struct gnss_serial *gserial = dev_get_drvdata(dev);
0217 
0218     return gnss_serial_set_power(gserial, GNSS_SERIAL_STANDBY);
0219 }
0220 
0221 static int gnss_serial_runtime_resume(struct device *dev)
0222 {
0223     struct gnss_serial *gserial = dev_get_drvdata(dev);
0224 
0225     return gnss_serial_set_power(gserial, GNSS_SERIAL_ACTIVE);
0226 }
0227 #endif /* CONFIG_PM */
0228 
0229 static int gnss_serial_prepare(struct device *dev)
0230 {
0231     if (pm_runtime_suspended(dev))
0232         return 1;
0233 
0234     return 0;
0235 }
0236 
0237 #ifdef CONFIG_PM_SLEEP
0238 static int gnss_serial_suspend(struct device *dev)
0239 {
0240     struct gnss_serial *gserial = dev_get_drvdata(dev);
0241     int ret = 0;
0242 
0243     /*
0244      * FIXME: serdev currently lacks support for managing the underlying
0245      * device's wakeup settings. A workaround would be to close the serdev
0246      * device here if it is open.
0247      */
0248 
0249     if (!pm_runtime_suspended(dev))
0250         ret = gnss_serial_set_power(gserial, GNSS_SERIAL_STANDBY);
0251 
0252     return ret;
0253 }
0254 
0255 static int gnss_serial_resume(struct device *dev)
0256 {
0257     struct gnss_serial *gserial = dev_get_drvdata(dev);
0258     int ret = 0;
0259 
0260     if (!pm_runtime_suspended(dev))
0261         ret = gnss_serial_set_power(gserial, GNSS_SERIAL_ACTIVE);
0262 
0263     return ret;
0264 }
0265 #endif /* CONFIG_PM_SLEEP */
0266 
0267 const struct dev_pm_ops gnss_serial_pm_ops = {
0268     .prepare    = gnss_serial_prepare,
0269     SET_SYSTEM_SLEEP_PM_OPS(gnss_serial_suspend, gnss_serial_resume)
0270     SET_RUNTIME_PM_OPS(gnss_serial_runtime_suspend, gnss_serial_runtime_resume, NULL)
0271 };
0272 EXPORT_SYMBOL_GPL(gnss_serial_pm_ops);
0273 
0274 MODULE_AUTHOR("Johan Hovold <johan@kernel.org>");
0275 MODULE_DESCRIPTION("Generic serial GNSS receiver driver");
0276 MODULE_LICENSE("GPL v2");