162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* ------------------------------------------------------------------------- 362306a36Sopenharmony_ci * Copyright (C) 2014-2016, Intel Corporation 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * ------------------------------------------------------------------------- 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/module.h> 962306a36Sopenharmony_ci#include <linux/nfc.h> 1062306a36Sopenharmony_ci#include <linux/i2c.h> 1162306a36Sopenharmony_ci#include <linux/delay.h> 1262306a36Sopenharmony_ci#include <linux/firmware.h> 1362306a36Sopenharmony_ci#include <net/nfc/nci_core.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include "fdp.h" 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#define FDP_OTP_PATCH_NAME "otp.bin" 1862306a36Sopenharmony_ci#define FDP_RAM_PATCH_NAME "ram.bin" 1962306a36Sopenharmony_ci#define FDP_FW_HEADER_SIZE 576 2062306a36Sopenharmony_ci#define FDP_FW_UPDATE_SLEEP 1000 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#define NCI_GET_VERSION_TIMEOUT 8000 2362306a36Sopenharmony_ci#define NCI_PATCH_REQUEST_TIMEOUT 8000 2462306a36Sopenharmony_ci#define FDP_PATCH_CONN_DEST 0xC2 2562306a36Sopenharmony_ci#define FDP_PATCH_CONN_PARAM_TYPE 0xA0 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#define NCI_PATCH_TYPE_RAM 0x00 2862306a36Sopenharmony_ci#define NCI_PATCH_TYPE_OTP 0x01 2962306a36Sopenharmony_ci#define NCI_PATCH_TYPE_EOT 0xFF 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#define NCI_PARAM_ID_FW_RAM_VERSION 0xA0 3262306a36Sopenharmony_ci#define NCI_PARAM_ID_FW_OTP_VERSION 0xA1 3362306a36Sopenharmony_ci#define NCI_PARAM_ID_OTP_LIMITED_VERSION 0xC5 3462306a36Sopenharmony_ci#define NCI_PARAM_ID_KEY_INDEX_ID 0xC6 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#define NCI_GID_PROP 0x0F 3762306a36Sopenharmony_ci#define NCI_OP_PROP_PATCH_OID 0x08 3862306a36Sopenharmony_ci#define NCI_OP_PROP_SET_PDATA_OID 0x23 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistruct fdp_nci_info { 4162306a36Sopenharmony_ci const struct nfc_phy_ops *phy_ops; 4262306a36Sopenharmony_ci struct fdp_i2c_phy *phy; 4362306a36Sopenharmony_ci struct nci_dev *ndev; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci const struct firmware *otp_patch; 4662306a36Sopenharmony_ci const struct firmware *ram_patch; 4762306a36Sopenharmony_ci u32 otp_patch_version; 4862306a36Sopenharmony_ci u32 ram_patch_version; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci u32 otp_version; 5162306a36Sopenharmony_ci u32 ram_version; 5262306a36Sopenharmony_ci u32 limited_otp_version; 5362306a36Sopenharmony_ci u8 key_index; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci const u8 *fw_vsc_cfg; 5662306a36Sopenharmony_ci u8 clock_type; 5762306a36Sopenharmony_ci u32 clock_freq; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci atomic_t data_pkt_counter; 6062306a36Sopenharmony_ci void (*data_pkt_counter_cb)(struct nci_dev *ndev); 6162306a36Sopenharmony_ci u8 setup_patch_sent; 6262306a36Sopenharmony_ci u8 setup_patch_ntf; 6362306a36Sopenharmony_ci u8 setup_patch_status; 6462306a36Sopenharmony_ci u8 setup_reset_ntf; 6562306a36Sopenharmony_ci wait_queue_head_t setup_wq; 6662306a36Sopenharmony_ci}; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic const u8 nci_core_get_config_otp_ram_version[5] = { 6962306a36Sopenharmony_ci 0x04, 7062306a36Sopenharmony_ci NCI_PARAM_ID_FW_RAM_VERSION, 7162306a36Sopenharmony_ci NCI_PARAM_ID_FW_OTP_VERSION, 7262306a36Sopenharmony_ci NCI_PARAM_ID_OTP_LIMITED_VERSION, 7362306a36Sopenharmony_ci NCI_PARAM_ID_KEY_INDEX_ID 7462306a36Sopenharmony_ci}; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistruct nci_core_get_config_rsp { 7762306a36Sopenharmony_ci u8 status; 7862306a36Sopenharmony_ci u8 count; 7962306a36Sopenharmony_ci u8 data[]; 8062306a36Sopenharmony_ci}; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic int fdp_nci_create_conn(struct nci_dev *ndev) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci struct fdp_nci_info *info = nci_get_drvdata(ndev); 8562306a36Sopenharmony_ci struct core_conn_create_dest_spec_params param; 8662306a36Sopenharmony_ci int r; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci /* proprietary destination specific paramerer without value */ 8962306a36Sopenharmony_ci param.type = FDP_PATCH_CONN_PARAM_TYPE; 9062306a36Sopenharmony_ci param.length = 0x00; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci r = nci_core_conn_create(info->ndev, FDP_PATCH_CONN_DEST, 1, 9362306a36Sopenharmony_ci sizeof(param), ¶m); 9462306a36Sopenharmony_ci if (r) 9562306a36Sopenharmony_ci return r; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci return nci_get_conn_info_by_dest_type_params(ndev, 9862306a36Sopenharmony_ci FDP_PATCH_CONN_DEST, NULL); 9962306a36Sopenharmony_ci} 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_cistatic inline int fdp_nci_get_versions(struct nci_dev *ndev) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci return nci_core_cmd(ndev, NCI_OP_CORE_GET_CONFIG_CMD, 10462306a36Sopenharmony_ci sizeof(nci_core_get_config_otp_ram_version), 10562306a36Sopenharmony_ci (__u8 *) &nci_core_get_config_otp_ram_version); 10662306a36Sopenharmony_ci} 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistatic inline int fdp_nci_patch_cmd(struct nci_dev *ndev, u8 type) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci return nci_prop_cmd(ndev, NCI_OP_PROP_PATCH_OID, sizeof(type), &type); 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cistatic inline int fdp_nci_set_production_data(struct nci_dev *ndev, u8 len, 11462306a36Sopenharmony_ci const char *data) 11562306a36Sopenharmony_ci{ 11662306a36Sopenharmony_ci return nci_prop_cmd(ndev, NCI_OP_PROP_SET_PDATA_OID, len, data); 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic int fdp_nci_set_clock(struct nci_dev *ndev, u8 clock_type, 12062306a36Sopenharmony_ci u32 clock_freq) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci u32 fc = 13560; 12362306a36Sopenharmony_ci u32 nd, num, delta; 12462306a36Sopenharmony_ci char data[9]; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci nd = (24 * fc) / clock_freq; 12762306a36Sopenharmony_ci delta = 24 * fc - nd * clock_freq; 12862306a36Sopenharmony_ci num = (32768 * delta) / clock_freq; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci data[0] = 0x00; 13162306a36Sopenharmony_ci data[1] = 0x00; 13262306a36Sopenharmony_ci data[2] = 0x00; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci data[3] = 0x10; 13562306a36Sopenharmony_ci data[4] = 0x04; 13662306a36Sopenharmony_ci data[5] = num & 0xFF; 13762306a36Sopenharmony_ci data[6] = (num >> 8) & 0xff; 13862306a36Sopenharmony_ci data[7] = nd; 13962306a36Sopenharmony_ci data[8] = clock_type; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci return fdp_nci_set_production_data(ndev, 9, data); 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic void fdp_nci_send_patch_cb(struct nci_dev *ndev) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci struct fdp_nci_info *info = nci_get_drvdata(ndev); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci info->setup_patch_sent = 1; 14962306a36Sopenharmony_ci wake_up(&info->setup_wq); 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci/* 15362306a36Sopenharmony_ci * Register a packet sent counter and a callback 15462306a36Sopenharmony_ci * 15562306a36Sopenharmony_ci * We have no other way of knowing when all firmware packets were sent out 15662306a36Sopenharmony_ci * on the i2c bus. We need to know that in order to close the connection and 15762306a36Sopenharmony_ci * send the patch end message. 15862306a36Sopenharmony_ci */ 15962306a36Sopenharmony_cistatic void fdp_nci_set_data_pkt_counter(struct nci_dev *ndev, 16062306a36Sopenharmony_ci void (*cb)(struct nci_dev *ndev), int count) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci struct fdp_nci_info *info = nci_get_drvdata(ndev); 16362306a36Sopenharmony_ci struct device *dev = &info->phy->i2c_dev->dev; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci dev_dbg(dev, "NCI data pkt counter %d\n", count); 16662306a36Sopenharmony_ci atomic_set(&info->data_pkt_counter, count); 16762306a36Sopenharmony_ci info->data_pkt_counter_cb = cb; 16862306a36Sopenharmony_ci} 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci/* 17162306a36Sopenharmony_ci * The device is expecting a stream of packets. All packets need to 17262306a36Sopenharmony_ci * have the PBF flag set to 0x0 (last packet) even if the firmware 17362306a36Sopenharmony_ci * file is segmented and there are multiple packets. If we give the 17462306a36Sopenharmony_ci * whole firmware to nci_send_data it will segment it and it will set 17562306a36Sopenharmony_ci * the PBF flag to 0x01 so we need to do the segmentation here. 17662306a36Sopenharmony_ci * 17762306a36Sopenharmony_ci * The firmware will be analyzed and applied when we send NCI_OP_PROP_PATCH_CMD 17862306a36Sopenharmony_ci * command with NCI_PATCH_TYPE_EOT parameter. The device will send a 17962306a36Sopenharmony_ci * NFCC_PATCH_NTF packet and a NCI_OP_CORE_RESET_NTF packet. 18062306a36Sopenharmony_ci */ 18162306a36Sopenharmony_cistatic int fdp_nci_send_patch(struct nci_dev *ndev, u8 conn_id, u8 type) 18262306a36Sopenharmony_ci{ 18362306a36Sopenharmony_ci struct fdp_nci_info *info = nci_get_drvdata(ndev); 18462306a36Sopenharmony_ci const struct firmware *fw; 18562306a36Sopenharmony_ci struct sk_buff *skb; 18662306a36Sopenharmony_ci unsigned long len; 18762306a36Sopenharmony_ci int max_size, payload_size; 18862306a36Sopenharmony_ci int rc = 0; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci if ((type == NCI_PATCH_TYPE_OTP && !info->otp_patch) || 19162306a36Sopenharmony_ci (type == NCI_PATCH_TYPE_RAM && !info->ram_patch)) 19262306a36Sopenharmony_ci return -EINVAL; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci if (type == NCI_PATCH_TYPE_OTP) 19562306a36Sopenharmony_ci fw = info->otp_patch; 19662306a36Sopenharmony_ci else 19762306a36Sopenharmony_ci fw = info->ram_patch; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci max_size = nci_conn_max_data_pkt_payload_size(ndev, conn_id); 20062306a36Sopenharmony_ci if (max_size <= 0) 20162306a36Sopenharmony_ci return -EINVAL; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci len = fw->size; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci fdp_nci_set_data_pkt_counter(ndev, fdp_nci_send_patch_cb, 20662306a36Sopenharmony_ci DIV_ROUND_UP(fw->size, max_size)); 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci while (len) { 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci payload_size = min_t(unsigned long, max_size, len); 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci skb = nci_skb_alloc(ndev, (NCI_CTRL_HDR_SIZE + payload_size), 21362306a36Sopenharmony_ci GFP_KERNEL); 21462306a36Sopenharmony_ci if (!skb) { 21562306a36Sopenharmony_ci fdp_nci_set_data_pkt_counter(ndev, NULL, 0); 21662306a36Sopenharmony_ci return -ENOMEM; 21762306a36Sopenharmony_ci } 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci skb_reserve(skb, NCI_CTRL_HDR_SIZE); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci skb_put_data(skb, fw->data + (fw->size - len), payload_size); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci rc = nci_send_data(ndev, conn_id, skb); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci if (rc) { 22762306a36Sopenharmony_ci fdp_nci_set_data_pkt_counter(ndev, NULL, 0); 22862306a36Sopenharmony_ci return rc; 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci len -= payload_size; 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci return rc; 23562306a36Sopenharmony_ci} 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_cistatic int fdp_nci_open(struct nci_dev *ndev) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci const struct fdp_nci_info *info = nci_get_drvdata(ndev); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci return info->phy_ops->enable(info->phy); 24262306a36Sopenharmony_ci} 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_cistatic int fdp_nci_close(struct nci_dev *ndev) 24562306a36Sopenharmony_ci{ 24662306a36Sopenharmony_ci return 0; 24762306a36Sopenharmony_ci} 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_cistatic int fdp_nci_send(struct nci_dev *ndev, struct sk_buff *skb) 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci struct fdp_nci_info *info = nci_get_drvdata(ndev); 25262306a36Sopenharmony_ci int ret; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci if (atomic_dec_and_test(&info->data_pkt_counter)) 25562306a36Sopenharmony_ci info->data_pkt_counter_cb(ndev); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci ret = info->phy_ops->write(info->phy, skb); 25862306a36Sopenharmony_ci if (ret < 0) { 25962306a36Sopenharmony_ci kfree_skb(skb); 26062306a36Sopenharmony_ci return ret; 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci consume_skb(skb); 26462306a36Sopenharmony_ci return 0; 26562306a36Sopenharmony_ci} 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_cistatic int fdp_nci_request_firmware(struct nci_dev *ndev) 26862306a36Sopenharmony_ci{ 26962306a36Sopenharmony_ci struct fdp_nci_info *info = nci_get_drvdata(ndev); 27062306a36Sopenharmony_ci struct device *dev = &info->phy->i2c_dev->dev; 27162306a36Sopenharmony_ci const u8 *data; 27262306a36Sopenharmony_ci int r; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci r = request_firmware(&info->ram_patch, FDP_RAM_PATCH_NAME, dev); 27562306a36Sopenharmony_ci if (r < 0) { 27662306a36Sopenharmony_ci nfc_err(dev, "RAM patch request error\n"); 27762306a36Sopenharmony_ci return r; 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci data = info->ram_patch->data; 28162306a36Sopenharmony_ci info->ram_patch_version = 28262306a36Sopenharmony_ci data[FDP_FW_HEADER_SIZE] | 28362306a36Sopenharmony_ci (data[FDP_FW_HEADER_SIZE + 1] << 8) | 28462306a36Sopenharmony_ci (data[FDP_FW_HEADER_SIZE + 2] << 16) | 28562306a36Sopenharmony_ci (data[FDP_FW_HEADER_SIZE + 3] << 24); 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci dev_dbg(dev, "RAM patch version: %d, size: %zu\n", 28862306a36Sopenharmony_ci info->ram_patch_version, info->ram_patch->size); 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci r = request_firmware(&info->otp_patch, FDP_OTP_PATCH_NAME, dev); 29262306a36Sopenharmony_ci if (r < 0) { 29362306a36Sopenharmony_ci nfc_err(dev, "OTP patch request error\n"); 29462306a36Sopenharmony_ci return 0; 29562306a36Sopenharmony_ci } 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci data = (u8 *) info->otp_patch->data; 29862306a36Sopenharmony_ci info->otp_patch_version = 29962306a36Sopenharmony_ci data[FDP_FW_HEADER_SIZE] | 30062306a36Sopenharmony_ci (data[FDP_FW_HEADER_SIZE + 1] << 8) | 30162306a36Sopenharmony_ci (data[FDP_FW_HEADER_SIZE+2] << 16) | 30262306a36Sopenharmony_ci (data[FDP_FW_HEADER_SIZE+3] << 24); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci dev_dbg(dev, "OTP patch version: %d, size: %zu\n", 30562306a36Sopenharmony_ci info->otp_patch_version, info->otp_patch->size); 30662306a36Sopenharmony_ci return 0; 30762306a36Sopenharmony_ci} 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_cistatic void fdp_nci_release_firmware(struct nci_dev *ndev) 31062306a36Sopenharmony_ci{ 31162306a36Sopenharmony_ci struct fdp_nci_info *info = nci_get_drvdata(ndev); 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci if (info->otp_patch) { 31462306a36Sopenharmony_ci release_firmware(info->otp_patch); 31562306a36Sopenharmony_ci info->otp_patch = NULL; 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci if (info->ram_patch) { 31962306a36Sopenharmony_ci release_firmware(info->ram_patch); 32062306a36Sopenharmony_ci info->ram_patch = NULL; 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci} 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_cistatic int fdp_nci_patch_otp(struct nci_dev *ndev) 32562306a36Sopenharmony_ci{ 32662306a36Sopenharmony_ci struct fdp_nci_info *info = nci_get_drvdata(ndev); 32762306a36Sopenharmony_ci struct device *dev = &info->phy->i2c_dev->dev; 32862306a36Sopenharmony_ci int conn_id; 32962306a36Sopenharmony_ci int r = 0; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci if (info->otp_version >= info->otp_patch_version) 33262306a36Sopenharmony_ci return r; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci info->setup_patch_sent = 0; 33562306a36Sopenharmony_ci info->setup_reset_ntf = 0; 33662306a36Sopenharmony_ci info->setup_patch_ntf = 0; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci /* Patch init request */ 33962306a36Sopenharmony_ci r = fdp_nci_patch_cmd(ndev, NCI_PATCH_TYPE_OTP); 34062306a36Sopenharmony_ci if (r) 34162306a36Sopenharmony_ci return r; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci /* Patch data connection creation */ 34462306a36Sopenharmony_ci conn_id = fdp_nci_create_conn(ndev); 34562306a36Sopenharmony_ci if (conn_id < 0) 34662306a36Sopenharmony_ci return conn_id; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci /* Send the patch over the data connection */ 34962306a36Sopenharmony_ci r = fdp_nci_send_patch(ndev, conn_id, NCI_PATCH_TYPE_OTP); 35062306a36Sopenharmony_ci if (r) 35162306a36Sopenharmony_ci return r; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci /* Wait for all the packets to be send over i2c */ 35462306a36Sopenharmony_ci wait_event_interruptible(info->setup_wq, 35562306a36Sopenharmony_ci info->setup_patch_sent == 1); 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci /* make sure that the NFCC processed the last data packet */ 35862306a36Sopenharmony_ci msleep(FDP_FW_UPDATE_SLEEP); 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci /* Close the data connection */ 36162306a36Sopenharmony_ci r = nci_core_conn_close(info->ndev, conn_id); 36262306a36Sopenharmony_ci if (r) 36362306a36Sopenharmony_ci return r; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci /* Patch finish message */ 36662306a36Sopenharmony_ci if (fdp_nci_patch_cmd(ndev, NCI_PATCH_TYPE_EOT)) { 36762306a36Sopenharmony_ci nfc_err(dev, "OTP patch error 0x%x\n", r); 36862306a36Sopenharmony_ci return -EINVAL; 36962306a36Sopenharmony_ci } 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci /* If the patch notification didn't arrive yet, wait for it */ 37262306a36Sopenharmony_ci wait_event_interruptible(info->setup_wq, info->setup_patch_ntf); 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci /* Check if the patching was successful */ 37562306a36Sopenharmony_ci r = info->setup_patch_status; 37662306a36Sopenharmony_ci if (r) { 37762306a36Sopenharmony_ci nfc_err(dev, "OTP patch error 0x%x\n", r); 37862306a36Sopenharmony_ci return -EINVAL; 37962306a36Sopenharmony_ci } 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci /* 38262306a36Sopenharmony_ci * We need to wait for the reset notification before we 38362306a36Sopenharmony_ci * can continue 38462306a36Sopenharmony_ci */ 38562306a36Sopenharmony_ci wait_event_interruptible(info->setup_wq, info->setup_reset_ntf); 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci return r; 38862306a36Sopenharmony_ci} 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_cistatic int fdp_nci_patch_ram(struct nci_dev *ndev) 39162306a36Sopenharmony_ci{ 39262306a36Sopenharmony_ci struct fdp_nci_info *info = nci_get_drvdata(ndev); 39362306a36Sopenharmony_ci struct device *dev = &info->phy->i2c_dev->dev; 39462306a36Sopenharmony_ci int conn_id; 39562306a36Sopenharmony_ci int r = 0; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci if (info->ram_version >= info->ram_patch_version) 39862306a36Sopenharmony_ci return r; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci info->setup_patch_sent = 0; 40162306a36Sopenharmony_ci info->setup_reset_ntf = 0; 40262306a36Sopenharmony_ci info->setup_patch_ntf = 0; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci /* Patch init request */ 40562306a36Sopenharmony_ci r = fdp_nci_patch_cmd(ndev, NCI_PATCH_TYPE_RAM); 40662306a36Sopenharmony_ci if (r) 40762306a36Sopenharmony_ci return r; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci /* Patch data connection creation */ 41062306a36Sopenharmony_ci conn_id = fdp_nci_create_conn(ndev); 41162306a36Sopenharmony_ci if (conn_id < 0) 41262306a36Sopenharmony_ci return conn_id; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci /* Send the patch over the data connection */ 41562306a36Sopenharmony_ci r = fdp_nci_send_patch(ndev, conn_id, NCI_PATCH_TYPE_RAM); 41662306a36Sopenharmony_ci if (r) 41762306a36Sopenharmony_ci return r; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci /* Wait for all the packets to be send over i2c */ 42062306a36Sopenharmony_ci wait_event_interruptible(info->setup_wq, 42162306a36Sopenharmony_ci info->setup_patch_sent == 1); 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci /* make sure that the NFCC processed the last data packet */ 42462306a36Sopenharmony_ci msleep(FDP_FW_UPDATE_SLEEP); 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci /* Close the data connection */ 42762306a36Sopenharmony_ci r = nci_core_conn_close(info->ndev, conn_id); 42862306a36Sopenharmony_ci if (r) 42962306a36Sopenharmony_ci return r; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci /* Patch finish message */ 43262306a36Sopenharmony_ci if (fdp_nci_patch_cmd(ndev, NCI_PATCH_TYPE_EOT)) { 43362306a36Sopenharmony_ci nfc_err(dev, "RAM patch error 0x%x\n", r); 43462306a36Sopenharmony_ci return -EINVAL; 43562306a36Sopenharmony_ci } 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci /* If the patch notification didn't arrive yet, wait for it */ 43862306a36Sopenharmony_ci wait_event_interruptible(info->setup_wq, info->setup_patch_ntf); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci /* Check if the patching was successful */ 44162306a36Sopenharmony_ci r = info->setup_patch_status; 44262306a36Sopenharmony_ci if (r) { 44362306a36Sopenharmony_ci nfc_err(dev, "RAM patch error 0x%x\n", r); 44462306a36Sopenharmony_ci return -EINVAL; 44562306a36Sopenharmony_ci } 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci /* 44862306a36Sopenharmony_ci * We need to wait for the reset notification before we 44962306a36Sopenharmony_ci * can continue 45062306a36Sopenharmony_ci */ 45162306a36Sopenharmony_ci wait_event_interruptible(info->setup_wq, info->setup_reset_ntf); 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci return r; 45462306a36Sopenharmony_ci} 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_cistatic int fdp_nci_setup(struct nci_dev *ndev) 45762306a36Sopenharmony_ci{ 45862306a36Sopenharmony_ci /* Format: total length followed by an NCI packet */ 45962306a36Sopenharmony_ci struct fdp_nci_info *info = nci_get_drvdata(ndev); 46062306a36Sopenharmony_ci struct device *dev = &info->phy->i2c_dev->dev; 46162306a36Sopenharmony_ci int r; 46262306a36Sopenharmony_ci u8 patched = 0; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci r = nci_core_init(ndev); 46562306a36Sopenharmony_ci if (r) 46662306a36Sopenharmony_ci goto error; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci /* Get RAM and OTP version */ 46962306a36Sopenharmony_ci r = fdp_nci_get_versions(ndev); 47062306a36Sopenharmony_ci if (r) 47162306a36Sopenharmony_ci goto error; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci /* Load firmware from disk */ 47462306a36Sopenharmony_ci r = fdp_nci_request_firmware(ndev); 47562306a36Sopenharmony_ci if (r) 47662306a36Sopenharmony_ci goto error; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci /* Update OTP */ 47962306a36Sopenharmony_ci if (info->otp_version < info->otp_patch_version) { 48062306a36Sopenharmony_ci r = fdp_nci_patch_otp(ndev); 48162306a36Sopenharmony_ci if (r) 48262306a36Sopenharmony_ci goto error; 48362306a36Sopenharmony_ci patched = 1; 48462306a36Sopenharmony_ci } 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci /* Update RAM */ 48762306a36Sopenharmony_ci if (info->ram_version < info->ram_patch_version) { 48862306a36Sopenharmony_ci r = fdp_nci_patch_ram(ndev); 48962306a36Sopenharmony_ci if (r) 49062306a36Sopenharmony_ci goto error; 49162306a36Sopenharmony_ci patched = 1; 49262306a36Sopenharmony_ci } 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci /* Release the firmware buffers */ 49562306a36Sopenharmony_ci fdp_nci_release_firmware(ndev); 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci /* If a patch was applied the new version is checked */ 49862306a36Sopenharmony_ci if (patched) { 49962306a36Sopenharmony_ci r = nci_core_init(ndev); 50062306a36Sopenharmony_ci if (r) 50162306a36Sopenharmony_ci goto error; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci r = fdp_nci_get_versions(ndev); 50462306a36Sopenharmony_ci if (r) 50562306a36Sopenharmony_ci goto error; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci if (info->otp_version != info->otp_patch_version || 50862306a36Sopenharmony_ci info->ram_version != info->ram_patch_version) { 50962306a36Sopenharmony_ci nfc_err(dev, "Firmware update failed"); 51062306a36Sopenharmony_ci r = -EINVAL; 51162306a36Sopenharmony_ci goto error; 51262306a36Sopenharmony_ci } 51362306a36Sopenharmony_ci } 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci /* 51662306a36Sopenharmony_ci * We initialized the devices but the NFC subsystem expects 51762306a36Sopenharmony_ci * it to not be initialized. 51862306a36Sopenharmony_ci */ 51962306a36Sopenharmony_ci return nci_core_reset(ndev); 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_cierror: 52262306a36Sopenharmony_ci fdp_nci_release_firmware(ndev); 52362306a36Sopenharmony_ci nfc_err(dev, "Setup error %d\n", r); 52462306a36Sopenharmony_ci return r; 52562306a36Sopenharmony_ci} 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_cistatic int fdp_nci_post_setup(struct nci_dev *ndev) 52862306a36Sopenharmony_ci{ 52962306a36Sopenharmony_ci struct fdp_nci_info *info = nci_get_drvdata(ndev); 53062306a36Sopenharmony_ci struct device *dev = &info->phy->i2c_dev->dev; 53162306a36Sopenharmony_ci int r; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci /* Check if the device has VSC */ 53462306a36Sopenharmony_ci if (info->fw_vsc_cfg && info->fw_vsc_cfg[0]) { 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci /* Set the vendor specific configuration */ 53762306a36Sopenharmony_ci r = fdp_nci_set_production_data(ndev, info->fw_vsc_cfg[3], 53862306a36Sopenharmony_ci &info->fw_vsc_cfg[4]); 53962306a36Sopenharmony_ci if (r) { 54062306a36Sopenharmony_ci nfc_err(dev, "Vendor specific config set error %d\n", 54162306a36Sopenharmony_ci r); 54262306a36Sopenharmony_ci return r; 54362306a36Sopenharmony_ci } 54462306a36Sopenharmony_ci } 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci /* Set clock type and frequency */ 54762306a36Sopenharmony_ci r = fdp_nci_set_clock(ndev, info->clock_type, info->clock_freq); 54862306a36Sopenharmony_ci if (r) { 54962306a36Sopenharmony_ci nfc_err(dev, "Clock set error %d\n", r); 55062306a36Sopenharmony_ci return r; 55162306a36Sopenharmony_ci } 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci /* 55462306a36Sopenharmony_ci * In order to apply the VSC FDP needs a reset 55562306a36Sopenharmony_ci */ 55662306a36Sopenharmony_ci r = nci_core_reset(ndev); 55762306a36Sopenharmony_ci if (r) 55862306a36Sopenharmony_ci return r; 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci /** 56162306a36Sopenharmony_ci * The nci core was initialized when post setup was called 56262306a36Sopenharmony_ci * so we leave it like that 56362306a36Sopenharmony_ci */ 56462306a36Sopenharmony_ci return nci_core_init(ndev); 56562306a36Sopenharmony_ci} 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_cistatic int fdp_nci_core_reset_ntf_packet(struct nci_dev *ndev, 56862306a36Sopenharmony_ci struct sk_buff *skb) 56962306a36Sopenharmony_ci{ 57062306a36Sopenharmony_ci struct fdp_nci_info *info = nci_get_drvdata(ndev); 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci info->setup_reset_ntf = 1; 57362306a36Sopenharmony_ci wake_up(&info->setup_wq); 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci return 0; 57662306a36Sopenharmony_ci} 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_cistatic int fdp_nci_prop_patch_ntf_packet(struct nci_dev *ndev, 57962306a36Sopenharmony_ci struct sk_buff *skb) 58062306a36Sopenharmony_ci{ 58162306a36Sopenharmony_ci struct fdp_nci_info *info = nci_get_drvdata(ndev); 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci info->setup_patch_ntf = 1; 58462306a36Sopenharmony_ci info->setup_patch_status = skb->data[0]; 58562306a36Sopenharmony_ci wake_up(&info->setup_wq); 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci return 0; 58862306a36Sopenharmony_ci} 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_cistatic int fdp_nci_prop_patch_rsp_packet(struct nci_dev *ndev, 59162306a36Sopenharmony_ci struct sk_buff *skb) 59262306a36Sopenharmony_ci{ 59362306a36Sopenharmony_ci struct fdp_nci_info *info = nci_get_drvdata(ndev); 59462306a36Sopenharmony_ci struct device *dev = &info->phy->i2c_dev->dev; 59562306a36Sopenharmony_ci u8 status = skb->data[0]; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci dev_dbg(dev, "%s: status 0x%x\n", __func__, status); 59862306a36Sopenharmony_ci nci_req_complete(ndev, status); 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci return 0; 60162306a36Sopenharmony_ci} 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_cistatic int fdp_nci_prop_set_production_data_rsp_packet(struct nci_dev *ndev, 60462306a36Sopenharmony_ci struct sk_buff *skb) 60562306a36Sopenharmony_ci{ 60662306a36Sopenharmony_ci struct fdp_nci_info *info = nci_get_drvdata(ndev); 60762306a36Sopenharmony_ci struct device *dev = &info->phy->i2c_dev->dev; 60862306a36Sopenharmony_ci u8 status = skb->data[0]; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci dev_dbg(dev, "%s: status 0x%x\n", __func__, status); 61162306a36Sopenharmony_ci nci_req_complete(ndev, status); 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci return 0; 61462306a36Sopenharmony_ci} 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_cistatic int fdp_nci_core_get_config_rsp_packet(struct nci_dev *ndev, 61762306a36Sopenharmony_ci struct sk_buff *skb) 61862306a36Sopenharmony_ci{ 61962306a36Sopenharmony_ci struct fdp_nci_info *info = nci_get_drvdata(ndev); 62062306a36Sopenharmony_ci struct device *dev = &info->phy->i2c_dev->dev; 62162306a36Sopenharmony_ci const struct nci_core_get_config_rsp *rsp = (void *) skb->data; 62262306a36Sopenharmony_ci unsigned int i; 62362306a36Sopenharmony_ci const u8 *p; 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci if (rsp->status == NCI_STATUS_OK) { 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci p = rsp->data; 62862306a36Sopenharmony_ci for (i = 0; i < 4; i++) { 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci switch (*p++) { 63162306a36Sopenharmony_ci case NCI_PARAM_ID_FW_RAM_VERSION: 63262306a36Sopenharmony_ci p++; 63362306a36Sopenharmony_ci info->ram_version = le32_to_cpup((__le32 *) p); 63462306a36Sopenharmony_ci p += 4; 63562306a36Sopenharmony_ci break; 63662306a36Sopenharmony_ci case NCI_PARAM_ID_FW_OTP_VERSION: 63762306a36Sopenharmony_ci p++; 63862306a36Sopenharmony_ci info->otp_version = le32_to_cpup((__le32 *) p); 63962306a36Sopenharmony_ci p += 4; 64062306a36Sopenharmony_ci break; 64162306a36Sopenharmony_ci case NCI_PARAM_ID_OTP_LIMITED_VERSION: 64262306a36Sopenharmony_ci p++; 64362306a36Sopenharmony_ci info->otp_version = le32_to_cpup((__le32 *) p); 64462306a36Sopenharmony_ci p += 4; 64562306a36Sopenharmony_ci break; 64662306a36Sopenharmony_ci case NCI_PARAM_ID_KEY_INDEX_ID: 64762306a36Sopenharmony_ci p++; 64862306a36Sopenharmony_ci info->key_index = *p++; 64962306a36Sopenharmony_ci } 65062306a36Sopenharmony_ci } 65162306a36Sopenharmony_ci } 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci dev_dbg(dev, "OTP version %d\n", info->otp_version); 65462306a36Sopenharmony_ci dev_dbg(dev, "RAM version %d\n", info->ram_version); 65562306a36Sopenharmony_ci dev_dbg(dev, "key index %d\n", info->key_index); 65662306a36Sopenharmony_ci dev_dbg(dev, "%s: status 0x%x\n", __func__, rsp->status); 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci nci_req_complete(ndev, rsp->status); 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci return 0; 66162306a36Sopenharmony_ci} 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_cistatic const struct nci_driver_ops fdp_core_ops[] = { 66462306a36Sopenharmony_ci { 66562306a36Sopenharmony_ci .opcode = NCI_OP_CORE_GET_CONFIG_RSP, 66662306a36Sopenharmony_ci .rsp = fdp_nci_core_get_config_rsp_packet, 66762306a36Sopenharmony_ci }, 66862306a36Sopenharmony_ci { 66962306a36Sopenharmony_ci .opcode = NCI_OP_CORE_RESET_NTF, 67062306a36Sopenharmony_ci .ntf = fdp_nci_core_reset_ntf_packet, 67162306a36Sopenharmony_ci }, 67262306a36Sopenharmony_ci}; 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_cistatic const struct nci_driver_ops fdp_prop_ops[] = { 67562306a36Sopenharmony_ci { 67662306a36Sopenharmony_ci .opcode = nci_opcode_pack(NCI_GID_PROP, NCI_OP_PROP_PATCH_OID), 67762306a36Sopenharmony_ci .rsp = fdp_nci_prop_patch_rsp_packet, 67862306a36Sopenharmony_ci .ntf = fdp_nci_prop_patch_ntf_packet, 67962306a36Sopenharmony_ci }, 68062306a36Sopenharmony_ci { 68162306a36Sopenharmony_ci .opcode = nci_opcode_pack(NCI_GID_PROP, 68262306a36Sopenharmony_ci NCI_OP_PROP_SET_PDATA_OID), 68362306a36Sopenharmony_ci .rsp = fdp_nci_prop_set_production_data_rsp_packet, 68462306a36Sopenharmony_ci }, 68562306a36Sopenharmony_ci}; 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_cistatic const struct nci_ops nci_ops = { 68862306a36Sopenharmony_ci .open = fdp_nci_open, 68962306a36Sopenharmony_ci .close = fdp_nci_close, 69062306a36Sopenharmony_ci .send = fdp_nci_send, 69162306a36Sopenharmony_ci .setup = fdp_nci_setup, 69262306a36Sopenharmony_ci .post_setup = fdp_nci_post_setup, 69362306a36Sopenharmony_ci .prop_ops = fdp_prop_ops, 69462306a36Sopenharmony_ci .n_prop_ops = ARRAY_SIZE(fdp_prop_ops), 69562306a36Sopenharmony_ci .core_ops = fdp_core_ops, 69662306a36Sopenharmony_ci .n_core_ops = ARRAY_SIZE(fdp_core_ops), 69762306a36Sopenharmony_ci}; 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ciint fdp_nci_probe(struct fdp_i2c_phy *phy, const struct nfc_phy_ops *phy_ops, 70062306a36Sopenharmony_ci struct nci_dev **ndevp, int tx_headroom, 70162306a36Sopenharmony_ci int tx_tailroom, u8 clock_type, u32 clock_freq, 70262306a36Sopenharmony_ci const u8 *fw_vsc_cfg) 70362306a36Sopenharmony_ci{ 70462306a36Sopenharmony_ci struct device *dev = &phy->i2c_dev->dev; 70562306a36Sopenharmony_ci struct fdp_nci_info *info; 70662306a36Sopenharmony_ci struct nci_dev *ndev; 70762306a36Sopenharmony_ci u32 protocols; 70862306a36Sopenharmony_ci int r; 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci info = devm_kzalloc(dev, sizeof(struct fdp_nci_info), GFP_KERNEL); 71162306a36Sopenharmony_ci if (!info) 71262306a36Sopenharmony_ci return -ENOMEM; 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci info->phy = phy; 71562306a36Sopenharmony_ci info->phy_ops = phy_ops; 71662306a36Sopenharmony_ci info->clock_type = clock_type; 71762306a36Sopenharmony_ci info->clock_freq = clock_freq; 71862306a36Sopenharmony_ci info->fw_vsc_cfg = fw_vsc_cfg; 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci init_waitqueue_head(&info->setup_wq); 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci protocols = NFC_PROTO_JEWEL_MASK | 72362306a36Sopenharmony_ci NFC_PROTO_MIFARE_MASK | 72462306a36Sopenharmony_ci NFC_PROTO_FELICA_MASK | 72562306a36Sopenharmony_ci NFC_PROTO_ISO14443_MASK | 72662306a36Sopenharmony_ci NFC_PROTO_ISO14443_B_MASK | 72762306a36Sopenharmony_ci NFC_PROTO_NFC_DEP_MASK | 72862306a36Sopenharmony_ci NFC_PROTO_ISO15693_MASK; 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci BUILD_BUG_ON(ARRAY_SIZE(fdp_prop_ops) > NCI_MAX_PROPRIETARY_CMD); 73162306a36Sopenharmony_ci ndev = nci_allocate_device(&nci_ops, protocols, tx_headroom, 73262306a36Sopenharmony_ci tx_tailroom); 73362306a36Sopenharmony_ci if (!ndev) { 73462306a36Sopenharmony_ci nfc_err(dev, "Cannot allocate nfc ndev\n"); 73562306a36Sopenharmony_ci return -ENOMEM; 73662306a36Sopenharmony_ci } 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci r = nci_register_device(ndev); 73962306a36Sopenharmony_ci if (r) 74062306a36Sopenharmony_ci goto err_regdev; 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci *ndevp = ndev; 74362306a36Sopenharmony_ci info->ndev = ndev; 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci nci_set_drvdata(ndev, info); 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci return 0; 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_cierr_regdev: 75062306a36Sopenharmony_ci nci_free_device(ndev); 75162306a36Sopenharmony_ci return r; 75262306a36Sopenharmony_ci} 75362306a36Sopenharmony_ciEXPORT_SYMBOL(fdp_nci_probe); 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_civoid fdp_nci_remove(struct nci_dev *ndev) 75662306a36Sopenharmony_ci{ 75762306a36Sopenharmony_ci nci_unregister_device(ndev); 75862306a36Sopenharmony_ci nci_free_device(ndev); 75962306a36Sopenharmony_ci} 76062306a36Sopenharmony_ciEXPORT_SYMBOL(fdp_nci_remove); 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 76362306a36Sopenharmony_ciMODULE_DESCRIPTION("NFC NCI driver for Intel Fields Peak NFC controller"); 76462306a36Sopenharmony_ciMODULE_AUTHOR("Robert Dolca <robert.dolca@intel.com>"); 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ciMODULE_FIRMWARE(FDP_OTP_PATCH_NAME); 76762306a36Sopenharmony_ciMODULE_FIRMWARE(FDP_RAM_PATCH_NAME); 768