18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Standalone EHCI usb debug driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Originally written by:
68c2ecf20Sopenharmony_ci *  Eric W. Biederman" <ebiederm@xmission.com> and
78c2ecf20Sopenharmony_ci *  Yinghai Lu <yhlu.kernel@gmail.com>
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * Changes for early/late printk and HW errata:
108c2ecf20Sopenharmony_ci *  Jason Wessel <jason.wessel@windriver.com>
118c2ecf20Sopenharmony_ci *  Copyright (C) 2009 Wind River Systems, Inc.
128c2ecf20Sopenharmony_ci *
138c2ecf20Sopenharmony_ci */
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include <linux/console.h>
168c2ecf20Sopenharmony_ci#include <linux/errno.h>
178c2ecf20Sopenharmony_ci#include <linux/init.h>
188c2ecf20Sopenharmony_ci#include <linux/iopoll.h>
198c2ecf20Sopenharmony_ci#include <linux/pci_regs.h>
208c2ecf20Sopenharmony_ci#include <linux/pci_ids.h>
218c2ecf20Sopenharmony_ci#include <linux/usb/ch9.h>
228c2ecf20Sopenharmony_ci#include <linux/usb/ehci_def.h>
238c2ecf20Sopenharmony_ci#include <linux/delay.h>
248c2ecf20Sopenharmony_ci#include <linux/serial_core.h>
258c2ecf20Sopenharmony_ci#include <linux/kgdb.h>
268c2ecf20Sopenharmony_ci#include <linux/kthread.h>
278c2ecf20Sopenharmony_ci#include <asm/io.h>
288c2ecf20Sopenharmony_ci#include <asm/pci-direct.h>
298c2ecf20Sopenharmony_ci#include <asm/fixmap.h>
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci/* The code here is intended to talk directly to the EHCI debug port
328c2ecf20Sopenharmony_ci * and does not require that you have any kind of USB host controller
338c2ecf20Sopenharmony_ci * drivers or USB device drivers compiled into the kernel.
348c2ecf20Sopenharmony_ci *
358c2ecf20Sopenharmony_ci * If you make a change to anything in here, the following test cases
368c2ecf20Sopenharmony_ci * need to pass where a USB debug device works in the following
378c2ecf20Sopenharmony_ci * configurations.
388c2ecf20Sopenharmony_ci *
398c2ecf20Sopenharmony_ci * 1. boot args:  earlyprintk=dbgp
408c2ecf20Sopenharmony_ci *     o kernel compiled with # CONFIG_USB_EHCI_HCD is not set
418c2ecf20Sopenharmony_ci *     o kernel compiled with CONFIG_USB_EHCI_HCD=y
428c2ecf20Sopenharmony_ci * 2. boot args: earlyprintk=dbgp,keep
438c2ecf20Sopenharmony_ci *     o kernel compiled with # CONFIG_USB_EHCI_HCD is not set
448c2ecf20Sopenharmony_ci *     o kernel compiled with CONFIG_USB_EHCI_HCD=y
458c2ecf20Sopenharmony_ci * 3. boot args: earlyprintk=dbgp console=ttyUSB0
468c2ecf20Sopenharmony_ci *     o kernel has CONFIG_USB_EHCI_HCD=y and
478c2ecf20Sopenharmony_ci *       CONFIG_USB_SERIAL_DEBUG=y
488c2ecf20Sopenharmony_ci * 4. boot args: earlyprintk=vga,dbgp
498c2ecf20Sopenharmony_ci *     o kernel compiled with # CONFIG_USB_EHCI_HCD is not set
508c2ecf20Sopenharmony_ci *     o kernel compiled with CONFIG_USB_EHCI_HCD=y
518c2ecf20Sopenharmony_ci *
528c2ecf20Sopenharmony_ci * For the 4th configuration you can turn on or off the DBGP_DEBUG
538c2ecf20Sopenharmony_ci * such that you can debug the dbgp device's driver code.
548c2ecf20Sopenharmony_ci */
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_cistatic int dbgp_phys_port = 1;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cistatic struct ehci_caps __iomem *ehci_caps;
598c2ecf20Sopenharmony_cistatic struct ehci_regs __iomem *ehci_regs;
608c2ecf20Sopenharmony_cistatic struct ehci_dbg_port __iomem *ehci_debug;
618c2ecf20Sopenharmony_cistatic int dbgp_not_safe; /* Cannot use debug device during ehci reset */
628c2ecf20Sopenharmony_cistatic unsigned int dbgp_endpoint_out;
638c2ecf20Sopenharmony_cistatic unsigned int dbgp_endpoint_in;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistruct ehci_dev {
668c2ecf20Sopenharmony_ci	u32 bus;
678c2ecf20Sopenharmony_ci	u32 slot;
688c2ecf20Sopenharmony_ci	u32 func;
698c2ecf20Sopenharmony_ci};
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_cistatic struct ehci_dev ehci_dev;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci#define USB_DEBUG_DEVNUM 127
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci#ifdef DBGP_DEBUG
768c2ecf20Sopenharmony_ci#define dbgp_printk printk
778c2ecf20Sopenharmony_cistatic void dbgp_ehci_status(char *str)
788c2ecf20Sopenharmony_ci{
798c2ecf20Sopenharmony_ci	if (!ehci_debug)
808c2ecf20Sopenharmony_ci		return;
818c2ecf20Sopenharmony_ci	dbgp_printk("dbgp: %s\n", str);
828c2ecf20Sopenharmony_ci	dbgp_printk("  Debug control: %08x", readl(&ehci_debug->control));
838c2ecf20Sopenharmony_ci	dbgp_printk("  ehci cmd     : %08x", readl(&ehci_regs->command));
848c2ecf20Sopenharmony_ci	dbgp_printk("  ehci conf flg: %08x\n",
858c2ecf20Sopenharmony_ci		    readl(&ehci_regs->configured_flag));
868c2ecf20Sopenharmony_ci	dbgp_printk("  ehci status  : %08x", readl(&ehci_regs->status));
878c2ecf20Sopenharmony_ci	dbgp_printk("  ehci portsc  : %08x\n",
888c2ecf20Sopenharmony_ci		    readl(&ehci_regs->port_status[dbgp_phys_port - 1]));
898c2ecf20Sopenharmony_ci}
908c2ecf20Sopenharmony_ci#else
918c2ecf20Sopenharmony_cistatic inline void dbgp_ehci_status(char *str) { }
928c2ecf20Sopenharmony_cistatic inline void dbgp_printk(const char *fmt, ...) { }
938c2ecf20Sopenharmony_ci#endif
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_cistatic inline u32 dbgp_len_update(u32 x, u32 len)
968c2ecf20Sopenharmony_ci{
978c2ecf20Sopenharmony_ci	return (x & ~0x0f) | (len & 0x0f);
988c2ecf20Sopenharmony_ci}
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci#ifdef CONFIG_KGDB
1018c2ecf20Sopenharmony_cistatic struct kgdb_io kgdbdbgp_io_ops;
1028c2ecf20Sopenharmony_ci#define dbgp_kgdb_mode (dbg_io_ops == &kgdbdbgp_io_ops)
1038c2ecf20Sopenharmony_ci#else
1048c2ecf20Sopenharmony_ci#define dbgp_kgdb_mode (0)
1058c2ecf20Sopenharmony_ci#endif
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci/* Local version of HC_LENGTH macro as ehci struct is not available here */
1088c2ecf20Sopenharmony_ci#define EARLY_HC_LENGTH(p)	(0x00ff & (p)) /* bits 7 : 0 */
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci/*
1118c2ecf20Sopenharmony_ci * USB Packet IDs (PIDs)
1128c2ecf20Sopenharmony_ci */
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci/* token */
1158c2ecf20Sopenharmony_ci#define USB_PID_OUT		0xe1
1168c2ecf20Sopenharmony_ci#define USB_PID_IN		0x69
1178c2ecf20Sopenharmony_ci#define USB_PID_SOF		0xa5
1188c2ecf20Sopenharmony_ci#define USB_PID_SETUP		0x2d
1198c2ecf20Sopenharmony_ci/* handshake */
1208c2ecf20Sopenharmony_ci#define USB_PID_ACK		0xd2
1218c2ecf20Sopenharmony_ci#define USB_PID_NAK		0x5a
1228c2ecf20Sopenharmony_ci#define USB_PID_STALL		0x1e
1238c2ecf20Sopenharmony_ci#define USB_PID_NYET		0x96
1248c2ecf20Sopenharmony_ci/* data */
1258c2ecf20Sopenharmony_ci#define USB_PID_DATA0		0xc3
1268c2ecf20Sopenharmony_ci#define USB_PID_DATA1		0x4b
1278c2ecf20Sopenharmony_ci#define USB_PID_DATA2		0x87
1288c2ecf20Sopenharmony_ci#define USB_PID_MDATA		0x0f
1298c2ecf20Sopenharmony_ci/* Special */
1308c2ecf20Sopenharmony_ci#define USB_PID_PREAMBLE	0x3c
1318c2ecf20Sopenharmony_ci#define USB_PID_ERR		0x3c
1328c2ecf20Sopenharmony_ci#define USB_PID_SPLIT		0x78
1338c2ecf20Sopenharmony_ci#define USB_PID_PING		0xb4
1348c2ecf20Sopenharmony_ci#define USB_PID_UNDEF_0		0xf0
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci#define USB_PID_DATA_TOGGLE	0x88
1378c2ecf20Sopenharmony_ci#define DBGP_CLAIM (DBGP_OWNER | DBGP_ENABLED | DBGP_INUSE)
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci#define PCI_CAP_ID_EHCI_DEBUG	0xa
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci#define HUB_ROOT_RESET_TIME	50	/* times are in msec */
1428c2ecf20Sopenharmony_ci#define HUB_SHORT_RESET_TIME	10
1438c2ecf20Sopenharmony_ci#define HUB_LONG_RESET_TIME	200
1448c2ecf20Sopenharmony_ci#define HUB_RESET_TIMEOUT	500
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci#define DBGP_MAX_PACKET		8
1478c2ecf20Sopenharmony_ci#define DBGP_TIMEOUT		(250 * 1000)
1488c2ecf20Sopenharmony_ci#define DBGP_LOOPS		1000
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_cistatic inline u32 dbgp_pid_write_update(u32 x, u32 tok)
1518c2ecf20Sopenharmony_ci{
1528c2ecf20Sopenharmony_ci	static int data0 = USB_PID_DATA1;
1538c2ecf20Sopenharmony_ci	data0 ^= USB_PID_DATA_TOGGLE;
1548c2ecf20Sopenharmony_ci	return (x & 0xffff0000) | (data0 << 8) | (tok & 0xff);
1558c2ecf20Sopenharmony_ci}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_cistatic inline u32 dbgp_pid_read_update(u32 x, u32 tok)
1588c2ecf20Sopenharmony_ci{
1598c2ecf20Sopenharmony_ci	return (x & 0xffff0000) | (USB_PID_DATA0 << 8) | (tok & 0xff);
1608c2ecf20Sopenharmony_ci}
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_cistatic int dbgp_wait_until_complete(void)
1638c2ecf20Sopenharmony_ci{
1648c2ecf20Sopenharmony_ci	u32 ctrl;
1658c2ecf20Sopenharmony_ci	int ret;
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	ret = readl_poll_timeout_atomic(&ehci_debug->control, ctrl,
1688c2ecf20Sopenharmony_ci				(ctrl & DBGP_DONE), 1, DBGP_TIMEOUT);
1698c2ecf20Sopenharmony_ci	if (ret)
1708c2ecf20Sopenharmony_ci		return -DBGP_TIMEOUT;
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	/*
1738c2ecf20Sopenharmony_ci	 * Now that we have observed the completed transaction,
1748c2ecf20Sopenharmony_ci	 * clear the done bit.
1758c2ecf20Sopenharmony_ci	 */
1768c2ecf20Sopenharmony_ci	writel(ctrl | DBGP_DONE, &ehci_debug->control);
1778c2ecf20Sopenharmony_ci	return (ctrl & DBGP_ERROR) ? -DBGP_ERRCODE(ctrl) : DBGP_LEN(ctrl);
1788c2ecf20Sopenharmony_ci}
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_cistatic inline void dbgp_mdelay(int ms)
1818c2ecf20Sopenharmony_ci{
1828c2ecf20Sopenharmony_ci	int i;
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	while (ms--) {
1858c2ecf20Sopenharmony_ci		for (i = 0; i < 1000; i++)
1868c2ecf20Sopenharmony_ci			outb(0x1, 0x80);
1878c2ecf20Sopenharmony_ci	}
1888c2ecf20Sopenharmony_ci}
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_cistatic void dbgp_breath(void)
1918c2ecf20Sopenharmony_ci{
1928c2ecf20Sopenharmony_ci	/* Sleep to give the debug port a chance to breathe */
1938c2ecf20Sopenharmony_ci}
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_cistatic int dbgp_wait_until_done(unsigned ctrl, int loop)
1968c2ecf20Sopenharmony_ci{
1978c2ecf20Sopenharmony_ci	u32 pids, lpid;
1988c2ecf20Sopenharmony_ci	int ret;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ciretry:
2018c2ecf20Sopenharmony_ci	writel(ctrl | DBGP_GO, &ehci_debug->control);
2028c2ecf20Sopenharmony_ci	ret = dbgp_wait_until_complete();
2038c2ecf20Sopenharmony_ci	pids = readl(&ehci_debug->pids);
2048c2ecf20Sopenharmony_ci	lpid = DBGP_PID_GET(pids);
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	if (ret < 0) {
2078c2ecf20Sopenharmony_ci		/* A -DBGP_TIMEOUT failure here means the device has
2088c2ecf20Sopenharmony_ci		 * failed, perhaps because it was unplugged, in which
2098c2ecf20Sopenharmony_ci		 * case we do not want to hang the system so the dbgp
2108c2ecf20Sopenharmony_ci		 * will be marked as unsafe to use.  EHCI reset is the
2118c2ecf20Sopenharmony_ci		 * only way to recover if you unplug the dbgp device.
2128c2ecf20Sopenharmony_ci		 */
2138c2ecf20Sopenharmony_ci		if (ret == -DBGP_TIMEOUT && !dbgp_not_safe)
2148c2ecf20Sopenharmony_ci			dbgp_not_safe = 1;
2158c2ecf20Sopenharmony_ci		if (ret == -DBGP_ERR_BAD && --loop > 0)
2168c2ecf20Sopenharmony_ci			goto retry;
2178c2ecf20Sopenharmony_ci		return ret;
2188c2ecf20Sopenharmony_ci	}
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	/*
2218c2ecf20Sopenharmony_ci	 * If the port is getting full or it has dropped data
2228c2ecf20Sopenharmony_ci	 * start pacing ourselves, not necessary but it's friendly.
2238c2ecf20Sopenharmony_ci	 */
2248c2ecf20Sopenharmony_ci	if ((lpid == USB_PID_NAK) || (lpid == USB_PID_NYET))
2258c2ecf20Sopenharmony_ci		dbgp_breath();
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	/* If I get a NACK reissue the transmission */
2288c2ecf20Sopenharmony_ci	if (lpid == USB_PID_NAK) {
2298c2ecf20Sopenharmony_ci		if (--loop > 0)
2308c2ecf20Sopenharmony_ci			goto retry;
2318c2ecf20Sopenharmony_ci	}
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	return ret;
2348c2ecf20Sopenharmony_ci}
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_cistatic inline void dbgp_set_data(const void *buf, int size)
2378c2ecf20Sopenharmony_ci{
2388c2ecf20Sopenharmony_ci	const unsigned char *bytes = buf;
2398c2ecf20Sopenharmony_ci	u32 lo, hi;
2408c2ecf20Sopenharmony_ci	int i;
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	lo = hi = 0;
2438c2ecf20Sopenharmony_ci	for (i = 0; i < 4 && i < size; i++)
2448c2ecf20Sopenharmony_ci		lo |= bytes[i] << (8*i);
2458c2ecf20Sopenharmony_ci	for (; i < 8 && i < size; i++)
2468c2ecf20Sopenharmony_ci		hi |= bytes[i] << (8*(i - 4));
2478c2ecf20Sopenharmony_ci	writel(lo, &ehci_debug->data03);
2488c2ecf20Sopenharmony_ci	writel(hi, &ehci_debug->data47);
2498c2ecf20Sopenharmony_ci}
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_cistatic inline void dbgp_get_data(void *buf, int size)
2528c2ecf20Sopenharmony_ci{
2538c2ecf20Sopenharmony_ci	unsigned char *bytes = buf;
2548c2ecf20Sopenharmony_ci	u32 lo, hi;
2558c2ecf20Sopenharmony_ci	int i;
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	lo = readl(&ehci_debug->data03);
2588c2ecf20Sopenharmony_ci	hi = readl(&ehci_debug->data47);
2598c2ecf20Sopenharmony_ci	for (i = 0; i < 4 && i < size; i++)
2608c2ecf20Sopenharmony_ci		bytes[i] = (lo >> (8*i)) & 0xff;
2618c2ecf20Sopenharmony_ci	for (; i < 8 && i < size; i++)
2628c2ecf20Sopenharmony_ci		bytes[i] = (hi >> (8*(i - 4))) & 0xff;
2638c2ecf20Sopenharmony_ci}
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_cistatic int dbgp_bulk_write(unsigned devnum, unsigned endpoint,
2668c2ecf20Sopenharmony_ci			 const char *bytes, int size)
2678c2ecf20Sopenharmony_ci{
2688c2ecf20Sopenharmony_ci	int ret;
2698c2ecf20Sopenharmony_ci	u32 addr;
2708c2ecf20Sopenharmony_ci	u32 pids, ctrl;
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	if (size > DBGP_MAX_PACKET)
2738c2ecf20Sopenharmony_ci		return -1;
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	addr = DBGP_EPADDR(devnum, endpoint);
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	pids = readl(&ehci_debug->pids);
2788c2ecf20Sopenharmony_ci	pids = dbgp_pid_write_update(pids, USB_PID_OUT);
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	ctrl = readl(&ehci_debug->control);
2818c2ecf20Sopenharmony_ci	ctrl = dbgp_len_update(ctrl, size);
2828c2ecf20Sopenharmony_ci	ctrl |= DBGP_OUT;
2838c2ecf20Sopenharmony_ci	ctrl |= DBGP_GO;
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	dbgp_set_data(bytes, size);
2868c2ecf20Sopenharmony_ci	writel(addr, &ehci_debug->address);
2878c2ecf20Sopenharmony_ci	writel(pids, &ehci_debug->pids);
2888c2ecf20Sopenharmony_ci	ret = dbgp_wait_until_done(ctrl, DBGP_LOOPS);
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	return ret;
2918c2ecf20Sopenharmony_ci}
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_cistatic int dbgp_bulk_read(unsigned devnum, unsigned endpoint, void *data,
2948c2ecf20Sopenharmony_ci			  int size, int loops)
2958c2ecf20Sopenharmony_ci{
2968c2ecf20Sopenharmony_ci	u32 pids, addr, ctrl;
2978c2ecf20Sopenharmony_ci	int ret;
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	if (size > DBGP_MAX_PACKET)
3008c2ecf20Sopenharmony_ci		return -1;
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	addr = DBGP_EPADDR(devnum, endpoint);
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	pids = readl(&ehci_debug->pids);
3058c2ecf20Sopenharmony_ci	pids = dbgp_pid_read_update(pids, USB_PID_IN);
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	ctrl = readl(&ehci_debug->control);
3088c2ecf20Sopenharmony_ci	ctrl = dbgp_len_update(ctrl, size);
3098c2ecf20Sopenharmony_ci	ctrl &= ~DBGP_OUT;
3108c2ecf20Sopenharmony_ci	ctrl |= DBGP_GO;
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	writel(addr, &ehci_debug->address);
3138c2ecf20Sopenharmony_ci	writel(pids, &ehci_debug->pids);
3148c2ecf20Sopenharmony_ci	ret = dbgp_wait_until_done(ctrl, loops);
3158c2ecf20Sopenharmony_ci	if (ret < 0)
3168c2ecf20Sopenharmony_ci		return ret;
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	if (size > ret)
3198c2ecf20Sopenharmony_ci		size = ret;
3208c2ecf20Sopenharmony_ci	dbgp_get_data(data, size);
3218c2ecf20Sopenharmony_ci	return ret;
3228c2ecf20Sopenharmony_ci}
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_cistatic int dbgp_control_msg(unsigned devnum, int requesttype,
3258c2ecf20Sopenharmony_ci	int request, int value, int index, void *data, int size)
3268c2ecf20Sopenharmony_ci{
3278c2ecf20Sopenharmony_ci	u32 pids, addr, ctrl;
3288c2ecf20Sopenharmony_ci	struct usb_ctrlrequest req;
3298c2ecf20Sopenharmony_ci	int read;
3308c2ecf20Sopenharmony_ci	int ret;
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	read = (requesttype & USB_DIR_IN) != 0;
3338c2ecf20Sopenharmony_ci	if (size > (read ? DBGP_MAX_PACKET : 0))
3348c2ecf20Sopenharmony_ci		return -1;
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	/* Compute the control message */
3378c2ecf20Sopenharmony_ci	req.bRequestType = requesttype;
3388c2ecf20Sopenharmony_ci	req.bRequest = request;
3398c2ecf20Sopenharmony_ci	req.wValue = cpu_to_le16(value);
3408c2ecf20Sopenharmony_ci	req.wIndex = cpu_to_le16(index);
3418c2ecf20Sopenharmony_ci	req.wLength = cpu_to_le16(size);
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	pids = DBGP_PID_SET(USB_PID_DATA0, USB_PID_SETUP);
3448c2ecf20Sopenharmony_ci	addr = DBGP_EPADDR(devnum, 0);
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	ctrl = readl(&ehci_debug->control);
3478c2ecf20Sopenharmony_ci	ctrl = dbgp_len_update(ctrl, sizeof(req));
3488c2ecf20Sopenharmony_ci	ctrl |= DBGP_OUT;
3498c2ecf20Sopenharmony_ci	ctrl |= DBGP_GO;
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	/* Send the setup message */
3528c2ecf20Sopenharmony_ci	dbgp_set_data(&req, sizeof(req));
3538c2ecf20Sopenharmony_ci	writel(addr, &ehci_debug->address);
3548c2ecf20Sopenharmony_ci	writel(pids, &ehci_debug->pids);
3558c2ecf20Sopenharmony_ci	ret = dbgp_wait_until_done(ctrl, DBGP_LOOPS);
3568c2ecf20Sopenharmony_ci	if (ret < 0)
3578c2ecf20Sopenharmony_ci		return ret;
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	/* Read the result */
3608c2ecf20Sopenharmony_ci	return dbgp_bulk_read(devnum, 0, data, size, DBGP_LOOPS);
3618c2ecf20Sopenharmony_ci}
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci/* Find a PCI capability */
3648c2ecf20Sopenharmony_cistatic u32 __init find_cap(u32 num, u32 slot, u32 func, int cap)
3658c2ecf20Sopenharmony_ci{
3668c2ecf20Sopenharmony_ci	u8 pos;
3678c2ecf20Sopenharmony_ci	int bytes;
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	if (!(read_pci_config_16(num, slot, func, PCI_STATUS) &
3708c2ecf20Sopenharmony_ci		PCI_STATUS_CAP_LIST))
3718c2ecf20Sopenharmony_ci		return 0;
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	pos = read_pci_config_byte(num, slot, func, PCI_CAPABILITY_LIST);
3748c2ecf20Sopenharmony_ci	for (bytes = 0; bytes < 48 && pos >= 0x40; bytes++) {
3758c2ecf20Sopenharmony_ci		u8 id;
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci		pos &= ~3;
3788c2ecf20Sopenharmony_ci		id = read_pci_config_byte(num, slot, func, pos+PCI_CAP_LIST_ID);
3798c2ecf20Sopenharmony_ci		if (id == 0xff)
3808c2ecf20Sopenharmony_ci			break;
3818c2ecf20Sopenharmony_ci		if (id == cap)
3828c2ecf20Sopenharmony_ci			return pos;
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci		pos = read_pci_config_byte(num, slot, func,
3858c2ecf20Sopenharmony_ci						 pos+PCI_CAP_LIST_NEXT);
3868c2ecf20Sopenharmony_ci	}
3878c2ecf20Sopenharmony_ci	return 0;
3888c2ecf20Sopenharmony_ci}
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_cistatic u32 __init __find_dbgp(u32 bus, u32 slot, u32 func)
3918c2ecf20Sopenharmony_ci{
3928c2ecf20Sopenharmony_ci	u32 class;
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	class = read_pci_config(bus, slot, func, PCI_CLASS_REVISION);
3958c2ecf20Sopenharmony_ci	if ((class >> 8) != PCI_CLASS_SERIAL_USB_EHCI)
3968c2ecf20Sopenharmony_ci		return 0;
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	return find_cap(bus, slot, func, PCI_CAP_ID_EHCI_DEBUG);
3998c2ecf20Sopenharmony_ci}
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_cistatic u32 __init find_dbgp(int ehci_num, u32 *rbus, u32 *rslot, u32 *rfunc)
4028c2ecf20Sopenharmony_ci{
4038c2ecf20Sopenharmony_ci	u32 bus, slot, func;
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	for (bus = 0; bus < 256; bus++) {
4068c2ecf20Sopenharmony_ci		for (slot = 0; slot < 32; slot++) {
4078c2ecf20Sopenharmony_ci			for (func = 0; func < 8; func++) {
4088c2ecf20Sopenharmony_ci				unsigned cap;
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci				cap = __find_dbgp(bus, slot, func);
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci				if (!cap)
4138c2ecf20Sopenharmony_ci					continue;
4148c2ecf20Sopenharmony_ci				if (ehci_num-- != 0)
4158c2ecf20Sopenharmony_ci					continue;
4168c2ecf20Sopenharmony_ci				*rbus = bus;
4178c2ecf20Sopenharmony_ci				*rslot = slot;
4188c2ecf20Sopenharmony_ci				*rfunc = func;
4198c2ecf20Sopenharmony_ci				return cap;
4208c2ecf20Sopenharmony_ci			}
4218c2ecf20Sopenharmony_ci		}
4228c2ecf20Sopenharmony_ci	}
4238c2ecf20Sopenharmony_ci	return 0;
4248c2ecf20Sopenharmony_ci}
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_cistatic int dbgp_ehci_startup(void)
4278c2ecf20Sopenharmony_ci{
4288c2ecf20Sopenharmony_ci	u32 ctrl, cmd, status;
4298c2ecf20Sopenharmony_ci	int loop;
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	/* Claim ownership, but do not enable yet */
4328c2ecf20Sopenharmony_ci	ctrl = readl(&ehci_debug->control);
4338c2ecf20Sopenharmony_ci	ctrl |= DBGP_OWNER;
4348c2ecf20Sopenharmony_ci	ctrl &= ~(DBGP_ENABLED | DBGP_INUSE);
4358c2ecf20Sopenharmony_ci	writel(ctrl, &ehci_debug->control);
4368c2ecf20Sopenharmony_ci	udelay(1);
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	dbgp_ehci_status("EHCI startup");
4398c2ecf20Sopenharmony_ci	/* Start the ehci running */
4408c2ecf20Sopenharmony_ci	cmd = readl(&ehci_regs->command);
4418c2ecf20Sopenharmony_ci	cmd &= ~(CMD_LRESET | CMD_IAAD | CMD_PSE | CMD_ASE | CMD_RESET);
4428c2ecf20Sopenharmony_ci	cmd |= CMD_RUN;
4438c2ecf20Sopenharmony_ci	writel(cmd, &ehci_regs->command);
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	/* Ensure everything is routed to the EHCI */
4468c2ecf20Sopenharmony_ci	writel(FLAG_CF, &ehci_regs->configured_flag);
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	/* Wait until the controller is no longer halted */
4498c2ecf20Sopenharmony_ci	loop = 1000;
4508c2ecf20Sopenharmony_ci	do {
4518c2ecf20Sopenharmony_ci		status = readl(&ehci_regs->status);
4528c2ecf20Sopenharmony_ci		if (!(status & STS_HALT))
4538c2ecf20Sopenharmony_ci			break;
4548c2ecf20Sopenharmony_ci		udelay(1);
4558c2ecf20Sopenharmony_ci	} while (--loop > 0);
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	if (!loop) {
4588c2ecf20Sopenharmony_ci		dbgp_printk("ehci can not be started\n");
4598c2ecf20Sopenharmony_ci		return -ENODEV;
4608c2ecf20Sopenharmony_ci	}
4618c2ecf20Sopenharmony_ci	dbgp_printk("ehci started\n");
4628c2ecf20Sopenharmony_ci	return 0;
4638c2ecf20Sopenharmony_ci}
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_cistatic int dbgp_ehci_controller_reset(void)
4668c2ecf20Sopenharmony_ci{
4678c2ecf20Sopenharmony_ci	int loop = 250 * 1000;
4688c2ecf20Sopenharmony_ci	u32 cmd;
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	/* Reset the EHCI controller */
4718c2ecf20Sopenharmony_ci	cmd = readl(&ehci_regs->command);
4728c2ecf20Sopenharmony_ci	cmd |= CMD_RESET;
4738c2ecf20Sopenharmony_ci	writel(cmd, &ehci_regs->command);
4748c2ecf20Sopenharmony_ci	do {
4758c2ecf20Sopenharmony_ci		cmd = readl(&ehci_regs->command);
4768c2ecf20Sopenharmony_ci	} while ((cmd & CMD_RESET) && (--loop > 0));
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	if (!loop) {
4798c2ecf20Sopenharmony_ci		dbgp_printk("can not reset ehci\n");
4808c2ecf20Sopenharmony_ci		return -1;
4818c2ecf20Sopenharmony_ci	}
4828c2ecf20Sopenharmony_ci	dbgp_ehci_status("ehci reset done");
4838c2ecf20Sopenharmony_ci	return 0;
4848c2ecf20Sopenharmony_ci}
4858c2ecf20Sopenharmony_cistatic int ehci_wait_for_port(int port);
4868c2ecf20Sopenharmony_ci/* Return 0 on success
4878c2ecf20Sopenharmony_ci * Return -ENODEV for any general failure
4888c2ecf20Sopenharmony_ci * Return -EIO if wait for port fails
4898c2ecf20Sopenharmony_ci */
4908c2ecf20Sopenharmony_cistatic int _dbgp_external_startup(void)
4918c2ecf20Sopenharmony_ci{
4928c2ecf20Sopenharmony_ci	int devnum;
4938c2ecf20Sopenharmony_ci	struct usb_debug_descriptor dbgp_desc;
4948c2ecf20Sopenharmony_ci	int ret;
4958c2ecf20Sopenharmony_ci	u32 ctrl, portsc, cmd;
4968c2ecf20Sopenharmony_ci	int dbg_port = dbgp_phys_port;
4978c2ecf20Sopenharmony_ci	int tries = 3;
4988c2ecf20Sopenharmony_ci	int reset_port_tries = 1;
4998c2ecf20Sopenharmony_ci	int try_hard_once = 1;
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_citry_port_reset_again:
5028c2ecf20Sopenharmony_ci	ret = dbgp_ehci_startup();
5038c2ecf20Sopenharmony_ci	if (ret)
5048c2ecf20Sopenharmony_ci		return ret;
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci	/* Wait for a device to show up in the debug port */
5078c2ecf20Sopenharmony_ci	ret = ehci_wait_for_port(dbg_port);
5088c2ecf20Sopenharmony_ci	if (ret < 0) {
5098c2ecf20Sopenharmony_ci		portsc = readl(&ehci_regs->port_status[dbg_port - 1]);
5108c2ecf20Sopenharmony_ci		if (!(portsc & PORT_CONNECT) && try_hard_once) {
5118c2ecf20Sopenharmony_ci			/* Last ditch effort to try to force enable
5128c2ecf20Sopenharmony_ci			 * the debug device by using the packet test
5138c2ecf20Sopenharmony_ci			 * ehci command to try and wake it up. */
5148c2ecf20Sopenharmony_ci			try_hard_once = 0;
5158c2ecf20Sopenharmony_ci			cmd = readl(&ehci_regs->command);
5168c2ecf20Sopenharmony_ci			cmd &= ~CMD_RUN;
5178c2ecf20Sopenharmony_ci			writel(cmd, &ehci_regs->command);
5188c2ecf20Sopenharmony_ci			portsc = readl(&ehci_regs->port_status[dbg_port - 1]);
5198c2ecf20Sopenharmony_ci			portsc |= PORT_TEST_PKT;
5208c2ecf20Sopenharmony_ci			writel(portsc, &ehci_regs->port_status[dbg_port - 1]);
5218c2ecf20Sopenharmony_ci			dbgp_ehci_status("Trying to force debug port online");
5228c2ecf20Sopenharmony_ci			mdelay(50);
5238c2ecf20Sopenharmony_ci			dbgp_ehci_controller_reset();
5248c2ecf20Sopenharmony_ci			goto try_port_reset_again;
5258c2ecf20Sopenharmony_ci		} else if (reset_port_tries--) {
5268c2ecf20Sopenharmony_ci			goto try_port_reset_again;
5278c2ecf20Sopenharmony_ci		}
5288c2ecf20Sopenharmony_ci		dbgp_printk("No device found in debug port\n");
5298c2ecf20Sopenharmony_ci		return -EIO;
5308c2ecf20Sopenharmony_ci	}
5318c2ecf20Sopenharmony_ci	dbgp_ehci_status("wait for port done");
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci	/* Enable the debug port */
5348c2ecf20Sopenharmony_ci	ctrl = readl(&ehci_debug->control);
5358c2ecf20Sopenharmony_ci	ctrl |= DBGP_CLAIM;
5368c2ecf20Sopenharmony_ci	writel(ctrl, &ehci_debug->control);
5378c2ecf20Sopenharmony_ci	ctrl = readl(&ehci_debug->control);
5388c2ecf20Sopenharmony_ci	if ((ctrl & DBGP_CLAIM) != DBGP_CLAIM) {
5398c2ecf20Sopenharmony_ci		dbgp_printk("No device in debug port\n");
5408c2ecf20Sopenharmony_ci		writel(ctrl & ~DBGP_CLAIM, &ehci_debug->control);
5418c2ecf20Sopenharmony_ci		return -ENODEV;
5428c2ecf20Sopenharmony_ci	}
5438c2ecf20Sopenharmony_ci	dbgp_ehci_status("debug ported enabled");
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci	/* Completely transfer the debug device to the debug controller */
5468c2ecf20Sopenharmony_ci	portsc = readl(&ehci_regs->port_status[dbg_port - 1]);
5478c2ecf20Sopenharmony_ci	portsc &= ~PORT_PE;
5488c2ecf20Sopenharmony_ci	writel(portsc, &ehci_regs->port_status[dbg_port - 1]);
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci	dbgp_mdelay(100);
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_citry_again:
5538c2ecf20Sopenharmony_ci	/* Find the debug device and make it device number 127 */
5548c2ecf20Sopenharmony_ci	for (devnum = 0; devnum <= 127; devnum++) {
5558c2ecf20Sopenharmony_ci		ret = dbgp_control_msg(devnum,
5568c2ecf20Sopenharmony_ci			USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE,
5578c2ecf20Sopenharmony_ci			USB_REQ_GET_DESCRIPTOR, (USB_DT_DEBUG << 8), 0,
5588c2ecf20Sopenharmony_ci			&dbgp_desc, sizeof(dbgp_desc));
5598c2ecf20Sopenharmony_ci		if (ret > 0)
5608c2ecf20Sopenharmony_ci			break;
5618c2ecf20Sopenharmony_ci	}
5628c2ecf20Sopenharmony_ci	if (devnum > 127) {
5638c2ecf20Sopenharmony_ci		dbgp_printk("Could not find attached debug device\n");
5648c2ecf20Sopenharmony_ci		goto err;
5658c2ecf20Sopenharmony_ci	}
5668c2ecf20Sopenharmony_ci	dbgp_endpoint_out = dbgp_desc.bDebugOutEndpoint;
5678c2ecf20Sopenharmony_ci	dbgp_endpoint_in = dbgp_desc.bDebugInEndpoint;
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci	/* Move the device to 127 if it isn't already there */
5708c2ecf20Sopenharmony_ci	if (devnum != USB_DEBUG_DEVNUM) {
5718c2ecf20Sopenharmony_ci		ret = dbgp_control_msg(devnum,
5728c2ecf20Sopenharmony_ci			USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE,
5738c2ecf20Sopenharmony_ci			USB_REQ_SET_ADDRESS, USB_DEBUG_DEVNUM, 0, NULL, 0);
5748c2ecf20Sopenharmony_ci		if (ret < 0) {
5758c2ecf20Sopenharmony_ci			dbgp_printk("Could not move attached device to %d\n",
5768c2ecf20Sopenharmony_ci				USB_DEBUG_DEVNUM);
5778c2ecf20Sopenharmony_ci			goto err;
5788c2ecf20Sopenharmony_ci		}
5798c2ecf20Sopenharmony_ci		dbgp_printk("debug device renamed to 127\n");
5808c2ecf20Sopenharmony_ci	}
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_ci	/* Enable the debug interface */
5838c2ecf20Sopenharmony_ci	ret = dbgp_control_msg(USB_DEBUG_DEVNUM,
5848c2ecf20Sopenharmony_ci		USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE,
5858c2ecf20Sopenharmony_ci		USB_REQ_SET_FEATURE, USB_DEVICE_DEBUG_MODE, 0, NULL, 0);
5868c2ecf20Sopenharmony_ci	if (ret < 0) {
5878c2ecf20Sopenharmony_ci		dbgp_printk(" Could not enable the debug device\n");
5888c2ecf20Sopenharmony_ci		goto err;
5898c2ecf20Sopenharmony_ci	}
5908c2ecf20Sopenharmony_ci	dbgp_printk("debug interface enabled\n");
5918c2ecf20Sopenharmony_ci	/* Perform a small write to get the even/odd data state in sync
5928c2ecf20Sopenharmony_ci	 */
5938c2ecf20Sopenharmony_ci	ret = dbgp_bulk_write(USB_DEBUG_DEVNUM, dbgp_endpoint_out, " ", 1);
5948c2ecf20Sopenharmony_ci	if (ret < 0) {
5958c2ecf20Sopenharmony_ci		dbgp_printk("dbgp_bulk_write failed: %d\n", ret);
5968c2ecf20Sopenharmony_ci		goto err;
5978c2ecf20Sopenharmony_ci	}
5988c2ecf20Sopenharmony_ci	dbgp_printk("small write done\n");
5998c2ecf20Sopenharmony_ci	dbgp_not_safe = 0;
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci	return 0;
6028c2ecf20Sopenharmony_cierr:
6038c2ecf20Sopenharmony_ci	if (tries--)
6048c2ecf20Sopenharmony_ci		goto try_again;
6058c2ecf20Sopenharmony_ci	return -ENODEV;
6068c2ecf20Sopenharmony_ci}
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_cistatic int ehci_reset_port(int port)
6098c2ecf20Sopenharmony_ci{
6108c2ecf20Sopenharmony_ci	u32 portsc;
6118c2ecf20Sopenharmony_ci	u32 delay_time, delay;
6128c2ecf20Sopenharmony_ci	int loop;
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci	dbgp_ehci_status("reset port");
6158c2ecf20Sopenharmony_ci	/* Reset the usb debug port */
6168c2ecf20Sopenharmony_ci	portsc = readl(&ehci_regs->port_status[port - 1]);
6178c2ecf20Sopenharmony_ci	portsc &= ~PORT_PE;
6188c2ecf20Sopenharmony_ci	portsc |= PORT_RESET;
6198c2ecf20Sopenharmony_ci	writel(portsc, &ehci_regs->port_status[port - 1]);
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci	delay = HUB_ROOT_RESET_TIME;
6228c2ecf20Sopenharmony_ci	for (delay_time = 0; delay_time < HUB_RESET_TIMEOUT;
6238c2ecf20Sopenharmony_ci	     delay_time += delay) {
6248c2ecf20Sopenharmony_ci		dbgp_mdelay(delay);
6258c2ecf20Sopenharmony_ci		portsc = readl(&ehci_regs->port_status[port - 1]);
6268c2ecf20Sopenharmony_ci		if (!(portsc & PORT_RESET))
6278c2ecf20Sopenharmony_ci			break;
6288c2ecf20Sopenharmony_ci	}
6298c2ecf20Sopenharmony_ci	if (portsc & PORT_RESET) {
6308c2ecf20Sopenharmony_ci		/* force reset to complete */
6318c2ecf20Sopenharmony_ci		loop = 100 * 1000;
6328c2ecf20Sopenharmony_ci		writel(portsc & ~(PORT_RWC_BITS | PORT_RESET),
6338c2ecf20Sopenharmony_ci			&ehci_regs->port_status[port - 1]);
6348c2ecf20Sopenharmony_ci		do {
6358c2ecf20Sopenharmony_ci			udelay(1);
6368c2ecf20Sopenharmony_ci			portsc = readl(&ehci_regs->port_status[port-1]);
6378c2ecf20Sopenharmony_ci		} while ((portsc & PORT_RESET) && (--loop > 0));
6388c2ecf20Sopenharmony_ci	}
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ci	/* Device went away? */
6418c2ecf20Sopenharmony_ci	if (!(portsc & PORT_CONNECT))
6428c2ecf20Sopenharmony_ci		return -ENOTCONN;
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_ci	/* bomb out completely if something weird happened */
6458c2ecf20Sopenharmony_ci	if ((portsc & PORT_CSC))
6468c2ecf20Sopenharmony_ci		return -EINVAL;
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_ci	/* If we've finished resetting, then break out of the loop */
6498c2ecf20Sopenharmony_ci	if (!(portsc & PORT_RESET) && (portsc & PORT_PE))
6508c2ecf20Sopenharmony_ci		return 0;
6518c2ecf20Sopenharmony_ci	return -EBUSY;
6528c2ecf20Sopenharmony_ci}
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_cistatic int ehci_wait_for_port(int port)
6558c2ecf20Sopenharmony_ci{
6568c2ecf20Sopenharmony_ci	u32 status;
6578c2ecf20Sopenharmony_ci	int ret, reps;
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_ci	for (reps = 0; reps < 300; reps++) {
6608c2ecf20Sopenharmony_ci		status = readl(&ehci_regs->status);
6618c2ecf20Sopenharmony_ci		if (status & STS_PCD)
6628c2ecf20Sopenharmony_ci			break;
6638c2ecf20Sopenharmony_ci		dbgp_mdelay(1);
6648c2ecf20Sopenharmony_ci	}
6658c2ecf20Sopenharmony_ci	ret = ehci_reset_port(port);
6668c2ecf20Sopenharmony_ci	if (ret == 0)
6678c2ecf20Sopenharmony_ci		return 0;
6688c2ecf20Sopenharmony_ci	return -ENOTCONN;
6698c2ecf20Sopenharmony_ci}
6708c2ecf20Sopenharmony_ci
6718c2ecf20Sopenharmony_citypedef void (*set_debug_port_t)(int port);
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_cistatic void __init default_set_debug_port(int port)
6748c2ecf20Sopenharmony_ci{
6758c2ecf20Sopenharmony_ci}
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_cistatic set_debug_port_t __initdata set_debug_port = default_set_debug_port;
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_cistatic void __init nvidia_set_debug_port(int port)
6808c2ecf20Sopenharmony_ci{
6818c2ecf20Sopenharmony_ci	u32 dword;
6828c2ecf20Sopenharmony_ci	dword = read_pci_config(ehci_dev.bus, ehci_dev.slot, ehci_dev.func,
6838c2ecf20Sopenharmony_ci				 0x74);
6848c2ecf20Sopenharmony_ci	dword &= ~(0x0f<<12);
6858c2ecf20Sopenharmony_ci	dword |= ((port & 0x0f)<<12);
6868c2ecf20Sopenharmony_ci	write_pci_config(ehci_dev.bus, ehci_dev.slot, ehci_dev.func, 0x74,
6878c2ecf20Sopenharmony_ci				 dword);
6888c2ecf20Sopenharmony_ci	dbgp_printk("set debug port to %d\n", port);
6898c2ecf20Sopenharmony_ci}
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_cistatic void __init detect_set_debug_port(void)
6928c2ecf20Sopenharmony_ci{
6938c2ecf20Sopenharmony_ci	u32 vendorid;
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_ci	vendorid = read_pci_config(ehci_dev.bus, ehci_dev.slot, ehci_dev.func,
6968c2ecf20Sopenharmony_ci		 0x00);
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci	if ((vendorid & 0xffff) == 0x10de) {
6998c2ecf20Sopenharmony_ci		dbgp_printk("using nvidia set_debug_port\n");
7008c2ecf20Sopenharmony_ci		set_debug_port = nvidia_set_debug_port;
7018c2ecf20Sopenharmony_ci	}
7028c2ecf20Sopenharmony_ci}
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci/* The code in early_ehci_bios_handoff() is derived from the usb pci
7058c2ecf20Sopenharmony_ci * quirk initialization, but altered so as to use the early PCI
7068c2ecf20Sopenharmony_ci * routines. */
7078c2ecf20Sopenharmony_ci#define EHCI_USBLEGSUP_BIOS	(1 << 16)	/* BIOS semaphore */
7088c2ecf20Sopenharmony_ci#define EHCI_USBLEGCTLSTS	4		/* legacy control/status */
7098c2ecf20Sopenharmony_cistatic void __init early_ehci_bios_handoff(void)
7108c2ecf20Sopenharmony_ci{
7118c2ecf20Sopenharmony_ci	u32 hcc_params = readl(&ehci_caps->hcc_params);
7128c2ecf20Sopenharmony_ci	int offset = (hcc_params >> 8) & 0xff;
7138c2ecf20Sopenharmony_ci	u32 cap;
7148c2ecf20Sopenharmony_ci	int msec;
7158c2ecf20Sopenharmony_ci
7168c2ecf20Sopenharmony_ci	if (!offset)
7178c2ecf20Sopenharmony_ci		return;
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_ci	cap = read_pci_config(ehci_dev.bus, ehci_dev.slot,
7208c2ecf20Sopenharmony_ci			      ehci_dev.func, offset);
7218c2ecf20Sopenharmony_ci	dbgp_printk("dbgp: ehci BIOS state %08x\n", cap);
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_ci	if ((cap & 0xff) == 1 && (cap & EHCI_USBLEGSUP_BIOS)) {
7248c2ecf20Sopenharmony_ci		dbgp_printk("dbgp: BIOS handoff\n");
7258c2ecf20Sopenharmony_ci		write_pci_config_byte(ehci_dev.bus, ehci_dev.slot,
7268c2ecf20Sopenharmony_ci				      ehci_dev.func, offset + 3, 1);
7278c2ecf20Sopenharmony_ci	}
7288c2ecf20Sopenharmony_ci
7298c2ecf20Sopenharmony_ci	/* if boot firmware now owns EHCI, spin till it hands it over. */
7308c2ecf20Sopenharmony_ci	msec = 1000;
7318c2ecf20Sopenharmony_ci	while ((cap & EHCI_USBLEGSUP_BIOS) && (msec > 0)) {
7328c2ecf20Sopenharmony_ci		mdelay(10);
7338c2ecf20Sopenharmony_ci		msec -= 10;
7348c2ecf20Sopenharmony_ci		cap = read_pci_config(ehci_dev.bus, ehci_dev.slot,
7358c2ecf20Sopenharmony_ci				      ehci_dev.func, offset);
7368c2ecf20Sopenharmony_ci	}
7378c2ecf20Sopenharmony_ci
7388c2ecf20Sopenharmony_ci	if (cap & EHCI_USBLEGSUP_BIOS) {
7398c2ecf20Sopenharmony_ci		/* well, possibly buggy BIOS... try to shut it down,
7408c2ecf20Sopenharmony_ci		 * and hope nothing goes too wrong */
7418c2ecf20Sopenharmony_ci		dbgp_printk("dbgp: BIOS handoff failed: %08x\n", cap);
7428c2ecf20Sopenharmony_ci		write_pci_config_byte(ehci_dev.bus, ehci_dev.slot,
7438c2ecf20Sopenharmony_ci				      ehci_dev.func, offset + 2, 0);
7448c2ecf20Sopenharmony_ci	}
7458c2ecf20Sopenharmony_ci
7468c2ecf20Sopenharmony_ci	/* just in case, always disable EHCI SMIs */
7478c2ecf20Sopenharmony_ci	write_pci_config_byte(ehci_dev.bus, ehci_dev.slot, ehci_dev.func,
7488c2ecf20Sopenharmony_ci			      offset + EHCI_USBLEGCTLSTS, 0);
7498c2ecf20Sopenharmony_ci}
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_cistatic int __init ehci_setup(void)
7528c2ecf20Sopenharmony_ci{
7538c2ecf20Sopenharmony_ci	u32 ctrl, portsc, hcs_params;
7548c2ecf20Sopenharmony_ci	u32 debug_port, new_debug_port = 0, n_ports;
7558c2ecf20Sopenharmony_ci	int ret, i;
7568c2ecf20Sopenharmony_ci	int port_map_tried;
7578c2ecf20Sopenharmony_ci	int playtimes = 3;
7588c2ecf20Sopenharmony_ci
7598c2ecf20Sopenharmony_ci	early_ehci_bios_handoff();
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_citry_next_time:
7628c2ecf20Sopenharmony_ci	port_map_tried = 0;
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_citry_next_port:
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_ci	hcs_params = readl(&ehci_caps->hcs_params);
7678c2ecf20Sopenharmony_ci	debug_port = HCS_DEBUG_PORT(hcs_params);
7688c2ecf20Sopenharmony_ci	dbgp_phys_port = debug_port;
7698c2ecf20Sopenharmony_ci	n_ports    = HCS_N_PORTS(hcs_params);
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_ci	dbgp_printk("debug_port: %d\n", debug_port);
7728c2ecf20Sopenharmony_ci	dbgp_printk("n_ports:    %d\n", n_ports);
7738c2ecf20Sopenharmony_ci	dbgp_ehci_status("");
7748c2ecf20Sopenharmony_ci
7758c2ecf20Sopenharmony_ci	for (i = 1; i <= n_ports; i++) {
7768c2ecf20Sopenharmony_ci		portsc = readl(&ehci_regs->port_status[i-1]);
7778c2ecf20Sopenharmony_ci		dbgp_printk("portstatus%d: %08x\n", i, portsc);
7788c2ecf20Sopenharmony_ci	}
7798c2ecf20Sopenharmony_ci
7808c2ecf20Sopenharmony_ci	if (port_map_tried && (new_debug_port != debug_port)) {
7818c2ecf20Sopenharmony_ci		if (--playtimes) {
7828c2ecf20Sopenharmony_ci			set_debug_port(new_debug_port);
7838c2ecf20Sopenharmony_ci			goto try_next_time;
7848c2ecf20Sopenharmony_ci		}
7858c2ecf20Sopenharmony_ci		return -1;
7868c2ecf20Sopenharmony_ci	}
7878c2ecf20Sopenharmony_ci
7888c2ecf20Sopenharmony_ci	/* Only reset the controller if it is not already in the
7898c2ecf20Sopenharmony_ci	 * configured state */
7908c2ecf20Sopenharmony_ci	if (!(readl(&ehci_regs->configured_flag) & FLAG_CF)) {
7918c2ecf20Sopenharmony_ci		if (dbgp_ehci_controller_reset() != 0)
7928c2ecf20Sopenharmony_ci			return -1;
7938c2ecf20Sopenharmony_ci	} else {
7948c2ecf20Sopenharmony_ci		dbgp_ehci_status("ehci skip - already configured");
7958c2ecf20Sopenharmony_ci	}
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_ci	ret = _dbgp_external_startup();
7988c2ecf20Sopenharmony_ci	if (ret == -EIO)
7998c2ecf20Sopenharmony_ci		goto next_debug_port;
8008c2ecf20Sopenharmony_ci
8018c2ecf20Sopenharmony_ci	if (ret < 0) {
8028c2ecf20Sopenharmony_ci		/* Things didn't work so remove my claim */
8038c2ecf20Sopenharmony_ci		ctrl = readl(&ehci_debug->control);
8048c2ecf20Sopenharmony_ci		ctrl &= ~(DBGP_CLAIM | DBGP_OUT);
8058c2ecf20Sopenharmony_ci		writel(ctrl, &ehci_debug->control);
8068c2ecf20Sopenharmony_ci		return -1;
8078c2ecf20Sopenharmony_ci	}
8088c2ecf20Sopenharmony_ci	return 0;
8098c2ecf20Sopenharmony_ci
8108c2ecf20Sopenharmony_cinext_debug_port:
8118c2ecf20Sopenharmony_ci	port_map_tried |= (1<<(debug_port - 1));
8128c2ecf20Sopenharmony_ci	new_debug_port = ((debug_port-1+1)%n_ports) + 1;
8138c2ecf20Sopenharmony_ci	if (port_map_tried != ((1<<n_ports) - 1)) {
8148c2ecf20Sopenharmony_ci		set_debug_port(new_debug_port);
8158c2ecf20Sopenharmony_ci		goto try_next_port;
8168c2ecf20Sopenharmony_ci	}
8178c2ecf20Sopenharmony_ci	if (--playtimes) {
8188c2ecf20Sopenharmony_ci		set_debug_port(new_debug_port);
8198c2ecf20Sopenharmony_ci		goto try_next_time;
8208c2ecf20Sopenharmony_ci	}
8218c2ecf20Sopenharmony_ci
8228c2ecf20Sopenharmony_ci	return -1;
8238c2ecf20Sopenharmony_ci}
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_ciint __init early_dbgp_init(char *s)
8268c2ecf20Sopenharmony_ci{
8278c2ecf20Sopenharmony_ci	u32 debug_port, bar, offset;
8288c2ecf20Sopenharmony_ci	u32 bus, slot, func, cap;
8298c2ecf20Sopenharmony_ci	void __iomem *ehci_bar;
8308c2ecf20Sopenharmony_ci	u32 dbgp_num;
8318c2ecf20Sopenharmony_ci	u32 bar_val;
8328c2ecf20Sopenharmony_ci	char *e;
8338c2ecf20Sopenharmony_ci	int ret;
8348c2ecf20Sopenharmony_ci	u8 byte;
8358c2ecf20Sopenharmony_ci
8368c2ecf20Sopenharmony_ci	if (!early_pci_allowed())
8378c2ecf20Sopenharmony_ci		return -1;
8388c2ecf20Sopenharmony_ci
8398c2ecf20Sopenharmony_ci	dbgp_num = 0;
8408c2ecf20Sopenharmony_ci	if (*s)
8418c2ecf20Sopenharmony_ci		dbgp_num = simple_strtoul(s, &e, 10);
8428c2ecf20Sopenharmony_ci	dbgp_printk("dbgp_num: %d\n", dbgp_num);
8438c2ecf20Sopenharmony_ci
8448c2ecf20Sopenharmony_ci	cap = find_dbgp(dbgp_num, &bus, &slot, &func);
8458c2ecf20Sopenharmony_ci	if (!cap)
8468c2ecf20Sopenharmony_ci		return -1;
8478c2ecf20Sopenharmony_ci
8488c2ecf20Sopenharmony_ci	dbgp_printk("Found EHCI debug port on %02x:%02x.%1x\n", bus, slot,
8498c2ecf20Sopenharmony_ci			 func);
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci	debug_port = read_pci_config(bus, slot, func, cap);
8528c2ecf20Sopenharmony_ci	bar = (debug_port >> 29) & 0x7;
8538c2ecf20Sopenharmony_ci	bar = (bar * 4) + 0xc;
8548c2ecf20Sopenharmony_ci	offset = (debug_port >> 16) & 0xfff;
8558c2ecf20Sopenharmony_ci	dbgp_printk("bar: %02x offset: %03x\n", bar, offset);
8568c2ecf20Sopenharmony_ci	if (bar != PCI_BASE_ADDRESS_0) {
8578c2ecf20Sopenharmony_ci		dbgp_printk("only debug ports on bar 1 handled.\n");
8588c2ecf20Sopenharmony_ci
8598c2ecf20Sopenharmony_ci		return -1;
8608c2ecf20Sopenharmony_ci	}
8618c2ecf20Sopenharmony_ci
8628c2ecf20Sopenharmony_ci	bar_val = read_pci_config(bus, slot, func, PCI_BASE_ADDRESS_0);
8638c2ecf20Sopenharmony_ci	dbgp_printk("bar_val: %02x offset: %03x\n", bar_val, offset);
8648c2ecf20Sopenharmony_ci	if (bar_val & ~PCI_BASE_ADDRESS_MEM_MASK) {
8658c2ecf20Sopenharmony_ci		dbgp_printk("only simple 32bit mmio bars supported\n");
8668c2ecf20Sopenharmony_ci
8678c2ecf20Sopenharmony_ci		return -1;
8688c2ecf20Sopenharmony_ci	}
8698c2ecf20Sopenharmony_ci
8708c2ecf20Sopenharmony_ci	/* double check if the mem space is enabled */
8718c2ecf20Sopenharmony_ci	byte = read_pci_config_byte(bus, slot, func, 0x04);
8728c2ecf20Sopenharmony_ci	if (!(byte & 0x2)) {
8738c2ecf20Sopenharmony_ci		byte  |= 0x02;
8748c2ecf20Sopenharmony_ci		write_pci_config_byte(bus, slot, func, 0x04, byte);
8758c2ecf20Sopenharmony_ci		dbgp_printk("mmio for ehci enabled\n");
8768c2ecf20Sopenharmony_ci	}
8778c2ecf20Sopenharmony_ci
8788c2ecf20Sopenharmony_ci	/*
8798c2ecf20Sopenharmony_ci	 * FIXME I don't have the bar size so just guess PAGE_SIZE is more
8808c2ecf20Sopenharmony_ci	 * than enough.  1K is the biggest I have seen.
8818c2ecf20Sopenharmony_ci	 */
8828c2ecf20Sopenharmony_ci	set_fixmap_nocache(FIX_DBGP_BASE, bar_val & PAGE_MASK);
8838c2ecf20Sopenharmony_ci	ehci_bar = (void __iomem *)__fix_to_virt(FIX_DBGP_BASE);
8848c2ecf20Sopenharmony_ci	ehci_bar += bar_val & ~PAGE_MASK;
8858c2ecf20Sopenharmony_ci	dbgp_printk("ehci_bar: %p\n", ehci_bar);
8868c2ecf20Sopenharmony_ci
8878c2ecf20Sopenharmony_ci	ehci_caps  = ehci_bar;
8888c2ecf20Sopenharmony_ci	ehci_regs  = ehci_bar + EARLY_HC_LENGTH(readl(&ehci_caps->hc_capbase));
8898c2ecf20Sopenharmony_ci	ehci_debug = ehci_bar + offset;
8908c2ecf20Sopenharmony_ci	ehci_dev.bus = bus;
8918c2ecf20Sopenharmony_ci	ehci_dev.slot = slot;
8928c2ecf20Sopenharmony_ci	ehci_dev.func = func;
8938c2ecf20Sopenharmony_ci
8948c2ecf20Sopenharmony_ci	detect_set_debug_port();
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_ci	ret = ehci_setup();
8978c2ecf20Sopenharmony_ci	if (ret < 0) {
8988c2ecf20Sopenharmony_ci		dbgp_printk("ehci_setup failed\n");
8998c2ecf20Sopenharmony_ci		ehci_debug = NULL;
9008c2ecf20Sopenharmony_ci
9018c2ecf20Sopenharmony_ci		return -1;
9028c2ecf20Sopenharmony_ci	}
9038c2ecf20Sopenharmony_ci	dbgp_ehci_status("early_init_complete");
9048c2ecf20Sopenharmony_ci
9058c2ecf20Sopenharmony_ci	return 0;
9068c2ecf20Sopenharmony_ci}
9078c2ecf20Sopenharmony_ci
9088c2ecf20Sopenharmony_cistatic void early_dbgp_write(struct console *con, const char *str, u32 n)
9098c2ecf20Sopenharmony_ci{
9108c2ecf20Sopenharmony_ci	int chunk;
9118c2ecf20Sopenharmony_ci	char buf[DBGP_MAX_PACKET];
9128c2ecf20Sopenharmony_ci	int use_cr = 0;
9138c2ecf20Sopenharmony_ci	u32 cmd, ctrl;
9148c2ecf20Sopenharmony_ci	int reset_run = 0;
9158c2ecf20Sopenharmony_ci
9168c2ecf20Sopenharmony_ci	if (!ehci_debug || dbgp_not_safe)
9178c2ecf20Sopenharmony_ci		return;
9188c2ecf20Sopenharmony_ci
9198c2ecf20Sopenharmony_ci	cmd = readl(&ehci_regs->command);
9208c2ecf20Sopenharmony_ci	if (unlikely(!(cmd & CMD_RUN))) {
9218c2ecf20Sopenharmony_ci		/* If the ehci controller is not in the run state do extended
9228c2ecf20Sopenharmony_ci		 * checks to see if the acpi or some other initialization also
9238c2ecf20Sopenharmony_ci		 * reset the ehci debug port */
9248c2ecf20Sopenharmony_ci		ctrl = readl(&ehci_debug->control);
9258c2ecf20Sopenharmony_ci		if (!(ctrl & DBGP_ENABLED)) {
9268c2ecf20Sopenharmony_ci			dbgp_not_safe = 1;
9278c2ecf20Sopenharmony_ci			_dbgp_external_startup();
9288c2ecf20Sopenharmony_ci		} else {
9298c2ecf20Sopenharmony_ci			cmd |= CMD_RUN;
9308c2ecf20Sopenharmony_ci			writel(cmd, &ehci_regs->command);
9318c2ecf20Sopenharmony_ci			reset_run = 1;
9328c2ecf20Sopenharmony_ci		}
9338c2ecf20Sopenharmony_ci	}
9348c2ecf20Sopenharmony_ci	while (n > 0) {
9358c2ecf20Sopenharmony_ci		for (chunk = 0; chunk < DBGP_MAX_PACKET && n > 0;
9368c2ecf20Sopenharmony_ci		     str++, chunk++, n--) {
9378c2ecf20Sopenharmony_ci			if (!use_cr && *str == '\n') {
9388c2ecf20Sopenharmony_ci				use_cr = 1;
9398c2ecf20Sopenharmony_ci				buf[chunk] = '\r';
9408c2ecf20Sopenharmony_ci				str--;
9418c2ecf20Sopenharmony_ci				n++;
9428c2ecf20Sopenharmony_ci				continue;
9438c2ecf20Sopenharmony_ci			}
9448c2ecf20Sopenharmony_ci			if (use_cr)
9458c2ecf20Sopenharmony_ci				use_cr = 0;
9468c2ecf20Sopenharmony_ci			buf[chunk] = *str;
9478c2ecf20Sopenharmony_ci		}
9488c2ecf20Sopenharmony_ci		if (chunk > 0) {
9498c2ecf20Sopenharmony_ci			dbgp_bulk_write(USB_DEBUG_DEVNUM,
9508c2ecf20Sopenharmony_ci					dbgp_endpoint_out, buf, chunk);
9518c2ecf20Sopenharmony_ci		}
9528c2ecf20Sopenharmony_ci	}
9538c2ecf20Sopenharmony_ci	if (unlikely(reset_run)) {
9548c2ecf20Sopenharmony_ci		cmd = readl(&ehci_regs->command);
9558c2ecf20Sopenharmony_ci		cmd &= ~CMD_RUN;
9568c2ecf20Sopenharmony_ci		writel(cmd, &ehci_regs->command);
9578c2ecf20Sopenharmony_ci	}
9588c2ecf20Sopenharmony_ci}
9598c2ecf20Sopenharmony_ci
9608c2ecf20Sopenharmony_cistruct console early_dbgp_console = {
9618c2ecf20Sopenharmony_ci	.name =		"earlydbg",
9628c2ecf20Sopenharmony_ci	.write =	early_dbgp_write,
9638c2ecf20Sopenharmony_ci	.flags =	CON_PRINTBUFFER,
9648c2ecf20Sopenharmony_ci	.index =	-1,
9658c2ecf20Sopenharmony_ci};
9668c2ecf20Sopenharmony_ci
9678c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_USB)
9688c2ecf20Sopenharmony_ciint dbgp_reset_prep(struct usb_hcd *hcd)
9698c2ecf20Sopenharmony_ci{
9708c2ecf20Sopenharmony_ci	int ret = xen_dbgp_reset_prep(hcd);
9718c2ecf20Sopenharmony_ci	u32 ctrl;
9728c2ecf20Sopenharmony_ci
9738c2ecf20Sopenharmony_ci	if (ret)
9748c2ecf20Sopenharmony_ci		return ret;
9758c2ecf20Sopenharmony_ci
9768c2ecf20Sopenharmony_ci	dbgp_not_safe = 1;
9778c2ecf20Sopenharmony_ci	if (!ehci_debug)
9788c2ecf20Sopenharmony_ci		return 0;
9798c2ecf20Sopenharmony_ci
9808c2ecf20Sopenharmony_ci	if ((early_dbgp_console.index != -1 &&
9818c2ecf20Sopenharmony_ci	     !(early_dbgp_console.flags & CON_BOOT)) ||
9828c2ecf20Sopenharmony_ci	    dbgp_kgdb_mode)
9838c2ecf20Sopenharmony_ci		return 1;
9848c2ecf20Sopenharmony_ci	/* This means the console is not initialized, or should get
9858c2ecf20Sopenharmony_ci	 * shutdown so as to allow for reuse of the usb device, which
9868c2ecf20Sopenharmony_ci	 * means it is time to shutdown the usb debug port. */
9878c2ecf20Sopenharmony_ci	ctrl = readl(&ehci_debug->control);
9888c2ecf20Sopenharmony_ci	if (ctrl & DBGP_ENABLED) {
9898c2ecf20Sopenharmony_ci		ctrl &= ~(DBGP_CLAIM);
9908c2ecf20Sopenharmony_ci		writel(ctrl, &ehci_debug->control);
9918c2ecf20Sopenharmony_ci	}
9928c2ecf20Sopenharmony_ci	return 0;
9938c2ecf20Sopenharmony_ci}
9948c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dbgp_reset_prep);
9958c2ecf20Sopenharmony_ci
9968c2ecf20Sopenharmony_ciint dbgp_external_startup(struct usb_hcd *hcd)
9978c2ecf20Sopenharmony_ci{
9988c2ecf20Sopenharmony_ci	return xen_dbgp_external_startup(hcd) ?: _dbgp_external_startup();
9998c2ecf20Sopenharmony_ci}
10008c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dbgp_external_startup);
10018c2ecf20Sopenharmony_ci#endif /* USB */
10028c2ecf20Sopenharmony_ci
10038c2ecf20Sopenharmony_ci#ifdef CONFIG_KGDB
10048c2ecf20Sopenharmony_ci
10058c2ecf20Sopenharmony_cistatic char kgdbdbgp_buf[DBGP_MAX_PACKET];
10068c2ecf20Sopenharmony_cistatic int kgdbdbgp_buf_sz;
10078c2ecf20Sopenharmony_cistatic int kgdbdbgp_buf_idx;
10088c2ecf20Sopenharmony_cistatic int kgdbdbgp_loop_cnt = DBGP_LOOPS;
10098c2ecf20Sopenharmony_ci
10108c2ecf20Sopenharmony_cistatic int kgdbdbgp_read_char(void)
10118c2ecf20Sopenharmony_ci{
10128c2ecf20Sopenharmony_ci	int ret;
10138c2ecf20Sopenharmony_ci
10148c2ecf20Sopenharmony_ci	if (kgdbdbgp_buf_idx < kgdbdbgp_buf_sz) {
10158c2ecf20Sopenharmony_ci		char ch = kgdbdbgp_buf[kgdbdbgp_buf_idx++];
10168c2ecf20Sopenharmony_ci		return ch;
10178c2ecf20Sopenharmony_ci	}
10188c2ecf20Sopenharmony_ci
10198c2ecf20Sopenharmony_ci	ret = dbgp_bulk_read(USB_DEBUG_DEVNUM, dbgp_endpoint_in,
10208c2ecf20Sopenharmony_ci			     &kgdbdbgp_buf, DBGP_MAX_PACKET,
10218c2ecf20Sopenharmony_ci			     kgdbdbgp_loop_cnt);
10228c2ecf20Sopenharmony_ci	if (ret <= 0)
10238c2ecf20Sopenharmony_ci		return NO_POLL_CHAR;
10248c2ecf20Sopenharmony_ci	kgdbdbgp_buf_sz = ret;
10258c2ecf20Sopenharmony_ci	kgdbdbgp_buf_idx = 1;
10268c2ecf20Sopenharmony_ci	return kgdbdbgp_buf[0];
10278c2ecf20Sopenharmony_ci}
10288c2ecf20Sopenharmony_ci
10298c2ecf20Sopenharmony_cistatic void kgdbdbgp_write_char(u8 chr)
10308c2ecf20Sopenharmony_ci{
10318c2ecf20Sopenharmony_ci	early_dbgp_write(NULL, &chr, 1);
10328c2ecf20Sopenharmony_ci}
10338c2ecf20Sopenharmony_ci
10348c2ecf20Sopenharmony_cistatic struct kgdb_io kgdbdbgp_io_ops = {
10358c2ecf20Sopenharmony_ci	.name = "kgdbdbgp",
10368c2ecf20Sopenharmony_ci	.read_char = kgdbdbgp_read_char,
10378c2ecf20Sopenharmony_ci	.write_char = kgdbdbgp_write_char,
10388c2ecf20Sopenharmony_ci};
10398c2ecf20Sopenharmony_ci
10408c2ecf20Sopenharmony_cistatic int kgdbdbgp_wait_time;
10418c2ecf20Sopenharmony_ci
10428c2ecf20Sopenharmony_cistatic int __init kgdbdbgp_parse_config(char *str)
10438c2ecf20Sopenharmony_ci{
10448c2ecf20Sopenharmony_ci	char *ptr;
10458c2ecf20Sopenharmony_ci
10468c2ecf20Sopenharmony_ci	if (!ehci_debug) {
10478c2ecf20Sopenharmony_ci		if (early_dbgp_init(str))
10488c2ecf20Sopenharmony_ci			return -1;
10498c2ecf20Sopenharmony_ci	}
10508c2ecf20Sopenharmony_ci	ptr = strchr(str, ',');
10518c2ecf20Sopenharmony_ci	if (ptr) {
10528c2ecf20Sopenharmony_ci		ptr++;
10538c2ecf20Sopenharmony_ci		kgdbdbgp_wait_time = simple_strtoul(ptr, &ptr, 10);
10548c2ecf20Sopenharmony_ci	}
10558c2ecf20Sopenharmony_ci	kgdb_register_io_module(&kgdbdbgp_io_ops);
10568c2ecf20Sopenharmony_ci	if (early_dbgp_console.index != -1)
10578c2ecf20Sopenharmony_ci		kgdbdbgp_io_ops.cons = &early_dbgp_console;
10588c2ecf20Sopenharmony_ci
10598c2ecf20Sopenharmony_ci	return 0;
10608c2ecf20Sopenharmony_ci}
10618c2ecf20Sopenharmony_ciearly_param("kgdbdbgp", kgdbdbgp_parse_config);
10628c2ecf20Sopenharmony_ci
10638c2ecf20Sopenharmony_cistatic int kgdbdbgp_reader_thread(void *ptr)
10648c2ecf20Sopenharmony_ci{
10658c2ecf20Sopenharmony_ci	int ret;
10668c2ecf20Sopenharmony_ci
10678c2ecf20Sopenharmony_ci	while (readl(&ehci_debug->control) & DBGP_ENABLED) {
10688c2ecf20Sopenharmony_ci		kgdbdbgp_loop_cnt = 1;
10698c2ecf20Sopenharmony_ci		ret = kgdbdbgp_read_char();
10708c2ecf20Sopenharmony_ci		kgdbdbgp_loop_cnt = DBGP_LOOPS;
10718c2ecf20Sopenharmony_ci		if (ret != NO_POLL_CHAR) {
10728c2ecf20Sopenharmony_ci			if (ret == 0x3 || ret == '$') {
10738c2ecf20Sopenharmony_ci				if (ret == '$')
10748c2ecf20Sopenharmony_ci					kgdbdbgp_buf_idx--;
10758c2ecf20Sopenharmony_ci				kgdb_breakpoint();
10768c2ecf20Sopenharmony_ci			}
10778c2ecf20Sopenharmony_ci			continue;
10788c2ecf20Sopenharmony_ci		}
10798c2ecf20Sopenharmony_ci		schedule_timeout_interruptible(kgdbdbgp_wait_time * HZ);
10808c2ecf20Sopenharmony_ci	}
10818c2ecf20Sopenharmony_ci	return 0;
10828c2ecf20Sopenharmony_ci}
10838c2ecf20Sopenharmony_ci
10848c2ecf20Sopenharmony_cistatic int __init kgdbdbgp_start_thread(void)
10858c2ecf20Sopenharmony_ci{
10868c2ecf20Sopenharmony_ci	if (dbgp_kgdb_mode && kgdbdbgp_wait_time)
10878c2ecf20Sopenharmony_ci		kthread_run(kgdbdbgp_reader_thread, NULL, "%s", "dbgp");
10888c2ecf20Sopenharmony_ci
10898c2ecf20Sopenharmony_ci	return 0;
10908c2ecf20Sopenharmony_ci}
10918c2ecf20Sopenharmony_cidevice_initcall(kgdbdbgp_start_thread);
10928c2ecf20Sopenharmony_ci#endif /* CONFIG_KGDB */
1093