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

uart.c (4635B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 * UART Link Layer for S3FWRN82 NCI based Driver
      4 *
      5 * Copyright (C) 2015 Samsung Electronics
      6 * Robert Baldyga <r.baldyga@samsung.com>
      7 * Copyright (C) 2020 Samsung Electronics
      8 * Bongsu Jeon <bongsu.jeon@samsung.com>
      9 */
     10
     11#include <linux/device.h>
     12#include <linux/kernel.h>
     13#include <linux/module.h>
     14#include <linux/nfc.h>
     15#include <linux/netdevice.h>
     16#include <linux/of.h>
     17#include <linux/serdev.h>
     18#include <linux/gpio.h>
     19#include <linux/of_gpio.h>
     20
     21#include "phy_common.h"
     22
     23#define S3FWRN82_NCI_HEADER 3
     24#define S3FWRN82_NCI_IDX 2
     25#define NCI_SKB_BUFF_LEN 258
     26
     27struct s3fwrn82_uart_phy {
     28	struct phy_common common;
     29	struct serdev_device *ser_dev;
     30	struct sk_buff *recv_skb;
     31};
     32
     33static int s3fwrn82_uart_write(void *phy_id, struct sk_buff *out)
     34{
     35	struct s3fwrn82_uart_phy *phy = phy_id;
     36	int err;
     37
     38	err = serdev_device_write(phy->ser_dev,
     39				  out->data, out->len,
     40				  MAX_SCHEDULE_TIMEOUT);
     41	if (err < 0)
     42		return err;
     43
     44	return 0;
     45}
     46
     47static const struct s3fwrn5_phy_ops uart_phy_ops = {
     48	.set_wake = s3fwrn5_phy_set_wake,
     49	.set_mode = s3fwrn5_phy_set_mode,
     50	.get_mode = s3fwrn5_phy_get_mode,
     51	.write = s3fwrn82_uart_write,
     52};
     53
     54static int s3fwrn82_uart_read(struct serdev_device *serdev,
     55			      const unsigned char *data,
     56			      size_t count)
     57{
     58	struct s3fwrn82_uart_phy *phy = serdev_device_get_drvdata(serdev);
     59	size_t i;
     60
     61	for (i = 0; i < count; i++) {
     62		skb_put_u8(phy->recv_skb, *data++);
     63
     64		if (phy->recv_skb->len < S3FWRN82_NCI_HEADER)
     65			continue;
     66
     67		if ((phy->recv_skb->len - S3FWRN82_NCI_HEADER)
     68				< phy->recv_skb->data[S3FWRN82_NCI_IDX])
     69			continue;
     70
     71		s3fwrn5_recv_frame(phy->common.ndev, phy->recv_skb,
     72				   phy->common.mode);
     73		phy->recv_skb = alloc_skb(NCI_SKB_BUFF_LEN, GFP_KERNEL);
     74		if (!phy->recv_skb)
     75			return 0;
     76	}
     77
     78	return i;
     79}
     80
     81static const struct serdev_device_ops s3fwrn82_serdev_ops = {
     82	.receive_buf = s3fwrn82_uart_read,
     83	.write_wakeup = serdev_device_write_wakeup,
     84};
     85
     86static const struct of_device_id s3fwrn82_uart_of_match[] = {
     87	{ .compatible = "samsung,s3fwrn82", },
     88	{},
     89};
     90MODULE_DEVICE_TABLE(of, s3fwrn82_uart_of_match);
     91
     92static int s3fwrn82_uart_parse_dt(struct serdev_device *serdev)
     93{
     94	struct s3fwrn82_uart_phy *phy = serdev_device_get_drvdata(serdev);
     95	struct device_node *np = serdev->dev.of_node;
     96
     97	if (!np)
     98		return -ENODEV;
     99
    100	phy->common.gpio_en = of_get_named_gpio(np, "en-gpios", 0);
    101	if (!gpio_is_valid(phy->common.gpio_en))
    102		return -ENODEV;
    103
    104	phy->common.gpio_fw_wake = of_get_named_gpio(np, "wake-gpios", 0);
    105	if (!gpio_is_valid(phy->common.gpio_fw_wake))
    106		return -ENODEV;
    107
    108	return 0;
    109}
    110
    111static int s3fwrn82_uart_probe(struct serdev_device *serdev)
    112{
    113	struct s3fwrn82_uart_phy *phy;
    114	int ret = -ENOMEM;
    115
    116	phy = devm_kzalloc(&serdev->dev, sizeof(*phy), GFP_KERNEL);
    117	if (!phy)
    118		goto err_exit;
    119
    120	phy->recv_skb = alloc_skb(NCI_SKB_BUFF_LEN, GFP_KERNEL);
    121	if (!phy->recv_skb)
    122		goto err_exit;
    123
    124	mutex_init(&phy->common.mutex);
    125	phy->common.mode = S3FWRN5_MODE_COLD;
    126
    127	phy->ser_dev = serdev;
    128	serdev_device_set_drvdata(serdev, phy);
    129	serdev_device_set_client_ops(serdev, &s3fwrn82_serdev_ops);
    130	ret = serdev_device_open(serdev);
    131	if (ret) {
    132		dev_err(&serdev->dev, "Unable to open device\n");
    133		goto err_skb;
    134	}
    135
    136	ret = serdev_device_set_baudrate(serdev, 115200);
    137	if (ret != 115200) {
    138		ret = -EINVAL;
    139		goto err_serdev;
    140	}
    141
    142	serdev_device_set_flow_control(serdev, false);
    143
    144	ret = s3fwrn82_uart_parse_dt(serdev);
    145	if (ret < 0)
    146		goto err_serdev;
    147
    148	ret = devm_gpio_request_one(&phy->ser_dev->dev, phy->common.gpio_en,
    149				    GPIOF_OUT_INIT_HIGH, "s3fwrn82_en");
    150	if (ret < 0)
    151		goto err_serdev;
    152
    153	ret = devm_gpio_request_one(&phy->ser_dev->dev,
    154				    phy->common.gpio_fw_wake,
    155				    GPIOF_OUT_INIT_LOW, "s3fwrn82_fw_wake");
    156	if (ret < 0)
    157		goto err_serdev;
    158
    159	ret = s3fwrn5_probe(&phy->common.ndev, phy, &phy->ser_dev->dev,
    160			    &uart_phy_ops);
    161	if (ret < 0)
    162		goto err_serdev;
    163
    164	return ret;
    165
    166err_serdev:
    167	serdev_device_close(serdev);
    168err_skb:
    169	kfree_skb(phy->recv_skb);
    170err_exit:
    171	return ret;
    172}
    173
    174static void s3fwrn82_uart_remove(struct serdev_device *serdev)
    175{
    176	struct s3fwrn82_uart_phy *phy = serdev_device_get_drvdata(serdev);
    177
    178	s3fwrn5_remove(phy->common.ndev);
    179	serdev_device_close(serdev);
    180	kfree_skb(phy->recv_skb);
    181}
    182
    183static struct serdev_device_driver s3fwrn82_uart_driver = {
    184	.probe = s3fwrn82_uart_probe,
    185	.remove = s3fwrn82_uart_remove,
    186	.driver = {
    187		.name = "s3fwrn82_uart",
    188		.of_match_table = s3fwrn82_uart_of_match,
    189	},
    190};
    191
    192module_serdev_device_driver(s3fwrn82_uart_driver);
    193
    194MODULE_LICENSE("GPL");
    195MODULE_DESCRIPTION("UART driver for Samsung NFC");
    196MODULE_AUTHOR("Bongsu Jeon <bongsu.jeon@samsung.com>");