18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------- 38c2ecf20Sopenharmony_ci * Copyright (C) 2014-2016, Intel Corporation 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * ------------------------------------------------------------------------- 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/module.h> 98c2ecf20Sopenharmony_ci#include <linux/nfc.h> 108c2ecf20Sopenharmony_ci#include <linux/i2c.h> 118c2ecf20Sopenharmony_ci#include <linux/delay.h> 128c2ecf20Sopenharmony_ci#include <linux/firmware.h> 138c2ecf20Sopenharmony_ci#include <net/nfc/nci_core.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include "fdp.h" 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#define FDP_OTP_PATCH_NAME "otp.bin" 188c2ecf20Sopenharmony_ci#define FDP_RAM_PATCH_NAME "ram.bin" 198c2ecf20Sopenharmony_ci#define FDP_FW_HEADER_SIZE 576 208c2ecf20Sopenharmony_ci#define FDP_FW_UPDATE_SLEEP 1000 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#define NCI_GET_VERSION_TIMEOUT 8000 238c2ecf20Sopenharmony_ci#define NCI_PATCH_REQUEST_TIMEOUT 8000 248c2ecf20Sopenharmony_ci#define FDP_PATCH_CONN_DEST 0xC2 258c2ecf20Sopenharmony_ci#define FDP_PATCH_CONN_PARAM_TYPE 0xA0 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define NCI_PATCH_TYPE_RAM 0x00 288c2ecf20Sopenharmony_ci#define NCI_PATCH_TYPE_OTP 0x01 298c2ecf20Sopenharmony_ci#define NCI_PATCH_TYPE_EOT 0xFF 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#define NCI_PARAM_ID_FW_RAM_VERSION 0xA0 328c2ecf20Sopenharmony_ci#define NCI_PARAM_ID_FW_OTP_VERSION 0xA1 338c2ecf20Sopenharmony_ci#define NCI_PARAM_ID_OTP_LIMITED_VERSION 0xC5 348c2ecf20Sopenharmony_ci#define NCI_PARAM_ID_KEY_INDEX_ID 0xC6 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#define NCI_GID_PROP 0x0F 378c2ecf20Sopenharmony_ci#define NCI_OP_PROP_PATCH_OID 0x08 388c2ecf20Sopenharmony_ci#define NCI_OP_PROP_SET_PDATA_OID 0x23 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistruct fdp_nci_info { 418c2ecf20Sopenharmony_ci struct nfc_phy_ops *phy_ops; 428c2ecf20Sopenharmony_ci struct fdp_i2c_phy *phy; 438c2ecf20Sopenharmony_ci struct nci_dev *ndev; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci const struct firmware *otp_patch; 468c2ecf20Sopenharmony_ci const struct firmware *ram_patch; 478c2ecf20Sopenharmony_ci u32 otp_patch_version; 488c2ecf20Sopenharmony_ci u32 ram_patch_version; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci u32 otp_version; 518c2ecf20Sopenharmony_ci u32 ram_version; 528c2ecf20Sopenharmony_ci u32 limited_otp_version; 538c2ecf20Sopenharmony_ci u8 key_index; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci u8 *fw_vsc_cfg; 568c2ecf20Sopenharmony_ci u8 clock_type; 578c2ecf20Sopenharmony_ci u32 clock_freq; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci atomic_t data_pkt_counter; 608c2ecf20Sopenharmony_ci void (*data_pkt_counter_cb)(struct nci_dev *ndev); 618c2ecf20Sopenharmony_ci u8 setup_patch_sent; 628c2ecf20Sopenharmony_ci u8 setup_patch_ntf; 638c2ecf20Sopenharmony_ci u8 setup_patch_status; 648c2ecf20Sopenharmony_ci u8 setup_reset_ntf; 658c2ecf20Sopenharmony_ci wait_queue_head_t setup_wq; 668c2ecf20Sopenharmony_ci}; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic u8 nci_core_get_config_otp_ram_version[5] = { 698c2ecf20Sopenharmony_ci 0x04, 708c2ecf20Sopenharmony_ci NCI_PARAM_ID_FW_RAM_VERSION, 718c2ecf20Sopenharmony_ci NCI_PARAM_ID_FW_OTP_VERSION, 728c2ecf20Sopenharmony_ci NCI_PARAM_ID_OTP_LIMITED_VERSION, 738c2ecf20Sopenharmony_ci NCI_PARAM_ID_KEY_INDEX_ID 748c2ecf20Sopenharmony_ci}; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistruct nci_core_get_config_rsp { 778c2ecf20Sopenharmony_ci u8 status; 788c2ecf20Sopenharmony_ci u8 count; 798c2ecf20Sopenharmony_ci u8 data[]; 808c2ecf20Sopenharmony_ci}; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic int fdp_nci_create_conn(struct nci_dev *ndev) 838c2ecf20Sopenharmony_ci{ 848c2ecf20Sopenharmony_ci struct fdp_nci_info *info = nci_get_drvdata(ndev); 858c2ecf20Sopenharmony_ci struct core_conn_create_dest_spec_params param; 868c2ecf20Sopenharmony_ci int r; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci /* proprietary destination specific paramerer without value */ 898c2ecf20Sopenharmony_ci param.type = FDP_PATCH_CONN_PARAM_TYPE; 908c2ecf20Sopenharmony_ci param.length = 0x00; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci r = nci_core_conn_create(info->ndev, FDP_PATCH_CONN_DEST, 1, 938c2ecf20Sopenharmony_ci sizeof(param), ¶m); 948c2ecf20Sopenharmony_ci if (r) 958c2ecf20Sopenharmony_ci return r; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci return nci_get_conn_info_by_dest_type_params(ndev, 988c2ecf20Sopenharmony_ci FDP_PATCH_CONN_DEST, NULL); 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic inline int fdp_nci_get_versions(struct nci_dev *ndev) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci return nci_core_cmd(ndev, NCI_OP_CORE_GET_CONFIG_CMD, 1048c2ecf20Sopenharmony_ci sizeof(nci_core_get_config_otp_ram_version), 1058c2ecf20Sopenharmony_ci (__u8 *) &nci_core_get_config_otp_ram_version); 1068c2ecf20Sopenharmony_ci} 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cistatic inline int fdp_nci_patch_cmd(struct nci_dev *ndev, u8 type) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci return nci_prop_cmd(ndev, NCI_OP_PROP_PATCH_OID, sizeof(type), &type); 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cistatic inline int fdp_nci_set_production_data(struct nci_dev *ndev, u8 len, 1148c2ecf20Sopenharmony_ci char *data) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci return nci_prop_cmd(ndev, NCI_OP_PROP_SET_PDATA_OID, len, data); 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cistatic int fdp_nci_set_clock(struct nci_dev *ndev, u8 clock_type, 1208c2ecf20Sopenharmony_ci u32 clock_freq) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci u32 fc = 13560; 1238c2ecf20Sopenharmony_ci u32 nd, num, delta; 1248c2ecf20Sopenharmony_ci char data[9]; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci nd = (24 * fc) / clock_freq; 1278c2ecf20Sopenharmony_ci delta = 24 * fc - nd * clock_freq; 1288c2ecf20Sopenharmony_ci num = (32768 * delta) / clock_freq; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci data[0] = 0x00; 1318c2ecf20Sopenharmony_ci data[1] = 0x00; 1328c2ecf20Sopenharmony_ci data[2] = 0x00; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci data[3] = 0x10; 1358c2ecf20Sopenharmony_ci data[4] = 0x04; 1368c2ecf20Sopenharmony_ci data[5] = num & 0xFF; 1378c2ecf20Sopenharmony_ci data[6] = (num >> 8) & 0xff; 1388c2ecf20Sopenharmony_ci data[7] = nd; 1398c2ecf20Sopenharmony_ci data[8] = clock_type; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci return fdp_nci_set_production_data(ndev, 9, data); 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_cistatic void fdp_nci_send_patch_cb(struct nci_dev *ndev) 1458c2ecf20Sopenharmony_ci{ 1468c2ecf20Sopenharmony_ci struct fdp_nci_info *info = nci_get_drvdata(ndev); 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci info->setup_patch_sent = 1; 1498c2ecf20Sopenharmony_ci wake_up(&info->setup_wq); 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci/** 1538c2ecf20Sopenharmony_ci * Register a packet sent counter and a callback 1548c2ecf20Sopenharmony_ci * 1558c2ecf20Sopenharmony_ci * We have no other way of knowing when all firmware packets were sent out 1568c2ecf20Sopenharmony_ci * on the i2c bus. We need to know that in order to close the connection and 1578c2ecf20Sopenharmony_ci * send the patch end message. 1588c2ecf20Sopenharmony_ci */ 1598c2ecf20Sopenharmony_cistatic void fdp_nci_set_data_pkt_counter(struct nci_dev *ndev, 1608c2ecf20Sopenharmony_ci void (*cb)(struct nci_dev *ndev), int count) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci struct fdp_nci_info *info = nci_get_drvdata(ndev); 1638c2ecf20Sopenharmony_ci struct device *dev = &info->phy->i2c_dev->dev; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci dev_dbg(dev, "NCI data pkt counter %d\n", count); 1668c2ecf20Sopenharmony_ci atomic_set(&info->data_pkt_counter, count); 1678c2ecf20Sopenharmony_ci info->data_pkt_counter_cb = cb; 1688c2ecf20Sopenharmony_ci} 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci/** 1718c2ecf20Sopenharmony_ci * The device is expecting a stream of packets. All packets need to 1728c2ecf20Sopenharmony_ci * have the PBF flag set to 0x0 (last packet) even if the firmware 1738c2ecf20Sopenharmony_ci * file is segmented and there are multiple packets. If we give the 1748c2ecf20Sopenharmony_ci * whole firmware to nci_send_data it will segment it and it will set 1758c2ecf20Sopenharmony_ci * the PBF flag to 0x01 so we need to do the segmentation here. 1768c2ecf20Sopenharmony_ci * 1778c2ecf20Sopenharmony_ci * The firmware will be analyzed and applied when we send NCI_OP_PROP_PATCH_CMD 1788c2ecf20Sopenharmony_ci * command with NCI_PATCH_TYPE_EOT parameter. The device will send a 1798c2ecf20Sopenharmony_ci * NFCC_PATCH_NTF packaet and a NCI_OP_CORE_RESET_NTF packet. 1808c2ecf20Sopenharmony_ci */ 1818c2ecf20Sopenharmony_cistatic int fdp_nci_send_patch(struct nci_dev *ndev, u8 conn_id, u8 type) 1828c2ecf20Sopenharmony_ci{ 1838c2ecf20Sopenharmony_ci struct fdp_nci_info *info = nci_get_drvdata(ndev); 1848c2ecf20Sopenharmony_ci const struct firmware *fw; 1858c2ecf20Sopenharmony_ci struct sk_buff *skb; 1868c2ecf20Sopenharmony_ci unsigned long len; 1878c2ecf20Sopenharmony_ci int max_size, payload_size; 1888c2ecf20Sopenharmony_ci int rc = 0; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci if ((type == NCI_PATCH_TYPE_OTP && !info->otp_patch) || 1918c2ecf20Sopenharmony_ci (type == NCI_PATCH_TYPE_RAM && !info->ram_patch)) 1928c2ecf20Sopenharmony_ci return -EINVAL; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci if (type == NCI_PATCH_TYPE_OTP) 1958c2ecf20Sopenharmony_ci fw = info->otp_patch; 1968c2ecf20Sopenharmony_ci else 1978c2ecf20Sopenharmony_ci fw = info->ram_patch; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci max_size = nci_conn_max_data_pkt_payload_size(ndev, conn_id); 2008c2ecf20Sopenharmony_ci if (max_size <= 0) 2018c2ecf20Sopenharmony_ci return -EINVAL; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci len = fw->size; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci fdp_nci_set_data_pkt_counter(ndev, fdp_nci_send_patch_cb, 2068c2ecf20Sopenharmony_ci DIV_ROUND_UP(fw->size, max_size)); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci while (len) { 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci payload_size = min_t(unsigned long, max_size, len); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci skb = nci_skb_alloc(ndev, (NCI_CTRL_HDR_SIZE + payload_size), 2138c2ecf20Sopenharmony_ci GFP_KERNEL); 2148c2ecf20Sopenharmony_ci if (!skb) { 2158c2ecf20Sopenharmony_ci fdp_nci_set_data_pkt_counter(ndev, NULL, 0); 2168c2ecf20Sopenharmony_ci return -ENOMEM; 2178c2ecf20Sopenharmony_ci } 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci skb_reserve(skb, NCI_CTRL_HDR_SIZE); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci skb_put_data(skb, fw->data + (fw->size - len), payload_size); 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci rc = nci_send_data(ndev, conn_id, skb); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci if (rc) { 2278c2ecf20Sopenharmony_ci fdp_nci_set_data_pkt_counter(ndev, NULL, 0); 2288c2ecf20Sopenharmony_ci return rc; 2298c2ecf20Sopenharmony_ci } 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci len -= payload_size; 2328c2ecf20Sopenharmony_ci } 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci return rc; 2358c2ecf20Sopenharmony_ci} 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_cistatic int fdp_nci_open(struct nci_dev *ndev) 2388c2ecf20Sopenharmony_ci{ 2398c2ecf20Sopenharmony_ci int r; 2408c2ecf20Sopenharmony_ci struct fdp_nci_info *info = nci_get_drvdata(ndev); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci r = info->phy_ops->enable(info->phy); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci return r; 2458c2ecf20Sopenharmony_ci} 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_cistatic int fdp_nci_close(struct nci_dev *ndev) 2488c2ecf20Sopenharmony_ci{ 2498c2ecf20Sopenharmony_ci return 0; 2508c2ecf20Sopenharmony_ci} 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_cistatic int fdp_nci_send(struct nci_dev *ndev, struct sk_buff *skb) 2538c2ecf20Sopenharmony_ci{ 2548c2ecf20Sopenharmony_ci struct fdp_nci_info *info = nci_get_drvdata(ndev); 2558c2ecf20Sopenharmony_ci int ret; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci if (atomic_dec_and_test(&info->data_pkt_counter)) 2588c2ecf20Sopenharmony_ci info->data_pkt_counter_cb(ndev); 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci ret = info->phy_ops->write(info->phy, skb); 2618c2ecf20Sopenharmony_ci if (ret < 0) { 2628c2ecf20Sopenharmony_ci kfree_skb(skb); 2638c2ecf20Sopenharmony_ci return ret; 2648c2ecf20Sopenharmony_ci } 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci consume_skb(skb); 2678c2ecf20Sopenharmony_ci return 0; 2688c2ecf20Sopenharmony_ci} 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_cistatic int fdp_nci_request_firmware(struct nci_dev *ndev) 2718c2ecf20Sopenharmony_ci{ 2728c2ecf20Sopenharmony_ci struct fdp_nci_info *info = nci_get_drvdata(ndev); 2738c2ecf20Sopenharmony_ci struct device *dev = &info->phy->i2c_dev->dev; 2748c2ecf20Sopenharmony_ci u8 *data; 2758c2ecf20Sopenharmony_ci int r; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci r = request_firmware(&info->ram_patch, FDP_RAM_PATCH_NAME, dev); 2788c2ecf20Sopenharmony_ci if (r < 0) { 2798c2ecf20Sopenharmony_ci nfc_err(dev, "RAM patch request error\n"); 2808c2ecf20Sopenharmony_ci goto error; 2818c2ecf20Sopenharmony_ci } 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci data = (u8 *) info->ram_patch->data; 2848c2ecf20Sopenharmony_ci info->ram_patch_version = 2858c2ecf20Sopenharmony_ci data[FDP_FW_HEADER_SIZE] | 2868c2ecf20Sopenharmony_ci (data[FDP_FW_HEADER_SIZE + 1] << 8) | 2878c2ecf20Sopenharmony_ci (data[FDP_FW_HEADER_SIZE + 2] << 16) | 2888c2ecf20Sopenharmony_ci (data[FDP_FW_HEADER_SIZE + 3] << 24); 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci dev_dbg(dev, "RAM patch version: %d, size: %d\n", 2918c2ecf20Sopenharmony_ci info->ram_patch_version, (int) info->ram_patch->size); 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci r = request_firmware(&info->otp_patch, FDP_OTP_PATCH_NAME, dev); 2958c2ecf20Sopenharmony_ci if (r < 0) { 2968c2ecf20Sopenharmony_ci nfc_err(dev, "OTP patch request error\n"); 2978c2ecf20Sopenharmony_ci goto out; 2988c2ecf20Sopenharmony_ci } 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci data = (u8 *) info->otp_patch->data; 3018c2ecf20Sopenharmony_ci info->otp_patch_version = 3028c2ecf20Sopenharmony_ci data[FDP_FW_HEADER_SIZE] | 3038c2ecf20Sopenharmony_ci (data[FDP_FW_HEADER_SIZE + 1] << 8) | 3048c2ecf20Sopenharmony_ci (data[FDP_FW_HEADER_SIZE+2] << 16) | 3058c2ecf20Sopenharmony_ci (data[FDP_FW_HEADER_SIZE+3] << 24); 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci dev_dbg(dev, "OTP patch version: %d, size: %d\n", 3088c2ecf20Sopenharmony_ci info->otp_patch_version, (int) info->otp_patch->size); 3098c2ecf20Sopenharmony_ciout: 3108c2ecf20Sopenharmony_ci return 0; 3118c2ecf20Sopenharmony_cierror: 3128c2ecf20Sopenharmony_ci return r; 3138c2ecf20Sopenharmony_ci} 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_cistatic void fdp_nci_release_firmware(struct nci_dev *ndev) 3168c2ecf20Sopenharmony_ci{ 3178c2ecf20Sopenharmony_ci struct fdp_nci_info *info = nci_get_drvdata(ndev); 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci if (info->otp_patch) { 3208c2ecf20Sopenharmony_ci release_firmware(info->otp_patch); 3218c2ecf20Sopenharmony_ci info->otp_patch = NULL; 3228c2ecf20Sopenharmony_ci } 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci if (info->ram_patch) { 3258c2ecf20Sopenharmony_ci release_firmware(info->ram_patch); 3268c2ecf20Sopenharmony_ci info->ram_patch = NULL; 3278c2ecf20Sopenharmony_ci } 3288c2ecf20Sopenharmony_ci} 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_cistatic int fdp_nci_patch_otp(struct nci_dev *ndev) 3318c2ecf20Sopenharmony_ci{ 3328c2ecf20Sopenharmony_ci struct fdp_nci_info *info = nci_get_drvdata(ndev); 3338c2ecf20Sopenharmony_ci struct device *dev = &info->phy->i2c_dev->dev; 3348c2ecf20Sopenharmony_ci int conn_id; 3358c2ecf20Sopenharmony_ci int r = 0; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci if (info->otp_version >= info->otp_patch_version) 3388c2ecf20Sopenharmony_ci goto out; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci info->setup_patch_sent = 0; 3418c2ecf20Sopenharmony_ci info->setup_reset_ntf = 0; 3428c2ecf20Sopenharmony_ci info->setup_patch_ntf = 0; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci /* Patch init request */ 3458c2ecf20Sopenharmony_ci r = fdp_nci_patch_cmd(ndev, NCI_PATCH_TYPE_OTP); 3468c2ecf20Sopenharmony_ci if (r) 3478c2ecf20Sopenharmony_ci goto out; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci /* Patch data connection creation */ 3508c2ecf20Sopenharmony_ci conn_id = fdp_nci_create_conn(ndev); 3518c2ecf20Sopenharmony_ci if (conn_id < 0) { 3528c2ecf20Sopenharmony_ci r = conn_id; 3538c2ecf20Sopenharmony_ci goto out; 3548c2ecf20Sopenharmony_ci } 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci /* Send the patch over the data connection */ 3578c2ecf20Sopenharmony_ci r = fdp_nci_send_patch(ndev, conn_id, NCI_PATCH_TYPE_OTP); 3588c2ecf20Sopenharmony_ci if (r) 3598c2ecf20Sopenharmony_ci goto out; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci /* Wait for all the packets to be send over i2c */ 3628c2ecf20Sopenharmony_ci wait_event_interruptible(info->setup_wq, 3638c2ecf20Sopenharmony_ci info->setup_patch_sent == 1); 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci /* make sure that the NFCC processed the last data packet */ 3668c2ecf20Sopenharmony_ci msleep(FDP_FW_UPDATE_SLEEP); 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci /* Close the data connection */ 3698c2ecf20Sopenharmony_ci r = nci_core_conn_close(info->ndev, conn_id); 3708c2ecf20Sopenharmony_ci if (r) 3718c2ecf20Sopenharmony_ci goto out; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci /* Patch finish message */ 3748c2ecf20Sopenharmony_ci if (fdp_nci_patch_cmd(ndev, NCI_PATCH_TYPE_EOT)) { 3758c2ecf20Sopenharmony_ci nfc_err(dev, "OTP patch error 0x%x\n", r); 3768c2ecf20Sopenharmony_ci r = -EINVAL; 3778c2ecf20Sopenharmony_ci goto out; 3788c2ecf20Sopenharmony_ci } 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci /* If the patch notification didn't arrive yet, wait for it */ 3818c2ecf20Sopenharmony_ci wait_event_interruptible(info->setup_wq, info->setup_patch_ntf); 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci /* Check if the patching was successful */ 3848c2ecf20Sopenharmony_ci r = info->setup_patch_status; 3858c2ecf20Sopenharmony_ci if (r) { 3868c2ecf20Sopenharmony_ci nfc_err(dev, "OTP patch error 0x%x\n", r); 3878c2ecf20Sopenharmony_ci r = -EINVAL; 3888c2ecf20Sopenharmony_ci goto out; 3898c2ecf20Sopenharmony_ci } 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci /* 3928c2ecf20Sopenharmony_ci * We need to wait for the reset notification before we 3938c2ecf20Sopenharmony_ci * can continue 3948c2ecf20Sopenharmony_ci */ 3958c2ecf20Sopenharmony_ci wait_event_interruptible(info->setup_wq, info->setup_reset_ntf); 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ciout: 3988c2ecf20Sopenharmony_ci return r; 3998c2ecf20Sopenharmony_ci} 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_cistatic int fdp_nci_patch_ram(struct nci_dev *ndev) 4028c2ecf20Sopenharmony_ci{ 4038c2ecf20Sopenharmony_ci struct fdp_nci_info *info = nci_get_drvdata(ndev); 4048c2ecf20Sopenharmony_ci struct device *dev = &info->phy->i2c_dev->dev; 4058c2ecf20Sopenharmony_ci int conn_id; 4068c2ecf20Sopenharmony_ci int r = 0; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci if (info->ram_version >= info->ram_patch_version) 4098c2ecf20Sopenharmony_ci goto out; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci info->setup_patch_sent = 0; 4128c2ecf20Sopenharmony_ci info->setup_reset_ntf = 0; 4138c2ecf20Sopenharmony_ci info->setup_patch_ntf = 0; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci /* Patch init request */ 4168c2ecf20Sopenharmony_ci r = fdp_nci_patch_cmd(ndev, NCI_PATCH_TYPE_RAM); 4178c2ecf20Sopenharmony_ci if (r) 4188c2ecf20Sopenharmony_ci goto out; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci /* Patch data connection creation */ 4218c2ecf20Sopenharmony_ci conn_id = fdp_nci_create_conn(ndev); 4228c2ecf20Sopenharmony_ci if (conn_id < 0) { 4238c2ecf20Sopenharmony_ci r = conn_id; 4248c2ecf20Sopenharmony_ci goto out; 4258c2ecf20Sopenharmony_ci } 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci /* Send the patch over the data connection */ 4288c2ecf20Sopenharmony_ci r = fdp_nci_send_patch(ndev, conn_id, NCI_PATCH_TYPE_RAM); 4298c2ecf20Sopenharmony_ci if (r) 4308c2ecf20Sopenharmony_ci goto out; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci /* Wait for all the packets to be send over i2c */ 4338c2ecf20Sopenharmony_ci wait_event_interruptible(info->setup_wq, 4348c2ecf20Sopenharmony_ci info->setup_patch_sent == 1); 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci /* make sure that the NFCC processed the last data packet */ 4378c2ecf20Sopenharmony_ci msleep(FDP_FW_UPDATE_SLEEP); 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci /* Close the data connection */ 4408c2ecf20Sopenharmony_ci r = nci_core_conn_close(info->ndev, conn_id); 4418c2ecf20Sopenharmony_ci if (r) 4428c2ecf20Sopenharmony_ci goto out; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci /* Patch finish message */ 4458c2ecf20Sopenharmony_ci if (fdp_nci_patch_cmd(ndev, NCI_PATCH_TYPE_EOT)) { 4468c2ecf20Sopenharmony_ci nfc_err(dev, "RAM patch error 0x%x\n", r); 4478c2ecf20Sopenharmony_ci r = -EINVAL; 4488c2ecf20Sopenharmony_ci goto out; 4498c2ecf20Sopenharmony_ci } 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci /* If the patch notification didn't arrive yet, wait for it */ 4528c2ecf20Sopenharmony_ci wait_event_interruptible(info->setup_wq, info->setup_patch_ntf); 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci /* Check if the patching was successful */ 4558c2ecf20Sopenharmony_ci r = info->setup_patch_status; 4568c2ecf20Sopenharmony_ci if (r) { 4578c2ecf20Sopenharmony_ci nfc_err(dev, "RAM patch error 0x%x\n", r); 4588c2ecf20Sopenharmony_ci r = -EINVAL; 4598c2ecf20Sopenharmony_ci goto out; 4608c2ecf20Sopenharmony_ci } 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci /* 4638c2ecf20Sopenharmony_ci * We need to wait for the reset notification before we 4648c2ecf20Sopenharmony_ci * can continue 4658c2ecf20Sopenharmony_ci */ 4668c2ecf20Sopenharmony_ci wait_event_interruptible(info->setup_wq, info->setup_reset_ntf); 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ciout: 4698c2ecf20Sopenharmony_ci return r; 4708c2ecf20Sopenharmony_ci} 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_cistatic int fdp_nci_setup(struct nci_dev *ndev) 4738c2ecf20Sopenharmony_ci{ 4748c2ecf20Sopenharmony_ci /* Format: total length followed by an NCI packet */ 4758c2ecf20Sopenharmony_ci struct fdp_nci_info *info = nci_get_drvdata(ndev); 4768c2ecf20Sopenharmony_ci struct device *dev = &info->phy->i2c_dev->dev; 4778c2ecf20Sopenharmony_ci int r; 4788c2ecf20Sopenharmony_ci u8 patched = 0; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci r = nci_core_init(ndev); 4818c2ecf20Sopenharmony_ci if (r) 4828c2ecf20Sopenharmony_ci goto error; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci /* Get RAM and OTP version */ 4858c2ecf20Sopenharmony_ci r = fdp_nci_get_versions(ndev); 4868c2ecf20Sopenharmony_ci if (r) 4878c2ecf20Sopenharmony_ci goto error; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci /* Load firmware from disk */ 4908c2ecf20Sopenharmony_ci r = fdp_nci_request_firmware(ndev); 4918c2ecf20Sopenharmony_ci if (r) 4928c2ecf20Sopenharmony_ci goto error; 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci /* Update OTP */ 4958c2ecf20Sopenharmony_ci if (info->otp_version < info->otp_patch_version) { 4968c2ecf20Sopenharmony_ci r = fdp_nci_patch_otp(ndev); 4978c2ecf20Sopenharmony_ci if (r) 4988c2ecf20Sopenharmony_ci goto error; 4998c2ecf20Sopenharmony_ci patched = 1; 5008c2ecf20Sopenharmony_ci } 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci /* Update RAM */ 5038c2ecf20Sopenharmony_ci if (info->ram_version < info->ram_patch_version) { 5048c2ecf20Sopenharmony_ci r = fdp_nci_patch_ram(ndev); 5058c2ecf20Sopenharmony_ci if (r) 5068c2ecf20Sopenharmony_ci goto error; 5078c2ecf20Sopenharmony_ci patched = 1; 5088c2ecf20Sopenharmony_ci } 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci /* Release the firmware buffers */ 5118c2ecf20Sopenharmony_ci fdp_nci_release_firmware(ndev); 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci /* If a patch was applied the new version is checked */ 5148c2ecf20Sopenharmony_ci if (patched) { 5158c2ecf20Sopenharmony_ci r = nci_core_init(ndev); 5168c2ecf20Sopenharmony_ci if (r) 5178c2ecf20Sopenharmony_ci goto error; 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci r = fdp_nci_get_versions(ndev); 5208c2ecf20Sopenharmony_ci if (r) 5218c2ecf20Sopenharmony_ci goto error; 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci if (info->otp_version != info->otp_patch_version || 5248c2ecf20Sopenharmony_ci info->ram_version != info->ram_patch_version) { 5258c2ecf20Sopenharmony_ci nfc_err(dev, "Firmware update failed"); 5268c2ecf20Sopenharmony_ci r = -EINVAL; 5278c2ecf20Sopenharmony_ci goto error; 5288c2ecf20Sopenharmony_ci } 5298c2ecf20Sopenharmony_ci } 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci /* 5328c2ecf20Sopenharmony_ci * We initialized the devices but the NFC subsystem expects 5338c2ecf20Sopenharmony_ci * it to not be initialized. 5348c2ecf20Sopenharmony_ci */ 5358c2ecf20Sopenharmony_ci return nci_core_reset(ndev); 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_cierror: 5388c2ecf20Sopenharmony_ci fdp_nci_release_firmware(ndev); 5398c2ecf20Sopenharmony_ci nfc_err(dev, "Setup error %d\n", r); 5408c2ecf20Sopenharmony_ci return r; 5418c2ecf20Sopenharmony_ci} 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_cistatic int fdp_nci_post_setup(struct nci_dev *ndev) 5448c2ecf20Sopenharmony_ci{ 5458c2ecf20Sopenharmony_ci struct fdp_nci_info *info = nci_get_drvdata(ndev); 5468c2ecf20Sopenharmony_ci struct device *dev = &info->phy->i2c_dev->dev; 5478c2ecf20Sopenharmony_ci int r; 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci /* Check if the device has VSC */ 5508c2ecf20Sopenharmony_ci if (info->fw_vsc_cfg && info->fw_vsc_cfg[0]) { 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci /* Set the vendor specific configuration */ 5538c2ecf20Sopenharmony_ci r = fdp_nci_set_production_data(ndev, info->fw_vsc_cfg[3], 5548c2ecf20Sopenharmony_ci &info->fw_vsc_cfg[4]); 5558c2ecf20Sopenharmony_ci if (r) { 5568c2ecf20Sopenharmony_ci nfc_err(dev, "Vendor specific config set error %d\n", 5578c2ecf20Sopenharmony_ci r); 5588c2ecf20Sopenharmony_ci return r; 5598c2ecf20Sopenharmony_ci } 5608c2ecf20Sopenharmony_ci } 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci /* Set clock type and frequency */ 5638c2ecf20Sopenharmony_ci r = fdp_nci_set_clock(ndev, info->clock_type, info->clock_freq); 5648c2ecf20Sopenharmony_ci if (r) { 5658c2ecf20Sopenharmony_ci nfc_err(dev, "Clock set error %d\n", r); 5668c2ecf20Sopenharmony_ci return r; 5678c2ecf20Sopenharmony_ci } 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci /* 5708c2ecf20Sopenharmony_ci * In order to apply the VSC FDP needs a reset 5718c2ecf20Sopenharmony_ci */ 5728c2ecf20Sopenharmony_ci r = nci_core_reset(ndev); 5738c2ecf20Sopenharmony_ci if (r) 5748c2ecf20Sopenharmony_ci return r; 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci /** 5778c2ecf20Sopenharmony_ci * The nci core was initialized when post setup was called 5788c2ecf20Sopenharmony_ci * so we leave it like that 5798c2ecf20Sopenharmony_ci */ 5808c2ecf20Sopenharmony_ci return nci_core_init(ndev); 5818c2ecf20Sopenharmony_ci} 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_cistatic int fdp_nci_core_reset_ntf_packet(struct nci_dev *ndev, 5848c2ecf20Sopenharmony_ci struct sk_buff *skb) 5858c2ecf20Sopenharmony_ci{ 5868c2ecf20Sopenharmony_ci struct fdp_nci_info *info = nci_get_drvdata(ndev); 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci info->setup_reset_ntf = 1; 5898c2ecf20Sopenharmony_ci wake_up(&info->setup_wq); 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci return 0; 5928c2ecf20Sopenharmony_ci} 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_cistatic int fdp_nci_prop_patch_ntf_packet(struct nci_dev *ndev, 5958c2ecf20Sopenharmony_ci struct sk_buff *skb) 5968c2ecf20Sopenharmony_ci{ 5978c2ecf20Sopenharmony_ci struct fdp_nci_info *info = nci_get_drvdata(ndev); 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci info->setup_patch_ntf = 1; 6008c2ecf20Sopenharmony_ci info->setup_patch_status = skb->data[0]; 6018c2ecf20Sopenharmony_ci wake_up(&info->setup_wq); 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci return 0; 6048c2ecf20Sopenharmony_ci} 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_cistatic int fdp_nci_prop_patch_rsp_packet(struct nci_dev *ndev, 6078c2ecf20Sopenharmony_ci struct sk_buff *skb) 6088c2ecf20Sopenharmony_ci{ 6098c2ecf20Sopenharmony_ci struct fdp_nci_info *info = nci_get_drvdata(ndev); 6108c2ecf20Sopenharmony_ci struct device *dev = &info->phy->i2c_dev->dev; 6118c2ecf20Sopenharmony_ci u8 status = skb->data[0]; 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci dev_dbg(dev, "%s: status 0x%x\n", __func__, status); 6148c2ecf20Sopenharmony_ci nci_req_complete(ndev, status); 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci return 0; 6178c2ecf20Sopenharmony_ci} 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_cistatic int fdp_nci_prop_set_production_data_rsp_packet(struct nci_dev *ndev, 6208c2ecf20Sopenharmony_ci struct sk_buff *skb) 6218c2ecf20Sopenharmony_ci{ 6228c2ecf20Sopenharmony_ci struct fdp_nci_info *info = nci_get_drvdata(ndev); 6238c2ecf20Sopenharmony_ci struct device *dev = &info->phy->i2c_dev->dev; 6248c2ecf20Sopenharmony_ci u8 status = skb->data[0]; 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci dev_dbg(dev, "%s: status 0x%x\n", __func__, status); 6278c2ecf20Sopenharmony_ci nci_req_complete(ndev, status); 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci return 0; 6308c2ecf20Sopenharmony_ci} 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_cistatic int fdp_nci_core_get_config_rsp_packet(struct nci_dev *ndev, 6338c2ecf20Sopenharmony_ci struct sk_buff *skb) 6348c2ecf20Sopenharmony_ci{ 6358c2ecf20Sopenharmony_ci struct fdp_nci_info *info = nci_get_drvdata(ndev); 6368c2ecf20Sopenharmony_ci struct device *dev = &info->phy->i2c_dev->dev; 6378c2ecf20Sopenharmony_ci struct nci_core_get_config_rsp *rsp = (void *) skb->data; 6388c2ecf20Sopenharmony_ci u8 i, *p; 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci if (rsp->status == NCI_STATUS_OK) { 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci p = rsp->data; 6438c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) { 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci switch (*p++) { 6468c2ecf20Sopenharmony_ci case NCI_PARAM_ID_FW_RAM_VERSION: 6478c2ecf20Sopenharmony_ci p++; 6488c2ecf20Sopenharmony_ci info->ram_version = le32_to_cpup((__le32 *) p); 6498c2ecf20Sopenharmony_ci p += 4; 6508c2ecf20Sopenharmony_ci break; 6518c2ecf20Sopenharmony_ci case NCI_PARAM_ID_FW_OTP_VERSION: 6528c2ecf20Sopenharmony_ci p++; 6538c2ecf20Sopenharmony_ci info->otp_version = le32_to_cpup((__le32 *) p); 6548c2ecf20Sopenharmony_ci p += 4; 6558c2ecf20Sopenharmony_ci break; 6568c2ecf20Sopenharmony_ci case NCI_PARAM_ID_OTP_LIMITED_VERSION: 6578c2ecf20Sopenharmony_ci p++; 6588c2ecf20Sopenharmony_ci info->otp_version = le32_to_cpup((__le32 *) p); 6598c2ecf20Sopenharmony_ci p += 4; 6608c2ecf20Sopenharmony_ci break; 6618c2ecf20Sopenharmony_ci case NCI_PARAM_ID_KEY_INDEX_ID: 6628c2ecf20Sopenharmony_ci p++; 6638c2ecf20Sopenharmony_ci info->key_index = *p++; 6648c2ecf20Sopenharmony_ci } 6658c2ecf20Sopenharmony_ci } 6668c2ecf20Sopenharmony_ci } 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci dev_dbg(dev, "OTP version %d\n", info->otp_version); 6698c2ecf20Sopenharmony_ci dev_dbg(dev, "RAM version %d\n", info->ram_version); 6708c2ecf20Sopenharmony_ci dev_dbg(dev, "key index %d\n", info->key_index); 6718c2ecf20Sopenharmony_ci dev_dbg(dev, "%s: status 0x%x\n", __func__, rsp->status); 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci nci_req_complete(ndev, rsp->status); 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci return 0; 6768c2ecf20Sopenharmony_ci} 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_cistatic struct nci_driver_ops fdp_core_ops[] = { 6798c2ecf20Sopenharmony_ci { 6808c2ecf20Sopenharmony_ci .opcode = NCI_OP_CORE_GET_CONFIG_RSP, 6818c2ecf20Sopenharmony_ci .rsp = fdp_nci_core_get_config_rsp_packet, 6828c2ecf20Sopenharmony_ci }, 6838c2ecf20Sopenharmony_ci { 6848c2ecf20Sopenharmony_ci .opcode = NCI_OP_CORE_RESET_NTF, 6858c2ecf20Sopenharmony_ci .ntf = fdp_nci_core_reset_ntf_packet, 6868c2ecf20Sopenharmony_ci }, 6878c2ecf20Sopenharmony_ci}; 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_cistatic struct nci_driver_ops fdp_prop_ops[] = { 6908c2ecf20Sopenharmony_ci { 6918c2ecf20Sopenharmony_ci .opcode = nci_opcode_pack(NCI_GID_PROP, NCI_OP_PROP_PATCH_OID), 6928c2ecf20Sopenharmony_ci .rsp = fdp_nci_prop_patch_rsp_packet, 6938c2ecf20Sopenharmony_ci .ntf = fdp_nci_prop_patch_ntf_packet, 6948c2ecf20Sopenharmony_ci }, 6958c2ecf20Sopenharmony_ci { 6968c2ecf20Sopenharmony_ci .opcode = nci_opcode_pack(NCI_GID_PROP, 6978c2ecf20Sopenharmony_ci NCI_OP_PROP_SET_PDATA_OID), 6988c2ecf20Sopenharmony_ci .rsp = fdp_nci_prop_set_production_data_rsp_packet, 6998c2ecf20Sopenharmony_ci }, 7008c2ecf20Sopenharmony_ci}; 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_cistatic struct nci_ops nci_ops = { 7038c2ecf20Sopenharmony_ci .open = fdp_nci_open, 7048c2ecf20Sopenharmony_ci .close = fdp_nci_close, 7058c2ecf20Sopenharmony_ci .send = fdp_nci_send, 7068c2ecf20Sopenharmony_ci .setup = fdp_nci_setup, 7078c2ecf20Sopenharmony_ci .post_setup = fdp_nci_post_setup, 7088c2ecf20Sopenharmony_ci .prop_ops = fdp_prop_ops, 7098c2ecf20Sopenharmony_ci .n_prop_ops = ARRAY_SIZE(fdp_prop_ops), 7108c2ecf20Sopenharmony_ci .core_ops = fdp_core_ops, 7118c2ecf20Sopenharmony_ci .n_core_ops = ARRAY_SIZE(fdp_core_ops), 7128c2ecf20Sopenharmony_ci}; 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ciint fdp_nci_probe(struct fdp_i2c_phy *phy, struct nfc_phy_ops *phy_ops, 7158c2ecf20Sopenharmony_ci struct nci_dev **ndevp, int tx_headroom, 7168c2ecf20Sopenharmony_ci int tx_tailroom, u8 clock_type, u32 clock_freq, 7178c2ecf20Sopenharmony_ci u8 *fw_vsc_cfg) 7188c2ecf20Sopenharmony_ci{ 7198c2ecf20Sopenharmony_ci struct device *dev = &phy->i2c_dev->dev; 7208c2ecf20Sopenharmony_ci struct fdp_nci_info *info; 7218c2ecf20Sopenharmony_ci struct nci_dev *ndev; 7228c2ecf20Sopenharmony_ci u32 protocols; 7238c2ecf20Sopenharmony_ci int r; 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci info = devm_kzalloc(dev, sizeof(struct fdp_nci_info), GFP_KERNEL); 7268c2ecf20Sopenharmony_ci if (!info) 7278c2ecf20Sopenharmony_ci return -ENOMEM; 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci info->phy = phy; 7308c2ecf20Sopenharmony_ci info->phy_ops = phy_ops; 7318c2ecf20Sopenharmony_ci info->clock_type = clock_type; 7328c2ecf20Sopenharmony_ci info->clock_freq = clock_freq; 7338c2ecf20Sopenharmony_ci info->fw_vsc_cfg = fw_vsc_cfg; 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci init_waitqueue_head(&info->setup_wq); 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci protocols = NFC_PROTO_JEWEL_MASK | 7388c2ecf20Sopenharmony_ci NFC_PROTO_MIFARE_MASK | 7398c2ecf20Sopenharmony_ci NFC_PROTO_FELICA_MASK | 7408c2ecf20Sopenharmony_ci NFC_PROTO_ISO14443_MASK | 7418c2ecf20Sopenharmony_ci NFC_PROTO_ISO14443_B_MASK | 7428c2ecf20Sopenharmony_ci NFC_PROTO_NFC_DEP_MASK | 7438c2ecf20Sopenharmony_ci NFC_PROTO_ISO15693_MASK; 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci ndev = nci_allocate_device(&nci_ops, protocols, tx_headroom, 7468c2ecf20Sopenharmony_ci tx_tailroom); 7478c2ecf20Sopenharmony_ci if (!ndev) { 7488c2ecf20Sopenharmony_ci nfc_err(dev, "Cannot allocate nfc ndev\n"); 7498c2ecf20Sopenharmony_ci return -ENOMEM; 7508c2ecf20Sopenharmony_ci } 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci r = nci_register_device(ndev); 7538c2ecf20Sopenharmony_ci if (r) 7548c2ecf20Sopenharmony_ci goto err_regdev; 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci *ndevp = ndev; 7578c2ecf20Sopenharmony_ci info->ndev = ndev; 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci nci_set_drvdata(ndev, info); 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci return 0; 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_cierr_regdev: 7648c2ecf20Sopenharmony_ci nci_free_device(ndev); 7658c2ecf20Sopenharmony_ci return r; 7668c2ecf20Sopenharmony_ci} 7678c2ecf20Sopenharmony_ciEXPORT_SYMBOL(fdp_nci_probe); 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_civoid fdp_nci_remove(struct nci_dev *ndev) 7708c2ecf20Sopenharmony_ci{ 7718c2ecf20Sopenharmony_ci nci_unregister_device(ndev); 7728c2ecf20Sopenharmony_ci nci_free_device(ndev); 7738c2ecf20Sopenharmony_ci} 7748c2ecf20Sopenharmony_ciEXPORT_SYMBOL(fdp_nci_remove); 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 7778c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("NFC NCI driver for Intel Fields Peak NFC controller"); 7788c2ecf20Sopenharmony_ciMODULE_AUTHOR("Robert Dolca <robert.dolca@intel.com>"); 779