diff --git a/cpu/sam0_common/include/periph_cpu_common.h b/cpu/sam0_common/include/periph_cpu_common.h index 19753d91fe..38cc58ac69 100644 --- a/cpu/sam0_common/include/periph_cpu_common.h +++ b/cpu/sam0_common/include/periph_cpu_common.h @@ -352,6 +352,38 @@ typedef struct { */ void gpio_init_mux(gpio_t pin, gpio_mux_t mux); +/** + * @brief Called before the power management enters a power mode + * + * @param[in] deep + */ +void gpio_pm_cb_enter(int deep); + +/** + * @brief Called after the power management left a power mode + * + * @param[in] deep + */ +void gpio_pm_cb_leave(int deep); + +/** + * @brief Wrapper for cortexm_sleep calling power management callbacks + * + * @param[in] deep + */ +static inline void sam0_cortexm_sleep(int deep) +{ +#ifdef MODULE_PERIPH_GPIO + gpio_pm_cb_enter(deep); +#endif + + cortexm_sleep(deep); + +#ifdef MODULE_PERIPH_GPIO + gpio_pm_cb_leave(deep); +#endif +} + /** * @brief Returns the frequency of a GCLK provider. * diff --git a/cpu/sam0_common/periph/gpio.c b/cpu/sam0_common/periph/gpio.c index 83892f9507..e721d75a72 100644 --- a/cpu/sam0_common/periph/gpio.c +++ b/cpu/sam0_common/periph/gpio.c @@ -56,6 +56,14 @@ #define _EIC EIC #endif +/** + * @brief Clock source for the External Interrupt Controller + */ +typedef enum { + _EIC_CLOCK_FAST, + _EIC_CLOCK_SLOW +} gpio_eic_clock_t; + static gpio_isr_ctx_t gpio_config[NUMOF_IRQS]; #endif /* MODULE_PERIPH_GPIO_IRQ */ @@ -155,6 +163,13 @@ void gpio_write(gpio_t pin, int value) } #ifdef MODULE_PERIPH_GPIO_IRQ + +#ifdef CPU_FAM_SAMD21 +#define EIC_SYNC() while (_EIC->STATUS.bit.SYNCBUSY) +#else +#define EIC_SYNC() while (_EIC->SYNCBUSY.bit.ENABLE) +#endif + static int _exti(gpio_t pin) { unsigned port_num = ((pin >> 7) & 0x03); @@ -196,7 +211,7 @@ int gpio_init_int(gpio_t pin, gpio_mode_t mode, gpio_flank_t flank, GCLK->PCHCTRL[EIC_GCLK_ID].reg = GCLK_PCHCTRL_CHEN | GCLK_PCHCTRL_GEN(SAM0_GCLK_MAIN); /* disable the EIC module*/ _EIC->CTRLA.reg = 0; - while (_EIC->SYNCBUSY.reg & EIC_SYNCBUSY_ENABLE) {} + EIC_SYNC(); #endif /* configure the active flank */ _EIC->CONFIG[exti >> 3].reg &= ~(0xf << ((exti & 0x7) * 4)); @@ -217,15 +232,76 @@ int gpio_init_int(gpio_t pin, gpio_mode_t mode, gpio_flank_t flank, _EIC->WAKEUP.reg |= (1 << exti); /* enable the EIC module*/ _EIC->CTRL.reg = EIC_CTRL_ENABLE; - while (_EIC->STATUS.reg & EIC_STATUS_SYNCBUSY) {} + EIC_SYNC(); #else /* CPU_FAM_SAML21 */ /* enable the EIC module*/ _EIC->CTRLA.reg = EIC_CTRLA_ENABLE; - while (_EIC->SYNCBUSY.reg & EIC_SYNCBUSY_ENABLE) {} + EIC_SYNC(); #endif return 0; } +inline static void reenable_eic(gpio_eic_clock_t clock) { +#if defined(CPU_SAMD21) + if (clock == _EIC_CLOCK_SLOW) { + GCLK->CLKCTRL.reg = EIC_GCLK_ID + | GCLK_CLKCTRL_CLKEN + | GCLK_CLKCTRL_GEN(SAM0_GCLK_32KHZ); + } else { + GCLK->CLKCTRL.reg = EIC_GCLK_ID + | GCLK_CLKCTRL_CLKEN + | GCLK_CLKCTRL_GEN(SAM0_GCLK_MAIN); + } + while (GCLK->STATUS.bit.SYNCBUSY) {} +#else + uint32_t ctrla_reg = EIC_CTRLA_ENABLE; + + EIC->CTRLA.reg = 0; + EIC_SYNC(); + + if (clock == _EIC_CLOCK_SLOW) { + ctrla_reg |= EIC_CTRLA_CKSEL; + } + + EIC->CTRLA.reg = ctrla_reg; + EIC_SYNC(); +#endif +} + +void gpio_pm_cb_enter(int deep) +{ +#if defined(PM_SLEEPCFG_SLEEPMODE_STANDBY) + (void) deep; + + if (PM->SLEEPCFG.bit.SLEEPMODE == PM_SLEEPCFG_SLEEPMODE_STANDBY) { + DEBUG_PUTS("gpio: switching EIC to slow clock"); + reenable_eic(_EIC_CLOCK_SLOW); + } +#else + if (deep) { + DEBUG_PUTS("gpio: switching EIC to slow clock"); + reenable_eic(_EIC_CLOCK_SLOW); + } +#endif +} + +void gpio_pm_cb_leave(int deep) +{ +#if defined(PM_SLEEPCFG_SLEEPMODE_STANDBY) + (void) deep; + + if (PM->SLEEPCFG.bit.SLEEPMODE == PM_SLEEPCFG_SLEEPMODE_STANDBY) { + DEBUG_PUTS("gpio: switching EIC to fast clock"); + reenable_eic(_EIC_CLOCK_FAST); + } +#else + if (deep) { + DEBUG_PUTS("gpio: switching EIC to fast clock"); + reenable_eic(_EIC_CLOCK_FAST); + } +#endif +} + void gpio_irq_enable(gpio_t pin) { int exti = _exti(pin); @@ -285,4 +361,16 @@ ISR_EICn(_other) #endif /* CPU_SAML1X */ #endif /* CPU_SAML1X || CPU_SAMD5X */ +#else /* MODULE_PERIPH_GPIO_IRQ */ + +void gpio_pm_cb_enter(int deep) +{ + (void) deep; +} + +void gpio_pm_cb_leave(int deep) +{ + (void) deep; +} + #endif /* MODULE_PERIPH_GPIO_IRQ */ diff --git a/cpu/samd21/periph/pm.c b/cpu/samd21/periph/pm.c index 17e8f7a706..687d8199a1 100644 --- a/cpu/samd21/periph/pm.c +++ b/cpu/samd21/periph/pm.c @@ -82,5 +82,5 @@ void pm_set(unsigned mode) break; } - cortexm_sleep(deep); + sam0_cortexm_sleep(deep); } diff --git a/cpu/samd5x/periph/pm.c b/cpu/samd5x/periph/pm.c index 8229881749..cbbc65c768 100644 --- a/cpu/samd5x/periph/pm.c +++ b/cpu/samd5x/periph/pm.c @@ -57,5 +57,5 @@ void pm_set(unsigned mode) /* make sure value has been set */ while (PM->SLEEPCFG.bit.SLEEPMODE != _mode) {} - cortexm_sleep(deep); + sam0_cortexm_sleep(deep); } diff --git a/cpu/saml1x/periph/pm.c b/cpu/saml1x/periph/pm.c index 4624533577..5068425492 100644 --- a/cpu/saml1x/periph/pm.c +++ b/cpu/saml1x/periph/pm.c @@ -47,5 +47,5 @@ void pm_set(unsigned mode) while (PM->SLEEPCFG.bit.SLEEPMODE != _mode) {} } - cortexm_sleep(0); + sam0_cortexm_sleep(0); } diff --git a/cpu/saml21/cpu.c b/cpu/saml21/cpu.c index 65783ae973..03e3b1dd1f 100644 --- a/cpu/saml21/cpu.c +++ b/cpu/saml21/cpu.c @@ -119,7 +119,7 @@ void cpu_init(void) /* set OSC16M to 16MHz */ OSCCTRL->OSC16MCTRL.bit.FSEL = 3; - OSCCTRL->OSC16MCTRL.bit.ONDEMAND = 0; + OSCCTRL->OSC16MCTRL.bit.ONDEMAND = 1; OSCCTRL->OSC16MCTRL.bit.RUNSTDBY = 0; _osc32k_setup(); diff --git a/cpu/saml21/periph/pm.c b/cpu/saml21/periph/pm.c index af839e5bd9..01e16c6707 100644 --- a/cpu/saml21/periph/pm.c +++ b/cpu/saml21/periph/pm.c @@ -55,5 +55,5 @@ void pm_set(unsigned mode) while (PM->SLEEPCFG.bit.SLEEPMODE != _mode) {} } - cortexm_sleep(0); + sam0_cortexm_sleep(0); }