Improve I2C power-cycling logic:

We only power, clock and enable the peripepheral when / if we need it

* We no longer automatically turn on the SERIAL PD when the CM3 is running
* Make sure the I2C peripheral is accessible (powered and clocked) before any operation
* If the peripheral is not accessible, automatically power it up and run the clock
* Put SDA, SCL, SDA HP and SCL HP in a low-leakage state when shutting down
* Don't automatically fire up the I2C controller when we wake up
This commit is contained in:
George Oikonomou 2015-05-01 16:34:42 +01:00
parent 0ad4b5f323
commit 34be012661
3 changed files with 108 additions and 14 deletions

View file

@ -39,9 +39,55 @@
#include "contiki-conf.h"
#include "ti-lib.h"
#include "board-i2c.h"
#include "lpm.h"
#include <string.h>
#include <stdbool.h>
/*---------------------------------------------------------------------------*/
#define NO_INTERFACE 0xFF
/*---------------------------------------------------------------------------*/
static uint8_t slave_addr = 0x00;
static uint8_t interface = 0xFF;
static uint8_t interface = NO_INTERFACE;
/*---------------------------------------------------------------------------*/
static bool
accessible(void)
{
/* First, check the PD */
if(ti_lib_prcm_power_domain_status(PRCM_DOMAIN_SERIAL)
!= PRCM_DOMAIN_POWER_ON) {
return false;
}
/* Then check the 'run mode' clock gate */
if(!(HWREG(PRCM_BASE + PRCM_O_I2CCLKGR) & PRCM_I2CCLKGR_CLK_EN)) {
return false;
}
return true;
}
/*---------------------------------------------------------------------------*/
void
board_i2c_wakeup()
{
/* First, make sure the SERIAL PD is on */
ti_lib_prcm_power_domain_on(PRCM_DOMAIN_SERIAL);
while((ti_lib_prcm_power_domain_status(PRCM_DOMAIN_SERIAL)
!= PRCM_DOMAIN_POWER_ON));
/* Enable the clock to I2C */
ti_lib_prcm_peripheral_run_enable(PRCM_PERIPH_I2C0);
ti_lib_prcm_load_set();
while(!ti_lib_prcm_load_get());
/* Reset the I2C controller */
HWREG(PRCM_BASE + PRCM_O_RESETI2C) = PRCM_RESETI2C_I2C;
/* Enable and initialize the I2C master module */
ti_lib_i2c_master_init_exp_clk(I2C0_BASE,
ti_lib_sys_ctrl_peripheral_clock_get(
PRCM_PERIPH_I2C0, SYSCTRL_SYSBUS_ON),
true);
}
/*---------------------------------------------------------------------------*/
static bool
i2c_status()
@ -57,21 +103,34 @@ i2c_status()
}
/*---------------------------------------------------------------------------*/
void
board_i2c_init()
board_i2c_shutdown()
{
/* The I2C peripheral must be enabled */
ti_lib_prcm_peripheral_run_enable(PRCM_PERIPH_I2C0);
interface = NO_INTERFACE;
if(accessible()) {
ti_lib_i2c_master_disable(I2C0_BASE);
}
ti_lib_prcm_peripheral_run_disable(PRCM_PERIPH_I2C0);
ti_lib_prcm_load_set();
while(!ti_lib_prcm_load_get());
/* Reset the I2C controller */
HWREG(PRCM_BASE + PRCM_O_RESETI2C) = PRCM_RESETI2C_I2C;
/*
* Set all pins to GPIO Input and disable the output driver. Set internal
* pull to match external pull
*
* SDA and SCL: external PU resistor
* SDA HP and SCL HP: MPU PWR low
*/
ti_lib_ioc_pin_type_gpio_input(BOARD_IOID_SDA_HP);
ti_lib_ioc_io_port_pull_set(BOARD_IOID_SDA_HP, IOC_IOPULL_DOWN);
ti_lib_ioc_pin_type_gpio_input(BOARD_IOID_SCL_HP);
ti_lib_ioc_io_port_pull_set(BOARD_IOID_SCL_HP, IOC_IOPULL_DOWN);
/* Enable and initialize the I2C master module */
ti_lib_i2c_master_init_exp_clk(I2C0_BASE,
ti_lib_sys_ctrl_peripheral_clock_get(
PRCM_PERIPH_I2C0, SYSCTRL_SYSBUS_ON),
true);
ti_lib_ioc_pin_type_gpio_input(BOARD_IOID_SDA);
ti_lib_ioc_io_port_pull_set(BOARD_IOID_SDA, IOC_IOPULL_UP);
ti_lib_ioc_pin_type_gpio_input(BOARD_IOID_SCL);
ti_lib_ioc_io_port_pull_set(BOARD_IOID_SCL, IOC_IOPULL_UP);
}
/*---------------------------------------------------------------------------*/
bool
@ -248,8 +307,15 @@ board_i2c_select(uint8_t new_interface, uint8_t address)
{
slave_addr = address;
if(accessible() == false) {
board_i2c_wakeup();
}
if(new_interface != interface) {
interface = new_interface;
ti_lib_i2c_master_disable(I2C0_BASE);
if(interface == BOARD_I2C_INTERFACE_0) {
ti_lib_ioc_io_port_pull_set(BOARD_IOID_SDA, IOC_NO_IOPULL);
ti_lib_ioc_io_port_pull_set(BOARD_IOID_SCL, IOC_NO_IOPULL);
@ -263,6 +329,12 @@ board_i2c_select(uint8_t new_interface, uint8_t address)
ti_lib_ioc_pin_type_gpio_input(BOARD_IOID_SDA);
ti_lib_ioc_pin_type_gpio_input(BOARD_IOID_SCL);
}
/* Enable and initialize the I2C master module */
ti_lib_i2c_master_init_exp_clk(I2C0_BASE,
ti_lib_sys_ctrl_peripheral_clock_get(
PRCM_PERIPH_I2C0, SYSCTRL_SYSBUS_ON),
true);
}
}
/*---------------------------------------------------------------------------*/

View file

@ -101,6 +101,27 @@ bool board_i2c_write_single(uint8_t data);
*/
bool board_i2c_write_read(uint8_t *wdata, uint8_t wlen, uint8_t *rdata,
uint8_t rlen);
/**
* \brief Enables the I2C peripheral with defaults
*
* This function is called to wakeup and initialise the I2C.
*
* This function can be called explicitly, but it will also be called
* automatically by board_i2c_select() when required. One of those two
* functions MUST be called before any other I2C operation after a chip
* sleep / wakeup cycle or after a call to board_i2c_shutdown(). Failing to do
* so will lead to a bus fault.
*/
void board_i2c_wakeup(void);
/**
* \brief Stops the I2C peripheral and restores pins to s/w control
*
* This function is called automatically by the board's LPM logic, but it
* can also be called explicitly.
*/
void board_i2c_shutdown(void);
/*---------------------------------------------------------------------------*/
#endif /* BOARD_I2C_H_ */
/*---------------------------------------------------------------------------*/

View file

@ -65,8 +65,6 @@ static void
lpm_wakeup_handler(void)
{
power_domains_on();
board_i2c_init();
}
/*---------------------------------------------------------------------------*/
static void
@ -80,6 +78,9 @@ shutdown_handler(uint8_t mode)
SENSORS_DEACTIVATE(hdc_1000_sensor);
mpu_9250_sensor.configure(MPU_9250_SENSOR_SHUTDOWN, 0);
}
/* In all cases, stop the I2C */
board_i2c_shutdown();
}
/*---------------------------------------------------------------------------*/
/*
@ -125,7 +126,6 @@ board_init()
ti_lib_ioc_io_port_pull_set(BOARD_IOID_KEY_RIGHT, IOC_IOPULL_UP);
/* I2C controller */
board_i2c_init();
/* Sensor interface */
ti_lib_rom_ioc_pin_type_gpio_input(BOARD_IOID_MPU_INT);
@ -139,6 +139,7 @@ board_init()
/* Flash interface */
ti_lib_rom_ioc_pin_type_gpio_output(BOARD_IOID_FLASH_CS);
ti_lib_gpio_pin_write(BOARD_FLASH_CS, 1);
board_i2c_wakeup();
buzzer_init();