18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Marvell NFC driver: Firmware downloader 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2015, Marvell International Ltd. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * This software file (the "File") is distributed by Marvell International 78c2ecf20Sopenharmony_ci * Ltd. under the terms of the GNU General Public License Version 2, June 1991 88c2ecf20Sopenharmony_ci * (the "License"). You may use, redistribute and/or modify this File in 98c2ecf20Sopenharmony_ci * accordance with the terms and conditions of the License, a copy of which 108c2ecf20Sopenharmony_ci * is available on the worldwide web at 118c2ecf20Sopenharmony_ci * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE 148c2ecf20Sopenharmony_ci * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE 158c2ecf20Sopenharmony_ci * ARE EXPRESSLY DISCLAIMED. The License provides additional details about 168c2ecf20Sopenharmony_ci * this warranty disclaimer. 178c2ecf20Sopenharmony_ci */ 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include <linux/module.h> 208c2ecf20Sopenharmony_ci#include <asm/unaligned.h> 218c2ecf20Sopenharmony_ci#include <linux/firmware.h> 228c2ecf20Sopenharmony_ci#include <linux/nfc.h> 238c2ecf20Sopenharmony_ci#include <net/nfc/nci.h> 248c2ecf20Sopenharmony_ci#include <net/nfc/nci_core.h> 258c2ecf20Sopenharmony_ci#include "nfcmrvl.h" 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define FW_DNLD_TIMEOUT 15000 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define NCI_OP_PROPRIETARY_BOOT_CMD nci_opcode_pack(NCI_GID_PROPRIETARY, \ 308c2ecf20Sopenharmony_ci NCI_OP_PROP_BOOT_CMD) 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci/* FW download states */ 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cienum { 358c2ecf20Sopenharmony_ci STATE_RESET = 0, 368c2ecf20Sopenharmony_ci STATE_INIT, 378c2ecf20Sopenharmony_ci STATE_SET_REF_CLOCK, 388c2ecf20Sopenharmony_ci STATE_SET_HI_CONFIG, 398c2ecf20Sopenharmony_ci STATE_OPEN_LC, 408c2ecf20Sopenharmony_ci STATE_FW_DNLD, 418c2ecf20Sopenharmony_ci STATE_CLOSE_LC, 428c2ecf20Sopenharmony_ci STATE_BOOT 438c2ecf20Sopenharmony_ci}; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cienum { 468c2ecf20Sopenharmony_ci SUBSTATE_WAIT_COMMAND = 0, 478c2ecf20Sopenharmony_ci SUBSTATE_WAIT_ACK_CREDIT, 488c2ecf20Sopenharmony_ci SUBSTATE_WAIT_NACK_CREDIT, 498c2ecf20Sopenharmony_ci SUBSTATE_WAIT_DATA_CREDIT, 508c2ecf20Sopenharmony_ci}; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci/* 538c2ecf20Sopenharmony_ci** Patterns for responses 548c2ecf20Sopenharmony_ci*/ 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic const uint8_t nci_pattern_core_reset_ntf[] = { 578c2ecf20Sopenharmony_ci 0x60, 0x00, 0x02, 0xA0, 0x01 588c2ecf20Sopenharmony_ci}; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistatic const uint8_t nci_pattern_core_init_rsp[] = { 618c2ecf20Sopenharmony_ci 0x40, 0x01, 0x11 628c2ecf20Sopenharmony_ci}; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic const uint8_t nci_pattern_core_set_config_rsp[] = { 658c2ecf20Sopenharmony_ci 0x40, 0x02, 0x02, 0x00, 0x00 668c2ecf20Sopenharmony_ci}; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic const uint8_t nci_pattern_core_conn_create_rsp[] = { 698c2ecf20Sopenharmony_ci 0x40, 0x04, 0x04, 0x00 708c2ecf20Sopenharmony_ci}; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistatic const uint8_t nci_pattern_core_conn_close_rsp[] = { 738c2ecf20Sopenharmony_ci 0x40, 0x05, 0x01, 0x00 748c2ecf20Sopenharmony_ci}; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistatic const uint8_t nci_pattern_core_conn_credits_ntf[] = { 778c2ecf20Sopenharmony_ci 0x60, 0x06, 0x03, 0x01, NCI_CORE_LC_CONNID_PROP_FW_DL, 0x01 788c2ecf20Sopenharmony_ci}; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic const uint8_t nci_pattern_proprietary_boot_rsp[] = { 818c2ecf20Sopenharmony_ci 0x4F, 0x3A, 0x01, 0x00 828c2ecf20Sopenharmony_ci}; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistatic struct sk_buff *alloc_lc_skb(struct nfcmrvl_private *priv, uint8_t plen) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci struct sk_buff *skb; 878c2ecf20Sopenharmony_ci struct nci_data_hdr *hdr; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci skb = nci_skb_alloc(priv->ndev, (NCI_DATA_HDR_SIZE + plen), GFP_KERNEL); 908c2ecf20Sopenharmony_ci if (!skb) { 918c2ecf20Sopenharmony_ci pr_err("no memory for data\n"); 928c2ecf20Sopenharmony_ci return NULL; 938c2ecf20Sopenharmony_ci } 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci hdr = skb_put(skb, NCI_DATA_HDR_SIZE); 968c2ecf20Sopenharmony_ci hdr->conn_id = NCI_CORE_LC_CONNID_PROP_FW_DL; 978c2ecf20Sopenharmony_ci hdr->rfu = 0; 988c2ecf20Sopenharmony_ci hdr->plen = plen; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci nci_mt_set((__u8 *)hdr, NCI_MT_DATA_PKT); 1018c2ecf20Sopenharmony_ci nci_pbf_set((__u8 *)hdr, NCI_PBF_LAST); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci return skb; 1048c2ecf20Sopenharmony_ci} 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cistatic void fw_dnld_over(struct nfcmrvl_private *priv, u32 error) 1078c2ecf20Sopenharmony_ci{ 1088c2ecf20Sopenharmony_ci if (priv->fw_dnld.fw) { 1098c2ecf20Sopenharmony_ci release_firmware(priv->fw_dnld.fw); 1108c2ecf20Sopenharmony_ci priv->fw_dnld.fw = NULL; 1118c2ecf20Sopenharmony_ci priv->fw_dnld.header = NULL; 1128c2ecf20Sopenharmony_ci priv->fw_dnld.binary_config = NULL; 1138c2ecf20Sopenharmony_ci } 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci atomic_set(&priv->ndev->cmd_cnt, 0); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci if (timer_pending(&priv->ndev->cmd_timer)) 1188c2ecf20Sopenharmony_ci del_timer_sync(&priv->ndev->cmd_timer); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci if (timer_pending(&priv->fw_dnld.timer)) 1218c2ecf20Sopenharmony_ci del_timer_sync(&priv->fw_dnld.timer); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci nfc_info(priv->dev, "FW loading over (%d)]\n", error); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci if (error != 0) { 1268c2ecf20Sopenharmony_ci /* failed, halt the chip to avoid power consumption */ 1278c2ecf20Sopenharmony_ci nfcmrvl_chip_halt(priv); 1288c2ecf20Sopenharmony_ci } 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci nfc_fw_download_done(priv->ndev->nfc_dev, priv->fw_dnld.name, error); 1318c2ecf20Sopenharmony_ci} 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cistatic void fw_dnld_timeout(struct timer_list *t) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci struct nfcmrvl_private *priv = from_timer(priv, t, fw_dnld.timer); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci nfc_err(priv->dev, "FW loading timeout"); 1388c2ecf20Sopenharmony_ci priv->fw_dnld.state = STATE_RESET; 1398c2ecf20Sopenharmony_ci fw_dnld_over(priv, -ETIMEDOUT); 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic int process_state_reset(struct nfcmrvl_private *priv, 1438c2ecf20Sopenharmony_ci struct sk_buff *skb) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci if (sizeof(nci_pattern_core_reset_ntf) != skb->len || 1468c2ecf20Sopenharmony_ci memcmp(skb->data, nci_pattern_core_reset_ntf, 1478c2ecf20Sopenharmony_ci sizeof(nci_pattern_core_reset_ntf))) 1488c2ecf20Sopenharmony_ci return -EINVAL; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci nfc_info(priv->dev, "BootROM reset, start fw download\n"); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci /* Start FW download state machine */ 1538c2ecf20Sopenharmony_ci priv->fw_dnld.state = STATE_INIT; 1548c2ecf20Sopenharmony_ci nci_send_cmd(priv->ndev, NCI_OP_CORE_INIT_CMD, 0, NULL); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci return 0; 1578c2ecf20Sopenharmony_ci} 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_cistatic int process_state_init(struct nfcmrvl_private *priv, struct sk_buff *skb) 1608c2ecf20Sopenharmony_ci{ 1618c2ecf20Sopenharmony_ci struct nci_core_set_config_cmd cmd; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci if (sizeof(nci_pattern_core_init_rsp) >= skb->len || 1648c2ecf20Sopenharmony_ci memcmp(skb->data, nci_pattern_core_init_rsp, 1658c2ecf20Sopenharmony_ci sizeof(nci_pattern_core_init_rsp))) 1668c2ecf20Sopenharmony_ci return -EINVAL; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci cmd.num_params = 1; 1698c2ecf20Sopenharmony_ci cmd.param.id = NFCMRVL_PROP_REF_CLOCK; 1708c2ecf20Sopenharmony_ci cmd.param.len = 4; 1718c2ecf20Sopenharmony_ci memcpy(cmd.param.val, &priv->fw_dnld.header->ref_clock, 4); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci nci_send_cmd(priv->ndev, NCI_OP_CORE_SET_CONFIG_CMD, 3 + cmd.param.len, 1748c2ecf20Sopenharmony_ci &cmd); 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci priv->fw_dnld.state = STATE_SET_REF_CLOCK; 1778c2ecf20Sopenharmony_ci return 0; 1788c2ecf20Sopenharmony_ci} 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_cistatic void create_lc(struct nfcmrvl_private *priv) 1818c2ecf20Sopenharmony_ci{ 1828c2ecf20Sopenharmony_ci uint8_t param[2] = { NCI_CORE_LC_PROP_FW_DL, 0x0 }; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci priv->fw_dnld.state = STATE_OPEN_LC; 1858c2ecf20Sopenharmony_ci nci_send_cmd(priv->ndev, NCI_OP_CORE_CONN_CREATE_CMD, 2, param); 1868c2ecf20Sopenharmony_ci} 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_cistatic int process_state_set_ref_clock(struct nfcmrvl_private *priv, 1898c2ecf20Sopenharmony_ci struct sk_buff *skb) 1908c2ecf20Sopenharmony_ci{ 1918c2ecf20Sopenharmony_ci struct nci_core_set_config_cmd cmd; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci if (sizeof(nci_pattern_core_set_config_rsp) != skb->len || 1948c2ecf20Sopenharmony_ci memcmp(skb->data, nci_pattern_core_set_config_rsp, skb->len)) 1958c2ecf20Sopenharmony_ci return -EINVAL; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci cmd.num_params = 1; 1988c2ecf20Sopenharmony_ci cmd.param.id = NFCMRVL_PROP_SET_HI_CONFIG; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci switch (priv->phy) { 2018c2ecf20Sopenharmony_ci case NFCMRVL_PHY_UART: 2028c2ecf20Sopenharmony_ci cmd.param.len = 5; 2038c2ecf20Sopenharmony_ci memcpy(cmd.param.val, 2048c2ecf20Sopenharmony_ci &priv->fw_dnld.binary_config->uart.baudrate, 2058c2ecf20Sopenharmony_ci 4); 2068c2ecf20Sopenharmony_ci cmd.param.val[4] = 2078c2ecf20Sopenharmony_ci priv->fw_dnld.binary_config->uart.flow_control; 2088c2ecf20Sopenharmony_ci break; 2098c2ecf20Sopenharmony_ci case NFCMRVL_PHY_I2C: 2108c2ecf20Sopenharmony_ci cmd.param.len = 5; 2118c2ecf20Sopenharmony_ci memcpy(cmd.param.val, 2128c2ecf20Sopenharmony_ci &priv->fw_dnld.binary_config->i2c.clk, 2138c2ecf20Sopenharmony_ci 4); 2148c2ecf20Sopenharmony_ci cmd.param.val[4] = 0; 2158c2ecf20Sopenharmony_ci break; 2168c2ecf20Sopenharmony_ci case NFCMRVL_PHY_SPI: 2178c2ecf20Sopenharmony_ci cmd.param.len = 5; 2188c2ecf20Sopenharmony_ci memcpy(cmd.param.val, 2198c2ecf20Sopenharmony_ci &priv->fw_dnld.binary_config->spi.clk, 2208c2ecf20Sopenharmony_ci 4); 2218c2ecf20Sopenharmony_ci cmd.param.val[4] = 0; 2228c2ecf20Sopenharmony_ci break; 2238c2ecf20Sopenharmony_ci default: 2248c2ecf20Sopenharmony_ci create_lc(priv); 2258c2ecf20Sopenharmony_ci return 0; 2268c2ecf20Sopenharmony_ci } 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci priv->fw_dnld.state = STATE_SET_HI_CONFIG; 2298c2ecf20Sopenharmony_ci nci_send_cmd(priv->ndev, NCI_OP_CORE_SET_CONFIG_CMD, 3 + cmd.param.len, 2308c2ecf20Sopenharmony_ci &cmd); 2318c2ecf20Sopenharmony_ci return 0; 2328c2ecf20Sopenharmony_ci} 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_cistatic int process_state_set_hi_config(struct nfcmrvl_private *priv, 2358c2ecf20Sopenharmony_ci struct sk_buff *skb) 2368c2ecf20Sopenharmony_ci{ 2378c2ecf20Sopenharmony_ci if (sizeof(nci_pattern_core_set_config_rsp) != skb->len || 2388c2ecf20Sopenharmony_ci memcmp(skb->data, nci_pattern_core_set_config_rsp, skb->len)) 2398c2ecf20Sopenharmony_ci return -EINVAL; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci create_lc(priv); 2428c2ecf20Sopenharmony_ci return 0; 2438c2ecf20Sopenharmony_ci} 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_cistatic int process_state_open_lc(struct nfcmrvl_private *priv, 2468c2ecf20Sopenharmony_ci struct sk_buff *skb) 2478c2ecf20Sopenharmony_ci{ 2488c2ecf20Sopenharmony_ci if (sizeof(nci_pattern_core_conn_create_rsp) >= skb->len || 2498c2ecf20Sopenharmony_ci memcmp(skb->data, nci_pattern_core_conn_create_rsp, 2508c2ecf20Sopenharmony_ci sizeof(nci_pattern_core_conn_create_rsp))) 2518c2ecf20Sopenharmony_ci return -EINVAL; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci priv->fw_dnld.state = STATE_FW_DNLD; 2548c2ecf20Sopenharmony_ci priv->fw_dnld.substate = SUBSTATE_WAIT_COMMAND; 2558c2ecf20Sopenharmony_ci priv->fw_dnld.offset = priv->fw_dnld.binary_config->offset; 2568c2ecf20Sopenharmony_ci return 0; 2578c2ecf20Sopenharmony_ci} 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_cistatic int process_state_fw_dnld(struct nfcmrvl_private *priv, 2608c2ecf20Sopenharmony_ci struct sk_buff *skb) 2618c2ecf20Sopenharmony_ci{ 2628c2ecf20Sopenharmony_ci uint16_t len; 2638c2ecf20Sopenharmony_ci uint16_t comp_len; 2648c2ecf20Sopenharmony_ci struct sk_buff *out_skb; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci switch (priv->fw_dnld.substate) { 2678c2ecf20Sopenharmony_ci case SUBSTATE_WAIT_COMMAND: 2688c2ecf20Sopenharmony_ci /* 2698c2ecf20Sopenharmony_ci * Command format: 2708c2ecf20Sopenharmony_ci * B0..2: NCI header 2718c2ecf20Sopenharmony_ci * B3 : Helper command (0xA5) 2728c2ecf20Sopenharmony_ci * B4..5: le16 data size 2738c2ecf20Sopenharmony_ci * B6..7: le16 data size complement (~) 2748c2ecf20Sopenharmony_ci * B8..N: payload 2758c2ecf20Sopenharmony_ci */ 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci /* Remove NCI HDR */ 2788c2ecf20Sopenharmony_ci skb_pull(skb, 3); 2798c2ecf20Sopenharmony_ci if (skb->data[0] != HELPER_CMD_PACKET_FORMAT || skb->len != 5) { 2808c2ecf20Sopenharmony_ci nfc_err(priv->dev, "bad command"); 2818c2ecf20Sopenharmony_ci return -EINVAL; 2828c2ecf20Sopenharmony_ci } 2838c2ecf20Sopenharmony_ci skb_pull(skb, 1); 2848c2ecf20Sopenharmony_ci len = get_unaligned_le16(skb->data); 2858c2ecf20Sopenharmony_ci skb_pull(skb, 2); 2868c2ecf20Sopenharmony_ci comp_len = get_unaligned_le16(skb->data); 2878c2ecf20Sopenharmony_ci memcpy(&comp_len, skb->data, 2); 2888c2ecf20Sopenharmony_ci skb_pull(skb, 2); 2898c2ecf20Sopenharmony_ci if (((~len) & 0xFFFF) != comp_len) { 2908c2ecf20Sopenharmony_ci nfc_err(priv->dev, "bad len complement: %x %x %x", 2918c2ecf20Sopenharmony_ci len, comp_len, (~len & 0xFFFF)); 2928c2ecf20Sopenharmony_ci out_skb = alloc_lc_skb(priv, 1); 2938c2ecf20Sopenharmony_ci if (!out_skb) 2948c2ecf20Sopenharmony_ci return -ENOMEM; 2958c2ecf20Sopenharmony_ci skb_put_u8(out_skb, 0xBF); 2968c2ecf20Sopenharmony_ci nci_send_frame(priv->ndev, out_skb); 2978c2ecf20Sopenharmony_ci priv->fw_dnld.substate = SUBSTATE_WAIT_NACK_CREDIT; 2988c2ecf20Sopenharmony_ci return 0; 2998c2ecf20Sopenharmony_ci } 3008c2ecf20Sopenharmony_ci priv->fw_dnld.chunk_len = len; 3018c2ecf20Sopenharmony_ci out_skb = alloc_lc_skb(priv, 1); 3028c2ecf20Sopenharmony_ci if (!out_skb) 3038c2ecf20Sopenharmony_ci return -ENOMEM; 3048c2ecf20Sopenharmony_ci skb_put_u8(out_skb, HELPER_ACK_PACKET_FORMAT); 3058c2ecf20Sopenharmony_ci nci_send_frame(priv->ndev, out_skb); 3068c2ecf20Sopenharmony_ci priv->fw_dnld.substate = SUBSTATE_WAIT_ACK_CREDIT; 3078c2ecf20Sopenharmony_ci break; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci case SUBSTATE_WAIT_ACK_CREDIT: 3108c2ecf20Sopenharmony_ci if (sizeof(nci_pattern_core_conn_credits_ntf) != skb->len || 3118c2ecf20Sopenharmony_ci memcmp(nci_pattern_core_conn_credits_ntf, skb->data, 3128c2ecf20Sopenharmony_ci skb->len)) { 3138c2ecf20Sopenharmony_ci nfc_err(priv->dev, "bad packet: waiting for credit"); 3148c2ecf20Sopenharmony_ci return -EINVAL; 3158c2ecf20Sopenharmony_ci } 3168c2ecf20Sopenharmony_ci if (priv->fw_dnld.chunk_len == 0) { 3178c2ecf20Sopenharmony_ci /* FW Loading is done */ 3188c2ecf20Sopenharmony_ci uint8_t conn_id = NCI_CORE_LC_CONNID_PROP_FW_DL; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci priv->fw_dnld.state = STATE_CLOSE_LC; 3218c2ecf20Sopenharmony_ci nci_send_cmd(priv->ndev, NCI_OP_CORE_CONN_CLOSE_CMD, 3228c2ecf20Sopenharmony_ci 1, &conn_id); 3238c2ecf20Sopenharmony_ci } else { 3248c2ecf20Sopenharmony_ci out_skb = alloc_lc_skb(priv, priv->fw_dnld.chunk_len); 3258c2ecf20Sopenharmony_ci if (!out_skb) 3268c2ecf20Sopenharmony_ci return -ENOMEM; 3278c2ecf20Sopenharmony_ci skb_put_data(out_skb, 3288c2ecf20Sopenharmony_ci ((uint8_t *)priv->fw_dnld.fw->data) + priv->fw_dnld.offset, 3298c2ecf20Sopenharmony_ci priv->fw_dnld.chunk_len); 3308c2ecf20Sopenharmony_ci nci_send_frame(priv->ndev, out_skb); 3318c2ecf20Sopenharmony_ci priv->fw_dnld.substate = SUBSTATE_WAIT_DATA_CREDIT; 3328c2ecf20Sopenharmony_ci } 3338c2ecf20Sopenharmony_ci break; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci case SUBSTATE_WAIT_DATA_CREDIT: 3368c2ecf20Sopenharmony_ci if (sizeof(nci_pattern_core_conn_credits_ntf) != skb->len || 3378c2ecf20Sopenharmony_ci memcmp(nci_pattern_core_conn_credits_ntf, skb->data, 3388c2ecf20Sopenharmony_ci skb->len)) { 3398c2ecf20Sopenharmony_ci nfc_err(priv->dev, "bad packet: waiting for credit"); 3408c2ecf20Sopenharmony_ci return -EINVAL; 3418c2ecf20Sopenharmony_ci } 3428c2ecf20Sopenharmony_ci priv->fw_dnld.offset += priv->fw_dnld.chunk_len; 3438c2ecf20Sopenharmony_ci priv->fw_dnld.chunk_len = 0; 3448c2ecf20Sopenharmony_ci priv->fw_dnld.substate = SUBSTATE_WAIT_COMMAND; 3458c2ecf20Sopenharmony_ci break; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci case SUBSTATE_WAIT_NACK_CREDIT: 3488c2ecf20Sopenharmony_ci if (sizeof(nci_pattern_core_conn_credits_ntf) != skb->len || 3498c2ecf20Sopenharmony_ci memcmp(nci_pattern_core_conn_credits_ntf, skb->data, 3508c2ecf20Sopenharmony_ci skb->len)) { 3518c2ecf20Sopenharmony_ci nfc_err(priv->dev, "bad packet: waiting for credit"); 3528c2ecf20Sopenharmony_ci return -EINVAL; 3538c2ecf20Sopenharmony_ci } 3548c2ecf20Sopenharmony_ci priv->fw_dnld.substate = SUBSTATE_WAIT_COMMAND; 3558c2ecf20Sopenharmony_ci break; 3568c2ecf20Sopenharmony_ci } 3578c2ecf20Sopenharmony_ci return 0; 3588c2ecf20Sopenharmony_ci} 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_cistatic int process_state_close_lc(struct nfcmrvl_private *priv, 3618c2ecf20Sopenharmony_ci struct sk_buff *skb) 3628c2ecf20Sopenharmony_ci{ 3638c2ecf20Sopenharmony_ci if (sizeof(nci_pattern_core_conn_close_rsp) != skb->len || 3648c2ecf20Sopenharmony_ci memcmp(skb->data, nci_pattern_core_conn_close_rsp, skb->len)) 3658c2ecf20Sopenharmony_ci return -EINVAL; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci priv->fw_dnld.state = STATE_BOOT; 3688c2ecf20Sopenharmony_ci nci_send_cmd(priv->ndev, NCI_OP_PROPRIETARY_BOOT_CMD, 0, NULL); 3698c2ecf20Sopenharmony_ci return 0; 3708c2ecf20Sopenharmony_ci} 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_cistatic int process_state_boot(struct nfcmrvl_private *priv, struct sk_buff *skb) 3738c2ecf20Sopenharmony_ci{ 3748c2ecf20Sopenharmony_ci if (sizeof(nci_pattern_proprietary_boot_rsp) != skb->len || 3758c2ecf20Sopenharmony_ci memcmp(skb->data, nci_pattern_proprietary_boot_rsp, skb->len)) 3768c2ecf20Sopenharmony_ci return -EINVAL; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci /* 3798c2ecf20Sopenharmony_ci * Update HI config to use the right configuration for the next 3808c2ecf20Sopenharmony_ci * data exchanges. 3818c2ecf20Sopenharmony_ci */ 3828c2ecf20Sopenharmony_ci priv->if_ops->nci_update_config(priv, 3838c2ecf20Sopenharmony_ci &priv->fw_dnld.binary_config->config); 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci if (priv->fw_dnld.binary_config == &priv->fw_dnld.header->helper) { 3868c2ecf20Sopenharmony_ci /* 3878c2ecf20Sopenharmony_ci * This is the case where an helper was needed and we have 3888c2ecf20Sopenharmony_ci * uploaded it. Now we have to wait the next RESET NTF to start 3898c2ecf20Sopenharmony_ci * FW download. 3908c2ecf20Sopenharmony_ci */ 3918c2ecf20Sopenharmony_ci priv->fw_dnld.state = STATE_RESET; 3928c2ecf20Sopenharmony_ci priv->fw_dnld.binary_config = &priv->fw_dnld.header->firmware; 3938c2ecf20Sopenharmony_ci nfc_info(priv->dev, "FW loading: helper loaded"); 3948c2ecf20Sopenharmony_ci } else { 3958c2ecf20Sopenharmony_ci nfc_info(priv->dev, "FW loading: firmware loaded"); 3968c2ecf20Sopenharmony_ci fw_dnld_over(priv, 0); 3978c2ecf20Sopenharmony_ci } 3988c2ecf20Sopenharmony_ci return 0; 3998c2ecf20Sopenharmony_ci} 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_cistatic void fw_dnld_rx_work(struct work_struct *work) 4028c2ecf20Sopenharmony_ci{ 4038c2ecf20Sopenharmony_ci int ret; 4048c2ecf20Sopenharmony_ci struct sk_buff *skb; 4058c2ecf20Sopenharmony_ci struct nfcmrvl_fw_dnld *fw_dnld = container_of(work, 4068c2ecf20Sopenharmony_ci struct nfcmrvl_fw_dnld, 4078c2ecf20Sopenharmony_ci rx_work); 4088c2ecf20Sopenharmony_ci struct nfcmrvl_private *priv = container_of(fw_dnld, 4098c2ecf20Sopenharmony_ci struct nfcmrvl_private, 4108c2ecf20Sopenharmony_ci fw_dnld); 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci while ((skb = skb_dequeue(&fw_dnld->rx_q))) { 4138c2ecf20Sopenharmony_ci nfc_send_to_raw_sock(priv->ndev->nfc_dev, skb, 4148c2ecf20Sopenharmony_ci RAW_PAYLOAD_NCI, NFC_DIRECTION_RX); 4158c2ecf20Sopenharmony_ci switch (fw_dnld->state) { 4168c2ecf20Sopenharmony_ci case STATE_RESET: 4178c2ecf20Sopenharmony_ci ret = process_state_reset(priv, skb); 4188c2ecf20Sopenharmony_ci break; 4198c2ecf20Sopenharmony_ci case STATE_INIT: 4208c2ecf20Sopenharmony_ci ret = process_state_init(priv, skb); 4218c2ecf20Sopenharmony_ci break; 4228c2ecf20Sopenharmony_ci case STATE_SET_REF_CLOCK: 4238c2ecf20Sopenharmony_ci ret = process_state_set_ref_clock(priv, skb); 4248c2ecf20Sopenharmony_ci break; 4258c2ecf20Sopenharmony_ci case STATE_SET_HI_CONFIG: 4268c2ecf20Sopenharmony_ci ret = process_state_set_hi_config(priv, skb); 4278c2ecf20Sopenharmony_ci break; 4288c2ecf20Sopenharmony_ci case STATE_OPEN_LC: 4298c2ecf20Sopenharmony_ci ret = process_state_open_lc(priv, skb); 4308c2ecf20Sopenharmony_ci break; 4318c2ecf20Sopenharmony_ci case STATE_FW_DNLD: 4328c2ecf20Sopenharmony_ci ret = process_state_fw_dnld(priv, skb); 4338c2ecf20Sopenharmony_ci break; 4348c2ecf20Sopenharmony_ci case STATE_CLOSE_LC: 4358c2ecf20Sopenharmony_ci ret = process_state_close_lc(priv, skb); 4368c2ecf20Sopenharmony_ci break; 4378c2ecf20Sopenharmony_ci case STATE_BOOT: 4388c2ecf20Sopenharmony_ci ret = process_state_boot(priv, skb); 4398c2ecf20Sopenharmony_ci break; 4408c2ecf20Sopenharmony_ci default: 4418c2ecf20Sopenharmony_ci ret = -EFAULT; 4428c2ecf20Sopenharmony_ci } 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci kfree_skb(skb); 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci if (ret != 0) { 4478c2ecf20Sopenharmony_ci nfc_err(priv->dev, "FW loading error"); 4488c2ecf20Sopenharmony_ci fw_dnld_over(priv, ret); 4498c2ecf20Sopenharmony_ci break; 4508c2ecf20Sopenharmony_ci } 4518c2ecf20Sopenharmony_ci } 4528c2ecf20Sopenharmony_ci} 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ciint nfcmrvl_fw_dnld_init(struct nfcmrvl_private *priv) 4558c2ecf20Sopenharmony_ci{ 4568c2ecf20Sopenharmony_ci char name[32]; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci INIT_WORK(&priv->fw_dnld.rx_work, fw_dnld_rx_work); 4598c2ecf20Sopenharmony_ci snprintf(name, sizeof(name), "%s_nfcmrvl_fw_dnld_rx_wq", 4608c2ecf20Sopenharmony_ci dev_name(&priv->ndev->nfc_dev->dev)); 4618c2ecf20Sopenharmony_ci priv->fw_dnld.rx_wq = create_singlethread_workqueue(name); 4628c2ecf20Sopenharmony_ci if (!priv->fw_dnld.rx_wq) 4638c2ecf20Sopenharmony_ci return -ENOMEM; 4648c2ecf20Sopenharmony_ci skb_queue_head_init(&priv->fw_dnld.rx_q); 4658c2ecf20Sopenharmony_ci return 0; 4668c2ecf20Sopenharmony_ci} 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_civoid nfcmrvl_fw_dnld_deinit(struct nfcmrvl_private *priv) 4698c2ecf20Sopenharmony_ci{ 4708c2ecf20Sopenharmony_ci destroy_workqueue(priv->fw_dnld.rx_wq); 4718c2ecf20Sopenharmony_ci} 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_civoid nfcmrvl_fw_dnld_recv_frame(struct nfcmrvl_private *priv, 4748c2ecf20Sopenharmony_ci struct sk_buff *skb) 4758c2ecf20Sopenharmony_ci{ 4768c2ecf20Sopenharmony_ci /* Discard command timer */ 4778c2ecf20Sopenharmony_ci if (timer_pending(&priv->ndev->cmd_timer)) 4788c2ecf20Sopenharmony_ci del_timer_sync(&priv->ndev->cmd_timer); 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci /* Allow next command */ 4818c2ecf20Sopenharmony_ci atomic_set(&priv->ndev->cmd_cnt, 1); 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci /* Queue and trigger rx work */ 4848c2ecf20Sopenharmony_ci skb_queue_tail(&priv->fw_dnld.rx_q, skb); 4858c2ecf20Sopenharmony_ci queue_work(priv->fw_dnld.rx_wq, &priv->fw_dnld.rx_work); 4868c2ecf20Sopenharmony_ci} 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_civoid nfcmrvl_fw_dnld_abort(struct nfcmrvl_private *priv) 4898c2ecf20Sopenharmony_ci{ 4908c2ecf20Sopenharmony_ci fw_dnld_over(priv, -EHOSTDOWN); 4918c2ecf20Sopenharmony_ci} 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ciint nfcmrvl_fw_dnld_start(struct nci_dev *ndev, const char *firmware_name) 4948c2ecf20Sopenharmony_ci{ 4958c2ecf20Sopenharmony_ci struct nfcmrvl_private *priv = nci_get_drvdata(ndev); 4968c2ecf20Sopenharmony_ci struct nfcmrvl_fw_dnld *fw_dnld = &priv->fw_dnld; 4978c2ecf20Sopenharmony_ci int res; 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci if (!priv->support_fw_dnld) 5008c2ecf20Sopenharmony_ci return -ENOTSUPP; 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci if (!firmware_name || !firmware_name[0]) 5038c2ecf20Sopenharmony_ci return -EINVAL; 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci strcpy(fw_dnld->name, firmware_name); 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci /* 5088c2ecf20Sopenharmony_ci * Retrieve FW binary file and parse it to initialize FW download 5098c2ecf20Sopenharmony_ci * state machine. 5108c2ecf20Sopenharmony_ci */ 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci /* Retrieve FW binary */ 5138c2ecf20Sopenharmony_ci res = request_firmware(&fw_dnld->fw, firmware_name, 5148c2ecf20Sopenharmony_ci &ndev->nfc_dev->dev); 5158c2ecf20Sopenharmony_ci if (res < 0) { 5168c2ecf20Sopenharmony_ci nfc_err(priv->dev, "failed to retrieve FW %s", firmware_name); 5178c2ecf20Sopenharmony_ci return -ENOENT; 5188c2ecf20Sopenharmony_ci } 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci fw_dnld->header = (const struct nfcmrvl_fw *) priv->fw_dnld.fw->data; 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci if (fw_dnld->header->magic != NFCMRVL_FW_MAGIC || 5238c2ecf20Sopenharmony_ci fw_dnld->header->phy != priv->phy) { 5248c2ecf20Sopenharmony_ci nfc_err(priv->dev, "bad firmware binary %s magic=0x%x phy=%d", 5258c2ecf20Sopenharmony_ci firmware_name, fw_dnld->header->magic, 5268c2ecf20Sopenharmony_ci fw_dnld->header->phy); 5278c2ecf20Sopenharmony_ci release_firmware(fw_dnld->fw); 5288c2ecf20Sopenharmony_ci fw_dnld->header = NULL; 5298c2ecf20Sopenharmony_ci return -EINVAL; 5308c2ecf20Sopenharmony_ci } 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci if (fw_dnld->header->helper.offset != 0) { 5338c2ecf20Sopenharmony_ci nfc_info(priv->dev, "loading helper"); 5348c2ecf20Sopenharmony_ci fw_dnld->binary_config = &fw_dnld->header->helper; 5358c2ecf20Sopenharmony_ci } else { 5368c2ecf20Sopenharmony_ci nfc_info(priv->dev, "loading firmware"); 5378c2ecf20Sopenharmony_ci fw_dnld->binary_config = &fw_dnld->header->firmware; 5388c2ecf20Sopenharmony_ci } 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci /* Configure a timer for timeout */ 5418c2ecf20Sopenharmony_ci timer_setup(&priv->fw_dnld.timer, fw_dnld_timeout, 0); 5428c2ecf20Sopenharmony_ci mod_timer(&priv->fw_dnld.timer, 5438c2ecf20Sopenharmony_ci jiffies + msecs_to_jiffies(FW_DNLD_TIMEOUT)); 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci /* Ronfigure HI to be sure that it is the bootrom values */ 5468c2ecf20Sopenharmony_ci priv->if_ops->nci_update_config(priv, 5478c2ecf20Sopenharmony_ci &fw_dnld->header->bootrom.config); 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci /* Allow first command */ 5508c2ecf20Sopenharmony_ci atomic_set(&priv->ndev->cmd_cnt, 1); 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci /* First, reset the chip */ 5538c2ecf20Sopenharmony_ci priv->fw_dnld.state = STATE_RESET; 5548c2ecf20Sopenharmony_ci nfcmrvl_chip_reset(priv); 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci /* Now wait for CORE_RESET_NTF or timeout */ 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci return 0; 5598c2ecf20Sopenharmony_ci} 560