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