USB: Implement support for "split" endian OHCI
authorBenjamin Herrenschmidt <benh@kernel.crashing.org>
Thu, 14 Dec 2006 19:54:03 +0000 (06:54 +1100)
committerGreg Kroah-Hartman <gregkh@suse.de>
Wed, 7 Feb 2007 23:44:31 +0000 (15:44 -0800)
This patch separates support for big endian MMIO register access
and big endian descriptors in order to support the Toshiba SCC
implementation which has big endian registers but little endian
in-memory descriptors.

It simplifies the access functions a bit in ohci.h while at it.

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Acked-by: David Brownell <dbrownell@users.sourceforge.net>
Acked-by: Geoff Levand <geoffrey.levand@am.sony.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/usb/host/Kconfig
drivers/usb/host/ohci-pci.c
drivers/usb/host/ohci-ppc-soc.c
drivers/usb/host/ohci.h

index cc60759083bf9ec9cd3d8a6e26f21d716e56b974..faabce8bf39fc8a792e9037c54c156ab3d1ba080 100644 (file)
@@ -101,7 +101,8 @@ 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
+       select USB_OHCI_BIG_ENDIAN_DESC
+       select USB_OHCI_BIG_ENDIAN_MMIO
        ---help---
          Enables support for the USB controller on the MPC52xx or
          STB03xxx processor chip.  If unsure, say Y.
@@ -115,7 +116,12 @@ config USB_OHCI_HCD_PCI
          Enables support for PCI-bus plug-in USB controller cards.
          If unsure, say Y.
 
-config USB_OHCI_BIG_ENDIAN
+config USB_OHCI_BIG_ENDIAN_DESC
+       bool
+       depends on USB_OHCI_HCD
+       default n
+
+config USB_OHCI_BIG_ENDIAN_MMIO
        bool
        depends on USB_OHCI_HCD
        default n
index 82fbec305a66a2663462fd941f8968c5fcfe31c8..292daf044b62da49b5c2f76d34af3a57c8156ee9 100644 (file)
@@ -85,6 +85,27 @@ static int __devinit ohci_quirk_zfmicro(struct usb_hcd *hcd)
        return 0;
 }
 
+/* Check for Toshiba SCC OHCI which has big endian registers
+ * and little endian in memory data structures
+ */
+static int __devinit ohci_quirk_toshiba_scc(struct usb_hcd *hcd)
+{
+       struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+
+       /* That chip is only present in the southbridge of some
+        * cell based platforms which are supposed to select
+        * CONFIG_USB_OHCI_BIG_ENDIAN_MMIO. We verify here if
+        * that was the case though.
+        */
+#ifdef CONFIG_USB_OHCI_BIG_ENDIAN_MMIO
+       ohci->flags |= OHCI_QUIRK_BE_MMIO;
+       ohci_dbg (ohci, "enabled big endian Toshiba quirk\n");
+       return 0;
+#else
+       ohci_err (ohci, "unsupported big endian Toshiba quirk\n");
+       return -ENXIO;
+#endif
+}
 
 /* List of quirks for OHCI */
 static const struct pci_device_id ohci_pci_quirks[] = {
@@ -104,9 +125,14 @@ static const struct pci_device_id ohci_pci_quirks[] = {
                PCI_DEVICE(PCI_VENDOR_ID_COMPAQ, 0xa0f8),
                .driver_data = (unsigned long)ohci_quirk_zfmicro,
        },
+       {
+               PCI_DEVICE(PCI_VENDOR_ID_TOSHIBA_2, 0x01b6),
+               .driver_data = (unsigned long)ohci_quirk_toshiba_scc,
+       },
        /* FIXME for some of the early AMD 760 southbridges, OHCI
         * won't work at all.  blacklist them.
         */
+
        {},
 };
 
index e1a7eb8173135fe7e0d68a32641a6c049b773770..c7ce8e638a250b592bf12af146b7e49c21ca91a0 100644 (file)
@@ -72,7 +72,7 @@ static int usb_hcd_ppc_soc_probe(const struct hc_driver *driver,
        }
 
        ohci = hcd_to_ohci(hcd);
-       ohci->flags |= OHCI_BIG_ENDIAN;
+       ohci->flags |= OHCI_QUIRK_BE_MMIO | OHCI_QUIRK_BE_DESC;
        ohci_hcd_init(ohci);
 
        retval = usb_add_hcd(hcd, irq, IRQF_DISABLED);
index 405257f3e8532ff2d250cdfd612d29d44261523d..fc7c1614cf9e001265d1e2beb0379c27451ee12b 100644 (file)
@@ -394,8 +394,9 @@ struct ohci_hcd {
 #define        OHCI_QUIRK_AMD756       0x01                    /* erratum #4 */
 #define        OHCI_QUIRK_SUPERIO      0x02                    /* natsemi */
 #define        OHCI_QUIRK_INITRESET    0x04                    /* SiS, OPTi, ... */
-#define        OHCI_BIG_ENDIAN         0x08                    /* big endian HC */
-#define        OHCI_QUIRK_ZFMICRO      0x10                    /* Compaq ZFMicro chipset*/
+#define        OHCI_QUIRK_BE_DESC      0x08                    /* BE descriptors */
+#define        OHCI_QUIRK_BE_MMIO      0x10                    /* BE registers */
+#define        OHCI_QUIRK_ZFMICRO      0x20                    /* Compaq ZFMicro chipset*/
        // there are also chip quirks/bugs in init logic
 
 };
@@ -439,117 +440,156 @@ static inline struct usb_hcd *ohci_to_hcd (const struct ohci_hcd *ohci)
  * a minority (notably the IBM STB04XXX and the Motorola MPC5200
  * processors) implement them in big endian format.
  *
+ * In addition some more exotic implementations like the Toshiba
+ * Spider (aka SCC) cell southbridge are "mixed" endian, that is,
+ * they have a different endianness for registers vs. in-memory
+ * descriptors.
+ *
  * This attempts to support either format at compile time without a
  * runtime penalty, or both formats with the additional overhead
  * of checking a flag bit.
+ *
+ * That leads to some tricky Kconfig rules howevber. There are
+ * different defaults based on some arch/ppc platforms, though
+ * the basic rules are:
+ *
+ * Controller type              Kconfig options needed
+ * ---------------              ----------------------
+ * little endian                CONFIG_USB_OHCI_LITTLE_ENDIAN
+ *
+ * fully big endian             CONFIG_USB_OHCI_BIG_ENDIAN_DESC _and_
+ *                              CONFIG_USB_OHCI_BIG_ENDIAN_MMIO
+ *
+ * mixed endian                 CONFIG_USB_OHCI_LITTLE_ENDIAN _and_
+ *                              CONFIG_USB_OHCI_BIG_ENDIAN_{MMIO,DESC}
+ *
+ * (If you have a mixed endian controller, you -must- also define
+ * CONFIG_USB_OHCI_LITTLE_ENDIAN or things will not work when building
+ * both your mixed endian and a fully big endian controller support in
+ * the same kernel image).
  */
 
-#ifdef CONFIG_USB_OHCI_BIG_ENDIAN
+#ifdef CONFIG_USB_OHCI_BIG_ENDIAN_DESC
+#ifdef CONFIG_USB_OHCI_LITTLE_ENDIAN
+#define big_endian_desc(ohci)  (ohci->flags & OHCI_QUIRK_BE_DESC)
+#else
+#define big_endian_desc(ohci)  1               /* only big endian */
+#endif
+#else
+#define big_endian_desc(ohci)  0               /* only little endian */
+#endif
 
+#ifdef CONFIG_USB_OHCI_BIG_ENDIAN_MMIO
 #ifdef CONFIG_USB_OHCI_LITTLE_ENDIAN
-#define big_endian(ohci)       (ohci->flags & OHCI_BIG_ENDIAN) /* either */
+#define big_endian_mmio(ohci)  (ohci->flags & OHCI_QUIRK_BE_MMIO)
+#else
+#define big_endian_mmio(ohci)  1               /* only big endian */
+#endif
 #else
-#define big_endian(ohci)       1               /* only big endian */
+#define big_endian_mmio(ohci)  0               /* only little endian */
 #endif
 
 /*
  * Big-endian read/write functions are arch-specific.
  * Other arches can be added if/when they're needed.
+ *
+ * REVISIT: arch/powerpc now has readl/writel_be, so the
+ * definition below can die once the STB04xxx support is
+ * finally ported over.
  */
-#if defined(CONFIG_PPC)
+#if defined(CONFIG_PPC) && !defined(CONFIG_PPC_MERGE)
 #define readl_be(addr)         in_be32((__force unsigned *)addr)
 #define writel_be(val, addr)   out_be32((__force unsigned *)addr, val)
 #endif
 
-static inline unsigned int ohci_readl (const struct ohci_hcd *ohci,
-                                                       __hc32 __iomem * regs)
+static inline unsigned int _ohci_readl (const struct ohci_hcd *ohci,
+                                       __hc32 __iomem * regs)
 {
-       return big_endian(ohci) ? readl_be (regs) : readl ((__force u32 *)regs);
+       return big_endian_mmio(ohci) ?
+               readl_be ((__force u32 *)regs) :
+               readl ((__force u32 *)regs);
 }
 
-static inline void ohci_writel (const struct ohci_hcd *ohci,
-                               const unsigned int val, __hc32 __iomem *regs)
+static inline void _ohci_writel (const struct ohci_hcd *ohci,
+                                const unsigned int val, __hc32 __iomem *regs)
 {
-       big_endian(ohci) ? writel_be (val, regs) :
-                          writel (val, (__force u32 *)regs);
+       big_endian_mmio(ohci) ?
+               writel_be (val, (__force u32 *)regs) :
+               writel (val, (__force u32 *)regs);
 }
 
-#else  /* !CONFIG_USB_OHCI_BIG_ENDIAN */
-
-#define big_endian(ohci)       0               /* only little endian */
-
 #ifdef CONFIG_ARCH_LH7A404
-       /* Marc Singer: at the time this code was written, the LH7A404
-        * had a problem reading the USB host registers.  This
-        * implementation of the ohci_readl function performs the read
-        * twice as a work-around.
-        */
-static inline unsigned int
-ohci_readl (const struct ohci_hcd *ohci, const __hc32 *regs)
-{
-       *(volatile __force unsigned int*) regs;
-       return *(volatile __force unsigned int*) regs;
-}
+/* Marc Singer: at the time this code was written, the LH7A404
+ * had a problem reading the USB host registers.  This
+ * implementation of the ohci_readl function performs the read
+ * twice as a work-around.
+ */
+#define ohci_readl(o,r)                (_ohci_readl(o,r),_ohci_readl(o,r))
+#define ohci_writel(o,v,r)     _ohci_writel(o,v,r)
 #else
-       /* Standard version of ohci_readl uses standard, platform
-        * specific implementation. */
-static inline unsigned int
-ohci_readl (const struct ohci_hcd *ohci, __hc32 __iomem * regs)
-{
-       return readl(regs);
-}
+#define ohci_readl(o,r)                _ohci_readl(o,r)
+#define ohci_writel(o,v,r)     _ohci_writel(o,v,r)
 #endif
 
-static inline void ohci_writel (const struct ohci_hcd *ohci,
-                               const unsigned int val, __hc32 __iomem *regs)
-{
-       writel (val, regs);
-}
-
-#endif /* !CONFIG_USB_OHCI_BIG_ENDIAN */
 
 /*-------------------------------------------------------------------------*/
 
 /* cpu to ohci */
 static inline __hc16 cpu_to_hc16 (const struct ohci_hcd *ohci, const u16 x)
 {
-       return big_endian(ohci) ? (__force __hc16)cpu_to_be16(x) : (__force __hc16)cpu_to_le16(x);
+       return big_endian_desc(ohci) ?
+               (__force __hc16)cpu_to_be16(x) :
+               (__force __hc16)cpu_to_le16(x);
 }
 
 static inline __hc16 cpu_to_hc16p (const struct ohci_hcd *ohci, const u16 *x)
 {
-       return big_endian(ohci) ? cpu_to_be16p(x) : cpu_to_le16p(x);
+       return big_endian_desc(ohci) ?
+               cpu_to_be16p(x) :
+               cpu_to_le16p(x);
 }
 
 static inline __hc32 cpu_to_hc32 (const struct ohci_hcd *ohci, const u32 x)
 {
-       return big_endian(ohci) ? (__force __hc32)cpu_to_be32(x) : (__force __hc32)cpu_to_le32(x);
+       return big_endian_desc(ohci) ?
+               (__force __hc32)cpu_to_be32(x) :
+               (__force __hc32)cpu_to_le32(x);
 }
 
 static inline __hc32 cpu_to_hc32p (const struct ohci_hcd *ohci, const u32 *x)
 {
-       return big_endian(ohci) ? cpu_to_be32p(x) : cpu_to_le32p(x);
+       return big_endian_desc(ohci) ?
+               cpu_to_be32p(x) :
+               cpu_to_le32p(x);
 }
 
 /* ohci to cpu */
 static inline u16 hc16_to_cpu (const struct ohci_hcd *ohci, const __hc16 x)
 {
-       return big_endian(ohci) ? be16_to_cpu((__force __be16)x) : le16_to_cpu((__force __le16)x);
+       return big_endian_desc(ohci) ?
+               be16_to_cpu((__force __be16)x) :
+               le16_to_cpu((__force __le16)x);
 }
 
 static inline u16 hc16_to_cpup (const struct ohci_hcd *ohci, const __hc16 *x)
 {
-       return big_endian(ohci) ? be16_to_cpup((__force __be16 *)x) : le16_to_cpup((__force __le16 *)x);
+       return big_endian_desc(ohci) ?
+               be16_to_cpup((__force __be16 *)x) :
+               le16_to_cpup((__force __le16 *)x);
 }
 
 static inline u32 hc32_to_cpu (const struct ohci_hcd *ohci, const __hc32 x)
 {
-       return big_endian(ohci) ? be32_to_cpu((__force __be32)x) : le32_to_cpu((__force __le32)x);
+       return big_endian_desc(ohci) ?
+               be32_to_cpu((__force __be32)x) :
+               le32_to_cpu((__force __le32)x);
 }
 
 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);
+       return big_endian_desc(ohci) ?
+               be32_to_cpup((__force __be32 *)x) :
+               le32_to_cpup((__force __le32 *)x);
 }
 
 /*-------------------------------------------------------------------------*/
@@ -557,6 +597,9 @@ static inline u32 hc32_to_cpup (const struct ohci_hcd *ohci, const __hc32 *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.
+ *
+ * FIXME: Deal with that as a runtime quirk when STB03xxx is ported over
+ * to arch/powerpc
  */
 
 #ifdef CONFIG_STB03xxx
@@ -568,7 +611,7 @@ static inline u32 hc32_to_cpup (const struct ohci_hcd *ohci, const __hc32 *x)
 static inline u16 ohci_frame_no(const struct ohci_hcd *ohci)
 {
        u32 tmp;
-       if (big_endian(ohci)) {
+       if (big_endian_desc(ohci)) {
                tmp = be32_to_cpup((__force __be32 *)&ohci->hcca->frame_no);
                tmp >>= OHCI_BE_FRAME_NO_SHIFT;
        } else
@@ -580,7 +623,7 @@ static inline u16 ohci_frame_no(const struct ohci_hcd *ohci)
 static inline __hc16 *ohci_hwPSWp(const struct ohci_hcd *ohci,
                                  const struct td *td, int index)
 {
-       return (__hc16 *)(big_endian(ohci) ?
+       return (__hc16 *)(big_endian_desc(ohci) ?
                        &td->hwPSW[index ^ 1] : &td->hwPSW[index]);
 }