diff --git a/arch/arm/src/stm32f7/stm32_adc.c b/arch/arm/src/stm32f7/stm32_adc.c index 710209a2bf1..6343472f4f0 100644 --- a/arch/arm/src/stm32f7/stm32_adc.c +++ b/arch/arm/src/stm32f7/stm32_adc.c @@ -62,6 +62,7 @@ #include #include #include +#include #include #include @@ -226,6 +227,11 @@ struct stm32_dev_s uint32_t pclck; /* The PCLK frequency that drives this timer */ uint32_t freq; /* The desired frequency of conversions */ #endif + +#ifdef CONFIG_PM + struct pm_callback_s pm_callback; +#endif + #ifdef ADC_HAVE_DMA DMA_HANDLE dma; /* Allocated DMA channel */ @@ -297,6 +303,11 @@ static void adc_dmaconvcallback(DMA_HANDLE handle, uint8_t isr, static void adc_startconv(FAR struct stm32_dev_s *priv, bool enable); +#ifdef CONFIG_PM +static int adc_pm_prepare(struct pm_callback_s *cb, int domain, + enum pm_state_e state); +#endif + /**************************************************************************** * Private Data ****************************************************************************/ @@ -333,6 +344,12 @@ static struct stm32_dev_s g_adcpriv1 = .dmachan = ADC1_DMA_CHAN, .hasdma = true, #endif +#ifdef CONFIG_PM + .pm_callback = + { + .prepare = adc_pm_prepare, + } +#endif }; static struct adc_dev_s g_adcdev1 = @@ -362,6 +379,12 @@ static struct stm32_dev_s g_adcpriv2 = .dmachan = ADC2_DMA_CHAN, .hasdma = true, #endif +#ifdef CONFIG_PM + .pm_callback = + { + .prepare = adc_pm_prepare, + } +#endif }; static struct adc_dev_s g_adcdev2 = @@ -391,6 +414,12 @@ static struct stm32_dev_s g_adcpriv3 = .dmachan = ADC3_DMA_CHAN, .hasdma = true, #endif +#ifdef CONFIG_PM + .pm_callback = + { + .prepare = adc_pm_prepare, + } +#endif }; static struct adc_dev_s g_adcdev3 = @@ -982,6 +1011,33 @@ static int adc_timinit(FAR struct stm32_dev_s *priv) } #endif +/**************************************************************************** + * Name: adc_pm_prepare + * + * Description: + * Called by power management framework when it wants to enter low power + * states. Check if ADC is in progress and if so prevent from entering STOP. + * + ****************************************************************************/ +#ifdef CONFIG_PM +static int adc_pm_prepare(struct pm_callback_s *cb, int domain, + enum pm_state_e state) +{ + struct stm32_dev_s *priv = + (struct stm32_dev_s *)((char *)cb - + offsetof(struct stm32_dev_s, pm_callback)); + uint32_t regval; + + regval = adc_getreg(priv, STM32_ADC_CR2_OFFSET); + if ((state >= PM_IDLE) && (regval & ADC_CR2_SWSTART)) + { + return -EBUSY; + } + + return OK; +} +#endif + /**************************************************************************** * Name: adc_startconv * diff --git a/arch/arm/src/stm32f7/stm32_i2c.c b/arch/arm/src/stm32f7/stm32_i2c.c index 13eb58e76be..26c79131fb4 100644 --- a/arch/arm/src/stm32f7/stm32_i2c.c +++ b/arch/arm/src/stm32f7/stm32_i2c.c @@ -232,6 +232,7 @@ #include #include #include +#include #include #include #include @@ -241,6 +242,7 @@ #include #include #include +#include #include #include @@ -446,6 +448,10 @@ struct stm32_i2c_priv_s #endif uint32_t status; /* End of transfer SR2|SR1 status */ + +#ifdef CONFIG_PM + struct pm_callback_s pm_cb; /* PM callbacks */ +#endif }; /* I2C Device, Instance */ @@ -504,6 +510,10 @@ static int stm32_i2c_transfer(FAR struct i2c_master_s *dev, FAR struct i2c_msg_s #ifdef CONFIG_I2C_RESET static int stm32_i2c_reset(FAR struct i2c_master_s * dev); #endif +#ifdef CONFIG_PM +static int stm32_i2c_pm_prepare(FAR struct pm_callback_s *cb, int domain, + enum pm_state_e pmstate); +#endif /************************************************************************************ * Private Data @@ -512,116 +522,128 @@ static int stm32_i2c_reset(FAR struct i2c_master_s * dev); #ifdef CONFIG_STM32F7_I2C1 static const struct stm32_i2c_config_s stm32_i2c1_config = { - .base = STM32_I2C1_BASE, - .clk_bit = RCC_APB1ENR_I2C1EN, - .reset_bit = RCC_APB1RSTR_I2C1RST, - .scl_pin = GPIO_I2C1_SCL, - .sda_pin = GPIO_I2C1_SDA, + .base = STM32_I2C1_BASE, + .clk_bit = RCC_APB1ENR_I2C1EN, + .reset_bit = RCC_APB1RSTR_I2C1RST, + .scl_pin = GPIO_I2C1_SCL, + .sda_pin = GPIO_I2C1_SDA, #ifndef CONFIG_I2C_POLLED - .ev_irq = STM32_IRQ_I2C1EV, - .er_irq = STM32_IRQ_I2C1ER + .ev_irq = STM32_IRQ_I2C1EV, + .er_irq = STM32_IRQ_I2C1ER #endif }; static struct stm32_i2c_priv_s stm32_i2c1_priv = { - .config = &stm32_i2c1_config, - .refs = 0, - .intstate = INTSTATE_IDLE, - .msgc = 0, - .msgv = NULL, - .ptr = NULL, - .frequency = 0, - .dcnt = 0, - .flags = 0, - .status = 0 + .config = &stm32_i2c1_config, + .refs = 0, + .intstate = INTSTATE_IDLE, + .msgc = 0, + .msgv = NULL, + .ptr = NULL, + .frequency = 0, + .dcnt = 0, + .flags = 0, + .status = 0, +#ifdef CONFIG_PM + .pm_cb.prepare = stm32_i2c_pm_prepare, +#endif }; #endif #ifdef CONFIG_STM32F7_I2C2 static const struct stm32_i2c_config_s stm32_i2c2_config = { - .base = STM32_I2C2_BASE, - .clk_bit = RCC_APB1ENR_I2C2EN, - .reset_bit = RCC_APB1RSTR_I2C2RST, - .scl_pin = GPIO_I2C2_SCL, - .sda_pin = GPIO_I2C2_SDA, + .base = STM32_I2C2_BASE, + .clk_bit = RCC_APB1ENR_I2C2EN, + .reset_bit = RCC_APB1RSTR_I2C2RST, + .scl_pin = GPIO_I2C2_SCL, + .sda_pin = GPIO_I2C2_SDA, #ifndef CONFIG_I2C_POLLED - .ev_irq = STM32_IRQ_I2C2EV, - .er_irq = STM32_IRQ_I2C2ER + .ev_irq = STM32_IRQ_I2C2EV, + .er_irq = STM32_IRQ_I2C2ER #endif }; static struct stm32_i2c_priv_s stm32_i2c2_priv = { - .config = &stm32_i2c2_config, - .refs = 0, - .intstate = INTSTATE_IDLE, - .msgc = 0, - .msgv = NULL, - .ptr = NULL, - .frequency = 0, - .dcnt = 0, - .flags = 0, - .status = 0 + .config = &stm32_i2c2_config, + .refs = 0, + .intstate = INTSTATE_IDLE, + .msgc = 0, + .msgv = NULL, + .ptr = NULL, + .frequency = 0, + .dcnt = 0, + .flags = 0, + .status = 0, +#ifdef CONFIG_PM + .pm_cb.prepare = stm32_i2c_pm_prepare, +#endif }; #endif #ifdef CONFIG_STM32F7_I2C3 static const struct stm32_i2c_config_s stm32_i2c3_config = { - .base = STM32_I2C3_BASE, - .clk_bit = RCC_APB1ENR_I2C3EN, - .reset_bit = RCC_APB1RSTR_I2C3RST, - .scl_pin = GPIO_I2C3_SCL, - .sda_pin = GPIO_I2C3_SDA, + .base = STM32_I2C3_BASE, + .clk_bit = RCC_APB1ENR_I2C3EN, + .reset_bit = RCC_APB1RSTR_I2C3RST, + .scl_pin = GPIO_I2C3_SCL, + .sda_pin = GPIO_I2C3_SDA, #ifndef CONFIG_I2C_POLLED - .ev_irq = STM32_IRQ_I2C3EV, - .er_irq = STM32_IRQ_I2C3ER + .ev_irq = STM32_IRQ_I2C3EV, + .er_irq = STM32_IRQ_I2C3ER #endif }; static struct stm32_i2c_priv_s stm32_i2c3_priv = { - .config = &stm32_i2c3_config, - .refs = 0, - .intstate = INTSTATE_IDLE, - .msgc = 0, - .msgv = NULL, - .ptr = NULL, - .frequency = 0, - .dcnt = 0, - .flags = 0, - .status = 0 + .config = &stm32_i2c3_config, + .refs = 0, + .intstate = INTSTATE_IDLE, + .msgc = 0, + .msgv = NULL, + .ptr = NULL, + .frequency = 0, + .dcnt = 0, + .flags = 0, + .status = 0, +#ifdef CONFIG_PM + .pm_cb.prepare = stm32_i2c_pm_prepare, +#endif }; #endif #ifdef CONFIG_STM32F7_I2C4 static const struct stm32_i2c_config_s stm32_i2c4_config = { - .base = STM32_I2C4_BASE, - .clk_bit = RCC_APB1ENR_I2C4EN, - .reset_bit = RCC_APB1RSTR_I2C4RST, - .scl_pin = GPIO_I2C4_SCL, - .sda_pin = GPIO_I2C4_SDA, + .base = STM32_I2C4_BASE, + .clk_bit = RCC_APB1ENR_I2C4EN, + .reset_bit = RCC_APB1RSTR_I2C4RST, + .scl_pin = GPIO_I2C4_SCL, + .sda_pin = GPIO_I2C4_SDA, #ifndef CONFIG_I2C_POLLED - .ev_irq = STM32_IRQ_I2C4EV, - .er_irq = STM32_IRQ_I2C4ER + .ev_irq = STM32_IRQ_I2C4EV, + .er_irq = STM32_IRQ_I2C4ER #endif }; static struct stm32_i2c_priv_s stm32_i2c4_priv = { - .config = &stm32_i2c4_config, - .refs = 0, - .intstate = INTSTATE_IDLE, - .msgc = 0, - .msgv = NULL, - .ptr = NULL, - .frequency = 0, - .dcnt = 0, - .flags = 0, - .status = 0 + .config = &stm32_i2c4_config, + .refs = 0, + .intstate = INTSTATE_IDLE, + .msgc = 0, + .msgv = NULL, + .ptr = NULL, + .frequency = 0, + .dcnt = 0, + .flags = 0, + .status = 0, +#ifdef CONFIG_PM + .pm_cb.prepare = stm32_i2c_pm_prepare, +#endif }; #endif @@ -629,9 +651,9 @@ static struct stm32_i2c_priv_s stm32_i2c4_priv = static const struct i2c_ops_s stm32_i2c_ops = { - .transfer = stm32_i2c_transfer + .transfer = stm32_i2c_transfer, #ifdef CONFIG_I2C_RESET - , .reset = stm32_i2c_reset + .reset = stm32_i2c_reset, #endif }; @@ -2640,12 +2662,82 @@ out: } #endif /* CONFIG_I2C_RESET */ +/************************************************************************************ + * Name: stm32_i2c_pm_prepare + * + * Description: + * Request the driver to prepare for a new power state. This is a + * warning that the system is about to enter into a new power state. The + * driver should begin whatever operations that may be required to enter + * power state. The driver may abort the state change mode by returning + * a non-zero value from the callback function. + * + * Input Parameters: + * cb - Returned to the driver. The driver version of the callback + * structure may include additional, driver-specific state + * data at the end of the structure. + * domain - Identifies the activity domain of the state change + * pmstate - Identifies the new PM state + * + * Returned Value: + * 0 (OK) means the event was successfully processed and that the driver + * is prepared for the PM state change. Non-zero means that the driver + * is not prepared to perform the tasks needed achieve this power setting + * and will cause the state change to be aborted. NOTE: The prepare + * method will also be recalled when reverting from lower back to higher + * power consumption modes (say because another driver refused a lower + * power state change). Drivers are not permitted to return non-zero + * values when reverting back to higher power consumption modes! + * + ************************************************************************************/ + +#ifdef CONFIG_PM +static int stm32_i2c_pm_prepare(FAR struct pm_callback_s *cb, int domain, + enum pm_state_e pmstate) +{ + struct stm32_i2c_priv_s *priv = + (struct stm32_i2c_priv_s *)((char *)cb - + offsetof(struct stm32_i2c_priv_s, pm_cb)); + int sval; + + /* Logic to prepare for a reduced power state goes here. */ + + switch (pmstate) + { + case PM_NORMAL: + case PM_IDLE: + break; + + case PM_STANDBY: + case PM_SLEEP: + /* Check if exclusive lock for I2C bus is held. */ + + if (nxsem_getvalue(&priv->sem_excl, &sval) < 0) + { + DEBUGASSERT(false); + return -EINVAL; + } + + if (sval <= 0) + { + /* Exclusive lock is held, do not allow entry to deeper PM states. */ + + return -EBUSY; + } + + break; + } + + return OK; +} +#endif + /************************************************************************************ * Public Functions ************************************************************************************/ /************************************************************************************ - * Name: up_i2cinitialize + * Name: stm32_i2cbus_initialize * * Description: * Initialize one I2C bus @@ -2656,7 +2748,10 @@ FAR struct i2c_master_s *stm32_i2cbus_initialize(int port) { struct stm32_i2c_priv_s * priv = NULL; /* private data of device with multiple instances */ struct stm32_i2c_inst_s * inst = NULL; /* device, single instance */ - int irqs; + irqstate_t irqs; +#ifdef CONFIG_PM + int ret; +#endif #if STM32_HSI_FREQUENCY != 16000000 || defined(INVALID_CLOCK_SOURCE) # warning STM32_I2C_INIT: Peripheral clock is HSI and it must be 16mHz or the speed/timing calculations need to be redone. @@ -2713,6 +2808,14 @@ FAR struct i2c_master_s *stm32_i2cbus_initialize(int port) { stm32_i2c_sem_init((struct i2c_master_s *)inst); stm32_i2c_init(priv); + +#ifdef CONFIG_PM + /* Register to receive power management callbacks */ + + ret = pm_register(&priv->pm_cb); + DEBUGASSERT(ret == OK); + UNUSED(ret); +#endif } leave_critical_section(irqs); @@ -2720,7 +2823,7 @@ FAR struct i2c_master_s *stm32_i2cbus_initialize(int port) } /************************************************************************************ - * Name: up_i2cuninitialize + * Name: stm32_i2cbus_uninitialize * * Description: * Uninitialize an I2C bus @@ -2729,7 +2832,7 @@ FAR struct i2c_master_s *stm32_i2cbus_initialize(int port) int stm32_i2cbus_uninitialize(FAR struct i2c_master_s * dev) { - int irqs; + irqstate_t irqs; ASSERT(dev); @@ -2751,6 +2854,12 @@ int stm32_i2cbus_uninitialize(FAR struct i2c_master_s * dev) leave_critical_section(irqs); +#ifdef CONFIG_PM + /* Unregister power management callbacks */ + + pm_unregister(&priv->pm_cb); +#endif + /* Disable power and other HW resource (GPIO's) */ stm32_i2c_deinit(((struct stm32_i2c_inst_s *)dev)->priv); @@ -2763,4 +2872,5 @@ int stm32_i2cbus_uninitialize(FAR struct i2c_master_s * dev) return OK; } -#endif /* CONFIG_STM32F7_I2C1 || CONFIG_STM32F7_I2C2 || CONFIG_STM32F7_I2C3 */ +#endif /* CONFIG_STM32F7_I2C1 || CONFIG_STM32F7_I2C2 || \ + CONFIG_STM32F7_I2C3 || CONFIG_STM32F7_I2C4 */ diff --git a/arch/arm/src/stm32f7/stm32_spi.c b/arch/arm/src/stm32f7/stm32_spi.c index cd1dc679893..4d433518ecf 100644 --- a/arch/arm/src/stm32f7/stm32_spi.c +++ b/arch/arm/src/stm32f7/stm32_spi.c @@ -66,6 +66,7 @@ #include #include #include +#include #include #include #include @@ -74,6 +75,7 @@ #include #include #include +#include #include @@ -165,6 +167,9 @@ struct stm32_spidev_s uint32_t actual; /* Actual clock frequency */ int8_t nbits; /* Width of word in bits */ uint8_t mode; /* Mode 0,1,2,3 */ +#ifdef CONFIG_PM + struct pm_callback_s pm_cb; /* PM callbacks */ +#endif }; /************************************************************************************ @@ -221,6 +226,13 @@ static void spi_recvblock(FAR struct spi_dev_s *dev, FAR void *rxbuffer, static void spi_bus_initialize(FAR struct stm32_spidev_s *priv); +/* PM interface */ + +#ifdef CONFIG_PM +static int spi_pm_prepare(FAR struct pm_callback_s *cb, int domain, + enum pm_state_e pmstate); +#endif + /************************************************************************************ * Private Data ************************************************************************************/ @@ -266,6 +278,9 @@ static struct stm32_spidev_s g_spi1dev = .rxch = DMAMAP_SPI1_RX, .txch = DMAMAP_SPI1_TX, #endif +#ifdef CONFIG_PM + .pm_cb.prepare = spi_pm_prepare, +#endif }; #endif @@ -310,6 +325,9 @@ static struct stm32_spidev_s g_spi2dev = .rxch = DMAMAP_SPI2_RX, .txch = DMAMAP_SPI2_TX, #endif +#ifdef CONFIG_PM + .pm_cb.prepare = spi_pm_prepare, +#endif }; #endif @@ -354,6 +372,9 @@ static struct stm32_spidev_s g_spi3dev = .rxch = DMAMAP_SPI3_RX, .txch = DMAMAP_SPI3_TX, #endif +#ifdef CONFIG_PM + .pm_cb.prepare = spi_pm_prepare, +#endif }; #endif @@ -398,6 +419,9 @@ static struct stm32_spidev_s g_spi4dev = .rxch = DMAMAP_SPI4_RX, .txch = DMAMAP_SPI4_TX, #endif +#ifdef CONFIG_PM + .pm_cb.prepare = spi_pm_prepare, +#endif }; #endif @@ -442,6 +466,9 @@ static struct stm32_spidev_s g_spi5dev = .rxch = DMAMAP_SPI5_RX, .txch = DMAMAP_SPI5_TX, #endif +#ifdef CONFIG_PM + .pm_cb.prepare = spi_pm_prepare, +#endif }; #endif @@ -486,6 +513,9 @@ static struct stm32_spidev_s g_spi6dev = .rxch = DMAMAP_SPI6_RX, .txch = DMAMAP_SPI6_TX, #endif +#ifdef CONFIG_PM + .pm_cb.prepare = spi_pm_prepare, +#endif }; #endif @@ -1693,6 +1723,76 @@ static void spi_recvblock(FAR struct spi_dev_s *dev, FAR void *rxbuffer, size_t } #endif +/************************************************************************************ + * Name: spi_pm_prepare + * + * Description: + * Request the driver to prepare for a new power state. This is a + * warning that the system is about to enter into a new power state. The + * driver should begin whatever operations that may be required to enter + * power state. The driver may abort the state change mode by returning + * a non-zero value from the callback function. + * + * Input Parameters: + * cb - Returned to the driver. The driver version of the callback + * structure may include additional, driver-specific state + * data at the end of the structure. + * domain - Identifies the activity domain of the state change + * pmstate - Identifies the new PM state + * + * Returned Value: + * 0 (OK) means the event was successfully processed and that the driver + * is prepared for the PM state change. Non-zero means that the driver + * is not prepared to perform the tasks needed achieve this power setting + * and will cause the state change to be aborted. NOTE: The prepare + * method will also be recalled when reverting from lower back to higher + * power consumption modes (say because another driver refused a lower + * power state change). Drivers are not permitted to return non-zero + * values when reverting back to higher power consumption modes! + * + ************************************************************************************/ + +#ifdef CONFIG_PM +static int spi_pm_prepare(FAR struct pm_callback_s *cb, int domain, + enum pm_state_e pmstate) +{ + struct stm32_spidev_s *priv = + (struct stm32_spidev_s *)((char *)cb - + offsetof(struct stm32_spidev_s, pm_cb)); + int sval; + + /* Logic to prepare for a reduced power state goes here. */ + + switch (pmstate) + { + case PM_NORMAL: + case PM_IDLE: + break; + + case PM_STANDBY: + case PM_SLEEP: + /* Check if exclusive lock for SPI bus is held. */ + + if (nxsem_getvalue(&priv->exclsem, &sval) < 0) + { + DEBUGASSERT(false); + return -EINVAL; + } + + if (sval <= 0) + { + /* Exclusive lock is held, do not allow entry to deeper PM states. */ + + return -EBUSY; + } + + break; + } + + return OK; +} +#endif + /************************************************************************************ * Name: spi_bus_initialize * @@ -1707,10 +1807,13 @@ static void spi_recvblock(FAR struct spi_dev_s *dev, FAR void *rxbuffer, size_t * ************************************************************************************/ -static void spi_bus_initialize(FAR struct stm32_spidev_s *priv) +static void spi_bus_initialize(struct stm32_spidev_s *priv) { uint16_t setbits; uint16_t clrbits; +#ifdef CONFIG_PM + int ret; +#endif /* Configure CR1 and CR2. Default configuration: * Mode 0: CR1.CPHA=0 and CR1.CPOL=0 @@ -1776,6 +1879,14 @@ static void spi_bus_initialize(FAR struct stm32_spidev_s *priv) /* Enable spi */ spi_modifycr1(priv, SPI_CR1_SPE, 0); + +#ifdef CONFIG_PM + /* Register to receive power management callbacks */ + + ret = pm_register(&priv->pm_cb); + DEBUGASSERT(ret == OK); + UNUSED(ret); +#endif } /************************************************************************************