diff --git a/arch/nios2/soc/nios2f-zephyr/Kconfig.defconfig b/arch/nios2/soc/nios2f-zephyr/Kconfig.defconfig index e570b037703..0d27a60db37 100644 --- a/arch/nios2/soc/nios2f-zephyr/Kconfig.defconfig +++ b/arch/nios2/soc/nios2f-zephyr/Kconfig.defconfig @@ -18,6 +18,13 @@ config ALTERA_AVALON_PIO endif # GPIO_ALTERA_NIOS2 +if SOC_FLASH_NIOS2_QSPI + +config ALTERA_AVALON_QSPI + def_bool y + +endif #SOC_FLASH_NIOS2_QSPI + if UART_NS16550 config UART_NS16550_PCI diff --git a/drivers/flash/CMakeLists.txt b/drivers/flash/CMakeLists.txt index c34ee6588ef..05908cf52d5 100644 --- a/drivers/flash/CMakeLists.txt +++ b/drivers/flash/CMakeLists.txt @@ -5,6 +5,7 @@ zephyr_sources_ifdef(CONFIG_SOC_FLASH_MCUX soc_flash_mcux.c) zephyr_sources_ifdef(CONFIG_FLASH_PAGE_LAYOUT flash_page_layout.c) zephyr_sources_ifdef(CONFIG_USERSPACE flash_handlers.c) zephyr_sources_ifdef(CONFIG_SOC_FLASH_SAM0 flash_sam0.c) +zephyr_sources_ifdef(CONFIG_SOC_FLASH_NIOS2_QSPI soc_flash_nios2_qspi.c) if(CONFIG_SOC_SERIES_STM32F0X) zephyr_sources_ifdef(CONFIG_SOC_FLASH_STM32 diff --git a/drivers/flash/Kconfig b/drivers/flash/Kconfig index 3900768e2fb..39980e7675d 100644 --- a/drivers/flash/Kconfig +++ b/drivers/flash/Kconfig @@ -150,6 +150,20 @@ config SOC_FLASH_MCUX have an impact on the overall system performance - whether this is acceptable or not will depend on the use case. +config SOC_FLASH_NIOS2_QSPI + bool "Nios-II QSPI flash driver" + depends on FLASH && HAS_ALTERA_HAL + default n + help + Enables the Nios-II QSPI flash driver. + +config SOC_FLASH_NIOS2_QSPI_DEV_NAME + string "Nios-II QSPI flash device name" + depends on SOC_FLASH_NIOS2_QSPI + default "NIOS2_QSPI_FLASH" + help + Specify the device name for the QSPI flash driver. + source "drivers/flash/Kconfig.stm32" source "drivers/flash/Kconfig.sam0" diff --git a/drivers/flash/soc_flash_nios2_qspi.c b/drivers/flash/soc_flash_nios2_qspi.c new file mode 100644 index 00000000000..828d04a6a29 --- /dev/null +++ b/drivers/flash/soc_flash_nios2_qspi.c @@ -0,0 +1,457 @@ +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* + * This driver is written based on the Altera's + * Nios-II QSPI Controller HAL driver. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "flash_priv.h" +#include "altera_generic_quad_spi_controller2_regs.h" +#include "altera_generic_quad_spi_controller2.h" + +/* + * Remove the following macros once the Altera HAL + * supports the QSPI Controller v2 IP. + */ +#define ALTERA_QSPI_CONTROLLER2_FLAG_STATUS_REG 0x0000001C +#define FLAG_STATUS_PROTECTION_ERROR (1 << 1) +#define FLAG_STATUS_PROGRAM_SUSPENDED (1 << 2) +#define FLAG_STATUS_PROGRAM_ERROR (1 << 4) +#define FLAG_STATUS_ERASE_ERROR (1 << 5) +#define FLAG_STATUS_ERASE_SUSPENDED (1 << 6) +#define FLAG_STATUS_CONTROLLER_READY (1 << 7) + +/* ALTERA_QSPI_CONTROLLER2_STATUS_REG bits */ +#define STATUS_PROTECTION_POS 2 +#define STATUS_PROTECTION_MASK 0x1F +#define STATUS_PROTECTION_EN_VAL 0x17 +#define STATUS_PROTECTION_DIS_VAL 0x0 + +/* ALTERA_QSPI_CONTROLLER2_MEM_OP_REG bits */ +#define MEM_OP_ERASE_CMD 0x00000002 +#define MEM_OP_WRITE_EN_CMD 0x00000004 +#define MEM_OP_SECTOR_OFFSET_BIT_POS 8 +#define MEM_OP_UNLOCK_ALL_SECTORS 0x00000003 +#define MEM_OP_LOCK_ALL_SECTORS 0x00000F03 + +#define NIOS2_QSPI_BLANK_WORD 0xFFFFFFFF + +#define NIOS2_WRITE_BLOCK_SIZE 4 + +#define USEC_TO_MSEC(x) (x / 1000) + +struct flash_nios2_qspi_config { + alt_qspi_controller2_dev qspi_dev; + struct k_sem sem_lock; +}; + +static int flash_nios2_qspi_erase(struct device *dev, off_t offset, size_t len) +{ + struct flash_nios2_qspi_config *flash_cfg = dev->driver_data; + alt_qspi_controller2_dev *qspi_dev = &flash_cfg->qspi_dev; + u32_t block_offset, offset_in_block, length_to_erase; + u32_t erase_offset = offset; /* address of next byte to erase */ + u32_t remaining_length = len; /* length of data left to be erased */ + u32_t flag_status; + s32_t rc = 0, i, timeout; + + k_sem_take(&flash_cfg->sem_lock, K_FOREVER); + /* + * check if offset is word aligned and + * length is with in the range + */ + if (((offset + len) > qspi_dev->data_end) || + (0 != (erase_offset & + (NIOS2_WRITE_BLOCK_SIZE - 1)))) { + SYS_LOG_ERR("erase failed at offset %ld\n", offset); + rc = -EINVAL; + goto qspi_erase_err; + } + + for (i = offset/qspi_dev->sector_size; + i < qspi_dev->number_of_sectors; i++) { + + if ((remaining_length <= 0) || + erase_offset >= (offset + len)) { + break; + } + + block_offset = 0; /* block offset in byte addressing */ + offset_in_block = 0; /* offset into current sector to erase */ + length_to_erase = 0; /* length to erase in current sector */ + + /* calculate current sector/block offset in byte addressing */ + block_offset = erase_offset & ~(qspi_dev->sector_size - 1); + + /* calculate offset into sector/block if there is one */ + if (block_offset != erase_offset) { + offset_in_block = erase_offset - block_offset; + } + + /* calculate the byte size of data to be written in a sector */ + length_to_erase = min(qspi_dev->sector_size - offset_in_block, + remaining_length); + + /* Erase sector */ + IOWR_32DIRECT(qspi_dev->csr_base, + ALTERA_QSPI_CONTROLLER2_MEM_OP_REG, + MEM_OP_WRITE_EN_CMD); + IOWR_32DIRECT(qspi_dev->csr_base, + ALTERA_QSPI_CONTROLLER2_MEM_OP_REG, + (i << MEM_OP_SECTOR_OFFSET_BIT_POS) + | MEM_OP_ERASE_CMD); + + /* + * poll the status register to know the + * completion of the erase operation. + */ + timeout = ALTERA_QSPI_CONTROLLER2_1US_TIMEOUT_VALUE; + while (timeout > 0) { + /* wait for 1 usec */ + k_busy_wait(1); + + flag_status = IORD_32DIRECT(qspi_dev->csr_base, + ALTERA_QSPI_CONTROLLER2_FLAG_STATUS_REG); + + if (flag_status & FLAG_STATUS_CONTROLLER_READY) { + break; + } + + timeout--; + } + + if ((flag_status & FLAG_STATUS_ERASE_ERROR) || + (flag_status & FLAG_STATUS_PROTECTION_ERROR)) { + SYS_LOG_ERR("erase failed, Flag Status Reg:%x\n", + flag_status); + rc = -EIO; + goto qspi_erase_err; + } + + /* update remaining length and erase_offset */ + remaining_length -= length_to_erase; + erase_offset += length_to_erase; + } + +qspi_erase_err: + k_sem_give(&flash_cfg->sem_lock); + return rc; + +} + +static int flash_nios2_qspi_write_block(struct device *dev, int block_offset, + int mem_offset, const void *data, + size_t len) +{ + struct flash_nios2_qspi_config *flash_cfg = dev->driver_data; + alt_qspi_controller2_dev *qspi_dev = &flash_cfg->qspi_dev; + u32_t buffer_offset = 0; /* offset into data buffer to get write data */ + u32_t remaining_length = len; /* length left to write */ + u32_t write_offset = mem_offset; /* offset into flash to write too */ + u32_t word_to_write, padding, bytes_to_copy; + u32_t flag_status; + s32_t rc = 0; + + while (remaining_length > 0) { + /* initialize word to write to blank word */ + word_to_write = NIOS2_QSPI_BLANK_WORD; + + /* bytes to pad the next word that is written */ + padding = 0; + + /* number of bytes from source to copy */ + bytes_to_copy = NIOS2_WRITE_BLOCK_SIZE; + + /* + * we need to make sure the write is word aligned + * this should only be true at most 1 time + */ + if (0 != (write_offset & (NIOS2_WRITE_BLOCK_SIZE - 1))) { + /* + * data is not word aligned calculate padding bytes + * need to add before start of a data offset + */ + padding = write_offset & (NIOS2_WRITE_BLOCK_SIZE - 1); + + /* + * update variables to account + * for padding being added + */ + bytes_to_copy -= padding; + + if (bytes_to_copy > remaining_length) { + bytes_to_copy = remaining_length; + } + + write_offset = write_offset - padding; + + if (0 != (write_offset & + (NIOS2_WRITE_BLOCK_SIZE - 1))) { + rc = -EINVAL; + goto qspi_write_block_err; + } + } else { + if (bytes_to_copy > remaining_length) { + bytes_to_copy = remaining_length; + } + } + + /* prepare the word to be written */ + memcpy(&word_to_write + padding, + data + buffer_offset, bytes_to_copy); + + /* enable write */ + IOWR_32DIRECT(qspi_dev->csr_base, + ALTERA_QSPI_CONTROLLER2_MEM_OP_REG, + MEM_OP_WRITE_EN_CMD); + + /* write to flash 32 bits at a time */ + IOWR_32DIRECT(qspi_dev->data_base, write_offset, word_to_write); + + /* check whether write operation is successful */ + flag_status = IORD_32DIRECT(qspi_dev->csr_base, + ALTERA_QSPI_CONTROLLER2_FLAG_STATUS_REG); + + if ((flag_status & FLAG_STATUS_PROGRAM_ERROR) || + (flag_status & FLAG_STATUS_PROTECTION_ERROR)) { + SYS_LOG_ERR("write failed, Flag Status Reg:%x\n", + flag_status); + rc = -EIO; /* sector might be protected */ + goto qspi_write_block_err; + } + + /* update offset and length variables */ + buffer_offset += bytes_to_copy; + remaining_length -= bytes_to_copy; + write_offset = write_offset + NIOS2_WRITE_BLOCK_SIZE; + } + +qspi_write_block_err: + return rc; +} + +static int flash_nios2_qspi_write(struct device *dev, off_t offset, + const void *data, size_t len) +{ + struct flash_nios2_qspi_config *flash_cfg = dev->driver_data; + alt_qspi_controller2_dev *qspi_dev = &flash_cfg->qspi_dev; + u32_t block_offset, offset_in_block, length_to_write; + u32_t write_offset = offset; /* address of next byte to write */ + u32_t buffer_offset = 0; /* offset into source buffer */ + u32_t remaining_length = len; /* length of data left to be written */ + s32_t rc = 0, i; + + k_sem_take(&flash_cfg->sem_lock, K_FOREVER); + /* + * check if offset is word aligned and + * length is with in the range + */ + if ((data == NULL) || ((offset + len) > qspi_dev->data_end) || + (0 != (write_offset & + (NIOS2_WRITE_BLOCK_SIZE - 1)))) { + SYS_LOG_ERR("write failed at offset %ld\n", offset); + rc = -EINVAL; + goto qspi_write_err; + } + + for (i = offset/qspi_dev->sector_size; + i < qspi_dev->number_of_sectors; i++) { + + if (remaining_length <= 0) { + break; + } + + block_offset = 0; /* block offset in byte addressing */ + offset_in_block = 0; /* offset into current sector to write */ + length_to_write = 0; /* length to write to current sector */ + + /* calculate current sector/block offset in byte addressing */ + block_offset = write_offset & ~(qspi_dev->sector_size - 1); + + /* calculate offset into sector/block if there is one */ + if (block_offset != write_offset) { + offset_in_block = write_offset - block_offset; + } + + /* calculate the byte size of data to be written in a sector */ + length_to_write = min(qspi_dev->sector_size - offset_in_block, + remaining_length); + + rc = flash_nios2_qspi_write_block(dev, + block_offset, write_offset, + data + buffer_offset, length_to_write); + if (rc < 0) { + goto qspi_write_err; + } + + /* update remaining length and buffer_offset */ + remaining_length -= length_to_write; + buffer_offset += length_to_write; + write_offset += length_to_write; + } + +qspi_write_err: + k_sem_give(&flash_cfg->sem_lock); + return rc; +} + +static int flash_nios2_qspi_read(struct device *dev, off_t offset, + void *data, size_t len) +{ + struct flash_nios2_qspi_config *flash_cfg = dev->driver_data; + alt_qspi_controller2_dev *qspi_dev = &flash_cfg->qspi_dev; + u32_t buffer_offset = 0; /* offset into data buffer to get read data */ + u32_t remaining_length = len; /* length left to read */ + u32_t read_offset = offset; /* offset into flash to read from */ + u32_t word_to_read, bytes_to_copy; + s32_t rc = 0; + + k_sem_take(&flash_cfg->sem_lock, K_FOREVER); + /* + * check if offset is word aligned and + * length is with in the range + */ + if ((data == NULL) || ((offset + len) > qspi_dev->data_end) || + (0 != (read_offset & (NIOS2_WRITE_BLOCK_SIZE - 1)))) { + SYS_LOG_ERR("read failed at offset %ld\n", offset); + rc = -EINVAL; + goto qspi_read_err; + } + + while (remaining_length > 0) { + /* number of bytes from source to copy */ + bytes_to_copy = NIOS2_WRITE_BLOCK_SIZE; + + if (bytes_to_copy > remaining_length) { + bytes_to_copy = remaining_length; + } + + /* read from flash 32 bits at a time */ + word_to_read = IORD_32DIRECT(qspi_dev->data_base, read_offset); + memcpy(data + buffer_offset, &word_to_read, bytes_to_copy); + + /* update offset and length variables */ + read_offset += bytes_to_copy; + buffer_offset += bytes_to_copy; + remaining_length -= bytes_to_copy; + } + +qspi_read_err: + k_sem_give(&flash_cfg->sem_lock); + return rc; +} + +static int flash_nios2_qspi_write_protection(struct device *dev, bool enable) +{ + struct flash_nios2_qspi_config *flash_cfg = dev->driver_data; + alt_qspi_controller2_dev *qspi_dev = &flash_cfg->qspi_dev; + u32_t status, lock_val; + s32_t rc = 0, timeout; + + k_sem_take(&flash_cfg->sem_lock, K_FOREVER); + /* set write enable */ + IOWR_32DIRECT(qspi_dev->csr_base, + ALTERA_QSPI_CONTROLLER2_MEM_OP_REG, + MEM_OP_WRITE_EN_CMD); + if (enable) { + IOWR_32DIRECT(qspi_dev->csr_base, + ALTERA_QSPI_CONTROLLER2_MEM_OP_REG, + MEM_OP_LOCK_ALL_SECTORS); + lock_val = STATUS_PROTECTION_EN_VAL; + } else { + IOWR_32DIRECT(qspi_dev->csr_base, + ALTERA_QSPI_CONTROLLER2_MEM_OP_REG, + MEM_OP_UNLOCK_ALL_SECTORS); + lock_val = STATUS_PROTECTION_DIS_VAL; + } + + /* + * poll the status register to know the + * completion of the erase operation. + */ + timeout = ALTERA_QSPI_CONTROLLER2_1US_TIMEOUT_VALUE; + while (timeout > 0) { + /* wait for 1 usec */ + k_busy_wait(1); + + /* + * read flash flag status register before + * checking the QSPI status + */ + IORD_32DIRECT(qspi_dev->csr_base, + ALTERA_QSPI_CONTROLLER2_FLAG_STATUS_REG); + + /* read QPSI status register */ + status = IORD_32DIRECT(qspi_dev->csr_base, + ALTERA_QSPI_CONTROLLER2_STATUS_REG); + if (((status >> STATUS_PROTECTION_POS) & + STATUS_PROTECTION_MASK) == lock_val) { + break; + } + + timeout--; + } + + if (timeout <= 0) { + SYS_LOG_ERR("locking failed, status-reg 0x%x\n", status); + rc = -EIO; + } + + /* clear flag status register */ + IOWR_32DIRECT(qspi_dev->csr_base, + ALTERA_QSPI_CONTROLLER2_FLAG_STATUS_REG, 0x0); + k_sem_give(&flash_cfg->sem_lock); + return rc; +} + +static const struct flash_driver_api flash_nios2_qspi_api = { + .write_protection = flash_nios2_qspi_write_protection, + .erase = flash_nios2_qspi_erase, + .write = flash_nios2_qspi_write, + .read = flash_nios2_qspi_read, +#if defined(CONFIG_FLASH_PAGE_LAYOUT) + .page_layout = (flash_api_pages_layout) + flash_page_layout_not_implemented, +#endif + .write_block_size = NIOS2_WRITE_BLOCK_SIZE, +}; + +static int flash_nios2_qspi_init(struct device *dev) +{ + struct flash_nios2_qspi_config *flash_cfg = dev->driver_data; + + k_sem_init(&flash_cfg->sem_lock, 1, 1); + return 0; +} + +struct flash_nios2_qspi_config flash_cfg = { + .qspi_dev = { + .data_base = EXT_FLASH_AVL_MEM_BASE, + .data_end = EXT_FLASH_AVL_MEM_BASE + EXT_FLASH_AVL_MEM_SPAN, + .csr_base = EXT_FLASH_AVL_CSR_BASE, + .size_in_bytes = EXT_FLASH_AVL_MEM_SPAN, + .is_epcs = EXT_FLASH_AVL_MEM_IS_EPCS, + .number_of_sectors = EXT_FLASH_AVL_MEM_NUMBER_OF_SECTORS, + .sector_size = EXT_FLASH_AVL_MEM_SECTOR_SIZE, + .page_size = EXT_FLASH_AVL_MEM_PAGE_SIZE, + } +}; + +DEVICE_AND_API_INIT(flash_nios2_qspi, + CONFIG_SOC_FLASH_NIOS2_QSPI_DEV_NAME, + flash_nios2_qspi_init, &flash_cfg, NULL, + POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, + &flash_nios2_qspi_api);