162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * UHCI HCD (Host Controller Driver) PCI Bus Glue.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Extracted from uhci-hcd.c:
662306a36Sopenharmony_ci * Maintainer: Alan Stern <stern@rowland.harvard.edu>
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * (C) Copyright 1999 Linus Torvalds
962306a36Sopenharmony_ci * (C) Copyright 1999-2002 Johannes Erdfelt, johannes@erdfelt.com
1062306a36Sopenharmony_ci * (C) Copyright 1999 Randy Dunlap
1162306a36Sopenharmony_ci * (C) Copyright 1999 Georg Acher, acher@in.tum.de
1262306a36Sopenharmony_ci * (C) Copyright 1999 Deti Fliegl, deti@fliegl.de
1362306a36Sopenharmony_ci * (C) Copyright 1999 Thomas Sailer, sailer@ife.ee.ethz.ch
1462306a36Sopenharmony_ci * (C) Copyright 1999 Roman Weissgaerber, weissg@vienna.at
1562306a36Sopenharmony_ci * (C) Copyright 2000 Yggdrasil Computing, Inc. (port of new PCI interface
1662306a36Sopenharmony_ci *               support from usb-ohci.c by Adam Richter, adam@yggdrasil.com).
1762306a36Sopenharmony_ci * (C) Copyright 1999 Gregory P. Smith (from usb-ohci.c)
1862306a36Sopenharmony_ci * (C) Copyright 2004-2007 Alan Stern, stern@rowland.harvard.edu
1962306a36Sopenharmony_ci */
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#include "pci-quirks.h"
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci/*
2462306a36Sopenharmony_ci * Make sure the controller is completely inactive, unable to
2562306a36Sopenharmony_ci * generate interrupts or do DMA.
2662306a36Sopenharmony_ci */
2762306a36Sopenharmony_cistatic void uhci_pci_reset_hc(struct uhci_hcd *uhci)
2862306a36Sopenharmony_ci{
2962306a36Sopenharmony_ci	uhci_reset_hc(to_pci_dev(uhci_dev(uhci)), uhci->io_addr);
3062306a36Sopenharmony_ci}
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci/*
3362306a36Sopenharmony_ci * Initialize a controller that was newly discovered or has just been
3462306a36Sopenharmony_ci * resumed.  In either case we can't be sure of its previous state.
3562306a36Sopenharmony_ci *
3662306a36Sopenharmony_ci * Returns: 1 if the controller was reset, 0 otherwise.
3762306a36Sopenharmony_ci */
3862306a36Sopenharmony_cistatic int uhci_pci_check_and_reset_hc(struct uhci_hcd *uhci)
3962306a36Sopenharmony_ci{
4062306a36Sopenharmony_ci	return uhci_check_and_reset_hc(to_pci_dev(uhci_dev(uhci)),
4162306a36Sopenharmony_ci				uhci->io_addr);
4262306a36Sopenharmony_ci}
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci/*
4562306a36Sopenharmony_ci * Store the basic register settings needed by the controller.
4662306a36Sopenharmony_ci * This function is called at the end of configure_hc in uhci-hcd.c.
4762306a36Sopenharmony_ci */
4862306a36Sopenharmony_cistatic void uhci_pci_configure_hc(struct uhci_hcd *uhci)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	struct pci_dev *pdev = to_pci_dev(uhci_dev(uhci));
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	/* Enable PIRQ */
5362306a36Sopenharmony_ci	pci_write_config_word(pdev, USBLEGSUP, USBLEGSUP_DEFAULT);
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	/* Disable platform-specific non-PME# wakeup */
5662306a36Sopenharmony_ci	if (pdev->vendor == PCI_VENDOR_ID_INTEL)
5762306a36Sopenharmony_ci		pci_write_config_byte(pdev, USBRES_INTEL, 0);
5862306a36Sopenharmony_ci}
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_cistatic int uhci_pci_resume_detect_interrupts_are_broken(struct uhci_hcd *uhci)
6162306a36Sopenharmony_ci{
6262306a36Sopenharmony_ci	int port;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	switch (to_pci_dev(uhci_dev(uhci))->vendor) {
6562306a36Sopenharmony_ci	default:
6662306a36Sopenharmony_ci		break;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	case PCI_VENDOR_ID_GENESYS:
6962306a36Sopenharmony_ci		/* Genesys Logic's GL880S controllers don't generate
7062306a36Sopenharmony_ci		 * resume-detect interrupts.
7162306a36Sopenharmony_ci		 */
7262306a36Sopenharmony_ci		return 1;
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	case PCI_VENDOR_ID_INTEL:
7562306a36Sopenharmony_ci		/* Some of Intel's USB controllers have a bug that causes
7662306a36Sopenharmony_ci		 * resume-detect interrupts if any port has an over-current
7762306a36Sopenharmony_ci		 * condition.  To make matters worse, some motherboards
7862306a36Sopenharmony_ci		 * hardwire unused USB ports' over-current inputs active!
7962306a36Sopenharmony_ci		 * To prevent problems, we will not enable resume-detect
8062306a36Sopenharmony_ci		 * interrupts if any ports are OC.
8162306a36Sopenharmony_ci		 */
8262306a36Sopenharmony_ci		for (port = 0; port < uhci->rh_numports; ++port) {
8362306a36Sopenharmony_ci			if (inw(uhci->io_addr + USBPORTSC1 + port * 2) &
8462306a36Sopenharmony_ci					USBPORTSC_OC)
8562306a36Sopenharmony_ci				return 1;
8662306a36Sopenharmony_ci		}
8762306a36Sopenharmony_ci		break;
8862306a36Sopenharmony_ci	}
8962306a36Sopenharmony_ci	return 0;
9062306a36Sopenharmony_ci}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_cistatic int uhci_pci_global_suspend_mode_is_broken(struct uhci_hcd *uhci)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	int port;
9562306a36Sopenharmony_ci	const char *sys_info;
9662306a36Sopenharmony_ci	static const char bad_Asus_board[] = "A7V8X";
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	/* One of Asus's motherboards has a bug which causes it to
9962306a36Sopenharmony_ci	 * wake up immediately from suspend-to-RAM if any of the ports
10062306a36Sopenharmony_ci	 * are connected.  In such cases we will not set EGSM.
10162306a36Sopenharmony_ci	 */
10262306a36Sopenharmony_ci	sys_info = dmi_get_system_info(DMI_BOARD_NAME);
10362306a36Sopenharmony_ci	if (sys_info && !strcmp(sys_info, bad_Asus_board)) {
10462306a36Sopenharmony_ci		for (port = 0; port < uhci->rh_numports; ++port) {
10562306a36Sopenharmony_ci			if (inw(uhci->io_addr + USBPORTSC1 + port * 2) &
10662306a36Sopenharmony_ci					USBPORTSC_CCS)
10762306a36Sopenharmony_ci				return 1;
10862306a36Sopenharmony_ci		}
10962306a36Sopenharmony_ci	}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	return 0;
11262306a36Sopenharmony_ci}
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_cistatic int uhci_pci_init(struct usb_hcd *hcd)
11562306a36Sopenharmony_ci{
11662306a36Sopenharmony_ci	struct uhci_hcd *uhci = hcd_to_uhci(hcd);
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	uhci->io_addr = (unsigned long) hcd->rsrc_start;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	uhci->rh_numports = uhci_count_ports(hcd);
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	/*
12362306a36Sopenharmony_ci	 * Intel controllers report the OverCurrent bit active on.  VIA
12462306a36Sopenharmony_ci	 * and ZHAOXIN controllers report it active off, so we'll adjust
12562306a36Sopenharmony_ci	 * the bit value.  (It's not standardized in the UHCI spec.)
12662306a36Sopenharmony_ci	 */
12762306a36Sopenharmony_ci	if (to_pci_dev(uhci_dev(uhci))->vendor == PCI_VENDOR_ID_VIA ||
12862306a36Sopenharmony_ci			to_pci_dev(uhci_dev(uhci))->vendor == PCI_VENDOR_ID_ZHAOXIN)
12962306a36Sopenharmony_ci		uhci->oc_low = 1;
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	/* HP's server management chip requires a longer port reset delay. */
13262306a36Sopenharmony_ci	if (to_pci_dev(uhci_dev(uhci))->vendor == PCI_VENDOR_ID_HP)
13362306a36Sopenharmony_ci		uhci->wait_for_hp = 1;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	/* Intel controllers use non-PME wakeup signalling */
13662306a36Sopenharmony_ci	if (to_pci_dev(uhci_dev(uhci))->vendor == PCI_VENDOR_ID_INTEL)
13762306a36Sopenharmony_ci		device_set_wakeup_capable(uhci_dev(uhci), true);
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	/* Set up pointers to PCI-specific functions */
14062306a36Sopenharmony_ci	uhci->reset_hc = uhci_pci_reset_hc;
14162306a36Sopenharmony_ci	uhci->check_and_reset_hc = uhci_pci_check_and_reset_hc;
14262306a36Sopenharmony_ci	uhci->configure_hc = uhci_pci_configure_hc;
14362306a36Sopenharmony_ci	uhci->resume_detect_interrupts_are_broken =
14462306a36Sopenharmony_ci		uhci_pci_resume_detect_interrupts_are_broken;
14562306a36Sopenharmony_ci	uhci->global_suspend_mode_is_broken =
14662306a36Sopenharmony_ci		uhci_pci_global_suspend_mode_is_broken;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	/* Kick BIOS off this hardware and reset if the controller
15062306a36Sopenharmony_ci	 * isn't already safely quiescent.
15162306a36Sopenharmony_ci	 */
15262306a36Sopenharmony_ci	check_and_reset_hc(uhci);
15362306a36Sopenharmony_ci	return 0;
15462306a36Sopenharmony_ci}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci/* Make sure the controller is quiescent and that we're not using it
15762306a36Sopenharmony_ci * any more.  This is mainly for the benefit of programs which, like kexec,
15862306a36Sopenharmony_ci * expect the hardware to be idle: not doing DMA or generating IRQs.
15962306a36Sopenharmony_ci *
16062306a36Sopenharmony_ci * This routine may be called in a damaged or failing kernel.  Hence we
16162306a36Sopenharmony_ci * do not acquire the spinlock before shutting down the controller.
16262306a36Sopenharmony_ci */
16362306a36Sopenharmony_cistatic void uhci_shutdown(struct pci_dev *pdev)
16462306a36Sopenharmony_ci{
16562306a36Sopenharmony_ci	struct usb_hcd *hcd = pci_get_drvdata(pdev);
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	uhci_hc_died(hcd_to_uhci(hcd));
16862306a36Sopenharmony_ci}
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci#ifdef CONFIG_PM
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_cistatic int uhci_pci_resume(struct usb_hcd *hcd, pm_message_t state);
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_cistatic int uhci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup)
17562306a36Sopenharmony_ci{
17662306a36Sopenharmony_ci	struct uhci_hcd *uhci = hcd_to_uhci(hcd);
17762306a36Sopenharmony_ci	struct pci_dev *pdev = to_pci_dev(uhci_dev(uhci));
17862306a36Sopenharmony_ci	int rc = 0;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	dev_dbg(uhci_dev(uhci), "%s\n", __func__);
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	spin_lock_irq(&uhci->lock);
18362306a36Sopenharmony_ci	if (!HCD_HW_ACCESSIBLE(hcd) || uhci->dead)
18462306a36Sopenharmony_ci		goto done_okay;		/* Already suspended or dead */
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	/* All PCI host controllers are required to disable IRQ generation
18762306a36Sopenharmony_ci	 * at the source, so we must turn off PIRQ.
18862306a36Sopenharmony_ci	 */
18962306a36Sopenharmony_ci	pci_write_config_word(pdev, USBLEGSUP, 0);
19062306a36Sopenharmony_ci	clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	/* Enable platform-specific non-PME# wakeup */
19362306a36Sopenharmony_ci	if (do_wakeup) {
19462306a36Sopenharmony_ci		if (pdev->vendor == PCI_VENDOR_ID_INTEL)
19562306a36Sopenharmony_ci			pci_write_config_byte(pdev, USBRES_INTEL,
19662306a36Sopenharmony_ci					USBPORT1EN | USBPORT2EN);
19762306a36Sopenharmony_ci	}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_cidone_okay:
20062306a36Sopenharmony_ci	clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
20162306a36Sopenharmony_ci	spin_unlock_irq(&uhci->lock);
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	synchronize_irq(hcd->irq);
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	/* Check for race with a wakeup request */
20662306a36Sopenharmony_ci	if (do_wakeup && HCD_WAKEUP_PENDING(hcd)) {
20762306a36Sopenharmony_ci		uhci_pci_resume(hcd, PMSG_SUSPEND);
20862306a36Sopenharmony_ci		rc = -EBUSY;
20962306a36Sopenharmony_ci	}
21062306a36Sopenharmony_ci	return rc;
21162306a36Sopenharmony_ci}
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_cistatic int uhci_pci_resume(struct usb_hcd *hcd, pm_message_t msg)
21462306a36Sopenharmony_ci{
21562306a36Sopenharmony_ci	bool hibernated = (msg.event == PM_EVENT_RESTORE);
21662306a36Sopenharmony_ci	struct uhci_hcd *uhci = hcd_to_uhci(hcd);
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	dev_dbg(uhci_dev(uhci), "%s\n", __func__);
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	/* Since we aren't in D3 any more, it's safe to set this flag
22162306a36Sopenharmony_ci	 * even if the controller was dead.
22262306a36Sopenharmony_ci	 */
22362306a36Sopenharmony_ci	set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	spin_lock_irq(&uhci->lock);
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	/* Make sure resume from hibernation re-enumerates everything */
22862306a36Sopenharmony_ci	if (hibernated) {
22962306a36Sopenharmony_ci		uhci->reset_hc(uhci);
23062306a36Sopenharmony_ci		finish_reset(uhci);
23162306a36Sopenharmony_ci	}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	/* The firmware may have changed the controller settings during
23462306a36Sopenharmony_ci	 * a system wakeup.  Check it and reconfigure to avoid problems.
23562306a36Sopenharmony_ci	 */
23662306a36Sopenharmony_ci	else {
23762306a36Sopenharmony_ci		check_and_reset_hc(uhci);
23862306a36Sopenharmony_ci	}
23962306a36Sopenharmony_ci	configure_hc(uhci);
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	/* Tell the core if the controller had to be reset */
24262306a36Sopenharmony_ci	if (uhci->rh_state == UHCI_RH_RESET)
24362306a36Sopenharmony_ci		usb_root_hub_lost_power(hcd->self.root_hub);
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	spin_unlock_irq(&uhci->lock);
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	/* If interrupts don't work and remote wakeup is enabled then
24862306a36Sopenharmony_ci	 * the suspended root hub needs to be polled.
24962306a36Sopenharmony_ci	 */
25062306a36Sopenharmony_ci	if (!uhci->RD_enable && hcd->self.root_hub->do_remote_wakeup)
25162306a36Sopenharmony_ci		set_bit(HCD_FLAG_POLL_RH, &hcd->flags);
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	/* Does the root hub have a port wakeup pending? */
25462306a36Sopenharmony_ci	usb_hcd_poll_rh_status(hcd);
25562306a36Sopenharmony_ci	return 0;
25662306a36Sopenharmony_ci}
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci#endif
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_cistatic const struct hc_driver uhci_driver = {
26162306a36Sopenharmony_ci	.description =		hcd_name,
26262306a36Sopenharmony_ci	.product_desc =		"UHCI Host Controller",
26362306a36Sopenharmony_ci	.hcd_priv_size =	sizeof(struct uhci_hcd),
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	/* Generic hardware linkage */
26662306a36Sopenharmony_ci	.irq =			uhci_irq,
26762306a36Sopenharmony_ci	.flags =		HCD_DMA | HCD_USB11,
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	/* Basic lifecycle operations */
27062306a36Sopenharmony_ci	.reset =		uhci_pci_init,
27162306a36Sopenharmony_ci	.start =		uhci_start,
27262306a36Sopenharmony_ci#ifdef CONFIG_PM
27362306a36Sopenharmony_ci	.pci_suspend =		uhci_pci_suspend,
27462306a36Sopenharmony_ci	.pci_resume =		uhci_pci_resume,
27562306a36Sopenharmony_ci	.bus_suspend =		uhci_rh_suspend,
27662306a36Sopenharmony_ci	.bus_resume =		uhci_rh_resume,
27762306a36Sopenharmony_ci#endif
27862306a36Sopenharmony_ci	.stop =			uhci_stop,
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	.urb_enqueue =		uhci_urb_enqueue,
28162306a36Sopenharmony_ci	.urb_dequeue =		uhci_urb_dequeue,
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	.endpoint_disable =	uhci_hcd_endpoint_disable,
28462306a36Sopenharmony_ci	.get_frame_number =	uhci_hcd_get_frame_number,
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	.hub_status_data =	uhci_hub_status_data,
28762306a36Sopenharmony_ci	.hub_control =		uhci_hub_control,
28862306a36Sopenharmony_ci};
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_cistatic const struct pci_device_id uhci_pci_ids[] = { {
29162306a36Sopenharmony_ci	/* handle any USB UHCI controller */
29262306a36Sopenharmony_ci	PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_USB_UHCI, ~0),
29362306a36Sopenharmony_ci	}, { /* end: all zeroes */ }
29462306a36Sopenharmony_ci};
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, uhci_pci_ids);
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_cistatic int uhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
29962306a36Sopenharmony_ci{
30062306a36Sopenharmony_ci	return usb_hcd_pci_probe(dev, &uhci_driver);
30162306a36Sopenharmony_ci}
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_cistatic struct pci_driver uhci_pci_driver = {
30462306a36Sopenharmony_ci	.name =		hcd_name,
30562306a36Sopenharmony_ci	.id_table =	uhci_pci_ids,
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	.probe =	uhci_pci_probe,
30862306a36Sopenharmony_ci	.remove =	usb_hcd_pci_remove,
30962306a36Sopenharmony_ci	.shutdown =	uhci_shutdown,
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci#ifdef CONFIG_PM
31262306a36Sopenharmony_ci	.driver =	{
31362306a36Sopenharmony_ci		.pm =	&usb_hcd_pci_pm_ops
31462306a36Sopenharmony_ci	},
31562306a36Sopenharmony_ci#endif
31662306a36Sopenharmony_ci};
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ciMODULE_SOFTDEP("pre: ehci_pci");
319