Message ID | 20200831044043.1561074-9-jckuo@nvidia.com |
---|---|
State | New |
Headers | show |
Series | Tegra XHCI controller ELPG support | expand |
On Mon, Aug 31, 2020 at 12:40:39PM +0800, JC Kuo wrote: > This commit implements Tegra210 XUSB PADCTL wake and sleepwalk > routines. Sleepwalk logic is in PMC (always-on) hardware block. > PMC driver provides managed access to the sleepwalk registers > via regmap framework. > > Signed-off-by: JC Kuo <jckuo@nvidia.com> > --- > drivers/phy/tegra/xusb-tegra210.c | 1094 ++++++++++++++++++++++++++++- > 1 file changed, 1079 insertions(+), 15 deletions(-) > > diff --git a/drivers/phy/tegra/xusb-tegra210.c b/drivers/phy/tegra/xusb-tegra210.c > index fe1ab440424d..1c03f4ec4b59 100644 > --- a/drivers/phy/tegra/xusb-tegra210.c > +++ b/drivers/phy/tegra/xusb-tegra210.c > @@ -16,6 +16,8 @@ > #include <linux/regulator/consumer.h> > #include <linux/reset.h> > #include <linux/slab.h> > +#include <linux/regmap.h> > +#include <linux/of_platform.h> > > #include <soc/tegra/fuse.h> > > @@ -52,6 +54,20 @@ > #define XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP(x, v) (((v) & 0x7) << ((x) * 5)) > #define XUSB_PADCTL_SS_PORT_MAP_PORT_DISABLED 0x7 > > +#define XUSB_PADCTL_ELPG_PROGRAM_0 0x20 > +#define USB2_PORT_WAKE_INTERRUPT_ENABLE(x) BIT((x)) > +#define USB2_PORT_WAKEUP_EVENT(x) BIT((x) + 7) > +#define SS_PORT_WAKE_INTERRUPT_ENABLE(x) BIT((x) + 14) > +#define SS_PORT_WAKEUP_EVENT(x) BIT((x) + 21) > +#define USB2_HSIC_PORT_WAKE_INTERRUPT_ENABLE(x) BIT((x) + 28) > +#define USB2_HSIC_PORT_WAKEUP_EVENT(x) BIT((x) + 30) > +#define ALL_WAKE_EVENTS ( \ > + USB2_PORT_WAKEUP_EVENT(0) | USB2_PORT_WAKEUP_EVENT(1) | \ > + USB2_PORT_WAKEUP_EVENT(2) | USB2_PORT_WAKEUP_EVENT(3) | \ > + SS_PORT_WAKEUP_EVENT(0) | SS_PORT_WAKEUP_EVENT(1) | \ > + SS_PORT_WAKEUP_EVENT(2) | SS_PORT_WAKEUP_EVENT(3) | \ > + USB2_HSIC_PORT_WAKEUP_EVENT(0)) > + > #define XUSB_PADCTL_ELPG_PROGRAM1 0x024 > #define XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_VCORE_DOWN (1 << 31) > #define XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_CLAMP_EN_EARLY (1 << 30) > @@ -90,6 +106,8 @@ > #define XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_DR (1 << 2) > #define XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_DISC_OVRD (1 << 1) > #define XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_CHRP_OVRD (1 << 0) > +#define RPD_CTRL(x) (((x) & 0x1f) << 26) > +#define RPD_CTRL_VALUE(x) (((x) >> 26) & 0x1f) > > #define XUSB_PADCTL_USB2_BIAS_PAD_CTL0 0x284 > #define XUSB_PADCTL_USB2_BIAS_PAD_CTL0_PD (1 << 11) > @@ -108,6 +126,8 @@ > #define XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_SHIFT 12 > #define XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_MASK 0x7f > #define XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_VAL 0x1e > +#define TCTRL_VALUE(x) (((x) & 0x3f) >> 0) > +#define PCTRL_VALUE(x) (((x) >> 6) & 0x3f) > > #define XUSB_PADCTL_HSIC_PADX_CTL0(x) (0x300 + (x) * 0x20) > #define XUSB_PADCTL_HSIC_PAD_CTL0_RPU_STROBE (1 << 18) > @@ -251,16 +271,161 @@ > #define XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_FLOATING 8 > #define XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_GROUNDED 0 > > +/* USB2 SLEEPWALK registers */ > +#define UTMIP(_port, _offset1, _offset2) \ > + (((_port) <= 2) ? (_offset1) : (_offset2)) > + > +#define PMC_UTMIP_UHSIC_SLEEP_CFG(x) UTMIP(x, 0x1fc, 0x4d0) > +#define UTMIP_MASTER_ENABLE(x) UTMIP(x, BIT(8 * (x)), BIT(0)) > +#define UTMIP_FSLS_USE_PMC(x) UTMIP(x, BIT(8 * (x) + 1), \ > + BIT(1)) > +#define UTMIP_PCTRL_USE_PMC(x) UTMIP(x, BIT(8 * (x) + 2), \ > + BIT(2)) > +#define UTMIP_TCTRL_USE_PMC(x) UTMIP(x, BIT(8 * (x) + 3), \ > + BIT(3)) > +#define UTMIP_WAKE_VAL(_port, _value) (((_value) & 0xf) << \ > + (UTMIP(_port, 8 * (_port) + 4, 4))) > +#define UTMIP_WAKE_VAL_NONE(_port) UTMIP_WAKE_VAL(_port, 12) > +#define UTMIP_WAKE_VAL_ANY(_port) UTMIP_WAKE_VAL(_port, 15) > + > +#define PMC_UTMIP_UHSIC_SLEEP_CFG1 (0x4d0) > +#define UTMIP_RPU_SWITC_LOW_USE_PMC_PX(x) BIT((x) + 8) > +#define UTMIP_RPD_CTRL_USE_PMC_PX(x) BIT((x) + 16) > + > +#define PMC_UTMIP_MASTER_CONFIG (0x274) > +#define UTMIP_PWR(x) UTMIP(x, BIT(x), BIT(4)) > +#define UHSIC_PWR(x) BIT(3) > + > +#define PMC_USB_DEBOUNCE_DEL (0xec) > +#define DEBOUNCE_VAL(x) (((x) & 0xffff) << 0) > +#define UTMIP_LINE_DEB_CNT(x) (((x) & 0xf) << 16) > +#define UHSIC_LINE_DEB_CNT(x) (((x) & 0xf) << 20) > + > +#define PMC_UTMIP_UHSIC_FAKE(x) UTMIP(x, 0x218, 0x294) > +#define UTMIP_FAKE_USBOP_VAL(x) UTMIP(x, BIT(4 * (x)), BIT(8)) > +#define UTMIP_FAKE_USBON_VAL(x) UTMIP(x, BIT(4 * (x) + 1), \ > + BIT(9)) > +#define UTMIP_FAKE_USBOP_EN(x) UTMIP(x, BIT(4 * (x) + 2), \ > + BIT(10)) > +#define UTMIP_FAKE_USBON_EN(x) UTMIP(x, BIT(4 * (x) + 3), \ > + BIT(11)) > + > +#define PMC_UTMIP_UHSIC_SLEEPWALK_CFG(x) UTMIP(x, 0x200, 0x288) > +#define UTMIP_LINEVAL_WALK_EN(x) UTMIP(x, BIT(8 * (x) + 7), \ > + BIT(15)) > + > +#define PMC_USB_AO (0xf0) > +#define USBOP_VAL_PD(x) UTMIP(x, BIT(4 * (x)), BIT(20)) > +#define USBON_VAL_PD(x) UTMIP(x, BIT(4 * (x) + 1), \ > + BIT(21)) > +#define STROBE_VAL_PD(x) BIT(12) > +#define DATA0_VAL_PD(x) BIT(13) > +#define DATA1_VAL_PD BIT(24) > + > +#define PMC_UTMIP_UHSIC_SAVED_STATE(x) UTMIP(x, 0x1f0, 0x280) > +#define SPEED(_port, _value) (((_value) & 0x3) << \ > + (UTMIP(_port, 8 * (_port), 8))) > +#define UTMI_HS(_port) SPEED(_port, 0) > +#define UTMI_FS(_port) SPEED(_port, 1) > +#define UTMI_LS(_port) SPEED(_port, 2) > +#define UTMI_RST(_port) SPEED(_port, 3) > + > +#define PMC_UTMIP_UHSIC_TRIGGERS (0x1ec) > +#define UTMIP_CLR_WALK_PTR(x) UTMIP(x, BIT(x), BIT(16)) > +#define UTMIP_CAP_CFG(x) UTMIP(x, BIT((x) + 4), BIT(17)) > +#define UTMIP_CLR_WAKE_ALARM(x) UTMIP(x, BIT((x) + 12), \ > + BIT(19)) > +#define UHSIC_CLR_WALK_PTR BIT(3) > +#define UHSIC_CLR_WAKE_ALARM BIT(15) > + > +#define PMC_UTMIP_SLEEPWALK_PX(x) UTMIP(x, 0x204 + (4 * (x)), \ > + 0x4e0) > +/* phase A */ > +#define UTMIP_USBOP_RPD_A BIT(0) > +#define UTMIP_USBON_RPD_A BIT(1) > +#define UTMIP_AP_A BIT(4) > +#define UTMIP_AN_A BIT(5) > +#define UTMIP_HIGHZ_A BIT(6) > +/* phase B */ > +#define UTMIP_USBOP_RPD_B BIT(8) > +#define UTMIP_USBON_RPD_B BIT(9) > +#define UTMIP_AP_B BIT(12) > +#define UTMIP_AN_B BIT(13) > +#define UTMIP_HIGHZ_B BIT(14) > +/* phase C */ > +#define UTMIP_USBOP_RPD_C BIT(16) > +#define UTMIP_USBON_RPD_C BIT(17) > +#define UTMIP_AP_C BIT(20) > +#define UTMIP_AN_C BIT(21) > +#define UTMIP_HIGHZ_C BIT(22) > +/* phase D */ > +#define UTMIP_USBOP_RPD_D BIT(24) > +#define UTMIP_USBON_RPD_D BIT(25) > +#define UTMIP_AP_D BIT(28) > +#define UTMIP_AN_D BIT(29) > +#define UTMIP_HIGHZ_D BIT(30) > + > +#define PMC_UTMIP_UHSIC_LINE_WAKEUP (0x26c) > +#define UTMIP_LINE_WAKEUP_EN(x) UTMIP(x, BIT(x), BIT(4)) > +#define UHSIC_LINE_WAKEUP_EN BIT(3) > + > +#define PMC_UTMIP_TERM_PAD_CFG (0x1f8) > +#define PCTRL_VAL(x) (((x) & 0x3f) << 1) > +#define TCTRL_VAL(x) (((x) & 0x3f) << 7) > + > +#define PMC_UTMIP_PAD_CFGX(x) (0x4c0 + (4 * (x))) > +#define RPD_CTRL_PX(x) (((x) & 0x1f) << 22) > + > +#define PMC_UHSIC_SLEEP_CFG PMC_UTMIP_UHSIC_SLEEP_CFG(0) > +#define UHSIC_MASTER_ENABLE BIT(24) > +#define UHSIC_WAKE_VAL(_value) (((_value) & 0xf) << 28) > +#define UHSIC_WAKE_VAL_SD10 UHSIC_WAKE_VAL(2) > +#define UHSIC_WAKE_VAL_NONE UHSIC_WAKE_VAL(12) > + > +#define PMC_UHSIC_FAKE PMC_UTMIP_UHSIC_FAKE(0) > +#define UHSIC_FAKE_STROBE_VAL BIT(12) > +#define UHSIC_FAKE_DATA_VAL BIT(13) > +#define UHSIC_FAKE_STROBE_EN BIT(14) > +#define UHSIC_FAKE_DATA_EN BIT(15) > + > +#define PMC_UHSIC_SAVED_STATE PMC_UTMIP_UHSIC_SAVED_STATE(0) > +#define UHSIC_MODE(_value) (((_value) & 0x1) << 24) > +#define UHSIC_HS UHSIC_MODE(0) > +#define UHSIC_RST UHSIC_MODE(1) > + > +#define PMC_UHSIC_SLEEPWALK_CFG PMC_UTMIP_UHSIC_SLEEPWALK_CFG(0) > +#define UHSIC_WAKE_WALK_EN BIT(30) > +#define UHSIC_LINEVAL_WALK_EN BIT(31) > + > +#define PMC_UHSIC_SLEEPWALK_P0 (0x210) > +#define UHSIC_DATA0_RPD_A BIT(1) > +#define UHSIC_DATA0_RPU_B BIT(11) > +#define UHSIC_DATA0_RPU_C BIT(19) > +#define UHSIC_DATA0_RPU_D BIT(27) > +#define UHSIC_STROBE_RPU_A BIT(2) > +#define UHSIC_STROBE_RPD_B BIT(8) > +#define UHSIC_STROBE_RPD_C BIT(16) > +#define UHSIC_STROBE_RPD_D BIT(24) > + > struct tegra210_xusb_fuse_calibration { > u32 hs_curr_level[4]; > u32 hs_term_range_adj; > u32 rpd_ctrl; > }; > > +struct tegra210_xusb_padctl_context { > + u32 usb2_pad_mux; > + u32 usb2_port_cap; > + u32 ss_port_map; > + u32 usb3_pad_mux; > +}; > + > struct tegra210_xusb_padctl { > struct tegra_xusb_padctl base; > > struct tegra210_xusb_fuse_calibration fuse; > + struct tegra210_xusb_padctl_context context; > + struct regmap *pmc_reg; I'd move this more towards the top because it's a resource that we're requesting early on. Also, perhaps just name it "regmap" since "pmc_reg" could be mistaken for a "PMC register offset". > }; > > static inline struct tegra210_xusb_padctl * > @@ -886,6 +1051,671 @@ static int tegra210_hsic_set_idle(struct tegra_xusb_padctl *padctl, > return 0; > } > > +static int tegra210_usb3_enable_phy_sleepwalk(struct phy *phy) > +{ > + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); > + struct tegra_xusb_padctl *padctl = lane->pad->padctl; > + int port = tegra210_usb3_lane_map(lane); > + struct device *dev = padctl->dev; > + u32 value; > + > + if (port < 0) { > + dev_err(dev, "invalid usb3 port number\n"); > + return -EINVAL; > + } > + > + dev_dbg(dev, "phy enable sleepwalk usb3 %d\n", port); > + > + mutex_lock(&padctl->lock); > + > + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1); > + value |= XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN_EARLY(port); > + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1); > + > + usleep_range(100, 200); > + > + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1); > + value |= XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN(port); > + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1); > + > + usleep_range(250, 350); > + > + mutex_unlock(&padctl->lock); > + > + return 0; > +} > + > +static int tegra210_usb3_disable_phy_sleepwalk(struct phy *phy) > +{ > + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); > + struct tegra_xusb_padctl *padctl = lane->pad->padctl; > + int port = tegra210_usb3_lane_map(lane); > + struct device *dev = padctl->dev; > + u32 value; > + > + if (port < 0) { > + dev_err(dev, "invalid usb3 port number\n"); > + return -EINVAL; > + } > + > + dev_dbg(dev, "phy disable sleepwalk usb3 %d\n", port); > + > + mutex_lock(&padctl->lock); > + > + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1); > + value &= ~XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN_EARLY(port); > + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1); > + > + usleep_range(100, 200); > + > + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1); > + value &= ~XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN(port); > + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1); > + > + mutex_unlock(&padctl->lock); > + > + return 0; > +} > + > +static int tegra210_usb3_enable_phy_wake(struct phy *phy) > +{ > + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); > + struct tegra_xusb_padctl *padctl = lane->pad->padctl; > + int port = tegra210_usb3_lane_map(lane); > + struct device *dev = padctl->dev; > + u32 value; > + > + if (port < 0) { > + dev_err(dev, "invalid usb3 port number\n"); > + return -EINVAL; > + } > + > + dev_dbg(dev, "phy enable wake usb3 %d\n", port); > + > + mutex_lock(&padctl->lock); > + > + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0); > + value &= ~ALL_WAKE_EVENTS; > + value |= SS_PORT_WAKEUP_EVENT(port); > + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_0); > + > + usleep_range(10, 20); > + > + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0); > + value &= ~ALL_WAKE_EVENTS; > + value |= SS_PORT_WAKE_INTERRUPT_ENABLE(port); > + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_0); > + > + mutex_unlock(&padctl->lock); > + > + return 0; > +} > + > +static int tegra210_usb3_disable_phy_wake(struct phy *phy) > +{ > + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); > + struct tegra_xusb_padctl *padctl = lane->pad->padctl; > + int port = tegra210_usb3_lane_map(lane); > + struct device *dev = padctl->dev; > + u32 value; > + > + if (port < 0) { > + dev_err(dev, "invalid usb3 port number\n"); > + return -EINVAL; > + } > + > + dev_dbg(dev, "phy disable wake usb3 %d\n", port); > + > + mutex_lock(&padctl->lock); > + > + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0); > + value &= ~ALL_WAKE_EVENTS; > + value &= ~SS_PORT_WAKE_INTERRUPT_ENABLE(port); > + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_0); > + > + usleep_range(10, 20); > + > + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0); > + value &= ~ALL_WAKE_EVENTS; > + value |= SS_PORT_WAKEUP_EVENT(port); > + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_0); > + > + mutex_unlock(&padctl->lock); > + > + return 0; > +} > + > +static int tegra210_utmi_enable_phy_wake(struct phy *phy) > +{ > + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); > + struct tegra_xusb_padctl *padctl = lane->pad->padctl; > + unsigned int index = lane->index; > + struct device *dev = padctl->dev; > + u32 value; > + > + dev_dbg(dev, "phy enable wake on usb2 %d\n", index); > + > + mutex_lock(&padctl->lock); > + > + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0); > + value &= ~ALL_WAKE_EVENTS; > + value |= USB2_PORT_WAKEUP_EVENT(index); > + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_0); > + > + usleep_range(10, 20); > + > + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0); > + value &= ~ALL_WAKE_EVENTS; > + value |= USB2_PORT_WAKE_INTERRUPT_ENABLE(index); > + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_0); > + > + mutex_unlock(&padctl->lock); > + > + return 0; > +} > + > +static int tegra210_utmi_disable_phy_wake(struct phy *phy) > +{ > + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); > + struct tegra_xusb_padctl *padctl = lane->pad->padctl; > + unsigned int index = lane->index; > + struct device *dev = padctl->dev; > + u32 value; > + > + dev_dbg(dev, "phy disable wake on usb2 %d\n", index); > + > + mutex_lock(&padctl->lock); > + > + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0); > + value &= ~ALL_WAKE_EVENTS; > + value &= ~USB2_PORT_WAKE_INTERRUPT_ENABLE(index); > + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_0); > + > + usleep_range(10, 20); > + > + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0); > + value &= ~ALL_WAKE_EVENTS; > + value |= USB2_PORT_WAKEUP_EVENT(index); > + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_0); > + > + mutex_unlock(&padctl->lock); > + > + return 0; > +} > + > +static int tegra210_hsic_enable_phy_wake(struct phy *phy) > +{ > + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); > + struct tegra_xusb_padctl *padctl = lane->pad->padctl; > + unsigned int index = lane->index; > + struct device *dev = padctl->dev; > + u32 value; > + > + dev_dbg(dev, "phy enable wake on hsic %d\n", index); > + > + mutex_lock(&padctl->lock); > + > + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0); > + value &= ~ALL_WAKE_EVENTS; > + value |= USB2_HSIC_PORT_WAKEUP_EVENT(index); > + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_0); > + > + usleep_range(10, 20); > + > + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0); > + value &= ~ALL_WAKE_EVENTS; > + value |= USB2_HSIC_PORT_WAKE_INTERRUPT_ENABLE(index); > + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_0); > + > + mutex_unlock(&padctl->lock); > + > + return 0; > +} > + > +static int tegra210_hsic_disable_phy_wake(struct phy *phy) > +{ > + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); > + struct tegra_xusb_padctl *padctl = lane->pad->padctl; > + unsigned int index = lane->index; > + struct device *dev = padctl->dev; > + u32 value; > + > + dev_dbg(dev, "phy disable wake on hsic %d\n", index); > + > + mutex_lock(&padctl->lock); > + > + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0); > + value &= ~ALL_WAKE_EVENTS; > + value &= ~USB2_HSIC_PORT_WAKE_INTERRUPT_ENABLE(index); > + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_0); > + > + usleep_range(10, 20); > + > + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0); > + value &= ~ALL_WAKE_EVENTS; > + value |= USB2_HSIC_PORT_WAKEUP_EVENT(index); > + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_0); > + > + mutex_unlock(&padctl->lock); > + > + return 0; > +} > + > +static int tegra210_usb3_phy_remote_wake_detected( > + struct tegra_xusb_padctl *padctl, int port) The 80 column limit no longer applies and you can now use up to 100 columns. There's a couple of other places where you've unnecessarily wrapped too early. > +{ > + u32 value; > + > + if (port < 0) { Do we need this check here? Since this is a local helper, shouldn't all the callers already make sure that they're not passing in invalid values? > + dev_err(padctl->dev, "invalid usb3 port number %d\n", > + port); > + return false; If you want the function to return bool, just make the return type bool as well. > + } > + > + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0); > + if ((value & SS_PORT_WAKE_INTERRUPT_ENABLE(port)) && > + (value & SS_PORT_WAKEUP_EVENT(port))) > + return true; > + else > + return false; The else is not needed here. > +} > + > +static int tegra210_utmi_phy_remote_wake_detected( > + struct tegra_xusb_padctl *padctl, int port) > +{ > + u32 value; > + > + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0); > + if ((value & USB2_PORT_WAKE_INTERRUPT_ENABLE(port)) && > + (value & USB2_PORT_WAKEUP_EVENT(port))) > + return true; > + else > + return false; > +} > + > +static int tegra210_hsic_phy_remote_wake_detected( > + struct tegra_xusb_padctl *padctl, int port) > +{ > + u32 value; > + > + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0); > + if ((value & USB2_HSIC_PORT_WAKE_INTERRUPT_ENABLE(port)) && > + (value & USB2_HSIC_PORT_WAKEUP_EVENT(port))) > + return true; > + else > + return false; > +} Perhaps you want to sort these with the USB3, HSIC and UTMI functions above rather than sort them by type of function? > + > +#define padctl_pmc_readl(_priv, _offset) \ > +({ \ > + int rc; \ > + u32 val; \ s/val/value/ here and below. > + rc = regmap_read(_priv->pmc_reg, _offset, &val); \ > + if (rc) \ > + return rc; \ > + val; \ > +}) > + > +#define padctl_pmc_writel(_priv, _val, _offset) \ > +do { \ > + int rc; \ > + rc = regmap_write(_priv->pmc_reg, _offset, _val); \ > + if (rc) \ > + return rc; \ > +} while (0) > + > +/* T210 USB2 SLEEPWALK APIs */ Tegra210, please. Although this really shouldn't be needed, since you can derive as much from the function names. > +int tegra_pmc_utmi_enable_phy_sleepwalk(struct phy *phy, > + enum usb_device_speed speed) Perhaps use tegra210_ as the prefix for consistency? > +{ > + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); > + struct tegra_xusb_padctl *padctl = lane->pad->padctl; > + struct tegra210_xusb_padctl *priv = to_tegra210_xusb_padctl(padctl); > + struct device *dev = padctl->dev; > + unsigned int port = lane->index; > + u32 val, tctrl, pctrl, rpd_ctrl; s/val/value/ here and below. > + > + if (speed > USB_SPEED_HIGH) > + return -EINVAL; > + > + dev_dbg(dev, "phy enable sleepwalk usb2 %d speed %d\n", port, speed); > + > + val = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL1); > + tctrl = TCTRL_VALUE(val); > + pctrl = PCTRL_VALUE(val); > + > + val = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL1(port)); > + rpd_ctrl = RPD_CTRL_VALUE(val); > + > + /* ensure sleepwalk logic is disabled */ > + val = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG(port)); > + val &= ~UTMIP_MASTER_ENABLE(port); > + padctl_pmc_writel(priv, val, PMC_UTMIP_UHSIC_SLEEP_CFG(port)); > + > + /* ensure sleepwalk logics are in low power mode */ > + val = padctl_pmc_readl(priv, PMC_UTMIP_MASTER_CONFIG); > + val |= UTMIP_PWR(port); > + padctl_pmc_writel(priv, val, PMC_UTMIP_MASTER_CONFIG); > + > + /* set debounce time */ > + val = padctl_pmc_readl(priv, PMC_USB_DEBOUNCE_DEL); > + val &= ~UTMIP_LINE_DEB_CNT(~0); > + val |= UTMIP_LINE_DEB_CNT(0x1); > + padctl_pmc_writel(priv, val, PMC_USB_DEBOUNCE_DEL); > + > + /* ensure fake events of sleepwalk logic are desiabled */ > + val = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_FAKE(port)); > + val &= ~(UTMIP_FAKE_USBOP_VAL(port) | UTMIP_FAKE_USBON_VAL(port) | > + UTMIP_FAKE_USBOP_EN(port) | UTMIP_FAKE_USBON_EN(port)); > + padctl_pmc_writel(priv, val, PMC_UTMIP_UHSIC_FAKE(port)); > + > + /* ensure wake events of sleepwalk logic are not latched */ > + val = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_LINE_WAKEUP); > + val &= ~UTMIP_LINE_WAKEUP_EN(port); > + padctl_pmc_writel(priv, val, PMC_UTMIP_UHSIC_LINE_WAKEUP); > + > + /* disable wake event triggers of sleepwalk logic */ > + val = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG(port)); > + val &= ~UTMIP_WAKE_VAL(port, ~0); > + val |= UTMIP_WAKE_VAL_NONE(port); > + padctl_pmc_writel(priv, val, PMC_UTMIP_UHSIC_SLEEP_CFG(port)); > + > + /* power down the line state detectors of the pad */ > + val = padctl_pmc_readl(priv, PMC_USB_AO); > + val |= (USBOP_VAL_PD(port) | USBON_VAL_PD(port)); > + padctl_pmc_writel(priv, val, PMC_USB_AO); > + > + /* save state per speed */ > + val = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SAVED_STATE(port)); > + val &= ~SPEED(port, ~0); > + if (speed == USB_SPEED_HIGH) > + val |= UTMI_HS(port); > + else if (speed == USB_SPEED_FULL) > + val |= UTMI_FS(port); > + else if (speed == USB_SPEED_LOW) > + val |= UTMI_LS(port); > + else > + val |= UTMI_RST(port); > + padctl_pmc_writel(priv, val, PMC_UTMIP_UHSIC_SAVED_STATE(port)); > + > + /* enable the trigger of the sleepwalk logic */ > + val = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEPWALK_CFG(port)); > + val |= UTMIP_LINEVAL_WALK_EN(port); > + padctl_pmc_writel(priv, val, PMC_UTMIP_UHSIC_SLEEPWALK_CFG(port)); > + > + /* reset the walk pointer and clear the alarm of the sleepwalk logic, > + * as well as capture the configuration of the USB2.0 pad > + */ > + val = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_TRIGGERS); > + val |= (UTMIP_CLR_WALK_PTR(port) | UTMIP_CLR_WAKE_ALARM(port) | > + UTMIP_CAP_CFG(port)); > + padctl_pmc_writel(priv, val, PMC_UTMIP_UHSIC_TRIGGERS); > + > + /* program electrical parameters read from XUSB PADCTL */ > + val = padctl_pmc_readl(priv, PMC_UTMIP_TERM_PAD_CFG); > + val &= ~(TCTRL_VAL(~0) | PCTRL_VAL(~0)); > + val |= (TCTRL_VAL(tctrl) | PCTRL_VAL(pctrl)); > + padctl_pmc_writel(priv, val, PMC_UTMIP_TERM_PAD_CFG); > + > + val = padctl_pmc_readl(priv, PMC_UTMIP_PAD_CFGX(port)); > + val &= ~RPD_CTRL_PX(~0); > + val |= RPD_CTRL_PX(rpd_ctrl); > + padctl_pmc_writel(priv, val, PMC_UTMIP_PAD_CFGX(port)); > + > + /* setup the pull-ups and pull-downs of the signals during the four > + * stages of sleepwalk. > + * if device is connected, program sleepwalk logic to maintain a J and > + * keep driving K upon seeing remote wake. > + */ > + val = padctl_pmc_readl(priv, PMC_UTMIP_SLEEPWALK_PX(port)); > + val = (UTMIP_USBOP_RPD_A | UTMIP_USBOP_RPD_B | UTMIP_USBOP_RPD_C | > + UTMIP_USBOP_RPD_D); > + val |= (UTMIP_USBON_RPD_A | UTMIP_USBON_RPD_B | UTMIP_USBON_RPD_C | > + UTMIP_USBON_RPD_D); > + if (speed == USB_SPEED_UNKNOWN) { > + val |= (UTMIP_HIGHZ_A | UTMIP_HIGHZ_B | UTMIP_HIGHZ_C | > + UTMIP_HIGHZ_D); > + } else if ((speed == USB_SPEED_HIGH) || (speed == USB_SPEED_FULL)) { > + /* J state: D+/D- = high/low, K state: D+/D- = low/high */ > + val |= UTMIP_HIGHZ_A; > + val |= UTMIP_AP_A; > + val |= (UTMIP_AN_B | UTMIP_AN_C | UTMIP_AN_D); > + } else if (speed == USB_SPEED_LOW) { > + /* J state: D+/D- = low/high, K state: D+/D- = high/low */ > + val |= UTMIP_HIGHZ_A; > + val |= UTMIP_AN_A; > + val |= (UTMIP_AP_B | UTMIP_AP_C | UTMIP_AP_D); > + } > + padctl_pmc_writel(priv, val, PMC_UTMIP_SLEEPWALK_PX(port)); > + > + /* power up the line state detectors of the pad */ > + val = padctl_pmc_readl(priv, PMC_USB_AO); > + val &= ~(USBOP_VAL_PD(port) | USBON_VAL_PD(port)); > + padctl_pmc_writel(priv, val, PMC_USB_AO); > + > + usleep_range(50, 100); > + > + /* switch the electric control of the USB2.0 pad to PMC */ > + val = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG(port)); > + val |= (UTMIP_FSLS_USE_PMC(port) | UTMIP_PCTRL_USE_PMC(port) | > + UTMIP_TCTRL_USE_PMC(port)); > + padctl_pmc_writel(priv, val, PMC_UTMIP_UHSIC_SLEEP_CFG(port)); > + > + val = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG1); > + val |= (UTMIP_RPD_CTRL_USE_PMC_PX(port) | > + UTMIP_RPU_SWITC_LOW_USE_PMC_PX(port)); > + padctl_pmc_writel(priv, val, PMC_UTMIP_UHSIC_SLEEP_CFG1); > + > + /* set the wake signaling trigger events */ > + val = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG(port)); > + val &= ~UTMIP_WAKE_VAL(port, ~0); > + val |= UTMIP_WAKE_VAL_ANY(port); > + padctl_pmc_writel(priv, val, PMC_UTMIP_UHSIC_SLEEP_CFG(port)); > + > + /* enable the wake detection */ > + val = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG(port)); > + val |= UTMIP_MASTER_ENABLE(port); > + padctl_pmc_writel(priv, val, PMC_UTMIP_UHSIC_SLEEP_CFG(port)); > + > + val = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_LINE_WAKEUP); > + val |= UTMIP_LINE_WAKEUP_EN(port); > + padctl_pmc_writel(priv, val, PMC_UTMIP_UHSIC_LINE_WAKEUP); > + > + return 0; > +} > + > +int tegra_pmc_utmi_disable_phy_sleepwalk(struct phy *phy) > +{ > + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); > + struct tegra_xusb_padctl *padctl = lane->pad->padctl; > + struct tegra210_xusb_padctl *priv = to_tegra210_xusb_padctl(padctl); > + struct device *dev = padctl->dev; > + unsigned int port = lane->index; > + u32 value; > + > + dev_dbg(dev, "phy disable sleepwalk usb2 %d\n", port); > + > + /* disable the wake detection */ > + value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG(port)); > + value &= ~UTMIP_MASTER_ENABLE(port); > + padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_SLEEP_CFG(port)); > + > + value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_LINE_WAKEUP); > + value &= ~UTMIP_LINE_WAKEUP_EN(port); > + padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_LINE_WAKEUP); > + > + /* switch the electric control of the USB2.0 pad to XUSB or USB2 */ > + value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG(port)); > + value &= ~(UTMIP_FSLS_USE_PMC(port) | UTMIP_PCTRL_USE_PMC(port) | > + UTMIP_TCTRL_USE_PMC(port)); > + padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_SLEEP_CFG(port)); > + > + value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG1); > + value &= ~(UTMIP_RPD_CTRL_USE_PMC_PX(port) | > + UTMIP_RPU_SWITC_LOW_USE_PMC_PX(port)); > + padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_SLEEP_CFG1); > + > + /* disable wake event triggers of sleepwalk logic */ > + value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG(port)); > + value &= ~UTMIP_WAKE_VAL(port, ~0); > + value |= UTMIP_WAKE_VAL_NONE(port); > + padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_SLEEP_CFG(port)); > + > + /* power down the line state detectors of the port */ > + value = padctl_pmc_readl(priv, PMC_USB_AO); > + value |= (USBOP_VAL_PD(port) | USBON_VAL_PD(port)); > + padctl_pmc_writel(priv, value, PMC_USB_AO); > + > + /* clear alarm of the sleepwalk logic */ > + value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_TRIGGERS); > + value |= UTMIP_CLR_WAKE_ALARM(port); > + padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_TRIGGERS); > + > + return 0; > +} > + > +int tegra_pmc_hsic_enable_phy_sleepwalk(struct phy *phy) > +{ > + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); > + struct tegra_xusb_padctl *padctl = lane->pad->padctl; > + struct tegra210_xusb_padctl *priv = to_tegra210_xusb_padctl(padctl); > + struct device *dev = padctl->dev; > + unsigned int port = lane->index; > + u32 value; > + > + dev_dbg(dev, "phy enable sleepwalk hsic %d\n", port); > + > + /* ensure sleepwalk logic is disabled */ > + value = padctl_pmc_readl(priv, PMC_UHSIC_SLEEP_CFG); > + value &= ~UHSIC_MASTER_ENABLE; > + padctl_pmc_writel(priv, value, PMC_UHSIC_SLEEP_CFG); > + > + /* ensure sleepwalk logics are in low power mode */ > + value = padctl_pmc_readl(priv, PMC_UTMIP_MASTER_CONFIG); > + value |= UHSIC_PWR(port); > + padctl_pmc_writel(priv, value, PMC_UTMIP_MASTER_CONFIG); > + > + /* set debounce time */ > + value = padctl_pmc_readl(priv, PMC_USB_DEBOUNCE_DEL); > + value &= ~UHSIC_LINE_DEB_CNT(~0); > + value |= UHSIC_LINE_DEB_CNT(0x1); > + padctl_pmc_writel(priv, value, PMC_USB_DEBOUNCE_DEL); > + > + /* ensure fake events of sleepwalk logic are desiabled */ > + value = padctl_pmc_readl(priv, PMC_UHSIC_FAKE); > + value &= ~(UHSIC_FAKE_STROBE_VAL | UHSIC_FAKE_DATA_VAL | > + UHSIC_FAKE_STROBE_EN | UHSIC_FAKE_DATA_EN); > + padctl_pmc_writel(priv, value, PMC_UHSIC_FAKE); > + > + /* ensure wake events of sleepwalk logic are not latched */ > + value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_LINE_WAKEUP); > + value &= ~UHSIC_LINE_WAKEUP_EN; > + padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_LINE_WAKEUP); > + > + /* disable wake event triggers of sleepwalk logic */ > + value = padctl_pmc_readl(priv, PMC_UHSIC_SLEEP_CFG); > + value &= ~UHSIC_WAKE_VAL(~0); > + value |= UHSIC_WAKE_VAL_NONE; > + padctl_pmc_writel(priv, value, PMC_UHSIC_SLEEP_CFG); > + > + /* power down the line state detectors of the port */ > + value = padctl_pmc_readl(priv, PMC_USB_AO); > + value |= (STROBE_VAL_PD(port) | DATA0_VAL_PD(port) | DATA1_VAL_PD); > + padctl_pmc_writel(priv, value, PMC_USB_AO); > + > + /* save state, HSIC always comes up as HS */ > + value = padctl_pmc_readl(priv, PMC_UHSIC_SAVED_STATE); > + value &= ~UHSIC_MODE(~0); > + value |= UHSIC_HS; > + padctl_pmc_writel(priv, value, PMC_UHSIC_SAVED_STATE); > + > + /* enable the trigger of the sleepwalk logic */ > + value = padctl_pmc_readl(priv, PMC_UHSIC_SLEEPWALK_CFG); > + value |= (UHSIC_WAKE_WALK_EN | UHSIC_LINEVAL_WALK_EN); > + padctl_pmc_writel(priv, value, PMC_UHSIC_SLEEPWALK_CFG); > + > + /* reset the walk pointer and clear the alarm of the sleepwalk logic, > + * as well as capture the configuration of the USB2.0 port > + */ > + value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_TRIGGERS); > + value |= (UHSIC_CLR_WALK_PTR | UHSIC_CLR_WAKE_ALARM); > + padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_TRIGGERS); > + > + /* setup the pull-ups and pull-downs of the signals during the four > + * stages of sleepwalk. > + * maintain a HSIC IDLE and keep driving HSIC RESUME upon remote wake > + */ > + value = padctl_pmc_readl(priv, PMC_UHSIC_SLEEPWALK_P0); > + value = (UHSIC_DATA0_RPD_A | UHSIC_DATA0_RPU_B | UHSIC_DATA0_RPU_C | > + UHSIC_DATA0_RPU_D); > + value |= (UHSIC_STROBE_RPU_A | UHSIC_STROBE_RPD_B | UHSIC_STROBE_RPD_C | > + UHSIC_STROBE_RPD_D); > + padctl_pmc_writel(priv, value, PMC_UHSIC_SLEEPWALK_P0); > + > + /* power up the line state detectors of the port */ > + value = padctl_pmc_readl(priv, PMC_USB_AO); > + value &= ~(STROBE_VAL_PD(port) | DATA0_VAL_PD(port) | DATA1_VAL_PD); > + padctl_pmc_writel(priv, value, PMC_USB_AO); > + > + usleep_range(50, 100); > + > + /* set the wake signaling trigger events */ > + value = padctl_pmc_readl(priv, PMC_UHSIC_SLEEP_CFG); > + value &= ~UHSIC_WAKE_VAL(~0); > + value |= UHSIC_WAKE_VAL_SD10; > + padctl_pmc_writel(priv, value, PMC_UHSIC_SLEEP_CFG); > + > + /* enable the wake detection */ > + value = padctl_pmc_readl(priv, PMC_UHSIC_SLEEP_CFG); > + value |= UHSIC_MASTER_ENABLE; > + padctl_pmc_writel(priv, value, PMC_UHSIC_SLEEP_CFG); > + > + value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_LINE_WAKEUP); > + value |= UHSIC_LINE_WAKEUP_EN; > + padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_LINE_WAKEUP); > + > + return 0; > +} > + > +int tegra_pmc_hsic_disable_phy_sleepwalk(struct phy *phy) > +{ > + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); > + struct tegra_xusb_padctl *padctl = lane->pad->padctl; > + struct tegra210_xusb_padctl *priv = to_tegra210_xusb_padctl(padctl); > + struct device *dev = padctl->dev; > + unsigned int port = lane->index; > + u32 value; > + > + dev_dbg(dev, "phy disable sleepwalk hsic %d\n", port); > + > + /* disable the wake detection */ > + value = padctl_pmc_readl(priv, PMC_UHSIC_SLEEP_CFG); > + value &= ~UHSIC_MASTER_ENABLE; > + padctl_pmc_writel(priv, value, PMC_UHSIC_SLEEP_CFG); > + > + value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_LINE_WAKEUP); > + value &= ~UHSIC_LINE_WAKEUP_EN; > + padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_LINE_WAKEUP); > + > + /* disable wake event triggers of sleepwalk logic */ > + value = padctl_pmc_readl(priv, PMC_UHSIC_SLEEP_CFG); > + value &= ~UHSIC_WAKE_VAL(~0); > + value |= UHSIC_WAKE_VAL_NONE; > + padctl_pmc_writel(priv, value, PMC_UHSIC_SLEEP_CFG); > + > + /* power down the line state detectors of the port */ > + value = padctl_pmc_readl(priv, PMC_USB_AO); > + value |= (STROBE_VAL_PD(port) | DATA0_VAL_PD(port) | DATA1_VAL_PD); > + padctl_pmc_writel(priv, value, PMC_USB_AO); > + > + /* clear alarm of the sleepwalk logic */ > + value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_TRIGGERS); > + value |= UHSIC_CLR_WAKE_ALARM; > + padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_TRIGGERS); > + > + return 0; > +} > + > static int tegra210_usb3_set_lfps_detect(struct tegra_xusb_padctl *padctl, > unsigned int index, bool enable) > { > @@ -988,8 +1818,23 @@ static int tegra210_usb2_phy_init(struct phy *phy) > { > struct tegra_xusb_lane *lane = phy_get_drvdata(phy); > struct tegra_xusb_padctl *padctl = lane->pad->padctl; > + unsigned int index = lane->index; > + struct tegra_xusb_usb2_port *port; > + int err; > u32 value; > > + port = tegra_xusb_find_usb2_port(padctl, index); > + if (!port) { > + dev_err(&phy->dev, "no port found for USB2 lane %u\n", index); > + return -ENODEV; > + } > + > + err = regulator_enable(port->supply); > + if (err) > + return err; > + > + mutex_lock(&padctl->lock); > + > value = padctl_readl(padctl, XUSB_PADCTL_USB2_PAD_MUX); > value &= ~(XUSB_PADCTL_USB2_PAD_MUX_USB2_BIAS_PAD_MASK << > XUSB_PADCTL_USB2_PAD_MUX_USB2_BIAS_PAD_SHIFT); > @@ -997,11 +1842,29 @@ static int tegra210_usb2_phy_init(struct phy *phy) > XUSB_PADCTL_USB2_PAD_MUX_USB2_BIAS_PAD_SHIFT; > padctl_writel(padctl, value, XUSB_PADCTL_USB2_PAD_MUX); > > + mutex_unlock(&padctl->lock); > + > return 0; > } How is this related to sleepwalk? Should this perhaps be a separate patch? Looks like some hunks below are also not immediately related to this commit. Or perhaps I don't understand how they are related. > > static int tegra210_usb2_phy_exit(struct phy *phy) > { > + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); > + struct tegra_xusb_padctl *padctl = lane->pad->padctl; > + unsigned int index = lane->index; > + struct tegra_xusb_usb2_port *port; > + int err; > + > + port = tegra_xusb_find_usb2_port(padctl, index); > + if (!port) { > + dev_err(&phy->dev, "no port found for USB2 lane %u\n", index); > + return -ENODEV; > + } > + > + err = regulator_disable(port->supply); > + if (err) > + return err; > + > return 0; > } > > @@ -1122,6 +1985,8 @@ static int tegra210_usb2_phy_power_on(struct phy *phy) > > priv = to_tegra210_xusb_padctl(padctl); > > + mutex_lock(&padctl->lock); > + > if (port->usb3_port_fake != -1) { > value = padctl_readl(padctl, XUSB_PADCTL_SS_PORT_MAP); > value &= ~XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP_MASK( > @@ -1215,14 +2080,6 @@ static int tegra210_usb2_phy_power_on(struct phy *phy) > padctl_writel(padctl, value, > XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPADX_CTL1(index)); > > - if (port->supply && port->mode == USB_DR_MODE_HOST) { > - err = regulator_enable(port->supply); > - if (err) > - return err; > - } > - > - mutex_lock(&padctl->lock); > - > if (pad->enable > 0) { > pad->enable++; > mutex_unlock(&padctl->lock); > @@ -1231,7 +2088,7 @@ static int tegra210_usb2_phy_power_on(struct phy *phy) > > err = clk_prepare_enable(pad->clk); > if (err) > - goto disable_regulator; > + goto out; > > value = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL1); > value &= ~((XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_MASK << > @@ -1263,8 +2120,7 @@ static int tegra210_usb2_phy_power_on(struct phy *phy) > > return 0; > > -disable_regulator: > - regulator_disable(port->supply); > +out: > mutex_unlock(&padctl->lock); > return err; > } > @@ -1275,12 +2131,12 @@ static int tegra210_usb2_phy_power_off(struct phy *phy) > struct tegra_xusb_usb2_pad *pad = to_usb2_pad(lane->pad); > struct tegra_xusb_padctl *padctl = lane->pad->padctl; > struct tegra_xusb_usb2_port *port; > + unsigned int index = lane->index; > u32 value; > > - port = tegra_xusb_find_usb2_port(padctl, lane->index); > + port = tegra_xusb_find_usb2_port(padctl, index); > if (!port) { > - dev_err(&phy->dev, "no port found for USB2 lane %u\n", > - lane->index); > + dev_err(&phy->dev, "no port found for USB2 lane %u\n", index); > return -ENODEV; > } > > @@ -1318,12 +2174,19 @@ static int tegra210_usb2_phy_power_off(struct phy *phy) > if (--pad->enable > 0) > goto out; > > + value = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL0(index)); > + value |= XUSB_PADCTL_USB2_OTG_PAD_CTL0_PD; > + padctl_writel(padctl, value, XUSB_PADCTL_USB2_OTG_PADX_CTL0(index)); > + > + value = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL1(index)); > + value |= XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_DR; > + padctl_writel(padctl, value, XUSB_PADCTL_USB2_OTG_PADX_CTL1(index)); > + > value = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL0); > value |= XUSB_PADCTL_USB2_BIAS_PAD_CTL0_PD; > padctl_writel(padctl, value, XUSB_PADCTL_USB2_BIAS_PAD_CTL0); > > out: > - regulator_disable(port->supply); > mutex_unlock(&padctl->lock); > return 0; > } > @@ -2120,6 +2983,120 @@ static const struct phy_ops tegra210_sata_phy_ops = { > .owner = THIS_MODULE, > }; > > +static inline bool is_usb3_phy(struct phy *phy) > +{ > + return (phy->ops == &tegra210_pcie_phy_ops || > + phy->ops == &tegra210_sata_phy_ops); > +} > + > +static inline bool is_hsic_phy(struct phy *phy) > +{ > + return phy->ops == &tegra210_hsic_phy_ops; > +} > + > +static inline bool is_utmi_phy(struct phy *phy) > +{ > + return phy->ops == &tegra210_usb2_phy_ops; > +} > + > +static int > +tegra210_xusb_padctl_enable_phy_wake(struct tegra_xusb_padctl *padctl, > + struct phy *phy) > +{ > + if (!phy) > + return 0; > + > + if (is_usb3_phy(phy)) > + return tegra210_usb3_enable_phy_wake(phy); > + else if (is_utmi_phy(phy)) > + return tegra210_utmi_enable_phy_wake(phy); > + else if (is_hsic_phy(phy)) > + return tegra210_hsic_enable_phy_wake(phy); > + else > + return -EINVAL; > +} Since all of these branches return, you don't need the else and can write this more simply as: if (is_usb3_phy(phy)) return ...; if (is_utmi_phy(phy)) return ...; if (is_hsic_phy(phy)) return ...; return -EINVAL; I'd probably also leave out the check for !phy since that should never happen and it might be good to just let it crash in that case to make sure that bug is found as quickly as possible. > + > +static int > +tegra210_xusb_padctl_disable_phy_wake(struct tegra_xusb_padctl *padctl, > + struct phy *phy) > +{ > + if (!phy) > + return 0; > + > + if (is_usb3_phy(phy)) > + return tegra210_usb3_disable_phy_wake(phy); > + else if (is_utmi_phy(phy)) > + return tegra210_utmi_disable_phy_wake(phy); > + else if (is_hsic_phy(phy)) > + return tegra210_hsic_disable_phy_wake(phy); > + else > + return -EINVAL; > +} > + > +int tegra210_xusb_padctl_remote_wake_detected(struct phy *phy) > +{ > + struct tegra_xusb_lane *lane; > + struct tegra_xusb_padctl *padctl; > + > + if (!phy) > + return 0; > + > + lane = phy_get_drvdata(phy); > + padctl = lane->pad->padctl; > + > + if (is_utmi_phy(phy)) > + return tegra210_utmi_phy_remote_wake_detected(padctl, > + lane->index); > + else if (is_hsic_phy(phy)) > + return tegra210_hsic_phy_remote_wake_detected(padctl, > + lane->index); > + else if (is_usb3_phy(phy)) > + return tegra210_usb3_phy_remote_wake_detected(padctl, > + tegra210_usb3_lane_map(lane)); > + > + return -EINVAL; > +} > + > +static int > +tegra210_xusb_padctl_enable_phy_sleepwalk(struct tegra_xusb_padctl *padctl, > + struct phy *phy, > + enum usb_device_speed speed) > +{ > + if (!phy) > + return 0; > + > + if (is_usb3_phy(phy)) > + return tegra210_usb3_enable_phy_sleepwalk(phy); > + else if (is_utmi_phy(phy)) > + return tegra_pmc_utmi_enable_phy_sleepwalk(phy, speed); > + else if (is_hsic_phy(phy)) > + return tegra_pmc_hsic_enable_phy_sleepwalk(phy); > + else > + return -EINVAL; > + > + return 0; > +} > + > +static int > +tegra210_xusb_padctl_disable_phy_sleepwalk(struct tegra_xusb_padctl *padctl, > + struct phy *phy) > +{ > + if (!phy) > + return 0; > + > + if (is_usb3_phy(phy)) > + return tegra210_usb3_disable_phy_sleepwalk(phy); > + else if (is_utmi_phy(phy)) > + return tegra_pmc_utmi_disable_phy_sleepwalk(phy); > + else if (is_hsic_phy(phy)) > + return tegra_pmc_hsic_disable_phy_sleepwalk(phy); > + else > + return -EINVAL; > + > + return 0; > +} > + > + > static struct tegra_xusb_pad * > tegra210_sata_pad_probe(struct tegra_xusb_padctl *padctl, > const struct tegra_xusb_pad_soc *soc, > @@ -2317,6 +3294,8 @@ tegra210_xusb_padctl_probe(struct device *dev, > const struct tegra_xusb_padctl_soc *soc) > { > struct tegra210_xusb_padctl *padctl; > + struct device_node *node, *np = dev->of_node; > + struct platform_device *pmc_dev; > int err; > > padctl = devm_kzalloc(dev, sizeof(*padctl), GFP_KERNEL); > @@ -2330,6 +3309,24 @@ tegra210_xusb_padctl_probe(struct device *dev, > if (err < 0) > return ERR_PTR(err); > > + node = of_parse_phandle(np, "nvidia,pmc", 0); > + if (!node) { > + dev_err(dev, "nvidia,pmc property is missing\n"); > + return ERR_PTR(-ENODEV); > + } > + > + pmc_dev = of_find_device_by_node(node); > + if (!pmc_dev) { > + dev_err(dev, "pmc device is not available\n"); > + return ERR_PTR(-ENODEV); > + } > + > + padctl->pmc_reg = dev_get_regmap(&pmc_dev->dev, "usb_sleepwalk"); > + if (!padctl->pmc_reg) { > + dev_err(dev, "pmc regmap is not available.\n"); > + return ERR_PTR(-ENODEV); > + } We'll have to make this optional for backwards compatibility, which will also help make this easier to merge because it doesn't all have to go in at the same time. Thierry > + > return &padctl->base; > } > > @@ -2337,13 +3334,80 @@ static void tegra210_xusb_padctl_remove(struct tegra_xusb_padctl *padctl) > { > } > > +static void tegra210_xusb_padctl_save(struct tegra_xusb_padctl *padctl) > +{ > + struct tegra210_xusb_padctl *priv = to_tegra210_xusb_padctl(padctl); > + > + priv->context.usb2_pad_mux = > + padctl_readl(padctl, XUSB_PADCTL_USB2_PAD_MUX); > + priv->context.usb2_port_cap = > + padctl_readl(padctl, XUSB_PADCTL_USB2_PORT_CAP); > + priv->context.ss_port_map = > + padctl_readl(padctl, XUSB_PADCTL_SS_PORT_MAP); > + priv->context.usb3_pad_mux = > + padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX); > +} > + > +static void tegra210_xusb_padctl_restore(struct tegra_xusb_padctl *padctl) > +{ > + struct tegra210_xusb_padctl *priv = to_tegra210_xusb_padctl(padctl); > + int i; > + > + padctl_writel(padctl, priv->context.usb2_pad_mux, > + XUSB_PADCTL_USB2_PAD_MUX); > + padctl_writel(padctl, priv->context.usb2_port_cap, > + XUSB_PADCTL_USB2_PORT_CAP); > + padctl_writel(padctl, priv->context.ss_port_map, > + XUSB_PADCTL_SS_PORT_MAP); > + > + for (i = 0; i <= 7; i ++) > + tegra210_uphy_lane_iddq_enable(padctl, i); > + > + padctl_writel(padctl, priv->context.usb3_pad_mux, > + XUSB_PADCTL_USB3_PAD_MUX); > + > + for (i = 0; i <= 7; i ++) > + tegra210_uphy_lane_iddq_disable(padctl, i); > +} > + > +static int tegra210_xusb_padctl_suspend_noirq(struct tegra_xusb_padctl *padctl) > +{ > + mutex_lock(&padctl->lock); > + > + tegra210_uphy_deinit(padctl); > + > + tegra210_xusb_padctl_save(padctl); > + > + mutex_unlock(&padctl->lock); > + return 0; > +} > + > +static int tegra210_xusb_padctl_resume_noirq(struct tegra_xusb_padctl *padctl) > +{ > + mutex_lock(&padctl->lock); > + > + tegra210_xusb_padctl_restore(padctl); > + > + tegra210_uphy_init(padctl); > + > + mutex_unlock(&padctl->lock); > + return 0; > +} > + > static const struct tegra_xusb_padctl_ops tegra210_xusb_padctl_ops = { > .probe = tegra210_xusb_padctl_probe, > .remove = tegra210_xusb_padctl_remove, > + .suspend_noirq = tegra210_xusb_padctl_suspend_noirq, > + .resume_noirq = tegra210_xusb_padctl_resume_noirq, > .usb3_set_lfps_detect = tegra210_usb3_set_lfps_detect, > .hsic_set_idle = tegra210_hsic_set_idle, > .vbus_override = tegra210_xusb_padctl_vbus_override, > .utmi_port_reset = tegra210_utmi_port_reset, > + .enable_phy_sleepwalk = tegra210_xusb_padctl_enable_phy_sleepwalk, > + .disable_phy_sleepwalk = tegra210_xusb_padctl_disable_phy_sleepwalk, > + .enable_phy_wake = tegra210_xusb_padctl_enable_phy_wake, > + .disable_phy_wake = tegra210_xusb_padctl_disable_phy_wake, > + .remote_wake_detected = tegra210_xusb_padctl_remote_wake_detected, > }; > > static const char * const tegra210_xusb_padctl_supply_names[] = { > -- > 2.25.1 >
diff --git a/drivers/phy/tegra/xusb-tegra210.c b/drivers/phy/tegra/xusb-tegra210.c index fe1ab440424d..1c03f4ec4b59 100644 --- a/drivers/phy/tegra/xusb-tegra210.c +++ b/drivers/phy/tegra/xusb-tegra210.c @@ -16,6 +16,8 @@ #include <linux/regulator/consumer.h> #include <linux/reset.h> #include <linux/slab.h> +#include <linux/regmap.h> +#include <linux/of_platform.h> #include <soc/tegra/fuse.h> @@ -52,6 +54,20 @@ #define XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP(x, v) (((v) & 0x7) << ((x) * 5)) #define XUSB_PADCTL_SS_PORT_MAP_PORT_DISABLED 0x7 +#define XUSB_PADCTL_ELPG_PROGRAM_0 0x20 +#define USB2_PORT_WAKE_INTERRUPT_ENABLE(x) BIT((x)) +#define USB2_PORT_WAKEUP_EVENT(x) BIT((x) + 7) +#define SS_PORT_WAKE_INTERRUPT_ENABLE(x) BIT((x) + 14) +#define SS_PORT_WAKEUP_EVENT(x) BIT((x) + 21) +#define USB2_HSIC_PORT_WAKE_INTERRUPT_ENABLE(x) BIT((x) + 28) +#define USB2_HSIC_PORT_WAKEUP_EVENT(x) BIT((x) + 30) +#define ALL_WAKE_EVENTS ( \ + USB2_PORT_WAKEUP_EVENT(0) | USB2_PORT_WAKEUP_EVENT(1) | \ + USB2_PORT_WAKEUP_EVENT(2) | USB2_PORT_WAKEUP_EVENT(3) | \ + SS_PORT_WAKEUP_EVENT(0) | SS_PORT_WAKEUP_EVENT(1) | \ + SS_PORT_WAKEUP_EVENT(2) | SS_PORT_WAKEUP_EVENT(3) | \ + USB2_HSIC_PORT_WAKEUP_EVENT(0)) + #define XUSB_PADCTL_ELPG_PROGRAM1 0x024 #define XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_VCORE_DOWN (1 << 31) #define XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_CLAMP_EN_EARLY (1 << 30) @@ -90,6 +106,8 @@ #define XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_DR (1 << 2) #define XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_DISC_OVRD (1 << 1) #define XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_CHRP_OVRD (1 << 0) +#define RPD_CTRL(x) (((x) & 0x1f) << 26) +#define RPD_CTRL_VALUE(x) (((x) >> 26) & 0x1f) #define XUSB_PADCTL_USB2_BIAS_PAD_CTL0 0x284 #define XUSB_PADCTL_USB2_BIAS_PAD_CTL0_PD (1 << 11) @@ -108,6 +126,8 @@ #define XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_SHIFT 12 #define XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_MASK 0x7f #define XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_VAL 0x1e +#define TCTRL_VALUE(x) (((x) & 0x3f) >> 0) +#define PCTRL_VALUE(x) (((x) >> 6) & 0x3f) #define XUSB_PADCTL_HSIC_PADX_CTL0(x) (0x300 + (x) * 0x20) #define XUSB_PADCTL_HSIC_PAD_CTL0_RPU_STROBE (1 << 18) @@ -251,16 +271,161 @@ #define XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_FLOATING 8 #define XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_GROUNDED 0 +/* USB2 SLEEPWALK registers */ +#define UTMIP(_port, _offset1, _offset2) \ + (((_port) <= 2) ? (_offset1) : (_offset2)) + +#define PMC_UTMIP_UHSIC_SLEEP_CFG(x) UTMIP(x, 0x1fc, 0x4d0) +#define UTMIP_MASTER_ENABLE(x) UTMIP(x, BIT(8 * (x)), BIT(0)) +#define UTMIP_FSLS_USE_PMC(x) UTMIP(x, BIT(8 * (x) + 1), \ + BIT(1)) +#define UTMIP_PCTRL_USE_PMC(x) UTMIP(x, BIT(8 * (x) + 2), \ + BIT(2)) +#define UTMIP_TCTRL_USE_PMC(x) UTMIP(x, BIT(8 * (x) + 3), \ + BIT(3)) +#define UTMIP_WAKE_VAL(_port, _value) (((_value) & 0xf) << \ + (UTMIP(_port, 8 * (_port) + 4, 4))) +#define UTMIP_WAKE_VAL_NONE(_port) UTMIP_WAKE_VAL(_port, 12) +#define UTMIP_WAKE_VAL_ANY(_port) UTMIP_WAKE_VAL(_port, 15) + +#define PMC_UTMIP_UHSIC_SLEEP_CFG1 (0x4d0) +#define UTMIP_RPU_SWITC_LOW_USE_PMC_PX(x) BIT((x) + 8) +#define UTMIP_RPD_CTRL_USE_PMC_PX(x) BIT((x) + 16) + +#define PMC_UTMIP_MASTER_CONFIG (0x274) +#define UTMIP_PWR(x) UTMIP(x, BIT(x), BIT(4)) +#define UHSIC_PWR(x) BIT(3) + +#define PMC_USB_DEBOUNCE_DEL (0xec) +#define DEBOUNCE_VAL(x) (((x) & 0xffff) << 0) +#define UTMIP_LINE_DEB_CNT(x) (((x) & 0xf) << 16) +#define UHSIC_LINE_DEB_CNT(x) (((x) & 0xf) << 20) + +#define PMC_UTMIP_UHSIC_FAKE(x) UTMIP(x, 0x218, 0x294) +#define UTMIP_FAKE_USBOP_VAL(x) UTMIP(x, BIT(4 * (x)), BIT(8)) +#define UTMIP_FAKE_USBON_VAL(x) UTMIP(x, BIT(4 * (x) + 1), \ + BIT(9)) +#define UTMIP_FAKE_USBOP_EN(x) UTMIP(x, BIT(4 * (x) + 2), \ + BIT(10)) +#define UTMIP_FAKE_USBON_EN(x) UTMIP(x, BIT(4 * (x) + 3), \ + BIT(11)) + +#define PMC_UTMIP_UHSIC_SLEEPWALK_CFG(x) UTMIP(x, 0x200, 0x288) +#define UTMIP_LINEVAL_WALK_EN(x) UTMIP(x, BIT(8 * (x) + 7), \ + BIT(15)) + +#define PMC_USB_AO (0xf0) +#define USBOP_VAL_PD(x) UTMIP(x, BIT(4 * (x)), BIT(20)) +#define USBON_VAL_PD(x) UTMIP(x, BIT(4 * (x) + 1), \ + BIT(21)) +#define STROBE_VAL_PD(x) BIT(12) +#define DATA0_VAL_PD(x) BIT(13) +#define DATA1_VAL_PD BIT(24) + +#define PMC_UTMIP_UHSIC_SAVED_STATE(x) UTMIP(x, 0x1f0, 0x280) +#define SPEED(_port, _value) (((_value) & 0x3) << \ + (UTMIP(_port, 8 * (_port), 8))) +#define UTMI_HS(_port) SPEED(_port, 0) +#define UTMI_FS(_port) SPEED(_port, 1) +#define UTMI_LS(_port) SPEED(_port, 2) +#define UTMI_RST(_port) SPEED(_port, 3) + +#define PMC_UTMIP_UHSIC_TRIGGERS (0x1ec) +#define UTMIP_CLR_WALK_PTR(x) UTMIP(x, BIT(x), BIT(16)) +#define UTMIP_CAP_CFG(x) UTMIP(x, BIT((x) + 4), BIT(17)) +#define UTMIP_CLR_WAKE_ALARM(x) UTMIP(x, BIT((x) + 12), \ + BIT(19)) +#define UHSIC_CLR_WALK_PTR BIT(3) +#define UHSIC_CLR_WAKE_ALARM BIT(15) + +#define PMC_UTMIP_SLEEPWALK_PX(x) UTMIP(x, 0x204 + (4 * (x)), \ + 0x4e0) +/* phase A */ +#define UTMIP_USBOP_RPD_A BIT(0) +#define UTMIP_USBON_RPD_A BIT(1) +#define UTMIP_AP_A BIT(4) +#define UTMIP_AN_A BIT(5) +#define UTMIP_HIGHZ_A BIT(6) +/* phase B */ +#define UTMIP_USBOP_RPD_B BIT(8) +#define UTMIP_USBON_RPD_B BIT(9) +#define UTMIP_AP_B BIT(12) +#define UTMIP_AN_B BIT(13) +#define UTMIP_HIGHZ_B BIT(14) +/* phase C */ +#define UTMIP_USBOP_RPD_C BIT(16) +#define UTMIP_USBON_RPD_C BIT(17) +#define UTMIP_AP_C BIT(20) +#define UTMIP_AN_C BIT(21) +#define UTMIP_HIGHZ_C BIT(22) +/* phase D */ +#define UTMIP_USBOP_RPD_D BIT(24) +#define UTMIP_USBON_RPD_D BIT(25) +#define UTMIP_AP_D BIT(28) +#define UTMIP_AN_D BIT(29) +#define UTMIP_HIGHZ_D BIT(30) + +#define PMC_UTMIP_UHSIC_LINE_WAKEUP (0x26c) +#define UTMIP_LINE_WAKEUP_EN(x) UTMIP(x, BIT(x), BIT(4)) +#define UHSIC_LINE_WAKEUP_EN BIT(3) + +#define PMC_UTMIP_TERM_PAD_CFG (0x1f8) +#define PCTRL_VAL(x) (((x) & 0x3f) << 1) +#define TCTRL_VAL(x) (((x) & 0x3f) << 7) + +#define PMC_UTMIP_PAD_CFGX(x) (0x4c0 + (4 * (x))) +#define RPD_CTRL_PX(x) (((x) & 0x1f) << 22) + +#define PMC_UHSIC_SLEEP_CFG PMC_UTMIP_UHSIC_SLEEP_CFG(0) +#define UHSIC_MASTER_ENABLE BIT(24) +#define UHSIC_WAKE_VAL(_value) (((_value) & 0xf) << 28) +#define UHSIC_WAKE_VAL_SD10 UHSIC_WAKE_VAL(2) +#define UHSIC_WAKE_VAL_NONE UHSIC_WAKE_VAL(12) + +#define PMC_UHSIC_FAKE PMC_UTMIP_UHSIC_FAKE(0) +#define UHSIC_FAKE_STROBE_VAL BIT(12) +#define UHSIC_FAKE_DATA_VAL BIT(13) +#define UHSIC_FAKE_STROBE_EN BIT(14) +#define UHSIC_FAKE_DATA_EN BIT(15) + +#define PMC_UHSIC_SAVED_STATE PMC_UTMIP_UHSIC_SAVED_STATE(0) +#define UHSIC_MODE(_value) (((_value) & 0x1) << 24) +#define UHSIC_HS UHSIC_MODE(0) +#define UHSIC_RST UHSIC_MODE(1) + +#define PMC_UHSIC_SLEEPWALK_CFG PMC_UTMIP_UHSIC_SLEEPWALK_CFG(0) +#define UHSIC_WAKE_WALK_EN BIT(30) +#define UHSIC_LINEVAL_WALK_EN BIT(31) + +#define PMC_UHSIC_SLEEPWALK_P0 (0x210) +#define UHSIC_DATA0_RPD_A BIT(1) +#define UHSIC_DATA0_RPU_B BIT(11) +#define UHSIC_DATA0_RPU_C BIT(19) +#define UHSIC_DATA0_RPU_D BIT(27) +#define UHSIC_STROBE_RPU_A BIT(2) +#define UHSIC_STROBE_RPD_B BIT(8) +#define UHSIC_STROBE_RPD_C BIT(16) +#define UHSIC_STROBE_RPD_D BIT(24) + struct tegra210_xusb_fuse_calibration { u32 hs_curr_level[4]; u32 hs_term_range_adj; u32 rpd_ctrl; }; +struct tegra210_xusb_padctl_context { + u32 usb2_pad_mux; + u32 usb2_port_cap; + u32 ss_port_map; + u32 usb3_pad_mux; +}; + struct tegra210_xusb_padctl { struct tegra_xusb_padctl base; struct tegra210_xusb_fuse_calibration fuse; + struct tegra210_xusb_padctl_context context; + struct regmap *pmc_reg; }; static inline struct tegra210_xusb_padctl * @@ -886,6 +1051,671 @@ static int tegra210_hsic_set_idle(struct tegra_xusb_padctl *padctl, return 0; } +static int tegra210_usb3_enable_phy_sleepwalk(struct phy *phy) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + int port = tegra210_usb3_lane_map(lane); + struct device *dev = padctl->dev; + u32 value; + + if (port < 0) { + dev_err(dev, "invalid usb3 port number\n"); + return -EINVAL; + } + + dev_dbg(dev, "phy enable sleepwalk usb3 %d\n", port); + + mutex_lock(&padctl->lock); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1); + value |= XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN_EARLY(port); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1); + + usleep_range(100, 200); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1); + value |= XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN(port); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1); + + usleep_range(250, 350); + + mutex_unlock(&padctl->lock); + + return 0; +} + +static int tegra210_usb3_disable_phy_sleepwalk(struct phy *phy) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + int port = tegra210_usb3_lane_map(lane); + struct device *dev = padctl->dev; + u32 value; + + if (port < 0) { + dev_err(dev, "invalid usb3 port number\n"); + return -EINVAL; + } + + dev_dbg(dev, "phy disable sleepwalk usb3 %d\n", port); + + mutex_lock(&padctl->lock); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1); + value &= ~XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN_EARLY(port); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1); + + usleep_range(100, 200); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1); + value &= ~XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN(port); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1); + + mutex_unlock(&padctl->lock); + + return 0; +} + +static int tegra210_usb3_enable_phy_wake(struct phy *phy) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + int port = tegra210_usb3_lane_map(lane); + struct device *dev = padctl->dev; + u32 value; + + if (port < 0) { + dev_err(dev, "invalid usb3 port number\n"); + return -EINVAL; + } + + dev_dbg(dev, "phy enable wake usb3 %d\n", port); + + mutex_lock(&padctl->lock); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0); + value &= ~ALL_WAKE_EVENTS; + value |= SS_PORT_WAKEUP_EVENT(port); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_0); + + usleep_range(10, 20); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0); + value &= ~ALL_WAKE_EVENTS; + value |= SS_PORT_WAKE_INTERRUPT_ENABLE(port); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_0); + + mutex_unlock(&padctl->lock); + + return 0; +} + +static int tegra210_usb3_disable_phy_wake(struct phy *phy) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + int port = tegra210_usb3_lane_map(lane); + struct device *dev = padctl->dev; + u32 value; + + if (port < 0) { + dev_err(dev, "invalid usb3 port number\n"); + return -EINVAL; + } + + dev_dbg(dev, "phy disable wake usb3 %d\n", port); + + mutex_lock(&padctl->lock); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0); + value &= ~ALL_WAKE_EVENTS; + value &= ~SS_PORT_WAKE_INTERRUPT_ENABLE(port); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_0); + + usleep_range(10, 20); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0); + value &= ~ALL_WAKE_EVENTS; + value |= SS_PORT_WAKEUP_EVENT(port); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_0); + + mutex_unlock(&padctl->lock); + + return 0; +} + +static int tegra210_utmi_enable_phy_wake(struct phy *phy) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + unsigned int index = lane->index; + struct device *dev = padctl->dev; + u32 value; + + dev_dbg(dev, "phy enable wake on usb2 %d\n", index); + + mutex_lock(&padctl->lock); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0); + value &= ~ALL_WAKE_EVENTS; + value |= USB2_PORT_WAKEUP_EVENT(index); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_0); + + usleep_range(10, 20); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0); + value &= ~ALL_WAKE_EVENTS; + value |= USB2_PORT_WAKE_INTERRUPT_ENABLE(index); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_0); + + mutex_unlock(&padctl->lock); + + return 0; +} + +static int tegra210_utmi_disable_phy_wake(struct phy *phy) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + unsigned int index = lane->index; + struct device *dev = padctl->dev; + u32 value; + + dev_dbg(dev, "phy disable wake on usb2 %d\n", index); + + mutex_lock(&padctl->lock); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0); + value &= ~ALL_WAKE_EVENTS; + value &= ~USB2_PORT_WAKE_INTERRUPT_ENABLE(index); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_0); + + usleep_range(10, 20); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0); + value &= ~ALL_WAKE_EVENTS; + value |= USB2_PORT_WAKEUP_EVENT(index); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_0); + + mutex_unlock(&padctl->lock); + + return 0; +} + +static int tegra210_hsic_enable_phy_wake(struct phy *phy) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + unsigned int index = lane->index; + struct device *dev = padctl->dev; + u32 value; + + dev_dbg(dev, "phy enable wake on hsic %d\n", index); + + mutex_lock(&padctl->lock); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0); + value &= ~ALL_WAKE_EVENTS; + value |= USB2_HSIC_PORT_WAKEUP_EVENT(index); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_0); + + usleep_range(10, 20); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0); + value &= ~ALL_WAKE_EVENTS; + value |= USB2_HSIC_PORT_WAKE_INTERRUPT_ENABLE(index); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_0); + + mutex_unlock(&padctl->lock); + + return 0; +} + +static int tegra210_hsic_disable_phy_wake(struct phy *phy) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + unsigned int index = lane->index; + struct device *dev = padctl->dev; + u32 value; + + dev_dbg(dev, "phy disable wake on hsic %d\n", index); + + mutex_lock(&padctl->lock); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0); + value &= ~ALL_WAKE_EVENTS; + value &= ~USB2_HSIC_PORT_WAKE_INTERRUPT_ENABLE(index); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_0); + + usleep_range(10, 20); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0); + value &= ~ALL_WAKE_EVENTS; + value |= USB2_HSIC_PORT_WAKEUP_EVENT(index); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_0); + + mutex_unlock(&padctl->lock); + + return 0; +} + +static int tegra210_usb3_phy_remote_wake_detected( + struct tegra_xusb_padctl *padctl, int port) +{ + u32 value; + + if (port < 0) { + dev_err(padctl->dev, "invalid usb3 port number %d\n", + port); + return false; + } + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0); + if ((value & SS_PORT_WAKE_INTERRUPT_ENABLE(port)) && + (value & SS_PORT_WAKEUP_EVENT(port))) + return true; + else + return false; +} + +static int tegra210_utmi_phy_remote_wake_detected( + struct tegra_xusb_padctl *padctl, int port) +{ + u32 value; + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0); + if ((value & USB2_PORT_WAKE_INTERRUPT_ENABLE(port)) && + (value & USB2_PORT_WAKEUP_EVENT(port))) + return true; + else + return false; +} + +static int tegra210_hsic_phy_remote_wake_detected( + struct tegra_xusb_padctl *padctl, int port) +{ + u32 value; + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_0); + if ((value & USB2_HSIC_PORT_WAKE_INTERRUPT_ENABLE(port)) && + (value & USB2_HSIC_PORT_WAKEUP_EVENT(port))) + return true; + else + return false; +} + +#define padctl_pmc_readl(_priv, _offset) \ +({ \ + int rc; \ + u32 val; \ + rc = regmap_read(_priv->pmc_reg, _offset, &val); \ + if (rc) \ + return rc; \ + val; \ +}) + +#define padctl_pmc_writel(_priv, _val, _offset) \ +do { \ + int rc; \ + rc = regmap_write(_priv->pmc_reg, _offset, _val); \ + if (rc) \ + return rc; \ +} while (0) + +/* T210 USB2 SLEEPWALK APIs */ +int tegra_pmc_utmi_enable_phy_sleepwalk(struct phy *phy, + enum usb_device_speed speed) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + struct tegra210_xusb_padctl *priv = to_tegra210_xusb_padctl(padctl); + struct device *dev = padctl->dev; + unsigned int port = lane->index; + u32 val, tctrl, pctrl, rpd_ctrl; + + if (speed > USB_SPEED_HIGH) + return -EINVAL; + + dev_dbg(dev, "phy enable sleepwalk usb2 %d speed %d\n", port, speed); + + val = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL1); + tctrl = TCTRL_VALUE(val); + pctrl = PCTRL_VALUE(val); + + val = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL1(port)); + rpd_ctrl = RPD_CTRL_VALUE(val); + + /* ensure sleepwalk logic is disabled */ + val = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG(port)); + val &= ~UTMIP_MASTER_ENABLE(port); + padctl_pmc_writel(priv, val, PMC_UTMIP_UHSIC_SLEEP_CFG(port)); + + /* ensure sleepwalk logics are in low power mode */ + val = padctl_pmc_readl(priv, PMC_UTMIP_MASTER_CONFIG); + val |= UTMIP_PWR(port); + padctl_pmc_writel(priv, val, PMC_UTMIP_MASTER_CONFIG); + + /* set debounce time */ + val = padctl_pmc_readl(priv, PMC_USB_DEBOUNCE_DEL); + val &= ~UTMIP_LINE_DEB_CNT(~0); + val |= UTMIP_LINE_DEB_CNT(0x1); + padctl_pmc_writel(priv, val, PMC_USB_DEBOUNCE_DEL); + + /* ensure fake events of sleepwalk logic are desiabled */ + val = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_FAKE(port)); + val &= ~(UTMIP_FAKE_USBOP_VAL(port) | UTMIP_FAKE_USBON_VAL(port) | + UTMIP_FAKE_USBOP_EN(port) | UTMIP_FAKE_USBON_EN(port)); + padctl_pmc_writel(priv, val, PMC_UTMIP_UHSIC_FAKE(port)); + + /* ensure wake events of sleepwalk logic are not latched */ + val = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_LINE_WAKEUP); + val &= ~UTMIP_LINE_WAKEUP_EN(port); + padctl_pmc_writel(priv, val, PMC_UTMIP_UHSIC_LINE_WAKEUP); + + /* disable wake event triggers of sleepwalk logic */ + val = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG(port)); + val &= ~UTMIP_WAKE_VAL(port, ~0); + val |= UTMIP_WAKE_VAL_NONE(port); + padctl_pmc_writel(priv, val, PMC_UTMIP_UHSIC_SLEEP_CFG(port)); + + /* power down the line state detectors of the pad */ + val = padctl_pmc_readl(priv, PMC_USB_AO); + val |= (USBOP_VAL_PD(port) | USBON_VAL_PD(port)); + padctl_pmc_writel(priv, val, PMC_USB_AO); + + /* save state per speed */ + val = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SAVED_STATE(port)); + val &= ~SPEED(port, ~0); + if (speed == USB_SPEED_HIGH) + val |= UTMI_HS(port); + else if (speed == USB_SPEED_FULL) + val |= UTMI_FS(port); + else if (speed == USB_SPEED_LOW) + val |= UTMI_LS(port); + else + val |= UTMI_RST(port); + padctl_pmc_writel(priv, val, PMC_UTMIP_UHSIC_SAVED_STATE(port)); + + /* enable the trigger of the sleepwalk logic */ + val = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEPWALK_CFG(port)); + val |= UTMIP_LINEVAL_WALK_EN(port); + padctl_pmc_writel(priv, val, PMC_UTMIP_UHSIC_SLEEPWALK_CFG(port)); + + /* reset the walk pointer and clear the alarm of the sleepwalk logic, + * as well as capture the configuration of the USB2.0 pad + */ + val = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_TRIGGERS); + val |= (UTMIP_CLR_WALK_PTR(port) | UTMIP_CLR_WAKE_ALARM(port) | + UTMIP_CAP_CFG(port)); + padctl_pmc_writel(priv, val, PMC_UTMIP_UHSIC_TRIGGERS); + + /* program electrical parameters read from XUSB PADCTL */ + val = padctl_pmc_readl(priv, PMC_UTMIP_TERM_PAD_CFG); + val &= ~(TCTRL_VAL(~0) | PCTRL_VAL(~0)); + val |= (TCTRL_VAL(tctrl) | PCTRL_VAL(pctrl)); + padctl_pmc_writel(priv, val, PMC_UTMIP_TERM_PAD_CFG); + + val = padctl_pmc_readl(priv, PMC_UTMIP_PAD_CFGX(port)); + val &= ~RPD_CTRL_PX(~0); + val |= RPD_CTRL_PX(rpd_ctrl); + padctl_pmc_writel(priv, val, PMC_UTMIP_PAD_CFGX(port)); + + /* setup the pull-ups and pull-downs of the signals during the four + * stages of sleepwalk. + * if device is connected, program sleepwalk logic to maintain a J and + * keep driving K upon seeing remote wake. + */ + val = padctl_pmc_readl(priv, PMC_UTMIP_SLEEPWALK_PX(port)); + val = (UTMIP_USBOP_RPD_A | UTMIP_USBOP_RPD_B | UTMIP_USBOP_RPD_C | + UTMIP_USBOP_RPD_D); + val |= (UTMIP_USBON_RPD_A | UTMIP_USBON_RPD_B | UTMIP_USBON_RPD_C | + UTMIP_USBON_RPD_D); + if (speed == USB_SPEED_UNKNOWN) { + val |= (UTMIP_HIGHZ_A | UTMIP_HIGHZ_B | UTMIP_HIGHZ_C | + UTMIP_HIGHZ_D); + } else if ((speed == USB_SPEED_HIGH) || (speed == USB_SPEED_FULL)) { + /* J state: D+/D- = high/low, K state: D+/D- = low/high */ + val |= UTMIP_HIGHZ_A; + val |= UTMIP_AP_A; + val |= (UTMIP_AN_B | UTMIP_AN_C | UTMIP_AN_D); + } else if (speed == USB_SPEED_LOW) { + /* J state: D+/D- = low/high, K state: D+/D- = high/low */ + val |= UTMIP_HIGHZ_A; + val |= UTMIP_AN_A; + val |= (UTMIP_AP_B | UTMIP_AP_C | UTMIP_AP_D); + } + padctl_pmc_writel(priv, val, PMC_UTMIP_SLEEPWALK_PX(port)); + + /* power up the line state detectors of the pad */ + val = padctl_pmc_readl(priv, PMC_USB_AO); + val &= ~(USBOP_VAL_PD(port) | USBON_VAL_PD(port)); + padctl_pmc_writel(priv, val, PMC_USB_AO); + + usleep_range(50, 100); + + /* switch the electric control of the USB2.0 pad to PMC */ + val = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG(port)); + val |= (UTMIP_FSLS_USE_PMC(port) | UTMIP_PCTRL_USE_PMC(port) | + UTMIP_TCTRL_USE_PMC(port)); + padctl_pmc_writel(priv, val, PMC_UTMIP_UHSIC_SLEEP_CFG(port)); + + val = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG1); + val |= (UTMIP_RPD_CTRL_USE_PMC_PX(port) | + UTMIP_RPU_SWITC_LOW_USE_PMC_PX(port)); + padctl_pmc_writel(priv, val, PMC_UTMIP_UHSIC_SLEEP_CFG1); + + /* set the wake signaling trigger events */ + val = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG(port)); + val &= ~UTMIP_WAKE_VAL(port, ~0); + val |= UTMIP_WAKE_VAL_ANY(port); + padctl_pmc_writel(priv, val, PMC_UTMIP_UHSIC_SLEEP_CFG(port)); + + /* enable the wake detection */ + val = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG(port)); + val |= UTMIP_MASTER_ENABLE(port); + padctl_pmc_writel(priv, val, PMC_UTMIP_UHSIC_SLEEP_CFG(port)); + + val = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_LINE_WAKEUP); + val |= UTMIP_LINE_WAKEUP_EN(port); + padctl_pmc_writel(priv, val, PMC_UTMIP_UHSIC_LINE_WAKEUP); + + return 0; +} + +int tegra_pmc_utmi_disable_phy_sleepwalk(struct phy *phy) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + struct tegra210_xusb_padctl *priv = to_tegra210_xusb_padctl(padctl); + struct device *dev = padctl->dev; + unsigned int port = lane->index; + u32 value; + + dev_dbg(dev, "phy disable sleepwalk usb2 %d\n", port); + + /* disable the wake detection */ + value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG(port)); + value &= ~UTMIP_MASTER_ENABLE(port); + padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_SLEEP_CFG(port)); + + value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_LINE_WAKEUP); + value &= ~UTMIP_LINE_WAKEUP_EN(port); + padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_LINE_WAKEUP); + + /* switch the electric control of the USB2.0 pad to XUSB or USB2 */ + value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG(port)); + value &= ~(UTMIP_FSLS_USE_PMC(port) | UTMIP_PCTRL_USE_PMC(port) | + UTMIP_TCTRL_USE_PMC(port)); + padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_SLEEP_CFG(port)); + + value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG1); + value &= ~(UTMIP_RPD_CTRL_USE_PMC_PX(port) | + UTMIP_RPU_SWITC_LOW_USE_PMC_PX(port)); + padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_SLEEP_CFG1); + + /* disable wake event triggers of sleepwalk logic */ + value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG(port)); + value &= ~UTMIP_WAKE_VAL(port, ~0); + value |= UTMIP_WAKE_VAL_NONE(port); + padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_SLEEP_CFG(port)); + + /* power down the line state detectors of the port */ + value = padctl_pmc_readl(priv, PMC_USB_AO); + value |= (USBOP_VAL_PD(port) | USBON_VAL_PD(port)); + padctl_pmc_writel(priv, value, PMC_USB_AO); + + /* clear alarm of the sleepwalk logic */ + value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_TRIGGERS); + value |= UTMIP_CLR_WAKE_ALARM(port); + padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_TRIGGERS); + + return 0; +} + +int tegra_pmc_hsic_enable_phy_sleepwalk(struct phy *phy) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + struct tegra210_xusb_padctl *priv = to_tegra210_xusb_padctl(padctl); + struct device *dev = padctl->dev; + unsigned int port = lane->index; + u32 value; + + dev_dbg(dev, "phy enable sleepwalk hsic %d\n", port); + + /* ensure sleepwalk logic is disabled */ + value = padctl_pmc_readl(priv, PMC_UHSIC_SLEEP_CFG); + value &= ~UHSIC_MASTER_ENABLE; + padctl_pmc_writel(priv, value, PMC_UHSIC_SLEEP_CFG); + + /* ensure sleepwalk logics are in low power mode */ + value = padctl_pmc_readl(priv, PMC_UTMIP_MASTER_CONFIG); + value |= UHSIC_PWR(port); + padctl_pmc_writel(priv, value, PMC_UTMIP_MASTER_CONFIG); + + /* set debounce time */ + value = padctl_pmc_readl(priv, PMC_USB_DEBOUNCE_DEL); + value &= ~UHSIC_LINE_DEB_CNT(~0); + value |= UHSIC_LINE_DEB_CNT(0x1); + padctl_pmc_writel(priv, value, PMC_USB_DEBOUNCE_DEL); + + /* ensure fake events of sleepwalk logic are desiabled */ + value = padctl_pmc_readl(priv, PMC_UHSIC_FAKE); + value &= ~(UHSIC_FAKE_STROBE_VAL | UHSIC_FAKE_DATA_VAL | + UHSIC_FAKE_STROBE_EN | UHSIC_FAKE_DATA_EN); + padctl_pmc_writel(priv, value, PMC_UHSIC_FAKE); + + /* ensure wake events of sleepwalk logic are not latched */ + value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_LINE_WAKEUP); + value &= ~UHSIC_LINE_WAKEUP_EN; + padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_LINE_WAKEUP); + + /* disable wake event triggers of sleepwalk logic */ + value = padctl_pmc_readl(priv, PMC_UHSIC_SLEEP_CFG); + value &= ~UHSIC_WAKE_VAL(~0); + value |= UHSIC_WAKE_VAL_NONE; + padctl_pmc_writel(priv, value, PMC_UHSIC_SLEEP_CFG); + + /* power down the line state detectors of the port */ + value = padctl_pmc_readl(priv, PMC_USB_AO); + value |= (STROBE_VAL_PD(port) | DATA0_VAL_PD(port) | DATA1_VAL_PD); + padctl_pmc_writel(priv, value, PMC_USB_AO); + + /* save state, HSIC always comes up as HS */ + value = padctl_pmc_readl(priv, PMC_UHSIC_SAVED_STATE); + value &= ~UHSIC_MODE(~0); + value |= UHSIC_HS; + padctl_pmc_writel(priv, value, PMC_UHSIC_SAVED_STATE); + + /* enable the trigger of the sleepwalk logic */ + value = padctl_pmc_readl(priv, PMC_UHSIC_SLEEPWALK_CFG); + value |= (UHSIC_WAKE_WALK_EN | UHSIC_LINEVAL_WALK_EN); + padctl_pmc_writel(priv, value, PMC_UHSIC_SLEEPWALK_CFG); + + /* reset the walk pointer and clear the alarm of the sleepwalk logic, + * as well as capture the configuration of the USB2.0 port + */ + value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_TRIGGERS); + value |= (UHSIC_CLR_WALK_PTR | UHSIC_CLR_WAKE_ALARM); + padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_TRIGGERS); + + /* setup the pull-ups and pull-downs of the signals during the four + * stages of sleepwalk. + * maintain a HSIC IDLE and keep driving HSIC RESUME upon remote wake + */ + value = padctl_pmc_readl(priv, PMC_UHSIC_SLEEPWALK_P0); + value = (UHSIC_DATA0_RPD_A | UHSIC_DATA0_RPU_B | UHSIC_DATA0_RPU_C | + UHSIC_DATA0_RPU_D); + value |= (UHSIC_STROBE_RPU_A | UHSIC_STROBE_RPD_B | UHSIC_STROBE_RPD_C | + UHSIC_STROBE_RPD_D); + padctl_pmc_writel(priv, value, PMC_UHSIC_SLEEPWALK_P0); + + /* power up the line state detectors of the port */ + value = padctl_pmc_readl(priv, PMC_USB_AO); + value &= ~(STROBE_VAL_PD(port) | DATA0_VAL_PD(port) | DATA1_VAL_PD); + padctl_pmc_writel(priv, value, PMC_USB_AO); + + usleep_range(50, 100); + + /* set the wake signaling trigger events */ + value = padctl_pmc_readl(priv, PMC_UHSIC_SLEEP_CFG); + value &= ~UHSIC_WAKE_VAL(~0); + value |= UHSIC_WAKE_VAL_SD10; + padctl_pmc_writel(priv, value, PMC_UHSIC_SLEEP_CFG); + + /* enable the wake detection */ + value = padctl_pmc_readl(priv, PMC_UHSIC_SLEEP_CFG); + value |= UHSIC_MASTER_ENABLE; + padctl_pmc_writel(priv, value, PMC_UHSIC_SLEEP_CFG); + + value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_LINE_WAKEUP); + value |= UHSIC_LINE_WAKEUP_EN; + padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_LINE_WAKEUP); + + return 0; +} + +int tegra_pmc_hsic_disable_phy_sleepwalk(struct phy *phy) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + struct tegra210_xusb_padctl *priv = to_tegra210_xusb_padctl(padctl); + struct device *dev = padctl->dev; + unsigned int port = lane->index; + u32 value; + + dev_dbg(dev, "phy disable sleepwalk hsic %d\n", port); + + /* disable the wake detection */ + value = padctl_pmc_readl(priv, PMC_UHSIC_SLEEP_CFG); + value &= ~UHSIC_MASTER_ENABLE; + padctl_pmc_writel(priv, value, PMC_UHSIC_SLEEP_CFG); + + value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_LINE_WAKEUP); + value &= ~UHSIC_LINE_WAKEUP_EN; + padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_LINE_WAKEUP); + + /* disable wake event triggers of sleepwalk logic */ + value = padctl_pmc_readl(priv, PMC_UHSIC_SLEEP_CFG); + value &= ~UHSIC_WAKE_VAL(~0); + value |= UHSIC_WAKE_VAL_NONE; + padctl_pmc_writel(priv, value, PMC_UHSIC_SLEEP_CFG); + + /* power down the line state detectors of the port */ + value = padctl_pmc_readl(priv, PMC_USB_AO); + value |= (STROBE_VAL_PD(port) | DATA0_VAL_PD(port) | DATA1_VAL_PD); + padctl_pmc_writel(priv, value, PMC_USB_AO); + + /* clear alarm of the sleepwalk logic */ + value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_TRIGGERS); + value |= UHSIC_CLR_WAKE_ALARM; + padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_TRIGGERS); + + return 0; +} + static int tegra210_usb3_set_lfps_detect(struct tegra_xusb_padctl *padctl, unsigned int index, bool enable) { @@ -988,8 +1818,23 @@ static int tegra210_usb2_phy_init(struct phy *phy) { struct tegra_xusb_lane *lane = phy_get_drvdata(phy); struct tegra_xusb_padctl *padctl = lane->pad->padctl; + unsigned int index = lane->index; + struct tegra_xusb_usb2_port *port; + int err; u32 value; + port = tegra_xusb_find_usb2_port(padctl, index); + if (!port) { + dev_err(&phy->dev, "no port found for USB2 lane %u\n", index); + return -ENODEV; + } + + err = regulator_enable(port->supply); + if (err) + return err; + + mutex_lock(&padctl->lock); + value = padctl_readl(padctl, XUSB_PADCTL_USB2_PAD_MUX); value &= ~(XUSB_PADCTL_USB2_PAD_MUX_USB2_BIAS_PAD_MASK << XUSB_PADCTL_USB2_PAD_MUX_USB2_BIAS_PAD_SHIFT); @@ -997,11 +1842,29 @@ static int tegra210_usb2_phy_init(struct phy *phy) XUSB_PADCTL_USB2_PAD_MUX_USB2_BIAS_PAD_SHIFT; padctl_writel(padctl, value, XUSB_PADCTL_USB2_PAD_MUX); + mutex_unlock(&padctl->lock); + return 0; } static int tegra210_usb2_phy_exit(struct phy *phy) { + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + unsigned int index = lane->index; + struct tegra_xusb_usb2_port *port; + int err; + + port = tegra_xusb_find_usb2_port(padctl, index); + if (!port) { + dev_err(&phy->dev, "no port found for USB2 lane %u\n", index); + return -ENODEV; + } + + err = regulator_disable(port->supply); + if (err) + return err; + return 0; } @@ -1122,6 +1985,8 @@ static int tegra210_usb2_phy_power_on(struct phy *phy) priv = to_tegra210_xusb_padctl(padctl); + mutex_lock(&padctl->lock); + if (port->usb3_port_fake != -1) { value = padctl_readl(padctl, XUSB_PADCTL_SS_PORT_MAP); value &= ~XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP_MASK( @@ -1215,14 +2080,6 @@ static int tegra210_usb2_phy_power_on(struct phy *phy) padctl_writel(padctl, value, XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPADX_CTL1(index)); - if (port->supply && port->mode == USB_DR_MODE_HOST) { - err = regulator_enable(port->supply); - if (err) - return err; - } - - mutex_lock(&padctl->lock); - if (pad->enable > 0) { pad->enable++; mutex_unlock(&padctl->lock); @@ -1231,7 +2088,7 @@ static int tegra210_usb2_phy_power_on(struct phy *phy) err = clk_prepare_enable(pad->clk); if (err) - goto disable_regulator; + goto out; value = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL1); value &= ~((XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_MASK << @@ -1263,8 +2120,7 @@ static int tegra210_usb2_phy_power_on(struct phy *phy) return 0; -disable_regulator: - regulator_disable(port->supply); +out: mutex_unlock(&padctl->lock); return err; } @@ -1275,12 +2131,12 @@ static int tegra210_usb2_phy_power_off(struct phy *phy) struct tegra_xusb_usb2_pad *pad = to_usb2_pad(lane->pad); struct tegra_xusb_padctl *padctl = lane->pad->padctl; struct tegra_xusb_usb2_port *port; + unsigned int index = lane->index; u32 value; - port = tegra_xusb_find_usb2_port(padctl, lane->index); + port = tegra_xusb_find_usb2_port(padctl, index); if (!port) { - dev_err(&phy->dev, "no port found for USB2 lane %u\n", - lane->index); + dev_err(&phy->dev, "no port found for USB2 lane %u\n", index); return -ENODEV; } @@ -1318,12 +2174,19 @@ static int tegra210_usb2_phy_power_off(struct phy *phy) if (--pad->enable > 0) goto out; + value = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL0(index)); + value |= XUSB_PADCTL_USB2_OTG_PAD_CTL0_PD; + padctl_writel(padctl, value, XUSB_PADCTL_USB2_OTG_PADX_CTL0(index)); + + value = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL1(index)); + value |= XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_DR; + padctl_writel(padctl, value, XUSB_PADCTL_USB2_OTG_PADX_CTL1(index)); + value = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL0); value |= XUSB_PADCTL_USB2_BIAS_PAD_CTL0_PD; padctl_writel(padctl, value, XUSB_PADCTL_USB2_BIAS_PAD_CTL0); out: - regulator_disable(port->supply); mutex_unlock(&padctl->lock); return 0; } @@ -2120,6 +2983,120 @@ static const struct phy_ops tegra210_sata_phy_ops = { .owner = THIS_MODULE, }; +static inline bool is_usb3_phy(struct phy *phy) +{ + return (phy->ops == &tegra210_pcie_phy_ops || + phy->ops == &tegra210_sata_phy_ops); +} + +static inline bool is_hsic_phy(struct phy *phy) +{ + return phy->ops == &tegra210_hsic_phy_ops; +} + +static inline bool is_utmi_phy(struct phy *phy) +{ + return phy->ops == &tegra210_usb2_phy_ops; +} + +static int +tegra210_xusb_padctl_enable_phy_wake(struct tegra_xusb_padctl *padctl, + struct phy *phy) +{ + if (!phy) + return 0; + + if (is_usb3_phy(phy)) + return tegra210_usb3_enable_phy_wake(phy); + else if (is_utmi_phy(phy)) + return tegra210_utmi_enable_phy_wake(phy); + else if (is_hsic_phy(phy)) + return tegra210_hsic_enable_phy_wake(phy); + else + return -EINVAL; +} + +static int +tegra210_xusb_padctl_disable_phy_wake(struct tegra_xusb_padctl *padctl, + struct phy *phy) +{ + if (!phy) + return 0; + + if (is_usb3_phy(phy)) + return tegra210_usb3_disable_phy_wake(phy); + else if (is_utmi_phy(phy)) + return tegra210_utmi_disable_phy_wake(phy); + else if (is_hsic_phy(phy)) + return tegra210_hsic_disable_phy_wake(phy); + else + return -EINVAL; +} + +int tegra210_xusb_padctl_remote_wake_detected(struct phy *phy) +{ + struct tegra_xusb_lane *lane; + struct tegra_xusb_padctl *padctl; + + if (!phy) + return 0; + + lane = phy_get_drvdata(phy); + padctl = lane->pad->padctl; + + if (is_utmi_phy(phy)) + return tegra210_utmi_phy_remote_wake_detected(padctl, + lane->index); + else if (is_hsic_phy(phy)) + return tegra210_hsic_phy_remote_wake_detected(padctl, + lane->index); + else if (is_usb3_phy(phy)) + return tegra210_usb3_phy_remote_wake_detected(padctl, + tegra210_usb3_lane_map(lane)); + + return -EINVAL; +} + +static int +tegra210_xusb_padctl_enable_phy_sleepwalk(struct tegra_xusb_padctl *padctl, + struct phy *phy, + enum usb_device_speed speed) +{ + if (!phy) + return 0; + + if (is_usb3_phy(phy)) + return tegra210_usb3_enable_phy_sleepwalk(phy); + else if (is_utmi_phy(phy)) + return tegra_pmc_utmi_enable_phy_sleepwalk(phy, speed); + else if (is_hsic_phy(phy)) + return tegra_pmc_hsic_enable_phy_sleepwalk(phy); + else + return -EINVAL; + + return 0; +} + +static int +tegra210_xusb_padctl_disable_phy_sleepwalk(struct tegra_xusb_padctl *padctl, + struct phy *phy) +{ + if (!phy) + return 0; + + if (is_usb3_phy(phy)) + return tegra210_usb3_disable_phy_sleepwalk(phy); + else if (is_utmi_phy(phy)) + return tegra_pmc_utmi_disable_phy_sleepwalk(phy); + else if (is_hsic_phy(phy)) + return tegra_pmc_hsic_disable_phy_sleepwalk(phy); + else + return -EINVAL; + + return 0; +} + + static struct tegra_xusb_pad * tegra210_sata_pad_probe(struct tegra_xusb_padctl *padctl, const struct tegra_xusb_pad_soc *soc, @@ -2317,6 +3294,8 @@ tegra210_xusb_padctl_probe(struct device *dev, const struct tegra_xusb_padctl_soc *soc) { struct tegra210_xusb_padctl *padctl; + struct device_node *node, *np = dev->of_node; + struct platform_device *pmc_dev; int err; padctl = devm_kzalloc(dev, sizeof(*padctl), GFP_KERNEL); @@ -2330,6 +3309,24 @@ tegra210_xusb_padctl_probe(struct device *dev, if (err < 0) return ERR_PTR(err); + node = of_parse_phandle(np, "nvidia,pmc", 0); + if (!node) { + dev_err(dev, "nvidia,pmc property is missing\n"); + return ERR_PTR(-ENODEV); + } + + pmc_dev = of_find_device_by_node(node); + if (!pmc_dev) { + dev_err(dev, "pmc device is not available\n"); + return ERR_PTR(-ENODEV); + } + + padctl->pmc_reg = dev_get_regmap(&pmc_dev->dev, "usb_sleepwalk"); + if (!padctl->pmc_reg) { + dev_err(dev, "pmc regmap is not available.\n"); + return ERR_PTR(-ENODEV); + } + return &padctl->base; } @@ -2337,13 +3334,80 @@ static void tegra210_xusb_padctl_remove(struct tegra_xusb_padctl *padctl) { } +static void tegra210_xusb_padctl_save(struct tegra_xusb_padctl *padctl) +{ + struct tegra210_xusb_padctl *priv = to_tegra210_xusb_padctl(padctl); + + priv->context.usb2_pad_mux = + padctl_readl(padctl, XUSB_PADCTL_USB2_PAD_MUX); + priv->context.usb2_port_cap = + padctl_readl(padctl, XUSB_PADCTL_USB2_PORT_CAP); + priv->context.ss_port_map = + padctl_readl(padctl, XUSB_PADCTL_SS_PORT_MAP); + priv->context.usb3_pad_mux = + padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX); +} + +static void tegra210_xusb_padctl_restore(struct tegra_xusb_padctl *padctl) +{ + struct tegra210_xusb_padctl *priv = to_tegra210_xusb_padctl(padctl); + int i; + + padctl_writel(padctl, priv->context.usb2_pad_mux, + XUSB_PADCTL_USB2_PAD_MUX); + padctl_writel(padctl, priv->context.usb2_port_cap, + XUSB_PADCTL_USB2_PORT_CAP); + padctl_writel(padctl, priv->context.ss_port_map, + XUSB_PADCTL_SS_PORT_MAP); + + for (i = 0; i <= 7; i ++) + tegra210_uphy_lane_iddq_enable(padctl, i); + + padctl_writel(padctl, priv->context.usb3_pad_mux, + XUSB_PADCTL_USB3_PAD_MUX); + + for (i = 0; i <= 7; i ++) + tegra210_uphy_lane_iddq_disable(padctl, i); +} + +static int tegra210_xusb_padctl_suspend_noirq(struct tegra_xusb_padctl *padctl) +{ + mutex_lock(&padctl->lock); + + tegra210_uphy_deinit(padctl); + + tegra210_xusb_padctl_save(padctl); + + mutex_unlock(&padctl->lock); + return 0; +} + +static int tegra210_xusb_padctl_resume_noirq(struct tegra_xusb_padctl *padctl) +{ + mutex_lock(&padctl->lock); + + tegra210_xusb_padctl_restore(padctl); + + tegra210_uphy_init(padctl); + + mutex_unlock(&padctl->lock); + return 0; +} + static const struct tegra_xusb_padctl_ops tegra210_xusb_padctl_ops = { .probe = tegra210_xusb_padctl_probe, .remove = tegra210_xusb_padctl_remove, + .suspend_noirq = tegra210_xusb_padctl_suspend_noirq, + .resume_noirq = tegra210_xusb_padctl_resume_noirq, .usb3_set_lfps_detect = tegra210_usb3_set_lfps_detect, .hsic_set_idle = tegra210_hsic_set_idle, .vbus_override = tegra210_xusb_padctl_vbus_override, .utmi_port_reset = tegra210_utmi_port_reset, + .enable_phy_sleepwalk = tegra210_xusb_padctl_enable_phy_sleepwalk, + .disable_phy_sleepwalk = tegra210_xusb_padctl_disable_phy_sleepwalk, + .enable_phy_wake = tegra210_xusb_padctl_enable_phy_wake, + .disable_phy_wake = tegra210_xusb_padctl_disable_phy_wake, + .remote_wake_detected = tegra210_xusb_padctl_remote_wake_detected, }; static const char * const tegra210_xusb_padctl_supply_names[] = {
This commit implements Tegra210 XUSB PADCTL wake and sleepwalk routines. Sleepwalk logic is in PMC (always-on) hardware block. PMC driver provides managed access to the sleepwalk registers via regmap framework. Signed-off-by: JC Kuo <jckuo@nvidia.com> --- drivers/phy/tegra/xusb-tegra210.c | 1094 ++++++++++++++++++++++++++++- 1 file changed, 1079 insertions(+), 15 deletions(-)