Loading drivers/phy/tegra/xusb-tegra186.c +549 −1 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2016-2019, NVIDIA CORPORATION. All rights reserved. * Copyright (c) 2016-2020, NVIDIA CORPORATION. All rights reserved. */ #include <linux/delay.h> Loading Loading @@ -113,6 +113,117 @@ #define ID_OVERRIDE_FLOATING ID_OVERRIDE(8) #define ID_OVERRIDE_GROUNDED ID_OVERRIDE(0) /* XUSB AO registers */ #define XUSB_AO_USB_DEBOUNCE_DEL (0x4) #define UHSIC_LINE_DEB_CNT(x) (((x) & 0xf) << 4) #define UTMIP_LINE_DEB_CNT(x) ((x) & 0xf) #define XUSB_AO_UTMIP_TRIGGERS(x) (0x40 + (x) * 4) #define CLR_WALK_PTR BIT(0) #define CAP_CFG BIT(1) #define CLR_WAKE_ALARM BIT(3) #define XUSB_AO_UHSIC_TRIGGERS(x) (0x60 + (x) * 4) #define HSIC_CLR_WALK_PTR BIT(0) #define HSIC_CLR_WAKE_ALARM BIT(3) #define HSIC_CAP_CFG BIT(4) #define XUSB_AO_UTMIP_SAVED_STATE(x) (0x70 + (x) * 4) #define SPEED(x) ((x) & 0x3) #define UTMI_HS SPEED(0) #define UTMI_FS SPEED(1) #define UTMI_LS SPEED(2) #define UTMI_RST SPEED(3) #define XUSB_AO_UHSIC_SAVED_STATE(x) (0x90 + (x) * 4) #define MODE(x) ((x) & 0x1) #define MODE_HS MODE(0) #define MODE_RST MODE(1) #define XUSB_AO_UTMIP_SLEEPWALK_CFG(x) (0xd0 + (x) * 4) #define XUSB_AO_UHSIC_SLEEPWALK_CFG(x) (0xf0 + (x) * 4) #define FAKE_USBOP_VAL BIT(0) #define FAKE_USBON_VAL BIT(1) #define FAKE_USBOP_EN BIT(2) #define FAKE_USBON_EN BIT(3) #define FAKE_STROBE_VAL BIT(0) #define FAKE_DATA_VAL BIT(1) #define FAKE_STROBE_EN BIT(2) #define FAKE_DATA_EN BIT(3) #define WAKE_WALK_EN BIT(14) #define MASTER_ENABLE BIT(15) #define LINEVAL_WALK_EN BIT(16) #define WAKE_VAL(x) (((x) & 0xf) << 17) #define WAKE_VAL_NONE WAKE_VAL(12) #define WAKE_VAL_ANY WAKE_VAL(15) #define WAKE_VAL_DS10 WAKE_VAL(2) #define LINE_WAKEUP_EN BIT(21) #define MASTER_CFG_SEL BIT(22) #define XUSB_AO_UTMIP_SLEEPWALK(x) (0x100 + (x) * 4) /* phase A */ #define USBOP_RPD_A BIT(0) #define USBON_RPD_A BIT(1) #define AP_A BIT(4) #define AN_A BIT(5) #define HIGHZ_A BIT(6) /* phase B */ #define USBOP_RPD_B BIT(8) #define USBON_RPD_B BIT(9) #define AP_B BIT(12) #define AN_B BIT(13) #define HIGHZ_B BIT(14) /* phase C */ #define USBOP_RPD_C BIT(16) #define USBON_RPD_C BIT(17) #define AP_C BIT(20) #define AN_C BIT(21) #define HIGHZ_C BIT(22) /* phase D */ #define USBOP_RPD_D BIT(24) #define USBON_RPD_D BIT(25) #define AP_D BIT(28) #define AN_D BIT(29) #define HIGHZ_D BIT(30) #define XUSB_AO_UHSIC_SLEEPWALK(x) (0x120 + (x) * 4) /* phase A */ #define RPD_STROBE_A BIT(0) #define RPD_DATA0_A BIT(1) #define RPU_STROBE_A BIT(2) #define RPU_DATA0_A BIT(3) /* phase B */ #define RPD_STROBE_B BIT(8) #define RPD_DATA0_B BIT(9) #define RPU_STROBE_B BIT(10) #define RPU_DATA0_B BIT(11) /* phase C */ #define RPD_STROBE_C BIT(16) #define RPD_DATA0_C BIT(17) #define RPU_STROBE_C BIT(18) #define RPU_DATA0_C BIT(19) /* phase D */ #define RPD_STROBE_D BIT(24) #define RPD_DATA0_D BIT(25) #define RPU_STROBE_D BIT(26) #define RPU_DATA0_D BIT(27) #define XUSB_AO_UTMIP_PAD_CFG(x) (0x130 + (x) * 4) #define FSLS_USE_XUSB_AO BIT(3) #define TRK_CTRL_USE_XUSB_AO BIT(4) #define RPD_CTRL_USE_XUSB_AO BIT(5) #define RPU_USE_XUSB_AO BIT(6) #define VREG_USE_XUSB_AO BIT(7) #define USBOP_VAL_PD BIT(8) #define USBON_VAL_PD BIT(9) #define E_DPD_OVRD_EN BIT(10) #define E_DPD_OVRD_VAL BIT(11) #define XUSB_AO_UHSIC_PAD_CFG(x) (0x150 + (x) * 4) #define STROBE_VAL_PD BIT(0) #define DATA0_VAL_PD BIT(1) #define USE_XUSB_AO BIT(4) #define TEGRA186_LANE(_name, _offset, _shift, _mask, _type) \ { \ .name = _name, \ Loading @@ -130,16 +241,37 @@ struct tegra_xusb_fuse_calibration { u32 rpd_ctrl; }; struct tegra186_xusb_padctl_context { u32 vbus_id; u32 usb2_pad_mux; u32 usb2_port_cap; u32 ss_port_cap; }; struct tegra186_xusb_padctl { struct tegra_xusb_padctl base; void __iomem *ao_regs; struct tegra_xusb_fuse_calibration calib; /* UTMI bias and tracking */ struct clk *usb2_trk_clk; unsigned int bias_pad_enable; /* padctl context */ struct tegra186_xusb_padctl_context context; }; static inline void ao_writel(struct tegra186_xusb_padctl *priv, u32 value, unsigned int offset) { writel(value, priv->ao_regs + offset); } static inline u32 ao_readl(struct tegra186_xusb_padctl *priv, unsigned int offset) { return readl(priv->ao_regs + offset); } static inline struct tegra186_xusb_padctl * to_tegra186_xusb_padctl(struct tegra_xusb_padctl *padctl) { Loading Loading @@ -180,9 +312,264 @@ static void tegra186_usb2_lane_remove(struct tegra_xusb_lane *lane) kfree(usb2); } static int tegra186_utmi_enable_phy_sleepwalk(struct tegra_xusb_lane *lane, enum usb_device_speed speed) { struct tegra_xusb_padctl *padctl = lane->pad->padctl; struct tegra186_xusb_padctl *priv = to_tegra186_xusb_padctl(padctl); unsigned int index = lane->index; u32 value; mutex_lock(&padctl->lock); /* ensure sleepwalk logic is disabled */ value = ao_readl(priv, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); value &= ~MASTER_ENABLE; ao_writel(priv, value, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); /* ensure sleepwalk logics are in low power mode */ value = ao_readl(priv, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); value |= MASTER_CFG_SEL; ao_writel(priv, value, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); /* set debounce time */ value = ao_readl(priv, XUSB_AO_USB_DEBOUNCE_DEL); value &= ~UTMIP_LINE_DEB_CNT(~0); value |= UTMIP_LINE_DEB_CNT(1); ao_writel(priv, value, XUSB_AO_USB_DEBOUNCE_DEL); /* ensure fake events of sleepwalk logic are desiabled */ value = ao_readl(priv, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); value &= ~(FAKE_USBOP_VAL | FAKE_USBON_VAL | FAKE_USBOP_EN | FAKE_USBON_EN); ao_writel(priv, value, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); /* ensure wake events of sleepwalk logic are not latched */ value = ao_readl(priv, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); value &= ~LINE_WAKEUP_EN; ao_writel(priv, value, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); /* disable wake event triggers of sleepwalk logic */ value = ao_readl(priv, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); value &= ~WAKE_VAL(~0); value |= WAKE_VAL_NONE; ao_writel(priv, value, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); /* power down the line state detectors of the pad */ value = ao_readl(priv, XUSB_AO_UTMIP_PAD_CFG(index)); value |= (USBOP_VAL_PD | USBON_VAL_PD); ao_writel(priv, value, XUSB_AO_UTMIP_PAD_CFG(index)); /* save state per speed */ value = ao_readl(priv, XUSB_AO_UTMIP_SAVED_STATE(index)); value &= ~SPEED(~0); switch (speed) { case USB_SPEED_HIGH: value |= UTMI_HS; break; case USB_SPEED_FULL: value |= UTMI_FS; break; case USB_SPEED_LOW: value |= UTMI_LS; break; default: value |= UTMI_RST; break; } ao_writel(priv, value, XUSB_AO_UTMIP_SAVED_STATE(index)); /* enable the trigger of the sleepwalk logic */ value = ao_readl(priv, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); value |= LINEVAL_WALK_EN; value &= ~WAKE_WALK_EN; ao_writel(priv, value, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); /* reset the walk pointer and clear the alarm of the sleepwalk logic, * as well as capture the configuration of the USB2.0 pad */ value = ao_readl(priv, XUSB_AO_UTMIP_TRIGGERS(index)); value |= (CLR_WALK_PTR | CLR_WAKE_ALARM | CAP_CFG); ao_writel(priv, value, XUSB_AO_UTMIP_TRIGGERS(index)); /* 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. */ value = USBOP_RPD_A | USBOP_RPD_B | USBOP_RPD_C | USBOP_RPD_D; value |= USBON_RPD_A | USBON_RPD_B | USBON_RPD_C | USBON_RPD_D; switch (speed) { case USB_SPEED_HIGH: case USB_SPEED_FULL: /* J state: D+/D- = high/low, K state: D+/D- = low/high */ value |= HIGHZ_A; value |= AP_A; value |= AN_B | AN_C | AN_D; break; case USB_SPEED_LOW: /* J state: D+/D- = low/high, K state: D+/D- = high/low */ value |= HIGHZ_A; value |= AN_A; value |= AP_B | AP_C | AP_D; break; default: value |= HIGHZ_A | HIGHZ_B | HIGHZ_C | HIGHZ_D; break; } ao_writel(priv, value, XUSB_AO_UTMIP_SLEEPWALK(index)); /* power up the line state detectors of the pad */ value = ao_readl(priv, XUSB_AO_UTMIP_PAD_CFG(index)); value &= ~(USBOP_VAL_PD | USBON_VAL_PD); ao_writel(priv, value, XUSB_AO_UTMIP_PAD_CFG(index)); usleep_range(150, 200); /* switch the electric control of the USB2.0 pad to XUSB_AO */ value = ao_readl(priv, XUSB_AO_UTMIP_PAD_CFG(index)); value |= FSLS_USE_XUSB_AO | TRK_CTRL_USE_XUSB_AO | RPD_CTRL_USE_XUSB_AO | RPU_USE_XUSB_AO | VREG_USE_XUSB_AO; ao_writel(priv, value, XUSB_AO_UTMIP_PAD_CFG(index)); /* set the wake signaling trigger events */ value = ao_readl(priv, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); value &= ~WAKE_VAL(~0); value |= WAKE_VAL_ANY; ao_writel(priv, value, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); /* enable the wake detection */ value = ao_readl(priv, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); value |= MASTER_ENABLE | LINE_WAKEUP_EN; ao_writel(priv, value, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); mutex_unlock(&padctl->lock); return 0; } static int tegra186_utmi_disable_phy_sleepwalk(struct tegra_xusb_lane *lane) { struct tegra_xusb_padctl *padctl = lane->pad->padctl; struct tegra186_xusb_padctl *priv = to_tegra186_xusb_padctl(padctl); unsigned int index = lane->index; u32 value; mutex_lock(&padctl->lock); /* disable the wake detection */ value = ao_readl(priv, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); value &= ~(MASTER_ENABLE | LINE_WAKEUP_EN); ao_writel(priv, value, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); /* switch the electric control of the USB2.0 pad to XUSB vcore logic */ value = ao_readl(priv, XUSB_AO_UTMIP_PAD_CFG(index)); value &= ~(FSLS_USE_XUSB_AO | TRK_CTRL_USE_XUSB_AO | RPD_CTRL_USE_XUSB_AO | RPU_USE_XUSB_AO | VREG_USE_XUSB_AO); ao_writel(priv, value, XUSB_AO_UTMIP_PAD_CFG(index)); /* disable wake event triggers of sleepwalk logic */ value = ao_readl(priv, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); value &= ~WAKE_VAL(~0); value |= WAKE_VAL_NONE; ao_writel(priv, value, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); /* power down the line state detectors of the port */ value = ao_readl(priv, XUSB_AO_UTMIP_PAD_CFG(index)); value |= USBOP_VAL_PD | USBON_VAL_PD; ao_writel(priv, value, XUSB_AO_UTMIP_PAD_CFG(index)); /* clear alarm of the sleepwalk logic */ value = ao_readl(priv, XUSB_AO_UTMIP_TRIGGERS(index)); value |= CLR_WAKE_ALARM; ao_writel(priv, value, XUSB_AO_UTMIP_TRIGGERS(index)); mutex_unlock(&padctl->lock); return 0; } static int tegra186_utmi_enable_phy_wake(struct tegra_xusb_lane *lane) { struct tegra_xusb_padctl *padctl = lane->pad->padctl; unsigned int index = lane->index; u32 value; mutex_lock(&padctl->lock); value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM); value &= ~ALL_WAKE_EVENTS; value |= USB2_PORT_WAKEUP_EVENT(index); padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM); usleep_range(10, 20); value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM); value &= ~ALL_WAKE_EVENTS; value |= USB2_PORT_WAKE_INTERRUPT_ENABLE(index); padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM); mutex_unlock(&padctl->lock); return 0; } static int tegra186_utmi_disable_phy_wake(struct tegra_xusb_lane *lane) { struct tegra_xusb_padctl *padctl = lane->pad->padctl; unsigned int index = lane->index; u32 value; mutex_lock(&padctl->lock); value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM); value &= ~ALL_WAKE_EVENTS; value &= ~USB2_PORT_WAKE_INTERRUPT_ENABLE(index); padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM); usleep_range(10, 20); value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM); value &= ~ALL_WAKE_EVENTS; value |= USB2_PORT_WAKEUP_EVENT(index); padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM); mutex_unlock(&padctl->lock); return 0; } static bool tegra186_utmi_phy_remote_wake_detected(struct tegra_xusb_lane *lane) { struct tegra_xusb_padctl *padctl = lane->pad->padctl; unsigned int index = lane->index; u32 value; value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM); if ((value & USB2_PORT_WAKE_INTERRUPT_ENABLE(index)) && (value & USB2_PORT_WAKEUP_EVENT(index))) return true; return false; } static const struct tegra_xusb_lane_ops tegra186_usb2_lane_ops = { .probe = tegra186_usb2_lane_probe, .remove = tegra186_usb2_lane_remove, .enable_phy_sleepwalk = tegra186_utmi_enable_phy_sleepwalk, .disable_phy_sleepwalk = tegra186_utmi_disable_phy_sleepwalk, .enable_phy_wake = tegra186_utmi_enable_phy_wake, .disable_phy_wake = tegra186_utmi_disable_phy_wake, .remote_wake_detected = tegra186_utmi_phy_remote_wake_detected, }; static void tegra186_utmi_bias_pad_power_on(struct tegra_xusb_padctl *padctl) Loading Loading @@ -656,10 +1043,128 @@ static void tegra186_usb3_lane_remove(struct tegra_xusb_lane *lane) kfree(usb3); } static int tegra186_usb3_enable_phy_sleepwalk(struct tegra_xusb_lane *lane, enum usb_device_speed speed) { struct tegra_xusb_padctl *padctl = lane->pad->padctl; unsigned int index = lane->index; u32 value; mutex_lock(&padctl->lock); value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_1); value |= SSPX_ELPG_CLAMP_EN_EARLY(index); padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_1); usleep_range(100, 200); value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_1); value |= SSPX_ELPG_CLAMP_EN(index); padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_1); usleep_range(250, 350); mutex_unlock(&padctl->lock); return 0; } static int tegra186_usb3_disable_phy_sleepwalk(struct tegra_xusb_lane *lane) { struct tegra_xusb_padctl *padctl = lane->pad->padctl; unsigned int index = lane->index; u32 value; mutex_lock(&padctl->lock); value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_1); value &= ~SSPX_ELPG_CLAMP_EN_EARLY(index); padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_1); usleep_range(100, 200); value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_1); value &= ~SSPX_ELPG_CLAMP_EN(index); padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_1); mutex_unlock(&padctl->lock); return 0; } static int tegra186_usb3_enable_phy_wake(struct tegra_xusb_lane *lane) { struct tegra_xusb_padctl *padctl = lane->pad->padctl; unsigned int index = lane->index; u32 value; mutex_lock(&padctl->lock); value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM); value &= ~ALL_WAKE_EVENTS; value |= SS_PORT_WAKEUP_EVENT(index); padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM); usleep_range(10, 20); value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM); value &= ~ALL_WAKE_EVENTS; value |= SS_PORT_WAKE_INTERRUPT_ENABLE(index); padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM); mutex_unlock(&padctl->lock); return 0; } static int tegra186_usb3_disable_phy_wake(struct tegra_xusb_lane *lane) { struct tegra_xusb_padctl *padctl = lane->pad->padctl; unsigned int index = lane->index; u32 value; mutex_lock(&padctl->lock); value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM); value &= ~ALL_WAKE_EVENTS; value &= ~SS_PORT_WAKE_INTERRUPT_ENABLE(index); padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM); usleep_range(10, 20); value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM); value &= ~ALL_WAKE_EVENTS; value |= SS_PORT_WAKEUP_EVENT(index); padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM); mutex_unlock(&padctl->lock); return 0; } static bool tegra186_usb3_phy_remote_wake_detected(struct tegra_xusb_lane *lane) { struct tegra_xusb_padctl *padctl = lane->pad->padctl; unsigned int index = lane->index; u32 value; value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM); if ((value & SS_PORT_WAKE_INTERRUPT_ENABLE(index)) && (value & SS_PORT_WAKEUP_EVENT(index))) return true; return false; } static const struct tegra_xusb_lane_ops tegra186_usb3_lane_ops = { .probe = tegra186_usb3_lane_probe, .remove = tegra186_usb3_lane_remove, .enable_phy_sleepwalk = tegra186_usb3_enable_phy_sleepwalk, .disable_phy_sleepwalk = tegra186_usb3_disable_phy_sleepwalk, .enable_phy_wake = tegra186_usb3_enable_phy_wake, .disable_phy_wake = tegra186_usb3_disable_phy_wake, .remote_wake_detected = tegra186_usb3_phy_remote_wake_detected, }; static int tegra186_usb3_port_enable(struct tegra_xusb_port *port) { return 0; Loading Loading @@ -913,7 +1418,9 @@ static struct tegra_xusb_padctl * tegra186_xusb_padctl_probe(struct device *dev, const struct tegra_xusb_padctl_soc *soc) { struct platform_device *pdev = to_platform_device(dev); struct tegra186_xusb_padctl *priv; struct resource *res; int err; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); Loading @@ -923,6 +1430,11 @@ tegra186_xusb_padctl_probe(struct device *dev, priv->base.dev = dev; priv->base.soc = soc; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ao"); priv->ao_regs = devm_ioremap_resource(dev, res); if (IS_ERR(priv->ao_regs)) return ERR_CAST(priv->ao_regs); err = tegra186_xusb_read_fuse_calibration(priv); if (err < 0) return ERR_PTR(err); Loading @@ -930,6 +1442,40 @@ tegra186_xusb_padctl_probe(struct device *dev, return &priv->base; } static void tegra186_xusb_padctl_save(struct tegra_xusb_padctl *padctl) { struct tegra186_xusb_padctl *priv = to_tegra186_xusb_padctl(padctl); priv->context.vbus_id = padctl_readl(padctl, USB2_VBUS_ID); 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_cap = padctl_readl(padctl, XUSB_PADCTL_SS_PORT_CAP); } static void tegra186_xusb_padctl_restore(struct tegra_xusb_padctl *padctl) { struct tegra186_xusb_padctl *priv = to_tegra186_xusb_padctl(padctl); 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_cap, XUSB_PADCTL_SS_PORT_CAP); padctl_writel(padctl, priv->context.vbus_id, USB2_VBUS_ID); } static int tegra186_xusb_padctl_suspend_noirq(struct tegra_xusb_padctl *padctl) { tegra186_xusb_padctl_save(padctl); return 0; } static int tegra186_xusb_padctl_resume_noirq(struct tegra_xusb_padctl *padctl) { tegra186_xusb_padctl_restore(padctl); return 0; } static void tegra186_xusb_padctl_remove(struct tegra_xusb_padctl *padctl) { } Loading @@ -937,6 +1483,8 @@ static void tegra186_xusb_padctl_remove(struct tegra_xusb_padctl *padctl) static const struct tegra_xusb_padctl_ops tegra186_xusb_padctl_ops = { .probe = tegra186_xusb_padctl_probe, .remove = tegra186_xusb_padctl_remove, .suspend_noirq = tegra186_xusb_padctl_suspend_noirq, .resume_noirq = tegra186_xusb_padctl_resume_noirq, .vbus_override = tegra186_xusb_padctl_vbus_override, }; Loading drivers/phy/tegra/xusb-tegra210.c +1460 −417 File changed.Preview size limit exceeded, changes collapsed. Show changes drivers/phy/tegra/xusb.c +90 −2 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2014-2016, NVIDIA CORPORATION. All rights reserved. * Copyright (c) 2014-2020, NVIDIA CORPORATION. All rights reserved. */ #include <linux/delay.h> Loading Loading @@ -321,11 +321,17 @@ static void tegra_xusb_lane_program(struct tegra_xusb_lane *lane) if (soc->num_funcs < 2) return; if (lane->pad->ops->iddq_enable) lane->pad->ops->iddq_enable(lane); /* choose function */ value = padctl_readl(padctl, soc->offset); value &= ~(soc->mask << soc->shift); value |= lane->function << soc->shift; padctl_writel(padctl, value, soc->offset); if (lane->pad->ops->iddq_disable) lane->pad->ops->iddq_disable(lane); } static void tegra_xusb_pad_program(struct tegra_xusb_pad *pad) Loading Loading @@ -376,7 +382,7 @@ static int tegra_xusb_setup_pads(struct tegra_xusb_padctl *padctl) return 0; } static bool tegra_xusb_lane_check(struct tegra_xusb_lane *lane, bool tegra_xusb_lane_check(struct tegra_xusb_lane *lane, const char *function) { const char *func = lane->soc->funcs[lane->function]; Loading Loading @@ -1267,10 +1273,36 @@ static int tegra_xusb_padctl_remove(struct platform_device *pdev) return err; } static int tegra_xusb_padctl_suspend_noirq(struct device *dev) { struct tegra_xusb_padctl *padctl = dev_get_drvdata(dev); if (padctl->soc && padctl->soc->ops && padctl->soc->ops->suspend_noirq) return padctl->soc->ops->suspend_noirq(padctl); return 0; } static int tegra_xusb_padctl_resume_noirq(struct device *dev) { struct tegra_xusb_padctl *padctl = dev_get_drvdata(dev); if (padctl->soc && padctl->soc->ops && padctl->soc->ops->resume_noirq) return padctl->soc->ops->resume_noirq(padctl); return 0; } static const struct dev_pm_ops tegra_xusb_padctl_pm_ops = { SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(tegra_xusb_padctl_suspend_noirq, tegra_xusb_padctl_resume_noirq) }; static struct platform_driver tegra_xusb_padctl_driver = { .driver = { .name = "tegra-xusb-padctl", .of_match_table = tegra_xusb_padctl_of_match, .pm = &tegra_xusb_padctl_pm_ops, }, .probe = tegra_xusb_padctl_probe, .remove = tegra_xusb_padctl_remove, Loading Loading @@ -1337,6 +1369,62 @@ int tegra_xusb_padctl_hsic_set_idle(struct tegra_xusb_padctl *padctl, } EXPORT_SYMBOL_GPL(tegra_xusb_padctl_hsic_set_idle); int tegra_xusb_padctl_enable_phy_sleepwalk(struct tegra_xusb_padctl *padctl, struct phy *phy, enum usb_device_speed speed) { struct tegra_xusb_lane *lane = phy_get_drvdata(phy); if (lane->pad->ops->enable_phy_sleepwalk) return lane->pad->ops->enable_phy_sleepwalk(lane, speed); return -EOPNOTSUPP; } EXPORT_SYMBOL_GPL(tegra_xusb_padctl_enable_phy_sleepwalk); int tegra_xusb_padctl_disable_phy_sleepwalk(struct tegra_xusb_padctl *padctl, struct phy *phy) { struct tegra_xusb_lane *lane = phy_get_drvdata(phy); if (lane->pad->ops->disable_phy_sleepwalk) return lane->pad->ops->disable_phy_sleepwalk(lane); return -EOPNOTSUPP; } EXPORT_SYMBOL_GPL(tegra_xusb_padctl_disable_phy_sleepwalk); int tegra_xusb_padctl_enable_phy_wake(struct tegra_xusb_padctl *padctl, struct phy *phy) { struct tegra_xusb_lane *lane = phy_get_drvdata(phy); if (lane->pad->ops->enable_phy_wake) return lane->pad->ops->enable_phy_wake(lane); return -EOPNOTSUPP; } EXPORT_SYMBOL_GPL(tegra_xusb_padctl_enable_phy_wake); int tegra_xusb_padctl_disable_phy_wake(struct tegra_xusb_padctl *padctl, struct phy *phy) { struct tegra_xusb_lane *lane = phy_get_drvdata(phy); if (lane->pad->ops->disable_phy_wake) return lane->pad->ops->disable_phy_wake(lane); return -EOPNOTSUPP; } EXPORT_SYMBOL_GPL(tegra_xusb_padctl_disable_phy_wake); bool tegra_xusb_padctl_remote_wake_detected(struct tegra_xusb_padctl *padctl, struct phy *phy) { struct tegra_xusb_lane *lane = phy_get_drvdata(phy); if (lane->pad->ops->remote_wake_detected) return lane->pad->ops->remote_wake_detected(lane); return false; } EXPORT_SYMBOL_GPL(tegra_xusb_padctl_remote_wake_detected); int tegra_xusb_padctl_usb3_set_lfps_detect(struct tegra_xusb_padctl *padctl, unsigned int port, bool enable) { Loading drivers/phy/tegra/xusb.h +19 −3 Original line number Diff line number Diff line /* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright (c) 2014-2015, NVIDIA CORPORATION. All rights reserved. * Copyright (c) 2014-2020, NVIDIA CORPORATION. All rights reserved. * Copyright (c) 2015, Google Inc. */ Loading @@ -11,6 +11,7 @@ #include <linux/mutex.h> #include <linux/workqueue.h> #include <linux/usb/ch9.h> #include <linux/usb/otg.h> #include <linux/usb/role.h> Loading @@ -35,6 +36,10 @@ struct tegra_xusb_lane_soc { const char * const *funcs; unsigned int num_funcs; struct { unsigned int misc_ctl2; } regs; }; struct tegra_xusb_lane { Loading Loading @@ -126,8 +131,17 @@ struct tegra_xusb_lane_ops { struct device_node *np, unsigned int index); void (*remove)(struct tegra_xusb_lane *lane); void (*iddq_enable)(struct tegra_xusb_lane *lane); void (*iddq_disable)(struct tegra_xusb_lane *lane); int (*enable_phy_sleepwalk)(struct tegra_xusb_lane *lane, enum usb_device_speed speed); int (*disable_phy_sleepwalk)(struct tegra_xusb_lane *lane); int (*enable_phy_wake)(struct tegra_xusb_lane *lane); int (*disable_phy_wake)(struct tegra_xusb_lane *lane); bool (*remote_wake_detected)(struct tegra_xusb_lane *lane); }; bool tegra_xusb_lane_check(struct tegra_xusb_lane *lane, const char *function); /* * pads */ Loading Loading @@ -230,7 +244,7 @@ struct tegra_xusb_pcie_pad { struct reset_control *rst; struct clk *pll; unsigned int enable; bool enable; }; static inline struct tegra_xusb_pcie_pad * Loading @@ -245,7 +259,7 @@ struct tegra_xusb_sata_pad { struct reset_control *rst; struct clk *pll; unsigned int enable; bool enable; }; static inline struct tegra_xusb_sata_pad * Loading Loading @@ -388,6 +402,8 @@ struct tegra_xusb_padctl_ops { const struct tegra_xusb_padctl_soc *soc); void (*remove)(struct tegra_xusb_padctl *padctl); int (*suspend_noirq)(struct tegra_xusb_padctl *padctl); int (*resume_noirq)(struct tegra_xusb_padctl *padctl); int (*usb3_save_context)(struct tegra_xusb_padctl *padctl, unsigned int index); int (*hsic_set_idle)(struct tegra_xusb_padctl *padctl, Loading include/linux/phy/tegra/xusb.h +9 −1 Original line number Diff line number Diff line /* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. * Copyright (c) 2016-2020, NVIDIA CORPORATION. All rights reserved. */ #ifndef PHY_TEGRA_XUSB_H Loading @@ -8,6 +8,7 @@ struct tegra_xusb_padctl; struct device; enum usb_device_speed; struct tegra_xusb_padctl *tegra_xusb_padctl_get(struct device *dev); void tegra_xusb_padctl_put(struct tegra_xusb_padctl *padctl); Loading @@ -23,4 +24,11 @@ int tegra_xusb_padctl_set_vbus_override(struct tegra_xusb_padctl *padctl, int tegra_phy_xusb_utmi_port_reset(struct phy *phy); int tegra_xusb_padctl_get_usb3_companion(struct tegra_xusb_padctl *padctl, unsigned int port); int tegra_xusb_padctl_enable_phy_sleepwalk(struct tegra_xusb_padctl *padctl, struct phy *phy, enum usb_device_speed speed); int tegra_xusb_padctl_disable_phy_sleepwalk(struct tegra_xusb_padctl *padctl, struct phy *phy); int tegra_xusb_padctl_enable_phy_wake(struct tegra_xusb_padctl *padctl, struct phy *phy); int tegra_xusb_padctl_disable_phy_wake(struct tegra_xusb_padctl *padctl, struct phy *phy); bool tegra_xusb_padctl_remote_wake_detected(struct tegra_xusb_padctl *padctl, struct phy *phy); #endif /* PHY_TEGRA_XUSB_H */ Loading
drivers/phy/tegra/xusb-tegra186.c +549 −1 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2016-2019, NVIDIA CORPORATION. All rights reserved. * Copyright (c) 2016-2020, NVIDIA CORPORATION. All rights reserved. */ #include <linux/delay.h> Loading Loading @@ -113,6 +113,117 @@ #define ID_OVERRIDE_FLOATING ID_OVERRIDE(8) #define ID_OVERRIDE_GROUNDED ID_OVERRIDE(0) /* XUSB AO registers */ #define XUSB_AO_USB_DEBOUNCE_DEL (0x4) #define UHSIC_LINE_DEB_CNT(x) (((x) & 0xf) << 4) #define UTMIP_LINE_DEB_CNT(x) ((x) & 0xf) #define XUSB_AO_UTMIP_TRIGGERS(x) (0x40 + (x) * 4) #define CLR_WALK_PTR BIT(0) #define CAP_CFG BIT(1) #define CLR_WAKE_ALARM BIT(3) #define XUSB_AO_UHSIC_TRIGGERS(x) (0x60 + (x) * 4) #define HSIC_CLR_WALK_PTR BIT(0) #define HSIC_CLR_WAKE_ALARM BIT(3) #define HSIC_CAP_CFG BIT(4) #define XUSB_AO_UTMIP_SAVED_STATE(x) (0x70 + (x) * 4) #define SPEED(x) ((x) & 0x3) #define UTMI_HS SPEED(0) #define UTMI_FS SPEED(1) #define UTMI_LS SPEED(2) #define UTMI_RST SPEED(3) #define XUSB_AO_UHSIC_SAVED_STATE(x) (0x90 + (x) * 4) #define MODE(x) ((x) & 0x1) #define MODE_HS MODE(0) #define MODE_RST MODE(1) #define XUSB_AO_UTMIP_SLEEPWALK_CFG(x) (0xd0 + (x) * 4) #define XUSB_AO_UHSIC_SLEEPWALK_CFG(x) (0xf0 + (x) * 4) #define FAKE_USBOP_VAL BIT(0) #define FAKE_USBON_VAL BIT(1) #define FAKE_USBOP_EN BIT(2) #define FAKE_USBON_EN BIT(3) #define FAKE_STROBE_VAL BIT(0) #define FAKE_DATA_VAL BIT(1) #define FAKE_STROBE_EN BIT(2) #define FAKE_DATA_EN BIT(3) #define WAKE_WALK_EN BIT(14) #define MASTER_ENABLE BIT(15) #define LINEVAL_WALK_EN BIT(16) #define WAKE_VAL(x) (((x) & 0xf) << 17) #define WAKE_VAL_NONE WAKE_VAL(12) #define WAKE_VAL_ANY WAKE_VAL(15) #define WAKE_VAL_DS10 WAKE_VAL(2) #define LINE_WAKEUP_EN BIT(21) #define MASTER_CFG_SEL BIT(22) #define XUSB_AO_UTMIP_SLEEPWALK(x) (0x100 + (x) * 4) /* phase A */ #define USBOP_RPD_A BIT(0) #define USBON_RPD_A BIT(1) #define AP_A BIT(4) #define AN_A BIT(5) #define HIGHZ_A BIT(6) /* phase B */ #define USBOP_RPD_B BIT(8) #define USBON_RPD_B BIT(9) #define AP_B BIT(12) #define AN_B BIT(13) #define HIGHZ_B BIT(14) /* phase C */ #define USBOP_RPD_C BIT(16) #define USBON_RPD_C BIT(17) #define AP_C BIT(20) #define AN_C BIT(21) #define HIGHZ_C BIT(22) /* phase D */ #define USBOP_RPD_D BIT(24) #define USBON_RPD_D BIT(25) #define AP_D BIT(28) #define AN_D BIT(29) #define HIGHZ_D BIT(30) #define XUSB_AO_UHSIC_SLEEPWALK(x) (0x120 + (x) * 4) /* phase A */ #define RPD_STROBE_A BIT(0) #define RPD_DATA0_A BIT(1) #define RPU_STROBE_A BIT(2) #define RPU_DATA0_A BIT(3) /* phase B */ #define RPD_STROBE_B BIT(8) #define RPD_DATA0_B BIT(9) #define RPU_STROBE_B BIT(10) #define RPU_DATA0_B BIT(11) /* phase C */ #define RPD_STROBE_C BIT(16) #define RPD_DATA0_C BIT(17) #define RPU_STROBE_C BIT(18) #define RPU_DATA0_C BIT(19) /* phase D */ #define RPD_STROBE_D BIT(24) #define RPD_DATA0_D BIT(25) #define RPU_STROBE_D BIT(26) #define RPU_DATA0_D BIT(27) #define XUSB_AO_UTMIP_PAD_CFG(x) (0x130 + (x) * 4) #define FSLS_USE_XUSB_AO BIT(3) #define TRK_CTRL_USE_XUSB_AO BIT(4) #define RPD_CTRL_USE_XUSB_AO BIT(5) #define RPU_USE_XUSB_AO BIT(6) #define VREG_USE_XUSB_AO BIT(7) #define USBOP_VAL_PD BIT(8) #define USBON_VAL_PD BIT(9) #define E_DPD_OVRD_EN BIT(10) #define E_DPD_OVRD_VAL BIT(11) #define XUSB_AO_UHSIC_PAD_CFG(x) (0x150 + (x) * 4) #define STROBE_VAL_PD BIT(0) #define DATA0_VAL_PD BIT(1) #define USE_XUSB_AO BIT(4) #define TEGRA186_LANE(_name, _offset, _shift, _mask, _type) \ { \ .name = _name, \ Loading @@ -130,16 +241,37 @@ struct tegra_xusb_fuse_calibration { u32 rpd_ctrl; }; struct tegra186_xusb_padctl_context { u32 vbus_id; u32 usb2_pad_mux; u32 usb2_port_cap; u32 ss_port_cap; }; struct tegra186_xusb_padctl { struct tegra_xusb_padctl base; void __iomem *ao_regs; struct tegra_xusb_fuse_calibration calib; /* UTMI bias and tracking */ struct clk *usb2_trk_clk; unsigned int bias_pad_enable; /* padctl context */ struct tegra186_xusb_padctl_context context; }; static inline void ao_writel(struct tegra186_xusb_padctl *priv, u32 value, unsigned int offset) { writel(value, priv->ao_regs + offset); } static inline u32 ao_readl(struct tegra186_xusb_padctl *priv, unsigned int offset) { return readl(priv->ao_regs + offset); } static inline struct tegra186_xusb_padctl * to_tegra186_xusb_padctl(struct tegra_xusb_padctl *padctl) { Loading Loading @@ -180,9 +312,264 @@ static void tegra186_usb2_lane_remove(struct tegra_xusb_lane *lane) kfree(usb2); } static int tegra186_utmi_enable_phy_sleepwalk(struct tegra_xusb_lane *lane, enum usb_device_speed speed) { struct tegra_xusb_padctl *padctl = lane->pad->padctl; struct tegra186_xusb_padctl *priv = to_tegra186_xusb_padctl(padctl); unsigned int index = lane->index; u32 value; mutex_lock(&padctl->lock); /* ensure sleepwalk logic is disabled */ value = ao_readl(priv, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); value &= ~MASTER_ENABLE; ao_writel(priv, value, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); /* ensure sleepwalk logics are in low power mode */ value = ao_readl(priv, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); value |= MASTER_CFG_SEL; ao_writel(priv, value, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); /* set debounce time */ value = ao_readl(priv, XUSB_AO_USB_DEBOUNCE_DEL); value &= ~UTMIP_LINE_DEB_CNT(~0); value |= UTMIP_LINE_DEB_CNT(1); ao_writel(priv, value, XUSB_AO_USB_DEBOUNCE_DEL); /* ensure fake events of sleepwalk logic are desiabled */ value = ao_readl(priv, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); value &= ~(FAKE_USBOP_VAL | FAKE_USBON_VAL | FAKE_USBOP_EN | FAKE_USBON_EN); ao_writel(priv, value, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); /* ensure wake events of sleepwalk logic are not latched */ value = ao_readl(priv, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); value &= ~LINE_WAKEUP_EN; ao_writel(priv, value, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); /* disable wake event triggers of sleepwalk logic */ value = ao_readl(priv, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); value &= ~WAKE_VAL(~0); value |= WAKE_VAL_NONE; ao_writel(priv, value, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); /* power down the line state detectors of the pad */ value = ao_readl(priv, XUSB_AO_UTMIP_PAD_CFG(index)); value |= (USBOP_VAL_PD | USBON_VAL_PD); ao_writel(priv, value, XUSB_AO_UTMIP_PAD_CFG(index)); /* save state per speed */ value = ao_readl(priv, XUSB_AO_UTMIP_SAVED_STATE(index)); value &= ~SPEED(~0); switch (speed) { case USB_SPEED_HIGH: value |= UTMI_HS; break; case USB_SPEED_FULL: value |= UTMI_FS; break; case USB_SPEED_LOW: value |= UTMI_LS; break; default: value |= UTMI_RST; break; } ao_writel(priv, value, XUSB_AO_UTMIP_SAVED_STATE(index)); /* enable the trigger of the sleepwalk logic */ value = ao_readl(priv, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); value |= LINEVAL_WALK_EN; value &= ~WAKE_WALK_EN; ao_writel(priv, value, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); /* reset the walk pointer and clear the alarm of the sleepwalk logic, * as well as capture the configuration of the USB2.0 pad */ value = ao_readl(priv, XUSB_AO_UTMIP_TRIGGERS(index)); value |= (CLR_WALK_PTR | CLR_WAKE_ALARM | CAP_CFG); ao_writel(priv, value, XUSB_AO_UTMIP_TRIGGERS(index)); /* 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. */ value = USBOP_RPD_A | USBOP_RPD_B | USBOP_RPD_C | USBOP_RPD_D; value |= USBON_RPD_A | USBON_RPD_B | USBON_RPD_C | USBON_RPD_D; switch (speed) { case USB_SPEED_HIGH: case USB_SPEED_FULL: /* J state: D+/D- = high/low, K state: D+/D- = low/high */ value |= HIGHZ_A; value |= AP_A; value |= AN_B | AN_C | AN_D; break; case USB_SPEED_LOW: /* J state: D+/D- = low/high, K state: D+/D- = high/low */ value |= HIGHZ_A; value |= AN_A; value |= AP_B | AP_C | AP_D; break; default: value |= HIGHZ_A | HIGHZ_B | HIGHZ_C | HIGHZ_D; break; } ao_writel(priv, value, XUSB_AO_UTMIP_SLEEPWALK(index)); /* power up the line state detectors of the pad */ value = ao_readl(priv, XUSB_AO_UTMIP_PAD_CFG(index)); value &= ~(USBOP_VAL_PD | USBON_VAL_PD); ao_writel(priv, value, XUSB_AO_UTMIP_PAD_CFG(index)); usleep_range(150, 200); /* switch the electric control of the USB2.0 pad to XUSB_AO */ value = ao_readl(priv, XUSB_AO_UTMIP_PAD_CFG(index)); value |= FSLS_USE_XUSB_AO | TRK_CTRL_USE_XUSB_AO | RPD_CTRL_USE_XUSB_AO | RPU_USE_XUSB_AO | VREG_USE_XUSB_AO; ao_writel(priv, value, XUSB_AO_UTMIP_PAD_CFG(index)); /* set the wake signaling trigger events */ value = ao_readl(priv, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); value &= ~WAKE_VAL(~0); value |= WAKE_VAL_ANY; ao_writel(priv, value, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); /* enable the wake detection */ value = ao_readl(priv, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); value |= MASTER_ENABLE | LINE_WAKEUP_EN; ao_writel(priv, value, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); mutex_unlock(&padctl->lock); return 0; } static int tegra186_utmi_disable_phy_sleepwalk(struct tegra_xusb_lane *lane) { struct tegra_xusb_padctl *padctl = lane->pad->padctl; struct tegra186_xusb_padctl *priv = to_tegra186_xusb_padctl(padctl); unsigned int index = lane->index; u32 value; mutex_lock(&padctl->lock); /* disable the wake detection */ value = ao_readl(priv, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); value &= ~(MASTER_ENABLE | LINE_WAKEUP_EN); ao_writel(priv, value, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); /* switch the electric control of the USB2.0 pad to XUSB vcore logic */ value = ao_readl(priv, XUSB_AO_UTMIP_PAD_CFG(index)); value &= ~(FSLS_USE_XUSB_AO | TRK_CTRL_USE_XUSB_AO | RPD_CTRL_USE_XUSB_AO | RPU_USE_XUSB_AO | VREG_USE_XUSB_AO); ao_writel(priv, value, XUSB_AO_UTMIP_PAD_CFG(index)); /* disable wake event triggers of sleepwalk logic */ value = ao_readl(priv, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); value &= ~WAKE_VAL(~0); value |= WAKE_VAL_NONE; ao_writel(priv, value, XUSB_AO_UTMIP_SLEEPWALK_CFG(index)); /* power down the line state detectors of the port */ value = ao_readl(priv, XUSB_AO_UTMIP_PAD_CFG(index)); value |= USBOP_VAL_PD | USBON_VAL_PD; ao_writel(priv, value, XUSB_AO_UTMIP_PAD_CFG(index)); /* clear alarm of the sleepwalk logic */ value = ao_readl(priv, XUSB_AO_UTMIP_TRIGGERS(index)); value |= CLR_WAKE_ALARM; ao_writel(priv, value, XUSB_AO_UTMIP_TRIGGERS(index)); mutex_unlock(&padctl->lock); return 0; } static int tegra186_utmi_enable_phy_wake(struct tegra_xusb_lane *lane) { struct tegra_xusb_padctl *padctl = lane->pad->padctl; unsigned int index = lane->index; u32 value; mutex_lock(&padctl->lock); value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM); value &= ~ALL_WAKE_EVENTS; value |= USB2_PORT_WAKEUP_EVENT(index); padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM); usleep_range(10, 20); value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM); value &= ~ALL_WAKE_EVENTS; value |= USB2_PORT_WAKE_INTERRUPT_ENABLE(index); padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM); mutex_unlock(&padctl->lock); return 0; } static int tegra186_utmi_disable_phy_wake(struct tegra_xusb_lane *lane) { struct tegra_xusb_padctl *padctl = lane->pad->padctl; unsigned int index = lane->index; u32 value; mutex_lock(&padctl->lock); value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM); value &= ~ALL_WAKE_EVENTS; value &= ~USB2_PORT_WAKE_INTERRUPT_ENABLE(index); padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM); usleep_range(10, 20); value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM); value &= ~ALL_WAKE_EVENTS; value |= USB2_PORT_WAKEUP_EVENT(index); padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM); mutex_unlock(&padctl->lock); return 0; } static bool tegra186_utmi_phy_remote_wake_detected(struct tegra_xusb_lane *lane) { struct tegra_xusb_padctl *padctl = lane->pad->padctl; unsigned int index = lane->index; u32 value; value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM); if ((value & USB2_PORT_WAKE_INTERRUPT_ENABLE(index)) && (value & USB2_PORT_WAKEUP_EVENT(index))) return true; return false; } static const struct tegra_xusb_lane_ops tegra186_usb2_lane_ops = { .probe = tegra186_usb2_lane_probe, .remove = tegra186_usb2_lane_remove, .enable_phy_sleepwalk = tegra186_utmi_enable_phy_sleepwalk, .disable_phy_sleepwalk = tegra186_utmi_disable_phy_sleepwalk, .enable_phy_wake = tegra186_utmi_enable_phy_wake, .disable_phy_wake = tegra186_utmi_disable_phy_wake, .remote_wake_detected = tegra186_utmi_phy_remote_wake_detected, }; static void tegra186_utmi_bias_pad_power_on(struct tegra_xusb_padctl *padctl) Loading Loading @@ -656,10 +1043,128 @@ static void tegra186_usb3_lane_remove(struct tegra_xusb_lane *lane) kfree(usb3); } static int tegra186_usb3_enable_phy_sleepwalk(struct tegra_xusb_lane *lane, enum usb_device_speed speed) { struct tegra_xusb_padctl *padctl = lane->pad->padctl; unsigned int index = lane->index; u32 value; mutex_lock(&padctl->lock); value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_1); value |= SSPX_ELPG_CLAMP_EN_EARLY(index); padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_1); usleep_range(100, 200); value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_1); value |= SSPX_ELPG_CLAMP_EN(index); padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_1); usleep_range(250, 350); mutex_unlock(&padctl->lock); return 0; } static int tegra186_usb3_disable_phy_sleepwalk(struct tegra_xusb_lane *lane) { struct tegra_xusb_padctl *padctl = lane->pad->padctl; unsigned int index = lane->index; u32 value; mutex_lock(&padctl->lock); value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_1); value &= ~SSPX_ELPG_CLAMP_EN_EARLY(index); padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_1); usleep_range(100, 200); value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_1); value &= ~SSPX_ELPG_CLAMP_EN(index); padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_1); mutex_unlock(&padctl->lock); return 0; } static int tegra186_usb3_enable_phy_wake(struct tegra_xusb_lane *lane) { struct tegra_xusb_padctl *padctl = lane->pad->padctl; unsigned int index = lane->index; u32 value; mutex_lock(&padctl->lock); value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM); value &= ~ALL_WAKE_EVENTS; value |= SS_PORT_WAKEUP_EVENT(index); padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM); usleep_range(10, 20); value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM); value &= ~ALL_WAKE_EVENTS; value |= SS_PORT_WAKE_INTERRUPT_ENABLE(index); padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM); mutex_unlock(&padctl->lock); return 0; } static int tegra186_usb3_disable_phy_wake(struct tegra_xusb_lane *lane) { struct tegra_xusb_padctl *padctl = lane->pad->padctl; unsigned int index = lane->index; u32 value; mutex_lock(&padctl->lock); value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM); value &= ~ALL_WAKE_EVENTS; value &= ~SS_PORT_WAKE_INTERRUPT_ENABLE(index); padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM); usleep_range(10, 20); value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM); value &= ~ALL_WAKE_EVENTS; value |= SS_PORT_WAKEUP_EVENT(index); padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM); mutex_unlock(&padctl->lock); return 0; } static bool tegra186_usb3_phy_remote_wake_detected(struct tegra_xusb_lane *lane) { struct tegra_xusb_padctl *padctl = lane->pad->padctl; unsigned int index = lane->index; u32 value; value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM); if ((value & SS_PORT_WAKE_INTERRUPT_ENABLE(index)) && (value & SS_PORT_WAKEUP_EVENT(index))) return true; return false; } static const struct tegra_xusb_lane_ops tegra186_usb3_lane_ops = { .probe = tegra186_usb3_lane_probe, .remove = tegra186_usb3_lane_remove, .enable_phy_sleepwalk = tegra186_usb3_enable_phy_sleepwalk, .disable_phy_sleepwalk = tegra186_usb3_disable_phy_sleepwalk, .enable_phy_wake = tegra186_usb3_enable_phy_wake, .disable_phy_wake = tegra186_usb3_disable_phy_wake, .remote_wake_detected = tegra186_usb3_phy_remote_wake_detected, }; static int tegra186_usb3_port_enable(struct tegra_xusb_port *port) { return 0; Loading Loading @@ -913,7 +1418,9 @@ static struct tegra_xusb_padctl * tegra186_xusb_padctl_probe(struct device *dev, const struct tegra_xusb_padctl_soc *soc) { struct platform_device *pdev = to_platform_device(dev); struct tegra186_xusb_padctl *priv; struct resource *res; int err; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); Loading @@ -923,6 +1430,11 @@ tegra186_xusb_padctl_probe(struct device *dev, priv->base.dev = dev; priv->base.soc = soc; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ao"); priv->ao_regs = devm_ioremap_resource(dev, res); if (IS_ERR(priv->ao_regs)) return ERR_CAST(priv->ao_regs); err = tegra186_xusb_read_fuse_calibration(priv); if (err < 0) return ERR_PTR(err); Loading @@ -930,6 +1442,40 @@ tegra186_xusb_padctl_probe(struct device *dev, return &priv->base; } static void tegra186_xusb_padctl_save(struct tegra_xusb_padctl *padctl) { struct tegra186_xusb_padctl *priv = to_tegra186_xusb_padctl(padctl); priv->context.vbus_id = padctl_readl(padctl, USB2_VBUS_ID); 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_cap = padctl_readl(padctl, XUSB_PADCTL_SS_PORT_CAP); } static void tegra186_xusb_padctl_restore(struct tegra_xusb_padctl *padctl) { struct tegra186_xusb_padctl *priv = to_tegra186_xusb_padctl(padctl); 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_cap, XUSB_PADCTL_SS_PORT_CAP); padctl_writel(padctl, priv->context.vbus_id, USB2_VBUS_ID); } static int tegra186_xusb_padctl_suspend_noirq(struct tegra_xusb_padctl *padctl) { tegra186_xusb_padctl_save(padctl); return 0; } static int tegra186_xusb_padctl_resume_noirq(struct tegra_xusb_padctl *padctl) { tegra186_xusb_padctl_restore(padctl); return 0; } static void tegra186_xusb_padctl_remove(struct tegra_xusb_padctl *padctl) { } Loading @@ -937,6 +1483,8 @@ static void tegra186_xusb_padctl_remove(struct tegra_xusb_padctl *padctl) static const struct tegra_xusb_padctl_ops tegra186_xusb_padctl_ops = { .probe = tegra186_xusb_padctl_probe, .remove = tegra186_xusb_padctl_remove, .suspend_noirq = tegra186_xusb_padctl_suspend_noirq, .resume_noirq = tegra186_xusb_padctl_resume_noirq, .vbus_override = tegra186_xusb_padctl_vbus_override, }; Loading
drivers/phy/tegra/xusb-tegra210.c +1460 −417 File changed.Preview size limit exceeded, changes collapsed. Show changes
drivers/phy/tegra/xusb.c +90 −2 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2014-2016, NVIDIA CORPORATION. All rights reserved. * Copyright (c) 2014-2020, NVIDIA CORPORATION. All rights reserved. */ #include <linux/delay.h> Loading Loading @@ -321,11 +321,17 @@ static void tegra_xusb_lane_program(struct tegra_xusb_lane *lane) if (soc->num_funcs < 2) return; if (lane->pad->ops->iddq_enable) lane->pad->ops->iddq_enable(lane); /* choose function */ value = padctl_readl(padctl, soc->offset); value &= ~(soc->mask << soc->shift); value |= lane->function << soc->shift; padctl_writel(padctl, value, soc->offset); if (lane->pad->ops->iddq_disable) lane->pad->ops->iddq_disable(lane); } static void tegra_xusb_pad_program(struct tegra_xusb_pad *pad) Loading Loading @@ -376,7 +382,7 @@ static int tegra_xusb_setup_pads(struct tegra_xusb_padctl *padctl) return 0; } static bool tegra_xusb_lane_check(struct tegra_xusb_lane *lane, bool tegra_xusb_lane_check(struct tegra_xusb_lane *lane, const char *function) { const char *func = lane->soc->funcs[lane->function]; Loading Loading @@ -1267,10 +1273,36 @@ static int tegra_xusb_padctl_remove(struct platform_device *pdev) return err; } static int tegra_xusb_padctl_suspend_noirq(struct device *dev) { struct tegra_xusb_padctl *padctl = dev_get_drvdata(dev); if (padctl->soc && padctl->soc->ops && padctl->soc->ops->suspend_noirq) return padctl->soc->ops->suspend_noirq(padctl); return 0; } static int tegra_xusb_padctl_resume_noirq(struct device *dev) { struct tegra_xusb_padctl *padctl = dev_get_drvdata(dev); if (padctl->soc && padctl->soc->ops && padctl->soc->ops->resume_noirq) return padctl->soc->ops->resume_noirq(padctl); return 0; } static const struct dev_pm_ops tegra_xusb_padctl_pm_ops = { SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(tegra_xusb_padctl_suspend_noirq, tegra_xusb_padctl_resume_noirq) }; static struct platform_driver tegra_xusb_padctl_driver = { .driver = { .name = "tegra-xusb-padctl", .of_match_table = tegra_xusb_padctl_of_match, .pm = &tegra_xusb_padctl_pm_ops, }, .probe = tegra_xusb_padctl_probe, .remove = tegra_xusb_padctl_remove, Loading Loading @@ -1337,6 +1369,62 @@ int tegra_xusb_padctl_hsic_set_idle(struct tegra_xusb_padctl *padctl, } EXPORT_SYMBOL_GPL(tegra_xusb_padctl_hsic_set_idle); int tegra_xusb_padctl_enable_phy_sleepwalk(struct tegra_xusb_padctl *padctl, struct phy *phy, enum usb_device_speed speed) { struct tegra_xusb_lane *lane = phy_get_drvdata(phy); if (lane->pad->ops->enable_phy_sleepwalk) return lane->pad->ops->enable_phy_sleepwalk(lane, speed); return -EOPNOTSUPP; } EXPORT_SYMBOL_GPL(tegra_xusb_padctl_enable_phy_sleepwalk); int tegra_xusb_padctl_disable_phy_sleepwalk(struct tegra_xusb_padctl *padctl, struct phy *phy) { struct tegra_xusb_lane *lane = phy_get_drvdata(phy); if (lane->pad->ops->disable_phy_sleepwalk) return lane->pad->ops->disable_phy_sleepwalk(lane); return -EOPNOTSUPP; } EXPORT_SYMBOL_GPL(tegra_xusb_padctl_disable_phy_sleepwalk); int tegra_xusb_padctl_enable_phy_wake(struct tegra_xusb_padctl *padctl, struct phy *phy) { struct tegra_xusb_lane *lane = phy_get_drvdata(phy); if (lane->pad->ops->enable_phy_wake) return lane->pad->ops->enable_phy_wake(lane); return -EOPNOTSUPP; } EXPORT_SYMBOL_GPL(tegra_xusb_padctl_enable_phy_wake); int tegra_xusb_padctl_disable_phy_wake(struct tegra_xusb_padctl *padctl, struct phy *phy) { struct tegra_xusb_lane *lane = phy_get_drvdata(phy); if (lane->pad->ops->disable_phy_wake) return lane->pad->ops->disable_phy_wake(lane); return -EOPNOTSUPP; } EXPORT_SYMBOL_GPL(tegra_xusb_padctl_disable_phy_wake); bool tegra_xusb_padctl_remote_wake_detected(struct tegra_xusb_padctl *padctl, struct phy *phy) { struct tegra_xusb_lane *lane = phy_get_drvdata(phy); if (lane->pad->ops->remote_wake_detected) return lane->pad->ops->remote_wake_detected(lane); return false; } EXPORT_SYMBOL_GPL(tegra_xusb_padctl_remote_wake_detected); int tegra_xusb_padctl_usb3_set_lfps_detect(struct tegra_xusb_padctl *padctl, unsigned int port, bool enable) { Loading
drivers/phy/tegra/xusb.h +19 −3 Original line number Diff line number Diff line /* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright (c) 2014-2015, NVIDIA CORPORATION. All rights reserved. * Copyright (c) 2014-2020, NVIDIA CORPORATION. All rights reserved. * Copyright (c) 2015, Google Inc. */ Loading @@ -11,6 +11,7 @@ #include <linux/mutex.h> #include <linux/workqueue.h> #include <linux/usb/ch9.h> #include <linux/usb/otg.h> #include <linux/usb/role.h> Loading @@ -35,6 +36,10 @@ struct tegra_xusb_lane_soc { const char * const *funcs; unsigned int num_funcs; struct { unsigned int misc_ctl2; } regs; }; struct tegra_xusb_lane { Loading Loading @@ -126,8 +131,17 @@ struct tegra_xusb_lane_ops { struct device_node *np, unsigned int index); void (*remove)(struct tegra_xusb_lane *lane); void (*iddq_enable)(struct tegra_xusb_lane *lane); void (*iddq_disable)(struct tegra_xusb_lane *lane); int (*enable_phy_sleepwalk)(struct tegra_xusb_lane *lane, enum usb_device_speed speed); int (*disable_phy_sleepwalk)(struct tegra_xusb_lane *lane); int (*enable_phy_wake)(struct tegra_xusb_lane *lane); int (*disable_phy_wake)(struct tegra_xusb_lane *lane); bool (*remote_wake_detected)(struct tegra_xusb_lane *lane); }; bool tegra_xusb_lane_check(struct tegra_xusb_lane *lane, const char *function); /* * pads */ Loading Loading @@ -230,7 +244,7 @@ struct tegra_xusb_pcie_pad { struct reset_control *rst; struct clk *pll; unsigned int enable; bool enable; }; static inline struct tegra_xusb_pcie_pad * Loading @@ -245,7 +259,7 @@ struct tegra_xusb_sata_pad { struct reset_control *rst; struct clk *pll; unsigned int enable; bool enable; }; static inline struct tegra_xusb_sata_pad * Loading Loading @@ -388,6 +402,8 @@ struct tegra_xusb_padctl_ops { const struct tegra_xusb_padctl_soc *soc); void (*remove)(struct tegra_xusb_padctl *padctl); int (*suspend_noirq)(struct tegra_xusb_padctl *padctl); int (*resume_noirq)(struct tegra_xusb_padctl *padctl); int (*usb3_save_context)(struct tegra_xusb_padctl *padctl, unsigned int index); int (*hsic_set_idle)(struct tegra_xusb_padctl *padctl, Loading
include/linux/phy/tegra/xusb.h +9 −1 Original line number Diff line number Diff line /* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. * Copyright (c) 2016-2020, NVIDIA CORPORATION. All rights reserved. */ #ifndef PHY_TEGRA_XUSB_H Loading @@ -8,6 +8,7 @@ struct tegra_xusb_padctl; struct device; enum usb_device_speed; struct tegra_xusb_padctl *tegra_xusb_padctl_get(struct device *dev); void tegra_xusb_padctl_put(struct tegra_xusb_padctl *padctl); Loading @@ -23,4 +24,11 @@ int tegra_xusb_padctl_set_vbus_override(struct tegra_xusb_padctl *padctl, int tegra_phy_xusb_utmi_port_reset(struct phy *phy); int tegra_xusb_padctl_get_usb3_companion(struct tegra_xusb_padctl *padctl, unsigned int port); int tegra_xusb_padctl_enable_phy_sleepwalk(struct tegra_xusb_padctl *padctl, struct phy *phy, enum usb_device_speed speed); int tegra_xusb_padctl_disable_phy_sleepwalk(struct tegra_xusb_padctl *padctl, struct phy *phy); int tegra_xusb_padctl_enable_phy_wake(struct tegra_xusb_padctl *padctl, struct phy *phy); int tegra_xusb_padctl_disable_phy_wake(struct tegra_xusb_padctl *padctl, struct phy *phy); bool tegra_xusb_padctl_remote_wake_detected(struct tegra_xusb_padctl *padctl, struct phy *phy); #endif /* PHY_TEGRA_XUSB_H */