ehci-spear.c revision ab1f046a1908d60d2bad20c3ac0f9e71db72318f
1c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri/* 2c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri* Driver for EHCI HCD on SPEAR SOC 3c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri* 4c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri* Copyright (C) 2010 ST Micro Electronics, 5c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri* Deepak Sikri <deepak.sikri@st.com> 6c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri* 7c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri* Based on various ehci-*.c drivers 8c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri* 9c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri* This file is subject to the terms and conditions of the GNU General Public 10c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri* License. See the file COPYING in the main directory of this archive for 11c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri* more details. 12c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri*/ 13c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri 14c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri#include <linux/clk.h> 158c1b369394d188afb954ef470d90ac887438530cDeepak Sikri#include <linux/jiffies.h> 1656fafb94f64efaca625206a3876432b96558dcb0Stefan Roese#include <linux/of.h> 178c1b369394d188afb954ef470d90ac887438530cDeepak Sikri#include <linux/platform_device.h> 188c1b369394d188afb954ef470d90ac887438530cDeepak Sikri#include <linux/pm.h> 19c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri 20c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikristruct spear_ehci { 21c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri struct ehci_hcd ehci; 22c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri struct clk *clk; 23c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri}; 24c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri 25c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri#define to_spear_ehci(hcd) (struct spear_ehci *)hcd_to_ehci(hcd) 26c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri 27c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikristatic void spear_start_ehci(struct spear_ehci *ehci) 28c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri{ 2915c9d50bbb4f754c02493fa880658d6bd5b5c27dViresh Kumar clk_prepare_enable(ehci->clk); 30c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri} 31c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri 32c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikristatic void spear_stop_ehci(struct spear_ehci *ehci) 33c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri{ 3415c9d50bbb4f754c02493fa880658d6bd5b5c27dViresh Kumar clk_disable_unprepare(ehci->clk); 35c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri} 36c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri 37c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikristatic int ehci_spear_setup(struct usb_hcd *hcd) 38c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri{ 39c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri struct ehci_hcd *ehci = hcd_to_ehci(hcd); 40c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri 41c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri /* registers start at offset 0x0 */ 42c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri ehci->caps = hcd->regs; 43c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri 44c73cee717e7d5da0698acb720ad1219646fe4f46Alan Stern return ehci_setup(hcd); 45c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri} 46c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri 47c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikristatic const struct hc_driver ehci_spear_hc_driver = { 48c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri .description = hcd_name, 49c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri .product_desc = "SPEAr EHCI", 50c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri .hcd_priv_size = sizeof(struct spear_ehci), 51c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri 52c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri /* generic hardware linkage */ 53c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri .irq = ehci_irq, 54c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri .flags = HCD_MEMORY | HCD_USB2, 55c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri 56c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri /* basic lifecycle operations */ 57c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri .reset = ehci_spear_setup, 58c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri .start = ehci_run, 59c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri .stop = ehci_stop, 60c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri .shutdown = ehci_shutdown, 61c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri 62c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri /* managing i/o requests and associated device resources */ 63c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri .urb_enqueue = ehci_urb_enqueue, 64c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri .urb_dequeue = ehci_urb_dequeue, 65c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri .endpoint_disable = ehci_endpoint_disable, 66c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri .endpoint_reset = ehci_endpoint_reset, 67c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri 68c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri /* scheduling support */ 69c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri .get_frame_number = ehci_get_frame, 70c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri 71c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri /* root hub support */ 72c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri .hub_status_data = ehci_hub_status_data, 73c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri .hub_control = ehci_hub_control, 74c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri .bus_suspend = ehci_bus_suspend, 75c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri .bus_resume = ehci_bus_resume, 76c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri .relinquish_port = ehci_relinquish_port, 77c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri .port_handed_over = ehci_port_handed_over, 78c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, 79c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri}; 80c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri 81ab1f046a1908d60d2bad20c3ac0f9e71db72318fJingoo Han#ifdef CONFIG_PM_SLEEP 828c1b369394d188afb954ef470d90ac887438530cDeepak Sikristatic int ehci_spear_drv_suspend(struct device *dev) 838c1b369394d188afb954ef470d90ac887438530cDeepak Sikri{ 848c1b369394d188afb954ef470d90ac887438530cDeepak Sikri struct usb_hcd *hcd = dev_get_drvdata(dev); 85c5cf9212a368d88fe1e25797699b167f6daa64a5Alan Stern bool do_wakeup = device_may_wakeup(dev); 86c5cf9212a368d88fe1e25797699b167f6daa64a5Alan Stern 87c5cf9212a368d88fe1e25797699b167f6daa64a5Alan Stern return ehci_suspend(hcd, do_wakeup); 888c1b369394d188afb954ef470d90ac887438530cDeepak Sikri} 898c1b369394d188afb954ef470d90ac887438530cDeepak Sikri 908c1b369394d188afb954ef470d90ac887438530cDeepak Sikristatic int ehci_spear_drv_resume(struct device *dev) 918c1b369394d188afb954ef470d90ac887438530cDeepak Sikri{ 928c1b369394d188afb954ef470d90ac887438530cDeepak Sikri struct usb_hcd *hcd = dev_get_drvdata(dev); 938c1b369394d188afb954ef470d90ac887438530cDeepak Sikri 94c5cf9212a368d88fe1e25797699b167f6daa64a5Alan Stern ehci_resume(hcd, false); 958c1b369394d188afb954ef470d90ac887438530cDeepak Sikri return 0; 968c1b369394d188afb954ef470d90ac887438530cDeepak Sikri} 97ab1f046a1908d60d2bad20c3ac0f9e71db72318fJingoo Han#endif /* CONFIG_PM_SLEEP */ 988c1b369394d188afb954ef470d90ac887438530cDeepak Sikri 998c1b369394d188afb954ef470d90ac887438530cDeepak Sikristatic SIMPLE_DEV_PM_OPS(ehci_spear_pm_ops, ehci_spear_drv_suspend, 1008c1b369394d188afb954ef470d90ac887438530cDeepak Sikri ehci_spear_drv_resume); 1018c1b369394d188afb954ef470d90ac887438530cDeepak Sikri 10256fafb94f64efaca625206a3876432b96558dcb0Stefan Roesestatic u64 spear_ehci_dma_mask = DMA_BIT_MASK(32); 10356fafb94f64efaca625206a3876432b96558dcb0Stefan Roese 104c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikristatic int spear_ehci_hcd_drv_probe(struct platform_device *pdev) 105c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri{ 106c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri struct usb_hcd *hcd ; 107c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri struct spear_ehci *ehci; 108c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri struct resource *res; 109c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri struct clk *usbh_clk; 110c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri const struct hc_driver *driver = &ehci_spear_hc_driver; 111c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri int irq, retval; 112c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri 113c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri if (usb_disabled()) 114c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri return -ENODEV; 115c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri 116c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri irq = platform_get_irq(pdev, 0); 117c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri if (irq < 0) { 118c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri retval = irq; 11998515e5923b1c8f982511eeec9d27014b05efebfViresh Kumar goto fail; 120c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri } 121c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri 12256fafb94f64efaca625206a3876432b96558dcb0Stefan Roese /* 12356fafb94f64efaca625206a3876432b96558dcb0Stefan Roese * Right now device-tree probed devices don't get dma_mask set. 12456fafb94f64efaca625206a3876432b96558dcb0Stefan Roese * Since shared usb code relies on it, set it here for now. 12556fafb94f64efaca625206a3876432b96558dcb0Stefan Roese * Once we have dma capability bindings this can go away. 12656fafb94f64efaca625206a3876432b96558dcb0Stefan Roese */ 12756fafb94f64efaca625206a3876432b96558dcb0Stefan Roese if (!pdev->dev.dma_mask) 12856fafb94f64efaca625206a3876432b96558dcb0Stefan Roese pdev->dev.dma_mask = &spear_ehci_dma_mask; 12956fafb94f64efaca625206a3876432b96558dcb0Stefan Roese 13098515e5923b1c8f982511eeec9d27014b05efebfViresh Kumar usbh_clk = devm_clk_get(&pdev->dev, NULL); 131c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri if (IS_ERR(usbh_clk)) { 132c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri dev_err(&pdev->dev, "Error getting interface clock\n"); 133c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri retval = PTR_ERR(usbh_clk); 13498515e5923b1c8f982511eeec9d27014b05efebfViresh Kumar goto fail; 135c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri } 136c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri 137c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev)); 138c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri if (!hcd) { 139c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri retval = -ENOMEM; 14098515e5923b1c8f982511eeec9d27014b05efebfViresh Kumar goto fail; 141c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri } 142c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri 143c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 144c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri if (!res) { 145c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri retval = -ENODEV; 14698515e5923b1c8f982511eeec9d27014b05efebfViresh Kumar goto err_put_hcd; 147c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri } 148c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri 149c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri hcd->rsrc_start = res->start; 150c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri hcd->rsrc_len = resource_size(res); 15198515e5923b1c8f982511eeec9d27014b05efebfViresh Kumar if (!devm_request_mem_region(&pdev->dev, hcd->rsrc_start, hcd->rsrc_len, 152c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri driver->description)) { 153c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri retval = -EBUSY; 15498515e5923b1c8f982511eeec9d27014b05efebfViresh Kumar goto err_put_hcd; 155c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri } 156c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri 15798515e5923b1c8f982511eeec9d27014b05efebfViresh Kumar hcd->regs = devm_ioremap(&pdev->dev, hcd->rsrc_start, hcd->rsrc_len); 158c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri if (hcd->regs == NULL) { 159c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri dev_dbg(&pdev->dev, "error mapping memory\n"); 160c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri retval = -ENOMEM; 16198515e5923b1c8f982511eeec9d27014b05efebfViresh Kumar goto err_put_hcd; 162c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri } 163c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri 164c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri ehci = (struct spear_ehci *)hcd_to_ehci(hcd); 165c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri ehci->clk = usbh_clk; 166c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri 167c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri spear_start_ehci(ehci); 168b5dd18d8747010e3f3eb1cc76a49f94291938559Yong Zhang retval = usb_add_hcd(hcd, irq, IRQF_SHARED); 169c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri if (retval) 17098515e5923b1c8f982511eeec9d27014b05efebfViresh Kumar goto err_stop_ehci; 171c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri 172c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri return retval; 173c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri 17498515e5923b1c8f982511eeec9d27014b05efebfViresh Kumarerr_stop_ehci: 175c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri spear_stop_ehci(ehci); 17698515e5923b1c8f982511eeec9d27014b05efebfViresh Kumarerr_put_hcd: 177c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri usb_put_hcd(hcd); 17898515e5923b1c8f982511eeec9d27014b05efebfViresh Kumarfail: 179c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri dev_err(&pdev->dev, "init fail, %d\n", retval); 180c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri 181c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri return retval ; 182c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri} 183c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri 184c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikristatic int spear_ehci_hcd_drv_remove(struct platform_device *pdev) 185c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri{ 186c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri struct usb_hcd *hcd = platform_get_drvdata(pdev); 187c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri struct spear_ehci *ehci_p = to_spear_ehci(hcd); 188c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri 189c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri if (!hcd) 190c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri return 0; 191c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri if (in_interrupt()) 192c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri BUG(); 193c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri usb_remove_hcd(hcd); 194c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri 195c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri if (ehci_p->clk) 196c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri spear_stop_ehci(ehci_p); 197c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri usb_put_hcd(hcd); 198c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri 199c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri return 0; 200c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri} 201c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri 202d3608b6dafc570bb671c3338288eb2523f8cd52aBill Pembertonstatic struct of_device_id spear_ehci_id_table[] = { 20356fafb94f64efaca625206a3876432b96558dcb0Stefan Roese { .compatible = "st,spear600-ehci", }, 20456fafb94f64efaca625206a3876432b96558dcb0Stefan Roese { }, 20556fafb94f64efaca625206a3876432b96558dcb0Stefan Roese}; 20656fafb94f64efaca625206a3876432b96558dcb0Stefan Roese 207c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikristatic struct platform_driver spear_ehci_hcd_driver = { 208c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri .probe = spear_ehci_hcd_drv_probe, 209c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri .remove = spear_ehci_hcd_drv_remove, 210c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri .shutdown = usb_hcd_platform_shutdown, 211c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri .driver = { 212c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri .name = "spear-ehci", 2138c1b369394d188afb954ef470d90ac887438530cDeepak Sikri .bus = &platform_bus_type, 2148c1b369394d188afb954ef470d90ac887438530cDeepak Sikri .pm = &ehci_spear_pm_ops, 21556fafb94f64efaca625206a3876432b96558dcb0Stefan Roese .of_match_table = of_match_ptr(spear_ehci_id_table), 216c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri } 217c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri}; 218c8c38de9d8002578599222296b90696745ac0fe3Deepak Sikri 219c8c38de9d8002578599222296b90696745ac0fe3Deepak SikriMODULE_ALIAS("platform:spear-ehci"); 220