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