aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVincent Cuissard <cuissard@marvell.com>2015-10-26 05:27:41 -0400
committerSamuel Ortiz <sameo@linux.intel.com>2015-10-26 23:21:14 -0400
commitb5b3e23e4cace008e1a30e8614a484d14dfd07a1 (patch)
tree451bd798e76313fb43dc654530374d4b3c6e3d5c
parent58d34aa677ec248539262c54a618d61804a893f1 (diff)
NFC: nfcmrvl: add i2c driver
This driver adds the support of I2C-based Marvell NFC controller. Signed-off-by: Vincent Cuissard <cuissard@marvell.com> Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
-rw-r--r--Documentation/devicetree/bindings/net/nfc/nfcmrvl.txt34
-rw-r--r--drivers/nfc/nfcmrvl/Kconfig12
-rw-r--r--drivers/nfc/nfcmrvl/Makefile3
-rw-r--r--drivers/nfc/nfcmrvl/i2c.c290
-rw-r--r--drivers/nfc/nfcmrvl/main.c9
-rw-r--r--drivers/nfc/nfcmrvl/nfcmrvl.h1
-rw-r--r--include/linux/platform_data/nfcmrvl.h8
-rw-r--r--include/net/nfc/nci.h1
8 files changed, 353 insertions, 5 deletions
diff --git a/Documentation/devicetree/bindings/net/nfc/nfcmrvl.txt b/Documentation/devicetree/bindings/net/nfc/nfcmrvl.txt
index 7c4a0cc370cf..0fa20cc2c33c 100644
--- a/Documentation/devicetree/bindings/net/nfc/nfcmrvl.txt
+++ b/Documentation/devicetree/bindings/net/nfc/nfcmrvl.txt
@@ -1,7 +1,9 @@
1* Marvell International Ltd. NCI NFC Controller 1* Marvell International Ltd. NCI NFC Controller
2 2
3Required properties: 3Required properties:
4- compatible: Should be "mrvl,nfc-uart". 4- compatible: Should be:
5 - "mrvl,nfc-uart" for UART devices
6 - "mrvl,nfc-i2c" for I2C devices
5 7
6Optional SoC specific properties: 8Optional SoC specific properties:
7- pinctrl-names: Contains only one value - "default". 9- pinctrl-names: Contains only one value - "default".
@@ -13,6 +15,12 @@ Optional UART-based chip specific properties:
13- flow-control: Specifies that the chip is using RTS/CTS. 15- flow-control: Specifies that the chip is using RTS/CTS.
14- break-control: Specifies that the chip needs specific break management. 16- break-control: Specifies that the chip needs specific break management.
15 17
18Optional I2C-based chip specific properties:
19- i2c-int-falling: Specifies that the chip read event shall be trigged on
20 falling edge.
21- i2c-int-rising: Specifies that the chip read event shall be trigged on
22 rising edge.
23
16Example (for ARM-based BeagleBoard Black with 88W8887 on UART5): 24Example (for ARM-based BeagleBoard Black with 88W8887 on UART5):
17 25
18&uart5 { 26&uart5 {
@@ -27,3 +35,27 @@ Example (for ARM-based BeagleBoard Black with 88W8887 on UART5):
27 flow-control; 35 flow-control;
28 } 36 }
29}; 37};
38
39
40Example (for ARM-based BeagleBoard Black with 88W8887 on I2C1):
41
42&i2c1 {
43 status = "okay";
44 clock-frequency = <400000>;
45
46 nfcmrvli2c0: i2c@1 {
47 compatible = "mrvl,nfc-i2c";
48
49 reg = <0x8>;
50
51 /* I2C INT configuration */
52 interrupt-parent = <&gpio3>;
53 interrupts = <21 0>;
54
55 /* I2C INT trigger configuration */
56 i2c-int-rising;
57
58 /* Reset IO */
59 reset-n-io = <&gpio3 19 0>;
60 };
61};
diff --git a/drivers/nfc/nfcmrvl/Kconfig b/drivers/nfc/nfcmrvl/Kconfig
index 19ac492bc25f..e18a979bb6d3 100644
--- a/drivers/nfc/nfcmrvl/Kconfig
+++ b/drivers/nfc/nfcmrvl/Kconfig
@@ -30,3 +30,15 @@ config NFC_MRVL_UART
30 30
31 Say Y here to compile support for Marvell NFC-over-UART driver 31 Say Y here to compile support for Marvell NFC-over-UART driver
32 into the kernel or say M to compile it as module. 32 into the kernel or say M to compile it as module.
33
34config NFC_MRVL_I2C
35 tristate "Marvell NFC-over-I2C driver"
36 depends on NFC_MRVL && I2C
37 help
38 Marvell NFC-over-I2C driver.
39
40 This driver provides support for Marvell NFC-over-I2C devices.
41
42 Say Y here to compile support for Marvell NFC-over-I2C driver
43 into the kernel or say M to compile it as module.
44
diff --git a/drivers/nfc/nfcmrvl/Makefile b/drivers/nfc/nfcmrvl/Makefile
index 4554ee8e3680..895866a3ebc6 100644
--- a/drivers/nfc/nfcmrvl/Makefile
+++ b/drivers/nfc/nfcmrvl/Makefile
@@ -10,3 +10,6 @@ obj-$(CONFIG_NFC_MRVL_USB) += nfcmrvl_usb.o
10 10
11nfcmrvl_uart-y += uart.o 11nfcmrvl_uart-y += uart.o
12obj-$(CONFIG_NFC_MRVL_UART) += nfcmrvl_uart.o 12obj-$(CONFIG_NFC_MRVL_UART) += nfcmrvl_uart.o
13
14nfcmrvl_i2c-y += i2c.o
15obj-$(CONFIG_NFC_MRVL_I2C) += nfcmrvl_i2c.o
diff --git a/drivers/nfc/nfcmrvl/i2c.c b/drivers/nfc/nfcmrvl/i2c.c
new file mode 100644
index 000000000000..7a44025bdaad
--- /dev/null
+++ b/drivers/nfc/nfcmrvl/i2c.c
@@ -0,0 +1,290 @@
1/**
2 * Marvell NFC-over-I2C driver: I2C interface related functions
3 *
4 * Copyright (C) 2015, Marvell International Ltd.
5 *
6 * This software file (the "File") is distributed by Marvell International
7 * Ltd. under the terms of the GNU General Public License Version 2, June 1991
8 * (the "License"). You may use, redistribute and/or modify this File in
9 * accordance with the terms and conditions of the License, a copy of which
10 * is available on the worldwide web at
11 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
12 *
13 * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
14 * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
15 * ARE EXPRESSLY DISCLAIMED. The License provides additional details about
16 * this warranty disclaimer.
17 **/
18
19#include <linux/module.h>
20#include <linux/interrupt.h>
21#include <linux/i2c.h>
22#include <linux/pm_runtime.h>
23#include <linux/nfc.h>
24#include <linux/gpio.h>
25#include <linux/delay.h>
26#include <linux/of_irq.h>
27#include <linux/of_gpio.h>
28#include <net/nfc/nci.h>
29#include <net/nfc/nci_core.h>
30#include "nfcmrvl.h"
31
32struct nfcmrvl_i2c_drv_data {
33 unsigned long flags;
34 struct device *dev;
35 struct i2c_client *i2c;
36 struct nfcmrvl_private *priv;
37};
38
39static int nfcmrvl_i2c_read(struct nfcmrvl_i2c_drv_data *drv_data,
40 struct sk_buff **skb)
41{
42 int ret;
43 struct nci_ctrl_hdr nci_hdr;
44
45 /* Read NCI header to know the payload size */
46 ret = i2c_master_recv(drv_data->i2c, (u8 *)&nci_hdr, NCI_CTRL_HDR_SIZE);
47 if (ret != NCI_CTRL_HDR_SIZE) {
48 nfc_err(&drv_data->i2c->dev, "cannot read NCI header\n");
49 return -EBADMSG;
50 }
51
52 if (nci_hdr.plen > NCI_MAX_PAYLOAD_SIZE) {
53 nfc_err(&drv_data->i2c->dev, "invalid packet payload size\n");
54 return -EBADMSG;
55 }
56
57 *skb = nci_skb_alloc(drv_data->priv->ndev,
58 nci_hdr.plen + NCI_CTRL_HDR_SIZE, GFP_KERNEL);
59 if (!*skb)
60 return -ENOMEM;
61
62 /* Copy NCI header into the SKB */
63 memcpy(skb_put(*skb, NCI_CTRL_HDR_SIZE), &nci_hdr, NCI_CTRL_HDR_SIZE);
64
65 if (nci_hdr.plen) {
66 /* Read the NCI payload */
67 ret = i2c_master_recv(drv_data->i2c,
68 skb_put(*skb, nci_hdr.plen),
69 nci_hdr.plen);
70
71 if (ret != nci_hdr.plen) {
72 nfc_err(&drv_data->i2c->dev,
73 "Invalid frame payload length: %u (expected %u)\n",
74 ret, nci_hdr.plen);
75 kfree_skb(*skb);
76 return -EBADMSG;
77 }
78 }
79
80 return 0;
81}
82
83static irqreturn_t nfcmrvl_i2c_int_irq_thread_fn(int irq, void *drv_data_ptr)
84{
85 struct nfcmrvl_i2c_drv_data *drv_data = drv_data_ptr;
86 struct sk_buff *skb = NULL;
87 int ret;
88
89 if (!drv_data->priv)
90 return IRQ_HANDLED;
91
92 if (test_bit(NFCMRVL_PHY_ERROR, &drv_data->priv->flags))
93 return IRQ_HANDLED;
94
95 ret = nfcmrvl_i2c_read(drv_data, &skb);
96
97 switch (ret) {
98 case -EREMOTEIO:
99 set_bit(NFCMRVL_PHY_ERROR, &drv_data->priv->flags);
100 break;
101 case -ENOMEM:
102 case -EBADMSG:
103 nfc_err(&drv_data->i2c->dev, "read failed %d\n", ret);
104 break;
105 default:
106 if (nfcmrvl_nci_recv_frame(drv_data->priv, skb) < 0)
107 nfc_err(&drv_data->i2c->dev, "corrupted RX packet\n");
108 break;
109 }
110 return IRQ_HANDLED;
111}
112
113static int nfcmrvl_i2c_nci_open(struct nfcmrvl_private *priv)
114{
115 struct nfcmrvl_i2c_drv_data *drv_data = priv->drv_data;
116
117 if (!drv_data)
118 return -ENODEV;
119
120 return 0;
121}
122
123static int nfcmrvl_i2c_nci_close(struct nfcmrvl_private *priv)
124{
125 return 0;
126}
127
128static int nfcmrvl_i2c_nci_send(struct nfcmrvl_private *priv,
129 struct sk_buff *skb)
130{
131 struct nfcmrvl_i2c_drv_data *drv_data = priv->drv_data;
132 int ret;
133
134 if (test_bit(NFCMRVL_PHY_ERROR, &priv->flags))
135 return -EREMOTEIO;
136
137 ret = i2c_master_send(drv_data->i2c, skb->data, skb->len);
138
139 /* Retry if chip was in standby */
140 if (ret == -EREMOTEIO) {
141 nfc_info(drv_data->dev, "chip may sleep, retry\n");
142 usleep_range(6000, 10000);
143 ret = i2c_master_send(drv_data->i2c, skb->data, skb->len);
144 }
145
146 if (ret >= 0) {
147 if (ret != skb->len) {
148 nfc_err(drv_data->dev,
149 "Invalid length sent: %u (expected %u)\n",
150 ret, skb->len);
151 ret = -EREMOTEIO;
152 } else
153 ret = 0;
154 kfree_skb(skb);
155 }
156
157 return ret;
158}
159
160static void nfcmrvl_i2c_nci_update_config(struct nfcmrvl_private *priv,
161 const void *param)
162{
163}
164
165static struct nfcmrvl_if_ops i2c_ops = {
166 .nci_open = nfcmrvl_i2c_nci_open,
167 .nci_close = nfcmrvl_i2c_nci_close,
168 .nci_send = nfcmrvl_i2c_nci_send,
169 .nci_update_config = nfcmrvl_i2c_nci_update_config,
170};
171
172static int nfcmrvl_i2c_parse_dt(struct device_node *node,
173 struct nfcmrvl_platform_data *pdata)
174{
175 int ret;
176
177 ret = nfcmrvl_parse_dt(node, pdata);
178 if (ret < 0) {
179 pr_err("Failed to get generic entries\n");
180 return ret;
181 }
182
183 if (of_find_property(node, "i2c-int-falling", NULL))
184 pdata->irq_polarity = IRQF_TRIGGER_FALLING;
185 else
186 pdata->irq_polarity = IRQF_TRIGGER_RISING;
187
188 ret = irq_of_parse_and_map(node, 0);
189 if (ret < 0) {
190 pr_err("Unable to get irq, error: %d\n", ret);
191 return ret;
192 }
193 pdata->irq = ret;
194
195 return 0;
196}
197
198static int nfcmrvl_i2c_probe(struct i2c_client *client,
199 const struct i2c_device_id *id)
200{
201 struct nfcmrvl_i2c_drv_data *drv_data;
202 struct nfcmrvl_platform_data *pdata;
203 struct nfcmrvl_platform_data config;
204 int ret;
205
206 if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
207 nfc_err(&client->dev, "Need I2C_FUNC_I2C\n");
208 return -ENODEV;
209 }
210
211 drv_data = devm_kzalloc(&client->dev, sizeof(*drv_data), GFP_KERNEL);
212 if (!drv_data)
213 return -ENOMEM;
214
215 drv_data->i2c = client;
216 drv_data->dev = &client->dev;
217 drv_data->priv = NULL;
218
219 i2c_set_clientdata(client, drv_data);
220
221 pdata = client->dev.platform_data;
222
223 if (!pdata && client->dev.of_node)
224 if (nfcmrvl_i2c_parse_dt(client->dev.of_node, &config) == 0)
225 pdata = &config;
226
227 if (!pdata)
228 return -EINVAL;
229
230 /* Request the read IRQ */
231 ret = devm_request_threaded_irq(&drv_data->i2c->dev, pdata->irq,
232 NULL, nfcmrvl_i2c_int_irq_thread_fn,
233 pdata->irq_polarity | IRQF_ONESHOT,
234 "nfcmrvl_i2c_int", drv_data);
235 if (ret < 0) {
236 nfc_err(&drv_data->i2c->dev,
237 "Unable to register IRQ handler\n");
238 return ret;
239 }
240
241 drv_data->priv = nfcmrvl_nci_register_dev(NFCMRVL_PHY_I2C,
242 drv_data, &i2c_ops,
243 &drv_data->i2c->dev, pdata);
244
245 if (IS_ERR(drv_data->priv))
246 return PTR_ERR(drv_data->priv);
247
248 drv_data->priv->support_fw_dnld = true;
249
250 return 0;
251}
252
253static int nfcmrvl_i2c_remove(struct i2c_client *client)
254{
255 struct nfcmrvl_i2c_drv_data *drv_data = i2c_get_clientdata(client);
256
257 nfcmrvl_nci_unregister_dev(drv_data->priv);
258
259 return 0;
260}
261
262
263static const struct of_device_id of_nfcmrvl_i2c_match[] = {
264 { .compatible = "mrvl,nfc-i2c", },
265 {},
266};
267MODULE_DEVICE_TABLE(of, of_nfcmrvl_i2c_match);
268
269static struct i2c_device_id nfcmrvl_i2c_id_table[] = {
270 { "nfcmrvl_i2c", 0 },
271 {}
272};
273MODULE_DEVICE_TABLE(i2c, nfcmrvl_i2c_id_table);
274
275static struct i2c_driver nfcmrvl_i2c_driver = {
276 .probe = nfcmrvl_i2c_probe,
277 .id_table = nfcmrvl_i2c_id_table,
278 .remove = nfcmrvl_i2c_remove,
279 .driver = {
280 .name = "nfcmrvl_i2c",
281 .owner = THIS_MODULE,
282 .of_match_table = of_match_ptr(of_nfcmrvl_i2c_match),
283 },
284};
285
286module_i2c_driver(nfcmrvl_i2c_driver);
287
288MODULE_AUTHOR("Marvell International Ltd.");
289MODULE_DESCRIPTION("Marvell NFC-over-I2C driver");
290MODULE_LICENSE("GPL v2");
diff --git a/drivers/nfc/nfcmrvl/main.c b/drivers/nfc/nfcmrvl/main.c
index a24a7ca9f33d..0c27de60a6bd 100644
--- a/drivers/nfc/nfcmrvl/main.c
+++ b/drivers/nfc/nfcmrvl/main.c
@@ -33,6 +33,9 @@ static int nfcmrvl_nci_open(struct nci_dev *ndev)
33 if (test_and_set_bit(NFCMRVL_NCI_RUNNING, &priv->flags)) 33 if (test_and_set_bit(NFCMRVL_NCI_RUNNING, &priv->flags))
34 return 0; 34 return 0;
35 35
36 /* Reset possible fault of previous session */
37 clear_bit(NFCMRVL_PHY_ERROR, &priv->flags);
38
36 err = priv->if_ops->nci_open(priv); 39 err = priv->if_ops->nci_open(priv);
37 40
38 if (err) 41 if (err)
@@ -226,10 +229,8 @@ EXPORT_SYMBOL_GPL(nfcmrvl_nci_recv_frame);
226 229
227void nfcmrvl_chip_reset(struct nfcmrvl_private *priv) 230void nfcmrvl_chip_reset(struct nfcmrvl_private *priv)
228{ 231{
229 /* 232 /* Reset possible fault of previous session */
230 * This function does not take care if someone is using the device. 233 clear_bit(NFCMRVL_PHY_ERROR, &priv->flags);
231 * To be improved.
232 */
233 234
234 if (priv->config.reset_n_io) { 235 if (priv->config.reset_n_io) {
235 nfc_info(priv->dev, "reset the chip\n"); 236 nfc_info(priv->dev, "reset the chip\n");
diff --git a/drivers/nfc/nfcmrvl/nfcmrvl.h b/drivers/nfc/nfcmrvl/nfcmrvl.h
index f82678be5aa9..de68ff45e49a 100644
--- a/drivers/nfc/nfcmrvl/nfcmrvl.h
+++ b/drivers/nfc/nfcmrvl/nfcmrvl.h
@@ -25,6 +25,7 @@
25 25
26/* Define private flags: */ 26/* Define private flags: */
27#define NFCMRVL_NCI_RUNNING 1 27#define NFCMRVL_NCI_RUNNING 1
28#define NFCMRVL_PHY_ERROR 2
28 29
29#define NFCMRVL_EXT_COEX_ID 0xE0 30#define NFCMRVL_EXT_COEX_ID 0xE0
30#define NFCMRVL_NOT_ALLOWED_ID 0xE1 31#define NFCMRVL_NOT_ALLOWED_ID 0xE1
diff --git a/include/linux/platform_data/nfcmrvl.h b/include/linux/platform_data/nfcmrvl.h
index ac91707dabcb..a6f9d633f5be 100644
--- a/include/linux/platform_data/nfcmrvl.h
+++ b/include/linux/platform_data/nfcmrvl.h
@@ -35,6 +35,14 @@ struct nfcmrvl_platform_data {
35 unsigned int flow_control; 35 unsigned int flow_control;
36 /* Tell if firmware supports break control for power management */ 36 /* Tell if firmware supports break control for power management */
37 unsigned int break_control; 37 unsigned int break_control;
38
39
40 /*
41 * I2C specific
42 */
43
44 unsigned int irq;
45 unsigned int irq_polarity;
38}; 46};
39 47
40#endif /* _NFCMRVL_PTF_H_ */ 48#endif /* _NFCMRVL_PTF_H_ */
diff --git a/include/net/nfc/nci.h b/include/net/nfc/nci.h
index b495825f8f49..707e3ab816c2 100644
--- a/include/net/nfc/nci.h
+++ b/include/net/nfc/nci.h
@@ -35,6 +35,7 @@
35#define NCI_MAX_NUM_RF_CONFIGS 10 35#define NCI_MAX_NUM_RF_CONFIGS 10
36#define NCI_MAX_NUM_CONN 10 36#define NCI_MAX_NUM_CONN 10
37#define NCI_MAX_PARAM_LEN 251 37#define NCI_MAX_PARAM_LEN 251
38#define NCI_MAX_PAYLOAD_SIZE 255
38#define NCI_MAX_PACKET_SIZE 258 39#define NCI_MAX_PACKET_SIZE 258
39 40
40/* NCI Status Codes */ 41/* NCI Status Codes */