atlantic: Fix driver resume flow.
[linux-2.6-microblaze.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                 return -EOPNOTSUPP;
75
76         if (info->mode != NXP_NCI_MODE_NCI)
77                 return -EINVAL;
78
79         r = info->phy_ops->write(info->phy_id, skb);
80         if (r < 0)
81                 kfree_skb(skb);
82
83         return r;
84 }
85
86 static struct nci_ops nxp_nci_ops = {
87         .open = nxp_nci_open,
88         .close = nxp_nci_close,
89         .send = nxp_nci_send,
90         .fw_download = nxp_nci_fw_download,
91 };
92
93 int nxp_nci_probe(void *phy_id, struct device *pdev,
94                   const struct nxp_nci_phy_ops *phy_ops,
95                   unsigned int max_payload,
96                   struct nci_dev **ndev)
97 {
98         struct nxp_nci_info *info;
99         int r;
100
101         info = devm_kzalloc(pdev, sizeof(struct nxp_nci_info), GFP_KERNEL);
102         if (!info)
103                 return -ENOMEM;
104
105         info->phy_id = phy_id;
106         info->pdev = pdev;
107         info->phy_ops = phy_ops;
108         info->max_payload = max_payload;
109         INIT_WORK(&info->fw_info.work, nxp_nci_fw_work);
110         init_completion(&info->fw_info.cmd_completion);
111         mutex_init(&info->info_lock);
112
113         if (info->phy_ops->set_mode) {
114                 r = info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_COLD);
115                 if (r < 0)
116                         return r;
117         }
118
119         info->mode = NXP_NCI_MODE_COLD;
120
121         info->ndev = nci_allocate_device(&nxp_nci_ops, NXP_NCI_NFC_PROTOCOLS,
122                                          NXP_NCI_HDR_LEN, 0);
123         if (!info->ndev)
124                 return -ENOMEM;
125
126         nci_set_parent_dev(info->ndev, pdev);
127         nci_set_drvdata(info->ndev, info);
128         r = nci_register_device(info->ndev);
129         if (r < 0) {
130                 nci_free_device(info->ndev);
131                 return r;
132         }
133
134         *ndev = info->ndev;
135         return r;
136 }
137 EXPORT_SYMBOL(nxp_nci_probe);
138
139 void nxp_nci_remove(struct nci_dev *ndev)
140 {
141         struct nxp_nci_info *info = nci_get_drvdata(ndev);
142
143         if (info->mode == NXP_NCI_MODE_FW)
144                 nxp_nci_fw_work_complete(info, -ESHUTDOWN);
145         cancel_work_sync(&info->fw_info.work);
146
147         mutex_lock(&info->info_lock);
148
149         if (info->phy_ops->set_mode)
150                 info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_COLD);
151
152         nci_unregister_device(ndev);
153         nci_free_device(ndev);
154
155         mutex_unlock(&info->info_lock);
156 }
157 EXPORT_SYMBOL(nxp_nci_remove);
158
159 MODULE_LICENSE("GPL");
160 MODULE_DESCRIPTION("NXP NCI NFC driver");
161 MODULE_AUTHOR("Clément Perrochaud <clement.perrochaud@nxp.com>");