1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * I2C Link Layer for Samsung S3FWRN5 NCI based Driver
5 * Copyright (C) 2015 Samsung Electrnoics
6 * Robert Baldyga <r.baldyga@samsung.com>
10 #include <linux/gpio.h>
11 #include <linux/delay.h>
12 #include <linux/of_gpio.h>
13 #include <linux/of_irq.h>
14 #include <linux/module.h>
16 #include <net/nfc/nfc.h>
18 #include "phy_common.h"
20 #define S3FWRN5_I2C_DRIVER_NAME "s3fwrn5_i2c"
22 struct s3fwrn5_i2c_phy
{
23 struct phy_common common
;
24 struct i2c_client
*i2c_dev
;
26 unsigned int irq_skip
:1;
29 static void s3fwrn5_i2c_set_mode(void *phy_id
, enum s3fwrn5_mode mode
)
31 struct s3fwrn5_i2c_phy
*phy
= phy_id
;
33 mutex_lock(&phy
->common
.mutex
);
35 if (s3fwrn5_phy_power_ctrl(&phy
->common
, mode
) == false)
41 mutex_unlock(&phy
->common
.mutex
);
44 static int s3fwrn5_i2c_write(void *phy_id
, struct sk_buff
*skb
)
46 struct s3fwrn5_i2c_phy
*phy
= phy_id
;
49 mutex_lock(&phy
->common
.mutex
);
51 phy
->irq_skip
= false;
53 ret
= i2c_master_send(phy
->i2c_dev
, skb
->data
, skb
->len
);
54 if (ret
== -EREMOTEIO
) {
55 /* Retry, chip was in standby */
56 usleep_range(110000, 120000);
57 ret
= i2c_master_send(phy
->i2c_dev
, skb
->data
, skb
->len
);
60 mutex_unlock(&phy
->common
.mutex
);
71 static const struct s3fwrn5_phy_ops i2c_phy_ops
= {
72 .set_wake
= s3fwrn5_phy_set_wake
,
73 .set_mode
= s3fwrn5_i2c_set_mode
,
74 .get_mode
= s3fwrn5_phy_get_mode
,
75 .write
= s3fwrn5_i2c_write
,
78 static int s3fwrn5_i2c_read(struct s3fwrn5_i2c_phy
*phy
)
86 hdr_size
= (phy
->common
.mode
== S3FWRN5_MODE_NCI
) ?
87 NCI_CTRL_HDR_SIZE
: S3FWRN5_FW_HDR_SIZE
;
88 ret
= i2c_master_recv(phy
->i2c_dev
, hdr
, hdr_size
);
95 data_len
= (phy
->common
.mode
== S3FWRN5_MODE_NCI
) ?
96 ((struct nci_ctrl_hdr
*)hdr
)->plen
:
97 ((struct s3fwrn5_fw_header
*)hdr
)->len
;
99 skb
= alloc_skb(hdr_size
+ data_len
, GFP_KERNEL
);
103 skb_put_data(skb
, hdr
, hdr_size
);
108 ret
= i2c_master_recv(phy
->i2c_dev
, skb_put(skb
, data_len
), data_len
);
109 if (ret
!= data_len
) {
115 return s3fwrn5_recv_frame(phy
->common
.ndev
, skb
, phy
->common
.mode
);
118 static irqreturn_t
s3fwrn5_i2c_irq_thread_fn(int irq
, void *phy_id
)
120 struct s3fwrn5_i2c_phy
*phy
= phy_id
;
122 if (!phy
|| !phy
->common
.ndev
) {
127 mutex_lock(&phy
->common
.mutex
);
132 switch (phy
->common
.mode
) {
133 case S3FWRN5_MODE_NCI
:
134 case S3FWRN5_MODE_FW
:
135 s3fwrn5_i2c_read(phy
);
137 case S3FWRN5_MODE_COLD
:
142 mutex_unlock(&phy
->common
.mutex
);
147 static int s3fwrn5_i2c_parse_dt(struct i2c_client
*client
)
149 struct s3fwrn5_i2c_phy
*phy
= i2c_get_clientdata(client
);
150 struct device_node
*np
= client
->dev
.of_node
;
155 phy
->common
.gpio_en
= of_get_named_gpio(np
, "en-gpios", 0);
156 if (!gpio_is_valid(phy
->common
.gpio_en
)) {
157 /* Support also deprecated property */
158 phy
->common
.gpio_en
= of_get_named_gpio(np
,
161 if (!gpio_is_valid(phy
->common
.gpio_en
))
165 phy
->common
.gpio_fw_wake
= of_get_named_gpio(np
, "wake-gpios", 0);
166 if (!gpio_is_valid(phy
->common
.gpio_fw_wake
)) {
167 /* Support also deprecated property */
168 phy
->common
.gpio_fw_wake
= of_get_named_gpio(np
,
171 if (!gpio_is_valid(phy
->common
.gpio_fw_wake
))
178 static int s3fwrn5_i2c_probe(struct i2c_client
*client
,
179 const struct i2c_device_id
*id
)
181 struct s3fwrn5_i2c_phy
*phy
;
184 phy
= devm_kzalloc(&client
->dev
, sizeof(*phy
), GFP_KERNEL
);
188 mutex_init(&phy
->common
.mutex
);
189 phy
->common
.mode
= S3FWRN5_MODE_COLD
;
190 phy
->irq_skip
= true;
192 phy
->i2c_dev
= client
;
193 i2c_set_clientdata(client
, phy
);
195 ret
= s3fwrn5_i2c_parse_dt(client
);
199 ret
= devm_gpio_request_one(&phy
->i2c_dev
->dev
, phy
->common
.gpio_en
,
200 GPIOF_OUT_INIT_HIGH
, "s3fwrn5_en");
204 ret
= devm_gpio_request_one(&phy
->i2c_dev
->dev
,
205 phy
->common
.gpio_fw_wake
,
206 GPIOF_OUT_INIT_LOW
, "s3fwrn5_fw_wake");
210 ret
= s3fwrn5_probe(&phy
->common
.ndev
, phy
, &phy
->i2c_dev
->dev
,
215 ret
= devm_request_threaded_irq(&client
->dev
, phy
->i2c_dev
->irq
, NULL
,
216 s3fwrn5_i2c_irq_thread_fn
, IRQF_ONESHOT
,
217 S3FWRN5_I2C_DRIVER_NAME
, phy
);
219 s3fwrn5_remove(phy
->common
.ndev
);
224 static int s3fwrn5_i2c_remove(struct i2c_client
*client
)
226 struct s3fwrn5_i2c_phy
*phy
= i2c_get_clientdata(client
);
228 s3fwrn5_remove(phy
->common
.ndev
);
233 static const struct i2c_device_id s3fwrn5_i2c_id_table
[] = {
234 {S3FWRN5_I2C_DRIVER_NAME
, 0},
237 MODULE_DEVICE_TABLE(i2c
, s3fwrn5_i2c_id_table
);
239 static const struct of_device_id of_s3fwrn5_i2c_match
[] = {
240 { .compatible
= "samsung,s3fwrn5-i2c", },
243 MODULE_DEVICE_TABLE(of
, of_s3fwrn5_i2c_match
);
245 static struct i2c_driver s3fwrn5_i2c_driver
= {
247 .name
= S3FWRN5_I2C_DRIVER_NAME
,
248 .of_match_table
= of_match_ptr(of_s3fwrn5_i2c_match
),
250 .probe
= s3fwrn5_i2c_probe
,
251 .remove
= s3fwrn5_i2c_remove
,
252 .id_table
= s3fwrn5_i2c_id_table
,
255 module_i2c_driver(s3fwrn5_i2c_driver
);
257 MODULE_LICENSE("GPL");
258 MODULE_DESCRIPTION("I2C driver for Samsung S3FWRN5");
259 MODULE_AUTHOR("Robert Baldyga <r.baldyga@samsung.com>");