1230 lines
40 KiB
C
1230 lines
40 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_D2255 && !WITH_HW_CHARGER
|
|
|
|
bool charger_has_usb(int dock) { return false; }
|
|
bool charger_has_external(int dock) { return false; }
|
|
bool charger_has_firewire(int dock) { return false; }
|
|
bool charger_has_batterypack(int dock) { return false; }
|
|
bool charger_charge_done(int dock) { return true; }
|
|
|
|
uint32_t charger_get_max_charge_current(int dock) { return 1500; } // bogus, only to prevent infinite loops
|
|
int charger_read_battery_level(uint32_t *milliVolts) { return 5000; }; // bogus, only to prevent infinite loops
|
|
|
|
void charger_clear_usb_state(void) { }
|
|
void charger_clear_alternate_usb_current_limit(void) { }
|
|
void charger_set_charging(int dock, uint32_t input_current_limit, uint32_t *charge_current_ma) { }
|
|
|
|
#elif !WITH_HW_CHARGER || WITH_HW_CHARGER_DIALOG || DIALOG_D2238
|
|
|
|
#if WITH_HW_CHARGER_DIALOG
|
|
|
|
#if DIALOG_D2231
|
|
#include "d2231.h"
|
|
#elif DIALOG_D2355
|
|
#include "d2355.h"
|
|
#else
|
|
#error Unknown Dialog charger selection
|
|
#endif
|
|
|
|
#else // !WITH_HW_CHARGER_DIALOG
|
|
|
|
#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"
|
|
#define charger_print_status pmu_charger_print_status
|
|
#define charger_set_charging pmu_charger_set_charging
|
|
#define charger_has_external pmu_charger_has_external
|
|
#elif DIALOG_D2257
|
|
#include "d2257.h"
|
|
#else
|
|
#error Unknown Dialog PMU selection
|
|
#endif
|
|
|
|
#endif // !WITH_HW_CHARGER_DIALOG
|
|
|
|
#include "NTCTables.h"
|
|
#include <target/powerconfig.h>
|
|
|
|
#if !WITH_HW_CHARGER
|
|
#define CHARGER_IIC_BUS PMU_IIC_BUS
|
|
#endif
|
|
|
|
#ifdef DIALOG_PMU_USE_CHARGE_TABLE
|
|
#error powerconfig.h is out of date
|
|
#endif
|
|
|
|
#if PMU_HAS_ISET_BAT_2BYTES
|
|
typedef uint16_t iset_bat_t;
|
|
#else
|
|
typedef uint8_t iset_bat_t;
|
|
#endif
|
|
|
|
static iset_bat_t ichg_bat_max;
|
|
static uint8_t iset_buck_limit;
|
|
#if DIALOG_D1755
|
|
static uint8_t iset_buck_calibration_500;
|
|
static uint8_t iset_buck_calibration_1000;
|
|
#endif
|
|
#if TARGET_USE_CHARGE_TABLE
|
|
#if DIALOG_D1815 || DIALOG_D1881 || DIALOG_D1946
|
|
static uint8_t ichg_tbat_max[kDIALOG_ICHG_TBAT_NUM] = TARGET_ICHG_TBAT_MAX;
|
|
#endif
|
|
#endif
|
|
|
|
// This goes after the powerconfig
|
|
#ifndef TARGET_MAX_USB_INPUT_CURRENT
|
|
#define TARGET_MAX_USB_INPUT_CURRENT (1000)
|
|
#endif
|
|
|
|
// this is aria With internal charger
|
|
#if DIALOG_D2257 && !DIALOG_D2231 && !DIALOG_D2355
|
|
#define DIALOG_CHG_CONTROL kD2257_CHG_CONTROL
|
|
#else
|
|
#define DIALOG_CHG_CONTROL kDIALOG_SYS_CONTROL
|
|
#endif
|
|
|
|
static int dialog_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;
|
|
|
|
iic_write(dev, kDIALOG_ADDR_W, data, sizeof(data));
|
|
if (do_confirm) {
|
|
iic_read(dev, kDIALOG_ADDR_R, data, sizeof(data)-1, &confirm, 1, IIC_NORMAL);
|
|
if (byte == confirm) {
|
|
dprintf(DEBUG_SPEW, "pmu%d: wrote %x to reg %x\n", dev, byte, reg);
|
|
} else {
|
|
dprintf(DEBUG_SPEW, "pmu%d: try to write %x to reg %x, but got %x back\n", dev, byte, reg, confirm);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int dialog_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);
|
|
return iic_read(dev, kDIALOG_ADDR_R, addr, sizeof(addr), byte, 1, IIC_NORMAL);
|
|
}
|
|
|
|
static void dialog_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(CHARGER_IIC_BUS, kDIALOG_ADDR_R, addr, sizeof(addr), data, sizeof(eventRegisters), IIC_NORMAL);
|
|
}
|
|
|
|
static void dialog_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(CHARGER_IIC_BUS, kDIALOG_ADDR_R, addr, sizeof(addr), data, sizeof(statusRegisters), IIC_NORMAL);
|
|
}
|
|
|
|
#if DIALOG_D2257 && !DIALOG_D2231 && !DIALOG_D2355
|
|
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 );
|
|
|
|
// 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_D2231 && !DIALOG_D2238 && !DIALOG_D2255 && !DIALOG_D2355
|
|
if (kDIALOG_ADC_CONTROL_MUX_SEL_ACC_ID == input_select) {
|
|
#if DIALOG_D2045 || DIALOG_D2089 || DIALOG_D2186 || DIALOG_D2207 || DIALOG_D2238
|
|
dialog_set_data(dev, kDIALOG_ADC_CONTROL2, kDIALOG_ADC_CONTROL2_ADC_REF_EN, 0);
|
|
#else
|
|
dialog_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
|
|
|
|
dialog_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
|
|
}
|
|
|
|
dialog_get_data(dev, kDIALOG_ADC_CONTROL, ®);
|
|
} 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) {
|
|
dialog_get_data(dev, kDIALOG_MEMBYTE0+0x09, &data[0]);
|
|
if(data[0]<255) data[0]++;
|
|
dialog_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(CHARGER_IIC_BUS, kDIALOG_ADDR_R, ®, 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
|
|
dialog_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
|
|
dialog_get_data(dev, DIALOG_CHG_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
|
|
dialog_set_data(dev, kDIALOG_MEMBYTE0+0x8, issues, 0);
|
|
// Write reset count to scratchpad
|
|
dialog_get_data(dev, kDIALOG_MEMBYTE0+0x0a, &data[0]);
|
|
if(data[0]<255) data[0]++;
|
|
dialog_set_data(dev, kDIALOG_MEMBYTE0+0x0a, data[0], 0);
|
|
#endif
|
|
// PMU register reset
|
|
dialog_get_data(dev, 0xE0, &data[0]);
|
|
data[0]|=1; // test_enable
|
|
dialog_set_data(dev, 0xE0, data[0], 0);
|
|
data[0]|=3; // test_enable|SW_RESET
|
|
dialog_set_data(dev, 0xE0, data[0], 0);
|
|
// NOTREACHED
|
|
while(1);
|
|
} else {
|
|
dialog_get_data(dev, DIALOG_CHG_CONTROL, &data[0]);
|
|
// Clear BAT_PWR_SUSP
|
|
data[0] &= ~kDIALOG_SYS_CONTROL_BAT_PWR_SUSPEND;
|
|
dialog_set_data(dev, DIALOG_CHG_CONTROL, data[0], 0);
|
|
// Wait
|
|
spin(1300);
|
|
// Set BAT_PWR_SUSP
|
|
data[0] |= kDIALOG_SYS_CONTROL_BAT_PWR_SUSPEND;
|
|
dialog_set_data(dev, DIALOG_CHG_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(CHARGER_IIC_BUS, kDIALOG_ADDR_R, addr, sizeof(addr), &data[0], sizeof(data), IIC_NORMAL);
|
|
|
|
dialog_set_data(dev, kDIALOG_ADC_CONTROL, kDIALOG_ADC_CONTROL_DEFAULTS, 0);
|
|
#if DIALOG_D2045 || DIALOG_D2089 || DIALOG_D2186 || DIALOG_D2207 || DIALOG_D2231 || DIALOG_D2238
|
|
dialog_set_data(dev, kDIALOG_ADC_CONTROL2, kDIALOG_ADC_CONTROL2_DEFAULTS, 0);
|
|
#endif
|
|
|
|
#if DIALOG_D1755 || DIALOG_D2231
|
|
*level = ((unsigned)data[1] << 2) | (data[0] & 0x3);
|
|
#else
|
|
*level = ((unsigned)data[1] << 4) | (data[0] & 0xf);
|
|
#endif
|
|
|
|
return result;
|
|
}
|
|
#endif
|
|
|
|
static int
|
|
dialog_read_battery_level(int dev, unsigned *level)
|
|
{
|
|
int result;
|
|
unsigned adc;
|
|
|
|
result = dialog_read_adc(dev, kDIALOG_ADC_CONTROL_MUX_SEL_VBAT, &adc);
|
|
|
|
#if DIALOG_D2045 || DIALOG_D2089 || DIALOG_D2186 || DIALOG_D2207 || DIALOG_D2231 || DIALOG_D2238 || DIALOG_D2257 || DIALOG_D2355
|
|
*level = ((adc * 2500) >> kDIALOG_ADC_RESOLUTION_BITS) + 2500;
|
|
#else
|
|
*level = ((adc * 2000) >> kDIALOG_ADC_RESOLUTION_BITS) + 2500;
|
|
#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;
|
|
unsigned adc;
|
|
|
|
if (level == NULL) return -1;
|
|
|
|
result = dialog_read_adc(CHARGER_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);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
#if DIALOG_D1815
|
|
bool dialog_workaround_7886796(const statusRegisters status)
|
|
{
|
|
// Ashley has a bug where if the voltage drops below VBUS_THR (~4.3V), the charger buck digital control
|
|
// resets to 100mA, but the analog side keeps drawing more current. The only way around it
|
|
// is to disable the charger.
|
|
if (STATUS_FLAG_TEST(status, kDIALOG_STATUS_CHG_ATT_MASK) && !EVENT_FLAG_TEST(status, kDIALOG_STATUS_USB_MASK)) {
|
|
dialog_set_data(CHARGER_IIC_BUS, kDIALOG_CHARGE_BUCK_CONTROL, kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_100, true);
|
|
dialog_set_data(CHARGER_IIC_BUS, kDIALOG_CHARGE_BUCK_CONTROL, kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_100 | kDIALOG_CHARGE_BUCK_CONTROL_ISET_VBUS_CHG_BUCK_EN, true);
|
|
task_sleep(25 * 1000); // wait out 16ms VBUS_PROT_DET debounce plus some margin
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
bool charger_has_usb(int dock)
|
|
{
|
|
statusRegisters status;
|
|
|
|
dialog_read_status(status);
|
|
|
|
#if DIALOG_D1815
|
|
if (dialog_workaround_7886796(status)) {
|
|
dialog_read_status(status);
|
|
}
|
|
#endif
|
|
|
|
#ifdef TARGET_POWER_USB_MASK
|
|
return (STATUS_FLAG_TEST(status, TARGET_POWER_USB_MASK));
|
|
#else
|
|
return (STATUS_FLAG_TEST(status, kDIALOG_STATUS_USB_MASK));
|
|
#endif
|
|
}
|
|
|
|
bool charger_has_firewire(int dock)
|
|
{
|
|
statusRegisters status;
|
|
dialog_read_status(status);
|
|
return STATUS_REGISTER_TEST_MASK(status, kDialogStatusFWMask);
|
|
}
|
|
|
|
bool charger_has_external(int dock)
|
|
{
|
|
// external charge not supported
|
|
return false;
|
|
}
|
|
|
|
static void
|
|
read_defaults_if_needed(void)
|
|
{
|
|
static bool defaults_read = 0;
|
|
if (defaults_read)
|
|
return;
|
|
defaults_read = 1;
|
|
|
|
#if PMU_HAS_ISET_BAT_2BYTES
|
|
uint8_t otp[2];
|
|
|
|
/* Read and remember max bat charge current */
|
|
dialog_get_data(CHARGER_IIC_BUS, kDIALOG_OTP_ISET_BAT_MSB, &otp[0]);
|
|
dialog_get_data(CHARGER_IIC_BUS, kDIALOG_OTP_ISET_BAT_MSB+1, &otp[1]);
|
|
ichg_bat_max = (otp[0] << 8) | otp[1];
|
|
#else
|
|
uint8_t otp;
|
|
|
|
/* Read and remember max bat charge current */
|
|
dialog_get_data(CHARGER_IIC_BUS, kDIALOG_OTP_ISET_BAT, &otp);
|
|
#if DIALOG_D2238
|
|
ichg_bat_max = kDIALOG_CHARGE_CONTROL_A_ISET_BAT_MASK & (otp << 1);
|
|
#else
|
|
ichg_bat_max = kDIALOG_CHARGE_CONTROL_A_ISET_BAT_MASK &
|
|
((otp >> kDIALOG_OTP_ISET_BAT_SHIFT) << kDIALOG_CHARGE_CONTROL_A_ISET_BAT_SHIFT);
|
|
#endif // DIALOG_D2238
|
|
|
|
#endif
|
|
|
|
#if DIALOG_D1755
|
|
// <rdar://problem/6019689>
|
|
if (5 == ichg_bat_max)
|
|
ichg_bat_max = 0x27;
|
|
// <rdar://problem/6861148>
|
|
if (1 == ichg_bat_max)
|
|
ichg_bat_max = 0x18;
|
|
|
|
// read out calibrated USB current limits from OTP. If unset or out of range, use defaults.
|
|
// use 450mA rather than 500mA setting by default, because that's what was tested on N88.
|
|
dialog_get_data(CHARGER_IIC_BUS, kD1755_OTP_USB_500_LIMIT, &iset_buck_calibration_500);
|
|
if ((iset_buck_calibration_500 < kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_350) || (iset_buck_calibration_500 > kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_600))
|
|
iset_buck_calibration_500 = kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_450;
|
|
dialog_get_data(CHARGER_IIC_BUS, kD1755_OTP_USB_900_LIMIT, &iset_buck_calibration_1000);
|
|
if ((iset_buck_calibration_1000 < kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_600) || (iset_buck_calibration_1000 > kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_1300))
|
|
iset_buck_calibration_1000 = kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_900;
|
|
#endif
|
|
}
|
|
|
|
static iset_bat_t
|
|
dialog_get_charge_current_setting(unsigned int charge_target_ma)
|
|
{
|
|
if (charge_target_ma > kDIALOG_CHARGE_CONTROL_MAX) charge_target_ma = kDIALOG_CHARGE_CONTROL_MAX;
|
|
return (charge_target_ma / kDIALOG_CHARGE_CONTROL_STEP);
|
|
}
|
|
|
|
static uint32_t
|
|
dialog_get_charge_current_limit(iset_bat_t setting)
|
|
{
|
|
setting = (setting & kDIALOG_CHARGE_CONTROL_A_ISET_BAT_MASK) >> kDIALOG_CHARGE_CONTROL_A_ISET_BAT_SHIFT;
|
|
return setting * kDIALOG_CHARGE_CONTROL_STEP;
|
|
}
|
|
|
|
uint32_t
|
|
charger_get_max_charge_current(int dock)
|
|
{
|
|
read_defaults_if_needed();
|
|
return dialog_get_charge_current_limit(ichg_bat_max);
|
|
}
|
|
|
|
int charger_read_battery_temperature(int *centiCelsiusTemperature)
|
|
{
|
|
return dialog_read_battery_temperature(centiCelsiusTemperature);
|
|
}
|
|
|
|
int charger_read_battery_level(uint32_t *milliVolts)
|
|
{
|
|
return dialog_read_battery_level(CHARGER_IIC_BUS, milliVolts);
|
|
}
|
|
|
|
#if !DIALOG_D2238
|
|
|
|
static int
|
|
dialog_get_charger_limit(UInt8 setting)
|
|
{
|
|
#if DIALOG_D1755
|
|
if (setting <= kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_120)
|
|
return 70 + setting*10;
|
|
else if (setting <= kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_600)
|
|
return 350 + (setting-6)*50;
|
|
else if (setting <= kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_1300)
|
|
return 700 + (setting-12)*200;
|
|
#elif DIALOG_D1815
|
|
if (setting <= kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_200)
|
|
return 50 + setting*10;
|
|
else if (setting <= kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_600)
|
|
return 250 + (setting-16)*50;
|
|
else if (setting <= kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_900)
|
|
return 700 + (setting-24)*200;
|
|
else if (setting <= kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_2000)
|
|
return 1000 + (setting-26)*200;
|
|
#else
|
|
if (setting <= kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_MIN)
|
|
return kDIALOG_CHARGE_BUCK_CONTROL_MIN;
|
|
else
|
|
return kDIALOG_CHARGE_BUCK_CONTROL_MIN + (100*(setting - kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_MIN) + (kDIALOG_CHARGE_BUCK_CONTROL_STEP_PER_100MA-1))/kDIALOG_CHARGE_BUCK_CONTROL_STEP_PER_100MA; // round up
|
|
#endif
|
|
|
|
return -1;
|
|
}
|
|
|
|
static UInt8
|
|
dialog_input_current_limit_step_down(UInt8 iset_buck)
|
|
{
|
|
#if DIALOG_D1755
|
|
// steps are big; go down by one
|
|
if (iset_buck > kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_100) {
|
|
return (iset_buck - 1);
|
|
} else {
|
|
return kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_100;
|
|
}
|
|
#elif DIALOG_D1815
|
|
if (iset_buck <= kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_200) {
|
|
return kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_100;
|
|
} else if (iset_buck <= kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_600) {
|
|
// 50mA steps from 200-600mA
|
|
return iset_buck - 2;
|
|
} else {
|
|
// steps above 600mA are all at least 100mA
|
|
return iset_buck - 1;
|
|
}
|
|
#else
|
|
if (iset_buck <= kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_200) {
|
|
return kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_100;
|
|
} else {
|
|
return iset_buck - kDIALOG_CHARGE_BUCK_CONTROL_STEP_PER_100MA;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
dialog_log_charger_limit(UInt8 iset_buck)
|
|
{
|
|
dprintf(DEBUG_CRITICAL, "limiting USB input current to %d mA\n", dialog_get_charger_limit(iset_buck));
|
|
}
|
|
|
|
#endif // !DIALOG_D2238
|
|
|
|
void
|
|
charger_set_charging(int dock, uint32_t input_current_limit, uint32_t *charge_current_ma)
|
|
{
|
|
dprintf(DEBUG_SPEW, "dialog_set_charging(input_current_limit=%d, charge_current_ma=%d)\n",
|
|
input_current_limit, *charge_current_ma);
|
|
|
|
iset_bat_t charge_control_bat;
|
|
bool pause = (*charge_current_ma == 0);
|
|
|
|
read_defaults_if_needed();
|
|
|
|
#if !DIALOG_D2238
|
|
UInt8 syscontrol;
|
|
UInt8 charge_buck_control, iset_buck;
|
|
|
|
dialog_get_data(CHARGER_IIC_BUS, DIALOG_CHG_CONTROL, &syscontrol);
|
|
dialog_get_data(CHARGER_IIC_BUS, kDIALOG_CHARGE_BUCK_CONTROL, &charge_buck_control);
|
|
|
|
syscontrol &= ~kDIALOG_SYS_CONTROL_CHRG_CONTROLS;
|
|
#endif
|
|
|
|
dprintf(DEBUG_SPEW, "dialog_set_charging(paus=%d, ichg_bat_max=0x%02x)\n", pause, ichg_bat_max);
|
|
|
|
if (!pause) {
|
|
charge_control_bat = dialog_get_charge_current_setting(*charge_current_ma);
|
|
*charge_current_ma -= dialog_get_charge_current_limit(charge_control_bat);
|
|
} else {
|
|
#if DIALOG_D1815 || DIALOG_D1881 || DIALOG_D1946
|
|
charge_control_bat = kDIALOG_CHARGE_CONTROL_A_CHG_SUSP;
|
|
#else
|
|
charge_control_bat = 0;
|
|
#endif
|
|
}
|
|
|
|
#if TARGET_USE_CHARGE_TABLE
|
|
#if DIALOG_D1815 || DIALOG_D1881 || DIALOG_D1946
|
|
// Ashley takes care of setting the charge limit based on temperature, but the temperature-relative limits
|
|
// themselves can change based on voltage (ATV) level. So program the ICHG_BAT registers.
|
|
if (!pause) {
|
|
for (unsigned int i = 0; i < kDIALOG_ICHG_TBAT_NUM; i++) {
|
|
UInt8 tbat_reg = kDIALOG_ICHG_TBAT_0 + 2*i;
|
|
UInt8 tbat_data = ichg_tbat_max[i];
|
|
if (charge_control_bat < tbat_data) tbat_data = charge_control_bat;
|
|
dialog_set_data(CHARGER_IIC_BUS, tbat_reg, tbat_data, true);
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
#if PMU_HAS_ISET_BAT_2BYTES
|
|
dialog_set_data(CHARGER_IIC_BUS, kDIALOG_CHARGE_CONTROL_ICHG_BAT_MSB, (charge_control_bat >> 8) & 0xFF, true);
|
|
dialog_set_data(CHARGER_IIC_BUS, kDIALOG_CHARGE_CONTROL_ICHG_BAT_MSB+1, charge_control_bat & 0xFF, true);
|
|
#else
|
|
dialog_set_data(CHARGER_IIC_BUS, kDIALOG_CHARGE_CONTROL_ICHG_BAT, charge_control_bat, true);
|
|
#endif
|
|
|
|
#if !DIALOG_D2238
|
|
if (input_current_limit < 100) {
|
|
syscontrol |= kDIALOG_SYS_CONTROL_BAT_PWR_SUSPEND;
|
|
}
|
|
|
|
dialog_set_data(CHARGER_IIC_BUS, DIALOG_CHG_CONTROL, syscontrol, true);
|
|
|
|
#if DIALOG_D1946
|
|
dialog_get_data(CHARGER_IIC_BUS, kDIALOG_SYS_CONTROL2, &syscontrol);
|
|
if (input_current_limit < 100) {
|
|
syscontrol |= kDIALOG_SYS_CONTROL2_BAT_PWR_SUSPEND;
|
|
} else {
|
|
syscontrol &= ~kDIALOG_SYS_CONTROL2_BAT_PWR_SUSPEND;
|
|
}
|
|
dialog_set_data(CHARGER_IIC_BUS, kDIALOG_SYS_CONTROL2, syscontrol, true);
|
|
#endif
|
|
|
|
if (input_current_limit > TARGET_MAX_USB_INPUT_CURRENT) {
|
|
input_current_limit = TARGET_MAX_USB_INPUT_CURRENT;
|
|
}
|
|
|
|
#if DIALOG_D1881 || DIALOG_D1972
|
|
// use syscfg calibration to limit Angelina (and Agatha) values
|
|
#if WITH_SYSCFG
|
|
uint32_t iset_buck_cal[3];
|
|
if (syscfgCopyDataForTag('CBAT', (uint8_t *)iset_buck_cal, sizeof(iset_buck_cal)) == sizeof(iset_buck_cal)) {
|
|
uint32_t cal100 = iset_buck_cal[0]; // 16.16 fixed-point
|
|
uint32_t cal500 = iset_buck_cal[1]; // 16.16 fixed-point
|
|
uint32_t cal1000 = iset_buck_cal[2]; // 16.16 fixed-point
|
|
|
|
uint32_t calibrated_target = 0;
|
|
|
|
if (input_current_limit < 330 && cal100 != 0) {
|
|
// round up (keep current below target)
|
|
uint32_t calMA = (cal100 + 65535) >> 16;
|
|
|
|
if (calMA < 100) {
|
|
calibrated_target = input_current_limit + (100 - calMA);
|
|
} else if (calMA > 100) {
|
|
calibrated_target = input_current_limit - (calMA - 100);
|
|
}
|
|
} else if (input_current_limit >= 330 && cal500 != 0 && cal1000 != 0) {
|
|
SInt32 delta = cal1000 - cal500; // 16.16 fixed-point
|
|
SInt32 base = cal500 - delta; // 16.16 fixed-point
|
|
SInt32 slope = delta / 500; // 16.16 fixed-point
|
|
|
|
SInt32 target = (input_current_limit << 16); // 16.16 fixed-point
|
|
// fixed-point divide
|
|
calibrated_target = (((SInt64)(target - base) << 16)/ slope) >> 16;
|
|
}
|
|
|
|
if (calibrated_target != 0) {
|
|
// limit to 15% corection
|
|
if (calibrated_target > (input_current_limit * 115)/100) {
|
|
input_current_limit = (input_current_limit * 115)/100;
|
|
} else if (calibrated_target < (input_current_limit * 85)/100) {
|
|
input_current_limit = (input_current_limit * 85)/100;
|
|
} else {
|
|
input_current_limit = calibrated_target;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
#endif /* WITH_SYSCFG */
|
|
{
|
|
// if not available, limit high-charge rate (above 500mA) by 15% to ensure
|
|
// charger is below its limit.
|
|
if (input_current_limit > 500) {
|
|
input_current_limit = (85 * input_current_limit) / 100;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if DIALOG_D1881 || DIALOG_D1946 || DIALOG_D1972 || DIALOG_D1974 || DIALOG_D2018 || DIALOG_2045 || DIALOG_D2089 || DIALOG_D2207 || DIALOG_D2231 || DIALOG_D2355
|
|
if (input_current_limit >= kDIALOG_CHARGE_BUCK_CONTROL_MAX) {
|
|
iset_buck = kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_MAX;
|
|
} else if (input_current_limit >= 100) {
|
|
iset_buck = kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_MIN + (kDIALOG_CHARGE_BUCK_CONTROL_STEP_PER_100MA * (input_current_limit - kDIALOG_CHARGE_BUCK_CONTROL_MIN)) / 100;
|
|
} else {
|
|
iset_buck = kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_100;
|
|
}
|
|
|
|
#if DIALOG_D1946
|
|
// Alison trim values are too high at high current settings (8922649)
|
|
// 1A up to but not including 1.5A should be 25mA lower 1.5A on up should be 50mA lower.
|
|
if (iset_buck > kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_1500) {
|
|
iset_buck -= (kDIALOG_CHARGE_BUCK_CONTROL_STEP_PER_100MA / 2);
|
|
} else if (iset_buck > kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_1450) {
|
|
iset_buck = kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_1450;
|
|
} else if (iset_buck > kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_1000) {
|
|
iset_buck -= (kDIALOG_CHARGE_BUCK_CONTROL_STEP_PER_100MA / 4);
|
|
} else if (iset_buck > kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_975) {
|
|
iset_buck = kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_975;
|
|
}
|
|
#elif DIALOG_D1974
|
|
// <rdar://problem/11910668> J1/J2/J2A : USB 500mA, 1A, 2.1A, 2.4A Adjust input current limit
|
|
if (iset_buck >= kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_2200) {
|
|
// For input current limits of 2.2A on up, should be 62.5mA lower.
|
|
iset_buck -= 5; // 5*12.5mA
|
|
|
|
} else if (iset_buck >= kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_1500) {
|
|
// For input current limits of 1.5A<=x<2.2A. should be 50mA lower.
|
|
iset_buck -= 4; // 4*12.5mA
|
|
|
|
} else if (iset_buck >= kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_750) {
|
|
// For input current limits of 0.75A<=x<1.5A, should be 37.5mA lower.
|
|
iset_buck -= 3; // 3*12.5mA
|
|
|
|
} else if ( (iset_buck >= kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_250) &&
|
|
(iset_buck < kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_750) ) {
|
|
// For input current limits of 0.25A<=x<0.75A, should be set 12.5mA lower.
|
|
iset_buck -= 1; // 12.5mA
|
|
}
|
|
|
|
// limit Amelia A0 to 2300mA (9066812)
|
|
if (iset_buck > kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_2300) {
|
|
UInt8 chip_id;
|
|
dialog_get_data(CHARGER_IIC_BUS, kDIALOG_CHIP_ID, &chip_id);
|
|
if ((chip_id & kD1974_CHIP_ID_MRC_MASK) == kD1974_CHIP_ID_MRC_A0) {
|
|
iset_buck = kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_2300;
|
|
}
|
|
}
|
|
#elif DIALOG_D2018
|
|
{
|
|
// <rdar://problem/11621561> P101/P102/P103: USB 1A, 2.1A, and 2.4A Input Current Limit Measures Too High
|
|
|
|
UInt8 chip_id;
|
|
dialog_get_data(CHARGER_IIC_BUS, kDIALOG_CHIP_ID, &chip_id);
|
|
|
|
if ((((chip_id & kD2018_CHIP_ID_MRC_MASK) == 0x09) && (chip_id & kD2018_CHIP_ID_TRC_MASK) <=0x30) ||
|
|
((chip_id & kD2018_CHIP_ID_MRC_MASK) == 0x08)) {
|
|
|
|
if (iset_buck >= kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_2200) {
|
|
// For input current limits of 2.2A on up, should be 100mA lower.
|
|
iset_buck -= kDIALOG_CHARGE_BUCK_CONTROL_STEP_PER_100MA;
|
|
} else if (iset_buck >= kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_1500) {
|
|
// For input current limits of 1.5A<=x<2.2A. should be 75mA lower.
|
|
iset_buck -= (3*kDIALOG_CHARGE_BUCK_CONTROL_STEP_PER_100MA)/4;
|
|
} else if (iset_buck >= kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_1000) {
|
|
// For input current limits of 1.0A<=x<1.5A, should be set 25mA lower
|
|
iset_buck -= kDIALOG_CHARGE_BUCK_CONTROL_STEP_PER_100MA/4;
|
|
}
|
|
}
|
|
|
|
// <rdar://problem/11744688> P101/P102/P103:USB 500mA, 2.1A, 2.4A Adjust input current limit for DVT build
|
|
// <rdar://problem/11749442> P101/P102/P103: Adjust USB input current limits for DVT build and beyond
|
|
if (((chip_id & kD2018_CHIP_ID_MRC_MASK) >= 0x09) && (chip_id & kD2018_CHIP_ID_TRC_MASK) >=0x40) {
|
|
if (iset_buck >= kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_1500) {
|
|
// For input current limits of 1.5A on up, should be 37.5mA higher.
|
|
iset_buck += 3; // 3*12.5mA
|
|
|
|
} else if (iset_buck >= kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_750) {
|
|
// For input current limits of 0.75A<=x<1.5A. should be 25mA higher.
|
|
iset_buck += 2; // 2*12.5mA
|
|
|
|
} else if ( (iset_buck >= kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_350) &&
|
|
(iset_buck < kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_750) ) {
|
|
// For input current limits of 0.35A<=x<0.75A, should be 12.5mA higher.
|
|
iset_buck += 1;
|
|
|
|
} else if (iset_buck < kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_350) {
|
|
// For input current limits of x<0.35A, should be set 12.5mA lower.
|
|
iset_buck -= 1;
|
|
}
|
|
}
|
|
}
|
|
#elif DIALOG_D2089
|
|
if (iset_buck >= kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_2100) {
|
|
// For input current of 2.1A and higher, should be 75mA lower.
|
|
iset_buck -= (3*kDIALOG_CHARGE_BUCK_CONTROL_STEP_PER_100MA)/4;
|
|
} else if (iset_buck >= kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_1000) {
|
|
// For input current nominally @ 1.0A should be set 50mA lower
|
|
iset_buck -= kDIALOG_CHARGE_BUCK_CONTROL_STEP_PER_100MA/2;
|
|
} else if (iset_buck >= kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_500) {
|
|
// For input current nominally @ 500mA should be set 25mA lower
|
|
iset_buck -= kDIALOG_CHARGE_BUCK_CONTROL_STEP_PER_100MA/4;
|
|
}
|
|
#elif DIALOG_D2355
|
|
// <rdar://problem/24336192> J127 : Implement input current limit calibration in iBoot
|
|
if (iset_buck >= kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_2400) {
|
|
iset_buck -= 5;
|
|
} else if (iset_buck >= kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_1900) {
|
|
iset_buck -= 4;
|
|
} else if (iset_buck >= kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_1000) {
|
|
iset_buck -= 3;
|
|
} else if ( iset_buck >= kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_100 ) {
|
|
iset_buck -= 1;
|
|
}
|
|
#endif
|
|
|
|
#else
|
|
if (input_current_limit >= 1000) {
|
|
#if DIALOG_D1755
|
|
iset_buck = iset_buck_calibration_1000;
|
|
#else
|
|
iset_buck = kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_1000;
|
|
#endif
|
|
}
|
|
else if (input_current_limit >= 500) {
|
|
#if DIALOG_D1755
|
|
iset_buck = iset_buck_calibration_500;
|
|
#elif DIALOG_D1815
|
|
// should use 450mA instead of 500mA when CNBRICK software behavior bit is set,
|
|
// but we can't access it here (syscfg may not yet be configured).
|
|
iset_buck = kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_450;
|
|
#else
|
|
iset_buck = kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_500;
|
|
#endif
|
|
}
|
|
else {
|
|
iset_buck = kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_100;
|
|
}
|
|
#endif /* DIALOG_D1881 || DIALOG_D1946 || DIALOG_D1972 || DIALOG_D1974 || DIALOG_D2018*/
|
|
|
|
if (iset_buck_limit > 0) iset_buck = iset_buck_limit;
|
|
|
|
#ifdef FORCE_ISET_BUCK
|
|
dprintf(DEBUG_INFO, "forcing 'iset_buck' from 0x%02X to 0x%02x\n", iset_buck, FORCE_ISET_BUCK);
|
|
iset_buck = FORCE_ISET_BUCK;
|
|
#endif
|
|
|
|
// this is used as a reference for ISET_BUCK, so remove everything else
|
|
charge_buck_control &= kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_MASK;
|
|
|
|
if (iset_buck < charge_buck_control) {
|
|
// Ashley charger needs to be disabled to reduce usb current limit (6692148)
|
|
charge_buck_control = iset_buck;
|
|
dialog_set_data(CHARGER_IIC_BUS, kDIALOG_CHARGE_BUCK_CONTROL, charge_buck_control, true);
|
|
#if DIALOG_D1755 || DIALOG_D1815
|
|
charge_buck_control |= kDIALOG_CHARGE_BUCK_CONTROL_ISET_VBUS_CHG_BUCK_EN;
|
|
dialog_set_data(CHARGER_IIC_BUS, kDIALOG_CHARGE_BUCK_CONTROL, charge_buck_control, true);
|
|
#endif
|
|
}
|
|
|
|
#if DIALOG_D1815
|
|
if (input_current_limit >= 100) {
|
|
statusRegisters status;
|
|
dialog_read_status(status);
|
|
if (dialog_workaround_7886796(status)) {
|
|
charge_buck_control = kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_100;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if DIALOG_D1755
|
|
if (iset_buck > charge_buck_control) {
|
|
// clear any pending interrupts from previous attach/detach
|
|
eventRegisters events;
|
|
charger_read_events(events);
|
|
}
|
|
#endif
|
|
|
|
/* Limit USB current slew rate and back off chargers that can't supply the amount they claim (6662542) */
|
|
while (iset_buck > charge_buck_control) {
|
|
#if DIALOG_D1755
|
|
/* Raise the current limit one step at a time, and back off if VBUS drops but the charger remains present
|
|
* at 100mA (which the PMU will default to when VBUS snaps off the bus briefly).
|
|
*/
|
|
|
|
UInt8 data = (++charge_buck_control) | kDIALOG_CHARGE_BUCK_CONTROL_ISET_VBUS_CHG_BUCK_EN;
|
|
dialog_set_data(CHARGER_IIC_BUS, kDIALOG_CHARGE_BUCK_CONTROL, data, true);
|
|
|
|
// check to see if the charger looks removed, which may mean we have overloaded it and VBUS has dropped.
|
|
UInt8 event;
|
|
bool vbus_rem;
|
|
dialog_get_data(CHARGER_IIC_BUS, kDIALOG_EVENT_D, &event);
|
|
vbus_rem = event & kD1755_EVENT_D_VBUS_REM;
|
|
|
|
if (vbus_rem) {
|
|
statusRegisters status;
|
|
|
|
// if CHG_ATT is set, this means that charger was not really extracted; otherwise, it might be real extraction and we should trigger a recheck
|
|
dialog_read_status(status);
|
|
if (STATUS_FLAG_TEST(status, kDIALOG_STATUS_CHG_ATT_MASK)) {
|
|
// back off to the last value that worked and try again.
|
|
iset_buck = dialog_input_current_limit_step_down(charge_buck_control);
|
|
|
|
dialog_log_charger_limit(iset_buck);
|
|
|
|
// reduce the current limit to 100mA and ramp up again (to keep the slew rate intact).
|
|
charge_buck_control = kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_100;
|
|
|
|
// make sure charger is disabled first, so we can reduce the current limit
|
|
data = charge_buck_control;
|
|
dialog_set_data(CHARGER_IIC_BUS, kDIALOG_CHARGE_BUCK_CONTROL, data, true);
|
|
data = charge_buck_control | kDIALOG_CHARGE_BUCK_CONTROL_ISET_VBUS_CHG_BUCK_EN;
|
|
dialog_set_data(CHARGER_IIC_BUS, kDIALOG_CHARGE_BUCK_CONTROL, data, true);
|
|
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
#else
|
|
|
|
#if DIALOG_D1815
|
|
/* Raise the current limit 100mA. Keep a dwell time of 10ms between steps,
|
|
* except for 700-900mA, which requires 20ms.
|
|
*/
|
|
if (charge_buck_control == kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_700) {
|
|
task_sleep(20 * 1000);
|
|
} else {
|
|
task_sleep(10 * 1000);
|
|
}
|
|
#else
|
|
// 10ms per step (100mA)
|
|
task_sleep(10 * 1000);
|
|
#endif
|
|
|
|
#if DIALOG_D1815
|
|
/* For Ashley only, need to keep VBUS above VBUS_THR (4.3V). So if VBUS falls below 4.4V,
|
|
* stop increasing current limit. This is not needed on Angelina/Alison, which are fine
|
|
* well below 4.3V (down to VCENTER_DET).
|
|
*/
|
|
unsigned int vbus;
|
|
if (dialog_read_adc(CHARGER_IIC_BUS, kDIALOG_ADC_CONTROL_MUX_SEL_VBUS, &vbus) == 0) {
|
|
// VBUS has range of 0-5.5V (2.5V ADC scaled by 0.4545)
|
|
vbus = ((vbus * 5500) >> kDIALOG_ADC_RESOLUTION_BITS);
|
|
|
|
if (vbus < 4400) {
|
|
iset_buck = charge_buck_control;
|
|
dialog_log_charger_limit(iset_buck);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (charge_buck_control < kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_200) {
|
|
charge_buck_control = kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_200; // from 100mA to 200mA
|
|
} else if (charge_buck_control < kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_300) {
|
|
charge_buck_control = kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_300; // from 200mA to 300mA
|
|
} else if (charge_buck_control < kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_400) {
|
|
charge_buck_control = kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_400; // from 300mA to 400mA
|
|
} else if (charge_buck_control < kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_500) {
|
|
charge_buck_control = kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_500; // from 400mA to 500mA
|
|
} else if (charge_buck_control < kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_600) {
|
|
charge_buck_control = kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_600; // from 500mA to 600mA
|
|
} else if (charge_buck_control < kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_700) {
|
|
charge_buck_control = kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_700; // from 600mA to 700mA
|
|
} else if (charge_buck_control < kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_900) {
|
|
charge_buck_control = kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_900; // from 700mA to 900mA
|
|
} else {
|
|
charge_buck_control = kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_1000; // from 900mA to 1000mA
|
|
}
|
|
#else
|
|
if (charge_buck_control + kDIALOG_CHARGE_BUCK_CONTROL_STEP_PER_100MA < iset_buck) {
|
|
charge_buck_control += kDIALOG_CHARGE_BUCK_CONTROL_STEP_PER_100MA;
|
|
} else {
|
|
charge_buck_control = iset_buck;
|
|
}
|
|
#endif
|
|
|
|
#if DIALOG_D1755 || DIALOG_D1815
|
|
dialog_set_data(CHARGER_IIC_BUS, kDIALOG_CHARGE_BUCK_CONTROL,
|
|
charge_buck_control | kDIALOG_CHARGE_BUCK_CONTROL_ISET_VBUS_CHG_BUCK_EN, true);
|
|
#else
|
|
dialog_set_data(CHARGER_IIC_BUS, kDIALOG_CHARGE_BUCK_CONTROL, charge_buck_control, true);
|
|
#endif
|
|
|
|
#endif /* !DIALOG_D1755 */
|
|
}
|
|
|
|
iset_buck_limit = iset_buck;
|
|
#endif // !DIALOG_D2238
|
|
}
|
|
|
|
bool charger_charge_done(int dock)
|
|
{
|
|
statusRegisters status;
|
|
|
|
dialog_read_status(status);
|
|
|
|
return !STATUS_FLAG_TEST(status, kDIALOG_STATUS_CHARGING_MASK);
|
|
}
|
|
|
|
void charger_clear_usb_state(void)
|
|
{
|
|
iset_buck_limit = 0;
|
|
}
|
|
|
|
void charger_clear_alternate_usb_current_limit(void)
|
|
{
|
|
|
|
#if DIALOG_D1755 || DIALOG_D2238
|
|
/* no alternate current USB limit */
|
|
#elif DIALOG_D1815
|
|
/* Ashley has an alternate USB current limit on boot; switch to the programmed
|
|
* limit, but only after we've programmed it in the case of precharge.
|
|
*/
|
|
dialog_set_data(CHARGER_IIC_BUS, kDIALOG_ADC_BIST_CONTROL, kD1815_BIST_CONTROL_STD_USB_LIMIT, true);
|
|
#else
|
|
uint8_t data;
|
|
/* Angelina's alternate USB current limit is elsewhere */
|
|
dialog_get_data(CHARGER_IIC_BUS, kDIALOG_CHARGE_CONTROL_EN, &data);
|
|
data |= kDIALOG_CHARGE_CONTROL_ALT_USB_DIS;
|
|
dialog_set_data(CHARGER_IIC_BUS, kDIALOG_CHARGE_CONTROL_EN, data, true);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
bool
|
|
charger_has_batterypack(int dock)
|
|
{
|
|
// Use NTC to try to detect a disconnected battery, which will
|
|
// appear very cold. Since an extremely cold battery pack
|
|
// will also fail this check, do not do anything that would
|
|
// prevent the system from working correctly if it eventually
|
|
// warmed up as a result of this check.
|
|
int result;
|
|
unsigned adc;
|
|
|
|
result = dialog_read_adc(CHARGER_IIC_BUS, kDIALOG_ADC_CONTROL_MUX_SEL_TBAT, &adc);
|
|
|
|
// "Cold" is when the upper 9 bits of the ADC reading are all 1.
|
|
uint32_t max = (((1 << kDIALOG_ADC_RESOLUTION_BITS)-1) -
|
|
((1 << (kDIALOG_ADC_RESOLUTION_BITS-9))-1));
|
|
|
|
return (result == 0) && (adc < max);
|
|
}
|
|
|
|
bool
|
|
dialog_charger_check_usb_change(const eventRegisters ints_pending, const statusRegisters status)
|
|
{
|
|
bool powersupply_change_event = EVENT_REGISTER_TEST_MASK(kDialogEventPwrsupplyMask, ints_pending);
|
|
|
|
#if !DIALOG_D2238
|
|
// if VBUS was removed but charger is still attached, try reducing the current limit
|
|
bool usb_limited = false;
|
|
if (EVENT_REGISTER_TEST_MASK(kDialogEventUSBMask, ints_pending)
|
|
&& STATUS_FLAG_TEST(status, kDIALOG_STATUS_CHG_ATT_MASK)
|
|
&& (iset_buck_limit > kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_100))
|
|
{
|
|
// if input limit has been set back to 100mA when we expected it to be higher,
|
|
// but is still attached, there was an extraction (we cannot use VBUS_EXT_REM
|
|
// because at low charging voltage, VBUS may not be asserted).
|
|
UInt8 iset_buck;
|
|
dialog_get_data(CHARGER_IIC_BUS, kDIALOG_CHARGE_BUCK_CONTROL, &iset_buck);
|
|
if ((iset_buck & kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_MASK) == kDIALOG_CHARGE_BUCK_CONTROL_ISET_BUCK_100) {
|
|
usb_limited = true;
|
|
}
|
|
}
|
|
if (usb_limited)
|
|
{
|
|
iset_buck_limit = dialog_input_current_limit_step_down(iset_buck_limit);
|
|
dialog_log_charger_limit(iset_buck_limit);
|
|
|
|
if (power_enable_charging(true, true)) {
|
|
powersupply_change_event = false;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return powersupply_change_event;
|
|
}
|
|
|
|
int charger_set_data(int dev, uint16_t reg, uint8_t byte, bool do_confirm)
|
|
{
|
|
return dialog_set_data(dev, reg, byte, do_confirm);
|
|
}
|
|
|
|
int charger_get_data(int dev, uint16_t reg, uint8_t *byte)
|
|
{
|
|
return dialog_get_data(dev, reg, byte);
|
|
}
|
|
|
|
#if WITH_HW_CHARGER
|
|
bool
|
|
charger_check_usb_change(int dock, bool expected)
|
|
{
|
|
eventRegisters ints_pending;
|
|
statusRegisters status;
|
|
|
|
/* Read & clear interrupts */
|
|
dialog_read_events(ints_pending);
|
|
dialog_read_status(status);
|
|
|
|
return dialog_charger_check_usb_change(ints_pending, status);
|
|
}
|
|
|
|
void
|
|
charger_early_init(void)
|
|
{
|
|
int rc;
|
|
uint8_t data;
|
|
|
|
if (!power_is_suspended()) {
|
|
// make sure to clear any events
|
|
rc=dialog_get_data(CHARGER_IIC_BUS, kDIALOG_EVENT_A + kDIALOG_EVENT_COUNT - 1, &data);
|
|
}
|
|
|
|
#if DIALOG_D2231 || DIALOG_D2355
|
|
// <rdar://problem/21879203> J99 EVT not recognizing lightning connection
|
|
|
|
rc=charger_get_data(CHARGER_IIC_BUS, kDIALOG_OTP_RELOAD_CONF, &data);
|
|
if ( rc!=0 ) {
|
|
dprintf(DEBUG_CRITICAL, "charger: cannot read reg=%x\n", kDIALOG_OTP_RELOAD_CONF);
|
|
} else if ( (data&kDIALOG_OTP_SKIP_RELOAD_TO_ACT)==0 ) {
|
|
data|=kDIALOG_OTP_SKIP_RELOAD_TO_ACT; // OVERRIDE OTP_SKIP_RELOAD_TO_ACT
|
|
|
|
rc=charger_set_data(CHARGER_IIC_BUS, kDIALOG_TEST_ACCESS, kDIALOG_TEST_ACCESS_ENA, false); // enter testmode
|
|
if ( rc!=0 ) {
|
|
|
|
} else {
|
|
rc=charger_set_data(CHARGER_IIC_BUS, kDIALOG_OTP_RELOAD_CONF, data, false);
|
|
if ( rc!=0 ) {
|
|
|
|
} else {
|
|
task_sleep(10 * 1000); // 10ms delay, race on charger insertion
|
|
|
|
rc=charger_set_data(CHARGER_IIC_BUS, kDIALOG_OTP_RELOAD_CONF, data, false);
|
|
if ( rc!=0 ) dprintf(DEBUG_CRITICAL, "charger: cannot set SKIP_RELOAD_TO_ACT\n");
|
|
}
|
|
|
|
rc=charger_set_data(CHARGER_IIC_BUS, kDIALOG_TEST_ACCESS, kDIALOG_TEST_ACCESS_DIS, false); // exit testmode
|
|
if ( rc!=0 ) panic("charger: cannot exit charger test mode");
|
|
}
|
|
} else {
|
|
dprintf(DEBUG_INFO, "charger: kDIALOG_OTP_RELOAD_CONF ok\n");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void
|
|
charger_print_status(void)
|
|
{
|
|
// no status
|
|
}
|
|
|
|
#endif // WITH_HW_CHARGER
|
|
|
|
#endif /* !WITH_HW_CHARGER || WITH_HW_CHARGER_DIALOG */
|