mirror of
https://github.com/google/pebble.git
synced 2025-05-27 21:43:12 +00:00
Import of the watch repository from Pebble
This commit is contained in:
commit
3b92768480
10334 changed files with 2564465 additions and 0 deletions
105
platform/robert/boot/src/board/board.h
Normal file
105
platform/robert/boot/src/board/board.h
Normal file
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "display.h"
|
||||
|
||||
#include "drivers/button_id.h"
|
||||
|
||||
#include "stm32f7xx.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define GPIO_Port_NULL ((GPIO_TypeDef *) 0)
|
||||
#define GPIO_Pin_NULL ((uint16_t)0x0000)
|
||||
|
||||
// This is generated in order to faciliate the check within the IRQ_MAP macro below
|
||||
enum {
|
||||
#define IRQ_DEF(num, irq) IS_VALID_IRQ__##irq,
|
||||
#include "irq_stm32f7.def"
|
||||
#undef IRQ_DEF
|
||||
};
|
||||
|
||||
//! Creates a trampoline to the interrupt handler defined within the driver
|
||||
#define IRQ_MAP(irq, handler, device) \
|
||||
void irq##_IRQHandler(void) { \
|
||||
handler(device); \
|
||||
} \
|
||||
_Static_assert(IS_VALID_IRQ__##irq || true, "(See comment below)")
|
||||
/*
|
||||
* The above static assert checks that the requested IRQ is valid by checking that the enum
|
||||
* value (generated above) is declared. The static assert itself will not trip, but you will get
|
||||
* a compilation error from that line if the IRQ does not exist within irq_stm32*.def.
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
GPIO_TypeDef* const gpio; ///< One of GPIOX. For example, GPIOA.
|
||||
const uint32_t gpio_pin; ///< One of GPIO_Pin_X.
|
||||
} InputConfig;
|
||||
|
||||
typedef struct {
|
||||
GPIO_TypeDef* const gpio; ///< One of GPIOX. For example, GPIOA.
|
||||
const uint32_t gpio_pin; ///< One of GPIO_Pin_X.
|
||||
bool active_high; ///< Pin is active high or active low
|
||||
} OutputConfig;
|
||||
|
||||
//! Alternate function pin configuration
|
||||
//! Used to configure a pin for use by a peripheral
|
||||
typedef struct {
|
||||
GPIO_TypeDef* const gpio; ///< One of GPIOX. For example, GPIOA.
|
||||
const uint32_t gpio_pin; ///< One of GPIO_Pin_X.
|
||||
const uint16_t gpio_pin_source; ///< One of GPIO_PinSourceX.
|
||||
const uint8_t gpio_af; ///< One of GPIO_AF_X
|
||||
} AfConfig;
|
||||
|
||||
typedef struct {
|
||||
InputConfig input;
|
||||
GPIOPuPd_TypeDef pupd;
|
||||
} ButtonConfig;
|
||||
|
||||
// Button Configuration
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
typedef struct {
|
||||
const ButtonConfig buttons[NUM_BUTTONS];
|
||||
} BoardConfigButton;
|
||||
|
||||
// Power Configuration
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
typedef struct {
|
||||
//! Voltage rail control lines
|
||||
const OutputConfig rail_4V5_ctrl;
|
||||
const OutputConfig rail_6V6_ctrl;
|
||||
} BoardConfigPower;
|
||||
|
||||
typedef struct {
|
||||
OutputConfig reset_gpio;
|
||||
} BoardConfigFlash;
|
||||
|
||||
typedef struct {
|
||||
const OutputConfig power_en; //< Enable power supply to the accessory connector.
|
||||
} BoardConfigAccessory;
|
||||
|
||||
typedef const struct SPIBus SPIBus;
|
||||
typedef const struct SPISlavePort SPISlavePort;
|
||||
typedef const struct I2CBus I2CBus;
|
||||
typedef const struct I2CSlavePort I2CSlavePort;
|
||||
typedef const struct ICE40LPDevice ICE40LPDevice;
|
||||
|
||||
void board_init(void);
|
||||
|
||||
#include "board_definitions.h"
|
25
platform/robert/boot/src/board/board_definitions.h
Normal file
25
platform/robert/boot/src/board/board_definitions.h
Normal file
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if BOARD_ROBERT_BB || BOARD_ROBERT_BB2
|
||||
#include "board_robert_bb.h"
|
||||
#elif BOARD_ROBERT_EVT
|
||||
#include "board_robert_evt.h"
|
||||
#else
|
||||
#error "Unknown board definition"
|
||||
#endif
|
114
platform/robert/boot/src/board/board_robert_bb.c
Normal file
114
platform/robert/boot/src/board/board_robert_bb.c
Normal file
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "board/board.h"
|
||||
|
||||
#include "drivers/i2c/i2c_definitions.h"
|
||||
#include "drivers/i2c/i2c_hal_definitions.h"
|
||||
#include "drivers/display/ice40lp_definitions.h"
|
||||
#include "util/misc.h"
|
||||
|
||||
//
|
||||
// iCE40LP configuration
|
||||
//
|
||||
|
||||
static ICE40LPDevice ICE40LP_DEVICE = {
|
||||
.spi = {
|
||||
.periph = SPI6,
|
||||
.rcc_bit = RCC_APB2Periph_SPI6,
|
||||
.clk = {
|
||||
.gpio = GPIOA,
|
||||
.gpio_pin = GPIO_Pin_5,
|
||||
.gpio_pin_source = GPIO_PinSource5,
|
||||
.gpio_af = GPIO_AF8_SPI6
|
||||
},
|
||||
.mosi = {
|
||||
.gpio = GPIOA,
|
||||
.gpio_pin = GPIO_Pin_7,
|
||||
.gpio_pin_source = GPIO_PinSource7,
|
||||
.gpio_af = GPIO_AF8_SPI6
|
||||
},
|
||||
.scs = {
|
||||
.gpio = GPIOA,
|
||||
.gpio_pin = GPIO_Pin_4,
|
||||
.active_high = false
|
||||
}
|
||||
},
|
||||
|
||||
.creset = {
|
||||
.gpio = GPIOA,
|
||||
.gpio_pin = GPIO_Pin_3,
|
||||
.active_high = true,
|
||||
},
|
||||
.cdone = {
|
||||
.gpio = GPIOB,
|
||||
.gpio_pin = GPIO_Pin_2,
|
||||
},
|
||||
.busy = {
|
||||
.gpio = GPIOB,
|
||||
.gpio_pin = GPIO_Pin_0,
|
||||
},
|
||||
|
||||
.use_6v6_rail = true,
|
||||
};
|
||||
|
||||
ICE40LPDevice * const ICE40LP = &ICE40LP_DEVICE;
|
||||
|
||||
|
||||
// I2C DEVICES
|
||||
|
||||
static I2CBusState I2C_PMIC_MAG_BUS_STATE = {};
|
||||
static const I2CBusHal I2C_PMIC_MAG_BUS_HAL = {
|
||||
.i2c = I2C4,
|
||||
.clock_ctrl = RCC_APB1Periph_I2C4,
|
||||
.clock_speed = 400000,
|
||||
.duty_cycle = I2CDutyCycle_16_9,
|
||||
.ev_irq_channel = I2C4_EV_IRQn,
|
||||
.er_irq_channel = I2C4_ER_IRQn,
|
||||
};
|
||||
|
||||
static const I2CBus I2C_PMIC_MAG_BUS = {
|
||||
.state = &I2C_PMIC_MAG_BUS_STATE,
|
||||
.hal = &I2C_PMIC_MAG_BUS_HAL,
|
||||
.scl_gpio = {
|
||||
.gpio = GPIOF,
|
||||
.gpio_pin = GPIO_Pin_14,
|
||||
.gpio_pin_source = GPIO_PinSource14,
|
||||
.gpio_af = GPIO_AF4_I2C4
|
||||
},
|
||||
.sda_gpio = {
|
||||
.gpio = GPIOF,
|
||||
.gpio_pin = GPIO_Pin_15,
|
||||
.gpio_pin_source = GPIO_PinSource15,
|
||||
.gpio_af = GPIO_AF4_I2C4
|
||||
},
|
||||
.name = "I2C_PMIC_MAG"
|
||||
};
|
||||
|
||||
static const I2CSlavePort I2C_SLAVE_MAX14690 = {
|
||||
.bus = &I2C_PMIC_MAG_BUS,
|
||||
.address = 0x50
|
||||
};
|
||||
|
||||
I2CSlavePort * const I2C_MAX14690 = &I2C_SLAVE_MAX14690;
|
||||
|
||||
IRQ_MAP(I2C4_EV, i2c_hal_event_irq_handler, &I2C_PMIC_MAG_BUS);
|
||||
IRQ_MAP(I2C4_ER, i2c_hal_error_irq_handler, &I2C_PMIC_MAG_BUS);
|
||||
|
||||
|
||||
void board_init(void) {
|
||||
i2c_init(&I2C_PMIC_MAG_BUS);
|
||||
}
|
132
platform/robert/boot/src/board/board_robert_bb.h
Normal file
132
platform/robert/boot/src/board/board_robert_bb.h
Normal file
|
@ -0,0 +1,132 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// ----------------------------------------------
|
||||
// Board definitions for Robert BB (C2 Bigboard)
|
||||
// ----------------------------------------------
|
||||
//
|
||||
|
||||
#include "util/size.h"
|
||||
|
||||
#define BOARD_LSE_MODE RCC_LSE_Bypass
|
||||
|
||||
static const BoardConfigButton BOARD_CONFIG_BUTTON = {
|
||||
.buttons = {
|
||||
[BUTTON_ID_BACK] = {
|
||||
.input = {
|
||||
.gpio = GPIOG,
|
||||
.gpio_pin = GPIO_Pin_6,
|
||||
},
|
||||
.pupd = GPIO_PuPd_UP
|
||||
},
|
||||
[BUTTON_ID_UP] = {
|
||||
.input = {
|
||||
.gpio = GPIOG,
|
||||
.gpio_pin = GPIO_Pin_3,
|
||||
},
|
||||
.pupd = GPIO_PuPd_NOPULL
|
||||
},
|
||||
[BUTTON_ID_SELECT] = {
|
||||
.input = {
|
||||
.gpio = GPIOG,
|
||||
.gpio_pin = GPIO_Pin_5,
|
||||
},
|
||||
.pupd = GPIO_PuPd_UP
|
||||
},
|
||||
[BUTTON_ID_DOWN] = {
|
||||
.input = {
|
||||
.gpio = GPIOG,
|
||||
.gpio_pin = GPIO_Pin_4,
|
||||
},
|
||||
.pupd = GPIO_PuPd_UP
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static const BoardConfigPower BOARD_CONFIG_POWER = {
|
||||
.rail_4V5_ctrl = {
|
||||
.gpio = GPIOH,
|
||||
.gpio_pin = GPIO_Pin_5,
|
||||
.active_high = true,
|
||||
},
|
||||
.rail_6V6_ctrl = {
|
||||
.gpio = GPIOH,
|
||||
.gpio_pin = GPIO_Pin_3,
|
||||
.active_high = true,
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
static const BoardConfigFlash BOARD_CONFIG_FLASH = {
|
||||
};
|
||||
|
||||
static const BoardConfigAccessory BOARD_CONFIG_ACCESSORY = {
|
||||
.power_en = { GPIOA, GPIO_Pin_11, true },
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
QSpiPin_CS,
|
||||
QSpiPin_SCLK,
|
||||
QSpiPin_DQ0,
|
||||
QSpiPin_DQ1,
|
||||
QSpiPin_DQ2,
|
||||
QSpiPin_DQ3,
|
||||
QSpiPinCount,
|
||||
} QSpiPin;
|
||||
|
||||
static const AfConfig BOARD_CONFIG_FLASH_PINS[] = {
|
||||
[QSpiPin_CS] = {
|
||||
.gpio = GPIOB,
|
||||
.gpio_pin = GPIO_Pin_10,
|
||||
.gpio_pin_source = GPIO_PinSource10,
|
||||
.gpio_af = GPIO_AF9_QUADSPI,
|
||||
},
|
||||
[QSpiPin_SCLK] = {
|
||||
.gpio = GPIOF,
|
||||
.gpio_pin = GPIO_Pin_10,
|
||||
.gpio_pin_source = GPIO_PinSource10,
|
||||
.gpio_af = GPIO_AF9_QUADSPI,
|
||||
},
|
||||
[QSpiPin_DQ0] = {
|
||||
.gpio = GPIOD,
|
||||
.gpio_pin = GPIO_Pin_11,
|
||||
.gpio_pin_source = GPIO_PinSource11,
|
||||
.gpio_af = GPIO_AF9_QUADSPI,
|
||||
},
|
||||
[QSpiPin_DQ1] = {
|
||||
.gpio = GPIOC,
|
||||
.gpio_pin = GPIO_Pin_10,
|
||||
.gpio_pin_source = GPIO_PinSource10,
|
||||
.gpio_af = GPIO_AF9_QUADSPI,
|
||||
},
|
||||
[QSpiPin_DQ2] = {
|
||||
.gpio = GPIOF,
|
||||
.gpio_pin = GPIO_Pin_7,
|
||||
.gpio_pin_source = GPIO_PinSource7,
|
||||
.gpio_af = GPIO_AF9_QUADSPI,
|
||||
},
|
||||
[QSpiPin_DQ3] = {
|
||||
.gpio = GPIOA,
|
||||
.gpio_pin = GPIO_Pin_1,
|
||||
.gpio_pin_source = GPIO_PinSource1,
|
||||
.gpio_af = GPIO_AF9_QUADSPI,
|
||||
},
|
||||
};
|
||||
|
||||
extern I2CSlavePort * const I2C_MAX14690;
|
||||
extern ICE40LPDevice * const ICE40LP;
|
114
platform/robert/boot/src/board/board_robert_evt.c
Normal file
114
platform/robert/boot/src/board/board_robert_evt.c
Normal file
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "board/board.h"
|
||||
|
||||
#include "drivers/i2c/i2c_definitions.h"
|
||||
#include "drivers/i2c/i2c_hal_definitions.h"
|
||||
#include "drivers/display/ice40lp_definitions.h"
|
||||
#include "util/misc.h"
|
||||
|
||||
//
|
||||
// iCE40LP configuration
|
||||
//
|
||||
|
||||
static ICE40LPDevice ICE40LP_DEVICE = {
|
||||
.spi = {
|
||||
.periph = SPI6,
|
||||
.rcc_bit = RCC_APB2Periph_SPI6,
|
||||
.clk = {
|
||||
.gpio = GPIOA,
|
||||
.gpio_pin = GPIO_Pin_5,
|
||||
.gpio_pin_source = GPIO_PinSource5,
|
||||
.gpio_af = GPIO_AF8_SPI6
|
||||
},
|
||||
.mosi = {
|
||||
.gpio = GPIOA,
|
||||
.gpio_pin = GPIO_Pin_7,
|
||||
.gpio_pin_source = GPIO_PinSource7,
|
||||
.gpio_af = GPIO_AF8_SPI6
|
||||
},
|
||||
.scs = {
|
||||
.gpio = GPIOA,
|
||||
.gpio_pin = GPIO_Pin_4,
|
||||
.active_high = false
|
||||
}
|
||||
},
|
||||
|
||||
.creset = {
|
||||
.gpio = GPIOA,
|
||||
.gpio_pin = GPIO_Pin_3,
|
||||
.active_high = true,
|
||||
},
|
||||
.cdone = {
|
||||
.gpio = GPIOB,
|
||||
.gpio_pin = GPIO_Pin_2,
|
||||
},
|
||||
.busy = {
|
||||
.gpio = GPIOB,
|
||||
.gpio_pin = GPIO_Pin_0,
|
||||
},
|
||||
|
||||
.use_6v6_rail = false,
|
||||
};
|
||||
|
||||
ICE40LPDevice * const ICE40LP = &ICE40LP_DEVICE;
|
||||
|
||||
|
||||
// I2C DEVICES
|
||||
|
||||
static I2CBusState I2C_PMIC_MAG_BUS_STATE = {};
|
||||
static const I2CBusHal I2C_PMIC_MAG_BUS_HAL = {
|
||||
.i2c = I2C4,
|
||||
.clock_ctrl = RCC_APB1Periph_I2C4,
|
||||
.clock_speed = 400000,
|
||||
.duty_cycle = I2CDutyCycle_16_9,
|
||||
.ev_irq_channel = I2C4_EV_IRQn,
|
||||
.er_irq_channel = I2C4_ER_IRQn,
|
||||
};
|
||||
|
||||
static const I2CBus I2C_PMIC_MAG_BUS = {
|
||||
.state = &I2C_PMIC_MAG_BUS_STATE,
|
||||
.hal = &I2C_PMIC_MAG_BUS_HAL,
|
||||
.scl_gpio = {
|
||||
.gpio = GPIOF,
|
||||
.gpio_pin = GPIO_Pin_14,
|
||||
.gpio_pin_source = GPIO_PinSource14,
|
||||
.gpio_af = GPIO_AF4_I2C4
|
||||
},
|
||||
.sda_gpio = {
|
||||
.gpio = GPIOF,
|
||||
.gpio_pin = GPIO_Pin_15,
|
||||
.gpio_pin_source = GPIO_PinSource15,
|
||||
.gpio_af = GPIO_AF4_I2C4
|
||||
},
|
||||
.name = "I2C_PMIC_MAG"
|
||||
};
|
||||
|
||||
static const I2CSlavePort I2C_SLAVE_MAX14690 = {
|
||||
.bus = &I2C_PMIC_MAG_BUS,
|
||||
.address = 0x50
|
||||
};
|
||||
|
||||
I2CSlavePort * const I2C_MAX14690 = &I2C_SLAVE_MAX14690;
|
||||
|
||||
IRQ_MAP(I2C4_EV, i2c_hal_event_irq_handler, &I2C_PMIC_MAG_BUS);
|
||||
IRQ_MAP(I2C4_ER, i2c_hal_error_irq_handler, &I2C_PMIC_MAG_BUS);
|
||||
|
||||
|
||||
void board_init(void) {
|
||||
i2c_init(&I2C_PMIC_MAG_BUS);
|
||||
}
|
133
platform/robert/boot/src/board/board_robert_evt.h
Normal file
133
platform/robert/boot/src/board/board_robert_evt.h
Normal file
|
@ -0,0 +1,133 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// ----------------------------------------------
|
||||
// Board definitions for Robert EVT
|
||||
// ----------------------------------------------
|
||||
//
|
||||
|
||||
#include "util/size.h"
|
||||
|
||||
#define BOARD_LSE_MODE RCC_LSE_Bypass
|
||||
|
||||
static const BoardConfigButton BOARD_CONFIG_BUTTON = {
|
||||
.buttons = {
|
||||
[BUTTON_ID_BACK] = {
|
||||
.input = {
|
||||
.gpio = GPIOG,
|
||||
.gpio_pin = GPIO_Pin_3,
|
||||
},
|
||||
.pupd = GPIO_PuPd_NOPULL,
|
||||
},
|
||||
[BUTTON_ID_UP] = {
|
||||
.input = {
|
||||
.gpio = GPIOG,
|
||||
.gpio_pin = GPIO_Pin_4,
|
||||
},
|
||||
.pupd = GPIO_PuPd_UP,
|
||||
},
|
||||
[BUTTON_ID_SELECT] = {
|
||||
.input = {
|
||||
.gpio = GPIOG,
|
||||
.gpio_pin = GPIO_Pin_5,
|
||||
},
|
||||
.pupd = GPIO_PuPd_UP,
|
||||
},
|
||||
[BUTTON_ID_DOWN] = {
|
||||
.input = {
|
||||
.gpio = GPIOG,
|
||||
.gpio_pin = GPIO_Pin_6,
|
||||
},
|
||||
.pupd = GPIO_PuPd_UP,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static const BoardConfigPower BOARD_CONFIG_POWER = {
|
||||
.rail_4V5_ctrl = {
|
||||
.gpio = GPIOH,
|
||||
.gpio_pin = GPIO_Pin_5,
|
||||
.active_high = true,
|
||||
},
|
||||
|
||||
.rail_6V6_ctrl = { GPIO_Port_NULL },
|
||||
};
|
||||
|
||||
static const BoardConfigFlash BOARD_CONFIG_FLASH = {
|
||||
.reset_gpio = {
|
||||
.gpio = GPIOE,
|
||||
.gpio_pin = GPIO_Pin_15,
|
||||
.active_high = false,
|
||||
},
|
||||
};
|
||||
|
||||
static const BoardConfigAccessory BOARD_CONFIG_ACCESSORY = {
|
||||
.power_en = { GPIOD, GPIO_Pin_2, true },
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
QSpiPin_CS,
|
||||
QSpiPin_SCLK,
|
||||
QSpiPin_DQ0,
|
||||
QSpiPin_DQ1,
|
||||
QSpiPin_DQ2,
|
||||
QSpiPin_DQ3,
|
||||
QSpiPinCount,
|
||||
} QSpiPin;
|
||||
|
||||
static const AfConfig BOARD_CONFIG_FLASH_PINS[] = {
|
||||
[QSpiPin_CS] = {
|
||||
.gpio = GPIOB,
|
||||
.gpio_pin = GPIO_Pin_10,
|
||||
.gpio_pin_source = GPIO_PinSource10,
|
||||
.gpio_af = GPIO_AF9_QUADSPI,
|
||||
},
|
||||
[QSpiPin_SCLK] = {
|
||||
.gpio = GPIOF,
|
||||
.gpio_pin = GPIO_Pin_10,
|
||||
.gpio_pin_source = GPIO_PinSource10,
|
||||
.gpio_af = GPIO_AF9_QUADSPI,
|
||||
},
|
||||
[QSpiPin_DQ0] = {
|
||||
.gpio = GPIOD,
|
||||
.gpio_pin = GPIO_Pin_11,
|
||||
.gpio_pin_source = GPIO_PinSource11,
|
||||
.gpio_af = GPIO_AF9_QUADSPI,
|
||||
},
|
||||
[QSpiPin_DQ1] = {
|
||||
.gpio = GPIOC,
|
||||
.gpio_pin = GPIO_Pin_10,
|
||||
.gpio_pin_source = GPIO_PinSource10,
|
||||
.gpio_af = GPIO_AF9_QUADSPI,
|
||||
},
|
||||
[QSpiPin_DQ2] = {
|
||||
.gpio = GPIOE,
|
||||
.gpio_pin = GPIO_Pin_2,
|
||||
.gpio_pin_source = GPIO_PinSource2,
|
||||
.gpio_af = GPIO_AF9_QUADSPI,
|
||||
},
|
||||
[QSpiPin_DQ3] = {
|
||||
.gpio = GPIOA,
|
||||
.gpio_pin = GPIO_Pin_1,
|
||||
.gpio_pin_source = GPIO_PinSource1,
|
||||
.gpio_af = GPIO_AF9_QUADSPI,
|
||||
},
|
||||
};
|
||||
|
||||
extern I2CSlavePort * const I2C_MAX14690;
|
||||
extern ICE40LPDevice * const ICE40LP;
|
33
platform/robert/boot/src/board/display.h
Normal file
33
platform/robert/boot/src/board/display.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#define DISP_COLS 200
|
||||
#define DISP_ROWS 228
|
||||
|
||||
#define DISPLAY_FRAMEBUFFER_BYTES (DISP_COLS * DISP_ROWS)
|
||||
|
||||
#define DISPLAY_ORIENTATION_COLUMN_MAJOR_INVERTED 0
|
||||
#define DISPLAY_ORIENTATION_ROTATED_180 0
|
||||
#define DISPLAY_ORIENTATION_ROW_MAJOR 0
|
||||
#define DISPLAY_ORIENTATION_ROW_MAJOR_INVERTED 1
|
||||
|
||||
#define PBL_BW 0
|
||||
#define PBL_RECT 1
|
||||
|
||||
#define PBL_ROUND 0
|
||||
#define PBL_COLOR 1
|
80
platform/robert/boot/src/boot_tests.c
Normal file
80
platform/robert/boot/src/boot_tests.c
Normal file
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "boot_tests.h"
|
||||
|
||||
#include "board/board.h"
|
||||
#include "drivers/button.h"
|
||||
#include "drivers/dbgserial.h"
|
||||
#include "drivers/flash.h"
|
||||
#include "system/bootbits.h"
|
||||
#include "system/rtc_registers.h"
|
||||
#include "util/misc.h"
|
||||
|
||||
#include "stm32f7xx.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
static const int STUCK_BUTTON_THRESHOLD = 5;
|
||||
|
||||
bool boot_test_is_button_stuck(void) {
|
||||
// We store how many times each button has been pressed on previous boots in this
|
||||
// rtc backup register. Every time when we boot without that button pressed that
|
||||
// counter gets cleared. If the byte reaches 5, return a failure.
|
||||
|
||||
uint32_t button_counter_register = RTC_ReadBackupRegister(STUCK_BUTTON_REGISTER);
|
||||
uint8_t* button_counter = (uint8_t*) (&button_counter_register);
|
||||
bool result = false;
|
||||
|
||||
for (int button_id = 0; button_id < NUM_BUTTONS; button_id++) {
|
||||
if (!button_is_pressed(button_id)) {
|
||||
button_counter[button_id] = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (button_counter[button_id] >= STUCK_BUTTON_THRESHOLD) {
|
||||
dbgserial_putstr("Stuck button register is invalid, clearing.");
|
||||
dbgserial_print_hex(button_counter_register);
|
||||
|
||||
RTC_WriteBackupRegister(STUCK_BUTTON_REGISTER, 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
button_counter[button_id] += 1;
|
||||
|
||||
if (button_counter[button_id] >= STUCK_BUTTON_THRESHOLD) {
|
||||
dbgserial_print("Button id ");
|
||||
dbgserial_print_hex(button_id);
|
||||
dbgserial_putstr("is stuck!");
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (button_counter_register != 0) {
|
||||
dbgserial_print("Button was pushed on boot. Button counter: ");
|
||||
dbgserial_print_hex(button_counter_register);
|
||||
dbgserial_newline();
|
||||
}
|
||||
|
||||
RTC_WriteBackupRegister(STUCK_BUTTON_REGISTER, button_counter_register);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool boot_test_is_flash_broken(void) {
|
||||
return !flash_sanity_check();
|
||||
}
|
22
platform/robert/boot/src/boot_tests.h
Normal file
22
platform/robert/boot/src/boot_tests.h
Normal file
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
bool boot_test_is_button_stuck(void);
|
||||
bool boot_test_is_flash_broken(void);
|
49
platform/robert/boot/src/drivers/button.c
Normal file
49
platform/robert/boot/src/drivers/button.c
Normal file
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "drivers/button.h"
|
||||
|
||||
#include "board/board.h"
|
||||
#include "drivers/periph_config.h"
|
||||
#include "drivers/gpio.h"
|
||||
|
||||
static void prv_initialize_button(const ButtonConfig* config) {
|
||||
// Configure the pin itself
|
||||
gpio_input_init_pull_up_down(&config->input, config->pupd);
|
||||
}
|
||||
|
||||
bool button_is_pressed(ButtonId id) {
|
||||
const ButtonConfig* button_config = &BOARD_CONFIG_BUTTON.buttons[id];
|
||||
return !gpio_input_read(&button_config->input);
|
||||
}
|
||||
|
||||
uint8_t button_get_state_bits(void) {
|
||||
uint8_t button_state = 0x00;
|
||||
for (int i = 0; i < NUM_BUTTONS; ++i) {
|
||||
button_state |= (button_is_pressed(i) ? 0x01 : 0x00) << i;
|
||||
}
|
||||
return button_state;
|
||||
}
|
||||
|
||||
void button_init(void) {
|
||||
periph_config_acquire_lock();
|
||||
|
||||
for (int i = 0; i < NUM_BUTTONS; ++i) {
|
||||
prv_initialize_button(&BOARD_CONFIG_BUTTON.buttons[i]);
|
||||
}
|
||||
|
||||
periph_config_release_lock();
|
||||
}
|
27
platform/robert/boot/src/drivers/button.h
Normal file
27
platform/robert/boot/src/drivers/button.h
Normal file
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "button_id.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
void button_init(void);
|
||||
|
||||
bool button_is_pressed(ButtonId id);
|
||||
uint8_t button_get_state_bits(void);
|
41
platform/robert/boot/src/drivers/button_id.h
Normal file
41
platform/robert/boot/src/drivers/button_id.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
//! @addtogroup UI
|
||||
//! @{
|
||||
//! @addtogroup Clicks
|
||||
//! \brief Dealing with button input
|
||||
//! @{
|
||||
|
||||
//! Button ID values
|
||||
//! @see \ref click_recognizer_get_button_id()
|
||||
typedef enum {
|
||||
//! Back button
|
||||
BUTTON_ID_BACK = 0,
|
||||
//! Up button
|
||||
BUTTON_ID_UP,
|
||||
//! Select (middle) button
|
||||
BUTTON_ID_SELECT,
|
||||
//! Down button
|
||||
BUTTON_ID_DOWN,
|
||||
//! Total number of buttons
|
||||
NUM_BUTTONS
|
||||
} ButtonId;
|
||||
|
||||
//! @} // end addtogroup Clicks
|
||||
//! @} // end addtogroup UI
|
179
platform/robert/boot/src/drivers/dbgserial.c
Normal file
179
platform/robert/boot/src/drivers/dbgserial.c
Normal file
|
@ -0,0 +1,179 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "dbgserial.h"
|
||||
|
||||
#include "drivers/gpio.h"
|
||||
#include "drivers/periph_config.h"
|
||||
#include "stm32f7haxx_rcc.h"
|
||||
#include "stm32f7haxx_gpio.h"
|
||||
#include "util/attributes.h"
|
||||
#include "util/cobs.h"
|
||||
#include "util/crc32.h"
|
||||
#include "util/net.h"
|
||||
#include "util/misc.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#define MAX_MESSAGE (256)
|
||||
#define FRAME_DELIMITER '\x55'
|
||||
#define PULSE_TRANSPORT_PUSH (0x5021)
|
||||
#define PULSE_PROTOCOL_LOGGING (0x0003)
|
||||
|
||||
static const int SERIAL_BAUD_RATE = 1000000;
|
||||
static USART_TypeDef *const DBGSERIAL_UART = USART3;
|
||||
|
||||
typedef struct PACKED PulseFrame {
|
||||
net16 protocol;
|
||||
unsigned char information[];
|
||||
} PulseFrame;
|
||||
|
||||
typedef struct PACKED PushPacket {
|
||||
net16 protocol;
|
||||
net16 length;
|
||||
unsigned char information[];
|
||||
} PushPacket;
|
||||
|
||||
static const unsigned char s_message_header[] = {
|
||||
// Message type: text
|
||||
1,
|
||||
// Source filename
|
||||
'B', 'O', 'O', 'T', 'L', 'O', 'A', 'D', 'E', 'R', 0, 0, 0, 0, 0, 0,
|
||||
// Log level and task
|
||||
'*', '*',
|
||||
// Timestamp
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
// Line number
|
||||
0, 0,
|
||||
};
|
||||
|
||||
static size_t s_message_length = 0;
|
||||
static unsigned char s_message_buffer[MAX_MESSAGE];
|
||||
|
||||
void dbgserial_init(void) {
|
||||
// Enable GPIO and UART3 peripheral clocks
|
||||
periph_config_enable(GPIOD, RCC_AHB1Periph_GPIOD);
|
||||
periph_config_enable(DBGSERIAL_UART, RCC_APB1Periph_USART3);
|
||||
|
||||
DBGSERIAL_UART->CR1 &= ~USART_CR1_UE;
|
||||
|
||||
AfConfig tx_cfg = {
|
||||
.gpio = GPIOD,
|
||||
.gpio_pin = GPIO_Pin_8,
|
||||
.gpio_pin_source = GPIO_PinSource8,
|
||||
.gpio_af = GPIO_AF7_USART3
|
||||
};
|
||||
|
||||
gpio_af_init(&tx_cfg, GPIO_OType_PP, GPIO_Speed_50MHz, GPIO_PuPd_NOPULL);
|
||||
|
||||
AfConfig rx_cfg = {
|
||||
.gpio = GPIOD,
|
||||
.gpio_pin = GPIO_Pin_9,
|
||||
.gpio_pin_source = GPIO_PinSource9,
|
||||
.gpio_af = GPIO_AF7_USART3
|
||||
};
|
||||
|
||||
gpio_af_init(&rx_cfg, GPIO_OType_PP, GPIO_Speed_50MHz, GPIO_PuPd_NOPULL);
|
||||
|
||||
// configure the UART peripheral control registers and baud rate
|
||||
// - 8-bit word length
|
||||
// - no parity
|
||||
// - RX / TX enabled
|
||||
// - 1 stop bit
|
||||
// - no flow control
|
||||
|
||||
const int k_div_precision = 100;
|
||||
|
||||
RCC_ClocksTypeDef clocks;
|
||||
RCC_GetClocksFreq(&clocks);
|
||||
|
||||
// calculate the baud rate value
|
||||
const uint64_t scaled_apbclock = k_div_precision * clocks.PCLK1_Frequency;
|
||||
const uint32_t div = (scaled_apbclock / SERIAL_BAUD_RATE);
|
||||
const uint32_t brr = (div & 0xFFF0) | ((div & 0xF) >> 1);
|
||||
|
||||
DBGSERIAL_UART->BRR = brr / k_div_precision;
|
||||
DBGSERIAL_UART->CR2 = 0;
|
||||
DBGSERIAL_UART->CR3 = 0;
|
||||
DBGSERIAL_UART->CR1 = USART_CR1_RE | USART_CR1_TE | USART_CR1_UE;
|
||||
}
|
||||
|
||||
static void prv_putchar(uint8_t c) {
|
||||
while ((DBGSERIAL_UART->ISR & USART_ISR_TXE) == 0) continue;
|
||||
DBGSERIAL_UART->TDR = c;
|
||||
while ((DBGSERIAL_UART->ISR & USART_ISR_TXE) == 0) continue;
|
||||
}
|
||||
|
||||
void dbgserial_print(const char* str) {
|
||||
for (; *str && s_message_length < MAX_MESSAGE; ++str) {
|
||||
if (*str == '\n') {
|
||||
dbgserial_newline();
|
||||
} else if (*str != '\r') {
|
||||
s_message_buffer[s_message_length++] = *str;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void dbgserial_newline(void) {
|
||||
uint32_t crc;
|
||||
size_t raw_length = sizeof(PulseFrame) + sizeof(PushPacket) +
|
||||
sizeof(s_message_header) + s_message_length + sizeof(crc);
|
||||
unsigned char raw_packet[raw_length];
|
||||
|
||||
PulseFrame *frame = (PulseFrame *)raw_packet;
|
||||
frame->protocol = hton16(PULSE_TRANSPORT_PUSH);
|
||||
|
||||
PushPacket *transport = (PushPacket *)frame->information;
|
||||
transport->protocol = hton16(PULSE_PROTOCOL_LOGGING);
|
||||
transport->length = hton16(sizeof(PushPacket) + sizeof(s_message_header) +
|
||||
s_message_length);
|
||||
|
||||
unsigned char *app = transport->information;
|
||||
memcpy(app, s_message_header, sizeof(s_message_header));
|
||||
memcpy(&app[sizeof(s_message_header)], s_message_buffer,
|
||||
s_message_length);
|
||||
|
||||
crc = crc32(CRC32_INIT, raw_packet, raw_length - sizeof(crc));
|
||||
memcpy(&raw_packet[raw_length - sizeof(crc)], &crc, sizeof(crc));
|
||||
|
||||
unsigned char cooked_packet[MAX_SIZE_AFTER_COBS_ENCODING(raw_length)];
|
||||
size_t cooked_length = cobs_encode(cooked_packet, raw_packet, raw_length);
|
||||
|
||||
prv_putchar(FRAME_DELIMITER);
|
||||
for (size_t i = 0; i < cooked_length; ++i) {
|
||||
if (cooked_packet[i] == FRAME_DELIMITER) {
|
||||
prv_putchar('\0');
|
||||
} else {
|
||||
prv_putchar(cooked_packet[i]);
|
||||
}
|
||||
}
|
||||
prv_putchar(FRAME_DELIMITER);
|
||||
|
||||
s_message_length = 0;
|
||||
}
|
||||
|
||||
void dbgserial_putstr(const char* str) {
|
||||
dbgserial_print(str);
|
||||
|
||||
dbgserial_newline();
|
||||
}
|
||||
|
||||
void dbgserial_print_hex(uint32_t value) {
|
||||
char buf[12];
|
||||
itoa_hex(value, buf, sizeof(buf));
|
||||
dbgserial_print(buf);
|
||||
}
|
34
platform/robert/boot/src/drivers/dbgserial.h
Normal file
34
platform/robert/boot/src/drivers/dbgserial.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
|
||||
void dbgserial_init(void);
|
||||
|
||||
void dbgserial_putstr(const char* str);
|
||||
|
||||
void dbgserial_newline(void);
|
||||
|
||||
//! Like dbgserial_putstr, but without a terminating newline
|
||||
void dbgserial_print(const char* str);
|
||||
|
||||
void dbgserial_print_hex(uint32_t value);
|
||||
|
||||
void dbgserial_putstr_fmt(char* buffer, unsigned int buffer_size, const char* fmt, ...)
|
||||
__attribute__((format(printf, 3, 4)));
|
34
platform/robert/boot/src/drivers/display.h
Normal file
34
platform/robert/boot/src/drivers/display.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
void display_init(void);
|
||||
|
||||
void display_boot_splash(void);
|
||||
|
||||
void display_error_code(uint32_t);
|
||||
|
||||
//! Do whatever is necessary to prevent visual artifacts when resetting
|
||||
//! the watch.
|
||||
void display_prepare_for_reset(void);
|
||||
|
||||
//! Display the progress of a firmware update.
|
||||
//!
|
||||
//! The progress is expressed as a rational number less than or equal to 1.
|
||||
//! When numerator == denominator, the progress indicator shows that the update
|
||||
//! is complete.
|
||||
void display_firmware_update_progress(uint32_t numerator, uint32_t denominator);
|
210
platform/robert/boot/src/drivers/display/boot_fpga.c
Normal file
210
platform/robert/boot/src/drivers/display/boot_fpga.c
Normal file
|
@ -0,0 +1,210 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "drivers/display.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "board/board.h"
|
||||
#include "drivers/dbgserial.h"
|
||||
#include "drivers/display/bootloader_fpga_bitstream.auto.h"
|
||||
#include "drivers/display/ice40lp.h"
|
||||
#include "drivers/display/ice40lp_definitions.h"
|
||||
#include "system/passert.h"
|
||||
#include "util/delay.h"
|
||||
#include "util/misc.h"
|
||||
#include "util/sle.h"
|
||||
|
||||
#define CMD_NULL (0)
|
||||
#define CMD_SET_PARAMETER (1)
|
||||
#define CMD_DISPLAY_OFF (2)
|
||||
#define CMD_DISPLAY_ON (3)
|
||||
#define CMD_DRAW_SCENE (4)
|
||||
#define CMD_RESET_RELEASE (8)
|
||||
#define CMD_RESET_ASSERT (9)
|
||||
|
||||
#define SCENE_BLACK (0)
|
||||
#define SCENE_SPLASH (1)
|
||||
#define SCENE_UPDATE (2)
|
||||
#define SCENE_ERROR (3)
|
||||
|
||||
#define UPDATE_PROGRESS_MAX (47)
|
||||
|
||||
static uint8_t s_decoded_fpga_image[35000]; // the FPGA image is currently ~30k
|
||||
|
||||
static bool prv_reset_fpga(void) {
|
||||
const uint32_t length = sle_decode(s_fpga_bitstream, sizeof(s_fpga_bitstream),
|
||||
s_decoded_fpga_image, sizeof(s_decoded_fpga_image));
|
||||
return display_program(s_decoded_fpga_image, length);
|
||||
}
|
||||
|
||||
static bool prv_wait_busy(void) {
|
||||
// The display should come out of busy within 35 milliseconds;
|
||||
// it is a waste of time to wait more than twice that.
|
||||
int timeout = 50 * 10;
|
||||
while (display_busy()) {
|
||||
if (timeout-- == 0) {
|
||||
dbgserial_putstr("Display busy-wait timeout expired!");
|
||||
return false;
|
||||
}
|
||||
delay_us(100);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void prv_screen_on(void) {
|
||||
display_write_cmd(CMD_DISPLAY_ON, NULL, 0);
|
||||
}
|
||||
|
||||
static void prv_screen_off(void) {
|
||||
display_write_cmd(CMD_DISPLAY_OFF, NULL, 0);
|
||||
}
|
||||
|
||||
static void prv_draw_scene(uint8_t scene) {
|
||||
display_write_cmd(CMD_DRAW_SCENE, &scene, sizeof(scene));
|
||||
}
|
||||
|
||||
static void prv_set_parameter(uint32_t param) {
|
||||
display_write_cmd(CMD_SET_PARAMETER, (uint8_t *)¶m, sizeof(param));
|
||||
}
|
||||
|
||||
#ifdef DISPLAY_DEMO_LOOP
|
||||
static void prv_play_demo_loop(void) {
|
||||
while (1) {
|
||||
for (int i = 0; i <= UPDATE_PROGRESS_MAX; ++i) {
|
||||
display_firmware_update_progress(i, UPDATE_PROGRESS_MAX);
|
||||
delay_ms(80);
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i <= 0xf; ++i) {
|
||||
display_error_code(i * 0x11111111);
|
||||
delay_ms(200);
|
||||
}
|
||||
for (uint32_t i = 0; i < 8; ++i) {
|
||||
for (uint32_t j = 1; j <= 0xf; ++j) {
|
||||
display_error_code(j << (i * 4));
|
||||
delay_ms(200);
|
||||
}
|
||||
}
|
||||
display_error_code(0x01234567);
|
||||
delay_ms(200);
|
||||
display_error_code(0x89abcdef);
|
||||
delay_ms(200);
|
||||
display_error_code(0xcafebabe);
|
||||
delay_ms(200);
|
||||
display_error_code(0xfeedface);
|
||||
delay_ms(200);
|
||||
display_error_code(0x8badf00d);
|
||||
delay_ms(200);
|
||||
display_error_code(0xbad1ce40);
|
||||
delay_ms(200);
|
||||
display_error_code(0xbeefcace);
|
||||
delay_ms(200);
|
||||
display_error_code(0x0defaced);
|
||||
delay_ms(200);
|
||||
display_error_code(0xd15ea5e5);
|
||||
delay_ms(200);
|
||||
display_error_code(0xdeadbeef);
|
||||
delay_ms(200);
|
||||
display_boot_splash();
|
||||
delay_ms(1000);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void display_init(void) {
|
||||
display_start();
|
||||
if (!prv_reset_fpga()) {
|
||||
dbgserial_putstr("FPGA configuration failed.");
|
||||
return;
|
||||
}
|
||||
|
||||
// enable the power rails
|
||||
display_power_enable();
|
||||
|
||||
// start with the screen off
|
||||
prv_screen_off();
|
||||
|
||||
// Work around an issue which some boards exhibit where the FPGA ring
|
||||
// oscillator can start up with higher harmonics, massively overclocking the
|
||||
// design and causing malfunction. When this occurrs, the draw-scene command
|
||||
// will not work, asserting BUSY indefinitely but never updating the display.
|
||||
// Other commands such as display-on and display-off are less affected by the
|
||||
// overclocking, so the display can be turned on while the FPGA is in this
|
||||
// state, showing only garbage.
|
||||
// FPGA malfunction can be detected in software. In an attempt to restore
|
||||
// proper functioning, the FPGA can be reset and reconfigured in the hopes
|
||||
// that the ring oscillator will start up and oscillate without any higher
|
||||
// harmonics. Bootloader release 03 attempts to mitigate this problem by
|
||||
// delaying oscillator startup until after configuration completes. Time will
|
||||
// tell whether this actually fixes things.
|
||||
for (int retries = 0; retries <= 10; ++retries) {
|
||||
prv_draw_scene(SCENE_SPLASH);
|
||||
if (prv_wait_busy()) {
|
||||
prv_screen_on();
|
||||
dbgserial_print("Display initialized after ");
|
||||
dbgserial_print_hex(retries);
|
||||
dbgserial_putstr(" retries.");
|
||||
#ifdef DISPLAY_DEMO_LOOP
|
||||
prv_play_demo_loop();
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
if (!prv_reset_fpga()) {
|
||||
dbgserial_putstr("FPGA configuration failed.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// It's taken too many attempts and the FPGA still isn't behaving. Give up on
|
||||
// showing the splash screen and keep the screen off so that the user doesn't
|
||||
// see a broken-looking staticky screen on boot.
|
||||
dbgserial_putstr("Display initialization failed.");
|
||||
prv_screen_off();
|
||||
}
|
||||
|
||||
void display_boot_splash(void) {
|
||||
prv_wait_busy();
|
||||
prv_draw_scene(SCENE_SPLASH);
|
||||
// Don't turn the screen on until the boot-splash is fully drawn.
|
||||
prv_wait_busy();
|
||||
prv_screen_on();
|
||||
}
|
||||
|
||||
void display_firmware_update_progress(
|
||||
uint32_t numerator, uint32_t denominator) {
|
||||
static uint8_t last_bar_fill = UINT8_MAX;
|
||||
// Scale progress to the number of pixels in the progress bar,
|
||||
// rounding half upwards.
|
||||
uint8_t bar_fill =
|
||||
((numerator * UPDATE_PROGRESS_MAX) + ((denominator+1)/2)) / denominator;
|
||||
// Don't waste time and power redrawing the same screen repeatedly.
|
||||
if (bar_fill != last_bar_fill) {
|
||||
last_bar_fill = bar_fill;
|
||||
prv_set_parameter(bar_fill);
|
||||
prv_draw_scene(SCENE_UPDATE);
|
||||
}
|
||||
}
|
||||
|
||||
void display_error_code(uint32_t error_code) {
|
||||
prv_set_parameter(error_code);
|
||||
prv_draw_scene(SCENE_ERROR);
|
||||
}
|
||||
|
||||
void display_prepare_for_reset(void) {
|
||||
prv_screen_off();
|
||||
}
|
186
platform/robert/boot/src/drivers/display/ice40lp.c
Normal file
186
platform/robert/boot/src/drivers/display/ice40lp.c
Normal file
|
@ -0,0 +1,186 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "ice40lp.h"
|
||||
|
||||
#include "board/board.h"
|
||||
#include "drivers/dbgserial.h"
|
||||
#include "drivers/display/ice40lp_definitions.h"
|
||||
#include "drivers/gpio.h"
|
||||
#include "drivers/periph_config.h"
|
||||
#include "drivers/pmic.h"
|
||||
#include "system/logging.h"
|
||||
#include "system/passert.h"
|
||||
#include "util/delay.h"
|
||||
|
||||
#include "stm32f7xx.h"
|
||||
#include "misc.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
|
||||
|
||||
static void prv_spi_init(void) {
|
||||
// Configure the GPIO (SCLK, MOSI - no MISO since the SPI is TX-only)
|
||||
gpio_af_init(&ICE40LP->spi.clk, GPIO_OType_PP, GPIO_Speed_25MHz, GPIO_PuPd_NOPULL);
|
||||
gpio_af_init(&ICE40LP->spi.mosi, GPIO_OType_PP, GPIO_Speed_25MHz, GPIO_PuPd_NOPULL);
|
||||
|
||||
// Reset the SPI peripheral and enable the clock
|
||||
RCC_APB2PeriphResetCmd(ICE40LP->spi.rcc_bit, ENABLE);
|
||||
RCC_APB2PeriphResetCmd(ICE40LP->spi.rcc_bit, DISABLE);
|
||||
periph_config_enable(ICE40LP->spi.periph, ICE40LP->spi.rcc_bit);
|
||||
|
||||
// Configure CR1 first:
|
||||
// * TX-only mode (BIDIMODE | BIDIOE)
|
||||
// * software control NSS pin (SSM | SSI)
|
||||
// * master mode (MSTR)
|
||||
// * clock polarity high / 2nd edge (CPOL | CPHA)
|
||||
ICE40LP->spi.periph->CR1 = SPI_CR1_BIDIMODE | SPI_CR1_BIDIOE | SPI_CR1_SSM | SPI_CR1_SSI |
|
||||
SPI_CR1_MSTR | SPI_CR1_CPOL | SPI_CR1_CPHA;
|
||||
|
||||
// Configure CR2:
|
||||
// * 8-bit data size (DS[4:0] == 0b0111)
|
||||
// * 1/4 RX threshold (for 8-bit transfers)
|
||||
ICE40LP->spi.periph->CR2 = SPI_CR2_DS_0 | SPI_CR2_DS_1 | SPI_CR2_DS_2 | SPI_CR2_FRXTH;
|
||||
|
||||
// enable the SPI
|
||||
ICE40LP->spi.periph->CR1 |= SPI_CR1_SPE;
|
||||
}
|
||||
|
||||
static void prv_spi_write(const uint8_t *data, uint32_t len) {
|
||||
for (uint32_t i = 0; i < len; ++i) {
|
||||
// Wait until we can transmit.
|
||||
while (!(ICE40LP->spi.periph->SR & SPI_SR_TXE)) continue;
|
||||
|
||||
// Write a byte. STM32F7 needs to access as 8 bits in order to actually do 8 bits.
|
||||
*(volatile uint8_t*)&ICE40LP->spi.periph->DR = data[i];
|
||||
}
|
||||
|
||||
// Wait until the TX FIFO is empty plus an extra little bit for the shift-register.
|
||||
while (((ICE40LP->spi.periph->SR & SPI_SR_FTLVL) >> __builtin_ctz(SPI_SR_FTLVL)) > 0) continue;
|
||||
delay_us(10);
|
||||
}
|
||||
|
||||
bool display_busy(void) {
|
||||
return gpio_input_read(&ICE40LP->busy);
|
||||
}
|
||||
|
||||
void display_start(void) {
|
||||
// Configure SCS before CRESET and before configuring the SPI so that we don't end up with the
|
||||
// FPGA in the "SPI Master Configuration Interface" on bigboards which don't have NVCM. If we end
|
||||
// up in this mode, the FPGA will drive the clock and put the SPI peripheral in a bad state.
|
||||
gpio_output_init(&ICE40LP->spi.scs, GPIO_OType_PP, GPIO_Speed_25MHz);
|
||||
gpio_output_set(&ICE40LP->spi.scs, false);
|
||||
gpio_input_init(&ICE40LP->cdone);
|
||||
gpio_input_init(&ICE40LP->busy);
|
||||
gpio_output_init(&ICE40LP->creset, GPIO_OType_OD, GPIO_Speed_25MHz);
|
||||
|
||||
prv_spi_init();
|
||||
}
|
||||
|
||||
bool display_program(const uint8_t *fpga_bitstream, uint32_t bitstream_size) {
|
||||
InputConfig creset_input = {
|
||||
.gpio = ICE40LP->creset.gpio,
|
||||
.gpio_pin = ICE40LP->creset.gpio_pin,
|
||||
};
|
||||
|
||||
delay_ms(1);
|
||||
|
||||
gpio_output_set(&ICE40LP->spi.scs, true); // SCS asserted (low)
|
||||
gpio_output_set(&ICE40LP->creset, false); // CRESET LOW
|
||||
|
||||
delay_ms(1);
|
||||
|
||||
if (gpio_input_read(&creset_input)) {
|
||||
dbgserial_putstr("CRESET not low during reset");
|
||||
return false;
|
||||
}
|
||||
|
||||
gpio_output_set(&ICE40LP->creset, true); // CRESET -> HIGH
|
||||
|
||||
delay_ms(1);
|
||||
|
||||
if (gpio_input_read(&ICE40LP->cdone)) {
|
||||
dbgserial_putstr("CDONE not low after reset");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!gpio_input_read(&creset_input)) {
|
||||
dbgserial_putstr("CRESET not high after reset");
|
||||
return false;
|
||||
}
|
||||
|
||||
delay_ms(1);
|
||||
|
||||
// Program the FPGA
|
||||
prv_spi_write(fpga_bitstream, bitstream_size);
|
||||
|
||||
// Set SCS high so that we don't process any of these clocks as commands.
|
||||
gpio_output_set(&ICE40LP->spi.scs, false); // SCS not asserted (high)
|
||||
|
||||
// 49+ SCLK cycles to tell FPGA we're done configuration.
|
||||
static const uint8_t spi_zeros[9] = {0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
prv_spi_write(spi_zeros, sizeof(spi_zeros));
|
||||
|
||||
if (!gpio_input_read(&ICE40LP->cdone)) {
|
||||
dbgserial_putstr("CDONE not high after programming");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void display_power_enable(void) {
|
||||
// The display requires us to wait 1ms between each power rail coming up. The PMIC
|
||||
// initialization brings up the 3.2V rail (VLCD on the display, LD02 on the PMIC) for us, but
|
||||
// we still need to wait before turning on the subsequent rails.
|
||||
delay_ms(2);
|
||||
|
||||
if (ICE40LP->use_6v6_rail) {
|
||||
dbgserial_putstr("Enabling 6v6 (Display VDDC)");
|
||||
set_6V6_power_state(true);
|
||||
|
||||
delay_ms(2);
|
||||
}
|
||||
|
||||
dbgserial_putstr("Enabling 4v5 (Display VDDP)");
|
||||
set_4V5_power_state(true);
|
||||
}
|
||||
|
||||
void display_power_disable(void) {
|
||||
dbgserial_putstr("Disabling 4v5 (Display VDDP)");
|
||||
set_4V5_power_state(false);
|
||||
|
||||
delay_ms(2);
|
||||
|
||||
if (ICE40LP->use_6v6_rail) {
|
||||
dbgserial_putstr("Disabling 6v6 (Display VDDC)");
|
||||
set_6V6_power_state(false);
|
||||
|
||||
delay_ms(2);
|
||||
}
|
||||
}
|
||||
|
||||
void display_write_cmd(uint8_t cmd, uint8_t *arg, uint32_t arg_len) {
|
||||
gpio_output_set(&ICE40LP->spi.scs, true); // SCS asserted (low)
|
||||
delay_us(100);
|
||||
|
||||
prv_spi_write(&cmd, sizeof(cmd));
|
||||
if (arg_len) {
|
||||
prv_spi_write(arg, arg_len);
|
||||
}
|
||||
|
||||
gpio_output_set(&ICE40LP->spi.scs, false); // SCS not asserted (high)
|
||||
}
|
27
platform/robert/boot/src/drivers/display/ice40lp.h
Normal file
27
platform/robert/boot/src/drivers/display/ice40lp.h
Normal file
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
bool display_busy(void);
|
||||
void display_start(void);
|
||||
bool display_program(const uint8_t *fpga_bitstream, uint32_t bitstream_size);
|
||||
void display_write_cmd(uint8_t cmd, uint8_t *arg, uint32_t arg_len);
|
||||
void display_power_enable(void);
|
||||
void display_power_disable(void);
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "board/board.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef const struct ICE40LPDevice {
|
||||
struct {
|
||||
SPI_TypeDef *periph;
|
||||
uint32_t rcc_bit;
|
||||
AfConfig clk;
|
||||
AfConfig mosi;
|
||||
OutputConfig scs;
|
||||
} spi;
|
||||
|
||||
OutputConfig creset;
|
||||
InputConfig cdone;
|
||||
InputConfig busy;
|
||||
|
||||
bool use_6v6_rail;
|
||||
} ICE40LPDevice;
|
46
platform/robert/boot/src/drivers/exti.h
Normal file
46
platform/robert/boot/src/drivers/exti.h
Normal file
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "board/board.h"
|
||||
|
||||
typedef enum {
|
||||
ExtiTrigger_Rising,
|
||||
ExtiTrigger_Falling,
|
||||
ExtiTrigger_RisingFalling
|
||||
} ExtiTrigger;
|
||||
|
||||
//! See section 12.2.5 "External interrupt/event line mapping" in the STM32F2 reference manual
|
||||
typedef enum {
|
||||
ExtiLineOther_RTCAlarm = 17,
|
||||
ExtiLineOther_RTCWakeup = 22
|
||||
} ExtiLineOther;
|
||||
|
||||
typedef void (*ExtiHandlerCallback)(void);
|
||||
|
||||
//! Configures the given EXTI and NVIC for the given configuration.
|
||||
void exti_configure_pin(ExtiConfig cfg, ExtiTrigger trigger, ExtiHandlerCallback cb);
|
||||
//! Configures the given EXTI and NVIC for the given configuration.
|
||||
void exti_configure_other(ExtiLineOther exti_line, ExtiTrigger trigger);
|
||||
|
||||
static inline void exti_enable(ExtiConfig config);
|
||||
static inline void exti_disable(ExtiConfig config);
|
||||
|
||||
void exti_enable_other(ExtiLineOther);
|
||||
void exti_disable_other(ExtiLineOther);
|
||||
|
||||
#include "exti.inl.h"
|
27
platform/robert/boot/src/drivers/exti.inl.h
Normal file
27
platform/robert/boot/src/drivers/exti.inl.h
Normal file
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
//! @file exti.inl.h
|
||||
//!
|
||||
//! Helper functions intended to be inlined into the calling code.
|
||||
|
||||
static inline void exti_enable(ExtiConfig config) {
|
||||
exti_enable_other(config.exti_line);
|
||||
}
|
||||
|
||||
static inline void exti_disable(ExtiConfig config) {
|
||||
exti_disable_other(config.exti_line);
|
||||
}
|
40
platform/robert/boot/src/drivers/flash.h
Normal file
40
platform/robert/boot/src/drivers/flash.h
Normal file
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
//! Configure the micro's peripherals to communicate with the flash chip
|
||||
void flash_init(void);
|
||||
|
||||
//! Read 1 or more bytes starting at the specified 24bit address into
|
||||
//! the provided buffer. This function does no range checking, so it is
|
||||
//! currently possible to run off the end of the flash.
|
||||
//!
|
||||
//! @param buffer A byte-buffer that will be used to store the data
|
||||
//! read from flash.
|
||||
//! @param start_addr The address of the first byte to be read from flash.
|
||||
//! @param buffer_size The total number of bytes to be read from flash.
|
||||
void flash_read_bytes(uint8_t* buffer, uint32_t start_addr, uint32_t buffer_size);
|
||||
|
||||
//! Check if we can talk to the flash.
|
||||
//! @return true if the CFI table can be queried.
|
||||
bool flash_sanity_check(void);
|
||||
|
||||
//! Get the checksum of a region of flash
|
||||
uint32_t flash_calculate_checksum(uint32_t flash_addr, uint32_t length);
|
38
platform/robert/boot/src/drivers/flash/flash_crc.c
Normal file
38
platform/robert/boot/src/drivers/flash/flash_crc.c
Normal file
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "drivers/flash.h"
|
||||
|
||||
#include "util/crc32.h"
|
||||
|
||||
#define CRC_CHUNK_SIZE 1024
|
||||
|
||||
uint32_t flash_calculate_checksum(uint32_t flash_addr, uint32_t num_bytes) {
|
||||
uint8_t buffer[CRC_CHUNK_SIZE];
|
||||
|
||||
uint32_t crc = CRC32_INIT;
|
||||
|
||||
while (num_bytes > CRC_CHUNK_SIZE) {
|
||||
flash_read_bytes(buffer, flash_addr, CRC_CHUNK_SIZE);
|
||||
crc = crc32(crc, buffer, CRC_CHUNK_SIZE);
|
||||
|
||||
num_bytes -= CRC_CHUNK_SIZE;
|
||||
flash_addr += CRC_CHUNK_SIZE;
|
||||
}
|
||||
|
||||
flash_read_bytes(buffer, flash_addr, num_bytes);
|
||||
return crc32(crc, buffer, num_bytes);
|
||||
}
|
220
platform/robert/boot/src/drivers/flash/mt25q.c
Normal file
220
platform/robert/boot/src/drivers/flash/mt25q.c
Normal file
|
@ -0,0 +1,220 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "board/board.h"
|
||||
#include "drivers/flash.h"
|
||||
#include "drivers/gpio.h"
|
||||
#include "drivers/periph_config.h"
|
||||
#include "util/delay.h"
|
||||
|
||||
#include "stm32f7haxx_qspi.h"
|
||||
|
||||
#define MT25Q_FASTREAD_DUMMYCYCLES 10
|
||||
|
||||
typedef enum MT25QCommand {
|
||||
// SPI/QSPI Commands
|
||||
MT25QCommand_FastRead = 0x0B, // FAST_READ
|
||||
MT25QCommand_QSPIEnable = 0x35, // QPI
|
||||
MT25QCommand_ResetEnable = 0x66, // RSTEN
|
||||
MT25QCommand_Reset = 0x99, // RST
|
||||
|
||||
// QSPI only commands
|
||||
MT25QCommand_QSPI_ID = 0xAF, // QPIID
|
||||
} MT25QCommand;
|
||||
|
||||
// Helpful Enums
|
||||
typedef enum {
|
||||
QSPIFlag_Retain = 0,
|
||||
QSPIFlag_ClearTC = 1,
|
||||
} QSPIFlag;
|
||||
|
||||
static void prv_enable_qspi_clock(void) {
|
||||
periph_config_enable(QUADSPI, RCC_AHB3Periph_QSPI);
|
||||
}
|
||||
|
||||
static void prv_disable_qspi_clock(void) {
|
||||
periph_config_disable(QUADSPI, RCC_AHB3Periph_QSPI);
|
||||
}
|
||||
|
||||
static void prv_set_num_data_bytes(uint32_t length) {
|
||||
// From the docs: QSPI_DataLength: Number of data to be retrieved, value+1.
|
||||
// so 0 is 1 byte, so we substract 1 from the length. -1 is read the entire flash length.
|
||||
QSPI_SetDataLength(length - 1);
|
||||
}
|
||||
|
||||
static void prv_wait_for_qspi_transfer_complete(QSPIFlag actions) {
|
||||
while (QSPI_GetFlagStatus(QSPI_FLAG_TC) == RESET) { }
|
||||
|
||||
if (actions == QSPIFlag_ClearTC) {
|
||||
QSPI_ClearFlag(QSPI_FLAG_TC);
|
||||
}
|
||||
}
|
||||
|
||||
static void prv_wait_for_qspi_not_busy(void) {
|
||||
while (QSPI_GetFlagStatus(QSPI_FLAG_BUSY) != RESET) { }
|
||||
}
|
||||
|
||||
static void prv_quad_enable() {
|
||||
QSPI_ComConfig_InitTypeDef qspi_com_config;
|
||||
QSPI_ComConfig_StructInit(&qspi_com_config);
|
||||
qspi_com_config.QSPI_ComConfig_FMode = QSPI_ComConfig_FMode_Indirect_Write;
|
||||
qspi_com_config.QSPI_ComConfig_IMode = QSPI_ComConfig_IMode_1Line;
|
||||
qspi_com_config.QSPI_ComConfig_Ins = MT25QCommand_QSPIEnable;
|
||||
QSPI_ComConfig_Init(&qspi_com_config);
|
||||
|
||||
prv_wait_for_qspi_transfer_complete(QSPIFlag_ClearTC);
|
||||
|
||||
prv_wait_for_qspi_not_busy();
|
||||
}
|
||||
|
||||
static void prv_flash_reset(void) {
|
||||
QSPI_ComConfig_InitTypeDef qspi_com_config;
|
||||
QSPI_ComConfig_StructInit(&qspi_com_config);
|
||||
qspi_com_config.QSPI_ComConfig_FMode = QSPI_ComConfig_FMode_Indirect_Write;
|
||||
qspi_com_config.QSPI_ComConfig_IMode = QSPI_ComConfig_IMode_4Line;
|
||||
qspi_com_config.QSPI_ComConfig_Ins = MT25QCommand_ResetEnable;
|
||||
QSPI_ComConfig_Init(&qspi_com_config);
|
||||
|
||||
prv_wait_for_qspi_transfer_complete(QSPIFlag_ClearTC);
|
||||
|
||||
QSPI_ComConfig_StructInit(&qspi_com_config);
|
||||
qspi_com_config.QSPI_ComConfig_FMode = QSPI_ComConfig_FMode_Indirect_Write;
|
||||
qspi_com_config.QSPI_ComConfig_IMode = QSPI_ComConfig_IMode_4Line;
|
||||
qspi_com_config.QSPI_ComConfig_Ins = MT25QCommand_Reset;
|
||||
QSPI_ComConfig_Init(&qspi_com_config);
|
||||
|
||||
prv_wait_for_qspi_transfer_complete(QSPIFlag_ClearTC);
|
||||
|
||||
delay_us(50000); // 50ms reset if busy with an erase!
|
||||
|
||||
// Return the flash to Quad SPI mode, all our commands are quad-spi and it'll just cause
|
||||
// problems/bugs for someone if it comes back in single spi mode
|
||||
prv_quad_enable();
|
||||
}
|
||||
|
||||
#include "system/passert.h"
|
||||
static bool prv_flash_check_whoami(void) {
|
||||
const unsigned int num_whoami_bytes = 3;
|
||||
|
||||
prv_set_num_data_bytes(num_whoami_bytes);
|
||||
|
||||
QSPI_ComConfig_InitTypeDef qspi_com_config;
|
||||
QSPI_ComConfig_StructInit(&qspi_com_config);
|
||||
qspi_com_config.QSPI_ComConfig_FMode = QSPI_ComConfig_FMode_Indirect_Read;
|
||||
qspi_com_config.QSPI_ComConfig_DMode = QSPI_ComConfig_DMode_4Line;
|
||||
qspi_com_config.QSPI_ComConfig_IMode = QSPI_ComConfig_IMode_4Line;
|
||||
qspi_com_config.QSPI_ComConfig_Ins = MT25QCommand_QSPI_ID;
|
||||
QSPI_ComConfig_Init(&qspi_com_config);
|
||||
|
||||
prv_wait_for_qspi_transfer_complete(QSPIFlag_ClearTC);
|
||||
|
||||
uint32_t read_whoami = 0;
|
||||
for (unsigned int i = 0; i < num_whoami_bytes; ++i) {
|
||||
read_whoami |= QSPI_ReceiveData8() << (8 * i);
|
||||
}
|
||||
|
||||
prv_wait_for_qspi_not_busy();
|
||||
|
||||
#if BOARD_ROBERT_BB || BOARD_ROBERT_BB2
|
||||
const uint32_t expected_whoami = 0x19BB20;
|
||||
#elif BOARD_ROBERT_EVT
|
||||
const uint32_t expected_whoami = 0x18BB20;
|
||||
#else
|
||||
#error "Unsupported board"
|
||||
#endif
|
||||
if (read_whoami == expected_whoami) {
|
||||
return true;
|
||||
} else {
|
||||
dbgserial_print_hex(read_whoami);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void flash_init(void) {
|
||||
prv_enable_qspi_clock();
|
||||
// init GPIOs
|
||||
for (unsigned i = 0; i < QSpiPinCount; ++i) {
|
||||
gpio_af_init(&BOARD_CONFIG_FLASH_PINS[i], GPIO_OType_PP, GPIO_Speed_100MHz, GPIO_PuPd_NOPULL);
|
||||
}
|
||||
if (BOARD_CONFIG_FLASH.reset_gpio.gpio) {
|
||||
gpio_output_init(&BOARD_CONFIG_FLASH.reset_gpio, GPIO_OType_PP, GPIO_Speed_2MHz);
|
||||
gpio_output_set(&BOARD_CONFIG_FLASH.reset_gpio, false);
|
||||
}
|
||||
|
||||
// Init QSPI peripheral
|
||||
QSPI_InitTypeDef qspi_config;
|
||||
QSPI_StructInit(&qspi_config);
|
||||
qspi_config.QSPI_SShift = QSPI_SShift_HalfCycleShift;
|
||||
qspi_config.QSPI_Prescaler = 0;
|
||||
qspi_config.QSPI_CKMode = QSPI_CKMode_Mode0;
|
||||
qspi_config.QSPI_CSHTime = QSPI_CSHTime_1Cycle;
|
||||
qspi_config.QSPI_FSize = 23; // 2^24 = 16MB. -> 24 - 1 = 23
|
||||
qspi_config.QSPI_FSelect = QSPI_FSelect_1;
|
||||
qspi_config.QSPI_DFlash = QSPI_DFlash_Disable;
|
||||
QSPI_Init(&qspi_config);
|
||||
|
||||
QSPI_Cmd(ENABLE);
|
||||
|
||||
// Must call quad_enable first, all commands are QSPI
|
||||
prv_quad_enable();
|
||||
|
||||
// Reset the flash to stop any program's or erase in progress from before reboot
|
||||
prv_flash_reset();
|
||||
|
||||
prv_disable_qspi_clock();
|
||||
}
|
||||
|
||||
bool flash_sanity_check(void) {
|
||||
prv_enable_qspi_clock();
|
||||
|
||||
bool result = prv_flash_check_whoami();
|
||||
|
||||
prv_disable_qspi_clock();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void flash_read_bytes(uint8_t *buffer_ptr, uint32_t start_addr, uint32_t buffer_size) {
|
||||
prv_enable_qspi_clock();
|
||||
|
||||
prv_set_num_data_bytes(buffer_size);
|
||||
|
||||
QSPI_ComConfig_InitTypeDef qspi_com_config;
|
||||
QSPI_ComConfig_StructInit(&qspi_com_config);
|
||||
qspi_com_config.QSPI_ComConfig_FMode = QSPI_ComConfig_FMode_Indirect_Read;
|
||||
qspi_com_config.QSPI_ComConfig_DMode = QSPI_ComConfig_DMode_4Line;
|
||||
qspi_com_config.QSPI_ComConfig_DummyCycles = MT25Q_FASTREAD_DUMMYCYCLES;
|
||||
qspi_com_config.QSPI_ComConfig_ADMode = QSPI_ComConfig_ADMode_4Line;
|
||||
qspi_com_config.QSPI_ComConfig_IMode = QSPI_ComConfig_IMode_4Line;
|
||||
qspi_com_config.QSPI_ComConfig_ADSize = QSPI_ComConfig_ADSize_24bit;
|
||||
qspi_com_config.QSPI_ComConfig_Ins = MT25QCommand_FastRead;
|
||||
QSPI_ComConfig_Init(&qspi_com_config);
|
||||
|
||||
QSPI_SetAddress(start_addr);
|
||||
|
||||
uint8_t *write_ptr = buffer_ptr;
|
||||
for (unsigned i = 0; i < buffer_size; ++i) {
|
||||
write_ptr[i] = QSPI_ReceiveData8();
|
||||
}
|
||||
|
||||
QSPI_ClearFlag(QSPI_FLAG_TC);
|
||||
prv_wait_for_qspi_not_busy();
|
||||
|
||||
prv_disable_qspi_clock();
|
||||
}
|
158
platform/robert/boot/src/drivers/gpio.c
Normal file
158
platform/robert/boot/src/drivers/gpio.c
Normal file
|
@ -0,0 +1,158 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "drivers/gpio.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#define MAX_GPIO (11)
|
||||
|
||||
#define GPIO_EN_MASK ((RCC_AHB1ENR_GPIOKEN << 1) - 1)
|
||||
|
||||
void gpio_disable_all(void) {
|
||||
RCC->AHB1ENR &= ~GPIO_EN_MASK;
|
||||
}
|
||||
|
||||
static void prv_init_common(const InputConfig *input_config, GPIO_InitTypeDef *gpio_init) {
|
||||
gpio_use(input_config->gpio);
|
||||
GPIO_Init(input_config->gpio, gpio_init);
|
||||
gpio_release(input_config->gpio);
|
||||
}
|
||||
|
||||
static uint8_t s_gpio_clock_count[MAX_GPIO];
|
||||
|
||||
void gpio_use(GPIO_TypeDef* GPIOx) {
|
||||
uint8_t idx = ((((uint32_t)GPIOx) - AHB1PERIPH_BASE) / 0x0400);
|
||||
if ((idx < MAX_GPIO) && !(s_gpio_clock_count[idx]++)) {
|
||||
SET_BIT(RCC->AHB1ENR, (0x1 << idx));
|
||||
}
|
||||
}
|
||||
|
||||
void gpio_release(GPIO_TypeDef* GPIOx) {
|
||||
uint8_t idx = ((((uint32_t)GPIOx) - AHB1PERIPH_BASE) / 0x0400);
|
||||
if ((idx < MAX_GPIO) && s_gpio_clock_count[idx] && !(--s_gpio_clock_count[idx])) {
|
||||
CLEAR_BIT(RCC->AHB1ENR, (0x1 << idx));
|
||||
}
|
||||
}
|
||||
|
||||
void gpio_output_init(const OutputConfig *pin_config, GPIOOType_TypeDef otype,
|
||||
GPIOSpeed_TypeDef speed) {
|
||||
GPIO_InitTypeDef init = {
|
||||
.GPIO_Pin = pin_config->gpio_pin,
|
||||
.GPIO_Mode = GPIO_Mode_OUT,
|
||||
.GPIO_Speed = speed,
|
||||
.GPIO_OType = otype,
|
||||
.GPIO_PuPd = GPIO_PuPd_NOPULL
|
||||
};
|
||||
|
||||
gpio_use(pin_config->gpio);
|
||||
GPIO_Init(pin_config->gpio, &init);
|
||||
gpio_release(pin_config->gpio);
|
||||
}
|
||||
|
||||
void gpio_output_set(const OutputConfig *pin_config, bool asserted) {
|
||||
if (!pin_config->active_high) {
|
||||
asserted = !asserted;
|
||||
}
|
||||
gpio_use(pin_config->gpio);
|
||||
GPIO_WriteBit(pin_config->gpio, pin_config->gpio_pin,
|
||||
asserted? Bit_SET : Bit_RESET);
|
||||
gpio_release(pin_config->gpio);
|
||||
}
|
||||
|
||||
void gpio_af_init(const AfConfig *af_config, GPIOOType_TypeDef otype,
|
||||
GPIOSpeed_TypeDef speed, GPIOPuPd_TypeDef pupd) {
|
||||
GPIO_InitTypeDef init = {
|
||||
.GPIO_Pin = af_config->gpio_pin,
|
||||
.GPIO_Mode = GPIO_Mode_AF,
|
||||
.GPIO_Speed = speed,
|
||||
.GPIO_OType = otype,
|
||||
.GPIO_PuPd = pupd
|
||||
};
|
||||
|
||||
gpio_use(af_config->gpio);
|
||||
GPIO_PinAFConfig(af_config->gpio, af_config->gpio_pin_source,
|
||||
af_config->gpio_af);
|
||||
GPIO_Init(af_config->gpio, &init);
|
||||
gpio_release(af_config->gpio);
|
||||
}
|
||||
|
||||
void gpio_af_configure_low_power(const AfConfig *af_config) {
|
||||
GPIO_InitTypeDef init = {
|
||||
.GPIO_Pin = af_config->gpio_pin,
|
||||
.GPIO_Mode = GPIO_Mode_AN,
|
||||
.GPIO_Speed = GPIO_Speed_2MHz,
|
||||
.GPIO_PuPd = GPIO_PuPd_NOPULL
|
||||
};
|
||||
|
||||
gpio_use(af_config->gpio);
|
||||
GPIO_Init(af_config->gpio, &init);
|
||||
gpio_release(af_config->gpio);
|
||||
}
|
||||
|
||||
void gpio_af_configure_fixed_output(const AfConfig *af_config, bool asserted) {
|
||||
GPIO_InitTypeDef init = {
|
||||
.GPIO_Pin = af_config->gpio_pin,
|
||||
.GPIO_Mode = GPIO_Mode_OUT,
|
||||
.GPIO_Speed = GPIO_Speed_2MHz,
|
||||
.GPIO_OType = GPIO_OType_PP,
|
||||
.GPIO_PuPd = GPIO_PuPd_NOPULL
|
||||
};
|
||||
|
||||
gpio_use(af_config->gpio);
|
||||
GPIO_Init(af_config->gpio, &init);
|
||||
GPIO_WriteBit(af_config->gpio, af_config->gpio_pin,
|
||||
asserted? Bit_SET : Bit_RESET);
|
||||
gpio_release(af_config->gpio);
|
||||
}
|
||||
|
||||
void gpio_input_init(const InputConfig *input_config) {
|
||||
if (input_config->gpio == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
gpio_input_init_pull_up_down(input_config, GPIO_PuPd_NOPULL);
|
||||
}
|
||||
|
||||
void gpio_input_init_pull_up_down(const InputConfig *input_config, GPIOPuPd_TypeDef pupd) {
|
||||
GPIO_InitTypeDef gpio_init = {
|
||||
.GPIO_Mode = GPIO_Mode_IN,
|
||||
.GPIO_PuPd = pupd,
|
||||
.GPIO_Pin = input_config->gpio_pin
|
||||
};
|
||||
|
||||
prv_init_common(input_config, &gpio_init);
|
||||
}
|
||||
|
||||
bool gpio_input_read(const InputConfig *input_config) {
|
||||
gpio_use(input_config->gpio);
|
||||
uint8_t bit = GPIO_ReadInputDataBit(input_config->gpio, input_config->gpio_pin);
|
||||
gpio_release(input_config->gpio);
|
||||
|
||||
return bit != 0;
|
||||
}
|
||||
|
||||
void gpio_analog_init(const InputConfig *input_config) {
|
||||
GPIO_InitTypeDef gpio_init = {
|
||||
.GPIO_Pin = input_config->gpio_pin,
|
||||
.GPIO_Mode = GPIO_Mode_AN,
|
||||
.GPIO_Speed = GPIO_Speed_2MHz,
|
||||
.GPIO_PuPd = GPIO_PuPd_NOPULL
|
||||
};
|
||||
|
||||
prv_init_common(input_config, &gpio_init);
|
||||
}
|
89
platform/robert/boot/src/drivers/gpio.h
Normal file
89
platform/robert/boot/src/drivers/gpio.h
Normal file
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "stm32f7xx.h"
|
||||
|
||||
#include "board/board.h"
|
||||
|
||||
void gpio_disable_all(void);
|
||||
|
||||
void gpio_use(GPIO_TypeDef* GPIOx);
|
||||
void gpio_release(GPIO_TypeDef* GPIOx);
|
||||
|
||||
//! Initialize a GPIO as an output.
|
||||
//!
|
||||
//! @param pin_config the BOARD_CONFIG pin configuration struct
|
||||
//! @param otype the output type of the pin (GPIO_OType_PP or GPIO_OType_OD)
|
||||
//! @param speed the output slew rate
|
||||
//! @note The slew rate should be set as low as possible for the
|
||||
//! pin function to minimize ringing and RF interference.
|
||||
void gpio_output_init(const OutputConfig *pin_config, GPIOOType_TypeDef otype,
|
||||
GPIOSpeed_TypeDef speed);
|
||||
|
||||
//! Assert or deassert the output pin.
|
||||
//!
|
||||
//! Asserting the output drives the pin high if pin_config.active_high
|
||||
//! is true, and drives it low if pin_config.active_high is false.
|
||||
void gpio_output_set(const OutputConfig *pin_config, bool asserted);
|
||||
|
||||
//! Configure a GPIO alternate function.
|
||||
//!
|
||||
//! @param pin_config the BOARD_CONFIG pin configuration struct
|
||||
//! @param otype the output type of the pin (GPIO_OType_PP or GPIO_OType_OD)
|
||||
//! @param speed the output slew rate
|
||||
//! @param pupd pull-up or pull-down configuration
|
||||
//! @note The slew rate should be set as low as possible for the
|
||||
//! pin function to minimize ringing and RF interference.
|
||||
void gpio_af_init(const AfConfig *af_config, GPIOOType_TypeDef otype,
|
||||
GPIOSpeed_TypeDef speed, GPIOPuPd_TypeDef pupd);
|
||||
|
||||
//! Configure a GPIO alternate function pin to minimize power consumption.
|
||||
//!
|
||||
//! Once a pin has been configured for low power, it is no longer
|
||||
//! connected to its alternate function. \ref gpio_af_init will need to
|
||||
//! be called again on the pin in order to configure it in alternate
|
||||
//! function mode again.
|
||||
void gpio_af_configure_low_power(const AfConfig *af_config);
|
||||
|
||||
//! Configure a GPIO alternate function pin to drive a constant output.
|
||||
//!
|
||||
//! Once a pin has been configured as a fixed output, it is no longer
|
||||
//! connected to its alternate function. \ref gpio_af_init will need to
|
||||
//! be called again on the pin in order to configure it in alternate
|
||||
//! function mode again.
|
||||
void gpio_af_configure_fixed_output(const AfConfig *af_config, bool asserted);
|
||||
|
||||
//! Configure all GPIOs in the system to optimize for power consumption.
|
||||
//! At poweron most GPIOs can be configured as analog inputs instead of the
|
||||
//! default digital input. This allows digital filtering logic to be shut down,
|
||||
//! saving quite a bit of power.
|
||||
void gpio_init_all(void);
|
||||
|
||||
//! Configure gpios as inputs (suitable for things like exti lines)
|
||||
void gpio_input_init(const InputConfig *input_cfg);
|
||||
|
||||
//! Configure gpio as an input with internal pull up/pull down configured.
|
||||
void gpio_input_init_pull_up_down(const InputConfig *input_cfg, GPIOPuPd_TypeDef pupd);
|
||||
|
||||
//! @return bool the current state of the GPIO pin
|
||||
bool gpio_input_read(const InputConfig *input_cfg);
|
||||
|
||||
//! Configure gpios as analog inputs. Useful for unused GPIOs as this is their lowest power state.
|
||||
void gpio_analog_init(const InputConfig *input_cfg);
|
97
platform/robert/boot/src/drivers/i2c.h
Normal file
97
platform/robert/boot/src/drivers/i2c.h
Normal file
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "board/board.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
//! Start using the I2C bus to which \a slave is connected
|
||||
//! Must be called before any other reads or writes to the slave are performed
|
||||
//! @param slave I2C slave reference, which will identify the bus to use
|
||||
void i2c_use(I2CSlavePort *slave);
|
||||
|
||||
//! Stop using the I2C bus to which \a slave is connected
|
||||
//! Call when done communicating with the slave
|
||||
//! @param slave I2C slave reference, which will identify the bus to release
|
||||
void i2c_release(I2CSlavePort *slave);
|
||||
|
||||
//! Reset the slave
|
||||
//! Will cycle the power to and re-initialize the bus to which \a slave is connected, if this is
|
||||
//! supported for the bus.
|
||||
//! @param slave I2C slave reference, which will identify the bus to be reset
|
||||
void i2c_reset(I2CSlavePort *slave);
|
||||
|
||||
//! Manually bang out the clock on the bus to which \a slave is connected until the data line
|
||||
//! recovers for a period or we timeout waiting for it to recover
|
||||
//! Must not be called before \ref i2c_use has been called for the slave
|
||||
//! @param slave I2C slave reference, which will identify the bus to be recovered
|
||||
//! @return true if the data line recovered, false otherwise
|
||||
bool i2c_bitbang_recovery(I2CSlavePort *slave);
|
||||
|
||||
//! Read the value of a register
|
||||
//! Must not be called before \ref i2c_use has been called for the slave
|
||||
//! @param slave I2C slave to communicate with
|
||||
//! @param register_address Address of register to read
|
||||
//! @param result Pointer to destination buffer
|
||||
//! @return true if transfer succeeded, false if error occurred
|
||||
bool i2c_read_register(I2CSlavePort *slave, uint8_t register_address, uint8_t *result);
|
||||
|
||||
//! Read a sequence of registers starting from \a register_address_start
|
||||
//! Must not be called before \ref i2c_use has been called for the slave
|
||||
//! @param slave I2C slave to communicate with
|
||||
//! @param register_address_start Address of first register to read
|
||||
//! @param read_size Number of bytes to read
|
||||
//! @param result_buffer Pointer to destination buffer
|
||||
//! @return true if transfer succeeded, false if error occurred
|
||||
bool i2c_read_register_block(I2CSlavePort *slave, uint8_t register_address_start,
|
||||
uint32_t read_size, uint8_t* result_buffer);
|
||||
|
||||
//! Read a block of data without sending a register address before doing so.
|
||||
//! Must not be called before \ref i2c_use has been called for the slave
|
||||
//! @param slave I2C slave to communicate with
|
||||
//! @param read_size Number of bytes to read
|
||||
//! @param result_buffer Pointer to destination buffer
|
||||
//! @return true if transfer succeeded, false if error occurred
|
||||
bool i2c_read_block(I2CSlavePort *slave, uint32_t read_size, uint8_t* result_buffer);
|
||||
|
||||
//! Write to a register
|
||||
//! Must not be called before \ref i2c_use has been called for the slave
|
||||
//! @param slave I2C slave to communicate with
|
||||
//! @param register_address Address of register to write to
|
||||
//! @param value Data value to write
|
||||
//! @return true if transfer succeeded, false if error occurred
|
||||
bool i2c_write_register(I2CSlavePort *slave, uint8_t register_address, uint8_t value);
|
||||
|
||||
//! Write to a sequence of registers starting from \a register_address_start
|
||||
//! Must not be called before \ref i2c_use has been called for the slave
|
||||
//! @param slave I2C slave to communicate with
|
||||
//! @param register_address_start Address of first register to read
|
||||
//! @param write_size Number of bytes to write
|
||||
//! @param buffer Pointer to source buffer
|
||||
//! @return true if transfer succeeded, false if error occurred
|
||||
bool i2c_write_register_block(I2CSlavePort *slave, uint8_t register_address_start,
|
||||
uint32_t write_size, const uint8_t* buffer);
|
||||
|
||||
//! Write a block of data without sending a register address before doing so.
|
||||
//! Must not be called before \ref i2c_use has been called for the slave
|
||||
//! @param slave I2C slave to communicate with
|
||||
//! @param write_size Number of bytes to write
|
||||
//! @param buffer Pointer to source buffer
|
||||
//! @return true if transfer succeeded, false if error occurred
|
||||
bool i2c_write_block(I2CSlavePort *slave, uint32_t write_size, const uint8_t* buffer);
|
479
platform/robert/boot/src/drivers/i2c/i2c.c
Normal file
479
platform/robert/boot/src/drivers/i2c/i2c.c
Normal file
|
@ -0,0 +1,479 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "drivers/i2c.h"
|
||||
#include "i2c_definitions.h"
|
||||
#include "i2c_hal.h"
|
||||
|
||||
#include "board/board.h"
|
||||
#include "drivers/gpio.h"
|
||||
#include "drivers/periph_config.h"
|
||||
#include "system/passert.h"
|
||||
#include "system/logging.h"
|
||||
#include "util/delay.h"
|
||||
#include "util/size.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "stm32f7xx.h"
|
||||
|
||||
#define I2C_ERROR_TIMEOUT_MS (1000)
|
||||
#define I2C_TIMEOUT_ATTEMPTS_MAX (2 * 1000 * 1000)
|
||||
|
||||
// MFI NACKs while busy. We delay ~1ms between retries so this is approximately a 1000ms timeout.
|
||||
// The longest operation of the MFi chip is "start signature generation", which seems to take
|
||||
// 223-224 NACKs, but sometimes for unknown reasons it can take much longer.
|
||||
#define I2C_NACK_COUNT_MAX (1000)
|
||||
|
||||
typedef enum {
|
||||
Read,
|
||||
Write
|
||||
} TransferDirection;
|
||||
|
||||
typedef enum {
|
||||
SendRegisterAddress, // Send a register address, followed by a repeat start for reads
|
||||
NoRegisterAddress // Do not send a register address
|
||||
} TransferType;
|
||||
|
||||
/*----------------SEMAPHORE/LOCKING FUNCTIONS--------------------------*/
|
||||
|
||||
static bool prv_semaphore_take(I2CBusState *bus) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool prv_semaphore_wait(I2CBusState *bus) {
|
||||
bus->busy = true;
|
||||
volatile uint32_t timeout_attempts = I2C_TIMEOUT_ATTEMPTS_MAX;
|
||||
while ((timeout_attempts-- > 0) && (bus->busy)) {};
|
||||
bus->busy = false;
|
||||
return (timeout_attempts != 0);
|
||||
}
|
||||
|
||||
static void prv_semaphore_give(I2CBusState *bus) {
|
||||
bus->busy = false;
|
||||
}
|
||||
|
||||
static void prv_semaphore_give_from_isr(I2CBusState *bus) {
|
||||
bus->busy = false;
|
||||
return;
|
||||
}
|
||||
|
||||
/*-------------------BUS/PIN CONFIG FUNCTIONS--------------------------*/
|
||||
|
||||
static void prv_rail_ctl(I2CBus *bus, bool enable) {
|
||||
bus->rail_ctl_fn(bus, enable);
|
||||
if (enable) {
|
||||
// wait for the bus supply to stabilize and the peripherals to start up.
|
||||
// the MFI chip requires its reset pin to be stable for at least 10ms from startup.
|
||||
delay_ms(20);
|
||||
}
|
||||
}
|
||||
|
||||
//! Power down I2C bus power supply
|
||||
//! Always lock bus and peripheral config access before use
|
||||
static void prv_bus_rail_power_down(I2CBus *bus) {
|
||||
if (!bus->rail_ctl_fn) {
|
||||
return;
|
||||
}
|
||||
prv_rail_ctl(bus, false);
|
||||
|
||||
// Drain through pull-ups
|
||||
OutputConfig out_scl = {
|
||||
.gpio = bus->scl_gpio.gpio,
|
||||
.gpio_pin = bus->scl_gpio.gpio_pin,
|
||||
.active_high = true
|
||||
};
|
||||
gpio_output_init(&out_scl, GPIO_PuPd_NOPULL, GPIO_Speed_2MHz);
|
||||
gpio_output_set(&out_scl, false);
|
||||
|
||||
OutputConfig out_sda = {
|
||||
.gpio = bus->sda_gpio.gpio,
|
||||
.gpio_pin = bus->sda_gpio.gpio_pin,
|
||||
.active_high = true
|
||||
};
|
||||
gpio_output_init(&out_sda, GPIO_PuPd_NOPULL, GPIO_Speed_2MHz);
|
||||
gpio_output_set(&out_sda, false);
|
||||
}
|
||||
|
||||
//! Configure bus pins for use by I2C peripheral
|
||||
//! Lock bus and peripheral config access before configuring pins
|
||||
static void prv_bus_pins_cfg_i2c(I2CBus *bus) {
|
||||
gpio_af_init(&bus->scl_gpio, GPIO_OType_OD, GPIO_Speed_50MHz, GPIO_PuPd_NOPULL);
|
||||
gpio_af_init(&bus->sda_gpio, GPIO_OType_OD, GPIO_Speed_50MHz, GPIO_PuPd_NOPULL);
|
||||
}
|
||||
|
||||
static void prv_bus_pins_cfg_input(I2CBus *bus) {
|
||||
InputConfig in_scl = {
|
||||
.gpio = bus->scl_gpio.gpio,
|
||||
.gpio_pin = bus->scl_gpio.gpio_pin,
|
||||
};
|
||||
gpio_input_init(&in_scl);
|
||||
|
||||
InputConfig in_sda = {
|
||||
.gpio = bus->sda_gpio.gpio,
|
||||
.gpio_pin = bus->sda_gpio.gpio_pin,
|
||||
};
|
||||
gpio_input_init(&in_sda);
|
||||
}
|
||||
|
||||
//! Power up I2C bus power supply
|
||||
//! Always lock bus and peripheral config access before use
|
||||
static void prv_bus_rail_power_up(I2CBus *bus) {
|
||||
if (!bus->rail_ctl_fn) {
|
||||
return;
|
||||
}
|
||||
|
||||
static const uint32_t MIN_STOP_TIME_MS = 10;
|
||||
delay_ms(MIN_STOP_TIME_MS);
|
||||
|
||||
prv_bus_pins_cfg_input(bus);
|
||||
|
||||
prv_rail_ctl(bus, true);
|
||||
}
|
||||
|
||||
//! Configure the bus pins, enable the peripheral clock and initialize the I2C peripheral.
|
||||
//! Always lock the bus and peripheral config access before enabling it
|
||||
static void prv_bus_enable(I2CBus *bus) {
|
||||
// Don't power up rail if the bus is already in use (enable can be called to reset bus)
|
||||
if (bus->state->user_count == 0) {
|
||||
prv_bus_rail_power_up(bus);
|
||||
}
|
||||
|
||||
prv_bus_pins_cfg_i2c(bus);
|
||||
|
||||
i2c_hal_enable(bus);
|
||||
}
|
||||
|
||||
//! De-initialize and gate the clock to the peripheral
|
||||
//! Power down rail if the bus supports that and no devices are using it
|
||||
//! Always lock the bus and peripheral config access before disabling it
|
||||
static void prv_bus_disable(I2CBus *bus) {
|
||||
i2c_hal_disable(bus);
|
||||
|
||||
// Do not de-power rail if there are still devices using bus (just reset peripheral and pin
|
||||
// configuration during a bus reset)
|
||||
if (bus->state->user_count == 0) {
|
||||
prv_bus_rail_power_down(bus);
|
||||
} else {
|
||||
prv_bus_pins_cfg_input(bus);
|
||||
}
|
||||
}
|
||||
|
||||
//! Perform a soft reset of the bus
|
||||
//! Always lock the bus before reset
|
||||
static void prv_bus_reset(I2CBus *bus) {
|
||||
prv_bus_disable(bus);
|
||||
prv_bus_enable(bus);
|
||||
}
|
||||
|
||||
/*---------------INIT/USE/RELEASE/RESET FUNCTIONS----------------------*/
|
||||
|
||||
void i2c_init(I2CBus *bus) {
|
||||
PBL_ASSERTN(bus);
|
||||
|
||||
*bus->state = (I2CBusState) {};
|
||||
|
||||
i2c_hal_init(bus);
|
||||
|
||||
if (bus->rail_gpio.gpio) {
|
||||
gpio_output_init(&bus->rail_gpio, GPIO_OType_PP, GPIO_Speed_2MHz);
|
||||
}
|
||||
prv_bus_rail_power_down(bus);
|
||||
}
|
||||
|
||||
void i2c_use(I2CSlavePort *slave) {
|
||||
PBL_ASSERTN(slave);
|
||||
|
||||
if (slave->bus->state->user_count == 0) {
|
||||
prv_bus_enable(slave->bus);
|
||||
}
|
||||
slave->bus->state->user_count++;
|
||||
}
|
||||
|
||||
void i2c_release(I2CSlavePort *slave) {
|
||||
PBL_ASSERTN(slave);
|
||||
if (slave->bus->state->user_count == 0) {
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "Attempted release of disabled bus %s", slave->bus->name);
|
||||
return;
|
||||
}
|
||||
|
||||
slave->bus->state->user_count--;
|
||||
if (slave->bus->state->user_count == 0) {
|
||||
prv_bus_disable(slave->bus);
|
||||
}
|
||||
}
|
||||
|
||||
void i2c_reset(I2CSlavePort *slave) {
|
||||
PBL_ASSERTN(slave);
|
||||
|
||||
if (slave->bus->state->user_count == 0) {
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "Attempted reset of disabled bus %s when still in use by "
|
||||
"another bus", slave->bus->name);
|
||||
return;
|
||||
}
|
||||
|
||||
PBL_LOG(LOG_LEVEL_WARNING, "Resetting I2C bus %s", slave->bus->name);
|
||||
|
||||
// decrement user count for reset so that if this user is the only user, the
|
||||
// bus will be powered down during the reset
|
||||
slave->bus->state->user_count--;
|
||||
|
||||
// Reset and reconfigure bus and pins
|
||||
prv_bus_reset(slave->bus);
|
||||
|
||||
// Restore user count
|
||||
slave->bus->state->user_count++;
|
||||
}
|
||||
|
||||
bool i2c_bitbang_recovery(I2CSlavePort *slave) {
|
||||
PBL_ASSERTN(slave);
|
||||
|
||||
static const int MAX_TOGGLE_COUNT = 10;
|
||||
static const int TOGGLE_DELAY = 10;
|
||||
|
||||
if (slave->bus->state->user_count == 0) {
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "Attempted bitbang recovery on disabled bus %s", slave->bus->name);
|
||||
return false;
|
||||
}
|
||||
|
||||
InputConfig in_sda = {
|
||||
.gpio = slave->bus->sda_gpio.gpio,
|
||||
.gpio_pin = slave->bus->sda_gpio.gpio_pin,
|
||||
};
|
||||
gpio_input_init(&in_sda);
|
||||
|
||||
OutputConfig out_scl = {
|
||||
.gpio = slave->bus->scl_gpio.gpio,
|
||||
.gpio_pin = slave->bus->scl_gpio.gpio_pin,
|
||||
.active_high = true
|
||||
};
|
||||
gpio_output_init(&out_scl, GPIO_PuPd_NOPULL, GPIO_Speed_2MHz);
|
||||
gpio_output_set(&out_scl, true);
|
||||
|
||||
bool recovered = false;
|
||||
for (int i = 0; i < MAX_TOGGLE_COUNT; ++i) {
|
||||
gpio_output_set(&out_scl, false);
|
||||
delay_ms(TOGGLE_DELAY);
|
||||
gpio_output_set(&out_scl, true);
|
||||
delay_ms(TOGGLE_DELAY);
|
||||
|
||||
if (gpio_input_read(&in_sda)) {
|
||||
recovered = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (recovered) {
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "I2C Bus %s recovered", slave->bus->name);
|
||||
} else {
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "I2C Bus %s still hung after bitbang reset", slave->bus->name);
|
||||
}
|
||||
|
||||
prv_bus_pins_cfg_i2c(slave->bus);
|
||||
prv_bus_reset(slave->bus);
|
||||
|
||||
return recovered;
|
||||
}
|
||||
|
||||
/*--------------------DATA TRANSFER FUNCTIONS--------------------------*/
|
||||
|
||||
//! Wait a short amount of time for busy bit to clear
|
||||
static bool prv_wait_for_not_busy(I2CBus *bus) {
|
||||
static const int WAIT_DELAY = 10; // milliseconds
|
||||
|
||||
if (i2c_hal_is_busy(bus)) {
|
||||
delay_ms(WAIT_DELAY);
|
||||
if (i2c_hal_is_busy(bus)) {
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "Timed out waiting for bus %s to become non-busy", bus->name);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//! Set up and start a transfer to a bus, wait for it to finish and clean up after the transfer
|
||||
//! has completed
|
||||
static bool prv_do_transfer(I2CBus *bus, TransferDirection direction, uint16_t device_address,
|
||||
uint8_t register_address, uint32_t size, uint8_t *data,
|
||||
TransferType type) {
|
||||
if (bus->state->user_count == 0) {
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "Attempted access to disabled bus %s", bus->name);
|
||||
return false;
|
||||
}
|
||||
|
||||
// If bus is busy (it shouldn't be as this function waits for the bus to report a non-idle state
|
||||
// before exiting) reset the bus and wait for it to become not-busy
|
||||
// Exit if bus remains busy. User module should reset the I2C module at this point
|
||||
if (i2c_hal_is_busy(bus)) {
|
||||
prv_bus_reset(bus);
|
||||
|
||||
if (!prv_wait_for_not_busy(bus)) {
|
||||
// Bus did not recover after reset
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "I2C bus did not recover after reset (%s)", bus->name);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Take binary semaphore so that next take will block
|
||||
PBL_ASSERT(prv_semaphore_take(bus->state), "Could not acquire semaphore token");
|
||||
|
||||
// Set up transfer
|
||||
bus->state->transfer = (I2CTransfer) {
|
||||
.device_address = device_address,
|
||||
.register_address = register_address,
|
||||
.direction = direction,
|
||||
.type = type,
|
||||
.size = size,
|
||||
.idx = 0,
|
||||
.data = data,
|
||||
};
|
||||
|
||||
i2c_hal_init_transfer(bus);
|
||||
|
||||
bus->state->transfer_nack_count = 0;
|
||||
|
||||
bool result = false;
|
||||
bool complete = false;
|
||||
do {
|
||||
i2c_hal_start_transfer(bus);
|
||||
|
||||
// Wait on semaphore until it is released by interrupt or a timeout occurs
|
||||
if (prv_semaphore_wait(bus->state)) {
|
||||
if ((bus->state->transfer_event == I2CTransferEvent_TransferComplete) ||
|
||||
(bus->state->transfer_event == I2CTransferEvent_Error)) {
|
||||
// Track the max transfer duration so we can keep tabs on the MFi chip's nacking behavior
|
||||
|
||||
if (bus->state->transfer_event == I2CTransferEvent_Error) {
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "I2C Error on bus %s", bus->name);
|
||||
}
|
||||
complete = true;
|
||||
result = (bus->state->transfer_event == I2CTransferEvent_TransferComplete);
|
||||
} else if (bus->state->transfer_nack_count < I2C_NACK_COUNT_MAX) {
|
||||
// NACK received after start condition sent: the MFI chip NACKs start conditions whilst it
|
||||
// is busy
|
||||
// Retry start condition after a short delay.
|
||||
// A NACK count is incremented for each NACK received, so that legitimate NACK
|
||||
// errors cause the transfer to be aborted (after the NACK count max has been reached).
|
||||
|
||||
bus->state->transfer_nack_count++;
|
||||
|
||||
// Wait 1-2ms:
|
||||
delay_ms(2);
|
||||
|
||||
} else {
|
||||
// Too many NACKs received, abort transfer
|
||||
i2c_hal_abort_transfer(bus);
|
||||
complete = true;
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "I2C Error: too many NACKs received on bus %s", bus->name);
|
||||
break;
|
||||
}
|
||||
|
||||
} else {
|
||||
// Timeout, abort transfer
|
||||
i2c_hal_abort_transfer(bus);
|
||||
complete = true;
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "Transfer timed out on bus %s", bus->name);
|
||||
break;
|
||||
}
|
||||
} while (!complete);
|
||||
|
||||
// Return semaphore token so another transfer can be started
|
||||
prv_semaphore_give(bus->state);
|
||||
|
||||
// Wait for bus to to clear the busy flag before a new transfer starts
|
||||
// Theoretically a transfer could complete successfully, but the busy flag never clears,
|
||||
// which would cause the next transfer to fail
|
||||
if (!prv_wait_for_not_busy(bus)) {
|
||||
// Reset I2C bus if busy flag does not clear
|
||||
prv_bus_reset(bus);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool i2c_read_register(I2CSlavePort *slave, uint8_t register_address, uint8_t *result) {
|
||||
return i2c_read_register_block(slave, register_address, 1, result);
|
||||
}
|
||||
|
||||
bool i2c_read_register_block(I2CSlavePort *slave, uint8_t register_address_start,
|
||||
uint32_t read_size, uint8_t* result_buffer) {
|
||||
PBL_ASSERTN(slave);
|
||||
PBL_ASSERTN(result_buffer);
|
||||
// Do transfer locks the bus
|
||||
bool result = prv_do_transfer(slave->bus, Read, slave->address, register_address_start, read_size,
|
||||
result_buffer, SendRegisterAddress);
|
||||
|
||||
if (!result) {
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "Read failed on bus %s", slave->bus->name);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool i2c_read_block(I2CSlavePort *slave, uint32_t read_size, uint8_t* result_buffer) {
|
||||
PBL_ASSERTN(slave);
|
||||
PBL_ASSERTN(result_buffer);
|
||||
|
||||
bool result = prv_do_transfer(slave->bus, Read, slave->address, 0, read_size, result_buffer,
|
||||
NoRegisterAddress);
|
||||
|
||||
if (!result) {
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "Block read failed on bus %s", slave->bus->name);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool i2c_write_register(I2CSlavePort *slave, uint8_t register_address, uint8_t value) {
|
||||
return i2c_write_register_block(slave, register_address, 1, &value);
|
||||
}
|
||||
|
||||
bool i2c_write_register_block(I2CSlavePort *slave, uint8_t register_address_start,
|
||||
uint32_t write_size, const uint8_t* buffer) {
|
||||
PBL_ASSERTN(slave);
|
||||
PBL_ASSERTN(buffer);
|
||||
// Do transfer locks the bus
|
||||
bool result = prv_do_transfer(slave->bus, Write, slave->address, register_address_start,
|
||||
write_size, (uint8_t*)buffer, SendRegisterAddress);
|
||||
|
||||
if (!result) {
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "Write failed on bus %s", slave->bus->name);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool i2c_write_block(I2CSlavePort *slave, uint32_t write_size, const uint8_t* buffer) {
|
||||
PBL_ASSERTN(slave);
|
||||
PBL_ASSERTN(buffer);
|
||||
|
||||
// Do transfer locks the bus
|
||||
bool result = prv_do_transfer(slave->bus, Write, slave->address, 0, write_size, (uint8_t*)buffer,
|
||||
NoRegisterAddress);
|
||||
|
||||
if (!result) {
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "Block write failed on bus %s", slave->bus->name);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*----------------------HAL INTERFACE--------------------------------*/
|
||||
|
||||
void i2c_handle_transfer_event(I2CBus *bus, I2CTransferEvent event) {
|
||||
bus->state->transfer_event = event;
|
||||
prv_semaphore_give_from_isr(bus->state);
|
||||
}
|
95
platform/robert/boot/src/drivers/i2c/i2c_definitions.h
Normal file
95
platform/robert/boot/src/drivers/i2c/i2c_definitions.h
Normal file
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef enum I2CTransferEvent {
|
||||
I2CTransferEvent_Timeout,
|
||||
I2CTransferEvent_TransferComplete,
|
||||
I2CTransferEvent_NackReceived,
|
||||
I2CTransferEvent_Error,
|
||||
} I2CTransferEvent;
|
||||
|
||||
typedef enum {
|
||||
I2CTransferDirection_Read,
|
||||
I2CTransferDirection_Write
|
||||
} I2CTransferDirection;
|
||||
|
||||
typedef enum {
|
||||
// Send a register address, followed by a repeat start for reads
|
||||
I2CTransferType_SendRegisterAddress,
|
||||
|
||||
// Do not send a register address; used for block writes/reads
|
||||
I2CTransferType_NoRegisterAddress
|
||||
} I2CTransferType;
|
||||
|
||||
typedef enum I2CTransferState {
|
||||
I2CTransferState_WriteAddressTx,
|
||||
I2CTransferState_WriteRegAddress,
|
||||
I2CTransferState_RepeatStart,
|
||||
I2CTransferState_WriteAddressRx,
|
||||
I2CTransferState_WaitForData,
|
||||
I2CTransferState_ReadData,
|
||||
I2CTransferState_WriteData,
|
||||
I2CTransferState_EndWrite,
|
||||
I2CTransferState_Complete,
|
||||
} I2CTransferState;
|
||||
|
||||
typedef struct I2CTransfer {
|
||||
I2CTransferState state;
|
||||
uint16_t device_address;
|
||||
I2CTransferDirection direction;
|
||||
I2CTransferType type;
|
||||
uint8_t register_address;
|
||||
uint32_t size;
|
||||
uint32_t idx;
|
||||
uint8_t *data;
|
||||
} I2CTransfer;
|
||||
|
||||
typedef struct I2CBusState {
|
||||
I2CTransfer transfer;
|
||||
I2CTransferEvent transfer_event;
|
||||
int transfer_nack_count;
|
||||
int user_count;
|
||||
volatile bool busy;
|
||||
} I2CBusState;
|
||||
|
||||
struct I2CBus {
|
||||
I2CBusState *const state;
|
||||
const struct I2CBusHal *const hal;
|
||||
AfConfig scl_gpio; ///< Alternate Function configuration for SCL pin
|
||||
AfConfig sda_gpio; ///< Alternate Function configuration for SDA pin
|
||||
OutputConfig rail_gpio; ///< Control pin for rail
|
||||
void (* const rail_ctl_fn)(I2CBus *device, bool enabled); ///< Control function for this rail.
|
||||
const char *name; //! Device ID for logging purposes
|
||||
};
|
||||
|
||||
struct I2CSlavePort {
|
||||
const I2CBus *bus;
|
||||
uint16_t address;
|
||||
};
|
||||
|
||||
//! Initialize the I2C driver.
|
||||
void i2c_init(I2CBus *bus);
|
||||
|
||||
//! Transfer event handler implemented in i2c.c and called by HAL implementation
|
||||
void i2c_handle_transfer_event(I2CBus *device, I2CTransferEvent event);
|
||||
|
||||
#define I2C_DEBUG(fmt, args...) \
|
||||
PBL_LOG_COLOR_D(LOG_DOMAIN_I2C, LOG_LEVEL_DEBUG, LOG_COLOR_LIGHT_MAGENTA, fmt, ## args)
|
336
platform/robert/boot/src/drivers/i2c/i2c_hal.c
Normal file
336
platform/robert/boot/src/drivers/i2c/i2c_hal.c
Normal file
|
@ -0,0 +1,336 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "i2c_hal.h"
|
||||
#include "i2c_definitions.h"
|
||||
#include "i2c_hal_definitions.h"
|
||||
|
||||
#include "drivers/periph_config.h"
|
||||
#include "system/logging.h"
|
||||
#include "system/passert.h"
|
||||
#include "util/attributes.h"
|
||||
|
||||
#include "stm32f7xx.h"
|
||||
|
||||
#define I2C_IRQ_PRIORITY (0xc)
|
||||
#define I2C_NORMAL_MODE_CLOCK_SPEED_MAX (100000)
|
||||
#define I2C_FAST_MODE_CLOCK_SPEED_MAX (400000)
|
||||
#define I2C_FAST_MODE_PLUS_CLOCK_SPEED_MAX (1000000)
|
||||
|
||||
#define TIMINGR_MASK_PRESC (0x0F)
|
||||
#define TIMINGR_MASK_SCLH (0xFF)
|
||||
#define TIMINGR_MASK_SCLL (0xFF)
|
||||
|
||||
#define CR1_CLEAR_MASK (0x00CFE0FF)
|
||||
|
||||
#define CR2_CLEAR_MASK (0x07FF7FFF)
|
||||
#define CR2_NBYTES_OFFSET (16)
|
||||
#define CR2_TRANSFER_SETUP_MASK (I2C_CR2_SADD | I2C_CR2_NBYTES | I2C_CR2_RELOAD | \
|
||||
I2C_CR2_AUTOEND | I2C_CR2_RD_WRN | I2C_CR2_START | \
|
||||
I2C_CR2_STOP)
|
||||
|
||||
typedef union PACKED TIMINGR {
|
||||
struct {
|
||||
int32_t SCLL:8;
|
||||
int32_t SCLH:8;
|
||||
int32_t SDADEL:4;
|
||||
int32_t SCLDEL:4;
|
||||
int32_t reserved:4;
|
||||
int32_t PRESC:4;
|
||||
};
|
||||
int32_t reg;
|
||||
} TIMINGR;
|
||||
|
||||
static void prv_i2c_deinit(I2CBus *bus) {
|
||||
// Reset the clock to the peripheral
|
||||
RCC_APB1PeriphResetCmd(bus->hal->clock_ctrl, ENABLE);
|
||||
RCC_APB1PeriphResetCmd(bus->hal->clock_ctrl, DISABLE);
|
||||
}
|
||||
|
||||
void i2c_hal_init(I2CBus *bus) {
|
||||
NVIC_SetPriority(bus->hal->ev_irq_channel, I2C_IRQ_PRIORITY);
|
||||
NVIC_SetPriority(bus->hal->er_irq_channel, I2C_IRQ_PRIORITY);
|
||||
NVIC_EnableIRQ(bus->hal->ev_irq_channel);
|
||||
NVIC_EnableIRQ(bus->hal->er_irq_channel);
|
||||
prv_i2c_deinit(bus);
|
||||
}
|
||||
|
||||
static void prv_i2c_init(I2C_TypeDef *i2c, TIMINGR timingr) {
|
||||
// Soft reset of the state machine and status bits by disabling the peripheral.
|
||||
// Note: PE must be low for 3 APB cycles after this is done for the reset to be successful
|
||||
i2c->CR1 &= ~I2C_CR1_PE;
|
||||
|
||||
i2c->CR1 &= ~CR1_CLEAR_MASK;
|
||||
|
||||
// Set the timing register
|
||||
i2c->TIMINGR = timingr.reg;
|
||||
|
||||
// I2C only used as a master; disable slave address acknowledgement
|
||||
i2c->OAR1 = 0;
|
||||
i2c->OAR2 = 0;
|
||||
|
||||
// Enable i2c Peripheral; clear any configured interrupt bits; use analog filter
|
||||
i2c->CR1 |= I2C_CR1_PE;
|
||||
|
||||
// Clear CR2, making it ready for the next transaction
|
||||
i2c->CR2 &= ~CR2_CLEAR_MASK;
|
||||
}
|
||||
|
||||
void i2c_hal_enable(I2CBus *bus) {
|
||||
// We don't need to support Fast Mode Plus yet, so make sure the desired clock speed is less than
|
||||
// the maximum Fast Mode clock speed.
|
||||
// When Fast Mode support is added the duty-cycle settings will probably have to be re-thought.
|
||||
PBL_ASSERT(bus->hal->clock_speed <= I2C_FAST_MODE_CLOCK_SPEED_MAX,
|
||||
"Fast Mode Plus not yet supported");
|
||||
|
||||
uint32_t duty_cycle_low = 1;
|
||||
uint32_t duty_cycle_high = 1;
|
||||
if (bus->hal->clock_speed > I2C_NORMAL_MODE_CLOCK_SPEED_MAX) { // Fast mode
|
||||
if (bus->hal->duty_cycle == I2CDutyCycle_16_9) {
|
||||
duty_cycle_low = 16;
|
||||
duty_cycle_high = 9;
|
||||
} else if (bus->hal->duty_cycle == I2CDutyCycle_2) {
|
||||
duty_cycle_low = 2;
|
||||
duty_cycle_high = 1;
|
||||
} else {
|
||||
WTF; // It might be possible to encode a duty cycle differently from the legacy I2C, if it's
|
||||
// ever necessary. Currently it's not, so just maintain the previous implementation
|
||||
}
|
||||
}
|
||||
|
||||
RCC_ClocksTypeDef rcc_clocks;
|
||||
RCC_GetClocksFreq(&rcc_clocks);
|
||||
|
||||
uint32_t prescaler = rcc_clocks.PCLK1_Frequency /
|
||||
(bus->hal->clock_speed * (duty_cycle_low + duty_cycle_high));
|
||||
if ((rcc_clocks.PCLK1_Frequency %
|
||||
(bus->hal->clock_speed * (duty_cycle_low + duty_cycle_high))) == 0) {
|
||||
// Prescaler is PRESC + 1. This subtracts one so that exact dividers are correct, but if there
|
||||
// is an integer remainder, the prescaler will ensure that the clock frequency is within spec.
|
||||
prescaler -= 1;
|
||||
}
|
||||
// Make sure all the values fit in their corresponding fields
|
||||
PBL_ASSERTN((duty_cycle_low <= TIMINGR_MASK_SCLL) &&
|
||||
(duty_cycle_high <= TIMINGR_MASK_SCLH) &&
|
||||
(prescaler <= TIMINGR_MASK_PRESC));
|
||||
|
||||
periph_config_enable(bus->hal->i2c, bus->hal->clock_ctrl);
|
||||
|
||||
// We currently don't need to worry about the other TIMINGR fields (they come out to 0), but might
|
||||
// need to revisit this if we ever need FM+ speeds.
|
||||
TIMINGR timingr = {
|
||||
.PRESC = prescaler,
|
||||
.SCLH = duty_cycle_high - 1, // Duty cycle high is SCLH + 1
|
||||
.SCLL = duty_cycle_low - 1, // Duty cycle low is SCLL + 1
|
||||
};
|
||||
prv_i2c_init(bus->hal->i2c, timingr);
|
||||
}
|
||||
|
||||
void i2c_hal_disable(I2CBus *bus) {
|
||||
periph_config_disable(bus->hal->i2c, bus->hal->clock_ctrl);
|
||||
prv_i2c_deinit(bus);
|
||||
}
|
||||
|
||||
bool i2c_hal_is_busy(I2CBus *bus) {
|
||||
return ((bus->hal->i2c->ISR & I2C_ISR_BUSY) != 0);
|
||||
}
|
||||
|
||||
static void prv_disable_all_interrupts(I2CBus *bus) {
|
||||
bus->hal->i2c->CR1 &= ~(I2C_CR1_TXIE |
|
||||
I2C_CR1_RXIE |
|
||||
I2C_CR1_TCIE |
|
||||
I2C_CR1_NACKIE |
|
||||
I2C_CR1_ERRIE);
|
||||
}
|
||||
|
||||
void i2c_hal_abort_transfer(I2CBus *bus) {
|
||||
// Disable all interrupts on the bus
|
||||
prv_disable_all_interrupts(bus);
|
||||
// Generate a stop condition
|
||||
bus->hal->i2c->CR2 |= I2C_CR2_STOP;
|
||||
}
|
||||
|
||||
void i2c_hal_init_transfer(I2CBus *bus) {
|
||||
I2CTransfer *transfer = &bus->state->transfer;
|
||||
|
||||
if (transfer->type == I2CTransferType_SendRegisterAddress) {
|
||||
transfer->state = I2CTransferState_WriteRegAddress;
|
||||
} else {
|
||||
if (transfer->direction == I2CTransferDirection_Read) {
|
||||
transfer->state = I2CTransferState_ReadData;
|
||||
} else {
|
||||
transfer->state = I2CTransferState_WriteData;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void prv_enable_interrupts(I2CBus *bus) {
|
||||
bus->hal->i2c->CR1 |= I2C_CR1_ERRIE | // Enable error interrupt
|
||||
I2C_CR1_NACKIE | // Enable NACK interrupt
|
||||
I2C_CR1_TCIE | // Enable transfer complete interrupt
|
||||
I2C_CR1_TXIE; // Enable transmit interrupt
|
||||
if (bus->state->transfer.direction == I2CTransferDirection_Read) {
|
||||
bus->hal->i2c->CR1 |= I2C_CR1_RXIE; // Enable receive interrupt
|
||||
}
|
||||
}
|
||||
|
||||
static void prv_resume_transfer(I2CBus *bus, bool generate_start) {
|
||||
const I2CTransfer *transfer = &bus->state->transfer;
|
||||
uint32_t cr2_value = transfer->device_address & I2C_CR2_SADD;
|
||||
|
||||
if ((transfer->direction == I2CTransferDirection_Read) &&
|
||||
(transfer->state != I2CTransferState_WriteRegAddress)) {
|
||||
cr2_value |= I2C_CR2_RD_WRN;
|
||||
}
|
||||
|
||||
const uint32_t remaining = bus->state->transfer.size - bus->state->transfer.idx;
|
||||
if (remaining > UINT8_MAX) {
|
||||
cr2_value |= I2C_CR2_RELOAD;
|
||||
cr2_value |= I2C_CR2_NBYTES;
|
||||
} else {
|
||||
cr2_value |= (remaining << CR2_NBYTES_OFFSET) & I2C_CR2_NBYTES;
|
||||
}
|
||||
|
||||
if (generate_start) {
|
||||
cr2_value |= I2C_CR2_START;
|
||||
}
|
||||
|
||||
bus->hal->i2c->CR2 = cr2_value;
|
||||
}
|
||||
|
||||
void i2c_hal_start_transfer(I2CBus *bus) {
|
||||
prv_enable_interrupts(bus);
|
||||
if (bus->state->transfer.state == I2CTransferState_WriteRegAddress) {
|
||||
// For writes, we'll reload with the payload once we send the address. Otherwise, we'd need to
|
||||
// send a repeated start, which we don't want to do.
|
||||
const bool reload = bus->state->transfer.direction == I2CTransferDirection_Write;
|
||||
bus->hal->i2c->CR2 = (bus->state->transfer.device_address & I2C_CR2_SADD) |
|
||||
(1 << CR2_NBYTES_OFFSET) |
|
||||
(reload ? I2C_CR2_RELOAD : 0) |
|
||||
I2C_CR2_START;
|
||||
} else {
|
||||
prv_resume_transfer(bus, true /* generate_start */);
|
||||
}
|
||||
}
|
||||
|
||||
/*------------------------INTERRUPT FUNCTIONS--------------------------*/
|
||||
static void prv_end_transfer_irq(I2CBus *bus, I2CTransferEvent event) {
|
||||
prv_disable_all_interrupts(bus);
|
||||
|
||||
// Generate stop condition
|
||||
bus->hal->i2c->CR2 |= I2C_CR2_STOP;
|
||||
bus->state->transfer.state = I2CTransferState_Complete;
|
||||
|
||||
i2c_handle_transfer_event(bus, event);
|
||||
}
|
||||
|
||||
//! Handle an IRQ event on the specified \a bus
|
||||
static void prv_event_irq_handler(I2CBus *bus) {
|
||||
I2C_TypeDef *i2c = bus->hal->i2c;
|
||||
I2CTransfer *transfer = &bus->state->transfer;
|
||||
switch (transfer->state) {
|
||||
case I2CTransferState_WriteRegAddress:
|
||||
if ((i2c->ISR & I2C_ISR_TXIS) != 0) {
|
||||
i2c->TXDR = transfer->register_address;
|
||||
}
|
||||
if ((transfer->direction == I2CTransferDirection_Read) && (i2c->ISR & I2C_ISR_TC)) {
|
||||
// done writing the register address for a read request - start a read request
|
||||
transfer->state = I2CTransferState_ReadData;
|
||||
prv_resume_transfer(bus, true /* generate_start */);
|
||||
} else if ((transfer->direction == I2CTransferDirection_Write) && (i2c->ISR & I2C_ISR_TCR)) {
|
||||
// done writing the register address for a write request - "reload" the write payload
|
||||
transfer->state = I2CTransferState_WriteData;
|
||||
prv_resume_transfer(bus, false /* !generate_start */);
|
||||
}
|
||||
if ((i2c->ISR & I2C_ISR_NACKF) != 0) {
|
||||
i2c->ICR |= I2C_ICR_NACKCF;
|
||||
i2c_handle_transfer_event(bus, I2CTransferEvent_NackReceived);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case I2CTransferState_ReadData:
|
||||
if ((i2c->ISR & I2C_ISR_RXNE) != 0) {
|
||||
transfer->data[transfer->idx++] = i2c->RXDR;
|
||||
}
|
||||
if ((i2c->ISR & I2C_ISR_TCR) != 0) {
|
||||
prv_resume_transfer(bus, false /* !generate_start */);
|
||||
}
|
||||
if ((i2c->ISR & I2C_ISR_TC) != 0) {
|
||||
prv_end_transfer_irq(bus, I2CTransferEvent_TransferComplete);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case I2CTransferState_WriteData:
|
||||
if ((i2c->ISR & I2C_ISR_TXIS) != 0) {
|
||||
i2c->TXDR = transfer->data[transfer->idx++];
|
||||
}
|
||||
if ((i2c->ISR & I2C_ISR_NACKF) != 0) {
|
||||
i2c->ICR |= I2C_ICR_NACKCF;
|
||||
return i2c_handle_transfer_event(bus, I2CTransferEvent_NackReceived);
|
||||
}
|
||||
if ((i2c->ISR & I2C_ISR_TCR) != 0) {
|
||||
prv_resume_transfer(bus, false /* !generate_start */);
|
||||
}
|
||||
if ((i2c->ISR & I2C_ISR_TC) != 0) {
|
||||
prv_end_transfer_irq(bus, I2CTransferEvent_TransferComplete);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case I2CTransferState_Complete:
|
||||
if (i2c->ISR & I2C_ISR_TXE) {
|
||||
// We seem to get a spurious interrupt after the last byte is sent
|
||||
// There is no bit to specifically disable this interrupt and the interrupt may have already
|
||||
// been pended when we would disable it, so just handle it silently.
|
||||
break;
|
||||
}
|
||||
// Fallthrough
|
||||
|
||||
// These extra states were defined for the F4 implementation but are not necessary for the F7,
|
||||
// because the interrupt scheme is a lot nicer.
|
||||
case I2CTransferState_RepeatStart:
|
||||
case I2CTransferState_EndWrite:
|
||||
case I2CTransferState_WaitForData:
|
||||
case I2CTransferState_WriteAddressRx:
|
||||
case I2CTransferState_WriteAddressTx:
|
||||
default:
|
||||
WTF;
|
||||
}
|
||||
}
|
||||
|
||||
static void prv_error_irq_handler(I2CBus *bus) {
|
||||
I2C_TypeDef *i2c = bus->hal->i2c;
|
||||
if ((i2c->ISR & I2C_ISR_BERR) != 0) {
|
||||
i2c->ICR |= I2C_ICR_BERRCF;
|
||||
}
|
||||
if ((i2c->ISR & I2C_ISR_OVR) != 0) {
|
||||
i2c->ICR |= I2C_ICR_OVRCF;
|
||||
}
|
||||
if ((i2c->ISR & I2C_ISR_ARLO) != 0) {
|
||||
i2c->ICR |= I2C_ICR_ARLOCF;
|
||||
}
|
||||
prv_end_transfer_irq(bus, I2CTransferEvent_Error);
|
||||
}
|
||||
|
||||
void i2c_hal_event_irq_handler(I2CBus *bus) {
|
||||
prv_event_irq_handler(bus);
|
||||
}
|
||||
|
||||
void i2c_hal_error_irq_handler(I2CBus *bus) {
|
||||
prv_error_irq_handler(bus);
|
||||
}
|
35
platform/robert/boot/src/drivers/i2c/i2c_hal.h
Normal file
35
platform/robert/boot/src/drivers/i2c/i2c_hal.h
Normal file
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "board/board.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
void i2c_hal_init(I2CBus *bus);
|
||||
|
||||
void i2c_hal_enable(I2CBus *bus);
|
||||
|
||||
void i2c_hal_disable(I2CBus *bus);
|
||||
|
||||
bool i2c_hal_is_busy(I2CBus *bus);
|
||||
|
||||
void i2c_hal_abort_transfer(I2CBus *bus);
|
||||
|
||||
void i2c_hal_init_transfer(I2CBus *bus);
|
||||
|
||||
void i2c_hal_start_transfer(I2CBus *bus);
|
37
platform/robert/boot/src/drivers/i2c/i2c_hal_definitions.h
Normal file
37
platform/robert/boot/src/drivers/i2c/i2c_hal_definitions.h
Normal file
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef enum I2CDutyCycle {
|
||||
I2CDutyCycle_16_9,
|
||||
I2CDutyCycle_2
|
||||
} I2CDutyCycle;
|
||||
|
||||
typedef struct I2CBusHal {
|
||||
I2C_TypeDef *const i2c;
|
||||
uint32_t clock_ctrl; ///< Peripheral clock control flag
|
||||
uint32_t clock_speed; ///< Bus clock speed
|
||||
I2CDutyCycle duty_cycle; ///< Bus clock duty cycle in fast mode
|
||||
IRQn_Type ev_irq_channel; ///< I2C Event interrupt (One of X_IRQn). For example, I2C1_EV_IRQn.
|
||||
IRQn_Type er_irq_channel; ///< I2C Error interrupt (One of X_IRQn). For example, I2C1_ER_IRQn.
|
||||
} I2CBusHal;
|
||||
|
||||
void i2c_hal_event_irq_handler(I2CBus *device);
|
||||
void i2c_hal_error_irq_handler(I2CBus *device);
|
331
platform/robert/boot/src/drivers/max14690_pmic.c
Normal file
331
platform/robert/boot/src/drivers/max14690_pmic.c
Normal file
|
@ -0,0 +1,331 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/* This file should probably go in the stm32f4 folder */
|
||||
|
||||
#include "drivers/pmic.h"
|
||||
|
||||
#include "board/board.h"
|
||||
#include "drivers/dbgserial.h"
|
||||
#include "drivers/gpio.h"
|
||||
#include "drivers/i2c.h"
|
||||
#include "drivers/periph_config.h"
|
||||
#include "system/logging.h"
|
||||
#include "system/passert.h"
|
||||
#include "util/delay.h"
|
||||
|
||||
#include "stm32f7xx.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define MAX14690_ADDR (0x50)
|
||||
|
||||
#define MAX14690_WHOAMI (0x01)
|
||||
|
||||
//! The addresses of the registers that we can read using i2c
|
||||
typedef enum PmicRegisters {
|
||||
PmicRegisters_CHIP_ID = 0x00,
|
||||
PmicRegisters_CHIP_REV = 0x01,
|
||||
PmicRegisters_STATUSA = 0x02,
|
||||
PmicRegisters_STATUSB = 0x03,
|
||||
PmicRegisters_INTA = 0x05,
|
||||
PmicRegisters_INTB = 0x06,
|
||||
PmicRegisters_INT_MASK_A = 0x07,
|
||||
PmicRegisters_INT_MASK_B = 0x08,
|
||||
PmicRegisters_CHG_CNTL_A = 0x0A,
|
||||
PmicRegisters_BUCK1_CONFIG = 0x0D,
|
||||
PmicRegisters_BUCK2_CONFIG = 0x0F,
|
||||
PmicRegisters_LDO1_CONFIG = 0x12,
|
||||
PmicRegisters_LDO2_CONFIG = 0x14,
|
||||
PmicRegisters_LDO3_CONFIG = 0x16,
|
||||
PmicRegisters_MON_CFG = 0x19,
|
||||
PmicRegisters_PWR_CFG = 0x1F
|
||||
} PmicRegisters;
|
||||
|
||||
//! The different power rails that our PMIC controls
|
||||
typedef enum PmicRail {
|
||||
PmicRail_BUCK1, //!< 1.2V
|
||||
PmicRail_BUCK2, //!< 1.8V
|
||||
PmicRail_LDO1, //!< 2.0V - Auto - RTC
|
||||
PmicRail_LDO2, //!< 3.2V - Manual - FPGA
|
||||
|
||||
//! snowy_bb: 2.5V - Manual - MFi, Magnetometer
|
||||
//! snowy_evt: 1.8V - Manual - MFi
|
||||
PmicRail_LDO3
|
||||
} PmicRail;
|
||||
|
||||
//! Gives configuration information for reading a given rail through the monitor pin.
|
||||
typedef struct {
|
||||
const char* name; //!< Name for the rail.
|
||||
|
||||
//! What ratio we need to divide by in order to bring it into the range we can sense. We can
|
||||
//! only read between 0 and 1.8Vs, so we need to use the PMIC hardware to divide it down before
|
||||
//! sending it to us. Valid values are 1-4.
|
||||
uint8_t ratio;
|
||||
|
||||
//! The binary value we need to put in the register to select the rail.
|
||||
uint8_t source_config;
|
||||
} PmicMonConfig;
|
||||
|
||||
// Using the Binary constants GCC extension here, supported in GCC and Clang
|
||||
// https://gcc.gnu.org/onlinedocs/gcc/Binary-constants.html
|
||||
static const PmicMonConfig MON_CONFIG[] = {
|
||||
{ "+VBAT", 3, 0b001 }, // 3:1
|
||||
};
|
||||
|
||||
static const int PMIC_MON_CONFIG_VBAT_INDEX = 0;
|
||||
|
||||
/* Private Function Definitions */
|
||||
static bool prv_is_alive(void);
|
||||
static void prv_set_pin_config(void);
|
||||
|
||||
//! Request that the rail be used or released. Internally refcounted per rail so you don't have
|
||||
//! to worry about turning this off on another client.
|
||||
static bool prv_update_rail_state(PmicRail rail, bool enable);
|
||||
|
||||
static void prv_mon_config_lock(void) {
|
||||
}
|
||||
|
||||
static void prv_mon_config_unlock(void) {
|
||||
}
|
||||
|
||||
static bool prv_read_register(uint8_t register_address, uint8_t *result) {
|
||||
i2c_use(I2C_MAX14690);
|
||||
bool rv = i2c_read_register(I2C_MAX14690, register_address, result);
|
||||
i2c_release(I2C_MAX14690);
|
||||
return (rv);
|
||||
}
|
||||
|
||||
|
||||
static bool prv_write_register(uint8_t register_address, uint8_t value) {
|
||||
i2c_use(I2C_MAX14690);
|
||||
bool rv = i2c_write_register(I2C_MAX14690, register_address, value);
|
||||
i2c_release(I2C_MAX14690);
|
||||
return (rv);
|
||||
}
|
||||
|
||||
/* Public Functions */
|
||||
bool pmic_init(void) {
|
||||
prv_set_pin_config();
|
||||
|
||||
if (!prv_is_alive()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Power up 3.2V rail
|
||||
prv_update_rail_state(PmicRail_LDO2, true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool prv_update_rail_state(PmicRail rail, bool enable) {
|
||||
static int8_t s_ldo2_ref_count = 0;
|
||||
static int8_t s_ldo3_ref_count = 0;
|
||||
|
||||
int8_t *ref_count;
|
||||
uint8_t rail_control_reg = 0;
|
||||
|
||||
if (rail == PmicRail_LDO2) {
|
||||
rail_control_reg = PmicRegisters_LDO2_CONFIG;
|
||||
ref_count = &s_ldo2_ref_count;
|
||||
} else if (rail == PmicRail_LDO3) {
|
||||
rail_control_reg = PmicRegisters_LDO3_CONFIG;
|
||||
ref_count = &s_ldo3_ref_count;
|
||||
} else {
|
||||
WTF;
|
||||
}
|
||||
|
||||
uint8_t register_value;
|
||||
bool success = prv_read_register(rail_control_reg, ®ister_value);
|
||||
|
||||
if (!success) {
|
||||
// Failed to read the current register value
|
||||
return false;
|
||||
}
|
||||
|
||||
if (enable) {
|
||||
if (*ref_count) {
|
||||
(*ref_count)++;
|
||||
return true;
|
||||
} else {
|
||||
// Set the register byte to XXXXX01X to enable the rail, mask and set
|
||||
register_value = (register_value & ~0x06) | 0x02;
|
||||
|
||||
success = prv_write_register(rail_control_reg, register_value);
|
||||
|
||||
if (success) {
|
||||
// We enabled the rail!
|
||||
*ref_count = 1;
|
||||
|
||||
// We need to wait a bit for the rail to stabilize before continuing to use the device.
|
||||
// It takes 2.6ms for the LDO rails to ramp.
|
||||
delay_ms(3);
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (*ref_count <= 1) {
|
||||
// Set the register byte to XXXXX00X to disable the rail, just mask
|
||||
register_value = (register_value & ~0x06);
|
||||
|
||||
success = prv_write_register(rail_control_reg, register_value);
|
||||
|
||||
if (success) {
|
||||
// We disabled the rail!
|
||||
*ref_count = 0;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
(*ref_count)--;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool pmic_power_off(void) {
|
||||
bool ret = prv_write_register(PmicRegisters_PWR_CFG, 0xB2);
|
||||
|
||||
if (ret) {
|
||||
// Goodbye cruel world. The PMIC should be removing our power at any time now.
|
||||
|
||||
while (1) {}
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool prv_set_mon_config_register(uint8_t value) {
|
||||
return prv_write_register(PmicRegisters_MON_CFG, value);
|
||||
}
|
||||
|
||||
static bool prv_set_mon_config(const PmicMonConfig *config) {
|
||||
const uint8_t ratio_config = 4 - config->ratio; // 4:1 is 0b00, 1:1 is 0b11.
|
||||
|
||||
const uint8_t register_value = (ratio_config << 4) | config->source_config;
|
||||
bool result = prv_set_mon_config_register(register_value);
|
||||
|
||||
// Need to wait a short period of time for the reading to settle due to capacitance on the line.
|
||||
delay_us(200);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool pmic_enable_battery_measure(void) {
|
||||
prv_mon_config_lock();
|
||||
|
||||
return prv_set_mon_config(&MON_CONFIG[PMIC_MON_CONFIG_VBAT_INDEX]);
|
||||
|
||||
// Don't prv_unlock, we don't want anyone else mucking with the mon config until
|
||||
// pmic_disable_battery_measure is called.
|
||||
}
|
||||
|
||||
bool pmic_disable_battery_measure(void) {
|
||||
bool result = prv_set_mon_config_register(0);
|
||||
|
||||
// Releases the lock that was previously aquired in pmic_enable_battery_measure.
|
||||
prv_mon_config_unlock();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool pmic_set_charger_state(bool enable) {
|
||||
// Defaults to ON
|
||||
// Default value is 0xF7
|
||||
const uint8_t register_value = enable ? 0xf7 : 0xf6;
|
||||
|
||||
bool result = prv_write_register(PmicRegisters_CHG_CNTL_A, register_value);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
bool pmic_is_charging(void) {
|
||||
uint8_t val;
|
||||
if (!prv_read_register(PmicRegisters_STATUSA, &val)) {
|
||||
// NOTE: When running on QEMU, i2c reads return false. For now, just assume a failed
|
||||
// i2c read means we are charging
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t chgstat = val & 0x07; // Mask off only charging status
|
||||
|
||||
if (chgstat == 0x02 || // Pre-charge in progress
|
||||
chgstat == 0x03 || // Fast charge, CC
|
||||
chgstat == 0x04 || // Fast charge, CV
|
||||
chgstat == 0x05) { // Maintain charge
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool pmic_is_usb_connected(void) {
|
||||
uint8_t val;
|
||||
if (!prv_read_register(PmicRegisters_STATUSB, &val)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool usb_connected = (val >> 3) & 1;
|
||||
|
||||
return usb_connected;
|
||||
}
|
||||
|
||||
void pmic_read_chip_info(uint8_t *chip_id, uint8_t *chip_revision) {
|
||||
prv_read_register(PmicRegisters_CHIP_ID, chip_id);
|
||||
prv_read_register(PmicRegisters_CHIP_REV, chip_revision);
|
||||
}
|
||||
|
||||
|
||||
/* Private Function Implementations */
|
||||
static bool prv_is_alive(void) {
|
||||
uint8_t val;
|
||||
prv_read_register(0x00, &val);
|
||||
|
||||
if (val == MAX14690_WHOAMI) {
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "Found the max14690");
|
||||
return true;
|
||||
} else {
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "Error reading max14690 WHOAMI byte");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static void prv_set_pin_config(void) {
|
||||
periph_config_acquire_lock();
|
||||
|
||||
// Initialize the GPIOs for the 4V5 & 6V6 rails
|
||||
gpio_output_init(&BOARD_CONFIG_POWER.rail_4V5_ctrl, GPIO_OType_OD, GPIO_Speed_50MHz);
|
||||
if (BOARD_CONFIG_POWER.rail_6V6_ctrl.gpio) {
|
||||
gpio_output_init(&BOARD_CONFIG_POWER.rail_6V6_ctrl, GPIO_OType_OD, GPIO_Speed_50MHz);
|
||||
}
|
||||
gpio_output_init(&BOARD_CONFIG_ACCESSORY.power_en, GPIO_OType_OD, GPIO_Speed_50MHz);
|
||||
|
||||
periph_config_release_lock();
|
||||
}
|
||||
|
||||
void set_4V5_power_state(bool enabled) {
|
||||
gpio_output_set(&BOARD_CONFIG_POWER.rail_4V5_ctrl, enabled);
|
||||
}
|
||||
|
||||
void set_6V6_power_state(bool enabled) {
|
||||
if (BOARD_CONFIG_POWER.rail_6V6_ctrl.gpio) {
|
||||
gpio_output_set(&BOARD_CONFIG_POWER.rail_6V6_ctrl, enabled);
|
||||
}
|
||||
}
|
103
platform/robert/boot/src/drivers/periph_config.c
Normal file
103
platform/robert/boot/src/drivers/periph_config.c
Normal file
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "drivers/periph_config.h"
|
||||
#include "system/logging.h"
|
||||
#include "system/passert.h"
|
||||
|
||||
#include "stm32f7xx.h"
|
||||
|
||||
#define PERIPH_CONFIG_DEBUG 0
|
||||
|
||||
#if PERIPH_CONFIG_DEBUG
|
||||
#define PERIPH_CONFIG_LOG(fmt, args...) \
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, fmt, ## args)
|
||||
#else
|
||||
#define PERIPH_CONFIG_LOG(fmt, args...)
|
||||
#endif
|
||||
|
||||
typedef void (*ClockCmd)(uint32_t periph, FunctionalState state);
|
||||
|
||||
#if PERIPH_CONFIG_DEBUG
|
||||
static const char *prv_string_for_cmd(ClockCmd cmd) {
|
||||
if (cmd == RCC_APB1PeriphClockCmd) {
|
||||
return "APB1";
|
||||
} else if (cmd == RCC_APB2PeriphClockCmd) {
|
||||
return "APB2";
|
||||
} else if (cmd == RCC_AHB1PeriphClockCmd) {
|
||||
return "AHB1";
|
||||
} else if (cmd == RCC_AHB2PeriphClockCmd) {
|
||||
return "AHB2";
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// F(S)MC is the only AHB3 peripheral
|
||||
#ifdef FMC_R_BASE
|
||||
#define AHB3_BASE FMC_R_BASE
|
||||
#else
|
||||
#define AHB3_BASE FSMC_R_BASE
|
||||
#endif
|
||||
|
||||
_Static_assert(APB1PERIPH_BASE < APB2PERIPH_BASE, "Clock mapping assumptions don't hold");
|
||||
_Static_assert(APB2PERIPH_BASE < AHB1PERIPH_BASE, "Clock mapping assumptions don't hold");
|
||||
_Static_assert(AHB1PERIPH_BASE < AHB2PERIPH_BASE, "Clock mapping assumptions don't hold");
|
||||
_Static_assert(AHB2PERIPH_BASE < AHB3_BASE, "Clock mapping assumptions don't hold");
|
||||
|
||||
// Note: this works only with peripheral (<...>Typedef_t *) defines, not with RCC defines
|
||||
static ClockCmd prv_get_clock_cmd(uintptr_t periph_addr) {
|
||||
PBL_ASSERTN(periph_addr >= APB1PERIPH_BASE);
|
||||
if (periph_addr < APB2PERIPH_BASE) {
|
||||
return RCC_APB1PeriphClockCmd;
|
||||
} else if (periph_addr < AHB1PERIPH_BASE) {
|
||||
return RCC_APB2PeriphClockCmd;
|
||||
} else if (periph_addr < AHB2PERIPH_BASE) {
|
||||
return RCC_AHB1PeriphClockCmd;
|
||||
} else if (periph_addr < AHB3_BASE) {
|
||||
return RCC_AHB2PeriphClockCmd;
|
||||
} else {
|
||||
return RCC_AHB3PeriphClockCmd;
|
||||
}
|
||||
}
|
||||
|
||||
void periph_config_init(void) {
|
||||
}
|
||||
|
||||
void periph_config_acquire_lock(void) {
|
||||
}
|
||||
|
||||
void periph_config_release_lock(void) {
|
||||
}
|
||||
|
||||
void periph_config_enable(void *periph, uint32_t rcc_bit) {
|
||||
ClockCmd clock_cmd = prv_get_clock_cmd((uintptr_t)periph);
|
||||
#if PERIPH_CONFIG_DEBUG
|
||||
if (prv_string_for_cmd(clock_cmd))
|
||||
PERIPH_CONFIG_LOG("Enabling clock %s", prv_string_for_cmd(clock_cmd));
|
||||
#endif
|
||||
clock_cmd(rcc_bit, ENABLE);
|
||||
}
|
||||
|
||||
void periph_config_disable(void *periph, uint32_t rcc_bit) {
|
||||
ClockCmd clock_cmd = prv_get_clock_cmd((uintptr_t)periph);
|
||||
#if PERIPH_CONFIG_DEBUG
|
||||
if (prv_string_for_cmd(clock_cmd))
|
||||
PERIPH_CONFIG_LOG("Disabling clock %s", prv_string_for_cmd(clock_cmd));
|
||||
#endif
|
||||
clock_cmd(rcc_bit, DISABLE);
|
||||
}
|
25
platform/robert/boot/src/drivers/periph_config.h
Normal file
25
platform/robert/boot/src/drivers/periph_config.h
Normal file
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
void periph_config_init(void);
|
||||
void periph_config_acquire_lock(void);
|
||||
void periph_config_release_lock(void);
|
||||
void periph_config_enable(void *periph, uint32_t rcc_bit);
|
||||
void periph_config_disable(void *periph, uint32_t rcc_bit);
|
61
platform/robert/boot/src/drivers/pmic.h
Normal file
61
platform/robert/boot/src/drivers/pmic.h
Normal file
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
//! Initialize the PMIC driver. Call this once at startup.
|
||||
bool pmic_init(void);
|
||||
|
||||
//! Tell the PMIC to power off the board and enter a standby-like state. All components will
|
||||
//! have their power removed (except for the RTC so we'll still keep time) and the PMIC itself
|
||||
//! will monitor the buttons for when to wake up.
|
||||
bool pmic_power_off(void);
|
||||
|
||||
//! Enable the battery monitor portion of the PMIC. Remember to turn this off with
|
||||
//! pmic_disable_battery_measure when immediate readings aren't required.
|
||||
bool pmic_enable_battery_measure(void);
|
||||
|
||||
//! Disable the battery monitor portion of the PMIC.
|
||||
bool pmic_disable_battery_measure(void);
|
||||
|
||||
//! Enable and disable the charging portion of the PMIC.
|
||||
bool pmic_set_charger_state(bool enable);
|
||||
|
||||
//! @return true if the PMIC thinks we're charging (adding additional charge to the battery).
|
||||
//! Note that once we hit full charge we'll no longer be charging, which is a different state
|
||||
//! that pmic_is_usb_connected.
|
||||
bool pmic_is_charging(void);
|
||||
|
||||
//! @return true if a usb-ish charger cable is currently connected.
|
||||
bool pmic_is_usb_connected(void);
|
||||
|
||||
//! Read information about the chip for tracking purposes.
|
||||
void pmic_read_chip_info(uint8_t *chip_id, uint8_t *chip_revision);
|
||||
|
||||
//! Enables the LDO3 power rail. Used for the MFi/Magnetometer on snowy_bb, MFi on snowy_evt.
|
||||
void set_ldo3_power_state(bool enabled);
|
||||
|
||||
//! Enables the 4.5V power rail. Used for the display on snowy.
|
||||
void set_4V5_power_state(bool enabled);
|
||||
|
||||
//! Enables the 6.6V power rail. Used for the display on snowy.
|
||||
void set_6V6_power_state(bool enabled);
|
||||
|
||||
//! Enables power to the accessory connector.
|
||||
void set_accessory_power_state(bool enabled);
|
41
platform/robert/boot/src/drivers/pwr.c
Normal file
41
platform/robert/boot/src/drivers/pwr.c
Normal file
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "drivers/pwr.h"
|
||||
|
||||
#include "drivers/periph_config.h"
|
||||
|
||||
#include "stm32f7xx.h"
|
||||
|
||||
void pwr_access_backup_domain(bool enable_access) {
|
||||
periph_config_enable(PWR, RCC_APB1Periph_PWR);
|
||||
if (enable_access) {
|
||||
__atomic_or_fetch(&PWR->CR1, PWR_CR1_DBP, __ATOMIC_RELAXED);
|
||||
} else {
|
||||
__atomic_and_fetch(&PWR->CR1, ~PWR_CR1_DBP, __ATOMIC_RELAXED);
|
||||
}
|
||||
periph_config_disable(PWR, RCC_APB1Periph_PWR);
|
||||
}
|
||||
|
||||
|
||||
bool pwr_did_boot_from_standby(void) {
|
||||
bool result = (PWR->CSR1 & PWR_CSR1_SBF) != 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
void pwr_clear_boot_from_standby_flag(void) {
|
||||
PWR->CR1 |= PWR_CR1_CSBF;
|
||||
}
|
25
platform/robert/boot/src/drivers/pwr.h
Normal file
25
platform/robert/boot/src/drivers/pwr.h
Normal file
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
void pwr_access_backup_domain(bool enable_access);
|
||||
|
||||
bool pwr_did_boot_from_standby(void);
|
||||
|
||||
void pwr_clear_boot_from_standby_flag(void);
|
109
platform/robert/boot/src/drivers/system_flash.c
Normal file
109
platform/robert/boot/src/drivers/system_flash.c
Normal file
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "drivers/system_flash.h"
|
||||
|
||||
#include "drivers/dbgserial.h"
|
||||
#include "util/misc.h"
|
||||
|
||||
#include "stm32f7xx.h"
|
||||
|
||||
static uint16_t s_sectors[] = {
|
||||
FLASH_Sector_0, FLASH_Sector_1, FLASH_Sector_2, FLASH_Sector_3,
|
||||
FLASH_Sector_4, FLASH_Sector_5, FLASH_Sector_6, FLASH_Sector_7,
|
||||
FLASH_Sector_8, FLASH_Sector_9, FLASH_Sector_10, FLASH_Sector_11
|
||||
};
|
||||
static uint32_t s_sector_addresses[] = {
|
||||
ADDR_FLASH_SECTOR_0, ADDR_FLASH_SECTOR_1, ADDR_FLASH_SECTOR_2, ADDR_FLASH_SECTOR_3,
|
||||
ADDR_FLASH_SECTOR_4, ADDR_FLASH_SECTOR_5, ADDR_FLASH_SECTOR_6, ADDR_FLASH_SECTOR_7,
|
||||
ADDR_FLASH_SECTOR_8, ADDR_FLASH_SECTOR_9, ADDR_FLASH_SECTOR_10, ADDR_FLASH_SECTOR_11
|
||||
};
|
||||
|
||||
static int prv_get_sector_num_for_address(uint32_t address) {
|
||||
if (address < s_sector_addresses[0]) {
|
||||
dbgserial_print("address ");
|
||||
dbgserial_print_hex(address);
|
||||
dbgserial_putstr(" is outside system flash");
|
||||
return -1;
|
||||
}
|
||||
for (size_t i=0; i < ARRAY_LENGTH(s_sector_addresses)-1; ++i) {
|
||||
if (s_sector_addresses[i] <= address
|
||||
&& address < s_sector_addresses[i+1]) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return ARRAY_LENGTH(s_sector_addresses)-1;
|
||||
}
|
||||
|
||||
bool system_flash_erase(uint32_t address, size_t length, SystemFlashProgressCb progress_callback,
|
||||
void *progress_context) {
|
||||
if (length == 0) {
|
||||
// Nothing to do
|
||||
return true;
|
||||
}
|
||||
|
||||
int first_sector = prv_get_sector_num_for_address(address);
|
||||
int last_sector = prv_get_sector_num_for_address(address + length - 1);
|
||||
if (first_sector < 0 || last_sector < 0) {
|
||||
return false;
|
||||
}
|
||||
int count = last_sector - first_sector + 1;
|
||||
if (progress_callback) {
|
||||
progress_callback(0, count, progress_context);
|
||||
}
|
||||
|
||||
FLASH_Unlock();
|
||||
for (int sector = first_sector; sector <= last_sector; ++sector) {
|
||||
FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR |
|
||||
FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR | FLASH_FLAG_PGSERR);
|
||||
if (FLASH_EraseSector(s_sectors[sector], VoltageRange_1) != FLASH_COMPLETE) {
|
||||
dbgserial_print("failed to erase sector ");
|
||||
dbgserial_print_hex(sector);
|
||||
dbgserial_newline();
|
||||
FLASH_Lock();
|
||||
return false;
|
||||
}
|
||||
if (progress_callback) {
|
||||
progress_callback(sector - first_sector + 1, count, progress_context);
|
||||
}
|
||||
}
|
||||
FLASH_Lock();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool system_flash_write(uint32_t address, const void *data, size_t length) {
|
||||
// enable programming of flash
|
||||
FLASH_Unlock();
|
||||
// clear errors
|
||||
FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR |
|
||||
FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR | FLASH_FLAG_PGSERR);
|
||||
|
||||
const uint8_t *data_array = data;
|
||||
for (uint32_t i = 0; i < length; ++i) {
|
||||
// wait till the previous operation finished
|
||||
if (FLASH_ProgramByte(address + i, data_array[i]) != FLASH_COMPLETE) {
|
||||
dbgserial_print("Program failed @");
|
||||
dbgserial_print_hex(address + i);
|
||||
dbgserial_newline();
|
||||
FLASH_Lock();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// disable programming of flash
|
||||
FLASH_Lock();
|
||||
return true;
|
||||
}
|
60
platform/robert/boot/src/drivers/system_flash.h
Normal file
60
platform/robert/boot/src/drivers/system_flash.h
Normal file
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "stm32f7xx.h"
|
||||
|
||||
#define ADDR_FLASH_SECTOR_0 ((uint32_t)0x08000000) /* Base @ of Sector 0, 32 Kbytes */
|
||||
#define ADDR_FLASH_SECTOR_1 ((uint32_t)0x08008000) /* Base @ of Sector 1, 32 Kbytes */
|
||||
#define ADDR_FLASH_SECTOR_2 ((uint32_t)0x08010000) /* Base @ of Sector 2, 32 Kbytes */
|
||||
#define ADDR_FLASH_SECTOR_3 ((uint32_t)0x08018000) /* Base @ of Sector 3, 32 Kbytes */
|
||||
#define ADDR_FLASH_SECTOR_4 ((uint32_t)0x08020000) /* Base @ of Sector 4, 128 Kbytes */
|
||||
#define ADDR_FLASH_SECTOR_5 ((uint32_t)0x08040000) /* Base @ of Sector 5, 256 Kbytes */
|
||||
#define ADDR_FLASH_SECTOR_6 ((uint32_t)0x08080000) /* Base @ of Sector 6, 256 Kbytes */
|
||||
#define ADDR_FLASH_SECTOR_7 ((uint32_t)0x080C0000) /* Base @ of Sector 7, 256 Kbytes */
|
||||
#define ADDR_FLASH_SECTOR_8 ((uint32_t)0x08100000) /* Base @ of Sector 8, 256 Kbytes */
|
||||
#define ADDR_FLASH_SECTOR_9 ((uint32_t)0x08140000) /* Base @ of Sector 9, 256 Kbytes */
|
||||
#define ADDR_FLASH_SECTOR_10 ((uint32_t)0x08180000) /* Base @ of Sector 10, 256 Kbytes */
|
||||
#define ADDR_FLASH_SECTOR_11 ((uint32_t)0x081C0000) /* Base @ of Sector 11, 256 Kbytes */
|
||||
|
||||
typedef void (*SystemFlashProgressCb)(uint32_t progress, uint32_t total, void *context);
|
||||
|
||||
// Erase the sectors of flash which lie within the given address range.
|
||||
//
|
||||
// If the address range overlaps even one single byte of a sector, the entire
|
||||
// sector is erased.
|
||||
//
|
||||
// If progress_callback is not NULL, it is called at the beginning of the erase
|
||||
// process and after each sector is erased. The rational number (progress/total)
|
||||
// increases monotonically as the sector erasue procedure progresses.
|
||||
//
|
||||
// Returns true if successful, false if an error occurred.
|
||||
bool system_flash_erase(uint32_t address, size_t length, SystemFlashProgressCb progress_callback,
|
||||
void *progress_context);
|
||||
|
||||
// Write data into flash. The flash must already be erased.
|
||||
//
|
||||
// If progress_callback is not NULL, it is called at the beginning of the
|
||||
// writing process and periodically thereafter. The rational number
|
||||
// (progress/total) increases monotonically as the data is written.
|
||||
//
|
||||
// Returns true if successful, false if an error occurred.
|
||||
bool system_flash_write(uint32_t address, const void *data, size_t length);
|
39
platform/robert/boot/src/drivers/watchdog.c
Normal file
39
platform/robert/boot/src/drivers/watchdog.c
Normal file
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "drivers/watchdog.h"
|
||||
|
||||
#include "stm32f7xx.h"
|
||||
|
||||
void watchdog_init(void) {
|
||||
IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);
|
||||
|
||||
IWDG_SetPrescaler(IWDG_Prescaler_64); // ~8 seconds
|
||||
IWDG_SetReload(0xfff);
|
||||
|
||||
IWDG_WriteAccessCmd(IWDG_WriteAccess_Disable);
|
||||
|
||||
DBGMCU_APB1PeriphConfig(DBGMCU_IWDG_STOP, ENABLE);
|
||||
}
|
||||
|
||||
void watchdog_start(void) {
|
||||
IWDG_Enable();
|
||||
IWDG_ReloadCounter();
|
||||
}
|
||||
|
||||
bool watchdog_check_reset_flag(void) {
|
||||
return RCC_GetFlagStatus(RCC_FLAG_IWDGRST) != RESET;
|
||||
}
|
24
platform/robert/boot/src/drivers/watchdog.h
Normal file
24
platform/robert/boot/src/drivers/watchdog.h
Normal file
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
void watchdog_init(void);
|
||||
void watchdog_start(void);
|
||||
|
||||
bool watchdog_check_reset_flag(void);
|
24
platform/robert/boot/src/firmware.h
Normal file
24
platform/robert/boot/src/firmware.h
Normal file
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "stm32f7xx.h"
|
||||
|
||||
extern const uint32_t __BOOTLOADER_size__[];
|
||||
|
||||
#define BOOTLOADER_SIZE ((uint32_t) __BOOTLOADER_size__)
|
||||
#define FIRMWARE_BASE (FLASH_BASE + BOOTLOADER_SIZE)
|
67
platform/robert/boot/src/flash_region.h
Normal file
67
platform/robert/boot/src/flash_region.h
Normal file
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#define PAGE_SIZE_BYTES (0x100)
|
||||
|
||||
#define SECTOR_SIZE_BYTES (0x10000)
|
||||
#define SECTOR_ADDR_MASK (~(SECTOR_SIZE_BYTES - 1))
|
||||
|
||||
#define SUBSECTOR_SIZE_BYTES (0x1000)
|
||||
#define SUBSECTOR_ADDR_MASK (~(SUBSECTOR_SIZE_BYTES - 1))
|
||||
|
||||
|
||||
// A bit of preprocessor magic to help with automatically calculating flash region addresses
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define FLASH_REGION_DEF(MACRO, arg) \
|
||||
MACRO(FIRMWARE_SCRATCH, 0x200000 /* 2048k */, arg) \
|
||||
MACRO(SYSTEM_RESOURCES_BANK_0, 0x100000 /* 1024k */, arg) \
|
||||
MACRO(SYSTEM_RESOURCES_BANK_1, 0x100000 /* 1024k */, arg) \
|
||||
MACRO(SAFE_FIRMWARE, 0x080000 /* 512k */, arg) \
|
||||
MACRO(DEBUG_DB, 0x020000 /* 128k */, arg) \
|
||||
MACRO(MFG_INFO, 0x020000 /* 128k */, arg) \
|
||||
MACRO(FILESYSTEM, 0xB30000 /* 11456k */, arg) \
|
||||
MACRO(RSVD, 0x00F000 /* 60k */, arg) \
|
||||
MACRO(SHARED_PRF_STORAGE, 0x001000 /* 4k */, arg)
|
||||
|
||||
#include "flash_region_def_helper.h"
|
||||
|
||||
|
||||
// Flash region _BEGIN and _END addresses
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define FLASH_REGION_FIRMWARE_SCRATCH_BEGIN FLASH_REGION_START_ADDR(FIRMWARE_SCRATCH)
|
||||
#define FLASH_REGION_FIRMWARE_SCRATCH_END FLASH_REGION_END_ADDR(FIRMWARE_SCRATCH)
|
||||
|
||||
#define FLASH_REGION_SAFE_FIRMWARE_BEGIN FLASH_REGION_START_ADDR(SAFE_FIRMWARE)
|
||||
#define FLASH_REGION_SAFE_FIRMWARE_END FLASH_REGION_END_ADDR(SAFE_FIRMWARE)
|
||||
|
||||
#define FLASH_REGION_MFG_INFO_BEGIN FLASH_REGION_START_ADDR(MFG_INFO)
|
||||
#define FLASH_REGION_MFG_INFO_END FLASH_REGION_END_ADDR(MFG_INFO)
|
||||
|
||||
#define BOARD_NOR_FLASH_SIZE FLASH_REGION_START_ADDR(_COUNT)
|
||||
|
||||
|
||||
// Static asserts to make sure everything worked out
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// make sure all the sizes are multiples of the subsector size (4k)
|
||||
FLASH_REGION_SIZE_CHECK(SUBSECTOR_SIZE_BYTES)
|
||||
|
||||
// make sure the total size is what we expect (16MB for robert)
|
||||
_Static_assert(BOARD_NOR_FLASH_SIZE == 0x1000000, "Flash size should be 16MB");
|
37
platform/robert/boot/src/flash_region_def_helper.h
Normal file
37
platform/robert/boot/src/flash_region_def_helper.h
Normal file
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
enum {
|
||||
#define FLASH_REGION_LIST(name, size, arg) FlashRegion_##name,
|
||||
FLASH_REGION_DEF(FLASH_REGION_LIST, NULL)
|
||||
FlashRegion__COUNT
|
||||
};
|
||||
|
||||
#define FLASH_REGION_ADDR_HELPER(name, size, tgt) \
|
||||
+ (FlashRegion_##name < (tgt) ? (size) : 0)
|
||||
|
||||
// These macros add up all the sizes of the flash regions that come before (and including in the
|
||||
// case of the _END_ADDR macro) the specified one to determine the proper flash address value.
|
||||
#define FLASH_REGION_START_ADDR(region) \
|
||||
((0) FLASH_REGION_DEF(FLASH_REGION_ADDR_HELPER, FlashRegion_##region))
|
||||
#define FLASH_REGION_END_ADDR(region) \
|
||||
((0) FLASH_REGION_DEF(FLASH_REGION_ADDR_HELPER, FlashRegion_##region + 1))
|
||||
|
||||
// Checks that all regions are a multiple of the specified size (usually sector or subsector size)
|
||||
#define FLASH_REGION_SIZE_CHECK_HELPER(name, size, arg) \
|
||||
&& ((size) % (arg) == 0)
|
||||
#define FLASH_REGION_SIZE_CHECK(size) \
|
||||
_Static_assert((1) FLASH_REGION_DEF(FLASH_REGION_SIZE_CHECK_HELPER, size), "Invalid region size");
|
221
platform/robert/boot/src/fw_copy.c
Normal file
221
platform/robert/boot/src/fw_copy.c
Normal file
|
@ -0,0 +1,221 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "fw_copy.h"
|
||||
|
||||
#include "drivers/dbgserial.h"
|
||||
#include "drivers/display.h"
|
||||
#include "drivers/flash.h"
|
||||
#include "drivers/system_flash.h"
|
||||
#include "firmware.h"
|
||||
#include "flash_region.h"
|
||||
#include "system/bootbits.h"
|
||||
#include "system/firmware_storage.h"
|
||||
#include "system/reset.h"
|
||||
#include "util/crc32.h"
|
||||
#include "util/misc.h"
|
||||
#include "util/delay.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define MAX_CHUNK_SIZE 65536
|
||||
|
||||
static bool prv_check_valid_firmware_crc(uint32_t flash_address, FirmwareDescription* desc) {
|
||||
dbgserial_putstr("Checksumming firmware update");
|
||||
const uint32_t crc = flash_calculate_checksum(flash_address, desc->firmware_length);
|
||||
dbgserial_print("Calculated checksum: ");
|
||||
dbgserial_print_hex(crc);
|
||||
dbgserial_newline();
|
||||
return crc == desc->checksum;
|
||||
}
|
||||
|
||||
static void prv_display_erase_progress(uint32_t progress, uint32_t total, void *ctx) {
|
||||
display_firmware_update_progress(progress, total * 2);
|
||||
}
|
||||
|
||||
static bool prv_erase_old_firmware(uint32_t firmware_length) {
|
||||
dbgserial_putstr("prv_erase_old_firmware");
|
||||
return system_flash_erase(FIRMWARE_BASE, firmware_length, prv_display_erase_progress, 0);
|
||||
}
|
||||
|
||||
static void prv_display_write_progress(uint32_t progress, uint32_t total, void *ctx) {
|
||||
display_firmware_update_progress(progress/2 + total/2, total);
|
||||
}
|
||||
|
||||
static bool prv_write_new_firmware(uint32_t flash_new_fw_start, uint32_t firmware_length) {
|
||||
dbgserial_putstr("prv_write_new_firmware");
|
||||
// We can't just read the flash like memory, so we gotta lift everything ourselves.
|
||||
// buffer is static so it goes in BSS, since stack is only 8192 bytes
|
||||
static uint8_t buffer[MAX_CHUNK_SIZE];
|
||||
uint32_t chunk_size;
|
||||
for (uint32_t i = 0; i < firmware_length; i += chunk_size) {
|
||||
chunk_size = MIN(MAX_CHUNK_SIZE, firmware_length - i);
|
||||
flash_read_bytes(buffer, flash_new_fw_start + i, chunk_size);
|
||||
if (!system_flash_write(FIRMWARE_BASE + i, buffer, chunk_size)) {
|
||||
dbgserial_putstr("We're dead");
|
||||
return false;
|
||||
}
|
||||
prv_display_write_progress(i, firmware_length, NULL);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool prv_check_firmware_crc(FirmwareDescription* firmware_description) {
|
||||
dbgserial_print("Checksumming ");
|
||||
dbgserial_print_hex(firmware_description->firmware_length);
|
||||
dbgserial_print(" bytes\r\n");
|
||||
|
||||
const uint32_t crc = crc32(CRC32_INIT, (const uint8_t *)FIRMWARE_BASE,
|
||||
firmware_description->firmware_length);
|
||||
|
||||
dbgserial_print("Checksum - wanted ");
|
||||
dbgserial_print_hex(firmware_description->checksum);
|
||||
dbgserial_print(" got ");
|
||||
dbgserial_print_hex(crc);
|
||||
dbgserial_newline();
|
||||
|
||||
return crc == firmware_description->checksum;
|
||||
}
|
||||
|
||||
typedef enum UpdateFirmwareResult {
|
||||
UPDATE_FW_SUCCESS = 0,
|
||||
UPDATE_FW_ERROR_MICRO_FLASH_UNTOUCHED = 1,
|
||||
UPDATE_FW_ERROR_MICRO_FLASH_MANGLED = 2
|
||||
} UpdateFirmwareResult;
|
||||
|
||||
static UpdateFirmwareResult prv_update_fw(uint32_t flash_address) {
|
||||
display_firmware_update_progress(0, 1);
|
||||
boot_bit_set(BOOT_BIT_NEW_FW_UPDATE_IN_PROGRESS);
|
||||
|
||||
FirmwareDescription firmware_description =
|
||||
firmware_storage_read_firmware_description(flash_address);
|
||||
|
||||
if (!firmware_storage_check_valid_firmware_description(&firmware_description)) {
|
||||
dbgserial_putstr("Invalid firmware description!");
|
||||
return UPDATE_FW_ERROR_MICRO_FLASH_UNTOUCHED;
|
||||
}
|
||||
|
||||
if (!prv_check_valid_firmware_crc(flash_address + sizeof(FirmwareDescription),
|
||||
&firmware_description)) {
|
||||
dbgserial_putstr("Invalid firmware CRC in SPI flash!");
|
||||
return UPDATE_FW_ERROR_MICRO_FLASH_UNTOUCHED;
|
||||
}
|
||||
|
||||
prv_erase_old_firmware(firmware_description.firmware_length);
|
||||
|
||||
prv_write_new_firmware(flash_address + sizeof(FirmwareDescription),
|
||||
firmware_description.firmware_length);
|
||||
|
||||
if (!prv_check_firmware_crc(&firmware_description)) {
|
||||
dbgserial_putstr(
|
||||
"Our internal flash contents are bad (checksum failed)! "
|
||||
"This is really bad!");
|
||||
return UPDATE_FW_ERROR_MICRO_FLASH_MANGLED;
|
||||
}
|
||||
|
||||
return UPDATE_FW_SUCCESS;
|
||||
}
|
||||
|
||||
void fw_copy_check_update_fw(void) {
|
||||
if (!boot_bit_test(BOOT_BIT_NEW_FW_AVAILABLE)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (boot_bit_test(BOOT_BIT_NEW_FW_UPDATE_IN_PROGRESS)) {
|
||||
dbgserial_putstr("Our previous firmware update failed, aborting update.");
|
||||
|
||||
// Pretend like the new firmware bit wasn't set afterall. We'll just run the
|
||||
// previous code, whether that was normal firmware or the recovery firmware.
|
||||
boot_bit_clear(BOOT_BIT_NEW_FW_UPDATE_IN_PROGRESS);
|
||||
boot_bit_clear(BOOT_BIT_NEW_FW_AVAILABLE);
|
||||
boot_bit_clear(BOOT_BIT_NEW_FW_INSTALLED);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
dbgserial_putstr("New firmware is available!");
|
||||
|
||||
boot_bit_clear(BOOT_BIT_FW_START_FAIL_STRIKE_ONE);
|
||||
boot_bit_clear(BOOT_BIT_FW_START_FAIL_STRIKE_TWO);
|
||||
boot_bit_clear(BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_ONE);
|
||||
boot_bit_clear(BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_TWO);
|
||||
|
||||
UpdateFirmwareResult result = prv_update_fw(FLASH_REGION_FIRMWARE_SCRATCH_BEGIN);
|
||||
switch (result) {
|
||||
case UPDATE_FW_SUCCESS:
|
||||
break;
|
||||
case UPDATE_FW_ERROR_MICRO_FLASH_UNTOUCHED:
|
||||
// Our firmware update failed in a way that didn't break our previous
|
||||
// firmware. Just run the previous code, whether that was normal firmware
|
||||
// or the recovery firmware.
|
||||
break;
|
||||
case UPDATE_FW_ERROR_MICRO_FLASH_MANGLED:
|
||||
// We've broken our internal flash when trying to update our normal
|
||||
// firmware. Fall back immediately to the recovery firmare.
|
||||
boot_bit_set(BOOT_BIT_FW_START_FAIL_STRIKE_ONE);
|
||||
boot_bit_set(BOOT_BIT_FW_START_FAIL_STRIKE_TWO);
|
||||
system_reset();
|
||||
return;
|
||||
}
|
||||
|
||||
// Done, we're ready to boot.
|
||||
boot_bit_clear(BOOT_BIT_NEW_FW_UPDATE_IN_PROGRESS);
|
||||
boot_bit_clear(BOOT_BIT_NEW_FW_AVAILABLE);
|
||||
boot_bit_set(BOOT_BIT_NEW_FW_INSTALLED);
|
||||
}
|
||||
|
||||
bool fw_copy_switch_to_recovery_fw() {
|
||||
dbgserial_putstr("Loading recovery firmware");
|
||||
|
||||
UpdateFirmwareResult result = prv_update_fw(FLASH_REGION_SAFE_FIRMWARE_BEGIN);
|
||||
bool recovery_fw_ok = true;
|
||||
switch (result) {
|
||||
case UPDATE_FW_SUCCESS:
|
||||
boot_bit_clear(BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_ONE);
|
||||
boot_bit_clear(BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_TWO);
|
||||
boot_bit_set(BOOT_BIT_RECOVERY_START_IN_PROGRESS);
|
||||
break;
|
||||
case UPDATE_FW_ERROR_MICRO_FLASH_UNTOUCHED:
|
||||
case UPDATE_FW_ERROR_MICRO_FLASH_MANGLED:
|
||||
// Keep us booting into recovery firmware.
|
||||
boot_bit_set(BOOT_BIT_FW_START_FAIL_STRIKE_ONE);
|
||||
boot_bit_set(BOOT_BIT_FW_START_FAIL_STRIKE_TWO);
|
||||
|
||||
if (!boot_bit_test(BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_ONE)) {
|
||||
dbgserial_putstr("Failed to load recovery firmware, strike one. Try again.");
|
||||
boot_bit_set(BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_ONE);
|
||||
boot_bit_set(BOOT_BIT_SOFTWARE_FAILURE_OCCURRED);
|
||||
system_reset();
|
||||
} else if (!boot_bit_test(BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_TWO)) {
|
||||
dbgserial_putstr("Failed to load recovery firmware, strike two. Try again.");
|
||||
boot_bit_set(BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_TWO);
|
||||
boot_bit_set(BOOT_BIT_SOFTWARE_FAILURE_OCCURRED);
|
||||
system_reset();
|
||||
} else {
|
||||
dbgserial_putstr("Failed to load recovery firmware, strike three. SAD WATCH");
|
||||
boot_bit_clear(BOOT_BIT_FW_START_FAIL_STRIKE_ONE);
|
||||
boot_bit_clear(BOOT_BIT_FW_START_FAIL_STRIKE_TWO);
|
||||
boot_bit_clear(BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_ONE);
|
||||
boot_bit_clear(BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_TWO);
|
||||
recovery_fw_ok = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
boot_bit_clear(BOOT_BIT_NEW_FW_UPDATE_IN_PROGRESS);
|
||||
return recovery_fw_ok;
|
||||
}
|
24
platform/robert/boot/src/fw_copy.h
Normal file
24
platform/robert/boot/src/fw_copy.h
Normal file
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
void fw_copy_check_update_fw(void);
|
||||
|
||||
//! @return false if we've failed to load recovery mode too many times and we should just sadwatch
|
||||
bool fw_copy_switch_to_recovery_fw(void);
|
8
platform/robert/boot/src/git_version.auto.h.in
Normal file
8
platform/robert/boot/src/git_version.auto.h.in
Normal file
|
@ -0,0 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
#define GIT_TIMESTAMP @TIMESTAMP@
|
||||
#define GIT_TAG "@TAG@"
|
||||
#define GIT_MAJOR_VERSION @MAJOR_VERSION@
|
||||
#define GIT_MINOR_VERSION @MINOR_VERSION@
|
||||
#define GIT_PATCH_VERSION @PATCH_VERSION@
|
||||
#define GIT_REVISION "@COMMIT@"
|
39
platform/robert/boot/src/hardfault_handler.c
Normal file
39
platform/robert/boot/src/hardfault_handler.c
Normal file
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "drivers/dbgserial.h"
|
||||
#include "system/die.h"
|
||||
#include "system/reset.h"
|
||||
|
||||
static void prv_hard_fault_handler_c(unsigned int *hardfault_args) {
|
||||
dbgserial_putstr("HARD FAULT");
|
||||
|
||||
#ifdef NO_WATCHDOG
|
||||
reset_due_to_software_failure();
|
||||
#else
|
||||
system_hard_reset();
|
||||
#endif
|
||||
}
|
||||
|
||||
void HardFault_Handler(void) {
|
||||
// Grab the stack pointer, shove it into a register and call
|
||||
// the c function above.
|
||||
__asm("tst lr, #4\n"
|
||||
"ite eq\n"
|
||||
"mrseq r0, msp\n"
|
||||
"mrsne r0, psp\n"
|
||||
"b %0\n" :: "i" (prv_hard_fault_handler_c));
|
||||
}
|
109
platform/robert/boot/src/irq_stm32f7.def
Normal file
109
platform/robert/boot/src/irq_stm32f7.def
Normal file
|
@ -0,0 +1,109 @@
|
|||
IRQ_DEF(0, WWDG) // Window WatchDog
|
||||
IRQ_DEF(1, PVD) // PVD through EXTI Line detection
|
||||
IRQ_DEF(2, TAMP_STAMP) // Tamper and TimeStamps through the EXTI line
|
||||
IRQ_DEF(3, RTC_WKUP) // RTC Wakeup through the EXTI line
|
||||
IRQ_DEF(4, FLASH) // FLASH
|
||||
IRQ_DEF(5, RCC) // RCC
|
||||
IRQ_DEF(6, EXTI0) // EXTI Line0
|
||||
IRQ_DEF(7, EXTI1) // EXTI Line1
|
||||
IRQ_DEF(8, EXTI2) // EXTI Line2
|
||||
IRQ_DEF(9, EXTI3) // EXTI Line3
|
||||
IRQ_DEF(10, EXTI4) // EXTI Line4
|
||||
IRQ_DEF(11, DMA1_Stream0) // DMA1 Stream 0
|
||||
IRQ_DEF(12, DMA1_Stream1) // DMA1 Stream 1
|
||||
IRQ_DEF(13, DMA1_Stream2) // DMA1 Stream 2
|
||||
IRQ_DEF(14, DMA1_Stream3) // DMA1 Stream 3
|
||||
IRQ_DEF(15, DMA1_Stream4) // DMA1 Stream 4
|
||||
IRQ_DEF(16, DMA1_Stream5) // DMA1 Stream 5
|
||||
IRQ_DEF(17, DMA1_Stream6) // DMA1 Stream 6
|
||||
IRQ_DEF(18, ADC) // ADC1, ADC2 and ADC3s
|
||||
IRQ_DEF(19, CAN1_TX) // CAN1 TX
|
||||
IRQ_DEF(20, CAN1_RX0) // CAN1 RX0
|
||||
IRQ_DEF(21, CAN1_RX1) // CAN1 RX1
|
||||
IRQ_DEF(22, CAN1_SCE) // CAN1 SCE
|
||||
IRQ_DEF(23, EXTI9_5) // External Line[9:5]s
|
||||
IRQ_DEF(24, TIM1_BRK_TIM9) // TIM1 Break and TIM9
|
||||
IRQ_DEF(25, TIM1_UP_TIM10) // TIM1 Update and TIM10
|
||||
IRQ_DEF(26, TIM1_TRG_COM_TIM11) // TIM1 Trigger and Commutation and TIM11
|
||||
IRQ_DEF(27, TIM1_CC) // TIM1 Capture Compare
|
||||
IRQ_DEF(28, TIM2) // TIM2
|
||||
IRQ_DEF(29, TIM3) // TIM3
|
||||
IRQ_DEF(30, TIM4) // TIM4
|
||||
IRQ_DEF(31, I2C1_EV) // I2C1 Event
|
||||
IRQ_DEF(32, I2C1_ER) // I2C1 Error
|
||||
IRQ_DEF(33, I2C2_EV) // I2C2 Event
|
||||
IRQ_DEF(34, I2C2_ER) // I2C2 Error
|
||||
IRQ_DEF(35, SPI1) // SPI1
|
||||
IRQ_DEF(36, SPI2) // SPI2
|
||||
IRQ_DEF(37, USART1) // USART1
|
||||
IRQ_DEF(38, USART2) // USART2
|
||||
IRQ_DEF(39, USART3) // USART3
|
||||
IRQ_DEF(40, EXTI15_10) // External Line[15:10]s
|
||||
IRQ_DEF(41, RTC_Alarm) // RTC Alarm (A and B) through EXTI Line
|
||||
IRQ_DEF(42, OTG_FS_WKUP) // USB OTG FS Wakeup through EXTI line
|
||||
IRQ_DEF(43, TIM8_BRK_TIM12) // TIM8 Break and TIM12
|
||||
IRQ_DEF(44, TIM8_UP_TIM13) // TIM8 Update and TIM13
|
||||
IRQ_DEF(45, TIM8_TRG_COM_TIM14) // TIM8 Trigger and Commutation and TIM14
|
||||
IRQ_DEF(46, TIM8_CC) // TIM8 Capture Compare
|
||||
IRQ_DEF(47, DMA1_Stream7) // DMA1 Stream7
|
||||
IRQ_DEF(48, FMC) // FMC
|
||||
IRQ_DEF(49, SDMMC1) // SDMMC1
|
||||
IRQ_DEF(50, TIM5) // TIM5
|
||||
IRQ_DEF(51, SPI3) // SPI3
|
||||
IRQ_DEF(52, UART4) // UART4
|
||||
IRQ_DEF(53, UART5) // UART5
|
||||
IRQ_DEF(54, TIM6_DAC) // TIM6 and DAC1&2 underrun errors
|
||||
IRQ_DEF(55, TIM7) // TIM7
|
||||
IRQ_DEF(56, DMA2_Stream0) // DMA2 Stream 0
|
||||
IRQ_DEF(57, DMA2_Stream1) // DMA2 Stream 1
|
||||
IRQ_DEF(58, DMA2_Stream2) // DMA2 Stream 2
|
||||
IRQ_DEF(59, DMA2_Stream3) // DMA2 Stream 3
|
||||
IRQ_DEF(60, DMA2_Stream4) // DMA2 Stream 4
|
||||
IRQ_DEF(61, ETH) // Ethernet
|
||||
IRQ_DEF(62, ETH_WKUP) // Ethernet Wakeup through EXTI line
|
||||
IRQ_DEF(63, CAN2_TX) // CAN2 TX
|
||||
IRQ_DEF(64, CAN2_RX0) // CAN2 RX0
|
||||
IRQ_DEF(65, CAN2_RX1) // CAN2 RX1
|
||||
IRQ_DEF(66, CAN2_SCE) // CAN2 SCE
|
||||
IRQ_DEF(67, OTG_FS) // USB OTG FS
|
||||
IRQ_DEF(68, DMA2_Stream5) // DMA2 Stream 5
|
||||
IRQ_DEF(69, DMA2_Stream6) // DMA2 Stream 6
|
||||
IRQ_DEF(70, DMA2_Stream7) // DMA2 Stream 7
|
||||
IRQ_DEF(71, USART6) // USART6
|
||||
IRQ_DEF(72, I2C3_EV) // I2C3 event
|
||||
IRQ_DEF(73, I2C3_ER) // I2C3 error
|
||||
IRQ_DEF(74, OTG_HS_EP1_OUT) // USB OTG HS End Point 1 Out
|
||||
IRQ_DEF(75, OTG_HS_EP1_IN) // USB OTG HS End Point 1 In
|
||||
IRQ_DEF(76, OTG_HS_WKUP) // USB OTG HS Wakeup through EXTI
|
||||
IRQ_DEF(77, OTG_HS) // USB OTG HS
|
||||
IRQ_DEF(78, DCMI) // DCMI
|
||||
IRQ_DEF(79, CRYP) // CRYP crypto
|
||||
IRQ_DEF(80, HASH_RNG) // Hash and Rng
|
||||
IRQ_DEF(81, FPU) // FPU
|
||||
IRQ_DEF(82, UART7) // UART7
|
||||
IRQ_DEF(83, UART8) // UART8
|
||||
IRQ_DEF(84, SPI4) // SPI4
|
||||
IRQ_DEF(85, SPI5) // SPI5
|
||||
IRQ_DEF(86, SPI6) // SPI6
|
||||
IRQ_DEF(87, SAI1) // SAI1
|
||||
IRQ_DEF(88, LTDC) // LTDC
|
||||
IRQ_DEF(89, LTDC_ER) // LTDC_ER
|
||||
IRQ_DEF(90, DMA2D) // DMA2D
|
||||
IRQ_DEF(91, SAI2) // SAI2
|
||||
IRQ_DEF(92, QUADSPI) // Quad SPI
|
||||
IRQ_DEF(93, LPTIM1) // LP TIM1
|
||||
IRQ_DEF(94, CEC) // HDMI-CEC
|
||||
IRQ_DEF(95, I2C4_EV) // I2C4 Event
|
||||
IRQ_DEF(96, I2C4_ER) // I2C4 Error
|
||||
IRQ_DEF(97, SPDIF_RX) // SPDIF-RX
|
||||
IRQ_DEF(99, DFSDM0) // DFSDM Filter1
|
||||
IRQ_DEF(100, DFSDM1) // DFSDM Filter2
|
||||
IRQ_DEF(101, DFSDM2) // DFSDM Filter3
|
||||
IRQ_DEF(102, DFSDM3) // DFSDM Filter4
|
||||
IRQ_DEF(103, SDMMC2) // SDMMC2
|
||||
IRQ_DEF(104, CAN3_TX) // CAN3 TX
|
||||
IRQ_DEF(105, CAN3_RX0) // CAN3 RX0
|
||||
IRQ_DEF(106, CAN3_RX1) // CAN3 RX1
|
||||
IRQ_DEF(107, CAN3_SCE) // CAN3 SCE
|
||||
IRQ_DEF(108, JPEG) // JPEG
|
||||
IRQ_DEF(109, MDIOS) // MDIO Slave
|
404
platform/robert/boot/src/main.c
Normal file
404
platform/robert/boot/src/main.c
Normal file
|
@ -0,0 +1,404 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "board/board.h"
|
||||
#include "drivers/button.h"
|
||||
#include "drivers/dbgserial.h"
|
||||
#include "drivers/display.h"
|
||||
#include "drivers/flash.h"
|
||||
#include "drivers/gpio.h"
|
||||
#include "drivers/periph_config.h"
|
||||
#include "drivers/pmic.h"
|
||||
#include "drivers/pwr.h"
|
||||
#include "drivers/watchdog.h"
|
||||
#include "boot_tests.h"
|
||||
#include "firmware.h"
|
||||
#include "fw_copy.h"
|
||||
#include "pebble_errors.h"
|
||||
#include "system/bootbits.h"
|
||||
#include "system/logging.h"
|
||||
#include "system/passert.h"
|
||||
#include "system/reset.h"
|
||||
#include "util/delay.h"
|
||||
#include "util/misc.h"
|
||||
|
||||
#include "stm32f7xx.h"
|
||||
|
||||
static void prv_get_fw_reset_vector(void **reset_handler,
|
||||
void **initial_stack_pointer) {
|
||||
void** fw_vector_table = (void**) FIRMWARE_BASE;
|
||||
|
||||
*initial_stack_pointer = fw_vector_table[0];
|
||||
*reset_handler = fw_vector_table[1];
|
||||
}
|
||||
|
||||
static void prv_hw_reset(void) {
|
||||
// Disable all interrupts, just in case.
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
// Interrupt Clear-Enable Register
|
||||
NVIC->ICER[i] = 0xFFFFFFFF;
|
||||
// Interrupt Clear-Pending Register
|
||||
NVIC->ICPR[i] = 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
// Set the peripheral clock enable registers to their reset values as
|
||||
// specified in the reference manual.
|
||||
RCC->AHB1ENR = 0x00100000;
|
||||
RCC->AHB2ENR = 0;
|
||||
RCC->AHB3ENR = 0;
|
||||
RCC->APB1ENR = 0x00000400; // Reserved bit needs to be set to enable RTC!
|
||||
RCC->APB2ENR = 0;
|
||||
|
||||
// Reset most peripherals used by the bootloader. We want to minimize the
|
||||
// chances that the firmware unintentionally relies on some state that the
|
||||
// bootloader leaves behind. This includes disabling the PLL.
|
||||
// GPIOs are not reset here: resetting them would change their output values,
|
||||
// which could unintentionally turn of e.g. PMIC power rails.
|
||||
// The backup domain is not reset; that would be foolish.
|
||||
const uint32_t ahb1_periphs =
|
||||
RCC_AHB1Periph_CRC | RCC_AHB1Periph_DMA1 | RCC_AHB1Periph_DMA2
|
||||
| RCC_AHB1Periph_DMA2D | RCC_AHB1Periph_ETHMAC | RCC_AHB1Periph_OTGHS;
|
||||
const uint32_t ahb2_periphs =
|
||||
RCC_AHB2Periph_DCMI | RCC_AHB2Periph_JPEG | RCC_AHB2Periph_CRYP | RCC_AHB2Periph_HASH
|
||||
| RCC_AHB2Periph_RNG | RCC_AHB2Periph_OTGFS;
|
||||
const uint32_t ahb3_periphs = RCC_AHB3Periph_FMC | RCC_AHB3Periph_QSPI;
|
||||
const uint32_t apb1_periphs =
|
||||
RCC_APB1Periph_TIM2 | RCC_APB1Periph_TIM3 | RCC_APB1Periph_TIM4| RCC_APB1Periph_TIM5
|
||||
| RCC_APB1Periph_TIM6 | RCC_APB1Periph_TIM7 | RCC_APB1Periph_TIM12 | RCC_APB1Periph_TIM13
|
||||
| RCC_APB1Periph_TIM14 | RCC_APB1Periph_LPTIM1 | RCC_APB1Periph_WWDG | RCC_APB1Periph_CAN3
|
||||
| RCC_APB1Periph_SPI2 | RCC_APB1Periph_SPI3 | RCC_APB1Periph_SPDIFRX | RCC_APB1Periph_USART2
|
||||
| RCC_APB1Periph_USART3 | RCC_APB1Periph_UART4 | RCC_APB1Periph_UART5 | RCC_APB1Periph_I2C1
|
||||
| RCC_APB1Periph_I2C2 | RCC_APB1Periph_I2C3 | RCC_APB1Periph_I2C4 | RCC_APB1Periph_CAN1
|
||||
| RCC_APB1Periph_CAN2 | RCC_APB1Periph_CEC | RCC_APB1Periph_PWR | RCC_APB1Periph_DAC
|
||||
| RCC_APB1Periph_UART7 | RCC_APB1Periph_UART8;
|
||||
const uint32_t apb2_periphs =
|
||||
RCC_APB2Periph_TIM1 | RCC_APB2Periph_TIM8 | RCC_APB2Periph_USART1
|
||||
| RCC_APB2Periph_USART6 | RCC_APB2Periph_SDMMC2 | RCC_APB2Periph_ADC | RCC_APB2Periph_SDMMC1
|
||||
| RCC_APB2Periph_SPI1 | RCC_APB2Periph_SPI4 | RCC_APB2Periph_SYSCFG
|
||||
| RCC_APB2Periph_TIM9 | RCC_APB2Periph_TIM10 | RCC_APB2Periph_TIM11
|
||||
| RCC_APB2Periph_SPI5 | RCC_APB2Periph_SPI6 | RCC_APB2Periph_SAI1 | RCC_APB2Periph_SAI2
|
||||
| RCC_APB2Periph_DFSDM | RCC_APB2Periph_MDIO | RCC_APB2Periph_LTDC;
|
||||
RCC_DeInit();
|
||||
RCC_AHB1PeriphResetCmd(ahb1_periphs, ENABLE);
|
||||
RCC_AHB1PeriphResetCmd(ahb1_periphs, DISABLE);
|
||||
RCC_AHB2PeriphResetCmd(ahb2_periphs, ENABLE);
|
||||
RCC_AHB2PeriphResetCmd(ahb2_periphs, DISABLE);
|
||||
RCC_AHB3PeriphResetCmd(ahb3_periphs, ENABLE);
|
||||
RCC_AHB3PeriphResetCmd(ahb3_periphs, DISABLE);
|
||||
RCC_APB1PeriphResetCmd(apb1_periphs, ENABLE);
|
||||
RCC_APB1PeriphResetCmd(apb1_periphs, DISABLE);
|
||||
RCC_APB2PeriphResetCmd(apb2_periphs, ENABLE);
|
||||
RCC_APB2PeriphResetCmd(apb2_periphs, DISABLE);
|
||||
}
|
||||
|
||||
static void __attribute__((noreturn)) prv_jump_to_fw(void) {
|
||||
void *initial_stack_pointer, *reset_handler;
|
||||
prv_get_fw_reset_vector(&reset_handler, &initial_stack_pointer);
|
||||
|
||||
dbgserial_print("Booting firmware @ ");
|
||||
dbgserial_print_hex((uintptr_t)reset_handler);
|
||||
dbgserial_print("...\r\n\r\n");
|
||||
|
||||
prv_hw_reset();
|
||||
|
||||
// The Cortex-M user guide states that the reset values for the core registers
|
||||
// are as follows:
|
||||
// R0-R12 = Unknown
|
||||
// MSP = VECTOR_TABLE[0] (main stack pointer)
|
||||
// PSP = Unknown (process stack pointer)
|
||||
// LR = 0xFFFFFFFF
|
||||
// PC = VECTOR_TABLE[1]
|
||||
// PRIMASK = 0x0
|
||||
// FAULTMASK = 0x0
|
||||
// BASEPRI = 0x0
|
||||
// CONTROL = 0x0
|
||||
//
|
||||
// Attempt to put the processor into as close to the reset state as possible
|
||||
// before passing control to the firmware.
|
||||
//
|
||||
// No attempt is made to set CONTROL to zero as it should already be set to
|
||||
// the reset value when this code executes.
|
||||
__asm volatile (
|
||||
"cpsie if\n" // Clear PRIMASK and FAULTMASK
|
||||
"mov lr, 0xFFFFFFFF\n"
|
||||
"mov sp, %[initial_sp]\n"
|
||||
"bx %[reset_handler]\n"
|
||||
: : [initial_sp] "r" (initial_stack_pointer),
|
||||
[reset_handler] "r" (reset_handler)
|
||||
);
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
static bool prv_check_and_increment_reset_loop_detection_bits(void) {
|
||||
uint8_t counter =
|
||||
(boot_bit_test(BOOT_BIT_RESET_LOOP_DETECT_THREE) << 2) |
|
||||
(boot_bit_test(BOOT_BIT_RESET_LOOP_DETECT_TWO) << 1) |
|
||||
boot_bit_test(BOOT_BIT_RESET_LOOP_DETECT_ONE);
|
||||
|
||||
if (counter == 7) {
|
||||
boot_bit_clear(BOOT_BIT_RESET_LOOP_DETECT_ONE);
|
||||
boot_bit_clear(BOOT_BIT_RESET_LOOP_DETECT_TWO);
|
||||
boot_bit_clear(BOOT_BIT_RESET_LOOP_DETECT_THREE);
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (++counter) {
|
||||
case 1:
|
||||
boot_bit_set(BOOT_BIT_RESET_LOOP_DETECT_ONE);
|
||||
break;
|
||||
case 2:
|
||||
boot_bit_clear(BOOT_BIT_RESET_LOOP_DETECT_ONE);
|
||||
boot_bit_set(BOOT_BIT_RESET_LOOP_DETECT_TWO);
|
||||
break;
|
||||
case 3:
|
||||
boot_bit_set(BOOT_BIT_RESET_LOOP_DETECT_ONE);
|
||||
break;
|
||||
case 4:
|
||||
boot_bit_clear(BOOT_BIT_RESET_LOOP_DETECT_ONE);
|
||||
boot_bit_clear(BOOT_BIT_RESET_LOOP_DETECT_TWO);
|
||||
boot_bit_set(BOOT_BIT_RESET_LOOP_DETECT_THREE);
|
||||
break;
|
||||
case 5:
|
||||
boot_bit_set(BOOT_BIT_RESET_LOOP_DETECT_ONE);
|
||||
break;
|
||||
case 6:
|
||||
boot_bit_clear(BOOT_BIT_RESET_LOOP_DETECT_ONE);
|
||||
boot_bit_set(BOOT_BIT_RESET_LOOP_DETECT_TWO);
|
||||
break;
|
||||
case 7:
|
||||
boot_bit_set(BOOT_BIT_RESET_LOOP_DETECT_ONE);
|
||||
break;
|
||||
default:
|
||||
PBL_CROAK("reset loop boot bits overrun");
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool prv_check_for_recovery_start_failure() {
|
||||
return boot_bit_test(BOOT_BIT_RECOVERY_START_IN_PROGRESS);
|
||||
}
|
||||
|
||||
static bool prv_check_for_fw_start_failure() {
|
||||
// Add more failure conditions here.
|
||||
if (!watchdog_check_reset_flag() && !boot_bit_test(BOOT_BIT_SOFTWARE_FAILURE_OCCURRED)) {
|
||||
// We're good, we're just starting normally.
|
||||
PBL_LOG_VERBOSE("We're good, we're just starting normally.");
|
||||
|
||||
boot_bit_clear(BOOT_BIT_FW_START_FAIL_STRIKE_ONE);
|
||||
boot_bit_clear(BOOT_BIT_FW_START_FAIL_STRIKE_TWO);
|
||||
return false;
|
||||
}
|
||||
|
||||
// We failed to start our firmware successfully!
|
||||
if (watchdog_check_reset_flag()) {
|
||||
dbgserial_putstr("Watchdog caused a reset");
|
||||
}
|
||||
if (boot_bit_test(BOOT_BIT_SOFTWARE_FAILURE_OCCURRED)) {
|
||||
dbgserial_putstr("Software failure caused a reset");
|
||||
}
|
||||
|
||||
// Clean up after the last failure.
|
||||
boot_bit_clear(BOOT_BIT_SOFTWARE_FAILURE_OCCURRED);
|
||||
|
||||
// We have a "three strikes" algorithm: if the watch fails three times, return true
|
||||
// to tell the parent we should load the recovery firmware. A reset for any other reason
|
||||
// will reset this algorithm.
|
||||
if (boot_bit_test(BOOT_BIT_FW_START_FAIL_STRIKE_TWO)) {
|
||||
// Yikes, our firmware is screwed. Boot into recovery mode.
|
||||
dbgserial_putstr("Failed to start firmware, strike three.");
|
||||
boot_bit_clear(BOOT_BIT_FW_START_FAIL_STRIKE_ONE);
|
||||
boot_bit_clear(BOOT_BIT_FW_START_FAIL_STRIKE_TWO);
|
||||
return true;
|
||||
} else if (boot_bit_test(BOOT_BIT_FW_START_FAIL_STRIKE_ONE)) {
|
||||
dbgserial_putstr("Failed to start firmware, strike two.");
|
||||
boot_bit_set(BOOT_BIT_FW_START_FAIL_STRIKE_TWO);
|
||||
} else {
|
||||
dbgserial_putstr("Failed to start firmware, strike one.");
|
||||
boot_bit_set(BOOT_BIT_FW_START_FAIL_STRIKE_ONE);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool prv_prf_button_combination_is_pressed(void) {
|
||||
return (button_is_pressed(BUTTON_ID_UP) && button_is_pressed(BUTTON_ID_BACK)
|
||||
&& button_is_pressed(BUTTON_ID_SELECT) && !button_is_pressed(BUTTON_ID_DOWN));
|
||||
}
|
||||
|
||||
static bool prv_check_force_boot_recovery(void) {
|
||||
if (boot_bit_test(BOOT_BIT_FORCE_PRF)) {
|
||||
boot_bit_clear(BOOT_BIT_FORCE_PRF);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (prv_prf_button_combination_is_pressed()) {
|
||||
dbgserial_putstr("Hold down UP + BACK + SELECT for 5 secs. to force-boot PRF");
|
||||
for (int i = 0; i < 5000; ++i) {
|
||||
if (!prv_prf_button_combination_is_pressed()) {
|
||||
// stop waiting if not held down any longer
|
||||
return false;
|
||||
}
|
||||
delay_ms(1);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void *reset_vector, *initial_sp;
|
||||
prv_get_fw_reset_vector(&reset_vector, &initial_sp);
|
||||
if ((uintptr_t)reset_vector == 0xffffffff ||
|
||||
(uintptr_t)initial_sp == 0xffffffff) {
|
||||
dbgserial_putstr("Firmware is erased");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void prv_sad_watch(uint32_t error_code) {
|
||||
dbgserial_putstr("SAD WATCH");
|
||||
|
||||
char error_code_buffer[12];
|
||||
itoa_hex(error_code, error_code_buffer, sizeof(error_code_buffer));
|
||||
dbgserial_putstr(error_code_buffer);
|
||||
|
||||
display_error_code(error_code);
|
||||
|
||||
bool prev_select_state = button_is_pressed(BUTTON_ID_SELECT);
|
||||
while (1) {
|
||||
// See if we should restart
|
||||
bool select_state = button_is_pressed(BUTTON_ID_SELECT);
|
||||
if (select_state != prev_select_state) {
|
||||
system_reset();
|
||||
}
|
||||
delay_ms(10);
|
||||
}
|
||||
}
|
||||
|
||||
static void prv_check_and_handle_resuming_from_standby(void) {
|
||||
periph_config_enable(PWR, RCC_APB1Periph_PWR);
|
||||
if (pwr_did_boot_from_standby()) {
|
||||
// We just woke up from standby. For some reason this leaves the system in a funny state,
|
||||
// so clear the flag and reboot again to really clear things up.
|
||||
|
||||
pwr_clear_boot_from_standby_flag();
|
||||
dbgserial_putstr("exit standby");
|
||||
system_hard_reset();
|
||||
}
|
||||
periph_config_disable(PWR, RCC_APB1Periph_PWR);
|
||||
}
|
||||
|
||||
static void prv_print_bootloader_version(void) {
|
||||
char bootloader_version_str[12];
|
||||
memset(bootloader_version_str, 0, 12);
|
||||
itoa_hex(boot_version_read(), bootloader_version_str, 12);
|
||||
dbgserial_putstr(bootloader_version_str);
|
||||
dbgserial_putstr("");
|
||||
dbgserial_putstr("");
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
prv_check_and_handle_resuming_from_standby();
|
||||
|
||||
board_init();
|
||||
|
||||
dbgserial_init();
|
||||
|
||||
dbgserial_putstr("\r\n\r\n\r\n");
|
||||
dbgserial_putstr("██████╗ ██████╗ ██████╗ ███████╗██████╗ ████████╗");
|
||||
dbgserial_putstr("██╔══██╗██╔═══██╗██╔══██╗██╔════╝██╔══██╗╚══██╔══╝");
|
||||
dbgserial_putstr("██████╔╝██║ ██║██████╔╝█████╗ ██████╔╝ ██║ ");
|
||||
dbgserial_putstr("██╔══██╗██║ ██║██╔══██╗██╔══╝ ██╔══██╗ ██║ ");
|
||||
dbgserial_putstr("██║ ██║╚██████╔╝██████╔╝███████╗██║ ██║ ██║ ");
|
||||
dbgserial_putstr("╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚══════╝╚═╝ ╚═╝ ╚═╝ ");
|
||||
|
||||
// Enable the 3.2V rail for the benefit of the FPGA and display
|
||||
pmic_init();
|
||||
|
||||
boot_bit_init();
|
||||
|
||||
boot_version_write();
|
||||
|
||||
prv_print_bootloader_version();
|
||||
|
||||
if (boot_bit_test(BOOT_BIT_FW_STABLE)) {
|
||||
dbgserial_putstr("Last firmware boot was stable; clear strikes");
|
||||
|
||||
boot_bit_clear(BOOT_BIT_FW_STABLE);
|
||||
|
||||
boot_bit_clear(BOOT_BIT_FW_START_FAIL_STRIKE_ONE);
|
||||
boot_bit_clear(BOOT_BIT_FW_START_FAIL_STRIKE_TWO);
|
||||
boot_bit_clear(BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_ONE);
|
||||
boot_bit_clear(BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_TWO);
|
||||
}
|
||||
|
||||
flash_init();
|
||||
button_init();
|
||||
pmic_init();
|
||||
display_init();
|
||||
|
||||
display_boot_splash();
|
||||
|
||||
if (boot_test_is_button_stuck()) {
|
||||
prv_sad_watch(ERROR_STUCK_BUTTON);
|
||||
}
|
||||
|
||||
if (boot_test_is_flash_broken()) {
|
||||
prv_sad_watch(ERROR_BAD_SPI_FLASH);
|
||||
}
|
||||
|
||||
boot_bit_dump();
|
||||
|
||||
// If the recovery firmware crashed at start-up, the watch is now a $199 brick. That's life!
|
||||
if (prv_check_for_recovery_start_failure()) {
|
||||
boot_bit_clear(BOOT_BIT_RECOVERY_START_IN_PROGRESS);
|
||||
prv_sad_watch(ERROR_CANT_LOAD_FW);
|
||||
}
|
||||
|
||||
bool force_boot_recovery_mode = prv_check_force_boot_recovery();
|
||||
if (force_boot_recovery_mode) {
|
||||
dbgserial_putstr("Force-booting recovery mode...");
|
||||
}
|
||||
|
||||
if (force_boot_recovery_mode || prv_check_for_fw_start_failure()) {
|
||||
if (!fw_copy_switch_to_recovery_fw()) {
|
||||
// We've failed to load recovery mode too many times.
|
||||
prv_sad_watch(ERROR_CANT_LOAD_FW);
|
||||
}
|
||||
} else {
|
||||
fw_copy_check_update_fw();
|
||||
}
|
||||
|
||||
if (prv_check_and_increment_reset_loop_detection_bits()) {
|
||||
prv_sad_watch(ERROR_RESET_LOOP);
|
||||
}
|
||||
|
||||
#if !NO_WATCHDOG
|
||||
dbgserial_putstr("Enabling watchdog");
|
||||
watchdog_init();
|
||||
watchdog_start();
|
||||
#endif
|
||||
|
||||
gpio_disable_all();
|
||||
|
||||
prv_jump_to_fw();
|
||||
}
|
||||
|
||||
// Stubs for libg_s.a, which is our libc implementation from nano-newlib
|
||||
void _exit(int status) {
|
||||
}
|
34
platform/robert/boot/src/pebble_errors.h
Normal file
34
platform/robert/boot/src/pebble_errors.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
static const uint32_t ERROR_NO_ACTIVE_ERROR = 0;
|
||||
|
||||
// FW Errors
|
||||
static const uint32_t ERROR_STUCK_BUTTON = 0xfe504501;
|
||||
static const uint32_t ERROR_BAD_SPI_FLASH = 0xfe504502;
|
||||
static const uint32_t ERROR_CANT_LOAD_FW = 0xfe504503;
|
||||
static const uint32_t ERROR_RESET_LOOP = 0xfe504504;
|
||||
|
||||
// BT Errors
|
||||
static const uint32_t ERROR_CANT_LOAD_BT = 0xfe504510;
|
||||
static const uint32_t ERROR_CANT_START_LSE = 0xfe504511;
|
||||
static const uint32_t ERROR_CANT_START_ACCEL = 0xfe504512;
|
||||
|
||||
static const uint32_t ERROR_LOW_BATTERY = 0xfe504520;
|
147
platform/robert/boot/src/stm32f_flash_boot.ld
Normal file
147
platform/robert/boot/src/stm32f_flash_boot.ld
Normal file
|
@ -0,0 +1,147 @@
|
|||
__Stack_Size = 8192;
|
||||
PROVIDE ( _Stack_Size = __Stack_Size ) ;
|
||||
|
||||
__Stack_Init = _estack - __Stack_Size ;
|
||||
PROVIDE ( _Stack_Init = __Stack_Init ) ;
|
||||
|
||||
/*
|
||||
There will be a link error if there is not this amount of RAM free at the end.
|
||||
*/
|
||||
_Minimum_Stack_Size = 0x100 ;
|
||||
|
||||
MEMORY
|
||||
{
|
||||
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
|
||||
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 32K
|
||||
}
|
||||
|
||||
__BOOTLOADER_size__ = LENGTH(FLASH);
|
||||
|
||||
__end_heap = ORIGIN(RAM) + LENGTH(RAM);
|
||||
PROVIDE(_heap_end = __end_heap);
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
/* for Cortex devices, the beginning of the startup code is stored in the .isr_vector section, which goes to FLASH */
|
||||
.isr_vector :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
KEEP(*(.isr_vector)) /* Startup code */
|
||||
. = ALIGN(4);
|
||||
} >FLASH
|
||||
|
||||
/* for some STRx devices, the beginning of the startup code is stored in the .flashtext section, which goes to FLASH */
|
||||
.flashtext :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
*(.flashtext) /* Startup code */
|
||||
. = ALIGN(4);
|
||||
} >FLASH
|
||||
|
||||
/* Exception handling sections. "contains index entries for section unwinding" */
|
||||
.ARM.exidx :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
*(.ARM.exidx)
|
||||
. = ALIGN(4);
|
||||
} >FLASH
|
||||
|
||||
/* the program code is stored in the .text section, which goes to Flash */
|
||||
.text :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
|
||||
*(.text) /* remaining code */
|
||||
*(.text.*) /* remaining code */
|
||||
*(.rodata) /* read-only data (constants) */
|
||||
*(.rodata*)
|
||||
*(.constdata) /* read-only data (constants) */
|
||||
*(.constdata*)
|
||||
*(.glue_7)
|
||||
*(.glue_7t)
|
||||
*(i.*)
|
||||
|
||||
. = ALIGN(4);
|
||||
} >FLASH
|
||||
|
||||
/* This is the initialized data section
|
||||
The program executes knowing that the data is in the RAM
|
||||
but the loader puts the initial values in the FLASH (inidata).
|
||||
It is one task of the startup to copy the initial values from FLASH to RAM. */
|
||||
.data : {
|
||||
. = ALIGN(4);
|
||||
/* This is used by the startup in order to initialize the .data secion */
|
||||
__data_start = .;
|
||||
|
||||
*(.data)
|
||||
*(.data.*)
|
||||
|
||||
. = ALIGN(4);
|
||||
__data_end = .; /* This is used by the startup in order to initialize the .data secion */
|
||||
} >RAM AT>FLASH
|
||||
__data_load_start = LOADADDR(.data);
|
||||
|
||||
/* This is the uninitialized data section */
|
||||
.bss (NOLOAD) : {
|
||||
. = ALIGN(4);
|
||||
__bss_start = .; /* This is used by the startup in order to initialize the .bss secion */
|
||||
|
||||
*(.bss)
|
||||
*(.bss.*)
|
||||
*(COMMON)
|
||||
|
||||
. = ALIGN(4);
|
||||
__bss_end = .; /* This is used by the startup in order to initialize the .bss secion */
|
||||
} >RAM
|
||||
|
||||
.stack (NOLOAD) : {
|
||||
. = ALIGN(8);
|
||||
_sstack = .;
|
||||
. = . + __Stack_Size;
|
||||
. = ALIGN(8);
|
||||
_estack = .;
|
||||
} >RAM
|
||||
|
||||
/* after that it's only debugging information. */
|
||||
|
||||
/* remove the debugging information from the standard libraries */
|
||||
DISCARD : {
|
||||
libc.a ( * )
|
||||
libm.a ( * )
|
||||
libgcc.a ( * )
|
||||
}
|
||||
|
||||
/* Stabs debugging sections. */
|
||||
.stab 0 : { *(.stab) }
|
||||
.stabstr 0 : { *(.stabstr) }
|
||||
.stab.excl 0 : { *(.stab.excl) }
|
||||
.stab.exclstr 0 : { *(.stab.exclstr) }
|
||||
.stab.index 0 : { *(.stab.index) }
|
||||
.stab.indexstr 0 : { *(.stab.indexstr) }
|
||||
.comment 0 : { *(.comment) }
|
||||
/* DWARF debug sections.
|
||||
Symbols in the DWARF debugging sections are relative to the beginning
|
||||
of the section so we begin them at 0. */
|
||||
/* DWARF 1 */
|
||||
.debug 0 : { *(.debug) }
|
||||
.line 0 : { *(.line) }
|
||||
/* GNU DWARF 1 extensions */
|
||||
.debug_srcinfo 0 : { *(.debug_srcinfo) }
|
||||
.debug_sfnames 0 : { *(.debug_sfnames) }
|
||||
/* DWARF 1.1 and DWARF 2 */
|
||||
.debug_aranges 0 : { *(.debug_aranges) }
|
||||
.debug_pubnames 0 : { *(.debug_pubnames) }
|
||||
/* DWARF 2 */
|
||||
.debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
|
||||
.debug_abbrev 0 : { *(.debug_abbrev) }
|
||||
.debug_line 0 : { *(.debug_line) }
|
||||
.debug_frame 0 : { *(.debug_frame) }
|
||||
.debug_str 0 : { *(.debug_str) }
|
||||
.debug_loc 0 : { *(.debug_loc) }
|
||||
.debug_macinfo 0 : { *(.debug_macinfo) }
|
||||
/* SGI/MIPS DWARF 2 extensions */
|
||||
.debug_weaknames 0 : { *(.debug_weaknames) }
|
||||
.debug_funcnames 0 : { *(.debug_funcnames) }
|
||||
.debug_typenames 0 : { *(.debug_typenames) }
|
||||
.debug_varnames 0 : { *(.debug_varnames) }
|
||||
}
|
71
platform/robert/boot/src/system/bootbits.c
Normal file
71
platform/robert/boot/src/system/bootbits.c
Normal file
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "drivers/dbgserial.h"
|
||||
#include "drivers/pwr.h"
|
||||
#include "system/bootbits.h"
|
||||
#include "system/rtc_registers.h"
|
||||
|
||||
#include "git_version.auto.h"
|
||||
|
||||
#include "stm32f7xx.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdint.h>
|
||||
|
||||
static const uint32_t s_bootloader_timestamp = GIT_TIMESTAMP;
|
||||
|
||||
void boot_bit_init(void) {
|
||||
pwr_access_backup_domain(true);
|
||||
|
||||
if (!boot_bit_test(BOOT_BIT_INITIALIZED)) {
|
||||
RTC_WriteBackupRegister(RTC_BKP_BOOTBIT_DR, BOOT_BIT_INITIALIZED);
|
||||
}
|
||||
}
|
||||
|
||||
void boot_bit_set(BootBitValue bit) {
|
||||
uint32_t current_value = RTC_ReadBackupRegister(RTC_BKP_BOOTBIT_DR);
|
||||
current_value |= bit;
|
||||
RTC_WriteBackupRegister(RTC_BKP_BOOTBIT_DR, current_value);
|
||||
}
|
||||
|
||||
void boot_bit_clear(BootBitValue bit) {
|
||||
uint32_t current_value = RTC_ReadBackupRegister(RTC_BKP_BOOTBIT_DR);
|
||||
current_value &= ~bit;
|
||||
RTC_WriteBackupRegister(RTC_BKP_BOOTBIT_DR, current_value);
|
||||
}
|
||||
|
||||
bool boot_bit_test(BootBitValue bit) {
|
||||
uint32_t current_value = RTC_ReadBackupRegister(RTC_BKP_BOOTBIT_DR);
|
||||
return (current_value & bit);
|
||||
}
|
||||
|
||||
void boot_bit_dump(void) {
|
||||
dbgserial_print("Boot bits: ");
|
||||
dbgserial_print_hex(RTC_ReadBackupRegister(RTC_BKP_BOOTBIT_DR));
|
||||
dbgserial_newline();
|
||||
}
|
||||
|
||||
void boot_version_write(void) {
|
||||
if (boot_version_read() == s_bootloader_timestamp) {
|
||||
return;
|
||||
}
|
||||
RTC_WriteBackupRegister(BOOTLOADER_VERSION_REGISTER, s_bootloader_timestamp);
|
||||
}
|
||||
|
||||
uint32_t boot_version_read(void) {
|
||||
return RTC_ReadBackupRegister(BOOTLOADER_VERSION_REGISTER);
|
||||
}
|
54
platform/robert/boot/src/system/bootbits.h
Normal file
54
platform/robert/boot/src/system/bootbits.h
Normal file
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef enum BootBitValue {
|
||||
BOOT_BIT_INITIALIZED = 0x1 << 0,
|
||||
BOOT_BIT_NEW_FW_AVAILABLE = 0x1 << 1,
|
||||
BOOT_BIT_NEW_FW_UPDATE_IN_PROGRESS = 0x1 << 2,
|
||||
BOOT_BIT_FW_START_FAIL_STRIKE_ONE = 0x1 << 3,
|
||||
BOOT_BIT_FW_START_FAIL_STRIKE_TWO = 0x1 << 4,
|
||||
BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_ONE = 0x1 << 5,
|
||||
BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_TWO = 0x1 << 6,
|
||||
BOOT_BIT_RECOVERY_START_IN_PROGRESS = 0x1 << 7,
|
||||
BOOT_BIT_STANDBY_MODE_REQUESTED = 0x1 << 8, //!< Bootloader enter standby immediately after reset.
|
||||
BOOT_BIT_SOFTWARE_FAILURE_OCCURRED = 0x1 << 9,
|
||||
BOOT_BIT_NEW_SYSTEM_RESOURCES_AVAILABLE = 0x1 << 10,
|
||||
BOOT_BIT_RESET_LOOP_DETECT_ONE = 0x1 << 11,
|
||||
BOOT_BIT_RESET_LOOP_DETECT_TWO = 0x1 << 12,
|
||||
BOOT_BIT_RESET_LOOP_DETECT_THREE = 0x1 << 13,
|
||||
BOOT_BIT_FW_STABLE = 0x1 << 14,
|
||||
BOOT_BIT_NEW_FW_INSTALLED = 0x1 << 15,
|
||||
BOOT_BIT_STANDBY_MODE_ENTERED = 0x1 << 16,
|
||||
BOOT_BIT_FORCE_PRF = 0x1 << 17,
|
||||
BOOT_BIT_NEW_PRF_AVAILABLE = 0x1 << 18,
|
||||
BOOT_BIT_SHUTDOWN_REQUESTED = 0x1 << 19, //!< Bootloader hard power-off instead of jumping to fw.
|
||||
} BootBitValue;
|
||||
|
||||
void boot_bit_init();
|
||||
void boot_bit_set(BootBitValue bit);
|
||||
void boot_bit_clear(BootBitValue bit);
|
||||
bool boot_bit_test(BootBitValue bit);
|
||||
|
||||
// Dump the contents through dbgserial
|
||||
void boot_bit_dump(void);
|
||||
|
||||
void boot_version_write(void);
|
||||
uint32_t boot_version_read(void);
|
33
platform/robert/boot/src/system/die.c
Normal file
33
platform/robert/boot/src/system/die.c
Normal file
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "die.h"
|
||||
#include "drivers/dbgserial.h"
|
||||
#include "system/reset.h"
|
||||
#include "system/passert.h"
|
||||
|
||||
NORETURN reset_due_to_software_failure(void) {
|
||||
#if defined(NO_WATCHDOG)
|
||||
// Don't reset right away, leave it in a state we can inspect
|
||||
|
||||
while (1) {
|
||||
BREAKPOINT;
|
||||
}
|
||||
#endif
|
||||
|
||||
dbgserial_putstr("Software failure; resetting!");
|
||||
system_reset();
|
||||
}
|
23
platform/robert/boot/src/system/die.h
Normal file
23
platform/robert/boot/src/system/die.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "util/attributes.h"
|
||||
|
||||
//! Does not call reboot_reason_set, only calls reboot_reason_set_restarted_safely if we were
|
||||
//! able shut everything down nicely before rebooting.
|
||||
NORETURN reset_due_to_software_failure(void);
|
30
platform/robert/boot/src/system/firmware_storage.c
Normal file
30
platform/robert/boot/src/system/firmware_storage.c
Normal file
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "firmware_storage.h"
|
||||
|
||||
#include "drivers/flash.h"
|
||||
|
||||
FirmwareDescription firmware_storage_read_firmware_description(uint32_t firmware_start_address) {
|
||||
FirmwareDescription firmware_description;
|
||||
flash_read_bytes((uint8_t*)&firmware_description, firmware_start_address,
|
||||
sizeof(FirmwareDescription));
|
||||
return firmware_description;
|
||||
}
|
||||
|
||||
bool firmware_storage_check_valid_firmware_description(FirmwareDescription* desc) {
|
||||
return desc->description_length == sizeof(FirmwareDescription);
|
||||
}
|
33
platform/robert/boot/src/system/firmware_storage.h
Normal file
33
platform/robert/boot/src/system/firmware_storage.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
//! @file firmware_storage.h
|
||||
//! Utilities for reading a firmware image stored in flash.
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct __attribute__((__packed__)) FirmwareDescription {
|
||||
uint32_t description_length;
|
||||
uint32_t firmware_length;
|
||||
uint32_t checksum;
|
||||
} FirmwareDescription;
|
||||
|
||||
FirmwareDescription firmware_storage_read_firmware_description(uint32_t firmware_start_address);
|
||||
|
||||
bool firmware_storage_check_valid_firmware_description(FirmwareDescription* desc);
|
64
platform/robert/boot/src/system/logging.h
Normal file
64
platform/robert/boot/src/system/logging.h
Normal file
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "drivers/dbgserial.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifndef __FILE_NAME__
|
||||
#define __FILE_NAME__ __FILE__
|
||||
#endif
|
||||
|
||||
#define LOG_LEVEL_ALWAYS 0
|
||||
#define LOG_LEVEL_ERROR 1
|
||||
#define LOG_LEVEL_WARNING 50
|
||||
#define LOG_LEVEL_INFO 100
|
||||
#define LOG_LEVEL_DEBUG 200
|
||||
#define LOG_LEVEL_DEBUG_VERBOSE 255
|
||||
|
||||
#ifndef STRINGIFY
|
||||
#define STRINGIFY_NX(a) #a
|
||||
#define STRINGIFY(a) STRINGIFY_NX(a)
|
||||
#endif // STRINGIFY
|
||||
|
||||
#define STATUS_STRING(s) STRINGIFY(s)
|
||||
|
||||
#ifdef PBL_LOG_ENABLED
|
||||
#define PBL_LOG(level, fmt, args...) \
|
||||
do { \
|
||||
char _pbl_log_buffer[128]; \
|
||||
dbgserial_putstr(__FILE_NAME__ ":" STRINGIFY(__LINE__) "> " fmt); \
|
||||
} while (0)
|
||||
|
||||
#ifdef VERBOSE_LOGGING
|
||||
|
||||
#define PBL_LOG_VERBOSE(fmt, args...) \
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, fmt, ## args)
|
||||
|
||||
#else // VERBOSE_LOGGING
|
||||
#define PBL_LOG_VERBOSE(fmt, args...)
|
||||
#endif // VERBOSE_LOGGING
|
||||
|
||||
#else // PBL_LOG_ENABLED
|
||||
#define PBL_LOG(level, fmt, args...)
|
||||
#define PBL_LOG_VERBOSE(fmt, args...)
|
||||
#endif // PBL_LOG_ENABLED
|
87
platform/robert/boot/src/system/passert.c
Normal file
87
platform/robert/boot/src/system/passert.c
Normal file
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "passert.h"
|
||||
|
||||
#include "drivers/dbgserial.h"
|
||||
#include "system/die.h"
|
||||
#include "util/attributes.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static NORETURN prv_handle_passert_failed_vargs(const char* filename, int line_number,
|
||||
uintptr_t lr, const char* expr, const char* fmt, va_list fmt_args) {
|
||||
dbgserial_print("ASSERT: ");
|
||||
dbgserial_print(expr);
|
||||
dbgserial_print(" ");
|
||||
dbgserial_print(filename);
|
||||
dbgserial_print(":");
|
||||
dbgserial_print_hex(line_number);
|
||||
if (fmt) {
|
||||
dbgserial_print(" ");
|
||||
dbgserial_print(fmt);
|
||||
}
|
||||
dbgserial_putstr("");
|
||||
|
||||
reset_due_to_software_failure();
|
||||
}
|
||||
|
||||
static NORETURN prv_handle_passert_failed(const char* filename, int line_number,
|
||||
uintptr_t lr, const char *expr, const char* fmt, ...) {
|
||||
va_list fmt_args;
|
||||
va_start(fmt_args, fmt);
|
||||
|
||||
prv_handle_passert_failed_vargs(filename, line_number, lr, expr, fmt, fmt_args);
|
||||
|
||||
va_end(fmt_args);
|
||||
}
|
||||
|
||||
void passert_failed(const char* filename, int line_number, const char* message, ...) {
|
||||
va_list fmt_args;
|
||||
va_start(fmt_args, message);
|
||||
|
||||
prv_handle_passert_failed_vargs(filename, line_number,
|
||||
(uintptr_t)__builtin_return_address(0), "ASSERT", message, fmt_args);
|
||||
|
||||
va_end(fmt_args);
|
||||
}
|
||||
|
||||
void passert_failed_no_message(const char* filename, int line_number) {
|
||||
prv_handle_passert_failed(filename, line_number,
|
||||
(uintptr_t)__builtin_return_address(0), "ASSERTN", NULL);
|
||||
}
|
||||
|
||||
void wtf(void) {
|
||||
uintptr_t saved_lr = (uintptr_t) __builtin_return_address(0);
|
||||
dbgserial_print("*** WTF ");
|
||||
dbgserial_print_hex(saved_lr);
|
||||
dbgserial_putstr("");
|
||||
reset_due_to_software_failure();
|
||||
}
|
||||
|
||||
//! Assert function called by the STM peripheral library's
|
||||
//! 'assert_param' method. See stm32f2xx_conf.h for more information.
|
||||
void assert_failed(uint8_t* file, uint32_t line) {
|
||||
register uintptr_t lr __asm("lr");
|
||||
uintptr_t saved_lr = lr;
|
||||
|
||||
prv_handle_passert_failed((const char*) file, line, saved_lr, "STM32", "STM32 peripheral library "
|
||||
"tripped an assert");
|
||||
}
|
53
platform/robert/boot/src/system/passert.h
Normal file
53
platform/robert/boot/src/system/passert.h
Normal file
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "logging.h"
|
||||
|
||||
|
||||
void passert_failed(const char* filename, int line_number, const char* message, ...)
|
||||
__attribute__((noreturn));
|
||||
|
||||
#define PBL_ASSERT(expr, ...) \
|
||||
do { \
|
||||
if (!(expr)) { \
|
||||
passert_failed(__FILE_NAME__, __LINE__, __VA_ARGS__); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define PBL_ASSERTN(expr) \
|
||||
do { \
|
||||
if (!(expr)) { \
|
||||
passert_failed_no_message(__FILE_NAME__, __LINE__); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
void passert_failed_no_message(const char* filename, int line_number)
|
||||
__attribute__((noreturn));
|
||||
|
||||
void wtf(void) __attribute__((noreturn));
|
||||
|
||||
#define WTF wtf()
|
||||
|
||||
// Insert a compiled-in breakpoint
|
||||
#define BREAKPOINT __asm("bkpt")
|
||||
|
||||
#define PBL_ASSERT_PRIVILEGED()
|
||||
#define PBL_ASSERT_TASK(task)
|
||||
|
||||
#define PBL_CROAK(fmt, args...) \
|
||||
passert_failed(__FILE_NAME__, __LINE__, "*** CROAK: " fmt, ## args)
|
36
platform/robert/boot/src/system/reset.c
Normal file
36
platform/robert/boot/src/system/reset.c
Normal file
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "system/reset.h"
|
||||
|
||||
#include "drivers/display.h"
|
||||
|
||||
#include "stm32f7xx.h"
|
||||
|
||||
void system_reset(void) {
|
||||
display_prepare_for_reset();
|
||||
|
||||
// Clear the reset reason since it will no longer
|
||||
// apply after this bootloader reset
|
||||
RCC_ClearFlag();
|
||||
|
||||
system_hard_reset();
|
||||
}
|
||||
|
||||
void system_hard_reset(void) {
|
||||
NVIC_SystemReset();
|
||||
__builtin_unreachable();
|
||||
}
|
26
platform/robert/boot/src/system/reset.h
Normal file
26
platform/robert/boot/src/system/reset.h
Normal file
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
//! Reset nicely after shutting down system services. Does not set the reboot_reason other than
|
||||
//! calling reboot_reason_set_restarted_safely just before the reset occurs.
|
||||
void system_reset(void)__attribute__((noreturn));
|
||||
|
||||
//! The final stage in the reset process.
|
||||
void system_hard_reset(void) __attribute__((noreturn));
|
31
platform/robert/boot/src/system/rtc_registers.h
Normal file
31
platform/robert/boot/src/system/rtc_registers.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#define RTC_BKP_BOOTBIT_DR RTC_BKP_DR0
|
||||
#define STUCK_BUTTON_REGISTER RTC_BKP_DR1
|
||||
#define BOOTLOADER_VERSION_REGISTER RTC_BKP_DR2
|
||||
#define CURRENT_TIME_REGISTER RTC_BKP_DR3
|
||||
#define CURRENT_INTERVAL_TICKS_REGISTER RTC_BKP_DR4
|
||||
#define REBOOT_REASON_REGISTER_1 RTC_BKP_DR5
|
||||
#define REBOOT_REASON_REGISTER_2 RTC_BKP_DR6
|
||||
#define REBOOT_REASON_STUCK_TASK_PC RTC_BKP_DR7
|
||||
#define REBOOT_REASON_STUCK_TASK_LR RTC_BKP_DR8
|
||||
#define REBOOT_REASON_STUCK_TASK_CALLBACK RTC_BKP_DR9
|
||||
#define REBOOT_REASON_MUTEX_LR RTC_BKP_DR10 // Now REBOOT_REASON_DROPPED_EVENT
|
||||
#define REBOOT_REASON_MUTEX_PC RTC_BKP_DR11 // Deprecated
|
||||
#define SLOT_OF_LAST_LAUNCHED_APP RTC_BKP_DR19
|
50
platform/robert/boot/src/util/attributes.h
Normal file
50
platform/robert/boot/src/util/attributes.h
Normal file
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if defined(__clang__)
|
||||
#define GCC_ONLY(x)
|
||||
#else
|
||||
#define GCC_ONLY(x) x
|
||||
#endif
|
||||
|
||||
// Function attributes
|
||||
#define FORMAT_FUNC(TYPE, STR_IDX, FIRST) __attribute__((__format__(TYPE, STR_IDX, FIRST)))
|
||||
|
||||
#define FORMAT_PRINTF(STR_IDX, FIRST) FORMAT_FUNC(__printf__, STR_IDX, FIRST)
|
||||
|
||||
#define ALWAYS_INLINE __attribute__((__always_inline__)) inline
|
||||
#define NOINLINE __attribute__((__noinline__))
|
||||
#define NORETURN __attribute__((__noreturn__)) void
|
||||
#define NAKED_FUNC __attribute__((__naked__))
|
||||
#define OPTIMIZE_FUNC(LVL) __attribute__((__optimize__(LVL)))
|
||||
#define CONST_FUNC __attribute__((__const__))
|
||||
#define PURE_FUNC __attribute__((__pure__))
|
||||
|
||||
// Variable attributes
|
||||
#define ATTR_CLEANUP(FUNC) __attribute__((__cleanup__(FUNC)))
|
||||
|
||||
// Structure attributes
|
||||
#define PACKED __attribute__((__packed__))
|
||||
|
||||
// General attributes
|
||||
#define USED __attribute__((__used__))
|
||||
#define UNUSED __attribute__((__unused__))
|
||||
#define WEAK __attribute__((__weak__))
|
||||
#define ALIAS(sym) __attribute__((__weak__, __alias__(sym)))
|
||||
#define SECTION(SEC) GCC_ONLY(__attribute__((__section__(SEC))))
|
||||
#define EXTERNALLY_VISIBLE GCC_ONLY(__attribute__((__externally_visible__)))
|
51
platform/robert/boot/src/util/cobs.c
Normal file
51
platform/robert/boot/src/util/cobs.c
Normal file
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "cobs.h"
|
||||
|
||||
size_t cobs_encode(void *dst_ptr, const void *src_ptr, size_t length) {
|
||||
const char *src = src_ptr;
|
||||
char *dst = dst_ptr;
|
||||
uint8_t code = 0x01;
|
||||
size_t code_idx = 0;
|
||||
size_t dst_idx = 1;
|
||||
|
||||
for (size_t src_idx = 0; src_idx < length; ++src_idx) {
|
||||
if (src[src_idx] == '\0') {
|
||||
dst[code_idx] = code;
|
||||
code_idx = dst_idx++;
|
||||
code = 0x01;
|
||||
} else {
|
||||
dst[dst_idx++] = src[src_idx];
|
||||
code++;
|
||||
if (code == 0xff) {
|
||||
if (src_idx == length - 1) {
|
||||
// Special case: the final encoded block is 254 bytes long with no
|
||||
// zero after it. While it's technically a valid encoding if a
|
||||
// trailing zero is appended, it causes the output to be one byte
|
||||
// longer than it needs to be. This violates consistent overhead
|
||||
// contract and could overflow a carefully sized buffer.
|
||||
break;
|
||||
}
|
||||
dst[code_idx] = code;
|
||||
code_idx = dst_idx++;
|
||||
code = 0x01;
|
||||
}
|
||||
}
|
||||
}
|
||||
dst[code_idx] = code;
|
||||
return dst_idx;
|
||||
}
|
40
platform/robert/boot/src/util/cobs.h
Normal file
40
platform/robert/boot/src/util/cobs.h
Normal file
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
//! An implementation of Consistent Overhead Byte Stuffing
|
||||
//!
|
||||
//! http://conferences.sigcomm.org/sigcomm/1997/papers/p062.pdf
|
||||
//! http://en.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing
|
||||
|
||||
//! Evaluates to the offset required when encoding in-place
|
||||
#define COBS_OVERHEAD(n) (((n) + 253) / 254)
|
||||
//! Evaluates to the maximum buffer size required to hold n bytes of data
|
||||
//! after COBS encoding.
|
||||
#define MAX_SIZE_AFTER_COBS_ENCODING(n) ((n) + COBS_OVERHEAD(n))
|
||||
|
||||
//! COBS-encode a buffer out to another buffer.
|
||||
//!
|
||||
//! @param [out] dst destination buffer. The buffer must be at least
|
||||
//! MAX_SIZE_AFTER_COBS_ENCODING(length) bytes long.
|
||||
//! @param [in] src source buffer
|
||||
//! @param length length of src
|
||||
size_t cobs_encode(void * restrict dst, const void * restrict src,
|
||||
size_t length);
|
48
platform/robert/boot/src/util/crc32.c
Normal file
48
platform/robert/boot/src/util/crc32.c
Normal file
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "util/crc32.h"
|
||||
|
||||
// Nybble-wide table driven CRC-32 algorithm
|
||||
//
|
||||
// A compromise between speed and size, this algorithm uses a lookup table to
|
||||
// calculate the CRC four bits at a time with a size cost of only 64 bytes. By
|
||||
// contrast, a byte-wide algorithm requires a lookup table sixteen times larger!
|
||||
//
|
||||
// The lookup table is generated by the crc32_lut.py
|
||||
|
||||
static const uint32_t s_lookup_table[] = {
|
||||
0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac,
|
||||
0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,
|
||||
0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c,
|
||||
0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c,
|
||||
};
|
||||
|
||||
uint32_t crc32(uint32_t crc, const void * restrict data, size_t length) {
|
||||
if (data == 0) {
|
||||
return 0;
|
||||
}
|
||||
const uint8_t * restrict bytes = data;
|
||||
|
||||
crc ^= 0xffffffff;
|
||||
while (length--) {
|
||||
crc = (crc >> 4) ^ s_lookup_table[(crc ^ *bytes) & 0xf];
|
||||
crc = (crc >> 4) ^ s_lookup_table[(crc ^ (*bytes >> 4)) & 0xf];
|
||||
bytes++;
|
||||
}
|
||||
crc ^= 0xffffffff;
|
||||
return crc;
|
||||
}
|
67
platform/robert/boot/src/util/crc32.h
Normal file
67
platform/robert/boot/src/util/crc32.h
Normal file
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
//! \file
|
||||
//! Calculate the CRC-32 checksum of data.
|
||||
//!
|
||||
//! The checksum is the standard CRC-32 used by zlib, PNG and others.
|
||||
//! The model parameters for the algorithm, as described in A Painless Guide to
|
||||
//! CRC Error Detection Algorithms (http://www.zlib.net/crc_v3.txt), are:
|
||||
//! Name: "CRC-32"
|
||||
//! Width: 32
|
||||
//! Poly: 04C11DB7
|
||||
//! Init: FFFFFFFF
|
||||
//! RefIn: True
|
||||
//! RefOut: True
|
||||
//! XorOut: FFFFFFFF
|
||||
//! Check: CBF43926
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
//! Update a running CRC-32 checksum with the bytes of data and return the
|
||||
//! updated CRC-32. If data is NULL, the function returns the required initial
|
||||
//! value for the CRC.
|
||||
//!
|
||||
//! This function is drop-in compatible with zlib's crc32 function.
|
||||
//!
|
||||
//! \par Usage
|
||||
//! \code
|
||||
//! uint32_t crc = crc32(0, NULL, 0);
|
||||
//! while (read_buffer(data, length)) {
|
||||
//! crc = crc32(crc, data, length);
|
||||
//! }
|
||||
//! \endcode
|
||||
uint32_t crc32(uint32_t crc, const void * restrict data, size_t length);
|
||||
|
||||
//! The initial CRC register value for a standard CRC-32 checksum.
|
||||
//!
|
||||
//! It is the same value as is returned by the `crc32` function when data is
|
||||
//! NULL.
|
||||
//!
|
||||
//! \code
|
||||
//! assert(CRC32_INIT == crc32(0, NULL, 0));
|
||||
//! \endcode
|
||||
#define CRC32_INIT (0)
|
||||
|
||||
//! The residue constant of the CRC-32 algorithm.
|
||||
//!
|
||||
//! If the CRC-32 value of a message is appended (little-endian) onto the
|
||||
//! end of the message, the CRC-32 of the concatenated message and CRC will be
|
||||
//! equal to CRC32_RESIDUE if the message has not been corrupted in transit.
|
||||
#define CRC32_RESIDUE (0x2144DF1C)
|
47
platform/robert/boot/src/util/delay.c
Normal file
47
platform/robert/boot/src/util/delay.c
Normal file
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "delay.h"
|
||||
#include "util/attributes.h"
|
||||
|
||||
#include "stm32f7xx.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
void delay_us(uint32_t us) {
|
||||
// Empirically (measured on a C2 bb), 1 loop = 1 cycle. (sysclk @
|
||||
// 16MHz, I-Cache disabled) Alignment of code will have some impact on how
|
||||
// long this actually takes
|
||||
uint32_t delay_loops = us * 16;
|
||||
|
||||
__asm volatile (
|
||||
"spinloop: \n"
|
||||
" subs %[delay_loops], #1 \n"
|
||||
" bne spinloop \n"
|
||||
: [delay_loops] "+r" (delay_loops) // read-write operand
|
||||
:
|
||||
: "cc"
|
||||
);
|
||||
}
|
||||
|
||||
void delay_ms(uint32_t millis) {
|
||||
// delay_us(millis*1000) is not used because a long delay could easily
|
||||
// overflow the veriable. Without the outer loop, a delay of even five
|
||||
// seconds would overflow.
|
||||
while (millis--) {
|
||||
delay_us(1000);
|
||||
}
|
||||
}
|
25
platform/robert/boot/src/util/delay.h
Normal file
25
platform/robert/boot/src/util/delay.h
Normal file
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
//! Carefully timed spinloop that allows one to delay at a microsecond
|
||||
//! granularity.
|
||||
void delay_us(uint32_t us);
|
||||
|
||||
void delay_ms(uint32_t millis);
|
31
platform/robert/boot/src/util/math.c
Normal file
31
platform/robert/boot/src/util/math.c
Normal file
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "util/math.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
int ceil_log_two(uint32_t n) {
|
||||
// clz stands for Count Leading Zeroes. We use it to find the MSB
|
||||
int msb = 31 - __builtin_clz(n);
|
||||
// popcount counts the number of set bits in a word (1's)
|
||||
bool power_of_two = __builtin_popcount(n) == 1;
|
||||
// if not exact power of two, use the next power of two
|
||||
// we want to err on the side of caution and want to
|
||||
// always round up
|
||||
return ((power_of_two) ? msb : (msb + 1));
|
||||
}
|
79
platform/robert/boot/src/util/math.h
Normal file
79
platform/robert/boot/src/util/math.h
Normal file
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
|
||||
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
|
||||
#define ABS(a) (((a) > 0) ? (a) : -1 * (a))
|
||||
#define CLIP(n, min, max) ((n) < (min) ? (min) : ((n) > (max) ? (max) : (n)))
|
||||
#define ROUND(num, denom) (((num) + ((denom) / 2))/(denom))
|
||||
#define WITHIN(n, min, max) ((n) >= (min) && (n) <= (max))
|
||||
#define RANGE_WITHIN(n_min, n_max, min, max) ((n_min) >= (min) && (n_max) <= (max))
|
||||
|
||||
// Divide num by denom, rounding up (ceil(0.5) is 1.0, and ceil(-0.5) is 0.0)
|
||||
// ex. 3, 4 (ie. 3/4) : returns 1
|
||||
// ex. -3, 4 : returns 0
|
||||
#define DIVIDE_CEIL(num, denom) ((num + (denom - 1)) / denom)
|
||||
|
||||
// Round value up to the next increment of modulus
|
||||
// ex. val = 152 mod = 32 : returns 160
|
||||
// val = -32 mod = 90 : returns -90
|
||||
#define ROUND_TO_MOD_CEIL(val, mod) \
|
||||
((val >= 0) ? \
|
||||
((((val) + ABS(ABS(mod) - 1)) / ABS(mod)) * ABS(mod)) : \
|
||||
-((((-val) + ABS(ABS(mod) - 1)) / ABS(mod)) * ABS(mod)))
|
||||
|
||||
/*
|
||||
* find the log base two of a number rounded up
|
||||
*/
|
||||
int ceil_log_two(uint32_t n);
|
||||
|
||||
/*
|
||||
* The -Wtype-limits flag generated an error with the previous IS_SIGNED maco.
|
||||
* If an unsigned number was passed in the macro would check if the unsigned number was less than 0.
|
||||
*/
|
||||
//! Determine whether a variable is signed or not.
|
||||
//! @param var The variable to evaluate.
|
||||
//! @return true if the variable is signed.
|
||||
#define IS_SIGNED(var) (__builtin_choose_expr( \
|
||||
__builtin_types_compatible_p(__typeof__(var), unsigned char), false, \
|
||||
__builtin_choose_expr( \
|
||||
__builtin_types_compatible_p(__typeof__(var), unsigned short), false, \
|
||||
__builtin_choose_expr( \
|
||||
__builtin_types_compatible_p(__typeof__(var), unsigned int), false, \
|
||||
__builtin_choose_expr( \
|
||||
__builtin_types_compatible_p(__typeof__(var), unsigned long), false, \
|
||||
__builtin_choose_expr( \
|
||||
__builtin_types_compatible_p(__typeof__(var), unsigned long long), false, true))))) \
|
||||
)
|
||||
|
||||
/**
|
||||
* Compute the next backoff interval using a bounded binary expoential backoff formula.
|
||||
*
|
||||
* @param[in,out] attempt The number of retries performed so far. This count will be incremented
|
||||
* by the function.
|
||||
* @param[in] initial_value The inital backoff interval. Subsequent backoff attempts will be this
|
||||
* number multiplied by a power of 2.
|
||||
* @param[in] max_value The maximum backoff interval that returned by the function.
|
||||
* @return The next backoff interval.
|
||||
*/
|
||||
uint32_t next_exponential_backoff(uint32_t *attempt, uint32_t initial_value, uint32_t max_value);
|
||||
|
||||
//! Find the greatest common divisor of two numbers.
|
||||
uint32_t gcd(uint32_t a, uint32_t b);
|
46
platform/robert/boot/src/util/misc.c
Normal file
46
platform/robert/boot/src/util/misc.c
Normal file
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "misc.h"
|
||||
|
||||
#include "drivers/dbgserial.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
void itoa_hex(uint32_t num, char *buffer, int buffer_length) {
|
||||
if (buffer_length < 11) {
|
||||
dbgserial_putstr("itoa buffer too small");
|
||||
return;
|
||||
}
|
||||
*buffer++ = '0';
|
||||
*buffer++ = 'x';
|
||||
|
||||
for (int i = 7; i >= 0; --i) {
|
||||
uint32_t digit = (num & (0xf << (i * 4))) >> (i * 4);
|
||||
|
||||
char c;
|
||||
if (digit < 0xa) {
|
||||
c = '0' + digit;
|
||||
} else if (digit < 0x10) {
|
||||
c = 'a' + (digit - 0xa);
|
||||
} else {
|
||||
c = ' ';
|
||||
}
|
||||
|
||||
*buffer++ = c;
|
||||
}
|
||||
*buffer = '\0';
|
||||
}
|
31
platform/robert/boot/src/util/misc.h
Normal file
31
platform/robert/boot/src/util/misc.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
|
||||
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
|
||||
|
||||
#define MHZ_TO_HZ(hz) (((uint32_t)(hz)) * 1000000)
|
||||
|
||||
#define ARRAY_LENGTH(array) (sizeof((array))/sizeof((array)[0]))
|
||||
|
||||
//! Convert num to a hex string and put in buffer
|
||||
void itoa_hex(uint32_t num, char *buffer, int buffer_length);
|
80
platform/robert/boot/src/util/net.h
Normal file
80
platform/robert/boot/src/util/net.h
Normal file
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// When compiling test, the host OS might have conflicting defines for this:
|
||||
#undef ntohs
|
||||
#undef htons
|
||||
#undef ntohl
|
||||
#undef htonl
|
||||
#undef ltohs
|
||||
#undef ltohl
|
||||
|
||||
static inline uint16_t ntohs(uint16_t v) {
|
||||
// return ((v & 0x00ff) << 8) | ((v & 0xff00) >> 8);
|
||||
return __builtin_bswap16(v);
|
||||
}
|
||||
|
||||
static inline uint16_t htons(uint16_t v) {
|
||||
return ntohs(v);
|
||||
}
|
||||
|
||||
static inline uint32_t ntohl(uint32_t v) {
|
||||
// return ((v & 0x000000ff) << 24) |
|
||||
// ((v & 0x0000ff00) << 8) |
|
||||
// ((v & 0x00ff0000) >> 8) |
|
||||
// ((v & 0xff000000) >> 24);
|
||||
return __builtin_bswap32(v);
|
||||
}
|
||||
|
||||
static inline uint32_t htonl(uint32_t v) {
|
||||
return ntohl(v);
|
||||
}
|
||||
|
||||
#define ltohs(v) (v)
|
||||
#define ltohl(v) (v)
|
||||
|
||||
// Types for values in network byte-order. They are wrapped in structs so that
|
||||
// the compiler will disallow implicit casting of these types to or from
|
||||
// integral types. This way it is a compile error to try using variables of
|
||||
// these types without first performing a byte-order conversion.
|
||||
// There is no overhead for wrapping the values in structs.
|
||||
typedef struct net16 {
|
||||
uint16_t v;
|
||||
} net16;
|
||||
|
||||
typedef struct net32 {
|
||||
uint32_t v;
|
||||
} net32;
|
||||
|
||||
static inline uint16_t ntoh16(net16 net) {
|
||||
return ntohs(net.v);
|
||||
}
|
||||
|
||||
static inline net16 hton16(uint16_t v) {
|
||||
return (net16){ htons(v) };
|
||||
}
|
||||
|
||||
static inline uint32_t ntoh32(net32 net) {
|
||||
return ntohl(net.v);
|
||||
}
|
||||
|
||||
static inline net32 hton32(uint32_t v) {
|
||||
return (net32){ htonl(v) };
|
||||
}
|
31
platform/robert/boot/src/util/size.h
Normal file
31
platform/robert/boot/src/util/size.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
//! Calculate the length of an array, based on the size of the element type.
|
||||
//! @param array The array to be evaluated.
|
||||
//! @return The length of the array.
|
||||
#define ARRAY_LENGTH(array) (sizeof((array))/sizeof((array)[0]))
|
||||
|
||||
//! Calculate the length of a literal array based on the size of the given type
|
||||
//! This is usable in contexts that require compile time constants
|
||||
//! @param type Type of the elements
|
||||
//! @param array Literal definition of the array
|
||||
//! @return Length of the array in bytes
|
||||
#define STATIC_ARRAY_LENGTH(type, array) (sizeof((type[]) array) / sizeof(type))
|
||||
|
||||
#define MEMBER_SIZE(type, member) sizeof(((type *)0)->member)
|
88
platform/robert/boot/src/util/sle.c
Normal file
88
platform/robert/boot/src/util/sle.c
Normal file
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "sle.h"
|
||||
|
||||
#include "system/passert.h"
|
||||
|
||||
// See waftools/sparse_length_encoding.py for more info on SLE encoding/decoding
|
||||
|
||||
typedef struct {
|
||||
const uint8_t *data;
|
||||
uint32_t index;
|
||||
uint32_t length;
|
||||
} ReadByteStream;
|
||||
|
||||
typedef struct {
|
||||
uint8_t *data;
|
||||
uint32_t index;
|
||||
uint32_t length;
|
||||
} WriteByteStream;
|
||||
|
||||
static uint8_t prv_byte_stream_read(ReadByteStream *stream) {
|
||||
PBL_ASSERTN(stream->index < stream->length);
|
||||
return stream->data[stream->index++];
|
||||
}
|
||||
|
||||
static void prv_byte_stream_write(WriteByteStream *stream, uint8_t data) {
|
||||
PBL_ASSERTN(stream->index < stream->length);
|
||||
stream->data[stream->index++] = data;
|
||||
}
|
||||
|
||||
uint32_t sle_decode(const uint8_t *in, uint32_t in_len, uint8_t *out, uint32_t out_len) {
|
||||
ReadByteStream in_stream = {
|
||||
.data = in,
|
||||
.length = in_len
|
||||
};
|
||||
WriteByteStream out_stream = {
|
||||
.data = out,
|
||||
.length = out_len
|
||||
};
|
||||
|
||||
const uint8_t escape = prv_byte_stream_read(&in_stream);
|
||||
while (true) {
|
||||
const uint8_t byte = prv_byte_stream_read(&in_stream);
|
||||
if (byte != escape) {
|
||||
// simply write the byte into the output stream
|
||||
prv_byte_stream_write(&out_stream, byte);
|
||||
continue;
|
||||
}
|
||||
|
||||
// read the escape code
|
||||
const uint8_t code = prv_byte_stream_read(&in_stream);
|
||||
if (code == 0) {
|
||||
// end of stream
|
||||
break;
|
||||
} else if (code == 1) {
|
||||
// literal escape byte
|
||||
prv_byte_stream_write(&out_stream, escape);
|
||||
} else {
|
||||
// a sequence of zeros
|
||||
uint16_t count;
|
||||
if ((code & 0x80) == 0) {
|
||||
// the count is only 1 byte (1-127)
|
||||
count = code;
|
||||
} else {
|
||||
// the count is 2 bytes
|
||||
count = (((uint16_t)(code & 0x7f) << 8) | prv_byte_stream_read(&in_stream)) + 0x80;
|
||||
}
|
||||
for (int i = 0; i < count; ++i) {
|
||||
prv_byte_stream_write(&out_stream, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
return out_stream.index;
|
||||
}
|
21
platform/robert/boot/src/util/sle.h
Normal file
21
platform/robert/boot/src/util/sle.h
Normal file
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
uint32_t sle_decode(const uint8_t *in, uint32_t in_len, uint8_t *out, uint32_t out_len);
|
96
platform/robert/boot/src/vector_table.c
Normal file
96
platform/robert/boot/src/vector_table.c
Normal file
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
extern int main(void);
|
||||
|
||||
//! These symbols are defined in the linker script for use in initializing
|
||||
//! the data sections. uint8_t since we do arithmetic with section lengths.
|
||||
//! These are arrays to avoid the need for an & when dealing with linker symbols.
|
||||
extern uint8_t __data_load_start[];
|
||||
extern uint8_t __data_start[];
|
||||
extern uint8_t __data_end[];
|
||||
extern uint8_t __bss_start[];
|
||||
extern uint8_t __bss_end[];
|
||||
extern uint8_t _estack[];
|
||||
|
||||
__attribute__((__noreturn__)) void Reset_Handler(void) {
|
||||
memcpy(__data_start, __data_load_start, __data_end - __data_start);
|
||||
|
||||
// Clear the bss section, assumes .bss goes directly after .data
|
||||
memset(__bss_start, 0, __bss_end - __bss_start);
|
||||
|
||||
main();
|
||||
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
__attribute__((__noreturn__)) void Default_Handler(void) {
|
||||
// This handler is only called if we haven't defined a specific
|
||||
// handler for the interrupt. This means the interrupt is unexpected,
|
||||
// so we loop infinitely to preserve the system state for examination
|
||||
// by a debugger
|
||||
while (true) {}
|
||||
}
|
||||
|
||||
|
||||
// All these functions are weak references to the Default_Handler,
|
||||
// so if we define a handler in elsewhere in the firmware, these
|
||||
// will be overriden
|
||||
#define ALIAS(sym) __attribute__((__weak__, __alias__(sym)))
|
||||
ALIAS("Default_Handler") void NMI_Handler(void);
|
||||
ALIAS("Default_Handler") void HardFault_Handler(void);
|
||||
ALIAS("Default_Handler") void MemManage_Handler(void);
|
||||
ALIAS("Default_Handler") void BusFault_Handler(void);
|
||||
ALIAS("Default_Handler") void UsageFault_Handler(void);
|
||||
ALIAS("Default_Handler") void SVC_Handler(void);
|
||||
ALIAS("Default_Handler") void DebugMon_Handler(void);
|
||||
ALIAS("Default_Handler") void PendSV_Handler(void);
|
||||
ALIAS("Default_Handler") void SysTick_Handler(void);
|
||||
|
||||
// External Interrupts
|
||||
#define IRQ_DEF(idx, irq) ALIAS("Default_Handler") void irq##_IRQHandler(void);
|
||||
#include "irq_stm32f7.def"
|
||||
#undef IRQ_DEF
|
||||
|
||||
|
||||
__attribute__((__section__(".isr_vector"))) const void * const vector_table[] = {
|
||||
_estack,
|
||||
Reset_Handler,
|
||||
NMI_Handler,
|
||||
HardFault_Handler,
|
||||
MemManage_Handler,
|
||||
BusFault_Handler,
|
||||
UsageFault_Handler,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
SVC_Handler,
|
||||
DebugMon_Handler,
|
||||
0,
|
||||
PendSV_Handler,
|
||||
SysTick_Handler,
|
||||
|
||||
// External Interrupts
|
||||
#define IRQ_DEF(idx, irq) [idx + 16] = irq##_IRQHandler,
|
||||
#include "irq_stm32f7.def"
|
||||
#undef IRQ_DEF
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue