You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
207 lines
5.9 KiB
207 lines
5.9 KiB
/* |
|
* Copyright (c) 2024 Vogl Electronic GmbH |
|
* |
|
* SPDX-License-Identifier: Apache-2.0 |
|
*/ |
|
|
|
#include <errno.h> |
|
#include <stdbool.h> |
|
#include <string.h> |
|
|
|
#include <zephyr/device.h> |
|
#include <zephyr/drivers/fpga.h> |
|
#include <zephyr/drivers/i2c.h> |
|
#include <zephyr/logging/log.h> |
|
|
|
LOG_MODULE_REGISTER(fpga_slg471x5, CONFIG_FPGA_LOG_LEVEL); |
|
|
|
#define SLG471X5_NREG 256 |
|
|
|
#define SLG471X5_I2C_RST_REG 0xF5U |
|
#define SLG471X5_I2C_RST_BIT BIT(0) |
|
|
|
#define SLG471X5_ADDR_UNCONFIGURED 0x00U |
|
|
|
/* |
|
* mem_region_t - Memory Region |
|
* |
|
* @addr starting address of memory region |
|
* @len size of memory region |
|
*/ |
|
typedef union { |
|
uint16_t mem_region; |
|
struct { |
|
uint8_t addr; |
|
uint8_t len; |
|
} __packed; |
|
} mem_region_t; |
|
|
|
struct fpga_slg471x5_data { |
|
bool loaded; |
|
struct k_spinlock lock; |
|
}; |
|
|
|
struct fpga_slg471x5_config { |
|
struct i2c_dt_spec bus; |
|
mem_region_t *verify_list; |
|
int verify_list_len; |
|
bool try_unconfigured; |
|
}; |
|
|
|
static enum FPGA_status fpga_slg471x5_get_status(const struct device *dev) |
|
{ |
|
enum FPGA_status status; |
|
k_spinlock_key_t key; |
|
struct fpga_slg471x5_data *data = dev->data; |
|
|
|
key = k_spin_lock(&data->lock); |
|
|
|
if (data->loaded) { |
|
status = FPGA_STATUS_ACTIVE; |
|
} else { |
|
status = FPGA_STATUS_INACTIVE; |
|
} |
|
|
|
k_spin_unlock(&data->lock, key); |
|
|
|
return status; |
|
} |
|
|
|
static int fpga_slg471x5_verify(const struct device *dev, uint8_t *img, uint32_t img_size) |
|
{ |
|
const struct fpga_slg471x5_config *config = dev->config; |
|
uint8_t buf[SLG471X5_NREG] = {0}, addr, len; |
|
int i, ret; |
|
|
|
ret = i2c_read_dt(&config->bus, buf, SLG471X5_NREG); |
|
if (ret < 0) { |
|
return ret; |
|
} |
|
|
|
for (i = 0; i < config->verify_list_len; i++) { |
|
addr = config->verify_list[i].addr; |
|
len = config->verify_list[i].len; |
|
if (memcmp(&img[addr], &buf[addr], len) != 0) { |
|
return -EIO; |
|
} |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
static int fpga_slg471x5_load(const struct device *dev, uint32_t *image_ptr, uint32_t img_size) |
|
{ |
|
const struct fpga_slg471x5_config *config = dev->config; |
|
struct fpga_slg471x5_data *data = dev->data; |
|
uint8_t buf[SLG471X5_NREG + 1]; |
|
int ret; |
|
|
|
if (img_size > SLG471X5_NREG) { |
|
img_size = SLG471X5_NREG; |
|
} |
|
|
|
buf[0] = 0; |
|
memcpy(buf + 1, image_ptr, img_size); |
|
|
|
if (config->try_unconfigured) { |
|
ret = i2c_write(config->bus.bus, buf, img_size + 1, SLG471X5_ADDR_UNCONFIGURED); |
|
if (ret == 0) { |
|
ret = fpga_slg471x5_verify(dev, buf + 1, img_size); |
|
if (ret == 0) { |
|
data->loaded = true; |
|
return 0; |
|
} |
|
} |
|
} |
|
|
|
ret = i2c_write_dt(&config->bus, buf, img_size + 1); |
|
if (ret < 0) { |
|
LOG_ERR("Loading bitstream failed"); |
|
return ret; |
|
} |
|
|
|
ret = fpga_slg471x5_verify(dev, buf + 1, img_size); |
|
if (ret < 0) { |
|
LOG_ERR("Verification failed"); |
|
return ret; |
|
} |
|
|
|
data->loaded = true; |
|
|
|
return 0; |
|
} |
|
|
|
static int fpga_slg471x5_reset(const struct device *dev) |
|
{ |
|
const struct fpga_slg471x5_config *config = dev->config; |
|
struct fpga_slg471x5_data *data = dev->data; |
|
int ret; |
|
|
|
ret = i2c_reg_update_byte_dt(&config->bus, SLG471X5_I2C_RST_REG, SLG471X5_I2C_RST_BIT, |
|
SLG471X5_I2C_RST_BIT); |
|
if (ret < 0) { |
|
return ret; |
|
} |
|
|
|
data->loaded = false; |
|
|
|
return 0; |
|
} |
|
|
|
static DEVICE_API(fpga, fpga_slg471x5_api) = { |
|
.get_status = fpga_slg471x5_get_status, |
|
.reset = fpga_slg471x5_reset, |
|
.load = fpga_slg471x5_load, |
|
}; |
|
|
|
static int fpga_slg471x5_init(const struct device *dev) |
|
{ |
|
const struct fpga_slg471x5_config *config = dev->config; |
|
|
|
if (!i2c_is_ready_dt(&config->bus)) { |
|
LOG_ERR("I2C bus %s not ready", config->bus.bus->name); |
|
return -ENODEV; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
#define SLG471X5_INIT(type, inst) \ |
|
static struct fpga_slg471x5_data fpga_slg##type##_data_##inst; \ |
|
\ |
|
static mem_region_t fpga_slg##type##_verify_list[] = FPGA_SLG##type##_VERIFY_LIST; \ |
|
\ |
|
static const struct fpga_slg471x5_config fpga_slg##type##_config_##inst = { \ |
|
.bus = I2C_DT_SPEC_INST_GET(inst), \ |
|
.verify_list = fpga_slg##type##_verify_list, \ |
|
.verify_list_len = sizeof(fpga_slg##type##_verify_list) / sizeof(mem_region_t), \ |
|
.try_unconfigured = DT_INST_NODE_HAS_PROP(inst, try_unconfigured), \ |
|
}; \ |
|
\ |
|
DEVICE_DT_INST_DEFINE(inst, fpga_slg471x5_init, NULL, &fpga_slg##type##_data_##inst, \ |
|
&fpga_slg##type##_config_##inst, POST_KERNEL, \ |
|
CONFIG_FPGA_INIT_PRIORITY, &fpga_slg471x5_api) |
|
|
|
#define FPGA_SLG47105_VERIFY_LIST \ |
|
{ \ |
|
{.addr = 0x00, .len = 0x47}, \ |
|
{.addr = 0x4C, .len = 0x01}, \ |
|
{.addr = 0xFD, .len = 0x01}, \ |
|
} |
|
|
|
#define SLG47105_INIT(inst) SLG471X5_INIT(47105, inst) |
|
#undef DT_DRV_COMPAT |
|
#define DT_DRV_COMPAT renesas_slg47105 |
|
DT_INST_FOREACH_STATUS_OKAY(SLG47105_INIT) |
|
|
|
#define FPGA_SLG47115_VERIFY_LIST \ |
|
{ \ |
|
{.addr = 0x00, .len = 0x47}, \ |
|
{.addr = 0x4C, .len = 0x01}, \ |
|
{.addr = 0xFD, .len = 0x01}, \ |
|
} |
|
|
|
#define SLG47115_INIT(inst) SLG471X5_INIT(47115, inst) |
|
#undef DT_DRV_COMPAT |
|
#define DT_DRV_COMPAT renesas_slg47115 |
|
DT_INST_FOREACH_STATUS_OKAY(SLG47115_INIT)
|
|
|