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