google_dc_pps: initial support for non-tcpm pps ports

Add initial support for non TCPM type PPS ports.

Bug: 177471684
Test: start DC charging, check logs
Signed-off-by: AleX Pelosi <[email protected]>
Change-Id: I4e68cf95a470bab920a96c1c1fd05c0d6f27540a
diff --git a/google_dc_pps.c b/google_dc_pps.c
index 9162cf2..e48ab26 100644
--- a/google_dc_pps.c
+++ b/google_dc_pps.c
@@ -68,7 +68,11 @@
 }
 // EXPORT_SYMBOL_GPL(pps_log);
 
-/* SW enable detect setting ->stage = PPS_NONE and calling pps_work() */
+/*
+ * State is initialized to PPS_DISABLED and SW will enable detect setting
+ * ->stage to PPS_NONE and calling pps_work() until the state becomes
+ * PPS_ACTIVE or PPS_NOTSUPP.
+ */
 void pps_init_state(struct pd_pps_data *pps_data)
 {
 	/* pps_data->src_caps can be NULL */
@@ -89,34 +93,75 @@
 }
 // EXPORT_SYMBOL_GPL(pps_init_state);
 
-static struct tcpm_port *chg_get_tcpm_port(struct power_supply *tcpm_psy)
+
+/*
+ * pps_psy can be tcpm, wireless or gcpm_pps.
+ * NOTE: gcpm_pps route the props to the underlying psy
+ */
+static int pps_check_type(struct pd_pps_data *pps_data)
 {
-	return tcpm_psy ? power_supply_get_drvdata(tcpm_psy) : NULL;
+	struct power_supply *pps_psy = pps_data->pps_psy;
+	union power_supply_propval pval;
+	int ret;
+
+	ret = power_supply_get_property(pps_psy, POWER_SUPPLY_PROP_USB_TYPE, &pval);
+	if (ret == 0 && pval.intval == POWER_SUPPLY_USB_TYPE_PD_PPS)
+		return true;
+	ret = power_supply_get_property(pps_psy, POWER_SUPPLY_PROP_TYPE, &pval);
+	if (ret == 0 && pval.intval == POWER_SUPPLY_TYPE_WIRELESS_EXT)
+		return true;
+
+	return false;
 }
 
-/* PUBLIC TODO: pass the actual PDO, deprecate */
-int chg_update_capability(struct power_supply *tcpm_psy,
-				 unsigned int nr_pdo,
-				 u32 pps_cap)
+/* always call with a tcpm_psy */
+static struct tcpm_port *chg_get_tcpm_port(struct power_supply *tcpm_psy)
 {
-	struct tcpm_port *port = chg_get_tcpm_port(tcpm_psy);
+	union power_supply_propval pval;
+	void *port = NULL;
+	int ret;
+
+	/* port is valid for a tcpm power supply but not for gcpm */
+	ret = power_supply_get_property(tcpm_psy, POWER_SUPPLY_PROP_USB_TYPE, &pval);
+	if (ret == 0 && pval.intval == POWER_SUPPLY_USB_TYPE_PD_PPS)
+		port = power_supply_get_drvdata(tcpm_psy);
+
+	/* TODO: make sure that tcpm_psy is really a tcpm source */
+
+	return port;
+}
+
+/*
+ * called from google_charger direcly on a tcpm_psy.
+ * NOTE: Do not call on anything else!
+ */
+int chg_update_capability(struct power_supply *tcpm_psy, unsigned int nr_pdo,
+		          u32 pps_cap)
+{
+	struct tcpm_port *port;
 	const u32 pdo[] = {PDO_FIXED(5000, PD_SNK_MAX_MA, PDO_FIXED_FLAGS),
 			   PDO_FIXED(PD_SNK_MAX_MV, PD_SNK_MAX_MA_9V, 0),
 			   pps_cap};
+	int ret = -ENODEV;
 
-	if (!port)
-		return -ENODEV;
-
-	if (!nr_pdo || nr_pdo > PDO_MAX_SUPP)
+	if (!tcpm_psy || !nr_pdo || nr_pdo > PDO_MAX_SUPP)
 		return -EINVAL;
 
-	return tcpm_update_sink_capabilities(port, pdo, nr_pdo, OP_SNK_MW);
+	port = chg_get_tcpm_port(tcpm_psy);
+	if (port)
+		ret = tcpm_update_sink_capabilities(port, pdo, nr_pdo, OP_SNK_MW);
+
+	return ret;
+}
+
+	return ret;
 }
 
 /* false when not present or error (either way don't run) */
 static unsigned int pps_is_avail(struct pd_pps_data *pps,
 				 struct power_supply *tcpm_psy)
 {
+	/* TODO: make silent, check return value and value */
 	pps->max_uv = GPSY_GET_PROP(tcpm_psy, POWER_SUPPLY_PROP_VOLTAGE_MAX);
 	pps->min_uv = GPSY_GET_PROP(tcpm_psy, POWER_SUPPLY_PROP_VOLTAGE_MIN);
 	pps->max_ua = GPSY_GET_PROP(tcpm_psy, POWER_SUPPLY_PROP_CURRENT_MAX);
@@ -143,6 +188,7 @@
 	if (!tcpm_psy)
 		return -ENODEV;
 
+	/* NOTE: the adapter should already be in PROG_ONLINE */
 	rc = GPSY_SET_PROP(tcpm_psy, POWER_SUPPLY_PROP_ONLINE,
 			   TCPM_PSY_PROG_ONLINE);
 	if (rc == 0)
@@ -156,12 +202,17 @@
 
 int pps_get_src_cap(struct pd_pps_data *pps, struct power_supply *tcpm_psy)
 {
-	struct tcpm_port *port = chg_get_tcpm_port(tcpm_psy);
+	struct tcpm_port *port;
 
-	if (!pps || !port)
+	if (!pps)
 		return -EINVAL;
 
-	pps->nr_src_cap = tcpm_get_partner_src_caps(port, &pps->src_caps);
+	port = chg_get_tcpm_port(tcpm_psy);
+	if (port)
+		pps->nr_src_cap = tcpm_get_partner_src_caps(port, &pps->src_caps);
+
+	/* check if this is is a pps_data thingie */
+
 
 	return pps->nr_src_cap;
 }
@@ -209,6 +260,7 @@
 	}
 
 	return true;
+
 not_supp:
 	pps_init_state(pps_data);
 	pps_data->stage = PPS_NOTSUPP;
@@ -238,7 +290,7 @@
 {
 	int ret;
 
-	if (!tcpm_psy)
+	if (!pps || !tcpm_psy)
 		return -ENODEV;
 
 	/* pd_online = TCPM_PSY_OFFLINE, stage = PPS_NONE; */
@@ -283,7 +335,7 @@
 	u32 max_mv, max_ma, max_mw;
 	u32 current_mw, current_ma;
 
-	if (pps->nr_src_cap < 2)
+	if (!pps || !tcpm_psy || pps->nr_src_cap < 2)
 		return -EINVAL;
 
 	current_ma = pps->op_ua / 1000;
@@ -381,7 +433,7 @@
 int pps_init_fs(struct pd_pps_data *pps_data, struct dentry *de)
 {
 	if (!de)
-		return -ENODEV;
+		return 0;
 
 	debugfs_create_file("pps_out_uv", 0600, de, pps_data,
 			    &debug_pps_out_uv_fops);
@@ -392,6 +444,19 @@
 
 	return 0;
 }
+
+int pps_register_logbuffer(struct pd_pps_data *pps_data, const char *name)
+{
+	int ret = 0;
+
+	pps_data->log = logbuffer_register(name);
+	if (IS_ERR(pps_data->log)) {
+		ret = PTR_ERR(pps_data->log);
+		pps_data->log = NULL;
+	}
+
+	return ret;
+}
 // EXPORT_SYMBOL_GPL(pps_init_fs);
 
 /* ------------------------------------------------------------------------ */
@@ -408,10 +473,8 @@
 		prop = of_get_property(node, "google,usb-c-connector", NULL);
 	if (!prop)
 		prop = of_get_property(node, "google,tcpm-power-supply", NULL);
-	if (!prop) {
-		pr_err("Couldn't find property \n");
+	if (!prop)
 		return -ENOENT;
-	}
 
 	dn = of_find_node_by_phandle(be32_to_cpup(prop));
 	if (!dn) {
@@ -471,25 +534,50 @@
 	return -ENODATA;
 }
 
-static int pps_init_node(struct pd_pps_data *pps_data,
+static int pps_init_snk(struct pd_pps_data *pps_data,
 			 struct device_node *node)
 {
-	int ret, nr_snk_pdo;
+	int ret, nr_snk_pdo = 0;
 
 	memset(pps_data, 0, sizeof(*pps_data));
 
 	nr_snk_pdo = pps_get_sink_pdo(pps_data->snk_pdo, PDO_MAX_OBJECTS, node);
 	if (nr_snk_pdo < 0) {
-		pr_err("Couldn't read sink-pdos, ret %d\n", nr_snk_pdo);
-		return -ENOENT;
+		pr_err("Couldn't find connector property (%d)\n", nr_snk_pdo);
+		nr_snk_pdo = 0;
+	} else {
+		ret = pps_find_apdo(pps_data, nr_snk_pdo);
+		if (ret < 0) {
+			pr_err("nr_sink_pdo=%d sink APDO not found ret=%d\n",
+				nr_snk_pdo, ret);
+			return -ENOENT;
+		}
 	}
 
-	ret = pps_find_apdo(pps_data, nr_snk_pdo);
-	if (ret < 0) {
-		pr_err("nr_sink_pdo=%d sink APDO not found ret=%d\n",
-			nr_snk_pdo, ret);
-		return -ENOENT;
-	}
+	pps_data->nr_snk_pdo = nr_snk_pdo;
+	return 0;
+}
+
+/* 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)
+{
+	const char *psy_name;
+	int ret;
+
+	if (!pps_data || !pps_psy)
+		return -EINVAL;
+
+	ret = pps_init_snk(pps_data, dev->of_node);
+	if (ret < 0)
+		return ret;
+
+	psy_name = pps_psy->desc->name ? pps_psy->desc->name : "<>";
+
+	/* TODO: look in the power supply device node ? maybe? */
+	if (!pps_data->nr_snk_pdo)
+		pr_warn("%s has nr_sink_pdo=0\n", psy_name);
 
 	/*
 	 * The port needs to ping or update the PPS adapter every 10 seconds
@@ -499,36 +587,16 @@
 	 *
 	 * Remove this wakeup source once we fix the Qualcomm PD phy issue.
 	 */
-	pps_data->stay_awake = of_property_read_bool(node, "google,pps-awake");
+	pps_data->stay_awake = of_property_read_bool(dev->of_node,
+						     "google,pps-awake");
 	if (pps_data->stay_awake) {
-		pps_data->pps_ws = wakeup_source_register(NULL, "google-pps");
+		pps_data->pps_ws = wakeup_source_register(NULL, psy_name);
 		if (!pps_data->pps_ws) {
 			pr_err("Failed to register wakeup source\n");
 			return -ENODEV;
 		}
 	}
 
-	pps_data->nr_snk_pdo = nr_snk_pdo;
-
-	return 0;
-}
-
-/* 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 ret;
-
-	ret = pps_init_node(pps_data, dev->of_node);
-	if (ret < 0)
-		return ret;
-
-	/* TODO(168287929): name should be const*/
-	pps_data->log = logbuffer_register((char*)dev_name(dev));
-	if (IS_ERR(pps_data->log))
-		pps_data->log = NULL;
-
 	return 0;
 }
 // EXPORT_SYMBOL_GPL(pps_init);
@@ -537,32 +605,34 @@
 
 /*
  * This is the first part of the DC/PPS charging state machine.
- * Detect and configure the PPS adapter for the profile.
+ * Detect and configure the PPS adapter for the PPS profile.
+ * TODO: cleanup the interface
  *
  * returns:
  * . the max update interval pps should vote for
  * . 0 to disable the PPS update interval voter
  * . <0 for error
  */
-int pps_work(struct pd_pps_data *pps, struct power_supply *tcpm_psy)
+int pps_work(struct pd_pps_data *pps, struct power_supply *pps_psy)
 {
-	int pd_online, usbc_type;
+	int type_ok, pd_online;
 	unsigned int stage;
 
-	if (!tcpm_psy)
-		return -ENODEV;
+	/* detection is done for this cycle */
+	if (pps->stage == PPS_NOTSUPP)
+		return 0;
 
 	/*
 	 * 2) pps->pd_online == TCPM_PSY_PROG_ONLINE && stage == PPS_NONE
 	 *  If the source really support PPS (set in 1): set stage to
 	 *  PPS_AVAILABLE and reschedule after PD_T_PPS_TIMEOUT
 	 */
-	if (pps->pd_online == TCPM_PSY_PROG_ONLINE && pps->stage == PPS_NONE) {
+	if (pps->stage == PPS_NONE && pps->pd_online == TCPM_PSY_PROG_ONLINE) {
 		int rc;
 
-		pps->stage = pps_is_avail(pps, tcpm_psy);
+		pps->stage = pps_is_avail(pps, pps_psy);
 		if (pps->stage == PPS_AVAILABLE) {
-			rc = pps_ping(pps, tcpm_psy);
+			rc = pps_ping(pps, pps_psy);
 			if (rc < 0) {
 				pps->pd_online = TCPM_PSY_FIXED_ONLINE;
 				return 0;
@@ -572,7 +642,7 @@
 				__pm_stay_awake(pps->pps_ws);
 
 			pps->last_update = get_boot_sec();
-			rc = pps_get_src_cap(pps, tcpm_psy);
+			rc = pps_get_src_cap(pps, pps_psy);
 			if (rc < 0)
 				pps_log(pps, "Cannot get partner src caps");
 		}
@@ -585,7 +655,7 @@
 	 * The prop is set to TCPM_PSY_PROG_ONLINE (from TCPM_PSY_FIXED_ONLINE)
 	 * when usbc_type is POWER_SUPPLY_USB_TYPE_PD_PPS.
 	 */
-	pd_online = GPSY_GET_PROP(tcpm_psy, POWER_SUPPLY_PROP_ONLINE);
+	pd_online = GPSY_GET_PROP(pps_psy, POWER_SUPPLY_PROP_ONLINE);
 	if (pd_online < 0)
 		return 0;
 
@@ -610,36 +680,37 @@
 
 	/*
 	 * 1) stage == PPS_NONE && pps->pd_online!=TCPM_PSY_PROG_ONLINE
-	 *  If usbc_type is POWER_SUPPLY_USB_TYPE_PD_PPS and pd_online is
-	 *  TCPM_PSY_FIXED_ONLINE, enable PSPS (set POWER_SUPPLY_PROP_ONLINE to
-	 *  TCPM_PSY_PROG_ONLINE and reschedule in PD_T_PPS_TIMEOUT.
+	 *  If usbc_type is ok and pd_online is TCPM_PSY_FIXED_ONLINE,
+	 *  set POWER_SUPPLY_PROP_ONLINE to TCPM_PSY_PROG_ONLINE (enable PPS)
+	 *  and reschedule in PD_T_PPS_TIMEOUT.
 	 */
-	usbc_type = GPSY_GET_PROP(tcpm_psy, POWER_SUPPLY_PROP_USB_TYPE);
-	if (pd_online == TCPM_PSY_FIXED_ONLINE &&
-	    usbc_type == POWER_SUPPLY_USB_TYPE_PD_PPS) {
+	type_ok = pps_check_type(pps);
+	if (type_ok && pd_online == TCPM_PSY_FIXED_ONLINE) {
 		int rc;
 
-		rc = pps_prog_online(pps, tcpm_psy);
+		rc = pps_prog_online(pps, pps_psy);
 		switch (rc) {
 		case 0:
 			return PD_T_PPS_TIMEOUT;
 		case -EAGAIN:
 			pps_log(pps, "work: not in SNK_READY, rerun");
 			return rc;
-
 		case -EOPNOTSUPP:
 			pps->stage = PPS_NOTSUPP;
-			if (pps->stay_awake)
-				__pm_relax(pps->pps_ws);
-
-			pps_log(pps, "work: PPS not supported for this TA");
 			break;
 		default:
 			pps_log(pps, "work: PROP_ONLINE (%d)", rc);
 			break;
 		}
 
-		return 0;
+	}
+
+	/* reset PPS_NOTSUPP with pps_init_state() */
+	if (pps->stage == PPS_NOTSUPP) {
+		if (pps->stay_awake)
+			__pm_relax(pps->pps_ws);
+		pps_log(pps, "work: PPS not supported for %s",
+			pps_psy->desc->name);
 	}
 
 	pps->pd_online = pd_online;
@@ -833,9 +904,10 @@
 		    unsigned int ta_max_vol, unsigned int ta_max_cur,
 		    struct power_supply *tcpm_psy)
 {
-	struct tcpm_port *port = chg_get_tcpm_port(tcpm_psy);
+	struct tcpm_port *port;
 	const unsigned int max_mw = ta_max_vol * ta_max_cur;
 
+	port = chg_get_tcpm_port(tcpm_psy);
 	if (!port)
 		return -ENODEV;
 	if (ta_idx > PDO_MAX_OBJECTS)
diff --git a/google_dc_pps.h b/google_dc_pps.h
index dfb5b82..384d1e2 100644
--- a/google_dc_pps.h
+++ b/google_dc_pps.h
@@ -28,7 +28,7 @@
 #define CHG_PPS_VOTER		"pps_chg"
 
 enum pd_pps_stage {
-	PPS_NOTSUPP = -1,	/* tried and failed */
+	PPS_NOTSUPP = -1,	/* tried and failed or disconnected */
 	PPS_DISABLED = 0,	/* default state, never tried */
 	PPS_NONE,		/* try to enable */
 	PPS_AVAILABLE,
@@ -45,6 +45,9 @@
 };
 
 struct pd_pps_data {
+	struct power_supply *pps_psy;
+	void *port_data;
+
 	struct wakeup_source *pps_ws;
 	bool stay_awake;
 
@@ -113,6 +116,8 @@
 
 int pps_get_src_cap(struct pd_pps_data *pps, struct power_supply *tcpm_psy);
 
+int pps_register_logbuffer(struct pd_pps_data *pps_data, const char *name);
+
 void pps_log(struct pd_pps_data *pps, const char *fmt, ...);
 
 /* probe */