diff options
author | Vincent Cuissard <cuissard@marvell.com> | 2015-06-11 08:00:20 -0400 |
---|---|---|
committer | Samuel Ortiz <sameo@linux.intel.com> | 2015-06-11 17:41:57 -0400 |
commit | e097dc624f784debbde49701a493bf920bc422c7 (patch) | |
tree | f6837ca9bd1ba197677fef320ffd61b3565ef195 /drivers/nfc | |
parent | dc14bdef8762a8098b1da881b611d722e24fe787 (diff) |
NFC: nfcmrvl: add UART driver
Add support of Marvell NFC chip controlled over UART
Signed-off-by: Vincent Cuissard <cuissard@marvell.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
Diffstat (limited to 'drivers/nfc')
-rw-r--r-- | drivers/nfc/nfcmrvl/Kconfig | 11 | ||||
-rw-r--r-- | drivers/nfc/nfcmrvl/Makefile | 3 | ||||
-rw-r--r-- | drivers/nfc/nfcmrvl/nfcmrvl.h | 7 | ||||
-rw-r--r-- | drivers/nfc/nfcmrvl/uart.c | 225 |
4 files changed, 246 insertions, 0 deletions
diff --git a/drivers/nfc/nfcmrvl/Kconfig b/drivers/nfc/nfcmrvl/Kconfig index 5e18afd9abe2..796be2411440 100644 --- a/drivers/nfc/nfcmrvl/Kconfig +++ b/drivers/nfc/nfcmrvl/Kconfig | |||
@@ -21,3 +21,14 @@ config NFC_MRVL_USB | |||
21 | 21 | ||
22 | Say Y here to compile support for Marvell NFC-over-USB driver | 22 | Say Y here to compile support for Marvell NFC-over-USB driver |
23 | into the kernel or say M to compile it as module. | 23 | into the kernel or say M to compile it as module. |
24 | |||
25 | config NFC_MRVL_UART | ||
26 | tristate "Marvell NFC-over-UART driver" | ||
27 | depends on NFC_MRVL && NFC_NCI_UART | ||
28 | help | ||
29 | Marvell NFC-over-UART driver. | ||
30 | |||
31 | This driver provides support for Marvell NFC-over-UART devices | ||
32 | |||
33 | Say Y here to compile support for Marvell NFC-over-UART driver | ||
34 | into the kernel or say M to compile it as module. | ||
diff --git a/drivers/nfc/nfcmrvl/Makefile b/drivers/nfc/nfcmrvl/Makefile index 97a0de72dc01..775196274d1f 100644 --- a/drivers/nfc/nfcmrvl/Makefile +++ b/drivers/nfc/nfcmrvl/Makefile | |||
@@ -7,3 +7,6 @@ obj-$(CONFIG_NFC_MRVL) += nfcmrvl.o | |||
7 | 7 | ||
8 | nfcmrvl_usb-y += usb.o | 8 | nfcmrvl_usb-y += usb.o |
9 | obj-$(CONFIG_NFC_MRVL_USB) += nfcmrvl_usb.o | 9 | obj-$(CONFIG_NFC_MRVL_USB) += nfcmrvl_usb.o |
10 | |||
11 | nfcmrvl_uart-y += uart.o | ||
12 | obj-$(CONFIG_NFC_MRVL_UART) += nfcmrvl_uart.o | ||
diff --git a/drivers/nfc/nfcmrvl/nfcmrvl.h b/drivers/nfc/nfcmrvl/nfcmrvl.h index 214412bd6110..09780d57c9b8 100644 --- a/drivers/nfc/nfcmrvl/nfcmrvl.h +++ b/drivers/nfc/nfcmrvl/nfcmrvl.h | |||
@@ -43,6 +43,11 @@ | |||
43 | #define NFCMRVL_HCI_OGF 0x81 | 43 | #define NFCMRVL_HCI_OGF 0x81 |
44 | #define NFCMRVL_HCI_OCF 0xFE | 44 | #define NFCMRVL_HCI_OCF 0xFE |
45 | 45 | ||
46 | enum nfcmrvl_phy { | ||
47 | NFCMRVL_PHY_USB = 0, | ||
48 | NFCMRVL_PHY_UART = 1, | ||
49 | }; | ||
50 | |||
46 | 51 | ||
47 | struct nfcmrvl_private { | 52 | struct nfcmrvl_private { |
48 | 53 | ||
@@ -61,6 +66,8 @@ struct nfcmrvl_private { | |||
61 | void *drv_data; | 66 | void *drv_data; |
62 | /* PHY device */ | 67 | /* PHY device */ |
63 | struct device *dev; | 68 | struct device *dev; |
69 | /* PHY type */ | ||
70 | enum nfcmrvl_phy phy; | ||
64 | /* Low level driver ops */ | 71 | /* Low level driver ops */ |
65 | struct nfcmrvl_if_ops *if_ops; | 72 | struct nfcmrvl_if_ops *if_ops; |
66 | }; | 73 | }; |
diff --git a/drivers/nfc/nfcmrvl/uart.c b/drivers/nfc/nfcmrvl/uart.c new file mode 100644 index 000000000000..61442d6528a6 --- /dev/null +++ b/drivers/nfc/nfcmrvl/uart.c | |||
@@ -0,0 +1,225 @@ | |||
1 | /** | ||
2 | * Marvell NFC-over-UART driver | ||
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/delay.h> | ||
21 | #include <linux/of_gpio.h> | ||
22 | #include <net/nfc/nci.h> | ||
23 | #include <net/nfc/nci_core.h> | ||
24 | #include "nfcmrvl.h" | ||
25 | |||
26 | static unsigned int hci_muxed; | ||
27 | static unsigned int flow_control; | ||
28 | static unsigned int break_control; | ||
29 | static unsigned int reset_n_io; | ||
30 | |||
31 | /* | ||
32 | ** NFCMRVL NCI OPS | ||
33 | */ | ||
34 | |||
35 | static int nfcmrvl_uart_nci_open(struct nfcmrvl_private *priv) | ||
36 | { | ||
37 | return 0; | ||
38 | } | ||
39 | |||
40 | static int nfcmrvl_uart_nci_close(struct nfcmrvl_private *priv) | ||
41 | { | ||
42 | return 0; | ||
43 | } | ||
44 | |||
45 | static int nfcmrvl_uart_nci_send(struct nfcmrvl_private *priv, | ||
46 | struct sk_buff *skb) | ||
47 | { | ||
48 | struct nci_uart *nu = priv->drv_data; | ||
49 | |||
50 | return nu->ops.send(nu, skb); | ||
51 | } | ||
52 | |||
53 | static struct nfcmrvl_if_ops uart_ops = { | ||
54 | .nci_open = nfcmrvl_uart_nci_open, | ||
55 | .nci_close = nfcmrvl_uart_nci_close, | ||
56 | .nci_send = nfcmrvl_uart_nci_send, | ||
57 | }; | ||
58 | |||
59 | #ifdef CONFIG_OF | ||
60 | |||
61 | static int nfcmrvl_uart_parse_dt(struct device_node *node, | ||
62 | struct nfcmrvl_platform_data *pdata) | ||
63 | { | ||
64 | struct device_node *matched_node; | ||
65 | int ret; | ||
66 | |||
67 | matched_node = of_find_compatible_node(node, NULL, "mrvl,nfc-uart"); | ||
68 | if (!matched_node) | ||
69 | return -ENODEV; | ||
70 | |||
71 | ret = nfcmrvl_parse_dt(matched_node, pdata); | ||
72 | if (ret < 0) { | ||
73 | pr_err("Failed to get generic entries\n"); | ||
74 | return ret; | ||
75 | } | ||
76 | |||
77 | if (of_find_property(matched_node, "flow-control", NULL)) | ||
78 | pdata->flow_control = 1; | ||
79 | else | ||
80 | pdata->flow_control = 0; | ||
81 | |||
82 | if (of_find_property(matched_node, "break-control", NULL)) | ||
83 | pdata->break_control = 1; | ||
84 | else | ||
85 | pdata->break_control = 0; | ||
86 | |||
87 | return 0; | ||
88 | } | ||
89 | |||
90 | #else | ||
91 | |||
92 | static int nfcmrvl_uart_parse_dt(struct device_node *node, | ||
93 | struct nfcmrvl_platform_data *pdata) | ||
94 | { | ||
95 | return -ENODEV; | ||
96 | } | ||
97 | |||
98 | #endif | ||
99 | |||
100 | /* | ||
101 | ** NCI UART OPS | ||
102 | */ | ||
103 | |||
104 | static int nfcmrvl_nci_uart_open(struct nci_uart *nu) | ||
105 | { | ||
106 | struct nfcmrvl_private *priv; | ||
107 | struct nfcmrvl_platform_data *pdata = NULL; | ||
108 | struct nfcmrvl_platform_data config; | ||
109 | |||
110 | /* | ||
111 | * Platform data cannot be used here since usually it is already used | ||
112 | * by low level serial driver. We can try to retrieve serial device | ||
113 | * and check if DT entries were added. | ||
114 | */ | ||
115 | |||
116 | if (nu->tty->dev->parent && nu->tty->dev->parent->of_node) | ||
117 | if (nfcmrvl_uart_parse_dt(nu->tty->dev->parent->of_node, | ||
118 | &config) == 0) | ||
119 | pdata = &config; | ||
120 | |||
121 | if (!pdata) { | ||
122 | pr_info("No platform data / DT -> fallback to module params\n"); | ||
123 | config.hci_muxed = hci_muxed; | ||
124 | config.reset_n_io = reset_n_io; | ||
125 | config.flow_control = flow_control; | ||
126 | config.break_control = break_control; | ||
127 | pdata = &config; | ||
128 | } | ||
129 | |||
130 | priv = nfcmrvl_nci_register_dev(nu, &uart_ops, nu->tty->dev, pdata); | ||
131 | if (IS_ERR(priv)) | ||
132 | return PTR_ERR(priv); | ||
133 | |||
134 | priv->phy = NFCMRVL_PHY_UART; | ||
135 | |||
136 | nu->drv_data = priv; | ||
137 | nu->ndev = priv->ndev; | ||
138 | |||
139 | /* Set BREAK */ | ||
140 | if (priv->config.break_control && nu->tty->ops->break_ctl) | ||
141 | nu->tty->ops->break_ctl(nu->tty, -1); | ||
142 | |||
143 | return 0; | ||
144 | } | ||
145 | |||
146 | static void nfcmrvl_nci_uart_close(struct nci_uart *nu) | ||
147 | { | ||
148 | nfcmrvl_nci_unregister_dev((struct nfcmrvl_private *)nu->drv_data); | ||
149 | } | ||
150 | |||
151 | static int nfcmrvl_nci_uart_recv(struct nci_uart *nu, struct sk_buff *skb) | ||
152 | { | ||
153 | return nfcmrvl_nci_recv_frame((struct nfcmrvl_private *)nu->drv_data, | ||
154 | skb); | ||
155 | } | ||
156 | |||
157 | static void nfcmrvl_nci_uart_tx_start(struct nci_uart *nu) | ||
158 | { | ||
159 | struct nfcmrvl_private *priv = (struct nfcmrvl_private *)nu->drv_data; | ||
160 | |||
161 | /* Remove BREAK to wake up the NFCC */ | ||
162 | if (priv->config.break_control && nu->tty->ops->break_ctl) { | ||
163 | nu->tty->ops->break_ctl(nu->tty, 0); | ||
164 | usleep_range(3000, 5000); | ||
165 | } | ||
166 | } | ||
167 | |||
168 | static void nfcmrvl_nci_uart_tx_done(struct nci_uart *nu) | ||
169 | { | ||
170 | struct nfcmrvl_private *priv = (struct nfcmrvl_private *)nu->drv_data; | ||
171 | |||
172 | /* | ||
173 | ** To ensure that if the NFCC goes in DEEP SLEEP sate we can wake him | ||
174 | ** up. we set BREAK. Once we will be ready to send again we will remove | ||
175 | ** it. | ||
176 | */ | ||
177 | if (priv->config.break_control && nu->tty->ops->break_ctl) | ||
178 | nu->tty->ops->break_ctl(nu->tty, -1); | ||
179 | } | ||
180 | |||
181 | static struct nci_uart nfcmrvl_nci_uart = { | ||
182 | .owner = THIS_MODULE, | ||
183 | .name = "nfcmrvl_uart", | ||
184 | .driver = NCI_UART_DRIVER_MARVELL, | ||
185 | .ops = { | ||
186 | .open = nfcmrvl_nci_uart_open, | ||
187 | .close = nfcmrvl_nci_uart_close, | ||
188 | .recv = nfcmrvl_nci_uart_recv, | ||
189 | .tx_start = nfcmrvl_nci_uart_tx_start, | ||
190 | .tx_done = nfcmrvl_nci_uart_tx_done, | ||
191 | } | ||
192 | }; | ||
193 | |||
194 | /* | ||
195 | ** Module init | ||
196 | */ | ||
197 | |||
198 | static int nfcmrvl_uart_init_module(void) | ||
199 | { | ||
200 | return nci_uart_register(&nfcmrvl_nci_uart); | ||
201 | } | ||
202 | |||
203 | static void nfcmrvl_uart_exit_module(void) | ||
204 | { | ||
205 | nci_uart_unregister(&nfcmrvl_nci_uart); | ||
206 | } | ||
207 | |||
208 | module_init(nfcmrvl_uart_init_module); | ||
209 | module_exit(nfcmrvl_uart_exit_module); | ||
210 | |||
211 | MODULE_AUTHOR("Marvell International Ltd."); | ||
212 | MODULE_DESCRIPTION("Marvell NFC-over-UART"); | ||
213 | MODULE_LICENSE("GPL v2"); | ||
214 | |||
215 | module_param(flow_control, uint, 0); | ||
216 | MODULE_PARM_DESC(flow_control, "Tell if UART needs flow control at init."); | ||
217 | |||
218 | module_param(break_control, uint, 0); | ||
219 | MODULE_PARM_DESC(break_control, "Tell if UART driver must drive break signal."); | ||
220 | |||
221 | module_param(hci_muxed, uint, 0); | ||
222 | MODULE_PARM_DESC(hci_muxed, "Tell if transport is muxed in HCI one."); | ||
223 | |||
224 | module_param(reset_n_io, uint, 0); | ||
225 | MODULE_PARM_DESC(reset_n_io, "GPIO that is wired to RESET_N signal."); | ||