aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/fpga/ice40-spi.c
diff options
context:
space:
mode:
authorJoel Holdsworth <joel@airwebreathe.org.uk>2017-02-27 17:14:26 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2017-03-17 02:10:48 -0400
commit21f8ba2ef378d906374a26f7abc05fb92219e9c6 (patch)
treef2acb4c44b5b0ca266d14194bd8e522d6d1b8295 /drivers/fpga/ice40-spi.c
parente10c11d2739fd3657d8d1d5b1fa06f9185b4f012 (diff)
fpga: Add support for Lattice iCE40 FPGAs
This patch adds support to the FPGA manager for configuring the SRAM of iCE40LM, iCE40LP, iCE40HX, iCE40 Ultra, iCE40 UltraLite and iCE40 UltraPlus devices, through slave SPI. Signed-off-by: Joel Holdsworth <joel@airwebreathe.org.uk> Reviewed-by: Marek Vasut <marex@denx.de> Reviewed-by: Moritz Fischer <moritz.fischer@ettus.com> Acked-by: Alan Tull <atull@opensource.altera.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/fpga/ice40-spi.c')
-rw-r--r--drivers/fpga/ice40-spi.c207
1 files changed, 207 insertions, 0 deletions
diff --git a/drivers/fpga/ice40-spi.c b/drivers/fpga/ice40-spi.c
new file mode 100644
index 000000000000..7fca82023062
--- /dev/null
+++ b/drivers/fpga/ice40-spi.c
@@ -0,0 +1,207 @@
1/*
2 * FPGA Manager Driver for Lattice iCE40.
3 *
4 * Copyright (c) 2016 Joel Holdsworth
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License.
9 *
10 * This driver adds support to the FPGA manager for configuring the SRAM of
11 * Lattice iCE40 FPGAs through slave SPI.
12 */
13
14#include <linux/fpga/fpga-mgr.h>
15#include <linux/gpio/consumer.h>
16#include <linux/module.h>
17#include <linux/of_gpio.h>
18#include <linux/spi/spi.h>
19#include <linux/stringify.h>
20
21#define ICE40_SPI_MAX_SPEED 25000000 /* Hz */
22#define ICE40_SPI_MIN_SPEED 1000000 /* Hz */
23
24#define ICE40_SPI_RESET_DELAY 1 /* us (>200ns) */
25#define ICE40_SPI_HOUSEKEEPING_DELAY 1200 /* us */
26
27#define ICE40_SPI_NUM_ACTIVATION_BYTES DIV_ROUND_UP(49, 8)
28
29struct ice40_fpga_priv {
30 struct spi_device *dev;
31 struct gpio_desc *reset;
32 struct gpio_desc *cdone;
33};
34
35static enum fpga_mgr_states ice40_fpga_ops_state(struct fpga_manager *mgr)
36{
37 struct ice40_fpga_priv *priv = mgr->priv;
38
39 return gpiod_get_value(priv->cdone) ? FPGA_MGR_STATE_OPERATING :
40 FPGA_MGR_STATE_UNKNOWN;
41}
42
43static int ice40_fpga_ops_write_init(struct fpga_manager *mgr,
44 struct fpga_image_info *info,
45 const char *buf, size_t count)
46{
47 struct ice40_fpga_priv *priv = mgr->priv;
48 struct spi_device *dev = priv->dev;
49 struct spi_message message;
50 struct spi_transfer assert_cs_then_reset_delay = {
51 .cs_change = 1,
52 .delay_usecs = ICE40_SPI_RESET_DELAY
53 };
54 struct spi_transfer housekeeping_delay_then_release_cs = {
55 .delay_usecs = ICE40_SPI_HOUSEKEEPING_DELAY
56 };
57 int ret;
58
59 if ((info->flags & FPGA_MGR_PARTIAL_RECONFIG)) {
60 dev_err(&dev->dev,
61 "Partial reconfiguration is not supported\n");
62 return -ENOTSUPP;
63 }
64
65 /* Lock the bus, assert CRESET_B and SS_B and delay >200ns */
66 spi_bus_lock(dev->master);
67
68 gpiod_set_value(priv->reset, 1);
69
70 spi_message_init(&message);
71 spi_message_add_tail(&assert_cs_then_reset_delay, &message);
72 ret = spi_sync_locked(dev, &message);
73
74 /* Come out of reset */
75 gpiod_set_value(priv->reset, 0);
76
77 /* Abort if the chip-select failed */
78 if (ret)
79 goto fail;
80
81 /* Check CDONE is de-asserted i.e. the FPGA is reset */
82 if (gpiod_get_value(priv->cdone)) {
83 dev_err(&dev->dev, "Device reset failed, CDONE is asserted\n");
84 ret = -EIO;
85 goto fail;
86 }
87
88 /* Wait for the housekeeping to complete, and release SS_B */
89 spi_message_init(&message);
90 spi_message_add_tail(&housekeeping_delay_then_release_cs, &message);
91 ret = spi_sync_locked(dev, &message);
92
93fail:
94 spi_bus_unlock(dev->master);
95
96 return ret;
97}
98
99static int ice40_fpga_ops_write(struct fpga_manager *mgr,
100 const char *buf, size_t count)
101{
102 struct ice40_fpga_priv *priv = mgr->priv;
103
104 return spi_write(priv->dev, buf, count);
105}
106
107static int ice40_fpga_ops_write_complete(struct fpga_manager *mgr,
108 struct fpga_image_info *info)
109{
110 struct ice40_fpga_priv *priv = mgr->priv;
111 struct spi_device *dev = priv->dev;
112 const u8 padding[ICE40_SPI_NUM_ACTIVATION_BYTES] = {0};
113
114 /* Check CDONE is asserted */
115 if (!gpiod_get_value(priv->cdone)) {
116 dev_err(&dev->dev,
117 "CDONE was not asserted after firmware transfer\n");
118 return -EIO;
119 }
120
121 /* Send of zero-padding to activate the firmware */
122 return spi_write(dev, padding, sizeof(padding));
123}
124
125static const struct fpga_manager_ops ice40_fpga_ops = {
126 .state = ice40_fpga_ops_state,
127 .write_init = ice40_fpga_ops_write_init,
128 .write = ice40_fpga_ops_write,
129 .write_complete = ice40_fpga_ops_write_complete,
130};
131
132static int ice40_fpga_probe(struct spi_device *spi)
133{
134 struct device *dev = &spi->dev;
135 struct ice40_fpga_priv *priv;
136 int ret;
137
138 priv = devm_kzalloc(&spi->dev, sizeof(*priv), GFP_KERNEL);
139 if (!priv)
140 return -ENOMEM;
141
142 priv->dev = spi;
143
144 /* Check board setup data. */
145 if (spi->max_speed_hz > ICE40_SPI_MAX_SPEED) {
146 dev_err(dev, "SPI speed is too high, maximum speed is "
147 __stringify(ICE40_SPI_MAX_SPEED) "\n");
148 return -EINVAL;
149 }
150
151 if (spi->max_speed_hz < ICE40_SPI_MIN_SPEED) {
152 dev_err(dev, "SPI speed is too low, minimum speed is "
153 __stringify(ICE40_SPI_MIN_SPEED) "\n");
154 return -EINVAL;
155 }
156
157 if (spi->mode & SPI_CPHA) {
158 dev_err(dev, "Bad SPI mode, CPHA not supported\n");
159 return -EINVAL;
160 }
161
162 /* Set up the GPIOs */
163 priv->cdone = devm_gpiod_get(dev, "cdone", GPIOD_IN);
164 if (IS_ERR(priv->cdone)) {
165 ret = PTR_ERR(priv->cdone);
166 dev_err(dev, "Failed to get CDONE GPIO: %d\n", ret);
167 return ret;
168 }
169
170 priv->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
171 if (IS_ERR(priv->reset)) {
172 ret = PTR_ERR(priv->reset);
173 dev_err(dev, "Failed to get CRESET_B GPIO: %d\n", ret);
174 return ret;
175 }
176
177 /* Register with the FPGA manager */
178 return fpga_mgr_register(dev, "Lattice iCE40 FPGA Manager",
179 &ice40_fpga_ops, priv);
180}
181
182static int ice40_fpga_remove(struct spi_device *spi)
183{
184 fpga_mgr_unregister(&spi->dev);
185 return 0;
186}
187
188static const struct of_device_id ice40_fpga_of_match[] = {
189 { .compatible = "lattice,ice40-fpga-mgr", },
190 {},
191};
192MODULE_DEVICE_TABLE(of, ice40_fpga_of_match);
193
194static struct spi_driver ice40_fpga_driver = {
195 .probe = ice40_fpga_probe,
196 .remove = ice40_fpga_remove,
197 .driver = {
198 .name = "ice40spi",
199 .of_match_table = of_match_ptr(ice40_fpga_of_match),
200 },
201};
202
203module_spi_driver(ice40_fpga_driver);
204
205MODULE_AUTHOR("Joel Holdsworth <joel@airwebreathe.org.uk>");
206MODULE_DESCRIPTION("Lattice iCE40 FPGA Manager");
207MODULE_LICENSE("GPL v2");