From d1b51b6328623dcd88b926e5ad668a5c7d74d9ca Mon Sep 17 00:00:00 2001 From: Martin Kojtal <0xc0170@gmail.com> Date: Fri, 10 Nov 2017 12:24:13 +0000 Subject: [PATCH] QSPI: initial HAL nrf52840 version This commit adds QSPI HAL implementation for nrf52840 MCU targets --- .../TARGET_MCU_NRF52840/config/sdk_config.h | 130 +++++++++++ .../TARGET_NRF5x/TARGET_NRF52/objects.h | 11 + targets/TARGET_NORDIC/TARGET_NRF5x/qspi_api.c | 214 ++++++++++++++++++ targets/targets.json | 3 +- 4 files changed, 357 insertions(+), 1 deletion(-) create mode 100644 targets/TARGET_NORDIC/TARGET_NRF5x/qspi_api.c diff --git a/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF52/TARGET_MCU_NRF52840/config/sdk_config.h b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF52/TARGET_MCU_NRF52840/config/sdk_config.h index 201402a18e..203602ec0a 100644 --- a/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF52/TARGET_MCU_NRF52840/config/sdk_config.h +++ b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF52/TARGET_MCU_NRF52840/config/sdk_config.h @@ -2748,6 +2748,136 @@ // +// QSPI_ENABLED - nrf_drv_qspi - QSPI peripheral driver. +//========================================================== +#ifndef QSPI_ENABLED +#define QSPI_ENABLED 1 +#endif +#if QSPI_ENABLED +// QSPI_CONFIG_SCK_DELAY - tSHSL, tWHSL and tSHWL in number of 16 MHz periods (62.5 ns). <0-255> + + +#ifndef QSPI_CONFIG_SCK_DELAY +#define QSPI_CONFIG_SCK_DELAY 1 +#endif + +// QSPI_CONFIG_READOC - Number of data lines and opcode used for reading. + +// <0=> FastRead +// <1=> Read2O +// <2=> Read2IO +// <3=> Read4O +// <4=> Read4IO + +#ifndef QSPI_CONFIG_READOC +#define QSPI_CONFIG_READOC 4 +#endif + +// QSPI_CONFIG_WRITEOC - Number of data lines and opcode used for writing. + +// <0=> PP +// <1=> PP2O +// <2=> PP4O +// <3=> PP4IO + +#ifndef QSPI_CONFIG_WRITEOC +#define QSPI_CONFIG_WRITEOC 3 +#endif + +// QSPI_CONFIG_ADDRMODE - Addressing mode. + +// <0=> 24bit +// <1=> 32bit + +#ifndef QSPI_CONFIG_ADDRMODE +#define QSPI_CONFIG_ADDRMODE 0 +#endif + +// QSPI_CONFIG_MODE - SPI mode. + +// <0=> Mode 0 +// <1=> Mode 1 + +#ifndef QSPI_CONFIG_MODE +#define QSPI_CONFIG_MODE 0 +#endif + +// QSPI_CONFIG_FREQUENCY - Frequency divider. + +// <0=> 32MHz/1 +// <1=> 32MHz/2 +// <2=> 32MHz/3 +// <3=> 32MHz/4 +// <4=> 32MHz/5 +// <5=> 32MHz/6 +// <6=> 32MHz/7 +// <7=> 32MHz/8 +// <8=> 32MHz/9 +// <9=> 32MHz/10 +// <10=> 32MHz/11 +// <11=> 32MHz/12 +// <12=> 32MHz/13 +// <13=> 32MHz/14 +// <14=> 32MHz/15 +// <15=> 32MHz/16 + +#ifndef QSPI_CONFIG_FREQUENCY +#define QSPI_CONFIG_FREQUENCY 1 +#endif + +// QSPI_PIN_SCK - SCK pin value. +#ifndef QSPI_PIN_SCK +#define QSPI_PIN_SCK NRF_QSPI_PIN_NOT_CONNECTED +#endif + +// QSPI_PIN_CSN - CSN pin value. +#ifndef QSPI_PIN_CSN +#define QSPI_PIN_CSN NRF_QSPI_PIN_NOT_CONNECTED +#endif + +// QSPI_PIN_IO0 - IO0 pin value. +#ifndef QSPI_PIN_IO0 +#define QSPI_PIN_IO0 NRF_QSPI_PIN_NOT_CONNECTED +#endif + +// QSPI_PIN_IO1 - IO1 pin value. +#ifndef QSPI_PIN_IO1 +#define QSPI_PIN_IO1 NRF_QSPI_PIN_NOT_CONNECTED +#endif + +// QSPI_PIN_IO2 - IO2 pin value. +#ifndef QSPI_PIN_IO2 +#define QSPI_PIN_IO2 NRF_QSPI_PIN_NOT_CONNECTED +#endif + +// QSPI_PIN_IO3 - IO3 pin value. +#ifndef QSPI_PIN_IO3 +#define QSPI_PIN_IO3 NRF_QSPI_PIN_NOT_CONNECTED +#endif + +// QSPI_CONFIG_IRQ_PRIORITY - Interrupt priority + + +// Priorities 0,2 (nRF51) and 0,1,4,5 (nRF52) are reserved for SoftDevice +// <0=> 0 (highest) +// <1=> 1 +// <2=> 2 +// <3=> 3 +// <4=> 4 +// <5=> 5 +// <6=> 6 +// <7=> 7 + +#ifndef QSPI_CONFIG_IRQ_PRIORITY +#define QSPI_CONFIG_IRQ_PRIORITY 7 +#endif + +#endif //QSPI_ENABLED + +// + +// + // TIMER_ENABLED - nrf_drv_timer - TIMER periperal driver //========================================================== #ifndef TIMER_ENABLED diff --git a/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF52/objects.h b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF52/objects.h index 47e6276a82..1a26f28db4 100644 --- a/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF52/objects.h +++ b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF52/objects.h @@ -142,6 +142,17 @@ struct flash_s { uint32_t placeholder; }; +#if DEVICE_QSPI + +// #include "nrf_drv_qspi.h" + +struct qspi_s { + uint32_t placeholder; + // nrf_drv_qspi_config_t config; +}; + +#endif + #include "gpio_object.h" #ifdef __cplusplus diff --git a/targets/TARGET_NORDIC/TARGET_NRF5x/qspi_api.c b/targets/TARGET_NORDIC/TARGET_NRF5x/qspi_api.c new file mode 100644 index 0000000000..b3f547e074 --- /dev/null +++ b/targets/TARGET_NORDIC/TARGET_NRF5x/qspi_api.c @@ -0,0 +1,214 @@ +/* + * Copyright (c) 2017 Nordic Semiconductor ASA + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list + * of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic Semiconductor ASA + * integrated circuit in a product or a software update for such product, must reproduce + * the above copyright notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific prior + * written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary or object form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "qspi_api.h" + +#if DEVICE_QSPI + +#include "nrf_drv_common.h" +#include "nrf_drv_qspi.h" + +/* +TODO + - config inside obj - nordic headers have some problems with inclusion + - free - is it really empty, nothing to do there? + - prepare command - support more protocols that nordic can do (now limited) + - nordic does not support + - alt + - dummy cycles +*/ + +#define MBED_HAL_QSPI_HZ_TO_CONFIG(hz) ((32000000/(hz))-1) +#define MBED_HAL_QSPI_MAX_FREQ 32000000UL + +static nrf_drv_qspi_config_t config; + +qspi_status_t qspi_prepare_command(qspi_t *obj, const qspi_command_t *command, bool write) +{ + // we need to remap to command-address-data - x_x_x + // most commmon are 1-1-1, 1-1-4, 1-4-4 + // 1-1-1 + if (command->instruction.bus_width == QSPI_CFG_BUS_SINGLE && + command->address.bus_width == QSPI_CFG_BUS_SINGLE && + command->data.bus_width == QSPI_CFG_BUS_SINGLE) { + if (write) { + config.prot_if.writeoc = NRF_QSPI_WRITEOC_PP; + } else { + config.prot_if.readoc = NRF_QSPI_READOC_FASTREAD; + } + // 1-1-4 + } else if (command->instruction.bus_width == QSPI_CFG_BUS_SINGLE && + command->address.bus_width == QSPI_CFG_BUS_SINGLE && + command->data.bus_width == QSPI_CFG_BUS_QUAD) { + // 1_1_4 + if (write) { + config.prot_if.writeoc = QSPI_IFCONFIG0_WRITEOC_PP4O; + } else { + config.prot_if.readoc = NRF_QSPI_READOC_READ4O; + } + // 1-4-4 + } else if (command->instruction.bus_width == QSPI_CFG_BUS_SINGLE && + command->address.bus_width == QSPI_CFG_BUS_QUAD && + command->data.bus_width == QSPI_CFG_BUS_QUAD) { + // 1_4_4 + if (write) { + config.prot_if.writeoc = QSPI_IFCONFIG0_WRITEOC_PP4IO; + } else { + config.prot_if.readoc = NRF_QSPI_READOC_READ4IO; + } + } + + qspi_status_t ret = QSPI_STATUS_OK; + + // supporting only 24 or 32 bit address + if (command->address.size == QSPI_CFG_ADDR_SIZE_24) { + config.prot_if.addrmode = NRF_QSPI_ADDRMODE_24BIT; + } else if (command->address.size == QSPI_CFG_ADDR_SIZE_32) { + config.prot_if.addrmode = QSPI_CFG_ADDR_SIZE_32; + } else { + ret = QSPI_STATUS_INVALID_PARAMETER; + } + return ret; +} + +qspi_status_t qspi_init(qspi_t *obj, PinName io0, PinName io1, PinName io2, PinName io3, PinName sclk, PinName ssel, uint32_t hz, uint8_t mode) +{ + (void)(obj); + if (hz > MBED_HAL_QSPI_MAX_FREQ) { + return QSPI_STATUS_INVALID_PARAMETER; + } + + // memset(config, 0, sizeof(config)); + + config.pins.sck_pin = (uint32_t)sclk; + config.pins.csn_pin = (uint32_t)ssel; + config.pins.io0_pin = (uint32_t)io0; + config.pins.io1_pin = (uint32_t)io1; + config.pins.io2_pin = (uint32_t)io2; + config.pins.io3_pin = (uint32_t)io3; + config.irq_priority = SPI_DEFAULT_CONFIG_IRQ_PRIORITY; + + config.phy_if.sck_freq = MBED_HAL_QSPI_HZ_TO_CONFIG(hz), + config.phy_if.sck_delay = 0x05, + config.phy_if.dpmen = false; + config.phy_if.spi_mode = mode == 0 ? NRF_QSPI_MODE_0 : NRF_QSPI_MODE_1; + + nrf_drv_qspi_init(&config, NULL , NULL); + + return 0; +} + +qspi_status_t qspi_free(qspi_t *obj) +{ + (void)(obj); + // possibly here uninit from SDK driver + return QSPI_STATUS_OK; +} + +qspi_status_t qspi_frequency(qspi_t *obj, int hz) +{ + config.phy_if.sck_freq = MBED_HAL_QSPI_HZ_TO_CONFIG(hz); + // use sync version, no handler + ret_code_t ret = nrf_drv_qspi_init(&config, NULL , NULL); + if (ret == NRF_SUCCESS ) { + return QSPI_STATUS_OK; + } else if (ret == NRF_ERROR_INVALID_PARAM) { + return QSPI_STATUS_INVALID_PARAMETER; + } else { + return QSPI_STATUS_ERROR; + } +} + +qspi_status_t qspi_write(qspi_t *obj, const qspi_command_t *command, const void *data, size_t *length) +{ + qspi_status_t status = qspi_prepare_command(obj, command, true); + if (status != QSPI_STATUS_OK) { + return status; + } + + // write here does not return how much it transfered, we return transfered all + ret_code_t ret = nrf_drv_qspi_write(data, *length, command->address.value); + if (ret == NRF_SUCCESS ) { + return QSPI_STATUS_OK; + } else { + return QSPI_STATUS_ERROR; + } +} + +qspi_status_t qspi_read(qspi_t *obj, const qspi_command_t *command, void *data, size_t *length) +{ + qspi_status_t status = qspi_prepare_command(obj, command, false); + if (status != QSPI_STATUS_OK) { + return status; + } + + ret_code_t ret = nrf_drv_qspi_read(data, *length, command->address.value); + if (ret == NRF_SUCCESS ) { + return QSPI_STATUS_OK; + } else { + return QSPI_STATUS_ERROR; + } +} + +// they provide 2 functions write or nrf_drv_qspi_cinstr_xfer +// nrf_drv_qspi_cinstr_xfer seems like it accepts simplified config that is very simplified +// and might not be useful for us. +// write on other hand, needs to write some data (errors if buffer is NULL!) +qspi_status_t qspi_write_command(qspi_t *obj, const qspi_command_t *command) +{ + // use simplified API, as we are sending only instruction here + nrf_qspi_cinstr_conf_t config; + config.length = NRF_QSPI_CINSTR_LEN_1B; // no data + config.opcode = command->instruction.value; + config.io2_level = false; + config.io3_level = false; + config.wipwait = false; + config.wren = false; + + // no data phase, send only config + ret_code_t ret = nrf_drv_qspi_cinstr_xfer(&config, NULL, NULL); + if (ret == NRF_SUCCESS ) { + return QSPI_STATUS_OK; + } else { + return QSPI_STATUS_ERROR; + } +} + +#endif + +/** @}*/ diff --git a/targets/targets.json b/targets/targets.json index 85b6623557..88c57e7871 100755 --- a/targets/targets.json +++ b/targets/targets.json @@ -3873,7 +3873,8 @@ "SPI_ASYNCH", "STCLK_OFF_DURING_SLEEP", "TRNG", - "USTICKER" + "USTICKER", + "QSPI" ], "extra_labels": [ "NORDIC",