*
* All rights reserved.
*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation version 2 of
- * the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
- * MA 02111-1307 USA
+ * SPDX-License-Identifier: GPL-2.0
*/
#include <common.h>
#include <dm.h>
* in the tree before that one!
*/
#ifdef CONFIG_DM_USB
+ /*
+ * When called from usb-uclass.c: usb_scan_device() udev->dev points
+ * to the parent udevice, not the actual udevice belonging to the
+ * udev as the device is not instantiated yet. So when searching
+ * for the first usb-2 parent start with udev->dev not
+ * udev->dev->parent .
+ */
struct udevice *parent;
+ struct usb_device *uparent;
+
+ ttdev = udev;
+ parent = udev->dev;
+ uparent = dev_get_parentdata(parent);
- for (ttdev = udev; ; ) {
- struct udevice *dev = ttdev->dev;
+ while (uparent->speed != USB_SPEED_HIGH) {
+ struct udevice *dev = parent;
- if (dev->parent &&
- device_get_uclass_id(dev->parent) == UCLASS_USB_HUB)
- parent = dev->parent;
- else
- parent = NULL;
- if (!parent)
+ if (device_get_uclass_id(dev->parent) != UCLASS_USB_HUB) {
+ printf("ehci: Error cannot find high-speed parent of usb-1 device\n");
return;
- ttdev = dev_get_parentdata(parent);
- if (!ttdev->speed != USB_SPEED_HIGH)
- break;
+ }
+
+ ttdev = dev_get_parentdata(dev);
+ parent = dev->parent;
+ uparent = dev_get_parentdata(parent);
}
- parent_devnum = ttdev->devnum;
+ parent_devnum = uparent->devnum;
#else
ttdev = udev;
while (ttdev->parent && ttdev->parent->speed != USB_SPEED_HIGH)
port - 1);
reg |= EHCI_PS_PO;
ehci_writel(status_reg, reg);
- break;
+ return -ENXIO;
} else {
int ret;
*/
ret = handshake(status_reg, EHCI_PS_PR, 0,
2 * 1000);
- if (!ret)
- ctrl->portreset |= 1 << port;
- else
+ if (!ret) {
+ reg = ehci_readl(status_reg);
+ if ((reg & (EHCI_PS_PE | EHCI_PS_CS))
+ == EHCI_PS_CS && !ehci_is_TDI()) {
+ debug("port %d full speed --> companion\n", port - 1);
+ reg &= ~EHCI_PS_CLEAR;
+ reg |= EHCI_PS_PO;
+ ehci_writel(status_reg, reg);
+ return -ENXIO;
+ } else {
+ ctrl->portreset |= 1 << port;
+ }
+ } else {
printf("port(%d) reset error\n",
port - 1);
+ }
}
break;
case USB_PORT_FEAT_TEST:
struct int_queue {
int elementsize;
+ unsigned long pipe;
struct QH *first;
struct QH *current;
struct QH *last;
return 0;
}
-struct int_queue *
-create_int_queue(struct usb_device *dev, unsigned long pipe, int queuesize,
- int elementsize, void *buffer, int interval)
+static struct int_queue *_ehci_create_int_queue(struct usb_device *dev,
+ unsigned long pipe, int queuesize, int elementsize,
+ void *buffer, int interval)
{
struct ehci_ctrl *ctrl = ehci_get_ctrl(dev);
struct int_queue *result = NULL;
- int i;
+ uint32_t i, toggle;
/*
* Interrupt transfers requiring several transactions are not supported
goto fail1;
}
result->elementsize = elementsize;
+ result->pipe = pipe;
result->first = memalign(USB_DMA_MINALIGN,
sizeof(struct QH) * queuesize);
if (!result->first) {
memset(result->first, 0, sizeof(struct QH) * queuesize);
memset(result->tds, 0, sizeof(struct qTD) * queuesize);
+ toggle = usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe));
+
for (i = 0; i < queuesize; i++) {
struct QH *qh = result->first + i;
struct qTD *td = result->tds + i;
td->qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE);
debug("communication direction is '%s'\n",
usb_pipein(pipe) ? "in" : "out");
- td->qt_token = cpu_to_hc32((elementsize << 16) |
+ td->qt_token = cpu_to_hc32(
+ QT_TOKEN_DT(toggle) |
+ (elementsize << 16) |
((usb_pipein(pipe) ? 1 : 0) << 8) | /* IN/OUT token */
0x80); /* active */
td->qt_buffer[0] =
cpu_to_hc32((td->qt_buffer[0] + 0x4000) & ~0xfff);
*buf = buffer + i * elementsize;
+ toggle ^= 1;
}
flush_dcache_range((unsigned long)buffer,
return NULL;
}
-void *poll_int_queue(struct usb_device *dev, struct int_queue *queue)
+static void *_ehci_poll_int_queue(struct usb_device *dev,
+ struct int_queue *queue)
{
struct QH *cur = queue->current;
struct qTD *cur_td;
+ uint32_t token, toggle;
+ unsigned long pipe = queue->pipe;
/* depleted queue */
if (cur == NULL) {
cur_td = &queue->tds[queue->current - queue->first];
invalidate_dcache_range((unsigned long)cur_td,
ALIGN_END_ADDR(struct qTD, cur_td, 1));
- if (QT_TOKEN_GET_STATUS(hc32_to_cpu(cur_td->qt_token)) &
- QT_TOKEN_STATUS_ACTIVE) {
- debug("Exit poll_int_queue with no completed intr transfer. token is %x\n",
- hc32_to_cpu(cur_td->qt_token));
+ token = hc32_to_cpu(cur_td->qt_token);
+ if (QT_TOKEN_GET_STATUS(token) & QT_TOKEN_STATUS_ACTIVE) {
+ debug("Exit poll_int_queue with no completed intr transfer. token is %x\n", token);
return NULL;
}
+
+ toggle = QT_TOKEN_GET_DT(token);
+ usb_settoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe), toggle);
+
if (!(cur->qh_link & QH_LINK_TERMINATE))
queue->current++;
else
queue->elementsize));
debug("Exit poll_int_queue with completed intr transfer. token is %x at %p (first at %p)\n",
- hc32_to_cpu(cur_td->qt_token), cur, queue->first);
+ token, cur, queue->first);
return cur->buffer;
}
/* Do not free buffers associated with QHs, they're owned by someone else */
-int
-destroy_int_queue(struct usb_device *dev, struct int_queue *queue)
+static int _ehci_destroy_int_queue(struct usb_device *dev,
+ struct int_queue *queue)
{
struct ehci_ctrl *ctrl = ehci_get_ctrl(dev);
int result = -1;
debug("dev=%p, pipe=%lu, buffer=%p, length=%d, interval=%d",
dev, pipe, buffer, length, interval);
- queue = create_int_queue(dev, pipe, 1, length, buffer, interval);
+ queue = _ehci_create_int_queue(dev, pipe, 1, length, buffer, interval);
if (!queue)
return -1;
timeout = get_timer(0) + USB_TIMEOUT_MS(pipe);
- while ((backbuffer = poll_int_queue(dev, queue)) == NULL)
+ while ((backbuffer = _ehci_poll_int_queue(dev, queue)) == NULL)
if (get_timer(0) > timeout) {
printf("Timeout poll on interrupt endpoint\n");
result = -ETIMEDOUT;
return -EINVAL;
}
- ret = destroy_int_queue(dev, queue);
+ ret = _ehci_destroy_int_queue(dev, queue);
if (ret < 0)
return ret;
{
return _ehci_submit_int_msg(dev, pipe, buffer, length, interval);
}
+
+struct int_queue *create_int_queue(struct usb_device *dev,
+ unsigned long pipe, int queuesize, int elementsize,
+ void *buffer, int interval)
+{
+ return _ehci_create_int_queue(dev, pipe, queuesize, elementsize,
+ buffer, interval);
+}
+
+void *poll_int_queue(struct usb_device *dev, struct int_queue *queue)
+{
+ return _ehci_poll_int_queue(dev, queue);
+}
+
+int destroy_int_queue(struct usb_device *dev, struct int_queue *queue)
+{
+ return _ehci_destroy_int_queue(dev, queue);
+}
#endif
#ifdef CONFIG_DM_USB
return _ehci_submit_int_msg(udev, pipe, buffer, length, interval);
}
+static struct int_queue *ehci_create_int_queue(struct udevice *dev,
+ struct usb_device *udev, unsigned long pipe, int queuesize,
+ int elementsize, void *buffer, int interval)
+{
+ debug("%s: dev='%s', udev=%p\n", __func__, dev->name, udev);
+ return _ehci_create_int_queue(udev, pipe, queuesize, elementsize,
+ buffer, interval);
+}
+
+static void *ehci_poll_int_queue(struct udevice *dev, struct usb_device *udev,
+ struct int_queue *queue)
+{
+ debug("%s: dev='%s', udev=%p\n", __func__, dev->name, udev);
+ return _ehci_poll_int_queue(udev, queue);
+}
+
+static int ehci_destroy_int_queue(struct udevice *dev, struct usb_device *udev,
+ struct int_queue *queue)
+{
+ debug("%s: dev='%s', udev=%p\n", __func__, dev->name, udev);
+ return _ehci_destroy_int_queue(udev, queue);
+}
+
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;
debug("%s: dev='%s', ctrl=%p, hccr=%p, hcor=%p, init=%d\n", __func__,
dev->name, ctrl, hccr, hcor, init);
+ priv->desc_before_addr = true;
+
ehci_setup_ops(ctrl, ops);
ctrl->hccr = hccr;
ctrl->hcor = hcor;
.control = ehci_submit_control_msg,
.bulk = ehci_submit_bulk_msg,
.interrupt = ehci_submit_int_msg,
+ .create_int_queue = ehci_create_int_queue,
+ .poll_int_queue = ehci_poll_int_queue,
+ .destroy_int_queue = ehci_destroy_int_queue,
};
#endif