linux-usb.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/6] ucsi driver changes for v5.8
@ 2020-04-23 13:20 Heikki Krogerus
  2020-04-23 13:20 ` [PATCH 1/6] usb: typec: ucsi: set USB data role when partner type is power cable/ufp Heikki Krogerus
                   ` (5 more replies)
  0 siblings, 6 replies; 7+ messages in thread
From: Heikki Krogerus @ 2020-04-23 13:20 UTC (permalink / raw)
  To: Greg Kroah-Hartman; +Cc: linux-usb, K V, Abhilash

Hi Greg,

Here are the ucsi driver changes I've collected so far. On top of the
usual driver improvements, there is a patch from Abhilash where a USB
type power supply (so the power supply class) is registered for each
port.

Let me know if you want me to change anything.

thanks,

K V, Abhilash (5):
  usb: typec: ucsi: Workaround for missed op_mode change
  usb: typec: ucsi: replace magic numbers
  usb: typec: ucsi: Correct bit-mask for CCI
  usb: typec: ucsi: save power data objects in PD mode
  usb: typec: ucsi: register with power_supply class

Mayank Rana (1):
  usb: typec: ucsi: set USB data role when partner type is power
    cable/ufp

 drivers/usb/typec/ucsi/Makefile |   4 +
 drivers/usb/typec/ucsi/psy.c    | 241 ++++++++++++++++++++++++++++++++
 drivers/usb/typec/ucsi/trace.c  |  10 +-
 drivers/usb/typec/ucsi/ucsi.c   |  41 +++++-
 drivers/usb/typec/ucsi/ucsi.h   |  26 +++-
 5 files changed, 315 insertions(+), 7 deletions(-)
 create mode 100644 drivers/usb/typec/ucsi/psy.c

-- 
2.26.1


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH 1/6] usb: typec: ucsi: set USB data role when partner type is power cable/ufp
  2020-04-23 13:20 [PATCH 0/6] ucsi driver changes for v5.8 Heikki Krogerus
@ 2020-04-23 13:20 ` Heikki Krogerus
  2020-04-23 13:20 ` [PATCH 2/6] usb: typec: ucsi: Workaround for missed op_mode change Heikki Krogerus
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Heikki Krogerus @ 2020-04-23 13:20 UTC (permalink / raw)
  To: Greg Kroah-Hartman; +Cc: linux-usb, K V, Abhilash, Mayank Rana

From: Mayank Rana <mrana@codeaurora.org>

Currently UCSI framework doesn't update USB data role when partner type
is reported as power cable or power cable with ufp connected. This
results into no USB host mode functionality. This is valid usecase where
user wants to use legacy type c power cable with type a female connector
to attach different USB devices like mouse, thumb drive etc. Hence update
USB data role as host when partner type is reported as power cable or
power cable with ufp connected.

Signed-off-by: Mayank Rana <mrana@codeaurora.org>
Signed-off-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
---
 drivers/usb/typec/ucsi/ucsi.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c
index ddf2ad3752de..e9baa9a749e5 100644
--- a/drivers/usb/typec/ucsi/ucsi.c
+++ b/drivers/usb/typec/ucsi/ucsi.c
@@ -566,6 +566,8 @@ static void ucsi_partner_change(struct ucsi_connector *con)
 
 	switch (UCSI_CONSTAT_PARTNER_TYPE(con->status.flags)) {
 	case UCSI_CONSTAT_PARTNER_TYPE_UFP:
+	case UCSI_CONSTAT_PARTNER_TYPE_CABLE:
+	case UCSI_CONSTAT_PARTNER_TYPE_CABLE_AND_UFP:
 		typec_set_data_role(con->port, TYPEC_HOST);
 		break;
 	case UCSI_CONSTAT_PARTNER_TYPE_DFP:
@@ -627,6 +629,8 @@ static void ucsi_handle_connector_change(struct work_struct *work)
 
 		switch (UCSI_CONSTAT_PARTNER_TYPE(con->status.flags)) {
 		case UCSI_CONSTAT_PARTNER_TYPE_UFP:
+		case UCSI_CONSTAT_PARTNER_TYPE_CABLE:
+		case UCSI_CONSTAT_PARTNER_TYPE_CABLE_AND_UFP:
 			typec_set_data_role(con->port, TYPEC_HOST);
 			break;
 		case UCSI_CONSTAT_PARTNER_TYPE_DFP:
@@ -927,6 +931,8 @@ static int ucsi_register_port(struct ucsi *ucsi, int index)
 
 	switch (UCSI_CONSTAT_PARTNER_TYPE(con->status.flags)) {
 	case UCSI_CONSTAT_PARTNER_TYPE_UFP:
+	case UCSI_CONSTAT_PARTNER_TYPE_CABLE:
+	case UCSI_CONSTAT_PARTNER_TYPE_CABLE_AND_UFP:
 		typec_set_data_role(con->port, TYPEC_HOST);
 		break;
 	case UCSI_CONSTAT_PARTNER_TYPE_DFP:
-- 
2.26.1


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 2/6] usb: typec: ucsi: Workaround for missed op_mode change
  2020-04-23 13:20 [PATCH 0/6] ucsi driver changes for v5.8 Heikki Krogerus
  2020-04-23 13:20 ` [PATCH 1/6] usb: typec: ucsi: set USB data role when partner type is power cable/ufp Heikki Krogerus
@ 2020-04-23 13:20 ` Heikki Krogerus
  2020-04-23 13:20 ` [PATCH 3/6] usb: typec: ucsi: replace magic numbers Heikki Krogerus
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Heikki Krogerus @ 2020-04-23 13:20 UTC (permalink / raw)
  To: Greg Kroah-Hartman; +Cc: linux-usb, K V, Abhilash

From: "K V, Abhilash" <abhilash.k.v@intel.com>

EC firmware on Dell XPS & Latitude series does not set "Power Operation
Mode Change" bit in "Connector Status change" field of MESSAGE IN Data
while transitioning from type-C current to PD mode.

Instead the "Negotiated Power Level Change" bit is set when the "Power
Operation Mode" field shows the correct mode (i.e. PD).

This patch adds a check for this bit also, to trigger an update of
power operation mode in class driver, while handling GET_CONNECTOR_STATUS
command.

Signed-off-by: K V, Abhilash <abhilash.k.v@intel.com>
Signed-off-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
---
 drivers/usb/typec/ucsi/ucsi.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c
index e9baa9a749e5..0c7c3f9b1b50 100644
--- a/drivers/usb/typec/ucsi/ucsi.c
+++ b/drivers/usb/typec/ucsi/ucsi.c
@@ -613,7 +613,8 @@ static void ucsi_handle_connector_change(struct work_struct *work)
 
 	role = !!(con->status.flags & UCSI_CONSTAT_PWR_DIR);
 
-	if (con->status.change & UCSI_CONSTAT_POWER_OPMODE_CHANGE)
+	if (con->status.change & UCSI_CONSTAT_POWER_OPMODE_CHANGE ||
+	    con->status.change & UCSI_CONSTAT_POWER_LEVEL_CHANGE)
 		ucsi_pwr_opmode_change(con);
 
 	if (con->status.change & UCSI_CONSTAT_POWER_DIR_CHANGE) {
-- 
2.26.1


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 3/6] usb: typec: ucsi: replace magic numbers
  2020-04-23 13:20 [PATCH 0/6] ucsi driver changes for v5.8 Heikki Krogerus
  2020-04-23 13:20 ` [PATCH 1/6] usb: typec: ucsi: set USB data role when partner type is power cable/ufp Heikki Krogerus
  2020-04-23 13:20 ` [PATCH 2/6] usb: typec: ucsi: Workaround for missed op_mode change Heikki Krogerus
@ 2020-04-23 13:20 ` Heikki Krogerus
  2020-04-23 13:20 ` [PATCH 4/6] usb: typec: ucsi: Correct bit-mask for CCI Heikki Krogerus
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Heikki Krogerus @ 2020-04-23 13:20 UTC (permalink / raw)
  To: Greg Kroah-Hartman; +Cc: linux-usb, K V, Abhilash

From: "K V, Abhilash" <abhilash.k.v@intel.com>

Replace magic numbers with macros in trace.h.

Signed-off-by: K V, Abhilash <abhilash.k.v@intel.com>
Signed-off-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
---
 drivers/usb/typec/ucsi/trace.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/drivers/usb/typec/ucsi/trace.c b/drivers/usb/typec/ucsi/trace.c
index 48ad1dc1b1b2..cb62ad835761 100644
--- a/drivers/usb/typec/ucsi/trace.c
+++ b/drivers/usb/typec/ucsi/trace.c
@@ -35,16 +35,16 @@ const char *ucsi_cmd_str(u64 raw_cmd)
 
 const char *ucsi_cci_str(u32 cci)
 {
-	if (cci & GENMASK(7, 0)) {
-		if (cci & BIT(29))
+	if (UCSI_CCI_CONNECTOR(cci)) {
+		if (cci & UCSI_CCI_ACK_COMPLETE)
 			return "Event pending (ACK completed)";
-		if (cci & BIT(31))
+		if (cci & UCSI_CCI_COMMAND_COMPLETE)
 			return "Event pending (command completed)";
 		return "Connector Change";
 	}
-	if (cci & BIT(29))
+	if (cci & UCSI_CCI_ACK_COMPLETE)
 		return "ACK completed";
-	if (cci & BIT(31))
+	if (cci & UCSI_CCI_COMMAND_COMPLETE)
 		return "Command completed";
 
 	return "";
-- 
2.26.1


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 4/6] usb: typec: ucsi: Correct bit-mask for CCI
  2020-04-23 13:20 [PATCH 0/6] ucsi driver changes for v5.8 Heikki Krogerus
                   ` (2 preceding siblings ...)
  2020-04-23 13:20 ` [PATCH 3/6] usb: typec: ucsi: replace magic numbers Heikki Krogerus
@ 2020-04-23 13:20 ` Heikki Krogerus
  2020-04-23 13:20 ` [PATCH 5/6] usb: typec: ucsi: save power data objects in PD mode Heikki Krogerus
  2020-04-23 13:20 ` [PATCH 6/6] usb: typec: ucsi: register with power_supply class Heikki Krogerus
  5 siblings, 0 replies; 7+ messages in thread
From: Heikki Krogerus @ 2020-04-23 13:20 UTC (permalink / raw)
  To: Greg Kroah-Hartman; +Cc: linux-usb, K V, Abhilash

From: "K V, Abhilash" <abhilash.k.v@intel.com>

Bit 0 is reserved in CCI (Command Status & Connector Change Indicator)
register. So, change bit-mask for connector number field to 7..1
instead of 7..0.
There would be no functional change since we were anyways right-shifing
by 1 bit.

Signed-off-by: K V, Abhilash <abhilash.k.v@intel.com>
Signed-off-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
---
 drivers/usb/typec/ucsi/ucsi.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h
index 8e831108f481..f068356cc325 100644
--- a/drivers/usb/typec/ucsi/ucsi.h
+++ b/drivers/usb/typec/ucsi/ucsi.h
@@ -21,7 +21,7 @@ struct ucsi_altmode;
 #define UCSI_MESSAGE_OUT		32
 
 /* Command Status and Connector Change Indication (CCI) bits */
-#define UCSI_CCI_CONNECTOR(_c_)		(((_c_) & GENMASK(7, 0)) >> 1)
+#define UCSI_CCI_CONNECTOR(_c_)		(((_c_) & GENMASK(7, 1)) >> 1)
 #define UCSI_CCI_LENGTH(_c_)		(((_c_) & GENMASK(15, 8)) >> 8)
 #define UCSI_CCI_NOT_SUPPORTED		BIT(25)
 #define UCSI_CCI_CANCEL_COMPLETE	BIT(26)
-- 
2.26.1


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 5/6] usb: typec: ucsi: save power data objects in PD mode
  2020-04-23 13:20 [PATCH 0/6] ucsi driver changes for v5.8 Heikki Krogerus
                   ` (3 preceding siblings ...)
  2020-04-23 13:20 ` [PATCH 4/6] usb: typec: ucsi: Correct bit-mask for CCI Heikki Krogerus
@ 2020-04-23 13:20 ` Heikki Krogerus
  2020-04-23 13:20 ` [PATCH 6/6] usb: typec: ucsi: register with power_supply class Heikki Krogerus
  5 siblings, 0 replies; 7+ messages in thread
From: Heikki Krogerus @ 2020-04-23 13:20 UTC (permalink / raw)
  To: Greg Kroah-Hartman; +Cc: linux-usb, K V, Abhilash

From: "K V, Abhilash" <abhilash.k.v@intel.com>

When connected to a PD-capable power-source, read & save all partner
power data objects (PDOs) by using GET_PDOS UCSI command.
Also, save the current power contract in request data object (RDO)
for that connector.

Signed-off-by: K V, Abhilash <abhilash.k.v@intel.com>
Signed-off-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
---
 drivers/usb/typec/ucsi/ucsi.c | 26 ++++++++++++++++++++++++++
 drivers/usb/typec/ucsi/ucsi.h |  9 +++++++++
 2 files changed, 35 insertions(+)

diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c
index 0c7c3f9b1b50..ffea795da815 100644
--- a/drivers/usb/typec/ucsi/ucsi.c
+++ b/drivers/usb/typec/ucsi/ucsi.c
@@ -492,19 +492,45 @@ static void ucsi_unregister_altmodes(struct ucsi_connector *con, u8 recipient)
 	}
 }
 
+static void ucsi_get_pdos(struct ucsi_connector *con, int is_partner)
+{
+	struct ucsi *ucsi = con->ucsi;
+	u64 command;
+	int ret;
+
+	command = UCSI_COMMAND(UCSI_GET_PDOS) | UCSI_CONNECTOR_NUMBER(con->num);
+	command |= UCSI_GET_PDOS_PARTNER_PDO(is_partner);
+	command |= UCSI_GET_PDOS_NUM_PDOS(UCSI_MAX_PDOS - 1);
+	command |= UCSI_GET_PDOS_SRC_PDOS;
+	ret = ucsi_run_command(ucsi, command, con->src_pdos,
+			       sizeof(con->src_pdos));
+	if (ret < 0) {
+		dev_err(ucsi->dev, "UCSI_GET_PDOS failed (%d)\n", ret);
+		return;
+	}
+	con->num_pdos = ret / sizeof(u32); /* number of bytes to 32-bit PDOs */
+	if (ret == 0)
+		dev_warn(ucsi->dev, "UCSI_GET_PDOS returned 0 bytes\n");
+}
+
 static void ucsi_pwr_opmode_change(struct ucsi_connector *con)
 {
 	switch (UCSI_CONSTAT_PWR_OPMODE(con->status.flags)) {
 	case UCSI_CONSTAT_PWR_OPMODE_PD:
+		con->rdo = con->status.request_data_obj;
 		typec_set_pwr_opmode(con->port, TYPEC_PWR_MODE_PD);
+		ucsi_get_pdos(con, 1);
 		break;
 	case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5:
+		con->rdo = 0;
 		typec_set_pwr_opmode(con->port, TYPEC_PWR_MODE_1_5A);
 		break;
 	case UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0:
+		con->rdo = 0;
 		typec_set_pwr_opmode(con->port, TYPEC_PWR_MODE_3_0A);
 		break;
 	default:
+		con->rdo = 0;
 		typec_set_pwr_opmode(con->port, TYPEC_PWR_MODE_USB);
 		break;
 	}
diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h
index f068356cc325..28e21a1e6b61 100644
--- a/drivers/usb/typec/ucsi/ucsi.h
+++ b/drivers/usb/typec/ucsi/ucsi.h
@@ -130,6 +130,11 @@ void ucsi_connector_change(struct ucsi *ucsi, u8 num);
 #define UCSI_GET_ALTMODE_OFFSET(_r_)		((u64)(_r_) << 32)
 #define UCSI_GET_ALTMODE_NUM_ALTMODES(_r_)	((u64)(_r_) << 40)
 
+/* GET_PDOS command bits */
+#define UCSI_GET_PDOS_PARTNER_PDO(_r_)		((u64)(_r_) << 23)
+#define UCSI_GET_PDOS_NUM_PDOS(_r_)		((u64)(_r_) << 32)
+#define UCSI_GET_PDOS_SRC_PDOS			((u64)1 << 34)
+
 /* -------------------------------------------------------------------------- */
 
 /* Error information returned by PPM in response to GET_ERROR_STATUS command. */
@@ -294,6 +299,7 @@ struct ucsi {
 
 #define UCSI_MAX_SVID		5
 #define UCSI_MAX_ALTMODES	(UCSI_MAX_SVID * 6)
+#define UCSI_MAX_PDOS		(4)
 
 struct ucsi_connector {
 	int num;
@@ -313,6 +319,9 @@ struct ucsi_connector {
 
 	struct ucsi_connector_status status;
 	struct ucsi_connector_capability cap;
+	u32 rdo;
+	u32 src_pdos[UCSI_MAX_PDOS];
+	int num_pdos;
 };
 
 int ucsi_send_command(struct ucsi *ucsi, u64 command,
-- 
2.26.1


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 6/6] usb: typec: ucsi: register with power_supply class
  2020-04-23 13:20 [PATCH 0/6] ucsi driver changes for v5.8 Heikki Krogerus
                   ` (4 preceding siblings ...)
  2020-04-23 13:20 ` [PATCH 5/6] usb: typec: ucsi: save power data objects in PD mode Heikki Krogerus
@ 2020-04-23 13:20 ` Heikki Krogerus
  5 siblings, 0 replies; 7+ messages in thread
From: Heikki Krogerus @ 2020-04-23 13:20 UTC (permalink / raw)
  To: Greg Kroah-Hartman; +Cc: linux-usb, K V, Abhilash

From: "K V, Abhilash" <abhilash.k.v@intel.com>

With this change the UCSI device will show up in
/sys/class/power_supply/. The following values are exported:
- online
- usb_type
- voltage_min
- voltage_max
- voltage_now
- current_max
- current_now

Once a PD-capable type-C power source is connected to the system, GET_PDOS
UCSI command is used to query all source capabilities. Request data object
(RDO) is used to get current values.

Signed-off-by: K V, Abhilash <abhilash.k.v@intel.com>
Signed-off-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
---
 drivers/usb/typec/ucsi/Makefile |   4 +
 drivers/usb/typec/ucsi/psy.c    | 241 ++++++++++++++++++++++++++++++++
 drivers/usb/typec/ucsi/ucsi.c   |   6 +
 drivers/usb/typec/ucsi/ucsi.h   |  15 ++
 4 files changed, 266 insertions(+)
 create mode 100644 drivers/usb/typec/ucsi/psy.c

diff --git a/drivers/usb/typec/ucsi/Makefile b/drivers/usb/typec/ucsi/Makefile
index b35e15a1f02c..8a8eb5cb8e0f 100644
--- a/drivers/usb/typec/ucsi/Makefile
+++ b/drivers/usb/typec/ucsi/Makefile
@@ -7,6 +7,10 @@ typec_ucsi-y				:= ucsi.o
 
 typec_ucsi-$(CONFIG_TRACING)		+= trace.o
 
+ifneq ($(CONFIG_POWER_SUPPLY),)
+	typec_ucsi-y			+= psy.o
+endif
+
 ifneq ($(CONFIG_TYPEC_DP_ALTMODE),)
 	typec_ucsi-y			+= displayport.o
 endif
diff --git a/drivers/usb/typec/ucsi/psy.c b/drivers/usb/typec/ucsi/psy.c
new file mode 100644
index 000000000000..26ed0b520749
--- /dev/null
+++ b/drivers/usb/typec/ucsi/psy.c
@@ -0,0 +1,241 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Power Supply for UCSI
+ *
+ * Copyright (C) 2020, Intel Corporation
+ * Author: K V, Abhilash <abhilash.k.v@intel.com>
+ * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+ */
+
+#include <linux/property.h>
+#include <linux/usb/pd.h>
+
+#include "ucsi.h"
+
+/* Power Supply access to expose source power information */
+enum ucsi_psy_online_states {
+	UCSI_PSY_OFFLINE = 0,
+	UCSI_PSY_FIXED_ONLINE,
+	UCSI_PSY_PROG_ONLINE,
+};
+
+static enum power_supply_property ucsi_psy_props[] = {
+	POWER_SUPPLY_PROP_USB_TYPE,
+	POWER_SUPPLY_PROP_ONLINE,
+	POWER_SUPPLY_PROP_VOLTAGE_MIN,
+	POWER_SUPPLY_PROP_VOLTAGE_MAX,
+	POWER_SUPPLY_PROP_VOLTAGE_NOW,
+	POWER_SUPPLY_PROP_CURRENT_MAX,
+	POWER_SUPPLY_PROP_CURRENT_NOW,
+};
+
+static int ucsi_psy_get_online(struct ucsi_connector *con,
+			       union power_supply_propval *val)
+{
+	val->intval = UCSI_PSY_OFFLINE;
+	if (con->status.flags & UCSI_CONSTAT_CONNECTED &&
+	    (con->status.flags & UCSI_CONSTAT_PWR_DIR) == TYPEC_SINK)
+		val->intval = UCSI_PSY_FIXED_ONLINE;
+	return 0;
+}
+
+static int ucsi_psy_get_voltage_min(struct ucsi_connector *con,
+				    union power_supply_propval *val)
+{
+	u32 pdo;
+
+	switch (UCSI_CONSTAT_PWR_OPMODE(con->status.flags)) {
+	case UCSI_CONSTAT_PWR_OPMODE_PD:
+		pdo = con->src_pdos[0];
+		val->intval = pdo_fixed_voltage(pdo) * 1000;
+		break;
+	case UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0:
+	case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5:
+	case UCSI_CONSTAT_PWR_OPMODE_BC:
+	case UCSI_CONSTAT_PWR_OPMODE_DEFAULT:
+		val->intval = UCSI_TYPEC_VSAFE5V * 1000;
+		break;
+	default:
+		val->intval = 0;
+		break;
+	}
+	return 0;
+}
+
+static int ucsi_psy_get_voltage_max(struct ucsi_connector *con,
+				    union power_supply_propval *val)
+{
+	u32 pdo;
+
+	switch (UCSI_CONSTAT_PWR_OPMODE(con->status.flags)) {
+	case UCSI_CONSTAT_PWR_OPMODE_PD:
+		if (con->num_pdos > 0) {
+			pdo = con->src_pdos[con->num_pdos - 1];
+			val->intval = pdo_fixed_voltage(pdo) * 1000;
+		} else {
+			val->intval = 0;
+		}
+		break;
+	case UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0:
+	case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5:
+	case UCSI_CONSTAT_PWR_OPMODE_BC:
+	case UCSI_CONSTAT_PWR_OPMODE_DEFAULT:
+		val->intval = UCSI_TYPEC_VSAFE5V * 1000;
+		break;
+	default:
+		val->intval = 0;
+		break;
+	}
+	return 0;
+}
+
+static int ucsi_psy_get_voltage_now(struct ucsi_connector *con,
+				    union power_supply_propval *val)
+{
+	int index;
+	u32 pdo;
+
+	switch (UCSI_CONSTAT_PWR_OPMODE(con->status.flags)) {
+	case UCSI_CONSTAT_PWR_OPMODE_PD:
+		index = rdo_index(con->rdo);
+		if (index > 0) {
+			pdo = con->src_pdos[index - 1];
+			val->intval = pdo_fixed_voltage(pdo) * 1000;
+		} else {
+			val->intval = 0;
+		}
+		break;
+	case UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0:
+	case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5:
+	case UCSI_CONSTAT_PWR_OPMODE_BC:
+	case UCSI_CONSTAT_PWR_OPMODE_DEFAULT:
+		val->intval = UCSI_TYPEC_VSAFE5V * 1000;
+		break;
+	default:
+		val->intval = 0;
+		break;
+	}
+	return 0;
+}
+
+static int ucsi_psy_get_current_max(struct ucsi_connector *con,
+				    union power_supply_propval *val)
+{
+	u32 pdo;
+
+	switch (UCSI_CONSTAT_PWR_OPMODE(con->status.flags)) {
+	case UCSI_CONSTAT_PWR_OPMODE_PD:
+		if (con->num_pdos > 0) {
+			pdo = con->src_pdos[con->num_pdos - 1];
+			val->intval = pdo_max_current(pdo) * 1000;
+		} else {
+			val->intval = 0;
+		}
+		break;
+	case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5:
+		val->intval = UCSI_TYPEC_1_5_CURRENT * 1000;
+		break;
+	case UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0:
+		val->intval = UCSI_TYPEC_3_0_CURRENT * 1000;
+		break;
+	case UCSI_CONSTAT_PWR_OPMODE_BC:
+	case UCSI_CONSTAT_PWR_OPMODE_DEFAULT:
+	/* UCSI can't tell b/w DCP/CDP or USB2/3x1/3x2 SDP chargers */
+	default:
+		val->intval = 0;
+		break;
+	}
+	return 0;
+}
+
+static int ucsi_psy_get_current_now(struct ucsi_connector *con,
+				    union power_supply_propval *val)
+{
+	u16 flags = con->status.flags;
+
+	if (UCSI_CONSTAT_PWR_OPMODE(flags) == UCSI_CONSTAT_PWR_OPMODE_PD)
+		val->intval = rdo_op_current(con->rdo) * 1000;
+	else
+		val->intval = 0;
+	return 0;
+}
+
+static int ucsi_psy_get_usb_type(struct ucsi_connector *con,
+				 union power_supply_propval *val)
+{
+	u16 flags = con->status.flags;
+
+	val->intval = POWER_SUPPLY_USB_TYPE_C;
+	if (flags & UCSI_CONSTAT_CONNECTED &&
+	    UCSI_CONSTAT_PWR_OPMODE(flags) == UCSI_CONSTAT_PWR_OPMODE_PD)
+		val->intval = POWER_SUPPLY_USB_TYPE_PD;
+
+	return 0;
+}
+
+static int ucsi_psy_get_prop(struct power_supply *psy,
+			     enum power_supply_property psp,
+			     union power_supply_propval *val)
+{
+	struct ucsi_connector *con = power_supply_get_drvdata(psy);
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_USB_TYPE:
+		return ucsi_psy_get_usb_type(con, val);
+	case POWER_SUPPLY_PROP_ONLINE:
+		return ucsi_psy_get_online(con, val);
+	case POWER_SUPPLY_PROP_VOLTAGE_MIN:
+		return ucsi_psy_get_voltage_min(con, val);
+	case POWER_SUPPLY_PROP_VOLTAGE_MAX:
+		return ucsi_psy_get_voltage_max(con, val);
+	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+		return ucsi_psy_get_voltage_now(con, val);
+	case POWER_SUPPLY_PROP_CURRENT_MAX:
+		return ucsi_psy_get_current_max(con, val);
+	case POWER_SUPPLY_PROP_CURRENT_NOW:
+		return ucsi_psy_get_current_now(con, val);
+	default:
+		return -EINVAL;
+	}
+}
+
+static enum power_supply_usb_type ucsi_psy_usb_types[] = {
+	POWER_SUPPLY_USB_TYPE_C,
+	POWER_SUPPLY_USB_TYPE_PD,
+	POWER_SUPPLY_USB_TYPE_PD_PPS,
+};
+
+int ucsi_register_port_psy(struct ucsi_connector *con)
+{
+	struct power_supply_config psy_cfg = {};
+	struct device *dev = con->ucsi->dev;
+	char *psy_name;
+
+	psy_cfg.drv_data = con;
+	psy_cfg.fwnode = dev_fwnode(dev);
+
+	psy_name = devm_kasprintf(dev, GFP_KERNEL, "ucsi-source-psy-%s%d",
+				  dev_name(dev), con->num);
+	if (!psy_name)
+		return -ENOMEM;
+
+	con->psy_desc.name = psy_name;
+	con->psy_desc.type = POWER_SUPPLY_TYPE_USB,
+	con->psy_desc.usb_types = ucsi_psy_usb_types;
+	con->psy_desc.num_usb_types = ARRAY_SIZE(ucsi_psy_usb_types);
+	con->psy_desc.properties = ucsi_psy_props,
+	con->psy_desc.num_properties = ARRAY_SIZE(ucsi_psy_props),
+	con->psy_desc.get_property = ucsi_psy_get_prop;
+
+	con->psy = power_supply_register(dev, &con->psy_desc, &psy_cfg);
+
+	return PTR_ERR_OR_ZERO(con->psy);
+}
+
+void ucsi_unregister_port_psy(struct ucsi_connector *con)
+{
+	if (IS_ERR_OR_NULL(con->psy))
+		return;
+
+	power_supply_unregister(con->psy);
+}
diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c
index ffea795da815..d0c63afaf345 100644
--- a/drivers/usb/typec/ucsi/ucsi.c
+++ b/drivers/usb/typec/ucsi/ucsi.c
@@ -936,6 +936,10 @@ static int ucsi_register_port(struct ucsi *ucsi, int index)
 	cap->driver_data = con;
 	cap->ops = &ucsi_ops;
 
+	ret = ucsi_register_port_psy(con);
+	if (ret)
+		return ret;
+
 	/* Register the connector */
 	con->port = typec_register_port(ucsi->dev, cap);
 	if (IS_ERR(con->port))
@@ -1062,6 +1066,7 @@ int ucsi_init(struct ucsi *ucsi)
 	for (con = ucsi->connector; con->port; con++) {
 		ucsi_unregister_partner(con);
 		ucsi_unregister_altmodes(con, UCSI_RECIPIENT_CON);
+		ucsi_unregister_port_psy(con);
 		typec_unregister_port(con->port);
 		con->port = NULL;
 	}
@@ -1185,6 +1190,7 @@ void ucsi_unregister(struct ucsi *ucsi)
 		ucsi_unregister_partner(&ucsi->connector[i]);
 		ucsi_unregister_altmodes(&ucsi->connector[i],
 					 UCSI_RECIPIENT_CON);
+		ucsi_unregister_port_psy(&ucsi->connector[i]);
 		typec_unregister_port(ucsi->connector[i].port);
 	}
 
diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h
index 28e21a1e6b61..e52b5540b254 100644
--- a/drivers/usb/typec/ucsi/ucsi.h
+++ b/drivers/usb/typec/ucsi/ucsi.h
@@ -5,6 +5,7 @@
 
 #include <linux/bitops.h>
 #include <linux/device.h>
+#include <linux/power_supply.h>
 #include <linux/types.h>
 #include <linux/usb/typec.h>
 
@@ -301,6 +302,10 @@ struct ucsi {
 #define UCSI_MAX_ALTMODES	(UCSI_MAX_SVID * 6)
 #define UCSI_MAX_PDOS		(4)
 
+#define UCSI_TYPEC_VSAFE5V	5000
+#define UCSI_TYPEC_1_5_CURRENT	1500
+#define UCSI_TYPEC_3_0_CURRENT	3000
+
 struct ucsi_connector {
 	int num;
 
@@ -319,6 +324,8 @@ struct ucsi_connector {
 
 	struct ucsi_connector_status status;
 	struct ucsi_connector_capability cap;
+	struct power_supply *psy;
+	struct power_supply_desc psy_desc;
 	u32 rdo;
 	u32 src_pdos[UCSI_MAX_PDOS];
 	int num_pdos;
@@ -330,6 +337,14 @@ int ucsi_send_command(struct ucsi *ucsi, u64 command,
 void ucsi_altmode_update_active(struct ucsi_connector *con);
 int ucsi_resume(struct ucsi *ucsi);
 
+#if IS_ENABLED(CONFIG_POWER_SUPPLY)
+int ucsi_register_port_psy(struct ucsi_connector *con);
+void ucsi_unregister_port_psy(struct ucsi_connector *con);
+#else
+static inline int ucsi_register_port(struct ucsi_connector *con) { return 0; }
+static inline void ucsi_unregister_port_psy(struct ucsi_connector *con) { }
+#endif /* CONFIG_POWER_SUPPLY */
+
 #if IS_ENABLED(CONFIG_TYPEC_DP_ALTMODE)
 struct typec_altmode *
 ucsi_register_displayport(struct ucsi_connector *con,
-- 
2.26.1


^ permalink raw reply related	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2020-04-23 13:21 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2020-04-23 13:20 [PATCH 0/6] ucsi driver changes for v5.8 Heikki Krogerus
2020-04-23 13:20 ` [PATCH 1/6] usb: typec: ucsi: set USB data role when partner type is power cable/ufp Heikki Krogerus
2020-04-23 13:20 ` [PATCH 2/6] usb: typec: ucsi: Workaround for missed op_mode change Heikki Krogerus
2020-04-23 13:20 ` [PATCH 3/6] usb: typec: ucsi: replace magic numbers Heikki Krogerus
2020-04-23 13:20 ` [PATCH 4/6] usb: typec: ucsi: Correct bit-mask for CCI Heikki Krogerus
2020-04-23 13:20 ` [PATCH 5/6] usb: typec: ucsi: save power data objects in PD mode Heikki Krogerus
2020-04-23 13:20 ` [PATCH 6/6] usb: typec: ucsi: register with power_supply class Heikki Krogerus

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).