12731b9a8SJean-Christophe PLAGNIOL-VILLARD /* 22731b9a8SJean-Christophe PLAGNIOL-VILLARD * URB OHCI HCD (Host Controller Driver) for USB on the AT91RM9200 and PCI bus. 32731b9a8SJean-Christophe PLAGNIOL-VILLARD * 42731b9a8SJean-Christophe PLAGNIOL-VILLARD * Interrupt support is added. Now, it has been tested 52731b9a8SJean-Christophe PLAGNIOL-VILLARD * on ULI1575 chip and works well with USB keyboard. 62731b9a8SJean-Christophe PLAGNIOL-VILLARD * 72731b9a8SJean-Christophe PLAGNIOL-VILLARD * (C) Copyright 2007 82731b9a8SJean-Christophe PLAGNIOL-VILLARD * Zhang Wei, Freescale Semiconductor, Inc. <wei.zhang@freescale.com> 92731b9a8SJean-Christophe PLAGNIOL-VILLARD * 102731b9a8SJean-Christophe PLAGNIOL-VILLARD * (C) Copyright 2003 11792a09ebSDetlev Zundel * Gary Jennejohn, DENX Software Engineering <garyj@denx.de> 122731b9a8SJean-Christophe PLAGNIOL-VILLARD * 132731b9a8SJean-Christophe PLAGNIOL-VILLARD * Note: Much of this code has been derived from Linux 2.4 142731b9a8SJean-Christophe PLAGNIOL-VILLARD * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at> 152731b9a8SJean-Christophe PLAGNIOL-VILLARD * (C) Copyright 2000-2002 David Brownell 162731b9a8SJean-Christophe PLAGNIOL-VILLARD * 172731b9a8SJean-Christophe PLAGNIOL-VILLARD * Modified for the MP2USB by (C) Copyright 2005 Eric Benard 182731b9a8SJean-Christophe PLAGNIOL-VILLARD * ebenard@eukrea.com - based on s3c24x0's driver 192731b9a8SJean-Christophe PLAGNIOL-VILLARD * 201a459660SWolfgang Denk * SPDX-License-Identifier: GPL-2.0+ 212731b9a8SJean-Christophe PLAGNIOL-VILLARD */ 222731b9a8SJean-Christophe PLAGNIOL-VILLARD /* 232731b9a8SJean-Christophe PLAGNIOL-VILLARD * IMPORTANT NOTES 242731b9a8SJean-Christophe PLAGNIOL-VILLARD * 1 - Read doc/README.generic_usb_ohci 252731b9a8SJean-Christophe PLAGNIOL-VILLARD * 2 - this driver is intended for use with USB Mass Storage Devices 262731b9a8SJean-Christophe PLAGNIOL-VILLARD * (BBB) and USB keyboard. There is NO support for Isochronous pipes! 272731b9a8SJean-Christophe PLAGNIOL-VILLARD * 2 - when running on a PQFP208 AT91RM9200, define CONFIG_AT91C_PQFP_UHPBUG 282731b9a8SJean-Christophe PLAGNIOL-VILLARD * to activate workaround for bug #41 or this driver will NOT work! 292731b9a8SJean-Christophe PLAGNIOL-VILLARD */ 302731b9a8SJean-Christophe PLAGNIOL-VILLARD 312731b9a8SJean-Christophe PLAGNIOL-VILLARD #include <common.h> 322731b9a8SJean-Christophe PLAGNIOL-VILLARD #include <asm/byteorder.h> 3358b4048fSHans de Goede #include <dm.h> 3458b4048fSHans de Goede #include <errno.h> 352731b9a8SJean-Christophe PLAGNIOL-VILLARD 362731b9a8SJean-Christophe PLAGNIOL-VILLARD #if defined(CONFIG_PCI_OHCI) 372731b9a8SJean-Christophe PLAGNIOL-VILLARD # include <pci.h> 382731b9a8SJean-Christophe PLAGNIOL-VILLARD #if !defined(CONFIG_PCI_OHCI_DEVNO) 392731b9a8SJean-Christophe PLAGNIOL-VILLARD #define CONFIG_PCI_OHCI_DEVNO 0 402731b9a8SJean-Christophe PLAGNIOL-VILLARD #endif 412731b9a8SJean-Christophe PLAGNIOL-VILLARD #endif 422731b9a8SJean-Christophe PLAGNIOL-VILLARD 432731b9a8SJean-Christophe PLAGNIOL-VILLARD #include <malloc.h> 44*cf92e05cSSimon Glass #include <memalign.h> 452731b9a8SJean-Christophe PLAGNIOL-VILLARD #include <usb.h> 462731b9a8SJean-Christophe PLAGNIOL-VILLARD 472731b9a8SJean-Christophe PLAGNIOL-VILLARD #include "ohci.h" 482731b9a8SJean-Christophe PLAGNIOL-VILLARD 492731b9a8SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_AT91RM9200 502731b9a8SJean-Christophe PLAGNIOL-VILLARD #include <asm/arch/hardware.h> /* needed for AT91_USB_HOST_BASE */ 512731b9a8SJean-Christophe PLAGNIOL-VILLARD #endif 522731b9a8SJean-Christophe PLAGNIOL-VILLARD 53f2168440SMasahiro Yamada #if defined(CONFIG_CPU_ARM920T) || \ 54ac67804fSkevin.morfitt@fearnside-systems.co.uk defined(CONFIG_S3C24X0) || \ 552731b9a8SJean-Christophe PLAGNIOL-VILLARD defined(CONFIG_440EP) || \ 562731b9a8SJean-Christophe PLAGNIOL-VILLARD defined(CONFIG_PCI_OHCI) || \ 572731b9a8SJean-Christophe PLAGNIOL-VILLARD defined(CONFIG_MPC5200) || \ 582731b9a8SJean-Christophe PLAGNIOL-VILLARD defined(CONFIG_SYS_OHCI_USE_NPS) 592731b9a8SJean-Christophe PLAGNIOL-VILLARD # define OHCI_USE_NPS /* force NoPowerSwitching mode */ 602731b9a8SJean-Christophe PLAGNIOL-VILLARD #endif 612731b9a8SJean-Christophe PLAGNIOL-VILLARD 622731b9a8SJean-Christophe PLAGNIOL-VILLARD #undef OHCI_VERBOSE_DEBUG /* not always helpful */ 632731b9a8SJean-Christophe PLAGNIOL-VILLARD #undef DEBUG 642731b9a8SJean-Christophe PLAGNIOL-VILLARD #undef SHOW_INFO 652731b9a8SJean-Christophe PLAGNIOL-VILLARD #undef OHCI_FILL_TRACE 662731b9a8SJean-Christophe PLAGNIOL-VILLARD 672731b9a8SJean-Christophe PLAGNIOL-VILLARD /* For initializing controller (mask in an HCFS mode too) */ 682731b9a8SJean-Christophe PLAGNIOL-VILLARD #define OHCI_CONTROL_INIT \ 692731b9a8SJean-Christophe PLAGNIOL-VILLARD (OHCI_CTRL_CBSR & 0x3) | OHCI_CTRL_IE | OHCI_CTRL_PLE 702731b9a8SJean-Christophe PLAGNIOL-VILLARD 712731b9a8SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_PCI_OHCI 722731b9a8SJean-Christophe PLAGNIOL-VILLARD static struct pci_device_id ohci_pci_ids[] = { 732731b9a8SJean-Christophe PLAGNIOL-VILLARD {0x10b9, 0x5237}, /* ULI1575 PCI OHCI module ids */ 742731b9a8SJean-Christophe PLAGNIOL-VILLARD {0x1033, 0x0035}, /* NEC PCI OHCI module ids */ 752731b9a8SJean-Christophe PLAGNIOL-VILLARD {0x1131, 0x1561}, /* Philips 1561 PCI OHCI module ids */ 762731b9a8SJean-Christophe PLAGNIOL-VILLARD /* Please add supported PCI OHCI controller ids here */ 772731b9a8SJean-Christophe PLAGNIOL-VILLARD {0, 0} 782731b9a8SJean-Christophe PLAGNIOL-VILLARD }; 792731b9a8SJean-Christophe PLAGNIOL-VILLARD #endif 802731b9a8SJean-Christophe PLAGNIOL-VILLARD 812731b9a8SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_PCI_EHCI_DEVNO 822731b9a8SJean-Christophe PLAGNIOL-VILLARD static struct pci_device_id ehci_pci_ids[] = { 832731b9a8SJean-Christophe PLAGNIOL-VILLARD {0x1131, 0x1562}, /* Philips 1562 PCI EHCI module ids */ 842731b9a8SJean-Christophe PLAGNIOL-VILLARD /* Please add supported PCI EHCI controller ids here */ 852731b9a8SJean-Christophe PLAGNIOL-VILLARD {0, 0} 862731b9a8SJean-Christophe PLAGNIOL-VILLARD }; 872731b9a8SJean-Christophe PLAGNIOL-VILLARD #endif 882731b9a8SJean-Christophe PLAGNIOL-VILLARD 892731b9a8SJean-Christophe PLAGNIOL-VILLARD #ifdef DEBUG 902731b9a8SJean-Christophe PLAGNIOL-VILLARD #define dbg(format, arg...) printf("DEBUG: " format "\n", ## arg) 912731b9a8SJean-Christophe PLAGNIOL-VILLARD #else 922731b9a8SJean-Christophe PLAGNIOL-VILLARD #define dbg(format, arg...) do {} while (0) 932731b9a8SJean-Christophe PLAGNIOL-VILLARD #endif /* DEBUG */ 942731b9a8SJean-Christophe PLAGNIOL-VILLARD #define err(format, arg...) printf("ERROR: " format "\n", ## arg) 952731b9a8SJean-Christophe PLAGNIOL-VILLARD #ifdef SHOW_INFO 962731b9a8SJean-Christophe PLAGNIOL-VILLARD #define info(format, arg...) printf("INFO: " format "\n", ## arg) 972731b9a8SJean-Christophe PLAGNIOL-VILLARD #else 982731b9a8SJean-Christophe PLAGNIOL-VILLARD #define info(format, arg...) do {} while (0) 992731b9a8SJean-Christophe PLAGNIOL-VILLARD #endif 1002731b9a8SJean-Christophe PLAGNIOL-VILLARD 1012731b9a8SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_SYS_OHCI_BE_CONTROLLER 1022731b9a8SJean-Christophe PLAGNIOL-VILLARD # define m16_swap(x) cpu_to_be16(x) 1032731b9a8SJean-Christophe PLAGNIOL-VILLARD # define m32_swap(x) cpu_to_be32(x) 1042731b9a8SJean-Christophe PLAGNIOL-VILLARD #else 1052731b9a8SJean-Christophe PLAGNIOL-VILLARD # define m16_swap(x) cpu_to_le16(x) 1062731b9a8SJean-Christophe PLAGNIOL-VILLARD # define m32_swap(x) cpu_to_le32(x) 1072731b9a8SJean-Christophe PLAGNIOL-VILLARD #endif /* CONFIG_SYS_OHCI_BE_CONTROLLER */ 1082731b9a8SJean-Christophe PLAGNIOL-VILLARD 109e0266f49SWu, Josh /* We really should do proper cache flushing everywhere */ 1108d005ef8SHans de Goede #define flush_dcache_buffer(addr, size) \ 1118d005ef8SHans de Goede flush_dcache_range((unsigned long)(addr), \ 1128d005ef8SHans de Goede ALIGN((unsigned long)(addr) + size, ARCH_DMA_MINALIGN)) 1138d005ef8SHans de Goede #define invalidate_dcache_buffer(addr, size) \ 1148d005ef8SHans de Goede invalidate_dcache_range((unsigned long)(addr), \ 1158d005ef8SHans de Goede ALIGN((unsigned long)(addr) + size, ARCH_DMA_MINALIGN)) 1168d005ef8SHans de Goede 1178d005ef8SHans de Goede /* Do not use sizeof(ed / td) as our ed / td structs contain extra members */ 1188d005ef8SHans de Goede #define flush_dcache_ed(addr) flush_dcache_buffer(addr, 16) 1198d005ef8SHans de Goede #define flush_dcache_td(addr) flush_dcache_buffer(addr, 16) 1208d005ef8SHans de Goede #define flush_dcache_iso_td(addr) flush_dcache_buffer(addr, 32) 1218d005ef8SHans de Goede #define flush_dcache_hcca(addr) flush_dcache_buffer(addr, 256) 1228d005ef8SHans de Goede #define invalidate_dcache_ed(addr) invalidate_dcache_buffer(addr, 16) 1238d005ef8SHans de Goede #define invalidate_dcache_td(addr) invalidate_dcache_buffer(addr, 16) 1248d005ef8SHans de Goede #define invalidate_dcache_iso_td(addr) invalidate_dcache_buffer(addr, 32) 1258d005ef8SHans de Goede #define invalidate_dcache_hcca(addr) invalidate_dcache_buffer(addr, 256) 1268d005ef8SHans de Goede 1278f761f02SHans de Goede #ifdef CONFIG_DM_USB 1288f761f02SHans de Goede /* 1298f761f02SHans de Goede * The various ohci_mdelay(1) calls in the code seem unnecessary. We keep 1308f761f02SHans de Goede * them around when building for older boards not yet converted to the dm 1318f761f02SHans de Goede * just in case (to avoid regressions), for dm this turns them into nops. 1328f761f02SHans de Goede */ 1338f761f02SHans de Goede #define ohci_mdelay(x) 1348f761f02SHans de Goede #else 1358f761f02SHans de Goede #define ohci_mdelay(x) mdelay(x) 1368f761f02SHans de Goede #endif 1378f761f02SHans de Goede 13858b4048fSHans de Goede #ifndef CONFIG_DM_USB 1392731b9a8SJean-Christophe PLAGNIOL-VILLARD /* global ohci_t */ 1402731b9a8SJean-Christophe PLAGNIOL-VILLARD static ohci_t gohci; 1412731b9a8SJean-Christophe PLAGNIOL-VILLARD /* this must be aligned to a 256 byte boundary */ 1422731b9a8SJean-Christophe PLAGNIOL-VILLARD struct ohci_hcca ghcca[1]; 14358b4048fSHans de Goede #endif 1442731b9a8SJean-Christophe PLAGNIOL-VILLARD 1456651c140SHans de Goede /* mapping of the OHCI CC status to error codes */ 1466651c140SHans de Goede static int cc_to_error[16] = { 1476651c140SHans de Goede /* No Error */ 0, 1486651c140SHans de Goede /* CRC Error */ USB_ST_CRC_ERR, 1496651c140SHans de Goede /* Bit Stuff */ USB_ST_BIT_ERR, 1506651c140SHans de Goede /* Data Togg */ USB_ST_CRC_ERR, 1516651c140SHans de Goede /* Stall */ USB_ST_STALLED, 1526651c140SHans de Goede /* DevNotResp */ -1, 1536651c140SHans de Goede /* PIDCheck */ USB_ST_BIT_ERR, 1546651c140SHans de Goede /* UnExpPID */ USB_ST_BIT_ERR, 1556651c140SHans de Goede /* DataOver */ USB_ST_BUF_ERR, 1566651c140SHans de Goede /* DataUnder */ USB_ST_BUF_ERR, 1576651c140SHans de Goede /* reservd */ -1, 1586651c140SHans de Goede /* reservd */ -1, 1596651c140SHans de Goede /* BufferOver */ USB_ST_BUF_ERR, 1606651c140SHans de Goede /* BuffUnder */ USB_ST_BUF_ERR, 1616651c140SHans de Goede /* Not Access */ -1, 1626651c140SHans de Goede /* Not Access */ -1 1636651c140SHans de Goede }; 1646651c140SHans de Goede 1656651c140SHans de Goede static const char *cc_to_string[16] = { 1666651c140SHans de Goede "No Error", 1676651c140SHans de Goede "CRC: Last data packet from endpoint contained a CRC error.", 1686651c140SHans de Goede "BITSTUFFING: Last data packet from endpoint contained a bit " \ 1696651c140SHans de Goede "stuffing violation", 1706651c140SHans de Goede "DATATOGGLEMISMATCH: Last packet from endpoint had data toggle PID\n" \ 1716651c140SHans de Goede "that did not match the expected value.", 1726651c140SHans de Goede "STALL: TD was moved to the Done Queue because the endpoint returned" \ 1736651c140SHans de Goede " a STALL PID", 1746651c140SHans de Goede "DEVICENOTRESPONDING: Device did not respond to token (IN) or did\n" \ 1756651c140SHans de Goede "not provide a handshake (OUT)", 1766651c140SHans de Goede "PIDCHECKFAILURE: Check bits on PID from endpoint failed on data PID\n"\ 1776651c140SHans de Goede "(IN) or handshake (OUT)", 1786651c140SHans de Goede "UNEXPECTEDPID: Receive PID was not valid when encountered or PID\n" \ 1796651c140SHans de Goede "value is not defined.", 1806651c140SHans de Goede "DATAOVERRUN: The amount of data returned by the endpoint exceeded\n" \ 1816651c140SHans de Goede "either the size of the maximum data packet allowed\n" \ 1826651c140SHans de Goede "from the endpoint (found in MaximumPacketSize field\n" \ 1836651c140SHans de Goede "of ED) or the remaining buffer size.", 1846651c140SHans de Goede "DATAUNDERRUN: The endpoint returned less than MaximumPacketSize\n" \ 1856651c140SHans de Goede "and that amount was not sufficient to fill the\n" \ 1866651c140SHans de Goede "specified buffer", 1876651c140SHans de Goede "reserved1", 1886651c140SHans de Goede "reserved2", 1896651c140SHans de Goede "BUFFEROVERRUN: During an IN, HC received data from endpoint faster\n" \ 1906651c140SHans de Goede "than it could be written to system memory", 1916651c140SHans de Goede "BUFFERUNDERRUN: During an OUT, HC could not retrieve data from\n" \ 1926651c140SHans de Goede "system memory fast enough to keep up with data USB " \ 1936651c140SHans de Goede "data rate.", 1946651c140SHans de Goede "NOT ACCESSED: This code is set by software before the TD is placed" \ 1956651c140SHans de Goede "on a list to be processed by the HC.(1)", 1966651c140SHans de Goede "NOT ACCESSED: This code is set by software before the TD is placed" \ 1976651c140SHans de Goede "on a list to be processed by the HC.(2)", 1986651c140SHans de Goede }; 1996651c140SHans de Goede 2002731b9a8SJean-Christophe PLAGNIOL-VILLARD static inline u32 roothub_a(struct ohci *hc) 201a5496a18SBecky Bruce { return ohci_readl(&hc->regs->roothub.a); } 2022731b9a8SJean-Christophe PLAGNIOL-VILLARD static inline u32 roothub_b(struct ohci *hc) 203a5496a18SBecky Bruce { return ohci_readl(&hc->regs->roothub.b); } 2042731b9a8SJean-Christophe PLAGNIOL-VILLARD static inline u32 roothub_status(struct ohci *hc) 205a5496a18SBecky Bruce { return ohci_readl(&hc->regs->roothub.status); } 2062731b9a8SJean-Christophe PLAGNIOL-VILLARD static inline u32 roothub_portstatus(struct ohci *hc, int i) 207a5496a18SBecky Bruce { return ohci_readl(&hc->regs->roothub.portstatus[i]); } 2082731b9a8SJean-Christophe PLAGNIOL-VILLARD 2092731b9a8SJean-Christophe PLAGNIOL-VILLARD /* forward declaration */ 210c5613df5SHans de Goede static int hc_interrupt(ohci_t *ohci); 211c5613df5SHans de Goede static void td_submit_job(ohci_t *ohci, struct usb_device *dev, 212c5613df5SHans de Goede unsigned long pipe, void *buffer, int transfer_len, 2132731b9a8SJean-Christophe PLAGNIOL-VILLARD struct devrequest *setup, urb_priv_t *urb, 2142731b9a8SJean-Christophe PLAGNIOL-VILLARD int interval); 2156651c140SHans de Goede static int ep_link(ohci_t * ohci, ed_t * ed); 2166651c140SHans de Goede static int ep_unlink(ohci_t * ohci, ed_t * ed); 2176651c140SHans de Goede static ed_t *ep_add_ed(ohci_dev_t *ohci_dev, struct usb_device *usb_dev, 2186651c140SHans de Goede unsigned long pipe, int interval, int load); 2196651c140SHans de Goede 2206651c140SHans de Goede /*-------------------------------------------------------------------------*/ 2216651c140SHans de Goede 2226651c140SHans de Goede /* TDs ... */ 2236651c140SHans de Goede static struct td *td_alloc(ohci_dev_t *ohci_dev, struct usb_device *usb_dev) 2246651c140SHans de Goede { 2256651c140SHans de Goede int i; 2266651c140SHans de Goede struct td *td; 2276651c140SHans de Goede 2286651c140SHans de Goede td = NULL; 2296651c140SHans de Goede for (i = 0; i < NUM_TD; i++) 2306651c140SHans de Goede { 2316651c140SHans de Goede if (ohci_dev->tds[i].usb_dev == NULL) 2326651c140SHans de Goede { 2336651c140SHans de Goede td = &ohci_dev->tds[i]; 2346651c140SHans de Goede td->usb_dev = usb_dev; 2356651c140SHans de Goede break; 2366651c140SHans de Goede } 2376651c140SHans de Goede } 2386651c140SHans de Goede 2396651c140SHans de Goede return td; 2406651c140SHans de Goede } 2416651c140SHans de Goede 2426651c140SHans de Goede static inline void ed_free(struct ed *ed) 2436651c140SHans de Goede { 2446651c140SHans de Goede ed->usb_dev = NULL; 2456651c140SHans de Goede } 2462731b9a8SJean-Christophe PLAGNIOL-VILLARD 2472731b9a8SJean-Christophe PLAGNIOL-VILLARD /*-------------------------------------------------------------------------* 2482731b9a8SJean-Christophe PLAGNIOL-VILLARD * URB support functions 2492731b9a8SJean-Christophe PLAGNIOL-VILLARD *-------------------------------------------------------------------------*/ 2502731b9a8SJean-Christophe PLAGNIOL-VILLARD 2512731b9a8SJean-Christophe PLAGNIOL-VILLARD /* free HCD-private data associated with this URB */ 2522731b9a8SJean-Christophe PLAGNIOL-VILLARD 2532731b9a8SJean-Christophe PLAGNIOL-VILLARD static void urb_free_priv(urb_priv_t *urb) 2542731b9a8SJean-Christophe PLAGNIOL-VILLARD { 2552731b9a8SJean-Christophe PLAGNIOL-VILLARD int i; 2562731b9a8SJean-Christophe PLAGNIOL-VILLARD int last; 2572731b9a8SJean-Christophe PLAGNIOL-VILLARD struct td *td; 2582731b9a8SJean-Christophe PLAGNIOL-VILLARD 2592731b9a8SJean-Christophe PLAGNIOL-VILLARD last = urb->length - 1; 2602731b9a8SJean-Christophe PLAGNIOL-VILLARD if (last >= 0) { 2612731b9a8SJean-Christophe PLAGNIOL-VILLARD for (i = 0; i <= last; i++) { 2622731b9a8SJean-Christophe PLAGNIOL-VILLARD td = urb->td[i]; 2632731b9a8SJean-Christophe PLAGNIOL-VILLARD if (td) { 2642731b9a8SJean-Christophe PLAGNIOL-VILLARD td->usb_dev = NULL; 2652731b9a8SJean-Christophe PLAGNIOL-VILLARD urb->td[i] = NULL; 2662731b9a8SJean-Christophe PLAGNIOL-VILLARD } 2672731b9a8SJean-Christophe PLAGNIOL-VILLARD } 2682731b9a8SJean-Christophe PLAGNIOL-VILLARD } 2692731b9a8SJean-Christophe PLAGNIOL-VILLARD free(urb); 2702731b9a8SJean-Christophe PLAGNIOL-VILLARD } 2712731b9a8SJean-Christophe PLAGNIOL-VILLARD 2722731b9a8SJean-Christophe PLAGNIOL-VILLARD /*-------------------------------------------------------------------------*/ 2732731b9a8SJean-Christophe PLAGNIOL-VILLARD 2742731b9a8SJean-Christophe PLAGNIOL-VILLARD #ifdef DEBUG 275c5613df5SHans de Goede static int sohci_get_current_frame_number(ohci_t *ohci); 2762731b9a8SJean-Christophe PLAGNIOL-VILLARD 2772731b9a8SJean-Christophe PLAGNIOL-VILLARD /* debug| print the main components of an URB 2782731b9a8SJean-Christophe PLAGNIOL-VILLARD * small: 0) header + data packets 1) just header */ 2792731b9a8SJean-Christophe PLAGNIOL-VILLARD 280c5613df5SHans de Goede static void pkt_print(ohci_t *ohci, urb_priv_t *purb, struct usb_device *dev, 2812731b9a8SJean-Christophe PLAGNIOL-VILLARD unsigned long pipe, void *buffer, int transfer_len, 2822731b9a8SJean-Christophe PLAGNIOL-VILLARD struct devrequest *setup, char *str, int small) 2832731b9a8SJean-Christophe PLAGNIOL-VILLARD { 2842731b9a8SJean-Christophe PLAGNIOL-VILLARD dbg("%s URB:[%4x] dev:%2lu,ep:%2lu-%c,type:%s,len:%d/%d stat:%#lx", 2852731b9a8SJean-Christophe PLAGNIOL-VILLARD str, 286c5613df5SHans de Goede sohci_get_current_frame_number(ohci), 2872731b9a8SJean-Christophe PLAGNIOL-VILLARD usb_pipedevice(pipe), 2882731b9a8SJean-Christophe PLAGNIOL-VILLARD usb_pipeendpoint(pipe), 2892731b9a8SJean-Christophe PLAGNIOL-VILLARD usb_pipeout(pipe)? 'O': 'I', 2902731b9a8SJean-Christophe PLAGNIOL-VILLARD usb_pipetype(pipe) < 2 ? \ 2912731b9a8SJean-Christophe PLAGNIOL-VILLARD (usb_pipeint(pipe)? "INTR": "ISOC"): \ 2922731b9a8SJean-Christophe PLAGNIOL-VILLARD (usb_pipecontrol(pipe)? "CTRL": "BULK"), 2932731b9a8SJean-Christophe PLAGNIOL-VILLARD (purb ? purb->actual_length : 0), 2942731b9a8SJean-Christophe PLAGNIOL-VILLARD transfer_len, dev->status); 2952731b9a8SJean-Christophe PLAGNIOL-VILLARD #ifdef OHCI_VERBOSE_DEBUG 2962731b9a8SJean-Christophe PLAGNIOL-VILLARD if (!small) { 2972731b9a8SJean-Christophe PLAGNIOL-VILLARD int i, len; 2982731b9a8SJean-Christophe PLAGNIOL-VILLARD 2992731b9a8SJean-Christophe PLAGNIOL-VILLARD if (usb_pipecontrol(pipe)) { 3002731b9a8SJean-Christophe PLAGNIOL-VILLARD printf(__FILE__ ": cmd(8):"); 3012731b9a8SJean-Christophe PLAGNIOL-VILLARD for (i = 0; i < 8 ; i++) 3022731b9a8SJean-Christophe PLAGNIOL-VILLARD printf(" %02x", ((__u8 *) setup) [i]); 3032731b9a8SJean-Christophe PLAGNIOL-VILLARD printf("\n"); 3042731b9a8SJean-Christophe PLAGNIOL-VILLARD } 3052731b9a8SJean-Christophe PLAGNIOL-VILLARD if (transfer_len > 0 && buffer) { 3062731b9a8SJean-Christophe PLAGNIOL-VILLARD printf(__FILE__ ": data(%d/%d):", 3072731b9a8SJean-Christophe PLAGNIOL-VILLARD (purb ? purb->actual_length : 0), 3082731b9a8SJean-Christophe PLAGNIOL-VILLARD transfer_len); 3092731b9a8SJean-Christophe PLAGNIOL-VILLARD len = usb_pipeout(pipe)? transfer_len: 3102731b9a8SJean-Christophe PLAGNIOL-VILLARD (purb ? purb->actual_length : 0); 3112731b9a8SJean-Christophe PLAGNIOL-VILLARD for (i = 0; i < 16 && i < len; i++) 3122731b9a8SJean-Christophe PLAGNIOL-VILLARD printf(" %02x", ((__u8 *) buffer) [i]); 3132731b9a8SJean-Christophe PLAGNIOL-VILLARD printf("%s\n", i < len? "...": ""); 3142731b9a8SJean-Christophe PLAGNIOL-VILLARD } 3152731b9a8SJean-Christophe PLAGNIOL-VILLARD } 3162731b9a8SJean-Christophe PLAGNIOL-VILLARD #endif 3172731b9a8SJean-Christophe PLAGNIOL-VILLARD } 3182731b9a8SJean-Christophe PLAGNIOL-VILLARD 3192731b9a8SJean-Christophe PLAGNIOL-VILLARD /* just for debugging; prints non-empty branches of the int ed tree 3202731b9a8SJean-Christophe PLAGNIOL-VILLARD * inclusive iso eds */ 3212731b9a8SJean-Christophe PLAGNIOL-VILLARD void ep_print_int_eds(ohci_t *ohci, char *str) 3222731b9a8SJean-Christophe PLAGNIOL-VILLARD { 3232731b9a8SJean-Christophe PLAGNIOL-VILLARD int i, j; 3242731b9a8SJean-Christophe PLAGNIOL-VILLARD __u32 *ed_p; 3252731b9a8SJean-Christophe PLAGNIOL-VILLARD for (i = 0; i < 32; i++) { 3262731b9a8SJean-Christophe PLAGNIOL-VILLARD j = 5; 3272731b9a8SJean-Christophe PLAGNIOL-VILLARD ed_p = &(ohci->hcca->int_table [i]); 3282731b9a8SJean-Christophe PLAGNIOL-VILLARD if (*ed_p == 0) 3292731b9a8SJean-Christophe PLAGNIOL-VILLARD continue; 3308d005ef8SHans de Goede invalidate_dcache_ed(ed_p); 3312731b9a8SJean-Christophe PLAGNIOL-VILLARD printf(__FILE__ ": %s branch int %2d(%2x):", str, i, i); 3322731b9a8SJean-Christophe PLAGNIOL-VILLARD while (*ed_p != 0 && j--) { 3332731b9a8SJean-Christophe PLAGNIOL-VILLARD ed_t *ed = (ed_t *)m32_swap(ed_p); 3348d005ef8SHans de Goede invalidate_dcache_ed(ed); 3352731b9a8SJean-Christophe PLAGNIOL-VILLARD printf(" ed: %4x;", ed->hwINFO); 3362731b9a8SJean-Christophe PLAGNIOL-VILLARD ed_p = &ed->hwNextED; 3372731b9a8SJean-Christophe PLAGNIOL-VILLARD } 3382731b9a8SJean-Christophe PLAGNIOL-VILLARD printf("\n"); 3392731b9a8SJean-Christophe PLAGNIOL-VILLARD } 3402731b9a8SJean-Christophe PLAGNIOL-VILLARD } 3412731b9a8SJean-Christophe PLAGNIOL-VILLARD 3422731b9a8SJean-Christophe PLAGNIOL-VILLARD static void ohci_dump_intr_mask(char *label, __u32 mask) 3432731b9a8SJean-Christophe PLAGNIOL-VILLARD { 3442731b9a8SJean-Christophe PLAGNIOL-VILLARD dbg("%s: 0x%08x%s%s%s%s%s%s%s%s%s", 3452731b9a8SJean-Christophe PLAGNIOL-VILLARD label, 3462731b9a8SJean-Christophe PLAGNIOL-VILLARD mask, 3472731b9a8SJean-Christophe PLAGNIOL-VILLARD (mask & OHCI_INTR_MIE) ? " MIE" : "", 3482731b9a8SJean-Christophe PLAGNIOL-VILLARD (mask & OHCI_INTR_OC) ? " OC" : "", 3492731b9a8SJean-Christophe PLAGNIOL-VILLARD (mask & OHCI_INTR_RHSC) ? " RHSC" : "", 3502731b9a8SJean-Christophe PLAGNIOL-VILLARD (mask & OHCI_INTR_FNO) ? " FNO" : "", 3512731b9a8SJean-Christophe PLAGNIOL-VILLARD (mask & OHCI_INTR_UE) ? " UE" : "", 3522731b9a8SJean-Christophe PLAGNIOL-VILLARD (mask & OHCI_INTR_RD) ? " RD" : "", 3532731b9a8SJean-Christophe PLAGNIOL-VILLARD (mask & OHCI_INTR_SF) ? " SF" : "", 3542731b9a8SJean-Christophe PLAGNIOL-VILLARD (mask & OHCI_INTR_WDH) ? " WDH" : "", 3552731b9a8SJean-Christophe PLAGNIOL-VILLARD (mask & OHCI_INTR_SO) ? " SO" : "" 3562731b9a8SJean-Christophe PLAGNIOL-VILLARD ); 3572731b9a8SJean-Christophe PLAGNIOL-VILLARD } 3582731b9a8SJean-Christophe PLAGNIOL-VILLARD 3592731b9a8SJean-Christophe PLAGNIOL-VILLARD static void maybe_print_eds(char *label, __u32 value) 3602731b9a8SJean-Christophe PLAGNIOL-VILLARD { 3612731b9a8SJean-Christophe PLAGNIOL-VILLARD ed_t *edp = (ed_t *)value; 3622731b9a8SJean-Christophe PLAGNIOL-VILLARD 3632731b9a8SJean-Christophe PLAGNIOL-VILLARD if (value) { 3642731b9a8SJean-Christophe PLAGNIOL-VILLARD dbg("%s %08x", label, value); 3658d005ef8SHans de Goede invalidate_dcache_ed(edp); 3662731b9a8SJean-Christophe PLAGNIOL-VILLARD dbg("%08x", edp->hwINFO); 3672731b9a8SJean-Christophe PLAGNIOL-VILLARD dbg("%08x", edp->hwTailP); 3682731b9a8SJean-Christophe PLAGNIOL-VILLARD dbg("%08x", edp->hwHeadP); 3692731b9a8SJean-Christophe PLAGNIOL-VILLARD dbg("%08x", edp->hwNextED); 3702731b9a8SJean-Christophe PLAGNIOL-VILLARD } 3712731b9a8SJean-Christophe PLAGNIOL-VILLARD } 3722731b9a8SJean-Christophe PLAGNIOL-VILLARD 3732731b9a8SJean-Christophe PLAGNIOL-VILLARD static char *hcfs2string(int state) 3742731b9a8SJean-Christophe PLAGNIOL-VILLARD { 3752731b9a8SJean-Christophe PLAGNIOL-VILLARD switch (state) { 3762731b9a8SJean-Christophe PLAGNIOL-VILLARD case OHCI_USB_RESET: return "reset"; 3772731b9a8SJean-Christophe PLAGNIOL-VILLARD case OHCI_USB_RESUME: return "resume"; 3782731b9a8SJean-Christophe PLAGNIOL-VILLARD case OHCI_USB_OPER: return "operational"; 3792731b9a8SJean-Christophe PLAGNIOL-VILLARD case OHCI_USB_SUSPEND: return "suspend"; 3802731b9a8SJean-Christophe PLAGNIOL-VILLARD } 3812731b9a8SJean-Christophe PLAGNIOL-VILLARD return "?"; 3822731b9a8SJean-Christophe PLAGNIOL-VILLARD } 3832731b9a8SJean-Christophe PLAGNIOL-VILLARD 3842731b9a8SJean-Christophe PLAGNIOL-VILLARD /* dump control and status registers */ 3852731b9a8SJean-Christophe PLAGNIOL-VILLARD static void ohci_dump_status(ohci_t *controller) 3862731b9a8SJean-Christophe PLAGNIOL-VILLARD { 3872731b9a8SJean-Christophe PLAGNIOL-VILLARD struct ohci_regs *regs = controller->regs; 3882731b9a8SJean-Christophe PLAGNIOL-VILLARD __u32 temp; 3892731b9a8SJean-Christophe PLAGNIOL-VILLARD 390a5496a18SBecky Bruce temp = ohci_readl(®s->revision) & 0xff; 3912731b9a8SJean-Christophe PLAGNIOL-VILLARD if (temp != 0x10) 3922731b9a8SJean-Christophe PLAGNIOL-VILLARD dbg("spec %d.%d", (temp >> 4), (temp & 0x0f)); 3932731b9a8SJean-Christophe PLAGNIOL-VILLARD 394a5496a18SBecky Bruce temp = ohci_readl(®s->control); 3952731b9a8SJean-Christophe PLAGNIOL-VILLARD dbg("control: 0x%08x%s%s%s HCFS=%s%s%s%s%s CBSR=%d", temp, 3962731b9a8SJean-Christophe PLAGNIOL-VILLARD (temp & OHCI_CTRL_RWE) ? " RWE" : "", 3972731b9a8SJean-Christophe PLAGNIOL-VILLARD (temp & OHCI_CTRL_RWC) ? " RWC" : "", 3982731b9a8SJean-Christophe PLAGNIOL-VILLARD (temp & OHCI_CTRL_IR) ? " IR" : "", 3992731b9a8SJean-Christophe PLAGNIOL-VILLARD hcfs2string(temp & OHCI_CTRL_HCFS), 4002731b9a8SJean-Christophe PLAGNIOL-VILLARD (temp & OHCI_CTRL_BLE) ? " BLE" : "", 4012731b9a8SJean-Christophe PLAGNIOL-VILLARD (temp & OHCI_CTRL_CLE) ? " CLE" : "", 4022731b9a8SJean-Christophe PLAGNIOL-VILLARD (temp & OHCI_CTRL_IE) ? " IE" : "", 4032731b9a8SJean-Christophe PLAGNIOL-VILLARD (temp & OHCI_CTRL_PLE) ? " PLE" : "", 4042731b9a8SJean-Christophe PLAGNIOL-VILLARD temp & OHCI_CTRL_CBSR 4052731b9a8SJean-Christophe PLAGNIOL-VILLARD ); 4062731b9a8SJean-Christophe PLAGNIOL-VILLARD 407a5496a18SBecky Bruce temp = ohci_readl(®s->cmdstatus); 4082731b9a8SJean-Christophe PLAGNIOL-VILLARD dbg("cmdstatus: 0x%08x SOC=%d%s%s%s%s", temp, 4092731b9a8SJean-Christophe PLAGNIOL-VILLARD (temp & OHCI_SOC) >> 16, 4102731b9a8SJean-Christophe PLAGNIOL-VILLARD (temp & OHCI_OCR) ? " OCR" : "", 4112731b9a8SJean-Christophe PLAGNIOL-VILLARD (temp & OHCI_BLF) ? " BLF" : "", 4122731b9a8SJean-Christophe PLAGNIOL-VILLARD (temp & OHCI_CLF) ? " CLF" : "", 4132731b9a8SJean-Christophe PLAGNIOL-VILLARD (temp & OHCI_HCR) ? " HCR" : "" 4142731b9a8SJean-Christophe PLAGNIOL-VILLARD ); 4152731b9a8SJean-Christophe PLAGNIOL-VILLARD 416a5496a18SBecky Bruce ohci_dump_intr_mask("intrstatus", ohci_readl(®s->intrstatus)); 417a5496a18SBecky Bruce ohci_dump_intr_mask("intrenable", ohci_readl(®s->intrenable)); 4182731b9a8SJean-Christophe PLAGNIOL-VILLARD 419a5496a18SBecky Bruce maybe_print_eds("ed_periodcurrent", 420a5496a18SBecky Bruce ohci_readl(®s->ed_periodcurrent)); 4212731b9a8SJean-Christophe PLAGNIOL-VILLARD 422a5496a18SBecky Bruce maybe_print_eds("ed_controlhead", ohci_readl(®s->ed_controlhead)); 423a5496a18SBecky Bruce maybe_print_eds("ed_controlcurrent", 424a5496a18SBecky Bruce ohci_readl(®s->ed_controlcurrent)); 4252731b9a8SJean-Christophe PLAGNIOL-VILLARD 426a5496a18SBecky Bruce maybe_print_eds("ed_bulkhead", ohci_readl(®s->ed_bulkhead)); 427a5496a18SBecky Bruce maybe_print_eds("ed_bulkcurrent", ohci_readl(®s->ed_bulkcurrent)); 4282731b9a8SJean-Christophe PLAGNIOL-VILLARD 429a5496a18SBecky Bruce maybe_print_eds("donehead", ohci_readl(®s->donehead)); 4302731b9a8SJean-Christophe PLAGNIOL-VILLARD } 4312731b9a8SJean-Christophe PLAGNIOL-VILLARD 4322731b9a8SJean-Christophe PLAGNIOL-VILLARD static void ohci_dump_roothub(ohci_t *controller, int verbose) 4332731b9a8SJean-Christophe PLAGNIOL-VILLARD { 4342731b9a8SJean-Christophe PLAGNIOL-VILLARD __u32 temp, ndp, i; 4352731b9a8SJean-Christophe PLAGNIOL-VILLARD 4362731b9a8SJean-Christophe PLAGNIOL-VILLARD temp = roothub_a(controller); 4372731b9a8SJean-Christophe PLAGNIOL-VILLARD ndp = (temp & RH_A_NDP); 4382731b9a8SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_AT91C_PQFP_UHPBUG 4392731b9a8SJean-Christophe PLAGNIOL-VILLARD ndp = (ndp == 2) ? 1:0; 4402731b9a8SJean-Christophe PLAGNIOL-VILLARD #endif 4412731b9a8SJean-Christophe PLAGNIOL-VILLARD if (verbose) { 4422731b9a8SJean-Christophe PLAGNIOL-VILLARD dbg("roothub.a: %08x POTPGT=%d%s%s%s%s%s NDP=%d", temp, 4432731b9a8SJean-Christophe PLAGNIOL-VILLARD ((temp & RH_A_POTPGT) >> 24) & 0xff, 4442731b9a8SJean-Christophe PLAGNIOL-VILLARD (temp & RH_A_NOCP) ? " NOCP" : "", 4452731b9a8SJean-Christophe PLAGNIOL-VILLARD (temp & RH_A_OCPM) ? " OCPM" : "", 4462731b9a8SJean-Christophe PLAGNIOL-VILLARD (temp & RH_A_DT) ? " DT" : "", 4472731b9a8SJean-Christophe PLAGNIOL-VILLARD (temp & RH_A_NPS) ? " NPS" : "", 4482731b9a8SJean-Christophe PLAGNIOL-VILLARD (temp & RH_A_PSM) ? " PSM" : "", 4492731b9a8SJean-Christophe PLAGNIOL-VILLARD ndp 4502731b9a8SJean-Christophe PLAGNIOL-VILLARD ); 4512731b9a8SJean-Christophe PLAGNIOL-VILLARD temp = roothub_b(controller); 4522731b9a8SJean-Christophe PLAGNIOL-VILLARD dbg("roothub.b: %08x PPCM=%04x DR=%04x", 4532731b9a8SJean-Christophe PLAGNIOL-VILLARD temp, 4542731b9a8SJean-Christophe PLAGNIOL-VILLARD (temp & RH_B_PPCM) >> 16, 4552731b9a8SJean-Christophe PLAGNIOL-VILLARD (temp & RH_B_DR) 4562731b9a8SJean-Christophe PLAGNIOL-VILLARD ); 4572731b9a8SJean-Christophe PLAGNIOL-VILLARD temp = roothub_status(controller); 4582731b9a8SJean-Christophe PLAGNIOL-VILLARD dbg("roothub.status: %08x%s%s%s%s%s%s", 4592731b9a8SJean-Christophe PLAGNIOL-VILLARD temp, 4602731b9a8SJean-Christophe PLAGNIOL-VILLARD (temp & RH_HS_CRWE) ? " CRWE" : "", 4612731b9a8SJean-Christophe PLAGNIOL-VILLARD (temp & RH_HS_OCIC) ? " OCIC" : "", 4622731b9a8SJean-Christophe PLAGNIOL-VILLARD (temp & RH_HS_LPSC) ? " LPSC" : "", 4632731b9a8SJean-Christophe PLAGNIOL-VILLARD (temp & RH_HS_DRWE) ? " DRWE" : "", 4642731b9a8SJean-Christophe PLAGNIOL-VILLARD (temp & RH_HS_OCI) ? " OCI" : "", 4652731b9a8SJean-Christophe PLAGNIOL-VILLARD (temp & RH_HS_LPS) ? " LPS" : "" 4662731b9a8SJean-Christophe PLAGNIOL-VILLARD ); 4672731b9a8SJean-Christophe PLAGNIOL-VILLARD } 4682731b9a8SJean-Christophe PLAGNIOL-VILLARD 4692731b9a8SJean-Christophe PLAGNIOL-VILLARD for (i = 0; i < ndp; i++) { 4702731b9a8SJean-Christophe PLAGNIOL-VILLARD temp = roothub_portstatus(controller, i); 4712731b9a8SJean-Christophe PLAGNIOL-VILLARD dbg("roothub.portstatus [%d] = 0x%08x%s%s%s%s%s%s%s%s%s%s%s%s", 4722731b9a8SJean-Christophe PLAGNIOL-VILLARD i, 4732731b9a8SJean-Christophe PLAGNIOL-VILLARD temp, 4742731b9a8SJean-Christophe PLAGNIOL-VILLARD (temp & RH_PS_PRSC) ? " PRSC" : "", 4752731b9a8SJean-Christophe PLAGNIOL-VILLARD (temp & RH_PS_OCIC) ? " OCIC" : "", 4762731b9a8SJean-Christophe PLAGNIOL-VILLARD (temp & RH_PS_PSSC) ? " PSSC" : "", 4772731b9a8SJean-Christophe PLAGNIOL-VILLARD (temp & RH_PS_PESC) ? " PESC" : "", 4782731b9a8SJean-Christophe PLAGNIOL-VILLARD (temp & RH_PS_CSC) ? " CSC" : "", 4792731b9a8SJean-Christophe PLAGNIOL-VILLARD 4802731b9a8SJean-Christophe PLAGNIOL-VILLARD (temp & RH_PS_LSDA) ? " LSDA" : "", 4812731b9a8SJean-Christophe PLAGNIOL-VILLARD (temp & RH_PS_PPS) ? " PPS" : "", 4822731b9a8SJean-Christophe PLAGNIOL-VILLARD (temp & RH_PS_PRS) ? " PRS" : "", 4832731b9a8SJean-Christophe PLAGNIOL-VILLARD (temp & RH_PS_POCI) ? " POCI" : "", 4842731b9a8SJean-Christophe PLAGNIOL-VILLARD (temp & RH_PS_PSS) ? " PSS" : "", 4852731b9a8SJean-Christophe PLAGNIOL-VILLARD 4862731b9a8SJean-Christophe PLAGNIOL-VILLARD (temp & RH_PS_PES) ? " PES" : "", 4872731b9a8SJean-Christophe PLAGNIOL-VILLARD (temp & RH_PS_CCS) ? " CCS" : "" 4882731b9a8SJean-Christophe PLAGNIOL-VILLARD ); 4892731b9a8SJean-Christophe PLAGNIOL-VILLARD } 4902731b9a8SJean-Christophe PLAGNIOL-VILLARD } 4912731b9a8SJean-Christophe PLAGNIOL-VILLARD 4922731b9a8SJean-Christophe PLAGNIOL-VILLARD static void ohci_dump(ohci_t *controller, int verbose) 4932731b9a8SJean-Christophe PLAGNIOL-VILLARD { 4942731b9a8SJean-Christophe PLAGNIOL-VILLARD dbg("OHCI controller usb-%s state", controller->slot_name); 4952731b9a8SJean-Christophe PLAGNIOL-VILLARD 4962731b9a8SJean-Christophe PLAGNIOL-VILLARD /* dumps some of the state we know about */ 4972731b9a8SJean-Christophe PLAGNIOL-VILLARD ohci_dump_status(controller); 4982731b9a8SJean-Christophe PLAGNIOL-VILLARD if (verbose) 4992731b9a8SJean-Christophe PLAGNIOL-VILLARD ep_print_int_eds(controller, "hcca"); 5008d005ef8SHans de Goede invalidate_dcache_hcca(controller->hcca); 5012731b9a8SJean-Christophe PLAGNIOL-VILLARD dbg("hcca frame #%04x", controller->hcca->frame_no); 5022731b9a8SJean-Christophe PLAGNIOL-VILLARD ohci_dump_roothub(controller, 1); 5032731b9a8SJean-Christophe PLAGNIOL-VILLARD } 5042731b9a8SJean-Christophe PLAGNIOL-VILLARD #endif /* DEBUG */ 5052731b9a8SJean-Christophe PLAGNIOL-VILLARD 5062731b9a8SJean-Christophe PLAGNIOL-VILLARD /*-------------------------------------------------------------------------* 5072731b9a8SJean-Christophe PLAGNIOL-VILLARD * Interface functions (URB) 5082731b9a8SJean-Christophe PLAGNIOL-VILLARD *-------------------------------------------------------------------------*/ 5092731b9a8SJean-Christophe PLAGNIOL-VILLARD 5102731b9a8SJean-Christophe PLAGNIOL-VILLARD /* get a transfer request */ 5112731b9a8SJean-Christophe PLAGNIOL-VILLARD 51219d95d57SHans de Goede int sohci_submit_job(ohci_t *ohci, ohci_dev_t *ohci_dev, urb_priv_t *urb, 51319d95d57SHans de Goede struct devrequest *setup) 5142731b9a8SJean-Christophe PLAGNIOL-VILLARD { 5152731b9a8SJean-Christophe PLAGNIOL-VILLARD ed_t *ed; 5162731b9a8SJean-Christophe PLAGNIOL-VILLARD urb_priv_t *purb_priv = urb; 5172731b9a8SJean-Christophe PLAGNIOL-VILLARD int i, size = 0; 5182731b9a8SJean-Christophe PLAGNIOL-VILLARD struct usb_device *dev = urb->dev; 5192731b9a8SJean-Christophe PLAGNIOL-VILLARD unsigned long pipe = urb->pipe; 5202731b9a8SJean-Christophe PLAGNIOL-VILLARD void *buffer = urb->transfer_buffer; 5212731b9a8SJean-Christophe PLAGNIOL-VILLARD int transfer_len = urb->transfer_buffer_length; 5222731b9a8SJean-Christophe PLAGNIOL-VILLARD int interval = urb->interval; 5232731b9a8SJean-Christophe PLAGNIOL-VILLARD 5242731b9a8SJean-Christophe PLAGNIOL-VILLARD /* when controller's hung, permit only roothub cleanup attempts 5252731b9a8SJean-Christophe PLAGNIOL-VILLARD * such as powering down ports */ 5262731b9a8SJean-Christophe PLAGNIOL-VILLARD if (ohci->disabled) { 5272731b9a8SJean-Christophe PLAGNIOL-VILLARD err("sohci_submit_job: EPIPE"); 5282731b9a8SJean-Christophe PLAGNIOL-VILLARD return -1; 5292731b9a8SJean-Christophe PLAGNIOL-VILLARD } 5302731b9a8SJean-Christophe PLAGNIOL-VILLARD 5312731b9a8SJean-Christophe PLAGNIOL-VILLARD /* we're about to begin a new transaction here so mark the 5322731b9a8SJean-Christophe PLAGNIOL-VILLARD * URB unfinished */ 5332731b9a8SJean-Christophe PLAGNIOL-VILLARD urb->finished = 0; 5342731b9a8SJean-Christophe PLAGNIOL-VILLARD 5352731b9a8SJean-Christophe PLAGNIOL-VILLARD /* every endpoint has a ed, locate and fill it */ 53619d95d57SHans de Goede ed = ep_add_ed(ohci_dev, dev, pipe, interval, 1); 5372731b9a8SJean-Christophe PLAGNIOL-VILLARD if (!ed) { 5382731b9a8SJean-Christophe PLAGNIOL-VILLARD err("sohci_submit_job: ENOMEM"); 5392731b9a8SJean-Christophe PLAGNIOL-VILLARD return -1; 5402731b9a8SJean-Christophe PLAGNIOL-VILLARD } 5412731b9a8SJean-Christophe PLAGNIOL-VILLARD 5422731b9a8SJean-Christophe PLAGNIOL-VILLARD /* for the private part of the URB we need the number of TDs (size) */ 5432731b9a8SJean-Christophe PLAGNIOL-VILLARD switch (usb_pipetype(pipe)) { 5442731b9a8SJean-Christophe PLAGNIOL-VILLARD case PIPE_BULK: /* one TD for every 4096 Byte */ 5452731b9a8SJean-Christophe PLAGNIOL-VILLARD size = (transfer_len - 1) / 4096 + 1; 5462731b9a8SJean-Christophe PLAGNIOL-VILLARD break; 5472731b9a8SJean-Christophe PLAGNIOL-VILLARD case PIPE_CONTROL:/* 1 TD for setup, 1 for ACK and 1 for every 4096 B */ 5482731b9a8SJean-Christophe PLAGNIOL-VILLARD size = (transfer_len == 0)? 2: 5492731b9a8SJean-Christophe PLAGNIOL-VILLARD (transfer_len - 1) / 4096 + 3; 5502731b9a8SJean-Christophe PLAGNIOL-VILLARD break; 5512731b9a8SJean-Christophe PLAGNIOL-VILLARD case PIPE_INTERRUPT: /* 1 TD */ 5522731b9a8SJean-Christophe PLAGNIOL-VILLARD size = 1; 5532731b9a8SJean-Christophe PLAGNIOL-VILLARD break; 5542731b9a8SJean-Christophe PLAGNIOL-VILLARD } 5552731b9a8SJean-Christophe PLAGNIOL-VILLARD 5562731b9a8SJean-Christophe PLAGNIOL-VILLARD ed->purb = urb; 5572731b9a8SJean-Christophe PLAGNIOL-VILLARD 5582731b9a8SJean-Christophe PLAGNIOL-VILLARD if (size >= (N_URB_TD - 1)) { 5592731b9a8SJean-Christophe PLAGNIOL-VILLARD err("need %d TDs, only have %d", size, N_URB_TD); 5602731b9a8SJean-Christophe PLAGNIOL-VILLARD return -1; 5612731b9a8SJean-Christophe PLAGNIOL-VILLARD } 5622731b9a8SJean-Christophe PLAGNIOL-VILLARD purb_priv->pipe = pipe; 5632731b9a8SJean-Christophe PLAGNIOL-VILLARD 5642731b9a8SJean-Christophe PLAGNIOL-VILLARD /* fill the private part of the URB */ 5652731b9a8SJean-Christophe PLAGNIOL-VILLARD purb_priv->length = size; 5662731b9a8SJean-Christophe PLAGNIOL-VILLARD purb_priv->ed = ed; 5672731b9a8SJean-Christophe PLAGNIOL-VILLARD purb_priv->actual_length = 0; 5682731b9a8SJean-Christophe PLAGNIOL-VILLARD 5692731b9a8SJean-Christophe PLAGNIOL-VILLARD /* allocate the TDs */ 5702731b9a8SJean-Christophe PLAGNIOL-VILLARD /* note that td[0] was allocated in ep_add_ed */ 5712731b9a8SJean-Christophe PLAGNIOL-VILLARD for (i = 0; i < size; i++) { 5723c5497d8SHans de Goede purb_priv->td[i] = td_alloc(ohci_dev, dev); 5732731b9a8SJean-Christophe PLAGNIOL-VILLARD if (!purb_priv->td[i]) { 5742731b9a8SJean-Christophe PLAGNIOL-VILLARD purb_priv->length = i; 5752731b9a8SJean-Christophe PLAGNIOL-VILLARD urb_free_priv(purb_priv); 5762731b9a8SJean-Christophe PLAGNIOL-VILLARD err("sohci_submit_job: ENOMEM"); 5772731b9a8SJean-Christophe PLAGNIOL-VILLARD return -1; 5782731b9a8SJean-Christophe PLAGNIOL-VILLARD } 5792731b9a8SJean-Christophe PLAGNIOL-VILLARD } 5802731b9a8SJean-Christophe PLAGNIOL-VILLARD 5812731b9a8SJean-Christophe PLAGNIOL-VILLARD if (ed->state == ED_NEW || (ed->state & ED_DEL)) { 5822731b9a8SJean-Christophe PLAGNIOL-VILLARD urb_free_priv(purb_priv); 5832731b9a8SJean-Christophe PLAGNIOL-VILLARD err("sohci_submit_job: EINVAL"); 5842731b9a8SJean-Christophe PLAGNIOL-VILLARD return -1; 5852731b9a8SJean-Christophe PLAGNIOL-VILLARD } 5862731b9a8SJean-Christophe PLAGNIOL-VILLARD 5872731b9a8SJean-Christophe PLAGNIOL-VILLARD /* link the ed into a chain if is not already */ 5882731b9a8SJean-Christophe PLAGNIOL-VILLARD if (ed->state != ED_OPER) 5892731b9a8SJean-Christophe PLAGNIOL-VILLARD ep_link(ohci, ed); 5902731b9a8SJean-Christophe PLAGNIOL-VILLARD 5912731b9a8SJean-Christophe PLAGNIOL-VILLARD /* fill the TDs and link it to the ed */ 592c5613df5SHans de Goede td_submit_job(ohci, dev, pipe, buffer, transfer_len, 5932731b9a8SJean-Christophe PLAGNIOL-VILLARD setup, purb_priv, interval); 5942731b9a8SJean-Christophe PLAGNIOL-VILLARD 5952731b9a8SJean-Christophe PLAGNIOL-VILLARD return 0; 5962731b9a8SJean-Christophe PLAGNIOL-VILLARD } 5972731b9a8SJean-Christophe PLAGNIOL-VILLARD 5982731b9a8SJean-Christophe PLAGNIOL-VILLARD /*-------------------------------------------------------------------------*/ 5992731b9a8SJean-Christophe PLAGNIOL-VILLARD 6002731b9a8SJean-Christophe PLAGNIOL-VILLARD #ifdef DEBUG 6012731b9a8SJean-Christophe PLAGNIOL-VILLARD /* tell us the current USB frame number */ 602c5613df5SHans de Goede static int sohci_get_current_frame_number(ohci_t *ohci) 6032731b9a8SJean-Christophe PLAGNIOL-VILLARD { 6048d005ef8SHans de Goede invalidate_dcache_hcca(ohci->hcca); 6052731b9a8SJean-Christophe PLAGNIOL-VILLARD return m16_swap(ohci->hcca->frame_no); 6062731b9a8SJean-Christophe PLAGNIOL-VILLARD } 6072731b9a8SJean-Christophe PLAGNIOL-VILLARD #endif 6082731b9a8SJean-Christophe PLAGNIOL-VILLARD 6092731b9a8SJean-Christophe PLAGNIOL-VILLARD /*-------------------------------------------------------------------------* 6102731b9a8SJean-Christophe PLAGNIOL-VILLARD * ED handling functions 6112731b9a8SJean-Christophe PLAGNIOL-VILLARD *-------------------------------------------------------------------------*/ 6122731b9a8SJean-Christophe PLAGNIOL-VILLARD 6132731b9a8SJean-Christophe PLAGNIOL-VILLARD /* search for the right branch to insert an interrupt ed into the int tree 6142731b9a8SJean-Christophe PLAGNIOL-VILLARD * do some load ballancing; 6152731b9a8SJean-Christophe PLAGNIOL-VILLARD * returns the branch and 6162731b9a8SJean-Christophe PLAGNIOL-VILLARD * sets the interval to interval = 2^integer (ld (interval)) */ 6172731b9a8SJean-Christophe PLAGNIOL-VILLARD 6182731b9a8SJean-Christophe PLAGNIOL-VILLARD static int ep_int_ballance(ohci_t *ohci, int interval, int load) 6192731b9a8SJean-Christophe PLAGNIOL-VILLARD { 6202731b9a8SJean-Christophe PLAGNIOL-VILLARD int i, branch = 0; 6212731b9a8SJean-Christophe PLAGNIOL-VILLARD 6222731b9a8SJean-Christophe PLAGNIOL-VILLARD /* search for the least loaded interrupt endpoint 6232731b9a8SJean-Christophe PLAGNIOL-VILLARD * branch of all 32 branches 6242731b9a8SJean-Christophe PLAGNIOL-VILLARD */ 6252731b9a8SJean-Christophe PLAGNIOL-VILLARD for (i = 0; i < 32; i++) 6262731b9a8SJean-Christophe PLAGNIOL-VILLARD if (ohci->ohci_int_load [branch] > ohci->ohci_int_load [i]) 6272731b9a8SJean-Christophe PLAGNIOL-VILLARD branch = i; 6282731b9a8SJean-Christophe PLAGNIOL-VILLARD 6292731b9a8SJean-Christophe PLAGNIOL-VILLARD branch = branch % interval; 6302731b9a8SJean-Christophe PLAGNIOL-VILLARD for (i = branch; i < 32; i += interval) 6312731b9a8SJean-Christophe PLAGNIOL-VILLARD ohci->ohci_int_load [i] += load; 6322731b9a8SJean-Christophe PLAGNIOL-VILLARD 6332731b9a8SJean-Christophe PLAGNIOL-VILLARD return branch; 6342731b9a8SJean-Christophe PLAGNIOL-VILLARD } 6352731b9a8SJean-Christophe PLAGNIOL-VILLARD 6362731b9a8SJean-Christophe PLAGNIOL-VILLARD /*-------------------------------------------------------------------------*/ 6372731b9a8SJean-Christophe PLAGNIOL-VILLARD 6382731b9a8SJean-Christophe PLAGNIOL-VILLARD /* 2^int( ld (inter)) */ 6392731b9a8SJean-Christophe PLAGNIOL-VILLARD 6402731b9a8SJean-Christophe PLAGNIOL-VILLARD static int ep_2_n_interval(int inter) 6412731b9a8SJean-Christophe PLAGNIOL-VILLARD { 6422731b9a8SJean-Christophe PLAGNIOL-VILLARD int i; 6432731b9a8SJean-Christophe PLAGNIOL-VILLARD for (i = 0; ((inter >> i) > 1) && (i < 5); i++); 6442731b9a8SJean-Christophe PLAGNIOL-VILLARD return 1 << i; 6452731b9a8SJean-Christophe PLAGNIOL-VILLARD } 6462731b9a8SJean-Christophe PLAGNIOL-VILLARD 6472731b9a8SJean-Christophe PLAGNIOL-VILLARD /*-------------------------------------------------------------------------*/ 6482731b9a8SJean-Christophe PLAGNIOL-VILLARD 6492731b9a8SJean-Christophe PLAGNIOL-VILLARD /* the int tree is a binary tree 6502731b9a8SJean-Christophe PLAGNIOL-VILLARD * in order to process it sequentially the indexes of the branches have to 6512731b9a8SJean-Christophe PLAGNIOL-VILLARD * be mapped the mapping reverses the bits of a word of num_bits length */ 6522731b9a8SJean-Christophe PLAGNIOL-VILLARD static int ep_rev(int num_bits, int word) 6532731b9a8SJean-Christophe PLAGNIOL-VILLARD { 6542731b9a8SJean-Christophe PLAGNIOL-VILLARD int i, wout = 0; 6552731b9a8SJean-Christophe PLAGNIOL-VILLARD 6562731b9a8SJean-Christophe PLAGNIOL-VILLARD for (i = 0; i < num_bits; i++) 6572731b9a8SJean-Christophe PLAGNIOL-VILLARD wout |= (((word >> i) & 1) << (num_bits - i - 1)); 6582731b9a8SJean-Christophe PLAGNIOL-VILLARD return wout; 6592731b9a8SJean-Christophe PLAGNIOL-VILLARD } 6602731b9a8SJean-Christophe PLAGNIOL-VILLARD 6612731b9a8SJean-Christophe PLAGNIOL-VILLARD /*-------------------------------------------------------------------------* 6622731b9a8SJean-Christophe PLAGNIOL-VILLARD * ED handling functions 6632731b9a8SJean-Christophe PLAGNIOL-VILLARD *-------------------------------------------------------------------------*/ 6642731b9a8SJean-Christophe PLAGNIOL-VILLARD 6652731b9a8SJean-Christophe PLAGNIOL-VILLARD /* link an ed into one of the HC chains */ 6662731b9a8SJean-Christophe PLAGNIOL-VILLARD 6672731b9a8SJean-Christophe PLAGNIOL-VILLARD static int ep_link(ohci_t *ohci, ed_t *edi) 6682731b9a8SJean-Christophe PLAGNIOL-VILLARD { 6692731b9a8SJean-Christophe PLAGNIOL-VILLARD volatile ed_t *ed = edi; 6702731b9a8SJean-Christophe PLAGNIOL-VILLARD int int_branch; 6712731b9a8SJean-Christophe PLAGNIOL-VILLARD int i; 6722731b9a8SJean-Christophe PLAGNIOL-VILLARD int inter; 6732731b9a8SJean-Christophe PLAGNIOL-VILLARD int interval; 6742731b9a8SJean-Christophe PLAGNIOL-VILLARD int load; 6752731b9a8SJean-Christophe PLAGNIOL-VILLARD __u32 *ed_p; 6762731b9a8SJean-Christophe PLAGNIOL-VILLARD 6772731b9a8SJean-Christophe PLAGNIOL-VILLARD ed->state = ED_OPER; 6782731b9a8SJean-Christophe PLAGNIOL-VILLARD ed->int_interval = 0; 6792731b9a8SJean-Christophe PLAGNIOL-VILLARD 6802731b9a8SJean-Christophe PLAGNIOL-VILLARD switch (ed->type) { 6812731b9a8SJean-Christophe PLAGNIOL-VILLARD case PIPE_CONTROL: 6822731b9a8SJean-Christophe PLAGNIOL-VILLARD ed->hwNextED = 0; 6838d005ef8SHans de Goede flush_dcache_ed(ed); 6842731b9a8SJean-Christophe PLAGNIOL-VILLARD if (ohci->ed_controltail == NULL) 685a5496a18SBecky Bruce ohci_writel(ed, &ohci->regs->ed_controlhead); 6862731b9a8SJean-Christophe PLAGNIOL-VILLARD else 6872731b9a8SJean-Christophe PLAGNIOL-VILLARD ohci->ed_controltail->hwNextED = 6882731b9a8SJean-Christophe PLAGNIOL-VILLARD m32_swap((unsigned long)ed); 6892731b9a8SJean-Christophe PLAGNIOL-VILLARD 6902731b9a8SJean-Christophe PLAGNIOL-VILLARD ed->ed_prev = ohci->ed_controltail; 6912731b9a8SJean-Christophe PLAGNIOL-VILLARD if (!ohci->ed_controltail && !ohci->ed_rm_list[0] && 6922731b9a8SJean-Christophe PLAGNIOL-VILLARD !ohci->ed_rm_list[1] && !ohci->sleeping) { 6932731b9a8SJean-Christophe PLAGNIOL-VILLARD ohci->hc_control |= OHCI_CTRL_CLE; 694a5496a18SBecky Bruce ohci_writel(ohci->hc_control, &ohci->regs->control); 6952731b9a8SJean-Christophe PLAGNIOL-VILLARD } 6962731b9a8SJean-Christophe PLAGNIOL-VILLARD ohci->ed_controltail = edi; 6972731b9a8SJean-Christophe PLAGNIOL-VILLARD break; 6982731b9a8SJean-Christophe PLAGNIOL-VILLARD 6992731b9a8SJean-Christophe PLAGNIOL-VILLARD case PIPE_BULK: 7002731b9a8SJean-Christophe PLAGNIOL-VILLARD ed->hwNextED = 0; 7018d005ef8SHans de Goede flush_dcache_ed(ed); 7022731b9a8SJean-Christophe PLAGNIOL-VILLARD if (ohci->ed_bulktail == NULL) 703a5496a18SBecky Bruce ohci_writel(ed, &ohci->regs->ed_bulkhead); 7042731b9a8SJean-Christophe PLAGNIOL-VILLARD else 7052731b9a8SJean-Christophe PLAGNIOL-VILLARD ohci->ed_bulktail->hwNextED = 7062731b9a8SJean-Christophe PLAGNIOL-VILLARD m32_swap((unsigned long)ed); 7072731b9a8SJean-Christophe PLAGNIOL-VILLARD 7082731b9a8SJean-Christophe PLAGNIOL-VILLARD ed->ed_prev = ohci->ed_bulktail; 7092731b9a8SJean-Christophe PLAGNIOL-VILLARD if (!ohci->ed_bulktail && !ohci->ed_rm_list[0] && 7102731b9a8SJean-Christophe PLAGNIOL-VILLARD !ohci->ed_rm_list[1] && !ohci->sleeping) { 7112731b9a8SJean-Christophe PLAGNIOL-VILLARD ohci->hc_control |= OHCI_CTRL_BLE; 712a5496a18SBecky Bruce ohci_writel(ohci->hc_control, &ohci->regs->control); 7132731b9a8SJean-Christophe PLAGNIOL-VILLARD } 7142731b9a8SJean-Christophe PLAGNIOL-VILLARD ohci->ed_bulktail = edi; 7152731b9a8SJean-Christophe PLAGNIOL-VILLARD break; 7162731b9a8SJean-Christophe PLAGNIOL-VILLARD 7172731b9a8SJean-Christophe PLAGNIOL-VILLARD case PIPE_INTERRUPT: 7182731b9a8SJean-Christophe PLAGNIOL-VILLARD load = ed->int_load; 7192731b9a8SJean-Christophe PLAGNIOL-VILLARD interval = ep_2_n_interval(ed->int_period); 7202731b9a8SJean-Christophe PLAGNIOL-VILLARD ed->int_interval = interval; 7212731b9a8SJean-Christophe PLAGNIOL-VILLARD int_branch = ep_int_ballance(ohci, interval, load); 7222731b9a8SJean-Christophe PLAGNIOL-VILLARD ed->int_branch = int_branch; 7232731b9a8SJean-Christophe PLAGNIOL-VILLARD 7242731b9a8SJean-Christophe PLAGNIOL-VILLARD for (i = 0; i < ep_rev(6, interval); i += inter) { 7252731b9a8SJean-Christophe PLAGNIOL-VILLARD inter = 1; 7262731b9a8SJean-Christophe PLAGNIOL-VILLARD for (ed_p = &(ohci->hcca->int_table[\ 7272731b9a8SJean-Christophe PLAGNIOL-VILLARD ep_rev(5, i) + int_branch]); 7282731b9a8SJean-Christophe PLAGNIOL-VILLARD (*ed_p != 0) && 7292731b9a8SJean-Christophe PLAGNIOL-VILLARD (((ed_t *)ed_p)->int_interval >= interval); 7302731b9a8SJean-Christophe PLAGNIOL-VILLARD ed_p = &(((ed_t *)ed_p)->hwNextED)) 7312731b9a8SJean-Christophe PLAGNIOL-VILLARD inter = ep_rev(6, 7322731b9a8SJean-Christophe PLAGNIOL-VILLARD ((ed_t *)ed_p)->int_interval); 7332731b9a8SJean-Christophe PLAGNIOL-VILLARD ed->hwNextED = *ed_p; 7348d005ef8SHans de Goede flush_dcache_ed(ed); 7352731b9a8SJean-Christophe PLAGNIOL-VILLARD *ed_p = m32_swap((unsigned long)ed); 7368d005ef8SHans de Goede flush_dcache_hcca(ohci->hcca); 7372731b9a8SJean-Christophe PLAGNIOL-VILLARD } 7382731b9a8SJean-Christophe PLAGNIOL-VILLARD break; 7392731b9a8SJean-Christophe PLAGNIOL-VILLARD } 7402731b9a8SJean-Christophe PLAGNIOL-VILLARD return 0; 7412731b9a8SJean-Christophe PLAGNIOL-VILLARD } 7422731b9a8SJean-Christophe PLAGNIOL-VILLARD 7432731b9a8SJean-Christophe PLAGNIOL-VILLARD /*-------------------------------------------------------------------------*/ 7442731b9a8SJean-Christophe PLAGNIOL-VILLARD 7452731b9a8SJean-Christophe PLAGNIOL-VILLARD /* scan the periodic table to find and unlink this ED */ 7462731b9a8SJean-Christophe PLAGNIOL-VILLARD static void periodic_unlink(struct ohci *ohci, volatile struct ed *ed, 7472731b9a8SJean-Christophe PLAGNIOL-VILLARD unsigned index, unsigned period) 7482731b9a8SJean-Christophe PLAGNIOL-VILLARD { 7498d005ef8SHans de Goede __maybe_unused unsigned long aligned_ed_p; 7508d005ef8SHans de Goede 7512731b9a8SJean-Christophe PLAGNIOL-VILLARD for (; index < NUM_INTS; index += period) { 7522731b9a8SJean-Christophe PLAGNIOL-VILLARD __u32 *ed_p = &ohci->hcca->int_table [index]; 7532731b9a8SJean-Christophe PLAGNIOL-VILLARD 7542731b9a8SJean-Christophe PLAGNIOL-VILLARD /* ED might have been unlinked through another path */ 7552731b9a8SJean-Christophe PLAGNIOL-VILLARD while (*ed_p != 0) { 7562731b9a8SJean-Christophe PLAGNIOL-VILLARD if (((struct ed *) 7572731b9a8SJean-Christophe PLAGNIOL-VILLARD m32_swap((unsigned long)ed_p)) == ed) { 7582731b9a8SJean-Christophe PLAGNIOL-VILLARD *ed_p = ed->hwNextED; 7598d005ef8SHans de Goede aligned_ed_p = (unsigned long)ed_p; 7608d005ef8SHans de Goede aligned_ed_p &= ~(ARCH_DMA_MINALIGN - 1); 7618d005ef8SHans de Goede flush_dcache_range(aligned_ed_p, 7628d005ef8SHans de Goede aligned_ed_p + ARCH_DMA_MINALIGN); 7632731b9a8SJean-Christophe PLAGNIOL-VILLARD break; 7642731b9a8SJean-Christophe PLAGNIOL-VILLARD } 7652731b9a8SJean-Christophe PLAGNIOL-VILLARD ed_p = &(((struct ed *) 7662731b9a8SJean-Christophe PLAGNIOL-VILLARD m32_swap((unsigned long)ed_p))->hwNextED); 7672731b9a8SJean-Christophe PLAGNIOL-VILLARD } 7682731b9a8SJean-Christophe PLAGNIOL-VILLARD } 7692731b9a8SJean-Christophe PLAGNIOL-VILLARD } 7702731b9a8SJean-Christophe PLAGNIOL-VILLARD 7712731b9a8SJean-Christophe PLAGNIOL-VILLARD /* unlink an ed from one of the HC chains. 7722731b9a8SJean-Christophe PLAGNIOL-VILLARD * just the link to the ed is unlinked. 7732731b9a8SJean-Christophe PLAGNIOL-VILLARD * the link from the ed still points to another operational ed or 0 7742731b9a8SJean-Christophe PLAGNIOL-VILLARD * so the HC can eventually finish the processing of the unlinked ed */ 7752731b9a8SJean-Christophe PLAGNIOL-VILLARD 7762731b9a8SJean-Christophe PLAGNIOL-VILLARD static int ep_unlink(ohci_t *ohci, ed_t *edi) 7772731b9a8SJean-Christophe PLAGNIOL-VILLARD { 7782731b9a8SJean-Christophe PLAGNIOL-VILLARD volatile ed_t *ed = edi; 7792731b9a8SJean-Christophe PLAGNIOL-VILLARD int i; 7802731b9a8SJean-Christophe PLAGNIOL-VILLARD 7812731b9a8SJean-Christophe PLAGNIOL-VILLARD ed->hwINFO |= m32_swap(OHCI_ED_SKIP); 7828d005ef8SHans de Goede flush_dcache_ed(ed); 7832731b9a8SJean-Christophe PLAGNIOL-VILLARD 7842731b9a8SJean-Christophe PLAGNIOL-VILLARD switch (ed->type) { 7852731b9a8SJean-Christophe PLAGNIOL-VILLARD case PIPE_CONTROL: 7862731b9a8SJean-Christophe PLAGNIOL-VILLARD if (ed->ed_prev == NULL) { 7872731b9a8SJean-Christophe PLAGNIOL-VILLARD if (!ed->hwNextED) { 7882731b9a8SJean-Christophe PLAGNIOL-VILLARD ohci->hc_control &= ~OHCI_CTRL_CLE; 789a5496a18SBecky Bruce ohci_writel(ohci->hc_control, 790a5496a18SBecky Bruce &ohci->regs->control); 7912731b9a8SJean-Christophe PLAGNIOL-VILLARD } 792a5496a18SBecky Bruce ohci_writel(m32_swap(*((__u32 *)&ed->hwNextED)), 7932731b9a8SJean-Christophe PLAGNIOL-VILLARD &ohci->regs->ed_controlhead); 7942731b9a8SJean-Christophe PLAGNIOL-VILLARD } else { 7952731b9a8SJean-Christophe PLAGNIOL-VILLARD ed->ed_prev->hwNextED = ed->hwNextED; 7968d005ef8SHans de Goede flush_dcache_ed(ed->ed_prev); 7972731b9a8SJean-Christophe PLAGNIOL-VILLARD } 7982731b9a8SJean-Christophe PLAGNIOL-VILLARD if (ohci->ed_controltail == ed) { 7992731b9a8SJean-Christophe PLAGNIOL-VILLARD ohci->ed_controltail = ed->ed_prev; 8002731b9a8SJean-Christophe PLAGNIOL-VILLARD } else { 8012731b9a8SJean-Christophe PLAGNIOL-VILLARD ((ed_t *)m32_swap( 8022731b9a8SJean-Christophe PLAGNIOL-VILLARD *((__u32 *)&ed->hwNextED)))->ed_prev = ed->ed_prev; 8032731b9a8SJean-Christophe PLAGNIOL-VILLARD } 8042731b9a8SJean-Christophe PLAGNIOL-VILLARD break; 8052731b9a8SJean-Christophe PLAGNIOL-VILLARD 8062731b9a8SJean-Christophe PLAGNIOL-VILLARD case PIPE_BULK: 8072731b9a8SJean-Christophe PLAGNIOL-VILLARD if (ed->ed_prev == NULL) { 8082731b9a8SJean-Christophe PLAGNIOL-VILLARD if (!ed->hwNextED) { 8092731b9a8SJean-Christophe PLAGNIOL-VILLARD ohci->hc_control &= ~OHCI_CTRL_BLE; 810a5496a18SBecky Bruce ohci_writel(ohci->hc_control, 811a5496a18SBecky Bruce &ohci->regs->control); 8122731b9a8SJean-Christophe PLAGNIOL-VILLARD } 813a5496a18SBecky Bruce ohci_writel(m32_swap(*((__u32 *)&ed->hwNextED)), 8142731b9a8SJean-Christophe PLAGNIOL-VILLARD &ohci->regs->ed_bulkhead); 8152731b9a8SJean-Christophe PLAGNIOL-VILLARD } else { 8162731b9a8SJean-Christophe PLAGNIOL-VILLARD ed->ed_prev->hwNextED = ed->hwNextED; 8178d005ef8SHans de Goede flush_dcache_ed(ed->ed_prev); 8182731b9a8SJean-Christophe PLAGNIOL-VILLARD } 8192731b9a8SJean-Christophe PLAGNIOL-VILLARD if (ohci->ed_bulktail == ed) { 8202731b9a8SJean-Christophe PLAGNIOL-VILLARD ohci->ed_bulktail = ed->ed_prev; 8212731b9a8SJean-Christophe PLAGNIOL-VILLARD } else { 8222731b9a8SJean-Christophe PLAGNIOL-VILLARD ((ed_t *)m32_swap( 8232731b9a8SJean-Christophe PLAGNIOL-VILLARD *((__u32 *)&ed->hwNextED)))->ed_prev = ed->ed_prev; 8242731b9a8SJean-Christophe PLAGNIOL-VILLARD } 8252731b9a8SJean-Christophe PLAGNIOL-VILLARD break; 8262731b9a8SJean-Christophe PLAGNIOL-VILLARD 8272731b9a8SJean-Christophe PLAGNIOL-VILLARD case PIPE_INTERRUPT: 8282731b9a8SJean-Christophe PLAGNIOL-VILLARD periodic_unlink(ohci, ed, 0, 1); 8292731b9a8SJean-Christophe PLAGNIOL-VILLARD for (i = ed->int_branch; i < 32; i += ed->int_interval) 8302731b9a8SJean-Christophe PLAGNIOL-VILLARD ohci->ohci_int_load[i] -= ed->int_load; 8312731b9a8SJean-Christophe PLAGNIOL-VILLARD break; 8322731b9a8SJean-Christophe PLAGNIOL-VILLARD } 8332731b9a8SJean-Christophe PLAGNIOL-VILLARD ed->state = ED_UNLINK; 8342731b9a8SJean-Christophe PLAGNIOL-VILLARD return 0; 8352731b9a8SJean-Christophe PLAGNIOL-VILLARD } 8362731b9a8SJean-Christophe PLAGNIOL-VILLARD 8372731b9a8SJean-Christophe PLAGNIOL-VILLARD /*-------------------------------------------------------------------------*/ 8382731b9a8SJean-Christophe PLAGNIOL-VILLARD 8392731b9a8SJean-Christophe PLAGNIOL-VILLARD /* add/reinit an endpoint; this should be done once at the 8402731b9a8SJean-Christophe PLAGNIOL-VILLARD * usb_set_configuration command, but the USB stack is a little bit 8412731b9a8SJean-Christophe PLAGNIOL-VILLARD * stateless so we do it at every transaction if the state of the ed 8422731b9a8SJean-Christophe PLAGNIOL-VILLARD * is ED_NEW then a dummy td is added and the state is changed to 8432731b9a8SJean-Christophe PLAGNIOL-VILLARD * ED_UNLINK in all other cases the state is left unchanged the ed 8442731b9a8SJean-Christophe PLAGNIOL-VILLARD * info fields are setted anyway even though most of them should not 8452731b9a8SJean-Christophe PLAGNIOL-VILLARD * change 8462731b9a8SJean-Christophe PLAGNIOL-VILLARD */ 84719d95d57SHans de Goede static ed_t *ep_add_ed(ohci_dev_t *ohci_dev, struct usb_device *usb_dev, 84819d95d57SHans de Goede unsigned long pipe, int interval, int load) 8492731b9a8SJean-Christophe PLAGNIOL-VILLARD { 8502731b9a8SJean-Christophe PLAGNIOL-VILLARD td_t *td; 8512731b9a8SJean-Christophe PLAGNIOL-VILLARD ed_t *ed_ret; 8522731b9a8SJean-Christophe PLAGNIOL-VILLARD volatile ed_t *ed; 8532731b9a8SJean-Christophe PLAGNIOL-VILLARD 85419d95d57SHans de Goede ed = ed_ret = &ohci_dev->ed[(usb_pipeendpoint(pipe) << 1) | 8552731b9a8SJean-Christophe PLAGNIOL-VILLARD (usb_pipecontrol(pipe)? 0: usb_pipeout(pipe))]; 8562731b9a8SJean-Christophe PLAGNIOL-VILLARD 8572731b9a8SJean-Christophe PLAGNIOL-VILLARD if ((ed->state & ED_DEL) || (ed->state & ED_URB_DEL)) { 8582731b9a8SJean-Christophe PLAGNIOL-VILLARD err("ep_add_ed: pending delete"); 8592731b9a8SJean-Christophe PLAGNIOL-VILLARD /* pending delete request */ 8602731b9a8SJean-Christophe PLAGNIOL-VILLARD return NULL; 8612731b9a8SJean-Christophe PLAGNIOL-VILLARD } 8622731b9a8SJean-Christophe PLAGNIOL-VILLARD 8632731b9a8SJean-Christophe PLAGNIOL-VILLARD if (ed->state == ED_NEW) { 8642731b9a8SJean-Christophe PLAGNIOL-VILLARD /* dummy td; end of td list for ed */ 8653c5497d8SHans de Goede td = td_alloc(ohci_dev, usb_dev); 8662731b9a8SJean-Christophe PLAGNIOL-VILLARD ed->hwTailP = m32_swap((unsigned long)td); 8672731b9a8SJean-Christophe PLAGNIOL-VILLARD ed->hwHeadP = ed->hwTailP; 8682731b9a8SJean-Christophe PLAGNIOL-VILLARD ed->state = ED_UNLINK; 8692731b9a8SJean-Christophe PLAGNIOL-VILLARD ed->type = usb_pipetype(pipe); 87019d95d57SHans de Goede ohci_dev->ed_cnt++; 8712731b9a8SJean-Christophe PLAGNIOL-VILLARD } 8722731b9a8SJean-Christophe PLAGNIOL-VILLARD 8732731b9a8SJean-Christophe PLAGNIOL-VILLARD ed->hwINFO = m32_swap(usb_pipedevice(pipe) 8742731b9a8SJean-Christophe PLAGNIOL-VILLARD | usb_pipeendpoint(pipe) << 7 8752731b9a8SJean-Christophe PLAGNIOL-VILLARD | (usb_pipeisoc(pipe)? 0x8000: 0) 8762731b9a8SJean-Christophe PLAGNIOL-VILLARD | (usb_pipecontrol(pipe)? 0: \ 8772731b9a8SJean-Christophe PLAGNIOL-VILLARD (usb_pipeout(pipe)? 0x800: 0x1000)) 878c60795f4SIlya Yanok | (usb_dev->speed == USB_SPEED_LOW) << 13 8792731b9a8SJean-Christophe PLAGNIOL-VILLARD | usb_maxpacket(usb_dev, pipe) << 16); 8802731b9a8SJean-Christophe PLAGNIOL-VILLARD 8812731b9a8SJean-Christophe PLAGNIOL-VILLARD if (ed->type == PIPE_INTERRUPT && ed->state == ED_UNLINK) { 8822731b9a8SJean-Christophe PLAGNIOL-VILLARD ed->int_period = interval; 8832731b9a8SJean-Christophe PLAGNIOL-VILLARD ed->int_load = load; 8842731b9a8SJean-Christophe PLAGNIOL-VILLARD } 8852731b9a8SJean-Christophe PLAGNIOL-VILLARD 8868d005ef8SHans de Goede flush_dcache_ed(ed); 8878d005ef8SHans de Goede 8882731b9a8SJean-Christophe PLAGNIOL-VILLARD return ed_ret; 8892731b9a8SJean-Christophe PLAGNIOL-VILLARD } 8902731b9a8SJean-Christophe PLAGNIOL-VILLARD 8912731b9a8SJean-Christophe PLAGNIOL-VILLARD /*-------------------------------------------------------------------------* 8922731b9a8SJean-Christophe PLAGNIOL-VILLARD * TD handling functions 8932731b9a8SJean-Christophe PLAGNIOL-VILLARD *-------------------------------------------------------------------------*/ 8942731b9a8SJean-Christophe PLAGNIOL-VILLARD 8952731b9a8SJean-Christophe PLAGNIOL-VILLARD /* enqueue next TD for this URB (OHCI spec 5.2.8.2) */ 8962731b9a8SJean-Christophe PLAGNIOL-VILLARD 8972731b9a8SJean-Christophe PLAGNIOL-VILLARD static void td_fill(ohci_t *ohci, unsigned int info, 8982731b9a8SJean-Christophe PLAGNIOL-VILLARD void *data, int len, 8992731b9a8SJean-Christophe PLAGNIOL-VILLARD struct usb_device *dev, int index, urb_priv_t *urb_priv) 9002731b9a8SJean-Christophe PLAGNIOL-VILLARD { 9012731b9a8SJean-Christophe PLAGNIOL-VILLARD volatile td_t *td, *td_pt; 9022731b9a8SJean-Christophe PLAGNIOL-VILLARD #ifdef OHCI_FILL_TRACE 9032731b9a8SJean-Christophe PLAGNIOL-VILLARD int i; 9042731b9a8SJean-Christophe PLAGNIOL-VILLARD #endif 9052731b9a8SJean-Christophe PLAGNIOL-VILLARD 9062731b9a8SJean-Christophe PLAGNIOL-VILLARD if (index > urb_priv->length) { 9072731b9a8SJean-Christophe PLAGNIOL-VILLARD err("index > length"); 9082731b9a8SJean-Christophe PLAGNIOL-VILLARD return; 9092731b9a8SJean-Christophe PLAGNIOL-VILLARD } 9102731b9a8SJean-Christophe PLAGNIOL-VILLARD /* use this td as the next dummy */ 9112731b9a8SJean-Christophe PLAGNIOL-VILLARD td_pt = urb_priv->td [index]; 9122731b9a8SJean-Christophe PLAGNIOL-VILLARD td_pt->hwNextTD = 0; 9138d005ef8SHans de Goede flush_dcache_td(td_pt); 9142731b9a8SJean-Christophe PLAGNIOL-VILLARD 9152731b9a8SJean-Christophe PLAGNIOL-VILLARD /* fill the old dummy TD */ 9162731b9a8SJean-Christophe PLAGNIOL-VILLARD td = urb_priv->td [index] = 9172731b9a8SJean-Christophe PLAGNIOL-VILLARD (td_t *)(m32_swap(urb_priv->ed->hwTailP) & ~0xf); 9182731b9a8SJean-Christophe PLAGNIOL-VILLARD 9192731b9a8SJean-Christophe PLAGNIOL-VILLARD td->ed = urb_priv->ed; 9202731b9a8SJean-Christophe PLAGNIOL-VILLARD td->next_dl_td = NULL; 9212731b9a8SJean-Christophe PLAGNIOL-VILLARD td->index = index; 9222731b9a8SJean-Christophe PLAGNIOL-VILLARD td->data = (__u32)data; 9232731b9a8SJean-Christophe PLAGNIOL-VILLARD #ifdef OHCI_FILL_TRACE 9242731b9a8SJean-Christophe PLAGNIOL-VILLARD if (usb_pipebulk(urb_priv->pipe) && usb_pipeout(urb_priv->pipe)) { 9252731b9a8SJean-Christophe PLAGNIOL-VILLARD for (i = 0; i < len; i++) 9262731b9a8SJean-Christophe PLAGNIOL-VILLARD printf("td->data[%d] %#2x ", i, ((unsigned char *)td->data)[i]); 9272731b9a8SJean-Christophe PLAGNIOL-VILLARD printf("\n"); 9282731b9a8SJean-Christophe PLAGNIOL-VILLARD } 9292731b9a8SJean-Christophe PLAGNIOL-VILLARD #endif 9302731b9a8SJean-Christophe PLAGNIOL-VILLARD if (!len) 9312731b9a8SJean-Christophe PLAGNIOL-VILLARD data = 0; 9322731b9a8SJean-Christophe PLAGNIOL-VILLARD 9332731b9a8SJean-Christophe PLAGNIOL-VILLARD td->hwINFO = m32_swap(info); 9342731b9a8SJean-Christophe PLAGNIOL-VILLARD td->hwCBP = m32_swap((unsigned long)data); 9352731b9a8SJean-Christophe PLAGNIOL-VILLARD if (data) 9362731b9a8SJean-Christophe PLAGNIOL-VILLARD td->hwBE = m32_swap((unsigned long)(data + len - 1)); 9372731b9a8SJean-Christophe PLAGNIOL-VILLARD else 9382731b9a8SJean-Christophe PLAGNIOL-VILLARD td->hwBE = 0; 9392731b9a8SJean-Christophe PLAGNIOL-VILLARD 9402731b9a8SJean-Christophe PLAGNIOL-VILLARD td->hwNextTD = m32_swap((unsigned long)td_pt); 9418d005ef8SHans de Goede flush_dcache_td(td); 9422731b9a8SJean-Christophe PLAGNIOL-VILLARD 9432731b9a8SJean-Christophe PLAGNIOL-VILLARD /* append to queue */ 9442731b9a8SJean-Christophe PLAGNIOL-VILLARD td->ed->hwTailP = td->hwNextTD; 9458d005ef8SHans de Goede flush_dcache_ed(td->ed); 9462731b9a8SJean-Christophe PLAGNIOL-VILLARD } 9472731b9a8SJean-Christophe PLAGNIOL-VILLARD 9482731b9a8SJean-Christophe PLAGNIOL-VILLARD /*-------------------------------------------------------------------------*/ 9492731b9a8SJean-Christophe PLAGNIOL-VILLARD 9502731b9a8SJean-Christophe PLAGNIOL-VILLARD /* prepare all TDs of a transfer */ 9512731b9a8SJean-Christophe PLAGNIOL-VILLARD 952c5613df5SHans de Goede static void td_submit_job(ohci_t *ohci, struct usb_device *dev, 953c5613df5SHans de Goede unsigned long pipe, void *buffer, int transfer_len, 9542731b9a8SJean-Christophe PLAGNIOL-VILLARD struct devrequest *setup, urb_priv_t *urb, 9552731b9a8SJean-Christophe PLAGNIOL-VILLARD int interval) 9562731b9a8SJean-Christophe PLAGNIOL-VILLARD { 9572731b9a8SJean-Christophe PLAGNIOL-VILLARD int data_len = transfer_len; 9582731b9a8SJean-Christophe PLAGNIOL-VILLARD void *data; 9592731b9a8SJean-Christophe PLAGNIOL-VILLARD int cnt = 0; 9602731b9a8SJean-Christophe PLAGNIOL-VILLARD __u32 info = 0; 9612731b9a8SJean-Christophe PLAGNIOL-VILLARD unsigned int toggle = 0; 9622731b9a8SJean-Christophe PLAGNIOL-VILLARD 9638d005ef8SHans de Goede flush_dcache_buffer(buffer, data_len); 9648d005ef8SHans de Goede 9652731b9a8SJean-Christophe PLAGNIOL-VILLARD /* OHCI handles the DATA-toggles itself, we just use the USB-toggle 9662731b9a8SJean-Christophe PLAGNIOL-VILLARD * bits for reseting */ 9672731b9a8SJean-Christophe PLAGNIOL-VILLARD if (usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe))) { 9682731b9a8SJean-Christophe PLAGNIOL-VILLARD toggle = TD_T_TOGGLE; 9692731b9a8SJean-Christophe PLAGNIOL-VILLARD } else { 9702731b9a8SJean-Christophe PLAGNIOL-VILLARD toggle = TD_T_DATA0; 9712731b9a8SJean-Christophe PLAGNIOL-VILLARD usb_settoggle(dev, usb_pipeendpoint(pipe), 9722731b9a8SJean-Christophe PLAGNIOL-VILLARD usb_pipeout(pipe), 1); 9732731b9a8SJean-Christophe PLAGNIOL-VILLARD } 9742731b9a8SJean-Christophe PLAGNIOL-VILLARD urb->td_cnt = 0; 9752731b9a8SJean-Christophe PLAGNIOL-VILLARD if (data_len) 9762731b9a8SJean-Christophe PLAGNIOL-VILLARD data = buffer; 9772731b9a8SJean-Christophe PLAGNIOL-VILLARD else 9782731b9a8SJean-Christophe PLAGNIOL-VILLARD data = 0; 9792731b9a8SJean-Christophe PLAGNIOL-VILLARD 9802731b9a8SJean-Christophe PLAGNIOL-VILLARD switch (usb_pipetype(pipe)) { 9812731b9a8SJean-Christophe PLAGNIOL-VILLARD case PIPE_BULK: 9822731b9a8SJean-Christophe PLAGNIOL-VILLARD info = usb_pipeout(pipe)? 9832731b9a8SJean-Christophe PLAGNIOL-VILLARD TD_CC | TD_DP_OUT : TD_CC | TD_DP_IN ; 9842731b9a8SJean-Christophe PLAGNIOL-VILLARD while (data_len > 4096) { 9852731b9a8SJean-Christophe PLAGNIOL-VILLARD td_fill(ohci, info | (cnt? TD_T_TOGGLE:toggle), 9862731b9a8SJean-Christophe PLAGNIOL-VILLARD data, 4096, dev, cnt, urb); 9872731b9a8SJean-Christophe PLAGNIOL-VILLARD data += 4096; data_len -= 4096; cnt++; 9882731b9a8SJean-Christophe PLAGNIOL-VILLARD } 9892731b9a8SJean-Christophe PLAGNIOL-VILLARD info = usb_pipeout(pipe)? 9902731b9a8SJean-Christophe PLAGNIOL-VILLARD TD_CC | TD_DP_OUT : TD_CC | TD_R | TD_DP_IN ; 9912731b9a8SJean-Christophe PLAGNIOL-VILLARD td_fill(ohci, info | (cnt? TD_T_TOGGLE:toggle), data, 9922731b9a8SJean-Christophe PLAGNIOL-VILLARD data_len, dev, cnt, urb); 9932731b9a8SJean-Christophe PLAGNIOL-VILLARD cnt++; 9942731b9a8SJean-Christophe PLAGNIOL-VILLARD 9952731b9a8SJean-Christophe PLAGNIOL-VILLARD if (!ohci->sleeping) { 9962731b9a8SJean-Christophe PLAGNIOL-VILLARD /* start bulk list */ 997a5496a18SBecky Bruce ohci_writel(OHCI_BLF, &ohci->regs->cmdstatus); 9982731b9a8SJean-Christophe PLAGNIOL-VILLARD } 9992731b9a8SJean-Christophe PLAGNIOL-VILLARD break; 10002731b9a8SJean-Christophe PLAGNIOL-VILLARD 10012731b9a8SJean-Christophe PLAGNIOL-VILLARD case PIPE_CONTROL: 10022731b9a8SJean-Christophe PLAGNIOL-VILLARD /* Setup phase */ 10032731b9a8SJean-Christophe PLAGNIOL-VILLARD info = TD_CC | TD_DP_SETUP | TD_T_DATA0; 10048d005ef8SHans de Goede flush_dcache_buffer(setup, 8); 10052731b9a8SJean-Christophe PLAGNIOL-VILLARD td_fill(ohci, info, setup, 8, dev, cnt++, urb); 10062731b9a8SJean-Christophe PLAGNIOL-VILLARD 10072731b9a8SJean-Christophe PLAGNIOL-VILLARD /* Optional Data phase */ 10082731b9a8SJean-Christophe PLAGNIOL-VILLARD if (data_len > 0) { 10092731b9a8SJean-Christophe PLAGNIOL-VILLARD info = usb_pipeout(pipe)? 10102731b9a8SJean-Christophe PLAGNIOL-VILLARD TD_CC | TD_R | TD_DP_OUT | TD_T_DATA1 : 10112731b9a8SJean-Christophe PLAGNIOL-VILLARD TD_CC | TD_R | TD_DP_IN | TD_T_DATA1; 10122731b9a8SJean-Christophe PLAGNIOL-VILLARD /* NOTE: mishandles transfers >8K, some >4K */ 10132731b9a8SJean-Christophe PLAGNIOL-VILLARD td_fill(ohci, info, data, data_len, dev, cnt++, urb); 10142731b9a8SJean-Christophe PLAGNIOL-VILLARD } 10152731b9a8SJean-Christophe PLAGNIOL-VILLARD 10162731b9a8SJean-Christophe PLAGNIOL-VILLARD /* Status phase */ 1017cae01cb2SHans de Goede info = (usb_pipeout(pipe) || data_len == 0) ? 10182731b9a8SJean-Christophe PLAGNIOL-VILLARD TD_CC | TD_DP_IN | TD_T_DATA1: 10192731b9a8SJean-Christophe PLAGNIOL-VILLARD TD_CC | TD_DP_OUT | TD_T_DATA1; 10202731b9a8SJean-Christophe PLAGNIOL-VILLARD td_fill(ohci, info, data, 0, dev, cnt++, urb); 10212731b9a8SJean-Christophe PLAGNIOL-VILLARD 10222731b9a8SJean-Christophe PLAGNIOL-VILLARD if (!ohci->sleeping) { 10232731b9a8SJean-Christophe PLAGNIOL-VILLARD /* start Control list */ 1024a5496a18SBecky Bruce ohci_writel(OHCI_CLF, &ohci->regs->cmdstatus); 10252731b9a8SJean-Christophe PLAGNIOL-VILLARD } 10262731b9a8SJean-Christophe PLAGNIOL-VILLARD break; 10272731b9a8SJean-Christophe PLAGNIOL-VILLARD 10282731b9a8SJean-Christophe PLAGNIOL-VILLARD case PIPE_INTERRUPT: 10292731b9a8SJean-Christophe PLAGNIOL-VILLARD info = usb_pipeout(urb->pipe)? 10302731b9a8SJean-Christophe PLAGNIOL-VILLARD TD_CC | TD_DP_OUT | toggle: 10312731b9a8SJean-Christophe PLAGNIOL-VILLARD TD_CC | TD_R | TD_DP_IN | toggle; 10322731b9a8SJean-Christophe PLAGNIOL-VILLARD td_fill(ohci, info, data, data_len, dev, cnt++, urb); 10332731b9a8SJean-Christophe PLAGNIOL-VILLARD break; 10342731b9a8SJean-Christophe PLAGNIOL-VILLARD } 10352731b9a8SJean-Christophe PLAGNIOL-VILLARD if (urb->length != cnt) 10362731b9a8SJean-Christophe PLAGNIOL-VILLARD dbg("TD LENGTH %d != CNT %d", urb->length, cnt); 10372731b9a8SJean-Christophe PLAGNIOL-VILLARD } 10382731b9a8SJean-Christophe PLAGNIOL-VILLARD 10392731b9a8SJean-Christophe PLAGNIOL-VILLARD /*-------------------------------------------------------------------------* 10402731b9a8SJean-Christophe PLAGNIOL-VILLARD * Done List handling functions 10412731b9a8SJean-Christophe PLAGNIOL-VILLARD *-------------------------------------------------------------------------*/ 10422731b9a8SJean-Christophe PLAGNIOL-VILLARD 10432731b9a8SJean-Christophe PLAGNIOL-VILLARD /* calculate the transfer length and update the urb */ 10442731b9a8SJean-Christophe PLAGNIOL-VILLARD 10452731b9a8SJean-Christophe PLAGNIOL-VILLARD static void dl_transfer_length(td_t *td) 10462731b9a8SJean-Christophe PLAGNIOL-VILLARD { 10476bc52ef3SWolfgang Denk __u32 tdBE, tdCBP; 10482731b9a8SJean-Christophe PLAGNIOL-VILLARD urb_priv_t *lurb_priv = td->ed->purb; 10492731b9a8SJean-Christophe PLAGNIOL-VILLARD 10502731b9a8SJean-Christophe PLAGNIOL-VILLARD tdBE = m32_swap(td->hwBE); 10512731b9a8SJean-Christophe PLAGNIOL-VILLARD tdCBP = m32_swap(td->hwCBP); 10522731b9a8SJean-Christophe PLAGNIOL-VILLARD 10532731b9a8SJean-Christophe PLAGNIOL-VILLARD if (!(usb_pipecontrol(lurb_priv->pipe) && 10542731b9a8SJean-Christophe PLAGNIOL-VILLARD ((td->index == 0) || (td->index == lurb_priv->length - 1)))) { 10552731b9a8SJean-Christophe PLAGNIOL-VILLARD if (tdBE != 0) { 10562731b9a8SJean-Christophe PLAGNIOL-VILLARD if (td->hwCBP == 0) 10572731b9a8SJean-Christophe PLAGNIOL-VILLARD lurb_priv->actual_length += tdBE - td->data + 1; 10582731b9a8SJean-Christophe PLAGNIOL-VILLARD else 10592731b9a8SJean-Christophe PLAGNIOL-VILLARD lurb_priv->actual_length += tdCBP - td->data; 10602731b9a8SJean-Christophe PLAGNIOL-VILLARD } 10612731b9a8SJean-Christophe PLAGNIOL-VILLARD } 10622731b9a8SJean-Christophe PLAGNIOL-VILLARD } 10632731b9a8SJean-Christophe PLAGNIOL-VILLARD 10642731b9a8SJean-Christophe PLAGNIOL-VILLARD /*-------------------------------------------------------------------------*/ 10652731b9a8SJean-Christophe PLAGNIOL-VILLARD static void check_status(td_t *td_list) 10662731b9a8SJean-Christophe PLAGNIOL-VILLARD { 10672731b9a8SJean-Christophe PLAGNIOL-VILLARD urb_priv_t *lurb_priv = td_list->ed->purb; 10682731b9a8SJean-Christophe PLAGNIOL-VILLARD int urb_len = lurb_priv->length; 10692731b9a8SJean-Christophe PLAGNIOL-VILLARD __u32 *phwHeadP = &td_list->ed->hwHeadP; 10702731b9a8SJean-Christophe PLAGNIOL-VILLARD int cc; 10712731b9a8SJean-Christophe PLAGNIOL-VILLARD 10722731b9a8SJean-Christophe PLAGNIOL-VILLARD cc = TD_CC_GET(m32_swap(td_list->hwINFO)); 10732731b9a8SJean-Christophe PLAGNIOL-VILLARD if (cc) { 10742731b9a8SJean-Christophe PLAGNIOL-VILLARD err(" USB-error: %s (%x)", cc_to_string[cc], cc); 10752731b9a8SJean-Christophe PLAGNIOL-VILLARD 10768d005ef8SHans de Goede invalidate_dcache_ed(td_list->ed); 10772731b9a8SJean-Christophe PLAGNIOL-VILLARD if (*phwHeadP & m32_swap(0x1)) { 10782731b9a8SJean-Christophe PLAGNIOL-VILLARD if (lurb_priv && 10792731b9a8SJean-Christophe PLAGNIOL-VILLARD ((td_list->index + 1) < urb_len)) { 10802731b9a8SJean-Christophe PLAGNIOL-VILLARD *phwHeadP = 10812731b9a8SJean-Christophe PLAGNIOL-VILLARD (lurb_priv->td[urb_len - 1]->hwNextTD &\ 10822731b9a8SJean-Christophe PLAGNIOL-VILLARD m32_swap(0xfffffff0)) | 10832731b9a8SJean-Christophe PLAGNIOL-VILLARD (*phwHeadP & m32_swap(0x2)); 10842731b9a8SJean-Christophe PLAGNIOL-VILLARD 10852731b9a8SJean-Christophe PLAGNIOL-VILLARD lurb_priv->td_cnt += urb_len - 10862731b9a8SJean-Christophe PLAGNIOL-VILLARD td_list->index - 1; 10872731b9a8SJean-Christophe PLAGNIOL-VILLARD } else 10882731b9a8SJean-Christophe PLAGNIOL-VILLARD *phwHeadP &= m32_swap(0xfffffff2); 10898d005ef8SHans de Goede flush_dcache_ed(td_list->ed); 10902731b9a8SJean-Christophe PLAGNIOL-VILLARD } 10912731b9a8SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_MPC5200 10922731b9a8SJean-Christophe PLAGNIOL-VILLARD td_list->hwNextTD = 0; 10938d005ef8SHans de Goede flush_dcache_td(td_list); 10942731b9a8SJean-Christophe PLAGNIOL-VILLARD #endif 10952731b9a8SJean-Christophe PLAGNIOL-VILLARD } 10962731b9a8SJean-Christophe PLAGNIOL-VILLARD } 10972731b9a8SJean-Christophe PLAGNIOL-VILLARD 10982731b9a8SJean-Christophe PLAGNIOL-VILLARD /* replies to the request have to be on a FIFO basis so 10992731b9a8SJean-Christophe PLAGNIOL-VILLARD * we reverse the reversed done-list */ 11002731b9a8SJean-Christophe PLAGNIOL-VILLARD static td_t *dl_reverse_done_list(ohci_t *ohci) 11012731b9a8SJean-Christophe PLAGNIOL-VILLARD { 11022731b9a8SJean-Christophe PLAGNIOL-VILLARD __u32 td_list_hc; 11032731b9a8SJean-Christophe PLAGNIOL-VILLARD td_t *td_rev = NULL; 11042731b9a8SJean-Christophe PLAGNIOL-VILLARD td_t *td_list = NULL; 11052731b9a8SJean-Christophe PLAGNIOL-VILLARD 11068d005ef8SHans de Goede invalidate_dcache_hcca(ohci->hcca); 11072731b9a8SJean-Christophe PLAGNIOL-VILLARD td_list_hc = m32_swap(ohci->hcca->done_head) & 0xfffffff0; 11082731b9a8SJean-Christophe PLAGNIOL-VILLARD ohci->hcca->done_head = 0; 11098d005ef8SHans de Goede flush_dcache_hcca(ohci->hcca); 11102731b9a8SJean-Christophe PLAGNIOL-VILLARD 11112731b9a8SJean-Christophe PLAGNIOL-VILLARD while (td_list_hc) { 11122731b9a8SJean-Christophe PLAGNIOL-VILLARD td_list = (td_t *)td_list_hc; 11138d005ef8SHans de Goede invalidate_dcache_td(td_list); 11142731b9a8SJean-Christophe PLAGNIOL-VILLARD check_status(td_list); 11152731b9a8SJean-Christophe PLAGNIOL-VILLARD td_list->next_dl_td = td_rev; 11162731b9a8SJean-Christophe PLAGNIOL-VILLARD td_rev = td_list; 11172731b9a8SJean-Christophe PLAGNIOL-VILLARD td_list_hc = m32_swap(td_list->hwNextTD) & 0xfffffff0; 11182731b9a8SJean-Christophe PLAGNIOL-VILLARD } 11192731b9a8SJean-Christophe PLAGNIOL-VILLARD return td_list; 11202731b9a8SJean-Christophe PLAGNIOL-VILLARD } 11212731b9a8SJean-Christophe PLAGNIOL-VILLARD 11222731b9a8SJean-Christophe PLAGNIOL-VILLARD /*-------------------------------------------------------------------------*/ 11232731b9a8SJean-Christophe PLAGNIOL-VILLARD /*-------------------------------------------------------------------------*/ 11242731b9a8SJean-Christophe PLAGNIOL-VILLARD 11252731b9a8SJean-Christophe PLAGNIOL-VILLARD static void finish_urb(ohci_t *ohci, urb_priv_t *urb, int status) 11262731b9a8SJean-Christophe PLAGNIOL-VILLARD { 11272731b9a8SJean-Christophe PLAGNIOL-VILLARD if ((status & (ED_OPER | ED_UNLINK)) && (urb->state != URB_DEL)) 112847976d2cSHans de Goede urb->finished = 1; 11292731b9a8SJean-Christophe PLAGNIOL-VILLARD else 11302731b9a8SJean-Christophe PLAGNIOL-VILLARD dbg("finish_urb: strange.., ED state %x, \n", status); 11312731b9a8SJean-Christophe PLAGNIOL-VILLARD } 11322731b9a8SJean-Christophe PLAGNIOL-VILLARD 11332731b9a8SJean-Christophe PLAGNIOL-VILLARD /* 11342731b9a8SJean-Christophe PLAGNIOL-VILLARD * Used to take back a TD from the host controller. This would normally be 11352731b9a8SJean-Christophe PLAGNIOL-VILLARD * called from within dl_done_list, however it may be called directly if the 11362731b9a8SJean-Christophe PLAGNIOL-VILLARD * HC no longer sees the TD and it has not appeared on the donelist (after 11372731b9a8SJean-Christophe PLAGNIOL-VILLARD * two frames). This bug has been observed on ZF Micro systems. 11382731b9a8SJean-Christophe PLAGNIOL-VILLARD */ 11392731b9a8SJean-Christophe PLAGNIOL-VILLARD static int takeback_td(ohci_t *ohci, td_t *td_list) 11402731b9a8SJean-Christophe PLAGNIOL-VILLARD { 11412731b9a8SJean-Christophe PLAGNIOL-VILLARD ed_t *ed; 11422731b9a8SJean-Christophe PLAGNIOL-VILLARD int cc; 11432731b9a8SJean-Christophe PLAGNIOL-VILLARD int stat = 0; 11442731b9a8SJean-Christophe PLAGNIOL-VILLARD /* urb_t *urb; */ 11452731b9a8SJean-Christophe PLAGNIOL-VILLARD urb_priv_t *lurb_priv; 11462731b9a8SJean-Christophe PLAGNIOL-VILLARD __u32 tdINFO, edHeadP, edTailP; 11472731b9a8SJean-Christophe PLAGNIOL-VILLARD 11488d005ef8SHans de Goede invalidate_dcache_td(td_list); 11492731b9a8SJean-Christophe PLAGNIOL-VILLARD tdINFO = m32_swap(td_list->hwINFO); 11502731b9a8SJean-Christophe PLAGNIOL-VILLARD 11512731b9a8SJean-Christophe PLAGNIOL-VILLARD ed = td_list->ed; 11522731b9a8SJean-Christophe PLAGNIOL-VILLARD lurb_priv = ed->purb; 11532731b9a8SJean-Christophe PLAGNIOL-VILLARD 11542731b9a8SJean-Christophe PLAGNIOL-VILLARD dl_transfer_length(td_list); 11552731b9a8SJean-Christophe PLAGNIOL-VILLARD 11562731b9a8SJean-Christophe PLAGNIOL-VILLARD lurb_priv->td_cnt++; 11572731b9a8SJean-Christophe PLAGNIOL-VILLARD 11582731b9a8SJean-Christophe PLAGNIOL-VILLARD /* error code of transfer */ 11592731b9a8SJean-Christophe PLAGNIOL-VILLARD cc = TD_CC_GET(tdINFO); 11602731b9a8SJean-Christophe PLAGNIOL-VILLARD if (cc) { 11612731b9a8SJean-Christophe PLAGNIOL-VILLARD err("USB-error: %s (%x)", cc_to_string[cc], cc); 11622731b9a8SJean-Christophe PLAGNIOL-VILLARD stat = cc_to_error[cc]; 11632731b9a8SJean-Christophe PLAGNIOL-VILLARD } 11642731b9a8SJean-Christophe PLAGNIOL-VILLARD 11652731b9a8SJean-Christophe PLAGNIOL-VILLARD /* see if this done list makes for all TD's of current URB, 11662731b9a8SJean-Christophe PLAGNIOL-VILLARD * and mark the URB finished if so */ 11672731b9a8SJean-Christophe PLAGNIOL-VILLARD if (lurb_priv->td_cnt == lurb_priv->length) 11682731b9a8SJean-Christophe PLAGNIOL-VILLARD finish_urb(ohci, lurb_priv, ed->state); 11692731b9a8SJean-Christophe PLAGNIOL-VILLARD 11702731b9a8SJean-Christophe PLAGNIOL-VILLARD dbg("dl_done_list: processing TD %x, len %x\n", 11712731b9a8SJean-Christophe PLAGNIOL-VILLARD lurb_priv->td_cnt, lurb_priv->length); 11722731b9a8SJean-Christophe PLAGNIOL-VILLARD 11732731b9a8SJean-Christophe PLAGNIOL-VILLARD if (ed->state != ED_NEW && (!usb_pipeint(lurb_priv->pipe))) { 11748d005ef8SHans de Goede invalidate_dcache_ed(ed); 11752731b9a8SJean-Christophe PLAGNIOL-VILLARD edHeadP = m32_swap(ed->hwHeadP) & 0xfffffff0; 11762731b9a8SJean-Christophe PLAGNIOL-VILLARD edTailP = m32_swap(ed->hwTailP); 11772731b9a8SJean-Christophe PLAGNIOL-VILLARD 11782731b9a8SJean-Christophe PLAGNIOL-VILLARD /* unlink eds if they are not busy */ 11792731b9a8SJean-Christophe PLAGNIOL-VILLARD if ((edHeadP == edTailP) && (ed->state == ED_OPER)) 11802731b9a8SJean-Christophe PLAGNIOL-VILLARD ep_unlink(ohci, ed); 11812731b9a8SJean-Christophe PLAGNIOL-VILLARD } 11822731b9a8SJean-Christophe PLAGNIOL-VILLARD return stat; 11832731b9a8SJean-Christophe PLAGNIOL-VILLARD } 11842731b9a8SJean-Christophe PLAGNIOL-VILLARD 11852731b9a8SJean-Christophe PLAGNIOL-VILLARD static int dl_done_list(ohci_t *ohci) 11862731b9a8SJean-Christophe PLAGNIOL-VILLARD { 11872731b9a8SJean-Christophe PLAGNIOL-VILLARD int stat = 0; 11882731b9a8SJean-Christophe PLAGNIOL-VILLARD td_t *td_list = dl_reverse_done_list(ohci); 11892731b9a8SJean-Christophe PLAGNIOL-VILLARD 11902731b9a8SJean-Christophe PLAGNIOL-VILLARD while (td_list) { 11912731b9a8SJean-Christophe PLAGNIOL-VILLARD td_t *td_next = td_list->next_dl_td; 11922731b9a8SJean-Christophe PLAGNIOL-VILLARD stat = takeback_td(ohci, td_list); 11932731b9a8SJean-Christophe PLAGNIOL-VILLARD td_list = td_next; 11942731b9a8SJean-Christophe PLAGNIOL-VILLARD } 11952731b9a8SJean-Christophe PLAGNIOL-VILLARD return stat; 11962731b9a8SJean-Christophe PLAGNIOL-VILLARD } 11972731b9a8SJean-Christophe PLAGNIOL-VILLARD 11982731b9a8SJean-Christophe PLAGNIOL-VILLARD /*-------------------------------------------------------------------------* 11992731b9a8SJean-Christophe PLAGNIOL-VILLARD * Virtual Root Hub 12002731b9a8SJean-Christophe PLAGNIOL-VILLARD *-------------------------------------------------------------------------*/ 12012731b9a8SJean-Christophe PLAGNIOL-VILLARD 1202eb838e7dSStephen Warren #include <usbroothubdes.h> 12032731b9a8SJean-Christophe PLAGNIOL-VILLARD 12042731b9a8SJean-Christophe PLAGNIOL-VILLARD /* Hub class-specific descriptor is constructed dynamically */ 12052731b9a8SJean-Christophe PLAGNIOL-VILLARD 12062731b9a8SJean-Christophe PLAGNIOL-VILLARD /*-------------------------------------------------------------------------*/ 12072731b9a8SJean-Christophe PLAGNIOL-VILLARD 12082731b9a8SJean-Christophe PLAGNIOL-VILLARD #define OK(x) len = (x); break 12092731b9a8SJean-Christophe PLAGNIOL-VILLARD #ifdef DEBUG 1210a5496a18SBecky Bruce #define WR_RH_STAT(x) {info("WR:status %#8x", (x)); ohci_writel((x), \ 1211c5613df5SHans de Goede &ohci->regs->roothub.status); } 12122731b9a8SJean-Christophe PLAGNIOL-VILLARD #define WR_RH_PORTSTAT(x) {info("WR:portstatus[%d] %#8x", wIndex-1, \ 1213c5613df5SHans de Goede (x)); ohci_writel((x), &ohci->regs->roothub.portstatus[wIndex-1]); } 12142731b9a8SJean-Christophe PLAGNIOL-VILLARD #else 1215c5613df5SHans de Goede #define WR_RH_STAT(x) ohci_writel((x), &ohci->regs->roothub.status) 1216a5496a18SBecky Bruce #define WR_RH_PORTSTAT(x) ohci_writel((x), \ 1217c5613df5SHans de Goede &ohci->regs->roothub.portstatus[wIndex-1]) 12182731b9a8SJean-Christophe PLAGNIOL-VILLARD #endif 1219c5613df5SHans de Goede #define RD_RH_STAT roothub_status(ohci) 1220c5613df5SHans de Goede #define RD_RH_PORTSTAT roothub_portstatus(ohci, wIndex-1) 12212731b9a8SJean-Christophe PLAGNIOL-VILLARD 12222731b9a8SJean-Christophe PLAGNIOL-VILLARD /* request to virtual root hub */ 12232731b9a8SJean-Christophe PLAGNIOL-VILLARD 12242731b9a8SJean-Christophe PLAGNIOL-VILLARD int rh_check_port_status(ohci_t *controller) 12252731b9a8SJean-Christophe PLAGNIOL-VILLARD { 12262731b9a8SJean-Christophe PLAGNIOL-VILLARD __u32 temp, ndp, i; 12272731b9a8SJean-Christophe PLAGNIOL-VILLARD int res; 12282731b9a8SJean-Christophe PLAGNIOL-VILLARD 12292731b9a8SJean-Christophe PLAGNIOL-VILLARD res = -1; 12302731b9a8SJean-Christophe PLAGNIOL-VILLARD temp = roothub_a(controller); 12312731b9a8SJean-Christophe PLAGNIOL-VILLARD ndp = (temp & RH_A_NDP); 12322731b9a8SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_AT91C_PQFP_UHPBUG 12332731b9a8SJean-Christophe PLAGNIOL-VILLARD ndp = (ndp == 2) ? 1:0; 12342731b9a8SJean-Christophe PLAGNIOL-VILLARD #endif 12352731b9a8SJean-Christophe PLAGNIOL-VILLARD for (i = 0; i < ndp; i++) { 12362731b9a8SJean-Christophe PLAGNIOL-VILLARD temp = roothub_portstatus(controller, i); 12372731b9a8SJean-Christophe PLAGNIOL-VILLARD /* check for a device disconnect */ 12382731b9a8SJean-Christophe PLAGNIOL-VILLARD if (((temp & (RH_PS_PESC | RH_PS_CSC)) == 12392731b9a8SJean-Christophe PLAGNIOL-VILLARD (RH_PS_PESC | RH_PS_CSC)) && 12402731b9a8SJean-Christophe PLAGNIOL-VILLARD ((temp & RH_PS_CCS) == 0)) { 12412731b9a8SJean-Christophe PLAGNIOL-VILLARD res = i; 12422731b9a8SJean-Christophe PLAGNIOL-VILLARD break; 12432731b9a8SJean-Christophe PLAGNIOL-VILLARD } 12442731b9a8SJean-Christophe PLAGNIOL-VILLARD } 12452731b9a8SJean-Christophe PLAGNIOL-VILLARD return res; 12462731b9a8SJean-Christophe PLAGNIOL-VILLARD } 12472731b9a8SJean-Christophe PLAGNIOL-VILLARD 1248c5613df5SHans de Goede static int ohci_submit_rh_msg(ohci_t *ohci, struct usb_device *dev, 1249c5613df5SHans de Goede unsigned long pipe, void *buffer, int transfer_len, 1250c5613df5SHans de Goede struct devrequest *cmd) 12512731b9a8SJean-Christophe PLAGNIOL-VILLARD { 12522731b9a8SJean-Christophe PLAGNIOL-VILLARD void *data = buffer; 12532731b9a8SJean-Christophe PLAGNIOL-VILLARD int leni = transfer_len; 12542731b9a8SJean-Christophe PLAGNIOL-VILLARD int len = 0; 12552731b9a8SJean-Christophe PLAGNIOL-VILLARD int stat = 0; 12562731b9a8SJean-Christophe PLAGNIOL-VILLARD __u16 bmRType_bReq; 12572731b9a8SJean-Christophe PLAGNIOL-VILLARD __u16 wValue; 12582731b9a8SJean-Christophe PLAGNIOL-VILLARD __u16 wIndex; 12592731b9a8SJean-Christophe PLAGNIOL-VILLARD __u16 wLength; 1260f1273f11STroy Kisky ALLOC_ALIGN_BUFFER(__u8, databuf, 16, sizeof(u32)); 12615f6aa03fSMarek Vasut 12622731b9a8SJean-Christophe PLAGNIOL-VILLARD #ifdef DEBUG 1263c5613df5SHans de Goede pkt_print(ohci, NULL, dev, pipe, buffer, transfer_len, 12642731b9a8SJean-Christophe PLAGNIOL-VILLARD cmd, "SUB(rh)", usb_pipein(pipe)); 12652731b9a8SJean-Christophe PLAGNIOL-VILLARD #else 12668f761f02SHans de Goede ohci_mdelay(1); 12672731b9a8SJean-Christophe PLAGNIOL-VILLARD #endif 12682731b9a8SJean-Christophe PLAGNIOL-VILLARD if (usb_pipeint(pipe)) { 12692731b9a8SJean-Christophe PLAGNIOL-VILLARD info("Root-Hub submit IRQ: NOT implemented"); 12702731b9a8SJean-Christophe PLAGNIOL-VILLARD return 0; 12712731b9a8SJean-Christophe PLAGNIOL-VILLARD } 12722731b9a8SJean-Christophe PLAGNIOL-VILLARD 12732731b9a8SJean-Christophe PLAGNIOL-VILLARD bmRType_bReq = cmd->requesttype | (cmd->request << 8); 12742731b9a8SJean-Christophe PLAGNIOL-VILLARD wValue = le16_to_cpu(cmd->value); 12752731b9a8SJean-Christophe PLAGNIOL-VILLARD wIndex = le16_to_cpu(cmd->index); 12762731b9a8SJean-Christophe PLAGNIOL-VILLARD wLength = le16_to_cpu(cmd->length); 12772731b9a8SJean-Christophe PLAGNIOL-VILLARD 12782731b9a8SJean-Christophe PLAGNIOL-VILLARD info("Root-Hub: adr: %2x cmd(%1x): %08x %04x %04x %04x", 12792731b9a8SJean-Christophe PLAGNIOL-VILLARD dev->devnum, 8, bmRType_bReq, wValue, wIndex, wLength); 12802731b9a8SJean-Christophe PLAGNIOL-VILLARD 12812731b9a8SJean-Christophe PLAGNIOL-VILLARD switch (bmRType_bReq) { 12822731b9a8SJean-Christophe PLAGNIOL-VILLARD /* Request Destination: 12832731b9a8SJean-Christophe PLAGNIOL-VILLARD without flags: Device, 12842731b9a8SJean-Christophe PLAGNIOL-VILLARD RH_INTERFACE: interface, 12852731b9a8SJean-Christophe PLAGNIOL-VILLARD RH_ENDPOINT: endpoint, 12862731b9a8SJean-Christophe PLAGNIOL-VILLARD RH_CLASS means HUB here, 12872731b9a8SJean-Christophe PLAGNIOL-VILLARD RH_OTHER | RH_CLASS almost ever means HUB_PORT here 12882731b9a8SJean-Christophe PLAGNIOL-VILLARD */ 12892731b9a8SJean-Christophe PLAGNIOL-VILLARD 12902731b9a8SJean-Christophe PLAGNIOL-VILLARD case RH_GET_STATUS: 1291f1273f11STroy Kisky *(u16 *)databuf = cpu_to_le16(1); 12922731b9a8SJean-Christophe PLAGNIOL-VILLARD OK(2); 12932731b9a8SJean-Christophe PLAGNIOL-VILLARD case RH_GET_STATUS | RH_INTERFACE: 1294f1273f11STroy Kisky *(u16 *)databuf = cpu_to_le16(0); 12952731b9a8SJean-Christophe PLAGNIOL-VILLARD OK(2); 12962731b9a8SJean-Christophe PLAGNIOL-VILLARD case RH_GET_STATUS | RH_ENDPOINT: 1297f1273f11STroy Kisky *(u16 *)databuf = cpu_to_le16(0); 12982731b9a8SJean-Christophe PLAGNIOL-VILLARD OK(2); 12992731b9a8SJean-Christophe PLAGNIOL-VILLARD case RH_GET_STATUS | RH_CLASS: 1300f1273f11STroy Kisky *(u32 *)databuf = cpu_to_le32( 13012731b9a8SJean-Christophe PLAGNIOL-VILLARD RD_RH_STAT & ~(RH_HS_CRWE | RH_HS_DRWE)); 13022731b9a8SJean-Christophe PLAGNIOL-VILLARD OK(4); 13032731b9a8SJean-Christophe PLAGNIOL-VILLARD case RH_GET_STATUS | RH_OTHER | RH_CLASS: 1304f1273f11STroy Kisky *(u32 *)databuf = cpu_to_le32(RD_RH_PORTSTAT); 13052731b9a8SJean-Christophe PLAGNIOL-VILLARD OK(4); 13062731b9a8SJean-Christophe PLAGNIOL-VILLARD 13072731b9a8SJean-Christophe PLAGNIOL-VILLARD case RH_CLEAR_FEATURE | RH_ENDPOINT: 13082731b9a8SJean-Christophe PLAGNIOL-VILLARD switch (wValue) { 13092731b9a8SJean-Christophe PLAGNIOL-VILLARD case (RH_ENDPOINT_STALL): 13102731b9a8SJean-Christophe PLAGNIOL-VILLARD OK(0); 13112731b9a8SJean-Christophe PLAGNIOL-VILLARD } 13122731b9a8SJean-Christophe PLAGNIOL-VILLARD break; 13132731b9a8SJean-Christophe PLAGNIOL-VILLARD 13142731b9a8SJean-Christophe PLAGNIOL-VILLARD case RH_CLEAR_FEATURE | RH_CLASS: 13152731b9a8SJean-Christophe PLAGNIOL-VILLARD switch (wValue) { 13162731b9a8SJean-Christophe PLAGNIOL-VILLARD case RH_C_HUB_LOCAL_POWER: 13172731b9a8SJean-Christophe PLAGNIOL-VILLARD OK(0); 13182731b9a8SJean-Christophe PLAGNIOL-VILLARD case (RH_C_HUB_OVER_CURRENT): 13192731b9a8SJean-Christophe PLAGNIOL-VILLARD WR_RH_STAT(RH_HS_OCIC); 13202731b9a8SJean-Christophe PLAGNIOL-VILLARD OK(0); 13212731b9a8SJean-Christophe PLAGNIOL-VILLARD } 13222731b9a8SJean-Christophe PLAGNIOL-VILLARD break; 13232731b9a8SJean-Christophe PLAGNIOL-VILLARD 13242731b9a8SJean-Christophe PLAGNIOL-VILLARD case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS: 13252731b9a8SJean-Christophe PLAGNIOL-VILLARD switch (wValue) { 13262731b9a8SJean-Christophe PLAGNIOL-VILLARD case (RH_PORT_ENABLE): WR_RH_PORTSTAT(RH_PS_CCS); OK(0); 13272731b9a8SJean-Christophe PLAGNIOL-VILLARD case (RH_PORT_SUSPEND): WR_RH_PORTSTAT(RH_PS_POCI); OK(0); 13282731b9a8SJean-Christophe PLAGNIOL-VILLARD case (RH_PORT_POWER): WR_RH_PORTSTAT(RH_PS_LSDA); OK(0); 13292731b9a8SJean-Christophe PLAGNIOL-VILLARD case (RH_C_PORT_CONNECTION): WR_RH_PORTSTAT(RH_PS_CSC); OK(0); 13302731b9a8SJean-Christophe PLAGNIOL-VILLARD case (RH_C_PORT_ENABLE): WR_RH_PORTSTAT(RH_PS_PESC); OK(0); 13312731b9a8SJean-Christophe PLAGNIOL-VILLARD case (RH_C_PORT_SUSPEND): WR_RH_PORTSTAT(RH_PS_PSSC); OK(0); 13322731b9a8SJean-Christophe PLAGNIOL-VILLARD case (RH_C_PORT_OVER_CURRENT):WR_RH_PORTSTAT(RH_PS_OCIC); OK(0); 13332731b9a8SJean-Christophe PLAGNIOL-VILLARD case (RH_C_PORT_RESET): WR_RH_PORTSTAT(RH_PS_PRSC); OK(0); 13342731b9a8SJean-Christophe PLAGNIOL-VILLARD } 13352731b9a8SJean-Christophe PLAGNIOL-VILLARD break; 13362731b9a8SJean-Christophe PLAGNIOL-VILLARD 13372731b9a8SJean-Christophe PLAGNIOL-VILLARD case RH_SET_FEATURE | RH_OTHER | RH_CLASS: 13382731b9a8SJean-Christophe PLAGNIOL-VILLARD switch (wValue) { 13392731b9a8SJean-Christophe PLAGNIOL-VILLARD case (RH_PORT_SUSPEND): 13402731b9a8SJean-Christophe PLAGNIOL-VILLARD WR_RH_PORTSTAT(RH_PS_PSS); OK(0); 13412731b9a8SJean-Christophe PLAGNIOL-VILLARD case (RH_PORT_RESET): /* BUG IN HUP CODE *********/ 13422731b9a8SJean-Christophe PLAGNIOL-VILLARD if (RD_RH_PORTSTAT & RH_PS_CCS) 13432731b9a8SJean-Christophe PLAGNIOL-VILLARD WR_RH_PORTSTAT(RH_PS_PRS); 13442731b9a8SJean-Christophe PLAGNIOL-VILLARD OK(0); 13452731b9a8SJean-Christophe PLAGNIOL-VILLARD case (RH_PORT_POWER): 13462731b9a8SJean-Christophe PLAGNIOL-VILLARD WR_RH_PORTSTAT(RH_PS_PPS); 13472731b9a8SJean-Christophe PLAGNIOL-VILLARD OK(0); 13482731b9a8SJean-Christophe PLAGNIOL-VILLARD case (RH_PORT_ENABLE): /* BUG IN HUP CODE *********/ 13492731b9a8SJean-Christophe PLAGNIOL-VILLARD if (RD_RH_PORTSTAT & RH_PS_CCS) 13502731b9a8SJean-Christophe PLAGNIOL-VILLARD WR_RH_PORTSTAT(RH_PS_PES); 13512731b9a8SJean-Christophe PLAGNIOL-VILLARD OK(0); 13522731b9a8SJean-Christophe PLAGNIOL-VILLARD } 13532731b9a8SJean-Christophe PLAGNIOL-VILLARD break; 13542731b9a8SJean-Christophe PLAGNIOL-VILLARD 13552731b9a8SJean-Christophe PLAGNIOL-VILLARD case RH_SET_ADDRESS: 1356c5613df5SHans de Goede ohci->rh.devnum = wValue; 13572731b9a8SJean-Christophe PLAGNIOL-VILLARD OK(0); 13582731b9a8SJean-Christophe PLAGNIOL-VILLARD 13592731b9a8SJean-Christophe PLAGNIOL-VILLARD case RH_GET_DESCRIPTOR: 13602731b9a8SJean-Christophe PLAGNIOL-VILLARD switch ((wValue & 0xff00) >> 8) { 13612731b9a8SJean-Christophe PLAGNIOL-VILLARD case (0x01): /* device descriptor */ 13622731b9a8SJean-Christophe PLAGNIOL-VILLARD len = min_t(unsigned int, 13632731b9a8SJean-Christophe PLAGNIOL-VILLARD leni, 13642731b9a8SJean-Christophe PLAGNIOL-VILLARD min_t(unsigned int, 13652731b9a8SJean-Christophe PLAGNIOL-VILLARD sizeof(root_hub_dev_des), 13662731b9a8SJean-Christophe PLAGNIOL-VILLARD wLength)); 1367f1273f11STroy Kisky databuf = root_hub_dev_des; OK(len); 13682731b9a8SJean-Christophe PLAGNIOL-VILLARD case (0x02): /* configuration descriptor */ 13692731b9a8SJean-Christophe PLAGNIOL-VILLARD len = min_t(unsigned int, 13702731b9a8SJean-Christophe PLAGNIOL-VILLARD leni, 13712731b9a8SJean-Christophe PLAGNIOL-VILLARD min_t(unsigned int, 13722731b9a8SJean-Christophe PLAGNIOL-VILLARD sizeof(root_hub_config_des), 13732731b9a8SJean-Christophe PLAGNIOL-VILLARD wLength)); 1374f1273f11STroy Kisky databuf = root_hub_config_des; OK(len); 13752731b9a8SJean-Christophe PLAGNIOL-VILLARD case (0x03): /* string descriptors */ 13762731b9a8SJean-Christophe PLAGNIOL-VILLARD if (wValue == 0x0300) { 13772731b9a8SJean-Christophe PLAGNIOL-VILLARD len = min_t(unsigned int, 13782731b9a8SJean-Christophe PLAGNIOL-VILLARD leni, 13792731b9a8SJean-Christophe PLAGNIOL-VILLARD min_t(unsigned int, 13802731b9a8SJean-Christophe PLAGNIOL-VILLARD sizeof(root_hub_str_index0), 13812731b9a8SJean-Christophe PLAGNIOL-VILLARD wLength)); 1382f1273f11STroy Kisky databuf = root_hub_str_index0; 13832731b9a8SJean-Christophe PLAGNIOL-VILLARD OK(len); 13842731b9a8SJean-Christophe PLAGNIOL-VILLARD } 13852731b9a8SJean-Christophe PLAGNIOL-VILLARD if (wValue == 0x0301) { 13862731b9a8SJean-Christophe PLAGNIOL-VILLARD len = min_t(unsigned int, 13872731b9a8SJean-Christophe PLAGNIOL-VILLARD leni, 13882731b9a8SJean-Christophe PLAGNIOL-VILLARD min_t(unsigned int, 13892731b9a8SJean-Christophe PLAGNIOL-VILLARD sizeof(root_hub_str_index1), 13902731b9a8SJean-Christophe PLAGNIOL-VILLARD wLength)); 1391f1273f11STroy Kisky databuf = root_hub_str_index1; 13922731b9a8SJean-Christophe PLAGNIOL-VILLARD OK(len); 13932731b9a8SJean-Christophe PLAGNIOL-VILLARD } 13942731b9a8SJean-Christophe PLAGNIOL-VILLARD default: 13952731b9a8SJean-Christophe PLAGNIOL-VILLARD stat = USB_ST_STALLED; 13962731b9a8SJean-Christophe PLAGNIOL-VILLARD } 13972731b9a8SJean-Christophe PLAGNIOL-VILLARD break; 13982731b9a8SJean-Christophe PLAGNIOL-VILLARD 13992731b9a8SJean-Christophe PLAGNIOL-VILLARD case RH_GET_DESCRIPTOR | RH_CLASS: 14002731b9a8SJean-Christophe PLAGNIOL-VILLARD { 1401c5613df5SHans de Goede __u32 temp = roothub_a(ohci); 14022731b9a8SJean-Christophe PLAGNIOL-VILLARD 1403f1273f11STroy Kisky databuf[0] = 9; /* min length; */ 1404f1273f11STroy Kisky databuf[1] = 0x29; 1405f1273f11STroy Kisky databuf[2] = temp & RH_A_NDP; 14062731b9a8SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_AT91C_PQFP_UHPBUG 1407f1273f11STroy Kisky databuf[2] = (databuf[2] == 2) ? 1 : 0; 14082731b9a8SJean-Christophe PLAGNIOL-VILLARD #endif 1409f1273f11STroy Kisky databuf[3] = 0; 14102731b9a8SJean-Christophe PLAGNIOL-VILLARD if (temp & RH_A_PSM) /* per-port power switching? */ 1411f1273f11STroy Kisky databuf[3] |= 0x1; 14122731b9a8SJean-Christophe PLAGNIOL-VILLARD if (temp & RH_A_NOCP) /* no overcurrent reporting? */ 1413f1273f11STroy Kisky databuf[3] |= 0x10; 14142731b9a8SJean-Christophe PLAGNIOL-VILLARD else if (temp & RH_A_OCPM)/* per-port overcurrent reporting? */ 1415f1273f11STroy Kisky databuf[3] |= 0x8; 14162731b9a8SJean-Christophe PLAGNIOL-VILLARD 1417f1273f11STroy Kisky databuf[4] = 0; 1418f1273f11STroy Kisky databuf[5] = (temp & RH_A_POTPGT) >> 24; 1419f1273f11STroy Kisky databuf[6] = 0; 1420c5613df5SHans de Goede temp = roothub_b(ohci); 1421f1273f11STroy Kisky databuf[7] = temp & RH_B_DR; 1422f1273f11STroy Kisky if (databuf[2] < 7) { 1423f1273f11STroy Kisky databuf[8] = 0xff; 14242731b9a8SJean-Christophe PLAGNIOL-VILLARD } else { 1425f1273f11STroy Kisky databuf[0] += 2; 1426f1273f11STroy Kisky databuf[8] = (temp & RH_B_DR) >> 8; 1427f1273f11STroy Kisky databuf[10] = databuf[9] = 0xff; 14282731b9a8SJean-Christophe PLAGNIOL-VILLARD } 14292731b9a8SJean-Christophe PLAGNIOL-VILLARD 14302731b9a8SJean-Christophe PLAGNIOL-VILLARD len = min_t(unsigned int, leni, 1431f1273f11STroy Kisky min_t(unsigned int, databuf[0], wLength)); 14322731b9a8SJean-Christophe PLAGNIOL-VILLARD OK(len); 14332731b9a8SJean-Christophe PLAGNIOL-VILLARD } 14342731b9a8SJean-Christophe PLAGNIOL-VILLARD 14355f6aa03fSMarek Vasut case RH_GET_CONFIGURATION: 1436f1273f11STroy Kisky databuf[0] = 0x01; 14375f6aa03fSMarek Vasut OK(1); 14382731b9a8SJean-Christophe PLAGNIOL-VILLARD 14395f6aa03fSMarek Vasut case RH_SET_CONFIGURATION: 14405f6aa03fSMarek Vasut WR_RH_STAT(0x10000); 14415f6aa03fSMarek Vasut OK(0); 14422731b9a8SJean-Christophe PLAGNIOL-VILLARD 14432731b9a8SJean-Christophe PLAGNIOL-VILLARD default: 14442731b9a8SJean-Christophe PLAGNIOL-VILLARD dbg("unsupported root hub command"); 14452731b9a8SJean-Christophe PLAGNIOL-VILLARD stat = USB_ST_STALLED; 14462731b9a8SJean-Christophe PLAGNIOL-VILLARD } 14472731b9a8SJean-Christophe PLAGNIOL-VILLARD 14482731b9a8SJean-Christophe PLAGNIOL-VILLARD #ifdef DEBUG 1449c5613df5SHans de Goede ohci_dump_roothub(ohci, 1); 14502731b9a8SJean-Christophe PLAGNIOL-VILLARD #else 14518f761f02SHans de Goede ohci_mdelay(1); 14522731b9a8SJean-Christophe PLAGNIOL-VILLARD #endif 14532731b9a8SJean-Christophe PLAGNIOL-VILLARD 14542731b9a8SJean-Christophe PLAGNIOL-VILLARD len = min_t(int, len, leni); 1455f1273f11STroy Kisky if (data != databuf) 1456f1273f11STroy Kisky memcpy(data, databuf, len); 14572731b9a8SJean-Christophe PLAGNIOL-VILLARD dev->act_len = len; 14582731b9a8SJean-Christophe PLAGNIOL-VILLARD dev->status = stat; 14592731b9a8SJean-Christophe PLAGNIOL-VILLARD 14602731b9a8SJean-Christophe PLAGNIOL-VILLARD #ifdef DEBUG 1461c5613df5SHans de Goede pkt_print(ohci, NULL, dev, pipe, buffer, 14622731b9a8SJean-Christophe PLAGNIOL-VILLARD transfer_len, cmd, "RET(rh)", 0/*usb_pipein(pipe)*/); 14632731b9a8SJean-Christophe PLAGNIOL-VILLARD #else 14648f761f02SHans de Goede ohci_mdelay(1); 14652731b9a8SJean-Christophe PLAGNIOL-VILLARD #endif 14662731b9a8SJean-Christophe PLAGNIOL-VILLARD 14672731b9a8SJean-Christophe PLAGNIOL-VILLARD return stat; 14682731b9a8SJean-Christophe PLAGNIOL-VILLARD } 14692731b9a8SJean-Christophe PLAGNIOL-VILLARD 14702731b9a8SJean-Christophe PLAGNIOL-VILLARD /*-------------------------------------------------------------------------*/ 14712731b9a8SJean-Christophe PLAGNIOL-VILLARD 147244dbc330SHans de Goede static ohci_dev_t *ohci_get_ohci_dev(ohci_t *ohci, int devnum, int intr) 147344dbc330SHans de Goede { 147444dbc330SHans de Goede int i; 147544dbc330SHans de Goede 147644dbc330SHans de Goede if (!intr) 147744dbc330SHans de Goede return &ohci->ohci_dev; 147844dbc330SHans de Goede 147944dbc330SHans de Goede /* First see if we already have an ohci_dev for this dev. */ 148044dbc330SHans de Goede for (i = 0; i < NUM_INT_DEVS; i++) { 148144dbc330SHans de Goede if (ohci->int_dev[i].devnum == devnum) 148244dbc330SHans de Goede return &ohci->int_dev[i]; 148344dbc330SHans de Goede } 148444dbc330SHans de Goede 148544dbc330SHans de Goede /* If not then find a free one. */ 148644dbc330SHans de Goede for (i = 0; i < NUM_INT_DEVS; i++) { 148744dbc330SHans de Goede if (ohci->int_dev[i].devnum == -1) { 148844dbc330SHans de Goede ohci->int_dev[i].devnum = devnum; 148944dbc330SHans de Goede return &ohci->int_dev[i]; 149044dbc330SHans de Goede } 149144dbc330SHans de Goede } 149244dbc330SHans de Goede 149344dbc330SHans de Goede printf("ohci: Error out of ohci_devs for interrupt endpoints\n"); 149444dbc330SHans de Goede return NULL; 149544dbc330SHans de Goede } 149644dbc330SHans de Goede 14972731b9a8SJean-Christophe PLAGNIOL-VILLARD /* common code for handling submit messages - used for all but root hub */ 14982731b9a8SJean-Christophe PLAGNIOL-VILLARD /* accesses. */ 1499d563e62cSHans de Goede static urb_priv_t *ohci_alloc_urb(struct usb_device *dev, unsigned long pipe, 1500d563e62cSHans de Goede void *buffer, int transfer_len, int interval) 1501d563e62cSHans de Goede { 1502d563e62cSHans de Goede urb_priv_t *urb; 1503d563e62cSHans de Goede 1504d563e62cSHans de Goede urb = calloc(1, sizeof(urb_priv_t)); 1505d563e62cSHans de Goede if (!urb) { 1506d563e62cSHans de Goede printf("ohci: Error out of memory allocating urb\n"); 1507d563e62cSHans de Goede return NULL; 1508d563e62cSHans de Goede } 1509d563e62cSHans de Goede 1510d563e62cSHans de Goede urb->dev = dev; 1511d563e62cSHans de Goede urb->pipe = pipe; 1512d563e62cSHans de Goede urb->transfer_buffer = buffer; 1513d563e62cSHans de Goede urb->transfer_buffer_length = transfer_len; 1514d563e62cSHans de Goede urb->interval = interval; 1515d563e62cSHans de Goede 1516d563e62cSHans de Goede return urb; 1517d563e62cSHans de Goede } 1518d563e62cSHans de Goede 1519c5613df5SHans de Goede static int submit_common_msg(ohci_t *ohci, struct usb_device *dev, 1520c5613df5SHans de Goede unsigned long pipe, void *buffer, int transfer_len, 1521c5613df5SHans de Goede struct devrequest *setup, int interval) 15222731b9a8SJean-Christophe PLAGNIOL-VILLARD { 15232731b9a8SJean-Christophe PLAGNIOL-VILLARD int stat = 0; 15242731b9a8SJean-Christophe PLAGNIOL-VILLARD int maxsize = usb_maxpacket(dev, pipe); 15252731b9a8SJean-Christophe PLAGNIOL-VILLARD int timeout; 15262731b9a8SJean-Christophe PLAGNIOL-VILLARD urb_priv_t *urb; 152744dbc330SHans de Goede ohci_dev_t *ohci_dev; 15282731b9a8SJean-Christophe PLAGNIOL-VILLARD 1529d563e62cSHans de Goede urb = ohci_alloc_urb(dev, pipe, buffer, transfer_len, interval); 1530d563e62cSHans de Goede if (!urb) 1531d563e62cSHans de Goede return -ENOMEM; 15322731b9a8SJean-Christophe PLAGNIOL-VILLARD 15332731b9a8SJean-Christophe PLAGNIOL-VILLARD #ifdef DEBUG 15342731b9a8SJean-Christophe PLAGNIOL-VILLARD urb->actual_length = 0; 1535c5613df5SHans de Goede pkt_print(ohci, urb, dev, pipe, buffer, transfer_len, 15362731b9a8SJean-Christophe PLAGNIOL-VILLARD setup, "SUB", usb_pipein(pipe)); 15372731b9a8SJean-Christophe PLAGNIOL-VILLARD #else 15388f761f02SHans de Goede ohci_mdelay(1); 15392731b9a8SJean-Christophe PLAGNIOL-VILLARD #endif 15402731b9a8SJean-Christophe PLAGNIOL-VILLARD if (!maxsize) { 15412731b9a8SJean-Christophe PLAGNIOL-VILLARD err("submit_common_message: pipesize for pipe %lx is zero", 15422731b9a8SJean-Christophe PLAGNIOL-VILLARD pipe); 15432731b9a8SJean-Christophe PLAGNIOL-VILLARD return -1; 15442731b9a8SJean-Christophe PLAGNIOL-VILLARD } 15452731b9a8SJean-Christophe PLAGNIOL-VILLARD 154644dbc330SHans de Goede ohci_dev = ohci_get_ohci_dev(ohci, dev->devnum, usb_pipeint(pipe)); 154744dbc330SHans de Goede if (!ohci_dev) 154844dbc330SHans de Goede return -ENOMEM; 154944dbc330SHans de Goede 155044dbc330SHans de Goede if (sohci_submit_job(ohci, ohci_dev, urb, setup) < 0) { 15512731b9a8SJean-Christophe PLAGNIOL-VILLARD err("sohci_submit_job failed"); 15522731b9a8SJean-Christophe PLAGNIOL-VILLARD return -1; 15532731b9a8SJean-Christophe PLAGNIOL-VILLARD } 15542731b9a8SJean-Christophe PLAGNIOL-VILLARD 15552731b9a8SJean-Christophe PLAGNIOL-VILLARD #if 0 15565b84dd67SMike Frysinger mdelay(10); 1557c5613df5SHans de Goede /* ohci_dump_status(ohci); */ 15582731b9a8SJean-Christophe PLAGNIOL-VILLARD #endif 15592731b9a8SJean-Christophe PLAGNIOL-VILLARD 156096820a35SSimon Glass timeout = USB_TIMEOUT_MS(pipe); 15612731b9a8SJean-Christophe PLAGNIOL-VILLARD 15622731b9a8SJean-Christophe PLAGNIOL-VILLARD /* wait for it to complete */ 15632731b9a8SJean-Christophe PLAGNIOL-VILLARD for (;;) { 15642731b9a8SJean-Christophe PLAGNIOL-VILLARD /* check whether the controller is done */ 1565c5613df5SHans de Goede stat = hc_interrupt(ohci); 15662731b9a8SJean-Christophe PLAGNIOL-VILLARD if (stat < 0) { 15672731b9a8SJean-Christophe PLAGNIOL-VILLARD stat = USB_ST_CRC_ERR; 15682731b9a8SJean-Christophe PLAGNIOL-VILLARD break; 15692731b9a8SJean-Christophe PLAGNIOL-VILLARD } 15702731b9a8SJean-Christophe PLAGNIOL-VILLARD 15712731b9a8SJean-Christophe PLAGNIOL-VILLARD /* NOTE: since we are not interrupt driven in U-Boot and always 15722731b9a8SJean-Christophe PLAGNIOL-VILLARD * handle only one URB at a time, we cannot assume the 15732731b9a8SJean-Christophe PLAGNIOL-VILLARD * transaction finished on the first successful return from 15742731b9a8SJean-Christophe PLAGNIOL-VILLARD * hc_interrupt().. unless the flag for current URB is set, 15752731b9a8SJean-Christophe PLAGNIOL-VILLARD * meaning that all TD's to/from device got actually 15762731b9a8SJean-Christophe PLAGNIOL-VILLARD * transferred and processed. If the current URB is not 15772731b9a8SJean-Christophe PLAGNIOL-VILLARD * finished we need to re-iterate this loop so as 15782731b9a8SJean-Christophe PLAGNIOL-VILLARD * hc_interrupt() gets called again as there needs to be some 15792731b9a8SJean-Christophe PLAGNIOL-VILLARD * more TD's to process still */ 15802731b9a8SJean-Christophe PLAGNIOL-VILLARD if ((stat >= 0) && (stat != 0xff) && (urb->finished)) { 15812731b9a8SJean-Christophe PLAGNIOL-VILLARD /* 0xff is returned for an SF-interrupt */ 15822731b9a8SJean-Christophe PLAGNIOL-VILLARD break; 15832731b9a8SJean-Christophe PLAGNIOL-VILLARD } 15842731b9a8SJean-Christophe PLAGNIOL-VILLARD 15852731b9a8SJean-Christophe PLAGNIOL-VILLARD if (--timeout) { 15865b84dd67SMike Frysinger mdelay(1); 15872731b9a8SJean-Christophe PLAGNIOL-VILLARD if (!urb->finished) 15882731b9a8SJean-Christophe PLAGNIOL-VILLARD dbg("*"); 15892731b9a8SJean-Christophe PLAGNIOL-VILLARD 15902731b9a8SJean-Christophe PLAGNIOL-VILLARD } else { 1591fa5b9baaSHans de Goede if (!usb_pipeint(pipe)) 15922731b9a8SJean-Christophe PLAGNIOL-VILLARD err("CTL:TIMEOUT "); 15932731b9a8SJean-Christophe PLAGNIOL-VILLARD dbg("submit_common_msg: TO status %x\n", stat); 15942731b9a8SJean-Christophe PLAGNIOL-VILLARD urb->finished = 1; 15952731b9a8SJean-Christophe PLAGNIOL-VILLARD stat = USB_ST_CRC_ERR; 15962731b9a8SJean-Christophe PLAGNIOL-VILLARD break; 15972731b9a8SJean-Christophe PLAGNIOL-VILLARD } 15982731b9a8SJean-Christophe PLAGNIOL-VILLARD } 15992731b9a8SJean-Christophe PLAGNIOL-VILLARD 16002731b9a8SJean-Christophe PLAGNIOL-VILLARD dev->status = stat; 1601522c9564SMateusz Kulikowski dev->act_len = urb->actual_length; 16022731b9a8SJean-Christophe PLAGNIOL-VILLARD 16038d005ef8SHans de Goede if (usb_pipein(pipe) && dev->status == 0 && dev->act_len) 16048d005ef8SHans de Goede invalidate_dcache_buffer(buffer, dev->act_len); 16058d005ef8SHans de Goede 16062731b9a8SJean-Christophe PLAGNIOL-VILLARD #ifdef DEBUG 1607c5613df5SHans de Goede pkt_print(ohci, urb, dev, pipe, buffer, transfer_len, 16082731b9a8SJean-Christophe PLAGNIOL-VILLARD setup, "RET(ctlr)", usb_pipein(pipe)); 16092731b9a8SJean-Christophe PLAGNIOL-VILLARD #else 16108f761f02SHans de Goede ohci_mdelay(1); 16112731b9a8SJean-Christophe PLAGNIOL-VILLARD #endif 16122731b9a8SJean-Christophe PLAGNIOL-VILLARD urb_free_priv(urb); 16132731b9a8SJean-Christophe PLAGNIOL-VILLARD return 0; 16142731b9a8SJean-Christophe PLAGNIOL-VILLARD } 16152731b9a8SJean-Christophe PLAGNIOL-VILLARD 1616bf495712SHans de Goede #define MAX_INT_QUEUESIZE 8 1617bf495712SHans de Goede 1618bf495712SHans de Goede struct int_queue { 1619bf495712SHans de Goede int queuesize; 1620bf495712SHans de Goede int curr_urb; 1621bf495712SHans de Goede urb_priv_t *urb[MAX_INT_QUEUESIZE]; 1622bf495712SHans de Goede }; 1623bf495712SHans de Goede 1624bf495712SHans de Goede static struct int_queue *_ohci_create_int_queue(ohci_t *ohci, 1625bf495712SHans de Goede struct usb_device *udev, unsigned long pipe, int queuesize, 1626bf495712SHans de Goede int elementsize, void *buffer, int interval) 1627bf495712SHans de Goede { 1628bf495712SHans de Goede struct int_queue *queue; 1629bf495712SHans de Goede ohci_dev_t *ohci_dev; 1630bf495712SHans de Goede int i; 1631bf495712SHans de Goede 1632bf495712SHans de Goede if (queuesize > MAX_INT_QUEUESIZE) 1633bf495712SHans de Goede return NULL; 1634bf495712SHans de Goede 1635bf495712SHans de Goede ohci_dev = ohci_get_ohci_dev(ohci, udev->devnum, 1); 1636bf495712SHans de Goede if (!ohci_dev) 1637bf495712SHans de Goede return NULL; 1638bf495712SHans de Goede 1639bf495712SHans de Goede queue = malloc(sizeof(*queue)); 1640bf495712SHans de Goede if (!queue) { 1641bf495712SHans de Goede printf("ohci: Error out of memory allocating int queue\n"); 1642bf495712SHans de Goede return NULL; 1643bf495712SHans de Goede } 1644bf495712SHans de Goede 1645bf495712SHans de Goede for (i = 0; i < queuesize; i++) { 1646bf495712SHans de Goede queue->urb[i] = ohci_alloc_urb(udev, pipe, 1647bf495712SHans de Goede buffer + i * elementsize, 1648bf495712SHans de Goede elementsize, interval); 1649bf495712SHans de Goede if (!queue->urb[i]) 1650bf495712SHans de Goede break; 1651bf495712SHans de Goede 1652bf495712SHans de Goede if (sohci_submit_job(ohci, ohci_dev, queue->urb[i], NULL)) { 1653bf495712SHans de Goede printf("ohci: Error submitting int queue job\n"); 1654bf495712SHans de Goede urb_free_priv(queue->urb[i]); 1655bf495712SHans de Goede break; 1656bf495712SHans de Goede } 1657bf495712SHans de Goede } 1658bf495712SHans de Goede if (i == 0) { 1659bf495712SHans de Goede /* We did not succeed in submitting even 1 urb */ 1660bf495712SHans de Goede free(queue); 1661bf495712SHans de Goede return NULL; 1662bf495712SHans de Goede } 1663bf495712SHans de Goede 1664bf495712SHans de Goede queue->queuesize = i; 1665bf495712SHans de Goede queue->curr_urb = 0; 1666bf495712SHans de Goede 1667bf495712SHans de Goede return queue; 1668bf495712SHans de Goede } 1669bf495712SHans de Goede 1670bf495712SHans de Goede static void *_ohci_poll_int_queue(ohci_t *ohci, struct usb_device *udev, 1671bf495712SHans de Goede struct int_queue *queue) 1672bf495712SHans de Goede { 1673bf495712SHans de Goede if (queue->curr_urb == queue->queuesize) 1674bf495712SHans de Goede return NULL; /* Queue depleted */ 1675bf495712SHans de Goede 1676bf495712SHans de Goede if (hc_interrupt(ohci) < 0) 1677bf495712SHans de Goede return NULL; 1678bf495712SHans de Goede 1679bf495712SHans de Goede if (queue->urb[queue->curr_urb]->finished) { 1680bf495712SHans de Goede void *ret = queue->urb[queue->curr_urb]->transfer_buffer; 1681bf495712SHans de Goede queue->curr_urb++; 1682bf495712SHans de Goede return ret; 1683bf495712SHans de Goede } 1684bf495712SHans de Goede 1685bf495712SHans de Goede return NULL; 1686bf495712SHans de Goede } 1687bf495712SHans de Goede 1688bf495712SHans de Goede static int _ohci_destroy_int_queue(ohci_t *ohci, struct usb_device *dev, 1689bf495712SHans de Goede struct int_queue *queue) 1690bf495712SHans de Goede { 1691bf495712SHans de Goede int i; 1692bf495712SHans de Goede 1693bf495712SHans de Goede for (i = 0; i < queue->queuesize; i++) 1694bf495712SHans de Goede urb_free_priv(queue->urb[i]); 1695bf495712SHans de Goede 1696bf495712SHans de Goede free(queue); 1697bf495712SHans de Goede 1698bf495712SHans de Goede return 0; 1699bf495712SHans de Goede } 1700bf495712SHans de Goede 170158b4048fSHans de Goede #ifndef CONFIG_DM_USB 17022731b9a8SJean-Christophe PLAGNIOL-VILLARD /* submit routines called from usb.c */ 17032731b9a8SJean-Christophe PLAGNIOL-VILLARD int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer, 17042731b9a8SJean-Christophe PLAGNIOL-VILLARD int transfer_len) 17052731b9a8SJean-Christophe PLAGNIOL-VILLARD { 17062731b9a8SJean-Christophe PLAGNIOL-VILLARD info("submit_bulk_msg"); 1707c5613df5SHans de Goede return submit_common_msg(&gohci, dev, pipe, buffer, transfer_len, 1708c5613df5SHans de Goede NULL, 0); 17092731b9a8SJean-Christophe PLAGNIOL-VILLARD } 17102731b9a8SJean-Christophe PLAGNIOL-VILLARD 1711c5613df5SHans de Goede int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, 1712c5613df5SHans de Goede int transfer_len, int interval) 1713c5613df5SHans de Goede { 1714c5613df5SHans de Goede info("submit_int_msg"); 1715c5613df5SHans de Goede return submit_common_msg(&gohci, dev, pipe, buffer, transfer_len, NULL, 1716c5613df5SHans de Goede interval); 1717c5613df5SHans de Goede } 1718bf495712SHans de Goede 1719bf495712SHans de Goede struct int_queue *create_int_queue(struct usb_device *dev, 1720bf495712SHans de Goede unsigned long pipe, int queuesize, int elementsize, 1721bf495712SHans de Goede void *buffer, int interval) 1722bf495712SHans de Goede { 1723bf495712SHans de Goede return _ohci_create_int_queue(&gohci, dev, pipe, queuesize, 1724bf495712SHans de Goede elementsize, buffer, interval); 1725bf495712SHans de Goede } 1726bf495712SHans de Goede 1727bf495712SHans de Goede void *poll_int_queue(struct usb_device *dev, struct int_queue *queue) 1728bf495712SHans de Goede { 1729bf495712SHans de Goede return _ohci_poll_int_queue(&gohci, dev, queue); 1730bf495712SHans de Goede } 1731bf495712SHans de Goede 1732bf495712SHans de Goede int destroy_int_queue(struct usb_device *dev, struct int_queue *queue) 1733bf495712SHans de Goede { 1734bf495712SHans de Goede return _ohci_destroy_int_queue(&gohci, dev, queue); 1735bf495712SHans de Goede } 173658b4048fSHans de Goede #endif 1737c5613df5SHans de Goede 1738c5613df5SHans de Goede static int _ohci_submit_control_msg(ohci_t *ohci, struct usb_device *dev, 1739c5613df5SHans de Goede unsigned long pipe, void *buffer, int transfer_len, 1740c5613df5SHans de Goede struct devrequest *setup) 17412731b9a8SJean-Christophe PLAGNIOL-VILLARD { 17422731b9a8SJean-Christophe PLAGNIOL-VILLARD int maxsize = usb_maxpacket(dev, pipe); 17432731b9a8SJean-Christophe PLAGNIOL-VILLARD 17442731b9a8SJean-Christophe PLAGNIOL-VILLARD info("submit_control_msg"); 17452731b9a8SJean-Christophe PLAGNIOL-VILLARD #ifdef DEBUG 1746c5613df5SHans de Goede pkt_print(ohci, NULL, dev, pipe, buffer, transfer_len, 17472731b9a8SJean-Christophe PLAGNIOL-VILLARD setup, "SUB", usb_pipein(pipe)); 17482731b9a8SJean-Christophe PLAGNIOL-VILLARD #else 17498f761f02SHans de Goede ohci_mdelay(1); 17502731b9a8SJean-Christophe PLAGNIOL-VILLARD #endif 17512731b9a8SJean-Christophe PLAGNIOL-VILLARD if (!maxsize) { 17522731b9a8SJean-Christophe PLAGNIOL-VILLARD err("submit_control_message: pipesize for pipe %lx is zero", 17532731b9a8SJean-Christophe PLAGNIOL-VILLARD pipe); 17542731b9a8SJean-Christophe PLAGNIOL-VILLARD return -1; 17552731b9a8SJean-Christophe PLAGNIOL-VILLARD } 1756c5613df5SHans de Goede if (((pipe >> 8) & 0x7f) == ohci->rh.devnum) { 1757c5613df5SHans de Goede ohci->rh.dev = dev; 17582731b9a8SJean-Christophe PLAGNIOL-VILLARD /* root hub - redirect */ 1759c5613df5SHans de Goede return ohci_submit_rh_msg(ohci, dev, pipe, buffer, 1760c5613df5SHans de Goede transfer_len, setup); 17612731b9a8SJean-Christophe PLAGNIOL-VILLARD } 17622731b9a8SJean-Christophe PLAGNIOL-VILLARD 1763c5613df5SHans de Goede return submit_common_msg(ohci, dev, pipe, buffer, transfer_len, 1764c5613df5SHans de Goede setup, 0); 17652731b9a8SJean-Christophe PLAGNIOL-VILLARD } 17662731b9a8SJean-Christophe PLAGNIOL-VILLARD 17672731b9a8SJean-Christophe PLAGNIOL-VILLARD /*-------------------------------------------------------------------------* 17682731b9a8SJean-Christophe PLAGNIOL-VILLARD * HC functions 17692731b9a8SJean-Christophe PLAGNIOL-VILLARD *-------------------------------------------------------------------------*/ 17702731b9a8SJean-Christophe PLAGNIOL-VILLARD 17712731b9a8SJean-Christophe PLAGNIOL-VILLARD /* reset the HC and BUS */ 17722731b9a8SJean-Christophe PLAGNIOL-VILLARD 17732731b9a8SJean-Christophe PLAGNIOL-VILLARD static int hc_reset(ohci_t *ohci) 17742731b9a8SJean-Christophe PLAGNIOL-VILLARD { 17752731b9a8SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_PCI_EHCI_DEVNO 17762731b9a8SJean-Christophe PLAGNIOL-VILLARD pci_dev_t pdev; 17772731b9a8SJean-Christophe PLAGNIOL-VILLARD #endif 17782731b9a8SJean-Christophe PLAGNIOL-VILLARD int timeout = 30; 17792731b9a8SJean-Christophe PLAGNIOL-VILLARD int smm_timeout = 50; /* 0,5 sec */ 17802731b9a8SJean-Christophe PLAGNIOL-VILLARD 17812731b9a8SJean-Christophe PLAGNIOL-VILLARD dbg("%s\n", __FUNCTION__); 17822731b9a8SJean-Christophe PLAGNIOL-VILLARD 17832731b9a8SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_PCI_EHCI_DEVNO 17842731b9a8SJean-Christophe PLAGNIOL-VILLARD /* 17852731b9a8SJean-Christophe PLAGNIOL-VILLARD * Some multi-function controllers (e.g. ISP1562) allow root hub 17862731b9a8SJean-Christophe PLAGNIOL-VILLARD * resetting via EHCI registers only. 17872731b9a8SJean-Christophe PLAGNIOL-VILLARD */ 17882731b9a8SJean-Christophe PLAGNIOL-VILLARD pdev = pci_find_devices(ehci_pci_ids, CONFIG_PCI_EHCI_DEVNO); 17892731b9a8SJean-Christophe PLAGNIOL-VILLARD if (pdev != -1) { 17902731b9a8SJean-Christophe PLAGNIOL-VILLARD u32 base; 17912731b9a8SJean-Christophe PLAGNIOL-VILLARD int timeout = 1000; 17922731b9a8SJean-Christophe PLAGNIOL-VILLARD 17932731b9a8SJean-Christophe PLAGNIOL-VILLARD pci_read_config_dword(pdev, PCI_BASE_ADDRESS_0, &base); 1794a5496a18SBecky Bruce base += EHCI_USBCMD_OFF; 1795a5496a18SBecky Bruce ohci_writel(ohci_readl(base) | EHCI_USBCMD_HCRESET, base); 17962731b9a8SJean-Christophe PLAGNIOL-VILLARD 1797a5496a18SBecky Bruce while (ohci_readl(base) & EHCI_USBCMD_HCRESET) { 17982731b9a8SJean-Christophe PLAGNIOL-VILLARD if (timeout-- <= 0) { 17992731b9a8SJean-Christophe PLAGNIOL-VILLARD printf("USB RootHub reset timed out!"); 18002731b9a8SJean-Christophe PLAGNIOL-VILLARD break; 18012731b9a8SJean-Christophe PLAGNIOL-VILLARD } 18022731b9a8SJean-Christophe PLAGNIOL-VILLARD udelay(1); 18032731b9a8SJean-Christophe PLAGNIOL-VILLARD } 18042731b9a8SJean-Christophe PLAGNIOL-VILLARD } else 18052731b9a8SJean-Christophe PLAGNIOL-VILLARD printf("No EHCI func at %d index!\n", CONFIG_PCI_EHCI_DEVNO); 18062731b9a8SJean-Christophe PLAGNIOL-VILLARD #endif 1807a5496a18SBecky Bruce if (ohci_readl(&ohci->regs->control) & OHCI_CTRL_IR) { 1808a5496a18SBecky Bruce /* SMM owns the HC, request ownership */ 1809a5496a18SBecky Bruce ohci_writel(OHCI_OCR, &ohci->regs->cmdstatus); 18102731b9a8SJean-Christophe PLAGNIOL-VILLARD info("USB HC TakeOver from SMM"); 1811a5496a18SBecky Bruce while (ohci_readl(&ohci->regs->control) & OHCI_CTRL_IR) { 18125b84dd67SMike Frysinger mdelay(10); 18132731b9a8SJean-Christophe PLAGNIOL-VILLARD if (--smm_timeout == 0) { 18142731b9a8SJean-Christophe PLAGNIOL-VILLARD err("USB HC TakeOver failed!"); 18152731b9a8SJean-Christophe PLAGNIOL-VILLARD return -1; 18162731b9a8SJean-Christophe PLAGNIOL-VILLARD } 18172731b9a8SJean-Christophe PLAGNIOL-VILLARD } 18182731b9a8SJean-Christophe PLAGNIOL-VILLARD } 18192731b9a8SJean-Christophe PLAGNIOL-VILLARD 18202731b9a8SJean-Christophe PLAGNIOL-VILLARD /* Disable HC interrupts */ 1821a5496a18SBecky Bruce ohci_writel(OHCI_INTR_MIE, &ohci->regs->intrdisable); 18222731b9a8SJean-Christophe PLAGNIOL-VILLARD 18232731b9a8SJean-Christophe PLAGNIOL-VILLARD dbg("USB HC reset_hc usb-%s: ctrl = 0x%X ;\n", 18242731b9a8SJean-Christophe PLAGNIOL-VILLARD ohci->slot_name, 1825a5496a18SBecky Bruce ohci_readl(&ohci->regs->control)); 18262731b9a8SJean-Christophe PLAGNIOL-VILLARD 18272731b9a8SJean-Christophe PLAGNIOL-VILLARD /* Reset USB (needed by some controllers) */ 18282731b9a8SJean-Christophe PLAGNIOL-VILLARD ohci->hc_control = 0; 1829a5496a18SBecky Bruce ohci_writel(ohci->hc_control, &ohci->regs->control); 18302731b9a8SJean-Christophe PLAGNIOL-VILLARD 18312731b9a8SJean-Christophe PLAGNIOL-VILLARD /* HC Reset requires max 10 us delay */ 1832a5496a18SBecky Bruce ohci_writel(OHCI_HCR, &ohci->regs->cmdstatus); 1833a5496a18SBecky Bruce while ((ohci_readl(&ohci->regs->cmdstatus) & OHCI_HCR) != 0) { 18342731b9a8SJean-Christophe PLAGNIOL-VILLARD if (--timeout == 0) { 18352731b9a8SJean-Christophe PLAGNIOL-VILLARD err("USB HC reset timed out!"); 18362731b9a8SJean-Christophe PLAGNIOL-VILLARD return -1; 18372731b9a8SJean-Christophe PLAGNIOL-VILLARD } 18382731b9a8SJean-Christophe PLAGNIOL-VILLARD udelay(1); 18392731b9a8SJean-Christophe PLAGNIOL-VILLARD } 18402731b9a8SJean-Christophe PLAGNIOL-VILLARD return 0; 18412731b9a8SJean-Christophe PLAGNIOL-VILLARD } 18422731b9a8SJean-Christophe PLAGNIOL-VILLARD 18432731b9a8SJean-Christophe PLAGNIOL-VILLARD /*-------------------------------------------------------------------------*/ 18442731b9a8SJean-Christophe PLAGNIOL-VILLARD 18452731b9a8SJean-Christophe PLAGNIOL-VILLARD /* Start an OHCI controller, set the BUS operational 18462731b9a8SJean-Christophe PLAGNIOL-VILLARD * enable interrupts 18472731b9a8SJean-Christophe PLAGNIOL-VILLARD * connect the virtual root hub */ 18482731b9a8SJean-Christophe PLAGNIOL-VILLARD 18492731b9a8SJean-Christophe PLAGNIOL-VILLARD static int hc_start(ohci_t *ohci) 18502731b9a8SJean-Christophe PLAGNIOL-VILLARD { 18512731b9a8SJean-Christophe PLAGNIOL-VILLARD __u32 mask; 18522731b9a8SJean-Christophe PLAGNIOL-VILLARD unsigned int fminterval; 185344dbc330SHans de Goede int i; 18542731b9a8SJean-Christophe PLAGNIOL-VILLARD 18552731b9a8SJean-Christophe PLAGNIOL-VILLARD ohci->disabled = 1; 185644dbc330SHans de Goede for (i = 0; i < NUM_INT_DEVS; i++) 185744dbc330SHans de Goede ohci->int_dev[i].devnum = -1; 18582731b9a8SJean-Christophe PLAGNIOL-VILLARD 18592731b9a8SJean-Christophe PLAGNIOL-VILLARD /* Tell the controller where the control and bulk lists are 18602731b9a8SJean-Christophe PLAGNIOL-VILLARD * The lists are empty now. */ 18612731b9a8SJean-Christophe PLAGNIOL-VILLARD 1862a5496a18SBecky Bruce ohci_writel(0, &ohci->regs->ed_controlhead); 1863a5496a18SBecky Bruce ohci_writel(0, &ohci->regs->ed_bulkhead); 18642731b9a8SJean-Christophe PLAGNIOL-VILLARD 1865a5496a18SBecky Bruce ohci_writel((__u32)ohci->hcca, 1866a5496a18SBecky Bruce &ohci->regs->hcca); /* reset clears this */ 18672731b9a8SJean-Christophe PLAGNIOL-VILLARD 18682731b9a8SJean-Christophe PLAGNIOL-VILLARD fminterval = 0x2edf; 1869a5496a18SBecky Bruce ohci_writel((fminterval * 9) / 10, &ohci->regs->periodicstart); 18702731b9a8SJean-Christophe PLAGNIOL-VILLARD fminterval |= ((((fminterval - 210) * 6) / 7) << 16); 1871a5496a18SBecky Bruce ohci_writel(fminterval, &ohci->regs->fminterval); 1872a5496a18SBecky Bruce ohci_writel(0x628, &ohci->regs->lsthresh); 18732731b9a8SJean-Christophe PLAGNIOL-VILLARD 18742731b9a8SJean-Christophe PLAGNIOL-VILLARD /* start controller operations */ 18752731b9a8SJean-Christophe PLAGNIOL-VILLARD ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER; 18762731b9a8SJean-Christophe PLAGNIOL-VILLARD ohci->disabled = 0; 1877a5496a18SBecky Bruce ohci_writel(ohci->hc_control, &ohci->regs->control); 18782731b9a8SJean-Christophe PLAGNIOL-VILLARD 18792731b9a8SJean-Christophe PLAGNIOL-VILLARD /* disable all interrupts */ 18802731b9a8SJean-Christophe PLAGNIOL-VILLARD mask = (OHCI_INTR_SO | OHCI_INTR_WDH | OHCI_INTR_SF | OHCI_INTR_RD | 18812731b9a8SJean-Christophe PLAGNIOL-VILLARD OHCI_INTR_UE | OHCI_INTR_FNO | OHCI_INTR_RHSC | 18822731b9a8SJean-Christophe PLAGNIOL-VILLARD OHCI_INTR_OC | OHCI_INTR_MIE); 1883a5496a18SBecky Bruce ohci_writel(mask, &ohci->regs->intrdisable); 18842731b9a8SJean-Christophe PLAGNIOL-VILLARD /* clear all interrupts */ 18852731b9a8SJean-Christophe PLAGNIOL-VILLARD mask &= ~OHCI_INTR_MIE; 1886a5496a18SBecky Bruce ohci_writel(mask, &ohci->regs->intrstatus); 18872731b9a8SJean-Christophe PLAGNIOL-VILLARD /* Choose the interrupts we care about now - but w/o MIE */ 18882731b9a8SJean-Christophe PLAGNIOL-VILLARD mask = OHCI_INTR_RHSC | OHCI_INTR_UE | OHCI_INTR_WDH | OHCI_INTR_SO; 1889a5496a18SBecky Bruce ohci_writel(mask, &ohci->regs->intrenable); 18902731b9a8SJean-Christophe PLAGNIOL-VILLARD 18912731b9a8SJean-Christophe PLAGNIOL-VILLARD #ifdef OHCI_USE_NPS 18922731b9a8SJean-Christophe PLAGNIOL-VILLARD /* required for AMD-756 and some Mac platforms */ 1893a5496a18SBecky Bruce ohci_writel((roothub_a(ohci) | RH_A_NPS) & ~RH_A_PSM, 18942731b9a8SJean-Christophe PLAGNIOL-VILLARD &ohci->regs->roothub.a); 1895a5496a18SBecky Bruce ohci_writel(RH_HS_LPSC, &ohci->regs->roothub.status); 18962731b9a8SJean-Christophe PLAGNIOL-VILLARD #endif /* OHCI_USE_NPS */ 18972731b9a8SJean-Christophe PLAGNIOL-VILLARD 18982731b9a8SJean-Christophe PLAGNIOL-VILLARD /* connect the virtual root hub */ 18992731b9a8SJean-Christophe PLAGNIOL-VILLARD ohci->rh.devnum = 0; 19002731b9a8SJean-Christophe PLAGNIOL-VILLARD 19012731b9a8SJean-Christophe PLAGNIOL-VILLARD return 0; 19022731b9a8SJean-Christophe PLAGNIOL-VILLARD } 19032731b9a8SJean-Christophe PLAGNIOL-VILLARD 19042731b9a8SJean-Christophe PLAGNIOL-VILLARD /*-------------------------------------------------------------------------*/ 19052731b9a8SJean-Christophe PLAGNIOL-VILLARD 19062731b9a8SJean-Christophe PLAGNIOL-VILLARD /* an interrupt happens */ 19072731b9a8SJean-Christophe PLAGNIOL-VILLARD 1908c5613df5SHans de Goede static int hc_interrupt(ohci_t *ohci) 19092731b9a8SJean-Christophe PLAGNIOL-VILLARD { 19102731b9a8SJean-Christophe PLAGNIOL-VILLARD struct ohci_regs *regs = ohci->regs; 19112731b9a8SJean-Christophe PLAGNIOL-VILLARD int ints; 19122731b9a8SJean-Christophe PLAGNIOL-VILLARD int stat = -1; 19132731b9a8SJean-Christophe PLAGNIOL-VILLARD 19148d005ef8SHans de Goede invalidate_dcache_hcca(ohci->hcca); 19158d005ef8SHans de Goede 19162731b9a8SJean-Christophe PLAGNIOL-VILLARD if ((ohci->hcca->done_head != 0) && 19172731b9a8SJean-Christophe PLAGNIOL-VILLARD !(m32_swap(ohci->hcca->done_head) & 0x01)) { 19182731b9a8SJean-Christophe PLAGNIOL-VILLARD ints = OHCI_INTR_WDH; 19192731b9a8SJean-Christophe PLAGNIOL-VILLARD } else { 1920a5496a18SBecky Bruce ints = ohci_readl(®s->intrstatus); 19212731b9a8SJean-Christophe PLAGNIOL-VILLARD if (ints == ~(u32)0) { 19222731b9a8SJean-Christophe PLAGNIOL-VILLARD ohci->disabled++; 19232731b9a8SJean-Christophe PLAGNIOL-VILLARD err("%s device removed!", ohci->slot_name); 19242731b9a8SJean-Christophe PLAGNIOL-VILLARD return -1; 19252731b9a8SJean-Christophe PLAGNIOL-VILLARD } else { 1926a5496a18SBecky Bruce ints &= ohci_readl(®s->intrenable); 19272731b9a8SJean-Christophe PLAGNIOL-VILLARD if (ints == 0) { 19282731b9a8SJean-Christophe PLAGNIOL-VILLARD dbg("hc_interrupt: returning..\n"); 19292731b9a8SJean-Christophe PLAGNIOL-VILLARD return 0xff; 19302731b9a8SJean-Christophe PLAGNIOL-VILLARD } 19312731b9a8SJean-Christophe PLAGNIOL-VILLARD } 19322731b9a8SJean-Christophe PLAGNIOL-VILLARD } 19332731b9a8SJean-Christophe PLAGNIOL-VILLARD 19342731b9a8SJean-Christophe PLAGNIOL-VILLARD /* dbg("Interrupt: %x frame: %x", ints, 19352731b9a8SJean-Christophe PLAGNIOL-VILLARD le16_to_cpu(ohci->hcca->frame_no)); */ 19362731b9a8SJean-Christophe PLAGNIOL-VILLARD 19372731b9a8SJean-Christophe PLAGNIOL-VILLARD if (ints & OHCI_INTR_RHSC) 19382731b9a8SJean-Christophe PLAGNIOL-VILLARD stat = 0xff; 19392731b9a8SJean-Christophe PLAGNIOL-VILLARD 19402731b9a8SJean-Christophe PLAGNIOL-VILLARD if (ints & OHCI_INTR_UE) { 19412731b9a8SJean-Christophe PLAGNIOL-VILLARD ohci->disabled++; 19422731b9a8SJean-Christophe PLAGNIOL-VILLARD err("OHCI Unrecoverable Error, controller usb-%s disabled", 19432731b9a8SJean-Christophe PLAGNIOL-VILLARD ohci->slot_name); 19442731b9a8SJean-Christophe PLAGNIOL-VILLARD /* e.g. due to PCI Master/Target Abort */ 19452731b9a8SJean-Christophe PLAGNIOL-VILLARD 19462731b9a8SJean-Christophe PLAGNIOL-VILLARD #ifdef DEBUG 19472731b9a8SJean-Christophe PLAGNIOL-VILLARD ohci_dump(ohci, 1); 19482731b9a8SJean-Christophe PLAGNIOL-VILLARD #else 19498f761f02SHans de Goede ohci_mdelay(1); 19502731b9a8SJean-Christophe PLAGNIOL-VILLARD #endif 19512731b9a8SJean-Christophe PLAGNIOL-VILLARD /* FIXME: be optimistic, hope that bug won't repeat often. */ 19522731b9a8SJean-Christophe PLAGNIOL-VILLARD /* Make some non-interrupt context restart the controller. */ 19532731b9a8SJean-Christophe PLAGNIOL-VILLARD /* Count and limit the retries though; either hardware or */ 19542731b9a8SJean-Christophe PLAGNIOL-VILLARD /* software errors can go forever... */ 19552731b9a8SJean-Christophe PLAGNIOL-VILLARD hc_reset(ohci); 19562731b9a8SJean-Christophe PLAGNIOL-VILLARD return -1; 19572731b9a8SJean-Christophe PLAGNIOL-VILLARD } 19582731b9a8SJean-Christophe PLAGNIOL-VILLARD 19592731b9a8SJean-Christophe PLAGNIOL-VILLARD if (ints & OHCI_INTR_WDH) { 19608f761f02SHans de Goede ohci_mdelay(1); 1961a5496a18SBecky Bruce ohci_writel(OHCI_INTR_WDH, ®s->intrdisable); 1962a5496a18SBecky Bruce (void)ohci_readl(®s->intrdisable); /* flush */ 1963c5613df5SHans de Goede stat = dl_done_list(ohci); 1964a5496a18SBecky Bruce ohci_writel(OHCI_INTR_WDH, ®s->intrenable); 1965a5496a18SBecky Bruce (void)ohci_readl(®s->intrdisable); /* flush */ 19662731b9a8SJean-Christophe PLAGNIOL-VILLARD } 19672731b9a8SJean-Christophe PLAGNIOL-VILLARD 19682731b9a8SJean-Christophe PLAGNIOL-VILLARD if (ints & OHCI_INTR_SO) { 19692731b9a8SJean-Christophe PLAGNIOL-VILLARD dbg("USB Schedule overrun\n"); 1970a5496a18SBecky Bruce ohci_writel(OHCI_INTR_SO, ®s->intrenable); 19712731b9a8SJean-Christophe PLAGNIOL-VILLARD stat = -1; 19722731b9a8SJean-Christophe PLAGNIOL-VILLARD } 19732731b9a8SJean-Christophe PLAGNIOL-VILLARD 19742731b9a8SJean-Christophe PLAGNIOL-VILLARD /* FIXME: this assumes SOF (1/ms) interrupts don't get lost... */ 19752731b9a8SJean-Christophe PLAGNIOL-VILLARD if (ints & OHCI_INTR_SF) { 19762731b9a8SJean-Christophe PLAGNIOL-VILLARD unsigned int frame = m16_swap(ohci->hcca->frame_no) & 1; 19775b84dd67SMike Frysinger mdelay(1); 1978a5496a18SBecky Bruce ohci_writel(OHCI_INTR_SF, ®s->intrdisable); 19792731b9a8SJean-Christophe PLAGNIOL-VILLARD if (ohci->ed_rm_list[frame] != NULL) 1980a5496a18SBecky Bruce ohci_writel(OHCI_INTR_SF, ®s->intrenable); 19812731b9a8SJean-Christophe PLAGNIOL-VILLARD stat = 0xff; 19822731b9a8SJean-Christophe PLAGNIOL-VILLARD } 19832731b9a8SJean-Christophe PLAGNIOL-VILLARD 1984a5496a18SBecky Bruce ohci_writel(ints, ®s->intrstatus); 19852731b9a8SJean-Christophe PLAGNIOL-VILLARD return stat; 19862731b9a8SJean-Christophe PLAGNIOL-VILLARD } 19872731b9a8SJean-Christophe PLAGNIOL-VILLARD 19882731b9a8SJean-Christophe PLAGNIOL-VILLARD /*-------------------------------------------------------------------------*/ 19892731b9a8SJean-Christophe PLAGNIOL-VILLARD 199058b4048fSHans de Goede #ifndef CONFIG_DM_USB 199158b4048fSHans de Goede 19922731b9a8SJean-Christophe PLAGNIOL-VILLARD /*-------------------------------------------------------------------------*/ 19932731b9a8SJean-Christophe PLAGNIOL-VILLARD 19942731b9a8SJean-Christophe PLAGNIOL-VILLARD /* De-allocate all resources.. */ 19952731b9a8SJean-Christophe PLAGNIOL-VILLARD 19962731b9a8SJean-Christophe PLAGNIOL-VILLARD static void hc_release_ohci(ohci_t *ohci) 19972731b9a8SJean-Christophe PLAGNIOL-VILLARD { 19982731b9a8SJean-Christophe PLAGNIOL-VILLARD dbg("USB HC release ohci usb-%s", ohci->slot_name); 19992731b9a8SJean-Christophe PLAGNIOL-VILLARD 20002731b9a8SJean-Christophe PLAGNIOL-VILLARD if (!ohci->disabled) 20012731b9a8SJean-Christophe PLAGNIOL-VILLARD hc_reset(ohci); 20022731b9a8SJean-Christophe PLAGNIOL-VILLARD } 20032731b9a8SJean-Christophe PLAGNIOL-VILLARD 20042731b9a8SJean-Christophe PLAGNIOL-VILLARD /*-------------------------------------------------------------------------*/ 20052731b9a8SJean-Christophe PLAGNIOL-VILLARD 20062731b9a8SJean-Christophe PLAGNIOL-VILLARD /* 20072731b9a8SJean-Christophe PLAGNIOL-VILLARD * low level initalisation routine, called from usb.c 20082731b9a8SJean-Christophe PLAGNIOL-VILLARD */ 20092731b9a8SJean-Christophe PLAGNIOL-VILLARD static char ohci_inited = 0; 20102731b9a8SJean-Christophe PLAGNIOL-VILLARD 201106d513ecSTroy Kisky int usb_lowlevel_init(int index, enum usb_init_type init, void **controller) 20122731b9a8SJean-Christophe PLAGNIOL-VILLARD { 20132731b9a8SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_PCI_OHCI 20142731b9a8SJean-Christophe PLAGNIOL-VILLARD pci_dev_t pdev; 20152731b9a8SJean-Christophe PLAGNIOL-VILLARD #endif 20162731b9a8SJean-Christophe PLAGNIOL-VILLARD 20172731b9a8SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_SYS_USB_OHCI_CPU_INIT 20182731b9a8SJean-Christophe PLAGNIOL-VILLARD /* cpu dependant init */ 20192731b9a8SJean-Christophe PLAGNIOL-VILLARD if (usb_cpu_init()) 20202731b9a8SJean-Christophe PLAGNIOL-VILLARD return -1; 20212731b9a8SJean-Christophe PLAGNIOL-VILLARD #endif 20222731b9a8SJean-Christophe PLAGNIOL-VILLARD 20232731b9a8SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_SYS_USB_OHCI_BOARD_INIT 20242731b9a8SJean-Christophe PLAGNIOL-VILLARD /* board dependant init */ 202516297cfbSMateusz Zalega if (board_usb_init(index, USB_INIT_HOST)) 20262731b9a8SJean-Christophe PLAGNIOL-VILLARD return -1; 20272731b9a8SJean-Christophe PLAGNIOL-VILLARD #endif 20282731b9a8SJean-Christophe PLAGNIOL-VILLARD memset(&gohci, 0, sizeof(ohci_t)); 20292731b9a8SJean-Christophe PLAGNIOL-VILLARD 20302731b9a8SJean-Christophe PLAGNIOL-VILLARD /* align the storage */ 20312731b9a8SJean-Christophe PLAGNIOL-VILLARD if ((__u32)&ghcca[0] & 0xff) { 20322731b9a8SJean-Christophe PLAGNIOL-VILLARD err("HCCA not aligned!!"); 20332731b9a8SJean-Christophe PLAGNIOL-VILLARD return -1; 20342731b9a8SJean-Christophe PLAGNIOL-VILLARD } 203526548bb2SHans de Goede gohci.hcca = &ghcca[0]; 203626548bb2SHans de Goede info("aligned ghcca %p", gohci.hcca); 203726548bb2SHans de Goede memset(gohci.hcca, 0, sizeof(struct ohci_hcca)); 20382731b9a8SJean-Christophe PLAGNIOL-VILLARD 20392731b9a8SJean-Christophe PLAGNIOL-VILLARD gohci.disabled = 1; 20402731b9a8SJean-Christophe PLAGNIOL-VILLARD gohci.sleeping = 0; 20412731b9a8SJean-Christophe PLAGNIOL-VILLARD gohci.irq = -1; 20422731b9a8SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_PCI_OHCI 20432731b9a8SJean-Christophe PLAGNIOL-VILLARD pdev = pci_find_devices(ohci_pci_ids, CONFIG_PCI_OHCI_DEVNO); 20442731b9a8SJean-Christophe PLAGNIOL-VILLARD 20452731b9a8SJean-Christophe PLAGNIOL-VILLARD if (pdev != -1) { 20462731b9a8SJean-Christophe PLAGNIOL-VILLARD u16 vid, did; 20472731b9a8SJean-Christophe PLAGNIOL-VILLARD u32 base; 20482731b9a8SJean-Christophe PLAGNIOL-VILLARD pci_read_config_word(pdev, PCI_VENDOR_ID, &vid); 20492731b9a8SJean-Christophe PLAGNIOL-VILLARD pci_read_config_word(pdev, PCI_DEVICE_ID, &did); 20502731b9a8SJean-Christophe PLAGNIOL-VILLARD printf("OHCI pci controller (%04x, %04x) found @(%d:%d:%d)\n", 20512731b9a8SJean-Christophe PLAGNIOL-VILLARD vid, did, (pdev >> 16) & 0xff, 20522731b9a8SJean-Christophe PLAGNIOL-VILLARD (pdev >> 11) & 0x1f, (pdev >> 8) & 0x7); 20532731b9a8SJean-Christophe PLAGNIOL-VILLARD pci_read_config_dword(pdev, PCI_BASE_ADDRESS_0, &base); 20542731b9a8SJean-Christophe PLAGNIOL-VILLARD printf("OHCI regs address 0x%08x\n", base); 20552731b9a8SJean-Christophe PLAGNIOL-VILLARD gohci.regs = (struct ohci_regs *)base; 20562731b9a8SJean-Christophe PLAGNIOL-VILLARD } else 20572731b9a8SJean-Christophe PLAGNIOL-VILLARD return -1; 20582731b9a8SJean-Christophe PLAGNIOL-VILLARD #else 20592731b9a8SJean-Christophe PLAGNIOL-VILLARD gohci.regs = (struct ohci_regs *)CONFIG_SYS_USB_OHCI_REGS_BASE; 20602731b9a8SJean-Christophe PLAGNIOL-VILLARD #endif 20612731b9a8SJean-Christophe PLAGNIOL-VILLARD 20622731b9a8SJean-Christophe PLAGNIOL-VILLARD gohci.flags = 0; 20632731b9a8SJean-Christophe PLAGNIOL-VILLARD gohci.slot_name = CONFIG_SYS_USB_OHCI_SLOT_NAME; 20642731b9a8SJean-Christophe PLAGNIOL-VILLARD 20652731b9a8SJean-Christophe PLAGNIOL-VILLARD if (hc_reset (&gohci) < 0) { 20662731b9a8SJean-Christophe PLAGNIOL-VILLARD hc_release_ohci (&gohci); 20672731b9a8SJean-Christophe PLAGNIOL-VILLARD err ("can't reset usb-%s", gohci.slot_name); 20682731b9a8SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_SYS_USB_OHCI_BOARD_INIT 20692731b9a8SJean-Christophe PLAGNIOL-VILLARD /* board dependant cleanup */ 207016297cfbSMateusz Zalega board_usb_cleanup(index, USB_INIT_HOST); 20712731b9a8SJean-Christophe PLAGNIOL-VILLARD #endif 20722731b9a8SJean-Christophe PLAGNIOL-VILLARD 20732731b9a8SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_SYS_USB_OHCI_CPU_INIT 20742731b9a8SJean-Christophe PLAGNIOL-VILLARD /* cpu dependant cleanup */ 20752731b9a8SJean-Christophe PLAGNIOL-VILLARD usb_cpu_init_fail(); 20762731b9a8SJean-Christophe PLAGNIOL-VILLARD #endif 20772731b9a8SJean-Christophe PLAGNIOL-VILLARD return -1; 20782731b9a8SJean-Christophe PLAGNIOL-VILLARD } 20792731b9a8SJean-Christophe PLAGNIOL-VILLARD 20802731b9a8SJean-Christophe PLAGNIOL-VILLARD if (hc_start(&gohci) < 0) { 20812731b9a8SJean-Christophe PLAGNIOL-VILLARD err("can't start usb-%s", gohci.slot_name); 20822731b9a8SJean-Christophe PLAGNIOL-VILLARD hc_release_ohci(&gohci); 20832731b9a8SJean-Christophe PLAGNIOL-VILLARD /* Initialization failed */ 20842731b9a8SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_SYS_USB_OHCI_BOARD_INIT 20852731b9a8SJean-Christophe PLAGNIOL-VILLARD /* board dependant cleanup */ 20862731b9a8SJean-Christophe PLAGNIOL-VILLARD usb_board_stop(); 20872731b9a8SJean-Christophe PLAGNIOL-VILLARD #endif 20882731b9a8SJean-Christophe PLAGNIOL-VILLARD 20892731b9a8SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_SYS_USB_OHCI_CPU_INIT 20902731b9a8SJean-Christophe PLAGNIOL-VILLARD /* cpu dependant cleanup */ 20912731b9a8SJean-Christophe PLAGNIOL-VILLARD usb_cpu_stop(); 20922731b9a8SJean-Christophe PLAGNIOL-VILLARD #endif 20932731b9a8SJean-Christophe PLAGNIOL-VILLARD return -1; 20942731b9a8SJean-Christophe PLAGNIOL-VILLARD } 20952731b9a8SJean-Christophe PLAGNIOL-VILLARD 20962731b9a8SJean-Christophe PLAGNIOL-VILLARD #ifdef DEBUG 20972731b9a8SJean-Christophe PLAGNIOL-VILLARD ohci_dump(&gohci, 1); 20982731b9a8SJean-Christophe PLAGNIOL-VILLARD #else 20998f761f02SHans de Goede ohci_mdelay(1); 21002731b9a8SJean-Christophe PLAGNIOL-VILLARD #endif 21012731b9a8SJean-Christophe PLAGNIOL-VILLARD ohci_inited = 1; 21022731b9a8SJean-Christophe PLAGNIOL-VILLARD return 0; 21032731b9a8SJean-Christophe PLAGNIOL-VILLARD } 21042731b9a8SJean-Christophe PLAGNIOL-VILLARD 2105c7e3b2b5SLucas Stach int usb_lowlevel_stop(int index) 21062731b9a8SJean-Christophe PLAGNIOL-VILLARD { 21072731b9a8SJean-Christophe PLAGNIOL-VILLARD /* this gets called really early - before the controller has */ 21082731b9a8SJean-Christophe PLAGNIOL-VILLARD /* even been initialized! */ 21092731b9a8SJean-Christophe PLAGNIOL-VILLARD if (!ohci_inited) 21102731b9a8SJean-Christophe PLAGNIOL-VILLARD return 0; 21112731b9a8SJean-Christophe PLAGNIOL-VILLARD /* TODO release any interrupts, etc. */ 21122731b9a8SJean-Christophe PLAGNIOL-VILLARD /* call hc_release_ohci() here ? */ 21132731b9a8SJean-Christophe PLAGNIOL-VILLARD hc_reset(&gohci); 21142731b9a8SJean-Christophe PLAGNIOL-VILLARD 21152731b9a8SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_SYS_USB_OHCI_BOARD_INIT 21162731b9a8SJean-Christophe PLAGNIOL-VILLARD /* board dependant cleanup */ 21172731b9a8SJean-Christophe PLAGNIOL-VILLARD if (usb_board_stop()) 21182731b9a8SJean-Christophe PLAGNIOL-VILLARD return -1; 21192731b9a8SJean-Christophe PLAGNIOL-VILLARD #endif 21202731b9a8SJean-Christophe PLAGNIOL-VILLARD 21212731b9a8SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_SYS_USB_OHCI_CPU_INIT 21222731b9a8SJean-Christophe PLAGNIOL-VILLARD /* cpu dependant cleanup */ 21232731b9a8SJean-Christophe PLAGNIOL-VILLARD if (usb_cpu_stop()) 21242731b9a8SJean-Christophe PLAGNIOL-VILLARD return -1; 21252731b9a8SJean-Christophe PLAGNIOL-VILLARD #endif 21262731b9a8SJean-Christophe PLAGNIOL-VILLARD /* This driver is no longer initialised. It needs a new low-level 21272731b9a8SJean-Christophe PLAGNIOL-VILLARD * init (board/cpu) before it can be used again. */ 21282731b9a8SJean-Christophe PLAGNIOL-VILLARD ohci_inited = 0; 21292731b9a8SJean-Christophe PLAGNIOL-VILLARD return 0; 21302731b9a8SJean-Christophe PLAGNIOL-VILLARD } 2131c5613df5SHans de Goede 2132c5613df5SHans de Goede int submit_control_msg(struct usb_device *dev, unsigned long pipe, 2133c5613df5SHans de Goede void *buffer, int transfer_len, struct devrequest *setup) 2134c5613df5SHans de Goede { 2135c5613df5SHans de Goede return _ohci_submit_control_msg(&gohci, dev, pipe, buffer, 2136c5613df5SHans de Goede transfer_len, setup); 2137c5613df5SHans de Goede } 213858b4048fSHans de Goede #endif 213958b4048fSHans de Goede 214058b4048fSHans de Goede #ifdef CONFIG_DM_USB 214158b4048fSHans de Goede static int ohci_submit_control_msg(struct udevice *dev, struct usb_device *udev, 214258b4048fSHans de Goede unsigned long pipe, void *buffer, int length, 214358b4048fSHans de Goede struct devrequest *setup) 214458b4048fSHans de Goede { 214558b4048fSHans de Goede ohci_t *ohci = dev_get_priv(usb_get_bus(dev)); 214658b4048fSHans de Goede 214758b4048fSHans de Goede return _ohci_submit_control_msg(ohci, udev, pipe, buffer, 214858b4048fSHans de Goede length, setup); 214958b4048fSHans de Goede } 215058b4048fSHans de Goede 215158b4048fSHans de Goede static int ohci_submit_bulk_msg(struct udevice *dev, struct usb_device *udev, 215258b4048fSHans de Goede unsigned long pipe, void *buffer, int length) 215358b4048fSHans de Goede { 215458b4048fSHans de Goede ohci_t *ohci = dev_get_priv(usb_get_bus(dev)); 215558b4048fSHans de Goede 215658b4048fSHans de Goede return submit_common_msg(ohci, udev, pipe, buffer, length, NULL, 0); 215758b4048fSHans de Goede } 215858b4048fSHans de Goede 215958b4048fSHans de Goede static int ohci_submit_int_msg(struct udevice *dev, struct usb_device *udev, 216058b4048fSHans de Goede unsigned long pipe, void *buffer, int length, 216158b4048fSHans de Goede int interval) 216258b4048fSHans de Goede { 216358b4048fSHans de Goede ohci_t *ohci = dev_get_priv(usb_get_bus(dev)); 216458b4048fSHans de Goede 216558b4048fSHans de Goede return submit_common_msg(ohci, udev, pipe, buffer, length, 216658b4048fSHans de Goede NULL, interval); 216758b4048fSHans de Goede } 216858b4048fSHans de Goede 2169bf495712SHans de Goede static struct int_queue *ohci_create_int_queue(struct udevice *dev, 2170bf495712SHans de Goede struct usb_device *udev, unsigned long pipe, int queuesize, 2171bf495712SHans de Goede int elementsize, void *buffer, int interval) 2172bf495712SHans de Goede { 2173bf495712SHans de Goede ohci_t *ohci = dev_get_priv(usb_get_bus(dev)); 2174bf495712SHans de Goede 2175bf495712SHans de Goede return _ohci_create_int_queue(ohci, udev, pipe, queuesize, elementsize, 2176bf495712SHans de Goede buffer, interval); 2177bf495712SHans de Goede } 2178bf495712SHans de Goede 2179bf495712SHans de Goede static void *ohci_poll_int_queue(struct udevice *dev, struct usb_device *udev, 2180bf495712SHans de Goede struct int_queue *queue) 2181bf495712SHans de Goede { 2182bf495712SHans de Goede ohci_t *ohci = dev_get_priv(usb_get_bus(dev)); 2183bf495712SHans de Goede 2184bf495712SHans de Goede return _ohci_poll_int_queue(ohci, udev, queue); 2185bf495712SHans de Goede } 2186bf495712SHans de Goede 2187bf495712SHans de Goede static int ohci_destroy_int_queue(struct udevice *dev, struct usb_device *udev, 2188bf495712SHans de Goede struct int_queue *queue) 2189bf495712SHans de Goede { 2190bf495712SHans de Goede ohci_t *ohci = dev_get_priv(usb_get_bus(dev)); 2191bf495712SHans de Goede 2192bf495712SHans de Goede return _ohci_destroy_int_queue(ohci, udev, queue); 2193bf495712SHans de Goede } 2194bf495712SHans de Goede 219558b4048fSHans de Goede int ohci_register(struct udevice *dev, struct ohci_regs *regs) 219658b4048fSHans de Goede { 219758b4048fSHans de Goede struct usb_bus_priv *priv = dev_get_uclass_priv(dev); 219858b4048fSHans de Goede ohci_t *ohci = dev_get_priv(dev); 219958b4048fSHans de Goede u32 reg; 220058b4048fSHans de Goede 220158b4048fSHans de Goede priv->desc_before_addr = true; 220258b4048fSHans de Goede 220358b4048fSHans de Goede ohci->regs = regs; 220458b4048fSHans de Goede ohci->hcca = memalign(256, sizeof(struct ohci_hcca)); 220558b4048fSHans de Goede if (!ohci->hcca) 220658b4048fSHans de Goede return -ENOMEM; 220758b4048fSHans de Goede memset(ohci->hcca, 0, sizeof(struct ohci_hcca)); 220858b4048fSHans de Goede 220958b4048fSHans de Goede if (hc_reset(ohci) < 0) 221058b4048fSHans de Goede return -EIO; 221158b4048fSHans de Goede 221258b4048fSHans de Goede if (hc_start(ohci) < 0) 221358b4048fSHans de Goede return -EIO; 221458b4048fSHans de Goede 221558b4048fSHans de Goede reg = ohci_readl(®s->revision); 221658b4048fSHans de Goede printf("USB OHCI %x.%x\n", (reg >> 4) & 0xf, reg & 0xf); 221758b4048fSHans de Goede 221858b4048fSHans de Goede return 0; 221958b4048fSHans de Goede } 222058b4048fSHans de Goede 222158b4048fSHans de Goede int ohci_deregister(struct udevice *dev) 222258b4048fSHans de Goede { 222358b4048fSHans de Goede ohci_t *ohci = dev_get_priv(dev); 222458b4048fSHans de Goede 222558b4048fSHans de Goede if (hc_reset(ohci) < 0) 222658b4048fSHans de Goede return -EIO; 222758b4048fSHans de Goede 222858b4048fSHans de Goede free(ohci->hcca); 222958b4048fSHans de Goede 223058b4048fSHans de Goede return 0; 223158b4048fSHans de Goede } 223258b4048fSHans de Goede 223358b4048fSHans de Goede struct dm_usb_ops ohci_usb_ops = { 223458b4048fSHans de Goede .control = ohci_submit_control_msg, 223558b4048fSHans de Goede .bulk = ohci_submit_bulk_msg, 223658b4048fSHans de Goede .interrupt = ohci_submit_int_msg, 2237bf495712SHans de Goede .create_int_queue = ohci_create_int_queue, 2238bf495712SHans de Goede .poll_int_queue = ohci_poll_int_queue, 2239bf495712SHans de Goede .destroy_int_queue = ohci_destroy_int_queue, 224058b4048fSHans de Goede }; 224158b4048fSHans de Goede 224258b4048fSHans de Goede #endif 2243