From 6dc0f4f1b68f4ac24bf85cc51252106ccfe43776 Mon Sep 17 00:00:00 2001 From: Yihui Xiong Date: Wed, 5 Aug 2020 01:10:58 +0800 Subject: [PATCH 1/5] add an option to turn off QSPI when sleep --- .../makerdiary_m60_keyboard/mpconfigboard.h | 1 + ports/nrf/supervisor/port.c | 13 ++++++++ ports/nrf/supervisor/qspi_flash.c | 33 +++++++++++++++++++ 3 files changed, 47 insertions(+) diff --git a/ports/nrf/boards/makerdiary_m60_keyboard/mpconfigboard.h b/ports/nrf/boards/makerdiary_m60_keyboard/mpconfigboard.h index 086718089a..04a49e5e23 100644 --- a/ports/nrf/boards/makerdiary_m60_keyboard/mpconfigboard.h +++ b/ports/nrf/boards/makerdiary_m60_keyboard/mpconfigboard.h @@ -40,6 +40,7 @@ #define MICROPY_QSPI_DATA3 NRF_GPIO_PIN_MAP(1, 12) #define MICROPY_QSPI_SCK NRF_GPIO_PIN_MAP(1, 11) #define MICROPY_QSPI_CS NRF_GPIO_PIN_MAP(1, 13) +#define MICROPY_QSPI_OFF_WHEN_SLEEP #define BOARD_HAS_CRYSTAL 1 diff --git a/ports/nrf/supervisor/port.c b/ports/nrf/supervisor/port.c index e681e6825f..68d6f5f712 100644 --- a/ports/nrf/supervisor/port.c +++ b/ports/nrf/supervisor/port.c @@ -295,6 +295,19 @@ void port_interrupt_after_ticks(uint32_t ticks) { } void port_sleep_until_interrupt(void) { +#if defined(MICROPY_QSPI_CS) && defined(MICROPY_QSPI_OFF_WHEN_SLEEP) + // Turn off QSPI when USB is disconnected + if (NRF_QSPI->ENABLE && !(NRF_POWER->USBREGSTATUS & POWER_USBREGSTATUS_VBUSDETECT_Msk)) { + // Keep CS high when QSPI is diabled + nrf_gpio_cfg_output(MICROPY_QSPI_CS); + nrf_gpio_pin_write(MICROPY_QSPI_CS, 1); + + *(volatile uint32_t *)0x40029010 = 1; + *(volatile uint32_t *)0x40029054 = 1; + NRF_QSPI->ENABLE = 0; + } +#endif + // Clear the FPU interrupt because it can prevent us from sleeping. if (NVIC_GetPendingIRQ(FPU_IRQn)) { __set_FPSCR(__get_FPSCR() & ~(0x9f)); diff --git a/ports/nrf/supervisor/qspi_flash.c b/ports/nrf/supervisor/qspi_flash.c index 90260b0912..83261f0800 100644 --- a/ports/nrf/supervisor/qspi_flash.c +++ b/ports/nrf/supervisor/qspi_flash.c @@ -38,7 +38,35 @@ #include "supervisor/shared/external_flash/common_commands.h" #include "supervisor/shared/external_flash/qspi_flash.h" +#if defined(MICROPY_QSPI_OFF_WHEN_SLEEP) +#define QSPI_ENABLE qspi_enable + +static void qspi_enable(void) +{ + if (NRF_QSPI->ENABLE) { + return; + } + + nrf_qspi_enable(NRF_QSPI); + + nrf_qspi_event_clear(NRF_QSPI, NRF_QSPI_EVENT_READY); + nrf_qspi_task_trigger(NRF_QSPI, NRF_QSPI_TASK_ACTIVATE); + + uint32_t remaining_attempts = 100; + do { + if (nrf_qspi_event_check(NRF_QSPI, NRF_QSPI_EVENT_READY)) { + break; + } + NRFX_DELAY_US(10); + } while (--remaining_attempts); +} + +#else +#define QSPI_ENABLE() +#endif + bool spi_flash_command(uint8_t command) { + QSPI_ENABLE(); nrf_qspi_cinstr_conf_t cinstr_cfg = { .opcode = command, .length = 1, @@ -51,6 +79,7 @@ bool spi_flash_command(uint8_t command) { } bool spi_flash_read_command(uint8_t command, uint8_t* response, uint32_t length) { + QSPI_ENABLE(); nrf_qspi_cinstr_conf_t cinstr_cfg = { .opcode = command, .length = length + 1, @@ -64,6 +93,7 @@ bool spi_flash_read_command(uint8_t command, uint8_t* response, uint32_t length) } bool spi_flash_write_command(uint8_t command, uint8_t* data, uint32_t length) { + QSPI_ENABLE(); nrf_qspi_cinstr_conf_t cinstr_cfg = { .opcode = command, .length = length + 1, @@ -76,6 +106,7 @@ bool spi_flash_write_command(uint8_t command, uint8_t* data, uint32_t length) { } bool spi_flash_sector_command(uint8_t command, uint32_t address) { + QSPI_ENABLE(); if (command != CMD_SECTOR_ERASE) { return false; } @@ -83,6 +114,7 @@ bool spi_flash_sector_command(uint8_t command, uint32_t address) { } bool spi_flash_write_data(uint32_t address, uint8_t* data, uint32_t length) { + QSPI_ENABLE(); // TODO: In theory, this also needs to handle unaligned data and // non-multiple-of-4 length. (in practice, I don't think the fat layer // generates such writes) @@ -90,6 +122,7 @@ bool spi_flash_write_data(uint32_t address, uint8_t* data, uint32_t length) { } bool spi_flash_read_data(uint32_t address, uint8_t* data, uint32_t length) { + QSPI_ENABLE(); int misaligned = ((intptr_t)data) & 3; // If the data is misaligned, we need to read 4 bytes // into an aligned buffer, and then copy 1, 2, or 3 bytes from the aligned From dbe47a6a2a3a5e34f11cc18c397dbdedff2ad462 Mon Sep 17 00:00:00 2001 From: Yihui Xiong Date: Wed, 5 Aug 2020 16:07:18 +0800 Subject: [PATCH 2/5] adjust --- ports/nrf/supervisor/port.c | 3 ++- ports/nrf/supervisor/qspi_flash.c | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/ports/nrf/supervisor/port.c b/ports/nrf/supervisor/port.c index 68d6f5f712..05970a3edf 100644 --- a/ports/nrf/supervisor/port.c +++ b/ports/nrf/supervisor/port.c @@ -302,7 +302,8 @@ void port_sleep_until_interrupt(void) { nrf_gpio_cfg_output(MICROPY_QSPI_CS); nrf_gpio_pin_write(MICROPY_QSPI_CS, 1); - *(volatile uint32_t *)0x40029010 = 1; + // Workaround to disable QSPI according to nRF52840 Revision 1 Errata V1.4 - 3.8 + NRF_QSPI->TASKS_DEACTIVATE = 1; *(volatile uint32_t *)0x40029054 = 1; NRF_QSPI->ENABLE = 0; } diff --git a/ports/nrf/supervisor/qspi_flash.c b/ports/nrf/supervisor/qspi_flash.c index 83261f0800..d5c60ac9ee 100644 --- a/ports/nrf/supervisor/qspi_flash.c +++ b/ports/nrf/supervisor/qspi_flash.c @@ -39,7 +39,7 @@ #include "supervisor/shared/external_flash/qspi_flash.h" #if defined(MICROPY_QSPI_OFF_WHEN_SLEEP) -#define QSPI_ENABLE qspi_enable +#define QSPI_ENABLE() qspi_enable() static void qspi_enable(void) { @@ -62,7 +62,7 @@ static void qspi_enable(void) } #else -#define QSPI_ENABLE() +#define QSPI_ENABLE() ((void)0) #endif bool spi_flash_command(uint8_t command) { From 7854625c4edac2dff663d693232e8c05380d960a Mon Sep 17 00:00:00 2001 From: Yihui Xiong Date: Wed, 5 Aug 2020 16:08:13 +0800 Subject: [PATCH 3/5] avoid using the RGB LEDs to save energy --- ports/nrf/boards/makerdiary_m60_keyboard/mpconfigboard.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ports/nrf/boards/makerdiary_m60_keyboard/mpconfigboard.h b/ports/nrf/boards/makerdiary_m60_keyboard/mpconfigboard.h index 04a49e5e23..e8c268d5bf 100644 --- a/ports/nrf/boards/makerdiary_m60_keyboard/mpconfigboard.h +++ b/ports/nrf/boards/makerdiary_m60_keyboard/mpconfigboard.h @@ -30,9 +30,10 @@ #define MICROPY_HW_BOARD_NAME "Makerdiary M60 Keyboard" #define MICROPY_HW_MCU_NAME "nRF52840" -#define CP_RGB_STATUS_R (&pin_P0_30) -#define CP_RGB_STATUS_G (&pin_P0_29) -#define CP_RGB_STATUS_B (&pin_P0_31) +// not use the RGB LEDs to save energy +// #define CP_RGB_STATUS_R (&pin_P0_30) +// #define CP_RGB_STATUS_G (&pin_P0_29) +// #define CP_RGB_STATUS_B (&pin_P0_31) #define MICROPY_QSPI_DATA0 NRF_GPIO_PIN_MAP(1, 10) #define MICROPY_QSPI_DATA1 NRF_GPIO_PIN_MAP(1, 14) From d8257380d7e1658d710bf841424a64c2c6451d5f Mon Sep 17 00:00:00 2001 From: Yihui Xiong Date: Thu, 6 Aug 2020 09:56:05 +0800 Subject: [PATCH 4/5] add qspi_disable() --- ports/nrf/supervisor/port.c | 16 ++++--------- ports/nrf/supervisor/qspi_flash.c | 39 +++++++++++++++++++++++-------- 2 files changed, 34 insertions(+), 21 deletions(-) diff --git a/ports/nrf/supervisor/port.c b/ports/nrf/supervisor/port.c index 05970a3edf..36c9f836ea 100644 --- a/ports/nrf/supervisor/port.c +++ b/ports/nrf/supervisor/port.c @@ -65,6 +65,10 @@ #include "common-hal/audiopwmio/PWMAudioOut.h" #endif +#if defined(MICROPY_QSPI_CS) && defined(MICROPY_QSPI_OFF_WHEN_SLEEP) +extern void qspi_disable(void); +#endif + static void power_warning_handler(void) { reset_into_safe_mode(BROWNOUT); } @@ -296,17 +300,7 @@ void port_interrupt_after_ticks(uint32_t ticks) { void port_sleep_until_interrupt(void) { #if defined(MICROPY_QSPI_CS) && defined(MICROPY_QSPI_OFF_WHEN_SLEEP) - // Turn off QSPI when USB is disconnected - if (NRF_QSPI->ENABLE && !(NRF_POWER->USBREGSTATUS & POWER_USBREGSTATUS_VBUSDETECT_Msk)) { - // Keep CS high when QSPI is diabled - nrf_gpio_cfg_output(MICROPY_QSPI_CS); - nrf_gpio_pin_write(MICROPY_QSPI_CS, 1); - - // Workaround to disable QSPI according to nRF52840 Revision 1 Errata V1.4 - 3.8 - NRF_QSPI->TASKS_DEACTIVATE = 1; - *(volatile uint32_t *)0x40029054 = 1; - NRF_QSPI->ENABLE = 0; - } + qspi_disable(); #endif // Clear the FPU interrupt because it can prevent us from sleeping. diff --git a/ports/nrf/supervisor/qspi_flash.c b/ports/nrf/supervisor/qspi_flash.c index d5c60ac9ee..852f0c44a5 100644 --- a/ports/nrf/supervisor/qspi_flash.c +++ b/ports/nrf/supervisor/qspi_flash.c @@ -38,10 +38,9 @@ #include "supervisor/shared/external_flash/common_commands.h" #include "supervisor/shared/external_flash/qspi_flash.h" +// When USB is disconnected, disable QSPI in sleep mode to save energy #if defined(MICROPY_QSPI_OFF_WHEN_SLEEP) -#define QSPI_ENABLE() qspi_enable() - -static void qspi_enable(void) +void qspi_enable(void) { if (NRF_QSPI->ENABLE) { return; @@ -61,12 +60,32 @@ static void qspi_enable(void) } while (--remaining_attempts); } +void qspi_disable(void) +{ + // Turn off QSPI when USB is disconnected + if (NRF_QSPI->ENABLE && !(NRF_POWER->USBREGSTATUS & POWER_USBREGSTATUS_VBUSDETECT_Msk)) { + // Keep CS high when QSPI is diabled + nrf_gpio_cfg_output(MICROPY_QSPI_CS); + nrf_gpio_pin_write(MICROPY_QSPI_CS, 1); + + // Workaround to disable QSPI according to nRF52840 Revision 1 Errata V1.4 - 3.8 + NRF_QSPI->TASKS_DEACTIVATE = 1; + *(volatile uint32_t *)0x40029054 = 1; + NRF_QSPI->ENABLE = 0; + } +} #else -#define QSPI_ENABLE() ((void)0) +void qspi_enable(void) +{ +} + +void qspi_disable(void) +{ +} #endif bool spi_flash_command(uint8_t command) { - QSPI_ENABLE(); + qspi_enable(); nrf_qspi_cinstr_conf_t cinstr_cfg = { .opcode = command, .length = 1, @@ -79,7 +98,7 @@ bool spi_flash_command(uint8_t command) { } bool spi_flash_read_command(uint8_t command, uint8_t* response, uint32_t length) { - QSPI_ENABLE(); + qspi_enable(); nrf_qspi_cinstr_conf_t cinstr_cfg = { .opcode = command, .length = length + 1, @@ -93,7 +112,7 @@ bool spi_flash_read_command(uint8_t command, uint8_t* response, uint32_t length) } bool spi_flash_write_command(uint8_t command, uint8_t* data, uint32_t length) { - QSPI_ENABLE(); + qspi_enable(); nrf_qspi_cinstr_conf_t cinstr_cfg = { .opcode = command, .length = length + 1, @@ -106,7 +125,7 @@ bool spi_flash_write_command(uint8_t command, uint8_t* data, uint32_t length) { } bool spi_flash_sector_command(uint8_t command, uint32_t address) { - QSPI_ENABLE(); + qspi_enable(); if (command != CMD_SECTOR_ERASE) { return false; } @@ -114,7 +133,7 @@ bool spi_flash_sector_command(uint8_t command, uint32_t address) { } bool spi_flash_write_data(uint32_t address, uint8_t* data, uint32_t length) { - QSPI_ENABLE(); + qspi_enable(); // TODO: In theory, this also needs to handle unaligned data and // non-multiple-of-4 length. (in practice, I don't think the fat layer // generates such writes) @@ -122,7 +141,7 @@ bool spi_flash_write_data(uint32_t address, uint8_t* data, uint32_t length) { } bool spi_flash_read_data(uint32_t address, uint8_t* data, uint32_t length) { - QSPI_ENABLE(); + qspi_enable(); int misaligned = ((intptr_t)data) & 3; // If the data is misaligned, we need to read 4 bytes // into an aligned buffer, and then copy 1, 2, or 3 bytes from the aligned From af1291ec28a8d2881e820fc1ce9b938ab5a6f097 Mon Sep 17 00:00:00 2001 From: Yihui Xiong Date: Fri, 7 Aug 2020 16:23:42 +0800 Subject: [PATCH 5/5] dynamically enable or disable QSPI by default --- .../makerdiary_m60_keyboard/mpconfigboard.h | 3 +- ports/nrf/supervisor/port.c | 4 +- ports/nrf/supervisor/qspi_flash.c | 40 +++++++------------ 3 files changed, 18 insertions(+), 29 deletions(-) diff --git a/ports/nrf/boards/makerdiary_m60_keyboard/mpconfigboard.h b/ports/nrf/boards/makerdiary_m60_keyboard/mpconfigboard.h index e8c268d5bf..4fb6049c5f 100644 --- a/ports/nrf/boards/makerdiary_m60_keyboard/mpconfigboard.h +++ b/ports/nrf/boards/makerdiary_m60_keyboard/mpconfigboard.h @@ -30,7 +30,7 @@ #define MICROPY_HW_BOARD_NAME "Makerdiary M60 Keyboard" #define MICROPY_HW_MCU_NAME "nRF52840" -// not use the RGB LEDs to save energy +// RGB LEDs use PWM peripheral, avoid using them to save energy // #define CP_RGB_STATUS_R (&pin_P0_30) // #define CP_RGB_STATUS_G (&pin_P0_29) // #define CP_RGB_STATUS_B (&pin_P0_31) @@ -41,7 +41,6 @@ #define MICROPY_QSPI_DATA3 NRF_GPIO_PIN_MAP(1, 12) #define MICROPY_QSPI_SCK NRF_GPIO_PIN_MAP(1, 11) #define MICROPY_QSPI_CS NRF_GPIO_PIN_MAP(1, 13) -#define MICROPY_QSPI_OFF_WHEN_SLEEP #define BOARD_HAS_CRYSTAL 1 diff --git a/ports/nrf/supervisor/port.c b/ports/nrf/supervisor/port.c index 36c9f836ea..87a4d7396f 100644 --- a/ports/nrf/supervisor/port.c +++ b/ports/nrf/supervisor/port.c @@ -65,7 +65,7 @@ #include "common-hal/audiopwmio/PWMAudioOut.h" #endif -#if defined(MICROPY_QSPI_CS) && defined(MICROPY_QSPI_OFF_WHEN_SLEEP) +#if defined(MICROPY_QSPI_CS) extern void qspi_disable(void); #endif @@ -299,7 +299,7 @@ void port_interrupt_after_ticks(uint32_t ticks) { } void port_sleep_until_interrupt(void) { -#if defined(MICROPY_QSPI_CS) && defined(MICROPY_QSPI_OFF_WHEN_SLEEP) +#if defined(MICROPY_QSPI_CS) qspi_disable(); #endif diff --git a/ports/nrf/supervisor/qspi_flash.c b/ports/nrf/supervisor/qspi_flash.c index 852f0c44a5..5ab56c6bc4 100644 --- a/ports/nrf/supervisor/qspi_flash.c +++ b/ports/nrf/supervisor/qspi_flash.c @@ -39,7 +39,21 @@ #include "supervisor/shared/external_flash/qspi_flash.h" // When USB is disconnected, disable QSPI in sleep mode to save energy -#if defined(MICROPY_QSPI_OFF_WHEN_SLEEP) +void qspi_disable(void) +{ + // If VBUS is detected, no need to disable QSPI + if (NRF_QSPI->ENABLE && !(NRF_POWER->USBREGSTATUS & POWER_USBREGSTATUS_VBUSDETECT_Msk)) { + // Keep CS high when QSPI is diabled + nrf_gpio_cfg_output(MICROPY_QSPI_CS); + nrf_gpio_pin_write(MICROPY_QSPI_CS, 1); + + // Workaround to disable QSPI according to nRF52840 Revision 1 Errata V1.4 - 3.8 + NRF_QSPI->TASKS_DEACTIVATE = 1; + *(volatile uint32_t *)0x40029054 = 1; + NRF_QSPI->ENABLE = 0; + } +} + void qspi_enable(void) { if (NRF_QSPI->ENABLE) { @@ -60,30 +74,6 @@ void qspi_enable(void) } while (--remaining_attempts); } -void qspi_disable(void) -{ - // Turn off QSPI when USB is disconnected - if (NRF_QSPI->ENABLE && !(NRF_POWER->USBREGSTATUS & POWER_USBREGSTATUS_VBUSDETECT_Msk)) { - // Keep CS high when QSPI is diabled - nrf_gpio_cfg_output(MICROPY_QSPI_CS); - nrf_gpio_pin_write(MICROPY_QSPI_CS, 1); - - // Workaround to disable QSPI according to nRF52840 Revision 1 Errata V1.4 - 3.8 - NRF_QSPI->TASKS_DEACTIVATE = 1; - *(volatile uint32_t *)0x40029054 = 1; - NRF_QSPI->ENABLE = 0; - } -} -#else -void qspi_enable(void) -{ -} - -void qspi_disable(void) -{ -} -#endif - bool spi_flash_command(uint8_t command) { qspi_enable(); nrf_qspi_cinstr_conf_t cinstr_cfg = {