1210 lines
27 KiB
C
1210 lines
27 KiB
C
|
/* Copyright 2017 The Chromium OS Authors. All rights reserved.
|
||
|
* Use of this source code is governed by a BSD-style license that can be
|
||
|
* found in the LICENSE file.
|
||
|
*
|
||
|
* Richtek rt946x, Mediatek mt6370 battery charger driver.
|
||
|
*/
|
||
|
|
||
|
#include "battery.h"
|
||
|
#include "battery_smart.h"
|
||
|
#include "charger.h"
|
||
|
#include "charge_manager.h"
|
||
|
#include "common.h"
|
||
|
#include "compile_time_macros.h"
|
||
|
#include "config.h"
|
||
|
#include "console.h"
|
||
|
#include "hooks.h"
|
||
|
#include "i2c.h"
|
||
|
#include "printf.h"
|
||
|
#include "driver/wpc/p9221.h"
|
||
|
#include "rt946x.h"
|
||
|
#include "task.h"
|
||
|
#include "timer.h"
|
||
|
#include "usb_charge.h"
|
||
|
#include "util.h"
|
||
|
|
||
|
/* Console output macros */
|
||
|
#define CPRINTF(format, args...) cprintf(CC_CHARGER, format, ## args)
|
||
|
#define CPRINTS(format, args...) cprints(CC_CHARGER, "CHG " format, ## args)
|
||
|
|
||
|
|
||
|
/* Charger parameters */
|
||
|
static const struct charger_info rt946x_charger_info = {
|
||
|
.name = CHARGER_NAME,
|
||
|
.voltage_max = CHARGE_V_MAX,
|
||
|
.voltage_min = CHARGE_V_MIN,
|
||
|
.voltage_step = CHARGE_V_STEP,
|
||
|
.current_max = CHARGE_I_MAX,
|
||
|
.current_min = CHARGE_I_MIN,
|
||
|
.current_step = CHARGE_I_STEP,
|
||
|
.input_current_max = INPUT_I_MAX,
|
||
|
.input_current_min = INPUT_I_MIN,
|
||
|
.input_current_step = INPUT_I_STEP,
|
||
|
};
|
||
|
|
||
|
static const struct rt946x_init_setting default_init_setting = {
|
||
|
.eoc_current = 400,
|
||
|
.mivr = 4000,
|
||
|
.ircmp_vclamp = 32,
|
||
|
.ircmp_res = 25,
|
||
|
.boost_voltage = 5050,
|
||
|
.boost_current = 1500,
|
||
|
};
|
||
|
|
||
|
__attribute__((weak))
|
||
|
const struct rt946x_init_setting *board_rt946x_init_setting(void)
|
||
|
{
|
||
|
return &default_init_setting;
|
||
|
}
|
||
|
|
||
|
enum rt946x_ilmtsel {
|
||
|
RT946X_ILMTSEL_PSEL_OTG,
|
||
|
RT946X_ILMTSEL_AICR = 2,
|
||
|
RT946X_ILMTSEL_LOWER_LEVEL, /* lower of above two */
|
||
|
};
|
||
|
|
||
|
enum rt946x_chg_stat {
|
||
|
RT946X_CHGSTAT_READY = 0,
|
||
|
RT946X_CHGSTAT_IN_PROGRESS,
|
||
|
RT946X_CHGSTAT_DONE,
|
||
|
RT946X_CHGSTAT_FAULT,
|
||
|
};
|
||
|
|
||
|
enum rt946x_adc_in_sel {
|
||
|
RT946X_ADC_VBUS_DIV5 = 1,
|
||
|
RT946X_ADC_VBUS_DIV2,
|
||
|
};
|
||
|
|
||
|
#if defined(CONFIG_CHARGER_RT9466) || defined(CONFIG_CHARGER_RT9467)
|
||
|
enum rt946x_irq {
|
||
|
RT946X_IRQ_CHGSTATC = 0,
|
||
|
RT946X_IRQ_CHGFAULT,
|
||
|
RT946X_IRQ_TSSTATC,
|
||
|
RT946X_IRQ_CHGIRQ1,
|
||
|
RT946X_IRQ_CHGIRQ2,
|
||
|
RT946X_IRQ_CHGIRQ3,
|
||
|
#ifdef CONFIG_CHARGER_RT9467
|
||
|
RT946X_IRQ_DPDMIRQ,
|
||
|
#endif
|
||
|
RT946X_IRQ_COUNT,
|
||
|
};
|
||
|
|
||
|
static uint8_t rt946x_irqmask[RT946X_IRQ_COUNT] = {
|
||
|
0xF0, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF,
|
||
|
#ifdef CONFIG_CHARGER_RT9467
|
||
|
0xFC,
|
||
|
#endif
|
||
|
};
|
||
|
|
||
|
static const uint8_t rt946x_irq_maskall[RT946X_IRQ_COUNT] = {
|
||
|
0xF0, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF,
|
||
|
#ifdef CONFIG_CHARGER_RT9467
|
||
|
0xFF,
|
||
|
#endif
|
||
|
};
|
||
|
#elif defined(CONFIG_CHARGER_MT6370)
|
||
|
enum rt946x_irq {
|
||
|
MT6370_IRQ_CHGSTAT1 = 0,
|
||
|
MT6370_IRQ_CHGSTAT2,
|
||
|
MT6370_IRQ_CHGSTAT3,
|
||
|
MT6370_IRQ_CHGSTAT4,
|
||
|
MT6370_IRQ_CHGSTAT5,
|
||
|
MT6370_IRQ_CHGSTAT6,
|
||
|
MT6370_IRQ_DPDMSTAT,
|
||
|
MT6370_IRQ_DICHGSTAT,
|
||
|
MT6370_IRQ_OVPCTRLSTAT,
|
||
|
MT6370_IRQ_FLEDSTAT1,
|
||
|
MT6370_IRQ_FLEDSTAT2,
|
||
|
MT6370_IRQ_BASESTAT,
|
||
|
MT6370_IRQ_LDOSTAT,
|
||
|
MT6370_IRQ_RGBSTAT,
|
||
|
MT6370_IRQ_BLSTAT,
|
||
|
MT6370_IRQ_DBSTAT,
|
||
|
RT946X_IRQ_COUNT,
|
||
|
};
|
||
|
|
||
|
static uint8_t rt946x_irqmask[RT946X_IRQ_COUNT] = {
|
||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||
|
0xFF, 0xFC, 0xFF, 0xFF, 0xFF,
|
||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||
|
0xFF,
|
||
|
};
|
||
|
|
||
|
static const uint8_t rt946x_irq_maskall[RT946X_IRQ_COUNT] = {
|
||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||
|
0xFF,
|
||
|
};
|
||
|
#endif
|
||
|
|
||
|
/* Must be in ascending order */
|
||
|
static const uint16_t rt946x_boost_current[] = {
|
||
|
500, 700, 1100, 1300, 1800, 2100, 2400,
|
||
|
};
|
||
|
|
||
|
static int rt946x_read8(int reg, int *val)
|
||
|
{
|
||
|
return i2c_read8(I2C_PORT_CHARGER, RT946X_ADDR_FLAGS, reg, val);
|
||
|
}
|
||
|
|
||
|
static int rt946x_write8(int reg, int val)
|
||
|
{
|
||
|
return i2c_write8(I2C_PORT_CHARGER, RT946X_ADDR_FLAGS, reg, val);
|
||
|
}
|
||
|
|
||
|
static int rt946x_block_write(int reg, const uint8_t *val, int len)
|
||
|
{
|
||
|
return i2c_write_block(I2C_PORT_CHARGER, RT946X_ADDR_FLAGS,
|
||
|
reg, val, len);
|
||
|
}
|
||
|
|
||
|
static int rt946x_update_bits(int reg, int mask, int val)
|
||
|
{
|
||
|
int rv;
|
||
|
int reg_val = 0;
|
||
|
|
||
|
rv = rt946x_read8(reg, ®_val);
|
||
|
if (rv)
|
||
|
return rv;
|
||
|
reg_val &= ~mask;
|
||
|
reg_val |= (mask & val);
|
||
|
rv = rt946x_write8(reg, reg_val);
|
||
|
return rv;
|
||
|
}
|
||
|
|
||
|
static inline int rt946x_set_bit(int reg, int mask)
|
||
|
{
|
||
|
return rt946x_update_bits(reg, mask, mask);
|
||
|
}
|
||
|
|
||
|
static inline int rt946x_clr_bit(int reg, int mask)
|
||
|
{
|
||
|
return rt946x_update_bits(reg, mask, 0x00);
|
||
|
}
|
||
|
|
||
|
static inline uint8_t rt946x_closest_reg(uint16_t min, uint16_t max,
|
||
|
uint16_t step, uint16_t target)
|
||
|
{
|
||
|
if (target < min)
|
||
|
return 0;
|
||
|
if (target >= max)
|
||
|
return ((max - min) / step);
|
||
|
return (target - min) / step;
|
||
|
}
|
||
|
|
||
|
static int rt946x_chip_rev(int *chip_rev)
|
||
|
{
|
||
|
int rv;
|
||
|
|
||
|
rv = rt946x_read8(RT946X_REG_DEVICEID, chip_rev);
|
||
|
if (rv == EC_SUCCESS)
|
||
|
*chip_rev &= RT946X_MASK_CHIP_REV;
|
||
|
return rv;
|
||
|
}
|
||
|
|
||
|
static inline int rt946x_enable_wdt(int en)
|
||
|
{
|
||
|
return (en ? rt946x_set_bit : rt946x_clr_bit)
|
||
|
(RT946X_REG_CHGCTRL13, RT946X_MASK_WDT_EN);
|
||
|
}
|
||
|
|
||
|
/* Enable high-impedance mode */
|
||
|
static inline int rt946x_enable_hz(int en)
|
||
|
{
|
||
|
return (en ? rt946x_set_bit : rt946x_clr_bit)
|
||
|
(RT946X_REG_CHGCTRL1, RT946X_MASK_HZ_EN);
|
||
|
}
|
||
|
|
||
|
int rt946x_por_reset(void)
|
||
|
{
|
||
|
int rv, val;
|
||
|
|
||
|
#ifdef CONFIG_CHARGER_MT6370
|
||
|
/* Soft reset. It takes only 1ns for resetting. b/116682788 */
|
||
|
val = RT946X_MASK_SOFT_RST;
|
||
|
/*
|
||
|
* MT6370 has to set passcodes before resetting all the registers and
|
||
|
* logics.
|
||
|
*/
|
||
|
rv = rt946x_write8(MT6370_REG_RSTPASCODE1, MT6370_MASK_RSTPASCODE1);
|
||
|
rv |= rt946x_write8(MT6370_REG_RSTPASCODE2, MT6370_MASK_RSTPASCODE2);
|
||
|
#else
|
||
|
/* Hard reset, may take several milliseconds. */
|
||
|
val = RT946X_MASK_RST;
|
||
|
rv = rt946x_enable_hz(0);
|
||
|
#endif
|
||
|
if (rv)
|
||
|
return rv;
|
||
|
|
||
|
return rt946x_set_bit(RT946X_REG_CORECTRL_RST, val);
|
||
|
}
|
||
|
|
||
|
static int rt946x_reset_to_zero(void)
|
||
|
{
|
||
|
int rv = 0;
|
||
|
|
||
|
rv = charger_set_current(0);
|
||
|
if (rv)
|
||
|
return rv;
|
||
|
|
||
|
rv = charger_set_voltage(0);
|
||
|
if (rv)
|
||
|
return rv;
|
||
|
|
||
|
return rt946x_enable_hz(1);
|
||
|
}
|
||
|
|
||
|
static int rt946x_enable_bc12_detection(int en)
|
||
|
{
|
||
|
#if defined(CONFIG_CHARGER_RT9467) || defined(CONFIG_CHARGER_MT6370)
|
||
|
#ifdef CONFIG_CHARGER_MT6370_BC12_GPIO
|
||
|
gpio_set_level(GPIO_BC12_DET_EN, en);
|
||
|
#endif /* CONFIG_CHARGER_MT6370_BC12_GPIO */
|
||
|
return (en ? rt946x_set_bit : rt946x_clr_bit)
|
||
|
(RT946X_REG_DPDM1, RT946X_MASK_USBCHGEN);
|
||
|
#endif
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int rt946x_set_ieoc(unsigned int ieoc)
|
||
|
{
|
||
|
uint8_t reg_ieoc = 0;
|
||
|
|
||
|
reg_ieoc = rt946x_closest_reg(RT946X_IEOC_MIN, RT946X_IEOC_MAX,
|
||
|
RT946X_IEOC_STEP, ieoc);
|
||
|
|
||
|
CPRINTF("%s ieoc = %d(0x%02X)\n", __func__, ieoc, reg_ieoc);
|
||
|
|
||
|
return rt946x_update_bits(RT946X_REG_CHGCTRL9, RT946X_MASK_IEOC,
|
||
|
reg_ieoc << RT946X_SHIFT_IEOC);
|
||
|
}
|
||
|
|
||
|
static int rt946x_set_mivr(unsigned int mivr)
|
||
|
{
|
||
|
uint8_t reg_mivr = 0;
|
||
|
|
||
|
reg_mivr = rt946x_closest_reg(RT946X_MIVR_MIN, RT946X_MIVR_MAX,
|
||
|
RT946X_MIVR_STEP, mivr);
|
||
|
|
||
|
CPRINTF("%s: mivr = %d(0x%02X)\n", __func__, mivr, reg_mivr);
|
||
|
|
||
|
return rt946x_update_bits(RT946X_REG_CHGCTRL6, RT946X_MASK_MIVR,
|
||
|
reg_mivr << RT946X_SHIFT_MIVR);
|
||
|
}
|
||
|
|
||
|
static int rt946x_set_boost_voltage(unsigned int voltage)
|
||
|
{
|
||
|
uint8_t reg_voltage = 0;
|
||
|
|
||
|
reg_voltage = rt946x_closest_reg(RT946X_BOOST_VOLTAGE_MIN,
|
||
|
RT946X_BOOST_VOLTAGE_MAX, RT946X_BOOST_VOLTAGE_STEP, voltage);
|
||
|
|
||
|
CPRINTF("%s voltage = %d(0x%02X)\n", __func__, voltage, reg_voltage);
|
||
|
|
||
|
return rt946x_update_bits(RT946X_REG_CHGCTRL5,
|
||
|
RT946X_MASK_BOOST_VOLTAGE,
|
||
|
reg_voltage << RT946X_SHIFT_BOOST_VOLTAGE);
|
||
|
}
|
||
|
|
||
|
static int rt946x_set_boost_current(unsigned int current)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
/*
|
||
|
* Find the smallest output current threshold which can support
|
||
|
* our requested output current. Use the greatest achievable
|
||
|
* boost current (2.4A) if requested current is too large.
|
||
|
*/
|
||
|
for (i = 0; i < ARRAY_SIZE(rt946x_boost_current) - 1; i++) {
|
||
|
if (current < rt946x_boost_current[i])
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
CPRINTF("%s current = %d(0x%02X)\n", __func__, current, i);
|
||
|
|
||
|
return rt946x_update_bits(RT946X_REG_CHGCTRL10,
|
||
|
RT946X_MASK_BOOST_CURRENT,
|
||
|
i << RT946X_SHIFT_BOOST_CURRENT);
|
||
|
}
|
||
|
|
||
|
static int rt946x_set_ircmp_vclamp(unsigned int vclamp)
|
||
|
{
|
||
|
uint8_t reg_vclamp = 0;
|
||
|
|
||
|
reg_vclamp = rt946x_closest_reg(RT946X_IRCMP_VCLAMP_MIN,
|
||
|
RT946X_IRCMP_VCLAMP_MAX, RT946X_IRCMP_VCLAMP_STEP, vclamp);
|
||
|
|
||
|
CPRINTF("%s: vclamp = %d(0x%02X)\n", __func__, vclamp, reg_vclamp);
|
||
|
|
||
|
return rt946x_update_bits(RT946X_REG_CHGCTRL18,
|
||
|
RT946X_MASK_IRCMP_VCLAMP,
|
||
|
reg_vclamp << RT946X_SHIFT_IRCMP_VCLAMP);
|
||
|
}
|
||
|
|
||
|
static int rt946x_set_ircmp_res(unsigned int res)
|
||
|
{
|
||
|
uint8_t reg_res = 0;
|
||
|
|
||
|
reg_res = rt946x_closest_reg(RT946X_IRCMP_RES_MIN, RT946X_IRCMP_RES_MAX,
|
||
|
RT946X_IRCMP_RES_STEP, res);
|
||
|
|
||
|
CPRINTF("%s: res = %d(0x%02X)\n", __func__, res, reg_res);
|
||
|
|
||
|
return rt946x_update_bits(RT946X_REG_CHGCTRL18, RT946X_MASK_IRCMP_RES,
|
||
|
reg_res << RT946X_SHIFT_IRCMP_RES);
|
||
|
}
|
||
|
|
||
|
static int rt946x_set_vprec(unsigned int vprec)
|
||
|
{
|
||
|
uint8_t reg_vprec = 0;
|
||
|
|
||
|
reg_vprec = rt946x_closest_reg(RT946X_VPREC_MIN, RT946X_VPREC_MAX,
|
||
|
RT946X_VPREC_STEP, vprec);
|
||
|
|
||
|
CPRINTF("%s: vprec = %d(0x%02X)\n", __func__, vprec, reg_vprec);
|
||
|
|
||
|
return rt946x_update_bits(RT946X_REG_CHGCTRL8, RT946X_MASK_VPREC,
|
||
|
reg_vprec << RT946X_SHIFT_VPREC);
|
||
|
}
|
||
|
|
||
|
static int rt946x_set_iprec(unsigned int iprec)
|
||
|
{
|
||
|
uint8_t reg_iprec = 0;
|
||
|
|
||
|
reg_iprec = rt946x_closest_reg(RT946X_IPREC_MIN, RT946X_IPREC_MAX,
|
||
|
RT946X_IPREC_STEP, iprec);
|
||
|
|
||
|
CPRINTF("%s: iprec = %d(0x%02X)\n", __func__, iprec, reg_iprec);
|
||
|
|
||
|
return rt946x_update_bits(RT946X_REG_CHGCTRL8, RT946X_MASK_IPREC,
|
||
|
reg_iprec << RT946X_SHIFT_IPREC);
|
||
|
}
|
||
|
|
||
|
static int rt946x_init_irq(void)
|
||
|
{
|
||
|
int rv = 0;
|
||
|
int dummy;
|
||
|
int i;
|
||
|
|
||
|
/* Mask all interrupts */
|
||
|
rv = rt946x_block_write(RT946X_REG_CHGSTATCCTRL, rt946x_irq_maskall,
|
||
|
RT946X_IRQ_COUNT);
|
||
|
if (rv)
|
||
|
return rv;
|
||
|
|
||
|
/* Clear all interrupt flags */
|
||
|
for (i = 0; i < RT946X_IRQ_COUNT; i++) {
|
||
|
rv = rt946x_read8(RT946X_REG_CHGSTATC + i, &dummy);
|
||
|
if (rv)
|
||
|
return rv;
|
||
|
}
|
||
|
|
||
|
/* Init interrupt */
|
||
|
return rt946x_block_write(RT946X_REG_CHGSTATCCTRL, rt946x_irqmask,
|
||
|
ARRAY_SIZE(rt946x_irqmask));
|
||
|
}
|
||
|
|
||
|
static int rt946x_init_setting(void)
|
||
|
{
|
||
|
int rv = 0;
|
||
|
const struct battery_info *batt_info = battery_get_info();
|
||
|
const struct rt946x_init_setting *setting = board_rt946x_init_setting();
|
||
|
|
||
|
#ifdef CONFIG_CHARGER_OTG
|
||
|
/* Disable boost-mode output voltage */
|
||
|
rv = charger_enable_otg_power(0);
|
||
|
if (rv)
|
||
|
return rv;
|
||
|
#endif
|
||
|
/* Enable/Disable BC 1.2 detection */
|
||
|
#ifdef HAS_TASK_USB_CHG
|
||
|
rv = rt946x_enable_bc12_detection(1);
|
||
|
#else
|
||
|
rv = rt946x_enable_bc12_detection(0);
|
||
|
#endif
|
||
|
if (rv)
|
||
|
return rv;
|
||
|
/* Disable WDT */
|
||
|
rv = rt946x_enable_wdt(0);
|
||
|
if (rv)
|
||
|
return rv;
|
||
|
/* Disable battery thermal protection */
|
||
|
rv = rt946x_clr_bit(RT946X_REG_CHGCTRL16, RT946X_MASK_JEITA_EN);
|
||
|
if (rv)
|
||
|
return rv;
|
||
|
/* Disable charge timer */
|
||
|
rv = rt946x_clr_bit(RT946X_REG_CHGCTRL12, RT946X_MASK_TMR_EN);
|
||
|
if (rv)
|
||
|
return rv;
|
||
|
rv = rt946x_set_mivr(setting->mivr);
|
||
|
if (rv)
|
||
|
return rv;
|
||
|
rv = rt946x_set_ieoc(setting->eoc_current);
|
||
|
if (rv)
|
||
|
return rv;
|
||
|
rv = rt946x_set_boost_voltage(
|
||
|
setting->boost_voltage);
|
||
|
if (rv)
|
||
|
return rv;
|
||
|
rv = rt946x_set_boost_current(
|
||
|
setting->boost_current);
|
||
|
if (rv)
|
||
|
return rv;
|
||
|
rv = rt946x_set_ircmp_vclamp(setting->ircmp_vclamp);
|
||
|
if (rv)
|
||
|
return rv;
|
||
|
rv = rt946x_set_ircmp_res(setting->ircmp_res);
|
||
|
if (rv)
|
||
|
return rv;
|
||
|
rv = rt946x_set_vprec(batt_info->precharge_voltage ?
|
||
|
batt_info->precharge_voltage : batt_info->voltage_min);
|
||
|
if (rv)
|
||
|
return rv;
|
||
|
rv = rt946x_set_iprec(batt_info->precharge_current);
|
||
|
if (rv)
|
||
|
return rv;
|
||
|
|
||
|
#ifdef CONFIG_CHARGER_MT6370_BACKLIGHT
|
||
|
rt946x_write8(MT6370_BACKLIGHT_BLEN,
|
||
|
MT6370_MASK_BLED_EXT_EN | MT6370_MASK_BLED_EN |
|
||
|
MT6370_MASK_BLED_1CH_EN | MT6370_MASK_BLED_2CH_EN |
|
||
|
MT6370_MASK_BLED_3CH_EN | MT6370_MASK_BLED_4CH_EN |
|
||
|
MT6370_BLED_CODE_LINEAR);
|
||
|
rt946x_update_bits(MT6370_BACKLIGHT_BLPWM, MT6370_MASK_BLPWM_BLED_PWM,
|
||
|
BIT(MT6370_SHIFT_BLPWM_BLED_PWM));
|
||
|
#endif
|
||
|
|
||
|
return rt946x_init_irq();
|
||
|
}
|
||
|
|
||
|
#ifdef CONFIG_CHARGER_OTG
|
||
|
int charger_enable_otg_power(int enabled)
|
||
|
{
|
||
|
return (enabled ? rt946x_set_bit : rt946x_clr_bit)
|
||
|
(RT946X_REG_CHGCTRL1, RT946X_MASK_OPA_MODE);
|
||
|
}
|
||
|
|
||
|
int charger_is_sourcing_otg_power(int port)
|
||
|
{
|
||
|
int val;
|
||
|
|
||
|
if (rt946x_read8(RT946X_REG_CHGCTRL1, &val))
|
||
|
return 0;
|
||
|
|
||
|
return !!(val & RT946X_MASK_OPA_MODE);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
int charger_set_input_current(int input_current)
|
||
|
{
|
||
|
uint8_t reg_iin = 0;
|
||
|
const struct charger_info * const info = charger_get_info();
|
||
|
|
||
|
reg_iin = rt946x_closest_reg(info->input_current_min,
|
||
|
info->input_current_max, info->input_current_step,
|
||
|
input_current);
|
||
|
|
||
|
CPRINTF("%s iin = %d(0x%02X)\n", __func__, input_current, reg_iin);
|
||
|
|
||
|
return rt946x_update_bits(RT946X_REG_CHGCTRL3, RT946X_MASK_AICR,
|
||
|
reg_iin << RT946X_SHIFT_AICR);
|
||
|
}
|
||
|
|
||
|
int charger_get_input_current(int *input_current)
|
||
|
{
|
||
|
int rv;
|
||
|
int val = 0;
|
||
|
const struct charger_info * const info = charger_get_info();
|
||
|
|
||
|
rv = rt946x_read8(RT946X_REG_CHGCTRL3, &val);
|
||
|
if (rv)
|
||
|
return rv;
|
||
|
|
||
|
val = (val & RT946X_MASK_AICR) >> RT946X_SHIFT_AICR;
|
||
|
*input_current = val * info->input_current_step
|
||
|
+ info->input_current_min;
|
||
|
|
||
|
return EC_SUCCESS;
|
||
|
}
|
||
|
|
||
|
int charger_manufacturer_id(int *id)
|
||
|
{
|
||
|
return EC_ERROR_UNIMPLEMENTED;
|
||
|
}
|
||
|
|
||
|
int charger_device_id(int *id)
|
||
|
{
|
||
|
int rv;
|
||
|
|
||
|
rv = rt946x_read8(RT946X_REG_DEVICEID, id);
|
||
|
if (rv == EC_SUCCESS)
|
||
|
*id &= RT946X_MASK_VENDOR_ID;
|
||
|
return rv;
|
||
|
}
|
||
|
|
||
|
int charger_get_option(int *option)
|
||
|
{
|
||
|
/* Ignored: does not exist */
|
||
|
*option = 0;
|
||
|
return EC_SUCCESS;
|
||
|
}
|
||
|
|
||
|
int charger_set_option(int option)
|
||
|
{
|
||
|
/* Ignored: does not exist */
|
||
|
return EC_SUCCESS;
|
||
|
}
|
||
|
|
||
|
const struct charger_info *charger_get_info(void)
|
||
|
{
|
||
|
return &rt946x_charger_info;
|
||
|
}
|
||
|
|
||
|
int charger_get_status(int *status)
|
||
|
{
|
||
|
int rv;
|
||
|
int val = 0;
|
||
|
|
||
|
rv = rt946x_read8(RT946X_REG_CHGCTRL2, &val);
|
||
|
if (rv)
|
||
|
return rv;
|
||
|
val = (val & RT946X_MASK_CHG_EN) >> RT946X_SHIFT_CHG_EN;
|
||
|
if (!val)
|
||
|
*status |= CHARGER_CHARGE_INHIBITED;
|
||
|
|
||
|
rv = rt946x_read8(RT946X_REG_CHGFAULT, &val);
|
||
|
if (rv)
|
||
|
return rv;
|
||
|
if (val & RT946X_MASK_CHG_VBATOV)
|
||
|
*status |= CHARGER_VOLTAGE_OR;
|
||
|
|
||
|
|
||
|
rv = rt946x_read8(RT946X_REG_CHGNTC, &val);
|
||
|
if (rv)
|
||
|
return rv;
|
||
|
val = (val & RT946X_MASK_BATNTC_FAULT) >> RT946X_SHIFT_BATNTC_FAULT;
|
||
|
|
||
|
switch (val) {
|
||
|
case RT946X_BATTEMP_WARM:
|
||
|
*status |= CHARGER_RES_HOT;
|
||
|
break;
|
||
|
case RT946X_BATTEMP_COOL:
|
||
|
*status |= CHARGER_RES_COLD;
|
||
|
break;
|
||
|
case RT946X_BATTEMP_COLD:
|
||
|
*status |= CHARGER_RES_COLD;
|
||
|
*status |= CHARGER_RES_UR;
|
||
|
break;
|
||
|
case RT946X_BATTEMP_HOT:
|
||
|
*status |= CHARGER_RES_HOT;
|
||
|
*status |= CHARGER_RES_OR;
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return EC_SUCCESS;
|
||
|
}
|
||
|
|
||
|
int charger_set_mode(int mode)
|
||
|
{
|
||
|
int rv;
|
||
|
|
||
|
if (mode & CHARGE_FLAG_POR_RESET) {
|
||
|
rv = rt946x_por_reset();
|
||
|
if (rv)
|
||
|
return rv;
|
||
|
}
|
||
|
|
||
|
if (mode & CHARGE_FLAG_RESET_TO_ZERO) {
|
||
|
rv = rt946x_reset_to_zero();
|
||
|
if (rv)
|
||
|
return rv;
|
||
|
}
|
||
|
|
||
|
return EC_SUCCESS;
|
||
|
}
|
||
|
|
||
|
int charger_get_current(int *current)
|
||
|
{
|
||
|
int rv;
|
||
|
int val = 0;
|
||
|
const struct charger_info * const info = charger_get_info();
|
||
|
|
||
|
rv = rt946x_read8(RT946X_REG_CHGCTRL7, &val);
|
||
|
if (rv)
|
||
|
return rv;
|
||
|
|
||
|
val = (val & RT946X_MASK_ICHG) >> RT946X_SHIFT_ICHG;
|
||
|
*current = val * info->current_step + info->current_min;
|
||
|
|
||
|
return EC_SUCCESS;
|
||
|
}
|
||
|
|
||
|
int charger_set_current(int current)
|
||
|
{
|
||
|
uint8_t reg_icc = 0;
|
||
|
const struct charger_info * const info = charger_get_info();
|
||
|
|
||
|
reg_icc = rt946x_closest_reg(info->current_min, info->current_max,
|
||
|
info->current_step, current);
|
||
|
|
||
|
return rt946x_update_bits(RT946X_REG_CHGCTRL7, RT946X_MASK_ICHG,
|
||
|
reg_icc << RT946X_SHIFT_ICHG);
|
||
|
}
|
||
|
|
||
|
int charger_get_voltage(int *voltage)
|
||
|
{
|
||
|
int rv;
|
||
|
int val = 0;
|
||
|
const struct charger_info * const info = charger_get_info();
|
||
|
|
||
|
rv = rt946x_read8(RT946X_REG_CHGCTRL4, &val);
|
||
|
if (rv)
|
||
|
return rv;
|
||
|
|
||
|
val = (val & RT946X_MASK_CV) >> RT946X_SHIFT_CV;
|
||
|
*voltage = val * info->voltage_step + info->voltage_min;
|
||
|
|
||
|
return EC_SUCCESS;
|
||
|
}
|
||
|
|
||
|
int charger_set_voltage(int voltage)
|
||
|
{
|
||
|
uint8_t reg_cv = 0;
|
||
|
const struct charger_info * const info = charger_get_info();
|
||
|
|
||
|
reg_cv = rt946x_closest_reg(info->voltage_min, info->voltage_max,
|
||
|
info->voltage_step, voltage);
|
||
|
|
||
|
return rt946x_update_bits(RT946X_REG_CHGCTRL4, RT946X_MASK_CV,
|
||
|
reg_cv << RT946X_SHIFT_CV);
|
||
|
}
|
||
|
|
||
|
int charger_discharge_on_ac(int enable)
|
||
|
{
|
||
|
return rt946x_enable_hz(enable);
|
||
|
}
|
||
|
|
||
|
int charger_get_vbus_voltage(int port)
|
||
|
{
|
||
|
int val;
|
||
|
static int vbus_mv;
|
||
|
int retries = 10;
|
||
|
|
||
|
/* Set VBUS as ADC input */
|
||
|
rt946x_update_bits(RT946X_REG_CHGADC, RT946X_MASK_ADC_IN_SEL,
|
||
|
RT946X_ADC_VBUS_DIV5 << RT946X_SHIFT_ADC_IN_SEL);
|
||
|
|
||
|
/* Start ADC conversion */
|
||
|
rt946x_set_bit(RT946X_REG_CHGADC, RT946X_MASK_ADC_START);
|
||
|
|
||
|
/*
|
||
|
* In practice, ADC conversion rarely takes more than 35ms.
|
||
|
* However, according to the datasheet, ADC conversion may take
|
||
|
* up to 200ms. But we can't wait for that long, otherwise
|
||
|
* host command would time out. So here we set ADC timeout as 50ms.
|
||
|
* If ADC times out, we just return the last read vbus_mv.
|
||
|
*
|
||
|
* TODO(chromium:820335): We may handle this more gracefully with
|
||
|
* EC_RES_IN_PROGRESS.
|
||
|
*/
|
||
|
while (--retries) {
|
||
|
rt946x_read8(RT946X_REG_CHGSTAT, &val);
|
||
|
if (!(val & RT946X_MASK_ADC_STAT))
|
||
|
break;
|
||
|
msleep(5);
|
||
|
}
|
||
|
|
||
|
if (retries) {
|
||
|
/* Read measured results if ADC finishes in time. */
|
||
|
rt946x_read8(RT946X_REG_ADCDATAL, &vbus_mv);
|
||
|
rt946x_read8(RT946X_REG_ADCDATAH, &val);
|
||
|
vbus_mv |= (val << 8);
|
||
|
vbus_mv *= 25;
|
||
|
}
|
||
|
|
||
|
return vbus_mv;
|
||
|
}
|
||
|
|
||
|
/* Setup sourcing current to prevent overload */
|
||
|
#ifdef CONFIG_CHARGER_ILIM_PIN_DISABLED
|
||
|
static int rt946x_enable_ilim_pin(int en)
|
||
|
{
|
||
|
int ret;
|
||
|
|
||
|
ret = (en ? rt946x_set_bit : rt946x_clr_bit)
|
||
|
(RT946X_REG_CHGCTRL3, RT946X_MASK_ILIMEN);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static int rt946x_select_ilmt(enum rt946x_ilmtsel sel)
|
||
|
{
|
||
|
int ret;
|
||
|
|
||
|
ret = rt946x_update_bits(RT946X_REG_CHGCTRL2, RT946X_MASK_ILMTSEL,
|
||
|
sel << RT946X_SHIFT_ILMTSEL);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
#endif /* CONFIG_CHARGER_ILIM_PIN_DISABLED */
|
||
|
|
||
|
/* Charging power state initialization */
|
||
|
int charger_post_init(void)
|
||
|
{
|
||
|
#ifdef CONFIG_CHARGER_ILIM_PIN_DISABLED
|
||
|
int rv;
|
||
|
|
||
|
rv = rt946x_select_ilmt(RT946X_ILMTSEL_AICR);
|
||
|
if (rv)
|
||
|
return rv;
|
||
|
/* Disable ILIM pin */
|
||
|
rv = rt946x_enable_ilim_pin(0);
|
||
|
if (rv)
|
||
|
return rv;
|
||
|
#endif
|
||
|
return EC_SUCCESS;
|
||
|
}
|
||
|
|
||
|
/* Hardware current ramping (aka AICL: Average Input Current Level) */
|
||
|
#ifdef CONFIG_CHARGE_RAMP_HW
|
||
|
static int rt946x_get_mivr(int *mivr)
|
||
|
{
|
||
|
int rv;
|
||
|
int val = 0;
|
||
|
|
||
|
rv = rt946x_read8(RT946X_REG_CHGCTRL6, &val);
|
||
|
if (rv)
|
||
|
return rv;
|
||
|
|
||
|
val = (val & RT946X_MASK_MIVR) >> RT946X_SHIFT_MIVR;
|
||
|
*mivr = val * RT946X_MIVR_STEP + RT946X_MIVR_MIN;
|
||
|
|
||
|
return EC_SUCCESS;
|
||
|
}
|
||
|
|
||
|
static int rt946x_set_aicl_vth(uint8_t aicl_vth)
|
||
|
{
|
||
|
uint8_t reg_aicl_vth = 0;
|
||
|
|
||
|
reg_aicl_vth = rt946x_closest_reg(RT946X_AICLVTH_MIN,
|
||
|
RT946X_AICLVTH_MAX, RT946X_AICLVTH_STEP, aicl_vth);
|
||
|
|
||
|
return rt946x_update_bits(RT946X_REG_CHGCTRL14, RT946X_MASK_AICLVTH,
|
||
|
reg_aicl_vth << RT946X_SHIFT_AICLVTH);
|
||
|
}
|
||
|
|
||
|
int charger_set_hw_ramp(int enable)
|
||
|
{
|
||
|
int rv;
|
||
|
unsigned int mivr = 0;
|
||
|
|
||
|
if (!enable) {
|
||
|
rv = rt946x_clr_bit(RT946X_REG_CHGCTRL14, RT946X_MASK_AICLMEAS);
|
||
|
return rv;
|
||
|
}
|
||
|
|
||
|
rv = rt946x_get_mivr(&mivr);
|
||
|
if (rv < 0)
|
||
|
return rv;
|
||
|
|
||
|
/*
|
||
|
* Check if there's a suitable AICL_VTH.
|
||
|
* The vendor suggests setting AICL_VTH as (MIVR + 200mV).
|
||
|
*/
|
||
|
if ((mivr + 200) > RT946X_AICLVTH_MAX) {
|
||
|
CPRINTF("%s: no suitable vth, mivr = %d\n", __func__, mivr);
|
||
|
return EC_ERROR_INVAL;
|
||
|
}
|
||
|
|
||
|
rv = rt946x_set_aicl_vth(mivr + 200);
|
||
|
if (rv < 0)
|
||
|
return rv;
|
||
|
|
||
|
return rt946x_set_bit(RT946X_REG_CHGCTRL14, RT946X_MASK_AICLMEAS);
|
||
|
}
|
||
|
|
||
|
int chg_ramp_is_stable(void)
|
||
|
{
|
||
|
int rv;
|
||
|
int val = 0;
|
||
|
|
||
|
rv = rt946x_read8(RT946X_REG_CHGCTRL14, &val);
|
||
|
val = (val & RT946X_MASK_AICLMEAS) >> RT946X_SHIFT_AICLMEAS;
|
||
|
|
||
|
return (!rv && !val);
|
||
|
}
|
||
|
|
||
|
int chg_ramp_is_detected(void)
|
||
|
{
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
int chg_ramp_get_current_limit(void)
|
||
|
{
|
||
|
int rv;
|
||
|
int input_current = 0;
|
||
|
|
||
|
rv = charger_get_input_current(&input_current);
|
||
|
|
||
|
return rv ? -1 : input_current;
|
||
|
}
|
||
|
#endif /* CONFIG_CHARGE_RAMP_HW */
|
||
|
|
||
|
static void rt946x_init(void)
|
||
|
{
|
||
|
int reg = 0xFFFFFFFF;
|
||
|
|
||
|
/* Check device id */
|
||
|
if (charger_device_id(®) || reg != RT946X_VENDOR_ID) {
|
||
|
CPRINTF("RT946X incorrect ID: 0x%02x\n", reg);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* Check revision id */
|
||
|
if (rt946x_chip_rev(®)) {
|
||
|
CPRINTF("Failed to read RT946X CHIP REV\n");
|
||
|
return;
|
||
|
}
|
||
|
CPRINTF("RT946X CHIP REV: 0x%02x\n", reg);
|
||
|
|
||
|
if (rt946x_init_setting()) {
|
||
|
CPRINTF("RT946X init failed\n");
|
||
|
return;
|
||
|
}
|
||
|
CPRINTF("RT946X init succeeded\n");
|
||
|
}
|
||
|
DECLARE_HOOK(HOOK_INIT, rt946x_init, HOOK_PRIO_INIT_I2C + 1);
|
||
|
|
||
|
#ifdef HAS_TASK_USB_CHG
|
||
|
static int rt946x_get_bc12_device_type(void)
|
||
|
{
|
||
|
int reg;
|
||
|
|
||
|
#if defined(CONFIG_CHARGER_RT9466) || defined(CONFIG_CHARGER_RT9467)
|
||
|
if (rt946x_read8(RT946X_REG_DPDM1, ®))
|
||
|
return CHARGE_SUPPLIER_NONE;
|
||
|
|
||
|
switch (reg & RT946X_MASK_BC12_TYPE) {
|
||
|
case RT946X_MASK_SDP:
|
||
|
return CHARGE_SUPPLIER_BC12_SDP;
|
||
|
case RT946X_MASK_CDP:
|
||
|
return CHARGE_SUPPLIER_BC12_CDP;
|
||
|
case RT946X_MASK_DCP:
|
||
|
return CHARGE_SUPPLIER_BC12_DCP;
|
||
|
default:
|
||
|
return CHARGE_SUPPLIER_NONE;
|
||
|
}
|
||
|
#elif defined(CONFIG_CHARGER_MT6370)
|
||
|
if (rt946x_read8(MT6370_REG_USBSTATUS1, ®))
|
||
|
return CHARGE_SUPPLIER_NONE;
|
||
|
|
||
|
switch ((reg & MT6370_MASK_USB_STATUS) >> MT6370_SHIFT_USB_STATUS) {
|
||
|
case MT6370_CHG_TYPE_SDP:
|
||
|
case MT6370_CHG_TYPE_SDPNSTD:
|
||
|
return CHARGE_SUPPLIER_BC12_SDP;
|
||
|
case MT6370_CHG_TYPE_CDP:
|
||
|
return CHARGE_SUPPLIER_BC12_CDP;
|
||
|
case MT6370_CHG_TYPE_DCP:
|
||
|
return CHARGE_SUPPLIER_BC12_DCP;
|
||
|
default:
|
||
|
return CHARGE_SUPPLIER_NONE;
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
static int rt946x_get_bc12_ilim(int charge_supplier)
|
||
|
{
|
||
|
switch (charge_supplier) {
|
||
|
case CHARGE_SUPPLIER_BC12_DCP:
|
||
|
if (IS_ENABLED(CONFIG_CHARGE_RAMP_SW) ||
|
||
|
IS_ENABLED(CONFIG_CHARGE_RAMP_HW))
|
||
|
/* A conservative value to prevent a bad charger. */
|
||
|
return 2000;
|
||
|
/* fallback */
|
||
|
case CHARGE_SUPPLIER_BC12_CDP:
|
||
|
return 1500;
|
||
|
case CHARGE_SUPPLIER_BC12_SDP:
|
||
|
default:
|
||
|
return USB_CHARGER_MIN_CURR_MA;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void rt946x_interrupt(enum gpio_signal signal)
|
||
|
{
|
||
|
task_wake(TASK_ID_USB_CHG);
|
||
|
}
|
||
|
|
||
|
int rt946x_toggle_bc12_detection(void)
|
||
|
{
|
||
|
int rv;
|
||
|
rv = rt946x_enable_bc12_detection(0);
|
||
|
if (rv)
|
||
|
return rv;
|
||
|
/* mt6370 requires 40us delay to toggle RT946X_MASK_USBCHGEN */
|
||
|
udelay(40);
|
||
|
return rt946x_enable_bc12_detection(1);
|
||
|
}
|
||
|
|
||
|
#ifdef CONFIG_CHARGER_MT6370_BC12_GPIO
|
||
|
static void usb_pd_connect(void)
|
||
|
{
|
||
|
rt946x_toggle_bc12_detection();
|
||
|
}
|
||
|
DECLARE_HOOK(HOOK_USB_PD_CONNECT, usb_pd_connect, HOOK_PRIO_DEFAULT);
|
||
|
#endif
|
||
|
|
||
|
void usb_charger_task(void *u)
|
||
|
{
|
||
|
struct charge_port_info chg;
|
||
|
int bc12_type = CHARGE_SUPPLIER_NONE;
|
||
|
int reg = 0;
|
||
|
|
||
|
chg.voltage = USB_CHARGER_VOLTAGE_MV;
|
||
|
while (1) {
|
||
|
rt946x_read8(RT946X_REG_DPDMIRQ, ®);
|
||
|
|
||
|
/* VBUS attach event */
|
||
|
if (reg & RT946X_MASK_DPDMIRQ_ATTACH) {
|
||
|
CPRINTS("VBUS attached: %dmV",
|
||
|
charger_get_vbus_voltage(0));
|
||
|
bc12_type = rt946x_get_bc12_device_type();
|
||
|
|
||
|
CPRINTS("BC12 type %d", bc12_type);
|
||
|
if (bc12_type != CHARGE_SUPPLIER_NONE) {
|
||
|
#ifdef CONFIG_WIRELESS_CHARGER_P9221_R7
|
||
|
if ((bc12_type == CHARGE_SUPPLIER_BC12_SDP) &&
|
||
|
wpc_chip_is_online()) {
|
||
|
p9221_notify_vbus_change(1);
|
||
|
CPRINTS("WPC ON");
|
||
|
} else {
|
||
|
|
||
|
#endif
|
||
|
chg.current = rt946x_get_bc12_ilim(
|
||
|
bc12_type);
|
||
|
charge_manager_update_charge(bc12_type,
|
||
|
0, &chg);
|
||
|
#ifdef CONFIG_WIRELESS_CHARGER_P9221_R7
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
rt946x_enable_bc12_detection(0);
|
||
|
hook_notify(HOOK_AC_CHANGE);
|
||
|
}
|
||
|
|
||
|
/* VBUS detach event */
|
||
|
if (reg & RT946X_MASK_DPDMIRQ_DETACH) {
|
||
|
CPRINTS("VBUS detached");
|
||
|
#ifdef CONFIG_WIRELESS_CHARGER_P9221_R7
|
||
|
p9221_notify_vbus_change(0);
|
||
|
#endif
|
||
|
charge_manager_update_charge(bc12_type, 0, NULL);
|
||
|
|
||
|
if (!IS_ENABLED(CONFIG_CHARGER_MT6370_BC12_GPIO))
|
||
|
rt946x_enable_bc12_detection(1);
|
||
|
|
||
|
hook_notify(HOOK_AC_CHANGE);
|
||
|
}
|
||
|
|
||
|
task_wait_event(-1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int usb_charger_ramp_allowed(int supplier)
|
||
|
{
|
||
|
return supplier == CHARGE_SUPPLIER_BC12_DCP;
|
||
|
}
|
||
|
|
||
|
int usb_charger_ramp_max(int supplier, int sup_curr)
|
||
|
{
|
||
|
return rt946x_get_bc12_ilim(supplier);
|
||
|
}
|
||
|
#endif /* HAS_TASK_USB_CHG */
|
||
|
|
||
|
/* Non-standard interface functions */
|
||
|
|
||
|
int rt946x_enable_charger_boost(int en)
|
||
|
{
|
||
|
return (en ? rt946x_set_bit : rt946x_clr_bit)
|
||
|
(RT946X_REG_CHGCTRL2, RT946X_MASK_CHG_EN);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* rt946x reports VBUS ready after VBUS is up for ~500ms.
|
||
|
* Check if this works for the use case before calling this function.
|
||
|
*/
|
||
|
int rt946x_is_vbus_ready(void)
|
||
|
{
|
||
|
int val = 0;
|
||
|
|
||
|
return rt946x_read8(RT946X_REG_CHGSTATC, &val) ?
|
||
|
0 : !!(val & RT946X_MASK_PWR_RDY);
|
||
|
}
|
||
|
|
||
|
int rt946x_is_charge_done(void)
|
||
|
{
|
||
|
int val = 0;
|
||
|
|
||
|
if (rt946x_read8(RT946X_REG_CHGSTAT, &val))
|
||
|
return 0;
|
||
|
|
||
|
val = (val & RT946X_MASK_CHG_STAT) >> RT946X_SHIFT_CHG_STAT;
|
||
|
|
||
|
return val == RT946X_CHGSTAT_DONE;
|
||
|
}
|
||
|
|
||
|
int rt946x_cutoff_battery(void)
|
||
|
{
|
||
|
int val = RT946X_MASK_SHIP_MODE;
|
||
|
|
||
|
#ifdef CONFIG_CHARGER_MT6370
|
||
|
val |= RT946X_MASK_TE | RT946X_MASK_CFO_EN | RT946X_MASK_CHG_EN;
|
||
|
#endif
|
||
|
return rt946x_set_bit(RT946X_REG_CHGCTRL2, val);
|
||
|
}
|
||
|
|
||
|
int rt946x_enable_charge_termination(int en)
|
||
|
{
|
||
|
return (en ? rt946x_set_bit : rt946x_clr_bit)
|
||
|
(RT946X_REG_CHGCTRL2, RT946X_MASK_TE);
|
||
|
}
|
||
|
|
||
|
#ifdef CONFIG_CHARGER_MT6370
|
||
|
/* MT6370 LDO */
|
||
|
|
||
|
int mt6370_set_ldo_voltage(int mv)
|
||
|
{
|
||
|
int rv;
|
||
|
int vout_val;
|
||
|
const int vout_mask = MT6370_MASK_LDOVOUT_EN | MT6370_MASK_LDOVOUT_VOUT;
|
||
|
|
||
|
/* LDO output-off mode to floating. */
|
||
|
rv = rt946x_update_bits(MT6370_REG_LDOCFG, MT6370_MASK_LDOCFG_OMS, 0);
|
||
|
if (rv)
|
||
|
return rv;
|
||
|
|
||
|
/* Disable LDO if voltage is zero. */
|
||
|
if (mv == 0)
|
||
|
return rt946x_clr_bit(MT6370_REG_LDOVOUT,
|
||
|
MT6370_MASK_LDOVOUT_EN);
|
||
|
|
||
|
vout_val = 1 << MT6370_SHIFT_LDOVOUT_EN;
|
||
|
vout_val |= rt946x_closest_reg(MT6370_LDO_MIN, MT6370_LDO_MAX,
|
||
|
MT6370_LDO_STEP, mv);
|
||
|
return rt946x_update_bits(MT6370_REG_LDOVOUT, vout_mask, vout_val);
|
||
|
}
|
||
|
|
||
|
/* MT6370 Display bias */
|
||
|
int mt6370_db_external_control(int en)
|
||
|
{
|
||
|
return rt946x_update_bits(MT6370_REG_DBCTRL1, MT6370_MASK_DB_EXT_EN,
|
||
|
en << MT6370_SHIFT_DB_EXT_EN);
|
||
|
}
|
||
|
|
||
|
int mt6370_db_set_voltages(int vbst, int vpos, int vneg)
|
||
|
{
|
||
|
int rv;
|
||
|
|
||
|
/* set display bias VBST */
|
||
|
rv = rt946x_update_bits(MT6370_REG_DBVBST, MT6370_MASK_DB_VBST,
|
||
|
rt946x_closest_reg(MT6370_DB_VBST_MIN,
|
||
|
MT6370_DB_VBST_MAX,
|
||
|
MT6370_DB_VBST_STEP, vbst));
|
||
|
|
||
|
/* set display bias VPOS */
|
||
|
rv |= rt946x_update_bits(MT6370_REG_DBVPOS, MT6370_MASK_DB_VPOS,
|
||
|
rt946x_closest_reg(MT6370_DB_VPOS_MIN,
|
||
|
MT6370_DB_VPOS_MAX,
|
||
|
MT6370_DB_VPOS_STEP, vpos));
|
||
|
|
||
|
/* set display bias VNEG */
|
||
|
rv |= rt946x_update_bits(MT6370_REG_DBVNEG, MT6370_MASK_DB_VNEG,
|
||
|
rt946x_closest_reg(MT6370_DB_VNEG_MIN,
|
||
|
MT6370_DB_VNEG_MAX,
|
||
|
MT6370_DB_VNEG_STEP, vneg));
|
||
|
|
||
|
/* Enable VNEG/VPOS discharge when VNEG/VPOS rails disabled. */
|
||
|
rv |= rt946x_update_bits(
|
||
|
MT6370_REG_DBCTRL2,
|
||
|
MT6370_MASK_DB_VNEG_DISC | MT6370_MASK_DB_VPOS_DISC,
|
||
|
MT6370_MASK_DB_VNEG_DISC | MT6370_MASK_DB_VPOS_DISC);
|
||
|
|
||
|
return rv;
|
||
|
}
|
||
|
|
||
|
/* MT6370 BACKLIGHT LED */
|
||
|
|
||
|
int mt6370_backlight_set_dim(uint16_t dim)
|
||
|
{
|
||
|
int rv;
|
||
|
|
||
|
/* datasheet suggests that update BLDIM2 first then BLDIM */
|
||
|
rv = rt946x_write8(MT6370_BACKLIGHT_BLDIM2, dim & MT6370_MASK_BLDIM2);
|
||
|
|
||
|
if (rv)
|
||
|
return rv;
|
||
|
|
||
|
rv = rt946x_write8(MT6370_BACKLIGHT_BLDIM,
|
||
|
(dim >> MT6370_SHIFT_BLDIM_MSB) & MT6370_MASK_BLDIM);
|
||
|
|
||
|
return rv;
|
||
|
}
|
||
|
|
||
|
/* MT6370 RGB LED */
|
||
|
|
||
|
int mt6370_led_set_dim_mode(enum mt6370_led_index index,
|
||
|
enum mt6370_led_dim_mode mode)
|
||
|
{
|
||
|
if (index <= MT6370_LED_ID_OFF || index >= MT6370_LED_ID_COUNT)
|
||
|
return EC_ERROR_INVAL;
|
||
|
|
||
|
rt946x_update_bits(MT6370_REG_RGBDIM_BASE + index,
|
||
|
MT6370_MASK_RGB_DIMMODE,
|
||
|
mode << MT6370_SHIFT_RGB_DIMMODE);
|
||
|
return EC_SUCCESS;
|
||
|
}
|
||
|
|
||
|
int mt6370_led_set_color(uint8_t mask)
|
||
|
{
|
||
|
return rt946x_update_bits(MT6370_REG_RGBEN, MT6370_MASK_RGB_ISNK_ALL_EN,
|
||
|
mask);
|
||
|
}
|
||
|
|
||
|
int mt6370_led_set_brightness(enum mt6370_led_index index, uint8_t brightness)
|
||
|
{
|
||
|
if (index >= MT6370_LED_ID_COUNT || index <= MT6370_LED_ID_OFF)
|
||
|
return EC_ERROR_INVAL;
|
||
|
|
||
|
rt946x_update_bits(MT6370_REG_RGBISNK_BASE + index,
|
||
|
MT6370_MASK_RGBISNK_CURSEL,
|
||
|
brightness << MT6370_SHIFT_RGBISNK_CURSEL);
|
||
|
return EC_SUCCESS;
|
||
|
}
|
||
|
|
||
|
int mt6370_led_set_pwm_dim_duty(enum mt6370_led_index index, uint8_t dim_duty)
|
||
|
{
|
||
|
if (index >= MT6370_LED_ID_COUNT || index <= MT6370_LED_ID_OFF)
|
||
|
return EC_ERROR_INVAL;
|
||
|
|
||
|
rt946x_update_bits(MT6370_REG_RGBDIM_BASE + index,
|
||
|
MT6370_MASK_RGB_DIMDUTY,
|
||
|
dim_duty << MT6370_SHIFT_RGB_DIMDUTY);
|
||
|
return EC_SUCCESS;
|
||
|
}
|
||
|
|
||
|
int mt6370_led_set_pwm_frequency(enum mt6370_led_index index,
|
||
|
enum mt6370_led_pwm_freq freq)
|
||
|
{
|
||
|
if (index >= MT6370_LED_ID_COUNT || index <= MT6370_LED_ID_OFF)
|
||
|
return EC_ERROR_INVAL;
|
||
|
|
||
|
rt946x_update_bits(MT6370_REG_RGBISNK_BASE + index,
|
||
|
MT6370_MASK_RGBISNK_DIMFSEL,
|
||
|
freq << MT6370_SHIFT_RGBISNK_DIMFSEL);
|
||
|
return EC_SUCCESS;
|
||
|
}
|
||
|
#endif /* CONFIG_CHARGER_MT6370 */
|