dm: core: Create a new header file for 'compat' features
[oweals/u-boot.git] / drivers / usb / dwc3 / core.c
index 85cc96ac87b3d663574d33f2e174cb47011965e4..c5066529b7d0a50f597775f9f03ac1e1835e512d 100644 (file)
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
 /**
  * core.c - DesignWare USB3 DRD Controller Core file
  *
  * to uboot.
  *
  * commit cd72f890d2 : usb: dwc3: core: enable phy suspend quirk on non-FPGA
- *
- * SPDX-License-Identifier:     GPL-2.0
  */
 
 #include <common.h>
+#include <cpu_func.h>
 #include <malloc.h>
 #include <dwc3-uboot.h>
 #include <asm/dma-mapping.h>
+#include <dm/device_compat.h>
+#include <dm/devres.h>
+#include <linux/err.h>
 #include <linux/ioport.h>
-
+#include <dm.h>
+#include <generic-phy.h>
 #include <linux/usb/ch9.h>
 #include <linux/usb/gadget.h>
 
@@ -111,7 +115,8 @@ static struct dwc3_event_buffer *dwc3_alloc_one_event_buffer(struct dwc3 *dwc,
 {
        struct dwc3_event_buffer        *evt;
 
-       evt = devm_kzalloc(dwc->dev, sizeof(*evt), GFP_KERNEL);
+       evt = devm_kzalloc((struct udevice *)dwc->dev, sizeof(*evt),
+                          GFP_KERNEL);
        if (!evt)
                return ERR_PTR(-ENOMEM);
 
@@ -122,6 +127,8 @@ static struct dwc3_event_buffer *dwc3_alloc_one_event_buffer(struct dwc3 *dwc,
        if (!evt->buf)
                return ERR_PTR(-ENOMEM);
 
+       dwc3_flush_cache((uintptr_t)evt->buf, evt->length);
+
        return evt;
 }
 
@@ -437,6 +444,8 @@ static int dwc3_core_init(struct dwc3 *dwc)
                goto err0;
        }
 
+       dwc3_phy_setup(dwc);
+
        ret = dwc3_core_soft_reset(dwc);
        if (ret)
                goto err0;
@@ -511,8 +520,6 @@ static int dwc3_core_init(struct dwc3 *dwc)
 
        dwc3_writel(dwc->regs, DWC3_GCTL, reg);
 
-       dwc3_phy_setup(dwc);
-
        ret = dwc3_alloc_scratch_buffers(dwc);
        if (ret)
                goto err0;
@@ -578,6 +585,12 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
        return 0;
 }
 
+static void dwc3_gadget_run(struct dwc3 *dwc)
+{
+       dwc3_writel(dwc->regs, DWC3_DCTL, DWC3_DCTL_RUN_STOP);
+       mdelay(100);
+}
+
 static void dwc3_core_exit_mode(struct dwc3 *dwc)
 {
        switch (dwc->dr_mode) {
@@ -595,6 +608,42 @@ static void dwc3_core_exit_mode(struct dwc3 *dwc)
                /* do nothing */
                break;
        }
+
+       /*
+        * switch back to peripheral mode
+        * This enables the phy to enter idle and then, if enabled, suspend.
+        */
+       dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE);
+       dwc3_gadget_run(dwc);
+}
+
+static void dwc3_uboot_hsphy_mode(struct dwc3_device *dwc3_dev,
+                                 struct dwc3 *dwc)
+{
+       enum usb_phy_interface hsphy_mode = dwc3_dev->hsphy_mode;
+       u32 reg;
+
+       /* Set dwc3 usb2 phy config */
+       reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
+
+       switch (hsphy_mode) {
+       case USBPHY_INTERFACE_MODE_UTMI:
+               reg &= ~(DWC3_GUSB2PHYCFG_PHYIF_MASK |
+                       DWC3_GUSB2PHYCFG_USBTRDTIM_MASK);
+               reg |= DWC3_GUSB2PHYCFG_PHYIF(UTMI_PHYIF_8_BIT) |
+                       DWC3_GUSB2PHYCFG_USBTRDTIM(USBTRDTIM_UTMI_8_BIT);
+               break;
+       case USBPHY_INTERFACE_MODE_UTMIW:
+               reg &= ~(DWC3_GUSB2PHYCFG_PHYIF_MASK |
+                       DWC3_GUSB2PHYCFG_USBTRDTIM_MASK);
+               reg |= DWC3_GUSB2PHYCFG_PHYIF(UTMI_PHYIF_16_BIT) |
+                       DWC3_GUSB2PHYCFG_USBTRDTIM(USBTRDTIM_UTMI_16_BIT);
+               break;
+       default:
+               break;
+       }
+
+       dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
 }
 
 #define DWC3_ALIGN_MASK                (16 - 1)
@@ -622,7 +671,8 @@ int dwc3_uboot_init(struct dwc3_device *dwc3_dev)
 
        void                    *mem;
 
-       mem = devm_kzalloc(dev, sizeof(*dwc) + DWC3_ALIGN_MASK, GFP_KERNEL);
+       mem = devm_kzalloc((struct udevice *)dev,
+                          sizeof(*dwc) + DWC3_ALIGN_MASK, GFP_KERNEL);
        if (!mem)
                return -ENOMEM;
 
@@ -690,9 +740,9 @@ int dwc3_uboot_init(struct dwc3_device *dwc3_dev)
                return -ENOMEM;
        }
 
-       if (IS_ENABLED(CONFIG_USB_DWC3_HOST))
+       if (!IS_ENABLED(CONFIG_USB_DWC3_GADGET))
                dwc->dr_mode = USB_DR_MODE_HOST;
-       else if (IS_ENABLED(CONFIG_USB_DWC3_GADGET))
+       else if (!IS_ENABLED(CONFIG_USB_HOST))
                dwc->dr_mode = USB_DR_MODE_PERIPHERAL;
 
        if (dwc->dr_mode == USB_DR_MODE_UNKNOWN)
@@ -704,6 +754,8 @@ int dwc3_uboot_init(struct dwc3_device *dwc3_dev)
                goto err0;
        }
 
+       dwc3_uboot_hsphy_mode(dwc3_dev, dwc);
+
        ret = dwc3_event_buffers_setup(dwc);
        if (ret) {
                dev_err(dwc->dev, "failed to setup event buffers\n");
@@ -784,3 +836,206 @@ MODULE_ALIAS("platform:dwc3");
 MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>");
 MODULE_LICENSE("GPL v2");
 MODULE_DESCRIPTION("DesignWare USB3 DRD Controller Driver");
+
+#if CONFIG_IS_ENABLED(PHY) && CONFIG_IS_ENABLED(DM_USB)
+int dwc3_setup_phy(struct udevice *dev, struct phy **array, int *num_phys)
+{
+       int i, ret, count;
+       struct phy *usb_phys;
+
+       /* Return if no phy declared */
+       if (!dev_read_prop(dev, "phys", NULL))
+               return 0;
+       count = dev_count_phandle_with_args(dev, "phys", "#phy-cells");
+       if (count <= 0)
+               return count;
+
+       usb_phys = devm_kcalloc(dev, count, sizeof(struct phy),
+                               GFP_KERNEL);
+       if (!usb_phys)
+               return -ENOMEM;
+
+       for (i = 0; i < count; i++) {
+               ret = generic_phy_get_by_index(dev, i, &usb_phys[i]);
+               if (ret && ret != -ENOENT) {
+                       pr_err("Failed to get USB PHY%d for %s\n",
+                              i, dev->name);
+                       return ret;
+               }
+       }
+
+       for (i = 0; i < count; i++) {
+               ret = generic_phy_init(&usb_phys[i]);
+               if (ret) {
+                       pr_err("Can't init USB PHY%d for %s\n",
+                              i, dev->name);
+                       goto phys_init_err;
+               }
+       }
+
+       for (i = 0; i < count; i++) {
+               ret = generic_phy_power_on(&usb_phys[i]);
+               if (ret) {
+                       pr_err("Can't power USB PHY%d for %s\n",
+                              i, dev->name);
+                       goto phys_poweron_err;
+               }
+       }
+
+       *array = usb_phys;
+       *num_phys =  count;
+       return 0;
+
+phys_poweron_err:
+       for (i = count - 1; i >= 0; i--)
+               generic_phy_power_off(&usb_phys[i]);
+
+       for (i = 0; i < count; i++)
+               generic_phy_exit(&usb_phys[i]);
+
+       return ret;
+
+phys_init_err:
+       for (; i >= 0; i--)
+               generic_phy_exit(&usb_phys[i]);
+
+       return ret;
+}
+
+int dwc3_shutdown_phy(struct udevice *dev, struct phy *usb_phys, int num_phys)
+{
+       int i, ret;
+
+       for (i = 0; i < num_phys; i++) {
+               if (!generic_phy_valid(&usb_phys[i]))
+                       continue;
+
+               ret = generic_phy_power_off(&usb_phys[i]);
+               ret |= generic_phy_exit(&usb_phys[i]);
+               if (ret) {
+                       pr_err("Can't shutdown USB PHY%d for %s\n",
+                              i, dev->name);
+               }
+       }
+
+       return 0;
+}
+#endif
+
+#if CONFIG_IS_ENABLED(DM_USB)
+void dwc3_of_parse(struct dwc3 *dwc)
+{
+       const u8 *tmp;
+       struct udevice *dev = dwc->dev;
+       u8 lpm_nyet_threshold;
+       u8 tx_de_emphasis;
+       u8 hird_threshold;
+
+       /* default to highest possible threshold */
+       lpm_nyet_threshold = 0xff;
+
+       /* default to -3.5dB de-emphasis */
+       tx_de_emphasis = 1;
+
+       /*
+        * default to assert utmi_sleep_n and use maximum allowed HIRD
+        * threshold value of 0b1100
+        */
+       hird_threshold = 12;
+
+       dwc->has_lpm_erratum = dev_read_bool(dev,
+                               "snps,has-lpm-erratum");
+       tmp = dev_read_u8_array_ptr(dev, "snps,lpm-nyet-threshold", 1);
+       if (tmp)
+               lpm_nyet_threshold = *tmp;
+
+       dwc->is_utmi_l1_suspend = dev_read_bool(dev,
+                               "snps,is-utmi-l1-suspend");
+       tmp = dev_read_u8_array_ptr(dev, "snps,hird-threshold", 1);
+       if (tmp)
+               hird_threshold = *tmp;
+
+       dwc->disable_scramble_quirk = dev_read_bool(dev,
+                               "snps,disable_scramble_quirk");
+       dwc->u2exit_lfps_quirk = dev_read_bool(dev,
+                               "snps,u2exit_lfps_quirk");
+       dwc->u2ss_inp3_quirk = dev_read_bool(dev,
+                               "snps,u2ss_inp3_quirk");
+       dwc->req_p1p2p3_quirk = dev_read_bool(dev,
+                               "snps,req_p1p2p3_quirk");
+       dwc->del_p1p2p3_quirk = dev_read_bool(dev,
+                               "snps,del_p1p2p3_quirk");
+       dwc->del_phy_power_chg_quirk = dev_read_bool(dev,
+                               "snps,del_phy_power_chg_quirk");
+       dwc->lfps_filter_quirk = dev_read_bool(dev,
+                               "snps,lfps_filter_quirk");
+       dwc->rx_detect_poll_quirk = dev_read_bool(dev,
+                               "snps,rx_detect_poll_quirk");
+       dwc->dis_u3_susphy_quirk = dev_read_bool(dev,
+                               "snps,dis_u3_susphy_quirk");
+       dwc->dis_u2_susphy_quirk = dev_read_bool(dev,
+                               "snps,dis_u2_susphy_quirk");
+       dwc->tx_de_emphasis_quirk = dev_read_bool(dev,
+                               "snps,tx_de_emphasis_quirk");
+       tmp = dev_read_u8_array_ptr(dev, "snps,tx_de_emphasis", 1);
+       if (tmp)
+               tx_de_emphasis = *tmp;
+
+       dwc->lpm_nyet_threshold = lpm_nyet_threshold;
+       dwc->tx_de_emphasis = tx_de_emphasis;
+
+       dwc->hird_threshold = hird_threshold
+               | (dwc->is_utmi_l1_suspend << 4);
+}
+
+int dwc3_init(struct dwc3 *dwc)
+{
+       int ret;
+
+       dwc3_cache_hwparams(dwc);
+
+       ret = dwc3_alloc_event_buffers(dwc, DWC3_EVENT_BUFFERS_SIZE);
+       if (ret) {
+               dev_err(dwc->dev, "failed to allocate event buffers\n");
+               return -ENOMEM;
+       }
+
+       ret = dwc3_core_init(dwc);
+       if (ret) {
+               dev_err(dev, "failed to initialize core\n");
+               goto core_fail;
+       }
+
+       ret = dwc3_event_buffers_setup(dwc);
+       if (ret) {
+               dev_err(dwc->dev, "failed to setup event buffers\n");
+               goto event_fail;
+       }
+
+       ret = dwc3_core_init_mode(dwc);
+       if (ret)
+               goto mode_fail;
+
+       return 0;
+
+mode_fail:
+       dwc3_event_buffers_cleanup(dwc);
+
+event_fail:
+       dwc3_core_exit(dwc);
+
+core_fail:
+       dwc3_free_event_buffers(dwc);
+
+       return ret;
+}
+
+void dwc3_remove(struct dwc3 *dwc)
+{
+       dwc3_core_exit_mode(dwc);
+       dwc3_event_buffers_cleanup(dwc);
+       dwc3_free_event_buffers(dwc);
+       dwc3_core_exit(dwc);
+       kfree(dwc->mem);
+}
+#endif