On Mon, Jan 17, 2005 at 09:21:04PM +0000, David Brownell wrote:
> Sorry I didn't get to this sooner ... it wasn't quite like the version
> I had from merging the two previous patches in ths series (the biggest
> parts by volume). Also, 2.6.11-rc1 includes some changes that affect
> the HCD glue, in small but significant ways. So review took extra
> time! I'd like to wrap this up soon too.
>
> So what I've got here is two patches that should help expedite getting
> something that can merge against RC1:
>
> - ohci-be3.patch ... core ohci updates, including Kconfig so this
> won't break the existing ARM SOC stuff (and is more obvious),
> replacing the corresponding parts of your patch.
>
> - ppc.patch ... catch up to rc1 (but I can't test it), modifies
> only your new "ohci-ppc-on-chip.c" file
>
> I'd also ask you to consider renaming your ".c" file; it bothers me
> for two reasons. First because the names is so long. Second since it's
> too generic ... it won't necessarily be right for all PPC SOC chips, just
> judging by the fact that we already have four different ARM SOC types.
> and each needs different SOC and board-specific glue.
Ok, thanks. I have renamed the file to be "ohci-ppc-soc.c". At
least it's a bit shorter. :) I think there is a good chance that
(possibly with a few modifications), we can get by with a single
glue file for ppc SOC implementations. For most of the ARM glue
files, the only differences are in platform-specific start/stop
functions. For the ppc, I'm implementing platform-specific callbacks,
passed via platform_data to handle these functions. I think it makes
sense to put the platform-specific start/stop functions in the platform
code rather than in the OHCI glue file.
> Question: at least some of the ARM SOC implementations of OHCI need
> (or will need!) better support for external transceiver logic for
> port power switching and overcurrent detection. (Reasons include
> shortage of pins on the SOC chip, and needing better control over
> clocking and current leakage). Are those issues (yet) for the PPC
> OHCI versions you've seen?
As far as I can tell, it isn't an issue for the currently available
boards with these ppc chips. Again, when it becomes an issue, it
will likely be board specific and will be better implemented via
a callback to platform- or board-specific code rather than putting
platform-specific info in the OHCI glue file.
I have attached two files. The first is modification of your
ohci-be3.patch. The only differences are the file rename:
ohci-ppc-on-chip.c => ohci-ppc-soc.c
and a config var rename:
USB_OHCI_HCD_PPC_ON_CHIP => USB_OHCI_HCD_PPC_SOC
The second file is a new version of ohci-ppc-soc.c . I'm sending
the diff versus /dev/null since the multi-generation diffs confused
me. The only substantive difference was adding the callbacks
mentioned above.
Thanks Dave,
-Dale
OHCI core updates for big-endian support on STB03xxx and MPC52xx PPC chips.
===== drivers/usb/host/Kconfig 1.10 vs edited =====
--- 1.10/drivers/usb/host/Kconfig 2004-12-06 11:45:04 -07:00
+++ edited/drivers/usb/host/Kconfig 2005-01-25 12:00:29 -07:00
@@ -7,13 +7,18 @@
default y if ARM # SL-811
default PCI
-# many non-PCI hcds implement OHCI
+# many non-PCI SOC chips embed OHCI
config USB_ARCH_HAS_OHCI
boolean
+ # ARM:
default y if SA1111
default y if ARCH_OMAP
default y if ARCH_LH7A404
default y if PXA27x
+ # PPC:
+ default y if STB03xxx
+ default y if PPC_MPC52xx
+ # more:
default PCI
#
@@ -83,6 +88,35 @@
To compile this driver as a module, choose M here: the
module will be called ohci-hcd.
+
+config USB_OHCI_HCD_PPC_SOC
+ bool "OHCI support for on-chip PPC USB controller"
+ depends on USB_OHCI_HCD && (STB03xxx || PPC_MPC52xx)
+ default y
+ select USB_OHCI_BIG_ENDIAN
+ ---help---
+ Enables support for the USB controller on the MPC52xx or
+ STB03xxx processor chip. If unsure, say Y.
+
+config USB_OHCI_HCD_PCI
+ bool "OHCI support for PCI-bus USB controllers"
+ depends on USB_OHCI_HCD && PCI && (STB03xxx || PPC_MPC52xx)
+ default y
+ select USB_OHCI_LITTLE_ENDIAN
+ ---help---
+ Enables support for PCI-bus plug-in USB controller cards.
+ If unsure, say Y.
+
+config USB_OHCI_BIG_ENDIAN
+ bool
+ depends on USB_OHCI_HCD
+ default n
+
+config USB_OHCI_LITTLE_ENDIAN
+ bool
+ depends on USB_OHCI_HCD
+ default n if STB03xxx || PPC_MPC52xx
+ default y
config USB_UHCI_HCD
tristate "UHCI HCD (most Intel and VIA) support"
===== drivers/usb/host/ohci-dbg.c 1.37 vs edited =====
--- 1.37/drivers/usb/host/ohci-dbg.c 2004-12-18 08:22:52 -07:00
+++ edited/drivers/usb/host/ohci-dbg.c 2005-01-24 10:04:17 -07:00
@@ -328,7 +328,7 @@
hc32_to_cpup (ohci, &td->hwCBP) & ~0x0fff,
hc32_to_cpup (ohci, &td->hwBE));
for (i = 0; i < MAXPSW; i++) {
- u16 psw = hc16_to_cpup (ohci, &td->hwPSW [i]);
+ u16 psw = ohci_hwPSW (ohci, td, i);
int cc = (psw >> 12) & 0x0f;
ohci_dbg (ohci, " psw [%d] = %2x, CC=%x %s=%d\n", i,
psw, cc,
===== drivers/usb/host/ohci-hcd.c 1.85 vs edited =====
--- 1.85/drivers/usb/host/ohci-hcd.c 2005-01-05 13:21:53 -07:00
+++ edited/drivers/usb/host/ohci-hcd.c 2005-01-25 12:25:48 -07:00
@@ -897,11 +897,16 @@
#include "ohci-pxa27x.c"
#endif
+#ifdef CONFIG_USB_OHCI_HCD_PPC_SOC
+#include "ohci-ppc-soc.c"
+#endif
+
#if !(defined(CONFIG_PCI) \
|| defined(CONFIG_SA1111) \
|| defined(CONFIG_ARCH_OMAP) \
|| defined (CONFIG_ARCH_LH7A404) \
|| defined (CONFIG_PXA27x) \
+ || defined (CONFIG_USB_OHCI_HCD_PPC_SOC) \
)
#error "missing bus glue for ohci-hcd"
#endif
===== drivers/usb/host/ohci-q.c 1.67 vs edited =====
--- 1.67/drivers/usb/host/ohci-q.c 2004-12-20 18:15:10 -07:00
+++ edited/drivers/usb/host/ohci-q.c 2005-01-24 10:04:17 -07:00
@@ -547,7 +547,8 @@
td->hwINFO = cpu_to_hc32 (ohci, info);
if (is_iso) {
td->hwCBP = cpu_to_hc32 (ohci, data & 0xFFFFF000);
- td->hwPSW [0] = cpu_to_hc16 (ohci, (data & 0x0FFF) | 0xE000);
+ *ohci_hwPSWp(ohci, td, 0) = cpu_to_hc16 (ohci,
+ (data & 0x0FFF) | 0xE000);
td->ed->last_iso = info & 0xffff;
} else {
td->hwCBP = cpu_to_hc32 (ohci, data);
@@ -719,10 +720,12 @@
/* ISO ... drivers see per-TD length/status */
if (tdINFO & TD_ISO) {
- u16 tdPSW = hc16_to_cpu (ohci, td->hwPSW [0]);
+ u16 tdPSW = ohci_hwPSW (ohci, td, 0);
int dlen = 0;
- /* NOTE: assumes FC in tdINFO == 0 (and MAXPSW == 1) */
+ /* NOTE: assumes FC in tdINFO == 0, and that
+ * only the first of 0..MAXPSW psws is used.
+ */
cc = (tdPSW >> 12) & 0xF;
if (tdINFO & TD_CC) /* hc didn't touch? */
===== drivers/usb/host/ohci.h 1.37 vs edited =====
--- 1.37/drivers/usb/host/ohci.h 2004-12-18 08:22:52 -07:00
+++ edited/drivers/usb/host/ohci.h 2005-01-24 10:04:18 -07:00
@@ -111,8 +111,10 @@
__hc32 hwNextTD; /* Next TD Pointer */
__hc32 hwBE; /* Memory Buffer End Pointer */
- /* PSW is only for ISO */
-#define MAXPSW 1 /* hardware allows 8 */
+ /* PSW is only for ISO. Only 1 PSW entry is used, but on
+ * big-endian PPC hardware that's the second entry.
+ */
+#define MAXPSW 2
__hc16 hwPSW [MAXPSW];
/* rest are purely for the driver's use */
@@ -183,7 +185,7 @@
/*
* OHCI defines u16 frame_no, followed by u16 zero pad.
* Since some processors can't do 16 bit bus accesses,
- * portable access must be a 32 bit byteswapped access.
+ * portable access must be a 32 bits wide.
*/
__hc32 frame_no; /* current frame number */
__hc32 done_head; /* info returned for an interrupt */
@@ -191,8 +193,6 @@
u8 what [4]; /* spec only identifies 252 bytes :) */
} __attribute__ ((aligned(256)));
-#define ohci_frame_no(ohci) ((u16)hc32_to_cpup(ohci,&(ohci)->hcca->frame_no))
-
/*
* This is the structure of the OHCI controller's memory mapped I/O region.
* You must use readl() and writel() (in <asm/io.h>) to access these fields!!
@@ -550,6 +550,44 @@
static inline u32 hc32_to_cpup (const struct ohci_hcd *ohci, const __hc32 *x)
{
return big_endian(ohci) ? be32_to_cpup((__force __be32 *)x) :
le32_to_cpup((__force __le32 *)x);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* HCCA frame number is 16 bits, but is accessed as 32 bits since not all
+ * hardware handles 16 bit reads. That creates a different confusion on
+ * some big-endian SOC implementations. Same thing happens with PSW access.
+ */
+
+#ifdef CONFIG_STB03xxx
+#define OHCI_BE_FRAME_NO_SHIFT 16
+#else
+#define OHCI_BE_FRAME_NO_SHIFT 0
+#endif
+
+static inline u16 ohci_frame_no(const struct ohci_hcd *ohci)
+{
+ u32 tmp;
+ if (big_endian(ohci)) {
+ tmp = be32_to_cpup((__force __be32 *)&ohci->hcca->frame_no);
+ tmp >>= OHCI_BE_FRAME_NO_SHIFT;
+ } else
+ tmp = le32_to_cpup((__force __le32 *)&ohci->hcca->frame_no);
+
+ return (u16)tmp;
+}
+
+static inline __hc16 *ohci_hwPSWp(const struct ohci_hcd *ohci,
+ const struct td *td, int index)
+{
+ return (__hc16 *)(big_endian(ohci) ?
+ &td->hwPSW[index ^ 1] : &td->hwPSW[index]);
+}
+
+static inline u16 ohci_hwPSW(const struct ohci_hcd *ohci,
+ const struct td *td, int index)
+{
+ return hc16_to_cpup(ohci, ohci_hwPSWp(ohci, td, index));
}
/*-------------------------------------------------------------------------*/
Diff to create drivers/usb/host/ohci-ppc-soc.c, the USB OHCI glue file
for PPC SOC implementations.
diff -Nru a/drivers/usb/host/ohci-ppc-soc.c b/drivers/usb/host/ohci-ppc-soc.c
--- /dev/null Wed Dec 31 16:00:00 196900
+++ b/drivers/usb/host/ohci-ppc-soc.c 2005-01-25 14:17:10 -07:00
@@ -0,0 +1,299 @@
+/*
+ * OHCI HCD (Host Controller Driver) for USB.
+ *
+ * (C) Copyright 1999 Roman Weissgaerber <[EMAIL PROTECTED]>
+ * (C) Copyright 2000-2002 David Brownell <[EMAIL PROTECTED]>
+ * (C) Copyright 2002 Hewlett-Packard Company
+ * (C) Copyright 2003-2005 MontaVista Software Inc.
+ *
+ * Bus Glue for PPC On-Chip OHCI driver
+ * Tested on Freescale MPC5200 and IBM STB04xxx
+ *
+ * Modified by Dale Farnsworth <[EMAIL PROTECTED]> from ohci-sa1111.c
+ *
+ * This file is licenced under the GPL.
+ */
+
+#include <asm/usb.h>
+
+static void usb_hcd_ppc_soc_remove(struct usb_hcd *, struct platform_device *);
+
+/* configure so an HC device and id are always provided */
+/* always called with process context; sleeping is OK */
+
+/**
+ * usb_hcd_ppc_soc_probe - initialize On-Chip HCDs
+ * Context: !in_interrupt()
+ *
+ * Allocates basic resources for this USB host controller, and
+ * then invokes the start() method for the HCD associated with it
+ * through the hotplug entry's driver_data.
+ *
+ * Store this function in the HCD's struct pci_driver as probe().
+ */
+static int usb_hcd_ppc_soc_probe(const struct hc_driver *driver,
+ struct usb_hcd **hcd_out,
+ struct platform_device *pdev)
+{
+ int retval;
+ struct usb_hcd *hcd = 0;
+ struct ohci_hcd *ohci;
+ struct resource *res;
+ int irq;
+ struct usb_hcd_platform_data *pd = pdev->dev.platform_data;
+
+ pr_debug("initializing PPC-SOC USB Controller\n");
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res) {
+ pr_debug(__FILE__ ": no irq\n");
+ return -ENODEV;
+ }
+ irq = res->start;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ pr_debug(__FILE__ ": no reg addr\n");
+ return -ENODEV;
+ }
+ if (!request_mem_region(res->start, res->end - res->start + 1,
+ hcd_name)) {
+ pr_debug(__FILE__ ": request_mem_region failed\n");
+ return -EBUSY;
+ }
+
+ if (pd->start && (retval = pd->start(pdev)))
+ goto err0;
+
+ hcd = usb_create_hcd(driver);
+ if (!hcd){
+ pr_debug(__FILE__ ": hcd_alloc failed\n");
+ retval = -ENOMEM;
+ goto err1;
+ }
+
+ ohci = hcd_to_ohci(hcd);
+
+ ohci->flags |= OHCI_BIG_ENDIAN;
+
+ ohci_hcd_init(ohci);
+
+ hcd->irq = irq;
+ hcd->regs = (struct ohci_regs *) ioremap(res->start,
+ res->end - res->start + 1);
+ if (!hcd->regs) {
+ pr_debug(__FILE__ ": ioremap failed\n");
+ retval = -ENOMEM;
+ goto err2;
+ }
+
+ hcd->self.controller = &pdev->dev;
+
+ retval = hcd_buffer_create(hcd);
+ if (retval) {
+ pr_debug(__FILE__ ": pool alloc fail\n");
+ goto err3;
+ }
+
+ retval = request_irq(hcd->irq, usb_hcd_irq, SA_INTERRUPT,
+ hcd_name, hcd);
+ if (retval) {
+ pr_debug(__FILE__ ": request_irq failed, returned %d\n",
+ retval);
+ retval = -EBUSY;
+ goto err4;
+ }
+
+ info("%s (PPC-SOC) at 0x%p, irq %d\n",
+ hcd_name, hcd->regs, hcd->irq);
+
+ hcd->self.bus_name = "PPC-SOC USB";
+
+ usb_register_bus(&hcd->self);
+
+ if ((retval = driver->start(hcd)) < 0) {
+ usb_hcd_ppc_soc_remove(hcd, pdev);
+ return retval;
+ }
+
+ *hcd_out = hcd;
+ return 0;
+
+ err4:
+ hcd_buffer_destroy(hcd);
+ err3:
+ iounmap(hcd->regs);
+ err2:
+ dev_set_drvdata(&pdev->dev, NULL);
+ usb_put_hcd(hcd);
+ err1:
+ pr_debug("Removing PPC-SOC USB Controller\n");
+ if (pd && pd->stop)
+ pd->stop(pdev);
+ err0:
+ release_mem_region(res->start, res->end - res->start + 1);
+ return retval;
+}
+
+
+/* may be called without controller electrically present */
+/* may be called with controller, bus, and devices active */
+
+/**
+ * usb_hcd_ppc_soc_remove - shutdown processing for On-Chip HCDs
+ * @pdev: USB Host Controller being removed
+ * Context: !in_interrupt()
+ *
+ * Reverses the effect of usb_hcd_ppc_soc_probe(), first invoking
+ * the HCD's stop() method. It is always called from a thread
+ * context, normally "rmmod", "apmd", or something similar.
+ *
+ */
+static void usb_hcd_ppc_soc_remove(struct usb_hcd *hcd, struct platform_device
*pdev)
+{
+ struct resource *res;
+ struct usb_hcd_platform_data *pd = pdev->dev.platform_data;
+
+ pr_debug(__FILE__ ": remove: %s, state %x\n", hcd->self.bus_name,
+ hcd->state);
+ if (in_interrupt())
+ BUG();
+
+ hcd->state = USB_STATE_QUIESCING;
+
+ pr_debug("%s: roothub graceful disconnect\n", hcd->self.bus_name);
+ usb_disconnect(&hcd->self.root_hub);
+
+ hcd->driver->stop(hcd);
+ hcd->state = USB_STATE_HALT;
+
+ free_irq(hcd->irq, hcd);
+ hcd_buffer_destroy(hcd);
+
+ usb_deregister_bus(&hcd->self);
+
+ iounmap(hcd->regs);
+ kfree(hcd);
+
+ pr_debug("stopping PPC-SOC USB Controller\n");
+
+ if (pd && pd->stop)
+ pd->stop(pdev);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(res->start, res->end - res->start + 1);
+}
+
+static int __devinit
+ohci_ppc_soc_start(struct usb_hcd *hcd)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci(hcd);
+ int ret;
+
+ if ((ret = ohci_init(ohci)) < 0)
+ return ret;
+
+ if ((ret = ohci_run(ohci)) < 0) {
+ err("can't start %s", ohci_to_hcd(ohci)->self.bus_name);
+ ohci_stop(hcd);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct hc_driver ohci_ppc_soc_hc_driver = {
+ .description = hcd_name,
+ .hcd_priv_size = sizeof(struct ohci_hcd),
+
+ /*
+ * generic hardware linkage
+ */
+ .irq = ohci_irq,
+ .flags = HCD_USB11,
+
+ /*
+ * basic lifecycle operations
+ */
+ .start = ohci_ppc_soc_start,
+ .stop = ohci_stop,
+
+ /*
+ * managing i/o requests and associated device resources
+ */
+ .urb_enqueue = ohci_urb_enqueue,
+ .urb_dequeue = ohci_urb_dequeue,
+ .endpoint_disable = ohci_endpoint_disable,
+
+ /*
+ * scheduling support
+ */
+ .get_frame_number = ohci_get_frame,
+
+ /*
+ * root hub support
+ */
+ .hub_status_data = ohci_hub_status_data,
+ .hub_control = ohci_hub_control,
+#ifdef CONFIG_USB_SUSPEND
+ .hub_suspend = ohci_hub_suspend,
+ .hub_resume = ohci_hub_resume,
+#endif
+ .start_port_reset = ohci_start_port_reset,
+};
+
+static int ohci_hcd_ppc_soc_drv_probe(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct usb_hcd *hcd = NULL;
+ int ret;
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ ret = usb_hcd_ppc_soc_probe(&ohci_ppc_soc_hc_driver, &hcd, pdev);
+
+ if (ret == 0)
+ dev_set_drvdata(dev, hcd);
+
+ return ret;
+}
+
+static int ohci_hcd_ppc_soc_drv_remove(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+
+ usb_hcd_ppc_soc_remove(hcd, pdev);
+
+ dev_set_drvdata(dev, NULL);
+ return 0;
+}
+
+static struct device_driver ohci_hcd_ppc_soc_driver = {
+ .name = "ppc-soc-ohci",
+ .bus = &platform_bus_type,
+ .probe = ohci_hcd_ppc_soc_drv_probe,
+ .remove = ohci_hcd_ppc_soc_drv_remove,
+#if defined(CONFIG_USB_SUSPEND) || defined(CONFIG_PM)
+ /*.suspend = ohci_hcd_ppc_soc_drv_suspend,*/
+ /*.resume = ohci_hcd_ppc_soc_drv_resume,*/
+#endif
+};
+
+static int __init ohci_hcd_ppc_soc_init(void)
+{
+ pr_debug(DRIVER_INFO " (PPC SOC)\n");
+ pr_debug("block sizes: ed %d td %d\n", sizeof(struct ed),
+ sizeof(struct td));
+
+ return driver_register(&ohci_hcd_ppc_soc_driver);
+}
+
+static void __exit ohci_hcd_ppc_soc_cleanup(void)
+{
+ driver_unregister(&ohci_hcd_ppc_soc_driver);
+}
+
+module_init(ohci_hcd_ppc_soc_init);
+module_exit(ohci_hcd_ppc_soc_cleanup);