This patch is part of a series that extend the UHCI HCD to support non-PCI host controllers. This patch also extends the uhci_{read,write}* functions to allow accesses to registers not mapped into PCI I/O space. This extension also includes the addition of a void __iomem pointer to the uhci structure. A new Kconfig option is added to signal that the system has a non-PCI HC. If this Kconfig option is set, uhci-hcd.c will include generic functions for systems that do not make use of keyboard and mouse legacy support. In this case these functions will also be used by the PCI UHCI HCD. Otherwise the functions from pci-quirks will be used. This patch is followed by a patch that adds bus glue for the first non-PCI UHCI HC. Signed-off-by: Jan Andersson <jan@xxxxxxxxxxx> --- Changes in V2 of this series: After comments from Alan Stern: * New patch that adds void __iomem pointer and register access functions. * Add generic hc reset functions in uhci-hcd.c * Do not add default n as last option in Kconfig --- drivers/usb/host/Kconfig | 4 ++ drivers/usb/host/uhci-hcd.c | 73 +++++++++++++++++++++++++++++++++++++++++++ drivers/usb/host/uhci-hcd.h | 65 ++++++++++++++++++++++++++++++++++++++ drivers/usb/host/uhci-pci.c | 15 +++++++++ 4 files changed, 157 insertions(+), 0 deletions(-) diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 7dd4c44..8045988 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -410,6 +410,10 @@ config USB_UHCI_HCD To compile this driver as a module, choose M here: the module will be called uhci-hcd. +config USB_UHCI_SUPPORT_NON_PCI_HC + bool + depends on USB_UHCI_HCD + config USB_FHCI_HCD tristate "Freescale QE USB Host Controller support" depends on USB && OF_GPIO && QE_GPIO && QUICC_ENGINE diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index 5176c53..cd482fc 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -167,6 +167,79 @@ static void check_and_reset_hc(struct uhci_hcd *uhci) finish_reset(uhci); } +#if defined(CONFIG_USB_UHCI_SUPPORT_NON_PCI_HC) +/* + * The two functions below are generic reset functions that are used on systems + * that do not have keyboard and mouse legacy support. We assume that we are + * running on such a system if CONFIG_USB_UHCI_SUPPORT_NON_PCI_HC is defined. + */ + +/* + * Make sure the controller is completely inactive, unable to + * generate interrupts or do DMA. + */ +static void uhci_generic_reset_hc(struct uhci_hcd *uhci) +{ + /* Reset the HC - this will force us to get a + * new notification of any already connected + * ports due to the virtual disconnect that it + * implies. + */ + uhci_writew(uhci, USBCMD_HCRESET, USBCMD); + mb(); + udelay(5); + if (uhci_readw(uhci, USBCMD) & USBCMD_HCRESET) + dev_warn(uhci_dev(uhci), "HCRESET not completed yet!\n"); + + /* Just to be safe, disable interrupt requests and + * make sure the controller is stopped. + */ + uhci_writew(uhci, 0, USBINTR); + uhci_writew(uhci, 0, USBCMD); +} + +/* + * Initialize a controller that was newly discovered or has just been + * resumed. In either case we can't be sure of its previous state. + * + * Returns: 1 if the controller was reset, 0 otherwise. + */ +static int uhci_generic_check_and_reset_hc(struct uhci_hcd *uhci) +{ + unsigned int cmd, intr; + + /* + * When restarting a suspended controller, we expect all the + * settings to be the same as we left them: + * + * Controller is stopped and configured with EGSM set; + * No interrupts enabled except possibly Resume Detect. + * + * If any of these conditions are violated we do a complete reset. + */ + + cmd = uhci_readw(uhci, USBCMD); + if ((cmd & USBCMD_RS) || !(cmd & USBCMD_CF) || !(cmd & USBCMD_EGSM)) { + dev_dbg(uhci_dev(uhci), "%s: cmd = 0x%04x\n", + __func__, cmd); + goto reset_needed; + } + + intr = uhci_readw(uhci, USBINTR); + if (intr & (~USBINTR_RESUME)) { + dev_dbg(uhci_dev(uhci), "%s: intr = 0x%04x\n", + __func__, intr); + goto reset_needed; + } + return 0; + +reset_needed: + dev_dbg(uhci_dev(uhci), "Performing full reset\n"); + uhci_generic_reset_hc(uhci); + return 1; +} +#endif /* CONFIG_USB_UHCI_SUPPORT_NON_PCI_HC */ + /* * Store the basic register settings needed by the controller. */ diff --git a/drivers/usb/host/uhci-hcd.h b/drivers/usb/host/uhci-hcd.h index a6de241..a4e64d0 100644 --- a/drivers/usb/host/uhci-hcd.h +++ b/drivers/usb/host/uhci-hcd.h @@ -380,6 +380,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; @@ -481,6 +484,14 @@ 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. + */ + +#if !defined(CONFIG_USB_UHCI_SUPPORT_NON_PCI_HC) +/* Support PCI only */ static inline u32 uhci_readl(struct uhci_hcd *uhci, int reg) { return inl(uhci->io_addr + reg); @@ -511,4 +522,58 @@ static inline void uhci_writeb(struct uhci_hcd *uhci, u8 val, int reg) outb(val, uhci->io_addr + reg); } +#else +/* Support PCI and non-PCI host controllers */ + +#define uhci_has_pci_registers(u) ((u)->io_addr != 0) + +static inline u32 uhci_readl(struct uhci_hcd *uhci, int reg) +{ + if (uhci_has_pci_registers(uhci)) + return inl(uhci->io_addr + reg); + else + return readl(uhci->regs + reg); +} + +static inline void uhci_writel(struct uhci_hcd *uhci, u32 val, int reg) +{ + if (uhci_has_pci_registers(uhci)) + outl(val, uhci->io_addr + reg); + else + writel(val, uhci->regs + reg); +} + +static inline u16 uhci_readw(struct uhci_hcd *uhci, int reg) +{ + if (uhci_has_pci_registers(uhci)) + return inw(uhci->io_addr + reg); + else + return readw(uhci->regs + reg); +} + +static inline void uhci_writew(struct uhci_hcd *uhci, u16 val, int reg) +{ + if (uhci_has_pci_registers(uhci)) + outw(val, uhci->io_addr + reg); + else + writew(val, uhci->regs + reg); +} + +static inline u8 uhci_readb(struct uhci_hcd *uhci, int reg) +{ + if (uhci_has_pci_registers(uhci)) + return inb(uhci->io_addr + reg); + else + return readb(uhci->regs + reg); +} + +static inline void uhci_writeb(struct uhci_hcd *uhci, u8 val, int reg) +{ + if (uhci_has_pci_registers(uhci)) + outb(val, uhci->io_addr + reg); + else + writeb(val, uhci->regs + reg); +} +#endif /* !defined(CONFIG_USB_UHCI_SUPPORT_NON_PCI_HC) */ + #endif diff --git a/drivers/usb/host/uhci-pci.c b/drivers/usb/host/uhci-pci.c index c300bd2..0b7d347 100644 --- a/drivers/usb/host/uhci-pci.c +++ b/drivers/usb/host/uhci-pci.c @@ -17,6 +17,14 @@ * (C) Copyright 2004-2007 Alan Stern, stern@xxxxxxxxxxxxxxxxxxx */ +/* + * If we are running on a system with keyboard and mouse legacy support we use + * the reset functions from pci-quirks. Otherwise we use generic functions from + * uhci-hcd.c + */ +#if !defined(CONFIG_USB_UHCI_SUPPORT_NON_PCI_HC) +/* System that may require keyboard and mouse legacy support */ + #include "pci-quirks.h" /* @@ -55,6 +63,13 @@ static void uhci_pci_configure_hc(struct uhci_hcd *uhci) if (pdev->vendor == PCI_VENDOR_ID_INTEL) pci_write_config_byte(pdev, USBRES_INTEL, 0); } +#else +/* System does not require keyboard and mouse legacy support */ + +#define uhci_pci_reset_hc uhci_generic_reset_hc +#define uhci_pci_check_and_reset_hc uhci_generic_check_and_reset_hc +#define uhci_pci_configure_hc NULL +#endif /* CONFIG_USB_UHCI_SUPPORT_NON_PCI_HC */ static int uhci_pci_resume_detect_interrupts_are_broken(struct uhci_hcd *uhci) { -- 1.7.0.4 -- 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