From: Jim Lin <jilin@nvidia.com>
To: <thierry.reding@gmail.com>, <jonathanh@nvidia.com>,
<mathias.nyman@intel.com>, <gregkh@linuxfoundation.org>
Cc: <linux-usb@vger.kernel.org>, <linux-tegra@vger.kernel.org>,
<linux-kernel@vger.kernel.org>, <jilin@nvidia.com>
Subject: [PATCH] xhci: tegra: USB2 pad power controls
Date: Thu, 22 Sep 2022 17:24:56 +0800 [thread overview]
Message-ID: <20220922092456.12264-1-jilin@nvidia.com> (raw)
Program USB2 pad PD controls during port connect/disconnect, port
suspend/resume, to reduce power consumption on disconnect or suspend.
Signed-off-by: Jim Lin <jilin@nvidia.com>
---
drivers/usb/host/xhci-tegra.c | 130 +++++++++++++++++++++++++++++++++-
1 file changed, 129 insertions(+), 1 deletion(-)
diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c
index 1ed46642abd5..9ebd68373bd5 100644
--- a/drivers/usb/host/xhci-tegra.c
+++ b/drivers/usb/host/xhci-tegra.c
@@ -189,6 +189,13 @@ struct tegra_xusb_context_soc {
} fpci;
};
+enum tegra_xhci_phy_type {
+ USB3_PHY,
+ USB2_PHY,
+ HSIC_PHY,
+ MAX_PHY_TYPES,
+};
+
struct tegra_xusb_soc {
const char *firmware;
const char * const *supply_names;
@@ -274,9 +281,17 @@ struct tegra_xusb {
bool suspended;
struct tegra_xusb_context context;
+ u32 enable_utmi_pad_after_lp0_exit;
};
static struct hc_driver __read_mostly tegra_xhci_hc_driver;
+static int (*original_xhci_hub_control)(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex,
+ char *buf, u16 wLength);
+
+static inline struct tegra_xusb *hcd_to_tegra_xusb(struct usb_hcd *hcd)
+{
+ return (struct tegra_xusb *) dev_get_drvdata(hcd->self.controller);
+}
static inline u32 fpci_readl(struct tegra_xusb *tegra, unsigned int offset)
{
@@ -1949,12 +1964,27 @@ static void tegra_xhci_enable_phy_sleepwalk_wake(struct tegra_xusb *tegra)
static void tegra_xhci_disable_phy_wake(struct tegra_xusb *tegra)
{
struct tegra_xusb_padctl *padctl = tegra->padctl;
- unsigned int i;
+ unsigned int i, j;
for (i = 0; i < tegra->num_phys; i++) {
if (!tegra->phys[i])
continue;
+ if (tegra_xusb_padctl_remote_wake_detected(padctl, tegra->phys[i])) {
+ if (i < (tegra->soc->ports.usb3.offset + tegra->soc->ports.usb3.count)) {
+ j = i;
+ } else if (i < (tegra->soc->ports.usb2.offset +
+ tegra->soc->ports.usb2.count)) {
+ j = i - tegra->soc->ports.usb2.offset;
+ tegra_phy_xusb_utmi_pad_power_on(tegra->phys[i]);
+ } else {
+ j = i - (tegra->soc->ports.usb2.offset +
+ tegra->soc->ports.usb2.count);
+ }
+ dev_dbg(tegra->dev,
+ "%s port %u (0 based) remote wake detected\n",
+ dev_name(&tegra->phys[i]->dev), j);
+ }
tegra_xusb_padctl_disable_phy_wake(padctl, tegra->phys[i]);
}
}
@@ -1972,6 +2002,22 @@ static void tegra_xhci_disable_phy_sleepwalk(struct tegra_xusb *tegra)
}
}
+static void tegra_xhci_program_utmi_power_lp0_exit(struct tegra_xusb *tegra)
+{
+ unsigned int i;
+
+ for (i = 0; i < tegra->soc->ports.usb2.count; i++) {
+ if (!is_host_mode_phy(tegra, USB2_PHY, i))
+ continue;
+ if (tegra->enable_utmi_pad_after_lp0_exit & BIT(i))
+ tegra_phy_xusb_utmi_pad_power_on(
+ tegra->phys[tegra->soc->ports.usb2.offset + i]);
+ else
+ tegra_phy_xusb_utmi_pad_power_down(
+ tegra->phys[tegra->soc->ports.usb2.offset + i]);
+ }
+}
+
static int tegra_xusb_enter_elpg(struct tegra_xusb *tegra, bool runtime)
{
struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd);
@@ -1980,6 +2026,7 @@ static int tegra_xusb_enter_elpg(struct tegra_xusb *tegra, bool runtime)
unsigned int i;
int err;
u32 usbcmd;
+ u32 portsc;
dev_dbg(dev, "entering ELPG\n");
@@ -1993,6 +2040,15 @@ static int tegra_xusb_enter_elpg(struct tegra_xusb *tegra, bool runtime)
goto out;
}
+ for (i = 0; i < tegra->soc->ports.usb2.count; i++) {
+ if (!xhci->usb2_rhub.ports[i])
+ continue;
+ portsc = readl(xhci->usb2_rhub.ports[i]->addr);
+ tegra->enable_utmi_pad_after_lp0_exit &= ~BIT(i);
+ if (((portsc & PORT_PLS_MASK) == XDEV_U3) || ((portsc & DEV_SPEED_MASK) == XDEV_FS))
+ tegra->enable_utmi_pad_after_lp0_exit |= BIT(i);
+ }
+
err = xhci_suspend(xhci, wakeup);
if (err < 0) {
dev_err(tegra->dev, "failed to suspend XHCI: %d\n", err);
@@ -2066,6 +2122,8 @@ static int tegra_xusb_exit_elpg(struct tegra_xusb *tegra, bool runtime)
phy_power_on(tegra->phys[i]);
}
+ if (tegra->suspended)
+ tegra_xhci_program_utmi_power_lp0_exit(tegra);
tegra_xusb_config(tegra);
tegra_xusb_restore_context(tegra);
@@ -2437,6 +2495,74 @@ static int tegra_xhci_setup(struct usb_hcd *hcd)
return xhci_gen_setup(hcd, tegra_xhci_quirks);
}
+static int tegra_xhci_hub_control(struct usb_hcd *hcd, u16 type_req, u16 value, u16 index,
+ char *buf, u16 length)
+{
+ struct tegra_xusb *tegra = hcd_to_tegra_xusb(hcd);
+ struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+ struct xhci_hub *rhub;
+ struct xhci_bus_state *bus_state;
+ int port = (index & 0xff) - 1;
+ int port_index;
+ struct xhci_port **ports;
+ u32 portsc;
+ int ret;
+
+ rhub = &xhci->usb2_rhub;
+ bus_state = &rhub->bus_state;
+ if (bus_state->resuming_ports && hcd->speed == HCD_USB2) {
+ ports = rhub->ports;
+ port_index = rhub->num_ports;
+ while (port_index--) {
+ if (!test_bit(port_index, &bus_state->resuming_ports))
+ continue;
+ portsc = readl(ports[port_index]->addr);
+ if ((portsc & PORT_PLS_MASK) == XDEV_RESUME)
+ tegra_phy_xusb_utmi_pad_power_on(
+ tegra->phys[tegra->soc->ports.usb2.offset + port_index]);
+ }
+ }
+
+ if (hcd->speed == HCD_USB2) {
+ port_index = tegra->soc->ports.usb2.offset + port;
+ if ((type_req == ClearPortFeature) && (value == USB_PORT_FEAT_SUSPEND))
+ tegra_phy_xusb_utmi_pad_power_on(tegra->phys[port_index]);
+ if ((type_req == SetPortFeature) && (value == USB_PORT_FEAT_RESET)) {
+ ports = rhub->ports;
+ portsc = readl(ports[port]->addr);
+ if (portsc & PORT_CONNECT)
+ tegra_phy_xusb_utmi_pad_power_on(tegra->phys[port_index]);
+ }
+ }
+
+ ret = (*original_xhci_hub_control)(hcd, type_req, value, index, buf, length);
+ if (ret < 0)
+ return ret;
+
+ if (hcd->speed == HCD_USB2) {
+ if ((type_req == SetPortFeature) && (value == USB_PORT_FEAT_SUSPEND))
+ /* We don't suspend the PAD while HNP role swap happens on the OTG port */
+ if (!((hcd->self.otg_port == (port + 1)) && hcd->self.b_hnp_enable))
+ tegra_phy_xusb_utmi_pad_power_down(tegra->phys[port_index]);
+
+ if ((type_req == ClearPortFeature) && (value == USB_PORT_FEAT_C_CONNECTION)) {
+ ports = rhub->ports;
+ portsc = readl(ports[port]->addr);
+ if (!(portsc & PORT_CONNECT)) {
+ /* We don't suspend the PAD while HNP role swap happens on the OTG
+ * port
+ */
+ if (!((hcd->self.otg_port == (port + 1)) && hcd->self.b_hnp_enable))
+ tegra_phy_xusb_utmi_pad_power_down(tegra->phys[port_index]);
+ }
+ }
+ if ((type_req == SetPortFeature) && (value == USB_PORT_FEAT_TEST))
+ tegra_phy_xusb_utmi_pad_power_on(tegra->phys[port_index]);
+ }
+
+ return ret;
+}
+
static const struct xhci_driver_overrides tegra_xhci_overrides __initconst = {
.reset = tegra_xhci_setup,
};
@@ -2444,6 +2570,8 @@ static const struct xhci_driver_overrides tegra_xhci_overrides __initconst = {
static int __init tegra_xusb_init(void)
{
xhci_init_driver(&tegra_xhci_hc_driver, &tegra_xhci_overrides);
+ original_xhci_hub_control = tegra_xhci_hc_driver.hub_control;
+ tegra_xhci_hc_driver.hub_control = tegra_xhci_hub_control;
return platform_driver_register(&tegra_xusb_driver);
}
--
2.17.1
next reply other threads:[~2022-09-22 9:26 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-09-22 9:24 Jim Lin [this message]
-- strict thread matches above, loose matches on Subject: below --
2022-07-27 10:53 [PATCH] xhci: tegra: USB2 pad power controls Jim Lin
2022-07-27 10:53 ` Jim Lin
2022-07-27 10:58 ` Greg KH
2022-07-27 10:58 ` Greg KH
[not found] ` <IA1PR12MB66038FF5370F31870ED62BA9C0969@IA1PR12MB6603.namprd12.prod.outlook.com>
2022-07-28 8:56 ` Greg KH
2022-07-28 8:56 ` Greg KH
2022-07-27 14:17 ` Thierry Reding
2022-07-27 14:17 ` Thierry Reding
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20220922092456.12264-1-jilin@nvidia.com \
--to=jilin@nvidia.com \
--cc=gregkh@linuxfoundation.org \
--cc=jonathanh@nvidia.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-tegra@vger.kernel.org \
--cc=linux-usb@vger.kernel.org \
--cc=mathias.nyman@intel.com \
--cc=thierry.reding@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.