/*
* 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/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]));
}
#else
static inline void dbgp_ehci_status(char *str) { }
static inline void dbgp_printk(const char *fmt, ...) { }
#endif
static inline u32 dbgp_len_update(u32 x, u32 len)
{
return (x & ~0x0f) | (len & 0x0f);
}
#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);
}
static inline u32 dbgp_pid_read_update(u32 x, u32 tok)
{
return (x & 0xffff0000) | (USB_PID_DATA0 << 8) | (tok & 0xff);
}
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