aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/nfc
diff options
context:
space:
mode:
authorSamuel Ortiz <sameo@linux.intel.com>2012-12-18 08:56:15 -0500
committerSamuel Ortiz <sameo@linux.intel.com>2013-02-03 14:37:48 -0500
commit71054c7db161b5947de8c2bcb02d5934cbddb722 (patch)
tree58a42c0c3ae901daa1525b8aa0750ee5c40cd68b /drivers/nfc
parentcfad1ba87150e198be9ea32367a24e500e59de2c (diff)
NFC: microread: Add i2c physical layer
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/Makefile3
-rw-r--r--drivers/nfc/microread/i2c.c340
3 files changed, 354 insertions, 0 deletions
diff --git a/drivers/nfc/microread/Kconfig b/drivers/nfc/microread/Kconfig
index 5b89d011d098..cd6744bc8db6 100644
--- a/drivers/nfc/microread/Kconfig
+++ b/drivers/nfc/microread/Kconfig
@@ -11,3 +11,14 @@ config NFC_MICROREAD
11 To compile this driver as a module, choose m here. The module will 11 To compile this driver as a module, choose m here. The module will
12 be called microread. 12 be called microread.
13 Say N if unsure. 13 Say N if unsure.
14
15config NFC_MICROREAD_I2C
16 tristate "NFC Microread i2c support"
17 depends on NFC_MICROREAD && I2C && NFC_SHDLC
18 ---help---
19 This module adds support for the i2c interface of adapters using
20 Inside microread chipsets. Select this if your platform is using
21 the i2c bus.
22
23 If you choose to build a module, it'll be called microread_i2c.
24 Say N if unsure.
diff --git a/drivers/nfc/microread/Makefile b/drivers/nfc/microread/Makefile
index 9ce2c53f49a7..74c185165ebf 100644
--- a/drivers/nfc/microread/Makefile
+++ b/drivers/nfc/microread/Makefile
@@ -2,4 +2,7 @@
2# Makefile for Microread HCI based NFC driver 2# Makefile for Microread HCI based NFC driver
3# 3#
4 4
5microread_i2c-objs = i2c.o
6
5obj-$(CONFIG_NFC_MICROREAD) += microread.o 7obj-$(CONFIG_NFC_MICROREAD) += microread.o
8obj-$(CONFIG_NFC_MICROREAD_I2C) += microread_i2c.o
diff --git a/drivers/nfc/microread/i2c.c b/drivers/nfc/microread/i2c.c
new file mode 100644
index 000000000000..101089495bf8
--- /dev/null
+++ b/drivers/nfc/microread/i2c.c
@@ -0,0 +1,340 @@
1/*
2 * HCI based Driver for Inside Secure microread NFC Chip - i2c layer
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/i2c.h>
23#include <linux/delay.h>
24#include <linux/slab.h>
25#include <linux/interrupt.h>
26#include <linux/gpio.h>
27
28#include <linux/nfc.h>
29#include <net/nfc/hci.h>
30#include <net/nfc/llc.h>
31
32#include "microread.h"
33
34#define MICROREAD_I2C_DRIVER_NAME "microread"
35
36#define MICROREAD_I2C_FRAME_HEADROOM 1
37#define MICROREAD_I2C_FRAME_TAILROOM 1
38
39/* framing in HCI mode */
40#define MICROREAD_I2C_LLC_LEN 1
41#define MICROREAD_I2C_LLC_CRC 1
42#define MICROREAD_I2C_LLC_LEN_CRC (MICROREAD_I2C_LLC_LEN + \
43 MICROREAD_I2C_LLC_CRC)
44#define MICROREAD_I2C_LLC_MIN_SIZE (1 + MICROREAD_I2C_LLC_LEN_CRC)
45#define MICROREAD_I2C_LLC_MAX_PAYLOAD 29
46#define MICROREAD_I2C_LLC_MAX_SIZE (MICROREAD_I2C_LLC_LEN_CRC + 1 + \
47 MICROREAD_I2C_LLC_MAX_PAYLOAD)
48
49struct microread_i2c_phy {
50 struct i2c_client *i2c_dev;
51 struct nfc_hci_dev *hdev;
52
53 int irq;
54
55 int hard_fault; /*
56 * < 0 if hardware error occured (e.g. i2c err)
57 * and prevents normal operation.
58 */
59};
60
61#define I2C_DUMP_SKB(info, skb) \
62do { \
63 pr_debug("%s:\n", info); \
64 print_hex_dump(KERN_DEBUG, "i2c: ", DUMP_PREFIX_OFFSET, \
65 16, 1, (skb)->data, (skb)->len, 0); \
66} while (0)
67
68static void microread_i2c_add_len_crc(struct sk_buff *skb)
69{
70 int i;
71 u8 crc = 0;
72 int len;
73
74 len = skb->len;
75 *skb_push(skb, 1) = len;
76
77 for (i = 0; i < skb->len; i++)
78 crc = crc ^ skb->data[i];
79
80 *skb_put(skb, 1) = crc;
81}
82
83static void microread_i2c_remove_len_crc(struct sk_buff *skb)
84{
85 skb_pull(skb, MICROREAD_I2C_FRAME_HEADROOM);
86 skb_trim(skb, MICROREAD_I2C_FRAME_TAILROOM);
87}
88
89static int check_crc(struct sk_buff *skb)
90{
91 int i;
92 u8 crc = 0;
93
94 for (i = 0; i < skb->len - 1; i++)
95 crc = crc ^ skb->data[i];
96
97 if (crc != skb->data[skb->len-1]) {
98 pr_err(MICROREAD_I2C_DRIVER_NAME
99 ": CRC error 0x%x != 0x%x\n",
100 crc, skb->data[skb->len-1]);
101
102 pr_info(DRIVER_DESC ": %s : BAD CRC\n", __func__);
103
104 return -EPERM;
105 }
106
107 return 0;
108}
109
110static int microread_i2c_enable(void *phy_id)
111{
112 return 0;
113}
114
115static void microread_i2c_disable(void *phy_id)
116{
117 return;
118}
119
120static int microread_i2c_write(void *phy_id, struct sk_buff *skb)
121{
122 int r;
123 struct microread_i2c_phy *phy = phy_id;
124 struct i2c_client *client = phy->i2c_dev;
125
126 if (phy->hard_fault != 0)
127 return phy->hard_fault;
128
129 usleep_range(3000, 6000);
130
131 microread_i2c_add_len_crc(skb);
132
133 I2C_DUMP_SKB("i2c frame written", skb);
134
135 r = i2c_master_send(client, skb->data, skb->len);
136
137 if (r == -EREMOTEIO) { /* Retry, chip was in standby */
138 usleep_range(6000, 10000);
139 r = i2c_master_send(client, skb->data, skb->len);
140 }
141
142 if (r >= 0) {
143 if (r != skb->len)
144 r = -EREMOTEIO;
145 else
146 r = 0;
147 }
148
149 microread_i2c_remove_len_crc(skb);
150
151 return r;
152}
153
154
155static int microread_i2c_read(struct microread_i2c_phy *phy,
156 struct sk_buff **skb)
157{
158 int r;
159 u8 len;
160 u8 tmp[MICROREAD_I2C_LLC_MAX_SIZE - 1];
161 struct i2c_client *client = phy->i2c_dev;
162
163 pr_debug("%s\n", __func__);
164
165 r = i2c_master_recv(client, &len, 1);
166 if (r != 1) {
167 dev_err(&client->dev, "cannot read len byte\n");
168 return -EREMOTEIO;
169 }
170
171 if ((len < MICROREAD_I2C_LLC_MIN_SIZE) ||
172 (len > MICROREAD_I2C_LLC_MAX_SIZE)) {
173 dev_err(&client->dev, "invalid len byte\n");
174 pr_err("invalid len byte\n");
175 r = -EBADMSG;
176 goto flush;
177 }
178
179 *skb = alloc_skb(1 + len, GFP_KERNEL);
180 if (*skb == NULL) {
181 r = -ENOMEM;
182 goto flush;
183 }
184
185 *skb_put(*skb, 1) = len;
186
187 r = i2c_master_recv(client, skb_put(*skb, len), len);
188 if (r != len) {
189 kfree_skb(*skb);
190 return -EREMOTEIO;
191 }
192
193 I2C_DUMP_SKB("cc frame read", *skb);
194
195 r = check_crc(*skb);
196 if (r != 0) {
197 kfree_skb(*skb);
198 r = -EBADMSG;
199 goto flush;
200 }
201
202 skb_pull(*skb, 1);
203 skb_trim(*skb, (*skb)->len - MICROREAD_I2C_FRAME_TAILROOM);
204
205 usleep_range(3000, 6000);
206
207 return 0;
208
209flush:
210 if (i2c_master_recv(client, tmp, sizeof(tmp)) < 0)
211 r = -EREMOTEIO;
212
213 usleep_range(3000, 6000);
214
215 return r;
216}
217
218static irqreturn_t microread_i2c_irq_thread_fn(int irq, void *phy_id)
219{
220 struct microread_i2c_phy *phy = phy_id;
221 struct i2c_client *client;
222 struct sk_buff *skb = NULL;
223 int r;
224
225 if (!phy || irq != phy->i2c_dev->irq) {
226 WARN_ON_ONCE(1);
227 return IRQ_NONE;
228 }
229
230 client = phy->i2c_dev;
231 dev_dbg(&client->dev, "IRQ\n");
232
233 if (phy->hard_fault != 0)
234 return IRQ_HANDLED;
235
236 r = microread_i2c_read(phy, &skb);
237 if (r == -EREMOTEIO) {
238 phy->hard_fault = r;
239
240 nfc_hci_recv_frame(phy->hdev, NULL);
241
242 return IRQ_HANDLED;
243 } else if ((r == -ENOMEM) || (r == -EBADMSG)) {
244 return IRQ_HANDLED;
245 }
246
247 nfc_hci_recv_frame(phy->hdev, skb);
248
249 return IRQ_HANDLED;
250}
251
252static struct nfc_phy_ops i2c_phy_ops = {
253 .write = microread_i2c_write,
254 .enable = microread_i2c_enable,
255 .disable = microread_i2c_disable,
256};
257
258static int microread_i2c_probe(struct i2c_client *client,
259 const struct i2c_device_id *id)
260{
261 struct microread_i2c_phy *phy;
262 struct microread_nfc_platform_data *pdata =
263 dev_get_platdata(&client->dev);
264 int r;
265
266 dev_dbg(&client->dev, "client %p", client);
267
268 if (!pdata) {
269 dev_err(&client->dev, "client %p: missing platform data",
270 client);
271 return -EINVAL;
272 }
273
274 phy = devm_kzalloc(&client->dev, sizeof(struct microread_i2c_phy),
275 GFP_KERNEL);
276 if (!phy) {
277 dev_err(&client->dev, "Can't allocate microread phy");
278 return -ENOMEM;
279 }
280
281 i2c_set_clientdata(client, phy);
282 phy->i2c_dev = client;
283
284 r = request_threaded_irq(client->irq, NULL, microread_i2c_irq_thread_fn,
285 IRQF_TRIGGER_RISING | IRQF_ONESHOT,
286 MICROREAD_I2C_DRIVER_NAME, phy);
287 if (r) {
288 dev_err(&client->dev, "Unable to register IRQ handler");
289 return r;
290 }
291
292 r = microread_probe(phy, &i2c_phy_ops, LLC_SHDLC_NAME,
293 MICROREAD_I2C_FRAME_HEADROOM,
294 MICROREAD_I2C_FRAME_TAILROOM,
295 MICROREAD_I2C_LLC_MAX_PAYLOAD, &phy->hdev);
296 if (r < 0)
297 goto err_irq;
298
299 dev_info(&client->dev, "Probed");
300
301 return 0;
302
303err_irq:
304 free_irq(client->irq, phy);
305
306 return r;
307}
308
309static int microread_i2c_remove(struct i2c_client *client)
310{
311 struct microread_i2c_phy *phy = i2c_get_clientdata(client);
312
313 dev_dbg(&client->dev, "%s\n", __func__);
314
315 microread_remove(phy->hdev);
316
317 free_irq(client->irq, phy);
318
319 return 0;
320}
321
322static struct i2c_device_id microread_i2c_id[] = {
323 { MICROREAD_I2C_DRIVER_NAME, 0},
324 { }
325};
326MODULE_DEVICE_TABLE(i2c, microread_i2c_id);
327
328static struct i2c_driver microread_i2c_driver = {
329 .driver = {
330 .name = MICROREAD_I2C_DRIVER_NAME,
331 },
332 .probe = microread_i2c_probe,
333 .remove = microread_i2c_remove,
334 .id_table = microread_i2c_id,
335};
336
337module_i2c_driver(microread_i2c_driver);
338
339MODULE_LICENSE("GPL");
340MODULE_DESCRIPTION(DRIVER_DESC);