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_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 void s3fwrn5_nci_get_prop_ops(struct nci_driver_ops **ops, size_t *n) 47 { 48 *ops = s3fwrn5_nci_prop_ops; 49 *n = ARRAY_SIZE(s3fwrn5_nci_prop_ops); 50 } 51 52 #define S3FWRN5_RFREG_SECTION_SIZE 252 53 54 int s3fwrn5_nci_rf_configure(struct s3fwrn5_info *info, const char *fw_name) 55 { 56 const struct firmware *fw; 57 struct nci_prop_fw_cfg_cmd fw_cfg; 58 struct nci_prop_set_rfreg_cmd set_rfreg; 59 struct nci_prop_stop_rfreg_cmd stop_rfreg; 60 u32 checksum; 61 int i, len; 62 int ret; 63 64 ret = request_firmware(&fw, fw_name, &info->ndev->nfc_dev->dev); 65 if (ret < 0) 66 return ret; 67 68 /* Compute rfreg checksum */ 69 70 checksum = 0; 71 for (i = 0; i < fw->size; i += 4) 72 checksum += *((u32 *)(fw->data+i)); 73 74 /* Set default clock configuration for external crystal */ 75 76 fw_cfg.clk_type = 0x01; 77 fw_cfg.clk_speed = 0xff; 78 fw_cfg.clk_req = 0xff; 79 ret = nci_prop_cmd(info->ndev, NCI_PROP_FW_CFG, 80 sizeof(fw_cfg), (__u8 *)&fw_cfg); 81 if (ret < 0) 82 goto out; 83 84 /* Start rfreg configuration */ 85 86 dev_info(&info->ndev->nfc_dev->dev, 87 "rfreg configuration update: %s\n", fw_name); 88 89 ret = nci_prop_cmd(info->ndev, NCI_PROP_START_RFREG, 0, NULL); 90 if (ret < 0) { 91 dev_err(&info->ndev->nfc_dev->dev, 92 "Unable to start rfreg update\n"); 93 goto out; 94 } 95 96 /* Update rfreg */ 97 98 set_rfreg.index = 0; 99 for (i = 0; i < fw->size; i += S3FWRN5_RFREG_SECTION_SIZE) { 100 len = (fw->size - i < S3FWRN5_RFREG_SECTION_SIZE) ? 101 (fw->size - i) : S3FWRN5_RFREG_SECTION_SIZE; 102 memcpy(set_rfreg.data, fw->data+i, len); 103 ret = nci_prop_cmd(info->ndev, NCI_PROP_SET_RFREG, 104 len+1, (__u8 *)&set_rfreg); 105 if (ret < 0) { 106 dev_err(&info->ndev->nfc_dev->dev, 107 "rfreg update error (code=%d)\n", ret); 108 goto out; 109 } 110 set_rfreg.index++; 111 } 112 113 /* Finish rfreg configuration */ 114 115 stop_rfreg.checksum = checksum & 0xffff; 116 ret = nci_prop_cmd(info->ndev, NCI_PROP_STOP_RFREG, 117 sizeof(stop_rfreg), (__u8 *)&stop_rfreg); 118 if (ret < 0) { 119 dev_err(&info->ndev->nfc_dev->dev, 120 "Unable to stop rfreg update\n"); 121 goto out; 122 } 123 124 dev_info(&info->ndev->nfc_dev->dev, 125 "rfreg configuration update: success\n"); 126 out: 127 release_firmware(fw); 128 return ret; 129 } 130