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