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