205 lines
5.9 KiB
C
205 lines
5.9 KiB
C
/*
|
|
* 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);
|
|
|
|
#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;
|
|
|
|
i2c_read_dt(&config->bus, buf, SLG471X5_NREG);
|
|
|
|
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 const struct fpga_driver_api 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)
|