Thread (16 messages) 16 messages, 6 authors, 2011-07-26
STALE5425d
Revisions (24)
  1. v1 [diff vs current]
  2. v1 [diff vs current]
  3. v1 [diff vs current]
  4. v1 [diff vs current]
  5. v1 [diff vs current]
  6. v1 [diff vs current]
  7. v1 [diff vs current]
  8. v2 [diff vs current]
  9. v2 current
  10. v2 [diff vs current]
  11. v2 [diff vs current]
  12. v2 [diff vs current]
  13. v2 [diff vs current]
  14. v2 [diff vs current]
  15. v3 [diff vs current]
  16. v3 [diff vs current]
  17. v3 [diff vs current]
  18. v3 [diff vs current]
  19. v3 [diff vs current]
  20. v3 [diff vs current]
  21. v3 [diff vs current]
  22. v3 [diff vs current]
  23. v3 [diff vs current]
  24. v4 [diff vs current]

[PATCH v2 5/6] ARM: mxs: add usb phy operations

From: Lothar Waßmann <hidden>
Date: 2011-07-25 07:15:23

Hi,

Tony Lin writes:
quoted hunk ↗ jump to hunk
add usb phy register definitions and functions
usb host driver will use these callback functions
to initialize usb phy and change working mode

Signed-off-by: Tony Lin <redacted>
---
 arch/arm/mach-mxs/Kconfig            |    1 +
 arch/arm/mach-mxs/Makefile           |    1 +
 arch/arm/mach-mxs/mxs_usb.c          |  286 ++++++++++++++++++++++++++++++++++
 arch/arm/mach-mxs/regs-usbphy-mx28.h |  240 ++++++++++++++++++++++++++++
 4 files changed, 528 insertions(+), 0 deletions(-)
diff --git a/arch/arm/mach-mxs/Kconfig b/arch/arm/mach-mxs/Kconfig
index 4cd0231..1c4264f 100644
--- a/arch/arm/mach-mxs/Kconfig
+++ b/arch/arm/mach-mxs/Kconfig
@@ -49,6 +49,7 @@ config MACH_MX28EVK
 	select MXS_HAVE_PLATFORM_MXS_MMC
 	select MXS_HAVE_PLATFORM_MXSFB
 	select MXS_OCOTP
+	select USB_ARCH_HAS_EHCI
 	help
 	  Include support for MX28EVK platform. This includes specific
 	  configurations for the board and its peripherals.
diff --git a/arch/arm/mach-mxs/Makefile b/arch/arm/mach-mxs/Makefile
index 6c38262..726c49f 100644
--- a/arch/arm/mach-mxs/Makefile
+++ b/arch/arm/mach-mxs/Makefile
@@ -12,5 +12,6 @@ obj-$(CONFIG_MACH_MX23EVK) += mach-mx23evk.o
 obj-$(CONFIG_MACH_MX28EVK) += mach-mx28evk.o
 obj-$(CONFIG_MODULE_TX28) += module-tx28.o
 obj-$(CONFIG_MACH_TX28)    += mach-tx28.o
+obj-$(CONFIG_USB_EHCI_MXC)   += mxs_usb.o
 
 obj-y += devices/
diff --git a/arch/arm/mach-mxs/mxs_usb.c b/arch/arm/mach-mxs/mxs_usb.c
new file mode 100644
index 0000000..01753ea
--- /dev/null
+++ b/arch/arm/mach-mxs/mxs_usb.c
@@ -0,0 +1,286 @@
+/*
+ * Copyright (C) 2009-2011 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/fsl_devices.h>
+#include <linux/gpio.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <mach/irqs.h>
+#include <mach/mx28.h>
+#include "regs-usbphy-mx28.h"
+
+/* EHCI registers: */
+#define UOG_USBCMD		(0x140) /* USB command register */
+#define UOG_USBSTS		(0x144) /* USB status register */
+#define UOG_PORTSC1		(0x184) /* port status and control */
+/* x_PORTSCx */
+#define PORTSC_PTS_MASK		(3 << 30) /* parallel xcvr mask */
+#define PORTSC_PTS_UTMI		(0 << 30) /* UTMI/UTMI+ */
+#define PORTSC_PTW		(1 << 28) /* UTMI width */
+/* USBCMD */
+#define UCMD_RUN_STOP           (1 << 0) /* controller run/stop */
+#define UCMD_RESET		(1 << 1) /* controller reset */
+
+#define HOSTPHY_CONNECT_STATE	(1 << 3)
+#define STS_PCD			(1 << 2) /* port change detect */
+
+struct mxs_usb_private_date {
+	struct clk *usb_clk, *usb_phy_clk;
+	int internal_phy_clk_already_on;
+	void __iomem *phy_regs;		/* usb phy register base */
+	void __iomem *ctrl_regs;	/* usb controller register base */
+};
+
+static inline int fsl_platform_get_usb_connect_status
+			(struct mxs_usb_private_date *ppriv)
+{
+	u32 status;
+
+	status = __raw_readl(ppriv->phy_regs + HW_USBPHY_STATUS);
+
+	return ((status & HOSTPHY_CONNECT_STATE) == 0);
+}
+
+/* enable/disable high-speed disconnect detector of phy ctrl */
+static inline void fsl_platform_disconnect_detect
+			(struct mxs_usb_private_date *ppriv, int enable)
+{
+	if (enable) {
+		__raw_writel(BM_USBPHY_CTRL_ENHOSTDISCONDETECT,
+		ppriv->phy_regs + HW_USBPHY_CTRL_SET);
+	} else {
+		__raw_writel(BM_USBPHY_CTRL_ENHOSTDISCONDETECT,
+		ppriv->phy_regs + HW_USBPHY_CTRL_CLR);
+	}
+}
+
+static void fsl_plt_usbh_irq_handler(struct mxc_usbh_platform_data *pdata)
+{
+	u32			status;
+	struct mxs_usb_private_date *ppriv = pdata->ppriv;
+
+	status = __raw_readl(ppriv->ctrl_regs + UOG_USBSTS);
+
+	if (status & STS_PCD)
+		fsl_platform_disconnect_detect(ppriv,
+			fsl_platform_get_usb_connect_status(ppriv));
+}
+
+static int usb_phy_enable(struct mxc_usbh_platform_data *pdata)
+{
+	u32 tmp;
+	u32 i = 0;
+	struct mxs_usb_private_date *ppriv = pdata->ppriv;
+	void __iomem *usbcmd, *phy_ctrl, *portsc;
+
+	/* Reset USB IP */
+	/* Set run stop bit */
+	/* Send reset command */
+	usbcmd = ppriv->ctrl_regs + UOG_USBCMD;
+	tmp = __raw_readl(usbcmd); /* usb command */
+	tmp &= ~UCMD_RUN_STOP;
+	__raw_writel(tmp, usbcmd);
+	while (__raw_readl(usbcmd) & UCMD_RUN_STOP) {
+		i++;
+		if (i == 1000)
+			break;
+		mdelay(1);
+	}
+	tmp |= UCMD_RESET;
+	__raw_writel(tmp, usbcmd);
+	i = 0;
+	while (__raw_readl(usbcmd) & UCMD_RESET) {
+		i++;
+		if (i == 1000)
+			break;
+		mdelay(1);
+	}
+	mdelay(10);
+
+	/* Reset USBPHY module, set soft reset bit */
+	phy_ctrl = ppriv->phy_regs + HW_USBPHY_CTRL;
+	tmp = __raw_readl(phy_ctrl);
+	tmp |= BM_USBPHY_CTRL_SFTRST;
+	__raw_writel(tmp, phy_ctrl);
+	udelay(10);
+
+	/* clear CLKGATE and SFTRST bits to be out of reset mode*/
+	tmp = __raw_readl(phy_ctrl);
+	tmp &= ~(BM_USBPHY_CTRL_CLKGATE | BM_USBPHY_CTRL_SFTRST);
+	__raw_writel(tmp, phy_ctrl);
+	udelay(10);
+
+	/* set UTMI xcvr */
+	/* Workaround an IC issue for ehci driver:
+	 * when turn off root hub port power, EHCI set
+	 * PORTSC reserved bits to be 0, but PTW with 0
+	 * means 8 bits tranceiver width, here change
+	 * it back to be 16 bits and do PHY diable and
+	 * then enable.
+	 */
+	portsc = ppriv->ctrl_regs + UOG_PORTSC1;
+	tmp = __raw_readl(portsc);
+	tmp &=  ~PORTSC_PTS_MASK;
+	tmp |= (PORTSC_PTS_UTMI | PORTSC_PTW);
+	__raw_writel(tmp, portsc);
+
+	/* Power up the PHY */
+	__raw_writel(0, ppriv->phy_regs + HW_USBPHY_PWD);
+	return 0;
+}
+
+static int fsl_usbh_init(struct platform_device *pdev)
+{
+	struct mxc_usbh_platform_data *pdata = pdev->dev.platform_data;
+	struct mxs_usb_private_date *ppriv = pdata->ppriv;
+	u32 tmp;
+
+	usb_phy_enable(pdata);
+	/* enable FS/LS device */
+	tmp = __raw_readl(ppriv->phy_regs + HW_USBPHY_CTRL);
+	tmp |= (BM_USBPHY_CTRL_ENUTMILEVEL2 | BM_USBPHY_CTRL_ENUTMILEVEL3);
+	__raw_writel(tmp, ppriv->phy_regs + HW_USBPHY_CTRL);
+
+	return 0;
+}
+
+static void phy_clock_gate(struct mxs_usb_private_date *ppriv, bool on)
+{
+	u32 tmp;
+
+	if (on) {
+		ppriv->internal_phy_clk_already_on += 1;
+		if (ppriv->internal_phy_clk_already_on == 1) {
+			tmp = BM_USBPHY_CTRL_SFTRST | BM_USBPHY_CTRL_CLKGATE;
+			__raw_writel(tmp, ppriv->phy_regs + HW_USBPHY_CTRL_CLR);
+		}
+	} else {
+		ppriv->internal_phy_clk_already_on -= 1;
+		if (ppriv->internal_phy_clk_already_on == 0) {
+			tmp = BM_USBPHY_CTRL_CLKGATE;
+			__raw_writel(tmp, ppriv->phy_regs + HW_USBPHY_CTRL_SET);
+		}
+	}
+	if (WARN_ON(ppriv->internal_phy_clk_already_on < 0))
+		printk(KERN_ERR "please check phy clock ON/OFF sequence\n");
+}
+static int fsl_usb_host_init(struct platform_device *pdev)
+{
+	struct mxc_usbh_platform_data *pdata = pdev->dev.platform_data;
+	struct mxs_usb_private_date *ppriv = pdata->ppriv;
+
+	ppriv->phy_regs = ioremap(MX28_USBPHY1_BASE_ADDR, SZ_8K);
+	if (ppriv->phy_regs == NULL)
+		return -ENOMEM;
+
+	ppriv->ctrl_regs = ioremap(MX28_USBCTRL1_BASE_ADDR, SZ_8K);
+	if (ppriv->ctrl_regs == NULL)
+		return -ENOMEM;
+
What about proper cleanup in the error case?
+	ppriv->usb_clk = clk_get(&pdev->dev, "usb1");
+	if (IS_ERR(ppriv->usb_clk))
+		return PTR_ERR(ppriv->usb_clk);
dto.
+	clk_enable(ppriv->usb_clk);
+
+	ppriv->usb_phy_clk = clk_get(&pdev->dev, "usb1_phy");
+	if (IS_ERR(ppriv->usb_phy_clk))
+		return PTR_ERR(ppriv->usb_phy_clk);
dto.
+	clk_enable(ppriv->usb_phy_clk);
+
+	phy_clock_gate(ppriv, true);
+	return fsl_usbh_init(pdev);
+}
+
+static int fsl_usb_host_uninit(struct platform_device *pdev)
+{
+	struct mxc_usbh_platform_data *pdata = pdev->dev.platform_data;
+	struct mxs_usb_private_date *ppriv = pdata->ppriv;
+
+	phy_clock_gate(ppriv, false);
+	if (ppriv->usb_phy_clk) {

+		clk_disable(ppriv->usb_phy_clk);
+		clk_put(ppriv->usb_phy_clk);
+	}
+	if (ppriv->usb_clk) {
+		clk_disable(ppriv->usb_clk);
+		clk_put(ppriv->usb_clk);
+	}
+	if (ppriv->phy_regs)
+		iounmap(ppriv->phy_regs);
+	if (ppriv->ctrl_regs)
+		iounmap(ppriv->ctrl_regs);
+
The 'if' clauses are redundant here. fsl_usb_host_uninit() will only
be called when fsl_usb_host_init() has succeeded and thus all pointers
have been set up correctly (which makes it necessary to have proper
cleanup code in fsl_usb_host_init()).


Lothar Wa?mann
-- 
___________________________________________________________

Ka-Ro electronics GmbH | Pascalstra?e 22 | D - 52076 Aachen
Phone: +49 2408 1402-0 | Fax: +49 2408 1402-10
Gesch?ftsf?hrer: Matthias Kaussen
Handelsregistereintrag: Amtsgericht Aachen, HRB 4996

www.karo-electronics.de | info at karo-electronics.de
___________________________________________________________
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help