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 
s3fwrn5_firmware_update(struct s3fwrn5_info *info)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 
s3fwrn5_nci_open(struct nci_dev *ndev)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 
s3fwrn5_nci_close(struct nci_dev *ndev)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 
s3fwrn5_nci_send(struct nci_dev *ndev, struct sk_buff *skb)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 		mutex_unlock(&info->mutex);
103 		return ret;
104 	}
105 
106 	consume_skb(skb);
107 	mutex_unlock(&info->mutex);
108 	return 0;
109 }
110 
s3fwrn5_nci_post_setup(struct nci_dev *ndev)111 static int s3fwrn5_nci_post_setup(struct nci_dev *ndev)
112 {
113 	struct s3fwrn5_info *info = nci_get_drvdata(ndev);
114 	int ret;
115 
116 	ret = s3fwrn5_firmware_update(info);
117 	if (ret < 0)
118 		goto out;
119 
120 	/* NCI core reset */
121 
122 	s3fwrn5_set_mode(info, S3FWRN5_MODE_NCI);
123 	s3fwrn5_set_wake(info, true);
124 
125 	ret = nci_core_reset(info->ndev);
126 	if (ret < 0)
127 		goto out;
128 
129 	ret = nci_core_init(info->ndev);
130 
131 out:
132 	return ret;
133 }
134 
135 static struct nci_ops s3fwrn5_nci_ops = {
136 	.open = s3fwrn5_nci_open,
137 	.close = s3fwrn5_nci_close,
138 	.send = s3fwrn5_nci_send,
139 	.post_setup = s3fwrn5_nci_post_setup,
140 };
141 
s3fwrn5_probe(struct nci_dev **ndev, void *phy_id, struct device *pdev, const struct s3fwrn5_phy_ops *phy_ops, unsigned int max_payload)142 int s3fwrn5_probe(struct nci_dev **ndev, void *phy_id, struct device *pdev,
143 	const struct s3fwrn5_phy_ops *phy_ops, unsigned int max_payload)
144 {
145 	struct s3fwrn5_info *info;
146 	int ret;
147 
148 	info = devm_kzalloc(pdev, sizeof(*info), GFP_KERNEL);
149 	if (!info)
150 		return -ENOMEM;
151 
152 	info->phy_id = phy_id;
153 	info->pdev = pdev;
154 	info->phy_ops = phy_ops;
155 	info->max_payload = max_payload;
156 	mutex_init(&info->mutex);
157 
158 	s3fwrn5_set_mode(info, S3FWRN5_MODE_COLD);
159 
160 	s3fwrn5_nci_get_prop_ops(&s3fwrn5_nci_ops.prop_ops,
161 		&s3fwrn5_nci_ops.n_prop_ops);
162 
163 	info->ndev = nci_allocate_device(&s3fwrn5_nci_ops,
164 		S3FWRN5_NFC_PROTOCOLS, 0, 0);
165 	if (!info->ndev)
166 		return -ENOMEM;
167 
168 	nci_set_parent_dev(info->ndev, pdev);
169 	nci_set_drvdata(info->ndev, info);
170 
171 	ret = nci_register_device(info->ndev);
172 	if (ret < 0) {
173 		nci_free_device(info->ndev);
174 		return ret;
175 	}
176 
177 	info->fw_info.ndev = info->ndev;
178 
179 	*ndev = info->ndev;
180 
181 	return ret;
182 }
183 EXPORT_SYMBOL(s3fwrn5_probe);
184 
s3fwrn5_remove(struct nci_dev *ndev)185 void s3fwrn5_remove(struct nci_dev *ndev)
186 {
187 	struct s3fwrn5_info *info = nci_get_drvdata(ndev);
188 
189 	s3fwrn5_set_mode(info, S3FWRN5_MODE_COLD);
190 
191 	nci_unregister_device(ndev);
192 	nci_free_device(ndev);
193 }
194 EXPORT_SYMBOL(s3fwrn5_remove);
195 
s3fwrn5_recv_frame(struct nci_dev *ndev, struct sk_buff *skb, enum s3fwrn5_mode mode)196 int s3fwrn5_recv_frame(struct nci_dev *ndev, struct sk_buff *skb,
197 	enum s3fwrn5_mode mode)
198 {
199 	switch (mode) {
200 	case S3FWRN5_MODE_NCI:
201 		return nci_recv_frame(ndev, skb);
202 	case S3FWRN5_MODE_FW:
203 		return s3fwrn5_fw_recv_frame(ndev, skb);
204 	default:
205 		kfree_skb(skb);
206 		return -ENODEV;
207 	}
208 }
209 EXPORT_SYMBOL(s3fwrn5_recv_frame);
210 
211 MODULE_LICENSE("GPL");
212 MODULE_DESCRIPTION("Samsung S3FWRN5 NFC driver");
213 MODULE_AUTHOR("Robert Baldyga <r.baldyga@samsung.com>");
214