diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2011-05-23 12:33:02 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-05-23 12:33:02 -0700 |
commit | c44dead70a841d90ddc01968012f323c33217c9e (patch) | |
tree | 85489ebe9b9a3413cd8ee197ffb40c8aa8d97e63 /drivers/usb/host/ehci-s5p.c | |
parent | 99dff5856220a02b8711f2e8746413ea6e53ccf6 (diff) | |
parent | d5f6db9e1aff6ccf1876224f152c0268b0c8a992 (diff) | |
download | linux-c44dead70a841d90ddc01968012f323c33217c9e.tar.gz linux-c44dead70a841d90ddc01968012f323c33217c9e.tar.bz2 linux-c44dead70a841d90ddc01968012f323c33217c9e.zip |
Merge branch 'usb-next' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb-2.6
* 'usb-next' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb-2.6: (205 commits)
USB: EHCI: Remove SPARC_LEON {read,write}_be definitions from ehci.h
USB: UHCI: Support big endian GRUSBHC HC
sparc: add {read,write}*_be routines
USB: UHCI: Add support for big endian descriptors
USB: UHCI: Use ACCESS_ONCE rather than using a full compiler barrier
USB: UHCI: Add support for big endian mmio
usb-storage: Correct adjust_quirks to include latest flags
usb/isp1760: Fix possible unlink problems
usb/isp1760: Move function isp1760_endpoint_disable() within file.
USB: remove remaining usages of hcd->state from usbcore and fix regression
usb: musb: ux500: add configuration and build options for ux500 dma
usb: musb: ux500: add dma glue layer for ux500
usb: musb: ux500: add dma name for ux500
usb: musb: ux500: add ux500 specific code for gadget side
usb: musb: fix compile error
usb-storage: fix up the unusual_realtek device list
USB: gadget: f_audio: Fix invalid dereference of initdata
EHCI: don't rescan interrupt QHs needlessly
OHCI: fix regression caused by nVidia shutdown workaround
USB: OTG: msm: Free VCCCX regulator even if we can't set the voltage
...
Diffstat (limited to 'drivers/usb/host/ehci-s5p.c')
-rw-r--r-- | drivers/usb/host/ehci-s5p.c | 202 |
1 files changed, 202 insertions, 0 deletions
diff --git a/drivers/usb/host/ehci-s5p.c b/drivers/usb/host/ehci-s5p.c new file mode 100644 index 000000000000..e3374c8f7b3f --- /dev/null +++ b/drivers/usb/host/ehci-s5p.c @@ -0,0 +1,202 @@ +/* + * SAMSUNG S5P USB HOST EHCI Controller + * + * Copyright (C) 2011 Samsung Electronics Co.Ltd + * Author: Jingoo Han <jg1.han@samsung.com> + * Author: Joonyoung Shim <jy0922.shim@samsung.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include <linux/clk.h> +#include <linux/platform_device.h> +#include <mach/regs-pmu.h> +#include <plat/cpu.h> +#include <plat/ehci.h> +#include <plat/usb-phy.h> + +struct s5p_ehci_hcd { + struct device *dev; + struct usb_hcd *hcd; + struct clk *clk; +}; + +static const struct hc_driver s5p_ehci_hc_driver = { + .description = hcd_name, + .product_desc = "S5P EHCI Host Controller", + .hcd_priv_size = sizeof(struct ehci_hcd), + + .irq = ehci_irq, + .flags = HCD_MEMORY | HCD_USB2, + + .reset = ehci_init, + .start = ehci_run, + .stop = ehci_stop, + .shutdown = ehci_shutdown, + + .get_frame_number = ehci_get_frame, + + .urb_enqueue = ehci_urb_enqueue, + .urb_dequeue = ehci_urb_dequeue, + .endpoint_disable = ehci_endpoint_disable, + .endpoint_reset = ehci_endpoint_reset, + + .hub_status_data = ehci_hub_status_data, + .hub_control = ehci_hub_control, + .bus_suspend = ehci_bus_suspend, + .bus_resume = ehci_bus_resume, + + .relinquish_port = ehci_relinquish_port, + .port_handed_over = ehci_port_handed_over, + + .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, +}; + +static int __devinit s5p_ehci_probe(struct platform_device *pdev) +{ + struct s5p_ehci_platdata *pdata; + struct s5p_ehci_hcd *s5p_ehci; + struct usb_hcd *hcd; + struct ehci_hcd *ehci; + struct resource *res; + int irq; + int err; + + pdata = pdev->dev.platform_data; + if (!pdata) { + dev_err(&pdev->dev, "No platform data defined\n"); + return -EINVAL; + } + + s5p_ehci = kzalloc(sizeof(struct s5p_ehci_hcd), GFP_KERNEL); + if (!s5p_ehci) + return -ENOMEM; + + s5p_ehci->dev = &pdev->dev; + + hcd = usb_create_hcd(&s5p_ehci_hc_driver, &pdev->dev, + dev_name(&pdev->dev)); + if (!hcd) { + dev_err(&pdev->dev, "Unable to create HCD\n"); + err = -ENOMEM; + goto fail_hcd; + } + + s5p_ehci->clk = clk_get(&pdev->dev, "usbhost"); + + if (IS_ERR(s5p_ehci->clk)) { + dev_err(&pdev->dev, "Failed to get usbhost clock\n"); + err = PTR_ERR(s5p_ehci->clk); + goto fail_clk; + } + + err = clk_enable(s5p_ehci->clk); + if (err) + goto fail_clken; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "Failed to get I/O memory\n"); + err = -ENXIO; + goto fail_io; + } + + hcd->rsrc_start = res->start; + hcd->rsrc_len = resource_size(res); + hcd->regs = ioremap(res->start, resource_size(res)); + if (!hcd->regs) { + dev_err(&pdev->dev, "Failed to remap I/O memory\n"); + err = -ENOMEM; + goto fail_io; + } + + irq = platform_get_irq(pdev, 0); + if (!irq) { + dev_err(&pdev->dev, "Failed to get IRQ\n"); + err = -ENODEV; + goto fail; + } + + if (pdata->phy_init) + pdata->phy_init(pdev, S5P_USB_PHY_HOST); + + ehci = hcd_to_ehci(hcd); + ehci->caps = hcd->regs; + ehci->regs = hcd->regs + + HC_LENGTH(ehci, readl(&ehci->caps->hc_capbase)); + + dbg_hcs_params(ehci, "reset"); + dbg_hcc_params(ehci, "reset"); + + /* cache this readonly data; minimize chip reads */ + ehci->hcs_params = readl(&ehci->caps->hcs_params); + + err = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); + if (err) { + dev_err(&pdev->dev, "Failed to add USB HCD\n"); + goto fail; + } + + platform_set_drvdata(pdev, s5p_ehci); + + return 0; + +fail: + iounmap(hcd->regs); +fail_io: + clk_disable(s5p_ehci->clk); +fail_clken: + clk_put(s5p_ehci->clk); +fail_clk: + usb_put_hcd(hcd); +fail_hcd: + kfree(s5p_ehci); + return err; +} + +static int __devexit s5p_ehci_remove(struct platform_device *pdev) +{ + struct s5p_ehci_platdata *pdata = pdev->dev.platform_data; + struct s5p_ehci_hcd *s5p_ehci = platform_get_drvdata(pdev); + struct usb_hcd *hcd = s5p_ehci->hcd; + + usb_remove_hcd(hcd); + + if (pdata && pdata->phy_exit) + pdata->phy_exit(pdev, S5P_USB_PHY_HOST); + + iounmap(hcd->regs); + + clk_disable(s5p_ehci->clk); + clk_put(s5p_ehci->clk); + + usb_put_hcd(hcd); + kfree(s5p_ehci); + + return 0; +} + +static void s5p_ehci_shutdown(struct platform_device *pdev) +{ + struct s5p_ehci_hcd *s5p_ehci = platform_get_drvdata(pdev); + struct usb_hcd *hcd = s5p_ehci->hcd; + + if (hcd->driver->shutdown) + hcd->driver->shutdown(hcd); +} + +static struct platform_driver s5p_ehci_driver = { + .probe = s5p_ehci_probe, + .remove = __devexit_p(s5p_ehci_remove), + .shutdown = s5p_ehci_shutdown, + .driver = { + .name = "s5p-ehci", + .owner = THIS_MODULE, + } +}; + +MODULE_ALIAS("platform:s5p-ehci"); |