xref: /rk3399_rockchip-uboot/drivers/usb/host/ehci-hcd.c (revision c60795f41d37600b6ebd79ec99252ec2f5efecd4)
12731b9a8SJean-Christophe PLAGNIOL-VILLARD /*-
22731b9a8SJean-Christophe PLAGNIOL-VILLARD  * Copyright (c) 2007-2008, Juniper Networks, Inc.
32731b9a8SJean-Christophe PLAGNIOL-VILLARD  * Copyright (c) 2008, Excito Elektronik i Skåne AB
42731b9a8SJean-Christophe PLAGNIOL-VILLARD  * Copyright (c) 2008, Michael Trimarchi <trimarchimichael@yahoo.it>
52731b9a8SJean-Christophe PLAGNIOL-VILLARD  *
62731b9a8SJean-Christophe PLAGNIOL-VILLARD  * All rights reserved.
72731b9a8SJean-Christophe PLAGNIOL-VILLARD  *
82731b9a8SJean-Christophe PLAGNIOL-VILLARD  * This program is free software; you can redistribute it and/or
92731b9a8SJean-Christophe PLAGNIOL-VILLARD  * modify it under the terms of the GNU General Public License as
102731b9a8SJean-Christophe PLAGNIOL-VILLARD  * published by the Free Software Foundation version 2 of
112731b9a8SJean-Christophe PLAGNIOL-VILLARD  * the License.
122731b9a8SJean-Christophe PLAGNIOL-VILLARD  *
132731b9a8SJean-Christophe PLAGNIOL-VILLARD  * This program is distributed in the hope that it will be useful,
142731b9a8SJean-Christophe PLAGNIOL-VILLARD  * but WITHOUT ANY WARRANTY; without even the implied warranty of
152731b9a8SJean-Christophe PLAGNIOL-VILLARD  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
162731b9a8SJean-Christophe PLAGNIOL-VILLARD  * GNU General Public License for more details.
172731b9a8SJean-Christophe PLAGNIOL-VILLARD  *
182731b9a8SJean-Christophe PLAGNIOL-VILLARD  * You should have received a copy of the GNU General Public License
192731b9a8SJean-Christophe PLAGNIOL-VILLARD  * along with this program; if not, write to the Free Software
202731b9a8SJean-Christophe PLAGNIOL-VILLARD  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
212731b9a8SJean-Christophe PLAGNIOL-VILLARD  * MA 02111-1307 USA
222731b9a8SJean-Christophe PLAGNIOL-VILLARD  */
232731b9a8SJean-Christophe PLAGNIOL-VILLARD #include <common.h>
242731b9a8SJean-Christophe PLAGNIOL-VILLARD #include <asm/byteorder.h>
2593ad908cSLucas Stach #include <asm/unaligned.h>
262731b9a8SJean-Christophe PLAGNIOL-VILLARD #include <usb.h>
272731b9a8SJean-Christophe PLAGNIOL-VILLARD #include <asm/io.h>
282731b9a8SJean-Christophe PLAGNIOL-VILLARD #include <malloc.h>
2967333f76SStefan Roese #include <watchdog.h>
302731b9a8SJean-Christophe PLAGNIOL-VILLARD 
312731b9a8SJean-Christophe PLAGNIOL-VILLARD #include "ehci.h"
322731b9a8SJean-Christophe PLAGNIOL-VILLARD 
33676ae068SLucas Stach #ifndef CONFIG_USB_MAX_CONTROLLER_COUNT
34676ae068SLucas Stach #define CONFIG_USB_MAX_CONTROLLER_COUNT 1
35676ae068SLucas Stach #endif
362731b9a8SJean-Christophe PLAGNIOL-VILLARD 
37676ae068SLucas Stach static struct ehci_ctrl {
38676ae068SLucas Stach 	struct ehci_hccr *hccr;	/* R/O registers, not need for volatile */
39676ae068SLucas Stach 	struct ehci_hcor *hcor;
40676ae068SLucas Stach 	int rootdev;
41676ae068SLucas Stach 	uint16_t portreset;
42676ae068SLucas Stach 	struct QH qh_list __attribute__((aligned(USB_DMA_MINALIGN)));
43676ae068SLucas Stach } ehcic[CONFIG_USB_MAX_CONTROLLER_COUNT];
4471c5de4fSTom Rini 
4571c5de4fSTom Rini #define ALIGN_END_ADDR(type, ptr, size)			\
4671c5de4fSTom Rini 	((uint32_t)(ptr) + roundup((size) * sizeof(type), USB_DMA_MINALIGN))
472731b9a8SJean-Christophe PLAGNIOL-VILLARD 
482731b9a8SJean-Christophe PLAGNIOL-VILLARD static struct descriptor {
492731b9a8SJean-Christophe PLAGNIOL-VILLARD 	struct usb_hub_descriptor hub;
502731b9a8SJean-Christophe PLAGNIOL-VILLARD 	struct usb_device_descriptor device;
512731b9a8SJean-Christophe PLAGNIOL-VILLARD 	struct usb_linux_config_descriptor config;
522731b9a8SJean-Christophe PLAGNIOL-VILLARD 	struct usb_linux_interface_descriptor interface;
532731b9a8SJean-Christophe PLAGNIOL-VILLARD 	struct usb_endpoint_descriptor endpoint;
542731b9a8SJean-Christophe PLAGNIOL-VILLARD }  __attribute__ ((packed)) descriptor = {
552731b9a8SJean-Christophe PLAGNIOL-VILLARD 	{
562731b9a8SJean-Christophe PLAGNIOL-VILLARD 		0x8,		/* bDescLength */
572731b9a8SJean-Christophe PLAGNIOL-VILLARD 		0x29,		/* bDescriptorType: hub descriptor */
582731b9a8SJean-Christophe PLAGNIOL-VILLARD 		2,		/* bNrPorts -- runtime modified */
592731b9a8SJean-Christophe PLAGNIOL-VILLARD 		0,		/* wHubCharacteristics */
605f4b4f2fSVincent Palatin 		10,		/* bPwrOn2PwrGood */
612731b9a8SJean-Christophe PLAGNIOL-VILLARD 		0,		/* bHubCntrCurrent */
622731b9a8SJean-Christophe PLAGNIOL-VILLARD 		{},		/* Device removable */
632731b9a8SJean-Christophe PLAGNIOL-VILLARD 		{}		/* at most 7 ports! XXX */
642731b9a8SJean-Christophe PLAGNIOL-VILLARD 	},
652731b9a8SJean-Christophe PLAGNIOL-VILLARD 	{
662731b9a8SJean-Christophe PLAGNIOL-VILLARD 		0x12,		/* bLength */
672731b9a8SJean-Christophe PLAGNIOL-VILLARD 		1,		/* bDescriptorType: UDESC_DEVICE */
686d313c84SSergei Shtylyov 		cpu_to_le16(0x0200), /* bcdUSB: v2.0 */
692731b9a8SJean-Christophe PLAGNIOL-VILLARD 		9,		/* bDeviceClass: UDCLASS_HUB */
702731b9a8SJean-Christophe PLAGNIOL-VILLARD 		0,		/* bDeviceSubClass: UDSUBCLASS_HUB */
712731b9a8SJean-Christophe PLAGNIOL-VILLARD 		1,		/* bDeviceProtocol: UDPROTO_HSHUBSTT */
722731b9a8SJean-Christophe PLAGNIOL-VILLARD 		64,		/* bMaxPacketSize: 64 bytes */
732731b9a8SJean-Christophe PLAGNIOL-VILLARD 		0x0000,		/* idVendor */
742731b9a8SJean-Christophe PLAGNIOL-VILLARD 		0x0000,		/* idProduct */
756d313c84SSergei Shtylyov 		cpu_to_le16(0x0100), /* bcdDevice */
762731b9a8SJean-Christophe PLAGNIOL-VILLARD 		1,		/* iManufacturer */
772731b9a8SJean-Christophe PLAGNIOL-VILLARD 		2,		/* iProduct */
782731b9a8SJean-Christophe PLAGNIOL-VILLARD 		0,		/* iSerialNumber */
792731b9a8SJean-Christophe PLAGNIOL-VILLARD 		1		/* bNumConfigurations: 1 */
802731b9a8SJean-Christophe PLAGNIOL-VILLARD 	},
812731b9a8SJean-Christophe PLAGNIOL-VILLARD 	{
822731b9a8SJean-Christophe PLAGNIOL-VILLARD 		0x9,
832731b9a8SJean-Christophe PLAGNIOL-VILLARD 		2,		/* bDescriptorType: UDESC_CONFIG */
842731b9a8SJean-Christophe PLAGNIOL-VILLARD 		cpu_to_le16(0x19),
852731b9a8SJean-Christophe PLAGNIOL-VILLARD 		1,		/* bNumInterface */
862731b9a8SJean-Christophe PLAGNIOL-VILLARD 		1,		/* bConfigurationValue */
872731b9a8SJean-Christophe PLAGNIOL-VILLARD 		0,		/* iConfiguration */
882731b9a8SJean-Christophe PLAGNIOL-VILLARD 		0x40,		/* bmAttributes: UC_SELF_POWER */
892731b9a8SJean-Christophe PLAGNIOL-VILLARD 		0		/* bMaxPower */
902731b9a8SJean-Christophe PLAGNIOL-VILLARD 	},
912731b9a8SJean-Christophe PLAGNIOL-VILLARD 	{
922731b9a8SJean-Christophe PLAGNIOL-VILLARD 		0x9,		/* bLength */
932731b9a8SJean-Christophe PLAGNIOL-VILLARD 		4,		/* bDescriptorType: UDESC_INTERFACE */
942731b9a8SJean-Christophe PLAGNIOL-VILLARD 		0,		/* bInterfaceNumber */
952731b9a8SJean-Christophe PLAGNIOL-VILLARD 		0,		/* bAlternateSetting */
962731b9a8SJean-Christophe PLAGNIOL-VILLARD 		1,		/* bNumEndpoints */
972731b9a8SJean-Christophe PLAGNIOL-VILLARD 		9,		/* bInterfaceClass: UICLASS_HUB */
982731b9a8SJean-Christophe PLAGNIOL-VILLARD 		0,		/* bInterfaceSubClass: UISUBCLASS_HUB */
992731b9a8SJean-Christophe PLAGNIOL-VILLARD 		0,		/* bInterfaceProtocol: UIPROTO_HSHUBSTT */
1002731b9a8SJean-Christophe PLAGNIOL-VILLARD 		0		/* iInterface */
1012731b9a8SJean-Christophe PLAGNIOL-VILLARD 	},
1022731b9a8SJean-Christophe PLAGNIOL-VILLARD 	{
1032731b9a8SJean-Christophe PLAGNIOL-VILLARD 		0x7,		/* bLength */
1042731b9a8SJean-Christophe PLAGNIOL-VILLARD 		5,		/* bDescriptorType: UDESC_ENDPOINT */
1052731b9a8SJean-Christophe PLAGNIOL-VILLARD 		0x81,		/* bEndpointAddress:
1062731b9a8SJean-Christophe PLAGNIOL-VILLARD 				 * UE_DIR_IN | EHCI_INTR_ENDPT
1072731b9a8SJean-Christophe PLAGNIOL-VILLARD 				 */
1082731b9a8SJean-Christophe PLAGNIOL-VILLARD 		3,		/* bmAttributes: UE_INTERRUPT */
1098f8bd565STom Rix 		8,		/* wMaxPacketSize */
1102731b9a8SJean-Christophe PLAGNIOL-VILLARD 		255		/* bInterval */
1112731b9a8SJean-Christophe PLAGNIOL-VILLARD 	},
1122731b9a8SJean-Christophe PLAGNIOL-VILLARD };
1132731b9a8SJean-Christophe PLAGNIOL-VILLARD 
1142731b9a8SJean-Christophe PLAGNIOL-VILLARD #if defined(CONFIG_EHCI_IS_TDI)
1152731b9a8SJean-Christophe PLAGNIOL-VILLARD #define ehci_is_TDI()	(1)
1162731b9a8SJean-Christophe PLAGNIOL-VILLARD #else
1172731b9a8SJean-Christophe PLAGNIOL-VILLARD #define ehci_is_TDI()	(0)
1182731b9a8SJean-Christophe PLAGNIOL-VILLARD #endif
1192731b9a8SJean-Christophe PLAGNIOL-VILLARD 
1203874b6d6SMarek Vasut void __ehci_powerup_fixup(uint32_t *status_reg, uint32_t *reg)
1213874b6d6SMarek Vasut {
1223874b6d6SMarek Vasut 	mdelay(50);
1233874b6d6SMarek Vasut }
1243874b6d6SMarek Vasut 
1253874b6d6SMarek Vasut void ehci_powerup_fixup(uint32_t *status_reg, uint32_t *reg)
1263874b6d6SMarek Vasut 	__attribute__((weak, alias("__ehci_powerup_fixup")));
1273874b6d6SMarek Vasut 
1282731b9a8SJean-Christophe PLAGNIOL-VILLARD static int handshake(uint32_t *ptr, uint32_t mask, uint32_t done, int usec)
1292731b9a8SJean-Christophe PLAGNIOL-VILLARD {
1302731b9a8SJean-Christophe PLAGNIOL-VILLARD 	uint32_t result;
1312731b9a8SJean-Christophe PLAGNIOL-VILLARD 	do {
1322731b9a8SJean-Christophe PLAGNIOL-VILLARD 		result = ehci_readl(ptr);
13309c83a45SWolfgang Denk 		udelay(5);
1342731b9a8SJean-Christophe PLAGNIOL-VILLARD 		if (result == ~(uint32_t)0)
1352731b9a8SJean-Christophe PLAGNIOL-VILLARD 			return -1;
1362731b9a8SJean-Christophe PLAGNIOL-VILLARD 		result &= mask;
1372731b9a8SJean-Christophe PLAGNIOL-VILLARD 		if (result == done)
1382731b9a8SJean-Christophe PLAGNIOL-VILLARD 			return 0;
1392731b9a8SJean-Christophe PLAGNIOL-VILLARD 		usec--;
1402731b9a8SJean-Christophe PLAGNIOL-VILLARD 	} while (usec > 0);
1412731b9a8SJean-Christophe PLAGNIOL-VILLARD 	return -1;
1422731b9a8SJean-Christophe PLAGNIOL-VILLARD }
1432731b9a8SJean-Christophe PLAGNIOL-VILLARD 
144676ae068SLucas Stach static int ehci_reset(int index)
1452731b9a8SJean-Christophe PLAGNIOL-VILLARD {
1462731b9a8SJean-Christophe PLAGNIOL-VILLARD 	uint32_t cmd;
1472731b9a8SJean-Christophe PLAGNIOL-VILLARD 	uint32_t tmp;
1482731b9a8SJean-Christophe PLAGNIOL-VILLARD 	uint32_t *reg_ptr;
1492731b9a8SJean-Christophe PLAGNIOL-VILLARD 	int ret = 0;
1502731b9a8SJean-Christophe PLAGNIOL-VILLARD 
151676ae068SLucas Stach 	cmd = ehci_readl(&ehcic[index].hcor->or_usbcmd);
152273d7204SStefan Roese 	cmd = (cmd & ~CMD_RUN) | CMD_RESET;
153676ae068SLucas Stach 	ehci_writel(&ehcic[index].hcor->or_usbcmd, cmd);
154676ae068SLucas Stach 	ret = handshake((uint32_t *)&ehcic[index].hcor->or_usbcmd,
155676ae068SLucas Stach 			CMD_RESET, 0, 250 * 1000);
1562731b9a8SJean-Christophe PLAGNIOL-VILLARD 	if (ret < 0) {
1572731b9a8SJean-Christophe PLAGNIOL-VILLARD 		printf("EHCI fail to reset\n");
1582731b9a8SJean-Christophe PLAGNIOL-VILLARD 		goto out;
1592731b9a8SJean-Christophe PLAGNIOL-VILLARD 	}
1602731b9a8SJean-Christophe PLAGNIOL-VILLARD 
1612731b9a8SJean-Christophe PLAGNIOL-VILLARD 	if (ehci_is_TDI()) {
162676ae068SLucas Stach 		reg_ptr = (uint32_t *)((u8 *)ehcic[index].hcor + USBMODE);
1632731b9a8SJean-Christophe PLAGNIOL-VILLARD 		tmp = ehci_readl(reg_ptr);
1642731b9a8SJean-Christophe PLAGNIOL-VILLARD 		tmp |= USBMODE_CM_HC;
1652731b9a8SJean-Christophe PLAGNIOL-VILLARD #if defined(CONFIG_EHCI_MMIO_BIG_ENDIAN)
1662731b9a8SJean-Christophe PLAGNIOL-VILLARD 		tmp |= USBMODE_BE;
1672731b9a8SJean-Christophe PLAGNIOL-VILLARD #endif
1682731b9a8SJean-Christophe PLAGNIOL-VILLARD 		ehci_writel(reg_ptr, tmp);
1692731b9a8SJean-Christophe PLAGNIOL-VILLARD 	}
1709ab4ce22SSimon Glass 
1719ab4ce22SSimon Glass #ifdef CONFIG_USB_EHCI_TXFIFO_THRESH
172676ae068SLucas Stach 	cmd = ehci_readl(&ehcic[index].hcor->or_txfilltuning);
17314eb79b7SBenoît Thébaudeau 	cmd &= ~TXFIFO_THRESH_MASK;
1749ab4ce22SSimon Glass 	cmd |= TXFIFO_THRESH(CONFIG_USB_EHCI_TXFIFO_THRESH);
175676ae068SLucas Stach 	ehci_writel(&ehcic[index].hcor->or_txfilltuning, cmd);
1769ab4ce22SSimon Glass #endif
1772731b9a8SJean-Christophe PLAGNIOL-VILLARD out:
1782731b9a8SJean-Christophe PLAGNIOL-VILLARD 	return ret;
1792731b9a8SJean-Christophe PLAGNIOL-VILLARD }
1802731b9a8SJean-Christophe PLAGNIOL-VILLARD 
1812731b9a8SJean-Christophe PLAGNIOL-VILLARD static int ehci_td_buffer(struct qTD *td, void *buf, size_t sz)
1822731b9a8SJean-Christophe PLAGNIOL-VILLARD {
183b8adb120SMarek Vasut 	uint32_t delta, next;
184b8adb120SMarek Vasut 	uint32_t addr = (uint32_t)buf;
1852731b9a8SJean-Christophe PLAGNIOL-VILLARD 	int idx;
1862731b9a8SJean-Christophe PLAGNIOL-VILLARD 
187189a6956SIlya Yanok 	if (addr != ALIGN(addr, ARCH_DMA_MINALIGN))
188b8adb120SMarek Vasut 		debug("EHCI-HCD: Misaligned buffer address (%p)\n", buf);
189b8adb120SMarek Vasut 
190189a6956SIlya Yanok 	flush_dcache_range(addr, ALIGN(addr + sz, ARCH_DMA_MINALIGN));
191189a6956SIlya Yanok 
1922731b9a8SJean-Christophe PLAGNIOL-VILLARD 	idx = 0;
193cdeb9161SBenoît Thébaudeau 	while (idx < QT_BUFFER_CNT) {
1942731b9a8SJean-Christophe PLAGNIOL-VILLARD 		td->qt_buffer[idx] = cpu_to_hc32(addr);
1953ed16071SWolfgang Denk 		td->qt_buffer_hi[idx] = 0;
19614eb79b7SBenoît Thébaudeau 		next = (addr + EHCI_PAGE_SIZE) & ~(EHCI_PAGE_SIZE - 1);
1972731b9a8SJean-Christophe PLAGNIOL-VILLARD 		delta = next - addr;
1982731b9a8SJean-Christophe PLAGNIOL-VILLARD 		if (delta >= sz)
1992731b9a8SJean-Christophe PLAGNIOL-VILLARD 			break;
2002731b9a8SJean-Christophe PLAGNIOL-VILLARD 		sz -= delta;
2012731b9a8SJean-Christophe PLAGNIOL-VILLARD 		addr = next;
2022731b9a8SJean-Christophe PLAGNIOL-VILLARD 		idx++;
2032731b9a8SJean-Christophe PLAGNIOL-VILLARD 	}
2042731b9a8SJean-Christophe PLAGNIOL-VILLARD 
205cdeb9161SBenoît Thébaudeau 	if (idx == QT_BUFFER_CNT) {
2062af16f85SIlya Yanok 		printf("out of buffer pointers (%u bytes left)\n", sz);
2072731b9a8SJean-Christophe PLAGNIOL-VILLARD 		return -1;
2082731b9a8SJean-Christophe PLAGNIOL-VILLARD 	}
2092731b9a8SJean-Christophe PLAGNIOL-VILLARD 
2102731b9a8SJean-Christophe PLAGNIOL-VILLARD 	return 0;
2112731b9a8SJean-Christophe PLAGNIOL-VILLARD }
2122731b9a8SJean-Christophe PLAGNIOL-VILLARD 
213*c60795f4SIlya Yanok static inline u8 ehci_encode_speed(enum usb_device_speed speed)
214*c60795f4SIlya Yanok {
215*c60795f4SIlya Yanok 	#define QH_HIGH_SPEED	2
216*c60795f4SIlya Yanok 	#define QH_FULL_SPEED	0
217*c60795f4SIlya Yanok 	#define QH_LOW_SPEED	1
218*c60795f4SIlya Yanok 	if (speed == USB_SPEED_HIGH)
219*c60795f4SIlya Yanok 		return QH_HIGH_SPEED;
220*c60795f4SIlya Yanok 	if (speed == USB_SPEED_LOW)
221*c60795f4SIlya Yanok 		return QH_LOW_SPEED;
222*c60795f4SIlya Yanok 	return QH_FULL_SPEED;
223*c60795f4SIlya Yanok }
224*c60795f4SIlya Yanok 
2252731b9a8SJean-Christophe PLAGNIOL-VILLARD static int
2262731b9a8SJean-Christophe PLAGNIOL-VILLARD ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
2272731b9a8SJean-Christophe PLAGNIOL-VILLARD 		   int length, struct devrequest *req)
2282731b9a8SJean-Christophe PLAGNIOL-VILLARD {
22971c5de4fSTom Rini 	ALLOC_ALIGN_BUFFER(struct QH, qh, 1, USB_DMA_MINALIGN);
2305cec214eSBenoît Thébaudeau 	struct qTD *qtd;
2315cec214eSBenoît Thébaudeau 	int qtd_count = 0;
232de98e8b2SMarek Vasut 	int qtd_counter = 0;
2332731b9a8SJean-Christophe PLAGNIOL-VILLARD 	volatile struct qTD *vtd;
2342731b9a8SJean-Christophe PLAGNIOL-VILLARD 	unsigned long ts;
2352731b9a8SJean-Christophe PLAGNIOL-VILLARD 	uint32_t *tdp;
236db191346SBenoît Thébaudeau 	uint32_t endpt, maxpacket, token, usbsts;
2372731b9a8SJean-Christophe PLAGNIOL-VILLARD 	uint32_t c, toggle;
2382731b9a8SJean-Christophe PLAGNIOL-VILLARD 	uint32_t cmd;
23996820a35SSimon Glass 	int timeout;
2402731b9a8SJean-Christophe PLAGNIOL-VILLARD 	int ret = 0;
241676ae068SLucas Stach 	struct ehci_ctrl *ctrl = dev->controller;
2422731b9a8SJean-Christophe PLAGNIOL-VILLARD 
2432731b9a8SJean-Christophe PLAGNIOL-VILLARD 	debug("dev=%p, pipe=%lx, buffer=%p, length=%d, req=%p\n", dev, pipe,
2442731b9a8SJean-Christophe PLAGNIOL-VILLARD 	      buffer, length, req);
2452731b9a8SJean-Christophe PLAGNIOL-VILLARD 	if (req != NULL)
2462731b9a8SJean-Christophe PLAGNIOL-VILLARD 		debug("req=%u (%#x), type=%u (%#x), value=%u (%#x), index=%u\n",
2472731b9a8SJean-Christophe PLAGNIOL-VILLARD 		      req->request, req->request,
2482731b9a8SJean-Christophe PLAGNIOL-VILLARD 		      req->requesttype, req->requesttype,
2492731b9a8SJean-Christophe PLAGNIOL-VILLARD 		      le16_to_cpu(req->value), le16_to_cpu(req->value),
2502731b9a8SJean-Christophe PLAGNIOL-VILLARD 		      le16_to_cpu(req->index));
2512731b9a8SJean-Christophe PLAGNIOL-VILLARD 
252db191346SBenoît Thébaudeau #define PKT_ALIGN	512
2535cec214eSBenoît Thébaudeau 	/*
2545cec214eSBenoît Thébaudeau 	 * The USB transfer is split into qTD transfers. Eeach qTD transfer is
2555cec214eSBenoît Thébaudeau 	 * described by a transfer descriptor (the qTD). The qTDs form a linked
2565cec214eSBenoît Thébaudeau 	 * list with a queue head (QH).
2575cec214eSBenoît Thébaudeau 	 *
2585cec214eSBenoît Thébaudeau 	 * Each qTD transfer starts with a new USB packet, i.e. a packet cannot
2595cec214eSBenoît Thébaudeau 	 * have its beginning in a qTD transfer and its end in the following
2605cec214eSBenoît Thébaudeau 	 * one, so the qTD transfer lengths have to be chosen accordingly.
2615cec214eSBenoît Thébaudeau 	 *
2625cec214eSBenoît Thébaudeau 	 * Each qTD transfer uses up to QT_BUFFER_CNT data buffers, mapped to
2635cec214eSBenoît Thébaudeau 	 * single pages. The first data buffer can start at any offset within a
2645cec214eSBenoît Thébaudeau 	 * page (not considering the cache-line alignment issues), while the
2655cec214eSBenoît Thébaudeau 	 * following buffers must be page-aligned. There is no alignment
2665cec214eSBenoît Thébaudeau 	 * constraint on the size of a qTD transfer.
2675cec214eSBenoît Thébaudeau 	 */
2685cec214eSBenoît Thébaudeau 	if (req != NULL)
2695cec214eSBenoît Thébaudeau 		/* 1 qTD will be needed for SETUP, and 1 for ACK. */
2705cec214eSBenoît Thébaudeau 		qtd_count += 1 + 1;
2715cec214eSBenoît Thébaudeau 	if (length > 0 || req == NULL) {
2725cec214eSBenoît Thébaudeau 		/*
2735cec214eSBenoît Thébaudeau 		 * Determine the qTD transfer size that will be used for the
274db191346SBenoît Thébaudeau 		 * data payload (not considering the first qTD transfer, which
275db191346SBenoît Thébaudeau 		 * may be longer or shorter, and the final one, which may be
276db191346SBenoît Thébaudeau 		 * shorter).
2775cec214eSBenoît Thébaudeau 		 *
2785cec214eSBenoît Thébaudeau 		 * In order to keep each packet within a qTD transfer, the qTD
279db191346SBenoît Thébaudeau 		 * transfer size is aligned to PKT_ALIGN, which is a multiple of
280db191346SBenoît Thébaudeau 		 * wMaxPacketSize (except in some cases for interrupt transfers,
281db191346SBenoît Thébaudeau 		 * see comment in submit_int_msg()).
2825cec214eSBenoît Thébaudeau 		 *
283db191346SBenoît Thébaudeau 		 * By default, i.e. if the input buffer is aligned to PKT_ALIGN,
2845cec214eSBenoît Thébaudeau 		 * QT_BUFFER_CNT full pages will be used.
2855cec214eSBenoît Thébaudeau 		 */
2865cec214eSBenoît Thébaudeau 		int xfr_sz = QT_BUFFER_CNT;
2875cec214eSBenoît Thébaudeau 		/*
288db191346SBenoît Thébaudeau 		 * However, if the input buffer is not aligned to PKT_ALIGN, the
289db191346SBenoît Thébaudeau 		 * qTD transfer size will be one page shorter, and the first qTD
2905cec214eSBenoît Thébaudeau 		 * data buffer of each transfer will be page-unaligned.
2915cec214eSBenoît Thébaudeau 		 */
292db191346SBenoît Thébaudeau 		if ((uint32_t)buffer & (PKT_ALIGN - 1))
2935cec214eSBenoît Thébaudeau 			xfr_sz--;
2945cec214eSBenoît Thébaudeau 		/* Convert the qTD transfer size to bytes. */
2955cec214eSBenoît Thébaudeau 		xfr_sz *= EHCI_PAGE_SIZE;
2965cec214eSBenoît Thébaudeau 		/*
297db191346SBenoît Thébaudeau 		 * Approximate by excess the number of qTDs that will be
298db191346SBenoît Thébaudeau 		 * required for the data payload. The exact formula is way more
299db191346SBenoît Thébaudeau 		 * complicated and saves at most 2 qTDs, i.e. a total of 128
300db191346SBenoît Thébaudeau 		 * bytes.
3015cec214eSBenoît Thébaudeau 		 */
302db191346SBenoît Thébaudeau 		qtd_count += 2 + length / xfr_sz;
3035cec214eSBenoît Thébaudeau 	}
3045cec214eSBenoît Thébaudeau /*
305db191346SBenoît Thébaudeau  * Threshold value based on the worst-case total size of the allocated qTDs for
306db191346SBenoît Thébaudeau  * a mass-storage transfer of 65535 blocks of 512 bytes.
3075cec214eSBenoît Thébaudeau  */
308db191346SBenoît Thébaudeau #if CONFIG_SYS_MALLOC_LEN <= 64 + 128 * 1024
3095cec214eSBenoît Thébaudeau #warning CONFIG_SYS_MALLOC_LEN may be too small for EHCI
3105cec214eSBenoît Thébaudeau #endif
3115cec214eSBenoît Thébaudeau 	qtd = memalign(USB_DMA_MINALIGN, qtd_count * sizeof(struct qTD));
3125cec214eSBenoît Thébaudeau 	if (qtd == NULL) {
3135cec214eSBenoît Thébaudeau 		printf("unable to allocate TDs\n");
3145cec214eSBenoît Thébaudeau 		return -1;
3155cec214eSBenoît Thébaudeau 	}
3165cec214eSBenoît Thébaudeau 
31771c5de4fSTom Rini 	memset(qh, 0, sizeof(struct QH));
3185cec214eSBenoît Thébaudeau 	memset(qtd, 0, qtd_count * sizeof(*qtd));
319de98e8b2SMarek Vasut 
320b8adb120SMarek Vasut 	toggle = usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe));
321b8adb120SMarek Vasut 
32241b1f0acSMarek Vasut 	/*
32341b1f0acSMarek Vasut 	 * Setup QH (3.6 in ehci-r10.pdf)
32441b1f0acSMarek Vasut 	 *
32541b1f0acSMarek Vasut 	 *   qh_link ................. 03-00 H
32641b1f0acSMarek Vasut 	 *   qh_endpt1 ............... 07-04 H
32741b1f0acSMarek Vasut 	 *   qh_endpt2 ............... 0B-08 H
32841b1f0acSMarek Vasut 	 * - qh_curtd
32941b1f0acSMarek Vasut 	 *   qh_overlay.qt_next ...... 13-10 H
33041b1f0acSMarek Vasut 	 * - qh_overlay.qt_altnext
33141b1f0acSMarek Vasut 	 */
332676ae068SLucas Stach 	qh->qh_link = cpu_to_hc32((uint32_t)&ctrl->qh_list | QH_LINK_TYPE_QH);
333*c60795f4SIlya Yanok 	c = (dev->speed != USB_SPEED_HIGH) && !usb_pipeendpoint(pipe);
334db191346SBenoît Thébaudeau 	maxpacket = usb_maxpacket(dev, pipe);
33514eb79b7SBenoît Thébaudeau 	endpt = QH_ENDPT1_RL(8) | QH_ENDPT1_C(c) |
336db191346SBenoît Thébaudeau 		QH_ENDPT1_MAXPKTLEN(maxpacket) | QH_ENDPT1_H(0) |
33714eb79b7SBenoît Thébaudeau 		QH_ENDPT1_DTC(QH_ENDPT1_DTC_DT_FROM_QTD) |
338*c60795f4SIlya Yanok 		QH_ENDPT1_EPS(ehci_encode_speed(dev->speed)) |
33914eb79b7SBenoît Thébaudeau 		QH_ENDPT1_ENDPT(usb_pipeendpoint(pipe)) | QH_ENDPT1_I(0) |
34014eb79b7SBenoît Thébaudeau 		QH_ENDPT1_DEVADDR(usb_pipedevice(pipe));
34171c5de4fSTom Rini 	qh->qh_endpt1 = cpu_to_hc32(endpt);
34214eb79b7SBenoît Thébaudeau 	endpt = QH_ENDPT2_MULT(1) | QH_ENDPT2_PORTNUM(dev->portnr) |
34314eb79b7SBenoît Thébaudeau 		QH_ENDPT2_HUBADDR(dev->parent->devnum) |
34414eb79b7SBenoît Thébaudeau 		QH_ENDPT2_UFCMASK(0) | QH_ENDPT2_UFSMASK(0);
34571c5de4fSTom Rini 	qh->qh_endpt2 = cpu_to_hc32(endpt);
34671c5de4fSTom Rini 	qh->qh_overlay.qt_next = cpu_to_hc32(QT_NEXT_TERMINATE);
3472731b9a8SJean-Christophe PLAGNIOL-VILLARD 
34871c5de4fSTom Rini 	tdp = &qh->qh_overlay.qt_next;
3492731b9a8SJean-Christophe PLAGNIOL-VILLARD 
3502731b9a8SJean-Christophe PLAGNIOL-VILLARD 	if (req != NULL) {
35141b1f0acSMarek Vasut 		/*
35241b1f0acSMarek Vasut 		 * Setup request qTD (3.5 in ehci-r10.pdf)
35341b1f0acSMarek Vasut 		 *
35441b1f0acSMarek Vasut 		 *   qt_next ................ 03-00 H
35541b1f0acSMarek Vasut 		 *   qt_altnext ............. 07-04 H
35641b1f0acSMarek Vasut 		 *   qt_token ............... 0B-08 H
35741b1f0acSMarek Vasut 		 *
35841b1f0acSMarek Vasut 		 *   [ buffer, buffer_hi ] loaded with "req".
35941b1f0acSMarek Vasut 		 */
360de98e8b2SMarek Vasut 		qtd[qtd_counter].qt_next = cpu_to_hc32(QT_NEXT_TERMINATE);
361de98e8b2SMarek Vasut 		qtd[qtd_counter].qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE);
36214eb79b7SBenoît Thébaudeau 		token = QT_TOKEN_DT(0) | QT_TOKEN_TOTALBYTES(sizeof(*req)) |
36314eb79b7SBenoît Thébaudeau 			QT_TOKEN_IOC(0) | QT_TOKEN_CPAGE(0) | QT_TOKEN_CERR(3) |
36414eb79b7SBenoît Thébaudeau 			QT_TOKEN_PID(QT_TOKEN_PID_SETUP) |
36514eb79b7SBenoît Thébaudeau 			QT_TOKEN_STATUS(QT_TOKEN_STATUS_ACTIVE);
366de98e8b2SMarek Vasut 		qtd[qtd_counter].qt_token = cpu_to_hc32(token);
36714eb79b7SBenoît Thébaudeau 		if (ehci_td_buffer(&qtd[qtd_counter], req, sizeof(*req))) {
36814eb79b7SBenoît Thébaudeau 			printf("unable to construct SETUP TD\n");
3692731b9a8SJean-Christophe PLAGNIOL-VILLARD 			goto fail;
3702731b9a8SJean-Christophe PLAGNIOL-VILLARD 		}
37141b1f0acSMarek Vasut 		/* Update previous qTD! */
372de98e8b2SMarek Vasut 		*tdp = cpu_to_hc32((uint32_t)&qtd[qtd_counter]);
373de98e8b2SMarek Vasut 		tdp = &qtd[qtd_counter++].qt_next;
3742731b9a8SJean-Christophe PLAGNIOL-VILLARD 		toggle = 1;
3752731b9a8SJean-Christophe PLAGNIOL-VILLARD 	}
3762731b9a8SJean-Christophe PLAGNIOL-VILLARD 
3772731b9a8SJean-Christophe PLAGNIOL-VILLARD 	if (length > 0 || req == NULL) {
3785cec214eSBenoît Thébaudeau 		uint8_t *buf_ptr = buffer;
3795cec214eSBenoît Thébaudeau 		int left_length = length;
3805cec214eSBenoît Thébaudeau 
3815cec214eSBenoît Thébaudeau 		do {
3825cec214eSBenoît Thébaudeau 			/*
3835cec214eSBenoît Thébaudeau 			 * Determine the size of this qTD transfer. By default,
3845cec214eSBenoît Thébaudeau 			 * QT_BUFFER_CNT full pages can be used.
3855cec214eSBenoît Thébaudeau 			 */
3865cec214eSBenoît Thébaudeau 			int xfr_bytes = QT_BUFFER_CNT * EHCI_PAGE_SIZE;
3875cec214eSBenoît Thébaudeau 			/*
3885cec214eSBenoît Thébaudeau 			 * However, if the input buffer is not page-aligned, the
3895cec214eSBenoît Thébaudeau 			 * portion of the first page before the buffer start
3905cec214eSBenoît Thébaudeau 			 * offset within that page is unusable.
3915cec214eSBenoît Thébaudeau 			 */
3925cec214eSBenoît Thébaudeau 			xfr_bytes -= (uint32_t)buf_ptr & (EHCI_PAGE_SIZE - 1);
3935cec214eSBenoît Thébaudeau 			/*
3945cec214eSBenoît Thébaudeau 			 * In order to keep each packet within a qTD transfer,
395db191346SBenoît Thébaudeau 			 * align the qTD transfer size to PKT_ALIGN.
3965cec214eSBenoît Thébaudeau 			 */
397db191346SBenoît Thébaudeau 			xfr_bytes &= ~(PKT_ALIGN - 1);
3985cec214eSBenoît Thébaudeau 			/*
3995cec214eSBenoît Thébaudeau 			 * This transfer may be shorter than the available qTD
4005cec214eSBenoît Thébaudeau 			 * transfer size that has just been computed.
4015cec214eSBenoît Thébaudeau 			 */
4025cec214eSBenoît Thébaudeau 			xfr_bytes = min(xfr_bytes, left_length);
4035cec214eSBenoît Thébaudeau 
40441b1f0acSMarek Vasut 			/*
40541b1f0acSMarek Vasut 			 * Setup request qTD (3.5 in ehci-r10.pdf)
40641b1f0acSMarek Vasut 			 *
40741b1f0acSMarek Vasut 			 *   qt_next ................ 03-00 H
40841b1f0acSMarek Vasut 			 *   qt_altnext ............. 07-04 H
40941b1f0acSMarek Vasut 			 *   qt_token ............... 0B-08 H
41041b1f0acSMarek Vasut 			 *
41141b1f0acSMarek Vasut 			 *   [ buffer, buffer_hi ] loaded with "buffer".
41241b1f0acSMarek Vasut 			 */
4135cec214eSBenoît Thébaudeau 			qtd[qtd_counter].qt_next =
4145cec214eSBenoît Thébaudeau 					cpu_to_hc32(QT_NEXT_TERMINATE);
4155cec214eSBenoît Thébaudeau 			qtd[qtd_counter].qt_altnext =
4165cec214eSBenoît Thébaudeau 					cpu_to_hc32(QT_NEXT_TERMINATE);
4175cec214eSBenoît Thébaudeau 			token = QT_TOKEN_DT(toggle) |
4185cec214eSBenoît Thébaudeau 				QT_TOKEN_TOTALBYTES(xfr_bytes) |
41914eb79b7SBenoît Thébaudeau 				QT_TOKEN_IOC(req == NULL) | QT_TOKEN_CPAGE(0) |
4205cec214eSBenoît Thébaudeau 				QT_TOKEN_CERR(3) |
4215cec214eSBenoît Thébaudeau 				QT_TOKEN_PID(usb_pipein(pipe) ?
42214eb79b7SBenoît Thébaudeau 					QT_TOKEN_PID_IN : QT_TOKEN_PID_OUT) |
42314eb79b7SBenoît Thébaudeau 				QT_TOKEN_STATUS(QT_TOKEN_STATUS_ACTIVE);
424de98e8b2SMarek Vasut 			qtd[qtd_counter].qt_token = cpu_to_hc32(token);
4255cec214eSBenoît Thébaudeau 			if (ehci_td_buffer(&qtd[qtd_counter], buf_ptr,
4265cec214eSBenoît Thébaudeau 						xfr_bytes)) {
42714eb79b7SBenoît Thébaudeau 				printf("unable to construct DATA TD\n");
4282731b9a8SJean-Christophe PLAGNIOL-VILLARD 				goto fail;
4292731b9a8SJean-Christophe PLAGNIOL-VILLARD 			}
43041b1f0acSMarek Vasut 			/* Update previous qTD! */
431de98e8b2SMarek Vasut 			*tdp = cpu_to_hc32((uint32_t)&qtd[qtd_counter]);
432de98e8b2SMarek Vasut 			tdp = &qtd[qtd_counter++].qt_next;
433db191346SBenoît Thébaudeau 			/*
434db191346SBenoît Thébaudeau 			 * Data toggle has to be adjusted since the qTD transfer
435db191346SBenoît Thébaudeau 			 * size is not always an even multiple of
436db191346SBenoît Thébaudeau 			 * wMaxPacketSize.
437db191346SBenoît Thébaudeau 			 */
438db191346SBenoît Thébaudeau 			if ((xfr_bytes / maxpacket) & 1)
439db191346SBenoît Thébaudeau 				toggle ^= 1;
4405cec214eSBenoît Thébaudeau 			buf_ptr += xfr_bytes;
4415cec214eSBenoît Thébaudeau 			left_length -= xfr_bytes;
4425cec214eSBenoît Thébaudeau 		} while (left_length > 0);
4432731b9a8SJean-Christophe PLAGNIOL-VILLARD 	}
4442731b9a8SJean-Christophe PLAGNIOL-VILLARD 
4452731b9a8SJean-Christophe PLAGNIOL-VILLARD 	if (req != NULL) {
44641b1f0acSMarek Vasut 		/*
44741b1f0acSMarek Vasut 		 * Setup request qTD (3.5 in ehci-r10.pdf)
44841b1f0acSMarek Vasut 		 *
44941b1f0acSMarek Vasut 		 *   qt_next ................ 03-00 H
45041b1f0acSMarek Vasut 		 *   qt_altnext ............. 07-04 H
45141b1f0acSMarek Vasut 		 *   qt_token ............... 0B-08 H
45241b1f0acSMarek Vasut 		 */
453de98e8b2SMarek Vasut 		qtd[qtd_counter].qt_next = cpu_to_hc32(QT_NEXT_TERMINATE);
454de98e8b2SMarek Vasut 		qtd[qtd_counter].qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE);
455db191346SBenoît Thébaudeau 		token = QT_TOKEN_DT(1) | QT_TOKEN_TOTALBYTES(0) |
45614eb79b7SBenoît Thébaudeau 			QT_TOKEN_IOC(1) | QT_TOKEN_CPAGE(0) | QT_TOKEN_CERR(3) |
45714eb79b7SBenoît Thébaudeau 			QT_TOKEN_PID(usb_pipein(pipe) ?
45814eb79b7SBenoît Thébaudeau 				QT_TOKEN_PID_OUT : QT_TOKEN_PID_IN) |
45914eb79b7SBenoît Thébaudeau 			QT_TOKEN_STATUS(QT_TOKEN_STATUS_ACTIVE);
460de98e8b2SMarek Vasut 		qtd[qtd_counter].qt_token = cpu_to_hc32(token);
46141b1f0acSMarek Vasut 		/* Update previous qTD! */
462de98e8b2SMarek Vasut 		*tdp = cpu_to_hc32((uint32_t)&qtd[qtd_counter]);
463de98e8b2SMarek Vasut 		tdp = &qtd[qtd_counter++].qt_next;
4642731b9a8SJean-Christophe PLAGNIOL-VILLARD 	}
4652731b9a8SJean-Christophe PLAGNIOL-VILLARD 
466676ae068SLucas Stach 	ctrl->qh_list.qh_link = cpu_to_hc32((uint32_t)qh | QH_LINK_TYPE_QH);
4672731b9a8SJean-Christophe PLAGNIOL-VILLARD 
4682731b9a8SJean-Christophe PLAGNIOL-VILLARD 	/* Flush dcache */
469676ae068SLucas Stach 	flush_dcache_range((uint32_t)&ctrl->qh_list,
470676ae068SLucas Stach 		ALIGN_END_ADDR(struct QH, &ctrl->qh_list, 1));
47171c5de4fSTom Rini 	flush_dcache_range((uint32_t)qh, ALIGN_END_ADDR(struct QH, qh, 1));
47214eb79b7SBenoît Thébaudeau 	flush_dcache_range((uint32_t)qtd,
4735cec214eSBenoît Thébaudeau 			   ALIGN_END_ADDR(struct qTD, qtd, qtd_count));
4742731b9a8SJean-Christophe PLAGNIOL-VILLARD 
475c7701af5SIlya Yanok 	/* Set async. queue head pointer. */
476676ae068SLucas Stach 	ehci_writel(&ctrl->hcor->or_asynclistaddr, (uint32_t)&ctrl->qh_list);
477c7701af5SIlya Yanok 
478676ae068SLucas Stach 	usbsts = ehci_readl(&ctrl->hcor->or_usbsts);
479676ae068SLucas Stach 	ehci_writel(&ctrl->hcor->or_usbsts, (usbsts & 0x3f));
4802731b9a8SJean-Christophe PLAGNIOL-VILLARD 
4812731b9a8SJean-Christophe PLAGNIOL-VILLARD 	/* Enable async. schedule. */
482676ae068SLucas Stach 	cmd = ehci_readl(&ctrl->hcor->or_usbcmd);
4832731b9a8SJean-Christophe PLAGNIOL-VILLARD 	cmd |= CMD_ASE;
484676ae068SLucas Stach 	ehci_writel(&ctrl->hcor->or_usbcmd, cmd);
4852731b9a8SJean-Christophe PLAGNIOL-VILLARD 
486676ae068SLucas Stach 	ret = handshake((uint32_t *)&ctrl->hcor->or_usbsts, STS_ASS, STS_ASS,
4872731b9a8SJean-Christophe PLAGNIOL-VILLARD 			100 * 1000);
4882731b9a8SJean-Christophe PLAGNIOL-VILLARD 	if (ret < 0) {
48914eb79b7SBenoît Thébaudeau 		printf("EHCI fail timeout STS_ASS set\n");
4902731b9a8SJean-Christophe PLAGNIOL-VILLARD 		goto fail;
4912731b9a8SJean-Christophe PLAGNIOL-VILLARD 	}
4922731b9a8SJean-Christophe PLAGNIOL-VILLARD 
4932731b9a8SJean-Christophe PLAGNIOL-VILLARD 	/* Wait for TDs to be processed. */
4942731b9a8SJean-Christophe PLAGNIOL-VILLARD 	ts = get_timer(0);
495de98e8b2SMarek Vasut 	vtd = &qtd[qtd_counter - 1];
49696820a35SSimon Glass 	timeout = USB_TIMEOUT_MS(pipe);
4972731b9a8SJean-Christophe PLAGNIOL-VILLARD 	do {
4982731b9a8SJean-Christophe PLAGNIOL-VILLARD 		/* Invalidate dcache */
499676ae068SLucas Stach 		invalidate_dcache_range((uint32_t)&ctrl->qh_list,
500676ae068SLucas Stach 			ALIGN_END_ADDR(struct QH, &ctrl->qh_list, 1));
50171c5de4fSTom Rini 		invalidate_dcache_range((uint32_t)qh,
50271c5de4fSTom Rini 			ALIGN_END_ADDR(struct QH, qh, 1));
503b8adb120SMarek Vasut 		invalidate_dcache_range((uint32_t)qtd,
5045cec214eSBenoît Thébaudeau 			ALIGN_END_ADDR(struct qTD, qtd, qtd_count));
505b8adb120SMarek Vasut 
5062731b9a8SJean-Christophe PLAGNIOL-VILLARD 		token = hc32_to_cpu(vtd->qt_token);
50714eb79b7SBenoît Thébaudeau 		if (!(QT_TOKEN_GET_STATUS(token) & QT_TOKEN_STATUS_ACTIVE))
5082731b9a8SJean-Christophe PLAGNIOL-VILLARD 			break;
50967333f76SStefan Roese 		WATCHDOG_RESET();
51096820a35SSimon Glass 	} while (get_timer(ts) < timeout);
51196820a35SSimon Glass 
512189a6956SIlya Yanok 	/*
513189a6956SIlya Yanok 	 * Invalidate the memory area occupied by buffer
514189a6956SIlya Yanok 	 * Don't try to fix the buffer alignment, if it isn't properly
515189a6956SIlya Yanok 	 * aligned it's upper layer's fault so let invalidate_dcache_range()
516189a6956SIlya Yanok 	 * vow about it. But we have to fix the length as it's actual
517189a6956SIlya Yanok 	 * transfer length and can be unaligned. This is potentially
518189a6956SIlya Yanok 	 * dangerous operation, it's responsibility of the calling
519189a6956SIlya Yanok 	 * code to make sure enough space is reserved.
520189a6956SIlya Yanok 	 */
521189a6956SIlya Yanok 	invalidate_dcache_range((uint32_t)buffer,
522189a6956SIlya Yanok 		ALIGN((uint32_t)buffer + length, ARCH_DMA_MINALIGN));
523b8adb120SMarek Vasut 
52496820a35SSimon Glass 	/* Check that the TD processing happened */
52514eb79b7SBenoît Thébaudeau 	if (QT_TOKEN_GET_STATUS(token) & QT_TOKEN_STATUS_ACTIVE)
52696820a35SSimon Glass 		printf("EHCI timed out on TD - token=%#x\n", token);
5272731b9a8SJean-Christophe PLAGNIOL-VILLARD 
5282731b9a8SJean-Christophe PLAGNIOL-VILLARD 	/* Disable async schedule. */
529676ae068SLucas Stach 	cmd = ehci_readl(&ctrl->hcor->or_usbcmd);
5302731b9a8SJean-Christophe PLAGNIOL-VILLARD 	cmd &= ~CMD_ASE;
531676ae068SLucas Stach 	ehci_writel(&ctrl->hcor->or_usbcmd, cmd);
5322731b9a8SJean-Christophe PLAGNIOL-VILLARD 
533676ae068SLucas Stach 	ret = handshake((uint32_t *)&ctrl->hcor->or_usbsts, STS_ASS, 0,
5342731b9a8SJean-Christophe PLAGNIOL-VILLARD 			100 * 1000);
5352731b9a8SJean-Christophe PLAGNIOL-VILLARD 	if (ret < 0) {
53614eb79b7SBenoît Thébaudeau 		printf("EHCI fail timeout STS_ASS reset\n");
5372731b9a8SJean-Christophe PLAGNIOL-VILLARD 		goto fail;
5382731b9a8SJean-Christophe PLAGNIOL-VILLARD 	}
5392731b9a8SJean-Christophe PLAGNIOL-VILLARD 
54071c5de4fSTom Rini 	token = hc32_to_cpu(qh->qh_overlay.qt_token);
54114eb79b7SBenoît Thébaudeau 	if (!(QT_TOKEN_GET_STATUS(token) & QT_TOKEN_STATUS_ACTIVE)) {
5422731b9a8SJean-Christophe PLAGNIOL-VILLARD 		debug("TOKEN=%#x\n", token);
54314eb79b7SBenoît Thébaudeau 		switch (QT_TOKEN_GET_STATUS(token) &
54414eb79b7SBenoît Thébaudeau 			~(QT_TOKEN_STATUS_SPLITXSTATE | QT_TOKEN_STATUS_PERR)) {
5452731b9a8SJean-Christophe PLAGNIOL-VILLARD 		case 0:
54614eb79b7SBenoît Thébaudeau 			toggle = QT_TOKEN_GET_DT(token);
5472731b9a8SJean-Christophe PLAGNIOL-VILLARD 			usb_settoggle(dev, usb_pipeendpoint(pipe),
5482731b9a8SJean-Christophe PLAGNIOL-VILLARD 				       usb_pipeout(pipe), toggle);
5492731b9a8SJean-Christophe PLAGNIOL-VILLARD 			dev->status = 0;
5502731b9a8SJean-Christophe PLAGNIOL-VILLARD 			break;
55114eb79b7SBenoît Thébaudeau 		case QT_TOKEN_STATUS_HALTED:
5522731b9a8SJean-Christophe PLAGNIOL-VILLARD 			dev->status = USB_ST_STALLED;
5532731b9a8SJean-Christophe PLAGNIOL-VILLARD 			break;
55414eb79b7SBenoît Thébaudeau 		case QT_TOKEN_STATUS_ACTIVE | QT_TOKEN_STATUS_DATBUFERR:
55514eb79b7SBenoît Thébaudeau 		case QT_TOKEN_STATUS_DATBUFERR:
5562731b9a8SJean-Christophe PLAGNIOL-VILLARD 			dev->status = USB_ST_BUF_ERR;
5572731b9a8SJean-Christophe PLAGNIOL-VILLARD 			break;
55814eb79b7SBenoît Thébaudeau 		case QT_TOKEN_STATUS_HALTED | QT_TOKEN_STATUS_BABBLEDET:
55914eb79b7SBenoît Thébaudeau 		case QT_TOKEN_STATUS_BABBLEDET:
5602731b9a8SJean-Christophe PLAGNIOL-VILLARD 			dev->status = USB_ST_BABBLE_DET;
5612731b9a8SJean-Christophe PLAGNIOL-VILLARD 			break;
5622731b9a8SJean-Christophe PLAGNIOL-VILLARD 		default:
5632731b9a8SJean-Christophe PLAGNIOL-VILLARD 			dev->status = USB_ST_CRC_ERR;
56414eb79b7SBenoît Thébaudeau 			if (QT_TOKEN_GET_STATUS(token) & QT_TOKEN_STATUS_HALTED)
565222d6dffSAnatolij Gustschin 				dev->status |= USB_ST_STALLED;
5662731b9a8SJean-Christophe PLAGNIOL-VILLARD 			break;
5672731b9a8SJean-Christophe PLAGNIOL-VILLARD 		}
56814eb79b7SBenoît Thébaudeau 		dev->act_len = length - QT_TOKEN_GET_TOTALBYTES(token);
5692731b9a8SJean-Christophe PLAGNIOL-VILLARD 	} else {
5702731b9a8SJean-Christophe PLAGNIOL-VILLARD 		dev->act_len = 0;
5712731b9a8SJean-Christophe PLAGNIOL-VILLARD 		debug("dev=%u, usbsts=%#x, p[1]=%#x, p[2]=%#x\n",
572676ae068SLucas Stach 		      dev->devnum, ehci_readl(&ctrl->hcor->or_usbsts),
573676ae068SLucas Stach 		      ehci_readl(&ctrl->hcor->or_portsc[0]),
574676ae068SLucas Stach 		      ehci_readl(&ctrl->hcor->or_portsc[1]));
5752731b9a8SJean-Christophe PLAGNIOL-VILLARD 	}
5762731b9a8SJean-Christophe PLAGNIOL-VILLARD 
5775cec214eSBenoît Thébaudeau 	free(qtd);
5782731b9a8SJean-Christophe PLAGNIOL-VILLARD 	return (dev->status != USB_ST_NOT_PROC) ? 0 : -1;
5792731b9a8SJean-Christophe PLAGNIOL-VILLARD 
5802731b9a8SJean-Christophe PLAGNIOL-VILLARD fail:
5815cec214eSBenoît Thébaudeau 	free(qtd);
5822731b9a8SJean-Christophe PLAGNIOL-VILLARD 	return -1;
5832731b9a8SJean-Christophe PLAGNIOL-VILLARD }
5842731b9a8SJean-Christophe PLAGNIOL-VILLARD 
5852731b9a8SJean-Christophe PLAGNIOL-VILLARD static inline int min3(int a, int b, int c)
5862731b9a8SJean-Christophe PLAGNIOL-VILLARD {
5872731b9a8SJean-Christophe PLAGNIOL-VILLARD 
5882731b9a8SJean-Christophe PLAGNIOL-VILLARD 	if (b < a)
5892731b9a8SJean-Christophe PLAGNIOL-VILLARD 		a = b;
5902731b9a8SJean-Christophe PLAGNIOL-VILLARD 	if (c < a)
5912731b9a8SJean-Christophe PLAGNIOL-VILLARD 		a = c;
5922731b9a8SJean-Christophe PLAGNIOL-VILLARD 	return a;
5932731b9a8SJean-Christophe PLAGNIOL-VILLARD }
5942731b9a8SJean-Christophe PLAGNIOL-VILLARD 
5952731b9a8SJean-Christophe PLAGNIOL-VILLARD int
5962731b9a8SJean-Christophe PLAGNIOL-VILLARD ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer,
5972731b9a8SJean-Christophe PLAGNIOL-VILLARD 		 int length, struct devrequest *req)
5982731b9a8SJean-Christophe PLAGNIOL-VILLARD {
5992731b9a8SJean-Christophe PLAGNIOL-VILLARD 	uint8_t tmpbuf[4];
6002731b9a8SJean-Christophe PLAGNIOL-VILLARD 	u16 typeReq;
6012731b9a8SJean-Christophe PLAGNIOL-VILLARD 	void *srcptr = NULL;
6022731b9a8SJean-Christophe PLAGNIOL-VILLARD 	int len, srclen;
6032731b9a8SJean-Christophe PLAGNIOL-VILLARD 	uint32_t reg;
6042731b9a8SJean-Christophe PLAGNIOL-VILLARD 	uint32_t *status_reg;
605676ae068SLucas Stach 	struct ehci_ctrl *ctrl = dev->controller;
6062731b9a8SJean-Christophe PLAGNIOL-VILLARD 
607e06a055bSSergei Shtylyov 	if (le16_to_cpu(req->index) > CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS) {
6082731b9a8SJean-Christophe PLAGNIOL-VILLARD 		printf("The request port(%d) is not configured\n",
6092731b9a8SJean-Christophe PLAGNIOL-VILLARD 			le16_to_cpu(req->index) - 1);
6102731b9a8SJean-Christophe PLAGNIOL-VILLARD 		return -1;
6112731b9a8SJean-Christophe PLAGNIOL-VILLARD 	}
612676ae068SLucas Stach 	status_reg = (uint32_t *)&ctrl->hcor->or_portsc[
6132731b9a8SJean-Christophe PLAGNIOL-VILLARD 						le16_to_cpu(req->index) - 1];
6142731b9a8SJean-Christophe PLAGNIOL-VILLARD 	srclen = 0;
6152731b9a8SJean-Christophe PLAGNIOL-VILLARD 
6162731b9a8SJean-Christophe PLAGNIOL-VILLARD 	debug("req=%u (%#x), type=%u (%#x), value=%u, index=%u\n",
6172731b9a8SJean-Christophe PLAGNIOL-VILLARD 	      req->request, req->request,
6182731b9a8SJean-Christophe PLAGNIOL-VILLARD 	      req->requesttype, req->requesttype,
6192731b9a8SJean-Christophe PLAGNIOL-VILLARD 	      le16_to_cpu(req->value), le16_to_cpu(req->index));
6202731b9a8SJean-Christophe PLAGNIOL-VILLARD 
62144259bb9SPrafulla Wadaskar 	typeReq = req->request | req->requesttype << 8;
6222731b9a8SJean-Christophe PLAGNIOL-VILLARD 
62344259bb9SPrafulla Wadaskar 	switch (typeReq) {
6242731b9a8SJean-Christophe PLAGNIOL-VILLARD 	case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
6252731b9a8SJean-Christophe PLAGNIOL-VILLARD 		switch (le16_to_cpu(req->value) >> 8) {
6262731b9a8SJean-Christophe PLAGNIOL-VILLARD 		case USB_DT_DEVICE:
6272731b9a8SJean-Christophe PLAGNIOL-VILLARD 			debug("USB_DT_DEVICE request\n");
6282731b9a8SJean-Christophe PLAGNIOL-VILLARD 			srcptr = &descriptor.device;
62914eb79b7SBenoît Thébaudeau 			srclen = descriptor.device.bLength;
6302731b9a8SJean-Christophe PLAGNIOL-VILLARD 			break;
6312731b9a8SJean-Christophe PLAGNIOL-VILLARD 		case USB_DT_CONFIG:
6322731b9a8SJean-Christophe PLAGNIOL-VILLARD 			debug("USB_DT_CONFIG config\n");
6332731b9a8SJean-Christophe PLAGNIOL-VILLARD 			srcptr = &descriptor.config;
63414eb79b7SBenoît Thébaudeau 			srclen = descriptor.config.bLength +
63514eb79b7SBenoît Thébaudeau 					descriptor.interface.bLength +
63614eb79b7SBenoît Thébaudeau 					descriptor.endpoint.bLength;
6372731b9a8SJean-Christophe PLAGNIOL-VILLARD 			break;
6382731b9a8SJean-Christophe PLAGNIOL-VILLARD 		case USB_DT_STRING:
6392731b9a8SJean-Christophe PLAGNIOL-VILLARD 			debug("USB_DT_STRING config\n");
6402731b9a8SJean-Christophe PLAGNIOL-VILLARD 			switch (le16_to_cpu(req->value) & 0xff) {
6412731b9a8SJean-Christophe PLAGNIOL-VILLARD 			case 0:	/* Language */
6422731b9a8SJean-Christophe PLAGNIOL-VILLARD 				srcptr = "\4\3\1\0";
6432731b9a8SJean-Christophe PLAGNIOL-VILLARD 				srclen = 4;
6442731b9a8SJean-Christophe PLAGNIOL-VILLARD 				break;
6452731b9a8SJean-Christophe PLAGNIOL-VILLARD 			case 1:	/* Vendor */
6462731b9a8SJean-Christophe PLAGNIOL-VILLARD 				srcptr = "\16\3u\0-\0b\0o\0o\0t\0";
6472731b9a8SJean-Christophe PLAGNIOL-VILLARD 				srclen = 14;
6482731b9a8SJean-Christophe PLAGNIOL-VILLARD 				break;
6492731b9a8SJean-Christophe PLAGNIOL-VILLARD 			case 2:	/* Product */
6502731b9a8SJean-Christophe PLAGNIOL-VILLARD 				srcptr = "\52\3E\0H\0C\0I\0 "
6512731b9a8SJean-Christophe PLAGNIOL-VILLARD 					 "\0H\0o\0s\0t\0 "
6522731b9a8SJean-Christophe PLAGNIOL-VILLARD 					 "\0C\0o\0n\0t\0r\0o\0l\0l\0e\0r\0";
6532731b9a8SJean-Christophe PLAGNIOL-VILLARD 				srclen = 42;
6542731b9a8SJean-Christophe PLAGNIOL-VILLARD 				break;
6552731b9a8SJean-Christophe PLAGNIOL-VILLARD 			default:
6562731b9a8SJean-Christophe PLAGNIOL-VILLARD 				debug("unknown value DT_STRING %x\n",
6572731b9a8SJean-Christophe PLAGNIOL-VILLARD 					le16_to_cpu(req->value));
6582731b9a8SJean-Christophe PLAGNIOL-VILLARD 				goto unknown;
6592731b9a8SJean-Christophe PLAGNIOL-VILLARD 			}
6602731b9a8SJean-Christophe PLAGNIOL-VILLARD 			break;
6612731b9a8SJean-Christophe PLAGNIOL-VILLARD 		default:
6622731b9a8SJean-Christophe PLAGNIOL-VILLARD 			debug("unknown value %x\n", le16_to_cpu(req->value));
6632731b9a8SJean-Christophe PLAGNIOL-VILLARD 			goto unknown;
6642731b9a8SJean-Christophe PLAGNIOL-VILLARD 		}
6652731b9a8SJean-Christophe PLAGNIOL-VILLARD 		break;
6662731b9a8SJean-Christophe PLAGNIOL-VILLARD 	case USB_REQ_GET_DESCRIPTOR | ((USB_DIR_IN | USB_RT_HUB) << 8):
6672731b9a8SJean-Christophe PLAGNIOL-VILLARD 		switch (le16_to_cpu(req->value) >> 8) {
6682731b9a8SJean-Christophe PLAGNIOL-VILLARD 		case USB_DT_HUB:
6692731b9a8SJean-Christophe PLAGNIOL-VILLARD 			debug("USB_DT_HUB config\n");
6702731b9a8SJean-Christophe PLAGNIOL-VILLARD 			srcptr = &descriptor.hub;
67114eb79b7SBenoît Thébaudeau 			srclen = descriptor.hub.bLength;
6722731b9a8SJean-Christophe PLAGNIOL-VILLARD 			break;
6732731b9a8SJean-Christophe PLAGNIOL-VILLARD 		default:
6742731b9a8SJean-Christophe PLAGNIOL-VILLARD 			debug("unknown value %x\n", le16_to_cpu(req->value));
6752731b9a8SJean-Christophe PLAGNIOL-VILLARD 			goto unknown;
6762731b9a8SJean-Christophe PLAGNIOL-VILLARD 		}
6772731b9a8SJean-Christophe PLAGNIOL-VILLARD 		break;
6782731b9a8SJean-Christophe PLAGNIOL-VILLARD 	case USB_REQ_SET_ADDRESS | (USB_RECIP_DEVICE << 8):
6792731b9a8SJean-Christophe PLAGNIOL-VILLARD 		debug("USB_REQ_SET_ADDRESS\n");
680676ae068SLucas Stach 		ctrl->rootdev = le16_to_cpu(req->value);
6812731b9a8SJean-Christophe PLAGNIOL-VILLARD 		break;
6822731b9a8SJean-Christophe PLAGNIOL-VILLARD 	case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
6832731b9a8SJean-Christophe PLAGNIOL-VILLARD 		debug("USB_REQ_SET_CONFIGURATION\n");
6842731b9a8SJean-Christophe PLAGNIOL-VILLARD 		/* Nothing to do */
6852731b9a8SJean-Christophe PLAGNIOL-VILLARD 		break;
6862731b9a8SJean-Christophe PLAGNIOL-VILLARD 	case USB_REQ_GET_STATUS | ((USB_DIR_IN | USB_RT_HUB) << 8):
6872731b9a8SJean-Christophe PLAGNIOL-VILLARD 		tmpbuf[0] = 1;	/* USB_STATUS_SELFPOWERED */
6882731b9a8SJean-Christophe PLAGNIOL-VILLARD 		tmpbuf[1] = 0;
6892731b9a8SJean-Christophe PLAGNIOL-VILLARD 		srcptr = tmpbuf;
6902731b9a8SJean-Christophe PLAGNIOL-VILLARD 		srclen = 2;
6912731b9a8SJean-Christophe PLAGNIOL-VILLARD 		break;
6922731b9a8SJean-Christophe PLAGNIOL-VILLARD 	case USB_REQ_GET_STATUS | ((USB_RT_PORT | USB_DIR_IN) << 8):
6932731b9a8SJean-Christophe PLAGNIOL-VILLARD 		memset(tmpbuf, 0, 4);
6942731b9a8SJean-Christophe PLAGNIOL-VILLARD 		reg = ehci_readl(status_reg);
6952731b9a8SJean-Christophe PLAGNIOL-VILLARD 		if (reg & EHCI_PS_CS)
6962731b9a8SJean-Christophe PLAGNIOL-VILLARD 			tmpbuf[0] |= USB_PORT_STAT_CONNECTION;
6972731b9a8SJean-Christophe PLAGNIOL-VILLARD 		if (reg & EHCI_PS_PE)
6982731b9a8SJean-Christophe PLAGNIOL-VILLARD 			tmpbuf[0] |= USB_PORT_STAT_ENABLE;
6992731b9a8SJean-Christophe PLAGNIOL-VILLARD 		if (reg & EHCI_PS_SUSP)
7002731b9a8SJean-Christophe PLAGNIOL-VILLARD 			tmpbuf[0] |= USB_PORT_STAT_SUSPEND;
7012731b9a8SJean-Christophe PLAGNIOL-VILLARD 		if (reg & EHCI_PS_OCA)
7022731b9a8SJean-Christophe PLAGNIOL-VILLARD 			tmpbuf[0] |= USB_PORT_STAT_OVERCURRENT;
703c8b2d1dcSSergei Shtylyov 		if (reg & EHCI_PS_PR)
7042731b9a8SJean-Christophe PLAGNIOL-VILLARD 			tmpbuf[0] |= USB_PORT_STAT_RESET;
7052731b9a8SJean-Christophe PLAGNIOL-VILLARD 		if (reg & EHCI_PS_PP)
7062731b9a8SJean-Christophe PLAGNIOL-VILLARD 			tmpbuf[1] |= USB_PORT_STAT_POWER >> 8;
7072731b9a8SJean-Christophe PLAGNIOL-VILLARD 
7082731b9a8SJean-Christophe PLAGNIOL-VILLARD 		if (ehci_is_TDI()) {
70914eb79b7SBenoît Thébaudeau 			switch (PORTSC_PSPD(reg)) {
71014eb79b7SBenoît Thébaudeau 			case PORTSC_PSPD_FS:
7112731b9a8SJean-Christophe PLAGNIOL-VILLARD 				break;
71214eb79b7SBenoît Thébaudeau 			case PORTSC_PSPD_LS:
7132731b9a8SJean-Christophe PLAGNIOL-VILLARD 				tmpbuf[1] |= USB_PORT_STAT_LOW_SPEED >> 8;
7142731b9a8SJean-Christophe PLAGNIOL-VILLARD 				break;
71514eb79b7SBenoît Thébaudeau 			case PORTSC_PSPD_HS:
7162731b9a8SJean-Christophe PLAGNIOL-VILLARD 			default:
7172731b9a8SJean-Christophe PLAGNIOL-VILLARD 				tmpbuf[1] |= USB_PORT_STAT_HIGH_SPEED >> 8;
7182731b9a8SJean-Christophe PLAGNIOL-VILLARD 				break;
7192731b9a8SJean-Christophe PLAGNIOL-VILLARD 			}
7202731b9a8SJean-Christophe PLAGNIOL-VILLARD 		} else {
7212731b9a8SJean-Christophe PLAGNIOL-VILLARD 			tmpbuf[1] |= USB_PORT_STAT_HIGH_SPEED >> 8;
7222731b9a8SJean-Christophe PLAGNIOL-VILLARD 		}
7232731b9a8SJean-Christophe PLAGNIOL-VILLARD 
7242731b9a8SJean-Christophe PLAGNIOL-VILLARD 		if (reg & EHCI_PS_CSC)
7252731b9a8SJean-Christophe PLAGNIOL-VILLARD 			tmpbuf[2] |= USB_PORT_STAT_C_CONNECTION;
7262731b9a8SJean-Christophe PLAGNIOL-VILLARD 		if (reg & EHCI_PS_PEC)
7272731b9a8SJean-Christophe PLAGNIOL-VILLARD 			tmpbuf[2] |= USB_PORT_STAT_C_ENABLE;
7282731b9a8SJean-Christophe PLAGNIOL-VILLARD 		if (reg & EHCI_PS_OCC)
7292731b9a8SJean-Christophe PLAGNIOL-VILLARD 			tmpbuf[2] |= USB_PORT_STAT_C_OVERCURRENT;
730676ae068SLucas Stach 		if (ctrl->portreset & (1 << le16_to_cpu(req->index)))
7312731b9a8SJean-Christophe PLAGNIOL-VILLARD 			tmpbuf[2] |= USB_PORT_STAT_C_RESET;
7322731b9a8SJean-Christophe PLAGNIOL-VILLARD 
7332731b9a8SJean-Christophe PLAGNIOL-VILLARD 		srcptr = tmpbuf;
7342731b9a8SJean-Christophe PLAGNIOL-VILLARD 		srclen = 4;
7352731b9a8SJean-Christophe PLAGNIOL-VILLARD 		break;
7362731b9a8SJean-Christophe PLAGNIOL-VILLARD 	case USB_REQ_SET_FEATURE | ((USB_DIR_OUT | USB_RT_PORT) << 8):
7372731b9a8SJean-Christophe PLAGNIOL-VILLARD 		reg = ehci_readl(status_reg);
7382731b9a8SJean-Christophe PLAGNIOL-VILLARD 		reg &= ~EHCI_PS_CLEAR;
7392731b9a8SJean-Christophe PLAGNIOL-VILLARD 		switch (le16_to_cpu(req->value)) {
7402731b9a8SJean-Christophe PLAGNIOL-VILLARD 		case USB_PORT_FEAT_ENABLE:
7412731b9a8SJean-Christophe PLAGNIOL-VILLARD 			reg |= EHCI_PS_PE;
7422731b9a8SJean-Christophe PLAGNIOL-VILLARD 			ehci_writel(status_reg, reg);
7432731b9a8SJean-Christophe PLAGNIOL-VILLARD 			break;
7442731b9a8SJean-Christophe PLAGNIOL-VILLARD 		case USB_PORT_FEAT_POWER:
745676ae068SLucas Stach 			if (HCS_PPC(ehci_readl(&ctrl->hccr->cr_hcsparams))) {
7462731b9a8SJean-Christophe PLAGNIOL-VILLARD 				reg |= EHCI_PS_PP;
7472731b9a8SJean-Christophe PLAGNIOL-VILLARD 				ehci_writel(status_reg, reg);
7482731b9a8SJean-Christophe PLAGNIOL-VILLARD 			}
7492731b9a8SJean-Christophe PLAGNIOL-VILLARD 			break;
7502731b9a8SJean-Christophe PLAGNIOL-VILLARD 		case USB_PORT_FEAT_RESET:
7512731b9a8SJean-Christophe PLAGNIOL-VILLARD 			if ((reg & (EHCI_PS_PE | EHCI_PS_CS)) == EHCI_PS_CS &&
7522731b9a8SJean-Christophe PLAGNIOL-VILLARD 			    !ehci_is_TDI() &&
7532731b9a8SJean-Christophe PLAGNIOL-VILLARD 			    EHCI_PS_IS_LOWSPEED(reg)) {
7542731b9a8SJean-Christophe PLAGNIOL-VILLARD 				/* Low speed device, give up ownership. */
7552731b9a8SJean-Christophe PLAGNIOL-VILLARD 				debug("port %d low speed --> companion\n",
7562731b9a8SJean-Christophe PLAGNIOL-VILLARD 				      req->index - 1);
7572731b9a8SJean-Christophe PLAGNIOL-VILLARD 				reg |= EHCI_PS_PO;
7582731b9a8SJean-Christophe PLAGNIOL-VILLARD 				ehci_writel(status_reg, reg);
7592731b9a8SJean-Christophe PLAGNIOL-VILLARD 				break;
7602731b9a8SJean-Christophe PLAGNIOL-VILLARD 			} else {
761c8b2d1dcSSergei Shtylyov 				int ret;
762c8b2d1dcSSergei Shtylyov 
7632731b9a8SJean-Christophe PLAGNIOL-VILLARD 				reg |= EHCI_PS_PR;
7642731b9a8SJean-Christophe PLAGNIOL-VILLARD 				reg &= ~EHCI_PS_PE;
7652731b9a8SJean-Christophe PLAGNIOL-VILLARD 				ehci_writel(status_reg, reg);
7662731b9a8SJean-Christophe PLAGNIOL-VILLARD 				/*
7672731b9a8SJean-Christophe PLAGNIOL-VILLARD 				 * caller must wait, then call GetPortStatus
7682731b9a8SJean-Christophe PLAGNIOL-VILLARD 				 * usb 2.0 specification say 50 ms resets on
7692731b9a8SJean-Christophe PLAGNIOL-VILLARD 				 * root
7702731b9a8SJean-Christophe PLAGNIOL-VILLARD 				 */
7713874b6d6SMarek Vasut 				ehci_powerup_fixup(status_reg, &reg);
7723874b6d6SMarek Vasut 
773b416191aSChris Zhang 				ehci_writel(status_reg, reg & ~EHCI_PS_PR);
774c8b2d1dcSSergei Shtylyov 				/*
775c8b2d1dcSSergei Shtylyov 				 * A host controller must terminate the reset
776c8b2d1dcSSergei Shtylyov 				 * and stabilize the state of the port within
777c8b2d1dcSSergei Shtylyov 				 * 2 milliseconds
778c8b2d1dcSSergei Shtylyov 				 */
779c8b2d1dcSSergei Shtylyov 				ret = handshake(status_reg, EHCI_PS_PR, 0,
780c8b2d1dcSSergei Shtylyov 						2 * 1000);
781c8b2d1dcSSergei Shtylyov 				if (!ret)
782676ae068SLucas Stach 					ctrl->portreset |=
783c8b2d1dcSSergei Shtylyov 						1 << le16_to_cpu(req->index);
784c8b2d1dcSSergei Shtylyov 				else
785c8b2d1dcSSergei Shtylyov 					printf("port(%d) reset error\n",
786c8b2d1dcSSergei Shtylyov 					le16_to_cpu(req->index) - 1);
7872731b9a8SJean-Christophe PLAGNIOL-VILLARD 			}
7882731b9a8SJean-Christophe PLAGNIOL-VILLARD 			break;
7892731b9a8SJean-Christophe PLAGNIOL-VILLARD 		default:
7902731b9a8SJean-Christophe PLAGNIOL-VILLARD 			debug("unknown feature %x\n", le16_to_cpu(req->value));
7912731b9a8SJean-Christophe PLAGNIOL-VILLARD 			goto unknown;
7922731b9a8SJean-Christophe PLAGNIOL-VILLARD 		}
7932731b9a8SJean-Christophe PLAGNIOL-VILLARD 		/* unblock posted writes */
794676ae068SLucas Stach 		(void) ehci_readl(&ctrl->hcor->or_usbcmd);
7952731b9a8SJean-Christophe PLAGNIOL-VILLARD 		break;
7962731b9a8SJean-Christophe PLAGNIOL-VILLARD 	case USB_REQ_CLEAR_FEATURE | ((USB_DIR_OUT | USB_RT_PORT) << 8):
7972731b9a8SJean-Christophe PLAGNIOL-VILLARD 		reg = ehci_readl(status_reg);
7982731b9a8SJean-Christophe PLAGNIOL-VILLARD 		switch (le16_to_cpu(req->value)) {
7992731b9a8SJean-Christophe PLAGNIOL-VILLARD 		case USB_PORT_FEAT_ENABLE:
8002731b9a8SJean-Christophe PLAGNIOL-VILLARD 			reg &= ~EHCI_PS_PE;
8012731b9a8SJean-Christophe PLAGNIOL-VILLARD 			break;
8022731b9a8SJean-Christophe PLAGNIOL-VILLARD 		case USB_PORT_FEAT_C_ENABLE:
8032731b9a8SJean-Christophe PLAGNIOL-VILLARD 			reg = (reg & ~EHCI_PS_CLEAR) | EHCI_PS_PE;
8042731b9a8SJean-Christophe PLAGNIOL-VILLARD 			break;
8052731b9a8SJean-Christophe PLAGNIOL-VILLARD 		case USB_PORT_FEAT_POWER:
806676ae068SLucas Stach 			if (HCS_PPC(ehci_readl(&ctrl->hccr->cr_hcsparams)))
8072731b9a8SJean-Christophe PLAGNIOL-VILLARD 				reg = reg & ~(EHCI_PS_CLEAR | EHCI_PS_PP);
8082731b9a8SJean-Christophe PLAGNIOL-VILLARD 		case USB_PORT_FEAT_C_CONNECTION:
8092731b9a8SJean-Christophe PLAGNIOL-VILLARD 			reg = (reg & ~EHCI_PS_CLEAR) | EHCI_PS_CSC;
8102731b9a8SJean-Christophe PLAGNIOL-VILLARD 			break;
8112731b9a8SJean-Christophe PLAGNIOL-VILLARD 		case USB_PORT_FEAT_OVER_CURRENT:
8122731b9a8SJean-Christophe PLAGNIOL-VILLARD 			reg = (reg & ~EHCI_PS_CLEAR) | EHCI_PS_OCC;
8132731b9a8SJean-Christophe PLAGNIOL-VILLARD 			break;
8142731b9a8SJean-Christophe PLAGNIOL-VILLARD 		case USB_PORT_FEAT_C_RESET:
815676ae068SLucas Stach 			ctrl->portreset &= ~(1 << le16_to_cpu(req->index));
8162731b9a8SJean-Christophe PLAGNIOL-VILLARD 			break;
8172731b9a8SJean-Christophe PLAGNIOL-VILLARD 		default:
8182731b9a8SJean-Christophe PLAGNIOL-VILLARD 			debug("unknown feature %x\n", le16_to_cpu(req->value));
8192731b9a8SJean-Christophe PLAGNIOL-VILLARD 			goto unknown;
8202731b9a8SJean-Christophe PLAGNIOL-VILLARD 		}
8212731b9a8SJean-Christophe PLAGNIOL-VILLARD 		ehci_writel(status_reg, reg);
8222731b9a8SJean-Christophe PLAGNIOL-VILLARD 		/* unblock posted write */
823676ae068SLucas Stach 		(void) ehci_readl(&ctrl->hcor->or_usbcmd);
8242731b9a8SJean-Christophe PLAGNIOL-VILLARD 		break;
8252731b9a8SJean-Christophe PLAGNIOL-VILLARD 	default:
8262731b9a8SJean-Christophe PLAGNIOL-VILLARD 		debug("Unknown request\n");
8272731b9a8SJean-Christophe PLAGNIOL-VILLARD 		goto unknown;
8282731b9a8SJean-Christophe PLAGNIOL-VILLARD 	}
8292731b9a8SJean-Christophe PLAGNIOL-VILLARD 
8305b84dd67SMike Frysinger 	mdelay(1);
8312731b9a8SJean-Christophe PLAGNIOL-VILLARD 	len = min3(srclen, le16_to_cpu(req->length), length);
8322731b9a8SJean-Christophe PLAGNIOL-VILLARD 	if (srcptr != NULL && len > 0)
8332731b9a8SJean-Christophe PLAGNIOL-VILLARD 		memcpy(buffer, srcptr, len);
8342731b9a8SJean-Christophe PLAGNIOL-VILLARD 	else
8352731b9a8SJean-Christophe PLAGNIOL-VILLARD 		debug("Len is 0\n");
8362731b9a8SJean-Christophe PLAGNIOL-VILLARD 
8372731b9a8SJean-Christophe PLAGNIOL-VILLARD 	dev->act_len = len;
8382731b9a8SJean-Christophe PLAGNIOL-VILLARD 	dev->status = 0;
8392731b9a8SJean-Christophe PLAGNIOL-VILLARD 	return 0;
8402731b9a8SJean-Christophe PLAGNIOL-VILLARD 
8412731b9a8SJean-Christophe PLAGNIOL-VILLARD unknown:
8422731b9a8SJean-Christophe PLAGNIOL-VILLARD 	debug("requesttype=%x, request=%x, value=%x, index=%x, length=%x\n",
8432731b9a8SJean-Christophe PLAGNIOL-VILLARD 	      req->requesttype, req->request, le16_to_cpu(req->value),
8442731b9a8SJean-Christophe PLAGNIOL-VILLARD 	      le16_to_cpu(req->index), le16_to_cpu(req->length));
8452731b9a8SJean-Christophe PLAGNIOL-VILLARD 
8462731b9a8SJean-Christophe PLAGNIOL-VILLARD 	dev->act_len = 0;
8472731b9a8SJean-Christophe PLAGNIOL-VILLARD 	dev->status = USB_ST_STALLED;
8482731b9a8SJean-Christophe PLAGNIOL-VILLARD 	return -1;
8492731b9a8SJean-Christophe PLAGNIOL-VILLARD }
8502731b9a8SJean-Christophe PLAGNIOL-VILLARD 
851c7e3b2b5SLucas Stach int usb_lowlevel_stop(int index)
8522731b9a8SJean-Christophe PLAGNIOL-VILLARD {
853676ae068SLucas Stach 	return ehci_hcd_stop(index);
8542731b9a8SJean-Christophe PLAGNIOL-VILLARD }
8552731b9a8SJean-Christophe PLAGNIOL-VILLARD 
856c7e3b2b5SLucas Stach int usb_lowlevel_init(int index, void **controller)
8572731b9a8SJean-Christophe PLAGNIOL-VILLARD {
8582731b9a8SJean-Christophe PLAGNIOL-VILLARD 	uint32_t reg;
8592731b9a8SJean-Christophe PLAGNIOL-VILLARD 	uint32_t cmd;
860676ae068SLucas Stach 	struct QH *qh_list;
8612731b9a8SJean-Christophe PLAGNIOL-VILLARD 
862676ae068SLucas Stach 	if (ehci_hcd_init(index, &ehcic[index].hccr, &ehcic[index].hcor))
8632731b9a8SJean-Christophe PLAGNIOL-VILLARD 		return -1;
8642731b9a8SJean-Christophe PLAGNIOL-VILLARD 
8652731b9a8SJean-Christophe PLAGNIOL-VILLARD 	/* EHCI spec section 4.1 */
866676ae068SLucas Stach 	if (ehci_reset(index))
8672731b9a8SJean-Christophe PLAGNIOL-VILLARD 		return -1;
8682731b9a8SJean-Christophe PLAGNIOL-VILLARD 
8692731b9a8SJean-Christophe PLAGNIOL-VILLARD #if defined(CONFIG_EHCI_HCD_INIT_AFTER_RESET)
870676ae068SLucas Stach 	if (ehci_hcd_init(index, &ehcic[index].hccr, &ehcic[index].hcor))
8712731b9a8SJean-Christophe PLAGNIOL-VILLARD 		return -1;
8722731b9a8SJean-Christophe PLAGNIOL-VILLARD #endif
8732731b9a8SJean-Christophe PLAGNIOL-VILLARD 
874676ae068SLucas Stach 	qh_list = &ehcic[index].qh_list;
875676ae068SLucas Stach 
8762731b9a8SJean-Christophe PLAGNIOL-VILLARD 	/* Set head of reclaim list */
87771c5de4fSTom Rini 	memset(qh_list, 0, sizeof(*qh_list));
87871c5de4fSTom Rini 	qh_list->qh_link = cpu_to_hc32((uint32_t)qh_list | QH_LINK_TYPE_QH);
87914eb79b7SBenoît Thébaudeau 	qh_list->qh_endpt1 = cpu_to_hc32(QH_ENDPT1_H(1) |
88014eb79b7SBenoît Thébaudeau 						QH_ENDPT1_EPS(USB_SPEED_HIGH));
88171c5de4fSTom Rini 	qh_list->qh_curtd = cpu_to_hc32(QT_NEXT_TERMINATE);
88271c5de4fSTom Rini 	qh_list->qh_overlay.qt_next = cpu_to_hc32(QT_NEXT_TERMINATE);
88371c5de4fSTom Rini 	qh_list->qh_overlay.qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE);
88414eb79b7SBenoît Thébaudeau 	qh_list->qh_overlay.qt_token =
88514eb79b7SBenoît Thébaudeau 			cpu_to_hc32(QT_TOKEN_STATUS(QT_TOKEN_STATUS_HALTED));
8862731b9a8SJean-Christophe PLAGNIOL-VILLARD 
887676ae068SLucas Stach 	reg = ehci_readl(&ehcic[index].hccr->cr_hcsparams);
8882731b9a8SJean-Christophe PLAGNIOL-VILLARD 	descriptor.hub.bNbrPorts = HCS_N_PORTS(reg);
8897a46b2c7SLucas Stach 	debug("Register %x NbrPorts %d\n", reg, descriptor.hub.bNbrPorts);
8902731b9a8SJean-Christophe PLAGNIOL-VILLARD 	/* Port Indicators */
8912731b9a8SJean-Christophe PLAGNIOL-VILLARD 	if (HCS_INDICATOR(reg))
89293ad908cSLucas Stach 		put_unaligned(get_unaligned(&descriptor.hub.wHubCharacteristics)
89393ad908cSLucas Stach 				| 0x80, &descriptor.hub.wHubCharacteristics);
8942731b9a8SJean-Christophe PLAGNIOL-VILLARD 	/* Port Power Control */
8952731b9a8SJean-Christophe PLAGNIOL-VILLARD 	if (HCS_PPC(reg))
89693ad908cSLucas Stach 		put_unaligned(get_unaligned(&descriptor.hub.wHubCharacteristics)
89793ad908cSLucas Stach 				| 0x01, &descriptor.hub.wHubCharacteristics);
8982731b9a8SJean-Christophe PLAGNIOL-VILLARD 
8992731b9a8SJean-Christophe PLAGNIOL-VILLARD 	/* Start the host controller. */
900676ae068SLucas Stach 	cmd = ehci_readl(&ehcic[index].hcor->or_usbcmd);
9012731b9a8SJean-Christophe PLAGNIOL-VILLARD 	/*
9022731b9a8SJean-Christophe PLAGNIOL-VILLARD 	 * Philips, Intel, and maybe others need CMD_RUN before the
9032731b9a8SJean-Christophe PLAGNIOL-VILLARD 	 * root hub will detect new devices (why?); NEC doesn't
9042731b9a8SJean-Christophe PLAGNIOL-VILLARD 	 */
9052731b9a8SJean-Christophe PLAGNIOL-VILLARD 	cmd &= ~(CMD_LRESET|CMD_IAAD|CMD_PSE|CMD_ASE|CMD_RESET);
9062731b9a8SJean-Christophe PLAGNIOL-VILLARD 	cmd |= CMD_RUN;
907676ae068SLucas Stach 	ehci_writel(&ehcic[index].hcor->or_usbcmd, cmd);
9082731b9a8SJean-Christophe PLAGNIOL-VILLARD 
9092731b9a8SJean-Christophe PLAGNIOL-VILLARD 	/* take control over the ports */
910676ae068SLucas Stach 	cmd = ehci_readl(&ehcic[index].hcor->or_configflag);
9112731b9a8SJean-Christophe PLAGNIOL-VILLARD 	cmd |= FLAG_CF;
912676ae068SLucas Stach 	ehci_writel(&ehcic[index].hcor->or_configflag, cmd);
9132731b9a8SJean-Christophe PLAGNIOL-VILLARD 	/* unblock posted write */
914676ae068SLucas Stach 	cmd = ehci_readl(&ehcic[index].hcor->or_usbcmd);
9155b84dd67SMike Frysinger 	mdelay(5);
916676ae068SLucas Stach 	reg = HC_VERSION(ehci_readl(&ehcic[index].hccr->cr_capbase));
9172731b9a8SJean-Christophe PLAGNIOL-VILLARD 	printf("USB EHCI %x.%02x\n", reg >> 8, reg & 0xff);
9182731b9a8SJean-Christophe PLAGNIOL-VILLARD 
919676ae068SLucas Stach 	ehcic[index].rootdev = 0;
9202731b9a8SJean-Christophe PLAGNIOL-VILLARD 
921676ae068SLucas Stach 	*controller = &ehcic[index];
9222731b9a8SJean-Christophe PLAGNIOL-VILLARD 	return 0;
9232731b9a8SJean-Christophe PLAGNIOL-VILLARD }
9242731b9a8SJean-Christophe PLAGNIOL-VILLARD 
9252731b9a8SJean-Christophe PLAGNIOL-VILLARD int
9262731b9a8SJean-Christophe PLAGNIOL-VILLARD submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
9272731b9a8SJean-Christophe PLAGNIOL-VILLARD 		int length)
9282731b9a8SJean-Christophe PLAGNIOL-VILLARD {
9292731b9a8SJean-Christophe PLAGNIOL-VILLARD 
9302731b9a8SJean-Christophe PLAGNIOL-VILLARD 	if (usb_pipetype(pipe) != PIPE_BULK) {
9312731b9a8SJean-Christophe PLAGNIOL-VILLARD 		debug("non-bulk pipe (type=%lu)", usb_pipetype(pipe));
9322731b9a8SJean-Christophe PLAGNIOL-VILLARD 		return -1;
9332731b9a8SJean-Christophe PLAGNIOL-VILLARD 	}
9342731b9a8SJean-Christophe PLAGNIOL-VILLARD 	return ehci_submit_async(dev, pipe, buffer, length, NULL);
9352731b9a8SJean-Christophe PLAGNIOL-VILLARD }
9362731b9a8SJean-Christophe PLAGNIOL-VILLARD 
9372731b9a8SJean-Christophe PLAGNIOL-VILLARD int
9382731b9a8SJean-Christophe PLAGNIOL-VILLARD submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
9392731b9a8SJean-Christophe PLAGNIOL-VILLARD 		   int length, struct devrequest *setup)
9402731b9a8SJean-Christophe PLAGNIOL-VILLARD {
941676ae068SLucas Stach 	struct ehci_ctrl *ctrl = dev->controller;
9422731b9a8SJean-Christophe PLAGNIOL-VILLARD 
9432731b9a8SJean-Christophe PLAGNIOL-VILLARD 	if (usb_pipetype(pipe) != PIPE_CONTROL) {
9442731b9a8SJean-Christophe PLAGNIOL-VILLARD 		debug("non-control pipe (type=%lu)", usb_pipetype(pipe));
9452731b9a8SJean-Christophe PLAGNIOL-VILLARD 		return -1;
9462731b9a8SJean-Christophe PLAGNIOL-VILLARD 	}
9472731b9a8SJean-Christophe PLAGNIOL-VILLARD 
948676ae068SLucas Stach 	if (usb_pipedevice(pipe) == ctrl->rootdev) {
949676ae068SLucas Stach 		if (!ctrl->rootdev)
9502731b9a8SJean-Christophe PLAGNIOL-VILLARD 			dev->speed = USB_SPEED_HIGH;
9512731b9a8SJean-Christophe PLAGNIOL-VILLARD 		return ehci_submit_root(dev, pipe, buffer, length, setup);
9522731b9a8SJean-Christophe PLAGNIOL-VILLARD 	}
9532731b9a8SJean-Christophe PLAGNIOL-VILLARD 	return ehci_submit_async(dev, pipe, buffer, length, setup);
9542731b9a8SJean-Christophe PLAGNIOL-VILLARD }
9552731b9a8SJean-Christophe PLAGNIOL-VILLARD 
9562731b9a8SJean-Christophe PLAGNIOL-VILLARD int
9572731b9a8SJean-Christophe PLAGNIOL-VILLARD submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
9582731b9a8SJean-Christophe PLAGNIOL-VILLARD 	       int length, int interval)
9592731b9a8SJean-Christophe PLAGNIOL-VILLARD {
9602731b9a8SJean-Christophe PLAGNIOL-VILLARD 	debug("dev=%p, pipe=%lu, buffer=%p, length=%d, interval=%d",
9612731b9a8SJean-Christophe PLAGNIOL-VILLARD 	      dev, pipe, buffer, length, interval);
96244ae0be7SBenoît Thébaudeau 
96344ae0be7SBenoît Thébaudeau 	/*
96444ae0be7SBenoît Thébaudeau 	 * Interrupt transfers requiring several transactions are not supported
96544ae0be7SBenoît Thébaudeau 	 * because bInterval is ignored.
9665cec214eSBenoît Thébaudeau 	 *
9675cec214eSBenoît Thébaudeau 	 * Also, ehci_submit_async() relies on wMaxPacketSize being a power of 2
968db191346SBenoît Thébaudeau 	 * <= PKT_ALIGN if several qTDs are required, while the USB
969db191346SBenoît Thébaudeau 	 * specification does not constrain this for interrupt transfers. That
970db191346SBenoît Thébaudeau 	 * means that ehci_submit_async() would support interrupt transfers
971db191346SBenoît Thébaudeau 	 * requiring several transactions only as long as the transfer size does
972db191346SBenoît Thébaudeau 	 * not require more than a single qTD.
97344ae0be7SBenoît Thébaudeau 	 */
97444ae0be7SBenoît Thébaudeau 	if (length > usb_maxpacket(dev, pipe)) {
97544ae0be7SBenoît Thébaudeau 		printf("%s: Interrupt transfers requiring several transactions "
97644ae0be7SBenoît Thébaudeau 			"are not supported.\n", __func__);
97744ae0be7SBenoît Thébaudeau 		return -1;
97844ae0be7SBenoît Thébaudeau 	}
9797555d5ecSMarek Vasut 	return ehci_submit_async(dev, pipe, buffer, length, NULL);
9802731b9a8SJean-Christophe PLAGNIOL-VILLARD }
981