Merge branch '5.18/scsi-queue' into 5.18/scsi-fixes
[linux-block.git] / drivers / usb / dwc3 / dwc3-imx8mp.c
index d328d20abfbc4977f0b44971be18cee4d0b32ed0..174f07614318b3b09dcb260c9e6c00ba8b345d26 100644 (file)
 
 #define USB_WAKEUP_EN_MASK             GENMASK(5, 0)
 
+/* USB glue registers */
+#define USB_CTRL0              0x00
+#define USB_CTRL1              0x04
+
+#define USB_CTRL0_PORTPWR_EN   BIT(12) /* 1 - PPC enabled (default) */
+#define USB_CTRL0_USB3_FIXED   BIT(22) /* 1 - USB3 permanent attached */
+#define USB_CTRL0_USB2_FIXED   BIT(23) /* 1 - USB2 permanent attached */
+
+#define USB_CTRL1_OC_POLARITY  BIT(16) /* 0 - HIGH / 1 - LOW */
+#define USB_CTRL1_PWR_POLARITY BIT(17) /* 0 - HIGH / 1 - LOW */
+
 struct dwc3_imx8mp {
        struct device                   *dev;
        struct platform_device          *dwc3;
+       void __iomem                    *hsio_blk_base;
        void __iomem                    *glue_base;
        struct clk                      *hsio_clk;
        struct clk                      *suspend_clk;
@@ -47,6 +59,42 @@ struct dwc3_imx8mp {
        bool                            wakeup_pending;
 };
 
+static void imx8mp_configure_glue(struct dwc3_imx8mp *dwc3_imx)
+{
+       struct device *dev = dwc3_imx->dev;
+       u32 value;
+
+       if (!dwc3_imx->glue_base)
+               return;
+
+       value = readl(dwc3_imx->glue_base + USB_CTRL0);
+
+       if (device_property_read_bool(dev, "fsl,permanently-attached"))
+               value |= (USB_CTRL0_USB2_FIXED | USB_CTRL0_USB3_FIXED);
+       else
+               value &= ~(USB_CTRL0_USB2_FIXED | USB_CTRL0_USB3_FIXED);
+
+       if (device_property_read_bool(dev, "fsl,disable-port-power-control"))
+               value &= ~(USB_CTRL0_PORTPWR_EN);
+       else
+               value |= USB_CTRL0_PORTPWR_EN;
+
+       writel(value, dwc3_imx->glue_base + USB_CTRL0);
+
+       value = readl(dwc3_imx->glue_base + USB_CTRL1);
+       if (device_property_read_bool(dev, "fsl,over-current-active-low"))
+               value |= USB_CTRL1_OC_POLARITY;
+       else
+               value &= ~USB_CTRL1_OC_POLARITY;
+
+       if (device_property_read_bool(dev, "fsl,power-active-low"))
+               value |= USB_CTRL1_PWR_POLARITY;
+       else
+               value &= ~USB_CTRL1_PWR_POLARITY;
+
+       writel(value, dwc3_imx->glue_base + USB_CTRL1);
+}
+
 static void dwc3_imx8mp_wakeup_enable(struct dwc3_imx8mp *dwc3_imx)
 {
        struct dwc3     *dwc3 = platform_get_drvdata(dwc3_imx->dwc3);
@@ -55,7 +103,7 @@ static void dwc3_imx8mp_wakeup_enable(struct dwc3_imx8mp *dwc3_imx)
        if (!dwc3)
                return;
 
-       val = readl(dwc3_imx->glue_base + USB_WAKEUP_CTRL);
+       val = readl(dwc3_imx->hsio_blk_base + USB_WAKEUP_CTRL);
 
        if ((dwc3->current_dr_role == DWC3_GCTL_PRTCAP_HOST) && dwc3->xhci)
                val |= USB_WAKEUP_EN | USB_WAKEUP_SS_CONN |
@@ -64,16 +112,16 @@ static void dwc3_imx8mp_wakeup_enable(struct dwc3_imx8mp *dwc3_imx)
                val |= USB_WAKEUP_EN | USB_WAKEUP_VBUS_EN |
                       USB_WAKEUP_VBUS_SRC_SESS_VAL;
 
-       writel(val, dwc3_imx->glue_base + USB_WAKEUP_CTRL);
+       writel(val, dwc3_imx->hsio_blk_base + USB_WAKEUP_CTRL);
 }
 
 static void dwc3_imx8mp_wakeup_disable(struct dwc3_imx8mp *dwc3_imx)
 {
        u32 val;
 
-       val = readl(dwc3_imx->glue_base + USB_WAKEUP_CTRL);
+       val = readl(dwc3_imx->hsio_blk_base + USB_WAKEUP_CTRL);
        val &= ~(USB_WAKEUP_EN | USB_WAKEUP_EN_MASK);
-       writel(val, dwc3_imx->glue_base + USB_WAKEUP_CTRL);
+       writel(val, dwc3_imx->hsio_blk_base + USB_WAKEUP_CTRL);
 }
 
 static irqreturn_t dwc3_imx8mp_interrupt(int irq, void *_dwc3_imx)
@@ -100,6 +148,7 @@ static int dwc3_imx8mp_probe(struct platform_device *pdev)
        struct device           *dev = &pdev->dev;
        struct device_node      *dwc3_np, *node = dev->of_node;
        struct dwc3_imx8mp      *dwc3_imx;
+       struct resource         *res;
        int                     err, irq;
 
        if (!node) {
@@ -115,9 +164,18 @@ static int dwc3_imx8mp_probe(struct platform_device *pdev)
 
        dwc3_imx->dev = dev;
 
-       dwc3_imx->glue_base = devm_platform_ioremap_resource(pdev, 0);
-       if (IS_ERR(dwc3_imx->glue_base))
-               return PTR_ERR(dwc3_imx->glue_base);
+       dwc3_imx->hsio_blk_base = devm_platform_ioremap_resource(pdev, 0);
+       if (IS_ERR(dwc3_imx->hsio_blk_base))
+               return PTR_ERR(dwc3_imx->hsio_blk_base);
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+       if (!res) {
+               dev_warn(dev, "Base address for glue layer missing. Continuing without, some features are missing though.");
+       } else {
+               dwc3_imx->glue_base = devm_ioremap_resource(dev, res);
+               if (IS_ERR(dwc3_imx->glue_base))
+                       return PTR_ERR(dwc3_imx->glue_base);
+       }
 
        dwc3_imx->hsio_clk = devm_clk_get(dev, "hsio");
        if (IS_ERR(dwc3_imx->hsio_clk)) {
@@ -152,6 +210,8 @@ static int dwc3_imx8mp_probe(struct platform_device *pdev)
        }
        dwc3_imx->irq = irq;
 
+       imx8mp_configure_glue(dwc3_imx);
+
        pm_runtime_set_active(dev);
        pm_runtime_enable(dev);
        err = pm_runtime_get_sync(dev);
@@ -252,6 +312,9 @@ static int __maybe_unused dwc3_imx8mp_resume(struct dwc3_imx8mp *dwc3_imx,
        dwc3_imx8mp_wakeup_disable(dwc3_imx);
        dwc3_imx->pm_suspended = false;
 
+       /* Upon power loss any previous configuration is lost, restore it */
+       imx8mp_configure_glue(dwc3_imx);
+
        if (dwc3_imx->wakeup_pending) {
                dwc3_imx->wakeup_pending = false;
                if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_DEVICE) {