1 // SPDX-License-Identifier: GPL-2.0-only
3 * Xilinx Spartan6 and 7 Series Slave Serial SPI Driver
5 * Copyright (C) 2017 DENX Software Engineering
7 * Anatolij Gustschin <agust@denx.de>
9 * Manage Xilinx FPGA firmware that is loaded over SPI using
10 * the slave serial configuration interface.
13 #include <linux/delay.h>
14 #include <linux/device.h>
15 #include <linux/fpga/fpga-mgr.h>
16 #include <linux/gpio/consumer.h>
17 #include <linux/module.h>
18 #include <linux/mod_devicetable.h>
20 #include <linux/spi/spi.h>
21 #include <linux/sizes.h>
23 struct xilinx_spi_conf
{
24 struct spi_device
*spi
;
25 struct gpio_desc
*prog_b
;
26 struct gpio_desc
*init_b
;
27 struct gpio_desc
*done
;
30 static int get_done_gpio(struct fpga_manager
*mgr
)
32 struct xilinx_spi_conf
*conf
= mgr
->priv
;
35 ret
= gpiod_get_value(conf
->done
);
38 dev_err(&mgr
->dev
, "Error reading DONE (%d)\n", ret
);
43 static enum fpga_mgr_states
xilinx_spi_state(struct fpga_manager
*mgr
)
45 if (!get_done_gpio(mgr
))
46 return FPGA_MGR_STATE_RESET
;
48 return FPGA_MGR_STATE_UNKNOWN
;
52 * wait_for_init_b - wait for the INIT_B pin to have a given state, or wait
53 * a given delay if the pin is unavailable
55 * @mgr: The FPGA manager object
56 * @value: Value INIT_B to wait for (1 = asserted = low)
57 * @alt_udelay: Delay to wait if the INIT_B GPIO is not available
59 * Returns 0 when the INIT_B GPIO reached the given state or -ETIMEDOUT if
60 * too much time passed waiting for that. If no INIT_B GPIO is available
61 * then always return 0.
63 static int wait_for_init_b(struct fpga_manager
*mgr
, int value
,
64 unsigned long alt_udelay
)
66 struct xilinx_spi_conf
*conf
= mgr
->priv
;
67 unsigned long timeout
= jiffies
+ msecs_to_jiffies(1000);
70 while (time_before(jiffies
, timeout
)) {
71 int ret
= gpiod_get_value(conf
->init_b
);
77 dev_err(&mgr
->dev
, "Error reading INIT_B (%d)\n", ret
);
81 usleep_range(100, 400);
84 dev_err(&mgr
->dev
, "Timeout waiting for INIT_B to %s\n",
85 value
? "assert" : "deassert");
94 static int xilinx_spi_write_init(struct fpga_manager
*mgr
,
95 struct fpga_image_info
*info
,
96 const char *buf
, size_t count
)
98 struct xilinx_spi_conf
*conf
= mgr
->priv
;
101 if (info
->flags
& FPGA_MGR_PARTIAL_RECONFIG
) {
102 dev_err(&mgr
->dev
, "Partial reconfiguration not supported\n");
106 gpiod_set_value(conf
->prog_b
, 1);
108 err
= wait_for_init_b(mgr
, 1, 1); /* min is 500 ns */
110 gpiod_set_value(conf
->prog_b
, 0);
114 gpiod_set_value(conf
->prog_b
, 0);
116 err
= wait_for_init_b(mgr
, 0, 0);
120 if (get_done_gpio(mgr
)) {
121 dev_err(&mgr
->dev
, "Unexpected DONE pin state...\n");
125 /* program latency */
126 usleep_range(7500, 7600);
130 static int xilinx_spi_write(struct fpga_manager
*mgr
, const char *buf
,
133 struct xilinx_spi_conf
*conf
= mgr
->priv
;
134 const char *fw_data
= buf
;
135 const char *fw_data_end
= fw_data
+ count
;
137 while (fw_data
< fw_data_end
) {
138 size_t remaining
, stride
;
141 remaining
= fw_data_end
- fw_data
;
142 stride
= min_t(size_t, remaining
, SZ_4K
);
144 ret
= spi_write(conf
->spi
, fw_data
, stride
);
146 dev_err(&mgr
->dev
, "SPI error in firmware write: %d\n",
156 static int xilinx_spi_apply_cclk_cycles(struct xilinx_spi_conf
*conf
)
158 struct spi_device
*spi
= conf
->spi
;
159 const u8 din_data
[1] = { 0xff };
162 ret
= spi_write(conf
->spi
, din_data
, sizeof(din_data
));
164 dev_err(&spi
->dev
, "applying CCLK cycles failed: %d\n", ret
);
169 static int xilinx_spi_write_complete(struct fpga_manager
*mgr
,
170 struct fpga_image_info
*info
)
172 struct xilinx_spi_conf
*conf
= mgr
->priv
;
173 unsigned long timeout
= jiffies
+ usecs_to_jiffies(info
->config_complete_timeout_us
);
174 bool expired
= false;
179 * This loop is carefully written such that if the driver is
180 * scheduled out for more than 'timeout', we still check for DONE
181 * before giving up and we apply 8 extra CCLK cycles in all cases.
184 expired
= time_after(jiffies
, timeout
);
186 done
= get_done_gpio(mgr
);
190 ret
= xilinx_spi_apply_cclk_cycles(conf
);
199 ret
= gpiod_get_value(conf
->init_b
);
202 dev_err(&mgr
->dev
, "Error reading INIT_B (%d)\n", ret
);
207 ret
? "CRC error or invalid device\n"
208 : "Missing sync word or incomplete bitstream\n");
210 dev_err(&mgr
->dev
, "Timeout after config data transfer\n");
216 static const struct fpga_manager_ops xilinx_spi_ops
= {
217 .state
= xilinx_spi_state
,
218 .write_init
= xilinx_spi_write_init
,
219 .write
= xilinx_spi_write
,
220 .write_complete
= xilinx_spi_write_complete
,
223 static int xilinx_spi_probe(struct spi_device
*spi
)
225 struct xilinx_spi_conf
*conf
;
226 struct fpga_manager
*mgr
;
228 conf
= devm_kzalloc(&spi
->dev
, sizeof(*conf
), GFP_KERNEL
);
234 /* PROGRAM_B is active low */
235 conf
->prog_b
= devm_gpiod_get(&spi
->dev
, "prog_b", GPIOD_OUT_LOW
);
236 if (IS_ERR(conf
->prog_b
)) {
237 dev_err(&spi
->dev
, "Failed to get PROGRAM_B gpio: %ld\n",
238 PTR_ERR(conf
->prog_b
));
239 return PTR_ERR(conf
->prog_b
);
242 conf
->init_b
= devm_gpiod_get_optional(&spi
->dev
, "init-b", GPIOD_IN
);
243 if (IS_ERR(conf
->init_b
)) {
244 dev_err(&spi
->dev
, "Failed to get INIT_B gpio: %ld\n",
245 PTR_ERR(conf
->init_b
));
246 return PTR_ERR(conf
->init_b
);
249 conf
->done
= devm_gpiod_get(&spi
->dev
, "done", GPIOD_IN
);
250 if (IS_ERR(conf
->done
)) {
251 dev_err(&spi
->dev
, "Failed to get DONE gpio: %ld\n",
252 PTR_ERR(conf
->done
));
253 return PTR_ERR(conf
->done
);
256 mgr
= devm_fpga_mgr_create(&spi
->dev
,
257 "Xilinx Slave Serial FPGA Manager",
258 &xilinx_spi_ops
, conf
);
262 return devm_fpga_mgr_register(&spi
->dev
, mgr
);
265 static const struct of_device_id xlnx_spi_of_match
[] = {
266 { .compatible
= "xlnx,fpga-slave-serial", },
269 MODULE_DEVICE_TABLE(of
, xlnx_spi_of_match
);
271 static struct spi_driver xilinx_slave_spi_driver
= {
273 .name
= "xlnx-slave-spi",
274 .of_match_table
= of_match_ptr(xlnx_spi_of_match
),
276 .probe
= xilinx_spi_probe
,
279 module_spi_driver(xilinx_slave_spi_driver
)
281 MODULE_LICENSE("GPL v2");
282 MODULE_AUTHOR("Anatolij Gustschin <agust@denx.de>");
283 MODULE_DESCRIPTION("Load Xilinx FPGA firmware over SPI");