18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * NCI based driver for Samsung S3FWRN5 NFC chip 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2015 Samsung Electrnoics 68c2ecf20Sopenharmony_ci * Robert Baldyga <r.baldyga@samsung.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/completion.h> 108c2ecf20Sopenharmony_ci#include <linux/firmware.h> 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include "s3fwrn5.h" 138c2ecf20Sopenharmony_ci#include "nci.h" 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_cistatic int s3fwrn5_nci_prop_rsp(struct nci_dev *ndev, struct sk_buff *skb) 168c2ecf20Sopenharmony_ci{ 178c2ecf20Sopenharmony_ci __u8 status = skb->data[0]; 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci nci_req_complete(ndev, status); 208c2ecf20Sopenharmony_ci return 0; 218c2ecf20Sopenharmony_ci} 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cistatic struct nci_driver_ops s3fwrn5_nci_prop_ops[] = { 248c2ecf20Sopenharmony_ci { 258c2ecf20Sopenharmony_ci .opcode = nci_opcode_pack(NCI_GID_PROPRIETARY, 268c2ecf20Sopenharmony_ci NCI_PROP_AGAIN), 278c2ecf20Sopenharmony_ci .rsp = s3fwrn5_nci_prop_rsp, 288c2ecf20Sopenharmony_ci }, 298c2ecf20Sopenharmony_ci { 308c2ecf20Sopenharmony_ci .opcode = nci_opcode_pack(NCI_GID_PROPRIETARY, 318c2ecf20Sopenharmony_ci NCI_PROP_GET_RFREG), 328c2ecf20Sopenharmony_ci .rsp = s3fwrn5_nci_prop_rsp, 338c2ecf20Sopenharmony_ci }, 348c2ecf20Sopenharmony_ci { 358c2ecf20Sopenharmony_ci .opcode = nci_opcode_pack(NCI_GID_PROPRIETARY, 368c2ecf20Sopenharmony_ci NCI_PROP_SET_RFREG), 378c2ecf20Sopenharmony_ci .rsp = s3fwrn5_nci_prop_rsp, 388c2ecf20Sopenharmony_ci }, 398c2ecf20Sopenharmony_ci { 408c2ecf20Sopenharmony_ci .opcode = nci_opcode_pack(NCI_GID_PROPRIETARY, 418c2ecf20Sopenharmony_ci NCI_PROP_GET_RFREG_VER), 428c2ecf20Sopenharmony_ci .rsp = s3fwrn5_nci_prop_rsp, 438c2ecf20Sopenharmony_ci }, 448c2ecf20Sopenharmony_ci { 458c2ecf20Sopenharmony_ci .opcode = nci_opcode_pack(NCI_GID_PROPRIETARY, 468c2ecf20Sopenharmony_ci NCI_PROP_SET_RFREG_VER), 478c2ecf20Sopenharmony_ci .rsp = s3fwrn5_nci_prop_rsp, 488c2ecf20Sopenharmony_ci }, 498c2ecf20Sopenharmony_ci { 508c2ecf20Sopenharmony_ci .opcode = nci_opcode_pack(NCI_GID_PROPRIETARY, 518c2ecf20Sopenharmony_ci NCI_PROP_START_RFREG), 528c2ecf20Sopenharmony_ci .rsp = s3fwrn5_nci_prop_rsp, 538c2ecf20Sopenharmony_ci }, 548c2ecf20Sopenharmony_ci { 558c2ecf20Sopenharmony_ci .opcode = nci_opcode_pack(NCI_GID_PROPRIETARY, 568c2ecf20Sopenharmony_ci NCI_PROP_STOP_RFREG), 578c2ecf20Sopenharmony_ci .rsp = s3fwrn5_nci_prop_rsp, 588c2ecf20Sopenharmony_ci }, 598c2ecf20Sopenharmony_ci { 608c2ecf20Sopenharmony_ci .opcode = nci_opcode_pack(NCI_GID_PROPRIETARY, 618c2ecf20Sopenharmony_ci NCI_PROP_FW_CFG), 628c2ecf20Sopenharmony_ci .rsp = s3fwrn5_nci_prop_rsp, 638c2ecf20Sopenharmony_ci }, 648c2ecf20Sopenharmony_ci { 658c2ecf20Sopenharmony_ci .opcode = nci_opcode_pack(NCI_GID_PROPRIETARY, 668c2ecf20Sopenharmony_ci NCI_PROP_WR_RESET), 678c2ecf20Sopenharmony_ci .rsp = s3fwrn5_nci_prop_rsp, 688c2ecf20Sopenharmony_ci }, 698c2ecf20Sopenharmony_ci}; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_civoid s3fwrn5_nci_get_prop_ops(struct nci_driver_ops **ops, size_t *n) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci *ops = s3fwrn5_nci_prop_ops; 748c2ecf20Sopenharmony_ci *n = ARRAY_SIZE(s3fwrn5_nci_prop_ops); 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci#define S3FWRN5_RFREG_SECTION_SIZE 252 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ciint s3fwrn5_nci_rf_configure(struct s3fwrn5_info *info, const char *fw_name) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci const struct firmware *fw; 828c2ecf20Sopenharmony_ci struct nci_prop_fw_cfg_cmd fw_cfg; 838c2ecf20Sopenharmony_ci struct nci_prop_set_rfreg_cmd set_rfreg; 848c2ecf20Sopenharmony_ci struct nci_prop_stop_rfreg_cmd stop_rfreg; 858c2ecf20Sopenharmony_ci u32 checksum; 868c2ecf20Sopenharmony_ci int i, len; 878c2ecf20Sopenharmony_ci int ret; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci ret = request_firmware(&fw, fw_name, &info->ndev->nfc_dev->dev); 908c2ecf20Sopenharmony_ci if (ret < 0) 918c2ecf20Sopenharmony_ci return ret; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci /* Compute rfreg checksum */ 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci checksum = 0; 968c2ecf20Sopenharmony_ci for (i = 0; i < fw->size; i += 4) 978c2ecf20Sopenharmony_ci checksum += *((u32 *)(fw->data+i)); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci /* Set default clock configuration for external crystal */ 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci fw_cfg.clk_type = 0x01; 1028c2ecf20Sopenharmony_ci fw_cfg.clk_speed = 0xff; 1038c2ecf20Sopenharmony_ci fw_cfg.clk_req = 0xff; 1048c2ecf20Sopenharmony_ci ret = nci_prop_cmd(info->ndev, NCI_PROP_FW_CFG, 1058c2ecf20Sopenharmony_ci sizeof(fw_cfg), (__u8 *)&fw_cfg); 1068c2ecf20Sopenharmony_ci if (ret < 0) 1078c2ecf20Sopenharmony_ci goto out; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci /* Start rfreg configuration */ 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci dev_info(&info->ndev->nfc_dev->dev, 1128c2ecf20Sopenharmony_ci "rfreg configuration update: %s\n", fw_name); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci ret = nci_prop_cmd(info->ndev, NCI_PROP_START_RFREG, 0, NULL); 1158c2ecf20Sopenharmony_ci if (ret < 0) { 1168c2ecf20Sopenharmony_ci dev_err(&info->ndev->nfc_dev->dev, 1178c2ecf20Sopenharmony_ci "Unable to start rfreg update\n"); 1188c2ecf20Sopenharmony_ci goto out; 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci /* Update rfreg */ 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci set_rfreg.index = 0; 1248c2ecf20Sopenharmony_ci for (i = 0; i < fw->size; i += S3FWRN5_RFREG_SECTION_SIZE) { 1258c2ecf20Sopenharmony_ci len = (fw->size - i < S3FWRN5_RFREG_SECTION_SIZE) ? 1268c2ecf20Sopenharmony_ci (fw->size - i) : S3FWRN5_RFREG_SECTION_SIZE; 1278c2ecf20Sopenharmony_ci memcpy(set_rfreg.data, fw->data+i, len); 1288c2ecf20Sopenharmony_ci ret = nci_prop_cmd(info->ndev, NCI_PROP_SET_RFREG, 1298c2ecf20Sopenharmony_ci len+1, (__u8 *)&set_rfreg); 1308c2ecf20Sopenharmony_ci if (ret < 0) { 1318c2ecf20Sopenharmony_ci dev_err(&info->ndev->nfc_dev->dev, 1328c2ecf20Sopenharmony_ci "rfreg update error (code=%d)\n", ret); 1338c2ecf20Sopenharmony_ci goto out; 1348c2ecf20Sopenharmony_ci } 1358c2ecf20Sopenharmony_ci set_rfreg.index++; 1368c2ecf20Sopenharmony_ci } 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci /* Finish rfreg configuration */ 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci stop_rfreg.checksum = checksum & 0xffff; 1418c2ecf20Sopenharmony_ci ret = nci_prop_cmd(info->ndev, NCI_PROP_STOP_RFREG, 1428c2ecf20Sopenharmony_ci sizeof(stop_rfreg), (__u8 *)&stop_rfreg); 1438c2ecf20Sopenharmony_ci if (ret < 0) { 1448c2ecf20Sopenharmony_ci dev_err(&info->ndev->nfc_dev->dev, 1458c2ecf20Sopenharmony_ci "Unable to stop rfreg update\n"); 1468c2ecf20Sopenharmony_ci goto out; 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci dev_info(&info->ndev->nfc_dev->dev, 1508c2ecf20Sopenharmony_ci "rfreg configuration update: success\n"); 1518c2ecf20Sopenharmony_ciout: 1528c2ecf20Sopenharmony_ci release_firmware(fw); 1538c2ecf20Sopenharmony_ci return ret; 1548c2ecf20Sopenharmony_ci} 155