diff options
Diffstat (limited to 'drivers/usb/phy/phy-mxs-usb.c')
-rw-r--r-- | drivers/usb/phy/phy-mxs-usb.c | 116 |
1 files changed, 116 insertions, 0 deletions
diff --git a/drivers/usb/phy/phy-mxs-usb.c b/drivers/usb/phy/phy-mxs-usb.c index 3009ab57614e..da2eb6c968ca 100644 --- a/drivers/usb/phy/phy-mxs-usb.c +++ b/drivers/usb/phy/phy-mxs-usb.c @@ -31,6 +31,9 @@ #define HW_USBPHY_CTRL_SET 0x34 #define HW_USBPHY_CTRL_CLR 0x38 +#define HW_USBPHY_DEBUG_SET 0x54 +#define HW_USBPHY_DEBUG_CLR 0x58 + #define HW_USBPHY_IP 0x90 #define HW_USBPHY_IP_SET 0x94 #define HW_USBPHY_IP_CLR 0x98 @@ -39,6 +42,9 @@ #define BM_USBPHY_CTRL_CLKGATE BIT(30) #define BM_USBPHY_CTRL_ENAUTOSET_USBCLKS BIT(26) #define BM_USBPHY_CTRL_ENAUTOCLR_USBCLKGATE BIT(25) +#define BM_USBPHY_CTRL_ENVBUSCHG_WKUP BIT(23) +#define BM_USBPHY_CTRL_ENIDCHG_WKUP BIT(22) +#define BM_USBPHY_CTRL_ENDPDMCHG_WKUP BIT(21) #define BM_USBPHY_CTRL_ENAUTOCLR_PHY_PWD BIT(20) #define BM_USBPHY_CTRL_ENAUTOCLR_CLKGATE BIT(19) #define BM_USBPHY_CTRL_ENAUTO_PWRON_PLL BIT(18) @@ -48,6 +54,25 @@ #define BM_USBPHY_IP_FIX (BIT(17) | BIT(18)) +#define BM_USBPHY_DEBUG_CLKGATE BIT(30) + +/* Anatop Registers */ +#define ANADIG_USB1_VBUS_DET_STAT 0x1c0 +#define ANADIG_USB2_VBUS_DET_STAT 0x220 + +#define ANADIG_USB1_LOOPBACK_SET 0x1e4 +#define ANADIG_USB1_LOOPBACK_CLR 0x1e8 +#define ANADIG_USB2_LOOPBACK_SET 0x244 +#define ANADIG_USB2_LOOPBACK_CLR 0x248 + +#define BM_ANADIG_USB1_VBUS_DET_STAT_VBUS_VALID BIT(3) +#define BM_ANADIG_USB2_VBUS_DET_STAT_VBUS_VALID BIT(3) + +#define BM_ANADIG_USB1_LOOPBACK_UTMI_DIG_TST1 BIT(2) +#define BM_ANADIG_USB1_LOOPBACK_TSTI_TX_EN BIT(5) +#define BM_ANADIG_USB2_LOOPBACK_UTMI_DIG_TST1 BIT(2) +#define BM_ANADIG_USB2_LOOPBACK_TSTI_TX_EN BIT(5) + #define to_mxs_phy(p) container_of((p), struct mxs_phy, phy) /* Do disconnection between PHY and controller without vbus */ @@ -141,6 +166,79 @@ static int mxs_phy_hw_init(struct mxs_phy *mxs_phy) return 0; } +/* Return true if the vbus is there */ +static bool mxs_phy_get_vbus_status(struct mxs_phy *mxs_phy) +{ + unsigned int vbus_value; + + if (mxs_phy->port_id == 0) + regmap_read(mxs_phy->regmap_anatop, + ANADIG_USB1_VBUS_DET_STAT, + &vbus_value); + else if (mxs_phy->port_id == 1) + regmap_read(mxs_phy->regmap_anatop, + ANADIG_USB2_VBUS_DET_STAT, + &vbus_value); + + if (vbus_value & BM_ANADIG_USB1_VBUS_DET_STAT_VBUS_VALID) + return true; + else + return false; +} + +static void __mxs_phy_disconnect_line(struct mxs_phy *mxs_phy, bool disconnect) +{ + void __iomem *base = mxs_phy->phy.io_priv; + u32 reg; + + if (disconnect) + writel_relaxed(BM_USBPHY_DEBUG_CLKGATE, + base + HW_USBPHY_DEBUG_CLR); + + if (mxs_phy->port_id == 0) { + reg = disconnect ? ANADIG_USB1_LOOPBACK_SET + : ANADIG_USB1_LOOPBACK_CLR; + regmap_write(mxs_phy->regmap_anatop, reg, + BM_ANADIG_USB1_LOOPBACK_UTMI_DIG_TST1 | + BM_ANADIG_USB1_LOOPBACK_TSTI_TX_EN); + } else if (mxs_phy->port_id == 1) { + reg = disconnect ? ANADIG_USB2_LOOPBACK_SET + : ANADIG_USB2_LOOPBACK_CLR; + regmap_write(mxs_phy->regmap_anatop, reg, + BM_ANADIG_USB2_LOOPBACK_UTMI_DIG_TST1 | + BM_ANADIG_USB2_LOOPBACK_TSTI_TX_EN); + } + + if (!disconnect) + writel_relaxed(BM_USBPHY_DEBUG_CLKGATE, + base + HW_USBPHY_DEBUG_SET); + + /* Delay some time, and let Linestate be SE0 for controller */ + if (disconnect) + usleep_range(500, 1000); +} + +static void mxs_phy_disconnect_line(struct mxs_phy *mxs_phy, bool on) +{ + bool vbus_is_on = false; + + /* If the SoCs don't need to disconnect line without vbus, quit */ + if (!(mxs_phy->data->flags & MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS)) + return; + + /* If the SoCs don't have anatop, quit */ + if (!mxs_phy->regmap_anatop) + return; + + vbus_is_on = mxs_phy_get_vbus_status(mxs_phy); + + if (on && !vbus_is_on) + __mxs_phy_disconnect_line(mxs_phy, true); + else + __mxs_phy_disconnect_line(mxs_phy, false); + +} + static int mxs_phy_init(struct usb_phy *phy) { int ret; @@ -185,6 +283,23 @@ static int mxs_phy_suspend(struct usb_phy *x, int suspend) return 0; } +static int mxs_phy_set_wakeup(struct usb_phy *x, bool enabled) +{ + struct mxs_phy *mxs_phy = to_mxs_phy(x); + u32 value = BM_USBPHY_CTRL_ENVBUSCHG_WKUP | + BM_USBPHY_CTRL_ENDPDMCHG_WKUP | + BM_USBPHY_CTRL_ENIDCHG_WKUP; + if (enabled) { + mxs_phy_disconnect_line(mxs_phy, true); + writel_relaxed(value, x->io_priv + HW_USBPHY_CTRL_SET); + } else { + writel_relaxed(value, x->io_priv + HW_USBPHY_CTRL_CLR); + mxs_phy_disconnect_line(mxs_phy, false); + } + + return 0; +} + static int mxs_phy_on_connect(struct usb_phy *phy, enum usb_device_speed speed) { @@ -265,6 +380,7 @@ static int mxs_phy_probe(struct platform_device *pdev) mxs_phy->phy.notify_connect = mxs_phy_on_connect; mxs_phy->phy.notify_disconnect = mxs_phy_on_disconnect; mxs_phy->phy.type = USB_PHY_TYPE_USB2; + mxs_phy->phy.set_wakeup = mxs_phy_set_wakeup; mxs_phy->clk = clk; mxs_phy->data = of_id->data; |