Skip to content
Snippets Groups Projects
sleep.c 6.66 KiB
Newer Older
#include "epicardium.h"
#include "modules/modules.h"
#include "modules/log.h"

#include "card10.h"
#include "simo.h"
#include "lp.h"
#include "max86150.h"
#include "MAX77650-Arduino-Library.h"

#include "max32665.h"
#include "mxc_sys.h"
#include "mxc_pins.h"

#include <stdint.h>

/* Most code is taken and adapted rom EvKitExamples/LP/main.c */

static uint32_t old_clkcn;
static uint32_t old_perckcn0, old_perckcn1;

void GPIOWAKE_IRQHandler(void)
{
	/* Nothing to do here */
}

static void switchToHIRC(void)
{
	MXC_GCR->clkcn &= ~(MXC_S_GCR_CLKCN_PSC_DIV128);
	MXC_GCR->clkcn |= MXC_S_GCR_CLKCN_PSC_DIV4;
	MXC_GCR->clkcn |= MXC_F_GCR_CLKCN_HIRC_EN;

	MXC_SETFIELD(
		MXC_GCR->clkcn,
		MXC_F_GCR_CLKCN_CLKSEL,
		MXC_S_GCR_CLKCN_CLKSEL_HIRC
	);

	while (!(MXC_GCR->clkcn & MXC_F_GCR_CLKCN_CKRDY))
		; /* Wait for the clock switch to occur */

	/* Disable the now unused 96 MHz clock */
	MXC_GCR->clkcn &= ~(MXC_F_GCR_CLKCN_HIRC96M_EN);

	SystemCoreClockUpdate();
}

static void deepsleep(void)
{
	SIMO_setVregO_B(810); /* Reduce VCOREB to 0.81 V */
	LP_FastWakeupEnable();
	LP_EnterDeepSleepMode();
	SIMO_setVregO_B(1000); /* Restore VCOREB to 1 V */
}

static void turnOffClocks(void)
{
	old_perckcn0 = MXC_GCR->perckcn0;
	old_perckcn1 = MXC_GCR->perckcn1;

	/* Allow the USB Switch to be turned off in deepsleep and backup modes. */
	/* TODO: should this be the default setting? */
	LP_USBSWLPDisable();

	/* Disable all peripheral clocks except GPIO0 (needed for interrupt wakeup) */
	MXC_GCR->perckcn0 = 0xFFFFFFFE;
	MXC_GCR->perckcn1 = 0xFFFFFFFF;
}

static void turnOnClocks(void)
{
	MXC_GCR->perckcn0 = old_perckcn0;
	MXC_GCR->perckcn1 = old_perckcn1;
}

/*
 * Move most GPIOs into a special low power state with the fact
 * in mind that the external 3.3 V are switched off.
 *
 * E.g. this means that the SD card pins need to be pulled low
 * to preven them from backfeeding into the 3.3 V rail via their
 * external pull-up resistors.
 *
 * Pins needed to talk to the PMIC are left untouched.
 * ECG AOUT and 32 kHz out as well.
 */
static void gpio_low_power(void)
{
	/* clang-format off */
	const gpio_cfg_t pins_low_power[] = {
		{ PORT_0, PIN_0, GPIO_FUNC_IN, GPIO_PAD_PULL_UP },      // FLash
		{ PORT_0, PIN_1, GPIO_FUNC_IN, GPIO_PAD_PULL_UP },      // Flash
		{ PORT_0, PIN_2, GPIO_FUNC_IN, GPIO_PAD_PULL_UP },      // Flash
		{ PORT_0, PIN_3, GPIO_FUNC_IN, GPIO_PAD_PULL_UP },      // Flash
		{ PORT_0, PIN_4, GPIO_FUNC_IN, GPIO_PAD_PULL_UP },      // Flash
		{ PORT_0, PIN_5, GPIO_FUNC_IN, GPIO_PAD_PULL_UP },      // Flash
		{ PORT_0, PIN_6, GPIO_FUNC_OUT, GPIO_PAD_NONE },        // I2C 3.3V
		{ PORT_0, PIN_7, GPIO_FUNC_OUT, GPIO_PAD_NONE },        // I2C 3.3V
		{ PORT_0, PIN_8, GPIO_FUNC_OUT, GPIO_PAD_NONE },        // Motor
		{ PORT_0, PIN_9, GPIO_FUNC_IN, GPIO_PAD_PULL_UP },      // UART TX
		{ PORT_0, PIN_10, GPIO_FUNC_IN, GPIO_PAD_PULL_UP },     // UART RX
		{ PORT_0, PIN_11, GPIO_FUNC_IN, GPIO_PAD_PULL_DOWN },   // BMA400 interrupt
		// 0, 12: PMIC IRQ
		{ PORT_0, PIN_13, GPIO_FUNC_IN, GPIO_PAD_NONE },        // BHI160 interrupt, not sure if PP
		// 0, 14: PMIC I2C
		// 0, 15: PMIC I2C
		{ PORT_0, PIN_16, GPIO_FUNC_IN, GPIO_PAD_PULL_UP },     // PMIC AMUX
		{ PORT_0, PIN_17, GPIO_FUNC_IN, GPIO_PAD_PULL_DOWN },   // SOA GPIO
		// 0, 18: ECG AOUT
		// 0, 19: 32 kHz
		{ PORT_0, PIN_20, GPIO_FUNC_IN, GPIO_PAD_PULL_DOWN },   // Wristband 1
		{ PORT_0, PIN_21, GPIO_FUNC_IN, GPIO_PAD_PULL_DOWN },   // Wristband 2
		{ PORT_0, PIN_22, GPIO_FUNC_IN, GPIO_PAD_PULL_DOWN },   // Wristband 3
		{ PORT_0, PIN_23, GPIO_FUNC_OUT, GPIO_PAD_NONE },       // IR-LED
		{ PORT_0, PIN_24, GPIO_FUNC_OUT, GPIO_PAD_NONE },       // display SS
		{ PORT_0, PIN_25, GPIO_FUNC_OUT, GPIO_PAD_NONE },       // display MOSI
		{ PORT_0, PIN_26, GPIO_FUNC_IN, GPIO_PAD_PULL_DOWN },   // SOA GPIO
		{ PORT_0, PIN_27, GPIO_FUNC_OUT, GPIO_PAD_NONE },       // display SCK
		{ PORT_0, PIN_28, GPIO_FUNC_OUT, GPIO_PAD_NONE },       // Backlight
		{ PORT_0, PIN_29, GPIO_FUNC_IN, GPIO_PAD_PULL_DOWN },   // Wristband 4
		// 0, 30: PMIC power hold
		{ PORT_0, PIN_31, GPIO_FUNC_OUT, GPIO_PAD_NONE },   // ECG switch

		{ PORT_1, PIN_0, GPIO_FUNC_OUT, GPIO_PAD_NONE },    // SDHC
		{ PORT_1, PIN_1, GPIO_FUNC_OUT, GPIO_PAD_NONE },    // SDHC
		{ PORT_1, PIN_2, GPIO_FUNC_OUT, GPIO_PAD_NONE },    // SDHC
		{ PORT_1, PIN_3, GPIO_FUNC_OUT, GPIO_PAD_NONE },    // SDHC
		{ PORT_1, PIN_4, GPIO_FUNC_OUT, GPIO_PAD_NONE },    // SDHC
		{ PORT_1, PIN_5, GPIO_FUNC_OUT, GPIO_PAD_NONE },    // SDHC
		{ PORT_1, PIN_6, GPIO_FUNC_OUT, GPIO_PAD_NONE },    // display RS
		{ PORT_1, PIN_7, GPIO_FUNC_IN, GPIO_PAD_PULL_UP },  // Portexpander interrupt
		{ PORT_1, PIN_8, GPIO_FUNC_IN, GPIO_PAD_PULL_UP },  // ECG CS TODO: better OUT high
		{ PORT_1, PIN_9, GPIO_FUNC_OUT, GPIO_PAD_NONE },    // ECG SDI
		{ PORT_1, PIN_10, GPIO_FUNC_IN, GPIO_PAD_PULL_UP }, // ECG SDO
		{ PORT_1, PIN_11, GPIO_FUNC_IN, GPIO_PAD_PULL_UP }, // ECG SCK
		{ PORT_1, PIN_11, GPIO_FUNC_IN, GPIO_PAD_PULL_UP }, // ECG INT
		{ PORT_1, PIN_13, GPIO_FUNC_IN, GPIO_PAD_NONE },    // PPG Interrupt
		{ PORT_1, PIN_14, GPIO_FUNC_OUT, GPIO_PAD_NONE },   // RGB LEDs
		{ PORT_1, PIN_15, GPIO_FUNC_OUT, GPIO_PAD_NONE },   // RGB LEDs
	};
	/* clang-format on */

	const unsigned int num_pins =
		(sizeof(pins_low_power) / sizeof(gpio_cfg_t));
	for (size_t i = 0; i < num_pins; i++) {
		GPIO_OutClr(&pins_low_power[i]);
		GPIO_Config(&pins_low_power[i]);
	}
}

/*
 * Go to deepsleep, turning off peripherals.
 *
 * This functions returns after waking up again.
 * Currently the only wakeup source is an interrupt
 * from the PMIC.
 *
 * Some peripherals and GPIOs are moved into a low
 * power state before going to sleep. There is no guarantee
 * that all peripherals will be moved to a low power state
 *
 * TODO: Move BHI160, BMA400, BME680, MAX30001 into a low
 * power state.
 *
 * The state of the GPIOs and generally all peripherials
 * on board is not restored after a wakeup.
 */
void sleep_deepsleep(void)
{
	LOG_WARN("pmic", "Powersave");
	epic_disp_backlight(0);
	epic_leds_set_rocket(0, 0);
	epic_leds_set_rocket(1, 0);
	epic_leds_set_rocket(2, 0);
#if 1
	/* This will fail if there is no
     * harmonic board attached */
	max86150_begin();
	max86150_getINT1();
	max86150_getINT2();
	max86150_shutDown();
#endif
	epic_bhi160_disable_all_sensors();
	epic_bme680_deinit();
	epic_max30001_disable_sensor();
	MAX77650_setEN_SBB2(0b100);
	MAX77650_setSBIA_LPM(true);
	core1_stop();
	MAX77650_getINT_GLBL();
	gpio_low_power();
	turnOffClocks();
	old_clkcn = MXC_GCR->clkcn;
	switchToHIRC();
	deepsleep();

	/* Now wait for an interrupt to wake us up */

	turnOnClocks();
	MXC_GCR->clkcn = old_clkcn;
	while (!(MXC_GCR->clkcn & MXC_F_GCR_CLKCN_CKRDY))
		; /* Wait for the clock switch to occur */
	SystemCoreClockUpdate();
	MAX77650_setEN_SBB2(0b110);
}