1 // SPDX-License-Identifier: GPL-2.0+
3 * UART Link Layer for S3FWRN82 NCI based Driver
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>
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>
17 #include <linux/serdev.h>
18 #include <linux/gpio.h>
19 #include <linux/of_gpio.h>
21 #include "phy_common.h"
23 #define S3FWRN82_NCI_HEADER 3
24 #define S3FWRN82_NCI_IDX 2
25 #define NCI_SKB_BUFF_LEN 258
27 struct s3fwrn82_uart_phy
{
28 struct phy_common common
;
29 struct serdev_device
*ser_dev
;
30 struct sk_buff
*recv_skb
;
33 static int s3fwrn82_uart_write(void *phy_id
, struct sk_buff
*out
)
35 struct s3fwrn82_uart_phy
*phy
= phy_id
;
38 err
= serdev_device_write(phy
->ser_dev
,
40 MAX_SCHEDULE_TIMEOUT
);
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
,
54 static int s3fwrn82_uart_read(struct serdev_device
*serdev
,
55 const unsigned char *data
,
58 struct s3fwrn82_uart_phy
*phy
= serdev_device_get_drvdata(serdev
);
61 for (i
= 0; i
< count
; i
++) {
62 skb_put_u8(phy
->recv_skb
, *data
++);
64 if (phy
->recv_skb
->len
< S3FWRN82_NCI_HEADER
)
67 if ((phy
->recv_skb
->len
- S3FWRN82_NCI_HEADER
)
68 < phy
->recv_skb
->data
[S3FWRN82_NCI_IDX
])
71 s3fwrn5_recv_frame(phy
->common
.ndev
, phy
->recv_skb
,
73 phy
->recv_skb
= alloc_skb(NCI_SKB_BUFF_LEN
, GFP_KERNEL
);
81 static const struct serdev_device_ops s3fwrn82_serdev_ops
= {
82 .receive_buf
= s3fwrn82_uart_read
,
83 .write_wakeup
= serdev_device_write_wakeup
,
86 static const struct of_device_id s3fwrn82_uart_of_match
[] = {
87 { .compatible
= "samsung,s3fwrn82", },
90 MODULE_DEVICE_TABLE(of
, s3fwrn82_uart_of_match
);
92 static int s3fwrn82_uart_parse_dt(struct serdev_device
*serdev
)
94 struct s3fwrn82_uart_phy
*phy
= serdev_device_get_drvdata(serdev
);
95 struct device_node
*np
= serdev
->dev
.of_node
;
100 phy
->common
.gpio_en
= of_get_named_gpio(np
, "en-gpios", 0);
101 if (!gpio_is_valid(phy
->common
.gpio_en
))
104 phy
->common
.gpio_fw_wake
= of_get_named_gpio(np
, "wake-gpios", 0);
105 if (!gpio_is_valid(phy
->common
.gpio_fw_wake
))
111 static int s3fwrn82_uart_probe(struct serdev_device
*serdev
)
113 struct s3fwrn82_uart_phy
*phy
;
116 phy
= devm_kzalloc(&serdev
->dev
, sizeof(*phy
), GFP_KERNEL
);
120 phy
->recv_skb
= alloc_skb(NCI_SKB_BUFF_LEN
, GFP_KERNEL
);
124 mutex_init(&phy
->common
.mutex
);
125 phy
->common
.mode
= S3FWRN5_MODE_COLD
;
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
);
132 dev_err(&serdev
->dev
, "Unable to open device\n");
136 ret
= serdev_device_set_baudrate(serdev
, 115200);
142 serdev_device_set_flow_control(serdev
, false);
144 ret
= s3fwrn82_uart_parse_dt(serdev
);
148 ret
= devm_gpio_request_one(&phy
->ser_dev
->dev
, phy
->common
.gpio_en
,
149 GPIOF_OUT_INIT_HIGH
, "s3fwrn82_en");
153 ret
= devm_gpio_request_one(&phy
->ser_dev
->dev
,
154 phy
->common
.gpio_fw_wake
,
155 GPIOF_OUT_INIT_LOW
, "s3fwrn82_fw_wake");
159 ret
= s3fwrn5_probe(&phy
->common
.ndev
, phy
, &phy
->ser_dev
->dev
,
167 serdev_device_close(serdev
);
169 kfree_skb(phy
->recv_skb
);
174 static void s3fwrn82_uart_remove(struct serdev_device
*serdev
)
176 struct s3fwrn82_uart_phy
*phy
= serdev_device_get_drvdata(serdev
);
178 s3fwrn5_remove(phy
->common
.ndev
);
179 serdev_device_close(serdev
);
180 kfree_skb(phy
->recv_skb
);
183 static struct serdev_device_driver s3fwrn82_uart_driver
= {
184 .probe
= s3fwrn82_uart_probe
,
185 .remove
= s3fwrn82_uart_remove
,
187 .name
= "s3fwrn82_uart",
188 .of_match_table
= s3fwrn82_uart_of_match
,
192 module_serdev_device_driver(s3fwrn82_uart_driver
);
194 MODULE_LICENSE("GPL");
195 MODULE_DESCRIPTION("UART driver for Samsung NFC");
196 MODULE_AUTHOR("Bongsu Jeon <bongsu.jeon@samsung.com>");