blob: 0d56618d1fb3316317d60906ef848a79ec0d5107 [file] [log] [blame]
/*
* Copyright (C) 2014 Marvell International Ltd.
* Xuhong Gao <gaoxhong@marvell.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <power/pmic.h>
#include <power/battery.h>
#include <power/88pm801.h>
#ifdef CONFIG_BAT_PM802
#include <power/pm802.h>
#endif
#ifdef CONFIG_CHRG_BQ2419X
#include <power/bq2419x.h>
#endif
#ifdef CONFIG_CHRG_ETA6005
#include <power/eta6005.h>
#endif
#include <errno.h>
#include <power/power_chrg.h>
#include <linux/usb/mv-phy.h>
static u32 onkey_status;
static struct battery pm801_bat;
__weak void oled_show_bat_charging(u16 percent) {}
__weak void lcd_show_bat_charging(int percent) {}
__weak void lcd_panel_on(int on_off) {}
static struct pmic *g_pmic;
int charger_get_vbus(unsigned int *level)
{
unsigned int val;
if (pmic_probe(g_pmic)) {
printf("%s: charger power seems lost\n", __func__);
*level = 0;
return -ENODEV;
}
pmic_reg_read(g_pmic, 0x01, &val);
debug("pm801 base_reg01: 0x%02x\n", val);
if (val & (0x1<<1)) {
*level = 1;
printf("%s: USB cable is valid!\n", __func__);
} else {
*level = 0;
printf("%s: USB cable not valid!\n", __func__);
}
return 0;
}
static int pm801_battery_charge(struct pmic *bat)
{
struct power_battery *p_bat = bat->pbat;
struct battery *battery = p_bat->bat;
int i, k, current, chg_type, j, t, ret = CHG_STAT_NORMAL;
int mv_discharge, mv_charge = 0, mv_offset = 0;
static int chg_time = 0, chg_full_flag = 0;
u32 long_det_mask = (0x1 << LONG_ONKEY_BOOT) - 1;
static u32 backlight_on_time = BACKLIGHT_INIT_ON_SEC, backlight_off = 0;
onkey_status = 0;
chg_type = bat->chrg->chrg_type(p_bat->chrg);
switch (chg_type) {
case NULL_CHARGER:
printf("%s: no charger, power off...\n", __func__);
ret = CHG_STAT_CHGR_LOST;
goto out;
break;
case DCP_CHARGER:
case CDP_CHARGER:
/*
* charger spec supports 1500mA,
* but need to set as battery requirement
*/
current = DCP_FAST_CHG_CURRENT;
break;
case SDP_CHARGER:
case NONE_STANDARD_CHARGER:
case DEFAULT_CHARGER:
current = SDP_FAST_CHG_CURRENT;
break;
default:
current = SDP_FAST_CHG_CURRENT;
}
printf("chg_current: %dmA\n", current);
if (bat->pbat->bat->voltage_uV > BAT_DISCHARGE_MIN_UVOLT) {
bat->chrg->chrg_state(p_bat->chrg, CHARGER_DISABLE, current);
mdelay(50);
mv_discharge = bat->fg->fg_vbat_vol(p_bat->fg, bat);
if (bat->chrg->chrg_state(p_bat->chrg, CHARGER_ENABLE, current)) {
ret = CHG_STAT_ABNORMAL;
goto out;
}
mdelay(50);
mv_charge = bat->fg->fg_vbat_vol(p_bat->fg, bat);
mv_offset = mv_charge - mv_discharge;
printf("chgmv: %d, dischgmv: %d\n", mv_charge, mv_discharge);
if (mv_offset < 0)
mv_offset = 0;
if (mv_offset > 400)
mv_offset = 400;
}
/* loop until battery voltage raise up to 3.6V */
for (k = 0;; k++) {
if (!bat->chrg->chrg_bat_present(p_bat->chrg)) {
ret = CHG_STAT_BAT_LOST;
goto out;
}
if (!bat->chrg->chrg_type(p_bat->chrg)) {
ret = CHG_STAT_CHGR_LOST;
goto out;
}
i = UDELAY_INTERVAL / ONKEY_CHECK_INTERVAL_USEC;
while (i--) {
if (marvell88pm_get_onkey()) {
if (!backlight_on_time) {
backlight_on_time = BACKLIGHT_ON_SEC;
if (backlight_off) {
lcd_panel_on(1);
backlight_off = 0;
printf("turn on backlight\n");
}
} else {
backlight_on_time = BACKLIGHT_ON_SEC;
printf("push backlight off time\n");
}
}
udelay(ONKEY_CHECK_INTERVAL_USEC);
}
if (backlight_on_time)
backlight_on_time--;
if (!backlight_on_time) {
if (!backlight_off) {
lcd_panel_on(0);
backlight_off = 1;
printf("turn off backlight\n");
}
}
chg_time++;
printf("charge_time: %d\n", chg_time);
onkey_status = onkey_status << 1;
onkey_status |= (marvell88pm_get_onkey() & 0x1);
if ((onkey_status & long_det_mask) == long_det_mask) {
printf("long onkey detected, boot...\n");
onkey_status = 0;
ret = CHG_STAT_ONKEY_BOOT;
goto out;
}
bat->fg->fg_battery_update(p_bat->fg, bat, mv_offset);
if (p_bat->bat->state_of_chrg == 100)
chg_full_flag = 1;
if (chg_full_flag) {
oled_show_bat_charging(100);
if (!backlight_off)
lcd_show_bat_charging(100);
} else {
oled_show_bat_charging(p_bat->bat->state_of_chrg);
if (!backlight_off)
lcd_show_bat_charging(p_bat->bat->state_of_chrg);
}
if (k <= CHARGE_ONE_ROUND_SECS) {
printf(" %d [uV]\n", battery->voltage_uV);
puts("\n");
} else
/* return to have chance to check voltage */
goto out;
if (ctrlc()) {
printf("Charging disabled on request.\n");
goto exit;
}
}
exit:
bat->chrg->chrg_state(p_bat->chrg, CHARGER_DISABLE, 0);
out:
return ret;
}
static int pm801_battery_init(struct pmic *bat_,
struct pmic *fg_,
struct pmic *chrg_,
struct pmic *muic_)
{
struct pmic *p = chrg_;
int ret;
u32 data, id;
if (!p) {
printf("Charger invalid\n");
return -1;
}
bat_->pbat->fg = fg_;
bat_->pbat->chrg = chrg_;
bat_->pbat->muic = muic_;
if (fg_)
bat_->fg = fg_->fg;
if (chrg_)
bat_->chrg = chrg_->chrg;
if (muic_)
bat_->chrg->chrg_type = muic_->chrg->chrg_type;
#ifndef CONFIG_CHRG_ETA6005
/* init PM801 */
if (pmic_probe(p)) {
printf("Can't find 88pm801 battery\n");
return -1;
}
#endif
#ifdef CONFIG_CHRG_BQ2419X
/* disable hiz, input: 4.36v, 500mA */
data = 0x32;
pmic_reg_write(p, BQ2419X_REG_00, data);
/*
* reg not reset, watchdog no reset, chg disable,
* sys: 3.3V, BOOST LIM: 1.3A
*/
data = 0x07;
pmic_reg_write(p, BQ2419X_REG_01, data);
/* fastchg: 512mA */
data = 0x00;
pmic_reg_write(p, BQ2419X_REG_02, data);
/*prechg: 128mA, term: 128mA */
data = 0x00;
pmic_reg_write(p, BQ2419X_REG_03, data);
/* Fast chg: 4.208v, prechg2fastchg: 3v */
data = 0xB2;
pmic_reg_write(p, BQ2419X_REG_04, data);
/* Enable term, disable safety timer, disable watchdog, 8hrs, 50%jeita*/
data = 0x82;
pmic_reg_write(p, BQ2419X_REG_05, data);
/* thermal regulation: 120C */
data = 0x03;
pmic_reg_write(p, BQ2419X_REG_06, data);
/* Jeita 4.2v, int on fault, safety timer slowed by 2x */
data = 0x4B;
pmic_reg_write(p, BQ2419X_REG_07, data);
#endif
#ifdef CONFIG_BAT_PM803
if (pmic_is_pm803()) {
/* do nothing now */
}
#endif
#ifdef CONFIG_BAT_PM801
if (pmic_is_pm801() || pmic_is_pm812()) {
/* enable vinldo meas */
pmic_reg_read(fg_, 0x1, &data);
data |= (1 << 2);
pmic_reg_write(fg_, 0x01, data);
/* enable gpadc0/1 meas */
pmic_reg_read(fg_, 0x2, &data);
data |= ((1 << 2) | (1 << 3));
pmic_reg_write(fg_, 0x02, data);
/* enable meas, none stop mode, duty cycle mode*/
data = 0x33;
pmic_reg_write(fg_, 0x06, data);
}
#endif
return 0;
}
static struct power_battery power_bat_ops = {
.bat = &pm801_bat,
.battery_init = pm801_battery_init,
.battery_charge = pm801_battery_charge,
};
int power_bat_init(unsigned char bus)
{
static const char name[] = MARVELL_PMIC_BATT;
struct pmic *p = pmic_alloc();
if (!p) {
printf("%s: POWER allocation error!\n", __func__);
return -ENOMEM;
}
debug("Board BAT init\n");
p->name = name;
p->interface = PMIC_I2C;
p->number_of_regs = PM801_BAT_REG_NUM;
p->hw.i2c.addr = PM801_BAT_I2C_ADDR;
p->hw.i2c.tx_num = 1;
p->bus = bus;
p->pbat = &power_bat_ops;
g_pmic = p;
return 0;
}