9ff8f6f818
This patch removes quite a bit of code duplication between cpu_to_le32() and clrsetbits_le32() style macros on the different architectures. This also syncs those macros back up to the new write32(a, v) style IO accessor macros that are now used on ARM and ARM64. CQ-DEPEND=CL:254862 BRANCH=none BUG=chromium:444723 TEST=Compiled Cosmos, Daisy, Blaze, Falco, Pinky, Pit, Rambi, Ryu, Storm and Urara. Booted on Jerry. Tried to compare binary images... unfortunately something about the new macro notation makes the compiler evaluate it more efficiently (not recalculating the address between the read and the write), so this was of limited value. Change-Id: If8ab62912c952d68a67a0f71e82b038732cd1317 Signed-off-by: Patrick Georgi <pgeorgi@chromium.org> Original-Commit-Id: fd43bf446581bfb84bec4f2ebb56b5de95971c3b Original-Change-Id: I7d301b5bb5ac0db7f5ff39e3adc2b28a1f402a72 Original-Signed-off-by: Julius Werner <jwerner@chromium.org> Original-Reviewed-on: https://chromium-review.googlesource.com/254866 Original-Reviewed-by: Aaron Durbin <adurbin@chromium.org> Reviewed-on: http://review.coreboot.org/9838 Tested-by: build bot (Jenkins) Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
715 lines
18 KiB
C
715 lines
18 KiB
C
/*
|
|
* This file is part of the coreboot project.
|
|
*
|
|
* Copyright (C) 2006 Eric Biederman (ebiederm@xmission.com)
|
|
* Copyright (C) 2007 AMD
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; version 2 of the License.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA
|
|
*/
|
|
|
|
#include <stddef.h>
|
|
#include <console/console.h>
|
|
#include <console/usb.h>
|
|
#include <arch/io.h>
|
|
#include <arch/early_variables.h>
|
|
#include <string.h>
|
|
#include <cbmem.h>
|
|
|
|
#include "ehci_debug.h"
|
|
#include "usb_ch9.h"
|
|
#include "ehci.h"
|
|
|
|
struct ehci_debug_info {
|
|
void *ehci_base;
|
|
void *ehci_debug;
|
|
|
|
struct dbgp_pipe ep_pipe[DBGP_MAX_ENDPOINTS];
|
|
};
|
|
|
|
#if CONFIG_DEBUG_USBDEBUG
|
|
static void dbgp_print_data(struct ehci_dbg_port *ehci_debug);
|
|
static int dbgp_enabled(void);
|
|
# define dprintk(LEVEL, args...) \
|
|
do { if (!dbgp_enabled()) printk(LEVEL, ##args); } while (0)
|
|
#else
|
|
# define dbgp_print_data(x) do {} while(0)
|
|
# define dprintk(LEVEL, args...) do {} while(0)
|
|
#endif
|
|
|
|
#define DBGP_LEN_UPDATE(x, len) (((x) & ~0x0f) | ((len) & 0x0f))
|
|
|
|
#define DBGP_CLAIM (DBGP_OWNER | DBGP_ENABLED | DBGP_INUSE)
|
|
|
|
#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_MICROFRAME_TIMEOUT_LOOPS 1000
|
|
#define DBGP_MICROFRAME_RETRIES 10
|
|
#define DBGP_MAX_PACKET 8
|
|
|
|
static struct ehci_debug_info glob_dbg_info CAR_GLOBAL;
|
|
static struct ehci_debug_info * glob_dbg_info_p CAR_GLOBAL;
|
|
|
|
static inline struct ehci_debug_info *dbgp_ehci_info(void)
|
|
{
|
|
if (car_get_var(glob_dbg_info_p) == NULL)
|
|
car_set_var(glob_dbg_info_p, &glob_dbg_info);
|
|
|
|
return car_get_var(glob_dbg_info_p);
|
|
}
|
|
|
|
static int dbgp_wait_until_complete(struct ehci_dbg_port *ehci_debug)
|
|
{
|
|
u32 ctrl;
|
|
int loop = 0;
|
|
|
|
do {
|
|
ctrl = read32(&ehci_debug->control);
|
|
/* Stop when the transaction is finished */
|
|
if (ctrl & DBGP_DONE)
|
|
break;
|
|
} while (++loop < DBGP_MICROFRAME_TIMEOUT_LOOPS);
|
|
|
|
if (! (ctrl & DBGP_DONE)) {
|
|
dprintk(BIOS_ERR, "dbgp_wait_until_complete: retry timeout.\n");
|
|
return -DBGP_ERR_SIGNAL;
|
|
}
|
|
|
|
/* Now that we have observed the completed transaction,
|
|
* clear the done bit.
|
|
*/
|
|
write32(&ehci_debug->control, ctrl | DBGP_DONE);
|
|
return (ctrl & DBGP_ERROR) ? -DBGP_ERRCODE(ctrl) : DBGP_LEN(ctrl);
|
|
}
|
|
|
|
static void dbgp_breath(void)
|
|
{
|
|
/* Sleep to give the debug port a chance to breathe */
|
|
}
|
|
|
|
static int dbgp_wait_until_done(struct ehci_dbg_port *ehci_debug, struct dbgp_pipe *pipe,
|
|
unsigned ctrl, const int timeout)
|
|
{
|
|
u32 rd_ctrl, rd_pids;
|
|
u32 ctrl_prev = 0, pids_prev = 0;
|
|
u8 lpid;
|
|
int ret, host_retries;
|
|
int loop;
|
|
|
|
loop = 0;
|
|
device_retry:
|
|
host_retries = 0;
|
|
if (loop++ >= timeout)
|
|
return -DBGP_ERR_BAD;
|
|
|
|
host_retry:
|
|
if (host_retries++ >= DBGP_MICROFRAME_RETRIES)
|
|
return -DBGP_ERR_BAD;
|
|
if (loop == 1 || host_retries > 1)
|
|
dprintk(BIOS_SPEW, "dbgp: start (@ %3d,%d) ctrl=%08x\n",
|
|
loop, host_retries, ctrl | DBGP_GO);
|
|
write32(&ehci_debug->control, ctrl | DBGP_GO);
|
|
ret = dbgp_wait_until_complete(ehci_debug);
|
|
rd_ctrl = read32(&ehci_debug->control);
|
|
rd_pids = read32(&ehci_debug->pids);
|
|
|
|
if (rd_ctrl != ctrl_prev || rd_pids != pids_prev || (ret<0)) {
|
|
ctrl_prev = rd_ctrl;
|
|
pids_prev = rd_pids;
|
|
dprintk(BIOS_SPEW, "dbgp: status (@ %3d,%d) ctrl=%08x pids=%08x ret=%d\n",
|
|
loop, host_retries, rd_ctrl, rd_pids, ret);
|
|
}
|
|
|
|
/* Controller hardware failure. */
|
|
if (ret == -DBGP_ERR_SIGNAL) {
|
|
return ret;
|
|
|
|
/* Bus failure (corrupted microframe). */
|
|
} else if (ret == -DBGP_ERR_BAD) {
|
|
goto host_retry;
|
|
}
|
|
|
|
lpid = DBGP_PID_GET(rd_pids);
|
|
|
|
/* If I get an ACK or in-sync DATA PID, we are done. */
|
|
if ((lpid == USB_PID_ACK) || (lpid == pipe->pid)) {
|
|
pipe->pid ^= USB_PID_DATA_TOGGLE;
|
|
}
|
|
|
|
/* If the port is getting full or it has dropped data
|
|
* start pacing ourselves, not necessary but it's friendly.
|
|
*/
|
|
else if (lpid == USB_PID_NYET) {
|
|
dbgp_breath();
|
|
goto device_retry;
|
|
}
|
|
|
|
/* If I get a NACK or out-of-sync DATA PID, reissue the transmission. */
|
|
else if ((lpid == USB_PID_NAK) || (lpid == (pipe->pid ^ USB_PID_DATA_TOGGLE))) {
|
|
goto device_retry;
|
|
}
|
|
|
|
/* Abort on STALL handshake for endpoint 0.*/
|
|
else if ((lpid == USB_PID_STALL) && (pipe->endpoint == 0x0)) {
|
|
ret = -DBGP_ERR_BAD;
|
|
}
|
|
|
|
dbgp_print_data(ehci_debug);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void dbgp_set_data(struct ehci_dbg_port *ehci_debug, 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));
|
|
write32(&ehci_debug->data03, lo);
|
|
write32(&ehci_debug->data47, hi);
|
|
}
|
|
|
|
static void dbgp_get_data(struct ehci_dbg_port *ehci_debug, void *buf, int size)
|
|
{
|
|
unsigned char *bytes = buf;
|
|
u32 lo, hi;
|
|
int i;
|
|
|
|
lo = read32(&ehci_debug->data03);
|
|
hi = read32(&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;
|
|
}
|
|
|
|
#if CONFIG_DEBUG_USBDEBUG
|
|
static void dbgp_print_data(struct ehci_dbg_port *ehci_debug)
|
|
{
|
|
u32 ctrl = read32(&ehci_debug->control);
|
|
u32 lo = read32(&ehci_debug->data03);
|
|
u32 hi = read32(&ehci_debug->data47);
|
|
int len = DBGP_LEN(ctrl);
|
|
if (len) {
|
|
int i;
|
|
dprintk(BIOS_SPEW, "dbgp: buf:");
|
|
for (i = 0; i < 4 && i < len; i++)
|
|
dprintk(BIOS_SPEW, " %02x", (lo >> (8*i)) & 0xff);
|
|
for (; i < 8 && i < len; i++)
|
|
dprintk(BIOS_SPEW, " %02x", (hi >> (8*(i - 4))) & 0xff);
|
|
dprintk(BIOS_SPEW, "\n");
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static int dbgp_bulk_write(struct ehci_dbg_port *ehci_debug, struct dbgp_pipe *pipe,
|
|
const char *bytes, int size)
|
|
{
|
|
u32 pids, addr, ctrl;
|
|
int ret;
|
|
|
|
if (size > DBGP_MAX_PACKET)
|
|
return -1;
|
|
|
|
addr = DBGP_EPADDR(pipe->devnum, pipe->endpoint);
|
|
pids = DBGP_PID_SET(pipe->pid, USB_PID_OUT);
|
|
|
|
ctrl = read32(&ehci_debug->control);
|
|
ctrl = DBGP_LEN_UPDATE(ctrl, size);
|
|
ctrl |= DBGP_OUT;
|
|
|
|
dbgp_set_data(ehci_debug, bytes, size);
|
|
write32(&ehci_debug->address, addr);
|
|
write32(&ehci_debug->pids, pids);
|
|
|
|
ret = dbgp_wait_until_done(ehci_debug, pipe, ctrl, pipe->timeout);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int dbgp_bulk_write_x(struct dbgp_pipe *pipe, const char *bytes, int size)
|
|
{
|
|
struct ehci_debug_info *dbg_info = dbgp_ehci_info();
|
|
return dbgp_bulk_write(dbg_info->ehci_debug, pipe, bytes, size);
|
|
}
|
|
|
|
static int dbgp_bulk_read(struct ehci_dbg_port *ehci_debug, struct dbgp_pipe *pipe,
|
|
void *data, int size)
|
|
{
|
|
u32 pids, addr, ctrl;
|
|
int ret;
|
|
|
|
if (size > DBGP_MAX_PACKET)
|
|
return -1;
|
|
|
|
addr = DBGP_EPADDR(pipe->devnum, pipe->endpoint);
|
|
pids = DBGP_PID_SET(pipe->pid, USB_PID_IN);
|
|
|
|
ctrl = read32(&ehci_debug->control);
|
|
ctrl = DBGP_LEN_UPDATE(ctrl, size);
|
|
ctrl &= ~DBGP_OUT;
|
|
|
|
write32(&ehci_debug->address, addr);
|
|
write32(&ehci_debug->pids, pids);
|
|
ret = dbgp_wait_until_done(ehci_debug, pipe, ctrl, pipe->timeout);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
if (size > ret)
|
|
size = ret;
|
|
dbgp_get_data(ehci_debug, data, size);
|
|
return ret;
|
|
}
|
|
|
|
int dbgp_bulk_read_x(struct dbgp_pipe *pipe, void *data, int size)
|
|
{
|
|
struct ehci_debug_info *dbg_info = dbgp_ehci_info();
|
|
return dbgp_bulk_read(dbg_info->ehci_debug, pipe, data, size);
|
|
}
|
|
|
|
void dbgp_mdelay(int ms)
|
|
{
|
|
int i;
|
|
|
|
while (ms--) {
|
|
for (i = 0; i < 1000; i++)
|
|
inb(0x80);
|
|
}
|
|
}
|
|
|
|
int dbgp_control_msg(struct ehci_dbg_port *ehci_debug, unsigned devnum, int requesttype,
|
|
int request, int value, int index, void *data, int size)
|
|
{
|
|
struct ehci_debug_info *info = dbgp_ehci_info();
|
|
struct dbgp_pipe *pipe = &info->ep_pipe[DBGP_SETUP_EP0];
|
|
u32 pids, addr, ctrl;
|
|
struct usb_ctrlrequest req;
|
|
int read;
|
|
int ret, ret2;
|
|
|
|
read = (requesttype & USB_DIR_IN) != 0;
|
|
if (size > DBGP_MAX_PACKET)
|
|
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);
|
|
|
|
pipe->devnum = devnum;
|
|
pipe->endpoint = 0;
|
|
pipe->pid = USB_PID_DATA0;
|
|
pipe->timeout = 1000;
|
|
addr = DBGP_EPADDR(pipe->devnum, pipe->endpoint);
|
|
pids = DBGP_PID_SET(pipe->pid, USB_PID_SETUP);
|
|
|
|
ctrl = read32(&ehci_debug->control);
|
|
ctrl = DBGP_LEN_UPDATE(ctrl, sizeof(req));
|
|
ctrl |= DBGP_OUT;
|
|
|
|
/* Setup stage */
|
|
dbgp_set_data(ehci_debug, &req, sizeof(req));
|
|
write32(&ehci_debug->address, addr);
|
|
write32(&ehci_debug->pids, pids);
|
|
ret = dbgp_wait_until_done(ehci_debug, pipe, ctrl, 1);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
/* Data stage (optional) */
|
|
if (read && size)
|
|
ret = dbgp_bulk_read(ehci_debug, pipe, data, size);
|
|
else if (!read && size)
|
|
ret = dbgp_bulk_write(ehci_debug, pipe, data, size);
|
|
|
|
/* Status stage in opposite direction */
|
|
pipe->pid = USB_PID_DATA1;
|
|
ctrl = read32(&ehci_debug->control);
|
|
ctrl = DBGP_LEN_UPDATE(ctrl, 0);
|
|
if (read) {
|
|
pids = DBGP_PID_SET(pipe->pid, USB_PID_OUT);
|
|
ctrl |= DBGP_OUT;
|
|
} else {
|
|
pids = DBGP_PID_SET(pipe->pid, USB_PID_IN);
|
|
ctrl &= ~DBGP_OUT;
|
|
}
|
|
|
|
write32(&ehci_debug->pids, pids);
|
|
ret2 = dbgp_wait_until_done(ehci_debug, pipe, ctrl, pipe->timeout);
|
|
if (ret2 < 0)
|
|
return ret2;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int ehci_reset_port(struct ehci_regs *ehci_regs, int port)
|
|
{
|
|
u32 portsc;
|
|
int loop;
|
|
|
|
/* Reset the usb debug port */
|
|
portsc = read32(&ehci_regs->port_status[port - 1]);
|
|
portsc &= ~PORT_PE;
|
|
portsc |= PORT_RESET;
|
|
write32(&ehci_regs->port_status[port - 1], portsc);
|
|
|
|
dbgp_mdelay(HUB_ROOT_RESET_TIME);
|
|
|
|
portsc = read32(&ehci_regs->port_status[port - 1]);
|
|
write32(&ehci_regs->port_status[port - 1],
|
|
portsc & ~(PORT_RWC_BITS | PORT_RESET));
|
|
|
|
loop = 100;
|
|
do {
|
|
dbgp_mdelay(1);
|
|
portsc = read32(&ehci_regs->port_status[port - 1]);
|
|
} while ((portsc & PORT_RESET) && (--loop > 0));
|
|
|
|
/* Device went away? */
|
|
if (!(portsc & PORT_CONNECT))
|
|
return -1; //-ENOTCONN;
|
|
|
|
/* bomb out completely if something weird happened */
|
|
if ((portsc & PORT_CSC))
|
|
return -2; //-EINVAL;
|
|
|
|
/* If we've finished resetting, then break out of the loop */
|
|
if (!(portsc & PORT_RESET) && (portsc & PORT_PE))
|
|
return 0;
|
|
|
|
return -3; //-EBUSY;
|
|
}
|
|
|
|
static int ehci_wait_for_port(struct ehci_regs *ehci_regs, int port)
|
|
{
|
|
u32 status;
|
|
int ret, reps;
|
|
|
|
for (reps = 0; reps < 3; reps++) {
|
|
dbgp_mdelay(100);
|
|
status = read32(&ehci_regs->status);
|
|
if (status & STS_PCD) {
|
|
ret = ehci_reset_port(ehci_regs, port);
|
|
if (ret == 0)
|
|
return 0;
|
|
}
|
|
}
|
|
return -1; //-ENOTCONN;
|
|
}
|
|
|
|
|
|
|
|
static int usbdebug_init_(unsigned ehci_bar, unsigned offset, struct ehci_debug_info *info)
|
|
{
|
|
struct ehci_caps *ehci_caps;
|
|
struct ehci_regs *ehci_regs;
|
|
|
|
u32 cmd, ctrl, status, portsc, hcs_params;
|
|
u32 debug_port, new_debug_port = 0, n_ports;
|
|
int ret, i;
|
|
int loop;
|
|
int port_map_tried;
|
|
int playtimes = 3;
|
|
|
|
/* Keep all endpoints disabled before any printk() call. */
|
|
memset(info, 0, sizeof (*info));
|
|
info->ehci_base = (void *)ehci_bar;
|
|
info->ehci_debug = (void *)(ehci_bar + offset);
|
|
|
|
dprintk(BIOS_INFO, "ehci_bar: 0x%x debug_offset 0x%x\n", ehci_bar, offset);
|
|
|
|
ehci_caps = (struct ehci_caps *)ehci_bar;
|
|
ehci_regs = (struct ehci_regs *)(ehci_bar +
|
|
HC_LENGTH(read32(&ehci_caps->hc_capbase)));
|
|
|
|
struct ehci_dbg_port *ehci_debug = info->ehci_debug;
|
|
|
|
if (CONFIG_USBDEBUG_DEFAULT_PORT > 0)
|
|
ehci_debug_select_port(CONFIG_USBDEBUG_DEFAULT_PORT);
|
|
else
|
|
ehci_debug_select_port(1);
|
|
|
|
try_next_time:
|
|
port_map_tried = 0;
|
|
|
|
try_next_port:
|
|
hcs_params = read32(&ehci_caps->hcs_params);
|
|
debug_port = HCS_DEBUG_PORT(hcs_params);
|
|
n_ports = HCS_N_PORTS(hcs_params);
|
|
|
|
dprintk(BIOS_INFO, "debug_port: %d\n", debug_port);
|
|
dprintk(BIOS_INFO, "n_ports: %d\n", n_ports);
|
|
|
|
for (i = 1; i <= n_ports; i++) {
|
|
portsc = read32(&ehci_regs->port_status[i-1]);
|
|
dprintk(BIOS_INFO, "PORTSC #%d: %08x\n", i, portsc);
|
|
}
|
|
|
|
if(port_map_tried && (new_debug_port != debug_port)) {
|
|
if(--playtimes) {
|
|
ehci_debug_select_port(debug_port);
|
|
goto try_next_time;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/* Wait until the controller is halted */
|
|
status = read32(&ehci_regs->status);
|
|
if (!(status & STS_HALT)) {
|
|
cmd = read32(&ehci_regs->command);
|
|
cmd &= ~CMD_RUN;
|
|
write32(&ehci_regs->command, cmd);
|
|
loop = 100;
|
|
do {
|
|
dbgp_mdelay(10);
|
|
status = read32(&ehci_regs->status);
|
|
} while (!(status & STS_HALT) && (--loop > 0));
|
|
if (status & STS_HALT)
|
|
dprintk(BIOS_INFO, "EHCI controller halted successfully.\n");
|
|
else
|
|
dprintk(BIOS_INFO, "EHCI controller is not halted. Reset may fail.\n");
|
|
}
|
|
|
|
loop = 100;
|
|
/* Reset the EHCI controller */
|
|
cmd = read32(&ehci_regs->command);
|
|
cmd |= CMD_RESET;
|
|
write32(&ehci_regs->command, cmd);
|
|
do {
|
|
dbgp_mdelay(10);
|
|
cmd = read32(&ehci_regs->command);
|
|
} while ((cmd & CMD_RESET) && (--loop > 0));
|
|
|
|
if(!loop) {
|
|
dprintk(BIOS_INFO, "Could not reset EHCI controller.\n");
|
|
// on some systems it works without succeeding here.
|
|
// return -2;
|
|
} else {
|
|
dprintk(BIOS_INFO, "EHCI controller reset successfully.\n");
|
|
}
|
|
|
|
/* Claim ownership, but do not enable yet */
|
|
ctrl = read32(&ehci_debug->control);
|
|
ctrl |= DBGP_OWNER;
|
|
ctrl &= ~(DBGP_ENABLED | DBGP_INUSE);
|
|
write32(&ehci_debug->control, ctrl);
|
|
|
|
/* Start EHCI controller */
|
|
cmd = read32(&ehci_regs->command);
|
|
cmd &= ~(CMD_LRESET | CMD_IAAD | CMD_PSE | CMD_ASE | CMD_RESET);
|
|
cmd |= CMD_RUN;
|
|
write32(&ehci_regs->command, cmd);
|
|
|
|
/* Ensure everything is routed to the EHCI */
|
|
write32(&ehci_regs->configured_flag, FLAG_CF);
|
|
|
|
/* Wait until the controller is no longer halted */
|
|
loop = 10;
|
|
do {
|
|
dbgp_mdelay(10);
|
|
status = read32(&ehci_regs->status);
|
|
} while ((status & STS_HALT) && (--loop > 0));
|
|
|
|
if(!loop) {
|
|
dprintk(BIOS_INFO, "EHCI could not be started.\n");
|
|
return -3;
|
|
}
|
|
dprintk(BIOS_INFO, "EHCI started.\n");
|
|
|
|
/* Wait for a device to show up in the debug port */
|
|
ret = ehci_wait_for_port(ehci_regs, debug_port);
|
|
if (ret < 0) {
|
|
dprintk(BIOS_INFO, "No device found in debug port %d\n", debug_port);
|
|
goto next_debug_port;
|
|
}
|
|
dprintk(BIOS_INFO, "EHCI done waiting for port.\n");
|
|
|
|
|
|
/* Enable the debug port */
|
|
ctrl = read32(&ehci_debug->control);
|
|
ctrl |= DBGP_CLAIM;
|
|
write32(&ehci_debug->control, ctrl);
|
|
ctrl = read32(&ehci_debug->control);
|
|
if ((ctrl & DBGP_CLAIM) != DBGP_CLAIM) {
|
|
dprintk(BIOS_INFO, "No device in EHCI debug port.\n");
|
|
write32(&ehci_debug->control, ctrl & ~DBGP_CLAIM);
|
|
ret = -4;
|
|
goto err;
|
|
}
|
|
dprintk(BIOS_INFO, "EHCI debug port enabled.\n");
|
|
|
|
#if 0
|
|
/* Completely transfer the debug device to the debug controller */
|
|
portsc = read32(&ehci_regs->port_status[debug_port - 1]);
|
|
portsc &= ~PORT_PE;
|
|
write32(&ehci_regs->port_status[debug_port - 1], portsc);
|
|
#endif
|
|
|
|
dbgp_mdelay(100);
|
|
|
|
ret = dbgp_probe_gadget(info->ehci_debug, &info->ep_pipe[0]);
|
|
if (ret < 0) {
|
|
dprintk(BIOS_INFO, "Could not probe gadget on debug port.\n");
|
|
ret = -6;
|
|
goto err;
|
|
}
|
|
|
|
return 0;
|
|
err:
|
|
/* Things didn't work so remove my claim */
|
|
ctrl = read32(&ehci_debug->control);
|
|
ctrl &= ~(DBGP_CLAIM | DBGP_OUT);
|
|
write32(&ehci_debug->control, ctrl);
|
|
//return ret;
|
|
|
|
next_debug_port:
|
|
#if CONFIG_USBDEBUG_DEFAULT_PORT==0
|
|
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)) {
|
|
ehci_debug_select_port(new_debug_port);
|
|
goto try_next_port;
|
|
}
|
|
if (--playtimes) {
|
|
ehci_debug_select_port(new_debug_port);
|
|
goto try_next_time;
|
|
}
|
|
#else
|
|
if (0)
|
|
goto try_next_port;
|
|
if (--playtimes)
|
|
goto try_next_time;
|
|
#endif
|
|
|
|
return -10;
|
|
}
|
|
|
|
#if CONFIG_DEBUG_USBDEBUG
|
|
static int dbgp_enabled(void)
|
|
{
|
|
struct dbgp_pipe *globals = &dbgp_ehci_info()->ep_pipe[DBGP_SETUP_EP0];
|
|
return (globals->status & DBGP_EP_ENABLED);
|
|
}
|
|
#endif
|
|
|
|
int dbgp_try_get(struct dbgp_pipe *pipe)
|
|
{
|
|
struct dbgp_pipe *globals = &dbgp_ehci_info()->ep_pipe[DBGP_SETUP_EP0];
|
|
if (!dbgp_ep_is_active(pipe) || (globals->status & DBGP_EP_BUSY))
|
|
return 0;
|
|
globals->status |= DBGP_EP_BUSY;
|
|
pipe->status |= DBGP_EP_BUSY;
|
|
return 1;
|
|
}
|
|
|
|
void dbgp_put(struct dbgp_pipe *pipe)
|
|
{
|
|
struct dbgp_pipe *globals = &dbgp_ehci_info()->ep_pipe[DBGP_SETUP_EP0];
|
|
globals->status &= ~DBGP_EP_BUSY;
|
|
pipe->status &= ~DBGP_EP_BUSY;
|
|
}
|
|
|
|
#if !defined(__PRE_RAM__) && !defined(__SMM__)
|
|
void usbdebug_re_enable(unsigned ehci_base)
|
|
{
|
|
struct ehci_debug_info *dbg_info = dbgp_ehci_info();
|
|
unsigned diff;
|
|
int i;
|
|
|
|
diff = (unsigned)dbg_info->ehci_base - ehci_base;
|
|
dbg_info->ehci_debug -= diff;
|
|
dbg_info->ehci_base = (void*)ehci_base;
|
|
|
|
for (i=0; i<DBGP_MAX_ENDPOINTS; i++)
|
|
if (dbg_info->ep_pipe[i].status & DBGP_EP_VALID)
|
|
dbg_info->ep_pipe[i].status |= DBGP_EP_ENABLED;
|
|
}
|
|
|
|
void usbdebug_disable(void)
|
|
{
|
|
struct ehci_debug_info *dbg_info = dbgp_ehci_info();
|
|
int i;
|
|
for (i=0; i<DBGP_MAX_ENDPOINTS; i++)
|
|
dbg_info->ep_pipe[i].status &= ~DBGP_EP_ENABLED;
|
|
}
|
|
|
|
#endif
|
|
|
|
#if !defined(__PRE_RAM__) && !defined(__SMM__)
|
|
static int get_usbdebug_from_cbmem(struct ehci_debug_info *info)
|
|
{
|
|
struct ehci_debug_info *dbg_info_cbmem;
|
|
|
|
dbg_info_cbmem = cbmem_find(CBMEM_ID_EHCI_DEBUG);
|
|
if (dbg_info_cbmem == NULL)
|
|
return -1;
|
|
|
|
memcpy(info, dbg_info_cbmem, sizeof (*info));
|
|
printk(BIOS_DEBUG, "EHCI debug port found in CBMEM.\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
#elif defined(__PRE_RAM__)
|
|
static void migrate_ehci_debug(void)
|
|
{
|
|
struct ehci_debug_info *dbg_info = dbgp_ehci_info();
|
|
struct ehci_debug_info *dbg_info_cbmem;
|
|
|
|
dbg_info_cbmem = cbmem_add(CBMEM_ID_EHCI_DEBUG, sizeof(*dbg_info));
|
|
if (dbg_info_cbmem == NULL)
|
|
return;
|
|
|
|
memcpy(dbg_info_cbmem, dbg_info, sizeof(*dbg_info));
|
|
car_set_var(glob_dbg_info_p, dbg_info_cbmem);
|
|
}
|
|
CAR_MIGRATE(migrate_ehci_debug);
|
|
#endif
|
|
|
|
int dbgp_ep_is_active(struct dbgp_pipe *pipe)
|
|
{
|
|
return (pipe->status & DBGP_EP_STATMASK) == (DBGP_EP_VALID | DBGP_EP_ENABLED);
|
|
}
|
|
|
|
struct dbgp_pipe *dbgp_console_output(void)
|
|
{
|
|
return &dbgp_ehci_info()->ep_pipe[DBGP_CONSOLE_EPOUT];
|
|
}
|
|
|
|
struct dbgp_pipe *dbgp_console_input(void)
|
|
{
|
|
return &dbgp_ehci_info()->ep_pipe[DBGP_CONSOLE_EPIN];
|
|
}
|
|
|
|
int usbdebug_init(void)
|
|
{
|
|
struct ehci_debug_info *dbg_info = dbgp_ehci_info();
|
|
unsigned int ehci_base, dbg_offset;
|
|
|
|
#if !defined(__PRE_RAM__) && !defined(__SMM__)
|
|
if (!get_usbdebug_from_cbmem(dbg_info))
|
|
return 0;
|
|
#endif
|
|
if (ehci_debug_hw_enable(&ehci_base, &dbg_offset))
|
|
return -1;
|
|
return usbdebug_init_(ehci_base, dbg_offset, dbg_info);
|
|
}
|