| /* |
| * 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; |
| } |