[PATCH 1/2] [ARM] pxa: add minimal ULPI functionality for USB host port 2 on PXA310.

Daniel Mack daniel at caiaq.de
Tue Apr 13 18:06:42 EDT 2010


On Wed, Apr 07, 2010 at 06:05:22PM +0300, Igor Grinberg wrote:
> 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

The USB OTG/ULPI framework could well be reused for that I think. All
that you would need is a struct otg_io_access_ops with two function
pointers to read and write registers. Then you can use otg_ulpi_create()
to set up an ULPI compliant device connected to the bus.

Have a look at the MXC code, I believe adopting that framework is better
than re-inventing it.

Daniel



> 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
> 
> 
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel



More information about the linux-arm-kernel mailing list