summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorchenjh <chenjh@rock-chips.com>2017-05-17 17:27:56 +0800
committerHuang, Tao <huangtao@rock-chips.com>2017-05-19 15:31:03 +0800
commit9decafdc60597edc2a63164597ede3fb601976e7 (patch)
treec67079763028e38576813231ee32f26de39beb87
parent3244d204d45b22ac4ec4d8f987a027a403d405dc (diff)
power: rk818-charger: add TS2 voltage detect when update input current
rk818's input charge voltage limit function doesn't works well. If software set input current over than charger's max support value, rk818 may cause charger over current protect which means disconnecting. To solve this problem, we need to detect vbus voltage by TS2 pin, if vbus is upper than 4.4v, we can safely adjust input current step by step from low to high until meeting the target input current value. Change-Id: I01d63974f251ad8ef0037158b66f4b85d3928baf Signed-off-by: chenjh <chenjh@rock-chips.com>
-rw-r--r--drivers/power/rk818_charger.c145
1 files changed, 140 insertions, 5 deletions
diff --git a/drivers/power/rk818_charger.c b/drivers/power/rk818_charger.c
index c38d4828f4b5..00146303d27e 100644
--- a/drivers/power/rk818_charger.c
+++ b/drivers/power/rk818_charger.c
@@ -57,6 +57,8 @@ module_param_named(dbg_level, dbg_enable, int, 0644);
/* RK818_USB_CTRL_REG */
#define INPUT_CUR450MA (0x00)
+#define INPUT_CUR80MA (0x01)
+#define INPUT_CUR850MA (0x02)
#define INPUT_CUR1500MA (0x05)
#define INPUT_CUR_MSK (0x0f)
/* RK818_CHRG_CTRL_REG3 */
@@ -77,9 +79,17 @@ module_param_named(dbg_level, dbg_enable, int, 0644);
#define PLUG_IN_STS BIT(6)
/* RK818_TS_CTRL_REG */
#define GG_EN BIT(7)
+#define TS2_FUN_ADC BIT(5)
+/* RK818_ADC_CTRL_REG */
+#define ADC_TS2_EN BIT(4)
#define DRIVER_VERSION "1.0"
+#define DEFAULT_TS2_THRESHOLD_VOL 4350
+#define DEFAULT_TS2_VALID_VOL 1000
+#define DEFAULT_TS2_VOL_MULTI 0
+#define DEFAULT_TS2_CHECK_CNT 5
+
static const u16 chrg_vol_sel_array[] = {
4050, 4100, 4150, 4200, 4250, 4300, 4350
};
@@ -115,6 +125,7 @@ struct charger_platform_data {
int sample_res;
int otg5v_suspend_enable;
bool extcon;
+ int ts2_vol_multi;
};
struct rk818_charger {
@@ -129,12 +140,14 @@ struct rk818_charger {
struct workqueue_struct *usb_charger_wq;
struct workqueue_struct *dc_charger_wq;
struct workqueue_struct *finish_sig_wq;
+ struct workqueue_struct *ts2_wq;
struct delayed_work dc_work;
struct delayed_work usb_work;
struct delayed_work host_work;
struct delayed_work discnt_work;
struct delayed_work finish_sig_work;
struct delayed_work irq_work;
+ struct delayed_work ts2_vol_work;
struct notifier_block bc_nb;
struct notifier_block cable_cg_nb;
struct notifier_block cable_host_nb;
@@ -226,6 +239,22 @@ static int rk818_cg_get_avg_current(struct rk818_charger *cg)
return cur;
}
+static int rk818_cg_get_ts2_voltage(struct rk818_charger *cg)
+{
+ u32 val = 0;
+ int voltage;
+
+ val |= rk818_reg_read(cg, RK818_TS2_ADC_REGL) << 0;
+ val |= rk818_reg_read(cg, RK818_TS2_ADC_REGH) << 8;
+
+ /* refer voltage 2.2V, 12bit adc accuracy */
+ voltage = val * 2200 * cg->pdata->ts2_vol_multi / 4095;
+
+ DBG("********* ts2 adc=%d, vol=%d\n", val, voltage);
+
+ return voltage;
+}
+
static u64 get_boot_sec(void)
{
struct timespec ts;
@@ -477,17 +506,32 @@ static void rk818_cg_set_chrg_param(struct rk818_charger *cg,
cg->ac_in = 1;
cg->usb_in = 0;
cg->prop_status = POWER_SUPPLY_STATUS_CHARGING;
- if (charger == USB_TYPE_AC_CHARGER)
- rk818_cg_set_input_current(cg, cg->chrg_input);
- else
+ if (charger == USB_TYPE_AC_CHARGER) {
+ if (cg->pdata->ts2_vol_multi) {
+ rk818_cg_set_input_current(cg, INPUT_CUR450MA);
+ queue_delayed_work(cg->ts2_wq,
+ &cg->ts2_vol_work,
+ msecs_to_jiffies(0));
+ } else {
+ rk818_cg_set_input_current(cg, cg->chrg_input);
+ }
+ } else {
rk818_cg_set_input_current(cg, INPUT_CUR1500MA);
+ }
power_supply_changed(cg->usb_psy);
power_supply_changed(cg->ac_psy);
break;
case DC_TYPE_DC_CHARGER:
cg->dc_in = 1;
cg->prop_status = POWER_SUPPLY_STATUS_CHARGING;
- rk818_cg_set_input_current(cg, cg->chrg_input);
+ if (cg->pdata->ts2_vol_multi) {
+ rk818_cg_set_input_current(cg, INPUT_CUR450MA);
+ queue_delayed_work(cg->ts2_wq,
+ &cg->ts2_vol_work,
+ msecs_to_jiffies(0));
+ } else {
+ rk818_cg_set_input_current(cg, cg->chrg_input);
+ }
power_supply_changed(cg->usb_psy);
power_supply_changed(cg->ac_psy);
break;
@@ -672,6 +716,63 @@ static void rk818_cg_init_config(struct rk818_charger *cg)
rk818_reg_write(cg, RK818_CHRG_CTRL_REG1, chrg_ctrl1);
}
+static void rk818_ts2_vol_work(struct work_struct *work)
+{
+ struct rk818_charger *cg;
+ int ts2_vol, input_current, invalid_cnt = 0, confirm_cnt = 0;
+
+ cg = container_of(work, struct rk818_charger, ts2_vol_work.work);
+
+ input_current = INPUT_CUR80MA;
+ while (input_current < cg->chrg_input) {
+ msleep(100);
+ ts2_vol = rk818_cg_get_ts2_voltage(cg);
+
+ /* filter invalid voltage */
+ if (ts2_vol <= DEFAULT_TS2_VALID_VOL) {
+ invalid_cnt++;
+ DBG("%s: invalid ts2 voltage: %d\n, cnt=%d",
+ __func__, ts2_vol, invalid_cnt);
+ if (invalid_cnt < DEFAULT_TS2_CHECK_CNT)
+ continue;
+
+ /* if fail, set max input current as default */
+ input_current = cg->chrg_input;
+ rk818_cg_set_input_current(cg, input_current);
+ break;
+ }
+
+ /* update input current */
+ if (ts2_vol >= DEFAULT_TS2_THRESHOLD_VOL) {
+ /* update input current */
+ input_current++;
+ rk818_cg_set_input_current(cg, input_current);
+ DBG("********* input=%d\n",
+ chrg_cur_input_array[input_current & 0x0f]);
+ } else {
+ /* confirm lower threshold voltage */
+ confirm_cnt++;
+ if (confirm_cnt < DEFAULT_TS2_CHECK_CNT) {
+ DBG("%s: confirm ts2 voltage: %d\n, cnt=%d",
+ __func__, ts2_vol, confirm_cnt);
+ continue;
+ }
+
+ /* trigger threshold, so roll back 1 step */
+ input_current--;
+ if (input_current == INPUT_CUR80MA ||
+ input_current < 0)
+ input_current = INPUT_CUR450MA;
+ rk818_cg_set_input_current(cg, input_current);
+ break;
+ }
+ }
+
+ if (input_current != cg->chrg_input)
+ CG_INFO("adjust input current: %dma\n",
+ chrg_cur_input_array[input_current & 0x0f]);
+}
+
static int rk818_cg_charger_evt_notifier(struct notifier_block *nb,
unsigned long event, void *ptr)
{
@@ -1111,6 +1212,32 @@ static void rk818_cg_init_finish_sig(struct rk818_charger *cg)
INIT_DELAYED_WORK(&cg->finish_sig_work, rk818_cg_finish_sig_work);
}
+static void rk818_cg_init_ts2_detect(struct rk818_charger *cg)
+{
+ u8 buf;
+
+ cg->ts2_wq = alloc_ordered_workqueue("%s",
+ WQ_MEM_RECLAIM | WQ_FREEZABLE,
+ "rk818-ts2-wq");
+ INIT_DELAYED_WORK(&cg->ts2_vol_work, rk818_ts2_vol_work);
+
+ if (!cg->pdata->ts2_vol_multi)
+ return;
+
+ /* TS2 adc mode */
+ buf = rk818_reg_read(cg, RK818_TS_CTRL_REG);
+ buf |= TS2_FUN_ADC;
+ rk818_reg_write(cg, RK818_TS_CTRL_REG, buf);
+
+ /* TS2 adc enable */
+ buf = rk818_reg_read(cg, RK818_ADC_CTRL_REG);
+ buf |= ADC_TS2_EN;
+ rk818_reg_write(cg, RK818_ADC_CTRL_REG, buf);
+
+ CG_INFO("enable ts2 voltage detect, multi=%d\n",
+ cg->pdata->ts2_vol_multi);
+}
+
static void rk818_cg_init_charger_state(struct rk818_charger *cg)
{
rk818_cg_init_config(cg);
@@ -1183,6 +1310,9 @@ static int rk818_cg_parse_dt(struct rk818_charger *cg)
dev_err(dev, "otg5v_suspend_enable missing!\n");
}
+ ret = of_property_read_u32(np, "ts2_vol_multi",
+ &pdata->ts2_vol_multi);
+
if (!is_battery_exist(cg))
pdata->virtual_power = 1;
@@ -1211,11 +1341,12 @@ static int rk818_cg_parse_dt(struct rk818_charger *cg)
"chrg_voltage:%d\n"
"sample_res:%d\n"
"extcon:%d\n"
+ "ts2_vol_multi:%d\n"
"virtual_power:%d\n"
"power_dc2otg:%d\n",
pdata->max_input_current, pdata->max_chrg_current,
pdata->max_chrg_voltage, pdata->sample_res, pdata->extcon,
- pdata->virtual_power, pdata->power_dc2otg);
+ pdata->ts2_vol_multi, pdata->virtual_power, pdata->power_dc2otg);
return 0;
}
@@ -1254,6 +1385,8 @@ static int rk818_charger_probe(struct platform_device *pdev)
return ret;
}
+ rk818_cg_init_ts2_detect(cg);
+
ret = rk818_cg_init_dc(cg);
if (ret) {
dev_err(cg->dev, "init dc failed!\n");
@@ -1293,6 +1426,8 @@ static void rk818_charger_shutdown(struct platform_device *pdev)
cancel_delayed_work_sync(&cg->dc_work);
cancel_delayed_work_sync(&cg->finish_sig_work);
cancel_delayed_work_sync(&cg->irq_work);
+ cancel_delayed_work_sync(&cg->ts2_vol_work);
+ destroy_workqueue(cg->ts2_wq);
destroy_workqueue(cg->usb_charger_wq);
destroy_workqueue(cg->dc_charger_wq);
destroy_workqueue(cg->finish_sig_wq);