forked from zephyrproject-rtos/zephyr
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsoc_flash_b91.c
236 lines (191 loc) · 5.52 KB
/
soc_flash_b91.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
/*
* Copyright (c) 2021 Telink Semiconductor
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT telink_b91_flash_controller
#define FLASH_SIZE DT_REG_SIZE(DT_INST(0, soc_nv_flash))
#define FLASH_ORIGIN DT_REG_ADDR(DT_INST(0, soc_nv_flash))
#include "flash.h"
#include <string.h>
#include <zephyr/device.h>
#include <zephyr/drivers/flash.h>
#include <zephyr/kernel.h>
/* driver definitions */
#define BLOCK_64K_SIZE (0x10000u)
#define BLOCK_64K_PAGES (BLOCK_64K_SIZE / PAGE_SIZE)
#define BLOCK_32K_SIZE (0x8000u)
#define BLOCK_32K_PAGES (BLOCK_32K_SIZE / PAGE_SIZE)
#define SECTOR_SIZE (0x1000u)
#define SECTOR_PAGES (SECTOR_SIZE / PAGE_SIZE)
/* driver data structure */
struct flash_b91_data {
struct k_sem write_lock;
};
/* driver parameters structure */
static const struct flash_parameters flash_b91_parameters = {
.write_block_size = DT_PROP(DT_INST(0, soc_nv_flash), write_block_size),
.erase_value = 0xff,
};
/* Check for correct offset and length */
static bool flash_b91_is_range_valid(off_t offset, size_t len)
{
/* check for min value */
if ((offset < 0) || (len < 1)) {
return false;
}
/* check for max value */
if ((offset + len) > FLASH_SIZE) {
return false;
}
return true;
}
/* API implementation: driver initialization */
static int flash_b91_init(const struct device *dev)
{
struct flash_b91_data *dev_data = dev->data;
k_sem_init(&dev_data->write_lock, 1, 1);
return 0;
}
/* API implementation: erase */
static int flash_b91_erase(const struct device *dev, off_t offset, size_t len)
{
int page_nums = len / PAGE_SIZE;
struct flash_b91_data *dev_data = dev->data;
/* return SUCCESS if len equals 0 (required by tests/drivers/flash) */
if (!len) {
return 0;
}
/* check for valid range */
if (!flash_b91_is_range_valid(offset, len)) {
return -EINVAL;
}
/* erase can be done only by pages */
if (((offset % PAGE_SIZE) != 0) || ((len % PAGE_SIZE) != 0)) {
return -EINVAL;
}
/* take semaphore */
if (k_sem_take(&dev_data->write_lock, K_NO_WAIT)) {
return -EACCES;
}
while (page_nums) {
/* check for 64K erase possibility, then check for 32K and so on.. */
if ((page_nums >= BLOCK_64K_PAGES) && ((offset % BLOCK_64K_SIZE) == 0)) {
/* erase 64K block */
flash_erase_64kblock(offset);
page_nums -= BLOCK_64K_PAGES;
offset += BLOCK_64K_SIZE;
} else if ((page_nums >= BLOCK_32K_PAGES) && ((offset % BLOCK_32K_SIZE) == 0)) {
/* erase 32K block */
flash_erase_32kblock(offset);
page_nums -= BLOCK_32K_PAGES;
offset += BLOCK_32K_SIZE;
} else if ((page_nums >= SECTOR_PAGES) && ((offset % SECTOR_SIZE) == 0)) {
/* erase sector */
flash_erase_sector(offset);
page_nums -= SECTOR_PAGES;
offset += SECTOR_SIZE;
} else {
/* erase page */
flash_erase_page(offset);
page_nums--;
offset += PAGE_SIZE;
}
}
/* release semaphore */
k_sem_give(&dev_data->write_lock);
return 0;
}
/* API implementation: write */
static int flash_b91_write(const struct device *dev, off_t offset,
const void *data, size_t len)
{
void *buf = NULL;
struct flash_b91_data *dev_data = dev->data;
/* return SUCCESS if len equals 0 (required by tests/drivers/flash) */
if (!len) {
return 0;
}
/* check for valid range */
if (!flash_b91_is_range_valid(offset, len)) {
return -EINVAL;
}
/* take semaphore */
if (k_sem_take(&dev_data->write_lock, K_NO_WAIT)) {
return -EACCES;
}
/* need to store data in intermediate RAM buffer in case from flash to flash write */
if (((uint32_t)data >= FLASH_ORIGIN) &&
((uint32_t)data < (FLASH_ORIGIN + FLASH_SIZE))) {
buf = k_malloc(len);
if (buf == NULL) {
k_sem_give(&dev_data->write_lock);
return -ENOMEM;
}
/* copy Flash data to RAM */
memcpy(buf, data, len);
/* substitute data with allocated buffer */
data = buf;
}
/* write flash */
flash_write_page(offset, len, (unsigned char *)data);
/* if ram memory is allocated for flash writing it should be free */
if (buf != NULL) {
k_free(buf);
}
/* release semaphore */
k_sem_give(&dev_data->write_lock);
return 0;
}
/* API implementation: read */
static int flash_b91_read(const struct device *dev, off_t offset,
void *data, size_t len)
{
ARG_UNUSED(dev);
/* return SUCCESS if len equals 0 (required by tests/drivers/flash) */
if (!len) {
return 0;
}
/* check for valid range */
if (!flash_b91_is_range_valid(offset, len)) {
return -EINVAL;
}
/* read flash */
flash_read_page(offset, len, (unsigned char *)data);
return 0;
}
/* API implementation: get_parameters */
static const struct flash_parameters *
flash_b91_get_parameters(const struct device *dev)
{
ARG_UNUSED(dev);
return &flash_b91_parameters;
}
/* API implementation: page_layout */
#if defined(CONFIG_FLASH_PAGE_LAYOUT)
static const struct flash_pages_layout dev_layout = {
.pages_count = FLASH_SIZE / PAGE_SIZE,
.pages_size = PAGE_SIZE,
};
static void flash_b91_pages_layout(const struct device *dev,
const struct flash_pages_layout **layout,
size_t *layout_size)
{
*layout = &dev_layout;
*layout_size = 1;
}
#endif /* CONFIG_FLASH_PAGE_LAYOUT */
static struct flash_b91_data flash_data;
static const struct flash_driver_api flash_b91_api = {
.erase = flash_b91_erase,
.write = flash_b91_write,
.read = flash_b91_read,
.get_parameters = flash_b91_get_parameters,
#if defined(CONFIG_FLASH_PAGE_LAYOUT)
.page_layout = flash_b91_pages_layout,
#endif
};
/* Driver registration */
DEVICE_DT_INST_DEFINE(0, flash_b91_init,
NULL, &flash_data, NULL, POST_KERNEL,
CONFIG_FLASH_INIT_PRIORITY, &flash_b91_api);