google_cpm: add support for WLCDC
Preliminary support for WLC DC:
= delegate the selection of the input to the PPS_Work workloop
= add the gcpm_pps power supply
Bug: 177471684
Signed-off-by: AleX Pelosi <[email protected]>
Change-Id: Ibd44d3d4e7d5077dcc4a2f1fa09085e1ae6772e6
diff --git a/google_charger.c b/google_charger.c
index c5a4646..a0f07e8 100644
--- a/google_charger.c
+++ b/google_charger.c
@@ -2303,7 +2303,7 @@
} else {
const char *name = tcpm_psy->desc->name;
- ret = pps_init(&chg_drv->pps_data, chg_drv->device);
+ ret = pps_init(&chg_drv->pps_data, chg_drv->device, tcpm_psy);
if (ret == 0 && chg_drv->debug_entry)
pps_init_fs(&chg_drv->pps_data, chg_drv->debug_entry);
if (ret < 0)
@@ -2472,8 +2472,6 @@
}
}
-
-
INIT_DELAYED_WORK(&chg_drv->init_work, google_charger_init_work);
INIT_DELAYED_WORK(&chg_drv->chg_work, chg_work);
INIT_WORK(&chg_drv->chg_psy_work, chg_psy_work);
diff --git a/google_cpm.c b/google_cpm.c
index a2d6428..8696a32 100644
--- a/google_cpm.c
+++ b/google_cpm.c
@@ -50,7 +50,8 @@
#define DC_VBATT_HEADROOM_MV 500000
enum gcpm_dc_state_t {
- DC_DISABLED = 0,
+ DC_DISABLED = -1,
+ DC_IDLE = 0,
DC_ENABLE,
DC_RUNNING,
DC_ENABLE_PASSTHROUGH,
@@ -62,25 +63,35 @@
struct power_supply *psy;
struct delayed_work init_work;
+ /* combine PPS, route to the active PPS source */
+ struct power_supply *pps_psy;
+
int chg_psy_retries;
struct power_supply *chg_psy_avail[GCPM_MAX_CHARGERS];
const char *chg_psy_names[GCPM_MAX_CHARGERS];
struct mutex chg_psy_lock;
int chg_psy_active;
int chg_psy_count;
+
/* force a charger, this might have side effects */
int force_active;
- /* TCPM state for PPS charging */
- struct power_supply *tcpm_psy;
+ /* TCPM state for wired PPS charging */
const char *tcpm_psy_name;
+ struct power_supply *tcpm_psy;
+ struct pd_pps_data tcpm_pps_data;
int log_psy_ratelimit;
u32 tcpm_phandle;
+ /* TCPM state for wireless PPS charging */
+ const char *wlc_dc_name;
+ struct power_supply *wlc_dc_psy;
+ struct pd_pps_data wlc_pps_data;
+ u32 wlc_phandle;
+
/* set to force PPS negoatiation */
bool force_pps;
/* pps state and detect */
- struct pd_pps_data pps_data;
struct delayed_work pps_work;
/* request of output ua, */
int out_ua;
@@ -89,12 +100,14 @@
int dcen_gpio;
u32 dcen_gpio_default;
- /* >0 when enabled */
+ /* >0 when enabled, pps charger to use */
+ int pps_index;
+ /* >0 when enabled, dc_charger */
int dc_index;
/* dc_charging state */
int dc_state;
- /* force check of the DC limit again */
+ /* force check of the DC limit again (debug) */
bool new_dc_limit;
/* force disable */
bool taper_control;
@@ -161,7 +174,8 @@
if (ret == 0)
gcpm->chg_psy_active = -1;
- pr_debug("active=%d offline=%d\n", gcpm->chg_psy_active, ret == 0);
+ pr_debug("%s: active=%d offline=%d\n", __func__,
+ gcpm->chg_psy_active, ret == 0);
return ret;
}
@@ -197,7 +211,8 @@
gcpm->chg_psy_active = index;
error_exit:
- pr_info("active charger %d->%d (%d)\n", index_old, index, ret);
+ pr_info("%s: active charger %d->%d (%d)\n",
+ __func__, index_old, index, ret);
return ret;
}
@@ -215,47 +230,6 @@
return chg_state.v;
}
-/*
- * Select the DC charger using the thermal policy.
- * NOTE: program target before enabling chaging.
- */
-static int gcpm_chg_dc_select(const struct gcpm_drv *gcpm)
-{
- struct power_supply *chg_psy = gcpm_chg_get_active(gcpm);
- int batt_demand, index = 0; /* 0 is the default */
-
- /* keep on default */
- if (gcpm->taper_control || !chg_psy)
- return 0;
- if (gcpm->cc_max <= 0 || gcpm->fv_uv <= 0)
- return 0;
-
- /* */
- batt_demand = (gcpm->cc_max / 1000) * (gcpm->fv_uv / 1000);
- if (batt_demand > gcpm->dc_limit_demand)
- index = 1;
- if (index == 0) {
- int vbatt;
-
- vbatt = GPSY_GET_PROP(chg_psy, POWER_SUPPLY_PROP_VOLTAGE_NOW);
- if (vbatt > 0 && gcpm->dc_limit_vbatt)
- index = 1;
- }
-
- pr_info("%s: index=%d count=%d demand=%d dc_limit_demand=%d\n",
- __func__, index, gcpm->chg_psy_count,
- batt_demand, gcpm->dc_limit_demand);
-
- if (index >= gcpm->chg_psy_count)
- index = 0;
-
- /* could select different modes here depending on capabilities */
-
- /* add margin .... debounce etc... */
-
- return index;
-}
-
/* Enable DirectCharge mode, PPS and DC charger must be already initialized */
static int gcpm_dc_enable(struct gcpm_drv *gcpm, bool enabled)
{
@@ -273,8 +247,11 @@
enabled);
}
-/* disable DC, and switch back to the default charger */
-/* NOTE: call with a lock around gcpm->chg_psy_lock */
+/*
+ * disable DC and switch back to the default charger. Final DC statate is
+ * DC_IDLE (i.e. this can be used to reset dc_state from DC_DISABLED).
+ * NOTE: call with a lock around gcpm->chg_psy_lock
+ */
static int gcpm_dc_stop(struct gcpm_drv *gcpm)
{
int ret;
@@ -303,7 +280,7 @@
}
/* Fall Through */
default:
- gcpm->dc_state = DC_DISABLED;
+ gcpm->dc_state = DC_IDLE;
ret = 0;
break;
}
@@ -359,6 +336,7 @@
if (gcpm->dcen_gpio >= 0 && !gcpm->dcen_gpio_default)
gpio_set_value(gcpm->dcen_gpio, 1);
+ /* vote on MODE */
ret = gcpm_dc_enable(gcpm, true);
if (ret < 0) {
pr_info("PPS_DC: dc_ready failed=%d\n", ret);
@@ -371,47 +349,78 @@
return 0;
}
-/* NOTE: call with a lock around gcpm->chg_psy_lock */
-static int gcpm_dc_charging(struct gcpm_drv *gcpm)
-{
- struct power_supply *dc_psy;
- int vchg, ichg, status;
-
- dc_psy = gcpm_chg_get_active(gcpm);
- if (!dc_psy) {
- pr_err("DC_CHG: invalid charger\n");
- return -ENODEV;
- }
-
- vchg = GPSY_GET_PROP(dc_psy, POWER_SUPPLY_PROP_VOLTAGE_NOW);
- ichg = GPSY_GET_PROP(dc_psy, POWER_SUPPLY_PROP_CURRENT_NOW);
- status = GPSY_GET_PROP(dc_psy, POWER_SUPPLY_PROP_STATUS);
-
- pr_err("DC_CHG: vchg=%d, ichg=%d status=%d\n",
- vchg, ichg, status);
-
- return 0;
-}
-
/* Will need to handle capabilities based on index number */
#define GCPM_INDEX_DC_ENABLE 1
-
-/* TODO: do not enable PPS when ->taper_control is set? */
-/* TODO: revert to fixed profile if cc_max is 0, remember failures */
-/* TODO: use a structure to describe charger types "needs_pps", "is_dc" etc */
-static int gcpm_chg_pps_check_enable(const struct gcpm_drv *gcpm, int index)
+/*
+ * Select the DC charger using the thermal policy.
+ * NOTE: program target before enabling chaging.
+ */
+static int gcpm_chg_dc_select(const struct gcpm_drv *gcpm)
{
- return gcpm->force_pps || index == GCPM_INDEX_DC_ENABLE;
+ struct power_supply *chg_psy = gcpm_chg_get_active(gcpm);
+ int batt_demand, index = 0; /* 0 is the default */
+
+ /* keep on default */
+ if (gcpm->taper_control || !chg_psy)
+ return 0;
+ if (gcpm->cc_max <= 0 || gcpm->fv_uv <= 0)
+ return 0;
+
+ /* battery demand comes from charging tier */
+ batt_demand = (gcpm->cc_max / 1000) * (gcpm->fv_uv / 1000);
+ if (batt_demand > gcpm->dc_limit_demand)
+ index = 1;
+ if (index == 0) {
+ int vbatt;
+
+ vbatt = GPSY_GET_PROP(chg_psy, POWER_SUPPLY_PROP_VOLTAGE_NOW);
+ if (vbatt > 0 && gcpm->dc_limit_vbatt)
+ index = 1;
+ }
+
+ pr_info("%s: index=%d count=%d demand=%d dc_limit_demand=%d\n",
+ __func__, index, gcpm->chg_psy_count,
+ batt_demand, gcpm->dc_limit_demand);
+
+ if (index >= gcpm->chg_psy_count)
+ index = 0;
+
+ /* could select different modes here depending on capabilities */
+
+ /* add margin .... debounce etc... */
+
+ return index;
}
-/* TODO: handle PPS-DC and WLC-DC, return dc_stage*/
/* NOTE: DC requires PPS, disable DC in taper control */
-static int gcpm_chg_dc_check_enable(const struct gcpm_drv *gcpm, int index)
+static bool gcpm_chg_dc_check_enable(const struct gcpm_drv *gcpm, int index)
{
+ if (gcpm->taper_control)
+ return false;
+
+ /* Will run detection only the first time */
+ if (gcpm->tcpm_pps_data.stage == PPS_NOTSUPP &&
+ gcpm->wlc_pps_data.stage == PPS_NOTSUPP )
+ return false;
+
return index == GCPM_INDEX_DC_ENABLE;
}
+static void gcpm_pps_online(struct gcpm_drv *gcpm)
+{
+ /* reset setpoint */
+ gcpm->out_ua = -1;
+ gcpm->out_uv = -1;
+
+ /* reset detection */
+ if (gcpm->tcpm_pps_data.pps_psy)
+ pps_init_state(&gcpm->tcpm_pps_data);
+ if (gcpm->wlc_pps_data.pps_psy)
+ pps_init_state(&gcpm->wlc_pps_data);
+ gcpm->pps_index = 0;
+}
+
/*
* need to run through the whole function even when index == gcpm->force_active
* because I have multiple steps and multiple failure points.
@@ -419,75 +428,35 @@
*/
static int gcpm_chg_check(struct gcpm_drv *gcpm)
{
- struct pd_pps_data *pps_data = &gcpm->pps_data;
bool schedule_pps_dc = false;
- bool dc_ena = 0, pps_ena;
int ret = 0, index;
+ bool dc_ena;
- /* might have more than one policy */
index = gcpm_chg_dc_select(gcpm);
if (gcpm->force_active >= 0)
index = gcpm->force_active;
- pr_debug("CHG_CHK: index:%d->%d\n", gcpm->chg_psy_active, index);
-
- /* first check if PPS needs to be enabled (or disable it) */
- pps_ena = gcpm->tcpm_psy && gcpm_chg_pps_check_enable(gcpm, index);
- pr_debug("CHG_CHK: PPS pps_stage=%d pps_ena=%d\n",
- pps_data->stage, pps_ena);
-
- if (pps_ena && pps_data->stage != PPS_NOTSUPP) {
-
- /* Could reset gcpm->out_uv and gcpm->out_ua to -1 */
- if (pps_data->stage == PPS_DISABLED) {
- pps_data->stage = PPS_NONE;
- schedule_pps_dc = true;
- }
-
- } else if (pps_data->stage == PPS_NOTSUPP) {
- /* keep PPS_NOTSUPP, continue to make sure DC is disabled */
- } else if (pps_data->stage != PPS_DISABLED) {
-
- /* force state, PPS_DC will take cate of it */
-
- ret = pps_prog_offline(&gcpm->pps_data, gcpm->tcpm_psy);
- pr_debug("CHG_CHK: PPS offline ret=%d\n", ret);
- if (ret < 0) {
- pr_debug("CHG_CHK: PPS pps_ena=%d dc_ena=%d->%d ret=%d\n",
- pps_ena, gcpm->dc_index, dc_ena, ret);
-
- /* force state, PPS_DC will take care of it */
- pps_data->stage = PPS_DISABLED;
-
- /*
- * transitions between MAIN<->PPS<->PPS+DC are handled
- * in gcpm_pps_dc_work() and the state of DC for this
- * index must be handled regardless of the success of
- * the immediate disable of pps.
- */
- }
-
- /* needs to continue of DC was enabled */
- schedule_pps_dc = true;
- }
-
/*
* NOTE: disabling DC might need to transition to charger mode 0
- * same might apply when switching between WLC-DC and PPS-DC.
+ * same might apply when switching between WLC-DC and PPS-DC.
+ * Figure out a way to do this if needed.
*/
dc_ena = gcpm_chg_dc_check_enable(gcpm, index);
- pr_debug("CHG_CHK: DC pps_ena=%d dc_ena=%d->%d \n",
- pps_ena, gcpm->dc_index, dc_ena);
+ pr_debug("CHG_CHK: DC dc_ena=%d dc_state=%d dc_index=%d->%d\n",
+ dc_ena, gcpm->dc_state, gcpm->dc_index, index);
if (!dc_ena) {
if (gcpm->dc_index) {
- gcpm->dc_index = 0;
schedule_pps_dc = true;
+ gcpm->dc_state = DC_IDLE;
+ gcpm->dc_index = 0;
}
} else if (gcpm->dc_state == DC_DISABLED) {
- gcpm->out_ua = -1;
- gcpm->out_uv = -1;
+ pr_debug("CHG_CHK: DC disabled for the session\n");
+ } else if (gcpm->dc_state == DC_IDLE) {
+ gcpm_pps_online(gcpm);
+ pr_debug("CHG_CHK: start DC Charging\n");
/* TODO: DC_ENABLE or DC_PASSTHROUGH depending on index */
gcpm->dc_state = DC_ENABLE_PASSTHROUGH;
gcpm->dc_index = index;
@@ -500,212 +469,194 @@
return ret;
}
-#define PPS_ERROR_RETRY_MS 1000
-
/* DC_ERROR_RETRY_MS <= DC_RUN_DELAY_MS */
#define DC_RUN_DELAY_MS 5000
#define DC_ERROR_RETRY_MS PPS_ERROR_RETRY_MS
+#define PPS_WAIT_RETRY_MS 3000
+#define PPS_ERROR_RETRY_MS 1000
+
+enum {
+ PPS_INDEX_NOT_SUPP = -1,
+ PPS_INDEX_TCPM = 1,
+ PPS_INDEX_WLC = 2,
+ PPS_INDEX_MAX = 2,
+};
+
+static struct pd_pps_data *gcpm_pps_data(struct gcpm_drv *gcpm)
+{
+ struct pd_pps_data *pps_data = NULL;
+
+ if (gcpm->pps_index == PPS_INDEX_TCPM)
+ pps_data = &gcpm->tcpm_pps_data;
+ else if (gcpm->pps_index == PPS_INDEX_WLC)
+ pps_data = &gcpm->wlc_pps_data;
+
+ return pps_data;
+}
+
+/*
+ * DC depends on PPS so run PPS first.
+ * - read source capabilities when stage transition from PPS_NONE to
+ * PPS_AVAILABLE (NOTE: pd_online=TCPM_PSY_PROG_ONLINE in this case)
+ *
+ * DISABLED => NONE -> AVAILABLE -> ACTIVE -> DISABLED
+ * -> DISABLED
+ * -> NOTSUPP
+ *
+ * return NULL until a PPS source becomes valid.
+ * TODO: set gcpm->pps_data to the gpm
+ */
+static int gcpm_pps_work(struct gcpm_drv *gcpm)
+{
+ int ret = 0, pps_index = 0;
+ int not_supported = 0;
+
+ if (gcpm->tcpm_pps_data.stage != PPS_NOTSUPP) {
+ struct pd_pps_data *pps_data = &gcpm->tcpm_pps_data;
+ int pps_ui;
+
+ pps_ui = pps_work(pps_data, pps_data->pps_psy);
+ if (pps_ui >= 0 && pps_data->stage == PPS_ACTIVE)
+ pps_index = PPS_INDEX_TCPM;
+ if (pps_data->pd_online < PPS_PSY_PROG_ONLINE)
+ pr_debug("PPS_Work: TCPM Wait pps_ui=%d online=%d, stage=%d\n",
+ pps_ui, pps_data->pd_online, pps_data->stage);
+ } else {
+ not_supported += 1;
+ }
+
+ if (gcpm->wlc_pps_data.stage != PPS_NOTSUPP) {
+ struct pd_pps_data *pps_data = &gcpm->wlc_pps_data;
+ int pps_ui;
+
+ pps_ui = pps_work(pps_data, pps_data->pps_psy);
+ if (pps_ui >= 0 && pps_data->stage == PPS_ACTIVE)
+ pps_index = PPS_INDEX_WLC;
+
+ if (pps_data->pd_online < PPS_PSY_PROG_ONLINE)
+ pr_debug("PPS_Work: WLC Wait pps_ui=%d online=%d, stage=%d\n",
+ pps_ui, pps_data->pd_online, pps_data->stage);
+ } else {
+ not_supported += 1;
+ }
+
+ /* 2 sources */
+ if (not_supported == PPS_INDEX_MAX)
+ return -ENODEV;
+
+ /* index==0 meand detecting */
+ if (gcpm->pps_index != pps_index)
+ pr_debug("PPS_Work: pps_index %d->%d\n",
+ gcpm->pps_index, pps_index);
+ if (gcpm->pps_index && !pps_index)
+ ret = -ENODEV;
+
+ gcpm->pps_index = pps_index;
+ return ret;
+}
+
+static int gcpm_pps_offline(struct gcpm_drv *gcpm)
+{
+ int ret;
+
+ if (gcpm->tcpm_pps_data.pps_psy) {
+ ret = pps_prog_offline(&gcpm->tcpm_pps_data,
+ gcpm->tcpm_pps_data.pps_psy);
+ if (ret < 0)
+ pr_err("PPS_DC: fail tcpm offline (%d)\n", ret);
+ }
+
+ if (gcpm->wlc_pps_data.pps_psy) {
+ ret = pps_prog_offline(&gcpm->wlc_pps_data,
+ gcpm->wlc_pps_data.pps_psy);
+ if (ret < 0)
+ pr_err("PPS_DC: fail wlc offline (%d)\n", ret);
+ }
+
+ gcpm->pps_index = 0;
+ return 0;
+}
+
/*
* pps_data->stage:
* PPS_NONE -> PPS_AVAILABLE -> PPS_ACTIVE
* -> PPS_DISABLED -> PPS_DISABLED
*/
-static void gcpm_pps_dc_work(struct work_struct *work)
+static void gcpm_pps_wlc_dc_work(struct work_struct *work)
{
struct gcpm_drv *gcpm =
container_of(work, struct gcpm_drv, pps_work.work);
- struct pd_pps_data *pps_data = &gcpm->pps_data;
- struct power_supply *tcpm_psy = gcpm->tcpm_psy;
- const bool log_on_done = tcpm_psy != 0 && pps_data->pd_online &&
- (gcpm->pps_data.stage || gcpm->dc_state);
- const int pre_out_ua = pps_data->op_ua;
- const int pre_out_uv = pps_data->out_uv;
+ struct pd_pps_data *pps_data;
int ret, pps_ui = -ENODEV;
+ /* spurious during init */
mutex_lock(&gcpm->chg_psy_lock);
-
- pr_info("PPS_Work: tcpm_psy_ok=%d pd_online=%d pps_stage=%d dc_state=%d",
- tcpm_psy != 0, pps_data->pd_online, gcpm->pps_data.stage,
- gcpm->dc_state);
-
- /* should not be calling this with !tcpm_psy */
- if (!gcpm->tcpm_psy)
- goto pps_dc_done;
-
- /* disable DC if not enabled */
- if (gcpm->dc_index <= 0 && gcpm->dc_state != DC_DISABLED) {
- const int dc_state = gcpm->dc_state;
-
- ret = gcpm_dc_stop(gcpm);
- if (ret < 0 || gcpm->dc_state != DC_DISABLED) {
- pr_info("PPS_DC: fail disable dc_state=%d (%d)\n",
- gcpm->dc_state, ret);
-
- pps_ui = DC_ERROR_RETRY_MS;
- goto pps_dc_reschedule;
- }
-
- pr_info("PPS_DC: disable DC dc_state=%d->%d\n",
- dc_state, gcpm->dc_state);
+ if (!gcpm->resume_complete || !gcpm->init_complete) {
+ mutex_unlock(&gcpm->chg_psy_lock);
+ return;
}
- if (pps_is_disabled(pps_data->stage))
- goto pps_dc_done;
+ /* disconnect, gcpm_chg_check() and most errors reset ->dc_index */
+ if (gcpm->dc_index <= 0) {
+ const int dc_state = gcpm->dc_state;
+ const int tgt_state = gcpm->dc_index < 0 ?
+ DC_DISABLED : DC_IDLE;
- /* DC depends on PPS so run PPS first.
- * - read source capabilities when stage transition from PPS_NONE to
- * PPS_AVAILABLE (NOTE: pd_online=TCPM_PSY_PROG_ONLINE in this case)
- * DISABLED ->DISABLED
- * ->NONE
- * NONE ->AVAILABLE
- * ->DISABLED
- * ->NOTSUPP
- * AVAILABLE ->ACTIVE
- * ->DISABLED
- * NOTSUPP ->NOTSUPP
- */
- pps_ui = pps_work(pps_data, tcpm_psy);
- pr_info("PPS_Work: pps_ui=%d pd_online=%d pps_stage=%d dc_state=%d\n",
- pps_ui, pps_data->pd_online,
- gcpm->pps_data.stage, gcpm->dc_state);
- if (pps_ui < 0) {
- pps_ui = PPS_ERROR_RETRY_MS;
- } else if (!pps_data->pd_online) {
-
- /* disable DC, revert to default charger */
- if (gcpm->dc_state != DC_DISABLED) {
+ /* try to disable DC, gcpm_chg_check() will re-enable if idle */
+ if (dc_state != tgt_state)
ret = gcpm_dc_stop(gcpm);
- if (ret < 0 || gcpm->dc_state != DC_DISABLED) {
- pr_info("PPS_DC: fail disable dc_state=%d (%d)\n",
- gcpm->dc_state, ret);
-
- /* will keep trying to disable */
- pps_ui = DC_ERROR_RETRY_MS;
+ if (gcpm->dc_state == DC_IDLE && tgt_state == DC_DISABLED)
+ gcpm->dc_state = DC_DISABLED;
+ if (gcpm->dc_state != tgt_state) {
+ pr_err("PPS_DC: fail disable dc_state=%d->%d (%d)\n",
+ dc_state, gcpm->dc_state, ret);
+ pps_ui = DC_ERROR_RETRY_MS;
+ } else {
+ /* and then disable PPS as well */
+ ret = gcpm_pps_offline(gcpm);
+ if (ret < 0) {
+ pr_err("PPS_DC: fail offline (%d)\n", ret);
+ pps_ui = PPS_ERROR_RETRY_MS;
}
}
- /* reset state on offline (should be already done) */
- pr_info("PPS_Work: PPS Offline\n");
- pps_init_state(pps_data);
- } else if (pps_data->stage == PPS_DISABLED) {
- /* PPS was disabled (not available) for this TA */
- pr_info("PPS_Work: PPS Disabled dc_ena=%d, dc_state=%d\n",
+ /* default pps_ui == -ENODEV */
+ goto pps_dc_reschedule;
+ }
+
+ /* check for one of the sources to come online, <0 when not supported */
+ ret = gcpm_pps_work(gcpm);
+ if (ret < 0) {
+ pr_info("PPS_Work: PPS Offline dc_index:%d->0 dc_state=%d\n",
gcpm->dc_index, gcpm->dc_state);
- /* reschedule to clear dc_ena for this session */
- if (gcpm->dc_index > 0) {
- pps_ui = PPS_ERROR_RETRY_MS;
- gcpm->dc_index = 0;
- }
- } else if (pps_data->stage != PPS_ACTIVE) {
+ pps_ui = PPS_ERROR_RETRY_MS;
+ gcpm->dc_index = 0;
+ goto pps_dc_reschedule;
+ }
+
+ /* will have PPS data when one of the sources becomes onliune */
+ pps_data = gcpm_pps_data(gcpm);
+ if (!pps_data) {
/*
- * DC run only when PPS is active, the only sane state for DC
- * when PPS is not ACTIVE is DC_DISABLE:
- * DC state is forced to DC_DISABLE when ->dc_index <= 0 at
- * the beginning of the loop
+ * Wait for PPS to be enabled: DC run only when PPS is active.
+ * NOTE: DC state is forced to DC_DISABLE and PPS is set to
+ * offline at the beginning of the loop when ->dc_index <= 0
*/
- pr_info("PPS_Work: PPS Wait stage=%d, pps_ui=%d dc_ena=%d dc_state=%d \n",
- pps_data->stage, pps_ui, gcpm->dc_index, gcpm->dc_state);
+ pr_debug("PPS_Work: PPS Wait for source dc_index=%d dc_state=%d\n",
+ gcpm->dc_index, gcpm->dc_state);
- /* TODO: keep track of time waiting for ACTIVE */
- } else if (gcpm->dc_state == DC_ENABLE) {
- struct pd_pps_data *pps_data = &gcpm->pps_data;
- bool pwr_ok;
-
- /* must run at the end of PPS negotiation */
- if (gcpm->out_ua == -1)
- gcpm->out_ua = min(gcpm->cc_max, pps_data->max_ua);
- if (gcpm->out_uv == -1) {
- struct power_supply *chg_psy =
- gcpm_chg_get_active(gcpm);
- unsigned long ta_max_v, value;
- int vbatt = -1;
-
- ta_max_v = pps_data->max_ua * pps_data->max_uv;
- ta_max_v /= gcpm->out_ua;
- if (ta_max_v > DC_TA_VMAX_MV)
- ta_max_v = DC_TA_VMAX_MV;
-
- if (chg_psy)
- vbatt = GPSY_GET_PROP(chg_psy,
- POWER_SUPPLY_PROP_VOLTAGE_NOW);
- if (vbatt < 0)
- vbatt = gcpm->fv_uv;
- if (vbatt < 0)
- vbatt = 0;
-
- /* good for pca9468 */
- value = 2 * vbatt + DC_VBATT_HEADROOM_MV;
- if (value < DC_TA_VMIN_MV)
- value = DC_TA_VMIN_MV;
-
- /* PPS voltage in 20mV steps */
- gcpm->out_uv = value - value % 20000;
- }
-
- pr_info("CHG_CHK: max_uv=%d,max_ua=%d out_uv=%d,out_ua=%d\n",
- pps_data->max_uv, pps_data->max_ua,
- gcpm->out_uv, gcpm->out_ua);
-
- pps_ui = pps_update_adapter(pps_data, gcpm->out_uv,
- gcpm->out_ua, tcpm_psy);
- if (pps_ui < 0)
- pps_ui = PPS_ERROR_RETRY_MS;
-
- /* wait until adapter is at or over request */
- pwr_ok = pps_data->out_uv == gcpm->out_uv &&
- pps_data->op_ua == gcpm->out_ua;
- if (pwr_ok) {
- ret = gcpm_chg_offline(gcpm);
- if (ret == 0)
- ret = gcpm_dc_start(gcpm, gcpm->dc_index);
- if (ret == 0) {
- gcpm->dc_state = DC_RUNNING;
- pps_ui = DC_RUN_DELAY_MS;
- } else if (pps_ui > DC_ERROR_RETRY_MS) {
- pps_ui = DC_ERROR_RETRY_MS;
- }
- }
-
- /*
- * TODO: add retries and switch to DC_ENABLE again or to
- * DC_DISABLED on timeout.
- */
-
- pr_info("PPS_DC: dc_state=%d out_uv=%d %d->%d, out_ua=%d %d->%d\n",
- gcpm->dc_state,
- pps_data->out_uv, pre_out_uv, gcpm->out_uv,
- pps_data->op_ua, pre_out_ua, gcpm->out_ua);
- } else if (gcpm->dc_state == DC_RUNNING) {
-
- ret = gcpm_chg_ping(gcpm, 0, 0);
- if (ret < 0)
- pr_err("PPS_DC: ping failed with %d\n", ret);
-
- /* update gcpm->out_uv, gcpm->out_ua */
- pr_info("PPS_DC: dc_state=%d out_uv=%d %d->%d out_ua=%d %d->%d\n",
- gcpm->dc_state,
- pps_data->out_uv, pre_out_uv, gcpm->out_uv,
- pps_data->op_ua, pre_out_ua, gcpm->out_ua);
-
- ret = gcpm_dc_charging(gcpm);
- if (ret < 0)
- pps_ui = DC_ERROR_RETRY_MS;
-
- ret = pps_update_adapter(&gcpm->pps_data,
- gcpm->out_uv, gcpm->out_ua,
- tcpm_psy);
- if (ret < 0)
- pps_ui = PPS_ERROR_RETRY_MS;
+ /* TODO: keep track of time waiting for ACTIVE and give up? */
+ pps_ui = PPS_WAIT_RETRY_MS;
} else if (gcpm->dc_state == DC_ENABLE_PASSTHROUGH) {
- struct pd_pps_data *pps_data = &gcpm->pps_data;
+ struct power_supply *pps_psy = pps_data->pps_psy;
- pps_ui = pps_update_adapter(pps_data, gcpm->out_uv,
- gcpm->out_ua, tcpm_psy);
+ /* steady on PPS, DC is not enabled */
+ pps_ui = pps_update_adapter(pps_data, -1, -1, pps_psy);
if (pps_ui < 0)
- pps_ui = PPS_ERROR_RETRY_MS;
+ pps_ui = DC_ERROR_RETRY_MS;
/* TODO: handoff handling of PPS to the DC charger */
ret = gcpm_chg_offline(gcpm);
@@ -718,37 +669,48 @@
pps_ui = DC_ERROR_RETRY_MS;
}
} else if (gcpm->dc_state == DC_PASSTHROUGH) {
+ const int pps_index = gcpm->pps_index;
struct power_supply *dc_psy;
dc_psy = gcpm_chg_get_active(gcpm);
if (!dc_psy) {
+ /* TODO: somethign went wrong, exit from it */
pr_err("PPS_Work: no adapter while in DC_PASSTHROUGH\n");
pps_ui = DC_ERROR_RETRY_MS;
} else {
- ret = GPSY_SET_PROP(dc_psy,
- GBMS_PROP_CHARGING_ENABLED,
- 1);
+ /* Keep enabling charging and pinging the watchdog */
+ ret = GPSY_SET_PROP(dc_psy, GBMS_PROP_CHARGING_ENABLED,
+ pps_index);
if (ret == 0) {
ret = gcpm_chg_ping(gcpm, 0, 0);
if (ret < 0)
pr_err("PPS_DC: ping failed with %d\n",
ret);
+ /* keep running to ping the adapters */
+ pps_ui = DC_RUN_DELAY_MS;
} else if (ret == -EBUSY) {
pps_ui = DC_ERROR_RETRY_MS;
} else {
- pr_err("PPS_Work: cannot enable DC_charging\n");
+ pr_err("PPS_Work: cannot enable DC_charging (%d)\n",
+ ret);
ret = gcpm_chg_set_online(gcpm, 0);
- if (ret < 0)
- pr_err("PPS_Work: online default %d\n",
- ret);
+ if (ret < 0) {
+ pr_err("PPS_Work: online default %d\n", ret);
+ pps_ui = DC_ERROR_RETRY_MS;
+ } else {
+ pr_err("PPS_Work: dc offline\n");
+ pps_ui = 0;
+ }
}
}
- } else {
+ } else if (pps_data) {
+ struct power_supply *pps_psy = pps_data->pps_psy;
+
/* steady on PPS, DC is not enabled */
- pps_ui = pps_update_adapter(&gcpm->pps_data, -1, -1, tcpm_psy);
+ pps_ui = pps_update_adapter(pps_data, -1, -1, pps_psy);
pr_info("PPS_Work: STEADY pd_online=%d pps_ui=%d dc_ena=%d dc_state=%d\n",
pps_data->pd_online, pps_ui, gcpm->dc_index,
@@ -757,24 +719,22 @@
pps_ui = PPS_ERROR_RETRY_MS;
}
+ if (pps_data)
+ pr_debug("PPS_Work: pps_stage=%d out_uv=%d op_ua=%d",
+ pps_data->stage, pps_data->out_uv, pps_data->op_ua);
+
pps_dc_reschedule:
if (pps_ui <= 0) {
- pr_info("PPS_Work: done=%d pps_stage=%d dc_state=%d",
- pps_ui, gcpm->pps_data.stage, gcpm->dc_state);
- if (log_on_done)
- pps_log(pps_data, "PPS_Work: done=%d pd_ol=%d pps_stage=%d dc_state=%d\n",
- pps_ui, pps_data->pd_online, gcpm->pps_data.stage,
- gcpm->dc_state);
+ pr_debug("PPS_Work: pps_ui=%d dc_state=%d",
+ pps_ui, gcpm->dc_state);
} else {
- pr_info("PPS_Work: reschedule in %d pps_stage=%d (%d:%d) dc_state=%d (%d:%d)",
- pps_ui, gcpm->pps_data.stage,
- gcpm->pps_data.out_uv, gcpm->pps_data.op_ua,
- gcpm->dc_state, gcpm->out_uv, gcpm->out_ua);
+ pr_debug("PPS_Work: reschedule in %d dc_state=%d (%d:%d)",
+ pps_ui, gcpm->dc_state, gcpm->out_uv, gcpm->out_ua);
+
schedule_delayed_work(&gcpm->pps_work,
msecs_to_jiffies(pps_ui));
}
-pps_dc_done:
mutex_unlock(&gcpm->chg_psy_lock);
}
@@ -797,32 +757,41 @@
mutex_lock(&gcpm->chg_psy_lock);
switch (psp) {
+ /* do not route to the active charger */
+ case GBMS_PROP_TAPER_CONTROL:
+ taper_control = (pval->intval != 0);
+ ta_check = taper_control != gcpm->taper_control;
+ gcpm->taper_control = taper_control;
+ route = false;
+ break;
+ /* also route to the active charger */
+ case GBMS_PROP_CHARGE_DISABLE:
+ /*
+ * google_charger send this on disconnect.
+ * TODO: reset DC state and PPS detection, disable dc
+ */
+ pr_info("%s: ChargeDisable value=%d\n", __func__, pval->intval);
+ ta_check = true;
+ break;
+ case POWER_SUPPLY_PROP_ONLINE:
+ ta_check = true;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_MAX:
+ psp = POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX;
+ /* compat, fall through */
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
+ ta_check = gcpm->fv_uv != pval->intval;
+ gcpm->fv_uv = pval->intval;
+ break;
- /* do not route to the active charger */
- case GBMS_PROP_TAPER_CONTROL:
- taper_control = (pval->intval != 0);
- ta_check = taper_control != gcpm->taper_control;
- gcpm->taper_control = taper_control;
- route = false;
- break;
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
+ ta_check = gcpm->cc_max != pval->intval;
+ gcpm->cc_max = pval->intval;
+ break;
- /* also route to the active charger */
- case POWER_SUPPLY_PROP_VOLTAGE_MAX:
- psp = POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX;
- /* compat, fall through */
- case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
- ta_check = gcpm->fv_uv != pval->intval;
- gcpm->fv_uv = pval->intval;
- break;
-
- case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
- ta_check = gcpm->cc_max != pval->intval;
- gcpm->cc_max = pval->intval;
- break;
-
- /* just route to the active charger */
- default:
- break;
+ /* just route to the active charger */
+ default:
+ break;
}
/* used only for debug */
@@ -847,7 +816,7 @@
psp, gcpm->chg_psy_active, name);
}
} else {
- pr_err("invalid charger=%d for prop=%d\n",
+ pr_err("invalid active charger = %d for prop=%d\n",
gcpm->chg_psy_active, psp);
}
@@ -877,15 +846,14 @@
return -ENODEV;
switch (psp) {
+ /* handle locally for now */
+ case GBMS_PROP_CHARGE_CHARGER_STATE:
+ gbms_propval_int64val(pval) = gcpm_get_charger_state(gcpm, chg_psy);
+ return 0;
- /* handle locally for now */
- case GBMS_PROP_CHARGE_CHARGER_STATE:
- gbms_propval_int64val(pval) = gcpm_get_charger_state(gcpm, chg_psy);
- return 0;
-
- /* route to the active charger */
- default:
- break;
+ /* route to the active charger */
+ default:
+ break;
}
return power_supply_get_property(chg_psy, psp, pval);
@@ -971,11 +939,16 @@
#define GCPM_TCPM_PSY_MAX 2
+/*thiscan run */
static void gcpm_init_work(struct work_struct *work)
{
struct gcpm_drv *gcpm = container_of(work, struct gcpm_drv,
init_work.work);
int i, found = 0, ret = 0;
+ bool dc_not_done;
+
+ /* might run along set_property() */
+ mutex_lock(&gcpm->chg_psy_lock);
/*
* could call pps_init() in probe() and use lazy init for ->tcpm_psy
@@ -987,24 +960,31 @@
tcpm_psy = pps_get_tcpm_psy(gcpm->device->of_node,
GCPM_TCPM_PSY_MAX);
if (!IS_ERR_OR_NULL(tcpm_psy)) {
- gcpm->tcpm_psy_name = tcpm_psy->desc->name;
+ const char *name = tcpm_psy->desc->name;
+
+ gcpm->tcpm_psy_name = name;
gcpm->tcpm_psy = tcpm_psy;
/* PPS charging: needs an APDO */
- ret = pps_init(&gcpm->pps_data, gcpm->device);
+ ret = pps_init(&gcpm->tcpm_pps_data, gcpm->device,
+ gcpm->tcpm_psy);
if (ret == 0 && gcpm->debug_entry)
- pps_init_fs(&gcpm->pps_data, gcpm->debug_entry);
+ pps_init_fs(&gcpm->tcpm_pps_data, gcpm->debug_entry);
if (ret < 0) {
pr_err("PPS init failure for %s (%d)\n",
- tcpm_psy->desc->name, ret);
+ name, ret);
} else {
- pps_init_state(&gcpm->pps_data);
+ gcpm->tcpm_pps_data.port_data =
+ power_supply_get_drvdata(tcpm_psy);
+
+ pps_init_state(&gcpm->tcpm_pps_data);
pr_info("PPS available for %s\n",
gcpm->tcpm_psy_name);
}
} else if (!tcpm_psy || !gcpm->log_psy_ratelimit) {
- pr_warn("PPS not available\n");
+ /* abort on an error */
+ pr_warn("PPS not available for tcpm\n");
gcpm->tcpm_phandle = 0;
} else {
pr_warn("tcpm power supply not found, retrying... ret:%d\n",
@@ -1014,6 +994,42 @@
}
+ /* TODO: lookup by phandle as the dude above */
+ if (gcpm->wlc_dc_name && !gcpm->wlc_dc_psy) {
+ struct power_supply *wlc_dc_psy;
+
+ wlc_dc_psy = power_supply_get_by_name(gcpm->wlc_dc_name);
+ if (wlc_dc_psy) {
+ const char *name = gcpm->wlc_dc_name;
+
+ gcpm->wlc_dc_psy = wlc_dc_psy;
+
+ /* PPS charging: needs an APDO */
+ ret = pps_init(&gcpm->wlc_pps_data, gcpm->device,
+ gcpm->wlc_dc_psy);
+ if (ret == 0 && gcpm->debug_entry)
+ pps_init_fs(&gcpm->wlc_pps_data, gcpm->debug_entry);
+ if (ret < 0) {
+ pr_err("PPS init failure for %s (%d)\n",
+ name, ret);
+ } else {
+ /* TODO: TBD */
+ gcpm->wlc_pps_data.port_data = NULL;
+ pps_init_state(&gcpm->wlc_pps_data);
+ pr_info("PPS available for %s\n", name);
+ }
+
+ } else if (!gcpm->log_psy_ratelimit) {
+ /* give up if wlc_dc_psy return an error */
+ pr_warn("PPS not available for %s\n", gcpm->wlc_dc_name);
+ gcpm->wlc_dc_name = NULL;
+ } else {
+ pr_warn("%s power supply not found, retrying... ret:%d\n",
+ gcpm->wlc_dc_name, ret);
+ gcpm->log_psy_ratelimit--;
+ }
+ }
+
/* default is index 0 */
for (i = 0; i < gcpm->chg_psy_count; i++) {
if (!gcpm->chg_psy_avail[i]) {
@@ -1039,24 +1055,43 @@
ret);
}
+ /* this is the reason why we need a lock here */
gcpm->resume_complete = true;
gcpm->init_complete = true;
}
- /* keep looking for late arrivals && TCPM */
- if (found != gcpm->chg_psy_count && gcpm->chg_psy_retries)
+ /* keep looking for late arrivals, TCPM and WLC if set */
+ if (found == gcpm->chg_psy_count)
+ gcpm->chg_psy_retries = 0;
+ else if (gcpm->chg_psy_retries)
gcpm->chg_psy_retries--;
- if (gcpm->chg_psy_retries || (gcpm->tcpm_phandle && !gcpm->tcpm_psy)) {
+ dc_not_done = (gcpm->tcpm_phandle && !gcpm->tcpm_psy) ||
+ (gcpm->wlc_dc_name && !gcpm->wlc_dc_psy);
+
+ pr_warn("%s:%d %s retries=%d dc_not_done=%d tcpm_ok=%d wlc_ok=%d\n",
+ __FILE__, __LINE__, __func__,
+ gcpm->chg_psy_retries,
+ dc_not_done,
+ (!gcpm->tcpm_phandle || gcpm->tcpm_psy),
+ (!gcpm->wlc_dc_name || gcpm->wlc_dc_psy));
+
+ if (gcpm->chg_psy_retries || dc_not_done) {
const unsigned long jif = msecs_to_jiffies(INIT_RETRY_DELAY_MS);
schedule_delayed_work(&gcpm->init_work, jif);
} else {
- pr_info("google_cpm init_work done %d/%d pps=%d\n", found,
- gcpm->chg_psy_count, !!gcpm->tcpm_psy);
+ pr_info("google_cpm init_work done %d/%d pps=%d wlc_dc=%d\n",
+ found, gcpm->chg_psy_count,
+ !!gcpm->tcpm_psy, !!gcpm->wlc_dc_psy);
}
+
+ /* might run along set_property() */
+ mutex_unlock(&gcpm->chg_psy_lock);
}
+/* ------------------------------------------------------------------------ */
+
static int gcpm_debug_get_active(void *data, u64 *val)
{
struct gcpm_drv *gcpm = data;
@@ -1089,7 +1124,6 @@
DEFINE_SIMPLE_ATTRIBUTE(gcpm_debug_active_fops, gcpm_debug_get_active,
gcpm_debug_set_active, "%llu\n");
-
static int gcpm_debug_dc_limit_demand_show(void *data, u64 *val)
{
struct gcpm_drv *gcpm = data;
@@ -1113,6 +1147,7 @@
return 0;
}
+
DEFINE_SIMPLE_ATTRIBUTE(gcpm_debug_dc_limit_demand_fops,
gcpm_debug_dc_limit_demand_show,
gcpm_debug_dc_limit_demand_set,
@@ -1122,9 +1157,12 @@
static int gcpm_debug_pps_stage_get(void *data, u64 *val)
{
struct gcpm_drv *gcpm = data;
+ struct pd_pps_data *pps_data;
mutex_lock(&gcpm->chg_psy_lock);
- *val = gcpm->pps_data.stage;
+ pps_data = gcpm_pps_data(gcpm);
+ if (pps_data)
+ *val = pps_data->stage;
mutex_unlock(&gcpm->chg_psy_lock);
return 0;
}
@@ -1133,12 +1171,15 @@
{
struct gcpm_drv *gcpm = data;
const int intval = (int)val;
+ struct pd_pps_data *pps_data;
if (intval < PPS_DISABLED || intval > PPS_ACTIVE)
return -EINVAL;
mutex_lock(&gcpm->chg_psy_lock);
- gcpm->pps_data.stage = intval;
+ pps_data = gcpm_pps_data(gcpm);
+ if (pps_data)
+ pps_data->stage = intval;
gcpm->force_pps = !pps_is_disabled(intval);
mod_delayed_work(system_wq, &gcpm->pps_work, 0);
mutex_unlock(&gcpm->chg_psy_lock);
@@ -1149,6 +1190,36 @@
DEFINE_SIMPLE_ATTRIBUTE(gcpm_debug_pps_stage_fops, gcpm_debug_pps_stage_get,
gcpm_debug_pps_stage_set, "%llu\n");
+static int gcpm_debug_dc_state_get(void *data, u64 *val)
+{
+ struct gcpm_drv *gcpm = data;
+
+ mutex_lock(&gcpm->chg_psy_lock);
+ *val = gcpm->dc_state;
+ mutex_unlock(&gcpm->chg_psy_lock);
+ return 0;
+}
+
+static int gcpm_debug_dc_state_set(void *data, u64 val)
+{
+ struct gcpm_drv *gcpm = data;
+ const int intval = (int)val;
+ int ret;
+
+ if (intval < DC_DISABLED || intval > DC_PASSTHROUGH)
+ return -EINVAL;
+
+ mutex_lock(&gcpm->chg_psy_lock);
+ gcpm->dc_state = intval;
+ ret = gcpm_chg_check(gcpm);
+ pr_debug("%s: gcpm_chg_check regt=%d\n", __func__, ret);
+ mutex_unlock(&gcpm->chg_psy_lock);
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(gcpm_debug_dc_state_fops, gcpm_debug_dc_state_get,
+ gcpm_debug_dc_state_set, "%llu\n");
+
static struct dentry *gcpm_init_fs(struct gcpm_drv *gcpm)
{
struct dentry *de;
@@ -1157,6 +1228,7 @@
if (IS_ERR_OR_NULL(de))
return NULL;
+ debugfs_create_file("dc_state", 0644, de, gcpm, &gcpm_debug_dc_state_fops);
debugfs_create_file("active", 0644, de, gcpm, &gcpm_debug_active_fops);
debugfs_create_file("dc_limit_demand", 0644, de, gcpm,
&gcpm_debug_dc_limit_demand_fops);
@@ -1166,6 +1238,8 @@
return de;
}
+/* ------------------------------------------------------------------------ */
+
static int gcpm_probe_psy_names(struct gcpm_drv *gcpm)
{
struct device *dev = gcpm->device;
@@ -1192,10 +1266,125 @@
return count;
}
+/* -------------------------------------------------------------------------
+ * Use to abstract the PPS adapter if needed.
+ */
+
+static int gcpm_pps_psy_set_property(struct power_supply *psy,
+ enum power_supply_property prop,
+ const union power_supply_propval *val)
+{
+ struct gcpm_drv *gcpm = power_supply_get_drvdata(psy);
+ struct pd_pps_data *pps_data;
+ int ret = 0;
+
+ mutex_lock(&gcpm->chg_psy_lock);
+
+ pps_data = gcpm_pps_data(gcpm);
+ if (!pps_data || !pps_data->pps_psy) {
+ pr_debug("%s: no target prop=%d ret=%d\n", __func__, prop, ret);
+ mutex_unlock(&gcpm->chg_psy_lock);
+ return -EAGAIN;
+ }
+
+ switch (prop) {
+ default:
+ ret = power_supply_set_property(pps_data->pps_psy, prop, val);
+ break;
+ }
+
+ mutex_unlock(&gcpm->chg_psy_lock);
+ pr_debug("%s: prop=%d val=%d ret=%d\n", __func__,
+ prop, val->intval, ret);
+
+ return ret;
+}
+
+static int gcpm_pps_psy_get_property(struct power_supply *psy,
+ enum power_supply_property prop,
+ union power_supply_propval *val)
+{
+ struct gcpm_drv *gcpm = power_supply_get_drvdata(psy);
+ struct pd_pps_data *pps_data;
+ int ret = 0;
+
+ mutex_lock(&gcpm->chg_psy_lock);
+
+ pps_data = gcpm_pps_data(gcpm);
+ if (!pps_data || !pps_data->pps_psy) {
+ pr_debug("%s: no target prop=%d ret=%d\n", __func__, prop, ret);
+ mutex_unlock(&gcpm->chg_psy_lock);
+ return -EAGAIN;
+ }
+
+ switch (prop) {
+ default:
+ ret = power_supply_get_property(pps_data->pps_psy, prop, val);
+ break;
+ }
+
+ mutex_unlock(&gcpm->chg_psy_lock);
+
+ pr_debug("%s: prop=%d val=%d ret=%d\n", __func__,
+ prop, val->intval, ret);
+
+ return ret;
+}
+
+/* check pps_is_avail(), pps_prog_online() and pps_check_type() */
+static enum power_supply_property gcpm_pps_psy_properties[] = {
+ POWER_SUPPLY_PROP_VOLTAGE_MAX,
+ POWER_SUPPLY_PROP_VOLTAGE_MIN,
+ POWER_SUPPLY_PROP_CURRENT_MAX,
+ POWER_SUPPLY_PROP_CURRENT_NOW, /* 17 */
+ POWER_SUPPLY_PROP_ONLINE, /* 4 */
+ POWER_SUPPLY_PROP_PRESENT, /* 3 */
+ POWER_SUPPLY_PROP_TYPE, /* */
+ POWER_SUPPLY_PROP_USB_TYPE, /* */
+ POWER_SUPPLY_PROP_VOLTAGE_NOW, /* */
+};
+
+static int gcpm_pps_psy_is_writeable(struct power_supply *psy,
+ enum power_supply_property psp)
+{
+ switch (psp) {
+ case POWER_SUPPLY_PROP_PRESENT:
+ case POWER_SUPPLY_PROP_ONLINE:
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ return 1;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static enum power_supply_usb_type gcpm_pps_usb_types[] = {
+ POWER_SUPPLY_USB_TYPE_PD_PPS
+};
+
+static const struct power_supply_desc gcpm_pps_psy_desc = {
+ .name = "gcpm_pps",
+ .type = POWER_SUPPLY_TYPE_UNKNOWN,
+ .get_property = gcpm_pps_psy_get_property,
+ .set_property = gcpm_pps_psy_set_property,
+ .properties = gcpm_pps_psy_properties,
+ .property_is_writeable = gcpm_pps_psy_is_writeable,
+ .num_properties = ARRAY_SIZE(gcpm_pps_psy_properties),
+
+ /* POWER_SUPPLY_PROP_USB_TYPE requires an array of these */
+ .usb_types = gcpm_pps_usb_types,
+ .num_usb_types = ARRAY_SIZE(gcpm_pps_usb_types),
+};
+
+/* ------------------------------------------------------------------------- */
+
#define LOG_PSY_RATELIMIT_CNT 200
static int google_cpm_probe(struct platform_device *pdev)
{
+ struct power_supply_config gcpm_pps_psy_cfg = { 0 };
struct power_supply_config psy_cfg = { 0 };
const char *tmp_name = NULL;
struct gcpm_drv *gcpm;
@@ -1211,7 +1400,7 @@
gcpm->chg_psy_retries = 10; /* chg_psy_retries * INIT_RETRY_DELAY_MS */
gcpm->out_uv = -1;
gcpm->out_ua = -1;
- INIT_DELAYED_WORK(&gcpm->pps_work, gcpm_pps_dc_work);
+ INIT_DELAYED_WORK(&gcpm->pps_work, gcpm_pps_wlc_dc_work);
INIT_DELAYED_WORK(&gcpm->init_work, gcpm_init_work);
mutex_init(&gcpm->chg_psy_lock);
@@ -1225,19 +1414,29 @@
return -ENOMEM;
}
- /* sub power supply names */
+ /* subs power supply names */
gcpm->chg_psy_count = gcpm_probe_psy_names(gcpm);
if (gcpm->chg_psy_count <= 0)
return -ENODEV;
- /* PPS needs a TCPM power supply */
+ /* DC/PPS needs at least one power supply of this type */
ret = of_property_read_u32(pdev->dev.of_node,
"google,tcpm-power-supply",
&gcpm->tcpm_phandle);
if (ret < 0)
pr_warn("google,tcpm-power-supply not defined\n");
- /* Direct Charging: valid only with PPS */
+ ret = of_property_read_string(pdev->dev.of_node,
+ "google,wlc_dc-power-supply",
+ &tmp_name);
+ if (ret == 0) {
+ gcpm->wlc_dc_name = devm_kstrdup(&pdev->dev, tmp_name,
+ GFP_KERNEL);
+ if (!gcpm->wlc_dc_name)
+ return -ENOMEM;
+ }
+
+ /* GCPM might need a gpio to enable/disable DC/PPS */
gcpm->dcen_gpio = of_get_named_gpio(pdev->dev.of_node,
"google,dc-en", 0);
if (gcpm->dcen_gpio >= 0) {
@@ -1251,6 +1450,7 @@
gcpm->dcen_gpio_default, ret);
}
+ /* Triggers to enable dc charging */
ret = of_property_read_u32(pdev->dev.of_node, "google,dc_limit-demand",
&gcpm->dc_limit_demand);
if (ret < 0)
@@ -1269,16 +1469,31 @@
psy_cfg.drv_data = gcpm;
psy_cfg.of_node = pdev->dev.of_node;
- gcpm->psy = devm_power_supply_register(gcpm->device, &gcpm_psy_desc,
+ gcpm->psy = devm_power_supply_register(gcpm->device,
+ &gcpm_psy_desc,
&psy_cfg);
if (IS_ERR(gcpm->psy)) {
ret = PTR_ERR(gcpm->psy);
if (ret == -EPROBE_DEFER)
return -EPROBE_DEFER;
- /* TODO: fail with -ENODEV */
- dev_err(gcpm->device, "Couldn't register as power supply, ret=%d\n",
- ret);
+ dev_err(gcpm->device, "Couldn't register gcpm, (%d)\n", ret);
+ return -ENODEV;
+ }
+
+ /* gcpm_pps_psy_cfg.of_node is used to find out the snk_pdos */
+ gcpm_pps_psy_cfg.drv_data = gcpm;
+ gcpm_pps_psy_cfg.of_node = pdev->dev.of_node;
+ gcpm->pps_psy = devm_power_supply_register(gcpm->device,
+ &gcpm_pps_psy_desc,
+ &gcpm_pps_psy_cfg);
+ if (IS_ERR(gcpm->pps_psy)) {
+ ret = PTR_ERR(gcpm->pps_psy);
+ if (ret == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+
+ dev_err(gcpm->device, "Couldn't register gcpm_pps (%d)\n", ret);
+ return -ENODEV;
}
/* give time to fg driver to start */
@@ -1304,6 +1519,9 @@
gcpm->chg_psy_avail[i] = NULL;
}
+ if (gcpm->wlc_dc_psy)
+ power_supply_put(gcpm->wlc_dc_psy);
+
return 0;
}
@@ -1333,3 +1551,130 @@
MODULE_DESCRIPTION("Google Charging Policy Manager");
MODULE_AUTHOR("AleX Pelosi <[email protected]>");
MODULE_LICENSE("GPL");
+
+#if 0
+
+/* NOTE: call with a lock around gcpm->chg_psy_lock */
+static int gcpm_dc_charging(struct gcpm_drv *gcpm)
+{
+ struct power_supply *dc_psy;
+ int vchg, ichg, status;
+
+ dc_psy = gcpm_chg_get_active(gcpm);
+ if (!dc_psy) {
+ pr_err("DC_CHG: invalid charger\n");
+ return -ENODEV;
+ }
+
+ vchg = GPSY_GET_PROP(dc_psy, POWER_SUPPLY_PROP_VOLTAGE_NOW);
+ ichg = GPSY_GET_PROP(dc_psy, POWER_SUPPLY_PROP_CURRENT_NOW);
+ status = GPSY_GET_PROP(dc_psy, POWER_SUPPLY_PROP_STATUS);
+
+ pr_err("DC_CHG: vchg=%d, ichg=%d status=%d\n",
+ vchg, ichg, status);
+
+ return 0;
+}
+
+static void gcpm_pps_dc_charging(struct gcpm_drv *gcpm)
+{
+ struct pd_pps_data *pps_data = &gcpm->pps_data;
+ struct power_supply *pps_psy = gcpm->tcpm_psy;
+ const int pre_out_ua = pps_data->op_ua;
+ const int pre_out_uv = pps_data->out_uv;
+ int ret, pps_ui = -ENODEV;
+
+ if (gcpm->dc_state == DC_ENABLE) {
+ struct pd_pps_data *pps_data = &gcpm->pps_data;
+ bool pwr_ok;
+
+ /* must run at the end of PPS negotiation */
+ if (gcpm->out_ua == -1)
+ gcpm->out_ua = min(gcpm->cc_max, pps_data->max_ua);
+ if (gcpm->out_uv == -1) {
+ struct power_supply *chg_psy =
+ gcpm_chg_get_active(gcpm);
+ unsigned long ta_max_v, value;
+ int vbatt = -1;
+
+ ta_max_v = pps_data->max_ua * pps_data->max_uv;
+ ta_max_v /= gcpm->out_ua;
+ if (ta_max_v > DC_TA_VMAX_MV)
+ ta_max_v = DC_TA_VMAX_MV;
+
+ if (chg_psy)
+ vbatt = GPSY_GET_PROP(chg_psy,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW);
+ if (vbatt < 0)
+ vbatt = gcpm->fv_uv;
+ if (vbatt < 0)
+ vbatt = 0;
+
+ /* good for pca9468 */
+ value = 2 * vbatt + DC_VBATT_HEADROOM_MV;
+ if (value < DC_TA_VMIN_MV)
+ value = DC_TA_VMIN_MV;
+
+ /* PPS voltage in 20mV steps */
+ gcpm->out_uv = value - value % 20000;
+ }
+
+ pr_info("CHG_CHK: max_uv=%d,max_ua=%d out_uv=%d,out_ua=%d\n",
+ pps_data->max_uv, pps_data->max_ua,
+ gcpm->out_uv, gcpm->out_ua);
+
+ pps_ui = pps_update_adapter(pps_data, gcpm->out_uv,
+ gcpm->out_ua, pps_psy);
+ if (pps_ui < 0)
+ pps_ui = PPS_ERROR_RETRY_MS;
+
+ /* wait until adapter is at or over request */
+ pwr_ok = pps_data->out_uv == gcpm->out_uv &&
+ pps_data->op_ua == gcpm->out_ua;
+ if (pwr_ok) {
+ ret = gcpm_chg_offline(gcpm);
+ if (ret == 0)
+ ret = gcpm_dc_start(gcpm, gcpm->dc_index);
+ if (ret == 0) {
+ gcpm->dc_state = DC_RUNNING;
+ pps_ui = DC_RUN_DELAY_MS;
+ } else if (pps_ui > DC_ERROR_RETRY_MS) {
+ pps_ui = DC_ERROR_RETRY_MS;
+ }
+ }
+
+ /*
+ * TODO: add retries and switch to DC_ENABLE again or to
+ * DC_DISABLED on timeout.
+ */
+
+ pr_info("PPS_DC: dc_state=%d out_uv=%d %d->%d, out_ua=%d %d->%d\n",
+ gcpm->dc_state,
+ pps_data->out_uv, pre_out_uv, gcpm->out_uv,
+ pps_data->op_ua, pre_out_ua, gcpm->out_ua);
+ } else if (gcpm->dc_state == DC_RUNNING) {
+
+ ret = gcpm_chg_ping(gcpm, 0, 0);
+ if (ret < 0)
+ pr_err("PPS_DC: ping failed with %d\n", ret);
+
+ /* update gcpm->out_uv, gcpm->out_ua */
+ pr_info("PPS_DC: dc_state=%d out_uv=%d %d->%d out_ua=%d %d->%d\n",
+ gcpm->dc_state,
+ pps_data->out_uv, pre_out_uv, gcpm->out_uv,
+ pps_data->op_ua, pre_out_ua, gcpm->out_ua);
+
+ ret = gcpm_dc_charging(gcpm);
+ if (ret < 0)
+ pps_ui = DC_ERROR_RETRY_MS;
+
+ ret = pps_update_adapter(&gcpm->pps_data,
+ gcpm->out_uv, gcpm->out_ua,
+ pps_psy);
+ if (ret < 0)
+ pps_ui = PPS_ERROR_RETRY_MS;
+ }
+
+ return pps_ui;
+}
+#endif
\ No newline at end of file
diff --git a/google_dc_pps.c b/google_dc_pps.c
index e48ab26..4a4a305 100644
--- a/google_dc_pps.c
+++ b/google_dc_pps.c
@@ -79,6 +79,9 @@
#if 0 //TODO
tcpm_put_partner_src_caps(&pps_data->src_caps);
#endif
+ pr_debug("%s: %s src_caps=%d\n", __func__,
+ pps_data->pps_psy ? pps_data->pps_psy->desc->name : "<>",
+ !!pps_data->src_caps);
pps_data->pd_online = TCPM_PSY_OFFLINE;
pps_data->stage = PPS_DISABLED;
@@ -233,6 +236,7 @@
if (pd_online != TCPM_PSY_PROG_ONLINE)
goto not_supp;
+ /* make the transition to active */
if (pps_data->stage != PPS_ACTIVE) {
int rc;
@@ -276,7 +280,9 @@
ret = GPSY_SET_PROP(tcpm_psy, POWER_SUPPLY_PROP_ONLINE,
TCPM_PSY_PROG_ONLINE);
- if (ret == 0) {
+ if (ret == -EOPNOTSUPP) {
+ pps->stage = PPS_NOTSUPP;
+ } else if (ret == 0) {
pps->pd_online = TCPM_PSY_PROG_ONLINE;
pps->stage = PPS_NONE;
}
@@ -432,6 +438,18 @@
int pps_init_fs(struct pd_pps_data *pps_data, struct dentry *de)
{
+ struct power_supply *pps_psy = pps_data->pps_psy;
+
+ if (pps_psy) {
+ char name[32];
+
+ scnprintf(name, sizeof(name), "%s_pps", pps_psy->desc->name);
+
+ pps_data->log = logbuffer_register(name);
+ if (IS_ERR(pps_data->log))
+ pps_data->log = NULL;
+ }
+
if (!de)
return 0;
@@ -561,7 +579,8 @@
/* Look for the connector and retrieve source capabilities.
* pps_data->nr_snk_pdo == 0 means no PPS
*/
-int pps_init(struct pd_pps_data *pps_data, struct device *dev)
+int pps_init(struct pd_pps_data *pps_data, struct device *dev,
+ struct power_supply *pps_psy)
{
const char *psy_name;
int ret;
@@ -572,6 +591,8 @@
ret = pps_init_snk(pps_data, dev->of_node);
if (ret < 0)
return ret;
+ if (!pps_data->nr_snk_pdo)
+ pr_err("nr_sink_pdo=%d this is a fake one\n", pps_data->nr_snk_pdo);
psy_name = pps_psy->desc->name ? pps_psy->desc->name : "<>";
@@ -597,6 +618,7 @@
}
}
+ pps_data->pps_psy = pps_psy;
return 0;
}
// EXPORT_SYMBOL_GPL(pps_init);
diff --git a/google_dc_pps.h b/google_dc_pps.h
index 384d1e2..05560e5 100644
--- a/google_dc_pps.h
+++ b/google_dc_pps.h
@@ -79,7 +79,8 @@
#define pps_is_disabled(x) (((x) == PPS_NOTSUPP) || ((x) == PPS_DISABLED))
struct dentry;
-int pps_init(struct pd_pps_data *pps_data, struct device *dev);
+int pps_init(struct pd_pps_data *pps_data, struct device *dev,
+ struct power_supply *pps_psy);
int pps_init_fs(struct pd_pps_data *pps_data, struct dentry *de);
/* reset state and leave in DISABLED */
void pps_init_state(struct pd_pps_data *pps_data);
diff --git a/pca9468_charger.c b/pca9468_charger.c
index 037f609..d8444d4 100644
--- a/pca9468_charger.c
+++ b/pca9468_charger.c
@@ -684,6 +684,15 @@
pca9468->tcpm_psy_name = tcpm_psy->desc->name;
pca9468->pd = tcpm_psy;
+ /* setup PPS, not needed if tcpm-power-supply is not there */
+ ret = pps_init(&pca9468->pps_data, pca9468->dev, tcpm_psy);
+ if (ret == 0 && pca9468->debug_root)
+ pps_init_fs(&pca9468->pps_data, pca9468->debug_root);
+ if (ret == 0) {
+ pps_init_state(&pca9468->pps_data);
+ pr_info("pca9468: PPS direct available\n");
+ }
+
check_online:
online = pps_prog_check_online(&pca9468->pps_data, pca9468->pd);
if (!online)
@@ -5251,16 +5260,6 @@
if (ret < 0)
dev_err(dev, "error while registering debugfs %d\n", ret);
- /* setup PPS, not needed if tcpm-power-supply is not there */
- ret = pps_init(&pca9468_chg->pps_data, pca9468_chg->dev);
- if (ret == 0 && pca9468_chg->debug_root)
- pps_init_fs(&pca9468_chg->pps_data,
- pca9468_chg->debug_root);
- if (ret == 0) {
- pps_init_state(&pca9468_chg->pps_data);
- pr_info("pca9468: PPS direct available\n");
- }
-
#ifdef CONFIG_DC_STEP_CHARGING
ret = pca9468_step_chg_init(pca9468_chg->dev);
if (ret < 0) {