diff --git a/arch/arm/soc/st_stm32/stm32f3/Kconfig.defconfig.stm32f303xc b/arch/arm/soc/st_stm32/stm32f3/Kconfig.defconfig.stm32f303xc index 200a82c7ecb..59d9271f72a 100644 --- a/arch/arm/soc/st_stm32/stm32f3/Kconfig.defconfig.stm32f303xc +++ b/arch/arm/soc/st_stm32/stm32f3/Kconfig.defconfig.stm32f303xc @@ -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 diff --git a/arch/arm/soc/st_stm32/stm32f3/Kconfig.defconfig.stm32f334x8 b/arch/arm/soc/st_stm32/stm32f3/Kconfig.defconfig.stm32f334x8 index 9929e023591..ab0ac442ae3 100644 --- a/arch/arm/soc/st_stm32/stm32f3/Kconfig.defconfig.stm32f334x8 +++ b/arch/arm/soc/st_stm32/stm32f3/Kconfig.defconfig.stm32f334x8 @@ -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 diff --git a/arch/arm/soc/st_stm32/stm32f3/Kconfig.defconfig.stm32f373xc b/arch/arm/soc/st_stm32/stm32f3/Kconfig.defconfig.stm32f373xc index 294f110a054..e6dcf95f6ea 100644 --- a/arch/arm/soc/st_stm32/stm32f3/Kconfig.defconfig.stm32f373xc +++ b/arch/arm/soc/st_stm32/stm32f3/Kconfig.defconfig.stm32f373xc @@ -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 diff --git a/drivers/flash/Kconfig b/drivers/flash/Kconfig index d59421dc37a..07f4c24fe44 100644 --- a/drivers/flash/Kconfig +++ b/drivers/flash/Kconfig @@ -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" diff --git a/drivers/flash/Kconfig.stm32f3x b/drivers/flash/Kconfig.stm32f3x new file mode 100644 index 00000000000..6b740019aad --- /dev/null +++ b/drivers/flash/Kconfig.stm32f3x @@ -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 diff --git a/drivers/flash/Makefile b/drivers/flash/Makefile index 370932a4342..5009e6e25a3 100644 --- a/drivers/flash/Makefile +++ b/drivers/flash/Makefile @@ -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 diff --git a/drivers/flash/flash_stm32f3x.c b/drivers/flash/flash_stm32f3x.c new file mode 100644 index 00000000000..8ad980bbfc8 --- /dev/null +++ b/drivers/flash/flash_stm32f3x.c @@ -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 +#include +#include + +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); diff --git a/drivers/flash/flash_stm32f3x.h b/drivers/flash/flash_stm32f3x.h new file mode 100644 index 00000000000..7762bec61b7 --- /dev/null +++ b/drivers/flash/flash_stm32f3x.h @@ -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 +#include +#include +#include + +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_ */ diff --git a/drivers/flash/flash_stm32f3x_priv.c b/drivers/flash/flash_stm32f3x_priv.c new file mode 100644 index 00000000000..0434b15bea6 --- /dev/null +++ b/drivers/flash/flash_stm32f3x_priv.c @@ -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 +#include + +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); +}