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