Merge tag 'reset-for-v5.3' of git://git.pengutronix.de/git/pza/linux into arm/drivers
[sfrench/cifs-2.6.git] / drivers / nfc / nxp-nci / core.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Generic driver for NXP NCI NFC chips
4  *
5  * Copyright (C) 2014  NXP Semiconductors  All rights reserved.
6  *
7  * Authors: Clément Perrochaud <clement.perrochaud@nxp.com>
8  *
9  * Derived from PN544 device driver:
10  * Copyright (C) 2012  Intel Corporation. All rights reserved.
11  */
12
13 #include <linux/delay.h>
14 #include <linux/gpio.h>
15 #include <linux/module.h>
16 #include <linux/nfc.h>
17 #include <linux/platform_data/nxp-nci.h>
18
19 #include <net/nfc/nci_core.h>
20
21 #include "nxp-nci.h"
22
23 #define NXP_NCI_HDR_LEN 4
24
25 #define NXP_NCI_NFC_PROTOCOLS (NFC_PROTO_JEWEL_MASK | \
26                                NFC_PROTO_MIFARE_MASK | \
27                                NFC_PROTO_FELICA_MASK | \
28                                NFC_PROTO_ISO14443_MASK | \
29                                NFC_PROTO_ISO14443_B_MASK | \
30                                NFC_PROTO_NFC_DEP_MASK)
31
32 static int nxp_nci_open(struct nci_dev *ndev)
33 {
34         struct nxp_nci_info *info = nci_get_drvdata(ndev);
35         int r = 0;
36
37         mutex_lock(&info->info_lock);
38
39         if (info->mode != NXP_NCI_MODE_COLD) {
40                 r = -EBUSY;
41                 goto open_exit;
42         }
43
44         if (info->phy_ops->set_mode)
45                 r = info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_NCI);
46
47         info->mode = NXP_NCI_MODE_NCI;
48
49 open_exit:
50         mutex_unlock(&info->info_lock);
51         return r;
52 }
53
54 static int nxp_nci_close(struct nci_dev *ndev)
55 {
56         struct nxp_nci_info *info = nci_get_drvdata(ndev);
57         int r = 0;
58
59         mutex_lock(&info->info_lock);
60
61         if (info->phy_ops->set_mode)
62                 r = info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_COLD);
63
64         info->mode = NXP_NCI_MODE_COLD;
65
66         mutex_unlock(&info->info_lock);
67         return r;
68 }
69
70 static int nxp_nci_send(struct nci_dev *ndev, struct sk_buff *skb)
71 {
72         struct nxp_nci_info *info = nci_get_drvdata(ndev);
73         int r;
74
75         if (!info->phy_ops->write) {
76                 r = -ENOTSUPP;
77                 goto send_exit;
78         }
79
80         if (info->mode != NXP_NCI_MODE_NCI) {
81                 r = -EINVAL;
82                 goto send_exit;
83         }
84
85         r = info->phy_ops->write(info->phy_id, skb);
86         if (r < 0)
87                 kfree_skb(skb);
88
89 send_exit:
90         return r;
91 }
92
93 static struct nci_ops nxp_nci_ops = {
94         .open = nxp_nci_open,
95         .close = nxp_nci_close,
96         .send = nxp_nci_send,
97         .fw_download = nxp_nci_fw_download,
98 };
99
100 int nxp_nci_probe(void *phy_id, struct device *pdev,
101                   const struct nxp_nci_phy_ops *phy_ops,
102                   unsigned int max_payload,
103                   struct nci_dev **ndev)
104 {
105         struct nxp_nci_info *info;
106         int r;
107
108         info = devm_kzalloc(pdev, sizeof(struct nxp_nci_info), GFP_KERNEL);
109         if (!info) {
110                 r = -ENOMEM;
111                 goto probe_exit;
112         }
113
114         info->phy_id = phy_id;
115         info->pdev = pdev;
116         info->phy_ops = phy_ops;
117         info->max_payload = max_payload;
118         INIT_WORK(&info->fw_info.work, nxp_nci_fw_work);
119         init_completion(&info->fw_info.cmd_completion);
120         mutex_init(&info->info_lock);
121
122         if (info->phy_ops->set_mode) {
123                 r = info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_COLD);
124                 if (r < 0)
125                         goto probe_exit;
126         }
127
128         info->mode = NXP_NCI_MODE_COLD;
129
130         info->ndev = nci_allocate_device(&nxp_nci_ops, NXP_NCI_NFC_PROTOCOLS,
131                                          NXP_NCI_HDR_LEN, 0);
132         if (!info->ndev) {
133                 r = -ENOMEM;
134                 goto probe_exit;
135         }
136
137         nci_set_parent_dev(info->ndev, pdev);
138         nci_set_drvdata(info->ndev, info);
139         r = nci_register_device(info->ndev);
140         if (r < 0)
141                 goto probe_exit_free_nci;
142
143         *ndev = info->ndev;
144
145         goto probe_exit;
146
147 probe_exit_free_nci:
148         nci_free_device(info->ndev);
149 probe_exit:
150         return r;
151 }
152 EXPORT_SYMBOL(nxp_nci_probe);
153
154 void nxp_nci_remove(struct nci_dev *ndev)
155 {
156         struct nxp_nci_info *info = nci_get_drvdata(ndev);
157
158         if (info->mode == NXP_NCI_MODE_FW)
159                 nxp_nci_fw_work_complete(info, -ESHUTDOWN);
160         cancel_work_sync(&info->fw_info.work);
161
162         mutex_lock(&info->info_lock);
163
164         if (info->phy_ops->set_mode)
165                 info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_COLD);
166
167         nci_unregister_device(ndev);
168         nci_free_device(ndev);
169
170         mutex_unlock(&info->info_lock);
171 }
172 EXPORT_SYMBOL(nxp_nci_remove);
173
174 MODULE_LICENSE("GPL");
175 MODULE_DESCRIPTION("NXP NCI NFC driver");
176 MODULE_AUTHOR("Clément Perrochaud <clement.perrochaud@nxp.com>");