blob: f87b038b95bf8f1377a41f1cccf8ef7a9b1e6705 [file] [log] [blame]
/*
* Copyright (C) 2014 Marvell International Ltd.
* Fenghang Yin <yinfh@marvell.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <power/pmic.h>
#include <power/fan540x.h>
#include <i2c.h>
#include <errno.h>
#include <power/power_chrg.h>
#include <power/battery.h>
/*depend on board design */
#define PM803_BAT_PRESENT_UP_THRESH (1250)
#define PM803_BAT_PRESENT_DOWN_TRESH (150)
#define PM802_BAT_PRESENT_UP_THRESH (1250)
#define PM802_BAT_PRESENT_DOWN_TRESH (50)
/*here we assume that nolimit is 2000mA */
static int input_cur_tbl[] = {100, 500, 800, 2000};
static int chg_current[] = {550, 650, 750, 850, 1050, 1150,1350, 1450};
static int fan540x_charger_type(struct pmic *p)
{
u32 val;
int charger;
/* if probe failed, return cable none */
if (pmic_probe(p)) {
printf("charger ic read failed\n");
return NULL_CHARGER;
}
/*
* just check the charger present status and if it's present, it will be
* recognized as USB charger
*/
charger = get_usb_charger_type();
return charger;
}
static int fan540x_charger_enable(struct pmic *p, int enable)
{
u32 data;
u32 val;
if (pmic_probe(p))
return -1;
/* clear CE and HZ_MODE */
data = enable ? 0x0 : (BIT1 | BIT2);
pmic_reg_read(p, FAN540X_REG_01, &val);
val &= ~(BIT1 | BIT2);
val |= data;
pmic_reg_write(p, FAN540X_REG_01, val);
pmic_reg_read(p, FAN540X_REG_01, &val);
debug("fan540x reg_01: %02x\n", val);
printf("fan540x charger is %s\n", (((val & (BIT1 | BIT2)) == 0x0) ?
"enabled" : "disable"));
return 0;
}
static int get_fastchg_cur(int fastchg_cur)
{
int i;
for (i = 0; i < sizeof(chg_current)/sizeof(int); i++) {
if (chg_current[i] < fastchg_cur)
continue;
else
break;
}
if (i == (sizeof(chg_current) / sizeof(int)))
i--;
return i & 0x7;
}
static int get_input_cur(int input_cur)
{
int i;
for (i = 0; i < sizeof(input_cur_tbl)/sizeof(int); i++) {
if (input_cur_tbl[i] < input_cur)
continue;
else
break;
}
if (i == (sizeof(input_cur_tbl) / sizeof(int)))
i--;
return i & 0x3;
}
static int fan540x_charger_state(struct pmic *p, int state, int current)
{
u32 val;
if (pmic_probe(p))
return -EINVAL;
if (state == CHARGER_DISABLE) {
debug("Disable the charger.\n");
fan540x_charger_enable(p, 0);
return 0;
}
/* reset the t32 timer */
pmic_reg_read(p, FAN540X_REG_00, &val);
val |= BIT7;
pmic_reg_write(p, FAN540X_REG_00, val);
/* set input current limit */
pmic_reg_read(p, FAN540X_REG_01, &val);
val &= ~(BIT6 | BIT7);
val |= get_input_cur(current) << 6;
pmic_reg_write(p, FAN540X_REG_01, val);
/* set chg current */
pmic_reg_read(p, FAN540X_REG_04, &val);
val &= ~(BIT4 | BIT5 | BIT6 | BIT7);
val |= get_fastchg_cur(1500) << 4;
pmic_reg_write(p, FAN540X_REG_04, val);
/* set iterm as battery cap/20 = 146mA */
pmic_reg_read(p, FAN540X_REG_04, &val);
val &= ~(BIT0 | BIT1 | BIT2 | BIT7);
val |= 0x2; /* 49, 97, 146, 194, ... */
pmic_reg_write(p, FAN540X_REG_04, val);
/* set chg voltage as 4.2v */
pmic_reg_read(p, FAN540X_REG_02, &val);
val &= 0x3;
val |= (0x23 << 2);
pmic_reg_write(p, FAN540X_REG_02, val);
/* controll output current by IOCHARGE */
pmic_reg_read(p, FAN540X_REG_05, &val);
val &= ~BIT5;
pmic_reg_write(p, FAN540X_REG_05, val);
/* set Vlowv as 3.4v */
pmic_reg_read(p, FAN540X_REG_01, &val);
val &= 0xcf;
pmic_reg_write(p, FAN540X_REG_01, val);
/* enable charging */
fan540x_charger_enable(p, 1);
return 0;
}
static int fan540x_charger_bat_present(struct pmic *p)
{
int volt;
struct pmic *p_fg;
if (pmic_probe(p))
return -1;
if (p->parent && p->parent->pbat && p->parent->pbat->fg) {
p_fg = p->parent->pbat->fg;
} else {
printf("%s: can't get pmic_fg\n", __func__);
return 0;
}
#ifdef CONFIG_FG_PM803
if (pmic_is_pm803()) {
volt = pmic_get_gpadc_bias_volt(p_fg, BAT_NTC_GPADC, BAT_PRESENT_CURRENT_SOC);
} else {
volt = pmic_get_gpadc_bias_volt(p_fg, BAT_NTC_GPADC, BAT_PRESENT_CURRENT);
}
#else
volt = pmic_get_gpadc_bias_volt(p_fg, BAT_NTC_GPADC, BAT_PRESENT_CURRENT);
#endif
debug("%s: bias mvolt = %dmV\n", __func__, volt);
#ifdef CONFIG_FG_PM803
if (pmic_is_pm803() && (volt <= PM803_BAT_PRESENT_UP_THRESH)
&& (volt >= PM803_BAT_PRESENT_DOWN_TRESH)) {
return 1;
} else if ((volt <= PM802_BAT_PRESENT_UP_THRESH)
&& (volt >= PM802_BAT_PRESENT_DOWN_TRESH)) {
return 1;
} else {
return 0;
}
#else
if ((volt <= PM802_BAT_PRESENT_UP_THRESH)
&& (volt >= PM802_BAT_PRESENT_DOWN_TRESH)) {
return 1;
} else {
return 0;
}
#endif
}
static struct power_chrg power_chrg_ops = {
.chrg_type = fan540x_charger_type,
.chrg_bat_present = fan540x_charger_bat_present,
.chrg_state = fan540x_charger_state,
};
static void fan540x_chrg_basic_init(struct pmic *p)
{
int val;
#if 0
/* reset the registers */
pmic_reg_read(p, FAN540X_REG_04, &val);
val |= BIT7;
pmic_reg_write(p, FAN540X_REG_04, val);
#endif
/* set ISAFE as 1450mA */
pmic_reg_read(p, FAN540X_REG_06, &val);
val &= ~(BIT4 | BIT5 | BIT6);
val |= (0x7 << 4);
pmic_reg_write(p, FAN540X_REG_06, val);
/* set VSAFE as 4.4V */
pmic_reg_read(p, FAN540X_REG_06, &val);
val &= 0xf0;
val |= (0x0A << 0);
pmic_reg_write(p, FAN540X_REG_06, val);
/* set Vlowv as 3.4v */
pmic_reg_read(p, FAN540X_REG_01, &val);
val &= 0xcf;
pmic_reg_write(p, FAN540X_REG_01, val);
/* set chg voltage as 4.2v */
pmic_reg_read(p, FAN540X_REG_02, &val);
val &= 0x3;
val |= (0x23 << 2);
pmic_reg_write(p, FAN540X_REG_02, val);
/* reset the t32 timer */
pmic_reg_read(p, FAN540X_REG_00, &val);
val |= BIT7;
pmic_reg_write(p, FAN540X_REG_00, val);
/* set INLIM as 500mA */
pmic_reg_read(p, FAN540X_REG_01, &val);
val &= ~(BIT6 | BIT7);
val |= (0x1 << 6);
pmic_reg_write(p, FAN540X_REG_01, val);
}
int power_chrg_init(unsigned char bus)
{
static const char name[] = MARVELL_PMIC_CHARGE;
struct pmic *p = pmic_alloc();
unsigned int buff;
int ret;
if (!p) {
printf("%s: POWER allocation error!\n", __func__);
return -ENOMEM;
}
debug("Board PMIC init\n");
p->name = name;
p->interface = PMIC_I2C;
p->number_of_regs = FAN540X_REG_NUM;
p->hw.i2c.addr = FAN540X_I2C_ADDR;
p->hw.i2c.tx_num = 1;
p->bus = bus;
p->chrg = &power_chrg_ops;
ret = pmic_reg_read(p, 0x0, &buff);
if (ret) {
printf("%s: can't find fan540x charger!\n", __func__);
return -ENODEV;
}
fan540x_chrg_basic_init(p);
return 0;
}