Skip to content

asterix: scrub watchdog and PMIC settings [FIRM-82] #182

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file removed bin/boot/[email protected]
Binary file not shown.
Binary file removed bin/boot/[email protected]
Binary file not shown.
1,061 changes: 0 additions & 1,061 deletions bin/boot/[email protected]

This file was deleted.

Binary file added bin/boot/[email protected]
Binary file not shown.
Binary file added bin/boot/[email protected]
Binary file not shown.
1,033 changes: 1,033 additions & 0 deletions bin/boot/[email protected]

Large diffs are not rendered by default.

Binary file removed bin/boot/[email protected]
Binary file not shown.
Binary file removed bin/boot/[email protected]
Binary file not shown.
1,057 changes: 0 additions & 1,057 deletions bin/boot/[email protected]

This file was deleted.

Binary file added bin/boot/[email protected]
Binary file not shown.
Binary file added bin/boot/[email protected]
Binary file not shown.
1,028 changes: 1,028 additions & 0 deletions bin/boot/[email protected]

Large diffs are not rendered by default.

210 changes: 142 additions & 68 deletions platform/asterix/boot/src/drivers/pmic.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,25 @@
#include <drivers/dbgserial.h>
#include <nrfx_twi.h>

#define VBUSIN_BASE 0x02U
#define CHARGER_BASE 0x03U
#define BUCK_BASE 0x04U
#define ADC_BASE 0x05U
#define GPIOS_BASE 0x06U
#define TIMER_BASE 0x07U
#define LDSW_BASE 0x08U
#define SHIP_BASE 0x0BU
#define ERRLOG_BASE 0x0EU

// VBUSIN
#define VBUSINILIMSTARTUP 0x02U
#define VBUSLIM_500MA 0x00U

// CHARGER
#define TASKRELEASEERROR 0x0U

#define TASKCLEARCHGERR 0x1U

#define BCHGENABLESET 0x04U
#define ENABLECHARGING_ENABLECHG 0x01U

Expand All @@ -25,22 +37,71 @@
#define BCHGVTERMR 0x0DU
#define BCHGVTERMREDUCED_4V00 0x4U

// BUCK
#define BUCK1ENASET 0x0
#define BUCK2ENASET 0x2

#define BUCK1PWMCLR 0x5U
#define BUCK2PWMCLR 0x7U
#define BUCKPWMCLR_SET 0x01U

#define BUCK1NORMVOUT 0x8U
#define BUCK1RETVOUT 0x9U
#define BUCK2NORMVOUT 0xAU
#define BUCK2RETVOUT 0xBU
#define BUCKVOUT_1V8 8U
#define BUCKVOUT_3V0 20U

#define BUCKENCTRL 0xC
#define BUCKVRETCTRL 0xD
#define BUCKPWMCTRL 0xE

#define BUCKSWCTRLSET 0xFU
#define BUCKSWCTRLSET_BUCK1SWCTRLSET 0x01U
#define BUCKSWCTRLSET_BUCK2SWCTRLSET 0x02U

#define BUCKCTRL0 0x15U

// ADC
#define ADCNTCRSEL 0x0AU
#define ADCNTCRSEL_10K 0x1U

// GPIOS
#define GPIOMODE0 0x0U
#define GPIOMODE1 0x1U
#define GPIOMODE2 0x2U
#define GPIOMODE3 0x3U
#define GPIOMODE4 0x4U

#define GPIOMODE_GPIINPUT 0U
#define GPIOMODE_GPIEVENTFALL 4U
#define GPIOMODE_GPOIRQ 5U

#define GPIOPUEN0 0xAU
#define GPIOOPENDRAIN0 0x14U

// TIMER
#define TIMERCLR 0x01U
#define TIMERCLR_TASKTIMERDIS 0x01U

// LDO
#define TASKLDSW1SET 0x00U
#define TASKLDSW2SET 0x02U

#define LDSW1GPISEL 0x05U
#define LDSW2GPISEL 0x06U

#define LDSW1LDOSEL 0x08U
#define LDSW2LDOSEL 0x09U
#define LDSW2LDOSEL_LDO 0x01U
#define LDSWLDOSEL_LDO 0x01U

#define LDSW1VOUTSEL 0x0CU
#define LDSW2VOUTSEL 0x0DU
#define LDSW2VOUTSEL_1V8 0x08U
#define LDSWVOUTSEL_1V8 0x08U

// SHIP
#define TASKSHPHLDCONFIGSTROBE 0x1U
#define LPRESETCFG 0x6U

// ERRLOG
#define SCRATCH0 0x1U
Expand All @@ -67,6 +128,12 @@ static int prv_pmic_write(uint8_t base, uint8_t reg, uint8_t val) {
return 0;
}

struct pmic_reg {
uint8_t base;
uint8_t reg;
uint8_t val;
};

int pmic_init(void) {
int ret;
nrfx_err_t err;
Expand All @@ -78,72 +145,79 @@ int pmic_init(void) {

nrfx_twi_enable(&twi);

// Turn off any watchdog / boot timer right away.
ret = prv_pmic_write(TIMER_BASE, TIMERCLR, TIMERCLR_TASKTIMERDIS);
if (ret != 0) {
return ret;
}

ret = prv_pmic_write(ERRLOG_BASE, SCRATCH0, 0x00);
if (ret != 0) {
return ret;
}

// Configure charger (TODO: values are board/battery dependent)
// - Thermistor: 10K NTC
// - Termination voltage: 4.2V
// - Reduced termination voltage (for warm region): 4.00V
// - 64mA charge/discharge current (standard charging)
// - Enable charging
ret = prv_pmic_write(CHARGER_BASE, BCHGENABLECLR, ENABLECHARGING_DISABLECHG);
if (ret != 0) {
return ret;
}

ret = prv_pmic_write(ADC_BASE, ADCNTCRSEL, ADCNTCRSEL_10K);
if (ret != 0) {
return ret;
}

ret = prv_pmic_write(CHARGER_BASE, BCHGVTERM, BCHGVTERMNORM_4V20);
if (ret != 0) {
return ret;
}

ret = prv_pmic_write(CHARGER_BASE, BCHGVTERMR, BCHGVTERMREDUCED_4V00);
if (ret != 0) {
return ret;
}

ret = prv_pmic_write(CHARGER_BASE, BCHGISETMSB, 16);
if (ret != 0) {
return ret;
}

ret = prv_pmic_write(CHARGER_BASE, BCHGISETDISCHARGEMSB, 16);
if (ret != 0) {
return ret;
}

ret = prv_pmic_write(CHARGER_BASE, BCHGENABLESET, ENABLECHARGING_ENABLECHG);
if (ret != 0) {
return ret;
}

// LDO2 as LDO @ 1.8V (powers the QSPI flash)
ret = prv_pmic_write(LDSW_BASE, LDSW2LDOSEL, LDSW2LDOSEL_LDO);
if (ret != 0) {
return ret;
}

ret = prv_pmic_write(LDSW_BASE, LDSW2VOUTSEL, LDSW2VOUTSEL_1V8);
if (ret != 0) {
return ret;
}

ret = prv_pmic_write(LDSW_BASE, TASKLDSW2SET, 0x01U);
if (ret != 0) {
return ret;
const struct pmic_reg regs[] = {
// Turn off any watchdog / boot timer right away.
{ TIMER_BASE, TIMERCLR, TIMERCLR_TASKTIMERDIS },
{ ERRLOG_BASE, SCRATCH0, 0x00 /* contains boot timer bit */ },

// Make sure right away that we can reset the device if needed.
{ SHIP_BASE, LPRESETCFG, 0 },
{ SHIP_BASE, TASKSHPHLDCONFIGSTROBE, 1 },

// Set up the BUCK1 regulator for manual control to 1.8V, automatic
// PWM/hysteresis control.
{ BUCK_BASE, BUCK1ENASET, 1 },
{ BUCK_BASE, BUCK2ENASET, 1 },
{ BUCK_BASE, BUCK1PWMCLR, 1 },
{ BUCK_BASE, BUCK2PWMCLR, 1 },
{ BUCK_BASE, BUCK1NORMVOUT, BUCKVOUT_1V8 },
{ BUCK_BASE, BUCK1RETVOUT, BUCKVOUT_1V8 },
{ BUCK_BASE, BUCK2NORMVOUT, BUCKVOUT_3V0 },
{ BUCK_BASE, BUCK2RETVOUT, BUCKVOUT_3V0 },
Comment on lines +163 to +166
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see previous comment

{ BUCK_BASE, BUCKENCTRL, 0 },
{ BUCK_BASE, BUCKVRETCTRL, 0 },
{ BUCK_BASE, BUCKPWMCTRL, 0 },
{ BUCK_BASE, BUCKSWCTRLSET, BUCKSWCTRLSET_BUCK1SWCTRLSET | BUCKSWCTRLSET_BUCK2SWCTRLSET /* use registers rather than resistor settings */ },
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note: vset resistors will always force nominal voltages (1.8 and 3.0V), so it may be unsafe to allow sw controlled values here.

Also, as a general comment: holding the back button for 10s will reset the PMIC, so we have a known path to go back to a safe state in case things go terribly wrong.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, it will reset the PMIC as long as LPRESETCFG is set properly!

But I would like to explicitly set these values, because 1) analog straps always kind of scare me, and 2) when we set them explicitly, the behavior changes in at least some way (!) vs. when we leave them implicitly set from Rset.

{ BUCK_BASE, BUCKCTRL0, 0 },

// Configure charger (TODO: values are board/battery dependent)
// - Thermistor: 10K NTC
// - Termination voltage: 4.2V
// - Reduced termination voltage (for warm region): 4.00V
// - Charge current limit of 152 mA (approximately 1C for most reasonable wearable batteries)
// - Discharge current limit of 200 mA (increase current measurement accuracy)
// - Release charger from error state if applicable (but do not clear
// safety timers) -- this doesn't happen in a loop because after we
// fail to boot three times, we will sit at sadwatch until a button is
// pressed
// - Enable charging
{ VBUSIN_BASE, VBUSINILIMSTARTUP, VBUSLIM_500MA }, // should be default, but 'reset value from OTP, value listed in this table may not be correct'
{ CHARGER_BASE, BCHGENABLECLR, ENABLECHARGING_DISABLECHG },
{ ADC_BASE, ADCNTCRSEL, ADCNTCRSEL_10K },
{ CHARGER_BASE, BCHGVTERM, BCHGVTERMNORM_4V20 },
{ CHARGER_BASE, BCHGVTERMR, BCHGVTERMREDUCED_4V00 },
{ CHARGER_BASE, BCHGISETMSB, 38 },
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0.5C may still be a safer limit here, considering discharge can be adjusted independently

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Really, 1C will be fine. (Actually, 1C will be too slow for most users and we will want to turn it up even further in firmware.) But 1C is definitely safe for the bootloader to do.

{ CHARGER_BASE, BCHGISETDISCHARGEMSB, 42 },
{ CHARGER_BASE, TASKCLEARCHGERR, 1 },
{ CHARGER_BASE, TASKRELEASEERROR, 1 },
{ CHARGER_BASE, BCHGENABLESET, ENABLECHARGING_ENABLECHG },

// LDO1 as LDO @ 1.8V (powers the DA7212 ... do not back power it through I/O pins, and it must always be on because sensors share I2C bus with it!)
{ LDSW_BASE, LDSW1GPISEL, 0 },
{ LDSW_BASE, LDSW1VOUTSEL, LDSWVOUTSEL_1V8 },
{ LDSW_BASE, LDSW1LDOSEL, LDSWLDOSEL_LDO },
{ LDSW_BASE, TASKLDSW1SET, 0x01U },

// LDO2 as LDO @ 1.8V (powers the QSPI flash)
{ LDSW_BASE, LDSW2GPISEL, 0 },
{ LDSW_BASE, LDSW2VOUTSEL, LDSWVOUTSEL_1V8 },
{ LDSW_BASE, LDSW2LDOSEL, LDSWLDOSEL_LDO },
{ LDSW_BASE, TASKLDSW2SET, 0x01U },

// Firmware will set up GPIOs as desired; set up everything as an input
// now to avoid drive fights in case it was previously set strangely.
{ GPIOS_BASE, GPIOMODE0, GPIOMODE_GPIINPUT },
{ GPIOS_BASE, GPIOMODE1, GPIOMODE_GPIINPUT },
{ GPIOS_BASE, GPIOMODE2, GPIOMODE_GPIINPUT },
{ GPIOS_BASE, GPIOMODE3, GPIOMODE_GPIINPUT },
{ GPIOS_BASE, GPIOMODE4, GPIOMODE_GPIINPUT },
};

for (unsigned int i = 0; i < sizeof(regs) / sizeof(regs[0]); i++) {
ret = prv_pmic_write(regs[i].base, regs[i].reg, regs[i].val);
if (ret != 0) {
return ret;
}
}

nrfx_twi_disable(&twi);
Expand Down
31 changes: 28 additions & 3 deletions platform/asterix/boot/src/drivers/watchdog.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,43 @@
#include <helpers/nrfx_reset_reason.h>
#include <nrfx.h>

#define WDT_INTERVAL_SECONDS 8

/* On nRF52840, the watchdog can only be disabled by a power-on reset. We
* don't really want the watchdog to be running during the bootloader: if
* the bootloader hangs, there is precious little we can do about it, and we
* don't want the watchdog to interrupt long-running operations like erasing
* microflash, or reading / writing QSPI flash.
*
* The upshot of this is that, even if we are running on a no-watchdog
* build, we must continually kick the watchdog, lest it bite, since the
* watchdog could have been configured from the previous boot!
*/

void watchdog_init(void) {
/* Allow us to be debugged, but keep the WDT ticking when the CPU is
* asleep for normal reasons. This is the reset value, as well, but it's
* always good to be sure before we do anything that we can't take back.
*/
NRF_WDT->CONFIG = (WDT_CONFIG_SLEEP_Run << WDT_CONFIG_SLEEP_Pos) |
(WDT_CONFIG_HALT_Pause << WDT_CONFIG_HALT_Pos);
NRF_WDT->RREN = WDT_RREN_RR0_Enabled << WDT_RREN_RR0_Pos;
/* WDT expiration: 8s */
NRF_WDT->CRV = 32768 * 8;
NRF_WDT->CRV = 32768 * WDT_INTERVAL_SECONDS;
NRF_WDT->TASKS_START = 1;
// NOTE: at this point WDT can no longer be stopped, it will even survive
// a system reset!
}

void watchdog_kick(void) {
if (NRF_WDT->CRV != WDT_CRV_CRV_Msk) {
NRF_WDT->RR[0] = WDT_RR_RR_Reload;
if (NRF_WDT->RUNSTATUS) {
// In theory, only RR0 should be enabled. But in case someone else has
// enabled other RRs out from under us, we had better kick all of them.
for (int i = 0; i < 8; i++) {
if (NRF_WDT->RREN & (1 << i)) {
NRF_WDT->RR[i] = WDT_RR_RR_Reload;
}
}
}
}

Expand Down
6 changes: 6 additions & 0 deletions platform/asterix/boot/src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ static void __attribute__((noreturn)) jump_to_fw(void) {
dbgserial_print_hex((uintptr_t)reset_handler);
dbgserial_print("...\r\n\r\n");

// Give it one last kick to give user firmware a known amount of time to
// start up.
watchdog_kick();

// The Cortex-M user guide states that the reset values for the core registers
// are as follows:
// R0-R12 = Unknown
Expand Down Expand Up @@ -170,6 +174,7 @@ static bool check_force_boot_recovery(void) {
// stop waiting if not held down any longer
return false;
}
watchdog_kick();
delay_ms(1);
}

Expand Down Expand Up @@ -202,6 +207,7 @@ static void sad_watch(uint32_t error_code) {
if (button_state != prev_button_state) {
system_reset();
}
watchdog_kick();
delay_ms(10);
}
}
Expand Down
Loading