diff options
| author | fishsoupisgood <github@madingley.org> | 2019-04-29 01:17:54 +0100 | 
|---|---|---|
| committer | fishsoupisgood <github@madingley.org> | 2019-05-27 03:43:43 +0100 | 
| commit | 3f2546b2ef55b661fd8dd69682b38992225e86f6 (patch) | |
| tree | 65ca85f13617aee1dce474596800950f266a456c /roms/u-boot/drivers/usb/host | |
| download | qemu-master.tar.gz qemu-master.tar.bz2 qemu-master.zip  | |
Diffstat (limited to 'roms/u-boot/drivers/usb/host')
40 files changed, 18263 insertions, 0 deletions
diff --git a/roms/u-boot/drivers/usb/host/Makefile b/roms/u-boot/drivers/usb/host/Makefile new file mode 100644 index 00000000..b301e282 --- /dev/null +++ b/roms/u-boot/drivers/usb/host/Makefile @@ -0,0 +1,44 @@ +# +# (C) Copyright 2000-2007 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# SPDX-License-Identifier:	GPL-2.0+ +# + +# ohci +obj-$(CONFIG_USB_OHCI_NEW) += ohci-hcd.o +obj-$(CONFIG_USB_ATMEL) += ohci-at91.o +obj-$(CONFIG_USB_OHCI_DA8XX) += ohci-da8xx.o +obj-$(CONFIG_USB_ISP116X_HCD) += isp116x-hcd.o +obj-$(CONFIG_USB_R8A66597_HCD) += r8a66597-hcd.o +obj-$(CONFIG_USB_SL811HS) += sl811-hcd.o +obj-$(CONFIG_USB_OHCI_S3C24XX) += ohci-s3c24xx.o + +# echi +obj-$(CONFIG_USB_EHCI) += ehci-hcd.o +obj-$(CONFIG_USB_EHCI_ARMADA100) += ehci-armada100.o utmi-armada100.o +obj-$(CONFIG_USB_EHCI_ATMEL) += ehci-atmel.o +ifdef CONFIG_MPC512X +obj-$(CONFIG_USB_EHCI_FSL) += ehci-mpc512x.o +else +obj-$(CONFIG_USB_EHCI_FSL) += ehci-fsl.o +endif +obj-$(CONFIG_USB_EHCI_FARADAY) += ehci-faraday.o +obj-$(CONFIG_USB_EHCI_EXYNOS) += ehci-exynos.o +obj-$(CONFIG_USB_EHCI_MXC) += ehci-mxc.o +obj-$(CONFIG_USB_EHCI_MXS) += ehci-mxs.o +obj-$(CONFIG_USB_EHCI_MX5) += ehci-mx5.o +obj-$(CONFIG_USB_EHCI_MX6) += ehci-mx6.o +obj-$(CONFIG_USB_EHCI_OMAP) += ehci-omap.o +obj-$(CONFIG_USB_EHCI_PPC4XX) += ehci-ppc4xx.o +obj-$(CONFIG_USB_EHCI_MARVELL) += ehci-marvell.o +obj-$(CONFIG_USB_EHCI_PCI) += ehci-pci.o +obj-$(CONFIG_USB_EHCI_SPEAR) += ehci-spear.o +obj-$(CONFIG_USB_EHCI_TEGRA) += ehci-tegra.o +obj-$(CONFIG_USB_EHCI_VCT) += ehci-vct.o +obj-$(CONFIG_USB_EHCI_RMOBILE) += ehci-rmobile.o + +# xhci +obj-$(CONFIG_USB_XHCI) += xhci.o xhci-mem.o xhci-ring.o +obj-$(CONFIG_USB_XHCI_EXYNOS) += xhci-exynos5.o +obj-$(CONFIG_USB_XHCI_OMAP) += xhci-omap.o diff --git a/roms/u-boot/drivers/usb/host/ehci-armada100.c b/roms/u-boot/drivers/usb/host/ehci-armada100.c new file mode 100644 index 00000000..012eb3a1 --- /dev/null +++ b/roms/u-boot/drivers/usb/host/ehci-armada100.c @@ -0,0 +1,48 @@ +/* + * (C) Copyright 2012 + * eInfochips Ltd. <www.einfochips.com> + * Written-by: Ajay Bhargav <ajay.bhargav@einfochips.com> + * + * This driver is based on Kirkwood echi driver + * (C) Copyright 2009 + * Marvell Semiconductor <www.marvell.com> + * Written-by: Prafulla Wadaskar <prafulla@marvell.com> + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +#include <common.h> +#include <asm/io.h> +#include <usb.h> +#include "ehci.h" +#include <asm/arch/cpu.h> +#include <asm/arch/armada100.h> +#include <asm/arch/utmi-armada100.h> + +/* + * EHCI host controller init + */ +int ehci_hcd_init(int index, enum usb_init_type init, +		struct ehci_hccr **hccr, struct ehci_hcor **hcor) +{ +	if (utmi_init() < 0) +		return -1; + +	*hccr = (struct ehci_hccr *)(ARMD1_USB_HOST_BASE + 0x100); +	*hcor = (struct ehci_hcor *)((uint32_t) *hccr +			+ HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase))); + +	debug("armada100-ehci: init hccr %x and hcor %x hc_length %d\n", +		(uint32_t)*hccr, (uint32_t)*hcor, +		(uint32_t)HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase))); + +	return 0; +} + +/* + * EHCI host controller stop + */ +int ehci_hcd_stop(int index) +{ +	return 0; +} diff --git a/roms/u-boot/drivers/usb/host/ehci-atmel.c b/roms/u-boot/drivers/usb/host/ehci-atmel.c new file mode 100644 index 00000000..9ffe5010 --- /dev/null +++ b/roms/u-boot/drivers/usb/host/ehci-atmel.c @@ -0,0 +1,73 @@ +/* + * (C) Copyright 2012 + * Atmel Semiconductor <www.atmel.com> + * Written-by: Bo Shen <voice.shen@atmel.com> + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +#include <common.h> +#include <watchdog.h> +#include <usb.h> +#include <asm/io.h> +#include <asm/arch/hardware.h> +#include <asm/arch/at91_pmc.h> +#include <asm/arch/clk.h> + +#include "ehci.h" + +/* Enable UTMI PLL time out 500us + * 10 times as datasheet specified + */ +#define EN_UPLL_TIMEOUT	500UL + +int ehci_hcd_init(int index, enum usb_init_type init, +		struct ehci_hccr **hccr, struct ehci_hcor **hcor) +{ +	at91_pmc_t *pmc = (at91_pmc_t *)ATMEL_BASE_PMC; +	ulong start_time, tmp_time; + +	start_time = get_timer(0); +	/* Enable UTMI PLL */ +	writel(AT91_PMC_UPLLEN | AT91_PMC_BIASEN, &pmc->uckr); +	while ((readl(&pmc->sr) & AT91_PMC_LOCKU) != AT91_PMC_LOCKU) { +		WATCHDOG_RESET(); +		tmp_time = get_timer(0); +		if ((tmp_time - start_time) > EN_UPLL_TIMEOUT) { +			printf("ERROR: failed to enable UPLL\n"); +			return -1; +		} +	} + +	/* Enable USB Host clock */ +	writel(1 << ATMEL_ID_UHPHS, &pmc->pcer); + +	*hccr = (struct ehci_hccr *)ATMEL_BASE_EHCI; +	*hcor = (struct ehci_hcor *)((uint32_t)*hccr + +			HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase))); + +	return 0; +} + +int ehci_hcd_stop(int index) +{ +	at91_pmc_t *pmc = (at91_pmc_t *)ATMEL_BASE_PMC; +	ulong start_time, tmp_time; + +	/* Disable USB Host Clock */ +	writel(1 << ATMEL_ID_UHPHS, &pmc->pcdr); + +	start_time = get_timer(0); +	/* Disable UTMI PLL */ +	writel(readl(&pmc->uckr) & ~AT91_PMC_UPLLEN, &pmc->uckr); +	while ((readl(&pmc->sr) & AT91_PMC_LOCKU) == AT91_PMC_LOCKU) { +		WATCHDOG_RESET(); +		tmp_time = get_timer(0); +		if ((tmp_time - start_time) > EN_UPLL_TIMEOUT) { +			printf("ERROR: failed to stop UPLL\n"); +			return -1; +		} +	} + +	return 0; +} diff --git a/roms/u-boot/drivers/usb/host/ehci-exynos.c b/roms/u-boot/drivers/usb/host/ehci-exynos.c new file mode 100644 index 00000000..edd91a84 --- /dev/null +++ b/roms/u-boot/drivers/usb/host/ehci-exynos.c @@ -0,0 +1,231 @@ +/* + * SAMSUNG EXYNOS USB HOST EHCI Controller + * + * Copyright (C) 2012 Samsung Electronics Co.Ltd + *	Vivek Gautam <gautam.vivek@samsung.com> + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +#include <common.h> +#include <fdtdec.h> +#include <libfdt.h> +#include <malloc.h> +#include <usb.h> +#include <asm/arch/cpu.h> +#include <asm/arch/ehci.h> +#include <asm/arch/system.h> +#include <asm/arch/power.h> +#include <asm/gpio.h> +#include <asm-generic/errno.h> +#include <linux/compat.h> +#include "ehci.h" + +/* Declare global data pointer */ +DECLARE_GLOBAL_DATA_PTR; + +/** + * Contains pointers to register base addresses + * for the usb controller. + */ +struct exynos_ehci { +	struct exynos_usb_phy *usb; +	struct ehci_hccr *hcd; +	struct fdt_gpio_state vbus_gpio; +}; + +static struct exynos_ehci exynos; + +#ifdef CONFIG_OF_CONTROL +static int exynos_usb_parse_dt(const void *blob, struct exynos_ehci *exynos) +{ +	fdt_addr_t addr; +	unsigned int node; +	int depth; + +	node = fdtdec_next_compatible(blob, 0, COMPAT_SAMSUNG_EXYNOS_EHCI); +	if (node <= 0) { +		debug("EHCI: Can't get device node for ehci\n"); +		return -ENODEV; +	} + +	/* +	 * Get the base address for EHCI controller from the device node +	 */ +	addr = fdtdec_get_addr(blob, node, "reg"); +	if (addr == FDT_ADDR_T_NONE) { +		debug("Can't get the EHCI register address\n"); +		return -ENXIO; +	} + +	exynos->hcd = (struct ehci_hccr *)addr; + +	/* Vbus gpio */ +	fdtdec_decode_gpio(blob, node, "samsung,vbus-gpio", &exynos->vbus_gpio); + +	depth = 0; +	node = fdtdec_next_compatible_subnode(blob, node, +					COMPAT_SAMSUNG_EXYNOS_USB_PHY, &depth); +	if (node <= 0) { +		debug("EHCI: Can't get device node for usb-phy controller\n"); +		return -ENODEV; +	} + +	/* +	 * Get the base address for usbphy from the device node +	 */ +	exynos->usb = (struct exynos_usb_phy *)fdtdec_get_addr(blob, node, +								"reg"); +	if (exynos->usb == NULL) { +		debug("Can't get the usbphy register address\n"); +		return -ENXIO; +	} + +	return 0; +} +#endif + +/* Setup the EHCI host controller. */ +static void setup_usb_phy(struct exynos_usb_phy *usb) +{ +	u32 hsic_ctrl; + +	set_usbhost_mode(USB20_PHY_CFG_HOST_LINK_EN); + +	set_usbhost_phy_ctrl(POWER_USB_HOST_PHY_CTRL_EN); + +	clrbits_le32(&usb->usbphyctrl0, +			HOST_CTRL0_FSEL_MASK | +			HOST_CTRL0_COMMONON_N | +			/* HOST Phy setting */ +			HOST_CTRL0_PHYSWRST | +			HOST_CTRL0_PHYSWRSTALL | +			HOST_CTRL0_SIDDQ | +			HOST_CTRL0_FORCESUSPEND | +			HOST_CTRL0_FORCESLEEP); + +	setbits_le32(&usb->usbphyctrl0, +			/* Setting up the ref freq */ +			(CLK_24MHZ << 16) | +			/* HOST Phy setting */ +			HOST_CTRL0_LINKSWRST | +			HOST_CTRL0_UTMISWRST); +	udelay(10); +	clrbits_le32(&usb->usbphyctrl0, +			HOST_CTRL0_LINKSWRST | +			HOST_CTRL0_UTMISWRST); + +	/* HSIC Phy Setting */ +	hsic_ctrl = (HSIC_CTRL_FORCESUSPEND | +			HSIC_CTRL_FORCESLEEP | +			HSIC_CTRL_SIDDQ); + +	clrbits_le32(&usb->hsicphyctrl1, hsic_ctrl); +	clrbits_le32(&usb->hsicphyctrl2, hsic_ctrl); + +	hsic_ctrl = (((HSIC_CTRL_REFCLKDIV_12 & HSIC_CTRL_REFCLKDIV_MASK) +				<< HSIC_CTRL_REFCLKDIV_SHIFT) +			| ((HSIC_CTRL_REFCLKSEL & HSIC_CTRL_REFCLKSEL_MASK) +				<< HSIC_CTRL_REFCLKSEL_SHIFT) +			| HSIC_CTRL_UTMISWRST); + +	setbits_le32(&usb->hsicphyctrl1, hsic_ctrl); +	setbits_le32(&usb->hsicphyctrl2, hsic_ctrl); + +	udelay(10); + +	clrbits_le32(&usb->hsicphyctrl1, HSIC_CTRL_PHYSWRST | +					HSIC_CTRL_UTMISWRST); + +	clrbits_le32(&usb->hsicphyctrl2, HSIC_CTRL_PHYSWRST | +					HSIC_CTRL_UTMISWRST); + +	udelay(20); + +	/* EHCI Ctrl setting */ +	setbits_le32(&usb->ehcictrl, +			EHCICTRL_ENAINCRXALIGN | +			EHCICTRL_ENAINCR4 | +			EHCICTRL_ENAINCR8 | +			EHCICTRL_ENAINCR16); +} + +/* Reset the EHCI host controller. */ +static void reset_usb_phy(struct exynos_usb_phy *usb) +{ +	u32 hsic_ctrl; + +	/* HOST_PHY reset */ +	setbits_le32(&usb->usbphyctrl0, +			HOST_CTRL0_PHYSWRST | +			HOST_CTRL0_PHYSWRSTALL | +			HOST_CTRL0_SIDDQ | +			HOST_CTRL0_FORCESUSPEND | +			HOST_CTRL0_FORCESLEEP); + +	/* HSIC Phy reset */ +	hsic_ctrl = (HSIC_CTRL_FORCESUSPEND | +			HSIC_CTRL_FORCESLEEP | +			HSIC_CTRL_SIDDQ | +			HSIC_CTRL_PHYSWRST); + +	setbits_le32(&usb->hsicphyctrl1, hsic_ctrl); +	setbits_le32(&usb->hsicphyctrl2, hsic_ctrl); + +	set_usbhost_phy_ctrl(POWER_USB_HOST_PHY_CTRL_DISABLE); +} + +/* + * EHCI-initialization + * Create the appropriate control structures to manage + * a new EHCI host controller. + */ +int ehci_hcd_init(int index, enum usb_init_type init, +		struct ehci_hccr **hccr, struct ehci_hcor **hcor) +{ +	struct exynos_ehci *ctx = &exynos; + +#ifdef CONFIG_OF_CONTROL +	if (exynos_usb_parse_dt(gd->fdt_blob, ctx)) { +		debug("Unable to parse device tree for ehci-exynos\n"); +		return -ENODEV; +	} +#else +	ctx->usb = (struct exynos_usb_phy *)samsung_get_base_usb_phy(); +	ctx->hcd = (struct ehci_hccr *)samsung_get_base_usb_ehci(); +#endif + +#ifdef CONFIG_OF_CONTROL +	/* setup the Vbus gpio here */ +	if (fdt_gpio_isvalid(&ctx->vbus_gpio) && +	    !fdtdec_setup_gpio(&ctx->vbus_gpio)) +		gpio_direction_output(ctx->vbus_gpio.gpio, 1); +#endif + +	setup_usb_phy(ctx->usb); + +	board_usb_init(index, init); + +	*hccr = ctx->hcd; +	*hcor = (struct ehci_hcor *)((uint32_t) *hccr +				+ HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase))); + +	debug("Exynos5-ehci: init hccr %x and hcor %x hc_length %d\n", +		(uint32_t)*hccr, (uint32_t)*hcor, +		(uint32_t)HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase))); + +	return 0; +} + +/* + * Destroy the appropriate control structures corresponding + * the EHCI host controller. + */ +int ehci_hcd_stop(int index) +{ +	struct exynos_ehci *ctx = &exynos; + +	reset_usb_phy(ctx->usb); + +	return 0; +} diff --git a/roms/u-boot/drivers/usb/host/ehci-faraday.c b/roms/u-boot/drivers/usb/host/ehci-faraday.c new file mode 100644 index 00000000..3b761bc3 --- /dev/null +++ b/roms/u-boot/drivers/usb/host/ehci-faraday.c @@ -0,0 +1,147 @@ +/* + * Faraday USB 2.0 EHCI Controller + * + * (C) Copyright 2010 Faraday Technology + * Dante Su <dantesu@faraday-tech.com> + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +#include <common.h> +#include <asm/io.h> +#include <usb.h> +#include <usb/fusbh200.h> +#include <usb/fotg210.h> + +#include "ehci.h" + +#ifndef CONFIG_USB_EHCI_BASE_LIST +#define CONFIG_USB_EHCI_BASE_LIST	{ CONFIG_USB_EHCI_BASE } +#endif + +union ehci_faraday_regs { +	struct fusbh200_regs usb; +	struct fotg210_regs  otg; +}; + +static inline int ehci_is_fotg2xx(union ehci_faraday_regs *regs) +{ +	return !readl(®s->usb.easstr); +} + +/* + * Create the appropriate control structures to manage + * a new EHCI host controller. + */ +int ehci_hcd_init(int index, enum usb_init_type init, +		struct ehci_hccr **ret_hccr, struct ehci_hcor **ret_hcor) +{ +	struct ehci_hccr *hccr; +	struct ehci_hcor *hcor; +	union ehci_faraday_regs *regs; +	uint32_t base_list[] = CONFIG_USB_EHCI_BASE_LIST; + +	if (index < 0 || index >= ARRAY_SIZE(base_list)) +		return -1; +	regs = (void __iomem *)base_list[index]; +	hccr = (struct ehci_hccr *)®s->usb.hccr; +	hcor = (struct ehci_hcor *)®s->usb.hcor; + +	if (ehci_is_fotg2xx(regs)) { +		/* A-device bus reset */ +		/* ... Power off A-device */ +		setbits_le32(®s->otg.otgcsr, OTGCSR_A_BUSDROP); +		/* ... Drop vbus and bus traffic */ +		clrbits_le32(®s->otg.otgcsr, OTGCSR_A_BUSREQ); +		mdelay(1); +		/* ... Power on A-device */ +		clrbits_le32(®s->otg.otgcsr, OTGCSR_A_BUSDROP); +		/* ... Drive vbus and bus traffic */ +		setbits_le32(®s->otg.otgcsr, OTGCSR_A_BUSREQ); +		mdelay(1); +		/* Disable OTG & DEV interrupts, triggered at level-high */ +		writel(IMR_IRQLH | IMR_OTG | IMR_DEV, ®s->otg.imr); +		/* Clear all interrupt status */ +		writel(ISR_HOST | ISR_OTG | ISR_DEV, ®s->otg.isr); +	} else { +		/* Interrupt=level-high */ +		setbits_le32(®s->usb.bmcsr, BMCSR_IRQLH); +		/* VBUS on */ +		clrbits_le32(®s->usb.bmcsr, BMCSR_VBUS_OFF); +		/* Disable all interrupts */ +		writel(0x00, ®s->usb.bmier); +		writel(0x1f, ®s->usb.bmisr); +	} + +	*ret_hccr = hccr; +	*ret_hcor = hcor; + +	return 0; +} + +/* + * Destroy the appropriate control structures corresponding + * the the EHCI host controller. + */ +int ehci_hcd_stop(int index) +{ +	return 0; +} + +/* + * This ehci_set_usbmode() overrides the weak function + * in "ehci-hcd.c". + */ +void ehci_set_usbmode(int index) +{ +	/* nothing needs to be done */ +} + +/* + * This ehci_get_port_speed() overrides the weak function + * in "ehci-hcd.c". + */ +int ehci_get_port_speed(struct ehci_hcor *hcor, uint32_t reg) +{ +	int spd, ret = PORTSC_PSPD_HS; +	union ehci_faraday_regs *regs = (void __iomem *)((ulong)hcor - 0x10); + +	if (ehci_is_fotg2xx(regs)) +		spd = OTGCSR_SPD(readl(®s->otg.otgcsr)); +	else +		spd = BMCSR_SPD(readl(®s->usb.bmcsr)); + +	switch (spd) { +	case 0:    /* full speed */ +		ret = PORTSC_PSPD_FS; +		break; +	case 1:    /* low  speed */ +		ret = PORTSC_PSPD_LS; +		break; +	case 2:    /* high speed */ +		ret = PORTSC_PSPD_HS; +		break; +	default: +		printf("ehci-faraday: invalid device speed\n"); +		break; +	} + +	return ret; +} + +/* + * This ehci_get_portsc_register() overrides the weak function + * in "ehci-hcd.c". + */ +uint32_t *ehci_get_portsc_register(struct ehci_hcor *hcor, int port) +{ +	/* Faraday EHCI has one and only one portsc register */ +	if (port) { +		/* Printing the message would cause a scan failure! */ +		debug("The request port(%d) is not configured\n", port); +		return NULL; +	} + +	/* Faraday EHCI PORTSC register offset is 0x20 from hcor */ +	return (uint32_t *)((uint8_t *)hcor + 0x20); +} diff --git a/roms/u-boot/drivers/usb/host/ehci-fsl.c b/roms/u-boot/drivers/usb/host/ehci-fsl.c new file mode 100644 index 00000000..6cb4d986 --- /dev/null +++ b/roms/u-boot/drivers/usb/host/ehci-fsl.c @@ -0,0 +1,156 @@ +/* + * (C) Copyright 2009, 2011 Freescale Semiconductor, Inc. + * + * (C) Copyright 2008, Excito Elektronik i Sk=E5ne AB + * + * Author: Tor Krill tor@excito.com + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +#include <common.h> +#include <pci.h> +#include <usb.h> +#include <asm/io.h> +#include <usb/ehci-fsl.h> +#include <hwconfig.h> +#include <asm/fsl_errata.h> + +#include "ehci.h" + +static void set_txfifothresh(struct usb_ehci *, u32); + +/* Check USB PHY clock valid */ +static int usb_phy_clk_valid(struct usb_ehci *ehci) +{ +	if (!((in_be32(&ehci->control) & PHY_CLK_VALID) || +			in_be32(&ehci->prictrl))) { +		printf("USB PHY clock invalid!\n"); +		return 0; +	} else { +		return 1; +	} +} + +/* + * Create the appropriate control structures to manage + * a new EHCI host controller. + * + * Excerpts from linux ehci fsl driver. + */ +int ehci_hcd_init(int index, enum usb_init_type init, +		struct ehci_hccr **hccr, struct ehci_hcor **hcor) +{ +	struct usb_ehci *ehci = NULL; +	const char *phy_type = NULL; +	size_t len; +	char current_usb_controller[5]; +#ifdef CONFIG_SYS_FSL_USB_INTERNAL_UTMI_PHY +	char usb_phy[5]; + +	usb_phy[0] = '\0'; +#endif +	if (has_erratum_a007075()) { +		/* +		 * A 5ms delay is needed after applying soft-reset to the +		 * controller to let external ULPI phy come out of reset. +		 * This delay needs to be added before re-initializing +		 * the controller after soft-resetting completes +		 */ +		mdelay(5); +	} +	memset(current_usb_controller, '\0', 5); +	snprintf(current_usb_controller, 4, "usb%d", index+1); + +	switch (index) { +	case 0: +		ehci = (struct usb_ehci *)CONFIG_SYS_FSL_USB1_ADDR; +		break; +	case 1: +		ehci = (struct usb_ehci *)CONFIG_SYS_FSL_USB2_ADDR; +		break; +	default: +		printf("ERROR: wrong controller index!!\n"); +		break; +	}; + +	*hccr = (struct ehci_hccr *)((uint32_t)&ehci->caplength); +	*hcor = (struct ehci_hcor *)((uint32_t) *hccr + +			HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase))); + +	/* Set to Host mode */ +	setbits_le32(&ehci->usbmode, CM_HOST); + +	out_be32(&ehci->snoop1, SNOOP_SIZE_2GB); +	out_be32(&ehci->snoop2, 0x80000000 | SNOOP_SIZE_2GB); + +	/* Init phy */ +	if (hwconfig_sub(current_usb_controller, "phy_type")) +		phy_type = hwconfig_subarg(current_usb_controller, +				"phy_type", &len); +	else +		phy_type = getenv("usb_phy_type"); + +	if (!phy_type) { +#ifdef CONFIG_SYS_FSL_USB_INTERNAL_UTMI_PHY +		/* if none specified assume internal UTMI */ +		strcpy(usb_phy, "utmi"); +		phy_type = usb_phy; +#else +		printf("WARNING: USB phy type not defined !!\n"); +		return -1; +#endif +	} + +	if (!strncmp(phy_type, "utmi", 4)) { +#if defined(CONFIG_SYS_FSL_USB_INTERNAL_UTMI_PHY) +		setbits_be32(&ehci->control, PHY_CLK_SEL_UTMI); +		setbits_be32(&ehci->control, UTMI_PHY_EN); +		udelay(1000); /* delay required for PHY Clk to appear */ +#endif +		out_le32(&(*hcor)->or_portsc[0], PORT_PTS_UTMI); +		setbits_be32(&ehci->control, USB_EN); +	} else { +		setbits_be32(&ehci->control, PHY_CLK_SEL_ULPI); +		clrsetbits_be32(&ehci->control, UTMI_PHY_EN, USB_EN); +		udelay(1000); /* delay required for PHY Clk to appear */ +		if (!usb_phy_clk_valid(ehci)) +			return -EINVAL; +		out_le32(&(*hcor)->or_portsc[0], PORT_PTS_ULPI); +	} + +	out_be32(&ehci->prictrl, 0x0000000c); +	out_be32(&ehci->age_cnt_limit, 0x00000040); +	out_be32(&ehci->sictrl, 0x00000001); + +	in_le32(&ehci->usbmode); + +	if (SVR_SOC_VER(get_svr()) == SVR_T4240 && +	    IS_SVR_REV(get_svr(), 2, 0)) +		set_txfifothresh(ehci, TXFIFOTHRESH); + +	return 0; +} + +/* + * Destroy the appropriate control structures corresponding + * the the EHCI host controller. + */ +int ehci_hcd_stop(int index) +{ +	return 0; +} + +/* + * Setting the value of TXFIFO_THRESH field in TXFILLTUNING register + * to counter DDR latencies in writing data into Tx buffer. + * This prevents Tx buffer from getting underrun + */ +static void set_txfifothresh(struct usb_ehci *ehci, u32 txfifo_thresh) +{ +	u32 cmd; +	cmd = ehci_readl(&ehci->txfilltuning); +	cmd &= ~TXFIFO_THRESH_MASK; +	cmd |= TXFIFO_THRESH(txfifo_thresh); +	ehci_writel(&ehci->txfilltuning, cmd); +} diff --git a/roms/u-boot/drivers/usb/host/ehci-hcd.c b/roms/u-boot/drivers/usb/host/ehci-hcd.c new file mode 100644 index 00000000..eaf59134 --- /dev/null +++ b/roms/u-boot/drivers/usb/host/ehci-hcd.c @@ -0,0 +1,1404 @@ +/*- + * Copyright (c) 2007-2008, Juniper Networks, Inc. + * Copyright (c) 2008, Excito Elektronik i Skåne AB + * Copyright (c) 2008, Michael Trimarchi <trimarchimichael@yahoo.it> + * + * 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 version 2 of + * the License. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ +#include <common.h> +#include <errno.h> +#include <asm/byteorder.h> +#include <asm/unaligned.h> +#include <usb.h> +#include <asm/io.h> +#include <malloc.h> +#include <watchdog.h> +#include <linux/compiler.h> + +#include "ehci.h" + +#ifndef CONFIG_USB_MAX_CONTROLLER_COUNT +#define CONFIG_USB_MAX_CONTROLLER_COUNT 1 +#endif + +/* + * EHCI spec page 20 says that the HC may take up to 16 uFrames (= 4ms) to halt. + * Let's time out after 8 to have a little safety margin on top of that. + */ +#define HCHALT_TIMEOUT (8 * 1000) + +static struct ehci_ctrl ehcic[CONFIG_USB_MAX_CONTROLLER_COUNT]; + +#define ALIGN_END_ADDR(type, ptr, size)			\ +	((uint32_t)(ptr) + roundup((size) * sizeof(type), USB_DMA_MINALIGN)) + +static struct descriptor { +	struct usb_hub_descriptor hub; +	struct usb_device_descriptor device; +	struct usb_linux_config_descriptor config; +	struct usb_linux_interface_descriptor interface; +	struct usb_endpoint_descriptor endpoint; +}  __attribute__ ((packed)) descriptor = { +	{ +		0x8,		/* bDescLength */ +		0x29,		/* bDescriptorType: hub descriptor */ +		2,		/* bNrPorts -- runtime modified */ +		0,		/* wHubCharacteristics */ +		10,		/* bPwrOn2PwrGood */ +		0,		/* bHubCntrCurrent */ +		{},		/* Device removable */ +		{}		/* at most 7 ports! XXX */ +	}, +	{ +		0x12,		/* bLength */ +		1,		/* bDescriptorType: UDESC_DEVICE */ +		cpu_to_le16(0x0200), /* bcdUSB: v2.0 */ +		9,		/* bDeviceClass: UDCLASS_HUB */ +		0,		/* bDeviceSubClass: UDSUBCLASS_HUB */ +		1,		/* bDeviceProtocol: UDPROTO_HSHUBSTT */ +		64,		/* bMaxPacketSize: 64 bytes */ +		0x0000,		/* idVendor */ +		0x0000,		/* idProduct */ +		cpu_to_le16(0x0100), /* bcdDevice */ +		1,		/* iManufacturer */ +		2,		/* iProduct */ +		0,		/* iSerialNumber */ +		1		/* bNumConfigurations: 1 */ +	}, +	{ +		0x9, +		2,		/* bDescriptorType: UDESC_CONFIG */ +		cpu_to_le16(0x19), +		1,		/* bNumInterface */ +		1,		/* bConfigurationValue */ +		0,		/* iConfiguration */ +		0x40,		/* bmAttributes: UC_SELF_POWER */ +		0		/* bMaxPower */ +	}, +	{ +		0x9,		/* bLength */ +		4,		/* bDescriptorType: UDESC_INTERFACE */ +		0,		/* bInterfaceNumber */ +		0,		/* bAlternateSetting */ +		1,		/* bNumEndpoints */ +		9,		/* bInterfaceClass: UICLASS_HUB */ +		0,		/* bInterfaceSubClass: UISUBCLASS_HUB */ +		0,		/* bInterfaceProtocol: UIPROTO_HSHUBSTT */ +		0		/* iInterface */ +	}, +	{ +		0x7,		/* bLength */ +		5,		/* bDescriptorType: UDESC_ENDPOINT */ +		0x81,		/* bEndpointAddress: +				 * UE_DIR_IN | EHCI_INTR_ENDPT +				 */ +		3,		/* bmAttributes: UE_INTERRUPT */ +		8,		/* wMaxPacketSize */ +		255		/* bInterval */ +	}, +}; + +#if defined(CONFIG_EHCI_IS_TDI) +#define ehci_is_TDI()	(1) +#else +#define ehci_is_TDI()	(0) +#endif + +int __ehci_get_port_speed(struct ehci_hcor *hcor, uint32_t reg) +{ +	return PORTSC_PSPD(reg); +} + +int ehci_get_port_speed(struct ehci_hcor *hcor, uint32_t reg) +	__attribute__((weak, alias("__ehci_get_port_speed"))); + +void __ehci_set_usbmode(int index) +{ +	uint32_t tmp; +	uint32_t *reg_ptr; + +	reg_ptr = (uint32_t *)((u8 *)&ehcic[index].hcor->or_usbcmd + USBMODE); +	tmp = ehci_readl(reg_ptr); +	tmp |= USBMODE_CM_HC; +#if defined(CONFIG_EHCI_MMIO_BIG_ENDIAN) +	tmp |= USBMODE_BE; +#endif +	ehci_writel(reg_ptr, tmp); +} + +void ehci_set_usbmode(int index) +	__attribute__((weak, alias("__ehci_set_usbmode"))); + +void __ehci_powerup_fixup(uint32_t *status_reg, uint32_t *reg) +{ +	mdelay(50); +} + +void ehci_powerup_fixup(uint32_t *status_reg, uint32_t *reg) +	__attribute__((weak, alias("__ehci_powerup_fixup"))); + +static int handshake(uint32_t *ptr, uint32_t mask, uint32_t done, int usec) +{ +	uint32_t result; +	do { +		result = ehci_readl(ptr); +		udelay(5); +		if (result == ~(uint32_t)0) +			return -1; +		result &= mask; +		if (result == done) +			return 0; +		usec--; +	} while (usec > 0); +	return -1; +} + +static int ehci_reset(int index) +{ +	uint32_t cmd; +	int ret = 0; + +	cmd = ehci_readl(&ehcic[index].hcor->or_usbcmd); +	cmd = (cmd & ~CMD_RUN) | CMD_RESET; +	ehci_writel(&ehcic[index].hcor->or_usbcmd, cmd); +	ret = handshake((uint32_t *)&ehcic[index].hcor->or_usbcmd, +			CMD_RESET, 0, 250 * 1000); +	if (ret < 0) { +		printf("EHCI fail to reset\n"); +		goto out; +	} + +	if (ehci_is_TDI()) +		ehci_set_usbmode(index); + +#ifdef CONFIG_USB_EHCI_TXFIFO_THRESH +	cmd = ehci_readl(&ehcic[index].hcor->or_txfilltuning); +	cmd &= ~TXFIFO_THRESH_MASK; +	cmd |= TXFIFO_THRESH(CONFIG_USB_EHCI_TXFIFO_THRESH); +	ehci_writel(&ehcic[index].hcor->or_txfilltuning, cmd); +#endif +out: +	return ret; +} + +static int ehci_shutdown(struct ehci_ctrl *ctrl) +{ +	int i, ret = 0; +	uint32_t cmd, reg; + +	if (!ctrl || !ctrl->hcor) +		return -EINVAL; + +	cmd = ehci_readl(&ctrl->hcor->or_usbcmd); +	cmd &= ~(CMD_PSE | CMD_ASE); +	ehci_writel(&ctrl->hcor->or_usbcmd, cmd); +	ret = handshake(&ctrl->hcor->or_usbsts, STS_ASS | STS_PSS, 0, +		100 * 1000); + +	if (!ret) { +		for (i = 0; i < CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS; i++) { +			reg = ehci_readl(&ctrl->hcor->or_portsc[i]); +			reg |= EHCI_PS_SUSP; +			ehci_writel(&ctrl->hcor->or_portsc[i], reg); +		} + +		cmd &= ~CMD_RUN; +		ehci_writel(&ctrl->hcor->or_usbcmd, cmd); +		ret = handshake(&ctrl->hcor->or_usbsts, STS_HALT, STS_HALT, +			HCHALT_TIMEOUT); +	} + +	if (ret) +		puts("EHCI failed to shut down host controller.\n"); + +	return ret; +} + +static int ehci_td_buffer(struct qTD *td, void *buf, size_t sz) +{ +	uint32_t delta, next; +	uint32_t addr = (uint32_t)buf; +	int idx; + +	if (addr != ALIGN(addr, ARCH_DMA_MINALIGN)) +		debug("EHCI-HCD: Misaligned buffer address (%p)\n", buf); + +	flush_dcache_range(addr, ALIGN(addr + sz, ARCH_DMA_MINALIGN)); + +	idx = 0; +	while (idx < QT_BUFFER_CNT) { +		td->qt_buffer[idx] = cpu_to_hc32(addr); +		td->qt_buffer_hi[idx] = 0; +		next = (addr + EHCI_PAGE_SIZE) & ~(EHCI_PAGE_SIZE - 1); +		delta = next - addr; +		if (delta >= sz) +			break; +		sz -= delta; +		addr = next; +		idx++; +	} + +	if (idx == QT_BUFFER_CNT) { +		printf("out of buffer pointers (%u bytes left)\n", sz); +		return -1; +	} + +	return 0; +} + +static inline u8 ehci_encode_speed(enum usb_device_speed speed) +{ +	#define QH_HIGH_SPEED	2 +	#define QH_FULL_SPEED	0 +	#define QH_LOW_SPEED	1 +	if (speed == USB_SPEED_HIGH) +		return QH_HIGH_SPEED; +	if (speed == USB_SPEED_LOW) +		return QH_LOW_SPEED; +	return QH_FULL_SPEED; +} + +static int +ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, +		   int length, struct devrequest *req) +{ +	ALLOC_ALIGN_BUFFER(struct QH, qh, 1, USB_DMA_MINALIGN); +	struct qTD *qtd; +	int qtd_count = 0; +	int qtd_counter = 0; +	volatile struct qTD *vtd; +	unsigned long ts; +	uint32_t *tdp; +	uint32_t endpt, maxpacket, token, usbsts; +	uint32_t c, toggle; +	uint32_t cmd; +	int timeout; +	int ret = 0; +	struct ehci_ctrl *ctrl = dev->controller; + +	debug("dev=%p, pipe=%lx, buffer=%p, length=%d, req=%p\n", dev, pipe, +	      buffer, length, req); +	if (req != NULL) +		debug("req=%u (%#x), type=%u (%#x), value=%u (%#x), index=%u\n", +		      req->request, req->request, +		      req->requesttype, req->requesttype, +		      le16_to_cpu(req->value), le16_to_cpu(req->value), +		      le16_to_cpu(req->index)); + +#define PKT_ALIGN	512 +	/* +	 * The USB transfer is split into qTD transfers. Eeach qTD transfer is +	 * described by a transfer descriptor (the qTD). The qTDs form a linked +	 * list with a queue head (QH). +	 * +	 * Each qTD transfer starts with a new USB packet, i.e. a packet cannot +	 * have its beginning in a qTD transfer and its end in the following +	 * one, so the qTD transfer lengths have to be chosen accordingly. +	 * +	 * Each qTD transfer uses up to QT_BUFFER_CNT data buffers, mapped to +	 * single pages. The first data buffer can start at any offset within a +	 * page (not considering the cache-line alignment issues), while the +	 * following buffers must be page-aligned. There is no alignment +	 * constraint on the size of a qTD transfer. +	 */ +	if (req != NULL) +		/* 1 qTD will be needed for SETUP, and 1 for ACK. */ +		qtd_count += 1 + 1; +	if (length > 0 || req == NULL) { +		/* +		 * Determine the qTD transfer size that will be used for the +		 * data payload (not considering the first qTD transfer, which +		 * may be longer or shorter, and the final one, which may be +		 * shorter). +		 * +		 * In order to keep each packet within a qTD transfer, the qTD +		 * transfer size is aligned to PKT_ALIGN, which is a multiple of +		 * wMaxPacketSize (except in some cases for interrupt transfers, +		 * see comment in submit_int_msg()). +		 * +		 * By default, i.e. if the input buffer is aligned to PKT_ALIGN, +		 * QT_BUFFER_CNT full pages will be used. +		 */ +		int xfr_sz = QT_BUFFER_CNT; +		/* +		 * However, if the input buffer is not aligned to PKT_ALIGN, the +		 * qTD transfer size will be one page shorter, and the first qTD +		 * data buffer of each transfer will be page-unaligned. +		 */ +		if ((uint32_t)buffer & (PKT_ALIGN - 1)) +			xfr_sz--; +		/* Convert the qTD transfer size to bytes. */ +		xfr_sz *= EHCI_PAGE_SIZE; +		/* +		 * Approximate by excess the number of qTDs that will be +		 * required for the data payload. The exact formula is way more +		 * complicated and saves at most 2 qTDs, i.e. a total of 128 +		 * bytes. +		 */ +		qtd_count += 2 + length / xfr_sz; +	} +/* + * Threshold value based on the worst-case total size of the allocated qTDs for + * a mass-storage transfer of 65535 blocks of 512 bytes. + */ +#if CONFIG_SYS_MALLOC_LEN <= 64 + 128 * 1024 +#warning CONFIG_SYS_MALLOC_LEN may be too small for EHCI +#endif +	qtd = memalign(USB_DMA_MINALIGN, qtd_count * sizeof(struct qTD)); +	if (qtd == NULL) { +		printf("unable to allocate TDs\n"); +		return -1; +	} + +	memset(qh, 0, sizeof(struct QH)); +	memset(qtd, 0, qtd_count * sizeof(*qtd)); + +	toggle = usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe)); + +	/* +	 * Setup QH (3.6 in ehci-r10.pdf) +	 * +	 *   qh_link ................. 03-00 H +	 *   qh_endpt1 ............... 07-04 H +	 *   qh_endpt2 ............... 0B-08 H +	 * - qh_curtd +	 *   qh_overlay.qt_next ...... 13-10 H +	 * - qh_overlay.qt_altnext +	 */ +	qh->qh_link = cpu_to_hc32((uint32_t)&ctrl->qh_list | QH_LINK_TYPE_QH); +	c = (dev->speed != USB_SPEED_HIGH) && !usb_pipeendpoint(pipe); +	maxpacket = usb_maxpacket(dev, pipe); +	endpt = QH_ENDPT1_RL(8) | QH_ENDPT1_C(c) | +		QH_ENDPT1_MAXPKTLEN(maxpacket) | QH_ENDPT1_H(0) | +		QH_ENDPT1_DTC(QH_ENDPT1_DTC_DT_FROM_QTD) | +		QH_ENDPT1_EPS(ehci_encode_speed(dev->speed)) | +		QH_ENDPT1_ENDPT(usb_pipeendpoint(pipe)) | QH_ENDPT1_I(0) | +		QH_ENDPT1_DEVADDR(usb_pipedevice(pipe)); +	qh->qh_endpt1 = cpu_to_hc32(endpt); +	endpt = QH_ENDPT2_MULT(1) | QH_ENDPT2_PORTNUM(dev->portnr) | +		QH_ENDPT2_HUBADDR(dev->parent->devnum) | +		QH_ENDPT2_UFCMASK(0) | QH_ENDPT2_UFSMASK(0); +	qh->qh_endpt2 = cpu_to_hc32(endpt); +	qh->qh_overlay.qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); +	qh->qh_overlay.qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); + +	tdp = &qh->qh_overlay.qt_next; + +	if (req != NULL) { +		/* +		 * Setup request qTD (3.5 in ehci-r10.pdf) +		 * +		 *   qt_next ................ 03-00 H +		 *   qt_altnext ............. 07-04 H +		 *   qt_token ............... 0B-08 H +		 * +		 *   [ buffer, buffer_hi ] loaded with "req". +		 */ +		qtd[qtd_counter].qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); +		qtd[qtd_counter].qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); +		token = QT_TOKEN_DT(0) | QT_TOKEN_TOTALBYTES(sizeof(*req)) | +			QT_TOKEN_IOC(0) | QT_TOKEN_CPAGE(0) | QT_TOKEN_CERR(3) | +			QT_TOKEN_PID(QT_TOKEN_PID_SETUP) | +			QT_TOKEN_STATUS(QT_TOKEN_STATUS_ACTIVE); +		qtd[qtd_counter].qt_token = cpu_to_hc32(token); +		if (ehci_td_buffer(&qtd[qtd_counter], req, sizeof(*req))) { +			printf("unable to construct SETUP TD\n"); +			goto fail; +		} +		/* Update previous qTD! */ +		*tdp = cpu_to_hc32((uint32_t)&qtd[qtd_counter]); +		tdp = &qtd[qtd_counter++].qt_next; +		toggle = 1; +	} + +	if (length > 0 || req == NULL) { +		uint8_t *buf_ptr = buffer; +		int left_length = length; + +		do { +			/* +			 * Determine the size of this qTD transfer. By default, +			 * QT_BUFFER_CNT full pages can be used. +			 */ +			int xfr_bytes = QT_BUFFER_CNT * EHCI_PAGE_SIZE; +			/* +			 * However, if the input buffer is not page-aligned, the +			 * portion of the first page before the buffer start +			 * offset within that page is unusable. +			 */ +			xfr_bytes -= (uint32_t)buf_ptr & (EHCI_PAGE_SIZE - 1); +			/* +			 * In order to keep each packet within a qTD transfer, +			 * align the qTD transfer size to PKT_ALIGN. +			 */ +			xfr_bytes &= ~(PKT_ALIGN - 1); +			/* +			 * This transfer may be shorter than the available qTD +			 * transfer size that has just been computed. +			 */ +			xfr_bytes = min(xfr_bytes, left_length); + +			/* +			 * Setup request qTD (3.5 in ehci-r10.pdf) +			 * +			 *   qt_next ................ 03-00 H +			 *   qt_altnext ............. 07-04 H +			 *   qt_token ............... 0B-08 H +			 * +			 *   [ buffer, buffer_hi ] loaded with "buffer". +			 */ +			qtd[qtd_counter].qt_next = +					cpu_to_hc32(QT_NEXT_TERMINATE); +			qtd[qtd_counter].qt_altnext = +					cpu_to_hc32(QT_NEXT_TERMINATE); +			token = QT_TOKEN_DT(toggle) | +				QT_TOKEN_TOTALBYTES(xfr_bytes) | +				QT_TOKEN_IOC(req == NULL) | QT_TOKEN_CPAGE(0) | +				QT_TOKEN_CERR(3) | +				QT_TOKEN_PID(usb_pipein(pipe) ? +					QT_TOKEN_PID_IN : QT_TOKEN_PID_OUT) | +				QT_TOKEN_STATUS(QT_TOKEN_STATUS_ACTIVE); +			qtd[qtd_counter].qt_token = cpu_to_hc32(token); +			if (ehci_td_buffer(&qtd[qtd_counter], buf_ptr, +						xfr_bytes)) { +				printf("unable to construct DATA TD\n"); +				goto fail; +			} +			/* Update previous qTD! */ +			*tdp = cpu_to_hc32((uint32_t)&qtd[qtd_counter]); +			tdp = &qtd[qtd_counter++].qt_next; +			/* +			 * Data toggle has to be adjusted since the qTD transfer +			 * size is not always an even multiple of +			 * wMaxPacketSize. +			 */ +			if ((xfr_bytes / maxpacket) & 1) +				toggle ^= 1; +			buf_ptr += xfr_bytes; +			left_length -= xfr_bytes; +		} while (left_length > 0); +	} + +	if (req != NULL) { +		/* +		 * Setup request qTD (3.5 in ehci-r10.pdf) +		 * +		 *   qt_next ................ 03-00 H +		 *   qt_altnext ............. 07-04 H +		 *   qt_token ............... 0B-08 H +		 */ +		qtd[qtd_counter].qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); +		qtd[qtd_counter].qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); +		token = QT_TOKEN_DT(1) | QT_TOKEN_TOTALBYTES(0) | +			QT_TOKEN_IOC(1) | QT_TOKEN_CPAGE(0) | QT_TOKEN_CERR(3) | +			QT_TOKEN_PID(usb_pipein(pipe) ? +				QT_TOKEN_PID_OUT : QT_TOKEN_PID_IN) | +			QT_TOKEN_STATUS(QT_TOKEN_STATUS_ACTIVE); +		qtd[qtd_counter].qt_token = cpu_to_hc32(token); +		/* Update previous qTD! */ +		*tdp = cpu_to_hc32((uint32_t)&qtd[qtd_counter]); +		tdp = &qtd[qtd_counter++].qt_next; +	} + +	ctrl->qh_list.qh_link = cpu_to_hc32((uint32_t)qh | QH_LINK_TYPE_QH); + +	/* Flush dcache */ +	flush_dcache_range((uint32_t)&ctrl->qh_list, +		ALIGN_END_ADDR(struct QH, &ctrl->qh_list, 1)); +	flush_dcache_range((uint32_t)qh, ALIGN_END_ADDR(struct QH, qh, 1)); +	flush_dcache_range((uint32_t)qtd, +			   ALIGN_END_ADDR(struct qTD, qtd, qtd_count)); + +	/* Set async. queue head pointer. */ +	ehci_writel(&ctrl->hcor->or_asynclistaddr, (uint32_t)&ctrl->qh_list); + +	usbsts = ehci_readl(&ctrl->hcor->or_usbsts); +	ehci_writel(&ctrl->hcor->or_usbsts, (usbsts & 0x3f)); + +	/* Enable async. schedule. */ +	cmd = ehci_readl(&ctrl->hcor->or_usbcmd); +	cmd |= CMD_ASE; +	ehci_writel(&ctrl->hcor->or_usbcmd, cmd); + +	ret = handshake((uint32_t *)&ctrl->hcor->or_usbsts, STS_ASS, STS_ASS, +			100 * 1000); +	if (ret < 0) { +		printf("EHCI fail timeout STS_ASS set\n"); +		goto fail; +	} + +	/* Wait for TDs to be processed. */ +	ts = get_timer(0); +	vtd = &qtd[qtd_counter - 1]; +	timeout = USB_TIMEOUT_MS(pipe); +	do { +		/* Invalidate dcache */ +		invalidate_dcache_range((uint32_t)&ctrl->qh_list, +			ALIGN_END_ADDR(struct QH, &ctrl->qh_list, 1)); +		invalidate_dcache_range((uint32_t)qh, +			ALIGN_END_ADDR(struct QH, qh, 1)); +		invalidate_dcache_range((uint32_t)qtd, +			ALIGN_END_ADDR(struct qTD, qtd, qtd_count)); + +		token = hc32_to_cpu(vtd->qt_token); +		if (!(QT_TOKEN_GET_STATUS(token) & QT_TOKEN_STATUS_ACTIVE)) +			break; +		WATCHDOG_RESET(); +	} while (get_timer(ts) < timeout); + +	/* +	 * Invalidate the memory area occupied by buffer +	 * Don't try to fix the buffer alignment, if it isn't properly +	 * aligned it's upper layer's fault so let invalidate_dcache_range() +	 * vow about it. But we have to fix the length as it's actual +	 * transfer length and can be unaligned. This is potentially +	 * dangerous operation, it's responsibility of the calling +	 * code to make sure enough space is reserved. +	 */ +	invalidate_dcache_range((uint32_t)buffer, +		ALIGN((uint32_t)buffer + length, ARCH_DMA_MINALIGN)); + +	/* Check that the TD processing happened */ +	if (QT_TOKEN_GET_STATUS(token) & QT_TOKEN_STATUS_ACTIVE) +		printf("EHCI timed out on TD - token=%#x\n", token); + +	/* Disable async schedule. */ +	cmd = ehci_readl(&ctrl->hcor->or_usbcmd); +	cmd &= ~CMD_ASE; +	ehci_writel(&ctrl->hcor->or_usbcmd, cmd); + +	ret = handshake((uint32_t *)&ctrl->hcor->or_usbsts, STS_ASS, 0, +			100 * 1000); +	if (ret < 0) { +		printf("EHCI fail timeout STS_ASS reset\n"); +		goto fail; +	} + +	token = hc32_to_cpu(qh->qh_overlay.qt_token); +	if (!(QT_TOKEN_GET_STATUS(token) & QT_TOKEN_STATUS_ACTIVE)) { +		debug("TOKEN=%#x\n", token); +		switch (QT_TOKEN_GET_STATUS(token) & +			~(QT_TOKEN_STATUS_SPLITXSTATE | QT_TOKEN_STATUS_PERR)) { +		case 0: +			toggle = QT_TOKEN_GET_DT(token); +			usb_settoggle(dev, usb_pipeendpoint(pipe), +				       usb_pipeout(pipe), toggle); +			dev->status = 0; +			break; +		case QT_TOKEN_STATUS_HALTED: +			dev->status = USB_ST_STALLED; +			break; +		case QT_TOKEN_STATUS_ACTIVE | QT_TOKEN_STATUS_DATBUFERR: +		case QT_TOKEN_STATUS_DATBUFERR: +			dev->status = USB_ST_BUF_ERR; +			break; +		case QT_TOKEN_STATUS_HALTED | QT_TOKEN_STATUS_BABBLEDET: +		case QT_TOKEN_STATUS_BABBLEDET: +			dev->status = USB_ST_BABBLE_DET; +			break; +		default: +			dev->status = USB_ST_CRC_ERR; +			if (QT_TOKEN_GET_STATUS(token) & QT_TOKEN_STATUS_HALTED) +				dev->status |= USB_ST_STALLED; +			break; +		} +		dev->act_len = length - QT_TOKEN_GET_TOTALBYTES(token); +	} else { +		dev->act_len = 0; +#ifndef CONFIG_USB_EHCI_FARADAY +		debug("dev=%u, usbsts=%#x, p[1]=%#x, p[2]=%#x\n", +		      dev->devnum, ehci_readl(&ctrl->hcor->or_usbsts), +		      ehci_readl(&ctrl->hcor->or_portsc[0]), +		      ehci_readl(&ctrl->hcor->or_portsc[1])); +#endif +	} + +	free(qtd); +	return (dev->status != USB_ST_NOT_PROC) ? 0 : -1; + +fail: +	free(qtd); +	return -1; +} + +__weak uint32_t *ehci_get_portsc_register(struct ehci_hcor *hcor, int port) +{ +	if (port < 0 || port >= CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS) { +		/* Printing the message would cause a scan failure! */ +		debug("The request port(%u) is not configured\n", port); +		return NULL; +	} + +	return (uint32_t *)&hcor->or_portsc[port]; +} + +int +ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, +		 int length, struct devrequest *req) +{ +	uint8_t tmpbuf[4]; +	u16 typeReq; +	void *srcptr = NULL; +	int len, srclen; +	uint32_t reg; +	uint32_t *status_reg; +	int port = le16_to_cpu(req->index) & 0xff; +	struct ehci_ctrl *ctrl = dev->controller; + +	srclen = 0; + +	debug("req=%u (%#x), type=%u (%#x), value=%u, index=%u\n", +	      req->request, req->request, +	      req->requesttype, req->requesttype, +	      le16_to_cpu(req->value), le16_to_cpu(req->index)); + +	typeReq = req->request | req->requesttype << 8; + +	switch (typeReq) { +	case USB_REQ_GET_STATUS | ((USB_RT_PORT | USB_DIR_IN) << 8): +	case USB_REQ_SET_FEATURE | ((USB_DIR_OUT | USB_RT_PORT) << 8): +	case USB_REQ_CLEAR_FEATURE | ((USB_DIR_OUT | USB_RT_PORT) << 8): +		status_reg = ehci_get_portsc_register(ctrl->hcor, port - 1); +		if (!status_reg) +			return -1; +		break; +	default: +		status_reg = NULL; +		break; +	} + +	switch (typeReq) { +	case DeviceRequest | USB_REQ_GET_DESCRIPTOR: +		switch (le16_to_cpu(req->value) >> 8) { +		case USB_DT_DEVICE: +			debug("USB_DT_DEVICE request\n"); +			srcptr = &descriptor.device; +			srclen = descriptor.device.bLength; +			break; +		case USB_DT_CONFIG: +			debug("USB_DT_CONFIG config\n"); +			srcptr = &descriptor.config; +			srclen = descriptor.config.bLength + +					descriptor.interface.bLength + +					descriptor.endpoint.bLength; +			break; +		case USB_DT_STRING: +			debug("USB_DT_STRING config\n"); +			switch (le16_to_cpu(req->value) & 0xff) { +			case 0:	/* Language */ +				srcptr = "\4\3\1\0"; +				srclen = 4; +				break; +			case 1:	/* Vendor */ +				srcptr = "\16\3u\0-\0b\0o\0o\0t\0"; +				srclen = 14; +				break; +			case 2:	/* Product */ +				srcptr = "\52\3E\0H\0C\0I\0 " +					 "\0H\0o\0s\0t\0 " +					 "\0C\0o\0n\0t\0r\0o\0l\0l\0e\0r\0"; +				srclen = 42; +				break; +			default: +				debug("unknown value DT_STRING %x\n", +					le16_to_cpu(req->value)); +				goto unknown; +			} +			break; +		default: +			debug("unknown value %x\n", le16_to_cpu(req->value)); +			goto unknown; +		} +		break; +	case USB_REQ_GET_DESCRIPTOR | ((USB_DIR_IN | USB_RT_HUB) << 8): +		switch (le16_to_cpu(req->value) >> 8) { +		case USB_DT_HUB: +			debug("USB_DT_HUB config\n"); +			srcptr = &descriptor.hub; +			srclen = descriptor.hub.bLength; +			break; +		default: +			debug("unknown value %x\n", le16_to_cpu(req->value)); +			goto unknown; +		} +		break; +	case USB_REQ_SET_ADDRESS | (USB_RECIP_DEVICE << 8): +		debug("USB_REQ_SET_ADDRESS\n"); +		ctrl->rootdev = le16_to_cpu(req->value); +		break; +	case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: +		debug("USB_REQ_SET_CONFIGURATION\n"); +		/* Nothing to do */ +		break; +	case USB_REQ_GET_STATUS | ((USB_DIR_IN | USB_RT_HUB) << 8): +		tmpbuf[0] = 1;	/* USB_STATUS_SELFPOWERED */ +		tmpbuf[1] = 0; +		srcptr = tmpbuf; +		srclen = 2; +		break; +	case USB_REQ_GET_STATUS | ((USB_RT_PORT | USB_DIR_IN) << 8): +		memset(tmpbuf, 0, 4); +		reg = ehci_readl(status_reg); +		if (reg & EHCI_PS_CS) +			tmpbuf[0] |= USB_PORT_STAT_CONNECTION; +		if (reg & EHCI_PS_PE) +			tmpbuf[0] |= USB_PORT_STAT_ENABLE; +		if (reg & EHCI_PS_SUSP) +			tmpbuf[0] |= USB_PORT_STAT_SUSPEND; +		if (reg & EHCI_PS_OCA) +			tmpbuf[0] |= USB_PORT_STAT_OVERCURRENT; +		if (reg & EHCI_PS_PR) +			tmpbuf[0] |= USB_PORT_STAT_RESET; +		if (reg & EHCI_PS_PP) +			tmpbuf[1] |= USB_PORT_STAT_POWER >> 8; + +		if (ehci_is_TDI()) { +			switch (ehci_get_port_speed(ctrl->hcor, reg)) { +			case PORTSC_PSPD_FS: +				break; +			case PORTSC_PSPD_LS: +				tmpbuf[1] |= USB_PORT_STAT_LOW_SPEED >> 8; +				break; +			case PORTSC_PSPD_HS: +			default: +				tmpbuf[1] |= USB_PORT_STAT_HIGH_SPEED >> 8; +				break; +			} +		} else { +			tmpbuf[1] |= USB_PORT_STAT_HIGH_SPEED >> 8; +		} + +		if (reg & EHCI_PS_CSC) +			tmpbuf[2] |= USB_PORT_STAT_C_CONNECTION; +		if (reg & EHCI_PS_PEC) +			tmpbuf[2] |= USB_PORT_STAT_C_ENABLE; +		if (reg & EHCI_PS_OCC) +			tmpbuf[2] |= USB_PORT_STAT_C_OVERCURRENT; +		if (ctrl->portreset & (1 << port)) +			tmpbuf[2] |= USB_PORT_STAT_C_RESET; + +		srcptr = tmpbuf; +		srclen = 4; +		break; +	case USB_REQ_SET_FEATURE | ((USB_DIR_OUT | USB_RT_PORT) << 8): +		reg = ehci_readl(status_reg); +		reg &= ~EHCI_PS_CLEAR; +		switch (le16_to_cpu(req->value)) { +		case USB_PORT_FEAT_ENABLE: +			reg |= EHCI_PS_PE; +			ehci_writel(status_reg, reg); +			break; +		case USB_PORT_FEAT_POWER: +			if (HCS_PPC(ehci_readl(&ctrl->hccr->cr_hcsparams))) { +				reg |= EHCI_PS_PP; +				ehci_writel(status_reg, reg); +			} +			break; +		case USB_PORT_FEAT_RESET: +			if ((reg & (EHCI_PS_PE | EHCI_PS_CS)) == EHCI_PS_CS && +			    !ehci_is_TDI() && +			    EHCI_PS_IS_LOWSPEED(reg)) { +				/* Low speed device, give up ownership. */ +				debug("port %d low speed --> companion\n", +				      port - 1); +				reg |= EHCI_PS_PO; +				ehci_writel(status_reg, reg); +				break; +			} else { +				int ret; + +				reg |= EHCI_PS_PR; +				reg &= ~EHCI_PS_PE; +				ehci_writel(status_reg, reg); +				/* +				 * caller must wait, then call GetPortStatus +				 * usb 2.0 specification say 50 ms resets on +				 * root +				 */ +				ehci_powerup_fixup(status_reg, ®); + +				ehci_writel(status_reg, reg & ~EHCI_PS_PR); +				/* +				 * A host controller must terminate the reset +				 * and stabilize the state of the port within +				 * 2 milliseconds +				 */ +				ret = handshake(status_reg, EHCI_PS_PR, 0, +						2 * 1000); +				if (!ret) +					ctrl->portreset |= 1 << port; +				else +					printf("port(%d) reset error\n", +					       port - 1); +			} +			break; +		case USB_PORT_FEAT_TEST: +			ehci_shutdown(ctrl); +			reg &= ~(0xf << 16); +			reg |= ((le16_to_cpu(req->index) >> 8) & 0xf) << 16; +			ehci_writel(status_reg, reg); +			break; +		default: +			debug("unknown feature %x\n", le16_to_cpu(req->value)); +			goto unknown; +		} +		/* unblock posted writes */ +		(void) ehci_readl(&ctrl->hcor->or_usbcmd); +		break; +	case USB_REQ_CLEAR_FEATURE | ((USB_DIR_OUT | USB_RT_PORT) << 8): +		reg = ehci_readl(status_reg); +		reg &= ~EHCI_PS_CLEAR; +		switch (le16_to_cpu(req->value)) { +		case USB_PORT_FEAT_ENABLE: +			reg &= ~EHCI_PS_PE; +			break; +		case USB_PORT_FEAT_C_ENABLE: +			reg |= EHCI_PS_PE; +			break; +		case USB_PORT_FEAT_POWER: +			if (HCS_PPC(ehci_readl(&ctrl->hccr->cr_hcsparams))) +				reg &= ~EHCI_PS_PP; +			break; +		case USB_PORT_FEAT_C_CONNECTION: +			reg |= EHCI_PS_CSC; +			break; +		case USB_PORT_FEAT_OVER_CURRENT: +			reg |= EHCI_PS_OCC; +			break; +		case USB_PORT_FEAT_C_RESET: +			ctrl->portreset &= ~(1 << port); +			break; +		default: +			debug("unknown feature %x\n", le16_to_cpu(req->value)); +			goto unknown; +		} +		ehci_writel(status_reg, reg); +		/* unblock posted write */ +		(void) ehci_readl(&ctrl->hcor->or_usbcmd); +		break; +	default: +		debug("Unknown request\n"); +		goto unknown; +	} + +	mdelay(1); +	len = min3(srclen, le16_to_cpu(req->length), length); +	if (srcptr != NULL && len > 0) +		memcpy(buffer, srcptr, len); +	else +		debug("Len is 0\n"); + +	dev->act_len = len; +	dev->status = 0; +	return 0; + +unknown: +	debug("requesttype=%x, request=%x, value=%x, index=%x, length=%x\n", +	      req->requesttype, req->request, le16_to_cpu(req->value), +	      le16_to_cpu(req->index), le16_to_cpu(req->length)); + +	dev->act_len = 0; +	dev->status = USB_ST_STALLED; +	return -1; +} + +int usb_lowlevel_stop(int index) +{ +	ehci_shutdown(&ehcic[index]); +	return ehci_hcd_stop(index); +} + +int usb_lowlevel_init(int index, enum usb_init_type init, void **controller) +{ +	uint32_t reg; +	uint32_t cmd; +	struct QH *qh_list; +	struct QH *periodic; +	int i; +	int rc; + +	rc = ehci_hcd_init(index, init, &ehcic[index].hccr, &ehcic[index].hcor); +	if (rc) +		return rc; +	if (init == USB_INIT_DEVICE) +		goto done; + +	/* EHCI spec section 4.1 */ +	if (ehci_reset(index)) +		return -1; + +#if defined(CONFIG_EHCI_HCD_INIT_AFTER_RESET) +	rc = ehci_hcd_init(index, init, &ehcic[index].hccr, &ehcic[index].hcor); +	if (rc) +		return rc; +#endif +	/* Set the high address word (aka segment) for 64-bit controller */ +	if (ehci_readl(&ehcic[index].hccr->cr_hccparams) & 1) +		ehci_writel(&ehcic[index].hcor->or_ctrldssegment, 0); + +	qh_list = &ehcic[index].qh_list; + +	/* Set head of reclaim list */ +	memset(qh_list, 0, sizeof(*qh_list)); +	qh_list->qh_link = cpu_to_hc32((uint32_t)qh_list | QH_LINK_TYPE_QH); +	qh_list->qh_endpt1 = cpu_to_hc32(QH_ENDPT1_H(1) | +						QH_ENDPT1_EPS(USB_SPEED_HIGH)); +	qh_list->qh_curtd = cpu_to_hc32(QT_NEXT_TERMINATE); +	qh_list->qh_overlay.qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); +	qh_list->qh_overlay.qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); +	qh_list->qh_overlay.qt_token = +			cpu_to_hc32(QT_TOKEN_STATUS(QT_TOKEN_STATUS_HALTED)); + +	flush_dcache_range((uint32_t)qh_list, +			   ALIGN_END_ADDR(struct QH, qh_list, 1)); + +	/* Set async. queue head pointer. */ +	ehci_writel(&ehcic[index].hcor->or_asynclistaddr, (uint32_t)qh_list); + +	/* +	 * Set up periodic list +	 * Step 1: Parent QH for all periodic transfers. +	 */ +	periodic = &ehcic[index].periodic_queue; +	memset(periodic, 0, sizeof(*periodic)); +	periodic->qh_link = cpu_to_hc32(QH_LINK_TERMINATE); +	periodic->qh_overlay.qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); +	periodic->qh_overlay.qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); + +	flush_dcache_range((uint32_t)periodic, +			   ALIGN_END_ADDR(struct QH, periodic, 1)); + +	/* +	 * Step 2: Setup frame-list: Every microframe, USB tries the same list. +	 *         In particular, device specifications on polling frequency +	 *         are disregarded. Keyboards seem to send NAK/NYet reliably +	 *         when polled with an empty buffer. +	 * +	 *         Split Transactions will be spread across microframes using +	 *         S-mask and C-mask. +	 */ +	if (ehcic[index].periodic_list == NULL) +		ehcic[index].periodic_list = memalign(4096, 1024 * 4); + +	if (!ehcic[index].periodic_list) +		return -ENOMEM; +	for (i = 0; i < 1024; i++) { +		ehcic[index].periodic_list[i] = cpu_to_hc32((uint32_t)periodic +						| QH_LINK_TYPE_QH); +	} + +	flush_dcache_range((uint32_t)ehcic[index].periodic_list, +			   ALIGN_END_ADDR(uint32_t, ehcic[index].periodic_list, +					  1024)); + +	/* Set periodic list base address */ +	ehci_writel(&ehcic[index].hcor->or_periodiclistbase, +		(uint32_t)ehcic[index].periodic_list); + +	reg = ehci_readl(&ehcic[index].hccr->cr_hcsparams); +	descriptor.hub.bNbrPorts = HCS_N_PORTS(reg); +	debug("Register %x NbrPorts %d\n", reg, descriptor.hub.bNbrPorts); +	/* Port Indicators */ +	if (HCS_INDICATOR(reg)) +		put_unaligned(get_unaligned(&descriptor.hub.wHubCharacteristics) +				| 0x80, &descriptor.hub.wHubCharacteristics); +	/* Port Power Control */ +	if (HCS_PPC(reg)) +		put_unaligned(get_unaligned(&descriptor.hub.wHubCharacteristics) +				| 0x01, &descriptor.hub.wHubCharacteristics); + +	/* Start the host controller. */ +	cmd = ehci_readl(&ehcic[index].hcor->or_usbcmd); +	/* +	 * Philips, Intel, and maybe others need CMD_RUN before the +	 * root hub will detect new devices (why?); NEC doesn't +	 */ +	cmd &= ~(CMD_LRESET|CMD_IAAD|CMD_PSE|CMD_ASE|CMD_RESET); +	cmd |= CMD_RUN; +	ehci_writel(&ehcic[index].hcor->or_usbcmd, cmd); + +#ifndef CONFIG_USB_EHCI_FARADAY +	/* take control over the ports */ +	cmd = ehci_readl(&ehcic[index].hcor->or_configflag); +	cmd |= FLAG_CF; +	ehci_writel(&ehcic[index].hcor->or_configflag, cmd); +#endif + +	/* unblock posted write */ +	cmd = ehci_readl(&ehcic[index].hcor->or_usbcmd); +	mdelay(5); +	reg = HC_VERSION(ehci_readl(&ehcic[index].hccr->cr_capbase)); +	printf("USB EHCI %x.%02x\n", reg >> 8, reg & 0xff); + +	ehcic[index].rootdev = 0; +done: +	*controller = &ehcic[index]; +	return 0; +} + +int +submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer, +		int length) +{ + +	if (usb_pipetype(pipe) != PIPE_BULK) { +		debug("non-bulk pipe (type=%lu)", usb_pipetype(pipe)); +		return -1; +	} +	return ehci_submit_async(dev, pipe, buffer, length, NULL); +} + +int +submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, +		   int length, struct devrequest *setup) +{ +	struct ehci_ctrl *ctrl = dev->controller; + +	if (usb_pipetype(pipe) != PIPE_CONTROL) { +		debug("non-control pipe (type=%lu)", usb_pipetype(pipe)); +		return -1; +	} + +	if (usb_pipedevice(pipe) == ctrl->rootdev) { +		if (!ctrl->rootdev) +			dev->speed = USB_SPEED_HIGH; +		return ehci_submit_root(dev, pipe, buffer, length, setup); +	} +	return ehci_submit_async(dev, pipe, buffer, length, setup); +} + +struct int_queue { +	struct QH *first; +	struct QH *current; +	struct QH *last; +	struct qTD *tds; +}; + +#define NEXT_QH(qh) (struct QH *)(hc32_to_cpu((qh)->qh_link) & ~0x1f) + +static int +enable_periodic(struct ehci_ctrl *ctrl) +{ +	uint32_t cmd; +	struct ehci_hcor *hcor = ctrl->hcor; +	int ret; + +	cmd = ehci_readl(&hcor->or_usbcmd); +	cmd |= CMD_PSE; +	ehci_writel(&hcor->or_usbcmd, cmd); + +	ret = handshake((uint32_t *)&hcor->or_usbsts, +			STS_PSS, STS_PSS, 100 * 1000); +	if (ret < 0) { +		printf("EHCI failed: timeout when enabling periodic list\n"); +		return -ETIMEDOUT; +	} +	udelay(1000); +	return 0; +} + +static int +disable_periodic(struct ehci_ctrl *ctrl) +{ +	uint32_t cmd; +	struct ehci_hcor *hcor = ctrl->hcor; +	int ret; + +	cmd = ehci_readl(&hcor->or_usbcmd); +	cmd &= ~CMD_PSE; +	ehci_writel(&hcor->or_usbcmd, cmd); + +	ret = handshake((uint32_t *)&hcor->or_usbsts, +			STS_PSS, 0, 100 * 1000); +	if (ret < 0) { +		printf("EHCI failed: timeout when disabling periodic list\n"); +		return -ETIMEDOUT; +	} +	return 0; +} + +static int periodic_schedules; + +struct int_queue * +create_int_queue(struct usb_device *dev, unsigned long pipe, int queuesize, +		 int elementsize, void *buffer) +{ +	struct ehci_ctrl *ctrl = dev->controller; +	struct int_queue *result = NULL; +	int i; + +	debug("Enter create_int_queue\n"); +	if (usb_pipetype(pipe) != PIPE_INTERRUPT) { +		debug("non-interrupt pipe (type=%lu)", usb_pipetype(pipe)); +		return NULL; +	} + +	/* limit to 4 full pages worth of data - +	 * we can safely fit them in a single TD, +	 * no matter the alignment +	 */ +	if (elementsize >= 16384) { +		debug("too large elements for interrupt transfers\n"); +		return NULL; +	} + +	result = malloc(sizeof(*result)); +	if (!result) { +		debug("ehci intr queue: out of memory\n"); +		goto fail1; +	} +	result->first = memalign(USB_DMA_MINALIGN, +				 sizeof(struct QH) * queuesize); +	if (!result->first) { +		debug("ehci intr queue: out of memory\n"); +		goto fail2; +	} +	result->current = result->first; +	result->last = result->first + queuesize - 1; +	result->tds = memalign(USB_DMA_MINALIGN, +			       sizeof(struct qTD) * queuesize); +	if (!result->tds) { +		debug("ehci intr queue: out of memory\n"); +		goto fail3; +	} +	memset(result->first, 0, sizeof(struct QH) * queuesize); +	memset(result->tds, 0, sizeof(struct qTD) * queuesize); + +	for (i = 0; i < queuesize; i++) { +		struct QH *qh = result->first + i; +		struct qTD *td = result->tds + i; +		void **buf = &qh->buffer; + +		qh->qh_link = cpu_to_hc32((uint32_t)(qh+1) | QH_LINK_TYPE_QH); +		if (i == queuesize - 1) +			qh->qh_link = cpu_to_hc32(QH_LINK_TERMINATE); + +		qh->qh_overlay.qt_next = cpu_to_hc32((uint32_t)td); +		qh->qh_overlay.qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); +		qh->qh_endpt1 = +			cpu_to_hc32((0 << 28) | /* No NAK reload (ehci 4.9) */ +			(usb_maxpacket(dev, pipe) << 16) | /* MPS */ +			(1 << 14) | +			QH_ENDPT1_EPS(ehci_encode_speed(dev->speed)) | +			(usb_pipeendpoint(pipe) << 8) | /* Endpoint Number */ +			(usb_pipedevice(pipe) << 0)); +		qh->qh_endpt2 = cpu_to_hc32((1 << 30) | /* 1 Tx per mframe */ +			(1 << 0)); /* S-mask: microframe 0 */ +		if (dev->speed == USB_SPEED_LOW || +				dev->speed == USB_SPEED_FULL) { +			debug("TT: port: %d, hub address: %d\n", +				dev->portnr, dev->parent->devnum); +			qh->qh_endpt2 |= cpu_to_hc32((dev->portnr << 23) | +				(dev->parent->devnum << 16) | +				(0x1c << 8)); /* C-mask: microframes 2-4 */ +		} + +		td->qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); +		td->qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); +		debug("communication direction is '%s'\n", +		      usb_pipein(pipe) ? "in" : "out"); +		td->qt_token = cpu_to_hc32((elementsize << 16) | +			((usb_pipein(pipe) ? 1 : 0) << 8) | /* IN/OUT token */ +			0x80); /* active */ +		td->qt_buffer[0] = +		    cpu_to_hc32((uint32_t)buffer + i * elementsize); +		td->qt_buffer[1] = +		    cpu_to_hc32((td->qt_buffer[0] + 0x1000) & ~0xfff); +		td->qt_buffer[2] = +		    cpu_to_hc32((td->qt_buffer[0] + 0x2000) & ~0xfff); +		td->qt_buffer[3] = +		    cpu_to_hc32((td->qt_buffer[0] + 0x3000) & ~0xfff); +		td->qt_buffer[4] = +		    cpu_to_hc32((td->qt_buffer[0] + 0x4000) & ~0xfff); + +		*buf = buffer + i * elementsize; +	} + +	flush_dcache_range((uint32_t)buffer, +			   ALIGN_END_ADDR(char, buffer, +					  queuesize * elementsize)); +	flush_dcache_range((uint32_t)result->first, +			   ALIGN_END_ADDR(struct QH, result->first, +					  queuesize)); +	flush_dcache_range((uint32_t)result->tds, +			   ALIGN_END_ADDR(struct qTD, result->tds, +					  queuesize)); + +	if (disable_periodic(ctrl) < 0) { +		debug("FATAL: periodic should never fail, but did"); +		goto fail3; +	} + +	/* hook up to periodic list */ +	struct QH *list = &ctrl->periodic_queue; +	result->last->qh_link = list->qh_link; +	list->qh_link = cpu_to_hc32((uint32_t)result->first | QH_LINK_TYPE_QH); + +	flush_dcache_range((uint32_t)result->last, +			   ALIGN_END_ADDR(struct QH, result->last, 1)); +	flush_dcache_range((uint32_t)list, +			   ALIGN_END_ADDR(struct QH, list, 1)); + +	if (enable_periodic(ctrl) < 0) { +		debug("FATAL: periodic should never fail, but did"); +		goto fail3; +	} +	periodic_schedules++; + +	debug("Exit create_int_queue\n"); +	return result; +fail3: +	if (result->tds) +		free(result->tds); +fail2: +	if (result->first) +		free(result->first); +	if (result) +		free(result); +fail1: +	return NULL; +} + +void *poll_int_queue(struct usb_device *dev, struct int_queue *queue) +{ +	struct QH *cur = queue->current; + +	/* depleted queue */ +	if (cur == NULL) { +		debug("Exit poll_int_queue with completed queue\n"); +		return NULL; +	} +	/* still active */ +	invalidate_dcache_range((uint32_t)cur, +				ALIGN_END_ADDR(struct QH, cur, 1)); +	if (cur->qh_overlay.qt_token & cpu_to_hc32(0x80)) { +		debug("Exit poll_int_queue with no completed intr transfer. " +		      "token is %x\n", cur->qh_overlay.qt_token); +		return NULL; +	} +	if (!(cur->qh_link & QH_LINK_TERMINATE)) +		queue->current++; +	else +		queue->current = NULL; +	debug("Exit poll_int_queue with completed intr transfer. " +	      "token is %x at %p (first at %p)\n", cur->qh_overlay.qt_token, +	      &cur->qh_overlay.qt_token, queue->first); +	return cur->buffer; +} + +/* Do not free buffers associated with QHs, they're owned by someone else */ +int +destroy_int_queue(struct usb_device *dev, struct int_queue *queue) +{ +	struct ehci_ctrl *ctrl = dev->controller; +	int result = -1; +	unsigned long timeout; + +	if (disable_periodic(ctrl) < 0) { +		debug("FATAL: periodic should never fail, but did"); +		goto out; +	} +	periodic_schedules--; + +	struct QH *cur = &ctrl->periodic_queue; +	timeout = get_timer(0) + 500; /* abort after 500ms */ +	while (!(cur->qh_link & cpu_to_hc32(QH_LINK_TERMINATE))) { +		debug("considering %p, with qh_link %x\n", cur, cur->qh_link); +		if (NEXT_QH(cur) == queue->first) { +			debug("found candidate. removing from chain\n"); +			cur->qh_link = queue->last->qh_link; +			result = 0; +			break; +		} +		cur = NEXT_QH(cur); +		if (get_timer(0) > timeout) { +			printf("Timeout destroying interrupt endpoint queue\n"); +			result = -1; +			goto out; +		} +	} + +	if (periodic_schedules > 0) { +		result = enable_periodic(ctrl); +		if (result < 0) +			debug("FATAL: periodic should never fail, but did"); +	} + +out: +	free(queue->tds); +	free(queue->first); +	free(queue); + +	return result; +} + +int +submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, +	       int length, int interval) +{ +	void *backbuffer; +	struct int_queue *queue; +	unsigned long timeout; +	int result = 0, ret; + +	debug("dev=%p, pipe=%lu, buffer=%p, length=%d, interval=%d", +	      dev, pipe, buffer, length, interval); + +	/* +	 * Interrupt transfers requiring several transactions are not supported +	 * because bInterval is ignored. +	 * +	 * Also, ehci_submit_async() relies on wMaxPacketSize being a power of 2 +	 * <= PKT_ALIGN if several qTDs are required, while the USB +	 * specification does not constrain this for interrupt transfers. That +	 * means that ehci_submit_async() would support interrupt transfers +	 * requiring several transactions only as long as the transfer size does +	 * not require more than a single qTD. +	 */ +	if (length > usb_maxpacket(dev, pipe)) { +		printf("%s: Interrupt transfers requiring several " +			"transactions are not supported.\n", __func__); +		return -1; +	} + +	queue = create_int_queue(dev, pipe, 1, length, buffer); + +	timeout = get_timer(0) + USB_TIMEOUT_MS(pipe); +	while ((backbuffer = poll_int_queue(dev, queue)) == NULL) +		if (get_timer(0) > timeout) { +			printf("Timeout poll on interrupt endpoint\n"); +			result = -ETIMEDOUT; +			break; +		} + +	if (backbuffer != buffer) { +		debug("got wrong buffer back (%x instead of %x)\n", +		      (uint32_t)backbuffer, (uint32_t)buffer); +		return -EINVAL; +	} + +	invalidate_dcache_range((uint32_t)buffer, +				ALIGN_END_ADDR(char, buffer, length)); + +	ret = destroy_int_queue(dev, queue); +	if (ret < 0) +		return ret; + +	/* everything worked out fine */ +	return result; +} diff --git a/roms/u-boot/drivers/usb/host/ehci-marvell.c b/roms/u-boot/drivers/usb/host/ehci-marvell.c new file mode 100644 index 00000000..52c43fdc --- /dev/null +++ b/roms/u-boot/drivers/usb/host/ehci-marvell.c @@ -0,0 +1,100 @@ +/* + * (C) Copyright 2009 + * Marvell Semiconductor <www.marvell.com> + * Written-by: Prafulla Wadaskar <prafulla@marvell.com> + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +#include <common.h> +#include <asm/io.h> +#include <usb.h> +#include "ehci.h" +#include <asm/arch/cpu.h> + +#if defined(CONFIG_KIRKWOOD) +#include <asm/arch/kirkwood.h> +#elif defined(CONFIG_ORION5X) +#include <asm/arch/orion5x.h> +#endif + +DECLARE_GLOBAL_DATA_PTR; + +#define rdl(off)	readl(MVUSB0_BASE + (off)) +#define wrl(off, val)	writel((val), MVUSB0_BASE + (off)) + +#define USB_WINDOW_CTRL(i)	(0x320 + ((i) << 4)) +#define USB_WINDOW_BASE(i)	(0x324 + ((i) << 4)) +#define USB_TARGET_DRAM		0x0 + +/* + * USB 2.0 Bridge Address Decoding registers setup + */ +static void usb_brg_adrdec_setup(void) +{ +	int i; +	u32 size, base, attrib; + +	for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) { + +		/* Enable DRAM bank */ +		switch (i) { +		case 0: +			attrib = MVUSB0_CPU_ATTR_DRAM_CS0; +			break; +		case 1: +			attrib = MVUSB0_CPU_ATTR_DRAM_CS1; +			break; +		case 2: +			attrib = MVUSB0_CPU_ATTR_DRAM_CS2; +			break; +		case 3: +			attrib = MVUSB0_CPU_ATTR_DRAM_CS3; +			break; +		default: +			/* invalide bank, disable access */ +			attrib = 0; +			break; +		} + +		size = gd->bd->bi_dram[i].size; +		base = gd->bd->bi_dram[i].start; +		if ((size) && (attrib)) +			wrl(USB_WINDOW_CTRL(i), +				MVCPU_WIN_CTRL_DATA(size, USB_TARGET_DRAM, +					attrib, MVCPU_WIN_ENABLE)); +		else +			wrl(USB_WINDOW_CTRL(i), MVCPU_WIN_DISABLE); + +		wrl(USB_WINDOW_BASE(i), base); +	} +} + +/* + * Create the appropriate control structures to manage + * a new EHCI host controller. + */ +int ehci_hcd_init(int index, enum usb_init_type init, +		struct ehci_hccr **hccr, struct ehci_hcor **hcor) +{ +	usb_brg_adrdec_setup(); + +	*hccr = (struct ehci_hccr *)(MVUSB0_BASE + 0x100); +	*hcor = (struct ehci_hcor *)((uint32_t) *hccr +			+ HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase))); + +	debug("ehci-marvell: init hccr %x and hcor %x hc_length %d\n", +		(uint32_t)*hccr, (uint32_t)*hcor, +		(uint32_t)HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase))); + +	return 0; +} + +/* + * Destroy the appropriate control structures corresponding + * the the EHCI host controller. + */ +int ehci_hcd_stop(int index) +{ +	return 0; +} diff --git a/roms/u-boot/drivers/usb/host/ehci-mpc512x.c b/roms/u-boot/drivers/usb/host/ehci-mpc512x.c new file mode 100644 index 00000000..b320c4a4 --- /dev/null +++ b/roms/u-boot/drivers/usb/host/ehci-mpc512x.c @@ -0,0 +1,140 @@ +/* + * (C) Copyright 2010, Damien Dusha, <d.dusha@gmail.com> + * + * (C) Copyright 2009, Value Team S.p.A. + * Francesco Rendine, <francesco.rendine@valueteam.com> + * + * (C) Copyright 2009 Freescale Semiconductor, Inc. + * + * (C) Copyright 2008, Excito Elektronik i Sk=E5ne AB + * + * Author: Tor Krill tor@excito.com + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +#include <common.h> +#include <pci.h> +#include <usb.h> +#include <asm/io.h> +#include <usb/ehci-fsl.h> + +#include "ehci.h" + +static void fsl_setup_phy(volatile struct ehci_hcor *); +static void fsl_platform_set_host_mode(volatile struct usb_ehci *ehci); +static int reset_usb_controller(volatile struct usb_ehci *ehci); +static void usb_platform_dr_init(volatile struct usb_ehci *ehci); + +/* + * Initialize SOC FSL EHCI Controller + * + * This code is derived from EHCI FSL USB Linux driver for MPC5121 + * + */ +int ehci_hcd_init(int index, enum usb_init_type init, +		struct ehci_hccr **hccr, struct ehci_hcor **hcor) +{ +	volatile struct usb_ehci *ehci; + +	/* Hook the memory mapped registers for EHCI-Controller */ +	ehci = (struct usb_ehci *)CONFIG_SYS_FSL_USB1_ADDR; +	*hccr = (struct ehci_hccr *)((uint32_t)&(ehci->caplength)); +	*hcor = (struct ehci_hcor *)((uint32_t) *hccr + +				HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase))); + +	/* configure interface for UTMI_WIDE */ +	usb_platform_dr_init(ehci); + +	/* Init Phy USB0 to UTMI+ */ +	fsl_setup_phy(*hcor); + +	/* Set to host mode */ +	fsl_platform_set_host_mode(ehci); + +	/* +	 * Setting the burst size seems to be required to prevent the +	 * USB from hanging when communicating with certain USB Mass +	 * storage devices. This was determined by analysing the +	 * EHCI registers under Linux vs U-Boot and burstsize was the +	 * major non-interrupt related difference between the two +	 * implementations. +	 * +	 * Some USB sticks behave better than others. In particular, +	 * the following USB stick is especially problematic: +	 * 0930:6545 Toshiba Corp +	 * +	 * The burstsize is set here to match the Linux implementation. +	 */ +	out_be32(&ehci->burstsize, FSL_EHCI_TXPBURST(8) | +				   FSL_EHCI_RXPBURST(8)); + +	return 0; +} + +/* + * Destroy the appropriate control structures corresponding + * the the EHCI host controller. + */ +int ehci_hcd_stop(int index) +{ +	volatile struct usb_ehci *ehci; +	int exit_status = 0; + +	/* Reset the USB controller */ +	ehci = (struct usb_ehci *)CONFIG_SYS_FSL_USB1_ADDR; +	exit_status = reset_usb_controller(ehci); + +	return exit_status; +} + +static int reset_usb_controller(volatile struct usb_ehci *ehci) +{ +	unsigned int i; + +	/* Command a reset of the USB Controller */ +	out_be32(&(ehci->usbcmd), EHCI_FSL_USBCMD_RST); + +	/* Wait for the reset process to finish */ +	for (i = 65535 ; i > 0 ; i--) { +		/* +		 * The host will set this bit to zero once the +		 * reset process is complete +		 */ +		if ((in_be32(&(ehci->usbcmd)) & EHCI_FSL_USBCMD_RST) == 0) +			return 0; +	} + +	/* Hub did not reset in time */ +	return -1; +} + +static void fsl_setup_phy(volatile struct ehci_hcor *hcor) +{ +	uint32_t portsc; + +	portsc  = ehci_readl(&hcor->or_portsc[0]); +	portsc &= ~(PORT_PTS_MSK | PORT_PTS_PTW); + +	/* Enable the phy mode to UTMI Wide */ +	portsc |= PORT_PTS_PTW; +	portsc |= PORT_PTS_UTMI; + +	ehci_writel(&hcor->or_portsc[0], portsc); +} + +static void fsl_platform_set_host_mode(volatile struct usb_ehci *ehci) +{ +	uint32_t temp; + +	temp  = in_le32(&ehci->usbmode); +	temp |= CM_HOST | ES_BE; +	out_le32(&ehci->usbmode, temp); +} + +static void usb_platform_dr_init(volatile struct usb_ehci *ehci) +{ +	/* Configure interface for UTMI_WIDE */ +	out_be32(&ehci->isiphyctrl, PHYCTRL_PHYE | PHYCTRL_PXE); +	out_be32(&ehci->usbgenctrl, GC_PPP | GC_PFP ); +} diff --git a/roms/u-boot/drivers/usb/host/ehci-mx5.c b/roms/u-boot/drivers/usb/host/ehci-mx5.c new file mode 100644 index 00000000..7566c612 --- /dev/null +++ b/roms/u-boot/drivers/usb/host/ehci-mx5.c @@ -0,0 +1,258 @@ +/* + * Copyright (c) 2009 Daniel Mack <daniel@caiaq.de> + * Copyright (C) 2010 Freescale Semiconductor, Inc. + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +#include <common.h> +#include <usb.h> +#include <errno.h> +#include <linux/compiler.h> +#include <usb/ehci-fsl.h> +#include <asm/io.h> +#include <asm/arch/imx-regs.h> +#include <asm/arch/clock.h> + +#include "ehci.h" + +#define MX5_USBOTHER_REGS_OFFSET 0x800 + + +#define MXC_OTG_OFFSET			0 +#define MXC_H1_OFFSET			0x200 +#define MXC_H2_OFFSET			0x400 +#define MXC_H3_OFFSET			0x600 + +#define MXC_USBCTRL_OFFSET		0 +#define MXC_USB_PHY_CTR_FUNC_OFFSET	0x8 +#define MXC_USB_PHY_CTR_FUNC2_OFFSET	0xc +#define MXC_USB_CTRL_1_OFFSET		0x10 +#define MXC_USBH2CTRL_OFFSET		0x14 +#define MXC_USBH3CTRL_OFFSET		0x18 + +/* USB_CTRL */ +/* OTG wakeup intr enable */ +#define MXC_OTG_UCTRL_OWIE_BIT		(1 << 27) +/* OTG power mask */ +#define MXC_OTG_UCTRL_OPM_BIT		(1 << 24) +/* OTG power pin polarity */ +#define MXC_OTG_UCTRL_O_PWR_POL_BIT	(1 << 24) +/* Host1 ULPI interrupt enable */ +#define MXC_H1_UCTRL_H1UIE_BIT		(1 << 12) +/* HOST1 wakeup intr enable */ +#define MXC_H1_UCTRL_H1WIE_BIT		(1 << 11) +/* HOST1 power mask */ +#define MXC_H1_UCTRL_H1PM_BIT		(1 << 8) +/* HOST1 power pin polarity */ +#define MXC_H1_UCTRL_H1_PWR_POL_BIT	(1 << 8) + +/* USB_PHY_CTRL_FUNC */ +/* OTG Polarity of Overcurrent */ +#define MXC_OTG_PHYCTRL_OC_POL_BIT	(1 << 9) +/* OTG Disable Overcurrent Event */ +#define MXC_OTG_PHYCTRL_OC_DIS_BIT	(1 << 8) +/* UH1 Polarity of Overcurrent */ +#define MXC_H1_OC_POL_BIT		(1 << 6) +/* UH1 Disable Overcurrent Event */ +#define MXC_H1_OC_DIS_BIT		(1 << 5) +/* OTG Power Pin Polarity */ +#define MXC_OTG_PHYCTRL_PWR_POL_BIT	(1 << 3) + +/* USBH2CTRL */ +#define MXC_H2_UCTRL_H2_OC_POL_BIT	(1 << 31) +#define MXC_H2_UCTRL_H2_OC_DIS_BIT	(1 << 30) +#define MXC_H2_UCTRL_H2UIE_BIT		(1 << 8) +#define MXC_H2_UCTRL_H2WIE_BIT		(1 << 7) +#define MXC_H2_UCTRL_H2PM_BIT		(1 << 4) +#define MXC_H2_UCTRL_H2_PWR_POL_BIT	(1 << 4) + +/* USBH3CTRL */ +#define MXC_H3_UCTRL_H3_OC_POL_BIT	(1 << 31) +#define MXC_H3_UCTRL_H3_OC_DIS_BIT	(1 << 30) +#define MXC_H3_UCTRL_H3UIE_BIT		(1 << 8) +#define MXC_H3_UCTRL_H3WIE_BIT		(1 << 7) +#define MXC_H3_UCTRL_H3_PWR_POL_BIT	(1 << 4) + +/* USB_CTRL_1 */ +#define MXC_USB_CTRL_UH1_EXT_CLK_EN	(1 << 25) + +int mxc_set_usbcontrol(int port, unsigned int flags) +{ +	unsigned int v; +	void __iomem *usb_base = (void __iomem *)OTG_BASE_ADDR; +	void __iomem *usbother_base; +	int ret = 0; + +	usbother_base = usb_base + MX5_USBOTHER_REGS_OFFSET; + +	switch (port) { +	case 0:	/* OTG port */ +		if (flags & MXC_EHCI_INTERNAL_PHY) { +			v = __raw_readl(usbother_base + +					MXC_USB_PHY_CTR_FUNC_OFFSET); +			if (flags & MXC_EHCI_OC_PIN_ACTIVE_LOW) +				v |= MXC_OTG_PHYCTRL_OC_POL_BIT; +			else +				v &= ~MXC_OTG_PHYCTRL_OC_POL_BIT; +			if (flags & MXC_EHCI_POWER_PINS_ENABLED) +				/* OC/USBPWR is used */ +				v &= ~MXC_OTG_PHYCTRL_OC_DIS_BIT; +			else +				/* OC/USBPWR is not used */ +				v |= MXC_OTG_PHYCTRL_OC_DIS_BIT; +#ifdef CONFIG_MX51 +			if (flags & MXC_EHCI_PWR_PIN_ACTIVE_HIGH) +				v |= MXC_OTG_PHYCTRL_PWR_POL_BIT; +			else +				v &= ~MXC_OTG_PHYCTRL_PWR_POL_BIT; +#endif +			__raw_writel(v, usbother_base + +					MXC_USB_PHY_CTR_FUNC_OFFSET); + +			v = __raw_readl(usbother_base + MXC_USBCTRL_OFFSET); +#ifdef CONFIG_MX51 +			if (flags & MXC_EHCI_POWER_PINS_ENABLED) +				v &= ~MXC_OTG_UCTRL_OPM_BIT; +			else +				v |= MXC_OTG_UCTRL_OPM_BIT; +#endif +#ifdef CONFIG_MX53 +			if (flags & MXC_EHCI_PWR_PIN_ACTIVE_HIGH) +				v |= MXC_OTG_UCTRL_O_PWR_POL_BIT; +			else +				v &= ~MXC_OTG_UCTRL_O_PWR_POL_BIT; +#endif +			__raw_writel(v, usbother_base + MXC_USBCTRL_OFFSET); +		} +		break; +	case 1:	/* Host 1 ULPI */ +#ifdef CONFIG_MX51 +		/* The clock for the USBH1 ULPI port will come externally +		   from the PHY. */ +		v = __raw_readl(usbother_base + MXC_USB_CTRL_1_OFFSET); +		__raw_writel(v | MXC_USB_CTRL_UH1_EXT_CLK_EN, usbother_base + +				MXC_USB_CTRL_1_OFFSET); +#endif + +		v = __raw_readl(usbother_base + MXC_USBCTRL_OFFSET); +#ifdef CONFIG_MX51 +		if (flags & MXC_EHCI_POWER_PINS_ENABLED) +			v &= ~MXC_H1_UCTRL_H1PM_BIT; /* H1 power mask unused */ +		else +			v |= MXC_H1_UCTRL_H1PM_BIT; /* H1 power mask used */ +#endif +#ifdef CONFIG_MX53 +		if (flags & MXC_EHCI_PWR_PIN_ACTIVE_HIGH) +			v |= MXC_H1_UCTRL_H1_PWR_POL_BIT; +		else +			v &= ~MXC_H1_UCTRL_H1_PWR_POL_BIT; +#endif +		__raw_writel(v, usbother_base + MXC_USBCTRL_OFFSET); + +		v = __raw_readl(usbother_base + MXC_USB_PHY_CTR_FUNC_OFFSET); +		if (flags & MXC_EHCI_OC_PIN_ACTIVE_LOW) +			v |= MXC_H1_OC_POL_BIT; +		else +			v &= ~MXC_H1_OC_POL_BIT; +		if (flags & MXC_EHCI_POWER_PINS_ENABLED) +			v &= ~MXC_H1_OC_DIS_BIT; /* OC is used */ +		else +			v |= MXC_H1_OC_DIS_BIT; /* OC is not used */ +		__raw_writel(v, usbother_base + MXC_USB_PHY_CTR_FUNC_OFFSET); + +		break; +	case 2: /* Host 2 ULPI */ +		v = __raw_readl(usbother_base + MXC_USBH2CTRL_OFFSET); +#ifdef CONFIG_MX51 +		if (flags & MXC_EHCI_POWER_PINS_ENABLED) +			v &= ~MXC_H2_UCTRL_H2PM_BIT; /* H2 power mask unused */ +		else +			v |= MXC_H2_UCTRL_H2PM_BIT; /* H2 power mask used */ +#endif +#ifdef CONFIG_MX53 +		if (flags & MXC_EHCI_OC_PIN_ACTIVE_LOW) +			v |= MXC_H2_UCTRL_H2_OC_POL_BIT; +		else +			v &= ~MXC_H2_UCTRL_H2_OC_POL_BIT; +		if (flags & MXC_EHCI_POWER_PINS_ENABLED) +			v &= ~MXC_H2_UCTRL_H2_OC_DIS_BIT; /* OC is used */ +		else +			v |= MXC_H2_UCTRL_H2_OC_DIS_BIT; /* OC is not used */ +		if (flags & MXC_EHCI_PWR_PIN_ACTIVE_HIGH) +			v |= MXC_H2_UCTRL_H2_PWR_POL_BIT; +		else +			v &= ~MXC_H2_UCTRL_H2_PWR_POL_BIT; +#endif +		__raw_writel(v, usbother_base + MXC_USBH2CTRL_OFFSET); +		break; +#ifdef CONFIG_MX53 +	case 3: /* Host 3 ULPI */ +		v = __raw_readl(usbother_base + MXC_USBH3CTRL_OFFSET); +		if (flags & MXC_EHCI_OC_PIN_ACTIVE_LOW) +			v |= MXC_H3_UCTRL_H3_OC_POL_BIT; +		else +			v &= ~MXC_H3_UCTRL_H3_OC_POL_BIT; +		if (flags & MXC_EHCI_POWER_PINS_ENABLED) +			v &= ~MXC_H3_UCTRL_H3_OC_DIS_BIT; /* OC is used */ +		else +			v |= MXC_H3_UCTRL_H3_OC_DIS_BIT; /* OC is not used */ +		if (flags & MXC_EHCI_PWR_PIN_ACTIVE_HIGH) +			v |= MXC_H3_UCTRL_H3_PWR_POL_BIT; +		else +			v &= ~MXC_H3_UCTRL_H3_PWR_POL_BIT; +		__raw_writel(v, usbother_base + MXC_USBH3CTRL_OFFSET); +		break; +#endif +	} + +	return ret; +} + +int __weak board_ehci_hcd_init(int port) +{ +	return 0; +} + +void __weak board_ehci_hcd_postinit(struct usb_ehci *ehci, int port) +{ +} + +int ehci_hcd_init(int index, enum usb_init_type init, +		struct ehci_hccr **hccr, struct ehci_hcor **hcor) +{ +	struct usb_ehci *ehci; + +	set_usboh3_clk(); +	enable_usboh3_clk(true); +	set_usb_phy_clk(); +	enable_usb_phy1_clk(true); +	enable_usb_phy2_clk(true); +	mdelay(1); + +	/* Do board specific initialization */ +	board_ehci_hcd_init(CONFIG_MXC_USB_PORT); + +	ehci = (struct usb_ehci *)(OTG_BASE_ADDR + +		(0x200 * CONFIG_MXC_USB_PORT)); +	*hccr = (struct ehci_hccr *)((uint32_t)&ehci->caplength); +	*hcor = (struct ehci_hcor *)((uint32_t)*hccr + +			HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase))); +	setbits_le32(&ehci->usbmode, CM_HOST); + +	__raw_writel(CONFIG_MXC_USB_PORTSC, &ehci->portsc); +	setbits_le32(&ehci->portsc, USB_EN); + +	mxc_set_usbcontrol(CONFIG_MXC_USB_PORT, CONFIG_MXC_USB_FLAGS); +	mdelay(10); + +	/* Do board specific post-initialization */ +	board_ehci_hcd_postinit(ehci, CONFIG_MXC_USB_PORT); + +	return 0; +} + +int ehci_hcd_stop(int index) +{ +	return 0; +} diff --git a/roms/u-boot/drivers/usb/host/ehci-mx6.c b/roms/u-boot/drivers/usb/host/ehci-mx6.c new file mode 100644 index 00000000..c0a557b2 --- /dev/null +++ b/roms/u-boot/drivers/usb/host/ehci-mx6.c @@ -0,0 +1,248 @@ +/* + * Copyright (c) 2009 Daniel Mack <daniel@caiaq.de> + * Copyright (C) 2010 Freescale Semiconductor, Inc. + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +#include <common.h> +#include <usb.h> +#include <errno.h> +#include <linux/compiler.h> +#include <usb/ehci-fsl.h> +#include <asm/io.h> +#include <asm/arch/imx-regs.h> +#include <asm/arch/clock.h> +#include <asm/imx-common/iomux-v3.h> + +#include "ehci.h" + +#define USB_OTGREGS_OFFSET	0x000 +#define USB_H1REGS_OFFSET	0x200 +#define USB_H2REGS_OFFSET	0x400 +#define USB_H3REGS_OFFSET	0x600 +#define USB_OTHERREGS_OFFSET	0x800 + +#define USB_H1_CTRL_OFFSET	0x04 + +#define USBPHY_CTRL				0x00000030 +#define USBPHY_CTRL_SET				0x00000034 +#define USBPHY_CTRL_CLR				0x00000038 +#define USBPHY_CTRL_TOG				0x0000003c + +#define USBPHY_PWD				0x00000000 +#define USBPHY_CTRL_SFTRST			0x80000000 +#define USBPHY_CTRL_CLKGATE			0x40000000 +#define USBPHY_CTRL_ENUTMILEVEL3		0x00008000 +#define USBPHY_CTRL_ENUTMILEVEL2		0x00004000 +#define USBPHY_CTRL_OTG_ID			0x08000000 + +#define ANADIG_USB2_CHRG_DETECT_EN_B		0x00100000 +#define ANADIG_USB2_CHRG_DETECT_CHK_CHRG_B	0x00080000 + +#define ANADIG_USB2_PLL_480_CTRL_BYPASS		0x00010000 +#define ANADIG_USB2_PLL_480_CTRL_ENABLE		0x00002000 +#define ANADIG_USB2_PLL_480_CTRL_POWER		0x00001000 +#define ANADIG_USB2_PLL_480_CTRL_EN_USB_CLKS	0x00000040 + + +#define UCTRL_OVER_CUR_POL	(1 << 8) /* OTG Polarity of Overcurrent */ +#define UCTRL_OVER_CUR_DIS	(1 << 7) /* Disable OTG Overcurrent Detection */ + +/* USBCMD */ +#define UCMD_RUN_STOP           (1 << 0) /* controller run/stop */ +#define UCMD_RESET		(1 << 1) /* controller reset */ + +static const unsigned phy_bases[] = { +	USB_PHY0_BASE_ADDR, +	USB_PHY1_BASE_ADDR, +}; + +static void usb_internal_phy_clock_gate(int index, int on) +{ +	void __iomem *phy_reg; + +	if (index >= ARRAY_SIZE(phy_bases)) +		return; + +	phy_reg = (void __iomem *)phy_bases[index]; +	phy_reg += on ? USBPHY_CTRL_CLR : USBPHY_CTRL_SET; +	__raw_writel(USBPHY_CTRL_CLKGATE, phy_reg); +} + +static void usb_power_config(int index) +{ +	struct anatop_regs __iomem *anatop = +		(struct anatop_regs __iomem *)ANATOP_BASE_ADDR; +	void __iomem *chrg_detect; +	void __iomem *pll_480_ctrl_clr; +	void __iomem *pll_480_ctrl_set; + +	switch (index) { +	case 0: +		chrg_detect = &anatop->usb1_chrg_detect; +		pll_480_ctrl_clr = &anatop->usb1_pll_480_ctrl_clr; +		pll_480_ctrl_set = &anatop->usb1_pll_480_ctrl_set; +		break; +	case 1: +		chrg_detect = &anatop->usb2_chrg_detect; +		pll_480_ctrl_clr = &anatop->usb2_pll_480_ctrl_clr; +		pll_480_ctrl_set = &anatop->usb2_pll_480_ctrl_set; +		break; +	default: +		return; +	} +	/* +	 * Some phy and power's special controls +	 * 1. The external charger detector needs to be disabled +	 * or the signal at DP will be poor +	 * 2. The PLL's power and output to usb +	 * is totally controlled by IC, so the Software only needs +	 * to enable them at initializtion. +	 */ +	__raw_writel(ANADIG_USB2_CHRG_DETECT_EN_B | +		     ANADIG_USB2_CHRG_DETECT_CHK_CHRG_B, +		     chrg_detect); + +	__raw_writel(ANADIG_USB2_PLL_480_CTRL_BYPASS, +		     pll_480_ctrl_clr); + +	__raw_writel(ANADIG_USB2_PLL_480_CTRL_ENABLE | +		     ANADIG_USB2_PLL_480_CTRL_POWER | +		     ANADIG_USB2_PLL_480_CTRL_EN_USB_CLKS, +		     pll_480_ctrl_set); +} + +/* Return 0 : host node, <>0 : device mode */ +static int usb_phy_enable(int index, struct usb_ehci *ehci) +{ +	void __iomem *phy_reg; +	void __iomem *phy_ctrl; +	void __iomem *usb_cmd; +	u32 val; + +	if (index >= ARRAY_SIZE(phy_bases)) +		return 0; + +	phy_reg = (void __iomem *)phy_bases[index]; +	phy_ctrl = (void __iomem *)(phy_reg + USBPHY_CTRL); +	usb_cmd = (void __iomem *)&ehci->usbcmd; + +	/* Stop then Reset */ +	val = __raw_readl(usb_cmd); +	val &= ~UCMD_RUN_STOP; +	__raw_writel(val, usb_cmd); +	while (__raw_readl(usb_cmd) & UCMD_RUN_STOP) +		; + +	val = __raw_readl(usb_cmd); +	val |= UCMD_RESET; +	__raw_writel(val, usb_cmd); +	while (__raw_readl(usb_cmd) & UCMD_RESET) +		; + +	/* Reset USBPHY module */ +	val = __raw_readl(phy_ctrl); +	val |= USBPHY_CTRL_SFTRST; +	__raw_writel(val, phy_ctrl); +	udelay(10); + +	/* Remove CLKGATE and SFTRST */ +	val = __raw_readl(phy_ctrl); +	val &= ~(USBPHY_CTRL_CLKGATE | USBPHY_CTRL_SFTRST); +	__raw_writel(val, phy_ctrl); +	udelay(10); + +	/* Power up the PHY */ +	__raw_writel(0, phy_reg + USBPHY_PWD); +	/* enable FS/LS device */ +	val = __raw_readl(phy_ctrl); +	val |= (USBPHY_CTRL_ENUTMILEVEL2 | USBPHY_CTRL_ENUTMILEVEL3); +	__raw_writel(val, phy_ctrl); + +	return val & USBPHY_CTRL_OTG_ID; +} + +/* Base address for this IP block is 0x02184800 */ +struct usbnc_regs { +	u32	ctrl[4];	/* otg/host1-3 */ +	u32	uh2_hsic_ctrl; +	u32	uh3_hsic_ctrl; +	u32	otg_phy_ctrl_0; +	u32	uh1_phy_ctrl_0; +}; + +static void usb_oc_config(int index) +{ +	struct usbnc_regs *usbnc = (struct usbnc_regs *)(USBOH3_USB_BASE_ADDR + +			USB_OTHERREGS_OFFSET); +	void __iomem *ctrl = (void __iomem *)(&usbnc->ctrl[index]); +	u32 val; + +	val = __raw_readl(ctrl); +#if CONFIG_MACH_TYPE == MACH_TYPE_MX6Q_ARM2 +	/* mx6qarm2 seems to required a different setting*/ +	val &= ~UCTRL_OVER_CUR_POL; +#else +	val |= UCTRL_OVER_CUR_POL; +#endif +	__raw_writel(val, ctrl); + +	val = __raw_readl(ctrl); +	val |= UCTRL_OVER_CUR_DIS; +	__raw_writel(val, ctrl); +} + +int __weak board_ehci_hcd_init(int port) +{ +	return 0; +} + +int __weak board_ehci_power(int port, int on) +{ +	return 0; +} + +int ehci_hcd_init(int index, enum usb_init_type init, +		struct ehci_hccr **hccr, struct ehci_hcor **hcor) +{ +	enum usb_init_type type; +	struct usb_ehci *ehci = (struct usb_ehci *)(USBOH3_USB_BASE_ADDR + +		(0x200 * index)); + +	if (index > 3) +		return -EINVAL; +	enable_usboh3_clk(1); +	mdelay(1); + +	/* Do board specific initialization */ +	board_ehci_hcd_init(index); + +	usb_power_config(index); +	usb_oc_config(index); +	usb_internal_phy_clock_gate(index, 1); +	type = usb_phy_enable(index, ehci) ? USB_INIT_DEVICE : USB_INIT_HOST; + +	*hccr = (struct ehci_hccr *)((uint32_t)&ehci->caplength); +	*hcor = (struct ehci_hcor *)((uint32_t)*hccr + +			HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase))); + +	if ((type == init) || (type == USB_INIT_DEVICE)) +		board_ehci_power(index, (type == USB_INIT_DEVICE) ? 0 : 1); +	if (type != init) +		return -ENODEV; +	if (type == USB_INIT_DEVICE) +		return 0; +	setbits_le32(&ehci->usbmode, CM_HOST); +	__raw_writel(CONFIG_MXC_USB_PORTSC, &ehci->portsc); +	setbits_le32(&ehci->portsc, USB_EN); + +	mdelay(10); + +	return 0; +} + +int ehci_hcd_stop(int index) +{ +	return 0; +} diff --git a/roms/u-boot/drivers/usb/host/ehci-mxc.c b/roms/u-boot/drivers/usb/host/ehci-mxc.c new file mode 100644 index 00000000..f09c75a9 --- /dev/null +++ b/roms/u-boot/drivers/usb/host/ehci-mxc.c @@ -0,0 +1,250 @@ +/* + * Copyright (c) 2009 Daniel Mack <daniel@caiaq.de> + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + + +#include <common.h> +#include <usb.h> +#include <asm/io.h> +#include <asm/arch/imx-regs.h> +#include <usb/ehci-fsl.h> +#include <errno.h> + +#include "ehci.h" + +#define USBCTRL_OTGBASE_OFFSET	0x600 + +#define MX25_OTG_SIC_SHIFT	29 +#define MX25_OTG_SIC_MASK	(0x3 << MX25_OTG_SIC_SHIFT) +#define MX25_OTG_PM_BIT		(1 << 24) +#define MX25_OTG_PP_BIT		(1 << 11) +#define MX25_OTG_OCPOL_BIT	(1 << 3) + +#define MX25_H1_SIC_SHIFT	21 +#define MX25_H1_SIC_MASK	(0x3 << MX25_H1_SIC_SHIFT) +#define MX25_H1_PP_BIT		(1 << 18) +#define MX25_H1_PM_BIT		(1 << 16) +#define MX25_H1_IPPUE_UP_BIT	(1 << 7) +#define MX25_H1_IPPUE_DOWN_BIT	(1 << 6) +#define MX25_H1_TLL_BIT		(1 << 5) +#define MX25_H1_USBTE_BIT	(1 << 4) +#define MX25_H1_OCPOL_BIT	(1 << 2) + +#define MX31_OTG_SIC_SHIFT	29 +#define MX31_OTG_SIC_MASK	(0x3 << MX31_OTG_SIC_SHIFT) +#define MX31_OTG_PM_BIT		(1 << 24) + +#define MX31_H2_SIC_SHIFT	21 +#define MX31_H2_SIC_MASK	(0x3 << MX31_H2_SIC_SHIFT) +#define MX31_H2_PM_BIT		(1 << 16) +#define MX31_H2_DT_BIT		(1 << 5) + +#define MX31_H1_SIC_SHIFT	13 +#define MX31_H1_SIC_MASK	(0x3 << MX31_H1_SIC_SHIFT) +#define MX31_H1_PM_BIT		(1 << 8) +#define MX31_H1_DT_BIT		(1 << 4) + +#define MX35_OTG_SIC_SHIFT	29 +#define MX35_OTG_SIC_MASK	(0x3 << MX35_OTG_SIC_SHIFT) +#define MX35_OTG_PM_BIT		(1 << 24) +#define MX35_OTG_PP_BIT		(1 << 11) +#define MX35_OTG_OCPOL_BIT	(1 << 3) + +#define MX35_H1_SIC_SHIFT	21 +#define MX35_H1_SIC_MASK	(0x3 << MX35_H1_SIC_SHIFT) +#define MX35_H1_PP_BIT		(1 << 18) +#define MX35_H1_PM_BIT		(1 << 16) +#define MX35_H1_IPPUE_UP_BIT	(1 << 7) +#define MX35_H1_IPPUE_DOWN_BIT	(1 << 6) +#define MX35_H1_TLL_BIT		(1 << 5) +#define MX35_H1_USBTE_BIT	(1 << 4) +#define MX35_H1_OCPOL_BIT	(1 << 2) + +static int mxc_set_usbcontrol(int port, unsigned int flags) +{ +	unsigned int v; + +	v = readl(IMX_USB_BASE + USBCTRL_OTGBASE_OFFSET); +#if defined(CONFIG_MX25) +	switch (port) { +	case 0:	/* OTG port */ +		v &= ~(MX25_OTG_SIC_MASK | MX25_OTG_PM_BIT | MX25_OTG_PP_BIT | +				MX25_OTG_OCPOL_BIT); +		v |= (flags & MXC_EHCI_INTERFACE_MASK) << MX25_OTG_SIC_SHIFT; + +		if (!(flags & MXC_EHCI_POWER_PINS_ENABLED)) +			v |= MX25_OTG_PM_BIT; + +		if (flags & MXC_EHCI_PWR_PIN_ACTIVE_HIGH) +			v |= MX25_OTG_PP_BIT; + +		if (!(flags & MXC_EHCI_OC_PIN_ACTIVE_LOW)) +			v |= MX25_OTG_OCPOL_BIT; + +		break; +	case 1: /* H1 port */ +		v &= ~(MX25_H1_SIC_MASK | MX25_H1_PM_BIT | MX25_H1_PP_BIT | +				MX25_H1_OCPOL_BIT | MX25_H1_TLL_BIT | +				MX25_H1_USBTE_BIT | MX25_H1_IPPUE_DOWN_BIT | +				MX25_H1_IPPUE_UP_BIT); +		v |= (flags & MXC_EHCI_INTERFACE_MASK) << MX25_H1_SIC_SHIFT; + +		if (!(flags & MXC_EHCI_POWER_PINS_ENABLED)) +			v |= MX25_H1_PM_BIT; + +		if (flags & MXC_EHCI_PWR_PIN_ACTIVE_HIGH) +			v |= MX25_H1_PP_BIT; + +		if (!(flags & MXC_EHCI_OC_PIN_ACTIVE_LOW)) +			v |= MX25_H1_OCPOL_BIT; + +		if (!(flags & MXC_EHCI_TTL_ENABLED)) +			v |= MX25_H1_TLL_BIT; + +		if (flags & MXC_EHCI_INTERNAL_PHY) +			v |= MX25_H1_USBTE_BIT; + +		if (flags & MXC_EHCI_IPPUE_DOWN) +			v |= MX25_H1_IPPUE_DOWN_BIT; + +		if (flags & MXC_EHCI_IPPUE_UP) +			v |= MX25_H1_IPPUE_UP_BIT; + +		break; +	default: +		return -EINVAL; +	} +#elif defined(CONFIG_MX31) +	switch (port) { +	case 0:	/* OTG port */ +		v &= ~(MX31_OTG_SIC_MASK | MX31_OTG_PM_BIT); +		v |= (flags & MXC_EHCI_INTERFACE_MASK) << MX31_OTG_SIC_SHIFT; + +		if (!(flags & MXC_EHCI_POWER_PINS_ENABLED)) +			v |= MX31_OTG_PM_BIT; + +		break; +	case 1: /* H1 port */ +		v &= ~(MX31_H1_SIC_MASK | MX31_H1_PM_BIT | MX31_H1_DT_BIT); +		v |= (flags & MXC_EHCI_INTERFACE_MASK) << MX31_H1_SIC_SHIFT; + +		if (!(flags & MXC_EHCI_POWER_PINS_ENABLED)) +			v |= MX31_H1_PM_BIT; + +		if (!(flags & MXC_EHCI_TTL_ENABLED)) +			v |= MX31_H1_DT_BIT; + +		break; +	case 2:	/* H2 port */ +		v &= ~(MX31_H2_SIC_MASK | MX31_H2_PM_BIT | MX31_H2_DT_BIT); +		v |= (flags & MXC_EHCI_INTERFACE_MASK) << MX31_H2_SIC_SHIFT; + +		if (!(flags & MXC_EHCI_POWER_PINS_ENABLED)) +			v |= MX31_H2_PM_BIT; + +		if (!(flags & MXC_EHCI_TTL_ENABLED)) +			v |= MX31_H2_DT_BIT; + +		break; +	default: +		return -EINVAL; +	} +#elif defined(CONFIG_MX35) +	switch (port) { +	case 0:	/* OTG port */ +		v &= ~(MX35_OTG_SIC_MASK | MX35_OTG_PM_BIT | MX35_OTG_PP_BIT | +				MX35_OTG_OCPOL_BIT); +		v |= (flags & MXC_EHCI_INTERFACE_MASK) << MX35_OTG_SIC_SHIFT; + +		if (!(flags & MXC_EHCI_POWER_PINS_ENABLED)) +			v |= MX35_OTG_PM_BIT; + +		if (flags & MXC_EHCI_PWR_PIN_ACTIVE_HIGH) +			v |= MX35_OTG_PP_BIT; + +		if (!(flags & MXC_EHCI_OC_PIN_ACTIVE_LOW)) +			v |= MX35_OTG_OCPOL_BIT; + +		break; +	case 1: /* H1 port */ +		v &= ~(MX35_H1_SIC_MASK | MX35_H1_PM_BIT | MX35_H1_PP_BIT | +				MX35_H1_OCPOL_BIT | MX35_H1_TLL_BIT | +				MX35_H1_USBTE_BIT | MX35_H1_IPPUE_DOWN_BIT | +				MX35_H1_IPPUE_UP_BIT); +		v |= (flags & MXC_EHCI_INTERFACE_MASK) << MX35_H1_SIC_SHIFT; + +		if (!(flags & MXC_EHCI_POWER_PINS_ENABLED)) +			v |= MX35_H1_PM_BIT; + +		if (flags & MXC_EHCI_PWR_PIN_ACTIVE_HIGH) +			v |= MX35_H1_PP_BIT; + +		if (!(flags & MXC_EHCI_OC_PIN_ACTIVE_LOW)) +			v |= MX35_H1_OCPOL_BIT; + +		if (!(flags & MXC_EHCI_TTL_ENABLED)) +			v |= MX35_H1_TLL_BIT; + +		if (flags & MXC_EHCI_INTERNAL_PHY) +			v |= MX35_H1_USBTE_BIT; + +		if (flags & MXC_EHCI_IPPUE_DOWN) +			v |= MX35_H1_IPPUE_DOWN_BIT; + +		if (flags & MXC_EHCI_IPPUE_UP) +			v |= MX35_H1_IPPUE_UP_BIT; + +		break; +	default: +		return -EINVAL; +	} +#else +#error MXC EHCI USB driver not supported on this platform +#endif +	writel(v, IMX_USB_BASE + USBCTRL_OTGBASE_OFFSET); + +	return 0; +} + +int ehci_hcd_init(int index, enum usb_init_type init, +		struct ehci_hccr **hccr, struct ehci_hcor **hcor) +{ +	struct usb_ehci *ehci; +#ifdef CONFIG_MX31 +	struct clock_control_regs *sc_regs = +		(struct clock_control_regs *)CCM_BASE; + +	__raw_readl(&sc_regs->ccmr); +	__raw_writel(__raw_readl(&sc_regs->ccmr) | (1 << 9), &sc_regs->ccmr) ; +#endif + +	udelay(80); + +	ehci = (struct usb_ehci *)(IMX_USB_BASE + +			IMX_USB_PORT_OFFSET * CONFIG_MXC_USB_PORT); +	*hccr = (struct ehci_hccr *)((uint32_t)&ehci->caplength); +	*hcor = (struct ehci_hcor *)((uint32_t) *hccr + +			HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase))); +	setbits_le32(&ehci->usbmode, CM_HOST); +	__raw_writel(CONFIG_MXC_USB_PORTSC, &ehci->portsc); +	mxc_set_usbcontrol(CONFIG_MXC_USB_PORT, CONFIG_MXC_USB_FLAGS); +#ifdef CONFIG_MX35 +	/* Workaround for ENGcm11601 */ +	__raw_writel(0, &ehci->sbuscfg); +#endif + +	udelay(10000); + +	return 0; +} + +/* + * Destroy the appropriate control structures corresponding + * the the EHCI host controller. + */ +int ehci_hcd_stop(int index) +{ +	return 0; +} diff --git a/roms/u-boot/drivers/usb/host/ehci-mxs.c b/roms/u-boot/drivers/usb/host/ehci-mxs.c new file mode 100644 index 00000000..4d652b32 --- /dev/null +++ b/roms/u-boot/drivers/usb/host/ehci-mxs.c @@ -0,0 +1,158 @@ +/* + * Freescale i.MX28 USB Host driver + * + * Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com> + * on behalf of DENX Software Engineering GmbH + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +#include <common.h> +#include <asm/io.h> +#include <asm/arch/imx-regs.h> +#include <errno.h> + +#include "ehci.h" + +/* This DIGCTL register ungates clock to USB */ +#define	HW_DIGCTL_CTRL			0x8001c000 +#define	HW_DIGCTL_CTRL_USB0_CLKGATE	(1 << 2) +#define	HW_DIGCTL_CTRL_USB1_CLKGATE	(1 << 16) + +struct ehci_mxs_port { +	uint32_t		usb_regs; +	struct mxs_usbphy_regs	*phy_regs; + +	struct mxs_register_32	*pll; +	uint32_t		pll_en_bits; +	uint32_t		pll_dis_bits; +	uint32_t		gate_bits; +}; + +static const struct ehci_mxs_port mxs_port[] = { +#ifdef CONFIG_EHCI_MXS_PORT0 +	{ +		MXS_USBCTRL0_BASE, +		(struct mxs_usbphy_regs *)MXS_USBPHY0_BASE, +		(struct mxs_register_32 *)(MXS_CLKCTRL_BASE + +			offsetof(struct mxs_clkctrl_regs, +			hw_clkctrl_pll0ctrl0_reg)), +		CLKCTRL_PLL0CTRL0_EN_USB_CLKS | CLKCTRL_PLL0CTRL0_POWER, +		CLKCTRL_PLL0CTRL0_EN_USB_CLKS, +		HW_DIGCTL_CTRL_USB0_CLKGATE, +	}, +#endif +#ifdef CONFIG_EHCI_MXS_PORT1 +	{ +		MXS_USBCTRL1_BASE, +		(struct mxs_usbphy_regs *)MXS_USBPHY1_BASE, +		(struct mxs_register_32 *)(MXS_CLKCTRL_BASE + +			offsetof(struct mxs_clkctrl_regs, +			hw_clkctrl_pll1ctrl0_reg)), +		CLKCTRL_PLL1CTRL0_EN_USB_CLKS | CLKCTRL_PLL1CTRL0_POWER, +		CLKCTRL_PLL1CTRL0_EN_USB_CLKS, +		HW_DIGCTL_CTRL_USB1_CLKGATE, +	}, +#endif +}; + +static int ehci_mxs_toggle_clock(const struct ehci_mxs_port *port, int enable) +{ +	struct mxs_register_32 *digctl_ctrl = +		(struct mxs_register_32 *)HW_DIGCTL_CTRL; +	int pll_offset, dig_offset; + +	if (enable) { +		pll_offset = offsetof(struct mxs_register_32, reg_set); +		dig_offset = offsetof(struct mxs_register_32, reg_clr); +		writel(port->gate_bits, (u32)&digctl_ctrl->reg + dig_offset); +		writel(port->pll_en_bits, (u32)port->pll + pll_offset); +	} else { +		pll_offset = offsetof(struct mxs_register_32, reg_clr); +		dig_offset = offsetof(struct mxs_register_32, reg_set); +		writel(port->pll_dis_bits, (u32)port->pll + pll_offset); +		writel(port->gate_bits, (u32)&digctl_ctrl->reg + dig_offset); +	} + +	return 0; +} + +int ehci_hcd_init(int index, enum usb_init_type init, +		struct ehci_hccr **hccr, struct ehci_hcor **hcor) +{ + +	int ret; +	uint32_t usb_base, cap_base; +	const struct ehci_mxs_port *port; + +	if ((index < 0) || (index >= ARRAY_SIZE(mxs_port))) { +		printf("Invalid port index (index = %d)!\n", index); +		return -EINVAL; +	} + +	port = &mxs_port[index]; + +	/* Reset the PHY block */ +	writel(USBPHY_CTRL_SFTRST, &port->phy_regs->hw_usbphy_ctrl_set); +	udelay(10); +	writel(USBPHY_CTRL_SFTRST | USBPHY_CTRL_CLKGATE, +		&port->phy_regs->hw_usbphy_ctrl_clr); + +	/* Enable USB clock */ +	ret = ehci_mxs_toggle_clock(port, 1); +	if (ret) +		return ret; + +	/* Start USB PHY */ +	writel(0, &port->phy_regs->hw_usbphy_pwd); + +	/* Enable UTMI+ Level 2 and Level 3 compatibility */ +	writel(USBPHY_CTRL_ENUTMILEVEL3 | USBPHY_CTRL_ENUTMILEVEL2 | 1, +		&port->phy_regs->hw_usbphy_ctrl_set); + +	usb_base = port->usb_regs + 0x100; +	*hccr = (struct ehci_hccr *)usb_base; + +	cap_base = ehci_readl(&(*hccr)->cr_capbase); +	*hcor = (struct ehci_hcor *)(usb_base + HC_LENGTH(cap_base)); + +	return 0; +} + +int ehci_hcd_stop(int index) +{ +	int ret; +	uint32_t usb_base, cap_base, tmp; +	struct ehci_hccr *hccr; +	struct ehci_hcor *hcor; +	const struct ehci_mxs_port *port; + +	if ((index < 0) || (index >= ARRAY_SIZE(mxs_port))) { +		printf("Invalid port index (index = %d)!\n", index); +		return -EINVAL; +	} + +	port = &mxs_port[index]; + +	/* Stop the USB port */ +	usb_base = port->usb_regs + 0x100; +	hccr = (struct ehci_hccr *)usb_base; +	cap_base = ehci_readl(&hccr->cr_capbase); +	hcor = (struct ehci_hcor *)(usb_base + HC_LENGTH(cap_base)); + +	tmp = ehci_readl(&hcor->or_usbcmd); +	tmp &= ~CMD_RUN; +	ehci_writel(tmp, &hcor->or_usbcmd); + +	/* Disable the PHY */ +	tmp = USBPHY_PWD_RXPWDRX | USBPHY_PWD_RXPWDDIFF | +		USBPHY_PWD_RXPWD1PT1 | USBPHY_PWD_RXPWDENV | +		USBPHY_PWD_TXPWDV2I | USBPHY_PWD_TXPWDIBIAS | +		USBPHY_PWD_TXPWDFS; +	writel(tmp, &port->phy_regs->hw_usbphy_pwd); + +	/* Disable USB clock */ +	ret = ehci_mxs_toggle_clock(port, 0); + +	return ret; +} diff --git a/roms/u-boot/drivers/usb/host/ehci-omap.c b/roms/u-boot/drivers/usb/host/ehci-omap.c new file mode 100644 index 00000000..1b215c25 --- /dev/null +++ b/roms/u-boot/drivers/usb/host/ehci-omap.c @@ -0,0 +1,295 @@ +/* + * (C) Copyright 2011 Ilya Yanok, Emcraft Systems + * (C) Copyright 2004-2008 + * Texas Instruments, <www.ti.com> + * + * Derived from Beagle Board code by + *	Sunil Kumar <sunilsaini05@gmail.com> + *	Shashi Ranjan <shashiranjanmca05@gmail.com> + * + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +#include <common.h> +#include <usb.h> +#include <usb/ulpi.h> +#include <errno.h> +#include <asm/io.h> +#include <asm/gpio.h> +#include <asm/arch/ehci.h> +#include <asm/ehci-omap.h> + +#include "ehci.h" + +static struct omap_uhh *const uhh = (struct omap_uhh *)OMAP_UHH_BASE; +static struct omap_usbtll *const usbtll = (struct omap_usbtll *)OMAP_USBTLL_BASE; +static struct omap_ehci *const ehci = (struct omap_ehci *)OMAP_EHCI_BASE; + +static int omap_uhh_reset(void) +{ +	int timeout = 0; +	u32 rev; + +	rev = readl(&uhh->rev); + +	/* Soft RESET */ +	writel(OMAP_UHH_SYSCONFIG_SOFTRESET, &uhh->sysc); + +	switch (rev) { +	case OMAP_USBHS_REV1: +		/* Wait for soft RESET to complete */ +		while (!(readl(&uhh->syss) & 0x1)) { +			if (timeout > 100) { +				printf("%s: RESET timeout\n", __func__); +				return -1; +			} +			udelay(10); +			timeout++; +		} + +		/* Set No-Idle, No-Standby */ +		writel(OMAP_UHH_SYSCONFIG_VAL, &uhh->sysc); +		break; + +	default:	/* Rev. 2 onwards */ + +		udelay(2); /* Need to wait before accessing SYSCONFIG back */ + +		/* Wait for soft RESET to complete */ +		while ((readl(&uhh->sysc) & 0x1)) { +			if (timeout > 100) { +				printf("%s: RESET timeout\n", __func__); +				return -1; +			} +			udelay(10); +			timeout++; +		} + +		writel(OMAP_UHH_SYSCONFIG_VAL, &uhh->sysc); +		break; +	} + +	return 0; +} + +static int omap_ehci_tll_reset(void) +{ +	unsigned long init = get_timer(0); + +	/* perform TLL soft reset, and wait until reset is complete */ +	writel(OMAP_USBTLL_SYSCONFIG_SOFTRESET, &usbtll->sysc); + +	/* Wait for TLL reset to complete */ +	while (!(readl(&usbtll->syss) & OMAP_USBTLL_SYSSTATUS_RESETDONE)) +		if (get_timer(init) > CONFIG_SYS_HZ) { +			debug("OMAP EHCI error: timeout resetting TLL\n"); +			return -EL3RST; +	} + +	return 0; +} + +static void omap_usbhs_hsic_init(int port) +{ +	unsigned int reg; + +	/* Enable channels now */ +	reg = readl(&usbtll->channel_conf + port); + +	setbits_le32(®, (OMAP_TLL_CHANNEL_CONF_CHANMODE_TRANSPARENT_UTMI +		| OMAP_TLL_CHANNEL_CONF_ULPINOBITSTUFF +		| OMAP_TLL_CHANNEL_CONF_DRVVBUS +		| OMAP_TLL_CHANNEL_CONF_CHRGVBUS +		| OMAP_TLL_CHANNEL_CONF_CHANEN)); + +	writel(reg, &usbtll->channel_conf + port); +} + +#ifdef CONFIG_USB_ULPI +static void omap_ehci_soft_phy_reset(int port) +{ +	struct ulpi_viewport ulpi_vp; + +	ulpi_vp.viewport_addr = (u32)&ehci->insreg05_utmi_ulpi; +	ulpi_vp.port_num = port; + +	ulpi_reset(&ulpi_vp); +} +#else +static void omap_ehci_soft_phy_reset(int port) +{ +	return; +} +#endif + +#if defined(CONFIG_OMAP_EHCI_PHY1_RESET_GPIO) || \ +	defined(CONFIG_OMAP_EHCI_PHY2_RESET_GPIO) || \ +	defined(CONFIG_OMAP_EHCI_PHY3_RESET_GPIO) +/* controls PHY(s) reset signal(s) */ +static inline void omap_ehci_phy_reset(int on, int delay) +{ +	/* +	 * Refer ISSUE1: +	 * Hold the PHY in RESET for enough time till +	 * PHY is settled and ready +	 */ +	if (delay && !on) +		udelay(delay); +#ifdef CONFIG_OMAP_EHCI_PHY1_RESET_GPIO +	gpio_request(CONFIG_OMAP_EHCI_PHY1_RESET_GPIO, "USB PHY1 reset"); +	gpio_direction_output(CONFIG_OMAP_EHCI_PHY1_RESET_GPIO, !on); +#endif +#ifdef CONFIG_OMAP_EHCI_PHY2_RESET_GPIO +	gpio_request(CONFIG_OMAP_EHCI_PHY2_RESET_GPIO, "USB PHY2 reset"); +	gpio_direction_output(CONFIG_OMAP_EHCI_PHY2_RESET_GPIO, !on); +#endif +#ifdef CONFIG_OMAP_EHCI_PHY3_RESET_GPIO +	gpio_request(CONFIG_OMAP_EHCI_PHY3_RESET_GPIO, "USB PHY3 reset"); +	gpio_direction_output(CONFIG_OMAP_EHCI_PHY3_RESET_GPIO, !on); +#endif + +	/* Hold the PHY in RESET for enough time till DIR is high */ +	/* Refer: ISSUE1 */ +	if (delay && on) +		udelay(delay); +} +#else +#define omap_ehci_phy_reset(on, delay)	do {} while (0) +#endif + +/* Reset is needed otherwise the kernel-driver will throw an error. */ +int omap_ehci_hcd_stop(void) +{ +	debug("Resetting OMAP EHCI\n"); +	omap_ehci_phy_reset(1, 0); + +	if (omap_uhh_reset() < 0) +		return -1; + +	if (omap_ehci_tll_reset() < 0) +		return -1; + +	return 0; +} + +/* + * Initialize the OMAP EHCI controller and PHY. + * Based on "drivers/usb/host/ehci-omap.c" from Linux 3.1 + * See there for additional Copyrights. + */ +int omap_ehci_hcd_init(int index, struct omap_usbhs_board_data *usbhs_pdata, +		       struct ehci_hccr **hccr, struct ehci_hcor **hcor) +{ +	int ret; +	unsigned int i, reg = 0, rev = 0; + +	debug("Initializing OMAP EHCI\n"); + +	ret = board_usb_init(index, USB_INIT_HOST); +	if (ret < 0) +		return ret; + +	/* Put the PHY in RESET */ +	omap_ehci_phy_reset(1, 10); + +	ret = omap_uhh_reset(); +	if (ret < 0) +		return ret; + +	ret = omap_ehci_tll_reset(); +	if (ret) +		return ret; + +	writel(OMAP_USBTLL_SYSCONFIG_ENAWAKEUP | +		OMAP_USBTLL_SYSCONFIG_SIDLEMODE | +		OMAP_USBTLL_SYSCONFIG_CACTIVITY, &usbtll->sysc); + +	/* Put UHH in NoIdle/NoStandby mode */ +	writel(OMAP_UHH_SYSCONFIG_VAL, &uhh->sysc); + +	/* setup ULPI bypass and burst configurations */ +	clrsetbits_le32(®, OMAP_UHH_HOSTCONFIG_INCRX_ALIGN_EN, +		(OMAP_UHH_HOSTCONFIG_INCR4_BURST_EN | +		OMAP_UHH_HOSTCONFIG_INCR8_BURST_EN | +		OMAP_UHH_HOSTCONFIG_INCR16_BURST_EN)); + +	rev = readl(&uhh->rev); +	if (rev == OMAP_USBHS_REV1) { +		if (is_ehci_phy_mode(usbhs_pdata->port_mode[0])) +			clrbits_le32(®, OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS); +		else +			setbits_le32(®, OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS); + +		if (is_ehci_phy_mode(usbhs_pdata->port_mode[1])) +			clrbits_le32(®, OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS); +		else +			setbits_le32(®, OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS); + +		if (is_ehci_phy_mode(usbhs_pdata->port_mode[2])) +			clrbits_le32(®, OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS); +		else +			setbits_le32(®, OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS); +	} else if (rev == OMAP_USBHS_REV2) { + +		clrsetbits_le32(®, (OMAP_P1_MODE_CLEAR | OMAP_P2_MODE_CLEAR), +					OMAP4_UHH_HOSTCONFIG_APP_START_CLK); + +		/* Clear port mode fields for PHY mode */ + +		if (is_ehci_hsic_mode(usbhs_pdata->port_mode[0])) +			setbits_le32(®, OMAP_P1_MODE_HSIC); + +		if (is_ehci_hsic_mode(usbhs_pdata->port_mode[1])) +			setbits_le32(®, OMAP_P2_MODE_HSIC); + +	} else if (rev == OMAP_USBHS_REV2_1) { + +		clrsetbits_le32(®, +				(OMAP_P1_MODE_CLEAR | +				 OMAP_P2_MODE_CLEAR | +				 OMAP_P3_MODE_CLEAR), +				OMAP4_UHH_HOSTCONFIG_APP_START_CLK); + +		/* Clear port mode fields for PHY mode */ + +		if (is_ehci_hsic_mode(usbhs_pdata->port_mode[0])) +			setbits_le32(®, OMAP_P1_MODE_HSIC); + +		if (is_ehci_hsic_mode(usbhs_pdata->port_mode[1])) +			setbits_le32(®, OMAP_P2_MODE_HSIC); + +		if (is_ehci_hsic_mode(usbhs_pdata->port_mode[2])) +			setbits_le32(®, OMAP_P3_MODE_HSIC); +	} + +	debug("OMAP UHH_REVISION 0x%x\n", rev); +	writel(reg, &uhh->hostconfig); + +	for (i = 0; i < OMAP_HS_USB_PORTS; i++) +		if (is_ehci_hsic_mode(usbhs_pdata->port_mode[i])) +			omap_usbhs_hsic_init(i); + +	omap_ehci_phy_reset(0, 10); + +	/* +	 * An undocumented "feature" in the OMAP3 EHCI controller, +	 * causes suspended ports to be taken out of suspend when +	 * the USBCMD.Run/Stop bit is cleared (for example when +	 * we do ehci_bus_suspend). +	 * This breaks suspend-resume if the root-hub is allowed +	 * to suspend. Writing 1 to this undocumented register bit +	 * disables this feature and restores normal behavior. +	 */ +	writel(EHCI_INSNREG04_DISABLE_UNSUSPEND, &ehci->insreg04); + +	for (i = 0; i < OMAP_HS_USB_PORTS; i++) +		if (is_ehci_phy_mode(usbhs_pdata->port_mode[i])) +			omap_ehci_soft_phy_reset(i); + +	*hccr = (struct ehci_hccr *)(OMAP_EHCI_BASE); +	*hcor = (struct ehci_hcor *)(OMAP_EHCI_BASE + 0x10); + +	debug("OMAP EHCI init done\n"); +	return 0; +} diff --git a/roms/u-boot/drivers/usb/host/ehci-pci.c b/roms/u-boot/drivers/usb/host/ehci-pci.c new file mode 100644 index 00000000..991b1999 --- /dev/null +++ b/roms/u-boot/drivers/usb/host/ehci-pci.c @@ -0,0 +1,138 @@ +/*- + * Copyright (c) 2007-2008, Juniper Networks, 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 version 2 of + * the License. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <errno.h> +#include <pci.h> +#include <usb.h> + +#include "ehci.h" + +#ifdef CONFIG_PCI_EHCI_DEVICE +static struct pci_device_id ehci_pci_ids[] = { +	/* Please add supported PCI EHCI controller ids here */ +	{0x1033, 0x00E0},	/* NEC */ +	{0x10B9, 0x5239},	/* ULI1575 PCI EHCI module ids */ +	{0x12D8, 0x400F},	/* Pericom */ +	{0, 0} +}; +#else +static pci_dev_t ehci_find_class(int index) +{ +	int bus; +	int devnum; +	pci_dev_t bdf; +	uint32_t class; + +	for (bus = 0; bus <= pci_last_busno(); bus++) { +		for (devnum = 0; devnum < PCI_MAX_PCI_DEVICES-1; devnum++) { +			pci_read_config_dword(PCI_BDF(bus, devnum, 0), +					      PCI_CLASS_REVISION, &class); +			if (class >> 16 == 0xffff) +				continue; + +			for (bdf = PCI_BDF(bus, devnum, 0); +					bdf <= PCI_BDF(bus, devnum, +						PCI_MAX_PCI_FUNCTIONS - 1); +					bdf += PCI_BDF(0, 0, 1)) { +				pci_read_config_dword(bdf, PCI_CLASS_REVISION, +						      &class); +				class >>= 8; +				/* +				 * Here be dragons! In case we have multiple +				 * PCI EHCI controllers, this function will +				 * be called multiple times as well. This +				 * function will scan the PCI busses, always +				 * starting from bus 0, device 0, function 0, +				 * until it finds an USB controller. The USB +				 * stack gives us an 'index' of a controller +				 * that is currently being registered, which +				 * is a number, starting from 0 and growing +				 * in ascending order as controllers are added. +				 * To avoid probing the same controller in tne +				 * subsequent runs of this function, we will +				 * skip 'index - 1' detected controllers and +				 * report the index'th controller. +				 */ +				if (class != PCI_CLASS_SERIAL_USB_EHCI) +					continue; +				if (index) { +					index--; +					continue; +				} +				/* Return index'th controller. */ +				return bdf; +			} +		} +	} + +	return -ENODEV; +} +#endif + +/* + * Create the appropriate control structures to manage + * a new EHCI host controller. + */ +int ehci_hcd_init(int index, enum usb_init_type init, +		struct ehci_hccr **ret_hccr, struct ehci_hcor **ret_hcor) +{ +	pci_dev_t pdev; +	uint32_t cmd; +	struct ehci_hccr *hccr; +	struct ehci_hcor *hcor; + +#ifdef CONFIG_PCI_EHCI_DEVICE +	pdev = pci_find_devices(ehci_pci_ids, CONFIG_PCI_EHCI_DEVICE); +#else +	pdev = ehci_find_class(index); +#endif +	if (pdev < 0) { +		printf("EHCI host controller not found\n"); +		return -1; +	} + +	hccr = (struct ehci_hccr *)pci_map_bar(pdev, +			PCI_BASE_ADDRESS_0, PCI_REGION_MEM); +	hcor = (struct ehci_hcor *)((uint32_t) hccr + +			HC_LENGTH(ehci_readl(&hccr->cr_capbase))); + +	debug("EHCI-PCI init hccr 0x%x and hcor 0x%x hc_length %d\n", +			(uint32_t)hccr, (uint32_t)hcor, +			(uint32_t)HC_LENGTH(ehci_readl(&hccr->cr_capbase))); + +	*ret_hccr = hccr; +	*ret_hcor = hcor; + +	/* enable busmaster */ +	pci_read_config_dword(pdev, PCI_COMMAND, &cmd); +	cmd |= PCI_COMMAND_MASTER; +	pci_write_config_dword(pdev, PCI_COMMAND, cmd); +	return 0; +} + +/* + * Destroy the appropriate control structures corresponding + * the the EHCI host controller. + */ +int ehci_hcd_stop(int index) +{ +	return 0; +} diff --git a/roms/u-boot/drivers/usb/host/ehci-ppc4xx.c b/roms/u-boot/drivers/usb/host/ehci-ppc4xx.c new file mode 100644 index 00000000..9aee3ff7 --- /dev/null +++ b/roms/u-boot/drivers/usb/host/ehci-ppc4xx.c @@ -0,0 +1,34 @@ +/* + * (C) Copyright 2010, Chris Zhang <chris@seamicro.com> + * + * Author: Chris Zhang <chris@seamicro.com> + * This code is based on ehci freescale driver + * + * SPDX-License-Identifier:	GPL-2.0+ + */ +#include <common.h> +#include <usb.h> + +#include "ehci.h" + +/* + * Create the appropriate control structures to manage + * a new EHCI host controller. + */ +int ehci_hcd_init(int index, enum usb_init_type init, +		struct ehci_hccr **hccr, struct ehci_hcor **hcor) +{ +	*hccr = (struct ehci_hccr *)(CONFIG_SYS_PPC4XX_USB_ADDR); +	*hcor = (struct ehci_hcor *)((uint32_t) *hccr + +		HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase))); +	return 0; +} + +/* + * Destroy the appropriate control structures corresponding + * the the EHCI host controller. + */ +int ehci_hcd_stop(int index) +{ +	return 0; +} diff --git a/roms/u-boot/drivers/usb/host/ehci-rmobile.c b/roms/u-boot/drivers/usb/host/ehci-rmobile.c new file mode 100644 index 00000000..049e4c4e --- /dev/null +++ b/roms/u-boot/drivers/usb/host/ehci-rmobile.c @@ -0,0 +1,130 @@ +/* + *  EHCI HCD (Host Controller Driver) for USB. + * + *  Copyright (C) 2013,2014 Renesas Electronics Corporation + *  Copyright (C) 2014 Nobuhiro Iwamatsu <nobuhiro.iwamatsu.yj@renesas.com> + * + *  SPDX-License-Identifier:     GPL-2.0 + */ + +#include <common.h> +#include <asm/io.h> +#include <asm/arch/ehci-rmobile.h> +#include "ehci.h" + +#if defined(CONFIG_R8A7740) +static u32 usb_base_address[CONFIG_USB_MAX_CONTROLLER_COUNT] = { +	0xC6700000 +}; +#elif defined(CONFIG_R8A7790) +static u32 usb_base_address[CONFIG_USB_MAX_CONTROLLER_COUNT] = { +	0xEE080000,	/* USB0 (EHCI) */ +	0xEE0A0000,	/* USB1 */ +	0xEE0C0000,	/* USB2 */ +	0xEE000000	/* USB3 (USB3.0 Host)*/ +}; +#elif defined(CONFIG_R8A7791) +static u32 usb_base_address[CONFIG_USB_MAX_CONTROLLER_COUNT] = { +	0xEE080000,	/* USB0 (EHCI) */ +	0xEE0C0000,	/* USB1 */ +	0xEE000000	/* USB3 (USB3.0 Host)*/ +}; +#else +#error rmobile EHCI USB driver not supported on this platform +#endif + +int ehci_hcd_stop(int index) +{ +	int i; +	u32 base; +	struct ahbcom_pci_bridge *ahbcom_pci; + +	base = usb_base_address[index]; +	ahbcom_pci = (struct ahbcom_pci_bridge *)(base + AHBPCI_OFFSET); +	writel(0, &ahbcom_pci->ahb_bus_ctr); + +	/* reset ehci */ +	setbits_le32(base + EHCI_USBCMD, CMD_RESET); +	for (i = 100; i > 0; i--) { +		if (!(readl(base + EHCI_USBCMD) & CMD_RESET)) +			break; +		udelay(100); +	} + +	if (!i) +		printf("error : ehci(%d) reset failed.\n", index); + +	if (index == (CONFIG_USB_MAX_CONTROLLER_COUNT - 1)) +		setbits_le32(SMSTPCR7, SMSTPCR703); + +	return 0; +} + +int ehci_hcd_init(int index, enum usb_init_type init, +	struct ehci_hccr **hccr, struct ehci_hcor **hcor) +{ +	u32 base; +	u32 phys_base; +	struct rmobile_ehci_reg *rehci; +	struct ahbcom_pci_bridge *ahbcom_pci; +	struct ahbconf_pci_bridge *ahbconf_pci; +	struct ahb_pciconf *ahb_pciconf_ohci; +	struct ahb_pciconf *ahb_pciconf_ehci; +	uint32_t cap_base; + +	base = usb_base_address[index]; +	phys_base = base; +	if (index == 0) +		clrbits_le32(SMSTPCR7, SMSTPCR703); + +	rehci = (struct rmobile_ehci_reg *)(base + EHCI_OFFSET); +	ahbcom_pci = (struct ahbcom_pci_bridge *)(base + AHBPCI_OFFSET); +	ahbconf_pci = +		(struct ahbconf_pci_bridge *)(base + PCI_CONF_AHBPCI_OFFSET); +	ahb_pciconf_ohci = (struct ahb_pciconf *)(base + PCI_CONF_OHCI_OFFSET); +	ahb_pciconf_ehci = (struct ahb_pciconf *)(base + PCI_CONF_EHCI_OFFSET); + +	/* Clock & Reset & Direct Power Down */ +	clrsetbits_le32(&ahbcom_pci->usbctr, +			(DIRPD | PCICLK_MASK | USBH_RST), USBCTR_WIN_SIZE_1GB); +	clrbits_le32(&ahbcom_pci->usbctr, PLL_RST); + +	/* AHB-PCI Bridge Communication Registers */ +	writel(AHB_BUS_CTR_INIT, &ahbcom_pci->ahb_bus_ctr); +	writel((CONFIG_SYS_SDRAM_BASE & 0xf0000000) | PCIAHB_WIN_PREFETCH, +	       &ahbcom_pci->pciahb_win1_ctr); +	writel(0xf0000000 | PCIAHB_WIN_PREFETCH, +	       &ahbcom_pci->pciahb_win2_ctr); +	writel(phys_base | PCIWIN2_PCICMD, &ahbcom_pci->ahbpci_win2_ctr); + +	setbits_le32(&ahbcom_pci->pci_arbiter_ctr, +		     PCIBP_MODE | PCIREQ1 | PCIREQ0); + +	/* PCI Configuration Registers for AHBPCI */ +	writel(PCIWIN1_PCICMD | AHB_CFG_AHBPCI, +	       &ahbcom_pci->ahbpci_win1_ctr); +	writel(phys_base + AHBPCI_OFFSET, &ahbconf_pci->basead); +	writel(CONFIG_SYS_SDRAM_BASE & 0xf0000000, &ahbconf_pci->win1_basead); +	writel(0xf0000000, &ahbconf_pci->win2_basead); +	writel(SERREN | PERREN | MASTEREN | MEMEN, +	       &ahbconf_pci->cmnd_sts); + +	/* PCI Configuration Registers for EHCI */ +	writel(PCIWIN1_PCICMD | AHB_CFG_HOST, &ahbcom_pci->ahbpci_win1_ctr); +	writel(phys_base + OHCI_OFFSET, &ahb_pciconf_ohci->basead); +	writel(phys_base + EHCI_OFFSET, &ahb_pciconf_ehci->basead); +	writel(SERREN | PERREN | MASTEREN | MEMEN, +	       &ahb_pciconf_ohci->cmnd_sts); +	writel(SERREN | PERREN | MASTEREN | MEMEN, +	       &ahb_pciconf_ehci->cmnd_sts); + +	/* Enable PCI interrupt */ +	setbits_le32(&ahbcom_pci->pci_int_enable, +		     USBH_PMEEN | USBH_INTBEN | USBH_INTAEN); + +	*hccr = (struct ehci_hccr *)((uint32_t)&rehci->hciversion); +	cap_base = ehci_readl(&(*hccr)->cr_capbase); +	*hcor = (struct ehci_hcor *)((uint32_t)*hccr + HC_LENGTH(cap_base)); + +	return 0; +} diff --git a/roms/u-boot/drivers/usb/host/ehci-spear.c b/roms/u-boot/drivers/usb/host/ehci-spear.c new file mode 100644 index 00000000..210ee9e8 --- /dev/null +++ b/roms/u-boot/drivers/usb/host/ehci-spear.c @@ -0,0 +1,44 @@ +/* + * (C) Copyright 2010 + * Armando Visconti, ST Micoelectronics, <armando.visconti@st.com>. + * + * (C) Copyright 2009 + * Marvell Semiconductor <www.marvell.com> + * Written-by: Prafulla Wadaskar <prafulla@marvell.com> + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +#include <common.h> +#include <asm/io.h> +#include <usb.h> +#include "ehci.h" +#include <asm/arch/hardware.h> + + +/* + * Create the appropriate control structures to manage + * a new EHCI host controller. + */ +int ehci_hcd_init(int index, enum usb_init_type init, +		struct ehci_hccr **hccr, struct ehci_hcor **hcor) +{ +	*hccr = (struct ehci_hccr *)(CONFIG_SYS_UHC0_EHCI_BASE + 0x100); +	*hcor = (struct ehci_hcor *)((uint32_t)*hccr +			+ HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase))); + +	debug("SPEAr-ehci: init hccr %x and hcor %x hc_length %d\n", +		(uint32_t)*hccr, (uint32_t)*hcor, +		(uint32_t)HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase))); + +	return 0; +} + +/* + * Destroy the appropriate control structures corresponding + * the the EHCI host controller. + */ +int ehci_hcd_stop(int index) +{ +	return 0; +} diff --git a/roms/u-boot/drivers/usb/host/ehci-tegra.c b/roms/u-boot/drivers/usb/host/ehci-tegra.c new file mode 100644 index 00000000..38db18e2 --- /dev/null +++ b/roms/u-boot/drivers/usb/host/ehci-tegra.c @@ -0,0 +1,827 @@ +/* + * Copyright (c) 2011 The Chromium OS Authors. + * Copyright (c) 2009-2013 NVIDIA Corporation + * Copyright (c) 2013 Lucas Stach + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +#include <common.h> +#include <asm/errno.h> +#include <asm/io.h> +#include <asm-generic/gpio.h> +#include <asm/arch/clock.h> +#include <asm/arch-tegra/usb.h> +#include <asm/arch-tegra/clk_rst.h> +#include <usb.h> +#include <usb/ulpi.h> +#include <libfdt.h> +#include <fdtdec.h> + +#include "ehci.h" + +#define USB1_ADDR_MASK	0xFFFF0000 + +#define HOSTPC1_DEVLC	0x84 +#define HOSTPC1_PSPD(x)		(((x) >> 25) & 0x3) + +#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	= 3,		/* Maximum ports we allow */ +}; + +/* Parameters we need for USB */ +enum { +	PARAM_DIVN,                     /* PLL FEEDBACK DIVIDer */ +	PARAM_DIVM,                     /* PLL INPUT DIVIDER */ +	PARAM_DIVP,                     /* POST DIVIDER (2^N) */ +	PARAM_CPCON,                    /* BASE PLLC CHARGE Pump setup ctrl */ +	PARAM_LFCON,                    /* BASE PLLC LOOP FILter setup ctrl */ +	PARAM_ENABLE_DELAY_COUNT,       /* PLL-U Enable Delay Count */ +	PARAM_STABLE_COUNT,             /* PLL-U STABLE count */ +	PARAM_ACTIVE_DELAY_COUNT,       /* PLL-U Active delay count */ +	PARAM_XTAL_FREQ_COUNT,          /* PLL-U XTAL frequency count */ +	PARAM_DEBOUNCE_A_TIME,          /* 10MS DELAY for BIAS_DEBOUNCE_A */ +	PARAM_BIAS_TIME,                /* 20US DELAY AFter bias cell op */ + +	PARAM_COUNT +}; + +/* Possible port types (dual role mode) */ +enum dr_mode { +	DR_MODE_NONE = 0, +	DR_MODE_HOST,		/* supports host operation */ +	DR_MODE_DEVICE,		/* supports device operation */ +	DR_MODE_OTG,		/* supports both */ +}; + +/* Information about a USB port */ +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 */ +	unsigned initialized:1; /* has this port already been initialized? */ +	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 */ +static unsigned port_count;			/* Number of available ports */ +/* Port that needs to clear CSC after Port Reset */ +static u32 port_addr_clear_csc; + +/* + * This table has USB timing parameters for each Oscillator frequency we + * support. There are four sets of values: + * + * 1. PLLU configuration information (reference clock is osc/clk_m and + * PLLU-FOs are fixed at 12MHz/60MHz/480MHz). + * + *  Reference frequency     13.0MHz      19.2MHz      12.0MHz      26.0MHz + *  ---------------------------------------------------------------------- + *      DIVN                960 (0x3c0)  200 (0c8)    960 (3c0h)   960 (3c0) + *      DIVM                13 (0d)      4 (04)       12 (0c)      26 (1a) + * Filter frequency (MHz)   1            4.8          6            2 + * CPCON                    1100b        0011b        1100b        1100b + * LFCON0                   0            0            0            0 + * + * 2. PLL CONFIGURATION & PARAMETERS for different clock generators: + * + * Reference frequency     13.0MHz         19.2MHz         12.0MHz     26.0MHz + * --------------------------------------------------------------------------- + * PLLU_ENABLE_DLY_COUNT   02 (0x02)       03 (03)         02 (02)     04 (04) + * PLLU_STABLE_COUNT       51 (33)         75 (4B)         47 (2F)    102 (66) + * PLL_ACTIVE_DLY_COUNT    05 (05)         06 (06)         04 (04)     09 (09) + * XTAL_FREQ_COUNT        127 (7F)        187 (BB)        118 (76)    254 (FE) + * + * 3. Debounce values IdDig, Avalid, Bvalid, VbusValid, VbusWakeUp, and + * SessEnd. Each of these signals have their own debouncer and for each of + * those one out of two debouncing times can be chosen (BIAS_DEBOUNCE_A or + * BIAS_DEBOUNCE_B). + * + * The values of DEBOUNCE_A and DEBOUNCE_B are calculated as follows: + *    0xffff -> No debouncing at all + *    <n> ms = <n> *1000 / (1/19.2MHz) / 4 + * + * So to program a 1 ms debounce for BIAS_DEBOUNCE_A, we have: + * BIAS_DEBOUNCE_A[15:0] = 1000 * 19.2 / 4  = 4800 = 0x12c0 + * + * We need to use only DebounceA for BOOTROM. We don't need the DebounceB + * values, so we can keep those to default. + * + * 4. The 20 microsecond delay after bias cell operation. + */ +static const unsigned T20_usb_pll[CLOCK_OSC_FREQ_COUNT][PARAM_COUNT] = { +	/* DivN, DivM, DivP, CPCON, LFCON, Delays             Debounce, Bias */ +	{ 0x3C0, 0x0D, 0x00, 0xC,   0,  0x02, 0x33, 0x05, 0x7F, 0x7EF4, 5 }, +	{ 0x0C8, 0x04, 0x00, 0x3,   0,  0x03, 0x4B, 0x06, 0xBB, 0xBB80, 7 }, +	{ 0x3C0, 0x0C, 0x00, 0xC,   0,  0x02, 0x2F, 0x04, 0x76, 0x7530, 5 }, +	{ 0x3C0, 0x1A, 0x00, 0xC,   0,  0x04, 0x66, 0x09, 0xFE, 0xFDE8, 9 } +}; + +static const unsigned T30_usb_pll[CLOCK_OSC_FREQ_COUNT][PARAM_COUNT] = { +	/* DivN, DivM, DivP, CPCON, LFCON, Delays             Debounce, Bias */ +	{ 0x3C0, 0x0D, 0x00, 0xC,   1,  0x02, 0x33, 0x09, 0x7F, 0x7EF4, 5 }, +	{ 0x0C8, 0x04, 0x00, 0x3,   0,  0x03, 0x4B, 0x0C, 0xBB, 0xBB80, 7 }, +	{ 0x3C0, 0x0C, 0x00, 0xC,   1,  0x02, 0x2F, 0x08, 0x76, 0x7530, 5 }, +	{ 0x3C0, 0x1A, 0x00, 0xC,   1,  0x04, 0x66, 0x09, 0xFE, 0xFDE8, 9 } +}; + +static const unsigned T114_usb_pll[CLOCK_OSC_FREQ_COUNT][PARAM_COUNT] = { +	/* DivN, DivM, DivP, CPCON, LFCON, Delays             Debounce, Bias */ +	{ 0x3C0, 0x0D, 0x00, 0xC,   2,  0x02, 0x33, 0x09, 0x7F, 0x7EF4, 6 }, +	{ 0x0C8, 0x04, 0x00, 0x3,   2,  0x03, 0x4B, 0x0C, 0xBB, 0xBB80, 8 }, +	{ 0x3C0, 0x0C, 0x00, 0xC,   2,  0x02, 0x2F, 0x08, 0x76, 0x7530, 5 }, +	{ 0x3C0, 0x1A, 0x00, 0xC,   2,  0x04, 0x66, 0x09, 0xFE, 0xFDE8, 0xB } +}; + +/* UTMIP Idle Wait Delay */ +static const u8 utmip_idle_wait_delay = 17; + +/* UTMIP Elastic limit */ +static const u8 utmip_elastic_limit = 16; + +/* UTMIP High Speed Sync Start Delay */ +static const u8 utmip_hs_sync_start_delay = 9; + +struct fdt_usb_controller { +	int compat; +	/* flag to determine whether controller supports hostpc register */ +	u32 has_hostpc:1; +	const unsigned *pll_parameter; +}; + +static struct fdt_usb_controller fdt_usb_controllers[] = { +	{ +		.compat		= COMPAT_NVIDIA_TEGRA20_USB, +		.has_hostpc	= 0, +		.pll_parameter	= (const unsigned *)T20_usb_pll, +	}, +	{ +		.compat		= COMPAT_NVIDIA_TEGRA30_USB, +		.has_hostpc	= 1, +		.pll_parameter	= (const unsigned *)T30_usb_pll, +	}, +	{ +		.compat		= COMPAT_NVIDIA_TEGRA114_USB, +		.has_hostpc	= 1, +		.pll_parameter	= (const unsigned *)T114_usb_pll, +	}, +}; + +static struct fdt_usb_controller *controller; + +/* + * A known hardware issue where Connect Status Change bit of PORTSC register + * of USB1 controller will be set after Port Reset. + * We have to clear it in order for later device enumeration to proceed. + * This ehci_powerup_fixup overrides the weak function ehci_powerup_fixup + * in "ehci-hcd.c". + */ +void ehci_powerup_fixup(uint32_t *status_reg, uint32_t *reg) +{ +	mdelay(50); +	/* This is to avoid PORT_ENABLE bit to be cleared in "ehci-hcd.c". */ +	if (controller->has_hostpc) +		*reg |= EHCI_PS_PE; + +	if (((u32)status_reg & TEGRA_USB_ADDR_MASK) != port_addr_clear_csc) +		return; +	/* For EHCI_PS_CSC to be cleared in ehci_hcd.c */ +	if (ehci_readl(status_reg) & EHCI_PS_CSC) +		*reg |= EHCI_PS_CSC; +} + +/* + * This ehci_set_usbmode overrides the weak function ehci_set_usbmode + * in "ehci-hcd.c". + */ +void ehci_set_usbmode(int index) +{ +	struct fdt_usb *config; +	struct usb_ctlr *usbctlr; +	uint32_t tmp; + +	config = &port[index]; +	usbctlr = config->reg; + +	tmp = ehci_readl(&usbctlr->usb_mode); +	tmp |= USBMODE_CM_HC; +	ehci_writel(&usbctlr->usb_mode, tmp); +} + +/* + * This ehci_get_port_speed overrides the weak function ehci_get_port_speed + * in "ehci-hcd.c". + */ +int ehci_get_port_speed(struct ehci_hcor *hcor, uint32_t reg) +{ +	uint32_t tmp; +	uint32_t *reg_ptr; + +	if (controller->has_hostpc) { +		reg_ptr = (uint32_t *)((u8 *)&hcor->or_usbcmd + HOSTPC1_DEVLC); +		tmp = ehci_readl(reg_ptr); +		return HOSTPC1_PSPD(tmp); +	} else +		return PORTSC_PSPD(reg); +} + +/* Put the port into host mode */ +static void set_host_mode(struct fdt_usb *config) +{ +	/* +	 * If we are an OTG port, check if remote host is driving VBus and +	 * bail out in this case. +	 */ +	if (config->dr_mode == DR_MODE_OTG && +		(readl(&config->reg->phy_vbus_sensors) & VBUS_VLD_STS)) +		return; + +	/* +	 * If not driving, we set the GPIO to enable VBUS. We assume +	 * that the pinmux is set up correctly for this. +	 */ +	if (fdt_gpio_isvalid(&config->vbus_gpio)) { +		fdtdec_setup_gpio(&config->vbus_gpio); +		gpio_direction_output(config->vbus_gpio.gpio, +			(config->vbus_gpio.flags & FDT_GPIO_ACTIVE_LOW) ? +				 0 : 1); +		debug("set_host_mode: GPIO %d %s\n", config->vbus_gpio.gpio, +			(config->vbus_gpio.flags & FDT_GPIO_ACTIVE_LOW) ? +				"low" : "high"); +	} +} + +void usbf_reset_controller(struct fdt_usb *config, struct usb_ctlr *usbctlr) +{ +	/* Reset the USB controller with 2us delay */ +	reset_periph(config->periph_id, 2); + +	/* +	 * Set USB1_NO_LEGACY_MODE to 1, Registers are accessible under +	 * base address +	 */ +	if (config->has_legacy_mode) +		setbits_le32(&usbctlr->usb1_legacy_ctrl, USB1_NO_LEGACY_MODE); + +	/* Put UTMIP1/3 in reset */ +	setbits_le32(&usbctlr->susp_ctrl, UTMIP_RESET); + +	/* Enable the UTMIP PHY */ +	if (config->utmi) +		setbits_le32(&usbctlr->susp_ctrl, UTMIP_PHY_ENB); +} + +static const unsigned *get_pll_timing(void) +{ +	const unsigned *timing; + +	timing = controller->pll_parameter + +		clock_get_osc_freq() * PARAM_COUNT; + +	return timing; +} + +/* set up the UTMI USB controller with the parameters provided */ +static int init_utmi_usb_controller(struct fdt_usb *config) +{ +	u32 val; +	int loop_count; +	const unsigned *timing; +	struct usb_ctlr *usbctlr = config->reg; +	struct clk_rst_ctlr *clkrst; +	struct usb_ctlr *usb1ctlr; + +	clock_enable(config->periph_id); + +	/* Reset the usb controller */ +	usbf_reset_controller(config, usbctlr); + +	/* Stop crystal clock by setting UTMIP_PHY_XTAL_CLOCKEN low */ +	clrbits_le32(&usbctlr->utmip_misc_cfg1, UTMIP_PHY_XTAL_CLOCKEN); + +	/* Follow the crystal clock disable by >100ns delay */ +	udelay(1); + +	/* +	 * To Use the A Session Valid for cable detection logic, VBUS_WAKEUP +	 * mux must be switched to actually use a_sess_vld threshold. +	 */ +	if (config->dr_mode == DR_MODE_OTG && +	    fdt_gpio_isvalid(&config->vbus_gpio)) +		clrsetbits_le32(&usbctlr->usb1_legacy_ctrl, +			VBUS_SENSE_CTL_MASK, +			VBUS_SENSE_CTL_A_SESS_VLD << VBUS_SENSE_CTL_SHIFT); + +	/* +	 * PLL Delay CONFIGURATION settings. The following parameters control +	 * the bring up of the plls. +	 */ +	timing = get_pll_timing(); + +	if (!controller->has_hostpc) { +		val = readl(&usbctlr->utmip_misc_cfg1); +		clrsetbits_le32(&val, UTMIP_PLLU_STABLE_COUNT_MASK, +				timing[PARAM_STABLE_COUNT] << +				UTMIP_PLLU_STABLE_COUNT_SHIFT); +		clrsetbits_le32(&val, UTMIP_PLL_ACTIVE_DLY_COUNT_MASK, +				timing[PARAM_ACTIVE_DELAY_COUNT] << +				UTMIP_PLL_ACTIVE_DLY_COUNT_SHIFT); +		writel(val, &usbctlr->utmip_misc_cfg1); + +		/* Set PLL enable delay count and crystal frequency count */ +		val = readl(&usbctlr->utmip_pll_cfg1); +		clrsetbits_le32(&val, UTMIP_PLLU_ENABLE_DLY_COUNT_MASK, +				timing[PARAM_ENABLE_DELAY_COUNT] << +				UTMIP_PLLU_ENABLE_DLY_COUNT_SHIFT); +		clrsetbits_le32(&val, UTMIP_XTAL_FREQ_COUNT_MASK, +				timing[PARAM_XTAL_FREQ_COUNT] << +				UTMIP_XTAL_FREQ_COUNT_SHIFT); +		writel(val, &usbctlr->utmip_pll_cfg1); +	} else { +		clkrst = (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE; + +		val = readl(&clkrst->crc_utmip_pll_cfg2); +		clrsetbits_le32(&val, UTMIP_PLLU_STABLE_COUNT_MASK, +				timing[PARAM_STABLE_COUNT] << +				UTMIP_PLLU_STABLE_COUNT_SHIFT); +		clrsetbits_le32(&val, UTMIP_PLL_ACTIVE_DLY_COUNT_MASK, +				timing[PARAM_ACTIVE_DELAY_COUNT] << +				UTMIP_PLL_ACTIVE_DLY_COUNT_SHIFT); +		writel(val, &clkrst->crc_utmip_pll_cfg2); + +		/* Set PLL enable delay count and crystal frequency count */ +		val = readl(&clkrst->crc_utmip_pll_cfg1); +		clrsetbits_le32(&val, UTMIP_PLLU_ENABLE_DLY_COUNT_MASK, +				timing[PARAM_ENABLE_DELAY_COUNT] << +				UTMIP_PLLU_ENABLE_DLY_COUNT_SHIFT); +		clrsetbits_le32(&val, UTMIP_XTAL_FREQ_COUNT_MASK, +				timing[PARAM_XTAL_FREQ_COUNT] << +				UTMIP_XTAL_FREQ_COUNT_SHIFT); +		writel(val, &clkrst->crc_utmip_pll_cfg1); + +		/* Disable Power Down state for PLL */ +		clrbits_le32(&clkrst->crc_utmip_pll_cfg1, +			     PLLU_POWERDOWN | PLL_ENABLE_POWERDOWN | +			     PLL_ACTIVE_POWERDOWN); + +		/* Recommended PHY settings for EYE diagram */ +		val = readl(&usbctlr->utmip_xcvr_cfg0); +		clrsetbits_le32(&val, UTMIP_XCVR_SETUP_MASK, +				0x4 << UTMIP_XCVR_SETUP_SHIFT); +		clrsetbits_le32(&val, UTMIP_XCVR_SETUP_MSB_MASK, +				0x3 << UTMIP_XCVR_SETUP_MSB_SHIFT); +		clrsetbits_le32(&val, UTMIP_XCVR_HSSLEW_MSB_MASK, +				0x8 << UTMIP_XCVR_HSSLEW_MSB_SHIFT); +		writel(val, &usbctlr->utmip_xcvr_cfg0); +		clrsetbits_le32(&usbctlr->utmip_xcvr_cfg1, +				UTMIP_XCVR_TERM_RANGE_ADJ_MASK, +				0x7 << UTMIP_XCVR_TERM_RANGE_ADJ_SHIFT); + +		/* Some registers can be controlled from USB1 only. */ +		if (config->periph_id != PERIPH_ID_USBD) { +			clock_enable(PERIPH_ID_USBD); +			/* Disable Reset if in Reset state */ +			reset_set_enable(PERIPH_ID_USBD, 0); +		} +		usb1ctlr = (struct usb_ctlr *) +			((u32)config->reg & USB1_ADDR_MASK); +		val = readl(&usb1ctlr->utmip_bias_cfg0); +		setbits_le32(&val, UTMIP_HSDISCON_LEVEL_MSB); +		clrsetbits_le32(&val, UTMIP_HSDISCON_LEVEL_MASK, +				0x1 << UTMIP_HSDISCON_LEVEL_SHIFT); +		clrsetbits_le32(&val, UTMIP_HSSQUELCH_LEVEL_MASK, +				0x2 << UTMIP_HSSQUELCH_LEVEL_SHIFT); +		writel(val, &usb1ctlr->utmip_bias_cfg0); + +		/* Miscellaneous setting mentioned in Programming Guide */ +		clrbits_le32(&usbctlr->utmip_misc_cfg0, +			     UTMIP_SUSPEND_EXIT_ON_EDGE); +	} + +	/* Setting the tracking length time */ +	clrsetbits_le32(&usbctlr->utmip_bias_cfg1, +		UTMIP_BIAS_PDTRK_COUNT_MASK, +		timing[PARAM_BIAS_TIME] << UTMIP_BIAS_PDTRK_COUNT_SHIFT); + +	/* Program debounce time for VBUS to become valid */ +	clrsetbits_le32(&usbctlr->utmip_debounce_cfg0, +		UTMIP_DEBOUNCE_CFG0_MASK, +		timing[PARAM_DEBOUNCE_A_TIME] << UTMIP_DEBOUNCE_CFG0_SHIFT); + +	setbits_le32(&usbctlr->utmip_tx_cfg0, UTMIP_FS_PREAMBLE_J); + +	/* Disable battery charge enabling bit */ +	setbits_le32(&usbctlr->utmip_bat_chrg_cfg0, UTMIP_PD_CHRG); + +	clrbits_le32(&usbctlr->utmip_xcvr_cfg0, UTMIP_XCVR_LSBIAS_SE); +	setbits_le32(&usbctlr->utmip_spare_cfg0, FUSE_SETUP_SEL); + +	/* +	 * Configure the UTMIP_IDLE_WAIT and UTMIP_ELASTIC_LIMIT +	 * Setting these fields, together with default values of the +	 * other fields, results in programming the registers below as +	 * follows: +	 *         UTMIP_HSRX_CFG0 = 0x9168c000 +	 *         UTMIP_HSRX_CFG1 = 0x13 +	 */ + +	/* Set PLL enable delay count and Crystal frequency count */ +	val = readl(&usbctlr->utmip_hsrx_cfg0); +	clrsetbits_le32(&val, UTMIP_IDLE_WAIT_MASK, +		utmip_idle_wait_delay << UTMIP_IDLE_WAIT_SHIFT); +	clrsetbits_le32(&val, UTMIP_ELASTIC_LIMIT_MASK, +		utmip_elastic_limit << UTMIP_ELASTIC_LIMIT_SHIFT); +	writel(val, &usbctlr->utmip_hsrx_cfg0); + +	/* Configure the UTMIP_HS_SYNC_START_DLY */ +	clrsetbits_le32(&usbctlr->utmip_hsrx_cfg1, +		UTMIP_HS_SYNC_START_DLY_MASK, +		utmip_hs_sync_start_delay << UTMIP_HS_SYNC_START_DLY_SHIFT); + +	/* Preceed the crystal clock disable by >100ns delay. */ +	udelay(1); + +	/* Resuscitate crystal clock by setting UTMIP_PHY_XTAL_CLOCKEN */ +	setbits_le32(&usbctlr->utmip_misc_cfg1, UTMIP_PHY_XTAL_CLOCKEN); + +	if (controller->has_hostpc) { +		if (config->periph_id == PERIPH_ID_USBD) +			clrbits_le32(&clkrst->crc_utmip_pll_cfg2, +				     UTMIP_FORCE_PD_SAMP_A_POWERDOWN); +		if (config->periph_id == PERIPH_ID_USB2) +			clrbits_le32(&clkrst->crc_utmip_pll_cfg2, +				     UTMIP_FORCE_PD_SAMP_B_POWERDOWN); +		if (config->periph_id == PERIPH_ID_USB3) +			clrbits_le32(&clkrst->crc_utmip_pll_cfg2, +				     UTMIP_FORCE_PD_SAMP_C_POWERDOWN); +	} +	/* Finished the per-controller init. */ + +	/* De-assert UTMIP_RESET to bring out of reset. */ +	clrbits_le32(&usbctlr->susp_ctrl, UTMIP_RESET); + +	/* Wait for the phy clock to become valid in 100 ms */ +	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; + +	/* Disable ICUSB FS/LS transceiver */ +	clrbits_le32(&usbctlr->icusb_ctrl, IC_ENB1); + +	/* Select UTMI parallel interface */ +#if defined(CONFIG_TEGRA20) +	if (config->periph_id == PERIPH_ID_USBD) { +		clrsetbits_le32(&usbctlr->port_sc1, PTS1_MASK, +				PTS_UTMI << PTS1_SHIFT); +		clrbits_le32(&usbctlr->port_sc1, STS1); +	} else { +		clrsetbits_le32(&usbctlr->port_sc1, PTS_MASK, +				PTS_UTMI << PTS_SHIFT); +		clrbits_le32(&usbctlr->port_sc1, STS); +	} +#else +	clrsetbits_le32(&usbctlr->hostpc1_devlc, PTS_MASK, +			PTS_UTMI << PTS_SHIFT); +	clrbits_le32(&usbctlr->hostpc1_devlc, STS); +#endif + +	/* 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); + +	if (controller->has_hostpc) { +		/* +		 * BIAS Pad Power Down is common among all 3 USB +		 * controllers and can be controlled from USB1 only. +		 */ +		usb1ctlr = (struct usb_ctlr *) +			((u32)config->reg & USB1_ADDR_MASK); +		clrbits_le32(&usb1ctlr->utmip_bias_cfg0, UTMIP_BIASPD); +		udelay(25); +		clrbits_le32(&usb1ctlr->utmip_bias_cfg1, +			     UTMIP_FORCE_PDTRK_POWERDOWN); +	} +	return 0; +} + +#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) +{ +	u32 val; +	int loop_count; +	struct ulpi_viewport ulpi_vp; +	struct usb_ctlr *usbctlr = config->reg; + +	/* 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 */ +#if defined(CONFIG_TEGRA20) +	clrsetbits_le32(&usbctlr->port_sc1, PTS_MASK, +			PTS_ULPI << PTS_SHIFT); +#else +	clrsetbits_le32(&usbctlr->hostpc1_devlc, PTS_MASK, +			PTS_ULPI << PTS_SHIFT); +#endif + +	/* 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) +{ +	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[]) +{ +	clock_start_pll(CLOCK_ID_USB, +		timing[PARAM_DIVM], timing[PARAM_DIVN], timing[PARAM_DIVP], +		timing[PARAM_CPCON], timing[PARAM_LFCON]); +} + +static int fdt_decode_usb(const void *blob, int node, struct fdt_usb *config) +{ +	const char *phy, *mode; + +	config->reg = (struct usb_ctlr *)fdtdec_get_addr(blob, node, "reg"); +	mode = fdt_getprop(blob, node, "dr_mode", NULL); +	if (mode) { +		if (0 == strcmp(mode, "host")) +			config->dr_mode = DR_MODE_HOST; +		else if (0 == strcmp(mode, "peripheral")) +			config->dr_mode = DR_MODE_DEVICE; +		else if (0 == strcmp(mode, "otg")) +			config->dr_mode = DR_MODE_OTG; +		else { +			debug("%s: Cannot decode dr_mode '%s'\n", __func__, +			      mode); +			return -FDT_ERR_NOTFOUND; +		} +	} else { +		config->dr_mode = DR_MODE_HOST; +	} + +	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"); +	if (config->has_legacy_mode) +		port_addr_clear_csc = (u32) config->reg; +	config->periph_id = clock_decode_periph_id(blob, node); +	if (config->periph_id == PERIPH_ID_NONE) { +		debug("%s: Missing/invalid peripheral ID\n", __func__); +		return -FDT_ERR_NOTFOUND; +	} +	fdtdec_decode_gpio(blob, node, "nvidia,vbus-gpio", &config->vbus_gpio); +	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; +} + +/* + * process_usb_nodes() - Process a list of USB nodes, adding them to our list + *			of USB ports. + * @blob:	fdt blob + * @node_list:	list of nodes to process (any <=0 are ignored) + * @count:	number of nodes to process + * + * Return:	0 - ok, -1 - error + */ +static int process_usb_nodes(const void *blob, int node_list[], int count) +{ +	struct fdt_usb config; +	int node, i; +	int clk_done = 0; + +	port_count = 0; +	for (i = 0; i < count; i++) { +		if (port_count == USB_PORTS_MAX) { +			printf("tegrausb: Cannot register more than %d ports\n", +				USB_PORTS_MAX); +			return -1; +		} + +		debug("USB %d: ", i); +		node = node_list[i]; +		if (!node) +			continue; +		if (fdt_decode_usb(blob, node, &config)) { +			debug("Cannot decode USB node %s\n", +			      fdt_get_name(blob, node, NULL)); +			return -1; +		} +		if (!clk_done) { +			config_clock(get_pll_timing()); +			clk_done = 1; +		} +		config.initialized = 0; + +		/* add new USB port to the list of available ports */ +		port[port_count++] = config; +	} + +	return 0; +} + +int usb_process_devicetree(const void *blob) +{ +	int node_list[USB_PORTS_MAX]; +	int count, err = 0; +	int i; + +	for (i = 0; i < ARRAY_SIZE(fdt_usb_controllers); i++) { +		controller = &fdt_usb_controllers[i]; + +		count = fdtdec_find_aliases_for_id(blob, "usb", +			controller->compat, node_list, USB_PORTS_MAX); +		if (count) { +			err = process_usb_nodes(blob, node_list, count); +			if (err) +				printf("%s: Error processing USB node!\n", +				       __func__); +			return err; +		} +	} +	if (i == ARRAY_SIZE(fdt_usb_controllers)) +		controller = NULL; + +	return err; +} + +/** + * Start up the given port number (ports are numbered from 0 on each board). + * This returns values for the appropriate hccr and hcor addresses to use for + * USB EHCI operations. + * + * @param index	port number to start + * @param hccr		returns start address of EHCI HCCR registers + * @param hcor		returns start address of EHCI HCOR registers + * @return 0 if ok, -1 on error (generally invalid port number) + */ +int ehci_hcd_init(int index, enum usb_init_type init, +		struct ehci_hccr **hccr, struct ehci_hcor **hcor) +{ +	struct fdt_usb *config; +	struct usb_ctlr *usbctlr; + +	if (index >= port_count) +		return -1; + +	config = &port[index]; + +	/* skip init, if the port is already initialized */ +	if (config->initialized) +		goto success; + +	if (config->utmi && init_utmi_usb_controller(config)) { +		printf("tegrausb: Cannot init port %d\n", index); +		return -1; +	} + +	if (config->ulpi && init_ulpi_usb_controller(config)) { +		printf("tegrausb: Cannot init port %d\n", index); +		return -1; +	} + +	set_host_mode(config); + +	config->initialized = 1; + +success: +	usbctlr = config->reg; +	*hccr = (struct ehci_hccr *)&usbctlr->cap_length; +	*hcor = (struct ehci_hcor *)&usbctlr->usb_cmd; + +	if (controller->has_hostpc) { +		/* Set to Host mode after Controller Reset was done */ +		clrsetbits_le32(&usbctlr->usb_mode, USBMODE_CM_HC, +				USBMODE_CM_HC); +		/* Select UTMI parallel interface after setting host mode */ +		if (config->utmi) { +			clrsetbits_le32((char *)&usbctlr->usb_cmd + +					HOSTPC1_DEVLC, PTS_MASK, +					PTS_UTMI << PTS_SHIFT); +			clrbits_le32((char *)&usbctlr->usb_cmd + +				     HOSTPC1_DEVLC, STS); +		} +	} +	return 0; +} + +/* + * Bring down the specified USB controller + */ +int ehci_hcd_stop(int index) +{ +	struct usb_ctlr *usbctlr; + +	usbctlr = port[index].reg; + +	/* Stop controller */ +	writel(0, &usbctlr->usb_cmd); +	udelay(1000); + +	/* Initiate controller reset */ +	writel(2, &usbctlr->usb_cmd); +	udelay(1000); + +	port[index].initialized = 0; + +	return 0; +} diff --git a/roms/u-boot/drivers/usb/host/ehci-vct.c b/roms/u-boot/drivers/usb/host/ehci-vct.c new file mode 100644 index 00000000..512ad3fb --- /dev/null +++ b/roms/u-boot/drivers/usb/host/ehci-vct.c @@ -0,0 +1,45 @@ +/* + * (C) Copyright 2009 Stefan Roese <sr@denx.de>, DENX Software Engineering + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +#include <common.h> +#include <usb.h> + +#include "ehci.h" + +int vct_ehci_hcd_init(u32 *hccr, u32 *hcor); + +/* + * Create the appropriate control structures to manage + * a new EHCI host controller. + */ +int ehci_hcd_init(int index, enum usb_init_type init, +		struct ehci_hccr **hccr, struct ehci_hcor **hcor) +{ +	int ret; +	u32 vct_hccr; +	u32 vct_hcor; + +	/* +	 * Init VCT specific stuff +	 */ +	ret = vct_ehci_hcd_init(&vct_hccr, &vct_hcor); +	if (ret) +		return ret; + +	*hccr = (struct ehci_hccr *)vct_hccr; +	*hcor = (struct ehci_hcor *)vct_hcor; + +	return 0; +} + +/* + * Destroy the appropriate control structures corresponding + * the the EHCI host controller. + */ +int ehci_hcd_stop(int index) +{ +	return 0; +} diff --git a/roms/u-boot/drivers/usb/host/ehci.h b/roms/u-boot/drivers/usb/host/ehci.h new file mode 100644 index 00000000..093eb4b8 --- /dev/null +++ b/roms/u-boot/drivers/usb/host/ehci.h @@ -0,0 +1,257 @@ +/*- + * Copyright (c) 2007-2008, Juniper Networks, Inc. + * Copyright (c) 2008, Michael Trimarchi <trimarchimichael@yahoo.it> + * 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 version 2 of + * the License. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#ifndef USB_EHCI_H +#define USB_EHCI_H + +#include <usb.h> + +#if !defined(CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS) +#define CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS	2 +#endif + +/* + * Register Space. + */ +struct ehci_hccr { +	uint32_t cr_capbase; +#define HC_LENGTH(p)		(((p) >> 0) & 0x00ff) +#define HC_VERSION(p)		(((p) >> 16) & 0xffff) +	uint32_t cr_hcsparams; +#define HCS_PPC(p)		((p) & (1 << 4)) +#define HCS_INDICATOR(p)	((p) & (1 << 16)) /* Port indicators */ +#define HCS_N_PORTS(p)		(((p) >> 0) & 0xf) +	uint32_t cr_hccparams; +	uint8_t cr_hcsp_portrt[8]; +} __attribute__ ((packed, aligned(4))); + +struct ehci_hcor { +	uint32_t or_usbcmd; +#define CMD_PARK	(1 << 11)		/* enable "park" */ +#define CMD_PARK_CNT(c)	(((c) >> 8) & 3)	/* how many transfers to park */ +#define CMD_ASE		(1 << 5)		/* async schedule enable */ +#define CMD_LRESET	(1 << 7)		/* partial reset */ +#define CMD_IAAD	(1 << 5)		/* "doorbell" interrupt */ +#define CMD_PSE		(1 << 4)		/* periodic schedule enable */ +#define CMD_RESET	(1 << 1)		/* reset HC not bus */ +#define CMD_RUN		(1 << 0)		/* start/stop HC */ +	uint32_t or_usbsts; +#define STS_ASS		(1 << 15) +#define	STS_PSS		(1 << 14) +#define STS_HALT	(1 << 12) +	uint32_t or_usbintr; +#define INTR_UE         (1 << 0)                /* USB interrupt enable */ +#define INTR_UEE        (1 << 1)                /* USB error interrupt enable */ +#define INTR_PCE        (1 << 2)                /* Port change detect enable */ +#define INTR_SEE        (1 << 4)                /* system error enable */ +#define INTR_AAE        (1 << 5)                /* Interrupt on async adavance enable */ +	uint32_t or_frindex; +	uint32_t or_ctrldssegment; +	uint32_t or_periodiclistbase; +	uint32_t or_asynclistaddr; +	uint32_t _reserved_0_; +	uint32_t or_burstsize; +	uint32_t or_txfilltuning; +#define TXFIFO_THRESH_MASK		(0x3f << 16) +#define TXFIFO_THRESH(p)		((p & 0x3f) << 16) +	uint32_t _reserved_1_[6]; +	uint32_t or_configflag; +#define FLAG_CF		(1 << 0)	/* true:  we'll support "high speed" */ +	uint32_t or_portsc[CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS]; +#define PORTSC_PSPD(x)		(((x) >> 26) & 0x3) +#define PORTSC_PSPD_FS			0x0 +#define PORTSC_PSPD_LS			0x1 +#define PORTSC_PSPD_HS			0x2 +	uint32_t or_systune; +} __attribute__ ((packed, aligned(4))); + +#define USBMODE		0x68		/* USB Device mode */ +#define USBMODE_SDIS	(1 << 3)	/* Stream disable */ +#define USBMODE_BE	(1 << 2)	/* BE/LE endiannes select */ +#define USBMODE_CM_HC	(3 << 0)	/* host controller mode */ +#define USBMODE_CM_IDLE	(0 << 0)	/* idle state */ + +/* Interface descriptor */ +struct usb_linux_interface_descriptor { +	unsigned char	bLength; +	unsigned char	bDescriptorType; +	unsigned char	bInterfaceNumber; +	unsigned char	bAlternateSetting; +	unsigned char	bNumEndpoints; +	unsigned char	bInterfaceClass; +	unsigned char	bInterfaceSubClass; +	unsigned char	bInterfaceProtocol; +	unsigned char	iInterface; +} __attribute__ ((packed)); + +/* Configuration descriptor information.. */ +struct usb_linux_config_descriptor { +	unsigned char	bLength; +	unsigned char	bDescriptorType; +	unsigned short	wTotalLength; +	unsigned char	bNumInterfaces; +	unsigned char	bConfigurationValue; +	unsigned char	iConfiguration; +	unsigned char	bmAttributes; +	unsigned char	MaxPower; +} __attribute__ ((packed)); + +#if defined CONFIG_EHCI_DESC_BIG_ENDIAN +#define	ehci_readl(x)		(*((volatile u32 *)(x))) +#define ehci_writel(a, b)	(*((volatile u32 *)(a)) = ((volatile u32)b)) +#else +#define ehci_readl(x)		cpu_to_le32((*((volatile u32 *)(x)))) +#define ehci_writel(a, b)	(*((volatile u32 *)(a)) = \ +					cpu_to_le32(((volatile u32)b))) +#endif + +#if defined CONFIG_EHCI_MMIO_BIG_ENDIAN +#define hc32_to_cpu(x)		be32_to_cpu((x)) +#define cpu_to_hc32(x)		cpu_to_be32((x)) +#else +#define hc32_to_cpu(x)		le32_to_cpu((x)) +#define cpu_to_hc32(x)		cpu_to_le32((x)) +#endif + +#define EHCI_PS_WKOC_E		(1 << 22)	/* RW wake on over current */ +#define EHCI_PS_WKDSCNNT_E	(1 << 21)	/* RW wake on disconnect */ +#define EHCI_PS_WKCNNT_E	(1 << 20)	/* RW wake on connect */ +#define EHCI_PS_PO		(1 << 13)	/* RW port owner */ +#define EHCI_PS_PP		(1 << 12)	/* RW,RO port power */ +#define EHCI_PS_LS		(3 << 10)	/* RO line status */ +#define EHCI_PS_PR		(1 << 8)	/* RW port reset */ +#define EHCI_PS_SUSP		(1 << 7)	/* RW suspend */ +#define EHCI_PS_FPR		(1 << 6)	/* RW force port resume */ +#define EHCI_PS_OCC		(1 << 5)	/* RWC over current change */ +#define EHCI_PS_OCA		(1 << 4)	/* RO over current active */ +#define EHCI_PS_PEC		(1 << 3)	/* RWC port enable change */ +#define EHCI_PS_PE		(1 << 2)	/* RW port enable */ +#define EHCI_PS_CSC		(1 << 1)	/* RWC connect status change */ +#define EHCI_PS_CS		(1 << 0)	/* RO connect status */ +#define EHCI_PS_CLEAR		(EHCI_PS_OCC | EHCI_PS_PEC | EHCI_PS_CSC) + +#define EHCI_PS_IS_LOWSPEED(x)	(((x) & EHCI_PS_LS) == (1 << 10)) + +/* + * Schedule Interface Space. + * + * IMPORTANT: Software must ensure that no interface data structure + * reachable by the EHCI host controller spans a 4K page boundary! + * + * Periodic transfers (i.e. isochronous and interrupt transfers) are + * not supported. + */ + +/* Queue Element Transfer Descriptor (qTD). */ +struct qTD { +	/* this part defined by EHCI spec */ +	uint32_t qt_next;			/* see EHCI 3.5.1 */ +#define	QT_NEXT_TERMINATE	1 +	uint32_t qt_altnext;			/* see EHCI 3.5.2 */ +	uint32_t qt_token;			/* see EHCI 3.5.3 */ +#define QT_TOKEN_DT(x)		(((x) & 0x1) << 31)	/* Data Toggle */ +#define QT_TOKEN_GET_DT(x)		(((x) >> 31) & 0x1) +#define QT_TOKEN_TOTALBYTES(x)	(((x) & 0x7fff) << 16)	/* Total Bytes to Transfer */ +#define QT_TOKEN_GET_TOTALBYTES(x)	(((x) >> 16) & 0x7fff) +#define QT_TOKEN_IOC(x)		(((x) & 0x1) << 15)	/* Interrupt On Complete */ +#define QT_TOKEN_CPAGE(x)	(((x) & 0x7) << 12)	/* Current Page */ +#define QT_TOKEN_CERR(x)	(((x) & 0x3) << 10)	/* Error Counter */ +#define QT_TOKEN_PID(x)		(((x) & 0x3) << 8)	/* PID Code */ +#define QT_TOKEN_PID_OUT		0x0 +#define QT_TOKEN_PID_IN			0x1 +#define QT_TOKEN_PID_SETUP		0x2 +#define QT_TOKEN_STATUS(x)	(((x) & 0xff) << 0)	/* Status */ +#define QT_TOKEN_GET_STATUS(x)		(((x) >> 0) & 0xff) +#define QT_TOKEN_STATUS_ACTIVE		0x80 +#define QT_TOKEN_STATUS_HALTED		0x40 +#define QT_TOKEN_STATUS_DATBUFERR	0x20 +#define QT_TOKEN_STATUS_BABBLEDET	0x10 +#define QT_TOKEN_STATUS_XACTERR		0x08 +#define QT_TOKEN_STATUS_MISSEDUFRAME	0x04 +#define QT_TOKEN_STATUS_SPLITXSTATE	0x02 +#define QT_TOKEN_STATUS_PERR		0x01 +#define QT_BUFFER_CNT		5 +	uint32_t qt_buffer[QT_BUFFER_CNT];	/* see EHCI 3.5.4 */ +	uint32_t qt_buffer_hi[QT_BUFFER_CNT];	/* Appendix B */ +	/* pad struct for 32 byte alignment */ +	uint32_t unused[3]; +}; + +#define EHCI_PAGE_SIZE		4096 + +/* Queue Head (QH). */ +struct QH { +	uint32_t qh_link; +#define	QH_LINK_TERMINATE	1 +#define	QH_LINK_TYPE_ITD	0 +#define	QH_LINK_TYPE_QH		2 +#define	QH_LINK_TYPE_SITD	4 +#define	QH_LINK_TYPE_FSTN	6 +	uint32_t qh_endpt1; +#define QH_ENDPT1_RL(x)		(((x) & 0xf) << 28)	/* NAK Count Reload */ +#define QH_ENDPT1_C(x)		(((x) & 0x1) << 27)	/* Control Endpoint Flag */ +#define QH_ENDPT1_MAXPKTLEN(x)	(((x) & 0x7ff) << 16)	/* Maximum Packet Length */ +#define QH_ENDPT1_H(x)		(((x) & 0x1) << 15)	/* Head of Reclamation List Flag */ +#define QH_ENDPT1_DTC(x)	(((x) & 0x1) << 14)	/* Data Toggle Control */ +#define QH_ENDPT1_DTC_IGNORE_QTD_TD	0x0 +#define QH_ENDPT1_DTC_DT_FROM_QTD	0x1 +#define QH_ENDPT1_EPS(x)	(((x) & 0x3) << 12)	/* Endpoint Speed */ +#define QH_ENDPT1_EPS_FS		0x0 +#define QH_ENDPT1_EPS_LS		0x1 +#define QH_ENDPT1_EPS_HS		0x2 +#define QH_ENDPT1_ENDPT(x)	(((x) & 0xf) << 8)	/* Endpoint Number */ +#define QH_ENDPT1_I(x)		(((x) & 0x1) << 7)	/* Inactivate on Next Transaction */ +#define QH_ENDPT1_DEVADDR(x)	(((x) & 0x7f) << 0)	/* Device Address */ +	uint32_t qh_endpt2; +#define QH_ENDPT2_MULT(x)	(((x) & 0x3) << 30)	/* High-Bandwidth Pipe Multiplier */ +#define QH_ENDPT2_PORTNUM(x)	(((x) & 0x7f) << 23)	/* Port Number */ +#define QH_ENDPT2_HUBADDR(x)	(((x) & 0x7f) << 16)	/* Hub Address */ +#define QH_ENDPT2_UFCMASK(x)	(((x) & 0xff) << 8)	/* Split Completion Mask */ +#define QH_ENDPT2_UFSMASK(x)	(((x) & 0xff) << 0)	/* Interrupt Schedule Mask */ +	uint32_t qh_curtd; +	struct qTD qh_overlay; +	/* +	 * Add dummy fill value to make the size of this struct +	 * aligned to 32 bytes +	 */ +	union { +		uint32_t fill[4]; +		void *buffer; +	}; +}; + +struct ehci_ctrl { +	struct ehci_hccr *hccr;	/* R/O registers, not need for volatile */ +	struct ehci_hcor *hcor; +	int rootdev; +	uint16_t portreset; +	struct QH qh_list __aligned(USB_DMA_MINALIGN); +	struct QH periodic_queue __aligned(USB_DMA_MINALIGN); +	uint32_t *periodic_list; +	int ntds; +}; + +/* Low level init functions */ +int ehci_hcd_init(int index, enum usb_init_type init, +		struct ehci_hccr **hccr, struct ehci_hcor **hcor); +int ehci_hcd_stop(int index); + +#endif /* USB_EHCI_H */ diff --git a/roms/u-boot/drivers/usb/host/isp116x-hcd.c b/roms/u-boot/drivers/usb/host/isp116x-hcd.c new file mode 100644 index 00000000..46e4cee1 --- /dev/null +++ b/roms/u-boot/drivers/usb/host/isp116x-hcd.c @@ -0,0 +1,1329 @@ +/* + * ISP116x HCD (Host Controller Driver) for u-boot. + * + * Copyright (C) 2006-2007 Rodolfo Giometti <giometti@linux.it> + * Copyright (C) 2006-2007 Eurotech S.p.A. <info@eurotech.it> + * + * SPDX-License-Identifier:	GPL-2.0+ + * + * Derived in part from the SL811 HCD driver "u-boot/drivers/usb/sl811_usb.c" + * (original copyright message follows): + * + *    (C) Copyright 2004 + *    Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + *    This code is based on linux driver for sl811hs chip, source at + *    drivers/usb/host/sl811.c: + * + *    SL811 Host Controller Interface driver for USB. + * + *    Copyright (c) 2003/06, Courage Co., Ltd. + * + *    Based on: + *         1.uhci.c by Linus Torvalds, Johannes Erdfelt, Randy Dunlap, + *           Georg Acher, Deti Fliegl, Thomas Sailer, Roman Weissgaerber, + *           Adam Richter, Gregory P. Smith; + *         2.Original SL811 driver (hc_sl811.o) by Pei Liu <pbl@cypress.com> + *         3.Rewrited as sl811.o by Yin Aihua <yinah:couragetech.com.cn> + * + *    [[GNU/GPL disclaimer]] + * + * and in part from AU1x00 OHCI HCD driver "u-boot/arch/mips/cpu/au1x00_usb_ohci.c" + * (original copyright message follows): + * + *    URB OHCI HCD (Host Controller Driver) for USB on the AU1x00. + * + *    (C) Copyright 2003 + *    Gary Jennejohn, DENX Software Engineering <garyj@denx.de> + * + *    [[GNU/GPL disclaimer]] + * + *    Note: Part of this code has been derived from linux + */ + +#include <common.h> +#include <asm/io.h> +#include <usb.h> +#include <malloc.h> +#include <linux/list.h> + +/* + * ISP116x chips require certain delays between accesses to its + * registers. The following timing options exist. + * + * 1. Configure your memory controller (the best) + * 2. Use ndelay (easiest, poorest). For that, enable the following macro. + * + * Value is in microseconds. + */ +#ifdef ISP116X_HCD_USE_UDELAY +#define UDELAY		1 +#endif + +/* + * On some (slowly?) machines an extra delay after data packing into + * controller's FIFOs is required, * otherwise you may get the following + * error: + * + *   uboot> usb start + *   (Re)start USB... + *   USB:   scanning bus for devices... isp116x: isp116x_submit_job: CTL:TIMEOUT + *   isp116x: isp116x_submit_job: ****** FIFO not ready! ****** + * + *         USB device not responding, giving up (status=4) + *         isp116x: isp116x_submit_job: ****** FIFO not empty! ****** + *         isp116x: isp116x_submit_job: ****** FIFO not empty! ****** + *         isp116x: isp116x_submit_job: ****** FIFO not empty! ****** + *         3 USB Device(s) found + *                scanning bus for storage devices... 0 Storage Device(s) found + * + * Value is in milliseconds. + */ +#ifdef ISP116X_HCD_USE_EXTRA_DELAY +#define EXTRA_DELAY	2 +#endif + +/* + * Enable the following defines if you wish enable debugging messages. + */ +#undef DEBUG			/* enable debugging messages */ +#undef TRACE			/* enable tracing code */ +#undef VERBOSE			/* verbose debugging messages */ + +#include "isp116x.h" + +#define DRIVER_VERSION	"08 Jan 2007" +static const char hcd_name[] = "isp116x-hcd"; + +struct isp116x isp116x_dev; +struct isp116x_platform_data isp116x_board; +static int got_rhsc;		/* root hub status change */ +struct usb_device *devgone;	/* device which was disconnected */ +static int rh_devnum;		/* address of Root Hub endpoint */ + +/* ------------------------------------------------------------------------- */ + +#define ALIGN(x,a)	(((x)+(a)-1UL)&~((a)-1UL)) +#define min_t(type,x,y)	\ +	({ type __x = (x); type __y = (y); __x < __y ? __x : __y; }) + +/* ------------------------------------------------------------------------- */ + +static int isp116x_reset(struct isp116x *isp116x); + +/* --- Debugging functions ------------------------------------------------- */ + +#define isp116x_show_reg(d, r) {				\ +	if ((r) < 0x20) {					\ +		DBG("%-12s[%02x]: %08x", #r,			\ +			r, isp116x_read_reg32(d, r));		\ +	} else {						\ +		DBG("%-12s[%02x]:     %04x", #r,		\ +			r, isp116x_read_reg16(d, r));		\ +	}							\ +} + +#define isp116x_show_regs(d) {					\ +	isp116x_show_reg(d, HCREVISION);			\ +	isp116x_show_reg(d, HCCONTROL);				\ +	isp116x_show_reg(d, HCCMDSTAT);				\ +	isp116x_show_reg(d, HCINTSTAT);				\ +	isp116x_show_reg(d, HCINTENB);				\ +	isp116x_show_reg(d, HCFMINTVL);				\ +	isp116x_show_reg(d, HCFMREM);				\ +	isp116x_show_reg(d, HCFMNUM);				\ +	isp116x_show_reg(d, HCLSTHRESH);			\ +	isp116x_show_reg(d, HCRHDESCA);				\ +	isp116x_show_reg(d, HCRHDESCB);				\ +	isp116x_show_reg(d, HCRHSTATUS);			\ +	isp116x_show_reg(d, HCRHPORT1);				\ +	isp116x_show_reg(d, HCRHPORT2);				\ +	isp116x_show_reg(d, HCHWCFG);				\ +	isp116x_show_reg(d, HCDMACFG);				\ +	isp116x_show_reg(d, HCXFERCTR);				\ +	isp116x_show_reg(d, HCuPINT);				\ +	isp116x_show_reg(d, HCuPINTENB);			\ +	isp116x_show_reg(d, HCCHIPID);				\ +	isp116x_show_reg(d, HCSCRATCH);				\ +	isp116x_show_reg(d, HCITLBUFLEN);			\ +	isp116x_show_reg(d, HCATLBUFLEN);			\ +	isp116x_show_reg(d, HCBUFSTAT);				\ +	isp116x_show_reg(d, HCRDITL0LEN);			\ +	isp116x_show_reg(d, HCRDITL1LEN);			\ +} + +#if defined(TRACE) + +static int isp116x_get_current_frame_number(struct usb_device *usb_dev) +{ +	struct isp116x *isp116x = &isp116x_dev; + +	return isp116x_read_reg32(isp116x, HCFMNUM); +} + +static void dump_msg(struct usb_device *dev, unsigned long pipe, void *buffer, +		     int len, char *str) +{ +#if defined(VERBOSE) +	int i; +#endif + +	DBG("%s URB:[%4x] dev:%2d,ep:%2d-%c,type:%s,len:%d stat:%#lx", +	    str, +	    isp116x_get_current_frame_number(dev), +	    usb_pipedevice(pipe), +	    usb_pipeendpoint(pipe), +	    usb_pipeout(pipe) ? 'O' : 'I', +	    usb_pipetype(pipe) < 2 ? +	    (usb_pipeint(pipe) ? +	     "INTR" : "ISOC") : +	    (usb_pipecontrol(pipe) ? "CTRL" : "BULK"), len, dev->status); +#if defined(VERBOSE) +	if (len > 0 && buffer) { +		printf(__FILE__ ": data(%d):", len); +		for (i = 0; i < 16 && i < len; i++) +			printf(" %02x", ((__u8 *) buffer)[i]); +		printf("%s\n", i < len ? "..." : ""); +	} +#endif +} + +#define PTD_DIR_STR(ptd)  ({char __c;		\ +	switch(PTD_GET_DIR(ptd)){		\ +	case 0:  __c = 's'; break;		\ +	case 1:  __c = 'o'; break;		\ +	default: __c = 'i'; break;		\ +	}; __c;}) + +/* +  Dump PTD info. The code documents the format +  perfectly, right :) +*/ +static inline void dump_ptd(struct ptd *ptd) +{ +#if defined(VERBOSE) +	int k; +#endif + +	DBG("PTD(ext) : cc:%x %d%c%d %d,%d,%d t:%x %x%x%x", +	    PTD_GET_CC(ptd), +	    PTD_GET_FA(ptd), PTD_DIR_STR(ptd), PTD_GET_EP(ptd), +	    PTD_GET_COUNT(ptd), PTD_GET_LEN(ptd), PTD_GET_MPS(ptd), +	    PTD_GET_TOGGLE(ptd), +	    PTD_GET_ACTIVE(ptd), PTD_GET_SPD(ptd), PTD_GET_LAST(ptd)); +#if defined(VERBOSE) +	printf("isp116x: %s: PTD(byte): ", __FUNCTION__); +	for (k = 0; k < sizeof(struct ptd); ++k) +		printf("%02x ", ((u8 *) ptd)[k]); +	printf("\n"); +#endif +} + +static inline void dump_ptd_data(struct ptd *ptd, u8 * buf, int type) +{ +#if defined(VERBOSE) +	int k; + +	if (type == 0 /* 0ut data */ ) { +		printf("isp116x: %s: out data: ", __FUNCTION__); +		for (k = 0; k < PTD_GET_LEN(ptd); ++k) +			printf("%02x ", ((u8 *) buf)[k]); +		printf("\n"); +	} +	if (type == 1 /* 1n data */ ) { +		printf("isp116x: %s: in data: ", __FUNCTION__); +		for (k = 0; k < PTD_GET_COUNT(ptd); ++k) +			printf("%02x ", ((u8 *) buf)[k]); +		printf("\n"); +	} + +	if (PTD_GET_LAST(ptd)) +		DBG("--- last PTD ---"); +#endif +} + +#else + +#define dump_msg(dev, pipe, buffer, len, str)			do { } while (0) +#define dump_pkt(dev, pipe, buffer, len, setup, str, small)	do {} while (0) + +#define dump_ptd(ptd)			do {} while (0) +#define dump_ptd_data(ptd, buf, type)	do {} while (0) + +#endif + +/* --- Virtual Root Hub ---------------------------------------------------- */ + +#include <usbroothubdes.h> + +/* + * Hub class-specific descriptor is constructed dynamically + */ + +/* --- Virtual root hub management functions ------------------------------- */ + +static int rh_check_port_status(struct isp116x *isp116x) +{ +	u32 temp, ndp, i; +	int res; + +	res = -1; +	temp = isp116x_read_reg32(isp116x, HCRHSTATUS); +	ndp = (temp & RH_A_NDP); +	for (i = 0; i < ndp; i++) { +		temp = isp116x_read_reg32(isp116x, HCRHPORT1 + i); +		/* check for a device disconnect */ +		if (((temp & (RH_PS_PESC | RH_PS_CSC)) == +		     (RH_PS_PESC | RH_PS_CSC)) && ((temp & RH_PS_CCS) == 0)) { +			res = i; +			break; +		} +	} +	return res; +} + +/* --- HC management functions --------------------------------------------- */ + +/* Write len bytes to fifo, pad till 32-bit boundary + */ +static void write_ptddata_to_fifo(struct isp116x *isp116x, void *buf, int len) +{ +	u8 *dp = (u8 *) buf; +	u16 *dp2 = (u16 *) buf; +	u16 w; +	int quot = len % 4; + +	if ((unsigned long)dp2 & 1) { +		/* not aligned */ +		for (; len > 1; len -= 2) { +			w = *dp++; +			w |= *dp++ << 8; +			isp116x_raw_write_data16(isp116x, w); +		} +		if (len) +			isp116x_write_data16(isp116x, (u16) * dp); +	} else { +		/* aligned */ +		for (; len > 1; len -= 2) +			isp116x_raw_write_data16(isp116x, *dp2++); +		if (len) +			isp116x_write_data16(isp116x, 0xff & *((u8 *) dp2)); +	} +	if (quot == 1 || quot == 2) +		isp116x_raw_write_data16(isp116x, 0); +} + +/* Read len bytes from fifo and then read till 32-bit boundary + */ +static void read_ptddata_from_fifo(struct isp116x *isp116x, void *buf, int len) +{ +	u8 *dp = (u8 *) buf; +	u16 *dp2 = (u16 *) buf; +	u16 w; +	int quot = len % 4; + +	if ((unsigned long)dp2 & 1) { +		/* not aligned */ +		for (; len > 1; len -= 2) { +			w = isp116x_raw_read_data16(isp116x); +			*dp++ = w & 0xff; +			*dp++ = (w >> 8) & 0xff; +		} +		if (len) +			*dp = 0xff & isp116x_read_data16(isp116x); +	} else { +		/* aligned */ +		for (; len > 1; len -= 2) +			*dp2++ = isp116x_raw_read_data16(isp116x); +		if (len) +			*(u8 *) dp2 = 0xff & isp116x_read_data16(isp116x); +	} +	if (quot == 1 || quot == 2) +		isp116x_raw_read_data16(isp116x); +} + +/* Write PTD's and data for scheduled transfers into the fifo ram. + * Fifo must be empty and ready */ +static void pack_fifo(struct isp116x *isp116x, struct usb_device *dev, +		      unsigned long pipe, struct ptd *ptd, int n, void *data, +		      int len) +{ +	int buflen = n * sizeof(struct ptd) + len; +	int i, done; + +	DBG("--- pack buffer %p - %d bytes (fifo %d) ---", data, len, buflen); + +	isp116x_write_reg16(isp116x, HCuPINT, HCuPINT_AIIEOT); +	isp116x_write_reg16(isp116x, HCXFERCTR, buflen); +	isp116x_write_addr(isp116x, HCATLPORT | ISP116x_WRITE_OFFSET); + +	done = 0; +	for (i = 0; i < n; i++) { +		DBG("i=%d - done=%d - len=%d", i, done, PTD_GET_LEN(&ptd[i])); + +		dump_ptd(&ptd[i]); +		isp116x_write_data16(isp116x, ptd[i].count); +		isp116x_write_data16(isp116x, ptd[i].mps); +		isp116x_write_data16(isp116x, ptd[i].len); +		isp116x_write_data16(isp116x, ptd[i].faddr); + +		dump_ptd_data(&ptd[i], (__u8 *) data + done, 0); +		write_ptddata_to_fifo(isp116x, +				      (__u8 *) data + done, +				      PTD_GET_LEN(&ptd[i])); + +		done += PTD_GET_LEN(&ptd[i]); +	} +} + +/* Read the processed PTD's and data from fifo ram back to URBs' buffers. + * Fifo must be full and done */ +static int unpack_fifo(struct isp116x *isp116x, struct usb_device *dev, +		       unsigned long pipe, struct ptd *ptd, int n, void *data, +		       int len) +{ +	int buflen = n * sizeof(struct ptd) + len; +	int i, done, cc, ret; + +	isp116x_write_reg16(isp116x, HCuPINT, HCuPINT_AIIEOT); +	isp116x_write_reg16(isp116x, HCXFERCTR, buflen); +	isp116x_write_addr(isp116x, HCATLPORT); + +	ret = TD_CC_NOERROR; +	done = 0; +	for (i = 0; i < n; i++) { +		DBG("i=%d - done=%d - len=%d", i, done, PTD_GET_LEN(&ptd[i])); + +		ptd[i].count = isp116x_read_data16(isp116x); +		ptd[i].mps = isp116x_read_data16(isp116x); +		ptd[i].len = isp116x_read_data16(isp116x); +		ptd[i].faddr = isp116x_read_data16(isp116x); +		dump_ptd(&ptd[i]); + +		read_ptddata_from_fifo(isp116x, +				       (__u8 *) data + done, +				       PTD_GET_LEN(&ptd[i])); +		dump_ptd_data(&ptd[i], (__u8 *) data + done, 1); + +		done += PTD_GET_LEN(&ptd[i]); + +		cc = PTD_GET_CC(&ptd[i]); + +		/* Data underrun means basically that we had more buffer space than +		 * the function had data. It is perfectly normal but upper levels have +		 * to know how much we actually transferred. +		 */ +		if (cc == TD_NOTACCESSED || +				(cc != TD_CC_NOERROR && (ret == TD_CC_NOERROR || ret == TD_DATAUNDERRUN))) +			ret = cc; +	} + +	DBG("--- unpack buffer %p - %d bytes (fifo %d) ---", data, len, buflen); + +	return ret; +} + +/* Interrupt handling + */ +static int isp116x_interrupt(struct isp116x *isp116x) +{ +	u16 irqstat; +	u32 intstat; +	int ret = 0; + +	isp116x_write_reg16(isp116x, HCuPINTENB, 0); +	irqstat = isp116x_read_reg16(isp116x, HCuPINT); +	isp116x_write_reg16(isp116x, HCuPINT, irqstat); +	DBG(">>>>>> irqstat %x <<<<<<", irqstat); + +	if (irqstat & HCuPINT_ATL) { +		DBG(">>>>>> HCuPINT_ATL <<<<<<"); +		udelay(500); +		ret = 1; +	} + +	if (irqstat & HCuPINT_OPR) { +		intstat = isp116x_read_reg32(isp116x, HCINTSTAT); +		isp116x_write_reg32(isp116x, HCINTSTAT, intstat); +		DBG(">>>>>> HCuPINT_OPR %x <<<<<<", intstat); + +		if (intstat & HCINT_UE) { +			ERR("unrecoverable error, controller disabled"); + +			/* FIXME: be optimistic, hope that bug won't repeat +			 * often. Make some non-interrupt context restart the +			 * controller. Count and limit the retries though; +			 * either hardware or software errors can go forever... +			 */ +			isp116x_reset(isp116x); +			ret = -1; +			return -1; +		} + +		if (intstat & HCINT_RHSC) { +			got_rhsc = 1; +			ret = 1; +			/* When root hub or any of its ports is going +			   to come out of suspend, it may take more +			   than 10ms for status bits to stabilize. */ +			mdelay(20); +		} + +		if (intstat & HCINT_SO) { +			ERR("schedule overrun"); +			ret = -1; +		} + +		irqstat &= ~HCuPINT_OPR; +	} + +	return ret; +} + +/* With one PTD we can transfer almost 1K in one go; + * HC does the splitting into endpoint digestible transactions + */ +struct ptd ptd[1]; + +static inline int max_transfer_len(struct usb_device *dev, unsigned long pipe) +{ +	unsigned mpck = usb_maxpacket(dev, pipe); + +	/* One PTD can transfer 1023 bytes but try to always +	 * transfer multiples of endpoint buffer size +	 */ +	return 1023 / mpck * mpck; +} + +/* Do an USB transfer + */ +static int isp116x_submit_job(struct usb_device *dev, unsigned long pipe, +			      int dir, void *buffer, int len) +{ +	struct isp116x *isp116x = &isp116x_dev; +	int type = usb_pipetype(pipe); +	int epnum = usb_pipeendpoint(pipe); +	int max = usb_maxpacket(dev, pipe); +	int dir_out = usb_pipeout(pipe); +	int speed_low = (dev->speed == USB_SPEED_LOW); +	int i, done = 0, stat, timeout, cc; + +	/* 500 frames or 0.5s timeout when function is busy and NAKs transactions for a while */ +	int retries = 500; + +	DBG("------------------------------------------------"); +	dump_msg(dev, pipe, buffer, len, "SUBMIT"); +	DBG("------------------------------------------------"); + +	if (len >= 1024) { +		ERR("Too big job"); +		dev->status = USB_ST_CRC_ERR; +		return -1; +	} + +	if (isp116x->disabled) { +		ERR("EPIPE"); +		dev->status = USB_ST_CRC_ERR; +		return -1; +	} + +	/* device pulled? Shortcut the action. */ +	if (devgone == dev) { +		ERR("ENODEV"); +		dev->status = USB_ST_CRC_ERR; +		return USB_ST_CRC_ERR; +	} + +	if (!max) { +		ERR("pipesize for pipe %lx is zero", pipe); +		dev->status = USB_ST_CRC_ERR; +		return -1; +	} + +	if (type == PIPE_ISOCHRONOUS) { +		ERR("isochronous transfers not supported"); +		dev->status = USB_ST_CRC_ERR; +		return -1; +	} + +	/* FIFO not empty? */ +	if (isp116x_read_reg16(isp116x, HCBUFSTAT) & HCBUFSTAT_ATL_FULL) { +		ERR("****** FIFO not empty! ******"); +		dev->status = USB_ST_BUF_ERR; +		return -1; +	} + +      retry: +	isp116x_write_reg32(isp116x, HCINTSTAT, 0xff); + +	/* Prepare the PTD data */ +	ptd->count = PTD_CC_MSK | PTD_ACTIVE_MSK | +		PTD_TOGGLE(usb_gettoggle(dev, epnum, dir_out)); +	ptd->mps = PTD_MPS(max) | PTD_SPD(speed_low) | PTD_EP(epnum) | PTD_LAST_MSK; +	ptd->len = PTD_LEN(len) | PTD_DIR(dir); +	ptd->faddr = PTD_FA(usb_pipedevice(pipe)); + +retry_same: +	/* Pack data into FIFO ram */ +	pack_fifo(isp116x, dev, pipe, ptd, 1, buffer, len); +#ifdef EXTRA_DELAY +	mdelay(EXTRA_DELAY); +#endif + +	/* Start the data transfer */ + +	/* Allow more time for a BULK device to react - some are slow */ +	if (usb_pipebulk(pipe)) +		timeout = 5000; +	else +		timeout = 100; + +	/* Wait for it to complete */ +	for (;;) { +		/* Check whether the controller is done */ +		stat = isp116x_interrupt(isp116x); + +		if (stat < 0) { +			dev->status = USB_ST_CRC_ERR; +			break; +		} +		if (stat > 0) +			break; + +		/* Check the timeout */ +		if (--timeout) +			udelay(1); +		else { +			ERR("CTL:TIMEOUT "); +			stat = USB_ST_CRC_ERR; +			break; +		} +	} + +	/* We got an Root Hub Status Change interrupt */ +	if (got_rhsc) { +		isp116x_show_regs(isp116x); + +		got_rhsc = 0; + +		/* Abuse timeout */ +		timeout = rh_check_port_status(isp116x); +		if (timeout >= 0) { +			/* +			 * FIXME! NOTE! AAAARGH! +			 * This is potentially dangerous because it assumes +			 * that only one device is ever plugged in! +			 */ +			devgone = dev; +		} +	} + +	/* Ok, now we can read transfer status */ + +	/* FIFO not ready? */ +	if (!(isp116x_read_reg16(isp116x, HCBUFSTAT) & HCBUFSTAT_ATL_DONE)) { +		ERR("****** FIFO not ready! ******"); +		dev->status = USB_ST_BUF_ERR; +		return -1; +	} + +	/* Unpack data from FIFO ram */ +	cc = unpack_fifo(isp116x, dev, pipe, ptd, 1, buffer, len); + +	i = PTD_GET_COUNT(ptd); +	done += i; +	buffer += i; +	len -= i; + +	/* There was some kind of real problem; Prepare the PTD again +	 * and retry from the failed transaction on +	 */ +	if (cc && cc != TD_NOTACCESSED && cc != TD_DATAUNDERRUN) { +		if (retries >= 100) { +			retries -= 100; +			/* The chip will have toggled the toggle bit for the failed +			 * transaction too. We have to toggle it back. +			 */ +			usb_settoggle(dev, epnum, dir_out, !PTD_GET_TOGGLE(ptd)); +			goto retry; +		} +	} +	/* "Normal" errors; TD_NOTACCESSED would mean in effect that the function have NAKed +	 * the transactions from the first on for the whole frame. It may be busy and we retry +	 * with the same PTD. PTD_ACTIVE (and not TD_NOTACCESSED) would mean that some of the +	 * PTD didn't make it because the function was busy or the frame ended before the PTD +	 * finished. We prepare the rest of the data and try again. +	 */ +	else if (cc == TD_NOTACCESSED || PTD_GET_ACTIVE(ptd) || (cc != TD_DATAUNDERRUN && PTD_GET_COUNT(ptd) < PTD_GET_LEN(ptd))) { +		if (retries) { +			--retries; +			if (cc == TD_NOTACCESSED && PTD_GET_ACTIVE(ptd) && !PTD_GET_COUNT(ptd)) goto retry_same; +			usb_settoggle(dev, epnum, dir_out, PTD_GET_TOGGLE(ptd)); +			goto retry; +		} +	} + +	if (cc != TD_CC_NOERROR && cc != TD_DATAUNDERRUN) { +		DBG("****** completition code error %x ******", cc); +		switch (cc) { +		case TD_CC_BITSTUFFING: +			dev->status = USB_ST_BIT_ERR; +			break; +		case TD_CC_STALL: +			dev->status = USB_ST_STALLED; +			break; +		case TD_BUFFEROVERRUN: +		case TD_BUFFERUNDERRUN: +			dev->status = USB_ST_BUF_ERR; +			break; +		default: +			dev->status = USB_ST_CRC_ERR; +		} +		return -cc; +	} +	else usb_settoggle(dev, epnum, dir_out, PTD_GET_TOGGLE(ptd)); + +	dump_msg(dev, pipe, buffer, len, "SUBMIT(ret)"); + +	dev->status = 0; +	return done; +} + +/* Adapted from au1x00_usb_ohci.c + */ +static int isp116x_submit_rh_msg(struct usb_device *dev, unsigned long pipe, +				 void *buffer, int transfer_len, +				 struct devrequest *cmd) +{ +	struct isp116x *isp116x = &isp116x_dev; +	u32 tmp = 0; + +	int leni = transfer_len; +	int len = 0; +	int stat = 0; +	u32 datab[4]; +	u8 *data_buf = (u8 *) datab; +	u16 bmRType_bReq; +	u16 wValue; +	u16 wIndex; +	u16 wLength; + +	if (usb_pipeint(pipe)) { +		INFO("Root-Hub submit IRQ: NOT implemented"); +		return 0; +	} + +	bmRType_bReq = cmd->requesttype | (cmd->request << 8); +	wValue = swap_16(cmd->value); +	wIndex = swap_16(cmd->index); +	wLength = swap_16(cmd->length); + +	DBG("--- HUB ----------------------------------------"); +	DBG("submit rh urb, req=%x val=%#x index=%#x len=%d", +	    bmRType_bReq, wValue, wIndex, wLength); +	dump_msg(dev, pipe, buffer, transfer_len, "RH"); +	DBG("------------------------------------------------"); + +	switch (bmRType_bReq) { +	case RH_GET_STATUS: +		DBG("RH_GET_STATUS"); + +		*(__u16 *) data_buf = swap_16(1); +		len = 2; +		break; + +	case RH_GET_STATUS | RH_INTERFACE: +		DBG("RH_GET_STATUS | RH_INTERFACE"); + +		*(__u16 *) data_buf = swap_16(0); +		len = 2; +		break; + +	case RH_GET_STATUS | RH_ENDPOINT: +		DBG("RH_GET_STATUS | RH_ENDPOINT"); + +		*(__u16 *) data_buf = swap_16(0); +		len = 2; +		break; + +	case RH_GET_STATUS | RH_CLASS: +		DBG("RH_GET_STATUS | RH_CLASS"); + +		tmp = isp116x_read_reg32(isp116x, HCRHSTATUS); + +		*(__u32 *) data_buf = swap_32(tmp & ~(RH_HS_CRWE | RH_HS_DRWE)); +		len = 4; +		break; + +	case RH_GET_STATUS | RH_OTHER | RH_CLASS: +		DBG("RH_GET_STATUS | RH_OTHER | RH_CLASS"); + +		tmp = isp116x_read_reg32(isp116x, HCRHPORT1 + wIndex - 1); +		*(__u32 *) data_buf = swap_32(tmp); +		isp116x_show_regs(isp116x); +		len = 4; +		break; + +	case RH_CLEAR_FEATURE | RH_ENDPOINT: +		DBG("RH_CLEAR_FEATURE | RH_ENDPOINT"); + +		switch (wValue) { +		case RH_ENDPOINT_STALL: +			DBG("C_HUB_ENDPOINT_STALL"); +			len = 0; +			break; +		} +		break; + +	case RH_CLEAR_FEATURE | RH_CLASS: +		DBG("RH_CLEAR_FEATURE | RH_CLASS"); + +		switch (wValue) { +		case RH_C_HUB_LOCAL_POWER: +			DBG("C_HUB_LOCAL_POWER"); +			len = 0; +			break; + +		case RH_C_HUB_OVER_CURRENT: +			DBG("C_HUB_OVER_CURRENT"); +			isp116x_write_reg32(isp116x, HCRHSTATUS, RH_HS_OCIC); +			len = 0; +			break; +		} +		break; + +	case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS: +		DBG("RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS"); + +		switch (wValue) { +		case RH_PORT_ENABLE: +			isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1, +					    RH_PS_CCS); +			len = 0; +			break; + +		case RH_PORT_SUSPEND: +			isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1, +					    RH_PS_POCI); +			len = 0; +			break; + +		case RH_PORT_POWER: +			isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1, +					    RH_PS_LSDA); +			len = 0; +			break; + +		case RH_C_PORT_CONNECTION: +			isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1, +					    RH_PS_CSC); +			len = 0; +			break; + +		case RH_C_PORT_ENABLE: +			isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1, +					    RH_PS_PESC); +			len = 0; +			break; + +		case RH_C_PORT_SUSPEND: +			isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1, +					    RH_PS_PSSC); +			len = 0; +			break; + +		case RH_C_PORT_OVER_CURRENT: +			isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1, +					    RH_PS_POCI); +			len = 0; +			break; + +		case RH_C_PORT_RESET: +			isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1, +					    RH_PS_PRSC); +			len = 0; +			break; + +		default: +			ERR("invalid wValue"); +			stat = USB_ST_STALLED; +		} + +		isp116x_show_regs(isp116x); + +		break; + +	case RH_SET_FEATURE | RH_OTHER | RH_CLASS: +		DBG("RH_SET_FEATURE | RH_OTHER | RH_CLASS"); + +		switch (wValue) { +		case RH_PORT_SUSPEND: +			isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1, +					    RH_PS_PSS); +			len = 0; +			break; + +		case RH_PORT_RESET: +			/* Spin until any current reset finishes */ +			while (1) { +				tmp = +				    isp116x_read_reg32(isp116x, +						       HCRHPORT1 + wIndex - 1); +				if (!(tmp & RH_PS_PRS)) +					break; +				mdelay(1); +			} +			isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1, +					    RH_PS_PRS); +			mdelay(10); + +			len = 0; +			break; + +		case RH_PORT_POWER: +			isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1, +					    RH_PS_PPS); +			len = 0; +			break; + +		case RH_PORT_ENABLE: +			isp116x_write_reg32(isp116x, HCRHPORT1 + wIndex - 1, +					    RH_PS_PES); +			len = 0; +			break; + +		default: +			ERR("invalid wValue"); +			stat = USB_ST_STALLED; +		} + +		isp116x_show_regs(isp116x); + +		break; + +	case RH_SET_ADDRESS: +		DBG("RH_SET_ADDRESS"); + +		rh_devnum = wValue; +		len = 0; +		break; + +	case RH_GET_DESCRIPTOR: +		DBG("RH_GET_DESCRIPTOR: %x, %d", wValue, wLength); + +		switch (wValue) { +		case (USB_DT_DEVICE << 8):	/* device descriptor */ +			len = min_t(unsigned int, +				    leni, min_t(unsigned int, +						sizeof(root_hub_dev_des), +						wLength)); +			data_buf = root_hub_dev_des; +			break; + +		case (USB_DT_CONFIG << 8):	/* configuration descriptor */ +			len = min_t(unsigned int, +				    leni, min_t(unsigned int, +						sizeof(root_hub_config_des), +						wLength)); +			data_buf = root_hub_config_des; +			break; + +		case ((USB_DT_STRING << 8) | 0x00):	/* string 0 descriptors */ +			len = min_t(unsigned int, +				    leni, min_t(unsigned int, +						sizeof(root_hub_str_index0), +						wLength)); +			data_buf = root_hub_str_index0; +			break; + +		case ((USB_DT_STRING << 8) | 0x01):	/* string 1 descriptors */ +			len = min_t(unsigned int, +				    leni, min_t(unsigned int, +						sizeof(root_hub_str_index1), +						wLength)); +			data_buf = root_hub_str_index1; +			break; + +		default: +			ERR("invalid wValue"); +			stat = USB_ST_STALLED; +		} + +		break; + +	case RH_GET_DESCRIPTOR | RH_CLASS: +		DBG("RH_GET_DESCRIPTOR | RH_CLASS"); + +		tmp = isp116x_read_reg32(isp116x, HCRHDESCA); + +		data_buf[0] = 0x09;	/* min length; */ +		data_buf[1] = 0x29; +		data_buf[2] = tmp & RH_A_NDP; +		data_buf[3] = 0; +		if (tmp & RH_A_PSM)	/* per-port power switching? */ +			data_buf[3] |= 0x01; +		if (tmp & RH_A_NOCP)	/* no overcurrent reporting? */ +			data_buf[3] |= 0x10; +		else if (tmp & RH_A_OCPM)	/* per-port overcurrent rep? */ +			data_buf[3] |= 0x08; + +		/* Corresponds to data_buf[4-7] */ +		datab[1] = 0; +		data_buf[5] = (tmp & RH_A_POTPGT) >> 24; + +		tmp = isp116x_read_reg32(isp116x, HCRHDESCB); + +		data_buf[7] = tmp & RH_B_DR; +		if (data_buf[2] < 7) +			data_buf[8] = 0xff; +		else { +			data_buf[0] += 2; +			data_buf[8] = (tmp & RH_B_DR) >> 8; +			data_buf[10] = data_buf[9] = 0xff; +		} + +		len = min_t(unsigned int, leni, +			    min_t(unsigned int, data_buf[0], wLength)); +		break; + +	case RH_GET_CONFIGURATION: +		DBG("RH_GET_CONFIGURATION"); + +		*(__u8 *) data_buf = 0x01; +		len = 1; +		break; + +	case RH_SET_CONFIGURATION: +		DBG("RH_SET_CONFIGURATION"); + +		isp116x_write_reg32(isp116x, HCRHSTATUS, RH_HS_LPSC); +		len = 0; +		break; + +	default: +		ERR("*** *** *** unsupported root hub command *** *** ***"); +		stat = USB_ST_STALLED; +	} + +	len = min_t(int, len, leni); +	if (buffer != data_buf) +		memcpy(buffer, data_buf, len); + +	dev->act_len = len; +	dev->status = stat; +	DBG("dev act_len %d, status %d", dev->act_len, dev->status); + +	dump_msg(dev, pipe, buffer, transfer_len, "RH(ret)"); + +	return stat; +} + +/* --- Transfer functions -------------------------------------------------- */ + +int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, +		   int len, int interval) +{ +	DBG("dev=%p pipe=%#lx buf=%p size=%d int=%d", +	    dev, pipe, buffer, len, interval); + +	return -1; +} + +int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, +		       int len, struct devrequest *setup) +{ +	int devnum = usb_pipedevice(pipe); +	int epnum = usb_pipeendpoint(pipe); +	int max = max_transfer_len(dev, pipe); +	int dir_in = usb_pipein(pipe); +	int done, ret; + +	/* Control message is for the HUB? */ +	if (devnum == rh_devnum) +		return isp116x_submit_rh_msg(dev, pipe, buffer, len, setup); + +	/* Ok, no HUB message so send the message to the device */ + +	/* Setup phase */ +	DBG("--- SETUP PHASE --------------------------------"); +	usb_settoggle(dev, epnum, 1, 0); +	ret = isp116x_submit_job(dev, pipe, +				 PTD_DIR_SETUP, +				 setup, sizeof(struct devrequest)); +	if (ret < 0) { +		DBG("control setup phase error (ret = %d", ret); +		return -1; +	} + +	/* Data phase */ +	DBG("--- DATA PHASE ---------------------------------"); +	done = 0; +	usb_settoggle(dev, epnum, !dir_in, 1); +	while (done < len) { +		ret = isp116x_submit_job(dev, pipe, +					 dir_in ? PTD_DIR_IN : PTD_DIR_OUT, +					 (__u8 *) buffer + done, +					 max > len - done ? len - done : max); +		if (ret < 0) { +			DBG("control data phase error (ret = %d)", ret); +			return -1; +		} +		done += ret; + +		if (dir_in && ret < max)	/* short packet */ +			break; +	} + +	/* Status phase */ +	DBG("--- STATUS PHASE -------------------------------"); +	usb_settoggle(dev, epnum, !dir_in, 1); +	ret = isp116x_submit_job(dev, pipe, +				 !dir_in ? PTD_DIR_IN : PTD_DIR_OUT, NULL, 0); +	if (ret < 0) { +		DBG("control status phase error (ret = %d", ret); +		return -1; +	} + +	dev->act_len = done; + +	dump_msg(dev, pipe, buffer, len, "DEV(ret)"); + +	return done; +} + +int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer, +		    int len) +{ +	int dir_out = usb_pipeout(pipe); +	int max = max_transfer_len(dev, pipe); +	int done, ret; + +	DBG("--- BULK ---------------------------------------"); +	DBG("dev=%ld pipe=%ld buf=%p size=%d dir_out=%d", +	    usb_pipedevice(pipe), usb_pipeendpoint(pipe), buffer, len, dir_out); + +	done = 0; +	while (done < len) { +		ret = isp116x_submit_job(dev, pipe, +					 !dir_out ? PTD_DIR_IN : PTD_DIR_OUT, +					 (__u8 *) buffer + done, +					 max > len - done ? len - done : max); +		if (ret < 0) { +			DBG("error on bulk message (ret = %d)", ret); +			return -1; +		} + +		done += ret; + +		if (!dir_out && ret < max)	/* short packet */ +			break; +	} + +	dev->act_len = done; + +	return 0; +} + +/* --- Basic functions ----------------------------------------------------- */ + +static int isp116x_sw_reset(struct isp116x *isp116x) +{ +	int retries = 15; +	int ret = 0; + +	DBG(""); + +	isp116x->disabled = 1; + +	isp116x_write_reg16(isp116x, HCSWRES, HCSWRES_MAGIC); +	isp116x_write_reg32(isp116x, HCCMDSTAT, HCCMDSTAT_HCR); +	while (--retries) { +		/* It usually resets within 1 ms */ +		mdelay(1); +		if (!(isp116x_read_reg32(isp116x, HCCMDSTAT) & HCCMDSTAT_HCR)) +			break; +	} +	if (!retries) { +		ERR("software reset timeout"); +		ret = -1; +	} +	return ret; +} + +static int isp116x_reset(struct isp116x *isp116x) +{ +	unsigned long t; +	u16 clkrdy = 0; +	int ret, timeout = 15 /* ms */ ; + +	DBG(""); + +	ret = isp116x_sw_reset(isp116x); +	if (ret) +		return ret; + +	for (t = 0; t < timeout; t++) { +		clkrdy = isp116x_read_reg16(isp116x, HCuPINT) & HCuPINT_CLKRDY; +		if (clkrdy) +			break; +		mdelay(1); +	} +	if (!clkrdy) { +		ERR("clock not ready after %dms", timeout); +		/* After sw_reset the clock won't report to be ready, if +		   H_WAKEUP pin is high. */ +		ERR("please make sure that the H_WAKEUP pin is pulled low!"); +		ret = -1; +	} +	return ret; +} + +static void isp116x_stop(struct isp116x *isp116x) +{ +	u32 val; + +	DBG(""); + +	isp116x_write_reg16(isp116x, HCuPINTENB, 0); + +	/* Switch off ports' power, some devices don't come up +	   after next 'start' without this */ +	val = isp116x_read_reg32(isp116x, HCRHDESCA); +	val &= ~(RH_A_NPS | RH_A_PSM); +	isp116x_write_reg32(isp116x, HCRHDESCA, val); +	isp116x_write_reg32(isp116x, HCRHSTATUS, RH_HS_LPS); + +	isp116x_sw_reset(isp116x); +} + +/* + *  Configure the chip. The chip must be successfully reset by now. + */ +static int isp116x_start(struct isp116x *isp116x) +{ +	struct isp116x_platform_data *board = isp116x->board; +	u32 val; + +	DBG(""); + +	/* Clear interrupt status and disable all interrupt sources */ +	isp116x_write_reg16(isp116x, HCuPINT, 0xff); +	isp116x_write_reg16(isp116x, HCuPINTENB, 0); + +	isp116x_write_reg16(isp116x, HCITLBUFLEN, ISP116x_ITL_BUFSIZE); +	isp116x_write_reg16(isp116x, HCATLBUFLEN, ISP116x_ATL_BUFSIZE); + +	/* Hardware configuration */ +	val = HCHWCFG_DBWIDTH(1); +	if (board->sel15Kres) +		val |= HCHWCFG_15KRSEL; +	/* Remote wakeup won't work without working clock */ +	if (board->remote_wakeup_enable) +		val |= HCHWCFG_CLKNOTSTOP; +	if (board->oc_enable) +		val |= HCHWCFG_ANALOG_OC; +	isp116x_write_reg16(isp116x, HCHWCFG, val); + +	/* --- Root hub configuration */ +	val = (25 << 24) & RH_A_POTPGT; +	/* AN10003_1.pdf recommends RH_A_NPS (no power switching) to +	   be always set. Yet, instead, we request individual port +	   power switching. */ +	val |= RH_A_PSM; +	/* Report overcurrent per port */ +	val |= RH_A_OCPM; +	isp116x_write_reg32(isp116x, HCRHDESCA, val); +	isp116x->rhdesca = isp116x_read_reg32(isp116x, HCRHDESCA); + +	val = RH_B_PPCM; +	isp116x_write_reg32(isp116x, HCRHDESCB, val); +	isp116x->rhdescb = isp116x_read_reg32(isp116x, HCRHDESCB); + +	val = 0; +	if (board->remote_wakeup_enable) +		val |= RH_HS_DRWE; +	isp116x_write_reg32(isp116x, HCRHSTATUS, val); +	isp116x->rhstatus = isp116x_read_reg32(isp116x, HCRHSTATUS); + +	isp116x_write_reg32(isp116x, HCFMINTVL, 0x27782edf); + +	/* Go operational */ +	val = HCCONTROL_USB_OPER; +	if (board->remote_wakeup_enable) +		val |= HCCONTROL_RWE; +	isp116x_write_reg32(isp116x, HCCONTROL, val); + +	/* Disable ports to avoid race in device enumeration */ +	isp116x_write_reg32(isp116x, HCRHPORT1, RH_PS_CCS); +	isp116x_write_reg32(isp116x, HCRHPORT2, RH_PS_CCS); + +	isp116x_show_regs(isp116x); + +	isp116x->disabled = 0; + +	return 0; +} + +/* --- Init functions ------------------------------------------------------ */ + +int isp116x_check_id(struct isp116x *isp116x) +{ +	int val; + +	val = isp116x_read_reg16(isp116x, HCCHIPID); +	if ((val & HCCHIPID_MASK) != HCCHIPID_MAGIC) { +		ERR("invalid chip ID %04x", val); +		return -1; +	} + +	return 0; +} + +int usb_lowlevel_init(int index, enum usb_init_type init, void **controller)) +{ +	struct isp116x *isp116x = &isp116x_dev; + +	DBG(""); + +	got_rhsc = rh_devnum = 0; + +	/* Init device registers addr */ +	isp116x->addr_reg = (u16 *) ISP116X_HCD_ADDR; +	isp116x->data_reg = (u16 *) ISP116X_HCD_DATA; + +	/* Setup specific board settings */ +#ifdef ISP116X_HCD_SEL15kRES +	isp116x_board.sel15Kres = 1; +#endif +#ifdef ISP116X_HCD_OC_ENABLE +	isp116x_board.oc_enable = 1; +#endif +#ifdef ISP116X_HCD_REMOTE_WAKEUP_ENABLE +	isp116x_board.remote_wakeup_enable = 1; +#endif +	isp116x->board = &isp116x_board; + +	/* Try to get ISP116x silicon chip ID */ +	if (isp116x_check_id(isp116x) < 0) +		return -1; + +	isp116x->disabled = 1; +	isp116x->sleeping = 0; + +	isp116x_reset(isp116x); +	isp116x_start(isp116x); + +	return 0; +} + +int usb_lowlevel_stop(int index) +{ +	struct isp116x *isp116x = &isp116x_dev; + +	DBG(""); + +	if (!isp116x->disabled) +		isp116x_stop(isp116x); + +	return 0; +} diff --git a/roms/u-boot/drivers/usb/host/isp116x.h b/roms/u-boot/drivers/usb/host/isp116x.h new file mode 100644 index 00000000..5b7afaf4 --- /dev/null +++ b/roms/u-boot/drivers/usb/host/isp116x.h @@ -0,0 +1,476 @@ +/* + * ISP116x register declarations and HCD data structures + * + * Copyright (C) 2007 Rodolfo Giometti <giometti@linux.it> + * Copyright (C) 2007 Eurotech S.p.A. <info@eurotech.it> + * Copyright (C) 2005 Olav Kongas <ok@artecdesign.ee> + * Portions: + * Copyright (C) 2004 Lothar Wassmann + * Copyright (C) 2004 Psion Teklogix + * Copyright (C) 2004 David Brownell + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +#ifdef DEBUG +#define DBG(fmt, args...)	\ +		printf("isp116x: %s: " fmt "\n" , __FUNCTION__ , ## args) +#else +#define DBG(fmt, args...)	do {} while (0) +#endif + +#ifdef VERBOSE +#    define VDBG		DBG +#else +#    define VDBG(fmt, args...)	do {} while (0) +#endif + +#define ERR(fmt, args...)	\ +		printf("isp116x: %s: " fmt "\n" , __FUNCTION__ , ## args) +#define WARN(fmt, args...)	\ +		printf("isp116x: %s: " fmt "\n" , __FUNCTION__ , ## args) +#define INFO(fmt, args...)	\ +		printf("isp116x: " fmt "\n" , ## args) + +/* ------------------------------------------------------------------------- */ + +/* us of 1ms frame */ +#define  MAX_LOAD_LIMIT		850 + +/* Full speed: max # of bytes to transfer for a single urb +   at a time must be < 1024 && must be multiple of 64. +   832 allows transfering 4kiB within 5 frames. */ +#define MAX_TRANSFER_SIZE_FULLSPEED	832 + +/* Low speed: there is no reason to schedule in very big +   chunks; often the requested long transfers are for +   string descriptors containing short strings. */ +#define MAX_TRANSFER_SIZE_LOWSPEED	64 + +/* Bytetime (us), a rough indication of how much time it +   would take to transfer a byte of useful data over USB */ +#define BYTE_TIME_FULLSPEED	1 +#define BYTE_TIME_LOWSPEED	20 + +/* Buffer sizes */ +#define ISP116x_BUF_SIZE	4096 +#define ISP116x_ITL_BUFSIZE	0 +#define ISP116x_ATL_BUFSIZE	((ISP116x_BUF_SIZE) - 2*(ISP116x_ITL_BUFSIZE)) + +#define ISP116x_WRITE_OFFSET	0x80 + +/* --- ISP116x registers/bits ---------------------------------------------- */ + +#define	HCREVISION	0x00 +#define	HCCONTROL	0x01 +#define		HCCONTROL_HCFS	(3 << 6)	/* host controller +						   functional state */ +#define		HCCONTROL_USB_RESET	(0 << 6) +#define		HCCONTROL_USB_RESUME	(1 << 6) +#define		HCCONTROL_USB_OPER	(2 << 6) +#define		HCCONTROL_USB_SUSPEND	(3 << 6) +#define		HCCONTROL_RWC	(1 << 9)	/* remote wakeup connected */ +#define		HCCONTROL_RWE	(1 << 10)	/* remote wakeup enable */ +#define	HCCMDSTAT	0x02 +#define		HCCMDSTAT_HCR	(1 << 0)	/* host controller reset */ +#define		HCCMDSTAT_SOC	(3 << 16)	/* scheduling overrun count */ +#define	HCINTSTAT	0x03 +#define		HCINT_SO	(1 << 0)	/* scheduling overrun */ +#define		HCINT_WDH	(1 << 1)	/* writeback of done_head */ +#define		HCINT_SF	(1 << 2)	/* start frame */ +#define		HCINT_RD	(1 << 3)	/* resume detect */ +#define		HCINT_UE	(1 << 4)	/* unrecoverable error */ +#define		HCINT_FNO	(1 << 5)	/* frame number overflow */ +#define		HCINT_RHSC	(1 << 6)	/* root hub status change */ +#define		HCINT_OC	(1 << 30)	/* ownership change */ +#define		HCINT_MIE	(1 << 31)	/* master interrupt enable */ +#define	HCINTENB	0x04 +#define	HCINTDIS	0x05 +#define	HCFMINTVL	0x0d +#define	HCFMREM		0x0e +#define	HCFMNUM		0x0f +#define	HCLSTHRESH	0x11 +#define	HCRHDESCA	0x12 +#define		RH_A_NDP	(0x3 << 0)	/* # downstream ports */ +#define		RH_A_PSM	(1 << 8)	/* power switching mode */ +#define		RH_A_NPS	(1 << 9)	/* no power switching */ +#define		RH_A_DT		(1 << 10)	/* device type (mbz) */ +#define		RH_A_OCPM	(1 << 11)	/* overcurrent protection +						   mode */ +#define		RH_A_NOCP	(1 << 12)	/* no overcurrent protection */ +#define		RH_A_POTPGT	(0xff << 24)	/* power on -> power good +						   time */ +#define	HCRHDESCB	0x13 +#define		RH_B_DR		(0xffff << 0)	/* device removable flags */ +#define		RH_B_PPCM	(0xffff << 16)	/* port power control mask */ +#define	HCRHSTATUS	0x14 +#define		RH_HS_LPS	(1 << 0)	/* local power status */ +#define		RH_HS_OCI	(1 << 1)	/* over current indicator */ +#define		RH_HS_DRWE	(1 << 15)	/* device remote wakeup +						   enable */ +#define		RH_HS_LPSC	(1 << 16)	/* local power status change */ +#define		RH_HS_OCIC	(1 << 17)	/* over current indicator +						   change */ +#define		RH_HS_CRWE	(1 << 31)	/* clear remote wakeup +						   enable */ +#define	HCRHPORT1	0x15 +#define		RH_PS_CCS	(1 << 0)	/* current connect status */ +#define		RH_PS_PES	(1 << 1)	/* port enable status */ +#define		RH_PS_PSS	(1 << 2)	/* port suspend status */ +#define		RH_PS_POCI	(1 << 3)	/* port over current +						   indicator */ +#define		RH_PS_PRS	(1 << 4)	/* port reset status */ +#define		RH_PS_PPS	(1 << 8)	/* port power status */ +#define		RH_PS_LSDA	(1 << 9)	/* low speed device attached */ +#define		RH_PS_CSC	(1 << 16)	/* connect status change */ +#define		RH_PS_PESC	(1 << 17)	/* port enable status change */ +#define		RH_PS_PSSC	(1 << 18)	/* port suspend status +						   change */ +#define		RH_PS_OCIC	(1 << 19)	/* over current indicator +						   change */ +#define		RH_PS_PRSC	(1 << 20)	/* port reset status change */ +#define		HCRHPORT_CLRMASK	(0x1f << 16) +#define	HCRHPORT2	0x16 +#define	HCHWCFG		0x20 +#define		HCHWCFG_15KRSEL		(1 << 12) +#define		HCHWCFG_CLKNOTSTOP	(1 << 11) +#define		HCHWCFG_ANALOG_OC	(1 << 10) +#define		HCHWCFG_DACK_MODE	(1 << 8) +#define		HCHWCFG_EOT_POL		(1 << 7) +#define		HCHWCFG_DACK_POL	(1 << 6) +#define		HCHWCFG_DREQ_POL	(1 << 5) +#define		HCHWCFG_DBWIDTH_MASK	(0x03 << 3) +#define		HCHWCFG_DBWIDTH(n)	(((n) << 3) & HCHWCFG_DBWIDTH_MASK) +#define		HCHWCFG_INT_POL		(1 << 2) +#define		HCHWCFG_INT_TRIGGER	(1 << 1) +#define		HCHWCFG_INT_ENABLE	(1 << 0) +#define	HCDMACFG	0x21 +#define		HCDMACFG_BURST_LEN_MASK	(0x03 << 5) +#define		HCDMACFG_BURST_LEN(n)	(((n) << 5) & HCDMACFG_BURST_LEN_MASK) +#define		HCDMACFG_BURST_LEN_1	HCDMACFG_BURST_LEN(0) +#define		HCDMACFG_BURST_LEN_4	HCDMACFG_BURST_LEN(1) +#define		HCDMACFG_BURST_LEN_8	HCDMACFG_BURST_LEN(2) +#define		HCDMACFG_DMA_ENABLE	(1 << 4) +#define		HCDMACFG_BUF_TYPE_MASK	(0x07 << 1) +#define		HCDMACFG_CTR_SEL	(1 << 2) +#define		HCDMACFG_ITLATL_SEL	(1 << 1) +#define		HCDMACFG_DMA_RW_SELECT	(1 << 0) +#define	HCXFERCTR	0x22 +#define	HCuPINT		0x24 +#define		HCuPINT_SOF		(1 << 0) +#define		HCuPINT_ATL		(1 << 1) +#define		HCuPINT_AIIEOT		(1 << 2) +#define		HCuPINT_OPR		(1 << 4) +#define		HCuPINT_SUSP		(1 << 5) +#define		HCuPINT_CLKRDY		(1 << 6) +#define	HCuPINTENB	0x25 +#define	HCCHIPID	0x27 +#define		HCCHIPID_MASK		0xff00 +#define		HCCHIPID_MAGIC		0x6100 +#define	HCSCRATCH	0x28 +#define	HCSWRES		0x29 +#define		HCSWRES_MAGIC		0x00f6 +#define	HCITLBUFLEN	0x2a +#define	HCATLBUFLEN	0x2b +#define	HCBUFSTAT	0x2c +#define		HCBUFSTAT_ITL0_FULL	(1 << 0) +#define		HCBUFSTAT_ITL1_FULL	(1 << 1) +#define		HCBUFSTAT_ATL_FULL	(1 << 2) +#define		HCBUFSTAT_ITL0_DONE	(1 << 3) +#define		HCBUFSTAT_ITL1_DONE	(1 << 4) +#define		HCBUFSTAT_ATL_DONE	(1 << 5) +#define	HCRDITL0LEN	0x2d +#define	HCRDITL1LEN	0x2e +#define	HCITLPORT	0x40 +#define	HCATLPORT	0x41 + +/* PTD accessor macros. */ +#define PTD_GET_COUNT(p)	(((p)->count & PTD_COUNT_MSK) >> 0) +#define PTD_COUNT(v)		(((v) << 0) & PTD_COUNT_MSK) +#define PTD_GET_TOGGLE(p)	(((p)->count & PTD_TOGGLE_MSK) >> 10) +#define PTD_TOGGLE(v)		(((v) << 10) & PTD_TOGGLE_MSK) +#define PTD_GET_ACTIVE(p)	(((p)->count & PTD_ACTIVE_MSK) >> 11) +#define PTD_ACTIVE(v)		(((v) << 11) & PTD_ACTIVE_MSK) +#define PTD_GET_CC(p)		(((p)->count & PTD_CC_MSK) >> 12) +#define PTD_CC(v)		(((v) << 12) & PTD_CC_MSK) +#define PTD_GET_MPS(p)		(((p)->mps & PTD_MPS_MSK) >> 0) +#define PTD_MPS(v)		(((v) << 0) & PTD_MPS_MSK) +#define PTD_GET_SPD(p)		(((p)->mps & PTD_SPD_MSK) >> 10) +#define PTD_SPD(v)		(((v) << 10) & PTD_SPD_MSK) +#define PTD_GET_LAST(p)		(((p)->mps & PTD_LAST_MSK) >> 11) +#define PTD_LAST(v)		(((v) << 11) & PTD_LAST_MSK) +#define PTD_GET_EP(p)		(((p)->mps & PTD_EP_MSK) >> 12) +#define PTD_EP(v)		(((v) << 12) & PTD_EP_MSK) +#define PTD_GET_LEN(p)		(((p)->len & PTD_LEN_MSK) >> 0) +#define PTD_LEN(v)		(((v) << 0) & PTD_LEN_MSK) +#define PTD_GET_DIR(p)		(((p)->len & PTD_DIR_MSK) >> 10) +#define PTD_DIR(v)		(((v) << 10) & PTD_DIR_MSK) +#define PTD_GET_B5_5(p)		(((p)->len & PTD_B5_5_MSK) >> 13) +#define PTD_B5_5(v)		(((v) << 13) & PTD_B5_5_MSK) +#define PTD_GET_FA(p)		(((p)->faddr & PTD_FA_MSK) >> 0) +#define PTD_FA(v)		(((v) << 0) & PTD_FA_MSK) +#define PTD_GET_FMT(p)		(((p)->faddr & PTD_FMT_MSK) >> 7) +#define PTD_FMT(v)		(((v) << 7) & PTD_FMT_MSK) + +/*  Hardware transfer status codes -- CC from ptd->count */ +#define TD_CC_NOERROR      0x00 +#define TD_CC_CRC          0x01 +#define TD_CC_BITSTUFFING  0x02 +#define TD_CC_DATATOGGLEM  0x03 +#define TD_CC_STALL        0x04 +#define TD_DEVNOTRESP      0x05 +#define TD_PIDCHECKFAIL    0x06 +#define TD_UNEXPECTEDPID   0x07 +#define TD_DATAOVERRUN     0x08 +#define TD_DATAUNDERRUN    0x09 +    /* 0x0A, 0x0B reserved for hardware */ +#define TD_BUFFEROVERRUN   0x0C +#define TD_BUFFERUNDERRUN  0x0D +    /* 0x0E, 0x0F reserved for HCD */ +#define TD_NOTACCESSED     0x0F + +/* ------------------------------------------------------------------------- */ + +#define	LOG2_PERIODIC_SIZE	5	/* arbitrary; this matches OHCI */ +#define	PERIODIC_SIZE		(1 << LOG2_PERIODIC_SIZE) + +/* Philips transfer descriptor */ +struct ptd { +	u16 count; +#define	PTD_COUNT_MSK	(0x3ff << 0) +#define	PTD_TOGGLE_MSK	(1 << 10) +#define	PTD_ACTIVE_MSK	(1 << 11) +#define	PTD_CC_MSK	(0xf << 12) +	u16 mps; +#define	PTD_MPS_MSK	(0x3ff << 0) +#define	PTD_SPD_MSK	(1 << 10) +#define	PTD_LAST_MSK	(1 << 11) +#define	PTD_EP_MSK	(0xf << 12) +	u16 len; +#define	PTD_LEN_MSK	(0x3ff << 0) +#define	PTD_DIR_MSK	(3 << 10) +#define	PTD_DIR_SETUP	(0) +#define	PTD_DIR_OUT	(1) +#define	PTD_DIR_IN	(2) +#define	PTD_B5_5_MSK	(1 << 13) +	u16 faddr; +#define	PTD_FA_MSK	(0x7f << 0) +#define	PTD_FMT_MSK	(1 << 7) +} __attribute__ ((packed, aligned(2))); + +struct isp116x_ep { +	struct usb_device *udev; +	struct ptd ptd; + +	u8 maxpacket; +	u8 epnum; +	u8 nextpid; + +	u16 length;		/* of current packet */ +	unsigned char *data;	/* to databuf */ + +	u16 error_count; +}; + +/* URB struct */ +#define N_URB_TD		48 +#define URB_DEL			1 +typedef struct { +	struct isp116x_ep *ed; +	void *transfer_buffer;	/* (in) associated data buffer */ +	int actual_length;	/* (return) actual transfer length */ +	unsigned long pipe;	/* (in) pipe information */ +#if 0 +	int state; +#endif +} urb_priv_t; + +struct isp116x_platform_data { +	/* Enable internal resistors on downstream ports */ +	unsigned sel15Kres:1; +	/* On-chip overcurrent detection */ +	unsigned oc_enable:1; +	/* Enable wakeup by devices on usb bus (e.g. wakeup +	   by attachment/detachment or by device activity +	   such as moving a mouse). When chosen, this option +	   prevents stopping internal clock, increasing +	   thereby power consumption in suspended state. */ +	unsigned remote_wakeup_enable:1; +}; + +struct isp116x { +	u16 *addr_reg; +	u16 *data_reg; + +	struct isp116x_platform_data *board; + +	struct dentry *dentry; +	unsigned long stat1, stat2, stat4, stat8, stat16; + +	/* Status flags */ +	unsigned disabled:1; +	unsigned sleeping:1; + +	/* Root hub registers */ +	u32 rhdesca; +	u32 rhdescb; +	u32 rhstatus; +	u32 rhport[2]; + +	/* Schedule for the current frame */ +	struct isp116x_ep *atl_active; +	int atl_buflen; +	int atl_bufshrt; +	int atl_last_dir; +	int atl_finishing; +}; + +/* ------------------------------------------------- */ + +/* Inter-io delay (ns). The chip is picky about access timings; it + * expects at least: + * 150ns delay between consecutive accesses to DATA_REG, + * 300ns delay between access to ADDR_REG and DATA_REG + * OE, WE MUST NOT be changed during these intervals + */ +#if defined(UDELAY) +#define	isp116x_delay(h,d)	udelay(d) +#else +#define	isp116x_delay(h,d)	do {} while (0) +#endif + +static inline void isp116x_write_addr(struct isp116x *isp116x, unsigned reg) +{ +	writew(reg & 0xff, isp116x->addr_reg); +	isp116x_delay(isp116x, UDELAY); +} + +static inline void isp116x_write_data16(struct isp116x *isp116x, u16 val) +{ +	writew(val, isp116x->data_reg); +	isp116x_delay(isp116x, UDELAY); +} + +static inline void isp116x_raw_write_data16(struct isp116x *isp116x, u16 val) +{ +	__raw_writew(val, isp116x->data_reg); +	isp116x_delay(isp116x, UDELAY); +} + +static inline u16 isp116x_read_data16(struct isp116x *isp116x) +{ +	u16 val; + +	val = readw(isp116x->data_reg); +	isp116x_delay(isp116x, UDELAY); +	return val; +} + +static inline u16 isp116x_raw_read_data16(struct isp116x *isp116x) +{ +	u16 val; + +	val = __raw_readw(isp116x->data_reg); +	isp116x_delay(isp116x, UDELAY); +	return val; +} + +static inline void isp116x_write_data32(struct isp116x *isp116x, u32 val) +{ +	writew(val & 0xffff, isp116x->data_reg); +	isp116x_delay(isp116x, UDELAY); +	writew(val >> 16, isp116x->data_reg); +	isp116x_delay(isp116x, UDELAY); +} + +static inline u32 isp116x_read_data32(struct isp116x *isp116x) +{ +	u32 val; + +	val = (u32) readw(isp116x->data_reg); +	isp116x_delay(isp116x, UDELAY); +	val |= ((u32) readw(isp116x->data_reg)) << 16; +	isp116x_delay(isp116x, UDELAY); +	return val; +} + +/* Let's keep register access functions out of line. Hint: +   we wait at least 150 ns at every access. +*/ +static u16 isp116x_read_reg16(struct isp116x *isp116x, unsigned reg) +{ +	isp116x_write_addr(isp116x, reg); +	return isp116x_read_data16(isp116x); +} + +static u32 isp116x_read_reg32(struct isp116x *isp116x, unsigned reg) +{ +	isp116x_write_addr(isp116x, reg); +	return isp116x_read_data32(isp116x); +} + +static void isp116x_write_reg16(struct isp116x *isp116x, unsigned reg, +				unsigned val) +{ +	isp116x_write_addr(isp116x, reg | ISP116x_WRITE_OFFSET); +	isp116x_write_data16(isp116x, (u16) (val & 0xffff)); +} + +static void isp116x_write_reg32(struct isp116x *isp116x, unsigned reg, +				unsigned val) +{ +	isp116x_write_addr(isp116x, reg | ISP116x_WRITE_OFFSET); +	isp116x_write_data32(isp116x, (u32) val); +} + +/* --- USB HUB constants (not OHCI-specific; see hub.h) -------------------- */ + +/* destination of request */ +#define RH_INTERFACE               0x01 +#define RH_ENDPOINT                0x02 +#define RH_OTHER                   0x03 + +#define RH_CLASS                   0x20 +#define RH_VENDOR                  0x40 + +/* Requests: bRequest << 8 | bmRequestType */ +#define RH_GET_STATUS           0x0080 +#define RH_CLEAR_FEATURE        0x0100 +#define RH_SET_FEATURE          0x0300 +#define RH_SET_ADDRESS          0x0500 +#define RH_GET_DESCRIPTOR       0x0680 +#define RH_SET_DESCRIPTOR       0x0700 +#define RH_GET_CONFIGURATION    0x0880 +#define RH_SET_CONFIGURATION    0x0900 +#define RH_GET_STATE            0x0280 +#define RH_GET_INTERFACE        0x0A80 +#define RH_SET_INTERFACE        0x0B00 +#define RH_SYNC_FRAME           0x0C80 +/* Our Vendor Specific Request */ +#define RH_SET_EP               0x2000 + +/* Hub port features */ +#define RH_PORT_CONNECTION         0x00 +#define RH_PORT_ENABLE             0x01 +#define RH_PORT_SUSPEND            0x02 +#define RH_PORT_OVER_CURRENT       0x03 +#define RH_PORT_RESET              0x04 +#define RH_PORT_POWER              0x08 +#define RH_PORT_LOW_SPEED          0x09 + +#define RH_C_PORT_CONNECTION       0x10 +#define RH_C_PORT_ENABLE           0x11 +#define RH_C_PORT_SUSPEND          0x12 +#define RH_C_PORT_OVER_CURRENT     0x13 +#define RH_C_PORT_RESET            0x14 + +/* Hub features */ +#define RH_C_HUB_LOCAL_POWER       0x00 +#define RH_C_HUB_OVER_CURRENT      0x01 + +#define RH_DEVICE_REMOTE_WAKEUP    0x00 +#define RH_ENDPOINT_STALL          0x01 + +#define RH_ACK                     0x01 +#define RH_REQ_ERR                 -1 +#define RH_NACK                    0x00 diff --git a/roms/u-boot/drivers/usb/host/ohci-at91.c b/roms/u-boot/drivers/usb/host/ohci-at91.c new file mode 100644 index 00000000..c24505e7 --- /dev/null +++ b/roms/u-boot/drivers/usb/host/ohci-at91.c @@ -0,0 +1,96 @@ +/* + * (C) Copyright 2006 + * DENX Software Engineering <mk@denx.de> + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +#include <common.h> + +#if defined(CONFIG_USB_OHCI_NEW) && defined(CONFIG_SYS_USB_OHCI_CPU_INIT) + +#include <asm/io.h> +#include <asm/arch/hardware.h> +#include <asm/arch/at91_pmc.h> +#include <asm/arch/clk.h> + +int usb_cpu_init(void) +{ +	at91_pmc_t *pmc	= (at91_pmc_t *)ATMEL_BASE_PMC; + +#ifdef CONFIG_USB_ATMEL_CLK_SEL_PLLB +	/* Enable PLLB */ +	writel(get_pllb_init(), &pmc->pllbr); +	while ((readl(&pmc->sr) & AT91_PMC_LOCKB) != AT91_PMC_LOCKB) +		; +#ifdef CONFIG_AT91SAM9N12 +	writel(AT91_PMC_USBS_USB_PLLB | AT91_PMC_USB_DIV_2, &pmc->usb); +#endif +#elif defined(CONFIG_USB_ATMEL_CLK_SEL_UPLL) +	/* Enable UPLL */ +	writel(readl(&pmc->uckr) | AT91_PMC_UPLLEN | AT91_PMC_BIASEN, +		&pmc->uckr); +	while ((readl(&pmc->sr) & AT91_PMC_LOCKU) != AT91_PMC_LOCKU) +		; + +	/* Select PLLA as input clock of OHCI */ +	writel(AT91_PMC_USBS_USB_UPLL | AT91_PMC_USBDIV_10, &pmc->usb); +#endif + +	/* Enable USB host clock. */ +#ifdef CONFIG_SAMA5D3 +	writel(1 << (ATMEL_ID_UHP - 32), &pmc->pcer1); +#else +	writel(1 << ATMEL_ID_UHP, &pmc->pcer); +#endif + +#if defined(CONFIG_AT91SAM9261) || defined(CONFIG_AT91SAM9G10) +	writel(ATMEL_PMC_UHP | AT91_PMC_HCK0, &pmc->scer); +#else +	writel(ATMEL_PMC_UHP, &pmc->scer); +#endif + +	return 0; +} + +int usb_cpu_stop(void) +{ +	at91_pmc_t *pmc	= (at91_pmc_t *)ATMEL_BASE_PMC; + +	/* Disable USB host clock. */ +#ifdef CONFIG_SAMA5D3 +	writel(1 << (ATMEL_ID_UHP - 32), &pmc->pcdr1); +#else +	writel(1 << ATMEL_ID_UHP, &pmc->pcdr); +#endif + +#if defined(CONFIG_AT91SAM9261) || defined(CONFIG_AT91SAM9G10) +	writel(ATMEL_PMC_UHP | AT91_PMC_HCK0, &pmc->scdr); +#else +	writel(ATMEL_PMC_UHP, &pmc->scdr); +#endif + +#ifdef CONFIG_USB_ATMEL_CLK_SEL_PLLB +#ifdef CONFIG_AT91SAM9N12 +	writel(0, &pmc->usb); +#endif +	/* Disable PLLB */ +	writel(0, &pmc->pllbr); +	while ((readl(&pmc->sr) & AT91_PMC_LOCKB) != 0) +		; +#elif defined(CONFIG_USB_ATMEL_CLK_SEL_UPLL) +	/* Disable UPLL */ +	writel(readl(&pmc->uckr) & (~AT91_PMC_UPLLEN), &pmc->uckr); +	while ((readl(&pmc->sr) & AT91_PMC_LOCKU) == AT91_PMC_LOCKU) +		; +#endif + +	return 0; +} + +int usb_cpu_init_fail(void) +{ +	return usb_cpu_stop(); +} + +#endif /* defined(CONFIG_USB_OHCI) && defined(CONFIG_SYS_USB_OHCI_CPU_INIT) */ diff --git a/roms/u-boot/drivers/usb/host/ohci-da8xx.c b/roms/u-boot/drivers/usb/host/ohci-da8xx.c new file mode 100644 index 00000000..98166280 --- /dev/null +++ b/roms/u-boot/drivers/usb/host/ohci-da8xx.c @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2012 Sughosh Ganu <urwithsughosh@gmail.com> + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +#include <common.h> + +#include <asm/arch/da8xx-usb.h> + +int usb_cpu_init(void) +{ +	/* enable psc for usb2.0 */ +	lpsc_on(DAVINCI_LPSC_USB20); + +	/* enable psc for usb1.0 */ +	lpsc_on(DAVINCI_LPSC_USB11); + +	/* start the on-chip usb phy and its pll */ +	if (usb_phy_on()) +		return 0; + +	return 1; +} + +int usb_cpu_stop(void) +{ +	usb_phy_off(); + +	/* turn off the usb clock and assert the module reset */ +	lpsc_disable(DAVINCI_LPSC_USB11); +	lpsc_disable(DAVINCI_LPSC_USB20); + +	return 0; +} + +int usb_cpu_init_fail(void) +{ +	return usb_cpu_stop(); +} diff --git a/roms/u-boot/drivers/usb/host/ohci-hcd.c b/roms/u-boot/drivers/usb/host/ohci-hcd.c new file mode 100644 index 00000000..dc0a4e31 --- /dev/null +++ b/roms/u-boot/drivers/usb/host/ohci-hcd.c @@ -0,0 +1,1885 @@ +/* + * URB OHCI HCD (Host Controller Driver) for USB on the AT91RM9200 and PCI bus. + * + * Interrupt support is added. Now, it has been tested + * on ULI1575 chip and works well with USB keyboard. + * + * (C) Copyright 2007 + * Zhang Wei, Freescale Semiconductor, Inc. <wei.zhang@freescale.com> + * + * (C) Copyright 2003 + * Gary Jennejohn, DENX Software Engineering <garyj@denx.de> + * + * Note: Much of this code has been derived from Linux 2.4 + * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at> + * (C) Copyright 2000-2002 David Brownell + * + * Modified for the MP2USB by (C) Copyright 2005 Eric Benard + * ebenard@eukrea.com - based on s3c24x0's driver + * + * SPDX-License-Identifier:	GPL-2.0+ + */ +/* + * IMPORTANT NOTES + * 1 - Read doc/README.generic_usb_ohci + * 2 - this driver is intended for use with USB Mass Storage Devices + *     (BBB) and USB keyboard. There is NO support for Isochronous pipes! + * 2 - when running on a PQFP208 AT91RM9200, define CONFIG_AT91C_PQFP_UHPBUG + *     to activate workaround for bug #41 or this driver will NOT work! + */ + +#include <common.h> +#include <asm/byteorder.h> + +#if defined(CONFIG_PCI_OHCI) +# include <pci.h> +#if !defined(CONFIG_PCI_OHCI_DEVNO) +#define CONFIG_PCI_OHCI_DEVNO	0 +#endif +#endif + +#include <malloc.h> +#include <usb.h> + +#include "ohci.h" + +#ifdef CONFIG_AT91RM9200 +#include <asm/arch/hardware.h>	/* needed for AT91_USB_HOST_BASE */ +#endif + +#if defined(CONFIG_ARM920T) || \ +    defined(CONFIG_S3C24X0) || \ +    defined(CONFIG_440EP) || \ +    defined(CONFIG_PCI_OHCI) || \ +    defined(CONFIG_MPC5200) || \ +    defined(CONFIG_SYS_OHCI_USE_NPS) +# define OHCI_USE_NPS		/* force NoPowerSwitching mode */ +#endif + +#undef OHCI_VERBOSE_DEBUG	/* not always helpful */ +#undef DEBUG +#undef SHOW_INFO +#undef OHCI_FILL_TRACE + +/* For initializing controller (mask in an HCFS mode too) */ +#define OHCI_CONTROL_INIT \ +	(OHCI_CTRL_CBSR & 0x3) | OHCI_CTRL_IE | OHCI_CTRL_PLE + +#define min_t(type, x, y) \ +		    ({ type __x = (x); type __y = (y); __x < __y ? __x: __y; }) + +#ifdef CONFIG_PCI_OHCI +static struct pci_device_id ohci_pci_ids[] = { +	{0x10b9, 0x5237},	/* ULI1575 PCI OHCI module ids */ +	{0x1033, 0x0035},	/* NEC PCI OHCI module ids */ +	{0x1131, 0x1561},	/* Philips 1561 PCI OHCI module ids */ +	/* Please add supported PCI OHCI controller ids here */ +	{0, 0} +}; +#endif + +#ifdef CONFIG_PCI_EHCI_DEVNO +static struct pci_device_id ehci_pci_ids[] = { +	{0x1131, 0x1562},	/* Philips 1562 PCI EHCI module ids */ +	/* Please add supported PCI EHCI controller ids here */ +	{0, 0} +}; +#endif + +#ifdef DEBUG +#define dbg(format, arg...) printf("DEBUG: " format "\n", ## arg) +#else +#define dbg(format, arg...) do {} while (0) +#endif /* DEBUG */ +#define err(format, arg...) printf("ERROR: " format "\n", ## arg) +#ifdef SHOW_INFO +#define info(format, arg...) printf("INFO: " format "\n", ## arg) +#else +#define info(format, arg...) do {} while (0) +#endif + +#ifdef CONFIG_SYS_OHCI_BE_CONTROLLER +# define m16_swap(x) cpu_to_be16(x) +# define m32_swap(x) cpu_to_be32(x) +#else +# define m16_swap(x) cpu_to_le16(x) +# define m32_swap(x) cpu_to_le32(x) +#endif /* CONFIG_SYS_OHCI_BE_CONTROLLER */ + +/* global ohci_t */ +static ohci_t gohci; +/* this must be aligned to a 256 byte boundary */ +struct ohci_hcca ghcca[1]; +/* a pointer to the aligned storage */ +struct ohci_hcca *phcca; +/* this allocates EDs for all possible endpoints */ +struct ohci_device ohci_dev; +/* device which was disconnected */ +struct usb_device *devgone; + +static inline u32 roothub_a(struct ohci *hc) +	{ return ohci_readl(&hc->regs->roothub.a); } +static inline u32 roothub_b(struct ohci *hc) +	{ return ohci_readl(&hc->regs->roothub.b); } +static inline u32 roothub_status(struct ohci *hc) +	{ return ohci_readl(&hc->regs->roothub.status); } +static inline u32 roothub_portstatus(struct ohci *hc, int i) +	{ return ohci_readl(&hc->regs->roothub.portstatus[i]); } + +/* forward declaration */ +static int hc_interrupt(void); +static void td_submit_job(struct usb_device *dev, unsigned long pipe, +			  void *buffer, int transfer_len, +			  struct devrequest *setup, urb_priv_t *urb, +			  int interval); + +/*-------------------------------------------------------------------------* + * URB support functions + *-------------------------------------------------------------------------*/ + +/* free HCD-private data associated with this URB */ + +static void urb_free_priv(urb_priv_t *urb) +{ +	int		i; +	int		last; +	struct td	*td; + +	last = urb->length - 1; +	if (last >= 0) { +		for (i = 0; i <= last; i++) { +			td = urb->td[i]; +			if (td) { +				td->usb_dev = NULL; +				urb->td[i] = NULL; +			} +		} +	} +	free(urb); +} + +/*-------------------------------------------------------------------------*/ + +#ifdef DEBUG +static int sohci_get_current_frame_number(struct usb_device *dev); + +/* debug| print the main components of an URB + * small: 0) header + data packets 1) just header */ + +static void pkt_print(urb_priv_t *purb, struct usb_device *dev, +		      unsigned long pipe, void *buffer, int transfer_len, +		      struct devrequest *setup, char *str, int small) +{ +	dbg("%s URB:[%4x] dev:%2lu,ep:%2lu-%c,type:%s,len:%d/%d stat:%#lx", +			str, +			sohci_get_current_frame_number(dev), +			usb_pipedevice(pipe), +			usb_pipeendpoint(pipe), +			usb_pipeout(pipe)? 'O': 'I', +			usb_pipetype(pipe) < 2 ? \ +				(usb_pipeint(pipe)? "INTR": "ISOC"): \ +				(usb_pipecontrol(pipe)? "CTRL": "BULK"), +			(purb ? purb->actual_length : 0), +			transfer_len, dev->status); +#ifdef	OHCI_VERBOSE_DEBUG +	if (!small) { +		int i, len; + +		if (usb_pipecontrol(pipe)) { +			printf(__FILE__ ": cmd(8):"); +			for (i = 0; i < 8 ; i++) +				printf(" %02x", ((__u8 *) setup) [i]); +			printf("\n"); +		} +		if (transfer_len > 0 && buffer) { +			printf(__FILE__ ": data(%d/%d):", +				(purb ? purb->actual_length : 0), +				transfer_len); +			len = usb_pipeout(pipe)? transfer_len: +					(purb ? purb->actual_length : 0); +			for (i = 0; i < 16 && i < len; i++) +				printf(" %02x", ((__u8 *) buffer) [i]); +			printf("%s\n", i < len? "...": ""); +		} +	} +#endif +} + +/* just for debugging; prints non-empty branches of the int ed tree + * inclusive iso eds */ +void ep_print_int_eds(ohci_t *ohci, char *str) +{ +	int i, j; +	 __u32 *ed_p; +	for (i = 0; i < 32; i++) { +		j = 5; +		ed_p = &(ohci->hcca->int_table [i]); +		if (*ed_p == 0) +		    continue; +		printf(__FILE__ ": %s branch int %2d(%2x):", str, i, i); +		while (*ed_p != 0 && j--) { +			ed_t *ed = (ed_t *)m32_swap(ed_p); +			printf(" ed: %4x;", ed->hwINFO); +			ed_p = &ed->hwNextED; +		} +		printf("\n"); +	} +} + +static void ohci_dump_intr_mask(char *label, __u32 mask) +{ +	dbg("%s: 0x%08x%s%s%s%s%s%s%s%s%s", +		label, +		mask, +		(mask & OHCI_INTR_MIE) ? " MIE" : "", +		(mask & OHCI_INTR_OC) ? " OC" : "", +		(mask & OHCI_INTR_RHSC) ? " RHSC" : "", +		(mask & OHCI_INTR_FNO) ? " FNO" : "", +		(mask & OHCI_INTR_UE) ? " UE" : "", +		(mask & OHCI_INTR_RD) ? " RD" : "", +		(mask & OHCI_INTR_SF) ? " SF" : "", +		(mask & OHCI_INTR_WDH) ? " WDH" : "", +		(mask & OHCI_INTR_SO) ? " SO" : "" +		); +} + +static void maybe_print_eds(char *label, __u32 value) +{ +	ed_t *edp = (ed_t *)value; + +	if (value) { +		dbg("%s %08x", label, value); +		dbg("%08x", edp->hwINFO); +		dbg("%08x", edp->hwTailP); +		dbg("%08x", edp->hwHeadP); +		dbg("%08x", edp->hwNextED); +	} +} + +static char *hcfs2string(int state) +{ +	switch (state) { +	case OHCI_USB_RESET:	return "reset"; +	case OHCI_USB_RESUME:	return "resume"; +	case OHCI_USB_OPER:	return "operational"; +	case OHCI_USB_SUSPEND:	return "suspend"; +	} +	return "?"; +} + +/* dump control and status registers */ +static void ohci_dump_status(ohci_t *controller) +{ +	struct ohci_regs	*regs = controller->regs; +	__u32			temp; + +	temp = ohci_readl(®s->revision) & 0xff; +	if (temp != 0x10) +		dbg("spec %d.%d", (temp >> 4), (temp & 0x0f)); + +	temp = ohci_readl(®s->control); +	dbg("control: 0x%08x%s%s%s HCFS=%s%s%s%s%s CBSR=%d", temp, +		(temp & OHCI_CTRL_RWE) ? " RWE" : "", +		(temp & OHCI_CTRL_RWC) ? " RWC" : "", +		(temp & OHCI_CTRL_IR) ? " IR" : "", +		hcfs2string(temp & OHCI_CTRL_HCFS), +		(temp & OHCI_CTRL_BLE) ? " BLE" : "", +		(temp & OHCI_CTRL_CLE) ? " CLE" : "", +		(temp & OHCI_CTRL_IE) ? " IE" : "", +		(temp & OHCI_CTRL_PLE) ? " PLE" : "", +		temp & OHCI_CTRL_CBSR +		); + +	temp = ohci_readl(®s->cmdstatus); +	dbg("cmdstatus: 0x%08x SOC=%d%s%s%s%s", temp, +		(temp & OHCI_SOC) >> 16, +		(temp & OHCI_OCR) ? " OCR" : "", +		(temp & OHCI_BLF) ? " BLF" : "", +		(temp & OHCI_CLF) ? " CLF" : "", +		(temp & OHCI_HCR) ? " HCR" : "" +		); + +	ohci_dump_intr_mask("intrstatus", ohci_readl(®s->intrstatus)); +	ohci_dump_intr_mask("intrenable", ohci_readl(®s->intrenable)); + +	maybe_print_eds("ed_periodcurrent", +			ohci_readl(®s->ed_periodcurrent)); + +	maybe_print_eds("ed_controlhead", ohci_readl(®s->ed_controlhead)); +	maybe_print_eds("ed_controlcurrent", +			ohci_readl(®s->ed_controlcurrent)); + +	maybe_print_eds("ed_bulkhead", ohci_readl(®s->ed_bulkhead)); +	maybe_print_eds("ed_bulkcurrent", ohci_readl(®s->ed_bulkcurrent)); + +	maybe_print_eds("donehead", ohci_readl(®s->donehead)); +} + +static void ohci_dump_roothub(ohci_t *controller, int verbose) +{ +	__u32			temp, ndp, i; + +	temp = roothub_a(controller); +	ndp = (temp & RH_A_NDP); +#ifdef CONFIG_AT91C_PQFP_UHPBUG +	ndp = (ndp == 2) ? 1:0; +#endif +	if (verbose) { +		dbg("roothub.a: %08x POTPGT=%d%s%s%s%s%s NDP=%d", temp, +			((temp & RH_A_POTPGT) >> 24) & 0xff, +			(temp & RH_A_NOCP) ? " NOCP" : "", +			(temp & RH_A_OCPM) ? " OCPM" : "", +			(temp & RH_A_DT) ? " DT" : "", +			(temp & RH_A_NPS) ? " NPS" : "", +			(temp & RH_A_PSM) ? " PSM" : "", +			ndp +			); +		temp = roothub_b(controller); +		dbg("roothub.b: %08x PPCM=%04x DR=%04x", +			temp, +			(temp & RH_B_PPCM) >> 16, +			(temp & RH_B_DR) +			); +		temp = roothub_status(controller); +		dbg("roothub.status: %08x%s%s%s%s%s%s", +			temp, +			(temp & RH_HS_CRWE) ? " CRWE" : "", +			(temp & RH_HS_OCIC) ? " OCIC" : "", +			(temp & RH_HS_LPSC) ? " LPSC" : "", +			(temp & RH_HS_DRWE) ? " DRWE" : "", +			(temp & RH_HS_OCI) ? " OCI" : "", +			(temp & RH_HS_LPS) ? " LPS" : "" +			); +	} + +	for (i = 0; i < ndp; i++) { +		temp = roothub_portstatus(controller, i); +		dbg("roothub.portstatus [%d] = 0x%08x%s%s%s%s%s%s%s%s%s%s%s%s", +			i, +			temp, +			(temp & RH_PS_PRSC) ? " PRSC" : "", +			(temp & RH_PS_OCIC) ? " OCIC" : "", +			(temp & RH_PS_PSSC) ? " PSSC" : "", +			(temp & RH_PS_PESC) ? " PESC" : "", +			(temp & RH_PS_CSC) ? " CSC" : "", + +			(temp & RH_PS_LSDA) ? " LSDA" : "", +			(temp & RH_PS_PPS) ? " PPS" : "", +			(temp & RH_PS_PRS) ? " PRS" : "", +			(temp & RH_PS_POCI) ? " POCI" : "", +			(temp & RH_PS_PSS) ? " PSS" : "", + +			(temp & RH_PS_PES) ? " PES" : "", +			(temp & RH_PS_CCS) ? " CCS" : "" +			); +	} +} + +static void ohci_dump(ohci_t *controller, int verbose) +{ +	dbg("OHCI controller usb-%s state", controller->slot_name); + +	/* dumps some of the state we know about */ +	ohci_dump_status(controller); +	if (verbose) +		ep_print_int_eds(controller, "hcca"); +	dbg("hcca frame #%04x", controller->hcca->frame_no); +	ohci_dump_roothub(controller, 1); +} +#endif /* DEBUG */ + +/*-------------------------------------------------------------------------* + * Interface functions (URB) + *-------------------------------------------------------------------------*/ + +/* get a transfer request */ + +int sohci_submit_job(urb_priv_t *urb, struct devrequest *setup) +{ +	ohci_t *ohci; +	ed_t *ed; +	urb_priv_t *purb_priv = urb; +	int i, size = 0; +	struct usb_device *dev = urb->dev; +	unsigned long pipe = urb->pipe; +	void *buffer = urb->transfer_buffer; +	int transfer_len = urb->transfer_buffer_length; +	int interval = urb->interval; + +	ohci = &gohci; + +	/* when controller's hung, permit only roothub cleanup attempts +	 * such as powering down ports */ +	if (ohci->disabled) { +		err("sohci_submit_job: EPIPE"); +		return -1; +	} + +	/* we're about to begin a new transaction here so mark the +	 * URB unfinished */ +	urb->finished = 0; + +	/* every endpoint has a ed, locate and fill it */ +	ed = ep_add_ed(dev, pipe, interval, 1); +	if (!ed) { +		err("sohci_submit_job: ENOMEM"); +		return -1; +	} + +	/* for the private part of the URB we need the number of TDs (size) */ +	switch (usb_pipetype(pipe)) { +	case PIPE_BULK: /* one TD for every 4096 Byte */ +		size = (transfer_len - 1) / 4096 + 1; +		break; +	case PIPE_CONTROL:/* 1 TD for setup, 1 for ACK and 1 for every 4096 B */ +		size = (transfer_len == 0)? 2: +					(transfer_len - 1) / 4096 + 3; +		break; +	case PIPE_INTERRUPT: /* 1 TD */ +		size = 1; +		break; +	} + +	ed->purb = urb; + +	if (size >= (N_URB_TD - 1)) { +		err("need %d TDs, only have %d", size, N_URB_TD); +		return -1; +	} +	purb_priv->pipe = pipe; + +	/* fill the private part of the URB */ +	purb_priv->length = size; +	purb_priv->ed = ed; +	purb_priv->actual_length = 0; + +	/* allocate the TDs */ +	/* note that td[0] was allocated in ep_add_ed */ +	for (i = 0; i < size; i++) { +		purb_priv->td[i] = td_alloc(dev); +		if (!purb_priv->td[i]) { +			purb_priv->length = i; +			urb_free_priv(purb_priv); +			err("sohci_submit_job: ENOMEM"); +			return -1; +		} +	} + +	if (ed->state == ED_NEW || (ed->state & ED_DEL)) { +		urb_free_priv(purb_priv); +		err("sohci_submit_job: EINVAL"); +		return -1; +	} + +	/* link the ed into a chain if is not already */ +	if (ed->state != ED_OPER) +		ep_link(ohci, ed); + +	/* fill the TDs and link it to the ed */ +	td_submit_job(dev, pipe, buffer, transfer_len, +		      setup, purb_priv, interval); + +	return 0; +} + +static inline int sohci_return_job(struct ohci *hc, urb_priv_t *urb) +{ +	struct ohci_regs *regs = hc->regs; + +	switch (usb_pipetype(urb->pipe)) { +	case PIPE_INTERRUPT: +		/* implicitly requeued */ +		if (urb->dev->irq_handle && +				(urb->dev->irq_act_len = urb->actual_length)) { +			ohci_writel(OHCI_INTR_WDH, ®s->intrenable); +			ohci_readl(®s->intrenable); /* PCI posting flush */ +			urb->dev->irq_handle(urb->dev); +			ohci_writel(OHCI_INTR_WDH, ®s->intrdisable); +			ohci_readl(®s->intrdisable); /* PCI posting flush */ +		} +		urb->actual_length = 0; +		td_submit_job( +				urb->dev, +				urb->pipe, +				urb->transfer_buffer, +				urb->transfer_buffer_length, +				NULL, +				urb, +				urb->interval); +		break; +	case PIPE_CONTROL: +	case PIPE_BULK: +		break; +	default: +		return 0; +	} +	return 1; +} + +/*-------------------------------------------------------------------------*/ + +#ifdef DEBUG +/* tell us the current USB frame number */ + +static int sohci_get_current_frame_number(struct usb_device *usb_dev) +{ +	ohci_t *ohci = &gohci; + +	return m16_swap(ohci->hcca->frame_no); +} +#endif + +/*-------------------------------------------------------------------------* + * ED handling functions + *-------------------------------------------------------------------------*/ + +/* search for the right branch to insert an interrupt ed into the int tree + * do some load ballancing; + * returns the branch and + * sets the interval to interval = 2^integer (ld (interval)) */ + +static int ep_int_ballance(ohci_t *ohci, int interval, int load) +{ +	int i, branch = 0; + +	/* search for the least loaded interrupt endpoint +	 * branch of all 32 branches +	 */ +	for (i = 0; i < 32; i++) +		if (ohci->ohci_int_load [branch] > ohci->ohci_int_load [i]) +			branch = i; + +	branch = branch % interval; +	for (i = branch; i < 32; i += interval) +		ohci->ohci_int_load [i] += load; + +	return branch; +} + +/*-------------------------------------------------------------------------*/ + +/*  2^int( ld (inter)) */ + +static int ep_2_n_interval(int inter) +{ +	int i; +	for (i = 0; ((inter >> i) > 1) && (i < 5); i++); +	return 1 << i; +} + +/*-------------------------------------------------------------------------*/ + +/* the int tree is a binary tree + * in order to process it sequentially the indexes of the branches have to + * be mapped the mapping reverses the bits of a word of num_bits length */ +static int ep_rev(int num_bits, int word) +{ +	int i, wout = 0; + +	for (i = 0; i < num_bits; i++) +		wout |= (((word >> i) & 1) << (num_bits - i - 1)); +	return wout; +} + +/*-------------------------------------------------------------------------* + * ED handling functions + *-------------------------------------------------------------------------*/ + +/* link an ed into one of the HC chains */ + +static int ep_link(ohci_t *ohci, ed_t *edi) +{ +	volatile ed_t *ed = edi; +	int int_branch; +	int i; +	int inter; +	int interval; +	int load; +	__u32 *ed_p; + +	ed->state = ED_OPER; +	ed->int_interval = 0; + +	switch (ed->type) { +	case PIPE_CONTROL: +		ed->hwNextED = 0; +		if (ohci->ed_controltail == NULL) +			ohci_writel(ed, &ohci->regs->ed_controlhead); +		else +			ohci->ed_controltail->hwNextED = +						   m32_swap((unsigned long)ed); + +		ed->ed_prev = ohci->ed_controltail; +		if (!ohci->ed_controltail && !ohci->ed_rm_list[0] && +			!ohci->ed_rm_list[1] && !ohci->sleeping) { +			ohci->hc_control |= OHCI_CTRL_CLE; +			ohci_writel(ohci->hc_control, &ohci->regs->control); +		} +		ohci->ed_controltail = edi; +		break; + +	case PIPE_BULK: +		ed->hwNextED = 0; +		if (ohci->ed_bulktail == NULL) +			ohci_writel(ed, &ohci->regs->ed_bulkhead); +		else +			ohci->ed_bulktail->hwNextED = +						   m32_swap((unsigned long)ed); + +		ed->ed_prev = ohci->ed_bulktail; +		if (!ohci->ed_bulktail && !ohci->ed_rm_list[0] && +			!ohci->ed_rm_list[1] && !ohci->sleeping) { +			ohci->hc_control |= OHCI_CTRL_BLE; +			ohci_writel(ohci->hc_control, &ohci->regs->control); +		} +		ohci->ed_bulktail = edi; +		break; + +	case PIPE_INTERRUPT: +		load = ed->int_load; +		interval = ep_2_n_interval(ed->int_period); +		ed->int_interval = interval; +		int_branch = ep_int_ballance(ohci, interval, load); +		ed->int_branch = int_branch; + +		for (i = 0; i < ep_rev(6, interval); i += inter) { +			inter = 1; +			for (ed_p = &(ohci->hcca->int_table[\ +						ep_rev(5, i) + int_branch]); +				(*ed_p != 0) && +				(((ed_t *)ed_p)->int_interval >= interval); +				ed_p = &(((ed_t *)ed_p)->hwNextED)) +					inter = ep_rev(6, +						 ((ed_t *)ed_p)->int_interval); +			ed->hwNextED = *ed_p; +			*ed_p = m32_swap((unsigned long)ed); +		} +		break; +	} +	return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* scan the periodic table to find and unlink this ED */ +static void periodic_unlink(struct ohci *ohci, volatile struct ed *ed, +			    unsigned index, unsigned period) +{ +	for (; index < NUM_INTS; index += period) { +		__u32	*ed_p = &ohci->hcca->int_table [index]; + +		/* ED might have been unlinked through another path */ +		while (*ed_p != 0) { +			if (((struct ed *) +					m32_swap((unsigned long)ed_p)) == ed) { +				*ed_p = ed->hwNextED; +				break; +			} +			ed_p = &(((struct ed *) +				     m32_swap((unsigned long)ed_p))->hwNextED); +		} +	} +} + +/* unlink an ed from one of the HC chains. + * just the link to the ed is unlinked. + * the link from the ed still points to another operational ed or 0 + * so the HC can eventually finish the processing of the unlinked ed */ + +static int ep_unlink(ohci_t *ohci, ed_t *edi) +{ +	volatile ed_t *ed = edi; +	int i; + +	ed->hwINFO |= m32_swap(OHCI_ED_SKIP); + +	switch (ed->type) { +	case PIPE_CONTROL: +		if (ed->ed_prev == NULL) { +			if (!ed->hwNextED) { +				ohci->hc_control &= ~OHCI_CTRL_CLE; +				ohci_writel(ohci->hc_control, +					    &ohci->regs->control); +			} +			ohci_writel(m32_swap(*((__u32 *)&ed->hwNextED)), +				&ohci->regs->ed_controlhead); +		} else { +			ed->ed_prev->hwNextED = ed->hwNextED; +		} +		if (ohci->ed_controltail == ed) { +			ohci->ed_controltail = ed->ed_prev; +		} else { +			((ed_t *)m32_swap( +			    *((__u32 *)&ed->hwNextED)))->ed_prev = ed->ed_prev; +		} +		break; + +	case PIPE_BULK: +		if (ed->ed_prev == NULL) { +			if (!ed->hwNextED) { +				ohci->hc_control &= ~OHCI_CTRL_BLE; +				ohci_writel(ohci->hc_control, +					    &ohci->regs->control); +			} +			ohci_writel(m32_swap(*((__u32 *)&ed->hwNextED)), +			       &ohci->regs->ed_bulkhead); +		} else { +			ed->ed_prev->hwNextED = ed->hwNextED; +		} +		if (ohci->ed_bulktail == ed) { +			ohci->ed_bulktail = ed->ed_prev; +		} else { +			((ed_t *)m32_swap( +			     *((__u32 *)&ed->hwNextED)))->ed_prev = ed->ed_prev; +		} +		break; + +	case PIPE_INTERRUPT: +		periodic_unlink(ohci, ed, 0, 1); +		for (i = ed->int_branch; i < 32; i += ed->int_interval) +		    ohci->ohci_int_load[i] -= ed->int_load; +		break; +	} +	ed->state = ED_UNLINK; +	return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* add/reinit an endpoint; this should be done once at the + * usb_set_configuration command, but the USB stack is a little bit + * stateless so we do it at every transaction if the state of the ed + * is ED_NEW then a dummy td is added and the state is changed to + * ED_UNLINK in all other cases the state is left unchanged the ed + * info fields are setted anyway even though most of them should not + * change + */ +static ed_t *ep_add_ed(struct usb_device *usb_dev, unsigned long pipe, +			int interval, int load) +{ +	td_t *td; +	ed_t *ed_ret; +	volatile ed_t *ed; + +	ed = ed_ret = &ohci_dev.ed[(usb_pipeendpoint(pipe) << 1) | +			(usb_pipecontrol(pipe)? 0: usb_pipeout(pipe))]; + +	if ((ed->state & ED_DEL) || (ed->state & ED_URB_DEL)) { +		err("ep_add_ed: pending delete"); +		/* pending delete request */ +		return NULL; +	} + +	if (ed->state == ED_NEW) { +		/* dummy td; end of td list for ed */ +		td = td_alloc(usb_dev); +		ed->hwTailP = m32_swap((unsigned long)td); +		ed->hwHeadP = ed->hwTailP; +		ed->state = ED_UNLINK; +		ed->type = usb_pipetype(pipe); +		ohci_dev.ed_cnt++; +	} + +	ed->hwINFO = m32_swap(usb_pipedevice(pipe) +			| usb_pipeendpoint(pipe) << 7 +			| (usb_pipeisoc(pipe)? 0x8000: 0) +			| (usb_pipecontrol(pipe)? 0: \ +					   (usb_pipeout(pipe)? 0x800: 0x1000)) +			| (usb_dev->speed == USB_SPEED_LOW) << 13 +			| usb_maxpacket(usb_dev, pipe) << 16); + +	if (ed->type == PIPE_INTERRUPT && ed->state == ED_UNLINK) { +		ed->int_period = interval; +		ed->int_load = load; +	} + +	return ed_ret; +} + +/*-------------------------------------------------------------------------* + * TD handling functions + *-------------------------------------------------------------------------*/ + +/* enqueue next TD for this URB (OHCI spec 5.2.8.2) */ + +static void td_fill(ohci_t *ohci, unsigned int info, +	void *data, int len, +	struct usb_device *dev, int index, urb_priv_t *urb_priv) +{ +	volatile td_t  *td, *td_pt; +#ifdef OHCI_FILL_TRACE +	int i; +#endif + +	if (index > urb_priv->length) { +		err("index > length"); +		return; +	} +	/* use this td as the next dummy */ +	td_pt = urb_priv->td [index]; +	td_pt->hwNextTD = 0; + +	/* fill the old dummy TD */ +	td = urb_priv->td [index] = +			     (td_t *)(m32_swap(urb_priv->ed->hwTailP) & ~0xf); + +	td->ed = urb_priv->ed; +	td->next_dl_td = NULL; +	td->index = index; +	td->data = (__u32)data; +#ifdef OHCI_FILL_TRACE +	if (usb_pipebulk(urb_priv->pipe) && usb_pipeout(urb_priv->pipe)) { +		for (i = 0; i < len; i++) +		printf("td->data[%d] %#2x ", i, ((unsigned char *)td->data)[i]); +		printf("\n"); +	} +#endif +	if (!len) +		data = 0; + +	td->hwINFO = m32_swap(info); +	td->hwCBP = m32_swap((unsigned long)data); +	if (data) +		td->hwBE = m32_swap((unsigned long)(data + len - 1)); +	else +		td->hwBE = 0; + +	td->hwNextTD = m32_swap((unsigned long)td_pt); + +	/* append to queue */ +	td->ed->hwTailP = td->hwNextTD; +} + +/*-------------------------------------------------------------------------*/ + +/* prepare all TDs of a transfer */ + +static void td_submit_job(struct usb_device *dev, unsigned long pipe, +			  void *buffer, int transfer_len, +			  struct devrequest *setup, urb_priv_t *urb, +			  int interval) +{ +	ohci_t *ohci = &gohci; +	int data_len = transfer_len; +	void *data; +	int cnt = 0; +	__u32 info = 0; +	unsigned int toggle = 0; + +	/* OHCI handles the DATA-toggles itself, we just use the USB-toggle +	 * bits for reseting */ +	if (usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe))) { +		toggle = TD_T_TOGGLE; +	} else { +		toggle = TD_T_DATA0; +		usb_settoggle(dev, usb_pipeendpoint(pipe), +				usb_pipeout(pipe), 1); +	} +	urb->td_cnt = 0; +	if (data_len) +		data = buffer; +	else +		data = 0; + +	switch (usb_pipetype(pipe)) { +	case PIPE_BULK: +		info = usb_pipeout(pipe)? +			TD_CC | TD_DP_OUT : TD_CC | TD_DP_IN ; +		while (data_len > 4096) { +			td_fill(ohci, info | (cnt? TD_T_TOGGLE:toggle), +				data, 4096, dev, cnt, urb); +			data += 4096; data_len -= 4096; cnt++; +		} +		info = usb_pipeout(pipe)? +			TD_CC | TD_DP_OUT : TD_CC | TD_R | TD_DP_IN ; +		td_fill(ohci, info | (cnt? TD_T_TOGGLE:toggle), data, +			data_len, dev, cnt, urb); +		cnt++; + +		if (!ohci->sleeping) { +			/* start bulk list */ +			ohci_writel(OHCI_BLF, &ohci->regs->cmdstatus); +		} +		break; + +	case PIPE_CONTROL: +		/* Setup phase */ +		info = TD_CC | TD_DP_SETUP | TD_T_DATA0; +		td_fill(ohci, info, setup, 8, dev, cnt++, urb); + +		/* Optional Data phase */ +		if (data_len > 0) { +			info = usb_pipeout(pipe)? +				TD_CC | TD_R | TD_DP_OUT | TD_T_DATA1 : +				TD_CC | TD_R | TD_DP_IN | TD_T_DATA1; +			/* NOTE:  mishandles transfers >8K, some >4K */ +			td_fill(ohci, info, data, data_len, dev, cnt++, urb); +		} + +		/* Status phase */ +		info = usb_pipeout(pipe)? +			TD_CC | TD_DP_IN | TD_T_DATA1: +			TD_CC | TD_DP_OUT | TD_T_DATA1; +		td_fill(ohci, info, data, 0, dev, cnt++, urb); + +		if (!ohci->sleeping) { +			/* start Control list */ +			ohci_writel(OHCI_CLF, &ohci->regs->cmdstatus); +		} +		break; + +	case PIPE_INTERRUPT: +		info = usb_pipeout(urb->pipe)? +			TD_CC | TD_DP_OUT | toggle: +			TD_CC | TD_R | TD_DP_IN | toggle; +		td_fill(ohci, info, data, data_len, dev, cnt++, urb); +		break; +	} +	if (urb->length != cnt) +		dbg("TD LENGTH %d != CNT %d", urb->length, cnt); +} + +/*-------------------------------------------------------------------------* + * Done List handling functions + *-------------------------------------------------------------------------*/ + +/* calculate the transfer length and update the urb */ + +static void dl_transfer_length(td_t *td) +{ +	__u32 tdBE, tdCBP; +	urb_priv_t *lurb_priv = td->ed->purb; + +	tdBE   = m32_swap(td->hwBE); +	tdCBP  = m32_swap(td->hwCBP); + +	if (!(usb_pipecontrol(lurb_priv->pipe) && +	    ((td->index == 0) || (td->index == lurb_priv->length - 1)))) { +		if (tdBE != 0) { +			if (td->hwCBP == 0) +				lurb_priv->actual_length += tdBE - td->data + 1; +			else +				lurb_priv->actual_length += tdCBP - td->data; +		} +	} +} + +/*-------------------------------------------------------------------------*/ +static void check_status(td_t *td_list) +{ +	urb_priv_t *lurb_priv = td_list->ed->purb; +	int	   urb_len    = lurb_priv->length; +	__u32      *phwHeadP  = &td_list->ed->hwHeadP; +	int	   cc; + +	cc = TD_CC_GET(m32_swap(td_list->hwINFO)); +	if (cc) { +		err(" USB-error: %s (%x)", cc_to_string[cc], cc); + +		if (*phwHeadP & m32_swap(0x1)) { +			if (lurb_priv && +			    ((td_list->index + 1) < urb_len)) { +				*phwHeadP = +					(lurb_priv->td[urb_len - 1]->hwNextTD &\ +							m32_swap(0xfffffff0)) | +						   (*phwHeadP & m32_swap(0x2)); + +				lurb_priv->td_cnt += urb_len - +						     td_list->index - 1; +			} else +				*phwHeadP &= m32_swap(0xfffffff2); +		} +#ifdef CONFIG_MPC5200 +		td_list->hwNextTD = 0; +#endif +	} +} + +/* replies to the request have to be on a FIFO basis so + * we reverse the reversed done-list */ +static td_t *dl_reverse_done_list(ohci_t *ohci) +{ +	__u32 td_list_hc; +	td_t *td_rev = NULL; +	td_t *td_list = NULL; + +	td_list_hc = m32_swap(ohci->hcca->done_head) & 0xfffffff0; +	ohci->hcca->done_head = 0; + +	while (td_list_hc) { +		td_list = (td_t *)td_list_hc; +		check_status(td_list); +		td_list->next_dl_td = td_rev; +		td_rev = td_list; +		td_list_hc = m32_swap(td_list->hwNextTD) & 0xfffffff0; +	} +	return td_list; +} + +/*-------------------------------------------------------------------------*/ +/*-------------------------------------------------------------------------*/ + +static void finish_urb(ohci_t *ohci, urb_priv_t *urb, int status) +{ +	if ((status & (ED_OPER | ED_UNLINK)) && (urb->state != URB_DEL)) +		urb->finished = sohci_return_job(ohci, urb); +	else +		dbg("finish_urb: strange.., ED state %x, \n", status); +} + +/* + * Used to take back a TD from the host controller. This would normally be + * called from within dl_done_list, however it may be called directly if the + * HC no longer sees the TD and it has not appeared on the donelist (after + * two frames).  This bug has been observed on ZF Micro systems. + */ +static int takeback_td(ohci_t *ohci, td_t *td_list) +{ +	ed_t *ed; +	int cc; +	int stat = 0; +	/* urb_t *urb; */ +	urb_priv_t *lurb_priv; +	__u32 tdINFO, edHeadP, edTailP; + +	tdINFO = m32_swap(td_list->hwINFO); + +	ed = td_list->ed; +	lurb_priv = ed->purb; + +	dl_transfer_length(td_list); + +	lurb_priv->td_cnt++; + +	/* error code of transfer */ +	cc = TD_CC_GET(tdINFO); +	if (cc) { +		err("USB-error: %s (%x)", cc_to_string[cc], cc); +		stat = cc_to_error[cc]; +	} + +	/* see if this done list makes for all TD's of current URB, +	* and mark the URB finished if so */ +	if (lurb_priv->td_cnt == lurb_priv->length) +		finish_urb(ohci, lurb_priv, ed->state); + +	dbg("dl_done_list: processing TD %x, len %x\n", +		lurb_priv->td_cnt, lurb_priv->length); + +	if (ed->state != ED_NEW && (!usb_pipeint(lurb_priv->pipe))) { +		edHeadP = m32_swap(ed->hwHeadP) & 0xfffffff0; +		edTailP = m32_swap(ed->hwTailP); + +		/* unlink eds if they are not busy */ +		if ((edHeadP == edTailP) && (ed->state == ED_OPER)) +			ep_unlink(ohci, ed); +	} +	return stat; +} + +static int dl_done_list(ohci_t *ohci) +{ +	int stat = 0; +	td_t	*td_list = dl_reverse_done_list(ohci); + +	while (td_list) { +		td_t	*td_next = td_list->next_dl_td; +		stat = takeback_td(ohci, td_list); +		td_list = td_next; +	} +	return stat; +} + +/*-------------------------------------------------------------------------* + * Virtual Root Hub + *-------------------------------------------------------------------------*/ + +#include <usbroothubdes.h> + +/* Hub class-specific descriptor is constructed dynamically */ + +/*-------------------------------------------------------------------------*/ + +#define OK(x)			len = (x); break +#ifdef DEBUG +#define WR_RH_STAT(x)		{info("WR:status %#8x", (x)); ohci_writel((x), \ +						&gohci.regs->roothub.status); } +#define WR_RH_PORTSTAT(x)	{info("WR:portstatus[%d] %#8x", wIndex-1, \ +	(x)); ohci_writel((x), &gohci.regs->roothub.portstatus[wIndex-1]); } +#else +#define WR_RH_STAT(x)		ohci_writel((x), &gohci.regs->roothub.status) +#define WR_RH_PORTSTAT(x)	ohci_writel((x), \ +				    &gohci.regs->roothub.portstatus[wIndex-1]) +#endif +#define RD_RH_STAT		roothub_status(&gohci) +#define RD_RH_PORTSTAT		roothub_portstatus(&gohci, wIndex-1) + +/* request to virtual root hub */ + +int rh_check_port_status(ohci_t *controller) +{ +	__u32 temp, ndp, i; +	int res; + +	res = -1; +	temp = roothub_a(controller); +	ndp = (temp & RH_A_NDP); +#ifdef CONFIG_AT91C_PQFP_UHPBUG +	ndp = (ndp == 2) ? 1:0; +#endif +	for (i = 0; i < ndp; i++) { +		temp = roothub_portstatus(controller, i); +		/* check for a device disconnect */ +		if (((temp & (RH_PS_PESC | RH_PS_CSC)) == +			(RH_PS_PESC | RH_PS_CSC)) && +			((temp & RH_PS_CCS) == 0)) { +			res = i; +			break; +		} +	} +	return res; +} + +static int ohci_submit_rh_msg(struct usb_device *dev, unsigned long pipe, +		void *buffer, int transfer_len, struct devrequest *cmd) +{ +	void *data = buffer; +	int leni = transfer_len; +	int len = 0; +	int stat = 0; +	__u16 bmRType_bReq; +	__u16 wValue; +	__u16 wIndex; +	__u16 wLength; +	ALLOC_ALIGN_BUFFER(__u8, databuf, 16, sizeof(u32)); + +#ifdef DEBUG +pkt_print(NULL, dev, pipe, buffer, transfer_len, +	  cmd, "SUB(rh)", usb_pipein(pipe)); +#else +	mdelay(1); +#endif +	if (usb_pipeint(pipe)) { +		info("Root-Hub submit IRQ: NOT implemented"); +		return 0; +	} + +	bmRType_bReq  = cmd->requesttype | (cmd->request << 8); +	wValue	      = le16_to_cpu(cmd->value); +	wIndex	      = le16_to_cpu(cmd->index); +	wLength	      = le16_to_cpu(cmd->length); + +	info("Root-Hub: adr: %2x cmd(%1x): %08x %04x %04x %04x", +		dev->devnum, 8, bmRType_bReq, wValue, wIndex, wLength); + +	switch (bmRType_bReq) { +	/* Request Destination: +	   without flags: Device, +	   RH_INTERFACE: interface, +	   RH_ENDPOINT: endpoint, +	   RH_CLASS means HUB here, +	   RH_OTHER | RH_CLASS	almost ever means HUB_PORT here +	*/ + +	case RH_GET_STATUS: +		*(u16 *)databuf = cpu_to_le16(1); +		OK(2); +	case RH_GET_STATUS | RH_INTERFACE: +		*(u16 *)databuf = cpu_to_le16(0); +		OK(2); +	case RH_GET_STATUS | RH_ENDPOINT: +		*(u16 *)databuf = cpu_to_le16(0); +		OK(2); +	case RH_GET_STATUS | RH_CLASS: +		*(u32 *)databuf = cpu_to_le32( +				RD_RH_STAT & ~(RH_HS_CRWE | RH_HS_DRWE)); +		OK(4); +	case RH_GET_STATUS | RH_OTHER | RH_CLASS: +		*(u32 *)databuf = cpu_to_le32(RD_RH_PORTSTAT); +		OK(4); + +	case RH_CLEAR_FEATURE | RH_ENDPOINT: +		switch (wValue) { +		case (RH_ENDPOINT_STALL): +			OK(0); +		} +		break; + +	case RH_CLEAR_FEATURE | RH_CLASS: +		switch (wValue) { +		case RH_C_HUB_LOCAL_POWER: +			OK(0); +		case (RH_C_HUB_OVER_CURRENT): +			WR_RH_STAT(RH_HS_OCIC); +			OK(0); +		} +		break; + +	case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS: +		switch (wValue) { +		case (RH_PORT_ENABLE):        WR_RH_PORTSTAT(RH_PS_CCS);  OK(0); +		case (RH_PORT_SUSPEND):       WR_RH_PORTSTAT(RH_PS_POCI); OK(0); +		case (RH_PORT_POWER):         WR_RH_PORTSTAT(RH_PS_LSDA); OK(0); +		case (RH_C_PORT_CONNECTION):  WR_RH_PORTSTAT(RH_PS_CSC);  OK(0); +		case (RH_C_PORT_ENABLE):      WR_RH_PORTSTAT(RH_PS_PESC); OK(0); +		case (RH_C_PORT_SUSPEND):     WR_RH_PORTSTAT(RH_PS_PSSC); OK(0); +		case (RH_C_PORT_OVER_CURRENT):WR_RH_PORTSTAT(RH_PS_OCIC); OK(0); +		case (RH_C_PORT_RESET):       WR_RH_PORTSTAT(RH_PS_PRSC); OK(0); +		} +		break; + +	case RH_SET_FEATURE | RH_OTHER | RH_CLASS: +		switch (wValue) { +		case (RH_PORT_SUSPEND): +			WR_RH_PORTSTAT(RH_PS_PSS);  OK(0); +		case (RH_PORT_RESET): /* BUG IN HUP CODE *********/ +			if (RD_RH_PORTSTAT & RH_PS_CCS) +				WR_RH_PORTSTAT(RH_PS_PRS); +			OK(0); +		case (RH_PORT_POWER): +			WR_RH_PORTSTAT(RH_PS_PPS); +			mdelay(100); +			OK(0); +		case (RH_PORT_ENABLE): /* BUG IN HUP CODE *********/ +			if (RD_RH_PORTSTAT & RH_PS_CCS) +				WR_RH_PORTSTAT(RH_PS_PES); +			OK(0); +		} +		break; + +	case RH_SET_ADDRESS: +		gohci.rh.devnum = wValue; +		OK(0); + +	case RH_GET_DESCRIPTOR: +		switch ((wValue & 0xff00) >> 8) { +		case (0x01): /* device descriptor */ +			len = min_t(unsigned int, +					leni, +					min_t(unsigned int, +					sizeof(root_hub_dev_des), +					wLength)); +			databuf = root_hub_dev_des; OK(len); +		case (0x02): /* configuration descriptor */ +			len = min_t(unsigned int, +					leni, +					min_t(unsigned int, +					sizeof(root_hub_config_des), +					wLength)); +			databuf = root_hub_config_des; OK(len); +		case (0x03): /* string descriptors */ +			if (wValue == 0x0300) { +				len = min_t(unsigned int, +						leni, +						min_t(unsigned int, +						sizeof(root_hub_str_index0), +						wLength)); +				databuf = root_hub_str_index0; +				OK(len); +			} +			if (wValue == 0x0301) { +				len = min_t(unsigned int, +						leni, +						min_t(unsigned int, +						sizeof(root_hub_str_index1), +						wLength)); +				databuf = root_hub_str_index1; +				OK(len); +		} +		default: +			stat = USB_ST_STALLED; +		} +		break; + +	case RH_GET_DESCRIPTOR | RH_CLASS: +	{ +		__u32 temp = roothub_a(&gohci); + +		databuf[0] = 9;		/* min length; */ +		databuf[1] = 0x29; +		databuf[2] = temp & RH_A_NDP; +#ifdef CONFIG_AT91C_PQFP_UHPBUG +		databuf[2] = (databuf[2] == 2) ? 1 : 0; +#endif +		databuf[3] = 0; +		if (temp & RH_A_PSM)	/* per-port power switching? */ +			databuf[3] |= 0x1; +		if (temp & RH_A_NOCP)	/* no overcurrent reporting? */ +			databuf[3] |= 0x10; +		else if (temp & RH_A_OCPM)/* per-port overcurrent reporting? */ +			databuf[3] |= 0x8; + +		databuf[4] = 0; +		databuf[5] = (temp & RH_A_POTPGT) >> 24; +		databuf[6] = 0; +		temp = roothub_b(&gohci); +		databuf[7] = temp & RH_B_DR; +		if (databuf[2] < 7) { +			databuf[8] = 0xff; +		} else { +			databuf[0] += 2; +			databuf[8] = (temp & RH_B_DR) >> 8; +			databuf[10] = databuf[9] = 0xff; +		} + +		len = min_t(unsigned int, leni, +			    min_t(unsigned int, databuf[0], wLength)); +		OK(len); +	} + +	case RH_GET_CONFIGURATION: +		databuf[0] = 0x01; +		OK(1); + +	case RH_SET_CONFIGURATION: +		WR_RH_STAT(0x10000); +		OK(0); + +	default: +		dbg("unsupported root hub command"); +		stat = USB_ST_STALLED; +	} + +#ifdef	DEBUG +	ohci_dump_roothub(&gohci, 1); +#else +	mdelay(1); +#endif + +	len = min_t(int, len, leni); +	if (data != databuf) +		memcpy(data, databuf, len); +	dev->act_len = len; +	dev->status = stat; + +#ifdef DEBUG +	pkt_print(NULL, dev, pipe, buffer, +		  transfer_len, cmd, "RET(rh)", 0/*usb_pipein(pipe)*/); +#else +	mdelay(1); +#endif + +	return stat; +} + +/*-------------------------------------------------------------------------*/ + +/* common code for handling submit messages - used for all but root hub */ +/* accesses. */ +int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *buffer, +		int transfer_len, struct devrequest *setup, int interval) +{ +	int stat = 0; +	int maxsize = usb_maxpacket(dev, pipe); +	int timeout; +	urb_priv_t *urb; + +	urb = malloc(sizeof(urb_priv_t)); +	memset(urb, 0, sizeof(urb_priv_t)); + +	urb->dev = dev; +	urb->pipe = pipe; +	urb->transfer_buffer = buffer; +	urb->transfer_buffer_length = transfer_len; +	urb->interval = interval; + +	/* device pulled? Shortcut the action. */ +	if (devgone == dev) { +		dev->status = USB_ST_CRC_ERR; +		return 0; +	} + +#ifdef DEBUG +	urb->actual_length = 0; +	pkt_print(urb, dev, pipe, buffer, transfer_len, +		  setup, "SUB", usb_pipein(pipe)); +#else +	mdelay(1); +#endif +	if (!maxsize) { +		err("submit_common_message: pipesize for pipe %lx is zero", +			pipe); +		return -1; +	} + +	if (sohci_submit_job(urb, setup) < 0) { +		err("sohci_submit_job failed"); +		return -1; +	} + +#if 0 +	mdelay(10); +	/* ohci_dump_status(&gohci); */ +#endif + +	timeout = USB_TIMEOUT_MS(pipe); + +	/* wait for it to complete */ +	for (;;) { +		/* check whether the controller is done */ +		stat = hc_interrupt(); +		if (stat < 0) { +			stat = USB_ST_CRC_ERR; +			break; +		} + +		/* NOTE: since we are not interrupt driven in U-Boot and always +		 * handle only one URB at a time, we cannot assume the +		 * transaction finished on the first successful return from +		 * hc_interrupt().. unless the flag for current URB is set, +		 * meaning that all TD's to/from device got actually +		 * transferred and processed. If the current URB is not +		 * finished we need to re-iterate this loop so as +		 * hc_interrupt() gets called again as there needs to be some +		 * more TD's to process still */ +		if ((stat >= 0) && (stat != 0xff) && (urb->finished)) { +			/* 0xff is returned for an SF-interrupt */ +			break; +		} + +		if (--timeout) { +			mdelay(1); +			if (!urb->finished) +				dbg("*"); + +		} else { +			err("CTL:TIMEOUT "); +			dbg("submit_common_msg: TO status %x\n", stat); +			urb->finished = 1; +			stat = USB_ST_CRC_ERR; +			break; +		} +	} + +	dev->status = stat; +	dev->act_len = urb->actual_length; + +#ifdef DEBUG +	pkt_print(urb, dev, pipe, buffer, transfer_len, +		  setup, "RET(ctlr)", usb_pipein(pipe)); +#else +	mdelay(1); +#endif + +	/* free TDs in urb_priv */ +	if (!usb_pipeint(pipe)) +		urb_free_priv(urb); +	return 0; +} + +/* submit routines called from usb.c */ +int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer, +		int transfer_len) +{ +	info("submit_bulk_msg"); +	return submit_common_msg(dev, pipe, buffer, transfer_len, NULL, 0); +} + +int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, +		int transfer_len, struct devrequest *setup) +{ +	int maxsize = usb_maxpacket(dev, pipe); + +	info("submit_control_msg"); +#ifdef DEBUG +	pkt_print(NULL, dev, pipe, buffer, transfer_len, +		  setup, "SUB", usb_pipein(pipe)); +#else +	mdelay(1); +#endif +	if (!maxsize) { +		err("submit_control_message: pipesize for pipe %lx is zero", +			pipe); +		return -1; +	} +	if (((pipe >> 8) & 0x7f) == gohci.rh.devnum) { +		gohci.rh.dev = dev; +		/* root hub - redirect */ +		return ohci_submit_rh_msg(dev, pipe, buffer, transfer_len, +			setup); +	} + +	return submit_common_msg(dev, pipe, buffer, transfer_len, setup, 0); +} + +int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, +		int transfer_len, int interval) +{ +	info("submit_int_msg"); +	return submit_common_msg(dev, pipe, buffer, transfer_len, NULL, +			interval); +} + +/*-------------------------------------------------------------------------* + * HC functions + *-------------------------------------------------------------------------*/ + +/* reset the HC and BUS */ + +static int hc_reset(ohci_t *ohci) +{ +#ifdef CONFIG_PCI_EHCI_DEVNO +	pci_dev_t pdev; +#endif +	int timeout = 30; +	int smm_timeout = 50; /* 0,5 sec */ + +	dbg("%s\n", __FUNCTION__); + +#ifdef CONFIG_PCI_EHCI_DEVNO +	/* +	 *  Some multi-function controllers (e.g. ISP1562) allow root hub +	 * resetting via EHCI registers only. +	 */ +	pdev = pci_find_devices(ehci_pci_ids, CONFIG_PCI_EHCI_DEVNO); +	if (pdev != -1) { +		u32 base; +		int timeout = 1000; + +		pci_read_config_dword(pdev, PCI_BASE_ADDRESS_0, &base); +		base += EHCI_USBCMD_OFF; +		ohci_writel(ohci_readl(base) | EHCI_USBCMD_HCRESET, base); + +		while (ohci_readl(base) & EHCI_USBCMD_HCRESET) { +			if (timeout-- <= 0) { +				printf("USB RootHub reset timed out!"); +				break; +			} +			udelay(1); +		} +	} else +		printf("No EHCI func at %d index!\n", CONFIG_PCI_EHCI_DEVNO); +#endif +	if (ohci_readl(&ohci->regs->control) & OHCI_CTRL_IR) { +		/* SMM owns the HC, request ownership */ +		ohci_writel(OHCI_OCR, &ohci->regs->cmdstatus); +		info("USB HC TakeOver from SMM"); +		while (ohci_readl(&ohci->regs->control) & OHCI_CTRL_IR) { +			mdelay(10); +			if (--smm_timeout == 0) { +				err("USB HC TakeOver failed!"); +				return -1; +			} +		} +	} + +	/* Disable HC interrupts */ +	ohci_writel(OHCI_INTR_MIE, &ohci->regs->intrdisable); + +	dbg("USB HC reset_hc usb-%s: ctrl = 0x%X ;\n", +		ohci->slot_name, +		ohci_readl(&ohci->regs->control)); + +	/* Reset USB (needed by some controllers) */ +	ohci->hc_control = 0; +	ohci_writel(ohci->hc_control, &ohci->regs->control); + +	/* HC Reset requires max 10 us delay */ +	ohci_writel(OHCI_HCR,  &ohci->regs->cmdstatus); +	while ((ohci_readl(&ohci->regs->cmdstatus) & OHCI_HCR) != 0) { +		if (--timeout == 0) { +			err("USB HC reset timed out!"); +			return -1; +		} +		udelay(1); +	} +	return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* Start an OHCI controller, set the BUS operational + * enable interrupts + * connect the virtual root hub */ + +static int hc_start(ohci_t *ohci) +{ +	__u32 mask; +	unsigned int fminterval; + +	ohci->disabled = 1; + +	/* Tell the controller where the control and bulk lists are +	 * The lists are empty now. */ + +	ohci_writel(0, &ohci->regs->ed_controlhead); +	ohci_writel(0, &ohci->regs->ed_bulkhead); + +	ohci_writel((__u32)ohci->hcca, +		    &ohci->regs->hcca); /* reset clears this */ + +	fminterval = 0x2edf; +	ohci_writel((fminterval * 9) / 10, &ohci->regs->periodicstart); +	fminterval |= ((((fminterval - 210) * 6) / 7) << 16); +	ohci_writel(fminterval, &ohci->regs->fminterval); +	ohci_writel(0x628, &ohci->regs->lsthresh); + +	/* start controller operations */ +	ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER; +	ohci->disabled = 0; +	ohci_writel(ohci->hc_control, &ohci->regs->control); + +	/* disable all interrupts */ +	mask = (OHCI_INTR_SO | OHCI_INTR_WDH | OHCI_INTR_SF | OHCI_INTR_RD | +			OHCI_INTR_UE | OHCI_INTR_FNO | OHCI_INTR_RHSC | +			OHCI_INTR_OC | OHCI_INTR_MIE); +	ohci_writel(mask, &ohci->regs->intrdisable); +	/* clear all interrupts */ +	mask &= ~OHCI_INTR_MIE; +	ohci_writel(mask, &ohci->regs->intrstatus); +	/* Choose the interrupts we care about now  - but w/o MIE */ +	mask = OHCI_INTR_RHSC | OHCI_INTR_UE | OHCI_INTR_WDH | OHCI_INTR_SO; +	ohci_writel(mask, &ohci->regs->intrenable); + +#ifdef	OHCI_USE_NPS +	/* required for AMD-756 and some Mac platforms */ +	ohci_writel((roothub_a(ohci) | RH_A_NPS) & ~RH_A_PSM, +		&ohci->regs->roothub.a); +	ohci_writel(RH_HS_LPSC, &ohci->regs->roothub.status); +#endif	/* OHCI_USE_NPS */ + +	/* POTPGT delay is bits 24-31, in 2 ms units. */ +	mdelay((roothub_a(ohci) >> 23) & 0x1fe); + +	/* connect the virtual root hub */ +	ohci->rh.devnum = 0; + +	return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* an interrupt happens */ + +static int hc_interrupt(void) +{ +	ohci_t *ohci = &gohci; +	struct ohci_regs *regs = ohci->regs; +	int ints; +	int stat = -1; + +	if ((ohci->hcca->done_head != 0) && +				!(m32_swap(ohci->hcca->done_head) & 0x01)) { +		ints =  OHCI_INTR_WDH; +	} else { +		ints = ohci_readl(®s->intrstatus); +		if (ints == ~(u32)0) { +			ohci->disabled++; +			err("%s device removed!", ohci->slot_name); +			return -1; +		} else { +			ints &= ohci_readl(®s->intrenable); +			if (ints == 0) { +				dbg("hc_interrupt: returning..\n"); +				return 0xff; +			} +		} +	} + +	/* dbg("Interrupt: %x frame: %x", ints, +					le16_to_cpu(ohci->hcca->frame_no)); */ + +	if (ints & OHCI_INTR_RHSC) +		stat = 0xff; + +	if (ints & OHCI_INTR_UE) { +		ohci->disabled++; +		err("OHCI Unrecoverable Error, controller usb-%s disabled", +			ohci->slot_name); +		/* e.g. due to PCI Master/Target Abort */ + +#ifdef	DEBUG +		ohci_dump(ohci, 1); +#else +		mdelay(1); +#endif +		/* FIXME: be optimistic, hope that bug won't repeat often. */ +		/* Make some non-interrupt context restart the controller. */ +		/* Count and limit the retries though; either hardware or */ +		/* software errors can go forever... */ +		hc_reset(ohci); +		return -1; +	} + +	if (ints & OHCI_INTR_WDH) { +		mdelay(1); +		ohci_writel(OHCI_INTR_WDH, ®s->intrdisable); +		(void)ohci_readl(®s->intrdisable); /* flush */ +		stat = dl_done_list(&gohci); +		ohci_writel(OHCI_INTR_WDH, ®s->intrenable); +		(void)ohci_readl(®s->intrdisable); /* flush */ +	} + +	if (ints & OHCI_INTR_SO) { +		dbg("USB Schedule overrun\n"); +		ohci_writel(OHCI_INTR_SO, ®s->intrenable); +		stat = -1; +	} + +	/* FIXME:  this assumes SOF (1/ms) interrupts don't get lost... */ +	if (ints & OHCI_INTR_SF) { +		unsigned int frame = m16_swap(ohci->hcca->frame_no) & 1; +		mdelay(1); +		ohci_writel(OHCI_INTR_SF, ®s->intrdisable); +		if (ohci->ed_rm_list[frame] != NULL) +			ohci_writel(OHCI_INTR_SF, ®s->intrenable); +		stat = 0xff; +	} + +	ohci_writel(ints, ®s->intrstatus); +	return stat; +} + +/*-------------------------------------------------------------------------*/ + +/*-------------------------------------------------------------------------*/ + +/* De-allocate all resources.. */ + +static void hc_release_ohci(ohci_t *ohci) +{ +	dbg("USB HC release ohci usb-%s", ohci->slot_name); + +	if (!ohci->disabled) +		hc_reset(ohci); +} + +/*-------------------------------------------------------------------------*/ + +/* + * low level initalisation routine, called from usb.c + */ +static char ohci_inited = 0; + +int usb_lowlevel_init(int index, enum usb_init_type init, void **controller) +{ +#ifdef CONFIG_PCI_OHCI +	pci_dev_t pdev; +#endif + +#ifdef CONFIG_SYS_USB_OHCI_CPU_INIT +	/* cpu dependant init */ +	if (usb_cpu_init()) +		return -1; +#endif + +#ifdef CONFIG_SYS_USB_OHCI_BOARD_INIT +	/*  board dependant init */ +	if (board_usb_init(index, USB_INIT_HOST)) +		return -1; +#endif +	memset(&gohci, 0, sizeof(ohci_t)); + +	/* align the storage */ +	if ((__u32)&ghcca[0] & 0xff) { +		err("HCCA not aligned!!"); +		return -1; +	} +	phcca = &ghcca[0]; +	info("aligned ghcca %p", phcca); +	memset(&ohci_dev, 0, sizeof(struct ohci_device)); +	if ((__u32)&ohci_dev.ed[0] & 0x7) { +		err("EDs not aligned!!"); +		return -1; +	} +	memset(gtd, 0, sizeof(td_t) * (NUM_TD + 1)); +	if ((__u32)gtd & 0x7) { +		err("TDs not aligned!!"); +		return -1; +	} +	ptd = gtd; +	gohci.hcca = phcca; +	memset(phcca, 0, sizeof(struct ohci_hcca)); + +	gohci.disabled = 1; +	gohci.sleeping = 0; +	gohci.irq = -1; +#ifdef CONFIG_PCI_OHCI +	pdev = pci_find_devices(ohci_pci_ids, CONFIG_PCI_OHCI_DEVNO); + +	if (pdev != -1) { +		u16 vid, did; +		u32 base; +		pci_read_config_word(pdev, PCI_VENDOR_ID, &vid); +		pci_read_config_word(pdev, PCI_DEVICE_ID, &did); +		printf("OHCI pci controller (%04x, %04x) found @(%d:%d:%d)\n", +				vid, did, (pdev >> 16) & 0xff, +				(pdev >> 11) & 0x1f, (pdev >> 8) & 0x7); +		pci_read_config_dword(pdev, PCI_BASE_ADDRESS_0, &base); +		printf("OHCI regs address 0x%08x\n", base); +		gohci.regs = (struct ohci_regs *)base; +	} else +		return -1; +#else +	gohci.regs = (struct ohci_regs *)CONFIG_SYS_USB_OHCI_REGS_BASE; +#endif + +	gohci.flags = 0; +	gohci.slot_name = CONFIG_SYS_USB_OHCI_SLOT_NAME; + +	if (hc_reset (&gohci) < 0) { +		hc_release_ohci (&gohci); +		err ("can't reset usb-%s", gohci.slot_name); +#ifdef CONFIG_SYS_USB_OHCI_BOARD_INIT +		/* board dependant cleanup */ +		board_usb_cleanup(index, USB_INIT_HOST); +#endif + +#ifdef CONFIG_SYS_USB_OHCI_CPU_INIT +		/* cpu dependant cleanup */ +		usb_cpu_init_fail(); +#endif +		return -1; +	} + +	if (hc_start(&gohci) < 0) { +		err("can't start usb-%s", gohci.slot_name); +		hc_release_ohci(&gohci); +		/* Initialization failed */ +#ifdef CONFIG_SYS_USB_OHCI_BOARD_INIT +		/* board dependant cleanup */ +		usb_board_stop(); +#endif + +#ifdef CONFIG_SYS_USB_OHCI_CPU_INIT +		/* cpu dependant cleanup */ +		usb_cpu_stop(); +#endif +		return -1; +	} + +#ifdef	DEBUG +	ohci_dump(&gohci, 1); +#else +	mdelay(1); +#endif +	ohci_inited = 1; +	return 0; +} + +int usb_lowlevel_stop(int index) +{ +	/* this gets called really early - before the controller has */ +	/* even been initialized! */ +	if (!ohci_inited) +		return 0; +	/* TODO release any interrupts, etc. */ +	/* call hc_release_ohci() here ? */ +	hc_reset(&gohci); + +#ifdef CONFIG_SYS_USB_OHCI_BOARD_INIT +	/* board dependant cleanup */ +	if (usb_board_stop()) +		return -1; +#endif + +#ifdef CONFIG_SYS_USB_OHCI_CPU_INIT +	/* cpu dependant cleanup */ +	if (usb_cpu_stop()) +		return -1; +#endif +	/* This driver is no longer initialised. It needs a new low-level +	 * init (board/cpu) before it can be used again. */ +	ohci_inited = 0; +	return 0; +} diff --git a/roms/u-boot/drivers/usb/host/ohci-s3c24xx.c b/roms/u-boot/drivers/usb/host/ohci-s3c24xx.c new file mode 100644 index 00000000..3c659c60 --- /dev/null +++ b/roms/u-boot/drivers/usb/host/ohci-s3c24xx.c @@ -0,0 +1,1691 @@ +/* + * URB OHCI HCD (Host Controller Driver) for USB on the S3C2400. + * + * (C) Copyright 2003 + * Gary Jennejohn, DENX Software Engineering <garyj@denx.de> + * + * Note: Much of this code has been derived from Linux 2.4 + * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at> + * (C) Copyright 2000-2002 David Brownell + * + * SPDX-License-Identifier:	GPL-2.0+ + */ +/* + * IMPORTANT NOTES + * 1 - this driver is intended for use with USB Mass Storage Devices + *     (BBB) ONLY. There is NO support for Interrupt or Isochronous pipes! + */ + +#include <common.h> +/* #include <pci.h> no PCI on the S3C24X0 */ + +#if defined(CONFIG_USB_OHCI) && defined(CONFIG_S3C24X0) + +#include <asm/arch/s3c24x0_cpu.h> +#include <asm/io.h> +#include <malloc.h> +#include <usb.h> +#include "ohci-s3c24xx.h" + +#define OHCI_USE_NPS		/* force NoPowerSwitching mode */ +#undef OHCI_VERBOSE_DEBUG	/* not always helpful */ + + +/* For initializing controller (mask in an HCFS mode too) */ +#define	OHCI_CONTROL_INIT \ +	(OHCI_CTRL_CBSR & 0x3) | OHCI_CTRL_IE | OHCI_CTRL_PLE + +#define min_t(type, x, y) \ +	({ type __x = (x); type __y = (y); __x < __y ? __x : __y; }) + +#undef DEBUG +#ifdef DEBUG +#define dbg(format, arg...) printf("DEBUG: " format "\n", ## arg) +#else +#define dbg(format, arg...) do {} while(0) +#endif /* DEBUG */ +#define err(format, arg...) printf("ERROR: " format "\n", ## arg) +#undef SHOW_INFO +#ifdef SHOW_INFO +#define info(format, arg...) printf("INFO: " format "\n", ## arg) +#else +#define info(format, arg...) do {} while(0) +#endif + +#define m16_swap(x) swap_16(x) +#define m32_swap(x) swap_32(x) + +/* global struct ohci */ +static struct ohci gohci; +/* this must be aligned to a 256 byte boundary */ +struct ohci_hcca ghcca[1]; +/* a pointer to the aligned storage */ +struct ohci_hcca *phcca; +/* this allocates EDs for all possible endpoints */ +struct ohci_device ohci_dev; +/* urb_priv */ +struct urb_priv urb_priv; +/* RHSC flag */ +int got_rhsc; +/* device which was disconnected */ +struct usb_device *devgone; +/* flag guarding URB transation */ +int urb_finished = 0; + +/*-------------------------------------------------------------------------*/ + +/* AMD-756 (D2 rev) reports corrupt register contents in some cases. + * The erratum (#4) description is incorrect.  AMD's workaround waits + * till some bits (mostly reserved) are clear; ok for all revs. + */ +#define OHCI_QUIRK_AMD756 0xabcd +#define read_roothub(hc, register, mask) ({ \ +	u32 temp = readl (&hc->regs->roothub.register); \ +	if (hc->flags & OHCI_QUIRK_AMD756) \ +		while (temp & mask) \ +			temp = readl (&hc->regs->roothub.register); \ +	temp; }) + +static u32 roothub_a(struct ohci *hc) +{ +	return read_roothub(hc, a, 0xfc0fe000); +} +static inline u32 roothub_b(struct ohci *hc) +{ +	return readl(&hc->regs->roothub.b); +} +static inline u32 roothub_status(struct ohci *hc) +{ +	return readl(&hc->regs->roothub.status); +} +static u32 roothub_portstatus(struct ohci *hc, int i) +{ +	return read_roothub(hc, portstatus[i], 0xffe0fce0); +} + +/* forward declaration */ +static int hc_interrupt(void); +static void td_submit_job(struct usb_device *dev, unsigned long pipe, +			  void *buffer, int transfer_len, +			  struct devrequest *setup, struct urb_priv *urb, +			  int interval); + +/*-------------------------------------------------------------------------* + * URB support functions + *-------------------------------------------------------------------------*/ + +/* free HCD-private data associated with this URB */ + +static void urb_free_priv(struct urb_priv *urb) +{ +	int i; +	int last; +	struct td *td; + +	last = urb->length - 1; +	if (last >= 0) { +		for (i = 0; i <= last; i++) { +			td = urb->td[i]; +			if (td) { +				td->usb_dev = NULL; +				urb->td[i] = NULL; +			} +		} +	} +} + +/*-------------------------------------------------------------------------*/ + +#ifdef DEBUG +static int sohci_get_current_frame_number(struct usb_device *dev); + +/* debug| print the main components of an URB + * small: 0) header + data packets 1) just header */ + +static void pkt_print(struct usb_device *dev, unsigned long pipe, void *buffer, +		      int transfer_len, struct devrequest *setup, char *str, +		      int small) +{ +	struct urb_priv *purb = &urb_priv; + +	dbg("%s URB:[%4x] dev:%2d,ep:%2d-%c,type:%s,len:%d/%d stat:%#lx", +	    str, +	    sohci_get_current_frame_number(dev), +	    usb_pipedevice(pipe), +	    usb_pipeendpoint(pipe), +	    usb_pipeout(pipe) ? 'O' : 'I', +	    usb_pipetype(pipe) < 2 ? +		(usb_pipeint(pipe) ? "INTR" : "ISOC") : +		(usb_pipecontrol(pipe) ? "CTRL" : "BULK"), +	    purb->actual_length, transfer_len, dev->status); +#ifdef	OHCI_VERBOSE_DEBUG +	if (!small) { +		int i, len; + +		if (usb_pipecontrol(pipe)) { +			printf(__FILE__ ": cmd(8):"); +			for (i = 0; i < 8; i++) +				printf(" %02x", ((__u8 *) setup)[i]); +			printf("\n"); +		} +		if (transfer_len > 0 && buffer) { +			printf(__FILE__ ": data(%d/%d):", +			       purb->actual_length, transfer_len); +			len = usb_pipeout(pipe) ? +			    transfer_len : purb->actual_length; +			for (i = 0; i < 16 && i < len; i++) +				printf(" %02x", ((__u8 *) buffer)[i]); +			printf("%s\n", i < len ? "..." : ""); +		} +	} +#endif +} + +/* just for debugging; prints non-empty branches of the +   int ed tree inclusive iso eds*/ +void ep_print_int_eds(struct ohci *ohci, char *str) +{ +	int i, j; +	__u32 *ed_p; +	for (i = 0; i < 32; i++) { +		j = 5; +		ed_p = &(ohci->hcca->int_table[i]); +		if (*ed_p == 0) +			continue; +		printf(__FILE__ ": %s branch int %2d(%2x):", str, i, i); +		while (*ed_p != 0 && j--) { +			struct ed *ed = (struct ed *) m32_swap(ed_p); +			printf(" ed: %4x;", ed->hwINFO); +			ed_p = &ed->hwNextED; +		} +		printf("\n"); +	} +} + +static void ohci_dump_intr_mask(char *label, __u32 mask) +{ +	dbg("%s: 0x%08x%s%s%s%s%s%s%s%s%s", +	    label, +	    mask, +	    (mask & OHCI_INTR_MIE) ? " MIE" : "", +	    (mask & OHCI_INTR_OC) ? " OC" : "", +	    (mask & OHCI_INTR_RHSC) ? " RHSC" : "", +	    (mask & OHCI_INTR_FNO) ? " FNO" : "", +	    (mask & OHCI_INTR_UE) ? " UE" : "", +	    (mask & OHCI_INTR_RD) ? " RD" : "", +	    (mask & OHCI_INTR_SF) ? " SF" : "", +	    (mask & OHCI_INTR_WDH) ? " WDH" : "", +	    (mask & OHCI_INTR_SO) ? " SO" : ""); +} + +static void maybe_print_eds(char *label, __u32 value) +{ +	struct ed *edp = (struct ed *) value; + +	if (value) { +		dbg("%s %08x", label, value); +		dbg("%08x", edp->hwINFO); +		dbg("%08x", edp->hwTailP); +		dbg("%08x", edp->hwHeadP); +		dbg("%08x", edp->hwNextED); +	} +} + +static char *hcfs2string(int state) +{ +	switch (state) { +	case OHCI_USB_RESET: +		return "reset"; +	case OHCI_USB_RESUME: +		return "resume"; +	case OHCI_USB_OPER: +		return "operational"; +	case OHCI_USB_SUSPEND: +		return "suspend"; +	} +	return "?"; +} + +/* dump control and status registers */ +static void ohci_dump_status(struct ohci *controller) +{ +	struct ohci_regs *regs = controller->regs; +	__u32 temp; + +	temp = readl(®s->revision) & 0xff; +	if (temp != 0x10) +		dbg("spec %d.%d", (temp >> 4), (temp & 0x0f)); + +	temp = readl(®s->control); +	dbg("control: 0x%08x%s%s%s HCFS=%s%s%s%s%s CBSR=%d", temp, +	    (temp & OHCI_CTRL_RWE) ? " RWE" : "", +	    (temp & OHCI_CTRL_RWC) ? " RWC" : "", +	    (temp & OHCI_CTRL_IR) ? " IR" : "", +	    hcfs2string(temp & OHCI_CTRL_HCFS), +	    (temp & OHCI_CTRL_BLE) ? " BLE" : "", +	    (temp & OHCI_CTRL_CLE) ? " CLE" : "", +	    (temp & OHCI_CTRL_IE) ? " IE" : "", +	    (temp & OHCI_CTRL_PLE) ? " PLE" : "", temp & OHCI_CTRL_CBSR); + +	temp = readl(®s->cmdstatus); +	dbg("cmdstatus: 0x%08x SOC=%d%s%s%s%s", temp, +	    (temp & OHCI_SOC) >> 16, +	    (temp & OHCI_OCR) ? " OCR" : "", +	    (temp & OHCI_BLF) ? " BLF" : "", +	    (temp & OHCI_CLF) ? " CLF" : "", (temp & OHCI_HCR) ? " HCR" : ""); + +	ohci_dump_intr_mask("intrstatus", readl(®s->intrstatus)); +	ohci_dump_intr_mask("intrenable", readl(®s->intrenable)); + +	maybe_print_eds("ed_periodcurrent", readl(®s->ed_periodcurrent)); + +	maybe_print_eds("ed_controlhead", readl(®s->ed_controlhead)); +	maybe_print_eds("ed_controlcurrent", readl(®s->ed_controlcurrent)); + +	maybe_print_eds("ed_bulkhead", readl(®s->ed_bulkhead)); +	maybe_print_eds("ed_bulkcurrent", readl(®s->ed_bulkcurrent)); + +	maybe_print_eds("donehead", readl(®s->donehead)); +} + +static void ohci_dump_roothub(struct ohci *controller, int verbose) +{ +	__u32 temp, ndp, i; + +	temp = roothub_a(controller); +	ndp = (temp & RH_A_NDP); + +	if (verbose) { +		dbg("roothub.a: %08x POTPGT=%d%s%s%s%s%s NDP=%d", temp, +		    ((temp & RH_A_POTPGT) >> 24) & 0xff, +		    (temp & RH_A_NOCP) ? " NOCP" : "", +		    (temp & RH_A_OCPM) ? " OCPM" : "", +		    (temp & RH_A_DT) ? " DT" : "", +		    (temp & RH_A_NPS) ? " NPS" : "", +		    (temp & RH_A_PSM) ? " PSM" : "", ndp); +		temp = roothub_b(controller); +		dbg("roothub.b: %08x PPCM=%04x DR=%04x", +		    temp, (temp & RH_B_PPCM) >> 16, (temp & RH_B_DR) +		    ); +		temp = roothub_status(controller); +		dbg("roothub.status: %08x%s%s%s%s%s%s", +		    temp, +		    (temp & RH_HS_CRWE) ? " CRWE" : "", +		    (temp & RH_HS_OCIC) ? " OCIC" : "", +		    (temp & RH_HS_LPSC) ? " LPSC" : "", +		    (temp & RH_HS_DRWE) ? " DRWE" : "", +		    (temp & RH_HS_OCI) ? " OCI" : "", +		    (temp & RH_HS_LPS) ? " LPS" : ""); +	} + +	for (i = 0; i < ndp; i++) { +		temp = roothub_portstatus(controller, i); +		dbg("roothub.portstatus [%d] = 0x%08x%s%s%s%s%s%s%s%s%s%s%s%s", +		    i, +		    temp, +		    (temp & RH_PS_PRSC) ? " PRSC" : "", +		    (temp & RH_PS_OCIC) ? " OCIC" : "", +		    (temp & RH_PS_PSSC) ? " PSSC" : "", +		    (temp & RH_PS_PESC) ? " PESC" : "", +		    (temp & RH_PS_CSC) ? " CSC" : "", +		    (temp & RH_PS_LSDA) ? " LSDA" : "", +		    (temp & RH_PS_PPS) ? " PPS" : "", +		    (temp & RH_PS_PRS) ? " PRS" : "", +		    (temp & RH_PS_POCI) ? " POCI" : "", +		    (temp & RH_PS_PSS) ? " PSS" : "", +		    (temp & RH_PS_PES) ? " PES" : "", +		    (temp & RH_PS_CCS) ? " CCS" : ""); +	} +} + +static void ohci_dump(struct ohci *controller, int verbose) +{ +	dbg("OHCI controller usb-%s state", controller->slot_name); + +	/* dumps some of the state we know about */ +	ohci_dump_status(controller); +	if (verbose) +		ep_print_int_eds(controller, "hcca"); +	dbg("hcca frame #%04x", controller->hcca->frame_no); +	ohci_dump_roothub(controller, 1); +} + +#endif /* DEBUG */ + +/*-------------------------------------------------------------------------* + * Interface functions (URB) + *-------------------------------------------------------------------------*/ + +/* get a transfer request */ + +int sohci_submit_job(struct usb_device *dev, unsigned long pipe, void *buffer, +		     int transfer_len, struct devrequest *setup, int interval) +{ +	struct ohci *ohci; +	struct ed *ed; +	struct urb_priv *purb_priv; +	int i, size = 0; + +	ohci = &gohci; + +	/* when controller's hung, permit only roothub cleanup attempts +	 * such as powering down ports */ +	if (ohci->disabled) { +		err("sohci_submit_job: EPIPE"); +		return -1; +	} + +	/* if we have an unfinished URB from previous transaction let's +	 * fail and scream as quickly as possible so as not to corrupt +	 * further communication */ +	if (!urb_finished) { +		err("sohci_submit_job: URB NOT FINISHED"); +		return -1; +	} +	/* we're about to begin a new transaction here +	   so mark the URB unfinished */ +	urb_finished = 0; + +	/* every endpoint has a ed, locate and fill it */ +	ed = ep_add_ed(dev, pipe); +	if (!ed) { +		err("sohci_submit_job: ENOMEM"); +		return -1; +	} + +	/* for the private part of the URB we need the number of TDs (size) */ +	switch (usb_pipetype(pipe)) { +	case PIPE_BULK: +		/* one TD for every 4096 Byte */ +		size = (transfer_len - 1) / 4096 + 1; +		break; +	case PIPE_CONTROL: +		/* 1 TD for setup, 1 for ACK and 1 for every 4096 B */ +		size = (transfer_len == 0) ? 2 : (transfer_len - 1) / 4096 + 3; +		break; +	} + +	if (size >= (N_URB_TD - 1)) { +		err("need %d TDs, only have %d", size, N_URB_TD); +		return -1; +	} +	purb_priv = &urb_priv; +	purb_priv->pipe = pipe; + +	/* fill the private part of the URB */ +	purb_priv->length = size; +	purb_priv->ed = ed; +	purb_priv->actual_length = 0; + +	/* allocate the TDs */ +	/* note that td[0] was allocated in ep_add_ed */ +	for (i = 0; i < size; i++) { +		purb_priv->td[i] = td_alloc(dev); +		if (!purb_priv->td[i]) { +			purb_priv->length = i; +			urb_free_priv(purb_priv); +			err("sohci_submit_job: ENOMEM"); +			return -1; +		} +	} + +	if (ed->state == ED_NEW || (ed->state & ED_DEL)) { +		urb_free_priv(purb_priv); +		err("sohci_submit_job: EINVAL"); +		return -1; +	} + +	/* link the ed into a chain if is not already */ +	if (ed->state != ED_OPER) +		ep_link(ohci, ed); + +	/* fill the TDs and link it to the ed */ +	td_submit_job(dev, pipe, buffer, transfer_len, setup, purb_priv, +		      interval); + +	return 0; +} + +/*-------------------------------------------------------------------------*/ + +#ifdef DEBUG +/* tell us the current USB frame number */ + +static int sohci_get_current_frame_number(struct usb_device *usb_dev) +{ +	struct ohci *ohci = &gohci; + +	return m16_swap(ohci->hcca->frame_no); +} +#endif + +/*-------------------------------------------------------------------------* + * ED handling functions + *-------------------------------------------------------------------------*/ + +/* link an ed into one of the HC chains */ + +static int ep_link(struct ohci *ohci, struct ed *edi) +{ +	struct ed *ed = edi; + +	ed->state = ED_OPER; + +	switch (ed->type) { +	case PIPE_CONTROL: +		ed->hwNextED = 0; +		if (ohci->ed_controltail == NULL) { +			writel((u32)ed, &ohci->regs->ed_controlhead); +		} else { +			ohci->ed_controltail->hwNextED = (__u32) m32_swap(ed); +		} +		ed->ed_prev = ohci->ed_controltail; +		if (!ohci->ed_controltail && !ohci->ed_rm_list[0] && +		    !ohci->ed_rm_list[1] && !ohci->sleeping) { +			ohci->hc_control |= OHCI_CTRL_CLE; +			writel(ohci->hc_control, &ohci->regs->control); +		} +		ohci->ed_controltail = edi; +		break; + +	case PIPE_BULK: +		ed->hwNextED = 0; +		if (ohci->ed_bulktail == NULL) { +			writel((u32)ed, &ohci->regs->ed_bulkhead); +		} else { +			ohci->ed_bulktail->hwNextED = (__u32) m32_swap(ed); +		} +		ed->ed_prev = ohci->ed_bulktail; +		if (!ohci->ed_bulktail && !ohci->ed_rm_list[0] && +		    !ohci->ed_rm_list[1] && !ohci->sleeping) { +			ohci->hc_control |= OHCI_CTRL_BLE; +			writel(ohci->hc_control, &ohci->regs->control); +		} +		ohci->ed_bulktail = edi; +		break; +	} +	return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* unlink an ed from one of the HC chains. + * just the link to the ed is unlinked. + * the link from the ed still points to another operational ed or 0 + * so the HC can eventually finish the processing of the unlinked ed */ + +static int ep_unlink(struct ohci *ohci, struct ed *ed) +{ +	struct ed *next; +	ed->hwINFO |= m32_swap(OHCI_ED_SKIP); + +	switch (ed->type) { +	case PIPE_CONTROL: +		if (ed->ed_prev == NULL) { +			if (!ed->hwNextED) { +				ohci->hc_control &= ~OHCI_CTRL_CLE; +				writel(ohci->hc_control, &ohci->regs->control); +			} +			writel(m32_swap(*((__u32 *) &ed->hwNextED)), +			       &ohci->regs->ed_controlhead); +		} else { +			ed->ed_prev->hwNextED = ed->hwNextED; +		} +		if (ohci->ed_controltail == ed) { +			ohci->ed_controltail = ed->ed_prev; +		} else { +			next = (struct ed *)m32_swap(*((__u32 *)&ed->hwNextED)); +			next->ed_prev = ed->ed_prev; +		} +		break; + +	case PIPE_BULK: +		if (ed->ed_prev == NULL) { +			if (!ed->hwNextED) { +				ohci->hc_control &= ~OHCI_CTRL_BLE; +				writel(ohci->hc_control, &ohci->regs->control); +			} +			writel(m32_swap(*((__u32 *) &ed->hwNextED)), +			       &ohci->regs->ed_bulkhead); +		} else { +			ed->ed_prev->hwNextED = ed->hwNextED; +		} +		if (ohci->ed_bulktail == ed) { +			ohci->ed_bulktail = ed->ed_prev; +		} else { +			next = (struct ed *)m32_swap(*((__u32 *)&ed->hwNextED)); +			next->ed_prev = ed->ed_prev; +		} +		break; +	} +	ed->state = ED_UNLINK; +	return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* add/reinit an endpoint; this should be done once at the usb_set_configuration + * command, but the USB stack is a little bit stateless  so we do it at every + * transaction. If the state of the ed is ED_NEW then a dummy td is added and + * the state is changed to ED_UNLINK. In all other cases the state is left + * unchanged. The ed info fields are setted anyway even though most of them + * should not change */ + +static struct ed *ep_add_ed(struct usb_device *usb_dev, unsigned long pipe) +{ +	struct td *td; +	struct ed *ed_ret; +	struct ed *ed; + +	ed = ed_ret = &ohci_dev.ed[(usb_pipeendpoint(pipe) << 1) | +				   (usb_pipecontrol(pipe) ? 0 : +				    usb_pipeout(pipe))]; + +	if ((ed->state & ED_DEL) || (ed->state & ED_URB_DEL)) { +		err("ep_add_ed: pending delete"); +		/* pending delete request */ +		return NULL; +	} + +	if (ed->state == ED_NEW) { +		ed->hwINFO = m32_swap(OHCI_ED_SKIP);	/* skip ed */ +		/* dummy td; end of td list for ed */ +		td = td_alloc(usb_dev); +		ed->hwTailP = (__u32) m32_swap(td); +		ed->hwHeadP = ed->hwTailP; +		ed->state = ED_UNLINK; +		ed->type = usb_pipetype(pipe); +		ohci_dev.ed_cnt++; +	} + +	ed->hwINFO = m32_swap(usb_pipedevice(pipe) +			      | usb_pipeendpoint(pipe) << 7 +			      | (usb_pipeisoc(pipe) ? 0x8000 : 0) +			      | (usb_pipecontrol(pipe) ? 0 : +				 (usb_pipeout(pipe) ? 0x800 : 0x1000)) +			      | (usb_dev->speed == USB_SPEED_LOW) << 13 | +			      usb_maxpacket(usb_dev, pipe) << 16); + +	return ed_ret; +} + +/*-------------------------------------------------------------------------* + * TD handling functions + *-------------------------------------------------------------------------*/ + +/* enqueue next TD for this URB (OHCI spec 5.2.8.2) */ + +static void td_fill(struct ohci *ohci, unsigned int info, void *data, int len, +		    struct usb_device *dev, int index, +		    struct urb_priv *urb_priv) +{ +	struct td *td, *td_pt; +#ifdef OHCI_FILL_TRACE +	int i; +#endif + +	if (index > urb_priv->length) { +		err("index > length"); +		return; +	} +	/* use this td as the next dummy */ +	td_pt = urb_priv->td[index]; +	td_pt->hwNextTD = 0; + +	/* fill the old dummy TD */ +	td = urb_priv->td[index] = +	    (struct td *) (m32_swap(urb_priv->ed->hwTailP) & ~0xf); + +	td->ed = urb_priv->ed; +	td->next_dl_td = NULL; +	td->index = index; +	td->data = (__u32) data; +#ifdef OHCI_FILL_TRACE +	if (usb_pipebulk(urb_priv->pipe) && usb_pipeout(urb_priv->pipe)) { +		for (i = 0; i < len; i++) +			printf("td->data[%d] %#2x ", i, +			       ((unsigned char *)td->data)[i]); +		printf("\n"); +	} +#endif +	if (!len) +		data = 0; + +	td->hwINFO = (__u32) m32_swap(info); +	td->hwCBP = (__u32) m32_swap(data); +	if (data) +		td->hwBE = (__u32) m32_swap(data + len - 1); +	else +		td->hwBE = 0; +	td->hwNextTD = (__u32) m32_swap(td_pt); + +	/* append to queue */ +	td->ed->hwTailP = td->hwNextTD; +} + +/*-------------------------------------------------------------------------*/ + +/* prepare all TDs of a transfer */ + +static void td_submit_job(struct usb_device *dev, unsigned long pipe, +			  void *buffer, int transfer_len, +			  struct devrequest *setup, struct urb_priv *urb, +			  int interval) +{ +	struct ohci *ohci = &gohci; +	int data_len = transfer_len; +	void *data; +	int cnt = 0; +	__u32 info = 0; +	unsigned int toggle = 0; + +	/* OHCI handles the DATA-toggles itself, we just +	   use the USB-toggle bits for reseting */ +	if (usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe))) { +		toggle = TD_T_TOGGLE; +	} else { +		toggle = TD_T_DATA0; +		usb_settoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe), +			      1); +	} +	urb->td_cnt = 0; +	if (data_len) +		data = buffer; +	else +		data = 0; + +	switch (usb_pipetype(pipe)) { +	case PIPE_BULK: +		info = usb_pipeout(pipe) ? TD_CC | TD_DP_OUT : TD_CC | TD_DP_IN; +		while (data_len > 4096) { +			td_fill(ohci, info | (cnt ? TD_T_TOGGLE : toggle), data, +				4096, dev, cnt, urb); +			data += 4096; +			data_len -= 4096; +			cnt++; +		} +		info = usb_pipeout(pipe) ? +				TD_CC | TD_DP_OUT : +				TD_CC | TD_R | TD_DP_IN; +		td_fill(ohci, info | (cnt ? TD_T_TOGGLE : toggle), data, +			data_len, dev, cnt, urb); +		cnt++; + +		if (!ohci->sleeping) +			/* start bulk list */ +			writel(OHCI_BLF, &ohci->regs->cmdstatus); +		break; + +	case PIPE_CONTROL: +		info = TD_CC | TD_DP_SETUP | TD_T_DATA0; +		td_fill(ohci, info, setup, 8, dev, cnt++, urb); +		if (data_len > 0) { +			info = usb_pipeout(pipe) ? +			    TD_CC | TD_R | TD_DP_OUT | TD_T_DATA1 : +			    TD_CC | TD_R | TD_DP_IN | TD_T_DATA1; +			/* NOTE:  mishandles transfers >8K, some >4K */ +			td_fill(ohci, info, data, data_len, dev, cnt++, urb); +		} +		info = usb_pipeout(pipe) ? +		    TD_CC | TD_DP_IN | TD_T_DATA1 : +		    TD_CC | TD_DP_OUT | TD_T_DATA1; +		td_fill(ohci, info, data, 0, dev, cnt++, urb); +		if (!ohci->sleeping) +			/* start Control list */ +			writel(OHCI_CLF, &ohci->regs->cmdstatus); +		break; +	} +	if (urb->length != cnt) +		dbg("TD LENGTH %d != CNT %d", urb->length, cnt); +} + +/*-------------------------------------------------------------------------* + * Done List handling functions + *-------------------------------------------------------------------------*/ + + +/* calculate the transfer length and update the urb */ + +static void dl_transfer_length(struct td *td) +{ +	__u32 tdBE, tdCBP; +	struct urb_priv *lurb_priv = &urb_priv; + +	tdBE = m32_swap(td->hwBE); +	tdCBP = m32_swap(td->hwCBP); + +	if (!(usb_pipecontrol(lurb_priv->pipe) && +	      ((td->index == 0) || (td->index == lurb_priv->length - 1)))) { +		if (tdBE != 0) { +			if (td->hwCBP == 0) +				lurb_priv->actual_length += tdBE - td->data + 1; +			else +				lurb_priv->actual_length += tdCBP - td->data; +		} +	} +} + +/*-------------------------------------------------------------------------*/ + +/* replies to the request have to be on a FIFO basis so + * we reverse the reversed done-list */ + +static struct td *dl_reverse_done_list(struct ohci *ohci) +{ +	__u32 td_list_hc; +	__u32 tmp; +	struct td *td_rev = NULL; +	struct td *td_list = NULL; +	struct urb_priv *lurb_priv = NULL; + +	td_list_hc = m32_swap(ohci->hcca->done_head) & 0xfffffff0; +	ohci->hcca->done_head = 0; + +	while (td_list_hc) { +		td_list = (struct td *) td_list_hc; + +		if (TD_CC_GET(m32_swap(td_list->hwINFO))) { +			lurb_priv = &urb_priv; +			dbg(" USB-error/status: %x : %p", +			    TD_CC_GET(m32_swap(td_list->hwINFO)), td_list); +			if (td_list->ed->hwHeadP & m32_swap(0x1)) { +				if (lurb_priv && +				    ((td_list->index+1) < lurb_priv->length)) { +					tmp = lurb_priv->length - 1; +					td_list->ed->hwHeadP = +						(lurb_priv->td[tmp]->hwNextTD & +						 m32_swap(0xfffffff0)) | +						 (td_list->ed->hwHeadP & +						  m32_swap(0x2)); +					lurb_priv->td_cnt += lurb_priv->length - +							     td_list->index - 1; +				} else +					td_list->ed->hwHeadP &= +					    m32_swap(0xfffffff2); +			} +		} + +		td_list->next_dl_td = td_rev; +		td_rev = td_list; +		td_list_hc = m32_swap(td_list->hwNextTD) & 0xfffffff0; +	} + +	return td_list; +} + +/*-------------------------------------------------------------------------*/ + +/* td done list */ +static int dl_done_list(struct ohci *ohci, struct td *td_list) +{ +	struct td *td_list_next = NULL; +	struct ed *ed; +	int cc = 0; +	int stat = 0; +	/* urb_t *urb; */ +	struct urb_priv *lurb_priv; +	__u32 tdINFO, edHeadP, edTailP; + +	while (td_list) { +		td_list_next = td_list->next_dl_td; + +		lurb_priv = &urb_priv; +		tdINFO = m32_swap(td_list->hwINFO); + +		ed = td_list->ed; + +		dl_transfer_length(td_list); + +		/* error code of transfer */ +		cc = TD_CC_GET(tdINFO); +		if (cc != 0) { +			dbg("ConditionCode %#x", cc); +			stat = cc_to_error[cc]; +		} + +		/* see if this done list makes for all TD's of current URB, +		 * and mark the URB finished if so */ +		if (++(lurb_priv->td_cnt) == lurb_priv->length) { +			if ((ed->state & (ED_OPER | ED_UNLINK))) +				urb_finished = 1; +			else +				dbg("dl_done_list: strange.., ED state %x, " +				    "ed->state\n"); +		} else +			dbg("dl_done_list: processing TD %x, len %x\n", +			    lurb_priv->td_cnt, lurb_priv->length); + +		if (ed->state != ED_NEW) { +			edHeadP = m32_swap(ed->hwHeadP) & 0xfffffff0; +			edTailP = m32_swap(ed->hwTailP); + +			/* unlink eds if they are not busy */ +			if ((edHeadP == edTailP) && (ed->state == ED_OPER)) +				ep_unlink(ohci, ed); +		} + +		td_list = td_list_next; +	} +	return stat; +} + +/*-------------------------------------------------------------------------* + * Virtual Root Hub + *-------------------------------------------------------------------------*/ + +#include <usbroothubdes.h> + +/* Hub class-specific descriptor is constructed dynamically */ + + +/*-------------------------------------------------------------------------*/ + +#define OK(x)			len = (x); break +#ifdef DEBUG +#define WR_RH_STAT(x) \ +{ \ +	info("WR:status %#8x", (x)); \ +	writel((x), &gohci.regs->roothub.status); \ +} +#define WR_RH_PORTSTAT(x) \ +{ \ +	info("WR:portstatus[%d] %#8x", wIndex-1, (x)); \ +	writel((x), &gohci.regs->roothub.portstatus[wIndex-1]); \ +} +#else +#define WR_RH_STAT(x) \ +	writel((x), &gohci.regs->roothub.status) +#define WR_RH_PORTSTAT(x)\ +	writel((x), &gohci.regs->roothub.portstatus[wIndex-1]) +#endif +#define RD_RH_STAT	roothub_status(&gohci) +#define RD_RH_PORTSTAT	roothub_portstatus(&gohci, wIndex-1) + +/* request to virtual root hub */ + +int rh_check_port_status(struct ohci *controller) +{ +	__u32 temp, ndp, i; +	int res; + +	res = -1; +	temp = roothub_a(controller); +	ndp = (temp & RH_A_NDP); +	for (i = 0; i < ndp; i++) { +		temp = roothub_portstatus(controller, i); +		/* check for a device disconnect */ +		if (((temp & (RH_PS_PESC | RH_PS_CSC)) == +		     (RH_PS_PESC | RH_PS_CSC)) && ((temp & RH_PS_CCS) == 0)) { +			res = i; +			break; +		} +	} +	return res; +} + +static int ohci_submit_rh_msg(struct usb_device *dev, unsigned long pipe, +			      void *buffer, int transfer_len, +			      struct devrequest *cmd) +{ +	void *data = buffer; +	int leni = transfer_len; +	int len = 0; +	int stat = 0; +	union { +		__u32 word[4]; +		__u16 hword[8]; +		__u8 byte[16]; +	} datab; +	__u8 *data_buf = datab.byte; +	__u16 bmRType_bReq; +	__u16 wValue; +	__u16 wIndex; +	__u16 wLength; + +#ifdef DEBUG +	urb_priv.actual_length = 0; +	pkt_print(dev, pipe, buffer, transfer_len, cmd, "SUB(rh)", +		  usb_pipein(pipe)); +#else +	mdelay(1); +#endif +	if (usb_pipeint(pipe)) { +		info("Root-Hub submit IRQ: NOT implemented"); +		return 0; +	} + +	bmRType_bReq = cmd->requesttype | (cmd->request << 8); +	wValue = m16_swap(cmd->value); +	wIndex = m16_swap(cmd->index); +	wLength = m16_swap(cmd->length); + +	info("Root-Hub: adr: %2x cmd(%1x): %08x %04x %04x %04x", +	     dev->devnum, 8, bmRType_bReq, wValue, wIndex, wLength); + +	switch (bmRType_bReq) { +		/* Request Destination: +		   without flags: Device, +		   RH_INTERFACE: interface, +		   RH_ENDPOINT: endpoint, +		   RH_CLASS means HUB here, +		   RH_OTHER | RH_CLASS  almost ever means HUB_PORT here +		 */ + +	case RH_GET_STATUS: +		datab.hword[0] = m16_swap(1); +		OK(2); +	case RH_GET_STATUS | RH_INTERFACE: +		datab.hword[0] = m16_swap(0); +		OK(2); +	case RH_GET_STATUS | RH_ENDPOINT: +		datab.hword[0] = m16_swap(0); +		OK(2); +	case RH_GET_STATUS | RH_CLASS: +		datab.word[0] = +		    m32_swap(RD_RH_STAT & ~(RH_HS_CRWE | RH_HS_DRWE)); +		OK(4); +	case RH_GET_STATUS | RH_OTHER | RH_CLASS: +		datab.word[0] = m32_swap(RD_RH_PORTSTAT); +		OK(4); + +	case RH_CLEAR_FEATURE | RH_ENDPOINT: +		switch (wValue) { +		case (RH_ENDPOINT_STALL): +			OK(0); +		} +		break; + +	case RH_CLEAR_FEATURE | RH_CLASS: +		switch (wValue) { +		case RH_C_HUB_LOCAL_POWER: +			OK(0); +		case (RH_C_HUB_OVER_CURRENT): +			WR_RH_STAT(RH_HS_OCIC); +			OK(0); +		} +		break; + +	case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS: +		switch (wValue) { +		case (RH_PORT_ENABLE): +			WR_RH_PORTSTAT(RH_PS_CCS); +			OK(0); +		case (RH_PORT_SUSPEND): +			WR_RH_PORTSTAT(RH_PS_POCI); +			OK(0); +		case (RH_PORT_POWER): +			WR_RH_PORTSTAT(RH_PS_LSDA); +			OK(0); +		case (RH_C_PORT_CONNECTION): +			WR_RH_PORTSTAT(RH_PS_CSC); +			OK(0); +		case (RH_C_PORT_ENABLE): +			WR_RH_PORTSTAT(RH_PS_PESC); +			OK(0); +		case (RH_C_PORT_SUSPEND): +			WR_RH_PORTSTAT(RH_PS_PSSC); +			OK(0); +		case (RH_C_PORT_OVER_CURRENT): +			WR_RH_PORTSTAT(RH_PS_OCIC); +			OK(0); +		case (RH_C_PORT_RESET): +			WR_RH_PORTSTAT(RH_PS_PRSC); +			OK(0); +		} +		break; + +	case RH_SET_FEATURE | RH_OTHER | RH_CLASS: +		switch (wValue) { +		case (RH_PORT_SUSPEND): +			WR_RH_PORTSTAT(RH_PS_PSS); +			OK(0); +		case (RH_PORT_RESET):	/* BUG IN HUP CODE ******** */ +			if (RD_RH_PORTSTAT & RH_PS_CCS) +				WR_RH_PORTSTAT(RH_PS_PRS); +			OK(0); +		case (RH_PORT_POWER): +			WR_RH_PORTSTAT(RH_PS_PPS); +			OK(0); +		case (RH_PORT_ENABLE):	/* BUG IN HUP CODE ******** */ +			if (RD_RH_PORTSTAT & RH_PS_CCS) +				WR_RH_PORTSTAT(RH_PS_PES); +			OK(0); +		} +		break; + +	case RH_SET_ADDRESS: +		gohci.rh.devnum = wValue; +		OK(0); + +	case RH_GET_DESCRIPTOR: +		switch ((wValue & 0xff00) >> 8) { +		case (0x01):	/* device descriptor */ +			len = min_t(unsigned int, +				    leni, +				    min_t(unsigned int, +					  sizeof(root_hub_dev_des), wLength)); +			data_buf = root_hub_dev_des; +			OK(len); +		case (0x02):	/* configuration descriptor */ +			len = min_t(unsigned int, +				    leni, +				    min_t(unsigned int, +					  sizeof(root_hub_config_des), +					  wLength)); +			data_buf = root_hub_config_des; +			OK(len); +		case (0x03):	/* string descriptors */ +			if (wValue == 0x0300) { +				len = min_t(unsigned int, +					    leni, +					    min_t(unsigned int, +						  sizeof(root_hub_str_index0), +						  wLength)); +				data_buf = root_hub_str_index0; +				OK(len); +			} +			if (wValue == 0x0301) { +				len = min_t(unsigned int, +					    leni, +					    min_t(unsigned int, +						  sizeof(root_hub_str_index1), +						  wLength)); +				data_buf = root_hub_str_index1; +				OK(len); +			} +		default: +			stat = USB_ST_STALLED; +		} +		break; + +	case RH_GET_DESCRIPTOR | RH_CLASS: +		{ +			__u32 temp = roothub_a(&gohci); + +			data_buf[0] = 9;	/* min length; */ +			data_buf[1] = 0x29; +			data_buf[2] = temp & RH_A_NDP; +			data_buf[3] = 0; +			if (temp & RH_A_PSM) +				/* per-port power switching? */ +				data_buf[3] |= 0x1; +			if (temp & RH_A_NOCP) +				/* no overcurrent reporting? */ +				data_buf[3] |= 0x10; +			else if (temp & RH_A_OCPM) +				/* per-port overcurrent reporting? */ +				data_buf[3] |= 0x8; + +			/* corresponds to data_buf[4-7] */ +			datab.word[1] = 0; +			data_buf[5] = (temp & RH_A_POTPGT) >> 24; +			temp = roothub_b(&gohci); +			data_buf[7] = temp & RH_B_DR; +			if (data_buf[2] < 7) { +				data_buf[8] = 0xff; +			} else { +				data_buf[0] += 2; +				data_buf[8] = (temp & RH_B_DR) >> 8; +				data_buf[10] = data_buf[9] = 0xff; +			} + +			len = min_t(unsigned int, leni, +				    min_t(unsigned int, data_buf[0], wLength)); +			OK(len); +		} + +	case RH_GET_CONFIGURATION: +		*(__u8 *) data_buf = 0x01; +		OK(1); + +	case RH_SET_CONFIGURATION: +		WR_RH_STAT(0x10000); +		OK(0); + +	default: +		dbg("unsupported root hub command"); +		stat = USB_ST_STALLED; +	} + +#ifdef	DEBUG +	ohci_dump_roothub(&gohci, 1); +#else +	mdelay(1); +#endif + +	len = min_t(int, len, leni); +	if (data != data_buf) +		memcpy(data, data_buf, len); +	dev->act_len = len; +	dev->status = stat; + +#ifdef DEBUG +	if (transfer_len) +		urb_priv.actual_length = transfer_len; +	pkt_print(dev, pipe, buffer, transfer_len, cmd, "RET(rh)", +		  0 /*usb_pipein(pipe) */); +#else +	mdelay(1); +#endif + +	return stat; +} + +/*-------------------------------------------------------------------------*/ + +/* common code for handling submit messages - used for all but root hub */ +/* accesses. */ +int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *buffer, +		      int transfer_len, struct devrequest *setup, int interval) +{ +	int stat = 0; +	int maxsize = usb_maxpacket(dev, pipe); +	int timeout; + +	/* device pulled? Shortcut the action. */ +	if (devgone == dev) { +		dev->status = USB_ST_CRC_ERR; +		return 0; +	} +#ifdef DEBUG +	urb_priv.actual_length = 0; +	pkt_print(dev, pipe, buffer, transfer_len, setup, "SUB", +		  usb_pipein(pipe)); +#else +	mdelay(1); +#endif +	if (!maxsize) { +		err("submit_common_message: pipesize for pipe %lx is zero", +		    pipe); +		return -1; +	} + +	if (sohci_submit_job(dev, pipe, buffer, transfer_len, setup, interval) < +	    0) { +		err("sohci_submit_job failed"); +		return -1; +	} + +	mdelay(10); +	/* ohci_dump_status(&gohci); */ + +	/* allow more time for a BULK device to react - some are slow */ +#define BULK_TO	 5000		/* timeout in milliseconds */ +	if (usb_pipebulk(pipe)) +		timeout = BULK_TO; +	else +		timeout = 100; + +	/* wait for it to complete */ +	for (;;) { +		/* check whether the controller is done */ +		stat = hc_interrupt(); + +		if (stat < 0) { +			stat = USB_ST_CRC_ERR; +			break; +		} + +		/* NOTE: since we are not interrupt driven in U-Boot and always +		 * handle only one URB at a time, we cannot assume the +		 * transaction finished on the first successful return from +		 * hc_interrupt().. unless the flag for current URB is set, +		 * meaning that all TD's to/from device got actually +		 * transferred and processed. If the current URB is not +		 * finished we need to re-iterate this loop so as +		 * hc_interrupt() gets called again as there needs to be some +		 * more TD's to process still */ +		if ((stat >= 0) && (stat != 0xff) && (urb_finished)) { +			/* 0xff is returned for an SF-interrupt */ +			break; +		} + +		if (--timeout) { +			mdelay(1); +			if (!urb_finished) +				dbg("\%"); + +		} else { +			err("CTL:TIMEOUT "); +			dbg("submit_common_msg: TO status %x\n", stat); +			stat = USB_ST_CRC_ERR; +			urb_finished = 1; +			break; +		} +	} + +#if 0 +	/* we got an Root Hub Status Change interrupt */ +	if (got_rhsc) { +#ifdef DEBUG +		ohci_dump_roothub(&gohci, 1); +#endif +		got_rhsc = 0; +		/* abuse timeout */ +		timeout = rh_check_port_status(&gohci); +		if (timeout >= 0) { +#if 0			/* this does nothing useful, but leave it here +			   in case that changes */ +			/* the called routine adds 1 to the passed value */ +			usb_hub_port_connect_change(gohci.rh.dev, timeout - 1); +#endif +			/* +			 * XXX +			 * This is potentially dangerous because it assumes +			 * that only one device is ever plugged in! +			 */ +			devgone = dev; +		} +	} +#endif + +	dev->status = stat; +	dev->act_len = transfer_len; + +#ifdef DEBUG +	pkt_print(dev, pipe, buffer, transfer_len, setup, "RET(ctlr)", +		  usb_pipein(pipe)); +#else +	mdelay(1); +#endif + +	/* free TDs in urb_priv */ +	urb_free_priv(&urb_priv); +	return 0; +} + +/* submit routines called from usb.c */ +int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer, +		    int transfer_len) +{ +	info("submit_bulk_msg"); +	return submit_common_msg(dev, pipe, buffer, transfer_len, NULL, 0); +} + +int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, +		       int transfer_len, struct devrequest *setup) +{ +	int maxsize = usb_maxpacket(dev, pipe); + +	info("submit_control_msg"); +#ifdef DEBUG +	urb_priv.actual_length = 0; +	pkt_print(dev, pipe, buffer, transfer_len, setup, "SUB", +		  usb_pipein(pipe)); +#else +	mdelay(1); +#endif +	if (!maxsize) { +		err("submit_control_message: pipesize for pipe %lx is zero", +		    pipe); +		return -1; +	} +	if (((pipe >> 8) & 0x7f) == gohci.rh.devnum) { +		gohci.rh.dev = dev; +		/* root hub - redirect */ +		return ohci_submit_rh_msg(dev, pipe, buffer, transfer_len, +					  setup); +	} + +	return submit_common_msg(dev, pipe, buffer, transfer_len, setup, 0); +} + +int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, +		   int transfer_len, int interval) +{ +	info("submit_int_msg"); +	return -1; +} + +/*-------------------------------------------------------------------------* + * HC functions + *-------------------------------------------------------------------------*/ + +/* reset the HC and BUS */ + +static int hc_reset(struct ohci *ohci) +{ +	int timeout = 30; +	int smm_timeout = 50;	/* 0,5 sec */ + +	if (readl(&ohci->regs->control) & OHCI_CTRL_IR) { +		/* SMM owns the HC - request ownership */ +		writel(OHCI_OCR, &ohci->regs->cmdstatus); +		info("USB HC TakeOver from SMM"); +		while (readl(&ohci->regs->control) & OHCI_CTRL_IR) { +			mdelay(10); +			if (--smm_timeout == 0) { +				err("USB HC TakeOver failed!"); +				return -1; +			} +		} +	} + +	/* Disable HC interrupts */ +	writel(OHCI_INTR_MIE, &ohci->regs->intrdisable); + +	dbg("USB HC reset_hc usb-%s: ctrl = 0x%X ;", +	    ohci->slot_name, readl(&ohci->regs->control)); + +	/* Reset USB (needed by some controllers) */ +	writel(0, &ohci->regs->control); + +	/* HC Reset requires max 10 us delay */ +	writel(OHCI_HCR, &ohci->regs->cmdstatus); +	while ((readl(&ohci->regs->cmdstatus) & OHCI_HCR) != 0) { +		if (--timeout == 0) { +			err("USB HC reset timed out!"); +			return -1; +		} +		udelay(1); +	} +	return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* Start an OHCI controller, set the BUS operational + * enable interrupts + * connect the virtual root hub */ + +static int hc_start(struct ohci *ohci) +{ +	__u32 mask; +	unsigned int fminterval; + +	ohci->disabled = 1; + +	/* Tell the controller where the control and bulk lists are +	 * The lists are empty now. */ + +	writel(0, &ohci->regs->ed_controlhead); +	writel(0, &ohci->regs->ed_bulkhead); + +	/* a reset clears this */ +	writel((__u32) ohci->hcca, &ohci->regs->hcca); + +	fminterval = 0x2edf; +	writel((fminterval * 9) / 10, &ohci->regs->periodicstart); +	fminterval |= ((((fminterval - 210) * 6) / 7) << 16); +	writel(fminterval, &ohci->regs->fminterval); +	writel(0x628, &ohci->regs->lsthresh); + +	/* start controller operations */ +	ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER; +	ohci->disabled = 0; +	writel(ohci->hc_control, &ohci->regs->control); + +	/* disable all interrupts */ +	mask = (OHCI_INTR_SO | OHCI_INTR_WDH | OHCI_INTR_SF | OHCI_INTR_RD | +		OHCI_INTR_UE | OHCI_INTR_FNO | OHCI_INTR_RHSC | +		OHCI_INTR_OC | OHCI_INTR_MIE); +	writel(mask, &ohci->regs->intrdisable); +	/* clear all interrupts */ +	mask &= ~OHCI_INTR_MIE; +	writel(mask, &ohci->regs->intrstatus); +	/* Choose the interrupts we care about now  - but w/o MIE */ +	mask = OHCI_INTR_RHSC | OHCI_INTR_UE | OHCI_INTR_WDH | OHCI_INTR_SO; +	writel(mask, &ohci->regs->intrenable); + +#ifdef	OHCI_USE_NPS +	/* required for AMD-756 and some Mac platforms */ +	writel((roothub_a(ohci) | RH_A_NPS) & ~RH_A_PSM, +	       &ohci->regs->roothub.a); +	writel(RH_HS_LPSC, &ohci->regs->roothub.status); +#endif /* OHCI_USE_NPS */ + +	/* POTPGT delay is bits 24-31, in 2 ms units. */ +	mdelay((roothub_a(ohci) >> 23) & 0x1fe); + +	/* connect the virtual root hub */ +	ohci->rh.devnum = 0; + +	return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* an interrupt happens */ + +static int hc_interrupt(void) +{ +	struct ohci *ohci = &gohci; +	struct ohci_regs *regs = ohci->regs; +	int ints; +	int stat = -1; + +	if ((ohci->hcca->done_head != 0) && +	    !(m32_swap(ohci->hcca->done_head) & 0x01)) { + +		ints = OHCI_INTR_WDH; + +	} else { +		ints = readl(®s->intrstatus); +		if (ints == ~(u32) 0) { +			ohci->disabled++; +			err("%s device removed!", ohci->slot_name); +			return -1; +		} +		ints &= readl(®s->intrenable); +		if (ints == 0) { +			dbg("hc_interrupt: returning..\n"); +			return 0xff; +		} +	} + +	/* dbg("Interrupt: %x frame: %x", ints, +	    le16_to_cpu(ohci->hcca->frame_no)); */ + +	if (ints & OHCI_INTR_RHSC) { +		got_rhsc = 1; +		stat = 0xff; +	} + +	if (ints & OHCI_INTR_UE) { +		ohci->disabled++; +		err("OHCI Unrecoverable Error, controller usb-%s disabled", +		    ohci->slot_name); +		/* e.g. due to PCI Master/Target Abort */ + +#ifdef	DEBUG +		ohci_dump(ohci, 1); +#else +		mdelay(1); +#endif +		/* FIXME: be optimistic, hope that bug won't repeat often. */ +		/* Make some non-interrupt context restart the controller. */ +		/* Count and limit the retries though; either hardware or */ +		/* software errors can go forever... */ +		hc_reset(ohci); +		return -1; +	} + +	if (ints & OHCI_INTR_WDH) { +		mdelay(1); + +		writel(OHCI_INTR_WDH, ®s->intrdisable); +		stat = dl_done_list(&gohci, dl_reverse_done_list(&gohci)); +		writel(OHCI_INTR_WDH, ®s->intrenable); +	} + +	if (ints & OHCI_INTR_SO) { +		dbg("USB Schedule overrun\n"); +		writel(OHCI_INTR_SO, ®s->intrenable); +		stat = -1; +	} + +	/* FIXME:  this assumes SOF (1/ms) interrupts don't get lost... */ +	if (ints & OHCI_INTR_SF) { +		unsigned int frame = m16_swap(ohci->hcca->frame_no) & 1; +		mdelay(1); +		writel(OHCI_INTR_SF, ®s->intrdisable); +		if (ohci->ed_rm_list[frame] != NULL) +			writel(OHCI_INTR_SF, ®s->intrenable); +		stat = 0xff; +	} + +	writel(ints, ®s->intrstatus); +	return stat; +} + +/*-------------------------------------------------------------------------*/ + +/*-------------------------------------------------------------------------*/ + +/* De-allocate all resources.. */ + +static void hc_release_ohci(struct ohci *ohci) +{ +	dbg("USB HC release ohci usb-%s", ohci->slot_name); + +	if (!ohci->disabled) +		hc_reset(ohci); +} + +/*-------------------------------------------------------------------------*/ + +/* + * low level initalisation routine, called from usb.c + */ +static char ohci_inited = 0; + +int usb_lowlevel_init(int index, enum usb_init_type init, void **controller) +{ +	struct s3c24x0_clock_power *clk_power = s3c24x0_get_base_clock_power(); +	struct s3c24x0_gpio *gpio = s3c24x0_get_base_gpio(); + +	/* +	 * Set the 48 MHz UPLL clocking. Values are taken from +	 * "PLL value selection guide", 6-23, s3c2400_UM.pdf. +	 */ +	clk_power->upllcon = ((40 << 12) + (1 << 4) + 2); +	gpio->misccr |= 0x8;	/* 1 = use pads related USB for USB host */ + +	/* +	 * Enable USB host clock. +	 */ +	clk_power->clkcon |= (1 << 4); + +	memset(&gohci, 0, sizeof(struct ohci)); +	memset(&urb_priv, 0, sizeof(struct urb_priv)); + +	/* align the storage */ +	if ((__u32) &ghcca[0] & 0xff) { +		err("HCCA not aligned!!"); +		return -1; +	} +	phcca = &ghcca[0]; +	info("aligned ghcca %p", phcca); +	memset(&ohci_dev, 0, sizeof(struct ohci_device)); +	if ((__u32) &ohci_dev.ed[0] & 0x7) { +		err("EDs not aligned!!"); +		return -1; +	} +	memset(gtd, 0, sizeof(struct td) * (NUM_TD + 1)); +	if ((__u32) gtd & 0x7) { +		err("TDs not aligned!!"); +		return -1; +	} +	ptd = gtd; +	gohci.hcca = phcca; +	memset(phcca, 0, sizeof(struct ohci_hcca)); + +	gohci.disabled = 1; +	gohci.sleeping = 0; +	gohci.irq = -1; +	gohci.regs = (struct ohci_regs *)S3C24X0_USB_HOST_BASE; + +	gohci.flags = 0; +	gohci.slot_name = "s3c2400"; + +	if (hc_reset(&gohci) < 0) { +		hc_release_ohci(&gohci); +		/* Initialization failed */ +		clk_power->clkcon &= ~(1 << 4); +		return -1; +	} + +	/* FIXME this is a second HC reset; why?? */ +	gohci.hc_control = OHCI_USB_RESET; +	writel(gohci.hc_control, &gohci.regs->control); +	mdelay(10); + +	if (hc_start(&gohci) < 0) { +		err("can't start usb-%s", gohci.slot_name); +		hc_release_ohci(&gohci); +		/* Initialization failed */ +		clk_power->clkcon &= ~(1 << 4); +		return -1; +	} +#ifdef	DEBUG +	ohci_dump(&gohci, 1); +#else +	mdelay(1); +#endif +	ohci_inited = 1; +	urb_finished = 1; + +	return 0; +} + +int usb_lowlevel_stop(int index) +{ +	struct s3c24x0_clock_power *clk_power = s3c24x0_get_base_clock_power(); + +	/* this gets called really early - before the controller has */ +	/* even been initialized! */ +	if (!ohci_inited) +		return 0; +	/* TODO release any interrupts, etc. */ +	/* call hc_release_ohci() here ? */ +	hc_reset(&gohci); +	/* may not want to do this */ +	clk_power->clkcon &= ~(1 << 4); +	return 0; +} + +#endif /* defined(CONFIG_USB_OHCI) && defined(CONFIG_S3C24X0) */ + +#if defined(CONFIG_USB_OHCI_NEW) && \ +    defined(CONFIG_SYS_USB_OHCI_CPU_INIT) && \ +    defined(CONFIG_S3C24X0) + +int usb_cpu_init(void) +{ +	struct s3c24x0_clock_power *clk_power = s3c24x0_get_base_clock_power(); +	struct s3c24x0_gpio *gpio = s3c24x0_get_base_gpio(); + +	/* +	 * Set the 48 MHz UPLL clocking. Values are taken from +	 * "PLL value selection guide", 6-23, s3c2400_UM.pdf. +	 */ +	writel((40 << 12) + (1 << 4) + 2, &clk_power->upllcon); +	/* 1 = use pads related USB for USB host */ +	writel(readl(&gpio->misccr) | 0x8, &gpio->misccr); + +	/* +	 * Enable USB host clock. +	 */ +	writel(readl(&clk_power->clkcon) | (1 << 4), &clk_power->clkcon); + +	return 0; +} + +int usb_cpu_stop(void) +{ +	struct s3c24x0_clock_power *clk_power = s3c24x0_get_base_clock_power(); +	/* may not want to do this */ +	writel(readl(&clk_power->clkcon) & ~(1 << 4), &clk_power->clkcon); +	return 0; +} + +int usb_cpu_init_fail(void) +{ +	struct s3c24x0_clock_power *clk_power = s3c24x0_get_base_clock_power(); +	writel(readl(&clk_power->clkcon) & ~(1 << 4), &clk_power->clkcon); +	return 0; +} + +#endif /* defined(CONFIG_USB_OHCI_NEW) && \ +	   defined(CONFIG_SYS_USB_OHCI_CPU_INIT) && \ +	   defined(CONFIG_S3C24X0) */ diff --git a/roms/u-boot/drivers/usb/host/ohci-s3c24xx.h b/roms/u-boot/drivers/usb/host/ohci-s3c24xx.h new file mode 100644 index 00000000..f272d788 --- /dev/null +++ b/roms/u-boot/drivers/usb/host/ohci-s3c24xx.h @@ -0,0 +1,409 @@ +/* + * URB OHCI HCD (Host Controller Driver) for USB. + * + * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at> + * (C) Copyright 2000-2001 David Brownell <dbrownell@users.sourceforge.net> + * + * usb-ohci.h + */ + + +static int cc_to_error[16] = { + +/* mapping of the OHCI CC status to error codes */ +	/* No  Error  */ 0, +	/* CRC Error  */ USB_ST_CRC_ERR, +	/* Bit Stuff  */ USB_ST_BIT_ERR, +	/* Data Togg  */ USB_ST_CRC_ERR, +	/* Stall      */ USB_ST_STALLED, +	/* DevNotResp */ -1, +	/* PIDCheck   */ USB_ST_BIT_ERR, +	/* UnExpPID   */ USB_ST_BIT_ERR, +	/* DataOver   */ USB_ST_BUF_ERR, +	/* DataUnder  */ USB_ST_BUF_ERR, +	/* reservd    */ -1, +	/* reservd    */ -1, +	/* BufferOver */ USB_ST_BUF_ERR, +	/* BuffUnder  */ USB_ST_BUF_ERR, +	/* Not Access */ -1, +	/* Not Access */ -1 +}; + +/* ED States */ +#define ED_NEW		0x00 +#define ED_UNLINK	0x01 +#define ED_OPER		0x02 +#define ED_DEL		0x04 +#define ED_URB_DEL	0x08 + +/* usb_ohci_ed */ +struct ed { +	__u32 hwINFO; +	__u32 hwTailP; +	__u32 hwHeadP; +	__u32 hwNextED; + +	struct ed *ed_prev; +	__u8 int_period; +	__u8 int_branch; +	__u8 int_load; +	__u8 int_interval; +	__u8 state; +	__u8 type; +	__u16 last_iso; +	struct ed *ed_rm_list; + +	struct usb_device *usb_dev; +	__u32 unused[3]; +} __attribute__ ((aligned(16))); + +/* TD info field */ +#define TD_CC			0xf0000000 +#define TD_CC_GET(td_p)		(((td_p) >> 28) & 0x0f) +#define TD_CC_SET(td_p, cc) \ +	{(td_p) = ((td_p) & 0x0fffffff) | (((cc) & 0x0f) << 28)} +#define TD_EC			0x0C000000 +#define TD_T			0x03000000 +#define TD_T_DATA0		0x02000000 +#define TD_T_DATA1		0x03000000 +#define TD_T_TOGGLE		0x00000000 +#define TD_R			0x00040000 +#define TD_DI			0x00E00000 +#define TD_DI_SET(X)		(((X) & 0x07)<< 21) +#define TD_DP			0x00180000 +#define TD_DP_SETUP		0x00000000 +#define TD_DP_IN		0x00100000 +#define TD_DP_OUT		0x00080000 + +#define TD_ISO			0x00010000 +#define TD_DEL			0x00020000 + +/* CC Codes */ +#define TD_CC_NOERROR		0x00 +#define TD_CC_CRC		0x01 +#define TD_CC_BITSTUFFING	0x02 +#define TD_CC_DATATOGGLEM	0x03 +#define TD_CC_STALL		0x04 +#define TD_DEVNOTRESP		0x05 +#define TD_PIDCHECKFAIL	0x06 +#define TD_UNEXPECTEDPID	0x07 +#define TD_DATAOVERRUN		0x08 +#define TD_DATAUNDERRUN	0x09 +#define TD_BUFFEROVERRUN	0x0C +#define TD_BUFFERUNDERRUN	0x0D +#define TD_NOTACCESSED		0x0F + + +#define MAXPSW 1 + +struct td { +	__u32 hwINFO; +	__u32 hwCBP;		/* Current Buffer Pointer */ +	__u32 hwNextTD;		/* Next TD Pointer */ +	__u32 hwBE;		/* Memory Buffer End Pointer */ + +	__u8 unused; +	__u8 index; +	struct ed *ed; +	struct td *next_dl_td; +	struct usb_device *usb_dev; +	int transfer_len; +	__u32 data; + +	__u32 unused2[2]; +} __attribute__ ((aligned(32))); + +#define OHCI_ED_SKIP	(1 << 14) + +/* + * The HCCA (Host Controller Communications Area) is a 256 byte + * structure defined in the OHCI spec. that the host controller is + * told the base address of.  It must be 256-byte aligned. + */ + +#define NUM_INTS 32		/* part of the OHCI standard */ +struct ohci_hcca { +	__u32 int_table[NUM_INTS];	/* Interrupt ED table */ +	__u16 frame_no;		/* current frame number */ +	__u16 pad1;		/* set to 0 on each frame_no change */ +	__u32 done_head;	/* info returned for an interrupt */ +	u8 reserved_for_hc[116]; +} __attribute__ ((aligned(256))); + +/* + * Maximum number of root hub ports. + */ +#define MAX_ROOT_PORTS	15	/* maximum OHCI root hub ports */ + +/* + * This is the structure of the OHCI controller's memory mapped I/O + * region.  This is Memory Mapped I/O.	You must use the readl() and + * writel() macros defined in asm/io.h to access these!! + */ +struct ohci_regs { +	/* control and status registers */ +	__u32 revision; +	__u32 control; +	__u32 cmdstatus; +	__u32 intrstatus; +	__u32 intrenable; +	__u32 intrdisable; +	/* memory pointers */ +	__u32 hcca; +	__u32 ed_periodcurrent; +	__u32 ed_controlhead; +	__u32 ed_controlcurrent; +	__u32 ed_bulkhead; +	__u32 ed_bulkcurrent; +	__u32 donehead; +	/* frame counters */ +	__u32 fminterval; +	__u32 fmremaining; +	__u32 fmnumber; +	__u32 periodicstart; +	__u32 lsthresh; +	/* Root hub ports */ +	struct ohci_roothub_regs { +		__u32 a; +		__u32 b; +		__u32 status; +		__u32 portstatus[MAX_ROOT_PORTS]; +	} roothub; +} __attribute__ ((aligned(32))); + +/* OHCI CONTROL AND STATUS REGISTER MASKS */ + +/* + * HcControl (control) register masks + */ +#define OHCI_CTRL_CBSR	(3 << 0)	/* control/bulk service ratio */ +#define OHCI_CTRL_PLE	(1 << 2)	/* periodic list enable */ +#define OHCI_CTRL_IE	(1 << 3)	/* isochronous enable */ +#define OHCI_CTRL_CLE	(1 << 4)	/* control list enable */ +#define OHCI_CTRL_BLE	(1 << 5)	/* bulk list enable */ +#define OHCI_CTRL_HCFS	(3 << 6)	/* host controller functional state */ +#define OHCI_CTRL_IR	(1 << 8)	/* interrupt routing */ +#define OHCI_CTRL_RWC	(1 << 9)	/* remote wakeup connected */ +#define OHCI_CTRL_RWE	(1 << 10)	/* remote wakeup enable */ + +/* pre-shifted values for HCFS */ +#	define OHCI_USB_RESET	(0 << 6) +#	define OHCI_USB_RESUME	(1 << 6) +#	define OHCI_USB_OPER	(2 << 6) +#	define OHCI_USB_SUSPEND	(3 << 6) + +/* + * HcCommandStatus (cmdstatus) register masks + */ +#define OHCI_HCR	(1 << 0)	/* host controller reset */ +#define OHCI_CLF	(1 << 1)	/* control list filled */ +#define OHCI_BLF	(1 << 2)	/* bulk list filled */ +#define OHCI_OCR	(1 << 3)	/* ownership change request */ +#define OHCI_SOC	(3 << 16)	/* scheduling overrun count */ + +/* + * masks used with interrupt registers: + * HcInterruptStatus (intrstatus) + * HcInterruptEnable (intrenable) + * HcInterruptDisable (intrdisable) + */ +#define OHCI_INTR_SO	(1 << 0)	/* scheduling overrun */ +#define OHCI_INTR_WDH	(1 << 1)	/* writeback of done_head */ +#define OHCI_INTR_SF	(1 << 2)	/* start frame */ +#define OHCI_INTR_RD	(1 << 3)	/* resume detect */ +#define OHCI_INTR_UE	(1 << 4)	/* unrecoverable error */ +#define OHCI_INTR_FNO	(1 << 5)	/* frame number overflow */ +#define OHCI_INTR_RHSC	(1 << 6)	/* root hub status change */ +#define OHCI_INTR_OC	(1 << 30)	/* ownership change */ +#define OHCI_INTR_MIE	(1 << 31)	/* master interrupt enable */ + +/* Virtual Root HUB */ +struct virt_root_hub { +	int devnum;		/* Address of Root Hub endpoint */ +	void *dev;		/* was urb */ +	void *int_addr; +	int send; +	int interval; +}; + +/* USB HUB CONSTANTS (not OHCI-specific; see hub.h) */ + +/* destination of request */ +#define RH_INTERFACE		0x01 +#define RH_ENDPOINT		0x02 +#define RH_OTHER		0x03 + +#define RH_CLASS		0x20 +#define RH_VENDOR		0x40 + +/* Requests: bRequest << 8 | bmRequestType */ +#define RH_GET_STATUS		0x0080 +#define RH_CLEAR_FEATURE	0x0100 +#define RH_SET_FEATURE		0x0300 +#define RH_SET_ADDRESS		0x0500 +#define RH_GET_DESCRIPTOR	0x0680 +#define RH_SET_DESCRIPTOR	0x0700 +#define RH_GET_CONFIGURATION	0x0880 +#define RH_SET_CONFIGURATION	0x0900 +#define RH_GET_STATE		0x0280 +#define RH_GET_INTERFACE	0x0A80 +#define RH_SET_INTERFACE	0x0B00 +#define RH_SYNC_FRAME		0x0C80 +/* Our Vendor Specific Request */ +#define RH_SET_EP		0x2000 + + +/* Hub port features */ +#define RH_PORT_CONNECTION	0x00 +#define RH_PORT_ENABLE		0x01 +#define RH_PORT_SUSPEND		0x02 +#define RH_PORT_OVER_CURRENT	0x03 +#define RH_PORT_RESET		0x04 +#define RH_PORT_POWER		0x08 +#define RH_PORT_LOW_SPEED	0x09 + +#define RH_C_PORT_CONNECTION	0x10 +#define RH_C_PORT_ENABLE	0x11 +#define RH_C_PORT_SUSPEND	0x12 +#define RH_C_PORT_OVER_CURRENT	0x13 +#define RH_C_PORT_RESET		0x14 + +/* Hub features */ +#define RH_C_HUB_LOCAL_POWER	0x00 +#define RH_C_HUB_OVER_CURRENT	0x01 + +#define RH_DEVICE_REMOTE_WAKEUP 0x00 +#define RH_ENDPOINT_STALL	0x01 + +#define RH_ACK			0x01 +#define RH_REQ_ERR		-1 +#define RH_NACK			0x00 + + +/* OHCI ROOT HUB REGISTER MASKS */ + +/* roothub.portstatus [i] bits */ +#define RH_PS_CCS		0x00000001 /* current connect status */ +#define RH_PS_PES		0x00000002 /* port enable status */ +#define RH_PS_PSS		0x00000004 /* port suspend status */ +#define RH_PS_POCI		0x00000008 /* port over current indicator */ +#define RH_PS_PRS		0x00000010 /* port reset status */ +#define RH_PS_PPS		0x00000100 /* port power status */ +#define RH_PS_LSDA		0x00000200 /* low speed device attached */ +#define RH_PS_CSC		0x00010000 /* connect status change */ +#define RH_PS_PESC		0x00020000 /* port enable status change */ +#define RH_PS_PSSC		0x00040000 /* port suspend status change */ +#define RH_PS_OCIC		0x00080000 /* over current indicator change */ +#define RH_PS_PRSC		0x00100000 /* port reset status change */ + +/* roothub.status bits */ +#define RH_HS_LPS		0x00000001 /* local power status */ +#define RH_HS_OCI		0x00000002 /* over current indicator */ +#define RH_HS_DRWE		0x00008000 /* device remote wakeup enable */ +#define RH_HS_LPSC		0x00010000 /* local power status change */ +#define RH_HS_OCIC		0x00020000 /* over current indicator change */ +#define RH_HS_CRWE		0x80000000 /* clear remote wakeup enable */ + +/* roothub.b masks */ +#define RH_B_DR			0x0000ffff /* device removable flags */ +#define RH_B_PPCM		0xffff0000 /* port power control mask */ + +/* roothub.a masks */ +#define	RH_A_NDP		(0xff << 0)  /* number of downstream ports */ +#define	RH_A_PSM		(1 << 8)     /* power switching mode */ +#define	RH_A_NPS		(1 << 9)     /* no power switching */ +#define	RH_A_DT			(1 << 10)    /* device type (mbz) */ +#define	RH_A_OCPM		(1 << 11)    /* over current protection mode */ +#define	RH_A_NOCP		(1 << 12)    /* no over current protection */ +#define	RH_A_POTPGT		(0xff << 24) /* power on to power good time */ + +/* urb */ +#define N_URB_TD 48 +struct urb_priv { +	struct ed *ed; +	__u16 length;		/* number of tds associated with this request */ +	__u16 td_cnt;		/* number of tds already serviced */ +	int state; +	unsigned long pipe; +	int actual_length; +	struct td *td[N_URB_TD];	/* list pointer to all corresponding TDs +					   associated with this request */ +}; +#define URB_DEL 1 + +/* + * This is the full ohci controller description + * + * Note how the "proper" USB information is just + * a subset of what the full implementation needs. (Linus) + */ + + +struct ohci { +	struct ohci_hcca *hcca;	/* hcca */ +	/*dma_addr_t hcca_dma; */ + +	int irq; +	int disabled;		/* e.g. got a UE, we're hung */ +	int sleeping; +	unsigned long flags;	/* for HC bugs */ + +	struct ohci_regs *regs;	/* OHCI controller's memory */ + +	struct ed *ed_rm_list[2];  /* lists of all endpoints to be removed */ +	struct ed *ed_bulktail;    /* last endpoint of bulk list */ +	struct ed *ed_controltail; /* last endpoint of control list */ +	int intrstatus; +	__u32 hc_control;	/* copy of the hc control reg */ +	struct usb_device *dev[32]; +	struct virt_root_hub rh; + +	const char *slot_name; +}; + +#define NUM_EDS 8		/* num of preallocated endpoint descriptors */ + +struct ohci_device { +	struct ed ed[NUM_EDS]; +	int ed_cnt; +}; + +/* hcd */ +/* endpoint */ +static int ep_link(struct ohci *ohci, struct ed *ed); +static int ep_unlink(struct ohci *ohci, struct ed *ed); +static struct ed *ep_add_ed(struct usb_device *usb_dev, unsigned long pipe); + +/*-------------------------------------------------------------------------*/ + +/* we need more TDs than EDs */ +#define NUM_TD 64 + +/* +1 so we can align the storage */ +struct td gtd[NUM_TD + 1]; + +/* pointers to aligned storage */ +struct td *ptd; + +/* TDs ... */ +static inline struct td *td_alloc(struct usb_device *usb_dev) +{ +	int i; +	struct td *td; + +	td = NULL; +	for (i = 0; i < NUM_TD; i++) { +		if (ptd[i].usb_dev == NULL) { +			td = &ptd[i]; +			td->usb_dev = usb_dev; +			break; +		} +	} + +	return td; +} + +static inline void ed_free(struct ed *ed) +{ +	ed->usb_dev = NULL; +} diff --git a/roms/u-boot/drivers/usb/host/ohci.h b/roms/u-boot/drivers/usb/host/ohci.h new file mode 100644 index 00000000..9a4a2c24 --- /dev/null +++ b/roms/u-boot/drivers/usb/host/ohci.h @@ -0,0 +1,491 @@ +/* + * URB OHCI HCD (Host Controller Driver) for USB. + * + * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at> + * (C) Copyright 2000-2001 David Brownell <dbrownell@users.sourceforge.net> + * + * usb-ohci.h + */ + +/* + * e.g. PCI controllers need this + */ +#ifdef CONFIG_SYS_OHCI_SWAP_REG_ACCESS +# define ohci_readl(a) __swap_32(*((volatile u32 *)(a))) +# define ohci_writel(a, b) (*((volatile u32 *)(b)) = __swap_32((volatile u32)a)) +#else +# define ohci_readl(a) (*((volatile u32 *)(a))) +# define ohci_writel(a, b) (*((volatile u32 *)(b)) = ((volatile u32)a)) +#endif /* CONFIG_SYS_OHCI_SWAP_REG_ACCESS */ + +/* functions for doing board or CPU specific setup/cleanup */ +int usb_board_stop(void); + +int usb_cpu_init(void); +int usb_cpu_stop(void); +int usb_cpu_init_fail(void); + +static int cc_to_error[16] = { + +/* mapping of the OHCI CC status to error codes */ +	/* No  Error  */	       0, +	/* CRC Error  */	       USB_ST_CRC_ERR, +	/* Bit Stuff  */	       USB_ST_BIT_ERR, +	/* Data Togg  */	       USB_ST_CRC_ERR, +	/* Stall      */	       USB_ST_STALLED, +	/* DevNotResp */	       -1, +	/* PIDCheck   */	       USB_ST_BIT_ERR, +	/* UnExpPID   */	       USB_ST_BIT_ERR, +	/* DataOver   */	       USB_ST_BUF_ERR, +	/* DataUnder  */	       USB_ST_BUF_ERR, +	/* reservd    */	       -1, +	/* reservd    */	       -1, +	/* BufferOver */	       USB_ST_BUF_ERR, +	/* BuffUnder  */	       USB_ST_BUF_ERR, +	/* Not Access */	       -1, +	/* Not Access */	       -1 +}; + +static const char *cc_to_string[16] = { +	"No Error", +	"CRC: Last data packet from endpoint contained a CRC error.", +	"BITSTUFFING: Last data packet from endpoint contained a bit " \ +		     "stuffing violation", +	"DATATOGGLEMISMATCH: Last packet from endpoint had data toggle PID\n" \ +		     "that did not match the expected value.", +	"STALL: TD was moved to the Done Queue because the endpoint returned" \ +		     " a STALL PID", +	"DEVICENOTRESPONDING: Device did not respond to token (IN) or did\n" \ +		     "not provide a handshake (OUT)", +	"PIDCHECKFAILURE: Check bits on PID from endpoint failed on data PID\n"\ +		     "(IN) or handshake (OUT)", +	"UNEXPECTEDPID: Receive PID was not valid when encountered or PID\n" \ +		     "value is not defined.", +	"DATAOVERRUN: The amount of data returned by the endpoint exceeded\n" \ +		     "either the size of the maximum data packet allowed\n" \ +		     "from the endpoint (found in MaximumPacketSize field\n" \ +		     "of ED) or the remaining buffer size.", +	"DATAUNDERRUN: The endpoint returned less than MaximumPacketSize\n" \ +		     "and that amount was not sufficient to fill the\n" \ +		     "specified buffer", +	"reserved1", +	"reserved2", +	"BUFFEROVERRUN: During an IN, HC received data from endpoint faster\n" \ +		     "than it could be written to system memory", +	"BUFFERUNDERRUN: During an OUT, HC could not retrieve data from\n" \ +		     "system memory fast enough to keep up with data USB " \ +		     "data rate.", +	"NOT ACCESSED: This code is set by software before the TD is placed" \ +		     "on a list to be processed by the HC.(1)", +	"NOT ACCESSED: This code is set by software before the TD is placed" \ +		     "on a list to be processed by the HC.(2)", +}; + +/* ED States */ + +#define ED_NEW		0x00 +#define ED_UNLINK	0x01 +#define ED_OPER		0x02 +#define ED_DEL		0x04 +#define ED_URB_DEL	0x08 + +/* usb_ohci_ed */ +struct ed { +	__u32 hwINFO; +	__u32 hwTailP; +	__u32 hwHeadP; +	__u32 hwNextED; + +	struct ed *ed_prev; +	__u8 int_period; +	__u8 int_branch; +	__u8 int_load; +	__u8 int_interval; +	__u8 state; +	__u8 type; +	__u16 last_iso; +	struct ed *ed_rm_list; + +	struct usb_device *usb_dev; +	void *purb; +	__u32 unused[2]; +} __attribute__((aligned(16))); +typedef struct ed ed_t; + + +/* TD info field */ +#define TD_CC	    0xf0000000 +#define TD_CC_GET(td_p) ((td_p >>28) & 0x0f) +#define TD_CC_SET(td_p, cc) (td_p) = ((td_p) & 0x0fffffff) | (((cc) & 0x0f) << 28) +#define TD_EC	    0x0C000000 +#define TD_T	    0x03000000 +#define TD_T_DATA0  0x02000000 +#define TD_T_DATA1  0x03000000 +#define TD_T_TOGGLE 0x00000000 +#define TD_R	    0x00040000 +#define TD_DI	    0x00E00000 +#define TD_DI_SET(X) (((X) & 0x07)<< 21) +#define TD_DP	    0x00180000 +#define TD_DP_SETUP 0x00000000 +#define TD_DP_IN    0x00100000 +#define TD_DP_OUT   0x00080000 + +#define TD_ISO	    0x00010000 +#define TD_DEL	    0x00020000 + +/* CC Codes */ +#define TD_CC_NOERROR	   0x00 +#define TD_CC_CRC	   0x01 +#define TD_CC_BITSTUFFING  0x02 +#define TD_CC_DATATOGGLEM  0x03 +#define TD_CC_STALL	   0x04 +#define TD_DEVNOTRESP	   0x05 +#define TD_PIDCHECKFAIL	   0x06 +#define TD_UNEXPECTEDPID   0x07 +#define TD_DATAOVERRUN	   0x08 +#define TD_DATAUNDERRUN	   0x09 +#define TD_BUFFEROVERRUN   0x0C +#define TD_BUFFERUNDERRUN  0x0D +#define TD_NOTACCESSED	   0x0F + + +#define MAXPSW 1 + +struct td { +	__u32 hwINFO; +	__u32 hwCBP;		/* Current Buffer Pointer */ +	__u32 hwNextTD;		/* Next TD Pointer */ +	__u32 hwBE;		/* Memory Buffer End Pointer */ + +/* #ifndef CONFIG_MPC5200 /\* this seems wrong *\/ */ +	__u16 hwPSW[MAXPSW]; +/* #endif */ +	__u8 unused; +	__u8 index; +	struct ed *ed; +	struct td *next_dl_td; +	struct usb_device *usb_dev; +	int transfer_len; +	__u32 data; + +	__u32 unused2[2]; +} __attribute__((aligned(32))); +typedef struct td td_t; + +#define OHCI_ED_SKIP	(1 << 14) + +/* + * The HCCA (Host Controller Communications Area) is a 256 byte + * structure defined in the OHCI spec. that the host controller is + * told the base address of.  It must be 256-byte aligned. + */ + +#define NUM_INTS 32	/* part of the OHCI standard */ +struct ohci_hcca { +	__u32	int_table[NUM_INTS];	/* Interrupt ED table */ +#if defined(CONFIG_MPC5200) +	__u16	pad1;			/* set to 0 on each frame_no change */ +	__u16	frame_no;		/* current frame number */ +#else +	__u16	frame_no;		/* current frame number */ +	__u16	pad1;			/* set to 0 on each frame_no change */ +#endif +	__u32	done_head;		/* info returned for an interrupt */ +	u8		reserved_for_hc[116]; +} __attribute__((aligned(256))); + + +/* + * Maximum number of root hub ports. + */ +#ifndef CONFIG_SYS_USB_OHCI_MAX_ROOT_PORTS +# error "CONFIG_SYS_USB_OHCI_MAX_ROOT_PORTS undefined!" +#endif + +/* + * This is the structure of the OHCI controller's memory mapped I/O + * region.  This is Memory Mapped I/O.	You must use the ohci_readl() and + * ohci_writel() macros defined in this file to access these!! + */ +struct ohci_regs { +	/* control and status registers */ +	__u32	revision; +	__u32	control; +	__u32	cmdstatus; +	__u32	intrstatus; +	__u32	intrenable; +	__u32	intrdisable; +	/* memory pointers */ +	__u32	hcca; +	__u32	ed_periodcurrent; +	__u32	ed_controlhead; +	__u32	ed_controlcurrent; +	__u32	ed_bulkhead; +	__u32	ed_bulkcurrent; +	__u32	donehead; +	/* frame counters */ +	__u32	fminterval; +	__u32	fmremaining; +	__u32	fmnumber; +	__u32	periodicstart; +	__u32	lsthresh; +	/* Root hub ports */ +	struct	ohci_roothub_regs { +		__u32	a; +		__u32	b; +		__u32	status; +		__u32	portstatus[CONFIG_SYS_USB_OHCI_MAX_ROOT_PORTS]; +	} roothub; +} __attribute__((aligned(32))); + +/* Some EHCI controls */ +#define EHCI_USBCMD_OFF		0x20 +#define EHCI_USBCMD_HCRESET	(1 << 1) + +/* OHCI CONTROL AND STATUS REGISTER MASKS */ + +/* + * HcControl (control) register masks + */ +#define OHCI_CTRL_CBSR	(3 << 0)	/* control/bulk service ratio */ +#define OHCI_CTRL_PLE	(1 << 2)	/* periodic list enable */ +#define OHCI_CTRL_IE	(1 << 3)	/* isochronous enable */ +#define OHCI_CTRL_CLE	(1 << 4)	/* control list enable */ +#define OHCI_CTRL_BLE	(1 << 5)	/* bulk list enable */ +#define OHCI_CTRL_HCFS	(3 << 6)	/* host controller functional state */ +#define OHCI_CTRL_IR	(1 << 8)	/* interrupt routing */ +#define OHCI_CTRL_RWC	(1 << 9)	/* remote wakeup connected */ +#define OHCI_CTRL_RWE	(1 << 10)	/* remote wakeup enable */ + +/* pre-shifted values for HCFS */ +#	define OHCI_USB_RESET	(0 << 6) +#	define OHCI_USB_RESUME	(1 << 6) +#	define OHCI_USB_OPER	(2 << 6) +#	define OHCI_USB_SUSPEND (3 << 6) + +/* + * HcCommandStatus (cmdstatus) register masks + */ +#define OHCI_HCR	(1 << 0)	/* host controller reset */ +#define OHCI_CLF	(1 << 1)	/* control list filled */ +#define OHCI_BLF	(1 << 2)	/* bulk list filled */ +#define OHCI_OCR	(1 << 3)	/* ownership change request */ +#define OHCI_SOC	(3 << 16)	/* scheduling overrun count */ + +/* + * masks used with interrupt registers: + * HcInterruptStatus (intrstatus) + * HcInterruptEnable (intrenable) + * HcInterruptDisable (intrdisable) + */ +#define OHCI_INTR_SO	(1 << 0)	/* scheduling overrun */ +#define OHCI_INTR_WDH	(1 << 1)	/* writeback of done_head */ +#define OHCI_INTR_SF	(1 << 2)	/* start frame */ +#define OHCI_INTR_RD	(1 << 3)	/* resume detect */ +#define OHCI_INTR_UE	(1 << 4)	/* unrecoverable error */ +#define OHCI_INTR_FNO	(1 << 5)	/* frame number overflow */ +#define OHCI_INTR_RHSC	(1 << 6)	/* root hub status change */ +#define OHCI_INTR_OC	(1 << 30)	/* ownership change */ +#define OHCI_INTR_MIE	(1 << 31)	/* master interrupt enable */ + + +/* Virtual Root HUB */ +struct virt_root_hub { +	int devnum; /* Address of Root Hub endpoint */ +	void *dev;  /* was urb */ +	void *int_addr; +	int send; +	int interval; +}; + +/* USB HUB CONSTANTS (not OHCI-specific; see hub.h) */ + +/* destination of request */ +#define RH_INTERFACE		   0x01 +#define RH_ENDPOINT		   0x02 +#define RH_OTHER		   0x03 + +#define RH_CLASS		   0x20 +#define RH_VENDOR		   0x40 + +/* Requests: bRequest << 8 | bmRequestType */ +#define RH_GET_STATUS		0x0080 +#define RH_CLEAR_FEATURE	0x0100 +#define RH_SET_FEATURE		0x0300 +#define RH_SET_ADDRESS		0x0500 +#define RH_GET_DESCRIPTOR	0x0680 +#define RH_SET_DESCRIPTOR	0x0700 +#define RH_GET_CONFIGURATION	0x0880 +#define RH_SET_CONFIGURATION	0x0900 +#define RH_GET_STATE		0x0280 +#define RH_GET_INTERFACE	0x0A80 +#define RH_SET_INTERFACE	0x0B00 +#define RH_SYNC_FRAME		0x0C80 +/* Our Vendor Specific Request */ +#define RH_SET_EP		0x2000 + + +/* Hub port features */ +#define RH_PORT_CONNECTION	   0x00 +#define RH_PORT_ENABLE		   0x01 +#define RH_PORT_SUSPEND		   0x02 +#define RH_PORT_OVER_CURRENT	   0x03 +#define RH_PORT_RESET		   0x04 +#define RH_PORT_POWER		   0x08 +#define RH_PORT_LOW_SPEED	   0x09 + +#define RH_C_PORT_CONNECTION	   0x10 +#define RH_C_PORT_ENABLE	   0x11 +#define RH_C_PORT_SUSPEND	   0x12 +#define RH_C_PORT_OVER_CURRENT	   0x13 +#define RH_C_PORT_RESET		   0x14 + +/* Hub features */ +#define RH_C_HUB_LOCAL_POWER	   0x00 +#define RH_C_HUB_OVER_CURRENT	   0x01 + +#define RH_DEVICE_REMOTE_WAKEUP	   0x00 +#define RH_ENDPOINT_STALL	   0x01 + +#define RH_ACK			   0x01 +#define RH_REQ_ERR		   -1 +#define RH_NACK			   0x00 + + +/* OHCI ROOT HUB REGISTER MASKS */ + +/* roothub.portstatus [i] bits */ +#define RH_PS_CCS	     0x00000001		/* current connect status */ +#define RH_PS_PES	     0x00000002		/* port enable status*/ +#define RH_PS_PSS	     0x00000004		/* port suspend status */ +#define RH_PS_POCI	     0x00000008		/* port over current indicator */ +#define RH_PS_PRS	     0x00000010		/* port reset status */ +#define RH_PS_PPS	     0x00000100		/* port power status */ +#define RH_PS_LSDA	     0x00000200		/* low speed device attached */ +#define RH_PS_CSC	     0x00010000		/* connect status change */ +#define RH_PS_PESC	     0x00020000		/* port enable status change */ +#define RH_PS_PSSC	     0x00040000		/* port suspend status change */ +#define RH_PS_OCIC	     0x00080000		/* over current indicator change */ +#define RH_PS_PRSC	     0x00100000		/* port reset status change */ + +/* roothub.status bits */ +#define RH_HS_LPS	     0x00000001		/* local power status */ +#define RH_HS_OCI	     0x00000002		/* over current indicator */ +#define RH_HS_DRWE	     0x00008000		/* device remote wakeup enable */ +#define RH_HS_LPSC	     0x00010000		/* local power status change */ +#define RH_HS_OCIC	     0x00020000		/* over current indicator change */ +#define RH_HS_CRWE	     0x80000000		/* clear remote wakeup enable */ + +/* roothub.b masks */ +#define RH_B_DR		0x0000ffff		/* device removable flags */ +#define RH_B_PPCM	0xffff0000		/* port power control mask */ + +/* roothub.a masks */ +#define RH_A_NDP	(0xff << 0)		/* number of downstream ports */ +#define RH_A_PSM	(1 << 8)		/* power switching mode */ +#define RH_A_NPS	(1 << 9)		/* no power switching */ +#define RH_A_DT		(1 << 10)		/* device type (mbz) */ +#define RH_A_OCPM	(1 << 11)		/* over current protection mode */ +#define RH_A_NOCP	(1 << 12)		/* no over current protection */ +#define RH_A_POTPGT	(0xff << 24)		/* power on to power good time */ + +/* urb */ +#define N_URB_TD 48 +typedef struct +{ +	ed_t *ed; +	__u16 length;	/* number of tds associated with this request */ +	__u16 td_cnt;	/* number of tds already serviced */ +	struct usb_device *dev; +	int   state; +	unsigned long pipe; +	void *transfer_buffer; +	int transfer_buffer_length; +	int interval; +	int actual_length; +	int finished; +	td_t *td[N_URB_TD];	/* list pointer to all corresponding TDs associated with this request */ +} urb_priv_t; +#define URB_DEL 1 + +/* + * This is the full ohci controller description + * + * Note how the "proper" USB information is just + * a subset of what the full implementation needs. (Linus) + */ + + +typedef struct ohci { +	struct ohci_hcca *hcca;		/* hcca */ +	/*dma_addr_t hcca_dma;*/ + +	int irq; +	int disabled;			/* e.g. got a UE, we're hung */ +	int sleeping; +	unsigned long flags;		/* for HC bugs */ + +	struct ohci_regs *regs; /* OHCI controller's memory */ + +	int ohci_int_load[32];	 /* load of the 32 Interrupt Chains (for load balancing)*/ +	ed_t *ed_rm_list[2];	 /* lists of all endpoints to be removed */ +	ed_t *ed_bulktail;	 /* last endpoint of bulk list */ +	ed_t *ed_controltail;	 /* last endpoint of control list */ +	int intrstatus; +	__u32 hc_control;		/* copy of the hc control reg */ +	struct usb_device *dev[32]; +	struct virt_root_hub rh; + +	const char	*slot_name; +} ohci_t; + +#define NUM_EDS 8		/* num of preallocated endpoint descriptors */ + +struct ohci_device { +	ed_t	ed[NUM_EDS]; +	int ed_cnt; +}; + +/* hcd */ +/* endpoint */ +static int ep_link(ohci_t * ohci, ed_t * ed); +static int ep_unlink(ohci_t * ohci, ed_t * ed); +static ed_t * ep_add_ed(struct usb_device * usb_dev, unsigned long pipe, +		int interval, int load); + +/*-------------------------------------------------------------------------*/ + +/* we need more TDs than EDs */ +#define NUM_TD 64 + +/* +1 so we can align the storage */ +td_t gtd[NUM_TD+1]; +/* pointers to aligned storage */ +td_t *ptd; + +/* TDs ... */ +static inline struct td * +td_alloc (struct usb_device *usb_dev) +{ +	int i; +	struct td	*td; + +	td = NULL; +	for (i = 0; i < NUM_TD; i++) +	{ +		if (ptd[i].usb_dev == NULL) +		{ +			td = &ptd[i]; +			td->usb_dev = usb_dev; +			break; +		} +	} + +	return td; +} + +static inline void +ed_free (struct ed *ed) +{ +	ed->usb_dev = NULL; +} diff --git a/roms/u-boot/drivers/usb/host/r8a66597-hcd.c b/roms/u-boot/drivers/usb/host/r8a66597-hcd.c new file mode 100644 index 00000000..dfe5423b --- /dev/null +++ b/roms/u-boot/drivers/usb/host/r8a66597-hcd.c @@ -0,0 +1,832 @@ +/* + * R8A66597 HCD (Host Controller Driver) for u-boot + * + * Copyright (C) 2008  Yoshihiro Shimoda <shimoda.yoshihiro@renesas.com> + * + * 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; version 2 of the License. + * + * 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 St, Fifth Floor, Boston, MA  02110-1301  USA + * + */ + +#include <common.h> +#include <usb.h> +#include <asm/io.h> + +#include "r8a66597.h" + +#ifdef R8A66597_DEBUG +#define R8A66597_DPRINT		printf +#else +#define R8A66597_DPRINT(...) +#endif + +static const char hcd_name[] = "r8a66597_hcd"; +static struct r8a66597 gr8a66597; + +static void get_hub_data(struct usb_device *dev, u16 *hub_devnum, u16 *hubport) +{ +	int i; + +	*hub_devnum = 0; +	*hubport = 0; + +	/* check a device connected to root_hub */ +	if ((dev->parent && dev->parent->devnum == 1) || +	    (dev->devnum == 1)) +		return; + +	for (i = 0; i < USB_MAXCHILDREN; i++) { +		if (dev->parent->children[i] == dev) { +			*hub_devnum = (u8)dev->parent->devnum; +			*hubport = i; +			return; +		} +	} + +	printf("get_hub_data error.\n"); +} + +static void set_devadd(struct r8a66597 *r8a66597, u8 r8a66597_address, +			struct usb_device *dev, int port) +{ +	u16 val, usbspd, upphub, hubport; +	unsigned long devadd_reg = get_devadd_addr(r8a66597_address); + +	get_hub_data(dev, &upphub, &hubport); +	usbspd = r8a66597->speed; +	val = (upphub << 11) | (hubport << 8) | (usbspd << 6) | (port & 0x0001); +	r8a66597_write(r8a66597, val, devadd_reg); +} + +static int r8a66597_clock_enable(struct r8a66597 *r8a66597) +{ +	u16 tmp; +	int i = 0; + +#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) +	do { +		r8a66597_write(r8a66597, SCKE, SYSCFG0); +		tmp = r8a66597_read(r8a66597, SYSCFG0); +		if (i++ > 1000) { +			printf("register access fail.\n"); +			return -1; +		} +	} while ((tmp & SCKE) != SCKE); +	r8a66597_write(r8a66597, 0x04, 0x02); +#else +	do { +		r8a66597_write(r8a66597, USBE, SYSCFG0); +		tmp = r8a66597_read(r8a66597, SYSCFG0); +		if (i++ > 1000) { +			printf("register access fail.\n"); +			return -1; +		} +	} while ((tmp & USBE) != USBE); +	r8a66597_bclr(r8a66597, USBE, SYSCFG0); +	r8a66597_mdfy(r8a66597, CONFIG_R8A66597_XTAL, XTAL, SYSCFG0); + +	i = 0; +	r8a66597_bset(r8a66597, XCKE, SYSCFG0); +	do { +		udelay(1000); +		tmp = r8a66597_read(r8a66597, SYSCFG0); +		if (i++ > 500) { +			printf("register access fail.\n"); +			return -1; +		} +	} while ((tmp & SCKE) != SCKE); +#endif	/* #if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) */ + +	return 0; +} + +static void r8a66597_clock_disable(struct r8a66597 *r8a66597) +{ +	r8a66597_bclr(r8a66597, SCKE, SYSCFG0); +	udelay(1); +#if !defined(CONFIG_SUPERH_ON_CHIP_R8A66597) +	r8a66597_bclr(r8a66597, PLLC, SYSCFG0); +	r8a66597_bclr(r8a66597, XCKE, SYSCFG0); +	r8a66597_bclr(r8a66597, USBE, SYSCFG0); +#endif +} + +static void r8a66597_enable_port(struct r8a66597 *r8a66597, int port) +{ +	u16 val; + +	val = port ? DRPD : DCFM | DRPD; +	r8a66597_bset(r8a66597, val, get_syscfg_reg(port)); +	r8a66597_bset(r8a66597, HSE, get_syscfg_reg(port)); + +	r8a66597_write(r8a66597, BURST | CPU_ADR_RD_WR, get_dmacfg_reg(port)); +} + +static void r8a66597_disable_port(struct r8a66597 *r8a66597, int port) +{ +	u16 val, tmp; + +	r8a66597_write(r8a66597, 0, get_intenb_reg(port)); +	r8a66597_write(r8a66597, 0, get_intsts_reg(port)); + +	r8a66597_port_power(r8a66597, port, 0); + +	do { +		tmp = r8a66597_read(r8a66597, SOFCFG) & EDGESTS; +		udelay(640); +	} while (tmp == EDGESTS); + +	val = port ? DRPD : DCFM | DRPD; +	r8a66597_bclr(r8a66597, val, get_syscfg_reg(port)); +	r8a66597_bclr(r8a66597, HSE, get_syscfg_reg(port)); +} + +static int enable_controller(struct r8a66597 *r8a66597) +{ +	int ret, port; + +	ret = r8a66597_clock_enable(r8a66597); +	if (ret < 0) +		return ret; + +	r8a66597_bset(r8a66597, CONFIG_R8A66597_LDRV & LDRV, PINCFG); +	r8a66597_bset(r8a66597, USBE, SYSCFG0); + +	r8a66597_bset(r8a66597, INTL, SOFCFG); +	r8a66597_write(r8a66597, 0, INTENB0); +	r8a66597_write(r8a66597, 0, INTENB1); +	r8a66597_write(r8a66597, 0, INTENB2); + +	r8a66597_bset(r8a66597, CONFIG_R8A66597_ENDIAN & BIGEND, CFIFOSEL); +	r8a66597_bset(r8a66597, CONFIG_R8A66597_ENDIAN & BIGEND, D0FIFOSEL); +	r8a66597_bset(r8a66597, CONFIG_R8A66597_ENDIAN & BIGEND, D1FIFOSEL); +	r8a66597_bset(r8a66597, TRNENSEL, SOFCFG); + +	for (port = 0; port < R8A66597_MAX_ROOT_HUB; port++) +		r8a66597_enable_port(r8a66597, port); + +	return 0; +} + +static void disable_controller(struct r8a66597 *r8a66597) +{ +	int i; + +	if (!(r8a66597_read(r8a66597, SYSCFG0) & USBE)) +		return; + +	r8a66597_write(r8a66597, 0, INTENB0); +	r8a66597_write(r8a66597, 0, INTSTS0); + +	r8a66597_write(r8a66597, 0, D0FIFOSEL); +	r8a66597_write(r8a66597, 0, D1FIFOSEL); +	r8a66597_write(r8a66597, 0, DCPCFG); +	r8a66597_write(r8a66597, 0x40, DCPMAXP); +	r8a66597_write(r8a66597, 0, DCPCTR); + +	for (i = 0; i <= 10; i++) +		r8a66597_write(r8a66597, 0, get_devadd_addr(i)); +	for (i = 1; i <= 5; i++) { +		r8a66597_write(r8a66597, 0, get_pipetre_addr(i)); +		r8a66597_write(r8a66597, 0, get_pipetrn_addr(i)); +	} +	for (i = 1; i < R8A66597_MAX_NUM_PIPE; i++) { +		r8a66597_write(r8a66597, 0, get_pipectr_addr(i)); +		r8a66597_write(r8a66597, i, PIPESEL); +		r8a66597_write(r8a66597, 0, PIPECFG); +		r8a66597_write(r8a66597, 0, PIPEBUF); +		r8a66597_write(r8a66597, 0, PIPEMAXP); +		r8a66597_write(r8a66597, 0, PIPEPERI); +	} + +	for (i = 0; i < R8A66597_MAX_ROOT_HUB; i++) +		r8a66597_disable_port(r8a66597, i); + +	r8a66597_clock_disable(r8a66597); +} + +static void r8a66597_reg_wait(struct r8a66597 *r8a66597, unsigned long reg, +			      u16 mask, u16 loop) +{ +	u16 tmp; +	int i = 0; + +	do { +		tmp = r8a66597_read(r8a66597, reg); +		if (i++ > 1000000) { +			printf("register%lx, loop %x is timeout\n", reg, loop); +			break; +		} +	} while ((tmp & mask) != loop); +} + +static void pipe_buffer_setting(struct r8a66597 *r8a66597, +				struct usb_device *dev, unsigned long pipe) +{ +	u16 val = 0; +	u16 pipenum, bufnum, maxpacket; + +	if (usb_pipein(pipe)) { +		pipenum = BULK_IN_PIPENUM; +		bufnum = BULK_IN_BUFNUM; +		maxpacket = dev->epmaxpacketin[usb_pipeendpoint(pipe)]; +	} else { +		pipenum = BULK_OUT_PIPENUM; +		bufnum = BULK_OUT_BUFNUM; +		maxpacket = dev->epmaxpacketout[usb_pipeendpoint(pipe)]; +	} + +	if (r8a66597->pipe_config & (1 << pipenum)) +		return; +	r8a66597->pipe_config |= (1 << pipenum); + +	r8a66597_bset(r8a66597, ACLRM, get_pipectr_addr(pipenum)); +	r8a66597_bclr(r8a66597, ACLRM, get_pipectr_addr(pipenum)); +	r8a66597_write(r8a66597, pipenum, PIPESEL); + +	/* FIXME: This driver support bulk transfer only. */ +	if (!usb_pipein(pipe)) +		val |= R8A66597_DIR; +	else +		val |= R8A66597_SHTNAK; +	val |= R8A66597_BULK | R8A66597_DBLB | usb_pipeendpoint(pipe); +	r8a66597_write(r8a66597, val, PIPECFG); + +	r8a66597_write(r8a66597, (8 << 10) | bufnum, PIPEBUF); +	r8a66597_write(r8a66597, make_devsel(usb_pipedevice(pipe)) | +				 maxpacket, PIPEMAXP); +	r8a66597_write(r8a66597, 0, PIPEPERI); +	r8a66597_write(r8a66597, SQCLR, get_pipectr_addr(pipenum)); +} + +static int send_setup_packet(struct r8a66597 *r8a66597, struct usb_device *dev, +			     struct devrequest *setup) +{ +	int i; +	unsigned short *p = (unsigned short *)setup; +	unsigned long setup_addr = USBREQ; +	u16 intsts1; +	int timeout = 3000; +	u16 devsel = setup->request == USB_REQ_SET_ADDRESS ? 0 : dev->devnum; + +	r8a66597_write(r8a66597, make_devsel(devsel) | +				 (8 << dev->maxpacketsize), DCPMAXP); +	r8a66597_write(r8a66597, ~(SIGN | SACK), INTSTS1); + +	for (i = 0; i < 4; i++) { +		r8a66597_write(r8a66597, le16_to_cpu(p[i]), setup_addr); +		setup_addr += 2; +	} +	r8a66597_write(r8a66597, ~0x0001, BRDYSTS); +	r8a66597_write(r8a66597, SUREQ, DCPCTR); + +	while (1) { +		intsts1 = r8a66597_read(r8a66597, INTSTS1); +		if (intsts1 & SACK) +			break; +		if (intsts1 & SIGN) { +			printf("setup packet send error\n"); +			return -1; +		} +		if (timeout-- < 0) { +			printf("setup packet timeout\n"); +			return -1; +		} +		udelay(500); +	} + +	return 0; +} + +static int send_bulk_packet(struct r8a66597 *r8a66597, struct usb_device *dev, +			    unsigned long pipe, void *buffer, int transfer_len) +{ +	u16 tmp, bufsize; +	u16 *buf; +	size_t size; + +	R8A66597_DPRINT("%s\n", __func__); + +	r8a66597_mdfy(r8a66597, MBW | BULK_OUT_PIPENUM, +			MBW | CURPIPE, CFIFOSEL); +	r8a66597_reg_wait(r8a66597, CFIFOSEL, CURPIPE, BULK_OUT_PIPENUM); +	tmp = r8a66597_read(r8a66597, CFIFOCTR); +	if ((tmp & FRDY) == 0) { +		printf("%s FRDY is not set (%x)\n", __func__, tmp); +		return -1; +	} + +	/* prepare parameters */ +	bufsize = dev->epmaxpacketout[usb_pipeendpoint(pipe)]; +	buf = (u16 *)(buffer + dev->act_len); +	size = min((int)bufsize, transfer_len - dev->act_len); + +	/* write fifo */ +	r8a66597_write(r8a66597, ~(1 << BULK_OUT_PIPENUM), BEMPSTS); +	if (buffer) { +		r8a66597_write_fifo(r8a66597, CFIFO, buf, size); +		r8a66597_write(r8a66597, BVAL, CFIFOCTR); +	} + +	/* update parameters */ +	dev->act_len += size; + +	r8a66597_mdfy(r8a66597, PID_BUF, PID, +			get_pipectr_addr(BULK_OUT_PIPENUM)); + +	while (!(r8a66597_read(r8a66597, BEMPSTS) & (1 << BULK_OUT_PIPENUM))) +		if (ctrlc()) +			return -1; +	r8a66597_write(r8a66597, ~(1 << BULK_OUT_PIPENUM), BEMPSTS); + +	if (dev->act_len >= transfer_len) +		r8a66597_mdfy(r8a66597, PID_NAK, PID, +				get_pipectr_addr(BULK_OUT_PIPENUM)); + +	return 0; +} + +static int receive_bulk_packet(struct r8a66597 *r8a66597, +			       struct usb_device *dev, +			       unsigned long pipe, +			       void *buffer, int transfer_len) +{ +	u16 tmp; +	u16 *buf; +	const u16 pipenum = BULK_IN_PIPENUM; +	int rcv_len; +	int maxpacket = dev->epmaxpacketin[usb_pipeendpoint(pipe)]; + +	R8A66597_DPRINT("%s\n", __func__); + +	/* prepare */ +	if (dev->act_len == 0) { +		r8a66597_mdfy(r8a66597, PID_NAK, PID, +				get_pipectr_addr(pipenum)); +		r8a66597_write(r8a66597, ~(1 << pipenum), BRDYSTS); + +		r8a66597_write(r8a66597, TRCLR, get_pipetre_addr(pipenum)); +		r8a66597_write(r8a66597, +				(transfer_len + maxpacket - 1) / maxpacket, +				get_pipetrn_addr(pipenum)); +		r8a66597_bset(r8a66597, TRENB, get_pipetre_addr(pipenum)); + +		r8a66597_mdfy(r8a66597, PID_BUF, PID, +				get_pipectr_addr(pipenum)); +	} + +	r8a66597_mdfy(r8a66597, MBW | pipenum, MBW | CURPIPE, CFIFOSEL); +	r8a66597_reg_wait(r8a66597, CFIFOSEL, CURPIPE, pipenum); + +	while (!(r8a66597_read(r8a66597, BRDYSTS) & (1 << pipenum))) +		if (ctrlc()) +			return -1; +	r8a66597_write(r8a66597, ~(1 << pipenum), BRDYSTS); + +	tmp = r8a66597_read(r8a66597, CFIFOCTR); +	if ((tmp & FRDY) == 0) { +		printf("%s FRDY is not set. (%x)\n", __func__, tmp); +		return -1; +	} + +	buf = (u16 *)(buffer + dev->act_len); +	rcv_len = tmp & DTLN; +	dev->act_len += rcv_len; + +	if (buffer) { +		if (rcv_len == 0) +			r8a66597_write(r8a66597, BCLR, CFIFOCTR); +		else +			r8a66597_read_fifo(r8a66597, CFIFO, buf, rcv_len); +	} + +	return 0; +} + +static int receive_control_packet(struct r8a66597 *r8a66597, +				  struct usb_device *dev, +				  void *buffer, int transfer_len) +{ +	u16 tmp; +	int rcv_len; + +	/* FIXME: limit transfer size : 64byte or less */ + +	r8a66597_bclr(r8a66597, R8A66597_DIR, DCPCFG); +	r8a66597_mdfy(r8a66597, 0, ISEL | CURPIPE, CFIFOSEL); +	r8a66597_reg_wait(r8a66597, CFIFOSEL, CURPIPE, 0); +	r8a66597_bset(r8a66597, SQSET, DCPCTR); +	r8a66597_write(r8a66597, BCLR, CFIFOCTR); +	r8a66597_mdfy(r8a66597, PID_BUF, PID, DCPCTR); + +	while (!(r8a66597_read(r8a66597, BRDYSTS) & 0x0001)) +		if (ctrlc()) +			return -1; +	r8a66597_write(r8a66597, ~0x0001, BRDYSTS); + +	r8a66597_mdfy(r8a66597, MBW, MBW | CURPIPE, CFIFOSEL); +	r8a66597_reg_wait(r8a66597, CFIFOSEL, CURPIPE, 0); + +	tmp = r8a66597_read(r8a66597, CFIFOCTR); +	if ((tmp & FRDY) == 0) { +		printf("%s FRDY is not set. (%x)\n", __func__, tmp); +		return -1; +	} + +	rcv_len = tmp & DTLN; +	dev->act_len += rcv_len; + +	r8a66597_mdfy(r8a66597, PID_NAK, PID, DCPCTR); + +	if (buffer) { +		if (rcv_len == 0) +			r8a66597_write(r8a66597, BCLR, DCPCTR); +		else +			r8a66597_read_fifo(r8a66597, CFIFO, buffer, rcv_len); +	} + +	return 0; +} + +static int send_status_packet(struct r8a66597 *r8a66597, +			       unsigned long pipe) +{ +	r8a66597_bset(r8a66597, SQSET, DCPCTR); +	r8a66597_mdfy(r8a66597, PID_NAK, PID, DCPCTR); + +	if (usb_pipein(pipe)) { +		r8a66597_bset(r8a66597, R8A66597_DIR, DCPCFG); +		r8a66597_mdfy(r8a66597, ISEL, ISEL | CURPIPE, CFIFOSEL); +		r8a66597_reg_wait(r8a66597, CFIFOSEL, CURPIPE, 0); +		r8a66597_write(r8a66597, ~BEMP0, BEMPSTS); +		r8a66597_write(r8a66597, BCLR | BVAL, CFIFOCTR); +	} else { +		r8a66597_bclr(r8a66597, R8A66597_DIR, DCPCFG); +		r8a66597_mdfy(r8a66597, 0, ISEL | CURPIPE, CFIFOSEL); +		r8a66597_reg_wait(r8a66597, CFIFOSEL, CURPIPE, 0); +		r8a66597_write(r8a66597, BCLR, CFIFOCTR); +	} +	r8a66597_mdfy(r8a66597, PID_BUF, PID, DCPCTR); + +	while (!(r8a66597_read(r8a66597, BEMPSTS) & 0x0001)) +		if (ctrlc()) +			return -1; + +	return 0; +} + +static void r8a66597_check_syssts(struct r8a66597 *r8a66597, int port) +{ +	int count = R8A66597_MAX_SAMPLING; +	unsigned short syssts, old_syssts; + +	R8A66597_DPRINT("%s\n", __func__); + +	old_syssts = r8a66597_read(r8a66597, get_syssts_reg(port) & LNST); +	while (count > 0) { +		mdelay(R8A66597_RH_POLL_TIME); + +		syssts = r8a66597_read(r8a66597, get_syssts_reg(port) & LNST); +		if (syssts == old_syssts) { +			count--; +		} else { +			count = R8A66597_MAX_SAMPLING; +			old_syssts = syssts; +		} +	} +} + +static void r8a66597_bus_reset(struct r8a66597 *r8a66597, int port) +{ +	mdelay(10); +	r8a66597_mdfy(r8a66597, USBRST, USBRST | UACT, get_dvstctr_reg(port)); +	mdelay(50); +	r8a66597_mdfy(r8a66597, UACT, USBRST | UACT, get_dvstctr_reg(port)); +	mdelay(50); +} + +static int check_usb_device_connecting(struct r8a66597 *r8a66597) +{ +	int timeout = 10000;	/* 100usec * 10000 = 1sec */ +	int i; + +	for (i = 0; i < 5; i++) { +		/* check a usb cable connect */ +		while (!(r8a66597_read(r8a66597, INTSTS1) & ATTCH)) { +			if (timeout-- < 0) { +				printf("%s timeout.\n", __func__); +				return -1; +			} +			udelay(100); +		} + +		/* check a data line */ +		r8a66597_check_syssts(r8a66597, 0); + +		r8a66597_bus_reset(r8a66597, 0); +		r8a66597->speed = get_rh_usb_speed(r8a66597, 0); + +		if (!(r8a66597_read(r8a66597, INTSTS1) & DTCH)) { +			r8a66597->port_change = USB_PORT_STAT_C_CONNECTION; +			r8a66597->port_status = USB_PORT_STAT_CONNECTION | +						USB_PORT_STAT_ENABLE; +			return 0;	/* success */ +		} + +		R8A66597_DPRINT("USB device has detached. retry = %d\n", i); +		r8a66597_write(r8a66597, ~DTCH, INTSTS1); +	} + +	return -1;	/* fail */ +} + +/* based on usb_ohci.c */ +#define min_t(type, x, y) \ +		({ type __x = (x); type __y = (y); __x < __y ? __x : __y; }) +/*-------------------------------------------------------------------------* + * Virtual Root Hub + *-------------------------------------------------------------------------*/ + +#include <usbroothubdes.h> + +static int r8a66597_submit_rh_msg(struct usb_device *dev, unsigned long pipe, +			void *buffer, int transfer_len, struct devrequest *cmd) +{ +	struct r8a66597 *r8a66597 = &gr8a66597; +	int leni = transfer_len; +	int len = 0; +	int stat = 0; +	__u16 bmRType_bReq; +	__u16 wValue; +	__u16 wLength; +	unsigned char data[32]; + +	R8A66597_DPRINT("%s\n", __func__); + +	if (usb_pipeint(pipe)) { +		printf("Root-Hub submit IRQ: NOT implemented"); +		return 0; +	} + +	bmRType_bReq  = cmd->requesttype | (cmd->request << 8); +	wValue	      = cpu_to_le16 (cmd->value); +	wLength	      = cpu_to_le16 (cmd->length); + +	switch (bmRType_bReq) { +	case RH_GET_STATUS: +		*(__u16 *)buffer = cpu_to_le16(1); +		len = 2; +		break; +	case RH_GET_STATUS | RH_INTERFACE: +		*(__u16 *)buffer = cpu_to_le16(0); +		len = 2; +		break; +	case RH_GET_STATUS | RH_ENDPOINT: +		*(__u16 *)buffer = cpu_to_le16(0); +		len = 2; +		break; +	case RH_GET_STATUS | RH_CLASS: +		*(__u32 *)buffer = cpu_to_le32(0); +		len = 4; +		break; +	case RH_GET_STATUS | RH_OTHER | RH_CLASS: +		*(__u32 *)buffer = cpu_to_le32(r8a66597->port_status | +						(r8a66597->port_change << 16)); +		len = 4; +		break; +	case RH_CLEAR_FEATURE | RH_ENDPOINT: +	case RH_CLEAR_FEATURE | RH_CLASS: +		break; + +	case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS: +		switch (wValue) { +		case RH_C_PORT_CONNECTION: +			r8a66597->port_change &= ~USB_PORT_STAT_C_CONNECTION; +			break; +		} +		break; + +	case RH_SET_FEATURE | RH_OTHER | RH_CLASS: +		switch (wValue) { +		case (RH_PORT_SUSPEND): +			break; +		case (RH_PORT_RESET): +			r8a66597_bus_reset(r8a66597, 0); +			break; +		case (RH_PORT_POWER): +			break; +		case (RH_PORT_ENABLE): +			break; +		} +		break; +	case RH_SET_ADDRESS: +		gr8a66597.rh_devnum = wValue; +		break; +	case RH_GET_DESCRIPTOR: +		switch ((wValue & 0xff00) >> 8) { +		case (0x01): /* device descriptor */ +			len = min_t(unsigned int, +				  leni, +				  min_t(unsigned int, +				      sizeof(root_hub_dev_des), +				      wLength)); +			memcpy(buffer, root_hub_dev_des, len); +			break; +		case (0x02): /* configuration descriptor */ +			len = min_t(unsigned int, +				  leni, +				  min_t(unsigned int, +				      sizeof(root_hub_config_des), +				      wLength)); +			memcpy(buffer, root_hub_config_des, len); +			break; +		case (0x03): /* string descriptors */ +			if (wValue == 0x0300) { +				len = min_t(unsigned int, +					  leni, +					  min_t(unsigned int, +					      sizeof(root_hub_str_index0), +					      wLength)); +				memcpy(buffer, root_hub_str_index0, len); +			} +			if (wValue == 0x0301) { +				len = min_t(unsigned int, +					  leni, +					  min_t(unsigned int, +					      sizeof(root_hub_str_index1), +					      wLength)); +				memcpy(buffer, root_hub_str_index1, len); +			} +			break; +		default: +			stat = USB_ST_STALLED; +		} +		break; + +	case RH_GET_DESCRIPTOR | RH_CLASS: +	{ +		__u32 temp = 0x00000001; + +		data[0] = 9;		/* min length; */ +		data[1] = 0x29; +		data[2] = temp & RH_A_NDP; +		data[3] = 0; +		if (temp & RH_A_PSM) +			data[3] |= 0x1; +		if (temp & RH_A_NOCP) +			data[3] |= 0x10; +		else if (temp & RH_A_OCPM) +			data[3] |= 0x8; + +		/* corresponds to data[4-7] */ +		data[5] = (temp & RH_A_POTPGT) >> 24; +		data[7] = temp & RH_B_DR; +		if (data[2] < 7) { +			data[8] = 0xff; +		} else { +			data[0] += 2; +			data[8] = (temp & RH_B_DR) >> 8; +			data[10] = data[9] = 0xff; +		} + +		len = min_t(unsigned int, leni, +			    min_t(unsigned int, data[0], wLength)); +		memcpy(buffer, data, len); +		break; +	} + +	case RH_GET_CONFIGURATION: +		*(__u8 *) buffer = 0x01; +		len = 1; +		break; +	case RH_SET_CONFIGURATION: +		break; +	default: +		R8A66597_DPRINT("unsupported root hub command"); +		stat = USB_ST_STALLED; +	} + +	mdelay(1); + +	len = min_t(int, len, leni); + +	dev->act_len = len; +	dev->status = stat; + +	return stat; +} + +int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer, +		    int transfer_len) +{ +	struct r8a66597 *r8a66597 = &gr8a66597; +	int ret = 0; + +	R8A66597_DPRINT("%s\n", __func__); +	R8A66597_DPRINT("pipe = %08x, buffer = %p, len = %d, devnum = %d\n", +			pipe, buffer, transfer_len, dev->devnum); + +	set_devadd(r8a66597, dev->devnum, dev, 0); + +	pipe_buffer_setting(r8a66597, dev, pipe); + +	dev->act_len = 0; +	while (dev->act_len < transfer_len && ret == 0) { +		if (ctrlc()) +			return -1; + +		if (usb_pipein(pipe)) +			ret = receive_bulk_packet(r8a66597, dev, pipe, buffer, +							transfer_len); +		else +			ret = send_bulk_packet(r8a66597, dev, pipe, buffer, +							transfer_len); +	} + +	if (ret == 0) +		dev->status = 0; + +	return ret; +} + +int submit_control_msg(struct usb_device *dev, unsigned long pipe, +		       void *buffer, int transfer_len, struct devrequest *setup) +{ +	struct r8a66597 *r8a66597 = &gr8a66597; +	u16 r8a66597_address = setup->request == USB_REQ_SET_ADDRESS ? +					0 : dev->devnum; + +	R8A66597_DPRINT("%s\n", __func__); +	if (usb_pipedevice(pipe) == r8a66597->rh_devnum) +		return r8a66597_submit_rh_msg(dev, pipe, buffer, transfer_len, +						setup); + +	R8A66597_DPRINT("%s: setup\n", __func__); +	set_devadd(r8a66597, r8a66597_address, dev, 0); + +	if (send_setup_packet(r8a66597, dev, setup) < 0) { +		printf("setup packet send error\n"); +		return -1; +	} + +	dev->act_len = 0; +	if (usb_pipein(pipe)) +		if (receive_control_packet(r8a66597, dev, buffer, +						transfer_len) < 0) +			return -1; + +	if (send_status_packet(r8a66597, pipe) < 0) +		return -1; + +	dev->status = 0; + +	return 0; +} + +int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, +			int transfer_len, int interval) +{ +	/* no implement */ +	R8A66597_DPRINT("%s\n", __func__); +	return 0; +} + +int usb_lowlevel_init(int index, enum usb_init_type init, void **controller) +{ +	struct r8a66597 *r8a66597 = &gr8a66597; + +	R8A66597_DPRINT("%s\n", __func__); + +	memset(r8a66597, 0, sizeof(r8a66597)); +	r8a66597->reg = CONFIG_R8A66597_BASE_ADDR; + +	disable_controller(r8a66597); +	mdelay(100); + +	enable_controller(r8a66597); +	r8a66597_port_power(r8a66597, 0 , 1); + +	/* check usb device */ +	check_usb_device_connecting(r8a66597); + +	mdelay(50); + +	return 0; +} + +int usb_lowlevel_stop(int index) +{ +	disable_controller(&gr8a66597); + +	return 0; +} diff --git a/roms/u-boot/drivers/usb/host/r8a66597.h b/roms/u-boot/drivers/usb/host/r8a66597.h new file mode 100644 index 00000000..ca1b6715 --- /dev/null +++ b/roms/u-boot/drivers/usb/host/r8a66597.h @@ -0,0 +1,659 @@ +/* + * R8A66597 HCD (Host Controller Driver) for u-boot + * + * Copyright (C) 2008  Yoshihiro Shimoda <shimoda.yoshihiro@renesas.com> + * + * 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; version 2 of the License. + * + * 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 St, Fifth Floor, Boston, MA  02110-1301  USA + * + */ + +#ifndef __R8A66597_H__ +#define __R8A66597_H__ + +#define SYSCFG0		0x00 +#define SYSCFG1		0x02 +#define SYSSTS0		0x04 +#define SYSSTS1		0x06 +#define DVSTCTR0	0x08 +#define DVSTCTR1	0x0A +#define TESTMODE	0x0C +#define PINCFG		0x0E +#define DMA0CFG		0x10 +#define DMA1CFG		0x12 +#define CFIFO		0x14 +#define D0FIFO		0x18 +#define D1FIFO		0x1C +#define CFIFOSEL	0x20 +#define CFIFOCTR	0x22 +#define CFIFOSIE	0x24 +#define D0FIFOSEL	0x28 +#define D0FIFOCTR	0x2A +#define D1FIFOSEL	0x2C +#define D1FIFOCTR	0x2E +#define INTENB0		0x30 +#define INTENB1		0x32 +#define INTENB2		0x34 +#define BRDYENB		0x36 +#define NRDYENB		0x38 +#define BEMPENB		0x3A +#define SOFCFG		0x3C +#define INTSTS0		0x40 +#define INTSTS1		0x42 +#define INTSTS2		0x44 +#define BRDYSTS		0x46 +#define NRDYSTS		0x48 +#define BEMPSTS		0x4A +#define FRMNUM		0x4C +#define UFRMNUM		0x4E +#define USBADDR		0x50 +#define USBREQ		0x54 +#define USBVAL		0x56 +#define USBINDX		0x58 +#define USBLENG		0x5A +#define DCPCFG		0x5C +#define DCPMAXP		0x5E +#define DCPCTR		0x60 +#define PIPESEL		0x64 +#define PIPECFG		0x68 +#define PIPEBUF		0x6A +#define PIPEMAXP	0x6C +#define PIPEPERI	0x6E +#define PIPE1CTR	0x70 +#define PIPE2CTR	0x72 +#define PIPE3CTR	0x74 +#define PIPE4CTR	0x76 +#define PIPE5CTR	0x78 +#define PIPE6CTR	0x7A +#define PIPE7CTR	0x7C +#define PIPE8CTR	0x7E +#define PIPE9CTR	0x80 +#define PIPE1TRE	0x90 +#define PIPE1TRN	0x92 +#define PIPE2TRE	0x94 +#define PIPE2TRN	0x96 +#define PIPE3TRE	0x98 +#define PIPE3TRN	0x9A +#define PIPE4TRE	0x9C +#define	PIPE4TRN	0x9E +#define	PIPE5TRE	0xA0 +#define	PIPE5TRN	0xA2 +#define DEVADD0		0xD0 +#define DEVADD1		0xD2 +#define DEVADD2		0xD4 +#define DEVADD3		0xD6 +#define DEVADD4		0xD8 +#define DEVADD5		0xDA +#define DEVADD6		0xDC +#define DEVADD7		0xDE +#define DEVADD8		0xE0 +#define DEVADD9		0xE2 +#define DEVADDA		0xE4 + +/* System Configuration Control Register */ +#define	XTAL		0xC000	/* b15-14: Crystal selection */ +#define	  XTAL48	 0x8000	  /* 48MHz */ +#define	  XTAL24	 0x4000	  /* 24MHz */ +#define	  XTAL12	 0x0000	  /* 12MHz */ +#define	XCKE		0x2000	/* b13: External clock enable */ +#define	PLLC		0x0800	/* b11: PLL control */ +#define	SCKE		0x0400	/* b10: USB clock enable */ +#define	PCSDIS		0x0200	/* b9: not CS wakeup */ +#define	LPSME		0x0100	/* b8: Low power sleep mode */ +#define	HSE		0x0080	/* b7: Hi-speed enable */ +#define	DCFM		0x0040	/* b6: Controller function select  */ +#define	DRPD		0x0020	/* b5: D+/- pull down control */ +#define	DPRPU		0x0010	/* b4: D+ pull up control */ +#define	USBE		0x0001	/* b0: USB module operation enable */ + +/* System Configuration Status Register */ +#define	OVCBIT		0x8000	/* b15-14: Over-current bit */ +#define	OVCMON		0xC000	/* b15-14: Over-current monitor */ +#define	SOFEA		0x0020	/* b5: SOF monitor */ +#define	IDMON		0x0004	/* b3: ID-pin monitor */ +#define	LNST		0x0003	/* b1-0: D+, D- line status */ +#define	  SE1		 0x0003	  /* SE1 */ +#define	  FS_KSTS	 0x0002	  /* Full-Speed K State */ +#define	  FS_JSTS	 0x0001	  /* Full-Speed J State */ +#define	  LS_JSTS	 0x0002	  /* Low-Speed J State */ +#define	  LS_KSTS	 0x0001	  /* Low-Speed K State */ +#define	  SE0		 0x0000	  /* SE0 */ + +/* Device State Control Register */ +#define	EXTLP0		0x0400	/* b10: External port */ +#define	VBOUT		0x0200	/* b9: VBUS output */ +#define	WKUP		0x0100	/* b8: Remote wakeup */ +#define	RWUPE		0x0080	/* b7: Remote wakeup sense */ +#define	USBRST		0x0040	/* b6: USB reset enable */ +#define	RESUME		0x0020	/* b5: Resume enable */ +#define	UACT		0x0010	/* b4: USB bus enable */ +#define	RHST		0x0007	/* b1-0: Reset handshake status */ +#define	  HSPROC	 0x0004	  /* HS handshake is processing */ +#define	  HSMODE	 0x0003	  /* Hi-Speed mode */ +#define	  FSMODE	 0x0002	  /* Full-Speed mode */ +#define	  LSMODE	 0x0001	  /* Low-Speed mode */ +#define	  UNDECID	 0x0000	  /* Undecided */ + +/* Test Mode Register */ +#define	UTST			0x000F	/* b3-0: Test select */ +#define	  H_TST_PACKET		 0x000C	  /* HOST TEST Packet */ +#define	  H_TST_SE0_NAK		 0x000B	  /* HOST TEST SE0 NAK */ +#define	  H_TST_K		 0x000A	  /* HOST TEST K */ +#define	  H_TST_J		 0x0009	  /* HOST TEST J */ +#define	  H_TST_NORMAL		 0x0000	  /* HOST Normal Mode */ +#define	  P_TST_PACKET		 0x0004	  /* PERI TEST Packet */ +#define	  P_TST_SE0_NAK		 0x0003	  /* PERI TEST SE0 NAK */ +#define	  P_TST_K		 0x0002	  /* PERI TEST K */ +#define	  P_TST_J		 0x0001	  /* PERI TEST J */ +#define	  P_TST_NORMAL		 0x0000	  /* PERI Normal Mode */ + +/* Data Pin Configuration Register */ +#define	LDRV			0x8000	/* b15: Drive Current Adjust */ +#define	  VIF1			  0x0000		/* VIF = 1.8V */ +#define	  VIF3			  0x8000		/* VIF = 3.3V */ +#define	INTA			0x0001	/* b1: USB INT-pin active */ + +/* DMAx Pin Configuration Register */ +#define	DREQA			0x4000	/* b14: Dreq active select */ +#define	BURST			0x2000	/* b13: Burst mode */ +#define	DACKA			0x0400	/* b10: Dack active select */ +#define	DFORM			0x0380	/* b9-7: DMA mode select */ +#define	  CPU_ADR_RD_WR		 0x0000	  /* Address + RD/WR mode (CPU bus) */ +#define	  CPU_DACK_RD_WR	 0x0100	  /* DACK + RD/WR mode (CPU bus) */ +#define	  CPU_DACK_ONLY		 0x0180	  /* DACK only mode (CPU bus) */ +#define	  SPLIT_DACK_ONLY	 0x0200	  /* DACK only mode (SPLIT bus) */ +#define	DENDA			0x0040	/* b6: Dend active select */ +#define	PKTM			0x0020	/* b5: Packet mode */ +#define	DENDE			0x0010	/* b4: Dend enable */ +#define	OBUS			0x0004	/* b2: OUTbus mode */ + +/* CFIFO/DxFIFO Port Select Register */ +#define	RCNT		0x8000	/* b15: Read count mode */ +#define	REW		0x4000	/* b14: Buffer rewind */ +#define	DCLRM		0x2000	/* b13: DMA buffer clear mode */ +#define	DREQE		0x1000	/* b12: DREQ output enable */ +#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) +#define	MBW		0x0800 +#else +#define	MBW		0x0400	/* b10: Maximum bit width for FIFO access */ +#endif +#define	  MBW_8		 0x0000	  /*  8bit */ +#define	  MBW_16	 0x0400	  /* 16bit */ +#define	BIGEND		0x0100	/* b8: Big endian mode */ +#define	  BYTE_LITTLE	 0x0000		/* little dendian */ +#define	  BYTE_BIG	 0x0100		/* big endifan */ +#define	ISEL		0x0020	/* b5: DCP FIFO port direction select */ +#define	CURPIPE		0x000F	/* b2-0: PIPE select */ + +/* CFIFO/DxFIFO Port Control Register */ +#define	BVAL		0x8000	/* b15: Buffer valid flag */ +#define	BCLR		0x4000	/* b14: Buffer clear */ +#define	FRDY		0x2000	/* b13: FIFO ready */ +#define	DTLN		0x0FFF	/* b11-0: FIFO received data length */ + +/* Interrupt Enable Register 0 */ +#define	VBSE	0x8000	/* b15: VBUS interrupt */ +#define	RSME	0x4000	/* b14: Resume interrupt */ +#define	SOFE	0x2000	/* b13: Frame update interrupt */ +#define	DVSE	0x1000	/* b12: Device state transition interrupt */ +#define	CTRE	0x0800	/* b11: Control transfer stage transition interrupt */ +#define	BEMPE	0x0400	/* b10: Buffer empty interrupt */ +#define	NRDYE	0x0200	/* b9: Buffer not ready interrupt */ +#define	BRDYE	0x0100	/* b8: Buffer ready interrupt */ + +/* Interrupt Enable Register 1 */ +#define	OVRCRE		0x8000	/* b15: Over-current interrupt */ +#define	BCHGE		0x4000	/* b14: USB us chenge interrupt */ +#define	DTCHE		0x1000	/* b12: Detach sense interrupt */ +#define	ATTCHE		0x0800	/* b11: Attach sense interrupt */ +#define	EOFERRE		0x0040	/* b6: EOF error interrupt */ +#define	SIGNE		0x0020	/* b5: SETUP IGNORE interrupt */ +#define	SACKE		0x0010	/* b4: SETUP ACK interrupt */ + +/* BRDY Interrupt Enable/Status Register */ +#define	BRDY9		0x0200	/* b9: PIPE9 */ +#define	BRDY8		0x0100	/* b8: PIPE8 */ +#define	BRDY7		0x0080	/* b7: PIPE7 */ +#define	BRDY6		0x0040	/* b6: PIPE6 */ +#define	BRDY5		0x0020	/* b5: PIPE5 */ +#define	BRDY4		0x0010	/* b4: PIPE4 */ +#define	BRDY3		0x0008	/* b3: PIPE3 */ +#define	BRDY2		0x0004	/* b2: PIPE2 */ +#define	BRDY1		0x0002	/* b1: PIPE1 */ +#define	BRDY0		0x0001	/* b1: PIPE0 */ + +/* NRDY Interrupt Enable/Status Register */ +#define	NRDY9		0x0200	/* b9: PIPE9 */ +#define	NRDY8		0x0100	/* b8: PIPE8 */ +#define	NRDY7		0x0080	/* b7: PIPE7 */ +#define	NRDY6		0x0040	/* b6: PIPE6 */ +#define	NRDY5		0x0020	/* b5: PIPE5 */ +#define	NRDY4		0x0010	/* b4: PIPE4 */ +#define	NRDY3		0x0008	/* b3: PIPE3 */ +#define	NRDY2		0x0004	/* b2: PIPE2 */ +#define	NRDY1		0x0002	/* b1: PIPE1 */ +#define	NRDY0		0x0001	/* b1: PIPE0 */ + +/* BEMP Interrupt Enable/Status Register */ +#define	BEMP9		0x0200	/* b9: PIPE9 */ +#define	BEMP8		0x0100	/* b8: PIPE8 */ +#define	BEMP7		0x0080	/* b7: PIPE7 */ +#define	BEMP6		0x0040	/* b6: PIPE6 */ +#define	BEMP5		0x0020	/* b5: PIPE5 */ +#define	BEMP4		0x0010	/* b4: PIPE4 */ +#define	BEMP3		0x0008	/* b3: PIPE3 */ +#define	BEMP2		0x0004	/* b2: PIPE2 */ +#define	BEMP1		0x0002	/* b1: PIPE1 */ +#define	BEMP0		0x0001	/* b0: PIPE0 */ + +/* SOF Pin Configuration Register */ +#define	TRNENSEL	0x0100	/* b8: Select transaction enable period */ +#define	BRDYM		0x0040	/* b6: BRDY clear timing */ +#define	INTL		0x0020	/* b5: Interrupt sense select */ +#define	EDGESTS		0x0010	/* b4:  */ +#define	SOFMODE		0x000C	/* b3-2: SOF pin select */ +#define	  SOF_125US	 0x0008	  /* SOF OUT 125us Frame Signal */ +#define	  SOF_1MS	 0x0004	  /* SOF OUT 1ms Frame Signal */ +#define	  SOF_DISABLE	 0x0000	  /* SOF OUT Disable */ + +/* Interrupt Status Register 0 */ +#define	VBINT	0x8000	/* b15: VBUS interrupt */ +#define	RESM	0x4000	/* b14: Resume interrupt */ +#define	SOFR	0x2000	/* b13: SOF frame update interrupt */ +#define	DVST	0x1000	/* b12: Device state transition interrupt */ +#define	CTRT	0x0800	/* b11: Control transfer stage transition interrupt */ +#define	BEMP	0x0400	/* b10: Buffer empty interrupt */ +#define	NRDY	0x0200	/* b9: Buffer not ready interrupt */ +#define	BRDY	0x0100	/* b8: Buffer ready interrupt */ +#define	VBSTS	0x0080	/* b7: VBUS input port */ +#define	DVSQ	0x0070	/* b6-4: Device state */ +#define	  DS_SPD_CNFG	 0x0070	  /* Suspend Configured */ +#define	  DS_SPD_ADDR	 0x0060	  /* Suspend Address */ +#define	  DS_SPD_DFLT	 0x0050	  /* Suspend Default */ +#define	  DS_SPD_POWR	 0x0040	  /* Suspend Powered */ +#define	  DS_SUSP	 0x0040	  /* Suspend */ +#define	  DS_CNFG	 0x0030	  /* Configured */ +#define	  DS_ADDS	 0x0020	  /* Address */ +#define	  DS_DFLT	 0x0010	  /* Default */ +#define	  DS_POWR	 0x0000	  /* Powered */ +#define	DVSQS		0x0030	/* b5-4: Device state */ +#define	VALID		0x0008	/* b3: Setup packet detected flag */ +#define	CTSQ		0x0007	/* b2-0: Control transfer stage */ +#define	  CS_SQER	 0x0006	  /* Sequence error */ +#define	  CS_WRND	 0x0005	  /* Control write nodata status stage */ +#define	  CS_WRSS	 0x0004	  /* Control write status stage */ +#define	  CS_WRDS	 0x0003	  /* Control write data stage */ +#define	  CS_RDSS	 0x0002	  /* Control read status stage */ +#define	  CS_RDDS	 0x0001	  /* Control read data stage */ +#define	  CS_IDST	 0x0000	  /* Idle or setup stage */ + +/* Interrupt Status Register 1 */ +#define	OVRCR		0x8000	/* b15: Over-current interrupt */ +#define	BCHG		0x4000	/* b14: USB bus chenge interrupt */ +#define	DTCH		0x1000	/* b12: Detach sense interrupt */ +#define	ATTCH		0x0800	/* b11: Attach sense interrupt */ +#define	EOFERR		0x0040	/* b6: EOF-error interrupt */ +#define	SIGN		0x0020	/* b5: Setup ignore interrupt */ +#define	SACK		0x0010	/* b4: Setup acknowledge interrupt */ + +/* Frame Number Register */ +#define	OVRN		0x8000	/* b15: Overrun error */ +#define	CRCE		0x4000	/* b14: Received data error */ +#define	FRNM		0x07FF	/* b10-0: Frame number */ + +/* Micro Frame Number Register */ +#define	UFRNM		0x0007	/* b2-0: Micro frame number */ + +/* Default Control Pipe Maxpacket Size Register */ +/* Pipe Maxpacket Size Register */ +#define	DEVSEL	0xF000	/* b15-14: Device address select */ +#define	MAXP	0x007F	/* b6-0: Maxpacket size of default control pipe */ + +/* Default Control Pipe Control Register */ +#define	BSTS		0x8000	/* b15: Buffer status */ +#define	SUREQ		0x4000	/* b14: Send USB request  */ +#define	CSCLR		0x2000	/* b13: complete-split status clear */ +#define	CSSTS		0x1000	/* b12: complete-split status */ +#define	SUREQCLR	0x0800	/* b11: stop setup request */ +#define	SQCLR		0x0100	/* b8: Sequence toggle bit clear */ +#define	SQSET		0x0080	/* b7: Sequence toggle bit set */ +#define	SQMON		0x0040	/* b6: Sequence toggle bit monitor */ +#define	PBUSY		0x0020	/* b5: pipe busy */ +#define	PINGE		0x0010	/* b4: ping enable */ +#define	CCPL		0x0004	/* b2: Enable control transfer complete */ +#define	PID		0x0003	/* b1-0: Response PID */ +#define	  PID_STALL11	 0x0003	  /* STALL */ +#define	  PID_STALL	 0x0002	  /* STALL */ +#define	  PID_BUF	 0x0001	  /* BUF */ +#define	  PID_NAK	 0x0000	  /* NAK */ + +/* Pipe Window Select Register */ +#define	PIPENM		0x0007	/* b2-0: Pipe select */ + +/* Pipe Configuration Register */ +#define	R8A66597_TYP	0xC000	/* b15-14: Transfer type */ +#define	  R8A66597_ISO	 0xC000		  /* Isochronous */ +#define	  R8A66597_INT	 0x8000		  /* Interrupt */ +#define	  R8A66597_BULK	 0x4000		  /* Bulk */ +#define	R8A66597_BFRE	0x0400	/* b10: Buffer ready interrupt mode select */ +#define	R8A66597_DBLB	0x0200	/* b9: Double buffer mode select */ +#define	R8A66597_CNTMD	0x0100	/* b8: Continuous transfer mode select */ +#define	R8A66597_SHTNAK	0x0080	/* b7: Transfer end NAK */ +#define	R8A66597_DIR	0x0010	/* b4: Transfer direction select */ +#define	R8A66597_EPNUM	0x000F	/* b3-0: Eendpoint number select */ + +/* Pipe Buffer Configuration Register */ +#define	BUFSIZE		0x7C00	/* b14-10: Pipe buffer size */ +#define	BUFNMB		0x007F	/* b6-0: Pipe buffer number */ +#define	PIPE0BUF	256 +#define	PIPExBUF	64 + +/* Pipe Maxpacket Size Register */ +#define	MXPS		0x07FF	/* b10-0: Maxpacket size */ + +/* Pipe Cycle Configuration Register */ +#define	IFIS	0x1000	/* b12: Isochronous in-buffer flush mode select */ +#define	IITV	0x0007	/* b2-0: Isochronous interval */ + +/* Pipex Control Register */ +#define	BSTS	0x8000	/* b15: Buffer status */ +#define	INBUFM	0x4000	/* b14: IN buffer monitor (Only for PIPE1 to 5) */ +#define	CSCLR	0x2000	/* b13: complete-split status clear */ +#define	CSSTS	0x1000	/* b12: complete-split status */ +#define	ATREPM	0x0400	/* b10: Auto repeat mode */ +#define	ACLRM	0x0200	/* b9: Out buffer auto clear mode */ +#define	SQCLR	0x0100	/* b8: Sequence toggle bit clear */ +#define	SQSET	0x0080	/* b7: Sequence toggle bit set */ +#define	SQMON	0x0040	/* b6: Sequence toggle bit monitor */ +#define	PBUSY	0x0020	/* b5: pipe busy */ +#define	PID	0x0003	/* b1-0: Response PID */ + +/* PIPExTRE */ +#define	TRENB		0x0200	/* b9: Transaction counter enable */ +#define	TRCLR		0x0100	/* b8: Transaction counter clear */ + +/* PIPExTRN */ +#define	TRNCNT		0xFFFF	/* b15-0: Transaction counter */ + +/* DEVADDx */ +#define	UPPHUB		0x7800 +#define	HUBPORT		0x0700 +#define	USBSPD		0x00C0 +#define	RTPORT		0x0001 + +#define R8A66597_MAX_NUM_PIPE		10 +#define R8A66597_BUF_BSIZE		8 +#define R8A66597_MAX_DEVICE		10 +#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) +#define R8A66597_MAX_ROOT_HUB		1 +#else +#define R8A66597_MAX_ROOT_HUB		2 +#endif +#define R8A66597_MAX_SAMPLING		5 +#define R8A66597_RH_POLL_TIME		10 + +#define BULK_IN_PIPENUM		3 +#define BULK_IN_BUFNUM		8 + +#define BULK_OUT_PIPENUM	4 +#define BULK_OUT_BUFNUM		40 + +#define check_bulk_or_isoc(pipenum)	((pipenum >= 1 && pipenum <= 5)) +#define check_interrupt(pipenum)	((pipenum >= 6 && pipenum <= 9)) +#define make_devsel(addr)		(addr << 12) + +struct r8a66597 { +	unsigned long reg; +	unsigned short pipe_config;	/* bit field */ +	unsigned short port_status; +	unsigned short port_change; +	u16 speed;	/* HSMODE or FSMODE or LSMODE */ +	unsigned char rh_devnum; +}; + +static inline u16 r8a66597_read(struct r8a66597 *r8a66597, unsigned long offset) +{ +	return inw(r8a66597->reg + offset); +} + +static inline void r8a66597_read_fifo(struct r8a66597 *r8a66597, +				      unsigned long offset, void *buf, +				      int len) +{ +	int i; +#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) +	unsigned long fifoaddr = r8a66597->reg + offset; +	unsigned long count; +	unsigned long *p = buf; + +	count = len / 4; +	for (i = 0; i < count; i++) +		p[i] = inl(r8a66597->reg + offset); + +	if (len & 0x00000003) { +		unsigned long tmp = inl(fifoaddr); +		memcpy((unsigned char *)buf + count * 4, &tmp, len & 0x03); +	} +#else +	unsigned short *p = buf; + +	len = (len + 1) / 2; +	for (i = 0; i < len; i++) +		p[i] = inw(r8a66597->reg + offset); +#endif +} + +static inline void r8a66597_write(struct r8a66597 *r8a66597, u16 val, +				  unsigned long offset) +{ +	outw(val, r8a66597->reg + offset); +} + +static inline void r8a66597_write_fifo(struct r8a66597 *r8a66597, +				       unsigned long offset, void *buf, +				       int len) +{ +	int i; +	unsigned long fifoaddr = r8a66597->reg + offset; +#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) +	unsigned long count; +	unsigned char *pb; +	unsigned long *p = buf; + +	count = len / 4; +	for (i = 0; i < count; i++) +		outl(p[i], fifoaddr); + +	if (len & 0x00000003) { +		pb = (unsigned char *)buf + count * 4; +		for (i = 0; i < (len & 0x00000003); i++) { +			if (r8a66597_read(r8a66597, CFIFOSEL) & BIGEND) +				outb(pb[i], fifoaddr + i); +			else +				outb(pb[i], fifoaddr + 3 - i); +		} +	} +#else +	int odd = len & 0x0001; +	unsigned short *p = buf; + +	len = len / 2; +	for (i = 0; i < len; i++) +		outw(p[i], fifoaddr); + +	if (odd) { +		unsigned char *pb = (unsigned char *)(buf + len); +		outb(*pb, fifoaddr); +	} +#endif +} + +static inline void r8a66597_mdfy(struct r8a66597 *r8a66597, +				 u16 val, u16 pat, unsigned long offset) +{ +	u16 tmp; +	tmp = r8a66597_read(r8a66597, offset); +	tmp = tmp & (~pat); +	tmp = tmp | val; +	r8a66597_write(r8a66597, tmp, offset); +} + +#define r8a66597_bclr(r8a66597, val, offset)	\ +			r8a66597_mdfy(r8a66597, 0, val, offset) +#define r8a66597_bset(r8a66597, val, offset)	\ +			r8a66597_mdfy(r8a66597, val, 0, offset) + +static inline unsigned long get_syscfg_reg(int port) +{ +	return port == 0 ? SYSCFG0 : SYSCFG1; +} + +static inline unsigned long get_syssts_reg(int port) +{ +	return port == 0 ? SYSSTS0 : SYSSTS1; +} + +static inline unsigned long get_dvstctr_reg(int port) +{ +	return port == 0 ? DVSTCTR0 : DVSTCTR1; +} + +static inline unsigned long get_dmacfg_reg(int port) +{ +	return port == 0 ? DMA0CFG : DMA1CFG; +} + +static inline unsigned long get_intenb_reg(int port) +{ +	return port == 0 ? INTENB1 : INTENB2; +} + +static inline unsigned long get_intsts_reg(int port) +{ +	return port == 0 ? INTSTS1 : INTSTS2; +} + +static inline u16 get_rh_usb_speed(struct r8a66597 *r8a66597, int port) +{ +	unsigned long dvstctr_reg = get_dvstctr_reg(port); + +	return r8a66597_read(r8a66597, dvstctr_reg) & RHST; +} + +static inline void r8a66597_port_power(struct r8a66597 *r8a66597, int port, +				       int power) +{ +	unsigned long dvstctr_reg = get_dvstctr_reg(port); + +	if (power) +		r8a66597_bset(r8a66597, VBOUT, dvstctr_reg); +	else +		r8a66597_bclr(r8a66597, VBOUT, dvstctr_reg); +} + +#define get_pipectr_addr(pipenum)	(PIPE1CTR + (pipenum - 1) * 2) +#define get_pipetre_addr(pipenum)	(PIPE1TRE + (pipenum - 1) * 4) +#define get_pipetrn_addr(pipenum)	(PIPE1TRN + (pipenum - 1) * 4) +#define get_devadd_addr(address)	(DEVADD0 + address * 2) + + +/* USB HUB CONSTANTS (not OHCI-specific; see hub.h, based on usb_ohci.h) */ + +/* destination of request */ +#define RH_INTERFACE		   0x01 +#define RH_ENDPOINT		   0x02 +#define RH_OTHER		   0x03 + +#define RH_CLASS		   0x20 +#define RH_VENDOR		   0x40 + +/* Requests: bRequest << 8 | bmRequestType */ +#define RH_GET_STATUS		0x0080 +#define RH_CLEAR_FEATURE	0x0100 +#define RH_SET_FEATURE		0x0300 +#define RH_SET_ADDRESS		0x0500 +#define RH_GET_DESCRIPTOR	0x0680 +#define RH_SET_DESCRIPTOR	0x0700 +#define RH_GET_CONFIGURATION	0x0880 +#define RH_SET_CONFIGURATION	0x0900 +#define RH_GET_STATE		0x0280 +#define RH_GET_INTERFACE	0x0A80 +#define RH_SET_INTERFACE	0x0B00 +#define RH_SYNC_FRAME		0x0C80 +/* Our Vendor Specific Request */ +#define RH_SET_EP		0x2000 + +/* Hub port features */ +#define RH_PORT_CONNECTION	   0x00 +#define RH_PORT_ENABLE		   0x01 +#define RH_PORT_SUSPEND		   0x02 +#define RH_PORT_OVER_CURRENT	   0x03 +#define RH_PORT_RESET		   0x04 +#define RH_PORT_POWER		   0x08 +#define RH_PORT_LOW_SPEED	   0x09 + +#define RH_C_PORT_CONNECTION	   0x10 +#define RH_C_PORT_ENABLE	   0x11 +#define RH_C_PORT_SUSPEND	   0x12 +#define RH_C_PORT_OVER_CURRENT	   0x13 +#define RH_C_PORT_RESET		   0x14 + +/* Hub features */ +#define RH_C_HUB_LOCAL_POWER	   0x00 +#define RH_C_HUB_OVER_CURRENT	   0x01 + +#define RH_DEVICE_REMOTE_WAKEUP	   0x00 +#define RH_ENDPOINT_STALL	   0x01 + +#define RH_ACK			   0x01 +#define RH_REQ_ERR		   -1 +#define RH_NACK			   0x00 + +/* OHCI ROOT HUB REGISTER MASKS */ + +/* roothub.portstatus [i] bits */ +#define RH_PS_CCS	0x00000001	/* current connect status */ +#define RH_PS_PES	0x00000002	/* port enable status*/ +#define RH_PS_PSS	0x00000004	/* port suspend status */ +#define RH_PS_POCI	0x00000008	/* port over current indicator */ +#define RH_PS_PRS	0x00000010	/* port reset status */ +#define RH_PS_PPS	0x00000100	/* port power status */ +#define RH_PS_LSDA	0x00000200	/* low speed device attached */ +#define RH_PS_CSC	0x00010000	/* connect status change */ +#define RH_PS_PESC	0x00020000	/* port enable status change */ +#define RH_PS_PSSC	0x00040000	/* port suspend status change */ +#define RH_PS_OCIC	0x00080000	/* over current indicator change */ +#define RH_PS_PRSC	0x00100000	/* port reset status change */ + +/* roothub.status bits */ +#define RH_HS_LPS	0x00000001	/* local power status */ +#define RH_HS_OCI	0x00000002	/* over current indicator */ +#define RH_HS_DRWE	0x00008000	/* device remote wakeup enable */ +#define RH_HS_LPSC	0x00010000	/* local power status change */ +#define RH_HS_OCIC	0x00020000	/* over current indicator change */ +#define RH_HS_CRWE	0x80000000	/* clear remote wakeup enable */ + +/* roothub.b masks */ +#define RH_B_DR		0x0000ffff	/* device removable flags */ +#define RH_B_PPCM	0xffff0000	/* port power control mask */ + +/* roothub.a masks */ +#define RH_A_NDP	(0xff << 0)	/* number of downstream ports */ +#define RH_A_PSM	(1 << 8)	/* power switching mode */ +#define RH_A_NPS	(1 << 9)	/* no power switching */ +#define RH_A_DT		(1 << 10)	/* device type (mbz) */ +#define RH_A_OCPM	(1 << 11)	/* over current protection mode */ +#define RH_A_NOCP	(1 << 12)	/* no over current protection */ +#define RH_A_POTPGT	(0xff << 24)	/* power on to power good time */ + +#endif	/* __R8A66597_H__ */ diff --git a/roms/u-boot/drivers/usb/host/sl811-hcd.c b/roms/u-boot/drivers/usb/host/sl811-hcd.c new file mode 100644 index 00000000..b29c67e1 --- /dev/null +++ b/roms/u-boot/drivers/usb/host/sl811-hcd.c @@ -0,0 +1,714 @@ +/* + * (C) Copyright 2004 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * This code is based on linux driver for sl811hs chip, source at + * drivers/usb/host/sl811.c: + * + * SL811 Host Controller Interface driver for USB. + * + * Copyright (c) 2003/06, Courage Co., Ltd. + * + * Based on: + *	1.uhci.c by Linus Torvalds, Johannes Erdfelt, Randy Dunlap, + *	  Georg Acher, Deti Fliegl, Thomas Sailer, Roman Weissgaerber, + *	  Adam Richter, Gregory P. Smith; + *	2.Original SL811 driver (hc_sl811.o) by Pei Liu <pbl@cypress.com> + *	3.Rewrited as sl811.o by Yin Aihua <yinah:couragetech.com.cn> + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +#include <common.h> +#include <mpc8xx.h> +#include <usb.h> +#include "sl811.h" + +#include "../../../board/kup/common/kup.h" + +#ifdef __PPC__ +# define EIEIO		__asm__ volatile ("eieio") +#else +# define EIEIO		/* nothing */ +#endif + +#define	 SL811_ADR (0x50000000) +#define	 SL811_DAT (0x50000001) + +#ifdef SL811_DEBUG +static int debug = 9; +#endif + +static int root_hub_devnum = 0; +static struct usb_port_status rh_status = { 0 };/* root hub port status */ + +static int sl811_rh_submit_urb(struct usb_device *usb_dev, unsigned long pipe, +			       void *data, int buf_len, struct devrequest *cmd); + +static void sl811_write (__u8 index, __u8 data) +{ +	*(volatile unsigned char *) (SL811_ADR) = index; +	EIEIO; +	*(volatile unsigned char *) (SL811_DAT) = data; +	EIEIO; +} + +static __u8 sl811_read (__u8 index) +{ +	__u8 data; + +	*(volatile unsigned char *) (SL811_ADR) = index; +	EIEIO; +	data = *(volatile unsigned char *) (SL811_DAT); +	EIEIO; +	return (data); +} + +/* + * Read consecutive bytes of data from the SL811H/SL11H buffer + */ +static void inline sl811_read_buf(__u8 offset, __u8 *buf, __u8 size) +{ +	*(volatile unsigned char *) (SL811_ADR) = offset; +	EIEIO; +	while (size--) { +		*buf++ = *(volatile unsigned char *) (SL811_DAT); +		EIEIO; +	} +} + +/* + * Write consecutive bytes of data to the SL811H/SL11H buffer + */ +static void inline sl811_write_buf(__u8 offset, __u8 *buf, __u8 size) +{ +	*(volatile unsigned char *) (SL811_ADR) = offset; +	EIEIO; +	while (size--) { +		*(volatile unsigned char *) (SL811_DAT) = *buf++; +		EIEIO; +	} +} + +int usb_init_kup4x (void) +{ +	volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR; +	volatile memctl8xx_t *memctl = &immap->im_memctl; +	int i; +	unsigned char tmp; + +	memctl = &immap->im_memctl; +	memctl->memc_or7 = 0xFFFF8726; +	memctl->memc_br7 = 0x50000401;	/* start at 0x50000000 */ +	/* BP 14 low = USB ON */ +	immap->im_cpm.cp_pbdat &= ~(BP_USB_VCC); +	/* PB 14 nomal port */ +	immap->im_cpm.cp_pbpar &= ~(BP_USB_VCC); +	/* output */ +	immap->im_cpm.cp_pbdir |= (BP_USB_VCC); + +	puts ("USB:   "); + +	for (i = 0x10; i < 0xff; i++) { +		sl811_write(i, i); +		tmp = (sl811_read(i)); +		if (tmp != i) { +			printf ("SL811 compare error index=0x%02x read=0x%02x\n", i, tmp); +			return (-1); +		} +	} +	printf ("SL811 ready\n"); +	return (0); +} + +/* + * This function resets SL811HS controller and detects the speed of + * the connecting device + * + * Return: 0 = no device attached; 1 = USB device attached + */ +static int sl811_hc_reset(void) +{ +	int status ; + +	sl811_write(SL811_CTRL2, SL811_CTL2_HOST | SL811_12M_HI); +	sl811_write(SL811_CTRL1, SL811_CTRL1_RESET); + +	mdelay(20); + +	/* Disable hardware SOF generation, clear all irq status. */ +	sl811_write(SL811_CTRL1, 0); +	mdelay(2); +	sl811_write(SL811_INTRSTS, 0xff); +	status = sl811_read(SL811_INTRSTS); + +	if (status & SL811_INTR_NOTPRESENT) { +		/* Device is not present */ +		PDEBUG(0, "Device not present\n"); +		rh_status.wPortStatus &= ~(USB_PORT_STAT_CONNECTION | USB_PORT_STAT_ENABLE); +		rh_status.wPortChange |= USB_PORT_STAT_C_CONNECTION; +		sl811_write(SL811_INTR, SL811_INTR_INSRMV); +		return 0; +	} + +	/* Send SOF to address 0, endpoint 0. */ +	sl811_write(SL811_LEN_B, 0); +	sl811_write(SL811_PIDEP_B, PIDEP(USB_PID_SOF, 0)); +	sl811_write(SL811_DEV_B, 0x00); +	sl811_write(SL811_SOFLOW, SL811_12M_LOW); + +	if (status & SL811_INTR_SPEED_FULL) { +		/* full speed device connect directly to root hub */ +		PDEBUG (0, "Full speed Device attached\n"); + +		sl811_write(SL811_CTRL1, SL811_CTRL1_RESET); +		mdelay(20); +		sl811_write(SL811_CTRL2, SL811_CTL2_HOST | SL811_12M_HI); +		sl811_write(SL811_CTRL1, SL811_CTRL1_SOF); + +		/* start the SOF or EOP */ +		sl811_write(SL811_CTRL_B, SL811_USB_CTRL_ARM); +		rh_status.wPortStatus |= USB_PORT_STAT_CONNECTION; +		rh_status.wPortStatus &= ~USB_PORT_STAT_LOW_SPEED; +		mdelay(2); +		sl811_write(SL811_INTRSTS, 0xff); +	} else { +		/* slow speed device connect directly to root-hub */ +		PDEBUG(0, "Low speed Device attached\n"); + +		sl811_write(SL811_CTRL1, SL811_CTRL1_RESET); +		mdelay(20); +		sl811_write(SL811_CTRL2, SL811_CTL2_HOST | SL811_CTL2_DSWAP | SL811_12M_HI); +		sl811_write(SL811_CTRL1, SL811_CTRL1_SPEED_LOW | SL811_CTRL1_SOF); + +		/* start the SOF or EOP */ +		sl811_write(SL811_CTRL_B, SL811_USB_CTRL_ARM); +		rh_status.wPortStatus |= USB_PORT_STAT_CONNECTION | USB_PORT_STAT_LOW_SPEED; +		mdelay(2); +		sl811_write(SL811_INTRSTS, 0xff); +	} + +	rh_status.wPortChange |= USB_PORT_STAT_C_CONNECTION; +	sl811_write(SL811_INTR, /*SL811_INTR_INSRMV*/SL811_INTR_DONE_A); + +	return 1; +} + +int usb_lowlevel_init(int index, enum usb_init_type init, void **controller) +{ +	root_hub_devnum = 0; +	sl811_hc_reset(); +	return 0; +} + +int usb_lowlevel_stop(int index) +{ +	sl811_hc_reset(); +	return 0; +} + +static int calc_needed_buswidth(int bytes, int need_preamble) +{ +	return !need_preamble ? bytes * 8 + 256 : 8 * 8 * bytes + 2048; +} + +static int sl811_send_packet(struct usb_device *dev, unsigned long pipe, __u8 *buffer, int len) +{ +	__u8 ctrl = SL811_USB_CTRL_ARM | SL811_USB_CTRL_ENABLE; +	__u16 status = 0; +	int err = 0, time_start = get_timer(0); +	int need_preamble = !(rh_status.wPortStatus & USB_PORT_STAT_LOW_SPEED) && +		(dev->speed == USB_SPEED_LOW); + +	if (len > 239) +		return -1; + +	if (usb_pipeout(pipe)) +		ctrl |= SL811_USB_CTRL_DIR_OUT; +	if (usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe))) +		ctrl |= SL811_USB_CTRL_TOGGLE_1; +	if (need_preamble) +		ctrl |= SL811_USB_CTRL_PREAMBLE; + +	sl811_write(SL811_INTRSTS, 0xff); + +	while (err < 3) { +		sl811_write(SL811_ADDR_A, 0x10); +		sl811_write(SL811_LEN_A, len); +		if (usb_pipeout(pipe) && len) +			sl811_write_buf(0x10, buffer, len); + +		if (!(rh_status.wPortStatus & USB_PORT_STAT_LOW_SPEED) && +		    sl811_read(SL811_SOFCNTDIV)*64 < calc_needed_buswidth(len, need_preamble)) +			ctrl |= SL811_USB_CTRL_SOF; +		else +			ctrl &= ~SL811_USB_CTRL_SOF; + +		sl811_write(SL811_CTRL_A, ctrl); +		while (!(sl811_read(SL811_INTRSTS) & SL811_INTR_DONE_A)) { +			if (5*CONFIG_SYS_HZ < get_timer(time_start)) { +				printf("USB transmit timed out\n"); +				return -USB_ST_CRC_ERR; +			} +		} + +		sl811_write(SL811_INTRSTS, 0xff); +		status = sl811_read(SL811_STS_A); + +		if (status & SL811_USB_STS_ACK) { +			int remainder = sl811_read(SL811_CNT_A); +			if (remainder) { +				PDEBUG(0, "usb transfer remainder = %d\n", remainder); +				len -= remainder; +			} +			if (usb_pipein(pipe) && len) +				sl811_read_buf(0x10, buffer, len); +			return len; +		} + +		if ((status & SL811_USB_STS_NAK) == SL811_USB_STS_NAK) +			continue; + +		PDEBUG(0, "usb transfer error %#x\n", (int)status); +		err++; +	} + +	err = 0; + +	if (status & SL811_USB_STS_ERROR) +		err |= USB_ST_BUF_ERR; +	if (status & SL811_USB_STS_TIMEOUT) +		err |= USB_ST_CRC_ERR; +	if (status & SL811_USB_STS_STALL) +		err |= USB_ST_STALLED; + +	return -err; +} + +int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer, +		    int len) +{ +	int dir_out = usb_pipeout(pipe); +	int ep = usb_pipeendpoint(pipe); +	int max = usb_maxpacket(dev, pipe); +	int done = 0; + +	PDEBUG(7, "dev = %ld pipe = %ld buf = %p size = %d dir_out = %d\n", +	       usb_pipedevice(pipe), usb_pipeendpoint(pipe), buffer, len, dir_out); + +	dev->status = 0; + +	sl811_write(SL811_DEV_A, usb_pipedevice(pipe)); +	sl811_write(SL811_PIDEP_A, PIDEP(!dir_out ? USB_PID_IN : USB_PID_OUT, ep)); +	while (done < len) { +		int res = sl811_send_packet(dev, pipe, (__u8*)buffer+done, +					    max > len - done ? len - done : max); +		if (res < 0) { +			dev->status = -res; +			return res; +		} + +		if (!dir_out && res < max) /* short packet */ +			break; + +		done += res; +		usb_dotoggle(dev, ep, dir_out); +	} + +	dev->act_len = done; + +	return 0; +} + +int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, +		       int len,struct devrequest *setup) +{ +	int done = 0; +	int devnum = usb_pipedevice(pipe); +	int ep = usb_pipeendpoint(pipe); + +	dev->status = 0; + +	if (devnum == root_hub_devnum) +		return sl811_rh_submit_urb(dev, pipe, buffer, len, setup); + +	PDEBUG(7, "dev = %d pipe = %ld buf = %p size = %d rt = %#x req = %#x bus = %i\n", +	       devnum, ep, buffer, len, (int)setup->requesttype, +	       (int)setup->request, sl811_read(SL811_SOFCNTDIV)*64); + +	sl811_write(SL811_DEV_A, devnum); +	sl811_write(SL811_PIDEP_A, PIDEP(USB_PID_SETUP, ep)); +	/* setup phase */ +	usb_settoggle(dev, ep, 1, 0); +	if (sl811_send_packet(dev, usb_sndctrlpipe(dev, ep), +			      (__u8*)setup, sizeof(*setup)) == sizeof(*setup)) { +		int dir_in = usb_pipein(pipe); +		int max = usb_maxpacket(dev, pipe); + +		/* data phase */ +		sl811_write(SL811_PIDEP_A, +			    PIDEP(dir_in ? USB_PID_IN : USB_PID_OUT, ep)); +		usb_settoggle(dev, ep, usb_pipeout(pipe), 1); +		while (done < len) { +			int res = sl811_send_packet(dev, pipe, (__u8*)buffer+done, +						    max > len - done ? len - done : max); +			if (res < 0) { +				PDEBUG(0, "status data failed!\n"); +				dev->status = -res; +				return 0; +			} +			done += res; +			usb_dotoggle(dev, ep, usb_pipeout(pipe)); +			if (dir_in && res < max) /* short packet */ +				break; +		} + +		/* status phase */ +		sl811_write(SL811_PIDEP_A, +			    PIDEP(!dir_in ? USB_PID_IN : USB_PID_OUT, ep)); +		usb_settoggle(dev, ep, !usb_pipeout(pipe), 1); +		if (sl811_send_packet(dev, +				      !dir_in ? usb_rcvctrlpipe(dev, ep) : +				      usb_sndctrlpipe(dev, ep), +				      0, 0) < 0) { +			PDEBUG(0, "status phase failed!\n"); +			dev->status = -1; +		} +	} else { +		PDEBUG(0, "setup phase failed!\n"); +		dev->status = -1; +	} + +	dev->act_len = done; + +	return done; +} + +int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, +		   int len, int interval) +{ +	PDEBUG(0, "dev = %p pipe = %#lx buf = %p size = %d int = %d\n", dev, pipe, +	       buffer, len, interval); +	return -1; +} + +/* + * SL811 Virtual Root Hub + */ + +/* Device descriptor */ +static __u8 sl811_rh_dev_des[] = +{ +	0x12,	    /*	__u8  bLength; */ +	0x01,	    /*	__u8  bDescriptorType; Device */ +	0x10,	    /*	__u16 bcdUSB; v1.1 */ +	0x01, +	0x09,	    /*	__u8  bDeviceClass; HUB_CLASSCODE */ +	0x00,	    /*	__u8  bDeviceSubClass; */ +	0x00,	    /*	__u8  bDeviceProtocol; */ +	0x08,	    /*	__u8  bMaxPacketSize0; 8 Bytes */ +	0x00,	    /*	__u16 idVendor; */ +	0x00, +	0x00,	    /*	__u16 idProduct; */ +	0x00, +	0x00,	    /*	__u16 bcdDevice; */ +	0x00, +	0x00,	    /*	__u8  iManufacturer; */ +	0x02,	    /*	__u8  iProduct; */ +	0x01,	    /*	__u8  iSerialNumber; */ +	0x01	    /*	__u8  bNumConfigurations; */ +}; + +/* Configuration descriptor */ +static __u8 sl811_rh_config_des[] = +{ +	0x09,	    /*	__u8  bLength; */ +	0x02,	    /*	__u8  bDescriptorType; Configuration */ +	0x19,	    /*	__u16 wTotalLength; */ +	0x00, +	0x01,	    /*	__u8  bNumInterfaces; */ +	0x01,	    /*	__u8  bConfigurationValue; */ +	0x00,	    /*	__u8  iConfiguration; */ +	0x40,	    /*	__u8  bmAttributes; +		    Bit 7: Bus-powered, 6: Self-powered, 5 Remote-wakwup, +		    4..0: resvd */ +	0x00,	    /*	__u8  MaxPower; */ + +	/* interface */ +	0x09,	    /*	__u8  if_bLength; */ +	0x04,	    /*	__u8  if_bDescriptorType; Interface */ +	0x00,	    /*	__u8  if_bInterfaceNumber; */ +	0x00,	    /*	__u8  if_bAlternateSetting; */ +	0x01,	    /*	__u8  if_bNumEndpoints; */ +	0x09,	    /*	__u8  if_bInterfaceClass; HUB_CLASSCODE */ +	0x00,	    /*	__u8  if_bInterfaceSubClass; */ +	0x00,	    /*	__u8  if_bInterfaceProtocol; */ +	0x00,	    /*	__u8  if_iInterface; */ + +	/* endpoint */ +	0x07,	    /*	__u8  ep_bLength; */ +	0x05,	    /*	__u8  ep_bDescriptorType; Endpoint */ +	0x81,	    /*	__u8  ep_bEndpointAddress; IN Endpoint 1 */ +	0x03,	    /*	__u8  ep_bmAttributes; Interrupt */ +	0x08,	    /*	__u16 ep_wMaxPacketSize; */ +	0x00, +	0xff	    /*	__u8  ep_bInterval; 255 ms */ +}; + +/* root hub class descriptor*/ +static __u8 sl811_rh_hub_des[] = +{ +	0x09,			/*  __u8  bLength; */ +	0x29,			/*  __u8  bDescriptorType; Hub-descriptor */ +	0x01,			/*  __u8  bNbrPorts; */ +	0x00,			/* __u16  wHubCharacteristics; */ +	0x00, +	0x50,			/*  __u8  bPwrOn2pwrGood; 2ms */ +	0x00,			/*  __u8  bHubContrCurrent; 0 mA */ +	0xfc,			/*  __u8  DeviceRemovable; *** 7 Ports max *** */ +	0xff			/*  __u8  PortPwrCtrlMask; *** 7 ports max *** */ +}; + +/* + * helper routine for returning string descriptors in UTF-16LE + * input can actually be ISO-8859-1; ASCII is its 7-bit subset + */ +static int ascii2utf (char *s, u8 *utf, int utfmax) +{ +	int retval; + +	for (retval = 0; *s && utfmax > 1; utfmax -= 2, retval += 2) { +		*utf++ = *s++; +		*utf++ = 0; +	} +	return retval; +} + +/* + * root_hub_string is used by each host controller's root hub code, + * so that they're identified consistently throughout the system. + */ +static int usb_root_hub_string (int id, int serial, char *type, __u8 *data, int len) +{ +	char buf [30]; + +	/* assert (len > (2 * (sizeof (buf) + 1))); +	   assert (strlen (type) <= 8);*/ + +	/* language ids */ +	if (id == 0) { +		*data++ = 4; *data++ = 3;	/* 4 bytes data */ +		*data++ = 0; *data++ = 0;	/* some language id */ +		return 4; + +	/* serial number */ +	} else if (id == 1) { +		sprintf (buf, "%#x", serial); + +	/* product description */ +	} else if (id == 2) { +		sprintf (buf, "USB %s Root Hub", type); + +	/* id 3 == vendor description */ + +	/* unsupported IDs --> "stall" */ +	} else +	    return 0; + +	ascii2utf (buf, data + 2, len - 2); +	data [0] = 2 + strlen(buf) * 2; +	data [1] = 3; +	return data [0]; +} + +/* helper macro */ +#define OK(x)	len = (x); break + +/* + * This function handles all USB request to the the virtual root hub + */ +static int sl811_rh_submit_urb(struct usb_device *usb_dev, unsigned long pipe, +			       void *data, int buf_len, struct devrequest *cmd) +{ +	__u8 data_buf[16]; +	__u8 *bufp = data_buf; +	int len = 0; +	int status = 0; +	__u16 bmRType_bReq; +	__u16 wValue  = le16_to_cpu (cmd->value); +	__u16 wLength = le16_to_cpu (cmd->length); +#ifdef SL811_DEBUG +	__u16 wIndex  = le16_to_cpu (cmd->index); +#endif + +	if (usb_pipeint(pipe)) { +		PDEBUG(0, "interrupt transfer unimplemented!\n"); +		return 0; +	} + +	bmRType_bReq  = cmd->requesttype | (cmd->request << 8); + +	PDEBUG(5, "submit rh urb, req = %d(%x) val = %#x index = %#x len=%d\n", +	       bmRType_bReq, bmRType_bReq, wValue, wIndex, wLength); + +	/* Request Destination: +		   without flags: Device, +		   USB_RECIP_INTERFACE: interface, +		   USB_RECIP_ENDPOINT: endpoint, +		   USB_TYPE_CLASS means HUB here, +		   USB_RECIP_OTHER | USB_TYPE_CLASS  almost ever means HUB_PORT here +	*/ +	switch (bmRType_bReq) { +	case RH_GET_STATUS: +		*(__u16 *)bufp = cpu_to_le16(1); +		OK(2); + +	case RH_GET_STATUS | USB_RECIP_INTERFACE: +		*(__u16 *)bufp = cpu_to_le16(0); +		OK(2); + +	case RH_GET_STATUS | USB_RECIP_ENDPOINT: +		*(__u16 *)bufp = cpu_to_le16(0); +		OK(2); + +	case RH_GET_STATUS | USB_TYPE_CLASS: +		*(__u32 *)bufp = cpu_to_le32(0); +		OK(4); + +	case RH_GET_STATUS | USB_RECIP_OTHER | USB_TYPE_CLASS: +		*(__u32 *)bufp = cpu_to_le32(rh_status.wPortChange<<16 | rh_status.wPortStatus); +		OK(4); + +	case RH_CLEAR_FEATURE | USB_RECIP_ENDPOINT: +		switch (wValue) { +		case 1: +			OK(0); +		} +		break; + +	case RH_CLEAR_FEATURE | USB_TYPE_CLASS: +		switch (wValue) { +		case C_HUB_LOCAL_POWER: +			OK(0); + +		case C_HUB_OVER_CURRENT: +			OK(0); +		} +		break; + +	case RH_CLEAR_FEATURE | USB_RECIP_OTHER | USB_TYPE_CLASS: +		switch (wValue) { +		case USB_PORT_FEAT_ENABLE: +			rh_status.wPortStatus &= ~USB_PORT_STAT_ENABLE; +			OK(0); + +		case USB_PORT_FEAT_SUSPEND: +			rh_status.wPortStatus &= ~USB_PORT_STAT_SUSPEND; +			OK(0); + +		case USB_PORT_FEAT_POWER: +			rh_status.wPortStatus &= ~USB_PORT_STAT_POWER; +			OK(0); + +		case USB_PORT_FEAT_C_CONNECTION: +			rh_status.wPortChange &= ~USB_PORT_STAT_C_CONNECTION; +			OK(0); + +		case USB_PORT_FEAT_C_ENABLE: +			rh_status.wPortChange &= ~USB_PORT_STAT_C_ENABLE; +			OK(0); + +		case USB_PORT_FEAT_C_SUSPEND: +			rh_status.wPortChange &= ~USB_PORT_STAT_C_SUSPEND; +			OK(0); + +		case USB_PORT_FEAT_C_OVER_CURRENT: +			rh_status.wPortChange &= ~USB_PORT_STAT_C_OVERCURRENT; +			OK(0); + +		case USB_PORT_FEAT_C_RESET: +			rh_status.wPortChange &= ~USB_PORT_STAT_C_RESET; +			OK(0); +		} +		break; + +	case RH_SET_FEATURE | USB_RECIP_OTHER | USB_TYPE_CLASS: +		switch (wValue) { +		case USB_PORT_FEAT_SUSPEND: +			rh_status.wPortStatus |= USB_PORT_STAT_SUSPEND; +			OK(0); + +		case USB_PORT_FEAT_RESET: +			rh_status.wPortStatus |= USB_PORT_STAT_RESET; +			rh_status.wPortChange = 0; +			rh_status.wPortChange |= USB_PORT_STAT_C_RESET; +			rh_status.wPortStatus &= ~USB_PORT_STAT_RESET; +			rh_status.wPortStatus |= USB_PORT_STAT_ENABLE; +			OK(0); + +		case USB_PORT_FEAT_POWER: +			rh_status.wPortStatus |= USB_PORT_STAT_POWER; +			OK(0); + +		case USB_PORT_FEAT_ENABLE: +			rh_status.wPortStatus |= USB_PORT_STAT_ENABLE; +			OK(0); +		} +		break; + +	case RH_SET_ADDRESS: +		root_hub_devnum = wValue; +		OK(0); + +	case RH_GET_DESCRIPTOR: +		switch ((wValue & 0xff00) >> 8) { +		case USB_DT_DEVICE: +			len = sizeof(sl811_rh_dev_des); +			bufp = sl811_rh_dev_des; +			OK(len); + +		case USB_DT_CONFIG: +			len = sizeof(sl811_rh_config_des); +			bufp = sl811_rh_config_des; +			OK(len); + +		case USB_DT_STRING: +			len = usb_root_hub_string(wValue & 0xff, (int)(long)0,	"SL811HS", data, wLength); +			if (len > 0) { +				bufp = data; +				OK(len); +			} + +		default: +			status = -32; +		} +		break; + +	case RH_GET_DESCRIPTOR | USB_TYPE_CLASS: +		len = sizeof(sl811_rh_hub_des); +		bufp = sl811_rh_hub_des; +		OK(len); + +	case RH_GET_CONFIGURATION: +		bufp[0] = 0x01; +		OK(1); + +	case RH_SET_CONFIGURATION: +		OK(0); + +	default: +		PDEBUG(1, "unsupported root hub command\n"); +		status = -32; +	} + +	len = min(len, buf_len); +	if (data != bufp) +		memcpy(data, bufp, len); + +	PDEBUG(5, "len = %d, status = %d\n", len, status); + +	usb_dev->status = status; +	usb_dev->act_len = len; + +	return status == 0 ? len : status; +} diff --git a/roms/u-boot/drivers/usb/host/sl811.h b/roms/u-boot/drivers/usb/host/sl811.h new file mode 100644 index 00000000..c1f9f013 --- /dev/null +++ b/roms/u-boot/drivers/usb/host/sl811.h @@ -0,0 +1,104 @@ +#ifndef __UBOOT_SL811_H +#define __UBOOT_SL811_H + +#undef SL811_DEBUG + +#ifdef SL811_DEBUG +	#define PDEBUG(level, fmt, args...) \ +		if (debug >= (level)) printf("[%s:%d] " fmt, \ +		__PRETTY_FUNCTION__, __LINE__ , ## args) +#else +	#define PDEBUG(level, fmt, args...) do {} while(0) +#endif + +/* Sl811 host control register */ +#define	SL811_CTRL_A		0x00 +#define	SL811_ADDR_A		0x01 +#define	SL811_LEN_A		0x02 +#define	SL811_STS_A		0x03	/* read	*/ +#define	SL811_PIDEP_A		0x03	/* write */ +#define	SL811_CNT_A		0x04	/* read	*/ +#define	SL811_DEV_A		0x04	/* write */ +#define	SL811_CTRL1		0x05 +#define	SL811_INTR		0x06 +#define	SL811_CTRL_B		0x08 +#define	SL811_ADDR_B		0x09 +#define	SL811_LEN_B		0x0A +#define	SL811_STS_B		0x0B	/* read	*/ +#define	SL811_PIDEP_B		0x0B	/* write */ +#define	SL811_CNT_B		0x0C	/* read	*/ +#define	SL811_DEV_B		0x0C	/* write */ +#define	SL811_INTRSTS		0x0D	/* write clears	bitwise	*/ +#define	SL811_HWREV		0x0E	/* read	*/ +#define	SL811_SOFLOW		0x0E	/* write */ +#define	SL811_SOFCNTDIV		0x0F	/* read	*/ +#define	SL811_CTRL2		0x0F	/* write */ + +/* USB control register bits (addr 0x00 and addr 0x08) */ +#define	SL811_USB_CTRL_ARM	0x01 +#define	SL811_USB_CTRL_ENABLE	0x02 +#define	SL811_USB_CTRL_DIR_OUT	0x04 +#define	SL811_USB_CTRL_ISO	0x10 +#define	SL811_USB_CTRL_SOF	0x20 +#define	SL811_USB_CTRL_TOGGLE_1	0x40 +#define	SL811_USB_CTRL_PREAMBLE	0x80 + +/* USB status register bits (addr 0x03 and addr 0x0B) */ +#define	SL811_USB_STS_ACK	0x01 +#define	SL811_USB_STS_ERROR	0x02 +#define	SL811_USB_STS_TIMEOUT	0x04 +#define	SL811_USB_STS_TOGGLE_1	0x08 +#define	SL811_USB_STS_SETUP	0x10 +#define	SL811_USB_STS_OVERFLOW	0x20 +#define	SL811_USB_STS_NAK	0x40 +#define	SL811_USB_STS_STALL	0x80 + +/* Control register 1 bits (addr 0x05) */ +#define	SL811_CTRL1_SOF		0x01 +#define	SL811_CTRL1_RESET	0x08 +#define	SL811_CTRL1_JKSTATE	0x10 +#define	SL811_CTRL1_SPEED_LOW	0x20 +#define	SL811_CTRL1_SUSPEND	0x40 + +/* Interrut enable (addr 0x06) and interrupt status register bits (addr 0x0D) */ +#define	SL811_INTR_DONE_A	0x01 +#define	SL811_INTR_DONE_B	0x02 +#define	SL811_INTR_SOF		0x10 +#define	SL811_INTR_INSRMV	0x20 +#define	SL811_INTR_DETECT	0x40 +#define	SL811_INTR_NOTPRESENT	0x40 +#define	SL811_INTR_SPEED_FULL	0x80    /* only in status reg */ + +/* HW rev and SOF lo register bits (addr 0x0E) */ +#define	SL811_HWR_HWREV		0xF0 + +/* SOF counter and control reg 2 (addr 0x0F) */ +#define	SL811_CTL2_SOFHI	0x3F +#define	SL811_CTL2_DSWAP	0x40 +#define	SL811_CTL2_HOST		0x80 + +/* Set up for 1-ms SOF time. */ +#define SL811_12M_LOW		0xE0 +#define SL811_12M_HI		0x2E + +#define SL811_DATA_START	0x10 +#define SL811_DATA_LIMIT	240 + +/* Requests: bRequest << 8 | bmRequestType */ +#define RH_GET_STATUS           0x0080 +#define RH_CLEAR_FEATURE        0x0100 +#define RH_SET_FEATURE          0x0300 +#define RH_SET_ADDRESS		0x0500 +#define RH_GET_DESCRIPTOR	0x0680 +#define RH_SET_DESCRIPTOR       0x0700 +#define RH_GET_CONFIGURATION	0x0880 +#define RH_SET_CONFIGURATION	0x0900 +#define RH_GET_STATE            0x0280 +#define RH_GET_INTERFACE        0x0A80 +#define RH_SET_INTERFACE        0x0B00 +#define RH_SYNC_FRAME           0x0C80 + + +#define PIDEP(pid, ep) (((pid) & 0x0f) << 4 | (ep)) + +#endif	/* __UBOOT_SL811_H */ diff --git a/roms/u-boot/drivers/usb/host/utmi-armada100.c b/roms/u-boot/drivers/usb/host/utmi-armada100.c new file mode 100644 index 00000000..1e878280 --- /dev/null +++ b/roms/u-boot/drivers/usb/host/utmi-armada100.c @@ -0,0 +1,80 @@ +/* + * (C) Copyright 2012 + * eInfochips Ltd. <www.einfochips.com> + * Written-by: Ajay Bhargav <ajay.bhargav@einfochips.com> + * + * (C) Copyright 2009 + * Marvell Semiconductor <www.marvell.com> + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +#include <common.h> +#include <asm/io.h> +#include <usb.h> +#include <asm/arch/cpu.h> +#include <asm/arch/armada100.h> +#include <asm/arch/utmi-armada100.h> + +static int utmi_phy_init(void) +{ +	struct armd1usb_phy_reg *phy_regs = +		(struct armd1usb_phy_reg *)UTMI_PHY_BASE; +	int timeout; + +	setbits_le32(&phy_regs->utmi_ctrl, INPKT_DELAY_SOF | PLL_PWR_UP); +	udelay(1000); +	setbits_le32(&phy_regs->utmi_ctrl, PHY_PWR_UP); + +	clrbits_le32(&phy_regs->utmi_pll, PLL_FBDIV_MASK | PLL_REFDIV_MASK); +	setbits_le32(&phy_regs->utmi_pll, N_DIVIDER << PLL_FBDIV | M_DIVIDER); + +	setbits_le32(&phy_regs->utmi_tx, PHSEL_VAL << CK60_PHSEL); + +	/* Calibrate pll */ +	timeout = 10000; +	while (--timeout && ((readl(&phy_regs->utmi_pll) & PLL_READY) == 0)) +		; +	if (!timeout) +		return -1; + +	udelay(200); +	setbits_le32(&phy_regs->utmi_pll, VCOCAL_START); +	udelay(400); +	clrbits_le32(&phy_regs->utmi_pll, VCOCAL_START); + +	udelay(200); +	setbits_le32(&phy_regs->utmi_tx, RCAL_START); +	udelay(400); +	clrbits_le32(&phy_regs->utmi_tx, RCAL_START); + +	timeout = 10000; +	while (--timeout && ((readl(&phy_regs->utmi_pll) & PLL_READY) == 0)) +		; +	if (!timeout) +		return -1; + +	return 0; +} + +/* + * Initialize USB host controller's UTMI Physical interface + */ +int utmi_init(void) +{ +	struct armd1mpmu_registers *mpmu_regs = +		(struct armd1mpmu_registers *)ARMD1_MPMU_BASE; + +	struct armd1apmu_registers *apmu_regs = +		(struct armd1apmu_registers *)ARMD1_APMU_BASE; + +	/* Turn on 26Mhz ref clock for UTMI PLL */ +	setbits_le32(&mpmu_regs->acgr, APB2_26M_EN | AP_26M); + +	/* USB Clock reset */ +	writel(USB_SPH_AXICLK_EN, &apmu_regs->usbcrc); +	writel(USB_SPH_AXICLK_EN | USB_SPH_AXI_RST, &apmu_regs->usbcrc); + +	/* Initialize UTMI transceiver */ +	return utmi_phy_init(); +} diff --git a/roms/u-boot/drivers/usb/host/xhci-exynos5.c b/roms/u-boot/drivers/usb/host/xhci-exynos5.c new file mode 100644 index 00000000..b4946a3f --- /dev/null +++ b/roms/u-boot/drivers/usb/host/xhci-exynos5.c @@ -0,0 +1,328 @@ +/* + * SAMSUNG EXYNOS5 USB HOST XHCI Controller + * + * Copyright (C) 2012 Samsung Electronics Co.Ltd + *	Vivek Gautam <gautam.vivek@samsung.com> + *	Vikas Sajjan <vikas.sajjan@samsung.com> + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +/* + * This file is a conglomeration for DWC3-init sequence and further + * exynos5 specific PHY-init sequence. + */ + +#include <common.h> +#include <fdtdec.h> +#include <libfdt.h> +#include <malloc.h> +#include <usb.h> +#include <watchdog.h> +#include <asm/arch/cpu.h> +#include <asm/arch/power.h> +#include <asm/arch/xhci-exynos.h> +#include <asm/gpio.h> +#include <asm-generic/errno.h> +#include <linux/compat.h> +#include <linux/usb/dwc3.h> + +#include "xhci.h" + +/* Declare global data pointer */ +DECLARE_GLOBAL_DATA_PTR; + +/** + * Contains pointers to register base addresses + * for the usb controller. + */ +struct exynos_xhci { +	struct exynos_usb3_phy *usb3_phy; +	struct xhci_hccr *hcd; +	struct dwc3 *dwc3_reg; +	struct fdt_gpio_state vbus_gpio; +}; + +static struct exynos_xhci exynos; + +#ifdef CONFIG_OF_CONTROL +static int exynos_usb3_parse_dt(const void *blob, struct exynos_xhci *exynos) +{ +	fdt_addr_t addr; +	unsigned int node; +	int depth; + +	node = fdtdec_next_compatible(blob, 0, COMPAT_SAMSUNG_EXYNOS5_XHCI); +	if (node <= 0) { +		debug("XHCI: Can't get device node for xhci\n"); +		return -ENODEV; +	} + +	/* +	 * Get the base address for XHCI controller from the device node +	 */ +	addr = fdtdec_get_addr(blob, node, "reg"); +	if (addr == FDT_ADDR_T_NONE) { +		debug("Can't get the XHCI register base address\n"); +		return -ENXIO; +	} +	exynos->hcd = (struct xhci_hccr *)addr; + +	/* Vbus gpio */ +	fdtdec_decode_gpio(blob, node, "samsung,vbus-gpio", &exynos->vbus_gpio); + +	depth = 0; +	node = fdtdec_next_compatible_subnode(blob, node, +				COMPAT_SAMSUNG_EXYNOS5_USB3_PHY, &depth); +	if (node <= 0) { +		debug("XHCI: Can't get device node for usb3-phy controller\n"); +		return -ENODEV; +	} + +	/* +	 * Get the base address for usbphy from the device node +	 */ +	exynos->usb3_phy = (struct exynos_usb3_phy *)fdtdec_get_addr(blob, node, +								"reg"); +	if (exynos->usb3_phy == NULL) { +		debug("Can't get the usbphy register address\n"); +		return -ENXIO; +	} + +	return 0; +} +#endif + +static void exynos5_usb3_phy_init(struct exynos_usb3_phy *phy) +{ +	u32 reg; + +	/* enabling usb_drd phy */ +	set_usbdrd_phy_ctrl(POWER_USB_DRD_PHY_CTRL_EN); + +	/* Reset USB 3.0 PHY */ +	writel(0x0, &phy->phy_reg0); + +	clrbits_le32(&phy->phy_param0, +			/* Select PHY CLK source */ +			PHYPARAM0_REF_USE_PAD | +			/* Set Loss-of-Signal Detector sensitivity */ +			PHYPARAM0_REF_LOSLEVEL_MASK); +	setbits_le32(&phy->phy_param0, PHYPARAM0_REF_LOSLEVEL); + +	writel(0x0, &phy->phy_resume); + +	/* +	 * Setting the Frame length Adj value[6:1] to default 0x20 +	 * See xHCI 1.0 spec, 5.2.4 +	 */ +	setbits_le32(&phy->link_system, +			LINKSYSTEM_XHCI_VERSION_CONTROL | +			LINKSYSTEM_FLADJ(0x20)); + +	/* Set Tx De-Emphasis level */ +	clrbits_le32(&phy->phy_param1, PHYPARAM1_PCS_TXDEEMPH_MASK); +	setbits_le32(&phy->phy_param1, PHYPARAM1_PCS_TXDEEMPH); + +	setbits_le32(&phy->phy_batchg, PHYBATCHG_UTMI_CLKSEL); + +	/* PHYTEST POWERDOWN Control */ +	clrbits_le32(&phy->phy_test, +			PHYTEST_POWERDOWN_SSP | +			PHYTEST_POWERDOWN_HSP); + +	/* UTMI Power Control */ +	writel(PHYUTMI_OTGDISABLE, &phy->phy_utmi); + +		/* Use core clock from main PLL */ +	reg = PHYCLKRST_REFCLKSEL_EXT_REFCLK | +		/* Default 24Mhz crystal clock */ +		PHYCLKRST_FSEL(FSEL_CLKSEL_24M) | +		PHYCLKRST_MPLL_MULTIPLIER_24MHZ_REF | +		PHYCLKRST_SSC_REFCLKSEL(0x88) | +		/* Force PortReset of PHY */ +		PHYCLKRST_PORTRESET | +		/* Digital power supply in normal operating mode */ +		PHYCLKRST_RETENABLEN | +		/* Enable ref clock for SS function */ +		PHYCLKRST_REF_SSP_EN | +		/* Enable spread spectrum */ +		PHYCLKRST_SSC_EN | +		/* Power down HS Bias and PLL blocks in suspend mode */ +		PHYCLKRST_COMMONONN; + +	writel(reg, &phy->phy_clk_rst); + +	/* giving time to Phy clock to settle before resetting */ +	udelay(10); + +	reg &= ~PHYCLKRST_PORTRESET; +	writel(reg, &phy->phy_clk_rst); +} + +static void exynos5_usb3_phy_exit(struct exynos_usb3_phy *phy) +{ +	setbits_le32(&phy->phy_utmi, +			PHYUTMI_OTGDISABLE | +			PHYUTMI_FORCESUSPEND | +			PHYUTMI_FORCESLEEP); + +	clrbits_le32(&phy->phy_clk_rst, +			PHYCLKRST_REF_SSP_EN | +			PHYCLKRST_SSC_EN | +			PHYCLKRST_COMMONONN); + +	/* PHYTEST POWERDOWN Control to remove leakage current */ +	setbits_le32(&phy->phy_test, +			PHYTEST_POWERDOWN_SSP | +			PHYTEST_POWERDOWN_HSP); + +	/* disabling usb_drd phy */ +	set_usbdrd_phy_ctrl(POWER_USB_DRD_PHY_CTRL_DISABLE); +} + +void dwc3_set_mode(struct dwc3 *dwc3_reg, u32 mode) +{ +	clrsetbits_le32(&dwc3_reg->g_ctl, +			DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_OTG), +			DWC3_GCTL_PRTCAPDIR(mode)); +} + +static void dwc3_core_soft_reset(struct dwc3 *dwc3_reg) +{ +	/* Before Resetting PHY, put Core in Reset */ +	setbits_le32(&dwc3_reg->g_ctl, +			DWC3_GCTL_CORESOFTRESET); + +	/* Assert USB3 PHY reset */ +	setbits_le32(&dwc3_reg->g_usb3pipectl[0], +			DWC3_GUSB3PIPECTL_PHYSOFTRST); + +	/* Assert USB2 PHY reset */ +	setbits_le32(&dwc3_reg->g_usb2phycfg, +			DWC3_GUSB2PHYCFG_PHYSOFTRST); + +	mdelay(100); + +	/* Clear USB3 PHY reset */ +	clrbits_le32(&dwc3_reg->g_usb3pipectl[0], +			DWC3_GUSB3PIPECTL_PHYSOFTRST); + +	/* Clear USB2 PHY reset */ +	clrbits_le32(&dwc3_reg->g_usb2phycfg, +			DWC3_GUSB2PHYCFG_PHYSOFTRST); + +	/* After PHYs are stable we can take Core out of reset state */ +	clrbits_le32(&dwc3_reg->g_ctl, +			DWC3_GCTL_CORESOFTRESET); +} + +static int dwc3_core_init(struct dwc3 *dwc3_reg) +{ +	u32 reg; +	u32 revision; +	unsigned int dwc3_hwparams1; + +	revision = readl(&dwc3_reg->g_snpsid); +	/* This should read as U3 followed by revision number */ +	if ((revision & DWC3_GSNPSID_MASK) != 0x55330000) { +		puts("this is not a DesignWare USB3 DRD Core\n"); +		return -EINVAL; +	} + +	dwc3_core_soft_reset(dwc3_reg); + +	dwc3_hwparams1 = readl(&dwc3_reg->g_hwparams1); + +	reg = readl(&dwc3_reg->g_ctl); +	reg &= ~DWC3_GCTL_SCALEDOWN_MASK; +	reg &= ~DWC3_GCTL_DISSCRAMBLE; +	switch (DWC3_GHWPARAMS1_EN_PWROPT(dwc3_hwparams1)) { +	case DWC3_GHWPARAMS1_EN_PWROPT_CLK: +		reg &= ~DWC3_GCTL_DSBLCLKGTNG; +		break; +	default: +		debug("No power optimization available\n"); +	} + +	/* +	 * WORKAROUND: DWC3 revisions <1.90a have a bug +	 * where the device can fail to connect at SuperSpeed +	 * and falls back to high-speed mode which causes +	 * the device to enter a Connect/Disconnect loop +	 */ +	if ((revision & DWC3_REVISION_MASK) < 0x190a) +		reg |= DWC3_GCTL_U2RSTECN; + +	writel(reg, &dwc3_reg->g_ctl); + +	return 0; +} + +static int exynos_xhci_core_init(struct exynos_xhci *exynos) +{ +	int ret; + +	exynos5_usb3_phy_init(exynos->usb3_phy); + +	ret = dwc3_core_init(exynos->dwc3_reg); +	if (ret) { +		debug("failed to initialize core\n"); +		return -EINVAL; +	} + +	/* We are hard-coding DWC3 core to Host Mode */ +	dwc3_set_mode(exynos->dwc3_reg, DWC3_GCTL_PRTCAP_HOST); + +	return 0; +} + +static void exynos_xhci_core_exit(struct exynos_xhci *exynos) +{ +	exynos5_usb3_phy_exit(exynos->usb3_phy); +} + +int xhci_hcd_init(int index, struct xhci_hccr **hccr, struct xhci_hcor **hcor) +{ +	struct exynos_xhci *ctx = &exynos; +	int ret; + +#ifdef CONFIG_OF_CONTROL +	exynos_usb3_parse_dt(gd->fdt_blob, ctx); +#else +	ctx->usb3_phy = (struct exynos_usb3_phy *)samsung_get_base_usb3_phy(); +	ctx->hcd = (struct xhci_hccr *)samsung_get_base_usb_xhci(); +#endif + +	ctx->dwc3_reg = (struct dwc3 *)((char *)(ctx->hcd) + DWC3_REG_OFFSET); + +#ifdef CONFIG_OF_CONTROL +	/* setup the Vbus gpio here */ +	if (fdt_gpio_isvalid(&ctx->vbus_gpio) && +	    !fdtdec_setup_gpio(&ctx->vbus_gpio)) +		gpio_direction_output(ctx->vbus_gpio.gpio, 1); +#endif + +	ret = exynos_xhci_core_init(ctx); +	if (ret) { +		puts("XHCI: failed to initialize controller\n"); +		return -EINVAL; +	} + +	*hccr = (ctx->hcd); +	*hcor = (struct xhci_hcor *)((uint32_t) *hccr +				+ HC_LENGTH(xhci_readl(&(*hccr)->cr_capbase))); + +	debug("Exynos5-xhci: init hccr %x and hcor %x hc_length %d\n", +		(uint32_t)*hccr, (uint32_t)*hcor, +		(uint32_t)HC_LENGTH(xhci_readl(&(*hccr)->cr_capbase))); + +	return 0; +} + +void xhci_hcd_stop(int index) +{ +	struct exynos_xhci *ctx = &exynos; + +	exynos_xhci_core_exit(ctx); +} diff --git a/roms/u-boot/drivers/usb/host/xhci-mem.c b/roms/u-boot/drivers/usb/host/xhci-mem.c new file mode 100644 index 00000000..89908e8a --- /dev/null +++ b/roms/u-boot/drivers/usb/host/xhci-mem.c @@ -0,0 +1,720 @@ +/* + * USB HOST XHCI Controller stack + * + * Based on xHCI host controller driver in linux-kernel + * by Sarah Sharp. + * + * Copyright (C) 2008 Intel Corp. + * Author: Sarah Sharp + * + * Copyright (C) 2013 Samsung Electronics Co.Ltd + * Authors: Vivek Gautam <gautam.vivek@samsung.com> + *	    Vikas Sajjan <vikas.sajjan@samsung.com> + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +#include <common.h> +#include <asm/byteorder.h> +#include <usb.h> +#include <malloc.h> +#include <asm/cache.h> +#include <asm-generic/errno.h> + +#include "xhci.h" + +#define CACHELINE_SIZE		CONFIG_SYS_CACHELINE_SIZE +/** + * flushes the address passed till the length + * + * @param addr	pointer to memory region to be flushed + * @param len	the length of the cache line to be flushed + * @return none + */ +void xhci_flush_cache(uint32_t addr, u32 len) +{ +	BUG_ON((void *)addr == NULL || len == 0); + +	flush_dcache_range(addr & ~(CACHELINE_SIZE - 1), +				ALIGN(addr + len, CACHELINE_SIZE)); +} + +/** + * invalidates the address passed till the length + * + * @param addr	pointer to memory region to be invalidates + * @param len	the length of the cache line to be invalidated + * @return none + */ +void xhci_inval_cache(uint32_t addr, u32 len) +{ +	BUG_ON((void *)addr == NULL || len == 0); + +	invalidate_dcache_range(addr & ~(CACHELINE_SIZE - 1), +				ALIGN(addr + len, CACHELINE_SIZE)); +} + + +/** + * frees the "segment" pointer passed + * + * @param ptr	pointer to "segement" to be freed + * @return none + */ +static void xhci_segment_free(struct xhci_segment *seg) +{ +	free(seg->trbs); +	seg->trbs = NULL; + +	free(seg); +} + +/** + * frees the "ring" pointer passed + * + * @param ptr	pointer to "ring" to be freed + * @return none + */ +static void xhci_ring_free(struct xhci_ring *ring) +{ +	struct xhci_segment *seg; +	struct xhci_segment *first_seg; + +	BUG_ON(!ring); + +	first_seg = ring->first_seg; +	seg = first_seg->next; +	while (seg != first_seg) { +		struct xhci_segment *next = seg->next; +		xhci_segment_free(seg); +		seg = next; +	} +	xhci_segment_free(first_seg); + +	free(ring); +} + +/** + * frees the "xhci_container_ctx" pointer passed + * + * @param ptr	pointer to "xhci_container_ctx" to be freed + * @return none + */ +static void xhci_free_container_ctx(struct xhci_container_ctx *ctx) +{ +	free(ctx->bytes); +	free(ctx); +} + +/** + * frees the virtual devices for "xhci_ctrl" pointer passed + * + * @param ptr	pointer to "xhci_ctrl" whose virtual devices are to be freed + * @return none + */ +static void xhci_free_virt_devices(struct xhci_ctrl *ctrl) +{ +	int i; +	int slot_id; +	struct xhci_virt_device *virt_dev; + +	/* +	 * refactored here to loop through all virt_dev +	 * Slot ID 0 is reserved +	 */ +	for (slot_id = 0; slot_id < MAX_HC_SLOTS; slot_id++) { +		virt_dev = ctrl->devs[slot_id]; +		if (!virt_dev) +			continue; + +		ctrl->dcbaa->dev_context_ptrs[slot_id] = 0; + +		for (i = 0; i < 31; ++i) +			if (virt_dev->eps[i].ring) +				xhci_ring_free(virt_dev->eps[i].ring); + +		if (virt_dev->in_ctx) +			xhci_free_container_ctx(virt_dev->in_ctx); +		if (virt_dev->out_ctx) +			xhci_free_container_ctx(virt_dev->out_ctx); + +		free(virt_dev); +		/* make sure we are pointing to NULL */ +		ctrl->devs[slot_id] = NULL; +	} +} + +/** + * frees all the memory allocated + * + * @param ptr	pointer to "xhci_ctrl" to be cleaned up + * @return none + */ +void xhci_cleanup(struct xhci_ctrl *ctrl) +{ +	xhci_ring_free(ctrl->event_ring); +	xhci_ring_free(ctrl->cmd_ring); +	xhci_free_virt_devices(ctrl); +	free(ctrl->erst.entries); +	free(ctrl->dcbaa); +	memset(ctrl, '\0', sizeof(struct xhci_ctrl)); +} + +/** + * Malloc the aligned memory + * + * @param size	size of memory to be allocated + * @return allocates the memory and returns the aligned pointer + */ +static void *xhci_malloc(unsigned int size) +{ +	void *ptr; +	size_t cacheline_size = max(XHCI_ALIGNMENT, CACHELINE_SIZE); + +	ptr = memalign(cacheline_size, ALIGN(size, cacheline_size)); +	BUG_ON(!ptr); +	memset(ptr, '\0', size); + +	xhci_flush_cache((uint32_t)ptr, size); + +	return ptr; +} + +/** + * Make the prev segment point to the next segment. + * Change the last TRB in the prev segment to be a Link TRB which points to the + * address of the next segment.  The caller needs to set any Link TRB + * related flags, such as End TRB, Toggle Cycle, and no snoop. + * + * @param prev	pointer to the previous segment + * @param next	pointer to the next segment + * @param link_trbs	flag to indicate whether to link the trbs or NOT + * @return none + */ +static void xhci_link_segments(struct xhci_segment *prev, +				struct xhci_segment *next, bool link_trbs) +{ +	u32 val; +	u64 val_64 = 0; + +	if (!prev || !next) +		return; +	prev->next = next; +	if (link_trbs) { +		val_64 = (uintptr_t)next->trbs; +		prev->trbs[TRBS_PER_SEGMENT-1].link.segment_ptr = val_64; + +		/* +		 * Set the last TRB in the segment to +		 * have a TRB type ID of Link TRB +		 */ +		val = le32_to_cpu(prev->trbs[TRBS_PER_SEGMENT-1].link.control); +		val &= ~TRB_TYPE_BITMASK; +		val |= (TRB_LINK << TRB_TYPE_SHIFT); + +		prev->trbs[TRBS_PER_SEGMENT-1].link.control = cpu_to_le32(val); +	} +} + +/** + * Initialises the Ring's enqueue,dequeue,enq_seg pointers + * + * @param ring	pointer to the RING to be intialised + * @return none + */ +static void xhci_initialize_ring_info(struct xhci_ring *ring) +{ +	/* +	 * The ring is empty, so the enqueue pointer == dequeue pointer +	 */ +	ring->enqueue = ring->first_seg->trbs; +	ring->enq_seg = ring->first_seg; +	ring->dequeue = ring->enqueue; +	ring->deq_seg = ring->first_seg; + +	/* +	 * The ring is initialized to 0. The producer must write 1 to the +	 * cycle bit to handover ownership of the TRB, so PCS = 1. +	 * The consumer must compare CCS to the cycle bit to +	 * check ownership, so CCS = 1. +	 */ +	ring->cycle_state = 1; +} + +/** + * Allocates a generic ring segment from the ring pool, sets the dma address, + * initializes the segment to zero, and sets the private next pointer to NULL. + * Section 4.11.1.1: + * "All components of all Command and Transfer TRBs shall be initialized to '0'" + * + * @param	none + * @return pointer to the newly allocated SEGMENT + */ +static struct xhci_segment *xhci_segment_alloc(void) +{ +	struct xhci_segment *seg; + +	seg = (struct xhci_segment *)malloc(sizeof(struct xhci_segment)); +	BUG_ON(!seg); + +	seg->trbs = (union xhci_trb *)xhci_malloc(SEGMENT_SIZE); + +	seg->next = NULL; + +	return seg; +} + +/** + * Create a new ring with zero or more segments. + * TODO: current code only uses one-time-allocated single-segment rings + * of 1KB anyway, so we might as well get rid of all the segment and + * linking code (and maybe increase the size a bit, e.g. 4KB). + * + * + * Link each segment together into a ring. + * Set the end flag and the cycle toggle bit on the last segment. + * See section 4.9.2 and figures 15 and 16 of XHCI spec rev1.0. + * + * @param num_segs	number of segments in the ring + * @param link_trbs	flag to indicate whether to link the trbs or NOT + * @return pointer to the newly created RING + */ +struct xhci_ring *xhci_ring_alloc(unsigned int num_segs, bool link_trbs) +{ +	struct xhci_ring *ring; +	struct xhci_segment *prev; + +	ring = (struct xhci_ring *)malloc(sizeof(struct xhci_ring)); +	BUG_ON(!ring); + +	if (num_segs == 0) +		return ring; + +	ring->first_seg = xhci_segment_alloc(); +	BUG_ON(!ring->first_seg); + +	num_segs--; + +	prev = ring->first_seg; +	while (num_segs > 0) { +		struct xhci_segment *next; + +		next = xhci_segment_alloc(); +		BUG_ON(!next); + +		xhci_link_segments(prev, next, link_trbs); + +		prev = next; +		num_segs--; +	} +	xhci_link_segments(prev, ring->first_seg, link_trbs); +	if (link_trbs) { +		/* See section 4.9.2.1 and 6.4.4.1 */ +		prev->trbs[TRBS_PER_SEGMENT-1].link.control |= +					cpu_to_le32(LINK_TOGGLE); +	} +	xhci_initialize_ring_info(ring); + +	return ring; +} + +/** + * Allocates the Container context + * + * @param ctrl	Host controller data structure + * @param type type of XHCI Container Context + * @return NULL if failed else pointer to the context on success + */ +static struct xhci_container_ctx +		*xhci_alloc_container_ctx(struct xhci_ctrl *ctrl, int type) +{ +	struct xhci_container_ctx *ctx; + +	ctx = (struct xhci_container_ctx *) +		malloc(sizeof(struct xhci_container_ctx)); +	BUG_ON(!ctx); + +	BUG_ON((type != XHCI_CTX_TYPE_DEVICE) && (type != XHCI_CTX_TYPE_INPUT)); +	ctx->type = type; +	ctx->size = (MAX_EP_CTX_NUM + 1) * +			CTX_SIZE(readl(&ctrl->hccr->cr_hccparams)); +	if (type == XHCI_CTX_TYPE_INPUT) +		ctx->size += CTX_SIZE(readl(&ctrl->hccr->cr_hccparams)); + +	ctx->bytes = (u8 *)xhci_malloc(ctx->size); + +	return ctx; +} + +/** + * Allocating virtual device + * + * @param udev	pointer to USB deivce structure + * @return 0 on success else -1 on failure + */ +int xhci_alloc_virt_device(struct usb_device *udev) +{ +	u64 byte_64 = 0; +	unsigned int slot_id = udev->slot_id; +	struct xhci_virt_device *virt_dev; +	struct xhci_ctrl *ctrl = udev->controller; + +	/* Slot ID 0 is reserved */ +	if (ctrl->devs[slot_id]) { +		printf("Virt dev for slot[%d] already allocated\n", slot_id); +		return -EEXIST; +	} + +	ctrl->devs[slot_id] = (struct xhci_virt_device *) +					malloc(sizeof(struct xhci_virt_device)); + +	if (!ctrl->devs[slot_id]) { +		puts("Failed to allocate virtual device\n"); +		return -ENOMEM; +	} + +	memset(ctrl->devs[slot_id], 0, sizeof(struct xhci_virt_device)); +	virt_dev = ctrl->devs[slot_id]; + +	/* Allocate the (output) device context that will be used in the HC. */ +	virt_dev->out_ctx = xhci_alloc_container_ctx(ctrl, +					XHCI_CTX_TYPE_DEVICE); +	if (!virt_dev->out_ctx) { +		puts("Failed to allocate out context for virt dev\n"); +		return -ENOMEM; +	} + +	/* Allocate the (input) device context for address device command */ +	virt_dev->in_ctx = xhci_alloc_container_ctx(ctrl, +					XHCI_CTX_TYPE_INPUT); +	if (!virt_dev->in_ctx) { +		puts("Failed to allocate in context for virt dev\n"); +		return -ENOMEM; +	} + +	/* Allocate endpoint 0 ring */ +	virt_dev->eps[0].ring = xhci_ring_alloc(1, true); + +	byte_64 = (uintptr_t)(virt_dev->out_ctx->bytes); + +	/* Point to output device context in dcbaa. */ +	ctrl->dcbaa->dev_context_ptrs[slot_id] = byte_64; + +	xhci_flush_cache((uint32_t)&ctrl->dcbaa->dev_context_ptrs[slot_id], +							sizeof(__le64)); +	return 0; +} + +/** + * Allocates the necessary data structures + * for XHCI host controller + * + * @param ctrl	Host controller data structure + * @param hccr	pointer to HOST Controller Control Registers + * @param hcor	pointer to HOST Controller Operational Registers + * @return 0 if successful else -1 on failure + */ +int xhci_mem_init(struct xhci_ctrl *ctrl, struct xhci_hccr *hccr, +					struct xhci_hcor *hcor) +{ +	uint64_t val_64; +	uint64_t trb_64; +	uint32_t val; +	unsigned long deq; +	int i; +	struct xhci_segment *seg; + +	/* DCBAA initialization */ +	ctrl->dcbaa = (struct xhci_device_context_array *) +			xhci_malloc(sizeof(struct xhci_device_context_array)); +	if (ctrl->dcbaa == NULL) { +		puts("unable to allocate DCBA\n"); +		return -ENOMEM; +	} + +	val_64 = (uintptr_t)ctrl->dcbaa; +	/* Set the pointer in DCBAA register */ +	xhci_writeq(&hcor->or_dcbaap, val_64); + +	/* Command ring control pointer register initialization */ +	ctrl->cmd_ring = xhci_ring_alloc(1, true); + +	/* Set the address in the Command Ring Control register */ +	trb_64 = (uintptr_t)ctrl->cmd_ring->first_seg->trbs; +	val_64 = xhci_readq(&hcor->or_crcr); +	val_64 = (val_64 & (u64) CMD_RING_RSVD_BITS) | +		(trb_64 & (u64) ~CMD_RING_RSVD_BITS) | +		ctrl->cmd_ring->cycle_state; +	xhci_writeq(&hcor->or_crcr, val_64); + +	/* write the address of db register */ +	val = xhci_readl(&hccr->cr_dboff); +	val &= DBOFF_MASK; +	ctrl->dba = (struct xhci_doorbell_array *)((char *)hccr + val); + +	/* write the address of runtime register */ +	val = xhci_readl(&hccr->cr_rtsoff); +	val &= RTSOFF_MASK; +	ctrl->run_regs = (struct xhci_run_regs *)((char *)hccr + val); + +	/* writting the address of ir_set structure */ +	ctrl->ir_set = &ctrl->run_regs->ir_set[0]; + +	/* Event ring does not maintain link TRB */ +	ctrl->event_ring = xhci_ring_alloc(ERST_NUM_SEGS, false); +	ctrl->erst.entries = (struct xhci_erst_entry *) +		xhci_malloc(sizeof(struct xhci_erst_entry) * ERST_NUM_SEGS); + +	ctrl->erst.num_entries = ERST_NUM_SEGS; + +	for (val = 0, seg = ctrl->event_ring->first_seg; +			val < ERST_NUM_SEGS; +			val++) { +		trb_64 = 0; +		trb_64 = (uintptr_t)seg->trbs; +		struct xhci_erst_entry *entry = &ctrl->erst.entries[val]; +		xhci_writeq(&entry->seg_addr, trb_64); +		entry->seg_size = cpu_to_le32(TRBS_PER_SEGMENT); +		entry->rsvd = 0; +		seg = seg->next; +	} +	xhci_flush_cache((uint32_t)ctrl->erst.entries, +			ERST_NUM_SEGS * sizeof(struct xhci_erst_entry)); + +	deq = (unsigned long)ctrl->event_ring->dequeue; + +	/* Update HC event ring dequeue pointer */ +	xhci_writeq(&ctrl->ir_set->erst_dequeue, +				(u64)deq & (u64)~ERST_PTR_MASK); + +	/* set ERST count with the number of entries in the segment table */ +	val = xhci_readl(&ctrl->ir_set->erst_size); +	val &= ERST_SIZE_MASK; +	val |= ERST_NUM_SEGS; +	xhci_writel(&ctrl->ir_set->erst_size, val); + +	/* this is the event ring segment table pointer */ +	val_64 = xhci_readq(&ctrl->ir_set->erst_base); +	val_64 &= ERST_PTR_MASK; +	val_64 |= ((u32)(ctrl->erst.entries) & ~ERST_PTR_MASK); + +	xhci_writeq(&ctrl->ir_set->erst_base, val_64); + +	/* initializing the virtual devices to NULL */ +	for (i = 0; i < MAX_HC_SLOTS; ++i) +		ctrl->devs[i] = NULL; + +	/* +	 * Just Zero'ing this register completely, +	 * or some spurious Device Notification Events +	 * might screw things here. +	 */ +	xhci_writel(&hcor->or_dnctrl, 0x0); + +	return 0; +} + +/** + * Give the input control context for the passed container context + * + * @param ctx	pointer to the context + * @return pointer to the Input control context data + */ +struct xhci_input_control_ctx +		*xhci_get_input_control_ctx(struct xhci_container_ctx *ctx) +{ +	BUG_ON(ctx->type != XHCI_CTX_TYPE_INPUT); +	return (struct xhci_input_control_ctx *)ctx->bytes; +} + +/** + * Give the slot context for the passed container context + * + * @param ctrl	Host controller data structure + * @param ctx	pointer to the context + * @return pointer to the slot control context data + */ +struct xhci_slot_ctx *xhci_get_slot_ctx(struct xhci_ctrl *ctrl, +				struct xhci_container_ctx *ctx) +{ +	if (ctx->type == XHCI_CTX_TYPE_DEVICE) +		return (struct xhci_slot_ctx *)ctx->bytes; + +	return (struct xhci_slot_ctx *) +		(ctx->bytes + CTX_SIZE(readl(&ctrl->hccr->cr_hccparams))); +} + +/** + * Gets the EP context from based on the ep_index + * + * @param ctrl	Host controller data structure + * @param ctx	context container + * @param ep_index	index of the endpoint + * @return pointer to the End point context + */ +struct xhci_ep_ctx *xhci_get_ep_ctx(struct xhci_ctrl *ctrl, +				    struct xhci_container_ctx *ctx, +				    unsigned int ep_index) +{ +	/* increment ep index by offset of start of ep ctx array */ +	ep_index++; +	if (ctx->type == XHCI_CTX_TYPE_INPUT) +		ep_index++; + +	return (struct xhci_ep_ctx *) +		(ctx->bytes + +		(ep_index * CTX_SIZE(readl(&ctrl->hccr->cr_hccparams)))); +} + +/** + * Copy output xhci_ep_ctx to the input xhci_ep_ctx copy. + * Useful when you want to change one particular aspect of the endpoint + * and then issue a configure endpoint command. + * + * @param ctrl	Host controller data structure + * @param in_ctx contains the input context + * @param out_ctx contains the input context + * @param ep_index index of the end point + * @return none + */ +void xhci_endpoint_copy(struct xhci_ctrl *ctrl, +			struct xhci_container_ctx *in_ctx, +			struct xhci_container_ctx *out_ctx, +			unsigned int ep_index) +{ +	struct xhci_ep_ctx *out_ep_ctx; +	struct xhci_ep_ctx *in_ep_ctx; + +	out_ep_ctx = xhci_get_ep_ctx(ctrl, out_ctx, ep_index); +	in_ep_ctx = xhci_get_ep_ctx(ctrl, in_ctx, ep_index); + +	in_ep_ctx->ep_info = out_ep_ctx->ep_info; +	in_ep_ctx->ep_info2 = out_ep_ctx->ep_info2; +	in_ep_ctx->deq = out_ep_ctx->deq; +	in_ep_ctx->tx_info = out_ep_ctx->tx_info; +} + +/** + * Copy output xhci_slot_ctx to the input xhci_slot_ctx. + * Useful when you want to change one particular aspect of the endpoint + * and then issue a configure endpoint command. + * Only the context entries field matters, but + * we'll copy the whole thing anyway. + * + * @param ctrl	Host controller data structure + * @param in_ctx contains the inpout context + * @param out_ctx contains the inpout context + * @return none + */ +void xhci_slot_copy(struct xhci_ctrl *ctrl, struct xhci_container_ctx *in_ctx, +					struct xhci_container_ctx *out_ctx) +{ +	struct xhci_slot_ctx *in_slot_ctx; +	struct xhci_slot_ctx *out_slot_ctx; + +	in_slot_ctx = xhci_get_slot_ctx(ctrl, in_ctx); +	out_slot_ctx = xhci_get_slot_ctx(ctrl, out_ctx); + +	in_slot_ctx->dev_info = out_slot_ctx->dev_info; +	in_slot_ctx->dev_info2 = out_slot_ctx->dev_info2; +	in_slot_ctx->tt_info = out_slot_ctx->tt_info; +	in_slot_ctx->dev_state = out_slot_ctx->dev_state; +} + +/** + * Setup an xHCI virtual device for a Set Address command + * + * @param udev pointer to the Device Data Structure + * @return returns negative value on failure else 0 on success + */ +void xhci_setup_addressable_virt_dev(struct usb_device *udev) +{ +	struct usb_device *hop = udev; +	struct xhci_virt_device *virt_dev; +	struct xhci_ep_ctx *ep0_ctx; +	struct xhci_slot_ctx *slot_ctx; +	u32 port_num = 0; +	u64 trb_64 = 0; +	struct xhci_ctrl *ctrl = udev->controller; + +	virt_dev = ctrl->devs[udev->slot_id]; + +	BUG_ON(!virt_dev); + +	/* Extract the EP0 and Slot Ctrl */ +	ep0_ctx = xhci_get_ep_ctx(ctrl, virt_dev->in_ctx, 0); +	slot_ctx = xhci_get_slot_ctx(ctrl, virt_dev->in_ctx); + +	/* Only the control endpoint is valid - one endpoint context */ +	slot_ctx->dev_info |= cpu_to_le32(LAST_CTX(1) | 0); + +	switch (udev->speed) { +	case USB_SPEED_SUPER: +		slot_ctx->dev_info |= cpu_to_le32(SLOT_SPEED_SS); +		break; +	case USB_SPEED_HIGH: +		slot_ctx->dev_info |= cpu_to_le32(SLOT_SPEED_HS); +		break; +	case USB_SPEED_FULL: +		slot_ctx->dev_info |= cpu_to_le32(SLOT_SPEED_FS); +		break; +	case USB_SPEED_LOW: +		slot_ctx->dev_info |= cpu_to_le32(SLOT_SPEED_LS); +		break; +	default: +		/* Speed was set earlier, this shouldn't happen. */ +		BUG(); +	} + +	/* Extract the root hub port number */ +	if (hop->parent) +		while (hop->parent->parent) +			hop = hop->parent; +	port_num = hop->portnr; +	debug("port_num = %d\n", port_num); + +	slot_ctx->dev_info2 |= +			cpu_to_le32(((port_num & ROOT_HUB_PORT_MASK) << +				ROOT_HUB_PORT_SHIFT)); + +	/* Step 4 - ring already allocated */ +	/* Step 5 */ +	ep0_ctx->ep_info2 = cpu_to_le32(CTRL_EP << EP_TYPE_SHIFT); +	debug("SPEED = %d\n", udev->speed); + +	switch (udev->speed) { +	case USB_SPEED_SUPER: +		ep0_ctx->ep_info2 |= cpu_to_le32(((512 & MAX_PACKET_MASK) << +					MAX_PACKET_SHIFT)); +		debug("Setting Packet size = 512bytes\n"); +		break; +	case USB_SPEED_HIGH: +	/* USB core guesses at a 64-byte max packet first for FS devices */ +	case USB_SPEED_FULL: +		ep0_ctx->ep_info2 |= cpu_to_le32(((64 & MAX_PACKET_MASK) << +					MAX_PACKET_SHIFT)); +		debug("Setting Packet size = 64bytes\n"); +		break; +	case USB_SPEED_LOW: +		ep0_ctx->ep_info2 |= cpu_to_le32(((8 & MAX_PACKET_MASK) << +					MAX_PACKET_SHIFT)); +		debug("Setting Packet size = 8bytes\n"); +		break; +	default: +		/* New speed? */ +		BUG(); +	} + +	/* EP 0 can handle "burst" sizes of 1, so Max Burst Size field is 0 */ +	ep0_ctx->ep_info2 |= +			cpu_to_le32(((0 & MAX_BURST_MASK) << MAX_BURST_SHIFT) | +			((3 & ERROR_COUNT_MASK) << ERROR_COUNT_SHIFT)); + +	trb_64 = (uintptr_t)virt_dev->eps[0].ring->first_seg->trbs; +	ep0_ctx->deq = cpu_to_le64(trb_64 | virt_dev->eps[0].ring->cycle_state); + +	/* Steps 7 and 8 were done in xhci_alloc_virt_device() */ + +	xhci_flush_cache((uint32_t)ep0_ctx, sizeof(struct xhci_ep_ctx)); +	xhci_flush_cache((uint32_t)slot_ctx, sizeof(struct xhci_slot_ctx)); +} diff --git a/roms/u-boot/drivers/usb/host/xhci-omap.c b/roms/u-boot/drivers/usb/host/xhci-omap.c new file mode 100644 index 00000000..e667810b --- /dev/null +++ b/roms/u-boot/drivers/usb/host/xhci-omap.c @@ -0,0 +1,158 @@ +/* + * OMAP USB HOST xHCI Controller + * + * (C) Copyright 2013 + * Texas Instruments, <www.ti.com> + * + * Author: Dan Murphy <dmurphy@ti.com> + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +#include <common.h> +#include <usb.h> +#include <asm-generic/errno.h> +#include <asm/omap_common.h> +#include <asm/arch/cpu.h> +#include <asm/arch/sys_proto.h> + +#include <linux/compat.h> +#include <linux/usb/dwc3.h> +#include <linux/usb/xhci-omap.h> + +#include "xhci.h" + +/* Declare global data pointer */ +DECLARE_GLOBAL_DATA_PTR; + +static struct omap_xhci omap; + +inline int __board_usb_init(int index, enum usb_init_type init) +{ +	return 0; +} +int board_usb_init(int index, enum usb_init_type init) +	__attribute__((weak, alias("__board_usb_init"))); + +static void dwc3_set_mode(struct dwc3 *dwc3_reg, u32 mode) +{ +	clrsetbits_le32(&dwc3_reg->g_ctl, +			DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_OTG), +			DWC3_GCTL_PRTCAPDIR(mode)); +} + +static void dwc3_core_soft_reset(struct dwc3 *dwc3_reg) +{ +	/* Before Resetting PHY, put Core in Reset */ +	setbits_le32(&dwc3_reg->g_ctl, DWC3_GCTL_CORESOFTRESET); + +	omap_reset_usb_phy(dwc3_reg); + +	/* After PHYs are stable we can take Core out of reset state */ +	clrbits_le32(&dwc3_reg->g_ctl, DWC3_GCTL_CORESOFTRESET); +} + +static int dwc3_core_init(struct dwc3 *dwc3_reg) +{ +	u32 reg; +	u32 revision; +	unsigned int dwc3_hwparams1; + +	revision = readl(&dwc3_reg->g_snpsid); +	/* This should read as U3 followed by revision number */ +	if ((revision & DWC3_GSNPSID_MASK) != 0x55330000) { +		puts("this is not a DesignWare USB3 DRD Core\n"); +		return -1; +	} + +	dwc3_core_soft_reset(dwc3_reg); + +	dwc3_hwparams1 = readl(&dwc3_reg->g_hwparams1); + +	reg = readl(&dwc3_reg->g_ctl); +	reg &= ~DWC3_GCTL_SCALEDOWN_MASK; +	reg &= ~DWC3_GCTL_DISSCRAMBLE; +	switch (DWC3_GHWPARAMS1_EN_PWROPT(dwc3_hwparams1)) { +	case DWC3_GHWPARAMS1_EN_PWROPT_CLK: +		reg &= ~DWC3_GCTL_DSBLCLKGTNG; +		break; +	default: +		debug("No power optimization available\n"); +	} + +	/* +	 * WORKAROUND: DWC3 revisions <1.90a have a bug +	 * where the device can fail to connect at SuperSpeed +	 * and falls back to high-speed mode which causes +	 * the device to enter a Connect/Disconnect loop +	 */ +	if ((revision & DWC3_REVISION_MASK) < 0x190a) +		reg |= DWC3_GCTL_U2RSTECN; + +	writel(reg, &dwc3_reg->g_ctl); + +	return 0; +} + +static int omap_xhci_core_init(struct omap_xhci *omap) +{ +	int ret = 0; + +	omap_enable_phy(omap); + +	ret = dwc3_core_init(omap->dwc3_reg); +	if (ret) { +		debug("%s:failed to initialize core\n", __func__); +		return ret; +	} + +	/* We are hard-coding DWC3 core to Host Mode */ +	dwc3_set_mode(omap->dwc3_reg, DWC3_GCTL_PRTCAP_HOST); + +	return ret; +} + +static void omap_xhci_core_exit(struct omap_xhci *omap) +{ +	usb_phy_power(0); +} + +int xhci_hcd_init(int index, struct xhci_hccr **hccr, struct xhci_hcor **hcor) +{ +	struct omap_xhci *ctx = &omap; +	int ret = 0; + +	ctx->hcd = (struct xhci_hccr *)OMAP_XHCI_BASE; +	ctx->dwc3_reg = (struct dwc3 *)((char *)(ctx->hcd) + DWC3_REG_OFFSET); +	ctx->usb3_phy = (struct omap_usb3_phy *)OMAP_OCP1_SCP_BASE; +	ctx->otg_wrapper = (struct omap_dwc_wrapper *)OMAP_OTG_WRAPPER_BASE; + +	ret = board_usb_init(index, USB_INIT_HOST); +	if (ret != 0) { +		puts("Failed to initialize board for USB\n"); +		return ret; +	} + +	ret = omap_xhci_core_init(ctx); +	if (ret < 0) { +		puts("Failed to initialize xhci\n"); +		return ret; +	} + +	*hccr = (struct xhci_hccr *)(OMAP_XHCI_BASE); +	*hcor = (struct xhci_hcor *)((uint32_t) *hccr +				+ HC_LENGTH(xhci_readl(&(*hccr)->cr_capbase))); + +	debug("omap-xhci: init hccr %x and hcor %x hc_length %d\n", +	      (uint32_t)*hccr, (uint32_t)*hcor, +	      (uint32_t)HC_LENGTH(xhci_readl(&(*hccr)->cr_capbase))); + +	return ret; +} + +void xhci_hcd_stop(int index) +{ +	struct omap_xhci *ctx = &omap; + +	omap_xhci_core_exit(ctx); +} diff --git a/roms/u-boot/drivers/usb/host/xhci-ring.c b/roms/u-boot/drivers/usb/host/xhci-ring.c new file mode 100644 index 00000000..19c3ec62 --- /dev/null +++ b/roms/u-boot/drivers/usb/host/xhci-ring.c @@ -0,0 +1,939 @@ +/* + * USB HOST XHCI Controller stack + * + * Based on xHCI host controller driver in linux-kernel + * by Sarah Sharp. + * + * Copyright (C) 2008 Intel Corp. + * Author: Sarah Sharp + * + * Copyright (C) 2013 Samsung Electronics Co.Ltd + * Authors: Vivek Gautam <gautam.vivek@samsung.com> + *	    Vikas Sajjan <vikas.sajjan@samsung.com> + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +#include <common.h> +#include <asm/byteorder.h> +#include <usb.h> +#include <asm/unaligned.h> +#include <asm-generic/errno.h> + +#include "xhci.h" + +/** + * Is this TRB a link TRB or was the last TRB the last TRB in this event ring + * segment?  I.e. would the updated event TRB pointer step off the end of the + * event seg ? + * + * @param ctrl	Host controller data structure + * @param ring	pointer to the ring + * @param seg	poniter to the segment to which TRB belongs + * @param trb	poniter to the ring trb + * @return 1 if this TRB a link TRB else 0 + */ +static int last_trb(struct xhci_ctrl *ctrl, struct xhci_ring *ring, +			struct xhci_segment *seg, union xhci_trb *trb) +{ +	if (ring == ctrl->event_ring) +		return trb == &seg->trbs[TRBS_PER_SEGMENT]; +	else +		return TRB_TYPE_LINK_LE32(trb->link.control); +} + +/** + * Does this link TRB point to the first segment in a ring, + * or was the previous TRB the last TRB on the last segment in the ERST? + * + * @param ctrl	Host controller data structure + * @param ring	pointer to the ring + * @param seg	poniter to the segment to which TRB belongs + * @param trb	poniter to the ring trb + * @return 1 if this TRB is the last TRB on the last segment else 0 + */ +static bool last_trb_on_last_seg(struct xhci_ctrl *ctrl, +				 struct xhci_ring *ring, +				 struct xhci_segment *seg, +				 union xhci_trb *trb) +{ +	if (ring == ctrl->event_ring) +		return ((trb == &seg->trbs[TRBS_PER_SEGMENT]) && +			(seg->next == ring->first_seg)); +	else +		return le32_to_cpu(trb->link.control) & LINK_TOGGLE; +} + +/** + * See Cycle bit rules. SW is the consumer for the event ring only. + * Don't make a ring full of link TRBs.  That would be dumb and this would loop. + * + * If we've just enqueued a TRB that is in the middle of a TD (meaning the + * chain bit is set), then set the chain bit in all the following link TRBs. + * If we've enqueued the last TRB in a TD, make sure the following link TRBs + * have their chain bit cleared (so that each Link TRB is a separate TD). + * + * Section 6.4.4.1 of the 0.95 spec says link TRBs cannot have the chain bit + * set, but other sections talk about dealing with the chain bit set.  This was + * fixed in the 0.96 specification errata, but we have to assume that all 0.95 + * xHCI hardware can't handle the chain bit being cleared on a link TRB. + * + * @param ctrl	Host controller data structure + * @param ring	pointer to the ring + * @param more_trbs_coming	flag to indicate whether more trbs + *				are expected or NOT. + *				Will you enqueue more TRBs before calling + *				prepare_ring()? + * @return none + */ +static void inc_enq(struct xhci_ctrl *ctrl, struct xhci_ring *ring, +						bool more_trbs_coming) +{ +	u32 chain; +	union xhci_trb *next; + +	chain = le32_to_cpu(ring->enqueue->generic.field[3]) & TRB_CHAIN; +	next = ++(ring->enqueue); + +	/* +	 * Update the dequeue pointer further if that was a link TRB or we're at +	 * the end of an event ring segment (which doesn't have link TRBS) +	 */ +	while (last_trb(ctrl, ring, ring->enq_seg, next)) { +		if (ring != ctrl->event_ring) { +			/* +			 * If the caller doesn't plan on enqueueing more +			 * TDs before ringing the doorbell, then we +			 * don't want to give the link TRB to the +			 * hardware just yet.  We'll give the link TRB +			 * back in prepare_ring() just before we enqueue +			 * the TD at the top of the ring. +			 */ +			if (!chain && !more_trbs_coming) +				break; + +			/* +			 * If we're not dealing with 0.95 hardware or +			 * isoc rings on AMD 0.96 host, +			 * carry over the chain bit of the previous TRB +			 * (which may mean the chain bit is cleared). +			 */ +			next->link.control &= cpu_to_le32(~TRB_CHAIN); +			next->link.control |= cpu_to_le32(chain); + +			next->link.control ^= cpu_to_le32(TRB_CYCLE); +			xhci_flush_cache((uint32_t)next, +						sizeof(union xhci_trb)); +		} +		/* Toggle the cycle bit after the last ring segment. */ +		if (last_trb_on_last_seg(ctrl, ring, +					ring->enq_seg, next)) +			ring->cycle_state = (ring->cycle_state ? 0 : 1); + +		ring->enq_seg = ring->enq_seg->next; +		ring->enqueue = ring->enq_seg->trbs; +		next = ring->enqueue; +	} +} + +/** + * See Cycle bit rules. SW is the consumer for the event ring only. + * Don't make a ring full of link TRBs.  That would be dumb and this would loop. + * + * @param ctrl	Host controller data structure + * @param ring	Ring whose Dequeue TRB pointer needs to be incremented. + * return none + */ +static void inc_deq(struct xhci_ctrl *ctrl, struct xhci_ring *ring) +{ +	do { +		/* +		 * Update the dequeue pointer further if that was a link TRB or +		 * we're at the end of an event ring segment (which doesn't have +		 * link TRBS) +		 */ +		if (last_trb(ctrl, ring, ring->deq_seg, ring->dequeue)) { +			if (ring == ctrl->event_ring && +					last_trb_on_last_seg(ctrl, ring, +						ring->deq_seg, ring->dequeue)) { +				ring->cycle_state = (ring->cycle_state ? 0 : 1); +			} +			ring->deq_seg = ring->deq_seg->next; +			ring->dequeue = ring->deq_seg->trbs; +		} else { +			ring->dequeue++; +		} +	} while (last_trb(ctrl, ring, ring->deq_seg, ring->dequeue)); +} + +/** + * Generic function for queueing a TRB on a ring. + * The caller must have checked to make sure there's room on the ring. + * + * @param	more_trbs_coming:   Will you enqueue more TRBs before calling + *				prepare_ring()? + * @param ctrl	Host controller data structure + * @param ring	pointer to the ring + * @param more_trbs_coming	flag to indicate whether more trbs + * @param trb_fields	pointer to trb field array containing TRB contents + * @return pointer to the enqueued trb + */ +static struct xhci_generic_trb *queue_trb(struct xhci_ctrl *ctrl, +					  struct xhci_ring *ring, +					  bool more_trbs_coming, +					  unsigned int *trb_fields) +{ +	struct xhci_generic_trb *trb; +	int i; + +	trb = &ring->enqueue->generic; + +	for (i = 0; i < 4; i++) +		trb->field[i] = cpu_to_le32(trb_fields[i]); + +	xhci_flush_cache((uint32_t)trb, sizeof(struct xhci_generic_trb)); + +	inc_enq(ctrl, ring, more_trbs_coming); + +	return trb; +} + +/** + * Does various checks on the endpoint ring, and makes it ready + * to queue num_trbs. + * + * @param ctrl		Host controller data structure + * @param ep_ring	pointer to the EP Transfer Ring + * @param ep_state	State of the End Point + * @return error code in case of invalid ep_state, 0 on success + */ +static int prepare_ring(struct xhci_ctrl *ctrl, struct xhci_ring *ep_ring, +							u32 ep_state) +{ +	union xhci_trb *next = ep_ring->enqueue; + +	/* Make sure the endpoint has been added to xHC schedule */ +	switch (ep_state) { +	case EP_STATE_DISABLED: +		/* +		 * USB core changed config/interfaces without notifying us, +		 * or hardware is reporting the wrong state. +		 */ +		puts("WARN urb submitted to disabled ep\n"); +		return -ENOENT; +	case EP_STATE_ERROR: +		puts("WARN waiting for error on ep to be cleared\n"); +		return -EINVAL; +	case EP_STATE_HALTED: +		puts("WARN halted endpoint, queueing URB anyway.\n"); +	case EP_STATE_STOPPED: +	case EP_STATE_RUNNING: +		debug("EP STATE RUNNING.\n"); +		break; +	default: +		puts("ERROR unknown endpoint state for ep\n"); +		return -EINVAL; +	} + +	while (last_trb(ctrl, ep_ring, ep_ring->enq_seg, next)) { +		/* +		 * If we're not dealing with 0.95 hardware or isoc rings +		 * on AMD 0.96 host, clear the chain bit. +		 */ +		next->link.control &= cpu_to_le32(~TRB_CHAIN); + +		next->link.control ^= cpu_to_le32(TRB_CYCLE); + +		xhci_flush_cache((uint32_t)next, sizeof(union xhci_trb)); + +		/* Toggle the cycle bit after the last ring segment. */ +		if (last_trb_on_last_seg(ctrl, ep_ring, +					ep_ring->enq_seg, next)) +			ep_ring->cycle_state = (ep_ring->cycle_state ? 0 : 1); +		ep_ring->enq_seg = ep_ring->enq_seg->next; +		ep_ring->enqueue = ep_ring->enq_seg->trbs; +		next = ep_ring->enqueue; +	} + +	return 0; +} + +/** + * Generic function for queueing a command TRB on the command ring. + * Check to make sure there's room on the command ring for one command TRB. + * + * @param ctrl		Host controller data structure + * @param ptr		Pointer address to write in the first two fields (opt.) + * @param slot_id	Slot ID to encode in the flags field (opt.) + * @param ep_index	Endpoint index to encode in the flags field (opt.) + * @param cmd		Command type to enqueue + * @return none + */ +void xhci_queue_command(struct xhci_ctrl *ctrl, u8 *ptr, u32 slot_id, +			u32 ep_index, trb_type cmd) +{ +	u32 fields[4]; +	u64 val_64 = (uintptr_t)ptr; + +	BUG_ON(prepare_ring(ctrl, ctrl->cmd_ring, EP_STATE_RUNNING)); + +	fields[0] = lower_32_bits(val_64); +	fields[1] = upper_32_bits(val_64); +	fields[2] = 0; +	fields[3] = TRB_TYPE(cmd) | EP_ID_FOR_TRB(ep_index) | +		    SLOT_ID_FOR_TRB(slot_id) | ctrl->cmd_ring->cycle_state; + +	queue_trb(ctrl, ctrl->cmd_ring, false, fields); + +	/* Ring the command ring doorbell */ +	xhci_writel(&ctrl->dba->doorbell[0], DB_VALUE_HOST); +} + +/** + * The TD size is the number of bytes remaining in the TD (including this TRB), + * right shifted by 10. + * It must fit in bits 21:17, so it can't be bigger than 31. + * + * @param remainder	remaining packets to be sent + * @return remainder if remainder is less than max else max + */ +static u32 xhci_td_remainder(unsigned int remainder) +{ +	u32 max = (1 << (21 - 17 + 1)) - 1; + +	if ((remainder >> 10) >= max) +		return max << 17; +	else +		return (remainder >> 10) << 17; +} + +/** + * Finds out the remanining packets to be sent + * + * @param running_total	total size sent so far + * @param trb_buff_len	length of the TRB Buffer + * @param total_packet_count	total packet count + * @param maxpacketsize		max packet size of current pipe + * @param num_trbs_left		number of TRBs left to be processed + * @return 0 if running_total or trb_buff_len is 0, else remainder + */ +static u32 xhci_v1_0_td_remainder(int running_total, +				int trb_buff_len, +				unsigned int total_packet_count, +				int maxpacketsize, +				unsigned int num_trbs_left) +{ +	int packets_transferred; + +	/* One TRB with a zero-length data packet. */ +	if (num_trbs_left == 0 || (running_total == 0 && trb_buff_len == 0)) +		return 0; + +	/* +	 * All the TRB queueing functions don't count the current TRB in +	 * running_total. +	 */ +	packets_transferred = (running_total + trb_buff_len) / maxpacketsize; + +	if ((total_packet_count - packets_transferred) > 31) +		return 31 << 17; +	return (total_packet_count - packets_transferred) << 17; +} + +/** + * Ring the doorbell of the End Point + * + * @param udev		pointer to the USB device structure + * @param ep_index	index of the endpoint + * @param start_cycle	cycle flag of the first TRB + * @param start_trb	pionter to the first TRB + * @return none + */ +static void giveback_first_trb(struct usb_device *udev, int ep_index, +				int start_cycle, +				struct xhci_generic_trb *start_trb) +{ +	struct xhci_ctrl *ctrl = udev->controller; + +	/* +	 * Pass all the TRBs to the hardware at once and make sure this write +	 * isn't reordered. +	 */ +	if (start_cycle) +		start_trb->field[3] |= cpu_to_le32(start_cycle); +	else +		start_trb->field[3] &= cpu_to_le32(~TRB_CYCLE); + +	xhci_flush_cache((uint32_t)start_trb, sizeof(struct xhci_generic_trb)); + +	/* Ringing EP doorbell here */ +	xhci_writel(&ctrl->dba->doorbell[udev->slot_id], +				DB_VALUE(ep_index, 0)); + +	return; +} + +/**** POLLING mechanism for XHCI ****/ + +/** + * Finalizes a handled event TRB by advancing our dequeue pointer and giving + * the TRB back to the hardware for recycling. Must call this exactly once at + * the end of each event handler, and not touch the TRB again afterwards. + * + * @param ctrl	Host controller data structure + * @return none + */ +void xhci_acknowledge_event(struct xhci_ctrl *ctrl) +{ +	/* Advance our dequeue pointer to the next event */ +	inc_deq(ctrl, ctrl->event_ring); + +	/* Inform the hardware */ +	xhci_writeq(&ctrl->ir_set->erst_dequeue, +		(uintptr_t)ctrl->event_ring->dequeue | ERST_EHB); +} + +/** + * Checks if there is a new event to handle on the event ring. + * + * @param ctrl	Host controller data structure + * @return 0 if failure else 1 on success + */ +static int event_ready(struct xhci_ctrl *ctrl) +{ +	union xhci_trb *event; + +	xhci_inval_cache((uint32_t)ctrl->event_ring->dequeue, +					sizeof(union xhci_trb)); + +	event = ctrl->event_ring->dequeue; + +	/* Does the HC or OS own the TRB? */ +	if ((le32_to_cpu(event->event_cmd.flags) & TRB_CYCLE) != +		ctrl->event_ring->cycle_state) +		return 0; + +	return 1; +} + +/** + * Waits for a specific type of event and returns it. Discards unexpected + * events. Caller *must* call xhci_acknowledge_event() after it is finished + * processing the event, and must not access the returned pointer afterwards. + * + * @param ctrl		Host controller data structure + * @param expected	TRB type expected from Event TRB + * @return pointer to event trb + */ +union xhci_trb *xhci_wait_for_event(struct xhci_ctrl *ctrl, trb_type expected) +{ +	trb_type type; +	unsigned long ts = get_timer(0); + +	do { +		union xhci_trb *event = ctrl->event_ring->dequeue; + +		if (!event_ready(ctrl)) +			continue; + +		type = TRB_FIELD_TO_TYPE(le32_to_cpu(event->event_cmd.flags)); +		if (type == expected) +			return event; + +		if (type == TRB_PORT_STATUS) +		/* TODO: remove this once enumeration has been reworked */ +			/* +			 * Port status change events always have a +			 * successful completion code +			 */ +			BUG_ON(GET_COMP_CODE( +				le32_to_cpu(event->generic.field[2])) != +								COMP_SUCCESS); +		else +			printf("Unexpected XHCI event TRB, skipping... " +				"(%08x %08x %08x %08x)\n", +				le32_to_cpu(event->generic.field[0]), +				le32_to_cpu(event->generic.field[1]), +				le32_to_cpu(event->generic.field[2]), +				le32_to_cpu(event->generic.field[3])); + +		xhci_acknowledge_event(ctrl); +	} while (get_timer(ts) < XHCI_TIMEOUT); + +	if (expected == TRB_TRANSFER) +		return NULL; + +	printf("XHCI timeout on event type %d... cannot recover.\n", expected); +	BUG(); +} + +/* + * Stops transfer processing for an endpoint and throws away all unprocessed + * TRBs by setting the xHC's dequeue pointer to our enqueue pointer. The next + * xhci_bulk_tx/xhci_ctrl_tx on this enpoint will add new transfers there and + * ring the doorbell, causing this endpoint to start working again. + * (Careful: This will BUG() when there was no transfer in progress. Shouldn't + * happen in practice for current uses and is too complicated to fix right now.) + */ +static void abort_td(struct usb_device *udev, int ep_index) +{ +	struct xhci_ctrl *ctrl = udev->controller; +	struct xhci_ring *ring =  ctrl->devs[udev->slot_id]->eps[ep_index].ring; +	union xhci_trb *event; +	u32 field; + +	xhci_queue_command(ctrl, NULL, udev->slot_id, ep_index, TRB_STOP_RING); + +	event = xhci_wait_for_event(ctrl, TRB_TRANSFER); +	field = le32_to_cpu(event->trans_event.flags); +	BUG_ON(TRB_TO_SLOT_ID(field) != udev->slot_id); +	BUG_ON(TRB_TO_EP_INDEX(field) != ep_index); +	BUG_ON(GET_COMP_CODE(le32_to_cpu(event->trans_event.transfer_len +		!= COMP_STOP))); +	xhci_acknowledge_event(ctrl); + +	event = xhci_wait_for_event(ctrl, TRB_COMPLETION); +	BUG_ON(TRB_TO_SLOT_ID(le32_to_cpu(event->event_cmd.flags)) +		!= udev->slot_id || GET_COMP_CODE(le32_to_cpu( +		event->event_cmd.status)) != COMP_SUCCESS); +	xhci_acknowledge_event(ctrl); + +	xhci_queue_command(ctrl, (void *)((uintptr_t)ring->enqueue | +		ring->cycle_state), udev->slot_id, ep_index, TRB_SET_DEQ); +	event = xhci_wait_for_event(ctrl, TRB_COMPLETION); +	BUG_ON(TRB_TO_SLOT_ID(le32_to_cpu(event->event_cmd.flags)) +		!= udev->slot_id || GET_COMP_CODE(le32_to_cpu( +		event->event_cmd.status)) != COMP_SUCCESS); +	xhci_acknowledge_event(ctrl); +} + +static void record_transfer_result(struct usb_device *udev, +				   union xhci_trb *event, int length) +{ +	udev->act_len = min(length, length - +		EVENT_TRB_LEN(le32_to_cpu(event->trans_event.transfer_len))); + +	switch (GET_COMP_CODE(le32_to_cpu(event->trans_event.transfer_len))) { +	case COMP_SUCCESS: +		BUG_ON(udev->act_len != length); +		/* fallthrough */ +	case COMP_SHORT_TX: +		udev->status = 0; +		break; +	case COMP_STALL: +		udev->status = USB_ST_STALLED; +		break; +	case COMP_DB_ERR: +	case COMP_TRB_ERR: +		udev->status = USB_ST_BUF_ERR; +		break; +	case COMP_BABBLE: +		udev->status = USB_ST_BABBLE_DET; +		break; +	default: +		udev->status = 0x80;  /* USB_ST_TOO_LAZY_TO_MAKE_A_NEW_MACRO */ +	} +} + +/**** Bulk and Control transfer methods ****/ +/** + * Queues up the BULK Request + * + * @param udev		pointer to the USB device structure + * @param pipe		contains the DIR_IN or OUT , devnum + * @param length	length of the buffer + * @param buffer	buffer to be read/written based on the request + * @return returns 0 if successful else -1 on failure + */ +int xhci_bulk_tx(struct usb_device *udev, unsigned long pipe, +			int length, void *buffer) +{ +	int num_trbs = 0; +	struct xhci_generic_trb *start_trb; +	bool first_trb = 0; +	int start_cycle; +	u32 field = 0; +	u32 length_field = 0; +	struct xhci_ctrl *ctrl = udev->controller; +	int slot_id = udev->slot_id; +	int ep_index; +	struct xhci_virt_device *virt_dev; +	struct xhci_ep_ctx *ep_ctx; +	struct xhci_ring *ring;		/* EP transfer ring */ +	union xhci_trb *event; + +	int running_total, trb_buff_len; +	unsigned int total_packet_count; +	int maxpacketsize; +	u64 addr; +	int ret; +	u32 trb_fields[4]; +	u64 val_64 = (uintptr_t)buffer; + +	debug("dev=%p, pipe=%lx, buffer=%p, length=%d\n", +		udev, pipe, buffer, length); + +	ep_index = usb_pipe_ep_index(pipe); +	virt_dev = ctrl->devs[slot_id]; + +	xhci_inval_cache((uint32_t)virt_dev->out_ctx->bytes, +					virt_dev->out_ctx->size); + +	ep_ctx = xhci_get_ep_ctx(ctrl, virt_dev->out_ctx, ep_index); + +	ring = virt_dev->eps[ep_index].ring; +	/* +	 * How much data is (potentially) left before the 64KB boundary? +	 * XHCI Spec puts restriction( TABLE 49 and 6.4.1 section of XHCI Spec) +	 * that the buffer should not span 64KB boundary. if so +	 * we send request in more than 1 TRB by chaining them. +	 */ +	running_total = TRB_MAX_BUFF_SIZE - +			(lower_32_bits(val_64) & (TRB_MAX_BUFF_SIZE - 1)); +	trb_buff_len = running_total; +	running_total &= TRB_MAX_BUFF_SIZE - 1; + +	/* +	 * If there's some data on this 64KB chunk, or we have to send a +	 * zero-length transfer, we need at least one TRB +	 */ +	if (running_total != 0 || length == 0) +		num_trbs++; + +	/* How many more 64KB chunks to transfer, how many more TRBs? */ +	while (running_total < length) { +		num_trbs++; +		running_total += TRB_MAX_BUFF_SIZE; +	} + +	/* +	 * XXX: Calling routine prepare_ring() called in place of +	 * prepare_trasfer() as there in 'Linux' since we are not +	 * maintaining multiple TDs/transfer at the same time. +	 */ +	ret = prepare_ring(ctrl, ring, +			   le32_to_cpu(ep_ctx->ep_info) & EP_STATE_MASK); +	if (ret < 0) +		return ret; + +	/* +	 * Don't give the first TRB to the hardware (by toggling the cycle bit) +	 * until we've finished creating all the other TRBs.  The ring's cycle +	 * state may change as we enqueue the other TRBs, so save it too. +	 */ +	start_trb = &ring->enqueue->generic; +	start_cycle = ring->cycle_state; + +	running_total = 0; +	maxpacketsize = usb_maxpacket(udev, pipe); + +	total_packet_count = DIV_ROUND_UP(length, maxpacketsize); + +	/* How much data is in the first TRB? */ +	/* +	 * How much data is (potentially) left before the 64KB boundary? +	 * XHCI Spec puts restriction( TABLE 49 and 6.4.1 section of XHCI Spec) +	 * that the buffer should not span 64KB boundary. if so +	 * we send request in more than 1 TRB by chaining them. +	 */ +	addr = val_64; + +	if (trb_buff_len > length) +		trb_buff_len = length; + +	first_trb = true; + +	/* flush the buffer before use */ +	xhci_flush_cache((uint32_t)buffer, length); + +	/* Queue the first TRB, even if it's zero-length */ +	do { +		u32 remainder = 0; +		field = 0; +		/* Don't change the cycle bit of the first TRB until later */ +		if (first_trb) { +			first_trb = false; +			if (start_cycle == 0) +				field |= TRB_CYCLE; +		} else { +			field |= ring->cycle_state; +		} + +		/* +		 * Chain all the TRBs together; clear the chain bit in the last +		 * TRB to indicate it's the last TRB in the chain. +		 */ +		if (num_trbs > 1) +			field |= TRB_CHAIN; +		else +			field |= TRB_IOC; + +		/* Only set interrupt on short packet for IN endpoints */ +		if (usb_pipein(pipe)) +			field |= TRB_ISP; + +		/* Set the TRB length, TD size, and interrupter fields. */ +		if (HC_VERSION(xhci_readl(&ctrl->hccr->cr_capbase)) < 0x100) +			remainder = xhci_td_remainder(length - running_total); +		else +			remainder = xhci_v1_0_td_remainder(running_total, +							   trb_buff_len, +							   total_packet_count, +							   maxpacketsize, +							   num_trbs - 1); + +		length_field = ((trb_buff_len & TRB_LEN_MASK) | +				remainder | +				((0 & TRB_INTR_TARGET_MASK) << +				TRB_INTR_TARGET_SHIFT)); + +		trb_fields[0] = lower_32_bits(addr); +		trb_fields[1] = upper_32_bits(addr); +		trb_fields[2] = length_field; +		trb_fields[3] = field | (TRB_NORMAL << TRB_TYPE_SHIFT); + +		queue_trb(ctrl, ring, (num_trbs > 1), trb_fields); + +		--num_trbs; + +		running_total += trb_buff_len; + +		/* Calculate length for next transfer */ +		addr += trb_buff_len; +		trb_buff_len = min((length - running_total), TRB_MAX_BUFF_SIZE); +	} while (running_total < length); + +	giveback_first_trb(udev, ep_index, start_cycle, start_trb); + +	event = xhci_wait_for_event(ctrl, TRB_TRANSFER); +	if (!event) { +		debug("XHCI bulk transfer timed out, aborting...\n"); +		abort_td(udev, ep_index); +		udev->status = USB_ST_NAK_REC;  /* closest thing to a timeout */ +		udev->act_len = 0; +		return -ETIMEDOUT; +	} +	field = le32_to_cpu(event->trans_event.flags); + +	BUG_ON(TRB_TO_SLOT_ID(field) != slot_id); +	BUG_ON(TRB_TO_EP_INDEX(field) != ep_index); +	BUG_ON(*(void **)(uintptr_t)le64_to_cpu(event->trans_event.buffer) - +		buffer > (size_t)length); + +	record_transfer_result(udev, event, length); +	xhci_acknowledge_event(ctrl); +	xhci_inval_cache((uint32_t)buffer, length); + +	return (udev->status != USB_ST_NOT_PROC) ? 0 : -1; +} + +/** + * Queues up the Control Transfer Request + * + * @param udev	pointer to the USB device structure + * @param pipe		contains the DIR_IN or OUT , devnum + * @param req		request type + * @param length	length of the buffer + * @param buffer	buffer to be read/written based on the request + * @return returns 0 if successful else error code on failure + */ +int xhci_ctrl_tx(struct usb_device *udev, unsigned long pipe, +			struct devrequest *req,	int length, +			void *buffer) +{ +	int ret; +	int start_cycle; +	int num_trbs; +	u32 field; +	u32 length_field; +	u64 buf_64 = 0; +	struct xhci_generic_trb *start_trb; +	struct xhci_ctrl *ctrl = udev->controller; +	int slot_id = udev->slot_id; +	int ep_index; +	u32 trb_fields[4]; +	struct xhci_virt_device *virt_dev = ctrl->devs[slot_id]; +	struct xhci_ring *ep_ring; +	union xhci_trb *event; + +	debug("req=%u (%#x), type=%u (%#x), value=%u (%#x), index=%u\n", +		req->request, req->request, +		req->requesttype, req->requesttype, +		le16_to_cpu(req->value), le16_to_cpu(req->value), +		le16_to_cpu(req->index)); + +	ep_index = usb_pipe_ep_index(pipe); + +	ep_ring = virt_dev->eps[ep_index].ring; + +	/* +	 * Check to see if the max packet size for the default control +	 * endpoint changed during FS device enumeration +	 */ +	if (udev->speed == USB_SPEED_FULL) { +		ret = xhci_check_maxpacket(udev); +		if (ret < 0) +			return ret; +	} + +	xhci_inval_cache((uint32_t)virt_dev->out_ctx->bytes, +				virt_dev->out_ctx->size); + +	struct xhci_ep_ctx *ep_ctx = NULL; +	ep_ctx = xhci_get_ep_ctx(ctrl, virt_dev->out_ctx, ep_index); + +	/* 1 TRB for setup, 1 for status */ +	num_trbs = 2; +	/* +	 * Don't need to check if we need additional event data and normal TRBs, +	 * since data in control transfers will never get bigger than 16MB +	 * XXX: can we get a buffer that crosses 64KB boundaries? +	 */ + +	if (length > 0) +		num_trbs++; +	/* +	 * XXX: Calling routine prepare_ring() called in place of +	 * prepare_trasfer() as there in 'Linux' since we are not +	 * maintaining multiple TDs/transfer at the same time. +	 */ +	ret = prepare_ring(ctrl, ep_ring, +				le32_to_cpu(ep_ctx->ep_info) & EP_STATE_MASK); + +	if (ret < 0) +		return ret; + +	/* +	 * Don't give the first TRB to the hardware (by toggling the cycle bit) +	 * until we've finished creating all the other TRBs.  The ring's cycle +	 * state may change as we enqueue the other TRBs, so save it too. +	 */ +	start_trb = &ep_ring->enqueue->generic; +	start_cycle = ep_ring->cycle_state; + +	debug("start_trb %p, start_cycle %d\n", start_trb, start_cycle); + +	/* Queue setup TRB - see section 6.4.1.2.1 */ +	/* FIXME better way to translate setup_packet into two u32 fields? */ +	field = 0; +	field |= TRB_IDT | (TRB_SETUP << TRB_TYPE_SHIFT); +	if (start_cycle == 0) +		field |= 0x1; + +	/* xHCI 1.0 6.4.1.2.1: Transfer Type field */ +	if (HC_VERSION(xhci_readl(&ctrl->hccr->cr_capbase)) == 0x100) { +		if (length > 0) { +			if (req->requesttype & USB_DIR_IN) +				field |= (TRB_DATA_IN << TRB_TX_TYPE_SHIFT); +			else +				field |= (TRB_DATA_OUT << TRB_TX_TYPE_SHIFT); +		} +	} + +	debug("req->requesttype = %d, req->request = %d," +		"le16_to_cpu(req->value) = %d," +		"le16_to_cpu(req->index) = %d," +		"le16_to_cpu(req->length) = %d\n", +		req->requesttype, req->request, le16_to_cpu(req->value), +		le16_to_cpu(req->index), le16_to_cpu(req->length)); + +	trb_fields[0] = req->requesttype | req->request << 8 | +				le16_to_cpu(req->value) << 16; +	trb_fields[1] = le16_to_cpu(req->index) | +			le16_to_cpu(req->length) << 16; +	/* TRB_LEN | (TRB_INTR_TARGET) */ +	trb_fields[2] = (8 | ((0 & TRB_INTR_TARGET_MASK) << +			TRB_INTR_TARGET_SHIFT)); +	/* Immediate data in pointer */ +	trb_fields[3] = field; +	queue_trb(ctrl, ep_ring, true, trb_fields); + +	/* Re-initializing field to zero */ +	field = 0; +	/* If there's data, queue data TRBs */ +	/* Only set interrupt on short packet for IN endpoints */ +	if (usb_pipein(pipe)) +		field = TRB_ISP | (TRB_DATA << TRB_TYPE_SHIFT); +	else +		field = (TRB_DATA << TRB_TYPE_SHIFT); + +	length_field = (length & TRB_LEN_MASK) | xhci_td_remainder(length) | +			((0 & TRB_INTR_TARGET_MASK) << TRB_INTR_TARGET_SHIFT); +	debug("length_field = %d, length = %d," +		"xhci_td_remainder(length) = %d , TRB_INTR_TARGET(0) = %d\n", +		length_field, (length & TRB_LEN_MASK), +		xhci_td_remainder(length), 0); + +	if (length > 0) { +		if (req->requesttype & USB_DIR_IN) +			field |= TRB_DIR_IN; +		buf_64 = (uintptr_t)buffer; + +		trb_fields[0] = lower_32_bits(buf_64); +		trb_fields[1] = upper_32_bits(buf_64); +		trb_fields[2] = length_field; +		trb_fields[3] = field | ep_ring->cycle_state; + +		xhci_flush_cache((uint32_t)buffer, length); +		queue_trb(ctrl, ep_ring, true, trb_fields); +	} + +	/* +	 * Queue status TRB - +	 * see Table 7 and sections 4.11.2.2 and 6.4.1.2.3 +	 */ + +	/* If the device sent data, the status stage is an OUT transfer */ +	field = 0; +	if (length > 0 && req->requesttype & USB_DIR_IN) +		field = 0; +	else +		field = TRB_DIR_IN; + +	trb_fields[0] = 0; +	trb_fields[1] = 0; +	trb_fields[2] = ((0 & TRB_INTR_TARGET_MASK) << TRB_INTR_TARGET_SHIFT); +		/* Event on completion */ +	trb_fields[3] = field | TRB_IOC | +			(TRB_STATUS << TRB_TYPE_SHIFT) | +			ep_ring->cycle_state; + +	queue_trb(ctrl, ep_ring, false, trb_fields); + +	giveback_first_trb(udev, ep_index, start_cycle, start_trb); + +	event = xhci_wait_for_event(ctrl, TRB_TRANSFER); +	if (!event) +		goto abort; +	field = le32_to_cpu(event->trans_event.flags); + +	BUG_ON(TRB_TO_SLOT_ID(field) != slot_id); +	BUG_ON(TRB_TO_EP_INDEX(field) != ep_index); + +	record_transfer_result(udev, event, length); +	xhci_acknowledge_event(ctrl); + +	/* Invalidate buffer to make it available to usb-core */ +	if (length > 0) +		xhci_inval_cache((uint32_t)buffer, length); + +	if (GET_COMP_CODE(le32_to_cpu(event->trans_event.transfer_len)) +			== COMP_SHORT_TX) { +		/* Short data stage, clear up additional status stage event */ +		event = xhci_wait_for_event(ctrl, TRB_TRANSFER); +		if (!event) +			goto abort; +		BUG_ON(TRB_TO_SLOT_ID(field) != slot_id); +		BUG_ON(TRB_TO_EP_INDEX(field) != ep_index); +		xhci_acknowledge_event(ctrl); +	} + +	return (udev->status != USB_ST_NOT_PROC) ? 0 : -1; + +abort: +	debug("XHCI control transfer timed out, aborting...\n"); +	abort_td(udev, ep_index); +	udev->status = USB_ST_NAK_REC; +	udev->act_len = 0; +	return -ETIMEDOUT; +} diff --git a/roms/u-boot/drivers/usb/host/xhci.c b/roms/u-boot/drivers/usb/host/xhci.c new file mode 100644 index 00000000..d1c2e5c4 --- /dev/null +++ b/roms/u-boot/drivers/usb/host/xhci.c @@ -0,0 +1,1030 @@ +/* + * USB HOST XHCI Controller stack + * + * Based on xHCI host controller driver in linux-kernel + * by Sarah Sharp. + * + * Copyright (C) 2008 Intel Corp. + * Author: Sarah Sharp + * + * Copyright (C) 2013 Samsung Electronics Co.Ltd + * Authors: Vivek Gautam <gautam.vivek@samsung.com> + *	    Vikas Sajjan <vikas.sajjan@samsung.com> + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +/** + * This file gives the xhci stack for usb3.0 looking into + * xhci specification Rev1.0 (5/21/10). + * The quirk devices support hasn't been given yet. + */ + +#include <common.h> +#include <asm/byteorder.h> +#include <usb.h> +#include <malloc.h> +#include <watchdog.h> +#include <asm/cache.h> +#include <asm/unaligned.h> +#include <asm-generic/errno.h> +#include "xhci.h" + +#ifndef CONFIG_USB_MAX_CONTROLLER_COUNT +#define CONFIG_USB_MAX_CONTROLLER_COUNT 1 +#endif + +static struct descriptor { +	struct usb_hub_descriptor hub; +	struct usb_device_descriptor device; +	struct usb_config_descriptor config; +	struct usb_interface_descriptor interface; +	struct usb_endpoint_descriptor endpoint; +	struct usb_ss_ep_comp_descriptor ep_companion; +} __attribute__ ((packed)) descriptor = { +	{ +		0xc,		/* bDescLength */ +		0x2a,		/* bDescriptorType: hub descriptor */ +		2,		/* bNrPorts -- runtime modified */ +		cpu_to_le16(0x8), /* wHubCharacteristics */ +		10,		/* bPwrOn2PwrGood */ +		0,		/* bHubCntrCurrent */ +		{},		/* Device removable */ +		{}		/* at most 7 ports! XXX */ +	}, +	{ +		0x12,		/* bLength */ +		1,		/* bDescriptorType: UDESC_DEVICE */ +		cpu_to_le16(0x0300), /* bcdUSB: v3.0 */ +		9,		/* bDeviceClass: UDCLASS_HUB */ +		0,		/* bDeviceSubClass: UDSUBCLASS_HUB */ +		3,		/* bDeviceProtocol: UDPROTO_SSHUBSTT */ +		9,		/* bMaxPacketSize: 512 bytes  2^9 */ +		0x0000,		/* idVendor */ +		0x0000,		/* idProduct */ +		cpu_to_le16(0x0100), /* bcdDevice */ +		1,		/* iManufacturer */ +		2,		/* iProduct */ +		0,		/* iSerialNumber */ +		1		/* bNumConfigurations: 1 */ +	}, +	{ +		0x9, +		2,		/* bDescriptorType: UDESC_CONFIG */ +		cpu_to_le16(0x1f), /* includes SS endpoint descriptor */ +		1,		/* bNumInterface */ +		1,		/* bConfigurationValue */ +		0,		/* iConfiguration */ +		0x40,		/* bmAttributes: UC_SELF_POWER */ +		0		/* bMaxPower */ +	}, +	{ +		0x9,		/* bLength */ +		4,		/* bDescriptorType: UDESC_INTERFACE */ +		0,		/* bInterfaceNumber */ +		0,		/* bAlternateSetting */ +		1,		/* bNumEndpoints */ +		9,		/* bInterfaceClass: UICLASS_HUB */ +		0,		/* bInterfaceSubClass: UISUBCLASS_HUB */ +		0,		/* bInterfaceProtocol: UIPROTO_HSHUBSTT */ +		0		/* iInterface */ +	}, +	{ +		0x7,		/* bLength */ +		5,		/* bDescriptorType: UDESC_ENDPOINT */ +		0x81,		/* bEndpointAddress: IN endpoint 1 */ +		3,		/* bmAttributes: UE_INTERRUPT */ +		8,		/* wMaxPacketSize */ +		255		/* bInterval */ +	}, +	{ +		0x06,		/* ss_bLength */ +		0x30,		/* ss_bDescriptorType: SS EP Companion */ +		0x00,		/* ss_bMaxBurst: allows 1 TX between ACKs */ +		/* ss_bmAttributes: 1 packet per service interval */ +		0x00, +		/* ss_wBytesPerInterval: 15 bits for max 15 ports */ +		cpu_to_le16(0x02), +	}, +}; + +static struct xhci_ctrl xhcic[CONFIG_USB_MAX_CONTROLLER_COUNT]; + +/** + * Waits for as per specified amount of time + * for the "result" to match with "done" + * + * @param ptr	pointer to the register to be read + * @param mask	mask for the value read + * @param done	value to be campared with result + * @param usec	time to wait till + * @return 0 if handshake is success else < 0 on failure + */ +static int handshake(uint32_t volatile *ptr, uint32_t mask, +					uint32_t done, int usec) +{ +	uint32_t result; + +	do { +		result = xhci_readl(ptr); +		if (result == ~(uint32_t)0) +			return -ENODEV; +		result &= mask; +		if (result == done) +			return 0; +		usec--; +		udelay(1); +	} while (usec > 0); + +	return -ETIMEDOUT; +} + +/** + * Set the run bit and wait for the host to be running. + * + * @param hcor	pointer to host controller operation registers + * @return status of the Handshake + */ +static int xhci_start(struct xhci_hcor *hcor) +{ +	u32 temp; +	int ret; + +	puts("Starting the controller\n"); +	temp = xhci_readl(&hcor->or_usbcmd); +	temp |= (CMD_RUN); +	xhci_writel(&hcor->or_usbcmd, temp); + +	/* +	 * Wait for the HCHalted Status bit to be 0 to indicate the host is +	 * running. +	 */ +	ret = handshake(&hcor->or_usbsts, STS_HALT, 0, XHCI_MAX_HALT_USEC); +	if (ret) +		debug("Host took too long to start, " +				"waited %u microseconds.\n", +				XHCI_MAX_HALT_USEC); +	return ret; +} + +/** + * Resets the XHCI Controller + * + * @param hcor	pointer to host controller operation registers + * @return -EBUSY if XHCI Controller is not halted else status of handshake + */ +int xhci_reset(struct xhci_hcor *hcor) +{ +	u32 cmd; +	u32 state; +	int ret; + +	/* Halting the Host first */ +	debug("// Halt the HC\n"); +	state = xhci_readl(&hcor->or_usbsts) & STS_HALT; +	if (!state) { +		cmd = xhci_readl(&hcor->or_usbcmd); +		cmd &= ~CMD_RUN; +		xhci_writel(&hcor->or_usbcmd, cmd); +	} + +	ret = handshake(&hcor->or_usbsts, +			STS_HALT, STS_HALT, XHCI_MAX_HALT_USEC); +	if (ret) { +		printf("Host not halted after %u microseconds.\n", +				XHCI_MAX_HALT_USEC); +		return -EBUSY; +	} + +	debug("// Reset the HC\n"); +	cmd = xhci_readl(&hcor->or_usbcmd); +	cmd |= CMD_RESET; +	xhci_writel(&hcor->or_usbcmd, cmd); + +	ret = handshake(&hcor->or_usbcmd, CMD_RESET, 0, XHCI_MAX_RESET_USEC); +	if (ret) +		return ret; + +	/* +	 * xHCI cannot write to any doorbells or operational registers other +	 * than status until the "Controller Not Ready" flag is cleared. +	 */ +	return handshake(&hcor->or_usbsts, STS_CNR, 0, XHCI_MAX_RESET_USEC); +} + +/** + * Used for passing endpoint bitmasks between the core and HCDs. + * Find the index for an endpoint given its descriptor. + * Use the return value to right shift 1 for the bitmask. + * + * Index  = (epnum * 2) + direction - 1, + * where direction = 0 for OUT, 1 for IN. + * For control endpoints, the IN index is used (OUT index is unused), so + * index = (epnum * 2) + direction - 1 = (epnum * 2) + 1 - 1 = (epnum * 2) + * + * @param desc	USB enpdoint Descriptor + * @return index of the Endpoint + */ +static unsigned int xhci_get_ep_index(struct usb_endpoint_descriptor *desc) +{ +	unsigned int index; + +	if (usb_endpoint_xfer_control(desc)) +		index = (unsigned int)(usb_endpoint_num(desc) * 2); +	else +		index = (unsigned int)((usb_endpoint_num(desc) * 2) - +				(usb_endpoint_dir_in(desc) ? 0 : 1)); + +	return index; +} + +/** + * Issue a configure endpoint command or evaluate context command + * and wait for it to finish. + * + * @param udev	pointer to the Device Data Structure + * @param ctx_change	flag to indicate the Context has changed or NOT + * @return 0 on success, -1 on failure + */ +static int xhci_configure_endpoints(struct usb_device *udev, bool ctx_change) +{ +	struct xhci_container_ctx *in_ctx; +	struct xhci_virt_device *virt_dev; +	struct xhci_ctrl *ctrl = udev->controller; +	union xhci_trb *event; + +	virt_dev = ctrl->devs[udev->slot_id]; +	in_ctx = virt_dev->in_ctx; + +	xhci_flush_cache((uint32_t)in_ctx->bytes, in_ctx->size); +	xhci_queue_command(ctrl, in_ctx->bytes, udev->slot_id, 0, +			   ctx_change ? TRB_EVAL_CONTEXT : TRB_CONFIG_EP); +	event = xhci_wait_for_event(ctrl, TRB_COMPLETION); +	BUG_ON(TRB_TO_SLOT_ID(le32_to_cpu(event->event_cmd.flags)) +		!= udev->slot_id); + +	switch (GET_COMP_CODE(le32_to_cpu(event->event_cmd.status))) { +	case COMP_SUCCESS: +		debug("Successful %s command\n", +			ctx_change ? "Evaluate Context" : "Configure Endpoint"); +		break; +	default: +		printf("ERROR: %s command returned completion code %d.\n", +			ctx_change ? "Evaluate Context" : "Configure Endpoint", +			GET_COMP_CODE(le32_to_cpu(event->event_cmd.status))); +		return -EINVAL; +	} + +	xhci_acknowledge_event(ctrl); + +	return 0; +} + +/** + * Configure the endpoint, programming the device contexts. + * + * @param udev	pointer to the USB device structure + * @return returns the status of the xhci_configure_endpoints + */ +static int xhci_set_configuration(struct usb_device *udev) +{ +	struct xhci_container_ctx *in_ctx; +	struct xhci_container_ctx *out_ctx; +	struct xhci_input_control_ctx *ctrl_ctx; +	struct xhci_slot_ctx *slot_ctx; +	struct xhci_ep_ctx *ep_ctx[MAX_EP_CTX_NUM]; +	int cur_ep; +	int max_ep_flag = 0; +	int ep_index; +	unsigned int dir; +	unsigned int ep_type; +	struct xhci_ctrl *ctrl = udev->controller; +	int num_of_ep; +	int ep_flag = 0; +	u64 trb_64 = 0; +	int slot_id = udev->slot_id; +	struct xhci_virt_device *virt_dev = ctrl->devs[slot_id]; +	struct usb_interface *ifdesc; + +	out_ctx = virt_dev->out_ctx; +	in_ctx = virt_dev->in_ctx; + +	num_of_ep = udev->config.if_desc[0].no_of_ep; +	ifdesc = &udev->config.if_desc[0]; + +	ctrl_ctx = xhci_get_input_control_ctx(in_ctx); +	/* Zero the input context control */ +	ctrl_ctx->add_flags = 0; +	ctrl_ctx->drop_flags = 0; + +	/* EP_FLAG gives values 1 & 4 for EP1OUT and EP2IN */ +	for (cur_ep = 0; cur_ep < num_of_ep; cur_ep++) { +		ep_flag = xhci_get_ep_index(&ifdesc->ep_desc[cur_ep]); +		ctrl_ctx->add_flags |= cpu_to_le32(1 << (ep_flag + 1)); +		if (max_ep_flag < ep_flag) +			max_ep_flag = ep_flag; +	} + +	xhci_inval_cache((uint32_t)out_ctx->bytes, out_ctx->size); + +	/* slot context */ +	xhci_slot_copy(ctrl, in_ctx, out_ctx); +	slot_ctx = xhci_get_slot_ctx(ctrl, in_ctx); +	slot_ctx->dev_info &= ~(LAST_CTX_MASK); +	slot_ctx->dev_info |= cpu_to_le32(LAST_CTX(max_ep_flag + 1) | 0); + +	xhci_endpoint_copy(ctrl, in_ctx, out_ctx, 0); + +	/* filling up ep contexts */ +	for (cur_ep = 0; cur_ep < num_of_ep; cur_ep++) { +		struct usb_endpoint_descriptor *endpt_desc = NULL; + +		endpt_desc = &ifdesc->ep_desc[cur_ep]; +		trb_64 = 0; + +		ep_index = xhci_get_ep_index(endpt_desc); +		ep_ctx[ep_index] = xhci_get_ep_ctx(ctrl, in_ctx, ep_index); + +		/* Allocate the ep rings */ +		virt_dev->eps[ep_index].ring = xhci_ring_alloc(1, true); +		if (!virt_dev->eps[ep_index].ring) +			return -ENOMEM; + +		/*NOTE: ep_desc[0] actually represents EP1 and so on */ +		dir = (((endpt_desc->bEndpointAddress) & (0x80)) >> 7); +		ep_type = (((endpt_desc->bmAttributes) & (0x3)) | (dir << 2)); +		ep_ctx[ep_index]->ep_info2 = +			cpu_to_le32(ep_type << EP_TYPE_SHIFT); +		ep_ctx[ep_index]->ep_info2 |= +			cpu_to_le32(MAX_PACKET +			(get_unaligned(&endpt_desc->wMaxPacketSize))); + +		ep_ctx[ep_index]->ep_info2 |= +			cpu_to_le32(((0 & MAX_BURST_MASK) << MAX_BURST_SHIFT) | +			((3 & ERROR_COUNT_MASK) << ERROR_COUNT_SHIFT)); + +		trb_64 = (uintptr_t) +				virt_dev->eps[ep_index].ring->enqueue; +		ep_ctx[ep_index]->deq = cpu_to_le64(trb_64 | +				virt_dev->eps[ep_index].ring->cycle_state); +	} + +	return xhci_configure_endpoints(udev, false); +} + +/** + * Issue an Address Device command (which will issue a SetAddress request to + * the device). + * + * @param udev pointer to the Device Data Structure + * @return 0 if successful else error code on failure + */ +static int xhci_address_device(struct usb_device *udev) +{ +	int ret = 0; +	struct xhci_ctrl *ctrl = udev->controller; +	struct xhci_slot_ctx *slot_ctx; +	struct xhci_input_control_ctx *ctrl_ctx; +	struct xhci_virt_device *virt_dev; +	int slot_id = udev->slot_id; +	union xhci_trb *event; + +	virt_dev = ctrl->devs[slot_id]; + +	/* +	 * This is the first Set Address since device plug-in +	 * so setting up the slot context. +	 */ +	debug("Setting up addressable devices\n"); +	xhci_setup_addressable_virt_dev(udev); + +	ctrl_ctx = xhci_get_input_control_ctx(virt_dev->in_ctx); +	ctrl_ctx->add_flags = cpu_to_le32(SLOT_FLAG | EP0_FLAG); +	ctrl_ctx->drop_flags = 0; + +	xhci_queue_command(ctrl, (void *)ctrl_ctx, slot_id, 0, TRB_ADDR_DEV); +	event = xhci_wait_for_event(ctrl, TRB_COMPLETION); +	BUG_ON(TRB_TO_SLOT_ID(le32_to_cpu(event->event_cmd.flags)) != slot_id); + +	switch (GET_COMP_CODE(le32_to_cpu(event->event_cmd.status))) { +	case COMP_CTX_STATE: +	case COMP_EBADSLT: +		printf("Setup ERROR: address device command for slot %d.\n", +								slot_id); +		ret = -EINVAL; +		break; +	case COMP_TX_ERR: +		puts("Device not responding to set address.\n"); +		ret = -EPROTO; +		break; +	case COMP_DEV_ERR: +		puts("ERROR: Incompatible device" +					"for address device command.\n"); +		ret = -ENODEV; +		break; +	case COMP_SUCCESS: +		debug("Successful Address Device command\n"); +		udev->status = 0; +		break; +	default: +		printf("ERROR: unexpected command completion code 0x%x.\n", +			GET_COMP_CODE(le32_to_cpu(event->event_cmd.status))); +		ret = -EINVAL; +		break; +	} + +	xhci_acknowledge_event(ctrl); + +	if (ret < 0) +		/* +		 * TODO: Unsuccessful Address Device command shall leave the +		 * slot in default state. So, issue Disable Slot command now. +		 */ +		return ret; + +	xhci_inval_cache((uint32_t)virt_dev->out_ctx->bytes, +				virt_dev->out_ctx->size); +	slot_ctx = xhci_get_slot_ctx(ctrl, virt_dev->out_ctx); + +	debug("xHC internal address is: %d\n", +		le32_to_cpu(slot_ctx->dev_state) & DEV_ADDR_MASK); + +	return 0; +} + +/** + * Issue Enable slot command to the controller to allocate + * device slot and assign the slot id. It fails if the xHC + * ran out of device slots, the Enable Slot command timed out, + * or allocating memory failed. + * + * @param udev	pointer to the Device Data Structure + * @return Returns 0 on succes else return error code on failure + */ +int usb_alloc_device(struct usb_device *udev) +{ +	union xhci_trb *event; +	struct xhci_ctrl *ctrl = udev->controller; +	int ret; + +	/* +	 * Root hub will be first device to be initailized. +	 * If this device is root-hub, don't do any xHC related +	 * stuff. +	 */ +	if (ctrl->rootdev == 0) { +		udev->speed = USB_SPEED_SUPER; +		return 0; +	} + +	xhci_queue_command(ctrl, NULL, 0, 0, TRB_ENABLE_SLOT); +	event = xhci_wait_for_event(ctrl, TRB_COMPLETION); +	BUG_ON(GET_COMP_CODE(le32_to_cpu(event->event_cmd.status)) +		!= COMP_SUCCESS); + +	udev->slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->event_cmd.flags)); + +	xhci_acknowledge_event(ctrl); + +	ret = xhci_alloc_virt_device(udev); +	if (ret < 0) { +		/* +		 * TODO: Unsuccessful Address Device command shall leave +		 * the slot in default. So, issue Disable Slot command now. +		 */ +		puts("Could not allocate xHCI USB device data structures\n"); +		return ret; +	} + +	return 0; +} + +/* + * Full speed devices may have a max packet size greater than 8 bytes, but the + * USB core doesn't know that until it reads the first 8 bytes of the + * descriptor.  If the usb_device's max packet size changes after that point, + * we need to issue an evaluate context command and wait on it. + * + * @param udev	pointer to the Device Data Structure + * @return returns the status of the xhci_configure_endpoints + */ +int xhci_check_maxpacket(struct usb_device *udev) +{ +	struct xhci_ctrl *ctrl = udev->controller; +	unsigned int slot_id = udev->slot_id; +	int ep_index = 0;	/* control endpoint */ +	struct xhci_container_ctx *in_ctx; +	struct xhci_container_ctx *out_ctx; +	struct xhci_input_control_ctx *ctrl_ctx; +	struct xhci_ep_ctx *ep_ctx; +	int max_packet_size; +	int hw_max_packet_size; +	int ret = 0; +	struct usb_interface *ifdesc; + +	ifdesc = &udev->config.if_desc[0]; + +	out_ctx = ctrl->devs[slot_id]->out_ctx; +	xhci_inval_cache((uint32_t)out_ctx->bytes, out_ctx->size); + +	ep_ctx = xhci_get_ep_ctx(ctrl, out_ctx, ep_index); +	hw_max_packet_size = MAX_PACKET_DECODED(le32_to_cpu(ep_ctx->ep_info2)); +	max_packet_size = usb_endpoint_maxp(&ifdesc->ep_desc[0]); +	if (hw_max_packet_size != max_packet_size) { +		debug("Max Packet Size for ep 0 changed.\n"); +		debug("Max packet size in usb_device = %d\n", max_packet_size); +		debug("Max packet size in xHCI HW = %d\n", hw_max_packet_size); +		debug("Issuing evaluate context command.\n"); + +		/* Set up the modified control endpoint 0 */ +		xhci_endpoint_copy(ctrl, ctrl->devs[slot_id]->in_ctx, +				ctrl->devs[slot_id]->out_ctx, ep_index); +		in_ctx = ctrl->devs[slot_id]->in_ctx; +		ep_ctx = xhci_get_ep_ctx(ctrl, in_ctx, ep_index); +		ep_ctx->ep_info2 &= cpu_to_le32(~MAX_PACKET_MASK); +		ep_ctx->ep_info2 |= cpu_to_le32(MAX_PACKET(max_packet_size)); + +		/* +		 * Set up the input context flags for the command +		 * FIXME: This won't work if a non-default control endpoint +		 * changes max packet sizes. +		 */ +		ctrl_ctx = xhci_get_input_control_ctx(in_ctx); +		ctrl_ctx->add_flags = cpu_to_le32(EP0_FLAG); +		ctrl_ctx->drop_flags = 0; + +		ret = xhci_configure_endpoints(udev, true); +	} +	return ret; +} + +/** + * Clears the Change bits of the Port Status Register + * + * @param wValue	request value + * @param wIndex	request index + * @param addr		address of posrt status register + * @param port_status	state of port status register + * @return none + */ +static void xhci_clear_port_change_bit(u16 wValue, +		u16 wIndex, volatile uint32_t *addr, u32 port_status) +{ +	char *port_change_bit; +	u32 status; + +	switch (wValue) { +	case USB_PORT_FEAT_C_RESET: +		status = PORT_RC; +		port_change_bit = "reset"; +		break; +	case USB_PORT_FEAT_C_CONNECTION: +		status = PORT_CSC; +		port_change_bit = "connect"; +		break; +	case USB_PORT_FEAT_C_OVER_CURRENT: +		status = PORT_OCC; +		port_change_bit = "over-current"; +		break; +	case USB_PORT_FEAT_C_ENABLE: +		status = PORT_PEC; +		port_change_bit = "enable/disable"; +		break; +	case USB_PORT_FEAT_C_SUSPEND: +		status = PORT_PLC; +		port_change_bit = "suspend/resume"; +		break; +	default: +		/* Should never happen */ +		return; +	} + +	/* Change bits are all write 1 to clear */ +	xhci_writel(addr, port_status | status); + +	port_status = xhci_readl(addr); +	debug("clear port %s change, actual port %d status  = 0x%x\n", +			port_change_bit, wIndex, port_status); +} + +/** + * Save Read Only (RO) bits and save read/write bits where + * writing a 0 clears the bit and writing a 1 sets the bit (RWS). + * For all other types (RW1S, RW1CS, RW, and RZ), writing a '0' has no effect. + * + * @param state	state of the Port Status and Control Regsiter + * @return a value that would result in the port being in the + *	   same state, if the value was written to the port + *	   status control register. + */ +static u32 xhci_port_state_to_neutral(u32 state) +{ +	/* Save read-only status and port state */ +	return (state & XHCI_PORT_RO) | (state & XHCI_PORT_RWS); +} + +/** + * Submits the Requests to the XHCI Host Controller + * + * @param udev pointer to the USB device structure + * @param pipe contains the DIR_IN or OUT , devnum + * @param buffer buffer to be read/written based on the request + * @return returns 0 if successful else -1 on failure + */ +static int xhci_submit_root(struct usb_device *udev, unsigned long pipe, +			void *buffer, struct devrequest *req) +{ +	uint8_t tmpbuf[4]; +	u16 typeReq; +	void *srcptr = NULL; +	int len, srclen; +	uint32_t reg; +	volatile uint32_t *status_reg; +	struct xhci_ctrl *ctrl = udev->controller; +	struct xhci_hcor *hcor = ctrl->hcor; + +	if (((req->requesttype & USB_RT_PORT) && +	     le16_to_cpu(req->index)) > CONFIG_SYS_USB_XHCI_MAX_ROOT_PORTS) { +		printf("The request port(%d) is not configured\n", +			le16_to_cpu(req->index) - 1); +		return -EINVAL; +	} + +	status_reg = (volatile uint32_t *) +		     (&hcor->portregs[le16_to_cpu(req->index) - 1].or_portsc); +	srclen = 0; + +	typeReq = req->request | req->requesttype << 8; + +	switch (typeReq) { +	case DeviceRequest | USB_REQ_GET_DESCRIPTOR: +		switch (le16_to_cpu(req->value) >> 8) { +		case USB_DT_DEVICE: +			debug("USB_DT_DEVICE request\n"); +			srcptr = &descriptor.device; +			srclen = 0x12; +			break; +		case USB_DT_CONFIG: +			debug("USB_DT_CONFIG config\n"); +			srcptr = &descriptor.config; +			srclen = 0x19; +			break; +		case USB_DT_STRING: +			debug("USB_DT_STRING config\n"); +			switch (le16_to_cpu(req->value) & 0xff) { +			case 0:	/* Language */ +				srcptr = "\4\3\11\4"; +				srclen = 4; +				break; +			case 1:	/* Vendor String  */ +				srcptr = "\16\3u\0-\0b\0o\0o\0t\0"; +				srclen = 14; +				break; +			case 2:	/* Product Name */ +				srcptr = "\52\3X\0H\0C\0I\0 " +					 "\0H\0o\0s\0t\0 " +					 "\0C\0o\0n\0t\0r\0o\0l\0l\0e\0r\0"; +				srclen = 42; +				break; +			default: +				printf("unknown value DT_STRING %x\n", +					le16_to_cpu(req->value)); +				goto unknown; +			} +			break; +		default: +			printf("unknown value %x\n", le16_to_cpu(req->value)); +			goto unknown; +		} +		break; +	case USB_REQ_GET_DESCRIPTOR | ((USB_DIR_IN | USB_RT_HUB) << 8): +		switch (le16_to_cpu(req->value) >> 8) { +		case USB_DT_HUB: +			debug("USB_DT_HUB config\n"); +			srcptr = &descriptor.hub; +			srclen = 0x8; +			break; +		default: +			printf("unknown value %x\n", le16_to_cpu(req->value)); +			goto unknown; +		} +		break; +	case USB_REQ_SET_ADDRESS | (USB_RECIP_DEVICE << 8): +		debug("USB_REQ_SET_ADDRESS\n"); +		ctrl->rootdev = le16_to_cpu(req->value); +		break; +	case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: +		/* Do nothing */ +		break; +	case USB_REQ_GET_STATUS | ((USB_DIR_IN | USB_RT_HUB) << 8): +		tmpbuf[0] = 1;	/* USB_STATUS_SELFPOWERED */ +		tmpbuf[1] = 0; +		srcptr = tmpbuf; +		srclen = 2; +		break; +	case USB_REQ_GET_STATUS | ((USB_RT_PORT | USB_DIR_IN) << 8): +		memset(tmpbuf, 0, 4); +		reg = xhci_readl(status_reg); +		if (reg & PORT_CONNECT) { +			tmpbuf[0] |= USB_PORT_STAT_CONNECTION; +			switch (reg & DEV_SPEED_MASK) { +			case XDEV_FS: +				debug("SPEED = FULLSPEED\n"); +				break; +			case XDEV_LS: +				debug("SPEED = LOWSPEED\n"); +				tmpbuf[1] |= USB_PORT_STAT_LOW_SPEED >> 8; +				break; +			case XDEV_HS: +				debug("SPEED = HIGHSPEED\n"); +				tmpbuf[1] |= USB_PORT_STAT_HIGH_SPEED >> 8; +				break; +			case XDEV_SS: +				debug("SPEED = SUPERSPEED\n"); +				tmpbuf[1] |= USB_PORT_STAT_SUPER_SPEED >> 8; +				break; +			} +		} +		if (reg & PORT_PE) +			tmpbuf[0] |= USB_PORT_STAT_ENABLE; +		if ((reg & PORT_PLS_MASK) == XDEV_U3) +			tmpbuf[0] |= USB_PORT_STAT_SUSPEND; +		if (reg & PORT_OC) +			tmpbuf[0] |= USB_PORT_STAT_OVERCURRENT; +		if (reg & PORT_RESET) +			tmpbuf[0] |= USB_PORT_STAT_RESET; +		if (reg & PORT_POWER) +			/* +			 * XXX: This Port power bit (for USB 3.0 hub) +			 * we are faking in USB 2.0 hub port status; +			 * since there's a change in bit positions in +			 * two: +			 * USB 2.0 port status PP is at position[8] +			 * USB 3.0 port status PP is at position[9] +			 * So, we are still keeping it at position [8] +			 */ +			tmpbuf[1] |= USB_PORT_STAT_POWER >> 8; +		if (reg & PORT_CSC) +			tmpbuf[2] |= USB_PORT_STAT_C_CONNECTION; +		if (reg & PORT_PEC) +			tmpbuf[2] |= USB_PORT_STAT_C_ENABLE; +		if (reg & PORT_OCC) +			tmpbuf[2] |= USB_PORT_STAT_C_OVERCURRENT; +		if (reg & PORT_RC) +			tmpbuf[2] |= USB_PORT_STAT_C_RESET; + +		srcptr = tmpbuf; +		srclen = 4; +		break; +	case USB_REQ_SET_FEATURE | ((USB_DIR_OUT | USB_RT_PORT) << 8): +		reg = xhci_readl(status_reg); +		reg = xhci_port_state_to_neutral(reg); +		switch (le16_to_cpu(req->value)) { +		case USB_PORT_FEAT_ENABLE: +			reg |= PORT_PE; +			xhci_writel(status_reg, reg); +			break; +		case USB_PORT_FEAT_POWER: +			reg |= PORT_POWER; +			xhci_writel(status_reg, reg); +			break; +		case USB_PORT_FEAT_RESET: +			reg |= PORT_RESET; +			xhci_writel(status_reg, reg); +			break; +		default: +			printf("unknown feature %x\n", le16_to_cpu(req->value)); +			goto unknown; +		} +		break; +	case USB_REQ_CLEAR_FEATURE | ((USB_DIR_OUT | USB_RT_PORT) << 8): +		reg = xhci_readl(status_reg); +		reg = xhci_port_state_to_neutral(reg); +		switch (le16_to_cpu(req->value)) { +		case USB_PORT_FEAT_ENABLE: +			reg &= ~PORT_PE; +			break; +		case USB_PORT_FEAT_POWER: +			reg &= ~PORT_POWER; +			break; +		case USB_PORT_FEAT_C_RESET: +		case USB_PORT_FEAT_C_CONNECTION: +		case USB_PORT_FEAT_C_OVER_CURRENT: +		case USB_PORT_FEAT_C_ENABLE: +			xhci_clear_port_change_bit((le16_to_cpu(req->value)), +							le16_to_cpu(req->index), +							status_reg, reg); +			break; +		default: +			printf("unknown feature %x\n", le16_to_cpu(req->value)); +			goto unknown; +		} +		xhci_writel(status_reg, reg); +		break; +	default: +		puts("Unknown request\n"); +		goto unknown; +	} + +	debug("scrlen = %d\n req->length = %d\n", +		srclen, le16_to_cpu(req->length)); + +	len = min(srclen, le16_to_cpu(req->length)); + +	if (srcptr != NULL && len > 0) +		memcpy(buffer, srcptr, len); +	else +		debug("Len is 0\n"); + +	udev->act_len = len; +	udev->status = 0; + +	return 0; + +unknown: +	udev->act_len = 0; +	udev->status = USB_ST_STALLED; + +	return -ENODEV; +} + +/** + * Submits the INT request to XHCI Host cotroller + * + * @param udev	pointer to the USB device + * @param pipe		contains the DIR_IN or OUT , devnum + * @param buffer	buffer to be read/written based on the request + * @param length	length of the buffer + * @param interval	interval of the interrupt + * @return 0 + */ +int +submit_int_msg(struct usb_device *udev, unsigned long pipe, void *buffer, +						int length, int interval) +{ +	/* +	 * TODO: Not addressing any interrupt type transfer requests +	 * Add support for it later. +	 */ +	return -EINVAL; +} + +/** + * submit the BULK type of request to the USB Device + * + * @param udev	pointer to the USB device + * @param pipe		contains the DIR_IN or OUT , devnum + * @param buffer	buffer to be read/written based on the request + * @param length	length of the buffer + * @return returns 0 if successful else -1 on failure + */ +int +submit_bulk_msg(struct usb_device *udev, unsigned long pipe, void *buffer, +								int length) +{ +	if (usb_pipetype(pipe) != PIPE_BULK) { +		printf("non-bulk pipe (type=%lu)", usb_pipetype(pipe)); +		return -EINVAL; +	} + +	return xhci_bulk_tx(udev, pipe, length, buffer); +} + +/** + * submit the control type of request to the Root hub/Device based on the devnum + * + * @param udev	pointer to the USB device + * @param pipe		contains the DIR_IN or OUT , devnum + * @param buffer	buffer to be read/written based on the request + * @param length	length of the buffer + * @param setup		Request type + * @return returns 0 if successful else -1 on failure + */ +int +submit_control_msg(struct usb_device *udev, unsigned long pipe, void *buffer, +					int length, struct devrequest *setup) +{ +	struct xhci_ctrl *ctrl = udev->controller; +	int ret = 0; + +	if (usb_pipetype(pipe) != PIPE_CONTROL) { +		printf("non-control pipe (type=%lu)", usb_pipetype(pipe)); +		return -EINVAL; +	} + +	if (usb_pipedevice(pipe) == ctrl->rootdev) +		return xhci_submit_root(udev, pipe, buffer, setup); + +	if (setup->request == USB_REQ_SET_ADDRESS) +		return xhci_address_device(udev); + +	if (setup->request == USB_REQ_SET_CONFIGURATION) { +		ret = xhci_set_configuration(udev); +		if (ret) { +			puts("Failed to configure xHCI endpoint\n"); +			return ret; +		} +	} + +	return xhci_ctrl_tx(udev, pipe, setup, length, buffer); +} + +/** + * Intialises the XHCI host controller + * and allocates the necessary data structures + * + * @param index	index to the host controller data structure + * @return pointer to the intialised controller + */ +int usb_lowlevel_init(int index, enum usb_init_type init, void **controller) +{ +	uint32_t val; +	uint32_t val2; +	uint32_t reg; +	struct xhci_hccr *hccr; +	struct xhci_hcor *hcor; +	struct xhci_ctrl *ctrl; + +	if (xhci_hcd_init(index, &hccr, (struct xhci_hcor **)&hcor) != 0) +		return -ENODEV; + +	if (xhci_reset(hcor) != 0) +		return -ENODEV; + +	ctrl = &xhcic[index]; + +	ctrl->hccr = hccr; +	ctrl->hcor = hcor; + +	/* +	 * Program the Number of Device Slots Enabled field in the CONFIG +	 * register with the max value of slots the HC can handle. +	 */ +	val = (xhci_readl(&hccr->cr_hcsparams1) & HCS_SLOTS_MASK); +	val2 = xhci_readl(&hcor->or_config); +	val |= (val2 & ~HCS_SLOTS_MASK); +	xhci_writel(&hcor->or_config, val); + +	/* initializing xhci data structures */ +	if (xhci_mem_init(ctrl, hccr, hcor) < 0) +		return -ENOMEM; + +	reg = xhci_readl(&hccr->cr_hcsparams1); +	descriptor.hub.bNbrPorts = ((reg & HCS_MAX_PORTS_MASK) >> +						HCS_MAX_PORTS_SHIFT); +	printf("Register %x NbrPorts %d\n", reg, descriptor.hub.bNbrPorts); + +	/* Port Indicators */ +	reg = xhci_readl(&hccr->cr_hccparams); +	if (HCS_INDICATOR(reg)) +		put_unaligned(get_unaligned(&descriptor.hub.wHubCharacteristics) +				| 0x80, &descriptor.hub.wHubCharacteristics); + +	/* Port Power Control */ +	if (HCC_PPC(reg)) +		put_unaligned(get_unaligned(&descriptor.hub.wHubCharacteristics) +				| 0x01, &descriptor.hub.wHubCharacteristics); + +	if (xhci_start(hcor)) { +		xhci_reset(hcor); +		return -ENODEV; +	} + +	/* Zero'ing IRQ control register and IRQ pending register */ +	xhci_writel(&ctrl->ir_set->irq_control, 0x0); +	xhci_writel(&ctrl->ir_set->irq_pending, 0x0); + +	reg = HC_VERSION(xhci_readl(&hccr->cr_capbase)); +	printf("USB XHCI %x.%02x\n", reg >> 8, reg & 0xff); + +	*controller = &xhcic[index]; + +	return 0; +} + +/** + * Stops the XHCI host controller + * and cleans up all the related data structures + * + * @param index	index to the host controller data structure + * @return none + */ +int usb_lowlevel_stop(int index) +{ +	struct xhci_ctrl *ctrl = (xhcic + index); +	u32 temp; + +	xhci_reset(ctrl->hcor); + +	debug("// Disabling event ring interrupts\n"); +	temp = xhci_readl(&ctrl->hcor->or_usbsts); +	xhci_writel(&ctrl->hcor->or_usbsts, temp & ~STS_EINT); +	temp = xhci_readl(&ctrl->ir_set->irq_pending); +	xhci_writel(&ctrl->ir_set->irq_pending, ER_IRQ_DISABLE(temp)); + +	xhci_hcd_stop(index); + +	xhci_cleanup(ctrl); + +	return 0; +} diff --git a/roms/u-boot/drivers/usb/host/xhci.h b/roms/u-boot/drivers/usb/host/xhci.h new file mode 100644 index 00000000..ceb1573d --- /dev/null +++ b/roms/u-boot/drivers/usb/host/xhci.h @@ -0,0 +1,1255 @@ +/* + * USB HOST XHCI Controller + * + * Based on xHCI host controller driver in linux-kernel + * by Sarah Sharp. + * + * Copyright (C) 2008 Intel Corp. + * Author: Sarah Sharp + * + * Copyright (C) 2013 Samsung Electronics Co.Ltd + * Authors: Vivek Gautam <gautam.vivek@samsung.com> + *	    Vikas Sajjan <vikas.sajjan@samsung.com> + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +#ifndef HOST_XHCI_H_ +#define HOST_XHCI_H_ + +#include <asm/cache.h> +#include <asm/io.h> +#include <linux/list.h> + +#define upper_32_bits(n) (u32)((n) >> 32) +#define lower_32_bits(n) (u32)(n) + +#define MAX_EP_CTX_NUM		31 +#define XHCI_ALIGNMENT		64 +/* Generic timeout for XHCI events */ +#define XHCI_TIMEOUT		5000 +/* Max number of USB devices for any host controller - limit in section 6.1 */ +#define MAX_HC_SLOTS            256 +/* Section 5.3.3 - MaxPorts */ +#define MAX_HC_PORTS            127 + +/* Up to 16 ms to halt an HC */ +#define XHCI_MAX_HALT_USEC	(16*1000) + +#define XHCI_MAX_RESET_USEC	(250*1000) + +/* + * These bits are Read Only (RO) and should be saved and written to the + * registers: 0, 3, 10:13, 30 + * connect status, over-current status, port speed, and device removable. + * connect status and port speed are also sticky - meaning they're in + * the AUX well and they aren't changed by a hot, warm, or cold reset. + */ +#define XHCI_PORT_RO ((1 << 0) | (1 << 3) | (0xf << 10) | (1 << 30)) +/* + * These bits are RW; writing a 0 clears the bit, writing a 1 sets the bit: + * bits 5:8, 9, 14:15, 25:27 + * link state, port power, port indicator state, "wake on" enable state + */ +#define XHCI_PORT_RWS ((0xf << 5) | (1 << 9) | (0x3 << 14) | (0x7 << 25)) +/* + * These bits are RW; writing a 1 sets the bit, writing a 0 has no effect: + * bit 4 (port reset) + */ +#define XHCI_PORT_RW1S ((1 << 4)) +/* + * These bits are RW; writing a 1 clears the bit, writing a 0 has no effect: + * bits 1, 17, 18, 19, 20, 21, 22, 23 + * port enable/disable, and + * change bits: connect, PED, + * warm port reset changed (reserved zero for USB 2.0 ports), + * over-current, reset, link state, and L1 change + */ +#define XHCI_PORT_RW1CS ((1 << 1) | (0x7f << 17)) +/* + * Bit 16 is RW, and writing a '1' to it causes the link state control to be + * latched in + */ +#define XHCI_PORT_RW ((1 << 16)) +/* + * These bits are Reserved Zero (RsvdZ) and zero should be written to them: + * bits 2, 24, 28:31 + */ +#define XHCI_PORT_RZ ((1 << 2) | (1 << 24) | (0xf << 28)) + +/* + * XHCI Register Space. + */ +struct xhci_hccr { +	uint32_t cr_capbase; +	uint32_t cr_hcsparams1; +	uint32_t cr_hcsparams2; +	uint32_t cr_hcsparams3; +	uint32_t cr_hccparams; +	uint32_t cr_dboff; +	uint32_t cr_rtsoff; + +/* hc_capbase bitmasks */ +/* bits 7:0 - how long is the Capabilities register */ +#define HC_LENGTH(p)		XHCI_HC_LENGTH(p) +/* bits 31:16	*/ +#define HC_VERSION(p)		(((p) >> 16) & 0xffff) + +/* HCSPARAMS1 - hcs_params1 - bitmasks */ +/* bits 0:7, Max Device Slots */ +#define HCS_MAX_SLOTS(p)	(((p) >> 0) & 0xff) +#define HCS_SLOTS_MASK		0xff +/* bits 8:18, Max Interrupters */ +#define HCS_MAX_INTRS(p)	(((p) >> 8) & 0x7ff) +/* bits 24:31, Max Ports - max value is 0x7F = 127 ports */ +#define HCS_MAX_PORTS_SHIFT	24 +#define HCS_MAX_PORTS_MASK	(0x7f << HCS_MAX_PORTS_SHIFT) +#define HCS_MAX_PORTS(p)	(((p) >> 24) & 0x7f) + +/* HCSPARAMS2 - hcs_params2 - bitmasks */ +/* bits 0:3, frames or uframes that SW needs to queue transactions + * ahead of the HW to meet periodic deadlines */ +#define HCS_IST(p)		(((p) >> 0) & 0xf) +/* bits 4:7, max number of Event Ring segments */ +#define HCS_ERST_MAX(p)		(((p) >> 4) & 0xf) +/* bit 26 Scratchpad restore - for save/restore HW state - not used yet */ +/* bits 27:31 number of Scratchpad buffers SW must allocate for the HW */ +#define HCS_MAX_SCRATCHPAD(p)   (((p) >> 27) & 0x1f) + +/* HCSPARAMS3 - hcs_params3 - bitmasks */ +/* bits 0:7, Max U1 to U0 latency for the roothub ports */ +#define HCS_U1_LATENCY(p)	(((p) >> 0) & 0xff) +/* bits 16:31, Max U2 to U0 latency for the roothub ports */ +#define HCS_U2_LATENCY(p)	(((p) >> 16) & 0xffff) + +/* HCCPARAMS - hcc_params - bitmasks */ +/* true: HC can use 64-bit address pointers */ +#define HCC_64BIT_ADDR(p)	((p) & (1 << 0)) +/* true: HC can do bandwidth negotiation */ +#define HCC_BANDWIDTH_NEG(p)	((p) & (1 << 1)) +/* true: HC uses 64-byte Device Context structures + * FIXME 64-byte context structures aren't supported yet. + */ +#define HCC_64BYTE_CONTEXT(p)	((p) & (1 << 2)) +/* true: HC has port power switches */ +#define HCC_PPC(p)		((p) & (1 << 3)) +/* true: HC has port indicators */ +#define HCS_INDICATOR(p)	((p) & (1 << 4)) +/* true: HC has Light HC Reset Capability */ +#define HCC_LIGHT_RESET(p)	((p) & (1 << 5)) +/* true: HC supports latency tolerance messaging */ +#define HCC_LTC(p)		((p) & (1 << 6)) +/* true: no secondary Stream ID Support */ +#define HCC_NSS(p)		((p) & (1 << 7)) +/* Max size for Primary Stream Arrays - 2^(n+1), where n is bits 12:15 */ +#define HCC_MAX_PSA(p)		(1 << ((((p) >> 12) & 0xf) + 1)) +/* Extended Capabilities pointer from PCI base - section 5.3.6 */ +#define HCC_EXT_CAPS(p)		XHCI_HCC_EXT_CAPS(p) + +/* db_off bitmask - bits 0:1 reserved */ +#define	DBOFF_MASK	(~0x3) + +/* run_regs_off bitmask - bits 0:4 reserved */ +#define	RTSOFF_MASK	(~0x1f) + +}; + +struct xhci_hcor_port_regs { +	volatile uint32_t or_portsc; +	volatile uint32_t or_portpmsc; +	volatile uint32_t or_portli; +	volatile uint32_t reserved_3; +}; + +struct xhci_hcor { +	volatile uint32_t or_usbcmd; +	volatile uint32_t or_usbsts; +	volatile uint32_t or_pagesize; +	volatile uint32_t reserved_0[2]; +	volatile uint32_t or_dnctrl; +	volatile uint64_t or_crcr; +	volatile uint32_t reserved_1[4]; +	volatile uint64_t or_dcbaap; +	volatile uint32_t or_config; +	volatile uint32_t reserved_2[241]; +	struct xhci_hcor_port_regs portregs[CONFIG_SYS_USB_XHCI_MAX_ROOT_PORTS]; + +	uint32_t reserved_4[CONFIG_SYS_USB_XHCI_MAX_ROOT_PORTS * 254]; +}; + +/* USBCMD - USB command - command bitmasks */ +/* start/stop HC execution - do not write unless HC is halted*/ +#define CMD_RUN		XHCI_CMD_RUN +/* Reset HC - resets internal HC state machine and all registers (except + * PCI config regs).  HC does NOT drive a USB reset on the downstream ports. + * The xHCI driver must reinitialize the xHC after setting this bit. + */ +#define CMD_RESET	(1 << 1) +/* Event Interrupt Enable - a '1' allows interrupts from the host controller */ +#define CMD_EIE		XHCI_CMD_EIE +/* Host System Error Interrupt Enable - get out-of-band signal for HC errors */ +#define CMD_HSEIE	XHCI_CMD_HSEIE +/* bits 4:6 are reserved (and should be preserved on writes). */ +/* light reset (port status stays unchanged) - reset completed when this is 0 */ +#define CMD_LRESET	(1 << 7) +/* host controller save/restore state. */ +#define CMD_CSS		(1 << 8) +#define CMD_CRS		(1 << 9) +/* Enable Wrap Event - '1' means xHC generates an event when MFINDEX wraps. */ +#define CMD_EWE		XHCI_CMD_EWE +/* MFINDEX power management - '1' means xHC can stop MFINDEX counter if all root + * hubs are in U3 (selective suspend), disconnect, disabled, or powered-off. + * '0' means the xHC can power it off if all ports are in the disconnect, + * disabled, or powered-off state. + */ +#define CMD_PM_INDEX	(1 << 11) +/* bits 12:31 are reserved (and should be preserved on writes). */ + +/* USBSTS - USB status - status bitmasks */ +/* HC not running - set to 1 when run/stop bit is cleared. */ +#define STS_HALT	XHCI_STS_HALT +/* serious error, e.g. PCI parity error.  The HC will clear the run/stop bit. */ +#define STS_FATAL	(1 << 2) +/* event interrupt - clear this prior to clearing any IP flags in IR set*/ +#define STS_EINT	(1 << 3) +/* port change detect */ +#define STS_PORT	(1 << 4) +/* bits 5:7 reserved and zeroed */ +/* save state status - '1' means xHC is saving state */ +#define STS_SAVE	(1 << 8) +/* restore state status - '1' means xHC is restoring state */ +#define STS_RESTORE	(1 << 9) +/* true: save or restore error */ +#define STS_SRE		(1 << 10) +/* true: Controller Not Ready to accept doorbell or op reg writes after reset */ +#define STS_CNR		XHCI_STS_CNR +/* true: internal Host Controller Error - SW needs to reset and reinitialize */ +#define STS_HCE		(1 << 12) +/* bits 13:31 reserved and should be preserved */ + +/* + * DNCTRL - Device Notification Control Register - dev_notification bitmasks + * Generate a device notification event when the HC sees a transaction with a + * notification type that matches a bit set in this bit field. + */ +#define	DEV_NOTE_MASK		(0xffff) +#define ENABLE_DEV_NOTE(x)	(1 << (x)) +/* Most of the device notification types should only be used for debug. + * SW does need to pay attention to function wake notifications. + */ +#define	DEV_NOTE_FWAKE		ENABLE_DEV_NOTE(1) + +/* CRCR - Command Ring Control Register - cmd_ring bitmasks */ +/* bit 0 is the command ring cycle state */ +/* stop ring operation after completion of the currently executing command */ +#define CMD_RING_PAUSE		(1 << 1) +/* stop ring immediately - abort the currently executing command */ +#define CMD_RING_ABORT		(1 << 2) +/* true: command ring is running */ +#define CMD_RING_RUNNING	(1 << 3) +/* bits 4:5 reserved and should be preserved */ +/* Command Ring pointer - bit mask for the lower 32 bits. */ +#define CMD_RING_RSVD_BITS	(0x3f) + +/* CONFIG - Configure Register - config_reg bitmasks */ +/* bits 0:7 - maximum number of device slots enabled (NumSlotsEn) */ +#define MAX_DEVS(p)	((p) & 0xff) +/* bits 8:31 - reserved and should be preserved */ + +/* PORTSC - Port Status and Control Register - port_status_base bitmasks */ +/* true: device connected */ +#define PORT_CONNECT	(1 << 0) +/* true: port enabled */ +#define PORT_PE		(1 << 1) +/* bit 2 reserved and zeroed */ +/* true: port has an over-current condition */ +#define PORT_OC		(1 << 3) +/* true: port reset signaling asserted */ +#define PORT_RESET	(1 << 4) +/* Port Link State - bits 5:8 + * A read gives the current link PM state of the port, + * a write with Link State Write Strobe set sets the link state. + */ +#define PORT_PLS_MASK	(0xf << 5) +#define XDEV_U0		(0x0 << 5) +#define XDEV_U2		(0x2 << 5) +#define XDEV_U3		(0x3 << 5) +#define XDEV_RESUME	(0xf << 5) +/* true: port has power (see HCC_PPC) */ +#define PORT_POWER	(1 << 9) +/* bits 10:13 indicate device speed: + * 0 - undefined speed - port hasn't be initialized by a reset yet + * 1 - full speed + * 2 - low speed + * 3 - high speed + * 4 - super speed + * 5-15 reserved + */ +#define DEV_SPEED_MASK		(0xf << 10) +#define	XDEV_FS			(0x1 << 10) +#define	XDEV_LS			(0x2 << 10) +#define	XDEV_HS			(0x3 << 10) +#define	XDEV_SS			(0x4 << 10) +#define DEV_UNDEFSPEED(p)	(((p) & DEV_SPEED_MASK) == (0x0<<10)) +#define DEV_FULLSPEED(p)	(((p) & DEV_SPEED_MASK) == XDEV_FS) +#define DEV_LOWSPEED(p)		(((p) & DEV_SPEED_MASK) == XDEV_LS) +#define DEV_HIGHSPEED(p)	(((p) & DEV_SPEED_MASK) == XDEV_HS) +#define DEV_SUPERSPEED(p)	(((p) & DEV_SPEED_MASK) == XDEV_SS) +/* Bits 20:23 in the Slot Context are the speed for the device */ +#define	SLOT_SPEED_FS		(XDEV_FS << 10) +#define	SLOT_SPEED_LS		(XDEV_LS << 10) +#define	SLOT_SPEED_HS		(XDEV_HS << 10) +#define	SLOT_SPEED_SS		(XDEV_SS << 10) +/* Port Indicator Control */ +#define PORT_LED_OFF	(0 << 14) +#define PORT_LED_AMBER	(1 << 14) +#define PORT_LED_GREEN	(2 << 14) +#define PORT_LED_MASK	(3 << 14) +/* Port Link State Write Strobe - set this when changing link state */ +#define PORT_LINK_STROBE	(1 << 16) +/* true: connect status change */ +#define PORT_CSC	(1 << 17) +/* true: port enable change */ +#define PORT_PEC	(1 << 18) +/* true: warm reset for a USB 3.0 device is done.  A "hot" reset puts the port + * into an enabled state, and the device into the default state.  A "warm" reset + * also resets the link, forcing the device through the link training sequence. + * SW can also look at the Port Reset register to see when warm reset is done. + */ +#define PORT_WRC	(1 << 19) +/* true: over-current change */ +#define PORT_OCC	(1 << 20) +/* true: reset change - 1 to 0 transition of PORT_RESET */ +#define PORT_RC		(1 << 21) +/* port link status change - set on some port link state transitions: + *  Transition				Reason + *  -------------------------------------------------------------------------- + *  - U3 to Resume		Wakeup signaling from a device + *  - Resume to Recovery to U0	USB 3.0 device resume + *  - Resume to U0		USB 2.0 device resume + *  - U3 to Recovery to U0	Software resume of USB 3.0 device complete + *  - U3 to U0			Software resume of USB 2.0 device complete + *  - U2 to U0			L1 resume of USB 2.1 device complete + *  - U0 to U0 (???)		L1 entry rejection by USB 2.1 device + *  - U0 to disabled		L1 entry error with USB 2.1 device + *  - Any state to inactive	Error on USB 3.0 port + */ +#define PORT_PLC	(1 << 22) +/* port configure error change - port failed to configure its link partner */ +#define PORT_CEC	(1 << 23) +/* bit 24 reserved */ +/* wake on connect (enable) */ +#define PORT_WKCONN_E	(1 << 25) +/* wake on disconnect (enable) */ +#define PORT_WKDISC_E	(1 << 26) +/* wake on over-current (enable) */ +#define PORT_WKOC_E	(1 << 27) +/* bits 28:29 reserved */ +/* true: device is removable - for USB 3.0 roothub emulation */ +#define PORT_DEV_REMOVE	(1 << 30) +/* Initiate a warm port reset - complete when PORT_WRC is '1' */ +#define PORT_WR		(1 << 31) + +/* We mark duplicate entries with -1 */ +#define DUPLICATE_ENTRY ((u8)(-1)) + +/* Port Power Management Status and Control - port_power_base bitmasks */ +/* Inactivity timer value for transitions into U1, in microseconds. + * Timeout can be up to 127us.  0xFF means an infinite timeout. + */ +#define PORT_U1_TIMEOUT(p)	((p) & 0xff) +/* Inactivity timer value for transitions into U2 */ +#define PORT_U2_TIMEOUT(p)	(((p) & 0xff) << 8) +/* Bits 24:31 for port testing */ + +/* USB2 Protocol PORTSPMSC */ +#define	PORT_L1S_MASK		7 +#define	PORT_L1S_SUCCESS	1 +#define	PORT_RWE		(1 << 3) +#define	PORT_HIRD(p)		(((p) & 0xf) << 4) +#define	PORT_HIRD_MASK		(0xf << 4) +#define	PORT_L1DS(p)		(((p) & 0xff) << 8) +#define	PORT_HLE		(1 << 16) + +/** +* struct xhci_intr_reg - Interrupt Register Set +* @irq_pending:	IMAN - Interrupt Management Register.  Used to enable +*			interrupts and check for pending interrupts. +* @irq_control:	IMOD - Interrupt Moderation Register. +*			Used to throttle interrupts. +* @erst_size:		Number of segments in the +			Event Ring Segment Table (ERST). +* @erst_base:		ERST base address. +* @erst_dequeue:	Event ring dequeue pointer. +* +* Each interrupter (defined by a MSI-X vector) has an event ring and an Event +* Ring Segment Table (ERST) associated with it. +* The event ring is comprised of  multiple segments of the same size. +* The HC places events on the ring and  "updates the Cycle bit in the TRBs to +* indicate to software the current  position of the Enqueue Pointer." +* The HCD (Linux) processes those events and  updates the dequeue pointer. +*/ +struct xhci_intr_reg { +	volatile __le32	irq_pending; +	volatile __le32	irq_control; +	volatile __le32	erst_size; +	volatile __le32	rsvd; +	volatile __le64	erst_base; +	volatile __le64	erst_dequeue; +}; + +/* irq_pending bitmasks */ +#define	ER_IRQ_PENDING(p)	((p) & 0x1) +/* bits 2:31 need to be preserved */ +/* THIS IS BUGGY - FIXME - IP IS WRITE 1 TO CLEAR */ +#define	ER_IRQ_CLEAR(p)		((p) & 0xfffffffe) +#define	ER_IRQ_ENABLE(p)	((ER_IRQ_CLEAR(p)) | 0x2) +#define	ER_IRQ_DISABLE(p)	((ER_IRQ_CLEAR(p)) & ~(0x2)) + +/* irq_control bitmasks */ +/* Minimum interval between interrupts (in 250ns intervals).  The interval + * between interrupts will be longer if there are no events on the event ring. + * Default is 4000 (1 ms). + */ +#define ER_IRQ_INTERVAL_MASK	(0xffff) +/* Counter used to count down the time to the next interrupt - HW use only */ +#define ER_IRQ_COUNTER_MASK	(0xffff << 16) + +/* erst_size bitmasks */ +/* Preserve bits 16:31 of erst_size */ +#define	ERST_SIZE_MASK		(0xffff << 16) + +/* erst_dequeue bitmasks */ +/* Dequeue ERST Segment Index (DESI) - Segment number (or alias) + * where the current dequeue pointer lies.  This is an optional HW hint. + */ +#define ERST_DESI_MASK		(0x7) +/* Event Handler Busy (EHB) - is the event ring scheduled to be serviced by + * a work queue (or delayed service routine)? + */ +#define ERST_EHB		(1 << 3) +#define ERST_PTR_MASK		(0xf) + +/** + * struct xhci_run_regs + * @microframe_index:	MFINDEX - current microframe number + * + * Section 5.5 Host Controller Runtime Registers: + * "Software should read and write these registers using only Dword (32 bit) + * or larger accesses" + */ +struct xhci_run_regs { +	__le32			microframe_index; +	__le32			rsvd[7]; +	struct xhci_intr_reg	ir_set[128]; +}; + +/** + * struct doorbell_array + * + * Bits  0 -  7: Endpoint target + * Bits  8 - 15: RsvdZ + * Bits 16 - 31: Stream ID + * + * Section 5.6 + */ +struct xhci_doorbell_array { +	volatile __le32	doorbell[256]; +}; + +#define DB_VALUE(ep, stream)	((((ep) + 1) & 0xff) | ((stream) << 16)) +#define DB_VALUE_HOST		0x00000000 + +/** + * struct xhci_protocol_caps + * @revision:		major revision, minor revision, capability ID, + *			and next capability pointer. + * @name_string:	Four ASCII characters to say which spec this xHC + *			follows, typically "USB ". + * @port_info:		Port offset, count, and protocol-defined information. + */ +struct xhci_protocol_caps { +	u32	revision; +	u32	name_string; +	u32	port_info; +}; + +#define	XHCI_EXT_PORT_MAJOR(x)	(((x) >> 24) & 0xff) +#define	XHCI_EXT_PORT_OFF(x)	((x) & 0xff) +#define	XHCI_EXT_PORT_COUNT(x)	(((x) >> 8) & 0xff) + +/** + * struct xhci_container_ctx + * @type: Type of context.  Used to calculated offsets to contained contexts. + * @size: Size of the context data + * @bytes: The raw context data given to HW + * @dma: dma address of the bytes + * + * Represents either a Device or Input context.  Holds a pointer to the raw + * memory used for the context (bytes) and dma address of it (dma). + */ +struct xhci_container_ctx { +	unsigned type; +#define XHCI_CTX_TYPE_DEVICE  0x1 +#define XHCI_CTX_TYPE_INPUT   0x2 + +	int size; +	u8 *bytes; +}; + +/** + * struct xhci_slot_ctx + * @dev_info:	Route string, device speed, hub info, and last valid endpoint + * @dev_info2:	Max exit latency for device number, root hub port number + * @tt_info:	tt_info is used to construct split transaction tokens + * @dev_state:	slot state and device address + * + * Slot Context - section 6.2.1.1.  This assumes the HC uses 32-byte context + * structures.  If the HC uses 64-byte contexts, there is an additional 32 bytes + * reserved at the end of the slot context for HC internal use. + */ +struct xhci_slot_ctx { +	__le32	dev_info; +	__le32	dev_info2; +	__le32	tt_info; +	__le32	dev_state; +	/* offset 0x10 to 0x1f reserved for HC internal use */ +	__le32	reserved[4]; +}; + +/* dev_info bitmasks */ +/* Route String - 0:19 */ +#define ROUTE_STRING_MASK	(0xfffff) +/* Device speed - values defined by PORTSC Device Speed field - 20:23 */ +#define DEV_SPEED		(0xf << 20) +/* bit 24 reserved */ +/* Is this LS/FS device connected through a HS hub? - bit 25 */ +#define DEV_MTT			(0x1 << 25) +/* Set if the device is a hub - bit 26 */ +#define DEV_HUB			(0x1 << 26) +/* Index of the last valid endpoint context in this device context - 27:31 */ +#define LAST_CTX_MASK		(0x1f << 27) +#define LAST_CTX(p)		((p) << 27) +#define LAST_CTX_TO_EP_NUM(p)	(((p) >> 27) - 1) +#define SLOT_FLAG		(1 << 0) +#define EP0_FLAG		(1 << 1) + +/* dev_info2 bitmasks */ +/* Max Exit Latency (ms) - worst case time to wake up all links in dev path */ +#define MAX_EXIT			(0xffff) +/* Root hub port number that is needed to access the USB device */ +#define ROOT_HUB_PORT(p)		(((p) & 0xff) << 16) +#define ROOT_HUB_PORT_MASK		(0xff) +#define ROOT_HUB_PORT_SHIFT		(16) +#define DEVINFO_TO_ROOT_HUB_PORT(p)	(((p) >> 16) & 0xff) +/* Maximum number of ports under a hub device */ +#define XHCI_MAX_PORTS(p)		(((p) & 0xff) << 24) + +/* tt_info bitmasks */ +/* + * TT Hub Slot ID - for low or full speed devices attached to a high-speed hub + * The Slot ID of the hub that isolates the high speed signaling from + * this low or full-speed device.  '0' if attached to root hub port. + */ +#define TT_SLOT			(0xff) +/* + * The number of the downstream facing port of the high-speed hub + * '0' if the device is not low or full speed. + */ +#define TT_PORT			(0xff << 8) +#define TT_THINK_TIME(p)	(((p) & 0x3) << 16) + +/* dev_state bitmasks */ +/* USB device address - assigned by the HC */ +#define DEV_ADDR_MASK	(0xff) +/* bits 8:26 reserved */ +/* Slot state */ +#define SLOT_STATE		(0x1f << 27) +#define GET_SLOT_STATE(p)	(((p) & (0x1f << 27)) >> 27) + +#define SLOT_STATE_DISABLED	0 +#define SLOT_STATE_ENABLED	SLOT_STATE_DISABLED +#define SLOT_STATE_DEFAULT	1 +#define SLOT_STATE_ADDRESSED	2 +#define SLOT_STATE_CONFIGURED	3 + +/** + * struct xhci_ep_ctx + * @ep_info:	endpoint state, streams, mult, and interval information. + * @ep_info2:	information on endpoint type, max packet size, max burst size, + *		error count, and whether the HC will force an event for all + *		transactions. + * @deq:	64-bit ring dequeue pointer address.  If the endpoint only + *		defines one stream, this points to the endpoint transfer ring. + *		Otherwise, it points to a stream context array, which has a + *		ring pointer for each flow. + * @tx_info: + *		Average TRB lengths for the endpoint ring and + *		max payload within an Endpoint Service Interval Time (ESIT). + * + * Endpoint Context - section 6.2.1.2.This assumes the HC uses 32-byte context + * structures.If the HC uses 64-byte contexts, there is an additional 32 bytes + * reserved at the end of the endpoint context for HC internal use. + */ +struct xhci_ep_ctx { +	__le32	ep_info; +	__le32	ep_info2; +	__le64	deq; +	__le32	tx_info; +	/* offset 0x14 - 0x1f reserved for HC internal use */ +	__le32	reserved[3]; +}; + +/* ep_info bitmasks */ +/* + * Endpoint State - bits 0:2 + * 0 - disabled + * 1 - running + * 2 - halted due to halt condition - ok to manipulate endpoint ring + * 3 - stopped + * 4 - TRB error + * 5-7 - reserved + */ +#define EP_STATE_MASK		(0xf) +#define EP_STATE_DISABLED	0 +#define EP_STATE_RUNNING	1 +#define EP_STATE_HALTED		2 +#define EP_STATE_STOPPED	3 +#define EP_STATE_ERROR		4 +/* Mult - Max number of burtst within an interval, in EP companion desc. */ +#define EP_MULT(p)		(((p) & 0x3) << 8) +#define CTX_TO_EP_MULT(p)	(((p) >> 8) & 0x3) +/* bits 10:14 are Max Primary Streams */ +/* bit 15 is Linear Stream Array */ +/* Interval - period between requests to an endpoint - 125u increments. */ +#define EP_INTERVAL(p)			(((p) & 0xff) << 16) +#define EP_INTERVAL_TO_UFRAMES(p)	(1 << (((p) >> 16) & 0xff)) +#define CTX_TO_EP_INTERVAL(p)		(((p) >> 16) & 0xff) +#define EP_MAXPSTREAMS_MASK		(0x1f << 10) +#define EP_MAXPSTREAMS(p)		(((p) << 10) & EP_MAXPSTREAMS_MASK) +/* Endpoint is set up with a Linear Stream Array (vs. Secondary Stream Array) */ +#define	EP_HAS_LSA			(1 << 15) + +/* ep_info2 bitmasks */ +/* + * Force Event - generate transfer events for all TRBs for this endpoint + * This will tell the HC to ignore the IOC and ISP flags (for debugging only). + */ +#define	FORCE_EVENT		(0x1) +#define ERROR_COUNT(p)		(((p) & 0x3) << 1) +#define ERROR_COUNT_SHIFT	(1) +#define ERROR_COUNT_MASK	(0x3) +#define CTX_TO_EP_TYPE(p)	(((p) >> 3) & 0x7) +#define EP_TYPE(p)		((p) << 3) +#define EP_TYPE_SHIFT		(3) +#define ISOC_OUT_EP		1 +#define BULK_OUT_EP		2 +#define INT_OUT_EP		3 +#define CTRL_EP			4 +#define ISOC_IN_EP		5 +#define BULK_IN_EP		6 +#define INT_IN_EP		7 +/* bit 6 reserved */ +/* bit 7 is Host Initiate Disable - for disabling stream selection */ +#define MAX_BURST(p)		(((p)&0xff) << 8) +#define MAX_BURST_MASK		(0xff) +#define MAX_BURST_SHIFT		(8) +#define CTX_TO_MAX_BURST(p)	(((p) >> 8) & 0xff) +#define MAX_PACKET(p)		(((p)&0xffff) << 16) +#define MAX_PACKET_MASK		(0xffff) +#define MAX_PACKET_DECODED(p)	(((p) >> 16) & 0xffff) +#define MAX_PACKET_SHIFT	(16) + +/* Get max packet size from ep desc. Bit 10..0 specify the max packet size. + * USB2.0 spec 9.6.6. + */ +#define GET_MAX_PACKET(p)	((p) & 0x7ff) + +/* tx_info bitmasks */ +#define AVG_TRB_LENGTH_FOR_EP(p)	((p) & 0xffff) +#define MAX_ESIT_PAYLOAD_FOR_EP(p)	(((p) & 0xffff) << 16) +#define CTX_TO_MAX_ESIT_PAYLOAD(p)	(((p) >> 16) & 0xffff) + +/* deq bitmasks */ +#define EP_CTX_CYCLE_MASK		(1 << 0) + + +/** + * struct xhci_input_control_context + * Input control context; see section 6.2.5. + * + * @drop_context:	set the bit of the endpoint context you want to disable + * @add_context:	set the bit of the endpoint context you want to enable + */ +struct xhci_input_control_ctx { +	volatile __le32	drop_flags; +	volatile __le32	add_flags; +	__le32	rsvd2[6]; +}; + + +/** + * struct xhci_device_context_array + * @dev_context_ptr	array of 64-bit DMA addresses for device contexts + */ +struct xhci_device_context_array { +	/* 64-bit device addresses; we only write 32-bit addresses */ +	__le64			dev_context_ptrs[MAX_HC_SLOTS]; +}; +/* TODO: write function to set the 64-bit device DMA address */ +/* + * TODO: change this to be dynamically sized at HC mem init time since the HC + * might not be able to handle the maximum number of devices possible. + */ + + +struct xhci_transfer_event { +	/* 64-bit buffer address, or immediate data */ +	__le64	buffer; +	__le32	transfer_len; +	/* This field is interpreted differently based on the type of TRB */ +	volatile __le32	flags; +}; + +/* Transfer event TRB length bit mask */ +/* bits 0:23 */ +#define EVENT_TRB_LEN(p)	((p) & 0xffffff) + +/** Transfer Event bit fields **/ +#define	TRB_TO_EP_ID(p)		(((p) >> 16) & 0x1f) + +/* Completion Code - only applicable for some types of TRBs */ +#define	COMP_CODE_MASK		(0xff << 24) +#define	COMP_CODE_SHIFT		(24) +#define GET_COMP_CODE(p)	(((p) & COMP_CODE_MASK) >> 24) + +typedef enum { +	COMP_SUCCESS = 1, +	/* Data Buffer Error */ +	COMP_DB_ERR, /* 2 */ +	/* Babble Detected Error */ +	COMP_BABBLE, /* 3 */ +	/* USB Transaction Error */ +	COMP_TX_ERR, /* 4 */ +	/* TRB Error - some TRB field is invalid */ +	COMP_TRB_ERR, /* 5 */ +	/* Stall Error - USB device is stalled */ +	COMP_STALL, /* 6 */ +	/* Resource Error - HC doesn't have memory for that device configuration */ +	COMP_ENOMEM, /* 7 */ +	/* Bandwidth Error - not enough room in schedule for this dev config */ +	COMP_BW_ERR, /* 8 */ +	/* No Slots Available Error - HC ran out of device slots */ +	COMP_ENOSLOTS, /* 9 */ +	/* Invalid Stream Type Error */ +	COMP_STREAM_ERR, /* 10 */ +	/* Slot Not Enabled Error - doorbell rung for disabled device slot */ +	COMP_EBADSLT, /* 11 */ +	/* Endpoint Not Enabled Error */ +	COMP_EBADEP,/* 12 */ +	/* Short Packet */ +	COMP_SHORT_TX, /* 13 */ +	/* Ring Underrun - doorbell rung for an empty isoc OUT ep ring */ +	COMP_UNDERRUN, /* 14 */ +	/* Ring Overrun - isoc IN ep ring is empty when ep is scheduled to RX */ +	COMP_OVERRUN, /* 15 */ +	/* Virtual Function Event Ring Full Error */ +	COMP_VF_FULL, /* 16 */ +	/* Parameter Error - Context parameter is invalid */ +	COMP_EINVAL, /* 17 */ +	/* Bandwidth Overrun Error - isoc ep exceeded its allocated bandwidth */ +	COMP_BW_OVER,/* 18 */ +	/* Context State Error - illegal context state transition requested */ +	COMP_CTX_STATE,/* 19 */ +	/* No Ping Response Error - HC didn't get PING_RESPONSE in time to TX */ +	COMP_PING_ERR,/* 20 */ +	/* Event Ring is full */ +	COMP_ER_FULL,/* 21 */ +	/* Incompatible Device Error */ +	COMP_DEV_ERR,/* 22 */ +	/* Missed Service Error - HC couldn't service an isoc ep within interval */ +	COMP_MISSED_INT,/* 23 */ +	/* Successfully stopped command ring */ +	COMP_CMD_STOP, /* 24 */ +	/* Successfully aborted current command and stopped command ring */ +	COMP_CMD_ABORT, /* 25 */ +	/* Stopped - transfer was terminated by a stop endpoint command */ +	COMP_STOP,/* 26 */ +	/* Same as COMP_EP_STOPPED, but the transferred length in the event +	 * is invalid */ +	COMP_STOP_INVAL, /* 27*/ +	/* Control Abort Error - Debug Capability - control pipe aborted */ +	COMP_DBG_ABORT, /* 28 */ +	/* Max Exit Latency Too Large Error */ +	COMP_MEL_ERR,/* 29 */ +	/* TRB type 30 reserved */ +	/* Isoc Buffer Overrun - an isoc IN ep sent more data than could fit in TD */ +	COMP_BUFF_OVER = 31, +	/* Event Lost Error - xHC has an "internal event overrun condition" */ +	COMP_ISSUES, /* 32 */ +	/* Undefined Error - reported when other error codes don't apply */ +	COMP_UNKNOWN, /* 33 */ +	/* Invalid Stream ID Error */ +	COMP_STRID_ERR, /* 34 */ +	/* Secondary Bandwidth Error - may be returned by a Configure Endpoint cmd */ +	COMP_2ND_BW_ERR, /* 35 */ +	/* Split Transaction Error */ +	COMP_SPLIT_ERR /* 36 */ + +} xhci_comp_code; + +struct xhci_link_trb { +	/* 64-bit segment pointer*/ +	volatile __le64 segment_ptr; +	volatile __le32 intr_target; +	volatile __le32 control; +}; + +/* control bitfields */ +#define LINK_TOGGLE (0x1 << 1) + +/* Command completion event TRB */ +struct xhci_event_cmd { +	/* Pointer to command TRB, or the value passed by the event data trb */ +	volatile __le64 cmd_trb; +	volatile __le32 status; +	volatile __le32 flags; +}; + +/* flags bitmasks */ +/* bits 16:23 are the virtual function ID */ +/* bits 24:31 are the slot ID */ +#define	TRB_TO_SLOT_ID(p)		(((p) & (0xff << 24)) >> 24) +#define	TRB_TO_SLOT_ID_SHIFT		(24) +#define	TRB_TO_SLOT_ID_MASK		(0xff << TRB_TO_SLOT_ID_SHIFT) +#define	SLOT_ID_FOR_TRB(p)		(((p) & 0xff) << 24) +#define	SLOT_ID_FOR_TRB_MASK		(0xff) +#define	SLOT_ID_FOR_TRB_SHIFT		(24) + +/* Stop Endpoint TRB - ep_index to endpoint ID for this TRB */ +#define TRB_TO_EP_INDEX(p)		((((p) & (0x1f << 16)) >> 16) - 1) +#define	EP_ID_FOR_TRB(p)		((((p) + 1) & 0x1f) << 16) + +#define SUSPEND_PORT_FOR_TRB(p)		(((p) & 1) << 23) +#define TRB_TO_SUSPEND_PORT(p)		(((p) & (1 << 23)) >> 23) +#define LAST_EP_INDEX			30 + +/* Set TR Dequeue Pointer command TRB fields */ +#define TRB_TO_STREAM_ID(p)		((((p) & (0xffff << 16)) >> 16)) +#define STREAM_ID_FOR_TRB(p)		((((p)) & 0xffff) << 16) + + +/* Port Status Change Event TRB fields */ +/* Port ID - bits 31:24 */ +#define GET_PORT_ID(p)			(((p) & (0xff << 24)) >> 24) +#define	PORT_ID_SHIFT			(24) +#define	PORT_ID_MASK			(0xff << PORT_ID_SHIFT) + +/* Normal TRB fields */ +/* transfer_len bitmasks - bits 0:16 */ +#define	TRB_LEN(p)			((p) & 0x1ffff) +#define	TRB_LEN_MASK			(0x1ffff) +/* Interrupter Target - which MSI-X vector to target the completion event at */ +#define	TRB_INTR_TARGET_SHIFT		(22) +#define	TRB_INTR_TARGET_MASK		(0x3ff) +#define TRB_INTR_TARGET(p)		(((p) & 0x3ff) << 22) +#define GET_INTR_TARGET(p)		(((p) >> 22) & 0x3ff) +#define TRB_TBC(p)			(((p) & 0x3) << 7) +#define TRB_TLBPC(p)			(((p) & 0xf) << 16) + +/* Cycle bit - indicates TRB ownership by HC or HCD */ +#define TRB_CYCLE		(1<<0) +/* + * Force next event data TRB to be evaluated before task switch. + * Used to pass OS data back after a TD completes. + */ +#define TRB_ENT			(1<<1) +/* Interrupt on short packet */ +#define TRB_ISP			(1<<2) +/* Set PCIe no snoop attribute */ +#define TRB_NO_SNOOP		(1<<3) +/* Chain multiple TRBs into a TD */ +#define TRB_CHAIN		(1<<4) +/* Interrupt on completion */ +#define TRB_IOC			(1<<5) +/* The buffer pointer contains immediate data */ +#define TRB_IDT			(1<<6) + +/* Block Event Interrupt */ +#define	TRB_BEI			(1<<9) + +/* Control transfer TRB specific fields */ +#define TRB_DIR_IN		(1<<16) +#define	TRB_TX_TYPE(p)		((p) << 16) +#define	TRB_TX_TYPE_SHIFT	(16) +#define	TRB_DATA_OUT		2 +#define	TRB_DATA_IN		3 + +/* Isochronous TRB specific fields */ +#define TRB_SIA			(1 << 31) + +struct xhci_generic_trb { +	volatile __le32 field[4]; +}; + +union xhci_trb { +	struct xhci_link_trb		link; +	struct xhci_transfer_event	trans_event; +	struct xhci_event_cmd		event_cmd; +	struct xhci_generic_trb		generic; +}; + +/* TRB bit mask */ +#define	TRB_TYPE_BITMASK	(0xfc00) +#define TRB_TYPE(p)		((p) << 10) +#define TRB_TYPE_SHIFT		(10) +#define TRB_FIELD_TO_TYPE(p)	(((p) & TRB_TYPE_BITMASK) >> 10) + +/* TRB type IDs */ +typedef enum { +	/* bulk, interrupt, isoc scatter/gather, and control data stage */ +	TRB_NORMAL = 1, +	/* setup stage for control transfers */ +	TRB_SETUP, /* 2 */ +	/* data stage for control transfers */ +	TRB_DATA, /* 3 */ +	/* status stage for control transfers */ +	TRB_STATUS, /* 4 */ +	/* isoc transfers */ +	TRB_ISOC, /* 5 */ +	/* TRB for linking ring segments */ +	TRB_LINK, /* 6 */ +	/* TRB for EVENT DATA */ +	TRB_EVENT_DATA, /* 7 */ +	/* Transfer Ring No-op (not for the command ring) */ +	TRB_TR_NOOP, /* 8 */ +	/* Command TRBs */ +	/* Enable Slot Command */ +	TRB_ENABLE_SLOT, /* 9 */ +	/* Disable Slot Command */ +	TRB_DISABLE_SLOT, /* 10 */ +	/* Address Device Command */ +	TRB_ADDR_DEV, /* 11 */ +	/* Configure Endpoint Command */ +	TRB_CONFIG_EP, /* 12 */ +	/* Evaluate Context Command */ +	TRB_EVAL_CONTEXT, /* 13 */ +	/* Reset Endpoint Command */ +	TRB_RESET_EP, /* 14 */ +	/* Stop Transfer Ring Command */ +	TRB_STOP_RING, /* 15 */ +	/* Set Transfer Ring Dequeue Pointer Command */ +	TRB_SET_DEQ, /* 16 */ +	/* Reset Device Command */ +	TRB_RESET_DEV, /* 17 */ +	/* Force Event Command (opt) */ +	TRB_FORCE_EVENT, /* 18 */ +	/* Negotiate Bandwidth Command (opt) */ +	TRB_NEG_BANDWIDTH, /* 19 */ +	/* Set Latency Tolerance Value Command (opt) */ +	TRB_SET_LT, /* 20 */ +	/* Get port bandwidth Command */ +	TRB_GET_BW, /* 21 */ +	/* Force Header Command - generate a transaction or link management packet */ +	TRB_FORCE_HEADER, /* 22 */ +	/* No-op Command - not for transfer rings */ +	TRB_CMD_NOOP, /* 23 */ +	/* TRB IDs 24-31 reserved */ +	/* Event TRBS */ +	/* Transfer Event */ +	TRB_TRANSFER = 32, +	/* Command Completion Event */ +	TRB_COMPLETION, /* 33 */ +	/* Port Status Change Event */ +	TRB_PORT_STATUS, /* 34 */ +	/* Bandwidth Request Event (opt) */ +	TRB_BANDWIDTH_EVENT, /* 35 */ +	/* Doorbell Event (opt) */ +	TRB_DOORBELL, /* 36 */ +	/* Host Controller Event */ +	TRB_HC_EVENT, /* 37 */ +	/* Device Notification Event - device sent function wake notification */ +	TRB_DEV_NOTE, /* 38 */ +	/* MFINDEX Wrap Event - microframe counter wrapped */ +	TRB_MFINDEX_WRAP, /* 39 */ +	/* TRB IDs 40-47 reserved, 48-63 is vendor-defined */ +	/* Nec vendor-specific command completion event. */ +	TRB_NEC_CMD_COMP = 48, /* 48 */ +	/* Get NEC firmware revision. */ +	TRB_NEC_GET_FW, /* 49 */ +} trb_type; + +#define TRB_TYPE_LINK(x)	(((x) & TRB_TYPE_BITMASK) == TRB_TYPE(TRB_LINK)) +/* Above, but for __le32 types -- can avoid work by swapping constants: */ +#define TRB_TYPE_LINK_LE32(x)	(((x) & cpu_to_le32(TRB_TYPE_BITMASK)) == \ +				 cpu_to_le32(TRB_TYPE(TRB_LINK))) +#define TRB_TYPE_NOOP_LE32(x)	(((x) & cpu_to_le32(TRB_TYPE_BITMASK)) == \ +				 cpu_to_le32(TRB_TYPE(TRB_TR_NOOP))) + +/* + * TRBS_PER_SEGMENT must be a multiple of 4, + * since the command ring is 64-byte aligned. + * It must also be greater than 16. + */ +#define TRBS_PER_SEGMENT	64 +/* Allow two commands + a link TRB, along with any reserved command TRBs */ +#define MAX_RSVD_CMD_TRBS	(TRBS_PER_SEGMENT - 3) +#define SEGMENT_SIZE		(TRBS_PER_SEGMENT*16) +/* SEGMENT_SHIFT should be log2(SEGMENT_SIZE). + * Change this if you change TRBS_PER_SEGMENT! + */ +#define SEGMENT_SHIFT		10 +/* TRB buffer pointers can't cross 64KB boundaries */ +#define TRB_MAX_BUFF_SHIFT	16 +#define TRB_MAX_BUFF_SIZE	(1 << TRB_MAX_BUFF_SHIFT) + +struct xhci_segment { +	union xhci_trb		*trbs; +	/* private to HCD */ +	struct xhci_segment	*next; +}; + +struct xhci_ring { +	struct xhci_segment	*first_seg; +	union  xhci_trb		*enqueue; +	struct xhci_segment	*enq_seg; +	union  xhci_trb		*dequeue; +	struct xhci_segment	*deq_seg; +	/* +	 * Write the cycle state into the TRB cycle field to give ownership of +	 * the TRB to the host controller (if we are the producer), or to check +	 * if we own the TRB (if we are the consumer).  See section 4.9.1. +	 */ +	volatile u32		cycle_state; +	unsigned int		num_segs; +}; + +struct xhci_erst_entry { +	/* 64-bit event ring segment address */ +	__le64	seg_addr; +	__le32	seg_size; +	/* Set to zero */ +	__le32	rsvd; +}; + +struct xhci_erst { +	struct xhci_erst_entry	*entries; +	unsigned int		num_entries; +	/* Num entries the ERST can contain */ +	unsigned int		erst_size; +}; + +/* + * Each segment table entry is 4*32bits long.  1K seems like an ok size: + * (1K bytes * 8bytes/bit) / (4*32 bits) = 64 segment entries in the table, + * meaning 64 ring segments. + * Initial allocated size of the ERST, in number of entries */ +#define	ERST_NUM_SEGS	3 +/* Initial number of event segment rings allocated */ +#define	ERST_ENTRIES	3 +/* Initial allocated size of the ERST, in number of entries */ +#define	ERST_SIZE	64 +/* Poll every 60 seconds */ +#define	POLL_TIMEOUT	60 +/* Stop endpoint command timeout (secs) for URB cancellation watchdog timer */ +#define XHCI_STOP_EP_CMD_TIMEOUT	5 +/* XXX: Make these module parameters */ + +struct xhci_virt_ep { +	struct xhci_ring		*ring; +	unsigned int			ep_state; +#define SET_DEQ_PENDING		(1 << 0) +#define EP_HALTED		(1 << 1)	/* For stall handling */ +#define EP_HALT_PENDING		(1 << 2)	/* For URB cancellation */ +/* Transitioning the endpoint to using streams, don't enqueue URBs */ +#define EP_GETTING_STREAMS	(1 << 3) +#define EP_HAS_STREAMS		(1 << 4) +/* Transitioning the endpoint to not using streams, don't enqueue URBs */ +#define EP_GETTING_NO_STREAMS	(1 << 5) +}; + +#define CTX_SIZE(_hcc) (HCC_64BYTE_CONTEXT(_hcc) ? 64 : 32) + +struct xhci_virt_device { +	struct usb_device		*udev; +	/* +	 * Commands to the hardware are passed an "input context" that +	 * tells the hardware what to change in its data structures. +	 * The hardware will return changes in an "output context" that +	 * software must allocate for the hardware.  We need to keep +	 * track of input and output contexts separately because +	 * these commands might fail and we don't trust the hardware. +	 */ +	struct xhci_container_ctx       *out_ctx; +	/* Used for addressing devices and configuration changes */ +	struct xhci_container_ctx       *in_ctx; +	/* Rings saved to ensure old alt settings can be re-instated */ +#define	XHCI_MAX_RINGS_CACHED	31 +	struct xhci_virt_ep		eps[31]; +}; + +/* TODO: copied from ehci.h - can be refactored? */ +/* xHCI spec says all registers are little endian */ +static inline unsigned int xhci_readl(uint32_t volatile *regs) +{ +	return readl(regs); +} + +static inline void xhci_writel(uint32_t volatile *regs, const unsigned int val) +{ +	writel(val, regs); +} + +/* + * Registers should always be accessed with double word or quad word accesses. + * Some xHCI implementations may support 64-bit address pointers.  Registers + * with 64-bit address pointers should be written to with dword accesses by + * writing the low dword first (ptr[0]), then the high dword (ptr[1]) second. + * xHCI implementations that do not support 64-bit address pointers will ignore + * the high dword, and write order is irrelevant. + */ +static inline u64 xhci_readq(__le64 volatile *regs) +{ +	__u32 *ptr = (__u32 *)regs; +	u64 val_lo = readl(ptr); +	u64 val_hi = readl(ptr + 1); +	return val_lo + (val_hi << 32); +} + +static inline void xhci_writeq(__le64 volatile *regs, const u64 val) +{ +	__u32 *ptr = (__u32 *)regs; +	u32 val_lo = lower_32_bits(val); +	/* FIXME */ +	u32 val_hi = 0; +	writel(val_lo, ptr); +	writel(val_hi, ptr + 1); +} + +int xhci_hcd_init(int index, struct xhci_hccr **ret_hccr, +					struct xhci_hcor **ret_hcor); +void xhci_hcd_stop(int index); + + +/************************************************************* +	EXTENDED CAPABILITY DEFINITIONS +*************************************************************/ +/* Up to 16 ms to halt an HC */ +#define XHCI_MAX_HALT_USEC	(16*1000) +/* HC not running - set to 1 when run/stop bit is cleared. */ +#define XHCI_STS_HALT		(1 << 0) + +/* HCCPARAMS offset from PCI base address */ +#define XHCI_HCC_PARAMS_OFFSET	0x10 +/* HCCPARAMS contains the first extended capability pointer */ +#define XHCI_HCC_EXT_CAPS(p)	(((p)>>16)&0xffff) + +/* Command and Status registers offset from the Operational Registers address */ +#define XHCI_CMD_OFFSET		0x00 +#define XHCI_STS_OFFSET		0x04 + +#define XHCI_MAX_EXT_CAPS		50 + +/* Capability Register */ +/* bits 7:0 - how long is the Capabilities register */ +#define XHCI_HC_LENGTH(p)	(((p) >> 00) & 0x00ff) + +/* Extended capability register fields */ +#define XHCI_EXT_CAPS_ID(p)	(((p) >> 0) & 0xff) +#define XHCI_EXT_CAPS_NEXT(p)	(((p) >> 8) & 0xff) +#define	XHCI_EXT_CAPS_VAL(p)	((p) >> 16) +/* Extended capability IDs - ID 0 reserved */ +#define XHCI_EXT_CAPS_LEGACY	1 +#define XHCI_EXT_CAPS_PROTOCOL	2 +#define XHCI_EXT_CAPS_PM	3 +#define XHCI_EXT_CAPS_VIRT	4 +#define XHCI_EXT_CAPS_ROUTE	5 +/* IDs 6-9 reserved */ +#define XHCI_EXT_CAPS_DEBUG	10 +/* USB Legacy Support Capability - section 7.1.1 */ +#define XHCI_HC_BIOS_OWNED	(1 << 16) +#define XHCI_HC_OS_OWNED	(1 << 24) + +/* USB Legacy Support Capability - section 7.1.1 */ +/* Add this offset, plus the value of xECP in HCCPARAMS to the base address */ +#define XHCI_LEGACY_SUPPORT_OFFSET	(0x00) + +/* USB Legacy Support Control and Status Register  - section 7.1.2 */ +/* Add this offset, plus the value of xECP in HCCPARAMS to the base address */ +#define XHCI_LEGACY_CONTROL_OFFSET	(0x04) +/* bits 1:2, 5:12, and 17:19 need to be preserved; bits 21:28 should be zero */ +#define	XHCI_LEGACY_DISABLE_SMI		((0x3 << 1) + (0xff << 5) + (0x7 << 17)) + +/* USB 2.0 xHCI 0.96 L1C capability - section 7.2.2.1.3.2 */ +#define XHCI_L1C               (1 << 16) + +/* USB 2.0 xHCI 1.0 hardware LMP capability - section 7.2.2.1.3.2 */ +#define XHCI_HLC               (1 << 19) + +/* command register values to disable interrupts and halt the HC */ +/* start/stop HC execution - do not write unless HC is halted*/ +#define XHCI_CMD_RUN		(1 << 0) +/* Event Interrupt Enable - get irq when EINT bit is set in USBSTS register */ +#define XHCI_CMD_EIE		(1 << 2) +/* Host System Error Interrupt Enable - get irq when HSEIE bit set in USBSTS */ +#define XHCI_CMD_HSEIE		(1 << 3) +/* Enable Wrap Event - '1' means xHC generates an event when MFINDEX wraps. */ +#define XHCI_CMD_EWE		(1 << 10) + +#define XHCI_IRQS		(XHCI_CMD_EIE | XHCI_CMD_HSEIE | XHCI_CMD_EWE) + +/* true: Controller Not Ready to accept doorbell or op reg writes after reset */ +#define XHCI_STS_CNR		(1 << 11) + +struct xhci_ctrl { +	struct xhci_hccr *hccr;	/* R/O registers, not need for volatile */ +	struct xhci_hcor *hcor; +	struct xhci_doorbell_array *dba; +	struct xhci_run_regs *run_regs; +	struct xhci_device_context_array *dcbaa		\ +			__attribute__ ((aligned(ARCH_DMA_MINALIGN))); +	struct xhci_ring *event_ring; +	struct xhci_ring *cmd_ring; +	struct xhci_ring *transfer_ring; +	struct xhci_segment *seg; +	struct xhci_intr_reg *ir_set; +	struct xhci_erst erst; +	struct xhci_erst_entry entry[ERST_NUM_SEGS]; +	struct xhci_virt_device *devs[MAX_HC_SLOTS]; +	int rootdev; +}; + +unsigned long trb_addr(struct xhci_segment *seg, union xhci_trb *trb); +struct xhci_input_control_ctx +		*xhci_get_input_control_ctx(struct xhci_container_ctx *ctx); +struct xhci_slot_ctx *xhci_get_slot_ctx(struct xhci_ctrl *ctrl, +					struct xhci_container_ctx *ctx); +struct xhci_ep_ctx *xhci_get_ep_ctx(struct xhci_ctrl *ctrl, +				    struct xhci_container_ctx *ctx, +				    unsigned int ep_index); +void xhci_endpoint_copy(struct xhci_ctrl *ctrl, +			struct xhci_container_ctx *in_ctx, +			struct xhci_container_ctx *out_ctx, +			unsigned int ep_index); +void xhci_slot_copy(struct xhci_ctrl *ctrl, +		    struct xhci_container_ctx *in_ctx, +		    struct xhci_container_ctx *out_ctx); +void xhci_setup_addressable_virt_dev(struct usb_device *udev); +void xhci_queue_command(struct xhci_ctrl *ctrl, u8 *ptr, +			u32 slot_id, u32 ep_index, trb_type cmd); +void xhci_acknowledge_event(struct xhci_ctrl *ctrl); +union xhci_trb *xhci_wait_for_event(struct xhci_ctrl *ctrl, trb_type expected); +int xhci_bulk_tx(struct usb_device *udev, unsigned long pipe, +		 int length, void *buffer); +int xhci_ctrl_tx(struct usb_device *udev, unsigned long pipe, +		 struct devrequest *req, int length, void *buffer); +int xhci_check_maxpacket(struct usb_device *udev); +void xhci_flush_cache(uint32_t addr, u32 type_len); +void xhci_inval_cache(uint32_t addr, u32 type_len); +void xhci_cleanup(struct xhci_ctrl *ctrl); +struct xhci_ring *xhci_ring_alloc(unsigned int num_segs, bool link_trbs); +int xhci_alloc_virt_device(struct usb_device *udev); +int xhci_mem_init(struct xhci_ctrl *ctrl, struct xhci_hccr *hccr, +		  struct xhci_hcor *hcor); + +#endif /* HOST_XHCI_H_ */  | 
