mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-25 06:00:43 +00:00
usb: tegra: support device mode
A few changes are made to the Tegra EHCI driver so that it can set everything up for device-mode operation on the first USB controller. This can be used in conjunction with ci_udc.c to operate as a USB device. Detailed changes are: * Rename set_host_mode() to set_up_vbus() since that's really what it does. * Modify set_up_vbus() to know whether it's initializing in host or device mode, and: - Skip the external VBUS check in device mode, since external VBUS is expected in this case. - Disable VBUS output in device mode. * Modify init_phy_mux() to know whether it's initializing in host or device mode, and hence skip setting USBMODE_CM_HC (which enables host mode) in device mode. See the comments in that function for why this is safe w.r.t. the ordering requirements of PHY selection. * Modify init_utmi_usb_controller() to force "b session valid" in device mode, since the HW requires this. This is done in UTMI-specific code, since we only support device mode on the first USB controller, and that controller can only talk to a UTMI PHY. * Enhance ehci_hcd_init() to error-check the requested host-/device-mode vs. the dr_mode (dual-role mode) value present in device tree, and the HW configurations which support device mode. * Enhance ehci_hcd_init() not to skip HW initialization when switching between host and device mode on a controller. This requires remembering which mode the last initialization used. Cc: Jim Lin <jilin@nvidia.com> Cc: Stefan Agner <stefan@agner.ch> Signed-off-by: Stephen Warren <swarren@nvidia.com>
This commit is contained in:
parent
2d34151f75
commit
a4539a2aa7
2 changed files with 86 additions and 30 deletions
|
@ -349,6 +349,8 @@ struct usb_ctlr {
|
||||||
|
|
||||||
/* USB3_IF_USB_PHY_VBUS_SENSORS_0 */
|
/* USB3_IF_USB_PHY_VBUS_SENSORS_0 */
|
||||||
#define VBUS_VLD_STS (1 << 26)
|
#define VBUS_VLD_STS (1 << 26)
|
||||||
|
#define VBUS_B_SESS_VLD_SW_VALUE (1 << 12)
|
||||||
|
#define VBUS_B_SESS_VLD_SW_EN (1 << 11)
|
||||||
|
|
||||||
/* Setup USB on the board */
|
/* Setup USB on the board */
|
||||||
int usb_process_devicetree(const void *blob);
|
int usb_process_devicetree(const void *blob);
|
||||||
|
|
|
@ -69,6 +69,7 @@ struct fdt_usb {
|
||||||
unsigned enabled:1; /* 1 to enable, 0 to disable */
|
unsigned enabled:1; /* 1 to enable, 0 to disable */
|
||||||
unsigned has_legacy_mode:1; /* 1 if this port has legacy mode */
|
unsigned has_legacy_mode:1; /* 1 if this port has legacy mode */
|
||||||
unsigned initialized:1; /* has this port already been initialized? */
|
unsigned initialized:1; /* has this port already been initialized? */
|
||||||
|
enum usb_init_type init_type;
|
||||||
enum dr_mode dr_mode; /* dual role mode */
|
enum dr_mode dr_mode; /* dual role mode */
|
||||||
enum periph_id periph_id;/* peripheral id */
|
enum periph_id periph_id;/* peripheral id */
|
||||||
struct fdt_gpio_state vbus_gpio; /* GPIO for vbus enable */
|
struct fdt_gpio_state vbus_gpio; /* GPIO for vbus enable */
|
||||||
|
@ -237,29 +238,31 @@ int ehci_get_port_speed(struct ehci_hcor *hcor, uint32_t reg)
|
||||||
return PORTSC_PSPD(reg);
|
return PORTSC_PSPD(reg);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Put the port into host mode */
|
/* Set up VBUS for host/device mode */
|
||||||
static void set_host_mode(struct fdt_usb *config)
|
static void set_up_vbus(struct fdt_usb *config, enum usb_init_type init)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* If we are an OTG port, check if remote host is driving VBus and
|
* If we are an OTG port initializing in host mode,
|
||||||
* bail out in this case.
|
* check if remote host is driving VBus and bail out in this case.
|
||||||
*/
|
*/
|
||||||
if (config->dr_mode == DR_MODE_OTG &&
|
if (init == USB_INIT_HOST &&
|
||||||
(readl(&config->reg->phy_vbus_sensors) & VBUS_VLD_STS))
|
config->dr_mode == DR_MODE_OTG &&
|
||||||
|
(readl(&config->reg->phy_vbus_sensors) & VBUS_VLD_STS)) {
|
||||||
|
printf("tegrausb: VBUS input active; not enabling as host\n");
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* If not driving, we set the GPIO to enable VBUS. We assume
|
|
||||||
* that the pinmux is set up correctly for this.
|
|
||||||
*/
|
|
||||||
if (fdt_gpio_isvalid(&config->vbus_gpio)) {
|
if (fdt_gpio_isvalid(&config->vbus_gpio)) {
|
||||||
|
int vbus_value;
|
||||||
|
|
||||||
fdtdec_setup_gpio(&config->vbus_gpio);
|
fdtdec_setup_gpio(&config->vbus_gpio);
|
||||||
gpio_direction_output(config->vbus_gpio.gpio,
|
|
||||||
(config->vbus_gpio.flags & FDT_GPIO_ACTIVE_LOW) ?
|
vbus_value = (init == USB_INIT_HOST) ^
|
||||||
0 : 1);
|
!!(config->vbus_gpio.flags & FDT_GPIO_ACTIVE_LOW);
|
||||||
debug("set_host_mode: GPIO %d %s\n", config->vbus_gpio.gpio,
|
gpio_direction_output(config->vbus_gpio.gpio, vbus_value);
|
||||||
(config->vbus_gpio.flags & FDT_GPIO_ACTIVE_LOW) ?
|
|
||||||
"low" : "high");
|
debug("set_up_vbus: GPIO %d %d\n", config->vbus_gpio.gpio,
|
||||||
|
vbus_value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -294,7 +297,8 @@ static const unsigned *get_pll_timing(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* select the PHY to use with a USB controller */
|
/* select the PHY to use with a USB controller */
|
||||||
static void init_phy_mux(struct fdt_usb *config, uint pts)
|
static void init_phy_mux(struct fdt_usb *config, uint pts,
|
||||||
|
enum usb_init_type init)
|
||||||
{
|
{
|
||||||
struct usb_ctlr *usbctlr = config->reg;
|
struct usb_ctlr *usbctlr = config->reg;
|
||||||
|
|
||||||
|
@ -309,10 +313,16 @@ static void init_phy_mux(struct fdt_usb *config, uint pts)
|
||||||
clrbits_le32(&usbctlr->port_sc1, STS);
|
clrbits_le32(&usbctlr->port_sc1, STS);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
/* Set to Host mode after Controller Reset was done */
|
/* Set to Host mode (if applicable) after Controller Reset was done */
|
||||||
clrsetbits_le32(&usbctlr->usb_mode, USBMODE_CM_HC,
|
clrsetbits_le32(&usbctlr->usb_mode, USBMODE_CM_HC,
|
||||||
USBMODE_CM_HC);
|
(init == USB_INIT_HOST) ? USBMODE_CM_HC : 0);
|
||||||
/* Select PHY interface after setting host mode */
|
/*
|
||||||
|
* Select PHY interface after setting host mode.
|
||||||
|
* For device mode, the ordering requirement is not an issue, since
|
||||||
|
* only the first USB controller supports device mode, and that USB
|
||||||
|
* controller can only talk to a UTMI PHY, so the PHY selection is
|
||||||
|
* already made at reset time, so this write is a no-op.
|
||||||
|
*/
|
||||||
clrsetbits_le32(&usbctlr->hostpc1_devlc, PTS_MASK,
|
clrsetbits_le32(&usbctlr->hostpc1_devlc, PTS_MASK,
|
||||||
pts << PTS_SHIFT);
|
pts << PTS_SHIFT);
|
||||||
clrbits_le32(&usbctlr->hostpc1_devlc, STS);
|
clrbits_le32(&usbctlr->hostpc1_devlc, STS);
|
||||||
|
@ -320,9 +330,10 @@ static void init_phy_mux(struct fdt_usb *config, uint pts)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* set up the UTMI USB controller with the parameters provided */
|
/* set up the UTMI USB controller with the parameters provided */
|
||||||
static int init_utmi_usb_controller(struct fdt_usb *config)
|
static int init_utmi_usb_controller(struct fdt_usb *config,
|
||||||
|
enum usb_init_type init)
|
||||||
{
|
{
|
||||||
u32 val;
|
u32 b_sess_valid_mask, val;
|
||||||
int loop_count;
|
int loop_count;
|
||||||
const unsigned *timing;
|
const unsigned *timing;
|
||||||
struct usb_ctlr *usbctlr = config->reg;
|
struct usb_ctlr *usbctlr = config->reg;
|
||||||
|
@ -340,6 +351,10 @@ static int init_utmi_usb_controller(struct fdt_usb *config)
|
||||||
/* Follow the crystal clock disable by >100ns delay */
|
/* Follow the crystal clock disable by >100ns delay */
|
||||||
udelay(1);
|
udelay(1);
|
||||||
|
|
||||||
|
b_sess_valid_mask = (VBUS_B_SESS_VLD_SW_VALUE | VBUS_B_SESS_VLD_SW_EN);
|
||||||
|
clrsetbits_le32(&usbctlr->phy_vbus_sensors, b_sess_valid_mask,
|
||||||
|
(init == USB_INIT_DEVICE) ? b_sess_valid_mask : 0);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* To Use the A Session Valid for cable detection logic, VBUS_WAKEUP
|
* To Use the A Session Valid for cable detection logic, VBUS_WAKEUP
|
||||||
* mux must be switched to actually use a_sess_vld threshold.
|
* mux must be switched to actually use a_sess_vld threshold.
|
||||||
|
@ -511,7 +526,7 @@ static int init_utmi_usb_controller(struct fdt_usb *config)
|
||||||
clrbits_le32(&usbctlr->icusb_ctrl, IC_ENB1);
|
clrbits_le32(&usbctlr->icusb_ctrl, IC_ENB1);
|
||||||
|
|
||||||
/* Select UTMI parallel interface */
|
/* Select UTMI parallel interface */
|
||||||
init_phy_mux(config, PTS_UTMI);
|
init_phy_mux(config, PTS_UTMI, init);
|
||||||
|
|
||||||
/* Deassert power down state */
|
/* Deassert power down state */
|
||||||
clrbits_le32(&usbctlr->utmip_xcvr_cfg0, UTMIP_FORCE_PD_POWERDOWN |
|
clrbits_le32(&usbctlr->utmip_xcvr_cfg0, UTMIP_FORCE_PD_POWERDOWN |
|
||||||
|
@ -541,7 +556,8 @@ static int init_utmi_usb_controller(struct fdt_usb *config)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* set up the ULPI USB controller with the parameters provided */
|
/* set up the ULPI USB controller with the parameters provided */
|
||||||
static int init_ulpi_usb_controller(struct fdt_usb *config)
|
static int init_ulpi_usb_controller(struct fdt_usb *config,
|
||||||
|
enum usb_init_type init)
|
||||||
{
|
{
|
||||||
u32 val;
|
u32 val;
|
||||||
int loop_count;
|
int loop_count;
|
||||||
|
@ -569,7 +585,7 @@ static int init_ulpi_usb_controller(struct fdt_usb *config)
|
||||||
ULPI_CLKOUT_PINMUX_BYP | ULPI_OUTPUT_PINMUX_BYP);
|
ULPI_CLKOUT_PINMUX_BYP | ULPI_OUTPUT_PINMUX_BYP);
|
||||||
|
|
||||||
/* Select ULPI parallel interface */
|
/* Select ULPI parallel interface */
|
||||||
init_phy_mux(config, PTS_ULPI);
|
init_phy_mux(config, PTS_ULPI, init);
|
||||||
|
|
||||||
/* enable ULPI transceiver */
|
/* enable ULPI transceiver */
|
||||||
setbits_le32(&usbctlr->susp_ctrl, ULPI_PHY_ENB);
|
setbits_le32(&usbctlr->susp_ctrl, ULPI_PHY_ENB);
|
||||||
|
@ -618,7 +634,8 @@ static int init_ulpi_usb_controller(struct fdt_usb *config)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
static int init_ulpi_usb_controller(struct fdt_usb *config)
|
static int init_ulpi_usb_controller(struct fdt_usb *config,
|
||||||
|
enum usb_init_type init)
|
||||||
{
|
{
|
||||||
printf("No code to set up ULPI controller, please enable"
|
printf("No code to set up ULPI controller, please enable"
|
||||||
"CONFIG_USB_ULPI and CONFIG_USB_ULPI_VIEWPORT");
|
"CONFIG_USB_ULPI and CONFIG_USB_ULPI_VIEWPORT");
|
||||||
|
@ -771,23 +788,60 @@ int ehci_hcd_init(int index, enum usb_init_type init,
|
||||||
|
|
||||||
config = &port[index];
|
config = &port[index];
|
||||||
|
|
||||||
|
switch (init) {
|
||||||
|
case USB_INIT_HOST:
|
||||||
|
switch (config->dr_mode) {
|
||||||
|
case DR_MODE_HOST:
|
||||||
|
case DR_MODE_OTG:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
printf("tegrausb: Invalid dr_mode %d for host mode\n",
|
||||||
|
config->dr_mode);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case USB_INIT_DEVICE:
|
||||||
|
if (config->periph_id != PERIPH_ID_USBD) {
|
||||||
|
printf("tegrausb: Device mode only supported on first USB controller\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (!config->utmi) {
|
||||||
|
printf("tegrausb: Device mode only supported with UTMI PHY\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
switch (config->dr_mode) {
|
||||||
|
case DR_MODE_DEVICE:
|
||||||
|
case DR_MODE_OTG:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
printf("tegrausb: Invalid dr_mode %d for device mode\n",
|
||||||
|
config->dr_mode);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
printf("tegrausb: Unknown USB_INIT_* %d\n", init);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
/* skip init, if the port is already initialized */
|
/* skip init, if the port is already initialized */
|
||||||
if (config->initialized)
|
if (config->initialized && config->init_type == init)
|
||||||
goto success;
|
goto success;
|
||||||
|
|
||||||
if (config->utmi && init_utmi_usb_controller(config)) {
|
if (config->utmi && init_utmi_usb_controller(config, init)) {
|
||||||
printf("tegrausb: Cannot init port %d\n", index);
|
printf("tegrausb: Cannot init port %d\n", index);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config->ulpi && init_ulpi_usb_controller(config)) {
|
if (config->ulpi && init_ulpi_usb_controller(config, init)) {
|
||||||
printf("tegrausb: Cannot init port %d\n", index);
|
printf("tegrausb: Cannot init port %d\n", index);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
set_host_mode(config);
|
set_up_vbus(config, init);
|
||||||
|
|
||||||
config->initialized = 1;
|
config->initialized = 1;
|
||||||
|
config->init_type = init;
|
||||||
|
|
||||||
success:
|
success:
|
||||||
usbctlr = config->reg;
|
usbctlr = config->reg;
|
||||||
|
|
Loading…
Reference in a new issue