diff options
Diffstat (limited to 'drivers/usb/host')
-rw-r--r-- | drivers/usb/host/Kconfig | 8 | ||||
-rw-r--r-- | drivers/usb/host/ehci-atmel.c | 3 | ||||
-rw-r--r-- | drivers/usb/host/ehci-dbg.c | 2 | ||||
-rw-r--r-- | drivers/usb/host/ehci-hcd.c | 12 | ||||
-rw-r--r-- | drivers/usb/host/ehci-mxc.c | 3 | ||||
-rw-r--r-- | drivers/usb/host/ehci-omap.c | 310 | ||||
-rw-r--r-- | drivers/usb/host/ehci-pci.c | 7 | ||||
-rw-r--r-- | drivers/usb/host/ehci-spear.c | 212 | ||||
-rw-r--r-- | drivers/usb/host/ehci-vt8500.c | 172 | ||||
-rw-r--r-- | drivers/usb/host/ehci-w90x900.c | 3 | ||||
-rw-r--r-- | drivers/usb/host/ehci-xilinx-of.c | 1 | ||||
-rw-r--r-- | drivers/usb/host/ohci-hcd.c | 5 | ||||
-rw-r--r-- | drivers/usb/host/ohci-spear.c | 240 | ||||
-rw-r--r-- | drivers/usb/host/uhci-hcd.c | 2 |
14 files changed, 909 insertions, 71 deletions
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 2391c396ca32..6a7c688b4781 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -133,6 +133,14 @@ config USB_EHCI_MXC ---help--- Variation of ARC USB block used in some Freescale chips. +config USB_EHCI_HCD_OMAP + bool "EHCI support for OMAP3 and later chips" + depends on USB_EHCI_HCD && ARCH_OMAP + default y + --- help --- + Enables support for the on-chip EHCI controller on + OMAP3 and later chips. + config USB_EHCI_HCD_PPC_OF bool "EHCI support for PPC USB controller on OF platform bus" depends on USB_EHCI_HCD && PPC_OF diff --git a/drivers/usb/host/ehci-atmel.c b/drivers/usb/host/ehci-atmel.c index 51bd0edf544f..d6a69d514a84 100644 --- a/drivers/usb/host/ehci-atmel.c +++ b/drivers/usb/host/ehci-atmel.c @@ -99,6 +99,7 @@ static const struct hc_driver ehci_atmel_hc_driver = { .urb_enqueue = ehci_urb_enqueue, .urb_dequeue = ehci_urb_dequeue, .endpoint_disable = ehci_endpoint_disable, + .endpoint_reset = ehci_endpoint_reset, /* scheduling support */ .get_frame_number = ehci_get_frame, @@ -110,6 +111,8 @@ static const struct hc_driver ehci_atmel_hc_driver = { .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 __init ehci_atmel_drv_probe(struct platform_device *pdev) diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c index 86afdc73322f..a6bab7264c63 100644 --- a/drivers/usb/host/ehci-dbg.c +++ b/drivers/usb/host/ehci-dbg.c @@ -879,7 +879,7 @@ static int fill_buffer(struct debug_buffer *buf) int ret = 0; if (!buf->output_buf) - buf->output_buf = (char *)vmalloc(buf->alloc_size); + buf->output_buf = vmalloc(buf->alloc_size); if (!buf->output_buf) { ret = -ENOMEM; diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 02ffbea0c8fa..abe8336ebffa 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -1176,7 +1176,7 @@ MODULE_LICENSE ("GPL"); #define PLATFORM_DRIVER ehci_hcd_au1xxx_driver #endif -#ifdef CONFIG_ARCH_OMAP3 +#ifdef CONFIG_USB_EHCI_HCD_OMAP #include "ehci-omap.c" #define PLATFORM_DRIVER ehci_hcd_omap_driver #endif @@ -1221,6 +1221,16 @@ MODULE_LICENSE ("GPL"); #define PLATFORM_DRIVER ehci_octeon_driver #endif +#ifdef CONFIG_ARCH_VT8500 +#include "ehci-vt8500.c" +#define PLATFORM_DRIVER vt8500_ehci_driver +#endif + +#ifdef CONFIG_PLAT_SPEAR +#include "ehci-spear.c" +#define PLATFORM_DRIVER spear_ehci_hcd_driver +#endif + #if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) && \ !defined(PS3_SYSTEM_BUS_DRIVER) && !defined(OF_PLATFORM_DRIVER) && \ !defined(XILINX_OF_PLATFORM_DRIVER) diff --git a/drivers/usb/host/ehci-mxc.c b/drivers/usb/host/ehci-mxc.c index ac9c4d7c44af..802fcf6f8839 100644 --- a/drivers/usb/host/ehci-mxc.c +++ b/drivers/usb/host/ehci-mxc.c @@ -92,6 +92,7 @@ static const struct hc_driver ehci_mxc_hc_driver = { .urb_enqueue = ehci_urb_enqueue, .urb_dequeue = ehci_urb_dequeue, .endpoint_disable = ehci_endpoint_disable, + .endpoint_reset = ehci_endpoint_reset, /* * scheduling support @@ -107,6 +108,8 @@ static const struct hc_driver ehci_mxc_hc_driver = { .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 ehci_mxc_drv_probe(struct platform_device *pdev) diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c index 116ae280053a..0374eb47f09b 100644 --- a/drivers/usb/host/ehci-omap.c +++ b/drivers/usb/host/ehci-omap.c @@ -1,11 +1,12 @@ /* - * ehci-omap.c - driver for USBHOST on OMAP 34xx processor + * ehci-omap.c - driver for USBHOST on OMAP3/4 processors * - * Bus Glue for OMAP34xx USBHOST 3 port EHCI controller - * Tested on OMAP3430 ES2.0 SDP + * Bus Glue for the EHCI controllers in OMAP3/4 + * Tested on several OMAP3 boards, and OMAP4 Pandaboard * - * Copyright (C) 2007-2008 Texas Instruments, Inc. + * Copyright (C) 2007-2010 Texas Instruments, Inc. * Author: Vikram Pandita <vikram.pandita@ti.com> + * Author: Anand Gadiyar <gadiyar@ti.com> * * Copyright (C) 2009 Nokia Corporation * Contact: Felipe Balbi <felipe.balbi@nokia.com> @@ -26,11 +27,14 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * - * TODO (last updated Feb 12, 2010): + * TODO (last updated Nov 21, 2010): * - add kernel-doc * - enable AUTOIDLE * - add suspend/resume * - move workarounds to board-files + * - factor out code common to OHCI + * - add HSIC and TLL support + * - convert to use hwmod and runtime PM */ #include <linux/platform_device.h> @@ -114,6 +118,23 @@ #define OMAP_UHH_HOSTCONFIG_P2_CONNECT_STATUS (1 << 9) #define OMAP_UHH_HOSTCONFIG_P3_CONNECT_STATUS (1 << 10) +/* OMAP4-specific defines */ +#define OMAP4_UHH_SYSCONFIG_IDLEMODE_CLEAR (3 << 2) +#define OMAP4_UHH_SYSCONFIG_NOIDLE (1 << 2) + +#define OMAP4_UHH_SYSCONFIG_STDBYMODE_CLEAR (3 << 4) +#define OMAP4_UHH_SYSCONFIG_NOSTDBY (1 << 4) +#define OMAP4_UHH_SYSCONFIG_SOFTRESET (1 << 0) + +#define OMAP4_P1_MODE_CLEAR (3 << 16) +#define OMAP4_P1_MODE_TLL (1 << 16) +#define OMAP4_P1_MODE_HSIC (3 << 16) +#define OMAP4_P2_MODE_CLEAR (3 << 18) +#define OMAP4_P2_MODE_TLL (1 << 18) +#define OMAP4_P2_MODE_HSIC (3 << 18) + +#define OMAP_REV2_TLL_CHANNEL_COUNT 2 + #define OMAP_UHH_DEBUG_CSR (0x44) /* EHCI Register Set */ @@ -127,6 +148,17 @@ #define EHCI_INSNREG05_ULPI_EXTREGADD_SHIFT 8 #define EHCI_INSNREG05_ULPI_WRDATA_SHIFT 0 +/* Values of UHH_REVISION - Note: these are not given in the TRM */ +#define OMAP_EHCI_REV1 0x00000010 /* OMAP3 */ +#define OMAP_EHCI_REV2 0x50700100 /* OMAP4 */ + +#define is_omap_ehci_rev1(x) (x->omap_ehci_rev == OMAP_EHCI_REV1) +#define is_omap_ehci_rev2(x) (x->omap_ehci_rev == OMAP_EHCI_REV2) + +#define is_ehci_phy_mode(x) (x == EHCI_HCD_OMAP_MODE_PHY) +#define is_ehci_tll_mode(x) (x == EHCI_HCD_OMAP_MODE_TLL) +#define is_ehci_hsic_mode(x) (x == EHCI_HCD_OMAP_MODE_HSIC) + /*-------------------------------------------------------------------------*/ static inline void ehci_omap_writel(void __iomem *base, u32 reg, u32 val) @@ -156,10 +188,14 @@ struct ehci_hcd_omap { struct device *dev; struct clk *usbhost_ick; - struct clk *usbhost2_120m_fck; - struct clk *usbhost1_48m_fck; + struct clk *usbhost_hs_fck; + struct clk *usbhost_fs_fck; struct clk *usbtll_fck; struct clk *usbtll_ick; + struct clk *xclk60mhsp1_ck; + struct clk *xclk60mhsp2_ck; + struct clk *utmi_p1_fck; + struct clk *utmi_p2_fck; /* FIXME the following two workarounds are * board specific not silicon-specific so these @@ -176,6 +212,9 @@ struct ehci_hcd_omap { /* phy reset workaround */ int phy_reset; + /* IP revision */ + u32 omap_ehci_rev; + /* desired phy_mode: TLL, PHY */ enum ehci_hcd_omap_mode port_mode[OMAP3_HS_USB_PORTS]; @@ -191,13 +230,14 @@ struct ehci_hcd_omap { /*-------------------------------------------------------------------------*/ -static void omap_usb_utmi_init(struct ehci_hcd_omap *omap, u8 tll_channel_mask) +static void omap_usb_utmi_init(struct ehci_hcd_omap *omap, u8 tll_channel_mask, + u8 tll_channel_count) { unsigned reg; int i; /* Program the 3 TLL channels upfront */ - for (i = 0; i < OMAP_TLL_CHANNEL_COUNT; i++) { + for (i = 0; i < tll_channel_count; i++) { reg = ehci_omap_readl(omap->tll_base, OMAP_TLL_CHANNEL_CONF(i)); /* Disable AutoIdle, BitStuffing and use SDR Mode */ @@ -217,7 +257,7 @@ static void omap_usb_utmi_init(struct ehci_hcd_omap *omap, u8 tll_channel_mask) ehci_omap_writel(omap->tll_base, OMAP_TLL_SHARED_CONF, reg); /* Enable channels now */ - for (i = 0; i < OMAP_TLL_CHANNEL_COUNT; i++) { + for (i = 0; i < tll_channel_count; i++) { reg = ehci_omap_readl(omap->tll_base, OMAP_TLL_CHANNEL_CONF(i)); /* Enable only the reg that is needed */ @@ -286,19 +326,19 @@ static int omap_start_ehc(struct ehci_hcd_omap *omap, struct usb_hcd *hcd) } clk_enable(omap->usbhost_ick); - omap->usbhost2_120m_fck = clk_get(omap->dev, "usbhost_120m_fck"); - if (IS_ERR(omap->usbhost2_120m_fck)) { - ret = PTR_ERR(omap->usbhost2_120m_fck); + omap->usbhost_hs_fck = clk_get(omap->dev, "hs_fck"); + if (IS_ERR(omap->usbhost_hs_fck)) { + ret = PTR_ERR(omap->usbhost_hs_fck); goto err_host_120m_fck; } - clk_enable(omap->usbhost2_120m_fck); + clk_enable(omap->usbhost_hs_fck); - omap->usbhost1_48m_fck = clk_get(omap->dev, "usbhost_48m_fck"); - if (IS_ERR(omap->usbhost1_48m_fck)) { - ret = PTR_ERR(omap->usbhost1_48m_fck); + omap->usbhost_fs_fck = clk_get(omap->dev, "fs_fck"); + if (IS_ERR(omap->usbhost_fs_fck)) { + ret = PTR_ERR(omap->usbhost_fs_fck); goto err_host_48m_fck; } - clk_enable(omap->usbhost1_48m_fck); + clk_enable(omap->usbhost_fs_fck); if (omap->phy_reset) { /* Refer: ISSUE1 */ @@ -333,6 +373,80 @@ static int omap_start_ehc(struct ehci_hcd_omap *omap, struct usb_hcd *hcd) } clk_enable(omap->usbtll_ick); + omap->omap_ehci_rev = ehci_omap_readl(omap->uhh_base, + OMAP_UHH_REVISION); + dev_dbg(omap->dev, "OMAP UHH_REVISION 0x%x\n", + omap->omap_ehci_rev); + + /* + * Enable per-port clocks as needed (newer controllers only). + * - External ULPI clock for PHY mode + * - Internal clocks for TLL and HSIC modes (TODO) + */ + if (is_omap_ehci_rev2(omap)) { + switch (omap->port_mode[0]) { + case EHCI_HCD_OMAP_MODE_PHY: + omap->xclk60mhsp1_ck = clk_get(omap->dev, + "xclk60mhsp1_ck"); + if (IS_ERR(omap->xclk60mhsp1_ck)) { + ret = PTR_ERR(omap->xclk60mhsp1_ck); + dev_err(omap->dev, + "Unable to get Port1 ULPI clock\n"); + } + + omap->utmi_p1_fck = clk_get(omap->dev, + "utmi_p1_gfclk"); + if (IS_ERR(omap->utmi_p1_fck)) { + ret = PTR_ERR(omap->utmi_p1_fck); + dev_err(omap->dev, + "Unable to get utmi_p1_fck\n"); + } + + ret = clk_set_parent(omap->utmi_p1_fck, + omap->xclk60mhsp1_ck); + if (ret != 0) { + dev_err(omap->dev, + "Unable to set P1 f-clock\n"); + } + break; + case EHCI_HCD_OMAP_MODE_TLL: + /* TODO */ + default: + break; + } + switch (omap->port_mode[1]) { + case EHCI_HCD_OMAP_MODE_PHY: + omap->xclk60mhsp2_ck = clk_get(omap->dev, + "xclk60mhsp2_ck"); + if (IS_ERR(omap->xclk60mhsp2_ck)) { + ret = PTR_ERR(omap->xclk60mhsp2_ck); + dev_err(omap->dev, + "Unable to get Port2 ULPI clock\n"); + } + + omap->utmi_p2_fck = clk_get(omap->dev, + "utmi_p2_gfclk"); + if (IS_ERR(omap->utmi_p2_fck)) { + ret = PTR_ERR(omap->utmi_p2_fck); + dev_err(omap->dev, + "Unable to get utmi_p2_fck\n"); + } + + ret = clk_set_parent(omap->utmi_p2_fck, + omap->xclk60mhsp2_ck); + if (ret != 0) { + dev_err(omap->dev, + "Unable to set P2 f-clock\n"); + } + break; + case EHCI_HCD_OMAP_MODE_TLL: + /* TODO */ + default: + break; + } + } + + /* perform TLL soft reset, and wait until reset is complete */ ehci_omap_writel(omap->tll_base, OMAP_USBTLL_SYSCONFIG, OMAP_USBTLL_SYSCONFIG_SOFTRESET); @@ -360,12 +474,20 @@ static int omap_start_ehc(struct ehci_hcd_omap *omap, struct usb_hcd *hcd) /* Put UHH in NoIdle/NoStandby mode */ reg = ehci_omap_readl(omap->uhh_base, OMAP_UHH_SYSCONFIG); - reg |= (OMAP_UHH_SYSCONFIG_ENAWAKEUP - | OMAP_UHH_SYSCONFIG_SIDLEMODE - | OMAP_UHH_SYSCONFIG_CACTIVITY - | OMAP_UHH_SYSCONFIG_MIDLEMODE); - reg &= ~OMAP_UHH_SYSCONFIG_AUTOIDLE; + if (is_omap_ehci_rev1(omap)) { + reg |= (OMAP_UHH_SYSCONFIG_ENAWAKEUP + | OMAP_UHH_SYSCONFIG_SIDLEMODE + | OMAP_UHH_SYSCONFIG_CACTIVITY + | OMAP_UHH_SYSCONFIG_MIDLEMODE); + reg &= ~OMAP_UHH_SYSCONFIG_AUTOIDLE; + + } else if (is_omap_ehci_rev2(omap)) { + reg &= ~OMAP4_UHH_SYSCONFIG_IDLEMODE_CLEAR; + reg |= OMAP4_UHH_SYSCONFIG_NOIDLE; + reg &= ~OMAP4_UHH_SYSCONFIG_STDBYMODE_CLEAR; + reg |= OMAP4_UHH_SYSCONFIG_NOSTDBY; + } ehci_omap_writel(omap->uhh_base, OMAP_UHH_SYSCONFIG, reg); reg = ehci_omap_readl(omap->uhh_base, OMAP_UHH_HOSTCONFIG); @@ -376,40 +498,56 @@ static int omap_start_ehc(struct ehci_hcd_omap *omap, struct usb_hcd *hcd) | OMAP_UHH_HOSTCONFIG_INCR16_BURST_EN); reg &= ~OMAP_UHH_HOSTCONFIG_INCRX_ALIGN_EN; - if (omap->port_mode[0] == EHCI_HCD_OMAP_MODE_UNKNOWN) - reg &= ~OMAP_UHH_HOSTCONFIG_P1_CONNECT_STATUS; - if (omap->port_mode[1] == EHCI_HCD_OMAP_MODE_UNKNOWN) - reg &= ~OMAP_UHH_HOSTCONFIG_P2_CONNECT_STATUS; - if (omap->port_mode[2] == EHCI_HCD_OMAP_MODE_UNKNOWN) - reg &= ~OMAP_UHH_HOSTCONFIG_P3_CONNECT_STATUS; - - /* Bypass the TLL module for PHY mode operation */ - if (cpu_is_omap3430() && (omap_rev() <= OMAP3430_REV_ES2_1)) { - dev_dbg(omap->dev, "OMAP3 ES version <= ES2.1\n"); - if ((omap->port_mode[0] == EHCI_HCD_OMAP_MODE_PHY) || - (omap->port_mode[1] == EHCI_HCD_OMAP_MODE_PHY) || - (omap->port_mode[2] == EHCI_HCD_OMAP_MODE_PHY)) - reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_BYPASS; - else - reg |= OMAP_UHH_HOSTCONFIG_ULPI_BYPASS; - } else { - dev_dbg(omap->dev, "OMAP3 ES version > ES2.1\n"); - if (omap->port_mode[0] == EHCI_HCD_OMAP_MODE_PHY) - reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS; - else if (omap->port_mode[0] == EHCI_HCD_OMAP_MODE_TLL) - reg |= OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS; - - if (omap->port_mode[1] == EHCI_HCD_OMAP_MODE_PHY) - reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS; - else if (omap->port_mode[1] == EHCI_HCD_OMAP_MODE_TLL) - reg |= OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS; - - if (omap->port_mode[2] == EHCI_HCD_OMAP_MODE_PHY) - reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS; - else if (omap->port_mode[2] == EHCI_HCD_OMAP_MODE_TLL) - reg |= OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS; + if (is_omap_ehci_rev1(omap)) { + if (omap->port_mode[0] == EHCI_HCD_OMAP_MODE_UNKNOWN) + reg &= ~OMAP_UHH_HOSTCONFIG_P1_CONNECT_STATUS; + if (omap->port_mode[1] == EHCI_HCD_OMAP_MODE_UNKNOWN) + reg &= ~OMAP_UHH_HOSTCONFIG_P2_CONNECT_STATUS; + if (omap->port_mode[2] == EHCI_HCD_OMAP_MODE_UNKNOWN) + reg &= ~OMAP_UHH_HOSTCONFIG_P3_CONNECT_STATUS; + + /* Bypass the TLL module for PHY mode operation */ + if (cpu_is_omap3430() && (omap_rev() <= OMAP3430_REV_ES2_1)) { + dev_dbg(omap->dev, "OMAP3 ES version <= ES2.1\n"); + if (is_ehci_phy_mode(omap->port_mode[0]) || + is_ehci_phy_mode(omap->port_mode[1]) || + is_ehci_phy_mode(omap->port_mode[2])) + reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_BYPASS; + else + reg |= OMAP_UHH_HOSTCONFIG_ULPI_BYPASS; + } else { + dev_dbg(omap->dev, "OMAP3 ES version > ES2.1\n"); + if (is_ehci_phy_mode(omap->port_mode[0])) + reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS; + else if (is_ehci_tll_mode(omap->port_mode[0])) + reg |= OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS; + + if (is_ehci_phy_mode(omap->port_mode[1])) + reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS; + else if (is_ehci_tll_mode(omap->port_mode[1])) + reg |= OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS; + + if (is_ehci_phy_mode(omap->port_mode[2])) + reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS; + else if (is_ehci_tll_mode(omap->port_mode[2])) + reg |= OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS; + } + } else if (is_omap_ehci_rev2(omap)) { + /* Clear port mode fields for PHY mode*/ + reg &= ~OMAP4_P1_MODE_CLEAR; + reg &= ~OMAP4_P2_MODE_CLEAR; + + if (is_ehci_tll_mode(omap->port_mode[0])) + reg |= OMAP4_P1_MODE_TLL; + else if (is_ehci_hsic_mode(omap->port_mode[0])) + reg |= OMAP4_P1_MODE_HSIC; + if (is_ehci_tll_mode(omap->port_mode[1])) + reg |= OMAP4_P2_MODE_TLL; + else if (is_ehci_hsic_mode(omap->port_mode[1])) + reg |= OMAP4_P2_MODE_HSIC; } + ehci_omap_writel(omap->uhh_base, OMAP_UHH_HOSTCONFIG, reg); dev_dbg(omap->dev, "UHH setup done, uhh_hostconfig=%x\n", reg); @@ -438,7 +576,7 @@ static int omap_start_ehc(struct ehci_hcd_omap *omap, struct usb_hcd *hcd) tll_ch_mask |= OMAP_TLL_CHANNEL_3_EN_MASK; /* Enable UTMI mode for required TLL channels */ - omap_usb_utmi_init(omap, tll_ch_mask); + omap_usb_utmi_init(omap, tll_ch_mask, OMAP_TLL_CHANNEL_COUNT); } if (omap->phy_reset) { @@ -464,6 +602,14 @@ static int omap_start_ehc(struct ehci_hcd_omap *omap, struct usb_hcd *hcd) return 0; err_sys_status: + clk_disable(omap->utmi_p2_fck); + clk_put(omap->utmi_p2_fck); + clk_disable(omap->xclk60mhsp2_ck); + clk_put(omap->xclk60mhsp2_ck); + clk_disable(omap->utmi_p1_fck); + clk_put(omap->utmi_p1_fck); + clk_disable(omap->xclk60mhsp1_ck); + clk_put(omap->xclk60mhsp1_ck); clk_disable(omap->usbtll_ick); clk_put(omap->usbtll_ick); @@ -472,8 +618,8 @@ err_tll_ick: clk_put(omap->usbtll_fck); err_tll_fck: - clk_disable(omap->usbhost1_48m_fck); - clk_put(omap->usbhost1_48m_fck); + clk_disable(omap->usbhost_fs_fck); + clk_put(omap->usbhost_fs_fck); if (omap->phy_reset) { if (gpio_is_valid(omap->reset_gpio_port[0])) @@ -484,8 +630,8 @@ err_tll_fck: } err_host_48m_fck: - clk_disable(omap->usbhost2_120m_fck); - clk_put(omap->usbhost2_120m_fck); + clk_disable(omap->usbhost_hs_fck); + clk_put(omap->usbhost_hs_fck); err_host_120m_fck: clk_disable(omap->usbhost_ick); @@ -503,6 +649,8 @@ static void omap_stop_ehc(struct ehci_hcd_omap *omap, struct usb_hcd *hcd) /* Reset OMAP modules for insmod/rmmod to work */ ehci_omap_writel(omap->uhh_base, OMAP_UHH_SYSCONFIG, + is_omap_ehci_rev2(omap) ? + OMAP4_UHH_SYSCONFIG_SOFTRESET : OMAP_UHH_SYSCONFIG_SOFTRESET); while (!(ehci_omap_readl(omap->uhh_base, OMAP_UHH_SYSSTATUS) & (1 << 0))) { @@ -550,16 +698,16 @@ static void omap_stop_ehc(struct ehci_hcd_omap *omap, struct usb_hcd *hcd) omap->usbhost_ick = NULL; } - if (omap->usbhost1_48m_fck != NULL) { - clk_disable(omap->usbhost1_48m_fck); - clk_put(omap->usbhost1_48m_fck); - omap->usbhost1_48m_fck = NULL; + if (omap->usbhost_fs_fck != NULL) { + clk_disable(omap->usbhost_fs_fck); + clk_put(omap->usbhost_fs_fck); + omap->usbhost_fs_fck = NULL; } - if (omap->usbhost2_120m_fck != NULL) { - clk_disable(omap->usbhost2_120m_fck); - clk_put(omap->usbhost2_120m_fck); - omap->usbhost2_120m_fck = NULL; + if (omap->usbhost_hs_fck != NULL) { + clk_disable(omap->usbhost_hs_fck); + clk_put(omap->usbhost_hs_fck); + omap->usbhost_hs_fck = NULL; } if (omap->usbtll_ick != NULL) { @@ -568,6 +716,32 @@ static void omap_stop_ehc(struct ehci_hcd_omap *omap, struct usb_hcd *hcd) omap->usbtll_ick = NULL; } + if (is_omap_ehci_rev2(omap)) { + if (omap->xclk60mhsp1_ck != NULL) { + clk_disable(omap->xclk60mhsp1_ck); + clk_put(omap->xclk60mhsp1_ck); + omap->xclk60mhsp1_ck = NULL; + } + + if (omap->utmi_p1_fck != NULL) { + clk_disable(omap->utmi_p1_fck); + clk_put(omap->utmi_p1_fck); + omap->utmi_p1_fck = NULL; + } + + if (omap->xclk60mhsp2_ck != NULL) { + clk_disable(omap->xclk60mhsp2_ck); + clk_put(omap->xclk60mhsp2_ck); + omap->xclk60mhsp2_ck = NULL; + } + + if (omap->utmi_p2_fck != NULL) { + clk_disable(omap->utmi_p2_fck); + clk_put(omap->utmi_p2_fck); + omap->utmi_p2_fck = NULL; + } + } + if (omap->phy_reset) { if (gpio_is_valid(omap->reset_gpio_port[0])) gpio_free(omap->reset_gpio_port[0]); diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index a1e8d273103f..56c78e93440f 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -22,6 +22,9 @@ #error "This file is PCI bus glue. CONFIG_PCI must be defined." #endif +/* defined here to avoid adding to pci_ids.h for single instance use */ +#define PCI_DEVICE_ID_INTEL_CE4100_USB 0x2e70 + /*-------------------------------------------------------------------------*/ /* called after powerup, by probe or system-pm "wakeup" */ @@ -124,6 +127,10 @@ static int ehci_pci_setup(struct usb_hcd *hcd) ehci_info(ehci, "disable lpm for langwell/penwell\n"); ehci->has_lpm = 0; } + if (pdev->device == PCI_DEVICE_ID_INTEL_CE4100_USB) { + hcd->has_tt = 1; + tdi_reset(ehci); + } break; case PCI_VENDOR_ID_TDI: if (pdev->device == PCI_DEVICE_ID_TDI_EHCI) { diff --git a/drivers/usb/host/ehci-spear.c b/drivers/usb/host/ehci-spear.c new file mode 100644 index 000000000000..75c00873443d --- /dev/null +++ b/drivers/usb/host/ehci-spear.c @@ -0,0 +1,212 @@ +/* +* Driver for EHCI HCD on SPEAR SOC +* +* Copyright (C) 2010 ST Micro Electronics, +* Deepak Sikri <deepak.sikri@st.com> +* +* Based on various ehci-*.c drivers +* +* This file is subject to the terms and conditions of the GNU General Public +* License. See the file COPYING in the main directory of this archive for +* more details. +*/ + +#include <linux/platform_device.h> +#include <linux/clk.h> + +struct spear_ehci { + struct ehci_hcd ehci; + struct clk *clk; +}; + +#define to_spear_ehci(hcd) (struct spear_ehci *)hcd_to_ehci(hcd) + +static void spear_start_ehci(struct spear_ehci *ehci) +{ + clk_enable(ehci->clk); +} + +static void spear_stop_ehci(struct spear_ehci *ehci) +{ + clk_disable(ehci->clk); +} + +static int ehci_spear_setup(struct usb_hcd *hcd) +{ + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + int retval = 0; + + /* registers start at offset 0x0 */ + ehci->caps = hcd->regs; + ehci->regs = hcd->regs + HC_LENGTH(ehci_readl(ehci, + &ehci->caps->hc_capbase)); + /* cache this readonly data; minimize chip reads */ + ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); + retval = ehci_halt(ehci); + if (retval) + return retval; + + retval = ehci_init(hcd); + if (retval) + return retval; + + ehci_reset(ehci); + ehci_port_power(ehci, 0); + + return retval; +} + +static const struct hc_driver ehci_spear_hc_driver = { + .description = hcd_name, + .product_desc = "SPEAr EHCI", + .hcd_priv_size = sizeof(struct spear_ehci), + + /* generic hardware linkage */ + .irq = ehci_irq, + .flags = HCD_MEMORY | HCD_USB2, + + /* basic lifecycle operations */ + .reset = ehci_spear_setup, + .start = ehci_run, + .stop = ehci_stop, + .shutdown = ehci_shutdown, + + /* managing i/o requests and associated device resources */ + .urb_enqueue = ehci_urb_enqueue, + .urb_dequeue = ehci_urb_dequeue, + .endpoint_disable = ehci_endpoint_disable, + .endpoint_reset = ehci_endpoint_reset, + + /* scheduling support */ + .get_frame_number = ehci_get_frame, + + /* root hub support */ + .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 spear_ehci_hcd_drv_probe(struct platform_device *pdev) +{ + struct usb_hcd *hcd ; + struct spear_ehci *ehci; + struct resource *res; + struct clk *usbh_clk; + const struct hc_driver *driver = &ehci_spear_hc_driver; + int *pdata = pdev->dev.platform_data; + int irq, retval; + char clk_name[20] = "usbh_clk"; + + if (pdata == NULL) + return -EFAULT; + + if (usb_disabled()) + return -ENODEV; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + retval = irq; + goto fail_irq_get; + } + + if (*pdata >= 0) + sprintf(clk_name, "usbh.%01d_clk", *pdata); + + usbh_clk = clk_get(NULL, clk_name); + if (IS_ERR(usbh_clk)) { + dev_err(&pdev->dev, "Error getting interface clock\n"); + retval = PTR_ERR(usbh_clk); + goto fail_get_usbh_clk; + } + + hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev)); + if (!hcd) { + retval = -ENOMEM; + goto fail_create_hcd; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + retval = -ENODEV; + goto fail_request_resource; + } + + hcd->rsrc_start = res->start; + hcd->rsrc_len = resource_size(res); + if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, + driver->description)) { + retval = -EBUSY; + goto fail_request_resource; + } + + hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); + if (hcd->regs == NULL) { + dev_dbg(&pdev->dev, "error mapping memory\n"); + retval = -ENOMEM; + goto fail_ioremap; + } + + ehci = (struct spear_ehci *)hcd_to_ehci(hcd); + ehci->clk = usbh_clk; + + spear_start_ehci(ehci); + retval = usb_add_hcd(hcd, irq, IRQF_SHARED | IRQF_DISABLED); + if (retval) + goto fail_add_hcd; + + return retval; + +fail_add_hcd: + spear_stop_ehci(ehci); + iounmap(hcd->regs); +fail_ioremap: + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); +fail_request_resource: + usb_put_hcd(hcd); +fail_create_hcd: + clk_put(usbh_clk); +fail_get_usbh_clk: +fail_irq_get: + dev_err(&pdev->dev, "init fail, %d\n", retval); + + return retval ; +} + +static int spear_ehci_hcd_drv_remove(struct platform_device *pdev) +{ + struct usb_hcd *hcd = platform_get_drvdata(pdev); + struct spear_ehci *ehci_p = to_spear_ehci(hcd); + + if (!hcd) + return 0; + if (in_interrupt()) + BUG(); + usb_remove_hcd(hcd); + + if (ehci_p->clk) + spear_stop_ehci(ehci_p); + iounmap(hcd->regs); + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + usb_put_hcd(hcd); + + if (ehci_p->clk) + clk_put(ehci_p->clk); + + return 0; +} + +static struct platform_driver spear_ehci_hcd_driver = { + .probe = spear_ehci_hcd_drv_probe, + .remove = spear_ehci_hcd_drv_remove, + .shutdown = usb_hcd_platform_shutdown, + .driver = { + .name = "spear-ehci", + .bus = &platform_bus_type + } +}; + +MODULE_ALIAS("platform:spear-ehci"); diff --git a/drivers/usb/host/ehci-vt8500.c b/drivers/usb/host/ehci-vt8500.c new file mode 100644 index 000000000000..20168062035a --- /dev/null +++ b/drivers/usb/host/ehci-vt8500.c @@ -0,0 +1,172 @@ +/* + * drivers/usb/host/ehci-vt8500.c + * + * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com> + * + * Based on ehci-au1xxx.c + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/platform_device.h> + +static int ehci_update_device(struct usb_hcd *hcd, struct usb_device *udev) +{ + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + int rc = 0; + + if (!udev->parent) /* udev is root hub itself, impossible */ + rc = -1; + /* we only support lpm device connected to root hub yet */ + if (ehci->has_lpm && !udev->parent->parent) { + rc = ehci_lpm_set_da(ehci, udev->devnum, udev->portnum); + if (!rc) + rc = ehci_lpm_check(ehci, udev->portnum); + } + return rc; +} + +static const struct hc_driver vt8500_ehci_hc_driver = { + .description = hcd_name, + .product_desc = "VT8500 EHCI", + .hcd_priv_size = sizeof(struct ehci_hcd), + + /* + * generic hardware linkage + */ + .irq = ehci_irq, + .flags = HCD_MEMORY | HCD_USB2, + + /* + * basic lifecycle operations + */ + .reset = ehci_init, + .start = ehci_run, + .stop = ehci_stop, + .shutdown = ehci_shutdown, + + /* + * managing i/o requests and associated device resources + */ + .urb_enqueue = ehci_urb_enqueue, + .urb_dequeue = ehci_urb_dequeue, + .endpoint_disable = ehci_endpoint_disable, + .endpoint_reset = ehci_endpoint_reset, + + /* + * scheduling support + */ + .get_frame_number = ehci_get_frame, + + /* + * root hub support + */ + .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, + + /* + * call back when device connected and addressed + */ + .update_device = ehci_update_device, + + .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, +}; + +static int vt8500_ehci_drv_probe(struct platform_device *pdev) +{ + struct usb_hcd *hcd; + struct ehci_hcd *ehci; + struct resource *res; + int ret; + + if (usb_disabled()) + return -ENODEV; + + if (pdev->resource[1].flags != IORESOURCE_IRQ) { + pr_debug("resource[1] is not IORESOURCE_IRQ"); + return -ENOMEM; + } + hcd = usb_create_hcd(&vt8500_ehci_hc_driver, &pdev->dev, "VT8500"); + if (!hcd) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + hcd->rsrc_start = res->start; + hcd->rsrc_len = resource_size(res); + + if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { + pr_debug("request_mem_region failed"); + ret = -EBUSY; + goto err1; + } + + hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); + if (!hcd->regs) { + pr_debug("ioremap failed"); + ret = -ENOMEM; + goto err2; + } + + 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); + + ehci_port_power(ehci, 1); + + ret = usb_add_hcd(hcd, pdev->resource[1].start, + IRQF_DISABLED | IRQF_SHARED); + if (ret == 0) { + platform_set_drvdata(pdev, hcd); + return ret; + } + + iounmap(hcd->regs); +err2: + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); +err1: + usb_put_hcd(hcd); + return ret; +} + +static int vt8500_ehci_drv_remove(struct platform_device *pdev) +{ + struct usb_hcd *hcd = platform_get_drvdata(pdev); + + usb_remove_hcd(hcd); + iounmap(hcd->regs); + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + usb_put_hcd(hcd); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver vt8500_ehci_driver = { + .probe = vt8500_ehci_drv_probe, + .remove = vt8500_ehci_drv_remove, + .shutdown = usb_hcd_platform_shutdown, + .driver = { + .name = "vt8500-ehci", + .owner = THIS_MODULE, + } +}; + +MODULE_ALIAS("platform:vt8500-ehci"); diff --git a/drivers/usb/host/ehci-w90x900.c b/drivers/usb/host/ehci-w90x900.c index cfa21ea20f82..6bc35809a5c6 100644 --- a/drivers/usb/host/ehci-w90x900.c +++ b/drivers/usb/host/ehci-w90x900.c @@ -130,6 +130,7 @@ static const struct hc_driver ehci_w90x900_hc_driver = { .urb_enqueue = ehci_urb_enqueue, .urb_dequeue = ehci_urb_dequeue, .endpoint_disable = ehci_endpoint_disable, + .endpoint_reset = ehci_endpoint_reset, /* * scheduling support @@ -147,6 +148,8 @@ static const struct hc_driver ehci_w90x900_hc_driver = { #endif .relinquish_port = ehci_relinquish_port, .port_handed_over = ehci_port_handed_over, + + .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, }; static int __devinit ehci_w90x900_probe(struct platform_device *pdev) diff --git a/drivers/usb/host/ehci-xilinx-of.c b/drivers/usb/host/ehci-xilinx-of.c index 6c8076ad821d..e8f4f36fdf0b 100644 --- a/drivers/usb/host/ehci-xilinx-of.c +++ b/drivers/usb/host/ehci-xilinx-of.c @@ -117,6 +117,7 @@ static const struct hc_driver ehci_xilinx_of_hc_driver = { .urb_enqueue = ehci_urb_enqueue, .urb_dequeue = ehci_urb_dequeue, .endpoint_disable = ehci_endpoint_disable, + .endpoint_reset = ehci_endpoint_reset, /* * scheduling support diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index 5179acb7aa2f..4a4a4f025385 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -1081,6 +1081,11 @@ MODULE_LICENSE ("GPL"); #define OF_PLATFORM_DRIVER ohci_hcd_ppc_of_driver #endif +#ifdef CONFIG_PLAT_SPEAR +#include "ohci-spear.c" +#define PLATFORM_DRIVER spear_ohci_hcd_driver +#endif + #ifdef CONFIG_PPC_PS3 #include "ohci-ps3.c" #define PS3_SYSTEM_BUS_DRIVER ps3_ohci_driver diff --git a/drivers/usb/host/ohci-spear.c b/drivers/usb/host/ohci-spear.c new file mode 100644 index 000000000000..4fd4bea9ac7a --- /dev/null +++ b/drivers/usb/host/ohci-spear.c @@ -0,0 +1,240 @@ +/* +* OHCI HCD (Host Controller Driver) for USB. +* +* Copyright (C) 2010 ST Microelectronics. +* Deepak Sikri<deepak.sikri@st.com> +* +* Based on various ohci-*.c drivers +* +* This file is licensed under the terms of the GNU General Public +* License version 2. This program is licensed "as is" without any +* warranty of any kind, whether express or implied. +*/ + +#include <linux/signal.h> +#include <linux/platform_device.h> +#include <linux/clk.h> + +struct spear_ohci { + struct ohci_hcd ohci; + struct clk *clk; +}; + +#define to_spear_ohci(hcd) (struct spear_ohci *)hcd_to_ohci(hcd) + +static void spear_start_ohci(struct spear_ohci *ohci) +{ + clk_enable(ohci->clk); +} + +static void spear_stop_ohci(struct spear_ohci *ohci) +{ + clk_disable(ohci->clk); +} + +static int __devinit ohci_spear_start(struct usb_hcd *hcd) +{ + struct ohci_hcd *ohci = hcd_to_ohci(hcd); + int ret; + + ret = ohci_init(ohci); + if (ret < 0) + return ret; + ohci->regs = hcd->regs; + + ret = ohci_run(ohci); + if (ret < 0) { + dev_err(hcd->self.controller, "can't start\n"); + ohci_stop(hcd); + return ret; + } + + create_debug_files(ohci); + +#ifdef DEBUG + ohci_dump(ohci, 1); +#endif + return 0; +} + +static const struct hc_driver ohci_spear_hc_driver = { + .description = hcd_name, + .product_desc = "SPEAr OHCI", + .hcd_priv_size = sizeof(struct spear_ohci), + + /* generic hardware linkage */ + .irq = ohci_irq, + .flags = HCD_USB11 | HCD_MEMORY, + + /* basic lifecycle operations */ + .start = ohci_spear_start, + .stop = ohci_stop, + .shutdown = ohci_shutdown, +#ifdef CONFIG_PM + .bus_suspend = ohci_bus_suspend, + .bus_resume = ohci_bus_resume, +#endif + + /* managing i/o requests and associated device resources */ + .urb_enqueue = ohci_urb_enqueue, + .urb_dequeue = ohci_urb_dequeue, + .endpoint_disable = ohci_endpoint_disable, + + /* scheduling support */ + .get_frame_number = ohci_get_frame, + + /* root hub support */ + .hub_status_data = ohci_hub_status_data, + .hub_control = ohci_hub_control, + + .start_port_reset = ohci_start_port_reset, +}; + +static int spear_ohci_hcd_drv_probe(struct platform_device *pdev) +{ + const struct hc_driver *driver = &ohci_spear_hc_driver; + struct usb_hcd *hcd = NULL; + struct clk *usbh_clk; + struct spear_ohci *ohci_p; + struct resource *res; + int retval, irq; + int *pdata = pdev->dev.platform_data; + char clk_name[20] = "usbh_clk"; + + if (pdata == NULL) + return -EFAULT; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + retval = irq; + goto fail_irq_get; + } + + if (*pdata >= 0) + sprintf(clk_name, "usbh.%01d_clk", *pdata); + + usbh_clk = clk_get(NULL, clk_name); + if (IS_ERR(usbh_clk)) { + dev_err(&pdev->dev, "Error getting interface clock\n"); + retval = PTR_ERR(usbh_clk); + goto fail_get_usbh_clk; + } + + hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev)); + if (!hcd) { + retval = -ENOMEM; + goto fail_create_hcd; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + retval = -ENODEV; + goto fail_request_resource; + } + + hcd->rsrc_start = pdev->resource[0].start; + hcd->rsrc_len = resource_size(res); + if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { + dev_dbg(&pdev->dev, "request_mem_region failed\n"); + retval = -EBUSY; + goto fail_request_resource; + } + + hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); + if (!hcd->regs) { + dev_dbg(&pdev->dev, "ioremap failed\n"); + retval = -ENOMEM; + goto fail_ioremap; + } + + ohci_p = (struct spear_ohci *)hcd_to_ohci(hcd); + ohci_p->clk = usbh_clk; + spear_start_ohci(ohci_p); + ohci_hcd_init(hcd_to_ohci(hcd)); + + retval = usb_add_hcd(hcd, platform_get_irq(pdev, 0), IRQF_DISABLED); + if (retval == 0) + return retval; + + spear_stop_ohci(ohci_p); + iounmap(hcd->regs); +fail_ioremap: + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); +fail_request_resource: + usb_put_hcd(hcd); +fail_create_hcd: + clk_put(usbh_clk); +fail_get_usbh_clk: +fail_irq_get: + dev_err(&pdev->dev, "init fail, %d\n", retval); + + return retval; +} + +static int spear_ohci_hcd_drv_remove(struct platform_device *pdev) +{ + struct usb_hcd *hcd = platform_get_drvdata(pdev); + struct spear_ohci *ohci_p = to_spear_ohci(hcd); + + usb_remove_hcd(hcd); + if (ohci_p->clk) + spear_stop_ohci(ohci_p); + + iounmap(hcd->regs); + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + usb_put_hcd(hcd); + + if (ohci_p->clk) + clk_put(ohci_p->clk); + platform_set_drvdata(pdev, NULL); + return 0; +} + +#if defined(CONFIG_PM) +static int spear_ohci_hcd_drv_suspend(struct platform_device *dev, + pm_message_t message) +{ + struct usb_hcd *hcd = platform_get_drvdata(dev); + struct ohci_hcd *ohci = hcd_to_ohci(hcd); + struct spear_ohci *ohci_p = to_spear_ohci(hcd); + + if (time_before(jiffies, ohci->next_statechange)) + msleep(5); + ohci->next_statechange = jiffies; + + spear_stop_ohci(ohci_p); + ohci_to_hcd(ohci)->state = HC_STATE_SUSPENDED; + return 0; +} + +static int spear_ohci_hcd_drv_resume(struct platform_device *dev) +{ + struct usb_hcd *hcd = platform_get_drvdata(dev); + struct ohci_hcd *ohci = hcd_to_ohci(hcd); + struct spear_ohci *ohci_p = to_spear_ohci(hcd); + + if (time_before(jiffies, ohci->next_statechange)) + msleep(5); + ohci->next_statechange = jiffies; + + spear_start_ohci(ohci_p); + ohci_finish_controller_resume(hcd); + return 0; +} +#endif + +/* Driver definition to register with the platform bus */ +static struct platform_driver spear_ohci_hcd_driver = { + .probe = spear_ohci_hcd_drv_probe, + .remove = spear_ohci_hcd_drv_remove, +#ifdef CONFIG_PM + .suspend = spear_ohci_hcd_drv_suspend, + .resume = spear_ohci_hcd_drv_resume, +#endif + .driver = { + .owner = THIS_MODULE, + .name = "spear-ohci", + }, +}; + +MODULE_ALIAS("platform:spear-ohci"); diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index f52d04db28f4..cee867829ec9 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -569,7 +569,7 @@ static int uhci_init(struct usb_hcd *hcd) */ static void uhci_shutdown(struct pci_dev *pdev) { - struct usb_hcd *hcd = (struct usb_hcd *) pci_get_drvdata(pdev); + struct usb_hcd *hcd = pci_get_drvdata(pdev); uhci_hc_died(hcd_to_uhci(hcd)); } |