18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * NCI based driver for Samsung S3FWRN5 NFC chip
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2015 Samsung Electrnoics
68c2ecf20Sopenharmony_ci * Robert Baldyga <r.baldyga@samsung.com>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/completion.h>
108c2ecf20Sopenharmony_ci#include <linux/firmware.h>
118c2ecf20Sopenharmony_ci#include <crypto/hash.h>
128c2ecf20Sopenharmony_ci#include <crypto/sha.h>
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include "s3fwrn5.h"
158c2ecf20Sopenharmony_ci#include "firmware.h"
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_cistruct s3fwrn5_fw_version {
188c2ecf20Sopenharmony_ci	__u8 major;
198c2ecf20Sopenharmony_ci	__u8 build1;
208c2ecf20Sopenharmony_ci	__u8 build2;
218c2ecf20Sopenharmony_ci	__u8 target;
228c2ecf20Sopenharmony_ci};
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_cistatic int s3fwrn5_fw_send_msg(struct s3fwrn5_fw_info *fw_info,
258c2ecf20Sopenharmony_ci	struct sk_buff *msg, struct sk_buff **rsp)
268c2ecf20Sopenharmony_ci{
278c2ecf20Sopenharmony_ci	struct s3fwrn5_info *info =
288c2ecf20Sopenharmony_ci		container_of(fw_info, struct s3fwrn5_info, fw_info);
298c2ecf20Sopenharmony_ci	long ret;
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci	reinit_completion(&fw_info->completion);
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	ret = s3fwrn5_write(info, msg);
348c2ecf20Sopenharmony_ci	if (ret < 0)
358c2ecf20Sopenharmony_ci		return ret;
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	ret = wait_for_completion_interruptible_timeout(
388c2ecf20Sopenharmony_ci		&fw_info->completion, msecs_to_jiffies(1000));
398c2ecf20Sopenharmony_ci	if (ret < 0)
408c2ecf20Sopenharmony_ci		return ret;
418c2ecf20Sopenharmony_ci	else if (ret == 0)
428c2ecf20Sopenharmony_ci		return -ENXIO;
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	if (!fw_info->rsp)
458c2ecf20Sopenharmony_ci		return -EINVAL;
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	*rsp = fw_info->rsp;
488c2ecf20Sopenharmony_ci	fw_info->rsp = NULL;
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	return 0;
518c2ecf20Sopenharmony_ci}
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistatic int s3fwrn5_fw_prep_msg(struct s3fwrn5_fw_info *fw_info,
548c2ecf20Sopenharmony_ci	struct sk_buff **msg, u8 type, u8 code, const void *data, u16 len)
558c2ecf20Sopenharmony_ci{
568c2ecf20Sopenharmony_ci	struct s3fwrn5_fw_header hdr;
578c2ecf20Sopenharmony_ci	struct sk_buff *skb;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	hdr.type = type | fw_info->parity;
608c2ecf20Sopenharmony_ci	fw_info->parity ^= 0x80;
618c2ecf20Sopenharmony_ci	hdr.code = code;
628c2ecf20Sopenharmony_ci	hdr.len = len;
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	skb = alloc_skb(S3FWRN5_FW_HDR_SIZE + len, GFP_KERNEL);
658c2ecf20Sopenharmony_ci	if (!skb)
668c2ecf20Sopenharmony_ci		return -ENOMEM;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	skb_put_data(skb, &hdr, S3FWRN5_FW_HDR_SIZE);
698c2ecf20Sopenharmony_ci	if (len)
708c2ecf20Sopenharmony_ci		skb_put_data(skb, data, len);
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	*msg = skb;
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	return 0;
758c2ecf20Sopenharmony_ci}
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_cistatic int s3fwrn5_fw_get_bootinfo(struct s3fwrn5_fw_info *fw_info,
788c2ecf20Sopenharmony_ci	struct s3fwrn5_fw_cmd_get_bootinfo_rsp *bootinfo)
798c2ecf20Sopenharmony_ci{
808c2ecf20Sopenharmony_ci	struct sk_buff *msg, *rsp = NULL;
818c2ecf20Sopenharmony_ci	struct s3fwrn5_fw_header *hdr;
828c2ecf20Sopenharmony_ci	int ret;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	/* Send GET_BOOTINFO command */
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	ret = s3fwrn5_fw_prep_msg(fw_info, &msg, S3FWRN5_FW_MSG_CMD,
878c2ecf20Sopenharmony_ci		S3FWRN5_FW_CMD_GET_BOOTINFO, NULL, 0);
888c2ecf20Sopenharmony_ci	if (ret < 0)
898c2ecf20Sopenharmony_ci		return ret;
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	ret = s3fwrn5_fw_send_msg(fw_info, msg, &rsp);
928c2ecf20Sopenharmony_ci	kfree_skb(msg);
938c2ecf20Sopenharmony_ci	if (ret < 0)
948c2ecf20Sopenharmony_ci		return ret;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	hdr = (struct s3fwrn5_fw_header *) rsp->data;
978c2ecf20Sopenharmony_ci	if (hdr->code != S3FWRN5_FW_RET_SUCCESS) {
988c2ecf20Sopenharmony_ci		ret = -EINVAL;
998c2ecf20Sopenharmony_ci		goto out;
1008c2ecf20Sopenharmony_ci	}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	memcpy(bootinfo, rsp->data + S3FWRN5_FW_HDR_SIZE, 10);
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ciout:
1058c2ecf20Sopenharmony_ci	kfree_skb(rsp);
1068c2ecf20Sopenharmony_ci	return ret;
1078c2ecf20Sopenharmony_ci}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_cistatic int s3fwrn5_fw_enter_update_mode(struct s3fwrn5_fw_info *fw_info,
1108c2ecf20Sopenharmony_ci	const void *hash_data, u16 hash_size,
1118c2ecf20Sopenharmony_ci	const void *sig_data, u16 sig_size)
1128c2ecf20Sopenharmony_ci{
1138c2ecf20Sopenharmony_ci	struct s3fwrn5_fw_cmd_enter_updatemode args;
1148c2ecf20Sopenharmony_ci	struct sk_buff *msg, *rsp = NULL;
1158c2ecf20Sopenharmony_ci	struct s3fwrn5_fw_header *hdr;
1168c2ecf20Sopenharmony_ci	int ret;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	/* Send ENTER_UPDATE_MODE command */
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	args.hashcode_size = hash_size;
1218c2ecf20Sopenharmony_ci	args.signature_size = sig_size;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	ret = s3fwrn5_fw_prep_msg(fw_info, &msg, S3FWRN5_FW_MSG_CMD,
1248c2ecf20Sopenharmony_ci		S3FWRN5_FW_CMD_ENTER_UPDATE_MODE, &args, sizeof(args));
1258c2ecf20Sopenharmony_ci	if (ret < 0)
1268c2ecf20Sopenharmony_ci		return ret;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	ret = s3fwrn5_fw_send_msg(fw_info, msg, &rsp);
1298c2ecf20Sopenharmony_ci	kfree_skb(msg);
1308c2ecf20Sopenharmony_ci	if (ret < 0)
1318c2ecf20Sopenharmony_ci		return ret;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	hdr = (struct s3fwrn5_fw_header *) rsp->data;
1348c2ecf20Sopenharmony_ci	if (hdr->code != S3FWRN5_FW_RET_SUCCESS) {
1358c2ecf20Sopenharmony_ci		ret = -EPROTO;
1368c2ecf20Sopenharmony_ci		goto out;
1378c2ecf20Sopenharmony_ci	}
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	kfree_skb(rsp);
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	/* Send hashcode data */
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	ret = s3fwrn5_fw_prep_msg(fw_info, &msg, S3FWRN5_FW_MSG_DATA, 0,
1448c2ecf20Sopenharmony_ci		hash_data, hash_size);
1458c2ecf20Sopenharmony_ci	if (ret < 0)
1468c2ecf20Sopenharmony_ci		return ret;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	ret = s3fwrn5_fw_send_msg(fw_info, msg, &rsp);
1498c2ecf20Sopenharmony_ci	kfree_skb(msg);
1508c2ecf20Sopenharmony_ci	if (ret < 0)
1518c2ecf20Sopenharmony_ci		return ret;
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	hdr = (struct s3fwrn5_fw_header *) rsp->data;
1548c2ecf20Sopenharmony_ci	if (hdr->code != S3FWRN5_FW_RET_SUCCESS) {
1558c2ecf20Sopenharmony_ci		ret = -EPROTO;
1568c2ecf20Sopenharmony_ci		goto out;
1578c2ecf20Sopenharmony_ci	}
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	kfree_skb(rsp);
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	/* Send signature data */
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	ret = s3fwrn5_fw_prep_msg(fw_info, &msg, S3FWRN5_FW_MSG_DATA, 0,
1648c2ecf20Sopenharmony_ci		sig_data, sig_size);
1658c2ecf20Sopenharmony_ci	if (ret < 0)
1668c2ecf20Sopenharmony_ci		return ret;
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	ret = s3fwrn5_fw_send_msg(fw_info, msg, &rsp);
1698c2ecf20Sopenharmony_ci	kfree_skb(msg);
1708c2ecf20Sopenharmony_ci	if (ret < 0)
1718c2ecf20Sopenharmony_ci		return ret;
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	hdr = (struct s3fwrn5_fw_header *) rsp->data;
1748c2ecf20Sopenharmony_ci	if (hdr->code != S3FWRN5_FW_RET_SUCCESS)
1758c2ecf20Sopenharmony_ci		ret = -EPROTO;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ciout:
1788c2ecf20Sopenharmony_ci	kfree_skb(rsp);
1798c2ecf20Sopenharmony_ci	return ret;
1808c2ecf20Sopenharmony_ci}
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_cistatic int s3fwrn5_fw_update_sector(struct s3fwrn5_fw_info *fw_info,
1838c2ecf20Sopenharmony_ci	u32 base_addr, const void *data)
1848c2ecf20Sopenharmony_ci{
1858c2ecf20Sopenharmony_ci	struct s3fwrn5_fw_cmd_update_sector args;
1868c2ecf20Sopenharmony_ci	struct sk_buff *msg, *rsp = NULL;
1878c2ecf20Sopenharmony_ci	struct s3fwrn5_fw_header *hdr;
1888c2ecf20Sopenharmony_ci	int ret, i;
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	/* Send UPDATE_SECTOR command */
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	args.base_address = base_addr;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	ret = s3fwrn5_fw_prep_msg(fw_info, &msg, S3FWRN5_FW_MSG_CMD,
1958c2ecf20Sopenharmony_ci		S3FWRN5_FW_CMD_UPDATE_SECTOR, &args, sizeof(args));
1968c2ecf20Sopenharmony_ci	if (ret < 0)
1978c2ecf20Sopenharmony_ci		return ret;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	ret = s3fwrn5_fw_send_msg(fw_info, msg, &rsp);
2008c2ecf20Sopenharmony_ci	kfree_skb(msg);
2018c2ecf20Sopenharmony_ci	if (ret < 0)
2028c2ecf20Sopenharmony_ci		return ret;
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	hdr = (struct s3fwrn5_fw_header *) rsp->data;
2058c2ecf20Sopenharmony_ci	if (hdr->code != S3FWRN5_FW_RET_SUCCESS) {
2068c2ecf20Sopenharmony_ci		ret = -EPROTO;
2078c2ecf20Sopenharmony_ci		goto err;
2088c2ecf20Sopenharmony_ci	}
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	kfree_skb(rsp);
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	/* Send data split into 256-byte packets */
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	for (i = 0; i < 16; ++i) {
2158c2ecf20Sopenharmony_ci		ret = s3fwrn5_fw_prep_msg(fw_info, &msg,
2168c2ecf20Sopenharmony_ci			S3FWRN5_FW_MSG_DATA, 0, data+256*i, 256);
2178c2ecf20Sopenharmony_ci		if (ret < 0)
2188c2ecf20Sopenharmony_ci			break;
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci		ret = s3fwrn5_fw_send_msg(fw_info, msg, &rsp);
2218c2ecf20Sopenharmony_ci		kfree_skb(msg);
2228c2ecf20Sopenharmony_ci		if (ret < 0)
2238c2ecf20Sopenharmony_ci			break;
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci		hdr = (struct s3fwrn5_fw_header *) rsp->data;
2268c2ecf20Sopenharmony_ci		if (hdr->code != S3FWRN5_FW_RET_SUCCESS) {
2278c2ecf20Sopenharmony_ci			ret = -EPROTO;
2288c2ecf20Sopenharmony_ci			goto err;
2298c2ecf20Sopenharmony_ci		}
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci		kfree_skb(rsp);
2328c2ecf20Sopenharmony_ci	}
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	return ret;
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_cierr:
2378c2ecf20Sopenharmony_ci	kfree_skb(rsp);
2388c2ecf20Sopenharmony_ci	return ret;
2398c2ecf20Sopenharmony_ci}
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_cistatic int s3fwrn5_fw_complete_update_mode(struct s3fwrn5_fw_info *fw_info)
2428c2ecf20Sopenharmony_ci{
2438c2ecf20Sopenharmony_ci	struct sk_buff *msg, *rsp = NULL;
2448c2ecf20Sopenharmony_ci	struct s3fwrn5_fw_header *hdr;
2458c2ecf20Sopenharmony_ci	int ret;
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	/* Send COMPLETE_UPDATE_MODE command */
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	ret = s3fwrn5_fw_prep_msg(fw_info, &msg, S3FWRN5_FW_MSG_CMD,
2508c2ecf20Sopenharmony_ci		S3FWRN5_FW_CMD_COMPLETE_UPDATE_MODE, NULL, 0);
2518c2ecf20Sopenharmony_ci	if (ret < 0)
2528c2ecf20Sopenharmony_ci		return ret;
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	ret = s3fwrn5_fw_send_msg(fw_info, msg, &rsp);
2558c2ecf20Sopenharmony_ci	kfree_skb(msg);
2568c2ecf20Sopenharmony_ci	if (ret < 0)
2578c2ecf20Sopenharmony_ci		return ret;
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	hdr = (struct s3fwrn5_fw_header *) rsp->data;
2608c2ecf20Sopenharmony_ci	if (hdr->code != S3FWRN5_FW_RET_SUCCESS)
2618c2ecf20Sopenharmony_ci		ret = -EPROTO;
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	kfree_skb(rsp);
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	return ret;
2668c2ecf20Sopenharmony_ci}
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci/*
2698c2ecf20Sopenharmony_ci * Firmware header stucture:
2708c2ecf20Sopenharmony_ci *
2718c2ecf20Sopenharmony_ci * 0x00 - 0x0B : Date and time string (w/o NUL termination)
2728c2ecf20Sopenharmony_ci * 0x10 - 0x13 : Firmware version
2738c2ecf20Sopenharmony_ci * 0x14 - 0x17 : Signature address
2748c2ecf20Sopenharmony_ci * 0x18 - 0x1B : Signature size
2758c2ecf20Sopenharmony_ci * 0x1C - 0x1F : Firmware image address
2768c2ecf20Sopenharmony_ci * 0x20 - 0x23 : Firmware sectors count
2778c2ecf20Sopenharmony_ci * 0x24 - 0x27 : Custom signature address
2788c2ecf20Sopenharmony_ci * 0x28 - 0x2B : Custom signature size
2798c2ecf20Sopenharmony_ci */
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci#define S3FWRN5_FW_IMAGE_HEADER_SIZE 44
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_cistatic int s3fwrn5_fw_request_firmware(struct s3fwrn5_fw_info *fw_info)
2848c2ecf20Sopenharmony_ci{
2858c2ecf20Sopenharmony_ci	struct s3fwrn5_fw_image *fw = &fw_info->fw;
2868c2ecf20Sopenharmony_ci	u32 sig_off;
2878c2ecf20Sopenharmony_ci	u32 image_off;
2888c2ecf20Sopenharmony_ci	u32 custom_sig_off;
2898c2ecf20Sopenharmony_ci	int ret;
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	ret = request_firmware(&fw->fw, fw_info->fw_name,
2928c2ecf20Sopenharmony_ci		&fw_info->ndev->nfc_dev->dev);
2938c2ecf20Sopenharmony_ci	if (ret < 0)
2948c2ecf20Sopenharmony_ci		return ret;
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	if (fw->fw->size < S3FWRN5_FW_IMAGE_HEADER_SIZE) {
2978c2ecf20Sopenharmony_ci		release_firmware(fw->fw);
2988c2ecf20Sopenharmony_ci		return -EINVAL;
2998c2ecf20Sopenharmony_ci	}
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	memcpy(fw->date, fw->fw->data + 0x00, 12);
3028c2ecf20Sopenharmony_ci	fw->date[12] = '\0';
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	memcpy(&fw->version, fw->fw->data + 0x10, 4);
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	memcpy(&sig_off, fw->fw->data + 0x14, 4);
3078c2ecf20Sopenharmony_ci	fw->sig = fw->fw->data + sig_off;
3088c2ecf20Sopenharmony_ci	memcpy(&fw->sig_size, fw->fw->data + 0x18, 4);
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	memcpy(&image_off, fw->fw->data + 0x1C, 4);
3118c2ecf20Sopenharmony_ci	fw->image = fw->fw->data + image_off;
3128c2ecf20Sopenharmony_ci	memcpy(&fw->image_sectors, fw->fw->data + 0x20, 4);
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	memcpy(&custom_sig_off, fw->fw->data + 0x24, 4);
3158c2ecf20Sopenharmony_ci	fw->custom_sig = fw->fw->data + custom_sig_off;
3168c2ecf20Sopenharmony_ci	memcpy(&fw->custom_sig_size, fw->fw->data + 0x28, 4);
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	return 0;
3198c2ecf20Sopenharmony_ci}
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_cistatic void s3fwrn5_fw_release_firmware(struct s3fwrn5_fw_info *fw_info)
3228c2ecf20Sopenharmony_ci{
3238c2ecf20Sopenharmony_ci	release_firmware(fw_info->fw.fw);
3248c2ecf20Sopenharmony_ci}
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_cistatic int s3fwrn5_fw_get_base_addr(
3278c2ecf20Sopenharmony_ci	struct s3fwrn5_fw_cmd_get_bootinfo_rsp *bootinfo, u32 *base_addr)
3288c2ecf20Sopenharmony_ci{
3298c2ecf20Sopenharmony_ci	int i;
3308c2ecf20Sopenharmony_ci	static const struct {
3318c2ecf20Sopenharmony_ci		u8 version[4];
3328c2ecf20Sopenharmony_ci		u32 base_addr;
3338c2ecf20Sopenharmony_ci	} match[] = {
3348c2ecf20Sopenharmony_ci		{{0x05, 0x00, 0x00, 0x00}, 0x00005000},
3358c2ecf20Sopenharmony_ci		{{0x05, 0x00, 0x00, 0x01}, 0x00003000},
3368c2ecf20Sopenharmony_ci		{{0x05, 0x00, 0x00, 0x02}, 0x00003000},
3378c2ecf20Sopenharmony_ci		{{0x05, 0x00, 0x00, 0x03}, 0x00003000},
3388c2ecf20Sopenharmony_ci		{{0x05, 0x00, 0x00, 0x05}, 0x00003000}
3398c2ecf20Sopenharmony_ci	};
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(match); ++i)
3428c2ecf20Sopenharmony_ci		if (bootinfo->hw_version[0] == match[i].version[0] &&
3438c2ecf20Sopenharmony_ci			bootinfo->hw_version[1] == match[i].version[1] &&
3448c2ecf20Sopenharmony_ci			bootinfo->hw_version[3] == match[i].version[3]) {
3458c2ecf20Sopenharmony_ci			*base_addr = match[i].base_addr;
3468c2ecf20Sopenharmony_ci			return 0;
3478c2ecf20Sopenharmony_ci		}
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	return -EINVAL;
3508c2ecf20Sopenharmony_ci}
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_cistatic inline bool
3538c2ecf20Sopenharmony_cis3fwrn5_fw_is_custom(const struct s3fwrn5_fw_cmd_get_bootinfo_rsp *bootinfo)
3548c2ecf20Sopenharmony_ci{
3558c2ecf20Sopenharmony_ci	return !!bootinfo->hw_version[2];
3568c2ecf20Sopenharmony_ci}
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ciint s3fwrn5_fw_setup(struct s3fwrn5_fw_info *fw_info)
3598c2ecf20Sopenharmony_ci{
3608c2ecf20Sopenharmony_ci	struct s3fwrn5_fw_cmd_get_bootinfo_rsp bootinfo;
3618c2ecf20Sopenharmony_ci	int ret;
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci	/* Get firmware data */
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	ret = s3fwrn5_fw_request_firmware(fw_info);
3668c2ecf20Sopenharmony_ci	if (ret < 0) {
3678c2ecf20Sopenharmony_ci		dev_err(&fw_info->ndev->nfc_dev->dev,
3688c2ecf20Sopenharmony_ci			"Failed to get fw file, ret=%02x\n", ret);
3698c2ecf20Sopenharmony_ci		return ret;
3708c2ecf20Sopenharmony_ci	}
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	/* Get bootloader info */
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	ret = s3fwrn5_fw_get_bootinfo(fw_info, &bootinfo);
3758c2ecf20Sopenharmony_ci	if (ret < 0) {
3768c2ecf20Sopenharmony_ci		dev_err(&fw_info->ndev->nfc_dev->dev,
3778c2ecf20Sopenharmony_ci			"Failed to get bootinfo, ret=%02x\n", ret);
3788c2ecf20Sopenharmony_ci		goto err;
3798c2ecf20Sopenharmony_ci	}
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	/* Match hardware version to obtain firmware base address */
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	ret = s3fwrn5_fw_get_base_addr(&bootinfo, &fw_info->base_addr);
3848c2ecf20Sopenharmony_ci	if (ret < 0) {
3858c2ecf20Sopenharmony_ci		dev_err(&fw_info->ndev->nfc_dev->dev,
3868c2ecf20Sopenharmony_ci			"Unknown hardware version\n");
3878c2ecf20Sopenharmony_ci		goto err;
3888c2ecf20Sopenharmony_ci	}
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	fw_info->sector_size = bootinfo.sector_size;
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	fw_info->sig_size = s3fwrn5_fw_is_custom(&bootinfo) ?
3938c2ecf20Sopenharmony_ci		fw_info->fw.custom_sig_size : fw_info->fw.sig_size;
3948c2ecf20Sopenharmony_ci	fw_info->sig = s3fwrn5_fw_is_custom(&bootinfo) ?
3958c2ecf20Sopenharmony_ci		fw_info->fw.custom_sig : fw_info->fw.sig;
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	return 0;
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_cierr:
4008c2ecf20Sopenharmony_ci	s3fwrn5_fw_release_firmware(fw_info);
4018c2ecf20Sopenharmony_ci	return ret;
4028c2ecf20Sopenharmony_ci}
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_cibool s3fwrn5_fw_check_version(const struct s3fwrn5_fw_info *fw_info, u32 version)
4058c2ecf20Sopenharmony_ci{
4068c2ecf20Sopenharmony_ci	struct s3fwrn5_fw_version *new = (void *) &fw_info->fw.version;
4078c2ecf20Sopenharmony_ci	struct s3fwrn5_fw_version *old = (void *) &version;
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	if (new->major > old->major)
4108c2ecf20Sopenharmony_ci		return true;
4118c2ecf20Sopenharmony_ci	if (new->build1 > old->build1)
4128c2ecf20Sopenharmony_ci		return true;
4138c2ecf20Sopenharmony_ci	if (new->build2 > old->build2)
4148c2ecf20Sopenharmony_ci		return true;
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	return false;
4178c2ecf20Sopenharmony_ci}
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ciint s3fwrn5_fw_download(struct s3fwrn5_fw_info *fw_info)
4208c2ecf20Sopenharmony_ci{
4218c2ecf20Sopenharmony_ci	struct s3fwrn5_fw_image *fw = &fw_info->fw;
4228c2ecf20Sopenharmony_ci	u8 hash_data[SHA1_DIGEST_SIZE];
4238c2ecf20Sopenharmony_ci	struct crypto_shash *tfm;
4248c2ecf20Sopenharmony_ci	u32 image_size, off;
4258c2ecf20Sopenharmony_ci	int ret;
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	image_size = fw_info->sector_size * fw->image_sectors;
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	/* Compute SHA of firmware data */
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	tfm = crypto_alloc_shash("sha1", 0, 0);
4328c2ecf20Sopenharmony_ci	if (IS_ERR(tfm)) {
4338c2ecf20Sopenharmony_ci		ret = PTR_ERR(tfm);
4348c2ecf20Sopenharmony_ci		dev_err(&fw_info->ndev->nfc_dev->dev,
4358c2ecf20Sopenharmony_ci			"Cannot allocate shash (code=%d)\n", ret);
4368c2ecf20Sopenharmony_ci		goto out;
4378c2ecf20Sopenharmony_ci	}
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	ret = crypto_shash_tfm_digest(tfm, fw->image, image_size, hash_data);
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	crypto_free_shash(tfm);
4428c2ecf20Sopenharmony_ci	if (ret) {
4438c2ecf20Sopenharmony_ci		dev_err(&fw_info->ndev->nfc_dev->dev,
4448c2ecf20Sopenharmony_ci			"Cannot compute hash (code=%d)\n", ret);
4458c2ecf20Sopenharmony_ci		goto out;
4468c2ecf20Sopenharmony_ci	}
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	/* Firmware update process */
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	dev_info(&fw_info->ndev->nfc_dev->dev,
4518c2ecf20Sopenharmony_ci		"Firmware update: %s\n", fw_info->fw_name);
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	ret = s3fwrn5_fw_enter_update_mode(fw_info, hash_data,
4548c2ecf20Sopenharmony_ci		SHA1_DIGEST_SIZE, fw_info->sig, fw_info->sig_size);
4558c2ecf20Sopenharmony_ci	if (ret < 0) {
4568c2ecf20Sopenharmony_ci		dev_err(&fw_info->ndev->nfc_dev->dev,
4578c2ecf20Sopenharmony_ci			"Unable to enter update mode\n");
4588c2ecf20Sopenharmony_ci		goto out;
4598c2ecf20Sopenharmony_ci	}
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	for (off = 0; off < image_size; off += fw_info->sector_size) {
4628c2ecf20Sopenharmony_ci		ret = s3fwrn5_fw_update_sector(fw_info,
4638c2ecf20Sopenharmony_ci			fw_info->base_addr + off, fw->image + off);
4648c2ecf20Sopenharmony_ci		if (ret < 0) {
4658c2ecf20Sopenharmony_ci			dev_err(&fw_info->ndev->nfc_dev->dev,
4668c2ecf20Sopenharmony_ci				"Firmware update error (code=%d)\n", ret);
4678c2ecf20Sopenharmony_ci			goto out;
4688c2ecf20Sopenharmony_ci		}
4698c2ecf20Sopenharmony_ci	}
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci	ret = s3fwrn5_fw_complete_update_mode(fw_info);
4728c2ecf20Sopenharmony_ci	if (ret < 0) {
4738c2ecf20Sopenharmony_ci		dev_err(&fw_info->ndev->nfc_dev->dev,
4748c2ecf20Sopenharmony_ci			"Unable to complete update mode\n");
4758c2ecf20Sopenharmony_ci		goto out;
4768c2ecf20Sopenharmony_ci	}
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	dev_info(&fw_info->ndev->nfc_dev->dev,
4798c2ecf20Sopenharmony_ci		"Firmware update: success\n");
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ciout:
4828c2ecf20Sopenharmony_ci	return ret;
4838c2ecf20Sopenharmony_ci}
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_civoid s3fwrn5_fw_init(struct s3fwrn5_fw_info *fw_info, const char *fw_name)
4868c2ecf20Sopenharmony_ci{
4878c2ecf20Sopenharmony_ci	fw_info->parity = 0x00;
4888c2ecf20Sopenharmony_ci	fw_info->rsp = NULL;
4898c2ecf20Sopenharmony_ci	fw_info->fw.fw = NULL;
4908c2ecf20Sopenharmony_ci	strcpy(fw_info->fw_name, fw_name);
4918c2ecf20Sopenharmony_ci	init_completion(&fw_info->completion);
4928c2ecf20Sopenharmony_ci}
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_civoid s3fwrn5_fw_cleanup(struct s3fwrn5_fw_info *fw_info)
4958c2ecf20Sopenharmony_ci{
4968c2ecf20Sopenharmony_ci	s3fwrn5_fw_release_firmware(fw_info);
4978c2ecf20Sopenharmony_ci}
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ciint s3fwrn5_fw_recv_frame(struct nci_dev *ndev, struct sk_buff *skb)
5008c2ecf20Sopenharmony_ci{
5018c2ecf20Sopenharmony_ci	struct s3fwrn5_info *info = nci_get_drvdata(ndev);
5028c2ecf20Sopenharmony_ci	struct s3fwrn5_fw_info *fw_info = &info->fw_info;
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci	if (WARN_ON(fw_info->rsp)) {
5058c2ecf20Sopenharmony_ci		kfree_skb(skb);
5068c2ecf20Sopenharmony_ci		return -EINVAL;
5078c2ecf20Sopenharmony_ci	}
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	fw_info->rsp = skb;
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	complete(&fw_info->completion);
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	return 0;
5148c2ecf20Sopenharmony_ci}
515