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