1 /*
2 * Copyright (c) 2024 Vogl Electronic GmbH
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <errno.h>
8 #include <stdbool.h>
9 #include <string.h>
10
11 #include <zephyr/device.h>
12 #include <zephyr/drivers/fpga.h>
13 #include <zephyr/drivers/i2c.h>
14 #include <zephyr/logging/log.h>
15
16 LOG_MODULE_REGISTER(fpga_slg471x5, CONFIG_FPGA_LOG_LEVEL);
17
18 #define SLG471X5_NREG 256
19
20 #define SLG471X5_I2C_RST_REG 0xF5U
21 #define SLG471X5_I2C_RST_BIT BIT(0)
22
23 #define SLG471X5_ADDR_UNCONFIGURED 0x00U
24
25 /*
26 * mem_region_t - Memory Region
27 *
28 * @addr starting address of memory region
29 * @len size of memory region
30 */
31 typedef union {
32 uint16_t mem_region;
33 struct {
34 uint8_t addr;
35 uint8_t len;
36 } __packed;
37 } mem_region_t;
38
39 struct fpga_slg471x5_data {
40 bool loaded;
41 struct k_spinlock lock;
42 };
43
44 struct fpga_slg471x5_config {
45 struct i2c_dt_spec bus;
46 mem_region_t *verify_list;
47 int verify_list_len;
48 bool try_unconfigured;
49 };
50
fpga_slg471x5_get_status(const struct device * dev)51 static enum FPGA_status fpga_slg471x5_get_status(const struct device *dev)
52 {
53 enum FPGA_status status;
54 k_spinlock_key_t key;
55 struct fpga_slg471x5_data *data = dev->data;
56
57 key = k_spin_lock(&data->lock);
58
59 if (data->loaded) {
60 status = FPGA_STATUS_ACTIVE;
61 } else {
62 status = FPGA_STATUS_INACTIVE;
63 }
64
65 k_spin_unlock(&data->lock, key);
66
67 return status;
68 }
69
fpga_slg471x5_verify(const struct device * dev,uint8_t * img,uint32_t img_size)70 static int fpga_slg471x5_verify(const struct device *dev, uint8_t *img, uint32_t img_size)
71 {
72 const struct fpga_slg471x5_config *config = dev->config;
73 uint8_t buf[SLG471X5_NREG] = {0}, addr, len;
74 int i, ret;
75
76 ret = i2c_read_dt(&config->bus, buf, SLG471X5_NREG);
77 if (ret < 0) {
78 return ret;
79 }
80
81 for (i = 0; i < config->verify_list_len; i++) {
82 addr = config->verify_list[i].addr;
83 len = config->verify_list[i].len;
84 if (memcmp(&img[addr], &buf[addr], len) != 0) {
85 return -EIO;
86 }
87 }
88
89 return 0;
90 }
91
fpga_slg471x5_load(const struct device * dev,uint32_t * image_ptr,uint32_t img_size)92 static int fpga_slg471x5_load(const struct device *dev, uint32_t *image_ptr, uint32_t img_size)
93 {
94 const struct fpga_slg471x5_config *config = dev->config;
95 struct fpga_slg471x5_data *data = dev->data;
96 uint8_t buf[SLG471X5_NREG + 1];
97 int ret;
98
99 if (img_size > SLG471X5_NREG) {
100 img_size = SLG471X5_NREG;
101 }
102
103 buf[0] = 0;
104 memcpy(buf + 1, image_ptr, img_size);
105
106 if (config->try_unconfigured) {
107 ret = i2c_write(config->bus.bus, buf, img_size + 1, SLG471X5_ADDR_UNCONFIGURED);
108 if (ret == 0) {
109 ret = fpga_slg471x5_verify(dev, buf + 1, img_size);
110 if (ret == 0) {
111 data->loaded = true;
112 return 0;
113 }
114 }
115 }
116
117 ret = i2c_write_dt(&config->bus, buf, img_size + 1);
118 if (ret < 0) {
119 LOG_ERR("Loading bitstream failed");
120 return ret;
121 }
122
123 ret = fpga_slg471x5_verify(dev, buf + 1, img_size);
124 if (ret < 0) {
125 LOG_ERR("Verification failed");
126 return ret;
127 }
128
129 data->loaded = true;
130
131 return 0;
132 }
133
fpga_slg471x5_reset(const struct device * dev)134 static int fpga_slg471x5_reset(const struct device *dev)
135 {
136 const struct fpga_slg471x5_config *config = dev->config;
137 struct fpga_slg471x5_data *data = dev->data;
138 int ret;
139
140 ret = i2c_reg_update_byte_dt(&config->bus, SLG471X5_I2C_RST_REG, SLG471X5_I2C_RST_BIT,
141 SLG471X5_I2C_RST_BIT);
142 if (ret < 0) {
143 return ret;
144 }
145
146 data->loaded = false;
147
148 return 0;
149 }
150
151 static DEVICE_API(fpga, fpga_slg471x5_api) = {
152 .get_status = fpga_slg471x5_get_status,
153 .reset = fpga_slg471x5_reset,
154 .load = fpga_slg471x5_load,
155 };
156
fpga_slg471x5_init(const struct device * dev)157 static int fpga_slg471x5_init(const struct device *dev)
158 {
159 const struct fpga_slg471x5_config *config = dev->config;
160
161 if (!i2c_is_ready_dt(&config->bus)) {
162 LOG_ERR("I2C bus %s not ready", config->bus.bus->name);
163 return -ENODEV;
164 }
165
166 return 0;
167 }
168
169 #define SLG471X5_INIT(type, inst) \
170 static struct fpga_slg471x5_data fpga_slg##type##_data_##inst; \
171 \
172 static mem_region_t fpga_slg##type##_verify_list[] = FPGA_SLG##type##_VERIFY_LIST; \
173 \
174 static const struct fpga_slg471x5_config fpga_slg##type##_config_##inst = { \
175 .bus = I2C_DT_SPEC_INST_GET(inst), \
176 .verify_list = fpga_slg##type##_verify_list, \
177 .verify_list_len = sizeof(fpga_slg##type##_verify_list) / sizeof(mem_region_t), \
178 .try_unconfigured = DT_INST_NODE_HAS_PROP(inst, try_unconfigured), \
179 }; \
180 \
181 DEVICE_DT_INST_DEFINE(inst, fpga_slg471x5_init, NULL, &fpga_slg##type##_data_##inst, \
182 &fpga_slg##type##_config_##inst, POST_KERNEL, \
183 CONFIG_FPGA_INIT_PRIORITY, &fpga_slg471x5_api)
184
185 #define FPGA_SLG47105_VERIFY_LIST \
186 { \
187 {.addr = 0x00, .len = 0x47}, \
188 {.addr = 0x4C, .len = 0x01}, \
189 {.addr = 0xFD, .len = 0x01}, \
190 }
191
192 #define SLG47105_INIT(inst) SLG471X5_INIT(47105, inst)
193 #undef DT_DRV_COMPAT
194 #define DT_DRV_COMPAT renesas_slg47105
195 DT_INST_FOREACH_STATUS_OKAY(SLG47105_INIT)
196
197 #define FPGA_SLG47115_VERIFY_LIST \
198 { \
199 {.addr = 0x00, .len = 0x47}, \
200 {.addr = 0x4C, .len = 0x01}, \
201 {.addr = 0xFD, .len = 0x01}, \
202 }
203
204 #define SLG47115_INIT(inst) SLG471X5_INIT(47115, inst)
205 #undef DT_DRV_COMPAT
206 #define DT_DRV_COMPAT renesas_slg47115
207 DT_INST_FOREACH_STATUS_OKAY(SLG47115_INIT)
208