From: JC Kuo <jckuo@nvidia.com> Date: 2020-08-31 04:40:39
Tegra XHCI controler can be placed in ELPG (Engine Level PowerGated)
state for power saving when all of the connected USB devices are in
suspended state. This patch series includes clk, phy and pmc changes
that are required for properly place controller in ELPG and bring
controller out of ELPG.
JC Kuo (12):
clk: tegra: Add PLLE HW power sequencer control
v2: no change
clk: tegra: don't enable PLLE HW sequencer at init
v2: no change
phy: tegra: xusb: t210: rearrange UPHY init
v2: no change
phy: tegra: xusb: t210: add lane_iddq operations
v2: no change
phy: tegra: xusb: add sleepwalk and suspend/resume
v2: no change
soc/tegra: pmc: provide usb sleepwalk register map
v2: make tegra_pmc_regmap_readl() and tegra_pmc_regmap_writel()
static
arm64: tegra210: XUSB PADCTL add "nvidia,pmc" prop
v2: no change
phy: tegra: xusb: t210: support wake and sleepwalk
v2: no change
phy: tegra: xusb: t186: support wake and sleepwalk
v2: no change
arm64: tegra210/tegra186/tegra194: XUSB PADCTL irq
v2: no change
usb: host: xhci-tegra: unlink power domain devices
v2: no change
xhci: tegra: enable ELPG for runtime/system PM
v2: no change
arch/arm64/boot/dts/nvidia/tegra186.dtsi | 1 +
arch/arm64/boot/dts/nvidia/tegra194.dtsi | 1 +
arch/arm64/boot/dts/nvidia/tegra210.dtsi | 2 +
drivers/clk/tegra/clk-pll.c | 12 -
drivers/clk/tegra/clk-tegra210.c | 51 +
drivers/phy/tegra/xusb-tegra186.c | 656 ++++++++
drivers/phy/tegra/xusb-tegra210.c | 1953 +++++++++++++++++-----
drivers/phy/tegra/xusb.c | 86 +-
drivers/phy/tegra/xusb.h | 23 +-
drivers/soc/tegra/pmc.c | 89 +
drivers/usb/host/xhci-tegra.c | 577 +++++--
include/linux/clk/tegra.h | 2 +
include/linux/phy/tegra/xusb.h | 13 +
13 files changed, 2957 insertions(+), 509 deletions(-)
--
2.25.1
From: JC Kuo <jckuo@nvidia.com> Date: 2020-08-31 04:40:47
PLLE has a hardware power sequencer logic which is a state machine
that can power on/off PLLE without any software intervention. The
sequencer has two inputs, one from XUSB UPHY PLL and the other from
SATA UPHY PLL. PLLE provides reference clock to XUSB and SATA UPHY
PLLs. When both of the downstream PLLs are powered-off, PLLE hardware
power sequencer will automatically power off PLLE for power saving.
XUSB and SATA UPHY PLLs also have their own hardware power sequencer
logic. XUSB UPHY PLL is shared between XUSB SuperSpeed ports and PCIE
controllers. The XUSB UPHY PLL hardware power sequencer has inputs
from XUSB and PCIE. When all of the XUSB SuperSpeed ports and PCIE
controllers are in low power state, XUSB UPHY PLL hardware power
sequencer automatically power off PLL and flags idle to PLLE hardware
power sequencer. Similar applies to SATA UPHY PLL.
PLLE hardware power sequencer has to be enabled after both downstream
sequencers are enabled.
This commit adds two helper functions:
1. tegra210_plle_hw_sequence_start() for XUSB PADCTL driver to enable
PLLE hardware sequencer at proper time.
2. tegra210_plle_hw_sequence_is_enabled() for XUSB PADCTL driver to
check whether PLLE hardware sequencer has been enabled or not.
Signed-off-by: JC Kuo <jckuo@nvidia.com>
---
drivers/clk/tegra/clk-tegra210.c | 51 ++++++++++++++++++++++++++++++++
include/linux/clk/tegra.h | 2 ++
2 files changed, 53 insertions(+)
From: JC Kuo <jckuo@nvidia.com> Date: 2020-08-31 04:40:54
As per Tegra210 TRM, before changing lane assignments, driver should
keep lanes in IDDQ and sleep state; after changing lane assignments,
driver should bring lanes out of IDDQ.
This commit implements the required operations.
Signed-off-by: JC Kuo <jckuo@nvidia.com>
---
drivers/phy/tegra/xusb-tegra210.c | 94 +++++++++++++++++++++++++++++++
drivers/phy/tegra/xusb.c | 6 ++
drivers/phy/tegra/xusb.h | 4 +-
3 files changed, 103 insertions(+), 1 deletion(-)
From: JC Kuo <jckuo@nvidia.com> Date: 2020-08-31 04:41:02
This commit adds sleepwalk/wake and suspend/resume interfaces
to Tegra XUSB PHY driver.
Tegra XUSB host controller driver makes use of sleepwalk functions
to enable/disable sleepwalk circuit which is in always-on partition
can respond to USB resume signals when controller is not powered.
Sleepwalk can be enabled/disabled for any USB phy individually.
- tegra_xusb_padctl_enable_phy_sleepwalk()
- tegra_xusb_padctl_disable_phy_sleepwalk()
Tegra XUSB host controller driver makes use of wake functions to
enable/disable/query wake circuit which is in always-on partition
can wake system up when USB resume happens.
Wake circuit can be enabled/disabled for any USB phy individually.
- tegra_xusb_padctl_enable_phy_wake()
- tegra_xusb_padctl_disable_phy_wake()
- tegra_xusb_padctl_remote_wake_detected()
This commit also adds two system suspend stubs that can be used to
save and restore XUSB PADCTL context during system suspend and
resume.
- tegra_xusb_padctl_suspend_noirq()
- tegra_xusb_padctl_resume_noirq()
Signed-off-by: JC Kuo <jckuo@nvidia.com>
---
drivers/phy/tegra/xusb.c | 78 ++++++++++++++++++++++++++++++++++
drivers/phy/tegra/xusb.h | 13 ++++++
include/linux/phy/tegra/xusb.h | 13 ++++++
3 files changed, 104 insertions(+)
From: JC Kuo <jckuo@nvidia.com> Date: 2020-08-31 04:41:11
This commit adds "interrupts" property to Tegra210/Tegra186/Tegra194
XUSB PADCTL node. XUSB PADCTL interrupt will be raised when USB wake
event happens. This is required for supporting XUSB host controller
ELPG.
Signed-off-by: JC Kuo <jckuo@nvidia.com>
---
arch/arm64/boot/dts/nvidia/tegra186.dtsi | 1 +
arch/arm64/boot/dts/nvidia/tegra194.dtsi | 1 +
arch/arm64/boot/dts/nvidia/tegra210.dtsi | 1 +
3 files changed, 3 insertions(+)
@@ -475,6 +613,238 @@ static int tegra186_utmi_phy_power_off(struct phy *phy)return0;}+staticinttegra186_utmi_phy_enable_sleepwalk(structphy*phy,+enumusb_device_speedspeed)+{+structtegra_xusb_lane*lane=phy_get_drvdata(phy);+structtegra_xusb_padctl*padctl=lane->pad->padctl;+structtegra186_xusb_padctl*priv=to_tegra186_xusb_padctl(padctl);+unsignedintindex=lane->index;+structdevice*dev=padctl->dev;+u32value;++dev_dbg(dev,"phy enable sleepwalk UTMI %d speed %d\n",index,speed);++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);+if(speed==USB_SPEED_HIGH)+value|=UTMI_HS;+elseif(speed==USB_SPEED_FULL)+value|=UTMI_FS;+elseif(speed==USB_SPEED_LOW)+value|=UTMI_LS;+else+value|=UTMI_RST;+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,+*aswellascapturetheconfigurationoftheUSB2.0pad+*/+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+*stagesofsleepwalk.+*ifdeviceisconnected,programsleepwalklogictomaintainaJand+*keepdrivingKuponseeingremotewake.+*/+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);+if(speed==USB_SPEED_UNKNOWN){+value|=(HIGHZ_A|HIGHZ_B|HIGHZ_C|HIGHZ_D);+}elseif((speed==USB_SPEED_HIGH)||(speed==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);+}elseif(speed==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);+}+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);++return0;+}++staticinttegra186_utmi_phy_disable_sleepwalk(structphy*phy)+{+structtegra_xusb_lane*lane=phy_get_drvdata(phy);+structtegra_xusb_padctl*padctl=lane->pad->padctl;+structtegra186_xusb_padctl*priv=to_tegra186_xusb_padctl(padctl);+unsignedintindex=lane->index;+structdevice*dev=padctl->dev;+u32value;++dev_dbg(dev,"phy disable sleepwalk UTMI %d\n",index);++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);++return0;+}++staticinttegra186_utmi_phy_enable_wake(structphy*phy)+{+structtegra_xusb_lane*lane=phy_get_drvdata(phy);+structtegra_xusb_padctl*padctl=lane->pad->padctl;+unsignedintindex=lane->index;+structdevice*dev=padctl->dev;+u32value;++dev_dbg(dev,"phy enable wake UTMI %d\n",index);++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);++return0;+}++staticinttegra186_utmi_phy_disable_wake(structphy*phy)+{+structtegra_xusb_lane*lane=phy_get_drvdata(phy);+structtegra_xusb_padctl*padctl=lane->pad->padctl;+unsignedintindex=lane->index;+structdevice*dev=padctl->dev;+u32value;++dev_dbg(dev,"phy disable wake UTMI %d\n",index);++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);++return0;+}+staticinttegra186_utmi_phy_init(structphy*phy){structtegra_xusb_lane*lane=phy_get_drvdata(phy);
@@ -886,6 +1051,671 @@ static int tegra210_hsic_set_idle(struct tegra_xusb_padctl *padctl,return0;}+staticinttegra210_usb3_enable_phy_sleepwalk(structphy*phy)+{+structtegra_xusb_lane*lane=phy_get_drvdata(phy);+structtegra_xusb_padctl*padctl=lane->pad->padctl;+intport=tegra210_usb3_lane_map(lane);+structdevice*dev=padctl->dev;+u32value;++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);++return0;+}++staticinttegra210_usb3_disable_phy_sleepwalk(structphy*phy)+{+structtegra_xusb_lane*lane=phy_get_drvdata(phy);+structtegra_xusb_padctl*padctl=lane->pad->padctl;+intport=tegra210_usb3_lane_map(lane);+structdevice*dev=padctl->dev;+u32value;++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);++return0;+}++staticinttegra210_usb3_enable_phy_wake(structphy*phy)+{+structtegra_xusb_lane*lane=phy_get_drvdata(phy);+structtegra_xusb_padctl*padctl=lane->pad->padctl;+intport=tegra210_usb3_lane_map(lane);+structdevice*dev=padctl->dev;+u32value;++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);++return0;+}++staticinttegra210_usb3_disable_phy_wake(structphy*phy)+{+structtegra_xusb_lane*lane=phy_get_drvdata(phy);+structtegra_xusb_padctl*padctl=lane->pad->padctl;+intport=tegra210_usb3_lane_map(lane);+structdevice*dev=padctl->dev;+u32value;++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);++return0;+}++staticinttegra210_utmi_enable_phy_wake(structphy*phy)+{+structtegra_xusb_lane*lane=phy_get_drvdata(phy);+structtegra_xusb_padctl*padctl=lane->pad->padctl;+unsignedintindex=lane->index;+structdevice*dev=padctl->dev;+u32value;++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);++return0;+}++staticinttegra210_utmi_disable_phy_wake(structphy*phy)+{+structtegra_xusb_lane*lane=phy_get_drvdata(phy);+structtegra_xusb_padctl*padctl=lane->pad->padctl;+unsignedintindex=lane->index;+structdevice*dev=padctl->dev;+u32value;++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);++return0;+}++staticinttegra210_hsic_enable_phy_wake(structphy*phy)+{+structtegra_xusb_lane*lane=phy_get_drvdata(phy);+structtegra_xusb_padctl*padctl=lane->pad->padctl;+unsignedintindex=lane->index;+structdevice*dev=padctl->dev;+u32value;++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);++return0;+}++staticinttegra210_hsic_disable_phy_wake(structphy*phy)+{+structtegra_xusb_lane*lane=phy_get_drvdata(phy);+structtegra_xusb_padctl*padctl=lane->pad->padctl;+unsignedintindex=lane->index;+structdevice*dev=padctl->dev;+u32value;++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);++return0;+}++staticinttegra210_usb3_phy_remote_wake_detected(+structtegra_xusb_padctl*padctl,intport)+{+u32value;++if(port<0){+dev_err(padctl->dev,"invalid usb3 port number %d\n",+port);+returnfalse;+}++value=padctl_readl(padctl,XUSB_PADCTL_ELPG_PROGRAM_0);+if((value&SS_PORT_WAKE_INTERRUPT_ENABLE(port))&&+(value&SS_PORT_WAKEUP_EVENT(port)))+returntrue;+else+returnfalse;+}++staticinttegra210_utmi_phy_remote_wake_detected(+structtegra_xusb_padctl*padctl,intport)+{+u32value;++value=padctl_readl(padctl,XUSB_PADCTL_ELPG_PROGRAM_0);+if((value&USB2_PORT_WAKE_INTERRUPT_ENABLE(port))&&+(value&USB2_PORT_WAKEUP_EVENT(port)))+returntrue;+else+returnfalse;+}++staticinttegra210_hsic_phy_remote_wake_detected(+structtegra_xusb_padctl*padctl,intport)+{+u32value;++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)))+returntrue;+else+returnfalse;+}++#define padctl_pmc_readl(_priv, _offset) \+({\+intrc;\+u32val;\+rc=regmap_read(_priv->pmc_reg,_offset,&val);\+if(rc)\+returnrc;\+val;\+})++#define padctl_pmc_writel(_priv, _val, _offset) \+do{\+intrc;\+rc=regmap_write(_priv->pmc_reg,_offset,_val);\+if(rc)\+returnrc;\+}while(0)++/* T210 USB2 SLEEPWALK APIs */+inttegra_pmc_utmi_enable_phy_sleepwalk(structphy*phy,+enumusb_device_speedspeed)+{+structtegra_xusb_lane*lane=phy_get_drvdata(phy);+structtegra_xusb_padctl*padctl=lane->pad->padctl;+structtegra210_xusb_padctl*priv=to_tegra210_xusb_padctl(padctl);+structdevice*dev=padctl->dev;+unsignedintport=lane->index;+u32val,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);+elseif(speed==USB_SPEED_FULL)+val|=UTMI_FS(port);+elseif(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,+*aswellascapturetheconfigurationoftheUSB2.0pad+*/+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+*stagesofsleepwalk.+*ifdeviceisconnected,programsleepwalklogictomaintainaJand+*keepdrivingKuponseeingremotewake.+*/+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);+}elseif((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);+}elseif(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);++return0;+}++inttegra_pmc_utmi_disable_phy_sleepwalk(structphy*phy)+{+structtegra_xusb_lane*lane=phy_get_drvdata(phy);+structtegra_xusb_padctl*padctl=lane->pad->padctl;+structtegra210_xusb_padctl*priv=to_tegra210_xusb_padctl(padctl);+structdevice*dev=padctl->dev;+unsignedintport=lane->index;+u32value;++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);++return0;+}++inttegra_pmc_hsic_enable_phy_sleepwalk(structphy*phy)+{+structtegra_xusb_lane*lane=phy_get_drvdata(phy);+structtegra_xusb_padctl*padctl=lane->pad->padctl;+structtegra210_xusb_padctl*priv=to_tegra210_xusb_padctl(padctl);+structdevice*dev=padctl->dev;+unsignedintport=lane->index;+u32value;++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,+*aswellascapturetheconfigurationoftheUSB2.0port+*/+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+*stagesofsleepwalk.+*maintainaHSICIDLEandkeepdrivingHSICRESUMEuponremotewake+*/+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);++return0;+}++inttegra_pmc_hsic_disable_phy_sleepwalk(structphy*phy)+{+structtegra_xusb_lane*lane=phy_get_drvdata(phy);+structtegra_xusb_padctl*padctl=lane->pad->padctl;+structtegra210_xusb_padctl*priv=to_tegra210_xusb_padctl(padctl);+structdevice*dev=padctl->dev;+unsignedintport=lane->index;+u32value;++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);++return0;+}+staticinttegra210_usb3_set_lfps_detect(structtegra_xusb_padctl*padctl,unsignedintindex,boolenable){
@@ -988,8 +1818,23 @@ static int tegra210_usb2_phy_init(struct phy *phy){structtegra_xusb_lane*lane=phy_get_drvdata(phy);structtegra_xusb_padctl*padctl=lane->pad->padctl;+unsignedintindex=lane->index;+structtegra_xusb_usb2_port*port;+interr;u32value;+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)+returnerr;++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);+return0;}staticinttegra210_usb2_phy_exit(structphy*phy){+structtegra_xusb_lane*lane=phy_get_drvdata(phy);+structtegra_xusb_padctl*padctl=lane->pad->padctl;+unsignedintindex=lane->index;+structtegra_xusb_usb2_port*port;+interr;++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)+returnerr;+return0;}
@@ -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)-returnerr;-}--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)-gotodisable_regulator;+gotoout;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)return0;-disable_regulator:-regulator_disable(port->supply);+out:mutex_unlock(&padctl->lock);returnerr;}
@@ -1275,12 +2131,12 @@ static int tegra210_usb2_phy_power_off(struct phy *phy)structtegra_xusb_usb2_pad*pad=to_usb2_pad(lane->pad);structtegra_xusb_padctl*padctl=lane->pad->padctl;structtegra_xusb_usb2_port*port;+unsignedintindex=lane->index;u32value;-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)gotoout;+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);return0;}
@@ -2330,6 +3309,24 @@ tegra210_xusb_padctl_probe(struct device *dev,if(err<0)returnERR_PTR(err);+node=of_parse_phandle(np,"nvidia,pmc",0);+if(!node){+dev_err(dev,"nvidia,pmc property is missing\n");+returnERR_PTR(-ENODEV);+}++pmc_dev=of_find_device_by_node(node);+if(!pmc_dev){+dev_err(dev,"pmc device is not available\n");+returnERR_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");+returnERR_PTR(-ENODEV);+}+return&padctl->base;}
From: JC Kuo <jckuo@nvidia.com> Date: 2020-08-31 04:41:27
This commit unlinks xhci-tegra platform device with ss/host power
domain devices. Reasons for this change is - at elpg entry, phy
sleepwalk and wake configuration need to be done before powering
down ss/host partitions, and phy need be powered off after powering
down ss/host partitions. Sequence looks like roughly below:
tegra_xusb_enter_elpg() -> xhci_suspend()
-> enable phy sleepwalk and wake if needed
-> power down ss/host partitions
-> power down phy
If ss/host power domains are linked to xhci-tegra platform device, we
are not able to perform the sequence like above.
This commit introduces:
1. tegra_xusb_unpowergate_partitions() to power up ss and host
partitions together. If ss/host power domain devices are
available, it invokes pm_runtime_get_sync() to request power
driver to power up partitions; If power domain devices are not
available, tegra_powergate_sequence_power_up() will be used to
power up partitions.
2. tegra_xusb_powergate_partitions() to power down ss and host
partitions together. If ss/host power domain devices are
available, it invokes pm_runtime_put_sync() to request power
driver to power down partitions; If power domain devices are not
available, tegra_powergate_power_off() will be used to power down
partitions.
Signed-off-by: JC Kuo <jckuo@nvidia.com>
---
drivers/usb/host/xhci-tegra.c | 202 +++++++++++++++++++---------------
1 file changed, 111 insertions(+), 91 deletions(-)
@@ -814,36 +812,12 @@ static void tegra_xusb_phy_disable(struct tegra_xusb *tegra)staticinttegra_xusb_runtime_suspend(structdevice*dev){-structtegra_xusb*tegra=dev_get_drvdata(dev);--regulator_bulk_disable(tegra->soc->num_supplies,tegra->supplies);-tegra_xusb_clk_disable(tegra);-return0;}staticinttegra_xusb_runtime_resume(structdevice*dev){-structtegra_xusb*tegra=dev_get_drvdata(dev);-interr;--err=tegra_xusb_clk_enable(tegra);-if(err){-dev_err(dev,"failed to enable clocks: %d\n",err);-returnerr;-}--err=regulator_bulk_enable(tegra->soc->num_supplies,tegra->supplies);-if(err){-dev_err(dev,"failed to enable regulators: %d\n",err);-gotodisable_clk;-}-return0;--disable_clk:-tegra_xusb_clk_disable(tegra);-returnerr;}#ifdef CONFIG_PM_SLEEP
@@ -1019,10 +993,6 @@ static int tegra_xusb_load_firmware(struct tegra_xusb *tegra)staticvoidtegra_xusb_powerdomain_remove(structdevice*dev,structtegra_xusb*tegra){-if(tegra->genpd_dl_ss)-device_link_del(tegra->genpd_dl_ss);-if(tegra->genpd_dl_host)-device_link_del(tegra->genpd_dl_host);if(!IS_ERR_OR_NULL(tegra->genpd_dev_ss))dev_pm_domain_detach(tegra->genpd_dev_ss,true);if(!IS_ERR_OR_NULL(tegra->genpd_dev_host))
@@ -1048,20 +1018,88 @@ static int tegra_xusb_powerdomain_init(struct device *dev,returnerr;}-tegra->genpd_dl_host=device_link_add(dev,tegra->genpd_dev_host,-DL_FLAG_PM_RUNTIME|-DL_FLAG_STATELESS);-if(!tegra->genpd_dl_host){-dev_err(dev,"adding host device link failed!\n");-return-ENODEV;+return0;+}++staticinttegra_xusb_unpowergate_partitions(structtegra_xusb*tegra)+{+structdevice*dev=tegra->dev;+booluse_genpd;+intrc;++use_genpd=of_property_read_bool(dev->of_node,"power-domains");++if(use_genpd){+rc=pm_runtime_get_sync(tegra->genpd_dev_ss);+if(rc<0){+dev_err(dev,"failed to enable XUSB SS partition\n");+returnrc;+}++rc=pm_runtime_get_sync(tegra->genpd_dev_host);+if(rc<0){+dev_err(dev,"failed to enable XUSB Host partition\n");+pm_runtime_put_sync(tegra->genpd_dev_ss);+returnrc;+}+}else{+rc=tegra_powergate_sequence_power_up(TEGRA_POWERGATE_XUSBA,+tegra->ss_clk,+tegra->ss_rst);+if(rc<0){+dev_err(dev,"failed to enable XUSB SS partition\n");+returnrc;+}++rc=tegra_powergate_sequence_power_up(TEGRA_POWERGATE_XUSBC,+tegra->host_clk,+tegra->host_rst);+if(rc<0){+dev_err(dev,"failed to enable XUSB Host partition\n");+tegra_powergate_power_off(TEGRA_POWERGATE_XUSBA);+returnrc;+}}-tegra->genpd_dl_ss=device_link_add(dev,tegra->genpd_dev_ss,-DL_FLAG_PM_RUNTIME|-DL_FLAG_STATELESS);-if(!tegra->genpd_dl_ss){-dev_err(dev,"adding superspeed device link failed!\n");-return-ENODEV;+return0;+}++staticinttegra_xusb_powergate_partitions(structtegra_xusb*tegra)+{+structdevice*dev=tegra->dev;+booluse_genpd;+intrc;++use_genpd=of_property_read_bool(dev->of_node,"power-domains");++if(use_genpd){+rc=pm_runtime_put_sync(tegra->genpd_dev_host);+if(rc<0){+dev_err(dev,"failed to disable XUSB Host partition\n");+returnrc;+}++rc=pm_runtime_put_sync(tegra->genpd_dev_ss);+if(rc<0){+dev_err(dev,"failed to disable XUSB SS partition\n");+pm_runtime_get_sync(tegra->genpd_dev_host);+returnrc;+}+}else{+rc=tegra_powergate_power_off(TEGRA_POWERGATE_XUSBC);+if(rc<0){+dev_err(dev,"failed to disable XUSB Host partition\n");+returnrc;+}++rc=tegra_powergate_power_off(TEGRA_POWERGATE_XUSBA);+if(rc<0){+dev_err(dev,"failed to disable XUSB SS partition\n");+tegra_powergate_sequence_power_up(TEGRA_POWERGATE_XUSBC,+tegra->host_clk,+tegra->host_rst);+returnrc;+}}return0;
@@ -1425,25 +1463,6 @@ static int tegra_xusb_probe(struct platform_device *pdev)err);gotoput_padctl;}--err=tegra_powergate_sequence_power_up(TEGRA_POWERGATE_XUSBA,-tegra->ss_clk,-tegra->ss_rst);-if(err){-dev_err(&pdev->dev,-"failed to enable XUSBA domain: %d\n",err);-gotoput_padctl;-}--err=tegra_powergate_sequence_power_up(TEGRA_POWERGATE_XUSBC,-tegra->host_clk,-tegra->host_rst);-if(err){-tegra_powergate_power_off(TEGRA_POWERGATE_XUSBA);-dev_err(&pdev->dev,-"failed to enable XUSBC domain: %d\n",err);-gotoput_padctl;-}}else{err=tegra_xusb_powerdomain_init(&pdev->dev,tegra);if(err)
@@ -1518,10 +1537,22 @@ static int tegra_xusb_probe(struct platform_device *pdev)*/platform_set_drvdata(pdev,tegra);+err=tegra_xusb_clk_enable(tegra);+if(err){+dev_err(tegra->dev,"failed to enable clocks: %d\n",err);+gotoput_hcd;+}++err=regulator_bulk_enable(tegra->soc->num_supplies,tegra->supplies);+if(err){+dev_err(tegra->dev,"failed to enable regulators: %d\n",err);+gotodisable_clk;+}+err=tegra_xusb_phy_enable(tegra);if(err<0){dev_err(&pdev->dev,"failed to enable PHYs: %d\n",err);-gotoput_hcd;+gotodisable_regulator;}/*
@@ -1540,30 +1571,22 @@ static int tegra_xusb_probe(struct platform_device *pdev)gotodisable_phy;}-pm_runtime_enable(&pdev->dev);--if(!pm_runtime_enabled(&pdev->dev))-err=tegra_xusb_runtime_resume(&pdev->dev);-else-err=pm_runtime_get_sync(&pdev->dev);--if(err<0){-dev_err(&pdev->dev,"failed to enable device: %d\n",err);+err=tegra_xusb_unpowergate_partitions(tegra);+if(err)gotofree_firmware;-}tegra_xusb_config(tegra);err=tegra_xusb_load_firmware(tegra);if(err<0){dev_err(&pdev->dev,"failed to load firmware: %d\n",err);-gotoput_rpm;+gotopowergate;}err=usb_add_hcd(tegra->hcd,tegra->xhci_irq,IRQF_SHARED);if(err<0){dev_err(&pdev->dev,"failed to add USB HCD: %d\n",err);-gotoput_rpm;+gotopowergate;}device_wakeup_enable(tegra->hcd->self.controller);
@@ -1615,24 +1638,21 @@ static int tegra_xusb_probe(struct platform_device *pdev)usb_put_hcd(xhci->shared_hcd);remove_usb2:usb_remove_hcd(tegra->hcd);-put_rpm:-if(!pm_runtime_status_suspended(&pdev->dev))-tegra_xusb_runtime_suspend(&pdev->dev);-put_hcd:-usb_put_hcd(tegra->hcd);+powergate:+tegra_xusb_powergate_partitions(tegra);free_firmware:dma_free_coherent(&pdev->dev,tegra->fw.size,tegra->fw.virt,tegra->fw.phys);disable_phy:tegra_xusb_phy_disable(tegra);-pm_runtime_disable(&pdev->dev);+disable_regulator:+regulator_bulk_disable(tegra->soc->num_supplies,tegra->supplies);+disable_clk:+tegra_xusb_clk_disable(tegra);+put_hcd:+usb_put_hcd(tegra->hcd);put_powerdomains:-if(!of_property_read_bool(pdev->dev.of_node,"power-domains")){-tegra_powergate_power_off(TEGRA_POWERGATE_XUSBC);-tegra_powergate_power_off(TEGRA_POWERGATE_XUSBA);-}else{-tegra_xusb_powerdomain_remove(&pdev->dev,tegra);-}+tegra_xusb_powerdomain_remove(&pdev->dev,tegra);put_padctl:tegra_xusb_padctl_put(tegra->padctl);returnerr;
@@ -1657,15 +1677,15 @@ static int tegra_xusb_remove(struct platform_device *pdev)pm_runtime_put_sync(&pdev->dev);pm_runtime_disable(&pdev->dev);-if(!of_property_read_bool(pdev->dev.of_node,"power-domains")){-tegra_powergate_power_off(TEGRA_POWERGATE_XUSBC);-tegra_powergate_power_off(TEGRA_POWERGATE_XUSBA);-}else{+tegra_xusb_powergate_partitions(tegra);++if(of_property_read_bool(pdev->dev.of_node,"power-domains")){tegra_xusb_powerdomain_remove(&pdev->dev,tegra);}tegra_xusb_phy_disable(tegra);-+tegra_xusb_clk_disable(tegra);+regulator_bulk_disable(tegra->soc->num_supplies,tegra->supplies);tegra_xusb_padctl_put(tegra->padctl);return0;
From: JC Kuo <jckuo@nvidia.com> Date: 2020-08-31 04:41:29
This commit implements the complete programming sequence for ELPG
entry and exit.
1. At ELPG entry, invokes tegra_xusb_padctl_enable_phy_sleepwalk()
and tegra_xusb_padctl_enable_phy_wake() to configure XUSB PADCTL
sleepwalk and wake detection circuits to maintain USB lines level
and respond to wake events (wake-on-connect, wake-on-disconnect,
device-initiated-wake).
2. At ELPG exit, invokes tegra_xusb_padctl_disable_phy_sleepwalk()
and tegra_xusb_padctl_disable_phy_wake() to disarm sleepwalk and
wake detection circuits.
At runtime suspend, XUSB host controller can enter ELPG to reduce
power consumption. When XUSB PADCTL wake detection circuit detects
a wake event, an interrupt will be raised. xhci-tegra driver then
will invoke pm_runtime_resume() for xhci-tegra.
Runtime resume could also be triggered by protocol drivers, this is
the host-initiated-wake event. At runtime resume, xhci-tegra driver
brings XUSB host controller out of ELPG to handle the wake events.
The same ELPG enter/exit procedure will be performed for system
suspend/resume path so USB devices can remain connected across SC7.
Signed-off-by: JC Kuo <jckuo@nvidia.com>
---
drivers/usb/host/xhci-tegra.c | 391 +++++++++++++++++++++++++++++++---
1 file changed, 361 insertions(+), 30 deletions(-)
@@ -1383,6 +1470,14 @@ static int tegra_xusb_probe(struct platform_device *pdev)if(IS_ERR(tegra->padctl))returnPTR_ERR(tegra->padctl);+np=of_parse_phandle(pdev->dev.of_node,"nvidia,xusb-padctl",0);+if(!np)+return-ENODEV;++tegra->padctl_irq=of_irq_get(np,0);+if(tegra->padctl_irq<0)+returntegra->padctl_irq;+tegra->host_clk=devm_clk_get(&pdev->dev,"xusb_host");if(IS_ERR(tegra->host_clk)){err=PTR_ERR(tegra->host_clk);
@@ -1527,6 +1622,7 @@ static int tegra_xusb_probe(struct platform_device *pdev)gotoput_powerdomains;}+tegra->hcd->skip_phy_initialization=1;tegra->hcd->regs=tegra->regs;tegra->hcd->rsrc_start=regs->start;tegra->hcd->rsrc_len=resource_size(regs);
@@ -1609,12 +1705,6 @@ static int tegra_xusb_probe(struct platform_device *pdev)gotoput_usb3;}-err=tegra_xusb_enable_firmware_messages(tegra);-if(err<0){-dev_err(&pdev->dev,"failed to enable messages: %d\n",err);-gotoremove_usb3;-}-err=devm_request_threaded_irq(&pdev->dev,tegra->mbox_irq,tegra_xusb_mbox_irq,tegra_xusb_mbox_thread,0,
@@ -1624,12 +1714,40 @@ static int tegra_xusb_probe(struct platform_device *pdev)gotoremove_usb3;}+err=devm_request_threaded_irq(&pdev->dev,tegra->padctl_irq,+NULL,+tegra_xusb_padctl_irq,+IRQF_ONESHOT|+IRQF_TRIGGER_HIGH,+dev_name(&pdev->dev),tegra);+if(err<0){+dev_err(&pdev->dev,"failed to request padctl IRQ: %d\n",err);+gotoremove_usb3;+}++err=tegra_xusb_enable_firmware_messages(tegra);+if(err<0){+dev_err(&pdev->dev,"failed to enable messages: %d\n",err);+gotoremove_usb3;+}+err=tegra_xusb_init_usb_phy(tegra);if(err<0){dev_err(&pdev->dev,"failed to init USB PHY: %d\n",err);gotoremove_usb3;}+/* Enable wake for both USB 2.0 and USB 3.0 roothubs */+device_init_wakeup(&tegra->hcd->self.root_hub->dev,true);+device_init_wakeup(&xhci->shared_hcd->self.root_hub->dev,true);+device_init_wakeup(tegra->dev,true);++pm_runtime_use_autosuspend(tegra->dev);+pm_runtime_set_autosuspend_delay(tegra->dev,2000);+pm_runtime_mark_last_busy(tegra->dev);+pm_runtime_set_active(tegra->dev);+pm_runtime_enable(tegra->dev);+return0;remove_usb3:
@@ -1665,6 +1783,7 @@ static int tegra_xusb_remove(struct platform_device *pdev)tegra_xusb_deinit_usb_phy(tegra);+pm_runtime_get_sync(&pdev->dev);usb_remove_hcd(xhci->shared_hcd);usb_put_hcd(xhci->shared_hcd);xhci->shared_hcd=NULL;
@@ -1674,8 +1793,8 @@ static int tegra_xusb_remove(struct platform_device *pdev)dma_free_coherent(&pdev->dev,tegra->fw.size,tegra->fw.virt,tegra->fw.phys);-pm_runtime_put_sync(&pdev->dev);pm_runtime_disable(&pdev->dev);+pm_runtime_put(&pdev->dev);tegra_xusb_powergate_partitions(tegra);
@@ -1717,9 +1836,17 @@ static bool xhci_hub_ports_suspended(struct xhci_hub *hub)staticinttegra_xusb_check_ports(structtegra_xusb*tegra){structxhci_hcd*xhci=hcd_to_xhci(tegra->hcd);+structxhci_hub*rhub=xhci_get_rhub(xhci->main_hcd);+structxhci_bus_state*bus_state=&rhub->bus_state;unsignedlongflags;interr=0;+if(bus_state->bus_suspended){+/* xusb_hub_suspend() has just directed one or more USB2 port(s)+*toU3state,ittakes3mstoenterU3.*/+usleep_range(3000,4000);+}+spin_lock_irqsave(&xhci->lock,flags);if(!xhci_hub_ports_suspended(&xhci->usb2_rhub)||
@@ -1765,45 +1892,184 @@ static void tegra_xusb_restore_context(struct tegra_xusb *tegra)}}-staticinttegra_xusb_enter_elpg(structtegra_xusb*tegra,boolwakeup)+staticenumusb_device_speed+tegra_xhci_portsc_to_speed(structtegra_xusb*tegra,u32portsc)+{+if(DEV_LOWSPEED(portsc))+returnUSB_SPEED_LOW;+elseif(DEV_HIGHSPEED(portsc))+returnUSB_SPEED_HIGH;+elseif(DEV_FULLSPEED(portsc))+returnUSB_SPEED_FULL;+elseif(DEV_SUPERSPEED_ANY(portsc))+returnUSB_SPEED_SUPER;+else+returnUSB_SPEED_UNKNOWN;+}++staticvoidtegra_xhci_enable_phy_sleepwalk_wake(structtegra_xusb*tegra)+{+structtegra_xusb_padctl*padctl=tegra->padctl;+structxhci_hcd*xhci=hcd_to_xhci(tegra->hcd);+enumusb_device_speedspeed;+structphy*phy;+intindex,offset;+inti,j,k;+structxhci_hub*rhub;+u32portsc;++for(i=0,k=0;i<tegra->soc->num_types;i++){+if(strcmp(tegra->soc->phy_types[i].name,"usb3")==0)+rhub=&xhci->usb3_rhub;+else+rhub=&xhci->usb2_rhub;++if(strcmp(tegra->soc->phy_types[i].name,"hsic")==0)+offset=tegra->soc->ports.usb2.count;+else+offset=0;++for(j=0;j<tegra->soc->phy_types[i].num;j++){+phy=tegra->phys[k++];++if(!phy)+continue;++index=j+offset;++if(index>=rhub->num_ports)+continue;++if(!is_host_mode_phy(tegra,i,j))+continue;++portsc=readl(rhub->ports[index]->addr);+speed=tegra_xhci_portsc_to_speed(tegra,portsc);+tegra_xusb_padctl_enable_phy_sleepwalk(padctl,phy,+speed);+tegra_xusb_padctl_enable_phy_wake(padctl,phy);+}+}+}++staticvoidtegra_xhci_disable_phy_wake(structtegra_xusb*tegra)+{+structtegra_xusb_padctl*padctl=tegra->padctl;+inti;++for(i=0;i<tegra->num_phys;i++){+if(!tegra->phys[i])+continue;++tegra_xusb_padctl_disable_phy_wake(padctl,tegra->phys[i]);+}+}++staticvoidtegra_xhci_disable_phy_sleepwalk(structtegra_xusb*tegra)+{+structtegra_xusb_padctl*padctl=tegra->padctl;+inti;++for(i=0;i<tegra->num_phys;i++){+if(!tegra->phys[i])+continue;++tegra_xusb_padctl_disable_phy_sleepwalk(padctl,tegra->phys[i]);+}+}++staticinttegra_xusb_enter_elpg(structtegra_xusb*tegra,boolruntime){structxhci_hcd*xhci=hcd_to_xhci(tegra->hcd);+structdevice*dev=tegra->dev;+boolwakeup=runtime?true:device_may_wakeup(dev);+unsignedinti;interr;+u32usbcmd;++dev_dbg(dev,"entering ELPG\n");++usbcmd=readl(&xhci->op_regs->command);+usbcmd&=~CMD_EIE;+writel(usbcmd,&xhci->op_regs->command);err=tegra_xusb_check_ports(tegra);if(err<0){dev_err(tegra->dev,"not all ports suspended: %d\n",err);-returnerr;+gotoout;}err=xhci_suspend(xhci,wakeup);if(err<0){dev_err(tegra->dev,"failed to suspend XHCI: %d\n",err);-returnerr;+gotoout;}tegra_xusb_save_context(tegra);-tegra_xusb_phy_disable(tegra);++if(wakeup)+tegra_xhci_enable_phy_sleepwalk_wake(tegra);++tegra_xusb_powergate_partitions(tegra);++for(i=0;i<tegra->num_phys;i++){+if(!tegra->phys[i])+continue;++phy_power_off(tegra->phys[i]);+if(!wakeup)+phy_exit(tegra->phys[i]);+}tegra_xusb_clk_disable(tegra);-return0;+out:+if(!err)+dev_dbg(tegra->dev,"entering ELPG done\n");+else{+usbcmd=readl(&xhci->op_regs->command);+usbcmd|=CMD_EIE;+writel(usbcmd,&xhci->op_regs->command);++dev_dbg(tegra->dev,"entering ELPG failed\n");+pm_runtime_mark_last_busy(tegra->dev);+}++returnerr;}-staticinttegra_xusb_exit_elpg(structtegra_xusb*tegra,boolwakeup)+staticinttegra_xusb_exit_elpg(structtegra_xusb*tegra,boolruntime){structxhci_hcd*xhci=hcd_to_xhci(tegra->hcd);+structdevice*dev=tegra->dev;+boolwakeup=runtime?true:device_may_wakeup(dev);+unsignedinti;+u32usbcmd;interr;+dev_dbg(dev,"exiting ELPG\n");+pm_runtime_mark_last_busy(tegra->dev);+err=tegra_xusb_clk_enable(tegra);if(err<0){dev_err(tegra->dev,"failed to enable clocks: %d\n",err);-returnerr;+gotoout;}-err=tegra_xusb_phy_enable(tegra);-if(err<0){-dev_err(tegra->dev,"failed to enable PHYs: %d\n",err);-gotodisable_clk;+err=tegra_xusb_unpowergate_partitions(tegra);+if(err)+gotodisable_clks;++if(wakeup)+tegra_xhci_disable_phy_wake(tegra);++for(i=0;i<tegra->num_phys;i++){+if(!tegra->phys[i])+continue;++if(!wakeup)+phy_init(tegra->phys[i]);++phy_power_on(tegra->phys[i]);}tegra_xusb_config(tegra);
@@ -1821,31 +2087,78 @@ static int tegra_xusb_exit_elpg(struct tegra_xusb *tegra, bool wakeup)gotodisable_phy;}-err=xhci_resume(xhci,true);+if(wakeup)+tegra_xhci_disable_phy_sleepwalk(tegra);++err=xhci_resume(xhci,0);if(err<0){dev_err(tegra->dev,"failed to resume XHCI: %d\n",err);gotodisable_phy;}-return0;+usbcmd=readl(&xhci->op_regs->command);+usbcmd|=CMD_EIE;+writel(usbcmd,&xhci->op_regs->command);++gotoout;disable_phy:-tegra_xusb_phy_disable(tegra);-disable_clk:+for(i=0;i<tegra->num_phys;i++){+if(!tegra->phys[i])+continue;++phy_power_off(tegra->phys[i]);+if(!wakeup)+phy_exit(tegra->phys[i]);+}+tegra_xusb_powergate_partitions(tegra);+disable_clks:tegra_xusb_clk_disable(tegra);+out:+if(!err)+dev_dbg(dev,"exiting ELPG done\n");+else+dev_dbg(dev,"exiting ELPG failed\n");+returnerr;}staticinttegra_xusb_suspend(structdevice*dev){structtegra_xusb*tegra=dev_get_drvdata(dev);-boolwakeup=device_may_wakeup(dev);interr;synchronize_irq(tegra->mbox_irq);-mutex_lock(&tegra->lock);-err=tegra_xusb_enter_elpg(tegra,wakeup);++if(pm_runtime_suspended(dev)){+err=tegra_xusb_exit_elpg(tegra,true);+if(err<0)+gotoout;+}++err=tegra_xusb_enter_elpg(tegra,false);+if(err<0){+if(pm_runtime_suspended(dev)){+pm_runtime_disable(dev);+pm_runtime_set_active(dev);+pm_runtime_enable(dev);+}++gotoout;+}++out:+if(!err){+tegra->suspended=true;+pm_runtime_disable(dev);++if(device_may_wakeup(dev)){+if(enable_irq_wake(tegra->padctl_irq))+dev_err(dev,"failed to enable padctl wakes\n");+}+}+mutex_unlock(&tegra->lock);returnerr;
@@ -1854,14 +2167,32 @@ static int tegra_xusb_suspend(struct device *dev)staticinttegra_xusb_resume(structdevice*dev){structtegra_xusb*tegra=dev_get_drvdata(dev);-boolwakeup=device_may_wakeup(dev);interr;mutex_lock(&tegra->lock);-err=tegra_xusb_exit_elpg(tegra,wakeup);++if(!tegra->suspended){+mutex_unlock(&tegra->lock);+return0;+}++err=tegra_xusb_exit_elpg(tegra,false);+if(err<0){+mutex_unlock(&tegra->lock);+returnerr;+}++if(device_may_wakeup(dev)){+if(disable_irq_wake(tegra->padctl_irq))+dev_err(dev,"failed to disable padctl wakes\n");+}+tegra->suspended=false;mutex_unlock(&tegra->lock);-returnerr;+pm_runtime_set_active(dev);+pm_runtime_enable(dev);++return0;}#endif
From: JC Kuo <jckuo@nvidia.com> Date: 2020-08-31 04:41:42
This commit is a preparation for enabling XUSB SC7 support.
It rearranges Tegra210 XUSB PADCTL UPHY initialization sequence,
for the following reasons:
1. PLLE hardware power sequencer has to be enabled only after both
PEX UPHY PLL and SATA UPHY PLL are initialized.
tegra210_uphy_init() -> tegra210_pex_uphy_enable()
-> tegra210_sata_uphy_enable()
-> tegra210_plle_hw_sequence_start()
-> tegra210_aux_mux_lp0_clamp_disable()
2. Once UPHY PLL hardware power sequencer is enabled, do not assert
reset to PEX/SATA PLLs, otherwise UPHY PLL operation will be
broken.
reset_control_assert(pcie->rst) and reset_control_assert(sata->rst)
are removed from PEX/SATA UPHY disable procedure.
3. At cold boot and SC7 exit, the following bits must be cleared after
PEX/SATA lanes are out of IDDQ (IDDQ_DISABLE=1).
a. XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_CLAMP_EN,
b. XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_CLAMP_EN_EARLY
c. XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_VCORE_DOWN
tegra210_pex_uphy_enable() and tegra210_sata_uphy_enable() are in
charge of bringing lanes out of IDDQ, and then AUX_MUX_LP0_* bits
will be cleared by tegra210_aux_mux_lp0_clamp_disable().
4. The programming sequence in tegra210_usb3_port_enable() is required
for both cold boot and SC7 exit, and must be performed only after
PEX/SATA UPHY is initialized. Therefore, this commit moves the
programming sequence to .power_on() stub which is invoked after
.init(). PEX/SATA UPHY is initialzied in .init().
Signed-off-by: JC Kuo <jckuo@nvidia.com>
---
drivers/phy/tegra/xusb-tegra210.c | 495 ++++++++++++++++--------------
drivers/phy/tegra/xusb.c | 2 +-
drivers/phy/tegra/xusb.h | 6 +-
3 files changed, 270 insertions(+), 233 deletions(-)
@@ -256,23 +256,52 @@ to_tegra210_xusb_padctl(struct tegra_xusb_padctl *padctl)returncontainer_of(padctl,structtegra210_xusb_padctl,base);}+staticconststructtegra_xusb_lane_maptegra210_usb3_map[]={+{0,"pcie",6},+{1,"pcie",5},+{2,"pcie",0},+{2,"pcie",3},+{3,"pcie",4},+{3,"pcie",4},+{0,NULL,0}+};++staticinttegra210_usb3_lane_map(structtegra_xusb_lane*lane)+{+conststructtegra_xusb_lane_map*map;++for(map=tegra210_usb3_map;map->type;map++){+if(map->index==lane->index&&+strcmp(map->type,lane->pad->soc->name)==0){+dev_dbg(lane->pad->padctl->dev,+"lane = %s map to port = usb3-%d\n",+lane->pad->soc->lanes[lane->index].name,+map->port);+returnmap->port;+}+}++return-EINVAL;+}+/* must be called under padctl->lock */staticinttegra210_pex_uphy_enable(structtegra_xusb_padctl*padctl){structtegra_xusb_pcie_pad*pcie=to_pcie_pad(padctl->pcie);unsignedlongtimeout;u32value;-interr;+interr,i;-if(pcie->enable>0){-pcie->enable++;+if(pcie->enable)return0;-}err=clk_prepare_enable(pcie->pll);if(err<0)returnerr;+if(tegra210_plle_hw_sequence_is_enabled())+gotoskip_pll_init;+err=reset_control_deassert(pcie->rst);if(err<0)gotodisable;
@@ -455,7 +484,14 @@ static int tegra210_pex_uphy_enable(struct tegra_xusb_padctl *padctl)tegra210_xusb_pll_hw_sequence_start();-pcie->enable++;+skip_pll_init:+pcie->enable=true;++for(i=0;i<padctl->pcie->soc->num_lanes;i++){+value=padctl_readl(padctl,XUSB_PADCTL_USB3_PAD_MUX);+value|=XUSB_PADCTL_USB3_PAD_MUX_PCIE_IDDQ_DISABLE(i);+padctl_writel(padctl,value,XUSB_PADCTL_USB3_PAD_MUX);+}return0;
@@ -469,34 +505,42 @@ static int tegra210_pex_uphy_enable(struct tegra_xusb_padctl *padctl)staticvoidtegra210_pex_uphy_disable(structtegra_xusb_padctl*padctl){structtegra_xusb_pcie_pad*pcie=to_pcie_pad(padctl->pcie);+u32value;+inti;-mutex_lock(&padctl->lock);--if(WARN_ON(pcie->enable==0))-gotounlock;+if(WARN_ON(!pcie->enable))+return;-if(--pcie->enable>0)-gotounlock;+pcie->enable=false;-reset_control_assert(pcie->rst);+for(i=0;i<padctl->pcie->soc->num_lanes;i++){+value=padctl_readl(padctl,XUSB_PADCTL_USB3_PAD_MUX);+value&=~XUSB_PADCTL_USB3_PAD_MUX_PCIE_IDDQ_DISABLE(i);+padctl_writel(padctl,value,XUSB_PADCTL_USB3_PAD_MUX);+}clk_disable_unprepare(pcie->pll);--unlock:-mutex_unlock(&padctl->lock);}/* must be called under padctl->lock */-staticinttegra210_sata_uphy_enable(structtegra_xusb_padctl*padctl,boolusb)+staticinttegra210_sata_uphy_enable(structtegra_xusb_padctl*padctl){structtegra_xusb_sata_pad*sata=to_sata_pad(padctl->sata);+structtegra_xusb_lane*lane=tegra_xusb_find_lane(padctl,"sata",0);unsignedlongtimeout;u32value;-interr;+interr,i;+boolusb;-if(sata->enable>0){-sata->enable++;+if(sata->enable)return0;-}++if(!lane)+return0;++if(tegra210_plle_hw_sequence_is_enabled())+gotoskip_pll_init;++usb=tegra_xusb_lane_check(lane,"usb3-ss");err=clk_prepare_enable(sata->pll);if(err<0)
@@ -751,24 +797,12 @@ static int tegra210_xusb_padctl_enable(struct tegra_xusb_padctl *padctl)value=padctl_readl(padctl,XUSB_PADCTL_ELPG_PROGRAM1);value&=~XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_VCORE_DOWN;padctl_writel(padctl,value,XUSB_PADCTL_ELPG_PROGRAM1);--out:-mutex_unlock(&padctl->lock);-return0;}-staticinttegra210_xusb_padctl_disable(structtegra_xusb_padctl*padctl)+staticvoidtegra210_aux_mux_lp0_clamp_enable(structtegra_xusb_padctl*padctl){u32value;-mutex_lock(&padctl->lock);--if(WARN_ON(padctl->enable==0))-gotoout;--if(--padctl->enable>0)-gotoout;-value=padctl_readl(padctl,XUSB_PADCTL_ELPG_PROGRAM1);value|=XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_VCORE_DOWN;padctl_writel(padctl,value,XUSB_PADCTL_ELPG_PROGRAM1);
@@ -784,12 +818,36 @@ static int tegra210_xusb_padctl_disable(struct tegra_xusb_padctl *padctl)value=padctl_readl(padctl,XUSB_PADCTL_ELPG_PROGRAM1);value|=XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_CLAMP_EN;padctl_writel(padctl,value,XUSB_PADCTL_ELPG_PROGRAM1);+}++staticinttegra210_uphy_init(structtegra_xusb_padctl*padctl)+{+if(padctl->pcie)+tegra210_pex_uphy_enable(padctl);+if(padctl->sata)+tegra210_sata_uphy_enable(padctl);++if(!tegra210_plle_hw_sequence_is_enabled())+tegra210_plle_hw_sequence_start();+else+dev_dbg(padctl->dev,"PLLE is already in HW control\n");++tegra210_aux_mux_lp0_clamp_disable(padctl);-out:-mutex_unlock(&padctl->lock);return0;}+staticvoid__maybe_unused+tegra210_uphy_deinit(structtegra_xusb_padctl*padctl)+{+tegra210_aux_mux_lp0_clamp_enable(padctl);++if(padctl->pcie)+tegra210_pex_uphy_disable(padctl);+if(padctl->sata)+tegra210_sata_uphy_disable(padctl);+}+staticinttegra210_hsic_set_idle(structtegra_xusb_padctl*padctl,unsignedintindex,boolidle){
@@ -926,14 +984,12 @@ 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);-returntegra210_xusb_padctl_enable(padctl);+return0;}staticinttegra210_usb2_phy_exit(structphy*phy){-structtegra_xusb_lane*lane=phy_get_drvdata(phy);--returntegra210_xusb_padctl_disable(lane->pad->padctl);+return0;}staticinttegra210_xusb_padctl_vbus_override(structtegra_xusb_padctl*padctl,
@@ -1391,14 +1447,12 @@ static int tegra210_hsic_phy_init(struct phy *phy)XUSB_PADCTL_USB2_PAD_MUX_HSIC_PAD_TRK_SHIFT;padctl_writel(padctl,value,XUSB_PADCTL_USB2_PAD_MUX);-returntegra210_xusb_padctl_enable(padctl);+return0;}staticinttegra210_hsic_phy_exit(structphy*phy){-structtegra_xusb_lane*lane=phy_get_drvdata(phy);--returntegra210_xusb_padctl_disable(lane->pad->padctl);+return0;}staticinttegra210_hsic_phy_power_on(structphy*phy)
@@ -1599,6 +1653,128 @@ static const struct tegra_xusb_lane_soc tegra210_pcie_lanes[] = {TEGRA210_LANE("pcie-6",0x028,24,0x3,pcie),};+staticstructtegra_xusb_usb3_port*+tegra210_lane_to_usb3_port(structtegra_xusb_lane*lane)+{+intport;++if(!lane||!lane->pad||!lane->pad->padctl)+returnNULL;++port=tegra210_usb3_lane_map(lane);+if(port<0)+returnNULL;++returntegra_xusb_find_usb3_port(lane->pad->padctl,port);+}++staticinttegra210_usb3_phy_power_on(structphy*phy)+{+structdevice*dev=&phy->dev;+structtegra_xusb_lane*lane=phy_get_drvdata(phy);+structtegra_xusb_padctl*padctl=lane->pad->padctl;+structtegra_xusb_usb3_port*usb3=tegra210_lane_to_usb3_port(lane);+unsignedintindex;+u32value;++if(!usb3){+dev_err(dev,"no USB3 port found for lane %u\n",lane->index);+return-ENODEV;+}++index=usb3->base.index;++value=padctl_readl(padctl,XUSB_PADCTL_SS_PORT_MAP);++if(!usb3->internal)+value&=~XUSB_PADCTL_SS_PORT_MAP_PORTX_INTERNAL(index);+else+value|=XUSB_PADCTL_SS_PORT_MAP_PORTX_INTERNAL(index);++value&=~XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP_MASK(index);+value|=XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP(index,usb3->port);+padctl_writel(padctl,value,XUSB_PADCTL_SS_PORT_MAP);++value=padctl_readl(padctl,XUSB_PADCTL_UPHY_USB3_PADX_ECTL1(index));+value&=~(XUSB_PADCTL_UPHY_USB3_PAD_ECTL1_TX_TERM_CTRL_MASK<<+XUSB_PADCTL_UPHY_USB3_PAD_ECTL1_TX_TERM_CTRL_SHIFT);+value|=XUSB_PADCTL_UPHY_USB3_PAD_ECTL1_TX_TERM_CTRL_VAL<<+XUSB_PADCTL_UPHY_USB3_PAD_ECTL1_TX_TERM_CTRL_SHIFT;+padctl_writel(padctl,value,XUSB_PADCTL_UPHY_USB3_PADX_ECTL1(index));++value=padctl_readl(padctl,XUSB_PADCTL_UPHY_USB3_PADX_ECTL2(index));+value&=~(XUSB_PADCTL_UPHY_USB3_PAD_ECTL2_RX_CTLE_MASK<<+XUSB_PADCTL_UPHY_USB3_PAD_ECTL2_RX_CTLE_SHIFT);+value|=XUSB_PADCTL_UPHY_USB3_PAD_ECTL2_RX_CTLE_VAL<<+XUSB_PADCTL_UPHY_USB3_PAD_ECTL2_RX_CTLE_SHIFT;+padctl_writel(padctl,value,XUSB_PADCTL_UPHY_USB3_PADX_ECTL2(index));++padctl_writel(padctl,XUSB_PADCTL_UPHY_USB3_PAD_ECTL3_RX_DFE_VAL,+XUSB_PADCTL_UPHY_USB3_PADX_ECTL3(index));++value=padctl_readl(padctl,XUSB_PADCTL_UPHY_USB3_PADX_ECTL4(index));+value&=~(XUSB_PADCTL_UPHY_USB3_PAD_ECTL4_RX_CDR_CTRL_MASK<<+XUSB_PADCTL_UPHY_USB3_PAD_ECTL4_RX_CDR_CTRL_SHIFT);+value|=XUSB_PADCTL_UPHY_USB3_PAD_ECTL4_RX_CDR_CTRL_VAL<<+XUSB_PADCTL_UPHY_USB3_PAD_ECTL4_RX_CDR_CTRL_SHIFT;+padctl_writel(padctl,value,XUSB_PADCTL_UPHY_USB3_PADX_ECTL4(index));++padctl_writel(padctl,XUSB_PADCTL_UPHY_USB3_PAD_ECTL6_RX_EQ_CTRL_H_VAL,+XUSB_PADCTL_UPHY_USB3_PADX_ECTL6(index));++value=padctl_readl(padctl,XUSB_PADCTL_ELPG_PROGRAM1);+value&=~XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_VCORE_DOWN(index);+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_EARLY(index);+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(index);+padctl_writel(padctl,value,XUSB_PADCTL_ELPG_PROGRAM1);++return0;+}++staticinttegra210_usb3_phy_power_off(structphy*phy)+{+structdevice*dev=&phy->dev;+structtegra_xusb_lane*lane=phy_get_drvdata(phy);+structtegra_xusb_padctl*padctl=lane->pad->padctl;+structtegra_xusb_usb3_port*usb3=tegra210_lane_to_usb3_port(lane);+unsignedintindex;+u32value;++if(!usb3){+dev_err(dev,"no USB3 port found for lane %u\n",lane->index);+return-ENODEV;+}++index=usb3->base.index;++value=padctl_readl(padctl,XUSB_PADCTL_ELPG_PROGRAM1);+value|=XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN_EARLY(index);+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(index);+padctl_writel(padctl,value,XUSB_PADCTL_ELPG_PROGRAM1);++usleep_range(250,350);++value=padctl_readl(padctl,XUSB_PADCTL_ELPG_PROGRAM1);+value|=XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_VCORE_DOWN(index);+padctl_writel(padctl,value,XUSB_PADCTL_ELPG_PROGRAM1);++return0;+}staticstructtegra_xusb_lane*tegra210_pcie_lane_probe(structtegra_xusb_pad*pad,structdevice_node*np,unsignedintindex)
From: JC Kuo <jckuo@nvidia.com> Date: 2020-08-31 04:42:00
This commit implements a register map which grants USB (UTMI and HSIC)
sleepwalk registers access to USB phy drivers. The USB sleepwalk logic
is in PMC hardware block but USB phy drivers have the best knowledge
of proper programming sequence. This approach prevents using custom
pmc APIs.
Signed-off-by: JC Kuo <jckuo@nvidia.com>
---
drivers/soc/tegra/pmc.c | 89 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 89 insertions(+)
From: JC Kuo <jckuo@nvidia.com> Date: 2020-08-31 04:42:02
PLLE hardware power sequencer references PEX/SATA UPHY PLL hardware
power sequencers' output to enable/disable PLLE. PLLE hardware power
sequencer has to be enabled only after PEX/SATA UPHY PLL's sequencers
are enabled.
Signed-off-by: JC Kuo <jckuo@nvidia.com>
---
drivers/clk/tegra/clk-pll.c | 12 ------------
1 file changed, 12 deletions(-)
Please start commit subjects with a capital letter after the prefix.
Also, please avoid t210 as abbreviation and use tegra210 instead.
The above should be something like:
phy: tegra: xusb: tegra210: Rearrange UPHY init
Or perhaps:
phy: tegra: xusb: Rearrange UPHY init on Tegra210
On Mon, Aug 31, 2020 at 12:40:34PM +0800, JC Kuo wrote:
This commit is a preparation for enabling XUSB SC7 support.
It rearranges Tegra210 XUSB PADCTL UPHY initialization sequence,
for the following reasons:
1. PLLE hardware power sequencer has to be enabled only after both
PEX UPHY PLL and SATA UPHY PLL are initialized.
tegra210_uphy_init() -> tegra210_pex_uphy_enable()
-> tegra210_sata_uphy_enable()
-> tegra210_plle_hw_sequence_start()
-> tegra210_aux_mux_lp0_clamp_disable()
2. Once UPHY PLL hardware power sequencer is enabled, do not assert
reset to PEX/SATA PLLs, otherwise UPHY PLL operation will be
broken.
reset_control_assert(pcie->rst) and reset_control_assert(sata->rst)
are removed from PEX/SATA UPHY disable procedure.
3. At cold boot and SC7 exit, the following bits must be cleared after
PEX/SATA lanes are out of IDDQ (IDDQ_DISABLE=1).
a. XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_CLAMP_EN,
b. XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_CLAMP_EN_EARLY
c. XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_VCORE_DOWN
tegra210_pex_uphy_enable() and tegra210_sata_uphy_enable() are in
charge of bringing lanes out of IDDQ, and then AUX_MUX_LP0_* bits
will be cleared by tegra210_aux_mux_lp0_clamp_disable().
4. The programming sequence in tegra210_usb3_port_enable() is required
for both cold boot and SC7 exit, and must be performed only after
PEX/SATA UPHY is initialized. Therefore, this commit moves the
programming sequence to .power_on() stub which is invoked after
.init(). PEX/SATA UPHY is initialzied in .init().
Signed-off-by: JC Kuo <jckuo@nvidia.com>
---
drivers/phy/tegra/xusb-tegra210.c | 495 ++++++++++++++++--------------
drivers/phy/tegra/xusb.c | 2 +-
drivers/phy/tegra/xusb.h | 6 +-
3 files changed, 270 insertions(+), 233 deletions(-)
You've listed 4 logically separate changes in the commit message, so I'm
wondering if it's possible to split this patch into 4 different ones. It
might not be worth doing that if they all basically fix the sequence in
one go, but it's pretty difficult to review this as-is.
@@ -256,23 +256,52 @@ to_tegra210_xusb_padctl(struct tegra_xusb_padctl *padctl)returncontainer_of(padctl,structtegra210_xusb_padctl,base);}+staticconststructtegra_xusb_lane_maptegra210_usb3_map[]={+{0,"pcie",6},+{1,"pcie",5},+{2,"pcie",0},+{2,"pcie",3},+{3,"pcie",4},+{3,"pcie",4},+{0,NULL,0}+};++staticinttegra210_usb3_lane_map(structtegra_xusb_lane*lane)+{+conststructtegra_xusb_lane_map*map;++for(map=tegra210_usb3_map;map->type;map++){+if(map->index==lane->index&&+strcmp(map->type,lane->pad->soc->name)==0){+dev_dbg(lane->pad->padctl->dev,+"lane = %s map to port = usb3-%d\n",
"mapped to port"?
+ lane->pad->soc->lanes[lane->index].name,
+ map->port);
+ return map->port;
+ }
+ }
+
+ return -EINVAL;
+}
+
/* must be called under padctl->lock */
static int tegra210_pex_uphy_enable(struct tegra_xusb_padctl *padctl)
{
struct tegra_xusb_pcie_pad *pcie = to_pcie_pad(padctl->pcie);
unsigned long timeout;
u32 value;
- int err;
+ int err, i;
i should be unsigned to match the type of padctl->pcie->soc->num_lanes.
- if (pcie->enable > 0) {
- pcie->enable++;
+ if (pcie->enable)
return 0;
- }
err = clk_prepare_enable(pcie->pll);
if (err < 0)
return err;
+ if (tegra210_plle_hw_sequence_is_enabled())
+ goto skip_pll_init;
+
err = reset_control_deassert(pcie->rst);
Is it guaranteed that the reset is asserted if the PLLE HW sequencer is
enabled? I suppose with the change to not enable the sequencer by
default in one of the earlier patches this may indeed be a valid
assumption.
quoted hunk
if (err < 0)
goto disable;
@@ -455,7 +484,14 @@ static int tegra210_pex_uphy_enable(struct tegra_xusb_padctl *padctl) tegra210_xusb_pll_hw_sequence_start();- pcie->enable++;+skip_pll_init:+ pcie->enable = true;++ for (i = 0; i < padctl->pcie->soc->num_lanes; i++) {+ value = padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX);+ value |= XUSB_PADCTL_USB3_PAD_MUX_PCIE_IDDQ_DISABLE(i);+ padctl_writel(padctl, value, XUSB_PADCTL_USB3_PAD_MUX);+ } return 0;
- mutex_lock(&padctl->lock);
-
- if (WARN_ON(sata->enable == 0))
- goto unlock;
+ if (WARN_ON(!sata->enable))
+ return;
- if (--sata->enable > 0)
- goto unlock;
+ sata->enable = false;
- reset_control_assert(sata->rst);
+ for (i = 0; i < padctl->sata->soc->num_lanes; i++) {
+ value = padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX);
+ value &= ~XUSB_PADCTL_USB3_PAD_MUX_SATA_IDDQ_DISABLE(i);
+ padctl_writel(padctl, value, XUSB_PADCTL_USB3_PAD_MUX);
+ }
clk_disable_unprepare(sata->pll);
-
-unlock:
- mutex_unlock(&padctl->lock);
}
-static int tegra210_xusb_padctl_enable(struct tegra_xusb_padctl *padctl)
+static void tegra210_aux_mux_lp0_clamp_disable(struct tegra_xusb_padctl *padctl)
{
u32 value;
- mutex_lock(&padctl->lock);
-
- if (padctl->enable++ > 0)
- goto out;
-
value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1);
value &= ~XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_CLAMP_EN;
padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1);
@@ -751,24 +797,12 @@ static int tegra210_xusb_padctl_enable(struct tegra_xusb_padctl *padctl) value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1); value &= ~XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_VCORE_DOWN; padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1);--out:- mutex_unlock(&padctl->lock);- return 0; }-static int tegra210_xusb_padctl_disable(struct tegra_xusb_padctl *padctl)+static void tegra210_aux_mux_lp0_clamp_enable(struct tegra_xusb_padctl *padctl) { u32 value;- mutex_lock(&padctl->lock);-- if (WARN_ON(padctl->enable == 0))- goto out;-- if (--padctl->enable > 0)- goto out;- value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1); value |= XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_VCORE_DOWN; padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1);
@@ -784,12 +818,36 @@ static int tegra210_xusb_padctl_disable(struct tegra_xusb_padctl *padctl) value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1); value |= XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_CLAMP_EN; padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1);+}++static int tegra210_uphy_init(struct tegra_xusb_padctl *padctl)+{+ if (padctl->pcie)+ tegra210_pex_uphy_enable(padctl);+ if (padctl->sata)+ tegra210_sata_uphy_enable(padctl);++ if (!tegra210_plle_hw_sequence_is_enabled())+ tegra210_plle_hw_sequence_start();+ else+ dev_dbg(padctl->dev, "PLLE is already in HW control\n");++ tegra210_aux_mux_lp0_clamp_disable(padctl);-out:- mutex_unlock(&padctl->lock); return 0; }+static void __maybe_unused+tegra210_uphy_deinit(struct tegra_xusb_padctl *padctl)+{+ tegra210_aux_mux_lp0_clamp_enable(padctl);
Do we need tegra210_plle_hw_sequence_stop() here?
+
+ if (padctl->pcie)
+ tegra210_pex_uphy_disable(padctl);
+ if (padctl->sata)
+ tegra210_sata_uphy_disable(padctl);
Maybe reverse the order of these two so that they are symmetrical with
tegra210_uphy_init()? Also, single blank lines between the two blocks
make this easier to read, in my opinion.
Thierry
On Mon, Aug 31, 2020 at 12:40:35PM +0800, JC Kuo wrote:
quoted hunk
As per Tegra210 TRM, before changing lane assignments, driver should
keep lanes in IDDQ and sleep state; after changing lane assignments,
driver should bring lanes out of IDDQ.
This commit implements the required operations.
Signed-off-by: JC Kuo <jckuo@nvidia.com>
---
drivers/phy/tegra/xusb-tegra210.c | 94 +++++++++++++++++++++++++++++++
drivers/phy/tegra/xusb.c | 6 ++
drivers/phy/tegra/xusb.h | 4 +-
3 files changed, 103 insertions(+), 1 deletion(-)
This looks like abstraction at the wrong level. You introduce this
arbtitrary offset 7 to differentiate between the two types, whereas what
you really only seem to be after is to get the correct offset.
Can't we instead make tegra210_uphy_lane_iddq_{enable,disable}() take
the offset instead and push the logic to pick the offset into the
callers? We could then have an extra helper that determines the offset
from the lane if we want to avoid duplicating that logic.
Or perhaps an even better way would be to store the offset to this MISC
register in struct tegra_xusb_lane_soc? Something like this perhaps:
struct tegra_xusb_lane_soc {
...
struct {
unsigned int misc;
} regs;
};
That way we don't even have to go through two layers but instead can
operate on the struct tegra_xusb_lane directly.
You can drop the second check because it isn't relevant here.
+
/* 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_enable && lane->pad->ops->iddq_disable)
+ lane->pad->ops->iddq_disable(lane);
Similarly, the first check can be dropped here because only the second
is relevant. It might make sense to only support IDDQ if both callbacks
are implemented, but that's not something we need to check at this
level. The check here is only to avoid calling a NULL function. If you
absolutely want to do sanity checks, do them at ->probe() time. But I
don't think we need that here. It's up to the developer to get this
right.
Again, use a capital letter to start the subject after the prefix.
On Mon, Aug 31, 2020 at 12:40:36PM +0800, JC Kuo wrote:
This commit adds sleepwalk/wake and suspend/resume interfaces
to Tegra XUSB PHY driver.
Tegra XUSB host controller driver makes use of sleepwalk functions
to enable/disable sleepwalk circuit which is in always-on partition
can respond to USB resume signals when controller is not powered.
"and can respond to ..."?
Sleepwalk can be enabled/disabled for any USB phy individually.
"USB PHY"
- tegra_xusb_padctl_enable_phy_sleepwalk()
- tegra_xusb_padctl_disable_phy_sleepwalk()
Tegra XUSB host controller driver makes use of wake functions to
enable/disable/query wake circuit which is in always-on partition
can wake system up when USB resume happens.
Wake circuit can be enabled/disabled for any USB phy individually.
On Mon, Aug 31, 2020 at 12:40:37PM +0800, JC Kuo wrote:
This commit implements a register map which grants USB (UTMI and HSIC)
sleepwalk registers access to USB phy drivers. The USB sleepwalk logic
is in PMC hardware block but USB phy drivers have the best knowledge
of proper programming sequence. This approach prevents using custom
pmc APIs.
Signed-off-by: JC Kuo <jckuo@nvidia.com>
---
drivers/soc/tegra/pmc.c | 89 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 89 insertions(+)
Same comment as in earlier patches regarding the subject and "USB PHY"
in the commit message.
This doesn't seem like a good idea. What if anyone ever thought it was a
good idea to add this to the core regmap header? We'd get a naming
conflict that would first have to get resolved.
You could call this perhaps a little bit earlier to avoid having to
clean up powergates? Since you register with devm_regmap_init() you
won't have to clean this up manually.
For that reason it makes sense as a general rule to initialize devm
things before anything that's not managed (unless, of course, if it
doesn't make any sense).
I'd prefer if we explicitly set this to false on SoC generations where
we don't have sleepwalk support (or don't need to deal with it in the
kernel). That avoids confusion as to whether this was simply forgotten
or whether the omission was on purpose.
Thierry
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".
quoted hunk
};
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.
+
+ 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);
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.
quoted hunk
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 <<
@@ -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; }
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.
@@ -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
On Mon, Aug 31, 2020 at 12:40:42PM +0800, JC Kuo wrote:
quoted hunk
This commit unlinks xhci-tegra platform device with ss/host power
domain devices. Reasons for this change is - at elpg entry, phy
sleepwalk and wake configuration need to be done before powering
down ss/host partitions, and phy need be powered off after powering
down ss/host partitions. Sequence looks like roughly below:
tegra_xusb_enter_elpg() -> xhci_suspend()
-> enable phy sleepwalk and wake if needed
-> power down ss/host partitions
-> power down phy
If ss/host power domains are linked to xhci-tegra platform device, we
are not able to perform the sequence like above.
This commit introduces:
1. tegra_xusb_unpowergate_partitions() to power up ss and host
partitions together. If ss/host power domain devices are
available, it invokes pm_runtime_get_sync() to request power
driver to power up partitions; If power domain devices are not
available, tegra_powergate_sequence_power_up() will be used to
power up partitions.
2. tegra_xusb_powergate_partitions() to power down ss and host
partitions together. If ss/host power domain devices are
available, it invokes pm_runtime_put_sync() to request power
driver to power down partitions; If power domain devices are not
available, tegra_powergate_power_off() will be used to power down
partitions.
Signed-off-by: JC Kuo <jckuo@nvidia.com>
---
drivers/usb/host/xhci-tegra.c | 202 +++++++++++++++++++---------------
1 file changed, 111 insertions(+), 91 deletions(-)
@@ -814,36 +812,12 @@ static void tegra_xusb_phy_disable(struct tegra_xusb *tegra)staticinttegra_xusb_runtime_suspend(structdevice*dev){-structtegra_xusb*tegra=dev_get_drvdata(dev);--regulator_bulk_disable(tegra->soc->num_supplies,tegra->supplies);-tegra_xusb_clk_disable(tegra);-return0;}staticinttegra_xusb_runtime_resume(structdevice*dev){-structtegra_xusb*tegra=dev_get_drvdata(dev);-interr;--err=tegra_xusb_clk_enable(tegra);-if(err){-dev_err(dev,"failed to enable clocks: %d\n",err);-returnerr;-}--err=regulator_bulk_enable(tegra->soc->num_supplies,tegra->supplies);-if(err){-dev_err(dev,"failed to enable regulators: %d\n",err);-gotodisable_clk;-}-return0;--disable_clk:-tegra_xusb_clk_disable(tegra);-returnerr;}#ifdef CONFIG_PM_SLEEP
@@ -1019,10 +993,6 @@ static int tegra_xusb_load_firmware(struct tegra_xusb *tegra)staticvoidtegra_xusb_powerdomain_remove(structdevice*dev,structtegra_xusb*tegra){-if(tegra->genpd_dl_ss)-device_link_del(tegra->genpd_dl_ss);-if(tegra->genpd_dl_host)-device_link_del(tegra->genpd_dl_host);if(!IS_ERR_OR_NULL(tegra->genpd_dev_ss))dev_pm_domain_detach(tegra->genpd_dev_ss,true);if(!IS_ERR_OR_NULL(tegra->genpd_dev_host))
@@ -1048,20 +1018,88 @@ static int tegra_xusb_powerdomain_init(struct device *dev,returnerr;}-tegra->genpd_dl_host=device_link_add(dev,tegra->genpd_dev_host,-DL_FLAG_PM_RUNTIME|-DL_FLAG_STATELESS);-if(!tegra->genpd_dl_host){-dev_err(dev,"adding host device link failed!\n");-return-ENODEV;+return0;+}++staticinttegra_xusb_unpowergate_partitions(structtegra_xusb*tegra)+{+structdevice*dev=tegra->dev;+booluse_genpd;+intrc;++use_genpd=of_property_read_bool(dev->of_node,"power-domains");
I don't think that's technically correct. Just because a "power-domains"
property exists in DT doesn't mean any power domains are necessarily
attached to the device. I think you'll need to check for something like
if (dev->pm_domain)
here.
Thierry
On Mon, Aug 31, 2020 at 12:40:43PM +0800, JC Kuo wrote:
quoted hunk
This commit implements the complete programming sequence for ELPG
entry and exit.
1. At ELPG entry, invokes tegra_xusb_padctl_enable_phy_sleepwalk()
and tegra_xusb_padctl_enable_phy_wake() to configure XUSB PADCTL
sleepwalk and wake detection circuits to maintain USB lines level
and respond to wake events (wake-on-connect, wake-on-disconnect,
device-initiated-wake).
2. At ELPG exit, invokes tegra_xusb_padctl_disable_phy_sleepwalk()
and tegra_xusb_padctl_disable_phy_wake() to disarm sleepwalk and
wake detection circuits.
At runtime suspend, XUSB host controller can enter ELPG to reduce
power consumption. When XUSB PADCTL wake detection circuit detects
a wake event, an interrupt will be raised. xhci-tegra driver then
will invoke pm_runtime_resume() for xhci-tegra.
Runtime resume could also be triggered by protocol drivers, this is
the host-initiated-wake event. At runtime resume, xhci-tegra driver
brings XUSB host controller out of ELPG to handle the wake events.
The same ELPG enter/exit procedure will be performed for system
suspend/resume path so USB devices can remain connected across SC7.
Signed-off-by: JC Kuo <jckuo@nvidia.com>
---
drivers/usb/host/xhci-tegra.c | 391 +++++++++++++++++++++++++++++++---
1 file changed, 361 insertions(+), 30 deletions(-)
@@ -1717,9 +1836,17 @@ static bool xhci_hub_ports_suspended(struct xhci_hub *hub) static int tegra_xusb_check_ports(struct tegra_xusb *tegra) { struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd);+ struct xhci_hub *rhub = xhci_get_rhub(xhci->main_hcd);+ struct xhci_bus_state *bus_state = &rhub->bus_state; unsigned long flags; int err = 0;+ if (bus_state->bus_suspended) {+ /* xusb_hub_suspend() has just directed one or more USB2 port(s)+ * to U3 state, it takes 3ms to enter U3. */+ usleep_range(3000, 4000);+ }+ spin_lock_irqsave(&xhci->lock, flags); if (!xhci_hub_ports_suspended(&xhci->usb2_rhub) ||
@@ -1765,45 +1892,184 @@ static void tegra_xusb_restore_context(struct tegra_xusb *tegra) } }-static int tegra_xusb_enter_elpg(struct tegra_xusb *tegra, bool wakeup)+static enum usb_device_speed+tegra_xhci_portsc_to_speed(struct tegra_xusb *tegra, u32 portsc)+{+ if (DEV_LOWSPEED(portsc))+ return USB_SPEED_LOW;+ else if (DEV_HIGHSPEED(portsc))+ return USB_SPEED_HIGH;+ else if (DEV_FULLSPEED(portsc))+ return USB_SPEED_FULL;+ else if (DEV_SUPERSPEED_ANY(portsc))+ return USB_SPEED_SUPER;+ else+ return USB_SPEED_UNKNOWN;+}
As in a prior patch you can make this simpler by dropping the elses.
From: kernel test robot <hidden> Date: 2020-09-01 17:08:48
Hi JC,
I love your patch! Perhaps something to improve:
[auto build test WARNING on tegra/for-next]
[also build test WARNING on robh/for-next usb/usb-testing char-misc/char-misc-testing staging/staging-testing driver-core/driver-core-testing v5.9-rc3 next-20200828]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]
url: https://github.com/0day-ci/linux/commits/JC-Kuo/Tegra-XHCI-controller-ELPG-support/20200831-124234
base: https://git.kernel.org/pub/scm/linux/kernel/git/tegra/linux.git for-next
config: arm64-randconfig-r014-20200901 (attached as .config)
compiler: clang version 12.0.0 (https://github.com/llvm/llvm-project c10e63677f5d20f18010f8f68c631ddc97546f7d)
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# install arm64 cross compiling tool for clang build
# apt-get install binutils-aarch64-linux-gnu
# save the attached .config to linux build tree
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross ARCH=arm64
If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <redacted>
All warnings (new ones prefixed by >>):
quoted
drivers/phy/tegra/xusb-tegra186.c:1561:5: warning: no previous prototype for function 'tegra186_xusb_padctl_remote_wake_detected' [-Wmissing-prototypes]
int tegra186_xusb_padctl_remote_wake_detected(struct phy *phy)
^
drivers/phy/tegra/xusb-tegra186.c:1561:1: note: declare 'static' if the function is not intended to be used outside of this translation unit
int tegra186_xusb_padctl_remote_wake_detected(struct phy *phy)
^
static
1 warning generated.
# https://github.com/0day-ci/linux/commit/405a5b91f6af8144ded6842a07a86e53f967fde2
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review JC-Kuo/Tegra-XHCI-controller-ELPG-support/20200831-124234
git checkout 405a5b91f6af8144ded6842a07a86e53f967fde2
vim +/tegra186_xusb_padctl_remote_wake_detected +1561 drivers/phy/tegra/xusb-tegra186.c
1560
1561 int tegra186_xusb_padctl_remote_wake_detected(struct phy *phy)
1562 {
1563 struct tegra_xusb_lane *lane;
1564 struct tegra_xusb_padctl *padctl;
1565 unsigned int index;
1566
1567 if (!phy)
1568 return 0;
1569
1570 lane = phy_get_drvdata(phy);
1571 padctl = lane->pad->padctl;
1572 index = lane->index;
1573
1574 if (is_utmi_phy(phy))
1575 return tegra186_utmi_phy_remote_wake_detected(padctl, index);
1576 else if (is_usb3_phy(phy))
1577 return tegra186_usb3_phy_remote_wake_detected(padctl, index);
1578 else
1579 return -EINVAL;
1580 }
1581
---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
From: kernel test robot <hidden> Date: 2020-09-01 17:15:23
Hi JC,
I love your patch! Perhaps something to improve:
[auto build test WARNING on tegra/for-next]
[also build test WARNING on robh/for-next usb/usb-testing char-misc/char-misc-testing staging/staging-testing driver-core/driver-core-testing v5.9-rc3 next-20200828]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]
url: https://github.com/0day-ci/linux/commits/JC-Kuo/Tegra-XHCI-controller-ELPG-support/20200831-124234
base: https://git.kernel.org/pub/scm/linux/kernel/git/tegra/linux.git for-next
config: arm64-randconfig-r014-20200901 (attached as .config)
compiler: clang version 12.0.0 (https://github.com/llvm/llvm-project c10e63677f5d20f18010f8f68c631ddc97546f7d)
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# install arm64 cross compiling tool for clang build
# apt-get install binutils-aarch64-linux-gnu
# save the attached .config to linux build tree
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross ARCH=arm64
If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <redacted>
All warnings (new ones prefixed by >>):
quoted
drivers/phy/tegra/xusb-tegra210.c:1368:5: warning: no previous prototype for function 'tegra_pmc_utmi_enable_phy_sleepwalk' [-Wmissing-prototypes]
int tegra_pmc_utmi_enable_phy_sleepwalk(struct phy *phy,
^
drivers/phy/tegra/xusb-tegra210.c:1368:1: note: declare 'static' if the function is not intended to be used outside of this translation unit
int tegra_pmc_utmi_enable_phy_sleepwalk(struct phy *phy,
^
static
quoted
drivers/phy/tegra/xusb-tegra210.c:1527:5: warning: no previous prototype for function 'tegra_pmc_utmi_disable_phy_sleepwalk' [-Wmissing-prototypes]
int tegra_pmc_utmi_disable_phy_sleepwalk(struct phy *phy)
^
drivers/phy/tegra/xusb-tegra210.c:1527:1: note: declare 'static' if the function is not intended to be used outside of this translation unit
int tegra_pmc_utmi_disable_phy_sleepwalk(struct phy *phy)
^
static
quoted
drivers/phy/tegra/xusb-tegra210.c:1577:5: warning: no previous prototype for function 'tegra_pmc_hsic_enable_phy_sleepwalk' [-Wmissing-prototypes]
int tegra_pmc_hsic_enable_phy_sleepwalk(struct phy *phy)
^
drivers/phy/tegra/xusb-tegra210.c:1577:1: note: declare 'static' if the function is not intended to be used outside of this translation unit
int tegra_pmc_hsic_enable_phy_sleepwalk(struct phy *phy)
^
static
quoted
drivers/phy/tegra/xusb-tegra210.c:1680:5: warning: no previous prototype for function 'tegra_pmc_hsic_disable_phy_sleepwalk' [-Wmissing-prototypes]
int tegra_pmc_hsic_disable_phy_sleepwalk(struct phy *phy)
^
drivers/phy/tegra/xusb-tegra210.c:1680:1: note: declare 'static' if the function is not intended to be used outside of this translation unit
int tegra_pmc_hsic_disable_phy_sleepwalk(struct phy *phy)
^
static
quoted
drivers/phy/tegra/xusb-tegra210.c:3036:5: warning: no previous prototype for function 'tegra210_xusb_padctl_remote_wake_detected' [-Wmissing-prototypes]
int tegra210_xusb_padctl_remote_wake_detected(struct phy *phy)
^
drivers/phy/tegra/xusb-tegra210.c:3036:1: note: declare 'static' if the function is not intended to be used outside of this translation unit
int tegra210_xusb_padctl_remote_wake_detected(struct phy *phy)
^
static
5 warnings generated.
# https://github.com/0day-ci/linux/commit/85501cb657fc0bbb792dc08358e31fad69c8b13c
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review JC-Kuo/Tegra-XHCI-controller-ELPG-support/20200831-124234
git checkout 85501cb657fc0bbb792dc08358e31fad69c8b13c
vim +/tegra_pmc_utmi_enable_phy_sleepwalk +1368 drivers/phy/tegra/xusb-tegra210.c
1366
1367 /* T210 USB2 SLEEPWALK APIs */
1368 int tegra_pmc_utmi_enable_phy_sleepwalk(struct phy *phy,
1369 enum usb_device_speed speed)
1370 {
1371 struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
1372 struct tegra_xusb_padctl *padctl = lane->pad->padctl;
1373 struct tegra210_xusb_padctl *priv = to_tegra210_xusb_padctl(padctl);
1374 struct device *dev = padctl->dev;
1375 unsigned int port = lane->index;
1376 u32 val, tctrl, pctrl, rpd_ctrl;
1377
1378 if (speed > USB_SPEED_HIGH)
1379 return -EINVAL;
1380
1381 dev_dbg(dev, "phy enable sleepwalk usb2 %d speed %d\n", port, speed);
1382
1383 val = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL1);
1384 tctrl = TCTRL_VALUE(val);
1385 pctrl = PCTRL_VALUE(val);
1386
1387 val = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL1(port));
1388 rpd_ctrl = RPD_CTRL_VALUE(val);
1389
1390 /* ensure sleepwalk logic is disabled */
1391 val = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG(port));
1392 val &= ~UTMIP_MASTER_ENABLE(port);
1393 padctl_pmc_writel(priv, val, PMC_UTMIP_UHSIC_SLEEP_CFG(port));
1394
1395 /* ensure sleepwalk logics are in low power mode */
1396 val = padctl_pmc_readl(priv, PMC_UTMIP_MASTER_CONFIG);
1397 val |= UTMIP_PWR(port);
1398 padctl_pmc_writel(priv, val, PMC_UTMIP_MASTER_CONFIG);
1399
1400 /* set debounce time */
1401 val = padctl_pmc_readl(priv, PMC_USB_DEBOUNCE_DEL);
1402 val &= ~UTMIP_LINE_DEB_CNT(~0);
1403 val |= UTMIP_LINE_DEB_CNT(0x1);
1404 padctl_pmc_writel(priv, val, PMC_USB_DEBOUNCE_DEL);
1405
1406 /* ensure fake events of sleepwalk logic are desiabled */
1407 val = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_FAKE(port));
1408 val &= ~(UTMIP_FAKE_USBOP_VAL(port) | UTMIP_FAKE_USBON_VAL(port) |
1409 UTMIP_FAKE_USBOP_EN(port) | UTMIP_FAKE_USBON_EN(port));
1410 padctl_pmc_writel(priv, val, PMC_UTMIP_UHSIC_FAKE(port));
1411
1412 /* ensure wake events of sleepwalk logic are not latched */
1413 val = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_LINE_WAKEUP);
1414 val &= ~UTMIP_LINE_WAKEUP_EN(port);
1415 padctl_pmc_writel(priv, val, PMC_UTMIP_UHSIC_LINE_WAKEUP);
1416
1417 /* disable wake event triggers of sleepwalk logic */
1418 val = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG(port));
1419 val &= ~UTMIP_WAKE_VAL(port, ~0);
1420 val |= UTMIP_WAKE_VAL_NONE(port);
1421 padctl_pmc_writel(priv, val, PMC_UTMIP_UHSIC_SLEEP_CFG(port));
1422
1423 /* power down the line state detectors of the pad */
1424 val = padctl_pmc_readl(priv, PMC_USB_AO);
1425 val |= (USBOP_VAL_PD(port) | USBON_VAL_PD(port));
1426 padctl_pmc_writel(priv, val, PMC_USB_AO);
1427
1428 /* save state per speed */
1429 val = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SAVED_STATE(port));
1430 val &= ~SPEED(port, ~0);
1431 if (speed == USB_SPEED_HIGH)
1432 val |= UTMI_HS(port);
1433 else if (speed == USB_SPEED_FULL)
1434 val |= UTMI_FS(port);
1435 else if (speed == USB_SPEED_LOW)
1436 val |= UTMI_LS(port);
1437 else
1438 val |= UTMI_RST(port);
1439 padctl_pmc_writel(priv, val, PMC_UTMIP_UHSIC_SAVED_STATE(port));
1440
1441 /* enable the trigger of the sleepwalk logic */
1442 val = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEPWALK_CFG(port));
1443 val |= UTMIP_LINEVAL_WALK_EN(port);
1444 padctl_pmc_writel(priv, val, PMC_UTMIP_UHSIC_SLEEPWALK_CFG(port));
1445
1446 /* reset the walk pointer and clear the alarm of the sleepwalk logic,
1447 * as well as capture the configuration of the USB2.0 pad
1448 */
1449 val = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_TRIGGERS);
1450 val |= (UTMIP_CLR_WALK_PTR(port) | UTMIP_CLR_WAKE_ALARM(port) |
1451 UTMIP_CAP_CFG(port));
1452 padctl_pmc_writel(priv, val, PMC_UTMIP_UHSIC_TRIGGERS);
1453
1454 /* program electrical parameters read from XUSB PADCTL */
1455 val = padctl_pmc_readl(priv, PMC_UTMIP_TERM_PAD_CFG);
1456 val &= ~(TCTRL_VAL(~0) | PCTRL_VAL(~0));
1457 val |= (TCTRL_VAL(tctrl) | PCTRL_VAL(pctrl));
1458 padctl_pmc_writel(priv, val, PMC_UTMIP_TERM_PAD_CFG);
1459
1460 val = padctl_pmc_readl(priv, PMC_UTMIP_PAD_CFGX(port));
1461 val &= ~RPD_CTRL_PX(~0);
1462 val |= RPD_CTRL_PX(rpd_ctrl);
1463 padctl_pmc_writel(priv, val, PMC_UTMIP_PAD_CFGX(port));
1464
1465 /* setup the pull-ups and pull-downs of the signals during the four
1466 * stages of sleepwalk.
1467 * if device is connected, program sleepwalk logic to maintain a J and
1468 * keep driving K upon seeing remote wake.
1469 */
1470 val = padctl_pmc_readl(priv, PMC_UTMIP_SLEEPWALK_PX(port));
1471 val = (UTMIP_USBOP_RPD_A | UTMIP_USBOP_RPD_B | UTMIP_USBOP_RPD_C |
1472 UTMIP_USBOP_RPD_D);
1473 val |= (UTMIP_USBON_RPD_A | UTMIP_USBON_RPD_B | UTMIP_USBON_RPD_C |
1474 UTMIP_USBON_RPD_D);
1475 if (speed == USB_SPEED_UNKNOWN) {
1476 val |= (UTMIP_HIGHZ_A | UTMIP_HIGHZ_B | UTMIP_HIGHZ_C |
1477 UTMIP_HIGHZ_D);
1478 } else if ((speed == USB_SPEED_HIGH) || (speed == USB_SPEED_FULL)) {
1479 /* J state: D+/D- = high/low, K state: D+/D- = low/high */
1480 val |= UTMIP_HIGHZ_A;
1481 val |= UTMIP_AP_A;
1482 val |= (UTMIP_AN_B | UTMIP_AN_C | UTMIP_AN_D);
1483 } else if (speed == USB_SPEED_LOW) {
1484 /* J state: D+/D- = low/high, K state: D+/D- = high/low */
1485 val |= UTMIP_HIGHZ_A;
1486 val |= UTMIP_AN_A;
1487 val |= (UTMIP_AP_B | UTMIP_AP_C | UTMIP_AP_D);
1488 }
1489 padctl_pmc_writel(priv, val, PMC_UTMIP_SLEEPWALK_PX(port));
1490
1491 /* power up the line state detectors of the pad */
1492 val = padctl_pmc_readl(priv, PMC_USB_AO);
1493 val &= ~(USBOP_VAL_PD(port) | USBON_VAL_PD(port));
1494 padctl_pmc_writel(priv, val, PMC_USB_AO);
1495
1496 usleep_range(50, 100);
1497
1498 /* switch the electric control of the USB2.0 pad to PMC */
1499 val = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG(port));
1500 val |= (UTMIP_FSLS_USE_PMC(port) | UTMIP_PCTRL_USE_PMC(port) |
1501 UTMIP_TCTRL_USE_PMC(port));
1502 padctl_pmc_writel(priv, val, PMC_UTMIP_UHSIC_SLEEP_CFG(port));
1503
1504 val = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG1);
1505 val |= (UTMIP_RPD_CTRL_USE_PMC_PX(port) |
1506 UTMIP_RPU_SWITC_LOW_USE_PMC_PX(port));
1507 padctl_pmc_writel(priv, val, PMC_UTMIP_UHSIC_SLEEP_CFG1);
1508
1509 /* set the wake signaling trigger events */
1510 val = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG(port));
1511 val &= ~UTMIP_WAKE_VAL(port, ~0);
1512 val |= UTMIP_WAKE_VAL_ANY(port);
1513 padctl_pmc_writel(priv, val, PMC_UTMIP_UHSIC_SLEEP_CFG(port));
1514
1515 /* enable the wake detection */
1516 val = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG(port));
1517 val |= UTMIP_MASTER_ENABLE(port);
1518 padctl_pmc_writel(priv, val, PMC_UTMIP_UHSIC_SLEEP_CFG(port));
1519
1520 val = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_LINE_WAKEUP);
1521 val |= UTMIP_LINE_WAKEUP_EN(port);
1522 padctl_pmc_writel(priv, val, PMC_UTMIP_UHSIC_LINE_WAKEUP);
1523
1524 return 0;
1525 }
1526
1527 int tegra_pmc_utmi_disable_phy_sleepwalk(struct phy *phy)
1528 {
1529 struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
1530 struct tegra_xusb_padctl *padctl = lane->pad->padctl;
1531 struct tegra210_xusb_padctl *priv = to_tegra210_xusb_padctl(padctl);
1532 struct device *dev = padctl->dev;
1533 unsigned int port = lane->index;
1534 u32 value;
1535
1536 dev_dbg(dev, "phy disable sleepwalk usb2 %d\n", port);
1537
1538 /* disable the wake detection */
1539 value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG(port));
1540 value &= ~UTMIP_MASTER_ENABLE(port);
1541 padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_SLEEP_CFG(port));
1542
1543 value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_LINE_WAKEUP);
1544 value &= ~UTMIP_LINE_WAKEUP_EN(port);
1545 padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_LINE_WAKEUP);
1546
1547 /* switch the electric control of the USB2.0 pad to XUSB or USB2 */
1548 value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG(port));
1549 value &= ~(UTMIP_FSLS_USE_PMC(port) | UTMIP_PCTRL_USE_PMC(port) |
1550 UTMIP_TCTRL_USE_PMC(port));
1551 padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_SLEEP_CFG(port));
1552
1553 value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG1);
1554 value &= ~(UTMIP_RPD_CTRL_USE_PMC_PX(port) |
1555 UTMIP_RPU_SWITC_LOW_USE_PMC_PX(port));
1556 padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_SLEEP_CFG1);
1557
1558 /* disable wake event triggers of sleepwalk logic */
1559 value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_SLEEP_CFG(port));
1560 value &= ~UTMIP_WAKE_VAL(port, ~0);
1561 value |= UTMIP_WAKE_VAL_NONE(port);
1562 padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_SLEEP_CFG(port));
1563
1564 /* power down the line state detectors of the port */
1565 value = padctl_pmc_readl(priv, PMC_USB_AO);
1566 value |= (USBOP_VAL_PD(port) | USBON_VAL_PD(port));
1567 padctl_pmc_writel(priv, value, PMC_USB_AO);
1568
1569 /* clear alarm of the sleepwalk logic */
1570 value = padctl_pmc_readl(priv, PMC_UTMIP_UHSIC_TRIGGERS);
1571 value |= UTMIP_CLR_WAKE_ALARM(port);
1572 padctl_pmc_writel(priv, value, PMC_UTMIP_UHSIC_TRIGGERS);
1573
1574 return 0;
1575 }
1576
---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
From: JC Kuo <jckuo@nvidia.com> Date: 2020-09-04 09:11:00
Hi Thierry,
Thanks for review.
On 8/31/20 7:42 PM, Thierry Reding wrote:
Please start commit subjects with a capital letter after the prefix.
Also, please avoid t210 as abbreviation and use tegra210 instead.
The above should be something like:
phy: tegra: xusb: tegra210: Rearrange UPHY init
Or perhaps:
phy: tegra: xusb: Rearrange UPHY init on Tegra210
I will take this one. Thanks.
On Mon, Aug 31, 2020 at 12:40:34PM +0800, JC Kuo wrote:
quoted
This commit is a preparation for enabling XUSB SC7 support.
It rearranges Tegra210 XUSB PADCTL UPHY initialization sequence,
for the following reasons:
1. PLLE hardware power sequencer has to be enabled only after both
PEX UPHY PLL and SATA UPHY PLL are initialized.
tegra210_uphy_init() -> tegra210_pex_uphy_enable()
-> tegra210_sata_uphy_enable()
-> tegra210_plle_hw_sequence_start()
-> tegra210_aux_mux_lp0_clamp_disable()
2. Once UPHY PLL hardware power sequencer is enabled, do not assert
reset to PEX/SATA PLLs, otherwise UPHY PLL operation will be
broken.
reset_control_assert(pcie->rst) and reset_control_assert(sata->rst)
are removed from PEX/SATA UPHY disable procedure.
3. At cold boot and SC7 exit, the following bits must be cleared after
PEX/SATA lanes are out of IDDQ (IDDQ_DISABLE=1).
a. XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_CLAMP_EN,
b. XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_CLAMP_EN_EARLY
c. XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_VCORE_DOWN
tegra210_pex_uphy_enable() and tegra210_sata_uphy_enable() are in
charge of bringing lanes out of IDDQ, and then AUX_MUX_LP0_* bits
will be cleared by tegra210_aux_mux_lp0_clamp_disable().
4. The programming sequence in tegra210_usb3_port_enable() is required
for both cold boot and SC7 exit, and must be performed only after
PEX/SATA UPHY is initialized. Therefore, this commit moves the
programming sequence to .power_on() stub which is invoked after
.init(). PEX/SATA UPHY is initialzied in .init().
Signed-off-by: JC Kuo <jckuo@nvidia.com>
---
drivers/phy/tegra/xusb-tegra210.c | 495 ++++++++++++++++--------------
drivers/phy/tegra/xusb.c | 2 +-
drivers/phy/tegra/xusb.h | 6 +-
3 files changed, 270 insertions(+), 233 deletions(-)
You've listed 4 logically separate changes in the commit message, so I'm
wondering if it's possible to split this patch into 4 different ones. It
might not be worth doing that if they all basically fix the sequence in
one go, but it's pretty difficult to review this as-is.
I found #1 and #3 are not possible to be split. I will submit #2 and #4 as
separate changes.
@@ -256,23 +256,52 @@ to_tegra210_xusb_padctl(struct tegra_xusb_padctl *padctl)returncontainer_of(padctl,structtegra210_xusb_padctl,base);}+staticconststructtegra_xusb_lane_maptegra210_usb3_map[]={+{0,"pcie",6},+{1,"pcie",5},+{2,"pcie",0},+{2,"pcie",3},+{3,"pcie",4},+{3,"pcie",4},+{0,NULL,0}+};++staticinttegra210_usb3_lane_map(structtegra_xusb_lane*lane)+{+conststructtegra_xusb_lane_map*map;++for(map=tegra210_usb3_map;map->type;map++){+if(map->index==lane->index&&+strcmp(map->type,lane->pad->soc->name)==0){+dev_dbg(lane->pad->padctl->dev,+"lane = %s map to port = usb3-%d\n",
"mapped to port"?
Yes, each PEX/SATA lane maps to an USB3 (super-speed) port.
quoted
+ lane->pad->soc->lanes[lane->index].name,
+ map->port);
+ return map->port;
+ }
+ }
+
+ return -EINVAL;
+}
+
/* must be called under padctl->lock */
static int tegra210_pex_uphy_enable(struct tegra_xusb_padctl *padctl)
{
struct tegra_xusb_pcie_pad *pcie = to_pcie_pad(padctl->pcie);
unsigned long timeout;
u32 value;
- int err;
+ int err, i;
i should be unsigned to match the type of padctl->pcie->soc->num_lanes.
I will fix this. Thanks.
quoted
- if (pcie->enable > 0) {
- pcie->enable++;
+ if (pcie->enable)
return 0;
- }
err = clk_prepare_enable(pcie->pll);
if (err < 0)
return err;
+ if (tegra210_plle_hw_sequence_is_enabled())
+ goto skip_pll_init;
+
err = reset_control_deassert(pcie->rst);
Is it guaranteed that the reset is asserted if the PLLE HW sequencer is
enabled? I suppose with the change to not enable the sequencer by
default in one of the earlier patches this may indeed be a valid
assumption.
Yes, reset is de-asserted before PLLE initialization happens.
quoted
if (err < 0)
goto disable;
@@ -455,7 +484,14 @@ static int tegra210_pex_uphy_enable(struct tegra_xusb_padctl *padctl) tegra210_xusb_pll_hw_sequence_start();- pcie->enable++;+skip_pll_init:+ pcie->enable = true;++ for (i = 0; i < padctl->pcie->soc->num_lanes; i++) {+ value = padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX);+ value |= XUSB_PADCTL_USB3_PAD_MUX_PCIE_IDDQ_DISABLE(i);+ padctl_writel(padctl, value, XUSB_PADCTL_USB3_PAD_MUX);+ } return 0;
- mutex_lock(&padctl->lock);
-
- if (WARN_ON(sata->enable == 0))
- goto unlock;
+ if (WARN_ON(!sata->enable))
+ return;
- if (--sata->enable > 0)
- goto unlock;
+ sata->enable = false;
- reset_control_assert(sata->rst);
+ for (i = 0; i < padctl->sata->soc->num_lanes; i++) {
+ value = padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX);
+ value &= ~XUSB_PADCTL_USB3_PAD_MUX_SATA_IDDQ_DISABLE(i);
+ padctl_writel(padctl, value, XUSB_PADCTL_USB3_PAD_MUX);
+ }
clk_disable_unprepare(sata->pll);
-
-unlock:
- mutex_unlock(&padctl->lock);
}
-static int tegra210_xusb_padctl_enable(struct tegra_xusb_padctl *padctl)
+static void tegra210_aux_mux_lp0_clamp_disable(struct tegra_xusb_padctl *padctl)
{
u32 value;
- mutex_lock(&padctl->lock);
-
- if (padctl->enable++ > 0)
- goto out;
-
value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1);
value &= ~XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_CLAMP_EN;
padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1);
@@ -751,24 +797,12 @@ static int tegra210_xusb_padctl_enable(struct tegra_xusb_padctl *padctl) value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1); value &= ~XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_VCORE_DOWN; padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1);--out:- mutex_unlock(&padctl->lock);- return 0; }-static int tegra210_xusb_padctl_disable(struct tegra_xusb_padctl *padctl)+static void tegra210_aux_mux_lp0_clamp_enable(struct tegra_xusb_padctl *padctl) { u32 value;- mutex_lock(&padctl->lock);-- if (WARN_ON(padctl->enable == 0))- goto out;-- if (--padctl->enable > 0)- goto out;- value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1); value |= XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_VCORE_DOWN; padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1);
@@ -784,12 +818,36 @@ static int tegra210_xusb_padctl_disable(struct tegra_xusb_padctl *padctl) value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1); value |= XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_CLAMP_EN; padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1);+}++static int tegra210_uphy_init(struct tegra_xusb_padctl *padctl)+{+ if (padctl->pcie)+ tegra210_pex_uphy_enable(padctl);+ if (padctl->sata)+ tegra210_sata_uphy_enable(padctl);++ if (!tegra210_plle_hw_sequence_is_enabled())+ tegra210_plle_hw_sequence_start();+ else+ dev_dbg(padctl->dev, "PLLE is already in HW control\n");++ tegra210_aux_mux_lp0_clamp_disable(padctl);-out:- mutex_unlock(&padctl->lock); return 0; }+static void __maybe_unused+tegra210_uphy_deinit(struct tegra_xusb_padctl *padctl)+{+ tegra210_aux_mux_lp0_clamp_enable(padctl);
Do we need tegra210_plle_hw_sequence_stop() here?
PLLE hardware power sequencer must remain enabled at SC7 entry.
quoted
+
+ if (padctl->pcie)
+ tegra210_pex_uphy_disable(padctl);
+ if (padctl->sata)
+ tegra210_sata_uphy_disable(padctl);
Maybe reverse the order of these two so that they are symmetrical with
tegra210_uphy_init()? Also, single blank lines between the two blocks
make this easier to read, in my opinion.
From: JC Kuo <jckuo@nvidia.com> Date: 2020-09-07 02:26:32
Hi Thierry,
Thanks for review. I will amend accordingly and submit a new patch.
JC
On 8/31/20 7:53 PM, Thierry Reding wrote:
On Mon, Aug 31, 2020 at 12:40:35PM +0800, JC Kuo wrote:
quoted
As per Tegra210 TRM, before changing lane assignments, driver should
keep lanes in IDDQ and sleep state; after changing lane assignments,
driver should bring lanes out of IDDQ.
This commit implements the required operations.
Signed-off-by: JC Kuo <jckuo@nvidia.com>
---
drivers/phy/tegra/xusb-tegra210.c | 94 +++++++++++++++++++++++++++++++
drivers/phy/tegra/xusb.c | 6 ++
drivers/phy/tegra/xusb.h | 4 +-
3 files changed, 103 insertions(+), 1 deletion(-)
This looks like abstraction at the wrong level. You introduce this
arbtitrary offset 7 to differentiate between the two types, whereas what
you really only seem to be after is to get the correct offset.
Can't we instead make tegra210_uphy_lane_iddq_{enable,disable}() take
the offset instead and push the logic to pick the offset into the
callers? We could then have an extra helper that determines the offset
from the lane if we want to avoid duplicating that logic.
Or perhaps an even better way would be to store the offset to this MISC
register in struct tegra_xusb_lane_soc? Something like this perhaps:
struct tegra_xusb_lane_soc {
...
struct {
unsigned int misc;
} regs;
};
That way we don't even have to go through two layers but instead can
operate on the struct tegra_xusb_lane directly.
You can drop the second check because it isn't relevant here.
quoted
+
/* 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_enable && lane->pad->ops->iddq_disable)
+ lane->pad->ops->iddq_disable(lane);
Similarly, the first check can be dropped here because only the second
is relevant. It might make sense to only support IDDQ if both callbacks
are implemented, but that's not something we need to check at this
level. The check here is only to avoid calling a NULL function. If you
absolutely want to do sanity checks, do them at ->probe() time. But I
don't think we need that here. It's up to the developer to get this
right.
From: JC Kuo <jckuo@nvidia.com> Date: 2020-09-07 02:34:52
Hi Thierry,
Thanks for review. I will amend accordingly and submit a new patch.
JC
On 8/31/20 7:58 PM, Thierry Reding wrote:
Again, use a capital letter to start the subject after the prefix.
On Mon, Aug 31, 2020 at 12:40:36PM +0800, JC Kuo wrote:
quoted
This commit adds sleepwalk/wake and suspend/resume interfaces
to Tegra XUSB PHY driver.
Tegra XUSB host controller driver makes use of sleepwalk functions
to enable/disable sleepwalk circuit which is in always-on partition
can respond to USB resume signals when controller is not powered.
"and can respond to ..."?
quoted
Sleepwalk can be enabled/disabled for any USB phy individually.
"USB PHY"
quoted
- tegra_xusb_padctl_enable_phy_sleepwalk()
- tegra_xusb_padctl_disable_phy_sleepwalk()
Tegra XUSB host controller driver makes use of wake functions to
enable/disable/query wake circuit which is in always-on partition
can wake system up when USB resume happens.
Wake circuit can be enabled/disabled for any USB phy individually.
From: JC Kuo <jckuo@nvidia.com> Date: 2020-09-07 03:07:27
Hi Thierry,
Thanks for review. I will amend accordingly and submit a new patch.
JC
On 8/31/20 8:09 PM, Thierry Reding wrote:
On Mon, Aug 31, 2020 at 12:40:37PM +0800, JC Kuo wrote:
quoted
This commit implements a register map which grants USB (UTMI and HSIC)
sleepwalk registers access to USB phy drivers. The USB sleepwalk logic
is in PMC hardware block but USB phy drivers have the best knowledge
of proper programming sequence. This approach prevents using custom
pmc APIs.
Signed-off-by: JC Kuo <jckuo@nvidia.com>
---
drivers/soc/tegra/pmc.c | 89 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 89 insertions(+)
Same comment as in earlier patches regarding the subject and "USB PHY"
in the commit message.
This doesn't seem like a good idea. What if anyone ever thought it was a
good idea to add this to the core regmap header? We'd get a naming
conflict that would first have to get resolved.
You could call this perhaps a little bit earlier to avoid having to
clean up powergates? Since you register with devm_regmap_init() you
won't have to clean this up manually.
For that reason it makes sense as a general rule to initialize devm
things before anything that's not managed (unless, of course, if it
doesn't make any sense).
I'd prefer if we explicitly set this to false on SoC generations where
we don't have sleepwalk support (or don't need to deal with it in the
kernel). That avoids confusion as to whether this was simply forgotten
or whether the omission was on purpose.
Thierry
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".
Yes, I will move and rename.
quoted
};
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.
Got it. Thanks.
quoted
+{
+ 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?
I will remove it.
quoted
+ 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.
+
+ 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);
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.
I will make it a change. Thanks.
quoted
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 <<
@@ -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; }
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.
@@ -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.
Understood. I will make it optional in the next revision.
From: JC Kuo <jckuo@nvidia.com> Date: 2020-09-08 02:19:31
On 8/31/20 8:42 PM, Thierry Reding wrote:
On Mon, Aug 31, 2020 at 12:40:42PM +0800, JC Kuo wrote:
quoted
This commit unlinks xhci-tegra platform device with ss/host power
domain devices. Reasons for this change is - at elpg entry, phy
sleepwalk and wake configuration need to be done before powering
down ss/host partitions, and phy need be powered off after powering
down ss/host partitions. Sequence looks like roughly below:
tegra_xusb_enter_elpg() -> xhci_suspend()
-> enable phy sleepwalk and wake if needed
-> power down ss/host partitions
-> power down phy
If ss/host power domains are linked to xhci-tegra platform device, we
are not able to perform the sequence like above.
This commit introduces:
1. tegra_xusb_unpowergate_partitions() to power up ss and host
partitions together. If ss/host power domain devices are
available, it invokes pm_runtime_get_sync() to request power
driver to power up partitions; If power domain devices are not
available, tegra_powergate_sequence_power_up() will be used to
power up partitions.
2. tegra_xusb_powergate_partitions() to power down ss and host
partitions together. If ss/host power domain devices are
available, it invokes pm_runtime_put_sync() to request power
driver to power down partitions; If power domain devices are not
available, tegra_powergate_power_off() will be used to power down
partitions.
Signed-off-by: JC Kuo <jckuo@nvidia.com>
---
drivers/usb/host/xhci-tegra.c | 202 +++++++++++++++++++---------------
1 file changed, 111 insertions(+), 91 deletions(-)
@@ -814,36 +812,12 @@ static void tegra_xusb_phy_disable(struct tegra_xusb *tegra)staticinttegra_xusb_runtime_suspend(structdevice*dev){-structtegra_xusb*tegra=dev_get_drvdata(dev);--regulator_bulk_disable(tegra->soc->num_supplies,tegra->supplies);-tegra_xusb_clk_disable(tegra);-return0;}staticinttegra_xusb_runtime_resume(structdevice*dev){-structtegra_xusb*tegra=dev_get_drvdata(dev);-interr;--err=tegra_xusb_clk_enable(tegra);-if(err){-dev_err(dev,"failed to enable clocks: %d\n",err);-returnerr;-}--err=regulator_bulk_enable(tegra->soc->num_supplies,tegra->supplies);-if(err){-dev_err(dev,"failed to enable regulators: %d\n",err);-gotodisable_clk;-}-return0;--disable_clk:-tegra_xusb_clk_disable(tegra);-returnerr;}#ifdef CONFIG_PM_SLEEP
@@ -1019,10 +993,6 @@ static int tegra_xusb_load_firmware(struct tegra_xusb *tegra)staticvoidtegra_xusb_powerdomain_remove(structdevice*dev,structtegra_xusb*tegra){-if(tegra->genpd_dl_ss)-device_link_del(tegra->genpd_dl_ss);-if(tegra->genpd_dl_host)-device_link_del(tegra->genpd_dl_host);if(!IS_ERR_OR_NULL(tegra->genpd_dev_ss))dev_pm_domain_detach(tegra->genpd_dev_ss,true);if(!IS_ERR_OR_NULL(tegra->genpd_dev_host))
@@ -1048,20 +1018,88 @@ static int tegra_xusb_powerdomain_init(struct device *dev,returnerr;}-tegra->genpd_dl_host=device_link_add(dev,tegra->genpd_dev_host,-DL_FLAG_PM_RUNTIME|-DL_FLAG_STATELESS);-if(!tegra->genpd_dl_host){-dev_err(dev,"adding host device link failed!\n");-return-ENODEV;+return0;+}++staticinttegra_xusb_unpowergate_partitions(structtegra_xusb*tegra)+{+structdevice*dev=tegra->dev;+booluse_genpd;+intrc;++use_genpd=of_property_read_bool(dev->of_node,"power-domains");
I don't think that's technically correct. Just because a "power-domains"
property exists in DT doesn't mean any power domains are necessarily
attached to the device. I think you'll need to check for something like
if (dev->pm_domain)
here.
Thanks Thierry. I will do so in the next revision.
From: JC Kuo <jckuo@nvidia.com> Date: 2020-09-08 02:29:19
Hi Thierry,
Thanks for review. I will amend accordingly and submit a new revision.
JC
On 8/31/20 8:50 PM, Thierry Reding wrote:
On Mon, Aug 31, 2020 at 12:40:43PM +0800, JC Kuo wrote:
quoted
This commit implements the complete programming sequence for ELPG
entry and exit.
1. At ELPG entry, invokes tegra_xusb_padctl_enable_phy_sleepwalk()
and tegra_xusb_padctl_enable_phy_wake() to configure XUSB PADCTL
sleepwalk and wake detection circuits to maintain USB lines level
and respond to wake events (wake-on-connect, wake-on-disconnect,
device-initiated-wake).
2. At ELPG exit, invokes tegra_xusb_padctl_disable_phy_sleepwalk()
and tegra_xusb_padctl_disable_phy_wake() to disarm sleepwalk and
wake detection circuits.
At runtime suspend, XUSB host controller can enter ELPG to reduce
power consumption. When XUSB PADCTL wake detection circuit detects
a wake event, an interrupt will be raised. xhci-tegra driver then
will invoke pm_runtime_resume() for xhci-tegra.
Runtime resume could also be triggered by protocol drivers, this is
the host-initiated-wake event. At runtime resume, xhci-tegra driver
brings XUSB host controller out of ELPG to handle the wake events.
The same ELPG enter/exit procedure will be performed for system
suspend/resume path so USB devices can remain connected across SC7.
Signed-off-by: JC Kuo <jckuo@nvidia.com>
---
drivers/usb/host/xhci-tegra.c | 391 +++++++++++++++++++++++++++++++---
1 file changed, 361 insertions(+), 30 deletions(-)
@@ -1717,9 +1836,17 @@ static bool xhci_hub_ports_suspended(struct xhci_hub *hub) static int tegra_xusb_check_ports(struct tegra_xusb *tegra) { struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd);+ struct xhci_hub *rhub = xhci_get_rhub(xhci->main_hcd);+ struct xhci_bus_state *bus_state = &rhub->bus_state; unsigned long flags; int err = 0;+ if (bus_state->bus_suspended) {+ /* xusb_hub_suspend() has just directed one or more USB2 port(s)+ * to U3 state, it takes 3ms to enter U3. */+ usleep_range(3000, 4000);+ }+ spin_lock_irqsave(&xhci->lock, flags); if (!xhci_hub_ports_suspended(&xhci->usb2_rhub) ||
@@ -1765,45 +1892,184 @@ static void tegra_xusb_restore_context(struct tegra_xusb *tegra) } }-static int tegra_xusb_enter_elpg(struct tegra_xusb *tegra, bool wakeup)+static enum usb_device_speed+tegra_xhci_portsc_to_speed(struct tegra_xusb *tegra, u32 portsc)+{+ if (DEV_LOWSPEED(portsc))+ return USB_SPEED_LOW;+ else if (DEV_HIGHSPEED(portsc))+ return USB_SPEED_HIGH;+ else if (DEV_FULLSPEED(portsc))+ return USB_SPEED_FULL;+ else if (DEV_SUPERSPEED_ANY(portsc))+ return USB_SPEED_SUPER;+ else+ return USB_SPEED_UNKNOWN;+}
As in a prior patch you can make this simpler by dropping the elses.