xref: /openbmc/linux/drivers/nfc/s3fwrn5/core.c (revision 7bc5c428)
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/module.h>
10 #include <net/nfc/nci_core.h>
11 
12 #include "s3fwrn5.h"
13 #include "firmware.h"
14 #include "nci.h"
15 
16 #define S3FWRN5_NFC_PROTOCOLS  (NFC_PROTO_JEWEL_MASK | \
17 				NFC_PROTO_MIFARE_MASK | \
18 				NFC_PROTO_FELICA_MASK | \
19 				NFC_PROTO_ISO14443_MASK | \
20 				NFC_PROTO_ISO14443_B_MASK | \
21 				NFC_PROTO_ISO15693_MASK)
22 
23 static int s3fwrn5_firmware_update(struct s3fwrn5_info *info)
24 {
25 	bool need_update;
26 	int ret;
27 
28 	s3fwrn5_fw_init(&info->fw_info, "sec_s3fwrn5_firmware.bin");
29 
30 	/* Update firmware */
31 
32 	s3fwrn5_set_wake(info, false);
33 	s3fwrn5_set_mode(info, S3FWRN5_MODE_FW);
34 
35 	ret = s3fwrn5_fw_setup(&info->fw_info);
36 	if (ret < 0)
37 		return ret;
38 
39 	need_update = s3fwrn5_fw_check_version(&info->fw_info,
40 		info->ndev->manufact_specific_info);
41 	if (!need_update)
42 		goto out;
43 
44 	dev_info(&info->ndev->nfc_dev->dev, "Detected new firmware version\n");
45 
46 	ret = s3fwrn5_fw_download(&info->fw_info);
47 	if (ret < 0)
48 		goto out;
49 
50 	/* Update RF configuration */
51 
52 	s3fwrn5_set_mode(info, S3FWRN5_MODE_NCI);
53 
54 	s3fwrn5_set_wake(info, true);
55 	ret = s3fwrn5_nci_rf_configure(info, "sec_s3fwrn5_rfreg.bin");
56 	s3fwrn5_set_wake(info, false);
57 
58 out:
59 	s3fwrn5_set_mode(info, S3FWRN5_MODE_COLD);
60 	s3fwrn5_fw_cleanup(&info->fw_info);
61 	return ret;
62 }
63 
64 static int s3fwrn5_nci_open(struct nci_dev *ndev)
65 {
66 	struct s3fwrn5_info *info = nci_get_drvdata(ndev);
67 
68 	if (s3fwrn5_get_mode(info) != S3FWRN5_MODE_COLD)
69 		return  -EBUSY;
70 
71 	s3fwrn5_set_mode(info, S3FWRN5_MODE_NCI);
72 	s3fwrn5_set_wake(info, true);
73 
74 	return 0;
75 }
76 
77 static int s3fwrn5_nci_close(struct nci_dev *ndev)
78 {
79 	struct s3fwrn5_info *info = nci_get_drvdata(ndev);
80 
81 	s3fwrn5_set_wake(info, false);
82 	s3fwrn5_set_mode(info, S3FWRN5_MODE_COLD);
83 
84 	return 0;
85 }
86 
87 static int s3fwrn5_nci_send(struct nci_dev *ndev, struct sk_buff *skb)
88 {
89 	struct s3fwrn5_info *info = nci_get_drvdata(ndev);
90 	int ret;
91 
92 	mutex_lock(&info->mutex);
93 
94 	if (s3fwrn5_get_mode(info) != S3FWRN5_MODE_NCI) {
95 		mutex_unlock(&info->mutex);
96 		return -EINVAL;
97 	}
98 
99 	ret = s3fwrn5_write(info, skb);
100 	if (ret < 0)
101 		kfree_skb(skb);
102 
103 	mutex_unlock(&info->mutex);
104 	return ret;
105 }
106 
107 static int s3fwrn5_nci_post_setup(struct nci_dev *ndev)
108 {
109 	struct s3fwrn5_info *info = nci_get_drvdata(ndev);
110 	int ret;
111 
112 	ret = s3fwrn5_firmware_update(info);
113 	if (ret < 0)
114 		goto out;
115 
116 	/* NCI core reset */
117 
118 	s3fwrn5_set_mode(info, S3FWRN5_MODE_NCI);
119 	s3fwrn5_set_wake(info, true);
120 
121 	ret = nci_core_reset(info->ndev);
122 	if (ret < 0)
123 		goto out;
124 
125 	ret = nci_core_init(info->ndev);
126 
127 out:
128 	return ret;
129 }
130 
131 static struct nci_ops s3fwrn5_nci_ops = {
132 	.open = s3fwrn5_nci_open,
133 	.close = s3fwrn5_nci_close,
134 	.send = s3fwrn5_nci_send,
135 	.post_setup = s3fwrn5_nci_post_setup,
136 };
137 
138 int s3fwrn5_probe(struct nci_dev **ndev, void *phy_id, struct device *pdev,
139 	const struct s3fwrn5_phy_ops *phy_ops, unsigned int max_payload)
140 {
141 	struct s3fwrn5_info *info;
142 	int ret;
143 
144 	info = devm_kzalloc(pdev, sizeof(*info), GFP_KERNEL);
145 	if (!info)
146 		return -ENOMEM;
147 
148 	info->phy_id = phy_id;
149 	info->pdev = pdev;
150 	info->phy_ops = phy_ops;
151 	info->max_payload = max_payload;
152 	mutex_init(&info->mutex);
153 
154 	s3fwrn5_set_mode(info, S3FWRN5_MODE_COLD);
155 
156 	s3fwrn5_nci_get_prop_ops(&s3fwrn5_nci_ops.prop_ops,
157 		&s3fwrn5_nci_ops.n_prop_ops);
158 
159 	info->ndev = nci_allocate_device(&s3fwrn5_nci_ops,
160 		S3FWRN5_NFC_PROTOCOLS, 0, 0);
161 	if (!info->ndev)
162 		return -ENOMEM;
163 
164 	nci_set_parent_dev(info->ndev, pdev);
165 	nci_set_drvdata(info->ndev, info);
166 
167 	ret = nci_register_device(info->ndev);
168 	if (ret < 0) {
169 		nci_free_device(info->ndev);
170 		return ret;
171 	}
172 
173 	info->fw_info.ndev = info->ndev;
174 
175 	*ndev = info->ndev;
176 
177 	return ret;
178 }
179 EXPORT_SYMBOL(s3fwrn5_probe);
180 
181 void s3fwrn5_remove(struct nci_dev *ndev)
182 {
183 	struct s3fwrn5_info *info = nci_get_drvdata(ndev);
184 
185 	s3fwrn5_set_mode(info, S3FWRN5_MODE_COLD);
186 
187 	nci_unregister_device(ndev);
188 	nci_free_device(ndev);
189 }
190 EXPORT_SYMBOL(s3fwrn5_remove);
191 
192 int s3fwrn5_recv_frame(struct nci_dev *ndev, struct sk_buff *skb,
193 	enum s3fwrn5_mode mode)
194 {
195 	switch (mode) {
196 	case S3FWRN5_MODE_NCI:
197 		return nci_recv_frame(ndev, skb);
198 	case S3FWRN5_MODE_FW:
199 		return s3fwrn5_fw_recv_frame(ndev, skb);
200 	default:
201 		kfree_skb(skb);
202 		return -ENODEV;
203 	}
204 }
205 EXPORT_SYMBOL(s3fwrn5_recv_frame);
206 
207 MODULE_LICENSE("GPL");
208 MODULE_DESCRIPTION("Samsung S3FWRN5 NFC driver");
209 MODULE_AUTHOR("Robert Baldyga <r.baldyga@samsung.com>");
210