]> Pileus Git - ~andy/linux/blobdiff - drivers/usb/host/uhci-hcd.h
Merge git://git.infradead.org/mtd-2.6
[~andy/linux] / drivers / usb / host / uhci-hcd.h
index 49bf2790f9c22233039ca89e0a61defa0cd3f448..7af2b7052047483be2b4a644de7f0a50355793e9 100644 (file)
 #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 */
 #define QH_WAIT_TIMEOUT                msecs_to_jiffies(200)
 
 
+/*
+ * __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
  */
 
 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;
@@ -168,14 +184,10 @@ struct uhci_qh {
  * We need a special accessor for the element pointer because it is
  * subject to asynchronous updates by the controller.
  */
-static inline __le32 qh_element(struct uhci_qh *qh) {
-       __le32 element = qh->element;
+#define qh_element(qh)         ACCESS_ONCE((qh)->element)
 
-       barrier();
-       return 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))
 
 
 /*
@@ -212,7 +224,7 @@ static inline __le32 qh_element(struct uhci_qh *qh) {
 /*
  * for TD <info>: (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)
@@ -245,10 +257,10 @@ static inline __le32 qh_element(struct uhci_qh *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;
@@ -263,14 +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.
  */
-static inline u32 td_status(struct uhci_td *td) {
-       __le32 status = td->status;
+#define td_status(uhci, td)            hc32_to_cpu((uhci), \
+                                               ACCESS_ONCE((td)->status))
 
-       barrier();
-       return le32_to_cpu(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))
 
 
 /*
@@ -380,6 +388,9 @@ struct uhci_hcd {
        /* Grabbed from PCI */
        unsigned long io_addr;
 
+       /* Used when registers are memory mapped */
+       void __iomem *regs;
+
        struct dma_pool *qh_pool;
        struct dma_pool *td_pool;
 
@@ -390,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;
@@ -415,6 +426,12 @@ struct uhci_hcd {
 
        struct timer_list fsbr_timer;           /* For turning off FBSR */
 
+       /* Silicon quirks */
+       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 */
        unsigned long resuming_ports;
@@ -429,6 +446,16 @@ struct uhci_hcd {
 
        int total_load;                         /* Sum of array values */
        short load[MAX_PHASE];                  /* Periodic allocations */
+
+       /* Reset host controller */
+       void    (*reset_hc) (struct uhci_hcd *uhci);
+       int     (*check_and_reset_hc) (struct uhci_hcd *uhci);
+       /* configure_hc should perform arch specific settings, if needed */
+       void    (*configure_hc) (struct uhci_hcd *uhci);
+       /* Check for broken resume detect interrupts */
+       int     (*resume_detect_interrupts_are_broken) (struct uhci_hcd *uhci);
+       /* Check for broken global suspend */
+       int     (*global_suspend_mode_is_broken) (struct uhci_hcd *uhci);
 };
 
 /* Convert between a usb_hcd pointer and the corresponding uhci_hcd */
@@ -467,4 +494,171 @@ struct urb_priv {
 #define PCI_VENDOR_ID_GENESYS          0x17a0
 #define PCI_DEVICE_ID_GL880S_UHCI      0x8083
 
+/*
+ * Functions used to access controller registers. The UCHI spec says that host
+ * controller I/O registers are mapped into PCI I/O space. For non-PCI hosts
+ * we use memory mapped registers.
+ */
+
+#ifndef CONFIG_USB_UHCI_SUPPORT_NON_PCI_HC
+/* Support PCI only */
+static inline u32 uhci_readl(const struct uhci_hcd *uhci, int reg)
+{
+       return inl(uhci->io_addr + reg);
+}
+
+static inline void uhci_writel(const struct uhci_hcd *uhci, u32 val, int reg)
+{
+       outl(val, uhci->io_addr + reg);
+}
+
+static inline u16 uhci_readw(const struct uhci_hcd *uhci, int reg)
+{
+       return inw(uhci->io_addr + reg);
+}
+
+static inline void uhci_writew(const struct uhci_hcd *uhci, u16 val, int reg)
+{
+       outw(val, uhci->io_addr + reg);
+}
+
+static inline u8 uhci_readb(const struct uhci_hcd *uhci, int reg)
+{
+       return inb(uhci->io_addr + reg);
+}
+
+static inline void uhci_writeb(const struct uhci_hcd *uhci, u8 val, int reg)
+{
+       outb(val, uhci->io_addr + reg);
+}
+
+#else
+/* Support non-PCI host controllers */
+#ifdef CONFIG_PCI
+/* Support PCI and non-PCI host controllers */
+#define uhci_has_pci_registers(u)      ((u)->io_addr != 0)
+#else
+/* Support non-PCI host controllers only */
+#define uhci_has_pci_registers(u)      0
+#endif
+
+#ifdef CONFIG_USB_UHCI_BIG_ENDIAN_MMIO
+/* Support (non-PCI) big endian host controllers */
+#define uhci_big_endian_mmio(u)                ((u)->big_endian_mmio)
+#else
+#define uhci_big_endian_mmio(u)                0
+#endif
+
+static inline u32 uhci_readl(const struct uhci_hcd *uhci, int reg)
+{
+       if (uhci_has_pci_registers(uhci))
+               return inl(uhci->io_addr + reg);
+#ifdef CONFIG_USB_UHCI_BIG_ENDIAN_MMIO
+       else if (uhci_big_endian_mmio(uhci))
+               return readl_be(uhci->regs + reg);
+#endif
+       else
+               return readl(uhci->regs + reg);
+}
+
+static inline void uhci_writel(const struct uhci_hcd *uhci, u32 val, int reg)
+{
+       if (uhci_has_pci_registers(uhci))
+               outl(val, uhci->io_addr + reg);
+#ifdef CONFIG_USB_UHCI_BIG_ENDIAN_MMIO
+       else if (uhci_big_endian_mmio(uhci))
+               writel_be(val, uhci->regs + reg);
+#endif
+       else
+               writel(val, uhci->regs + reg);
+}
+
+static inline u16 uhci_readw(const struct uhci_hcd *uhci, int reg)
+{
+       if (uhci_has_pci_registers(uhci))
+               return inw(uhci->io_addr + reg);
+#ifdef CONFIG_USB_UHCI_BIG_ENDIAN_MMIO
+       else if (uhci_big_endian_mmio(uhci))
+               return readw_be(uhci->regs + reg);
+#endif
+       else
+               return readw(uhci->regs + reg);
+}
+
+static inline void uhci_writew(const struct uhci_hcd *uhci, u16 val, int reg)
+{
+       if (uhci_has_pci_registers(uhci))
+               outw(val, uhci->io_addr + reg);
+#ifdef CONFIG_USB_UHCI_BIG_ENDIAN_MMIO
+       else if (uhci_big_endian_mmio(uhci))
+               writew_be(val, uhci->regs + reg);
+#endif
+       else
+               writew(val, uhci->regs + reg);
+}
+
+static inline u8 uhci_readb(const struct uhci_hcd *uhci, int reg)
+{
+       if (uhci_has_pci_registers(uhci))
+               return inb(uhci->io_addr + reg);
+#ifdef CONFIG_USB_UHCI_BIG_ENDIAN_MMIO
+       else if (uhci_big_endian_mmio(uhci))
+               return readb_be(uhci->regs + reg);
+#endif
+       else
+               return readb(uhci->regs + reg);
+}
+
+static inline void uhci_writeb(const struct uhci_hcd *uhci, u8 val, int reg)
+{
+       if (uhci_has_pci_registers(uhci))
+               outb(val, uhci->io_addr + reg);
+#ifdef CONFIG_USB_UHCI_BIG_ENDIAN_MMIO
+       else if (uhci_big_endian_mmio(uhci))
+               writeb_be(val, uhci->regs + reg);
+#endif
+       else
+               writeb(val, uhci->regs + 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