From 51e2f62fe79651e7ed8e16ba126a163b116fe3d7 Mon Sep 17 00:00:00 2001 From: Jan Andersson Date: Wed, 18 May 2011 10:44:51 +0200 Subject: USB: UHCI: Add support for big endian descriptors This patch adds support for universal host controllers that use big endian descriptors. Support for BE descriptors requires a non-PCI host controller. For kernels with PCI-only UHCI there should be no change in behaviour. This patch tries to replicate the technique used to support BE descriptors in the EHCI HCD. Parts added to uhci-hcd.h are basically copy'n'paste from ehci.h. Signed-off-by: Jan Andersson Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/uhci-hcd.h | 90 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 74 insertions(+), 16 deletions(-) (limited to 'drivers/usb/host/uhci-hcd.h') diff --git a/drivers/usb/host/uhci-hcd.h b/drivers/usb/host/uhci-hcd.h index 0deeab6..7af2b70 100644 --- a/drivers/usb/host/uhci-hcd.h +++ b/drivers/usb/host/uhci-hcd.h @@ -78,11 +78,11 @@ #define USBPORT1EN 0x01 #define USBPORT2EN 0x02 -#define UHCI_PTR_BITS cpu_to_le32(0x000F) -#define UHCI_PTR_TERM cpu_to_le32(0x0001) -#define UHCI_PTR_QH cpu_to_le32(0x0002) -#define UHCI_PTR_DEPTH cpu_to_le32(0x0004) -#define UHCI_PTR_BREADTH cpu_to_le32(0x0000) +#define UHCI_PTR_BITS(uhci) cpu_to_hc32((uhci), 0x000F) +#define UHCI_PTR_TERM(uhci) cpu_to_hc32((uhci), 0x0001) +#define UHCI_PTR_QH(uhci) cpu_to_hc32((uhci), 0x0002) +#define UHCI_PTR_DEPTH(uhci) cpu_to_hc32((uhci), 0x0004) +#define UHCI_PTR_BREADTH(uhci) cpu_to_hc32((uhci), 0x0000) #define UHCI_NUMFRAMES 1024 /* in the frame list [array] */ #define UHCI_MAX_SOF_NUMBER 2047 /* in an SOF packet */ @@ -99,6 +99,22 @@ /* + * __hc32 and __hc16 are "Host Controller" types, they may be equivalent to + * __leXX (normally) or __beXX (given UHCI_BIG_ENDIAN_DESC), depending on + * the host controller implementation. + * + * To facilitate the strongest possible byte-order checking from "sparse" + * and so on, we use __leXX unless that's not practical. + */ +#ifdef CONFIG_USB_UHCI_BIG_ENDIAN_DESC +typedef __u32 __bitwise __hc32; +typedef __u16 __bitwise __hc16; +#else +#define __hc32 __le32 +#define __hc16 __le16 +#endif + +/* * Queue Headers */ @@ -130,8 +146,8 @@ struct uhci_qh { /* Hardware fields */ - __le32 link; /* Next QH in the schedule */ - __le32 element; /* Queue element (TD) pointer */ + __hc32 link; /* Next QH in the schedule */ + __hc32 element; /* Queue element (TD) pointer */ /* Software fields */ dma_addr_t dma_handle; @@ -170,7 +186,8 @@ struct uhci_qh { */ #define qh_element(qh) ACCESS_ONCE((qh)->element) -#define LINK_TO_QH(qh) (UHCI_PTR_QH | cpu_to_le32((qh)->dma_handle)) +#define LINK_TO_QH(uhci, qh) (UHCI_PTR_QH((uhci)) | \ + cpu_to_hc32((uhci), (qh)->dma_handle)) /* @@ -207,7 +224,7 @@ struct uhci_qh { /* * for TD : (a.k.a. Token) */ -#define td_token(td) le32_to_cpu((td)->token) +#define td_token(uhci, td) hc32_to_cpu((uhci), (td)->token) #define TD_TOKEN_DEVADDR_SHIFT 8 #define TD_TOKEN_TOGGLE_SHIFT 19 #define TD_TOKEN_TOGGLE (1 << 19) @@ -240,10 +257,10 @@ struct uhci_qh { */ struct uhci_td { /* Hardware fields */ - __le32 link; - __le32 status; - __le32 token; - __le32 buffer; + __hc32 link; + __hc32 status; + __hc32 token; + __hc32 buffer; /* Software fields */ dma_addr_t dma_handle; @@ -258,9 +275,10 @@ struct uhci_td { * We need a special accessor for the control/status word because it is * subject to asynchronous updates by the controller. */ -#define td_status(td) le32_to_cpu(ACCESS_ONCE((td)->status)) +#define td_status(uhci, td) hc32_to_cpu((uhci), \ + ACCESS_ONCE((td)->status)) -#define LINK_TO_TD(td) (cpu_to_le32((td)->dma_handle)) +#define LINK_TO_TD(uhci, td) (cpu_to_hc32((uhci), (td)->dma_handle)) /* @@ -383,7 +401,7 @@ struct uhci_hcd { spinlock_t lock; dma_addr_t frame_dma_handle; /* Hardware frame list */ - __le32 *frame; + __hc32 *frame; void **frame_cpu; /* CPU's frame list */ enum uhci_rh_state rh_state; @@ -412,6 +430,7 @@ struct uhci_hcd { unsigned int oc_low:1; /* OverCurrent bit active low */ unsigned int wait_for_hp:1; /* Wait for HP port reset */ unsigned int big_endian_mmio:1; /* Big endian registers */ + unsigned int big_endian_desc:1; /* Big endian descriptors */ /* Support for port suspend/resume/reset */ unsigned long port_c_suspend; /* Bit-arrays of ports */ @@ -603,4 +622,43 @@ static inline void uhci_writeb(const struct uhci_hcd *uhci, u8 val, int reg) } #endif /* CONFIG_USB_UHCI_SUPPORT_NON_PCI_HC */ +/* + * The GRLIB GRUSBHC controller can use big endian format for its descriptors. + * + * UHCI controllers accessed through PCI work normally (little-endian + * everywhere), so we don't bother supporting a BE-only mode. + */ +#ifdef CONFIG_USB_UHCI_BIG_ENDIAN_DESC +#define uhci_big_endian_desc(u) ((u)->big_endian_desc) + +/* cpu to uhci */ +static inline __hc32 cpu_to_hc32(const struct uhci_hcd *uhci, const u32 x) +{ + return uhci_big_endian_desc(uhci) + ? (__force __hc32)cpu_to_be32(x) + : (__force __hc32)cpu_to_le32(x); +} + +/* uhci to cpu */ +static inline u32 hc32_to_cpu(const struct uhci_hcd *uhci, const __hc32 x) +{ + return uhci_big_endian_desc(uhci) + ? be32_to_cpu((__force __be32)x) + : le32_to_cpu((__force __le32)x); +} + +#else +/* cpu to uhci */ +static inline __hc32 cpu_to_hc32(const struct uhci_hcd *uhci, const u32 x) +{ + return cpu_to_le32(x); +} + +/* uhci to cpu */ +static inline u32 hc32_to_cpu(const struct uhci_hcd *uhci, const __hc32 x) +{ + return le32_to_cpu(x); +} +#endif + #endif -- cgit v1.1