]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - drivers/nfc/s3fwrn5/i2c.c
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next
[karo-tx-linux.git] / drivers / nfc / s3fwrn5 / i2c.c
1 /*
2  * I2C Link Layer for Samsung S3FWRN5 NCI based Driver
3  *
4  * Copyright (C) 2015 Samsung Electrnoics
5  * Robert Baldyga <r.baldyga@samsung.com>
6  *
7  * This program is free software; you can redistribute it and/or modify it
8  * under the terms and conditions of the GNU General Public License,
9  * version 2 or later, as published by the Free Software Foundation.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <linux/i2c.h>
21 #include <linux/gpio.h>
22 #include <linux/delay.h>
23 #include <linux/of_gpio.h>
24 #include <linux/of_irq.h>
25 #include <linux/module.h>
26
27 #include <net/nfc/nfc.h>
28
29 #include "s3fwrn5.h"
30
31 #define S3FWRN5_I2C_DRIVER_NAME "s3fwrn5_i2c"
32
33 #define S3FWRN5_I2C_MAX_PAYLOAD 32
34 #define S3FWRN5_EN_WAIT_TIME 150
35
36 struct s3fwrn5_i2c_phy {
37         struct i2c_client *i2c_dev;
38         struct nci_dev *ndev;
39
40         unsigned int gpio_en;
41         unsigned int gpio_fw_wake;
42
43         struct mutex mutex;
44
45         enum s3fwrn5_mode mode;
46         unsigned int irq_skip:1;
47 };
48
49 static void s3fwrn5_i2c_set_wake(void *phy_id, bool wake)
50 {
51         struct s3fwrn5_i2c_phy *phy = phy_id;
52
53         mutex_lock(&phy->mutex);
54         gpio_set_value(phy->gpio_fw_wake, wake);
55         msleep(S3FWRN5_EN_WAIT_TIME/2);
56         mutex_unlock(&phy->mutex);
57 }
58
59 static void s3fwrn5_i2c_set_mode(void *phy_id, enum s3fwrn5_mode mode)
60 {
61         struct s3fwrn5_i2c_phy *phy = phy_id;
62
63         mutex_lock(&phy->mutex);
64
65         if (phy->mode == mode)
66                 goto out;
67
68         phy->mode = mode;
69
70         gpio_set_value(phy->gpio_en, 1);
71         gpio_set_value(phy->gpio_fw_wake, 0);
72         if (mode == S3FWRN5_MODE_FW)
73                 gpio_set_value(phy->gpio_fw_wake, 1);
74
75         if (mode != S3FWRN5_MODE_COLD) {
76                 msleep(S3FWRN5_EN_WAIT_TIME);
77                 gpio_set_value(phy->gpio_en, 0);
78                 msleep(S3FWRN5_EN_WAIT_TIME/2);
79         }
80
81         phy->irq_skip = true;
82
83 out:
84         mutex_unlock(&phy->mutex);
85 }
86
87 static enum s3fwrn5_mode s3fwrn5_i2c_get_mode(void *phy_id)
88 {
89         struct s3fwrn5_i2c_phy *phy = phy_id;
90         enum s3fwrn5_mode mode;
91
92         mutex_lock(&phy->mutex);
93
94         mode = phy->mode;
95
96         mutex_unlock(&phy->mutex);
97
98         return mode;
99 }
100
101 static int s3fwrn5_i2c_write(void *phy_id, struct sk_buff *skb)
102 {
103         struct s3fwrn5_i2c_phy *phy = phy_id;
104         int ret;
105
106         mutex_lock(&phy->mutex);
107
108         phy->irq_skip = false;
109
110         ret = i2c_master_send(phy->i2c_dev, skb->data, skb->len);
111         if (ret == -EREMOTEIO) {
112                 /* Retry, chip was in standby */
113                 usleep_range(110000, 120000);
114                 ret  = i2c_master_send(phy->i2c_dev, skb->data, skb->len);
115         }
116
117         mutex_unlock(&phy->mutex);
118
119         if (ret < 0)
120                 return ret;
121
122         if (ret != skb->len)
123                 return -EREMOTEIO;
124
125         return 0;
126 }
127
128 static struct s3fwrn5_phy_ops i2c_phy_ops = {
129         .set_wake = s3fwrn5_i2c_set_wake,
130         .set_mode = s3fwrn5_i2c_set_mode,
131         .get_mode = s3fwrn5_i2c_get_mode,
132         .write = s3fwrn5_i2c_write,
133 };
134
135 static int s3fwrn5_i2c_read(struct s3fwrn5_i2c_phy *phy)
136 {
137         struct sk_buff *skb;
138         size_t hdr_size;
139         size_t data_len;
140         char hdr[4];
141         int ret;
142
143         hdr_size = (phy->mode == S3FWRN5_MODE_NCI) ?
144                 NCI_CTRL_HDR_SIZE : S3FWRN5_FW_HDR_SIZE;
145         ret = i2c_master_recv(phy->i2c_dev, hdr, hdr_size);
146         if (ret < 0)
147                 return ret;
148
149         if (ret < hdr_size)
150                 return -EBADMSG;
151
152         data_len = (phy->mode == S3FWRN5_MODE_NCI) ?
153                 ((struct nci_ctrl_hdr *)hdr)->plen :
154                 ((struct s3fwrn5_fw_header *)hdr)->len;
155
156         skb = alloc_skb(hdr_size + data_len, GFP_KERNEL);
157         if (!skb)
158                 return -ENOMEM;
159
160         memcpy(skb_put(skb, hdr_size), hdr, hdr_size);
161
162         if (data_len == 0)
163                 goto out;
164
165         ret = i2c_master_recv(phy->i2c_dev, skb_put(skb, data_len), data_len);
166         if (ret != data_len) {
167                 kfree_skb(skb);
168                 return -EBADMSG;
169         }
170
171 out:
172         return s3fwrn5_recv_frame(phy->ndev, skb, phy->mode);
173 }
174
175 static irqreturn_t s3fwrn5_i2c_irq_thread_fn(int irq, void *phy_id)
176 {
177         struct s3fwrn5_i2c_phy *phy = phy_id;
178         int ret = 0;
179
180         if (!phy || !phy->ndev) {
181                 WARN_ON_ONCE(1);
182                 return IRQ_NONE;
183         }
184
185         mutex_lock(&phy->mutex);
186
187         if (phy->irq_skip)
188                 goto out;
189
190         switch (phy->mode) {
191         case S3FWRN5_MODE_NCI:
192         case S3FWRN5_MODE_FW:
193                 ret = s3fwrn5_i2c_read(phy);
194                 break;
195         case S3FWRN5_MODE_COLD:
196                 ret = -EREMOTEIO;
197                 break;
198         }
199
200 out:
201         mutex_unlock(&phy->mutex);
202
203         return IRQ_HANDLED;
204 }
205
206 static int s3fwrn5_i2c_parse_dt(struct i2c_client *client)
207 {
208         struct s3fwrn5_i2c_phy *phy = i2c_get_clientdata(client);
209         struct device_node *np = client->dev.of_node;
210
211         if (!np)
212                 return -ENODEV;
213
214         phy->gpio_en = of_get_named_gpio(np, "s3fwrn5,en-gpios", 0);
215         if (!gpio_is_valid(phy->gpio_en))
216                 return -ENODEV;
217
218         phy->gpio_fw_wake = of_get_named_gpio(np, "s3fwrn5,fw-gpios", 0);
219         if (!gpio_is_valid(phy->gpio_fw_wake))
220                 return -ENODEV;
221
222         return 0;
223 }
224
225 static int s3fwrn5_i2c_probe(struct i2c_client *client,
226                                   const struct i2c_device_id *id)
227 {
228         struct s3fwrn5_i2c_phy *phy;
229         int ret;
230
231         phy = devm_kzalloc(&client->dev, sizeof(*phy), GFP_KERNEL);
232         if (!phy)
233                 return -ENOMEM;
234
235         mutex_init(&phy->mutex);
236         phy->mode = S3FWRN5_MODE_COLD;
237         phy->irq_skip = true;
238
239         phy->i2c_dev = client;
240         i2c_set_clientdata(client, phy);
241
242         ret = s3fwrn5_i2c_parse_dt(client);
243         if (ret < 0)
244                 return ret;
245
246         ret = devm_gpio_request_one(&phy->i2c_dev->dev, phy->gpio_en,
247                 GPIOF_OUT_INIT_HIGH, "s3fwrn5_en");
248         if (ret < 0)
249                 return ret;
250
251         ret = devm_gpio_request_one(&phy->i2c_dev->dev, phy->gpio_fw_wake,
252                 GPIOF_OUT_INIT_LOW, "s3fwrn5_fw_wake");
253         if (ret < 0)
254                 return ret;
255
256         ret = s3fwrn5_probe(&phy->ndev, phy, &phy->i2c_dev->dev, &i2c_phy_ops,
257                 S3FWRN5_I2C_MAX_PAYLOAD);
258         if (ret < 0)
259                 return ret;
260
261         ret = devm_request_threaded_irq(&client->dev, phy->i2c_dev->irq, NULL,
262                 s3fwrn5_i2c_irq_thread_fn, IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
263                 S3FWRN5_I2C_DRIVER_NAME, phy);
264         if (ret)
265                 s3fwrn5_remove(phy->ndev);
266
267         return ret;
268 }
269
270 static int s3fwrn5_i2c_remove(struct i2c_client *client)
271 {
272         struct s3fwrn5_i2c_phy *phy = i2c_get_clientdata(client);
273
274         s3fwrn5_remove(phy->ndev);
275
276         return 0;
277 }
278
279 static struct i2c_device_id s3fwrn5_i2c_id_table[] = {
280         {S3FWRN5_I2C_DRIVER_NAME, 0},
281         {}
282 };
283 MODULE_DEVICE_TABLE(i2c, s3fwrn5_i2c_id_table);
284
285 static const struct of_device_id of_s3fwrn5_i2c_match[] = {
286         { .compatible = "samsung,s3fwrn5-i2c", },
287         {}
288 };
289 MODULE_DEVICE_TABLE(of, of_s3fwrn5_i2c_match);
290
291 static struct i2c_driver s3fwrn5_i2c_driver = {
292         .driver = {
293                 .owner = THIS_MODULE,
294                 .name = S3FWRN5_I2C_DRIVER_NAME,
295                 .of_match_table = of_match_ptr(of_s3fwrn5_i2c_match),
296         },
297         .probe = s3fwrn5_i2c_probe,
298         .remove = s3fwrn5_i2c_remove,
299         .id_table = s3fwrn5_i2c_id_table,
300 };
301
302 module_i2c_driver(s3fwrn5_i2c_driver);
303
304 MODULE_LICENSE("GPL");
305 MODULE_DESCRIPTION("I2C driver for Samsung S3FWRN5");
306 MODULE_AUTHOR("Robert Baldyga <r.baldyga@samsung.com>");