flash/stm32: flash driver for STM32F3x series microcontrollers

Change-Id: I0b41a50507a09a513a67d13374323d831a0ec86a
Signed-off-by: Adam Podogrocki <adam.podogrocki@rndity.com>
This commit is contained in:
Adam Podogrocki 2016-11-02 15:34:02 +01:00 committed by Kumar Gala
commit 61cc74c425
9 changed files with 512 additions and 7 deletions

View file

@ -27,6 +27,10 @@ config SRAM_SIZE
config FLASH_SIZE
default 256
config FLASH_PAGE_SIZE
hex
default 0x800
config NUM_IRQS
int
default 82

View file

@ -27,6 +27,10 @@ config SRAM_SIZE
config FLASH_SIZE
default 64
config FLASH_PAGE_SIZE
hex
default 0x800
config NUM_IRQS
int
default 82

View file

@ -27,6 +27,10 @@ config SRAM_SIZE
config FLASH_SIZE
default 256
config FLASH_PAGE_SIZE
hex
default 0x800
config NUM_IRQS
int
default 82

View file

@ -108,6 +108,14 @@ config SOC_FLASH_QMSI_SYS_SIZE
help
Specify system flash size on the quark SOC.
config SOC_FLASH_QMSI_API_REENTRANCY
bool
prompt "flash driver API reentrancy for QMSI shim driver"
depends on SOC_FLASH_QMSI
default n
help
Enable support for QMSI flash driver API reentrancy.
config SOC_FLASH_NRF5
bool "Nordic Semiconductor nRF5X flash driver"
depends on FLASH && SOC_FAMILY_NRF5
@ -140,10 +148,4 @@ config SOC_FLASH_MCUX_DEV_NAME
help
Specify the device name for the flash driver.
config SOC_FLASH_QMSI_API_REENTRANCY
bool
prompt "flash driver API reentrancy for QMSI shim driver"
depends on SOC_FLASH_QMSI
default n
help
Enable support for QMSI flash driver API reentrancy.
source "drivers/flash/Kconfig.stm32f3x"

View file

@ -0,0 +1,35 @@
# Kconfig - ST Microelectronics STM32F3x MCUs Flash driver config
#
# Copyright (c) 2016 RnDity Sp. z o.o.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
if FLASH && SOC_SERIES_STM32F3X
menuconfig SOC_FLASH_STM32
bool
prompt "STM32 flash driver"
depends on SOC_SERIES_STM32F3X && FLASH
default n
help
Enable STM32F3X series MCUs flash driver.
config SOC_FLASH_STM32_DEV_NAME
string "STM32 flash device name"
depends on SOC_FLASH_STM32
default "FLASH_STM32"
help
Specify the device name for the flash driver.
endif

View file

@ -2,3 +2,8 @@ obj-$(CONFIG_SPI_FLASH_W25QXXDV) += spi_flash_w25qxxdv.o
obj-$(CONFIG_SOC_FLASH_QMSI) += soc_flash_qmsi.o
obj-$(CONFIG_SOC_FLASH_NRF5) += soc_flash_nrf5.o
obj-$(CONFIG_SOC_FLASH_MCUX) += soc_flash_mcux.o
ifeq ($(CONFIG_SOC_SERIES_STM32F3X),y)
obj-$(CONFIG_SOC_FLASH_STM32) += flash_stm32f3x.o
obj-$(CONFIG_SOC_FLASH_STM32) += flash_stm32f3x_priv.o
endif

View file

@ -0,0 +1,159 @@
/*
* Copyright (c) 2016 RnDity Sp. z o.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "flash_stm32f3x.h"
#include <errno.h>
#include <misc/__assert.h>
#include <clock_control/stm32_clock_control.h>
static int flash_stm32_erase(struct device *dev, off_t offset, size_t size)
{
uint32_t first_page_addr = 0;
uint32_t last_page_addr = 0;
uint16_t no_of_pages = size / CONFIG_FLASH_PAGE_SIZE;
uint16_t page_index = 0;
/* Check offset and size alignment. */
if (((offset % CONFIG_FLASH_PAGE_SIZE) != 0) ||
((size % CONFIG_FLASH_PAGE_SIZE) != 0) ||
(no_of_pages == 0)) {
return -EINVAL;
}
/* Find address of the first page to be erased. */
page_index = offset / CONFIG_FLASH_PAGE_SIZE;
first_page_addr = CONFIG_FLASH_BASE_ADDRESS +
page_index * CONFIG_FLASH_PAGE_SIZE;
__ASSERT_NO_MSG(IS_FLASH_PROGRAM_ADDRESS(first_page_addr));
/* Find address of the last page to be erased. */
page_index = ((offset + size) / CONFIG_FLASH_PAGE_SIZE) - 1;
last_page_addr = CONFIG_FLASH_BASE_ADDRESS +
page_index * CONFIG_FLASH_PAGE_SIZE;
__ASSERT_NO_MSG(IS_FLASH_PROGRAM_ADDRESS(last_page_addr));
while (no_of_pages) {
if (flash_stm32_erase_page(dev, first_page_addr)
!= FLASH_COMPLETE) {
return -EINVAL;
}
no_of_pages--;
first_page_addr += CONFIG_FLASH_PAGE_SIZE;
}
return 0;
}
static int flash_stm32_read(struct device *dev, off_t offset,
void *data, size_t len)
{
uint32_t address = CONFIG_FLASH_BASE_ADDRESS + offset;
__ASSERT_NO_MSG(IS_FLASH_PROGRAM_ADDRESS(address));
flash_stm32_read_data(data, address, len);
return 0;
}
static int flash_stm32_write(struct device *dev, off_t offset,
const void *data, size_t len)
{
uint16_t halfword = 0;
uint32_t address =
CONFIG_FLASH_BASE_ADDRESS + offset;
uint8_t remainder = 0;
if ((len % 2) != 0) {
remainder = 1;
}
len = len / 2;
while (len--) {
halfword = *((uint8_t *)data++);
halfword |= *((uint8_t *)data++) << 8;
if (flash_stm32_program_halfword(dev, address, halfword)
!= FLASH_COMPLETE) {
return -EINVAL;
}
address += 2;
}
if (remainder) {
halfword = (*((uint16_t *)data)) & 0x00FF;
if (flash_stm32_program_halfword(dev, address, halfword)
!= FLASH_COMPLETE) {
return -EINVAL;
}
}
return 0;
}
static int flash_stm32_protection_set(struct device *dev, bool enable)
{
if (enable) {
flash_stm32_lock(dev);
} else {
flash_stm32_unlock(dev);
}
return 0;
}
static int flash_stm32_init(struct device *dev)
{
const struct flash_stm32_dev_config *cfg = FLASH_CFG(dev);
struct device *clk = device_get_binding(STM32_CLOCK_CONTROL_NAME);
if (clock_control_on(clk, cfg->clock_subsys) != 0)
return -ENODEV;
return 0;
}
static const struct flash_driver_api flash_stm32_api = {
.read = flash_stm32_read,
.write = flash_stm32_write,
.erase = flash_stm32_erase,
.write_protection = flash_stm32_protection_set,
};
static const struct flash_stm32_dev_config flash_device_config = {
.base = (uint32_t *)FLASH_R_BASE,
.clock_subsys = UINT_TO_POINTER(STM32F3X_CLOCK_SUBSYS_FLITF),
};
static struct flash_stm32_dev_data flash_device_data = {
};
DEVICE_AND_API_INIT(flash_stm32, CONFIG_SOC_FLASH_STM32_DEV_NAME,
flash_stm32_init,
&flash_device_data,
&flash_device_config,
POST_KERNEL,
CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
&flash_stm32_api);

View file

@ -0,0 +1,76 @@
/*
* Copyright (c) 2016 RnDity Sp. z o.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef DRIVERS_FLASH_FLASH_STM32_H_
#define DRIVERS_FLASH_FLASH_STM32_H_
#include <soc.h>
#include <flash.h>
#include <clock_control.h>
#include <flash_registers.h>
struct flash_stm32_dev_config {
uint32_t *base;
clock_control_subsys_t clock_subsys;
};
struct flash_stm32_dev_data {
/* For future use. */
};
#define FLASH_CFG(dev) \
((const struct flash_stm32_dev_config * const)(dev)->config->config_info)
#define FLASH_DATA(dev) \
((struct flash_stm32_dev_data * const)(dev)->driver_data)
#define FLASH_STRUCT(base) \
(volatile struct stm32_flash *)(base)
/* Flash programming timeout definition. */
#define FLASH_ER_PRG_TIMEOUT ((uint32_t)0x000B0000)
enum flash_status {
FLASH_BUSY = 1,
FLASH_ERROR_WRITE_PROTECTION,
FLASH_ERROR_PROGRAM,
FLASH_COMPLETE,
FLASH_TIMEOUT
};
void flash_stm32_lock(struct device *flash);
void flash_stm32_unlock(struct device *flash);
uint8_t flash_stm32_program_halfword(struct device *flash,
uint32_t address,
uint16_t data);
uint8_t flash_stm32_program_word(struct device *flash,
uint32_t address,
uint32_t data);
void flash_stm32_read_data(void *data, uint32_t address, size_t len);
uint8_t flash_stm32_wait_for_last_operation(struct device *flash,
uint32_t timeout);
uint8_t flash_stm32_get_status(struct device *flash);
uint8_t flash_stm32_erase_page(struct device *flash,
uint32_t page_address);
uint8_t flash_stm32_erase_all_pages(struct device *flash);
#endif /* DRIVERS_FLASH_FLASH_STM32_H_ */

View file

@ -0,0 +1,216 @@
/*
* Copyright (c) 2016 RnDity Sp. z o.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "flash_stm32f3x.h"
#include <misc/__assert.h>
#include <string.h>
void flash_stm32_unlock(struct device *flash)
{
const struct flash_stm32_dev_config *config = FLASH_CFG(flash);
volatile struct stm32_flash *reg = FLASH_STRUCT(config->base);
if ((reg->cr & FLASH_CR_LOCK) != 0) {
/* Authorize the FLASH Registers access */
reg->keyr = FLASH_KEY1;
reg->keyr = FLASH_KEY2;
}
}
void flash_stm32_lock(struct device *flash)
{
const struct flash_stm32_dev_config *config = FLASH_CFG(flash);
volatile struct stm32_flash *reg = FLASH_STRUCT(config->base);
reg->cr |= FLASH_CR_LOCK;
}
uint8_t flash_stm32_program_halfword(struct device *flash,
uint32_t address,
uint16_t data)
{
uint8_t status = FLASH_COMPLETE;
const struct flash_stm32_dev_config *config = FLASH_CFG(flash);
volatile struct stm32_flash *reg = FLASH_STRUCT(config->base);
__ASSERT_NO_MSG(IS_FLASH_PROGRAM_ADDRESS(address));
status = flash_stm32_wait_for_last_operation(flash,
FLASH_ER_PRG_TIMEOUT);
if (status == FLASH_COMPLETE) {
reg->cr |= FLASH_CR_PG;
*(volatile uint16_t *)address = data;
status = flash_stm32_wait_for_last_operation(flash,
FLASH_ER_PRG_TIMEOUT);
reg->cr &= ~FLASH_CR_PG;
}
return status;
}
uint8_t flash_stm32_program_word(struct device *flash,
uint32_t address,
uint32_t data)
{
uint8_t status = FLASH_COMPLETE;
const struct flash_stm32_dev_config *config = FLASH_CFG(flash);
volatile struct stm32_flash *reg = FLASH_STRUCT(config->base);
__ASSERT_NO_MSG(IS_FLASH_PROGRAM_ADDRESS(address));
status = flash_stm32_wait_for_last_operation(flash,
FLASH_ER_PRG_TIMEOUT);
if (status == FLASH_COMPLETE) {
reg->cr |= FLASH_CR_PG;
*(volatile uint16_t *)address = (uint16_t)data;
status = flash_stm32_wait_for_last_operation(flash,
FLASH_ER_PRG_TIMEOUT);
if (status == FLASH_COMPLETE) {
address += 2;
*(volatile uint16_t *)address = data >> 16;
status = flash_stm32_wait_for_last_operation(flash,
FLASH_ER_PRG_TIMEOUT);
}
reg->cr &= ~FLASH_CR_PG;
}
return status;
}
uint8_t flash_stm32_wait_for_last_operation(struct device *flash,
uint32_t timeout)
{
uint8_t status = FLASH_COMPLETE;
/* Check for the FLASH Status */
status = flash_stm32_get_status(flash);
/* Wait for a FLASH operation to complete or a TIMEOUT to occur. */
while ((status == FLASH_BUSY) && (timeout != 0x00)) {
status = flash_stm32_get_status(flash);
timeout--;
}
if (timeout == 0x00) {
status = FLASH_TIMEOUT;
}
return status;
}
uint8_t flash_stm32_get_status(struct device *flash)
{
uint8_t status = FLASH_COMPLETE;
const struct flash_stm32_dev_config *config = FLASH_CFG(flash);
volatile struct stm32_flash *reg = FLASH_STRUCT(config->base);
do {
if ((reg->sr & FLASH_SR_BSY) == FLASH_SR_BSY) {
status = FLASH_BUSY;
break;
}
if ((reg->sr & FLASH_SR_WRPERR) != (uint32_t)0x00) {
status = FLASH_ERROR_WRITE_PROTECTION;
break;
}
if ((reg->sr & FLASH_SR_PGERR) != (uint32_t)0x00) {
status = FLASH_ERROR_PROGRAM;
break;
}
} while (0);
return status;
}
uint8_t flash_stm32_erase_page(struct device *flash,
uint32_t page_address)
{
uint8_t status = FLASH_COMPLETE;
const struct flash_stm32_dev_config *config = FLASH_CFG(flash);
volatile struct stm32_flash *reg = FLASH_STRUCT(config->base);
__ASSERT_NO_MSG(IS_FLASH_PROGRAM_ADDRESS(page_address));
status = flash_stm32_wait_for_last_operation(flash,
FLASH_ER_PRG_TIMEOUT);
if (status == FLASH_COMPLETE) {
reg->cr |= FLASH_CR_PER;
reg->ar = page_address;
reg->cr |= FLASH_CR_STRT;
status = flash_stm32_wait_for_last_operation(flash,
FLASH_ER_PRG_TIMEOUT);
reg->cr &= ~FLASH_CR_PER;
}
return status;
}
uint8_t flash_stm32_erase_all_pages(struct device *flash)
{
uint8_t status = FLASH_COMPLETE;
const struct flash_stm32_dev_config *config = FLASH_CFG(flash);
volatile struct stm32_flash *reg = FLASH_STRUCT(config->base);
status = flash_stm32_wait_for_last_operation(flash,
FLASH_ER_PRG_TIMEOUT);
if (status == FLASH_COMPLETE) {
reg->cr |= FLASH_CR_MER;
reg->cr |= FLASH_CR_STRT;
status = flash_stm32_wait_for_last_operation(flash,
FLASH_ER_PRG_TIMEOUT);
reg->cr &= ~FLASH_CR_MER;
}
return status;
}
void flash_stm32_read_data(void *data, uint32_t address, size_t len)
{
uint8_t *addr = INT_TO_POINTER(address);
memcpy(data, addr, len);
}