Signed-off-by: Igor Grinberg <grinberg@xxxxxxxxxxxxxx> Signed-off-by: Mike Rapoport <mike@xxxxxxxxxxxxxx> --- arch/arm/mach-pxa/include/mach/ohci.h | 3 + arch/arm/mach-pxa/include/mach/regs-u2d.h | 2 + drivers/usb/host/ohci-pxa27x.c | 183 +++++++++++++++++++++++++++++ 3 files changed, 188 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-pxa/include/mach/ohci.h b/arch/arm/mach-pxa/include/mach/ohci.h index 95b6e2a..a543d0f 100644 --- a/arch/arm/mach-pxa/include/mach/ohci.h +++ b/arch/arm/mach-pxa/include/mach/ohci.h @@ -29,6 +29,9 @@ struct pxaohci_platform_data { #define PMM_PERPORT_MODE 3 int power_budget; + + struct otg_transceiver *otg; + unsigned long ulpi_mode; }; extern void pxa_set_ohci_info(struct pxaohci_platform_data *info); diff --git a/arch/arm/mach-pxa/include/mach/regs-u2d.h b/arch/arm/mach-pxa/include/mach/regs-u2d.h index c15c0c5..82597e7 100644 --- a/arch/arm/mach-pxa/include/mach/regs-u2d.h +++ b/arch/arm/mach-pxa/include/mach/regs-u2d.h @@ -3,6 +3,8 @@ #include <mach/bitfield.h> +#define U2D_PHYS_BASE 0x54100000 + /* * USB2 device controller registers and bits definitions */ diff --git a/drivers/usb/host/ohci-pxa27x.c b/drivers/usb/host/ohci-pxa27x.c index a18debd..c4aa239 100644 --- a/drivers/usb/host/ohci-pxa27x.c +++ b/drivers/usb/host/ohci-pxa27x.c @@ -24,6 +24,8 @@ #include <linux/platform_device.h> #include <linux/clk.h> #include <mach/ohci.h> +#include <mach/regs-u2d.h> +#include <mach/pxa310-ulpi.h> /* * UHC: USB Host Controller (OHCI-like) register definitions @@ -104,10 +106,126 @@ struct pxa27x_ohci { struct device *dev; struct clk *clk; void __iomem *mmio_base; + + struct clk *u2d_clk; + void __iomem *u2d_mmio_base; }; #define to_pxa27x_ohci(hcd) (struct pxa27x_ohci *)hcd_to_ohci(hcd) +#if defined(CONFIG_PXA310_ULPI) +static void pxa310_setup_otg_hc(struct pxa27x_ohci *ohci) +{ + void __iomem *u2d_mmio_base = ohci->u2d_mmio_base; + u32 u2dotgcr; + + u2dotgcr = __raw_readl(u2d_mmio_base + U2DOTGCR); + u2dotgcr |= U2DOTGCR_ULAF | U2DOTGCR_UTMID; + u2dotgcr &= ~(U2DOTGCR_SMAF | U2DOTGCR_CKAF); + __raw_writel(u2dotgcr, u2d_mmio_base + U2DOTGCR); + msleep(5); + __raw_writel(u2dotgcr | U2DOTGCR_ULE, u2d_mmio_base + U2DOTGCR); + msleep(5); + __raw_writel(__raw_readl(u2d_mmio_base + U2DOTGICR) & ~0x37F7F, + u2d_mmio_base + U2DOTGICR); +} + +static void pxa310_otg_transceiver_rtsm(struct pxa27x_ohci *ohci) +{ + void __iomem *u2d_mmio_base = ohci->u2d_mmio_base; + u32 u2dotgcr; + + /* put PHY to sync mode */ + u2dotgcr = __raw_readl(u2d_mmio_base + U2DOTGCR); + u2dotgcr |= U2DOTGCR_RTSM | U2DOTGCR_UTMID; + __raw_writel(u2dotgcr, u2d_mmio_base + U2DOTGCR); + msleep(10); + /* setup OTG sync mode */ + u2dotgcr = __raw_readl(u2d_mmio_base + U2DOTGCR); + u2dotgcr |= U2DOTGCR_ULAF; + u2dotgcr &= ~(U2DOTGCR_SMAF | U2DOTGCR_CKAF); + __raw_writel(u2dotgcr, u2d_mmio_base + U2DOTGCR); +} + +static int start_otg_transceiver(struct pxa27x_ohci *ohci, + struct otg_transceiver *otg) +{ + int err; + + err = otg_init(otg); + if (err) { + pr_err("OTG transceiver init failed"); + return err; + } + + err = otg_set_vbus(otg, 1); + if (err) { + pr_err("OTG transceiver VBUS set failed"); + return err; + } + + err = otg_set_host(otg, &ohci_to_hcd(&ohci->ohci)->self); + if (err) + pr_err("OTG transceiver Host mode set failed"); + + return err; +} + +static int pxa310_start_otg_hc(struct pxa27x_ohci *ohci, + struct pxaohci_platform_data *inf) +{ + void __iomem *u2d_mmio_base = ohci->u2d_mmio_base; + u32 u2dotgcr; + int err; + + pxa310_otg_transceiver_rtsm(ohci); + + /* disable USB device controller */ + __raw_writel(__raw_readl(u2d_mmio_base + U2DCR) & ~U2DCR_UDE, + u2d_mmio_base + U2DCR); + __raw_writel(__raw_readl(u2d_mmio_base + U2DOTGCR) | + U2DOTGCR_UTMID, u2d_mmio_base + U2DOTGCR); + __raw_writel(__raw_readl(u2d_mmio_base + U2DOTGICR) & ~0x37F7F, + u2d_mmio_base + U2DOTGICR); + + err = start_otg_transceiver(ohci, inf->otg); + if (err) + return err; + + /* set xceiver mode */ + if (inf->ulpi_mode == ULPI_IC_6PIN) + __raw_writel(__raw_readl(u2d_mmio_base + U2DP3CR) & + ~U2DP3CR_P2SS, u2d_mmio_base + U2DP3CR); + else if (inf->ulpi_mode == ULPI_IC_3PIN) + __raw_writel(__raw_readl(u2d_mmio_base + U2DP3CR) | + U2DP3CR_P2SS, u2d_mmio_base + U2DP3CR); + + /* start OTG host controller */ + u2dotgcr =__raw_readl(u2d_mmio_base + U2DOTGCR) | U2DOTGCR_SMAF; + __raw_writel(u2dotgcr & ~(U2DOTGCR_ULAF | U2DOTGCR_CKAF), + u2d_mmio_base + U2DOTGCR); + + return 0; +} + +static inline bool pxa310_u2d_is_setup(struct pxa27x_ohci *ohci) +{ + return cpu_is_pxa310() && !IS_ERR(ohci->u2d_clk) && ohci->u2d_mmio_base; +} +#else +static inline void pxa310_setup_otg_hc(struct pxa27x_ohci *ohci) {} +static inline void pxa310_otg_transceiver_rtsm(struct pxa27x_ohci *ohci) {} +static inline int pxa310_start_otg_hc(struct pxa27x_ohci *ohci, + struct pxaohci_platform_data *inf) +{ + return 0; +} +static inline bool pxa310_u2d_is_setup(struct pxa27x_ohci *ohci) +{ + return false; +} +#endif /* CONFIG_PXA310_ULPI */ + /* PMM_NPS_MODE -- PMM Non-power switching mode Ports are powered continuously. @@ -229,12 +347,24 @@ static int pxa27x_start_hc(struct pxa27x_ohci *ohci, struct device *dev) pxa27x_setup_hc(ohci, inf); + if (pxa310_u2d_is_setup(ohci)) { + clk_enable(ohci->u2d_clk); + pxa310_setup_otg_hc(ohci); + } + if (inf->init) retval = inf->init(dev); if (retval < 0) return retval; + /* + * Platform can have a special init for the ulpi phy, + * therefore the rest of the init must be done after the platform code. + */ + if (pxa310_u2d_is_setup(ohci)) + pxa310_start_otg_hc(ohci, inf); + uhchr = __raw_readl(ohci->mmio_base + UHCHR) & ~UHCHR_SSE; __raw_writel(uhchr, ohci->mmio_base + UHCHR); __raw_writel(UHCHIE_UPRIE | UHCHIE_RWIE, ohci->mmio_base + UHCHIE); @@ -251,6 +381,11 @@ static void pxa27x_stop_hc(struct pxa27x_ohci *ohci, struct device *dev) inf = dev->platform_data; + if (pxa310_u2d_is_setup(ohci)) { + pxa310_otg_transceiver_rtsm(ohci); + clk_disable(ohci->u2d_clk); + } + if (inf->exit) inf->exit(dev); @@ -264,6 +399,38 @@ static void pxa27x_stop_hc(struct pxa27x_ohci *ohci, struct device *dev) clk_disable(ohci->clk); } +static int pxa310_init_otg_resources(struct pxa27x_ohci *ohci, + struct pxaohci_platform_data *inf) +{ + int retval; + + ohci->u2d_clk = clk_get(NULL, "U2DCLK"); + if (IS_ERR(ohci->u2d_clk)) + return PTR_ERR(ohci->u2d_clk); + + inf->otg->io_priv = ioremap(U2D_PHYS_BASE, 0x1300); + if (!inf->otg->io_priv) { + retval = -ENOMEM; + goto err_ioremap; + } + + ohci->u2d_mmio_base = inf->otg->io_priv; + + return 0; + +err_ioremap: + clk_put(ohci->u2d_clk); + return retval; +} + +static void pxa310_free_otg_resources(struct pxa27x_ohci *ohci) +{ + if (ohci->u2d_mmio_base) + iounmap(ohci->u2d_mmio_base); + + if (ohci->u2d_clk) + clk_put(ohci->u2d_clk); +} /*-------------------------------------------------------------------------*/ @@ -337,6 +504,12 @@ int usb_hcd_pxa27x_probe (const struct hc_driver *driver, struct platform_device ohci->clk = usb_clk; ohci->mmio_base = (void __iomem *)hcd->regs; + if (cpu_is_pxa310() && inf->otg) { + retval = pxa310_init_otg_resources(ohci, inf); + if (retval) + goto err3; + } + if ((retval = pxa27x_start_hc(ohci, &pdev->dev)) < 0) { pr_debug("pxa27x_start_hc failed"); goto err3; @@ -357,11 +530,15 @@ int usb_hcd_pxa27x_probe (const struct hc_driver *driver, struct platform_device pxa27x_stop_hc(ohci, &pdev->dev); err3: iounmap(hcd->regs); + + if (cpu_is_pxa310() && inf->otg) + pxa310_free_otg_resources(ohci); err2: release_mem_region(hcd->rsrc_start, hcd->rsrc_len); err1: usb_put_hcd(hcd); clk_put(usb_clk); + return retval; } @@ -385,6 +562,12 @@ void usb_hcd_pxa27x_remove (struct usb_hcd *hcd, struct platform_device *pdev) usb_remove_hcd(hcd); pxa27x_stop_hc(ohci, &pdev->dev); + + if (pxa310_u2d_is_setup(ohci)) { + iounmap(ohci->u2d_mmio_base); + clk_put(ohci->u2d_clk); + } + iounmap(hcd->regs); release_mem_region(hcd->rsrc_start, hcd->rsrc_len); usb_put_hcd(hcd); -- 1.6.4.4 -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html