blob: 2abb778e6487fbb2c6126894f1bdd250cd6af1fe [file] [log] [blame]
/*
* 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