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 size_t s3fwrn82_uart_read(struct serdev_device
*serdev
,
55 const u8
*data
, size_t count
)
57 struct s3fwrn82_uart_phy
*phy
= serdev_device_get_drvdata(serdev
);
60 for (i
= 0; i
< count
; i
++) {
61 skb_put_u8(phy
->recv_skb
, *data
++);
63 if (phy
->recv_skb
->len
< S3FWRN82_NCI_HEADER
)
66 if ((phy
->recv_skb
->len
- S3FWRN82_NCI_HEADER
)
67 < phy
->recv_skb
->data
[S3FWRN82_NCI_IDX
])
70 s3fwrn5_recv_frame(phy
->common
.ndev
, phy
->recv_skb
,
72 phy
->recv_skb
= alloc_skb(NCI_SKB_BUFF_LEN
, GFP_KERNEL
);
80 static const struct serdev_device_ops s3fwrn82_serdev_ops
= {
81 .receive_buf
= s3fwrn82_uart_read
,
82 .write_wakeup
= serdev_device_write_wakeup
,
85 static const struct of_device_id s3fwrn82_uart_of_match
[] = {
86 { .compatible
= "samsung,s3fwrn82", },
89 MODULE_DEVICE_TABLE(of
, s3fwrn82_uart_of_match
);
91 static int s3fwrn82_uart_parse_dt(struct serdev_device
*serdev
)
93 struct s3fwrn82_uart_phy
*phy
= serdev_device_get_drvdata(serdev
);
94 struct device_node
*np
= serdev
->dev
.of_node
;
99 phy
->common
.gpio_en
= of_get_named_gpio(np
, "en-gpios", 0);
100 if (!gpio_is_valid(phy
->common
.gpio_en
))
103 phy
->common
.gpio_fw_wake
= of_get_named_gpio(np
, "wake-gpios", 0);
104 if (!gpio_is_valid(phy
->common
.gpio_fw_wake
))
110 static int s3fwrn82_uart_probe(struct serdev_device
*serdev
)
112 struct s3fwrn82_uart_phy
*phy
;
115 phy
= devm_kzalloc(&serdev
->dev
, sizeof(*phy
), GFP_KERNEL
);
119 phy
->recv_skb
= alloc_skb(NCI_SKB_BUFF_LEN
, GFP_KERNEL
);
123 mutex_init(&phy
->common
.mutex
);
124 phy
->common
.mode
= S3FWRN5_MODE_COLD
;
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
);
131 dev_err(&serdev
->dev
, "Unable to open device\n");
135 ret
= serdev_device_set_baudrate(serdev
, 115200);
141 serdev_device_set_flow_control(serdev
, false);
143 ret
= s3fwrn82_uart_parse_dt(serdev
);
147 ret
= devm_gpio_request_one(&phy
->ser_dev
->dev
, phy
->common
.gpio_en
,
148 GPIOF_OUT_INIT_HIGH
, "s3fwrn82_en");
152 ret
= devm_gpio_request_one(&phy
->ser_dev
->dev
,
153 phy
->common
.gpio_fw_wake
,
154 GPIOF_OUT_INIT_LOW
, "s3fwrn82_fw_wake");
158 ret
= s3fwrn5_probe(&phy
->common
.ndev
, phy
, &phy
->ser_dev
->dev
,
166 serdev_device_close(serdev
);
168 kfree_skb(phy
->recv_skb
);
173 static void s3fwrn82_uart_remove(struct serdev_device
*serdev
)
175 struct s3fwrn82_uart_phy
*phy
= serdev_device_get_drvdata(serdev
);
177 s3fwrn5_remove(phy
->common
.ndev
);
178 serdev_device_close(serdev
);
179 kfree_skb(phy
->recv_skb
);
182 static struct serdev_device_driver s3fwrn82_uart_driver
= {
183 .probe
= s3fwrn82_uart_probe
,
184 .remove
= s3fwrn82_uart_remove
,
186 .name
= "s3fwrn82_uart",
187 .of_match_table
= s3fwrn82_uart_of_match
,
191 module_serdev_device_driver(s3fwrn82_uart_driver
);
193 MODULE_LICENSE("GPL");
194 MODULE_DESCRIPTION("UART driver for Samsung NFC");
195 MODULE_AUTHOR("Bongsu Jeon <bongsu.jeon@samsung.com>");