/* * Copyright (c) 2022 Meta * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include #include #include #include #include "fpga_ice40_common.h" LOG_MODULE_DECLARE(fpga_ice40); static int fpga_ice40_load(const struct device *dev, uint32_t *image_ptr, uint32_t img_size) { int ret; uint32_t crc; k_spinlock_key_t key; struct spi_buf tx_buf; const struct spi_buf_set tx_bufs = { .buffers = &tx_buf, .count = 1, }; struct fpga_ice40_data *data = dev->data; uint8_t clock_buf[(UINT8_MAX + 1) / BITS_PER_BYTE]; const struct fpga_ice40_config *config = dev->config; struct spi_dt_spec bus; memcpy(&bus, &config->bus, sizeof(bus)); /* * Disable the automatism for chip select within the SPI driver, * as the configuration sequence requires this signal to be inactive * during the leading and trailing clock phase. */ bus.config.cs.gpio.port = NULL; /* crc check */ crc = crc32_ieee((uint8_t *)image_ptr, img_size); if (data->loaded && crc == data->crc) { LOG_WRN("already loaded with image CRC32c: 0x%08x", data->crc); } key = k_spin_lock(&data->lock); /* clear crc */ data->crc = 0; data->loaded = false; fpga_ice40_crc_to_str(0, data->info); LOG_DBG("Initializing GPIO"); ret = gpio_pin_configure_dt(&config->cdone, GPIO_INPUT) || gpio_pin_configure_dt(&config->creset, GPIO_OUTPUT_HIGH) || gpio_pin_configure_dt(&config->bus.config.cs.gpio, GPIO_OUTPUT_HIGH); __ASSERT(ret == 0, "Failed to initialize GPIO: %d", ret); LOG_DBG("Set CRESET low"); ret = gpio_pin_configure_dt(&config->creset, GPIO_OUTPUT_LOW); if (ret < 0) { LOG_ERR("failed to set CRESET low: %d", ret); goto unlock; } LOG_DBG("Set SPI_CS low"); ret = gpio_pin_configure_dt(&config->bus.config.cs.gpio, GPIO_OUTPUT_LOW); if (ret < 0) { LOG_ERR("failed to set SPI_CS low: %d", ret); goto unlock; } /* Wait a minimum of 200ns */ LOG_DBG("Delay %u us", config->creset_delay_us); k_usleep(config->creset_delay_us); if (gpio_pin_get_dt(&config->cdone) != 0) { LOG_ERR("CDONE should be low after the reset"); ret = -EIO; goto unlock; } LOG_DBG("Set CRESET high"); ret = gpio_pin_configure_dt(&config->creset, GPIO_OUTPUT_HIGH); if (ret < 0) { LOG_ERR("failed to set CRESET high: %d", ret); goto unlock; } LOG_DBG("Delay %u us", config->config_delay_us); k_busy_wait(config->config_delay_us); LOG_DBG("Set SPI_CS high"); ret = gpio_pin_configure_dt(&config->bus.config.cs.gpio, GPIO_OUTPUT_HIGH); if (ret < 0) { LOG_ERR("failed to set SPI_CS high: %d", ret); goto unlock; } LOG_DBG("Send %u clocks", config->leading_clocks); tx_buf.buf = clock_buf; tx_buf.len = DIV_ROUND_UP(config->leading_clocks, BITS_PER_BYTE); ret = spi_write_dt(&bus, &tx_bufs); if (ret < 0) { LOG_ERR("Failed to send leading %u clocks: %d", config->leading_clocks, ret); goto unlock; } LOG_DBG("Set SPI_CS low"); ret = gpio_pin_configure_dt(&config->bus.config.cs.gpio, GPIO_OUTPUT_LOW); if (ret < 0) { LOG_ERR("failed to set SPI_CS low: %d", ret); goto unlock; } LOG_DBG("Send bin file"); tx_buf.buf = image_ptr; tx_buf.len = img_size; ret = spi_write_dt(&bus, &tx_bufs); if (ret < 0) { LOG_ERR("Failed to send bin file: %d", ret); goto unlock; } LOG_DBG("Set SPI_CS high"); ret = gpio_pin_configure_dt(&config->bus.config.cs.gpio, GPIO_OUTPUT_HIGH); if (ret < 0) { LOG_ERR("failed to set SPI_CS high: %d", ret); goto unlock; } LOG_DBG("Send %u clocks", config->trailing_clocks); tx_buf.buf = clock_buf; tx_buf.len = DIV_ROUND_UP(config->trailing_clocks, BITS_PER_BYTE); ret = spi_write_dt(&bus, &tx_bufs); if (ret < 0) { LOG_ERR("Failed to send trailing %u clocks: %d", config->trailing_clocks, ret); goto unlock; } LOG_DBG("checking CDONE"); ret = gpio_pin_get_dt(&config->cdone); if (ret < 0) { LOG_ERR("failed to read CDONE: %d", ret); goto unlock; } else if (ret != 1) { ret = -EIO; LOG_ERR("CDONE did not go high"); goto unlock; } ret = 0; data->loaded = true; fpga_ice40_crc_to_str(crc, data->info); LOG_INF("Loaded image with CRC32 0x%08x", crc); unlock: (void)gpio_pin_configure_dt(&config->creset, GPIO_OUTPUT_HIGH); (void)gpio_pin_configure_dt(&config->bus.config.cs.gpio, GPIO_OUTPUT_HIGH); k_spin_unlock(&data->lock, key); return ret; } static DEVICE_API(fpga, fpga_ice40_api) = { .get_status = fpga_ice40_get_status, .reset = fpga_ice40_reset, .load = fpga_ice40_load, .on = fpga_ice40_on, .off = fpga_ice40_off, .get_info = fpga_ice40_get_info, }; #define FPGA_ICE40_DEFINE(inst) \ static struct fpga_ice40_data fpga_ice40_data_##inst; \ \ FPGA_ICE40_CONFIG_DEFINE(inst, NULL); \ \ DEVICE_DT_INST_DEFINE(inst, fpga_ice40_init, NULL, &fpga_ice40_data_##inst, \ &fpga_ice40_config_##inst, POST_KERNEL, CONFIG_FPGA_INIT_PRIORITY, \ &fpga_ice40_api); #define DT_DRV_COMPAT lattice_ice40_fpga DT_INST_FOREACH_STATUS_OKAY(FPGA_ICE40_DEFINE)