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