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 | #define NXP_NCI_RF_PLL_UNLOCKED_NTF nci_opcode_pack(NCI_GID_RF_MGMT, 0x21) |
31 | #define NXP_NCI_RF_TXLDO_ERROR_NTF nci_opcode_pack(NCI_GID_RF_MGMT, 0x23) |
32 | |
33 | static int nxp_nci_open(struct nci_dev *ndev) |
34 | { |
35 | struct nxp_nci_info *info = nci_get_drvdata(ndev); |
36 | int r = 0; |
37 | |
38 | mutex_lock(&info->info_lock); |
39 | |
40 | if (info->mode != NXP_NCI_MODE_COLD) { |
41 | r = -EBUSY; |
42 | goto open_exit; |
43 | } |
44 | |
45 | if (info->phy_ops->set_mode) |
46 | r = info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_NCI); |
47 | |
48 | info->mode = NXP_NCI_MODE_NCI; |
49 | |
50 | open_exit: |
51 | mutex_unlock(lock: &info->info_lock); |
52 | return r; |
53 | } |
54 | |
55 | static int nxp_nci_close(struct nci_dev *ndev) |
56 | { |
57 | struct nxp_nci_info *info = nci_get_drvdata(ndev); |
58 | int r = 0; |
59 | |
60 | mutex_lock(&info->info_lock); |
61 | |
62 | if (info->phy_ops->set_mode) |
63 | r = info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_COLD); |
64 | |
65 | info->mode = NXP_NCI_MODE_COLD; |
66 | |
67 | mutex_unlock(lock: &info->info_lock); |
68 | return r; |
69 | } |
70 | |
71 | static int nxp_nci_send(struct nci_dev *ndev, struct sk_buff *skb) |
72 | { |
73 | struct nxp_nci_info *info = nci_get_drvdata(ndev); |
74 | int r; |
75 | |
76 | if (!info->phy_ops->write) { |
77 | kfree_skb(skb); |
78 | return -EOPNOTSUPP; |
79 | } |
80 | |
81 | if (info->mode != NXP_NCI_MODE_NCI) { |
82 | kfree_skb(skb); |
83 | return -EINVAL; |
84 | } |
85 | |
86 | r = info->phy_ops->write(info->phy_id, skb); |
87 | if (r < 0) { |
88 | kfree_skb(skb); |
89 | return r; |
90 | } |
91 | |
92 | consume_skb(skb); |
93 | return 0; |
94 | } |
95 | |
96 | static int nxp_nci_rf_pll_unlocked_ntf(struct nci_dev *ndev, |
97 | struct sk_buff *skb) |
98 | { |
99 | nfc_err(&ndev->nfc_dev->dev, |
100 | "PLL didn't lock. Missing or unstable clock?\n" ); |
101 | |
102 | return 0; |
103 | } |
104 | |
105 | static int nxp_nci_rf_txldo_error_ntf(struct nci_dev *ndev, |
106 | struct sk_buff *skb) |
107 | { |
108 | nfc_err(&ndev->nfc_dev->dev, |
109 | "RF transmitter couldn't start. Bad power and/or configuration?\n" ); |
110 | |
111 | return 0; |
112 | } |
113 | |
114 | static const struct nci_driver_ops nxp_nci_core_ops[] = { |
115 | { |
116 | .opcode = NXP_NCI_RF_PLL_UNLOCKED_NTF, |
117 | .ntf = nxp_nci_rf_pll_unlocked_ntf, |
118 | }, |
119 | { |
120 | .opcode = NXP_NCI_RF_TXLDO_ERROR_NTF, |
121 | .ntf = nxp_nci_rf_txldo_error_ntf, |
122 | }, |
123 | }; |
124 | |
125 | static const struct nci_ops nxp_nci_ops = { |
126 | .open = nxp_nci_open, |
127 | .close = nxp_nci_close, |
128 | .send = nxp_nci_send, |
129 | .fw_download = nxp_nci_fw_download, |
130 | .core_ops = nxp_nci_core_ops, |
131 | .n_core_ops = ARRAY_SIZE(nxp_nci_core_ops), |
132 | }; |
133 | |
134 | int nxp_nci_probe(void *phy_id, struct device *pdev, |
135 | const struct nxp_nci_phy_ops *phy_ops, |
136 | unsigned int max_payload, |
137 | struct nci_dev **ndev) |
138 | { |
139 | struct nxp_nci_info *info; |
140 | int r; |
141 | |
142 | info = devm_kzalloc(dev: pdev, size: sizeof(struct nxp_nci_info), GFP_KERNEL); |
143 | if (!info) |
144 | return -ENOMEM; |
145 | |
146 | info->phy_id = phy_id; |
147 | info->pdev = pdev; |
148 | info->phy_ops = phy_ops; |
149 | info->max_payload = max_payload; |
150 | INIT_WORK(&info->fw_info.work, nxp_nci_fw_work); |
151 | init_completion(x: &info->fw_info.cmd_completion); |
152 | mutex_init(&info->info_lock); |
153 | |
154 | if (info->phy_ops->set_mode) { |
155 | r = info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_COLD); |
156 | if (r < 0) |
157 | return r; |
158 | } |
159 | |
160 | info->mode = NXP_NCI_MODE_COLD; |
161 | |
162 | info->ndev = nci_allocate_device(ops: &nxp_nci_ops, NXP_NCI_NFC_PROTOCOLS, |
163 | NXP_NCI_HDR_LEN, tx_tailroom: 0); |
164 | if (!info->ndev) |
165 | return -ENOMEM; |
166 | |
167 | nci_set_parent_dev(ndev: info->ndev, dev: pdev); |
168 | nci_set_drvdata(ndev: info->ndev, data: info); |
169 | r = nci_register_device(ndev: info->ndev); |
170 | if (r < 0) { |
171 | nci_free_device(ndev: info->ndev); |
172 | return r; |
173 | } |
174 | |
175 | *ndev = info->ndev; |
176 | return r; |
177 | } |
178 | EXPORT_SYMBOL(nxp_nci_probe); |
179 | |
180 | void nxp_nci_remove(struct nci_dev *ndev) |
181 | { |
182 | struct nxp_nci_info *info = nci_get_drvdata(ndev); |
183 | |
184 | if (info->mode == NXP_NCI_MODE_FW) |
185 | nxp_nci_fw_work_complete(info, result: -ESHUTDOWN); |
186 | cancel_work_sync(work: &info->fw_info.work); |
187 | |
188 | mutex_lock(&info->info_lock); |
189 | |
190 | if (info->phy_ops->set_mode) |
191 | info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_COLD); |
192 | |
193 | nci_unregister_device(ndev); |
194 | nci_free_device(ndev); |
195 | |
196 | mutex_unlock(lock: &info->info_lock); |
197 | } |
198 | EXPORT_SYMBOL(nxp_nci_remove); |
199 | |
200 | MODULE_LICENSE("GPL" ); |
201 | MODULE_DESCRIPTION("NXP NCI NFC driver" ); |
202 | MODULE_AUTHOR("Clément Perrochaud <clement.perrochaud@nxp.com>" ); |
203 | |