Hi,
I am working on a PXA320 board running 2.6.25.7. I am having some
issues in getting OXU210 driver working. If anyone could give me any
suggestions/pointers, I would be most grateful.
Following is the description of the problem.
Basically the device can't work with storage devices. It fails to
perform a simple 1 MB 'dd' or fsck on to the attached USB memory
sticks.
Following are the patches that I have applied on top of the vanilla
driver :
oxu_memlock_critical_section_reduction.patch
oxu_schedule_while_atomic.patch
oxu_hardirqsafe_mem_lock.patch
(all attached alongwith)
With this driver, the device is getting reset with following errors :
oxu210hp-hcd oxu210hp-hcd: devpath <NULL> ep1in 3strikes
usb 2-1: reset high speed USB device using oxu210hp-hcd and address 2
oxu210hp-hcd oxu210hp-hcd: port 1 high speed
oxu210hp-hcd oxu210hp-hcd: GetStatus port 1 status 8001205 POWER
sig=se0 PE CONNECT
usb 2-1: reset high speed USB device using oxu210hp-hcd and address 2
oxu210hp-hcd oxu210hp-hcd: port 1 high speed
oxu210hp-hcd oxu210hp-hcd: GetStatus port 1 status 8001205 POWER
sig=se0 PE CONNECT
If I act patient enough, the device eventually gives Buffer I/O errors,
resets and detects the USB memory stick again.
I tried 2.6.30 too. There, the problem persists, but the resets are
prolonged.
In a discussion with Rodolfo Giometti (author of the driver); we
thought using Oxu210's internal 72KB SRAM for storing data structures
and packets (HCD_LOCAL_MEM) should be tried. I backported the DMA
coherent support for ARM to 2.6.25.7 and adapted the driver to those
changes (dma-coherent.patch and oxu_hcd_local_mem.patch attached). There
I am getting a 'System error' IRQ. Here is a snippet from the error
log :
oxu210hp-hcd oxu210hp-hcd: port 1 reset
oxu210hp-hcd oxu210hp-hcd: port 1 high speed
oxu210hp-hcd oxu210hp-hcd: GetStatus port 1 status 8001205 POWER
sig=se0 PE CONNECT
oxu210hp-hcd oxu210hp-hcd: irq status 0004 PCD
usb 3-1: new high speed USB device using oxu210hp-hcd and address 2
oxu210hp-hcd oxu210hp-hcd: submit_async 1 urb cfde0300 ep0out len 64,
qtd d0886860 [qh 00000000]
oxu210hp-hcd oxu210hp-hcd: irq status 0010 FATAL
oxu210hp-hcd oxu210hp-hcd: fatal command 010028 (park)=0 ithresh=1
Async period=256 HALT
oxu210hp-hcd oxu210hp-hcd: fatal status
9088 Async Halt FLR oxu210hp-hcd oxu210hp-hcd: fatal error
oxu210hp-hcd oxu210hp-hcd: reset command 01002a (park)=0 ithresh=1
Async period=256 Reset HALT
oxu210hp-hcd oxu210hp-hcd: ehci_urb_done 1 urb cfde0300 ep0in status
-108 len 0/64
oxu210hp-hcd oxu210hp-hcd: HC died; cleaning up
oxu210hp-hcd oxu210hp-hcd: submit_async 1 urb cfde0300 ep0out len 64,
qtd d08868c0 [qh d0886080]
oxu210hp-hcd oxu210hp-hcd: irq status 0004 PCD oxu210hp-hcd
oxu210hp-hcd: lost IAA
I tried many other things that didn't work (like disabling OTG/SPH,
trying to find the last successful urb etc.). OxSemi's documentation
doesn't help much either.
Please share your insights or suggest any approach that you think might
be helpful.
Let me know if you need more information.
Thanks and regards,
- Amit
Index: linux-2.6.25.7/arch/arm/Kconfig
===================================================================
--- linux-2.6.25.7.orig/arch/arm/Kconfig 2009-06-04 10:37:14.000000000 +0100
+++ linux-2.6.25.7/arch/arm/Kconfig 2009-06-04 10:38:49.000000000 +0100
@@ -13,6 +13,7 @@
select HAVE_OPROFILE
select HAVE_KPROBES if (!XIP_KERNEL)
select HAVE_KRETPROBES if (HAVE_KPROBES)
+ select HAVE_GENERIC_DMA_COHERENT
help
The ARM series is a line of low-power-consumption RISC chip designs
licensed by ARM Ltd and targeted at embedded applications and
Index: linux-2.6.25.7/include/asm-generic/dma-coherent.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.25.7/include/asm-generic/dma-coherent.h 2009-06-05 13:37:27.000000000 +0100
@@ -0,0 +1,40 @@
+#ifndef DMA_COHERENT_H
+#define DMA_COHERENT_H
+
+#ifdef CONFIG_HAVE_GENERIC_DMA_COHERENT
+struct dma_coherent_mem {
+ void *virt_base;
+ u32 device_base;
+ int size;
+ int flags;
+ unsigned long *bitmap;
+};
+
+/*
+ * These two functions are only for dma allocator.
+ * Don't use them in device drivers.
+ */
+int dma_alloc_from_coherent(struct device *dev, ssize_t size,
+ dma_addr_t *dma_handle, void **ret);
+int dma_release_from_coherent(struct device *dev, int order, void *vaddr);
+
+/*
+ * Standard interface
+ */
+#define ARCH_HAS_DMA_DECLARE_COHERENT_MEMORY
+extern int
+dma_declare_coherent_memory(struct device *dev, dma_addr_t bus_addr,
+ dma_addr_t device_addr, size_t size, int flags);
+
+extern void
+dma_release_declared_memory(struct device *dev);
+
+extern void *
+dma_mark_declared_memory_occupied(struct device *dev,
+ dma_addr_t device_addr, size_t size);
+#else
+#define dma_alloc_from_coherent(dev, size, handle, ret) (0)
+#define dma_release_from_coherent(dev, order, vaddr) (0)
+#endif
+
+#endif
Index: linux-2.6.25.7/init/Kconfig
===================================================================
--- linux-2.6.25.7.orig/init/Kconfig 2009-06-04 10:37:15.000000000 +0100
+++ linux-2.6.25.7/init/Kconfig 2009-06-04 10:38:49.000000000 +0100
@@ -761,6 +761,10 @@
endmenu # General setup
+config HAVE_GENERIC_DMA_COHERENT
+ bool
+ default n
+
config SLABINFO
bool
depends on PROC_FS
Index: linux-2.6.25.7/kernel/dma-coherent.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.25.7/kernel/dma-coherent.c 2009-06-05 13:37:27.000000000 +0100
@@ -0,0 +1,169 @@
+/*
+ * Coherent per-device memory handling.
+ * Borrowed from i386
+ */
+#include <linux/kernel.h>
+#include <linux/dma-mapping.h>
+#include <asm-generic/dma-coherent.h>
+
+int dma_declare_coherent_memory(struct device *dev, dma_addr_t bus_addr,
+ dma_addr_t device_addr, size_t size, int flags)
+{
+ void __iomem *mem_base = NULL;
+ int pages = size >> PAGE_SHIFT;
+ int bitmap_size = BITS_TO_LONGS(pages) * sizeof(long);
+
+ if ((flags & (DMA_MEMORY_MAP | DMA_MEMORY_IO)) == 0)
+ goto out;
+ if (!size)
+ goto out;
+ if (dev->dma_mem)
+ goto out;
+
+ /* FIXME: this routine just ignores DMA_MEMORY_INCLUDES_CHILDREN */
+
+ mem_base = ioremap(bus_addr, size);
+ if (!mem_base)
+ goto out;
+
+ dev->dma_mem = kzalloc(sizeof(struct dma_coherent_mem), GFP_KERNEL);
+ if (!dev->dma_mem)
+ goto out;
+ dev->dma_mem->bitmap = kzalloc(bitmap_size, GFP_KERNEL);
+ if (!dev->dma_mem->bitmap)
+ goto free1_out;
+
+ dev->dma_mem->virt_base = mem_base;
+ dev->dma_mem->device_base = device_addr;
+ dev->dma_mem->size = pages;
+ dev->dma_mem->flags = flags;
+
+ if (flags & DMA_MEMORY_MAP)
+ return DMA_MEMORY_MAP;
+
+ return DMA_MEMORY_IO;
+
+ free1_out:
+ kfree(dev->dma_mem);
+ out:
+ if (mem_base)
+ iounmap(mem_base);
+ return 0;
+}
+EXPORT_SYMBOL(dma_declare_coherent_memory);
+
+void dma_release_declared_memory(struct device *dev)
+{
+ struct dma_coherent_mem *mem = dev->dma_mem;
+
+ if (!mem)
+ return;
+ dev->dma_mem = NULL;
+ iounmap(mem->virt_base);
+ kfree(mem->bitmap);
+ kfree(mem);
+}
+EXPORT_SYMBOL(dma_release_declared_memory);
+
+void *dma_mark_declared_memory_occupied(struct device *dev,
+ dma_addr_t device_addr, size_t size)
+{
+ struct dma_coherent_mem *mem = dev->dma_mem;
+ int pos, err;
+
+ size += device_addr & ~PAGE_MASK;
+
+ if (!mem)
+ return ERR_PTR(-EINVAL);
+
+ pos = (device_addr - mem->device_base) >> PAGE_SHIFT;
+ err = bitmap_allocate_region(mem->bitmap, pos, get_order(size));
+ if (err != 0)
+ return ERR_PTR(err);
+ return mem->virt_base + (pos << PAGE_SHIFT);
+}
+EXPORT_SYMBOL(dma_mark_declared_memory_occupied);
+
+/**
+ * dma_alloc_from_coherent() - try to allocate memory from the per-device coherent area
+ *
+ * @dev: device from which we allocate memory
+ * @size: size of requested memory area
+ * @dma_handle: This will be filled with the correct dma handle
+ * @ret: This pointer will be filled with the virtual address
+ * to allocated area.
+ *
+ * This function should be only called from per-arch dma_alloc_coherent()
+ * to support allocation from per-device coherent memory pools.
+ *
+ * Returns 0 if dma_alloc_coherent should continue with allocating from
+ * generic memory areas, or !0 if dma_alloc_coherent should return @ret.
+ */
+int dma_alloc_from_coherent(struct device *dev, ssize_t size,
+ dma_addr_t *dma_handle, void **ret)
+{
+ struct dma_coherent_mem *mem;
+ int order = get_order(size);
+ int pageno;
+
+ if (!dev)
+ return 0;
+ mem = dev->dma_mem;
+ if (!mem)
+ return 0;
+
+ *ret = NULL;
+
+ if (unlikely(size > (mem->size << PAGE_SHIFT)))
+ goto err;
+
+ pageno = bitmap_find_free_region(mem->bitmap, mem->size, order);
+ if (unlikely(pageno < 0))
+ goto err;
+
+ /*
+ * Memory was found in the per-device area.
+ */
+ *dma_handle = mem->device_base + (pageno << PAGE_SHIFT);
+ *ret = mem->virt_base + (pageno << PAGE_SHIFT);
+ memset(*ret, 0, size);
+
+ return 1;
+
+err:
+ /*
+ * In the case where the allocation can not be satisfied from the
+ * per-device area, try to fall back to generic memory if the
+ * constraints allow it.
+ */
+ return mem->flags & DMA_MEMORY_EXCLUSIVE;
+}
+EXPORT_SYMBOL(dma_alloc_from_coherent);
+
+/**
+ * dma_release_from_coherent() - try to free the memory allocated from per-device coherent memory pool
+ * @dev: device from which the memory was allocated
+ * @order: the order of pages allocated
+ * @vaddr: virtual address of allocated pages
+ *
+ * This checks whether the memory was allocated from the per-device
+ * coherent memory pool and if so, releases that memory.
+ *
+ * Returns 1 if we correctly released the memory, or 0 if
+ * dma_release_coherent() should proceed with releasing memory from
+ * generic pools.
+ */
+int dma_release_from_coherent(struct device *dev, int order, void *vaddr)
+{
+ struct dma_coherent_mem *mem = dev ? dev->dma_mem : NULL;
+
+ if (mem && vaddr >= mem->virt_base && vaddr <
+ (mem->virt_base + (mem->size << PAGE_SHIFT))) {
+ int page = (vaddr - mem->virt_base) >> PAGE_SHIFT;
+
+ bitmap_release_region(mem->bitmap, page, order);
+ return 1;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(dma_release_from_coherent);
Index: linux-2.6.25.7/kernel/Makefile
===================================================================
--- linux-2.6.25.7.orig/kernel/Makefile 2009-06-04 10:37:15.000000000 +0100
+++ linux-2.6.25.7/kernel/Makefile 2009-06-04 10:38:49.000000000 +0100
@@ -68,6 +68,7 @@
obj-$(CONFIG_TASKSTATS) += taskstats.o tsacct.o
obj-$(CONFIG_MARKERS) += marker.o
obj-$(CONFIG_LATENCYTOP) += latencytop.o
+obj-$(CONFIG_HAVE_GENERIC_DMA_COHERENT) += dma-coherent.o
ifneq ($(CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER),y)
# According to Alan Modra <alan@xxxxxxxxxxxxxxxx>, the -fno-omit-frame-pointer is
Index: linux-2.6.25.7/arch/arm/mm/consistent.c
===================================================================
--- linux-2.6.25.7.orig/arch/arm/mm/consistent.c 2009-06-04 10:37:14.000000000 +0100
+++ linux-2.6.25.7/arch/arm/mm/consistent.c 2009-06-05 06:01:03.000000000 +0100
@@ -18,6 +18,7 @@
#include <linux/device.h>
#include <linux/dma-mapping.h>
+#include <asm-generic/dma-coherent.h>
#include <asm/memory.h>
#include <asm/cacheflush.h>
#include <asm/tlbflush.h>
@@ -274,6 +275,11 @@
void *
dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp)
{
+ void *memory;
+
+ if (dma_alloc_from_coherent(dev, size, handle, &memory))
+ return memory;
+
if (arch_is_coherent()) {
void *virt;
@@ -362,6 +368,9 @@
WARN_ON(irqs_disabled());
+ if (dma_release_from_coherent(dev, get_order(size), cpu_addr))
+ return;
+
if (arch_is_coherent()) {
kfree(cpu_addr);
return;
Index: linux-2.6.25.7/drivers/usb/host/oxu210hp-hcd.c
===================================================================
--- linux-2.6.25.7.orig/drivers/usb/host/oxu210hp-hcd.c 2009-05-15 12:35:04.000000000 +0100
+++ linux-2.6.25.7/drivers/usb/host/oxu210hp-hcd.c 2009-05-15 12:37:22.000000000 +0100
@@ -485,6 +485,7 @@
int n_blocks; /* minium blocks needed to hold len */
int a_blocks; /* blocks allocated */
int i, j;
+ unsigned long flags;
/* Don't allocte bigger than supported */
if (len > BUFFER_SIZE * BUFFER_NUM) {
@@ -499,7 +500,7 @@
for (a_blocks = 1; a_blocks < n_blocks; a_blocks <<= 1)
;
- spin_lock(&oxu->mem_lock);
+ spin_lock_irqsave(&oxu->mem_lock, flags);
/* Find a suitable available data buffer */
for (i = 0; i < BUFFER_NUM;
@@ -515,7 +516,7 @@
/* Allocate blocks found! */
oxu->db_used[i] = a_blocks;
- spin_unlock(&oxu->mem_lock);
+ spin_unlock_irqrestore(&oxu->mem_lock, flags);
qtd->buffer = (void *) &oxu->mem->db_pool[i];
qtd->buffer_dma = virt_to_phys(qtd->buffer);
@@ -526,8 +527,7 @@
}
/* Failed */
-
- spin_unlock(&oxu->mem_lock);
+ spin_unlock_irqrestore(&oxu->mem_lock, flags);
return -ENOMEM;
}
@@ -535,8 +535,9 @@
static void oxu_buf_free(struct oxu_hcd *oxu, struct ehci_qtd *qtd)
{
int index;
+ unsigned long flags;
- spin_lock(&oxu->mem_lock);
+ spin_lock_irqsave(&oxu->mem_lock, flags);
index = (qtd->buffer - (void *) &oxu->mem->db_pool[0])
/ BUFFER_SIZE;
@@ -545,7 +546,7 @@
qtd->buffer_dma = 0;
qtd->buffer = NULL;
- spin_unlock(&oxu->mem_lock);
+ spin_unlock_irqrestore(&oxu->mem_lock, flags);
return;
}
@@ -563,16 +564,17 @@
static inline void oxu_qtd_free(struct oxu_hcd *oxu, struct ehci_qtd *qtd)
{
int index;
+ unsigned long flags;
if (qtd->buffer)
oxu_buf_free(oxu, qtd);
- spin_lock(&oxu->mem_lock);
+ spin_lock_irqsave(&oxu->mem_lock, flags);
index = qtd - &oxu->mem->qtd_pool[0];
oxu->qtd_used[index] = 0;
- spin_unlock(&oxu->mem_lock);
+ spin_unlock_irqrestore(&oxu->mem_lock, flags);
return;
}
@@ -581,8 +583,9 @@
{
int i;
struct ehci_qtd *qtd = NULL;
+ unsigned long flags;
- spin_lock(&oxu->mem_lock);
+ spin_lock_irqsave(&oxu->mem_lock, flags);
for (i = 0; i < QTD_NUM; i++)
if (!oxu->qtd_used[i])
@@ -590,9 +593,9 @@
if (i < QTD_NUM) {
oxu->qtd_used[i] = 1;
- spin_unlock(&oxu->mem_lock);
+ spin_unlock_irqrestore(&oxu->mem_lock, flags);
} else {
- spin_unlock(&oxu->mem_lock);
+ spin_unlock_irqrestore(&oxu->mem_lock, flags);
return qtd;
}
@@ -612,13 +615,14 @@
static void oxu_qh_free(struct oxu_hcd *oxu, struct ehci_qh *qh)
{
int index;
+ unsigned long flags;
- spin_lock(&oxu->mem_lock);
+ spin_lock_irqsave(&oxu->mem_lock, flags);
index = qh - &oxu->mem->qh_pool[0];
oxu->qh_used[index] = 0;
- spin_unlock(&oxu->mem_lock);
+ spin_unlock_irqrestore(&oxu->mem_lock, flags);
return;
}
@@ -642,8 +646,9 @@
{
int i;
struct ehci_qh *qh = NULL;
+ unsigned long flags;
- spin_lock(&oxu->mem_lock);
+ spin_lock_irqsave(&oxu->mem_lock, flags);
for (i = 0; i < QHEAD_NUM; i++)
if (!oxu->qh_used[i])
@@ -651,9 +656,9 @@
if (i < QHEAD_NUM) {
oxu->qh_used[i] = 1;
- spin_unlock(&oxu->mem_lock);
+ spin_unlock_irqrestore(&oxu->mem_lock, flags);
} else {
- spin_unlock(&oxu->mem_lock);
+ spin_unlock_irqrestore(&oxu->mem_lock, flags);
return qh;
}
@@ -692,13 +697,14 @@
static void oxu_murb_free(struct oxu_hcd *oxu, struct oxu_murb *murb)
{
int index;
+ unsigned long flags;
- spin_lock(&oxu->mem_lock);
+ spin_lock_irqsave(&oxu->mem_lock, flags);
index = murb - &oxu->murb_pool[0];
oxu->murb_used[index] = 0;
- spin_unlock(&oxu->mem_lock);
+ spin_unlock_irqrestore(&oxu->mem_lock, flags);
return;
}
@@ -708,8 +714,9 @@
{
int i;
struct oxu_murb *murb = NULL;
+ unsigned long flags;
- spin_lock(&oxu->mem_lock);
+ spin_lock_irqsave(&oxu->mem_lock, flags);
for (i = 0; i < MURB_NUM; i++)
if (!oxu->murb_used[i])
@@ -721,7 +728,7 @@
oxu->murb_used[i] = 1;
}
- spin_unlock(&oxu->mem_lock);
+ spin_unlock_irqrestore(&oxu->mem_lock, flags);
return murb;
}
Index: linux-2.6.25.7/arch/arm/mach-pxa/mm6.c
===================================================================
--- linux-2.6.25.7.orig/arch/arm/mach-pxa/mm6.c 2009-06-05 13:37:13.000000000 +0100
+++ linux-2.6.25.7/arch/arm/mach-pxa/mm6.c 2009-06-05 13:37:58.000000000 +0100
@@ -449,6 +449,11 @@
.flags = IORESOURCE_MEM,
},
{
+ .start = MM6_USB2_SRAM_PHYS,
+ .end = MM6_USB2_SRAM_PHYS + MM6_USB2_SRAM_PHYS_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
.start = MM6_USB2_IRQ,
.flags = IORESOURCE_IRQ,
}
Index: linux-2.6.25.7/drivers/usb/host/oxu210hp-hcd.c
===================================================================
--- linux-2.6.25.7.orig/drivers/usb/host/oxu210hp-hcd.c 2009-06-05 13:37:13.000000000 +0100
+++ linux-2.6.25.7/drivers/usb/host/oxu210hp-hcd.c 2009-06-05 15:28:30.000000000 +0100
@@ -44,6 +44,7 @@
#include <asm/irq.h>
#include <asm/system.h>
#include <asm/unaligned.h>
+#include <asm-generic/dma-coherent.h>
#include <linux/irq.h>
#include <linux/platform_device.h>
@@ -56,6 +57,8 @@
* Main defines
*/
+void *oxu_sram_base_virtual;
+
#define oxu_dbg(oxu, fmt, args...) \
dev_dbg(oxu_to_hcd(oxu)->self.controller , fmt , ## args)
#define oxu_err(oxu, fmt, args...) \
@@ -1524,6 +1531,7 @@
u32 cmd = readl(&oxu->regs->command);
if (!(cmd & CMD_ASE)) {
+ dbg("async schedule not enabled\n");
/* in case a clear of CMD_ASE didn't take yet */
(void)handshake(oxu, &oxu->regs->status,
STS_ASS, 0, 150);
@@ -2679,6 +2687,7 @@
temp |= (EHCI_TUNE_FLS << 2);
}
oxu->command = temp;
+ printk("oxu_hcd_init() oxu->command 0x%x\n", oxu->command);
return 0;
}
@@ -2702,14 +2711,15 @@
oxu->regs = hcd->regs + OXU_OTG_CAP_OFFSET + \
HC_LENGTH(readl(&oxu->caps->hc_capbase));
- oxu->mem = hcd->regs + OXU_SPH_MEM;
+ oxu->mem = oxu_sram_base_virtual;
} else {
oxu->caps = hcd->regs + OXU_SPH_CAP_OFFSET;
oxu->regs = hcd->regs + OXU_SPH_CAP_OFFSET + \
HC_LENGTH(readl(&oxu->caps->hc_capbase));
- oxu->mem = hcd->regs + OXU_OTG_MEM;
+ oxu->mem = oxu_sram_base_virtual + sizeof(struct oxu_onchip_mem);
}
+ printk("oxu_reset() : otg %d mem 0x%x size %d hcd->regs 0x%x\n", oxu->is_otg, oxu->mem, sizeof(struct oxu_onchip_mem), hcd->regs);
oxu->hcs_params = readl(&oxu->caps->hcs_params);
oxu->sbrn = 0x20;
@@ -3644,7 +3654,7 @@
* Generic hardware linkage
*/
.irq = oxu_irq,
- .flags = HCD_MEMORY | HCD_USB2,
+ .flags = HCD_MEMORY | HCD_USB2 | HCD_LOCAL_MEM,
/*
* Basic lifecycle operations
@@ -3827,6 +3837,7 @@
struct resource *res;
void *base;
unsigned long memstart, memlen;
+ unsigned long coh_memstart, coh_memlen;
int irq, ret;
struct oxu_info *info;
@@ -3836,6 +3847,7 @@
/*
* Get the platform resources
*/
+ /* IRQs */
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!res) {
dev_err(&pdev->dev,
@@ -3845,6 +3857,14 @@
irq = res->start;
dev_dbg(&pdev->dev, "IRQ resource %d\n", irq);
+ ret = set_irq_type(irq, IRQF_TRIGGER_FALLING);
+ if (ret) {
+ dev_err(&pdev->dev, "error setting irq type\n");
+ ret = -EFAULT;
+ goto error_set_irq_type;
+ }
+
+ /* Registers area */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev, "no registers address! Check %s setup!\n",
@@ -3856,24 +3876,56 @@
dev_dbg(&pdev->dev, "MEM resource %lx-%lx\n", memstart, memlen);
if (!request_mem_region(memstart, memlen,
oxu_hc_driver.description)) {
- dev_dbg(&pdev->dev, "memory area already in use\n");
+ dev_err(&pdev->dev, "register memory area already in use\n");
return -EBUSY;
}
- ret = set_irq_type(irq, IRQF_TRIGGER_FALLING);
- if (ret) {
- dev_err(&pdev->dev, "error setting irq type\n");
- ret = -EFAULT;
- goto error_set_irq_type;
- }
-
base = ioremap(memstart, memlen);
if (!base) {
- dev_dbg(&pdev->dev, "error mapping memory\n");
+ dev_err(&pdev->dev, "error mapping memory\n");
ret = -EFAULT;
goto error_ioremap;
}
+ /* Internal memory for coherent mapping */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "no resource definition for coherent memory\n");
+ ret = -ENOENT;
+ goto error_alloc;
+ }
+
+ coh_memstart = res->start;
+ coh_memlen = res->end - res->start + 1;
+ if (!request_mem_region(coh_memstart, coh_memlen,
+ oxu_hc_driver.description)) {
+ dev_err(&pdev->dev, "device SRAM memory area already in use\n");
+ ret = -EBUSY;
+ goto error_ioremap;
+ }
+
+ /* Here we try to use dma_declare_coherent_memory() to make sure
+ * usb allocations with dma_alloc_coherent() allocate from
+ * OXU210's local memory. The dma_handle returned by dma_alloc_coherent()
+ * will be an offset starting from 0 for the first local memory byte.
+ *
+ * Using the corresponding HCD_LOCAL_MEM flag in HCD creation.
+ */
+ printk("dma_declare_coherent_memory() : memstart = 0x%x,memlen = 0x%x\n", coh_memstart, coh_memlen);
+ if (!dma_declare_coherent_memory(&pdev->dev, coh_memstart,
+ coh_memstart - res->parent->start,
+ coh_memlen,
+ DMA_MEMORY_MAP |
+ DMA_MEMORY_EXCLUSIVE)) {
+ dev_err(&pdev->dev, "cannot declare DMA coherent memory\n");
+ ret = -ENXIO;
+ goto error_coherent_memory;
+ }
+ /* set aside some memory for OTG/SPH data structures */
+ dma_mark_declared_memory_occupied(&pdev->dev, coh_memstart, 2 * sizeof(struct oxu_onchip_mem));
+ oxu_sram_base_virtual = pdev->dev.dma_mem->virt_base;
+ printk("oxu_sram_base_virtual 0x%x\n", oxu_sram_base_virtual);
+
/* Allocate a driver data struct to hold useful info for both
* SPH & OTG devices
*/
@@ -3881,7 +3933,7 @@
if (!info) {
dev_dbg(&pdev->dev, "error allocating memory\n");
ret = -EFAULT;
- goto error_alloc;
+ goto error_coherent_memory;
}
platform_set_drvdata(pdev, info);
@@ -3900,6 +3952,12 @@
kfree(info);
platform_set_drvdata(pdev, NULL);
+error_dma_declare_memory:
+ dma_release_declared_memory(&pdev->dev);
+
+error_coherent_memory:
+ release_mem_region(coh_memstart, coh_memlen);
+
error_alloc:
iounmap(base);
@@ -3923,10 +3981,15 @@
unsigned long memstart = info->hcd[0]->rsrc_start,
memlen = info->hcd[0]->rsrc_len;
void *base = info->hcd[0]->regs;
+ struct resource *mem;
oxu_remove(pdev, info->hcd[0]);
oxu_remove(pdev, info->hcd[1]);
+ dma_release_declared_memory(&pdev->dev);
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ release_mem_region(mem->start, mem->end - mem->start + 1);
+
iounmap(base);
release_mem_region(memstart, memlen);
Index: linux-2.6.25.7/include/asm-arm/arch-pxa/mm6.h
===================================================================
--- linux-2.6.25.7.orig/include/asm-arm/arch-pxa/mm6.h 2009-06-05 13:37:13.000000000 +0100
+++ linux-2.6.25.7/include/asm-arm/arch-pxa/mm6.h 2009-06-05 13:37:58.000000000 +0100
@@ -37,7 +37,9 @@
#define MM6_USB2_PHYS 0x12000000
#define MM6_USB2_IRQ IRQ_GPIO(51)
-#define MM6_USB2_PHYS_SIZE 0x3FFFFF
+#define MM6_USB2_PHYS_SIZE 0x0000E000
+#define MM6_USB2_SRAM_PHYS 0x1200E000
+#define MM6_USB2_SRAM_PHYS_SIZE 0x00012000
#define MM6_UARTA_IRQ IRQ_GPIO(93)
#define MM6_UARTB_IRQ IRQ_GPIO(97)
Index: linux-2.6.25.7/drivers/usb/host/oxu210hp-hcd.c
===================================================================
--- linux-2.6.25.7.orig/drivers/usb/host/oxu210hp-hcd.c 2009-05-15 10:53:12.000000000 +0100
+++ linux-2.6.25.7/drivers/usb/host/oxu210hp-hcd.c 2009-05-15 12:15:26.000000000 +0100
@@ -492,8 +492,6 @@
return -ENOMEM;
}
- spin_lock(&oxu->mem_lock);
-
/* Number of blocks needed to hold len */
n_blocks = (len + BUFFER_SIZE - 1) / BUFFER_SIZE;
@@ -501,6 +499,8 @@
for (a_blocks = 1; a_blocks < n_blocks; a_blocks <<= 1)
;
+ spin_lock(&oxu->mem_lock);
+
/* Find a suitable available data buffer */
for (i = 0; i < BUFFER_NUM;
i += max(a_blocks, (int)oxu->db_used[i])) {
@@ -514,13 +514,13 @@
continue;
/* Allocate blocks found! */
+ oxu->db_used[i] = a_blocks;
+ spin_unlock(&oxu->mem_lock);
+
qtd->buffer = (void *) &oxu->mem->db_pool[i];
qtd->buffer_dma = virt_to_phys(qtd->buffer);
qtd->qtd_buffer_len = BUFFER_SIZE * a_blocks;
- oxu->db_used[i] = a_blocks;
-
- spin_unlock(&oxu->mem_lock);
return 0;
}
@@ -589,20 +589,22 @@
break;
if (i < QTD_NUM) {
- qtd = (struct ehci_qtd *) &oxu->mem->qtd_pool[i];
- memset(qtd, 0, sizeof *qtd);
-
- qtd->hw_token = cpu_to_le32(QTD_STS_HALT);
- qtd->hw_next = EHCI_LIST_END;
- qtd->hw_alt_next = EHCI_LIST_END;
- INIT_LIST_HEAD(&qtd->qtd_list);
-
- qtd->qtd_dma = virt_to_phys(qtd);
-
oxu->qtd_used[i] = 1;
+ spin_unlock(&oxu->mem_lock);
+ } else {
+ spin_unlock(&oxu->mem_lock);
+ return qtd;
}
- spin_unlock(&oxu->mem_lock);
+ qtd = (struct ehci_qtd *) &oxu->mem->qtd_pool[i];
+ memset(qtd, 0, sizeof *qtd);
+
+ qtd->hw_token = cpu_to_le32(QTD_STS_HALT);
+ qtd->hw_next = EHCI_LIST_END;
+ qtd->hw_alt_next = EHCI_LIST_END;
+ INIT_LIST_HEAD(&qtd->qtd_list);
+
+ qtd->qtd_dma = virt_to_phys(qtd);
return qtd;
}
@@ -648,28 +650,30 @@
break;
if (i < QHEAD_NUM) {
- qh = (struct ehci_qh *) &oxu->mem->qh_pool[i];
- memset(qh, 0, sizeof *qh);
+ oxu->qh_used[i] = 1;
+ spin_unlock(&oxu->mem_lock);
+ } else {
+ spin_unlock(&oxu->mem_lock);
+ return qh;
+ }
- kref_init(&qh->kref);
- qh->oxu = oxu;
- qh->qh_dma = virt_to_phys(qh);
- INIT_LIST_HEAD(&qh->qtd_list);
-
- /* dummy td enables safe urb queuing */
- qh->dummy = ehci_qtd_alloc(oxu);
- if (qh->dummy == NULL) {
+ qh = (struct ehci_qh *) &oxu->mem->qh_pool[i];
+ memset(qh, 0, sizeof *qh);
+
+ kref_init(&qh->kref);
+ qh->oxu = oxu;
+ qh->qh_dma = virt_to_phys(qh);
+ INIT_LIST_HEAD(&qh->qtd_list);
+
+ /* dummy td enables safe urb queuing */
+ qh->dummy = ehci_qtd_alloc(oxu);
+ if (qh->dummy == NULL) {
oxu_dbg(oxu, "no dummy td\n");
oxu->qh_used[i] = 0;
return NULL;
- }
-
- oxu->qh_used[i] = 1;
}
- spin_unlock(&oxu->mem_lock);
-
return qh;
}
Index: linux-2.6.25.7/drivers/usb/host/oxu210hp-hcd.c
===================================================================
--- linux-2.6.25.7.orig/drivers/usb/host/oxu210hp-hcd.c 2009-05-15 12:18:58.000000000 +0100
+++ linux-2.6.25.7/drivers/usb/host/oxu210hp-hcd.c 2009-05-15 12:31:10.000000000 +0100
@@ -2870,8 +2870,8 @@
do {
murb = (struct urb *) oxu_murb_alloc(oxu);
- if (!murb)
- schedule();
+ if (murb == NULL)
+ return -ENOMEM;
} while (!murb);
/* Coping the urb */
@@ -2892,7 +2892,7 @@
do {
ret = __oxu_urb_enqueue(hcd, murb, mem_flags);
if (ret)
- schedule();
+ return ret;
} while (ret);
}
@@ -2901,8 +2901,8 @@
/* Get free micro URB poll till a free urb is recieved */
do {
murb = (struct urb *) oxu_murb_alloc(oxu);
- if (!murb)
- schedule();
+ if (murb == NULL)
+ return -ENOMEM;
} while (!murb);
/* Coping the urb */
@@ -2920,7 +2920,7 @@
do {
ret = __oxu_urb_enqueue(hcd, murb, mem_flags);
if (ret)
- schedule();
+ return ret;
} while (ret);
return ret;