1be3d162aSKrzysztof Kozlowski // SPDX-License-Identifier: GPL-2.0-only
2f26e30ccSAmitkumar Karwar /*
3f26e30ccSAmitkumar Karwar * Marvell NFC driver: major functions
4f26e30ccSAmitkumar Karwar *
5fb101c0eSVincent Cuissard * Copyright (C) 2014-2015 Marvell International Ltd.
6f26e30ccSAmitkumar Karwar */
7f26e30ccSAmitkumar Karwar
8f26e30ccSAmitkumar Karwar #include <linux/module.h>
94a2b947fSVincent Cuissard #include <linux/gpio.h>
104a2b947fSVincent Cuissard #include <linux/delay.h>
11dc14bdefSVincent Cuissard #include <linux/of_gpio.h>
12f26e30ccSAmitkumar Karwar #include <linux/nfc.h>
13f26e30ccSAmitkumar Karwar #include <net/nfc/nci.h>
14f26e30ccSAmitkumar Karwar #include <net/nfc/nci_core.h>
15f26e30ccSAmitkumar Karwar #include "nfcmrvl.h"
16f26e30ccSAmitkumar Karwar
nfcmrvl_nci_open(struct nci_dev * ndev)17f26e30ccSAmitkumar Karwar static int nfcmrvl_nci_open(struct nci_dev *ndev)
18f26e30ccSAmitkumar Karwar {
19f26e30ccSAmitkumar Karwar struct nfcmrvl_private *priv = nci_get_drvdata(ndev);
20f26e30ccSAmitkumar Karwar int err;
21f26e30ccSAmitkumar Karwar
22f26e30ccSAmitkumar Karwar if (test_and_set_bit(NFCMRVL_NCI_RUNNING, &priv->flags))
23f26e30ccSAmitkumar Karwar return 0;
24f26e30ccSAmitkumar Karwar
25b5b3e23eSVincent Cuissard /* Reset possible fault of previous session */
26b5b3e23eSVincent Cuissard clear_bit(NFCMRVL_PHY_ERROR, &priv->flags);
27b5b3e23eSVincent Cuissard
28f26e30ccSAmitkumar Karwar err = priv->if_ops->nci_open(priv);
29f26e30ccSAmitkumar Karwar
30f26e30ccSAmitkumar Karwar if (err)
31f26e30ccSAmitkumar Karwar clear_bit(NFCMRVL_NCI_RUNNING, &priv->flags);
32f26e30ccSAmitkumar Karwar
33f26e30ccSAmitkumar Karwar return err;
34f26e30ccSAmitkumar Karwar }
35f26e30ccSAmitkumar Karwar
nfcmrvl_nci_close(struct nci_dev * ndev)36f26e30ccSAmitkumar Karwar static int nfcmrvl_nci_close(struct nci_dev *ndev)
37f26e30ccSAmitkumar Karwar {
38f26e30ccSAmitkumar Karwar struct nfcmrvl_private *priv = nci_get_drvdata(ndev);
39f26e30ccSAmitkumar Karwar
40f26e30ccSAmitkumar Karwar if (!test_and_clear_bit(NFCMRVL_NCI_RUNNING, &priv->flags))
41f26e30ccSAmitkumar Karwar return 0;
42f26e30ccSAmitkumar Karwar
43f26e30ccSAmitkumar Karwar priv->if_ops->nci_close(priv);
44f26e30ccSAmitkumar Karwar
45f26e30ccSAmitkumar Karwar return 0;
46f26e30ccSAmitkumar Karwar }
47f26e30ccSAmitkumar Karwar
nfcmrvl_nci_send(struct nci_dev * ndev,struct sk_buff * skb)48f26e30ccSAmitkumar Karwar static int nfcmrvl_nci_send(struct nci_dev *ndev, struct sk_buff *skb)
49f26e30ccSAmitkumar Karwar {
50f26e30ccSAmitkumar Karwar struct nfcmrvl_private *priv = nci_get_drvdata(ndev);
51f26e30ccSAmitkumar Karwar
52f26e30ccSAmitkumar Karwar nfc_info(priv->dev, "send entry, len %d\n", skb->len);
53f26e30ccSAmitkumar Karwar
54f26e30ccSAmitkumar Karwar skb->dev = (void *)ndev;
55f26e30ccSAmitkumar Karwar
56dc14bdefSVincent Cuissard if (priv->config.hci_muxed) {
57f1f1a7daSVincent Cuissard unsigned char *hdr;
58f1f1a7daSVincent Cuissard unsigned char len = skb->len;
59f1f1a7daSVincent Cuissard
60d58ff351SJohannes Berg hdr = skb_push(skb, NFCMRVL_HCI_EVENT_HEADER_SIZE);
61f1f1a7daSVincent Cuissard hdr[0] = NFCMRVL_HCI_COMMAND_CODE;
62f1f1a7daSVincent Cuissard hdr[1] = NFCMRVL_HCI_OGF;
63f1f1a7daSVincent Cuissard hdr[2] = NFCMRVL_HCI_OCF;
64f1f1a7daSVincent Cuissard hdr[3] = len;
65f1f1a7daSVincent Cuissard }
66f1f1a7daSVincent Cuissard
67f26e30ccSAmitkumar Karwar return priv->if_ops->nci_send(priv, skb);
68f26e30ccSAmitkumar Karwar }
69f26e30ccSAmitkumar Karwar
nfcmrvl_nci_setup(struct nci_dev * ndev)7015203b4cSAmitkumar Karwar static int nfcmrvl_nci_setup(struct nci_dev *ndev)
7115203b4cSAmitkumar Karwar {
72d0dcad8bSVincent Cuissard __u8 val = 1;
73d0dcad8bSVincent Cuissard
74d0dcad8bSVincent Cuissard nci_set_config(ndev, NFCMRVL_PB_BAIL_OUT, 1, &val);
7515203b4cSAmitkumar Karwar return 0;
7615203b4cSAmitkumar Karwar }
7715203b4cSAmitkumar Karwar
nfcmrvl_nci_fw_download(struct nci_dev * ndev,const char * firmware_name)783194c687SVincent Cuissard static int nfcmrvl_nci_fw_download(struct nci_dev *ndev,
793194c687SVincent Cuissard const char *firmware_name)
803194c687SVincent Cuissard {
813194c687SVincent Cuissard return nfcmrvl_fw_dnld_start(ndev, firmware_name);
823194c687SVincent Cuissard }
833194c687SVincent Cuissard
84b9c28286SKrzysztof Kozlowski static const struct nci_ops nfcmrvl_nci_ops = {
85f26e30ccSAmitkumar Karwar .open = nfcmrvl_nci_open,
86f26e30ccSAmitkumar Karwar .close = nfcmrvl_nci_close,
87f26e30ccSAmitkumar Karwar .send = nfcmrvl_nci_send,
8815203b4cSAmitkumar Karwar .setup = nfcmrvl_nci_setup,
893194c687SVincent Cuissard .fw_download = nfcmrvl_nci_fw_download,
90f26e30ccSAmitkumar Karwar };
91f26e30ccSAmitkumar Karwar
nfcmrvl_nci_register_dev(enum nfcmrvl_phy phy,void * drv_data,const struct nfcmrvl_if_ops * ops,struct device * dev,const struct nfcmrvl_platform_data * pdata)9258d34aa6SVincent Cuissard struct nfcmrvl_private *nfcmrvl_nci_register_dev(enum nfcmrvl_phy phy,
9358d34aa6SVincent Cuissard void *drv_data,
9426955037SKrzysztof Kozlowski const struct nfcmrvl_if_ops *ops,
95f1f1a7daSVincent Cuissard struct device *dev,
96fe53159fSKrzysztof Kozlowski const struct nfcmrvl_platform_data *pdata)
97f26e30ccSAmitkumar Karwar {
98f26e30ccSAmitkumar Karwar struct nfcmrvl_private *priv;
99f26e30ccSAmitkumar Karwar int rc;
10058d34aa6SVincent Cuissard int headroom;
10158d34aa6SVincent Cuissard int tailroom;
102f26e30ccSAmitkumar Karwar u32 protocols;
103f26e30ccSAmitkumar Karwar
104f26e30ccSAmitkumar Karwar priv = kzalloc(sizeof(*priv), GFP_KERNEL);
105f26e30ccSAmitkumar Karwar if (!priv)
106f26e30ccSAmitkumar Karwar return ERR_PTR(-ENOMEM);
107f26e30ccSAmitkumar Karwar
108f26e30ccSAmitkumar Karwar priv->drv_data = drv_data;
109f26e30ccSAmitkumar Karwar priv->if_ops = ops;
110f26e30ccSAmitkumar Karwar priv->dev = dev;
11158d34aa6SVincent Cuissard priv->phy = phy;
1124a2b947fSVincent Cuissard
113dc14bdefSVincent Cuissard memcpy(&priv->config, pdata, sizeof(*pdata));
114dc14bdefSVincent Cuissard
115e33a3f84SJohan Hovold if (gpio_is_valid(priv->config.reset_n_io)) {
1160cbe4011SJohan Hovold rc = gpio_request_one(priv->config.reset_n_io,
1174a2b947fSVincent Cuissard GPIOF_OUT_INIT_LOW,
1184a2b947fSVincent Cuissard "nfcmrvl_reset_n");
1190cbe4011SJohan Hovold if (rc < 0) {
120e33a3f84SJohan Hovold priv->config.reset_n_io = -EINVAL;
1214a2b947fSVincent Cuissard nfc_err(dev, "failed to request reset_n io\n");
1224a2b947fSVincent Cuissard }
1230cbe4011SJohan Hovold }
124f1f1a7daSVincent Cuissard
125caf6e49bSVincent Cuissard if (phy == NFCMRVL_PHY_SPI) {
126caf6e49bSVincent Cuissard headroom = NCI_SPI_HDR_LEN;
127caf6e49bSVincent Cuissard tailroom = 1;
128caf6e49bSVincent Cuissard } else
12958d34aa6SVincent Cuissard headroom = tailroom = 0;
13058d34aa6SVincent Cuissard
131dc14bdefSVincent Cuissard if (priv->config.hci_muxed)
13258d34aa6SVincent Cuissard headroom += NFCMRVL_HCI_EVENT_HEADER_SIZE;
133f26e30ccSAmitkumar Karwar
134f26e30ccSAmitkumar Karwar protocols = NFC_PROTO_JEWEL_MASK
135dc14bdefSVincent Cuissard | NFC_PROTO_MIFARE_MASK
136dc14bdefSVincent Cuissard | NFC_PROTO_FELICA_MASK
137f26e30ccSAmitkumar Karwar | NFC_PROTO_ISO14443_MASK
138f26e30ccSAmitkumar Karwar | NFC_PROTO_ISO14443_B_MASK
13983d56725SVincent Cuissard | NFC_PROTO_ISO15693_MASK
140f26e30ccSAmitkumar Karwar | NFC_PROTO_NFC_DEP_MASK;
141f26e30ccSAmitkumar Karwar
142f1f1a7daSVincent Cuissard priv->ndev = nci_allocate_device(&nfcmrvl_nci_ops, protocols,
14358d34aa6SVincent Cuissard headroom, tailroom);
144f26e30ccSAmitkumar Karwar if (!priv->ndev) {
1453590ebc0SJoe Perches nfc_err(dev, "nci_allocate_device failed\n");
146bb55dc2aSAmitkumar Karwar rc = -ENOMEM;
1470cbe4011SJohan Hovold goto error_free_gpio;
148f26e30ccSAmitkumar Karwar }
149f26e30ccSAmitkumar Karwar
1503194c687SVincent Cuissard rc = nfcmrvl_fw_dnld_init(priv);
1513194c687SVincent Cuissard if (rc) {
1523194c687SVincent Cuissard nfc_err(dev, "failed to initialize FW download %d\n", rc);
1533194c687SVincent Cuissard goto error_free_dev;
154f26e30ccSAmitkumar Karwar }
155f26e30ccSAmitkumar Karwar
15645dd39b9SJohan Hovold nci_set_drvdata(priv->ndev, priv);
15745dd39b9SJohan Hovold
15845dd39b9SJohan Hovold rc = nci_register_device(priv->ndev);
15945dd39b9SJohan Hovold if (rc) {
16045dd39b9SJohan Hovold nfc_err(dev, "nci_register_device failed %d\n", rc);
16145dd39b9SJohan Hovold goto error_fw_dnld_deinit;
16245dd39b9SJohan Hovold }
16345dd39b9SJohan Hovold
16445dd39b9SJohan Hovold /* Ensure that controller is powered off */
16545dd39b9SJohan Hovold nfcmrvl_chip_halt(priv);
16645dd39b9SJohan Hovold
167f26e30ccSAmitkumar Karwar nfc_info(dev, "registered with nci successfully\n");
168f26e30ccSAmitkumar Karwar return priv;
169bb55dc2aSAmitkumar Karwar
17045dd39b9SJohan Hovold error_fw_dnld_deinit:
17145dd39b9SJohan Hovold nfcmrvl_fw_dnld_deinit(priv);
1723194c687SVincent Cuissard error_free_dev:
1733194c687SVincent Cuissard nci_free_device(priv->ndev);
1740cbe4011SJohan Hovold error_free_gpio:
175e33a3f84SJohan Hovold if (gpio_is_valid(priv->config.reset_n_io))
1760cbe4011SJohan Hovold gpio_free(priv->config.reset_n_io);
177bb55dc2aSAmitkumar Karwar kfree(priv);
178bb55dc2aSAmitkumar Karwar return ERR_PTR(rc);
179f26e30ccSAmitkumar Karwar }
180f26e30ccSAmitkumar Karwar EXPORT_SYMBOL_GPL(nfcmrvl_nci_register_dev);
181f26e30ccSAmitkumar Karwar
nfcmrvl_nci_unregister_dev(struct nfcmrvl_private * priv)182f26e30ccSAmitkumar Karwar void nfcmrvl_nci_unregister_dev(struct nfcmrvl_private *priv)
183f26e30ccSAmitkumar Karwar {
184f26e30ccSAmitkumar Karwar struct nci_dev *ndev = priv->ndev;
185f26e30ccSAmitkumar Karwar
186d270453aSDuoming Zhou nci_unregister_device(ndev);
1873194c687SVincent Cuissard if (priv->ndev->nfc_dev->fw_download_in_progress)
1883194c687SVincent Cuissard nfcmrvl_fw_dnld_abort(priv);
1893194c687SVincent Cuissard
1903194c687SVincent Cuissard nfcmrvl_fw_dnld_deinit(priv);
1913194c687SVincent Cuissard
192e33a3f84SJohan Hovold if (gpio_is_valid(priv->config.reset_n_io))
1930cbe4011SJohan Hovold gpio_free(priv->config.reset_n_io);
194b2fe288eSVincent Cuissard
195f26e30ccSAmitkumar Karwar nci_free_device(ndev);
196f26e30ccSAmitkumar Karwar kfree(priv);
197f26e30ccSAmitkumar Karwar }
198f26e30ccSAmitkumar Karwar EXPORT_SYMBOL_GPL(nfcmrvl_nci_unregister_dev);
199f26e30ccSAmitkumar Karwar
nfcmrvl_nci_recv_frame(struct nfcmrvl_private * priv,struct sk_buff * skb)200e1bf80c2SVincent Cuissard int nfcmrvl_nci_recv_frame(struct nfcmrvl_private *priv, struct sk_buff *skb)
201f26e30ccSAmitkumar Karwar {
202dc14bdefSVincent Cuissard if (priv->config.hci_muxed) {
203f1f1a7daSVincent Cuissard if (skb->data[0] == NFCMRVL_HCI_EVENT_CODE &&
204f1f1a7daSVincent Cuissard skb->data[1] == NFCMRVL_HCI_NFC_EVENT_CODE) {
205f1f1a7daSVincent Cuissard /* Data packet, let's extract NCI payload */
206f1f1a7daSVincent Cuissard skb_pull(skb, NFCMRVL_HCI_EVENT_HEADER_SIZE);
207f1f1a7daSVincent Cuissard } else {
208f1f1a7daSVincent Cuissard /* Skip this packet */
209f1f1a7daSVincent Cuissard kfree_skb(skb);
210f1f1a7daSVincent Cuissard return 0;
211f1f1a7daSVincent Cuissard }
212f1f1a7daSVincent Cuissard }
213f1f1a7daSVincent Cuissard
2143194c687SVincent Cuissard if (priv->ndev->nfc_dev->fw_download_in_progress) {
2153194c687SVincent Cuissard nfcmrvl_fw_dnld_recv_frame(priv, skb);
2163194c687SVincent Cuissard return 0;
2173194c687SVincent Cuissard }
2183194c687SVincent Cuissard
219e1bf80c2SVincent Cuissard if (test_bit(NFCMRVL_NCI_RUNNING, &priv->flags))
220f26e30ccSAmitkumar Karwar nci_recv_frame(priv->ndev, skb);
221e1bf80c2SVincent Cuissard else {
222e1bf80c2SVincent Cuissard /* Drop this packet since nobody wants it */
223e1bf80c2SVincent Cuissard kfree_skb(skb);
224e1bf80c2SVincent Cuissard return 0;
225e1bf80c2SVincent Cuissard }
226f26e30ccSAmitkumar Karwar
227e1bf80c2SVincent Cuissard return 0;
228f26e30ccSAmitkumar Karwar }
229f26e30ccSAmitkumar Karwar EXPORT_SYMBOL_GPL(nfcmrvl_nci_recv_frame);
230f26e30ccSAmitkumar Karwar
nfcmrvl_chip_reset(struct nfcmrvl_private * priv)2314a2b947fSVincent Cuissard void nfcmrvl_chip_reset(struct nfcmrvl_private *priv)
2324a2b947fSVincent Cuissard {
233b5b3e23eSVincent Cuissard /* Reset possible fault of previous session */
234b5b3e23eSVincent Cuissard clear_bit(NFCMRVL_PHY_ERROR, &priv->flags);
2354a2b947fSVincent Cuissard
236c3953a3cSJohan Hovold if (gpio_is_valid(priv->config.reset_n_io)) {
2374a2b947fSVincent Cuissard nfc_info(priv->dev, "reset the chip\n");
238dc14bdefSVincent Cuissard gpio_set_value(priv->config.reset_n_io, 0);
2394a2b947fSVincent Cuissard usleep_range(5000, 10000);
240dc14bdefSVincent Cuissard gpio_set_value(priv->config.reset_n_io, 1);
2414a2b947fSVincent Cuissard } else
2424a2b947fSVincent Cuissard nfc_info(priv->dev, "no reset available on this interface\n");
2434a2b947fSVincent Cuissard }
2444a2b947fSVincent Cuissard
nfcmrvl_chip_halt(struct nfcmrvl_private * priv)2453194c687SVincent Cuissard void nfcmrvl_chip_halt(struct nfcmrvl_private *priv)
2463194c687SVincent Cuissard {
247c3953a3cSJohan Hovold if (gpio_is_valid(priv->config.reset_n_io))
2483194c687SVincent Cuissard gpio_set_value(priv->config.reset_n_io, 0);
2493194c687SVincent Cuissard }
2503194c687SVincent Cuissard
nfcmrvl_parse_dt(struct device_node * node,struct nfcmrvl_platform_data * pdata)251dc14bdefSVincent Cuissard int nfcmrvl_parse_dt(struct device_node *node,
252dc14bdefSVincent Cuissard struct nfcmrvl_platform_data *pdata)
253dc14bdefSVincent Cuissard {
254dc14bdefSVincent Cuissard int reset_n_io;
255dc14bdefSVincent Cuissard
256dc14bdefSVincent Cuissard reset_n_io = of_get_named_gpio(node, "reset-n-io", 0);
257dc14bdefSVincent Cuissard if (reset_n_io < 0) {
258dc14bdefSVincent Cuissard pr_info("no reset-n-io config\n");
259dc14bdefSVincent Cuissard } else if (!gpio_is_valid(reset_n_io)) {
260dc14bdefSVincent Cuissard pr_err("invalid reset-n-io GPIO\n");
261dc14bdefSVincent Cuissard return reset_n_io;
262dc14bdefSVincent Cuissard }
263dc14bdefSVincent Cuissard pdata->reset_n_io = reset_n_io;
264*cc6d85c1SRob Herring pdata->hci_muxed = of_property_read_bool(node, "hci-muxed");
265dc14bdefSVincent Cuissard
266dc14bdefSVincent Cuissard return 0;
267dc14bdefSVincent Cuissard }
268dc14bdefSVincent Cuissard EXPORT_SYMBOL_GPL(nfcmrvl_parse_dt);
269dc14bdefSVincent Cuissard
270f26e30ccSAmitkumar Karwar MODULE_AUTHOR("Marvell International Ltd.");
271fb101c0eSVincent Cuissard MODULE_DESCRIPTION("Marvell NFC driver");
272f26e30ccSAmitkumar Karwar MODULE_LICENSE("GPL v2");
273