xref: /rk3399_rockchip-uboot/drivers/usb/musb/musb_hcd.c (revision 1a4f6af8bfd44c8ae6e87a81ff125eed47042cc5)
12731b9a8SJean-Christophe PLAGNIOL-VILLARD /*
22731b9a8SJean-Christophe PLAGNIOL-VILLARD  * Mentor USB OTG Core host controller driver.
32731b9a8SJean-Christophe PLAGNIOL-VILLARD  *
42731b9a8SJean-Christophe PLAGNIOL-VILLARD  * Copyright (c) 2008 Texas Instruments
52731b9a8SJean-Christophe PLAGNIOL-VILLARD  *
61a459660SWolfgang Denk  * SPDX-License-Identifier:	GPL-2.0+
72731b9a8SJean-Christophe PLAGNIOL-VILLARD  *
82731b9a8SJean-Christophe PLAGNIOL-VILLARD  * Author: Thomas Abraham t-abraham@ti.com, Texas Instruments
92731b9a8SJean-Christophe PLAGNIOL-VILLARD  */
102731b9a8SJean-Christophe PLAGNIOL-VILLARD 
112731b9a8SJean-Christophe PLAGNIOL-VILLARD #include <common.h>
12c60795f4SIlya Yanok #include <usb.h>
132731b9a8SJean-Christophe PLAGNIOL-VILLARD #include "musb_hcd.h"
142731b9a8SJean-Christophe PLAGNIOL-VILLARD 
152731b9a8SJean-Christophe PLAGNIOL-VILLARD /* MSC control transfers */
162731b9a8SJean-Christophe PLAGNIOL-VILLARD #define USB_MSC_BBB_RESET 	0xFF
172731b9a8SJean-Christophe PLAGNIOL-VILLARD #define USB_MSC_BBB_GET_MAX_LUN	0xFE
182731b9a8SJean-Christophe PLAGNIOL-VILLARD 
192731b9a8SJean-Christophe PLAGNIOL-VILLARD /* Endpoint configuration information */
200228348eSMike Frysinger static const struct musb_epinfo epinfo[3] = {
212731b9a8SJean-Christophe PLAGNIOL-VILLARD 	{MUSB_BULK_EP, 1, 512}, /* EP1 - Bluk Out - 512 Bytes */
222731b9a8SJean-Christophe PLAGNIOL-VILLARD 	{MUSB_BULK_EP, 0, 512}, /* EP1 - Bluk In  - 512 Bytes */
232731b9a8SJean-Christophe PLAGNIOL-VILLARD 	{MUSB_INTR_EP, 0, 64}   /* EP2 - Interrupt IN - 64 Bytes */
242731b9a8SJean-Christophe PLAGNIOL-VILLARD };
252731b9a8SJean-Christophe PLAGNIOL-VILLARD 
26321790f6SBryan Wu /* --- Virtual Root Hub ---------------------------------------------------- */
27321790f6SBryan Wu #ifdef MUSB_NO_MULTIPOINT
28321790f6SBryan Wu static int rh_devnum;
29321790f6SBryan Wu static u32 port_status;
30321790f6SBryan Wu 
31eb838e7dSStephen Warren #include <usbroothubdes.h>
32321790f6SBryan Wu 
33321790f6SBryan Wu #endif
34321790f6SBryan Wu 
352731b9a8SJean-Christophe PLAGNIOL-VILLARD /*
362731b9a8SJean-Christophe PLAGNIOL-VILLARD  * This function writes the data toggle value.
372731b9a8SJean-Christophe PLAGNIOL-VILLARD  */
write_toggle(struct usb_device * dev,u8 ep,u8 dir_out)382731b9a8SJean-Christophe PLAGNIOL-VILLARD static void write_toggle(struct usb_device *dev, u8 ep, u8 dir_out)
392731b9a8SJean-Christophe PLAGNIOL-VILLARD {
402731b9a8SJean-Christophe PLAGNIOL-VILLARD 	u16 toggle = usb_gettoggle(dev, ep, dir_out);
412731b9a8SJean-Christophe PLAGNIOL-VILLARD 	u16 csr;
422731b9a8SJean-Christophe PLAGNIOL-VILLARD 
432731b9a8SJean-Christophe PLAGNIOL-VILLARD 	if (dir_out) {
442731b9a8SJean-Christophe PLAGNIOL-VILLARD 		csr = readw(&musbr->txcsr);
454ee691f6SBryan Wu 		if (!toggle) {
464ee691f6SBryan Wu 			if (csr & MUSB_TXCSR_MODE)
474ee691f6SBryan Wu 				csr = MUSB_TXCSR_CLRDATATOG;
484ee691f6SBryan Wu 			else
494ee691f6SBryan Wu 				csr = 0;
504ee691f6SBryan Wu 			writew(csr, &musbr->txcsr);
514ee691f6SBryan Wu 		} else {
522731b9a8SJean-Christophe PLAGNIOL-VILLARD 			csr |= MUSB_TXCSR_H_WR_DATATOGGLE;
532731b9a8SJean-Christophe PLAGNIOL-VILLARD 			writew(csr, &musbr->txcsr);
542731b9a8SJean-Christophe PLAGNIOL-VILLARD 			csr |= (toggle << MUSB_TXCSR_H_DATATOGGLE_SHIFT);
552731b9a8SJean-Christophe PLAGNIOL-VILLARD 			writew(csr, &musbr->txcsr);
562731b9a8SJean-Christophe PLAGNIOL-VILLARD 		}
572731b9a8SJean-Christophe PLAGNIOL-VILLARD 	} else {
584ee691f6SBryan Wu 		if (!toggle) {
594ee691f6SBryan Wu 			csr = readw(&musbr->txcsr);
604ee691f6SBryan Wu 			if (csr & MUSB_TXCSR_MODE)
614ee691f6SBryan Wu 				csr = MUSB_RXCSR_CLRDATATOG;
624ee691f6SBryan Wu 			else
634ee691f6SBryan Wu 				csr = 0;
644ee691f6SBryan Wu 			writew(csr, &musbr->rxcsr);
654ee691f6SBryan Wu 		} else {
662731b9a8SJean-Christophe PLAGNIOL-VILLARD 			csr = readw(&musbr->rxcsr);
672731b9a8SJean-Christophe PLAGNIOL-VILLARD 			csr |= MUSB_RXCSR_H_WR_DATATOGGLE;
682731b9a8SJean-Christophe PLAGNIOL-VILLARD 			writew(csr, &musbr->rxcsr);
692731b9a8SJean-Christophe PLAGNIOL-VILLARD 			csr |= (toggle << MUSB_S_RXCSR_H_DATATOGGLE);
702731b9a8SJean-Christophe PLAGNIOL-VILLARD 			writew(csr, &musbr->rxcsr);
712731b9a8SJean-Christophe PLAGNIOL-VILLARD 		}
722731b9a8SJean-Christophe PLAGNIOL-VILLARD 	}
732731b9a8SJean-Christophe PLAGNIOL-VILLARD }
742731b9a8SJean-Christophe PLAGNIOL-VILLARD 
752731b9a8SJean-Christophe PLAGNIOL-VILLARD /*
76eae4b2b6SVagrant Cascadian  * This function checks if RxStall has occurred on the endpoint. If a RxStall
77eae4b2b6SVagrant Cascadian  * has occurred, the RxStall is cleared and 1 is returned. If RxStall has
78eae4b2b6SVagrant Cascadian  * not occurred, 0 is returned.
792731b9a8SJean-Christophe PLAGNIOL-VILLARD  */
check_stall(u8 ep,u8 dir_out)802731b9a8SJean-Christophe PLAGNIOL-VILLARD static u8 check_stall(u8 ep, u8 dir_out)
812731b9a8SJean-Christophe PLAGNIOL-VILLARD {
822731b9a8SJean-Christophe PLAGNIOL-VILLARD 	u16 csr;
832731b9a8SJean-Christophe PLAGNIOL-VILLARD 
842731b9a8SJean-Christophe PLAGNIOL-VILLARD 	/* For endpoint 0 */
852731b9a8SJean-Christophe PLAGNIOL-VILLARD 	if (!ep) {
862731b9a8SJean-Christophe PLAGNIOL-VILLARD 		csr = readw(&musbr->txcsr);
872731b9a8SJean-Christophe PLAGNIOL-VILLARD 		if (csr & MUSB_CSR0_H_RXSTALL) {
882731b9a8SJean-Christophe PLAGNIOL-VILLARD 			csr &= ~MUSB_CSR0_H_RXSTALL;
892731b9a8SJean-Christophe PLAGNIOL-VILLARD 			writew(csr, &musbr->txcsr);
902731b9a8SJean-Christophe PLAGNIOL-VILLARD 			return 1;
912731b9a8SJean-Christophe PLAGNIOL-VILLARD 		}
922731b9a8SJean-Christophe PLAGNIOL-VILLARD 	} else { /* For non-ep0 */
932731b9a8SJean-Christophe PLAGNIOL-VILLARD 		if (dir_out) { /* is it tx ep */
942731b9a8SJean-Christophe PLAGNIOL-VILLARD 			csr = readw(&musbr->txcsr);
952731b9a8SJean-Christophe PLAGNIOL-VILLARD 			if (csr & MUSB_TXCSR_H_RXSTALL) {
962731b9a8SJean-Christophe PLAGNIOL-VILLARD 				csr &= ~MUSB_TXCSR_H_RXSTALL;
972731b9a8SJean-Christophe PLAGNIOL-VILLARD 				writew(csr, &musbr->txcsr);
982731b9a8SJean-Christophe PLAGNIOL-VILLARD 				return 1;
992731b9a8SJean-Christophe PLAGNIOL-VILLARD 			}
1002731b9a8SJean-Christophe PLAGNIOL-VILLARD 		} else { /* is it rx ep */
1012731b9a8SJean-Christophe PLAGNIOL-VILLARD 			csr = readw(&musbr->rxcsr);
1022731b9a8SJean-Christophe PLAGNIOL-VILLARD 			if (csr & MUSB_RXCSR_H_RXSTALL) {
1032731b9a8SJean-Christophe PLAGNIOL-VILLARD 				csr &= ~MUSB_RXCSR_H_RXSTALL;
1042731b9a8SJean-Christophe PLAGNIOL-VILLARD 				writew(csr, &musbr->rxcsr);
1052731b9a8SJean-Christophe PLAGNIOL-VILLARD 				return 1;
1062731b9a8SJean-Christophe PLAGNIOL-VILLARD 			}
1072731b9a8SJean-Christophe PLAGNIOL-VILLARD 		}
1082731b9a8SJean-Christophe PLAGNIOL-VILLARD 	}
1092731b9a8SJean-Christophe PLAGNIOL-VILLARD 	return 0;
1102731b9a8SJean-Christophe PLAGNIOL-VILLARD }
1112731b9a8SJean-Christophe PLAGNIOL-VILLARD 
1122731b9a8SJean-Christophe PLAGNIOL-VILLARD /*
1132731b9a8SJean-Christophe PLAGNIOL-VILLARD  * waits until ep0 is ready. Returns 0 if ep is ready, -1 for timeout
1142731b9a8SJean-Christophe PLAGNIOL-VILLARD  * error and -2 for stall.
1152731b9a8SJean-Christophe PLAGNIOL-VILLARD  */
wait_until_ep0_ready(struct usb_device * dev,u32 bit_mask)1162731b9a8SJean-Christophe PLAGNIOL-VILLARD static int wait_until_ep0_ready(struct usb_device *dev, u32 bit_mask)
1172731b9a8SJean-Christophe PLAGNIOL-VILLARD {
1182731b9a8SJean-Christophe PLAGNIOL-VILLARD 	u16 csr;
1192731b9a8SJean-Christophe PLAGNIOL-VILLARD 	int result = 1;
12095de1e2fSPaul Kocialkowski 	int timeout = CONFIG_USB_MUSB_TIMEOUT;
1212731b9a8SJean-Christophe PLAGNIOL-VILLARD 
1222731b9a8SJean-Christophe PLAGNIOL-VILLARD 	while (result > 0) {
1232731b9a8SJean-Christophe PLAGNIOL-VILLARD 		csr = readw(&musbr->txcsr);
1242731b9a8SJean-Christophe PLAGNIOL-VILLARD 		if (csr & MUSB_CSR0_H_ERROR) {
1252731b9a8SJean-Christophe PLAGNIOL-VILLARD 			csr &= ~MUSB_CSR0_H_ERROR;
1262731b9a8SJean-Christophe PLAGNIOL-VILLARD 			writew(csr, &musbr->txcsr);
1272731b9a8SJean-Christophe PLAGNIOL-VILLARD 			dev->status = USB_ST_CRC_ERR;
1282731b9a8SJean-Christophe PLAGNIOL-VILLARD 			result = -1;
1292731b9a8SJean-Christophe PLAGNIOL-VILLARD 			break;
1302731b9a8SJean-Christophe PLAGNIOL-VILLARD 		}
1312731b9a8SJean-Christophe PLAGNIOL-VILLARD 
1322731b9a8SJean-Christophe PLAGNIOL-VILLARD 		switch (bit_mask) {
1332731b9a8SJean-Christophe PLAGNIOL-VILLARD 		case MUSB_CSR0_TXPKTRDY:
1342731b9a8SJean-Christophe PLAGNIOL-VILLARD 			if (!(csr & MUSB_CSR0_TXPKTRDY)) {
1352731b9a8SJean-Christophe PLAGNIOL-VILLARD 				if (check_stall(MUSB_CONTROL_EP, 0)) {
1362731b9a8SJean-Christophe PLAGNIOL-VILLARD 					dev->status = USB_ST_STALLED;
1372731b9a8SJean-Christophe PLAGNIOL-VILLARD 					result = -2;
1382731b9a8SJean-Christophe PLAGNIOL-VILLARD 				} else
1392731b9a8SJean-Christophe PLAGNIOL-VILLARD 					result = 0;
1402731b9a8SJean-Christophe PLAGNIOL-VILLARD 			}
1412731b9a8SJean-Christophe PLAGNIOL-VILLARD 			break;
1422731b9a8SJean-Christophe PLAGNIOL-VILLARD 
1432731b9a8SJean-Christophe PLAGNIOL-VILLARD 		case MUSB_CSR0_RXPKTRDY:
1442731b9a8SJean-Christophe PLAGNIOL-VILLARD 			if (check_stall(MUSB_CONTROL_EP, 0)) {
1452731b9a8SJean-Christophe PLAGNIOL-VILLARD 				dev->status = USB_ST_STALLED;
1462731b9a8SJean-Christophe PLAGNIOL-VILLARD 				result = -2;
1472731b9a8SJean-Christophe PLAGNIOL-VILLARD 			} else
1482731b9a8SJean-Christophe PLAGNIOL-VILLARD 				if (csr & MUSB_CSR0_RXPKTRDY)
1492731b9a8SJean-Christophe PLAGNIOL-VILLARD 					result = 0;
1502731b9a8SJean-Christophe PLAGNIOL-VILLARD 			break;
1512731b9a8SJean-Christophe PLAGNIOL-VILLARD 
1522731b9a8SJean-Christophe PLAGNIOL-VILLARD 		case MUSB_CSR0_H_REQPKT:
1532731b9a8SJean-Christophe PLAGNIOL-VILLARD 			if (!(csr & MUSB_CSR0_H_REQPKT)) {
1542731b9a8SJean-Christophe PLAGNIOL-VILLARD 				if (check_stall(MUSB_CONTROL_EP, 0)) {
1552731b9a8SJean-Christophe PLAGNIOL-VILLARD 					dev->status = USB_ST_STALLED;
1562731b9a8SJean-Christophe PLAGNIOL-VILLARD 					result = -2;
1572731b9a8SJean-Christophe PLAGNIOL-VILLARD 				} else
1582731b9a8SJean-Christophe PLAGNIOL-VILLARD 					result = 0;
1592731b9a8SJean-Christophe PLAGNIOL-VILLARD 			}
1602731b9a8SJean-Christophe PLAGNIOL-VILLARD 			break;
1612731b9a8SJean-Christophe PLAGNIOL-VILLARD 		}
162c3a012ceSBryan Wu 
163c3a012ceSBryan Wu 		/* Check the timeout */
164c3a012ceSBryan Wu 		if (--timeout)
165c3a012ceSBryan Wu 			udelay(1);
166c3a012ceSBryan Wu 		else {
167c3a012ceSBryan Wu 			dev->status = USB_ST_CRC_ERR;
168c3a012ceSBryan Wu 			result = -1;
169c3a012ceSBryan Wu 			break;
1702731b9a8SJean-Christophe PLAGNIOL-VILLARD 		}
171c3a012ceSBryan Wu 	}
172c3a012ceSBryan Wu 
1732731b9a8SJean-Christophe PLAGNIOL-VILLARD 	return result;
1742731b9a8SJean-Christophe PLAGNIOL-VILLARD }
1752731b9a8SJean-Christophe PLAGNIOL-VILLARD 
1762731b9a8SJean-Christophe PLAGNIOL-VILLARD /*
1772731b9a8SJean-Christophe PLAGNIOL-VILLARD  * waits until tx ep is ready. Returns 1 when ep is ready and 0 on error.
1782731b9a8SJean-Christophe PLAGNIOL-VILLARD  */
wait_until_txep_ready(struct usb_device * dev,u8 ep)179ddd025bbSAndrew Murray static int wait_until_txep_ready(struct usb_device *dev, u8 ep)
1802731b9a8SJean-Christophe PLAGNIOL-VILLARD {
1812731b9a8SJean-Christophe PLAGNIOL-VILLARD 	u16 csr;
18295de1e2fSPaul Kocialkowski 	int timeout = CONFIG_USB_MUSB_TIMEOUT;
1832731b9a8SJean-Christophe PLAGNIOL-VILLARD 
1842731b9a8SJean-Christophe PLAGNIOL-VILLARD 	do {
1852731b9a8SJean-Christophe PLAGNIOL-VILLARD 		if (check_stall(ep, 1)) {
1862731b9a8SJean-Christophe PLAGNIOL-VILLARD 			dev->status = USB_ST_STALLED;
1872731b9a8SJean-Christophe PLAGNIOL-VILLARD 			return 0;
1882731b9a8SJean-Christophe PLAGNIOL-VILLARD 		}
1892731b9a8SJean-Christophe PLAGNIOL-VILLARD 
1902731b9a8SJean-Christophe PLAGNIOL-VILLARD 		csr = readw(&musbr->txcsr);
1912731b9a8SJean-Christophe PLAGNIOL-VILLARD 		if (csr & MUSB_TXCSR_H_ERROR) {
1922731b9a8SJean-Christophe PLAGNIOL-VILLARD 			dev->status = USB_ST_CRC_ERR;
1932731b9a8SJean-Christophe PLAGNIOL-VILLARD 			return 0;
1942731b9a8SJean-Christophe PLAGNIOL-VILLARD 		}
195c3a012ceSBryan Wu 
196c3a012ceSBryan Wu 		/* Check the timeout */
197c3a012ceSBryan Wu 		if (--timeout)
198c3a012ceSBryan Wu 			udelay(1);
199c3a012ceSBryan Wu 		else {
200c3a012ceSBryan Wu 			dev->status = USB_ST_CRC_ERR;
201c3a012ceSBryan Wu 			return -1;
202c3a012ceSBryan Wu 		}
203c3a012ceSBryan Wu 
2042731b9a8SJean-Christophe PLAGNIOL-VILLARD 	} while (csr & MUSB_TXCSR_TXPKTRDY);
2052731b9a8SJean-Christophe PLAGNIOL-VILLARD 	return 1;
2062731b9a8SJean-Christophe PLAGNIOL-VILLARD }
2072731b9a8SJean-Christophe PLAGNIOL-VILLARD 
2082731b9a8SJean-Christophe PLAGNIOL-VILLARD /*
2092731b9a8SJean-Christophe PLAGNIOL-VILLARD  * waits until rx ep is ready. Returns 1 when ep is ready and 0 on error.
2102731b9a8SJean-Christophe PLAGNIOL-VILLARD  */
wait_until_rxep_ready(struct usb_device * dev,u8 ep)211ddd025bbSAndrew Murray static int wait_until_rxep_ready(struct usb_device *dev, u8 ep)
2122731b9a8SJean-Christophe PLAGNIOL-VILLARD {
2132731b9a8SJean-Christophe PLAGNIOL-VILLARD 	u16 csr;
21495de1e2fSPaul Kocialkowski 	int timeout = CONFIG_USB_MUSB_TIMEOUT;
2152731b9a8SJean-Christophe PLAGNIOL-VILLARD 
2162731b9a8SJean-Christophe PLAGNIOL-VILLARD 	do {
2172731b9a8SJean-Christophe PLAGNIOL-VILLARD 		if (check_stall(ep, 0)) {
2182731b9a8SJean-Christophe PLAGNIOL-VILLARD 			dev->status = USB_ST_STALLED;
2192731b9a8SJean-Christophe PLAGNIOL-VILLARD 			return 0;
2202731b9a8SJean-Christophe PLAGNIOL-VILLARD 		}
2212731b9a8SJean-Christophe PLAGNIOL-VILLARD 
2222731b9a8SJean-Christophe PLAGNIOL-VILLARD 		csr = readw(&musbr->rxcsr);
2232731b9a8SJean-Christophe PLAGNIOL-VILLARD 		if (csr & MUSB_RXCSR_H_ERROR) {
2242731b9a8SJean-Christophe PLAGNIOL-VILLARD 			dev->status = USB_ST_CRC_ERR;
2252731b9a8SJean-Christophe PLAGNIOL-VILLARD 			return 0;
2262731b9a8SJean-Christophe PLAGNIOL-VILLARD 		}
227c3a012ceSBryan Wu 
228c3a012ceSBryan Wu 		/* Check the timeout */
229c3a012ceSBryan Wu 		if (--timeout)
230c3a012ceSBryan Wu 			udelay(1);
231c3a012ceSBryan Wu 		else {
232c3a012ceSBryan Wu 			dev->status = USB_ST_CRC_ERR;
233c3a012ceSBryan Wu 			return -1;
234c3a012ceSBryan Wu 		}
235c3a012ceSBryan Wu 
2362731b9a8SJean-Christophe PLAGNIOL-VILLARD 	} while (!(csr & MUSB_RXCSR_RXPKTRDY));
2372731b9a8SJean-Christophe PLAGNIOL-VILLARD 	return 1;
2382731b9a8SJean-Christophe PLAGNIOL-VILLARD }
2392731b9a8SJean-Christophe PLAGNIOL-VILLARD 
2402731b9a8SJean-Christophe PLAGNIOL-VILLARD /*
2412731b9a8SJean-Christophe PLAGNIOL-VILLARD  * This function performs the setup phase of the control transfer
2422731b9a8SJean-Christophe PLAGNIOL-VILLARD  */
ctrlreq_setup_phase(struct usb_device * dev,struct devrequest * setup)2432731b9a8SJean-Christophe PLAGNIOL-VILLARD static int ctrlreq_setup_phase(struct usb_device *dev, struct devrequest *setup)
2442731b9a8SJean-Christophe PLAGNIOL-VILLARD {
2452731b9a8SJean-Christophe PLAGNIOL-VILLARD 	int result;
2462731b9a8SJean-Christophe PLAGNIOL-VILLARD 	u16 csr;
2472731b9a8SJean-Christophe PLAGNIOL-VILLARD 
2482731b9a8SJean-Christophe PLAGNIOL-VILLARD 	/* write the control request to ep0 fifo */
2492731b9a8SJean-Christophe PLAGNIOL-VILLARD 	write_fifo(MUSB_CONTROL_EP, sizeof(struct devrequest), (void *)setup);
2502731b9a8SJean-Christophe PLAGNIOL-VILLARD 
2512731b9a8SJean-Christophe PLAGNIOL-VILLARD 	/* enable transfer of setup packet */
2522731b9a8SJean-Christophe PLAGNIOL-VILLARD 	csr = readw(&musbr->txcsr);
2532731b9a8SJean-Christophe PLAGNIOL-VILLARD 	csr |= (MUSB_CSR0_TXPKTRDY|MUSB_CSR0_H_SETUPPKT);
2542731b9a8SJean-Christophe PLAGNIOL-VILLARD 	writew(csr, &musbr->txcsr);
2552731b9a8SJean-Christophe PLAGNIOL-VILLARD 
2562731b9a8SJean-Christophe PLAGNIOL-VILLARD 	/* wait until the setup packet is transmitted */
2572731b9a8SJean-Christophe PLAGNIOL-VILLARD 	result = wait_until_ep0_ready(dev, MUSB_CSR0_TXPKTRDY);
2582731b9a8SJean-Christophe PLAGNIOL-VILLARD 	dev->act_len = 0;
2592731b9a8SJean-Christophe PLAGNIOL-VILLARD 	return result;
2602731b9a8SJean-Christophe PLAGNIOL-VILLARD }
2612731b9a8SJean-Christophe PLAGNIOL-VILLARD 
2622731b9a8SJean-Christophe PLAGNIOL-VILLARD /*
2632731b9a8SJean-Christophe PLAGNIOL-VILLARD  * This function handles the control transfer in data phase
2642731b9a8SJean-Christophe PLAGNIOL-VILLARD  */
ctrlreq_in_data_phase(struct usb_device * dev,u32 len,void * buffer)2652731b9a8SJean-Christophe PLAGNIOL-VILLARD static int ctrlreq_in_data_phase(struct usb_device *dev, u32 len, void *buffer)
2662731b9a8SJean-Christophe PLAGNIOL-VILLARD {
2672731b9a8SJean-Christophe PLAGNIOL-VILLARD 	u16 csr;
2682731b9a8SJean-Christophe PLAGNIOL-VILLARD 	u32 rxlen = 0;
2692731b9a8SJean-Christophe PLAGNIOL-VILLARD 	u32 nextlen = 0;
2702731b9a8SJean-Christophe PLAGNIOL-VILLARD 	u8  maxpktsize = (1 << dev->maxpacketsize) * 8;
2712731b9a8SJean-Christophe PLAGNIOL-VILLARD 	u8  *rxbuff = (u8 *)buffer;
2722731b9a8SJean-Christophe PLAGNIOL-VILLARD 	u8  rxedlength;
2732731b9a8SJean-Christophe PLAGNIOL-VILLARD 	int result;
2742731b9a8SJean-Christophe PLAGNIOL-VILLARD 
2752731b9a8SJean-Christophe PLAGNIOL-VILLARD 	while (rxlen < len) {
2762731b9a8SJean-Christophe PLAGNIOL-VILLARD 		/* Determine the next read length */
2772731b9a8SJean-Christophe PLAGNIOL-VILLARD 		nextlen = ((len-rxlen) > maxpktsize) ? maxpktsize : (len-rxlen);
2782731b9a8SJean-Christophe PLAGNIOL-VILLARD 
2792731b9a8SJean-Christophe PLAGNIOL-VILLARD 		/* Set the ReqPkt bit */
2802731b9a8SJean-Christophe PLAGNIOL-VILLARD 		csr = readw(&musbr->txcsr);
2812731b9a8SJean-Christophe PLAGNIOL-VILLARD 		writew(csr | MUSB_CSR0_H_REQPKT, &musbr->txcsr);
2822731b9a8SJean-Christophe PLAGNIOL-VILLARD 		result = wait_until_ep0_ready(dev, MUSB_CSR0_RXPKTRDY);
2832731b9a8SJean-Christophe PLAGNIOL-VILLARD 		if (result < 0)
2842731b9a8SJean-Christophe PLAGNIOL-VILLARD 			return result;
2852731b9a8SJean-Christophe PLAGNIOL-VILLARD 
2862731b9a8SJean-Christophe PLAGNIOL-VILLARD 		/* Actual number of bytes received by usb */
2872731b9a8SJean-Christophe PLAGNIOL-VILLARD 		rxedlength = readb(&musbr->rxcount);
2882731b9a8SJean-Christophe PLAGNIOL-VILLARD 
2892731b9a8SJean-Christophe PLAGNIOL-VILLARD 		/* Read the data from the RxFIFO */
2902731b9a8SJean-Christophe PLAGNIOL-VILLARD 		read_fifo(MUSB_CONTROL_EP, rxedlength, &rxbuff[rxlen]);
2912731b9a8SJean-Christophe PLAGNIOL-VILLARD 
2922731b9a8SJean-Christophe PLAGNIOL-VILLARD 		/* Clear the RxPktRdy Bit */
2932731b9a8SJean-Christophe PLAGNIOL-VILLARD 		csr = readw(&musbr->txcsr);
2942731b9a8SJean-Christophe PLAGNIOL-VILLARD 		csr &= ~MUSB_CSR0_RXPKTRDY;
2952731b9a8SJean-Christophe PLAGNIOL-VILLARD 		writew(csr, &musbr->txcsr);
2962731b9a8SJean-Christophe PLAGNIOL-VILLARD 
2972731b9a8SJean-Christophe PLAGNIOL-VILLARD 		/* short packet? */
2982731b9a8SJean-Christophe PLAGNIOL-VILLARD 		if (rxedlength != nextlen) {
2992731b9a8SJean-Christophe PLAGNIOL-VILLARD 			dev->act_len += rxedlength;
3002731b9a8SJean-Christophe PLAGNIOL-VILLARD 			break;
3012731b9a8SJean-Christophe PLAGNIOL-VILLARD 		}
3022731b9a8SJean-Christophe PLAGNIOL-VILLARD 		rxlen += nextlen;
3032731b9a8SJean-Christophe PLAGNIOL-VILLARD 		dev->act_len = rxlen;
3042731b9a8SJean-Christophe PLAGNIOL-VILLARD 	}
3052731b9a8SJean-Christophe PLAGNIOL-VILLARD 	return 0;
3062731b9a8SJean-Christophe PLAGNIOL-VILLARD }
3072731b9a8SJean-Christophe PLAGNIOL-VILLARD 
3082731b9a8SJean-Christophe PLAGNIOL-VILLARD /*
3092731b9a8SJean-Christophe PLAGNIOL-VILLARD  * This function handles the control transfer out data phase
3102731b9a8SJean-Christophe PLAGNIOL-VILLARD  */
ctrlreq_out_data_phase(struct usb_device * dev,u32 len,void * buffer)3112731b9a8SJean-Christophe PLAGNIOL-VILLARD static int ctrlreq_out_data_phase(struct usb_device *dev, u32 len, void *buffer)
3122731b9a8SJean-Christophe PLAGNIOL-VILLARD {
3132731b9a8SJean-Christophe PLAGNIOL-VILLARD 	u16 csr;
3142731b9a8SJean-Christophe PLAGNIOL-VILLARD 	u32 txlen = 0;
3152731b9a8SJean-Christophe PLAGNIOL-VILLARD 	u32 nextlen = 0;
3162731b9a8SJean-Christophe PLAGNIOL-VILLARD 	u8  maxpktsize = (1 << dev->maxpacketsize) * 8;
3172731b9a8SJean-Christophe PLAGNIOL-VILLARD 	u8  *txbuff = (u8 *)buffer;
3182731b9a8SJean-Christophe PLAGNIOL-VILLARD 	int result = 0;
3192731b9a8SJean-Christophe PLAGNIOL-VILLARD 
3202731b9a8SJean-Christophe PLAGNIOL-VILLARD 	while (txlen < len) {
3212731b9a8SJean-Christophe PLAGNIOL-VILLARD 		/* Determine the next write length */
3222731b9a8SJean-Christophe PLAGNIOL-VILLARD 		nextlen = ((len-txlen) > maxpktsize) ? maxpktsize : (len-txlen);
3232731b9a8SJean-Christophe PLAGNIOL-VILLARD 
3242731b9a8SJean-Christophe PLAGNIOL-VILLARD 		/* Load the data to send in FIFO */
3252731b9a8SJean-Christophe PLAGNIOL-VILLARD 		write_fifo(MUSB_CONTROL_EP, txlen, &txbuff[txlen]);
3262731b9a8SJean-Christophe PLAGNIOL-VILLARD 
3272731b9a8SJean-Christophe PLAGNIOL-VILLARD 		/* Set TXPKTRDY bit */
3282731b9a8SJean-Christophe PLAGNIOL-VILLARD 		csr = readw(&musbr->txcsr);
32999b4eaa6SAndrew Murray 
33099b4eaa6SAndrew Murray 		csr |= MUSB_CSR0_TXPKTRDY;
33199b4eaa6SAndrew Murray #if !defined(CONFIG_SOC_DM365)
33299b4eaa6SAndrew Murray 		csr |= MUSB_CSR0_H_DIS_PING;
33399b4eaa6SAndrew Murray #endif
33499b4eaa6SAndrew Murray 		writew(csr, &musbr->txcsr);
3352731b9a8SJean-Christophe PLAGNIOL-VILLARD 		result = wait_until_ep0_ready(dev, MUSB_CSR0_TXPKTRDY);
3362731b9a8SJean-Christophe PLAGNIOL-VILLARD 		if (result < 0)
3372731b9a8SJean-Christophe PLAGNIOL-VILLARD 			break;
3382731b9a8SJean-Christophe PLAGNIOL-VILLARD 
3392731b9a8SJean-Christophe PLAGNIOL-VILLARD 		txlen += nextlen;
3402731b9a8SJean-Christophe PLAGNIOL-VILLARD 		dev->act_len = txlen;
3412731b9a8SJean-Christophe PLAGNIOL-VILLARD 	}
3422731b9a8SJean-Christophe PLAGNIOL-VILLARD 	return result;
3432731b9a8SJean-Christophe PLAGNIOL-VILLARD }
3442731b9a8SJean-Christophe PLAGNIOL-VILLARD 
3452731b9a8SJean-Christophe PLAGNIOL-VILLARD /*
3462731b9a8SJean-Christophe PLAGNIOL-VILLARD  * This function handles the control transfer out status phase
3472731b9a8SJean-Christophe PLAGNIOL-VILLARD  */
ctrlreq_out_status_phase(struct usb_device * dev)3482731b9a8SJean-Christophe PLAGNIOL-VILLARD static int ctrlreq_out_status_phase(struct usb_device *dev)
3492731b9a8SJean-Christophe PLAGNIOL-VILLARD {
3502731b9a8SJean-Christophe PLAGNIOL-VILLARD 	u16 csr;
3512731b9a8SJean-Christophe PLAGNIOL-VILLARD 	int result;
3522731b9a8SJean-Christophe PLAGNIOL-VILLARD 
3532731b9a8SJean-Christophe PLAGNIOL-VILLARD 	/* Set the StatusPkt bit */
3542731b9a8SJean-Christophe PLAGNIOL-VILLARD 	csr = readw(&musbr->txcsr);
35599b4eaa6SAndrew Murray 	csr |= (MUSB_CSR0_TXPKTRDY | MUSB_CSR0_H_STATUSPKT);
35699b4eaa6SAndrew Murray #if !defined(CONFIG_SOC_DM365)
35799b4eaa6SAndrew Murray 	csr |= MUSB_CSR0_H_DIS_PING;
35899b4eaa6SAndrew Murray #endif
3592731b9a8SJean-Christophe PLAGNIOL-VILLARD 	writew(csr, &musbr->txcsr);
3602731b9a8SJean-Christophe PLAGNIOL-VILLARD 
3612731b9a8SJean-Christophe PLAGNIOL-VILLARD 	/* Wait until TXPKTRDY bit is cleared */
3622731b9a8SJean-Christophe PLAGNIOL-VILLARD 	result = wait_until_ep0_ready(dev, MUSB_CSR0_TXPKTRDY);
3632731b9a8SJean-Christophe PLAGNIOL-VILLARD 	return result;
3642731b9a8SJean-Christophe PLAGNIOL-VILLARD }
3652731b9a8SJean-Christophe PLAGNIOL-VILLARD 
3662731b9a8SJean-Christophe PLAGNIOL-VILLARD /*
3672731b9a8SJean-Christophe PLAGNIOL-VILLARD  * This function handles the control transfer in status phase
3682731b9a8SJean-Christophe PLAGNIOL-VILLARD  */
ctrlreq_in_status_phase(struct usb_device * dev)3692731b9a8SJean-Christophe PLAGNIOL-VILLARD static int ctrlreq_in_status_phase(struct usb_device *dev)
3702731b9a8SJean-Christophe PLAGNIOL-VILLARD {
3712731b9a8SJean-Christophe PLAGNIOL-VILLARD 	u16 csr;
3722731b9a8SJean-Christophe PLAGNIOL-VILLARD 	int result;
3732731b9a8SJean-Christophe PLAGNIOL-VILLARD 
3742731b9a8SJean-Christophe PLAGNIOL-VILLARD 	/* Set the StatusPkt bit and ReqPkt bit */
37599b4eaa6SAndrew Murray 	csr = MUSB_CSR0_H_REQPKT | MUSB_CSR0_H_STATUSPKT;
37699b4eaa6SAndrew Murray #if !defined(CONFIG_SOC_DM365)
37799b4eaa6SAndrew Murray 	csr |= MUSB_CSR0_H_DIS_PING;
37899b4eaa6SAndrew Murray #endif
3792731b9a8SJean-Christophe PLAGNIOL-VILLARD 	writew(csr, &musbr->txcsr);
3802731b9a8SJean-Christophe PLAGNIOL-VILLARD 	result = wait_until_ep0_ready(dev, MUSB_CSR0_H_REQPKT);
3812731b9a8SJean-Christophe PLAGNIOL-VILLARD 
3822731b9a8SJean-Christophe PLAGNIOL-VILLARD 	/* clear StatusPkt bit and RxPktRdy bit */
3832731b9a8SJean-Christophe PLAGNIOL-VILLARD 	csr = readw(&musbr->txcsr);
3842731b9a8SJean-Christophe PLAGNIOL-VILLARD 	csr &= ~(MUSB_CSR0_RXPKTRDY | MUSB_CSR0_H_STATUSPKT);
3852731b9a8SJean-Christophe PLAGNIOL-VILLARD 	writew(csr, &musbr->txcsr);
3862731b9a8SJean-Christophe PLAGNIOL-VILLARD 	return result;
3872731b9a8SJean-Christophe PLAGNIOL-VILLARD }
3882731b9a8SJean-Christophe PLAGNIOL-VILLARD 
3892731b9a8SJean-Christophe PLAGNIOL-VILLARD /*
3902731b9a8SJean-Christophe PLAGNIOL-VILLARD  * determines the speed of the device (High/Full/Slow)
3912731b9a8SJean-Christophe PLAGNIOL-VILLARD  */
get_dev_speed(struct usb_device * dev)3922731b9a8SJean-Christophe PLAGNIOL-VILLARD static u8 get_dev_speed(struct usb_device *dev)
3932731b9a8SJean-Christophe PLAGNIOL-VILLARD {
394c60795f4SIlya Yanok 	return (dev->speed == USB_SPEED_HIGH) ? MUSB_TYPE_SPEED_HIGH :
395c60795f4SIlya Yanok 		((dev->speed == USB_SPEED_LOW) ? MUSB_TYPE_SPEED_LOW :
3962731b9a8SJean-Christophe PLAGNIOL-VILLARD 						MUSB_TYPE_SPEED_FULL);
3972731b9a8SJean-Christophe PLAGNIOL-VILLARD }
3982731b9a8SJean-Christophe PLAGNIOL-VILLARD 
3992731b9a8SJean-Christophe PLAGNIOL-VILLARD /*
4002731b9a8SJean-Christophe PLAGNIOL-VILLARD  * configure the hub address and the port address.
4012731b9a8SJean-Christophe PLAGNIOL-VILLARD  */
config_hub_port(struct usb_device * dev,u8 ep)4022731b9a8SJean-Christophe PLAGNIOL-VILLARD static void config_hub_port(struct usb_device *dev, u8 ep)
4032731b9a8SJean-Christophe PLAGNIOL-VILLARD {
4042731b9a8SJean-Christophe PLAGNIOL-VILLARD 	u8 chid;
4052731b9a8SJean-Christophe PLAGNIOL-VILLARD 	u8 hub;
4062731b9a8SJean-Christophe PLAGNIOL-VILLARD 
4072731b9a8SJean-Christophe PLAGNIOL-VILLARD 	/* Find out the nearest parent which is high speed */
4082731b9a8SJean-Christophe PLAGNIOL-VILLARD 	while (dev->parent->parent != NULL)
4092731b9a8SJean-Christophe PLAGNIOL-VILLARD 		if (get_dev_speed(dev->parent) !=  MUSB_TYPE_SPEED_HIGH)
4102731b9a8SJean-Christophe PLAGNIOL-VILLARD 			dev = dev->parent;
4112731b9a8SJean-Christophe PLAGNIOL-VILLARD 		else
4122731b9a8SJean-Christophe PLAGNIOL-VILLARD 			break;
4132731b9a8SJean-Christophe PLAGNIOL-VILLARD 
4142731b9a8SJean-Christophe PLAGNIOL-VILLARD 	/* determine the port address at that hub */
4152731b9a8SJean-Christophe PLAGNIOL-VILLARD 	hub = dev->parent->devnum;
4162731b9a8SJean-Christophe PLAGNIOL-VILLARD 	for (chid = 0; chid < USB_MAXCHILDREN; chid++)
4172731b9a8SJean-Christophe PLAGNIOL-VILLARD 		if (dev->parent->children[chid] == dev)
4182731b9a8SJean-Christophe PLAGNIOL-VILLARD 			break;
4192731b9a8SJean-Christophe PLAGNIOL-VILLARD 
4208868fd44SBryan Wu #ifndef MUSB_NO_MULTIPOINT
4212731b9a8SJean-Christophe PLAGNIOL-VILLARD 	/* configure the hub address and the port address */
4222731b9a8SJean-Christophe PLAGNIOL-VILLARD 	writeb(hub, &musbr->tar[ep].txhubaddr);
4232731b9a8SJean-Christophe PLAGNIOL-VILLARD 	writeb((chid + 1), &musbr->tar[ep].txhubport);
4242731b9a8SJean-Christophe PLAGNIOL-VILLARD 	writeb(hub, &musbr->tar[ep].rxhubaddr);
4252731b9a8SJean-Christophe PLAGNIOL-VILLARD 	writeb((chid + 1), &musbr->tar[ep].rxhubport);
4268868fd44SBryan Wu #endif
4272731b9a8SJean-Christophe PLAGNIOL-VILLARD }
4282731b9a8SJean-Christophe PLAGNIOL-VILLARD 
429321790f6SBryan Wu #ifdef MUSB_NO_MULTIPOINT
430321790f6SBryan Wu 
musb_port_reset(int do_reset)431321790f6SBryan Wu static void musb_port_reset(int do_reset)
432321790f6SBryan Wu {
433321790f6SBryan Wu 	u8 power = readb(&musbr->power);
434321790f6SBryan Wu 
435321790f6SBryan Wu 	if (do_reset) {
436321790f6SBryan Wu 		power &= 0xf0;
437321790f6SBryan Wu 		writeb(power | MUSB_POWER_RESET, &musbr->power);
438321790f6SBryan Wu 		port_status |= USB_PORT_STAT_RESET;
439321790f6SBryan Wu 		port_status &= ~USB_PORT_STAT_ENABLE;
440321790f6SBryan Wu 		udelay(30000);
441321790f6SBryan Wu 	} else {
442321790f6SBryan Wu 		writeb(power & ~MUSB_POWER_RESET, &musbr->power);
443321790f6SBryan Wu 
444321790f6SBryan Wu 		power = readb(&musbr->power);
445321790f6SBryan Wu 		if (power & MUSB_POWER_HSMODE)
446321790f6SBryan Wu 			port_status |= USB_PORT_STAT_HIGH_SPEED;
447321790f6SBryan Wu 
448321790f6SBryan Wu 		port_status &= ~(USB_PORT_STAT_RESET | (USB_PORT_STAT_C_CONNECTION << 16));
449321790f6SBryan Wu 		port_status |= USB_PORT_STAT_ENABLE
450321790f6SBryan Wu 			| (USB_PORT_STAT_C_RESET << 16)
451321790f6SBryan Wu 			| (USB_PORT_STAT_C_ENABLE << 16);
452321790f6SBryan Wu 	}
453321790f6SBryan Wu }
454321790f6SBryan Wu 
455321790f6SBryan Wu /*
456321790f6SBryan Wu  * root hub control
457321790f6SBryan Wu  */
musb_submit_rh_msg(struct usb_device * dev,unsigned long pipe,void * buffer,int transfer_len,struct devrequest * cmd)458321790f6SBryan Wu static int musb_submit_rh_msg(struct usb_device *dev, unsigned long pipe,
459321790f6SBryan Wu 			      void *buffer, int transfer_len,
460321790f6SBryan Wu 			      struct devrequest *cmd)
461321790f6SBryan Wu {
462321790f6SBryan Wu 	int leni = transfer_len;
463321790f6SBryan Wu 	int len = 0;
464321790f6SBryan Wu 	int stat = 0;
465321790f6SBryan Wu 	u32 datab[4];
4660228348eSMike Frysinger 	const u8 *data_buf = (u8 *) datab;
467321790f6SBryan Wu 	u16 bmRType_bReq;
468321790f6SBryan Wu 	u16 wValue;
469321790f6SBryan Wu 	u16 wIndex;
470321790f6SBryan Wu 	u16 wLength;
471321790f6SBryan Wu 	u16 int_usb;
472321790f6SBryan Wu 
473321790f6SBryan Wu 	if ((pipe & PIPE_INTERRUPT) == PIPE_INTERRUPT) {
474321790f6SBryan Wu 		debug("Root-Hub submit IRQ: NOT implemented\n");
475321790f6SBryan Wu 		return 0;
476321790f6SBryan Wu 	}
477321790f6SBryan Wu 
478321790f6SBryan Wu 	bmRType_bReq = cmd->requesttype | (cmd->request << 8);
479321790f6SBryan Wu 	wValue = swap_16(cmd->value);
480321790f6SBryan Wu 	wIndex = swap_16(cmd->index);
481321790f6SBryan Wu 	wLength = swap_16(cmd->length);
482321790f6SBryan Wu 
483321790f6SBryan Wu 	debug("--- HUB ----------------------------------------\n");
484321790f6SBryan Wu 	debug("submit rh urb, req=%x val=%#x index=%#x len=%d\n",
485321790f6SBryan Wu 	    bmRType_bReq, wValue, wIndex, wLength);
486321790f6SBryan Wu 	debug("------------------------------------------------\n");
487321790f6SBryan Wu 
488321790f6SBryan Wu 	switch (bmRType_bReq) {
489321790f6SBryan Wu 	case RH_GET_STATUS:
490321790f6SBryan Wu 		debug("RH_GET_STATUS\n");
491321790f6SBryan Wu 
492321790f6SBryan Wu 		*(__u16 *) data_buf = swap_16(1);
493321790f6SBryan Wu 		len = 2;
494321790f6SBryan Wu 		break;
495321790f6SBryan Wu 
496321790f6SBryan Wu 	case RH_GET_STATUS | RH_INTERFACE:
497321790f6SBryan Wu 		debug("RH_GET_STATUS | RH_INTERFACE\n");
498321790f6SBryan Wu 
499321790f6SBryan Wu 		*(__u16 *) data_buf = swap_16(0);
500321790f6SBryan Wu 		len = 2;
501321790f6SBryan Wu 		break;
502321790f6SBryan Wu 
503321790f6SBryan Wu 	case RH_GET_STATUS | RH_ENDPOINT:
504321790f6SBryan Wu 		debug("RH_GET_STATUS | RH_ENDPOINT\n");
505321790f6SBryan Wu 
506321790f6SBryan Wu 		*(__u16 *) data_buf = swap_16(0);
507321790f6SBryan Wu 		len = 2;
508321790f6SBryan Wu 		break;
509321790f6SBryan Wu 
510321790f6SBryan Wu 	case RH_GET_STATUS | RH_CLASS:
511321790f6SBryan Wu 		debug("RH_GET_STATUS | RH_CLASS\n");
512321790f6SBryan Wu 
513321790f6SBryan Wu 		*(__u32 *) data_buf = swap_32(0);
514321790f6SBryan Wu 		len = 4;
515321790f6SBryan Wu 		break;
516321790f6SBryan Wu 
517321790f6SBryan Wu 	case RH_GET_STATUS | RH_OTHER | RH_CLASS:
518321790f6SBryan Wu 		debug("RH_GET_STATUS | RH_OTHER | RH_CLASS\n");
519321790f6SBryan Wu 
520321790f6SBryan Wu 		int_usb = readw(&musbr->intrusb);
521321790f6SBryan Wu 		if (int_usb & MUSB_INTR_CONNECT) {
522321790f6SBryan Wu 			port_status |= USB_PORT_STAT_CONNECTION
523321790f6SBryan Wu 				| (USB_PORT_STAT_C_CONNECTION << 16);
524321790f6SBryan Wu 			port_status |= USB_PORT_STAT_HIGH_SPEED
525321790f6SBryan Wu 				| USB_PORT_STAT_ENABLE;
526321790f6SBryan Wu 		}
527321790f6SBryan Wu 
528321790f6SBryan Wu 		if (port_status & USB_PORT_STAT_RESET)
529321790f6SBryan Wu 			musb_port_reset(0);
530321790f6SBryan Wu 
531321790f6SBryan Wu 		*(__u32 *) data_buf = swap_32(port_status);
532321790f6SBryan Wu 		len = 4;
533321790f6SBryan Wu 		break;
534321790f6SBryan Wu 
535321790f6SBryan Wu 	case RH_CLEAR_FEATURE | RH_ENDPOINT:
536321790f6SBryan Wu 		debug("RH_CLEAR_FEATURE | RH_ENDPOINT\n");
537321790f6SBryan Wu 
538321790f6SBryan Wu 		switch (wValue) {
539321790f6SBryan Wu 		case RH_ENDPOINT_STALL:
540321790f6SBryan Wu 			debug("C_HUB_ENDPOINT_STALL\n");
541321790f6SBryan Wu 			len = 0;
542321790f6SBryan Wu 			break;
543321790f6SBryan Wu 		}
544321790f6SBryan Wu 		port_status &= ~(1 << wValue);
545321790f6SBryan Wu 		break;
546321790f6SBryan Wu 
547321790f6SBryan Wu 	case RH_CLEAR_FEATURE | RH_CLASS:
548321790f6SBryan Wu 		debug("RH_CLEAR_FEATURE | RH_CLASS\n");
549321790f6SBryan Wu 
550321790f6SBryan Wu 		switch (wValue) {
551321790f6SBryan Wu 		case RH_C_HUB_LOCAL_POWER:
552321790f6SBryan Wu 			debug("C_HUB_LOCAL_POWER\n");
553321790f6SBryan Wu 			len = 0;
554321790f6SBryan Wu 			break;
555321790f6SBryan Wu 
556321790f6SBryan Wu 		case RH_C_HUB_OVER_CURRENT:
557321790f6SBryan Wu 			debug("C_HUB_OVER_CURRENT\n");
558321790f6SBryan Wu 			len = 0;
559321790f6SBryan Wu 			break;
560321790f6SBryan Wu 		}
561321790f6SBryan Wu 		port_status &= ~(1 << wValue);
562321790f6SBryan Wu 		break;
563321790f6SBryan Wu 
564321790f6SBryan Wu 	case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS:
565321790f6SBryan Wu 		debug("RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS\n");
566321790f6SBryan Wu 
567321790f6SBryan Wu 		switch (wValue) {
568321790f6SBryan Wu 		case RH_PORT_ENABLE:
569321790f6SBryan Wu 			len = 0;
570321790f6SBryan Wu 			break;
571321790f6SBryan Wu 
572321790f6SBryan Wu 		case RH_PORT_SUSPEND:
573321790f6SBryan Wu 			len = 0;
574321790f6SBryan Wu 			break;
575321790f6SBryan Wu 
576321790f6SBryan Wu 		case RH_PORT_POWER:
577321790f6SBryan Wu 			len = 0;
578321790f6SBryan Wu 			break;
579321790f6SBryan Wu 
580321790f6SBryan Wu 		case RH_C_PORT_CONNECTION:
581321790f6SBryan Wu 			len = 0;
582321790f6SBryan Wu 			break;
583321790f6SBryan Wu 
584321790f6SBryan Wu 		case RH_C_PORT_ENABLE:
585321790f6SBryan Wu 			len = 0;
586321790f6SBryan Wu 			break;
587321790f6SBryan Wu 
588321790f6SBryan Wu 		case RH_C_PORT_SUSPEND:
589321790f6SBryan Wu 			len = 0;
590321790f6SBryan Wu 			break;
591321790f6SBryan Wu 
592321790f6SBryan Wu 		case RH_C_PORT_OVER_CURRENT:
593321790f6SBryan Wu 			len = 0;
594321790f6SBryan Wu 			break;
595321790f6SBryan Wu 
596321790f6SBryan Wu 		case RH_C_PORT_RESET:
597321790f6SBryan Wu 			len = 0;
598321790f6SBryan Wu 			break;
599321790f6SBryan Wu 
600321790f6SBryan Wu 		default:
601321790f6SBryan Wu 			debug("invalid wValue\n");
602321790f6SBryan Wu 			stat = USB_ST_STALLED;
603321790f6SBryan Wu 		}
604321790f6SBryan Wu 
605321790f6SBryan Wu 		port_status &= ~(1 << wValue);
606321790f6SBryan Wu 		break;
607321790f6SBryan Wu 
608321790f6SBryan Wu 	case RH_SET_FEATURE | RH_OTHER | RH_CLASS:
609321790f6SBryan Wu 		debug("RH_SET_FEATURE | RH_OTHER | RH_CLASS\n");
610321790f6SBryan Wu 
611321790f6SBryan Wu 		switch (wValue) {
612321790f6SBryan Wu 		case RH_PORT_SUSPEND:
613321790f6SBryan Wu 			len = 0;
614321790f6SBryan Wu 			break;
615321790f6SBryan Wu 
616321790f6SBryan Wu 		case RH_PORT_RESET:
617321790f6SBryan Wu 			musb_port_reset(1);
618321790f6SBryan Wu 			len = 0;
619321790f6SBryan Wu 			break;
620321790f6SBryan Wu 
621321790f6SBryan Wu 		case RH_PORT_POWER:
622321790f6SBryan Wu 			len = 0;
623321790f6SBryan Wu 			break;
624321790f6SBryan Wu 
625321790f6SBryan Wu 		case RH_PORT_ENABLE:
626321790f6SBryan Wu 			len = 0;
627321790f6SBryan Wu 			break;
628321790f6SBryan Wu 
629321790f6SBryan Wu 		default:
630321790f6SBryan Wu 			debug("invalid wValue\n");
631321790f6SBryan Wu 			stat = USB_ST_STALLED;
632321790f6SBryan Wu 		}
633321790f6SBryan Wu 
634321790f6SBryan Wu 		port_status |= 1 << wValue;
635321790f6SBryan Wu 		break;
636321790f6SBryan Wu 
637321790f6SBryan Wu 	case RH_SET_ADDRESS:
638321790f6SBryan Wu 		debug("RH_SET_ADDRESS\n");
639321790f6SBryan Wu 
640321790f6SBryan Wu 		rh_devnum = wValue;
641321790f6SBryan Wu 		len = 0;
642321790f6SBryan Wu 		break;
643321790f6SBryan Wu 
644321790f6SBryan Wu 	case RH_GET_DESCRIPTOR:
645321790f6SBryan Wu 		debug("RH_GET_DESCRIPTOR: %x, %d\n", wValue, wLength);
646321790f6SBryan Wu 
647321790f6SBryan Wu 		switch (wValue) {
648321790f6SBryan Wu 		case (USB_DT_DEVICE << 8):	/* device descriptor */
649321790f6SBryan Wu 			len = min_t(unsigned int,
650321790f6SBryan Wu 				    leni, min_t(unsigned int,
651321790f6SBryan Wu 						sizeof(root_hub_dev_des),
652321790f6SBryan Wu 						wLength));
653321790f6SBryan Wu 			data_buf = root_hub_dev_des;
654321790f6SBryan Wu 			break;
655321790f6SBryan Wu 
656321790f6SBryan Wu 		case (USB_DT_CONFIG << 8):	/* configuration descriptor */
657321790f6SBryan Wu 			len = min_t(unsigned int,
658321790f6SBryan Wu 				    leni, min_t(unsigned int,
659321790f6SBryan Wu 						sizeof(root_hub_config_des),
660321790f6SBryan Wu 						wLength));
661321790f6SBryan Wu 			data_buf = root_hub_config_des;
662321790f6SBryan Wu 			break;
663321790f6SBryan Wu 
664321790f6SBryan Wu 		case ((USB_DT_STRING << 8) | 0x00):	/* string 0 descriptors */
665321790f6SBryan Wu 			len = min_t(unsigned int,
666321790f6SBryan Wu 				    leni, min_t(unsigned int,
667321790f6SBryan Wu 						sizeof(root_hub_str_index0),
668321790f6SBryan Wu 						wLength));
669321790f6SBryan Wu 			data_buf = root_hub_str_index0;
670321790f6SBryan Wu 			break;
671321790f6SBryan Wu 
672321790f6SBryan Wu 		case ((USB_DT_STRING << 8) | 0x01):	/* string 1 descriptors */
673321790f6SBryan Wu 			len = min_t(unsigned int,
674321790f6SBryan Wu 				    leni, min_t(unsigned int,
675321790f6SBryan Wu 						sizeof(root_hub_str_index1),
676321790f6SBryan Wu 						wLength));
677321790f6SBryan Wu 			data_buf = root_hub_str_index1;
678321790f6SBryan Wu 			break;
679321790f6SBryan Wu 
680321790f6SBryan Wu 		default:
681321790f6SBryan Wu 			debug("invalid wValue\n");
682321790f6SBryan Wu 			stat = USB_ST_STALLED;
683321790f6SBryan Wu 		}
684321790f6SBryan Wu 
685321790f6SBryan Wu 		break;
686321790f6SBryan Wu 
6870228348eSMike Frysinger 	case RH_GET_DESCRIPTOR | RH_CLASS: {
6880228348eSMike Frysinger 		u8 *_data_buf = (u8 *) datab;
689321790f6SBryan Wu 		debug("RH_GET_DESCRIPTOR | RH_CLASS\n");
690321790f6SBryan Wu 
6910228348eSMike Frysinger 		_data_buf[0] = 0x09;	/* min length; */
6920228348eSMike Frysinger 		_data_buf[1] = 0x29;
6930228348eSMike Frysinger 		_data_buf[2] = 0x1;	/* 1 port */
6940228348eSMike Frysinger 		_data_buf[3] = 0x01;	/* per-port power switching */
6950228348eSMike Frysinger 		_data_buf[3] |= 0x10;	/* no overcurrent reporting */
696321790f6SBryan Wu 
697321790f6SBryan Wu 		/* Corresponds to data_buf[4-7] */
6980228348eSMike Frysinger 		_data_buf[4] = 0;
6990228348eSMike Frysinger 		_data_buf[5] = 5;
7000228348eSMike Frysinger 		_data_buf[6] = 0;
7010228348eSMike Frysinger 		_data_buf[7] = 0x02;
7020228348eSMike Frysinger 		_data_buf[8] = 0xff;
703321790f6SBryan Wu 
704321790f6SBryan Wu 		len = min_t(unsigned int, leni,
705321790f6SBryan Wu 			    min_t(unsigned int, data_buf[0], wLength));
706321790f6SBryan Wu 		break;
7070228348eSMike Frysinger 	}
708321790f6SBryan Wu 
709321790f6SBryan Wu 	case RH_GET_CONFIGURATION:
710321790f6SBryan Wu 		debug("RH_GET_CONFIGURATION\n");
711321790f6SBryan Wu 
712321790f6SBryan Wu 		*(__u8 *) data_buf = 0x01;
713321790f6SBryan Wu 		len = 1;
714321790f6SBryan Wu 		break;
715321790f6SBryan Wu 
716321790f6SBryan Wu 	case RH_SET_CONFIGURATION:
717321790f6SBryan Wu 		debug("RH_SET_CONFIGURATION\n");
718321790f6SBryan Wu 
719321790f6SBryan Wu 		len = 0;
720321790f6SBryan Wu 		break;
721321790f6SBryan Wu 
722321790f6SBryan Wu 	default:
723321790f6SBryan Wu 		debug("*** *** *** unsupported root hub command *** *** ***\n");
724321790f6SBryan Wu 		stat = USB_ST_STALLED;
725321790f6SBryan Wu 	}
726321790f6SBryan Wu 
727321790f6SBryan Wu 	len = min_t(int, len, leni);
728321790f6SBryan Wu 	if (buffer != data_buf)
729321790f6SBryan Wu 		memcpy(buffer, data_buf, len);
730321790f6SBryan Wu 
731321790f6SBryan Wu 	dev->act_len = len;
732321790f6SBryan Wu 	dev->status = stat;
733b9743081SMike Frysinger 	debug("dev act_len %d, status %lu\n", dev->act_len, dev->status);
734321790f6SBryan Wu 
735321790f6SBryan Wu 	return stat;
736321790f6SBryan Wu }
737321790f6SBryan Wu 
musb_rh_init(void)738321790f6SBryan Wu static void musb_rh_init(void)
739321790f6SBryan Wu {
740321790f6SBryan Wu 	rh_devnum = 0;
741321790f6SBryan Wu 	port_status = 0;
742321790f6SBryan Wu }
743321790f6SBryan Wu 
744321790f6SBryan Wu #else
745321790f6SBryan Wu 
musb_rh_init(void)746321790f6SBryan Wu static void musb_rh_init(void) {}
747321790f6SBryan Wu 
748321790f6SBryan Wu #endif
749321790f6SBryan Wu 
7502731b9a8SJean-Christophe PLAGNIOL-VILLARD /*
7512731b9a8SJean-Christophe PLAGNIOL-VILLARD  * do a control transfer
7522731b9a8SJean-Christophe PLAGNIOL-VILLARD  */
submit_control_msg(struct usb_device * dev,unsigned long pipe,void * buffer,int len,struct devrequest * setup)7532731b9a8SJean-Christophe PLAGNIOL-VILLARD int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
7542731b9a8SJean-Christophe PLAGNIOL-VILLARD 			int len, struct devrequest *setup)
7552731b9a8SJean-Christophe PLAGNIOL-VILLARD {
7562731b9a8SJean-Christophe PLAGNIOL-VILLARD 	int devnum = usb_pipedevice(pipe);
7572731b9a8SJean-Christophe PLAGNIOL-VILLARD 	u8  devspeed;
7582731b9a8SJean-Christophe PLAGNIOL-VILLARD 
759321790f6SBryan Wu #ifdef MUSB_NO_MULTIPOINT
760321790f6SBryan Wu 	/* Control message is for the HUB? */
761b17ce92aSCliff Cai 	if (devnum == rh_devnum) {
762b17ce92aSCliff Cai 		int stat = musb_submit_rh_msg(dev, pipe, buffer, len, setup);
763b17ce92aSCliff Cai 		if (stat)
764b17ce92aSCliff Cai 			return stat;
765b17ce92aSCliff Cai 	}
766321790f6SBryan Wu #endif
767321790f6SBryan Wu 
7682731b9a8SJean-Christophe PLAGNIOL-VILLARD 	/* select control endpoint */
7692731b9a8SJean-Christophe PLAGNIOL-VILLARD 	writeb(MUSB_CONTROL_EP, &musbr->index);
77094191960SAnatolij Gustschin 	readw(&musbr->txcsr);
7712731b9a8SJean-Christophe PLAGNIOL-VILLARD 
7728868fd44SBryan Wu #ifndef MUSB_NO_MULTIPOINT
7732731b9a8SJean-Christophe PLAGNIOL-VILLARD 	/* target addr and (for multipoint) hub addr/port */
7742731b9a8SJean-Christophe PLAGNIOL-VILLARD 	writeb(devnum, &musbr->tar[MUSB_CONTROL_EP].txfuncaddr);
7752731b9a8SJean-Christophe PLAGNIOL-VILLARD 	writeb(devnum, &musbr->tar[MUSB_CONTROL_EP].rxfuncaddr);
7768868fd44SBryan Wu #endif
7772731b9a8SJean-Christophe PLAGNIOL-VILLARD 
7782731b9a8SJean-Christophe PLAGNIOL-VILLARD 	/* configure the hub address and the port number as required */
7792731b9a8SJean-Christophe PLAGNIOL-VILLARD 	devspeed = get_dev_speed(dev);
7802731b9a8SJean-Christophe PLAGNIOL-VILLARD 	if ((musb_ishighspeed()) && (dev->parent != NULL) &&
7812731b9a8SJean-Christophe PLAGNIOL-VILLARD 		(devspeed != MUSB_TYPE_SPEED_HIGH)) {
7822731b9a8SJean-Christophe PLAGNIOL-VILLARD 		config_hub_port(dev, MUSB_CONTROL_EP);
7832731b9a8SJean-Christophe PLAGNIOL-VILLARD 		writeb(devspeed << 6, &musbr->txtype);
7842731b9a8SJean-Christophe PLAGNIOL-VILLARD 	} else {
7852731b9a8SJean-Christophe PLAGNIOL-VILLARD 		writeb(musb_cfg.musb_speed << 6, &musbr->txtype);
7868868fd44SBryan Wu #ifndef MUSB_NO_MULTIPOINT
7872731b9a8SJean-Christophe PLAGNIOL-VILLARD 		writeb(0, &musbr->tar[MUSB_CONTROL_EP].txhubaddr);
7882731b9a8SJean-Christophe PLAGNIOL-VILLARD 		writeb(0, &musbr->tar[MUSB_CONTROL_EP].txhubport);
7892731b9a8SJean-Christophe PLAGNIOL-VILLARD 		writeb(0, &musbr->tar[MUSB_CONTROL_EP].rxhubaddr);
7902731b9a8SJean-Christophe PLAGNIOL-VILLARD 		writeb(0, &musbr->tar[MUSB_CONTROL_EP].rxhubport);
7918868fd44SBryan Wu #endif
7922731b9a8SJean-Christophe PLAGNIOL-VILLARD 	}
7932731b9a8SJean-Christophe PLAGNIOL-VILLARD 
7942731b9a8SJean-Christophe PLAGNIOL-VILLARD 	/* Control transfer setup phase */
7952731b9a8SJean-Christophe PLAGNIOL-VILLARD 	if (ctrlreq_setup_phase(dev, setup) < 0)
7962731b9a8SJean-Christophe PLAGNIOL-VILLARD 		return 0;
7972731b9a8SJean-Christophe PLAGNIOL-VILLARD 
7982731b9a8SJean-Christophe PLAGNIOL-VILLARD 	switch (setup->request) {
7992731b9a8SJean-Christophe PLAGNIOL-VILLARD 	case USB_REQ_GET_DESCRIPTOR:
8002731b9a8SJean-Christophe PLAGNIOL-VILLARD 	case USB_REQ_GET_CONFIGURATION:
8012731b9a8SJean-Christophe PLAGNIOL-VILLARD 	case USB_REQ_GET_INTERFACE:
8022731b9a8SJean-Christophe PLAGNIOL-VILLARD 	case USB_REQ_GET_STATUS:
8032731b9a8SJean-Christophe PLAGNIOL-VILLARD 	case USB_MSC_BBB_GET_MAX_LUN:
8042731b9a8SJean-Christophe PLAGNIOL-VILLARD 		/* control transfer in-data-phase */
8052731b9a8SJean-Christophe PLAGNIOL-VILLARD 		if (ctrlreq_in_data_phase(dev, len, buffer) < 0)
8062731b9a8SJean-Christophe PLAGNIOL-VILLARD 			return 0;
8072731b9a8SJean-Christophe PLAGNIOL-VILLARD 		/* control transfer out-status-phase */
8082731b9a8SJean-Christophe PLAGNIOL-VILLARD 		if (ctrlreq_out_status_phase(dev) < 0)
8092731b9a8SJean-Christophe PLAGNIOL-VILLARD 			return 0;
8102731b9a8SJean-Christophe PLAGNIOL-VILLARD 		break;
8112731b9a8SJean-Christophe PLAGNIOL-VILLARD 
8122731b9a8SJean-Christophe PLAGNIOL-VILLARD 	case USB_REQ_SET_ADDRESS:
8132731b9a8SJean-Christophe PLAGNIOL-VILLARD 	case USB_REQ_SET_CONFIGURATION:
8142731b9a8SJean-Christophe PLAGNIOL-VILLARD 	case USB_REQ_SET_FEATURE:
8152731b9a8SJean-Christophe PLAGNIOL-VILLARD 	case USB_REQ_SET_INTERFACE:
8162731b9a8SJean-Christophe PLAGNIOL-VILLARD 	case USB_REQ_CLEAR_FEATURE:
8172731b9a8SJean-Christophe PLAGNIOL-VILLARD 	case USB_MSC_BBB_RESET:
8182731b9a8SJean-Christophe PLAGNIOL-VILLARD 		/* control transfer in status phase */
8192731b9a8SJean-Christophe PLAGNIOL-VILLARD 		if (ctrlreq_in_status_phase(dev) < 0)
8202731b9a8SJean-Christophe PLAGNIOL-VILLARD 			return 0;
8212731b9a8SJean-Christophe PLAGNIOL-VILLARD 		break;
8222731b9a8SJean-Christophe PLAGNIOL-VILLARD 
8232731b9a8SJean-Christophe PLAGNIOL-VILLARD 	case USB_REQ_SET_DESCRIPTOR:
8242731b9a8SJean-Christophe PLAGNIOL-VILLARD 		/* control transfer out data phase */
8252731b9a8SJean-Christophe PLAGNIOL-VILLARD 		if (ctrlreq_out_data_phase(dev, len, buffer) < 0)
8262731b9a8SJean-Christophe PLAGNIOL-VILLARD 			return 0;
8272731b9a8SJean-Christophe PLAGNIOL-VILLARD 		/* control transfer in status phase */
8282731b9a8SJean-Christophe PLAGNIOL-VILLARD 		if (ctrlreq_in_status_phase(dev) < 0)
8292731b9a8SJean-Christophe PLAGNIOL-VILLARD 			return 0;
8302731b9a8SJean-Christophe PLAGNIOL-VILLARD 		break;
8312731b9a8SJean-Christophe PLAGNIOL-VILLARD 
8322731b9a8SJean-Christophe PLAGNIOL-VILLARD 	default:
8332731b9a8SJean-Christophe PLAGNIOL-VILLARD 		/* unhandled control transfer */
8342731b9a8SJean-Christophe PLAGNIOL-VILLARD 		return -1;
8352731b9a8SJean-Christophe PLAGNIOL-VILLARD 	}
8362731b9a8SJean-Christophe PLAGNIOL-VILLARD 
8372731b9a8SJean-Christophe PLAGNIOL-VILLARD 	dev->status = 0;
8382731b9a8SJean-Christophe PLAGNIOL-VILLARD 	dev->act_len = len;
83993ceb479SBryan Wu 
84093ceb479SBryan Wu #ifdef MUSB_NO_MULTIPOINT
84193ceb479SBryan Wu 	/* Set device address to USB_FADDR register */
84293ceb479SBryan Wu 	if (setup->request == USB_REQ_SET_ADDRESS)
84393ceb479SBryan Wu 		writeb(dev->devnum, &musbr->faddr);
84493ceb479SBryan Wu #endif
84593ceb479SBryan Wu 
8462731b9a8SJean-Christophe PLAGNIOL-VILLARD 	return len;
8472731b9a8SJean-Christophe PLAGNIOL-VILLARD }
8482731b9a8SJean-Christophe PLAGNIOL-VILLARD 
8492731b9a8SJean-Christophe PLAGNIOL-VILLARD /*
8502731b9a8SJean-Christophe PLAGNIOL-VILLARD  * do a bulk transfer
8512731b9a8SJean-Christophe PLAGNIOL-VILLARD  */
submit_bulk_msg(struct usb_device * dev,unsigned long pipe,void * buffer,int len)8522731b9a8SJean-Christophe PLAGNIOL-VILLARD int submit_bulk_msg(struct usb_device *dev, unsigned long pipe,
8532731b9a8SJean-Christophe PLAGNIOL-VILLARD 					void *buffer, int len)
8542731b9a8SJean-Christophe PLAGNIOL-VILLARD {
8552731b9a8SJean-Christophe PLAGNIOL-VILLARD 	int dir_out = usb_pipeout(pipe);
8562731b9a8SJean-Christophe PLAGNIOL-VILLARD 	int ep = usb_pipeendpoint(pipe);
8578868fd44SBryan Wu #ifndef MUSB_NO_MULTIPOINT
8582731b9a8SJean-Christophe PLAGNIOL-VILLARD 	int devnum = usb_pipedevice(pipe);
8598868fd44SBryan Wu #endif
8602731b9a8SJean-Christophe PLAGNIOL-VILLARD 	u8  type;
8612731b9a8SJean-Christophe PLAGNIOL-VILLARD 	u16 csr;
8622731b9a8SJean-Christophe PLAGNIOL-VILLARD 	u32 txlen = 0;
8632731b9a8SJean-Christophe PLAGNIOL-VILLARD 	u32 nextlen = 0;
8642731b9a8SJean-Christophe PLAGNIOL-VILLARD 	u8  devspeed;
8652731b9a8SJean-Christophe PLAGNIOL-VILLARD 
8662731b9a8SJean-Christophe PLAGNIOL-VILLARD 	/* select bulk endpoint */
8672731b9a8SJean-Christophe PLAGNIOL-VILLARD 	writeb(MUSB_BULK_EP, &musbr->index);
8682731b9a8SJean-Christophe PLAGNIOL-VILLARD 
8698868fd44SBryan Wu #ifndef MUSB_NO_MULTIPOINT
8702731b9a8SJean-Christophe PLAGNIOL-VILLARD 	/* write the address of the device */
8712731b9a8SJean-Christophe PLAGNIOL-VILLARD 	if (dir_out)
8722731b9a8SJean-Christophe PLAGNIOL-VILLARD 		writeb(devnum, &musbr->tar[MUSB_BULK_EP].txfuncaddr);
8732731b9a8SJean-Christophe PLAGNIOL-VILLARD 	else
8742731b9a8SJean-Christophe PLAGNIOL-VILLARD 		writeb(devnum, &musbr->tar[MUSB_BULK_EP].rxfuncaddr);
8758868fd44SBryan Wu #endif
8762731b9a8SJean-Christophe PLAGNIOL-VILLARD 
8772731b9a8SJean-Christophe PLAGNIOL-VILLARD 	/* configure the hub address and the port number as required */
8782731b9a8SJean-Christophe PLAGNIOL-VILLARD 	devspeed = get_dev_speed(dev);
8792731b9a8SJean-Christophe PLAGNIOL-VILLARD 	if ((musb_ishighspeed()) && (dev->parent != NULL) &&
8802731b9a8SJean-Christophe PLAGNIOL-VILLARD 		(devspeed != MUSB_TYPE_SPEED_HIGH)) {
8812731b9a8SJean-Christophe PLAGNIOL-VILLARD 		/*
8822731b9a8SJean-Christophe PLAGNIOL-VILLARD 		 * MUSB is in high speed and the destination device is full
8832731b9a8SJean-Christophe PLAGNIOL-VILLARD 		 * speed device. So configure the hub address and port
8842731b9a8SJean-Christophe PLAGNIOL-VILLARD 		 * address registers.
8852731b9a8SJean-Christophe PLAGNIOL-VILLARD 		 */
8862731b9a8SJean-Christophe PLAGNIOL-VILLARD 		config_hub_port(dev, MUSB_BULK_EP);
8872731b9a8SJean-Christophe PLAGNIOL-VILLARD 	} else {
8888868fd44SBryan Wu #ifndef MUSB_NO_MULTIPOINT
8892731b9a8SJean-Christophe PLAGNIOL-VILLARD 		if (dir_out) {
8902731b9a8SJean-Christophe PLAGNIOL-VILLARD 			writeb(0, &musbr->tar[MUSB_BULK_EP].txhubaddr);
8912731b9a8SJean-Christophe PLAGNIOL-VILLARD 			writeb(0, &musbr->tar[MUSB_BULK_EP].txhubport);
8922731b9a8SJean-Christophe PLAGNIOL-VILLARD 		} else {
8932731b9a8SJean-Christophe PLAGNIOL-VILLARD 			writeb(0, &musbr->tar[MUSB_BULK_EP].rxhubaddr);
8942731b9a8SJean-Christophe PLAGNIOL-VILLARD 			writeb(0, &musbr->tar[MUSB_BULK_EP].rxhubport);
8952731b9a8SJean-Christophe PLAGNIOL-VILLARD 		}
8968868fd44SBryan Wu #endif
8972731b9a8SJean-Christophe PLAGNIOL-VILLARD 		devspeed = musb_cfg.musb_speed;
8982731b9a8SJean-Christophe PLAGNIOL-VILLARD 	}
8992731b9a8SJean-Christophe PLAGNIOL-VILLARD 
9002731b9a8SJean-Christophe PLAGNIOL-VILLARD 	/* Write the saved toggle bit value */
9012731b9a8SJean-Christophe PLAGNIOL-VILLARD 	write_toggle(dev, ep, dir_out);
9022731b9a8SJean-Christophe PLAGNIOL-VILLARD 
9032731b9a8SJean-Christophe PLAGNIOL-VILLARD 	if (dir_out) { /* bulk-out transfer */
9042731b9a8SJean-Christophe PLAGNIOL-VILLARD 		/* Program the TxType register */
9052731b9a8SJean-Christophe PLAGNIOL-VILLARD 		type = (devspeed << MUSB_TYPE_SPEED_SHIFT) |
9062731b9a8SJean-Christophe PLAGNIOL-VILLARD 			   (MUSB_TYPE_PROTO_BULK << MUSB_TYPE_PROTO_SHIFT) |
9072731b9a8SJean-Christophe PLAGNIOL-VILLARD 			   (ep & MUSB_TYPE_REMOTE_END);
9082731b9a8SJean-Christophe PLAGNIOL-VILLARD 		writeb(type, &musbr->txtype);
9092731b9a8SJean-Christophe PLAGNIOL-VILLARD 
9102731b9a8SJean-Christophe PLAGNIOL-VILLARD 		/* Write maximum packet size to the TxMaxp register */
9112731b9a8SJean-Christophe PLAGNIOL-VILLARD 		writew(dev->epmaxpacketout[ep], &musbr->txmaxp);
9122731b9a8SJean-Christophe PLAGNIOL-VILLARD 		while (txlen < len) {
9132731b9a8SJean-Christophe PLAGNIOL-VILLARD 			nextlen = ((len-txlen) < dev->epmaxpacketout[ep]) ?
9142731b9a8SJean-Christophe PLAGNIOL-VILLARD 					(len-txlen) : dev->epmaxpacketout[ep];
9152731b9a8SJean-Christophe PLAGNIOL-VILLARD 
9162731b9a8SJean-Christophe PLAGNIOL-VILLARD 			/* Write the data to the FIFO */
9172731b9a8SJean-Christophe PLAGNIOL-VILLARD 			write_fifo(MUSB_BULK_EP, nextlen,
9182731b9a8SJean-Christophe PLAGNIOL-VILLARD 					(void *)(((u8 *)buffer) + txlen));
9192731b9a8SJean-Christophe PLAGNIOL-VILLARD 
9202731b9a8SJean-Christophe PLAGNIOL-VILLARD 			/* Set the TxPktRdy bit */
9212731b9a8SJean-Christophe PLAGNIOL-VILLARD 			csr = readw(&musbr->txcsr);
9222731b9a8SJean-Christophe PLAGNIOL-VILLARD 			writew(csr | MUSB_TXCSR_TXPKTRDY, &musbr->txcsr);
9232731b9a8SJean-Christophe PLAGNIOL-VILLARD 
9242731b9a8SJean-Christophe PLAGNIOL-VILLARD 			/* Wait until the TxPktRdy bit is cleared */
925ddd025bbSAndrew Murray 			if (wait_until_txep_ready(dev, MUSB_BULK_EP) != 1) {
9262731b9a8SJean-Christophe PLAGNIOL-VILLARD 				readw(&musbr->txcsr);
9272731b9a8SJean-Christophe PLAGNIOL-VILLARD 				usb_settoggle(dev, ep, dir_out,
9282731b9a8SJean-Christophe PLAGNIOL-VILLARD 				(csr >> MUSB_TXCSR_H_DATATOGGLE_SHIFT) & 1);
9292731b9a8SJean-Christophe PLAGNIOL-VILLARD 				dev->act_len = txlen;
9302731b9a8SJean-Christophe PLAGNIOL-VILLARD 				return 0;
9312731b9a8SJean-Christophe PLAGNIOL-VILLARD 			}
9322731b9a8SJean-Christophe PLAGNIOL-VILLARD 			txlen += nextlen;
9332731b9a8SJean-Christophe PLAGNIOL-VILLARD 		}
9342731b9a8SJean-Christophe PLAGNIOL-VILLARD 
9352731b9a8SJean-Christophe PLAGNIOL-VILLARD 		/* Keep a copy of the data toggle bit */
9362731b9a8SJean-Christophe PLAGNIOL-VILLARD 		csr = readw(&musbr->txcsr);
9372731b9a8SJean-Christophe PLAGNIOL-VILLARD 		usb_settoggle(dev, ep, dir_out,
9382731b9a8SJean-Christophe PLAGNIOL-VILLARD 				(csr >> MUSB_TXCSR_H_DATATOGGLE_SHIFT) & 1);
9392731b9a8SJean-Christophe PLAGNIOL-VILLARD 	} else { /* bulk-in transfer */
9402731b9a8SJean-Christophe PLAGNIOL-VILLARD 		/* Write the saved toggle bit value */
9412731b9a8SJean-Christophe PLAGNIOL-VILLARD 		write_toggle(dev, ep, dir_out);
9422731b9a8SJean-Christophe PLAGNIOL-VILLARD 
9432731b9a8SJean-Christophe PLAGNIOL-VILLARD 		/* Program the RxType register */
9442731b9a8SJean-Christophe PLAGNIOL-VILLARD 		type = (devspeed << MUSB_TYPE_SPEED_SHIFT) |
9452731b9a8SJean-Christophe PLAGNIOL-VILLARD 			   (MUSB_TYPE_PROTO_BULK << MUSB_TYPE_PROTO_SHIFT) |
9462731b9a8SJean-Christophe PLAGNIOL-VILLARD 			   (ep & MUSB_TYPE_REMOTE_END);
9472731b9a8SJean-Christophe PLAGNIOL-VILLARD 		writeb(type, &musbr->rxtype);
9482731b9a8SJean-Christophe PLAGNIOL-VILLARD 
9492731b9a8SJean-Christophe PLAGNIOL-VILLARD 		/* Write the maximum packet size to the RxMaxp register */
9502731b9a8SJean-Christophe PLAGNIOL-VILLARD 		writew(dev->epmaxpacketin[ep], &musbr->rxmaxp);
9512731b9a8SJean-Christophe PLAGNIOL-VILLARD 		while (txlen < len) {
9522731b9a8SJean-Christophe PLAGNIOL-VILLARD 			nextlen = ((len-txlen) < dev->epmaxpacketin[ep]) ?
9532731b9a8SJean-Christophe PLAGNIOL-VILLARD 					(len-txlen) : dev->epmaxpacketin[ep];
9542731b9a8SJean-Christophe PLAGNIOL-VILLARD 
9552731b9a8SJean-Christophe PLAGNIOL-VILLARD 			/* Set the ReqPkt bit */
956bc72a919SBryan Wu 			csr = readw(&musbr->rxcsr);
957bc72a919SBryan Wu 			writew(csr | MUSB_RXCSR_H_REQPKT, &musbr->rxcsr);
9582731b9a8SJean-Christophe PLAGNIOL-VILLARD 
9592731b9a8SJean-Christophe PLAGNIOL-VILLARD 			/* Wait until the RxPktRdy bit is set */
960ddd025bbSAndrew Murray 			if (wait_until_rxep_ready(dev, MUSB_BULK_EP) != 1) {
9612731b9a8SJean-Christophe PLAGNIOL-VILLARD 				csr = readw(&musbr->rxcsr);
9622731b9a8SJean-Christophe PLAGNIOL-VILLARD 				usb_settoggle(dev, ep, dir_out,
9632731b9a8SJean-Christophe PLAGNIOL-VILLARD 				(csr >> MUSB_S_RXCSR_H_DATATOGGLE) & 1);
9642731b9a8SJean-Christophe PLAGNIOL-VILLARD 				csr &= ~MUSB_RXCSR_RXPKTRDY;
9652731b9a8SJean-Christophe PLAGNIOL-VILLARD 				writew(csr, &musbr->rxcsr);
9662731b9a8SJean-Christophe PLAGNIOL-VILLARD 				dev->act_len = txlen;
9672731b9a8SJean-Christophe PLAGNIOL-VILLARD 				return 0;
9682731b9a8SJean-Christophe PLAGNIOL-VILLARD 			}
9692731b9a8SJean-Christophe PLAGNIOL-VILLARD 
9702731b9a8SJean-Christophe PLAGNIOL-VILLARD 			/* Read the data from the FIFO */
9712731b9a8SJean-Christophe PLAGNIOL-VILLARD 			read_fifo(MUSB_BULK_EP, nextlen,
9722731b9a8SJean-Christophe PLAGNIOL-VILLARD 					(void *)(((u8 *)buffer) + txlen));
9732731b9a8SJean-Christophe PLAGNIOL-VILLARD 
9742731b9a8SJean-Christophe PLAGNIOL-VILLARD 			/* Clear the RxPktRdy bit */
9752731b9a8SJean-Christophe PLAGNIOL-VILLARD 			csr =  readw(&musbr->rxcsr);
9762731b9a8SJean-Christophe PLAGNIOL-VILLARD 			csr &= ~MUSB_RXCSR_RXPKTRDY;
9772731b9a8SJean-Christophe PLAGNIOL-VILLARD 			writew(csr, &musbr->rxcsr);
9782731b9a8SJean-Christophe PLAGNIOL-VILLARD 			txlen += nextlen;
9792731b9a8SJean-Christophe PLAGNIOL-VILLARD 		}
9802731b9a8SJean-Christophe PLAGNIOL-VILLARD 
9812731b9a8SJean-Christophe PLAGNIOL-VILLARD 		/* Keep a copy of the data toggle bit */
9822731b9a8SJean-Christophe PLAGNIOL-VILLARD 		csr = readw(&musbr->rxcsr);
9832731b9a8SJean-Christophe PLAGNIOL-VILLARD 		usb_settoggle(dev, ep, dir_out,
9842731b9a8SJean-Christophe PLAGNIOL-VILLARD 				(csr >> MUSB_S_RXCSR_H_DATATOGGLE) & 1);
9852731b9a8SJean-Christophe PLAGNIOL-VILLARD 	}
9862731b9a8SJean-Christophe PLAGNIOL-VILLARD 
9872731b9a8SJean-Christophe PLAGNIOL-VILLARD 	/* bulk transfer is complete */
9882731b9a8SJean-Christophe PLAGNIOL-VILLARD 	dev->status = 0;
9892731b9a8SJean-Christophe PLAGNIOL-VILLARD 	dev->act_len = len;
9902731b9a8SJean-Christophe PLAGNIOL-VILLARD 	return 0;
9912731b9a8SJean-Christophe PLAGNIOL-VILLARD }
9922731b9a8SJean-Christophe PLAGNIOL-VILLARD 
9932731b9a8SJean-Christophe PLAGNIOL-VILLARD /*
9942731b9a8SJean-Christophe PLAGNIOL-VILLARD  * This function initializes the usb controller module.
9952731b9a8SJean-Christophe PLAGNIOL-VILLARD  */
usb_lowlevel_init(int index,enum usb_init_type init,void ** controller)99606d513ecSTroy Kisky int usb_lowlevel_init(int index, enum usb_init_type init, void **controller)
9972731b9a8SJean-Christophe PLAGNIOL-VILLARD {
9982731b9a8SJean-Christophe PLAGNIOL-VILLARD 	u8  power;
9992731b9a8SJean-Christophe PLAGNIOL-VILLARD 	u32 timeout;
10002731b9a8SJean-Christophe PLAGNIOL-VILLARD 
1001321790f6SBryan Wu 	musb_rh_init();
1002321790f6SBryan Wu 
10032731b9a8SJean-Christophe PLAGNIOL-VILLARD 	if (musb_platform_init() == -1)
10042731b9a8SJean-Christophe PLAGNIOL-VILLARD 		return -1;
10052731b9a8SJean-Christophe PLAGNIOL-VILLARD 
10062731b9a8SJean-Christophe PLAGNIOL-VILLARD 	/* Configure all the endpoint FIFO's and start usb controller */
10072731b9a8SJean-Christophe PLAGNIOL-VILLARD 	musbr = musb_cfg.regs;
1008e31dc61eSAxel Lin 	musb_configure_ep(&epinfo[0], ARRAY_SIZE(epinfo));
10092731b9a8SJean-Christophe PLAGNIOL-VILLARD 	musb_start();
10102731b9a8SJean-Christophe PLAGNIOL-VILLARD 
10112731b9a8SJean-Christophe PLAGNIOL-VILLARD 	/*
10122731b9a8SJean-Christophe PLAGNIOL-VILLARD 	 * Wait until musb is enabled in host mode with a timeout. There
10132731b9a8SJean-Christophe PLAGNIOL-VILLARD 	 * should be a usb device connected.
10142731b9a8SJean-Christophe PLAGNIOL-VILLARD 	 */
10152731b9a8SJean-Christophe PLAGNIOL-VILLARD 	timeout = musb_cfg.timeout;
10168c865018SMatej Frančeškin 	while (--timeout)
10172731b9a8SJean-Christophe PLAGNIOL-VILLARD 		if (readb(&musbr->devctl) & MUSB_DEVCTL_HM)
10182731b9a8SJean-Christophe PLAGNIOL-VILLARD 			break;
10192731b9a8SJean-Christophe PLAGNIOL-VILLARD 
10202731b9a8SJean-Christophe PLAGNIOL-VILLARD 	/* if musb core is not in host mode, then return */
10212731b9a8SJean-Christophe PLAGNIOL-VILLARD 	if (!timeout)
10222731b9a8SJean-Christophe PLAGNIOL-VILLARD 		return -1;
10232731b9a8SJean-Christophe PLAGNIOL-VILLARD 
10242731b9a8SJean-Christophe PLAGNIOL-VILLARD 	/* start usb bus reset */
10252731b9a8SJean-Christophe PLAGNIOL-VILLARD 	power = readb(&musbr->power);
10262731b9a8SJean-Christophe PLAGNIOL-VILLARD 	writeb(power | MUSB_POWER_RESET, &musbr->power);
10272731b9a8SJean-Christophe PLAGNIOL-VILLARD 
10282731b9a8SJean-Christophe PLAGNIOL-VILLARD 	/* After initiating a usb reset, wait for about 20ms to 30ms */
10292731b9a8SJean-Christophe PLAGNIOL-VILLARD 	udelay(30000);
10302731b9a8SJean-Christophe PLAGNIOL-VILLARD 
10312731b9a8SJean-Christophe PLAGNIOL-VILLARD 	/* stop usb bus reset */
10322731b9a8SJean-Christophe PLAGNIOL-VILLARD 	power = readb(&musbr->power);
10332731b9a8SJean-Christophe PLAGNIOL-VILLARD 	power &= ~MUSB_POWER_RESET;
10342731b9a8SJean-Christophe PLAGNIOL-VILLARD 	writeb(power, &musbr->power);
10352731b9a8SJean-Christophe PLAGNIOL-VILLARD 
10362731b9a8SJean-Christophe PLAGNIOL-VILLARD 	/* Determine if the connected device is a high/full/low speed device */
10372731b9a8SJean-Christophe PLAGNIOL-VILLARD 	musb_cfg.musb_speed = (readb(&musbr->power) & MUSB_POWER_HSMODE) ?
10382731b9a8SJean-Christophe PLAGNIOL-VILLARD 			MUSB_TYPE_SPEED_HIGH :
10392731b9a8SJean-Christophe PLAGNIOL-VILLARD 			((readb(&musbr->devctl) & MUSB_DEVCTL_FSDEV) ?
10402731b9a8SJean-Christophe PLAGNIOL-VILLARD 			MUSB_TYPE_SPEED_FULL : MUSB_TYPE_SPEED_LOW);
10412731b9a8SJean-Christophe PLAGNIOL-VILLARD 	return 0;
10422731b9a8SJean-Christophe PLAGNIOL-VILLARD }
10432731b9a8SJean-Christophe PLAGNIOL-VILLARD 
10442731b9a8SJean-Christophe PLAGNIOL-VILLARD /*
10452731b9a8SJean-Christophe PLAGNIOL-VILLARD  * This function stops the operation of the davinci usb module.
10462731b9a8SJean-Christophe PLAGNIOL-VILLARD  */
usb_lowlevel_stop(int index)1047c7e3b2b5SLucas Stach int usb_lowlevel_stop(int index)
10482731b9a8SJean-Christophe PLAGNIOL-VILLARD {
10492731b9a8SJean-Christophe PLAGNIOL-VILLARD 	/* Reset the USB module */
10502731b9a8SJean-Christophe PLAGNIOL-VILLARD 	musb_platform_deinit();
10512731b9a8SJean-Christophe PLAGNIOL-VILLARD 	writeb(0, &musbr->devctl);
10522731b9a8SJean-Christophe PLAGNIOL-VILLARD 	return 0;
10532731b9a8SJean-Christophe PLAGNIOL-VILLARD }
10542731b9a8SJean-Christophe PLAGNIOL-VILLARD 
10552731b9a8SJean-Christophe PLAGNIOL-VILLARD /*
10562731b9a8SJean-Christophe PLAGNIOL-VILLARD  * This function supports usb interrupt transfers. Currently, usb interrupt
10572731b9a8SJean-Christophe PLAGNIOL-VILLARD  * transfers are not supported.
10582731b9a8SJean-Christophe PLAGNIOL-VILLARD  */
submit_int_msg(struct usb_device * dev,unsigned long pipe,void * buffer,int len,int interval,bool nonblock)1059*c203be14SJean-Jacques Hiblot int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
1060*c203be14SJean-Jacques Hiblot 		   int len, int interval, bool nonblock)
10612731b9a8SJean-Christophe PLAGNIOL-VILLARD {
10622731b9a8SJean-Christophe PLAGNIOL-VILLARD 	int dir_out = usb_pipeout(pipe);
10632731b9a8SJean-Christophe PLAGNIOL-VILLARD 	int ep = usb_pipeendpoint(pipe);
10648868fd44SBryan Wu #ifndef MUSB_NO_MULTIPOINT
10652731b9a8SJean-Christophe PLAGNIOL-VILLARD 	int devnum = usb_pipedevice(pipe);
10668868fd44SBryan Wu #endif
10672731b9a8SJean-Christophe PLAGNIOL-VILLARD 	u8  type;
10682731b9a8SJean-Christophe PLAGNIOL-VILLARD 	u16 csr;
10692731b9a8SJean-Christophe PLAGNIOL-VILLARD 	u32 txlen = 0;
10702731b9a8SJean-Christophe PLAGNIOL-VILLARD 	u32 nextlen = 0;
10712731b9a8SJean-Christophe PLAGNIOL-VILLARD 	u8  devspeed;
10722731b9a8SJean-Christophe PLAGNIOL-VILLARD 
10732731b9a8SJean-Christophe PLAGNIOL-VILLARD 	/* select interrupt endpoint */
10742731b9a8SJean-Christophe PLAGNIOL-VILLARD 	writeb(MUSB_INTR_EP, &musbr->index);
10752731b9a8SJean-Christophe PLAGNIOL-VILLARD 
10768868fd44SBryan Wu #ifndef MUSB_NO_MULTIPOINT
10772731b9a8SJean-Christophe PLAGNIOL-VILLARD 	/* write the address of the device */
10782731b9a8SJean-Christophe PLAGNIOL-VILLARD 	if (dir_out)
10792731b9a8SJean-Christophe PLAGNIOL-VILLARD 		writeb(devnum, &musbr->tar[MUSB_INTR_EP].txfuncaddr);
10802731b9a8SJean-Christophe PLAGNIOL-VILLARD 	else
10812731b9a8SJean-Christophe PLAGNIOL-VILLARD 		writeb(devnum, &musbr->tar[MUSB_INTR_EP].rxfuncaddr);
10828868fd44SBryan Wu #endif
10832731b9a8SJean-Christophe PLAGNIOL-VILLARD 
10842731b9a8SJean-Christophe PLAGNIOL-VILLARD 	/* configure the hub address and the port number as required */
10852731b9a8SJean-Christophe PLAGNIOL-VILLARD 	devspeed = get_dev_speed(dev);
10862731b9a8SJean-Christophe PLAGNIOL-VILLARD 	if ((musb_ishighspeed()) && (dev->parent != NULL) &&
10872731b9a8SJean-Christophe PLAGNIOL-VILLARD 		(devspeed != MUSB_TYPE_SPEED_HIGH)) {
10882731b9a8SJean-Christophe PLAGNIOL-VILLARD 		/*
10892731b9a8SJean-Christophe PLAGNIOL-VILLARD 		 * MUSB is in high speed and the destination device is full
10902731b9a8SJean-Christophe PLAGNIOL-VILLARD 		 * speed device. So configure the hub address and port
10912731b9a8SJean-Christophe PLAGNIOL-VILLARD 		 * address registers.
10922731b9a8SJean-Christophe PLAGNIOL-VILLARD 		 */
10932731b9a8SJean-Christophe PLAGNIOL-VILLARD 		config_hub_port(dev, MUSB_INTR_EP);
10942731b9a8SJean-Christophe PLAGNIOL-VILLARD 	} else {
10958868fd44SBryan Wu #ifndef MUSB_NO_MULTIPOINT
10962731b9a8SJean-Christophe PLAGNIOL-VILLARD 		if (dir_out) {
10972731b9a8SJean-Christophe PLAGNIOL-VILLARD 			writeb(0, &musbr->tar[MUSB_INTR_EP].txhubaddr);
10982731b9a8SJean-Christophe PLAGNIOL-VILLARD 			writeb(0, &musbr->tar[MUSB_INTR_EP].txhubport);
10992731b9a8SJean-Christophe PLAGNIOL-VILLARD 		} else {
11002731b9a8SJean-Christophe PLAGNIOL-VILLARD 			writeb(0, &musbr->tar[MUSB_INTR_EP].rxhubaddr);
11012731b9a8SJean-Christophe PLAGNIOL-VILLARD 			writeb(0, &musbr->tar[MUSB_INTR_EP].rxhubport);
11022731b9a8SJean-Christophe PLAGNIOL-VILLARD 		}
11038868fd44SBryan Wu #endif
11042731b9a8SJean-Christophe PLAGNIOL-VILLARD 		devspeed = musb_cfg.musb_speed;
11052731b9a8SJean-Christophe PLAGNIOL-VILLARD 	}
11062731b9a8SJean-Christophe PLAGNIOL-VILLARD 
11072731b9a8SJean-Christophe PLAGNIOL-VILLARD 	/* Write the saved toggle bit value */
11082731b9a8SJean-Christophe PLAGNIOL-VILLARD 	write_toggle(dev, ep, dir_out);
11092731b9a8SJean-Christophe PLAGNIOL-VILLARD 
11102731b9a8SJean-Christophe PLAGNIOL-VILLARD 	if (!dir_out) { /* intrrupt-in transfer */
11112731b9a8SJean-Christophe PLAGNIOL-VILLARD 		/* Write the saved toggle bit value */
11122731b9a8SJean-Christophe PLAGNIOL-VILLARD 		write_toggle(dev, ep, dir_out);
11132731b9a8SJean-Christophe PLAGNIOL-VILLARD 		writeb(interval, &musbr->rxinterval);
11142731b9a8SJean-Christophe PLAGNIOL-VILLARD 
11152731b9a8SJean-Christophe PLAGNIOL-VILLARD 		/* Program the RxType register */
11162731b9a8SJean-Christophe PLAGNIOL-VILLARD 		type = (devspeed << MUSB_TYPE_SPEED_SHIFT) |
11172731b9a8SJean-Christophe PLAGNIOL-VILLARD 			   (MUSB_TYPE_PROTO_INTR << MUSB_TYPE_PROTO_SHIFT) |
11182731b9a8SJean-Christophe PLAGNIOL-VILLARD 			   (ep & MUSB_TYPE_REMOTE_END);
11192731b9a8SJean-Christophe PLAGNIOL-VILLARD 		writeb(type, &musbr->rxtype);
11202731b9a8SJean-Christophe PLAGNIOL-VILLARD 
11212731b9a8SJean-Christophe PLAGNIOL-VILLARD 		/* Write the maximum packet size to the RxMaxp register */
11222731b9a8SJean-Christophe PLAGNIOL-VILLARD 		writew(dev->epmaxpacketin[ep], &musbr->rxmaxp);
11232731b9a8SJean-Christophe PLAGNIOL-VILLARD 
11242731b9a8SJean-Christophe PLAGNIOL-VILLARD 		while (txlen < len) {
11252731b9a8SJean-Christophe PLAGNIOL-VILLARD 			nextlen = ((len-txlen) < dev->epmaxpacketin[ep]) ?
11262731b9a8SJean-Christophe PLAGNIOL-VILLARD 					(len-txlen) : dev->epmaxpacketin[ep];
11272731b9a8SJean-Christophe PLAGNIOL-VILLARD 
11282731b9a8SJean-Christophe PLAGNIOL-VILLARD 			/* Set the ReqPkt bit */
1129bc72a919SBryan Wu 			csr = readw(&musbr->rxcsr);
1130bc72a919SBryan Wu 			writew(csr | MUSB_RXCSR_H_REQPKT, &musbr->rxcsr);
11312731b9a8SJean-Christophe PLAGNIOL-VILLARD 
11322731b9a8SJean-Christophe PLAGNIOL-VILLARD 			/* Wait until the RxPktRdy bit is set */
1133ddd025bbSAndrew Murray 			if (wait_until_rxep_ready(dev, MUSB_INTR_EP) != 1) {
11342731b9a8SJean-Christophe PLAGNIOL-VILLARD 				csr = readw(&musbr->rxcsr);
11352731b9a8SJean-Christophe PLAGNIOL-VILLARD 				usb_settoggle(dev, ep, dir_out,
11362731b9a8SJean-Christophe PLAGNIOL-VILLARD 				(csr >> MUSB_S_RXCSR_H_DATATOGGLE) & 1);
11372731b9a8SJean-Christophe PLAGNIOL-VILLARD 				csr &= ~MUSB_RXCSR_RXPKTRDY;
11382731b9a8SJean-Christophe PLAGNIOL-VILLARD 				writew(csr, &musbr->rxcsr);
11392731b9a8SJean-Christophe PLAGNIOL-VILLARD 				dev->act_len = txlen;
11402731b9a8SJean-Christophe PLAGNIOL-VILLARD 				return 0;
11412731b9a8SJean-Christophe PLAGNIOL-VILLARD 			}
11422731b9a8SJean-Christophe PLAGNIOL-VILLARD 
11432731b9a8SJean-Christophe PLAGNIOL-VILLARD 			/* Read the data from the FIFO */
11442731b9a8SJean-Christophe PLAGNIOL-VILLARD 			read_fifo(MUSB_INTR_EP, nextlen,
11452731b9a8SJean-Christophe PLAGNIOL-VILLARD 					(void *)(((u8 *)buffer) + txlen));
11462731b9a8SJean-Christophe PLAGNIOL-VILLARD 
11472731b9a8SJean-Christophe PLAGNIOL-VILLARD 			/* Clear the RxPktRdy bit */
11482731b9a8SJean-Christophe PLAGNIOL-VILLARD 			csr =  readw(&musbr->rxcsr);
11492731b9a8SJean-Christophe PLAGNIOL-VILLARD 			csr &= ~MUSB_RXCSR_RXPKTRDY;
11502731b9a8SJean-Christophe PLAGNIOL-VILLARD 			writew(csr, &musbr->rxcsr);
11512731b9a8SJean-Christophe PLAGNIOL-VILLARD 			txlen += nextlen;
11522731b9a8SJean-Christophe PLAGNIOL-VILLARD 		}
11532731b9a8SJean-Christophe PLAGNIOL-VILLARD 
11542731b9a8SJean-Christophe PLAGNIOL-VILLARD 		/* Keep a copy of the data toggle bit */
11552731b9a8SJean-Christophe PLAGNIOL-VILLARD 		csr = readw(&musbr->rxcsr);
11562731b9a8SJean-Christophe PLAGNIOL-VILLARD 		usb_settoggle(dev, ep, dir_out,
11572731b9a8SJean-Christophe PLAGNIOL-VILLARD 				(csr >> MUSB_S_RXCSR_H_DATATOGGLE) & 1);
11582731b9a8SJean-Christophe PLAGNIOL-VILLARD 	}
11592731b9a8SJean-Christophe PLAGNIOL-VILLARD 
11602731b9a8SJean-Christophe PLAGNIOL-VILLARD 	/* interrupt transfer is complete */
11612731b9a8SJean-Christophe PLAGNIOL-VILLARD 	dev->irq_status = 0;
11622731b9a8SJean-Christophe PLAGNIOL-VILLARD 	dev->irq_act_len = len;
11632731b9a8SJean-Christophe PLAGNIOL-VILLARD 	dev->irq_handle(dev);
11642731b9a8SJean-Christophe PLAGNIOL-VILLARD 	dev->status = 0;
11652731b9a8SJean-Christophe PLAGNIOL-VILLARD 	dev->act_len = len;
11662731b9a8SJean-Christophe PLAGNIOL-VILLARD 	return 0;
11672731b9a8SJean-Christophe PLAGNIOL-VILLARD }
1168