diff options
author | Clément Perrochaud <clement.perrochaud@nxp.com> | 2015-03-09 06:12:05 -0400 |
---|---|---|
committer | Samuel Ortiz <sameo@linux.intel.com> | 2015-03-26 06:21:41 -0400 |
commit | 6be88670fc59d50426f90f734a36b90e1de7d148 (patch) | |
tree | 8296eaff5b2747937b4239fcee58f5fa380926dc | |
parent | dece45855a8b0d1dcf48eb01d0822070ded6a4c8 (diff) |
NFC: nxp-nci_i2c: Add I2C support to NXP NCI driver
Add a module to the NXP-NCI driver to support NFC controllers with an
I2C control interface, such as the NPC100.
Signed-off-by: Clément Perrochaud <clement.perrochaud@effinnov.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
-rw-r--r-- | Documentation/devicetree/bindings/net/nfc/nxp-nci.txt | 35 | ||||
-rw-r--r-- | drivers/nfc/nxp-nci/Kconfig | 12 | ||||
-rw-r--r-- | drivers/nfc/nxp-nci/Makefile | 2 | ||||
-rw-r--r-- | drivers/nfc/nxp-nci/i2c.c | 415 |
4 files changed, 464 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/net/nfc/nxp-nci.txt b/Documentation/devicetree/bindings/net/nfc/nxp-nci.txt new file mode 100644 index 000000000000..5b6cd9b3f628 --- /dev/null +++ b/Documentation/devicetree/bindings/net/nfc/nxp-nci.txt | |||
@@ -0,0 +1,35 @@ | |||
1 | * NXP Semiconductors NXP NCI NFC Controllers | ||
2 | |||
3 | Required properties: | ||
4 | - compatible: Should be "nxp,nxp-nci-i2c". | ||
5 | - clock-frequency: I²C work frequency. | ||
6 | - reg: address on the bus | ||
7 | - interrupt-parent: phandle for the interrupt gpio controller | ||
8 | - interrupts: GPIO interrupt to which the chip is connected | ||
9 | - enable-gpios: Output GPIO pin used for enabling/disabling the chip | ||
10 | - firmware-gpios: Output GPIO pin used to enter firmware download mode | ||
11 | |||
12 | Optional SoC Specific Properties: | ||
13 | - pinctrl-names: Contains only one value - "default". | ||
14 | - pintctrl-0: Specifies the pin control groups used for this controller. | ||
15 | |||
16 | Example (for ARM-based BeagleBone with NPC100 NFC controller on I2C2): | ||
17 | |||
18 | &i2c2 { | ||
19 | |||
20 | status = "okay"; | ||
21 | |||
22 | npc100: npc100@29 { | ||
23 | |||
24 | compatible = "nxp,nxp-nci-i2c"; | ||
25 | |||
26 | reg = <0x29>; | ||
27 | clock-frequency = <100000>; | ||
28 | |||
29 | interrupt-parent = <&gpio1>; | ||
30 | interrupts = <29 GPIO_ACTIVE_HIGH>; | ||
31 | |||
32 | enable-gpios = <&gpio0 30 GPIO_ACTIVE_HIGH>; | ||
33 | firmware-gpios = <&gpio0 31 GPIO_ACTIVE_HIGH>; | ||
34 | }; | ||
35 | }; | ||
diff --git a/drivers/nfc/nxp-nci/Kconfig b/drivers/nfc/nxp-nci/Kconfig index 5f60c7cf02e8..37b40612520d 100644 --- a/drivers/nfc/nxp-nci/Kconfig +++ b/drivers/nfc/nxp-nci/Kconfig | |||
@@ -11,3 +11,15 @@ config NFC_NXP_NCI | |||
11 | To compile this driver as a module, choose m here. The module will | 11 | To compile this driver as a module, choose m here. The module will |
12 | be called nxp_nci. | 12 | be called nxp_nci. |
13 | Say N if unsure. | 13 | Say N if unsure. |
14 | |||
15 | config NFC_NXP_NCI_I2C | ||
16 | tristate "NXP-NCI I2C support" | ||
17 | depends on NFC_NXP_NCI && I2C | ||
18 | ---help--- | ||
19 | This module adds support for an I2C interface to the NXP NCI | ||
20 | chips. | ||
21 | Select this if your platform is using the I2C bus. | ||
22 | |||
23 | To compile this driver as a module, choose m here. The module will | ||
24 | be called nxp_nci_i2c. | ||
25 | Say Y if unsure. | ||
diff --git a/drivers/nfc/nxp-nci/Makefile b/drivers/nfc/nxp-nci/Makefile index 8f1e32826961..c008be30bb18 100644 --- a/drivers/nfc/nxp-nci/Makefile +++ b/drivers/nfc/nxp-nci/Makefile | |||
@@ -3,7 +3,9 @@ | |||
3 | # | 3 | # |
4 | 4 | ||
5 | nxp-nci-objs = core.o firmware.o | 5 | nxp-nci-objs = core.o firmware.o |
6 | nxp-nci_i2c-objs = i2c.o | ||
6 | 7 | ||
7 | obj-$(CONFIG_NFC_NXP_NCI) += nxp-nci.o | 8 | obj-$(CONFIG_NFC_NXP_NCI) += nxp-nci.o |
9 | obj-$(CONFIG_NFC_NXP_NCI_I2C) += nxp-nci_i2c.o | ||
8 | 10 | ||
9 | ccflags-$(CONFIG_NFC_DEBUG) := -DDEBUG | 11 | ccflags-$(CONFIG_NFC_DEBUG) := -DDEBUG |
diff --git a/drivers/nfc/nxp-nci/i2c.c b/drivers/nfc/nxp-nci/i2c.c new file mode 100644 index 000000000000..17bd67dbebf0 --- /dev/null +++ b/drivers/nfc/nxp-nci/i2c.c | |||
@@ -0,0 +1,415 @@ | |||
1 | /* | ||
2 | * I2C link layer for the NXP NCI driver | ||
3 | * | ||
4 | * Copyright (C) 2014 NXP Semiconductors All rights reserved. | ||
5 | * | ||
6 | * Authors: Clément Perrochaud <clement.perrochaud@nxp.com> | ||
7 | * | ||
8 | * Derived from PN544 device driver: | ||
9 | * Copyright (C) 2012 Intel Corporation. All rights reserved. | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify it | ||
12 | * under the terms and conditions of the GNU General Public License, | ||
13 | * version 2, as published by the Free Software Foundation. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, | ||
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
18 | * GNU General Public License for more details. | ||
19 | * | ||
20 | * You should have received a copy of the GNU General Public License | ||
21 | * along with this program; if not, see <http://www.gnu.org/licenses/>. | ||
22 | */ | ||
23 | |||
24 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
25 | |||
26 | #include <linux/delay.h> | ||
27 | #include <linux/i2c.h> | ||
28 | #include <linux/interrupt.h> | ||
29 | #include <linux/miscdevice.h> | ||
30 | #include <linux/module.h> | ||
31 | #include <linux/nfc.h> | ||
32 | #include <linux/of_gpio.h> | ||
33 | #include <linux/of_irq.h> | ||
34 | #include <linux/platform_data/nxp-nci.h> | ||
35 | #include <linux/unaligned/access_ok.h> | ||
36 | |||
37 | #include <net/nfc/nfc.h> | ||
38 | |||
39 | #include "nxp-nci.h" | ||
40 | |||
41 | #define NXP_NCI_I2C_DRIVER_NAME "nxp-nci_i2c" | ||
42 | |||
43 | #define NXP_NCI_I2C_MAX_PAYLOAD 32 | ||
44 | |||
45 | struct nxp_nci_i2c_phy { | ||
46 | struct i2c_client *i2c_dev; | ||
47 | struct nci_dev *ndev; | ||
48 | |||
49 | unsigned int gpio_en; | ||
50 | unsigned int gpio_fw; | ||
51 | |||
52 | int hard_fault; /* | ||
53 | * < 0 if hardware error occurred (e.g. i2c err) | ||
54 | * and prevents normal operation. | ||
55 | */ | ||
56 | }; | ||
57 | |||
58 | static int nxp_nci_i2c_set_mode(void *phy_id, | ||
59 | enum nxp_nci_mode mode) | ||
60 | { | ||
61 | struct nxp_nci_i2c_phy *phy = (struct nxp_nci_i2c_phy *) phy_id; | ||
62 | |||
63 | gpio_set_value(phy->gpio_fw, (mode == NXP_NCI_MODE_FW) ? 1 : 0); | ||
64 | gpio_set_value(phy->gpio_en, (mode != NXP_NCI_MODE_COLD) ? 1 : 0); | ||
65 | usleep_range(10000, 15000); | ||
66 | |||
67 | if (mode == NXP_NCI_MODE_COLD) | ||
68 | phy->hard_fault = 0; | ||
69 | |||
70 | return 0; | ||
71 | } | ||
72 | |||
73 | static int nxp_nci_i2c_write(void *phy_id, struct sk_buff *skb) | ||
74 | { | ||
75 | int r; | ||
76 | struct nxp_nci_i2c_phy *phy = phy_id; | ||
77 | struct i2c_client *client = phy->i2c_dev; | ||
78 | |||
79 | if (phy->hard_fault != 0) | ||
80 | return phy->hard_fault; | ||
81 | |||
82 | r = i2c_master_send(client, skb->data, skb->len); | ||
83 | if (r == -EREMOTEIO) { | ||
84 | /* Retry, chip was in standby */ | ||
85 | usleep_range(110000, 120000); | ||
86 | r = i2c_master_send(client, skb->data, skb->len); | ||
87 | } | ||
88 | |||
89 | if (r < 0) { | ||
90 | nfc_err(&client->dev, "Error %d on I2C send\n", r); | ||
91 | } else if (r != skb->len) { | ||
92 | nfc_err(&client->dev, | ||
93 | "Invalid length sent: %u (expected %u)\n", | ||
94 | r, skb->len); | ||
95 | r = -EREMOTEIO; | ||
96 | } else { | ||
97 | /* Success but return 0 and not number of bytes */ | ||
98 | r = 0; | ||
99 | } | ||
100 | |||
101 | return r; | ||
102 | } | ||
103 | |||
104 | static struct nxp_nci_phy_ops i2c_phy_ops = { | ||
105 | .set_mode = nxp_nci_i2c_set_mode, | ||
106 | .write = nxp_nci_i2c_write, | ||
107 | }; | ||
108 | |||
109 | static int nxp_nci_i2c_fw_read(struct nxp_nci_i2c_phy *phy, | ||
110 | struct sk_buff **skb) | ||
111 | { | ||
112 | struct i2c_client *client = phy->i2c_dev; | ||
113 | u16 header; | ||
114 | size_t frame_len; | ||
115 | int r; | ||
116 | |||
117 | r = i2c_master_recv(client, (u8 *) &header, NXP_NCI_FW_HDR_LEN); | ||
118 | if (r < 0) { | ||
119 | goto fw_read_exit; | ||
120 | } else if (r != NXP_NCI_FW_HDR_LEN) { | ||
121 | nfc_err(&client->dev, "Incorrect header length: %u\n", r); | ||
122 | r = -EBADMSG; | ||
123 | goto fw_read_exit; | ||
124 | } | ||
125 | |||
126 | frame_len = (get_unaligned_be16(&header) & NXP_NCI_FW_FRAME_LEN_MASK) + | ||
127 | NXP_NCI_FW_CRC_LEN; | ||
128 | |||
129 | *skb = alloc_skb(NXP_NCI_FW_HDR_LEN + frame_len, GFP_KERNEL); | ||
130 | if (*skb == NULL) { | ||
131 | r = -ENOMEM; | ||
132 | goto fw_read_exit; | ||
133 | } | ||
134 | |||
135 | memcpy(skb_put(*skb, NXP_NCI_FW_HDR_LEN), &header, NXP_NCI_FW_HDR_LEN); | ||
136 | |||
137 | r = i2c_master_recv(client, skb_put(*skb, frame_len), frame_len); | ||
138 | if (r != frame_len) { | ||
139 | nfc_err(&client->dev, | ||
140 | "Invalid frame length: %u (expected %zu)\n", | ||
141 | r, frame_len); | ||
142 | r = -EBADMSG; | ||
143 | goto fw_read_exit_free_skb; | ||
144 | } | ||
145 | |||
146 | return 0; | ||
147 | |||
148 | fw_read_exit_free_skb: | ||
149 | kfree_skb(*skb); | ||
150 | fw_read_exit: | ||
151 | return r; | ||
152 | } | ||
153 | |||
154 | static int nxp_nci_i2c_nci_read(struct nxp_nci_i2c_phy *phy, | ||
155 | struct sk_buff **skb) | ||
156 | { | ||
157 | struct nci_ctrl_hdr header; /* May actually be a data header */ | ||
158 | struct i2c_client *client = phy->i2c_dev; | ||
159 | int r; | ||
160 | |||
161 | r = i2c_master_recv(client, (u8 *) &header, NCI_CTRL_HDR_SIZE); | ||
162 | if (r < 0) { | ||
163 | goto nci_read_exit; | ||
164 | } else if (r != NCI_CTRL_HDR_SIZE) { | ||
165 | nfc_err(&client->dev, "Incorrect header length: %u\n", r); | ||
166 | r = -EBADMSG; | ||
167 | goto nci_read_exit; | ||
168 | } | ||
169 | |||
170 | *skb = alloc_skb(NCI_CTRL_HDR_SIZE + header.plen, GFP_KERNEL); | ||
171 | if (*skb == NULL) { | ||
172 | r = -ENOMEM; | ||
173 | goto nci_read_exit; | ||
174 | } | ||
175 | |||
176 | memcpy(skb_put(*skb, NCI_CTRL_HDR_SIZE), (void *) &header, | ||
177 | NCI_CTRL_HDR_SIZE); | ||
178 | |||
179 | r = i2c_master_recv(client, skb_put(*skb, header.plen), header.plen); | ||
180 | if (r != header.plen) { | ||
181 | nfc_err(&client->dev, | ||
182 | "Invalid frame payload length: %u (expected %u)\n", | ||
183 | r, header.plen); | ||
184 | r = -EBADMSG; | ||
185 | goto nci_read_exit_free_skb; | ||
186 | } | ||
187 | |||
188 | return 0; | ||
189 | |||
190 | nci_read_exit_free_skb: | ||
191 | kfree_skb(*skb); | ||
192 | nci_read_exit: | ||
193 | return r; | ||
194 | } | ||
195 | |||
196 | static irqreturn_t nxp_nci_i2c_irq_thread_fn(int irq, void *phy_id) | ||
197 | { | ||
198 | struct nxp_nci_i2c_phy *phy = phy_id; | ||
199 | struct i2c_client *client; | ||
200 | struct nxp_nci_info *info; | ||
201 | |||
202 | struct sk_buff *skb = NULL; | ||
203 | int r = 0; | ||
204 | |||
205 | if (!phy || !phy->ndev) | ||
206 | goto exit_irq_none; | ||
207 | |||
208 | client = phy->i2c_dev; | ||
209 | |||
210 | if (!client || irq != client->irq) | ||
211 | goto exit_irq_none; | ||
212 | |||
213 | info = nci_get_drvdata(phy->ndev); | ||
214 | |||
215 | if (!info) | ||
216 | goto exit_irq_none; | ||
217 | |||
218 | mutex_lock(&info->info_lock); | ||
219 | |||
220 | if (phy->hard_fault != 0) | ||
221 | goto exit_irq_handled; | ||
222 | |||
223 | switch (info->mode) { | ||
224 | case NXP_NCI_MODE_NCI: | ||
225 | r = nxp_nci_i2c_nci_read(phy, &skb); | ||
226 | break; | ||
227 | case NXP_NCI_MODE_FW: | ||
228 | r = nxp_nci_i2c_fw_read(phy, &skb); | ||
229 | break; | ||
230 | case NXP_NCI_MODE_COLD: | ||
231 | r = -EREMOTEIO; | ||
232 | break; | ||
233 | } | ||
234 | |||
235 | if (r == -EREMOTEIO) { | ||
236 | phy->hard_fault = r; | ||
237 | skb = NULL; | ||
238 | } else if (r < 0) { | ||
239 | nfc_err(&client->dev, "Read failed with error %d\n", r); | ||
240 | goto exit_irq_handled; | ||
241 | } | ||
242 | |||
243 | switch (info->mode) { | ||
244 | case NXP_NCI_MODE_NCI: | ||
245 | nci_recv_frame(phy->ndev, skb); | ||
246 | break; | ||
247 | case NXP_NCI_MODE_FW: | ||
248 | nxp_nci_fw_recv_frame(phy->ndev, skb); | ||
249 | break; | ||
250 | case NXP_NCI_MODE_COLD: | ||
251 | break; | ||
252 | } | ||
253 | |||
254 | exit_irq_handled: | ||
255 | mutex_unlock(&info->info_lock); | ||
256 | return IRQ_HANDLED; | ||
257 | exit_irq_none: | ||
258 | WARN_ON_ONCE(1); | ||
259 | return IRQ_NONE; | ||
260 | } | ||
261 | |||
262 | #ifdef CONFIG_OF | ||
263 | |||
264 | static int nxp_nci_i2c_parse_devtree(struct i2c_client *client) | ||
265 | { | ||
266 | struct nxp_nci_i2c_phy *phy = i2c_get_clientdata(client); | ||
267 | struct device_node *pp; | ||
268 | int r; | ||
269 | |||
270 | pp = client->dev.of_node; | ||
271 | if (!pp) | ||
272 | return -ENODEV; | ||
273 | |||
274 | r = of_get_named_gpio(pp, "enable-gpios", 0); | ||
275 | if (r == -EPROBE_DEFER) | ||
276 | r = of_get_named_gpio(pp, "enable-gpios", 0); | ||
277 | if (r < 0) { | ||
278 | nfc_err(&client->dev, "Failed to get EN gpio, error: %d\n", r); | ||
279 | return r; | ||
280 | } | ||
281 | phy->gpio_en = r; | ||
282 | |||
283 | r = of_get_named_gpio(pp, "firmware-gpios", 0); | ||
284 | if (r == -EPROBE_DEFER) | ||
285 | r = of_get_named_gpio(pp, "firmware-gpios", 0); | ||
286 | if (r < 0) { | ||
287 | nfc_err(&client->dev, "Failed to get FW gpio, error: %d\n", r); | ||
288 | return r; | ||
289 | } | ||
290 | phy->gpio_fw = r; | ||
291 | |||
292 | r = irq_of_parse_and_map(pp, 0); | ||
293 | if (r < 0) { | ||
294 | nfc_err(&client->dev, "Unable to get irq, error: %d\n", r); | ||
295 | return r; | ||
296 | } | ||
297 | client->irq = r; | ||
298 | |||
299 | return 0; | ||
300 | } | ||
301 | |||
302 | #else | ||
303 | |||
304 | static int nxp_nci_i2c_parse_devtree(struct i2c_client *client) | ||
305 | { | ||
306 | return -ENODEV; | ||
307 | } | ||
308 | |||
309 | #endif | ||
310 | |||
311 | static int nxp_nci_i2c_probe(struct i2c_client *client, | ||
312 | const struct i2c_device_id *id) | ||
313 | { | ||
314 | struct nxp_nci_i2c_phy *phy; | ||
315 | struct nxp_nci_nfc_platform_data *pdata; | ||
316 | int r; | ||
317 | |||
318 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { | ||
319 | nfc_err(&client->dev, "Need I2C_FUNC_I2C\n"); | ||
320 | r = -ENODEV; | ||
321 | goto probe_exit; | ||
322 | } | ||
323 | |||
324 | phy = devm_kzalloc(&client->dev, sizeof(struct nxp_nci_i2c_phy), | ||
325 | GFP_KERNEL); | ||
326 | if (!phy) { | ||
327 | r = -ENOMEM; | ||
328 | goto probe_exit; | ||
329 | } | ||
330 | |||
331 | phy->i2c_dev = client; | ||
332 | i2c_set_clientdata(client, phy); | ||
333 | |||
334 | pdata = client->dev.platform_data; | ||
335 | |||
336 | if (!pdata && client->dev.of_node) { | ||
337 | r = nxp_nci_i2c_parse_devtree(client); | ||
338 | if (r < 0) { | ||
339 | nfc_err(&client->dev, "Failed to get DT data\n"); | ||
340 | goto probe_exit; | ||
341 | } | ||
342 | } else if (pdata) { | ||
343 | phy->gpio_en = pdata->gpio_en; | ||
344 | phy->gpio_fw = pdata->gpio_fw; | ||
345 | client->irq = pdata->irq; | ||
346 | } else { | ||
347 | nfc_err(&client->dev, "No platform data\n"); | ||
348 | r = -EINVAL; | ||
349 | goto probe_exit; | ||
350 | } | ||
351 | |||
352 | r = devm_gpio_request_one(&phy->i2c_dev->dev, phy->gpio_en, | ||
353 | GPIOF_OUT_INIT_LOW, "nxp_nci_en"); | ||
354 | if (r < 0) | ||
355 | goto probe_exit; | ||
356 | |||
357 | r = devm_gpio_request_one(&phy->i2c_dev->dev, phy->gpio_fw, | ||
358 | GPIOF_OUT_INIT_LOW, "nxp_nci_fw"); | ||
359 | if (r < 0) | ||
360 | goto probe_exit; | ||
361 | |||
362 | r = nxp_nci_probe(phy, &client->dev, &i2c_phy_ops, | ||
363 | NXP_NCI_I2C_MAX_PAYLOAD, &phy->ndev); | ||
364 | if (r < 0) | ||
365 | goto probe_exit; | ||
366 | |||
367 | r = request_threaded_irq(client->irq, NULL, | ||
368 | nxp_nci_i2c_irq_thread_fn, | ||
369 | IRQF_TRIGGER_RISING | IRQF_ONESHOT, | ||
370 | NXP_NCI_I2C_DRIVER_NAME, phy); | ||
371 | if (r < 0) | ||
372 | nfc_err(&client->dev, "Unable to register IRQ handler\n"); | ||
373 | |||
374 | probe_exit: | ||
375 | return r; | ||
376 | } | ||
377 | |||
378 | static int nxp_nci_i2c_remove(struct i2c_client *client) | ||
379 | { | ||
380 | struct nxp_nci_i2c_phy *phy = i2c_get_clientdata(client); | ||
381 | |||
382 | nxp_nci_remove(phy->ndev); | ||
383 | free_irq(client->irq, phy); | ||
384 | |||
385 | return 0; | ||
386 | } | ||
387 | |||
388 | static struct i2c_device_id nxp_nci_i2c_id_table[] = { | ||
389 | {"nxp-nci_i2c", 0}, | ||
390 | {} | ||
391 | }; | ||
392 | MODULE_DEVICE_TABLE(i2c, nxp_nci_i2c_id_table); | ||
393 | |||
394 | static const struct of_device_id of_nxp_nci_i2c_match[] = { | ||
395 | { .compatible = "nxp,nxp-nci-i2c", }, | ||
396 | {}, | ||
397 | }; | ||
398 | MODULE_DEVICE_TABLE(of, of_nxp_nci_i2c_match); | ||
399 | |||
400 | static struct i2c_driver nxp_nci_i2c_driver = { | ||
401 | .driver = { | ||
402 | .name = NXP_NCI_I2C_DRIVER_NAME, | ||
403 | .owner = THIS_MODULE, | ||
404 | .of_match_table = of_match_ptr(of_nxp_nci_i2c_match), | ||
405 | }, | ||
406 | .probe = nxp_nci_i2c_probe, | ||
407 | .id_table = nxp_nci_i2c_id_table, | ||
408 | .remove = nxp_nci_i2c_remove, | ||
409 | }; | ||
410 | |||
411 | module_i2c_driver(nxp_nci_i2c_driver); | ||
412 | |||
413 | MODULE_LICENSE("GPL"); | ||
414 | MODULE_DESCRIPTION("I2C driver for NXP NCI NFC controllers"); | ||
415 | MODULE_AUTHOR("Clément Perrochaud <clement.perrochaud@nxp.com>"); | ||