Import of the watch repository from Pebble

This commit is contained in:
Matthieu Jeanson
2024-12-12 16:43:03 -08:00
committed by Katharine Berry
commit 3b92768480
10334 changed files with 2564465 additions and 0 deletions

View File

@@ -0,0 +1,311 @@
/*
* 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/pmic.h"
#include "board/board.h"
#include "drivers/gpio.h"
#include "drivers/i2c.h"
#include "system/passert.h"
#include "stm32f4xx_rcc.h"
#include "stm32f4xx_gpio.h"
#include "stm32f4xx_adc.h"
typedef enum {
PmicRegisters_SD1_VOLTAGE = 0x01,
PmicRegisters_LDO1_VOLTAGE = 0x02,
PmicRegisters_LDO2_VOLTAGE = 0x03,
PmicRegisters_GPIO1_CNTL = 0x09,
PmicRegisters_GPIO2_CNTL = 0x0a,
PmicRegisters_GPIO3_CNTL = 0x0b,
PmicRegisters_GPIO4_CNTL = 0x0c,
PmicRegisters_GPIO5_CNTL = 0x0d,
PmicRegisters_GPIO_SIG_OUT = 0x20,
PmicRegisters_GPIO_SIG_IN = 0x21,
PmicRegisters_REG1_VOLTAGE = 0x22,
PmicRegisters_REG2_VOLTAGE = 0x23,
PmicRegisters_REG_CNTL = 0x24,
PmicRegisters_GPIO_CNTL1 = 0x25,
PmicRegisters_GPIO_CNTL2 = 0x26,
PmicRegisters_SD_CNTL1 = 0x30,
PmicRegisters_BATT_VOLTAGE_MON = 0x32,
PmicRegisters_STARTUP_CNTL = 0x33,
PmicRegisters_REFERENCE_CNTL = 0x35,
PmicRegisters_RESET_CNTL = 0x36,
PmicRegisters_OVERTEMP_CNTL = 0x37,
PmicRegisters_REG_STANDBY_MOD1 = 0x39,
PmicRegisters_PWM_CNTL_L = 0x41,
PmicRegisters_PWM_CNTL_H = 0x42,
PmicRegisters_CURR1_VAL = 0x43,
PmicRegisters_CURR2_VAL = 0x44,
PmicRegisters_REG_STATUS = 0x73,
PmicRegisters_INT_MASK_1 = 0x74,
PmicRegisters_INT_MASK_2 = 0x75,
PmicRegisters_INT_STATUS_1 = 0x77,
PmicRegisters_INT_STATUS_2 = 0x78,
PmicRegisters_CHARGE_CNTL = 0x80,
PmicRegisters_CHARGE_VOLTAGE_CNTL = 0x81,
PmicRegisters_CHARGE_CURRENT_CNTL = 0x82,
PmicRegisters_CHARGE_CONFIG_1 = 0x83,
PmicRegisters_CHARGE_CONFIG_2 = 0x84,
PmicRegisters_CHARGE_SUPERVISION = 0x85,
PmicRegisters_CHARGE_STATUS_1 = 0x86,
PmicRegisters_CHARGE_STATUS_2 = 0x87,
PmicRegisters_LOCK_REG = 0x8e,
PmicRegisters_CHIP_ID = 0x90,
PmicRegisters_CHIP_REV = 0x91,
PmicRegisters_FUSE_5 = 0xa5,
PmicRegisters_FUSE_6 = 0xa6,
PmicRegisters_FUSE_7 = 0xa7,
PmicRegisters_FUSE_8 = 0xa8,
PmicRegisters_FUSE_9 = 0xa9,
PmicRegisters_FUSE_10 = 0xaa,
PmicRegisters_FUSE_11 = 0xab,
PmicRegisters_FUSE_12 = 0xac,
PmicRegisters_FUSE_13 = 0xad,
PmicRegisters_FUSE_14 = 0xae,
PmicRegisters_FUSE_15 = 0xaf,
} PmicRegisters;
// These are values for the reset_reason field of the ResetControl register.
// None of these values should ever be changed, as conversions are done on
// readings dont directly out of the ResetControl register.
// See Figure 79 of the AS3701B datasheet for more information.
typedef enum {
PmicResetReason_PowerUpFromScratch = 0x00, //!< Battery or charger insertion from scratch
PmicResetReason_ResVoltFall = 0x01, //!< Battery voltage drop below 2.75V
PmicResetReason_ForcedReset = 0x02, //!< sw force_reset
PmicResetReason_OnPulledHigh = 0x03, //!< Force sw power_off, ON pulled high
PmicResetReason_Charger = 0x04, //!< Forced sw power_off, Charger detected.
PmicResetReason_XRES = 0x05, //!< External trigger through XRES
PmicResetReason_OverTemperature = 0x06, //!< Reset caused by overtemperature
PmicResetReason_OnKeyHold = 0x08, //!< Reset for holding down on key
PmicResetReason_StandbyInterrupt = 0x0b, //!< Reset for interrupt in standby
PmicResetReason_StandbyOnPulledHigh = 0x0c, //!< Reset for ON pulled high in standby
PmicResetReason_Unknown,
} PmicResetReason;
static void prv_start_120hz_clock(void);
static bool prv_init_gpio(void) {
return true;
}
//! Interrupt masks for InterruptStatus1 and InterruptMask1 registers
enum PmicInt1 {
PmicInt1_Trickle = (1 << 0), //!< Trickle charge
PmicInt1_NoBat = (1 << 1), //!< Battery detached
PmicInt1_Resume = (1 << 2), //!< Resuming charge on drop after full
PmicInt1_EOC = (1 << 3), //!< End of charge
PmicInt1_ChDet = (1 << 4), //!< Charger detected
PmicInt1_OnKey = (1 << 5), //!< On Key held
PmicInt1_OvTemp = (1 << 6), //!< Set when 110deg is exceeded
PmicInt1_LowBat = (1 << 7), //!< Low Battery detected. Set when BSUP drops below ResVoltFall
};
enum PmicRail {
PmicRail_SD1, //!< 1.8V
PmicRail_LDO1, //!< 3.0V
PmicRail_LDO2, //!< 2.0V
};
#define AS3701B_CHIP_ID 0x11
#define AS3701B_WRITE_ADDR 0x80
#define AS3701B_READ_ADDR 0x81
static bool prv_read_register(uint8_t register_address, uint8_t *result) {
i2c_use(I2C_DEVICE_AS3701B);
bool rv = i2c_read_register(I2C_DEVICE_AS3701B, AS3701B_READ_ADDR, register_address, result);
i2c_release(I2C_DEVICE_AS3701B);
return rv;
}
static bool prv_write_register(uint8_t register_address, uint8_t value) {
i2c_use(I2C_DEVICE_AS3701B);
bool rv = i2c_write_register(I2C_DEVICE_AS3701B, AS3701B_WRITE_ADDR, register_address, value);
i2c_release(I2C_DEVICE_AS3701B);
return rv;
}
static bool prv_register_set_bit(uint8_t register_address, uint8_t bit) {
uint8_t val;
if (!prv_read_register(register_address, &val)) {
return false;
}
val |= (1 << bit);
return prv_write_register(register_address, val);
}
static bool prv_register_clear_bit(uint8_t register_address, uint8_t bit) {
uint8_t val;
if (!prv_read_register(register_address, &val)) {
return false;
}
val &= ~(1 << bit);
return prv_write_register(register_address, val);
}
// Read the interrupt status registers to clear pending bits.
static void prv_clear_pending_interrupts(void) {
uint8_t throwaway_read_result;
prv_read_register(PmicRegisters_INT_STATUS_1, &throwaway_read_result);
prv_read_register(PmicRegisters_INT_STATUS_2, &throwaway_read_result);
}
// Set up 120Hz clock which is used for VCOM.
// Slowest possible setting, with divisor of 16 and high/low duration of 256us.
void prv_start_120hz_clock(void) {
const uint8_t pwm_high_low_time_us = (256 - 1);
prv_write_register(PmicRegisters_PWM_CNTL_H, pwm_high_low_time_us);
prv_write_register(PmicRegisters_PWM_CNTL_L, pwm_high_low_time_us);
bool success = false;
uint8_t ref_cntl;
if (prv_read_register(PmicRegisters_REFERENCE_CNTL, &ref_cntl)) {
ref_cntl |= 0x3; // Divisor of 16
prv_write_register(PmicRegisters_REFERENCE_CNTL, ref_cntl);
// Enable PWM Output on GPIO2 (Fig. 64)
// Bits 6-4: Mode, 0x1 = Output
// Bits 0-3: iosf, 0xe = PWM
uint8_t val = (1 << 4) | 0x0e;
success = prv_write_register(PmicRegisters_GPIO2_CNTL, val);
}
PBL_ASSERT(success, "Failed to start PMIC 120Hz PWM");
}
static bool prv_is_alive(void) {
uint8_t chip_id;
if (!prv_read_register(PmicRegisters_CHIP_ID, &chip_id)) {
return false;
}
const bool found = (chip_id == AS3701B_CHIP_ID);
if (found) {
PBL_LOG(LOG_LEVEL_DEBUG, "Found the as3701b");
} else {
PBL_LOG(LOG_LEVEL_DEBUG, "Error: read as3701b whoami byte 0x%x, expecting 0x11", chip_id);
}
return found;
}
bool pmic_init(void) {
prv_init_gpio();
if (!prv_is_alive()) {
return false;
}
prv_start_120hz_clock();
return true;
}
bool pmic_is_usb_connected(void) {
uint8_t status;
if (!prv_read_register(PmicRegisters_CHARGE_STATUS_2, &status)) {
return false;
}
// ChargerStatus2 (Fig. 98)
// Bit 2: Charger detected
return !!(status & (1 << 2));
}
static PmicResetReason prv_reset_reason(void) {
uint8_t val;
if (!prv_read_register(PmicRegisters_RESET_CNTL, &val)) {
return PmicResetReason_Unknown;
}
return (val & 0xf0) >> 4;
}
// If the pmic indicates that we were reset due to a charger interrupt, but the
// charger is currently disconnected, then we know we were woken by a disconnect event.
bool pmic_boot_due_to_charger_disconnect(void) {
if (prv_reset_reason() != PmicResetReason_StandbyInterrupt) {
return false;
}
uint8_t int_status;
if (!prv_read_register(PmicRegisters_INT_STATUS_1, &int_status)) {
return false;
}
return ((int_status & PmicInt1_ChDet) && !pmic_is_usb_connected());
}
// This is a hard power off, resulting in all rails being disabled.
bool pmic_full_power_off(void) {
// ResetControl (Fig. 79)
// Bit 1: power_off - Start a reset cycle, and wait for ON or charger to complete the reset.
if (prv_register_set_bit(PmicRegisters_RESET_CNTL, 1)) {
while (1) {}
__builtin_unreachable();
}
return false;
}
// On the as3701b, a power_off will cut power to all rails. We want to keep the
// RTC alive, so rather than performing a sw_power_off, enter the pmic's standby
// mode, powering down all but LDO2.
bool pmic_power_off(void) {
// Only enable interrupts that should be able to wake us out of standby
// - Wake on charger detect
const uint8_t int_mask = ~((uint8_t)PmicInt1_ChDet);
prv_write_register(PmicRegisters_INT_MASK_1, int_mask);
prv_write_register(PmicRegisters_INT_MASK_2, 0xff);
// Clear interrupt status so we're not woken immediately (read the regs)
prv_clear_pending_interrupts();
// Set Reg_Standby_mod1 to specify which rails to turn off / keep on
// - SD1, LDO1 off
// - LDO2 on
// - Disable regulator pulldowns
prv_write_register(PmicRegisters_REG_STANDBY_MOD1, 0xa);
// Set standby_mode_on (bit 4) in ReferenceControl to 1 (See Fig. 78)
if (prv_register_set_bit(PmicRegisters_REFERENCE_CNTL, 4)) {
while (1) {}
__builtin_unreachable();
}
return false;
}
void set_ldo3_power_state(bool enabled) {
}
void set_4V5_power_state(bool enabled) {
}
void set_6V6_power_state(bool enabled) {
}