blob: b9f6b8d5d3d67f2a25429f26b8ecfe0e02315489 [file] [log] [blame]
/* Copyright 2015 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
/* USB mux high-level driver. */
#include "common.h"
#include "console.h"
#include "host_command.h"
#include "usb_mux.h"
#include "util.h"
#define CPRINTS(format, args...) cprints(CC_USBCHARGE, format, ## args)
#define CPRINTF(format, args...) cprintf(CC_USBCHARGE, format, ## args)
static int enable_debug_prints;
void usb_mux_init(int port)
{
const struct usb_mux *mux = &usb_muxes[port];
int res;
ASSERT(port >= 0 && port < CONFIG_USB_PD_PORT_COUNT);
res = mux->driver->init(mux->port_addr);
if (res)
CPRINTS("Error initializing mux port(%d): %d", port, res);
/* Apply board specific initialization */
if (mux->board_init)
mux->board_init(mux);
}
/*
* TODO(crbug.com/505480): Setting muxes often involves I2C transcations,
* which can block. Consider implementing an asynchronous task.
*/
void usb_mux_set(int port, enum typec_mux mux_mode,
enum usb_switch usb_mode, int polarity)
{
const struct usb_mux *mux = &usb_muxes[port];
int res;
mux_state_t mux_state;
#ifdef CONFIG_USB_CHARGER
/* Configure USB2.0 */
usb_charger_set_switches(port, usb_mode);
#endif
/* Configure superspeed lanes */
mux_state = polarity ? mux_mode | MUX_POLARITY_INVERTED : mux_mode;
res = mux->driver->set(mux->port_addr, mux_state);
if (res) {
CPRINTS("Error setting mux port(%d): %d", port, res);
return;
}
if (enable_debug_prints)
CPRINTS(
"usb/dp mux: port(%d) typec_mux(%d) usb2(%d) polarity(%d)",
port, mux_mode, usb_mode, polarity);
}
int usb_mux_get(int port, const char **dp_str, const char **usb_str)
{
const struct usb_mux *mux = &usb_muxes[port];
int res;
mux_state_t mux_state;
const char *dp, *usb;
res = mux->driver->get(mux->port_addr, &mux_state);
if (res) {
CPRINTS("Error getting mux port(%d): %d", port, res);
return 0;
}
dp = mux_state & MUX_POLARITY_INVERTED ? "DP2" : "DP1";
usb = mux_state & MUX_POLARITY_INVERTED ? "USB2" : "USB1";
*dp_str = mux_state & MUX_DP_ENABLED ? dp : NULL;
*usb_str = mux_state & MUX_USB_ENABLED ? usb : NULL;
return *dp_str || *usb_str;
}
void usb_mux_flip(int port)
{
const struct usb_mux *mux = &usb_muxes[port];
int res;
mux_state_t mux_state;
res = mux->driver->get(mux->port_addr, &mux_state);
if (res) {
CPRINTS("Error getting mux port(%d): %d", port, res);
return;
}
if (mux_state & MUX_POLARITY_INVERTED)
mux_state &= ~MUX_POLARITY_INVERTED;
else
mux_state |= MUX_POLARITY_INVERTED;
res = mux->driver->set(mux->port_addr, mux_state);
if (res)
CPRINTS("Error setting mux port(%d): %d", port, res);
}
#ifdef CONFIG_CMD_TYPEC
static int command_typec(int argc, char **argv)
{
const char * const mux_name[] = {"none", "usb", "dp", "dock"};
char *e;
int port;
enum typec_mux mux = TYPEC_MUX_NONE;
int i;
if (argc == 2 && !strcasecmp(argv[1], "debug")) {
enable_debug_prints = 1;
return EC_SUCCESS;
}
if (argc < 2)
return EC_ERROR_PARAM_COUNT;
port = strtoi(argv[1], &e, 10);
if (*e || port >= CONFIG_USB_PD_PORT_COUNT)
return EC_ERROR_PARAM1;
if (argc < 3) {
const char *dp_str, *usb_str;
ccprintf("Port C%d: polarity:CC%d\n",
port, pd_get_polarity(port) + 1);
if (usb_mux_get(port, &dp_str, &usb_str))
ccprintf("Superspeed %s%s%s\n",
dp_str ? dp_str : "",
dp_str && usb_str ? "+" : "",
usb_str ? usb_str : "");
else
ccprintf("No Superspeed connection\n");
return EC_SUCCESS;
}
for (i = 0; i < ARRAY_SIZE(mux_name); i++)
if (!strcasecmp(argv[2], mux_name[i]))
mux = i;
usb_mux_set(port, mux, mux == TYPEC_MUX_NONE ?
USB_SWITCH_DISCONNECT :
USB_SWITCH_CONNECT,
pd_get_polarity(port));
return EC_SUCCESS;
}
DECLARE_CONSOLE_COMMAND(typec, command_typec,
"[port|debug] [none|usb|dp|dock]",
"Control type-C connector muxing");
#endif
static int hc_usb_pd_mux_info(struct host_cmd_handler_args *args)
{
const struct ec_params_usb_pd_mux_info *p = args->params;
struct ec_response_usb_pd_mux_info *r = args->response;
int port = p->port;
const struct usb_mux *mux;
if (port >= CONFIG_USB_PD_PORT_COUNT)
return EC_RES_INVALID_PARAM;
mux = &usb_muxes[port];
if (mux->driver->get(mux->port_addr, &r->flags) != EC_SUCCESS)
return EC_RES_ERROR;
#ifdef CONFIG_USB_MUX_VIRTUAL
/* Clear HPD IRQ event since we're about to inform host of it. */
if ((r->flags & USB_PD_MUX_HPD_IRQ) &&
mux->driver == &virtual_usb_mux_driver)
mux->hpd_update(port, 0, 0);
#endif
args->response_size = sizeof(*r);
return EC_RES_SUCCESS;
}
DECLARE_HOST_COMMAND(EC_CMD_USB_PD_MUX_INFO,
hc_usb_pd_mux_info,
EC_VER_MASK(0));