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 static struct nci_driver_ops s3fwrn5_nci_prop_ops[] = { 24 { 25 .opcode = nci_opcode_pack(NCI_GID_PROPRIETARY, 26 NCI_PROP_AGAIN), 27 .rsp = s3fwrn5_nci_prop_rsp, 28 }, 29 { 30 .opcode = nci_opcode_pack(NCI_GID_PROPRIETARY, 31 NCI_PROP_GET_RFREG), 32 .rsp = s3fwrn5_nci_prop_rsp, 33 }, 34 { 35 .opcode = nci_opcode_pack(NCI_GID_PROPRIETARY, 36 NCI_PROP_SET_RFREG), 37 .rsp = s3fwrn5_nci_prop_rsp, 38 }, 39 { 40 .opcode = nci_opcode_pack(NCI_GID_PROPRIETARY, 41 NCI_PROP_GET_RFREG_VER), 42 .rsp = s3fwrn5_nci_prop_rsp, 43 }, 44 { 45 .opcode = nci_opcode_pack(NCI_GID_PROPRIETARY, 46 NCI_PROP_SET_RFREG_VER), 47 .rsp = s3fwrn5_nci_prop_rsp, 48 }, 49 { 50 .opcode = nci_opcode_pack(NCI_GID_PROPRIETARY, 51 NCI_PROP_START_RFREG), 52 .rsp = s3fwrn5_nci_prop_rsp, 53 }, 54 { 55 .opcode = nci_opcode_pack(NCI_GID_PROPRIETARY, 56 NCI_PROP_STOP_RFREG), 57 .rsp = s3fwrn5_nci_prop_rsp, 58 }, 59 { 60 .opcode = nci_opcode_pack(NCI_GID_PROPRIETARY, 61 NCI_PROP_FW_CFG), 62 .rsp = s3fwrn5_nci_prop_rsp, 63 }, 64 { 65 .opcode = nci_opcode_pack(NCI_GID_PROPRIETARY, 66 NCI_PROP_WR_RESET), 67 .rsp = s3fwrn5_nci_prop_rsp, 68 }, 69 }; 70 71 void s3fwrn5_nci_get_prop_ops(struct nci_driver_ops **ops, size_t *n) 72 { 73 *ops = s3fwrn5_nci_prop_ops; 74 *n = ARRAY_SIZE(s3fwrn5_nci_prop_ops); 75 } 76 77 #define S3FWRN5_RFREG_SECTION_SIZE 252 78 79 int s3fwrn5_nci_rf_configure(struct s3fwrn5_info *info, const char *fw_name) 80 { 81 const struct firmware *fw; 82 struct nci_prop_fw_cfg_cmd fw_cfg; 83 struct nci_prop_set_rfreg_cmd set_rfreg; 84 struct nci_prop_stop_rfreg_cmd stop_rfreg; 85 u32 checksum; 86 int i, len; 87 int ret; 88 89 ret = request_firmware(&fw, fw_name, &info->ndev->nfc_dev->dev); 90 if (ret < 0) 91 return ret; 92 93 /* Compute rfreg checksum */ 94 95 checksum = 0; 96 for (i = 0; i < fw->size; i += 4) 97 checksum += *((u32 *)(fw->data+i)); 98 99 /* Set default clock configuration for external crystal */ 100 101 fw_cfg.clk_type = 0x01; 102 fw_cfg.clk_speed = 0xff; 103 fw_cfg.clk_req = 0xff; 104 ret = nci_prop_cmd(info->ndev, NCI_PROP_FW_CFG, 105 sizeof(fw_cfg), (__u8 *)&fw_cfg); 106 if (ret < 0) 107 goto out; 108 109 /* Start rfreg configuration */ 110 111 dev_info(&info->ndev->nfc_dev->dev, 112 "rfreg configuration update: %s\n", fw_name); 113 114 ret = nci_prop_cmd(info->ndev, NCI_PROP_START_RFREG, 0, NULL); 115 if (ret < 0) { 116 dev_err(&info->ndev->nfc_dev->dev, 117 "Unable to start rfreg update\n"); 118 goto out; 119 } 120 121 /* Update rfreg */ 122 123 set_rfreg.index = 0; 124 for (i = 0; i < fw->size; i += S3FWRN5_RFREG_SECTION_SIZE) { 125 len = (fw->size - i < S3FWRN5_RFREG_SECTION_SIZE) ? 126 (fw->size - i) : S3FWRN5_RFREG_SECTION_SIZE; 127 memcpy(set_rfreg.data, fw->data+i, len); 128 ret = nci_prop_cmd(info->ndev, NCI_PROP_SET_RFREG, 129 len+1, (__u8 *)&set_rfreg); 130 if (ret < 0) { 131 dev_err(&info->ndev->nfc_dev->dev, 132 "rfreg update error (code=%d)\n", ret); 133 goto out; 134 } 135 set_rfreg.index++; 136 } 137 138 /* Finish rfreg configuration */ 139 140 stop_rfreg.checksum = checksum & 0xffff; 141 ret = nci_prop_cmd(info->ndev, NCI_PROP_STOP_RFREG, 142 sizeof(stop_rfreg), (__u8 *)&stop_rfreg); 143 if (ret < 0) { 144 dev_err(&info->ndev->nfc_dev->dev, 145 "Unable to stop rfreg update\n"); 146 goto out; 147 } 148 149 dev_info(&info->ndev->nfc_dev->dev, 150 "rfreg configuration update: success\n"); 151 out: 152 release_firmware(fw); 153 return ret; 154 } 155