1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Marvell NFC driver: major functions |
4 | * |
5 | * Copyright (C) 2014-2015 Marvell International Ltd. |
6 | */ |
7 | |
8 | #include <linux/module.h> |
9 | #include <linux/gpio.h> |
10 | #include <linux/delay.h> |
11 | #include <linux/of_gpio.h> |
12 | #include <linux/nfc.h> |
13 | #include <net/nfc/nci.h> |
14 | #include <net/nfc/nci_core.h> |
15 | #include "nfcmrvl.h" |
16 | |
17 | static int nfcmrvl_nci_open(struct nci_dev *ndev) |
18 | { |
19 | struct nfcmrvl_private *priv = nci_get_drvdata(ndev); |
20 | int err; |
21 | |
22 | if (test_and_set_bit(NFCMRVL_NCI_RUNNING, addr: &priv->flags)) |
23 | return 0; |
24 | |
25 | /* Reset possible fault of previous session */ |
26 | clear_bit(NFCMRVL_PHY_ERROR, addr: &priv->flags); |
27 | |
28 | err = priv->if_ops->nci_open(priv); |
29 | |
30 | if (err) |
31 | clear_bit(NFCMRVL_NCI_RUNNING, addr: &priv->flags); |
32 | |
33 | return err; |
34 | } |
35 | |
36 | static int nfcmrvl_nci_close(struct nci_dev *ndev) |
37 | { |
38 | struct nfcmrvl_private *priv = nci_get_drvdata(ndev); |
39 | |
40 | if (!test_and_clear_bit(NFCMRVL_NCI_RUNNING, addr: &priv->flags)) |
41 | return 0; |
42 | |
43 | priv->if_ops->nci_close(priv); |
44 | |
45 | return 0; |
46 | } |
47 | |
48 | static int nfcmrvl_nci_send(struct nci_dev *ndev, struct sk_buff *skb) |
49 | { |
50 | struct nfcmrvl_private *priv = nci_get_drvdata(ndev); |
51 | |
52 | nfc_info(priv->dev, "send entry, len %d\n" , skb->len); |
53 | |
54 | skb->dev = (void *)ndev; |
55 | |
56 | if (priv->config.hci_muxed) { |
57 | unsigned char *hdr; |
58 | unsigned char len = skb->len; |
59 | |
60 | hdr = skb_push(skb, NFCMRVL_HCI_EVENT_HEADER_SIZE); |
61 | hdr[0] = NFCMRVL_HCI_COMMAND_CODE; |
62 | hdr[1] = NFCMRVL_HCI_OGF; |
63 | hdr[2] = NFCMRVL_HCI_OCF; |
64 | hdr[3] = len; |
65 | } |
66 | |
67 | return priv->if_ops->nci_send(priv, skb); |
68 | } |
69 | |
70 | static int nfcmrvl_nci_setup(struct nci_dev *ndev) |
71 | { |
72 | __u8 val = 1; |
73 | |
74 | nci_set_config(ndev, NFCMRVL_PB_BAIL_OUT, len: 1, val: &val); |
75 | return 0; |
76 | } |
77 | |
78 | static int nfcmrvl_nci_fw_download(struct nci_dev *ndev, |
79 | const char *firmware_name) |
80 | { |
81 | return nfcmrvl_fw_dnld_start(ndev, firmware_name); |
82 | } |
83 | |
84 | static const struct nci_ops nfcmrvl_nci_ops = { |
85 | .open = nfcmrvl_nci_open, |
86 | .close = nfcmrvl_nci_close, |
87 | .send = nfcmrvl_nci_send, |
88 | .setup = nfcmrvl_nci_setup, |
89 | .fw_download = nfcmrvl_nci_fw_download, |
90 | }; |
91 | |
92 | struct nfcmrvl_private *nfcmrvl_nci_register_dev(enum nfcmrvl_phy phy, |
93 | void *drv_data, |
94 | const struct nfcmrvl_if_ops *ops, |
95 | struct device *dev, |
96 | const struct nfcmrvl_platform_data *pdata) |
97 | { |
98 | struct nfcmrvl_private *priv; |
99 | int rc; |
100 | int headroom; |
101 | int tailroom; |
102 | u32 protocols; |
103 | |
104 | priv = kzalloc(size: sizeof(*priv), GFP_KERNEL); |
105 | if (!priv) |
106 | return ERR_PTR(error: -ENOMEM); |
107 | |
108 | priv->drv_data = drv_data; |
109 | priv->if_ops = ops; |
110 | priv->dev = dev; |
111 | priv->phy = phy; |
112 | |
113 | memcpy(&priv->config, pdata, sizeof(*pdata)); |
114 | |
115 | if (gpio_is_valid(number: priv->config.reset_n_io)) { |
116 | rc = gpio_request_one(gpio: priv->config.reset_n_io, |
117 | GPIOF_OUT_INIT_LOW, |
118 | label: "nfcmrvl_reset_n" ); |
119 | if (rc < 0) { |
120 | priv->config.reset_n_io = -EINVAL; |
121 | nfc_err(dev, "failed to request reset_n io\n" ); |
122 | } |
123 | } |
124 | |
125 | if (phy == NFCMRVL_PHY_SPI) { |
126 | headroom = NCI_SPI_HDR_LEN; |
127 | tailroom = 1; |
128 | } else |
129 | headroom = tailroom = 0; |
130 | |
131 | if (priv->config.hci_muxed) |
132 | headroom += NFCMRVL_HCI_EVENT_HEADER_SIZE; |
133 | |
134 | protocols = NFC_PROTO_JEWEL_MASK |
135 | | NFC_PROTO_MIFARE_MASK |
136 | | NFC_PROTO_FELICA_MASK |
137 | | NFC_PROTO_ISO14443_MASK |
138 | | NFC_PROTO_ISO14443_B_MASK |
139 | | NFC_PROTO_ISO15693_MASK |
140 | | NFC_PROTO_NFC_DEP_MASK; |
141 | |
142 | priv->ndev = nci_allocate_device(ops: &nfcmrvl_nci_ops, supported_protocols: protocols, |
143 | tx_headroom: headroom, tx_tailroom: tailroom); |
144 | if (!priv->ndev) { |
145 | nfc_err(dev, "nci_allocate_device failed\n" ); |
146 | rc = -ENOMEM; |
147 | goto error_free_gpio; |
148 | } |
149 | |
150 | rc = nfcmrvl_fw_dnld_init(priv); |
151 | if (rc) { |
152 | nfc_err(dev, "failed to initialize FW download %d\n" , rc); |
153 | goto error_free_dev; |
154 | } |
155 | |
156 | nci_set_drvdata(ndev: priv->ndev, data: priv); |
157 | |
158 | rc = nci_register_device(ndev: priv->ndev); |
159 | if (rc) { |
160 | nfc_err(dev, "nci_register_device failed %d\n" , rc); |
161 | goto error_fw_dnld_deinit; |
162 | } |
163 | |
164 | /* Ensure that controller is powered off */ |
165 | nfcmrvl_chip_halt(priv); |
166 | |
167 | nfc_info(dev, "registered with nci successfully\n" ); |
168 | return priv; |
169 | |
170 | error_fw_dnld_deinit: |
171 | nfcmrvl_fw_dnld_deinit(priv); |
172 | error_free_dev: |
173 | nci_free_device(ndev: priv->ndev); |
174 | error_free_gpio: |
175 | if (gpio_is_valid(number: priv->config.reset_n_io)) |
176 | gpio_free(gpio: priv->config.reset_n_io); |
177 | kfree(objp: priv); |
178 | return ERR_PTR(error: rc); |
179 | } |
180 | EXPORT_SYMBOL_GPL(nfcmrvl_nci_register_dev); |
181 | |
182 | void nfcmrvl_nci_unregister_dev(struct nfcmrvl_private *priv) |
183 | { |
184 | struct nci_dev *ndev = priv->ndev; |
185 | |
186 | nci_unregister_device(ndev); |
187 | if (priv->ndev->nfc_dev->fw_download_in_progress) |
188 | nfcmrvl_fw_dnld_abort(priv); |
189 | |
190 | nfcmrvl_fw_dnld_deinit(priv); |
191 | |
192 | if (gpio_is_valid(number: priv->config.reset_n_io)) |
193 | gpio_free(gpio: priv->config.reset_n_io); |
194 | |
195 | nci_free_device(ndev); |
196 | kfree(objp: priv); |
197 | } |
198 | EXPORT_SYMBOL_GPL(nfcmrvl_nci_unregister_dev); |
199 | |
200 | int nfcmrvl_nci_recv_frame(struct nfcmrvl_private *priv, struct sk_buff *skb) |
201 | { |
202 | if (priv->config.hci_muxed) { |
203 | if (skb->data[0] == NFCMRVL_HCI_EVENT_CODE && |
204 | skb->data[1] == NFCMRVL_HCI_NFC_EVENT_CODE) { |
205 | /* Data packet, let's extract NCI payload */ |
206 | skb_pull(skb, NFCMRVL_HCI_EVENT_HEADER_SIZE); |
207 | } else { |
208 | /* Skip this packet */ |
209 | kfree_skb(skb); |
210 | return 0; |
211 | } |
212 | } |
213 | |
214 | if (priv->ndev->nfc_dev->fw_download_in_progress) { |
215 | nfcmrvl_fw_dnld_recv_frame(priv, skb); |
216 | return 0; |
217 | } |
218 | |
219 | if (test_bit(NFCMRVL_NCI_RUNNING, &priv->flags)) |
220 | nci_recv_frame(ndev: priv->ndev, skb); |
221 | else { |
222 | /* Drop this packet since nobody wants it */ |
223 | kfree_skb(skb); |
224 | return 0; |
225 | } |
226 | |
227 | return 0; |
228 | } |
229 | EXPORT_SYMBOL_GPL(nfcmrvl_nci_recv_frame); |
230 | |
231 | void nfcmrvl_chip_reset(struct nfcmrvl_private *priv) |
232 | { |
233 | /* Reset possible fault of previous session */ |
234 | clear_bit(NFCMRVL_PHY_ERROR, addr: &priv->flags); |
235 | |
236 | if (gpio_is_valid(number: priv->config.reset_n_io)) { |
237 | nfc_info(priv->dev, "reset the chip\n" ); |
238 | gpio_set_value(gpio: priv->config.reset_n_io, value: 0); |
239 | usleep_range(min: 5000, max: 10000); |
240 | gpio_set_value(gpio: priv->config.reset_n_io, value: 1); |
241 | } else |
242 | nfc_info(priv->dev, "no reset available on this interface\n" ); |
243 | } |
244 | |
245 | void nfcmrvl_chip_halt(struct nfcmrvl_private *priv) |
246 | { |
247 | if (gpio_is_valid(number: priv->config.reset_n_io)) |
248 | gpio_set_value(gpio: priv->config.reset_n_io, value: 0); |
249 | } |
250 | |
251 | int nfcmrvl_parse_dt(struct device_node *node, |
252 | struct nfcmrvl_platform_data *pdata) |
253 | { |
254 | int reset_n_io; |
255 | |
256 | reset_n_io = of_get_named_gpio(np: node, list_name: "reset-n-io" , index: 0); |
257 | if (reset_n_io < 0) { |
258 | pr_info("no reset-n-io config\n" ); |
259 | } else if (!gpio_is_valid(number: reset_n_io)) { |
260 | pr_err("invalid reset-n-io GPIO\n" ); |
261 | return reset_n_io; |
262 | } |
263 | pdata->reset_n_io = reset_n_io; |
264 | pdata->hci_muxed = of_property_read_bool(np: node, propname: "hci-muxed" ); |
265 | |
266 | return 0; |
267 | } |
268 | EXPORT_SYMBOL_GPL(nfcmrvl_parse_dt); |
269 | |
270 | MODULE_AUTHOR("Marvell International Ltd." ); |
271 | MODULE_DESCRIPTION("Marvell NFC driver" ); |
272 | MODULE_LICENSE("GPL v2" ); |
273 | |