summaryrefslogtreecommitdiffstats
path: root/drivers/fpga
diff options
context:
space:
mode:
authorJoshua Clayton <stillcompiling@gmail.com>2017-06-14 11:36:29 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2017-07-17 11:26:14 -0400
commit5692fae0742df7587189174b1cba364d22846c85 (patch)
tree295d4af6f51bc3713f68ba9b0da00ae9b02b2c19 /drivers/fpga
parentf2b56452c48a6973e447dbe64ae44212e656db42 (diff)
fpga manager: Add altera-ps-spi driver for Altera FPGAs
altera-ps-spi loads FPGA firmware over SPI, using the "passive serial" interface on Altera Arria 10, Cyclone V or Stratix V FPGAs. This is one of the simpler ways to set up an FPGA at runtime. The signal interface is close to unidirectional SPI with lsb first. Signed-off-by: Joshua Clayton <stillcompiling@gmail.com> Signed-off-by: Anatolij Gustschin <agust@denx.de> Signed-off-by: Alan Tull <atull@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/fpga')
-rw-r--r--drivers/fpga/Kconfig7
-rw-r--r--drivers/fpga/Makefile1
-rw-r--r--drivers/fpga/altera-ps-spi.c297
3 files changed, 305 insertions, 0 deletions
diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig
index 10361902c830..e0e2d6db669f 100644
--- a/drivers/fpga/Kconfig
+++ b/drivers/fpga/Kconfig
@@ -24,6 +24,13 @@ config FPGA_MGR_ICE40_SPI
24 help 24 help
25 FPGA manager driver support for Lattice iCE40 FPGAs over SPI. 25 FPGA manager driver support for Lattice iCE40 FPGAs over SPI.
26 26
27config FPGA_MGR_ALTERA_PS_SPI
28 tristate "Altera FPGA Passive Serial over SPI"
29 depends on SPI
30 help
31 FPGA manager driver support for Altera Arria/Cyclone/Stratix
32 using the passive serial interface over SPI.
33
27config FPGA_MGR_SOCFPGA 34config FPGA_MGR_SOCFPGA
28 tristate "Altera SOCFPGA FPGA Manager" 35 tristate "Altera SOCFPGA FPGA Manager"
29 depends on ARCH_SOCFPGA || COMPILE_TEST 36 depends on ARCH_SOCFPGA || COMPILE_TEST
diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile
index 2a4f0218145c..e75d3570e26a 100644
--- a/drivers/fpga/Makefile
+++ b/drivers/fpga/Makefile
@@ -6,6 +6,7 @@
6obj-$(CONFIG_FPGA) += fpga-mgr.o 6obj-$(CONFIG_FPGA) += fpga-mgr.o
7 7
8# FPGA Manager Drivers 8# FPGA Manager Drivers
9obj-$(CONFIG_FPGA_MGR_ALTERA_PS_SPI) += altera-ps-spi.o
9obj-$(CONFIG_FPGA_MGR_ICE40_SPI) += ice40-spi.o 10obj-$(CONFIG_FPGA_MGR_ICE40_SPI) += ice40-spi.o
10obj-$(CONFIG_FPGA_MGR_SOCFPGA) += socfpga.o 11obj-$(CONFIG_FPGA_MGR_SOCFPGA) += socfpga.o
11obj-$(CONFIG_FPGA_MGR_SOCFPGA_A10) += socfpga-a10.o 12obj-$(CONFIG_FPGA_MGR_SOCFPGA_A10) += socfpga-a10.o
diff --git a/drivers/fpga/altera-ps-spi.c b/drivers/fpga/altera-ps-spi.c
new file mode 100644
index 000000000000..0db8def668ed
--- /dev/null
+++ b/drivers/fpga/altera-ps-spi.c
@@ -0,0 +1,297 @@
1/*
2 * Altera Passive Serial SPI Driver
3 *
4 * Copyright (c) 2017 United Western Technologies, Corporation
5 *
6 * Joshua Clayton <stillcompiling@gmail.com>
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 Altera FPGA firmware that is loaded over SPI using the passive
13 * serial configuration method.
14 * Firmware must be in binary "rbf" format.
15 * Works on Arria 10, Cyclone V and Stratix V. Should work on Cyclone series.
16 * May work on other Altera FPGAs.
17 */
18
19#include <linux/bitrev.h>
20#include <linux/delay.h>
21#include <linux/fpga/fpga-mgr.h>
22#include <linux/gpio/consumer.h>
23#include <linux/module.h>
24#include <linux/of_gpio.h>
25#include <linux/of_device.h>
26#include <linux/spi/spi.h>
27#include <linux/sizes.h>
28
29enum altera_ps_devtype {
30 CYCLONE5,
31 ARRIA10,
32};
33
34struct altera_ps_data {
35 enum altera_ps_devtype devtype;
36 int status_wait_min_us;
37 int status_wait_max_us;
38 int t_cfg_us;
39 int t_st2ck_us;
40};
41
42struct altera_ps_conf {
43 struct gpio_desc *config;
44 struct gpio_desc *confd;
45 struct gpio_desc *status;
46 struct spi_device *spi;
47 const struct altera_ps_data *data;
48 u32 info_flags;
49 char mgr_name[64];
50};
51
52/* | Arria 10 | Cyclone5 | Stratix5 |
53 * t_CF2ST0 | [; 600] | [; 600] | [; 600] |ns
54 * t_CFG | [2;] | [2;] | [2;] |µs
55 * t_STATUS | [268; 3000] | [268; 1506] | [268; 1506] |µs
56 * t_CF2ST1 | [; 3000] | [; 1506] | [; 1506] |µs
57 * t_CF2CK | [3010;] | [1506;] | [1506;] |µs
58 * t_ST2CK | [10;] | [2;] | [2;] |µs
59 * t_CD2UM | [175; 830] | [175; 437] | [175; 437] |µs
60 */
61static struct altera_ps_data c5_data = {
62 /* these values for Cyclone5 are compatible with Stratix5 */
63 .devtype = CYCLONE5,
64 .status_wait_min_us = 268,
65 .status_wait_max_us = 1506,
66 .t_cfg_us = 2,
67 .t_st2ck_us = 2,
68};
69
70static struct altera_ps_data a10_data = {
71 .devtype = ARRIA10,
72 .status_wait_min_us = 268, /* min(t_STATUS) */
73 .status_wait_max_us = 3000, /* max(t_CF2ST1) */
74 .t_cfg_us = 2, /* max { min(t_CFG), max(tCF2ST0) } */
75 .t_st2ck_us = 10, /* min(t_ST2CK) */
76};
77
78static const struct of_device_id of_ef_match[] = {
79 { .compatible = "altr,fpga-passive-serial", .data = &c5_data },
80 { .compatible = "altr,fpga-arria10-passive-serial", .data = &a10_data },
81 {}
82};
83MODULE_DEVICE_TABLE(of, of_ef_match);
84
85static enum fpga_mgr_states altera_ps_state(struct fpga_manager *mgr)
86{
87 struct altera_ps_conf *conf = mgr->priv;
88
89 if (gpiod_get_value_cansleep(conf->status))
90 return FPGA_MGR_STATE_RESET;
91
92 return FPGA_MGR_STATE_UNKNOWN;
93}
94
95static inline void altera_ps_delay(int delay_us)
96{
97 if (delay_us > 10)
98 usleep_range(delay_us, delay_us + 5);
99 else
100 udelay(delay_us);
101}
102
103static int altera_ps_write_init(struct fpga_manager *mgr,
104 struct fpga_image_info *info,
105 const char *buf, size_t count)
106{
107 struct altera_ps_conf *conf = mgr->priv;
108 int min, max, waits;
109 int i;
110
111 conf->info_flags = info->flags;
112
113 if (info->flags & FPGA_MGR_PARTIAL_RECONFIG) {
114 dev_err(&mgr->dev, "Partial reconfiguration not supported.\n");
115 return -EINVAL;
116 }
117
118 gpiod_set_value_cansleep(conf->config, 1);
119
120 /* wait min reset pulse time */
121 altera_ps_delay(conf->data->t_cfg_us);
122
123 if (!gpiod_get_value_cansleep(conf->status)) {
124 dev_err(&mgr->dev, "Status pin failed to show a reset\n");
125 return -EIO;
126 }
127
128 gpiod_set_value_cansleep(conf->config, 0);
129
130 min = conf->data->status_wait_min_us;
131 max = conf->data->status_wait_max_us;
132 waits = max / min;
133 if (max % min)
134 waits++;
135
136 /* wait for max { max(t_STATUS), max(t_CF2ST1) } */
137 for (i = 0; i < waits; i++) {
138 usleep_range(min, min + 10);
139 if (!gpiod_get_value_cansleep(conf->status)) {
140 /* wait for min(t_ST2CK)*/
141 altera_ps_delay(conf->data->t_st2ck_us);
142 return 0;
143 }
144 }
145
146 dev_err(&mgr->dev, "Status pin not ready.\n");
147 return -EIO;
148}
149
150static void rev_buf(char *buf, size_t len)
151{
152 const char *fw_end = (buf + len);
153
154 /* set buffer to lsb first */
155 while (buf < fw_end) {
156 *buf = bitrev8(*buf);
157 buf++;
158 }
159}
160
161static int altera_ps_write(struct fpga_manager *mgr, const char *buf,
162 size_t count)
163{
164 struct altera_ps_conf *conf = mgr->priv;
165 const char *fw_data = buf;
166 const char *fw_data_end = fw_data + count;
167
168 while (fw_data < fw_data_end) {
169 int ret;
170 size_t stride = min_t(size_t, fw_data_end - fw_data, SZ_4K);
171
172 if (!(conf->info_flags & FPGA_MGR_BITSTREAM_LSB_FIRST))
173 rev_buf((char *)fw_data, stride);
174
175 ret = spi_write(conf->spi, fw_data, stride);
176 if (ret) {
177 dev_err(&mgr->dev, "spi error in firmware write: %d\n",
178 ret);
179 return ret;
180 }
181 fw_data += stride;
182 }
183
184 return 0;
185}
186
187static int altera_ps_write_complete(struct fpga_manager *mgr,
188 struct fpga_image_info *info)
189{
190 struct altera_ps_conf *conf = mgr->priv;
191 const char dummy[] = {0};
192 int ret;
193
194 if (gpiod_get_value_cansleep(conf->status)) {
195 dev_err(&mgr->dev, "Error during configuration.\n");
196 return -EIO;
197 }
198
199 if (!IS_ERR(conf->confd)) {
200 if (!gpiod_get_raw_value_cansleep(conf->confd)) {
201 dev_err(&mgr->dev, "CONF_DONE is inactive!\n");
202 return -EIO;
203 }
204 }
205
206 /*
207 * After CONF_DONE goes high, send two additional falling edges on DCLK
208 * to begin initialization and enter user mode
209 */
210 ret = spi_write(conf->spi, dummy, 1);
211 if (ret) {
212 dev_err(&mgr->dev, "spi error during end sequence: %d\n", ret);
213 return ret;
214 }
215
216 return 0;
217}
218
219static const struct fpga_manager_ops altera_ps_ops = {
220 .state = altera_ps_state,
221 .write_init = altera_ps_write_init,
222 .write = altera_ps_write,
223 .write_complete = altera_ps_write_complete,
224};
225
226static int altera_ps_probe(struct spi_device *spi)
227{
228 struct altera_ps_conf *conf;
229 const struct of_device_id *of_id;
230
231 conf = devm_kzalloc(&spi->dev, sizeof(*conf), GFP_KERNEL);
232 if (!conf)
233 return -ENOMEM;
234
235 of_id = of_match_device(of_ef_match, &spi->dev);
236 if (!of_id)
237 return -ENODEV;
238
239 conf->data = of_id->data;
240 conf->spi = spi;
241 conf->config = devm_gpiod_get(&spi->dev, "nconfig", GPIOD_OUT_HIGH);
242 if (IS_ERR(conf->config)) {
243 dev_err(&spi->dev, "Failed to get config gpio: %ld\n",
244 PTR_ERR(conf->config));
245 return PTR_ERR(conf->config);
246 }
247
248 conf->status = devm_gpiod_get(&spi->dev, "nstat", GPIOD_IN);
249 if (IS_ERR(conf->status)) {
250 dev_err(&spi->dev, "Failed to get status gpio: %ld\n",
251 PTR_ERR(conf->status));
252 return PTR_ERR(conf->status);
253 }
254
255 conf->confd = devm_gpiod_get(&spi->dev, "confd", GPIOD_IN);
256 if (IS_ERR(conf->confd)) {
257 dev_warn(&spi->dev, "Not using confd gpio: %ld\n",
258 PTR_ERR(conf->confd));
259 }
260
261 /* Register manager with unique name */
262 snprintf(conf->mgr_name, sizeof(conf->mgr_name), "%s %s",
263 dev_driver_string(&spi->dev), dev_name(&spi->dev));
264
265 return fpga_mgr_register(&spi->dev, conf->mgr_name,
266 &altera_ps_ops, conf);
267}
268
269static int altera_ps_remove(struct spi_device *spi)
270{
271 fpga_mgr_unregister(&spi->dev);
272
273 return 0;
274}
275
276static const struct spi_device_id altera_ps_spi_ids[] = {
277 {"cyclone-ps-spi", 0},
278 {}
279};
280MODULE_DEVICE_TABLE(spi, altera_ps_spi_ids);
281
282static struct spi_driver altera_ps_driver = {
283 .driver = {
284 .name = "altera-ps-spi",
285 .owner = THIS_MODULE,
286 .of_match_table = of_match_ptr(of_ef_match),
287 },
288 .id_table = altera_ps_spi_ids,
289 .probe = altera_ps_probe,
290 .remove = altera_ps_remove,
291};
292
293module_spi_driver(altera_ps_driver)
294
295MODULE_LICENSE("GPL v2");
296MODULE_AUTHOR("Joshua Clayton <stillcompiling@gmail.com>");
297MODULE_DESCRIPTION("Module to load Altera FPGA firmware over SPI");