aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorAnatolij Gustschin <agust@denx.de>2017-03-23 20:34:26 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2017-04-08 11:45:28 -0400
commit061c97d13f1a69c0edcab4dc6e97788e5bf3230f (patch)
tree450bb6e43e267d4de22de9b6c9a3775d624b7295 /drivers
parent12cc5a123a173d6236903af6f3d9d44c81723751 (diff)
fpga manager: Add Xilinx slave serial SPI driver
The driver loads FPGA firmware over SPI, using the "slave serial" configuration interface on Xilinx FPGAs. Signed-off-by: Anatolij Gustschin <agust@denx.de> Acked-by: Michal Simek <michal.simek@xilinx.com> Reviewed-by: Moritz Fischer <mdf@kernel.org> Acked-by: Alan Tull <atull@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/fpga/Kconfig7
-rw-r--r--drivers/fpga/Makefile1
-rw-r--r--drivers/fpga/xilinx-spi.c198
3 files changed, 206 insertions, 0 deletions
diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig
index dee470f9113f..c81cb7d9fbd5 100644
--- a/drivers/fpga/Kconfig
+++ b/drivers/fpga/Kconfig
@@ -46,6 +46,13 @@ config FPGA_MGR_TS73XX
46 FPGA manager driver support for the Altera Cyclone II FPGA 46 FPGA manager driver support for the Altera Cyclone II FPGA
47 present on the TS-73xx SBC boards. 47 present on the TS-73xx SBC boards.
48 48
49config FPGA_MGR_XILINX_SPI
50 tristate "Xilinx Configuration over Slave Serial (SPI)"
51 depends on SPI
52 help
53 FPGA manager driver support for Xilinx FPGA configuration
54 over slave serial interface.
55
49config FPGA_MGR_ZYNQ_FPGA 56config FPGA_MGR_ZYNQ_FPGA
50 tristate "Xilinx Zynq FPGA" 57 tristate "Xilinx Zynq FPGA"
51 depends on ARCH_ZYNQ || COMPILE_TEST 58 depends on ARCH_ZYNQ || COMPILE_TEST
diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile
index a5ee3ffe8b18..c6f5d740b946 100644
--- a/drivers/fpga/Makefile
+++ b/drivers/fpga/Makefile
@@ -10,6 +10,7 @@ obj-$(CONFIG_FPGA_MGR_ICE40_SPI) += ice40-spi.o
10obj-$(CONFIG_FPGA_MGR_SOCFPGA) += socfpga.o 10obj-$(CONFIG_FPGA_MGR_SOCFPGA) += socfpga.o
11obj-$(CONFIG_FPGA_MGR_SOCFPGA_A10) += socfpga-a10.o 11obj-$(CONFIG_FPGA_MGR_SOCFPGA_A10) += socfpga-a10.o
12obj-$(CONFIG_FPGA_MGR_TS73XX) += ts73xx-fpga.o 12obj-$(CONFIG_FPGA_MGR_TS73XX) += ts73xx-fpga.o
13obj-$(CONFIG_FPGA_MGR_XILINX_SPI) += xilinx-spi.o
13obj-$(CONFIG_FPGA_MGR_ZYNQ_FPGA) += zynq-fpga.o 14obj-$(CONFIG_FPGA_MGR_ZYNQ_FPGA) += zynq-fpga.o
14 15
15# FPGA Bridge Drivers 16# FPGA Bridge Drivers
diff --git a/drivers/fpga/xilinx-spi.c b/drivers/fpga/xilinx-spi.c
new file mode 100644
index 000000000000..9b62a4c2a3df
--- /dev/null
+++ b/drivers/fpga/xilinx-spi.c
@@ -0,0 +1,198 @@
1/*
2 * Xilinx Spartan6 Slave Serial SPI Driver
3 *
4 * Copyright (C) 2017 DENX Software Engineering
5 *
6 * Anatolij Gustschin <agust@denx.de>
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms and conditions of the GNU General Public License,
10 * version 2, as published by the Free Software Foundation.
11 *
12 * Manage Xilinx FPGA firmware that is loaded over SPI using
13 * the slave serial configuration interface.
14 */
15
16#include <linux/delay.h>
17#include <linux/device.h>
18#include <linux/fpga/fpga-mgr.h>
19#include <linux/gpio/consumer.h>
20#include <linux/module.h>
21#include <linux/mod_devicetable.h>
22#include <linux/of.h>
23#include <linux/spi/spi.h>
24#include <linux/sizes.h>
25
26struct xilinx_spi_conf {
27 struct spi_device *spi;
28 struct gpio_desc *prog_b;
29 struct gpio_desc *done;
30};
31
32static enum fpga_mgr_states xilinx_spi_state(struct fpga_manager *mgr)
33{
34 struct xilinx_spi_conf *conf = mgr->priv;
35
36 if (!gpiod_get_value(conf->done))
37 return FPGA_MGR_STATE_RESET;
38
39 return FPGA_MGR_STATE_UNKNOWN;
40}
41
42static int xilinx_spi_write_init(struct fpga_manager *mgr,
43 struct fpga_image_info *info,
44 const char *buf, size_t count)
45{
46 struct xilinx_spi_conf *conf = mgr->priv;
47 const size_t prog_latency_7500us = 7500;
48 const size_t prog_pulse_1us = 1;
49
50 if (info->flags & FPGA_MGR_PARTIAL_RECONFIG) {
51 dev_err(&mgr->dev, "Partial reconfiguration not supported.\n");
52 return -EINVAL;
53 }
54
55 gpiod_set_value(conf->prog_b, 1);
56
57 udelay(prog_pulse_1us); /* min is 500 ns */
58
59 gpiod_set_value(conf->prog_b, 0);
60
61 if (gpiod_get_value(conf->done)) {
62 dev_err(&mgr->dev, "Unexpected DONE pin state...\n");
63 return -EIO;
64 }
65
66 /* program latency */
67 usleep_range(prog_latency_7500us, prog_latency_7500us + 100);
68 return 0;
69}
70
71static int xilinx_spi_write(struct fpga_manager *mgr, const char *buf,
72 size_t count)
73{
74 struct xilinx_spi_conf *conf = mgr->priv;
75 const char *fw_data = buf;
76 const char *fw_data_end = fw_data + count;
77
78 while (fw_data < fw_data_end) {
79 size_t remaining, stride;
80 int ret;
81
82 remaining = fw_data_end - fw_data;
83 stride = min_t(size_t, remaining, SZ_4K);
84
85 ret = spi_write(conf->spi, fw_data, stride);
86 if (ret) {
87 dev_err(&mgr->dev, "SPI error in firmware write: %d\n",
88 ret);
89 return ret;
90 }
91 fw_data += stride;
92 }
93
94 return 0;
95}
96
97static int xilinx_spi_apply_cclk_cycles(struct xilinx_spi_conf *conf)
98{
99 struct spi_device *spi = conf->spi;
100 const u8 din_data[1] = { 0xff };
101 int ret;
102
103 ret = spi_write(conf->spi, din_data, sizeof(din_data));
104 if (ret)
105 dev_err(&spi->dev, "applying CCLK cycles failed: %d\n", ret);
106
107 return ret;
108}
109
110static int xilinx_spi_write_complete(struct fpga_manager *mgr,
111 struct fpga_image_info *info)
112{
113 struct xilinx_spi_conf *conf = mgr->priv;
114 unsigned long timeout;
115 int ret;
116
117 if (gpiod_get_value(conf->done))
118 return xilinx_spi_apply_cclk_cycles(conf);
119
120 timeout = jiffies + usecs_to_jiffies(info->config_complete_timeout_us);
121
122 while (time_before(jiffies, timeout)) {
123
124 ret = xilinx_spi_apply_cclk_cycles(conf);
125 if (ret)
126 return ret;
127
128 if (gpiod_get_value(conf->done))
129 return xilinx_spi_apply_cclk_cycles(conf);
130 }
131
132 dev_err(&mgr->dev, "Timeout after config data transfer.\n");
133 return -ETIMEDOUT;
134}
135
136static const struct fpga_manager_ops xilinx_spi_ops = {
137 .state = xilinx_spi_state,
138 .write_init = xilinx_spi_write_init,
139 .write = xilinx_spi_write,
140 .write_complete = xilinx_spi_write_complete,
141};
142
143static int xilinx_spi_probe(struct spi_device *spi)
144{
145 struct xilinx_spi_conf *conf;
146
147 conf = devm_kzalloc(&spi->dev, sizeof(*conf), GFP_KERNEL);
148 if (!conf)
149 return -ENOMEM;
150
151 conf->spi = spi;
152
153 /* PROGRAM_B is active low */
154 conf->prog_b = devm_gpiod_get(&spi->dev, "prog_b", GPIOD_OUT_LOW);
155 if (IS_ERR(conf->prog_b)) {
156 dev_err(&spi->dev, "Failed to get PROGRAM_B gpio: %ld\n",
157 PTR_ERR(conf->prog_b));
158 return PTR_ERR(conf->prog_b);
159 }
160
161 conf->done = devm_gpiod_get(&spi->dev, "done", GPIOD_IN);
162 if (IS_ERR(conf->done)) {
163 dev_err(&spi->dev, "Failed to get DONE gpio: %ld\n",
164 PTR_ERR(conf->done));
165 return PTR_ERR(conf->done);
166 }
167
168 return fpga_mgr_register(&spi->dev, "Xilinx Slave Serial FPGA Manager",
169 &xilinx_spi_ops, conf);
170}
171
172static int xilinx_spi_remove(struct spi_device *spi)
173{
174 fpga_mgr_unregister(&spi->dev);
175
176 return 0;
177}
178
179static const struct of_device_id xlnx_spi_of_match[] = {
180 { .compatible = "xlnx,fpga-slave-serial", },
181 {}
182};
183MODULE_DEVICE_TABLE(of, xlnx_spi_of_match);
184
185static struct spi_driver xilinx_slave_spi_driver = {
186 .driver = {
187 .name = "xlnx-slave-spi",
188 .of_match_table = of_match_ptr(xlnx_spi_of_match),
189 },
190 .probe = xilinx_spi_probe,
191 .remove = xilinx_spi_remove,
192};
193
194module_spi_driver(xilinx_slave_spi_driver)
195
196MODULE_LICENSE("GPL v2");
197MODULE_AUTHOR("Anatolij Gustschin <agust@denx.de>");
198MODULE_DESCRIPTION("Load Xilinx FPGA firmware over SPI");