diff options
author | Paolo Pisati <p.pisati@gmail.com> | 2018-04-16 23:43:36 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2018-04-23 07:33:01 -0400 |
commit | 88fb3a0023307356a05f6b8e61a0ccddc6d32b2c (patch) | |
tree | c8c69853771b1fcb9a11b055c6b90d8c2ee4d06e /drivers/fpga/machxo2-spi.c | |
parent | d549ac081c41d1925804bf73393aca8e3978b582 (diff) |
fpga: lattice machxo2: Add Lattice MachXO2 support
This patch adds support to the FPGA manager for programming
MachXO2 device’s internal flash memory, via slave SPI.
Signed-off-by: Paolo Pisati <p.pisati@gmail.com>
[atull@kernel.org: use existing FPGA mgr API]
Signed-off-by: Alan Tull <atull@kernel.org>
Signed-off-by: Moritz Fischer <mdf@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/fpga/machxo2-spi.c')
-rw-r--r-- | drivers/fpga/machxo2-spi.c | 403 |
1 files changed, 403 insertions, 0 deletions
diff --git a/drivers/fpga/machxo2-spi.c b/drivers/fpga/machxo2-spi.c new file mode 100644 index 000000000000..8e95ec9c5c9a --- /dev/null +++ b/drivers/fpga/machxo2-spi.c | |||
@@ -0,0 +1,403 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * Lattice MachXO2 Slave SPI Driver | ||
4 | * | ||
5 | * Manage Lattice FPGA firmware that is loaded over SPI using | ||
6 | * the slave serial configuration interface. | ||
7 | * | ||
8 | * Copyright (C) 2018 Paolo Pisati <p.pisati@gmail.com> | ||
9 | */ | ||
10 | |||
11 | #include <linux/delay.h> | ||
12 | #include <linux/fpga/fpga-mgr.h> | ||
13 | #include <linux/gpio/consumer.h> | ||
14 | #include <linux/module.h> | ||
15 | #include <linux/of.h> | ||
16 | #include <linux/spi/spi.h> | ||
17 | |||
18 | /* MachXO2 Programming Guide - sysCONFIG Programming Commands */ | ||
19 | #define IDCODE_PUB {0xe0, 0x00, 0x00, 0x00} | ||
20 | #define ISC_ENABLE {0xc6, 0x08, 0x00, 0x00} | ||
21 | #define ISC_ERASE {0x0e, 0x04, 0x00, 0x00} | ||
22 | #define ISC_PROGRAMDONE {0x5e, 0x00, 0x00, 0x00} | ||
23 | #define LSC_INITADDRESS {0x46, 0x00, 0x00, 0x00} | ||
24 | #define LSC_PROGINCRNV {0x70, 0x00, 0x00, 0x01} | ||
25 | #define LSC_READ_STATUS {0x3c, 0x00, 0x00, 0x00} | ||
26 | #define LSC_REFRESH {0x79, 0x00, 0x00, 0x00} | ||
27 | |||
28 | /* | ||
29 | * Max CCLK in Slave SPI mode according to 'MachXO2 Family Data | ||
30 | * Sheet' sysCONFIG Port Timing Specifications (3-36) | ||
31 | */ | ||
32 | #define MACHXO2_MAX_SPEED 66000000 | ||
33 | |||
34 | #define MACHXO2_LOW_DELAY_USEC 5 | ||
35 | #define MACHXO2_HIGH_DELAY_USEC 200 | ||
36 | #define MACHXO2_REFRESH_USEC 4800 | ||
37 | #define MACHXO2_MAX_BUSY_LOOP 128 | ||
38 | #define MACHXO2_MAX_REFRESH_LOOP 16 | ||
39 | |||
40 | #define MACHXO2_PAGE_SIZE 16 | ||
41 | #define MACHXO2_BUF_SIZE (MACHXO2_PAGE_SIZE + 4) | ||
42 | |||
43 | /* Status register bits, errors and error mask */ | ||
44 | #define BUSY 12 | ||
45 | #define DONE 8 | ||
46 | #define DVER 27 | ||
47 | #define ENAB 9 | ||
48 | #define ERRBITS 23 | ||
49 | #define ERRMASK 7 | ||
50 | #define FAIL 13 | ||
51 | |||
52 | #define ENOERR 0 /* no error */ | ||
53 | #define EID 1 | ||
54 | #define ECMD 2 | ||
55 | #define ECRC 3 | ||
56 | #define EPREAM 4 /* preamble error */ | ||
57 | #define EABRT 5 /* abort error */ | ||
58 | #define EOVERFL 6 /* overflow error */ | ||
59 | #define ESDMEOF 7 /* SDM EOF */ | ||
60 | |||
61 | static inline u8 get_err(unsigned long *status) | ||
62 | { | ||
63 | return (*status >> ERRBITS) & ERRMASK; | ||
64 | } | ||
65 | |||
66 | static int get_status(struct spi_device *spi, unsigned long *status) | ||
67 | { | ||
68 | struct spi_message msg; | ||
69 | struct spi_transfer rx, tx; | ||
70 | static const u8 cmd[] = LSC_READ_STATUS; | ||
71 | int ret; | ||
72 | |||
73 | memset(&rx, 0, sizeof(rx)); | ||
74 | memset(&tx, 0, sizeof(tx)); | ||
75 | tx.tx_buf = cmd; | ||
76 | tx.len = sizeof(cmd); | ||
77 | rx.rx_buf = status; | ||
78 | rx.len = 4; | ||
79 | spi_message_init(&msg); | ||
80 | spi_message_add_tail(&tx, &msg); | ||
81 | spi_message_add_tail(&rx, &msg); | ||
82 | ret = spi_sync(spi, &msg); | ||
83 | if (ret) | ||
84 | return ret; | ||
85 | |||
86 | *status = be32_to_cpu(*status); | ||
87 | |||
88 | return 0; | ||
89 | } | ||
90 | |||
91 | #ifdef DEBUG | ||
92 | static const char *get_err_string(u8 err) | ||
93 | { | ||
94 | switch (err) { | ||
95 | case ENOERR: return "No Error"; | ||
96 | case EID: return "ID ERR"; | ||
97 | case ECMD: return "CMD ERR"; | ||
98 | case ECRC: return "CRC ERR"; | ||
99 | case EPREAM: return "Preamble ERR"; | ||
100 | case EABRT: return "Abort ERR"; | ||
101 | case EOVERFL: return "Overflow ERR"; | ||
102 | case ESDMEOF: return "SDM EOF"; | ||
103 | } | ||
104 | |||
105 | return "Default switch case"; | ||
106 | } | ||
107 | #endif | ||
108 | |||
109 | static void dump_status_reg(unsigned long *status) | ||
110 | { | ||
111 | #ifdef DEBUG | ||
112 | pr_debug("machxo2 status: 0x%08lX - done=%d, cfgena=%d, busy=%d, fail=%d, devver=%d, err=%s\n", | ||
113 | *status, test_bit(DONE, status), test_bit(ENAB, status), | ||
114 | test_bit(BUSY, status), test_bit(FAIL, status), | ||
115 | test_bit(DVER, status), get_err_string(get_err(status))); | ||
116 | #endif | ||
117 | } | ||
118 | |||
119 | static int wait_until_not_busy(struct spi_device *spi) | ||
120 | { | ||
121 | unsigned long status; | ||
122 | int ret, loop = 0; | ||
123 | |||
124 | do { | ||
125 | ret = get_status(spi, &status); | ||
126 | if (ret) | ||
127 | return ret; | ||
128 | if (++loop >= MACHXO2_MAX_BUSY_LOOP) | ||
129 | return -EBUSY; | ||
130 | } while (test_bit(BUSY, &status)); | ||
131 | |||
132 | return 0; | ||
133 | } | ||
134 | |||
135 | static int machxo2_cleanup(struct fpga_manager *mgr) | ||
136 | { | ||
137 | struct spi_device *spi = mgr->priv; | ||
138 | struct spi_message msg; | ||
139 | struct spi_transfer tx[2]; | ||
140 | static const u8 erase[] = ISC_ERASE; | ||
141 | static const u8 refresh[] = LSC_REFRESH; | ||
142 | int ret; | ||
143 | |||
144 | memset(tx, 0, sizeof(tx)); | ||
145 | spi_message_init(&msg); | ||
146 | tx[0].tx_buf = &erase; | ||
147 | tx[0].len = sizeof(erase); | ||
148 | spi_message_add_tail(&tx[0], &msg); | ||
149 | ret = spi_sync(spi, &msg); | ||
150 | if (ret) | ||
151 | goto fail; | ||
152 | |||
153 | ret = wait_until_not_busy(spi); | ||
154 | if (ret) | ||
155 | goto fail; | ||
156 | |||
157 | spi_message_init(&msg); | ||
158 | tx[1].tx_buf = &refresh; | ||
159 | tx[1].len = sizeof(refresh); | ||
160 | tx[1].delay_usecs = MACHXO2_REFRESH_USEC; | ||
161 | spi_message_add_tail(&tx[1], &msg); | ||
162 | ret = spi_sync(spi, &msg); | ||
163 | if (ret) | ||
164 | goto fail; | ||
165 | |||
166 | return 0; | ||
167 | fail: | ||
168 | dev_err(&mgr->dev, "Cleanup failed\n"); | ||
169 | |||
170 | return ret; | ||
171 | } | ||
172 | |||
173 | static enum fpga_mgr_states machxo2_spi_state(struct fpga_manager *mgr) | ||
174 | { | ||
175 | struct spi_device *spi = mgr->priv; | ||
176 | unsigned long status; | ||
177 | |||
178 | get_status(spi, &status); | ||
179 | if (!test_bit(BUSY, &status) && test_bit(DONE, &status) && | ||
180 | get_err(&status) == ENOERR) | ||
181 | return FPGA_MGR_STATE_OPERATING; | ||
182 | |||
183 | return FPGA_MGR_STATE_UNKNOWN; | ||
184 | } | ||
185 | |||
186 | static int machxo2_write_init(struct fpga_manager *mgr, | ||
187 | struct fpga_image_info *info, | ||
188 | const char *buf, size_t count) | ||
189 | { | ||
190 | struct spi_device *spi = mgr->priv; | ||
191 | struct spi_message msg; | ||
192 | struct spi_transfer tx[3]; | ||
193 | static const u8 enable[] = ISC_ENABLE; | ||
194 | static const u8 erase[] = ISC_ERASE; | ||
195 | static const u8 initaddr[] = LSC_INITADDRESS; | ||
196 | unsigned long status; | ||
197 | int ret; | ||
198 | |||
199 | if ((info->flags & FPGA_MGR_PARTIAL_RECONFIG)) { | ||
200 | dev_err(&mgr->dev, | ||
201 | "Partial reconfiguration is not supported\n"); | ||
202 | return -ENOTSUPP; | ||
203 | } | ||
204 | |||
205 | get_status(spi, &status); | ||
206 | dump_status_reg(&status); | ||
207 | memset(tx, 0, sizeof(tx)); | ||
208 | spi_message_init(&msg); | ||
209 | tx[0].tx_buf = &enable; | ||
210 | tx[0].len = sizeof(enable); | ||
211 | tx[0].delay_usecs = MACHXO2_LOW_DELAY_USEC; | ||
212 | spi_message_add_tail(&tx[0], &msg); | ||
213 | |||
214 | tx[1].tx_buf = &erase; | ||
215 | tx[1].len = sizeof(erase); | ||
216 | spi_message_add_tail(&tx[1], &msg); | ||
217 | ret = spi_sync(spi, &msg); | ||
218 | if (ret) | ||
219 | goto fail; | ||
220 | |||
221 | ret = wait_until_not_busy(spi); | ||
222 | if (ret) | ||
223 | goto fail; | ||
224 | |||
225 | get_status(spi, &status); | ||
226 | if (test_bit(FAIL, &status)) | ||
227 | goto fail; | ||
228 | dump_status_reg(&status); | ||
229 | |||
230 | spi_message_init(&msg); | ||
231 | tx[2].tx_buf = &initaddr; | ||
232 | tx[2].len = sizeof(initaddr); | ||
233 | spi_message_add_tail(&tx[2], &msg); | ||
234 | ret = spi_sync(spi, &msg); | ||
235 | if (ret) | ||
236 | goto fail; | ||
237 | |||
238 | get_status(spi, &status); | ||
239 | dump_status_reg(&status); | ||
240 | |||
241 | return 0; | ||
242 | fail: | ||
243 | dev_err(&mgr->dev, "Error during FPGA init.\n"); | ||
244 | |||
245 | return ret; | ||
246 | } | ||
247 | |||
248 | static int machxo2_write(struct fpga_manager *mgr, const char *buf, | ||
249 | size_t count) | ||
250 | { | ||
251 | struct spi_device *spi = mgr->priv; | ||
252 | struct spi_message msg; | ||
253 | struct spi_transfer tx; | ||
254 | static const u8 progincr[] = LSC_PROGINCRNV; | ||
255 | u8 payload[MACHXO2_BUF_SIZE]; | ||
256 | unsigned long status; | ||
257 | int i, ret; | ||
258 | |||
259 | if (count % MACHXO2_PAGE_SIZE != 0) { | ||
260 | dev_err(&mgr->dev, "Malformed payload.\n"); | ||
261 | return -EINVAL; | ||
262 | } | ||
263 | get_status(spi, &status); | ||
264 | dump_status_reg(&status); | ||
265 | memcpy(payload, &progincr, sizeof(progincr)); | ||
266 | for (i = 0; i < count; i += MACHXO2_PAGE_SIZE) { | ||
267 | memcpy(&payload[sizeof(progincr)], &buf[i], MACHXO2_PAGE_SIZE); | ||
268 | memset(&tx, 0, sizeof(tx)); | ||
269 | spi_message_init(&msg); | ||
270 | tx.tx_buf = payload; | ||
271 | tx.len = MACHXO2_BUF_SIZE; | ||
272 | tx.delay_usecs = MACHXO2_HIGH_DELAY_USEC; | ||
273 | spi_message_add_tail(&tx, &msg); | ||
274 | ret = spi_sync(spi, &msg); | ||
275 | if (ret) { | ||
276 | dev_err(&mgr->dev, "Error loading the bitstream.\n"); | ||
277 | return ret; | ||
278 | } | ||
279 | } | ||
280 | get_status(spi, &status); | ||
281 | dump_status_reg(&status); | ||
282 | |||
283 | return 0; | ||
284 | } | ||
285 | |||
286 | static int machxo2_write_complete(struct fpga_manager *mgr, | ||
287 | struct fpga_image_info *info) | ||
288 | { | ||
289 | struct spi_device *spi = mgr->priv; | ||
290 | struct spi_message msg; | ||
291 | struct spi_transfer tx[2]; | ||
292 | static const u8 progdone[] = ISC_PROGRAMDONE; | ||
293 | static const u8 refresh[] = LSC_REFRESH; | ||
294 | unsigned long status; | ||
295 | int ret, refreshloop = 0; | ||
296 | |||
297 | memset(tx, 0, sizeof(tx)); | ||
298 | spi_message_init(&msg); | ||
299 | tx[0].tx_buf = &progdone; | ||
300 | tx[0].len = sizeof(progdone); | ||
301 | spi_message_add_tail(&tx[0], &msg); | ||
302 | ret = spi_sync(spi, &msg); | ||
303 | if (ret) | ||
304 | goto fail; | ||
305 | ret = wait_until_not_busy(spi); | ||
306 | if (ret) | ||
307 | goto fail; | ||
308 | |||
309 | get_status(spi, &status); | ||
310 | dump_status_reg(&status); | ||
311 | if (!test_bit(DONE, &status)) { | ||
312 | machxo2_cleanup(mgr); | ||
313 | goto fail; | ||
314 | } | ||
315 | |||
316 | do { | ||
317 | spi_message_init(&msg); | ||
318 | tx[1].tx_buf = &refresh; | ||
319 | tx[1].len = sizeof(refresh); | ||
320 | tx[1].delay_usecs = MACHXO2_REFRESH_USEC; | ||
321 | spi_message_add_tail(&tx[1], &msg); | ||
322 | ret = spi_sync(spi, &msg); | ||
323 | if (ret) | ||
324 | goto fail; | ||
325 | |||
326 | /* check refresh status */ | ||
327 | get_status(spi, &status); | ||
328 | dump_status_reg(&status); | ||
329 | if (!test_bit(BUSY, &status) && test_bit(DONE, &status) && | ||
330 | get_err(&status) == ENOERR) | ||
331 | break; | ||
332 | if (++refreshloop == MACHXO2_MAX_REFRESH_LOOP) { | ||
333 | machxo2_cleanup(mgr); | ||
334 | goto fail; | ||
335 | } | ||
336 | } while (1); | ||
337 | |||
338 | get_status(spi, &status); | ||
339 | dump_status_reg(&status); | ||
340 | |||
341 | return 0; | ||
342 | fail: | ||
343 | dev_err(&mgr->dev, "Refresh failed.\n"); | ||
344 | |||
345 | return ret; | ||
346 | } | ||
347 | |||
348 | static const struct fpga_manager_ops machxo2_ops = { | ||
349 | .state = machxo2_spi_state, | ||
350 | .write_init = machxo2_write_init, | ||
351 | .write = machxo2_write, | ||
352 | .write_complete = machxo2_write_complete, | ||
353 | }; | ||
354 | |||
355 | static int machxo2_spi_probe(struct spi_device *spi) | ||
356 | { | ||
357 | struct device *dev = &spi->dev; | ||
358 | |||
359 | if (spi->max_speed_hz > MACHXO2_MAX_SPEED) { | ||
360 | dev_err(dev, "Speed is too high\n"); | ||
361 | return -EINVAL; | ||
362 | } | ||
363 | |||
364 | return fpga_mgr_register(dev, "Lattice MachXO2 SPI FPGA Manager", | ||
365 | &machxo2_ops, spi); | ||
366 | } | ||
367 | |||
368 | static int machxo2_spi_remove(struct spi_device *spi) | ||
369 | { | ||
370 | struct device *dev = &spi->dev; | ||
371 | |||
372 | fpga_mgr_unregister(dev); | ||
373 | |||
374 | return 0; | ||
375 | } | ||
376 | |||
377 | static const struct of_device_id of_match[] = { | ||
378 | { .compatible = "lattice,machxo2-slave-spi", }, | ||
379 | {} | ||
380 | }; | ||
381 | MODULE_DEVICE_TABLE(of, of_match); | ||
382 | |||
383 | static const struct spi_device_id lattice_ids[] = { | ||
384 | { "machxo2-slave-spi", 0 }, | ||
385 | { }, | ||
386 | }; | ||
387 | MODULE_DEVICE_TABLE(spi, lattice_ids); | ||
388 | |||
389 | static struct spi_driver machxo2_spi_driver = { | ||
390 | .driver = { | ||
391 | .name = "machxo2-slave-spi", | ||
392 | .of_match_table = of_match_ptr(of_match), | ||
393 | }, | ||
394 | .probe = machxo2_spi_probe, | ||
395 | .remove = machxo2_spi_remove, | ||
396 | .id_table = lattice_ids, | ||
397 | }; | ||
398 | |||
399 | module_spi_driver(machxo2_spi_driver) | ||
400 | |||
401 | MODULE_AUTHOR("Paolo Pisati <p.pisati@gmail.com>"); | ||
402 | MODULE_DESCRIPTION("Load Lattice FPGA firmware over SPI"); | ||
403 | MODULE_LICENSE("GPL v2"); | ||