Moved fetch of flash device bindings to early initialization of the application. Device bindings are constant while the application is running so it is better to fetch it at startup, and not every time flash_map procedures are called. Signed-off-by: Andrzej Puzdrowski <andrzej.puzdrowski@nordicsemi.no>
282 lines
6.0 KiB
C
282 lines
6.0 KiB
C
/*
|
|
* Copyright (c) 2017 Nordic Semiconductor ASA
|
|
* Copyright (c) 2015 Runtime Inc
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <zephyr/types.h>
|
|
#include <stddef.h>
|
|
#include <sys/types.h>
|
|
#include <device.h>
|
|
#include <flash_map.h>
|
|
#include <flash.h>
|
|
#include <soc.h>
|
|
#include <init.h>
|
|
|
|
#if defined(CONFIG_FLASH_PAGE_LAYOUT)
|
|
struct layout_data {
|
|
u32_t area_idx;
|
|
u32_t area_off;
|
|
u32_t area_len;
|
|
void *ret; /* struct flash_area* or struct flash_sector* */
|
|
u32_t ret_idx;
|
|
u32_t ret_len;
|
|
int status;
|
|
};
|
|
#endif /* CONFIG_FLASH_PAGE_LAYOUT */
|
|
|
|
struct driver_map_entry {
|
|
u8_t id;
|
|
const char * const name;
|
|
};
|
|
|
|
static const struct driver_map_entry flash_drivers_map[] = {
|
|
#ifdef FLASH_DRIVER_NAME /* SoC embedded flash driver */
|
|
{SOC_FLASH_0_ID, FLASH_DRIVER_NAME},
|
|
#endif
|
|
#ifdef CONFIG_SPI_FLASH_W25QXXDV
|
|
{SPI_FLASH_0_ID, CONFIG_SPI_FLASH_W25QXXDV_DRV_NAME},
|
|
#endif
|
|
};
|
|
|
|
const struct flash_area *flash_map;
|
|
extern const int flash_map_entries;
|
|
static struct device *flash_dev[ARRAY_SIZE(flash_drivers_map)];
|
|
|
|
int flash_area_open(u8_t id, const struct flash_area **fap)
|
|
{
|
|
const struct flash_area *area;
|
|
int i;
|
|
|
|
if (flash_map == NULL) {
|
|
return -EACCES;
|
|
}
|
|
|
|
for (i = 0; i < flash_map_entries; i++) {
|
|
area = flash_map + i;
|
|
if (area->fa_id == id) {
|
|
*fap = area;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return -ENOENT;
|
|
}
|
|
|
|
void flash_area_close(const struct flash_area *fa)
|
|
{
|
|
/* nothing to do for now */
|
|
}
|
|
|
|
static struct device *get_flah_dev_from_id(u8_t id)
|
|
{
|
|
for (int i = 0; i < ARRAY_SIZE(flash_drivers_map); i++) {
|
|
if (flash_drivers_map[i].id == id) {
|
|
return flash_dev[i];
|
|
}
|
|
}
|
|
|
|
k_panic();
|
|
}
|
|
|
|
static inline bool is_in_flash_area_bounds(const struct flash_area *fa,
|
|
off_t off, size_t len)
|
|
{
|
|
return (off <= fa->fa_size && off + len <= fa->fa_size);
|
|
}
|
|
|
|
struct flash_area const *get_flash_area_from_id(int idx)
|
|
{
|
|
for (int i = 0; i < flash_map_entries; i++) {
|
|
if (flash_map[i].fa_id == idx) {
|
|
return &flash_map[i];
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
#if defined(CONFIG_FLASH_PAGE_LAYOUT)
|
|
/*
|
|
* Check if a flash_page_foreach() callback should exit early, due to
|
|
* one of the following conditions:
|
|
*
|
|
* - The flash page described by "info" is before the area of interest
|
|
* described in "data"
|
|
* - The flash page is after the end of the area
|
|
* - There are too many flash pages on the device to fit in the array
|
|
* held in data->ret. In this case, data->status is set to -ENOMEM.
|
|
*
|
|
* The value to return to flash_page_foreach() is stored in
|
|
* "bail_value" if the callback should exit early.
|
|
*/
|
|
static bool should_bail(const struct flash_pages_info *info,
|
|
struct layout_data *data,
|
|
bool *bail_value)
|
|
{
|
|
if (info->start_offset < data->area_off) {
|
|
*bail_value = true;
|
|
return true;
|
|
} else if (info->start_offset >= data->area_off + data->area_len) {
|
|
*bail_value = false;
|
|
return true;
|
|
} else if (data->ret_idx >= data->ret_len) {
|
|
data->status = -ENOMEM;
|
|
*bail_value = false;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* Generic page layout discovery routine. This is kept separate to
|
|
* support both the deprecated flash_area_to_sectors() and the current
|
|
* flash_area_get_sectors(). A lot of this can be inlined once
|
|
* flash_area_to_sectors() is removed.
|
|
*/
|
|
static int flash_area_layout(int idx, int *cnt, void *ret,
|
|
flash_page_cb cb, struct layout_data *cb_data)
|
|
{
|
|
struct device *flash_dev;
|
|
|
|
cb_data->area_idx = idx;
|
|
|
|
const struct flash_area *fa;
|
|
|
|
fa = get_flash_area_from_id(idx);
|
|
if (fa == NULL) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
cb_data->area_idx = idx;
|
|
cb_data->area_off = fa->fa_off;
|
|
cb_data->area_len = fa->fa_size;
|
|
|
|
cb_data->ret = ret;
|
|
cb_data->ret_idx = 0;
|
|
cb_data->ret_len = *cnt;
|
|
cb_data->status = 0;
|
|
|
|
flash_dev = get_flah_dev_from_id(fa->fa_device_id);
|
|
|
|
flash_page_foreach(flash_dev, cb, cb_data);
|
|
|
|
if (cb_data->status == 0) {
|
|
*cnt = cb_data->ret_idx;
|
|
}
|
|
|
|
return cb_data->status;
|
|
}
|
|
|
|
static bool get_sectors_cb(const struct flash_pages_info *info, void *datav)
|
|
{
|
|
struct layout_data *data = datav;
|
|
struct flash_sector *ret = data->ret;
|
|
bool bail;
|
|
|
|
if (should_bail(info, data, &bail)) {
|
|
return bail;
|
|
}
|
|
|
|
ret[data->ret_idx].fs_off = info->start_offset - data->area_off;
|
|
ret[data->ret_idx].fs_size = info->size;
|
|
data->ret_idx++;
|
|
|
|
return true;
|
|
}
|
|
|
|
int flash_area_get_sectors(int idx, uint32_t *cnt, struct flash_sector *ret)
|
|
{
|
|
struct layout_data data;
|
|
|
|
return flash_area_layout(idx, cnt, ret, get_sectors_cb, &data);
|
|
}
|
|
#endif /* CONFIG_FLASH_PAGE_LAYOUT */
|
|
|
|
int flash_area_read(const struct flash_area *fa, off_t off, void *dst,
|
|
size_t len)
|
|
{
|
|
struct device *dev;
|
|
|
|
if (!is_in_flash_area_bounds(fa, off, len)) {
|
|
return -1;
|
|
}
|
|
|
|
dev = get_flah_dev_from_id(fa->fa_device_id);
|
|
|
|
return flash_read(dev, fa->fa_off + off, dst, len);
|
|
}
|
|
|
|
int flash_area_write(const struct flash_area *fa, off_t off, const void *src,
|
|
size_t len)
|
|
{
|
|
struct device *flash_dev;
|
|
int rc;
|
|
|
|
if (!is_in_flash_area_bounds(fa, off, len)) {
|
|
return -1;
|
|
}
|
|
|
|
flash_dev = get_flah_dev_from_id(fa->fa_device_id);
|
|
|
|
rc = flash_write_protection_set(flash_dev, false);
|
|
if (rc) {
|
|
return rc;
|
|
}
|
|
|
|
rc = flash_write(flash_dev, fa->fa_off + off, (void *)src, len);
|
|
|
|
/* Ignore errors here - this does not affect write operation */
|
|
(void) flash_write_protection_set(flash_dev, true);
|
|
|
|
return rc;
|
|
}
|
|
|
|
int flash_area_erase(const struct flash_area *fa, off_t off, size_t len)
|
|
{
|
|
struct device *flash_dev;
|
|
int rc;
|
|
|
|
if (!is_in_flash_area_bounds(fa, off, len)) {
|
|
return -1;
|
|
}
|
|
|
|
flash_dev = get_flah_dev_from_id(fa->fa_device_id);
|
|
|
|
rc = flash_write_protection_set(flash_dev, false);
|
|
if (rc) {
|
|
return rc;
|
|
}
|
|
|
|
rc = flash_erase(flash_dev, fa->fa_off + off, len);
|
|
|
|
/* Ignore errors here - this does not affect write operation */
|
|
(void) flash_write_protection_set(flash_dev, true);
|
|
|
|
return rc;
|
|
}
|
|
|
|
u8_t flash_area_align(const struct flash_area *fa)
|
|
{
|
|
struct device *dev;
|
|
|
|
dev = get_flah_dev_from_id(fa->fa_device_id);
|
|
|
|
return flash_get_write_block_size(dev);
|
|
}
|
|
|
|
static int flash_map_init(struct device *dev)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(flash_dev); i++) {
|
|
flash_dev[i] = device_get_binding(flash_drivers_map[i].name);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
SYS_INIT(flash_map_init, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY);
|