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>
20 #define S3FWRN5_I2C_DRIVER_NAME "s3fwrn5_i2c"
22 #define S3FWRN5_I2C_MAX_PAYLOAD 32
23 #define S3FWRN5_EN_WAIT_TIME 150
25 struct s3fwrn5_i2c_phy
{
26 struct i2c_client
*i2c_dev
;
30 unsigned int gpio_fw_wake
;
34 enum s3fwrn5_mode mode
;
35 unsigned int irq_skip
:1;
38 static void s3fwrn5_i2c_set_wake(void *phy_id
, bool wake
)
40 struct s3fwrn5_i2c_phy
*phy
= phy_id
;
42 mutex_lock(&phy
->mutex
);
43 gpio_set_value(phy
->gpio_fw_wake
, wake
);
44 msleep(S3FWRN5_EN_WAIT_TIME
/2);
45 mutex_unlock(&phy
->mutex
);
48 static void s3fwrn5_i2c_set_mode(void *phy_id
, enum s3fwrn5_mode mode
)
50 struct s3fwrn5_i2c_phy
*phy
= phy_id
;
52 mutex_lock(&phy
->mutex
);
54 if (phy
->mode
== mode
)
59 gpio_set_value(phy
->gpio_en
, 1);
60 gpio_set_value(phy
->gpio_fw_wake
, 0);
61 if (mode
== S3FWRN5_MODE_FW
)
62 gpio_set_value(phy
->gpio_fw_wake
, 1);
64 if (mode
!= S3FWRN5_MODE_COLD
) {
65 msleep(S3FWRN5_EN_WAIT_TIME
);
66 gpio_set_value(phy
->gpio_en
, 0);
67 msleep(S3FWRN5_EN_WAIT_TIME
/2);
73 mutex_unlock(&phy
->mutex
);
76 static enum s3fwrn5_mode
s3fwrn5_i2c_get_mode(void *phy_id
)
78 struct s3fwrn5_i2c_phy
*phy
= phy_id
;
79 enum s3fwrn5_mode mode
;
81 mutex_lock(&phy
->mutex
);
85 mutex_unlock(&phy
->mutex
);
90 static int s3fwrn5_i2c_write(void *phy_id
, struct sk_buff
*skb
)
92 struct s3fwrn5_i2c_phy
*phy
= phy_id
;
95 mutex_lock(&phy
->mutex
);
97 phy
->irq_skip
= false;
99 ret
= i2c_master_send(phy
->i2c_dev
, skb
->data
, skb
->len
);
100 if (ret
== -EREMOTEIO
) {
101 /* Retry, chip was in standby */
102 usleep_range(110000, 120000);
103 ret
= i2c_master_send(phy
->i2c_dev
, skb
->data
, skb
->len
);
106 mutex_unlock(&phy
->mutex
);
117 static const struct s3fwrn5_phy_ops i2c_phy_ops
= {
118 .set_wake
= s3fwrn5_i2c_set_wake
,
119 .set_mode
= s3fwrn5_i2c_set_mode
,
120 .get_mode
= s3fwrn5_i2c_get_mode
,
121 .write
= s3fwrn5_i2c_write
,
124 static int s3fwrn5_i2c_read(struct s3fwrn5_i2c_phy
*phy
)
132 hdr_size
= (phy
->mode
== S3FWRN5_MODE_NCI
) ?
133 NCI_CTRL_HDR_SIZE
: S3FWRN5_FW_HDR_SIZE
;
134 ret
= i2c_master_recv(phy
->i2c_dev
, hdr
, hdr_size
);
141 data_len
= (phy
->mode
== S3FWRN5_MODE_NCI
) ?
142 ((struct nci_ctrl_hdr
*)hdr
)->plen
:
143 ((struct s3fwrn5_fw_header
*)hdr
)->len
;
145 skb
= alloc_skb(hdr_size
+ data_len
, GFP_KERNEL
);
149 skb_put_data(skb
, hdr
, hdr_size
);
154 ret
= i2c_master_recv(phy
->i2c_dev
, skb_put(skb
, data_len
), data_len
);
155 if (ret
!= data_len
) {
161 return s3fwrn5_recv_frame(phy
->ndev
, skb
, phy
->mode
);
164 static irqreturn_t
s3fwrn5_i2c_irq_thread_fn(int irq
, void *phy_id
)
166 struct s3fwrn5_i2c_phy
*phy
= phy_id
;
169 if (!phy
|| !phy
->ndev
) {
174 mutex_lock(&phy
->mutex
);
180 case S3FWRN5_MODE_NCI
:
181 case S3FWRN5_MODE_FW
:
182 ret
= s3fwrn5_i2c_read(phy
);
184 case S3FWRN5_MODE_COLD
:
190 mutex_unlock(&phy
->mutex
);
195 static int s3fwrn5_i2c_parse_dt(struct i2c_client
*client
)
197 struct s3fwrn5_i2c_phy
*phy
= i2c_get_clientdata(client
);
198 struct device_node
*np
= client
->dev
.of_node
;
203 phy
->gpio_en
= of_get_named_gpio(np
, "s3fwrn5,en-gpios", 0);
204 if (!gpio_is_valid(phy
->gpio_en
))
207 phy
->gpio_fw_wake
= of_get_named_gpio(np
, "s3fwrn5,fw-gpios", 0);
208 if (!gpio_is_valid(phy
->gpio_fw_wake
))
214 static int s3fwrn5_i2c_probe(struct i2c_client
*client
,
215 const struct i2c_device_id
*id
)
217 struct s3fwrn5_i2c_phy
*phy
;
220 phy
= devm_kzalloc(&client
->dev
, sizeof(*phy
), GFP_KERNEL
);
224 mutex_init(&phy
->mutex
);
225 phy
->mode
= S3FWRN5_MODE_COLD
;
226 phy
->irq_skip
= true;
228 phy
->i2c_dev
= client
;
229 i2c_set_clientdata(client
, phy
);
231 ret
= s3fwrn5_i2c_parse_dt(client
);
235 ret
= devm_gpio_request_one(&phy
->i2c_dev
->dev
, phy
->gpio_en
,
236 GPIOF_OUT_INIT_HIGH
, "s3fwrn5_en");
240 ret
= devm_gpio_request_one(&phy
->i2c_dev
->dev
, phy
->gpio_fw_wake
,
241 GPIOF_OUT_INIT_LOW
, "s3fwrn5_fw_wake");
245 ret
= s3fwrn5_probe(&phy
->ndev
, phy
, &phy
->i2c_dev
->dev
, &i2c_phy_ops
,
246 S3FWRN5_I2C_MAX_PAYLOAD
);
250 ret
= devm_request_threaded_irq(&client
->dev
, phy
->i2c_dev
->irq
, NULL
,
251 s3fwrn5_i2c_irq_thread_fn
, IRQF_TRIGGER_HIGH
| IRQF_ONESHOT
,
252 S3FWRN5_I2C_DRIVER_NAME
, phy
);
254 s3fwrn5_remove(phy
->ndev
);
259 static int s3fwrn5_i2c_remove(struct i2c_client
*client
)
261 struct s3fwrn5_i2c_phy
*phy
= i2c_get_clientdata(client
);
263 s3fwrn5_remove(phy
->ndev
);
268 static const struct i2c_device_id s3fwrn5_i2c_id_table
[] = {
269 {S3FWRN5_I2C_DRIVER_NAME
, 0},
272 MODULE_DEVICE_TABLE(i2c
, s3fwrn5_i2c_id_table
);
274 static const struct of_device_id of_s3fwrn5_i2c_match
[] = {
275 { .compatible
= "samsung,s3fwrn5-i2c", },
278 MODULE_DEVICE_TABLE(of
, of_s3fwrn5_i2c_match
);
280 static struct i2c_driver s3fwrn5_i2c_driver
= {
282 .name
= S3FWRN5_I2C_DRIVER_NAME
,
283 .of_match_table
= of_match_ptr(of_s3fwrn5_i2c_match
),
285 .probe
= s3fwrn5_i2c_probe
,
286 .remove
= s3fwrn5_i2c_remove
,
287 .id_table
= s3fwrn5_i2c_id_table
,
290 module_i2c_driver(s3fwrn5_i2c_driver
);
292 MODULE_LICENSE("GPL");
293 MODULE_DESCRIPTION("I2C driver for Samsung S3FWRN5");
294 MODULE_AUTHOR("Robert Baldyga <r.baldyga@samsung.com>");