Convert CONFIG_USB_MAX_CONTROLLER_COUNT to Kconfig
[platform/kernel/u-boot.git] / drivers / usb / host / ehci-hcd.c
index 61a61ab..f033198 100644 (file)
@@ -7,23 +7,24 @@
  * All rights reserved.
  */
 #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"
 
-#ifndef CONFIG_USB_MAX_CONTROLLER_COUNT
-#define CONFIG_USB_MAX_CONTROLLER_COUNT 1
-#endif
-
 /*
  * EHCI spec page 20 says that the HC may take up to 16 uFrames (= 4ms) to halt.
  * Let's time out after 8 to have a little safety margin on top of that.
@@ -103,7 +104,7 @@ static struct descriptor {
        },
 };
 
-#if defined(CONFIG_EHCI_IS_TDI)
+#if defined(CONFIG_USB_EHCI_IS_TDI)
 #define ehci_is_TDI()  (1)
 #else
 #define ehci_is_TDI()  (0)
@@ -296,6 +297,73 @@ 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_iaa_cycle(struct ehci_ctrl *ctrl)
+{
+       u32 cmd, status;
+       int ret;
+
+       /* Enable Interrupt on Async Advance Doorbell. */
+       cmd = ehci_readl(&ctrl->hcor->or_usbcmd);
+       cmd |= CMD_IAAD;
+       ehci_writel(&ctrl->hcor->or_usbcmd, cmd);
+
+       ret = handshake(&ctrl->hcor->or_usbsts, STS_IAA, STS_IAA,
+                       10 * 1000); /* 10ms timeout */
+       if (ret < 0)
+               printf("EHCI fail timeout STS_IAA set\n");
+
+       status = ehci_readl(&ctrl->hcor->or_usbsts);
+       if (status & STS_IAA)
+               ehci_writel(&ctrl->hcor->or_usbsts, STS_IAA);
+
+       return ret;
+}
+
 static int
 ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
                   int length, struct devrequest *req)
@@ -307,9 +375,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);
@@ -551,23 +618,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, virt_to_phys(&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);
@@ -587,6 +643,16 @@ 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));
+
+       /* Set IAAD, poll IAA */
+       ret = ehci_iaa_cycle(ctrl);
+       if (ret)
+               goto fail;
 
        /*
         * Invalidate the memory area occupied by buffer
@@ -605,25 +671,16 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
        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;
@@ -641,11 +698,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
@@ -1382,13 +1439,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;
 }
@@ -1519,6 +1573,16 @@ static int _ehci_submit_int_msg(struct usb_device *dev, unsigned long pipe,
        return result;
 }
 
+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)
@@ -1556,6 +1620,13 @@ 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
 
 #if CONFIG_IS_ENABLED(DM_USB)
@@ -1619,6 +1690,13 @@ static int ehci_get_max_xfer_size(struct udevice *dev, size_t *size)
        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)
@@ -1685,6 +1763,7 @@ struct dm_usb_ops ehci_usb_ops = {
        .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
@@ -1706,13 +1785,13 @@ int ehci_setup_phy(struct udevice *dev, struct phy *phy, int index)
        } else {
                ret = generic_phy_init(phy);
                if (ret) {
-                       dev_err(dev, "failed to init usb phy\n");
+                       dev_dbg(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");
+                       dev_dbg(dev, "failed to power on usb phy\n");
                        return generic_phy_exit(phy);
                }
        }
@@ -1730,13 +1809,13 @@ int ehci_shutdown_phy(struct udevice *dev, struct phy *phy)
        if (generic_phy_valid(phy)) {
                ret = generic_phy_power_off(phy);
                if (ret) {
-                       dev_err(dev, "failed to power off usb phy\n");
+                       dev_dbg(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");
+                       dev_dbg(dev, "failed to power off usb phy\n");
                        return ret;
                }
        }