Re: [PATCH] [RFC] USB: EHCI: add support for the EV-OXU210-PCI board

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi all,

I did other tests with this driver (attaching the latest version).

Now I'm using a custom USB device, so that I can easily dump the
device's internal memory and monitor the actual data received from the
host controller.

I use a simple testcase: I'm sending a buffer from the host controller
to the device via control transfer, the device stores the buffer into
its internal memory, then I re-read back the buffer and compare the
data.

I've found a very predictable pattern, only transfers using multiple of
8 bytes seem to succeed, in all the other cases I re-read wrong data at
the end of the buffer (only for the reminder, considering 8-bytes
chunks):

bufsize    1: mismatch
bufsize    2: mismatch
bufsize    3: mismatch
bufsize    4: mismatch
bufsize    5: mismatch
bufsize    6: mismatch
bufsize    7: mismatch
bufsize    8: ok
bufsize    9: mismatch
bufsize   10: mismatch
bufsize   11: mismatch
bufsize   12: mismatch
bufsize   13: mismatch
bufsize   14: mismatch
bufsize   15: mismatch
bufsize   16: ok
bufsize   17: mismatch
bufsize   18: mismatch
bufsize   19: mismatch
bufsize   20: mismatch
bufsize   21: mismatch
bufsize   22: mismatch
bufsize   23: mismatch
bufsize   24: ok
...

[ always the same pattern up to bufsize=4096 ]

So, it seems there's a big problem with the hardware...

The datasheet states that it's possible to configure the burst-size for
TX and RX operations and the default value is very suspicious (default
burst len is 8 bytes):

BurstSize
Offset: 0x0160
Reset: 0x00000808
Read/write (writes must be DWord)

- TX Prog Burst (bit 15-8):
   Programmable TX burst length. Maximum length of a burst, in 32-bit
   Burst words, while moving data from system memory to the USB. Default 8

- RX Prog Burst (bit  7-0):
   Programmable RX burst length. Maximum length of a burst, in 32-bit
   Burst words, while moving data from the USB to system memory. Default 8

Unfortunately, setting a different value doesn't seem to change
anything. Maybe I misunderstand the meaning of this BurtSize setting, or
I'm missing something else, but I can't find other concerning parameters
in the datasheet.

[ For those who worked in the past with the same chip: did you find a
similar behaviour? Am I missing some obvious configurations? ]

Could you suggest me if there's a possible way to workaround this issue
at the EHCI layer?

BTW padding the urb size to the upper 8-bytes (for example) is not a
vaild solution, because the device could receive more data than the
amount requested, causing unpredictable results.

Thanks,
-Andrea

Signed-off-by: Andrea Righi <arighi@xxxxxxxxxxx>
---
 arch/x86/Kconfig                 |    2 +-
 drivers/usb/core/hcd.c           |   47 ++++--
 drivers/usb/host/Kconfig         |   10 +
 drivers/usb/host/ehci-hcd.c      |   25 +++-
 drivers/usb/host/ehci-oxu210hp.c |  370 ++++++++++++++++++++++++++++++++++++++
 drivers/usb/host/ehci-oxu210hp.h |   77 ++++++++
 6 files changed, 513 insertions(+), 18 deletions(-)

diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index dcb0593..2396776 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -43,7 +43,7 @@ config X86
 	select HAVE_KVM
 	select HAVE_ARCH_KGDB
 	select HAVE_ARCH_TRACEHOOK
-	select HAVE_GENERIC_DMA_COHERENT if X86_32
+	select HAVE_GENERIC_DMA_COHERENT
 	select HAVE_EFFICIENT_UNALIGNED_ACCESS
 	select USER_STACKTRACE_SUPPORT
 	select HAVE_REGS_AND_STACK_ACCESS_API
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index 12742f1..cabb14d 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -1212,28 +1212,43 @@ EXPORT_SYMBOL_GPL(usb_hcd_unlink_urb_from_ep);
  *
  */
 
+/*
+ * TODO: need refactoring.
+ *
+ * This is only needed to avoid the corruption of the coherent memory
+ * allocations with the EV-OXU210-PCI controller, that can only perform 8-bytes
+ * chunk r/w operations.
+ */
+#define HCD_COHERENT_CHUNK	8
 static int hcd_alloc_coherent(struct usb_bus *bus,
 			      gfp_t mem_flags, dma_addr_t *dma_handle,
 			      void **vaddr_handle, size_t size,
 			      enum dma_data_direction dir)
 {
 	unsigned char *vaddr;
+	size_t size2;
 
-	vaddr = hcd_buffer_alloc(bus, size + sizeof(vaddr),
-				 mem_flags, dma_handle);
-	if (!vaddr)
-		return -ENOMEM;
+	if (*vaddr_handle == NULL) {
+		WARN_ON_ONCE(1);
+		return -EFAULT;
+	}
 
 	/*
-	 * Store the virtual address of the buffer at the end
-	 * of the allocated dma buffer. The size of the buffer
-	 * may be uneven so use unaligned functions instead
-	 * of just rounding up. It makes sense to optimize for
-	 * memory footprint over access speed since the amount
-	 * of memory available for dma may be limited.
+	 * Store the address of the original buffer at the end of the
+	 * newly-allocated dma buffer, so that it can be put back when
+	 * the dma buffer is deallocated.
+	 *
+	 * Avoid unaligned accesses (which some buses can't handle) by rounding
+	 * up to a multiple of HCD_COHERENT_CHUNK.
 	 */
-	put_unaligned((unsigned long)*vaddr_handle,
-		      (unsigned long *)(vaddr + size));
+	size2 = HCD_COHERENT_CHUNK *
+		DIV_ROUND_UP(size + sizeof(vaddr), HCD_COHERENT_CHUNK);
+	vaddr = hcd_buffer_alloc(bus, size2, mem_flags, dma_handle);
+
+	if (!vaddr)
+		return -ENOMEM;
+
+	*(void **)(vaddr + size2 - sizeof(vaddr)) = *vaddr_handle;
 
 	if (dir == DMA_TO_DEVICE)
 		memcpy(vaddr, *vaddr_handle, size);
@@ -1247,17 +1262,21 @@ static void hcd_free_coherent(struct usb_bus *bus, dma_addr_t *dma_handle,
 			      enum dma_data_direction dir)
 {
 	unsigned char *vaddr = *vaddr_handle;
+	size_t size2;
 
-	vaddr = (void *)get_unaligned((unsigned long *)(vaddr + size));
+	size2 = HCD_COHERENT_CHUNK *
+		DIV_ROUND_UP(size + sizeof(vaddr), HCD_COHERENT_CHUNK);
+	vaddr = *(void **)(vaddr + size2 - sizeof(vaddr));
 
 	if (dir == DMA_FROM_DEVICE)
 		memcpy(vaddr, *vaddr_handle, size);
 
-	hcd_buffer_free(bus, size + sizeof(vaddr), *vaddr_handle, *dma_handle);
+	hcd_buffer_free(bus, size2, *vaddr_handle, *dma_handle);
 
 	*vaddr_handle = vaddr;
 	*dma_handle = 0;
 }
+#undef HCD_COHERENT_SIZE
 
 static void unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb)
 {
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index f865be2..1668a5c 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -137,6 +137,16 @@ config USB_W90X900_EHCI
 	---help---
 		Enables support for the W90X900 USB controller
 
+config USB_EHCI_OXU210HP_PCI
+	bool "EHCI based EV-OXU210HP-PCI HCD support (EXPERIMENTAL)"
+	depends on PCI && USB_EHCI_HCD && EXPERIMENTAL
+	default n
+	---help---
+	  The EV-OXU210HP-PCI is an USB host/OTG/device controller. Enable this
+	  option if you have this evaluation board connected to the PCI bus.
+
+	  If unsure, say N.
+
 config USB_OXU210HP_HCD
 	tristate "OXU210HP HCD support"
 	depends on USB
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index a3ef2a9..c0b5a4b 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -606,7 +606,8 @@ static int ehci_init(struct usb_hcd *hcd)
 	ehci->command = temp;
 
 	/* Accept arbitrarily long scatter-gather lists */
-	hcd->self.sg_tablesize = ~0;
+	if (!(hcd->driver->flags & HCD_LOCAL_MEM))
+		hcd->self.sg_tablesize = ~0;
 	return 0;
 }
 
@@ -1103,6 +1104,11 @@ MODULE_LICENSE ("GPL");
 #define	PCI_DRIVER		ehci_pci_driver
 #endif
 
+#ifdef CONFIG_USB_EHCI_OXU210HP_PCI
+#include "ehci-oxu210hp.c"
+#define	OXU210HP_DRIVER		oxu210hp_ehci_driver
+#endif
+
 #ifdef CONFIG_USB_EHCI_FSL
 #include "ehci-fsl.c"
 #define	PLATFORM_DRIVER		ehci_fsl_driver
@@ -1160,7 +1166,7 @@ MODULE_LICENSE ("GPL");
 
 #if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) && \
     !defined(PS3_SYSTEM_BUS_DRIVER) && !defined(OF_PLATFORM_DRIVER) && \
-    !defined(XILINX_OF_PLATFORM_DRIVER)
+    !defined(XILINX_OF_PLATFORM_DRIVER) && !defined(OXU210HP_DRIVER)
 #error "missing bus glue for ehci-hcd"
 #endif
 
@@ -1220,10 +1226,20 @@ static int __init ehci_hcd_init(void)
 	if (retval < 0)
 		goto clean4;
 #endif
+
+#ifdef OXU210HP_DRIVER
+	retval = pci_register_driver(&oxu210_pci_driver);
+	if (retval < 0)
+		goto clean5;
+#endif
 	return retval;
 
+#ifdef OXU210HP_DRIVER
+	/* pci_unregister_driver(&oxu210_pci_driver); */
+clean5:
+#endif
 #ifdef XILINX_OF_PLATFORM_DRIVER
-	/* of_unregister_platform_driver(&XILINX_OF_PLATFORM_DRIVER); */
+	of_unregister_platform_driver(&XILINX_OF_PLATFORM_DRIVER);
 clean4:
 #endif
 #ifdef OF_PLATFORM_DRIVER
@@ -1254,6 +1270,9 @@ module_init(ehci_hcd_init);
 
 static void __exit ehci_hcd_cleanup(void)
 {
+#ifdef OXU210HP_DRIVER
+	pci_unregister_driver(&oxu210_pci_driver);
+#endif
 #ifdef XILINX_OF_PLATFORM_DRIVER
 	of_unregister_platform_driver(&XILINX_OF_PLATFORM_DRIVER);
 #endif
diff --git a/drivers/usb/host/ehci-oxu210hp.c b/drivers/usb/host/ehci-oxu210hp.c
new file mode 100644
index 0000000..82ad041
--- /dev/null
+++ b/drivers/usb/host/ehci-oxu210hp.c
@@ -0,0 +1,370 @@
+/*
+ * EV-OXU210-PCI EHCI Host Controller Driver
+ *
+ * Copyright (c) 2010 Andrea Righi <arighi@xxxxxxxxxxx>
+ *
+ * Part of this code is based on the OXU210HP-HCD driver by Rodolfo Giometti
+ * <giometti@xxxxxxxx> and EHCI-OXU210HP by Amit Walambe
+ * <amit.walambe@xxxxxxxxxxxx>.
+ *
+ * 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; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/irq.h>
+#include <linux/pci.h>
+#include <linux/types.h>
+#include <asm-generic/dma-coherent.h>
+
+#include "ehci-oxu210hp.h"
+
+/* OXU210 PCI definitions */
+#define OXU210_PCI_VENDOR_ID    0x192e
+#define OXU210_PCI_DEVICE_ID    0x032b
+#define OXU210_PCI_BAR          3
+#define OXU210_MEM_SIZE         0x20000
+
+/* OXU210 configurations */
+#define OXU_STARTUP_CONFIG	0x00000b7c
+
+static int oxu210hp_reset(struct usb_hcd *hcd)
+{
+	struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+	struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
+	int ret;
+
+	printk(KERN_INFO "initializing SPH controller\n");
+
+	ehci->caps = hcd->regs + OXU_SPH_CAP_OFFSET;
+	ehci->regs = hcd->regs + OXU_SPH_CAP_OFFSET + HC_LENGTH(ehci_readl(ehci,
+				&ehci->caps->hc_capbase));
+	dbg_hcs_params(ehci, "reset");
+	dbg_hcc_params(ehci, "reset");
+
+	/* Cache this readonly data: minimize chip reads */
+	ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
+
+	ret = ehci_halt(ehci);
+	if (ret)
+		return ret;
+	ret = ehci_init(hcd);
+	if (ret)
+		return ret;
+
+	/* Serial Bus Release Number is at PCI 0x60 offset */
+	pci_read_config_byte(pdev, 0x60, &ehci->sbrn);
+
+	hcd->has_tt = 1;
+	tdi_reset(ehci);
+
+	ehci_reset(ehci);
+
+	/* PCI Memory-Write-Invalidate cycle support is optional (uncommon) */
+	if (!pci_set_mwi(pdev))
+		ehci_dbg(ehci, "MWI active\n");
+
+	ehci_port_power(ehci, 1);
+	if (!pci_set_mwi(pdev))
+		ehci_dbg(ehci, "MWI active\n");
+	return ret;
+}
+
+static const struct hc_driver oxu210hp_ehci_hc_driver = {
+	.description		= hcd_name,
+	.product_desc		= "EV-OXU210-PCI EHCI Host Controller",
+	.hcd_priv_size		= sizeof(struct ehci_hcd),
+
+	/* generic hardware linkage */
+	.irq			= ehci_irq,
+	.flags			= HCD_MEMORY | HCD_LOCAL_MEM | HCD_USB2,
+
+	/* basic lifecycle operations */
+	.reset			= oxu210hp_reset,
+	.start			= ehci_run,
+	.stop			= ehci_stop,
+	.shutdown		= ehci_shutdown,
+
+	/* managing i/o requests and associated device resources */
+	.urb_enqueue		= ehci_urb_enqueue,
+	.urb_dequeue		= ehci_urb_dequeue,
+	.endpoint_disable	= ehci_endpoint_disable,
+	.endpoint_reset		= ehci_endpoint_reset,
+
+	/* scheduling support */
+	.get_frame_number	= ehci_get_frame,
+
+	/* root hub support */
+	.hub_status_data	= ehci_hub_status_data,
+	.hub_control		= ehci_hub_control,
+#if defined(CONFIG_PM)
+	.bus_suspend		= ehci_bus_suspend,
+	.bus_resume		= ehci_bus_resume,
+#endif
+	.relinquish_port	= ehci_relinquish_port,
+	.port_handed_over	= ehci_port_handed_over,
+
+	.clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
+};
+
+static struct usb_hcd *oxu210hp_create(struct pci_dev *pdev,
+				unsigned long memstart, unsigned long memlen,
+				void *base, int irq)
+{
+	struct device *dev = &pdev->dev;
+	struct usb_hcd *hcd;
+	int ret;
+
+	/* Set endian mode and host mode */
+	writel(OXU_CM_HOST_ONLY | OXU_ES_LITTLE | OXU_VBPS,
+		base + OXU_SPH_CORE_OFFSET + OXU_USBMODE);
+	hcd = usb_create_hcd(&oxu210hp_ehci_hc_driver, dev, "oxu210hp_sph");
+	if (!hcd)
+		return ERR_PTR(-ENOMEM);
+
+	hcd->rsrc_start = memstart;
+	hcd->rsrc_len = memlen;
+	hcd->regs = base;
+	hcd->irq = irq;
+	hcd->state = HC_STATE_HALT;
+
+	ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
+	if (ret < 0)
+		return ERR_PTR(ret);
+	return hcd;
+}
+
+static void oxu210hp_configuration(struct pci_dev *pdev, void *base)
+{
+	u32 reg;
+
+	/* Initialize top level registers */
+	writel(OXU_STARTUP_CONFIG, base + OXU_HOSTIFCONFIG);
+	writel(OXU_SRESET, base + OXU_SOFTRESET);
+	writel(OXU_STARTUP_CONFIG, base + OXU_HOSTIFCONFIG);
+
+	/* Disable PIO & SDMA burst read*/
+	writel(0, base + OXU_PIOBURSTREADCTRL);
+	writel(0, base + OXU_SDMABURSTREADCTRL);
+
+	/* Application-speicific register */
+	writel(OXU_SPHPOEN | OXU_OVRCCURPUPDEN | OXU_COMPARATOR | OXU_ASO_OP,
+			base + OXU_ASO);
+
+	/* Configure clocking */
+	writel(OXU_CLK_120MHZ, base + OXU_CLKFREQ);
+	reg = readl(base + OXU_CLKCTRL_SET);
+	writel(reg | OXU_SYSCLKEN, base + OXU_CLKCTRL_SET);
+
+	/* Clear all top interrupt enable */
+	writel(0xff, base + OXU_CHIPIRQEN_CLR);
+
+	/* Clear all top interrupt status */
+	writel(0xff, base + OXU_CHIPIRQSTATUS);
+
+	/* Enable all needed top interrupt except OTG SPH core */
+	writel(OXU_USBSPHLPWUI | OXU_USBOTGLPWUI, base + OXU_CHIPIRQEN_SET);
+}
+
+static int oxu210hp_verify_id(struct pci_dev *pdev, void *base)
+{
+	const char *bo[] = {
+		"reserved",
+		"128-pin LQFP",
+		"84-pin TFBGA",
+		"reserved",
+	};
+	u32 id;
+
+	/* Read controller signature register to find a match */
+	id = readl(base + OXU_DEVICEID);
+	dev_info(&pdev->dev, "device ID %x\n", id);
+	if ((id & OXU_REV_MASK) != (OXU_REV_2100 << OXU_REV_SHIFT))
+		return -ENODEV;
+	dev_info(&pdev->dev, "found device %x %s (%04x:%04x)\n",
+		id >> OXU_REV_SHIFT,
+		bo[(id & OXU_BO_MASK) >> OXU_BO_SHIFT],
+		(id & OXU_MAJ_REV_MASK) >> OXU_MAJ_REV_SHIFT,
+		(id & OXU_MIN_REV_MASK) >> OXU_MIN_REV_SHIFT);
+	return 0;
+}
+
+static int oxu210hp_init(struct pci_dev *pdev,
+				unsigned long memstart, unsigned long memlen,
+				void *base, int irq)
+{
+	struct usb_hcd *hcd = pci_get_drvdata(pdev);
+	int ret;
+
+	oxu210hp_configuration(pdev, base);
+
+	ret = oxu210hp_verify_id(pdev, base);
+	if (ret) {
+		dev_err(&pdev->dev, "no device found!\n");
+		return -ENODEV;
+	}
+
+	/* Create the SPH host controller */
+	hcd = oxu210hp_create(pdev, memstart, memlen, base, irq);
+	if (IS_ERR(hcd)) {
+		dev_err(&pdev->dev, "could not create SPH controller!\n");
+		return PTR_ERR(hcd);
+	}
+	pci_set_drvdata(pdev, hcd);
+
+	writel(readl(base + OXU_CHIPIRQEN_SET) | OXU_USBSPHI,
+			base + OXU_CHIPIRQEN_SET);
+	return 0;
+}
+
+static int
+oxu210hp_ehci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+	void __iomem *base;
+	unsigned long memstart, memlen;
+	int ret = 0;
+
+	ret = pci_enable_device(pdev);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "could not enable device\n");
+		return ret;
+	}
+	if (!(pci_resource_flags(pdev, OXU210_PCI_BAR) & IORESOURCE_MEM)) {
+		dev_err(&pdev->dev,
+			"region #%d not an MMIO resource\n",
+			OXU210_PCI_BAR);
+		ret = -ENODEV;
+		goto err_out_disable;
+	}
+	memlen = pci_resource_len(pdev, OXU210_PCI_BAR);
+	if (memlen < OXU210_MEM_SIZE) {
+		dev_err(&pdev->dev, "invalid PCI region size\n");
+		ret = -ENODEV;
+		goto err_out_disable;
+	}
+	ret = pci_request_regions(pdev, "oxu210hp-ehci");
+	if (ret < 0) {
+		dev_err(&pdev->dev, "could not request regions\n");
+		goto err_out_disable;
+	}
+	ret = set_irq_type(pdev->irq, IRQF_TRIGGER_FALLING);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "error setting irq type\n");
+		ret = -EFAULT;
+		goto err_out_release;
+	}
+	dev_info(&pdev->dev, "IRQ resource %d\n", pdev->irq);
+	memstart = pci_resource_start(pdev, OXU210_PCI_BAR);
+
+	/*
+	 * OXU210 memory map
+	 *
+	 * Block		Address		Size
+	 * -----------------------------------------
+	 * Host interface	0x0000		 1KB
+	 * USB OTG regs		0x0400		 1KB
+	 * USB SPH regs		0x0800		 1KB
+	 * Reserved		0x0c00		 1KB
+	 * Memory expansion	0x1000		12KB
+	 * Reserved		0x4000		32KB
+	 * Memory expansion	0xc000		 8KB
+	 * Static RAM		0xe000		72KB
+	 */
+	base = ioremap(memstart, OXU_REG_SIZE);
+	if (!base) {
+		dev_err(&pdev->dev, "could not remap MMIO\n");
+		ret = -EIO;
+		goto err_out_release;
+	}
+	if (!dma_declare_coherent_memory(&pdev->dev,
+			memstart + OXU_SRAM_OFFSET,	/* bus address */
+			memstart + OXU_SRAM_OFFSET,	/* device address */
+			OXU_SRAM_SIZE,			/* size */
+			DMA_MEMORY_MAP | DMA_MEMORY_EXCLUSIVE)) {
+		dev_err(&pdev->dev,
+			"could not declare DMA coherent memory\n");
+		ret = -ENXIO;
+		goto err_out_iounmap;
+	}
+
+	/* prevent usb-core allocating system DMA pages */
+	pdev->dev.dma_mask = NULL;
+
+	/* Initialize OXU210 chipset */
+	ret = oxu210hp_init(pdev, memstart, OXU_REG_SIZE, base, pdev->irq);
+	if (ret < 0) {
+		dev_dbg(&pdev->dev, "cannot init USB devices\n");
+		goto err_out_dma;
+	}
+	dev_info(&pdev->dev, "devices enabled and running\n");
+out:
+	return ret;
+
+err_out_dma:
+	dma_release_declared_memory(&pdev->dev);
+err_out_iounmap:
+	iounmap(base);
+err_out_release:
+	pci_release_regions(pdev);
+err_out_disable:
+	pci_disable_device(pdev);
+	goto out;
+}
+
+static void oxu210hp_ehci_remove(struct pci_dev *pdev)
+{
+	struct usb_hcd *hcd;
+
+	hcd = pci_get_drvdata(pdev);
+	if (!hcd)
+		return;
+
+	usb_remove_hcd(hcd);
+	iounmap(hcd->regs);
+	pci_release_regions(pdev);
+	dma_release_declared_memory(&pdev->dev);
+
+	dev_set_drvdata(&pdev->dev, NULL);
+	usb_put_hcd(hcd);
+
+	pci_disable_device(pdev);
+}
+
+static void oxu210hp_ehci_shutdown(struct pci_dev *pdev)
+{
+	struct usb_hcd *hcd;
+
+	hcd = pci_get_drvdata(pdev);
+	if (!hcd)
+		return;
+	if (hcd->driver->shutdown)
+		hcd->driver->shutdown(hcd);
+}
+
+MODULE_ALIAS("oxu210hp-ehci");
+
+static struct pci_device_id oxu210hp_pci_table[] = {
+	{ PCI_DEVICE(OXU210_PCI_VENDOR_ID, OXU210_PCI_DEVICE_ID), 0, 0,
+		(unsigned long)&oxu210hp_ehci_hc_driver},
+	{ },
+};
+MODULE_DEVICE_TABLE(pci, oxu210hp_pci_table);
+
+static struct pci_driver oxu210_pci_driver = {
+	.name		= "oxu210hp-ehci",
+	.id_table	= oxu210hp_pci_table,
+
+	.probe		= oxu210hp_ehci_probe,
+	.remove		= oxu210hp_ehci_remove,
+	.shutdown	= oxu210hp_ehci_shutdown,
+};
diff --git a/drivers/usb/host/ehci-oxu210hp.h b/drivers/usb/host/ehci-oxu210hp.h
new file mode 100644
index 0000000..8f93ff8
--- /dev/null
+++ b/drivers/usb/host/ehci-oxu210hp.h
@@ -0,0 +1,77 @@
+#ifndef EHCI_OXU210HP_H
+#define EHCI_OXU210HP_H
+
+/*
+ * OXU210 registers and values definition
+ */
+
+#define OXU_DEVICEID			0x00
+	#define OXU_REV_MASK		0xffff0000
+	#define OXU_REV_SHIFT		16
+	#define OXU_REV_2100		0x2100
+	#define OXU_BO_SHIFT		8
+	#define OXU_BO_MASK		(0x3 << OXU_BO_SHIFT)
+	#define OXU_MAJ_REV_SHIFT	4
+	#define OXU_MAJ_REV_MASK	(0xf << OXU_MAJ_REV_SHIFT)
+	#define OXU_MIN_REV_SHIFT	0
+	#define OXU_MIN_REV_MASK	(0xf << OXU_MIN_REV_SHIFT)
+#define OXU_HOSTIFCONFIG		0x04
+#define OXU_SOFTRESET			0x08
+	#define OXU_SRESET		(1 << 0)
+
+#define OXU_PIOBURSTREADCTRL		0x0C
+#define OXU_SDMABURSTREADCTRL		0x108
+
+#define OXU_CHIPIRQSTATUS		0x10
+#define OXU_CHIPIRQEN_SET		0x14
+#define OXU_CHIPIRQEN_CLR		0x18
+	#define OXU_USBSPHLPWUI		0x00000080
+	#define OXU_USBOTGLPWUI		0x00000040
+	#define OXU_USBSPHI		0x00000002
+	#define OXU_USBOTGI		0x00000001
+
+#define OXU_CLKCTRL_SET			0x1C
+	#define OXU_SYSCLKEN		0x00000008
+	#define OXU_USBSPHCLKEN		0x00000002
+	#define OXU_USBOTGCLKEN		0x00000001
+
+#define OXU_CLKFREQ			0x24
+	#define OXU_CLK_60MHZ		0x00000001
+	#define OXU_CLK_80MHZ		0x00000003
+	#define OXU_CLK_120MHZ		0x00000000
+	#define OXU_CLK_160MHZ		0x00000002
+
+#define OXU_ASO				0x68
+	#define OXU_SPHPOEN		0x00000100
+	#define OXU_OVRCCURPUPDEN	0x00000800
+	#define OXU_ASO_OP		(1 << 10)
+	#define OXU_COMPARATOR		0x000004000
+
+#define OXU_USBMODE			0x1A8
+	#define OXU_VBPS		0x00000020
+	#define OXU_ES_LITTLE		0x00000000
+	#define OXU_CM_HOST_ONLY	0x00000003
+
+/*
+ * OXU210HP memory organization
+ */
+
+#define OXU_OTG_CORE_OFFSET	0x00400
+#define OXU_OTG_CAP_OFFSET	(OXU_OTG_CORE_OFFSET + 0x100)
+#define OXU_SPH_CORE_OFFSET	0x00800
+#define OXU_SPH_CAP_OFFSET	(OXU_SPH_CORE_OFFSET + 0x100)
+
+/*
+ * Register space is limited to the first 4KB of the first 32KB memory section:
+ * Chip, GPIO, slave DMA & mailbox registers.
+ */
+#define OXU_REG_OFFSET		0x1000
+#define OXU_REG_SIZE		0x1000
+
+#define OXU_SRAM_OFFSET		0xe000
+#define OXU_SRAM_END		0x20000
+#define OXU_SRAM_SIZE		(OXU_SRAM_END - OXU_SRAM_OFFSET)
+
+#include <linux/oxu210hp.h>
+
+#endif /* EHCI_OXU210HP_H */
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux