zephyr/drivers/flash/soc_flash_mcux.c
Marti Bolivar 4e6e7374c4 flash: mcux: implement software write protection (HACK)
Write protection is currently not supported by the mcux flash
driver. This prevents it from working with the DFU subsystem, which
checks the error codes from the write protect routine. Hack it for now
with a semaphore.

Signed-off-by: Marti Bolivar <marti@opensourcefoundries.com>
2018-02-06 15:39:45 -06:00

158 lines
3.5 KiB
C

/*
* Copyright (c) 2016 Linaro Limited
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <logging/sys_log.h>
#include <kernel.h>
#include <device.h>
#include <string.h>
#include <flash.h>
#include <errno.h>
#include <init.h>
#include <soc.h>
#include "flash_priv.h"
#include "fsl_common.h"
#include "fsl_flash.h"
struct flash_priv {
flash_config_t config;
/*
* HACK: flash write protection is managed in software.
*/
struct k_sem write_lock;
};
/*
* Interrupt vectors could be executed from flash hence the need for locking.
* The underlying MCUX driver takes care of copying the functions to SRAM.
*
* For more information, see the application note below on Read-While-Write
* http://cache.freescale.com/files/32bit/doc/app_note/AN4695.pdf
*
*/
static int flash_mcux_erase(struct device *dev, off_t offset, size_t len)
{
struct flash_priv *priv = dev->driver_data;
u32_t addr;
status_t rc;
int key;
if (k_sem_take(&priv->write_lock, K_NO_WAIT)) {
return -EACCES;
}
addr = offset + priv->config.PFlashBlockBase;
key = irq_lock();
rc = FLASH_Erase(&priv->config, addr, len, kFLASH_ApiEraseKey);
irq_unlock(key);
k_sem_give(&priv->write_lock);
return (rc == kStatus_Success) ? 0 : -EINVAL;
}
static int flash_mcux_read(struct device *dev, off_t offset,
void *data, size_t len)
{
struct flash_priv *priv = dev->driver_data;
u32_t addr;
/*
* The MCUX supports different flash chips whose valid ranges are
* hidden below the API: until the API export these ranges, we can not
* do any generic validation
*/
addr = offset + priv->config.PFlashBlockBase;
memcpy(data, (void *) addr, len);
return 0;
}
static int flash_mcux_write(struct device *dev, off_t offset,
const void *data, size_t len)
{
struct flash_priv *priv = dev->driver_data;
u32_t addr;
status_t rc;
int key;
if (k_sem_take(&priv->write_lock, K_NO_WAIT)) {
return -EACCES;
}
addr = offset + priv->config.PFlashBlockBase;
key = irq_lock();
rc = FLASH_Program(&priv->config, addr, (u32_t *) data, len);
irq_unlock(key);
k_sem_give(&priv->write_lock);
return (rc == kStatus_Success) ? 0 : -EINVAL;
}
static int flash_mcux_write_protection(struct device *dev, bool enable)
{
struct flash_priv *priv = dev->driver_data;
int rc = 0;
if (enable) {
rc = k_sem_take(&priv->write_lock, K_FOREVER);
} else {
k_sem_give(&priv->write_lock);
}
return rc;
}
#if defined(CONFIG_FLASH_PAGE_LAYOUT)
static const struct flash_pages_layout dev_layout = {
.pages_count = KB(CONFIG_FLASH_SIZE) / FLASH_ERASE_BLOCK_SIZE,
.pages_size = FLASH_ERASE_BLOCK_SIZE,
};
static void flash_mcux_pages_layout(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_priv flash_data;
static const struct flash_driver_api flash_mcux_api = {
.write_protection = flash_mcux_write_protection,
.erase = flash_mcux_erase,
.write = flash_mcux_write,
.read = flash_mcux_read,
#if defined(CONFIG_FLASH_PAGE_LAYOUT)
.page_layout = flash_mcux_pages_layout,
#endif
.write_block_size = FSL_FEATURE_FLASH_PFLASH_BLOCK_WRITE_UNIT_SIZE,
};
static int flash_mcux_init(struct device *dev)
{
struct flash_priv *priv = dev->driver_data;
status_t rc;
k_sem_init(&priv->write_lock, 0, 1);
rc = FLASH_Init(&priv->config);
return (rc == kStatus_Success) ? 0 : -EIO;
}
DEVICE_AND_API_INIT(flash_mcux, FLASH_DEV_NAME,
flash_mcux_init, &flash_data, NULL, POST_KERNEL,
CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &flash_mcux_api);