1/*
2 * Generic driver for NXP NCI NFC chips
3 *
4 * Copyright (C) 2014  NXP Semiconductors  All rights reserved.
5 *
6 * Authors: Clément Perrochaud <clement.perrochaud@nxp.com>
7 *
8 * Derived from PN544 device driver:
9 * Copyright (C) 2012  Intel Corporation. All rights reserved.
10 *
11 * This program is free software; you can redistribute it and/or modify it
12 * under the terms and conditions of the GNU General Public License,
13 * version 2, as published by the Free Software Foundation.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, see <http://www.gnu.org/licenses/>.
22 */
23
24#include <linux/delay.h>
25#include <linux/gpio.h>
26#include <linux/module.h>
27#include <linux/nfc.h>
28#include <linux/platform_data/nxp-nci.h>
29
30#include <net/nfc/nci_core.h>
31
32#include "nxp-nci.h"
33
34#define NXP_NCI_HDR_LEN	4
35
36#define NXP_NCI_NFC_PROTOCOLS (NFC_PROTO_JEWEL_MASK | \
37			       NFC_PROTO_MIFARE_MASK | \
38			       NFC_PROTO_FELICA_MASK | \
39			       NFC_PROTO_ISO14443_MASK | \
40			       NFC_PROTO_ISO14443_B_MASK | \
41			       NFC_PROTO_NFC_DEP_MASK)
42
43static int nxp_nci_open(struct nci_dev *ndev)
44{
45	struct nxp_nci_info *info = nci_get_drvdata(ndev);
46	int r = 0;
47
48	mutex_lock(&info->info_lock);
49
50	if (info->mode != NXP_NCI_MODE_COLD) {
51		r = -EBUSY;
52		goto open_exit;
53	}
54
55	if (info->phy_ops->set_mode)
56		r = info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_NCI);
57
58	info->mode = NXP_NCI_MODE_NCI;
59
60open_exit:
61	mutex_unlock(&info->info_lock);
62	return r;
63}
64
65static int nxp_nci_close(struct nci_dev *ndev)
66{
67	struct nxp_nci_info *info = nci_get_drvdata(ndev);
68	int r = 0;
69
70	mutex_lock(&info->info_lock);
71
72	if (info->phy_ops->set_mode)
73		r = info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_COLD);
74
75	info->mode = NXP_NCI_MODE_COLD;
76
77	mutex_unlock(&info->info_lock);
78	return r;
79}
80
81static int nxp_nci_send(struct nci_dev *ndev, struct sk_buff *skb)
82{
83	struct nxp_nci_info *info = nci_get_drvdata(ndev);
84	int r;
85
86	if (!info->phy_ops->write) {
87		r = -ENOTSUPP;
88		goto send_exit;
89	}
90
91	if (info->mode != NXP_NCI_MODE_NCI) {
92		r = -EINVAL;
93		goto send_exit;
94	}
95
96	r = info->phy_ops->write(info->phy_id, skb);
97	if (r < 0)
98		kfree_skb(skb);
99
100send_exit:
101	return r;
102}
103
104static struct nci_ops nxp_nci_ops = {
105	.open = nxp_nci_open,
106	.close = nxp_nci_close,
107	.send = nxp_nci_send,
108	.fw_download = nxp_nci_fw_download,
109};
110
111int nxp_nci_probe(void *phy_id, struct device *pdev,
112		  struct nxp_nci_phy_ops *phy_ops, unsigned int max_payload,
113		  struct nci_dev **ndev)
114{
115	struct nxp_nci_info *info;
116	int r;
117
118	info = devm_kzalloc(pdev, sizeof(struct nxp_nci_info), GFP_KERNEL);
119	if (!info) {
120		r = -ENOMEM;
121		goto probe_exit;
122	}
123
124	info->phy_id = phy_id;
125	info->pdev = pdev;
126	info->phy_ops = phy_ops;
127	info->max_payload = max_payload;
128	INIT_WORK(&info->fw_info.work, nxp_nci_fw_work);
129	init_completion(&info->fw_info.cmd_completion);
130	mutex_init(&info->info_lock);
131
132	if (info->phy_ops->set_mode) {
133		r = info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_COLD);
134		if (r < 0)
135			goto probe_exit;
136	}
137
138	info->mode = NXP_NCI_MODE_COLD;
139
140	info->ndev = nci_allocate_device(&nxp_nci_ops, NXP_NCI_NFC_PROTOCOLS,
141					 NXP_NCI_HDR_LEN, 0);
142	if (!info->ndev) {
143		r = -ENOMEM;
144		goto probe_exit;
145	}
146
147	nci_set_parent_dev(info->ndev, pdev);
148	nci_set_drvdata(info->ndev, info);
149	r = nci_register_device(info->ndev);
150	if (r < 0)
151		goto probe_exit_free_nci;
152
153	*ndev = info->ndev;
154
155	goto probe_exit;
156
157probe_exit_free_nci:
158	nci_free_device(info->ndev);
159probe_exit:
160	return r;
161}
162EXPORT_SYMBOL(nxp_nci_probe);
163
164void nxp_nci_remove(struct nci_dev *ndev)
165{
166	struct nxp_nci_info *info = nci_get_drvdata(ndev);
167
168	if (info->mode == NXP_NCI_MODE_FW)
169		nxp_nci_fw_work_complete(info, -ESHUTDOWN);
170	cancel_work_sync(&info->fw_info.work);
171
172	mutex_lock(&info->info_lock);
173
174	if (info->phy_ops->set_mode)
175		info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_COLD);
176
177	nci_unregister_device(ndev);
178	nci_free_device(ndev);
179
180	mutex_unlock(&info->info_lock);
181}
182EXPORT_SYMBOL(nxp_nci_remove);
183
184MODULE_LICENSE("GPL");
185MODULE_DESCRIPTION("NXP NCI NFC driver");
186MODULE_AUTHOR("Clément Perrochaud <clement.perrochaud@nxp.com>");
187