[U-Boot] [PATCH v3 2/2] tegra20: add USB ULPI init code

Igor Grinberg grinberg at compulab.co.il
Fri Sep 28 10:24:06 CEST 2012


On 09/26/12 00:35, Lucas Stach wrote:
> This adds the required code to set up a ULPI USB port. It is
> mostly a port of the Linux ULPI setup code with some tweaks
> added for more correctness, discovered along the way of
> debugging this.
> 
> To use this both CONFIG_USB_ULPI and CONFIG_USB_ULPI_VIEWPORT
> have to be set in the board configuration file.
> 
> v2:
> - move all controller init stuff in the respective functions to
>   make them self contained
> - let board define ULPI_REF_CLK to account for the possibility
>   that some ULPI phys need a other ref clk than 24MHz
> - don't touch ULPI regs directly, use ULPI framework functions
> - don't hide error messages under debug()
> 
> v3:
> - apply last comments from Igor, which make code still a bit cleaner
> - add description of CONFIG_ULPI_REF_CLK to README

I think the above (change history) belongs under the scissors line ('---').

> 
> Signed-off-by: Lucas Stach <dev at lynxeye.de>

Apart from some (this time really) minor comments above and below:
Acked-by: Igor Grinberg <grinberg at compulab.co.il>

> ---
>  README                                  |   3 +
>  arch/arm/cpu/armv7/tegra20/usb.c        | 152 +++++++++++++++++++++++++++-----
>  arch/arm/include/asm/arch-tegra20/usb.h |  29 ++++--
>  3 Dateien geändert, 157 Zeilen hinzugefügt(+), 27 Zeilen entfernt(-)
> 
> diff --git a/README b/README
> index 4428205..3919143 100644
> --- a/README
> +++ b/README
> @@ -1239,6 +1239,9 @@ The following options need to be configured:
>  		viewport is supported.
>  		To enable the ULPI layer support, define CONFIG_USB_ULPI and
>  		CONFIG_USB_ULPI_VIEWPORT in your board configuration file.
> +		If your ULPI phy needs a different reference clock than the
> +		standard 24 MHz then you have to define CONFIG_ULPI_REF_CLK to
> +		the appropiate value in Hz.

s/appropiate/appropriate/

>  
>  - MMC Support:
>  		The MMC controller on the Intel PXA is supported. To
> diff --git a/arch/arm/cpu/armv7/tegra20/usb.c b/arch/arm/cpu/armv7/tegra20/usb.c
> index cac0918..86e52f6 100644
> --- a/arch/arm/cpu/armv7/tegra20/usb.c
> +++ b/arch/arm/cpu/armv7/tegra20/usb.c
> @@ -32,9 +32,17 @@
>  #include <asm/arch/sys_proto.h>
>  #include <asm/arch/uart.h>
>  #include <asm/arch/usb.h>
> +#include <usb/ulpi.h>
>  #include <libfdt.h>
>  #include <fdtdec.h>
>  
> +#ifdef CONFIG_USB_ULPI
> +	#ifndef CONFIG_USB_ULPI_VIEWPORT
> +	#error	"To use CONFIG_USB_ULPI on Tegra Boards you have to also \
> +			define CONFIG_USB_ULPI_VIEWPORT"
> +	#endif
> +#endif
> +
>  enum {
>  	USB_PORTS_MAX	= 4,			/* Maximum ports we allow */
>  };
> @@ -68,11 +76,13 @@ enum dr_mode {
>  struct fdt_usb {
>  	struct usb_ctlr *reg;	/* address of registers in physical memory */
>  	unsigned utmi:1;	/* 1 if port has external tranceiver, else 0 */
> +	unsigned ulpi:1;	/* 1 if port has external ULPI transceiver */
>  	unsigned enabled:1;	/* 1 to enable, 0 to disable */
>  	unsigned has_legacy_mode:1; /* 1 if this port has legacy mode */
>  	enum dr_mode dr_mode;	/* dual role mode */
>  	enum periph_id periph_id;/* peripheral id */
>  	struct fdt_gpio_state vbus_gpio;	/* GPIO for vbus enable */
> +	struct fdt_gpio_state phy_reset_gpio; /* GPIO to reset ULPI phy */
>  };
>  
>  static struct fdt_usb port[USB_PORTS_MAX];	/* List of valid USB ports */
> @@ -188,8 +198,8 @@ void usbf_reset_controller(struct fdt_usb *config, struct usb_ctlr *usbctlr)
>  	 */
>  }
>  
> -/* set up the USB controller with the parameters provided */
> -static int init_usb_controller(struct fdt_usb *config,
> +/* set up the UTMI USB controller with the parameters provided */
> +static int init_utmi_usb_controller(struct fdt_usb *config,
>  				struct usb_ctlr *usbctlr, const u32 timing[])
>  {
>  	u32 val;
> @@ -298,17 +308,114 @@ static int init_usb_controller(struct fdt_usb *config,
>  	if (!loop_count)
>  		return -1;
>  
> -	return 0;
> -}
> +	/* Disable ICUSB FS/LS transceiver */
> +	clrbits_le32(&usbctlr->icusb_ctrl, IC_ENB1);
> +
> +	/* Select UTMI parallel interface */
> +	clrsetbits_le32(&usbctlr->port_sc1, PTS_MASK,
> +			PTS_UTMI << PTS_SHIFT);
> +	clrbits_le32(&usbctlr->port_sc1, STS);
>  
> -static void power_up_port(struct usb_ctlr *usbctlr)
> -{
>  	/* Deassert power down state */
>  	clrbits_le32(&usbctlr->utmip_xcvr_cfg0, UTMIP_FORCE_PD_POWERDOWN |
>  		UTMIP_FORCE_PD2_POWERDOWN | UTMIP_FORCE_PDZI_POWERDOWN);
>  	clrbits_le32(&usbctlr->utmip_xcvr_cfg1, UTMIP_FORCE_PDDISC_POWERDOWN |
>  		UTMIP_FORCE_PDCHRP_POWERDOWN | UTMIP_FORCE_PDDR_POWERDOWN);
> +
> +	return 0;
> +}

an empty line here would be nice.

> +#ifdef CONFIG_USB_ULPI
> +/* if board file does not set a ULPI reference frequency we default to 24MHz */
> +#ifndef CONFIG_ULPI_REF_CLK
> +#define CONFIG_ULPI_REF_CLK 24000000
> +#endif
> +
> +/* set up the ULPI USB controller with the parameters provided */
> +static int init_ulpi_usb_controller(struct fdt_usb *config,
> +				struct usb_ctlr *usbctlr)
> +{
> +	u32 val;
> +	int loop_count;
> +	struct ulpi_viewport ulpi_vp;
> +
> +	/* set up ULPI reference clock on pllp_out4 */
> +	clock_enable(PERIPH_ID_DEV2_OUT);
> +	clock_set_pllout(CLOCK_ID_PERIPH, PLL_OUT4, CONFIG_ULPI_REF_CLK);
> +
> +	/* reset ULPI phy */
> +	if (fdt_gpio_isvalid(&config->phy_reset_gpio)) {
> +		fdtdec_setup_gpio(&config->phy_reset_gpio);
> +		gpio_direction_output(config->phy_reset_gpio.gpio, 0);
> +		mdelay(5);
> +		gpio_set_value(config->phy_reset_gpio.gpio, 1);
> +	}
> +
> +	/* Reset the usb controller */
> +	clock_enable(config->periph_id);
> +	usbf_reset_controller(config, usbctlr);
> +
> +	/* enable pinmux bypass */
> +	setbits_le32(&usbctlr->ulpi_timing_ctrl_0,
> +			ULPI_CLKOUT_PINMUX_BYP | ULPI_OUTPUT_PINMUX_BYP);
> +
> +	/* Select ULPI parallel interface */
> +	clrsetbits_le32(&usbctlr->port_sc1, PTS_MASK, PTS_ULPI << PTS_SHIFT);
> +
> +	/* enable ULPI transceiver */
> +	setbits_le32(&usbctlr->susp_ctrl, ULPI_PHY_ENB);
> +
> +	/* configure ULPI transceiver timings */
> +	val = 0;
> +	writel(val, &usbctlr->ulpi_timing_ctrl_1);
> +
> +	val |= ULPI_DATA_TRIMMER_SEL(4);
> +	val |= ULPI_STPDIRNXT_TRIMMER_SEL(4);
> +	val |= ULPI_DIR_TRIMMER_SEL(4);
> +	writel(val, &usbctlr->ulpi_timing_ctrl_1);
> +	udelay(10);
> +
> +	val |= ULPI_DATA_TRIMMER_LOAD;
> +	val |= ULPI_STPDIRNXT_TRIMMER_LOAD;
> +	val |= ULPI_DIR_TRIMMER_LOAD;
> +	writel(val, &usbctlr->ulpi_timing_ctrl_1);
> +
> +	/* set up phy for host operation with external vbus supply */
> +	ulpi_vp.port_num = 0;
> +	ulpi_vp.viewport_addr = (u32)&usbctlr->ulpi_viewport;
> +
> +	if (ulpi_init(&ulpi_vp)) {
> +		printf("Tegra ULPI viewport init failed\n");
> +		return -1;
> +	}
> +
> +	ulpi_set_vbus(&ulpi_vp, 1, 1);
> +	ulpi_set_vbus_indicator(&ulpi_vp, 1, 1, 0);
> +
> +	/* enable wakeup events */
> +	setbits_le32(&usbctlr->port_sc1, WKCN | WKDS | WKOC);
> +
> +	/* Enable and wait for the phy clock to become valid in 100 ms */
> +	setbits_le32(&usbctlr->susp_ctrl, USB_SUSP_CLR);
> +	for (loop_count = 100000; loop_count != 0; loop_count--) {
> +		if (readl(&usbctlr->susp_ctrl) & USB_PHY_CLK_VALID)
> +			break;
> +		udelay(1);
> +	}
> +	if (!loop_count)
> +		return -1;
> +	clrbits_le32(&usbctlr->susp_ctrl, USB_SUSP_CLR);
> +
> +	return 0;
>  }
> +#else
> +static int init_ulpi_usb_controller(struct fdt_usb *config,
> +				struct usb_ctlr *usbctlr)
> +{
> +	printf("No code to set up ULPI controller, please enable"
> +			"CONFIG_USB_ULPI and CONFIG_USB_ULPI_VIEWPORT");
> +	return -1;
> +}
> +#endif
>  
>  static void config_clock(const u32 timing[])
>  {
> @@ -328,24 +435,21 @@ static int add_port(struct fdt_usb *config, const u32 timing[])
>  	struct usb_ctlr *usbctlr = config->reg;
>  
>  	if (port_count == USB_PORTS_MAX) {
> -		debug("tegrausb: Cannot register more than %d ports\n",
> +		printf("tegrausb: Cannot register more than %d ports\n",
>  		      USB_PORTS_MAX);
>  		return -1;
>  	}
> -	if (init_usb_controller(config, usbctlr, timing)) {
> -		debug("tegrausb: Cannot init port\n");
> +
> +	if (config->utmi && init_utmi_usb_controller(config, usbctlr, timing)) {
> +		printf("tegrausb: Cannot init port\n");
>  		return -1;
>  	}
> -	if (config->utmi) {
> -		/* Disable ICUSB FS/LS transceiver */
> -		clrbits_le32(&usbctlr->icusb_ctrl, IC_ENB1);
> -
> -		/* Select UTMI parallel interface */
> -		clrsetbits_le32(&usbctlr->port_sc1, PTS_MASK,
> -				PTS_UTMI << PTS_SHIFT);
> -		clrbits_le32(&usbctlr->port_sc1, STS);
> -		power_up_port(usbctlr);
> +
> +	if (config->ulpi && init_ulpi_usb_controller(config, usbctlr)) {
> +		printf("tegrausb: Cannot init port\n");
> +		return -1;
>  	}
> +
>  	port[port_count++] = *config;
>  
>  	return 0;
> @@ -412,6 +516,7 @@ int fdt_decode_usb(const void *blob, int node, unsigned osc_frequency_mhz,
>  
>  	phy = fdt_getprop(blob, node, "phy_type", NULL);
>  	config->utmi = phy && 0 == strcmp("utmi", phy);
> +	config->ulpi = phy && 0 == strcmp("ulpi", phy);
>  	config->enabled = fdtdec_get_is_enabled(blob, node);
>  	config->has_legacy_mode = fdtdec_get_bool(blob, node,
>  						  "nvidia,has-legacy-mode");
> @@ -421,10 +526,13 @@ int fdt_decode_usb(const void *blob, int node, unsigned osc_frequency_mhz,
>  		return -FDT_ERR_NOTFOUND;
>  	}
>  	fdtdec_decode_gpio(blob, node, "nvidia,vbus-gpio", &config->vbus_gpio);
> -	debug("enabled=%d, legacy_mode=%d, utmi=%d, periph_id=%d, vbus=%d, "
> -	      "dr_mode=%d\n", config->enabled, config->has_legacy_mode,
> -	      config->utmi, config->periph_id, config->vbus_gpio.gpio,
> -	      config->dr_mode);
> +	fdtdec_decode_gpio(blob, node, "nvidia,phy-reset-gpio",
> +			&config->phy_reset_gpio);
> +	debug("enabled=%d, legacy_mode=%d, utmi=%d, ulpi=%d, periph_id=%d, "
> +		"vbus=%d, phy_reset=%d, dr_mode=%d\n",
> +		config->enabled, config->has_legacy_mode, config->utmi,
> +		config->ulpi, config->periph_id, config->vbus_gpio.gpio,
> +		config->phy_reset_gpio.gpio, config->dr_mode);
>  
>  	return 0;
>  }
> diff --git a/arch/arm/include/asm/arch-tegra20/usb.h b/arch/arm/include/asm/arch-tegra20/usb.h
> index 638033b..bd89d66 100644
> --- a/arch/arm/include/asm/arch-tegra20/usb.h
> +++ b/arch/arm/include/asm/arch-tegra20/usb.h
> @@ -100,10 +100,12 @@ struct usb_ctlr {
>  
>  	/* 0x410 */
>  	uint usb1_legacy_ctrl;
> -	uint reserved12[3];
> +	uint reserved12[4];
>  
> -	/* 0x420 */
> -	uint reserved13[56];
> +	/* 0x424 */
> +	uint ulpi_timing_ctrl_0;
> +	uint ulpi_timing_ctrl_1;
> +	uint reserved13[53];
>  
>  	/* 0x500 */
>  	uint reserved14[64 * 3];
> @@ -144,10 +146,24 @@ struct usb_ctlr {
>  #define VBUS_SENSE_CTL_AB_SESS_VLD		2
>  #define VBUS_SENSE_CTL_A_SESS_VLD		3
>  
> +/* USB2_IF_ULPI_TIMING_CTRL_0 */
> +#define ULPI_OUTPUT_PINMUX_BYP			(1 << 10)
> +#define ULPI_CLKOUT_PINMUX_BYP			(1 << 11)
> +
> +/* USB2_IF_ULPI_TIMING_CTRL_1 */
> +#define ULPI_DATA_TRIMMER_LOAD			(1 << 0)
> +#define ULPI_DATA_TRIMMER_SEL(x)		(((x) & 0x7) << 1)
> +#define ULPI_STPDIRNXT_TRIMMER_LOAD		(1 << 16)
> +#define ULPI_STPDIRNXT_TRIMMER_SEL(x)	(((x) & 0x7) << 17)
> +#define ULPI_DIR_TRIMMER_LOAD			(1 << 24)
> +#define ULPI_DIR_TRIMMER_SEL(x)			(((x) & 0x7) << 25)
> +
>  /* USBx_IF_USB_SUSP_CTRL_0 */
> +#define ULPI_PHY_ENB				(1 << 13)
>  #define UTMIP_PHY_ENB			        (1 << 12)
>  #define UTMIP_RESET			        (1 << 11)
>  #define USB_PHY_CLK_VALID			(1 << 7)
> +#define USB_SUSP_CLR				(1 << 5)
>  
>  /* USBx_UTMIP_MISC_CFG1 */
>  #define UTMIP_PLLU_STABLE_COUNT_SHIFT		6
> @@ -203,12 +219,15 @@ struct usb_ctlr {
>  /* SB2_CONTROLLER_2_USB2D_PORTSC1_0 */
>  #define PTS_SHIFT				30
>  #define PTS_MASK				(3U << PTS_SHIFT)
> -#define PTS_UTMI	0
> +#define PTS_UTMI		0
>  #define PTS_RESERVED	1
> -#define PTS_ULP		2
> +#define PTS_ULPI		2
>  #define PTS_ICUSB_SER	3
>  
>  #define STS					(1 << 29)
> +#define WKOC				(1 << 22)
> +#define WKDS				(1 << 21)
> +#define WKCN				(1 << 20)
>  
>  /* USBx_UTMIP_XCVR_CFG0_0 */
>  #define UTMIP_FORCE_PD_POWERDOWN		(1 << 14)

-- 
Regards,
Igor.


More information about the U-Boot mailing list