From d226dd2f5999d27533386c4cd95a4200b38f3b65 Mon Sep 17 00:00:00 2001 From: danicampora Date: Fri, 27 Feb 2015 16:50:06 +0100 Subject: [PATCH] cc3200: Add preliminary low power deep sleep support. --- cc3200/application.mk | 1 + cc3200/boards/cc3200_prefix.c | 15 +- cc3200/mods/modnetwork.c | 2 +- cc3200/mods/modnetwork.h | 2 +- cc3200/mods/pybadc.c | 3 +- cc3200/mods/pybextint.c | 2 +- cc3200/mods/pybpin.c | 42 +- cc3200/mods/pybpin.h | 13 +- cc3200/mods/pybsd.c | 2 +- cc3200/mods/pybsleep.c | 845 ++++++++++++++++++++++++++++++++++ cc3200/mods/pybsleep.h | 37 ++ cc3200/mods/pybuart.c | 5 + cc3200/mpconfigport.h | 1 + cc3200/mptask.c | 8 +- 14 files changed, 951 insertions(+), 27 deletions(-) create mode 100644 cc3200/mods/pybsleep.c create mode 100644 cc3200/mods/pybsleep.h diff --git a/cc3200/application.mk b/cc3200/application.mk index 59aac13770..7f8a4fafc3 100644 --- a/cc3200/application.mk +++ b/cc3200/application.mk @@ -94,6 +94,7 @@ APP_MODS_SRC_C = $(addprefix mods/,\ pybpin.c \ pybrtc.c \ pybsd.c \ + pybsleep.c \ pybsystick.c \ pybuart.c \ pybwdt.c \ diff --git a/cc3200/boards/cc3200_prefix.c b/cc3200/boards/cc3200_prefix.c index 27f388d95a..26afba9a27 100644 --- a/cc3200/boards/cc3200_prefix.c +++ b/cc3200/boards/cc3200_prefix.c @@ -34,14 +34,21 @@ #include "py/obj.h" #include "inc/hw_types.h" #include "inc/hw_memmap.h" +#include "pin.h" +#include "gpio.h" #include "pybpin.h" #define PIN(p_pin_name, p_port, p_bit, p_pin_num) \ { \ { &pin_type }, \ - .name = MP_QSTR_ ## p_pin_name, \ - .port = PORT_A ## p_port, \ - .bit = (p_bit), \ - .pin_num = (p_pin_num) \ + .name = MP_QSTR_ ## p_pin_name, \ + .port = PORT_A ## p_port, \ + .type = PIN_TYPE_STD, \ + .bit = (p_bit), \ + .pin_num = (p_pin_num), \ + .af = PIN_MODE_0, \ + .strength = PIN_STRENGTH_4MA, \ + .mode = GPIO_DIR_MODE_IN, \ + .used = false \ } diff --git a/cc3200/mods/modnetwork.c b/cc3200/mods/modnetwork.c index 7b5ae5f006..0ab5686fd1 100644 --- a/cc3200/mods/modnetwork.c +++ b/cc3200/mods/modnetwork.c @@ -38,7 +38,7 @@ /// /// This module provides network drivers and routing configuration. -void mod_network_init(void) { +void mod_network_init0(void) { mp_obj_list_init(&MP_STATE_PORT(mod_network_nic_list), 0); } diff --git a/cc3200/mods/modnetwork.h b/cc3200/mods/modnetwork.h index a5b89c8a1e..00e10b2c71 100644 --- a/cc3200/mods/modnetwork.h +++ b/cc3200/mods/modnetwork.h @@ -70,7 +70,7 @@ typedef struct _mod_network_socket_obj_t { extern const mod_network_nic_type_t mod_network_nic_type_wlan; -void mod_network_init(void); +void mod_network_init0(void); void mod_network_register_nic(mp_obj_t nic); mp_obj_t mod_network_find_nic(const uint8_t *ip); diff --git a/cc3200/mods/pybadc.c b/cc3200/mods/pybadc.c index 03602e99b1..fb2ae16b4a 100644 --- a/cc3200/mods/pybadc.c +++ b/cc3200/mods/pybadc.c @@ -42,6 +42,7 @@ #include "rom_map.h" #include "interrupt.h" #include "pin.h" +#include "gpio.h" #include "prcm.h" #include "adc.h" #include "pybadc.h" @@ -117,7 +118,7 @@ STATIC mp_obj_t adc_make_new(mp_obj_t type_in, mp_uint_t n_args, mp_uint_t n_kw, self->num = num; // configure the pin in analog mode - pin_config (pin, 0, 0, PIN_TYPE_ANALOG, PIN_STRENGTH_2MA); + pin_config ((pin_obj_t *)pin, PIN_MODE_0, GPIO_DIR_MODE_IN, PYBPIN_ANALOG_TYPE, PIN_STRENGTH_2MA); // enable the ADC channel MAP_ADCChannelEnable(ADC_BASE, channel); diff --git a/cc3200/mods/pybextint.c b/cc3200/mods/pybextint.c index c135c4f560..09eaeee1c4 100644 --- a/cc3200/mods/pybextint.c +++ b/cc3200/mods/pybextint.c @@ -303,7 +303,7 @@ extint_obj_t* extint_register(mp_obj_t pin_obj, uint32_t intmode, uint32_t pull, self->callback = NULL; // before enabling the interrupt, configure the gpio pin - pin_config(pin, PIN_MODE_0, GPIO_DIR_MODE_IN, pull, PIN_STRENGTH_4MA); + pin_config ((pin_obj_t *)pin, PIN_MODE_0, GPIO_DIR_MODE_IN, pull, PIN_STRENGTH_4MA); MAP_GPIOIntTypeSet(pin->port, pin->bit, intmode); switch (pin->port) { diff --git a/cc3200/mods/pybpin.c b/cc3200/mods/pybpin.c index 505c2a105e..095440f79c 100644 --- a/cc3200/mods/pybpin.c +++ b/cc3200/mods/pybpin.c @@ -42,6 +42,7 @@ #include "prcm.h" #include "gpio.h" #include "pybpin.h" +#include "pybsleep.h" #include "mpexception.h" @@ -78,7 +79,13 @@ /// 2. Supply a string which matches a CPU pin name /// 3. Provide a pin number + +STATIC mp_obj_t pin_obj_init_helper(const pin_obj_t *pin, mp_uint_t n_args, const mp_obj_t *args, mp_map_t *kw_args); +STATIC void pin_obj_configure (const pin_obj_t *self); + + void pin_init0(void) { + } // C API used to convert a user-supplied pin name into an ordinal pin number. @@ -116,11 +123,26 @@ void pin_verify_af (uint af) { } } -void pin_config(const pin_obj_t *self, uint af, uint mode, uint type, uint strength) { +void pin_config (pin_obj_t *self, uint af, uint mode, uint type, uint strength) { + // configure the pin in analog mode + ((pin_obj_t *)self)->af = af; + ((pin_obj_t *)self)->mode = mode; + ((pin_obj_t *)self)->type = type; + ((pin_obj_t *)self)->strength = strength; + pin_obj_configure ((const pin_obj_t *)self); + // mark the pin as used + ((pin_obj_t *)self)->used = true; + // register it with the sleep module + pybsleep_add (self, (WakeUpCB_t)pin_obj_configure); +} + +STATIC void pin_obj_configure (const pin_obj_t *self) { // Skip all this if the pin is to be used in analog mode - if (type != PIN_TYPE_ANALOG) { - // PIN_MODE_0 means it stays as a Pin, else, another peripheral will take control of it - if (af == PIN_MODE_0) { + if (self->type != PYBPIN_ANALOG_TYPE) { + // verify the alternate function + pin_verify_af (self->af); + // PIN_MODE_0 means it stays as a pin, else, another peripheral will take control of it + if (self->af == PIN_MODE_0) { // enable the peripheral clock for the GPIO port of this pin switch (self->port) { case PORT_A0: @@ -139,14 +161,12 @@ void pin_config(const pin_obj_t *self, uint af, uint mode, uint type, uint stren break; } // configure the direction - MAP_GPIODirModeSet(self->port, self->bit, mode); + MAP_GPIODirModeSet(self->port, self->bit, self->mode); } - // verify the alternate function - pin_verify_af (af); // now set the alternate function, strenght and type - MAP_PinModeSet (self->pin_num, af); + MAP_PinModeSet (self->pin_num, self->af); } - MAP_PinConfigSet(self->pin_num, strength, type); + MAP_PinConfigSet(self->pin_num, self->strength, self->type); } /// \method print() @@ -201,8 +221,6 @@ STATIC void pin_print(void (*print)(void *env, const char *fmt, ...), void *env, print(env, ", strength=Pin.%s)", qstr_str(str_qst)); } -STATIC mp_obj_t pin_obj_init_helper(const pin_obj_t *pin, mp_uint_t n_args, const mp_obj_t *args, mp_map_t *kw_args); - /// \classmethod \constructor(id, ...) /// Create a new Pin object associated with the id. If additional arguments are given, /// they are used to initialise the pin. See `init`. @@ -280,7 +298,7 @@ STATIC mp_obj_t pin_obj_init_helper(const pin_obj_t *self, mp_uint_t n_args, con } // configure the pin as requested - pin_config (self, af, mode, type, strength); + pin_config ((pin_obj_t *)self, af, mode, type, strength); return mp_const_none; } diff --git a/cc3200/mods/pybpin.h b/cc3200/mods/pybpin.h index 7a407d7bf1..0759022f1a 100644 --- a/cc3200/mods/pybpin.h +++ b/cc3200/mods/pybpin.h @@ -30,12 +30,19 @@ #include MICROPY_PIN_DEFS_PORT_H +#define PYBPIN_ANALOG_TYPE 0xFF + typedef struct { mp_obj_base_t base; qstr name; uint32_t port; - uint32_t bit : 8; - uint32_t pin_num : 7; + uint16_t type; + uint8_t bit; + uint8_t pin_num; + uint8_t af; + uint8_t strength; + uint8_t mode; + bool used; } pin_obj_t; extern const mp_obj_type_t pin_type; @@ -58,7 +65,7 @@ MP_DECLARE_CONST_FUN_OBJ(pin_init_obj); void pin_init0(void); void pin_verify_af (uint af); -void pin_config(const pin_obj_t *self, uint af, uint mode, uint type, uint strength); +void pin_config(pin_obj_t *self, uint af, uint mode, uint type, uint strength); const pin_obj_t *pin_find(mp_obj_t user_obj); const pin_obj_t *pin_find_named_pin(const mp_obj_dict_t *named_pins, mp_obj_t name); const pin_obj_t *pin_find_pin(const mp_obj_dict_t *named_pins, uint pin_num); diff --git a/cc3200/mods/pybsd.c b/cc3200/mods/pybsd.c index 69eb81581d..4ae8772503 100644 --- a/cc3200/mods/pybsd.c +++ b/cc3200/mods/pybsd.c @@ -138,7 +138,7 @@ STATIC mp_obj_t pybsd_make_new (mp_obj_t type_in, mp_uint_t n_args, mp_uint_t n_ // card detect pin was provided if (n_args == 7) { pybsd_obj.pin_sd_detect = (pin_obj_t *)pin_find(args[6]); - pin_config(pybsd_obj.pin_sd_detect, PIN_MODE_0, GPIO_DIR_MODE_IN, PIN_TYPE_STD_PU, PIN_STRENGTH_4MA); + pin_config (pybsd_obj.pin_sd_detect, PIN_MODE_0, GPIO_DIR_MODE_IN, PIN_TYPE_STD_PU, PIN_STRENGTH_4MA); } pybsd_obj.pinsset = true; } diff --git a/cc3200/mods/pybsleep.c b/cc3200/mods/pybsleep.c new file mode 100644 index 0000000000..8e94100a91 --- /dev/null +++ b/cc3200/mods/pybsleep.c @@ -0,0 +1,845 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2015 Daniel Campora + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include +#include + +#include "py/mpstate.h" +#include MICROPY_HAL_H +#include "hw_types.h" +#include "pybsleep.h" + +/* Storage memory for Cortex M4 registers. To make implementation independent of + CPU, register specific constructs must be made part of platform services. +*/ + +typedef struct { + uint32_t msp; + uint32_t psp; + uint32_t psr; + uint32_t primask; + uint32_t faultmask; + uint32_t basepri; + uint32_t control; +}arm_cm4_core_regs; + +//static arm_cm4_core_regs vault_arm_registers; + + +#define BACK_UP_ARM_REGISTERS() { \ + __asm(" push {r0-r12, LR} \n" \ + " ldr r1, pxVaultRegistersSave \n" \ + " mrs r0, msp \n" \ + " str r0, [r1] \n" \ + " mrs r0, psp \n" \ + " str r0, [r1, #4] \n" \ + " mrs r0, primask \n" \ + " str r0, [r1, #12] \n" \ + " mrs r0, faultmask \n" \ + " str r0, [r1, #16] \n" \ + " mrs r0, basepri \n" \ + " str r0, [r1, #20] \n" \ + " mrs r0, control \n" \ + " str r0, [r1, #24] \n" \ + "pxVaultRegistersSave: .word vault_arm_registers \n"); \ +} + +#define RESTORE_ARM_REGISTERS() { \ + __asm(" ldr r1, pxVaultRegistersLoad \n" \ + " ldr r0, [r1, #24] \n" \ + " msr control, r0 \n" \ + " ldr r0, [r1] \n" \ + " msr msp, r0 \n" \ + " ldr r0, [r1,#4] \n" \ + " msr psp, r0 \n" \ + " ldr r0, [r1, #12] \n" \ + " msr primask, r0 \n" \ + " ldr r0, [r1, #16] \n" \ + " msr faultmask, r0 \n" \ + " ldr r0, [r1, #20] \n" \ + " msr basepri, r0 \n" \ + " pop {r0-r12, LR} \n" \ + "pxVaultRegistersLoad: .word vault_arm_registers \n"); \ +} + +#if 0 +/* Called directly by boot ROM after waking from S3 state */ +void resume_from_S3(void) +{ + /* Jump from ROM context hence introduce the sync barriers */ + INTRODUCE_SYNC_BARRIER(); /* Data and instruction sync barriers */ + + RESTORE_ARM_REGISTERS(); /* Core registers and code is in assembly */ + + INTRODUCE_SYNC_BARRIER(); /* Data and instruction sync barriers */ + + pform->pm_ops->restore_soc_data(); + make_modules_to_M0_no_irq(pform->used_list_len); /* Wake up all */ + pform->pm_ops->handle_S3_wakeup(); /* Should be last statement */ + return; +} + +static void enter_into_S3(void) +{ + pform->pm_ops->back_up_soc_data(); + + INTRODUCE_SYNC_BARRIER(); /* Data and instruction sync barriers */ + + BACK_UP_ARM_REGISTERS(); /* Core registers and code is in assembly */ + + cc_enter_S3(resume_from_S3, vault_arm_registers.psp/*save_restore[1]*/); + + /* Introducing delays to facilitate CPU to fade away ........ */ + asm(" NOP"); asm(" NOP"); asm(" NOP"); asm(" NOP"); asm(" NOP"); + asm(" NOP"); asm(" NOP"); asm(" NOP"); asm(" NOP"); asm(" NOP"); + asm(" NOP"); asm(" NOP"); asm(" NOP"); asm(" NOP"); asm(" NOP"); +} + +static void apply_io_park(u8 pin_num, + enum io_park_state park_value) +{ + u32 pin_strength, pin_type; + + if(DONT_CARE != park_value) { + /* Change the pin mode to GPIO to be safe */ + //MAP_PinModeSet(pin_num, PIN_MODE_0); + + /* First apply PullUp/PullDn (or no pull) according + to the default levels specified in the user supplied + parking table */ + MAP_PinConfigGet(pin_num, &pin_strength, &pin_type); + + if(NO_PULL_HIZ != park_value) { + MAP_PinConfigSet(pin_num, pin_strength, park_value); + } else { + MAP_PinConfigSet(pin_num, pin_strength, PIN_TYPE_STD); + } + + /* One by one HiZ all the IOs, + by writing the register that drives IOEN_N control + pin of the IOs. This register and the signal path is + always-on and hence not get lost during True-LPDS */ + MAP_PinDirModeSet(pin_num, PIN_DIR_MODE_IN); + + /* Once all the digital IOs has been made HiZ, + the desired default PAD levels would be held by + the weak-pulls. Input buffers would be alive + (such as auto-SPI or wake-GPIOs) and would not + have Iddq issue since pulls are present. */ + } + return; +} + +i32 cc_io_park_safe(struct soc_io_park *io_park_choice, + u8 num_pins) +{ + i32 loopcnt; + + if(NULL == io_park_choice) { + return -1; + } + + /* Park the IOs safely as specified by the application */ + for(loopcnt = 0; loopcnt < num_pins; loopcnt++) { + switch(io_park_choice[loopcnt].pin_num) { + /* Shared SPI pins for SFLASH */ + case PIN_11: + case PIN_12: + case PIN_13: + case PIN_14: +#ifdef DEBUG_MODE + /* JTAG pins */ + case PIN_16: + case PIN_17: + case PIN_19: + case PIN_20: +#endif + /* Do not park these pins as they may + have external dependencies */ + break; + default: + /* Apply the specified IO parking scheme */ + apply_io_park(io_park_choice[loopcnt].pin_num, + io_park_choice[loopcnt].park_val); + + } + + } + + /* parking the SFLASH IOs */ + HWREG(0x4402E0E8) &= ~(0x3 << 8); + HWREG(0x4402E0E8) |= (0x2 << 8); + HWREG(0x4402E0EC) &= ~(0x3 << 8); + HWREG(0x4402E0EC) |= (0x2 << 8); + HWREG(0x4402E0F0) &= ~(0x3 << 8); + HWREG(0x4402E0F0) |= (0x2 << 8); + HWREG(0x4402E0F4) &= ~(0x3 << 8); + HWREG(0x4402E0F4) |= (0x1 << 8); + + return 0; +} + + + +#define INSTR_READ_STATUS 0x05 +#define INSTR_DEEP_POWER_DOWN 0xB9 +#define STATUS_BUSY 0x01 +//**************************************************************************** +// +//! Put SPI flash into Deep Power Down mode +//! +//! Note:SPI flash is a shared resource between MCU and Network processing +//! units. This routine should only be exercised after all the network +//! processing has been stopped. To stop network processing use sl_stop API +//! \param None +//! +//! \return Status, 0:Pass, -1:Fail +// +//**************************************************************************** +void SpiFlashDeepPowerDown(void) { + uint32_t status; + + // Enable clock for SSPI module + MAP_PRCMPeripheralClkEnable(PRCM_SSPI, PRCM_RUN_MODE_CLK); + // Reset SSPI at PRCM level and wait for reset to complete + MAP_PRCMPeripheralReset(PRCM_SSPI); + while(!MAP_PRCMPeripheralStatusGet(PRCM_SSPI); + + // Reset SSPI at module level + MAP_SPIReset(SSPI_BASE); + // Configure SSPI module + MAP_SPIConfigSetExpClk (SSPI_BASE, PRCMPeripheralClockGet(PRCM_SSPI), + 20000000, SPI_MODE_MASTER,SPI_SUB_MODE_0, + (SPI_SW_CTRL_CS | + SPI_4PIN_MODE | + SPI_TURBO_OFF | + SPI_CS_ACTIVELOW | + SPI_WL_8)); + + // Enable SSPI module + MAP_SPIEnable(SSPI_BASE); + // Enable chip select for the spi flash. + MAP_SPICSEnable(SSPI_BASE); + // Wait for the spi flash + do { + // Send the status register read instruction and read back a dummy byte. + MAP_SPIDataPut(SSPI_BASE, INSTR_READ_STATUS); + MAP_SPIDataGet(SSPI_BASE, &status); + + // Write a dummy byte then read back the actual status. + MAP_SPIDataPut(SSPI_BASE, 0xFF); + MAP_SPIDataGet(SSPI_BASE, &status); + } while ((status & 0xFF) == STATUS_BUSY); + + // Disable chip select for the spi flash. + MAP_SPICSDisable(SSPI_BASE); + // Start another CS enable sequence for Power down command. + MAP_SPICSEnable(SSPI_BASE); + // Send Deep Power Down command to spi flash + MAP_SPIDataPut(SSPI_BASE, INSTR_DEEP_POWER_DOWN); + // Disable chip select for the spi flash. + MAP_SPICSDisable(SSPI_BASE); +} + + +#define DBG_PRINT Report +#define NUM_NVIC_PEND_REG 6 +#define ERR_TIMER_TO_WAKE (-2) +#define MAX_GPIO_WAKESOURCE 6 + +struct { + u32 vector_table; // Vector Table Offset + u32 aux_ctrl; // Auxiliary control register + u32 int_ctrl_state; // Interrupt Control and State + u32 app_int; // Application Interrupt Reset control + u32 sys_ctrl; // System control + u32 config_ctrl; // Configuration control + u32 sys_pri_1; // System Handler Priority 1 + u32 sys_pri_2; // System Handler Priority 2 + u32 sys_pri_3; // System Handler Priority 3 + u32 sys_hcrs; // System Handler control and state register + u32 systick_ctrl; // SysTick Control Status + u32 systick_reload; // SysTick Reload + u32 systick_calib; // SysTick Calibration + u32 int_en[6]; // Interrupt set enable + u32 int_priority[49]; // Interrupt priority +} nvic_reg_store; + +u8 gpio_wake_src[] = {2, 4, 13, 17, 11, 24}; +u8 gpio_lpds_inttype[] = {1, 1, 2, 0xFF, 3, 0xFF, 0}; +u8 gpio_hib_inttype[] = {2, 2, 0, 0xFF, 3, 0xFF, 1}; + +u32 nvic_int_mask[] = {NVIC_PEND0_MASK, NVIC_PEND1_MASK, NVIC_PEND2_MASK, + NVIC_PEND3_MASK, NVIC_PEND4_MASK, NVIC_PEND5_MASK}; + +volatile i32 debug = 0; + + +/* Network (Host IRQ) based wakeup from S3(LPDS) */ +static i32 setup_S3_wakeup_from_nw() +{ +#define IS_NWPIC_INTR_SET() (HWREG(NVIC_EN5) & (1 << ((INT_NWPIC - 16) & 31))) + + /* Check if the NWP->APPs interrupt is enabled */ + if(IS_NWPIC_INTR_SET()) { + /* Set LPDS Wakeup source as NWP request */ + MAP_PRCMLPDSWakeupSourceEnable(PRCM_LPDS_HOST_IRQ); + return 0; + } else { + return -1; + } +} + + + +/* GPIO based wakeup from S3(LPDS) */ +static i32 check_n_setup_S3_wakeup_from_gpio() +{ + i32 retval, indx; + u8 gpio_num[MAX_GPIO_WAKESOURCE]; + u8 int_type[MAX_GPIO_WAKESOURCE]; + + /* Check for any special purpose GPIO usage */ + retval = cc_gpio_get_spl_purpose(&gpio_num[0], + &int_type[0], + MAX_GPIO_WAKESOURCE); + + if(retval > 0) { + for(indx = 0; indx < sizeof(gpio_wake_src); indx++) { + if(gpio_wake_src[indx] == gpio_num[0]) { + /* Setup the GPIO to be the wake source */ + MAP_PRCMLPDSWakeUpGPIOSelect( + indx, gpio_lpds_inttype[int_type[0]]); + MAP_PRCMLPDSWakeupSourceEnable(PRCM_LPDS_GPIO); + /* Save the GPIO number wake from LPDS */ + cc_pm_ctrl.spl_gpio_wakefrom_lpds = gpio_num[0]; + break; + } + } + } else { + return -1; + } + + return 0; +} + +/* Timer based wakeup from S3 (LPDS) */ +static i32 check_n_setup_S3_wakeup_from_timer() +{ + u64 scc_match, scc_curr, scc_remaining; + + /* Check if there is an alarm set */ + if(cc_rtc_has_alarm()) { + /* Get the time remaining for the RTC timer to expire */ + scc_match = MAP_PRCMSlowClkCtrMatchGet(); + scc_curr = MAP_PRCMSlowClkCtrGet(); + + if(scc_match > scc_curr) { + /* Get the time remaining in terms of slow clocks */ + scc_remaining = (scc_match - scc_curr); + if(scc_remaining > WAKEUP_TIME_LPDS) { + /* Subtract the time it takes for wakeup + from S3 (LPDS) */ + scc_remaining -= WAKEUP_TIME_LPDS; + scc_remaining = (scc_remaining > 0xFFFFFFFF)? + 0xFFFFFFFF: scc_remaining; + /* Setup the LPDS wake time */ + MAP_PRCMLPDSIntervalSet( + (u32)scc_remaining); + /* Enable the wake source to be timer */ + MAP_PRCMLPDSWakeupSourceEnable( + PRCM_LPDS_TIMER); + } else { + /* Cannot enter LPDS */ + return ERR_TIMER_TO_WAKE; + } + } else { + return ERR_TIMER_TO_WAKE; + } + } else { + /* Disable timer as the wake source */ + MAP_PRCMLPDSWakeupSourceDisable(PRCM_LPDS_TIMER); + return -1; + } + + return 0; +} + +/* Setup the HIBernate wakr source as apecified GPIO */ +static void setup_hib_gpio_wake(u32 gpio_num, + u32 gpio_wake_type) +{ + MAP_PRCMHibernateWakeUpGPIOSelect(gpio_num, gpio_wake_type); + MAP_PRCMHibernateWakeupSourceEnable(gpio_num); + + return; +} + +/* GPIO based wakeup from S4 (HIB) */ +static i32 check_n_setup_S4_wakeup_from_gpio() +{ + i32 retval, indx; + u8 gpio_num[MAX_GPIO_WAKESOURCE]; + u8 int_type[MAX_GPIO_WAKESOURCE]; + + /* Check for any special purpose GPIO usage */ + retval = cc_gpio_get_spl_purpose(&gpio_num[0], + &int_type[0], + MAX_GPIO_WAKESOURCE); + + if(retval > 0) { + for(indx = 0; indx < retval; indx++) { + switch(gpio_num[indx]) { + case 2: + setup_hib_gpio_wake(PRCM_HIB_GPIO2, + gpio_hib_inttype[int_type[indx]]); + break; + case 4: + setup_hib_gpio_wake(PRCM_HIB_GPIO4, + gpio_hib_inttype[int_type[indx]]); + break; + case 13: + setup_hib_gpio_wake(PRCM_HIB_GPIO13, + gpio_hib_inttype[int_type[indx]]); + break; + case 17: + setup_hib_gpio_wake(PRCM_HIB_GPIO17, + gpio_hib_inttype[int_type[indx]]); + + break; + case 11: + setup_hib_gpio_wake(PRCM_HIB_GPIO11, + gpio_hib_inttype[int_type[indx]]); + break; + case 24: + setup_hib_gpio_wake(PRCM_HIB_GPIO24, + gpio_hib_inttype[int_type[indx]]); + break; + default: + break; + } + } + } else { + return -1; + } + + return 0; +} + +/* Timer based wakeup from S4 (HIB) */ +static i32 check_n_setup_S4_wakeup_from_timer() +{ + u64 scc_match, scc_curr, scc_remaining; + + /* Check if there is an alarm set */ + if(cc_rtc_has_alarm()) { + /* Get the time remaining for the RTC timer to expire */ + scc_match = MAP_PRCMSlowClkCtrMatchGet(); + scc_curr = MAP_PRCMSlowClkCtrGet(); + + if(scc_match > scc_curr) { + /* Get the time remaining in terms of slow clocks */ + scc_remaining = (scc_match - scc_curr); + if(scc_remaining > WAKEUP_TIME_HIB) { + /* Subtract the time it takes for wakeup + from S4 (HIB) */ + scc_remaining -= WAKEUP_TIME_HIB; + /* Setup the HIB wake time */ + MAP_PRCMHibernateIntervalSet(scc_remaining); + /* Enable the wake source to be RTC */ + MAP_PRCMHibernateWakeupSourceEnable( + PRCM_HIB_SLOW_CLK_CTR); + } else { + /* Cannot enter HIB */ + return ERR_TIMER_TO_WAKE; + } + } else { + return -1; + } + } else { + /* Disable Timer as wake source */ + MAP_PRCMHibernateWakeupSourceDisable(PRCM_HIB_SLOW_CLK_CTR); + return -1; + } + + return 0; +} + +/* Sets up wake-up sources for indicated power mode */ +i32 cc_set_up_wkup_srcs(enum soc_pm target) +{ + i32 nw_ret = -1, gpio_ret = -1, timer_ret = -1; + switch(target) { + case e_pm_S0: + case e_pm_S1: + case e_pm_S2: + /* These handle the cases of run, sleep, deepsleep. + Wake source is configured outside this scope in + individual peripherals */ + break; + case e_pm_S3: + /* Low power deep sleep condition */ + /* Network (Host IRQ) based wakeup is always enabled */ + nw_ret = setup_S3_wakeup_from_nw(); + /* Check and enable GPIO based wakeup */ + gpio_ret = check_n_setup_S3_wakeup_from_gpio(); + /* Check and enable LRT based wakeup */ + timer_ret = check_n_setup_S3_wakeup_from_timer(); + break; + case e_pm_S4: + /* Hibernate condition */ + /* Check and enable GPIO based wakeup */ + gpio_ret = check_n_setup_S4_wakeup_from_gpio(); + /* Check and enable LRT based wakeup */ + timer_ret = check_n_setup_S4_wakeup_from_timer(); + break; + default: + break; + } + + if(ERR_TIMER_TO_WAKE == timer_ret) { + return -1; + } + if((nw_ret < 0) && (gpio_ret < 0) && (timer_ret < 0)) { + return -1; + } + else if((gpio_ret < 0) && (timer_ret < 0)) { + /* Setup the LPDS wake time */ + MAP_PRCMLPDSIntervalSet(LPDS_WDOG_TIME); + /* Enable the wake source to be timer */ + MAP_PRCMLPDSWakeupSourceEnable( + PRCM_LPDS_TIMER); + } + return 0; +} + +/* LPDS wake SW interrupt handler */ +void wake_interrupt_handler() +{ + i32 wake_source; + + /* Identify the wakeup source */ + wake_source = MAP_PRCMLPDSWakeupCauseGet(); + + switch(wake_source) { + case PRCM_LPDS_HOST_IRQ: + break; + case PRCM_LPDS_GPIO: + /* Invoke the callback with the last GPIO num + used to enter LPDS (S3) */ + gpio_wake_interrupt_handler( + &cc_pm_ctrl.spl_gpio_wakefrom_lpds); + break; + case PRCM_LPDS_TIMER: + break; + } + + return; +} + +/* Process events that have woken up system from S3 (LPDS) */ +i32 cc_handle_S3_wakeup() +{ + /* Trigger the SW interrupt */ + MAP_IntPendSet(INT_PRCM); + return 0; +} + +/* Are there interrupts pending in system? TRUE -> yes else no */ +bool cc_are_irqs_pending(void) +{ + i32 indx = 0; + u32 *base_reg_addr; + + /* Check if there are any interrupts pending */ + base_reg_addr = (u32 *)NVIC_PEND0; + for(indx = 0; indx < NUM_NVIC_PEND_REG; indx++) { + if(base_reg_addr[indx] & nvic_int_mask[indx]) { + return true; + } + } + + return false; +} + +/* Must push system to low power state of S4 (Hibernate) */ +i32 cc_enter_S4(void) +{ + /* Invoke the driverlib API to enter HIBernate */ + MAP_PRCMHibernateEnter(); + + return 0; +} + +/* Must push system to low power state of S3 (LPDS) */ +i32 cc_enter_S3(void(*resume_fn)(void), u32 stack_ptr) +{ + MAP_PRCMLPDSRestoreInfoSet(stack_ptr, (u32)resume_fn); + + /* Enter LPDS */ + MAP_PRCMLPDSEnter(); + return 0; +} + +/* Must push system to low power state of S2 (Deepsleep) */ +i32 cc_enter_S2(void) +{ + /* Enter deepsleep */ + //MAP_PRCMDeepSleepEnter(); + + return 0; +} +volatile i32 sleep_count = 0; +/* Must push system to low power state of S1 */ +i32 cc_enter_S1(void) +{ + //MAP_PRCMSleepEnter(); + return 0; +} + +/* Save the NVIC registers */ +void back_up_nvic_regs() +{ + i32 indx = 0; + u32 *base_reg_addr; + /* Save the NVIC control registers */ + nvic_reg_store.vector_table = HWREG(NVIC_VTABLE); + nvic_reg_store.aux_ctrl = HWREG(NVIC_ACTLR); + nvic_reg_store.int_ctrl_state = HWREG(NVIC_INT_CTRL); + nvic_reg_store.app_int = HWREG(NVIC_APINT); + nvic_reg_store.sys_ctrl = HWREG(NVIC_SYS_CTRL); + nvic_reg_store.config_ctrl = HWREG(NVIC_CFG_CTRL); + nvic_reg_store.sys_pri_1 = HWREG(NVIC_SYS_PRI1); + nvic_reg_store.sys_pri_2 = HWREG(NVIC_SYS_PRI2); + nvic_reg_store.sys_pri_3 = HWREG(NVIC_SYS_PRI3); + nvic_reg_store.sys_hcrs = HWREG(NVIC_SYS_HND_CTRL); + + /* Systick registers */ + nvic_reg_store.systick_ctrl = HWREG(NVIC_ST_CTRL); + nvic_reg_store.systick_reload = HWREG(NVIC_ST_RELOAD); + nvic_reg_store.systick_calib = HWREG(NVIC_ST_CAL); + + /* Save the interrupt enable registers */ + base_reg_addr = (u32 *)NVIC_EN0; + for(indx = 0; indx < (sizeof(nvic_reg_store.int_en) / 4); indx++) { + nvic_reg_store.int_en[indx] = base_reg_addr[indx]; + } + + /* Save the interrupt priority registers */ + base_reg_addr = (u32 *)NVIC_PRI0; + for(indx = 0; indx < (sizeof(nvic_reg_store.int_priority) / 4); indx++) { + nvic_reg_store.int_priority[indx] = base_reg_addr[indx]; + } + + return; +} + +/* Reestore the NVIC registers */ +void restore_nvic_regs() +{ + i32 indx = 0; + u32 *base_reg_addr; + + /* Restore the NVIC control registers */ + HWREG(NVIC_VTABLE) = nvic_reg_store.vector_table; + HWREG(NVIC_ACTLR) = nvic_reg_store.aux_ctrl; + HWREG(NVIC_APINT) = nvic_reg_store.app_int; + HWREG(NVIC_SYS_CTRL) = nvic_reg_store.sys_ctrl; + HWREG(NVIC_CFG_CTRL) = nvic_reg_store.config_ctrl; + HWREG(NVIC_SYS_PRI1) = nvic_reg_store.sys_pri_1; + HWREG(NVIC_SYS_PRI2) = nvic_reg_store.sys_pri_2; + HWREG(NVIC_SYS_PRI3) = nvic_reg_store.sys_pri_3; + HWREG(NVIC_SYS_HND_CTRL) = nvic_reg_store.sys_hcrs; + + /* Systick registers */ + HWREG(NVIC_ST_CTRL) = nvic_reg_store.systick_ctrl; + HWREG(NVIC_ST_RELOAD) = nvic_reg_store.systick_reload; + HWREG(NVIC_ST_CAL) = nvic_reg_store.systick_calib; + + /* Restore the interrupt priority registers */ + base_reg_addr = (u32 *)NVIC_PRI0; + for(indx = 0; indx < (sizeof(nvic_reg_store.int_priority) / 4); indx++) { + base_reg_addr[indx] = nvic_reg_store.int_priority[indx]; + } + + /* Restore the interrupt enable registers */ + base_reg_addr = (u32 *)NVIC_EN0; + for(indx = 0; indx < (sizeof(nvic_reg_store.int_en) / 4); indx++) { + base_reg_addr[indx] = nvic_reg_store.int_en[indx]; + } + + INTRODUCE_SYNC_BARRIER(); /* Data and instruction sync barriers */ + + return; +} + +/* S3 (LPDS): Back-up system regs & data */ +void cc_back_up_soc_data(void) { + /* Enable the RAM retention */ + MAP_PRCMSRAMRetentionEnable(PRCM_SRAM_COL_1 | PRCM_SRAM_COL_2 | PRCM_SRAM_COL_3 | PRCM_SRAM_COL_4, PRCM_SRAM_LPDS_RET); + /* Store the NVIC registers */ + back_up_nvic_regs(); + + // Park all IO pins + + // Park antenna selection pins + HWREG(0x4402E108) = 0x00000E61; + HWREG(0x4402E10C) = 0x00000E61; + + INTRODUCE_SYNC_BARRIER(); /* Data and instruction sync barriers */ + + BACK_UP_ARM_REGISTERS(); /* Core registers and code is in assembly */ + + return; +} + +/* S3 (LPDS): Restore system regs & data */ +void cc_restore_soc_data(void) +{ + uint32_t reg; + /* Check if any of the registers/data need to be restored */ + /* Take I2C semaphore */ + reg = HWREG(COMMON_REG_BASE + COMMON_REG_O_I2C_Properties_Register); + reg = (reg & ~0x3) | 0x1; + HWREG(COMMON_REG_BASE + COMMON_REG_O_I2C_Properties_Register) = reg; + + /* Take GPIO semaphore */ + reg = HWREG(COMMON_REG_BASE + COMMON_REG_O_GPIO_properties_register); + reg = (reg & ~0x3FF) | 0x155; + HWREG(COMMON_REG_BASE + COMMON_REG_O_GPIO_properties_register) = reg; + + /* Restore the NVIC registers */ + restore_nvic_regs(); + + /* ungates the clk for the shared SPI*/ + MAP_PRCMPeripheralClkEnable(PRCM_SSPI, PRCM_RUN_MODE_CLK | PRCM_SLP_MODE_CLK); + MAP_PRCMIntEnable (PRCM_INT_SLOW_CLK_CTR); + + return; +} + + +void prcm_interrupt_handler(void *intr_param) +{ + int status; + + /* Read the interrupt status, also clears the status */ + status = MAP_PRCMIntStatus(); + + if((PRCM_INT_SLOW_CLK_CTR == status) || (sw_simulate_rtc)) { + sw_simulate_rtc = 0; + /* Invoke the RTC interrupt handler */ + cc_rtc_isr(); + } else if(0 == status) { + /* Invoke the wake from LPDS interrupt handler */ + wake_interrupt_handler(); + } else { + } +} + +/* LPDS wake SW interrupt handler */ +void wake_interrupt_handler() +{ + i32 wake_source; + + /* Identify the wakeup source */ + wake_source = MAP_PRCMLPDSWakeupCauseGet(); + + switch(wake_source) { + case PRCM_LPDS_HOST_IRQ: + break; + case PRCM_LPDS_GPIO: + /* Invoke the callback with the last GPIO num + used to enter LPDS (S3) */ + gpio_wake_interrupt_handler( + &cc_pm_ctrl.spl_gpio_wakefrom_lpds); + break; + case PRCM_LPDS_TIMER: + break; + } + + return; +} + +/* Invoked in interrupt context */ +void cc_rtc_isr(void) { + struct u64_time alarm, value; + u32 status; + + /* Read the interrupt status, also clears the status */ + status = MAP_PRCMIntStatus(); + + // call the python RTC callback interrupt handler +} +#endif + + +typedef struct { + mp_obj_t obj; + WakeUpCB_t wakeup; +}pybsleep_obj_t; + + +STATIC pybsleep_obj_t * pybsleep_find (mp_obj_t obj) { + for (mp_uint_t i = 0; i < MP_STATE_PORT(pybsleep_obj_list).len; i++) { + // search for the object and then remove it + pybsleep_obj_t *sleep_obj = ((pybsleep_obj_t *)MP_STATE_PORT(pybsleep_obj_list).items[i]); + if (sleep_obj->obj == obj) { + return sleep_obj; + } + } + return NULL; +} + +void pyblsleep_init0 (void) { + mp_obj_list_init(&MP_STATE_PORT(pybsleep_obj_list), 0); +} + +void pybsleep_add (mp_obj_t obj, WakeUpCB_t wakeup) { + pybsleep_obj_t * sleep_obj = m_new_obj(pybsleep_obj_t); + sleep_obj->obj = obj; + sleep_obj->wakeup = wakeup; + // only add objects once + if (!pybsleep_find(sleep_obj)) { + mp_obj_list_append(&MP_STATE_PORT(pybsleep_obj_list), sleep_obj); + } +} + +void pybsleep_remove (mp_obj_t obj) { + pybsleep_obj_t *sleep_obj; + if ((sleep_obj = pybsleep_find(obj))) { + mp_obj_list_remove(&MP_STATE_PORT(pybsleep_obj_list), sleep_obj); + } +} + +void pybsleep_wakeup (void) { + for (mp_uint_t i = 0; i < MP_STATE_PORT(pybsleep_obj_list).len; i++) { + pybsleep_obj_t *sleep_obj = ((pybsleep_obj_t *)MP_STATE_PORT(pybsleep_obj_list).items[i]); + sleep_obj->wakeup(sleep_obj->obj); + } +} + diff --git a/cc3200/mods/pybsleep.h b/cc3200/mods/pybsleep.h new file mode 100644 index 0000000000..d91437ffa2 --- /dev/null +++ b/cc3200/mods/pybsleep.h @@ -0,0 +1,37 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2015 Daniel Campora + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef PYBSLEEP_H_ +#define PYBSLEEP_H_ + +typedef void (*WakeUpCB_t)(mp_obj_t self); + +void pyblsleep_init0 (void); +void pybsleep_add (mp_obj_t obj, WakeUpCB_t wakeup); +void pybsleep_remove (mp_obj_t obj); +void pybsleep_wakeup (void); + +#endif /* PYBSLEEP_H_ */ diff --git a/cc3200/mods/pybuart.c b/cc3200/mods/pybuart.c index b5593b50cb..8d054bb088 100644 --- a/cc3200/mods/pybuart.c +++ b/cc3200/mods/pybuart.c @@ -46,6 +46,7 @@ #include "uart.h" #include "pybuart.h" #include "pybioctl.h" +#include "pybsleep.h" #include "mpexception.h" #include "py/mpstate.h" #include "osi.h" @@ -181,6 +182,8 @@ bool uart_init2(pyb_uart_obj_t *self) { self->enabled = true; + // register it with the sleep module + pybsleep_add (self, (WakeUpCB_t)uart_init2); return true; } @@ -512,6 +515,8 @@ STATIC mp_obj_t pyb_uart_deinit(mp_obj_t self_in) { return mp_const_none; } + // unregister it with the sleep module + pybsleep_remove (self); self->enabled = false; MAP_UARTIntDisable(self->reg, UART_INT_RX | UART_INT_RT); MAP_UARTIntClear(self->reg, UART_INT_RX | UART_INT_RT); diff --git a/cc3200/mpconfigport.h b/cc3200/mpconfigport.h index 8f7c6e38e1..a32e9b9dc6 100644 --- a/cc3200/mpconfigport.h +++ b/cc3200/mpconfigport.h @@ -123,6 +123,7 @@ extern const struct _mp_obj_module_t mp_module_network; mp_obj_list_t pyb_extint_list; \ mp_obj_list_t pyb_uart_list; \ mp_obj_list_t mod_network_nic_list; \ + mp_obj_list_t pybsleep_obj_list; \ // type definitions for the specific machine diff --git a/cc3200/mptask.c b/cc3200/mptask.c index f760b641d8..f90a64e8c0 100644 --- a/cc3200/mptask.c +++ b/cc3200/mptask.c @@ -60,6 +60,7 @@ #include "pybi2c.h" #include "pybsd.h" #include "pins.h" +#include "pybsleep.h" /****************************************************************************** DECLARE PRIVATE CONSTANTS @@ -141,13 +142,14 @@ soft_reset: mperror_init0(); mpexception_init0(); + pyblsleep_init0(); uart_init0(); pin_init0(); // configure stdio uart pins with the correct af // param 3 ("mode") is DON'T CARE" for AFs others than GPIO - pin_config(&pin_GPIO1, PIN_MODE_3, 0, PIN_TYPE_STD, PIN_STRENGTH_2MA); - pin_config(&pin_GPIO2, PIN_MODE_3, 0, PIN_TYPE_STD, PIN_STRENGTH_2MA); + pin_config ((pin_obj_t *)&pin_GPIO1, PIN_MODE_3, 0, PIN_TYPE_STD, PIN_STRENGTH_2MA); + pin_config ((pin_obj_t *)&pin_GPIO2, PIN_MODE_3, 0, PIN_TYPE_STD, PIN_STRENGTH_2MA); // Instantiate the stdio uart mp_obj_t args[2] = { mp_obj_new_int(MICROPY_STDIO_UART), @@ -157,7 +159,7 @@ soft_reset: readline_init0(); extint_init0(); - mod_network_init(); + mod_network_init0(); wlan_init0(); #if MICROPY_HW_ENABLE_RNG rng_init0();