diff options
-rw-r--r-- | drivers/nfc/st-nci/Kconfig | 11 | ||||
-rw-r--r-- | drivers/nfc/st-nci/Makefile | 3 | ||||
-rw-r--r-- | drivers/nfc/st-nci/spi.c | 392 |
3 files changed, 406 insertions, 0 deletions
diff --git a/drivers/nfc/st-nci/Kconfig b/drivers/nfc/st-nci/Kconfig index fc3904c946ee..e7c6db9c5860 100644 --- a/drivers/nfc/st-nci/Kconfig +++ b/drivers/nfc/st-nci/Kconfig | |||
@@ -21,3 +21,14 @@ config NFC_ST_NCI_I2C | |||
21 | 21 | ||
22 | If you choose to build a module, it'll be called st-nci_i2c. | 22 | If you choose to build a module, it'll be called st-nci_i2c. |
23 | Say N if unsure. | 23 | Say N if unsure. |
24 | |||
25 | config NFC_ST_NCI_SPI | ||
26 | tristate "NFC ST NCI spi support" | ||
27 | depends on NFC_ST_NCI && SPI | ||
28 | ---help--- | ||
29 | This module adds support for an SPI interface to the | ||
30 | STMicroelectronics NFC NCI chips familly. | ||
31 | Select this if your platform is using the spi bus. | ||
32 | |||
33 | If you choose to build a module, it'll be called st-nci_spi. | ||
34 | Say N if unsure. | ||
diff --git a/drivers/nfc/st-nci/Makefile b/drivers/nfc/st-nci/Makefile index 0df157df3a94..348ce76f2177 100644 --- a/drivers/nfc/st-nci/Makefile +++ b/drivers/nfc/st-nci/Makefile | |||
@@ -7,3 +7,6 @@ obj-$(CONFIG_NFC_ST_NCI) += st-nci.o | |||
7 | 7 | ||
8 | st-nci_i2c-objs = i2c.o | 8 | st-nci_i2c-objs = i2c.o |
9 | obj-$(CONFIG_NFC_ST_NCI_I2C) += st-nci_i2c.o | 9 | obj-$(CONFIG_NFC_ST_NCI_I2C) += st-nci_i2c.o |
10 | |||
11 | st-nci_spi-objs = spi.o | ||
12 | obj-$(CONFIG_NFC_ST_NCI_SPI) += st-nci_spi.o | ||
diff --git a/drivers/nfc/st-nci/spi.c b/drivers/nfc/st-nci/spi.c new file mode 100644 index 000000000000..598a58c4d6d1 --- /dev/null +++ b/drivers/nfc/st-nci/spi.c | |||
@@ -0,0 +1,392 @@ | |||
1 | /* | ||
2 | * SPI Link Layer for ST NCI based Driver | ||
3 | * Copyright (C) 2014-2015 STMicroelectronics SAS. All rights reserved. | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms and conditions of the GNU General Public License, | ||
7 | * version 2, as published by the Free Software Foundation. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License | ||
15 | * along with this program; if not, see <http://www.gnu.org/licenses/>. | ||
16 | */ | ||
17 | |||
18 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
19 | |||
20 | #include <linux/module.h> | ||
21 | #include <linux/spi/spi.h> | ||
22 | #include <linux/gpio.h> | ||
23 | #include <linux/of_irq.h> | ||
24 | #include <linux/of_gpio.h> | ||
25 | #include <linux/interrupt.h> | ||
26 | #include <linux/delay.h> | ||
27 | #include <linux/nfc.h> | ||
28 | #include <linux/platform_data/st-nci.h> | ||
29 | |||
30 | #include "ndlc.h" | ||
31 | |||
32 | #define DRIVER_DESC "NCI NFC driver for ST_NCI" | ||
33 | |||
34 | /* ndlc header */ | ||
35 | #define ST_NCI_FRAME_HEADROOM 1 | ||
36 | #define ST_NCI_FRAME_TAILROOM 0 | ||
37 | |||
38 | #define ST_NCI_SPI_MIN_SIZE 4 /* PCB(1) + NCI Packet header(3) */ | ||
39 | #define ST_NCI_SPI_MAX_SIZE 250 /* req 4.2.1 */ | ||
40 | |||
41 | #define ST_NCI_SPI_DRIVER_NAME "st_nci_spi" | ||
42 | |||
43 | static struct spi_device_id st_nci_spi_id_table[] = { | ||
44 | {ST_NCI_SPI_DRIVER_NAME, 0}, | ||
45 | {} | ||
46 | }; | ||
47 | MODULE_DEVICE_TABLE(spi, st_nci_spi_id_table); | ||
48 | |||
49 | struct st_nci_spi_phy { | ||
50 | struct spi_device *spi_dev; | ||
51 | struct llt_ndlc *ndlc; | ||
52 | |||
53 | unsigned int gpio_reset; | ||
54 | unsigned int irq_polarity; | ||
55 | }; | ||
56 | |||
57 | #define SPI_DUMP_SKB(info, skb) \ | ||
58 | do { \ | ||
59 | pr_debug("%s:\n", info); \ | ||
60 | print_hex_dump(KERN_DEBUG, "spi: ", DUMP_PREFIX_OFFSET, \ | ||
61 | 16, 1, (skb)->data, (skb)->len, 0); \ | ||
62 | } while (0) | ||
63 | |||
64 | static int st_nci_spi_enable(void *phy_id) | ||
65 | { | ||
66 | struct st_nci_spi_phy *phy = phy_id; | ||
67 | |||
68 | gpio_set_value(phy->gpio_reset, 0); | ||
69 | usleep_range(10000, 15000); | ||
70 | gpio_set_value(phy->gpio_reset, 1); | ||
71 | usleep_range(80000, 85000); | ||
72 | |||
73 | if (phy->ndlc->powered == 0) | ||
74 | enable_irq(phy->spi_dev->irq); | ||
75 | |||
76 | return 0; | ||
77 | } | ||
78 | |||
79 | static void st_nci_spi_disable(void *phy_id) | ||
80 | { | ||
81 | struct st_nci_spi_phy *phy = phy_id; | ||
82 | |||
83 | disable_irq_nosync(phy->spi_dev->irq); | ||
84 | } | ||
85 | |||
86 | /* | ||
87 | * Writing a frame must not return the number of written bytes. | ||
88 | * It must return either zero for success, or <0 for error. | ||
89 | * In addition, it must not alter the skb | ||
90 | */ | ||
91 | static int st_nci_spi_write(void *phy_id, struct sk_buff *skb) | ||
92 | { | ||
93 | int r; | ||
94 | struct st_nci_spi_phy *phy = phy_id; | ||
95 | struct spi_device *dev = phy->spi_dev; | ||
96 | struct sk_buff *skb_rx; | ||
97 | u8 buf[ST_NCI_SPI_MAX_SIZE]; | ||
98 | struct spi_transfer spi_xfer = { | ||
99 | .tx_buf = skb->data, | ||
100 | .rx_buf = buf, | ||
101 | .len = skb->len, | ||
102 | }; | ||
103 | |||
104 | SPI_DUMP_SKB("st_nci_spi_write", skb); | ||
105 | |||
106 | if (phy->ndlc->hard_fault != 0) | ||
107 | return phy->ndlc->hard_fault; | ||
108 | |||
109 | r = spi_sync_transfer(dev, &spi_xfer, 1); | ||
110 | /* | ||
111 | * We may have received some valuable data on miso line. | ||
112 | * Send them back in the ndlc state machine. | ||
113 | */ | ||
114 | if (!r) { | ||
115 | skb_rx = alloc_skb(skb->len, GFP_KERNEL); | ||
116 | if (!skb_rx) { | ||
117 | r = -ENOMEM; | ||
118 | goto exit; | ||
119 | } | ||
120 | |||
121 | skb_put(skb_rx, skb->len); | ||
122 | memcpy(skb_rx->data, buf, skb->len); | ||
123 | ndlc_recv(phy->ndlc, skb_rx); | ||
124 | } | ||
125 | |||
126 | exit: | ||
127 | return r; | ||
128 | } | ||
129 | |||
130 | /* | ||
131 | * Reads an ndlc frame and returns it in a newly allocated sk_buff. | ||
132 | * returns: | ||
133 | * 0 : if received frame is complete | ||
134 | * -EREMOTEIO : i2c read error (fatal) | ||
135 | * -EBADMSG : frame was incorrect and discarded | ||
136 | * -ENOMEM : cannot allocate skb, frame dropped | ||
137 | */ | ||
138 | static int st_nci_spi_read(struct st_nci_spi_phy *phy, | ||
139 | struct sk_buff **skb) | ||
140 | { | ||
141 | int r; | ||
142 | u8 len; | ||
143 | u8 buf[ST_NCI_SPI_MAX_SIZE]; | ||
144 | struct spi_device *dev = phy->spi_dev; | ||
145 | struct spi_transfer spi_xfer = { | ||
146 | .rx_buf = buf, | ||
147 | .len = ST_NCI_SPI_MIN_SIZE, | ||
148 | }; | ||
149 | |||
150 | r = spi_sync_transfer(dev, &spi_xfer, 1); | ||
151 | if (r < 0) | ||
152 | return -EREMOTEIO; | ||
153 | |||
154 | len = be16_to_cpu(*(__be16 *) (buf + 2)); | ||
155 | if (len > ST_NCI_SPI_MAX_SIZE) { | ||
156 | nfc_err(&dev->dev, "invalid frame len\n"); | ||
157 | phy->ndlc->hard_fault = 1; | ||
158 | return -EBADMSG; | ||
159 | } | ||
160 | |||
161 | *skb = alloc_skb(ST_NCI_SPI_MIN_SIZE + len, GFP_KERNEL); | ||
162 | if (*skb == NULL) | ||
163 | return -ENOMEM; | ||
164 | |||
165 | skb_reserve(*skb, ST_NCI_SPI_MIN_SIZE); | ||
166 | skb_put(*skb, ST_NCI_SPI_MIN_SIZE); | ||
167 | memcpy((*skb)->data, buf, ST_NCI_SPI_MIN_SIZE); | ||
168 | |||
169 | if (!len) | ||
170 | return 0; | ||
171 | |||
172 | spi_xfer.len = len; | ||
173 | r = spi_sync_transfer(dev, &spi_xfer, 1); | ||
174 | if (r < 0) { | ||
175 | kfree_skb(*skb); | ||
176 | return -EREMOTEIO; | ||
177 | } | ||
178 | |||
179 | skb_put(*skb, len); | ||
180 | memcpy((*skb)->data + ST_NCI_SPI_MIN_SIZE, buf, len); | ||
181 | |||
182 | SPI_DUMP_SKB("spi frame read", *skb); | ||
183 | |||
184 | return 0; | ||
185 | } | ||
186 | |||
187 | /* | ||
188 | * Reads an ndlc frame from the chip. | ||
189 | * | ||
190 | * On ST21NFCB, IRQ goes in idle state when read starts. | ||
191 | */ | ||
192 | static irqreturn_t st_nci_irq_thread_fn(int irq, void *phy_id) | ||
193 | { | ||
194 | struct st_nci_spi_phy *phy = phy_id; | ||
195 | struct spi_device *dev; | ||
196 | struct sk_buff *skb = NULL; | ||
197 | int r; | ||
198 | |||
199 | if (!phy || !phy->ndlc || irq != phy->spi_dev->irq) { | ||
200 | WARN_ON_ONCE(1); | ||
201 | return IRQ_NONE; | ||
202 | } | ||
203 | |||
204 | dev = phy->spi_dev; | ||
205 | dev_dbg(&dev->dev, "IRQ\n"); | ||
206 | |||
207 | if (phy->ndlc->hard_fault) | ||
208 | return IRQ_HANDLED; | ||
209 | |||
210 | if (!phy->ndlc->powered) { | ||
211 | st_nci_spi_disable(phy); | ||
212 | return IRQ_HANDLED; | ||
213 | } | ||
214 | |||
215 | r = st_nci_spi_read(phy, &skb); | ||
216 | if (r == -EREMOTEIO || r == -ENOMEM || r == -EBADMSG) | ||
217 | return IRQ_HANDLED; | ||
218 | |||
219 | ndlc_recv(phy->ndlc, skb); | ||
220 | |||
221 | return IRQ_HANDLED; | ||
222 | } | ||
223 | |||
224 | static struct nfc_phy_ops spi_phy_ops = { | ||
225 | .write = st_nci_spi_write, | ||
226 | .enable = st_nci_spi_enable, | ||
227 | .disable = st_nci_spi_disable, | ||
228 | }; | ||
229 | |||
230 | #ifdef CONFIG_OF | ||
231 | static int st_nci_spi_of_request_resources(struct spi_device *dev) | ||
232 | { | ||
233 | struct st_nci_spi_phy *phy = spi_get_drvdata(dev); | ||
234 | struct device_node *pp; | ||
235 | int gpio; | ||
236 | int r; | ||
237 | |||
238 | pp = dev->dev.of_node; | ||
239 | if (!pp) | ||
240 | return -ENODEV; | ||
241 | |||
242 | /* Get GPIO from device tree */ | ||
243 | gpio = of_get_named_gpio(pp, "reset-gpios", 0); | ||
244 | if (gpio < 0) { | ||
245 | nfc_err(&dev->dev, | ||
246 | "Failed to retrieve reset-gpios from device tree\n"); | ||
247 | return gpio; | ||
248 | } | ||
249 | |||
250 | /* GPIO request and configuration */ | ||
251 | r = devm_gpio_request_one(&dev->dev, gpio, | ||
252 | GPIOF_OUT_INIT_HIGH, "clf_reset"); | ||
253 | if (r) { | ||
254 | nfc_err(&dev->dev, "Failed to request reset pin\n"); | ||
255 | return r; | ||
256 | } | ||
257 | phy->gpio_reset = gpio; | ||
258 | |||
259 | phy->irq_polarity = irq_get_trigger_type(dev->irq); | ||
260 | |||
261 | return 0; | ||
262 | } | ||
263 | #else | ||
264 | static int st_nci_spi_of_request_resources(struct spi_device *dev) | ||
265 | { | ||
266 | return -ENODEV; | ||
267 | } | ||
268 | #endif | ||
269 | |||
270 | static int st_nci_spi_request_resources(struct spi_device *dev) | ||
271 | { | ||
272 | struct st_nci_nfc_platform_data *pdata; | ||
273 | struct st_nci_spi_phy *phy = spi_get_drvdata(dev); | ||
274 | int r; | ||
275 | |||
276 | pdata = dev->dev.platform_data; | ||
277 | if (pdata == NULL) { | ||
278 | nfc_err(&dev->dev, "No platform data\n"); | ||
279 | return -EINVAL; | ||
280 | } | ||
281 | |||
282 | /* store for later use */ | ||
283 | phy->gpio_reset = pdata->gpio_reset; | ||
284 | phy->irq_polarity = pdata->irq_polarity; | ||
285 | |||
286 | r = devm_gpio_request_one(&dev->dev, | ||
287 | phy->gpio_reset, GPIOF_OUT_INIT_HIGH, "clf_reset"); | ||
288 | if (r) { | ||
289 | pr_err("%s : reset gpio_request failed\n", __FILE__); | ||
290 | return r; | ||
291 | } | ||
292 | |||
293 | return 0; | ||
294 | } | ||
295 | |||
296 | static int st_nci_spi_probe(struct spi_device *dev) | ||
297 | { | ||
298 | struct st_nci_spi_phy *phy; | ||
299 | struct st_nci_nfc_platform_data *pdata; | ||
300 | int r; | ||
301 | |||
302 | dev_dbg(&dev->dev, "%s\n", __func__); | ||
303 | dev_dbg(&dev->dev, "IRQ: %d\n", dev->irq); | ||
304 | |||
305 | /* Check SPI platform functionnalities */ | ||
306 | if (!dev) { | ||
307 | pr_debug("%s: dev is NULL. Device is not accessible.\n", | ||
308 | __func__); | ||
309 | return -ENODEV; | ||
310 | } | ||
311 | |||
312 | phy = devm_kzalloc(&dev->dev, sizeof(struct st_nci_spi_phy), | ||
313 | GFP_KERNEL); | ||
314 | if (!phy) | ||
315 | return -ENOMEM; | ||
316 | |||
317 | phy->spi_dev = dev; | ||
318 | |||
319 | spi_set_drvdata(dev, phy); | ||
320 | |||
321 | pdata = dev->dev.platform_data; | ||
322 | if (!pdata && dev->dev.of_node) { | ||
323 | r = st_nci_spi_of_request_resources(dev); | ||
324 | if (r) { | ||
325 | nfc_err(&dev->dev, "No platform data\n"); | ||
326 | return r; | ||
327 | } | ||
328 | } else if (pdata) { | ||
329 | r = st_nci_spi_request_resources(dev); | ||
330 | if (r) { | ||
331 | nfc_err(&dev->dev, | ||
332 | "Cannot get platform resources\n"); | ||
333 | return r; | ||
334 | } | ||
335 | } else { | ||
336 | nfc_err(&dev->dev, | ||
337 | "st_nci platform resources not available\n"); | ||
338 | return -ENODEV; | ||
339 | } | ||
340 | |||
341 | r = ndlc_probe(phy, &spi_phy_ops, &dev->dev, | ||
342 | ST_NCI_FRAME_HEADROOM, ST_NCI_FRAME_TAILROOM, | ||
343 | &phy->ndlc); | ||
344 | if (r < 0) { | ||
345 | nfc_err(&dev->dev, "Unable to register ndlc layer\n"); | ||
346 | return r; | ||
347 | } | ||
348 | |||
349 | r = devm_request_threaded_irq(&dev->dev, dev->irq, NULL, | ||
350 | st_nci_irq_thread_fn, | ||
351 | phy->irq_polarity | IRQF_ONESHOT, | ||
352 | ST_NCI_SPI_DRIVER_NAME, phy); | ||
353 | if (r < 0) | ||
354 | nfc_err(&dev->dev, "Unable to register IRQ handler\n"); | ||
355 | |||
356 | return r; | ||
357 | } | ||
358 | |||
359 | static int st_nci_spi_remove(struct spi_device *dev) | ||
360 | { | ||
361 | struct st_nci_spi_phy *phy = spi_get_drvdata(dev); | ||
362 | |||
363 | dev_dbg(&dev->dev, "%s\n", __func__); | ||
364 | |||
365 | ndlc_remove(phy->ndlc); | ||
366 | |||
367 | return 0; | ||
368 | } | ||
369 | |||
370 | #ifdef CONFIG_OF | ||
371 | static const struct of_device_id of_st_nci_spi_match[] = { | ||
372 | { .compatible = "st,st21nfcb-spi", }, | ||
373 | {} | ||
374 | }; | ||
375 | MODULE_DEVICE_TABLE(of, of_st_nci_spi_match); | ||
376 | #endif | ||
377 | |||
378 | static struct spi_driver st_nci_spi_driver = { | ||
379 | .driver = { | ||
380 | .owner = THIS_MODULE, | ||
381 | .name = ST_NCI_SPI_DRIVER_NAME, | ||
382 | .of_match_table = of_match_ptr(of_st_nci_spi_match), | ||
383 | }, | ||
384 | .probe = st_nci_spi_probe, | ||
385 | .id_table = st_nci_spi_id_table, | ||
386 | .remove = st_nci_spi_remove, | ||
387 | }; | ||
388 | |||
389 | module_spi_driver(st_nci_spi_driver); | ||
390 | |||
391 | MODULE_LICENSE("GPL"); | ||
392 | MODULE_DESCRIPTION(DRIVER_DESC); | ||