xref: /openbmc/linux/drivers/nfc/s3fwrn5/firmware.c (revision 03ab8e6297acd1bc0eedaa050e2a1635c576fd11)
19dd0abd2SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2c04c674fSRobert Baldyga /*
3c04c674fSRobert Baldyga  * NCI based driver for Samsung S3FWRN5 NFC chip
4c04c674fSRobert Baldyga  *
5c04c674fSRobert Baldyga  * Copyright (C) 2015 Samsung Electrnoics
6c04c674fSRobert Baldyga  * Robert Baldyga <r.baldyga@samsung.com>
7c04c674fSRobert Baldyga  */
8c04c674fSRobert Baldyga 
9c04c674fSRobert Baldyga #include <linux/completion.h>
10c04c674fSRobert Baldyga #include <linux/firmware.h>
114a31340bSHerbert Xu #include <crypto/hash.h>
12a24d22b2SEric Biggers #include <crypto/sha1.h>
13c04c674fSRobert Baldyga 
14c04c674fSRobert Baldyga #include "s3fwrn5.h"
15c04c674fSRobert Baldyga #include "firmware.h"
16c04c674fSRobert Baldyga 
17c04c674fSRobert Baldyga struct s3fwrn5_fw_version {
18c04c674fSRobert Baldyga 	__u8 major;
19c04c674fSRobert Baldyga 	__u8 build1;
20c04c674fSRobert Baldyga 	__u8 build2;
21c04c674fSRobert Baldyga 	__u8 target;
22c04c674fSRobert Baldyga };
23c04c674fSRobert Baldyga 
s3fwrn5_fw_send_msg(struct s3fwrn5_fw_info * fw_info,struct sk_buff * msg,struct sk_buff ** rsp)24c04c674fSRobert Baldyga static int s3fwrn5_fw_send_msg(struct s3fwrn5_fw_info *fw_info,
25c04c674fSRobert Baldyga 	struct sk_buff *msg, struct sk_buff **rsp)
26c04c674fSRobert Baldyga {
27c04c674fSRobert Baldyga 	struct s3fwrn5_info *info =
28c04c674fSRobert Baldyga 		container_of(fw_info, struct s3fwrn5_info, fw_info);
29c04c674fSRobert Baldyga 	long ret;
30c04c674fSRobert Baldyga 
31c04c674fSRobert Baldyga 	reinit_completion(&fw_info->completion);
32c04c674fSRobert Baldyga 
33c04c674fSRobert Baldyga 	ret = s3fwrn5_write(info, msg);
34c04c674fSRobert Baldyga 	if (ret < 0)
35c04c674fSRobert Baldyga 		return ret;
36c04c674fSRobert Baldyga 
37c04c674fSRobert Baldyga 	ret = wait_for_completion_interruptible_timeout(
38c04c674fSRobert Baldyga 		&fw_info->completion, msecs_to_jiffies(1000));
39c04c674fSRobert Baldyga 	if (ret < 0)
40c04c674fSRobert Baldyga 		return ret;
41c04c674fSRobert Baldyga 	else if (ret == 0)
42c04c674fSRobert Baldyga 		return -ENXIO;
43c04c674fSRobert Baldyga 
44c04c674fSRobert Baldyga 	if (!fw_info->rsp)
45c04c674fSRobert Baldyga 		return -EINVAL;
46c04c674fSRobert Baldyga 
47c04c674fSRobert Baldyga 	*rsp = fw_info->rsp;
48c04c674fSRobert Baldyga 	fw_info->rsp = NULL;
49c04c674fSRobert Baldyga 
50c04c674fSRobert Baldyga 	return 0;
51c04c674fSRobert Baldyga }
52c04c674fSRobert Baldyga 
s3fwrn5_fw_prep_msg(struct s3fwrn5_fw_info * fw_info,struct sk_buff ** msg,u8 type,u8 code,const void * data,u16 len)53c04c674fSRobert Baldyga static int s3fwrn5_fw_prep_msg(struct s3fwrn5_fw_info *fw_info,
54c04c674fSRobert Baldyga 	struct sk_buff **msg, u8 type, u8 code, const void *data, u16 len)
55c04c674fSRobert Baldyga {
56c04c674fSRobert Baldyga 	struct s3fwrn5_fw_header hdr;
57c04c674fSRobert Baldyga 	struct sk_buff *skb;
58c04c674fSRobert Baldyga 
59c04c674fSRobert Baldyga 	hdr.type = type | fw_info->parity;
60c04c674fSRobert Baldyga 	fw_info->parity ^= 0x80;
61c04c674fSRobert Baldyga 	hdr.code = code;
62c04c674fSRobert Baldyga 	hdr.len = len;
63c04c674fSRobert Baldyga 
64c04c674fSRobert Baldyga 	skb = alloc_skb(S3FWRN5_FW_HDR_SIZE + len, GFP_KERNEL);
65c04c674fSRobert Baldyga 	if (!skb)
66c04c674fSRobert Baldyga 		return -ENOMEM;
67c04c674fSRobert Baldyga 
6859ae1d12SJohannes Berg 	skb_put_data(skb, &hdr, S3FWRN5_FW_HDR_SIZE);
69c04c674fSRobert Baldyga 	if (len)
7059ae1d12SJohannes Berg 		skb_put_data(skb, data, len);
71c04c674fSRobert Baldyga 
72c04c674fSRobert Baldyga 	*msg = skb;
73c04c674fSRobert Baldyga 
74c04c674fSRobert Baldyga 	return 0;
75c04c674fSRobert Baldyga }
76c04c674fSRobert Baldyga 
s3fwrn5_fw_get_bootinfo(struct s3fwrn5_fw_info * fw_info,struct s3fwrn5_fw_cmd_get_bootinfo_rsp * bootinfo)77c04c674fSRobert Baldyga static int s3fwrn5_fw_get_bootinfo(struct s3fwrn5_fw_info *fw_info,
78c04c674fSRobert Baldyga 	struct s3fwrn5_fw_cmd_get_bootinfo_rsp *bootinfo)
79c04c674fSRobert Baldyga {
80c04c674fSRobert Baldyga 	struct sk_buff *msg, *rsp = NULL;
81c04c674fSRobert Baldyga 	struct s3fwrn5_fw_header *hdr;
82c04c674fSRobert Baldyga 	int ret;
83c04c674fSRobert Baldyga 
84c04c674fSRobert Baldyga 	/* Send GET_BOOTINFO command */
85c04c674fSRobert Baldyga 
86c04c674fSRobert Baldyga 	ret = s3fwrn5_fw_prep_msg(fw_info, &msg, S3FWRN5_FW_MSG_CMD,
87c04c674fSRobert Baldyga 		S3FWRN5_FW_CMD_GET_BOOTINFO, NULL, 0);
88c04c674fSRobert Baldyga 	if (ret < 0)
89c04c674fSRobert Baldyga 		return ret;
90c04c674fSRobert Baldyga 
91c04c674fSRobert Baldyga 	ret = s3fwrn5_fw_send_msg(fw_info, msg, &rsp);
92c04c674fSRobert Baldyga 	kfree_skb(msg);
93c04c674fSRobert Baldyga 	if (ret < 0)
94c04c674fSRobert Baldyga 		return ret;
95c04c674fSRobert Baldyga 
96c04c674fSRobert Baldyga 	hdr = (struct s3fwrn5_fw_header *) rsp->data;
97c04c674fSRobert Baldyga 	if (hdr->code != S3FWRN5_FW_RET_SUCCESS) {
98c04c674fSRobert Baldyga 		ret = -EINVAL;
99c04c674fSRobert Baldyga 		goto out;
100c04c674fSRobert Baldyga 	}
101c04c674fSRobert Baldyga 
102c04c674fSRobert Baldyga 	memcpy(bootinfo, rsp->data + S3FWRN5_FW_HDR_SIZE, 10);
103c04c674fSRobert Baldyga 
104c04c674fSRobert Baldyga out:
105c04c674fSRobert Baldyga 	kfree_skb(rsp);
106c04c674fSRobert Baldyga 	return ret;
107c04c674fSRobert Baldyga }
108c04c674fSRobert Baldyga 
s3fwrn5_fw_enter_update_mode(struct s3fwrn5_fw_info * fw_info,const void * hash_data,u16 hash_size,const void * sig_data,u16 sig_size)109c04c674fSRobert Baldyga static int s3fwrn5_fw_enter_update_mode(struct s3fwrn5_fw_info *fw_info,
110c04c674fSRobert Baldyga 	const void *hash_data, u16 hash_size,
111c04c674fSRobert Baldyga 	const void *sig_data, u16 sig_size)
112c04c674fSRobert Baldyga {
113c04c674fSRobert Baldyga 	struct s3fwrn5_fw_cmd_enter_updatemode args;
114c04c674fSRobert Baldyga 	struct sk_buff *msg, *rsp = NULL;
115c04c674fSRobert Baldyga 	struct s3fwrn5_fw_header *hdr;
116c04c674fSRobert Baldyga 	int ret;
117c04c674fSRobert Baldyga 
118c04c674fSRobert Baldyga 	/* Send ENTER_UPDATE_MODE command */
119c04c674fSRobert Baldyga 
120c04c674fSRobert Baldyga 	args.hashcode_size = hash_size;
121c04c674fSRobert Baldyga 	args.signature_size = sig_size;
122c04c674fSRobert Baldyga 
123c04c674fSRobert Baldyga 	ret = s3fwrn5_fw_prep_msg(fw_info, &msg, S3FWRN5_FW_MSG_CMD,
124c04c674fSRobert Baldyga 		S3FWRN5_FW_CMD_ENTER_UPDATE_MODE, &args, sizeof(args));
125c04c674fSRobert Baldyga 	if (ret < 0)
126c04c674fSRobert Baldyga 		return ret;
127c04c674fSRobert Baldyga 
128c04c674fSRobert Baldyga 	ret = s3fwrn5_fw_send_msg(fw_info, msg, &rsp);
129c04c674fSRobert Baldyga 	kfree_skb(msg);
130c04c674fSRobert Baldyga 	if (ret < 0)
131c04c674fSRobert Baldyga 		return ret;
132c04c674fSRobert Baldyga 
133c04c674fSRobert Baldyga 	hdr = (struct s3fwrn5_fw_header *) rsp->data;
134c04c674fSRobert Baldyga 	if (hdr->code != S3FWRN5_FW_RET_SUCCESS) {
135c04c674fSRobert Baldyga 		ret = -EPROTO;
136c04c674fSRobert Baldyga 		goto out;
137c04c674fSRobert Baldyga 	}
138c04c674fSRobert Baldyga 
139c04c674fSRobert Baldyga 	kfree_skb(rsp);
140c04c674fSRobert Baldyga 
141c04c674fSRobert Baldyga 	/* Send hashcode data */
142c04c674fSRobert Baldyga 
143c04c674fSRobert Baldyga 	ret = s3fwrn5_fw_prep_msg(fw_info, &msg, S3FWRN5_FW_MSG_DATA, 0,
144c04c674fSRobert Baldyga 		hash_data, hash_size);
145c04c674fSRobert Baldyga 	if (ret < 0)
146c04c674fSRobert Baldyga 		return ret;
147c04c674fSRobert Baldyga 
148c04c674fSRobert Baldyga 	ret = s3fwrn5_fw_send_msg(fw_info, msg, &rsp);
149c04c674fSRobert Baldyga 	kfree_skb(msg);
150c04c674fSRobert Baldyga 	if (ret < 0)
151c04c674fSRobert Baldyga 		return ret;
152c04c674fSRobert Baldyga 
153c04c674fSRobert Baldyga 	hdr = (struct s3fwrn5_fw_header *) rsp->data;
154c04c674fSRobert Baldyga 	if (hdr->code != S3FWRN5_FW_RET_SUCCESS) {
155c04c674fSRobert Baldyga 		ret = -EPROTO;
156c04c674fSRobert Baldyga 		goto out;
157c04c674fSRobert Baldyga 	}
158c04c674fSRobert Baldyga 
159c04c674fSRobert Baldyga 	kfree_skb(rsp);
160c04c674fSRobert Baldyga 
161c04c674fSRobert Baldyga 	/* Send signature data */
162c04c674fSRobert Baldyga 
163c04c674fSRobert Baldyga 	ret = s3fwrn5_fw_prep_msg(fw_info, &msg, S3FWRN5_FW_MSG_DATA, 0,
164c04c674fSRobert Baldyga 		sig_data, sig_size);
165c04c674fSRobert Baldyga 	if (ret < 0)
166c04c674fSRobert Baldyga 		return ret;
167c04c674fSRobert Baldyga 
168c04c674fSRobert Baldyga 	ret = s3fwrn5_fw_send_msg(fw_info, msg, &rsp);
169c04c674fSRobert Baldyga 	kfree_skb(msg);
170c04c674fSRobert Baldyga 	if (ret < 0)
171c04c674fSRobert Baldyga 		return ret;
172c04c674fSRobert Baldyga 
173c04c674fSRobert Baldyga 	hdr = (struct s3fwrn5_fw_header *) rsp->data;
174c04c674fSRobert Baldyga 	if (hdr->code != S3FWRN5_FW_RET_SUCCESS)
175c04c674fSRobert Baldyga 		ret = -EPROTO;
176c04c674fSRobert Baldyga 
177c04c674fSRobert Baldyga out:
178c04c674fSRobert Baldyga 	kfree_skb(rsp);
179c04c674fSRobert Baldyga 	return ret;
180c04c674fSRobert Baldyga }
181c04c674fSRobert Baldyga 
s3fwrn5_fw_update_sector(struct s3fwrn5_fw_info * fw_info,u32 base_addr,const void * data)182c04c674fSRobert Baldyga static int s3fwrn5_fw_update_sector(struct s3fwrn5_fw_info *fw_info,
183c04c674fSRobert Baldyga 	u32 base_addr, const void *data)
184c04c674fSRobert Baldyga {
185c04c674fSRobert Baldyga 	struct s3fwrn5_fw_cmd_update_sector args;
186c04c674fSRobert Baldyga 	struct sk_buff *msg, *rsp = NULL;
187c04c674fSRobert Baldyga 	struct s3fwrn5_fw_header *hdr;
188c04c674fSRobert Baldyga 	int ret, i;
189c04c674fSRobert Baldyga 
190c04c674fSRobert Baldyga 	/* Send UPDATE_SECTOR command */
191c04c674fSRobert Baldyga 
192c04c674fSRobert Baldyga 	args.base_address = base_addr;
193c04c674fSRobert Baldyga 
194c04c674fSRobert Baldyga 	ret = s3fwrn5_fw_prep_msg(fw_info, &msg, S3FWRN5_FW_MSG_CMD,
195c04c674fSRobert Baldyga 		S3FWRN5_FW_CMD_UPDATE_SECTOR, &args, sizeof(args));
196c04c674fSRobert Baldyga 	if (ret < 0)
197c04c674fSRobert Baldyga 		return ret;
198c04c674fSRobert Baldyga 
199c04c674fSRobert Baldyga 	ret = s3fwrn5_fw_send_msg(fw_info, msg, &rsp);
200c04c674fSRobert Baldyga 	kfree_skb(msg);
201c04c674fSRobert Baldyga 	if (ret < 0)
202c04c674fSRobert Baldyga 		return ret;
203c04c674fSRobert Baldyga 
204c04c674fSRobert Baldyga 	hdr = (struct s3fwrn5_fw_header *) rsp->data;
205c04c674fSRobert Baldyga 	if (hdr->code != S3FWRN5_FW_RET_SUCCESS) {
206c04c674fSRobert Baldyga 		ret = -EPROTO;
207c04c674fSRobert Baldyga 		goto err;
208c04c674fSRobert Baldyga 	}
209c04c674fSRobert Baldyga 
210c04c674fSRobert Baldyga 	kfree_skb(rsp);
211c04c674fSRobert Baldyga 
212c04c674fSRobert Baldyga 	/* Send data split into 256-byte packets */
213c04c674fSRobert Baldyga 
214c04c674fSRobert Baldyga 	for (i = 0; i < 16; ++i) {
215c04c674fSRobert Baldyga 		ret = s3fwrn5_fw_prep_msg(fw_info, &msg,
216c04c674fSRobert Baldyga 			S3FWRN5_FW_MSG_DATA, 0, data+256*i, 256);
217c04c674fSRobert Baldyga 		if (ret < 0)
218c04c674fSRobert Baldyga 			break;
219c04c674fSRobert Baldyga 
220c04c674fSRobert Baldyga 		ret = s3fwrn5_fw_send_msg(fw_info, msg, &rsp);
221c04c674fSRobert Baldyga 		kfree_skb(msg);
222c04c674fSRobert Baldyga 		if (ret < 0)
223c04c674fSRobert Baldyga 			break;
224c04c674fSRobert Baldyga 
225c04c674fSRobert Baldyga 		hdr = (struct s3fwrn5_fw_header *) rsp->data;
226c04c674fSRobert Baldyga 		if (hdr->code != S3FWRN5_FW_RET_SUCCESS) {
227c04c674fSRobert Baldyga 			ret = -EPROTO;
228c04c674fSRobert Baldyga 			goto err;
229c04c674fSRobert Baldyga 		}
230c04c674fSRobert Baldyga 
231c04c674fSRobert Baldyga 		kfree_skb(rsp);
232c04c674fSRobert Baldyga 	}
233c04c674fSRobert Baldyga 
234c04c674fSRobert Baldyga 	return ret;
235c04c674fSRobert Baldyga 
236c04c674fSRobert Baldyga err:
237c04c674fSRobert Baldyga 	kfree_skb(rsp);
238c04c674fSRobert Baldyga 	return ret;
239c04c674fSRobert Baldyga }
240c04c674fSRobert Baldyga 
s3fwrn5_fw_complete_update_mode(struct s3fwrn5_fw_info * fw_info)241c04c674fSRobert Baldyga static int s3fwrn5_fw_complete_update_mode(struct s3fwrn5_fw_info *fw_info)
242c04c674fSRobert Baldyga {
243c04c674fSRobert Baldyga 	struct sk_buff *msg, *rsp = NULL;
244c04c674fSRobert Baldyga 	struct s3fwrn5_fw_header *hdr;
245c04c674fSRobert Baldyga 	int ret;
246c04c674fSRobert Baldyga 
247c04c674fSRobert Baldyga 	/* Send COMPLETE_UPDATE_MODE command */
248c04c674fSRobert Baldyga 
249c04c674fSRobert Baldyga 	ret = s3fwrn5_fw_prep_msg(fw_info, &msg, S3FWRN5_FW_MSG_CMD,
250c04c674fSRobert Baldyga 		S3FWRN5_FW_CMD_COMPLETE_UPDATE_MODE, NULL, 0);
251c04c674fSRobert Baldyga 	if (ret < 0)
252c04c674fSRobert Baldyga 		return ret;
253c04c674fSRobert Baldyga 
254c04c674fSRobert Baldyga 	ret = s3fwrn5_fw_send_msg(fw_info, msg, &rsp);
255c04c674fSRobert Baldyga 	kfree_skb(msg);
256c04c674fSRobert Baldyga 	if (ret < 0)
257c04c674fSRobert Baldyga 		return ret;
258c04c674fSRobert Baldyga 
259c04c674fSRobert Baldyga 	hdr = (struct s3fwrn5_fw_header *) rsp->data;
260c04c674fSRobert Baldyga 	if (hdr->code != S3FWRN5_FW_RET_SUCCESS)
261c04c674fSRobert Baldyga 		ret = -EPROTO;
262c04c674fSRobert Baldyga 
263c04c674fSRobert Baldyga 	kfree_skb(rsp);
264c04c674fSRobert Baldyga 
265c04c674fSRobert Baldyga 	return ret;
266c04c674fSRobert Baldyga }
267c04c674fSRobert Baldyga 
268c04c674fSRobert Baldyga /*
26907437600SBongsu Jeon  * Firmware header structure:
270c04c674fSRobert Baldyga  *
271c04c674fSRobert Baldyga  * 0x00 - 0x0B : Date and time string (w/o NUL termination)
272c04c674fSRobert Baldyga  * 0x10 - 0x13 : Firmware version
273c04c674fSRobert Baldyga  * 0x14 - 0x17 : Signature address
274c04c674fSRobert Baldyga  * 0x18 - 0x1B : Signature size
275c04c674fSRobert Baldyga  * 0x1C - 0x1F : Firmware image address
276c04c674fSRobert Baldyga  * 0x20 - 0x23 : Firmware sectors count
277c04c674fSRobert Baldyga  * 0x24 - 0x27 : Custom signature address
278c04c674fSRobert Baldyga  * 0x28 - 0x2B : Custom signature size
279c04c674fSRobert Baldyga  */
280c04c674fSRobert Baldyga 
281c04c674fSRobert Baldyga #define S3FWRN5_FW_IMAGE_HEADER_SIZE 44
282c04c674fSRobert Baldyga 
s3fwrn5_fw_request_firmware(struct s3fwrn5_fw_info * fw_info)2834fb7b98cSBongsu Jeon int s3fwrn5_fw_request_firmware(struct s3fwrn5_fw_info *fw_info)
284c04c674fSRobert Baldyga {
285c04c674fSRobert Baldyga 	struct s3fwrn5_fw_image *fw = &fw_info->fw;
286c04c674fSRobert Baldyga 	u32 sig_off;
287c04c674fSRobert Baldyga 	u32 image_off;
288c04c674fSRobert Baldyga 	u32 custom_sig_off;
289c04c674fSRobert Baldyga 	int ret;
290c04c674fSRobert Baldyga 
291c04c674fSRobert Baldyga 	ret = request_firmware(&fw->fw, fw_info->fw_name,
292c04c674fSRobert Baldyga 		&fw_info->ndev->nfc_dev->dev);
293c04c674fSRobert Baldyga 	if (ret < 0)
294c04c674fSRobert Baldyga 		return ret;
295c04c674fSRobert Baldyga 
296a4485baeSBongsu Jeon 	if (fw->fw->size < S3FWRN5_FW_IMAGE_HEADER_SIZE) {
297a4485baeSBongsu Jeon 		release_firmware(fw->fw);
298c04c674fSRobert Baldyga 		return -EINVAL;
299a4485baeSBongsu Jeon 	}
300c04c674fSRobert Baldyga 
301c04c674fSRobert Baldyga 	memcpy(fw->date, fw->fw->data + 0x00, 12);
302c04c674fSRobert Baldyga 	fw->date[12] = '\0';
303c04c674fSRobert Baldyga 
304c04c674fSRobert Baldyga 	memcpy(&fw->version, fw->fw->data + 0x10, 4);
305c04c674fSRobert Baldyga 
306c04c674fSRobert Baldyga 	memcpy(&sig_off, fw->fw->data + 0x14, 4);
307c04c674fSRobert Baldyga 	fw->sig = fw->fw->data + sig_off;
308c04c674fSRobert Baldyga 	memcpy(&fw->sig_size, fw->fw->data + 0x18, 4);
309c04c674fSRobert Baldyga 
310c04c674fSRobert Baldyga 	memcpy(&image_off, fw->fw->data + 0x1C, 4);
311c04c674fSRobert Baldyga 	fw->image = fw->fw->data + image_off;
312c04c674fSRobert Baldyga 	memcpy(&fw->image_sectors, fw->fw->data + 0x20, 4);
313c04c674fSRobert Baldyga 
314c04c674fSRobert Baldyga 	memcpy(&custom_sig_off, fw->fw->data + 0x24, 4);
315c04c674fSRobert Baldyga 	fw->custom_sig = fw->fw->data + custom_sig_off;
316c04c674fSRobert Baldyga 	memcpy(&fw->custom_sig_size, fw->fw->data + 0x28, 4);
317c04c674fSRobert Baldyga 
318c04c674fSRobert Baldyga 	return 0;
319c04c674fSRobert Baldyga }
320c04c674fSRobert Baldyga 
s3fwrn5_fw_release_firmware(struct s3fwrn5_fw_info * fw_info)321c04c674fSRobert Baldyga static void s3fwrn5_fw_release_firmware(struct s3fwrn5_fw_info *fw_info)
322c04c674fSRobert Baldyga {
323c04c674fSRobert Baldyga 	release_firmware(fw_info->fw.fw);
324c04c674fSRobert Baldyga }
325c04c674fSRobert Baldyga 
s3fwrn5_fw_get_base_addr(struct s3fwrn5_fw_cmd_get_bootinfo_rsp * bootinfo,u32 * base_addr)326c04c674fSRobert Baldyga static int s3fwrn5_fw_get_base_addr(
327c04c674fSRobert Baldyga 	struct s3fwrn5_fw_cmd_get_bootinfo_rsp *bootinfo, u32 *base_addr)
328c04c674fSRobert Baldyga {
329c04c674fSRobert Baldyga 	int i;
3305057f664SColin Ian King 	static const struct {
331c04c674fSRobert Baldyga 		u8 version[4];
332c04c674fSRobert Baldyga 		u32 base_addr;
333c04c674fSRobert Baldyga 	} match[] = {
334c04c674fSRobert Baldyga 		{{0x05, 0x00, 0x00, 0x00}, 0x00005000},
335c04c674fSRobert Baldyga 		{{0x05, 0x00, 0x00, 0x01}, 0x00003000},
336c04c674fSRobert Baldyga 		{{0x05, 0x00, 0x00, 0x02}, 0x00003000},
337c04c674fSRobert Baldyga 		{{0x05, 0x00, 0x00, 0x03}, 0x00003000},
338c04c674fSRobert Baldyga 		{{0x05, 0x00, 0x00, 0x05}, 0x00003000}
339c04c674fSRobert Baldyga 	};
340c04c674fSRobert Baldyga 
341c04c674fSRobert Baldyga 	for (i = 0; i < ARRAY_SIZE(match); ++i)
342c04c674fSRobert Baldyga 		if (bootinfo->hw_version[0] == match[i].version[0] &&
343c04c674fSRobert Baldyga 			bootinfo->hw_version[1] == match[i].version[1] &&
344c04c674fSRobert Baldyga 			bootinfo->hw_version[3] == match[i].version[3]) {
345c04c674fSRobert Baldyga 			*base_addr = match[i].base_addr;
346c04c674fSRobert Baldyga 			return 0;
347c04c674fSRobert Baldyga 		}
348c04c674fSRobert Baldyga 
349c04c674fSRobert Baldyga 	return -EINVAL;
350c04c674fSRobert Baldyga }
351c04c674fSRobert Baldyga 
352c04c674fSRobert Baldyga static inline bool
s3fwrn5_fw_is_custom(const struct s3fwrn5_fw_cmd_get_bootinfo_rsp * bootinfo)353171a7000SKrzysztof Kozlowski s3fwrn5_fw_is_custom(const struct s3fwrn5_fw_cmd_get_bootinfo_rsp *bootinfo)
354c04c674fSRobert Baldyga {
355c04c674fSRobert Baldyga 	return !!bootinfo->hw_version[2];
356c04c674fSRobert Baldyga }
357c04c674fSRobert Baldyga 
s3fwrn5_fw_setup(struct s3fwrn5_fw_info * fw_info)358c04c674fSRobert Baldyga int s3fwrn5_fw_setup(struct s3fwrn5_fw_info *fw_info)
359c04c674fSRobert Baldyga {
360*edfa5366SKrzysztof Kozlowski 	struct device *dev = &fw_info->ndev->nfc_dev->dev;
361c04c674fSRobert Baldyga 	struct s3fwrn5_fw_cmd_get_bootinfo_rsp bootinfo;
362c04c674fSRobert Baldyga 	int ret;
363c04c674fSRobert Baldyga 
364c04c674fSRobert Baldyga 	/* Get bootloader info */
365c04c674fSRobert Baldyga 
366c04c674fSRobert Baldyga 	ret = s3fwrn5_fw_get_bootinfo(fw_info, &bootinfo);
367c04c674fSRobert Baldyga 	if (ret < 0) {
368*edfa5366SKrzysztof Kozlowski 		dev_err(dev, "Failed to get bootinfo, ret=%02x\n", ret);
369c04c674fSRobert Baldyga 		goto err;
370c04c674fSRobert Baldyga 	}
371c04c674fSRobert Baldyga 
372c04c674fSRobert Baldyga 	/* Match hardware version to obtain firmware base address */
373c04c674fSRobert Baldyga 
374c04c674fSRobert Baldyga 	ret = s3fwrn5_fw_get_base_addr(&bootinfo, &fw_info->base_addr);
375c04c674fSRobert Baldyga 	if (ret < 0) {
376*edfa5366SKrzysztof Kozlowski 		dev_err(dev, "Unknown hardware version\n");
377c04c674fSRobert Baldyga 		goto err;
378c04c674fSRobert Baldyga 	}
379c04c674fSRobert Baldyga 
380c04c674fSRobert Baldyga 	fw_info->sector_size = bootinfo.sector_size;
381c04c674fSRobert Baldyga 
382c04c674fSRobert Baldyga 	fw_info->sig_size = s3fwrn5_fw_is_custom(&bootinfo) ?
383c04c674fSRobert Baldyga 		fw_info->fw.custom_sig_size : fw_info->fw.sig_size;
384c04c674fSRobert Baldyga 	fw_info->sig = s3fwrn5_fw_is_custom(&bootinfo) ?
385c04c674fSRobert Baldyga 		fw_info->fw.custom_sig : fw_info->fw.sig;
386c04c674fSRobert Baldyga 
387c04c674fSRobert Baldyga 	return 0;
388c04c674fSRobert Baldyga 
389c04c674fSRobert Baldyga err:
390c04c674fSRobert Baldyga 	s3fwrn5_fw_release_firmware(fw_info);
391c04c674fSRobert Baldyga 	return ret;
392c04c674fSRobert Baldyga }
393c04c674fSRobert Baldyga 
s3fwrn5_fw_check_version(const struct s3fwrn5_fw_info * fw_info,u32 version)394171a7000SKrzysztof Kozlowski bool s3fwrn5_fw_check_version(const struct s3fwrn5_fw_info *fw_info, u32 version)
395c04c674fSRobert Baldyga {
396c04c674fSRobert Baldyga 	struct s3fwrn5_fw_version *new = (void *) &fw_info->fw.version;
397c04c674fSRobert Baldyga 	struct s3fwrn5_fw_version *old = (void *) &version;
398c04c674fSRobert Baldyga 
399c04c674fSRobert Baldyga 	if (new->major > old->major)
400c04c674fSRobert Baldyga 		return true;
401c04c674fSRobert Baldyga 	if (new->build1 > old->build1)
402c04c674fSRobert Baldyga 		return true;
403c04c674fSRobert Baldyga 	if (new->build2 > old->build2)
404c04c674fSRobert Baldyga 		return true;
405c04c674fSRobert Baldyga 
406c04c674fSRobert Baldyga 	return false;
407c04c674fSRobert Baldyga }
408c04c674fSRobert Baldyga 
s3fwrn5_fw_download(struct s3fwrn5_fw_info * fw_info)409c04c674fSRobert Baldyga int s3fwrn5_fw_download(struct s3fwrn5_fw_info *fw_info)
410c04c674fSRobert Baldyga {
411*edfa5366SKrzysztof Kozlowski 	struct device *dev = &fw_info->ndev->nfc_dev->dev;
412c04c674fSRobert Baldyga 	struct s3fwrn5_fw_image *fw = &fw_info->fw;
413c04c674fSRobert Baldyga 	u8 hash_data[SHA1_DIGEST_SIZE];
4144a31340bSHerbert Xu 	struct crypto_shash *tfm;
415c04c674fSRobert Baldyga 	u32 image_size, off;
416c04c674fSRobert Baldyga 	int ret;
417c04c674fSRobert Baldyga 
418c04c674fSRobert Baldyga 	image_size = fw_info->sector_size * fw->image_sectors;
419c04c674fSRobert Baldyga 
420c04c674fSRobert Baldyga 	/* Compute SHA of firmware data */
421c04c674fSRobert Baldyga 
4224a31340bSHerbert Xu 	tfm = crypto_alloc_shash("sha1", 0, 0);
4234a31340bSHerbert Xu 	if (IS_ERR(tfm)) {
424*edfa5366SKrzysztof Kozlowski 		dev_err(dev, "Cannot allocate shash (code=%pe)\n", tfm);
425a0302ff5Swengjianfeng 		return PTR_ERR(tfm);
4264a31340bSHerbert Xu 	}
4274a31340bSHerbert Xu 
42896a5aa72SEric Biggers 	ret = crypto_shash_tfm_digest(tfm, fw->image, image_size, hash_data);
4294a31340bSHerbert Xu 
4304a31340bSHerbert Xu 	crypto_free_shash(tfm);
4314a31340bSHerbert Xu 	if (ret) {
432*edfa5366SKrzysztof Kozlowski 		dev_err(dev, "Cannot compute hash (code=%d)\n", ret);
433a0302ff5Swengjianfeng 		return ret;
4344a31340bSHerbert Xu 	}
435c04c674fSRobert Baldyga 
436c04c674fSRobert Baldyga 	/* Firmware update process */
437c04c674fSRobert Baldyga 
438*edfa5366SKrzysztof Kozlowski 	dev_info(dev, "Firmware update: %s\n", fw_info->fw_name);
439c04c674fSRobert Baldyga 
440c04c674fSRobert Baldyga 	ret = s3fwrn5_fw_enter_update_mode(fw_info, hash_data,
441c04c674fSRobert Baldyga 		SHA1_DIGEST_SIZE, fw_info->sig, fw_info->sig_size);
442c04c674fSRobert Baldyga 	if (ret < 0) {
443*edfa5366SKrzysztof Kozlowski 		dev_err(dev, "Unable to enter update mode\n");
444a0302ff5Swengjianfeng 		return ret;
445c04c674fSRobert Baldyga 	}
446c04c674fSRobert Baldyga 
447c04c674fSRobert Baldyga 	for (off = 0; off < image_size; off += fw_info->sector_size) {
448c04c674fSRobert Baldyga 		ret = s3fwrn5_fw_update_sector(fw_info,
449c04c674fSRobert Baldyga 			fw_info->base_addr + off, fw->image + off);
450c04c674fSRobert Baldyga 		if (ret < 0) {
451*edfa5366SKrzysztof Kozlowski 			dev_err(dev, "Firmware update error (code=%d)\n", ret);
452a0302ff5Swengjianfeng 			return ret;
453c04c674fSRobert Baldyga 		}
454c04c674fSRobert Baldyga 	}
455c04c674fSRobert Baldyga 
456c04c674fSRobert Baldyga 	ret = s3fwrn5_fw_complete_update_mode(fw_info);
457c04c674fSRobert Baldyga 	if (ret < 0) {
458*edfa5366SKrzysztof Kozlowski 		dev_err(dev, "Unable to complete update mode\n");
459a0302ff5Swengjianfeng 		return ret;
460c04c674fSRobert Baldyga 	}
461c04c674fSRobert Baldyga 
462*edfa5366SKrzysztof Kozlowski 	dev_info(dev, "Firmware update: success\n");
463c04c674fSRobert Baldyga 
464c04c674fSRobert Baldyga 	return ret;
465c04c674fSRobert Baldyga }
466c04c674fSRobert Baldyga 
s3fwrn5_fw_init(struct s3fwrn5_fw_info * fw_info,const char * fw_name)467c04c674fSRobert Baldyga void s3fwrn5_fw_init(struct s3fwrn5_fw_info *fw_info, const char *fw_name)
468c04c674fSRobert Baldyga {
469c04c674fSRobert Baldyga 	fw_info->parity = 0x00;
470c04c674fSRobert Baldyga 	fw_info->rsp = NULL;
471c04c674fSRobert Baldyga 	fw_info->fw.fw = NULL;
472c04c674fSRobert Baldyga 	strcpy(fw_info->fw_name, fw_name);
473c04c674fSRobert Baldyga 	init_completion(&fw_info->completion);
474c04c674fSRobert Baldyga }
475c04c674fSRobert Baldyga 
s3fwrn5_fw_cleanup(struct s3fwrn5_fw_info * fw_info)476c04c674fSRobert Baldyga void s3fwrn5_fw_cleanup(struct s3fwrn5_fw_info *fw_info)
477c04c674fSRobert Baldyga {
478c04c674fSRobert Baldyga 	s3fwrn5_fw_release_firmware(fw_info);
479c04c674fSRobert Baldyga }
480c04c674fSRobert Baldyga 
s3fwrn5_fw_recv_frame(struct nci_dev * ndev,struct sk_buff * skb)481c04c674fSRobert Baldyga int s3fwrn5_fw_recv_frame(struct nci_dev *ndev, struct sk_buff *skb)
482c04c674fSRobert Baldyga {
483c04c674fSRobert Baldyga 	struct s3fwrn5_info *info = nci_get_drvdata(ndev);
484c04c674fSRobert Baldyga 	struct s3fwrn5_fw_info *fw_info = &info->fw_info;
485c04c674fSRobert Baldyga 
486615f22f5SAditya Pakki 	if (WARN_ON(fw_info->rsp)) {
487615f22f5SAditya Pakki 		kfree_skb(skb);
488615f22f5SAditya Pakki 		return -EINVAL;
489615f22f5SAditya Pakki 	}
490c04c674fSRobert Baldyga 
491c04c674fSRobert Baldyga 	fw_info->rsp = skb;
492c04c674fSRobert Baldyga 
493c04c674fSRobert Baldyga 	complete(&fw_info->completion);
494c04c674fSRobert Baldyga 
495c04c674fSRobert Baldyga 	return 0;
496c04c674fSRobert Baldyga }
497