mirror of
https://github.com/google/pebble.git
synced 2025-05-28 05:53: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
61
platform/tintin/boot/src/board/board.h
Normal file
61
platform/tintin/boot/src/board/board.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 "display.h"
|
||||
|
||||
#include "drivers/button_id.h"
|
||||
|
||||
#include "stm32f2xx_gpio.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define GPIO_Port_NULL ((GPIO_TypeDef *) 0)
|
||||
#define GPIO_Pin_NULL ((uint16_t)0x0000)
|
||||
|
||||
typedef struct {
|
||||
const char* const name; ///< Name for debugging purposes.
|
||||
GPIO_TypeDef* const gpio; ///< One of GPIOX. For example, GPIOA.
|
||||
const uint32_t gpio_pin; ///< One of GPIO_Pin_X.
|
||||
} ButtonConfig;
|
||||
|
||||
typedef struct {
|
||||
GPIO_TypeDef* const gpio; ///< One of GPIOX. For example, GPIOA.
|
||||
const uint32_t gpio_pin; ///< One of GPIO_Pin_X.
|
||||
} ButtonComConfig;
|
||||
|
||||
typedef struct {
|
||||
GPIO_TypeDef* const gpio; ///< One of GPIOX. For example, GPIOA.
|
||||
const uint32_t gpio_pin; ///< One of GPIO_Pin_X.
|
||||
} InputConfig;
|
||||
|
||||
// Power Configuration
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
typedef struct {
|
||||
const InputConfig vusb_stat;
|
||||
const bool wake_on_usb_power;
|
||||
} BoardConfigPower;
|
||||
|
||||
// Button Configuration
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
typedef struct {
|
||||
const ButtonConfig buttons[NUM_BUTTONS];
|
||||
const ButtonComConfig button_com;
|
||||
} BoardConfigButton;
|
||||
|
||||
#include "board_definitions.h"
|
43
platform/tintin/boot/src/board/board_bb2.h
Normal file
43
platform/tintin/boot/src/board/board_bb2.h
Normal file
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* 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/button.h"
|
||||
#include "util/misc.h"
|
||||
|
||||
#include "stm32f2xx_rcc.h"
|
||||
#include "stm32f2xx_gpio.h"
|
||||
#include "stm32f2xx_syscfg.h"
|
||||
|
||||
static const BoardConfigPower BOARD_CONFIG_POWER = {
|
||||
.vusb_stat = {
|
||||
.gpio = GPIOC,
|
||||
.gpio_pin = GPIO_Pin_13,
|
||||
},
|
||||
.wake_on_usb_power = true
|
||||
};
|
||||
|
||||
static const BoardConfigButton BOARD_CONFIG_BUTTON = {
|
||||
.buttons = {
|
||||
[BUTTON_ID_BACK] = { "Back", GPIOC, GPIO_Pin_3 },
|
||||
[BUTTON_ID_UP] = { "Up", GPIOA, GPIO_Pin_2 },
|
||||
[BUTTON_ID_SELECT] = { "Select", GPIOC, GPIO_Pin_6 },
|
||||
[BUTTON_ID_DOWN] = { "Down", GPIOA, GPIO_Pin_1 },
|
||||
},
|
||||
|
||||
.button_com = { GPIOA, GPIO_Pin_0 },
|
||||
};
|
30
platform/tintin/boot/src/board/board_definitions.h
Normal file
30
platform/tintin/boot/src/board/board_definitions.h
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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// FIXME: PBL-21049 Fix platform abstraction and board definition scheme
|
||||
#if BOARD_EV2_4
|
||||
#include "board_ev2_4.h" // shipped as Pebble 1.0
|
||||
#elif BOARD_BB2
|
||||
#include "board_bb2.h"
|
||||
#elif BOARD_V1_5
|
||||
#include "board_v1_5.h" // prototypes for Pebble 1.3/Pebble 1.5
|
||||
#elif BOARD_V2_0
|
||||
#include "board_v2_0.h" // prototypes for Pebble 2.0
|
||||
#else
|
||||
#error "Unknown board definition"
|
||||
#endif
|
43
platform/tintin/boot/src/board/board_ev2_4.h
Normal file
43
platform/tintin/boot/src/board/board_ev2_4.h
Normal file
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* 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/button.h"
|
||||
#include "util/misc.h"
|
||||
|
||||
#include "stm32f2xx_rcc.h"
|
||||
#include "stm32f2xx_gpio.h"
|
||||
#include "stm32f2xx_syscfg.h"
|
||||
|
||||
static const BoardConfigPower BOARD_CONFIG_POWER = {
|
||||
.vusb_stat = {
|
||||
.gpio = GPIOC,
|
||||
.gpio_pin = GPIO_Pin_12,
|
||||
},
|
||||
.wake_on_usb_power = false
|
||||
};
|
||||
|
||||
static const BoardConfigButton BOARD_CONFIG_BUTTON = {
|
||||
.buttons = {
|
||||
[BUTTON_ID_BACK] = { "Back", GPIOC, GPIO_Pin_3 },
|
||||
[BUTTON_ID_UP] = { "Up", GPIOA, GPIO_Pin_2 },
|
||||
[BUTTON_ID_SELECT] = { "Select", GPIOC, GPIO_Pin_6 },
|
||||
[BUTTON_ID_DOWN] = { "Down", GPIOA, GPIO_Pin_1 },
|
||||
},
|
||||
|
||||
.button_com = { GPIOA, GPIO_Pin_0 }
|
||||
};
|
43
platform/tintin/boot/src/board/board_v1_5.h
Normal file
43
platform/tintin/boot/src/board/board_v1_5.h
Normal file
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* 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/button.h"
|
||||
#include "util/misc.h"
|
||||
|
||||
#include "stm32f2xx_rcc.h"
|
||||
#include "stm32f2xx_gpio.h"
|
||||
#include "stm32f2xx_syscfg.h"
|
||||
|
||||
static const BoardConfigPower BOARD_CONFIG_POWER = {
|
||||
.vusb_stat = {
|
||||
.gpio = GPIOC,
|
||||
.gpio_pin = GPIO_Pin_13,
|
||||
},
|
||||
.wake_on_usb_power = true
|
||||
};
|
||||
|
||||
static const BoardConfigButton BOARD_CONFIG_BUTTON = {
|
||||
.buttons = {
|
||||
[BUTTON_ID_BACK] = { "Back", GPIOC, GPIO_Pin_3 },
|
||||
[BUTTON_ID_UP] = { "Up", GPIOA, GPIO_Pin_2 },
|
||||
[BUTTON_ID_SELECT] = { "Select", GPIOC, GPIO_Pin_6 },
|
||||
[BUTTON_ID_DOWN] = { "Down", GPIOA, GPIO_Pin_1 },
|
||||
},
|
||||
|
||||
.button_com = { GPIOA, GPIO_Pin_0 },
|
||||
};
|
43
platform/tintin/boot/src/board/board_v2_0.h
Normal file
43
platform/tintin/boot/src/board/board_v2_0.h
Normal file
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* 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/button.h"
|
||||
#include "util/misc.h"
|
||||
|
||||
#include "stm32f2xx_rcc.h"
|
||||
#include "stm32f2xx_gpio.h"
|
||||
#include "stm32f2xx_syscfg.h"
|
||||
|
||||
static const BoardConfigPower BOARD_CONFIG_POWER = {
|
||||
.vusb_stat = {
|
||||
.gpio = GPIOC,
|
||||
.gpio_pin = GPIO_Pin_13,
|
||||
},
|
||||
.wake_on_usb_power = true
|
||||
};
|
||||
|
||||
static const BoardConfigButton BOARD_CONFIG_BUTTON = {
|
||||
.buttons = {
|
||||
[BUTTON_ID_BACK] = { "Back", GPIOC, GPIO_Pin_3 },
|
||||
[BUTTON_ID_UP] = { "Up", GPIOA, GPIO_Pin_2 },
|
||||
[BUTTON_ID_SELECT] = { "Select", GPIOC, GPIO_Pin_6 },
|
||||
[BUTTON_ID_DOWN] = { "Down", GPIOA, GPIO_Pin_1 },
|
||||
},
|
||||
|
||||
.button_com = { GPIOA, GPIO_Pin_0 },
|
||||
};
|
21
platform/tintin/boot/src/board/display.h
Normal file
21
platform/tintin/boot/src/board/display.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
|
||||
|
||||
#define DISP_COLS 144
|
||||
#define DISP_ROWS 168
|
||||
#define PBL_DISP_SHAPE_RECT
|
80
platform/tintin/boot/src/boot_tests.c
Normal file
80
platform/tintin/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 "stm32f2xx.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
static const int STUCK_BUTTON_THRESHOLD = 5;
|
||||
|
||||
bool 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 is_flash_broken(void) {
|
||||
return !flash_sanity_check();
|
||||
}
|
22
platform/tintin/boot/src/boot_tests.h
Normal file
22
platform/tintin/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 is_button_stuck(void);
|
||||
bool is_flash_broken(void);
|
27
platform/tintin/boot/src/drivers/button.h
Normal file
27
platform/tintin/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/tintin/boot/src/drivers/button_id.h
Normal file
41
platform/tintin/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
|
31
platform/tintin/boot/src/drivers/crc.h
Normal file
31
platform/tintin/boot/src/drivers/crc.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 <stdint.h>
|
||||
|
||||
/*
|
||||
* calculate the CRC32 for a stream of bytes.
|
||||
* NOTE: not safe to call from ISR
|
||||
*/
|
||||
uint32_t crc_calculate_bytes(const uint8_t* data, unsigned int data_length);
|
||||
|
||||
/*
|
||||
* calculate the CRC32 for a stream of bytes from flash
|
||||
* NOTE: not safe to call from ISR
|
||||
*/
|
||||
uint32_t crc_calculate_flash(uint32_t address, unsigned int num_bytes);
|
34
platform/tintin/boot/src/drivers/dbgserial.h
Normal file
34
platform/tintin/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 <stdbool.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/tintin/boot/src/drivers/display.h
Normal file
34
platform/tintin/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);
|
|
@ -0,0 +1,47 @@
|
|||
#define dead_face_width 92
|
||||
#define dead_face_height 44
|
||||
static const unsigned char dead_face_bits[] = {
|
||||
0x00, 0x0c, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x30, 0x00,
|
||||
0x00, 0x18, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x18, 0x00,
|
||||
0x00, 0x30, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x0c, 0x00,
|
||||
0x00, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x06, 0x00,
|
||||
0x00, 0xc0, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x03, 0x00,
|
||||
0x00, 0x80, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8c, 0x01, 0x00,
|
||||
0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x00, 0x00,
|
||||
0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00,
|
||||
0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00,
|
||||
0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x00, 0x00,
|
||||
0x00, 0x80, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8c, 0x01, 0x00,
|
||||
0x00, 0xc0, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x03, 0x00,
|
||||
0x00, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x06, 0x00,
|
||||
0x00, 0x30, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x0c, 0x00,
|
||||
0x00, 0x18, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x18, 0x00,
|
||||
0x00, 0x0c, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x30, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x80, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0xe0, 0x7f, 0x00, 0x00, 0xfe, 0x07, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0xfe, 0x01, 0x00, 0x00, 0x80, 0x7f, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x80, 0x1f, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x01, 0x00, 0x00,
|
||||
0x00, 0x00, 0xf0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x80, 0x0f, 0x00, 0x00,
|
||||
0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00,
|
||||
0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00,
|
||||
0x00, 0x80, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x01, 0x00,
|
||||
0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00,
|
||||
0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00,
|
||||
0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00,
|
||||
0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00,
|
||||
0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00,
|
||||
0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00,
|
||||
0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01,
|
||||
0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
|
||||
0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06,
|
||||
0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c,
|
||||
0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08 };
|
|
@ -0,0 +1,11 @@
|
|||
#define empty_bar_width 96
|
||||
#define empty_bar_height 8
|
||||
static const unsigned char empty_bar_bits[] = {
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
|
||||
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
|
||||
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
|
||||
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
|
||||
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
|
||||
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
|
|
@ -0,0 +1,20 @@
|
|||
#define error_url_width 109
|
||||
#define error_url_height 14
|
||||
static const unsigned char error_url_bits[] = {
|
||||
0x00, 0x00, 0x06, 0x83, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x06, 0x83, 0x01, 0x00, 0x00, 0x00, 0x00, 0x0c,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x83, 0x01, 0x00, 0x00, 0x00,
|
||||
0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x78, 0x36, 0x9b, 0x79, 0xc0,
|
||||
0xe3, 0xd9, 0x0c, 0x8c, 0x67, 0xdb, 0x3c, 0x1b, 0xf8, 0xfd, 0x7e, 0xbf,
|
||||
0xfd, 0xe0, 0xf7, 0xfb, 0x1f, 0xc6, 0xef, 0xfb, 0x7e, 0x1f, 0x98, 0xcd,
|
||||
0x66, 0xb3, 0xcd, 0x60, 0x36, 0x9b, 0x19, 0xc6, 0x6c, 0x18, 0x66, 0x03,
|
||||
0x98, 0xfd, 0x66, 0xb3, 0xfd, 0x60, 0x30, 0x9b, 0x19, 0xc6, 0x6f, 0x18,
|
||||
0x66, 0x03, 0x98, 0xfd, 0x66, 0xb3, 0xfd, 0x60, 0x30, 0x9b, 0x19, 0xc3,
|
||||
0x6f, 0x18, 0x66, 0x03, 0x98, 0x0d, 0x66, 0xb3, 0x0d, 0x60, 0x36, 0x9b,
|
||||
0x19, 0xc3, 0x60, 0x18, 0x66, 0x03, 0xf8, 0xfd, 0x7e, 0xbf, 0xfd, 0xec,
|
||||
0xf7, 0x9b, 0x19, 0xc3, 0x6f, 0x18, 0x7e, 0x03, 0xd8, 0x78, 0x36, 0x9b,
|
||||
0x79, 0xcc, 0xe3, 0x99, 0x99, 0x81, 0x67, 0x18, 0x3c, 0x03, 0x18, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00,
|
||||
0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00 };
|
103
platform/tintin/boot/src/drivers/display/resources/hex_digits.h
Normal file
103
platform/tintin/boot/src/drivers/display/resources/hex_digits.h
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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// hex_digits_bits is indexed on the digit:
|
||||
// ie: hex_digits_bits[0] is the bits to display 0 on
|
||||
// the screen stored in xbm format
|
||||
static const uint8_t hex_digits_bits[][36] = {
|
||||
{
|
||||
0x7C, 0x00, 0xFE, 0x00, 0xFF, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xC7, 0x01,
|
||||
0xC7, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xC7, 0x01,
|
||||
0xC7, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xFF, 0x01, 0xFE, 0x00, 0x7C, 0x00,
|
||||
},
|
||||
{
|
||||
0x38, 0x00, 0x3C, 0x00, 0x3E, 0x00, 0x3E, 0x00, 0x38, 0x00, 0x38, 0x00,
|
||||
0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00,
|
||||
0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0xFE, 0x00, 0xFE, 0x00, 0xFE, 0x00,
|
||||
},
|
||||
{
|
||||
0x7C, 0x00, 0xFE, 0x00, 0xFF, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xC0, 0x01,
|
||||
0xC0, 0x01, 0xE0, 0x00, 0xF0, 0x00, 0x78, 0x00, 0x3C, 0x00, 0x1E, 0x00,
|
||||
0x0E, 0x00, 0x0F, 0x00, 0x07, 0x00, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01,
|
||||
},
|
||||
{
|
||||
0x7C, 0x00, 0xFE, 0x00, 0xFF, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xC0, 0x01,
|
||||
0xC0, 0x01, 0xF8, 0x00, 0x78, 0x00, 0xF8, 0x00, 0xC0, 0x01, 0xC0, 0x01,
|
||||
0xC0, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xFF, 0x01, 0xFE, 0x00, 0x7C, 0x00,
|
||||
},
|
||||
{
|
||||
0xE0, 0x00, 0xE0, 0x00, 0xF0, 0x00, 0xF0, 0x00, 0xF8, 0x00, 0xF8, 0x00,
|
||||
0xF8, 0x00, 0xFC, 0x00, 0xEC, 0x00, 0xEE, 0x00, 0xE6, 0x00, 0xFF, 0x01,
|
||||
0xFF, 0x01, 0xFF, 0x01, 0xE0, 0x00, 0xE0, 0x00, 0xE0, 0x00, 0xE0, 0x00,
|
||||
},
|
||||
{
|
||||
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x07, 0x00, 0x07, 0x00, 0x07, 0x00,
|
||||
0x7F, 0x00, 0xFF, 0x00, 0xFF, 0x01, 0xC7, 0x01, 0xC0, 0x01, 0xC0, 0x01,
|
||||
0xC7, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xFF, 0x01, 0xFE, 0x00, 0x7C, 0x00,
|
||||
},
|
||||
{
|
||||
0x7C, 0x00, 0xFE, 0x00, 0xFF, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0x07, 0x00,
|
||||
0x07, 0x00, 0x77, 0x00, 0xFF, 0x00, 0xFF, 0x01, 0xC7, 0x01, 0xC7, 0x01,
|
||||
0xC7, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xFF, 0x01, 0xFE, 0x00, 0x7C, 0x00,
|
||||
},
|
||||
{
|
||||
0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xE0, 0x00, 0xE0, 0x00, 0x70, 0x00,
|
||||
0x70, 0x00, 0x70, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00,
|
||||
0x1C, 0x00, 0x1C, 0x00, 0x1C, 0x00, 0x1C, 0x00, 0x1C, 0x00, 0x1C, 0x00,
|
||||
},
|
||||
{
|
||||
0x7C, 0x00, 0xFE, 0x00, 0xFF, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xC7, 0x01,
|
||||
0xC7, 0x01, 0xFE, 0x00, 0x7C, 0x00, 0xFE, 0x00, 0xC7, 0x01, 0xC7, 0x01,
|
||||
0xC7, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xFF, 0x01, 0xFE, 0x00, 0x7C, 0x00,
|
||||
},
|
||||
{
|
||||
0x7C, 0x00, 0xFE, 0x00, 0xFF, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xC7, 0x01,
|
||||
0xC7, 0x01, 0xC7, 0x01, 0xFF, 0x01, 0xFE, 0x01, 0xDC, 0x01, 0xC0, 0x01,
|
||||
0xC0, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xFF, 0x01, 0xFE, 0x00, 0x7C, 0x00,
|
||||
},
|
||||
{
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x00, 0xFE, 0x00,
|
||||
0xFF, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xF0, 0x01, 0xFC, 0x01, 0xCE, 0x01,
|
||||
0xC7, 0x01, 0xC7, 0x01, 0xE7, 0x01, 0xFF, 0x01, 0xDF, 0x01, 0xCE, 0x01,
|
||||
},
|
||||
{
|
||||
0x07, 0x00, 0x07, 0x00, 0x07, 0x00, 0x07, 0x00, 0xE7, 0x00, 0xF7, 0x01,
|
||||
0xFF, 0x01, 0xCF, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xC7, 0x01,
|
||||
0xC7, 0x01, 0xC7, 0x01, 0xCF, 0x01, 0xFF, 0x01, 0xF7, 0x01, 0xE7, 0x00,
|
||||
},
|
||||
{
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x00, 0xFE, 0x00,
|
||||
0xFF, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0x07, 0x00, 0x07, 0x00, 0x07, 0x00,
|
||||
0x07, 0x00, 0xC7, 0x01, 0xC7, 0x01, 0xFF, 0x01, 0xFE, 0x00, 0x7C, 0x00,
|
||||
},
|
||||
{
|
||||
0xC0, 0x01, 0xC0, 0x01, 0xC0, 0x01, 0xC0, 0x01, 0xCE, 0x01, 0xDF, 0x01,
|
||||
0xFF, 0x01, 0xE7, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xC7, 0x01,
|
||||
0xC7, 0x01, 0xC7, 0x01, 0xE7, 0x01, 0xFF, 0x01, 0xDF, 0x01, 0xCE, 0x01,
|
||||
},
|
||||
{
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x00, 0xFE, 0x00,
|
||||
0xFF, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xC7, 0x01, 0xFF, 0x01, 0xFF, 0x01,
|
||||
0x07, 0x00, 0xC7, 0x01, 0xC7, 0x01, 0xFF, 0x01, 0xFE, 0x00, 0x7C, 0x00,
|
||||
},
|
||||
{
|
||||
0xE0, 0x00, 0xF0, 0x00, 0xF8, 0x00, 0x38, 0x00, 0xFE, 0x00, 0xFE, 0x00,
|
||||
0xFE, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00,
|
||||
0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00,
|
||||
}
|
||||
};
|
|
@ -0,0 +1,35 @@
|
|||
#define pebble_logo_width 105
|
||||
#define pebble_logo_height 27
|
||||
static const unsigned char pebble_logo_bits[] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x03, 0x00, 0x18, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x03, 0x00,
|
||||
0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00,
|
||||
0x03, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60,
|
||||
0x00, 0x00, 0x03, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x60, 0x00, 0x00, 0x03, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0xE0,
|
||||
0x03, 0x00, 0x1F, 0x60, 0xF8, 0x00, 0xC3, 0x07, 0x18, 0xC0, 0x07, 0x00,
|
||||
0x00, 0xF8, 0x0F, 0xC0, 0x7F, 0x60, 0xFE, 0x03, 0xF3, 0x1F, 0x18, 0xF0,
|
||||
0x1F, 0x00, 0x00, 0x0C, 0x18, 0x60, 0xC0, 0x60, 0x03, 0x06, 0x1B, 0x30,
|
||||
0x18, 0x18, 0x30, 0x00, 0x00, 0x06, 0x30, 0x30, 0x80, 0xE1, 0x01, 0x0C,
|
||||
0x0F, 0x60, 0x18, 0x0C, 0x60, 0x00, 0x00, 0x03, 0x60, 0x18, 0x00, 0xE3,
|
||||
0x00, 0x18, 0x07, 0xC0, 0x18, 0x06, 0xC0, 0x00, 0x80, 0x01, 0x40, 0x0C,
|
||||
0x00, 0x62, 0x00, 0x10, 0x03, 0x80, 0x18, 0x03, 0x80, 0x00, 0x80, 0x01,
|
||||
0xC0, 0x0C, 0x00, 0x66, 0x00, 0x30, 0x03, 0x80, 0x19, 0x03, 0x80, 0x01,
|
||||
0x80, 0x01, 0xC0, 0xFC, 0xFF, 0x67, 0x00, 0x30, 0x03, 0x80, 0x19, 0xFF,
|
||||
0xFF, 0x01, 0x80, 0x01, 0xC0, 0xFC, 0xFF, 0x63, 0x00, 0x30, 0x03, 0x80,
|
||||
0x19, 0xFF, 0xFF, 0x00, 0x80, 0x01, 0xC0, 0x0C, 0x00, 0x60, 0x00, 0x30,
|
||||
0x03, 0x80, 0x19, 0x03, 0x00, 0x00, 0x80, 0x01, 0xC0, 0x0C, 0x00, 0x60,
|
||||
0x00, 0x30, 0x03, 0x80, 0x19, 0x03, 0x00, 0x00, 0x80, 0x01, 0x40, 0x0C,
|
||||
0x00, 0x62, 0x00, 0x10, 0x03, 0x80, 0x18, 0x03, 0x80, 0x00, 0x80, 0x03,
|
||||
0x60, 0x18, 0x00, 0xC3, 0x00, 0x18, 0x06, 0xC0, 0x18, 0x06, 0xC0, 0x00,
|
||||
0x80, 0x07, 0x30, 0x30, 0x80, 0x81, 0x01, 0x0C, 0x0C, 0x60, 0x18, 0x0C,
|
||||
0x60, 0x00, 0x80, 0x0D, 0x18, 0x60, 0xC0, 0x00, 0x03, 0x06, 0x18, 0x30,
|
||||
0x38, 0x18, 0x30, 0x00, 0x80, 0xF9, 0x0F, 0xC0, 0x7F, 0x00, 0xFE, 0x03,
|
||||
0xF0, 0x1F, 0x70, 0xF0, 0x1F, 0x00, 0x80, 0xE1, 0x03, 0x00, 0x1F, 0x00,
|
||||
0xF8, 0x00, 0xC0, 0x07, 0x60, 0xC0, 0x07, 0x00, 0x80, 0x01, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, };
|
425
platform/tintin/boot/src/drivers/display/sharp_ls013b7dh01.c
Normal file
425
platform/tintin/boot/src/drivers/display/sharp_ls013b7dh01.c
Normal file
|
@ -0,0 +1,425 @@
|
|||
/*
|
||||
* 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/display.h"
|
||||
#include "drivers/periph_config.h"
|
||||
#include "drivers/gpio.h"
|
||||
#include "drivers/dbgserial.h"
|
||||
#include "util/attributes.h"
|
||||
#include "util/delay.h"
|
||||
|
||||
#include "stm32f2xx_dma.h"
|
||||
#include "stm32f2xx_gpio.h"
|
||||
#include "stm32f2xx_rcc.h"
|
||||
#include "stm32f2xx_spi.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// Bootloader images
|
||||
#include "drivers/display/resources/hex_digits.h"
|
||||
#include "drivers/display/resources/dead_face.xbm"
|
||||
#include "drivers/display/resources/empty_bar.xbm"
|
||||
#include "drivers/display/resources/error_url.xbm"
|
||||
#include "drivers/display/resources/pebble_logo.xbm"
|
||||
|
||||
#define DISP_LINE_BYTES (DISP_COLS / 8)
|
||||
#define DISP_LINE_WORDS (((DISP_COLS - 1) / 32) + 1)
|
||||
|
||||
// GPIO constants
|
||||
#define DISP_SPI (SPI2)
|
||||
#define DISP_GPIO (GPIOB)
|
||||
#define PWR_CTL_GPIO (GPIOC)
|
||||
#define PWR_CTL_PIN (GPIO_Pin_5)
|
||||
#define DISPLAY_SPI_CLOCK (RCC_APB1Periph_SPI2)
|
||||
#define DISP_PIN_VCOM (GPIO_Pin_1)
|
||||
#define DISP_PINSOURCE_VCOM (GPIO_PinSource1)
|
||||
#define DISP_PIN_SCS (GPIO_Pin_12)
|
||||
#define DISP_PIN_SCLK (GPIO_Pin_13)
|
||||
#define DISP_PIN_LCD (GPIO_Pin_14)
|
||||
#define DISP_PIN_SI (GPIO_Pin_15)
|
||||
#define DISP_MODE_STATIC (0x00)
|
||||
#define DISP_MODE_WRITE (0x80)
|
||||
#define DISP_MODE_CLEAR (0x20)
|
||||
|
||||
// The bootloader leaves SYSCLK at defaults (connected to HSI at 16 Mhz),
|
||||
// and there are no prescalers on any of the buses. Since the display
|
||||
// can handle a max of 2 Mhz, we want to divide by 8
|
||||
#define DISPLAY_PERIPH_PRESCALER (SPI_BaudRatePrescaler_8)
|
||||
|
||||
static void prv_enable_display_spi_clock() {
|
||||
periph_config_enable(RCC_APB1PeriphClockCmd, DISPLAY_SPI_CLOCK);
|
||||
}
|
||||
|
||||
static void prv_disable_display_spi_clock() {
|
||||
periph_config_disable(RCC_APB1PeriphClockCmd, DISPLAY_SPI_CLOCK);
|
||||
}
|
||||
|
||||
static void prv_enable_chip_select(void) {
|
||||
gpio_use(DISP_GPIO);
|
||||
GPIO_WriteBit(DISP_GPIO, DISP_PIN_SCS, Bit_SET);
|
||||
// required setup time > 3us
|
||||
delay_us(7);
|
||||
gpio_release(DISP_GPIO);
|
||||
}
|
||||
|
||||
static void prv_disable_chip_select(void) {
|
||||
gpio_use(DISP_GPIO);
|
||||
// delay while last byte is emitted by the SPI peripheral
|
||||
delay_us(7);
|
||||
|
||||
GPIO_WriteBit(DISP_GPIO, DISP_PIN_SCS, Bit_RESET);
|
||||
// hold time > 1us
|
||||
// produce a delay 4ms
|
||||
delay_us(4);
|
||||
gpio_release(DISP_GPIO);
|
||||
}
|
||||
|
||||
//! These functions needed to be called around any commands that
|
||||
//! are sent to the display. NOINLINE only for code size savings.
|
||||
static NOINLINE void prv_enable_display_access(void) {
|
||||
prv_enable_display_spi_clock();
|
||||
prv_enable_chip_select();
|
||||
}
|
||||
|
||||
static NOINLINE void prv_disable_display_access(void) {
|
||||
prv_disable_chip_select();
|
||||
prv_disable_display_spi_clock();
|
||||
}
|
||||
|
||||
//! Write a single byte synchronously to the display. This is the only practical
|
||||
//! way to write to the display in the bootloader since we don't have interrupts.
|
||||
static void prv_display_write_byte(uint8_t d) {
|
||||
// Block until the tx buffer is empty
|
||||
SPI_I2S_SendData(DISP_SPI, d);
|
||||
while (!SPI_I2S_GetFlagStatus(DISP_SPI, SPI_I2S_FLAG_TXE)) {}
|
||||
}
|
||||
|
||||
// Since all these values are constant we can save code space
|
||||
// by storing the initialized struct in memory rather than
|
||||
// needing to copy in each value
|
||||
static GPIO_InitTypeDef s_disp_gpio_init = {
|
||||
.GPIO_OType = GPIO_OType_PP,
|
||||
.GPIO_PuPd = GPIO_PuPd_NOPULL,
|
||||
.GPIO_Mode = GPIO_Mode_AF,
|
||||
.GPIO_Speed = GPIO_Speed_50MHz,
|
||||
.GPIO_Pin = DISP_PIN_SCLK | DISP_PIN_SI
|
||||
};
|
||||
|
||||
static SPI_InitTypeDef s_disp_spi_init = {
|
||||
.SPI_Direction = SPI_Direction_1Line_Tx, // Write-only SPI
|
||||
.SPI_Mode = SPI_Mode_Master,
|
||||
.SPI_DataSize = SPI_DataSize_8b,
|
||||
.SPI_CPOL = SPI_CPOL_Low,
|
||||
.SPI_CPHA = SPI_CPHA_1Edge,
|
||||
.SPI_NSS = SPI_NSS_Soft,
|
||||
// We want the SPI clock to run at 2MHz
|
||||
.SPI_BaudRatePrescaler = DISPLAY_PERIPH_PRESCALER,
|
||||
// MSB order allows us to write pixels out without reversing bytes, but command bytes
|
||||
// have to be reversed
|
||||
.SPI_FirstBit = SPI_FirstBit_MSB,
|
||||
.SPI_CRCPolynomial = 7 // default
|
||||
};
|
||||
|
||||
//! Setup TIM3 to pulse VCOM every second to avoid damage to the display
|
||||
void prv_setup_pulse_vcom(void) {
|
||||
periph_config_enable(RCC_APB1PeriphClockCmd, RCC_APB1Periph_TIM3);
|
||||
|
||||
// Standard peripheral functions are too big so toggle the registers manually:
|
||||
// Scale timer to roll over once per second
|
||||
TIM3->CR1 = TIM_CounterMode_Up | TIM_CKD_DIV1 | TIM_CR1_ARPE;
|
||||
|
||||
// Prescaler divides by PSC + 1, which gives us 64000 ticks per second
|
||||
// in the timer. This prescaler is the smallest divisor that still allows the
|
||||
// timer to roll over exactly once per second (since the timer is 16 bits)
|
||||
TIM3->PSC = 249;
|
||||
|
||||
// The timer reloads one cycle after ARR is reached, so ARR of
|
||||
// 63999 means the timer will roll over once per second
|
||||
TIM3->ARR = 63999;
|
||||
|
||||
// Enable the preload register and put us in PWM mode 2
|
||||
TIM3->CCMR2 = TIM_CCMR2_OC4PE | (TIM_OCMode_PWM2 << 8);
|
||||
|
||||
// The timer forces the pin high when the counter is greater than
|
||||
// or equal to this value. Since ARR is 63999, this means the
|
||||
// pin will be high for exactly one tick of the timer (~16 us)
|
||||
TIM3->CCR4 = 63999;
|
||||
|
||||
// Enable channel 4
|
||||
TIM3->CCER = TIM_CCER_CC4E;
|
||||
|
||||
// Hook up the VCOM pin's alternate function to TIM3
|
||||
GPIO_PinAFConfig(DISP_GPIO, DISP_PINSOURCE_VCOM, GPIO_AF_TIM3);
|
||||
|
||||
TIM3->EGR = TIM_PSCReloadMode_Immediate; // Reload shadow registers
|
||||
TIM_Cmd(TIM3, ENABLE);
|
||||
}
|
||||
|
||||
static void prv_display_start(void) {
|
||||
// Enable the GPIO{B,C} clocks; this is required before configuring the pins
|
||||
gpio_use(DISP_GPIO);
|
||||
gpio_use(PWR_CTL_GPIO);
|
||||
|
||||
// Connect PB13 to SPI2_SCK
|
||||
GPIO_PinAFConfig(DISP_GPIO, GPIO_PinSource13, GPIO_AF_SPI2);
|
||||
|
||||
// Connect PB15 to SPI2_MOSI
|
||||
GPIO_PinAFConfig(DISP_GPIO, GPIO_PinSource15, GPIO_AF_SPI2);
|
||||
|
||||
GPIO_Init(DISP_GPIO, &s_disp_gpio_init);
|
||||
|
||||
s_disp_gpio_init.GPIO_Mode = GPIO_Mode_OUT;
|
||||
|
||||
s_disp_gpio_init.GPIO_OType = GPIO_OType_OD;
|
||||
|
||||
s_disp_gpio_init.GPIO_Pin = PWR_CTL_PIN;
|
||||
GPIO_Init(PWR_CTL_GPIO, &s_disp_gpio_init);
|
||||
|
||||
s_disp_gpio_init.GPIO_Mode = GPIO_Mode_OUT;
|
||||
s_disp_gpio_init.GPIO_OType = GPIO_OType_PP;
|
||||
s_disp_gpio_init.GPIO_Pin = DISP_PIN_SCS;
|
||||
GPIO_Init(DISP_GPIO, &s_disp_gpio_init);
|
||||
|
||||
s_disp_gpio_init.GPIO_Mode = GPIO_Mode_AF;
|
||||
s_disp_gpio_init.GPIO_Pin = DISP_PIN_VCOM;
|
||||
GPIO_Init(DISP_GPIO, &s_disp_gpio_init);
|
||||
|
||||
s_disp_gpio_init.GPIO_Mode = GPIO_Mode_OUT;
|
||||
s_disp_gpio_init.GPIO_OType = GPIO_OType_OD;
|
||||
s_disp_gpio_init.GPIO_Pin = DISP_PIN_LCD;
|
||||
GPIO_Init(DISP_GPIO, &s_disp_gpio_init);
|
||||
|
||||
// Set up a SPI bus on SPI2
|
||||
SPI_I2S_DeInit(DISP_SPI);
|
||||
SPI_Init(DISP_SPI, &s_disp_spi_init);
|
||||
|
||||
SPI_Cmd(DISP_SPI, ENABLE);
|
||||
|
||||
// +5V to 5V_EN pin
|
||||
GPIO_WriteBit(PWR_CTL_GPIO, PWR_CTL_PIN, Bit_RESET);
|
||||
|
||||
// +5V to LCD pin (Set this pin low to turn off the display)
|
||||
GPIO_WriteBit(DISP_GPIO, DISP_PIN_LCD, Bit_SET);
|
||||
|
||||
prv_setup_pulse_vcom();
|
||||
|
||||
// Don't need the GPIO peripheral clocks to be enabled anymore
|
||||
gpio_release(PWR_CTL_GPIO);
|
||||
gpio_release(DISP_GPIO);
|
||||
}
|
||||
|
||||
// Clear-all mode is entered by sending 0x04 to the panel
|
||||
void display_clear(void) {
|
||||
prv_enable_display_access();
|
||||
|
||||
prv_display_write_byte(DISP_MODE_CLEAR);
|
||||
prv_display_write_byte(0x00);
|
||||
|
||||
prv_disable_display_access();
|
||||
}
|
||||
|
||||
//! Static mode is entered by sending 0x00 to the panel
|
||||
//! This stops any further updates being registered by
|
||||
//! the display, preventing corruption on shutdown / boot
|
||||
static void prv_display_enter_static(void) {
|
||||
prv_enable_display_access();
|
||||
|
||||
prv_display_write_byte(DISP_MODE_STATIC);
|
||||
prv_display_write_byte(0x00);
|
||||
prv_display_write_byte(0x00);
|
||||
|
||||
prv_disable_display_access();
|
||||
}
|
||||
|
||||
// Helper to reverse command bytes
|
||||
static uint8_t prv_reverse_bits(uint8_t input) {
|
||||
uint8_t result;
|
||||
__asm__ ("rev %[result], %[input]\n\t"
|
||||
"rbit %[result], %[result]"
|
||||
: [result] "=r" (result)
|
||||
: [input] "r" (input));
|
||||
return result;
|
||||
}
|
||||
|
||||
static void prv_display_start_write(void) {
|
||||
prv_enable_display_access();
|
||||
|
||||
prv_display_write_byte(DISP_MODE_WRITE);
|
||||
}
|
||||
|
||||
static void prv_display_write_line(uint8_t line_addr, const uint8_t *line) {
|
||||
// 1-indexed (ugh) 8bit line address (1-168)
|
||||
prv_display_write_byte(prv_reverse_bits(168 - line_addr));
|
||||
|
||||
for (int i = DISP_LINE_BYTES - 1; i >= 0; --i) {
|
||||
prv_display_write_byte(line[i]);
|
||||
}
|
||||
|
||||
prv_display_write_byte(0x00);
|
||||
}
|
||||
|
||||
static void prv_display_end_write(void) {
|
||||
prv_display_write_byte(0x00);
|
||||
|
||||
|
||||
prv_disable_display_access();
|
||||
}
|
||||
|
||||
// Round a bit offset to a byte offset
|
||||
static unsigned prv_round_to_byte(unsigned x) {
|
||||
return (x + 7) >> 3;
|
||||
}
|
||||
|
||||
// Draw bitmap onto buffer.
|
||||
static void prv_draw_bitmap(const uint8_t *bitmap, unsigned x_offset, unsigned y_offset,
|
||||
unsigned width, unsigned height,
|
||||
uint8_t buffer[DISP_ROWS][DISP_LINE_BYTES]) {
|
||||
// Need to convert offsets to bytes for the horizontal dimensions
|
||||
x_offset = prv_round_to_byte(x_offset);
|
||||
width = prv_round_to_byte(width);
|
||||
|
||||
for (unsigned i = 0; i < height; i++) {
|
||||
memcpy(buffer[i + y_offset] + x_offset, bitmap + i * width, width);
|
||||
}
|
||||
}
|
||||
|
||||
static void prv_display_buffer(uint8_t buffer[DISP_ROWS][DISP_LINE_BYTES]) {
|
||||
prv_display_start_write();
|
||||
for (int i = 0; i < DISP_ROWS; i++) {
|
||||
prv_display_write_line(i, buffer[i]);
|
||||
}
|
||||
prv_display_end_write();
|
||||
}
|
||||
|
||||
void display_boot_splash(void) {
|
||||
uint8_t buffer[DISP_ROWS][DISP_LINE_BYTES];
|
||||
// Draw black
|
||||
memset(buffer, 0x00, sizeof(buffer));
|
||||
|
||||
prv_draw_bitmap(pebble_logo_bits, 16, 64, pebble_logo_width, pebble_logo_height, buffer);
|
||||
|
||||
prv_display_buffer(buffer);
|
||||
}
|
||||
|
||||
static void prv_set_bit(uint8_t x, uint8_t y, uint8_t buffer[DISP_ROWS][DISP_LINE_BYTES]) {
|
||||
buffer[y][x / 8] |= (1 << (x % 8));
|
||||
}
|
||||
|
||||
static void prv_render_char(unsigned digit, uint8_t x_offset_bits, uint8_t y_offset,
|
||||
uint8_t buffer[DISP_ROWS][DISP_LINE_BYTES]) {
|
||||
const unsigned char_rows = 18, char_cols = 9;
|
||||
const uint8_t * char_data = hex_digits_bits[digit];
|
||||
|
||||
// Each character requires 2 bytes of storage
|
||||
for (unsigned y = 0; y < char_rows; y++) {
|
||||
unsigned cur_y = y_offset + y;
|
||||
uint8_t first_byte = char_data[2 * y];
|
||||
|
||||
for (unsigned x = 0; x < char_cols; x++) {
|
||||
bool pixel;
|
||||
if (x < 8) { // Pixel is in first byte
|
||||
pixel = first_byte & (1 << x);
|
||||
}
|
||||
else { // Last pixel is in second byte
|
||||
pixel = char_data[2 * y + 1] & 1;
|
||||
}
|
||||
|
||||
// The buffer is already all black, so just set the white pixels
|
||||
if (pixel) {
|
||||
prv_set_bit(x_offset_bits + x, cur_y, buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void prv_draw_code(uint32_t code, uint8_t buffer[DISP_ROWS][DISP_LINE_BYTES]) {
|
||||
const unsigned y_offset = 116; // beneath sad face, above url
|
||||
unsigned x_offset = 28; // Aligned with sad face
|
||||
|
||||
// Extract and print digits
|
||||
for (int i = 7; i >= 0; i--) {
|
||||
// Mask off 4 bits at a time
|
||||
uint32_t mask = (0xf << (i * 4));
|
||||
unsigned digit = ((code & mask) >> (i * 4));
|
||||
prv_render_char(digit, x_offset, y_offset, buffer);
|
||||
|
||||
// Each character is 9px wide plus 2px of padding
|
||||
x_offset += 11;
|
||||
}
|
||||
}
|
||||
|
||||
void display_error_code(uint32_t code) {
|
||||
uint8_t buffer[DISP_ROWS][DISP_LINE_BYTES];
|
||||
memset(buffer, 0x00, sizeof(buffer));
|
||||
|
||||
prv_draw_bitmap(dead_face_bits, 24, 32, dead_face_width, dead_face_height, buffer);
|
||||
|
||||
prv_draw_code(code, buffer);
|
||||
|
||||
prv_draw_bitmap(error_url_bits, 16, 144, error_url_width, error_url_height, buffer);
|
||||
|
||||
prv_display_buffer(buffer);
|
||||
}
|
||||
|
||||
//! Do whatever is necessary to prevent visual artifacts when resetting
|
||||
//! the watch.
|
||||
void display_prepare_for_reset(void) {
|
||||
prv_display_enter_static();
|
||||
}
|
||||
|
||||
//! 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) {
|
||||
// Dimensions for progress bar
|
||||
const unsigned x_offset = 24, y_offset = 106,
|
||||
inner_bar_width = 94, inner_bar_height = 6;
|
||||
|
||||
static unsigned s_prev_num_pixels = -1;
|
||||
// Calculate number of pixels to fill in
|
||||
unsigned num_pixels = inner_bar_width * numerator / denominator;
|
||||
|
||||
if (num_pixels == s_prev_num_pixels) {
|
||||
return;
|
||||
}
|
||||
s_prev_num_pixels = num_pixels;
|
||||
|
||||
uint8_t buffer[DISP_ROWS][DISP_LINE_BYTES];
|
||||
memset(buffer, 0x00, sizeof(buffer));
|
||||
|
||||
prv_draw_bitmap(pebble_logo_bits, 16, 64, pebble_logo_width, pebble_logo_height, buffer);
|
||||
|
||||
|
||||
prv_draw_bitmap(empty_bar_bits, x_offset, y_offset, empty_bar_width, empty_bar_height, buffer);
|
||||
|
||||
for (unsigned y = 0; y < inner_bar_height; y++) {
|
||||
for (unsigned x = 0; x < num_pixels; x++) {
|
||||
// Add 1 to offsets so we don't write into outer box
|
||||
prv_set_bit(x + x_offset + 1, y_offset + y + 1, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
prv_display_buffer(buffer);
|
||||
}
|
||||
|
||||
void display_init(void) {
|
||||
prv_enable_display_spi_clock();
|
||||
prv_display_start();
|
||||
prv_disable_display_spi_clock();
|
||||
}
|
37
platform/tintin/boot/src/drivers/flash.h
Normal file
37
platform/tintin/boot/src/drivers/flash.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>
|
||||
|
||||
//! 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);
|
258
platform/tintin/boot/src/drivers/flash/n25q.c
Normal file
258
platform/tintin/boot/src/drivers/flash/n25q.c
Normal file
|
@ -0,0 +1,258 @@
|
|||
/*
|
||||
* 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 "drivers/flash.h"
|
||||
#include "drivers/gpio.h"
|
||||
#include "drivers/periph_config.h"
|
||||
#include "stm32f2xx_gpio.h"
|
||||
#include "util/delay.h"
|
||||
|
||||
static const uint32_t EXPECTED_SPI_FLASH_ID_32MBIT = 0x20bb16;
|
||||
static const uint32_t EXPECTED_SPI_FLASH_ID_64MBIT = 0x20bb17;
|
||||
|
||||
// Serial-flash commands
|
||||
static const uint8_t FLASH_CMD_WRITE_ENABLE = 0x06;
|
||||
static const uint8_t FLASH_CMD_WRITE_DISABLE = 0x04;
|
||||
static const uint8_t FLASH_CMD_READ_STATUS_REG = 0x05;
|
||||
static const uint8_t FLASH_CMD_READ = 0x03;
|
||||
static const uint8_t FLASH_CMD_READ_ID = 0x9F;
|
||||
static const uint8_t FLASH_CMD_DEEP_SLEEP = 0xB9;
|
||||
static const uint8_t FLASH_CMD_WAKE = 0xAB;
|
||||
static const uint8_t FLASH_CMD_DUMMY = 0xA9;
|
||||
|
||||
static const struct {
|
||||
SPI_TypeDef *const spi;
|
||||
GPIO_TypeDef *const spi_gpio;
|
||||
uint8_t scs_pin, sclk_pin, miso_pin, mosi_pin;
|
||||
} FLASH_CONFIG = {
|
||||
.spi = SPI1,
|
||||
.spi_gpio = GPIOA,
|
||||
.scs_pin = 4,
|
||||
.sclk_pin = 5,
|
||||
.miso_pin = 6,
|
||||
.mosi_pin = 7,
|
||||
};
|
||||
|
||||
static void prv_enable_flash_spi_clock(void) {
|
||||
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
|
||||
}
|
||||
|
||||
static void prv_disable_flash_spi_clock(void) {
|
||||
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, DISABLE);
|
||||
}
|
||||
|
||||
static void prv_flash_start(void) {
|
||||
gpio_use(FLASH_CONFIG.spi_gpio);
|
||||
|
||||
// Enable the GPIOA clock
|
||||
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
|
||||
|
||||
uint8_t altfunc = GPIO_AF_SPI1;
|
||||
|
||||
// Connect pins to their SPI functionality
|
||||
GPIO_PinAFConfig(FLASH_CONFIG.spi_gpio, FLASH_CONFIG.sclk_pin, altfunc);
|
||||
GPIO_PinAFConfig(FLASH_CONFIG.spi_gpio, FLASH_CONFIG.miso_pin, altfunc);
|
||||
GPIO_PinAFConfig(FLASH_CONFIG.spi_gpio, FLASH_CONFIG.mosi_pin, altfunc);
|
||||
|
||||
// Setup MISO/MOSI
|
||||
GPIO_InitTypeDef gpio_cfg;
|
||||
gpio_cfg.GPIO_OType = GPIO_OType_PP;
|
||||
gpio_cfg.GPIO_PuPd = GPIO_PuPd_NOPULL;
|
||||
gpio_cfg.GPIO_Mode = GPIO_Mode_AF;
|
||||
gpio_cfg.GPIO_Speed = GPIO_Speed_50MHz;
|
||||
gpio_cfg.GPIO_Pin = (1 << FLASH_CONFIG.miso_pin) | (1 << FLASH_CONFIG.mosi_pin);
|
||||
GPIO_Init(FLASH_CONFIG.spi_gpio, &gpio_cfg);
|
||||
|
||||
// Configure the SCLK pin to have a weak pull-down to put it in a known state
|
||||
// when SCS is toggled
|
||||
gpio_cfg.GPIO_PuPd = GPIO_PuPd_DOWN;
|
||||
gpio_cfg.GPIO_Pin = (1 << FLASH_CONFIG.sclk_pin);
|
||||
GPIO_Init(FLASH_CONFIG.spi_gpio, &gpio_cfg);
|
||||
|
||||
// Configure SCS to be controlled in software; pull up to high when inactive
|
||||
gpio_cfg.GPIO_Mode = GPIO_Mode_OUT;
|
||||
gpio_cfg.GPIO_Pin = (1 << FLASH_CONFIG.scs_pin);
|
||||
gpio_cfg.GPIO_PuPd = GPIO_PuPd_UP;
|
||||
GPIO_Init(FLASH_CONFIG.spi_gpio, &gpio_cfg);
|
||||
|
||||
// Set up a SPI bus on SPI1
|
||||
SPI_InitTypeDef spi_cfg;
|
||||
SPI_I2S_DeInit(FLASH_CONFIG.spi);
|
||||
spi_cfg.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
|
||||
spi_cfg.SPI_Mode = SPI_Mode_Master;
|
||||
spi_cfg.SPI_DataSize = SPI_DataSize_8b;
|
||||
spi_cfg.SPI_CPOL = SPI_CPOL_Low;
|
||||
spi_cfg.SPI_CPHA = SPI_CPHA_1Edge;
|
||||
spi_cfg.SPI_NSS = SPI_NSS_Soft;
|
||||
// APB2 is at 16MHz, max is 54MHz, so we want the smallest prescaler
|
||||
spi_cfg.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;
|
||||
spi_cfg.SPI_FirstBit = SPI_FirstBit_MSB;
|
||||
spi_cfg.SPI_CRCPolynomial = 7;
|
||||
SPI_Init(FLASH_CONFIG.spi, &spi_cfg);
|
||||
|
||||
SPI_Cmd(FLASH_CONFIG.spi, ENABLE);
|
||||
|
||||
gpio_release(FLASH_CONFIG.spi_gpio);
|
||||
}
|
||||
|
||||
static void prv_flash_start_cmd(void) {
|
||||
gpio_use(FLASH_CONFIG.spi_gpio);
|
||||
GPIO_ResetBits(FLASH_CONFIG.spi_gpio, 1 << FLASH_CONFIG.scs_pin);
|
||||
gpio_release(FLASH_CONFIG.spi_gpio);
|
||||
}
|
||||
|
||||
static void prv_flash_end_cmd(void) {
|
||||
gpio_use(FLASH_CONFIG.spi_gpio);
|
||||
GPIO_SetBits(FLASH_CONFIG.spi_gpio, 1 << FLASH_CONFIG.scs_pin);
|
||||
gpio_release(FLASH_CONFIG.spi_gpio);
|
||||
|
||||
// 50ns required between SCS going high and low again, so just delay here to be safe
|
||||
delay_us(1);
|
||||
}
|
||||
|
||||
static uint8_t prv_flash_send_and_receive_byte(uint8_t byte) {
|
||||
// Ensure that there are no other write operations in progress
|
||||
while (SPI_I2S_GetFlagStatus(FLASH_CONFIG.spi, SPI_I2S_FLAG_TXE) == RESET) {}
|
||||
// Send the byte on the SPI bus
|
||||
SPI_I2S_SendData(FLASH_CONFIG.spi, byte);
|
||||
|
||||
// Wait for the response byte to be received
|
||||
while (SPI_I2S_GetFlagStatus(FLASH_CONFIG.spi, SPI_I2S_FLAG_RXNE) == RESET) {}
|
||||
// Return the byte
|
||||
return SPI_I2S_ReceiveData(FLASH_CONFIG.spi);
|
||||
}
|
||||
|
||||
static void prv_flash_send_24b_address(uint32_t start_addr) {
|
||||
// Ensure the high bits are not set.
|
||||
prv_flash_send_and_receive_byte((start_addr & 0xFF0000) >> 16);
|
||||
prv_flash_send_and_receive_byte((start_addr & 0x00FF00) >> 8);
|
||||
prv_flash_send_and_receive_byte((start_addr & 0x0000FF));
|
||||
}
|
||||
|
||||
static uint8_t prv_flash_read_next_byte(void) {
|
||||
uint8_t result = prv_flash_send_and_receive_byte(FLASH_CMD_DUMMY);
|
||||
return result;
|
||||
}
|
||||
|
||||
static void prv_flash_wait_for_write_bounded(volatile int cycles_to_wait) {
|
||||
prv_flash_start_cmd();
|
||||
|
||||
prv_flash_send_and_receive_byte(FLASH_CMD_READ_STATUS_REG);
|
||||
|
||||
uint8_t status_register = 0;
|
||||
do {
|
||||
if (cycles_to_wait-- < 1) {
|
||||
break;
|
||||
}
|
||||
status_register = prv_flash_read_next_byte();
|
||||
} while (status_register & 0x1);
|
||||
|
||||
prv_flash_end_cmd();
|
||||
}
|
||||
|
||||
static void prv_flash_wait_for_write(void) {
|
||||
prv_flash_start_cmd();
|
||||
|
||||
prv_flash_send_and_receive_byte(FLASH_CMD_READ_STATUS_REG);
|
||||
|
||||
uint8_t status_register = 0;
|
||||
do {
|
||||
status_register = prv_flash_read_next_byte();
|
||||
} while (status_register & 0x1);
|
||||
|
||||
prv_flash_end_cmd();
|
||||
}
|
||||
|
||||
static void prv_flash_deep_sleep_exit(void) {
|
||||
prv_flash_start_cmd();
|
||||
prv_flash_send_and_receive_byte(FLASH_CMD_WAKE);
|
||||
prv_flash_end_cmd();
|
||||
|
||||
// wait a sufficient amount of time to enter standby mode
|
||||
// It appears violating these timing conditions can lead to
|
||||
// random bit corruptions on flash writes!
|
||||
delay_us(100);
|
||||
}
|
||||
|
||||
static uint32_t prv_flash_whoami(void) {
|
||||
prv_enable_flash_spi_clock();
|
||||
|
||||
prv_flash_wait_for_write_bounded(64000000);
|
||||
|
||||
prv_flash_start_cmd();
|
||||
prv_flash_send_and_receive_byte(FLASH_CMD_READ_ID);
|
||||
uint32_t manufacturer = prv_flash_read_next_byte();
|
||||
uint32_t type = prv_flash_read_next_byte();
|
||||
uint32_t capacity = prv_flash_read_next_byte();
|
||||
prv_flash_end_cmd();
|
||||
|
||||
prv_disable_flash_spi_clock();
|
||||
|
||||
return ((manufacturer << 16) | (type << 8) | capacity);
|
||||
}
|
||||
|
||||
static bool prv_check_whoami(uint32_t spi_flash_id) {
|
||||
return spi_flash_id == EXPECTED_SPI_FLASH_ID_32MBIT ||
|
||||
spi_flash_id == EXPECTED_SPI_FLASH_ID_64MBIT;
|
||||
}
|
||||
|
||||
static bool prv_is_whoami_correct(void) {
|
||||
uint32_t spi_flash_id = prv_flash_whoami();
|
||||
return prv_check_whoami(spi_flash_id);
|
||||
}
|
||||
|
||||
void flash_init(void) {
|
||||
prv_enable_flash_spi_clock();
|
||||
|
||||
prv_flash_start();
|
||||
|
||||
// Assume that last time we shut down we were asleep. Come back out.
|
||||
prv_flash_deep_sleep_exit();
|
||||
|
||||
prv_disable_flash_spi_clock();
|
||||
|
||||
prv_flash_whoami();
|
||||
}
|
||||
|
||||
bool flash_sanity_check(void) {
|
||||
return prv_is_whoami_correct();
|
||||
}
|
||||
|
||||
void flash_read_bytes(uint8_t* buffer, uint32_t start_addr, uint32_t buffer_size) {
|
||||
if (!buffer_size) {
|
||||
return;
|
||||
}
|
||||
|
||||
prv_enable_flash_spi_clock();
|
||||
prv_flash_wait_for_write();
|
||||
|
||||
prv_flash_start_cmd();
|
||||
|
||||
prv_flash_send_and_receive_byte(FLASH_CMD_READ);
|
||||
prv_flash_send_24b_address(start_addr);
|
||||
|
||||
while (buffer_size--) {
|
||||
*buffer = prv_flash_read_next_byte();
|
||||
buffer++;
|
||||
}
|
||||
|
||||
prv_flash_end_cmd();
|
||||
|
||||
prv_disable_flash_spi_clock();
|
||||
}
|
26
platform/tintin/boot/src/drivers/gpio.h
Normal file
26
platform/tintin/boot/src/drivers/gpio.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>
|
||||
|
||||
#include "stm32f2xx_gpio.h"
|
||||
|
||||
#include "board/board.h"
|
||||
|
||||
void gpio_use(GPIO_TypeDef* GPIOx);
|
||||
void gpio_release(GPIO_TypeDef* GPIOx);
|
41
platform/tintin/boot/src/drivers/otp.h
Normal file
41
platform/tintin/boot/src/drivers/otp.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
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
enum {
|
||||
// ML/FL / 1.0 and later:
|
||||
OTP_SERIAL1 = 0,
|
||||
OTP_HWVER = 1,
|
||||
OTP_PCBA_SERIAL1 = 2,
|
||||
// Quanta / HW 1.3 and later:
|
||||
OTP_SERIAL2 = 3,
|
||||
OTP_SERIAL3 = 4,
|
||||
OTP_SERIAL4 = 5,
|
||||
OTP_SERIAL5 = 6,
|
||||
OTP_PCBA_SERIAL2 = 7,
|
||||
OTP_PCBA_SERIAL3 = 8,
|
||||
|
||||
NUM_OTP_SLOTS = 16,
|
||||
};
|
||||
|
||||
uint8_t * otp_get_lock(const uint8_t index);
|
||||
bool otp_is_locked(const uint8_t index);
|
||||
|
||||
char * otp_get_slot(const uint8_t index);
|
29
platform/tintin/boot/src/drivers/periph_config.h
Normal file
29
platform/tintin/boot/src/drivers/periph_config.h
Normal file
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* 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 "stm32f2xx.h"
|
||||
|
||||
typedef void (*ClockCmd)(uint32_t periph, FunctionalState state);
|
||||
|
||||
static inline void periph_config_enable(ClockCmd clock_cmd, uint32_t periph) {
|
||||
clock_cmd(periph, ENABLE);
|
||||
}
|
||||
|
||||
static inline void periph_config_disable(ClockCmd clock_cmd, uint32_t periph) {
|
||||
clock_cmd(periph, DISABLE);
|
||||
}
|
30
platform/tintin/boot/src/drivers/rtc.h
Normal file
30
platform/tintin/boot/src/drivers/rtc.h
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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
//! Initialize the RTC with LSE as the clocksource
|
||||
//! @return false if LSE init failed
|
||||
bool rtc_init(void);
|
||||
|
||||
//! Set the RTC to run in fast mode
|
||||
void rtc_initialize_fast_mode(void);
|
||||
|
||||
//! Slow down the RTC so we can keep time in standby mode
|
||||
void rtc_slow_down(void);
|
||||
|
||||
//! Speed up the RTC for the firmware
|
||||
void rtc_speed_up(void);
|
93
platform/tintin/boot/src/drivers/stm32_common/button.c
Normal file
93
platform/tintin/boot/src/drivers/stm32_common/button.c
Normal file
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* 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 initialize_button_common(void) {
|
||||
if (!BOARD_CONFIG_BUTTON.button_com.gpio) {
|
||||
// This board doesn't use a button common pin.
|
||||
return;
|
||||
}
|
||||
|
||||
// Configure BUTTON_COM to drive low. When the button
|
||||
// is pressed this pin will be connected to the pin for the
|
||||
// button.
|
||||
gpio_use(BOARD_CONFIG_BUTTON.button_com.gpio);
|
||||
|
||||
GPIO_InitTypeDef GPIO_InitStructure;
|
||||
GPIO_StructInit(&GPIO_InitStructure);
|
||||
|
||||
GPIO_InitStructure.GPIO_Pin = BOARD_CONFIG_BUTTON.button_com.gpio_pin;
|
||||
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
|
||||
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
|
||||
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
|
||||
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
|
||||
GPIO_Init(BOARD_CONFIG_BUTTON.button_com.gpio, &GPIO_InitStructure);
|
||||
GPIO_WriteBit(BOARD_CONFIG_BUTTON.button_com.gpio, BOARD_CONFIG_BUTTON.button_com.gpio_pin, 0);
|
||||
|
||||
gpio_release(BOARD_CONFIG_BUTTON.button_com.gpio);
|
||||
}
|
||||
|
||||
static void initialize_button(const ButtonConfig* config) {
|
||||
// Configure the pin itself
|
||||
gpio_use(config->gpio);
|
||||
|
||||
GPIO_InitTypeDef GPIO_InitStructure;
|
||||
GPIO_StructInit(&GPIO_InitStructure);
|
||||
|
||||
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
|
||||
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
|
||||
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
|
||||
GPIO_InitStructure.GPIO_Pin = config->gpio_pin;
|
||||
GPIO_Init(config->gpio, &GPIO_InitStructure);
|
||||
|
||||
gpio_release(config->gpio);
|
||||
}
|
||||
|
||||
bool button_is_pressed(ButtonId id) {
|
||||
const ButtonConfig* button_config = &BOARD_CONFIG_BUTTON.buttons[id];
|
||||
gpio_use(button_config->gpio);
|
||||
uint8_t bit = GPIO_ReadInputDataBit(button_config->gpio, button_config->gpio_pin);
|
||||
gpio_release(button_config->gpio);
|
||||
return !bit;
|
||||
}
|
||||
|
||||
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) {
|
||||
// Need to disable button wakeup functionality
|
||||
// or the buttons don't register input
|
||||
PWR_WakeUpPinCmd(DISABLE);
|
||||
|
||||
periph_config_enable(RCC_APB2PeriphClockCmd, RCC_APB2Periph_SYSCFG);
|
||||
|
||||
initialize_button_common();
|
||||
for (int i = 0; i < NUM_BUTTONS; ++i) {
|
||||
initialize_button(&BOARD_CONFIG_BUTTON.buttons[i]);
|
||||
}
|
||||
|
||||
periph_config_disable(RCC_APB2PeriphClockCmd, RCC_APB2Periph_SYSCFG);
|
||||
}
|
108
platform/tintin/boot/src/drivers/stm32_common/crc.c
Normal file
108
platform/tintin/boot/src/drivers/stm32_common/crc.c
Normal file
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
* 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/crc.h"
|
||||
|
||||
#include "drivers/flash.h"
|
||||
#include "drivers/periph_config.h"
|
||||
|
||||
#include "stm32f2xx_crc.h"
|
||||
#include "stm32f2xx_rcc.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
static void prv_enable_crc_clock(void) {
|
||||
periph_config_enable(RCC_AHB1PeriphClockCmd, RCC_AHB1Periph_CRC);
|
||||
}
|
||||
|
||||
static void prv_disable_crc_clock(void) {
|
||||
periph_config_disable(RCC_AHB1PeriphClockCmd, RCC_AHB1Periph_CRC);
|
||||
}
|
||||
|
||||
static void prv_calculate_incremental_start(void) {
|
||||
prv_enable_crc_clock();
|
||||
CRC_ResetDR();
|
||||
}
|
||||
|
||||
static void prv_calculate_incremental_words(const uint32_t* data, unsigned int data_length) {
|
||||
CRC_CalcBlockCRC((uint32_t*) data, data_length);
|
||||
}
|
||||
|
||||
static uint32_t prv_calculate_incremental_remaining_bytes(const uint8_t* data,
|
||||
unsigned int data_length) {
|
||||
uint32_t crc_value;
|
||||
|
||||
if (data_length >= 4) {
|
||||
const unsigned int num_words = data_length / 4;
|
||||
prv_calculate_incremental_words((uint32_t*) data, num_words);
|
||||
|
||||
data += num_words * 4;
|
||||
data_length -= num_words * 4;
|
||||
}
|
||||
|
||||
if (data_length) {
|
||||
uint32_t last_word = 0;
|
||||
for (unsigned int i = 0; i < data_length; ++i) {
|
||||
last_word = (last_word << 8) | data[i];
|
||||
}
|
||||
crc_value = CRC_CalcCRC(last_word);
|
||||
} else {
|
||||
crc_value = CRC_GetCRC();
|
||||
}
|
||||
|
||||
return crc_value;
|
||||
}
|
||||
|
||||
static void prv_calculate_incremental_stop(void) {
|
||||
prv_disable_crc_clock();
|
||||
}
|
||||
|
||||
uint32_t crc_calculate_bytes(const uint8_t* data, unsigned int data_length) {
|
||||
prv_calculate_incremental_start();
|
||||
|
||||
// First calculate the CRC of the whole words, since the hardware works 4
|
||||
// bytes at a time.
|
||||
uint32_t* data_words = (uint32_t*) data;
|
||||
const unsigned int num_words = data_length / 4;
|
||||
prv_calculate_incremental_words(data_words, num_words);
|
||||
|
||||
const unsigned int num_remaining_bytes = data_length % 4;
|
||||
const uint32_t res =
|
||||
prv_calculate_incremental_remaining_bytes(data + (num_words * 4), num_remaining_bytes);
|
||||
prv_calculate_incremental_stop();
|
||||
|
||||
return (res);
|
||||
}
|
||||
|
||||
uint32_t crc_calculate_flash(uint32_t address, unsigned int num_bytes) {
|
||||
prv_calculate_incremental_start();
|
||||
const unsigned int chunk_size = 128;
|
||||
|
||||
uint8_t buffer[chunk_size];
|
||||
while (num_bytes > chunk_size) {
|
||||
flash_read_bytes(buffer, address, chunk_size);
|
||||
prv_calculate_incremental_words((const uint32_t*) buffer, chunk_size / 4);
|
||||
|
||||
num_bytes -= chunk_size;
|
||||
address += chunk_size;
|
||||
}
|
||||
|
||||
flash_read_bytes(buffer, address, num_bytes);
|
||||
const uint32_t res = prv_calculate_incremental_remaining_bytes(buffer, num_bytes);
|
||||
prv_calculate_incremental_stop();
|
||||
|
||||
return (res);
|
||||
}
|
107
platform/tintin/boot/src/drivers/stm32_common/dbgserial.c
Normal file
107
platform/tintin/boot/src/drivers/stm32_common/dbgserial.c
Normal file
|
@ -0,0 +1,107 @@
|
|||
/*
|
||||
* 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/periph_config.h"
|
||||
|
||||
#include "drivers/gpio.h"
|
||||
|
||||
#include "stm32f2xx_rcc.h"
|
||||
#include "stm32f2xx_gpio.h"
|
||||
#include "stm32f2xx_usart.h"
|
||||
#include "misc.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
|
||||
static const int SERIAL_BAUD_RATE = 230400;
|
||||
|
||||
void dbgserial_init(void) {
|
||||
GPIO_InitTypeDef GPIO_InitStructure;
|
||||
USART_InitTypeDef USART_InitStructure;
|
||||
|
||||
// Enable GPIO and UART3 peripheral clocks
|
||||
gpio_use(GPIOC);
|
||||
periph_config_enable(RCC_APB1PeriphClockCmd, RCC_APB1Periph_USART3);
|
||||
|
||||
// USART_OverSampling8Cmd(USART3, ENABLE);
|
||||
|
||||
/* Connect PXx to USARTx_Tx*/
|
||||
GPIO_PinAFConfig(GPIOC, GPIO_PinSource10, GPIO_AF_USART3);
|
||||
|
||||
/* Connect PXx to USARTx_Rx*/
|
||||
GPIO_PinAFConfig(GPIOC, GPIO_PinSource11, GPIO_AF_USART3);
|
||||
|
||||
/* Configure USART Tx as alternate function */
|
||||
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
|
||||
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
|
||||
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
|
||||
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
|
||||
|
||||
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
|
||||
GPIO_Init(GPIOC, &GPIO_InitStructure);
|
||||
|
||||
/* Configure USART Rx as alternate function */
|
||||
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
|
||||
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
|
||||
GPIO_Init(GPIOC, &GPIO_InitStructure);
|
||||
|
||||
/* USART configuration */
|
||||
USART_InitStructure.USART_BaudRate = SERIAL_BAUD_RATE;
|
||||
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
|
||||
USART_InitStructure.USART_StopBits = USART_StopBits_1;
|
||||
USART_InitStructure.USART_Parity = USART_Parity_No;
|
||||
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
|
||||
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
|
||||
USART_Init(USART3, &USART_InitStructure);
|
||||
|
||||
/* Enable USART */
|
||||
USART_Cmd(USART3, ENABLE);
|
||||
|
||||
gpio_release(GPIOC);
|
||||
}
|
||||
|
||||
static void prv_putchar(uint8_t c) {
|
||||
while (USART_GetFlagStatus(USART3, USART_FLAG_TC) == RESET) continue;
|
||||
USART_SendData(USART3, c);
|
||||
while (USART_GetFlagStatus(USART3, USART_FLAG_TC) == RESET) continue;
|
||||
}
|
||||
|
||||
void dbgserial_print(const char* str) {
|
||||
while (*str) {
|
||||
prv_putchar(*str);
|
||||
++str;
|
||||
}
|
||||
}
|
||||
|
||||
void dbgserial_newline(void) {
|
||||
prv_putchar('\r');
|
||||
prv_putchar('\n');
|
||||
}
|
||||
|
||||
void dbgserial_putstr(const char* str) {
|
||||
dbgserial_print(str);
|
||||
|
||||
dbgserial_newline();
|
||||
}
|
||||
|
||||
void dbgserial_print_hex(uint32_t value) {
|
||||
char buf[12];
|
||||
itoa(value, buf, sizeof(buf));
|
||||
dbgserial_print(buf);
|
||||
}
|
38
platform/tintin/boot/src/drivers/stm32_common/gpio.c
Normal file
38
platform/tintin/boot/src/drivers/stm32_common/gpio.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/gpio.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define MAX_GPIO (9)
|
||||
|
||||
|
||||
static uint32_t s_gpio_clock_count[MAX_GPIO];
|
||||
|
||||
void gpio_use(GPIO_TypeDef* GPIOx) {
|
||||
uint32_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) {
|
||||
uint32_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));
|
||||
}
|
||||
}
|
39
platform/tintin/boot/src/drivers/stm32_common/otp.c
Normal file
39
platform/tintin/boot/src/drivers/stm32_common/otp.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/otp.h"
|
||||
|
||||
#include "stm32f2xx_flash.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
// See page 53 of STM Reference Manual RM0033:
|
||||
#define OTP_SLOTS_BASE_ADDR (0x1FFF7800)
|
||||
#define OTP_LOCKS_BASE_ADDR (0x1FFF7A00)
|
||||
|
||||
//! Each OTP slot is 32 bytes. There are 16 slots: [0-15]
|
||||
char * otp_get_slot(const uint8_t index) {
|
||||
return (char * const) (OTP_SLOTS_BASE_ADDR + (32 * index));
|
||||
}
|
||||
|
||||
uint8_t * otp_get_lock(const uint8_t index) {
|
||||
return (uint8_t * const) (OTP_LOCKS_BASE_ADDR + index);
|
||||
}
|
||||
|
||||
bool otp_is_locked(const uint8_t index) {
|
||||
return (*otp_get_lock(index) == 0);
|
||||
}
|
219
platform/tintin/boot/src/drivers/stm32_common/rtc.c
Normal file
219
platform/tintin/boot/src/drivers/stm32_common/rtc.c
Normal file
|
@ -0,0 +1,219 @@
|
|||
/*
|
||||
* 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 "drivers/dbgserial.h"
|
||||
#include "drivers/periph_config.h"
|
||||
#include "drivers/rtc.h"
|
||||
#include "util/delay.h"
|
||||
#include "system/rtc_registers.h"
|
||||
|
||||
#include "stm32f2xx_rcc.h"
|
||||
#include "stm32f2xx_rtc.h"
|
||||
|
||||
//! LSE startup time, about 4 seconds empirically,
|
||||
//! but we give it 30 seconds since it it fails we sadwatch
|
||||
static const int LSE_READY_TIMEOUT_MS = 30000;
|
||||
static const unsigned int LSE_FREQUENCY_HZ = 32768;
|
||||
static const int RTC_ASYNC_PRESCALER = 7;
|
||||
static const int RTC_SYNC_PRESCALER = 3;
|
||||
|
||||
static const unsigned int RTC_TICKS_HZ = 1024;
|
||||
static const unsigned int TICKS_IN_INTERVAL = 60 * 60 * 24;
|
||||
|
||||
static uint32_t prv_get_asynchronous_prescaler(void) {
|
||||
return (RTC->PRER >> 16) & 0x7f;
|
||||
}
|
||||
|
||||
static uint32_t prv_get_synchronous_prescaler(void) {
|
||||
return RTC->PRER & 0x1fff;
|
||||
}
|
||||
|
||||
//! Are we in slow mode?
|
||||
static bool prv_slow_mode() {
|
||||
return prv_get_asynchronous_prescaler() == 0x7f && prv_get_synchronous_prescaler() == 0xff;
|
||||
}
|
||||
|
||||
static bool prv_clocksource_is_lse_started(void) {
|
||||
return RCC_GetFlagStatus(RCC_FLAG_LSERDY) != RESET;
|
||||
}
|
||||
|
||||
static bool prv_clocksource_lse_configure(void) {
|
||||
if (prv_clocksource_is_lse_started()) {
|
||||
// LSE remains on through standby and resets so often don't need to do anything
|
||||
return true;
|
||||
}
|
||||
|
||||
dbgserial_putstr("Starting LSE oscillator");
|
||||
RCC_LSEConfig(RCC_LSE_ON);
|
||||
for (int i = 0; i < LSE_READY_TIMEOUT_MS; i++) {
|
||||
if (prv_clocksource_is_lse_started()) {
|
||||
return true;
|
||||
}
|
||||
delay_us(1000);
|
||||
}
|
||||
|
||||
dbgserial_putstr("LSE oscillator did not start");
|
||||
return false;
|
||||
}
|
||||
|
||||
//! This routine relies on bootbits already having enabled
|
||||
//! access to the PWR clock and backup domain. Re-enabling
|
||||
//! it here breaks wakeup for some reason
|
||||
//! Returns false if configuring LSE failed
|
||||
bool rtc_init(void) {
|
||||
if (!prv_clocksource_lse_configure()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);
|
||||
RCC_RTCCLKCmd(ENABLE);
|
||||
RTC_WaitForSynchro();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Before entering standby we set the RTC to it's default time (Jan 1, 2000)
|
||||
// here we calculate the seconds elapsed since then
|
||||
static int32_t prv_seconds_since_standby(void) {
|
||||
// This function assumes the RTC is running in slow mode
|
||||
|
||||
RTC_TimeTypeDef rtc_time;
|
||||
RTC_GetTime(RTC_Format_BIN, &rtc_time);
|
||||
|
||||
RTC_DateTypeDef rtc_date;
|
||||
RTC_GetDate(RTC_Format_BIN, &rtc_date);
|
||||
|
||||
// Unlike mktime there's no error checking here since if something goes wrong
|
||||
// it'll just give us the wrong time anyway
|
||||
|
||||
unsigned days = rtc_date.RTC_Year * 365; // RTC_Year is 0-99
|
||||
days += (rtc_date.RTC_Year / 4); // Leap years
|
||||
|
||||
// Cumulative days from previous months
|
||||
const unsigned month_days[] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
|
||||
|
||||
days += month_days[rtc_date.RTC_Month - 1 ]; // RTC_Month is 1-12
|
||||
if ((rtc_date.RTC_Year + 1) % 4 == 0 && rtc_date.RTC_Month > 2) {
|
||||
// On a leap year and past February so add a leap day.
|
||||
days++;
|
||||
}
|
||||
|
||||
// Add in previous days of the current month
|
||||
days += rtc_date.RTC_Date - 1;
|
||||
|
||||
return rtc_time.RTC_Seconds + 60 * (rtc_time.RTC_Minutes + 60 *
|
||||
(rtc_time.RTC_Hours + 24 * days));
|
||||
}
|
||||
|
||||
void rtc_initialize_fast_mode(void) {
|
||||
// We configure the RTC to run in "fast time". This means that the calendar will
|
||||
// be completely wrong, as we're incrementing the second count many times for every
|
||||
// real second. The firmware's driver will hide this fact from the rest of the
|
||||
// system. The reason we're doing this is because the STM32F2 micro doesn't offer
|
||||
// a subsecond field in their calendar, so we resort to crazy workarounds to get
|
||||
// a higher resolution timer.
|
||||
RTC_InitTypeDef rtc_init_struct;
|
||||
RTC_StructInit(&rtc_init_struct);
|
||||
|
||||
_Static_assert((LSE_FREQUENCY_HZ / ((RTC_ASYNC_PRESCALER + 1) * (RTC_SYNC_PRESCALER + 1))) ==
|
||||
RTC_TICKS_HZ, "Our prescalers won't create the clock we want");
|
||||
_Static_assert(RTC_ASYNC_PRESCALER >= 6, "PREDIV_A < 6 - Coarse calibration will not work.");
|
||||
|
||||
rtc_init_struct.RTC_AsynchPrediv = RTC_ASYNC_PRESCALER;
|
||||
rtc_init_struct.RTC_SynchPrediv = RTC_SYNC_PRESCALER;
|
||||
|
||||
RTC_Init(&rtc_init_struct);
|
||||
|
||||
// Reset RTC time to 0, fast mode doesn't use the date register so leave it alone
|
||||
RTC_TimeTypeDef rtc_time;
|
||||
RTC_TimeStructInit(&rtc_time);
|
||||
RTC_SetTime(RTC_Format_BIN, &rtc_time);
|
||||
}
|
||||
|
||||
void rtc_speed_up(void) {
|
||||
if (!prv_slow_mode()) {
|
||||
// If we're not in slow mode there's nothing to do
|
||||
return;
|
||||
}
|
||||
// On standby the RTC is reset to date 0, so the RTC's time is really
|
||||
// the number of seconds we've been in standby
|
||||
int32_t elapsed_since_standby = prv_seconds_since_standby();
|
||||
|
||||
int32_t saved_time = RTC_ReadBackupRegister(CURRENT_TIME_REGISTER);
|
||||
// Correct the saved time with the number of seconds we've been in standby mode
|
||||
saved_time += elapsed_since_standby;
|
||||
|
||||
// Save time in the backup register so the firmware can read it once it boots
|
||||
RTC_WriteBackupRegister(CURRENT_TIME_REGISTER, saved_time);
|
||||
RTC_WriteBackupRegister(CURRENT_INTERVAL_TICKS_REGISTER, 0);
|
||||
|
||||
rtc_initialize_fast_mode();
|
||||
}
|
||||
|
||||
static uint32_t prv_bcd_to_byte(uint32_t value) {
|
||||
const uint32_t tmp = ((value & 0xF0) >> 0x4) * 10;
|
||||
return (tmp + (value & 0x0F));
|
||||
}
|
||||
|
||||
static uint32_t prv_cur_ticks(void) {
|
||||
uint32_t time_register = RTC->TR;
|
||||
|
||||
const uint32_t hours = prv_bcd_to_byte((time_register & (RTC_TR_HT | RTC_TR_HU)) >> 16);
|
||||
const uint32_t minutes = prv_bcd_to_byte((time_register & (RTC_TR_MNT | RTC_TR_MNU)) >> 8);
|
||||
const uint32_t seconds = prv_bcd_to_byte(time_register & (RTC_TR_ST | RTC_TR_SU));
|
||||
|
||||
return (((hours * 60) + minutes) * 60) + seconds;
|
||||
}
|
||||
|
||||
static uint32_t prv_elapsed_ticks(uint32_t before, uint32_t after) {
|
||||
int32_t result = after - before;
|
||||
if (result < 0) {
|
||||
result = (TICKS_IN_INTERVAL - before) + after;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void rtc_slow_down(void) {
|
||||
if (prv_slow_mode()) {
|
||||
// If we're already slowed down there is nothing to do
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate the current time and then save it back into the backup register
|
||||
int32_t last_save_time = RTC_ReadBackupRegister(CURRENT_TIME_REGISTER);
|
||||
uint32_t last_save_ticks = RTC_ReadBackupRegister(CURRENT_INTERVAL_TICKS_REGISTER);
|
||||
uint32_t ticks_since_save = prv_elapsed_ticks(last_save_ticks, prv_cur_ticks());
|
||||
|
||||
int32_t cur_time = last_save_time + ticks_since_save / RTC_TICKS_HZ;
|
||||
// Save the current time into the backup registers
|
||||
RTC_WriteBackupRegister(CURRENT_TIME_REGISTER, cur_time);
|
||||
|
||||
// Set the RTC back to defaults (normal prescalers)
|
||||
RTC_InitTypeDef rtc_init_struct;
|
||||
RTC_StructInit(&rtc_init_struct);
|
||||
RTC_Init(&rtc_init_struct);
|
||||
|
||||
// Set the RTC to default date and time.
|
||||
// When we speed up the clock we'll add the elapsed seconds
|
||||
// to the saved register to get the correct time
|
||||
RTC_TimeTypeDef rtc_default_time;
|
||||
RTC_TimeStructInit(&rtc_default_time);
|
||||
RTC_SetTime(RTC_Format_BIN, &rtc_default_time);
|
||||
RTC_DateTypeDef rtc_default_date;
|
||||
RTC_DateStructInit(&rtc_default_date);
|
||||
RTC_SetDate(RTC_Format_BIN, &rtc_default_date);
|
||||
}
|
109
platform/tintin/boot/src/drivers/stm32_common/system_flash.c
Normal file
109
platform/tintin/boot/src/drivers/stm32_common/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 "stm32f2xx_flash.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
|
||||
};
|
||||
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
|
||||
};
|
||||
|
||||
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,
|
||||
SystemFlashProgressCb progress_callback, void *progress_context) {
|
||||
FLASH_Unlock();
|
||||
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) {
|
||||
if (FLASH_ProgramByte(address + i, data_array[i]) != FLASH_COMPLETE) {
|
||||
dbgserial_print("failed to write address ");
|
||||
dbgserial_print_hex(address);
|
||||
dbgserial_newline();
|
||||
FLASH_Lock();
|
||||
return false;
|
||||
}
|
||||
if (progress_callback && i % 128 == 0) {
|
||||
progress_callback(i/128, length/128, progress_context);
|
||||
}
|
||||
}
|
||||
FLASH_Lock();
|
||||
return true;
|
||||
}
|
41
platform/tintin/boot/src/drivers/stm32_common/watchdog.c
Normal file
41
platform/tintin/boot/src/drivers/stm32_common/watchdog.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/watchdog.h"
|
||||
|
||||
#include "stm32f2xx_dbgmcu.h"
|
||||
#include "stm32f2xx_iwdg.h"
|
||||
#include "stm32f2xx_rcc.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;
|
||||
}
|
60
platform/tintin/boot/src/drivers/system_flash.h
Normal file
60
platform/tintin/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 "stm32f2xx_flash.h"
|
||||
|
||||
#define ADDR_FLASH_SECTOR_0 ((uint32_t)0x08000000) /* Base @ of Sector 0, 16 Kbytes */
|
||||
#define ADDR_FLASH_SECTOR_1 ((uint32_t)0x08004000) /* Base @ of Sector 1, 16 Kbytes */
|
||||
#define ADDR_FLASH_SECTOR_2 ((uint32_t)0x08008000) /* Base @ of Sector 2, 16 Kbytes */
|
||||
#define ADDR_FLASH_SECTOR_3 ((uint32_t)0x0800C000) /* Base @ of Sector 3, 16 Kbytes */
|
||||
#define ADDR_FLASH_SECTOR_4 ((uint32_t)0x08010000) /* Base @ of Sector 4, 64 Kbytes */
|
||||
#define ADDR_FLASH_SECTOR_5 ((uint32_t)0x08020000) /* Base @ of Sector 5, 128 Kbytes */
|
||||
#define ADDR_FLASH_SECTOR_6 ((uint32_t)0x08040000) /* Base @ of Sector 6, 128 Kbytes */
|
||||
#define ADDR_FLASH_SECTOR_7 ((uint32_t)0x08060000) /* Base @ of Sector 7, 128 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,
|
||||
SystemFlashProgressCb progress_callback, void *progress_context);
|
24
platform/tintin/boot/src/drivers/watchdog.h
Normal file
24
platform/tintin/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);
|
31
platform/tintin/boot/src/firmware.h
Normal file
31
platform/tintin/boot/src/firmware.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 "stm32f2xx.h"
|
||||
|
||||
#define FW_OLDWORLD_OFFSET (0x10000)
|
||||
#define FW_NEWWORLD_OFFSET (0x04000)
|
||||
#define FW_WORLD_DIFFERENCE (FW_OLDWORLD_OFFSET - FW_NEWWORLD_OFFSET)
|
||||
|
||||
#define FIRMWARE_OLDWORLD_BASE (FLASH_BASE + FW_OLDWORLD_OFFSET)
|
||||
#define FIRMWARE_NEWWORLD_BASE (FLASH_BASE + FW_NEWWORLD_OFFSET)
|
||||
|
||||
// Byte offset of NeWo in firmware
|
||||
#define FW_IDENTIFIER_OFFSET (28)
|
||||
|
||||
bool firmware_is_new_world(void* base);
|
25
platform/tintin/boot/src/flash_region.h
Normal file
25
platform/tintin/boot/src/flash_region.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
|
||||
|
||||
// Scratch space for firmware images (normal and recovery).
|
||||
// We assume this is 64k aligned...
|
||||
#define FLASH_REGION_FIRMWARE_SCRATCH_BEGIN 0x0
|
||||
#define FLASH_REGION_FIRMWARE_SCRATCH_END 0x80000 // 512k
|
||||
|
||||
#define FLASH_REGION_SAFE_FIRMWARE_BEGIN 0x200000
|
||||
#define FLASH_REGION_SAFE_FIRMWARE_END 0x280000 // 512k
|
264
platform/tintin/boot/src/fw_copy.c
Normal file
264
platform/tintin/boot/src/fw_copy.c
Normal file
|
@ -0,0 +1,264 @@
|
|||
/*
|
||||
* 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/crc.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/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");
|
||||
uint32_t crc = crc_calculate_flash(flash_address, desc->firmware_length);
|
||||
return crc == desc->checksum;
|
||||
}
|
||||
|
||||
// Fills in the first 50% of the progress bar
|
||||
static void prv_display_erase_progress(
|
||||
uint32_t progress, uint32_t total, void *ctx) {
|
||||
display_firmware_update_progress(progress, total * 2);
|
||||
}
|
||||
|
||||
//! Returns true if we're going to install a new world firmware
|
||||
static bool prv_check_firmware_world(uint32_t flash_new_fw_addr) {
|
||||
// Read the beginning of the firmware off flash to see if it's new world or old world
|
||||
const unsigned world_length = FW_IDENTIFIER_OFFSET + sizeof(uint32_t); // NeWo read as uint32_t
|
||||
|
||||
uint8_t buffer[world_length];
|
||||
flash_read_bytes(buffer, flash_new_fw_addr, world_length);
|
||||
|
||||
return firmware_is_new_world(buffer);
|
||||
}
|
||||
|
||||
static bool prv_erase_old_firmware(bool new_world, uint32_t firmware_length) {
|
||||
dbgserial_putstr("erase_old_firmware");
|
||||
uint32_t system_flash_base = FIRMWARE_NEWWORLD_BASE;
|
||||
uint32_t erase_length = firmware_length;
|
||||
|
||||
if (!new_world) {
|
||||
// If the incoming firmware is an old world firmware, we need to not only erase
|
||||
// enough room for the old world firmware but we also need to erase the area
|
||||
// between the new world and old world base addresses. If we don't erase this
|
||||
// area and just load in the old world firmware at FIRMWARE_OLDWORLD_BASE, the
|
||||
// bootloader will still see the start of the old new world firmware in this
|
||||
// section and will attempt to boot that.
|
||||
dbgserial_putstr("Old World firmware base");
|
||||
erase_length += FW_WORLD_DIFFERENCE;
|
||||
}
|
||||
|
||||
return system_flash_erase(system_flash_base, erase_length, prv_display_erase_progress, 0);
|
||||
}
|
||||
|
||||
// Fills in the last 50% of the progress bar
|
||||
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(bool new_world, uint32_t flash_new_fw_start,
|
||||
uint32_t firmware_length) {
|
||||
dbgserial_putstr("write_new_firmware");
|
||||
uint32_t system_flash_base = new_world ? FIRMWARE_NEWWORLD_BASE : FIRMWARE_OLDWORLD_BASE;
|
||||
// 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(system_flash_base + i, buffer, chunk_size, NULL, NULL)) {
|
||||
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_putstr(" bytes");
|
||||
|
||||
void *system_flash_base = (void*)FIRMWARE_OLDWORLD_BASE;
|
||||
|
||||
if (firmware_is_new_world(NULL)) {
|
||||
dbgserial_putstr("New World firmware system_flash_base");
|
||||
system_flash_base = (void*)FIRMWARE_NEWWORLD_BASE;
|
||||
} else {
|
||||
dbgserial_putstr("Old World firmware system_flash_base");
|
||||
}
|
||||
uint32_t calculated_crc = crc_calculate_bytes((const uint8_t*)system_flash_base,
|
||||
firmware_description->firmware_length);
|
||||
|
||||
dbgserial_print("Checksum - wanted ");
|
||||
dbgserial_print_hex(firmware_description->checksum);
|
||||
dbgserial_print(" got ");
|
||||
dbgserial_print_hex(calculated_crc);
|
||||
dbgserial_newline();
|
||||
|
||||
return calculated_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_new_fw_addr) {
|
||||
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_new_fw_addr);
|
||||
|
||||
if (!firmware_storage_check_valid_firmware_description(&firmware_description)) {
|
||||
dbgserial_print("Desclen ");
|
||||
dbgserial_print_hex(firmware_description.description_length);
|
||||
dbgserial_print("\nFirmlen ");
|
||||
dbgserial_print_hex(firmware_description.firmware_length);
|
||||
dbgserial_print("\nXsum ");
|
||||
dbgserial_print_hex(firmware_description.checksum);
|
||||
dbgserial_putstr("\nInvalid firmware description!");
|
||||
return UPDATE_FW_ERROR_MICRO_FLASH_UNTOUCHED;
|
||||
}
|
||||
|
||||
if (!prv_check_valid_firmware_crc(
|
||||
flash_new_fw_addr + sizeof(FirmwareDescription), &firmware_description)) {
|
||||
dbgserial_putstr("Invalid firmware CRC in SPI flash!");
|
||||
return UPDATE_FW_ERROR_MICRO_FLASH_UNTOUCHED;
|
||||
}
|
||||
|
||||
bool new_world = prv_check_firmware_world(flash_new_fw_addr + sizeof(FirmwareDescription));
|
||||
|
||||
prv_erase_old_firmware(new_world, firmware_description.firmware_length);
|
||||
|
||||
prv_write_new_firmware(new_world, flash_new_fw_addr + 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 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();
|
||||
}
|
||||
|
||||
// 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 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/tintin/boot/src/fw_copy.h
Normal file
24
platform/tintin/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 check_update_fw(void);
|
||||
|
||||
//! @return false if we've failed to load recovery mode too many times and we should just sadwatch
|
||||
bool switch_to_recovery_fw(void);
|
9
platform/tintin/boot/src/git_version.auto.h.in
Normal file
9
platform/tintin/boot/src/git_version.auto.h.in
Normal file
|
@ -0,0 +1,9 @@
|
|||
#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/tintin/boot/src/hardfault_handler.c
Normal file
39
platform/tintin/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/reset.h"
|
||||
|
||||
static void prv_hard_fault_handler_c(unsigned int *hardfault_args) {
|
||||
dbgserial_putstr("HARD FAULT");
|
||||
|
||||
#ifdef NO_WATCHDOG
|
||||
__asm("bkpt\n");
|
||||
while (1) continue;
|
||||
#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));
|
||||
}
|
523
platform/tintin/boot/src/main.c
Normal file
523
platform/tintin/boot/src/main.c
Normal file
|
@ -0,0 +1,523 @@
|
|||
/*
|
||||
* 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 <inttypes.h>
|
||||
|
||||
#include "board/board.h"
|
||||
#include "boot_tests.h"
|
||||
#include "drivers/button.h"
|
||||
#include "drivers/dbgserial.h"
|
||||
#include "drivers/display.h"
|
||||
#include "drivers/flash.h"
|
||||
#include "drivers/periph_config.h"
|
||||
#include "drivers/rtc.h"
|
||||
#include "drivers/watchdog.h"
|
||||
#include "firmware.h"
|
||||
#include "fw_copy.h"
|
||||
#include "pebble_errors.h"
|
||||
#include "standby.h"
|
||||
#include "system/bootbits.h"
|
||||
#include "system/reset.h"
|
||||
#include "util/delay.h"
|
||||
|
||||
#include "stm32f2xx.h"
|
||||
|
||||
static const uint8_t SELECT_BUTTON_MASK = 0x4;
|
||||
|
||||
bool firmware_is_new_world(void* base) {
|
||||
uint32_t* fw_base = base;
|
||||
if (fw_base == NULL) {
|
||||
fw_base = (uint32_t*)FIRMWARE_NEWWORLD_BASE;
|
||||
}
|
||||
if (fw_base[0] == 0xFFFFFFFF ||
|
||||
fw_base[1] == 0xFFFFFFFF) { // Erased flash
|
||||
return false;
|
||||
}
|
||||
if (fw_base[FW_IDENTIFIER_OFFSET / sizeof(uint32_t)] != 0x4E65576F) { // NeWo
|
||||
return false;
|
||||
}
|
||||
// TODO: Additional checks?
|
||||
return true;
|
||||
}
|
||||
|
||||
static void prv_get_fw_reset_vector(void **reset_handler,
|
||||
void **initial_stack_pointer) {
|
||||
void** fw_vector_table;
|
||||
if (firmware_is_new_world(NULL)) {
|
||||
fw_vector_table = (void**) FIRMWARE_NEWWORLD_BASE;
|
||||
} else {
|
||||
fw_vector_table = (void**) FIRMWARE_OLDWORLD_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 = 0;
|
||||
RCC->AHB2ENR = 0;
|
||||
RCC->AHB3ENR = 0;
|
||||
RCC->APB1ENR = 0;
|
||||
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 modify peripherals (such as the display).
|
||||
// The backup domain is not reset; that would be foolish.
|
||||
RCC_DeInit();
|
||||
|
||||
// Reset flags for each bus taken from reset register lists in reference manual
|
||||
// starting with 5.3.5 "RCC AHB1 peripheral reset register"
|
||||
|
||||
const uint32_t ahb1_periphs = 0
|
||||
| RCC_AHB1Periph_CRC
|
||||
| RCC_AHB1Periph_DMA1
|
||||
| RCC_AHB1Periph_DMA2
|
||||
| RCC_AHB1Periph_ETH_MAC
|
||||
| RCC_AHB1Periph_OTG_HS;
|
||||
|
||||
RCC_AHB1PeriphResetCmd(ahb1_periphs, ENABLE);
|
||||
RCC_AHB1PeriphResetCmd(ahb1_periphs, DISABLE);
|
||||
|
||||
const uint32_t ahb2_periphs = 0
|
||||
| RCC_AHB2Periph_DCMI
|
||||
| RCC_AHB2Periph_CRYP
|
||||
| RCC_AHB2Periph_HASH
|
||||
| RCC_AHB2Periph_RNG
|
||||
| RCC_AHB2Periph_OTG_FS;
|
||||
|
||||
RCC_AHB2PeriphResetCmd(ahb2_periphs, ENABLE);
|
||||
RCC_AHB2PeriphResetCmd(ahb2_periphs, DISABLE);
|
||||
|
||||
const uint32_t ahb3_periphs = 0
|
||||
| RCC_AHB3Periph_FSMC;
|
||||
|
||||
RCC_AHB3PeriphResetCmd(ahb3_periphs, ENABLE);
|
||||
RCC_AHB3PeriphResetCmd(ahb3_periphs, DISABLE);
|
||||
|
||||
const uint32_t apb1_periphs = 0
|
||||
| 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_WWDG
|
||||
| RCC_APB1Periph_SPI2
|
||||
| RCC_APB1Periph_SPI3
|
||||
| RCC_APB1Periph_USART2
|
||||
| RCC_APB1Periph_USART3
|
||||
| RCC_APB1Periph_UART4
|
||||
| RCC_APB1Periph_UART5
|
||||
| RCC_APB1Periph_I2C1
|
||||
| RCC_APB1Periph_I2C2
|
||||
| RCC_APB1Periph_I2C3
|
||||
| RCC_APB1Periph_CAN1
|
||||
| RCC_APB1Periph_CAN2
|
||||
| RCC_APB1Periph_PWR
|
||||
| RCC_APB1Periph_DAC;
|
||||
|
||||
RCC_APB1PeriphResetCmd(apb1_periphs, ENABLE);
|
||||
RCC_APB1PeriphResetCmd(apb1_periphs, DISABLE);
|
||||
|
||||
const uint32_t apb2_periphs = 0
|
||||
| RCC_APB2Periph_TIM1
|
||||
| RCC_APB2Periph_TIM8
|
||||
| RCC_APB2Periph_USART1
|
||||
| RCC_APB2Periph_USART6
|
||||
| RCC_APB2Periph_ADC
|
||||
| RCC_APB2Periph_SDIO
|
||||
| RCC_APB2Periph_SPI1
|
||||
| RCC_APB2Periph_SYSCFG
|
||||
| RCC_APB2Periph_TIM9
|
||||
| RCC_APB2Periph_TIM10
|
||||
| RCC_APB2Periph_TIM11;
|
||||
|
||||
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_newline();
|
||||
dbgserial_newline();
|
||||
|
||||
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);
|
||||
|
||||
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:
|
||||
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;
|
||||
}
|
||||
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.
|
||||
dbgserial_putstr("Booting 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("Boot failed, strike 3");
|
||||
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("Boot failed, strike 2");
|
||||
boot_bit_set(BOOT_BIT_FW_START_FAIL_STRIKE_TWO);
|
||||
} else {
|
||||
dbgserial_putstr("Boot failed, strike 1");
|
||||
boot_bit_set(BOOT_BIT_FW_START_FAIL_STRIKE_ONE);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
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 (button_is_pressed(BUTTON_ID_UP) && button_is_pressed(BUTTON_ID_BACK)) {
|
||||
dbgserial_putstr("Hold down UP + BACK for 5 secs. to force-boot PRF");
|
||||
for (int i = 0; i < 5000; ++i) {
|
||||
if (!(button_is_pressed(BUTTON_ID_UP) && button_is_pressed(BUTTON_ID_BACK))) {
|
||||
// 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_print("SAD WATCH: ");
|
||||
dbgserial_print_hex(error_code);
|
||||
dbgserial_newline();
|
||||
|
||||
display_error_code(error_code);
|
||||
|
||||
static uint8_t prev_button_state = 0;
|
||||
prev_button_state = button_get_state_bits() & ~SELECT_BUTTON_MASK;
|
||||
while (1) {
|
||||
// See if we should restart
|
||||
uint8_t button_state = button_get_state_bits() & ~SELECT_BUTTON_MASK;
|
||||
if (button_state != prev_button_state) {
|
||||
system_reset();
|
||||
}
|
||||
|
||||
delay_ms(10);
|
||||
}
|
||||
}
|
||||
|
||||
static void prv_print_reset_reason(void) {
|
||||
dbgserial_print("Reset Register ");
|
||||
dbgserial_print_hex(RCC->CSR);
|
||||
dbgserial_newline();
|
||||
if (RCC_GetFlagStatus(RCC_FLAG_BORRST) == SET) {
|
||||
dbgserial_putstr("Brown out reset");
|
||||
}
|
||||
}
|
||||
|
||||
// SystemInit does this for the firmware, but since the bootloader isn't using
|
||||
// the vendor SystemInit, initialize the flash cache here
|
||||
static void prv_configure_system_flash(void) {
|
||||
// Enable flash instruction and data caches
|
||||
FLASH->ACR = FLASH_ACR_ICEN | FLASH_ACR_DCEN;
|
||||
}
|
||||
|
||||
// RTC and bootbit assume access to the backup
|
||||
// registers has been enabled
|
||||
static void prv_enable_backup_access() {
|
||||
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
|
||||
PWR_BackupAccessCmd(ENABLE); // Disable write-protect on RTC_BKP_x registers
|
||||
}
|
||||
|
||||
void boot_main(void) {
|
||||
prv_configure_system_flash();
|
||||
prv_enable_backup_access();
|
||||
|
||||
dbgserial_init();
|
||||
prv_print_reset_reason();
|
||||
|
||||
boot_bit_init();
|
||||
|
||||
if (!rtc_init()) {
|
||||
// Need to initialize the display in this
|
||||
// case so we can see the sad watch
|
||||
display_init();
|
||||
prv_sad_watch(ERROR_CANT_START_LSE);
|
||||
}
|
||||
|
||||
// Standby checks need to know the button pressed state
|
||||
button_init();
|
||||
|
||||
// On tintin the bootloader handles entering and leaving standby manually
|
||||
if (boot_bit_test(BOOT_BIT_STANDBY_MODE_REQUESTED)) {
|
||||
boot_bit_clear(BOOT_BIT_STANDBY_MODE_REQUESTED);
|
||||
enter_standby_mode();
|
||||
} else if (PWR_GetFlagStatus(PWR_FLAG_SB) == SET) { // Woke up from standby
|
||||
// Clear the standby flag since only a power reset clears it
|
||||
PWR_ClearFlag(PWR_FLAG_SB);
|
||||
|
||||
// Before coming out of standby make sure we should be waking up
|
||||
if (should_leave_standby_mode()) {
|
||||
leave_standby_mode();
|
||||
} else {
|
||||
dbgserial_putstr("returning to standby");
|
||||
enter_standby_mode();
|
||||
}
|
||||
|
||||
dbgserial_putstr("leaving standby");
|
||||
} else {
|
||||
// If not entering or leaving standby this is a cold boot. The firmware
|
||||
// expects the clock to be running in fast mode
|
||||
rtc_initialize_fast_mode();
|
||||
}
|
||||
|
||||
// Print out our super cool bootloader logo:
|
||||
// ______ __
|
||||
// /_ __/ __/ /_
|
||||
// / / /_ __/
|
||||
// /_/ /_/
|
||||
|
||||
dbgserial_putstr(" ______ __\r\n/_ __/ __/ /\r\n / / /_ __/\r\n/_/ /_/\r\n");
|
||||
|
||||
boot_version_write();
|
||||
|
||||
// Write the bootloader version to serial-out
|
||||
dbgserial_print("Bootloader version: ");
|
||||
dbgserial_print_hex(boot_version_read());
|
||||
dbgserial_newline();
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
display_init();
|
||||
display_boot_splash();
|
||||
|
||||
#ifdef DISPLAY_DEMO_LOOP
|
||||
while (1) {
|
||||
display_boot_splash();
|
||||
delay_us(1000000);
|
||||
|
||||
for (int i=0; i <= 91; ++i) {
|
||||
display_firmware_update_progress(i, 91);
|
||||
delay_us(80000);
|
||||
}
|
||||
|
||||
for (uint32_t i=0; i <= 0xf; ++i) {
|
||||
display_error_code(i * 0x11111111);
|
||||
delay_us(200000);
|
||||
}
|
||||
for (uint32_t i=0; i < 8; ++i) {
|
||||
for (uint32_t j=1; j <= 0xf; ++j) {
|
||||
display_error_code(j << (i*4));
|
||||
delay_us(200000);
|
||||
}
|
||||
}
|
||||
display_error_code(0x01234567);
|
||||
delay_us(200000);
|
||||
display_error_code(0x89abcdef);
|
||||
delay_us(200000);
|
||||
display_error_code(0xcafebabe);
|
||||
delay_us(200000);
|
||||
display_error_code(0xfeedface);
|
||||
delay_us(200000);
|
||||
display_error_code(0x8badf00d);
|
||||
delay_us(200000);
|
||||
display_error_code(0xbad1ce40);
|
||||
delay_us(200000);
|
||||
display_error_code(0xbeefcace);
|
||||
delay_us(200000);
|
||||
display_error_code(0x0defaced);
|
||||
delay_us(200000);
|
||||
display_error_code(0xd15ea5e5);
|
||||
delay_us(200000);
|
||||
display_error_code(0xdeadbeef);
|
||||
delay_us(200000);
|
||||
}
|
||||
#endif
|
||||
|
||||
flash_init();
|
||||
|
||||
if (is_button_stuck()) {
|
||||
prv_sad_watch(ERROR_STUCK_BUTTON);
|
||||
}
|
||||
|
||||
if (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
|
||||
// $150 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 (!switch_to_recovery_fw()) {
|
||||
// We've failed to load recovery mode too many times.
|
||||
prv_sad_watch(ERROR_CANT_LOAD_FW);
|
||||
}
|
||||
} else {
|
||||
check_update_fw();
|
||||
}
|
||||
|
||||
if (prv_check_and_increment_reset_loop_detection_bits()) {
|
||||
prv_sad_watch(ERROR_RESET_LOOP);
|
||||
}
|
||||
|
||||
watchdog_init();
|
||||
#ifndef NO_WATCHDOG
|
||||
watchdog_start();
|
||||
#endif
|
||||
|
||||
prv_jump_to_fw();
|
||||
}
|
34
platform/tintin/boot/src/pebble_errors.h
Normal file
34
platform/tintin/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;
|
186
platform/tintin/boot/src/standby.c
Normal file
186
platform/tintin/boot/src/standby.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 "board/board.h"
|
||||
|
||||
#include "drivers/button.h"
|
||||
#include "drivers/otp.h"
|
||||
#include "drivers/rtc.h"
|
||||
#include "drivers/periph_config.h"
|
||||
#include "drivers/dbgserial.h"
|
||||
|
||||
#include "system/reset.h"
|
||||
|
||||
static bool prv_is_wake_on_usb_supported(void) {
|
||||
// we accidentally left off a pull-up on early BB2s and v1_5 boards
|
||||
// with the upshot of not being able to support wake from standby on VUSB
|
||||
if (BOARD_CONFIG_POWER.wake_on_usb_power) {
|
||||
if (!otp_is_locked(OTP_HWVER)) {
|
||||
dbgserial_putstr("No HW Version in OTP");
|
||||
// let's be optimistic
|
||||
return true;
|
||||
}
|
||||
|
||||
const char* hw_ver = otp_get_slot(OTP_HWVER);
|
||||
#if defined(BOARD_BB2)
|
||||
// We fixed the issue for BB2.1 (900-0-22-02-R1)
|
||||
const char* no_support_hw_ver = "BB2.0";
|
||||
return (memcmp(hw_ver, no_support_hw_ver, strlen(no_support_hw_ver)) != 0);
|
||||
#elif defined(BOARD_V1_5)
|
||||
// We fixed the issue for V3R2 (101-0-22-10-R3)
|
||||
const char* no_support_hw_ver = "V3R1";
|
||||
return (memcmp(hw_ver, no_support_hw_ver, strlen(no_support_hw_ver)) != 0);
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static void prv_wait_until_buttons_are_released(void) {
|
||||
for (int bounce_count = 0; bounce_count < 10; ++bounce_count) {
|
||||
uint8_t button_state;
|
||||
|
||||
// First, see if the buttons are all released for a period of time.
|
||||
for (int i = 0; i < 10000; ++i) {
|
||||
button_state = button_get_state_bits();
|
||||
if (button_state != 0) {
|
||||
// Someone pushed a button!
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (button_state == 0) {
|
||||
// We made it through with all the buttons released. We're good.
|
||||
return;
|
||||
}
|
||||
|
||||
// Alright, so either the button is held down or we hit a bounce. Wait
|
||||
// for all the buttons to release again.
|
||||
// 100000 is about a second in practice
|
||||
for (int i = 0; i < 100000; ++i) {
|
||||
if (button_get_state_bits() == 0) {
|
||||
// All the buttons are released!
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void prv_clear_wakeup_flags(void) {
|
||||
// This function follows the steps listed in Erratum 2.1.4 "Wakeup sequence from Standby mode..."
|
||||
// to avoid a situation where the watch cannot wake up
|
||||
// or immediately wakes up after going into standby.
|
||||
|
||||
// The erratum says all used wakeup sources need to be disabled before
|
||||
// reenabling the required ones, so to be safe we disable all wakeup sources
|
||||
// to avoid dependence on knowing which wakeup sources the firmware left set
|
||||
// Possible wakeup sources taken from reference manual 4.3.5 "Exiting Standby Mode"
|
||||
|
||||
// Disable the Wakeup pin
|
||||
PWR_WakeUpPinCmd(DISABLE);
|
||||
|
||||
// Clear RTC interrupts, this ensures the flags won't be reset after we clear them
|
||||
RTC_ITConfig(RTC_IT_TAMP
|
||||
| RTC_IT_TS
|
||||
| RTC_IT_WUT
|
||||
| RTC_IT_ALRA
|
||||
| RTC_IT_ALRB, DISABLE);
|
||||
// Clear all RTC wakeup flags
|
||||
RTC_ClearFlag(RTC_FLAG_TAMP1F
|
||||
| RTC_FLAG_TSF
|
||||
| RTC_FLAG_WUTF
|
||||
| RTC_FLAG_ALRBF
|
||||
| RTC_FLAG_ALRAF);
|
||||
|
||||
// At this point we know the wakeup flags are cleared so we can clear the PWR wakeup flag
|
||||
PWR->CR |= PWR_CR_CWUF;
|
||||
}
|
||||
|
||||
static void prv_enable_wake_on_usb(void) {
|
||||
// Use the RTC timestamp alternate function to trigger a wakeup from the VUSB interrupt
|
||||
// We don't clear all the wakeup flags here as said in
|
||||
// 4.3.6 "Safe RTC alternate function wakeup flag clearing sequence", because
|
||||
// prv_clear_wakeup_flags already cleared them for use by multiple wakeup sources
|
||||
RTC_TimeStampPinSelection(RTC_TimeStampPin_PC13);
|
||||
RTC_TimeStampCmd(RTC_TimeStampEdge_Falling, ENABLE);
|
||||
RTC_ITConfig(RTC_IT_TS, ENABLE);
|
||||
}
|
||||
|
||||
void enter_standby_mode(void) {
|
||||
rtc_slow_down();
|
||||
|
||||
// Set wakeup events for the board
|
||||
// If the WKUP pin is high when we enable wakeup, an additional
|
||||
// wakeup event is registered (4.4.2 "PWR power control/status register"),
|
||||
// which will cause the board to wake up immediately after entering standby. Therefore we
|
||||
// wait until the button is released (or too much time has passed).
|
||||
// It is possible to work around needing this by enabling the WKUP pin before
|
||||
// clearing the PWR WUF flag, but that risks running afoul of errata 2.1.4
|
||||
prv_wait_until_buttons_are_released();
|
||||
|
||||
prv_clear_wakeup_flags();
|
||||
|
||||
PWR_WakeUpPinCmd(ENABLE);
|
||||
|
||||
if (prv_is_wake_on_usb_supported()) {
|
||||
dbgserial_putstr("usb wakeup supported");
|
||||
prv_enable_wake_on_usb();
|
||||
}
|
||||
|
||||
// Put the board into standby mode. The standard peripheral library provides PWR_EnterSTANDBYMode
|
||||
// to do this, but that function clears the WUF (wakeup) flag. According to errata 2.1.4 if the
|
||||
// wakeup flag is cleared when any wakeup source is high, further wakeup events may be masked.
|
||||
// This means if a button press or usb plugin was to occur in between enabling the wakeup events
|
||||
// and clearing the flag, the watch wouldn't wake up.
|
||||
dbgserial_putstr("Entering standby");
|
||||
|
||||
// Steps to enter standby follow 4.3.5 "Entering Standby mode" Table 11
|
||||
// (except where they conflict with errata 2.1.4)
|
||||
|
||||
// Select STANDBY mode
|
||||
PWR->CR |= PWR_CR_PDDS;
|
||||
|
||||
// Set SLEEPDEEP bit on the cortex system control register
|
||||
SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
|
||||
|
||||
// Wait for interrupt
|
||||
__WFI();
|
||||
}
|
||||
|
||||
bool should_leave_standby_mode(void) {
|
||||
if (RTC_GetFlagStatus(RTC_FLAG_TSF)) {
|
||||
// we were woken by the USB power being plugged in
|
||||
dbgserial_putstr("USB wakeup");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Make sure a button is held down before waking up
|
||||
for (int i = 0; i < 100000; ++i) {
|
||||
if (button_get_state_bits() == 0) {
|
||||
// stop waiting if not held down any longer and go back to sleep
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void leave_standby_mode(void) {
|
||||
// Speed up the RTC so the firmware doesn't need to deal with it
|
||||
rtc_speed_up();
|
||||
}
|
26
platform/tintin/boot/src/standby.h
Normal file
26
platform/tintin/boot/src/standby.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
|
||||
|
||||
//! Setup wakeup sources and put board into standby mode
|
||||
void enter_standby_mode(void);
|
||||
|
||||
//! Check to make sure we should be leaving standby mode
|
||||
bool should_leave_standby_mode(void);
|
||||
|
||||
//! Return functionality to normal after standby mode
|
||||
void leave_standby_mode(void);
|
80
platform/tintin/boot/src/startup_stm32.c
Normal file
80
platform/tintin/boot/src/startup_stm32.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.
|
||||
*/
|
||||
|
||||
//! Initial firmware startup, contains the vector table that the bootloader loads.
|
||||
//! Based on "https://github.com/pfalcon/cortex-uni-startup/blob/master/startup.c"
|
||||
//! by Paul Sokolovsky (public domain)
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include "util/attributes.h"
|
||||
|
||||
//! 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[];
|
||||
|
||||
//! Bootloader entry point, ResetHandler calls this
|
||||
extern int boot_main(void);
|
||||
|
||||
//! STM32 system initialization function, defined in the standard peripheral library
|
||||
extern void SystemInit(void);
|
||||
|
||||
//! We don't use any interrupts in the bootloader so we map the core interrupts to
|
||||
//! the HardFault_Handler to get useful debugging info if something goes wrong
|
||||
extern void HardFault_Handler(void);
|
||||
|
||||
//! This function is what gets called when the processor first
|
||||
//! starts execution following a reset event. The data and bss
|
||||
//! sections are initialized, then we call the firmware's main
|
||||
//! function
|
||||
void Reset_Handler(void) {
|
||||
// Copy data section from flash to RAM
|
||||
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);
|
||||
|
||||
boot_main();
|
||||
|
||||
// Main shouldn't return
|
||||
while (true) {}
|
||||
}
|
||||
|
||||
EXTERNALLY_VISIBLE SECTION(".isr_vector") void *vector_table[] = {
|
||||
_estack,
|
||||
Reset_Handler,
|
||||
HardFault_Handler,
|
||||
HardFault_Handler,
|
||||
HardFault_Handler,
|
||||
HardFault_Handler,
|
||||
HardFault_Handler,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
HardFault_Handler,
|
||||
HardFault_Handler,
|
||||
0,
|
||||
HardFault_Handler,
|
||||
HardFault_Handler
|
||||
};
|
236
platform/tintin/boot/src/stm32f_flash_boot.ld.in
Normal file
236
platform/tintin/boot/src/stm32f_flash_boot.ld.in
Normal file
|
@ -0,0 +1,236 @@
|
|||
/*
|
||||
Linker script for STM32F2xx_1024K_128K
|
||||
*/
|
||||
|
||||
__Stack_Size = 8192;
|
||||
|
||||
/* Memory Spaces Definitions */
|
||||
|
||||
MEMORY
|
||||
{
|
||||
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
|
||||
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = @BOOTLOADER_LENGTH@
|
||||
}
|
||||
|
||||
/* include the sections management sub-script for FLASH mode */
|
||||
|
||||
/* Sections Definitions */
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
/* for Cortex devices, the beginning of the startup code is stored in the .isr_vector section, which goes to FLASH */
|
||||
.isr_vector :
|
||||
{
|
||||
KEEP(*(.isr_vector)) /* Startup code */
|
||||
} >FLASH
|
||||
|
||||
/* Exception handling sections. "contains index entries for section unwinding"
|
||||
* We don't actually use this or know what it is, but it seems happy to go here. */
|
||||
.ARM.exidx :
|
||||
{
|
||||
*(.ARM.exidx)
|
||||
} >FLASH
|
||||
|
||||
/* Exported symbols:
|
||||
* This list can be calculated by putting all the KEEP directives in the normal
|
||||
* .text section, then using nm to find the addresses of each symbol. Then we
|
||||
* put each exported symbol in its own section so we can control their addresses */
|
||||
|
||||
.text.memcpy 0x08000048 : { KEEP(*(.text.memcpy)) } > FLASH
|
||||
.text.memset 0x0800005a : { KEEP(*(.text.memset)) } > FLASH
|
||||
.text.memchr 0x0800006a : { KEEP(*(.text.memchr)) } > FLASH
|
||||
.text.strcat 0x08000086 : { KEEP(*(.text.strcat)) } > FLASH
|
||||
.text.strchr 0x0800009c : { KEEP(*(.text.strchr)) } > FLASH
|
||||
.text.strcmp 0x080000b2 : { KEEP(*(.text.strcmp)) } > FLASH
|
||||
.text.strcpy 0x080000c8 : { KEEP(*(.text.strcpy)) } > FLASH
|
||||
.text.strlen 0x080000e0 : { KEEP(*(.text.strlen)) } > FLASH
|
||||
.text.strnlen 0x080000ee : { KEEP(*(.text.strnlen)) } > FLASH
|
||||
.text.strspn 0x08000100 : { KEEP(*(.text.strspn)) } > FLASH
|
||||
.text.strtol 0x0800011c : { KEEP(*(.text.strtol)) } > FLASH
|
||||
.text.atoi 0x08000140 : { KEEP(*(.text.atoi)) } > FLASH
|
||||
.text.atol 0x08000168 : { KEEP(*(.text.atol)) } > FLASH
|
||||
.text.puts 0x08000190 : { KEEP(*(.text.puts)) } > FLASH
|
||||
.text.exit 0x08000194 : { KEEP(*(.text.exit)) } > FLASH
|
||||
.text.strstr 0x08000196 : { KEEP(*(.text.strstr)) } > FLASH
|
||||
.text.strcspn 0x080001c2 : { KEEP(*(.text.strcspn)) } > FLASH
|
||||
.text.strncpy 0x080001e0 : { KEEP(*(.text.strncpy)) } > FLASH
|
||||
.text.strncmp 0x08000214 : { KEEP(*(.text.strncmp)) } > FLASH
|
||||
.text.strrchr 0x08000232 : { KEEP(*(.text.strrchr)) } > FLASH
|
||||
.text.strncat 0x08000252 : { KEEP(*(.text.strncat)) } > FLASH
|
||||
.text.memmove 0x0800027e : { KEEP(*(.text.memmove)) } > FLASH
|
||||
.text.memcmp 0x080002a2 : { KEEP(*(.text.memcmp)) } > FLASH
|
||||
.text.vsnprintf 0x080002bc : { KEEP(*(.text.vsnprintf)) } > FLASH
|
||||
.text.snprintf 0x08000730 : { KEEP(*(.text.snprintf)) } > FLASH
|
||||
.text.vsprintf 0x0800074a : { KEEP(*(.text.vsprintf)) } > FLASH
|
||||
.text.sprintf 0x0800075c : { KEEP(*(.text.sprintf)) } > FLASH
|
||||
.text.setlocale 0x08000776 : { KEEP(*(.text.setlocale)) } > FLASH
|
||||
.text.RCC_ClearFlag 0x0800077c : { KEEP(*(.text.RCC_ClearFlag)) } > FLASH
|
||||
.text.TIM_Cmd 0x0800078c : { KEEP(*(.text.TIM_Cmd)) } > FLASH
|
||||
.text.CRC_ResetDR 0x08000810 : { KEEP(*(.text.CRC_ResetDR)) } > FLASH
|
||||
.text.CRC_CalcCRC 0x0800081c : { KEEP(*(.text.CRC_CalcCRC)) } > FLASH
|
||||
.text.CRC_CalcBlockCRC 0x08000828 : { KEEP(*(.text.CRC_CalcBlockCRC)) } > FLASH
|
||||
.text.CRC_GetCRC 0x08000844 : { KEEP(*(.text.CRC_GetCRC)) } > FLASH
|
||||
.text.DBGMCU_APB1PeriphConfig 0x08000850 : { KEEP(*(.text.DBGMCU_APB1PeriphConfig)) } > FLASH
|
||||
.text.FLASH_Unlock 0x08000868 : { KEEP(*(.text.FLASH_Unlock)) } > FLASH
|
||||
.text.FLASH_Lock 0x08000884 : { KEEP(*(.text.FLASH_Lock)) } > FLASH
|
||||
.text.FLASH_ClearFlag 0x08000894 : { KEEP(*(.text.FLASH_ClearFlag)) } > FLASH
|
||||
.text.FLASH_GetStatus 0x080008a0 : { KEEP(*(.text.FLASH_GetStatus)) } > FLASH
|
||||
.text.FLASH_EraseSector 0x080008d4 : { KEEP(*(.text.FLASH_EraseSector)) } > FLASH
|
||||
.text.FLASH_ProgramByte 0x08000944 : { KEEP(*(.text.FLASH_ProgramByte)) } > FLASH
|
||||
.text.IWDG_WriteAccessCmd 0x0800097c : { KEEP(*(.text.IWDG_WriteAccessCmd)) } > FLASH
|
||||
.text.IWDG_SetPrescaler 0x08000988 : { KEEP(*(.text.IWDG_SetPrescaler)) } > FLASH
|
||||
.text.IWDG_SetReload 0x08000994 : { KEEP(*(.text.IWDG_SetReload)) } > FLASH
|
||||
.text.IWDG_ReloadCounter 0x080009a0 : { KEEP(*(.text.IWDG_ReloadCounter)) } > FLASH
|
||||
.text.IWDG_Enable 0x080009b0 : { KEEP(*(.text.IWDG_Enable)) } > FLASH
|
||||
.text.PWR_BackupAccessCmd 0x080009c0 : { KEEP(*(.text.PWR_BackupAccessCmd)) } > FLASH
|
||||
.text.PWR_WakeUpPinCmd 0x080009cc : { KEEP(*(.text.PWR_WakeUpPinCmd)) } > FLASH
|
||||
.text.PWR_GetFlagStatus 0x080009d8 : { KEEP(*(.text.PWR_GetFlagStatus)) } > FLASH
|
||||
.text.PWR_ClearFlag 0x080009ec : { KEEP(*(.text.PWR_ClearFlag)) } > FLASH
|
||||
.text.RCC_DeInit 0x080009fc : { KEEP(*(.text.RCC_DeInit)) } > FLASH
|
||||
.text.RCC_LSEConfig 0x08000a30 : { KEEP(*(.text.RCC_LSEConfig)) } > FLASH
|
||||
.text.RCC_GetClocksFreq 0x08000a50 : { KEEP(*(.text.RCC_GetClocksFreq)) } > FLASH
|
||||
.text.RCC_RTCCLKConfig 0x08000ad8 : { KEEP(*(.text.RCC_RTCCLKConfig)) } > FLASH
|
||||
.text.RCC_RTCCLKCmd 0x08000b08 : { KEEP(*(.text.RCC_RTCCLKCmd)) } > FLASH
|
||||
.text.RCC_AHB1PeriphClockCmd 0x08000b14 : { KEEP(*(.text.RCC_AHB1PeriphClockCmd)) } > FLASH
|
||||
.text.RCC_APB1PeriphClockCmd 0x08000b2c : { KEEP(*(.text.RCC_APB1PeriphClockCmd)) } > FLASH
|
||||
.text.RCC_APB2PeriphClockCmd 0x08000b44 : { KEEP(*(.text.RCC_APB2PeriphClockCmd)) } > FLASH
|
||||
.text.RCC_AHB1PeriphResetCmd 0x08000b5c : { KEEP(*(.text.RCC_AHB1PeriphResetCmd)) } > FLASH
|
||||
.text.RCC_AHB2PeriphResetCmd 0x08000b74 : { KEEP(*(.text.RCC_AHB2PeriphResetCmd)) } > FLASH
|
||||
.text.RCC_AHB3PeriphResetCmd 0x08000b8c : { KEEP(*(.text.RCC_AHB3PeriphResetCmd)) } > FLASH
|
||||
.text.RCC_APB1PeriphResetCmd 0x08000ba4 : { KEEP(*(.text.RCC_APB1PeriphResetCmd)) } > FLASH
|
||||
.text.RCC_APB2PeriphResetCmd 0x08000bbc : { KEEP(*(.text.RCC_APB2PeriphResetCmd)) } > FLASH
|
||||
.text.RCC_GetFlagStatus 0x08000bd4 : { KEEP(*(.text.RCC_GetFlagStatus)) } > FLASH
|
||||
.text.RTC_EnterInitMode 0x08000bfc : { KEEP(*(.text.RTC_EnterInitMode)) } > FLASH
|
||||
.text.RTC_ExitInitMode 0x08000c3c : { KEEP(*(.text.RTC_ExitInitMode)) } > FLASH
|
||||
.text.RTC_Init 0x08000c4c : { KEEP(*(.text.RTC_Init)) } > FLASH
|
||||
.text.RTC_WaitForSynchro 0x08000c90 : { KEEP(*(.text.RTC_WaitForSynchro)) } > FLASH
|
||||
.text.RTC_SetTime 0x08000cd4 : { KEEP(*(.text.RTC_SetTime)) } > FLASH
|
||||
.text.RTC_GetTime 0x08000d64 : { KEEP(*(.text.RTC_GetTime)) } > FLASH
|
||||
.text.RTC_SetDate 0x08000da8 : { KEEP(*(.text.RTC_SetDate)) } > FLASH
|
||||
.text.RTC_GetDate 0x08000e2c : { KEEP(*(.text.RTC_GetDate)) } > FLASH
|
||||
.text.RTC_TimeStampCmd 0x08000e6c : { KEEP(*(.text.RTC_TimeStampCmd)) } > FLASH
|
||||
.text.RTC_WriteBackupRegister 0x08000e94 : { KEEP(*(.text.RTC_WriteBackupRegister)) } > FLASH
|
||||
.text.RTC_ReadBackupRegister 0x08000eb4 : { KEEP(*(.text.RTC_ReadBackupRegister)) } > FLASH
|
||||
.text.RTC_TimeStampPinSelection 0x08000ed4 : { KEEP(*(.text.RTC_TimeStampPinSelection)) } > FLASH
|
||||
.text.RTC_ITConfig 0x08000eec : { KEEP(*(.text.RTC_ITConfig)) } > FLASH
|
||||
.text.RTC_GetFlagStatus 0x08000f28 : { KEEP(*(.text.RTC_GetFlagStatus)) } > FLASH
|
||||
.text.RTC_ClearFlag 0x08000f44 : { KEEP(*(.text.RTC_ClearFlag)) } > FLASH
|
||||
.text.SPI_I2S_DeInit 0x08000f60 : { KEEP(*(.text.SPI_I2S_DeInit)) } > FLASH
|
||||
.text.USART_Init 0x08000fbc : { KEEP(*(.text.USART_Init)) } > FLASH
|
||||
.text.FLASH_WaitForLastOperation 0x0800106c : { KEEP(*(.text.FLASH_WaitForLastOperation)) } > FLASH
|
||||
.text.GPIO_Init 0x0800108e : { KEEP(*(.text.GPIO_Init)) } > FLASH
|
||||
.text.GPIO_StructInit 0x0800110e : { KEEP(*(.text.GPIO_StructInit)) } > FLASH
|
||||
.text.GPIO_ReadInputDataBit 0x08001120 : { KEEP(*(.text.GPIO_ReadInputDataBit)) } > FLASH
|
||||
.text.GPIO_SetBits 0x0800112c : { KEEP(*(.text.GPIO_SetBits)) } > FLASH
|
||||
.text.GPIO_ResetBits 0x08001130 : { KEEP(*(.text.GPIO_ResetBits)) } > FLASH
|
||||
.text.GPIO_WriteBit 0x08001134 : { KEEP(*(.text.GPIO_WriteBit)) } > FLASH
|
||||
.text.GPIO_PinAFConfig 0x0800113e : { KEEP(*(.text.GPIO_PinAFConfig)) } > FLASH
|
||||
.text.RTC_ByteToBcd2 0x08001162 : { KEEP(*(.text.RTC_ByteToBcd2)) } > FLASH
|
||||
.text.RTC_Bcd2ToByte 0x0800117c : { KEEP(*(.text.RTC_Bcd2ToByte)) } > FLASH
|
||||
.text.RTC_StructInit 0x0800118e : { KEEP(*(.text.RTC_StructInit)) } > FLASH
|
||||
.text.RTC_TimeStructInit 0x0800119a : { KEEP(*(.text.RTC_TimeStructInit)) } > FLASH
|
||||
.text.RTC_DateStructInit 0x080011a6 : { KEEP(*(.text.RTC_DateStructInit)) } > FLASH
|
||||
.text.SPI_Init 0x080011b4 : { KEEP(*(.text.SPI_Init)) } > FLASH
|
||||
.text.SPI_Cmd 0x080011f2 : { KEEP(*(.text.SPI_Cmd)) } > FLASH
|
||||
.text.SPI_I2S_ReceiveData 0x0800120a : { KEEP(*(.text.SPI_I2S_ReceiveData)) } > FLASH
|
||||
.text.SPI_I2S_SendData 0x08001210 : { KEEP(*(.text.SPI_I2S_SendData)) } > FLASH
|
||||
.text.SPI_I2S_GetFlagStatus 0x08001214 : { KEEP(*(.text.SPI_I2S_GetFlagStatus)) } > FLASH
|
||||
.text.USART_Cmd 0x08001220 : { KEEP(*(.text.USART_Cmd)) } > FLASH
|
||||
.text.USART_SendData 0x08001238 : { KEEP(*(.text.USART_SendData)) } > FLASH
|
||||
.text.USART_GetFlagStatus 0x08001240 : { KEEP(*(.text.USART_GetFlagStatus)) } > FLASH
|
||||
.text.ctype_data 0x0800124c : { KEEP(*(.rodata.__ctype_data)) } > FLASH
|
||||
.text.bpapi 0x0800134c : { *(libgcc.a:bpapi.o*) } > FLASH
|
||||
.text.uldivmod 0x08001378 : { *(libgcc.a:_aeabi_uldivmod.o*) } > FLASH
|
||||
.text.dvmd 0x080013d8 : { *(libgcc.a:_dvmd_tls.o*) } > FLASH
|
||||
.text.divdi3 0x080013dc : { *(libgcc.a:_divdi3.o*) } > FLASH
|
||||
.text.udivdi3 0x0800167c : { *(libgcc.a:_udivdi3.o*) } > FLASH
|
||||
|
||||
/* the program code is stored in the .text section, which goes to Flash */
|
||||
.text : {
|
||||
*(.text) /* remaining code */
|
||||
*(.text.*) /* remaining code */
|
||||
*(.rodata) /* read-only data (constants) */
|
||||
*(.rodata*)
|
||||
*(.constdata) /* read-only data (constants) */
|
||||
*(.constdata*)
|
||||
. = 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);
|
||||
. += __Stack_Size;
|
||||
_estack = .;
|
||||
} >RAM
|
||||
|
||||
/* after that it's only debugging information. */
|
||||
/* remove the debugging information from the standard libraries */
|
||||
DISCARD : {
|
||||
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) }
|
||||
}
|
68
platform/tintin/boot/src/system/bootbits.c
Normal file
68
platform/tintin/boot/src/system/bootbits.c
Normal file
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* 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/bootbits.h"
|
||||
#include "system/rtc_registers.h"
|
||||
|
||||
#include "git_version.auto.h"
|
||||
|
||||
#include "stm32f2xx.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdint.h>
|
||||
|
||||
static const uint32_t s_bootloader_timestamp = GIT_TIMESTAMP;
|
||||
|
||||
void boot_bit_init(void) {
|
||||
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);
|
||||
}
|
53
platform/tintin/boot/src/system/bootbits.h
Normal file
53
platform/tintin/boot/src/system/bootbits.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 <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,
|
||||
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
|
||||
} 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);
|
30
platform/tintin/boot/src/system/firmware_storage.c
Normal file
30
platform/tintin/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/tintin/boot/src/system/firmware_storage.h
Normal file
33
platform/tintin/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);
|
36
platform/tintin/boot/src/system/reset.c
Normal file
36
platform/tintin/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 "stm32f2xx.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/tintin/boot/src/system/reset.h
Normal file
26
platform/tintin/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));
|
33
platform/tintin/boot/src/system/rtc_registers.h
Normal file
33
platform/tintin/boot/src/system/rtc_registers.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 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 MAG_XY_CORRECTION_VALS RTC_BKP_DR17
|
||||
#define MAG_Z_CORRECTION_VAL RTC_BKP_DR18
|
||||
#define SLOT_OF_LAST_LAUNCHED_APP RTC_BKP_DR19
|
50
platform/tintin/boot/src/util/attributes.h
Normal file
50
platform/tintin/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__)))
|
49
platform/tintin/boot/src/util/delay.c
Normal file
49
platform/tintin/boot/src/util/delay.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 "delay.h"
|
||||
#include "stm32f2xx.h"
|
||||
#include "util/attributes.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
// Calculated using the formula from the firmware's delay_init:
|
||||
// ceil( NS_PER_US / ( clock_period * INSTRUCTIONS_PER_LOOP )
|
||||
// where NS_PER_US = 1000 and INSTRUCTIONS_PER_LOOP = 3,
|
||||
// and clock_period = 62.5
|
||||
#define LOOPS_PER_US (6)
|
||||
|
||||
void NOINLINE delay_us(uint32_t us) {
|
||||
uint32_t delay_loops = us * LOOPS_PER_US;
|
||||
|
||||
__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);
|
||||
}
|
||||
}
|
28
platform/tintin/boot/src/util/delay.h
Normal file
28
platform/tintin/boot/src/util/delay.h
Normal file
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* 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);
|
||||
|
||||
//! Waits for a certain amount of milliseconds by busy-waiting.
|
||||
//!
|
||||
//! @param millis The number of milliseconds to wait for
|
||||
void delay_ms(uint32_t millis);
|
46
platform/tintin/boot/src/util/misc.c
Normal file
46
platform/tintin/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(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';
|
||||
}
|
34
platform/tintin/boot/src/util/misc.h
Normal file
34
platform/tintin/boot/src/util/misc.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 <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]))
|
||||
|
||||
//! Find the log base two of a number rounded up
|
||||
int ceil_log_two(uint32_t n);
|
||||
|
||||
//! Convert num to a hex string and put in buffer
|
||||
void itoa(uint32_t num, char *buffer, int buffer_length);
|
Loading…
Add table
Add a link
Reference in a new issue