+// 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"
*/
#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
0, /* wHubCharacteristics */
10, /* bPwrOn2PwrGood */
0, /* bHubCntrCurrent */
- {}, /* Device removable */
- {} /* at most 7 ports! XXX */
+ { /* Device removable */
+ } /* at most 7 ports! XXX */
},
{
0x12, /* bLength */
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;
tmp |= USBMODE_CM_HC;
#if defined(CONFIG_EHCI_MMIO_BIG_ENDIAN)
tmp |= USBMODE_BE;
+#else
+ tmp &= ~USBMODE_BE;
#endif
ehci_writel(reg_ptr, tmp);
}
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;
}
{
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);
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))
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;
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)
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);
* 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);
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)
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;
}
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
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,
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);
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
* 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;
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
} 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);
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,
}
}
-#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];
/* 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);
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
return 0;
}
-#ifndef CONFIG_DM_USB
+#if !CONFIG_IS_ENABLED(DM_USB)
int usb_lowlevel_stop(int index)
{
ehci_shutdown(&ehcic[index]);
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;
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;
}
}
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;
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)
{
}
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,
{
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)
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,
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);
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;
.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