common: Drop linux/delay.h from common header
[oweals/u-boot.git] / drivers / usb / host / ehci-hcd.c
index c664b1629e090cc68975cb9d392aa2846fa3cd2e..f79f06320bfb4c24953914faa53ad6ad3b0096af 100644 (file)
@@ -1,23 +1,27 @@
+// SPDX-License-Identifier: GPL-2.0
 /*-
  * Copyright (c) 2007-2008, Juniper Networks, Inc.
  * Copyright (c) 2008, Excito Elektronik i Skåne AB
  * Copyright (c) 2008, Michael Trimarchi <trimarchimichael@yahoo.it>
  *
  * All rights reserved.
- *
- * SPDX-License-Identifier:    GPL-2.0
  */
 #include <common.h>
+#include <cpu_func.h>
 #include <dm.h>
 #include <errno.h>
+#include <log.h>
 #include <asm/byteorder.h>
+#include <asm/cache.h>
 #include <asm/unaligned.h>
 #include <usb.h>
 #include <asm/io.h>
 #include <malloc.h>
 #include <memalign.h>
 #include <watchdog.h>
+#include <dm/device_compat.h>
 #include <linux/compiler.h>
+#include <linux/delay.h>
 
 #include "ehci.h"
 
@@ -31,7 +35,7 @@
  */
 #define HCHALT_TIMEOUT (8 * 1000)
 
-#ifndef CONFIG_DM_USB
+#if !CONFIG_IS_ENABLED(DM_USB)
 static struct ehci_ctrl ehcic[CONFIG_USB_MAX_CONTROLLER_COUNT];
 #endif
 
@@ -52,8 +56,8 @@ static struct descriptor {
                0,              /* wHubCharacteristics */
                10,             /* bPwrOn2PwrGood */
                0,              /* bHubCntrCurrent */
-               {},             /* Device removable */
-               {}              /* at most 7 ports! XXX */
+               {               /* Device removable */
+                             /* at most 7 ports! XXX */
        },
        {
                0x12,           /* bLength */
@@ -112,7 +116,7 @@ static struct descriptor {
 
 static struct ehci_ctrl *ehci_get_ctrl(struct usb_device *udev)
 {
-#ifdef CONFIG_DM_USB
+#if CONFIG_IS_ENABLED(DM_USB)
        return dev_get_priv(usb_get_bus(udev->dev));
 #else
        return udev->controller;
@@ -134,6 +138,8 @@ static void ehci_set_usbmode(struct ehci_ctrl *ctrl)
        tmp |= USBMODE_CM_HC;
 #if defined(CONFIG_EHCI_MMIO_BIG_ENDIAN)
        tmp |= USBMODE_BE;
+#else
+       tmp &= ~USBMODE_BE;
 #endif
        ehci_writel(reg_ptr, tmp);
 }
@@ -146,9 +152,12 @@ static void ehci_powerup_fixup(struct ehci_ctrl *ctrl, uint32_t *status_reg,
 
 static uint32_t *ehci_get_portsc_register(struct ehci_ctrl *ctrl, int port)
 {
-       if (port < 0 || port >= CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS) {
+       int max_ports = HCS_N_PORTS(ehci_readl(&ctrl->hccr->cr_hcsparams));
+
+       if (port < 0 || port >= max_ports) {
                /* Printing the message would cause a scan failure! */
-               debug("The request port(%u) is not configured\n", port);
+               debug("The request port(%u) exceeds maximum port number\n",
+                     port);
                return NULL;
        }
 
@@ -203,18 +212,19 @@ static int ehci_shutdown(struct ehci_ctrl *ctrl)
 {
        int i, ret = 0;
        uint32_t cmd, reg;
-
-       if (!ctrl || !ctrl->hcor)
-               return -EINVAL;
+       int max_ports = HCS_N_PORTS(ehci_readl(&ctrl->hccr->cr_hcsparams));
 
        cmd = ehci_readl(&ctrl->hcor->or_usbcmd);
+       /* If not run, directly return */
+       if (!(cmd & CMD_RUN))
+               return 0;
        cmd &= ~(CMD_PSE | CMD_ASE);
        ehci_writel(&ctrl->hcor->or_usbcmd, cmd);
        ret = handshake(&ctrl->hcor->or_usbsts, STS_ASS | STS_PSS, 0,
                100 * 1000);
 
        if (!ret) {
-               for (i = 0; i < CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS; i++) {
+               for (i = 0; i < max_ports; i++) {
                        reg = ehci_readl(&ctrl->hcor->or_portsc[i]);
                        reg |= EHCI_PS_SUSP;
                        ehci_writel(&ctrl->hcor->or_portsc[i], reg);
@@ -235,7 +245,7 @@ static int ehci_shutdown(struct ehci_ctrl *ctrl)
 static int ehci_td_buffer(struct qTD *td, void *buf, size_t sz)
 {
        uint32_t delta, next;
-       uint32_t addr = (unsigned long)buf;
+       unsigned long addr = (unsigned long)buf;
        int idx;
 
        if (addr != ALIGN(addr, ARCH_DMA_MINALIGN))
@@ -245,7 +255,7 @@ static int ehci_td_buffer(struct qTD *td, void *buf, size_t sz)
 
        idx = 0;
        while (idx < QT_BUFFER_CNT) {
-               td->qt_buffer[idx] = cpu_to_hc32(addr);
+               td->qt_buffer[idx] = cpu_to_hc32(virt_to_phys((void *)addr));
                td->qt_buffer_hi[idx] = 0;
                next = (addr + EHCI_PAGE_SIZE) & ~(EHCI_PAGE_SIZE - 1);
                delta = next - addr;
@@ -291,6 +301,51 @@ static void ehci_update_endpt2_dev_n_port(struct usb_device *udev,
                                     QH_ENDPT2_HUBADDR(hubaddr));
 }
 
+static int ehci_enable_async(struct ehci_ctrl *ctrl)
+{
+       u32 cmd;
+       int ret;
+
+       /* Enable async. schedule. */
+       cmd = ehci_readl(&ctrl->hcor->or_usbcmd);
+       if (cmd & CMD_ASE)
+               return 0;
+
+       cmd |= CMD_ASE;
+       ehci_writel(&ctrl->hcor->or_usbcmd, cmd);
+
+       ret = handshake((uint32_t *)&ctrl->hcor->or_usbsts, STS_ASS, STS_ASS,
+                       100 * 1000);
+       if (ret < 0)
+               printf("EHCI fail timeout STS_ASS set\n");
+
+       return ret;
+}
+
+static int ehci_disable_async(struct ehci_ctrl *ctrl)
+{
+       u32 cmd;
+       int ret;
+
+       if (ctrl->async_locked)
+               return 0;
+
+       /* Disable async schedule. */
+       cmd = ehci_readl(&ctrl->hcor->or_usbcmd);
+       if (!(cmd & CMD_ASE))
+               return 0;
+
+       cmd &= ~CMD_ASE;
+       ehci_writel(&ctrl->hcor->or_usbcmd, cmd);
+
+       ret = handshake((uint32_t *)&ctrl->hcor->or_usbsts, STS_ASS, 0,
+                       100 * 1000);
+       if (ret < 0)
+               printf("EHCI fail timeout STS_ASS reset\n");
+
+       return ret;
+}
+
 static int
 ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
                   int length, struct devrequest *req)
@@ -302,9 +357,8 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
        volatile struct qTD *vtd;
        unsigned long ts;
        uint32_t *tdp;
-       uint32_t endpt, maxpacket, token, usbsts;
+       uint32_t endpt, maxpacket, token, usbsts, qhtoken;
        uint32_t c, toggle;
-       uint32_t cmd;
        int timeout;
        int ret = 0;
        struct ehci_ctrl *ctrl = ehci_get_ctrl(dev);
@@ -398,15 +452,21 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
         *   qh_overlay.qt_next ...... 13-10 H
         * - qh_overlay.qt_altnext
         */
-       qh->qh_link = cpu_to_hc32((unsigned long)&ctrl->qh_list | QH_LINK_TYPE_QH);
+       qh->qh_link = cpu_to_hc32(virt_to_phys(&ctrl->qh_list) | QH_LINK_TYPE_QH);
        c = (dev->speed != USB_SPEED_HIGH) && !usb_pipeendpoint(pipe);
        maxpacket = usb_maxpacket(dev, pipe);
        endpt = QH_ENDPT1_RL(8) | QH_ENDPT1_C(c) |
                QH_ENDPT1_MAXPKTLEN(maxpacket) | QH_ENDPT1_H(0) |
                QH_ENDPT1_DTC(QH_ENDPT1_DTC_DT_FROM_QTD) |
-               QH_ENDPT1_EPS(ehci_encode_speed(dev->speed)) |
                QH_ENDPT1_ENDPT(usb_pipeendpoint(pipe)) | QH_ENDPT1_I(0) |
                QH_ENDPT1_DEVADDR(usb_pipedevice(pipe));
+
+       /* Force FS for fsl HS quirk */
+       if (!ctrl->has_fsl_erratum_a005275)
+               endpt |= QH_ENDPT1_EPS(ehci_encode_speed(dev->speed));
+       else
+               endpt |= QH_ENDPT1_EPS(ehci_encode_speed(QH_FULL_SPEED));
+
        qh->qh_endpt1 = cpu_to_hc32(endpt);
        endpt = QH_ENDPT2_MULT(1) | QH_ENDPT2_UFCMASK(0) | QH_ENDPT2_UFSMASK(0);
        qh->qh_endpt2 = cpu_to_hc32(endpt);
@@ -415,7 +475,6 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
        qh->qh_overlay.qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE);
 
        tdp = &qh->qh_overlay.qt_next;
-
        if (req != NULL) {
                /*
                 * Setup request qTD (3.5 in ehci-r10.pdf)
@@ -438,7 +497,7 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
                        goto fail;
                }
                /* Update previous qTD! */
-               *tdp = cpu_to_hc32((unsigned long)&qtd[qtd_counter]);
+               *tdp = cpu_to_hc32(virt_to_phys(&qtd[qtd_counter]));
                tdp = &qtd[qtd_counter++].qt_next;
                toggle = 1;
        }
@@ -497,7 +556,7 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
                                goto fail;
                        }
                        /* Update previous qTD! */
-                       *tdp = cpu_to_hc32((unsigned long)&qtd[qtd_counter]);
+                       *tdp = cpu_to_hc32(virt_to_phys(&qtd[qtd_counter]));
                        tdp = &qtd[qtd_counter++].qt_next;
                        /*
                         * Data toggle has to be adjusted since the qTD transfer
@@ -528,11 +587,11 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
                        QT_TOKEN_STATUS(QT_TOKEN_STATUS_ACTIVE);
                qtd[qtd_counter].qt_token = cpu_to_hc32(token);
                /* Update previous qTD! */
-               *tdp = cpu_to_hc32((unsigned long)&qtd[qtd_counter]);
+               *tdp = cpu_to_hc32(virt_to_phys(&qtd[qtd_counter]));
                tdp = &qtd[qtd_counter++].qt_next;
        }
 
-       ctrl->qh_list.qh_link = cpu_to_hc32((unsigned long)qh | QH_LINK_TYPE_QH);
+       ctrl->qh_list.qh_link = cpu_to_hc32(virt_to_phys(qh) | QH_LINK_TYPE_QH);
 
        /* Flush dcache */
        flush_dcache_range((unsigned long)&ctrl->qh_list,
@@ -541,23 +600,12 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
        flush_dcache_range((unsigned long)qtd,
                           ALIGN_END_ADDR(struct qTD, qtd, qtd_count));
 
-       /* Set async. queue head pointer. */
-       ehci_writel(&ctrl->hcor->or_asynclistaddr, (unsigned long)&ctrl->qh_list);
-
        usbsts = ehci_readl(&ctrl->hcor->or_usbsts);
        ehci_writel(&ctrl->hcor->or_usbsts, (usbsts & 0x3f));
 
-       /* Enable async. schedule. */
-       cmd = ehci_readl(&ctrl->hcor->or_usbcmd);
-       cmd |= CMD_ASE;
-       ehci_writel(&ctrl->hcor->or_usbcmd, cmd);
-
-       ret = handshake((uint32_t *)&ctrl->hcor->or_usbsts, STS_ASS, STS_ASS,
-                       100 * 1000);
-       if (ret < 0) {
-               printf("EHCI fail timeout STS_ASS set\n");
+       ret = ehci_enable_async(ctrl);
+       if (ret)
                goto fail;
-       }
 
        /* Wait for TDs to be processed. */
        ts = get_timer(0);
@@ -577,6 +625,11 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
                        break;
                WATCHDOG_RESET();
        } while (get_timer(ts) < timeout);
+       qhtoken = hc32_to_cpu(qh->qh_overlay.qt_token);
+
+       ctrl->qh_list.qh_link = cpu_to_hc32(virt_to_phys(&ctrl->qh_list) | QH_LINK_TYPE_QH);
+       flush_dcache_range((unsigned long)&ctrl->qh_list,
+               ALIGN_END_ADDR(struct QH, &ctrl->qh_list, 1));
 
        /*
         * Invalidate the memory area occupied by buffer
@@ -587,32 +640,24 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
         * dangerous operation, it's responsibility of the calling
         * code to make sure enough space is reserved.
         */
-       invalidate_dcache_range((unsigned long)buffer,
-               ALIGN((unsigned long)buffer + length, ARCH_DMA_MINALIGN));
+       if (buffer != NULL && length > 0)
+               invalidate_dcache_range((unsigned long)buffer,
+                       ALIGN((unsigned long)buffer + length, ARCH_DMA_MINALIGN));
 
        /* Check that the TD processing happened */
        if (QT_TOKEN_GET_STATUS(token) & QT_TOKEN_STATUS_ACTIVE)
                printf("EHCI timed out on TD - token=%#x\n", token);
 
-       /* Disable async schedule. */
-       cmd = ehci_readl(&ctrl->hcor->or_usbcmd);
-       cmd &= ~CMD_ASE;
-       ehci_writel(&ctrl->hcor->or_usbcmd, cmd);
-
-       ret = handshake((uint32_t *)&ctrl->hcor->or_usbsts, STS_ASS, 0,
-                       100 * 1000);
-       if (ret < 0) {
-               printf("EHCI fail timeout STS_ASS reset\n");
+       ret = ehci_disable_async(ctrl);
+       if (ret)
                goto fail;
-       }
 
-       token = hc32_to_cpu(qh->qh_overlay.qt_token);
-       if (!(QT_TOKEN_GET_STATUS(token) & QT_TOKEN_STATUS_ACTIVE)) {
-               debug("TOKEN=%#x\n", token);
-               switch (QT_TOKEN_GET_STATUS(token) &
+       if (!(QT_TOKEN_GET_STATUS(qhtoken) & QT_TOKEN_STATUS_ACTIVE)) {
+               debug("TOKEN=%#x\n", qhtoken);
+               switch (QT_TOKEN_GET_STATUS(qhtoken) &
                        ~(QT_TOKEN_STATUS_SPLITXSTATE | QT_TOKEN_STATUS_PERR)) {
                case 0:
-                       toggle = QT_TOKEN_GET_DT(token);
+                       toggle = QT_TOKEN_GET_DT(qhtoken);
                        usb_settoggle(dev, usb_pipeendpoint(pipe),
                                       usb_pipeout(pipe), toggle);
                        dev->status = 0;
@@ -630,11 +675,11 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
                        break;
                default:
                        dev->status = USB_ST_CRC_ERR;
-                       if (QT_TOKEN_GET_STATUS(token) & QT_TOKEN_STATUS_HALTED)
+                       if (QT_TOKEN_GET_STATUS(qhtoken) & QT_TOKEN_STATUS_HALTED)
                                dev->status |= USB_ST_STALLED;
                        break;
                }
-               dev->act_len = length - QT_TOKEN_GET_TOTALBYTES(token);
+               dev->act_len = length - QT_TOKEN_GET_TOTALBYTES(qhtoken);
        } else {
                dev->act_len = 0;
 #ifndef CONFIG_USB_EHCI_FARADAY
@@ -827,6 +872,10 @@ static int ehci_submit_root(struct usb_device *dev, unsigned long pipe,
                        } else {
                                int ret;
 
+                               /* Disable chirp for HS erratum */
+                               if (ctrl->has_fsl_erratum_a005275)
+                                       reg |= PORTSC_FSL_PFSC;
+
                                reg |= EHCI_PS_PR;
                                reg &= ~EHCI_PS_PE;
                                ehci_writel(status_reg, reg);
@@ -933,7 +982,7 @@ unknown:
        return -1;
 }
 
-const struct ehci_ops default_ehci_ops = {
+static const struct ehci_ops default_ehci_ops = {
        .set_usb_mode           = ehci_set_usbmode,
        .get_port_speed         = ehci_get_port_speed,
        .powerup_fixup          = ehci_powerup_fixup,
@@ -958,7 +1007,7 @@ static void ehci_setup_ops(struct ehci_ctrl *ctrl, const struct ehci_ops *ops)
        }
 }
 
-#ifndef CONFIG_DM_USB
+#if !CONFIG_IS_ENABLED(DM_USB)
 void ehci_set_controller_priv(int index, void *priv, const struct ehci_ops *ops)
 {
        struct ehci_ctrl *ctrl = &ehcic[index];
@@ -989,7 +1038,7 @@ static int ehci_common_init(struct ehci_ctrl *ctrl, uint tweaks)
 
        /* Set head of reclaim list */
        memset(qh_list, 0, sizeof(*qh_list));
-       qh_list->qh_link = cpu_to_hc32((unsigned long)qh_list | QH_LINK_TYPE_QH);
+       qh_list->qh_link = cpu_to_hc32(virt_to_phys(qh_list) | QH_LINK_TYPE_QH);
        qh_list->qh_endpt1 = cpu_to_hc32(QH_ENDPT1_H(1) |
                                                QH_ENDPT1_EPS(USB_SPEED_HIGH));
        qh_list->qh_overlay.qt_next = cpu_to_hc32(QT_NEXT_TERMINATE);
@@ -1001,7 +1050,7 @@ static int ehci_common_init(struct ehci_ctrl *ctrl, uint tweaks)
                           ALIGN_END_ADDR(struct QH, qh_list, 1));
 
        /* Set async. queue head pointer. */
-       ehci_writel(&ctrl->hcor->or_asynclistaddr, (unsigned long)qh_list);
+       ehci_writel(&ctrl->hcor->or_asynclistaddr, virt_to_phys(qh_list));
 
        /*
         * Set up periodic list
@@ -1082,7 +1131,7 @@ static int ehci_common_init(struct ehci_ctrl *ctrl, uint tweaks)
        return 0;
 }
 
-#ifndef CONFIG_DM_USB
+#if !CONFIG_IS_ENABLED(DM_USB)
 int usb_lowlevel_stop(int index)
 {
        ehci_shutdown(&ehcic[index]);
@@ -1104,6 +1153,8 @@ int usb_lowlevel_init(int index, enum usb_init_type init, void **controller)
        rc = ehci_hcd_init(index, init, &ctrl->hccr, &ctrl->hcor);
        if (rc)
                return rc;
+       if (!ctrl->hccr || !ctrl->hcor)
+               return -1;
        if (init == USB_INIT_DEVICE)
                goto done;
 
@@ -1365,13 +1416,10 @@ static struct int_queue *_ehci_create_int_queue(struct usb_device *dev,
        debug("Exit create_int_queue\n");
        return result;
 fail3:
-       if (result->tds)
-               free(result->tds);
+       free(result->tds);
 fail2:
-       if (result->first)
-               free(result->first);
-       if (result)
-               free(result);
+       free(result->first);
+       free(result);
 fail1:
        return NULL;
 }
@@ -1465,7 +1513,8 @@ out:
 }
 
 static int _ehci_submit_int_msg(struct usb_device *dev, unsigned long pipe,
-                               void *buffer, int length, int interval)
+                               void *buffer, int length, int interval,
+                               bool nonblock)
 {
        void *backbuffer;
        struct int_queue *queue;
@@ -1501,7 +1550,17 @@ static int _ehci_submit_int_msg(struct usb_device *dev, unsigned long pipe,
        return result;
 }
 
-#ifndef CONFIG_DM_USB
+static int _ehci_lock_async(struct ehci_ctrl *ctrl, int lock)
+{
+       ctrl->async_locked = lock;
+
+       if (lock)
+               return 0;
+
+       return ehci_disable_async(ctrl);
+}
+
+#if !CONFIG_IS_ENABLED(DM_USB)
 int submit_bulk_msg(struct usb_device *dev, unsigned long pipe,
                            void *buffer, int length)
 {
@@ -1515,9 +1574,10 @@ int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
 }
 
 int submit_int_msg(struct usb_device *dev, unsigned long pipe,
-                  void *buffer, int length, int interval)
+                  void *buffer, int length, int interval, bool nonblock)
 {
-       return _ehci_submit_int_msg(dev, pipe, buffer, length, interval);
+       return _ehci_submit_int_msg(dev, pipe, buffer, length, interval,
+                                   nonblock);
 }
 
 struct int_queue *create_int_queue(struct usb_device *dev,
@@ -1537,9 +1597,16 @@ int destroy_int_queue(struct usb_device *dev, struct int_queue *queue)
 {
        return _ehci_destroy_int_queue(dev, queue);
 }
+
+int usb_lock_async(struct usb_device *dev, int lock)
+{
+       struct ehci_ctrl *ctrl = ehci_get_ctrl(dev);
+
+       return _ehci_lock_async(ctrl, lock);
+}
 #endif
 
-#ifdef CONFIG_DM_USB
+#if CONFIG_IS_ENABLED(DM_USB)
 static int ehci_submit_control_msg(struct udevice *dev, struct usb_device *udev,
                                   unsigned long pipe, void *buffer, int length,
                                   struct devrequest *setup)
@@ -1559,10 +1626,11 @@ static int ehci_submit_bulk_msg(struct udevice *dev, struct usb_device *udev,
 
 static int ehci_submit_int_msg(struct udevice *dev, struct usb_device *udev,
                               unsigned long pipe, void *buffer, int length,
-                              int interval)
+                              int interval, bool nonblock)
 {
        debug("%s: dev='%s', udev=%p\n", __func__, dev->name, udev);
-       return _ehci_submit_int_msg(udev, pipe, buffer, length, interval);
+       return _ehci_submit_int_msg(udev, pipe, buffer, length, interval,
+                                   nonblock);
 }
 
 static struct int_queue *ehci_create_int_queue(struct udevice *dev,
@@ -1588,17 +1656,38 @@ static int ehci_destroy_int_queue(struct udevice *dev, struct usb_device *udev,
        return _ehci_destroy_int_queue(udev, queue);
 }
 
+static int ehci_get_max_xfer_size(struct udevice *dev, size_t *size)
+{
+       /*
+        * EHCD can handle any transfer length as long as there is enough
+        * free heap space left, hence set the theoretical max number here.
+        */
+       *size = SIZE_MAX;
+
+       return 0;
+}
+
+static int ehci_lock_async(struct udevice *dev, int lock)
+{
+       struct ehci_ctrl *ctrl = dev_get_priv(dev);
+
+       return _ehci_lock_async(ctrl, lock);
+}
+
 int ehci_register(struct udevice *dev, struct ehci_hccr *hccr,
                  struct ehci_hcor *hcor, const struct ehci_ops *ops,
                  uint tweaks, enum usb_init_type init)
 {
        struct usb_bus_priv *priv = dev_get_uclass_priv(dev);
        struct ehci_ctrl *ctrl = dev_get_priv(dev);
-       int ret;
+       int ret = -1;
 
        debug("%s: dev='%s', ctrl=%p, hccr=%p, hcor=%p, init=%d\n", __func__,
              dev->name, ctrl, hccr, hcor, init);
 
+       if (!ctrl || !hccr || !hcor)
+               goto err;
+
        priv->desc_before_addr = true;
 
        ehci_setup_ops(ctrl, ops);
@@ -1614,6 +1703,12 @@ int ehci_register(struct udevice *dev, struct ehci_hccr *hccr,
        if (ret)
                goto err;
 
+       if (ctrl->ops.init_after_reset) {
+               ret = ctrl->ops.init_after_reset(ctrl);
+               if (ret)
+                       goto err;
+       }
+
        ret = ehci_common_init(ctrl, tweaks);
        if (ret)
                goto err;
@@ -1644,6 +1739,74 @@ struct dm_usb_ops ehci_usb_ops = {
        .create_int_queue = ehci_create_int_queue,
        .poll_int_queue = ehci_poll_int_queue,
        .destroy_int_queue = ehci_destroy_int_queue,
+       .get_max_xfer_size  = ehci_get_max_xfer_size,
+       .lock_async = ehci_lock_async,
 };
 
 #endif
+
+#ifdef CONFIG_PHY
+int ehci_setup_phy(struct udevice *dev, struct phy *phy, int index)
+{
+       int ret;
+
+       if (!phy)
+               return 0;
+
+       ret = generic_phy_get_by_index(dev, index, phy);
+       if (ret) {
+               if (ret != -ENOENT) {
+                       dev_err(dev, "failed to get usb phy\n");
+                       return ret;
+               }
+       } else {
+               ret = generic_phy_init(phy);
+               if (ret) {
+                       dev_err(dev, "failed to init usb phy\n");
+                       return ret;
+               }
+
+               ret = generic_phy_power_on(phy);
+               if (ret) {
+                       dev_err(dev, "failed to power on usb phy\n");
+                       return generic_phy_exit(phy);
+               }
+       }
+
+       return 0;
+}
+
+int ehci_shutdown_phy(struct udevice *dev, struct phy *phy)
+{
+       int ret = 0;
+
+       if (!phy)
+               return 0;
+
+       if (generic_phy_valid(phy)) {
+               ret = generic_phy_power_off(phy);
+               if (ret) {
+                       dev_err(dev, "failed to power off usb phy\n");
+                       return ret;
+               }
+
+               ret = generic_phy_exit(phy);
+               if (ret) {
+                       dev_err(dev, "failed to power off usb phy\n");
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+#else
+int ehci_setup_phy(struct udevice *dev, struct phy *phy, int index)
+{
+       return 0;
+}
+
+int ehci_shutdown_phy(struct udevice *dev, struct phy *phy)
+{
+       return 0;
+}
+#endif