+// SPDX-License-Identifier: GPL-2.0+
/*
* URB OHCI HCD (Host Controller Driver) for USB on the AT91RM9200 and PCI bus.
*
*
* Modified for the MP2USB by (C) Copyright 2005 Eric Benard
* ebenard@eukrea.com - based on s3c24x0's driver
- *
- * SPDX-License-Identifier: GPL-2.0+
*/
/*
* IMPORTANT NOTES
*/
#include <common.h>
+#include <cpu_func.h>
#include <asm/byteorder.h>
+#include <dm.h>
+#include <errno.h>
+#include <asm/cache.h>
+#include <linux/delay.h>
#if defined(CONFIG_PCI_OHCI)
# include <pci.h>
#endif
#include <malloc.h>
+#include <memalign.h>
#include <usb.h>
#include "ohci.h"
#endif
#if defined(CONFIG_CPU_ARM920T) || \
- defined(CONFIG_S3C24X0) || \
- defined(CONFIG_440EP) || \
- defined(CONFIG_PCI_OHCI) || \
- defined(CONFIG_MPC5200) || \
- defined(CONFIG_SYS_OHCI_USE_NPS)
+ defined(CONFIG_PCI_OHCI) || \
+ defined(CONFIG_PCI) || \
+ defined(CONFIG_SYS_OHCI_USE_NPS)
# define OHCI_USE_NPS /* force NoPowerSwitching mode */
#endif
#define OHCI_CONTROL_INIT \
(OHCI_CTRL_CBSR & 0x3) | OHCI_CTRL_IE | OHCI_CTRL_PLE
+#if !CONFIG_IS_ENABLED(DM_USB)
#ifdef CONFIG_PCI_OHCI
static struct pci_device_id ohci_pci_ids[] = {
{0x10b9, 0x5237}, /* ULI1575 PCI OHCI module ids */
{0, 0}
};
#endif
+#endif
#ifdef CONFIG_PCI_EHCI_DEVNO
static struct pci_device_id ehci_pci_ids[] = {
# define m32_swap(x) cpu_to_le32(x)
#endif /* CONFIG_SYS_OHCI_BE_CONTROLLER */
-#ifdef CONFIG_DM_USB
-/*
- * We really should do proper cache flushing everywhere, but for now we only
- * do it for new (driver-model) usb code to avoid regressions.
- */
+/* We really should do proper cache flushing everywhere */
#define flush_dcache_buffer(addr, size) \
flush_dcache_range((unsigned long)(addr), \
ALIGN((unsigned long)(addr) + size, ARCH_DMA_MINALIGN))
#define invalidate_dcache_buffer(addr, size) \
invalidate_dcache_range((unsigned long)(addr), \
ALIGN((unsigned long)(addr) + size, ARCH_DMA_MINALIGN))
-#else
-#define flush_dcache_buffer(addr, size)
-#define invalidate_dcache_buffer(addr, size)
-#endif
/* Do not use sizeof(ed / td) as our ed / td structs contain extra members */
#define flush_dcache_ed(addr) flush_dcache_buffer(addr, 16)
#define invalidate_dcache_iso_td(addr) invalidate_dcache_buffer(addr, 32)
#define invalidate_dcache_hcca(addr) invalidate_dcache_buffer(addr, 256)
-#ifdef CONFIG_DM_USB
+#if CONFIG_IS_ENABLED(DM_USB)
/*
* The various ohci_mdelay(1) calls in the code seem unnecessary. We keep
* them around when building for older boards not yet converted to the dm
#define ohci_mdelay(x) mdelay(x)
#endif
+#if !CONFIG_IS_ENABLED(DM_USB)
/* global ohci_t */
static ohci_t gohci;
/* this must be aligned to a 256 byte boundary */
struct ohci_hcca ghcca[1];
+#endif
/* mapping of the OHCI CC status to error codes */
static int cc_to_error[16] = {
ed->hwNextED = 0;
flush_dcache_ed(ed);
if (ohci->ed_controltail == NULL)
- ohci_writel(ed, &ohci->regs->ed_controlhead);
+ ohci_writel((uintptr_t)ed, &ohci->regs->ed_controlhead);
else
ohci->ed_controltail->hwNextED =
m32_swap((unsigned long)ed);
ed->hwNextED = 0;
flush_dcache_ed(ed);
if (ohci->ed_bulktail == NULL)
- ohci_writel(ed, &ohci->regs->ed_bulkhead);
+ ohci_writel((uintptr_t)ed, &ohci->regs->ed_bulkhead);
else
ohci->ed_bulktail->hwNextED =
m32_swap((unsigned long)ed);
/* ED might have been unlinked through another path */
while (*ed_p != 0) {
- if (((struct ed *)
+ if (((struct ed *)(uintptr_t)
m32_swap((unsigned long)ed_p)) == ed) {
*ed_p = ed->hwNextED;
-#ifdef CONFIG_DM_USB
aligned_ed_p = (unsigned long)ed_p;
aligned_ed_p &= ~(ARCH_DMA_MINALIGN - 1);
flush_dcache_range(aligned_ed_p,
aligned_ed_p + ARCH_DMA_MINALIGN);
-#endif
break;
}
- ed_p = &(((struct ed *)
+ ed_p = &(((struct ed *)(uintptr_t)
m32_swap((unsigned long)ed_p))->hwNextED);
}
}
if (ohci->ed_controltail == ed) {
ohci->ed_controltail = ed->ed_prev;
} else {
- ((ed_t *)m32_swap(
+ ((ed_t *)(uintptr_t)m32_swap(
*((__u32 *)&ed->hwNextED)))->ed_prev = ed->ed_prev;
}
break;
if (ohci->ed_bulktail == ed) {
ohci->ed_bulktail = ed->ed_prev;
} else {
- ((ed_t *)m32_swap(
+ ((ed_t *)(uintptr_t)m32_swap(
*((__u32 *)&ed->hwNextED)))->ed_prev = ed->ed_prev;
}
break;
/* fill the old dummy TD */
td = urb_priv->td [index] =
- (td_t *)(m32_swap(urb_priv->ed->hwTailP) & ~0xf);
+ (td_t *)(uintptr_t)
+ (m32_swap(urb_priv->ed->hwTailP) & ~0xf);
td->ed = urb_priv->ed;
td->next_dl_td = NULL;
td->index = index;
- td->data = (__u32)data;
+ td->data = (uintptr_t)data;
#ifdef OHCI_FILL_TRACE
if (usb_pipebulk(urb_priv->pipe) && usb_pipeout(urb_priv->pipe)) {
for (i = 0; i < len; i++)
flush_dcache_buffer(buffer, data_len);
/* OHCI handles the DATA-toggles itself, we just use the USB-toggle
- * bits for reseting */
+ * bits for resetting */
if (usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe))) {
toggle = TD_T_TOGGLE;
} else {
*phwHeadP &= m32_swap(0xfffffff2);
flush_dcache_ed(td_list->ed);
}
-#ifdef CONFIG_MPC5200
- td_list->hwNextTD = 0;
- flush_dcache_td(td_list);
-#endif
}
}
* we reverse the reversed done-list */
static td_t *dl_reverse_done_list(ohci_t *ohci)
{
- __u32 td_list_hc;
+ uintptr_t td_list_hc;
td_t *td_rev = NULL;
td_t *td_list = NULL;
/*-------------------------------------------------------------------------*/
+static ohci_dev_t *ohci_get_ohci_dev(ohci_t *ohci, int devnum, int intr)
+{
+ int i;
+
+ if (!intr)
+ return &ohci->ohci_dev;
+
+ /* First see if we already have an ohci_dev for this dev. */
+ for (i = 0; i < NUM_INT_DEVS; i++) {
+ if (ohci->int_dev[i].devnum == devnum)
+ return &ohci->int_dev[i];
+ }
+
+ /* If not then find a free one. */
+ for (i = 0; i < NUM_INT_DEVS; i++) {
+ if (ohci->int_dev[i].devnum == -1) {
+ ohci->int_dev[i].devnum = devnum;
+ return &ohci->int_dev[i];
+ }
+ }
+
+ printf("ohci: Error out of ohci_devs for interrupt endpoints\n");
+ return NULL;
+}
+
/* common code for handling submit messages - used for all but root hub */
/* accesses. */
-static int submit_common_msg(ohci_t *ohci, struct usb_device *dev,
- unsigned long pipe, void *buffer, int transfer_len,
- struct devrequest *setup, int interval)
+static urb_priv_t *ohci_alloc_urb(struct usb_device *dev, unsigned long pipe,
+ void *buffer, int transfer_len, int interval)
{
- int stat = 0;
- int maxsize = usb_maxpacket(dev, pipe);
- int timeout;
urb_priv_t *urb;
- urb = malloc(sizeof(urb_priv_t));
- memset(urb, 0, sizeof(urb_priv_t));
+ urb = calloc(1, sizeof(urb_priv_t));
+ if (!urb) {
+ printf("ohci: Error out of memory allocating urb\n");
+ return NULL;
+ }
urb->dev = dev;
urb->pipe = pipe;
urb->transfer_buffer_length = transfer_len;
urb->interval = interval;
+ return urb;
+}
+
+static int submit_common_msg(ohci_t *ohci, struct usb_device *dev,
+ unsigned long pipe, void *buffer, int transfer_len,
+ struct devrequest *setup, int interval)
+{
+ int stat = 0;
+ int maxsize = usb_maxpacket(dev, pipe);
+ int timeout;
+ urb_priv_t *urb;
+ ohci_dev_t *ohci_dev;
+
+ urb = ohci_alloc_urb(dev, pipe, buffer, transfer_len, interval);
+ if (!urb)
+ return -ENOMEM;
+
#ifdef DEBUG
urb->actual_length = 0;
pkt_print(ohci, urb, dev, pipe, buffer, transfer_len,
return -1;
}
- if (sohci_submit_job(ohci, &ohci->ohci_dev, urb, setup) < 0) {
+ ohci_dev = ohci_get_ohci_dev(ohci, dev->devnum, usb_pipeint(pipe));
+ if (!ohci_dev)
+ return -ENOMEM;
+
+ if (sohci_submit_job(ohci, ohci_dev, urb, setup) < 0) {
err("sohci_submit_job failed");
return -1;
}
-#if 0
mdelay(10);
/* ohci_dump_status(ohci); */
-#endif
timeout = USB_TIMEOUT_MS(pipe);
return 0;
}
+#define MAX_INT_QUEUESIZE 8
+
+struct int_queue {
+ int queuesize;
+ int curr_urb;
+ urb_priv_t *urb[MAX_INT_QUEUESIZE];
+};
+
+static struct int_queue *_ohci_create_int_queue(ohci_t *ohci,
+ struct usb_device *udev, unsigned long pipe, int queuesize,
+ int elementsize, void *buffer, int interval)
+{
+ struct int_queue *queue;
+ ohci_dev_t *ohci_dev;
+ int i;
+
+ if (queuesize > MAX_INT_QUEUESIZE)
+ return NULL;
+
+ ohci_dev = ohci_get_ohci_dev(ohci, udev->devnum, 1);
+ if (!ohci_dev)
+ return NULL;
+
+ queue = malloc(sizeof(*queue));
+ if (!queue) {
+ printf("ohci: Error out of memory allocating int queue\n");
+ return NULL;
+ }
+
+ for (i = 0; i < queuesize; i++) {
+ queue->urb[i] = ohci_alloc_urb(udev, pipe,
+ buffer + i * elementsize,
+ elementsize, interval);
+ if (!queue->urb[i])
+ break;
+
+ if (sohci_submit_job(ohci, ohci_dev, queue->urb[i], NULL)) {
+ printf("ohci: Error submitting int queue job\n");
+ urb_free_priv(queue->urb[i]);
+ break;
+ }
+ }
+ if (i == 0) {
+ /* We did not succeed in submitting even 1 urb */
+ free(queue);
+ return NULL;
+ }
+
+ queue->queuesize = i;
+ queue->curr_urb = 0;
+
+ return queue;
+}
+
+static void *_ohci_poll_int_queue(ohci_t *ohci, struct usb_device *udev,
+ struct int_queue *queue)
+{
+ if (queue->curr_urb == queue->queuesize)
+ return NULL; /* Queue depleted */
+
+ if (hc_interrupt(ohci) < 0)
+ return NULL;
+
+ if (queue->urb[queue->curr_urb]->finished) {
+ void *ret = queue->urb[queue->curr_urb]->transfer_buffer;
+ queue->curr_urb++;
+ return ret;
+ }
+
+ return NULL;
+}
+
+static int _ohci_destroy_int_queue(ohci_t *ohci, struct usb_device *dev,
+ struct int_queue *queue)
+{
+ int i;
+
+ for (i = 0; i < queue->queuesize; i++)
+ urb_free_priv(queue->urb[i]);
+
+ free(queue);
+
+ return 0;
+}
+
+#if !CONFIG_IS_ENABLED(DM_USB)
/* submit routines called from usb.c */
int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
int transfer_len)
}
int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
- int transfer_len, int interval)
+ int transfer_len, int interval, bool nonblock)
{
info("submit_int_msg");
return submit_common_msg(&gohci, dev, pipe, buffer, transfer_len, NULL,
interval);
}
+struct int_queue *create_int_queue(struct usb_device *dev,
+ unsigned long pipe, int queuesize, int elementsize,
+ void *buffer, int interval)
+{
+ return _ohci_create_int_queue(&gohci, dev, pipe, queuesize,
+ elementsize, buffer, interval);
+}
+
+void *poll_int_queue(struct usb_device *dev, struct int_queue *queue)
+{
+ return _ohci_poll_int_queue(&gohci, dev, queue);
+}
+
+int destroy_int_queue(struct usb_device *dev, struct int_queue *queue)
+{
+ return _ohci_destroy_int_queue(&gohci, dev, queue);
+}
+#endif
+
static int _ohci_submit_control_msg(ohci_t *ohci, struct usb_device *dev,
unsigned long pipe, void *buffer, int transfer_len,
struct devrequest *setup)
{
__u32 mask;
unsigned int fminterval;
+ int i;
ohci->disabled = 1;
+ for (i = 0; i < NUM_INT_DEVS; i++)
+ ohci->int_dev[i].devnum = -1;
/* Tell the controller where the control and bulk lists are
* The lists are empty now. */
ohci_writel(0, &ohci->regs->ed_controlhead);
ohci_writel(0, &ohci->regs->ed_bulkhead);
- ohci_writel((__u32)ohci->hcca,
+ ohci_writel((uintptr_t)ohci->hcca,
&ohci->regs->hcca); /* reset clears this */
fminterval = 0x2edf;
/*-------------------------------------------------------------------------*/
+#if !CONFIG_IS_ENABLED(DM_USB)
+
/*-------------------------------------------------------------------------*/
/* De-allocate all resources.. */
pci_read_config_dword(pdev, PCI_BASE_ADDRESS_0, &base);
printf("OHCI regs address 0x%08x\n", base);
gohci.regs = (struct ohci_regs *)base;
- } else
+ } else {
+ printf("%s: OHCI devnr: %d not found\n", __func__,
+ CONFIG_PCI_OHCI_DEVNO);
return -1;
+ }
#else
gohci.regs = (struct ohci_regs *)CONFIG_SYS_USB_OHCI_REGS_BASE;
#endif
return _ohci_submit_control_msg(&gohci, dev, pipe, buffer,
transfer_len, setup);
}
+#endif
+
+#if CONFIG_IS_ENABLED(DM_USB)
+static int ohci_submit_control_msg(struct udevice *dev, struct usb_device *udev,
+ unsigned long pipe, void *buffer, int length,
+ struct devrequest *setup)
+{
+ ohci_t *ohci = dev_get_priv(usb_get_bus(dev));
+
+ return _ohci_submit_control_msg(ohci, udev, pipe, buffer,
+ length, setup);
+}
+
+static int ohci_submit_bulk_msg(struct udevice *dev, struct usb_device *udev,
+ unsigned long pipe, void *buffer, int length)
+{
+ ohci_t *ohci = dev_get_priv(usb_get_bus(dev));
+
+ return submit_common_msg(ohci, udev, pipe, buffer, length, NULL, 0);
+}
+
+static int ohci_submit_int_msg(struct udevice *dev, struct usb_device *udev,
+ unsigned long pipe, void *buffer, int length,
+ int interval, bool nonblock)
+{
+ ohci_t *ohci = dev_get_priv(usb_get_bus(dev));
+
+ return submit_common_msg(ohci, udev, pipe, buffer, length,
+ NULL, interval);
+}
+
+static struct int_queue *ohci_create_int_queue(struct udevice *dev,
+ struct usb_device *udev, unsigned long pipe, int queuesize,
+ int elementsize, void *buffer, int interval)
+{
+ ohci_t *ohci = dev_get_priv(usb_get_bus(dev));
+
+ return _ohci_create_int_queue(ohci, udev, pipe, queuesize, elementsize,
+ buffer, interval);
+}
+
+static void *ohci_poll_int_queue(struct udevice *dev, struct usb_device *udev,
+ struct int_queue *queue)
+{
+ ohci_t *ohci = dev_get_priv(usb_get_bus(dev));
+
+ return _ohci_poll_int_queue(ohci, udev, queue);
+}
+
+static int ohci_destroy_int_queue(struct udevice *dev, struct usb_device *udev,
+ struct int_queue *queue)
+{
+ ohci_t *ohci = dev_get_priv(usb_get_bus(dev));
+
+ return _ohci_destroy_int_queue(ohci, udev, queue);
+}
+
+int ohci_register(struct udevice *dev, struct ohci_regs *regs)
+{
+ struct usb_bus_priv *priv = dev_get_uclass_priv(dev);
+ ohci_t *ohci = dev_get_priv(dev);
+ u32 reg;
+
+ priv->desc_before_addr = true;
+
+ ohci->regs = regs;
+ ohci->hcca = memalign(256, sizeof(struct ohci_hcca));
+ if (!ohci->hcca)
+ return -ENOMEM;
+ memset(ohci->hcca, 0, sizeof(struct ohci_hcca));
+ flush_dcache_hcca(ohci->hcca);
+
+ if (hc_reset(ohci) < 0)
+ return -EIO;
+
+ if (hc_start(ohci) < 0)
+ return -EIO;
+
+ reg = ohci_readl(®s->revision);
+ printf("USB OHCI %x.%x\n", (reg >> 4) & 0xf, reg & 0xf);
+
+ return 0;
+}
+
+int ohci_deregister(struct udevice *dev)
+{
+ ohci_t *ohci = dev_get_priv(dev);
+
+ if (hc_reset(ohci) < 0)
+ return -EIO;
+
+ free(ohci->hcca);
+
+ return 0;
+}
+
+struct dm_usb_ops ohci_usb_ops = {
+ .control = ohci_submit_control_msg,
+ .bulk = ohci_submit_bulk_msg,
+ .interrupt = ohci_submit_int_msg,
+ .create_int_queue = ohci_create_int_queue,
+ .poll_int_queue = ohci_poll_int_queue,
+ .destroy_int_queue = ohci_destroy_int_queue,
+};
+
+#endif