1 /*
2  * Copyright (c) 2022 Meta
3  * Copyright (c) 2024 SILA Embedded Solutions GmbH
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #include <zephyr/device.h>
9 #include <zephyr/drivers/fpga.h>
10 #include <zephyr/drivers/gpio.h>
11 #ifdef CONFIG_PINCTRL
12 #include <zephyr/drivers/pinctrl.h>
13 #endif /* CONFIG_PINCTRL */
14 #include <zephyr/kernel.h>
15 #include <zephyr/logging/log.h>
16 #include <zephyr/sys/crc.h>
17 #include <zephyr/sys/util.h>
18 
19 #include "fpga_ice40_common.h"
20 
21 /*
22  * Note: When loading a bitstream, the iCE40 has a 'quirk' in that the CS
23  * polarity must be inverted during the 'leading clocks' phase and
24  * 'trailing clocks' phase. While the bitstream is being transmitted, the
25  * CS polarity is normal (active low). Zephyr's SPI driver model currently
26  * does not handle these types of quirks (in contrast to e.g. Linux).
27  *
28  * The logical alternative would be to put the CS into GPIO mode, perform 3
29  * separate SPI transfers (inverting CS polarity as necessary) and then
30  * restore the default pinctrl settings. On some higher-end microcontrollers
31  * and microprocessors, it's possible to do that without breaking the iCE40
32  * timing requirements.
33  *
34  * However, on lower-end microcontrollers, the amount of time that elapses
35  * between SPI transfers does break the iCE40 timing requirements. That
36  * leaves us with the bitbanging option. Of course, on lower-end
37  * microcontrollers, the amount of time required to execute something
38  * like gpio_pin_configure_dt() dwarfs the 2*500 nanoseconds needed to
39  * achieve the minimum 1 MHz clock rate for loading the iCE40 bistream. So
40  * in order to bitbang on lower-end microcontrollers, we actually require
41  * direct register access to the set and clear registers.
42  */
43 
44 LOG_MODULE_DECLARE(fpga_ice40);
45 
46 struct fpga_ice40_config_bitbang {
47 	struct gpio_dt_spec clk;
48 	struct gpio_dt_spec pico;
49 	volatile gpio_port_pins_t *set;
50 	volatile gpio_port_pins_t *clear;
51 	uint16_t mhz_delay_count;
52 	const struct pinctrl_dev_config *pincfg;
53 };
54 
55 /*
56  * This is a calibrated delay loop used to achieve a 1 MHz SPI_CLK frequency
57  * with the GPIO bitbang mode. It is used both in fpga_ice40_send_clocks()
58  * and fpga_ice40_spi_send_data().
59  *
60  * Calibration is achieved via the mhz_delay_count device tree parameter. See
61  * lattice,ice40-fpga.yaml for details.
62  */
fpga_ice40_delay(size_t n)63 static inline void fpga_ice40_delay(size_t n)
64 {
65 	for (; n > 0; --n) {
66 		__asm__ __volatile__("");
67 	}
68 }
69 
fpga_ice40_send_clocks(size_t delay,volatile gpio_port_pins_t * set,volatile gpio_port_pins_t * clear,gpio_port_pins_t clk,size_t n)70 static void fpga_ice40_send_clocks(size_t delay, volatile gpio_port_pins_t *set,
71 				   volatile gpio_port_pins_t *clear, gpio_port_pins_t clk, size_t n)
72 {
73 	for (; n > 0; --n) {
74 		*clear |= clk;
75 		fpga_ice40_delay(delay);
76 		*set |= clk;
77 		fpga_ice40_delay(delay);
78 	}
79 }
80 
fpga_ice40_spi_send_data(size_t delay,volatile gpio_port_pins_t * set,volatile gpio_port_pins_t * clear,gpio_port_pins_t cs,gpio_port_pins_t clk,gpio_port_pins_t pico,uint8_t * z,size_t n)81 static void fpga_ice40_spi_send_data(size_t delay, volatile gpio_port_pins_t *set,
82 				     volatile gpio_port_pins_t *clear, gpio_port_pins_t cs,
83 				     gpio_port_pins_t clk, gpio_port_pins_t pico, uint8_t *z,
84 				     size_t n)
85 {
86 	bool hi;
87 
88 	/* assert chip-select (active low) */
89 	*clear |= cs;
90 
91 	for (; n > 0; --n, ++z) {
92 		/* msb down to lsb */
93 		for (int b = 7; b >= 0; --b) {
94 
95 			/* Data is shifted out on the falling edge (CPOL=0) */
96 			*clear |= clk;
97 			fpga_ice40_delay(delay);
98 
99 			hi = !!(BIT(b) & *z);
100 			if (hi) {
101 				*set |= pico;
102 			} else {
103 				*clear |= pico;
104 			}
105 
106 			/* Data is sampled on the rising edge (CPHA=0) */
107 			*set |= clk;
108 			fpga_ice40_delay(delay);
109 		}
110 	}
111 
112 	/* de-assert chip-select (active low) */
113 	*set |= cs;
114 }
115 
116 /*
117  * See iCE40 Family Handbook, Appendix A. SPI Slave Configuration Procedure,
118  * pp 15-21.
119  *
120  * https://www.latticesemi.com/~/media/LatticeSemi/Documents/Handbooks/iCE40FamilyHandbook.pdf
121  */
fpga_ice40_load(const struct device * dev,uint32_t * image_ptr,uint32_t img_size)122 static int fpga_ice40_load(const struct device *dev, uint32_t *image_ptr, uint32_t img_size)
123 {
124 	int ret;
125 	uint32_t crc;
126 	gpio_port_pins_t cs;
127 	gpio_port_pins_t clk;
128 	k_spinlock_key_t key;
129 	gpio_port_pins_t pico;
130 	gpio_port_pins_t creset;
131 	struct fpga_ice40_data *data = dev->data;
132 	const struct fpga_ice40_config *config = dev->config;
133 	const struct fpga_ice40_config_bitbang *config_bitbang = config->derived_config;
134 
135 	if (!device_is_ready(config_bitbang->clk.port)) {
136 		LOG_ERR("%s: GPIO for clk is not ready", dev->name);
137 		return -ENODEV;
138 	}
139 
140 	if (!device_is_ready(config_bitbang->pico.port)) {
141 		LOG_ERR("%s: GPIO for pico is not ready", dev->name);
142 		return -ENODEV;
143 	}
144 
145 	/* prepare masks */
146 	cs = BIT(config->bus.config.cs.gpio.pin);
147 	clk = BIT(config_bitbang->clk.pin);
148 	pico = BIT(config_bitbang->pico.pin);
149 	creset = BIT(config->creset.pin);
150 
151 	/* crc check */
152 	crc = crc32_ieee((uint8_t *)image_ptr, img_size);
153 	if (data->loaded && crc == data->crc) {
154 		LOG_WRN("already loaded with image CRC32c: 0x%08x", data->crc);
155 	}
156 
157 	key = k_spin_lock(&data->lock);
158 
159 	/* clear crc */
160 	data->crc = 0;
161 	data->loaded = false;
162 	fpga_ice40_crc_to_str(0, data->info);
163 
164 	LOG_DBG("Initializing GPIO");
165 	ret = gpio_pin_configure_dt(&config->cdone, GPIO_INPUT) ||
166 	      gpio_pin_configure_dt(&config->creset, GPIO_OUTPUT_HIGH) ||
167 	      gpio_pin_configure_dt(&config->bus.config.cs.gpio, GPIO_OUTPUT_HIGH) ||
168 	      gpio_pin_configure_dt(&config_bitbang->clk, GPIO_OUTPUT_HIGH) ||
169 	      gpio_pin_configure_dt(&config_bitbang->pico, GPIO_OUTPUT_HIGH);
170 	__ASSERT(ret == 0, "Failed to initialize GPIO: %d", ret);
171 
172 	LOG_DBG("Set CRESET low");
173 	LOG_DBG("Set SPI_CS low");
174 	*config_bitbang->clear |= (creset | cs);
175 
176 	/* Wait a minimum of 200ns */
177 	LOG_DBG("Delay %u us", config->creset_delay_us);
178 	fpga_ice40_delay(2 * config_bitbang->mhz_delay_count * config->creset_delay_us);
179 
180 	if (gpio_pin_get_dt(&config->cdone) != 0) {
181 		LOG_ERR("CDONE should be low after the reset");
182 		ret = -EIO;
183 		goto unlock;
184 	}
185 
186 	LOG_DBG("Set CRESET high");
187 	*config_bitbang->set |= creset;
188 
189 	LOG_DBG("Delay %u us", config->config_delay_us);
190 	k_busy_wait(config->config_delay_us);
191 
192 	LOG_DBG("Set SPI_CS high");
193 	*config_bitbang->set |= cs;
194 
195 	LOG_DBG("Send %u clocks", config->leading_clocks);
196 	fpga_ice40_send_clocks(config_bitbang->mhz_delay_count, config_bitbang->set,
197 			       config_bitbang->clear, clk, config->leading_clocks);
198 
199 	LOG_DBG("Set SPI_CS low");
200 	LOG_DBG("Send bin file");
201 	LOG_DBG("Set SPI_CS high");
202 	fpga_ice40_spi_send_data(config_bitbang->mhz_delay_count, config_bitbang->set,
203 				 config_bitbang->clear, cs, clk, pico, (uint8_t *)image_ptr,
204 				 img_size);
205 
206 	LOG_DBG("Send %u clocks", config->trailing_clocks);
207 	fpga_ice40_send_clocks(config_bitbang->mhz_delay_count, config_bitbang->set,
208 			       config_bitbang->clear, clk, config->trailing_clocks);
209 
210 	LOG_DBG("checking CDONE");
211 	ret = gpio_pin_get_dt(&config->cdone);
212 	if (ret < 0) {
213 		LOG_ERR("failed to read CDONE: %d", ret);
214 		goto unlock;
215 	} else if (ret != 1) {
216 		ret = -EIO;
217 		LOG_ERR("CDONE did not go high");
218 		goto unlock;
219 	}
220 
221 	ret = 0;
222 	data->loaded = true;
223 	fpga_ice40_crc_to_str(crc, data->info);
224 	LOG_INF("Loaded image with CRC32 0x%08x", crc);
225 
226 unlock:
227 	(void)gpio_pin_configure_dt(&config->creset, GPIO_OUTPUT_HIGH);
228 	(void)gpio_pin_configure_dt(&config->bus.config.cs.gpio, GPIO_OUTPUT_HIGH);
229 	(void)gpio_pin_configure_dt(&config_bitbang->clk, GPIO_DISCONNECTED);
230 	(void)gpio_pin_configure_dt(&config_bitbang->pico, GPIO_DISCONNECTED);
231 #ifdef CONFIG_PINCTRL
232 	(void)pinctrl_apply_state(config->pincfg, PINCTRL_STATE_DEFAULT);
233 #endif /* CONFIG_PINCTRL */
234 
235 	k_spin_unlock(&data->lock, key);
236 
237 	return ret;
238 }
239 
240 static DEVICE_API(fpga, fpga_ice40_api) = {
241 	.get_status = fpga_ice40_get_status,
242 	.reset = fpga_ice40_reset,
243 	.load = fpga_ice40_load,
244 	.on = fpga_ice40_on,
245 	.off = fpga_ice40_off,
246 	.get_info = fpga_ice40_get_info,
247 };
248 
249 #ifdef CONFIG_PINCTRL
250 #define FPGA_ICE40_PINCTRL_DEFINE(inst) PINCTRL_DT_DEFINE(DT_INST_PARENT(inst))
251 #define FPGA_ICE40_PINCTRL_GET(inst)    .pincfg = PINCTRL_DT_DEV_CONFIG_GET(DT_INST_PARENT(inst)),
252 #else
253 #define FPGA_ICE40_PINCTRL_DEFINE(inst)
254 #define FPGA_ICE40_PINCTRL_GET(inst)
255 #endif /* CONFIG_PINCTRL */
256 
257 #define FPGA_ICE40_DEFINE(inst)                                                                    \
258 	BUILD_ASSERT(DT_INST_PROP(inst, mhz_delay_count) >= 0);                                    \
259                                                                                                    \
260 	FPGA_ICE40_PINCTRL_DEFINE(inst);                                                           \
261 	static struct fpga_ice40_data fpga_ice40_data_##inst;                                      \
262                                                                                                    \
263 	static const struct fpga_ice40_config_bitbang fpga_ice40_config_bitbang_##inst = {         \
264 		.clk = GPIO_DT_SPEC_INST_GET(inst, clk_gpios),                                     \
265 		.pico = GPIO_DT_SPEC_INST_GET(inst, pico_gpios),                                   \
266 		.set = DT_INST_PROP(inst, gpios_set_reg),                                          \
267 		.clear = DT_INST_PROP(inst, gpios_clear_reg),                                      \
268 		.mhz_delay_count = DT_INST_PROP(inst, mhz_delay_count),                            \
269 		FPGA_ICE40_PINCTRL_GET(inst)};                                                     \
270                                                                                                    \
271 	FPGA_ICE40_CONFIG_DEFINE(inst, &fpga_ice40_config_bitbang_##inst);                         \
272                                                                                                    \
273 	DEVICE_DT_INST_DEFINE(inst, fpga_ice40_init, NULL, &fpga_ice40_data_##inst,                \
274 			      &fpga_ice40_config_##inst, POST_KERNEL, CONFIG_FPGA_INIT_PRIORITY,   \
275 			      &fpga_ice40_api);
276 
277 #define DT_DRV_COMPAT lattice_ice40_fpga_bitbang
278 DT_INST_FOREACH_STATUS_OKAY(FPGA_ICE40_DEFINE)
279