summaryrefslogtreecommitdiffstats
path: root/drivers/nfc
diff options
context:
space:
mode:
authorVincent Cuissard <cuissard@marvell.com>2015-10-26 05:27:39 -0400
committerSamuel Ortiz <sameo@linux.intel.com>2015-10-26 23:18:44 -0400
commit3194c6870158e305dac2af52f83681e9cb67280f (patch)
treeb4fc6cbfbd419132051f3a06d67c5d827a5d6e5a /drivers/nfc
parente5629d29470134af1954d2bbe45c4f2b73f68ee9 (diff)
NFC: nfcmrvl: add firmware download support
Implement firmware download protocol for Marvell NFC controllers. This protocol is based on NCI frames that's why parts of its implementation use some NCI generic functions. Signed-off-by: Vincent Cuissard <cuissard@marvell.com> Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
Diffstat (limited to 'drivers/nfc')
-rw-r--r--drivers/nfc/nfcmrvl/Makefile2
-rw-r--r--drivers/nfc/nfcmrvl/fw_dnld.c553
-rw-r--r--drivers/nfc/nfcmrvl/fw_dnld.h98
-rw-r--r--drivers/nfc/nfcmrvl/main.c42
-rw-r--r--drivers/nfc/nfcmrvl/nfcmrvl.h19
-rw-r--r--drivers/nfc/nfcmrvl/uart.c12
-rw-r--r--drivers/nfc/nfcmrvl/usb.c2
7 files changed, 718 insertions, 10 deletions
diff --git a/drivers/nfc/nfcmrvl/Makefile b/drivers/nfc/nfcmrvl/Makefile
index 775196274d1f..4554ee8e3680 100644
--- a/drivers/nfc/nfcmrvl/Makefile
+++ b/drivers/nfc/nfcmrvl/Makefile
@@ -2,7 +2,7 @@
2# Makefile for NFCMRVL NCI based NFC driver 2# Makefile for NFCMRVL NCI based NFC driver
3# 3#
4 4
5nfcmrvl-y += main.o 5nfcmrvl-y += main.o fw_dnld.o
6obj-$(CONFIG_NFC_MRVL) += nfcmrvl.o 6obj-$(CONFIG_NFC_MRVL) += nfcmrvl.o
7 7
8nfcmrvl_usb-y += usb.o 8nfcmrvl_usb-y += usb.o
diff --git a/drivers/nfc/nfcmrvl/fw_dnld.c b/drivers/nfc/nfcmrvl/fw_dnld.c
new file mode 100644
index 000000000000..bfa771392b1f
--- /dev/null
+++ b/drivers/nfc/nfcmrvl/fw_dnld.c
@@ -0,0 +1,553 @@
1/*
2 * Marvell NFC driver: Firmware downloader
3 *
4 * Copyright (C) 2015, Marvell International Ltd.
5 *
6 * This software file (the "File") is distributed by Marvell International
7 * Ltd. under the terms of the GNU General Public License Version 2, June 1991
8 * (the "License"). You may use, redistribute and/or modify this File in
9 * accordance with the terms and conditions of the License, a copy of which
10 * is available on the worldwide web at
11 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
12 *
13 * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
14 * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
15 * ARE EXPRESSLY DISCLAIMED. The License provides additional details about
16 * this warranty disclaimer.
17 */
18
19#include <linux/module.h>
20#include <linux/unaligned/access_ok.h>
21#include <linux/firmware.h>
22#include <linux/nfc.h>
23#include <net/nfc/nci.h>
24#include <net/nfc/nci_core.h>
25#include "nfcmrvl.h"
26
27#define FW_DNLD_TIMEOUT 15000
28
29#define NCI_OP_PROPRIETARY_BOOT_CMD nci_opcode_pack(NCI_GID_PROPRIETARY, \
30 NCI_OP_PROP_BOOT_CMD)
31
32/* FW download states */
33
34enum {
35 STATE_RESET = 0,
36 STATE_INIT,
37 STATE_SET_REF_CLOCK,
38 STATE_SET_HI_CONFIG,
39 STATE_OPEN_LC,
40 STATE_FW_DNLD,
41 STATE_CLOSE_LC,
42 STATE_BOOT
43};
44
45enum {
46 SUBSTATE_WAIT_COMMAND = 0,
47 SUBSTATE_WAIT_ACK_CREDIT,
48 SUBSTATE_WAIT_NACK_CREDIT,
49 SUBSTATE_WAIT_DATA_CREDIT,
50};
51
52/*
53** Patterns for responses
54*/
55
56static const uint8_t nci_pattern_core_reset_ntf[] = {
57 0x60, 0x00, 0x02, 0xA0, 0x01
58};
59
60static const uint8_t nci_pattern_core_init_rsp[] = {
61 0x40, 0x01, 0x11
62};
63
64static const uint8_t nci_pattern_core_set_config_rsp[] = {
65 0x40, 0x02, 0x02, 0x00, 0x00
66};
67
68static const uint8_t nci_pattern_core_conn_create_rsp[] = {
69 0x40, 0x04, 0x04, 0x00
70};
71
72static const uint8_t nci_pattern_core_conn_close_rsp[] = {
73 0x40, 0x05, 0x01, 0x00
74};
75
76static const uint8_t nci_pattern_core_conn_credits_ntf[] = {
77 0x60, 0x06, 0x03, 0x01, NCI_CORE_LC_CONNID_PROP_FW_DL, 0x01
78};
79
80static const uint8_t nci_pattern_proprietary_boot_rsp[] = {
81 0x4F, 0x3A, 0x01, 0x00
82};
83
84static struct sk_buff *alloc_lc_skb(struct nfcmrvl_private *priv, uint8_t plen)
85{
86 struct sk_buff *skb;
87 struct nci_data_hdr *hdr;
88
89 skb = nci_skb_alloc(priv->ndev, (NCI_DATA_HDR_SIZE + plen), GFP_KERNEL);
90 if (!skb) {
91 pr_err("no memory for data\n");
92 return NULL;
93 }
94
95 hdr = (struct nci_data_hdr *) skb_put(skb, NCI_DATA_HDR_SIZE);
96 hdr->conn_id = NCI_CORE_LC_CONNID_PROP_FW_DL;
97 hdr->rfu = 0;
98 hdr->plen = plen;
99
100 nci_mt_set((__u8 *)hdr, NCI_MT_DATA_PKT);
101 nci_pbf_set((__u8 *)hdr, NCI_PBF_LAST);
102
103 return skb;
104}
105
106static void fw_dnld_over(struct nfcmrvl_private *priv, u32 error)
107{
108 if (priv->fw_dnld.fw) {
109 release_firmware(priv->fw_dnld.fw);
110 priv->fw_dnld.fw = NULL;
111 priv->fw_dnld.header = NULL;
112 priv->fw_dnld.binary_config = NULL;
113 }
114
115 atomic_set(&priv->ndev->cmd_cnt, 0);
116 del_timer_sync(&priv->ndev->cmd_timer);
117
118 del_timer_sync(&priv->fw_dnld.timer);
119
120 nfc_info(priv->dev, "FW loading over (%d)]\n", error);
121
122 if (error != 0) {
123 /* failed, halt the chip to avoid power consumption */
124 nfcmrvl_chip_halt(priv);
125 }
126
127 nfc_fw_download_done(priv->ndev->nfc_dev, priv->fw_dnld.name, error);
128}
129
130static void fw_dnld_timeout(unsigned long arg)
131{
132 struct nfcmrvl_private *priv = (struct nfcmrvl_private *) arg;
133
134 nfc_err(priv->dev, "FW loading timeout");
135 priv->fw_dnld.state = STATE_RESET;
136 fw_dnld_over(priv, -ETIMEDOUT);
137}
138
139static int process_state_reset(struct nfcmrvl_private *priv,
140 struct sk_buff *skb)
141{
142 if (sizeof(nci_pattern_core_reset_ntf) != skb->len ||
143 memcmp(skb->data, nci_pattern_core_reset_ntf,
144 sizeof(nci_pattern_core_reset_ntf)))
145 return -EINVAL;
146
147 nfc_info(priv->dev, "BootROM reset, start fw download\n");
148
149 /* Start FW download state machine */
150 priv->fw_dnld.state = STATE_INIT;
151 nci_send_cmd(priv->ndev, NCI_OP_CORE_INIT_CMD, 0, NULL);
152
153 return 0;
154}
155
156static int process_state_init(struct nfcmrvl_private *priv, struct sk_buff *skb)
157{
158 struct nci_core_set_config_cmd cmd;
159
160 if (sizeof(nci_pattern_core_init_rsp) >= skb->len ||
161 memcmp(skb->data, nci_pattern_core_init_rsp,
162 sizeof(nci_pattern_core_init_rsp)))
163 return -EINVAL;
164
165 cmd.num_params = 1;
166 cmd.param.id = NFCMRVL_PROP_REF_CLOCK;
167 cmd.param.len = 4;
168 memcpy(cmd.param.val, &priv->fw_dnld.header->ref_clock, 4);
169
170 nci_send_cmd(priv->ndev, NCI_OP_CORE_SET_CONFIG_CMD, 3 + cmd.param.len,
171 &cmd);
172
173 priv->fw_dnld.state = STATE_SET_REF_CLOCK;
174 return 0;
175}
176
177static void create_lc(struct nfcmrvl_private *priv)
178{
179 uint8_t param[2] = { NCI_CORE_LC_PROP_FW_DL, 0x0 };
180
181 priv->fw_dnld.state = STATE_OPEN_LC;
182 nci_send_cmd(priv->ndev, NCI_OP_CORE_CONN_CREATE_CMD, 2, param);
183}
184
185static int process_state_set_ref_clock(struct nfcmrvl_private *priv,
186 struct sk_buff *skb)
187{
188 struct nci_core_set_config_cmd cmd;
189
190 if (sizeof(nci_pattern_core_set_config_rsp) != skb->len ||
191 memcmp(skb->data, nci_pattern_core_set_config_rsp, skb->len))
192 return -EINVAL;
193
194 cmd.num_params = 1;
195 cmd.param.id = NFCMRVL_PROP_SET_HI_CONFIG;
196
197 switch (priv->phy) {
198 case NFCMRVL_PHY_UART:
199 cmd.param.len = 5;
200 memcpy(cmd.param.val,
201 &priv->fw_dnld.binary_config->uart.baudrate,
202 4);
203 cmd.param.val[4] =
204 priv->fw_dnld.binary_config->uart.flow_control;
205 break;
206 case NFCMRVL_PHY_I2C:
207 cmd.param.len = 5;
208 memcpy(cmd.param.val,
209 &priv->fw_dnld.binary_config->i2c.clk,
210 4);
211 cmd.param.val[4] = 0;
212 break;
213 case NFCMRVL_PHY_SPI:
214 cmd.param.len = 5;
215 memcpy(cmd.param.val,
216 &priv->fw_dnld.binary_config->spi.clk,
217 4);
218 cmd.param.val[4] = 0;
219 break;
220 default:
221 create_lc(priv);
222 return 0;
223 }
224
225 priv->fw_dnld.state = STATE_SET_HI_CONFIG;
226 nci_send_cmd(priv->ndev, NCI_OP_CORE_SET_CONFIG_CMD, 3 + cmd.param.len,
227 &cmd);
228 return 0;
229}
230
231static int process_state_set_hi_config(struct nfcmrvl_private *priv,
232 struct sk_buff *skb)
233{
234 if (sizeof(nci_pattern_core_set_config_rsp) != skb->len ||
235 memcmp(skb->data, nci_pattern_core_set_config_rsp, skb->len))
236 return -EINVAL;
237
238 create_lc(priv);
239 return 0;
240}
241
242static int process_state_open_lc(struct nfcmrvl_private *priv,
243 struct sk_buff *skb)
244{
245 if (sizeof(nci_pattern_core_conn_create_rsp) >= skb->len ||
246 memcmp(skb->data, nci_pattern_core_conn_create_rsp,
247 sizeof(nci_pattern_core_conn_create_rsp)))
248 return -EINVAL;
249
250 priv->fw_dnld.state = STATE_FW_DNLD;
251 priv->fw_dnld.substate = SUBSTATE_WAIT_COMMAND;
252 priv->fw_dnld.offset = priv->fw_dnld.binary_config->offset;
253 return 0;
254}
255
256static int process_state_fw_dnld(struct nfcmrvl_private *priv,
257 struct sk_buff *skb)
258{
259 uint16_t len;
260 uint16_t comp_len;
261 struct sk_buff *out_skb;
262
263 switch (priv->fw_dnld.substate) {
264 case SUBSTATE_WAIT_COMMAND:
265 /*
266 * Command format:
267 * B0..2: NCI header
268 * B3 : Helper command (0xA5)
269 * B4..5: le16 data size
270 * B6..7: le16 data size complement (~)
271 * B8..N: payload
272 */
273
274 /* Remove NCI HDR */
275 skb_pull(skb, 3);
276 if (skb->data[0] != HELPER_CMD_PACKET_FORMAT || skb->len != 5) {
277 nfc_err(priv->dev, "bad command");
278 return -EINVAL;
279 }
280 skb_pull(skb, 1);
281 memcpy(&len, skb->data, 2);
282 skb_pull(skb, 2);
283 memcpy(&comp_len, skb->data, 2);
284 skb_pull(skb, 2);
285 len = get_unaligned_le16(&len);
286 comp_len = get_unaligned_le16(&comp_len);
287 if (((~len) & 0xFFFF) != comp_len) {
288 nfc_err(priv->dev, "bad len complement: %x %x %x",
289 len, comp_len, (~len & 0xFFFF));
290 out_skb = alloc_lc_skb(priv, 1);
291 if (!out_skb)
292 return -ENOMEM;
293 *skb_put(out_skb, 1) = 0xBF;
294 nci_send_frame(priv->ndev, out_skb);
295 priv->fw_dnld.substate = SUBSTATE_WAIT_NACK_CREDIT;
296 return 0;
297 }
298 priv->fw_dnld.chunk_len = len;
299 out_skb = alloc_lc_skb(priv, 1);
300 if (!out_skb)
301 return -ENOMEM;
302 *skb_put(out_skb, 1) = HELPER_ACK_PACKET_FORMAT;
303 nci_send_frame(priv->ndev, out_skb);
304 priv->fw_dnld.substate = SUBSTATE_WAIT_ACK_CREDIT;
305 break;
306
307 case SUBSTATE_WAIT_ACK_CREDIT:
308 if (sizeof(nci_pattern_core_conn_credits_ntf) != skb->len ||
309 memcmp(nci_pattern_core_conn_credits_ntf, skb->data,
310 skb->len)) {
311 nfc_err(priv->dev, "bad packet: waiting for credit");
312 return -EINVAL;
313 }
314 if (priv->fw_dnld.chunk_len == 0) {
315 /* FW Loading is done */
316 uint8_t conn_id = NCI_CORE_LC_CONNID_PROP_FW_DL;
317
318 priv->fw_dnld.state = STATE_CLOSE_LC;
319 nci_send_cmd(priv->ndev, NCI_OP_CORE_CONN_CLOSE_CMD,
320 1, &conn_id);
321 } else {
322 out_skb = alloc_lc_skb(priv, priv->fw_dnld.chunk_len);
323 if (!out_skb)
324 return -ENOMEM;
325 memcpy(skb_put(out_skb, priv->fw_dnld.chunk_len),
326 ((uint8_t *)priv->fw_dnld.fw->data) +
327 priv->fw_dnld.offset,
328 priv->fw_dnld.chunk_len);
329 nci_send_frame(priv->ndev, out_skb);
330 priv->fw_dnld.substate = SUBSTATE_WAIT_DATA_CREDIT;
331 }
332 break;
333
334 case SUBSTATE_WAIT_DATA_CREDIT:
335 if (sizeof(nci_pattern_core_conn_credits_ntf) != skb->len ||
336 memcmp(nci_pattern_core_conn_credits_ntf, skb->data,
337 skb->len)) {
338 nfc_err(priv->dev, "bad packet: waiting for credit");
339 return -EINVAL;
340 }
341 priv->fw_dnld.offset += priv->fw_dnld.chunk_len;
342 priv->fw_dnld.chunk_len = 0;
343 priv->fw_dnld.substate = SUBSTATE_WAIT_COMMAND;
344 break;
345
346 case SUBSTATE_WAIT_NACK_CREDIT:
347 if (sizeof(nci_pattern_core_conn_credits_ntf) != skb->len ||
348 memcmp(nci_pattern_core_conn_credits_ntf, skb->data,
349 skb->len)) {
350 nfc_err(priv->dev, "bad packet: waiting for credit");
351 return -EINVAL;
352 }
353 priv->fw_dnld.substate = SUBSTATE_WAIT_COMMAND;
354 break;
355 }
356 return 0;
357}
358
359static int process_state_close_lc(struct nfcmrvl_private *priv,
360 struct sk_buff *skb)
361{
362 if (sizeof(nci_pattern_core_conn_close_rsp) != skb->len ||
363 memcmp(skb->data, nci_pattern_core_conn_close_rsp, skb->len))
364 return -EINVAL;
365
366 priv->fw_dnld.state = STATE_BOOT;
367 nci_send_cmd(priv->ndev, NCI_OP_PROPRIETARY_BOOT_CMD, 0, NULL);
368 return 0;
369}
370
371static int process_state_boot(struct nfcmrvl_private *priv, struct sk_buff *skb)
372{
373 if (sizeof(nci_pattern_proprietary_boot_rsp) != skb->len ||
374 memcmp(skb->data, nci_pattern_proprietary_boot_rsp, skb->len))
375 return -EINVAL;
376
377 /*
378 * Update HI config to use the right configuration for the next
379 * data exchanges.
380 */
381 priv->if_ops->nci_update_config(priv,
382 &priv->fw_dnld.binary_config->config);
383
384 if (priv->fw_dnld.binary_config == &priv->fw_dnld.header->helper) {
385 /*
386 * This is the case where an helper was needed and we have
387 * uploaded it. Now we have to wait the next RESET NTF to start
388 * FW download.
389 */
390 priv->fw_dnld.state = STATE_RESET;
391 priv->fw_dnld.binary_config = &priv->fw_dnld.header->firmware;
392 nfc_info(priv->dev, "FW loading: helper loaded");
393 } else {
394 nfc_info(priv->dev, "FW loading: firmware loaded");
395 fw_dnld_over(priv, 0);
396 }
397 return 0;
398}
399
400static void fw_dnld_rx_work(struct work_struct *work)
401{
402 int ret;
403 struct sk_buff *skb;
404 struct nfcmrvl_fw_dnld *fw_dnld = container_of(work,
405 struct nfcmrvl_fw_dnld,
406 rx_work);
407 struct nfcmrvl_private *priv = container_of(fw_dnld,
408 struct nfcmrvl_private,
409 fw_dnld);
410
411 while ((skb = skb_dequeue(&fw_dnld->rx_q))) {
412 nfc_send_to_raw_sock(priv->ndev->nfc_dev, skb,
413 RAW_PAYLOAD_NCI, NFC_DIRECTION_RX);
414 switch (fw_dnld->state) {
415 case STATE_RESET:
416 ret = process_state_reset(priv, skb);
417 break;
418 case STATE_INIT:
419 ret = process_state_init(priv, skb);
420 break;
421 case STATE_SET_REF_CLOCK:
422 ret = process_state_set_ref_clock(priv, skb);
423 break;
424 case STATE_SET_HI_CONFIG:
425 ret = process_state_set_hi_config(priv, skb);
426 break;
427 case STATE_OPEN_LC:
428 ret = process_state_open_lc(priv, skb);
429 break;
430 case STATE_FW_DNLD:
431 ret = process_state_fw_dnld(priv, skb);
432 break;
433 case STATE_CLOSE_LC:
434 ret = process_state_close_lc(priv, skb);
435 break;
436 case STATE_BOOT:
437 ret = process_state_boot(priv, skb);
438 break;
439 default:
440 ret = -EFAULT;
441 }
442
443 kfree_skb(skb);
444
445 if (ret != 0) {
446 nfc_err(priv->dev, "FW loading error");
447 fw_dnld_over(priv, ret);
448 break;
449 }
450 }
451}
452
453int nfcmrvl_fw_dnld_init(struct nfcmrvl_private *priv)
454{
455 char name[32];
456
457 INIT_WORK(&priv->fw_dnld.rx_work, fw_dnld_rx_work);
458 snprintf(name, sizeof(name), "%s_nfcmrvl_fw_dnld_rx_wq",
459 dev_name(priv->dev));
460 priv->fw_dnld.rx_wq = create_singlethread_workqueue(name);
461 if (!priv->fw_dnld.rx_wq)
462 return -ENOMEM;
463 skb_queue_head_init(&priv->fw_dnld.rx_q);
464 return 0;
465}
466
467void nfcmrvl_fw_dnld_deinit(struct nfcmrvl_private *priv)
468{
469 destroy_workqueue(priv->fw_dnld.rx_wq);
470}
471
472void nfcmrvl_fw_dnld_recv_frame(struct nfcmrvl_private *priv,
473 struct sk_buff *skb)
474{
475 /* Allow next command */
476 atomic_set(&priv->ndev->cmd_cnt, 1);
477 del_timer_sync(&priv->ndev->cmd_timer);
478
479 /* Queue and trigger rx work */
480 skb_queue_tail(&priv->fw_dnld.rx_q, skb);
481 queue_work(priv->fw_dnld.rx_wq, &priv->fw_dnld.rx_work);
482}
483
484void nfcmrvl_fw_dnld_abort(struct nfcmrvl_private *priv)
485{
486 fw_dnld_over(priv, -EHOSTDOWN);
487}
488
489int nfcmrvl_fw_dnld_start(struct nci_dev *ndev, const char *firmware_name)
490{
491 struct nfcmrvl_private *priv = nci_get_drvdata(ndev);
492 struct nfcmrvl_fw_dnld *fw_dnld = &priv->fw_dnld;
493
494 if (!priv->support_fw_dnld)
495 return -ENOTSUPP;
496
497 if (!firmware_name || !firmware_name[0])
498 return -EINVAL;
499
500 strcpy(fw_dnld->name, firmware_name);
501
502 /*
503 * Retrieve FW binary file and parse it to initialize FW download
504 * state machine.
505 */
506
507 /* Retrieve FW binary */
508 if (request_firmware(&fw_dnld->fw, firmware_name, priv->dev) < 0) {
509 nfc_err(priv->dev, "failed to retrieve FW %s", firmware_name);
510 return -ENOENT;
511 }
512
513 fw_dnld->header = (const struct nfcmrvl_fw *) priv->fw_dnld.fw->data;
514
515 if (fw_dnld->header->magic != NFCMRVL_FW_MAGIC ||
516 fw_dnld->header->phy != priv->phy) {
517 nfc_err(priv->dev, "bad firmware binary %s magic=0x%x phy=%d",
518 firmware_name, fw_dnld->header->magic,
519 fw_dnld->header->phy);
520 release_firmware(fw_dnld->fw);
521 fw_dnld->header = NULL;
522 return -EINVAL;
523 }
524
525 if (fw_dnld->header->helper.offset != 0) {
526 nfc_info(priv->dev, "loading helper");
527 fw_dnld->binary_config = &fw_dnld->header->helper;
528 } else {
529 nfc_info(priv->dev, "loading firmware");
530 fw_dnld->binary_config = &fw_dnld->header->firmware;
531 }
532
533 /* Configure a timer for timeout */
534 setup_timer(&priv->fw_dnld.timer, fw_dnld_timeout,
535 (unsigned long) priv);
536 mod_timer(&priv->fw_dnld.timer,
537 jiffies + msecs_to_jiffies(FW_DNLD_TIMEOUT));
538
539 /* Ronfigure HI to be sure that it is the bootrom values */
540 priv->if_ops->nci_update_config(priv,
541 &fw_dnld->header->bootrom.config);
542
543 /* Allow first command */
544 atomic_set(&priv->ndev->cmd_cnt, 1);
545
546 /* First, reset the chip */
547 priv->fw_dnld.state = STATE_RESET;
548 nfcmrvl_chip_reset(priv);
549
550 /* Now wait for CORE_RESET_NTF or timeout */
551
552 return 0;
553}
diff --git a/drivers/nfc/nfcmrvl/fw_dnld.h b/drivers/nfc/nfcmrvl/fw_dnld.h
new file mode 100644
index 000000000000..ee4a339c05fd
--- /dev/null
+++ b/drivers/nfc/nfcmrvl/fw_dnld.h
@@ -0,0 +1,98 @@
1/**
2 * Marvell NFC driver: Firmware downloader
3 *
4 * Copyright (C) 2015, Marvell International Ltd.
5 *
6 * This software file (the "File") is distributed by Marvell International
7 * Ltd. under the terms of the GNU General Public License Version 2, June 1991
8 * (the "License"). You may use, redistribute and/or modify this File in
9 * accordance with the terms and conditions of the License, a copy of which
10 * is available on the worldwide web at
11 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
12 *
13 * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
14 * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
15 * ARE EXPRESSLY DISCLAIMED. The License provides additional details about
16 * this warranty disclaimer.
17 **/
18
19#ifndef __NFCMRVL_FW_DNLD_H__
20#define __NFCMRVL_FW_DNLD_H__
21
22#include <linux/workqueue.h>
23
24#define NFCMRVL_FW_MAGIC 0x88888888
25
26#define NCI_OP_PROP_BOOT_CMD 0x3A
27
28#define NCI_CORE_LC_PROP_FW_DL 0xFD
29#define NCI_CORE_LC_CONNID_PROP_FW_DL 0x02
30
31#define HELPER_CMD_ENTRY_POINT 0x04
32#define HELPER_CMD_PACKET_FORMAT 0xA5
33#define HELPER_ACK_PACKET_FORMAT 0x5A
34#define HELPER_RETRY_REQUESTED (1 << 15)
35
36struct nfcmrvl_private;
37
38struct nfcmrvl_fw_uart_config {
39 uint8_t flow_control;
40 uint32_t baudrate;
41} __packed;
42
43struct nfcmrvl_fw_i2c_config {
44 uint32_t clk;
45} __packed;
46
47struct nfcmrvl_fw_spi_config {
48 uint32_t clk;
49} __packed;
50
51struct nfcmrvl_fw_binary_config {
52 uint32_t offset;
53 union {
54 void *config;
55 struct nfcmrvl_fw_uart_config uart;
56 struct nfcmrvl_fw_i2c_config i2c;
57 struct nfcmrvl_fw_spi_config spi;
58 uint8_t reserved[64];
59 };
60} __packed;
61
62struct nfcmrvl_fw {
63 uint32_t magic;
64 uint32_t ref_clock;
65 uint32_t phy;
66 struct nfcmrvl_fw_binary_config bootrom;
67 struct nfcmrvl_fw_binary_config helper;
68 struct nfcmrvl_fw_binary_config firmware;
69 uint8_t reserved[64];
70} __packed;
71
72struct nfcmrvl_fw_dnld {
73 char name[NFC_FIRMWARE_NAME_MAXSIZE + 1];
74 const struct firmware *fw;
75
76 const struct nfcmrvl_fw *header;
77 const struct nfcmrvl_fw_binary_config *binary_config;
78
79 int state;
80 int substate;
81 int offset;
82 int chunk_len;
83
84 struct workqueue_struct *rx_wq;
85 struct work_struct rx_work;
86 struct sk_buff_head rx_q;
87
88 struct timer_list timer;
89};
90
91int nfcmrvl_fw_dnld_init(struct nfcmrvl_private *priv);
92void nfcmrvl_fw_dnld_deinit(struct nfcmrvl_private *priv);
93void nfcmrvl_fw_dnld_abort(struct nfcmrvl_private *priv);
94int nfcmrvl_fw_dnld_start(struct nci_dev *ndev, const char *firmware_name);
95void nfcmrvl_fw_dnld_recv_frame(struct nfcmrvl_private *priv,
96 struct sk_buff *skb);
97
98#endif
diff --git a/drivers/nfc/nfcmrvl/main.c b/drivers/nfc/nfcmrvl/main.c
index 77e292f45dd8..6fb23cc9a659 100644
--- a/drivers/nfc/nfcmrvl/main.c
+++ b/drivers/nfc/nfcmrvl/main.c
@@ -61,9 +61,6 @@ static int nfcmrvl_nci_send(struct nci_dev *ndev, struct sk_buff *skb)
61 61
62 skb->dev = (void *)ndev; 62 skb->dev = (void *)ndev;
63 63
64 if (!test_bit(NFCMRVL_NCI_RUNNING, &priv->flags))
65 return -EBUSY;
66
67 if (priv->config.hci_muxed) { 64 if (priv->config.hci_muxed) {
68 unsigned char *hdr; 65 unsigned char *hdr;
69 unsigned char len = skb->len; 66 unsigned char len = skb->len;
@@ -86,11 +83,18 @@ static int nfcmrvl_nci_setup(struct nci_dev *ndev)
86 return 0; 83 return 0;
87} 84}
88 85
86static int nfcmrvl_nci_fw_download(struct nci_dev *ndev,
87 const char *firmware_name)
88{
89 return nfcmrvl_fw_dnld_start(ndev, firmware_name);
90}
91
89static struct nci_ops nfcmrvl_nci_ops = { 92static struct nci_ops nfcmrvl_nci_ops = {
90 .open = nfcmrvl_nci_open, 93 .open = nfcmrvl_nci_open,
91 .close = nfcmrvl_nci_close, 94 .close = nfcmrvl_nci_close,
92 .send = nfcmrvl_nci_send, 95 .send = nfcmrvl_nci_send,
93 .setup = nfcmrvl_nci_setup, 96 .setup = nfcmrvl_nci_setup,
97 .fw_download = nfcmrvl_nci_fw_download,
94}; 98};
95 99
96struct nfcmrvl_private *nfcmrvl_nci_register_dev(void *drv_data, 100struct nfcmrvl_private *nfcmrvl_nci_register_dev(void *drv_data,
@@ -143,18 +147,26 @@ struct nfcmrvl_private *nfcmrvl_nci_register_dev(void *drv_data,
143 147
144 nci_set_drvdata(priv->ndev, priv); 148 nci_set_drvdata(priv->ndev, priv);
145 149
146 nfcmrvl_chip_reset(priv);
147
148 rc = nci_register_device(priv->ndev); 150 rc = nci_register_device(priv->ndev);
149 if (rc) { 151 if (rc) {
150 nfc_err(dev, "nci_register_device failed %d\n", rc); 152 nfc_err(dev, "nci_register_device failed %d\n", rc);
151 nci_free_device(priv->ndev); 153 goto error_free_dev;
152 goto error; 154 }
155
156 /* Ensure that controller is powered off */
157 nfcmrvl_chip_halt(priv);
158
159 rc = nfcmrvl_fw_dnld_init(priv);
160 if (rc) {
161 nfc_err(dev, "failed to initialize FW download %d\n", rc);
162 goto error_free_dev;
153 } 163 }
154 164
155 nfc_info(dev, "registered with nci successfully\n"); 165 nfc_info(dev, "registered with nci successfully\n");
156 return priv; 166 return priv;
157 167
168error_free_dev:
169 nci_free_device(priv->ndev);
158error: 170error:
159 kfree(priv); 171 kfree(priv);
160 return ERR_PTR(rc); 172 return ERR_PTR(rc);
@@ -165,6 +177,11 @@ void nfcmrvl_nci_unregister_dev(struct nfcmrvl_private *priv)
165{ 177{
166 struct nci_dev *ndev = priv->ndev; 178 struct nci_dev *ndev = priv->ndev;
167 179
180 if (priv->ndev->nfc_dev->fw_download_in_progress)
181 nfcmrvl_fw_dnld_abort(priv);
182
183 nfcmrvl_fw_dnld_deinit(priv);
184
168 nci_unregister_device(ndev); 185 nci_unregister_device(ndev);
169 nci_free_device(ndev); 186 nci_free_device(ndev);
170 kfree(priv); 187 kfree(priv);
@@ -185,6 +202,11 @@ int nfcmrvl_nci_recv_frame(struct nfcmrvl_private *priv, struct sk_buff *skb)
185 } 202 }
186 } 203 }
187 204
205 if (priv->ndev->nfc_dev->fw_download_in_progress) {
206 nfcmrvl_fw_dnld_recv_frame(priv, skb);
207 return 0;
208 }
209
188 if (test_bit(NFCMRVL_NCI_RUNNING, &priv->flags)) 210 if (test_bit(NFCMRVL_NCI_RUNNING, &priv->flags))
189 nci_recv_frame(priv->ndev, skb); 211 nci_recv_frame(priv->ndev, skb);
190 else { 212 else {
@@ -213,6 +235,12 @@ void nfcmrvl_chip_reset(struct nfcmrvl_private *priv)
213 nfc_info(priv->dev, "no reset available on this interface\n"); 235 nfc_info(priv->dev, "no reset available on this interface\n");
214} 236}
215 237
238void nfcmrvl_chip_halt(struct nfcmrvl_private *priv)
239{
240 if (priv->config.reset_n_io)
241 gpio_set_value(priv->config.reset_n_io, 0);
242}
243
216#ifdef CONFIG_OF 244#ifdef CONFIG_OF
217 245
218int nfcmrvl_parse_dt(struct device_node *node, 246int nfcmrvl_parse_dt(struct device_node *node,
diff --git a/drivers/nfc/nfcmrvl/nfcmrvl.h b/drivers/nfc/nfcmrvl/nfcmrvl.h
index e5a7e5464f2e..1b30f043139c 100644
--- a/drivers/nfc/nfcmrvl/nfcmrvl.h
+++ b/drivers/nfc/nfcmrvl/nfcmrvl.h
@@ -1,7 +1,7 @@
1/** 1/**
2 * Marvell NFC driver 2 * Marvell NFC driver
3 * 3 *
4 * Copyright (C) 2014, Marvell International Ltd. 4 * Copyright (C) 2014-2015, Marvell International Ltd.
5 * 5 *
6 * This software file (the "File") is distributed by Marvell International 6 * This software file (the "File") is distributed by Marvell International
7 * Ltd. under the terms of the GNU General Public License Version 2, June 1991 7 * Ltd. under the terms of the GNU General Public License Version 2, June 1991
@@ -21,6 +21,8 @@
21 21
22#include <linux/platform_data/nfcmrvl.h> 22#include <linux/platform_data/nfcmrvl.h>
23 23
24#include "fw_dnld.h"
25
24/* Define private flags: */ 26/* Define private flags: */
25#define NFCMRVL_NCI_RUNNING 1 27#define NFCMRVL_NCI_RUNNING 1
26 28
@@ -37,6 +39,8 @@
37*/ 39*/
38 40
39#define NFCMRVL_PB_BAIL_OUT 0x11 41#define NFCMRVL_PB_BAIL_OUT 0x11
42#define NFCMRVL_PROP_REF_CLOCK 0xF0
43#define NFCMRVL_PROP_SET_HI_CONFIG 0xF1
40 44
41/* 45/*
42** HCI defines 46** HCI defines
@@ -52,9 +56,10 @@
52enum nfcmrvl_phy { 56enum nfcmrvl_phy {
53 NFCMRVL_PHY_USB = 0, 57 NFCMRVL_PHY_USB = 0,
54 NFCMRVL_PHY_UART = 1, 58 NFCMRVL_PHY_UART = 1,
59 NFCMRVL_PHY_I2C = 2,
60 NFCMRVL_PHY_SPI = 3,
55}; 61};
56 62
57
58struct nfcmrvl_private { 63struct nfcmrvl_private {
59 64
60 unsigned long flags; 65 unsigned long flags;
@@ -62,8 +67,15 @@ struct nfcmrvl_private {
62 /* Platform configuration */ 67 /* Platform configuration */
63 struct nfcmrvl_platform_data config; 68 struct nfcmrvl_platform_data config;
64 69
70 /* Parent dev */
65 struct nci_dev *ndev; 71 struct nci_dev *ndev;
66 72
73 /* FW download context */
74 struct nfcmrvl_fw_dnld fw_dnld;
75
76 /* FW download support */
77 bool support_fw_dnld;
78
67 /* 79 /*
68 ** PHY related information 80 ** PHY related information
69 */ 81 */
@@ -82,6 +94,8 @@ struct nfcmrvl_if_ops {
82 int (*nci_open) (struct nfcmrvl_private *priv); 94 int (*nci_open) (struct nfcmrvl_private *priv);
83 int (*nci_close) (struct nfcmrvl_private *priv); 95 int (*nci_close) (struct nfcmrvl_private *priv);
84 int (*nci_send) (struct nfcmrvl_private *priv, struct sk_buff *skb); 96 int (*nci_send) (struct nfcmrvl_private *priv, struct sk_buff *skb);
97 void (*nci_update_config)(struct nfcmrvl_private *priv,
98 const void *param);
85}; 99};
86 100
87void nfcmrvl_nci_unregister_dev(struct nfcmrvl_private *priv); 101void nfcmrvl_nci_unregister_dev(struct nfcmrvl_private *priv);
@@ -93,6 +107,7 @@ struct nfcmrvl_private *nfcmrvl_nci_register_dev(void *drv_data,
93 107
94 108
95void nfcmrvl_chip_reset(struct nfcmrvl_private *priv); 109void nfcmrvl_chip_reset(struct nfcmrvl_private *priv);
110void nfcmrvl_chip_halt(struct nfcmrvl_private *priv);
96 111
97int nfcmrvl_parse_dt(struct device_node *node, 112int nfcmrvl_parse_dt(struct device_node *node,
98 struct nfcmrvl_platform_data *pdata); 113 struct nfcmrvl_platform_data *pdata);
diff --git a/drivers/nfc/nfcmrvl/uart.c b/drivers/nfc/nfcmrvl/uart.c
index 61442d6528a6..835582435560 100644
--- a/drivers/nfc/nfcmrvl/uart.c
+++ b/drivers/nfc/nfcmrvl/uart.c
@@ -50,10 +50,21 @@ static int nfcmrvl_uart_nci_send(struct nfcmrvl_private *priv,
50 return nu->ops.send(nu, skb); 50 return nu->ops.send(nu, skb);
51} 51}
52 52
53static void nfcmrvl_uart_nci_update_config(struct nfcmrvl_private *priv,
54 const void *param)
55{
56 struct nci_uart *nu = priv->drv_data;
57 const struct nfcmrvl_fw_uart_config *config = param;
58
59 nci_uart_set_config(nu, le32_to_cpu(config->baudrate),
60 config->flow_control);
61}
62
53static struct nfcmrvl_if_ops uart_ops = { 63static struct nfcmrvl_if_ops uart_ops = {
54 .nci_open = nfcmrvl_uart_nci_open, 64 .nci_open = nfcmrvl_uart_nci_open,
55 .nci_close = nfcmrvl_uart_nci_close, 65 .nci_close = nfcmrvl_uart_nci_close,
56 .nci_send = nfcmrvl_uart_nci_send, 66 .nci_send = nfcmrvl_uart_nci_send,
67 .nci_update_config = nfcmrvl_uart_nci_update_config
57}; 68};
58 69
59#ifdef CONFIG_OF 70#ifdef CONFIG_OF
@@ -132,6 +143,7 @@ static int nfcmrvl_nci_uart_open(struct nci_uart *nu)
132 return PTR_ERR(priv); 143 return PTR_ERR(priv);
133 144
134 priv->phy = NFCMRVL_PHY_UART; 145 priv->phy = NFCMRVL_PHY_UART;
146 priv->support_fw_dnld = true;
135 147
136 nu->drv_data = priv; 148 nu->drv_data = priv;
137 nu->ndev = priv->ndev; 149 nu->ndev = priv->ndev;
diff --git a/drivers/nfc/nfcmrvl/usb.c b/drivers/nfc/nfcmrvl/usb.c
index 5e624d1a730a..b27617434427 100644
--- a/drivers/nfc/nfcmrvl/usb.c
+++ b/drivers/nfc/nfcmrvl/usb.c
@@ -347,6 +347,8 @@ static int nfcmrvl_probe(struct usb_interface *intf,
347 347
348 drv_data->priv = priv; 348 drv_data->priv = priv;
349 drv_data->priv->phy = NFCMRVL_PHY_USB; 349 drv_data->priv->phy = NFCMRVL_PHY_USB;
350 drv_data->priv->support_fw_dnld = false;
351
350 priv->dev = &drv_data->udev->dev; 352 priv->dev = &drv_data->udev->dev;
351 353
352 usb_set_intfdata(intf, drv_data); 354 usb_set_intfdata(intf, drv_data);