aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/power
diff options
context:
space:
mode:
authorSebastian Reichel <sre@kernel.org>2019-10-20 20:24:59 +0200
committerSebastian Reichel <sre@kernel.org>2019-10-20 20:24:59 +0200
commit8bc8fc088bcd9da7ff565620a2778def59902404 (patch)
tree2ed7103a36dd930f32433f1c33a4261cc222b89d /drivers/power
parentpower: supply: ab8500: Handle invalid IRQ from platform_get_irq_byname() (diff)
parentpower: supply: cpcap-charger: Limit voltage to 4.2V for battery (diff)
downloadlinux-dev-8bc8fc088bcd9da7ff565620a2778def59902404.tar.xz
linux-dev-8bc8fc088bcd9da7ff565620a2778def59902404.zip
Merge tag 'psy-cpcap-charge-volt-limit-signed' into psy-next
Immutable branch between arm and power-supply for cpcap-charger This immutable branch contains CPCAP charger changes, which touch ARM and power-supply subsystem. Signed-off-by: Sebastian Reichel <sre@kernel.org>
Diffstat (limited to 'drivers/power')
-rw-r--r--drivers/power/supply/cpcap-charger.c132
1 files changed, 130 insertions, 2 deletions
diff --git a/drivers/power/supply/cpcap-charger.c b/drivers/power/supply/cpcap-charger.c
index 74258c7fe17d..88bbab6e62f0 100644
--- a/drivers/power/supply/cpcap-charger.c
+++ b/drivers/power/supply/cpcap-charger.c
@@ -120,6 +120,13 @@ enum {
CPCAP_CHARGER_IIO_NR,
};
+enum {
+ CPCAP_CHARGER_DISCONNECTED,
+ CPCAP_CHARGER_DETECTING,
+ CPCAP_CHARGER_CHARGING,
+ CPCAP_CHARGER_DONE,
+};
+
struct cpcap_charger_ddata {
struct device *dev;
struct regmap *reg;
@@ -138,6 +145,8 @@ struct cpcap_charger_ddata {
atomic_t active;
int status;
+ int state;
+ int voltage;
};
struct cpcap_interrupt_desc {
@@ -153,6 +162,7 @@ struct cpcap_charger_ints_state {
bool chrg_se1b;
bool rvrs_mode;
+ bool chrgcurr2;
bool chrgcurr1;
bool vbusvld;
@@ -422,6 +432,7 @@ static int cpcap_charger_get_ints_state(struct cpcap_charger_ddata *ddata,
s->chrg_se1b = val & BIT(13);
s->rvrs_mode = val & BIT(6);
+ s->chrgcurr2 = val & BIT(5);
s->chrgcurr1 = val & BIT(4);
s->vbusvld = val & BIT(3);
@@ -434,6 +445,79 @@ static int cpcap_charger_get_ints_state(struct cpcap_charger_ddata *ddata,
return 0;
}
+static void cpcap_charger_update_state(struct cpcap_charger_ddata *ddata,
+ int state)
+{
+ const char *status;
+
+ if (state > CPCAP_CHARGER_DONE) {
+ dev_warn(ddata->dev, "unknown state: %i\n", state);
+
+ return;
+ }
+
+ ddata->state = state;
+
+ switch (state) {
+ case CPCAP_CHARGER_DISCONNECTED:
+ status = "DISCONNECTED";
+ break;
+ case CPCAP_CHARGER_DETECTING:
+ status = "DETECTING";
+ break;
+ case CPCAP_CHARGER_CHARGING:
+ status = "CHARGING";
+ break;
+ case CPCAP_CHARGER_DONE:
+ status = "DONE";
+ break;
+ default:
+ return;
+ }
+
+ dev_dbg(ddata->dev, "state: %s\n", status);
+}
+
+int cpcap_charger_voltage_to_regval(int voltage)
+{
+ int offset;
+
+ switch (voltage) {
+ case 0 ... 4100000 - 1:
+ return 0;
+ case 4100000 ... 4200000 - 1:
+ offset = 1;
+ break;
+ case 4200000 ... 4300000 - 1:
+ offset = 0;
+ break;
+ case 4300000 ... 4380000 - 1:
+ offset = -1;
+ break;
+ case 4380000 ... 4440000:
+ offset = -2;
+ break;
+ default:
+ return 0;
+ }
+
+ return ((voltage - 4100000) / 20000) + offset;
+}
+
+static void cpcap_charger_disconnect(struct cpcap_charger_ddata *ddata,
+ int state, unsigned long delay)
+{
+ int error;
+
+ error = cpcap_charger_set_state(ddata, 0, 0, 0);
+ if (error)
+ return;
+
+ cpcap_charger_update_state(ddata, state);
+ power_supply_changed(ddata->usb);
+ schedule_delayed_work(&ddata->detect_work, delay);
+}
+
static void cpcap_usb_detect(struct work_struct *work)
{
struct cpcap_charger_ddata *ddata;
@@ -447,24 +531,67 @@ static void cpcap_usb_detect(struct work_struct *work)
if (error)
return;
+ /* Just init the state if a charger is connected with no chrg_det set */
+ if (!s.chrg_det && s.chrgcurr1 && s.vbusvld) {
+ cpcap_charger_update_state(ddata, CPCAP_CHARGER_DETECTING);
+
+ return;
+ }
+
+ /*
+ * If battery voltage is higher than charge voltage, it may have been
+ * charged to 4.35V by Android. Try again in 10 minutes.
+ */
+ if (cpcap_charger_get_charge_voltage(ddata) > ddata->voltage) {
+ cpcap_charger_disconnect(ddata, CPCAP_CHARGER_DETECTING,
+ HZ * 60 * 10);
+
+ return;
+ }
+
+ /* Throttle chrgcurr2 interrupt for charger done and retry */
+ switch (ddata->state) {
+ case CPCAP_CHARGER_CHARGING:
+ if (s.chrgcurr2)
+ break;
+ if (s.chrgcurr1 && s.vbusvld) {
+ cpcap_charger_disconnect(ddata, CPCAP_CHARGER_DONE,
+ HZ * 5);
+ return;
+ }
+ break;
+ case CPCAP_CHARGER_DONE:
+ if (!s.chrgcurr2)
+ break;
+ cpcap_charger_disconnect(ddata, CPCAP_CHARGER_DETECTING,
+ HZ * 5);
+ return;
+ default:
+ break;
+ }
+
if (!ddata->feeding_vbus && cpcap_charger_vbus_valid(ddata) &&
s.chrgcurr1) {
int max_current;
+ int vchrg;
if (cpcap_charger_battery_found(ddata))
max_current = CPCAP_REG_CRM_ICHRG_1A596;
else
max_current = CPCAP_REG_CRM_ICHRG_0A532;
+ vchrg = cpcap_charger_voltage_to_regval(ddata->voltage);
error = cpcap_charger_set_state(ddata,
- CPCAP_REG_CRM_VCHRG_4V35,
+ CPCAP_REG_CRM_VCHRG(vchrg),
max_current, 0);
if (error)
goto out_err;
+ cpcap_charger_update_state(ddata, CPCAP_CHARGER_CHARGING);
} else {
error = cpcap_charger_set_state(ddata, 0, 0, 0);
if (error)
goto out_err;
+ cpcap_charger_update_state(ddata, CPCAP_CHARGER_DISCONNECTED);
}
power_supply_changed(ddata->usb);
@@ -524,7 +651,7 @@ static const char * const cpcap_charger_irqs[] = {
"chrg_det", "rvrs_chrg",
/* REG_INT1 */
- "chrg_se1b", "se0conn", "rvrs_mode", "chrgcurr1", "vbusvld",
+ "chrg_se1b", "se0conn", "rvrs_mode", "chrgcurr2", "chrgcurr1", "vbusvld",
/* REG_INT_3 */
"battdetb",
@@ -625,6 +752,7 @@ static int cpcap_charger_probe(struct platform_device *pdev)
return -ENOMEM;
ddata->dev = &pdev->dev;
+ ddata->voltage = 4200000;
ddata->reg = dev_get_regmap(ddata->dev->parent, NULL);
if (!ddata->reg)