1 /*
2  * Copyright (c) 2024 Silicon Laboratories Inc.
3  * SPDX-License-Identifier: Apache-2.0
4  */
5 #define DT_DRV_COMPAT silabs_siwx91x_flash_controller
6 
7 #include <zephyr/kernel.h>
8 #include <zephyr/device.h>
9 #include <zephyr/drivers/flash.h>
10 #include <zephyr/logging/log.h>
11 
12 #include "sl_si91x_driver.h"
13 
14 LOG_MODULE_REGISTER(siwx91x_soc_flash);
15 
16 struct siwx91x_config {
17 	uintptr_t base_address;
18 	uint32_t size;
19 	uint32_t write_block_size;
20 	uint32_t erase_block_size;
21 	struct flash_parameters flash_parameters;
22 #ifdef CONFIG_FLASH_PAGE_LAYOUT
23 	struct flash_pages_layout flash_pages_layout;
24 #endif
25 };
26 
27 struct siwx91x_data {
28 	struct k_sem lock;
29 };
30 
flash_siwx91x_range_is_in_bounds(const struct device * dev,off_t offset,size_t len)31 static bool flash_siwx91x_range_is_in_bounds(const struct device *dev, off_t offset, size_t len)
32 {
33 	const struct siwx91x_config *cfg = dev->config;
34 
35 	/* Note: If offset < __rom_region_end, the user to overwriting the current firmware. User
36 	 * is probably doing a mistake, but not an error from this driver point of view.
37 	 */
38 	if (offset < 0) {
39 		return false;
40 	}
41 	if (offset + len > cfg->size) {
42 		return false;
43 	}
44 	return true;
45 }
46 
flash_siwx91x_get_parameters(const struct device * dev)47 static const struct flash_parameters *flash_siwx91x_get_parameters(const struct device *dev)
48 {
49 	const struct siwx91x_config *cfg = dev->config;
50 
51 	return &cfg->flash_parameters;
52 }
53 
flash_siwx91x_read(const struct device * dev,off_t offset,void * buf,size_t len)54 static int flash_siwx91x_read(const struct device *dev, off_t offset, void *buf, size_t len)
55 {
56 	const struct siwx91x_config *cfg = dev->config;
57 	struct siwx91x_data *data = dev->data;
58 	void *location = (void *)(cfg->base_address + offset);
59 
60 	if (!flash_siwx91x_range_is_in_bounds(dev, offset, len)) {
61 		return -EINVAL;
62 	}
63 	k_sem_take(&data->lock, K_FOREVER);
64 	memcpy(buf, location, len);
65 	k_sem_give(&data->lock);
66 	return 0;
67 }
68 
flash_siwx91x_write(const struct device * dev,off_t offset,const void * buf,size_t len)69 static int flash_siwx91x_write(const struct device *dev, off_t offset, const void *buf, size_t len)
70 {
71 	const struct siwx91x_config *cfg = dev->config;
72 	struct siwx91x_data *data = dev->data;
73 	uint32_t ret;
74 
75 	if (!flash_siwx91x_range_is_in_bounds(dev, offset, len)) {
76 		return -EINVAL;
77 	}
78 	if (offset % cfg->write_block_size) {
79 		return -EINVAL;
80 	}
81 	if (len % cfg->write_block_size) {
82 		return -EINVAL;
83 	}
84 	k_sem_take(&data->lock, K_FOREVER);
85 	ret = sl_si91x_command_to_write_common_flash(cfg->base_address + offset, (void *)buf, len,
86 						     false);
87 	k_sem_give(&data->lock);
88 	if (ret) {
89 		return -EIO;
90 	}
91 	return 0;
92 }
93 
flash_siwx91x_erase(const struct device * dev,off_t offset,size_t len)94 static int flash_siwx91x_erase(const struct device *dev, off_t offset, size_t len)
95 {
96 	const struct siwx91x_config *cfg = dev->config;
97 	struct siwx91x_data *data = dev->data;
98 	uint32_t ret;
99 
100 	if (!flash_siwx91x_range_is_in_bounds(dev, offset, len)) {
101 		return -EINVAL;
102 	}
103 	if (offset % cfg->erase_block_size) {
104 		return -EINVAL;
105 	}
106 	if (len % cfg->erase_block_size) {
107 		return -EINVAL;
108 	}
109 	k_sem_take(&data->lock, K_FOREVER);
110 	ret = sl_si91x_command_to_write_common_flash(cfg->base_address + offset, NULL, len, true);
111 	k_sem_give(&data->lock);
112 	if (ret) {
113 		return -EIO;
114 	}
115 	return 0;
116 }
117 
118 #ifdef CONFIG_FLASH_PAGE_LAYOUT
flash_siwx91x_page_layout(const struct device * dev,const struct flash_pages_layout ** layout,size_t * layout_size)119 static void flash_siwx91x_page_layout(const struct device *dev,
120 				      const struct flash_pages_layout **layout, size_t *layout_size)
121 {
122 	const struct siwx91x_config *cfg = dev->config;
123 
124 	*layout = &cfg->flash_pages_layout;
125 	*layout_size = 1;
126 }
127 #endif
128 
129 static DEVICE_API(flash, siwx91x_api) = {
130 	.read = flash_siwx91x_read,
131 	.write = flash_siwx91x_write,
132 	.erase = flash_siwx91x_erase,
133 	.get_parameters = flash_siwx91x_get_parameters,
134 #ifdef CONFIG_FLASH_PAGE_LAYOUT
135 	.page_layout = flash_siwx91x_page_layout,
136 #endif
137 };
138 
flash_siwx91x_init(const struct device * dev)139 static int flash_siwx91x_init(const struct device *dev)
140 {
141 	struct siwx91x_data *data = dev->data;
142 
143 	k_sem_init(&data->lock, 1, 1);
144 	return 0;
145 }
146 
147 #ifdef CONFIG_FLASH_PAGE_LAYOUT
148 #define SIWX91X_PAGE_LAYOUT_FLASH_INIT(n)                                                          \
149 	.flash_pages_layout.pages_count = DT_REG_SIZE(n) / DT_PROP(n, erase_block_size),           \
150 	.flash_pages_layout.pages_size = DT_PROP(n, erase_block_size),
151 #else
152 #define SIWX91X_PAGE_LAYOUT_FLASH_INIT(n)
153 #endif
154 
155 #define SIWX91X_FLASH_INIT_P(n, p)                                                                 \
156 	static const struct siwx91x_config flash_siwx91x_config_##p = {                            \
157 		.base_address = DT_REG_ADDR(n),                                                    \
158 		.size = DT_REG_SIZE(n),                                                            \
159 		.write_block_size = DT_PROP(n, write_block_size),                                  \
160 		.erase_block_size = DT_PROP(n, erase_block_size),                                  \
161 		.flash_parameters.write_block_size = DT_PROP(n, write_block_size),                 \
162 		.flash_parameters.erase_value = 0xff,                                              \
163 		SIWX91X_PAGE_LAYOUT_FLASH_INIT(n)                                                  \
164 	};                                                \
165 	static struct siwx91x_data flash_siwx91x_data_##p = {                                      \
166 		.lock = Z_SEM_INITIALIZER(flash_siwx91x_data_##p.lock, 1, 1),                      \
167 	};                                                                                         \
168 	DEVICE_DT_INST_DEFINE(p, flash_siwx91x_init, NULL, &flash_siwx91x_data_##p,                \
169 			      &flash_siwx91x_config_##p, POST_KERNEL, CONFIG_FLASH_INIT_PRIORITY,  \
170 			      &siwx91x_api);
171 
172 #define SIWX91X_FLASH_INIT(p)                                                                      \
173 	BUILD_ASSERT(DT_INST_CHILD_NUM_STATUS_OKAY(p) == 1);                                       \
174 	DT_INST_FOREACH_CHILD_STATUS_OKAY_VARGS(p, SIWX91X_FLASH_INIT_P, p)
175 
176 DT_INST_FOREACH_STATUS_OKAY(SIWX91X_FLASH_INIT)
177