cregit-Linux how code gets into the kernel

Release 4.7 drivers/usb/early/ehci-dbgp.c

/*
 * Standalone EHCI usb debug driver
 *
 * Originally written by:
 *  Eric W. Biederman" <ebiederm@xmission.com> and
 *  Yinghai Lu <yhlu.kernel@gmail.com>
 *
 * Changes for early/late printk and HW errata:
 *  Jason Wessel <jason.wessel@windriver.com>
 *  Copyright (C) 2009 Wind River Systems, Inc.
 *
 */

#include <linux/console.h>
#include <linux/errno.h>
#include <linux/module.h>
#include <linux/pci_regs.h>
#include <linux/pci_ids.h>
#include <linux/usb/ch9.h>
#include <linux/usb/ehci_def.h>
#include <linux/delay.h>
#include <linux/serial_core.h>
#include <linux/kconfig.h>
#include <linux/kgdb.h>
#include <linux/kthread.h>
#include <asm/io.h>
#include <asm/pci-direct.h>
#include <asm/fixmap.h>

/* The code here is intended to talk directly to the EHCI debug port
 * and does not require that you have any kind of USB host controller
 * drivers or USB device drivers compiled into the kernel.
 *
 * If you make a change to anything in here, the following test cases
 * need to pass where a USB debug device works in the following
 * configurations.
 *
 * 1. boot args:  earlyprintk=dbgp
 *     o kernel compiled with # CONFIG_USB_EHCI_HCD is not set
 *     o kernel compiled with CONFIG_USB_EHCI_HCD=y
 * 2. boot args: earlyprintk=dbgp,keep
 *     o kernel compiled with # CONFIG_USB_EHCI_HCD is not set
 *     o kernel compiled with CONFIG_USB_EHCI_HCD=y
 * 3. boot args: earlyprintk=dbgp console=ttyUSB0
 *     o kernel has CONFIG_USB_EHCI_HCD=y and
 *       CONFIG_USB_SERIAL_DEBUG=y
 * 4. boot args: earlyprintk=vga,dbgp
 *     o kernel compiled with # CONFIG_USB_EHCI_HCD is not set
 *     o kernel compiled with CONFIG_USB_EHCI_HCD=y
 *
 * For the 4th configuration you can turn on or off the DBGP_DEBUG
 * such that you can debug the dbgp device's driver code.
 */


static int dbgp_phys_port = 1;


static struct ehci_caps __iomem *ehci_caps;

static struct ehci_regs __iomem *ehci_regs;

static struct ehci_dbg_port __iomem *ehci_debug;

static int dbgp_not_safe; 
/* Cannot use debug device during ehci reset */

static unsigned int dbgp_endpoint_out;

static unsigned int dbgp_endpoint_in;


struct ehci_dev {
	
u32 bus;
	
u32 slot;
	
u32 func;
};


static struct ehci_dev ehci_dev;


#define USB_DEBUG_DEVNUM 127

#ifdef DBGP_DEBUG

#define dbgp_printk printk

static void dbgp_ehci_status(char *str) { if (!ehci_debug) return; dbgp_printk("dbgp: %s\n", str); dbgp_printk(" Debug control: %08x", readl(&ehci_debug->control)); dbgp_printk(" ehci cmd : %08x", readl(&ehci_regs->command)); dbgp_printk(" ehci conf flg: %08x\n", readl(&ehci_regs->configured_flag)); dbgp_printk(" ehci status : %08x", readl(&ehci_regs->status)); dbgp_printk(" ehci portsc : %08x\n", readl(&ehci_regs->port_status[dbgp_phys_port - 1])); }

Contributors

PersonTokensPropCommitsCommitProp
jason wesseljason wessel93100.00%1100.00%
Total93100.00%1100.00%

#else
static inline void dbgp_ehci_status(char *str) { }

Contributors

PersonTokensPropCommitsCommitProp
jason wesseljason wessel10100.00%1100.00%
Total10100.00%1100.00%


static inline void dbgp_printk(const char *fmt, ...) { }

Contributors

PersonTokensPropCommitsCommitProp
jason wesseljason wessel13100.00%1100.00%
Total13100.00%1100.00%

#endif
static inline u32 dbgp_len_update(u32 x, u32 len) { return (x & ~0x0f) | (len & 0x0f); }

Contributors

PersonTokensPropCommitsCommitProp
jason wesseljason wessel27100.00%1100.00%
Total27100.00%1100.00%

#ifdef CONFIG_KGDB static struct kgdb_io kgdbdbgp_io_ops; #define dbgp_kgdb_mode (dbg_io_ops == &kgdbdbgp_io_ops) #else #define dbgp_kgdb_mode (0) #endif /* Local version of HC_LENGTH macro as ehci struct is not available here */ #define EARLY_HC_LENGTH(p) (0x00ff & (p)) /* bits 7 : 0 */ /* * USB Packet IDs (PIDs) */ /* token */ #define USB_PID_OUT 0xe1 #define USB_PID_IN 0x69 #define USB_PID_SOF 0xa5 #define USB_PID_SETUP 0x2d /* handshake */ #define USB_PID_ACK 0xd2 #define USB_PID_NAK 0x5a #define USB_PID_STALL 0x1e #define USB_PID_NYET 0x96 /* data */ #define USB_PID_DATA0 0xc3 #define USB_PID_DATA1 0x4b #define USB_PID_DATA2 0x87 #define USB_PID_MDATA 0x0f /* Special */ #define USB_PID_PREAMBLE 0x3c #define USB_PID_ERR 0x3c #define USB_PID_SPLIT 0x78 #define USB_PID_PING 0xb4 #define USB_PID_UNDEF_0 0xf0 #define USB_PID_DATA_TOGGLE 0x88 #define DBGP_CLAIM (DBGP_OWNER | DBGP_ENABLED | DBGP_INUSE) #define PCI_CAP_ID_EHCI_DEBUG 0xa #define HUB_ROOT_RESET_TIME 50 /* times are in msec */ #define HUB_SHORT_RESET_TIME 10 #define HUB_LONG_RESET_TIME 200 #define HUB_RESET_TIMEOUT 500 #define DBGP_MAX_PACKET 8 #define DBGP_TIMEOUT (250 * 1000) #define DBGP_LOOPS 1000
static inline u32 dbgp_pid_write_update(u32 x, u32 tok) { static int data0 = USB_PID_DATA1; data0 ^= USB_PID_DATA_TOGGLE; return (x & 0xffff0000) | (data0 << 8) | (tok & 0xff); }

Contributors

PersonTokensPropCommitsCommitProp
jason wesseljason wessel42100.00%1100.00%
Total42100.00%1100.00%


static inline u32 dbgp_pid_read_update(u32 x, u32 tok) { return (x & 0xffff0000) | (USB_PID_DATA0 << 8) | (tok & 0xff); }

Contributors

PersonTokensPropCommitsCommitProp
jason wesseljason wessel32100.00%1100.00%
Total32100.00%1100.00%


static int dbgp_wait_until_complete(void) { u32 ctrl; int loop = DBGP_TIMEOUT; do { ctrl = readl(&ehci_debug->control); /* Stop when the transaction is finished */ if (ctrl & DBGP_DONE) break; udelay(1); } while (--loop > 0); if (!loop) return -DBGP_TIMEOUT; /* * Now that we have observed the completed transaction, * clear the done bit. */ writel(ctrl | DBGP_DONE, &ehci_debug->control); return (ctrl & DBGP_ERROR) ? -DBGP_ERRCODE(ctrl) : DBGP_LEN(ctrl); }

Contributors

PersonTokensPropCommitsCommitProp
jason wesseljason wessel90100.00%2100.00%
Total90100.00%2100.00%


static inline void dbgp_mdelay(int ms) { int i; while (ms--) { for (i = 0; i < 1000; i++) outb(0x1, 0x80); } }

Contributors

PersonTokensPropCommitsCommitProp
jason wesseljason wessel40100.00%2100.00%
Total40100.00%2100.00%


static void dbgp_breath(void) { /* Sleep to give the debug port a chance to breathe */ }

Contributors

PersonTokensPropCommitsCommitProp
jason wesseljason wessel9100.00%1100.00%
Total9100.00%1100.00%


static int dbgp_wait_until_done(unsigned ctrl, int loop) { u32 pids, lpid; int ret; retry: writel(ctrl | DBGP_GO, &ehci_debug->control); ret = dbgp_wait_until_complete(); pids = readl(&ehci_debug->pids); lpid = DBGP_PID_GET(pids); if (ret < 0) { /* A -DBGP_TIMEOUT failure here means the device has * failed, perhaps because it was unplugged, in which * case we do not want to hang the system so the dbgp * will be marked as unsafe to use. EHCI reset is the * only way to recover if you unplug the dbgp device. */ if (ret == -DBGP_TIMEOUT && !dbgp_not_safe) dbgp_not_safe = 1; if (ret == -DBGP_ERR_BAD && --loop > 0) goto retry; return ret; } /* * If the port is getting full or it has dropped data * start pacing ourselves, not necessary but it's friendly. */ if ((lpid == USB_PID_NAK) || (lpid == USB_PID_NYET)) dbgp_breath(); /* If I get a NACK reissue the transmission */ if (lpid == USB_PID_NAK) { if (--loop > 0) goto retry; } return ret; }

Contributors

PersonTokensPropCommitsCommitProp
jason wesseljason wessel137100.00%4100.00%
Total137100.00%4100.00%


static inline void dbgp_set_data(const void *buf, int size) { const unsigned char *bytes = buf; u32 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->data03); writel(hi, &ehci_debug->data47); }

Contributors

PersonTokensPropCommitsCommitProp
jason wesseljason wessel118100.00%2100.00%
Total118100.00%2100.00%


static inline void dbgp_get_data(void *buf, int size) { unsigned char *bytes = buf; u32 lo, hi; int i; lo = readl(&ehci_debug->data03); hi = readl(&ehci_debug->data47); 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; }

Contributors

PersonTokensPropCommitsCommitProp
jason wesseljason wessel118100.00%2100.00%
Total118100.00%2100.00%


static int dbgp_bulk_write(unsigned devnum, unsigned endpoint, const char *bytes, int size) { int ret; u32 addr; u32 pids, ctrl; if (size > DBGP_MAX_PACKET) return -1; addr = DBGP_EPADDR(devnum, endpoint); pids = readl(&ehci_debug->pids); pids = dbgp_pid_write_update(pids, USB_PID_OUT); ctrl = readl(&ehci_debug->control); ctrl = dbgp_len_update(ctrl, size); ctrl |= DBGP_OUT; ctrl |= DBGP_GO; dbgp_set_data(bytes, size); writel(addr, &ehci_debug->address); writel(pids, &ehci_debug->pids); ret = dbgp_wait_until_done(ctrl, DBGP_LOOPS); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
jason wesseljason wessel135100.00%4100.00%
Total135100.00%4100.00%


static int dbgp_bulk_read(unsigned devnum, unsigned endpoint, void *data, int size, int loops) { u32 pids, addr, ctrl; int ret; if (size > DBGP_MAX_PACKET) return -1; addr = DBGP_EPADDR(devnum, endpoint); pids = readl(&ehci_debug->pids); pids = dbgp_pid_read_update(pids, USB_PID_IN); ctrl = readl(&ehci_debug->control); ctrl = dbgp_len_update(ctrl, size); ctrl &= ~DBGP_OUT; ctrl |= DBGP_GO; writel(addr, &ehci_debug->address); writel(pids, &ehci_debug->pids); ret = dbgp_wait_until_done(ctrl, loops); if (ret < 0) return ret; if (size > ret) size = ret; dbgp_get_data(data, size); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
jason wesseljason wessel156100.00%3100.00%
Total156100.00%3100.00%


static int dbgp_control_msg(unsigned devnum, int requesttype, int request, int value, int index, void *data, int size) { u32 pids, addr, ctrl; struct usb_ctrlrequest req; int read; int ret; read = (requesttype & USB_DIR_IN) != 0; if (size > (read ? DBGP_MAX_PACKET : 0)) return -1; /* Compute the control message */ req.bRequestType = requesttype; req.bRequest = request; req.wValue = cpu_to_le16(value); req.wIndex = cpu_to_le16(index); req.wLength = cpu_to_le16(size); pids = DBGP_PID_SET(USB_PID_DATA0, USB_PID_SETUP); addr = DBGP_EPADDR(devnum, 0); ctrl = readl(&ehci_debug->control); ctrl = dbgp_len_update(ctrl, sizeof(req)); ctrl |= DBGP_OUT; ctrl |= DBGP_GO; /* Send the setup message */ dbgp_set_data(&req, sizeof(req)); writel(addr, &ehci_debug->address); writel(pids, &ehci_debug->pids); ret = dbgp_wait_until_done(ctrl, DBGP_LOOPS); if (ret < 0) return ret; /* Read the result */ return dbgp_bulk_read(devnum, 0, data, size, DBGP_LOOPS); }

Contributors

PersonTokensPropCommitsCommitProp
jason wesseljason wessel224100.00%2100.00%
Total224100.00%2100.00%

/* Find a PCI capability */
static u32 __init find_cap(u32 num, u32 slot, u32 func, int cap) { u8 pos; int bytes; if (!(read_pci_config_16(num, slot, func, PCI_STATUS) & PCI_STATUS_CAP_LIST)) return 0; pos = read_pci_config_byte(num, slot, func, PCI_CAPABILITY_LIST); for (bytes = 0; bytes < 48 && pos >= 0x40; bytes++) { u8 id; pos &= ~3; id = read_pci_config_byte(num, slot, func, pos+PCI_CAP_LIST_ID); if (id == 0xff) break; if (id == cap) return pos; pos = read_pci_config_byte(num, slot, func, pos+PCI_CAP_LIST_NEXT); } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
jason wesseljason wessel135100.00%1100.00%
Total135100.00%1100.00%


static u32 __init __find_dbgp(u32 bus, u32 slot, u32 func) { u32 class; class = read_pci_config(bus, slot, func, PCI_CLASS_REVISION); if ((class >> 8) != PCI_CLASS_SERIAL_USB_EHCI) return 0; return find_cap(bus, slot, func, PCI_CAP_ID_EHCI_DEBUG); }

Contributors

PersonTokensPropCommitsCommitProp
jason wesseljason wessel57100.00%1100.00%
Total57100.00%1100.00%


static u32 __init find_dbgp(int ehci_num, u32 *rbus, u32 *rslot, u32 *rfunc) { u32 bus, slot, func; for (bus = 0; bus < 256; bus++) { for (slot = 0; slot < 32; slot++) { for (func = 0; func < 8; func++) { unsigned cap; cap = __find_dbgp(bus, slot, func); if (!cap) continue; if (ehci_num-- != 0) continue; *rbus = bus; *rslot = slot; *rfunc = func; return cap; } } } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
jason wesseljason wessel123100.00%1100.00%
Total123100.00%1100.00%


static int dbgp_ehci_startup(void) { u32 ctrl, cmd, status; int loop; /* Claim ownership, but do not enable yet */ ctrl = readl(&ehci_debug->control); ctrl |= DBGP_OWNER; ctrl &= ~(DBGP_ENABLED | DBGP_INUSE); writel(ctrl, &ehci_debug->control); udelay(1); dbgp_ehci_status("EHCI startup"); /* Start the ehci running */ cmd = readl(&ehci_regs->command); cmd &= ~(CMD_LRESET | CMD_IAAD | CMD_PSE | CMD_ASE | CMD_RESET); cmd |= CMD_RUN; writel(cmd, &ehci_regs->command); /* Ensure everything is routed to the EHCI */ writel(FLAG_CF, &ehci_regs->configured_flag); /* Wait until the controller is no longer halted */ loop = 1000; do { status = readl(&ehci_regs->status); if (!(status & STS_HALT)) break; udelay(1); } while (--loop > 0); if (!loop) { dbgp_printk("ehci can not be started\n"); return -ENODEV; } dbgp_printk("ehci started\n"); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
jason wesseljason wessel17799.44%375.00%
colin kingcolin king10.56%125.00%
Total178100.00%4100.00%


static int dbgp_ehci_controller_reset(void) { int loop = 250 * 1000; u32 cmd; /* Reset the EHCI controller */ cmd = readl(&ehci_regs->command); cmd |= CMD_RESET; writel(cmd, &ehci_regs->command); do { cmd = readl(&ehci_regs->command); } while ((cmd & CMD_RESET) && (--loop > 0)); if (!loop) { dbgp_printk("can not reset ehci\n"); return -1; } dbgp_ehci_status("ehci reset done"); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
jason wesseljason wessel96100.00%3100.00%
Total96100.00%3100.00%

static int ehci_wait_for_port(int port); /* Return 0 on success * Return -ENODEV for any general failure * Return -EIO if wait for port fails */
static int _dbgp_external_startup(void) { int devnum; struct usb_debug_descriptor dbgp_desc; int ret; u32 ctrl, portsc, cmd; int dbg_port = dbgp_phys_port; int tries = 3; int reset_port_tries = 1; int try_hard_once = 1; try_port_reset_again: ret = dbgp_ehci_startup(); if (ret) return ret; /* Wait for a device to show up in the debug port */ ret = ehci_wait_for_port(dbg_port); if (ret < 0) { portsc = readl(&ehci_regs->port_status[dbg_port - 1]); if (!(portsc & PORT_CONNECT) && try_hard_once) { /* Last ditch effort to try to force enable * the debug device by using the packet test * ehci command to try and wake it up. */ try_hard_once = 0; cmd = readl(&ehci_regs->command); cmd &= ~CMD_RUN; writel(cmd, &ehci_regs->command); portsc = readl(&ehci_regs->port_status[dbg_port - 1]); portsc |= PORT_TEST_PKT; writel(portsc, &ehci_regs->port_status[dbg_port - 1]); dbgp_ehci_status("Trying to force debug port online"); mdelay(50); dbgp_ehci_controller_reset(); goto try_port_reset_again; } else if (reset_port_tries--) { goto try_port_reset_again; } dbgp_printk("No device found in debug port\n"); return -EIO; } dbgp_ehci_status("wait for port done"); /* Enable the debug port */ ctrl = readl(&ehci_debug->control); ctrl |= DBGP_CLAIM; writel(ctrl, &ehci_debug->control); ctrl = readl(&ehci_debug->control); if ((ctrl & DBGP_CLAIM) != DBGP_CLAIM) { dbgp_printk("No device in debug port\n"); writel(ctrl & ~DBGP_CLAIM, &ehci_debug->control); return -ENODEV; } dbgp_ehci_status("debug ported enabled"); /* Completely transfer the debug device to the debug controller */ portsc = readl(&ehci_regs->port_status[dbg_port - 1]); portsc &= ~PORT_PE; writel(portsc, &ehci_regs->port_status[dbg_port - 1]); dbgp_mdelay(100); try_again: /* Find the debug device and make it device number 127 */ for (devnum = 0; devnum <= 127; devnum++) { ret = dbgp_control_msg(devnum, USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE, USB_REQ_GET_DESCRIPTOR, (USB_DT_DEBUG << 8), 0, &dbgp_desc, sizeof(dbgp_desc)); if (ret > 0) break; } if (devnum > 127) { dbgp_printk("Could not find attached debug device\n"); goto err; } dbgp_endpoint_out = dbgp_desc.bDebugOutEndpoint; dbgp_endpoint_in = dbgp_desc.bDebugInEndpoint; /* Move the device to 127 if it isn't already there */ if (devnum != USB_DEBUG_DEVNUM) { ret = dbgp_control_msg(devnum, USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE, USB_REQ_SET_ADDRESS, USB_DEBUG_DEVNUM, 0, NULL, 0); if (ret < 0) { dbgp_printk("Could not move attached device to %d\n", USB_DEBUG_DEVNUM); goto err; } devnum = USB_DEBUG_DEVNUM; dbgp_printk("debug device renamed to 127\n"); } /* Enable the debug interface */ ret = dbgp_control_msg(USB_DEBUG_DEVNUM, USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE, USB_REQ_SET_FEATURE, USB_DEVICE_DEBUG_MODE, 0, NULL, 0); if (ret < 0) { dbgp_printk(" Could not enable the debug device\n"); goto err; } dbgp_printk("debug interface enabled\n"); /* Perform a small write to get the even/odd data state in sync */ ret = dbgp_bulk_write(USB_DEBUG_DEVNUM, dbgp_endpoint_out, " ", 1); if (ret < 0) { dbgp_printk("dbgp_bulk_write failed: %d\n", ret); goto err; } dbgp_printk("small write done\n"); dbgp_not_safe = 0; return 0; err: if (tries--) goto try_again; return -ENODEV; }

Contributors

PersonTokensPropCommitsCommitProp
jason wesseljason wessel56699.47%571.43%
jan beulichjan beulich20.35%114.29%
wagner ferencwagner ferenc10.18%114.29%
Total569100.00%7100.00%


static int ehci_reset_port(int port) { u32 portsc; u32 delay_time, delay; int loop; dbgp_ehci_status("reset port"); /* Reset the usb debug port */ portsc = readl(&ehci_regs->port_status[port - 1]); portsc &= ~PORT_PE; portsc |= PORT_RESET; writel(portsc, &ehci_regs->port_status[port - 1]); delay = HUB_ROOT_RESET_TIME; for (delay_time = 0; delay_time < HUB_RESET_TIMEOUT; delay_time += delay) { dbgp_mdelay(delay); portsc = readl(&ehci_regs->port_status[port - 1]); if (!(portsc & PORT_RESET)) break; } if (portsc & PORT_RESET) { /* force reset to complete */ loop = 100 * 1000; writel(portsc & ~(PORT_RWC_BITS | PORT_RESET), &ehci_regs->port_status[port - 1]); do { udelay(1); portsc = readl(&ehci_regs->port_status[port-1]); } while ((portsc & PORT_RESET) && (--loop > 0)); } /* Device went away? */ if (!(portsc & PORT_CONNECT)) return -ENOTCONN; /* bomb out completely if something weird happened */ if ((portsc & PORT_CSC)) return -EINVAL; /* If we've finished resetting, then break out of the loop */ if (!(portsc & PORT_RESET) && (portsc & PORT_PE)) return 0; return -EBUSY; }

Contributors

PersonTokensPropCommitsCommitProp
jason wesseljason wessel24099.59%266.67%
lucas de marchilucas de marchi10.41%133.33%
Total241100.00%3100.00%


static int ehci_wait_for_port(int port) { u32 status; int ret, reps; for (reps = 0; reps < 300; reps++) { status = readl(&ehci_regs->status); if (status & STS_PCD) break; dbgp_mdelay(1); } ret = ehci_reset_port(port); if (ret == 0) return 0; return -ENOTCONN; }

Contributors

PersonTokensPropCommitsCommitProp
jason wesseljason wessel74100.00%2100.00%
Total74100.00%2100.00%

typedef void (*set_debug_port_t)(int port);
static void __init default_set_debug_port(int port) { }

Contributors

PersonTokensPropCommitsCommitProp
jason wesseljason wessel9100.00%1100.00%
Total9100.00%1100.00%

static set_debug_port_t __initdata set_debug_port = default_set_debug_port;
static void __init nvidia_set_debug_port(int port) { u32 dword; dword = read_pci_config(ehci_dev.bus, ehci_dev.slot, ehci_dev.func, 0x74); dword &= ~(0x0f<<12); dword |= ((port & 0x0f)<<12); write_pci_config(ehci_dev.bus, ehci_dev.slot, ehci_dev.func, 0x74, dword); dbgp_printk("set debug port to %d\n", port); }

Contributors

PersonTokensPropCommitsCommitProp
jason wesseljason wessel79100.00%3100.00%
Total79100.00%3100.00%


static void __init detect_set_debug_port(void) { u32 vendorid; vendorid = read_pci_config(ehci_dev.bus, ehci_dev.slot, ehci_dev.func, 0x00); if ((vendorid & 0xffff) == 0x10de) { dbgp_printk("using nvidia set_debug_port\n"); set_debug_port = nvidia_set_debug_port; } }

Contributors

PersonTokensPropCommitsCommitProp
jason wesseljason wessel52100.00%2100.00%
Total52100.00%2100.00%

/* The code in early_ehci_bios_handoff() is derived from the usb pci * quirk initialization, but altered so as to use the early PCI * routines. */ #define EHCI_USBLEGSUP_BIOS (1 << 16) /* BIOS semaphore */ #define EHCI_USBLEGCTLSTS 4 /* legacy control/status */
static void __init early_ehci_bios_handoff(void) { u32 hcc_params = readl(&ehci_caps->hcc_params); int offset = (hcc_params >> 8) & 0xff; u32 cap; int msec; if (!offset) return; cap = read_pci_config(ehci_dev.bus, ehci_dev.slot, ehci_dev.func, offset); dbgp_printk("dbgp: ehci BIOS state %08x\n", cap); if ((cap & 0xff) == 1 && (cap & EHCI_USBLEGSUP_BIOS)) { dbgp_printk("dbgp: BIOS handoff\n"); write_pci_config_byte(ehci_dev.bus, ehci_dev.slot, ehci_dev.func, offset + 3, 1); } /* if boot firmware now owns EHCI, spin till it hands it over. */ msec = 1000; while ((cap & EHCI_USBLEGSUP_BIOS) && (msec > 0)) { mdelay(10); msec -= 10; cap = read_pci_config(ehci_dev.bus, ehci_dev.slot, ehci_dev.func, offset); } if (cap & EHCI_USBLEGSUP_BIOS) { /* well, possibly buggy BIOS... try to shut it down, * and hope nothing goes too wrong */ dbgp_printk("dbgp: BIOS handoff failed: %08x\n", cap); write_pci_config_byte(ehci_dev.bus, ehci_dev.slot, ehci_dev.func, offset + 2, 0); } /* just in case, always disable EHCI SMIs */ write_pci_config_byte(ehci_dev.bus, ehci_dev.slot, ehci_dev.func, offset + EHCI_USBLEGCTLSTS, 0); }

Contributors

PersonTokensPropCommitsCommitProp
jason wesseljason wessel221100.00%3100.00%
Total221100.00%3100.00%


static int __init ehci_setup(void) { u32 ctrl, portsc, hcs_params; u32 debug_port, new_debug_port = 0, n_ports; int ret, i; int port_map_tried; int playtimes = 3; early_ehci_bios_handoff(); try_next_time: port_map_tried = 0; try_next_port: hcs_params = readl(&ehci_caps->hcs_params); debug_port = HCS_DEBUG_PORT(hcs_params); dbgp_phys_port = debug_port; n_ports = HCS_N_PORTS(hcs_params); dbgp_printk("debug_port: %d\n", debug_port); dbgp_printk("n_ports: %d\n", n_ports); dbgp_ehci_status(""); for (i = 1; i <= n_ports; i++) { portsc = readl(&ehci_regs->port_status[i-1]); dbgp_printk("portstatus%d: %08x\n", i, portsc); } if (port_map_tried && (new_debug_port != debug_port)) { if (--playtimes) { set_debug_port(new_debug_port); goto try_next_time; } return -1; } /* Only reset the controller if it is not already in the * configured state */ if (!(readl(&ehci_regs->configured_flag) & FLAG_CF)) { if (dbgp_ehci_controller_reset() != 0) return -1; } else { dbgp_ehci_status("ehci skip - already configured"); } ret = _dbgp_external_startup(); if (ret == -EIO) goto next_debug_port; if (ret < 0) { /* Things didn't work so remove my claim */ ctrl = readl(&ehci_debug->control); ctrl &= ~(DBGP_CLAIM | DBGP_OUT); writel(ctrl, &ehci_debug->control); return -1; } return 0; next_debug_port: port_map_tried |= (1<<(debug_port - 1)); new_debug_port = ((debug_port-1+1)%n_ports) + 1; if (port_map_tried != ((1<<n_ports) - 1)) { set_debug_port(new_debug_port); goto try_next_port; } if (--playtimes) { set_debug_port(new_debug_port); goto try_next_time; } return -1; }

Contributors

PersonTokensPropCommitsCommitProp
jason wesseljason wessel33599.70%266.67%
jan beulichjan beulich10.30%133.33%
Total336100.00%3100.00%


int __init early_dbgp_init(char *s) { u32 debug_port, bar, offset; u32 bus, slot, func, cap; void __iomem *ehci_bar; u32 dbgp_num; u32 bar_val; char *e; int ret; u8 byte; if (!early_pci_allowed()) return -1; dbgp_num = 0; if (*s) dbgp_num = simple_strtoul(s, &e, 10); dbgp_printk("dbgp_num: %d\n", dbgp_num); cap = find_dbgp(dbgp_num, &bus, &slot, &func); if (!cap) return -1; dbgp_printk("Found EHCI debug port on %02x:%02x.%1x\n", bus, slot, func); debug_port = read_pci_config(bus, slot, func, cap); bar = (debug_port >> 29) & 0x7; bar = (bar * 4) + 0xc; offset = (debug_port >> 16) & 0xfff; dbgp_printk("bar: %02x offset: %03x\n", bar, offset); if (bar != PCI_BASE_ADDRESS_0) { dbgp_printk("only debug ports on bar 1 handled.\n"); return -1; } bar_val = read_pci_config(bus, slot, func, PCI_BASE_ADDRESS_0); dbgp_printk("bar_val: %02x offset: %03x\n", bar_val, offset); if (bar_val & ~PCI_BASE_ADDRESS_MEM_MASK) { dbgp_printk("only simple 32bit mmio bars supported\n"); return -1; } /* double check if the mem space is enabled */ byte = read_pci_config_byte(bus, slot, func, 0x04); if (!(byte & 0x2)) { byte |= 0x02; write_pci_config_byte(bus, slot, func, 0x04, byte); dbgp_printk("mmio for ehci enabled\n"); } /* * FIXME I don't have the bar size so just guess PAGE_SIZE is more * than enough. 1K is the biggest I have seen. */ set_fixmap_nocache(FIX_DBGP_BASE, bar_val & PAGE_MASK); ehci_bar = (void __iomem *)__fix_to_virt(FIX_DBGP_BASE); ehci_bar += bar_val & ~PAGE_MASK; dbgp_printk("ehci_bar: %p\n", ehci_bar); ehci_caps = ehci_bar; ehci_regs = ehci_bar + EARLY_HC_LENGTH(readl(&ehci_caps->hc_capbase)); ehci_debug = ehci_bar + offset; ehci_dev.bus = bus; ehci_dev.slot = slot; ehci_dev.func = func; detect_set_debug_port(); ret = ehci_setup(); if (ret < 0) { dbgp_printk("ehci_setup failed\n"); ehci_debug = NULL; return -1; } dbgp_ehci_status("early_init_complete"); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
jason wesseljason wessel39299.75%266.67%
jan anderssonjan andersson10.25%133.33%
Total393100.00%3100.00%


static void early_dbgp_write(struct console *con, const char *str, u32 n) { int chunk, ret; char buf[DBGP_MAX_PACKET]; int use_cr = 0; u32 cmd, ctrl; int reset_run = 0; if (!ehci_debug || dbgp_not_safe) return; cmd = readl(&ehci_regs->command); if (unlikely(!(cmd & CMD_RUN))) { /* If the ehci controller is not in the run state do extended * checks to see if the acpi or some other initialization also * reset the ehci debug port */ ctrl = readl(&ehci_debug->control); if (!(ctrl & DBGP_ENABLED)) { dbgp_not_safe = 1; _dbgp_external_startup(); } else { cmd |= CMD_RUN; writel(cmd, &ehci_regs->command); reset_run = 1; } } while (n > 0) { for (chunk = 0; chunk < DBGP_MAX_PACKET && n > 0; str++, chunk++, n--) { if (!use_cr && *str == '\n') { use_cr = 1; buf[chunk] = '\r'; str--; n++; continue; } if (use_cr) use_cr = 0; buf[chunk] = *str; } if (chunk > 0) { ret = dbgp_bulk_write(USB_DEBUG_DEVNUM, dbgp_endpoint_out, buf, chunk); } } if (unlikely(reset_run)) { cmd = readl(&ehci_regs->command); cmd &= ~CMD_RUN; writel(cmd, &ehci_regs->command); } }

Contributors

PersonTokensPropCommitsCommitProp
jason wesseljason wessel26099.62%375.00%
jan beulichjan beulich10.38%125.00%
Total261100.00%4100.00%

struct console early_dbgp_console = { .name = "earlydbg", .write = early_dbgp_write, .flags = CON_PRINTBUFFER, .index = -1, }; #if IS_ENABLED(CONFIG_USB)
int dbgp_reset_prep(struct usb_hcd *hcd) { int ret = xen_dbgp_reset_prep(hcd); u32 ctrl; if (ret) return ret; dbgp_not_safe = 1; if (!ehci_debug) return 0; if ((early_dbgp_console.index != -1 && !(early_dbgp_console.flags & CON_BOOT)) || dbgp_kgdb_mode) return 1; /* This means the console is not initialized, or should get * shutdown so as to allow for reuse of the usb device, which * means it is time to shutdown the usb debug port. */ ctrl = readl(&ehci_debug->control); if (ctrl & DBGP_ENABLED) { ctrl &= ~(DBGP_CLAIM); writel(ctrl, &ehci_debug->control); } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
jason wesseljason wessel8581.73%266.67%
jan beulichjan beulich1918.27%133.33%
Total104100.00%3100.00%

EXPORT_SYMBOL_GPL(dbgp_reset_prep);
int dbgp_external_startup(struct usb_hcd *hcd) { return xen_dbgp_external_startup(hcd) ?: _dbgp_external_startup(); }

Contributors

PersonTokensPropCommitsCommitProp
jan beulichjan beulich20100.00%1100.00%
Total20100.00%1100.00%

EXPORT_SYMBOL_GPL(dbgp_external_startup); #endif /* USB */ #ifdef CONFIG_KGDB static char kgdbdbgp_buf[DBGP_MAX_PACKET]; static int kgdbdbgp_buf_sz; static int kgdbdbgp_buf_idx; static int kgdbdbgp_loop_cnt = DBGP_LOOPS;
static int kgdbdbgp_read_char(void) { int ret; if (kgdbdbgp_buf_idx < kgdbdbgp_buf_sz) { char ch = kgdbdbgp_buf[kgdbdbgp_buf_idx++]; return ch; } ret = dbgp_bulk_read(USB_DEBUG_DEVNUM, dbgp_endpoint_in, &kgdbdbgp_buf, DBGP_MAX_PACKET, kgdbdbgp_loop_cnt); if (ret <= 0) return NO_POLL_CHAR; kgdbdbgp_buf_sz = ret; kgdbdbgp_buf_idx = 1; return kgdbdbgp_buf[0]; }

Contributors

PersonTokensPropCommitsCommitProp
jason wesseljason wessel70100.00%1100.00%
Total70100.00%1100.00%


static void kgdbdbgp_write_char(u8 chr) { early_dbgp_write(NULL, &chr, 1); }

Contributors

PersonTokensPropCommitsCommitProp
jason wesseljason wessel19100.00%1100.00%
Total19100.00%1100.00%

static struct kgdb_io kgdbdbgp_io_ops = { .name = "kgdbdbgp", .read_char = kgdbdbgp_read_char, .write_char = kgdbdbgp_write_char, }; static int kgdbdbgp_wait_time;
static int __init kgdbdbgp_parse_config(char *str) { char *ptr; if (!ehci_debug) { if (early_dbgp_init(str)) return -1; } ptr = strchr(str, ','); if (ptr) { ptr++; kgdbdbgp_wait_time = simple_strtoul(ptr, &ptr, 10); } kgdb_register_io_module(&kgdbdbgp_io_ops); kgdbdbgp_io_ops.is_console = early_dbgp_console.index != -1; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
jason wesseljason wessel83100.00%1100.00%
Total83100.00%1100.00%

early_param("kgdbdbgp", kgdbdbgp_parse_config);
static int kgdbdbgp_reader_thread(void *ptr) { int ret; while (readl(&ehci_debug->control) & DBGP_ENABLED) { kgdbdbgp_loop_cnt = 1; ret = kgdbdbgp_read_char(); kgdbdbgp_loop_cnt = DBGP_LOOPS; if (ret != NO_POLL_CHAR) { if (ret == 0x3 || ret == '$') { if (ret == '$') kgdbdbgp_buf_idx--; kgdb_breakpoint(); } continue; } schedule_timeout_interruptible(kgdbdbgp_wait_time * HZ); } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
jason wesseljason wessel83100.00%1100.00%
Total83100.00%1100.00%


static int __init kgdbdbgp_start_thread(void) { if (dbgp_kgdb_mode && kgdbdbgp_wait_time) kthread_run(kgdbdbgp_reader_thread, NULL, "%s", "dbgp"); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
jason wesseljason wessel29100.00%1100.00%
Total29100.00%1100.00%

module_init(kgdbdbgp_start_thread); #endif /* CONFIG_KGDB */

Overall Contributors

PersonTokensPropCommitsCommitProp
jason wesseljason wessel481998.51%1058.82%
jan beulichjan beulich601.23%317.65%
jan anderssonjan andersson100.20%15.88%
colin kingcolin king10.02%15.88%
lucas de marchilucas de marchi10.02%15.88%
wagner ferencwagner ferenc10.02%15.88%
Total4892100.00%17100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
{% endraw %}