Commit | Line | Data |
---|---|---|
f26e30cc AK |
1 | /* |
2 | * Marvell NFC driver: major functions | |
3 | * | |
fb101c0e | 4 | * Copyright (C) 2014-2015 Marvell International Ltd. |
f26e30cc AK |
5 | * |
6 | * This software file (the "File") is distributed by Marvell International | |
7 | * Ltd. under the terms of the GNU General Public License Version 2, June 1991 | |
8 | * (the "License"). You may use, redistribute and/or modify this File in | |
9 | * accordance with the terms and conditions of the License, a copy of which | |
10 | * is available on the worldwide web at | |
11 | * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. | |
12 | * | |
13 | * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE | |
14 | * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE | |
15 | * ARE EXPRESSLY DISCLAIMED. The License provides additional details about | |
16 | * this warranty disclaimer. | |
17 | */ | |
18 | ||
19 | #include <linux/module.h> | |
4a2b947f VC |
20 | #include <linux/gpio.h> |
21 | #include <linux/delay.h> | |
dc14bdef | 22 | #include <linux/of_gpio.h> |
f26e30cc AK |
23 | #include <linux/nfc.h> |
24 | #include <net/nfc/nci.h> | |
25 | #include <net/nfc/nci_core.h> | |
26 | #include "nfcmrvl.h" | |
27 | ||
f26e30cc AK |
28 | static int nfcmrvl_nci_open(struct nci_dev *ndev) |
29 | { | |
30 | struct nfcmrvl_private *priv = nci_get_drvdata(ndev); | |
31 | int err; | |
32 | ||
33 | if (test_and_set_bit(NFCMRVL_NCI_RUNNING, &priv->flags)) | |
34 | return 0; | |
35 | ||
b5b3e23e VC |
36 | /* Reset possible fault of previous session */ |
37 | clear_bit(NFCMRVL_PHY_ERROR, &priv->flags); | |
38 | ||
f26e30cc AK |
39 | err = priv->if_ops->nci_open(priv); |
40 | ||
41 | if (err) | |
42 | clear_bit(NFCMRVL_NCI_RUNNING, &priv->flags); | |
43 | ||
44 | return err; | |
45 | } | |
46 | ||
47 | static int nfcmrvl_nci_close(struct nci_dev *ndev) | |
48 | { | |
49 | struct nfcmrvl_private *priv = nci_get_drvdata(ndev); | |
50 | ||
51 | if (!test_and_clear_bit(NFCMRVL_NCI_RUNNING, &priv->flags)) | |
52 | return 0; | |
53 | ||
54 | priv->if_ops->nci_close(priv); | |
55 | ||
56 | return 0; | |
57 | } | |
58 | ||
59 | static int nfcmrvl_nci_send(struct nci_dev *ndev, struct sk_buff *skb) | |
60 | { | |
61 | struct nfcmrvl_private *priv = nci_get_drvdata(ndev); | |
62 | ||
63 | nfc_info(priv->dev, "send entry, len %d\n", skb->len); | |
64 | ||
65 | skb->dev = (void *)ndev; | |
66 | ||
dc14bdef | 67 | if (priv->config.hci_muxed) { |
f1f1a7da VC |
68 | unsigned char *hdr; |
69 | unsigned char len = skb->len; | |
70 | ||
d58ff351 | 71 | hdr = skb_push(skb, NFCMRVL_HCI_EVENT_HEADER_SIZE); |
f1f1a7da VC |
72 | hdr[0] = NFCMRVL_HCI_COMMAND_CODE; |
73 | hdr[1] = NFCMRVL_HCI_OGF; | |
74 | hdr[2] = NFCMRVL_HCI_OCF; | |
75 | hdr[3] = len; | |
76 | } | |
77 | ||
f26e30cc AK |
78 | return priv->if_ops->nci_send(priv, skb); |
79 | } | |
80 | ||
15203b4c AK |
81 | static int nfcmrvl_nci_setup(struct nci_dev *ndev) |
82 | { | |
d0dcad8b VC |
83 | __u8 val = 1; |
84 | ||
85 | nci_set_config(ndev, NFCMRVL_PB_BAIL_OUT, 1, &val); | |
15203b4c AK |
86 | return 0; |
87 | } | |
88 | ||
3194c687 VC |
89 | static int nfcmrvl_nci_fw_download(struct nci_dev *ndev, |
90 | const char *firmware_name) | |
91 | { | |
92 | return nfcmrvl_fw_dnld_start(ndev, firmware_name); | |
93 | } | |
94 | ||
f26e30cc AK |
95 | static struct nci_ops nfcmrvl_nci_ops = { |
96 | .open = nfcmrvl_nci_open, | |
97 | .close = nfcmrvl_nci_close, | |
98 | .send = nfcmrvl_nci_send, | |
15203b4c | 99 | .setup = nfcmrvl_nci_setup, |
3194c687 | 100 | .fw_download = nfcmrvl_nci_fw_download, |
f26e30cc AK |
101 | }; |
102 | ||
58d34aa6 VC |
103 | struct nfcmrvl_private *nfcmrvl_nci_register_dev(enum nfcmrvl_phy phy, |
104 | void *drv_data, | |
dc14bdef VC |
105 | struct nfcmrvl_if_ops *ops, |
106 | struct device *dev, | |
107 | struct nfcmrvl_platform_data *pdata) | |
f26e30cc AK |
108 | { |
109 | struct nfcmrvl_private *priv; | |
110 | int rc; | |
58d34aa6 VC |
111 | int headroom; |
112 | int tailroom; | |
f26e30cc AK |
113 | u32 protocols; |
114 | ||
115 | priv = kzalloc(sizeof(*priv), GFP_KERNEL); | |
116 | if (!priv) | |
117 | return ERR_PTR(-ENOMEM); | |
118 | ||
119 | priv->drv_data = drv_data; | |
120 | priv->if_ops = ops; | |
121 | priv->dev = dev; | |
58d34aa6 | 122 | priv->phy = phy; |
4a2b947f | 123 | |
dc14bdef VC |
124 | memcpy(&priv->config, pdata, sizeof(*pdata)); |
125 | ||
126 | if (priv->config.reset_n_io) { | |
4a2b947f | 127 | rc = devm_gpio_request_one(dev, |
dc14bdef | 128 | priv->config.reset_n_io, |
4a2b947f VC |
129 | GPIOF_OUT_INIT_LOW, |
130 | "nfcmrvl_reset_n"); | |
131 | if (rc < 0) | |
132 | nfc_err(dev, "failed to request reset_n io\n"); | |
133 | } | |
f1f1a7da | 134 | |
caf6e49b VC |
135 | if (phy == NFCMRVL_PHY_SPI) { |
136 | headroom = NCI_SPI_HDR_LEN; | |
137 | tailroom = 1; | |
138 | } else | |
139 | headroom = tailroom = 0; | |
58d34aa6 | 140 | |
dc14bdef | 141 | if (priv->config.hci_muxed) |
58d34aa6 | 142 | headroom += NFCMRVL_HCI_EVENT_HEADER_SIZE; |
f26e30cc AK |
143 | |
144 | protocols = NFC_PROTO_JEWEL_MASK | |
dc14bdef VC |
145 | | NFC_PROTO_MIFARE_MASK |
146 | | NFC_PROTO_FELICA_MASK | |
f26e30cc AK |
147 | | NFC_PROTO_ISO14443_MASK |
148 | | NFC_PROTO_ISO14443_B_MASK | |
83d56725 | 149 | | NFC_PROTO_ISO15693_MASK |
f26e30cc AK |
150 | | NFC_PROTO_NFC_DEP_MASK; |
151 | ||
f1f1a7da | 152 | priv->ndev = nci_allocate_device(&nfcmrvl_nci_ops, protocols, |
58d34aa6 | 153 | headroom, tailroom); |
f26e30cc | 154 | if (!priv->ndev) { |
3590ebc0 | 155 | nfc_err(dev, "nci_allocate_device failed\n"); |
bb55dc2a AK |
156 | rc = -ENOMEM; |
157 | goto error; | |
f26e30cc AK |
158 | } |
159 | ||
160 | nci_set_drvdata(priv->ndev, priv); | |
161 | ||
162 | rc = nci_register_device(priv->ndev); | |
163 | if (rc) { | |
3590ebc0 | 164 | nfc_err(dev, "nci_register_device failed %d\n", rc); |
3194c687 VC |
165 | goto error_free_dev; |
166 | } | |
167 | ||
168 | /* Ensure that controller is powered off */ | |
169 | nfcmrvl_chip_halt(priv); | |
170 | ||
171 | rc = nfcmrvl_fw_dnld_init(priv); | |
172 | if (rc) { | |
173 | nfc_err(dev, "failed to initialize FW download %d\n", rc); | |
174 | goto error_free_dev; | |
f26e30cc AK |
175 | } |
176 | ||
177 | nfc_info(dev, "registered with nci successfully\n"); | |
178 | return priv; | |
bb55dc2a | 179 | |
3194c687 VC |
180 | error_free_dev: |
181 | nci_free_device(priv->ndev); | |
bb55dc2a AK |
182 | error: |
183 | kfree(priv); | |
184 | return ERR_PTR(rc); | |
f26e30cc AK |
185 | } |
186 | EXPORT_SYMBOL_GPL(nfcmrvl_nci_register_dev); | |
187 | ||
188 | void nfcmrvl_nci_unregister_dev(struct nfcmrvl_private *priv) | |
189 | { | |
190 | struct nci_dev *ndev = priv->ndev; | |
191 | ||
3194c687 VC |
192 | if (priv->ndev->nfc_dev->fw_download_in_progress) |
193 | nfcmrvl_fw_dnld_abort(priv); | |
194 | ||
195 | nfcmrvl_fw_dnld_deinit(priv); | |
196 | ||
b2fe288e VC |
197 | if (priv->config.reset_n_io) |
198 | devm_gpio_free(priv->dev, priv->config.reset_n_io); | |
199 | ||
f26e30cc AK |
200 | nci_unregister_device(ndev); |
201 | nci_free_device(ndev); | |
202 | kfree(priv); | |
203 | } | |
204 | EXPORT_SYMBOL_GPL(nfcmrvl_nci_unregister_dev); | |
205 | ||
e1bf80c2 | 206 | int nfcmrvl_nci_recv_frame(struct nfcmrvl_private *priv, struct sk_buff *skb) |
f26e30cc | 207 | { |
dc14bdef | 208 | if (priv->config.hci_muxed) { |
f1f1a7da VC |
209 | if (skb->data[0] == NFCMRVL_HCI_EVENT_CODE && |
210 | skb->data[1] == NFCMRVL_HCI_NFC_EVENT_CODE) { | |
211 | /* Data packet, let's extract NCI payload */ | |
212 | skb_pull(skb, NFCMRVL_HCI_EVENT_HEADER_SIZE); | |
213 | } else { | |
214 | /* Skip this packet */ | |
215 | kfree_skb(skb); | |
216 | return 0; | |
217 | } | |
218 | } | |
219 | ||
3194c687 VC |
220 | if (priv->ndev->nfc_dev->fw_download_in_progress) { |
221 | nfcmrvl_fw_dnld_recv_frame(priv, skb); | |
222 | return 0; | |
223 | } | |
224 | ||
e1bf80c2 VC |
225 | if (test_bit(NFCMRVL_NCI_RUNNING, &priv->flags)) |
226 | nci_recv_frame(priv->ndev, skb); | |
227 | else { | |
228 | /* Drop this packet since nobody wants it */ | |
229 | kfree_skb(skb); | |
230 | return 0; | |
231 | } | |
f26e30cc | 232 | |
e1bf80c2 | 233 | return 0; |
f26e30cc AK |
234 | } |
235 | EXPORT_SYMBOL_GPL(nfcmrvl_nci_recv_frame); | |
236 | ||
4a2b947f VC |
237 | void nfcmrvl_chip_reset(struct nfcmrvl_private *priv) |
238 | { | |
b5b3e23e VC |
239 | /* Reset possible fault of previous session */ |
240 | clear_bit(NFCMRVL_PHY_ERROR, &priv->flags); | |
4a2b947f | 241 | |
dc14bdef | 242 | if (priv->config.reset_n_io) { |
4a2b947f | 243 | nfc_info(priv->dev, "reset the chip\n"); |
dc14bdef | 244 | gpio_set_value(priv->config.reset_n_io, 0); |
4a2b947f | 245 | usleep_range(5000, 10000); |
dc14bdef | 246 | gpio_set_value(priv->config.reset_n_io, 1); |
4a2b947f VC |
247 | } else |
248 | nfc_info(priv->dev, "no reset available on this interface\n"); | |
249 | } | |
250 | ||
3194c687 VC |
251 | void nfcmrvl_chip_halt(struct nfcmrvl_private *priv) |
252 | { | |
253 | if (priv->config.reset_n_io) | |
254 | gpio_set_value(priv->config.reset_n_io, 0); | |
255 | } | |
256 | ||
dc14bdef VC |
257 | int nfcmrvl_parse_dt(struct device_node *node, |
258 | struct nfcmrvl_platform_data *pdata) | |
259 | { | |
260 | int reset_n_io; | |
261 | ||
262 | reset_n_io = of_get_named_gpio(node, "reset-n-io", 0); | |
263 | if (reset_n_io < 0) { | |
264 | pr_info("no reset-n-io config\n"); | |
265 | reset_n_io = 0; | |
266 | } else if (!gpio_is_valid(reset_n_io)) { | |
267 | pr_err("invalid reset-n-io GPIO\n"); | |
268 | return reset_n_io; | |
269 | } | |
270 | pdata->reset_n_io = reset_n_io; | |
271 | ||
272 | if (of_find_property(node, "hci-muxed", NULL)) | |
273 | pdata->hci_muxed = 1; | |
274 | else | |
275 | pdata->hci_muxed = 0; | |
276 | ||
277 | return 0; | |
278 | } | |
dc14bdef VC |
279 | EXPORT_SYMBOL_GPL(nfcmrvl_parse_dt); |
280 | ||
f26e30cc | 281 | MODULE_AUTHOR("Marvell International Ltd."); |
fb101c0e | 282 | MODULE_DESCRIPTION("Marvell NFC driver"); |
f26e30cc | 283 | MODULE_LICENSE("GPL v2"); |