1 /* 2 * NCI based driver for Samsung S3FWRN5 NFC chip 3 * 4 * Copyright (C) 2015 Samsung Electrnoics 5 * Robert Baldyga <r.baldyga@samsung.com> 6 * 7 * This program is free software; you can redistribute it and/or modify it 8 * under the terms and conditions of the GNU General Public License, 9 * version 2 or later, as published by the Free Software Foundation. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, see <http://www.gnu.org/licenses/>. 18 */ 19 20 #include <linux/completion.h> 21 #include <linux/firmware.h> 22 23 #include "s3fwrn5.h" 24 #include "nci.h" 25 26 static int s3fwrn5_nci_prop_rsp(struct nci_dev *ndev, struct sk_buff *skb) 27 { 28 __u8 status = skb->data[0]; 29 30 nci_req_complete(ndev, status); 31 return 0; 32 } 33 34 static struct nci_driver_ops s3fwrn5_nci_prop_ops[] = { 35 { 36 .opcode = nci_opcode_pack(NCI_GID_PROPRIETARY, 37 NCI_PROP_AGAIN), 38 .rsp = s3fwrn5_nci_prop_rsp, 39 }, 40 { 41 .opcode = nci_opcode_pack(NCI_GID_PROPRIETARY, 42 NCI_PROP_GET_RFREG), 43 .rsp = s3fwrn5_nci_prop_rsp, 44 }, 45 { 46 .opcode = nci_opcode_pack(NCI_GID_PROPRIETARY, 47 NCI_PROP_SET_RFREG), 48 .rsp = s3fwrn5_nci_prop_rsp, 49 }, 50 { 51 .opcode = nci_opcode_pack(NCI_GID_PROPRIETARY, 52 NCI_PROP_GET_RFREG_VER), 53 .rsp = s3fwrn5_nci_prop_rsp, 54 }, 55 { 56 .opcode = nci_opcode_pack(NCI_GID_PROPRIETARY, 57 NCI_PROP_SET_RFREG_VER), 58 .rsp = s3fwrn5_nci_prop_rsp, 59 }, 60 { 61 .opcode = nci_opcode_pack(NCI_GID_PROPRIETARY, 62 NCI_PROP_START_RFREG), 63 .rsp = s3fwrn5_nci_prop_rsp, 64 }, 65 { 66 .opcode = nci_opcode_pack(NCI_GID_PROPRIETARY, 67 NCI_PROP_STOP_RFREG), 68 .rsp = s3fwrn5_nci_prop_rsp, 69 }, 70 { 71 .opcode = nci_opcode_pack(NCI_GID_PROPRIETARY, 72 NCI_PROP_FW_CFG), 73 .rsp = s3fwrn5_nci_prop_rsp, 74 }, 75 { 76 .opcode = nci_opcode_pack(NCI_GID_PROPRIETARY, 77 NCI_PROP_WR_RESET), 78 .rsp = s3fwrn5_nci_prop_rsp, 79 }, 80 }; 81 82 void s3fwrn5_nci_get_prop_ops(struct nci_driver_ops **ops, size_t *n) 83 { 84 *ops = s3fwrn5_nci_prop_ops; 85 *n = ARRAY_SIZE(s3fwrn5_nci_prop_ops); 86 } 87 88 #define S3FWRN5_RFREG_SECTION_SIZE 252 89 90 int s3fwrn5_nci_rf_configure(struct s3fwrn5_info *info, const char *fw_name) 91 { 92 const struct firmware *fw; 93 struct nci_prop_fw_cfg_cmd fw_cfg; 94 struct nci_prop_set_rfreg_cmd set_rfreg; 95 struct nci_prop_stop_rfreg_cmd stop_rfreg; 96 u32 checksum; 97 int i, len; 98 int ret; 99 100 ret = request_firmware(&fw, fw_name, &info->ndev->nfc_dev->dev); 101 if (ret < 0) 102 return ret; 103 104 /* Compute rfreg checksum */ 105 106 checksum = 0; 107 for (i = 0; i < fw->size; i += 4) 108 checksum += *((u32 *)(fw->data+i)); 109 110 /* Set default clock configuration for external crystal */ 111 112 fw_cfg.clk_type = 0x01; 113 fw_cfg.clk_speed = 0xff; 114 fw_cfg.clk_req = 0xff; 115 ret = nci_prop_cmd(info->ndev, NCI_PROP_FW_CFG, 116 sizeof(fw_cfg), (__u8 *)&fw_cfg); 117 if (ret < 0) 118 goto out; 119 120 /* Start rfreg configuration */ 121 122 dev_info(&info->ndev->nfc_dev->dev, 123 "rfreg configuration update: %s\n", fw_name); 124 125 ret = nci_prop_cmd(info->ndev, NCI_PROP_START_RFREG, 0, NULL); 126 if (ret < 0) { 127 dev_err(&info->ndev->nfc_dev->dev, 128 "Unable to start rfreg update\n"); 129 goto out; 130 } 131 132 /* Update rfreg */ 133 134 set_rfreg.index = 0; 135 for (i = 0; i < fw->size; i += S3FWRN5_RFREG_SECTION_SIZE) { 136 len = (fw->size - i < S3FWRN5_RFREG_SECTION_SIZE) ? 137 (fw->size - i) : S3FWRN5_RFREG_SECTION_SIZE; 138 memcpy(set_rfreg.data, fw->data+i, len); 139 ret = nci_prop_cmd(info->ndev, NCI_PROP_SET_RFREG, 140 len+1, (__u8 *)&set_rfreg); 141 if (ret < 0) { 142 dev_err(&info->ndev->nfc_dev->dev, 143 "rfreg update error (code=%d)\n", ret); 144 goto out; 145 } 146 set_rfreg.index++; 147 } 148 149 /* Finish rfreg configuration */ 150 151 stop_rfreg.checksum = checksum & 0xffff; 152 ret = nci_prop_cmd(info->ndev, NCI_PROP_STOP_RFREG, 153 sizeof(stop_rfreg), (__u8 *)&stop_rfreg); 154 if (ret < 0) { 155 dev_err(&info->ndev->nfc_dev->dev, 156 "Unable to stop rfreg update\n"); 157 goto out; 158 } 159 160 dev_info(&info->ndev->nfc_dev->dev, 161 "rfreg configuration update: success\n"); 162 out: 163 release_firmware(fw); 164 return ret; 165 } 166