[PATCH 1/2] [ARM] pxa: add minimal ULPI functionality for USB host port 2 on PXA310.
Igor Grinberg
grinberg at compulab.co.il
Wed Apr 7 11:05:22 EDT 2010
Signed-off-by: Igor Grinberg <grinberg at compulab.co.il>
Signed-off-by: Mike Rapoport <mike at compulab.co.il>
---
arch/arm/mach-pxa/Makefile | 1 +
arch/arm/mach-pxa/pxa310-ulpi.c | 240 +++++++++++++++++++++++++++++++++++++++
arch/arm/mach-pxa/pxa310-ulpi.h | 85 ++++++++++++++
3 files changed, 326 insertions(+), 0 deletions(-)
create mode 100644 arch/arm/mach-pxa/pxa310-ulpi.c
create mode 100644 arch/arm/mach-pxa/pxa310-ulpi.h
diff --git a/arch/arm/mach-pxa/Makefile b/arch/arm/mach-pxa/Makefile
index 86bc87b..76d52f4 100644
--- a/arch/arm/mach-pxa/Makefile
+++ b/arch/arm/mach-pxa/Makefile
@@ -21,6 +21,7 @@ obj-$(CONFIG_PXA25x) += mfp-pxa2xx.o pxa2xx.o pxa25x.o
obj-$(CONFIG_PXA27x) += mfp-pxa2xx.o pxa2xx.o pxa27x.o
obj-$(CONFIG_PXA3xx) += mfp-pxa3xx.o pxa3xx.o smemc.o
obj-$(CONFIG_CPU_PXA300) += pxa300.o
+obj-$(CONFIG_CPU_PXA310) += pxa310-ulpi.o
obj-$(CONFIG_CPU_PXA320) += pxa320.o
obj-$(CONFIG_CPU_PXA930) += pxa930.o
diff --git a/arch/arm/mach-pxa/pxa310-ulpi.c b/arch/arm/mach-pxa/pxa310-ulpi.c
new file mode 100644
index 0000000..ab82db8
--- /dev/null
+++ b/arch/arm/mach-pxa/pxa310-ulpi.c
@@ -0,0 +1,240 @@
+/*
+ * linux/arch/arm/mach-pxa/pxa310-ulpi.c
+ *
+ * PXA310 ULPI USB host implementation.
+ *
+ * Copyright (C) 2010 CompuLab Ltd.
+ *
+ * Igor Grinberg <grinberg at compulab.co.il>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+
+#include <mach/regs-u2d.h>
+
+#include "pxa310-ulpi.h"
+
+static void __iomem *u2d_mmio_base;
+
+static inline unsigned int u2d_readl(unsigned int off)
+{
+ return __raw_readl(u2d_mmio_base + off);
+}
+
+static inline void u2d_writel(unsigned int off, unsigned int val)
+{
+ __raw_writel(val, u2d_mmio_base + off);
+}
+
+static inline enum u2d_phy_mode ulpi_get_phymode(void)
+{
+ return (u2d_readl(U2DOTGUSR) & 0xF0000000) >> 28;
+}
+
+static bool ulpi_is_in_sync_mode(void)
+{
+ enum u2d_phy_mode state = ulpi_get_phymode();
+
+ return (state == SYNCH) || (state == PRE_SYNCH);
+}
+
+static int ulpi_poll(void)
+{
+ int timeout = 50000;
+
+ while (timeout--) {
+ if (!(u2d_readl(U2DOTGUCR) & U2DOTGUCR_RUN))
+ return 0;
+
+ cpu_relax();
+ }
+
+ pr_warning("%s: ULPI access timed out!\n", __func__);
+
+ return -ETIMEDOUT;
+}
+
+static int ulpi_reg_read(u8 reg, u8 *value)
+{
+ int err = 0;
+
+ if (!ulpi_is_in_sync_mode()) {
+ pr_warning("%s: PHY is not in SYNCH mode!\n", __func__);
+ return -EBUSY;
+ }
+
+ u2d_writel(U2DOTGUCR,
+ U2DOTGUCR_RUN | U2DOTGUCR_RNW | (reg << U2DOTGUCR_ADDR_S));
+ msleep(5);
+
+ err = ulpi_poll();
+ if (err)
+ return err;
+
+ *value = (u8)(u2d_readl(U2DOTGUCR) & U2DOTGUCR_RDATA);
+
+ return 0;
+}
+
+static int ulpi_reg_write(u8 reg, u8 value)
+{
+ int err;
+
+ if (!ulpi_is_in_sync_mode()) {
+ pr_warning("%s: PHY is not in SYNCH mode!\n", __func__);
+ return -EBUSY;
+ }
+
+ u2d_writel(U2DOTGUCR, U2DOTGUCR_RUN | (reg << U2DOTGUCR_ADDR_S)
+ | (value << U2DOTGUCR_WDATA_S));
+ msleep(5);
+
+ err = ulpi_poll();
+ if (err)
+ return err;
+
+ return 0;
+}
+
+static int u2d_clk_enable(void)
+{
+ int err;
+ struct clk *u2d_clk;
+
+ u2d_clk = clk_get(NULL, "U2DCLK");
+ if (IS_ERR(u2d_clk)) {
+ err = PTR_ERR(u2d_clk);
+ pr_err("%s: failed to get U2DCLK: %d\n", __func__, err);
+ return err;
+ }
+ clk_enable(u2d_clk);
+
+ return 0;
+}
+
+static void u2d_otg_host_init(void)
+{
+ u32 u2dotgcr;
+
+ /* setup OTG controller registers */
+ u2dotgcr = u2d_readl(U2DOTGCR);
+ u2dotgcr |= U2DOTGCR_ULAF | U2DOTGCR_UTMID;
+ u2dotgcr &= ~(U2DOTGCR_SMAF | U2DOTGCR_CKAF);
+ u2d_writel(U2DOTGCR, u2dotgcr);
+ msleep(5);
+ u2d_writel(U2DOTGCR, u2dotgcr | U2DOTGCR_ULE);
+ msleep(5);
+
+ /* no OTG interrupts in host mode */
+ u2d_writel(U2DOTGICR, u2d_readl(U2DOTGICR) & ~U2DOTGICR_MASK);
+}
+
+static void u2d_ulpi_rtsm(void)
+{
+ u32 u2dotgcr;
+
+ /* put PHY to sync mode */
+ u2d_writel(U2DOTGCR, u2d_readl(U2DOTGCR) | U2DOTGCR_RTSM);
+ msleep(10);
+
+ /* setup OTG sync mode */
+ u2dotgcr = u2d_readl(U2DOTGCR) | U2DOTGCR_ULAF;
+ u2dotgcr &= ~(U2DOTGCR_SMAF | U2DOTGCR_CKAF);
+ u2d_writel(U2DOTGCR, u2dotgcr);
+}
+
+static void u2d_disable_otg_device(void)
+{
+ /* disable USB2 Device controller */
+ u2d_writel(U2DCR, u2d_readl(U2DCR) & ~U2DCR_UDE);
+ u2d_writel(U2DOTGCR, u2d_readl(U2DOTGCR) | U2DOTGCR_UTMID);
+
+ /* disable the otg interrupts */
+ u2d_writel(U2DOTGICR, u2d_readl(U2DOTGICR) & ~U2DOTGICR_MASK);
+}
+
+static int u2d_set_ulpi_serial(enum u2d_phy_mode mode)
+{
+ int err = 0;
+ u32 u2dotgcr;
+
+ /* enable the 15K Ohm pull-down resistor on D+ */
+ err = ulpi_reg_write(ULPI_OTG_CONTROL_SET, ULPI_OC_DPPULLDOWN);
+ if (err)
+ return err;
+
+ /* enable the 15K Ohm pull-down resistor on D- */
+ err = ulpi_reg_write(ULPI_OTG_CONTROL_SET, ULPI_OC_DMPULLDOWN);
+ if (err)
+ return err;
+
+ /* drive 5V on VBUS */
+ err = ulpi_reg_write(ULPI_OTG_CONTROL_SET, ULPI_OC_DRVVBUS);
+ if (err)
+ return err;
+
+ /* configure UHC for 6/3-pin serial mode */
+ if (mode == SER_6PIN)
+ u2d_writel(U2DP3CR, u2d_readl(U2DP3CR) & ~U2DP3CR_P2SS);
+ else if (mode == SER_3PIN)
+ u2d_writel(U2DP3CR, u2d_readl(U2DP3CR) | U2DP3CR_P2SS);
+
+ /* put PHY into host mode */
+ err = ulpi_reg_write(ULPI_FUNCTION_CONTROL_SET, 0x45);
+ if (err)
+ return err;
+
+ /* set PHY to serial 6/3 pin mode */
+ if (mode == SER_6PIN)
+ err = ulpi_reg_write(ULPI_INTERFACE_CONTROL, ULPI_IC_6PIN);
+ else if (mode == SER_3PIN)
+ err = ulpi_reg_write(ULPI_INTERFACE_CONTROL, ULPI_IC_3PIN);
+ if (err)
+ return err;
+
+ /* enable the serial mode */
+ u2dotgcr = u2d_readl(U2DOTGCR) | U2DOTGCR_SMAF;
+ u2d_writel(U2DOTGCR, u2dotgcr & ~(U2DOTGCR_ULAF | U2DOTGCR_CKAF));
+
+ return 0;
+}
+
+int pxa310_ulpi_init(enum u2d_phy_mode mode)
+{
+ int err;
+
+ err = u2d_clk_enable();
+ if (err)
+ goto err_out;
+
+ u2d_mmio_base = ioremap(U2D_PHYS_BASE, 0x1300);
+ if (u2d_mmio_base == NULL) {
+ pr_err("%s: failed to remap U2D registers address space\n",
+ __func__);
+ err = -ENOMEM;
+ goto err_out;
+ }
+
+ u2d_otg_host_init();
+ u2d_ulpi_rtsm();
+ u2d_disable_otg_device();
+
+ err = u2d_set_ulpi_serial(mode);
+ if (err)
+ goto err_out;
+
+ return 0;
+
+err_out:
+ pr_err("%s: ULPI init failed: %d\n", __func__, err);
+ return err;
+}
diff --git a/arch/arm/mach-pxa/pxa310-ulpi.h b/arch/arm/mach-pxa/pxa310-ulpi.h
new file mode 100644
index 0000000..87558f0
--- /dev/null
+++ b/arch/arm/mach-pxa/pxa310-ulpi.h
@@ -0,0 +1,85 @@
+/*
+ * linux/arch/arm/mach-pxa/pxa310-ulpi.h
+ *
+ * PXA310 ULPI USB host header file.
+ *
+ * Copyright (C) 2010 CompuLab Ltd.
+ *
+ * Igor Grinberg <grinberg at compulab.co.il>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef __PXA310_ULPI__
+#define __PXA310_ULPI__
+
+/* OTG Interrupts */
+#define U2DOTGICR_MASK 0x37F7F
+
+/* PXA310 USB OTG Controller */
+#define ULPI_VENDOR_LOW 0x0
+#define ULPI_VENDOR_HIGH 0x1
+#define ULPI_PRODUCT_LOW 0x2
+#define ULPI_PRODUCT_HIGH 0x3
+#define ULPI_FUNCTION_CONTROL 0x4
+#define ULPI_FUNCTION_CONTROL_SET 0x5
+#define ULPI_FUNCTION_CONTROL_CLEAR 0x6
+#define ULPI_INTERFACE_CONTROL 0x7
+#define ULPI_INTERFACE_CONTROL_SET 0x8
+#define ULPI_INTERFACE_CONTROL_CLEAR 0x9
+#define ULPI_OTG_CONTROL 0xA
+#define ULPI_OTG_CONTROL_SET 0xB
+#define ULPI_OTG_CONTROL_CLEAR 0xC
+#define ULPI_INT_RISE 0xD
+#define ULPI_INT_RISE_SET 0xE
+#define ULPI_INT_RISE_CLEAR 0xF
+#define ULPI_INT_FALL 0x10
+#define ULPI_INT_FALL_SET 0x11
+#define ULPI_INT_FALL_CLEAR 0x12
+#define ULPI_INT_STATUS 0x13
+#define ULPI_INT_LATCH 0x14
+#define ULPI_DEBUG 0x15
+#define ULPI_SCRATCH 0x16
+#define ULPI_SCRATCH_SET 0x17
+#define ULPI_SCRATCH_CLEAR 0x18
+
+#define ULPI_FC_RESET (1 << 5) /* XCVR Reset */
+#define ULPI_FC_SUSPENDM (1 << 6) /* XCVR SuspendM, Low Power Mode */
+
+#define ULPI_IC_6PIN (1 << 0) /* XCVR 6 pin mode */
+#define ULPI_IC_3PIN (1 << 1) /* XCVR 3 pin mode */
+#define ULPI_IC_CARKIT (1 << 2) /* Carkit mode */
+#define ULPI_IC_CLKSUSPENDM (1 << 3) /* Active low clock suspend */
+
+#define ULPI_OC_IDPULLUP (1 << 0) /* ID Pull Up, enable sampling of ID line */
+#define ULPI_OC_DPPULLDOWN (1 << 1) /* Enable the 15K Ohm pull down resistor on D+ */
+#define ULPI_OC_DMPULLDOWN (1 << 2) /* Enable the 15K Ohm pull down resistor on D- */
+#define ULPI_OC_DISCHRGVBUS (1 << 3) /* Discharge Vbus */
+#define ULPI_OC_CHRGVBUS (1 << 4) /* Charge Vbus, for Vbus pulsing SRP */
+#define ULPI_OC_DRVVBUS (1 << 5) /* Drive 5V on Vbus */
+#define ULPI_OC_DRVVBUSEXT (1 << 6) /* Drive Vbus using external supply */
+
+#define ULPI_INT_HOSTDISCON (1 << 0) /* Host Disconnect */
+#define ULPI_INT_VBUSVALID (1 << 1) /* Vbus Valid */
+#define ULPI_INT_SESSIONVALID (1 << 2) /* Session Valid */
+#define ULPI_INT_SESSIONEND (1 << 3) /* Session End */
+#define ULPI_INT_IDGND (1 << 4) /* current status of IDGND */
+
+#define U2DOTGUCR_ADDR_S (16)
+#define U2DOTGUCR_WDATA_S (8)
+
+#define U2D_PHYS_BASE 0x54100000
+
+enum u2d_phy_mode {
+ SYNCH = 0,
+ CARKIT = (1 << 0),
+ SER_3PIN = (1 << 1),
+ SER_6PIN = (1 << 2),
+ LOWPOWER = (1 << 3),
+ PRE_SYNCH = (1 << 4),
+};
+
+int pxa310_ulpi_init(enum u2d_phy_mode);
+
+#endif /* __PXA310_ULPI__ */
--
1.6.4.4
More information about the linux-arm-kernel
mailing list