/* * Copyright (C) 2006 Eric Biederman (ebiederm@xmission.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 as published by the Free Software Foundation. * */ #ifndef __ROMCC__ #include #else #if CONFIG_USE_PRINTK_IN_CAR==0 #define printk_debug(fmt, arg...) do {} while(0) #endif #endif #include #include //#define EHCI_BAR 0xbfce0000 #define EHCI_BAR_BYTES 4096 //#define EHCI_DEBUG_OFFSET 0x98 /* Off of ehci_base */ #define EHCI_CAPLENGTH 0x00 #define EHCI_HCIVERSION 0x02 #define EHCI_HCSPARAMS 0x04 #define EHCI_HCCPARAMS 0x08 #define EHCI_HCSP_PORTROUTE 0x0c /* Off of ehci_op_base */ #define EHCI_USBCMD 0x00 #define EHCI_USBCMD_RUN (1 << 0) #define EHCI_USBSTS 0x04 #define EHCI_USBSTS_HCHALTED (1 << 12) #define EHCI_USBINTR 0x08 #define EHCI_FRINDEX 0x0c #define EHCI_CTRLDSSEGMENT 0x10 #define EHCI_PERIODICLISTBASE 0x14 #define EHCI_ASYNCLISTADDR 0x18 #define EHCI_CONFIGFLAG 0x40 #define EHCI_CONFIGFLAG_FLAG (1 << 0) #define EHCI_PORTSC 0x44 #define EHCI_PORTSC_PORT_OWNER (1 << 13) #define EHCI_PORTSC_PORT_RESET (1 << 12) #define EHCI_PORTSC_PORT_ENABLED (1 << 2) #define EHCI_PORTSC_CONNECT_STATUS_CHANGED (1 << 1) #define EHCI_PORTSC_CONNECTED (1 << 0) /* Off of ehci_debug_base */ #define EHCI_CTRL 0x00 #define EHCI_CTRL_OWNER (1 << 30) #define EHCI_CTRL_ENABLED (1 << 28) #define EHCI_CTRL_DONE (1 << 16) #define EHCI_CTRL_INUSE (1 << 10) #define EHCI_CTRL_EXCEPTION_MASK 7 #define EHCI_CTRL_EXCEPTION_SHIFT 7 #define EHCI_CTRL_EXCEPTION_NONE 0 #define EHCI_CTRL_EXCEPTION_TRANSACTION 1 #define EHCI_CTRL_EXCEPTION_HARDWARE 2 #define EHCI_CTRL_ERROR (1 << 6) #define EHCI_CTRL_GO (1 << 5) #define EHCI_CTRL_WRITE (1 << 4) #define EHCI_CTRL_LENGTH_MASK (0xf << 0) #define EHCI_PID 0x04 #define EHCI_PID_RECEIVED_SHIFT 16 #define EHCI_PID_SEND_SHIFT 8 #define EHCI_PID_TOKEN_SHIFT 0 #define EHCI_DATA0 0x08 #define EHCI_DATA1 0x0c #define EHCI_ADDR 0x10 #define EHCI_ADDR_DEVNUM_SHIFT 8 #define EHCI_ADDR_ENDPOINT_SHIFT 0 #define MKPID(x) (((x) & 0xf) | ((~(x) & 0xf) << 4)) /* token */ #define PID_OUT MKPID(0x1) #define PID_IN MKPID(0x9) #define PID_SOF MKPID(0x5) #define PID_SETUP MKPID(0xd) /* data */ #define PID_DATA0 MKPID(0x3) #define PID_DATA1 MKPID(0xb) #define PID_DATA2 MKPID(0x7) #define PID_MDATA MKPID(0xf) #define PID_DATA_TOGGLE (0x88) /* handshake */ #define PID_ACK MKPID(0x2) #define PID_NAK MKPID(0xa) #define PID_STALL MKPID(0xe) #define PID_NYET MKPID(0x6) /* Special */ #define PID_PRE MKPID(0xc) #define PID_ERR MKPID(0xc) #define PID_SPLIT MKPID(0x8) #define PID_PING MKPID(0x4) #define PID_RESERVED MKPID(0x0) /* * Standard requests */ #define USB_REQ_GET_STATUS 0x00 #define USB_REQ_CLEAR_FEATURE 0x01 /* 0x02 is reserved */ #define USB_REQ_SET_FEATURE 0x03 /* 0x04 is reserved */ #define USB_REQ_SET_ADDRESS 0x05 #define USB_REQ_GET_DESCRIPTOR 0x06 #define USB_REQ_SET_DESCRIPTOR 0x07 #define USB_REQ_GET_CONFIGURATION 0x08 #define USB_REQ_SET_CONFIGURATION 0x09 #define USB_REQ_GET_INTERFACE 0x0A #define USB_REQ_SET_INTERFACE 0x0B #define USB_REQ_SYNCH_FRAME 0x0C #define USB_TYPE_STANDARD (0x00 << 5) #define USB_TYPE_CLASS (0x01 << 5) #define USB_TYPE_VENDOR (0x02 << 5) #define USB_TYPE_RESERVED (0x03 << 5) #define USB_RECIP_DEVICE 0x00 #define USB_RECIP_INTERFACE 0x01 #define USB_RECIP_ENDPOINT 0x02 #define USB_RECIP_OTHER 0x03 /* * Various libusb API related stuff */ #define USB_ENDPOINT_IN 0x80 #define USB_ENDPOINT_OUT 0x00 #ifndef USB_DT_DEBUG #define USB_DT_DEBUG 10 #endif #ifndef USB_FT_DEBUG_MODE #define USB_FT_DEBUG_MODE 6 #endif struct usb_debug_descriptor { uint8_t bLength; uint8_t bDescriptorType; uint8_t bDebugInEndpoint; uint8_t bDebugOutEndpoint; } __attribute__ ((packed)); struct usb_status { uint16_t status; } __attribute__ ((packed)); struct usb_request { uint8_t bmRequestType; uint8_t bRequest; uint16_t wValue; uint16_t wIndex; uint16_t wLength; } __attribute__ ((packed)); static int usb_wait_until_complete(void *ehci_debug_base) { unsigned ctrl; for (;;) { ctrl = readl(ehci_debug_base + EHCI_CTRL); /* Stop when the transaction is finished */ if (ctrl & EHCI_CTRL_DONE) break; } /* Now that we have observed the completed transaction, * clear the done bit. */ writel(ctrl | EHCI_CTRL_DONE, ehci_debug_base + EHCI_CTRL); return (ctrl & EHCI_CTRL_ERROR) ? -((ctrl >> EHCI_CTRL_EXCEPTION_SHIFT) & EHCI_CTRL_EXCEPTION_MASK): ctrl & 0xf; } static void usb_breath(void) { // with out_port 80? in CAR stage // or tsc? mdelay(10); } static int usb_wait_until_done(void *ehci_debug_base, unsigned ctrl) { unsigned pids, lpid; int ret; retry: writel(ctrl | EHCI_CTRL_GO, ehci_debug_base + EHCI_CTRL); ret = usb_wait_until_complete(ehci_debug_base); pids = readl(ehci_debug_base + EHCI_PID); lpid = (pids >> EHCI_PID_RECEIVED_SHIFT) & 0xff; #if 1 if ((ret >= 0) && lpid != PID_ACK) printk_debug("lpid: %02x ret: %d\n", lpid, ret); #endif if (ret < 0) return ret; /* If the port is getting full or it has dropped data * start pacing ourselves, not necessary but it's friendly. */ if ((lpid == PID_NAK) || (lpid == PID_NYET)) usb_breath(); /* If I get a NACK reissue the transmission */ if (lpid == PID_NAK) goto retry; return ret; } static void usb_set_data(void *ehci_debug_base, const void *buf, int size) { const unsigned char *bytes = buf; uint32_t lo, hi; int i; lo = hi = 0; for (i = 0; i < 4 && i < size; i++) lo |= bytes[i] << (8*i); for (; i < 8 && i < size; i++) hi |= bytes[i] << (8*(i - 4)); writel(lo, ehci_debug_base + EHCI_DATA0); writel(hi, ehci_debug_base + EHCI_DATA1); } static void usb_get_data(void *ehci_debug_base, void *buf, int size) { unsigned char *bytes = buf; uint32_t lo, hi; int i; lo = readl(ehci_debug_base + EHCI_DATA0); hi = readl(ehci_debug_base + EHCI_DATA1); #if 0 printf("data: %08x%08x\n", hi, lo); #endif for (i = 0; i < 4 && i < size; i++) bytes[i] = (lo >> (8*i)) & 0xff; for (; i < 8 && i < size; i++) bytes[i] = (hi >> (8*(i - 4))) & 0xff; } static int usb_bulk_write(void *ehci_debug_base, int address, int endpoint, const char *bytes, int size) { unsigned pids, addr, ctrl; int ret; if (size > 8) return -1; addr = ((address & 0x7f) << EHCI_ADDR_DEVNUM_SHIFT) | (endpoint & 0xf); pids = readl(ehci_debug_base + EHCI_PID); pids &= ~(0xff << EHCI_PID_TOKEN_SHIFT); pids |= PID_OUT << EHCI_PID_TOKEN_SHIFT; pids ^= (PID_DATA_TOGGLE << EHCI_PID_SEND_SHIFT); ctrl = readl(ehci_debug_base + EHCI_CTRL); ctrl &= ~EHCI_CTRL_LENGTH_MASK; ctrl |= EHCI_CTRL_WRITE; ctrl |= size & EHCI_CTRL_LENGTH_MASK; ctrl |= EHCI_CTRL_GO; usb_set_data(ehci_debug_base, bytes, size); writel(addr, ehci_debug_base + EHCI_ADDR); writel(pids, ehci_debug_base + EHCI_PID); ret = usb_wait_until_done(ehci_debug_base, ctrl); if (ret < 0) { printk_debug("out failed!: %d\n", ret); return ret; } return ret; } int usb_bulk_write_x(struct ehci_debug_info *dbg_info, const char *bytes, int size) { return usb_bulk_write(dbg_info->ehci_debug_base, dbg_info->devnum, dbg_info->endpoint_out, bytes, size); } static int usb_bulk_read(void *ehci_debug_base, int address, int endpoint, void *data, int size) { unsigned pids, addr, ctrl; int ret; if (size > 8) return -1; addr = ((address & 0x7f) << EHCI_ADDR_DEVNUM_SHIFT) | (endpoint & 0xf); pids = readl(ehci_debug_base + EHCI_PID); pids &= ~(0xff << EHCI_PID_TOKEN_SHIFT); pids |= PID_IN << EHCI_PID_TOKEN_SHIFT; pids ^= (PID_DATA_TOGGLE << EHCI_PID_SEND_SHIFT); ctrl = readl(ehci_debug_base + EHCI_CTRL); ctrl &= ~EHCI_CTRL_LENGTH_MASK; ctrl &= ~EHCI_CTRL_WRITE; ctrl |= size & EHCI_CTRL_LENGTH_MASK; ctrl |= EHCI_CTRL_GO; writel(addr, ehci_debug_base + EHCI_ADDR); writel(pids, ehci_debug_base + EHCI_PID); ret = usb_wait_until_done(ehci_debug_base, ctrl); if (ret < 0) { printk_debug("in failed!: %d\n", ret); return ret; } if (size > ret) size = ret; usb_get_data(ehci_debug_base, data, size); return ret; } int usb_bulk_read_x(struct ehci_debug_info *dbg_info, void *data, int size) { return usb_bulk_read(dbg_info->ehci_debug_base, dbg_info->devnum, dbg_info->endpoint_in, data, size); } static int usb_control_msg(void *ehci_debug_base, int address, int requesttype, int request, int value, int index, void *data, int size) { unsigned pids, addr, ctrl; struct usb_request req; int read; int ret; read = (requesttype & USB_ENDPOINT_IN) != 0; if (size > (read?8:0)) return -1; /* Compute the control message */ req.bmRequestType = requesttype; req.bRequest = request; req.wValue = value; req.wIndex = index; req.wLength = size; pids = PID_SETUP << EHCI_PID_TOKEN_SHIFT; pids |= PID_DATA0 << EHCI_PID_SEND_SHIFT; addr = ((address & 0x7f) << EHCI_ADDR_DEVNUM_SHIFT) | 0; ctrl = readl(ehci_debug_base + EHCI_CTRL); ctrl &= ~EHCI_CTRL_LENGTH_MASK; ctrl |= EHCI_CTRL_WRITE; ctrl |= sizeof(req) & EHCI_CTRL_LENGTH_MASK; ctrl |= EHCI_CTRL_GO; /* Send the setup message */ usb_set_data(ehci_debug_base, &req, sizeof(req)); writel(addr, ehci_debug_base + EHCI_ADDR); writel(pids, ehci_debug_base + EHCI_PID); ret = usb_wait_until_done(ehci_debug_base, ctrl); if (ret < 0) { //printk_debug("setup failed!: %d\n", ret); return ret; } /* Read the result */ ret = usb_bulk_read(ehci_debug_base, address, 0, data, size); #if 0 pids = readl(ehci_debug_base + EHCI_PID); printf("final pids: %08x ret: %d, size: %d\n", pids, ret, size); #endif return ret; } static int usb_control_msg0(void *ehci_debug_base, int address, int requesttype, int request, int value, int index) { return usb_control_msg(ehci_debug_base, address, requesttype, request, value, index, (void *)0, 0); } int usbdebug_direct_init(unsigned ehci_bar, unsigned ehci_debug_offset, struct ehci_debug_info *dbg_info) { void *ehci_base, *ehci_op_base, *ehci_debug_base; struct usb_debug_descriptor debug; unsigned endpoint_out, endpoint_in; unsigned devnum; unsigned hcsparams; unsigned debug_port, n_ports; unsigned val; int result = -1; int ret, i; ehci_base = ehci_bar; val = readb(ehci_base + EHCI_CAPLENGTH); ehci_op_base = ehci_base + val; ehci_debug_base = ehci_base + ehci_debug_offset; hcsparams = readl(ehci_base + EHCI_HCSPARAMS); debug_port = (hcsparams >> 20) & 0xf; n_ports = hcsparams & 0xf; printk_debug("debug_port: %d\n", debug_port); printk_debug("n_ports: %d\n", n_ports); for (i = 1; i <= n_ports; i++) { val = readl(ehci_op_base + EHCI_PORTSC + (4*(i - 1))); printk_debug("portsc%d: %08x\n", i, val); } #define EHCI_CTRL_CLAIM (EHCI_CTRL_OWNER | EHCI_CTRL_ENABLED | EHCI_CTRL_INUSE) val = readl(ehci_debug_base + EHCI_CTRL); printk_debug("ctrl: %04x\n", val); writel(val | EHCI_CTRL_CLAIM, ehci_debug_base + EHCI_CTRL); val = readl(ehci_debug_base + EHCI_CTRL); printk_debug("ctrl: %04x\n", val); if ((val & EHCI_CTRL_CLAIM) != EHCI_CTRL_CLAIM) { printk_debug("No device in debug port\n"); writel(val & ~EHCI_CTRL_CLAIM, ehci_debug_base + EHCI_CTRL); return -1; } printk_debug("Find the debug device!\n"); /* Find the debug device and make it device number 127 */ for (devnum = 0; devnum <= 127; devnum++) { //printf("devnum: %d\n", devnum); ret = usb_control_msg(ehci_debug_base, devnum, USB_ENDPOINT_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE, USB_REQ_GET_DESCRIPTOR, (USB_DT_DEBUG << 8), 0, &debug, sizeof(debug)); if (ret > 0) break; } if (devnum > 127) { printk_debug("Could not find attached debug device\n"); goto err; } if (ret < 0) { printk_debug("Attach device is not a debug device\n"); goto err; } printk_debug("devnum: %d\n", devnum); endpoint_out = debug.bDebugOutEndpoint; endpoint_in = debug.bDebugInEndpoint; /* Move the device to 127 if it isn't already there */ if (devnum != 127) { ret = usb_control_msg0(ehci_debug_base, devnum, USB_TYPE_STANDARD | USB_RECIP_DEVICE, USB_REQ_SET_ADDRESS, 127, 0); printk_debug("set_address: %d\n", ret); if (ret < 0) { printk_debug("Could not move attached device to 127\n"); goto err; } devnum = 127; } /* Enable the debug interface */ ret = usb_control_msg0(ehci_debug_base, devnum, USB_TYPE_STANDARD | USB_RECIP_DEVICE, USB_REQ_SET_FEATURE, USB_FT_DEBUG_MODE, 0); printk_debug("set_feature_debug_mode: %d\n", ret); if (ret < 0) { printk_debug(" Could not enable the debug device\n"); goto err; } dbg_info->ehci_base = ehci_base; dbg_info->ehci_op_base = ehci_op_base; dbg_info->ehci_debug_base = ehci_debug_base; dbg_info->devnum = devnum; dbg_info->endpoint_out = endpoint_out; dbg_info->endpoint_in = endpoint_in; #if 0 { //unsigned devnum = 2; //unsigned endpoint_out = 1; //unsigned endpoint_in = 0x82; char *test_strings[] = { "zero000\n", "one1111\n", "two2222\n", "three33\n", "four444\n", "five555\n", "six6666\n", "seven77\n", "eight88\n", "nine999\n", "tenaaaa\n", "elevenb\n", (void *)0, }; char **ptr; /* Perform a small write to get the even/odd data state in sync */ ret = usb_bulk_write(ehci_debug_base, devnum, endpoint_out, " ",1); printk_debug("usb_bulk_write: %d\n", ret); /* Write the test messages */ for (ptr = test_strings; *ptr; ptr++) { /* Write a test message */ ret = usb_bulk_write(ehci_debug_base, devnum, endpoint_out, *ptr, 8); printk_debug("usb_bulk_write: %d\n", ret); } } #endif #if 0 /* Read some test messages */ for (;;) { char buf[8]; ret = usb_bulk_read(ehci_debug_base, devnum, endpoint_in, buf, sizeof(buf)); if (ret > 0) printk_debug("%d:%*.*s", ret, ret, ret, buf); } #endif #if 0 for (i = 0; i < 256; i+=1) { val = readb(ehci_base + i); printk_debug("%02x ", val); if ((i & 0xf) == 0xf) printk_debug("\n"); } printk_debug("\n"); #endif result = 0; dbg_info->inited = 1; return result; err: #ifdef EHCI_CTRL_CLAIM val = readl(ehci_debug_base + EHCI_CTRL); printk_debug("ctrl: %08x\n", val); val &= ~(EHCI_CTRL_CLAIM | EHCI_CTRL_WRITE); writel(val, ehci_debug_base + EHCI_CTRL); val = readl(ehci_debug_base + EHCI_CTRL); printk_debug("ctrl: %08x\n", val); #endif return result; }