123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196 |
- // SPDX-License-Identifier: GPL-2.0+
- /*
- * UART Link Layer for S3FWRN82 NCI based Driver
- *
- * Copyright (C) 2015 Samsung Electronics
- * Robert Baldyga <[email protected]>
- * Copyright (C) 2020 Samsung Electronics
- * Bongsu Jeon <[email protected]>
- */
- #include <linux/device.h>
- #include <linux/kernel.h>
- #include <linux/module.h>
- #include <linux/nfc.h>
- #include <linux/netdevice.h>
- #include <linux/of.h>
- #include <linux/serdev.h>
- #include <linux/gpio.h>
- #include <linux/of_gpio.h>
- #include "phy_common.h"
- #define S3FWRN82_NCI_HEADER 3
- #define S3FWRN82_NCI_IDX 2
- #define NCI_SKB_BUFF_LEN 258
- struct s3fwrn82_uart_phy {
- struct phy_common common;
- struct serdev_device *ser_dev;
- struct sk_buff *recv_skb;
- };
- static int s3fwrn82_uart_write(void *phy_id, struct sk_buff *out)
- {
- struct s3fwrn82_uart_phy *phy = phy_id;
- int err;
- err = serdev_device_write(phy->ser_dev,
- out->data, out->len,
- MAX_SCHEDULE_TIMEOUT);
- if (err < 0)
- return err;
- return 0;
- }
- static const struct s3fwrn5_phy_ops uart_phy_ops = {
- .set_wake = s3fwrn5_phy_set_wake,
- .set_mode = s3fwrn5_phy_set_mode,
- .get_mode = s3fwrn5_phy_get_mode,
- .write = s3fwrn82_uart_write,
- };
- static int s3fwrn82_uart_read(struct serdev_device *serdev,
- const unsigned char *data,
- size_t count)
- {
- struct s3fwrn82_uart_phy *phy = serdev_device_get_drvdata(serdev);
- size_t i;
- for (i = 0; i < count; i++) {
- skb_put_u8(phy->recv_skb, *data++);
- if (phy->recv_skb->len < S3FWRN82_NCI_HEADER)
- continue;
- if ((phy->recv_skb->len - S3FWRN82_NCI_HEADER)
- < phy->recv_skb->data[S3FWRN82_NCI_IDX])
- continue;
- s3fwrn5_recv_frame(phy->common.ndev, phy->recv_skb,
- phy->common.mode);
- phy->recv_skb = alloc_skb(NCI_SKB_BUFF_LEN, GFP_KERNEL);
- if (!phy->recv_skb)
- return 0;
- }
- return i;
- }
- static const struct serdev_device_ops s3fwrn82_serdev_ops = {
- .receive_buf = s3fwrn82_uart_read,
- .write_wakeup = serdev_device_write_wakeup,
- };
- static const struct of_device_id s3fwrn82_uart_of_match[] = {
- { .compatible = "samsung,s3fwrn82", },
- {},
- };
- MODULE_DEVICE_TABLE(of, s3fwrn82_uart_of_match);
- static int s3fwrn82_uart_parse_dt(struct serdev_device *serdev)
- {
- struct s3fwrn82_uart_phy *phy = serdev_device_get_drvdata(serdev);
- struct device_node *np = serdev->dev.of_node;
- if (!np)
- return -ENODEV;
- phy->common.gpio_en = of_get_named_gpio(np, "en-gpios", 0);
- if (!gpio_is_valid(phy->common.gpio_en))
- return -ENODEV;
- phy->common.gpio_fw_wake = of_get_named_gpio(np, "wake-gpios", 0);
- if (!gpio_is_valid(phy->common.gpio_fw_wake))
- return -ENODEV;
- return 0;
- }
- static int s3fwrn82_uart_probe(struct serdev_device *serdev)
- {
- struct s3fwrn82_uart_phy *phy;
- int ret = -ENOMEM;
- phy = devm_kzalloc(&serdev->dev, sizeof(*phy), GFP_KERNEL);
- if (!phy)
- goto err_exit;
- phy->recv_skb = alloc_skb(NCI_SKB_BUFF_LEN, GFP_KERNEL);
- if (!phy->recv_skb)
- goto err_exit;
- mutex_init(&phy->common.mutex);
- phy->common.mode = S3FWRN5_MODE_COLD;
- phy->ser_dev = serdev;
- serdev_device_set_drvdata(serdev, phy);
- serdev_device_set_client_ops(serdev, &s3fwrn82_serdev_ops);
- ret = serdev_device_open(serdev);
- if (ret) {
- dev_err(&serdev->dev, "Unable to open device\n");
- goto err_skb;
- }
- ret = serdev_device_set_baudrate(serdev, 115200);
- if (ret != 115200) {
- ret = -EINVAL;
- goto err_serdev;
- }
- serdev_device_set_flow_control(serdev, false);
- ret = s3fwrn82_uart_parse_dt(serdev);
- if (ret < 0)
- goto err_serdev;
- ret = devm_gpio_request_one(&phy->ser_dev->dev, phy->common.gpio_en,
- GPIOF_OUT_INIT_HIGH, "s3fwrn82_en");
- if (ret < 0)
- goto err_serdev;
- ret = devm_gpio_request_one(&phy->ser_dev->dev,
- phy->common.gpio_fw_wake,
- GPIOF_OUT_INIT_LOW, "s3fwrn82_fw_wake");
- if (ret < 0)
- goto err_serdev;
- ret = s3fwrn5_probe(&phy->common.ndev, phy, &phy->ser_dev->dev,
- &uart_phy_ops);
- if (ret < 0)
- goto err_serdev;
- return ret;
- err_serdev:
- serdev_device_close(serdev);
- err_skb:
- kfree_skb(phy->recv_skb);
- err_exit:
- return ret;
- }
- static void s3fwrn82_uart_remove(struct serdev_device *serdev)
- {
- struct s3fwrn82_uart_phy *phy = serdev_device_get_drvdata(serdev);
- s3fwrn5_remove(phy->common.ndev);
- serdev_device_close(serdev);
- kfree_skb(phy->recv_skb);
- }
- static struct serdev_device_driver s3fwrn82_uart_driver = {
- .probe = s3fwrn82_uart_probe,
- .remove = s3fwrn82_uart_remove,
- .driver = {
- .name = "s3fwrn82_uart",
- .of_match_table = s3fwrn82_uart_of_match,
- },
- };
- module_serdev_device_driver(s3fwrn82_uart_driver);
- MODULE_LICENSE("GPL");
- MODULE_DESCRIPTION("UART driver for Samsung NFC");
- MODULE_AUTHOR("Bongsu Jeon <[email protected]>");
|