From 6e24826d2c51a0e005ecf3ed74ea48688fca0306 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 15 Apr 2020 21:24:48 +0200 Subject: usb: fusb302: Convert to use GPIO descriptors This converts the FUSB302 driver to use GPIO descriptors. The conversion to descriptors per se is pretty straight-forward. In the process I discovered that: 1. The driver uses a completely undocumented device tree binding for the interrupt GPIO line, "fcs,int_n". Ooops. 2. The undocumented binding, presumably since it has not seen review, is just "fcs,int_n", lacking the compulsory "-gpios" suffix and also something that is not a good name because the "_n" implies the line is inverted which is something we handle with flags in the device tree. Ooops. 3. Possibly the driver should not be requesting the line as a GPIO and request the corresponding interrupt line by open coding, the GPIO chip is very likely doubleing as an IRQ controller and can probably provide an interrupt directly for this line with interrupts-extended = <&gpio0 ...>; 4. Possibly the IRQ should just be tagged on the I2C client node in the device tree like apparently ACPI does, as it overrides this IRQ with client->irq if that exists. But now it is too late to do much about that and as I can see this is used like this in the Pinebook which is a shipping product so let'a just contain the mess and move on. The property currently appears in: arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts Create a quirk in the GPIO OF library to allow this property specifically to be specified without the "-gpios" suffix, we have other such bindings already. Cc: Tobias Schramm Cc: Heikki Krogerus Cc: Yueyao Zhu Cc: Guenter Roeck Cc: devicetree@vger.kernel.org Signed-off-by: Linus Walleij Link: https://lore.kernel.org/r/20200415192448.305257-1-linus.walleij@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/fusb302.c | 32 +++++++++----------------------- 1 file changed, 9 insertions(+), 23 deletions(-) (limited to 'drivers/usb/typec') diff --git a/drivers/usb/typec/tcpm/fusb302.c b/drivers/usb/typec/tcpm/fusb302.c index b498960ff72b..b28facece43c 100644 --- a/drivers/usb/typec/tcpm/fusb302.c +++ b/drivers/usb/typec/tcpm/fusb302.c @@ -9,14 +9,13 @@ #include #include #include -#include +#include #include #include #include #include #include #include -#include #include #include #include @@ -83,7 +82,7 @@ struct fusb302_chip { struct work_struct irq_work; bool irq_suspended; bool irq_while_suspended; - int gpio_int_n; + struct gpio_desc *gpio_int_n; int gpio_int_n_irq; struct extcon_dev *extcon; @@ -1618,30 +1617,17 @@ done: static int init_gpio(struct fusb302_chip *chip) { - struct device_node *node; + struct device *dev = chip->dev; int ret = 0; - node = chip->dev->of_node; - chip->gpio_int_n = of_get_named_gpio(node, "fcs,int_n", 0); - if (!gpio_is_valid(chip->gpio_int_n)) { - ret = chip->gpio_int_n; - dev_err(chip->dev, "cannot get named GPIO Int_N, ret=%d", ret); - return ret; - } - ret = devm_gpio_request(chip->dev, chip->gpio_int_n, "fcs,int_n"); - if (ret < 0) { - dev_err(chip->dev, "cannot request GPIO Int_N, ret=%d", ret); - return ret; - } - ret = gpio_direction_input(chip->gpio_int_n); - if (ret < 0) { - dev_err(chip->dev, - "cannot set GPIO Int_N to input, ret=%d", ret); - return ret; + chip->gpio_int_n = devm_gpiod_get(dev, "fcs,int_n", GPIOD_IN); + if (IS_ERR(chip->gpio_int_n)) { + dev_err(dev, "failed to request gpio_int_n\n"); + return PTR_ERR(chip->gpio_int_n); } - ret = gpio_to_irq(chip->gpio_int_n); + ret = gpiod_to_irq(chip->gpio_int_n); if (ret < 0) { - dev_err(chip->dev, + dev_err(dev, "cannot request IRQ for GPIO Int_N, ret=%d", ret); return ret; } -- cgit v1.2.3 From b99bb85a31a6bfacfbf281ab809cd9109762df52 Mon Sep 17 00:00:00 2001 From: Nishad Kamdar Date: Sun, 19 Apr 2020 19:00:55 +0530 Subject: USB: typec: Use the correct style for SPDX License Identifier This patch corrects the SPDX License Identifier style in header file related to USB Type-C support. For C header files Documentation/process/license-rules.rst mandates C-like comments (opposed to C source files where C++ style should be used). Changes made by using a script provided by Joe Perches here: https://lkml.org/lkml/2019/2/7/46. Suggested-by: Joe Perches Signed-off-by: Nishad Kamdar Reviewed-by Heikki Krogerus Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20200419133051.GA7154@nishad Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/fusb302_reg.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb/typec') diff --git a/drivers/usb/typec/tcpm/fusb302_reg.h b/drivers/usb/typec/tcpm/fusb302_reg.h index 00b39d365478..edc0e4b0f1e6 100644 --- a/drivers/usb/typec/tcpm/fusb302_reg.h +++ b/drivers/usb/typec/tcpm/fusb302_reg.h @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0+ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * Copyright 2016-2017 Google, Inc * -- cgit v1.2.3 From 91813ef8da12fad01ee4aadd173504feefe60293 Mon Sep 17 00:00:00 2001 From: Mayank Rana Date: Thu, 23 Apr 2020 16:20:53 +0300 Subject: usb: typec: ucsi: set USB data role when partner type is power cable/ufp 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 Signed-off-by: Heikki Krogerus Link: https://lore.kernel.org/r/20200423132058.6972-2-heikki.krogerus@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/ucsi.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/usb/typec') 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: -- cgit v1.2.3 From a0d4618788f26bd6f31f80a00f922f1c4bc66457 Mon Sep 17 00:00:00 2001 From: "K V, Abhilash" Date: Thu, 23 Apr 2020 16:20:54 +0300 Subject: usb: typec: ucsi: Workaround for missed op_mode change 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 Signed-off-by: Heikki Krogerus Link: https://lore.kernel.org/r/20200423132058.6972-3-heikki.krogerus@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/ucsi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/usb/typec') 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) { -- cgit v1.2.3 From 0db592b1a3b7b513e16920a9cf8545e6732189c9 Mon Sep 17 00:00:00 2001 From: "K V, Abhilash" Date: Thu, 23 Apr 2020 16:20:55 +0300 Subject: usb: typec: ucsi: replace magic numbers Replace magic numbers with macros in trace.h. Signed-off-by: K V, Abhilash Signed-off-by: Heikki Krogerus Link: https://lore.kernel.org/r/20200423132058.6972-4-heikki.krogerus@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/trace.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/usb/typec') 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 ""; -- cgit v1.2.3 From e2f38ff2f41fb88e3b5e81bbf51aeabe4a0d1188 Mon Sep 17 00:00:00 2001 From: "K V, Abhilash" Date: Thu, 23 Apr 2020 16:20:56 +0300 Subject: usb: typec: ucsi: Correct bit-mask for CCI 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 Signed-off-by: Heikki Krogerus Link: https://lore.kernel.org/r/20200423132058.6972-5-heikki.krogerus@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/ucsi.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb/typec') 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) -- cgit v1.2.3 From 4dbc6a4ef06d6a79ff91be6fc2e90f8660031ce0 Mon Sep 17 00:00:00 2001 From: "K V, Abhilash" Date: Thu, 23 Apr 2020 16:20:57 +0300 Subject: usb: typec: ucsi: save power data objects in PD mode 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 Signed-off-by: Heikki Krogerus Link: https://lore.kernel.org/r/20200423132058.6972-6-heikki.krogerus@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/ucsi.c | 26 ++++++++++++++++++++++++++ drivers/usb/typec/ucsi/ucsi.h | 9 +++++++++ 2 files changed, 35 insertions(+) (limited to 'drivers/usb/typec') 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, -- cgit v1.2.3 From 992a60ed0d5e312ce9a485c9e12097ac82ae4b3e Mon Sep 17 00:00:00 2001 From: "K V, Abhilash" Date: Thu, 23 Apr 2020 16:20:58 +0300 Subject: usb: typec: ucsi: register with power_supply class 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 Signed-off-by: Heikki Krogerus Link: https://lore.kernel.org/r/20200423132058.6972-7-heikki.krogerus@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- 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 (limited to 'drivers/usb/typec') 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 + * Author: Heikki Krogerus + */ + +#include +#include + +#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 @@ err_unregister: 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 #include +#include #include #include @@ -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, -- cgit v1.2.3 From 1417cff96ef8ae2108df7084f035b90d2eabbba3 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 27 Apr 2020 14:12:46 +0300 Subject: usb: typec: ucsi: Fix the stub for ucsi_register_port_psy() The stub was ucsi_register_port() when it should have been ucsi_register_port_psy(). Cc: Abhilash K V Fixes: 992a60ed0d5e ("usb: typec: ucsi: register with power_supply class") Reported-by: kbuild test robot Signed-off-by: Heikki Krogerus Link: https://lore.kernel.org/r/20200427111246.4889-1-heikki.krogerus@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/ucsi.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb/typec') diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h index e52b5540b254..cba6f77bea61 100644 --- a/drivers/usb/typec/ucsi/ucsi.h +++ b/drivers/usb/typec/ucsi/ucsi.h @@ -341,7 +341,7 @@ int ucsi_resume(struct ucsi *ucsi); 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 int ucsi_register_port_psy(struct ucsi_connector *con) { return 0; } static inline void ucsi_unregister_port_psy(struct ucsi_connector *con) { } #endif /* CONFIG_POWER_SUPPLY */ -- cgit v1.2.3 From 8c49c9ee4a91c158d28c583862718b348881cb16 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Thu, 7 May 2020 18:08:57 +0300 Subject: usb: typec: Add typec_find_orientation() Function that converts orientation string into orientation value. Signed-off-by: Heikki Krogerus Link: https://lore.kernel.org/r/20200507150900.12102-2-heikki.krogerus@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/class.c | 36 ++++++++++++++++++++++++------------ include/linux/usb/typec.h | 1 + 2 files changed, 25 insertions(+), 12 deletions(-) (limited to 'drivers/usb/typec') diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index 8d894bdff77d..c9234748537a 100644 --- a/drivers/usb/typec/class.c +++ b/drivers/usb/typec/class.c @@ -917,6 +917,12 @@ EXPORT_SYMBOL_GPL(typec_unregister_cable); /* ------------------------------------------------------------------------- */ /* USB Type-C ports */ +static const char * const typec_orientations[] = { + [TYPEC_ORIENTATION_NONE] = "unknown", + [TYPEC_ORIENTATION_NORMAL] = "normal", + [TYPEC_ORIENTATION_REVERSE] = "reverse", +}; + static const char * const typec_roles[] = { [TYPEC_SINK] = "sink", [TYPEC_SOURCE] = "source", @@ -1248,18 +1254,9 @@ static ssize_t orientation_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct typec_port *p = to_typec_port(dev); - enum typec_orientation orientation = typec_get_orientation(p); - - switch (orientation) { - case TYPEC_ORIENTATION_NORMAL: - return sprintf(buf, "%s\n", "normal"); - case TYPEC_ORIENTATION_REVERSE: - return sprintf(buf, "%s\n", "reverse"); - case TYPEC_ORIENTATION_NONE: - default: - return sprintf(buf, "%s\n", "unknown"); - } + struct typec_port *port = to_typec_port(dev); + + return sprintf(buf, "%s\n", typec_orientations[port->orientation]); } static DEVICE_ATTR_RO(orientation); @@ -1451,6 +1448,21 @@ void typec_set_pwr_opmode(struct typec_port *port, } EXPORT_SYMBOL_GPL(typec_set_pwr_opmode); +/** + * typec_find_orientation - Convert orientation string to enum typec_orientation + * @name: Orientation string + * + * This routine is used to find the typec_orientation by its string name @name. + * + * Returns the orientation value on success, otherwise negative error code. + */ +int typec_find_orientation(const char *name) +{ + return match_string(typec_orientations, ARRAY_SIZE(typec_orientations), + name); +} +EXPORT_SYMBOL_GPL(typec_find_orientation); + /** * typec_find_port_power_role - Get the typec port power capability * @name: port power capability string diff --git a/include/linux/usb/typec.h b/include/linux/usb/typec.h index b00a2642a9cd..5daa1c49761c 100644 --- a/include/linux/usb/typec.h +++ b/include/linux/usb/typec.h @@ -254,6 +254,7 @@ int typec_set_mode(struct typec_port *port, int mode); void *typec_get_drvdata(struct typec_port *port); +int typec_find_orientation(const char *name); int typec_find_port_power_role(const char *name); int typec_find_power_role(const char *name); int typec_find_port_data_role(const char *name); -- cgit v1.2.3 From ff4a30d5e24307b416d3eac092c81b1f12a7a599 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Thu, 7 May 2020 18:08:58 +0300 Subject: usb: typec: mux: intel_pmc_mux: Support for static SBU/HSL orientation The SBU and HSL orientation may be fixed/static from the mux PoW. Apparently the retimer may take care of the orientation of these lines. Handling the static SBU (AUX) and HSL orientation with device properties. If the SBU orientation is static, a device property "sbu-orintation" can be used. When the property exists, the driver always sets the SBU orientation according to the property value, and when it's not set, the driver uses the cable plug orientation with SBU. And with static HSL orientation, "hsl-orientation" device property can be used in the same way. Signed-off-by: Heikki Krogerus Link: https://lore.kernel.org/r/20200507150900.12102-3-heikki.krogerus@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/mux/intel_pmc_mux.c | 42 ++++++++++++++++++++++++++++++----- 1 file changed, 36 insertions(+), 6 deletions(-) (limited to 'drivers/usb/typec') diff --git a/drivers/usb/typec/mux/intel_pmc_mux.c b/drivers/usb/typec/mux/intel_pmc_mux.c index 67c5139cfa0d..724efd053838 100644 --- a/drivers/usb/typec/mux/intel_pmc_mux.c +++ b/drivers/usb/typec/mux/intel_pmc_mux.c @@ -91,6 +91,9 @@ struct pmc_usb_port { u8 usb2_port; u8 usb3_port; + + enum typec_orientation sbu_orientation; + enum typec_orientation hsl_orientation; }; struct pmc_usb { @@ -99,6 +102,22 @@ struct pmc_usb { struct pmc_usb_port *port; }; +static int sbu_orientation(struct pmc_usb_port *port) +{ + if (port->sbu_orientation) + return port->sbu_orientation - 1; + + return port->orientation - 1; +} + +static int hsl_orientation(struct pmc_usb_port *port) +{ + if (port->hsl_orientation) + return port->hsl_orientation - 1; + + return port->orientation - 1; +} + static int pmc_usb_command(struct pmc_usb_port *port, u8 *msg, u32 len) { u8 response[4]; @@ -151,8 +170,9 @@ pmc_usb_mux_dp(struct pmc_usb_port *port, struct typec_mux_state *state) req.mode_data = (port->orientation - 1) << PMC_USB_ALTMODE_ORI_SHIFT; req.mode_data |= (port->role - 1) << PMC_USB_ALTMODE_UFP_SHIFT; - req.mode_data |= (port->orientation - 1) << PMC_USB_ALTMODE_ORI_AUX_SHIFT; - req.mode_data |= (port->orientation - 1) << PMC_USB_ALTMODE_ORI_HSL_SHIFT; + + req.mode_data |= sbu_orientation(port) << PMC_USB_ALTMODE_ORI_AUX_SHIFT; + req.mode_data |= hsl_orientation(port) << PMC_USB_ALTMODE_ORI_HSL_SHIFT; req.mode_data |= (state->mode - TYPEC_STATE_MODAL) << PMC_USB_ALTMODE_DP_MODE_SHIFT; @@ -177,8 +197,9 @@ pmc_usb_mux_tbt(struct pmc_usb_port *port, struct typec_mux_state *state) req.mode_data = (port->orientation - 1) << PMC_USB_ALTMODE_ORI_SHIFT; req.mode_data |= (port->role - 1) << PMC_USB_ALTMODE_UFP_SHIFT; - req.mode_data |= (port->orientation - 1) << PMC_USB_ALTMODE_ORI_AUX_SHIFT; - req.mode_data |= (port->orientation - 1) << PMC_USB_ALTMODE_ORI_HSL_SHIFT; + + req.mode_data |= sbu_orientation(port) << PMC_USB_ALTMODE_ORI_AUX_SHIFT; + req.mode_data |= hsl_orientation(port) << PMC_USB_ALTMODE_ORI_HSL_SHIFT; if (TBT_ADAPTER(data->device_mode) == TBT_ADAPTER_TBT3) req.mode_data |= PMC_USB_ALTMODE_TBT_TYPE; @@ -215,8 +236,8 @@ static int pmc_usb_connect(struct pmc_usb_port *port) msg[0] |= port->usb3_port << PMC_USB_MSG_USB3_PORT_SHIFT; msg[1] = port->usb2_port << PMC_USB_MSG_USB2_PORT_SHIFT; - msg[1] |= (port->orientation - 1) << PMC_USB_MSG_ORI_HSL_SHIFT; - msg[1] |= (port->orientation - 1) << PMC_USB_MSG_ORI_AUX_SHIFT; + msg[1] |= hsl_orientation(port) << PMC_USB_MSG_ORI_HSL_SHIFT; + msg[1] |= sbu_orientation(port) << PMC_USB_MSG_ORI_AUX_SHIFT; return pmc_usb_command(port, msg, sizeof(msg)); } @@ -300,6 +321,7 @@ static int pmc_usb_register_port(struct pmc_usb *pmc, int index, struct usb_role_switch_desc desc = { }; struct typec_switch_desc sw_desc = { }; struct typec_mux_desc mux_desc = { }; + const char *str; int ret; ret = fwnode_property_read_u8(fwnode, "usb2-port-number", &port->usb2_port); @@ -310,6 +332,14 @@ static int pmc_usb_register_port(struct pmc_usb *pmc, int index, if (ret) return ret; + ret = fwnode_property_read_string(fwnode, "sbu-orientation", &str); + if (!ret) + port->sbu_orientation = typec_find_orientation(str); + + ret = fwnode_property_read_string(fwnode, "hsl-orientation", &str); + if (!ret) + port->hsl_orientation = typec_find_orientation(str); + port->num = index; port->pmc = pmc; -- cgit v1.2.3 From 0ef1f6e3808b15f3bcede4976235a7d81cc7f254 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Thu, 7 May 2020 22:47:33 +0100 Subject: usb: typec: tps6598x: Add OF probe binding Adds a MODULE_DEVICE_TABLE() to allow probing of this driver from a DTS setting. Cc: Heikki Krogerus Cc: Greg Kroah-Hartman Cc: Nikolaus Voss Cc: Andy Shevchenko Cc: Gustavo A. R. Silva Cc: Kees Cook Cc: linux-usb@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Bryan O'Donoghue Tested-by: Martin Kepplinger Reviewed-by: Heikki Krogerus Link: https://lore.kernel.org/r/20200507214733.1982696-3-bryan.odonoghue@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tps6598x.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers/usb/typec') diff --git a/drivers/usb/typec/tps6598x.c b/drivers/usb/typec/tps6598x.c index 0698addd1185..defa651282b0 100644 --- a/drivers/usb/typec/tps6598x.c +++ b/drivers/usb/typec/tps6598x.c @@ -563,6 +563,12 @@ static int tps6598x_remove(struct i2c_client *client) return 0; } +static const struct of_device_id tps6598x_of_match[] = { + { .compatible = "ti,tps6598x", }, + {} +}; +MODULE_DEVICE_TABLE(of, tps6598x_of_match); + static const struct i2c_device_id tps6598x_id[] = { { "tps6598x" }, { } @@ -572,6 +578,7 @@ MODULE_DEVICE_TABLE(i2c, tps6598x_id); static struct i2c_driver tps6598x_i2c_driver = { .driver = { .name = "tps6598x", + .of_match_table = tps6598x_of_match, }, .probe_new = tps6598x_probe, .remove = tps6598x_remove, -- cgit v1.2.3 From 18a6c866bb191f360a16db6a79e005247dd3fd70 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Tue, 12 May 2020 00:19:30 +0100 Subject: usb: typec: tps6598x: Add USB role switching logic This patch adds USB role switch support to the tps6598x. The setup to initiate or accept a data-role switch is both assumed and currently required to be baked-into the firmware as described in TI's document here. Link: https://www.ti.com/lit/an/slva843a/slva843a.pdf With this change its possible to use the USB role-switch API to detect and notify role-switches to downstream consumers. Tested with a ChipIdea controller on a Qualcomm MSM8939. Cc: Heikki Krogerus Cc: Greg Kroah-Hartman Cc: Nikolaus Voss Cc: Andy Shevchenko Cc: Gustavo A. R. Silva Cc: Kees Cook Cc: linux-usb@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Bryan O'Donoghue Link: https://lore.kernel.org/r/20200511231930.2825183-2-bryan.odonoghue@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tps6598x.c | 57 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 50 insertions(+), 7 deletions(-) (limited to 'drivers/usb/typec') diff --git a/drivers/usb/typec/tps6598x.c b/drivers/usb/typec/tps6598x.c index defa651282b0..b7c9fe5caabe 100644 --- a/drivers/usb/typec/tps6598x.c +++ b/drivers/usb/typec/tps6598x.c @@ -12,6 +12,7 @@ #include #include #include +#include /* Register offsets */ #define TPS_REG_VID 0x00 @@ -94,6 +95,7 @@ struct tps6598x { struct typec_port *port; struct typec_partner *partner; struct usb_pd_identity partner_identity; + struct usb_role_switch *role_sw; }; /* @@ -190,6 +192,23 @@ static int tps6598x_read_partner_identity(struct tps6598x *tps) return 0; } +static void tps6598x_set_data_role(struct tps6598x *tps, + enum typec_data_role role, bool connected) +{ + enum usb_role role_val; + + if (role == TYPEC_HOST) + role_val = USB_ROLE_HOST; + else + role_val = USB_ROLE_DEVICE; + + if (!connected) + role_val = USB_ROLE_NONE; + + usb_role_switch_set_role(tps->role_sw, role_val); + typec_set_data_role(tps->port, role); +} + static int tps6598x_connect(struct tps6598x *tps, u32 status) { struct typec_partner_desc desc; @@ -220,7 +239,7 @@ static int tps6598x_connect(struct tps6598x *tps, u32 status) typec_set_pwr_opmode(tps->port, mode); typec_set_pwr_role(tps->port, TPS_STATUS_PORTROLE(status)); typec_set_vconn_role(tps->port, TPS_STATUS_VCONN(status)); - typec_set_data_role(tps->port, TPS_STATUS_DATAROLE(status)); + tps6598x_set_data_role(tps, TPS_STATUS_DATAROLE(status), true); tps->partner = typec_register_partner(tps->port, &desc); if (IS_ERR(tps->partner)) @@ -240,7 +259,7 @@ static void tps6598x_disconnect(struct tps6598x *tps, u32 status) typec_set_pwr_opmode(tps->port, TYPEC_PWR_MODE_USB); typec_set_pwr_role(tps->port, TPS_STATUS_PORTROLE(status)); typec_set_vconn_role(tps->port, TPS_STATUS_VCONN(status)); - typec_set_data_role(tps->port, TPS_STATUS_DATAROLE(status)); + tps6598x_set_data_role(tps, TPS_STATUS_DATAROLE(status), false); } static int tps6598x_exec_cmd(struct tps6598x *tps, const char *cmd, @@ -328,7 +347,7 @@ static int tps6598x_dr_set(struct typec_port *port, enum typec_data_role role) goto out_unlock; } - typec_set_data_role(tps->port, role); + tps6598x_set_data_role(tps, role, true); out_unlock: mutex_unlock(&tps->lock); @@ -452,6 +471,7 @@ static int tps6598x_probe(struct i2c_client *client) { struct typec_capability typec_cap = { }; struct tps6598x *tps; + struct fwnode_handle *fwnode; u32 status; u32 conf; u32 vid; @@ -495,11 +515,22 @@ static int tps6598x_probe(struct i2c_client *client) if (ret < 0) return ret; + fwnode = device_get_named_child_node(&client->dev, "connector"); + if (IS_ERR(fwnode)) + return PTR_ERR(fwnode); + + tps->role_sw = fwnode_usb_role_switch_get(fwnode); + if (IS_ERR(tps->role_sw)) { + ret = PTR_ERR(tps->role_sw); + goto err_fwnode_put; + } + typec_cap.revision = USB_TYPEC_REV_1_2; typec_cap.pd_revision = 0x200; typec_cap.prefer_role = TYPEC_NO_PREFERRED_ROLE; typec_cap.driver_data = tps; typec_cap.ops = &tps6598x_ops; + typec_cap.fwnode = fwnode; switch (TPS_SYSCONF_PORTINFO(conf)) { case TPS_PORTINFO_SINK_ACCESSORY: @@ -525,12 +556,16 @@ static int tps6598x_probe(struct i2c_client *client) typec_cap.data = TYPEC_PORT_DFP; break; default: - return -ENODEV; + ret = -ENODEV; + goto err_role_put; } tps->port = typec_register_port(&client->dev, &typec_cap); - if (IS_ERR(tps->port)) - return PTR_ERR(tps->port); + if (IS_ERR(tps->port)) { + ret = PTR_ERR(tps->port); + goto err_role_put; + } + fwnode_handle_put(fwnode); if (status & TPS_STATUS_PLUG_PRESENT) { ret = tps6598x_connect(tps, status); @@ -545,12 +580,19 @@ static int tps6598x_probe(struct i2c_client *client) if (ret) { tps6598x_disconnect(tps, 0); typec_unregister_port(tps->port); - return ret; + goto err_role_put; } i2c_set_clientdata(client, tps); return 0; + +err_role_put: + usb_role_switch_put(tps->role_sw); +err_fwnode_put: + fwnode_handle_put(fwnode); + + return ret; } static int tps6598x_remove(struct i2c_client *client) @@ -559,6 +601,7 @@ static int tps6598x_remove(struct i2c_client *client) tps6598x_disconnect(tps, 0); typec_unregister_port(tps->port); + usb_role_switch_put(tps->role_sw); return 0; } -- cgit v1.2.3 From e9ccc35b86653cb15e9bffbe2cbef8781ea2c1dd Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Wed, 20 May 2020 16:36:17 +0100 Subject: usb: typec: Ensure USB_ROLE_SWITCH is set as a dependency for tps6598x When I switched on USB role switching for the tps6598x I completely forgot to add the Kconfig dependency. Ensure USB_ROLE_SWITCH is selected to prevent the typs6598x driver being compiled in but the role-switch driver being compiled as a module, leading to link error. Suggested-by: Heikki Krogerus Signed-off-by: Bryan O'Donoghue Acked-by: Heikki Krogerus Link: https://lore.kernel.org/r/20200520153617.610909-1-bryan.odonoghue@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/usb/typec') diff --git a/drivers/usb/typec/Kconfig b/drivers/usb/typec/Kconfig index b4f2aac7ae8a..559dd06117e7 100644 --- a/drivers/usb/typec/Kconfig +++ b/drivers/usb/typec/Kconfig @@ -64,7 +64,8 @@ config TYPEC_HD3SS3220 config TYPEC_TPS6598X tristate "TI TPS6598x USB Power Delivery controller driver" depends on I2C - select REGMAP_I2C + depends on REGMAP_I2C + depends on USB_ROLE_SWITCH || !USB_ROLE_SWITCH help Say Y or M here if your system has TI TPS65982 or TPS65983 USB Power Delivery controller. -- cgit v1.2.3