Commit | Line | Data |
---|---|---|
75a6faf6 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
061c97d1 | 2 | /* |
d1ddca78 | 3 | * Xilinx Spartan6 and 7 Series Slave Serial SPI Driver |
061c97d1 AG |
4 | * |
5 | * Copyright (C) 2017 DENX Software Engineering | |
6 | * | |
7 | * Anatolij Gustschin <agust@denx.de> | |
8 | * | |
061c97d1 AG |
9 | * Manage Xilinx FPGA firmware that is loaded over SPI using |
10 | * the slave serial configuration interface. | |
11 | */ | |
12 | ||
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> | |
19 | #include <linux/of.h> | |
20 | #include <linux/spi/spi.h> | |
21 | #include <linux/sizes.h> | |
22 | ||
23 | struct xilinx_spi_conf { | |
24 | struct spi_device *spi; | |
25 | struct gpio_desc *prog_b; | |
dd2784c0 | 26 | struct gpio_desc *init_b; |
061c97d1 AG |
27 | struct gpio_desc *done; |
28 | }; | |
29 | ||
eefe64fb | 30 | static int get_done_gpio(struct fpga_manager *mgr) |
061c97d1 AG |
31 | { |
32 | struct xilinx_spi_conf *conf = mgr->priv; | |
eefe64fb LC |
33 | int ret; |
34 | ||
35 | ret = gpiod_get_value(conf->done); | |
061c97d1 | 36 | |
eefe64fb LC |
37 | if (ret < 0) |
38 | dev_err(&mgr->dev, "Error reading DONE (%d)\n", ret); | |
39 | ||
40 | return ret; | |
41 | } | |
42 | ||
43 | static enum fpga_mgr_states xilinx_spi_state(struct fpga_manager *mgr) | |
44 | { | |
45 | if (!get_done_gpio(mgr)) | |
061c97d1 AG |
46 | return FPGA_MGR_STATE_RESET; |
47 | ||
48 | return FPGA_MGR_STATE_UNKNOWN; | |
49 | } | |
50 | ||
dd2784c0 LC |
51 | /** |
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 | |
54 | * | |
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 | |
58 | * | |
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. | |
62 | */ | |
63 | static int wait_for_init_b(struct fpga_manager *mgr, int value, | |
64 | unsigned long alt_udelay) | |
65 | { | |
66 | struct xilinx_spi_conf *conf = mgr->priv; | |
67 | unsigned long timeout = jiffies + msecs_to_jiffies(1000); | |
68 | ||
69 | if (conf->init_b) { | |
70 | while (time_before(jiffies, timeout)) { | |
eefe64fb LC |
71 | int ret = gpiod_get_value(conf->init_b); |
72 | ||
73 | if (ret == value) | |
dd2784c0 | 74 | return 0; |
eefe64fb LC |
75 | |
76 | if (ret < 0) { | |
77 | dev_err(&mgr->dev, "Error reading INIT_B (%d)\n", ret); | |
78 | return ret; | |
79 | } | |
80 | ||
dd2784c0 LC |
81 | usleep_range(100, 400); |
82 | } | |
eefe64fb LC |
83 | |
84 | dev_err(&mgr->dev, "Timeout waiting for INIT_B to %s\n", | |
85 | value ? "assert" : "deassert"); | |
dd2784c0 LC |
86 | return -ETIMEDOUT; |
87 | } | |
88 | ||
89 | udelay(alt_udelay); | |
90 | ||
91 | return 0; | |
92 | } | |
93 | ||
061c97d1 AG |
94 | static int xilinx_spi_write_init(struct fpga_manager *mgr, |
95 | struct fpga_image_info *info, | |
96 | const char *buf, size_t count) | |
97 | { | |
98 | struct xilinx_spi_conf *conf = mgr->priv; | |
dd2784c0 | 99 | int err; |
061c97d1 AG |
100 | |
101 | if (info->flags & FPGA_MGR_PARTIAL_RECONFIG) { | |
a44ecdc9 | 102 | dev_err(&mgr->dev, "Partial reconfiguration not supported\n"); |
061c97d1 AG |
103 | return -EINVAL; |
104 | } | |
105 | ||
106 | gpiod_set_value(conf->prog_b, 1); | |
107 | ||
dd2784c0 LC |
108 | err = wait_for_init_b(mgr, 1, 1); /* min is 500 ns */ |
109 | if (err) { | |
dd2784c0 LC |
110 | gpiod_set_value(conf->prog_b, 0); |
111 | return err; | |
112 | } | |
061c97d1 AG |
113 | |
114 | gpiod_set_value(conf->prog_b, 0); | |
115 | ||
dd2784c0 | 116 | err = wait_for_init_b(mgr, 0, 0); |
eefe64fb | 117 | if (err) |
dd2784c0 | 118 | return err; |
dd2784c0 | 119 | |
eefe64fb | 120 | if (get_done_gpio(mgr)) { |
061c97d1 AG |
121 | dev_err(&mgr->dev, "Unexpected DONE pin state...\n"); |
122 | return -EIO; | |
123 | } | |
124 | ||
125 | /* program latency */ | |
23f872b6 | 126 | usleep_range(7500, 7600); |
061c97d1 AG |
127 | return 0; |
128 | } | |
129 | ||
130 | static int xilinx_spi_write(struct fpga_manager *mgr, const char *buf, | |
131 | size_t count) | |
132 | { | |
133 | struct xilinx_spi_conf *conf = mgr->priv; | |
134 | const char *fw_data = buf; | |
135 | const char *fw_data_end = fw_data + count; | |
136 | ||
137 | while (fw_data < fw_data_end) { | |
138 | size_t remaining, stride; | |
139 | int ret; | |
140 | ||
141 | remaining = fw_data_end - fw_data; | |
142 | stride = min_t(size_t, remaining, SZ_4K); | |
143 | ||
144 | ret = spi_write(conf->spi, fw_data, stride); | |
145 | if (ret) { | |
146 | dev_err(&mgr->dev, "SPI error in firmware write: %d\n", | |
147 | ret); | |
148 | return ret; | |
149 | } | |
150 | fw_data += stride; | |
151 | } | |
152 | ||
153 | return 0; | |
154 | } | |
155 | ||
156 | static int xilinx_spi_apply_cclk_cycles(struct xilinx_spi_conf *conf) | |
157 | { | |
158 | struct spi_device *spi = conf->spi; | |
159 | const u8 din_data[1] = { 0xff }; | |
160 | int ret; | |
161 | ||
162 | ret = spi_write(conf->spi, din_data, sizeof(din_data)); | |
163 | if (ret) | |
164 | dev_err(&spi->dev, "applying CCLK cycles failed: %d\n", ret); | |
165 | ||
166 | return ret; | |
167 | } | |
168 | ||
169 | static int xilinx_spi_write_complete(struct fpga_manager *mgr, | |
170 | struct fpga_image_info *info) | |
171 | { | |
172 | struct xilinx_spi_conf *conf = mgr->priv; | |
16b7856d LC |
173 | unsigned long timeout = jiffies + usecs_to_jiffies(info->config_complete_timeout_us); |
174 | bool expired = false; | |
175 | int done; | |
061c97d1 AG |
176 | int ret; |
177 | ||
16b7856d LC |
178 | /* |
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. | |
182 | */ | |
183 | while (!expired) { | |
184 | expired = time_after(jiffies, timeout); | |
061c97d1 | 185 | |
16b7856d LC |
186 | done = get_done_gpio(mgr); |
187 | if (done < 0) | |
188 | return done; | |
061c97d1 AG |
189 | |
190 | ret = xilinx_spi_apply_cclk_cycles(conf); | |
191 | if (ret) | |
192 | return ret; | |
193 | ||
16b7856d LC |
194 | if (done) |
195 | return 0; | |
061c97d1 AG |
196 | } |
197 | ||
4e772ab8 LC |
198 | if (conf->init_b) { |
199 | ret = gpiod_get_value(conf->init_b); | |
200 | ||
201 | if (ret < 0) { | |
202 | dev_err(&mgr->dev, "Error reading INIT_B (%d)\n", ret); | |
203 | return ret; | |
204 | } | |
205 | ||
206 | dev_err(&mgr->dev, | |
207 | ret ? "CRC error or invalid device\n" | |
208 | : "Missing sync word or incomplete bitstream\n"); | |
209 | } else { | |
210 | dev_err(&mgr->dev, "Timeout after config data transfer\n"); | |
211 | } | |
212 | ||
061c97d1 AG |
213 | return -ETIMEDOUT; |
214 | } | |
215 | ||
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, | |
221 | }; | |
222 | ||
223 | static int xilinx_spi_probe(struct spi_device *spi) | |
224 | { | |
225 | struct xilinx_spi_conf *conf; | |
7085e2a9 | 226 | struct fpga_manager *mgr; |
061c97d1 AG |
227 | |
228 | conf = devm_kzalloc(&spi->dev, sizeof(*conf), GFP_KERNEL); | |
229 | if (!conf) | |
230 | return -ENOMEM; | |
231 | ||
232 | conf->spi = spi; | |
233 | ||
234 | /* PROGRAM_B is active low */ | |
235 | conf->prog_b = devm_gpiod_get(&spi->dev, "prog_b", GPIOD_OUT_LOW); | |
ce453ee6 LC |
236 | if (IS_ERR(conf->prog_b)) |
237 | return dev_err_probe(&spi->dev, PTR_ERR(conf->prog_b), | |
238 | "Failed to get PROGRAM_B gpio\n"); | |
061c97d1 | 239 | |
dd2784c0 | 240 | conf->init_b = devm_gpiod_get_optional(&spi->dev, "init-b", GPIOD_IN); |
ce453ee6 LC |
241 | if (IS_ERR(conf->init_b)) |
242 | return dev_err_probe(&spi->dev, PTR_ERR(conf->init_b), | |
243 | "Failed to get INIT_B gpio\n"); | |
dd2784c0 | 244 | |
061c97d1 | 245 | conf->done = devm_gpiod_get(&spi->dev, "done", GPIOD_IN); |
ce453ee6 LC |
246 | if (IS_ERR(conf->done)) |
247 | return dev_err_probe(&spi->dev, PTR_ERR(conf->done), | |
248 | "Failed to get DONE gpio\n"); | |
061c97d1 | 249 | |
4ba0b2c2 RW |
250 | mgr = devm_fpga_mgr_register(&spi->dev, |
251 | "Xilinx Slave Serial FPGA Manager", | |
252 | &xilinx_spi_ops, conf); | |
253 | return PTR_ERR_OR_ZERO(mgr); | |
061c97d1 AG |
254 | } |
255 | ||
1aa3fc69 | 256 | #ifdef CONFIG_OF |
061c97d1 AG |
257 | static const struct of_device_id xlnx_spi_of_match[] = { |
258 | { .compatible = "xlnx,fpga-slave-serial", }, | |
259 | {} | |
260 | }; | |
261 | MODULE_DEVICE_TABLE(of, xlnx_spi_of_match); | |
1aa3fc69 | 262 | #endif |
061c97d1 AG |
263 | |
264 | static struct spi_driver xilinx_slave_spi_driver = { | |
265 | .driver = { | |
266 | .name = "xlnx-slave-spi", | |
267 | .of_match_table = of_match_ptr(xlnx_spi_of_match), | |
268 | }, | |
269 | .probe = xilinx_spi_probe, | |
061c97d1 AG |
270 | }; |
271 | ||
272 | module_spi_driver(xilinx_slave_spi_driver) | |
273 | ||
274 | MODULE_LICENSE("GPL v2"); | |
275 | MODULE_AUTHOR("Anatolij Gustschin <agust@denx.de>"); | |
276 | MODULE_DESCRIPTION("Load Xilinx FPGA firmware over SPI"); |