mirror of
https://github.com/google/pebble.git
synced 2026-02-15 09:56:51 -05:00
Import of the watch repository from Pebble
This commit is contained in:
88
src/fw/kernel/util/delay.c
Normal file
88
src/fw/kernel/util/delay.c
Normal file
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "delay.h"
|
||||
#include "util/attributes.h"
|
||||
#include "util/units.h"
|
||||
|
||||
#define STM32F2_COMPATIBLE
|
||||
#define STM32F4_COMPATIBLE
|
||||
#define STM32F7_COMPATIBLE
|
||||
#include <mcu.h>
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#if MICRO_FAMILY_STM32F7
|
||||
# define INSTRUCTIONS_PER_LOOP (1)
|
||||
#elif MICRO_FAMILY_STM32F2 || MICRO_FAMILY_STM32F4
|
||||
# define INSTRUCTIONS_PER_LOOP (3)
|
||||
#else
|
||||
# error "Unexpected micro family"
|
||||
#endif
|
||||
|
||||
static uint32_t s_loops_per_us = 0;
|
||||
|
||||
void NOINLINE delay_us(uint32_t us) {
|
||||
|
||||
uint32_t delay_loops = us * s_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_init(void) {
|
||||
// The loop above consists of 2 instructions (output of arm-none-eabi-objdump -d
|
||||
// delay.X.o):
|
||||
//
|
||||
// subs r0, #1
|
||||
// bne.w 4 <spinloop>
|
||||
//
|
||||
// Subtract consumes 1 cycle & the conditional branch consumes 1 + P (pipeline fill delay,
|
||||
// 1-3 cycles) if the branch is taken, or 1 if not taken. For this situation, it appears that P=1
|
||||
// on the STM32F2/F4, so the loop takes 3 and 2 cycles respectively. The Cortex-M7 (STM32F7) has a
|
||||
// superscalar dual-issue architecture which allows for 1-cycle loops (including the subtract).
|
||||
//
|
||||
// @ 64MHz 1 instructions is ~15.6ns which translates to the previously measured 47ns for one loop
|
||||
// Thus we can derive that to get a duration of 1µs from an arbitrary clock frequency the count
|
||||
// value needs to be:
|
||||
// count = 1e-6 / (1/F * 3) where F is the core clock frequency
|
||||
//
|
||||
// An additional note is that delay_us is always executed from flash. The
|
||||
// instruction cache in the cortex M3 & M4 cores is pretty good at saving
|
||||
// instructions with simple branches which means we don't stall on flash
|
||||
// reads after the first loop. Counterintuitively, executing from SRAM
|
||||
// actually adds a extra delay cycle on instruction fetches and can be
|
||||
// stalled if peripherals are doing DMAs. (See PBL-22265 for more details)
|
||||
|
||||
RCC_ClocksTypeDef clocks;
|
||||
RCC_GetClocksFreq(&clocks);
|
||||
// Get the frequency in MHz so we don't overflow a uint32_t.
|
||||
const uint32_t frequency_mhz = clocks.HCLK_Frequency / MHZ_TO_HZ(1);
|
||||
const uint32_t clock_period_ps = PS_PER_US / frequency_mhz;
|
||||
s_loops_per_us = PS_PER_US / (clock_period_ps * INSTRUCTIONS_PER_LOOP);
|
||||
|
||||
// we always want to delay for more than the time specified so round up
|
||||
// if the numbers don't divide evenly
|
||||
if ((PS_PER_US % (clock_period_ps * INSTRUCTIONS_PER_LOOP)) != 0) {
|
||||
s_loops_per_us += 1;
|
||||
}
|
||||
}
|
||||
25
src/fw/kernel/util/delay.h
Normal file
25
src/fw/kernel/util/delay.h
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
//! Carefully timed spinloop that allows one to delay at a microsecond
|
||||
//! granularity.
|
||||
void delay_us(uint32_t us);
|
||||
|
||||
void delay_init(void);
|
||||
132
src/fw/kernel/util/factory_reset.c
Normal file
132
src/fw/kernel/util/factory_reset.c
Normal file
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "kernel/util/factory_reset.h"
|
||||
|
||||
#include "drivers/rtc.h"
|
||||
#include "drivers/task_watchdog.h"
|
||||
#include "flash_region/filesystem_regions.h"
|
||||
#include "kernel/event_loop.h"
|
||||
#include "kernel/util/standby.h"
|
||||
#include "process_management/worker_manager.h"
|
||||
#include "services/common/event_service.h"
|
||||
#include "services/common/shared_prf_storage/shared_prf_storage.h"
|
||||
#include "services/common/system_task.h"
|
||||
#include "services/runlevel.h"
|
||||
#include "shell/normal/app_idle_timeout.h"
|
||||
#include "system/bootbits.h"
|
||||
#include "system/logging.h"
|
||||
#include "system/reboot_reason.h"
|
||||
#include "system/reset.h"
|
||||
#include "kernel/util/sleep.h"
|
||||
|
||||
#if !RECOVERY_FW
|
||||
#include "services/normal/blob_db/pin_db.h"
|
||||
#include "services/normal/blob_db/reminder_db.h"
|
||||
#include "services/normal/filesystem/pfs.h"
|
||||
#include "services/normal/timeline/event.h"
|
||||
#endif
|
||||
|
||||
static bool s_in_factory_reset = false;
|
||||
|
||||
static void prv_factory_reset_non_pfs_data() {
|
||||
PBL_LOG_SYNC(LOG_LEVEL_INFO, "Factory resetting...");
|
||||
|
||||
// This function can block the system task for a long time.
|
||||
// Prevent callbacks being added to the system task so it doesn't overflow.
|
||||
system_task_block_callbacks(true /* block callbacks */);
|
||||
launcher_block_popups(true);
|
||||
|
||||
worker_manager_disable();
|
||||
event_service_clear_process_subscriptions(PebbleTask_App);
|
||||
|
||||
shared_prf_storage_wipe_all();
|
||||
|
||||
services_set_runlevel(RunLevel_BareMinimum);
|
||||
app_idle_timeout_stop();
|
||||
|
||||
while (worker_manager_get_current_worker_md()) {
|
||||
// busy loop until the worker is killed
|
||||
psleep(3);
|
||||
}
|
||||
|
||||
rtc_timezone_clear();
|
||||
}
|
||||
|
||||
void factory_reset_set_reason_and_reset(void) {
|
||||
RebootReason reason = { RebootReasonCode_FactoryResetReset, 0 };
|
||||
reboot_reason_set(&reason);
|
||||
system_reset();
|
||||
}
|
||||
|
||||
static void prv_factory_reset_post(bool should_shutdown) {
|
||||
if (should_shutdown) {
|
||||
enter_standby(RebootReasonCode_FactoryResetShutdown);
|
||||
} else {
|
||||
factory_reset_set_reason_and_reset();
|
||||
}
|
||||
}
|
||||
|
||||
void factory_reset(bool should_shutdown) {
|
||||
s_in_factory_reset = true;
|
||||
|
||||
prv_factory_reset_non_pfs_data();
|
||||
|
||||
// TODO: wipe the registry on tintin?
|
||||
filesystem_regions_erase_all();
|
||||
|
||||
#if !defined(RECOVERY_FW)
|
||||
// "First use" is part of the PRF image for Snowy
|
||||
boot_bit_set(BOOT_BIT_FORCE_PRF);
|
||||
#endif
|
||||
|
||||
prv_factory_reset_post(should_shutdown);
|
||||
}
|
||||
|
||||
#if !RECOVERY_FW
|
||||
void close_db_files() {
|
||||
// Deinit the databases and any clients
|
||||
timeline_event_deinit();
|
||||
reminder_db_deinit();
|
||||
pin_db_deinit();
|
||||
}
|
||||
|
||||
void factory_reset_fast(void *unused) {
|
||||
s_in_factory_reset = true;
|
||||
|
||||
// disable the watchdog... we've got lots to do before we reset
|
||||
task_watchdog_mask_clear(pebble_task_get_current());
|
||||
|
||||
close_db_files();
|
||||
|
||||
prv_factory_reset_non_pfs_data();
|
||||
|
||||
pfs_remove_files(NULL);
|
||||
|
||||
prv_factory_reset_post(false /* should_shutdown */);
|
||||
}
|
||||
#endif // !RECOVERY_FW
|
||||
|
||||
//! Used by the mfg flow to kick us out the MFG firmware and into the conumer PRF that's stored
|
||||
//! on the external flash.
|
||||
void command_enter_consumer_mode(void) {
|
||||
boot_bit_set(BOOT_BIT_FORCE_PRF);
|
||||
factory_reset(true /* should_shutdown */);
|
||||
}
|
||||
|
||||
bool factory_reset_ongoing(void) {
|
||||
return s_in_factory_reset;
|
||||
}
|
||||
29
src/fw/kernel/util/factory_reset.h
Normal file
29
src/fw/kernel/util/factory_reset.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 <stdbool.h>
|
||||
|
||||
//! Factory resets the device by wiping the flash
|
||||
//! @param should_shutdown If true, shutdown after factory resetting, otherwise reboot.
|
||||
void factory_reset(bool should_shutdown);
|
||||
|
||||
//! Factory resets the device by deleting all files
|
||||
void factory_reset_fast(void *unused);
|
||||
|
||||
//! Returns true if a factory reset is in progress.
|
||||
bool factory_reset_ongoing(void);
|
||||
93
src/fw/kernel/util/fw_reset.c
Normal file
93
src/fw/kernel/util/fw_reset.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 "kernel/util/fw_reset.h"
|
||||
|
||||
#include "console/pulse_internal.h"
|
||||
#include "kernel/core_dump.h"
|
||||
#include "kernel/util/factory_reset.h"
|
||||
#include "services/common/comm_session/session.h"
|
||||
#include "services/common/system_task.h"
|
||||
#include "services/runlevel.h"
|
||||
#include "system/bootbits.h"
|
||||
#include "system/logging.h"
|
||||
#include "system/passert.h"
|
||||
#include "system/reset.h"
|
||||
|
||||
static void prv_reset_into_prf(void) {
|
||||
RebootReason reason = { RebootReasonCode_PrfReset, 0 };
|
||||
reboot_reason_set(&reason);
|
||||
boot_bit_set(BOOT_BIT_FORCE_PRF);
|
||||
services_set_runlevel(RunLevel_BareMinimum);
|
||||
system_reset();
|
||||
}
|
||||
|
||||
void fw_reset_into_prf(void) {
|
||||
prv_reset_into_prf();
|
||||
}
|
||||
|
||||
static const uint8_t s_prf_reset_cmd = 0xff;
|
||||
|
||||
typedef enum {
|
||||
ResetCmdNormal = 0x00,
|
||||
ResetCmdCoreDump = 0x01,
|
||||
ResetCmdFactoryReset = 0xfe,
|
||||
ResetCmdIntoRecovery = 0xff,
|
||||
} ResetCmd;
|
||||
|
||||
void reset_protocol_msg_callback(CommSession *session, const uint8_t* data, unsigned int length) {
|
||||
PBL_ASSERT_RUNNING_FROM_EXPECTED_TASK(PebbleTask_KernelBackground);
|
||||
|
||||
const uint8_t cmd = data[0];
|
||||
|
||||
switch (cmd) {
|
||||
case ResetCmdNormal:
|
||||
PBL_LOG(LOG_LEVEL_WARNING, "Rebooting");
|
||||
system_reset();
|
||||
break;
|
||||
|
||||
case ResetCmdCoreDump:
|
||||
PBL_LOG(LOG_LEVEL_INFO, "Core dump + Reboot triggered");
|
||||
core_dump_reset(true /* force overwrite any existing core dump */);
|
||||
break;
|
||||
|
||||
case ResetCmdIntoRecovery:
|
||||
PBL_LOG(LOG_LEVEL_WARNING, "Rebooting into PRF");
|
||||
prv_reset_into_prf();
|
||||
break;
|
||||
|
||||
case ResetCmdFactoryReset:
|
||||
factory_reset(false /* should_shutdown */);
|
||||
break;
|
||||
|
||||
default:
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "Invalid reset msg, data[0] %u", data[0]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void fw_prepare_for_reset(bool unsafe_reset) {
|
||||
if (!unsafe_reset) {
|
||||
// Tear down Bluetooth, to avoid confusing the phone:
|
||||
services_set_runlevel(RunLevel_BareMinimum);
|
||||
#if PULSE_EVERYWHERE
|
||||
pulse_end();
|
||||
#endif
|
||||
} else {
|
||||
pulse_prepare_to_crash();
|
||||
}
|
||||
}
|
||||
|
||||
28
src/fw/kernel/util/fw_reset.h
Normal file
28
src/fw/kernel/util/fw_reset.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 <stdbool.h>
|
||||
|
||||
void fw_prepare_for_reset(bool skip_bt_teardown);
|
||||
|
||||
void fw_reset_into_prf(void);
|
||||
|
||||
typedef enum RemoteResetType {
|
||||
RemoteResetRegular = 0x00,
|
||||
RemoteResetPrf = 0xff,
|
||||
} RemoteResetType;
|
||||
95
src/fw/kernel/util/interval_timer.c
Normal file
95
src/fw/kernel/util/interval_timer.c
Normal file
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "interval_timer.h"
|
||||
|
||||
#include "drivers/rtc.h"
|
||||
#include "FreeRTOS.h"
|
||||
|
||||
static uint64_t prv_get_curr_system_time_ms(void) {
|
||||
time_t time_s;
|
||||
uint16_t time_ms;
|
||||
rtc_get_time_ms(&time_s, &time_ms);
|
||||
return (((uint64_t)time_s) * 1000 + time_ms);
|
||||
}
|
||||
|
||||
void interval_timer_init(IntervalTimer *timer, uint32_t min_expected_ms, uint32_t max_expected_ms,
|
||||
uint32_t weighting_factor_inverted) {
|
||||
PBL_ASSERTN(weighting_factor_inverted != 0); // Divide by zero is not awesome
|
||||
|
||||
*timer = (IntervalTimer) {
|
||||
.min_expected_ms = min_expected_ms,
|
||||
.max_expected_ms = max_expected_ms,
|
||||
.weighting_factor_inverted = weighting_factor_inverted
|
||||
};
|
||||
}
|
||||
|
||||
//! Record a sample that marks the start/end of an interval.
|
||||
//! Safe to call from an ISR.
|
||||
void interval_timer_take_sample(IntervalTimer *timer) {
|
||||
portENTER_CRITICAL();
|
||||
{
|
||||
const uint64_t current_time = prv_get_curr_system_time_ms();
|
||||
|
||||
// Handle the first sample specially. We don't have an interval until we have
|
||||
// 2 samples.
|
||||
if (timer->num_samples == 0) {
|
||||
timer->num_samples++;
|
||||
} else {
|
||||
const int64_t last_interval = current_time - timer->last_sample_timestamp_ms;
|
||||
|
||||
// Make sure this interval is valid
|
||||
if (last_interval >= timer->min_expected_ms &&
|
||||
last_interval <= timer->max_expected_ms) {
|
||||
|
||||
// It's valid! Let's roll it into our moving average
|
||||
|
||||
// This is an exponential moving average.
|
||||
// https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average
|
||||
// average_now = average_previous + (weighting_factor * (new_value - average_previous))
|
||||
// Where alpha is between 0 and 1. The closer to 1 the more responsive to recent changes
|
||||
// the average is.
|
||||
|
||||
if (timer->num_samples == 1) {
|
||||
// Initialize the average to the first sample we have
|
||||
timer->average_ms = last_interval;
|
||||
} else {
|
||||
timer->average_ms = timer->average_ms +
|
||||
((last_interval - timer->average_ms) / timer->weighting_factor_inverted);
|
||||
}
|
||||
|
||||
timer->num_samples++;
|
||||
}
|
||||
}
|
||||
|
||||
timer->last_sample_timestamp_ms = current_time;
|
||||
}
|
||||
|
||||
portEXIT_CRITICAL();
|
||||
}
|
||||
|
||||
uint32_t interval_timer_get(IntervalTimer *timer, uint32_t *average_ms_out) {
|
||||
uint32_t num_intervals;
|
||||
|
||||
portENTER_CRITICAL();
|
||||
{
|
||||
num_intervals = timer->num_samples ? timer->num_samples - 1 : 0;
|
||||
*average_ms_out = timer->average_ms;
|
||||
}
|
||||
portEXIT_CRITICAL();
|
||||
|
||||
return num_intervals;
|
||||
}
|
||||
74
src/fw/kernel/util/interval_timer.h
Normal file
74
src/fw/kernel/util/interval_timer.h
Normal file
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* 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>
|
||||
|
||||
//! @file interval_timer.h
|
||||
//!
|
||||
//! Times the average number of milliseconds between samples. Taking a sample is ISR-safe. Uses
|
||||
//! the RTC time domain which should be fairly accurate for real clock time. Note that because
|
||||
//! we use the RTC we may occasionally have to discard intervals because the wall clock time was
|
||||
//! changed. This is not ideal, but it's still a better source of time than our SysTick time source
|
||||
//! as that is not necessarily synced to real time.
|
||||
//!
|
||||
//! The resolution of the recorded timer will be same as the configured resolution of our RTC
|
||||
//! peripheral, which at the time of writing is 1/256 of a second.
|
||||
//!
|
||||
//! The average is calculated as an exponential moving average.
|
||||
//! https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average
|
||||
//! The weighting factor (how responsive to recent changes) is configurable.
|
||||
|
||||
typedef struct {
|
||||
uint64_t last_sample_timestamp_ms;
|
||||
|
||||
// The minimum and maximum values for an interval for it to be included into the average.
|
||||
uint32_t min_expected_ms;
|
||||
uint32_t max_expected_ms;
|
||||
|
||||
uint32_t weighting_factor_inverted;
|
||||
|
||||
//! The moving average we've calculated based on the samples we have so far.
|
||||
uint32_t average_ms;
|
||||
//! The number of samples we've taken.
|
||||
uint32_t num_samples;
|
||||
} IntervalTimer;
|
||||
|
||||
//! Initialize an interval timer.
|
||||
//!
|
||||
//! Allows the specification of an acceptable range of intervals. This is used to discard invalid
|
||||
//! intervals.
|
||||
//!
|
||||
//! Intervals are averaged together in a moving average that includes the last n intervals. The
|
||||
//! number of intervals to average over is configurable.
|
||||
//!
|
||||
//! @param min_expected_ms The minimum number of milliseconds between samples
|
||||
//! @param min_expected_ms The maximum number of milliseconds between samples
|
||||
//! @param weighting_factor_inverted
|
||||
//! 1 / alpha. Specified as a inverted number to avoid dealing with floats. The higher the
|
||||
//! number the less responsive to recent changes our average is.
|
||||
void interval_timer_init(IntervalTimer *timer, uint32_t min_expected_ms, uint32_t max_expected_ms,
|
||||
uint32_t weighting_factory_inverted);
|
||||
|
||||
//! Record a sample that marks the start/end of an interval.
|
||||
//! Safe to call from an ISR.
|
||||
void interval_timer_take_sample(IntervalTimer *timer);
|
||||
|
||||
//! @param[out] average_ms_out The average ms for the interval.
|
||||
//! @return The number of valid intervals that are in our moving average. Note that this value
|
||||
//! will never be larger than num_intervals_in_average.
|
||||
uint32_t interval_timer_get(IntervalTimer *timer, uint32_t *average_ms_out);
|
||||
74
src/fw/kernel/util/segment.c
Normal file
74
src/fw/kernel/util/segment.c
Normal file
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* 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 "segment.h"
|
||||
|
||||
#include "system/passert.h"
|
||||
|
||||
#include <stdalign.h>
|
||||
|
||||
// Remove once the Bamboo build agents build unit tests with a more
|
||||
// C11-compliant compiler (i.e. clang >= 3.5)
|
||||
#if !defined(__CLANG_MAX_ALIGN_T_DEFINED) && !defined(_GCC_MAX_ALIGN_T)
|
||||
typedef long double max_align_t;
|
||||
#endif
|
||||
|
||||
static void prv_assert_sane_segment(MemorySegment *segment) {
|
||||
PBL_ASSERT(segment->start <= segment->end,
|
||||
"Segment end points before segment start");
|
||||
}
|
||||
|
||||
static void * prv_align(void *ptr) {
|
||||
uintptr_t c = (uintptr_t)ptr;
|
||||
// Advance the pointer to the next alignment boundary.
|
||||
return (void *)((c + alignof(max_align_t) - 1) & ~(alignof(max_align_t) - 1));
|
||||
}
|
||||
|
||||
|
||||
size_t memory_segment_get_size(MemorySegment *segment) {
|
||||
prv_assert_sane_segment(segment);
|
||||
return (size_t)((uintptr_t)segment->end - (uintptr_t)segment->start);
|
||||
}
|
||||
|
||||
void memory_segment_align(MemorySegment *segment) {
|
||||
segment->start = prv_align(segment->start);
|
||||
prv_assert_sane_segment(segment);
|
||||
}
|
||||
|
||||
void * memory_segment_split(MemorySegment * restrict parent,
|
||||
MemorySegment * restrict child, size_t size) {
|
||||
prv_assert_sane_segment(parent);
|
||||
char *child_start = prv_align(parent->start);
|
||||
void *child_end = child_start + size;
|
||||
if (child_end > parent->end) {
|
||||
// Requested size is too big to fit in the parent segment.
|
||||
return NULL;
|
||||
}
|
||||
void *adjusted_parent_start = prv_align(child_end);
|
||||
if (adjusted_parent_start > parent->end) {
|
||||
// The child has left no room for the adjusted parent.
|
||||
return NULL;
|
||||
}
|
||||
parent->start = adjusted_parent_start;
|
||||
|
||||
if (child) {
|
||||
*child = (MemorySegment) {
|
||||
.start = child_start,
|
||||
.end = child_end,
|
||||
};
|
||||
}
|
||||
return child_start;
|
||||
}
|
||||
59
src/fw/kernel/util/segment.h
Normal file
59
src/fw/kernel/util/segment.h
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
//! Memory Segments
|
||||
//!
|
||||
//! A memory segment is a representation of a contiguous chunk of memory.
|
||||
//! Memory segments can be split, dividing the segment in two. This API
|
||||
//! is designed to simplify tasks such as process loading where a chunk
|
||||
//! of memory must be allocated into a bunch of smaller chunks of
|
||||
//! various fixed and dynamic sizes.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
|
||||
typedef struct MemorySegment {
|
||||
void *start; //!< The lowest address of the segment
|
||||
void *end; //!< One past the highest address of the segment
|
||||
} MemorySegment;
|
||||
|
||||
|
||||
//! Returns the size of the largest object that the segment can contain.
|
||||
size_t memory_segment_get_size(MemorySegment *segment);
|
||||
|
||||
//! Align the start pointer of a segment such that it is suitably
|
||||
//! aligned for any object.
|
||||
void memory_segment_align(MemorySegment *segment);
|
||||
|
||||
//! Split a memory segment into two.
|
||||
//!
|
||||
//! The child memory segment is allocated from the start of the parent,
|
||||
//! and the start of the parent is moved to the end of the child.
|
||||
//!
|
||||
//! After the split, the start addresses of both the parent and the
|
||||
//! child are guaranteed to be suitably aligned for any object.
|
||||
//!
|
||||
//! @param parent the memory segment to split.
|
||||
//! @param child the memory segment created from the split. May be NULL.
|
||||
//! @param size size of the new memory segment.
|
||||
//!
|
||||
//! @return start of child memory segment if successful, or NULL if
|
||||
//! there is not enough space in the parent segment to split
|
||||
//! with the requested size.
|
||||
void * memory_segment_split(MemorySegment * restrict parent,
|
||||
MemorySegment * restrict child, size_t size);
|
||||
22
src/fw/kernel/util/sleep.c
Normal file
22
src/fw/kernel/util/sleep.c
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.
|
||||
*/
|
||||
|
||||
#include "syscall/syscall.h"
|
||||
|
||||
void psleep(int millis) {
|
||||
sys_psleep(millis);
|
||||
}
|
||||
|
||||
34
src/fw/kernel/util/sleep.h
Normal file
34
src/fw/kernel/util/sleep.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
|
||||
|
||||
//! @internal
|
||||
//!
|
||||
//! Waits for a certain amount of milliseconds by suspending the thread in firmware or by just busy
|
||||
//! waiting in the bootloader.
|
||||
//!
|
||||
//! Note that the thread is slept until the current tick + millis of ticks, so that means that if
|
||||
//! we're currently part way through a tick, we'll actually wait for a time that's less than
|
||||
//! expected. For example, if we're at tick n and we want to wait for 1 millisecond, we'll sleep
|
||||
//! the thread until tick n+1, which will result in a sleep of less than a millisecond, as we're
|
||||
//! probably halfway through the n-th tick at this time. Also note that your thread isn't
|
||||
//! guaranteed to be scheduled immediately after you're done sleeping, so you may sleep for longer
|
||||
//! than you expect.
|
||||
//!
|
||||
//! @param millis The number of milliseconds to wait for
|
||||
void psleep(int millis);
|
||||
|
||||
43
src/fw/kernel/util/stack_info.c
Normal file
43
src/fw/kernel/util/stack_info.c
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.
|
||||
*/
|
||||
|
||||
#include "FreeRTOS.h"
|
||||
#include "task.h"
|
||||
|
||||
#include "mcu/interrupts.h"
|
||||
|
||||
extern uint32_t __isr_stack_start__[];
|
||||
|
||||
uint32_t stack_free_bytes(void) {
|
||||
|
||||
// Get the current SP
|
||||
register uint32_t SP __asm ("sp");
|
||||
uint32_t cur_sp = SP;
|
||||
|
||||
// Default stack
|
||||
uint32_t start = (uint32_t) __isr_stack_start__;
|
||||
|
||||
// On ISR stack?
|
||||
if (!mcu_state_is_isr()) {
|
||||
TaskHandle_t task_handle = xTaskGetCurrentTaskHandle();
|
||||
if (task_handle != NULL) {
|
||||
// task_handle is NULL before we start the first task
|
||||
start = (uint32_t)ulTaskGetStackStart(task_handle);
|
||||
}
|
||||
}
|
||||
|
||||
return cur_sp - start;
|
||||
}
|
||||
23
src/fw/kernel/util/stack_info.h
Normal file
23
src/fw/kernel/util/stack_info.h
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
//! Return the number of free bytes left in the current stack. Returns 0 if stack space could
|
||||
//! not be determined.
|
||||
uint32_t stack_free_bytes(void);
|
||||
111
src/fw/kernel/util/standby.c
Normal file
111
src/fw/kernel/util/standby.c
Normal file
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* 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 "kernel/util/standby.h"
|
||||
|
||||
#include "drivers/imu.h"
|
||||
#include "drivers/rtc.h"
|
||||
#include "drivers/flash.h"
|
||||
#include "drivers/pmic.h"
|
||||
#include "drivers/pwr.h"
|
||||
#include "drivers/periph_config.h"
|
||||
#include "system/bootbits.h"
|
||||
#include "system/logging.h"
|
||||
#include "system/reset.h"
|
||||
|
||||
#include "drivers/display/display.h"
|
||||
|
||||
#define STM32F2_COMPATIBLE
|
||||
#define STM32F4_COMPATIBLE
|
||||
#define STM32F7_COMPATIBLE
|
||||
#include <mcu.h>
|
||||
|
||||
#include "FreeRTOS.h"
|
||||
#include "task.h"
|
||||
|
||||
//! If we don't have a PMIC entering standby is a little more complicated.
|
||||
//! See platform/tintin/boot/src/standby.c.
|
||||
//! We set a bootbit and reboot, and then the bootloader is responsible for really winding us down.
|
||||
//! This is necessary because:
|
||||
//! - When entering standby, a system reset is the only way to disable the IWDG
|
||||
//! - When shutting down, it simplifies waiting on the charger to be removed,
|
||||
//! and allows for us to handle other boot bits (eg. Force PRF) before powering down.
|
||||
//!
|
||||
//! @param boot_bit Boot bit to set. It should cause the bootloader to shut down or enter standby.
|
||||
static NORETURN prv_enter_standby_non_pmic(BootBitValue boot_bit) {
|
||||
// The I2C bus is not initialized in the bootloader.
|
||||
// Put the Accelerometer into low power mode before resetting
|
||||
imu_power_down();
|
||||
|
||||
boot_bit_set(boot_bit);
|
||||
|
||||
PBL_LOG(LOG_LEVEL_ALWAYS, "Rebooting to enter Standby mode.");
|
||||
reboot_reason_set_restarted_safely();
|
||||
|
||||
system_hard_reset();
|
||||
}
|
||||
|
||||
static NORETURN prv_enter_standby_pmic(void) {
|
||||
reboot_reason_set_restarted_safely();
|
||||
|
||||
#if defined(TARGET_QEMU)
|
||||
#if MICRO_FAMILY_STM32F7
|
||||
WTF; // Unsupported
|
||||
#else
|
||||
// QEMU does not implement i2c devices, like the PMIC, yet. Let's turn off instead
|
||||
// by going into standby mode using the power control of the STM32. We can't use
|
||||
// prv_enter_standby_non_pmic() because PMIC based boards don't support that feature in their
|
||||
// bootloader.
|
||||
periph_config_enable(PWR, RCC_APB1Periph_PWR);
|
||||
pwr_enable_wakeup(true);
|
||||
PWR_EnterSTANDBYMode();
|
||||
#endif
|
||||
#endif
|
||||
|
||||
PBL_LOG(LOG_LEVEL_ALWAYS, "Using the PMIC to enter standby mode.");
|
||||
pmic_power_off();
|
||||
PBL_CROAK("PMIC didn't shut us down!");
|
||||
}
|
||||
|
||||
NORETURN enter_standby(RebootReasonCode reason) {
|
||||
PBL_LOG(LOG_LEVEL_ALWAYS, "Preparing to enter standby mode.");
|
||||
|
||||
RebootReason reboot_reason = { reason, 0 };
|
||||
reboot_reason_set(&reboot_reason);
|
||||
|
||||
// Wipe display
|
||||
display_clear();
|
||||
|
||||
/* skip BT teardown if BT isn't working */
|
||||
system_reset_prepare(reason == RebootReasonCode_DialogBootFault);
|
||||
|
||||
#if PLATFORM_SILK && RECOVERY_FW
|
||||
// For Silk PRF & MFG firmwares, fully shutdown the watch using the bootloader.
|
||||
// Always entering full shutdown in these two situations will guarantee a much
|
||||
// better shelf-life, and ensure that watches are shipped in full shutdown mode.
|
||||
//
|
||||
// Request the bootloader to completely power down as the last thing it does,
|
||||
// rather than jumping into the fw. The bootloader may spin on the charger
|
||||
// connection status, as we cannot shutdown while the charger is plugged.
|
||||
// Luckily, we never try to power down the watch while plugged when in PRF.
|
||||
prv_enter_standby_non_pmic(BOOT_BIT_SHUTDOWN_REQUESTED);
|
||||
#elif CAPABILITY_HAS_PMIC
|
||||
prv_enter_standby_pmic();
|
||||
#else
|
||||
// Request the bootloader to enter standby mode immediately after the system is reset.
|
||||
prv_enter_standby_non_pmic(BOOT_BIT_STANDBY_MODE_REQUESTED);
|
||||
#endif
|
||||
}
|
||||
27
src/fw/kernel/util/standby.h
Normal file
27
src/fw/kernel/util/standby.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 "system/reboot_reason.h"
|
||||
#include "util/attributes.h"
|
||||
|
||||
#if !UNITTEST
|
||||
NORETURN
|
||||
#else
|
||||
void
|
||||
#endif
|
||||
enter_standby(RebootReasonCode reason);
|
||||
215
src/fw/kernel/util/stop.c
Normal file
215
src/fw/kernel/util/stop.c
Normal file
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
* 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 "console/dbgserial.h"
|
||||
#include "console/dbgserial_input.h"
|
||||
#include "drivers/flash.h"
|
||||
#include "drivers/periph_config.h"
|
||||
#include "drivers/rtc.h"
|
||||
#include "drivers/task_watchdog.h"
|
||||
#include "os/tick.h"
|
||||
#include "kernel/util/stop.h"
|
||||
#include "kernel/util/wfi.h"
|
||||
#include "mcu/interrupts.h"
|
||||
#include "services/common/analytics/analytics.h"
|
||||
#include "system/passert.h"
|
||||
|
||||
#define STM32F2_COMPATIBLE
|
||||
#define STM32F4_COMPATIBLE
|
||||
#define STM32F7_COMPATIBLE
|
||||
#include <mcu.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
static int s_num_items_disallowing_stop_mode = 0;
|
||||
|
||||
#ifdef PBL_NOSLEEP
|
||||
static bool s_sleep_mode_allowed = false;
|
||||
#else
|
||||
static bool s_sleep_mode_allowed = true;
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
uint32_t active_count;
|
||||
RtcTicks ticks_when_stop_mode_disabled;
|
||||
RtcTicks total_ticks_while_disabled;
|
||||
} InhibitorTickProfile;
|
||||
|
||||
// Note: These variables should be protected within a critical section since
|
||||
// they are read and modified by multiple threads
|
||||
static InhibitorTickProfile s_inhibitor_profile[InhibitorNumItems];
|
||||
|
||||
void enter_stop_mode(void) {
|
||||
// enable the interrupt on the debug RX line so that we can use the serial
|
||||
// console even when we are in stop mode.
|
||||
dbgserial_enable_rx_exti();
|
||||
|
||||
flash_power_down_for_stop_mode();
|
||||
|
||||
// Turn on the power control peripheral so that we can put the regulator into low-power mode
|
||||
periph_config_enable(PWR, RCC_APB1Periph_PWR);
|
||||
|
||||
if (mcu_state_are_interrupts_enabled()) {
|
||||
// If INTs aren't disabled here, we would wind up servicing INTs
|
||||
// immediately after the WFI (while running at the wrong clock speed) which
|
||||
// can confuse peripherals in subtle ways
|
||||
WTF;
|
||||
}
|
||||
|
||||
// Enter stop mode.
|
||||
//PWR_EnterSTOPMode(PWR_Regulator_LowPower, PWR_STOPEntry_WFI);
|
||||
// We don't use ^^ the above function because of a silicon bug which
|
||||
// causes the processor to skip some instructions upon wake from STOP
|
||||
// in certain sitations. See the STM32F20x and STM32F21x Errata sheet
|
||||
// section 2.1.3 "Debugging Stop mode with WFE entry", or the erratum
|
||||
// of the same name in section 2.1.2 of the STM32F42x and STM32F43x
|
||||
// Errata sheet, for (misleading) details.
|
||||
// http://www.st.com/web/en/resource/technical/document/errata_sheet/DM00027213.pdf
|
||||
// http://www.st.com/web/en/resource/technical/document/errata_sheet/DM00068628.pdf
|
||||
|
||||
// Configure the PWR peripheral to put us in low-power STOP mode when
|
||||
// the processor enters deepsleep.
|
||||
#if defined(MICRO_FAMILY_STM32F7)
|
||||
uint32_t temp = PWR->CR1;
|
||||
temp &= ~PWR_CR1_PDDS;
|
||||
temp |= PWR_CR1_LPDS;
|
||||
PWR->CR1 = temp;
|
||||
#else
|
||||
uint32_t temp = PWR->CR;
|
||||
temp &= ~PWR_CR_PDDS;
|
||||
temp |= PWR_CR_LPDS;
|
||||
#if STM32F412xG
|
||||
// STM32F412xG suports a new "low-power regulator low voltage in deep sleep" mode.
|
||||
temp |= PWR_CR_LPLVDS;
|
||||
#endif
|
||||
PWR->CR = temp;
|
||||
#endif
|
||||
|
||||
// Configure the processor core to enter deepsleep mode when we
|
||||
// execute a WFI or WFE instruction.
|
||||
SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
|
||||
|
||||
// Go stop now.
|
||||
__DSB(); // Drain any pending memory writes before entering sleep.
|
||||
do_wfi(); // Wait for Interrupt (enter sleep mode). Work around F2/F4 errata.
|
||||
__ISB(); // Let the pipeline catch up (force the WFI to activate before moving on).
|
||||
|
||||
// Tell the processor not to emter deepsleep mode for future WFIs.
|
||||
SCB->SCR &= ~SCB_SCR_SLEEPDEEP_Msk;
|
||||
|
||||
// Stop mode will change our system clock to the HSI. Move it back to the PLL.
|
||||
|
||||
// Enable the PLL and wait until it's ready
|
||||
RCC_PLLCmd(ENABLE);
|
||||
while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET) {}
|
||||
|
||||
// Select PLL as system clock source and wait until it's being used
|
||||
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
|
||||
while (RCC_GetSYSCLKSource() != 0x08) {}
|
||||
|
||||
// No longer need the power control peripheral
|
||||
periph_config_disable(PWR, RCC_APB1Periph_PWR);
|
||||
|
||||
flash_power_up_after_stop_mode();
|
||||
}
|
||||
|
||||
void stop_mode_disable( StopModeInhibitor inhibitor ) {
|
||||
portENTER_CRITICAL();
|
||||
++s_num_items_disallowing_stop_mode;
|
||||
|
||||
++s_inhibitor_profile[inhibitor].active_count;
|
||||
// TODO: We should probably check if s_inhibitor_profile.active_count == 1
|
||||
// before doing this assignment. We don't seem to ever run into this case
|
||||
// yet (i.e. active_count is never > 1), but when we do, this code would
|
||||
// report the wrong number of nostop ticks.
|
||||
s_inhibitor_profile[inhibitor].ticks_when_stop_mode_disabled = rtc_get_ticks();
|
||||
portEXIT_CRITICAL();
|
||||
}
|
||||
|
||||
void stop_mode_enable( StopModeInhibitor inhibitor ) {
|
||||
portENTER_CRITICAL();
|
||||
PBL_ASSERTN(s_num_items_disallowing_stop_mode != 0);
|
||||
PBL_ASSERTN(s_inhibitor_profile[inhibitor].active_count != 0);
|
||||
|
||||
--s_num_items_disallowing_stop_mode;
|
||||
--s_inhibitor_profile[inhibitor].active_count;
|
||||
if (s_inhibitor_profile[inhibitor].active_count == 0) {
|
||||
s_inhibitor_profile[inhibitor].total_ticks_while_disabled += rtc_get_ticks() -
|
||||
s_inhibitor_profile[inhibitor].ticks_when_stop_mode_disabled;
|
||||
}
|
||||
portEXIT_CRITICAL();
|
||||
}
|
||||
|
||||
bool stop_mode_is_allowed(void) {
|
||||
#if PBL_NOSTOP
|
||||
return false;
|
||||
#else
|
||||
return s_num_items_disallowing_stop_mode == 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void sleep_mode_enable(bool enable) {
|
||||
s_sleep_mode_allowed = enable;
|
||||
}
|
||||
|
||||
bool sleep_mode_is_allowed(void) {
|
||||
#ifdef PBL_NOSLEEP
|
||||
return false;
|
||||
#endif
|
||||
return s_sleep_mode_allowed;
|
||||
}
|
||||
|
||||
static RtcTicks prv_get_nostop_ticks(StopModeInhibitor inhibitor, RtcTicks now_ticks) {
|
||||
RtcTicks total_ticks = s_inhibitor_profile[inhibitor].total_ticks_while_disabled;
|
||||
if (s_inhibitor_profile[inhibitor].active_count != 0) {
|
||||
total_ticks += (now_ticks - s_inhibitor_profile[inhibitor].ticks_when_stop_mode_disabled);
|
||||
}
|
||||
return total_ticks;
|
||||
}
|
||||
|
||||
static void prv_collect(AnalyticsMetric metric, StopModeInhibitor inhibitor, RtcTicks now_ticks) {
|
||||
// operating on 64 bit values so the load/stores will _not_ be atomic
|
||||
portENTER_CRITICAL();
|
||||
RtcTicks ticks = prv_get_nostop_ticks(inhibitor, now_ticks);
|
||||
s_inhibitor_profile[inhibitor].total_ticks_while_disabled = 0;
|
||||
portEXIT_CRITICAL();
|
||||
analytics_set(metric, ticks_to_milliseconds(ticks), AnalyticsClient_System);
|
||||
}
|
||||
|
||||
void analytics_external_collect_stop_inhibitor_stats(RtcTicks now_ticks) {
|
||||
prv_collect(ANALYTICS_DEVICE_METRIC_CPU_NOSTOP_MAIN_TIME, InhibitorMain, now_ticks);
|
||||
// We don't care about the serial console nostop time, it should always
|
||||
// be zero on watches in the field anyway. (InhibitorDbgSerial skipped)
|
||||
prv_collect(ANALYTICS_DEVICE_METRIC_CPU_NOSTOP_BUTTON_TIME, InhibitorButton, now_ticks);
|
||||
prv_collect(ANALYTICS_DEVICE_METRIC_CPU_NOSTOP_BLUETOOTH_TIME, InhibitorBluetooth, now_ticks);
|
||||
prv_collect(ANALYTICS_DEVICE_METRIC_CPU_NOSTOP_DISPLAY_TIME, InhibitorDisplay, now_ticks);
|
||||
prv_collect(ANALYTICS_DEVICE_METRIC_CPU_NOSTOP_BACKLIGHT_TIME, InhibitorBacklight, now_ticks);
|
||||
prv_collect(ANALYTICS_DEVICE_METRIC_CPU_NOSTOP_COMM_TIME, InhibitorCommMode, now_ticks);
|
||||
prv_collect(ANALYTICS_DEVICE_METRIC_CPU_NOSTOP_FLASH_TIME, InhibitorFlash, now_ticks);
|
||||
prv_collect(ANALYTICS_DEVICE_METRIC_CPU_NOSTOP_I2C1_TIME, InhibitorI2C1, now_ticks);
|
||||
prv_collect(ANALYTICS_DEVICE_METRIC_CPU_NOSTOP_ACCESSORY, InhibitorAccessory, now_ticks);
|
||||
prv_collect(ANALYTICS_DEVICE_METRIC_CPU_NOSTOP_MIC, InhibitorMic, now_ticks);
|
||||
// TODO PBL-37941: Add analytics for InhibitorDMA
|
||||
}
|
||||
|
||||
void command_scheduler_force_active(void) {
|
||||
sleep_mode_enable(false);
|
||||
}
|
||||
|
||||
void command_scheduler_resume_normal(void) {
|
||||
sleep_mode_enable(true);
|
||||
}
|
||||
76
src/fw/kernel/util/stop.h
Normal file
76
src/fw/kernel/util/stop.h
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* 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>
|
||||
|
||||
typedef enum {
|
||||
InhibitorMain,
|
||||
InhibitorDbgSerial,
|
||||
InhibitorButton,
|
||||
InhibitorBluetooth,
|
||||
InhibitorDisplay,
|
||||
InhibitorBacklight,
|
||||
InhibitorCommMode,
|
||||
InhibitorFlash,
|
||||
InhibitorI2C1,
|
||||
InhibitorI2C2,
|
||||
InhibitorMic,
|
||||
InhibitorAccessory,
|
||||
InhibitorVibes,
|
||||
InhibitorCompositor,
|
||||
InhibitorI2C3,
|
||||
InhibitorI2C4,
|
||||
InhibitorBluetoothWatchdog,
|
||||
|
||||
InhibitorNumItems
|
||||
} StopModeInhibitor;
|
||||
|
||||
/** Enter stop mode.
|
||||
*
|
||||
* \note Probably no good reason to call this function from most application
|
||||
* code. Let the FreeRTOS scheduler do its job.
|
||||
*/
|
||||
void enter_stop_mode(void);
|
||||
|
||||
/** Prevent the scheduler from entering stop mode in idle. Usually called when
|
||||
* we know that there is some resource or peripheral being used that does not
|
||||
* require the use of the CPU, but that going into stop mode would interrupt.
|
||||
* \note Internally this is implemented as a reference counter, so it is
|
||||
* necessary to balance each call to disallow_stop_mode with a matching call to
|
||||
* allow_stop_mode.
|
||||
* CAUTION: This function cannot be called at priorities > Systick
|
||||
*/
|
||||
void stop_mode_disable(StopModeInhibitor inhibitor);
|
||||
|
||||
/** Allow the scheduler to enter stop mode in idle again.
|
||||
* CAUTION: This function cannot be called at priorities > Systick
|
||||
*/
|
||||
void stop_mode_enable(StopModeInhibitor inhibitor);
|
||||
|
||||
//! Check whether we are permitted to go into stop mode
|
||||
bool stop_mode_is_allowed(void);
|
||||
|
||||
|
||||
//! Enable or disable sleep mode.
|
||||
//! Note: When sleep mode is disabled so is stop mode. When sleep mode is enabled, stop mode is
|
||||
//! controlled by stop_mode_is_allowed.
|
||||
void sleep_mode_enable(bool enable);
|
||||
|
||||
//! Check whether we are permitted to go into sleep mode.
|
||||
bool sleep_mode_is_allowed(void);
|
||||
|
||||
31
src/fw/kernel/util/task_init.c
Normal file
31
src/fw/kernel/util/task_init.c
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "task_init.h"
|
||||
|
||||
#include "drivers/rng.h"
|
||||
#include "drivers/rtc.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
void task_init(void) {
|
||||
uint32_t seed;
|
||||
if (!rng_rand(&seed)) {
|
||||
// Fallback, time XOR'd with an approximation of the current stack pointer:
|
||||
seed = rtc_get_time() ^ (uintptr_t) &seed;
|
||||
}
|
||||
srand(seed);
|
||||
}
|
||||
22
src/fw/kernel/util/task_init.h
Normal file
22
src/fw/kernel/util/task_init.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
|
||||
|
||||
//! Performs initialization for the current FreeRTOS task. Currently, all it does is it
|
||||
//! attempts to seed the current task's REENT random seed with a value from the
|
||||
//! hardware random number generator.
|
||||
void task_init(void);
|
||||
76
src/fw/kernel/util/task_telemetry.c
Normal file
76
src/fw/kernel/util/task_telemetry.c
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* 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 "console/prompt.h"
|
||||
#include "kernel/pbl_malloc.h"
|
||||
|
||||
#include "FreeRTOS.h"
|
||||
#include "task.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#if 0
|
||||
void command_print_task_list(void) {
|
||||
#if ( configUSE_TRACE_FACILITY == 1 )
|
||||
char str_buffer[100];
|
||||
|
||||
prompt_send_response(
|
||||
"name state pri fstk num stk_beg stk_ptr");
|
||||
|
||||
int num_tasks = uxTaskGetNumberOfTasks();
|
||||
TaskStatus_t *task_info = kernel_malloc(num_tasks * sizeof( TaskStatus_t ));
|
||||
if (!task_info) {
|
||||
return;
|
||||
}
|
||||
num_tasks = uxTaskGetSystemState( task_info, num_tasks, NULL );
|
||||
|
||||
// Print info on each task
|
||||
char status;
|
||||
for (int i=0; i<num_tasks; i++) {
|
||||
switch(task_info[i].eCurrentState) {
|
||||
case eReady:
|
||||
status = 'R';
|
||||
break;
|
||||
case eBlocked:
|
||||
status = 'B';
|
||||
break;
|
||||
case eSuspended:
|
||||
status = 'S';
|
||||
break;
|
||||
case eDeleted:
|
||||
status = 'D';
|
||||
break;
|
||||
default:
|
||||
status = '?';
|
||||
break;
|
||||
}
|
||||
|
||||
prompt_send_response_fmt(str_buffer, sizeof(str_buffer), "%-16s %6c %8u %8u %8u %p %p",
|
||||
task_info[i].pcTaskName,
|
||||
status,
|
||||
(unsigned int) task_info[i].uxCurrentPriority,
|
||||
(unsigned int)(sizeof(StackType_t) * task_info[i].usStackHighWaterMark),
|
||||
(unsigned int)task_info[i].xTaskNumber,
|
||||
(void *)task_info[i].pxStack,
|
||||
(void *)task_info[i].pxTopOfStack);
|
||||
}
|
||||
kernel_free(task_info);
|
||||
|
||||
#else
|
||||
prompt_send_response("Not available");
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
36
src/fw/kernel/util/wfi.c
Normal file
36
src/fw/kernel/util/wfi.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 "util/attributes.h"
|
||||
|
||||
#include "wfi.h"
|
||||
|
||||
void NOINLINE NAKED_FUNC do_wfi(void) {
|
||||
// Work around a very strange bug in the STM32F where, upon waking from
|
||||
// STOP or SLEEP mode, the processor begins acting strangely depending on the
|
||||
// contents of the bytes following the "bx lr" instruction.
|
||||
__asm volatile (
|
||||
".align 4 \n" // Force 16-byte alignment
|
||||
"wfi \n" // This instruction cannot be placed at 0xnnnnnnn4
|
||||
"nop \n"
|
||||
"bx lr \n"
|
||||
"nop \n" // Fill the rest of the cache line with NOPs as the bytes
|
||||
"nop \n" // following the bx affect the processor for some reason.
|
||||
"nop \n"
|
||||
"nop \n"
|
||||
"nop \n"
|
||||
);
|
||||
}
|
||||
19
src/fw/kernel/util/wfi.h
Normal file
19
src/fw/kernel/util/wfi.h
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
void do_wfi(void);
|
||||
Reference in New Issue
Block a user