Skip to content

Commit a30951d

Browse files
petlozupgregkh
authored andcommitted
xhci: tegra: USB2 pad power controls
Program USB2 pad PD controls during port connect/disconnect, port suspend/resume, and test mode, to reduce power consumption on disconnect or suspend. Signed-off-by: Petlozu Pravareshwar <[email protected]> Co-developed-by: Jim Lin <[email protected]> Signed-off-by: Jim Lin <[email protected]> Acked-by: Thierry Reding <[email protected]> Reviewed-by: Jon Hunter <[email protected]> Tested-by: Jon Hunter <[email protected]> Acked-by: Mathias Nyman <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 2cbe475 commit a30951d

File tree

1 file changed

+125
-0
lines changed

1 file changed

+125
-0
lines changed

drivers/usb/host/xhci-tegra.c

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,7 @@ struct tegra_xusb {
311311

312312
bool suspended;
313313
struct tegra_xusb_context context;
314+
u8 lp0_utmi_pad_mask;
314315
};
315316

316317
static struct hc_driver __read_mostly tegra_xhci_hc_driver;
@@ -2092,10 +2093,24 @@ static void tegra_xhci_disable_phy_wake(struct tegra_xusb *tegra)
20922093
struct tegra_xusb_padctl *padctl = tegra->padctl;
20932094
unsigned int i;
20942095

2096+
for (i = 0; i < tegra->num_usb_phys; i++) {
2097+
struct phy *phy = tegra_xusb_get_phy(tegra, "usb2", i);
2098+
2099+
if (!phy)
2100+
continue;
2101+
2102+
if (tegra_xusb_padctl_remote_wake_detected(padctl, phy))
2103+
tegra_phy_xusb_utmi_pad_power_on(phy);
2104+
}
2105+
20952106
for (i = 0; i < tegra->num_phys; i++) {
20962107
if (!tegra->phys[i])
20972108
continue;
20982109

2110+
if (tegra_xusb_padctl_remote_wake_detected(padctl, tegra->phys[i]))
2111+
dev_dbg(tegra->dev, "%pOF remote wake detected\n",
2112+
tegra->phys[i]->dev.of_node);
2113+
20992114
tegra_xusb_padctl_disable_phy_wake(padctl, tegra->phys[i]);
21002115
}
21012116
}
@@ -2113,6 +2128,28 @@ static void tegra_xhci_disable_phy_sleepwalk(struct tegra_xusb *tegra)
21132128
}
21142129
}
21152130

2131+
static void tegra_xhci_program_utmi_power_lp0_exit(struct tegra_xusb *tegra)
2132+
{
2133+
unsigned int i, index_to_usb2;
2134+
struct phy *phy;
2135+
2136+
for (i = 0; i < tegra->soc->num_types; i++) {
2137+
if (strcmp(tegra->soc->phy_types[i].name, "usb2") == 0)
2138+
index_to_usb2 = i;
2139+
}
2140+
2141+
for (i = 0; i < tegra->num_usb_phys; i++) {
2142+
if (!is_host_mode_phy(tegra, index_to_usb2, i))
2143+
continue;
2144+
2145+
phy = tegra_xusb_get_phy(tegra, "usb2", i);
2146+
if (tegra->lp0_utmi_pad_mask & BIT(i))
2147+
tegra_phy_xusb_utmi_pad_power_on(phy);
2148+
else
2149+
tegra_phy_xusb_utmi_pad_power_down(phy);
2150+
}
2151+
}
2152+
21162153
static int tegra_xusb_enter_elpg(struct tegra_xusb *tegra, bool runtime)
21172154
{
21182155
struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd);
@@ -2121,6 +2158,7 @@ static int tegra_xusb_enter_elpg(struct tegra_xusb *tegra, bool runtime)
21212158
unsigned int i;
21222159
int err;
21232160
u32 usbcmd;
2161+
u32 portsc;
21242162

21252163
dev_dbg(dev, "entering ELPG\n");
21262164

@@ -2134,6 +2172,15 @@ static int tegra_xusb_enter_elpg(struct tegra_xusb *tegra, bool runtime)
21342172
goto out;
21352173
}
21362174

2175+
for (i = 0; i < tegra->num_usb_phys; i++) {
2176+
if (!xhci->usb2_rhub.ports[i])
2177+
continue;
2178+
portsc = readl(xhci->usb2_rhub.ports[i]->addr);
2179+
tegra->lp0_utmi_pad_mask &= ~BIT(i);
2180+
if (((portsc & PORT_PLS_MASK) == XDEV_U3) || ((portsc & DEV_SPEED_MASK) == XDEV_FS))
2181+
tegra->lp0_utmi_pad_mask |= BIT(i);
2182+
}
2183+
21372184
err = xhci_suspend(xhci, wakeup);
21382185
if (err < 0) {
21392186
dev_err(tegra->dev, "failed to suspend XHCI: %d\n", err);
@@ -2207,6 +2254,8 @@ static int tegra_xusb_exit_elpg(struct tegra_xusb *tegra, bool runtime)
22072254

22082255
phy_power_on(tegra->phys[i]);
22092256
}
2257+
if (tegra->suspended)
2258+
tegra_xhci_program_utmi_power_lp0_exit(tegra);
22102259

22112260
tegra_xusb_config(tegra);
22122261
tegra_xusb_restore_context(tegra);
@@ -2626,8 +2675,84 @@ static int tegra_xhci_setup(struct usb_hcd *hcd)
26262675
return xhci_gen_setup(hcd, tegra_xhci_quirks);
26272676
}
26282677

2678+
static int tegra_xhci_hub_control(struct usb_hcd *hcd, u16 type_req, u16 value, u16 index,
2679+
char *buf, u16 length)
2680+
{
2681+
struct tegra_xusb *tegra = dev_get_drvdata(hcd->self.controller);
2682+
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
2683+
struct xhci_hub *rhub;
2684+
struct xhci_bus_state *bus_state;
2685+
int port = (index & 0xff) - 1;
2686+
unsigned int i;
2687+
struct xhci_port **ports;
2688+
u32 portsc;
2689+
int ret;
2690+
struct phy *phy;
2691+
2692+
rhub = &xhci->usb2_rhub;
2693+
bus_state = &rhub->bus_state;
2694+
if (bus_state->resuming_ports && hcd->speed == HCD_USB2) {
2695+
ports = rhub->ports;
2696+
i = rhub->num_ports;
2697+
while (i--) {
2698+
if (!test_bit(i, &bus_state->resuming_ports))
2699+
continue;
2700+
portsc = readl(ports[i]->addr);
2701+
if ((portsc & PORT_PLS_MASK) == XDEV_RESUME)
2702+
tegra_phy_xusb_utmi_pad_power_on(
2703+
tegra_xusb_get_phy(tegra, "usb2", (int) i));
2704+
}
2705+
}
2706+
2707+
if (hcd->speed == HCD_USB2) {
2708+
phy = tegra_xusb_get_phy(tegra, "usb2", port);
2709+
if ((type_req == ClearPortFeature) && (value == USB_PORT_FEAT_SUSPEND)) {
2710+
if (!index || index > rhub->num_ports)
2711+
return -EPIPE;
2712+
tegra_phy_xusb_utmi_pad_power_on(phy);
2713+
}
2714+
if ((type_req == SetPortFeature) && (value == USB_PORT_FEAT_RESET)) {
2715+
if (!index || index > rhub->num_ports)
2716+
return -EPIPE;
2717+
ports = rhub->ports;
2718+
portsc = readl(ports[port]->addr);
2719+
if (portsc & PORT_CONNECT)
2720+
tegra_phy_xusb_utmi_pad_power_on(phy);
2721+
}
2722+
}
2723+
2724+
ret = xhci_hub_control(hcd, type_req, value, index, buf, length);
2725+
if (ret < 0)
2726+
return ret;
2727+
2728+
if (hcd->speed == HCD_USB2) {
2729+
/* Use phy where we set previously */
2730+
if ((type_req == SetPortFeature) && (value == USB_PORT_FEAT_SUSPEND))
2731+
/* We don't suspend the PAD while HNP role swap happens on the OTG port */
2732+
if (!((hcd->self.otg_port == (port + 1)) && hcd->self.b_hnp_enable))
2733+
tegra_phy_xusb_utmi_pad_power_down(phy);
2734+
2735+
if ((type_req == ClearPortFeature) && (value == USB_PORT_FEAT_C_CONNECTION)) {
2736+
ports = rhub->ports;
2737+
portsc = readl(ports[port]->addr);
2738+
if (!(portsc & PORT_CONNECT)) {
2739+
/* We don't suspend the PAD while HNP role swap happens on the OTG
2740+
* port
2741+
*/
2742+
if (!((hcd->self.otg_port == (port + 1)) && hcd->self.b_hnp_enable))
2743+
tegra_phy_xusb_utmi_pad_power_down(phy);
2744+
}
2745+
}
2746+
if ((type_req == SetPortFeature) && (value == USB_PORT_FEAT_TEST))
2747+
tegra_phy_xusb_utmi_pad_power_on(phy);
2748+
}
2749+
2750+
return ret;
2751+
}
2752+
26292753
static const struct xhci_driver_overrides tegra_xhci_overrides __initconst = {
26302754
.reset = tegra_xhci_setup,
2755+
.hub_control = tegra_xhci_hub_control,
26312756
};
26322757

26332758
static int __init tegra_xusb_init(void)

0 commit comments

Comments
 (0)