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 27 struct s3fwrn82_uart_phy { 28 struct phy_common common; 29 struct serdev_device *ser_dev; 30 struct sk_buff *recv_skb; 31 }; 32 33 static 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 47 static 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 54 static size_t s3fwrn82_uart_read(struct serdev_device *serdev, 55 const u8 *data, size_t count) 56 { 57 struct s3fwrn82_uart_phy *phy = serdev_device_get_drvdata(serdev); 58 size_t i; 59 60 for (i = 0; i < count; i++) { 61 skb_put_u8(phy->recv_skb, *data++); 62 63 if (phy->recv_skb->len < S3FWRN82_NCI_HEADER) 64 continue; 65 66 if ((phy->recv_skb->len - S3FWRN82_NCI_HEADER) 67 < phy->recv_skb->data[S3FWRN82_NCI_IDX]) 68 continue; 69 70 s3fwrn5_recv_frame(phy->common.ndev, phy->recv_skb, 71 phy->common.mode); 72 phy->recv_skb = alloc_skb(NCI_SKB_BUFF_LEN, GFP_KERNEL); 73 if (!phy->recv_skb) 74 return 0; 75 } 76 77 return i; 78 } 79 80 static const struct serdev_device_ops s3fwrn82_serdev_ops = { 81 .receive_buf = s3fwrn82_uart_read, 82 .write_wakeup = serdev_device_write_wakeup, 83 }; 84 85 static const struct of_device_id s3fwrn82_uart_of_match[] = { 86 { .compatible = "samsung,s3fwrn82", }, 87 {}, 88 }; 89 MODULE_DEVICE_TABLE(of, s3fwrn82_uart_of_match); 90 91 static int s3fwrn82_uart_parse_dt(struct serdev_device *serdev) 92 { 93 struct s3fwrn82_uart_phy *phy = serdev_device_get_drvdata(serdev); 94 struct device_node *np = serdev->dev.of_node; 95 96 if (!np) 97 return -ENODEV; 98 99 phy->common.gpio_en = of_get_named_gpio(np, "en-gpios", 0); 100 if (!gpio_is_valid(phy->common.gpio_en)) 101 return -ENODEV; 102 103 phy->common.gpio_fw_wake = of_get_named_gpio(np, "wake-gpios", 0); 104 if (!gpio_is_valid(phy->common.gpio_fw_wake)) 105 return -ENODEV; 106 107 return 0; 108 } 109 110 static int s3fwrn82_uart_probe(struct serdev_device *serdev) 111 { 112 struct s3fwrn82_uart_phy *phy; 113 int ret = -ENOMEM; 114 115 phy = devm_kzalloc(&serdev->dev, sizeof(*phy), GFP_KERNEL); 116 if (!phy) 117 goto err_exit; 118 119 phy->recv_skb = alloc_skb(NCI_SKB_BUFF_LEN, GFP_KERNEL); 120 if (!phy->recv_skb) 121 goto err_exit; 122 123 mutex_init(&phy->common.mutex); 124 phy->common.mode = S3FWRN5_MODE_COLD; 125 126 phy->ser_dev = serdev; 127 serdev_device_set_drvdata(serdev, phy); 128 serdev_device_set_client_ops(serdev, &s3fwrn82_serdev_ops); 129 ret = serdev_device_open(serdev); 130 if (ret) { 131 dev_err(&serdev->dev, "Unable to open device\n"); 132 goto err_skb; 133 } 134 135 ret = serdev_device_set_baudrate(serdev, 115200); 136 if (ret != 115200) { 137 ret = -EINVAL; 138 goto err_serdev; 139 } 140 141 serdev_device_set_flow_control(serdev, false); 142 143 ret = s3fwrn82_uart_parse_dt(serdev); 144 if (ret < 0) 145 goto err_serdev; 146 147 ret = devm_gpio_request_one(&phy->ser_dev->dev, phy->common.gpio_en, 148 GPIOF_OUT_INIT_HIGH, "s3fwrn82_en"); 149 if (ret < 0) 150 goto err_serdev; 151 152 ret = devm_gpio_request_one(&phy->ser_dev->dev, 153 phy->common.gpio_fw_wake, 154 GPIOF_OUT_INIT_LOW, "s3fwrn82_fw_wake"); 155 if (ret < 0) 156 goto err_serdev; 157 158 ret = s3fwrn5_probe(&phy->common.ndev, phy, &phy->ser_dev->dev, 159 &uart_phy_ops); 160 if (ret < 0) 161 goto err_serdev; 162 163 return ret; 164 165 err_serdev: 166 serdev_device_close(serdev); 167 err_skb: 168 kfree_skb(phy->recv_skb); 169 err_exit: 170 return ret; 171 } 172 173 static void s3fwrn82_uart_remove(struct serdev_device *serdev) 174 { 175 struct s3fwrn82_uart_phy *phy = serdev_device_get_drvdata(serdev); 176 177 s3fwrn5_remove(phy->common.ndev); 178 serdev_device_close(serdev); 179 kfree_skb(phy->recv_skb); 180 } 181 182 static struct serdev_device_driver s3fwrn82_uart_driver = { 183 .probe = s3fwrn82_uart_probe, 184 .remove = s3fwrn82_uart_remove, 185 .driver = { 186 .name = "s3fwrn82_uart", 187 .of_match_table = s3fwrn82_uart_of_match, 188 }, 189 }; 190 191 module_serdev_device_driver(s3fwrn82_uart_driver); 192 193 MODULE_LICENSE("GPL"); 194 MODULE_DESCRIPTION("UART driver for Samsung NFC"); 195 MODULE_AUTHOR("Bongsu Jeon <bongsu.jeon@samsung.com>"); 196