aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/nfc
diff options
context:
space:
mode:
authorSamuel Ortiz <sameo@linux.intel.com>2013-01-14 14:35:22 -0500
committerSamuel Ortiz <sameo@linux.intel.com>2013-02-08 06:18:32 -0500
commite0af11fa0df30bc645e6abb4d3ddc7ed05af0451 (patch)
treed1cec90a13719fa744603de68cbe1493c4483fa5 /drivers/nfc
parent71054c7db161b5947de8c2bcb02d5934cbddb722 (diff)
NFC: microread: Add MEI physical layer
On some peculiar worlds, microreads are found hidden behind MEIs and needs to be accessed through the ME bus. Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
Diffstat (limited to 'drivers/nfc')
-rw-r--r--drivers/nfc/microread/Kconfig11
-rw-r--r--drivers/nfc/microread/Makefile2
-rw-r--r--drivers/nfc/microread/mei.c241
3 files changed, 254 insertions, 0 deletions
diff --git a/drivers/nfc/microread/Kconfig b/drivers/nfc/microread/Kconfig
index cd6744bc8db6..572305be6e37 100644
--- a/drivers/nfc/microread/Kconfig
+++ b/drivers/nfc/microread/Kconfig
@@ -22,3 +22,14 @@ config NFC_MICROREAD_I2C
22 22
23 If you choose to build a module, it'll be called microread_i2c. 23 If you choose to build a module, it'll be called microread_i2c.
24 Say N if unsure. 24 Say N if unsure.
25
26config NFC_MICROREAD_MEI
27 tristate "NFC Microread MEI support"
28 depends on NFC_MICROREAD && INTEL_MEI_BUS_NFC
29 ---help---
30 This module adds support for the mei interface of adapters using
31 Inside microread chipsets. Select this if your microread chipset
32 is handled by Intel's Management Engine Interface on your platform.
33
34 If you choose to build a module, it'll be called microread_mei.
35 Say N if unsure.
diff --git a/drivers/nfc/microread/Makefile b/drivers/nfc/microread/Makefile
index 74c185165ebf..755c24cba253 100644
--- a/drivers/nfc/microread/Makefile
+++ b/drivers/nfc/microread/Makefile
@@ -3,6 +3,8 @@
3# 3#
4 4
5microread_i2c-objs = i2c.o 5microread_i2c-objs = i2c.o
6microread_mei-objs = mei.o
6 7
7obj-$(CONFIG_NFC_MICROREAD) += microread.o 8obj-$(CONFIG_NFC_MICROREAD) += microread.o
8obj-$(CONFIG_NFC_MICROREAD_I2C) += microread_i2c.o 9obj-$(CONFIG_NFC_MICROREAD_I2C) += microread_i2c.o
10obj-$(CONFIG_NFC_MICROREAD_MEI) += microread_mei.o
diff --git a/drivers/nfc/microread/mei.c b/drivers/nfc/microread/mei.c
new file mode 100644
index 000000000000..c078e56d7d14
--- /dev/null
+++ b/drivers/nfc/microread/mei.c
@@ -0,0 +1,241 @@
1/*
2 * HCI based Driver for Inside Secure microread NFC Chip
3 *
4 * Copyright (C) 2013 Intel Corporation. All rights reserved.
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms and conditions of the GNU General Public License,
8 * version 2, as published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the
17 * Free Software Foundation, Inc.,
18 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 */
20
21#include <linux/module.h>
22#include <linux/slab.h>
23#include <linux/interrupt.h>
24#include <linux/gpio.h>
25#include <linux/mei_bus.h>
26
27#include <linux/nfc.h>
28#include <net/nfc/hci.h>
29#include <net/nfc/llc.h>
30
31#include "microread.h"
32
33#define MICROREAD_DRIVER_NAME "microread"
34
35#define MICROREAD_UUID UUID_LE(0x0bb17a78, 0x2a8e, 0x4c50, 0x94, \
36 0xd4, 0x50, 0x26, 0x67, 0x23, 0x77, 0x5c)
37
38struct mei_nfc_hdr {
39 u8 cmd;
40 u8 status;
41 u16 req_id;
42 u32 reserved;
43 u16 data_size;
44} __attribute__((packed));
45
46#define MEI_NFC_HEADER_SIZE 10
47#define MEI_NFC_MAX_HCI_PAYLOAD 300
48#define MEI_NFC_MAX_READ (MEI_NFC_HEADER_SIZE + MEI_NFC_MAX_HCI_PAYLOAD)
49
50struct microread_mei_phy {
51 struct mei_bus_client *client;
52 struct nfc_hci_dev *hdev;
53
54 int powered;
55
56 int hard_fault; /*
57 * < 0 if hardware error occured (e.g. i2c err)
58 * and prevents normal operation.
59 */
60};
61
62#define MEI_DUMP_SKB_IN(info, skb) \
63do { \
64 pr_debug("%s:\n", info); \
65 print_hex_dump(KERN_DEBUG, "mei in : ", DUMP_PREFIX_OFFSET, \
66 16, 1, (skb)->data, (skb)->len, 0); \
67} while (0)
68
69#define MEI_DUMP_SKB_OUT(info, skb) \
70do { \
71 pr_debug("%s:\n", info); \
72 print_hex_dump(KERN_DEBUG, "mei out: ", DUMP_PREFIX_OFFSET, \
73 16, 1, (skb)->data, (skb)->len, 0); \
74} while (0)
75
76static int microread_mei_enable(void *phy_id)
77{
78 struct microread_mei_phy *phy = phy_id;
79
80 pr_info(DRIVER_DESC ": %s\n", __func__);
81
82 phy->powered = 1;
83
84 return 0;
85}
86
87static void microread_mei_disable(void *phy_id)
88{
89 struct microread_mei_phy *phy = phy_id;
90
91 pr_info(DRIVER_DESC ": %s\n", __func__);
92
93 phy->powered = 0;
94}
95
96/*
97 * Writing a frame must not return the number of written bytes.
98 * It must return either zero for success, or <0 for error.
99 * In addition, it must not alter the skb
100 */
101static int microread_mei_write(void *phy_id, struct sk_buff *skb)
102{
103 struct microread_mei_phy *phy = phy_id;
104 int r;
105
106 MEI_DUMP_SKB_OUT("mei frame sent", skb);
107
108 r = mei_bus_send(phy->client, skb->data, skb->len);
109 if (r > 0)
110 r = 0;
111
112 return r;
113}
114
115static void microread_event_cb(struct mei_bus_client *client, u32 events,
116 void *context)
117{
118 struct microread_mei_phy *phy = context;
119
120 if (phy->hard_fault != 0)
121 return;
122
123 if (events & BIT(MEI_BUS_EVENT_RX)) {
124 struct sk_buff *skb;
125 int reply_size;
126
127 skb = alloc_skb(MEI_NFC_MAX_READ, GFP_KERNEL);
128 if (!skb)
129 return;
130
131 reply_size = mei_bus_recv(client, skb->data, MEI_NFC_MAX_READ);
132 if (reply_size < MEI_NFC_HEADER_SIZE) {
133 kfree(skb);
134 return;
135 }
136
137 skb_put(skb, reply_size);
138 skb_pull(skb, MEI_NFC_HEADER_SIZE);
139
140 MEI_DUMP_SKB_IN("mei frame read", skb);
141
142 nfc_hci_recv_frame(phy->hdev, skb);
143 }
144}
145
146static struct nfc_phy_ops mei_phy_ops = {
147 .write = microread_mei_write,
148 .enable = microread_mei_enable,
149 .disable = microread_mei_disable,
150};
151
152static int microread_mei_probe(struct mei_bus_client *client)
153{
154 struct microread_mei_phy *phy;
155 int r;
156
157 pr_info("Probing NFC microread\n");
158
159 phy = kzalloc(sizeof(struct microread_mei_phy), GFP_KERNEL);
160 if (!phy) {
161 pr_err("Cannot allocate memory for microread mei phy.\n");
162 return -ENOMEM;
163 }
164
165 phy->client = client;
166 mei_bus_set_clientdata(client, phy);
167
168 r = mei_bus_register_event_cb(client, microread_event_cb, phy);
169 if (r) {
170 pr_err(MICROREAD_DRIVER_NAME ": event cb registration failed\n");
171 goto err_out;
172 }
173
174 r = microread_probe(phy, &mei_phy_ops, LLC_NOP_NAME,
175 MEI_NFC_HEADER_SIZE, 0, MEI_NFC_MAX_HCI_PAYLOAD,
176 &phy->hdev);
177 if (r < 0)
178 goto err_out;
179
180 return 0;
181
182err_out:
183 kfree(phy);
184
185 return r;
186}
187
188static int microread_mei_remove(struct mei_bus_client *client)
189{
190 struct microread_mei_phy *phy = mei_bus_get_clientdata(client);
191
192 pr_info("Removing microread\n");
193
194 microread_remove(phy->hdev);
195
196 if (phy->powered)
197 microread_mei_disable(phy);
198
199 kfree(phy);
200
201 return 0;
202}
203
204static struct mei_bus_driver microread_driver = {
205 .driver = {
206 .name = MICROREAD_DRIVER_NAME,
207 },
208 .id = {
209 .name = MICROREAD_DRIVER_NAME,
210 .uuid = MICROREAD_UUID,
211 },
212
213 .probe = microread_mei_probe,
214 .remove = microread_mei_remove,
215};
216
217static int microread_mei_init(void)
218{
219 int r;
220
221 pr_debug(DRIVER_DESC ": %s\n", __func__);
222
223 r = mei_driver_register(&microread_driver);
224 if (r) {
225 pr_err(MICROREAD_DRIVER_NAME ": driver registration failed\n");
226 return r;
227 }
228
229 return 0;
230}
231
232static void microread_mei_exit(void)
233{
234 mei_driver_unregister(&microread_driver);
235}
236
237module_init(microread_mei_init);
238module_exit(microread_mei_exit);
239
240MODULE_LICENSE("GPL");
241MODULE_DESCRIPTION(DRIVER_DESC);