From: Jan Andersson Date: Fri, 6 May 2011 10:00:13 +0000 (+0200) Subject: USB: UHCI: Allow dynamic assignment of bus specific functions X-Git-Tag: v3.0-rc1~337^2~61 X-Git-Url: http://pileus.org/git/?a=commitdiff_plain;h=e7652e1ebc0f5e07929067ece14ca869dad20dd6;p=~andy%2Flinux USB: UHCI: Allow dynamic assignment of bus specific functions This patch is part of a series that extend the UHCI HCD to support non-PCI controllers. This patch changes calls to uhci_reset_hc, uhci_check_and_reset_hc, configure_hc, resume_detect_interrupts_are_broken and global_suspend_mode_is_broken so that they are made through pointers in the uhci hcd struct. This will allow these functions to be replaced with bus/arch specific functions. Signed-off-by: Jan Andersson Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index 214851a6244..683e87e49a0 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -142,6 +142,15 @@ static void finish_reset(struct uhci_hcd *uhci) clear_bit(HCD_FLAG_POLL_RH, &uhci_to_hcd(uhci)->flags); } +/* + * Make sure the controller is completely inactive, unable to + * generate interrupts or do DMA. + */ +static void uhci_pci_reset_hc(struct uhci_hcd *uhci) +{ + uhci_reset_hc(to_pci_dev(uhci_dev(uhci)), uhci->io_addr); +} + /* * Last rites for a defunct/nonfunctional controller * or one we don't want to use any more. @@ -149,7 +158,7 @@ static void finish_reset(struct uhci_hcd *uhci) static void uhci_hc_died(struct uhci_hcd *uhci) { uhci_get_current_frame_number(uhci); - uhci_reset_hc(to_pci_dev(uhci_dev(uhci)), uhci->io_addr); + uhci->reset_hc(uhci); finish_reset(uhci); uhci->dead = 1; @@ -157,6 +166,18 @@ static void uhci_hc_died(struct uhci_hcd *uhci) ++uhci->frame_number; } +/* + * 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_pci_check_and_reset_hc(struct uhci_hcd *uhci) +{ + return uhci_check_and_reset_hc(to_pci_dev(uhci_dev(uhci)), + uhci->io_addr); +} + /* * Initialize a controller that was newly discovered or has lost power * or otherwise been reset while it was suspended. In none of these cases @@ -164,17 +185,27 @@ static void uhci_hc_died(struct uhci_hcd *uhci) */ static void check_and_reset_hc(struct uhci_hcd *uhci) { - if (uhci_check_and_reset_hc(to_pci_dev(uhci_dev(uhci)), uhci->io_addr)) + if (uhci->check_and_reset_hc(uhci)) finish_reset(uhci); } +static void uhci_pci_configure_hc(struct uhci_hcd *uhci) +{ + struct pci_dev *pdev = to_pci_dev(uhci_dev(uhci)); + + /* Enable PIRQ */ + pci_write_config_word(pdev, USBLEGSUP, USBLEGSUP_DEFAULT); + + /* Disable platform-specific non-PME# wakeup */ + if (pdev->vendor == PCI_VENDOR_ID_INTEL) + pci_write_config_byte(pdev, USBRES_INTEL, 0); +} + /* * Store the basic register settings needed by the controller. */ static void configure_hc(struct uhci_hcd *uhci) { - struct pci_dev *pdev = to_pci_dev(uhci_dev(uhci)); - /* Set the frame length to the default: 1 ms exactly */ outb(USBSOF_DEFAULT, uhci->io_addr + USBSOF); @@ -185,24 +216,15 @@ static void configure_hc(struct uhci_hcd *uhci) outw(uhci->frame_number & UHCI_MAX_SOF_NUMBER, uhci->io_addr + USBFRNUM); - /* Enable PIRQ */ - pci_write_config_word(pdev, USBLEGSUP, USBLEGSUP_DEFAULT); - - /* Disable platform-specific non-PME# wakeup */ - if (pdev->vendor == PCI_VENDOR_ID_INTEL) - pci_write_config_byte(pdev, USBRES_INTEL, 0); + /* perform any arch/bus specific configuration */ + if (uhci->configure_hc) + uhci->configure_hc(uhci); } - -static int resume_detect_interrupts_are_broken(struct uhci_hcd *uhci) +static int uhci_pci_resume_detect_interrupts_are_broken(struct uhci_hcd *uhci) { int port; - /* If we have to ignore overcurrent events then almost by definition - * we can't depend on resume-detect interrupts. */ - if (ignore_oc) - return 1; - switch (to_pci_dev(uhci_dev(uhci))->vendor) { default: break; @@ -231,7 +253,18 @@ static int resume_detect_interrupts_are_broken(struct uhci_hcd *uhci) return 0; } -static int global_suspend_mode_is_broken(struct uhci_hcd *uhci) +static int resume_detect_interrupts_are_broken(struct uhci_hcd *uhci) +{ + /* If we have to ignore overcurrent events then almost by definition + * we can't depend on resume-detect interrupts. */ + if (ignore_oc) + return 1; + + return uhci->resume_detect_interrupts_are_broken ? + uhci->resume_detect_interrupts_are_broken(uhci) : 0; +} + +static int uhci_pci_global_suspend_mode_is_broken(struct uhci_hcd *uhci) { int port; const char *sys_info; @@ -253,6 +286,12 @@ static int global_suspend_mode_is_broken(struct uhci_hcd *uhci) return 0; } +static int global_suspend_mode_is_broken(struct uhci_hcd *uhci) +{ + return uhci->global_suspend_mode_is_broken ? + uhci->global_suspend_mode_is_broken(uhci) : 0; +} + static void suspend_rh(struct uhci_hcd *uhci, enum uhci_rh_state new_state) __releases(uhci->lock) __acquires(uhci->lock) @@ -557,6 +596,16 @@ static int uhci_init(struct usb_hcd *hcd) if (to_pci_dev(uhci_dev(uhci))->vendor == PCI_VENDOR_ID_HP) uhci->wait_for_hp = 1; + /* Set up pointers to PCI-specific functions */ + uhci->reset_hc = uhci_pci_reset_hc; + uhci->check_and_reset_hc = uhci_pci_check_and_reset_hc; + uhci->configure_hc = uhci_pci_configure_hc; + uhci->resume_detect_interrupts_are_broken = + uhci_pci_resume_detect_interrupts_are_broken; + uhci->global_suspend_mode_is_broken = + uhci_pci_global_suspend_mode_is_broken; + + /* Kick BIOS off this hardware and reset if the controller * isn't already safely quiescent. */ @@ -847,7 +896,7 @@ static int uhci_pci_resume(struct usb_hcd *hcd, bool hibernated) /* Make sure resume from hibernation re-enumerates everything */ if (hibernated) { - uhci_reset_hc(to_pci_dev(uhci_dev(uhci)), uhci->io_addr); + uhci->reset_hc(uhci); finish_reset(uhci); } diff --git a/drivers/usb/host/uhci-hcd.h b/drivers/usb/host/uhci-hcd.h index f86db61cf08..56943795457 100644 --- a/drivers/usb/host/uhci-hcd.h +++ b/drivers/usb/host/uhci-hcd.h @@ -433,6 +433,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 */