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