1 /*
2  * Copyright (c) 2022 Meta
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/device.h>
8 #include <zephyr/drivers/fpga.h>
9 #include <zephyr/drivers/gpio.h>
10 #include <zephyr/drivers/spi.h>
11 #include <zephyr/kernel.h>
12 #include <zephyr/logging/log.h>
13 #include <zephyr/sys/crc.h>
14 #include <zephyr/sys/util.h>
15 
16 #include "fpga_ice40_common.h"
17 
18 LOG_MODULE_DECLARE(fpga_ice40);
19 
fpga_ice40_load(const struct device * dev,uint32_t * image_ptr,uint32_t img_size)20 static int fpga_ice40_load(const struct device *dev, uint32_t *image_ptr, uint32_t img_size)
21 {
22 	int ret;
23 	uint32_t crc;
24 	k_spinlock_key_t key;
25 	struct spi_buf tx_buf;
26 	const struct spi_buf_set tx_bufs = {
27 		.buffers = &tx_buf,
28 		.count = 1,
29 	};
30 	struct fpga_ice40_data *data = dev->data;
31 	uint8_t clock_buf[(UINT8_MAX + 1) / BITS_PER_BYTE];
32 	const struct fpga_ice40_config *config = dev->config;
33 	struct spi_dt_spec bus;
34 
35 	memcpy(&bus, &config->bus, sizeof(bus));
36 	/*
37 	 * Disable the automatism for chip select within the SPI driver,
38 	 * as the configuration sequence requires this signal to be inactive
39 	 * during the leading and trailing clock phase.
40 	 */
41 	bus.config.cs.gpio.port = NULL;
42 
43 	/* crc check */
44 	crc = crc32_ieee((uint8_t *)image_ptr, img_size);
45 	if (data->loaded && crc == data->crc) {
46 		LOG_WRN("already loaded with image CRC32c: 0x%08x", data->crc);
47 	}
48 
49 	key = k_spin_lock(&data->lock);
50 
51 	/* clear crc */
52 	data->crc = 0;
53 	data->loaded = false;
54 	fpga_ice40_crc_to_str(0, data->info);
55 
56 	LOG_DBG("Initializing GPIO");
57 	ret = gpio_pin_configure_dt(&config->cdone, GPIO_INPUT) ||
58 	      gpio_pin_configure_dt(&config->creset, GPIO_OUTPUT_HIGH) ||
59 	      gpio_pin_configure_dt(&config->bus.config.cs.gpio, GPIO_OUTPUT_HIGH);
60 	__ASSERT(ret == 0, "Failed to initialize GPIO: %d", ret);
61 
62 	LOG_DBG("Set CRESET low");
63 	ret = gpio_pin_configure_dt(&config->creset, GPIO_OUTPUT_LOW);
64 	if (ret < 0) {
65 		LOG_ERR("failed to set CRESET low: %d", ret);
66 		goto unlock;
67 	}
68 
69 	LOG_DBG("Set SPI_CS low");
70 	ret = gpio_pin_configure_dt(&config->bus.config.cs.gpio, GPIO_OUTPUT_LOW);
71 	if (ret < 0) {
72 		LOG_ERR("failed to set SPI_CS low: %d", ret);
73 		goto unlock;
74 	}
75 
76 	/* Wait a minimum of 200ns */
77 	LOG_DBG("Delay %u us", config->creset_delay_us);
78 	k_usleep(config->creset_delay_us);
79 
80 	if (gpio_pin_get_dt(&config->cdone) != 0) {
81 		LOG_ERR("CDONE should be low after the reset");
82 		ret = -EIO;
83 		goto unlock;
84 	}
85 
86 	LOG_DBG("Set CRESET high");
87 	ret = gpio_pin_configure_dt(&config->creset, GPIO_OUTPUT_HIGH);
88 	if (ret < 0) {
89 		LOG_ERR("failed to set CRESET high: %d", ret);
90 		goto unlock;
91 	}
92 
93 	LOG_DBG("Delay %u us", config->config_delay_us);
94 	k_busy_wait(config->config_delay_us);
95 
96 	LOG_DBG("Set SPI_CS high");
97 	ret = gpio_pin_configure_dt(&config->bus.config.cs.gpio, GPIO_OUTPUT_HIGH);
98 	if (ret < 0) {
99 		LOG_ERR("failed to set SPI_CS high: %d", ret);
100 		goto unlock;
101 	}
102 
103 	LOG_DBG("Send %u clocks", config->leading_clocks);
104 	tx_buf.buf = clock_buf;
105 	tx_buf.len = DIV_ROUND_UP(config->leading_clocks, BITS_PER_BYTE);
106 	ret = spi_write_dt(&bus, &tx_bufs);
107 	if (ret < 0) {
108 		LOG_ERR("Failed to send leading %u clocks: %d", config->leading_clocks, ret);
109 		goto unlock;
110 	}
111 
112 	LOG_DBG("Set SPI_CS low");
113 	ret = gpio_pin_configure_dt(&config->bus.config.cs.gpio, GPIO_OUTPUT_LOW);
114 	if (ret < 0) {
115 		LOG_ERR("failed to set SPI_CS low: %d", ret);
116 		goto unlock;
117 	}
118 
119 	LOG_DBG("Send bin file");
120 	tx_buf.buf = image_ptr;
121 	tx_buf.len = img_size;
122 	ret = spi_write_dt(&bus, &tx_bufs);
123 	if (ret < 0) {
124 		LOG_ERR("Failed to send bin file: %d", ret);
125 		goto unlock;
126 	}
127 
128 	LOG_DBG("Set SPI_CS high");
129 	ret = gpio_pin_configure_dt(&config->bus.config.cs.gpio, GPIO_OUTPUT_HIGH);
130 	if (ret < 0) {
131 		LOG_ERR("failed to set SPI_CS high: %d", ret);
132 		goto unlock;
133 	}
134 
135 	LOG_DBG("Send %u clocks", config->trailing_clocks);
136 	tx_buf.buf = clock_buf;
137 	tx_buf.len = DIV_ROUND_UP(config->trailing_clocks, BITS_PER_BYTE);
138 	ret = spi_write_dt(&bus, &tx_bufs);
139 	if (ret < 0) {
140 		LOG_ERR("Failed to send trailing %u clocks: %d", config->trailing_clocks, ret);
141 		goto unlock;
142 	}
143 
144 	LOG_DBG("checking CDONE");
145 	ret = gpio_pin_get_dt(&config->cdone);
146 	if (ret < 0) {
147 		LOG_ERR("failed to read CDONE: %d", ret);
148 		goto unlock;
149 	} else if (ret != 1) {
150 		ret = -EIO;
151 		LOG_ERR("CDONE did not go high");
152 		goto unlock;
153 	}
154 
155 	ret = 0;
156 	data->loaded = true;
157 	fpga_ice40_crc_to_str(crc, data->info);
158 	LOG_INF("Loaded image with CRC32 0x%08x", crc);
159 
160 unlock:
161 	(void)gpio_pin_configure_dt(&config->creset, GPIO_OUTPUT_HIGH);
162 	(void)gpio_pin_configure_dt(&config->bus.config.cs.gpio, GPIO_OUTPUT_HIGH);
163 
164 	k_spin_unlock(&data->lock, key);
165 
166 	return ret;
167 }
168 
169 static DEVICE_API(fpga, fpga_ice40_api) = {
170 	.get_status = fpga_ice40_get_status,
171 	.reset = fpga_ice40_reset,
172 	.load = fpga_ice40_load,
173 	.on = fpga_ice40_on,
174 	.off = fpga_ice40_off,
175 	.get_info = fpga_ice40_get_info,
176 };
177 
178 #define FPGA_ICE40_DEFINE(inst)                                                                    \
179 	static struct fpga_ice40_data fpga_ice40_data_##inst;                                      \
180                                                                                                    \
181 	FPGA_ICE40_CONFIG_DEFINE(inst, NULL);                                                      \
182                                                                                                    \
183 	DEVICE_DT_INST_DEFINE(inst, fpga_ice40_init, NULL, &fpga_ice40_data_##inst,                \
184 			      &fpga_ice40_config_##inst, POST_KERNEL, CONFIG_FPGA_INIT_PRIORITY,   \
185 			      &fpga_ice40_api);
186 
187 #define DT_DRV_COMPAT lattice_ice40_fpga
188 DT_INST_FOREACH_STATUS_OKAY(FPGA_ICE40_DEFINE)
189