mirror of https://github.com/ARMmbed/mbed-os.git
Initial commit of Silicon Labs QSPI HAL implementation
* For EFM32GG11, since that is the only Silicon Labs target with QSPI per today * Verified working using the on-board flash and tests-mbed_hal-qspipull/7825/head
parent
2f8e679183
commit
845a5beb30
|
@ -0,0 +1,192 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2018-2018 ARM Limited
|
||||
*
|
||||
* 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 MBED_QSPI_FLASH_MX25R3235F_H
|
||||
#define MBED_QSPI_FLASH_MX25R3235F_H
|
||||
|
||||
|
||||
#define QSPI_FLASH_CHIP_STRING "macronix MX25R3235F"
|
||||
|
||||
// Command for reading status register
|
||||
#define QSPI_CMD_RDSR 0x05
|
||||
// Command for reading configuration register
|
||||
#define QSPI_CMD_RDCR0 0x15
|
||||
// Command for writing status/configuration register
|
||||
#define QSPI_CMD_WRSR 0x01
|
||||
// Command for reading security register
|
||||
#define QSPI_CMD_RDSCUR 0x2B
|
||||
|
||||
// Command for setting Reset Enable
|
||||
#define QSPI_CMD_RSTEN 0x66
|
||||
// Command for setting Reset
|
||||
#define QSPI_CMD_RST 0x99
|
||||
|
||||
// Command for setting write enable
|
||||
#define QSPI_CMD_WREN 0x06
|
||||
// Command for setting write disable
|
||||
#define QSPI_CMD_WRDI 0x04
|
||||
|
||||
// WRSR operations max time [us] (datasheet max time + 15%)
|
||||
#define QSPI_WRSR_MAX_TIME 34500 // 30ms
|
||||
// general wait max time [us]
|
||||
#define QSPI_WAIT_MAX_TIME 100000 // 100ms
|
||||
|
||||
|
||||
// Commands for writing (page programming)
|
||||
#define QSPI_CMD_WRITE_1IO 0x02 // 1-1-1 mode
|
||||
#define QSPI_CMD_WRITE_4IO 0x38 // 1-4-4 mode
|
||||
|
||||
// write operations max time [us] (datasheet max time + 15%)
|
||||
#define QSPI_PAGE_PROG_MAX_TIME 11500 // 10ms
|
||||
|
||||
#define QSPI_PAGE_SIZE 256 // 256B
|
||||
|
||||
// Commands for reading
|
||||
#define QSPI_CMD_READ_1IO_FAST 0x0B // 1-1-1 mode
|
||||
#define QSPI_CMD_READ_1IO 0x03 // 1-1-1 mode
|
||||
#define QSPI_CMD_READ_2IO 0xBB // 1-2-2 mode
|
||||
#define QSPI_CMD_READ_1I2O 0x3B // 1-1-2 mode
|
||||
#define QSPI_CMD_READ_4IO 0xEB // 1-4-4 mode
|
||||
#define QSPI_CMD_READ_1I4O 0x6B // 1-1-4 mode
|
||||
|
||||
#define QSPI_READ_1IO_DUMMY_CYCLE 0
|
||||
#define QSPI_READ_FAST_DUMMY_CYCLE 8
|
||||
#define QSPI_READ_2IO_DUMMY_CYCLE 4
|
||||
#define QSPI_READ_1I2O_DUMMY_CYCLE 8
|
||||
#define QSPI_READ_4IO_DUMMY_CYCLE 6
|
||||
#define QSPI_READ_1I4O_DUMMY_CYCLE 8
|
||||
|
||||
// Commands for erasing
|
||||
#define QSPI_CMD_ERASE_SECTOR 0x20 // 4kB
|
||||
#define QSPI_CMD_ERASE_BLOCK_32 0x52 // 32kB
|
||||
#define QSPI_CMD_ERASE_BLOCK_64 0xD8 // 64kB
|
||||
#define QSPI_CMD_ERASE_CHIP 0x60 // or 0xC7
|
||||
|
||||
// erase operations max time [us] (datasheet max time + 15%)
|
||||
#define QSPI_ERASE_SECTOR_MAX_TIME 276000 // 240 ms
|
||||
#define QSPI_ERASE_BLOCK_32_MAX_TIME 3450000 // 3s
|
||||
#define QSPI_ERASE_BLOCK_64_MAX_TIME 4025000 // 3.5s
|
||||
|
||||
// max frequency for basic rw operation
|
||||
#define QSPI_COMMON_MAX_FREQUENCY 32000000
|
||||
|
||||
#define QSPI_STATUS_REG_SIZE 1
|
||||
#define QSPI_CONFIG_REG_0_SIZE 2
|
||||
#define QSPI_SECURITY_REG_SIZE 1
|
||||
#define QSPI_MAX_REG_SIZE 2
|
||||
|
||||
// status register
|
||||
#define STATUS_BIT_WIP (1 << 0) // write in progress bit
|
||||
#define STATUS_BIT_WEL (1 << 1) // write enable latch
|
||||
#define STATUS_BIT_BP0 (1 << 2) //
|
||||
#define STATUS_BIT_BP1 (1 << 3) //
|
||||
#define STATUS_BIT_BP2 (1 << 4) //
|
||||
#define STATUS_BIT_BP3 (1 << 5) //
|
||||
#define STATUS_BIT_QE (1 << 6) // Quad Enable
|
||||
#define STATUS_BIT_SRWD (1 << 7) // status register write protect
|
||||
|
||||
// configuration register 0
|
||||
// bit 0, 1, 2, 4, 5, 7 reserved
|
||||
#define CONFIG0_BIT_TB (1 << 3) // Top/Bottom area protect
|
||||
#define CONFIG0_BIT_DC (1 << 6) // Dummy Cycle
|
||||
|
||||
// configuration register 1
|
||||
// bit 0, 2, 3, 4, 5, 6, 7 reserved
|
||||
#define CONFIG1_BIT_LH (1 << 1) // 0 = Ultra Low power mode, 1 = High performance mode
|
||||
|
||||
|
||||
// single quad enable flag for both dual and quad mode
|
||||
#define QUAD_ENABLE() \
|
||||
\
|
||||
uint8_t reg_data[QSPI_STATUS_REG_SIZE]; \
|
||||
\
|
||||
if (write_enable(qspi) != QSPI_STATUS_OK) { \
|
||||
return QSPI_STATUS_ERROR; \
|
||||
} \
|
||||
WAIT_FOR(WRSR_MAX_TIME, qspi); \
|
||||
\
|
||||
reg_data[0] = STATUS_BIT_QE; \
|
||||
qspi.cmd.build(QSPI_CMD_WRSR); \
|
||||
\
|
||||
if (qspi_command_transfer(&qspi.handle, qspi.cmd.get(), \
|
||||
reg_data, QSPI_STATUS_REG_SIZE, NULL, 0) != QSPI_STATUS_OK) { \
|
||||
return QSPI_STATUS_ERROR; \
|
||||
} \
|
||||
WAIT_FOR(WRSR_MAX_TIME, qspi); \
|
||||
\
|
||||
memset(reg_data, 0, QSPI_STATUS_REG_SIZE); \
|
||||
if (read_register(STATUS_REG, reg_data, \
|
||||
QSPI_STATUS_REG_SIZE, qspi) != QSPI_STATUS_OK) { \
|
||||
return QSPI_STATUS_ERROR; \
|
||||
} \
|
||||
\
|
||||
return ((reg_data[0] & STATUS_BIT_QE) != 0 ? \
|
||||
QSPI_STATUS_OK : QSPI_STATUS_ERROR)
|
||||
|
||||
|
||||
|
||||
#define QUAD_DISABLE() \
|
||||
\
|
||||
uint8_t reg_data[QSPI_STATUS_REG_SIZE]; \
|
||||
\
|
||||
if (write_enable(qspi) != QSPI_STATUS_OK) { \
|
||||
return QSPI_STATUS_ERROR; \
|
||||
} \
|
||||
WAIT_FOR(WRSR_MAX_TIME, qspi); \
|
||||
\
|
||||
reg_data[0] = 0; \
|
||||
qspi.cmd.build(QSPI_CMD_WRSR); \
|
||||
\
|
||||
if (qspi_command_transfer(&qspi.handle, qspi.cmd.get(), \
|
||||
reg_data, QSPI_STATUS_REG_SIZE, NULL, 0) != QSPI_STATUS_OK) { \
|
||||
return QSPI_STATUS_ERROR; \
|
||||
} \
|
||||
WAIT_FOR(WRSR_MAX_TIME, qspi); \
|
||||
\
|
||||
reg_data[0] = 0; \
|
||||
if (read_register(STATUS_REG, reg_data, \
|
||||
QSPI_STATUS_REG_SIZE, qspi) != QSPI_STATUS_OK) { \
|
||||
return QSPI_STATUS_ERROR; \
|
||||
} \
|
||||
\
|
||||
return ((reg_data[0] & STATUS_BIT_QE) == 0 ? \
|
||||
QSPI_STATUS_OK : QSPI_STATUS_ERROR)
|
||||
|
||||
|
||||
|
||||
#define FAST_MODE_ENABLE() \
|
||||
\
|
||||
qspi_status_t ret; \
|
||||
const int32_t reg_size = QSPI_STATUS_REG_SIZE + QSPI_CONFIG_REG_0_SIZE; \
|
||||
uint8_t reg_data[reg_size]; \
|
||||
\
|
||||
if (read_register(STATUS_REG, reg_data, \
|
||||
QSPI_STATUS_REG_SIZE, qspi) != QSPI_STATUS_OK) { \
|
||||
return QSPI_STATUS_ERROR; \
|
||||
} \
|
||||
if (read_register(CONFIG_REG0, reg_data + QSPI_STATUS_REG_SIZE, \
|
||||
QSPI_CONFIG_REG_0_SIZE, qspi) != QSPI_STATUS_OK) { \
|
||||
return QSPI_STATUS_ERROR; \
|
||||
} \
|
||||
\
|
||||
reg_data[2] |= CONFIG1_BIT_LH; \
|
||||
qspi.cmd.build(QSPI_CMD_WRSR); \
|
||||
\
|
||||
return qspi_command_transfer(&qspi.handle, qspi.cmd.get(), \
|
||||
reg_data, reg_size, NULL, 0)
|
||||
|
||||
|
||||
|
||||
#endif // MBED_QSPI_FLASH_MX25R3235F_H
|
|
@ -0,0 +1,22 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2018-2018 ARM Limited
|
||||
*
|
||||
* 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 MBED_QSPI_FLASH_CONFIG_H
|
||||
#define MBED_QSPI_FLASH_CONFIG_H
|
||||
|
||||
#include "../../MX25R3235F_config.h"
|
||||
|
||||
|
||||
#endif // MBED_QSPI_FLASH_CONFIG_H
|
|
@ -23,6 +23,8 @@
|
|||
#include "NORDIC/NRF52840_DK/flash_config.h"
|
||||
#elif defined(TARGET_DISCO_F413ZH)
|
||||
#include "STM/DISCO_F413ZH/flash_config.h"
|
||||
#elif defined(TARGET_EFM32GG11_STK3701)
|
||||
#include "SiliconLabs/EFM32GG11_STK3701/flash_config.h"
|
||||
#endif
|
||||
|
||||
#endif // MBED_FLASH_CONFIGS_H
|
||||
|
|
|
@ -136,6 +136,14 @@ typedef enum {
|
|||
} UARTName;
|
||||
#endif
|
||||
|
||||
#if DEVICE_QSPI
|
||||
typedef enum {
|
||||
#ifdef QSPI0_BASE
|
||||
QSPI_0 = QSPI0_BASE,
|
||||
#endif
|
||||
} QSPIName;
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -68,5 +68,15 @@ extern const PinMap PinMap_CAN_TX[];
|
|||
extern const PinMap PinMap_CAN_RX[];
|
||||
#endif
|
||||
|
||||
#if DEVICE_QSPI
|
||||
/************QSPI**************/
|
||||
extern const PinMap PinMap_QSPI_DQ0[];
|
||||
extern const PinMap PinMap_QSPI_DQ1[];
|
||||
extern const PinMap PinMap_QSPI_DQ2[];
|
||||
extern const PinMap PinMap_QSPI_DQ3[];
|
||||
extern const PinMap PinMap_QSPI_SCLK[];
|
||||
extern const PinMap PinMap_QSPI_CS0[];
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -579,3 +579,53 @@ MBED_WEAK const PinMap PinMap_CAN_RX[] = {
|
|||
#endif
|
||||
};
|
||||
#endif
|
||||
|
||||
#if DEVICE_QSPI
|
||||
MBED_WEAK const PinMap PinMap_QSPI_DQ0[] = {
|
||||
#ifdef QSPI0_BASE
|
||||
{PD9, QSPI_0, 0},
|
||||
{PA2, QSPI_0, 1},
|
||||
{PG1, QSPI_0, 2},
|
||||
#endif
|
||||
};
|
||||
|
||||
MBED_WEAK const PinMap PinMap_QSPI_DQ1[] = {
|
||||
#ifdef QSPI0_BASE
|
||||
{PD10, QSPI_0, 0},
|
||||
{PA3, QSPI_0, 1},
|
||||
{PG2, QSPI_0, 2},
|
||||
#endif
|
||||
};
|
||||
|
||||
MBED_WEAK const PinMap PinMap_QSPI_DQ2[] = {
|
||||
#ifdef QSPI0_BASE
|
||||
{PD11, QSPI_0, 0},
|
||||
{PA4, QSPI_0, 1},
|
||||
{PG3, QSPI_0, 2},
|
||||
#endif
|
||||
};
|
||||
|
||||
MBED_WEAK const PinMap PinMap_QSPI_DQ3[] = {
|
||||
#ifdef QSPI0_BASE
|
||||
{PD12, QSPI_0, 0},
|
||||
{PA5, QSPI_0, 1},
|
||||
{PG4, QSPI_0, 2},
|
||||
#endif
|
||||
};
|
||||
|
||||
MBED_WEAK const PinMap PinMap_QSPI_SCLK[] = {
|
||||
#ifdef QSPI0_BASE
|
||||
{PF6, QSPI_0, 0},
|
||||
{PE14, QSPI_0, 1},
|
||||
{PG0, QSPI_0, 2},
|
||||
#endif
|
||||
};
|
||||
|
||||
MBED_WEAK const PinMap PinMap_QSPI_CS0[] = {
|
||||
#ifdef QSPI0_BASE
|
||||
{PF7, QSPI_0, 0},
|
||||
{PA0, QSPI_0, 1},
|
||||
{PG9, QSPI_0, 2},
|
||||
#endif
|
||||
};
|
||||
#endif
|
||||
|
|
|
@ -76,7 +76,16 @@ typedef enum {
|
|||
|
||||
/* Board Controller */
|
||||
STDIO_UART_TX = USBTX,
|
||||
STDIO_UART_RX = USBRX
|
||||
STDIO_UART_RX = USBRX,
|
||||
|
||||
/* On-board MX25R3235F */
|
||||
QSPI_PIN_IO0 = PG1,
|
||||
QSPI_PIN_IO1 = PG2,
|
||||
QSPI_PIN_IO2 = PG3,
|
||||
QSPI_PIN_IO3 = PG4,
|
||||
QSPI_PIN_SCK = PG0,
|
||||
QSPI_PIN_CSN = PG9,
|
||||
|
||||
} PinName;
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
|
|
@ -154,6 +154,18 @@ struct can_s {
|
|||
};
|
||||
#endif
|
||||
|
||||
#if DEVICE_QSPI
|
||||
struct qspi_s {
|
||||
QSPI_TypeDef *instance;
|
||||
PinName io0;
|
||||
PinName io1;
|
||||
PinName io2;
|
||||
PinName io3;
|
||||
PinName sclk;
|
||||
PinName ssel;
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,362 @@
|
|||
/***************************************************************************//**
|
||||
* @file rtc_rtcc.c
|
||||
*******************************************************************************
|
||||
* @section License
|
||||
* <b>(C) Copyright 2018 Silicon Labs, http://www.silabs.com</b>
|
||||
*******************************************************************************
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* 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 "device.h"
|
||||
#if DEVICE_QSPI && defined(QSPI_PRESENT)
|
||||
|
||||
#include "stddef.h"
|
||||
#include "qspi_api.h"
|
||||
#include "mbed_error.h"
|
||||
#include "em_cmu.h"
|
||||
#include "em_qspi.h"
|
||||
#include "pinmap.h"
|
||||
#include "PeripheralPins.h"
|
||||
#include "pinmap_function.h"
|
||||
|
||||
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)
|
||||
{
|
||||
|
||||
#if defined(QSPI_FLASH_EN)
|
||||
pin_mode(QSPI_FLASH_EN, PushPull);
|
||||
GPIO_PinOutSet((GPIO_Port_TypeDef)(QSPI_FLASH_EN >> 4 & 0xF), QSPI_FLASH_EN & 0xF);
|
||||
#endif
|
||||
|
||||
// There's only one QSPI per chip for now
|
||||
obj->instance = QSPI0;
|
||||
obj->io0 = io0;
|
||||
obj->io1 = io1;
|
||||
obj->io2 = io2;
|
||||
obj->io3 = io3;
|
||||
obj->ssel = ssel;
|
||||
obj->sclk = sclk;
|
||||
|
||||
CMU_ClockEnable(cmuClock_GPIO, true);
|
||||
|
||||
#if (CORE_CLOCK_SOURCE == HFXO)
|
||||
CMU_ClockSelectSet(cmuClock_QSPI0REF, cmuSelect_HFXO);
|
||||
#endif
|
||||
|
||||
CMU_ClockEnable(cmuClock_QSPI0, true);
|
||||
CMU_ClockEnable(cmuClock_QSPI0REF, true);
|
||||
|
||||
qspi_frequency(obj, hz);
|
||||
|
||||
if (mode) {
|
||||
obj->instance->CONFIG |= QSPI_CONFIG_SELCLKPOL | QSPI_CONFIG_SELCLKPHASE;
|
||||
} else {
|
||||
obj->instance->CONFIG &= ~(QSPI_CONFIG_SELCLKPOL | QSPI_CONFIG_SELCLKPHASE);
|
||||
}
|
||||
|
||||
uint32_t loc = pin_location(io0, PinMap_QSPI_DQ0);
|
||||
if (loc != pin_location(io1, PinMap_QSPI_DQ1) ||
|
||||
loc != pin_location(io2, PinMap_QSPI_DQ2) ||
|
||||
loc != pin_location(io3, PinMap_QSPI_DQ3) ||
|
||||
loc != pin_location(sclk, PinMap_QSPI_SCLK) ||
|
||||
loc != pin_location(ssel, PinMap_QSPI_CS0)) {
|
||||
// All pins need to be on the same location number
|
||||
qspi_free(obj);
|
||||
return QSPI_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
// Configure QSPI pins
|
||||
GPIO_PinOutClear((GPIO_Port_TypeDef)(io0 >> 4 & 0xF), io0 & 0xF);
|
||||
pin_mode(io0, PushPull);
|
||||
|
||||
GPIO_PinOutClear((GPIO_Port_TypeDef)(io1 >> 4 & 0xF), io1 & 0xF);
|
||||
pin_mode(io1, PushPull);
|
||||
|
||||
GPIO_PinOutClear((GPIO_Port_TypeDef)(io2 >> 4 & 0xF), io2 & 0xF);
|
||||
pin_mode(io2, PushPull);
|
||||
|
||||
GPIO_PinOutClear((GPIO_Port_TypeDef)(io3 >> 4 & 0xF), io3 & 0xF);
|
||||
pin_mode(io3, PushPull);
|
||||
|
||||
GPIO_PinOutClear((GPIO_Port_TypeDef)(sclk >> 4 & 0xF), sclk & 0xF);
|
||||
pin_mode(sclk, PushPull);
|
||||
|
||||
GPIO_PinOutSet((GPIO_Port_TypeDef)(ssel >> 4 & 0xF), ssel & 0xF);
|
||||
pin_mode(ssel, PushPull);
|
||||
|
||||
|
||||
// Configure QSPI routing to GPIO
|
||||
obj->instance->ROUTELOC0 = loc;
|
||||
obj->instance->ROUTEPEN = QSPI_ROUTEPEN_SCLKPEN
|
||||
| QSPI_ROUTEPEN_CS0PEN
|
||||
| QSPI_ROUTEPEN_DQ0PEN
|
||||
| QSPI_ROUTEPEN_DQ1PEN
|
||||
| QSPI_ROUTEPEN_DQ2PEN
|
||||
| QSPI_ROUTEPEN_DQ3PEN;
|
||||
|
||||
// Configure direct read
|
||||
QSPI_ReadConfig_TypeDef readConfig = QSPI_READCONFIG_DEFAULT;
|
||||
QSPI_ReadConfig(obj->instance, &readConfig);
|
||||
|
||||
// Configure direct write
|
||||
QSPI_WriteConfig_TypeDef writeConfig = QSPI_WRITECONFIG_DEFAULT;
|
||||
QSPI_WriteConfig(obj->instance, &writeConfig);
|
||||
|
||||
return QSPI_STATUS_OK;
|
||||
}
|
||||
|
||||
qspi_status_t qspi_free(qspi_t *obj)
|
||||
{
|
||||
pin_mode(obj->io0, Disabled);
|
||||
pin_mode(obj->io1, Disabled);
|
||||
pin_mode(obj->io2, Disabled);
|
||||
pin_mode(obj->io3, Disabled);
|
||||
pin_mode(obj->ssel, Disabled);
|
||||
pin_mode(obj->sclk, Disabled);
|
||||
|
||||
obj->instance->ROUTEPEN = 0;
|
||||
|
||||
QSPI_Enable(obj->instance, false);
|
||||
CMU_ClockEnable(cmuClock_QSPI0REF, false);
|
||||
CMU_ClockEnable(cmuClock_QSPI0, false);
|
||||
|
||||
return QSPI_STATUS_OK;
|
||||
}
|
||||
|
||||
qspi_status_t qspi_frequency(qspi_t *obj, int hz)
|
||||
{
|
||||
if (hz <= 0) {
|
||||
return QSPI_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
QSPI_Enable(obj->instance, false);
|
||||
|
||||
// Need at least a DIV4 for non-PHY mode and SDR transfers
|
||||
uint32_t basefreq = CMU_ClockFreqGet(cmuClock_QSPI0REF);
|
||||
uint32_t basediv = 4;
|
||||
if ((uint32_t)hz < (basefreq / basediv)) {
|
||||
basediv = (basefreq / hz) + 1;
|
||||
}
|
||||
|
||||
QSPI_Init_TypeDef initQspi = QSPI_INIT_DEFAULT;
|
||||
initQspi.divisor = basediv;
|
||||
QSPI_Init(obj->instance, &initQspi);
|
||||
|
||||
return QSPI_STATUS_OK;
|
||||
}
|
||||
|
||||
qspi_status_t qspi_write(qspi_t *obj, const qspi_command_t *command, const void *data, size_t *length)
|
||||
{
|
||||
QSPI_WriteConfig_TypeDef cfg = QSPI_WRITECONFIG_DEFAULT;
|
||||
uint32_t to_write = *length;
|
||||
|
||||
// Enforce word-aligned and word-sized access
|
||||
if ((to_write & 0x3) != 0 || ((uint32_t)data & 0x3) != 0) {
|
||||
return QSPI_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
cfg.dummyCycles = command->dummy_count;
|
||||
|
||||
if (command->instruction.disabled) {
|
||||
cfg.opCode = 0x02;
|
||||
} else {
|
||||
cfg.opCode = command->instruction.value;
|
||||
}
|
||||
|
||||
if (command->address.disabled) {
|
||||
return QSPI_STATUS_INVALID_PARAMETER;
|
||||
} else {
|
||||
if (command->address.bus_width == QSPI_CFG_BUS_SINGLE) {
|
||||
cfg.addrTransfer = qspiTransferSingle;
|
||||
} else if (command->address.bus_width == QSPI_CFG_BUS_DUAL) {
|
||||
cfg.addrTransfer = qspiTransferDual;
|
||||
} else if (command->address.bus_width == QSPI_CFG_BUS_QUAD) {
|
||||
cfg.addrTransfer = qspiTransferQuad;
|
||||
} else {
|
||||
return QSPI_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
}
|
||||
|
||||
if (command->data.bus_width == QSPI_CFG_BUS_SINGLE) {
|
||||
cfg.dataTransfer = qspiTransferSingle;
|
||||
} else if (command->data.bus_width == QSPI_CFG_BUS_DUAL) {
|
||||
cfg.dataTransfer = qspiTransferDual;
|
||||
} else if (command->data.bus_width == QSPI_CFG_BUS_QUAD) {
|
||||
cfg.dataTransfer = qspiTransferQuad;
|
||||
}
|
||||
|
||||
QSPI_WriteConfig(obj->instance, &cfg);
|
||||
|
||||
if (!command->alt.disabled) {
|
||||
// Do not support alt mode in write mode
|
||||
return QSPI_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
// Do an indirect write
|
||||
obj->instance->INDAHBADDRTRIGGER = QSPI0_MEM_BASE;
|
||||
obj->instance->INDIRECTWRITEXFERSTART = command->address.value;
|
||||
obj->instance->INDIRECTWRITEXFERNUMBYTES = to_write;
|
||||
obj->instance->INDIRECTWRITEXFERCTRL = QSPI_INDIRECTWRITEXFERCTRL_START;
|
||||
|
||||
// For the size of the transfer, poll the SRAM and fetch words from the SRAM
|
||||
for (uint32_t i = 0; i < to_write; i+=4) {
|
||||
// Wait for the QSPI in case we're writing too fast
|
||||
while (((obj->instance->SRAMFILL & _QSPI_SRAMFILL_SRAMFILLINDACWRITE_MASK) >> _QSPI_SRAMFILL_SRAMFILLINDACWRITE_SHIFT) >= 126);
|
||||
|
||||
*((uint32_t*)QSPI0_MEM_BASE) = ((uint32_t*)data)[i/4];
|
||||
}
|
||||
|
||||
return QSPI_STATUS_OK;
|
||||
}
|
||||
|
||||
qspi_status_t qspi_command_transfer(qspi_t *obj, const qspi_command_t *command, const void *tx_data, size_t tx_size, void *rx_data, size_t rx_size)
|
||||
{
|
||||
QSPI_StigCmd_TypeDef cfg;
|
||||
|
||||
if (tx_size > 8 || rx_size > 8) {
|
||||
return QSPI_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
cfg.writeDataSize = tx_size;
|
||||
cfg.writeBuffer = (void*)tx_data;
|
||||
|
||||
cfg.readDataSize = rx_size;
|
||||
cfg.readBuffer = rx_data;
|
||||
|
||||
if (command->address.disabled) {
|
||||
cfg.addrSize = 0;
|
||||
cfg.address = 0;
|
||||
} else {
|
||||
if (command->address.size == QSPI_CFG_ADDR_SIZE_8) {
|
||||
cfg.addrSize = 1;
|
||||
} else if (command->address.size == QSPI_CFG_ADDR_SIZE_16) {
|
||||
cfg.addrSize = 2;
|
||||
} else if (command->address.size == QSPI_CFG_ADDR_SIZE_24) {
|
||||
cfg.addrSize = 3;
|
||||
} else if (command->address.size == QSPI_CFG_ADDR_SIZE_32) {
|
||||
cfg.addrSize = 4;
|
||||
} else {
|
||||
return QSPI_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
cfg.address = command->address.value;
|
||||
}
|
||||
|
||||
if (command->instruction.disabled) {
|
||||
return QSPI_STATUS_INVALID_PARAMETER;
|
||||
} else {
|
||||
cfg.cmdOpcode = command->instruction.value;
|
||||
}
|
||||
|
||||
cfg.dummyCycles = command->dummy_count;
|
||||
|
||||
if (!command->alt.disabled) {
|
||||
cfg.modeBitEnable = true;
|
||||
obj->instance->MODEBITCONFIG = command->alt.value & _QSPI_MODEBITCONFIG_MODE_MASK;
|
||||
|
||||
if(command->alt.size != QSPI_CFG_ALT_SIZE_8) {
|
||||
//do not support 'alt' bigger than 8 bit
|
||||
return QSPI_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
} else {
|
||||
cfg.modeBitEnable = false;
|
||||
}
|
||||
|
||||
QSPI_ExecStigCmd(obj->instance, &cfg);
|
||||
|
||||
return QSPI_STATUS_OK;
|
||||
}
|
||||
|
||||
qspi_status_t qspi_read(qspi_t *obj, const qspi_command_t *command, void *data, size_t *length)
|
||||
{
|
||||
QSPI_ReadConfig_TypeDef cfg = QSPI_READCONFIG_DEFAULT;
|
||||
uint32_t to_read = *length;
|
||||
|
||||
// Enforce word-aligned and word-sized access
|
||||
if ((to_read & 0x3) != 0 || ((uint32_t)data & 0x3) != 0) {
|
||||
return QSPI_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
cfg.dummyCycles = command->dummy_count;
|
||||
|
||||
if (command->instruction.disabled) {
|
||||
cfg.opCode = 0x03;
|
||||
cfg.instTransfer = qspiTransferSingle;
|
||||
} else {
|
||||
cfg.opCode = command->instruction.value;
|
||||
if (command->instruction.bus_width == QSPI_CFG_BUS_SINGLE) {
|
||||
cfg.instTransfer = qspiTransferSingle;
|
||||
} else if (command->instruction.bus_width == QSPI_CFG_BUS_DUAL) {
|
||||
cfg.instTransfer = qspiTransferDual;
|
||||
} else if (command->instruction.bus_width == QSPI_CFG_BUS_QUAD) {
|
||||
cfg.instTransfer = qspiTransferQuad;
|
||||
} else {
|
||||
return QSPI_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
}
|
||||
|
||||
if (command->address.disabled) {
|
||||
return QSPI_STATUS_INVALID_PARAMETER;
|
||||
} else {
|
||||
if (command->address.bus_width == QSPI_CFG_BUS_SINGLE) {
|
||||
cfg.addrTransfer = qspiTransferSingle;
|
||||
} else if (command->address.bus_width == QSPI_CFG_BUS_DUAL) {
|
||||
cfg.addrTransfer = qspiTransferDual;
|
||||
} else if (command->address.bus_width == QSPI_CFG_BUS_QUAD) {
|
||||
cfg.addrTransfer = qspiTransferQuad;
|
||||
} else {
|
||||
return QSPI_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
}
|
||||
|
||||
if (command->data.bus_width == QSPI_CFG_BUS_SINGLE) {
|
||||
cfg.dataTransfer = qspiTransferSingle;
|
||||
} else if (command->data.bus_width == QSPI_CFG_BUS_DUAL) {
|
||||
cfg.dataTransfer = qspiTransferDual;
|
||||
} else if (command->data.bus_width == QSPI_CFG_BUS_QUAD) {
|
||||
cfg.dataTransfer = qspiTransferQuad;
|
||||
}
|
||||
|
||||
QSPI_ReadConfig(obj->instance, &cfg);
|
||||
|
||||
if (!command->alt.disabled) {
|
||||
// Need to set up alt mode manually, called 'mode bits' in EFM32GG11 refman
|
||||
obj->instance->DEVINSTRRDCONFIG |= QSPI_DEVINSTRRDCONFIG_MODEBITENABLE;
|
||||
obj->instance->MODEBITCONFIG = command->alt.value & _QSPI_MODEBITCONFIG_MODE_MASK;
|
||||
|
||||
if(command->alt.size != QSPI_CFG_ALT_SIZE_8) {
|
||||
// Do not support 'alt' bigger than 8 bit
|
||||
return QSPI_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
}
|
||||
|
||||
// Do an indirect read
|
||||
obj->instance->INDAHBADDRTRIGGER = QSPI0_MEM_BASE;
|
||||
obj->instance->INDIRECTREADXFERSTART = command->address.value;
|
||||
obj->instance->INDIRECTREADXFERNUMBYTES = to_read;
|
||||
obj->instance->INDIRECTREADXFERCTRL = QSPI_INDIRECTREADXFERCTRL_START;
|
||||
|
||||
// For the size of the transfer, poll the SRAM and fetch words from the SRAM
|
||||
for (uint32_t i = 0; i < to_read; i+=4) {
|
||||
// Wait for the FIFO in case we're reading too fast
|
||||
while ((obj->instance->SRAMFILL & _QSPI_SRAMFILL_SRAMFILLINDACREAD_MASK) >> _QSPI_SRAMFILL_SRAMFILLINDACREAD_SHIFT == 0);
|
||||
|
||||
((uint32_t*)data)[i/4] = *((uint32_t*)QSPI0_MEM_BASE);
|
||||
}
|
||||
|
||||
return QSPI_STATUS_OK;
|
||||
}
|
||||
|
||||
#endif /* DEVICE_QSPI && QSPI_PRESENT */
|
|
@ -3628,7 +3628,7 @@
|
|||
"EFM32GG11_STK3701": {
|
||||
"inherits": ["EFM32GG11B820F2048GL192"],
|
||||
"device_name": "EFM32GG11B820F2048GL192",
|
||||
"device_has": ["ANALOGIN", "CRC", "EMAC", "I2C", "I2CSLAVE", "I2C_ASYNCH", "INTERRUPTIN", "LPTICKER", "PORTIN", "PORTINOUT", "PORTOUT", "PWMOUT", "RTC", "SERIAL", "SERIAL_ASYNCH", "SLEEP", "SPI", "SPISLAVE", "SPI_ASYNCH", "STDIO_MESSAGES", "USTICKER", "TRNG", "FLASH"],
|
||||
"device_has": ["ANALOGIN", "CRC", "EMAC", "I2C", "I2CSLAVE", "I2C_ASYNCH", "INTERRUPTIN", "LPTICKER", "PORTIN", "PORTINOUT", "PORTOUT", "PWMOUT", "QSPI", "RTC", "SERIAL", "SERIAL_ASYNCH", "SLEEP", "SPI", "SPISLAVE", "SPI_ASYNCH", "STDIO_MESSAGES", "USTICKER", "TRNG", "FLASH"],
|
||||
"forced_reset_timeout": 5,
|
||||
"config": {
|
||||
"hf_clock_src": {
|
||||
|
@ -3665,6 +3665,11 @@
|
|||
"help": "Pin to pull high for enabling the USB serial port",
|
||||
"value": "PE1",
|
||||
"macro_name": "EFM_BC_EN"
|
||||
},
|
||||
"qspi_flash_enable": {
|
||||
"help": "Pin to pull high for enabling the on-board QSPI flash",
|
||||
"value": "PG13",
|
||||
"macro_name": "QSPI_FLASH_EN"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue