| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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 <string.h> |
| #include <assert.h> |
| #include <stdint.h> |
| #include "os/mynewt.h" |
| #if MYNEWT_VAL(QSPI_ENABLE) |
| #include <mcu/cmsis_nvic.h> |
| #include <hal/hal_flash_int.h> |
| #include "mcu/nrf52_hal.h" |
| #include "nrf.h" |
| #include <nrfx/hal/nrf_qspi.h> |
| |
| #if MYNEWT_VAL(QSPI_FLASH_SECTOR_SIZE) < 1 |
| #error QSPI_FLASH_SECTOR_SIZE must be set to the correct value in bsp syscfg.yml |
| #endif |
| |
| #if MYNEWT_VAL(QSPI_FLASH_PAGE_SIZE) < 1 |
| #error QSPI_FLASH_PAGE_SIZE must be set to the correct value in bsp syscfg.yml |
| #endif |
| |
| #if MYNEWT_VAL(QSPI_FLASH_SECTOR_COUNT) < 1 |
| #error QSPI_FLASH_SECTOR_COUNT must be set to the correct value in bsp syscfg.yml |
| #endif |
| |
| #if MYNEWT_VAL(QSPI_PIN_CS) < 0 |
| #error QSPI_PIN_CS must be set to the correct value in bsp syscfg.yml |
| #endif |
| |
| #if MYNEWT_VAL(QSPI_PIN_SCK) < 0 |
| #error QSPI_PIN_SCK must be set to the correct value in bsp syscfg.yml |
| #endif |
| |
| #if MYNEWT_VAL(QSPI_PIN_DIO0) < 0 |
| #error QSPI_PIN_DIO0 must be set to the correct value in bsp syscfg.yml |
| #endif |
| |
| #if MYNEWT_VAL(QSPI_PIN_DIO1) < 0 |
| #error QSPI_PIN_DIO1 must be set to the correct value in bsp syscfg.yml |
| #endif |
| |
| #if MYNEWT_VAL(QSPI_PIN_DIO2) < 0 && (MYNEWT_VAL(QSPI_READOC) > 2 || MYNEWT_VAL(QSPI_WRITEOC) > 1) |
| #error QSPI_PIN_DIO2 must be set to the correct value in bsp syscfg.yml |
| #endif |
| |
| #if MYNEWT_VAL(QSPI_PIN_DIO3) < 0 && (MYNEWT_VAL(QSPI_READOC) > 2 || MYNEWT_VAL(QSPI_WRITEOC) > 1) |
| #error QSPI_PIN_DIO3 must be set to the correct value in bsp syscfg.yml |
| #endif |
| |
| static int |
| nrf52k_qspi_read(const struct hal_flash *dev, uint32_t address, |
| void *dst, uint32_t num_bytes); |
| static int |
| nrf52k_qspi_write(const struct hal_flash *dev, uint32_t address, |
| const void *src, uint32_t num_bytes); |
| static int |
| nrf52k_qspi_erase_sector(const struct hal_flash *dev, |
| uint32_t sector_address); |
| static int |
| nrf52k_qspi_sector_info(const struct hal_flash *dev, int idx, |
| uint32_t *address, uint32_t *sz); |
| static int |
| nrf52k_qspi_init(const struct hal_flash *dev); |
| |
| static const struct hal_flash_funcs nrf52k_qspi_funcs = { |
| .hff_read = nrf52k_qspi_read, |
| .hff_write = nrf52k_qspi_write, |
| .hff_erase_sector = nrf52k_qspi_erase_sector, |
| .hff_sector_info = nrf52k_qspi_sector_info, |
| .hff_init = nrf52k_qspi_init |
| }; |
| |
| const struct hal_flash nrf52k_qspi_dev = { |
| .hf_itf = &nrf52k_qspi_funcs, |
| .hf_base_addr = 0x00000000, |
| .hf_size = MYNEWT_VAL(QSPI_FLASH_SECTOR_COUNT) * MYNEWT_VAL(QSPI_FLASH_SECTOR_SIZE), |
| .hf_sector_cnt = MYNEWT_VAL(QSPI_FLASH_SECTOR_COUNT), |
| .hf_align = 1, |
| .hf_erased_val = 0xff, |
| }; |
| |
| static int |
| nrf52k_qspi_read(const struct hal_flash *dev, uint32_t address, |
| void *dst, uint32_t num_bytes) { |
| uint32_t ram_buffer[4]; |
| uint8_t *ram_ptr = NULL; |
| uint32_t read_bytes; |
| |
| while ((NRF_QSPI->STATUS & QSPI_STATUS_READY_Msk) == 0) |
| ; |
| |
| while (num_bytes != 0) { |
| /* |
| * If address or destination pointer are unaligned or |
| * number of bytes to read is less then 4 read using ram buffer. |
| */ |
| if (((address & 3) != 0) || |
| ((((uint32_t) dst) & 3) != 0) || |
| num_bytes < 4) { |
| uint32_t to_read = (num_bytes + (address & 3) + 3) & ~3; |
| if (to_read > sizeof(ram_buffer)) { |
| to_read = sizeof(ram_buffer); |
| } |
| ram_ptr = (uint8_t *)((uint32_t) ram_buffer + (address & 3)); |
| read_bytes = to_read - (address & 3); |
| if (read_bytes > num_bytes) { |
| read_bytes = num_bytes; |
| } |
| NRF_QSPI->READ.DST = (uint32_t) ram_buffer; |
| NRF_QSPI->READ.SRC = address & ~3; |
| NRF_QSPI->READ.CNT = to_read; |
| } else { |
| read_bytes = num_bytes & ~3; |
| NRF_QSPI->READ.DST = (uint32_t) dst; |
| NRF_QSPI->READ.SRC = address; |
| NRF_QSPI->READ.CNT = read_bytes; |
| } |
| NRF_QSPI->EVENTS_READY = 0; |
| NRF_QSPI->TASKS_READSTART = 1; |
| while (NRF_QSPI->EVENTS_READY == 0) |
| ; |
| if (ram_ptr != NULL) { |
| memcpy(dst, ram_ptr, read_bytes); |
| ram_ptr = NULL; |
| } |
| address += read_bytes; |
| dst = (void *) ((uint32_t) dst + read_bytes); |
| num_bytes -= read_bytes; |
| } |
| return 0; |
| } |
| |
| static int |
| nrf52k_qspi_write(const struct hal_flash *dev, uint32_t address, |
| const void *src, uint32_t num_bytes) |
| { |
| uint32_t ram_buffer[4]; |
| uint8_t *ram_ptr = NULL; |
| uint32_t written_bytes; |
| const char src_not_in_ram = (((uint32_t) src) & 0xE0000000) != 0x20000000; |
| uint32_t page_limit; |
| |
| while ((NRF_QSPI->STATUS & QSPI_STATUS_READY_Msk) == 0) |
| ; |
| |
| while (num_bytes != 0) { |
| page_limit = (address & ~(MYNEWT_VAL(QSPI_FLASH_PAGE_SIZE) - 1)) + |
| MYNEWT_VAL(QSPI_FLASH_PAGE_SIZE); |
| /* |
| * Use RAM buffer if src or address is not 4 bytes aligned, |
| * or number of bytes to write is less then 4 |
| * or src pointer is not from RAM. |
| */ |
| if (((address & 3) != 0) || |
| ((((uint32_t) src) & 3) != 0) || |
| num_bytes < 4 || |
| src_not_in_ram) { |
| uint32_t to_write; |
| if (address + num_bytes > page_limit) { |
| to_write = ((page_limit - address) + 3) & ~3; |
| } else { |
| to_write = (num_bytes + (address & 3) + 3) & ~3; |
| } |
| if (to_write > sizeof(ram_buffer)) { |
| to_write = sizeof(ram_buffer); |
| } |
| memset(ram_buffer, 0xFF, sizeof(ram_buffer)); |
| ram_ptr = (uint8_t *)((uint32_t) ram_buffer + (address & 3)); |
| |
| written_bytes = to_write - (address & 3); |
| if (written_bytes > num_bytes) { |
| written_bytes = num_bytes; |
| } |
| memcpy(ram_ptr, src, written_bytes); |
| |
| NRF_QSPI->WRITE.SRC = (uint32_t) ram_buffer; |
| NRF_QSPI->WRITE.DST = address & ~3; |
| NRF_QSPI->WRITE.CNT = to_write; |
| } else { |
| NRF_QSPI->WRITE.SRC = (uint32_t) src; |
| NRF_QSPI->WRITE.DST = address; |
| /* |
| * Limit write to single page. |
| */ |
| if (address + num_bytes > page_limit) { |
| written_bytes = page_limit - address; |
| } else { |
| written_bytes = num_bytes & ~3; |
| } |
| NRF_QSPI->WRITE.CNT = written_bytes; |
| } |
| NRF_QSPI->EVENTS_READY = 0; |
| NRF_QSPI->TASKS_WRITESTART = 1; |
| while (NRF_QSPI->EVENTS_READY == 0) |
| ; |
| |
| address += written_bytes; |
| src = (void *) ((uint32_t) src + written_bytes); |
| num_bytes -= written_bytes; |
| } |
| return 0; |
| } |
| |
| static int |
| nrf52k_qspi_erase_sector(const struct hal_flash *dev, |
| uint32_t sector_address) |
| { |
| while ((NRF_QSPI->STATUS & QSPI_STATUS_READY_Msk) == 0) |
| ; |
| NRF_QSPI->EVENTS_READY = 0; |
| NRF_QSPI->ERASE.PTR = sector_address; |
| NRF_QSPI->ERASE.LEN = MYNEWT_VAL(QSPI_FLASH_SECTOR_SIZE); |
| NRF_QSPI->TASKS_ERASESTART = 1; |
| while (NRF_QSPI->EVENTS_READY == 0) |
| ; |
| return 0; |
| } |
| |
| static int |
| nrf52k_qspi_sector_info(const struct hal_flash *dev, int idx, |
| uint32_t *address, uint32_t *sz) |
| { |
| *address = idx * MYNEWT_VAL(QSPI_FLASH_SECTOR_SIZE); |
| *sz = MYNEWT_VAL(QSPI_FLASH_SECTOR_SIZE); |
| |
| return 0; |
| } |
| |
| static int |
| nrf52k_qspi_init(const struct hal_flash *dev) |
| { |
| const nrf_qspi_prot_conf_t config0 = { |
| .readoc = MYNEWT_VAL(QSPI_READOC), |
| .writeoc = MYNEWT_VAL(QSPI_WRITEOC), |
| .addrmode = MYNEWT_VAL(QSPI_ADDRMODE), |
| .dpmconfig = MYNEWT_VAL(QSPI_DPMCONFIG) |
| }; |
| const nrf_qspi_phy_conf_t config1 = { |
| .sck_delay = MYNEWT_VAL(QSPI_SCK_DELAY), |
| .dpmen = 0, |
| .spi_mode = MYNEWT_VAL(QSPI_SPI_MODE), |
| .sck_freq = MYNEWT_VAL(QSPI_SCK_FREQ), |
| }; |
| /* |
| * Configure pins |
| */ |
| NRF_QSPI->PSEL.CSN = MYNEWT_VAL(QSPI_PIN_CS); |
| NRF_QSPI->PSEL.SCK = MYNEWT_VAL(QSPI_PIN_SCK); |
| NRF_QSPI->PSEL.IO0 = MYNEWT_VAL(QSPI_PIN_DIO0); |
| NRF_QSPI->PSEL.IO1 = MYNEWT_VAL(QSPI_PIN_DIO1); |
| /* |
| * Setup only known fields of IFCONFIG0. Other bits may be set by erratas code. |
| */ |
| nrf_qspi_ifconfig0_set(NRF_QSPI, &config0); |
| nrf_qspi_ifconfig1_set(NRF_QSPI, &config1); |
| |
| NRF_QSPI->XIPOFFSET = 0x12000000; |
| |
| NRF_QSPI->ENABLE = 1; |
| NRF_QSPI->TASKS_ACTIVATE = 1; |
| while (NRF_QSPI->EVENTS_READY == 0) |
| ; |
| |
| #if (MYNEWT_VAL(QSPI_READOC) > 2) || (MYNEWT_VAL(QSPI_WRITEOC) > 1) |
| NRF_QSPI->PSEL.IO2 = MYNEWT_VAL(QSPI_PIN_DIO2); |
| NRF_QSPI->PSEL.IO3 = MYNEWT_VAL(QSPI_PIN_DIO3); |
| #endif |
| |
| return 0; |
| } |
| |
| #endif |