// SPDX-License-Identifier: GPL-2.0 /* * 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. *
*/
/* 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.
*/
staticint dbgp_wait_until_complete(void)
{
u32 ctrl; int ret;
ret = readl_poll_timeout_atomic(&ehci_debug->control, ctrl,
(ctrl & DBGP_DONE), 1, DBGP_TIMEOUT); if (ret) 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);
}
staticinlinevoid dbgp_mdelay(int ms)
{ int i;
while (ms--) { for (i = 0; i < 1000; i++)
outb(0x1, 0x80);
}
}
staticvoid dbgp_breath(void)
{ /* Sleep to give the debug port a chance to breathe */
}
staticint dbgp_wait_until_done(unsigned ctrl, int loop)
{
u32 pids, lpid; int ret;
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;
}
staticinlinevoid dbgp_set_data(constvoid *buf, int size)
{ constunsignedchar *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);
}
staticinlinevoid dbgp_get_data(void *buf, int size)
{ unsignedchar *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;
}
staticint dbgp_bulk_write(unsigned devnum, unsigned endpoint, constchar *bytes, int size)
{ int ret;
u32 addr;
u32 pids, ctrl;
staticint 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;
/* 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;
}
/* 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;
} staticint ehci_wait_for_port(int port); /* Return 0 on success * Return -ENODEV for any general failure * Return -EIO if wait for port fails
*/ staticint _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;
} elseif (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;
}
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;
}
/* 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 */ staticvoid __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);
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;
/* 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);
staticvoid early_dbgp_write(struct console *con, constchar *str, u32 n)
{ int chunk; 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) {
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);
}
}
#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;
}
EXPORT_SYMBOL_GPL(dbgp_reset_prep);
int dbgp_external_startup(struct usb_hcd *hcd)
{ return xen_dbgp_external_startup(hcd) ?: _dbgp_external_startup();
}
EXPORT_SYMBOL_GPL(dbgp_external_startup); #endif/* USB */
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.