diff options
author | Vincent Cuissard <cuissard@marvell.com> | 2015-10-26 05:27:44 -0400 |
---|---|---|
committer | Samuel Ortiz <sameo@linux.intel.com> | 2015-10-26 23:24:35 -0400 |
commit | caf6e49bf6d02e6bb94df680bbe3beaf680fdefa (patch) | |
tree | 94c3e0be315322c66ed2347fb52666b5d81ede75 | |
parent | 2bd832459a0827b8dcf13b345380b66f92089d74 (diff) |
NFC: nfcmrvl: add spi driver
This driver adds the support of SPI-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.txt | 25 | ||||
-rw-r--r-- | drivers/nfc/nfcmrvl/Kconfig | 10 | ||||
-rw-r--r-- | drivers/nfc/nfcmrvl/Makefile | 3 | ||||
-rw-r--r-- | drivers/nfc/nfcmrvl/main.c | 6 | ||||
-rw-r--r-- | drivers/nfc/nfcmrvl/spi.c | 228 |
5 files changed, 271 insertions, 1 deletions
diff --git a/Documentation/devicetree/bindings/net/nfc/nfcmrvl.txt b/Documentation/devicetree/bindings/net/nfc/nfcmrvl.txt index 0fa20cc2c33c..41058fcbd9ae 100644 --- a/Documentation/devicetree/bindings/net/nfc/nfcmrvl.txt +++ b/Documentation/devicetree/bindings/net/nfc/nfcmrvl.txt | |||
@@ -4,6 +4,7 @@ Required properties: | |||
4 | - compatible: Should be: | 4 | - compatible: Should be: |
5 | - "mrvl,nfc-uart" for UART devices | 5 | - "mrvl,nfc-uart" for UART devices |
6 | - "mrvl,nfc-i2c" for I2C devices | 6 | - "mrvl,nfc-i2c" for I2C devices |
7 | - "mrvl,nfc-spi" for SPI devices | ||
7 | 8 | ||
8 | Optional SoC specific properties: | 9 | Optional SoC specific properties: |
9 | - pinctrl-names: Contains only one value - "default". | 10 | - pinctrl-names: Contains only one value - "default". |
@@ -59,3 +60,27 @@ Example (for ARM-based BeagleBoard Black with 88W8887 on I2C1): | |||
59 | reset-n-io = <&gpio3 19 0>; | 60 | reset-n-io = <&gpio3 19 0>; |
60 | }; | 61 | }; |
61 | }; | 62 | }; |
63 | |||
64 | |||
65 | Example (for ARM-based BeagleBoard Black on SPI0): | ||
66 | |||
67 | &spi0 { | ||
68 | |||
69 | mrvlnfcspi0: spi@0 { | ||
70 | compatible = "mrvl,nfc-spi"; | ||
71 | |||
72 | reg = <0>; | ||
73 | |||
74 | /* SPI Bus configuration */ | ||
75 | spi-max-frequency = <3000000>; | ||
76 | spi-cpha; | ||
77 | spi-cpol; | ||
78 | |||
79 | /* SPI INT configuration */ | ||
80 | interrupt-parent = <&gpio1>; | ||
81 | interrupts = <17 0>; | ||
82 | |||
83 | /* Reset IO */ | ||
84 | reset-n-io = <&gpio3 19 0>; | ||
85 | }; | ||
86 | }; | ||
diff --git a/drivers/nfc/nfcmrvl/Kconfig b/drivers/nfc/nfcmrvl/Kconfig index e18a979bb6d3..444ca94697d9 100644 --- a/drivers/nfc/nfcmrvl/Kconfig +++ b/drivers/nfc/nfcmrvl/Kconfig | |||
@@ -42,3 +42,13 @@ config NFC_MRVL_I2C | |||
42 | Say Y here to compile support for Marvell NFC-over-I2C driver | 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. | 43 | into the kernel or say M to compile it as module. |
44 | 44 | ||
45 | config NFC_MRVL_SPI | ||
46 | tristate "Marvell NFC-over-SPI driver" | ||
47 | depends on NFC_MRVL && SPI | ||
48 | help | ||
49 | Marvell NFC-over-SPI driver. | ||
50 | |||
51 | This driver provides support for Marvell NFC-over-SPI devices. | ||
52 | |||
53 | Say Y here to compile support for Marvell NFC-over-SPI driver | ||
54 | into the kernel or say M to compile it as module. | ||
diff --git a/drivers/nfc/nfcmrvl/Makefile b/drivers/nfc/nfcmrvl/Makefile index 895866a3ebc6..fa07c7806492 100644 --- a/drivers/nfc/nfcmrvl/Makefile +++ b/drivers/nfc/nfcmrvl/Makefile | |||
@@ -13,3 +13,6 @@ obj-$(CONFIG_NFC_MRVL_UART) += nfcmrvl_uart.o | |||
13 | 13 | ||
14 | nfcmrvl_i2c-y += i2c.o | 14 | nfcmrvl_i2c-y += i2c.o |
15 | obj-$(CONFIG_NFC_MRVL_I2C) += nfcmrvl_i2c.o | 15 | obj-$(CONFIG_NFC_MRVL_I2C) += nfcmrvl_i2c.o |
16 | |||
17 | nfcmrvl_spi-y += spi.o | ||
18 | obj-$(CONFIG_NFC_MRVL_SPI) += nfcmrvl_spi.o | ||
diff --git a/drivers/nfc/nfcmrvl/main.c b/drivers/nfc/nfcmrvl/main.c index 0c27de60a6bd..8079ae0de21e 100644 --- a/drivers/nfc/nfcmrvl/main.c +++ b/drivers/nfc/nfcmrvl/main.c | |||
@@ -132,7 +132,11 @@ struct nfcmrvl_private *nfcmrvl_nci_register_dev(enum nfcmrvl_phy phy, | |||
132 | nfc_err(dev, "failed to request reset_n io\n"); | 132 | nfc_err(dev, "failed to request reset_n io\n"); |
133 | } | 133 | } |
134 | 134 | ||
135 | headroom = tailroom = 0; | 135 | if (phy == NFCMRVL_PHY_SPI) { |
136 | headroom = NCI_SPI_HDR_LEN; | ||
137 | tailroom = 1; | ||
138 | } else | ||
139 | headroom = tailroom = 0; | ||
136 | 140 | ||
137 | if (priv->config.hci_muxed) | 141 | if (priv->config.hci_muxed) |
138 | headroom += NFCMRVL_HCI_EVENT_HEADER_SIZE; | 142 | headroom += NFCMRVL_HCI_EVENT_HEADER_SIZE; |
diff --git a/drivers/nfc/nfcmrvl/spi.c b/drivers/nfc/nfcmrvl/spi.c new file mode 100644 index 000000000000..358660acd54a --- /dev/null +++ b/drivers/nfc/nfcmrvl/spi.c | |||
@@ -0,0 +1,228 @@ | |||
1 | /** | ||
2 | * Marvell NFC-over-SPI driver: SPI 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/pm_runtime.h> | ||
22 | #include <linux/nfc.h> | ||
23 | #include <linux/gpio.h> | ||
24 | #include <linux/of_irq.h> | ||
25 | #include <linux/of_gpio.h> | ||
26 | #include <net/nfc/nci.h> | ||
27 | #include <net/nfc/nci_core.h> | ||
28 | #include <linux/spi/spi.h> | ||
29 | #include <linux/gpio.h> | ||
30 | #include "nfcmrvl.h" | ||
31 | |||
32 | #define SPI_WAIT_HANDSHAKE 1 | ||
33 | |||
34 | struct nfcmrvl_spi_drv_data { | ||
35 | unsigned long flags; | ||
36 | struct spi_device *spi; | ||
37 | struct nci_spi *nci_spi; | ||
38 | struct completion handshake_completion; | ||
39 | struct nfcmrvl_private *priv; | ||
40 | }; | ||
41 | |||
42 | static irqreturn_t nfcmrvl_spi_int_irq_thread_fn(int irq, void *drv_data_ptr) | ||
43 | { | ||
44 | struct nfcmrvl_spi_drv_data *drv_data = drv_data_ptr; | ||
45 | struct sk_buff *skb; | ||
46 | |||
47 | /* | ||
48 | * Special case where we are waiting for SPI_INT deassertion to start a | ||
49 | * transfer. | ||
50 | */ | ||
51 | if (test_and_clear_bit(SPI_WAIT_HANDSHAKE, &drv_data->flags)) { | ||
52 | complete(&drv_data->handshake_completion); | ||
53 | return IRQ_HANDLED; | ||
54 | } | ||
55 | |||
56 | /* Normal case, SPI_INT deasserted by slave to trigger a master read */ | ||
57 | |||
58 | skb = nci_spi_read(drv_data->nci_spi); | ||
59 | if (!skb) { | ||
60 | nfc_err(&drv_data->spi->dev, "failed to read spi packet"); | ||
61 | return IRQ_HANDLED; | ||
62 | } | ||
63 | |||
64 | if (nfcmrvl_nci_recv_frame(drv_data->priv, skb) < 0) | ||
65 | nfc_err(&drv_data->spi->dev, "corrupted RX packet"); | ||
66 | |||
67 | return IRQ_HANDLED; | ||
68 | } | ||
69 | |||
70 | static int nfcmrvl_spi_nci_open(struct nfcmrvl_private *priv) | ||
71 | { | ||
72 | return 0; | ||
73 | } | ||
74 | |||
75 | static int nfcmrvl_spi_nci_close(struct nfcmrvl_private *priv) | ||
76 | { | ||
77 | return 0; | ||
78 | } | ||
79 | |||
80 | static int nfcmrvl_spi_nci_send(struct nfcmrvl_private *priv, | ||
81 | struct sk_buff *skb) | ||
82 | { | ||
83 | struct nfcmrvl_spi_drv_data *drv_data = priv->drv_data; | ||
84 | int err; | ||
85 | |||
86 | /* Reinit completion for slave handshake */ | ||
87 | reinit_completion(&drv_data->handshake_completion); | ||
88 | set_bit(SPI_WAIT_HANDSHAKE, &drv_data->flags); | ||
89 | |||
90 | /* | ||
91 | * Append a dummy byte at the end of SPI frame. This is due to a | ||
92 | * specific DMA implementation in the controller | ||
93 | */ | ||
94 | skb_put(skb, 1); | ||
95 | |||
96 | /* Send the SPI packet */ | ||
97 | err = nci_spi_send(drv_data->nci_spi, &drv_data->handshake_completion, | ||
98 | skb); | ||
99 | if (err != 0) { | ||
100 | nfc_err(priv->dev, "spi_send failed %d", err); | ||
101 | kfree_skb(skb); | ||
102 | } | ||
103 | return err; | ||
104 | } | ||
105 | |||
106 | static void nfcmrvl_spi_nci_update_config(struct nfcmrvl_private *priv, | ||
107 | const void *param) | ||
108 | { | ||
109 | struct nfcmrvl_spi_drv_data *drv_data = priv->drv_data; | ||
110 | const struct nfcmrvl_fw_spi_config *config = param; | ||
111 | |||
112 | drv_data->nci_spi->xfer_speed_hz = config->clk; | ||
113 | } | ||
114 | |||
115 | static struct nfcmrvl_if_ops spi_ops = { | ||
116 | .nci_open = nfcmrvl_spi_nci_open, | ||
117 | .nci_close = nfcmrvl_spi_nci_close, | ||
118 | .nci_send = nfcmrvl_spi_nci_send, | ||
119 | .nci_update_config = nfcmrvl_spi_nci_update_config, | ||
120 | }; | ||
121 | |||
122 | static int nfcmrvl_spi_parse_dt(struct device_node *node, | ||
123 | struct nfcmrvl_platform_data *pdata) | ||
124 | { | ||
125 | int ret; | ||
126 | |||
127 | ret = nfcmrvl_parse_dt(node, pdata); | ||
128 | if (ret < 0) { | ||
129 | pr_err("Failed to get generic entries\n"); | ||
130 | return ret; | ||
131 | } | ||
132 | |||
133 | ret = irq_of_parse_and_map(node, 0); | ||
134 | if (ret < 0) { | ||
135 | pr_err("Unable to get irq, error: %d\n", ret); | ||
136 | return ret; | ||
137 | } | ||
138 | pdata->irq = ret; | ||
139 | |||
140 | return 0; | ||
141 | } | ||
142 | |||
143 | static int nfcmrvl_spi_probe(struct spi_device *spi) | ||
144 | { | ||
145 | struct nfcmrvl_platform_data *pdata; | ||
146 | struct nfcmrvl_platform_data config; | ||
147 | struct nfcmrvl_spi_drv_data *drv_data; | ||
148 | int ret = 0; | ||
149 | |||
150 | drv_data = devm_kzalloc(&spi->dev, sizeof(*drv_data), GFP_KERNEL); | ||
151 | if (!drv_data) | ||
152 | return -ENOMEM; | ||
153 | |||
154 | drv_data->spi = spi; | ||
155 | drv_data->priv = NULL; | ||
156 | spi_set_drvdata(spi, drv_data); | ||
157 | |||
158 | pdata = spi->dev.platform_data; | ||
159 | |||
160 | if (!pdata && spi->dev.of_node) | ||
161 | if (nfcmrvl_spi_parse_dt(spi->dev.of_node, &config) == 0) | ||
162 | pdata = &config; | ||
163 | |||
164 | if (!pdata) | ||
165 | return -EINVAL; | ||
166 | |||
167 | ret = devm_request_threaded_irq(&drv_data->spi->dev, pdata->irq, | ||
168 | NULL, nfcmrvl_spi_int_irq_thread_fn, | ||
169 | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, | ||
170 | "nfcmrvl_spi_int", drv_data); | ||
171 | if (ret < 0) { | ||
172 | nfc_err(&drv_data->spi->dev, "Unable to register IRQ handler"); | ||
173 | return -ENODEV; | ||
174 | } | ||
175 | |||
176 | drv_data->priv = nfcmrvl_nci_register_dev(NFCMRVL_PHY_SPI, | ||
177 | drv_data, &spi_ops, | ||
178 | &drv_data->spi->dev, | ||
179 | pdata); | ||
180 | if (IS_ERR(drv_data->priv)) | ||
181 | return PTR_ERR(drv_data->priv); | ||
182 | |||
183 | drv_data->priv->support_fw_dnld = true; | ||
184 | |||
185 | drv_data->nci_spi = nci_spi_allocate_spi(drv_data->spi, 0, 10, | ||
186 | drv_data->priv->ndev); | ||
187 | |||
188 | /* Init completion for slave handshake */ | ||
189 | init_completion(&drv_data->handshake_completion); | ||
190 | return 0; | ||
191 | } | ||
192 | |||
193 | static int nfcmrvl_spi_remove(struct spi_device *spi) | ||
194 | { | ||
195 | struct nfcmrvl_spi_drv_data *drv_data = spi_get_drvdata(spi); | ||
196 | |||
197 | nfcmrvl_nci_unregister_dev(drv_data->priv); | ||
198 | return 0; | ||
199 | } | ||
200 | |||
201 | static const struct of_device_id of_nfcmrvl_spi_match[] = { | ||
202 | { .compatible = "mrvl,nfc-spi", }, | ||
203 | {}, | ||
204 | }; | ||
205 | MODULE_DEVICE_TABLE(of, of_nfcmrvl_spi_match); | ||
206 | |||
207 | static const struct spi_device_id nfcmrvl_spi_id_table[] = { | ||
208 | { "nfcmrvl_spi", 0 }, | ||
209 | { } | ||
210 | }; | ||
211 | MODULE_DEVICE_TABLE(spi, nfcmrvl_spi_id_table); | ||
212 | |||
213 | static struct spi_driver nfcmrvl_spi_driver = { | ||
214 | .probe = nfcmrvl_spi_probe, | ||
215 | .remove = nfcmrvl_spi_remove, | ||
216 | .id_table = nfcmrvl_spi_id_table, | ||
217 | .driver = { | ||
218 | .name = "nfcmrvl_spi", | ||
219 | .owner = THIS_MODULE, | ||
220 | .of_match_table = of_match_ptr(of_nfcmrvl_spi_match), | ||
221 | }, | ||
222 | }; | ||
223 | |||
224 | module_spi_driver(nfcmrvl_spi_driver); | ||
225 | |||
226 | MODULE_AUTHOR("Marvell International Ltd."); | ||
227 | MODULE_DESCRIPTION("Marvell NFC-over-SPI driver"); | ||
228 | MODULE_LICENSE("GPL v2"); | ||