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/serdev.h> 17 #include <linux/gpio/consumer.h> 18 19 #include "phy_common.h" 20 21 #define S3FWRN82_NCI_HEADER 3 22 #define S3FWRN82_NCI_IDX 2 23 #define NCI_SKB_BUFF_LEN 258 24 25 struct s3fwrn82_uart_phy { 26 struct phy_common common; 27 struct serdev_device *ser_dev; 28 struct sk_buff *recv_skb; 29 }; 30 31 static int s3fwrn82_uart_write(void *phy_id, struct sk_buff *out) 32 { 33 struct s3fwrn82_uart_phy *phy = phy_id; 34 int err; 35 36 err = serdev_device_write(phy->ser_dev, 37 out->data, out->len, 38 MAX_SCHEDULE_TIMEOUT); 39 if (err < 0) 40 return err; 41 42 return 0; 43 } 44 45 static const struct s3fwrn5_phy_ops uart_phy_ops = { 46 .set_wake = s3fwrn5_phy_set_wake, 47 .set_mode = s3fwrn5_phy_set_mode, 48 .get_mode = s3fwrn5_phy_get_mode, 49 .write = s3fwrn82_uart_write, 50 }; 51 52 static size_t s3fwrn82_uart_read(struct serdev_device *serdev, 53 const u8 *data, size_t count) 54 { 55 struct s3fwrn82_uart_phy *phy = serdev_device_get_drvdata(serdev); 56 size_t i; 57 58 for (i = 0; i < count; i++) { 59 if (!phy->recv_skb) { 60 phy->recv_skb = alloc_skb(NCI_SKB_BUFF_LEN, GFP_KERNEL); 61 if (!phy->recv_skb) 62 return i; 63 } 64 65 skb_put_u8(phy->recv_skb, *data++); 66 67 if (phy->recv_skb->len < S3FWRN82_NCI_HEADER) 68 continue; 69 70 if ((phy->recv_skb->len - S3FWRN82_NCI_HEADER) 71 < phy->recv_skb->data[S3FWRN82_NCI_IDX]) 72 continue; 73 74 s3fwrn5_recv_frame(phy->common.ndev, phy->recv_skb, 75 phy->common.mode); 76 phy->recv_skb = NULL; 77 } 78 79 return i; 80 } 81 82 static const struct serdev_device_ops s3fwrn82_serdev_ops = { 83 .receive_buf = s3fwrn82_uart_read, 84 .write_wakeup = serdev_device_write_wakeup, 85 }; 86 87 static const struct of_device_id s3fwrn82_uart_of_match[] = { 88 { .compatible = "samsung,s3fwrn82", }, 89 {}, 90 }; 91 MODULE_DEVICE_TABLE(of, s3fwrn82_uart_of_match); 92 93 static int s3fwrn82_uart_probe(struct serdev_device *serdev) 94 { 95 struct s3fwrn82_uart_phy *phy; 96 int ret = -ENOMEM; 97 98 phy = devm_kzalloc(&serdev->dev, sizeof(*phy), GFP_KERNEL); 99 if (!phy) 100 goto err_exit; 101 102 phy->recv_skb = alloc_skb(NCI_SKB_BUFF_LEN, GFP_KERNEL); 103 if (!phy->recv_skb) 104 goto err_exit; 105 106 mutex_init(&phy->common.mutex); 107 phy->common.mode = S3FWRN5_MODE_COLD; 108 109 phy->ser_dev = serdev; 110 serdev_device_set_drvdata(serdev, phy); 111 serdev_device_set_client_ops(serdev, &s3fwrn82_serdev_ops); 112 ret = serdev_device_open(serdev); 113 if (ret) { 114 dev_err(&serdev->dev, "Unable to open device\n"); 115 goto err_skb; 116 } 117 118 ret = serdev_device_set_baudrate(serdev, 115200); 119 if (ret != 115200) { 120 ret = -EINVAL; 121 goto err_serdev; 122 } 123 124 serdev_device_set_flow_control(serdev, false); 125 126 phy->common.gpio_en = devm_gpiod_get(&serdev->dev, "en", GPIOD_OUT_HIGH); 127 if (IS_ERR(phy->common.gpio_en)) { 128 ret = PTR_ERR(phy->common.gpio_en); 129 goto err_serdev; 130 } 131 132 phy->common.gpio_fw_wake = devm_gpiod_get(&serdev->dev, "wake", GPIOD_OUT_LOW); 133 if (IS_ERR(phy->common.gpio_fw_wake)) { 134 ret = PTR_ERR(phy->common.gpio_fw_wake); 135 goto err_serdev; 136 } 137 138 ret = s3fwrn5_probe(&phy->common.ndev, phy, &phy->ser_dev->dev, 139 &uart_phy_ops); 140 if (ret < 0) 141 goto err_serdev; 142 143 return ret; 144 145 err_serdev: 146 serdev_device_close(serdev); 147 err_skb: 148 kfree_skb(phy->recv_skb); 149 err_exit: 150 return ret; 151 } 152 153 static void s3fwrn82_uart_remove(struct serdev_device *serdev) 154 { 155 struct s3fwrn82_uart_phy *phy = serdev_device_get_drvdata(serdev); 156 157 s3fwrn5_remove(phy->common.ndev); 158 serdev_device_close(serdev); 159 kfree_skb(phy->recv_skb); 160 } 161 162 static struct serdev_device_driver s3fwrn82_uart_driver = { 163 .probe = s3fwrn82_uart_probe, 164 .remove = s3fwrn82_uart_remove, 165 .driver = { 166 .name = "s3fwrn82_uart", 167 .of_match_table = s3fwrn82_uart_of_match, 168 }, 169 }; 170 171 module_serdev_device_driver(s3fwrn82_uart_driver); 172 173 MODULE_LICENSE("GPL"); 174 MODULE_DESCRIPTION("UART driver for Samsung NFC"); 175 MODULE_AUTHOR("Bongsu Jeon <bongsu.jeon@samsung.com>"); 176