iBoot/drivers/dialog/pmu/power.c

1935 lines
56 KiB
C

/*
* Copyright (C) 2008-2013 Apple Inc. All rights reserved.
*
* This document is the property of Apple Inc.
* It is considered confidential and proprietary.
*
* This document may not be reproduced or transmitted in any form,
* in whole or in part, without the express written permission of
* Apple Inc.
*/
#include <debug.h>
#include <drivers/iic.h>
#include <drivers/power.h>
#include <drivers/usb/usb_public.h>
#include <drivers/gasgauge.h>
#include <drivers/charger.h>
#include <drivers/tristar.h>
#include <lib/paint.h>
#include <lib/syscfg.h>
#include <platform.h>
#include <platform/gpiodef.h>
#include <platform/miu.h>
#include <target.h>
#include <sys.h>
#include <sys/boot.h>
#include <sys/menu.h>
#include <sys/task.h>
#if DIALOG_D1755
#include "d1755.h"
#elif DIALOG_D1815
#include "d1815.h"
#elif DIALOG_D1881
#include "d1881.h"
#elif DIALOG_D1946
#include "d1946.h"
#elif DIALOG_D1972
#include "d1972.h"
#elif DIALOG_D1974
#include "d1974.h"
#elif DIALOG_D2018
#include "d2018.h"
#elif DIALOG_D2045
#include "d2045.h"
#elif DIALOG_D2089
#include "d2089.h"
#elif DIALOG_D2186
#include "d2186.h"
#elif DIALOG_D2207
#include "d2207.h"
#elif DIALOG_D2238
#include "d2238.h"
#elif DIALOG_D2255
#include "d2255.h"
#elif DIALOG_D2257
#include "d2257.h"
#else
#error Unknown Dialog PMU selection
#endif
#include "NTCTables.h"
struct pmu_setup_struct
{
int pmu;
uint16_t reg;
uint16_t value;
};
struct core_rails_struct
{
int pmu;
uint16_t ctrl_reg;
uint16_t data_reg;
};
struct power_rail_s {
uint8_t index;
uint16_t min_mv;
uint16_t max_mv;
uint16_t step_uv; // 3125=3.125 mV
};
typedef struct power_rail_s target_rails_t[POWER_RAIL_COUNT];
// must include this after the above declarations
#define POWERCONFIG_PMU_SETUP 1
#include <target/powerconfig.h>
static uint8_t boot_flags;
static eventRegisters cold_boot_ints;
static bool cold_boot_ints_valid;
// This goes after the powerconfig
#ifndef MAX_BACKLIGHT_LEVEL
#define MAX_BACKLIGHT_LEVEL ((1 << (WLED_ISET_BITS)) - 1)
#endif
#ifndef TARGET_BRICKID_FULL_SCALE
#define TARGET_BRICKID_FULL_SCALE (5000)
#endif
// define PMU_DATA_DEBUG_SPEW as DEBUG_CRITICAL to enable logging
#define PMU_DATA_DEBUG_SPEW DEBUG_SPEW
int pmu_set_data(int dev, uint16_t reg, uint8_t byte, bool do_confirm)
{
uint8_t confirm;
UInt8 data[kDIALOG_REG_BYTES + 1];
if (kDIALOG_REG_BYTES > 1) data[kDIALOG_REG_BYTES - 2] = (reg >> 8) & 0xFF;
data[kDIALOG_REG_BYTES - 1] = (reg & 0xFF);
data[kDIALOG_REG_BYTES] = byte;
int rc=iic_write(dev, kDIALOG_ADDR_W, data, sizeof(data));
if ( rc!=0 ) {
dprintf(DEBUG_CRITICAL, "pmu%d: wrote %x to reg %x failed (%d)\n", dev, byte, reg, rc);
} else if (do_confirm) {
iic_read(dev, kDIALOG_ADDR_R, data, sizeof(data)-1, &confirm, 1, IIC_NORMAL);
if (byte == confirm) {
dprintf(PMU_DATA_DEBUG_SPEW, "pmu%d: wrote %x to reg %x\n", dev, byte, reg);
} else {
dprintf(PMU_DATA_DEBUG_SPEW, "pmu%d: try to write %x to reg %x, but got %x back\n", dev, byte, reg, confirm);
return -1;
}
}
return 0;
}
int pmu_get_data(int dev, uint16_t reg, uint8_t *byte)
{
UInt8 addr[kDIALOG_REG_BYTES];
if (kDIALOG_REG_BYTES > 1) addr[kDIALOG_REG_BYTES - 2] = (reg >> 8) & 0xFF;
addr[kDIALOG_REG_BYTES - 1] = (reg & 0xFF);
const int rc=iic_read(dev, kDIALOG_ADDR_R, addr, sizeof(addr), byte, 1, IIC_NORMAL);
dprintf(PMU_DATA_DEBUG_SPEW, "pmu%d: read %x from reg %x\n", dev, *byte, reg);
return rc;
}
void pmu_read_events(eventRegisters data)
{
bzero(data, sizeof(eventRegisters));
UInt8 addr[kDIALOG_REG_BYTES];
if (kDIALOG_REG_BYTES > 1) addr[kDIALOG_REG_BYTES - 2] = (kDIALOG_EVENT_A >> 8) & 0xFF;
addr[kDIALOG_REG_BYTES - 1] = (kDIALOG_EVENT_A & 0xFF);
iic_read(PMU_IIC_BUS, kDIALOG_ADDR_R, addr, sizeof(addr), data, sizeof(eventRegisters), IIC_NORMAL);
}
static void pmu_read_status(statusRegisters data)
{
bzero(data, sizeof(statusRegisters));
UInt8 addr[kDIALOG_REG_BYTES];
if (kDIALOG_REG_BYTES > 1) addr[kDIALOG_REG_BYTES - 2] = (kDIALOG_STATUS_A >> 8) & 0xFF;
addr[kDIALOG_REG_BYTES - 1] = (kDIALOG_STATUS_A & 0xFF);
iic_read(PMU_IIC_BUS, kDIALOG_ADDR_R, addr, sizeof(addr), data, sizeof(statusRegisters), IIC_NORMAL);
}
static void pmu_write_mask(const eventRegisters data)
{
uint8_t bytes[sizeof(eventRegisters) + kDIALOG_REG_BYTES];
if (kDIALOG_REG_BYTES > 1) bytes[kDIALOG_REG_BYTES - 2] = (kDIALOG_IRQ_MASK_A >> 8) & 0xFF;
bytes[kDIALOG_REG_BYTES - 1] = (kDIALOG_IRQ_MASK_A & 0xFF);
memcpy(bytes+kDIALOG_REG_BYTES, data, sizeof(eventRegisters));
iic_write(PMU_IIC_BUS, kDIALOG_ADDR_W, bytes, sizeof(bytes));
}
static int pmu_read_nvram(uint16_t addr, void *buffer, uint8_t count)
{
uint8_t bytes[kDIALOG_REG_BYTES];
if (kDIALOG_REG_BYTES > 1) bytes[kDIALOG_REG_BYTES - 2] = (addr>>8) & 0xFF;
bytes[kDIALOG_REG_BYTES - 1] = (addr & 0xFF);
return iic_read(PMU_IIC_BUS, kDIALOG_ADDR_R, bytes, sizeof(bytes), buffer, count, IIC_NORMAL);
}
static int pmu_write_nvram(uint16_t addr, void *buffer, uint8_t count)
{
uint8_t bytes[kDIALOG_REG_BYTES+count];
if (kDIALOG_REG_BYTES > 1) bytes[kDIALOG_REG_BYTES - 2] = (addr>>8) & 0xFF;
bytes[kDIALOG_REG_BYTES - 1] = (addr & 0xFF);
memcpy(bytes+kDIALOG_REG_BYTES, buffer, count);
return iic_write(PMU_IIC_BUS, kDIALOG_ADDR_W, bytes, kDIALOG_REG_BYTES + count);
}
static int
power_read_memory_calibration_data(uint16_t addr, void *buffer, uint32_t length)
{
for (uint32_t size=0, count=0; length-count ; count+=size) {
size=length-count;
if ( size>64 ) size=64;
const bool success=( pmu_read_nvram(addr+count, ((char*)buffer)+count, size) == 0 );
if ( !success ) return 1;
}
return 0;
}
static int
power_write_memory_calibration_data(uint16_t addr, void *buffer, uint32_t length)
{
for (uint32_t size=0, count=0; length-count ; count+=size) {
size=length-count;
if ( size>64 ) size=64;
const bool success=( pmu_write_nvram(addr+count, ((char*)buffer)+count, size) == 0 );
if ( !success ) return 1;
}
return 0;
}
// @pre settings is kDIALOG_EXT_MEM_CAL_SIZE byes
bool
power_load_memory_calibration(void *settings, uint32_t length) {
#if PMU_HAS_RAM && !DIALOG_D2238
uint32_t count=0, size;
if ((!settings) || (length > kDIALOG_EXT_MEM_CAL_SIZE)) return false;
bzero(settings, length);
size=( length>kDIALOG_EXT_MEM_CAL0_SIZE ) ? kDIALOG_EXT_MEM_CAL0_SIZE : length ;
if ( pmu_read_nvram(kDIALOG_EXT_MEM_CAL0, &settings[count], size)!=0 ) return false;
length-=size; count+=size;
if ( length ) {
size= ( length>kDIALOG_EXT_MEM_CAL1_SIZE ) ? kDIALOG_EXT_MEM_CAL1_SIZE : length ;
if ( power_read_memory_calibration_data(kDIALOG_EXT_MEM_CAL1, &settings[count], size)!=0 ) return false;
length-=size; count+=size;
}
#if DIALOG_D2257
if ( length ) {
size= ( length>kDIALOG_EXT_MEM_CAL2_SIZE ) ? kDIALOG_EXT_MEM_CAL2_SIZE : length ;
if ( power_read_memory_calibration_data(kDIALOG_EXT_MEM_CAL2, &settings[count], size)!=0 ) return false;
length-=size; count+=size;
}
#endif
if ( length ) dprintf(DEBUG_SPEW, "WARN: power_load_memory_calibration remainder=%d\n", length);
return true;
#endif
return false;
}
bool
power_store_memory_calibration(void *settings, uint32_t length) {
#if PMU_HAS_RAM && !DIALOG_D2238
uint32_t count=0, size;
if ((!settings) || (length > kDIALOG_EXT_MEM_CAL_SIZE)) return false;
size=(length>kDIALOG_EXT_MEM_CAL0_SIZE)?kDIALOG_EXT_MEM_CAL0_SIZE:length;
if ( power_write_memory_calibration_data(kDIALOG_EXT_MEM_CAL0, &settings[count], size)!=0 ) return false;
length-=size; count+=size;
if ( length ) {
size=(length>kDIALOG_EXT_MEM_CAL1_SIZE)?kDIALOG_EXT_MEM_CAL1_SIZE:length;
if ( power_write_memory_calibration_data(kDIALOG_EXT_MEM_CAL1, &settings[count], size)!=0 ) return false;
length-=size; count+=size;
}
#if DIALOG_D2257
if ( length ) {
size=(length>kDIALOG_EXT_MEM_CAL2_SIZE)?kDIALOG_EXT_MEM_CAL2_SIZE:length;
if ( power_write_memory_calibration_data(kDIALOG_EXT_MEM_CAL2, &settings[count], size)!=0 ) return false;
length-=size; count+=size;
}
#endif
if ( length ) dprintf(DEBUG_SPEW, "WARN: power_store_memory_calibration remainder=%d\n", length);
return true;
#endif
return false;
}
// @pre settings is kDIALOG_VOLTAGE_KNOBS_SIZE bytes
bool
power_load_voltage_knobs(void *settings, uint32_t length) {
bool success=false;
#if WITH_VOLTAGE_KNOBS
if ((!settings) || (length != kDIALOG_VOLTAGE_KNOBS_SIZE)) return false;
bzero(settings, length);
success=( 0 == pmu_read_nvram(kDIALOG_VOLTAGE_KNOBS, settings, length) );
#endif
return success;
}
bool
power_store_voltage_knobs(void *settings, uint32_t length) {
bool success=false;
#if WITH_VOLTAGE_KNOBS
if ((!settings) || (length != kDIALOG_VOLTAGE_KNOBS_SIZE)) return false;
success=(0 == pmu_write_nvram(kDIALOG_VOLTAGE_KNOBS, settings, length));
#endif
return success;
}
#if DIALOG_D2255 || DIALOG_D2257
static int
dialog_read_adc(int dev, unsigned input_select, unsigned *level)
{
int result;
const uint16_t man_ctrl_addr=kDIALOG_ADC_MAN_CTL;
const uint8_t channel_index=input_select & kDIALOG_ADC_CONTROL_MUX_SEL_MASK;
result=pmu_set_data(dev, man_ctrl_addr, channel_index, 0);
if ( result!=0 ) return -1;
int done=0;
uint8_t data[2]; // MAN*_RES_LSB, MAN*_RES_MSB
uint8_t addr[2] = { (kDIALOG_ADC_LSB >> 8) & 0xFF, (kDIALOG_ADC_LSB & 0xFF)};
const utime_t end_time = system_time() + 50*1000 ;
do {
spin(1000);
result=iic_read(PMU_IIC_BUS, kDIALOG_ADDR_R, addr, sizeof(addr), &data[0], sizeof(data), IIC_NORMAL);
if ( result!=0 ) break;
done=( (data[0]&kDIALOG_ADC_LSB_MANADC_ERROR)==0 );
if ( done ) {
*level = ((unsigned)data[1] << 4) | (data[0] & 0xf);
} else if ( system_time() > end_time ) {
result=-1;
dprintf(DEBUG_CRITICAL, "dialog_read_adc timeout, MUX_SEL=%x\n", input_select);
break;
}
} while ( !done );
#if DIALOG_D2255
// Antigua130 inverts the ADC bits for these channels
if (input_select == kD2255_ADC_CONTROL_MUX_SEL_VDD_MAIN ||
input_select == kD2255_ADC_CONTROL_MUX_SEL_BRICK_ID ||
input_select == kD2255_ADC_CONTROL_MUX_SEL_APP_MUX_A ||
input_select == kD2255_ADC_CONTROL_MUX_SEL_APP_MUX_B) {
// check for 2265 part (Antigua130)
uint8_t antigua130;
uint8_t addr[2] = { (kD2265_IMPLEMENTATION >> 8) & 0xFF, (kD2265_IMPLEMENTATION & 0xFF)};
result=iic_read(PMU_IIC_BUS, kDIALOG_ADDR_R, addr, sizeof(addr), &antigua130, 1, IIC_NORMAL);
if ( (result == 0) && (antigua130 == kD2265_130NM) ) {
*level = ~(*level) & ((1 << kDIALOG_ADC_RESOLUTION_BITS) - 1);
}
}
dprintf (DEBUG_SPEW, "kD2255_ADC_CONTROL_(%d): 2265 bit inverting %#x -> %#x\n", input_select, ((unsigned)data[1] << 4) | (data[0] & 0xf), *level);
#endif
// clear EOC
eventRegisters ints_pending;
pmu_read_events(ints_pending);
return result;
}
#else
static int
dialog_read_adc(int dev, unsigned input_select, unsigned *level)
{
int result = 0;
uint8_t data[2];
uint8_t reg;
utime_t start_time;
int timeoutOccurred;
#if ADC_TIMEOUT_STATISTICS
int chg_dis=0;
#endif
#if ADC_TIMEOUT_WORKAROUND
int numTimeouts=0;
#endif
reg = (input_select & kDIALOG_ADC_CONTROL_MUX_SEL_MASK) | kDIALOG_ADC_CONTROL_DEFAULTS;
#if !DIALOG_D2238
if (kDIALOG_ADC_CONTROL_MUX_SEL_ACC_ID == input_select) {
#if DIALOG_D2045 || DIALOG_D2089 || DIALOG_D2186 || DIALOG_D2207 || DIALOG_D2238
pmu_set_data(dev, kDIALOG_ADC_CONTROL2, kDIALOG_ADC_CONTROL2_ADC_REF_EN, 0);
#else
pmu_set_data(dev, kDIALOG_ADC_CONTROL, kDIALOG_ADC_CONTROL_ADC_REF_EN | reg, 0);
reg |= kDIALOG_ADC_CONTROL_ADC_REF_EN;
#endif
spin(80 * 1000);
}
#endif
pmu_set_data(dev, kDIALOG_ADC_CONTROL, kDIALOG_ADC_CONTROL_MAN_CONV | reg, 0);
#if ADC_TIMEOUT_WORKAROUND
continue_conv:
#endif
start_time = system_time();
timeoutOccurred = 0;
do {
spin(1000);
if (system_time() > (start_time + 50*1000))
{
dprintf(DEBUG_CRITICAL,
"dialog_read_adc timeout, MUX_SEL=%x\n",
input_select);
#if ADC_TIMEOUT_WORKAROUND
timeoutOccurred = 1;
break;
#else
return -1;
#endif
}
pmu_get_data(dev, kDIALOG_ADC_CONTROL, &reg);
} while ((reg & kDIALOG_ADC_CONTROL_MAN_CONV) != 0);
#if ADC_TIMEOUT_WORKAROUND
enum adc_issues {
kRetriesDidntHelp = 0x01,
kAutoConversionVDD = 0x02,
kAutoConversionTemp = 0x04,
kBatPwrSuspNotAsserted = 0x08,
kChrgDisabled = 0x10
};
if(timeoutOccurred) {
uint8_t issues = 0;
#if ADC_TIMEOUT_STATISTICS
// Write timeout count to scratchpad
if(numTimeouts==0) {
pmu_get_data(dev, kDIALOG_MEMBYTE0+0x09, &data[0]);
if(data[0]<255) data[0]++;
pmu_set_data(dev, kDIALOG_MEMBYTE0+0x09, data[0], 0);
}
// Check that auto conversions are disabled
uint8_t adc_regs[5];
reg = kDIALOG_ADC_CONTROL;
iic_read(PMU_IIC_BUS, kDIALOG_ADDR_R, &reg, 1, &adc_regs[0],
sizeof(adc_regs), IIC_NORMAL);
if(adc_regs[0] & kDIALOG_ADC_CONTROL_AUTO_VDD_OUT_EN)
issues |= kAutoConversionVDD;
if(adc_regs[4] & 0x80) issues |= kAutoConversionTemp;
// Check that we're charging. Not treating this as critical yet,
// but if we're not charging this might be a reason the workaround
// could fail
pmu_get_data(dev, kDIALOG_STATUS_A, &data[0]);
if((data[0]&kD1815_STATUS_A_VBUS_DET)==0) chg_dis |= 1;
// Check that bat_pwr_susp is enabled
pmu_get_data(dev, kDIALOG_SYS_CONTROL, &data[0]);
if((data[0]&kDIALOG_SYS_CONTROL_BAT_PWR_SUSPEND)==0)
issues |= kBatPwrSuspNotAsserted;
#endif
numTimeouts++;
if(numTimeouts > 10) issues |= kRetriesDidntHelp;
// Trouble? Give up
if(issues) {
#if ADC_TIMEOUT_STATISTICS
// Charging was disabled anytime we tried the workaround
if(chg_dis) issues |= kChrgDisabled;
// Write issues to scratchpad
pmu_set_data(dev, kDIALOG_MEMBYTE0+0x8, issues, 0);
// Write reset count to scratchpad
pmu_get_data(dev, kDIALOG_MEMBYTE0+0x0a, &data[0]);
if(data[0]<255) data[0]++;
pmu_set_data(dev, kDIALOG_MEMBYTE0+0x0a, data[0], 0);
#endif
// PMU register reset
pmu_get_data(dev, 0xE0, &data[0]);
data[0]|=1; // test_enable
pmu_set_data(dev, 0xE0, data[0], 0);
data[0]|=3; // test_enable|SW_RESET
pmu_set_data(dev, 0xE0, data[0], 0);
// NOTREACHED
while(1);
} else {
pmu_get_data(dev, kDIALOG_SYS_CONTROL, &data[0]);
// Clear BAT_PWR_SUSP
data[0] &= ~kDIALOG_SYS_CONTROL_BAT_PWR_SUSPEND;
pmu_set_data(dev, kDIALOG_SYS_CONTROL, data[0], 0);
// Wait
spin(1300);
// Set BAT_PWR_SUSP
data[0] |= kDIALOG_SYS_CONTROL_BAT_PWR_SUSPEND;
pmu_set_data(dev, kDIALOG_SYS_CONTROL, data[0], 0);
// Try again!
goto continue_conv;
}
}
#endif /* ADC_TIMEOUT_WORKAROUND */
UInt8 addr[kDIALOG_REG_BYTES];
if (kDIALOG_REG_BYTES > 1) addr[kDIALOG_REG_BYTES - 2] = (kDIALOG_ADC_LSB >> 8) & 0xFF;
addr[kDIALOG_REG_BYTES - 1] = (kDIALOG_ADC_LSB & 0xFF);
iic_read(PMU_IIC_BUS, kDIALOG_ADDR_R, addr, sizeof(addr), &data[0], sizeof(data), IIC_NORMAL);
pmu_set_data(dev, kDIALOG_ADC_CONTROL, kDIALOG_ADC_CONTROL_DEFAULTS, 0);
#if DIALOG_D2045 || DIALOG_D2089 || DIALOG_D2186 || DIALOG_D2207 || DIALOG_D2238
pmu_set_data(dev, kDIALOG_ADC_CONTROL2, kDIALOG_ADC_CONTROL2_DEFAULTS, 0);
#endif
#if DIALOG_D1755
*level = ((unsigned)data[1] << 2) | (data[0] & 0x3);
#else
*level = ((unsigned)data[1] << 4) | (data[0] & 0xf);
#endif
return result;
}
#endif
int
pmu_uvwarn_config(int dev, uint32_t thresholdMV)
{
int rc=-1; // not supported
#if DIALOG_D2257
rc=pmu_set_data(dev, kDIALOG_TEST_ACCESS, kDIALOG_TEST_ACCESS_ENA, false);
if ( rc<0 ) {
} else {
uint8_t uv_dig_conf;
rc=pmu_get_data(dev, kD2257_IPK_UV_BUCK1_UV_DIG_CONF_1, &uv_dig_conf);
if ( rc<0 ) {
dprintf(DEBUG_CRITICAL, "pmu: cannot read UV_CONF (%d)\n", rc);
} else {
if ( thresholdMV!=0 ) {
u_int32_t uv_en_thr;
rc=power_get_rail_value(POWER_RAIL_GPU, thresholdMV, &uv_en_thr);
if ( rc==0 ) rc=pmu_set_data(dev, kD2257_IPK_UV_BUCK1_UV_EN_THR, uv_en_thr, true);
uv_dig_conf|=1<<0; // UV_EN
} else {
uv_dig_conf&=0xfe; // clear UV_EN
}
if ( rc==0 ) rc=pmu_set_data(dev, kD2257_IPK_UV_BUCK1_UV_DIG_CONF_1, uv_dig_conf, true);
}
int temp=pmu_set_data(dev, kDIALOG_TEST_ACCESS, kDIALOG_TEST_ACCESS_DIS, false);
if ( temp<0 ) panic("pmu: cannot exit test mode\n");
}
#endif
return rc;
}
static int
dialog_read_battery_level(int dev, unsigned *level)
{
int result;
#if DIALOG_D2255
// PFIXME: D2255 need to use charger_read_battery_level()
result = 0;
#else
unsigned adc = 0;
result = dialog_read_adc(dev, kDIALOG_ADC_CONTROL_MUX_SEL_VBAT, &adc);
#if DIALOG_D2045 || DIALOG_D2089 || DIALOG_D2186 || DIALOG_D2207 || DIALOG_D2238 || DIALOG_D2231
*level = ((adc * 2500) >> kDIALOG_ADC_RESOLUTION_BITS) + 2500;
#else
*level = ((adc * 2000) >> kDIALOG_ADC_RESOLUTION_BITS) + 2500;
#endif
#endif
return result;
}
static int lut_interpolate(const struct NTCLUT * const LUT, const uint32_t R)
{
int idx=0;
uint32_t upper, lower, R1, R2;
int32_t T1, T2;
do {
upper = LUT[idx].ohm;
lower = LUT[idx+1].ohm;
// Is our lookup value within the range of the two table entries?
// This is also true if the lookup value is outside the table
// range; we just interpolate from the last table entries.
if((R<=lower) && (LUT[idx+2].ohm != 0)) {
idx++;
continue;
}
// Subtract the lower limit, so lower is the zero offset for interpolation
R1 = upper-lower;
R2 = R - lower;
T1 = LUT[idx].centiCelsius - LUT[idx+1].centiCelsius;
// The interpolation.
// (T1/T2) = (R1/R2) <=> T2 = ( (T1*R2) / R1 )
T2 = ( (T1*(int32_t)R2) / (int32_t)R1 );
// Add back the lower limit to our result.
T2 += LUT[idx+1].centiCelsius;
dprintf(DEBUG_SPEW, "LUTInterpolate: R=%d, R1=%d, R2=%d, T1=%d, T2=%d\n",
R, R1, R2, T1, T2);
break;
} while(1);
return T2;
}
static int
dialog_read_battery_temperature(int *level)
{
int result;
#if DIALOG_D2255
// PFIXME: D2255 need to use charger_read_battery_temperature()
result=-1;
#else
unsigned adc = 0;
if (level == NULL) return -1;
result = dialog_read_adc(PMU_IIC_BUS, kDIALOG_ADC_CONTROL_MUX_SEL_TBAT, &adc);
if (result == 0) {
// The NTC is connected to a 50uA current source
// R = U / I; 1/I = 1/50uA = 20000 * 1/A
// U = <adc_val> * <adc_full_scale_range> / <adc_resolution>
// adc_fsr comes in mV, take the factor of 1000 out of current constant
const uint32_t Rntc = (adc * (20000/1000) * kDIALOG_ADC_FULL_SCALE_MV) >> kDIALOG_ADC_RESOLUTION_BITS;
dprintf(DEBUG_SPEW, "ADC chan %d: value=%d, Rntc=%d\n", kDIALOG_ADC_CONTROL_MUX_SEL_TBAT, adc, Rntc);
*level=lut_interpolate(NTCG103JF103F, Rntc);
}
#endif
return result;
}
static int
dialog_use_setup(const struct pmu_setup_struct *setup, uint32_t count)
{
uint32_t i;
/* set pmu registers from the list */
for (i = 0; i < count; i++) {
pmu_set_data(setup[i].pmu, setup[i].reg, setup[i].value, true);
}
return 0;
}
// assume that the offset of the lock register from the VSEL register is fixed
static int
dialog_lock_buck_vsel(int dev, uint16_t buck_vsel)
{
#if PMU_HAS_VSEL_LOCK
const uint16_t buck_vsel_lock=buck_vsel+kDIALOG_BUCK_VSEL_LOCK_OFFSET;
//dprintf(DEBUG_CRITICAL, " pmu: locking vsel_lock_addr=%x\n", buck_vsel_lock);
return pmu_set_data(dev, buck_vsel_lock, kDIALOG_BUCK_VSEL_LOCK_EN, true);
#else
return 0;
#endif
}
u_int32_t
pmu_read_brick_id_level(void)
{
#if !DIALOG_D2238
uint32_t adc = 0;
if (dialog_read_adc(PMU_IIC_BUS, kDIALOG_ADC_CONTROL_MUX_SEL_BRICK_ID, &adc))
return 0;
// Scale to a 1mV resolution - 5000mV is full scale
return (adc * TARGET_BRICKID_FULL_SCALE) >> kDIALOG_ADC_RESOLUTION_BITS;
#else
return 0;
#endif
}
int pmu_read_system_temperature(int idx, int *centiCelsiusTemperature)
{
int result;
unsigned adc = 0;
result = dialog_read_adc(PMU_IIC_BUS, kDIALOG_ADC_CONTROL_MUX_SEL_NTC0 + idx, &adc);
if (result == 0) {
// The NTC is connected to a 50uA current source
// R = U / I; 1/I = 1/50uA = 20000 * 1/A
// U = <adc_val> * <adc_full_scale_range> / <adc_resolution>
// adc_fsr comes in mV, take the factor of 1000 out of current constant
const uint32_t Rntc = (adc * (20000/1000) * kDIALOG_ADC_FULL_SCALE_MV) >> kDIALOG_ADC_RESOLUTION_BITS;
dprintf(DEBUG_SPEW, "ADC chan %d: value=%d, Rntc=%d\n", kDIALOG_ADC_CONTROL_MUX_SEL_NTC0, adc, Rntc);
*centiCelsiusTemperature=lut_interpolate(NTCG103JF103F, Rntc);
}
return result;
}
/* pmu_early_init() is only called in iBSS and LLB - not in iBoot or iBEC. */
void
pmu_early_init(void)
{
int rc;
u_int8_t data;
/* Get and clear the boot flags, ie. kDIALOG_EVENT_B_HIB. */
/* Events in this register are lost over sleep/wake, so they're stored for the OS later */
rc=pmu_get_data(PMU_IIC_BUS, kDIALOG_EVENT_A + EVENT_FLAG_GET_BYTE(kDIALOG_EVENT_HIB_MASK), &boot_flags);
if ( rc!=0 ) dprintf(DEBUG_CRITICAL, "pmu%d: cannot read HIB_MASK (%d)\n", PMU_IIC_BUS, rc);
/* Set all the warm boot defaults */
dialog_use_setup(pmu_warm_init, sizeof(pmu_warm_init)/sizeof(pmu_warm_init[0]));
if (!power_is_suspended()) {
dialog_use_setup(pmu_cold_init, sizeof(pmu_cold_init)/sizeof(pmu_cold_init[0]));
#if PMU_COLD_INIT_AP_DEV
if (target_config_dev()) {
dialog_use_setup(pmu_cold_init_dev, sizeof(pmu_cold_init_dev)/sizeof(pmu_cold_init_dev[0]));
} else {
dialog_use_setup(pmu_cold_init_ap, sizeof(pmu_cold_init_ap)/sizeof(pmu_cold_init_ap[0]));
}
#endif
#if DIALOG_D1946
/* <rdar://problem/11006711> Force sync mode on P105 proto 1 only */
pmu_get_data(PMU_IIC_BUS, kDIALOG_CHIP_ID, &data);
if (data == 0x45) {
pmu_set_data(PMU_IIC_BUS, kDIALOG_BANKSEL, 1, true); // Bank 1
pmu_set_data(PMU_IIC_BUS, kD1946_BUCK_CONTROL1, 0x0e, true); // Buck0 sync mode, 1250mA limit per phase
pmu_set_data(PMU_IIC_BUS, kD1946_BUCK_CONTROL3, 0x0e, true); // Buck2 sync mode, 1250mA limit per phase
pmu_set_data(PMU_IIC_BUS, kDIALOG_BANKSEL, 0, true);
}
#endif
#if DIALOG_D1881
// <rdar://problem/10420919> Update PMU settings to support PRQs
pmu_get_data(PMU_IIC_BUS, kDIALOG_CHIP_ID, &data);
data &= kD1881_CHIP_ID_MRC_MASK;
// Angelina prior to Revision B1 requires adjustment to OTP buck parameters
// <rdar://problem/9892023> N94 Force PWM for buck 0/2 in iboot
if (data < kD1881_CHIP_ID_MRC_B1) {
pmu_set_data(PMU_IIC_BUS, kDIALOG_BUCK_CONTROL1, 0x0a, true); // buck0 sync mode, 1.25A limit per phase
pmu_set_data(PMU_IIC_BUS, kDIALOG_BUCK_CONTROL3, 0x0e, true); // buck2 sync mode, 1.55A limit per phase
}
#endif
/* Make sure we don't hit the interrupt-squashing
* behavior. See <rdar://problem/8167729> */
pmu_get_data(PMU_IIC_BUS, kDIALOG_EVENT_A + kDIALOG_EVENT_COUNT - 1, &data);
#if DIALOG_D1881
// <rdar://problem/9005339>
pmu_set_data(PMU_IIC_BUS, kDIALOG_BANKSEL, 1, true);
pmu_get_data(PMU_IIC_BUS, 0xAD, &data);
if (data & 0x60) {
pmu_set_data(PMU_IIC_BUS, kD1881_TEST_MODE, 1, true); // Enable test mode
pmu_set_data(PMU_IIC_BUS, 0xE4, 8, true);
pmu_set_data(PMU_IIC_BUS, 0xAD, data & ~0x60, true); // Clear softstart
pmu_set_data(PMU_IIC_BUS, 0xE4, 0, true);
pmu_set_data(PMU_IIC_BUS, kD1881_TEST_MODE, 0, true); // Disable test mode
}
pmu_set_data(PMU_IIC_BUS, kDIALOG_BANKSEL, 0, true);
#endif
#if DIALOG_D2018
// <rdar://problem/10608368> P102: WLED boost frequency incorrectly set to 2MHz in the initial A0 samples
pmu_get_data(PMU_IIC_BUS, kDIALOG_CHIP_ID, &data);
if (data == 0x20) {
dialog_use_setup(pmu_cold_init_a0_samples, sizeof(pmu_cold_init_a0_samples)/sizeof(pmu_cold_init_a0_samples[0]));
}
#endif
#if DIALOG_D1972
// <rdar://problem/11661786> N41 Test iBoot - VDD_SRAM DVC down-ramp
pmu_get_data(PMU_IIC_BUS, kDIALOG_CHIP_ID, &data);
if (data == 0x34) {
pmu_set_data(PMU_IIC_BUS, kD1972_TEST_MODE, 0x1d, true); // Enable test mode
pmu_set_data(PMU_IIC_BUS, kDIALOG_BANKSEL, 0x01, true); // Select bank1
pmu_set_data(PMU_IIC_BUS, 0xb2, 0xf1, true); // Change LDO16 soak field
pmu_set_data(PMU_IIC_BUS, 0xb3, 0x2f, true); // Change LDO16 pull down strength to 150mA
pmu_set_data(PMU_IIC_BUS, kDIALOG_BANKSEL, 0x00, true); // Select bank0
pmu_set_data(PMU_IIC_BUS, kD1972_TEST_MODE, 0x00, true); // Disable test mode
}
#endif /* DIALOG_D1972 */
#if DIALOG_D2257
// <rdar://problem/19688297> Re: Aria A0 - Change OTP settings of PMU for better buck response
data=0xff;
pmu_get_data(PMU_IIC_BUS, kDIALOG_CHIP_ID, &data);
if ( data==0x00 ) {
uint8_t plat_id=0xff;
pmu_get_data(PMU_IIC_BUS, kDIALOG_PLATFORM_ID, &plat_id);
if ( plat_id==0x01 || plat_id==0x02 ) {
data=0xff;
pmu_get_data(PMU_IIC_BUS, kDIALOG_TRIM_REL_CODE, &data);
if ( data==0x02 ) {
int rc;
rc=pmu_set_data(PMU_IIC_BUS, kDIALOG_TEST_ACCESS, kDIALOG_TEST_ACCESS_ENA, false);
if ( rc!=0 ) {
dprintf(DEBUG_CRITICAL, "D2257: cannot enter PMU test mode\n");
} else {
rc=pmu_set_data(PMU_IIC_BUS, 0x1161, 0x0a, true);
if ( rc==0 ) rc=pmu_set_data(PMU_IIC_BUS, 0x1861, 0x0a, true);
if ( rc==0 && plat_id==0x2 ) rc=pmu_set_data(PMU_IIC_BUS, 0x3af, 0x44, true);
if ( rc==0 ) rc=pmu_set_data(PMU_IIC_BUS, 0x1792, 0x7B, true);
if ( rc!=0 ) {
dprintf(DEBUG_CRITICAL, "D2257: cannot apply fix for PMU A0 silicon (%d)\n", rc);
} else {
dprintf(DEBUG_CRITICAL, "D2257: PMU A0 fixups ok\n");
}
rc=pmu_set_data(PMU_IIC_BUS, kDIALOG_TEST_ACCESS, kDIALOG_TEST_ACCESS_DIS, false);
if ( rc!=0 ) panic("D2257: cannot exit PMU test mode");
}
}
}
}
#endif
}
}
/* pmu_setup() is only called in iBSS and LLB - not in iBoot or iBEC. */
void pmu_setup(void)
{
/* Set core voltage to the platform's base soc voltage */
power_set_soc_voltage(platform_get_base_soc_voltage(), false);
#if !WITH_CPU_APSC
/* Set cpu voltage to the platform's base cpu voltage */
power_set_cpu_voltage(platform_get_base_cpu_voltage(), false);
#endif
/* Set ram voltage to the platform's base ram voltage */
power_set_ram_voltage(platform_get_base_ram_voltage(), false);
#if DIALOG_D1972 || DIALOG_D2018 || DIALOG_D2045 || DIALOG_D2089 || DIALOG_D2186 || DIALOG_D2207
uint8_t data;
/* Enable DWI from the start — we need to ensure this happens on wakeup */
pmu_get_data(PMU_IIC_BUS, kDIALOG_SYS_CONTROL2, &data);
data |= kDIALOG_SYS_CONTROL2_DWI_EN;
pmu_set_data(PMU_IIC_BUS, kDIALOG_SYS_CONTROL2, data, true);
#endif
#if DIALOG_D2018
/* Enable DWI for BUCK[0,2] & LDO16 */
pmu_set_data(PMU_IIC_BUS, kD2018_BUCK_DWI, kDIALOG_DWI_BUCK0_DWI_EN |
kDIALOG_DWI_BUCK2_DWI_EN | kDIALOG_DWI_LDO16_DWI_EN |
kDIALOG_DWI_CPUA_EN_CTRL | kDIALOG_DWI_CPUB_EN_CTRL, true);
#endif
/* Initialize the LDOs to default values */
dialog_use_setup(pmu_ldo_warm_setup, sizeof(pmu_ldo_warm_setup)/sizeof(pmu_ldo_warm_setup[0]));
if (!power_is_suspended()) {
dialog_use_setup(pmu_ldo_cold_setup, sizeof(pmu_ldo_cold_setup)/sizeof(pmu_ldo_cold_setup[0]));
#if PMU_LDO_COLD_SETUP_AP_DEV
if (target_config_dev()) {
dialog_use_setup(pmu_ldo_cold_setup_dev, sizeof(pmu_ldo_cold_setup_dev)/sizeof(pmu_ldo_cold_setup_dev[0]));
} else {
dialog_use_setup(pmu_ldo_cold_setup_ap, sizeof(pmu_ldo_cold_setup_ap)/sizeof(pmu_ldo_cold_setup_ap[0]));
}
#endif
}
#if PMU_HAS_VSEL_LOCK && !DEBUG_BUILD
unsigned i;
for (i= 0; (i != sizeof(ram_rails)/sizeof(ram_rails[0])); i++) {
dialog_lock_buck_vsel(ram_rails[i].pmu, ram_rails[i].data_reg);
}
#endif
}
void
pmu_late_init(void)
{
#if DIALOG_D1755
u_int8_t otp_iset_usb;
u_int8_t test_mode;
int error = 0;
pmu_get_data(PMU_IIC_BUS, kD1755_OTP_ISET_USB, &otp_iset_usb);
pmu_get_data(PMU_IIC_BUS, kD1755_TEST_MODE, &test_mode);
if ((otp_iset_usb & kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_MASK) != kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_100) {
// enter test mode
test_mode = kD1755_TEST_MODE_TEST_EN;
pmu_set_data(PMU_IIC_BUS, kD1755_TEST_MODE, test_mode, true);
// override OTP shadow register: 350mA -> 100mA
otp_iset_usb = (otp_iset_usb & ~kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_MASK) | kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_100;
error = pmu_set_data(PMU_IIC_BUS, kD1755_OTP_ISET_USB, otp_iset_usb, true);
}
// exit test mode if enabled
if ((error == 0) && (test_mode & kD1755_TEST_MODE_TEST_EN)) {
test_mode = 0;
error = pmu_set_data(PMU_IIC_BUS, kD1755_TEST_MODE, test_mode, true);
}
// Dialog recommends that if this sequence fails, the PMU should be reset
if (error != 0) {
pmu_set_data(PMU_IIC_BUS, kD1755_TEST_MODE, kD1755_TEST_MODE_TEST_EN | kD1755_TEST_MODE_RESET_PMU, true);
pmu_set_data(PMU_IIC_BUS, kD1755_TEST_MODE, kD1755_TEST_MODE_TEST_EN | kD1755_TEST_MODE_RESET_PMU, true);
}
#endif
}
extern bool dialog_charger_check_usb_change(const eventRegisters ints_pending, const statusRegisters status);
void
pmu_check_events(bool *powersupply_change_event, bool *button_event, bool *other_wake_event)
{
eventRegisters ints_pending;
statusRegisters status;
/* Read & clear interrupts */
pmu_read_events(ints_pending);
pmu_read_status(status);
#if !WITH_HW_CHARGER && !DIALOG_D2238 && !DIALOG_D2255
*powersupply_change_event = dialog_charger_check_usb_change(ints_pending, status);
#else
*powersupply_change_event = false;
#endif
// remove power supply events from ints_pending so that they are not picked up by
// the secondary checks; powersupply_changed will always be true if it's an
// event we still care about
for (int i = 0; i < kDIALOG_EVENT_COUNT; i++) {
ints_pending[i] &= ~kDialogEventPwrsupplyMask[i];
}
// PMU only wakes on ACC_DET assert, but event fires on either edge. Remove
// accessory detach events.
if (EVENT_FLAG_TEST(ints_pending, kDIALOG_EVENT_ACC_DET_MASK) && !STATUS_FLAG_TEST(status, kDIALOG_STATUS_ACC_DET_MASK)) {
EVENT_REGISTERS_GET_BYTE(ints_pending, kDIALOG_EVENT_ACC_DET_MASK) &= ~EVENT_FLAG_GET_BIT(kDIALOG_EVENT_ACC_DET_MASK);
}
*button_event = EVENT_FLAG_TEST(ints_pending, kDIALOG_EVENT_BUTTONS_MASK);
*other_wake_event = EVENT_FLAG_TEST(ints_pending, kDIALOG_EVENT_ACC_DET_MASK) || EVENT_FLAG_TEST(ints_pending, kDIALOG_EVENT_ALARM_MASK);
}
static int
do_pmem_cmd(int argc, struct cmd_arg *args)
{
#if PMU_HAS_RAM && !DIALOG_D2238
bool success;
char buffer[kDIALOG_EXT_MEM_CAL_SIZE];
if ( argc<2 || strcmp("read", args[1].str)==0 ) {
size_t count=kDIALOG_EXT_MEM_CAL0_SIZE;
size_t offset=0;
if ( argc>2 ) count=atoi( args[2].str );
if ( argc>3 ) offset=atoi( args[3].str );
success=power_load_memory_calibration(buffer, count);
if ( !success ) {
printf("failed to read %zu bytes\n", count);
} else {
for (size_t i=0; i<count; i++) {
printf("%02x", buffer[i+offset]);
if ( (i+1)%4==0 ) printf(" ");
if ( (i+1)%64==0 ) printf("\n");
}
printf("\n");
}
} else if ( argc>3 && strcmp("write", args[1].str)==0 ) {
success=power_load_memory_calibration(buffer, kDIALOG_EXT_MEM_CAL1_SIZE);
if ( !success ) {
printf("failed to read %d bytes\n", kDIALOG_EXT_MEM_CAL1_SIZE);
} else {
size_t offset=atoi( args[2].str );
int i, overflow=0;
for (i=3; !overflow && i<argc; i++) {
const int index=offset+i-3;
const uint8_t val=atoi(args[i].str)&0xff;
overflow=index>kDIALOG_EXT_MEM_CAL_SIZE ;
if ( overflow ) {
printf("%#x@%d is out of bounds\n", val, index);
} else {
buffer[index]=val;
}
}
if ( !overflow ) {
size_t count=kDIALOG_EXT_MEM_CAL0_SIZE;
if ( offset+i-3>kDIALOG_EXT_MEM_CAL0_SIZE ) count+=kDIALOG_EXT_MEM_CAL1_SIZE;
success=power_store_memory_calibration(buffer, count);
if ( !success ) printf("failed to write\n");
}
}
} else {
printf("usage:\n\t%s <command>\n", args[0].str);
printf("\t\tread [<count>] [<offset>] show memory calibration values\n");
printf("\t\twrite <offset> byte byte ... change byte(s) at offset\n");
return -1;
}
return 0;
#else
printf("not available on this target\n");
return -1;
#endif
}
static inline uint16_t pmu_index2gpio_addr(uint32_t index)
{
#if DIALOG_D2207
if (index >= 17) return kD2207_GPIO18 + (index-17)*3;
#endif
#if PMU_HAS_GPIO_CONF
return kDIALOG_SYS_GPIO_REG_START + index*2;
#endif
return kDIALOG_SYS_GPIO_REG_START + index;
}
static
void dump_gpiocfg_(int gpion) // gpio[1-21]
{
int result;
uint8_t val[2];
const uint16_t addr=pmu_index2gpio_addr(gpion-1);
result=pmu_get_data(PMU_IIC_BUS, addr, val);
#if PMU_HAS_GPIO_CONF
result=pmu_get_data(PMU_IIC_BUS, addr+1, &val[1]);
#endif
if ( result!=0 ) {
printf("gpio%02d: result=%d\n", gpion, result);
} else {
printf("gpio%02d: 0x%04x : cnfga=%02x", gpion, addr, val[0]);
#if PMU_HAS_GPIO_CONF
printf(" cnfgb=%02x", val[1]);
#endif
printf("\n");
}
}
static void dump_railcfg_(uint32_t index)
{
int result;
uint8_t vsel, ena;
char label[32];
const struct ldo_params *p=&LDOP[index];
#if DIALOG_D2257
const uint8_t last_ldo=16;
const uint8_t last_buck=25;
#else
const uint8_t last_ldo=15;
const uint8_t last_buck=24;
#endif
if (index<last_ldo) {
snprintf(label, sizeof(label), "LDO%02d", index+1);
} else if ( index<last_buck ) {
snprintf(label, sizeof(label), "BUCK%d", index-last_ldo);
} else {
snprintf(label, sizeof(label), " ");
}
if ( p->ldoreg && p->actreg ) {
result=pmu_get_data(PMU_IIC_BUS, p->ldoreg, &vsel);
if ( result==0 ) result=pmu_get_data(PMU_IIC_BUS, p->actreg, &ena);
if ( result!=0 ) {
printf("rail%02d: result=%d\n", index, result);
} else {
printf("rail%02d: %s : ena[%04x]=%02x vsel[%04x]=%02x",
index, label, p->actreg, ena, p->ldoreg, vsel);
#if DIALOG_D2255
uint8_t actual=0xff, fvsel=0xff;
uint16_t areg=0xffff, fvreg=0xffff;
if ( index<15 ) {
areg=p->ldoreg+1;
} else if ( index>=15 ) {
areg=p->ldoreg+2;
fvreg=p->ldoreg-1;
}
if ( areg!=0xffff ) {
result=pmu_get_data(PMU_IIC_BUS, areg, &actual);
if ( result!=0 ) actual=0xff;
}
if ( fvreg!=0xffff ) {
result=pmu_get_data(PMU_IIC_BUS, fvreg, &fvsel);
if ( result!=0 ) actual=0xff;
printf(" fvsel[%04x]=%02x", fvreg, fvsel);
}
// TODO: convert value to voltage
if ( ena ) printf(" actual[%04x]=%02x", areg, actual);
#endif
printf("\n");
}
} else {
printf("rail%02d: %s N/A\n", index, label);
}
}
static void do_dctl_cmd_help()
{
printf("\tdctl gpio[<n>] [cfg <config>][<value> <direction>]\n");
printf("\tdctl adc <channel>\n");
printf("\tdctl rail[<n>]\n");
}
// dctl gpio
// dctl gpio<n>
// dctl gpio<n> cfg configa
// dctl gpio<n> value direction
// dctl adc <chan>
// dctl rail
// dctl rail<n>
// dctl uvwarn thresholdMV
static int
do_dctl_cmd(int argc, struct cmd_arg *args)
{
if ( argc==1 ) {
do_dctl_cmd_help();
} else if ( strncmp("gpio", args[1].str, 4)==0 ) {
if ( args[1].str[4]==0 ) {
for (int gpion=1; gpion<=kDIALOG_GPIO_COUNT; gpion++) {
dump_gpiocfg_(gpion);
}
} else if ( argc==2 ) {
const int gpion=atoi( &args[1].str[4] );
if ( gpion>0 && gpion<=kDIALOG_GPIO_COUNT ) dump_gpiocfg_(gpion);
} else if ( argc==4 && args[2].str[0]=='c' ) {
const int gpion=atoi( &args[1].str[4] );
if ( gpion>0 && gpion<=kDIALOG_GPIO_COUNT ) {
const uint32_t cfga=strtoul( args[3].str,NULL,0 );
power_gpio_configure(gpion-1, cfga);
dump_gpiocfg_(gpion);
}
} else if ( argc==4 ) {
const int gpion=atoi( &args[1].str[4] );
if ( gpion>0 && gpion<=kDIALOG_GPIO_COUNT ) {
const uint32_t direction=atoi( args[2].str );
const uint32_t value=atoi( args[3].str );
const int rc=power_set_gpio(gpion-1, direction, value);
if ( rc!=0 ) printf("gpio%02d: direction=%x value=%x (%d)\n", gpion, direction, value, rc);
dump_gpiocfg_(gpion);
}
}
} else if ( strncmp("rail", args[1].str, 4)==0 ) {
if ( args[1].str[4]==0 ) {
for (int index=0; index<NUM_LDOS; index++) {
dump_railcfg_(index);
}
} else if ( argc==2 ) {
const int index=atoi( &args[1].str[4] );
if ( index<NUM_LDOS ) dump_railcfg_(index);
} else if ( argc>=3 ) {
// dctl rail<n>0 ena [vsel]
const int index=atoi( &args[1].str[4] );
if ( index<NUM_LDOS ) {
int result;
const struct ldo_params *p=&LDOP[index];
if ( !p->actreg ) {
printf("rail%d: enable register not available", index);
} else {
const uint32_t ena=atoi( args[2].str )&0x1;
result=pmu_set_data(PMU_IIC_BUS, p->actreg, ena, true);
if ( result!=0 ) {
printf("rail%d: enable error (%d)", index, result);
} else if ( argc==4 ) {
if ( !p->ldoreg ) {
printf("rail%d: vsel register not available", index);
} else {
const uint32_t vsel=atoi( args[3].str )&0xff;
result=pmu_set_data(PMU_IIC_BUS, p->ldoreg, vsel, true);
if ( result!=0 ) {
printf("rail%d: vsel=%x error (%d)", index, vsel, result);
}
}
}
}
dump_railcfg_(index);
}
}
} else if ( strncmp("uvwarn", args[1].str, 5)==0 && argc>1) {
const uint32_t thresholdMV=(uint32_t)atoi( args[2].str );
int result=pmu_uvwarn_config(0, thresholdMV);
printf("uvwarn: threshold=%dmV (%d)\n", thresholdMV, result);
} else if ( strncmp("adc", args[1].str, 4)==0 && argc>1) {
unsigned value = 0;
const int chan=atoi( args[2].str );
int result=dialog_read_adc(0, chan, &value);
if ( result<0 ) {
printf("error: chan=%d (%d)\n", chan, result);
} else {
printf("adc: chan=%d value=%u\n", chan, value);
}
} else {
printf("usage:\n\t%s <command>\n", args[0].str);
do_dctl_cmd_help();
return -1;
}
return 0;
}
#if WITH_RECOVERY_MODE
MENU_COMMAND_DEBUG(pmem, do_pmem_cmd, "Manage memory calibration nvram", NULL);
MENU_COMMAND_DEBUG(dctl, do_dctl_cmd, "Dialog control menu", NULL);
#endif
static uint8_t nvram_key_map[kPowerNVRAMPropertyCount] = {
0x0F, // kPowerNVRAMiBootStateKey
0x00, // kPowerNVRAMiBootDebugKey
0x01, // kPowerNVRAMiBootStageKey
0x02, // kPowerNVRAMiBootErrorCountKey
0x03, // kPowerNVRAMiBootErrorStageKey
0x10, // kPowerNVRAMiBootMemCalCAOffset0Key
0x11, // kPowerNVRAMiBootMemCalCAOffset1Key
0x12, // kPowerNVRAMiBootMemCalCAOffset2Key
0x13, // kPowerNVRAMiBootMemCalCAOffset3Key
0x0D, // kPowerNVRAMiBootBootFlags0Key
0x0E, // kPowerNVRAMiBootBootFlags1Key
0x14, // kPowerNVRAMiBootEnterDFUKey
};
static int dialog_get_nvram(int dev, uint8_t offset, uint8_t *data);
static int dialog_set_nvram(int dev, uint8_t offset, uint8_t data);
int
power_get_nvram(uint8_t key, uint8_t *data)
{
uint8_t offset;
if (key >= kPowerNVRAMPropertyCount) return -1;
offset = nvram_key_map[key];
return dialog_get_nvram(PMU_IIC_BUS, offset, data);
}
int
power_set_nvram(uint8_t key, uint8_t data)
{
uint8_t offset;
if (key >= kPowerNVRAMPropertyCount) return -1;
offset = nvram_key_map[key];
return dialog_set_nvram(PMU_IIC_BUS, offset, data);
}
void
power_clr_events(int wake)
{
/* Read & clear interrupts */
eventRegisters events;
pmu_read_events(events);
if (wake)
{
/* Unmask pmu wake interrupts */
pmu_write_mask(kDialogEventIntMasks);
}
}
static void
dialog_shutdown(u_int8_t type)
{
#if !DIALOG_D1755
if (type == kDIALOG_SYS_CONTROL_STANDBY)
{
int rc;
// turn off all GPIO wake sources so that spurious events will not prevent
// the system from turning off; Ashley will reload OTP once entering standby,
// so OTP wake sources will still turn the system on (this was not true on
// previous PMUs).
for (int i = 0; i < kDIALOG_GPIO_COUNT; i++) {
UInt8 gpioData;
UInt16 addr=pmu_index2gpio_addr(i);
rc=pmu_get_data(PMU_IIC_BUS, addr, &gpioData);
if ( rc!=0 ) {
dprintf(DEBUG_SPEW, "gpio%d: cannot read (%d)\n", i+1, rc);
} else if (!IS_GPIO_OUTPUT(gpioData) && (gpioData & kDIALOG_SYS_GPIO_INPUT_WAKE) ) {
gpioData &= ~kDIALOG_SYS_GPIO_INPUT_WAKE;
rc=pmu_set_data(PMU_IIC_BUS, addr, gpioData, false);
if ( rc!=0 ) dprintf(DEBUG_SPEW, "gpio%d: cannot read (%d)\n", i+1, rc);
}
}
}
#endif
pmu_set_data(PMU_IIC_BUS, kDIALOG_SYS_CONTROL, type, false);
enter_critical_section();
while (true);
}
void
pmu_shutdown(void)
{
dialog_shutdown(kDIALOG_SYS_CONTROL_STANDBY);
}
void
pmu_suspend(void)
{
dialog_shutdown(kDIALOG_SYS_CONTROL_HIBERNATE);
}
void
pmu_set_backlight_enable(uint32_t backlight_level)
{
#if WITH_SWI_BACKLIGHT
extern int swi_backlight_enable(u_int32_t iset_code, u_int32_t backlight_level);
swi_backlight_enable(kDIALOG_SWI_WLED_ISET, backlight_level);
#elif PMU_HAS_WLED
uint32_t cnt, count = sizeof(pmu_backlight_enable)/sizeof(pmu_backlight_enable[0]);
struct pmu_setup_struct tmp[count];
const struct pmu_setup_struct *setup;
if (backlight_level) {
memcpy(tmp, pmu_backlight_enable, count * sizeof(struct pmu_setup_struct));
setup = tmp;
if ((backlight_level >= 1) && (backlight_level <= MAX_BACKLIGHT_LEVEL)) {
UInt8 backlight_level_1;
#if DIALOG_D1755
backlight_level_1 = backlight_level;
#elif DIALOG_D2257
UInt8 backlight_level_2;
backlight_level_1 = (backlight_level >> 8) & 0x7; // msb bits
backlight_level_2 = backlight_level & 0xff;
#else
UInt8 backlight_level_2;
backlight_level_1 = backlight_level >> 3;
backlight_level_2 = backlight_level & 0x7;
#endif
for (cnt = 0; cnt < count; cnt++) {
if (tmp[cnt].reg == kDIALOG_WLED_ISET) {
tmp[cnt].value = backlight_level_1;
}
#if !DIALOG_D1755
if (tmp[cnt].reg == kDIALOG_WLED_ISET2) {
tmp[cnt].value = backlight_level_2;
}
#endif
}
}
} else {
count = sizeof(pmu_backlight_disable)/sizeof(pmu_backlight_disable[0]);
setup = pmu_backlight_disable;
}
dialog_use_setup(setup, count);
#endif
}
#if DIALOG_D1881 || DIALOG_D1946 || DIALOG_D1972 || DIALOG_D1974 || DIALOG_D2045 || DIALOG_D2186 || DIALOG_D2238
#define POWER_SOC_VMIN (600)
#define POWER_SOC_VMAX (1400)
#define DIALOG_CORE_BUCKOUT(mv) (((mv-600)*1000)+3124)/3125
#define DIALOG_CORE_BUCKMV(vsel) (600 + ((((vsel) * 3125)) / 1000))
#elif DIALOG_D2018
#define POWER_SOC_VMIN (725)
#define POWER_SOC_VMAX (1521)
#define DIALOG_CORE_BUCKOUT(mv) (((mv-725)*1000)+3124)/3125
#define DIALOG_CORE_BUCKMV(vsel) (725 + ((((vsel) * 3125)) / 1000))
#elif DIALOG_D2089 || DIALOG_D2207 || DIALOG_D2255 || DIALOG_D2257
#define POWER_SOC_VMIN (600)
#define POWER_SOC_VMAX (1396)
#define DIALOG_CORE_BUCKOUT(mv) (((mv-600)*1000)+3124)/3125
#define DIALOG_CORE_BUCKMV(vsel) (600 + ((((vsel) * 3125)) / 1000))
#else
#define POWER_SOC_VMIN (725)
#define POWER_SOC_VMAX (1500)
#define DIALOG_CORE_BUCKOUT(mv) ((mv-725)+24)/25
#define DIALOG_CORE_BUCKMV(vsel) (725 + ((vsel) * 25))
#endif
#if DIALOG_D2238
int power_set_soc_voltage(unsigned mv, int override)
{
return 0;
}
#else
int
power_set_soc_voltage(unsigned mv, int override)
{
u_int8_t tmp;
tmp = 0;
if ((mv < POWER_SOC_VMIN) || (mv > POWER_SOC_VMAX)) return -1;
unsigned i;
for (i= 0; (i != sizeof(soc_rails)/sizeof(soc_rails[0])); i++) {
pmu_set_data(soc_rails[i].pmu, soc_rails[i].data_reg, DIALOG_CORE_BUCKOUT(mv), true);
#if DIALOG_D1755 || DIALOG_D1815
if (soc_rails[i].data_reg == kDIALOG_BUCK1) {
// Be sure to set the BUCK1 selector to 0; this requires/assumes VBUCK1_SWI_EN=0
pmu_set_data(soc_rails[i].pmu, kDIALOG_BUCK1_SEL, 0, true);
pmu_get_data(soc_rails[i].pmu, soc_rails[i].ctrl_reg, &tmp);
tmp |= kDIALOG_BUCK_CONTROL_BUCK1_GO;
pmu_set_data(soc_rails[i].pmu, soc_rails[i].ctrl_reg, tmp, true);
}
#endif
}
return 0;
}
#endif
#if DIALOG_D1881 || DIALOG_D1946 || DIALOG_D1972 || DIALOG_D1974 || DIALOG_D2045 || DIALOG_D2186 || DIALOG_D2238
#define POWER_CPU_VMIN (600)
#define POWER_CPU_VMAX (1400)
#define DIALOG_CPU_BUCKOUT(mv) (((mv-600)*1000)+3124)/3125
#define DIALOG_CPU_BUCKMV(vsel) (600 + ((((vsel) * 3125)) / 1000))
#elif DIALOG_D2018
#define POWER_CPU_VMIN (725)
#define POWER_CPU_VMAX (1521)
#define DIALOG_CPU_BUCKOUT(mv) (((mv-725)*1000)+3124)/3125
#define DIALOG_CPU_BUCKMV(vsel) (725 + ((((vsel) * 3125)) / 1000))
#elif DIALOG_D2089 || DIALOG_D2207 || DIALOG_D2255
#define POWER_CPU_VMIN (600)
#define POWER_CPU_VMAX (1396)
#define DIALOG_CPU_BUCKOUT(mv) (((mv-600)*1000)+3124)/3125
#define DIALOG_CPU_BUCKMV(vsel) (600 + ((((vsel) * 3125)) / 1000))
#elif DIALOG_D2255
#define POWER_CPU_VMIN (500)
#define POWER_CPU_VMAX (1296)
#define DIALOG_CPU_BUCKOUT(mv) (((mv-500)*1000)+3124)/3125
#define DIALOG_CPU_BUCKMV(vsel) (500 + ((((vsel) * 3125)) / 1000))
#elif DIALOG_D2257
#define POWER_CPU_VMIN (450)
#define POWER_CPU_VMAX (1250)
#define DIALOG_CPU_BUCKOUT(mv) (((mv-450)*1000)+3124)/3125
#define DIALOG_CPU_BUCKMV(vsel) (450 + ((((vsel) * 3125)) / 1000))
#endif
#if DIALOG_D1881 || DIALOG_D1946 || DIALOG_D1972 || DIALOG_D1974 || DIALOG_D2018 || DIALOG_D2045 || DIALOG_D2089 || DIALOG_D2186 || DIALOG_D2207 || DIALOG_D2255 || DIALOG_D2257
int
power_set_cpu_voltage(unsigned mv, int override)
{
u_int8_t tmp;
tmp = 0;
if ((mv < POWER_CPU_VMIN) || (mv > POWER_CPU_VMAX)) return -1;
unsigned i;
for (i= 0; (i != sizeof(cpu_rails)/sizeof(cpu_rails[0])); i++) {
pmu_set_data(cpu_rails[i].pmu, cpu_rails[i].data_reg, DIALOG_CPU_BUCKOUT(mv), true);
}
return 0;
}
#else
int
power_set_cpu_voltage(unsigned mv, int override)
{
return -1;
}
#endif
#if DIALOG_D1972 || DIALOG_D2018 || DIALOG_D2045 || DIALOG_D2089 || DIALOG_D2186 || DIALOG_D2207 || DIALOG_D2255 || DIALOG_D2257
#define POWER_RAM_VMIN (600)
#define POWER_RAM_VMAX (1400)
#define DIALOG_RAM_BUCKOUT(mv) (((mv-600)*1000)+3124)/3125
int
power_set_ram_voltage(unsigned mv, int override)
{
u_int8_t tmp;
tmp = 0;
if ((mv < POWER_RAM_VMIN) || (mv > POWER_RAM_VMAX)) return -1;
unsigned i;
for (i= 0; (i != sizeof(ram_rails)/sizeof(ram_rails[0])); i++) {
pmu_set_data(ram_rails[i].pmu, ram_rails[i].data_reg, DIALOG_RAM_BUCKOUT(mv), true);
}
return 0;
}
#else
int
power_set_ram_voltage(unsigned mv, int override)
{
return -1;
}
#endif
// TODO: deprecate, use power_get_rail_value() instead
int power_get_buck_value(int buck, unsigned mv, u_int32_t *buffer)
{
int ret = 0;
switch (buck) {
#if DIALOG_D1881 || DIALOG_D1946 || DIALOG_D1972 || DIALOG_D1974 || DIALOG_D2018 || DIALOG_D2045 || DIALOG_D2089 || DIALOG_D2186 || DIALOG_D2207 || DIALOG_D2238
case 0:
case 1:
*buffer = DIALOG_CPU_BUCKOUT(mv);
break;
case 2:
*buffer = DIALOG_CORE_BUCKOUT(mv);
break;
#endif
// compatibility only
#if DIALOG_D1972 || DIALOG_D2018
case 16:
*buffer = DIALOG_RAM_BUCKOUT(mv);
break;
#endif
default: ret = -1; break;
}
return ret;
}
int
power_get_rail_value(int rail, unsigned mv, u_int32_t *buffer)
{
int ret = 0;
#if DIALOG_D2255 || DIALOG_D2257
switch (rail) {
case POWER_RAIL_CPU:
case POWER_RAIL_SOC:
case POWER_RAIL_VDD_FIXED:
case POWER_RAIL_CPU_RAM:
case POWER_RAIL_GPU:
case POWER_RAIL_GPU_RAM:
if ( mv<target_rails[rail].min_mv || mv>target_rails[rail].max_mv ) {
*buffer=0; // safe?
ret=-1;
} else {
*buffer=(( (mv-target_rails[rail].min_mv)*1000 ) + target_rails[rail].step_uv-1 ) / target_rails[rail].step_uv;
}
break;
default:
ret = -1;
break;
}
#elif DIALOG_D1881 || DIALOG_D1946 || DIALOG_D1972 || DIALOG_D1974 || DIALOG_D2018 || DIALOG_D2045 || DIALOG_D2089 || DIALOG_D2186 || DIALOG_D2207 || DIALOG_D2238 || DIALOG_D1972 || DIALOG_D2018
// compat. rail is a buck
ret=power_get_buck_value(rail, mv, buffer);
#endif
return ret;
}
int
power_convert_dwi_to_mv(int rail, u_int32_t dwival)
{
int ret;
switch (rail) {
#if DIALOG_D2255 || DIALOG_D2257
case POWER_RAIL_CPU:
case POWER_RAIL_SOC:
case POWER_RAIL_VDD_FIXED:
case POWER_RAIL_CPU_RAM:
case POWER_RAIL_GPU:
case POWER_RAIL_GPU_RAM:
ret=target_rails[rail].min_mv+(dwival*target_rails[rail].step_uv)/1000 ;
if ( ret>target_rails[rail].max_mv ) ret=-1;
break;
#elif DIALOG_D1881 || DIALOG_D1946 || DIALOG_D1972 || DIALOG_D1974 || DIALOG_D2018 || DIALOG_D2045 || DIALOG_D2089 || DIALOG_D2186 || DIALOG_D2207 || DIALOG_D2238
case 0:
case 1:
ret = DIALOG_CPU_BUCKMV(dwival);
break;
case 2:
ret = DIALOG_CORE_BUCKMV(dwival);
break;
#endif
default: ret = -1; break;
}
return ret;
}
int
power_set_display_voltage_offset(u_int32_t voltage_offset, bool fixed_boost)
{
#if DIALOG_D1755 || !PMU_HAS_WLED
return -1;
#elif !defined(TARGET_DISPLAY_VOLTAGE_BASE) || !defined(TARGET_DISPLAY_VOLTAGE_SCALE) || !defined(TARGET_DISPLAY_BOOST_LDO) || !defined(TARGET_DISPLAY_BOOST_OFFSET)
return -1;
#else
u_int32_t display_voltage = TARGET_DISPLAY_VOLTAGE_BASE - (voltage_offset * TARGET_DISPLAY_VOLTAGE_SCALE);
u_int8_t lcm_controlx_data, lcm_boost_data;
// Convert display_voltage to LCM_CONTROLx value
lcm_controlx_data = (display_voltage - 5000 + 49) / 50;
// Set LCM_CONTROLx register
pmu_set_data(PMU_IIC_BUS, TARGET_DISPLAY_BOOST_LDO, lcm_controlx_data, true);
if (false == fixed_boost) {
// Wait for LCMx to settle... 1ms is probably enough, but 10ms for now
task_sleep(10 * 1000);
// LCM_BOOST should be TARGET_DISPLAY_BOOST_OFFSET higher then LCM_CONTROLx
lcm_boost_data = lcm_controlx_data + TARGET_DISPLAY_BOOST_OFFSET / 50;
// Set LCM_BOOST register
pmu_set_data(PMU_IIC_BUS, kDIALOG_LCM_BOOST, lcm_boost_data, true);
}
return 0;
#endif
}
int
power_get_boot_flag(void)
{
if ((EVENT_FLAG_GET_BIT(kDIALOG_EVENT_HIB_MASK) & boot_flags) != 0) return kPowerBootFlagWarm;
if (!cold_boot_ints_valid) {
/* Read & clear interrupts */
pmu_read_events(cold_boot_ints);
cold_boot_ints_valid = true;
}
// if a cold boot, distinguish between two types of boot:
// - boot immediately
// - boot only if the power button is pressed and held
// For example, charger attach, alarm wake, or 30-pin accessory insert always powers up.
// A short tap of the power button or E75 accessory insert does not.
bool button_event = false;
eventRegisters wakeEvents;
memcpy(wakeEvents, cold_boot_ints, sizeof(eventRegisters));
const UInt16 cold_button_events[] = {
kDIALOG_EVENT_PWR_BUTTON_MASK,
#if WITH_HW_TRISTAR
kDIALOG_EVENT_ACC_DET_MASK,
#endif
};
const size_t num_cold_button_events = sizeof(cold_button_events)/sizeof(UInt16);
for (unsigned i = 0; i < num_cold_button_events; i++) {
UInt16 event = cold_button_events[i];
if (EVENT_FLAG_TEST(wakeEvents, event)) {
button_event = true;
wakeEvents[EVENT_FLAG_GET_BYTE(event)] &= ~EVENT_FLAG_GET_BIT(event);
}
}
// if we saw one of the events that would result in a cold-button boot,
// but there are other events pending, or USB is attached (but somehow failed
// to flag an event), continue to boot.
//
// Ignore all buttons from wake mask, not just power, since
// although they (e.g., home button) are "wake" events, they are not "power-on" events.
if (button_event
&& !(EVENT_REGISTER_TEST_MASK(kDialogEventNotButtonMasks, wakeEvents))
&& !(power_has_usb()))
return kPowerBootFlagColdButton;
return kPowerBootFlagCold;
}
void
pmu_will_resume(void)
{
// store boot flag for OS use (re-use same NVRAM byte used for voltage between LLB and iBoot)
power_set_nvram(kPowerNVRAMiBootBootFlags0Key, boot_flags);
}
int
power_read_dock_id(unsigned *id)
{
#if !DIALOG_D2238 && !DIALOG_D2255
int result;
unsigned value = 0;
result = dialog_read_adc(PMU_IIC_BUS, kDIALOG_ADC_CONTROL_MUX_SEL_ACC_ID, &value);
// Only want the high 4 bits
*id = value >> (kDIALOG_ADC_RESOLUTION_BITS - 4);
return result;
#else
return -1;
#endif
}
int
power_set_gpio(uint32_t index, uint32_t direction, uint32_t value)
{
int result;
uint8_t data;
if (index >= kDIALOG_GPIO_COUNT) return -1;
/* Read kDIALOG_SYS_GPIO_X register */
result = pmu_get_data(PMU_IIC_BUS, pmu_index2gpio_addr(index), &data);
if (result != 0) {
dprintf(DEBUG_SPEW, "gpio%d: cannot read (%d)\n", index+1, result);
return result;
}
data &= ~(kDIALOG_SYS_GPIO_DIRECTION_MASK | kDIALOG_SYS_GPIO_OUTPUT_LEVEL_HIGH);
switch (direction) {
case 2:
data |= kDIALOG_SYS_GPIO_DIRECTION_OUT_32KHZ;
break;
case 1:
data |= kDIALOG_SYS_GPIO_DIRECTION_OUT;
data |= value ? kDIALOG_SYS_GPIO_OUTPUT_LEVEL_HIGH : kDIALOG_SYS_GPIO_OUTPUT_LEVEL_LOW;
break;
case 0:
default:
data |= kDIALOG_SYS_GPIO_DIRECTION_IN_LEVEL_HIGH;
break;
}
/* Set kDIALOG_SYS_GPIO_X register */
result=pmu_set_data(PMU_IIC_BUS, pmu_index2gpio_addr(index), data, true);
if ( result!=0 ) dprintf(DEBUG_SPEW, "gpio%d: cannot set (%d)\n", index+1, result);
return result;
}
bool
power_get_gpio(uint32_t index)
{
u_int8_t reg, mask, data;
if (index >= kDIALOG_GPIO_COUNT) return false;
reg = EVENT_FLAG_GET_BYTE(kDIALOG_STATUS_GPIO_MASK(index));
mask = EVENT_FLAG_GET_BIT(kDIALOG_STATUS_GPIO_MASK(index));
int result=pmu_get_data(PMU_IIC_BUS, kDIALOG_STATUS_A + reg, &data);
if ( result!=0 ) dprintf(DEBUG_SPEW, "gpio%d: cannot read (%d)\n", index+1, result);
return (data & mask) != 0;
}
void
power_gpio_configure(uint32_t index, u_int32_t config)
{
if (index >= kDIALOG_GPIO_COUNT) return;
/* Set kDIALOG_SYS_GPIO_X register */
int result=pmu_set_data(PMU_IIC_BUS, pmu_index2gpio_addr(index), config, true);
if ( result!=0 ) dprintf(DEBUG_SPEW, "gpio%d: cannot configure (%d)\n", index+1, result);
}
int
power_set_ldo(uint32_t ldoSelect, uint32_t voltage)
{
u_int8_t data;
// The argument is 1-based, so decrement to be an array index
ldoSelect--;
// Bounds check and make sure the "LDO" isn't a switch: must have a valid min voltage
if ((ldoSelect >= NUM_LDOS) || (LDOP[ldoSelect].minv == 0)) return -1;
// Range check the requested voltage
if ((voltage < LDOP[ldoSelect].minv) || (voltage > (uint32_t)(LDOP[ldoSelect].step *
LDOP[ldoSelect].maxs +
LDOP[ldoSelect].minv)))
return -1;
// Construct the encoding and write the appropriate register
data = ((voltage - LDOP[ldoSelect].minv)/LDOP[ldoSelect].step);
pmu_set_data(PMU_IIC_BUS, LDOP[ldoSelect].ldoreg, data, true);
return 0;
}
int
power_enable_ldo(uint32_t ldo, bool enable)
{
u_int8_t data;
ldo--;
if (ldo >= NUM_LDOS) return -1;
pmu_get_data(PMU_IIC_BUS, LDOP[ldo].actreg, &data);
if (enable) data |= LDOP[ldo].actmask;
else data &= ~LDOP[ldo].actmask;
pmu_set_data(PMU_IIC_BUS, LDOP[ldo].actreg, data, true);
return 0;
}
static uint32_t nvram_valid_mask;
static uint8_t nvram_shadow[kDIALOG_MEMBYTE_LAST - kDIALOG_MEMBYTE0 + 1];
static int
dialog_get_nvram(int dev, uint8_t offset, uint8_t *data)
{
int result;
if (offset > (kDIALOG_MEMBYTE_LAST - kDIALOG_MEMBYTE0)) return -1;
if (!((1 << offset) & nvram_valid_mask)) {
result = pmu_get_data(dev, kDIALOG_MEMBYTE0 + offset, &nvram_shadow[offset]);
if (result != 0) return result;
nvram_valid_mask |= (1 << offset);
}
*data = nvram_shadow[offset];
return 0;
}
static int
dialog_set_nvram(int dev, uint8_t offset, uint8_t data)
{
if (offset > (kDIALOG_MEMBYTE_LAST - kDIALOG_MEMBYTE0)) return -1;
if (((1 << offset) & nvram_valid_mask) && (data == nvram_shadow[offset]))
return 0;
nvram_shadow[offset] = data;
nvram_valid_mask |= (1 << offset);
return pmu_set_data(dev, kDIALOG_MEMBYTE0 + offset, data, 0);
}
utime_t
power_get_calendar_time(void)
{
uint8_t offset_secs[4], offset_ticks[2];
static const uint64_t tick_frequency = 32768;
if (pmu_read_nvram(kDIALOG_MEMBYTE0 + 4, offset_secs, sizeof(offset_secs)) != 0) return 0;
if (pmu_read_nvram(kDIALOG_MEMBYTE0 + 21, offset_ticks, sizeof(offset_ticks)) != 0) return 0;
uint64_t offset = ((uint64_t)(offset_secs[3]) << (24 + 15))
+ ((uint64_t)(offset_secs[2]) << (16 + 15))
+ ((uint64_t)(offset_secs[1]) << (8 + 15))
+ ((uint64_t)(offset_secs[0]) << (15))
+ ((uint64_t)(offset_ticks[1] & 0x7F) << (8))
+ (uint64_t)(offset_ticks[0]);
uint64_t ticks;
#if PMU_HAS_32K_RTC
// registers are latched when reading the first byte; read all six at once
UInt8 data[(kDIALOG_RTC_SECOND_D - kDIALOG_RTC_SUB_SECOND_A) + 1];
if (pmu_read_nvram(kDIALOG_RTC_SUB_SECOND_A, data, sizeof(data)) != 0) return 0;
ticks = ((uint64_t)(data[kDIALOG_RTC_SUB_SECOND_A - kDIALOG_RTC_SUB_SECOND_A])>>1)
| ((uint64_t)(data[kDIALOG_RTC_SUB_SECOND_B - kDIALOG_RTC_SUB_SECOND_A]) << 7)
| ((uint64_t)(data[kDIALOG_RTC_SECOND_A - kDIALOG_RTC_SUB_SECOND_A]) << 15)
| ((uint64_t)(data[kDIALOG_RTC_SECOND_B - kDIALOG_RTC_SUB_SECOND_A]) << 23)
| ((uint64_t)(data[kDIALOG_RTC_SECOND_C - kDIALOG_RTC_SUB_SECOND_A]) << 31)
| ((uint64_t)(data[kDIALOG_RTC_SECOND_D - kDIALOG_RTC_SUB_SECOND_A]) << 39);
#else
uint32_t upcount, upcount2;
// read twice, to guard against race with the incrementer
do {
if (pmu_read_nvram(kDIALOG_UPCOUNT_A, &upcount, sizeof(upcount)) != 0) return 0;
if (pmu_read_nvram(kDIALOG_UPCOUNT_A, &upcount2, sizeof(upcount2)) != 0) return 0;
} while (upcount != upcount2);
ticks = upcount * tick_frequency;
#endif
ticks += offset;
utime_t secs = ticks / tick_frequency;
utime_t usecs = ((ticks % tick_frequency) * 1000000) / tick_frequency;
return (secs * 1000000) + usecs;
}
#if !WITH_HW_CHARGER
void charger_print_status(void)
{
}
#endif