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