1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * NCI based driver for Samsung S3FWRN5 NFC chip 4 * 5 * Copyright (C) 2015 Samsung Electrnoics 6 * Robert Baldyga <r.baldyga@samsung.com> 7 */ 8 9 #include <linux/completion.h> 10 #include <linux/firmware.h> 11 12 #include "s3fwrn5.h" 13 #include "nci.h" 14 15 static int s3fwrn5_nci_prop_rsp(struct nci_dev *ndev, struct sk_buff *skb) 16 { 17 __u8 status = skb->data[0]; 18 19 nci_req_complete(ndev, status); 20 return 0; 21 } 22 23 const struct nci_driver_ops s3fwrn5_nci_prop_ops[4] = { 24 { 25 .opcode = nci_opcode_pack(NCI_GID_PROPRIETARY, 26 NCI_PROP_SET_RFREG), 27 .rsp = s3fwrn5_nci_prop_rsp, 28 }, 29 { 30 .opcode = nci_opcode_pack(NCI_GID_PROPRIETARY, 31 NCI_PROP_START_RFREG), 32 .rsp = s3fwrn5_nci_prop_rsp, 33 }, 34 { 35 .opcode = nci_opcode_pack(NCI_GID_PROPRIETARY, 36 NCI_PROP_STOP_RFREG), 37 .rsp = s3fwrn5_nci_prop_rsp, 38 }, 39 { 40 .opcode = nci_opcode_pack(NCI_GID_PROPRIETARY, 41 NCI_PROP_FW_CFG), 42 .rsp = s3fwrn5_nci_prop_rsp, 43 }, 44 }; 45 46 #define S3FWRN5_RFREG_SECTION_SIZE 252 47 48 int s3fwrn5_nci_rf_configure(struct s3fwrn5_info *info, const char *fw_name) 49 { 50 struct device *dev = &info->ndev->nfc_dev->dev; 51 const struct firmware *fw; 52 struct nci_prop_fw_cfg_cmd fw_cfg; 53 struct nci_prop_set_rfreg_cmd set_rfreg; 54 struct nci_prop_stop_rfreg_cmd stop_rfreg; 55 u32 checksum; 56 int i, len; 57 int ret; 58 59 ret = request_firmware(&fw, fw_name, dev); 60 if (ret < 0) 61 return ret; 62 63 /* Compute rfreg checksum */ 64 65 checksum = 0; 66 for (i = 0; i < fw->size; i += 4) 67 checksum += *((u32 *)(fw->data+i)); 68 69 /* Set default clock configuration for external crystal */ 70 71 fw_cfg.clk_type = 0x01; 72 fw_cfg.clk_speed = 0xff; 73 fw_cfg.clk_req = 0xff; 74 ret = nci_prop_cmd(info->ndev, NCI_PROP_FW_CFG, 75 sizeof(fw_cfg), (__u8 *)&fw_cfg); 76 if (ret < 0) 77 goto out; 78 79 /* Start rfreg configuration */ 80 81 dev_info(dev, "rfreg configuration update: %s\n", fw_name); 82 83 ret = nci_prop_cmd(info->ndev, NCI_PROP_START_RFREG, 0, NULL); 84 if (ret < 0) { 85 dev_err(dev, "Unable to start rfreg update\n"); 86 goto out; 87 } 88 89 /* Update rfreg */ 90 91 set_rfreg.index = 0; 92 for (i = 0; i < fw->size; i += S3FWRN5_RFREG_SECTION_SIZE) { 93 len = (fw->size - i < S3FWRN5_RFREG_SECTION_SIZE) ? 94 (fw->size - i) : S3FWRN5_RFREG_SECTION_SIZE; 95 memcpy(set_rfreg.data, fw->data+i, len); 96 ret = nci_prop_cmd(info->ndev, NCI_PROP_SET_RFREG, 97 len+1, (__u8 *)&set_rfreg); 98 if (ret < 0) { 99 dev_err(dev, "rfreg update error (code=%d)\n", ret); 100 goto out; 101 } 102 set_rfreg.index++; 103 } 104 105 /* Finish rfreg configuration */ 106 107 stop_rfreg.checksum = checksum & 0xffff; 108 ret = nci_prop_cmd(info->ndev, NCI_PROP_STOP_RFREG, 109 sizeof(stop_rfreg), (__u8 *)&stop_rfreg); 110 if (ret < 0) { 111 dev_err(dev, "Unable to stop rfreg update\n"); 112 goto out; 113 } 114 115 dev_info(dev, "rfreg configuration update: success\n"); 116 out: 117 release_firmware(fw); 118 return ret; 119 } 120