Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e7652e1e authored by Jan Andersson's avatar Jan Andersson Committed by Greg Kroah-Hartman
Browse files

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: default avatarJan Andersson <jan@gaisler.com>
Acked-by: default avatarAlan Stern <stern@rowland.harvard.edu>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent dfeca7a8
Loading
Loading
Loading
Loading
+68 −19
Original line number Diff line number Diff line
@@ -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);
	}

+10 −0
Original line number Diff line number Diff line
@@ -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 */