Commit da4c428a authored by Hans de Goede's avatar Hans de Goede Committed by Greg Kroah-Hartman

usb: typec: tcpm: Notify the tcpc to start connection-detection for SRPs

[ Upstream commit 7893f9e1 ]

Some tcpc device-drivers need to explicitly be told to watch for connection
events, otherwise the tcpc will not generate any TCPM_CC_EVENTs and devices
being plugged into the Type-C port will not be noticed.

For dual-role ports tcpm_start_drp_toggling() is used to tell the tcpc to
watch for connection events. Sofar we lack a similar callback to the tcpc
for single-role ports. With some tcpc-s such as the fusb302 this means
no TCPM_CC_EVENTs will be generated when the port is configured as a
single-role port.

This commit renames start_drp_toggling to start_toggling and since the
device-properties are parsed by the tcpm-core, adds a port_type parameter
to the start_toggling callback so that the tcpc_dev driver knows the
port-type and can act accordingly when it starts toggling.

The new start_toggling callback now always gets called if defined, instead
of only being called for DRP ports.

To avoid this causing undesirable functional changes all existing
start_drp_toggling implementations are not only renamed to start_toggling,
but also get a port_type check added and return -EOPNOTSUPP when port_type
is not DRP.

Fixes: ea3b4d55("usb: typec: fusb302: Resolve fixed power role ...")
Cc: Adam Thomson <Adam.Thomson.Opensource@diasemi.com>
Signed-off-by: default avatarHans de Goede <hdegoede@redhat.com>
Reviewed-by: default avatarGuenter Roeck <linux@roeck-us.net>
Acked-by: default avatarHeikki Krogerus <heikki.krogerus@linux.intel.com>
Tested-by: default avatarAdam Thomson <Adam.Thomson.Opensource@diasemi.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: default avatarSasha Levin <sashal@kernel.org>
parent f4e55515
...@@ -990,13 +990,17 @@ static int tcpm_set_roles(struct tcpc_dev *dev, bool attached, ...@@ -990,13 +990,17 @@ static int tcpm_set_roles(struct tcpc_dev *dev, bool attached,
return ret; return ret;
} }
static int tcpm_start_drp_toggling(struct tcpc_dev *dev, static int tcpm_start_toggling(struct tcpc_dev *dev,
enum typec_cc_status cc) enum typec_port_type port_type,
enum typec_cc_status cc)
{ {
struct fusb302_chip *chip = container_of(dev, struct fusb302_chip, struct fusb302_chip *chip = container_of(dev, struct fusb302_chip,
tcpc_dev); tcpc_dev);
int ret = 0; int ret = 0;
if (port_type != TYPEC_PORT_DRP)
return -EOPNOTSUPP;
mutex_lock(&chip->lock); mutex_lock(&chip->lock);
ret = fusb302_set_src_current(chip, cc_src_current[cc]); ret = fusb302_set_src_current(chip, cc_src_current[cc]);
if (ret < 0) { if (ret < 0) {
...@@ -1206,7 +1210,7 @@ static void init_tcpc_dev(struct tcpc_dev *fusb302_tcpc_dev) ...@@ -1206,7 +1210,7 @@ static void init_tcpc_dev(struct tcpc_dev *fusb302_tcpc_dev)
fusb302_tcpc_dev->set_vbus = tcpm_set_vbus; fusb302_tcpc_dev->set_vbus = tcpm_set_vbus;
fusb302_tcpc_dev->set_pd_rx = tcpm_set_pd_rx; fusb302_tcpc_dev->set_pd_rx = tcpm_set_pd_rx;
fusb302_tcpc_dev->set_roles = tcpm_set_roles; fusb302_tcpc_dev->set_roles = tcpm_set_roles;
fusb302_tcpc_dev->start_drp_toggling = tcpm_start_drp_toggling; fusb302_tcpc_dev->start_toggling = tcpm_start_toggling;
fusb302_tcpc_dev->pd_transmit = tcpm_pd_transmit; fusb302_tcpc_dev->pd_transmit = tcpm_pd_transmit;
} }
......
...@@ -100,13 +100,17 @@ static int tcpci_set_cc(struct tcpc_dev *tcpc, enum typec_cc_status cc) ...@@ -100,13 +100,17 @@ static int tcpci_set_cc(struct tcpc_dev *tcpc, enum typec_cc_status cc)
return 0; return 0;
} }
static int tcpci_start_drp_toggling(struct tcpc_dev *tcpc, static int tcpci_start_toggling(struct tcpc_dev *tcpc,
enum typec_cc_status cc) enum typec_port_type port_type,
enum typec_cc_status cc)
{ {
int ret; int ret;
struct tcpci *tcpci = tcpc_to_tcpci(tcpc); struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
unsigned int reg = TCPC_ROLE_CTRL_DRP; unsigned int reg = TCPC_ROLE_CTRL_DRP;
if (port_type != TYPEC_PORT_DRP)
return -EOPNOTSUPP;
/* Handle vendor drp toggling */ /* Handle vendor drp toggling */
if (tcpci->data->start_drp_toggling) { if (tcpci->data->start_drp_toggling) {
ret = tcpci->data->start_drp_toggling(tcpci, tcpci->data, cc); ret = tcpci->data->start_drp_toggling(tcpci, tcpci->data, cc);
...@@ -511,7 +515,7 @@ struct tcpci *tcpci_register_port(struct device *dev, struct tcpci_data *data) ...@@ -511,7 +515,7 @@ struct tcpci *tcpci_register_port(struct device *dev, struct tcpci_data *data)
tcpci->tcpc.get_cc = tcpci_get_cc; tcpci->tcpc.get_cc = tcpci_get_cc;
tcpci->tcpc.set_polarity = tcpci_set_polarity; tcpci->tcpc.set_polarity = tcpci_set_polarity;
tcpci->tcpc.set_vconn = tcpci_set_vconn; tcpci->tcpc.set_vconn = tcpci_set_vconn;
tcpci->tcpc.start_drp_toggling = tcpci_start_drp_toggling; tcpci->tcpc.start_toggling = tcpci_start_toggling;
tcpci->tcpc.set_pd_rx = tcpci_set_pd_rx; tcpci->tcpc.set_pd_rx = tcpci_set_pd_rx;
tcpci->tcpc.set_roles = tcpci_set_roles; tcpci->tcpc.set_roles = tcpci_set_roles;
......
...@@ -31,7 +31,7 @@ ...@@ -31,7 +31,7 @@
#define FOREACH_STATE(S) \ #define FOREACH_STATE(S) \
S(INVALID_STATE), \ S(INVALID_STATE), \
S(DRP_TOGGLING), \ S(TOGGLING), \
S(SRC_UNATTACHED), \ S(SRC_UNATTACHED), \
S(SRC_ATTACH_WAIT), \ S(SRC_ATTACH_WAIT), \
S(SRC_ATTACHED), \ S(SRC_ATTACHED), \
...@@ -473,7 +473,7 @@ static void tcpm_log(struct tcpm_port *port, const char *fmt, ...) ...@@ -473,7 +473,7 @@ static void tcpm_log(struct tcpm_port *port, const char *fmt, ...)
/* Do not log while disconnected and unattached */ /* Do not log while disconnected and unattached */
if (tcpm_port_is_disconnected(port) && if (tcpm_port_is_disconnected(port) &&
(port->state == SRC_UNATTACHED || port->state == SNK_UNATTACHED || (port->state == SRC_UNATTACHED || port->state == SNK_UNATTACHED ||
port->state == DRP_TOGGLING)) port->state == TOGGLING))
return; return;
va_start(args, fmt); va_start(args, fmt);
...@@ -2561,20 +2561,16 @@ static int tcpm_set_charge(struct tcpm_port *port, bool charge) ...@@ -2561,20 +2561,16 @@ static int tcpm_set_charge(struct tcpm_port *port, bool charge)
return 0; return 0;
} }
static bool tcpm_start_drp_toggling(struct tcpm_port *port, static bool tcpm_start_toggling(struct tcpm_port *port, enum typec_cc_status cc)
enum typec_cc_status cc)
{ {
int ret; int ret;
if (port->tcpc->start_drp_toggling && if (!port->tcpc->start_toggling)
port->port_type == TYPEC_PORT_DRP) { return false;
tcpm_log_force(port, "Start DRP toggling");
ret = port->tcpc->start_drp_toggling(port->tcpc, cc);
if (!ret)
return true;
}
return false; tcpm_log_force(port, "Start toggling");
ret = port->tcpc->start_toggling(port->tcpc, port->port_type, cc);
return ret == 0;
} }
static void tcpm_set_cc(struct tcpm_port *port, enum typec_cc_status cc) static void tcpm_set_cc(struct tcpm_port *port, enum typec_cc_status cc)
...@@ -2868,15 +2864,15 @@ static void run_state_machine(struct tcpm_port *port) ...@@ -2868,15 +2864,15 @@ static void run_state_machine(struct tcpm_port *port)
port->enter_state = port->state; port->enter_state = port->state;
switch (port->state) { switch (port->state) {
case DRP_TOGGLING: case TOGGLING:
break; break;
/* SRC states */ /* SRC states */
case SRC_UNATTACHED: case SRC_UNATTACHED:
if (!port->non_pd_role_swap) if (!port->non_pd_role_swap)
tcpm_swap_complete(port, -ENOTCONN); tcpm_swap_complete(port, -ENOTCONN);
tcpm_src_detach(port); tcpm_src_detach(port);
if (tcpm_start_drp_toggling(port, tcpm_rp_cc(port))) { if (tcpm_start_toggling(port, tcpm_rp_cc(port))) {
tcpm_set_state(port, DRP_TOGGLING, 0); tcpm_set_state(port, TOGGLING, 0);
break; break;
} }
tcpm_set_cc(port, tcpm_rp_cc(port)); tcpm_set_cc(port, tcpm_rp_cc(port));
...@@ -3074,8 +3070,8 @@ static void run_state_machine(struct tcpm_port *port) ...@@ -3074,8 +3070,8 @@ static void run_state_machine(struct tcpm_port *port)
tcpm_swap_complete(port, -ENOTCONN); tcpm_swap_complete(port, -ENOTCONN);
tcpm_pps_complete(port, -ENOTCONN); tcpm_pps_complete(port, -ENOTCONN);
tcpm_snk_detach(port); tcpm_snk_detach(port);
if (tcpm_start_drp_toggling(port, TYPEC_CC_RD)) { if (tcpm_start_toggling(port, TYPEC_CC_RD)) {
tcpm_set_state(port, DRP_TOGGLING, 0); tcpm_set_state(port, TOGGLING, 0);
break; break;
} }
tcpm_set_cc(port, TYPEC_CC_RD); tcpm_set_cc(port, TYPEC_CC_RD);
...@@ -3642,7 +3638,7 @@ static void _tcpm_cc_change(struct tcpm_port *port, enum typec_cc_status cc1, ...@@ -3642,7 +3638,7 @@ static void _tcpm_cc_change(struct tcpm_port *port, enum typec_cc_status cc1,
: "connected"); : "connected");
switch (port->state) { switch (port->state) {
case DRP_TOGGLING: case TOGGLING:
if (tcpm_port_is_debug(port) || tcpm_port_is_audio(port) || if (tcpm_port_is_debug(port) || tcpm_port_is_audio(port) ||
tcpm_port_is_source(port)) tcpm_port_is_source(port))
tcpm_set_state(port, SRC_ATTACH_WAIT, 0); tcpm_set_state(port, SRC_ATTACH_WAIT, 0);
......
...@@ -416,12 +416,16 @@ static int wcove_pd_transmit(struct tcpc_dev *tcpc, ...@@ -416,12 +416,16 @@ static int wcove_pd_transmit(struct tcpc_dev *tcpc,
return regmap_write(wcove->regmap, USBC_TXCMD, cmd | USBC_TXCMD_START); return regmap_write(wcove->regmap, USBC_TXCMD, cmd | USBC_TXCMD_START);
} }
static int wcove_start_drp_toggling(struct tcpc_dev *tcpc, static int wcove_start_toggling(struct tcpc_dev *tcpc,
enum typec_cc_status cc) enum typec_port_type port_type,
enum typec_cc_status cc)
{ {
struct wcove_typec *wcove = tcpc_to_wcove(tcpc); struct wcove_typec *wcove = tcpc_to_wcove(tcpc);
unsigned int usbc_ctrl; unsigned int usbc_ctrl;
if (port_type != TYPEC_PORT_DRP)
return -EOPNOTSUPP;
usbc_ctrl = USBC_CONTROL1_MODE_DRP | USBC_CONTROL1_DRPTOGGLE_RANDOM; usbc_ctrl = USBC_CONTROL1_MODE_DRP | USBC_CONTROL1_DRPTOGGLE_RANDOM;
switch (cc) { switch (cc) {
...@@ -642,7 +646,7 @@ static int wcove_typec_probe(struct platform_device *pdev) ...@@ -642,7 +646,7 @@ static int wcove_typec_probe(struct platform_device *pdev)
wcove->tcpc.set_polarity = wcove_set_polarity; wcove->tcpc.set_polarity = wcove_set_polarity;
wcove->tcpc.set_vconn = wcove_set_vconn; wcove->tcpc.set_vconn = wcove_set_vconn;
wcove->tcpc.set_current_limit = wcove_set_current_limit; wcove->tcpc.set_current_limit = wcove_set_current_limit;
wcove->tcpc.start_drp_toggling = wcove_start_drp_toggling; wcove->tcpc.start_toggling = wcove_start_toggling;
wcove->tcpc.set_pd_rx = wcove_set_pd_rx; wcove->tcpc.set_pd_rx = wcove_set_pd_rx;
wcove->tcpc.set_roles = wcove_set_roles; wcove->tcpc.set_roles = wcove_set_roles;
......
...@@ -121,10 +121,10 @@ struct tcpc_config { ...@@ -121,10 +121,10 @@ struct tcpc_config {
* with partner. * with partner.
* @set_pd_rx: Called to enable or disable reception of PD messages * @set_pd_rx: Called to enable or disable reception of PD messages
* @set_roles: Called to set power and data roles * @set_roles: Called to set power and data roles
* @start_drp_toggling: * @start_toggling:
* Optional; if supported by hardware, called to start DRP * Optional; if supported by hardware, called to start dual-role
* toggling. DRP toggling is stopped automatically if * toggling or single-role connection detection. Toggling stops
* a connection is established. * automatically if a connection is established.
* @try_role: Optional; called to set a preferred role * @try_role: Optional; called to set a preferred role
* @pd_transmit:Called to transmit PD message * @pd_transmit:Called to transmit PD message
* @mux: Pointer to multiplexer data * @mux: Pointer to multiplexer data
...@@ -147,8 +147,9 @@ struct tcpc_dev { ...@@ -147,8 +147,9 @@ struct tcpc_dev {
int (*set_pd_rx)(struct tcpc_dev *dev, bool on); int (*set_pd_rx)(struct tcpc_dev *dev, bool on);
int (*set_roles)(struct tcpc_dev *dev, bool attached, int (*set_roles)(struct tcpc_dev *dev, bool attached,
enum typec_role role, enum typec_data_role data); enum typec_role role, enum typec_data_role data);
int (*start_drp_toggling)(struct tcpc_dev *dev, int (*start_toggling)(struct tcpc_dev *dev,
enum typec_cc_status cc); enum typec_port_type port_type,
enum typec_cc_status cc);
int (*try_role)(struct tcpc_dev *dev, int role); int (*try_role)(struct tcpc_dev *dev, int role);
int (*pd_transmit)(struct tcpc_dev *dev, enum tcpm_transmit_type type, int (*pd_transmit)(struct tcpc_dev *dev, enum tcpm_transmit_type type,
const struct pd_message *msg); const struct pd_message *msg);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment