162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-1.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * OHCI HCD (Host Controller Driver) for USB.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
662306a36Sopenharmony_ci * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
762306a36Sopenharmony_ci * (C) Copyright 2002 Hewlett-Packard Company
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * SA1111 Bus Glue
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci * Written by Christopher Hoover <ch@hpl.hp.com>
1262306a36Sopenharmony_ci * Based on fragments of previous driver by Russell King et al.
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci * This file is licenced under the GPL.
1562306a36Sopenharmony_ci */
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include <asm/mach-types.h>
1862306a36Sopenharmony_ci#include <asm/hardware/sa1111.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#ifndef CONFIG_SA1111
2162306a36Sopenharmony_ci#error "This file is SA-1111 bus glue.  CONFIG_SA1111 must be defined."
2262306a36Sopenharmony_ci#endif
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#define USB_STATUS	0x0118
2562306a36Sopenharmony_ci#define USB_RESET	0x011c
2662306a36Sopenharmony_ci#define USB_IRQTEST	0x0120
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#define USB_RESET_FORCEIFRESET	(1 << 0)
2962306a36Sopenharmony_ci#define USB_RESET_FORCEHCRESET	(1 << 1)
3062306a36Sopenharmony_ci#define USB_RESET_CLKGENRESET	(1 << 2)
3162306a36Sopenharmony_ci#define USB_RESET_SIMSCALEDOWN	(1 << 3)
3262306a36Sopenharmony_ci#define USB_RESET_USBINTTEST	(1 << 4)
3362306a36Sopenharmony_ci#define USB_RESET_SLEEPSTBYEN	(1 << 5)
3462306a36Sopenharmony_ci#define USB_RESET_PWRSENSELOW	(1 << 6)
3562306a36Sopenharmony_ci#define USB_RESET_PWRCTRLLOW	(1 << 7)
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#define USB_STATUS_IRQHCIRMTWKUP  (1 <<  7)
3862306a36Sopenharmony_ci#define USB_STATUS_IRQHCIBUFFACC  (1 <<  8)
3962306a36Sopenharmony_ci#define USB_STATUS_NIRQHCIM       (1 <<  9)
4062306a36Sopenharmony_ci#define USB_STATUS_NHCIMFCLR      (1 << 10)
4162306a36Sopenharmony_ci#define USB_STATUS_USBPWRSENSE    (1 << 11)
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci#if 0
4462306a36Sopenharmony_cistatic void dump_hci_status(struct usb_hcd *hcd, const char *label)
4562306a36Sopenharmony_ci{
4662306a36Sopenharmony_ci	unsigned long status = readl_relaxed(hcd->regs + USB_STATUS);
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	printk(KERN_DEBUG "%s USB_STATUS = { %s%s%s%s%s}\n", label,
4962306a36Sopenharmony_ci	     ((status & USB_STATUS_IRQHCIRMTWKUP) ? "IRQHCIRMTWKUP " : ""),
5062306a36Sopenharmony_ci	     ((status & USB_STATUS_IRQHCIBUFFACC) ? "IRQHCIBUFFACC " : ""),
5162306a36Sopenharmony_ci	     ((status & USB_STATUS_NIRQHCIM) ? "" : "IRQHCIM "),
5262306a36Sopenharmony_ci	     ((status & USB_STATUS_NHCIMFCLR) ? "" : "HCIMFCLR "),
5362306a36Sopenharmony_ci	     ((status & USB_STATUS_USBPWRSENSE) ? "USBPWRSENSE " : ""));
5462306a36Sopenharmony_ci}
5562306a36Sopenharmony_ci#endif
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistatic int ohci_sa1111_reset(struct usb_hcd *hcd)
5862306a36Sopenharmony_ci{
5962306a36Sopenharmony_ci	struct ohci_hcd *ohci = hcd_to_ohci(hcd);
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	ohci_hcd_init(ohci);
6262306a36Sopenharmony_ci	return ohci_init(ohci);
6362306a36Sopenharmony_ci}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistatic int ohci_sa1111_start(struct usb_hcd *hcd)
6662306a36Sopenharmony_ci{
6762306a36Sopenharmony_ci	struct ohci_hcd	*ohci = hcd_to_ohci(hcd);
6862306a36Sopenharmony_ci	int ret;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	ret = ohci_run(ohci);
7162306a36Sopenharmony_ci	if (ret < 0) {
7262306a36Sopenharmony_ci		ohci_err(ohci, "can't start\n");
7362306a36Sopenharmony_ci		ohci_stop(hcd);
7462306a36Sopenharmony_ci	}
7562306a36Sopenharmony_ci	return ret;
7662306a36Sopenharmony_ci}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_cistatic const struct hc_driver ohci_sa1111_hc_driver = {
7962306a36Sopenharmony_ci	.description =		hcd_name,
8062306a36Sopenharmony_ci	.product_desc =		"SA-1111 OHCI",
8162306a36Sopenharmony_ci	.hcd_priv_size =	sizeof(struct ohci_hcd),
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	/*
8462306a36Sopenharmony_ci	 * generic hardware linkage
8562306a36Sopenharmony_ci	 */
8662306a36Sopenharmony_ci	.irq =			ohci_irq,
8762306a36Sopenharmony_ci	.flags =		HCD_USB11 | HCD_DMA | HCD_MEMORY,
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	/*
9062306a36Sopenharmony_ci	 * basic lifecycle operations
9162306a36Sopenharmony_ci	 */
9262306a36Sopenharmony_ci	.reset =		ohci_sa1111_reset,
9362306a36Sopenharmony_ci	.start =		ohci_sa1111_start,
9462306a36Sopenharmony_ci	.stop =			ohci_stop,
9562306a36Sopenharmony_ci	.shutdown =		ohci_shutdown,
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	/*
9862306a36Sopenharmony_ci	 * managing i/o requests and associated device resources
9962306a36Sopenharmony_ci	 */
10062306a36Sopenharmony_ci	.urb_enqueue =		ohci_urb_enqueue,
10162306a36Sopenharmony_ci	.urb_dequeue =		ohci_urb_dequeue,
10262306a36Sopenharmony_ci	.endpoint_disable =	ohci_endpoint_disable,
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	/*
10562306a36Sopenharmony_ci	 * scheduling support
10662306a36Sopenharmony_ci	 */
10762306a36Sopenharmony_ci	.get_frame_number =	ohci_get_frame,
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	/*
11062306a36Sopenharmony_ci	 * root hub support
11162306a36Sopenharmony_ci	 */
11262306a36Sopenharmony_ci	.hub_status_data =	ohci_hub_status_data,
11362306a36Sopenharmony_ci	.hub_control =		ohci_hub_control,
11462306a36Sopenharmony_ci#ifdef	CONFIG_PM
11562306a36Sopenharmony_ci	.bus_suspend =		ohci_bus_suspend,
11662306a36Sopenharmony_ci	.bus_resume =		ohci_bus_resume,
11762306a36Sopenharmony_ci#endif
11862306a36Sopenharmony_ci	.start_port_reset =	ohci_start_port_reset,
11962306a36Sopenharmony_ci};
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_cistatic int sa1111_start_hc(struct sa1111_dev *dev)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	unsigned int usb_rst = 0;
12462306a36Sopenharmony_ci	int ret;
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	dev_dbg(&dev->dev, "starting SA-1111 OHCI USB Controller\n");
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	if (machine_is_assabet())
12962306a36Sopenharmony_ci		usb_rst = USB_RESET_PWRSENSELOW | USB_RESET_PWRCTRLLOW;
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	/*
13262306a36Sopenharmony_ci	 * Configure the power sense and control lines.  Place the USB
13362306a36Sopenharmony_ci	 * host controller in reset.
13462306a36Sopenharmony_ci	 */
13562306a36Sopenharmony_ci	writel_relaxed(usb_rst | USB_RESET_FORCEIFRESET | USB_RESET_FORCEHCRESET,
13662306a36Sopenharmony_ci		      dev->mapbase + USB_RESET);
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	/*
13962306a36Sopenharmony_ci	 * Now, carefully enable the USB clock, and take
14062306a36Sopenharmony_ci	 * the USB host controller out of reset.
14162306a36Sopenharmony_ci	 */
14262306a36Sopenharmony_ci	ret = sa1111_enable_device(dev);
14362306a36Sopenharmony_ci	if (ret == 0) {
14462306a36Sopenharmony_ci		udelay(11);
14562306a36Sopenharmony_ci		writel_relaxed(usb_rst, dev->mapbase + USB_RESET);
14662306a36Sopenharmony_ci	}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	return ret;
14962306a36Sopenharmony_ci}
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_cistatic void sa1111_stop_hc(struct sa1111_dev *dev)
15262306a36Sopenharmony_ci{
15362306a36Sopenharmony_ci	unsigned int usb_rst;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	dev_dbg(&dev->dev, "stopping SA-1111 OHCI USB Controller\n");
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	/*
15862306a36Sopenharmony_ci	 * Put the USB host controller into reset.
15962306a36Sopenharmony_ci	 */
16062306a36Sopenharmony_ci	usb_rst = readl_relaxed(dev->mapbase + USB_RESET);
16162306a36Sopenharmony_ci	writel_relaxed(usb_rst | USB_RESET_FORCEIFRESET | USB_RESET_FORCEHCRESET,
16262306a36Sopenharmony_ci		      dev->mapbase + USB_RESET);
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	/*
16562306a36Sopenharmony_ci	 * Stop the USB clock.
16662306a36Sopenharmony_ci	 */
16762306a36Sopenharmony_ci	sa1111_disable_device(dev);
16862306a36Sopenharmony_ci}
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci/**
17162306a36Sopenharmony_ci * ohci_hcd_sa1111_probe - initialize SA-1111-based HCDs
17262306a36Sopenharmony_ci *
17362306a36Sopenharmony_ci * Allocates basic resources for this USB host controller, and
17462306a36Sopenharmony_ci * then invokes the start() method for the HCD associated with it.
17562306a36Sopenharmony_ci */
17662306a36Sopenharmony_cistatic int ohci_hcd_sa1111_probe(struct sa1111_dev *dev)
17762306a36Sopenharmony_ci{
17862306a36Sopenharmony_ci	struct usb_hcd *hcd;
17962306a36Sopenharmony_ci	int ret, irq;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	if (usb_disabled())
18262306a36Sopenharmony_ci		return -ENODEV;
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	/*
18562306a36Sopenharmony_ci	 * We don't call dma_set_mask_and_coherent() here because the
18662306a36Sopenharmony_ci	 * DMA mask has already been appropraitely setup by the core
18762306a36Sopenharmony_ci	 * SA-1111 bus code (which includes bug workarounds.)
18862306a36Sopenharmony_ci	 */
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	hcd = usb_create_hcd(&ohci_sa1111_hc_driver, &dev->dev, "sa1111");
19162306a36Sopenharmony_ci	if (!hcd)
19262306a36Sopenharmony_ci		return -ENOMEM;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	hcd->rsrc_start = dev->res.start;
19562306a36Sopenharmony_ci	hcd->rsrc_len = resource_size(&dev->res);
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	irq = sa1111_get_irq(dev, 1);
19862306a36Sopenharmony_ci	if (irq <= 0) {
19962306a36Sopenharmony_ci		ret = irq ? : -ENXIO;
20062306a36Sopenharmony_ci		goto err1;
20162306a36Sopenharmony_ci	}
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	/*
20462306a36Sopenharmony_ci	 * According to the "Intel StrongARM SA-1111 Microprocessor Companion
20562306a36Sopenharmony_ci	 * Chip Specification Update" (June 2000), erratum #7, there is a
20662306a36Sopenharmony_ci	 * significant bug in the SA1111 SDRAM shared memory controller.  If
20762306a36Sopenharmony_ci	 * an access to a region of memory above 1MB relative to the bank base,
20862306a36Sopenharmony_ci	 * it is important that address bit 10 _NOT_ be asserted. Depending
20962306a36Sopenharmony_ci	 * on the configuration of the RAM, bit 10 may correspond to one
21062306a36Sopenharmony_ci	 * of several different (processor-relative) address bits.
21162306a36Sopenharmony_ci	 *
21262306a36Sopenharmony_ci	 * Section 4.6 of the "Intel StrongARM SA-1111 Development Module
21362306a36Sopenharmony_ci	 * User's Guide" mentions that jumpers R51 and R52 control the
21462306a36Sopenharmony_ci	 * target of SA-1111 DMA (either SDRAM bank 0 on Assabet, or
21562306a36Sopenharmony_ci	 * SDRAM bank 1 on Neponset). The default configuration selects
21662306a36Sopenharmony_ci	 * Assabet, so any address in bank 1 is necessarily invalid.
21762306a36Sopenharmony_ci	 *
21862306a36Sopenharmony_ci	 * As a workaround, use a bounce buffer in addressable memory
21962306a36Sopenharmony_ci	 * as local_mem, relying on ZONE_DMA to provide an area that
22062306a36Sopenharmony_ci	 * fits within the above constraints.
22162306a36Sopenharmony_ci	 *
22262306a36Sopenharmony_ci	 * SZ_64K is an estimate for what size this might need.
22362306a36Sopenharmony_ci	 */
22462306a36Sopenharmony_ci	ret = usb_hcd_setup_local_mem(hcd, 0, 0, SZ_64K);
22562306a36Sopenharmony_ci	if (ret)
22662306a36Sopenharmony_ci		goto err1;
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
22962306a36Sopenharmony_ci		dev_dbg(&dev->dev, "request_mem_region failed\n");
23062306a36Sopenharmony_ci		ret = -EBUSY;
23162306a36Sopenharmony_ci		goto err1;
23262306a36Sopenharmony_ci	}
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	hcd->regs = dev->mapbase;
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	ret = sa1111_start_hc(dev);
23762306a36Sopenharmony_ci	if (ret)
23862306a36Sopenharmony_ci		goto err2;
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	ret = usb_add_hcd(hcd, irq, 0);
24162306a36Sopenharmony_ci	if (ret == 0) {
24262306a36Sopenharmony_ci		device_wakeup_enable(hcd->self.controller);
24362306a36Sopenharmony_ci		return ret;
24462306a36Sopenharmony_ci	}
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	sa1111_stop_hc(dev);
24762306a36Sopenharmony_ci err2:
24862306a36Sopenharmony_ci	release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
24962306a36Sopenharmony_ci err1:
25062306a36Sopenharmony_ci	usb_put_hcd(hcd);
25162306a36Sopenharmony_ci	return ret;
25262306a36Sopenharmony_ci}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci/**
25562306a36Sopenharmony_ci * ohci_hcd_sa1111_remove - shutdown processing for SA-1111-based HCDs
25662306a36Sopenharmony_ci * @dev: USB Host Controller being removed
25762306a36Sopenharmony_ci *
25862306a36Sopenharmony_ci * Reverses the effect of ohci_hcd_sa1111_probe(), first invoking
25962306a36Sopenharmony_ci * the HCD's stop() method.
26062306a36Sopenharmony_ci */
26162306a36Sopenharmony_cistatic void ohci_hcd_sa1111_remove(struct sa1111_dev *dev)
26262306a36Sopenharmony_ci{
26362306a36Sopenharmony_ci	struct usb_hcd *hcd = sa1111_get_drvdata(dev);
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	usb_remove_hcd(hcd);
26662306a36Sopenharmony_ci	sa1111_stop_hc(dev);
26762306a36Sopenharmony_ci	release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
26862306a36Sopenharmony_ci	usb_put_hcd(hcd);
26962306a36Sopenharmony_ci}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_cistatic void ohci_hcd_sa1111_shutdown(struct device *_dev)
27262306a36Sopenharmony_ci{
27362306a36Sopenharmony_ci	struct sa1111_dev *dev = to_sa1111_device(_dev);
27462306a36Sopenharmony_ci	struct usb_hcd *hcd = sa1111_get_drvdata(dev);
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	if (test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) {
27762306a36Sopenharmony_ci		hcd->driver->shutdown(hcd);
27862306a36Sopenharmony_ci		sa1111_stop_hc(dev);
27962306a36Sopenharmony_ci	}
28062306a36Sopenharmony_ci}
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_cistatic struct sa1111_driver ohci_hcd_sa1111_driver = {
28362306a36Sopenharmony_ci	.drv = {
28462306a36Sopenharmony_ci		.name	= "sa1111-ohci",
28562306a36Sopenharmony_ci		.owner	= THIS_MODULE,
28662306a36Sopenharmony_ci		.shutdown = ohci_hcd_sa1111_shutdown,
28762306a36Sopenharmony_ci	},
28862306a36Sopenharmony_ci	.devid		= SA1111_DEVID_USB,
28962306a36Sopenharmony_ci	.probe		= ohci_hcd_sa1111_probe,
29062306a36Sopenharmony_ci	.remove		= ohci_hcd_sa1111_remove,
29162306a36Sopenharmony_ci};
292