-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This patch adds host USB high speed driver for samsung S5P series. This is initial driver and we need additional implementation to support some functions like power management. Signed-off-by: Jingoo Han <jg1.han@samsung.com> Signed-off-by: Joonyoung Shim <jy0922.shim@samsung.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
- Loading branch information
Joonyoung Shim
authored and
Greg Kroah-Hartman
committed
Apr 13, 2011
1 parent
8f1d169
commit 1bcc5aa
Showing
4 changed files
with
213 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,201 @@ | ||
/* | ||
* 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 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(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 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 = s5p_ehci_remove, | ||
.shutdown = s5p_ehci_shutdown, | ||
.driver = { | ||
.name = "s5p-ehci", | ||
.owner = THIS_MODULE, | ||
} | ||
}; | ||
|
||
MODULE_ALIAS("platform:s5p-ehci"); |