162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * NCI based driver for Samsung S3FWRN5 NFC chip 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2015 Samsung Electrnoics 662306a36Sopenharmony_ci * Robert Baldyga <r.baldyga@samsung.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/completion.h> 1062306a36Sopenharmony_ci#include <linux/firmware.h> 1162306a36Sopenharmony_ci#include <crypto/hash.h> 1262306a36Sopenharmony_ci#include <crypto/sha1.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include "s3fwrn5.h" 1562306a36Sopenharmony_ci#include "firmware.h" 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_cistruct s3fwrn5_fw_version { 1862306a36Sopenharmony_ci __u8 major; 1962306a36Sopenharmony_ci __u8 build1; 2062306a36Sopenharmony_ci __u8 build2; 2162306a36Sopenharmony_ci __u8 target; 2262306a36Sopenharmony_ci}; 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cistatic int s3fwrn5_fw_send_msg(struct s3fwrn5_fw_info *fw_info, 2562306a36Sopenharmony_ci struct sk_buff *msg, struct sk_buff **rsp) 2662306a36Sopenharmony_ci{ 2762306a36Sopenharmony_ci struct s3fwrn5_info *info = 2862306a36Sopenharmony_ci container_of(fw_info, struct s3fwrn5_info, fw_info); 2962306a36Sopenharmony_ci long ret; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci reinit_completion(&fw_info->completion); 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci ret = s3fwrn5_write(info, msg); 3462306a36Sopenharmony_ci if (ret < 0) 3562306a36Sopenharmony_ci return ret; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci ret = wait_for_completion_interruptible_timeout( 3862306a36Sopenharmony_ci &fw_info->completion, msecs_to_jiffies(1000)); 3962306a36Sopenharmony_ci if (ret < 0) 4062306a36Sopenharmony_ci return ret; 4162306a36Sopenharmony_ci else if (ret == 0) 4262306a36Sopenharmony_ci return -ENXIO; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci if (!fw_info->rsp) 4562306a36Sopenharmony_ci return -EINVAL; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci *rsp = fw_info->rsp; 4862306a36Sopenharmony_ci fw_info->rsp = NULL; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci return 0; 5162306a36Sopenharmony_ci} 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistatic int s3fwrn5_fw_prep_msg(struct s3fwrn5_fw_info *fw_info, 5462306a36Sopenharmony_ci struct sk_buff **msg, u8 type, u8 code, const void *data, u16 len) 5562306a36Sopenharmony_ci{ 5662306a36Sopenharmony_ci struct s3fwrn5_fw_header hdr; 5762306a36Sopenharmony_ci struct sk_buff *skb; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci hdr.type = type | fw_info->parity; 6062306a36Sopenharmony_ci fw_info->parity ^= 0x80; 6162306a36Sopenharmony_ci hdr.code = code; 6262306a36Sopenharmony_ci hdr.len = len; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci skb = alloc_skb(S3FWRN5_FW_HDR_SIZE + len, GFP_KERNEL); 6562306a36Sopenharmony_ci if (!skb) 6662306a36Sopenharmony_ci return -ENOMEM; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci skb_put_data(skb, &hdr, S3FWRN5_FW_HDR_SIZE); 6962306a36Sopenharmony_ci if (len) 7062306a36Sopenharmony_ci skb_put_data(skb, data, len); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci *msg = skb; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci return 0; 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic int s3fwrn5_fw_get_bootinfo(struct s3fwrn5_fw_info *fw_info, 7862306a36Sopenharmony_ci struct s3fwrn5_fw_cmd_get_bootinfo_rsp *bootinfo) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci struct sk_buff *msg, *rsp = NULL; 8162306a36Sopenharmony_ci struct s3fwrn5_fw_header *hdr; 8262306a36Sopenharmony_ci int ret; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci /* Send GET_BOOTINFO command */ 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci ret = s3fwrn5_fw_prep_msg(fw_info, &msg, S3FWRN5_FW_MSG_CMD, 8762306a36Sopenharmony_ci S3FWRN5_FW_CMD_GET_BOOTINFO, NULL, 0); 8862306a36Sopenharmony_ci if (ret < 0) 8962306a36Sopenharmony_ci return ret; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci ret = s3fwrn5_fw_send_msg(fw_info, msg, &rsp); 9262306a36Sopenharmony_ci kfree_skb(msg); 9362306a36Sopenharmony_ci if (ret < 0) 9462306a36Sopenharmony_ci return ret; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci hdr = (struct s3fwrn5_fw_header *) rsp->data; 9762306a36Sopenharmony_ci if (hdr->code != S3FWRN5_FW_RET_SUCCESS) { 9862306a36Sopenharmony_ci ret = -EINVAL; 9962306a36Sopenharmony_ci goto out; 10062306a36Sopenharmony_ci } 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci memcpy(bootinfo, rsp->data + S3FWRN5_FW_HDR_SIZE, 10); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ciout: 10562306a36Sopenharmony_ci kfree_skb(rsp); 10662306a36Sopenharmony_ci return ret; 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_cistatic int s3fwrn5_fw_enter_update_mode(struct s3fwrn5_fw_info *fw_info, 11062306a36Sopenharmony_ci const void *hash_data, u16 hash_size, 11162306a36Sopenharmony_ci const void *sig_data, u16 sig_size) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci struct s3fwrn5_fw_cmd_enter_updatemode args; 11462306a36Sopenharmony_ci struct sk_buff *msg, *rsp = NULL; 11562306a36Sopenharmony_ci struct s3fwrn5_fw_header *hdr; 11662306a36Sopenharmony_ci int ret; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci /* Send ENTER_UPDATE_MODE command */ 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci args.hashcode_size = hash_size; 12162306a36Sopenharmony_ci args.signature_size = sig_size; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci ret = s3fwrn5_fw_prep_msg(fw_info, &msg, S3FWRN5_FW_MSG_CMD, 12462306a36Sopenharmony_ci S3FWRN5_FW_CMD_ENTER_UPDATE_MODE, &args, sizeof(args)); 12562306a36Sopenharmony_ci if (ret < 0) 12662306a36Sopenharmony_ci return ret; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci ret = s3fwrn5_fw_send_msg(fw_info, msg, &rsp); 12962306a36Sopenharmony_ci kfree_skb(msg); 13062306a36Sopenharmony_ci if (ret < 0) 13162306a36Sopenharmony_ci return ret; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci hdr = (struct s3fwrn5_fw_header *) rsp->data; 13462306a36Sopenharmony_ci if (hdr->code != S3FWRN5_FW_RET_SUCCESS) { 13562306a36Sopenharmony_ci ret = -EPROTO; 13662306a36Sopenharmony_ci goto out; 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci kfree_skb(rsp); 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci /* Send hashcode data */ 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci ret = s3fwrn5_fw_prep_msg(fw_info, &msg, S3FWRN5_FW_MSG_DATA, 0, 14462306a36Sopenharmony_ci hash_data, hash_size); 14562306a36Sopenharmony_ci if (ret < 0) 14662306a36Sopenharmony_ci return ret; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci ret = s3fwrn5_fw_send_msg(fw_info, msg, &rsp); 14962306a36Sopenharmony_ci kfree_skb(msg); 15062306a36Sopenharmony_ci if (ret < 0) 15162306a36Sopenharmony_ci return ret; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci hdr = (struct s3fwrn5_fw_header *) rsp->data; 15462306a36Sopenharmony_ci if (hdr->code != S3FWRN5_FW_RET_SUCCESS) { 15562306a36Sopenharmony_ci ret = -EPROTO; 15662306a36Sopenharmony_ci goto out; 15762306a36Sopenharmony_ci } 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci kfree_skb(rsp); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci /* Send signature data */ 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci ret = s3fwrn5_fw_prep_msg(fw_info, &msg, S3FWRN5_FW_MSG_DATA, 0, 16462306a36Sopenharmony_ci sig_data, sig_size); 16562306a36Sopenharmony_ci if (ret < 0) 16662306a36Sopenharmony_ci return ret; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci ret = s3fwrn5_fw_send_msg(fw_info, msg, &rsp); 16962306a36Sopenharmony_ci kfree_skb(msg); 17062306a36Sopenharmony_ci if (ret < 0) 17162306a36Sopenharmony_ci return ret; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci hdr = (struct s3fwrn5_fw_header *) rsp->data; 17462306a36Sopenharmony_ci if (hdr->code != S3FWRN5_FW_RET_SUCCESS) 17562306a36Sopenharmony_ci ret = -EPROTO; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ciout: 17862306a36Sopenharmony_ci kfree_skb(rsp); 17962306a36Sopenharmony_ci return ret; 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic int s3fwrn5_fw_update_sector(struct s3fwrn5_fw_info *fw_info, 18362306a36Sopenharmony_ci u32 base_addr, const void *data) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci struct s3fwrn5_fw_cmd_update_sector args; 18662306a36Sopenharmony_ci struct sk_buff *msg, *rsp = NULL; 18762306a36Sopenharmony_ci struct s3fwrn5_fw_header *hdr; 18862306a36Sopenharmony_ci int ret, i; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci /* Send UPDATE_SECTOR command */ 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci args.base_address = base_addr; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci ret = s3fwrn5_fw_prep_msg(fw_info, &msg, S3FWRN5_FW_MSG_CMD, 19562306a36Sopenharmony_ci S3FWRN5_FW_CMD_UPDATE_SECTOR, &args, sizeof(args)); 19662306a36Sopenharmony_ci if (ret < 0) 19762306a36Sopenharmony_ci return ret; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci ret = s3fwrn5_fw_send_msg(fw_info, msg, &rsp); 20062306a36Sopenharmony_ci kfree_skb(msg); 20162306a36Sopenharmony_ci if (ret < 0) 20262306a36Sopenharmony_ci return ret; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci hdr = (struct s3fwrn5_fw_header *) rsp->data; 20562306a36Sopenharmony_ci if (hdr->code != S3FWRN5_FW_RET_SUCCESS) { 20662306a36Sopenharmony_ci ret = -EPROTO; 20762306a36Sopenharmony_ci goto err; 20862306a36Sopenharmony_ci } 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci kfree_skb(rsp); 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci /* Send data split into 256-byte packets */ 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci for (i = 0; i < 16; ++i) { 21562306a36Sopenharmony_ci ret = s3fwrn5_fw_prep_msg(fw_info, &msg, 21662306a36Sopenharmony_ci S3FWRN5_FW_MSG_DATA, 0, data+256*i, 256); 21762306a36Sopenharmony_ci if (ret < 0) 21862306a36Sopenharmony_ci break; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci ret = s3fwrn5_fw_send_msg(fw_info, msg, &rsp); 22162306a36Sopenharmony_ci kfree_skb(msg); 22262306a36Sopenharmony_ci if (ret < 0) 22362306a36Sopenharmony_ci break; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci hdr = (struct s3fwrn5_fw_header *) rsp->data; 22662306a36Sopenharmony_ci if (hdr->code != S3FWRN5_FW_RET_SUCCESS) { 22762306a36Sopenharmony_ci ret = -EPROTO; 22862306a36Sopenharmony_ci goto err; 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci kfree_skb(rsp); 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci return ret; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_cierr: 23762306a36Sopenharmony_ci kfree_skb(rsp); 23862306a36Sopenharmony_ci return ret; 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_cistatic int s3fwrn5_fw_complete_update_mode(struct s3fwrn5_fw_info *fw_info) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci struct sk_buff *msg, *rsp = NULL; 24462306a36Sopenharmony_ci struct s3fwrn5_fw_header *hdr; 24562306a36Sopenharmony_ci int ret; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci /* Send COMPLETE_UPDATE_MODE command */ 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci ret = s3fwrn5_fw_prep_msg(fw_info, &msg, S3FWRN5_FW_MSG_CMD, 25062306a36Sopenharmony_ci S3FWRN5_FW_CMD_COMPLETE_UPDATE_MODE, NULL, 0); 25162306a36Sopenharmony_ci if (ret < 0) 25262306a36Sopenharmony_ci return ret; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci ret = s3fwrn5_fw_send_msg(fw_info, msg, &rsp); 25562306a36Sopenharmony_ci kfree_skb(msg); 25662306a36Sopenharmony_ci if (ret < 0) 25762306a36Sopenharmony_ci return ret; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci hdr = (struct s3fwrn5_fw_header *) rsp->data; 26062306a36Sopenharmony_ci if (hdr->code != S3FWRN5_FW_RET_SUCCESS) 26162306a36Sopenharmony_ci ret = -EPROTO; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci kfree_skb(rsp); 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci return ret; 26662306a36Sopenharmony_ci} 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci/* 26962306a36Sopenharmony_ci * Firmware header structure: 27062306a36Sopenharmony_ci * 27162306a36Sopenharmony_ci * 0x00 - 0x0B : Date and time string (w/o NUL termination) 27262306a36Sopenharmony_ci * 0x10 - 0x13 : Firmware version 27362306a36Sopenharmony_ci * 0x14 - 0x17 : Signature address 27462306a36Sopenharmony_ci * 0x18 - 0x1B : Signature size 27562306a36Sopenharmony_ci * 0x1C - 0x1F : Firmware image address 27662306a36Sopenharmony_ci * 0x20 - 0x23 : Firmware sectors count 27762306a36Sopenharmony_ci * 0x24 - 0x27 : Custom signature address 27862306a36Sopenharmony_ci * 0x28 - 0x2B : Custom signature size 27962306a36Sopenharmony_ci */ 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci#define S3FWRN5_FW_IMAGE_HEADER_SIZE 44 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ciint s3fwrn5_fw_request_firmware(struct s3fwrn5_fw_info *fw_info) 28462306a36Sopenharmony_ci{ 28562306a36Sopenharmony_ci struct s3fwrn5_fw_image *fw = &fw_info->fw; 28662306a36Sopenharmony_ci u32 sig_off; 28762306a36Sopenharmony_ci u32 image_off; 28862306a36Sopenharmony_ci u32 custom_sig_off; 28962306a36Sopenharmony_ci int ret; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci ret = request_firmware(&fw->fw, fw_info->fw_name, 29262306a36Sopenharmony_ci &fw_info->ndev->nfc_dev->dev); 29362306a36Sopenharmony_ci if (ret < 0) 29462306a36Sopenharmony_ci return ret; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci if (fw->fw->size < S3FWRN5_FW_IMAGE_HEADER_SIZE) { 29762306a36Sopenharmony_ci release_firmware(fw->fw); 29862306a36Sopenharmony_ci return -EINVAL; 29962306a36Sopenharmony_ci } 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci memcpy(fw->date, fw->fw->data + 0x00, 12); 30262306a36Sopenharmony_ci fw->date[12] = '\0'; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci memcpy(&fw->version, fw->fw->data + 0x10, 4); 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci memcpy(&sig_off, fw->fw->data + 0x14, 4); 30762306a36Sopenharmony_ci fw->sig = fw->fw->data + sig_off; 30862306a36Sopenharmony_ci memcpy(&fw->sig_size, fw->fw->data + 0x18, 4); 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci memcpy(&image_off, fw->fw->data + 0x1C, 4); 31162306a36Sopenharmony_ci fw->image = fw->fw->data + image_off; 31262306a36Sopenharmony_ci memcpy(&fw->image_sectors, fw->fw->data + 0x20, 4); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci memcpy(&custom_sig_off, fw->fw->data + 0x24, 4); 31562306a36Sopenharmony_ci fw->custom_sig = fw->fw->data + custom_sig_off; 31662306a36Sopenharmony_ci memcpy(&fw->custom_sig_size, fw->fw->data + 0x28, 4); 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci return 0; 31962306a36Sopenharmony_ci} 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_cistatic void s3fwrn5_fw_release_firmware(struct s3fwrn5_fw_info *fw_info) 32262306a36Sopenharmony_ci{ 32362306a36Sopenharmony_ci release_firmware(fw_info->fw.fw); 32462306a36Sopenharmony_ci} 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_cistatic int s3fwrn5_fw_get_base_addr( 32762306a36Sopenharmony_ci struct s3fwrn5_fw_cmd_get_bootinfo_rsp *bootinfo, u32 *base_addr) 32862306a36Sopenharmony_ci{ 32962306a36Sopenharmony_ci int i; 33062306a36Sopenharmony_ci static const struct { 33162306a36Sopenharmony_ci u8 version[4]; 33262306a36Sopenharmony_ci u32 base_addr; 33362306a36Sopenharmony_ci } match[] = { 33462306a36Sopenharmony_ci {{0x05, 0x00, 0x00, 0x00}, 0x00005000}, 33562306a36Sopenharmony_ci {{0x05, 0x00, 0x00, 0x01}, 0x00003000}, 33662306a36Sopenharmony_ci {{0x05, 0x00, 0x00, 0x02}, 0x00003000}, 33762306a36Sopenharmony_ci {{0x05, 0x00, 0x00, 0x03}, 0x00003000}, 33862306a36Sopenharmony_ci {{0x05, 0x00, 0x00, 0x05}, 0x00003000} 33962306a36Sopenharmony_ci }; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(match); ++i) 34262306a36Sopenharmony_ci if (bootinfo->hw_version[0] == match[i].version[0] && 34362306a36Sopenharmony_ci bootinfo->hw_version[1] == match[i].version[1] && 34462306a36Sopenharmony_ci bootinfo->hw_version[3] == match[i].version[3]) { 34562306a36Sopenharmony_ci *base_addr = match[i].base_addr; 34662306a36Sopenharmony_ci return 0; 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci return -EINVAL; 35062306a36Sopenharmony_ci} 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_cistatic inline bool 35362306a36Sopenharmony_cis3fwrn5_fw_is_custom(const struct s3fwrn5_fw_cmd_get_bootinfo_rsp *bootinfo) 35462306a36Sopenharmony_ci{ 35562306a36Sopenharmony_ci return !!bootinfo->hw_version[2]; 35662306a36Sopenharmony_ci} 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ciint s3fwrn5_fw_setup(struct s3fwrn5_fw_info *fw_info) 35962306a36Sopenharmony_ci{ 36062306a36Sopenharmony_ci struct device *dev = &fw_info->ndev->nfc_dev->dev; 36162306a36Sopenharmony_ci struct s3fwrn5_fw_cmd_get_bootinfo_rsp bootinfo; 36262306a36Sopenharmony_ci int ret; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci /* Get bootloader info */ 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci ret = s3fwrn5_fw_get_bootinfo(fw_info, &bootinfo); 36762306a36Sopenharmony_ci if (ret < 0) { 36862306a36Sopenharmony_ci dev_err(dev, "Failed to get bootinfo, ret=%02x\n", ret); 36962306a36Sopenharmony_ci goto err; 37062306a36Sopenharmony_ci } 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci /* Match hardware version to obtain firmware base address */ 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci ret = s3fwrn5_fw_get_base_addr(&bootinfo, &fw_info->base_addr); 37562306a36Sopenharmony_ci if (ret < 0) { 37662306a36Sopenharmony_ci dev_err(dev, "Unknown hardware version\n"); 37762306a36Sopenharmony_ci goto err; 37862306a36Sopenharmony_ci } 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci fw_info->sector_size = bootinfo.sector_size; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci fw_info->sig_size = s3fwrn5_fw_is_custom(&bootinfo) ? 38362306a36Sopenharmony_ci fw_info->fw.custom_sig_size : fw_info->fw.sig_size; 38462306a36Sopenharmony_ci fw_info->sig = s3fwrn5_fw_is_custom(&bootinfo) ? 38562306a36Sopenharmony_ci fw_info->fw.custom_sig : fw_info->fw.sig; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci return 0; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_cierr: 39062306a36Sopenharmony_ci s3fwrn5_fw_release_firmware(fw_info); 39162306a36Sopenharmony_ci return ret; 39262306a36Sopenharmony_ci} 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_cibool s3fwrn5_fw_check_version(const struct s3fwrn5_fw_info *fw_info, u32 version) 39562306a36Sopenharmony_ci{ 39662306a36Sopenharmony_ci struct s3fwrn5_fw_version *new = (void *) &fw_info->fw.version; 39762306a36Sopenharmony_ci struct s3fwrn5_fw_version *old = (void *) &version; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci if (new->major > old->major) 40062306a36Sopenharmony_ci return true; 40162306a36Sopenharmony_ci if (new->build1 > old->build1) 40262306a36Sopenharmony_ci return true; 40362306a36Sopenharmony_ci if (new->build2 > old->build2) 40462306a36Sopenharmony_ci return true; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci return false; 40762306a36Sopenharmony_ci} 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ciint s3fwrn5_fw_download(struct s3fwrn5_fw_info *fw_info) 41062306a36Sopenharmony_ci{ 41162306a36Sopenharmony_ci struct device *dev = &fw_info->ndev->nfc_dev->dev; 41262306a36Sopenharmony_ci struct s3fwrn5_fw_image *fw = &fw_info->fw; 41362306a36Sopenharmony_ci u8 hash_data[SHA1_DIGEST_SIZE]; 41462306a36Sopenharmony_ci struct crypto_shash *tfm; 41562306a36Sopenharmony_ci u32 image_size, off; 41662306a36Sopenharmony_ci int ret; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci image_size = fw_info->sector_size * fw->image_sectors; 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci /* Compute SHA of firmware data */ 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci tfm = crypto_alloc_shash("sha1", 0, 0); 42362306a36Sopenharmony_ci if (IS_ERR(tfm)) { 42462306a36Sopenharmony_ci dev_err(dev, "Cannot allocate shash (code=%pe)\n", tfm); 42562306a36Sopenharmony_ci return PTR_ERR(tfm); 42662306a36Sopenharmony_ci } 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci ret = crypto_shash_tfm_digest(tfm, fw->image, image_size, hash_data); 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci crypto_free_shash(tfm); 43162306a36Sopenharmony_ci if (ret) { 43262306a36Sopenharmony_ci dev_err(dev, "Cannot compute hash (code=%d)\n", ret); 43362306a36Sopenharmony_ci return ret; 43462306a36Sopenharmony_ci } 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci /* Firmware update process */ 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci dev_info(dev, "Firmware update: %s\n", fw_info->fw_name); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci ret = s3fwrn5_fw_enter_update_mode(fw_info, hash_data, 44162306a36Sopenharmony_ci SHA1_DIGEST_SIZE, fw_info->sig, fw_info->sig_size); 44262306a36Sopenharmony_ci if (ret < 0) { 44362306a36Sopenharmony_ci dev_err(dev, "Unable to enter update mode\n"); 44462306a36Sopenharmony_ci return ret; 44562306a36Sopenharmony_ci } 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci for (off = 0; off < image_size; off += fw_info->sector_size) { 44862306a36Sopenharmony_ci ret = s3fwrn5_fw_update_sector(fw_info, 44962306a36Sopenharmony_ci fw_info->base_addr + off, fw->image + off); 45062306a36Sopenharmony_ci if (ret < 0) { 45162306a36Sopenharmony_ci dev_err(dev, "Firmware update error (code=%d)\n", ret); 45262306a36Sopenharmony_ci return ret; 45362306a36Sopenharmony_ci } 45462306a36Sopenharmony_ci } 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci ret = s3fwrn5_fw_complete_update_mode(fw_info); 45762306a36Sopenharmony_ci if (ret < 0) { 45862306a36Sopenharmony_ci dev_err(dev, "Unable to complete update mode\n"); 45962306a36Sopenharmony_ci return ret; 46062306a36Sopenharmony_ci } 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci dev_info(dev, "Firmware update: success\n"); 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci return ret; 46562306a36Sopenharmony_ci} 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_civoid s3fwrn5_fw_init(struct s3fwrn5_fw_info *fw_info, const char *fw_name) 46862306a36Sopenharmony_ci{ 46962306a36Sopenharmony_ci fw_info->parity = 0x00; 47062306a36Sopenharmony_ci fw_info->rsp = NULL; 47162306a36Sopenharmony_ci fw_info->fw.fw = NULL; 47262306a36Sopenharmony_ci strcpy(fw_info->fw_name, fw_name); 47362306a36Sopenharmony_ci init_completion(&fw_info->completion); 47462306a36Sopenharmony_ci} 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_civoid s3fwrn5_fw_cleanup(struct s3fwrn5_fw_info *fw_info) 47762306a36Sopenharmony_ci{ 47862306a36Sopenharmony_ci s3fwrn5_fw_release_firmware(fw_info); 47962306a36Sopenharmony_ci} 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ciint s3fwrn5_fw_recv_frame(struct nci_dev *ndev, struct sk_buff *skb) 48262306a36Sopenharmony_ci{ 48362306a36Sopenharmony_ci struct s3fwrn5_info *info = nci_get_drvdata(ndev); 48462306a36Sopenharmony_ci struct s3fwrn5_fw_info *fw_info = &info->fw_info; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci if (WARN_ON(fw_info->rsp)) { 48762306a36Sopenharmony_ci kfree_skb(skb); 48862306a36Sopenharmony_ci return -EINVAL; 48962306a36Sopenharmony_ci } 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci fw_info->rsp = skb; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci complete(&fw_info->completion); 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci return 0; 49662306a36Sopenharmony_ci} 497