|
|
1.1 ! root 1: /* ! 2: * "Inventra" High-speed Dual-Role Controller (MUSB-HDRC), Mentor Graphics, ! 3: * USB2.0 OTG compliant core used in various chips. ! 4: * ! 5: * Copyright (C) 2008 Nokia Corporation ! 6: * Written by Andrzej Zaborowski <[email protected]> ! 7: * ! 8: * This program is free software; you can redistribute it and/or ! 9: * modify it under the terms of the GNU General Public License as ! 10: * published by the Free Software Foundation; either version 2 or ! 11: * (at your option) version 3 of the License. ! 12: * ! 13: * This program is distributed in the hope that it will be useful, ! 14: * but WITHOUT ANY WARRANTY; without even the implied warranty of ! 15: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ! 16: * GNU General Public License for more details. ! 17: * ! 18: * You should have received a copy of the GNU General Public License along ! 19: * with this program; if not, see <http://www.gnu.org/licenses/>. ! 20: * ! 21: * Only host-mode and non-DMA accesses are currently supported. ! 22: */ ! 23: #include "qemu-common.h" ! 24: #include "qemu-timer.h" ! 25: #include "hw/usb.h" ! 26: #include "hw/irq.h" ! 27: #include "hw/hw.h" ! 28: ! 29: /* Common USB registers */ ! 30: #define MUSB_HDRC_FADDR 0x00 /* 8-bit */ ! 31: #define MUSB_HDRC_POWER 0x01 /* 8-bit */ ! 32: ! 33: #define MUSB_HDRC_INTRTX 0x02 /* 16-bit */ ! 34: #define MUSB_HDRC_INTRRX 0x04 ! 35: #define MUSB_HDRC_INTRTXE 0x06 ! 36: #define MUSB_HDRC_INTRRXE 0x08 ! 37: #define MUSB_HDRC_INTRUSB 0x0a /* 8 bit */ ! 38: #define MUSB_HDRC_INTRUSBE 0x0b /* 8 bit */ ! 39: #define MUSB_HDRC_FRAME 0x0c /* 16-bit */ ! 40: #define MUSB_HDRC_INDEX 0x0e /* 8 bit */ ! 41: #define MUSB_HDRC_TESTMODE 0x0f /* 8 bit */ ! 42: ! 43: /* Per-EP registers in indexed mode */ ! 44: #define MUSB_HDRC_EP_IDX 0x10 /* 8-bit */ ! 45: ! 46: /* EP FIFOs */ ! 47: #define MUSB_HDRC_FIFO 0x20 ! 48: ! 49: /* Additional Control Registers */ ! 50: #define MUSB_HDRC_DEVCTL 0x60 /* 8 bit */ ! 51: ! 52: /* These are indexed */ ! 53: #define MUSB_HDRC_TXFIFOSZ 0x62 /* 8 bit (see masks) */ ! 54: #define MUSB_HDRC_RXFIFOSZ 0x63 /* 8 bit (see masks) */ ! 55: #define MUSB_HDRC_TXFIFOADDR 0x64 /* 16 bit offset shifted right 3 */ ! 56: #define MUSB_HDRC_RXFIFOADDR 0x66 /* 16 bit offset shifted right 3 */ ! 57: ! 58: /* Some more registers */ ! 59: #define MUSB_HDRC_VCTRL 0x68 /* 8 bit */ ! 60: #define MUSB_HDRC_HWVERS 0x6c /* 8 bit */ ! 61: ! 62: /* Added in HDRC 1.9(?) & MHDRC 1.4 */ ! 63: /* ULPI pass-through */ ! 64: #define MUSB_HDRC_ULPI_VBUSCTL 0x70 ! 65: #define MUSB_HDRC_ULPI_REGDATA 0x74 ! 66: #define MUSB_HDRC_ULPI_REGADDR 0x75 ! 67: #define MUSB_HDRC_ULPI_REGCTL 0x76 ! 68: ! 69: /* Extended config & PHY control */ ! 70: #define MUSB_HDRC_ENDCOUNT 0x78 /* 8 bit */ ! 71: #define MUSB_HDRC_DMARAMCFG 0x79 /* 8 bit */ ! 72: #define MUSB_HDRC_PHYWAIT 0x7a /* 8 bit */ ! 73: #define MUSB_HDRC_PHYVPLEN 0x7b /* 8 bit */ ! 74: #define MUSB_HDRC_HS_EOF1 0x7c /* 8 bit, units of 546.1 us */ ! 75: #define MUSB_HDRC_FS_EOF1 0x7d /* 8 bit, units of 533.3 ns */ ! 76: #define MUSB_HDRC_LS_EOF1 0x7e /* 8 bit, units of 1.067 us */ ! 77: ! 78: /* Per-EP BUSCTL registers */ ! 79: #define MUSB_HDRC_BUSCTL 0x80 ! 80: ! 81: /* Per-EP registers in flat mode */ ! 82: #define MUSB_HDRC_EP 0x100 ! 83: ! 84: /* offsets to registers in flat model */ ! 85: #define MUSB_HDRC_TXMAXP 0x00 /* 16 bit apparently */ ! 86: #define MUSB_HDRC_TXCSR 0x02 /* 16 bit apparently */ ! 87: #define MUSB_HDRC_CSR0 MUSB_HDRC_TXCSR /* re-used for EP0 */ ! 88: #define MUSB_HDRC_RXMAXP 0x04 /* 16 bit apparently */ ! 89: #define MUSB_HDRC_RXCSR 0x06 /* 16 bit apparently */ ! 90: #define MUSB_HDRC_RXCOUNT 0x08 /* 16 bit apparently */ ! 91: #define MUSB_HDRC_COUNT0 MUSB_HDRC_RXCOUNT /* re-used for EP0 */ ! 92: #define MUSB_HDRC_TXTYPE 0x0a /* 8 bit apparently */ ! 93: #define MUSB_HDRC_TYPE0 MUSB_HDRC_TXTYPE /* re-used for EP0 */ ! 94: #define MUSB_HDRC_TXINTERVAL 0x0b /* 8 bit apparently */ ! 95: #define MUSB_HDRC_NAKLIMIT0 MUSB_HDRC_TXINTERVAL /* re-used for EP0 */ ! 96: #define MUSB_HDRC_RXTYPE 0x0c /* 8 bit apparently */ ! 97: #define MUSB_HDRC_RXINTERVAL 0x0d /* 8 bit apparently */ ! 98: #define MUSB_HDRC_FIFOSIZE 0x0f /* 8 bit apparently */ ! 99: #define MUSB_HDRC_CONFIGDATA MGC_O_HDRC_FIFOSIZE /* re-used for EP0 */ ! 100: ! 101: /* "Bus control" registers */ ! 102: #define MUSB_HDRC_TXFUNCADDR 0x00 ! 103: #define MUSB_HDRC_TXHUBADDR 0x02 ! 104: #define MUSB_HDRC_TXHUBPORT 0x03 ! 105: ! 106: #define MUSB_HDRC_RXFUNCADDR 0x04 ! 107: #define MUSB_HDRC_RXHUBADDR 0x06 ! 108: #define MUSB_HDRC_RXHUBPORT 0x07 ! 109: ! 110: /* ! 111: * MUSBHDRC Register bit masks ! 112: */ ! 113: ! 114: /* POWER */ ! 115: #define MGC_M_POWER_ISOUPDATE 0x80 ! 116: #define MGC_M_POWER_SOFTCONN 0x40 ! 117: #define MGC_M_POWER_HSENAB 0x20 ! 118: #define MGC_M_POWER_HSMODE 0x10 ! 119: #define MGC_M_POWER_RESET 0x08 ! 120: #define MGC_M_POWER_RESUME 0x04 ! 121: #define MGC_M_POWER_SUSPENDM 0x02 ! 122: #define MGC_M_POWER_ENSUSPEND 0x01 ! 123: ! 124: /* INTRUSB */ ! 125: #define MGC_M_INTR_SUSPEND 0x01 ! 126: #define MGC_M_INTR_RESUME 0x02 ! 127: #define MGC_M_INTR_RESET 0x04 ! 128: #define MGC_M_INTR_BABBLE 0x04 ! 129: #define MGC_M_INTR_SOF 0x08 ! 130: #define MGC_M_INTR_CONNECT 0x10 ! 131: #define MGC_M_INTR_DISCONNECT 0x20 ! 132: #define MGC_M_INTR_SESSREQ 0x40 ! 133: #define MGC_M_INTR_VBUSERROR 0x80 /* FOR SESSION END */ ! 134: #define MGC_M_INTR_EP0 0x01 /* FOR EP0 INTERRUPT */ ! 135: ! 136: /* DEVCTL */ ! 137: #define MGC_M_DEVCTL_BDEVICE 0x80 ! 138: #define MGC_M_DEVCTL_FSDEV 0x40 ! 139: #define MGC_M_DEVCTL_LSDEV 0x20 ! 140: #define MGC_M_DEVCTL_VBUS 0x18 ! 141: #define MGC_S_DEVCTL_VBUS 3 ! 142: #define MGC_M_DEVCTL_HM 0x04 ! 143: #define MGC_M_DEVCTL_HR 0x02 ! 144: #define MGC_M_DEVCTL_SESSION 0x01 ! 145: ! 146: /* TESTMODE */ ! 147: #define MGC_M_TEST_FORCE_HOST 0x80 ! 148: #define MGC_M_TEST_FIFO_ACCESS 0x40 ! 149: #define MGC_M_TEST_FORCE_FS 0x20 ! 150: #define MGC_M_TEST_FORCE_HS 0x10 ! 151: #define MGC_M_TEST_PACKET 0x08 ! 152: #define MGC_M_TEST_K 0x04 ! 153: #define MGC_M_TEST_J 0x02 ! 154: #define MGC_M_TEST_SE0_NAK 0x01 ! 155: ! 156: /* CSR0 */ ! 157: #define MGC_M_CSR0_FLUSHFIFO 0x0100 ! 158: #define MGC_M_CSR0_TXPKTRDY 0x0002 ! 159: #define MGC_M_CSR0_RXPKTRDY 0x0001 ! 160: ! 161: /* CSR0 in Peripheral mode */ ! 162: #define MGC_M_CSR0_P_SVDSETUPEND 0x0080 ! 163: #define MGC_M_CSR0_P_SVDRXPKTRDY 0x0040 ! 164: #define MGC_M_CSR0_P_SENDSTALL 0x0020 ! 165: #define MGC_M_CSR0_P_SETUPEND 0x0010 ! 166: #define MGC_M_CSR0_P_DATAEND 0x0008 ! 167: #define MGC_M_CSR0_P_SENTSTALL 0x0004 ! 168: ! 169: /* CSR0 in Host mode */ ! 170: #define MGC_M_CSR0_H_NO_PING 0x0800 ! 171: #define MGC_M_CSR0_H_WR_DATATOGGLE 0x0400 /* set to allow setting: */ ! 172: #define MGC_M_CSR0_H_DATATOGGLE 0x0200 /* data toggle control */ ! 173: #define MGC_M_CSR0_H_NAKTIMEOUT 0x0080 ! 174: #define MGC_M_CSR0_H_STATUSPKT 0x0040 ! 175: #define MGC_M_CSR0_H_REQPKT 0x0020 ! 176: #define MGC_M_CSR0_H_ERROR 0x0010 ! 177: #define MGC_M_CSR0_H_SETUPPKT 0x0008 ! 178: #define MGC_M_CSR0_H_RXSTALL 0x0004 ! 179: ! 180: /* CONFIGDATA */ ! 181: #define MGC_M_CONFIGDATA_MPRXE 0x80 /* auto bulk pkt combining */ ! 182: #define MGC_M_CONFIGDATA_MPTXE 0x40 /* auto bulk pkt splitting */ ! 183: #define MGC_M_CONFIGDATA_BIGENDIAN 0x20 ! 184: #define MGC_M_CONFIGDATA_HBRXE 0x10 /* HB-ISO for RX */ ! 185: #define MGC_M_CONFIGDATA_HBTXE 0x08 /* HB-ISO for TX */ ! 186: #define MGC_M_CONFIGDATA_DYNFIFO 0x04 /* dynamic FIFO sizing */ ! 187: #define MGC_M_CONFIGDATA_SOFTCONE 0x02 /* SoftConnect */ ! 188: #define MGC_M_CONFIGDATA_UTMIDW 0x01 /* Width, 0 => 8b, 1 => 16b */ ! 189: ! 190: /* TXCSR in Peripheral and Host mode */ ! 191: #define MGC_M_TXCSR_AUTOSET 0x8000 ! 192: #define MGC_M_TXCSR_ISO 0x4000 ! 193: #define MGC_M_TXCSR_MODE 0x2000 ! 194: #define MGC_M_TXCSR_DMAENAB 0x1000 ! 195: #define MGC_M_TXCSR_FRCDATATOG 0x0800 ! 196: #define MGC_M_TXCSR_DMAMODE 0x0400 ! 197: #define MGC_M_TXCSR_CLRDATATOG 0x0040 ! 198: #define MGC_M_TXCSR_FLUSHFIFO 0x0008 ! 199: #define MGC_M_TXCSR_FIFONOTEMPTY 0x0002 ! 200: #define MGC_M_TXCSR_TXPKTRDY 0x0001 ! 201: ! 202: /* TXCSR in Peripheral mode */ ! 203: #define MGC_M_TXCSR_P_INCOMPTX 0x0080 ! 204: #define MGC_M_TXCSR_P_SENTSTALL 0x0020 ! 205: #define MGC_M_TXCSR_P_SENDSTALL 0x0010 ! 206: #define MGC_M_TXCSR_P_UNDERRUN 0x0004 ! 207: ! 208: /* TXCSR in Host mode */ ! 209: #define MGC_M_TXCSR_H_WR_DATATOGGLE 0x0200 ! 210: #define MGC_M_TXCSR_H_DATATOGGLE 0x0100 ! 211: #define MGC_M_TXCSR_H_NAKTIMEOUT 0x0080 ! 212: #define MGC_M_TXCSR_H_RXSTALL 0x0020 ! 213: #define MGC_M_TXCSR_H_ERROR 0x0004 ! 214: ! 215: /* RXCSR in Peripheral and Host mode */ ! 216: #define MGC_M_RXCSR_AUTOCLEAR 0x8000 ! 217: #define MGC_M_RXCSR_DMAENAB 0x2000 ! 218: #define MGC_M_RXCSR_DISNYET 0x1000 ! 219: #define MGC_M_RXCSR_DMAMODE 0x0800 ! 220: #define MGC_M_RXCSR_INCOMPRX 0x0100 ! 221: #define MGC_M_RXCSR_CLRDATATOG 0x0080 ! 222: #define MGC_M_RXCSR_FLUSHFIFO 0x0010 ! 223: #define MGC_M_RXCSR_DATAERROR 0x0008 ! 224: #define MGC_M_RXCSR_FIFOFULL 0x0002 ! 225: #define MGC_M_RXCSR_RXPKTRDY 0x0001 ! 226: ! 227: /* RXCSR in Peripheral mode */ ! 228: #define MGC_M_RXCSR_P_ISO 0x4000 ! 229: #define MGC_M_RXCSR_P_SENTSTALL 0x0040 ! 230: #define MGC_M_RXCSR_P_SENDSTALL 0x0020 ! 231: #define MGC_M_RXCSR_P_OVERRUN 0x0004 ! 232: ! 233: /* RXCSR in Host mode */ ! 234: #define MGC_M_RXCSR_H_AUTOREQ 0x4000 ! 235: #define MGC_M_RXCSR_H_WR_DATATOGGLE 0x0400 ! 236: #define MGC_M_RXCSR_H_DATATOGGLE 0x0200 ! 237: #define MGC_M_RXCSR_H_RXSTALL 0x0040 ! 238: #define MGC_M_RXCSR_H_REQPKT 0x0020 ! 239: #define MGC_M_RXCSR_H_ERROR 0x0004 ! 240: ! 241: /* HUBADDR */ ! 242: #define MGC_M_HUBADDR_MULTI_TT 0x80 ! 243: ! 244: /* ULPI: Added in HDRC 1.9(?) & MHDRC 1.4 */ ! 245: #define MGC_M_ULPI_VBCTL_USEEXTVBUSIND 0x02 ! 246: #define MGC_M_ULPI_VBCTL_USEEXTVBUS 0x01 ! 247: #define MGC_M_ULPI_REGCTL_INT_ENABLE 0x08 ! 248: #define MGC_M_ULPI_REGCTL_READNOTWRITE 0x04 ! 249: #define MGC_M_ULPI_REGCTL_COMPLETE 0x02 ! 250: #define MGC_M_ULPI_REGCTL_REG 0x01 ! 251: ! 252: /* #define MUSB_DEBUG */ ! 253: ! 254: #ifdef MUSB_DEBUG ! 255: #define TRACE(fmt,...) fprintf(stderr, "%s@%d: " fmt "\n", __FUNCTION__, \ ! 256: __LINE__, ##__VA_ARGS__) ! 257: #else ! 258: #define TRACE(...) ! 259: #endif ! 260: ! 261: ! 262: static void musb_attach(USBPort *port); ! 263: static void musb_detach(USBPort *port); ! 264: static void musb_child_detach(USBPort *port, USBDevice *child); ! 265: static void musb_schedule_cb(USBPort *port, USBPacket *p); ! 266: static void musb_async_cancel_device(MUSBState *s, USBDevice *dev); ! 267: ! 268: static USBPortOps musb_port_ops = { ! 269: .attach = musb_attach, ! 270: .detach = musb_detach, ! 271: .child_detach = musb_child_detach, ! 272: .complete = musb_schedule_cb, ! 273: }; ! 274: ! 275: static USBBusOps musb_bus_ops = { ! 276: }; ! 277: ! 278: typedef struct MUSBPacket MUSBPacket; ! 279: typedef struct MUSBEndPoint MUSBEndPoint; ! 280: ! 281: struct MUSBPacket { ! 282: USBPacket p; ! 283: MUSBEndPoint *ep; ! 284: int dir; ! 285: }; ! 286: ! 287: struct MUSBEndPoint { ! 288: uint16_t faddr[2]; ! 289: uint8_t haddr[2]; ! 290: uint8_t hport[2]; ! 291: uint16_t csr[2]; ! 292: uint16_t maxp[2]; ! 293: uint16_t rxcount; ! 294: uint8_t type[2]; ! 295: uint8_t interval[2]; ! 296: uint8_t config; ! 297: uint8_t fifosize; ! 298: int timeout[2]; /* Always in microframes */ ! 299: ! 300: uint8_t *buf[2]; ! 301: int fifolen[2]; ! 302: int fifostart[2]; ! 303: int fifoaddr[2]; ! 304: MUSBPacket packey[2]; ! 305: int status[2]; ! 306: int ext_size[2]; ! 307: ! 308: /* For callbacks' use */ ! 309: int epnum; ! 310: int interrupt[2]; ! 311: MUSBState *musb; ! 312: USBCallback *delayed_cb[2]; ! 313: QEMUTimer *intv_timer[2]; ! 314: }; ! 315: ! 316: struct MUSBState { ! 317: qemu_irq irqs[musb_irq_max]; ! 318: USBBus bus; ! 319: USBPort port; ! 320: ! 321: int idx; ! 322: uint8_t devctl; ! 323: uint8_t power; ! 324: uint8_t faddr; ! 325: ! 326: uint8_t intr; ! 327: uint8_t mask; ! 328: uint16_t tx_intr; ! 329: uint16_t tx_mask; ! 330: uint16_t rx_intr; ! 331: uint16_t rx_mask; ! 332: ! 333: int setup_len; ! 334: int session; ! 335: ! 336: uint8_t buf[0x8000]; ! 337: ! 338: /* Duplicating the world since 2008!... probably we should have 32 ! 339: * logical, single endpoints instead. */ ! 340: MUSBEndPoint ep[16]; ! 341: }; ! 342: ! 343: void musb_reset(MUSBState *s) ! 344: { ! 345: int i; ! 346: ! 347: s->faddr = 0x00; ! 348: s->devctl = 0; ! 349: s->power = MGC_M_POWER_HSENAB; ! 350: s->tx_intr = 0x0000; ! 351: s->rx_intr = 0x0000; ! 352: s->tx_mask = 0xffff; ! 353: s->rx_mask = 0xffff; ! 354: s->intr = 0x00; ! 355: s->mask = 0x06; ! 356: s->idx = 0; ! 357: ! 358: s->setup_len = 0; ! 359: s->session = 0; ! 360: memset(s->buf, 0, sizeof(s->buf)); ! 361: ! 362: /* TODO: _DW */ ! 363: s->ep[0].config = MGC_M_CONFIGDATA_SOFTCONE | MGC_M_CONFIGDATA_DYNFIFO; ! 364: for (i = 0; i < 16; i ++) { ! 365: s->ep[i].fifosize = 64; ! 366: s->ep[i].maxp[0] = 0x40; ! 367: s->ep[i].maxp[1] = 0x40; ! 368: s->ep[i].musb = s; ! 369: s->ep[i].epnum = i; ! 370: usb_packet_init(&s->ep[i].packey[0].p); ! 371: usb_packet_init(&s->ep[i].packey[1].p); ! 372: } ! 373: } ! 374: ! 375: struct MUSBState *musb_init(DeviceState *parent_device, int gpio_base) ! 376: { ! 377: MUSBState *s = g_malloc0(sizeof(*s)); ! 378: int i; ! 379: ! 380: for (i = 0; i < musb_irq_max; i++) { ! 381: s->irqs[i] = qdev_get_gpio_in(parent_device, gpio_base + i); ! 382: } ! 383: ! 384: musb_reset(s); ! 385: ! 386: usb_bus_new(&s->bus, &musb_bus_ops, parent_device); ! 387: usb_register_port(&s->bus, &s->port, s, 0, &musb_port_ops, ! 388: USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL); ! 389: ! 390: return s; ! 391: } ! 392: ! 393: static void musb_vbus_set(MUSBState *s, int level) ! 394: { ! 395: if (level) ! 396: s->devctl |= 3 << MGC_S_DEVCTL_VBUS; ! 397: else ! 398: s->devctl &= ~MGC_M_DEVCTL_VBUS; ! 399: ! 400: qemu_set_irq(s->irqs[musb_set_vbus], level); ! 401: } ! 402: ! 403: static void musb_intr_set(MUSBState *s, int line, int level) ! 404: { ! 405: if (!level) { ! 406: s->intr &= ~(1 << line); ! 407: qemu_irq_lower(s->irqs[line]); ! 408: } else if (s->mask & (1 << line)) { ! 409: s->intr |= 1 << line; ! 410: qemu_irq_raise(s->irqs[line]); ! 411: } ! 412: } ! 413: ! 414: static void musb_tx_intr_set(MUSBState *s, int line, int level) ! 415: { ! 416: if (!level) { ! 417: s->tx_intr &= ~(1 << line); ! 418: if (!s->tx_intr) ! 419: qemu_irq_lower(s->irqs[musb_irq_tx]); ! 420: } else if (s->tx_mask & (1 << line)) { ! 421: s->tx_intr |= 1 << line; ! 422: qemu_irq_raise(s->irqs[musb_irq_tx]); ! 423: } ! 424: } ! 425: ! 426: static void musb_rx_intr_set(MUSBState *s, int line, int level) ! 427: { ! 428: if (line) { ! 429: if (!level) { ! 430: s->rx_intr &= ~(1 << line); ! 431: if (!s->rx_intr) ! 432: qemu_irq_lower(s->irqs[musb_irq_rx]); ! 433: } else if (s->rx_mask & (1 << line)) { ! 434: s->rx_intr |= 1 << line; ! 435: qemu_irq_raise(s->irqs[musb_irq_rx]); ! 436: } ! 437: } else ! 438: musb_tx_intr_set(s, line, level); ! 439: } ! 440: ! 441: uint32_t musb_core_intr_get(MUSBState *s) ! 442: { ! 443: return (s->rx_intr << 15) | s->tx_intr; ! 444: } ! 445: ! 446: void musb_core_intr_clear(MUSBState *s, uint32_t mask) ! 447: { ! 448: if (s->rx_intr) { ! 449: s->rx_intr &= mask >> 15; ! 450: if (!s->rx_intr) ! 451: qemu_irq_lower(s->irqs[musb_irq_rx]); ! 452: } ! 453: ! 454: if (s->tx_intr) { ! 455: s->tx_intr &= mask & 0xffff; ! 456: if (!s->tx_intr) ! 457: qemu_irq_lower(s->irqs[musb_irq_tx]); ! 458: } ! 459: } ! 460: ! 461: void musb_set_size(MUSBState *s, int epnum, int size, int is_tx) ! 462: { ! 463: s->ep[epnum].ext_size[!is_tx] = size; ! 464: s->ep[epnum].fifostart[0] = 0; ! 465: s->ep[epnum].fifostart[1] = 0; ! 466: s->ep[epnum].fifolen[0] = 0; ! 467: s->ep[epnum].fifolen[1] = 0; ! 468: } ! 469: ! 470: static void musb_session_update(MUSBState *s, int prev_dev, int prev_sess) ! 471: { ! 472: int detect_prev = prev_dev && prev_sess; ! 473: int detect = !!s->port.dev && s->session; ! 474: ! 475: if (detect && !detect_prev) { ! 476: /* Let's skip the ID pin sense and VBUS sense formalities and ! 477: * and signal a successful SRP directly. This should work at least ! 478: * for the Linux driver stack. */ ! 479: musb_intr_set(s, musb_irq_connect, 1); ! 480: ! 481: if (s->port.dev->speed == USB_SPEED_LOW) { ! 482: s->devctl &= ~MGC_M_DEVCTL_FSDEV; ! 483: s->devctl |= MGC_M_DEVCTL_LSDEV; ! 484: } else { ! 485: s->devctl |= MGC_M_DEVCTL_FSDEV; ! 486: s->devctl &= ~MGC_M_DEVCTL_LSDEV; ! 487: } ! 488: ! 489: /* A-mode? */ ! 490: s->devctl &= ~MGC_M_DEVCTL_BDEVICE; ! 491: ! 492: /* Host-mode bit? */ ! 493: s->devctl |= MGC_M_DEVCTL_HM; ! 494: #if 1 ! 495: musb_vbus_set(s, 1); ! 496: #endif ! 497: } else if (!detect && detect_prev) { ! 498: #if 1 ! 499: musb_vbus_set(s, 0); ! 500: #endif ! 501: } ! 502: } ! 503: ! 504: /* Attach or detach a device on our only port. */ ! 505: static void musb_attach(USBPort *port) ! 506: { ! 507: MUSBState *s = (MUSBState *) port->opaque; ! 508: ! 509: musb_intr_set(s, musb_irq_vbus_request, 1); ! 510: musb_session_update(s, 0, s->session); ! 511: } ! 512: ! 513: static void musb_detach(USBPort *port) ! 514: { ! 515: MUSBState *s = (MUSBState *) port->opaque; ! 516: ! 517: musb_async_cancel_device(s, port->dev); ! 518: ! 519: musb_intr_set(s, musb_irq_disconnect, 1); ! 520: musb_session_update(s, 1, s->session); ! 521: } ! 522: ! 523: static void musb_child_detach(USBPort *port, USBDevice *child) ! 524: { ! 525: MUSBState *s = (MUSBState *) port->opaque; ! 526: ! 527: musb_async_cancel_device(s, child); ! 528: } ! 529: ! 530: static void musb_cb_tick0(void *opaque) ! 531: { ! 532: MUSBEndPoint *ep = (MUSBEndPoint *) opaque; ! 533: ! 534: ep->delayed_cb[0](&ep->packey[0].p, opaque); ! 535: } ! 536: ! 537: static void musb_cb_tick1(void *opaque) ! 538: { ! 539: MUSBEndPoint *ep = (MUSBEndPoint *) opaque; ! 540: ! 541: ep->delayed_cb[1](&ep->packey[1].p, opaque); ! 542: } ! 543: ! 544: #define musb_cb_tick (dir ? musb_cb_tick1 : musb_cb_tick0) ! 545: ! 546: static void musb_schedule_cb(USBPort *port, USBPacket *packey) ! 547: { ! 548: MUSBPacket *p = container_of(packey, MUSBPacket, p); ! 549: MUSBEndPoint *ep = p->ep; ! 550: int dir = p->dir; ! 551: int timeout = 0; ! 552: ! 553: if (ep->status[dir] == USB_RET_NAK) ! 554: timeout = ep->timeout[dir]; ! 555: else if (ep->interrupt[dir]) ! 556: timeout = 8; ! 557: else ! 558: return musb_cb_tick(ep); ! 559: ! 560: if (!ep->intv_timer[dir]) ! 561: ep->intv_timer[dir] = qemu_new_timer_ns(vm_clock, musb_cb_tick, ep); ! 562: ! 563: qemu_mod_timer(ep->intv_timer[dir], qemu_get_clock_ns(vm_clock) + ! 564: muldiv64(timeout, get_ticks_per_sec(), 8000)); ! 565: } ! 566: ! 567: static int musb_timeout(int ttype, int speed, int val) ! 568: { ! 569: #if 1 ! 570: return val << 3; ! 571: #endif ! 572: ! 573: switch (ttype) { ! 574: case USB_ENDPOINT_XFER_CONTROL: ! 575: if (val < 2) ! 576: return 0; ! 577: else if (speed == USB_SPEED_HIGH) ! 578: return 1 << (val - 1); ! 579: else ! 580: return 8 << (val - 1); ! 581: ! 582: case USB_ENDPOINT_XFER_INT: ! 583: if (speed == USB_SPEED_HIGH) ! 584: if (val < 2) ! 585: return 0; ! 586: else ! 587: return 1 << (val - 1); ! 588: else ! 589: return val << 3; ! 590: ! 591: case USB_ENDPOINT_XFER_BULK: ! 592: case USB_ENDPOINT_XFER_ISOC: ! 593: if (val < 2) ! 594: return 0; ! 595: else if (speed == USB_SPEED_HIGH) ! 596: return 1 << (val - 1); ! 597: else ! 598: return 8 << (val - 1); ! 599: /* TODO: what with low-speed Bulk and Isochronous? */ ! 600: } ! 601: ! 602: hw_error("bad interval\n"); ! 603: } ! 604: ! 605: static void musb_packet(MUSBState *s, MUSBEndPoint *ep, ! 606: int epnum, int pid, int len, USBCallback cb, int dir) ! 607: { ! 608: USBDevice *dev; ! 609: USBEndpoint *uep; ! 610: int ret; ! 611: int idx = epnum && dir; ! 612: int ttype; ! 613: ! 614: /* ep->type[0,1] contains: ! 615: * in bits 7:6 the speed (0 - invalid, 1 - high, 2 - full, 3 - slow) ! 616: * in bits 5:4 the transfer type (BULK / INT) ! 617: * in bits 3:0 the EP num ! 618: */ ! 619: ttype = epnum ? (ep->type[idx] >> 4) & 3 : 0; ! 620: ! 621: ep->timeout[dir] = musb_timeout(ttype, ! 622: ep->type[idx] >> 6, ep->interval[idx]); ! 623: ep->interrupt[dir] = ttype == USB_ENDPOINT_XFER_INT; ! 624: ep->delayed_cb[dir] = cb; ! 625: ! 626: /* A wild guess on the FADDR semantics... */ ! 627: dev = usb_find_device(&s->port, ep->faddr[idx]); ! 628: uep = usb_ep_get(dev, pid, ep->type[idx] & 0xf); ! 629: usb_packet_setup(&ep->packey[dir].p, pid, uep); ! 630: usb_packet_addbuf(&ep->packey[dir].p, ep->buf[idx], len); ! 631: ep->packey[dir].ep = ep; ! 632: ep->packey[dir].dir = dir; ! 633: ! 634: ret = usb_handle_packet(dev, &ep->packey[dir].p); ! 635: ! 636: if (ret == USB_RET_ASYNC) { ! 637: ep->status[dir] = len; ! 638: return; ! 639: } ! 640: ! 641: ep->status[dir] = ret; ! 642: musb_schedule_cb(&s->port, &ep->packey[dir].p); ! 643: } ! 644: ! 645: static void musb_tx_packet_complete(USBPacket *packey, void *opaque) ! 646: { ! 647: /* Unfortunately we can't use packey->devep because that's the remote ! 648: * endpoint number and may be different than our local. */ ! 649: MUSBEndPoint *ep = (MUSBEndPoint *) opaque; ! 650: int epnum = ep->epnum; ! 651: MUSBState *s = ep->musb; ! 652: ! 653: ep->fifostart[0] = 0; ! 654: ep->fifolen[0] = 0; ! 655: #ifdef CLEAR_NAK ! 656: if (ep->status[0] != USB_RET_NAK) { ! 657: #endif ! 658: if (epnum) ! 659: ep->csr[0] &= ~(MGC_M_TXCSR_FIFONOTEMPTY | MGC_M_TXCSR_TXPKTRDY); ! 660: else ! 661: ep->csr[0] &= ~MGC_M_CSR0_TXPKTRDY; ! 662: #ifdef CLEAR_NAK ! 663: } ! 664: #endif ! 665: ! 666: /* Clear all of the error bits first */ ! 667: if (epnum) ! 668: ep->csr[0] &= ~(MGC_M_TXCSR_H_ERROR | MGC_M_TXCSR_H_RXSTALL | ! 669: MGC_M_TXCSR_H_NAKTIMEOUT); ! 670: else ! 671: ep->csr[0] &= ~(MGC_M_CSR0_H_ERROR | MGC_M_CSR0_H_RXSTALL | ! 672: MGC_M_CSR0_H_NAKTIMEOUT | MGC_M_CSR0_H_NO_PING); ! 673: ! 674: if (ep->status[0] == USB_RET_STALL) { ! 675: /* Command not supported by target! */ ! 676: ep->status[0] = 0; ! 677: ! 678: if (epnum) ! 679: ep->csr[0] |= MGC_M_TXCSR_H_RXSTALL; ! 680: else ! 681: ep->csr[0] |= MGC_M_CSR0_H_RXSTALL; ! 682: } ! 683: ! 684: if (ep->status[0] == USB_RET_NAK) { ! 685: ep->status[0] = 0; ! 686: ! 687: /* NAK timeouts are only generated in Bulk transfers and ! 688: * Data-errors in Isochronous. */ ! 689: if (ep->interrupt[0]) { ! 690: return; ! 691: } ! 692: ! 693: if (epnum) ! 694: ep->csr[0] |= MGC_M_TXCSR_H_NAKTIMEOUT; ! 695: else ! 696: ep->csr[0] |= MGC_M_CSR0_H_NAKTIMEOUT; ! 697: } ! 698: ! 699: if (ep->status[0] < 0) { ! 700: if (ep->status[0] == USB_RET_BABBLE) ! 701: musb_intr_set(s, musb_irq_rst_babble, 1); ! 702: ! 703: /* Pretend we've tried three times already and failed (in ! 704: * case of USB_TOKEN_SETUP). */ ! 705: if (epnum) ! 706: ep->csr[0] |= MGC_M_TXCSR_H_ERROR; ! 707: else ! 708: ep->csr[0] |= MGC_M_CSR0_H_ERROR; ! 709: ! 710: musb_tx_intr_set(s, epnum, 1); ! 711: return; ! 712: } ! 713: /* TODO: check len for over/underruns of an OUT packet? */ ! 714: ! 715: #ifdef SETUPLEN_HACK ! 716: if (!epnum && ep->packey[0].pid == USB_TOKEN_SETUP) ! 717: s->setup_len = ep->packey[0].data[6]; ! 718: #endif ! 719: ! 720: /* In DMA mode: if no error, assert DMA request for this EP, ! 721: * and skip the interrupt. */ ! 722: musb_tx_intr_set(s, epnum, 1); ! 723: } ! 724: ! 725: static void musb_rx_packet_complete(USBPacket *packey, void *opaque) ! 726: { ! 727: /* Unfortunately we can't use packey->devep because that's the remote ! 728: * endpoint number and may be different than our local. */ ! 729: MUSBEndPoint *ep = (MUSBEndPoint *) opaque; ! 730: int epnum = ep->epnum; ! 731: MUSBState *s = ep->musb; ! 732: ! 733: ep->fifostart[1] = 0; ! 734: ep->fifolen[1] = 0; ! 735: ! 736: #ifdef CLEAR_NAK ! 737: if (ep->status[1] != USB_RET_NAK) { ! 738: #endif ! 739: ep->csr[1] &= ~MGC_M_RXCSR_H_REQPKT; ! 740: if (!epnum) ! 741: ep->csr[0] &= ~MGC_M_CSR0_H_REQPKT; ! 742: #ifdef CLEAR_NAK ! 743: } ! 744: #endif ! 745: ! 746: /* Clear all of the imaginable error bits first */ ! 747: ep->csr[1] &= ~(MGC_M_RXCSR_H_ERROR | MGC_M_RXCSR_H_RXSTALL | ! 748: MGC_M_RXCSR_DATAERROR); ! 749: if (!epnum) ! 750: ep->csr[0] &= ~(MGC_M_CSR0_H_ERROR | MGC_M_CSR0_H_RXSTALL | ! 751: MGC_M_CSR0_H_NAKTIMEOUT | MGC_M_CSR0_H_NO_PING); ! 752: ! 753: if (ep->status[1] == USB_RET_STALL) { ! 754: ep->status[1] = 0; ! 755: packey->result = 0; ! 756: ! 757: ep->csr[1] |= MGC_M_RXCSR_H_RXSTALL; ! 758: if (!epnum) ! 759: ep->csr[0] |= MGC_M_CSR0_H_RXSTALL; ! 760: } ! 761: ! 762: if (ep->status[1] == USB_RET_NAK) { ! 763: ep->status[1] = 0; ! 764: ! 765: /* NAK timeouts are only generated in Bulk transfers and ! 766: * Data-errors in Isochronous. */ ! 767: if (ep->interrupt[1]) ! 768: return musb_packet(s, ep, epnum, USB_TOKEN_IN, ! 769: packey->iov.size, musb_rx_packet_complete, 1); ! 770: ! 771: ep->csr[1] |= MGC_M_RXCSR_DATAERROR; ! 772: if (!epnum) ! 773: ep->csr[0] |= MGC_M_CSR0_H_NAKTIMEOUT; ! 774: } ! 775: ! 776: if (ep->status[1] < 0) { ! 777: if (ep->status[1] == USB_RET_BABBLE) { ! 778: musb_intr_set(s, musb_irq_rst_babble, 1); ! 779: return; ! 780: } ! 781: ! 782: /* Pretend we've tried three times already and failed (in ! 783: * case of a control transfer). */ ! 784: ep->csr[1] |= MGC_M_RXCSR_H_ERROR; ! 785: if (!epnum) ! 786: ep->csr[0] |= MGC_M_CSR0_H_ERROR; ! 787: ! 788: musb_rx_intr_set(s, epnum, 1); ! 789: return; ! 790: } ! 791: /* TODO: check len for over/underruns of an OUT packet? */ ! 792: /* TODO: perhaps make use of e->ext_size[1] here. */ ! 793: ! 794: packey->result = ep->status[1]; ! 795: ! 796: if (!(ep->csr[1] & (MGC_M_RXCSR_H_RXSTALL | MGC_M_RXCSR_DATAERROR))) { ! 797: ep->csr[1] |= MGC_M_RXCSR_FIFOFULL | MGC_M_RXCSR_RXPKTRDY; ! 798: if (!epnum) ! 799: ep->csr[0] |= MGC_M_CSR0_RXPKTRDY; ! 800: ! 801: ep->rxcount = packey->result; /* XXX: MIN(packey->len, ep->maxp[1]); */ ! 802: /* In DMA mode: assert DMA request for this EP */ ! 803: } ! 804: ! 805: /* Only if DMA has not been asserted */ ! 806: musb_rx_intr_set(s, epnum, 1); ! 807: } ! 808: ! 809: static void musb_async_cancel_device(MUSBState *s, USBDevice *dev) ! 810: { ! 811: int ep, dir; ! 812: ! 813: for (ep = 0; ep < 16; ep++) { ! 814: for (dir = 0; dir < 2; dir++) { ! 815: if (!usb_packet_is_inflight(&s->ep[ep].packey[dir].p) || ! 816: s->ep[ep].packey[dir].p.ep->dev != dev) { ! 817: continue; ! 818: } ! 819: usb_cancel_packet(&s->ep[ep].packey[dir].p); ! 820: /* status updates needed here? */ ! 821: } ! 822: } ! 823: } ! 824: ! 825: static void musb_tx_rdy(MUSBState *s, int epnum) ! 826: { ! 827: MUSBEndPoint *ep = s->ep + epnum; ! 828: int pid; ! 829: int total, valid = 0; ! 830: TRACE("start %d, len %d", ep->fifostart[0], ep->fifolen[0] ); ! 831: ep->fifostart[0] += ep->fifolen[0]; ! 832: ep->fifolen[0] = 0; ! 833: ! 834: /* XXX: how's the total size of the packet retrieved exactly in ! 835: * the generic case? */ ! 836: total = ep->maxp[0] & 0x3ff; ! 837: ! 838: if (ep->ext_size[0]) { ! 839: total = ep->ext_size[0]; ! 840: ep->ext_size[0] = 0; ! 841: valid = 1; ! 842: } ! 843: ! 844: /* If the packet is not fully ready yet, wait for a next segment. */ ! 845: if (epnum && (ep->fifostart[0]) < total) ! 846: return; ! 847: ! 848: if (!valid) ! 849: total = ep->fifostart[0]; ! 850: ! 851: pid = USB_TOKEN_OUT; ! 852: if (!epnum && (ep->csr[0] & MGC_M_CSR0_H_SETUPPKT)) { ! 853: pid = USB_TOKEN_SETUP; ! 854: if (total != 8) { ! 855: TRACE("illegal SETUPPKT length of %i bytes", total); ! 856: } ! 857: /* Controller should retry SETUP packets three times on errors ! 858: * but it doesn't make sense for us to do that. */ ! 859: } ! 860: ! 861: return musb_packet(s, ep, epnum, pid, ! 862: total, musb_tx_packet_complete, 0); ! 863: } ! 864: ! 865: static void musb_rx_req(MUSBState *s, int epnum) ! 866: { ! 867: MUSBEndPoint *ep = s->ep + epnum; ! 868: int total; ! 869: ! 870: /* If we already have a packet, which didn't fit into the ! 871: * 64 bytes of the FIFO, only move the FIFO start and return. (Obsolete) */ ! 872: if (ep->packey[1].p.pid == USB_TOKEN_IN && ep->status[1] >= 0 && ! 873: (ep->fifostart[1]) + ep->rxcount < ! 874: ep->packey[1].p.iov.size) { ! 875: TRACE("0x%08x, %d", ep->fifostart[1], ep->rxcount ); ! 876: ep->fifostart[1] += ep->rxcount; ! 877: ep->fifolen[1] = 0; ! 878: ! 879: ep->rxcount = MIN(ep->packey[0].p.iov.size - (ep->fifostart[1]), ! 880: ep->maxp[1]); ! 881: ! 882: ep->csr[1] &= ~MGC_M_RXCSR_H_REQPKT; ! 883: if (!epnum) ! 884: ep->csr[0] &= ~MGC_M_CSR0_H_REQPKT; ! 885: ! 886: /* Clear all of the error bits first */ ! 887: ep->csr[1] &= ~(MGC_M_RXCSR_H_ERROR | MGC_M_RXCSR_H_RXSTALL | ! 888: MGC_M_RXCSR_DATAERROR); ! 889: if (!epnum) ! 890: ep->csr[0] &= ~(MGC_M_CSR0_H_ERROR | MGC_M_CSR0_H_RXSTALL | ! 891: MGC_M_CSR0_H_NAKTIMEOUT | MGC_M_CSR0_H_NO_PING); ! 892: ! 893: ep->csr[1] |= MGC_M_RXCSR_FIFOFULL | MGC_M_RXCSR_RXPKTRDY; ! 894: if (!epnum) ! 895: ep->csr[0] |= MGC_M_CSR0_RXPKTRDY; ! 896: musb_rx_intr_set(s, epnum, 1); ! 897: return; ! 898: } ! 899: ! 900: /* The driver sets maxp[1] to 64 or less because it knows the hardware ! 901: * FIFO is this deep. Bigger packets get split in ! 902: * usb_generic_handle_packet but we can also do the splitting locally ! 903: * for performance. It turns out we can also have a bigger FIFO and ! 904: * ignore the limit set in ep->maxp[1]. The Linux MUSB driver deals ! 905: * OK with single packets of even 32KB and we avoid splitting, however ! 906: * usb_msd.c sometimes sends a packet bigger than what Linux expects ! 907: * (e.g. 8192 bytes instead of 4096) and we get an OVERRUN. Splitting ! 908: * hides this overrun from Linux. Up to 4096 everything is fine ! 909: * though. Currently this is disabled. ! 910: * ! 911: * XXX: mind ep->fifosize. */ ! 912: total = MIN(ep->maxp[1] & 0x3ff, sizeof(s->buf)); ! 913: ! 914: #ifdef SETUPLEN_HACK ! 915: /* Why should *we* do that instead of Linux? */ ! 916: if (!epnum) { ! 917: if (ep->packey[0].p.devaddr == 2) { ! 918: total = MIN(s->setup_len, 8); ! 919: } else { ! 920: total = MIN(s->setup_len, 64); ! 921: } ! 922: s->setup_len -= total; ! 923: } ! 924: #endif ! 925: ! 926: return musb_packet(s, ep, epnum, USB_TOKEN_IN, ! 927: total, musb_rx_packet_complete, 1); ! 928: } ! 929: ! 930: static uint8_t musb_read_fifo(MUSBEndPoint *ep) ! 931: { ! 932: uint8_t value; ! 933: if (ep->fifolen[1] >= 64) { ! 934: /* We have a FIFO underrun */ ! 935: TRACE("EP%d FIFO is now empty, stop reading", ep->epnum); ! 936: return 0x00000000; ! 937: } ! 938: /* In DMA mode clear RXPKTRDY and set REQPKT automatically ! 939: * (if AUTOREQ is set) */ ! 940: ! 941: ep->csr[1] &= ~MGC_M_RXCSR_FIFOFULL; ! 942: value=ep->buf[1][ep->fifostart[1] + ep->fifolen[1] ++]; ! 943: TRACE("EP%d 0x%02x, %d", ep->epnum, value, ep->fifolen[1] ); ! 944: return value; ! 945: } ! 946: ! 947: static void musb_write_fifo(MUSBEndPoint *ep, uint8_t value) ! 948: { ! 949: TRACE("EP%d = %02x", ep->epnum, value); ! 950: if (ep->fifolen[0] >= 64) { ! 951: /* We have a FIFO overrun */ ! 952: TRACE("EP%d FIFO exceeded 64 bytes, stop feeding data", ep->epnum); ! 953: return; ! 954: } ! 955: ! 956: ep->buf[0][ep->fifostart[0] + ep->fifolen[0] ++] = value; ! 957: ep->csr[0] |= MGC_M_TXCSR_FIFONOTEMPTY; ! 958: } ! 959: ! 960: static void musb_ep_frame_cancel(MUSBEndPoint *ep, int dir) ! 961: { ! 962: if (ep->intv_timer[dir]) ! 963: qemu_del_timer(ep->intv_timer[dir]); ! 964: } ! 965: ! 966: /* Bus control */ ! 967: static uint8_t musb_busctl_readb(void *opaque, int ep, int addr) ! 968: { ! 969: MUSBState *s = (MUSBState *) opaque; ! 970: ! 971: switch (addr) { ! 972: /* For USB2.0 HS hubs only */ ! 973: case MUSB_HDRC_TXHUBADDR: ! 974: return s->ep[ep].haddr[0]; ! 975: case MUSB_HDRC_TXHUBPORT: ! 976: return s->ep[ep].hport[0]; ! 977: case MUSB_HDRC_RXHUBADDR: ! 978: return s->ep[ep].haddr[1]; ! 979: case MUSB_HDRC_RXHUBPORT: ! 980: return s->ep[ep].hport[1]; ! 981: ! 982: default: ! 983: TRACE("unknown register 0x%02x", addr); ! 984: return 0x00; ! 985: }; ! 986: } ! 987: ! 988: static void musb_busctl_writeb(void *opaque, int ep, int addr, uint8_t value) ! 989: { ! 990: MUSBState *s = (MUSBState *) opaque; ! 991: ! 992: switch (addr) { ! 993: case MUSB_HDRC_TXFUNCADDR: ! 994: s->ep[ep].faddr[0] = value; ! 995: break; ! 996: case MUSB_HDRC_RXFUNCADDR: ! 997: s->ep[ep].faddr[1] = value; ! 998: break; ! 999: case MUSB_HDRC_TXHUBADDR: ! 1000: s->ep[ep].haddr[0] = value; ! 1001: break; ! 1002: case MUSB_HDRC_TXHUBPORT: ! 1003: s->ep[ep].hport[0] = value; ! 1004: break; ! 1005: case MUSB_HDRC_RXHUBADDR: ! 1006: s->ep[ep].haddr[1] = value; ! 1007: break; ! 1008: case MUSB_HDRC_RXHUBPORT: ! 1009: s->ep[ep].hport[1] = value; ! 1010: break; ! 1011: ! 1012: default: ! 1013: TRACE("unknown register 0x%02x", addr); ! 1014: break; ! 1015: }; ! 1016: } ! 1017: ! 1018: static uint16_t musb_busctl_readh(void *opaque, int ep, int addr) ! 1019: { ! 1020: MUSBState *s = (MUSBState *) opaque; ! 1021: ! 1022: switch (addr) { ! 1023: case MUSB_HDRC_TXFUNCADDR: ! 1024: return s->ep[ep].faddr[0]; ! 1025: case MUSB_HDRC_RXFUNCADDR: ! 1026: return s->ep[ep].faddr[1]; ! 1027: ! 1028: default: ! 1029: return musb_busctl_readb(s, ep, addr) | ! 1030: (musb_busctl_readb(s, ep, addr | 1) << 8); ! 1031: }; ! 1032: } ! 1033: ! 1034: static void musb_busctl_writeh(void *opaque, int ep, int addr, uint16_t value) ! 1035: { ! 1036: MUSBState *s = (MUSBState *) opaque; ! 1037: ! 1038: switch (addr) { ! 1039: case MUSB_HDRC_TXFUNCADDR: ! 1040: s->ep[ep].faddr[0] = value; ! 1041: break; ! 1042: case MUSB_HDRC_RXFUNCADDR: ! 1043: s->ep[ep].faddr[1] = value; ! 1044: break; ! 1045: ! 1046: default: ! 1047: musb_busctl_writeb(s, ep, addr, value & 0xff); ! 1048: musb_busctl_writeb(s, ep, addr | 1, value >> 8); ! 1049: }; ! 1050: } ! 1051: ! 1052: /* Endpoint control */ ! 1053: static uint8_t musb_ep_readb(void *opaque, int ep, int addr) ! 1054: { ! 1055: MUSBState *s = (MUSBState *) opaque; ! 1056: ! 1057: switch (addr) { ! 1058: case MUSB_HDRC_TXTYPE: ! 1059: return s->ep[ep].type[0]; ! 1060: case MUSB_HDRC_TXINTERVAL: ! 1061: return s->ep[ep].interval[0]; ! 1062: case MUSB_HDRC_RXTYPE: ! 1063: return s->ep[ep].type[1]; ! 1064: case MUSB_HDRC_RXINTERVAL: ! 1065: return s->ep[ep].interval[1]; ! 1066: case (MUSB_HDRC_FIFOSIZE & ~1): ! 1067: return 0x00; ! 1068: case MUSB_HDRC_FIFOSIZE: ! 1069: return ep ? s->ep[ep].fifosize : s->ep[ep].config; ! 1070: case MUSB_HDRC_RXCOUNT: ! 1071: return s->ep[ep].rxcount; ! 1072: ! 1073: default: ! 1074: TRACE("unknown register 0x%02x", addr); ! 1075: return 0x00; ! 1076: }; ! 1077: } ! 1078: ! 1079: static void musb_ep_writeb(void *opaque, int ep, int addr, uint8_t value) ! 1080: { ! 1081: MUSBState *s = (MUSBState *) opaque; ! 1082: ! 1083: switch (addr) { ! 1084: case MUSB_HDRC_TXTYPE: ! 1085: s->ep[ep].type[0] = value; ! 1086: break; ! 1087: case MUSB_HDRC_TXINTERVAL: ! 1088: s->ep[ep].interval[0] = value; ! 1089: musb_ep_frame_cancel(&s->ep[ep], 0); ! 1090: break; ! 1091: case MUSB_HDRC_RXTYPE: ! 1092: s->ep[ep].type[1] = value; ! 1093: break; ! 1094: case MUSB_HDRC_RXINTERVAL: ! 1095: s->ep[ep].interval[1] = value; ! 1096: musb_ep_frame_cancel(&s->ep[ep], 1); ! 1097: break; ! 1098: case (MUSB_HDRC_FIFOSIZE & ~1): ! 1099: break; ! 1100: case MUSB_HDRC_FIFOSIZE: ! 1101: TRACE("somebody messes with fifosize (now %i bytes)", value); ! 1102: s->ep[ep].fifosize = value; ! 1103: break; ! 1104: default: ! 1105: TRACE("unknown register 0x%02x", addr); ! 1106: break; ! 1107: }; ! 1108: } ! 1109: ! 1110: static uint16_t musb_ep_readh(void *opaque, int ep, int addr) ! 1111: { ! 1112: MUSBState *s = (MUSBState *) opaque; ! 1113: uint16_t ret; ! 1114: ! 1115: switch (addr) { ! 1116: case MUSB_HDRC_TXMAXP: ! 1117: return s->ep[ep].maxp[0]; ! 1118: case MUSB_HDRC_TXCSR: ! 1119: return s->ep[ep].csr[0]; ! 1120: case MUSB_HDRC_RXMAXP: ! 1121: return s->ep[ep].maxp[1]; ! 1122: case MUSB_HDRC_RXCSR: ! 1123: ret = s->ep[ep].csr[1]; ! 1124: ! 1125: /* TODO: This and other bits probably depend on ! 1126: * ep->csr[1] & MGC_M_RXCSR_AUTOCLEAR. */ ! 1127: if (s->ep[ep].csr[1] & MGC_M_RXCSR_AUTOCLEAR) ! 1128: s->ep[ep].csr[1] &= ~MGC_M_RXCSR_RXPKTRDY; ! 1129: ! 1130: return ret; ! 1131: case MUSB_HDRC_RXCOUNT: ! 1132: return s->ep[ep].rxcount; ! 1133: ! 1134: default: ! 1135: return musb_ep_readb(s, ep, addr) | ! 1136: (musb_ep_readb(s, ep, addr | 1) << 8); ! 1137: }; ! 1138: } ! 1139: ! 1140: static void musb_ep_writeh(void *opaque, int ep, int addr, uint16_t value) ! 1141: { ! 1142: MUSBState *s = (MUSBState *) opaque; ! 1143: ! 1144: switch (addr) { ! 1145: case MUSB_HDRC_TXMAXP: ! 1146: s->ep[ep].maxp[0] = value; ! 1147: break; ! 1148: case MUSB_HDRC_TXCSR: ! 1149: if (ep) { ! 1150: s->ep[ep].csr[0] &= value & 0xa6; ! 1151: s->ep[ep].csr[0] |= value & 0xff59; ! 1152: } else { ! 1153: s->ep[ep].csr[0] &= value & 0x85; ! 1154: s->ep[ep].csr[0] |= value & 0xf7a; ! 1155: } ! 1156: ! 1157: musb_ep_frame_cancel(&s->ep[ep], 0); ! 1158: ! 1159: if ((ep && (value & MGC_M_TXCSR_FLUSHFIFO)) || ! 1160: (!ep && (value & MGC_M_CSR0_FLUSHFIFO))) { ! 1161: s->ep[ep].fifolen[0] = 0; ! 1162: s->ep[ep].fifostart[0] = 0; ! 1163: if (ep) ! 1164: s->ep[ep].csr[0] &= ! 1165: ~(MGC_M_TXCSR_FIFONOTEMPTY | MGC_M_TXCSR_TXPKTRDY); ! 1166: else ! 1167: s->ep[ep].csr[0] &= ! 1168: ~(MGC_M_CSR0_TXPKTRDY | MGC_M_CSR0_RXPKTRDY); ! 1169: } ! 1170: if ( ! 1171: (ep && ! 1172: #ifdef CLEAR_NAK ! 1173: (value & MGC_M_TXCSR_TXPKTRDY) && ! 1174: !(value & MGC_M_TXCSR_H_NAKTIMEOUT)) || ! 1175: #else ! 1176: (value & MGC_M_TXCSR_TXPKTRDY)) || ! 1177: #endif ! 1178: (!ep && ! 1179: #ifdef CLEAR_NAK ! 1180: (value & MGC_M_CSR0_TXPKTRDY) && ! 1181: !(value & MGC_M_CSR0_H_NAKTIMEOUT))) ! 1182: #else ! 1183: (value & MGC_M_CSR0_TXPKTRDY))) ! 1184: #endif ! 1185: musb_tx_rdy(s, ep); ! 1186: if (!ep && ! 1187: (value & MGC_M_CSR0_H_REQPKT) && ! 1188: #ifdef CLEAR_NAK ! 1189: !(value & (MGC_M_CSR0_H_NAKTIMEOUT | ! 1190: MGC_M_CSR0_RXPKTRDY))) ! 1191: #else ! 1192: !(value & MGC_M_CSR0_RXPKTRDY)) ! 1193: #endif ! 1194: musb_rx_req(s, ep); ! 1195: break; ! 1196: ! 1197: case MUSB_HDRC_RXMAXP: ! 1198: s->ep[ep].maxp[1] = value; ! 1199: break; ! 1200: case MUSB_HDRC_RXCSR: ! 1201: /* (DMA mode only) */ ! 1202: if ( ! 1203: (value & MGC_M_RXCSR_H_AUTOREQ) && ! 1204: !(value & MGC_M_RXCSR_RXPKTRDY) && ! 1205: (s->ep[ep].csr[1] & MGC_M_RXCSR_RXPKTRDY)) ! 1206: value |= MGC_M_RXCSR_H_REQPKT; ! 1207: ! 1208: s->ep[ep].csr[1] &= 0x102 | (value & 0x4d); ! 1209: s->ep[ep].csr[1] |= value & 0xfeb0; ! 1210: ! 1211: musb_ep_frame_cancel(&s->ep[ep], 1); ! 1212: ! 1213: if (value & MGC_M_RXCSR_FLUSHFIFO) { ! 1214: s->ep[ep].fifolen[1] = 0; ! 1215: s->ep[ep].fifostart[1] = 0; ! 1216: s->ep[ep].csr[1] &= ~(MGC_M_RXCSR_FIFOFULL | MGC_M_RXCSR_RXPKTRDY); ! 1217: /* If double buffering and we have two packets ready, flush ! 1218: * only the first one and set up the fifo at the second packet. */ ! 1219: } ! 1220: #ifdef CLEAR_NAK ! 1221: if ((value & MGC_M_RXCSR_H_REQPKT) && !(value & MGC_M_RXCSR_DATAERROR)) ! 1222: #else ! 1223: if (value & MGC_M_RXCSR_H_REQPKT) ! 1224: #endif ! 1225: musb_rx_req(s, ep); ! 1226: break; ! 1227: case MUSB_HDRC_RXCOUNT: ! 1228: s->ep[ep].rxcount = value; ! 1229: break; ! 1230: ! 1231: default: ! 1232: musb_ep_writeb(s, ep, addr, value & 0xff); ! 1233: musb_ep_writeb(s, ep, addr | 1, value >> 8); ! 1234: }; ! 1235: } ! 1236: ! 1237: /* Generic control */ ! 1238: static uint32_t musb_readb(void *opaque, target_phys_addr_t addr) ! 1239: { ! 1240: MUSBState *s = (MUSBState *) opaque; ! 1241: int ep, i; ! 1242: uint8_t ret; ! 1243: ! 1244: switch (addr) { ! 1245: case MUSB_HDRC_FADDR: ! 1246: return s->faddr; ! 1247: case MUSB_HDRC_POWER: ! 1248: return s->power; ! 1249: case MUSB_HDRC_INTRUSB: ! 1250: ret = s->intr; ! 1251: for (i = 0; i < sizeof(ret) * 8; i ++) ! 1252: if (ret & (1 << i)) ! 1253: musb_intr_set(s, i, 0); ! 1254: return ret; ! 1255: case MUSB_HDRC_INTRUSBE: ! 1256: return s->mask; ! 1257: case MUSB_HDRC_INDEX: ! 1258: return s->idx; ! 1259: case MUSB_HDRC_TESTMODE: ! 1260: return 0x00; ! 1261: ! 1262: case MUSB_HDRC_EP_IDX ... (MUSB_HDRC_EP_IDX + 0xf): ! 1263: return musb_ep_readb(s, s->idx, addr & 0xf); ! 1264: ! 1265: case MUSB_HDRC_DEVCTL: ! 1266: return s->devctl; ! 1267: ! 1268: case MUSB_HDRC_TXFIFOSZ: ! 1269: case MUSB_HDRC_RXFIFOSZ: ! 1270: case MUSB_HDRC_VCTRL: ! 1271: /* TODO */ ! 1272: return 0x00; ! 1273: ! 1274: case MUSB_HDRC_HWVERS: ! 1275: return (1 << 10) | 400; ! 1276: ! 1277: case (MUSB_HDRC_VCTRL | 1): ! 1278: case (MUSB_HDRC_HWVERS | 1): ! 1279: case (MUSB_HDRC_DEVCTL | 1): ! 1280: return 0x00; ! 1281: ! 1282: case MUSB_HDRC_BUSCTL ... (MUSB_HDRC_BUSCTL + 0x7f): ! 1283: ep = (addr >> 3) & 0xf; ! 1284: return musb_busctl_readb(s, ep, addr & 0x7); ! 1285: ! 1286: case MUSB_HDRC_EP ... (MUSB_HDRC_EP + 0xff): ! 1287: ep = (addr >> 4) & 0xf; ! 1288: return musb_ep_readb(s, ep, addr & 0xf); ! 1289: ! 1290: case MUSB_HDRC_FIFO ... (MUSB_HDRC_FIFO + 0x3f): ! 1291: ep = ((addr - MUSB_HDRC_FIFO) >> 2) & 0xf; ! 1292: return musb_read_fifo(s->ep + ep); ! 1293: ! 1294: default: ! 1295: TRACE("unknown register 0x%02x", (int) addr); ! 1296: return 0x00; ! 1297: }; ! 1298: } ! 1299: ! 1300: static void musb_writeb(void *opaque, target_phys_addr_t addr, uint32_t value) ! 1301: { ! 1302: MUSBState *s = (MUSBState *) opaque; ! 1303: int ep; ! 1304: ! 1305: switch (addr) { ! 1306: case MUSB_HDRC_FADDR: ! 1307: s->faddr = value & 0x7f; ! 1308: break; ! 1309: case MUSB_HDRC_POWER: ! 1310: s->power = (value & 0xef) | (s->power & 0x10); ! 1311: /* MGC_M_POWER_RESET is also read-only in Peripheral Mode */ ! 1312: if ((value & MGC_M_POWER_RESET) && s->port.dev) { ! 1313: usb_device_reset(s->port.dev); ! 1314: /* Negotiate high-speed operation if MGC_M_POWER_HSENAB is set. */ ! 1315: if ((value & MGC_M_POWER_HSENAB) && ! 1316: s->port.dev->speed == USB_SPEED_HIGH) ! 1317: s->power |= MGC_M_POWER_HSMODE; /* Success */ ! 1318: /* Restart frame counting. */ ! 1319: } ! 1320: if (value & MGC_M_POWER_SUSPENDM) { ! 1321: /* When all transfers finish, suspend and if MGC_M_POWER_ENSUSPEND ! 1322: * is set, also go into low power mode. Frame counting stops. */ ! 1323: /* XXX: Cleared when the interrupt register is read */ ! 1324: } ! 1325: if (value & MGC_M_POWER_RESUME) { ! 1326: /* Wait 20ms and signal resuming on the bus. Frame counting ! 1327: * restarts. */ ! 1328: } ! 1329: break; ! 1330: case MUSB_HDRC_INTRUSB: ! 1331: break; ! 1332: case MUSB_HDRC_INTRUSBE: ! 1333: s->mask = value & 0xff; ! 1334: break; ! 1335: case MUSB_HDRC_INDEX: ! 1336: s->idx = value & 0xf; ! 1337: break; ! 1338: case MUSB_HDRC_TESTMODE: ! 1339: break; ! 1340: ! 1341: case MUSB_HDRC_EP_IDX ... (MUSB_HDRC_EP_IDX + 0xf): ! 1342: musb_ep_writeb(s, s->idx, addr & 0xf, value); ! 1343: break; ! 1344: ! 1345: case MUSB_HDRC_DEVCTL: ! 1346: s->session = !!(value & MGC_M_DEVCTL_SESSION); ! 1347: musb_session_update(s, ! 1348: !!s->port.dev, ! 1349: !!(s->devctl & MGC_M_DEVCTL_SESSION)); ! 1350: ! 1351: /* It seems this is the only R/W bit in this register? */ ! 1352: s->devctl &= ~MGC_M_DEVCTL_SESSION; ! 1353: s->devctl |= value & MGC_M_DEVCTL_SESSION; ! 1354: break; ! 1355: ! 1356: case MUSB_HDRC_TXFIFOSZ: ! 1357: case MUSB_HDRC_RXFIFOSZ: ! 1358: case MUSB_HDRC_VCTRL: ! 1359: /* TODO */ ! 1360: break; ! 1361: ! 1362: case (MUSB_HDRC_VCTRL | 1): ! 1363: case (MUSB_HDRC_DEVCTL | 1): ! 1364: break; ! 1365: ! 1366: case MUSB_HDRC_BUSCTL ... (MUSB_HDRC_BUSCTL + 0x7f): ! 1367: ep = (addr >> 3) & 0xf; ! 1368: musb_busctl_writeb(s, ep, addr & 0x7, value); ! 1369: break; ! 1370: ! 1371: case MUSB_HDRC_EP ... (MUSB_HDRC_EP + 0xff): ! 1372: ep = (addr >> 4) & 0xf; ! 1373: musb_ep_writeb(s, ep, addr & 0xf, value); ! 1374: break; ! 1375: ! 1376: case MUSB_HDRC_FIFO ... (MUSB_HDRC_FIFO + 0x3f): ! 1377: ep = ((addr - MUSB_HDRC_FIFO) >> 2) & 0xf; ! 1378: musb_write_fifo(s->ep + ep, value & 0xff); ! 1379: break; ! 1380: ! 1381: default: ! 1382: TRACE("unknown register 0x%02x", (int) addr); ! 1383: break; ! 1384: }; ! 1385: } ! 1386: ! 1387: static uint32_t musb_readh(void *opaque, target_phys_addr_t addr) ! 1388: { ! 1389: MUSBState *s = (MUSBState *) opaque; ! 1390: int ep, i; ! 1391: uint16_t ret; ! 1392: ! 1393: switch (addr) { ! 1394: case MUSB_HDRC_INTRTX: ! 1395: ret = s->tx_intr; ! 1396: /* Auto clear */ ! 1397: for (i = 0; i < sizeof(ret) * 8; i ++) ! 1398: if (ret & (1 << i)) ! 1399: musb_tx_intr_set(s, i, 0); ! 1400: return ret; ! 1401: case MUSB_HDRC_INTRRX: ! 1402: ret = s->rx_intr; ! 1403: /* Auto clear */ ! 1404: for (i = 0; i < sizeof(ret) * 8; i ++) ! 1405: if (ret & (1 << i)) ! 1406: musb_rx_intr_set(s, i, 0); ! 1407: return ret; ! 1408: case MUSB_HDRC_INTRTXE: ! 1409: return s->tx_mask; ! 1410: case MUSB_HDRC_INTRRXE: ! 1411: return s->rx_mask; ! 1412: ! 1413: case MUSB_HDRC_FRAME: ! 1414: /* TODO */ ! 1415: return 0x0000; ! 1416: case MUSB_HDRC_TXFIFOADDR: ! 1417: return s->ep[s->idx].fifoaddr[0]; ! 1418: case MUSB_HDRC_RXFIFOADDR: ! 1419: return s->ep[s->idx].fifoaddr[1]; ! 1420: ! 1421: case MUSB_HDRC_EP_IDX ... (MUSB_HDRC_EP_IDX + 0xf): ! 1422: return musb_ep_readh(s, s->idx, addr & 0xf); ! 1423: ! 1424: case MUSB_HDRC_BUSCTL ... (MUSB_HDRC_BUSCTL + 0x7f): ! 1425: ep = (addr >> 3) & 0xf; ! 1426: return musb_busctl_readh(s, ep, addr & 0x7); ! 1427: ! 1428: case MUSB_HDRC_EP ... (MUSB_HDRC_EP + 0xff): ! 1429: ep = (addr >> 4) & 0xf; ! 1430: return musb_ep_readh(s, ep, addr & 0xf); ! 1431: ! 1432: case MUSB_HDRC_FIFO ... (MUSB_HDRC_FIFO + 0x3f): ! 1433: ep = ((addr - MUSB_HDRC_FIFO) >> 2) & 0xf; ! 1434: return (musb_read_fifo(s->ep + ep) | musb_read_fifo(s->ep + ep) << 8); ! 1435: ! 1436: default: ! 1437: return musb_readb(s, addr) | (musb_readb(s, addr | 1) << 8); ! 1438: }; ! 1439: } ! 1440: ! 1441: static void musb_writeh(void *opaque, target_phys_addr_t addr, uint32_t value) ! 1442: { ! 1443: MUSBState *s = (MUSBState *) opaque; ! 1444: int ep; ! 1445: ! 1446: switch (addr) { ! 1447: case MUSB_HDRC_INTRTXE: ! 1448: s->tx_mask = value; ! 1449: /* XXX: the masks seem to apply on the raising edge like with ! 1450: * edge-triggered interrupts, thus no need to update. I may be ! 1451: * wrong though. */ ! 1452: break; ! 1453: case MUSB_HDRC_INTRRXE: ! 1454: s->rx_mask = value; ! 1455: break; ! 1456: ! 1457: case MUSB_HDRC_FRAME: ! 1458: /* TODO */ ! 1459: break; ! 1460: case MUSB_HDRC_TXFIFOADDR: ! 1461: s->ep[s->idx].fifoaddr[0] = value; ! 1462: s->ep[s->idx].buf[0] = ! 1463: s->buf + ((value << 3) & 0x7ff ); ! 1464: break; ! 1465: case MUSB_HDRC_RXFIFOADDR: ! 1466: s->ep[s->idx].fifoaddr[1] = value; ! 1467: s->ep[s->idx].buf[1] = ! 1468: s->buf + ((value << 3) & 0x7ff); ! 1469: break; ! 1470: ! 1471: case MUSB_HDRC_EP_IDX ... (MUSB_HDRC_EP_IDX + 0xf): ! 1472: musb_ep_writeh(s, s->idx, addr & 0xf, value); ! 1473: break; ! 1474: ! 1475: case MUSB_HDRC_BUSCTL ... (MUSB_HDRC_BUSCTL + 0x7f): ! 1476: ep = (addr >> 3) & 0xf; ! 1477: musb_busctl_writeh(s, ep, addr & 0x7, value); ! 1478: break; ! 1479: ! 1480: case MUSB_HDRC_EP ... (MUSB_HDRC_EP + 0xff): ! 1481: ep = (addr >> 4) & 0xf; ! 1482: musb_ep_writeh(s, ep, addr & 0xf, value); ! 1483: break; ! 1484: ! 1485: case MUSB_HDRC_FIFO ... (MUSB_HDRC_FIFO + 0x3f): ! 1486: ep = ((addr - MUSB_HDRC_FIFO) >> 2) & 0xf; ! 1487: musb_write_fifo(s->ep + ep, value & 0xff); ! 1488: musb_write_fifo(s->ep + ep, (value >> 8) & 0xff); ! 1489: break; ! 1490: ! 1491: default: ! 1492: musb_writeb(s, addr, value & 0xff); ! 1493: musb_writeb(s, addr | 1, value >> 8); ! 1494: }; ! 1495: } ! 1496: ! 1497: static uint32_t musb_readw(void *opaque, target_phys_addr_t addr) ! 1498: { ! 1499: MUSBState *s = (MUSBState *) opaque; ! 1500: int ep; ! 1501: ! 1502: switch (addr) { ! 1503: case MUSB_HDRC_FIFO ... (MUSB_HDRC_FIFO + 0x3f): ! 1504: ep = ((addr - MUSB_HDRC_FIFO) >> 2) & 0xf; ! 1505: return ( musb_read_fifo(s->ep + ep) | ! 1506: musb_read_fifo(s->ep + ep) << 8 | ! 1507: musb_read_fifo(s->ep + ep) << 16 | ! 1508: musb_read_fifo(s->ep + ep) << 24 ); ! 1509: default: ! 1510: TRACE("unknown register 0x%02x", (int) addr); ! 1511: return 0x00000000; ! 1512: }; ! 1513: } ! 1514: ! 1515: static void musb_writew(void *opaque, target_phys_addr_t addr, uint32_t value) ! 1516: { ! 1517: MUSBState *s = (MUSBState *) opaque; ! 1518: int ep; ! 1519: ! 1520: switch (addr) { ! 1521: case MUSB_HDRC_FIFO ... (MUSB_HDRC_FIFO + 0x3f): ! 1522: ep = ((addr - MUSB_HDRC_FIFO) >> 2) & 0xf; ! 1523: musb_write_fifo(s->ep + ep, value & 0xff); ! 1524: musb_write_fifo(s->ep + ep, (value >> 8 ) & 0xff); ! 1525: musb_write_fifo(s->ep + ep, (value >> 16) & 0xff); ! 1526: musb_write_fifo(s->ep + ep, (value >> 24) & 0xff); ! 1527: break; ! 1528: default: ! 1529: TRACE("unknown register 0x%02x", (int) addr); ! 1530: break; ! 1531: }; ! 1532: } ! 1533: ! 1534: CPUReadMemoryFunc * const musb_read[] = { ! 1535: musb_readb, ! 1536: musb_readh, ! 1537: musb_readw, ! 1538: }; ! 1539: ! 1540: CPUWriteMemoryFunc * const musb_write[] = { ! 1541: musb_writeb, ! 1542: musb_writeh, ! 1543: musb_writew, ! 1544: };
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.