cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

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");