-----Original Message----- From: Greg KH [mailto:gregkh@suse.de]
I can do that in about 15 minutes if you give me the device ids for the usb debug device that you wish to have.
Or you can also use the generic usb-serial driver today just fine with no modification. Have you had a problem with using that option?
We are talking about using USB debug device/EHCI debug port in LinuxBIOS in legacy free PC. Because one AM2+MCP55 MB doesn't have serial port.
I guess Eric is working on USB debug device/EHCI debug port for earlyprintk or printk.
So we need one client program on host side. So it would great if we could use current USB stack for the clients on system even without debug port.
I'm getting one USB debug device cable, and will test generic usb_serial driver.
Thanks
YH
On Fri, Dec 01, 2006 at 10:55:48AM -0800, Lu, Yinghai wrote:
-----Original Message----- From: Greg KH [mailto:gregkh@suse.de]
I can do that in about 15 minutes if you give me the device ids for the usb debug device that you wish to have.
Or you can also use the generic usb-serial driver today just fine with no modification. Have you had a problem with using that option?
We are talking about using USB debug device/EHCI debug port in LinuxBIOS in legacy free PC. Because one AM2+MCP55 MB doesn't have serial port.
I guess Eric is working on USB debug device/EHCI debug port for earlyprintk or printk.
Well, earlyprintk will not work, as you need PCI up and running.
And I have some code that barely works for this already, perhaps Eric and I should work together on this :)
So we need one client program on host side. So it would great if we could use current USB stack for the clients on system even without debug port.
Yes, that will work just fine today using the usb-serial generic driver. I'll knock up a "real" driver for the device later today and send it to Linus, as it's trivial to do so, and will make it simpler than using the module parameters.
thanks,
greg k-h
On Fri, Dec 01, 2006 at 11:19:16AM -0800, Greg KH wrote:
Well, earlyprintk will not work, as you need PCI up and running.
Not all of it though. LinuxBIOS will probably do just enough PCI setup to talk to the EHCI controller and use the debug port _very_ soon after power on.
And I have some code that barely works for this already, perhaps Eric and I should work together on this :)
I would be interested in having a look at any code for it too.
Yes, that will work just fine today using the usb-serial generic driver.
Ugh. I did not know it was that generic. The irony is that I always ask other libusb users to check the kernel drivers to see if they really need to write a libusb app.
I'll knock up a "real" driver for the device later today and send it to Linus, as it's trivial to do so, and will make it simpler than using the module parameters.
Awesome. Thanks!
//Peter
Peter Stuge stuge-linuxbios@cdy.org writes:
On Fri, Dec 01, 2006 at 11:19:16AM -0800, Greg KH wrote:
Well, earlyprintk will not work, as you need PCI up and running.
Not all of it though. LinuxBIOS will probably do just enough PCI setup to talk to the EHCI controller and use the debug port _very_ soon after power on.
Right. For LinuxBIOS not a problem for earlyprintk in the kernel somethings might need to be refactored. The challenge in the kernel is we don't know at build to how to do a pci_read_config...
The other hard part early in the kernel is the fact that the bar is memory mapped I/O. Which means it will need to get mapped into the kernels page tables.
And I have some code that barely works for this already, perhaps Eric and I should work together on this :)
I would be interested in having a look at any code for it too.
Sure, I will send it out shortly. I currently have a working user space libusb thing (easy, but useful for my debug) and a rude read/write to the bar from user space program that allowed me to debug the worst of the state machine from user space. I don't think I have the state setup logic correct yet but that is minor in comparison.
I really wish the EHCI spec had made that stupid interface 16 bytes instead of 8 or had a way to chain multiple access together. The we could have used a normal usb cable. As it is most descriptors are 1 byte to big to read.
Yes, that will work just fine today using the usb-serial generic driver.
Ugh. I did not know it was that generic. The irony is that I always ask other libusb users to check the kernel drivers to see if they really need to write a libusb app.
I'll knock up a "real" driver for the device later today and send it to Linus, as it's trivial to do so, and will make it simpler than using the module parameters.
Awesome. Thanks!
Yep. It looks like it sufficient generic. The Maximum packet size appears to be reported correctly for writes which is the tidbit I was worried about but otherwise it is just a pair of bulk transfer endpoints.
Eric
On Fri, Dec 01, 2006 at 02:15:24PM -0700, Eric W. Biederman wrote:
Right. For LinuxBIOS not a problem for earlyprintk in the kernel somethings might need to be refactored. The challenge in the kernel is we don't know at build to how to do a pci_read_config...
The other hard part early in the kernel is the fact that the bar is memory mapped I/O. Which means it will need to get mapped into the kernels page tables.
I see.
And I have some code that barely works for this already, perhaps Eric and I should work together on this :)
I would be interested in having a look at any code for it too.
Sure, I will send it out shortly. I currently have a working user space libusb thing (easy, but useful for my debug)
Hm - for driving which end?
and a rude read/write to the bar from user space program that
How does that work? /dev/{port,mem}?
allowed me to debug the worst of the state machine from user space. I don't think I have the state setup logic correct yet but that is minor in comparison.
I really wish the EHCI spec had made that stupid interface 16 bytes instead of 8 or had a way to chain multiple access together. The we could have used a normal usb cable. As it is most descriptors are 1 byte to big to read.
Which descriptors are you reading?
The debug port isn't really supposed to be used with anything but a debug device - which can't be enumerated normally anyway.
//Peter
Peter Stuge stuge-linuxbios@cdy.org writes:
On Fri, Dec 01, 2006 at 02:15:24PM -0700, Eric W. Biederman wrote:
Right. For LinuxBIOS not a problem for earlyprintk in the kernel somethings might need to be refactored. The challenge in the kernel is we don't know at build to how to do a pci_read_config...
The other hard part early in the kernel is the fact that the bar is memory mapped I/O. Which means it will need to get mapped into the kernels page tables.
I see.
And I have some code that barely works for this already, perhaps Eric and I should work together on this :)
I would be interested in having a look at any code for it too.
Sure, I will send it out shortly. I currently have a working user space libusb thing (easy, but useful for my debug)
Hm - for driving which end?
Either. The specific device we are talking about doesn't care.
and a rude read/write to the bar from user space program that
How does that work? /dev/{port,mem}?
mmap /dev/mem.
allowed me to debug the worst of the state machine from user space. I don't think I have the state setup logic correct yet but that is minor in comparison.
I really wish the EHCI spec had made that stupid interface 16 bytes instead of 8 or had a way to chain multiple access together. The we could have used a normal usb cable. As it is most descriptors are 1 byte too big to read.
Which descriptors are you reading?
Minor. I was just wishing for less magic in this process, which would make these kinds of devices much more available.
The debug port isn't really supposed to be used with anything but a debug device - which can't be enumerated normally anyway.
It depends. If you have a debug cable with magic ends and a hardcoded address of 127 the normal enumeration doesn't work. I don't think anyone actually makes one of those. Debug devices are also allowed to be normal devices that just support the debug descriptor. Which is what I'm working with.
Eric
On Fri, Dec 01, 2006 at 04:02:03PM -0700, Eric W. Biederman wrote:
Sure, I will send it out shortly. I currently have a working user space libusb thing (easy, but useful for my debug)
Hm - for driving which end?
Either. The specific device we are talking about doesn't care.
Which device do you have?
The debug port isn't really supposed to be used with anything but a debug device - which can't be enumerated normally anyway.
It depends. If you have a debug cable with magic ends and a hardcoded address of 127 the normal enumeration doesn't work. I don't think anyone actually makes one of those.
Only one of the ports on Stefan's PLX NET20DC that I had a look at during the LinuxBIOS symposium enumerated for me.
Debug devices are also allowed to be normal devices that just support the debug descriptor. Which is what I'm working with.
Aye. I would be happy if we could get something out, as you have done! :) Looking forward to trying it, I hope I get my device soon.
//Peter
Peter Stuge stuge-linuxbios@cdy.org writes:
On Fri, Dec 01, 2006 at 04:02:03PM -0700, Eric W. Biederman wrote:
Sure, I will send it out shortly. I currently have a working user space libusb thing (easy, but useful for my debug)
Hm - for driving which end?
Either. The specific device we are talking about doesn't care.
Which device do you have?
Well it is built by PLX, and from lsusb I see are: 0525:127a Netchip Technology, Inc.
The hardware is a little rectangular pcb board a little smaller then a business card. Wrapped in a blue case, with vertical vents on both of the long sides, and gets a little warm when you have been running it for a while. The device has what appears to be 2 normal host to slave cables running into it.
The picture at the bottom of: http://advdbg.org/blogs/advdbg_system/articles/64.aspx
Looks like what I have. I'm curious about the whole plug both ends into the host before plugging it into the client, and about the strange target system BIOS requirements.
I think I succeeded in making it work without out that by just putting in a reset. It does make the whole setup of the device a pain though.
The debug port isn't really supposed to be used with anything but a debug device - which can't be enumerated normally anyway.
It depends. If you have a debug cable with magic ends and a hardcoded address of 127 the normal enumeration doesn't work. I don't think anyone actually makes one of those.
Only one of the ports on Stefan's PLX NET20DC that I had a look at during the LinuxBIOS symposium enumerated for me.
Very odd. I'm pretty certain we are talking same thing. But I do know it has a couple of weird quirks, so maybe you just ran up against that.
Debug devices are also allowed to be normal devices that just support the debug descriptor. Which is what I'm working with.
Aye. I would be happy if we could get something out, as you have done! :) Looking forward to trying it, I hope I get my device soon.
Well at least this means after it works I can probably forget about it and let someone else maintain the code ;)
Eric
Greg KH gregkh@suse.de writes:
On Fri, Dec 01, 2006 at 10:55:48AM -0800, Lu, Yinghai wrote:
-----Original Message----- From: Greg KH [mailto:gregkh@suse.de]
I can do that in about 15 minutes if you give me the device ids for the usb debug device that you wish to have.
Or you can also use the generic usb-serial driver today just fine with no modification. Have you had a problem with using that option?
We are talking about using USB debug device/EHCI debug port in LinuxBIOS in legacy free PC. Because one AM2+MCP55 MB doesn't have serial port.
I guess Eric is working on USB debug device/EHCI debug port for earlyprintk or printk.
Well, earlyprintk will not work, as you need PCI up and running.
And I have some code that barely works for this already, perhaps Eric and I should work together on this :)
I'd love to work with someone on this. I'm cc'ing Andi Kleen because he asked me where we had gotten on this the other day.
One big thing we need is a way to tell if you have the system booted if your device is plugged into the usb port that connects to the usb debug port. Figuring out which usb port you really have to plug into is still trying and error but at least being able to tell without having to try the code is good.
So here is my mostly somewhat working code. I don't understand what you have todo if you want to reset the device and then find the device so this code currently only works if you have ehci_hcd already loaded.
I am avoiding the pci bit simply by having someone pass me the hard coded numbers...
I think I can get it down to a single base address if I don't print debugging of which port you are plugged into, or try and debug the state out of reset.
usbtest.c is my little libusb client program. It's useful for exploring things.
usbdebug_direct.c is roughly a driver living in user space so I can debug the hard bits of the logic. Not a good production technique but great for prototyping. It has all of the basic primitives needed to actually use the ehci debug port.
The next big thing for me I guess is to modify a kernel and see what state the usb ports are in when I am booting and how much of the reset logic I need to understand to make this work. Greg I expect you understand that a little better than I do.
Eric
Greg KH gregkh@suse.de writes:
On Fri, Dec 01, 2006 at 10:55:48AM -0800, Lu, Yinghai wrote:
-----Original Message----- From: Greg KH [mailto:gregkh@suse.de]
I can do that in about 15 minutes if you give me the device ids for the usb debug device that you wish to have.
Or you can also use the generic usb-serial driver today just fine with no modification. Have you had a problem with using that option?
We are talking about using USB debug device/EHCI debug port in LinuxBIOS in legacy free PC. Because one AM2+MCP55 MB doesn't have serial port.
I guess Eric is working on USB debug device/EHCI debug port for earlyprintk or printk.
Well, earlyprintk will not work, as you need PCI up and running.
*grin* I just generated the bootlog below. So I think I have it working. There is a lot of cleanup left and I need some sleep but it works for me. I will generate a patch to start the conversation after I wake up.
And I have some code that barely works for this already, perhaps Eric and I should work together on this :)
Eric
Linux version 2.6.19-rc6devel (eric@fess.biederman.org) (gcc version 4.1.1 2006 0525 (Red Hat 4.1.1-1)) #153 SMP Sun Dec 3 07:56:52 MST 2006 Command line: ro root=LABEL=/ rhgb earlyprintk=dbgp console=tty0 console=ttyS0,1 15200 panic=30 BIOS-provided physical RAM map: BIOS-e820: 0000000000000000 - 000000000009fc00 (usable) BIOS-e820: 000000000009fc00 - 00000000000a0000 (reserved) BIOS-e820: 00000000000e6000 - 0000000000100000 (reserved) BIOS-e820: 0000000000100000 - 000000009ffd0000 (usable) BIOS-e820: 000000009ffd0000 - 000000009ffde000 (ACPI data) BIOS-e820: 000000009ffde000 - 00000000a0000000 (ACPI NVS) BIOS-e820: 00000000fec00000 - 00000000fec01000 (reserved) BIOS-e820: 00000000fee00000 - 00000000fee01000 (reserved) BIOS-e820: 00000000ff780000 - 0000000100000000 (reserved) BIOS-e820: 0000000100000000 - 000000024c000000 (usable) end_pfn_map = 2408448 kernel direct mapping tables up to 24c000000 @ 8000-13000 DMI 2.3 present. SRAT: PXM 0 -> APIC 0 -> Node 0 SRAT: PXM 0 -> APIC 1 -> Node 0 SRAT: PXM 1 -> APIC 2 -> Node 1 SRAT: PXM 1 -> APIC 3 -> Node 1 SRAT: Node 0 PXM 0 100000-a0000000 SRAT: Node 1 PXM 1 14c000000-24c000000 SRAT: Node 0 PXM 0 100000-14c000000 SRAT: Node 0 PXM 0 0-14c000000 Bootmem setup node 0 0000000000000000-000000014c000000 Bootmem setup node 1 000000014c000000-000000024c000000 Zone PFN ranges: DMA 0 -> 4096 DMA32 4096 -> 1048576 Normal 1048576 -> 2408448 early_node_map[4] active PFN ranges 0: 0 -> 159 0: 256 -> 655312 0: 1048576 -> 1359872 1: 1359872 -> 2408448 Nvidia board detected. Ignoring ACPI timer override. If you got timer trouble try acpi_use_timer_override ACPI: PM-Timer IO Port: 0x4008 ACPI: LAPIC (acpi_id[0x01] lapic_id[0x00] enabled) Processor #0 (Bootup-CPU) ACPI: LAPIC (acpi_id[0x02] lapic_id[0x01] enabled) Processor #1 ACPI: LAPIC (acpi_id[0x03] lapic_id[0x02] enabled) Processor #2 ACPI: LAPIC (acpi_id[0x04] lapic_id[0x03] enabled) Processor #3 ACPI: IOAPIC (id[0x04] address[0xfec00000] gsi_base[0]) IOAPIC[0]: apic_id 4, address 0xfec00000, GSI 0-23 ACPI: IOAPIC (id[0x05] address[0xdfefc000] gsi_base[24]) IOAPIC[1]: apic_id 5, address 0xdfefc000, GSI 24-47 ACPI: INT_SRC_OVR (bus 0 bus_irq 0 global_irq 2 dfl dfl) ACPI: BIOS IRQ0 pin2 override ignored. ACPI: INT_SRC_OVR (bus 0 bus_irq 9 global_irq 9 high level) Setting APIC routing to flat Using ACPI (MADT) for SMP configuration information Nosave address range: 000000000009f000 - 00000000000a0000 Nosave address range: 00000000000a0000 - 00000000000e6000 Nosave address range: 00000000000e6000 - 0000000000100000 Nosave address range: 000000009ffd0000 - 000000009ffde000 Nosave address range: 000000009ffde000 - 00000000a0000000 Nosave address range: 00000000a0000000 - 00000000fec00000 Nosave address range: 00000000fec00000 - 00000000fec01000 Nosave address range: 00000000fec01000 - 00000000fee00000 Nosave address range: 00000000fee00000 - 00000000fee01000 Nosave address range: 00000000fee01000 - 00000000ff780000 Nosave address range: 00000000ff780000 - 0000000100000000 Allocating PCI resources starting at a8000000 (gap: a0000000:5ec00000) PERCPU: Allocating 66560 bytes of per cpu data Built 2 zonelists. Total pages: 1960348 Kernel command line: ro root=LABEL=/ rhgb earlyprintk=dbgp console=tty0 console= ttyS0,115200 panic=30 Initializing CPU#0 PID hash table entries: 4096 (order: 12, 32768 bytes) disabling early console
With legacy free systems serial ports have stopped being an option to get early boot traces and other debug information out of a machine.
EHCI USB controllers provide a relatively simple debug interface that can control port 1 of the root hub. This interface is limited to 8 byte packets so it can be used with most USB devices. But with a USB debug device this is sufficient to talk to another machine.
When the special feature of the EHCI is not enabled the port 1 of the root hub acts just like any other USB port so machines with the necessary support are widely available.
This debug device can be used to replace serial ports for kgdb, kdb, and console support. And gregkh has a simple usb serial driver for it so user space applications that control serial ports should work unmodified.
Currently there only appears to be one manufacturer of debug devices see: http://www.plxtech.com/products/NET2000/NET20DC/default.asp
I think simple RS232 serial ports provide a nicer and simpler interface but the usb debug port looks like a functional alternative when you don't have that.
My code likely doesn't handle all of the corner cases yet, and needs a little more work to integrate cleanly into the build. But this is getting it out there so other people can look and help make clean drivers. When writing a polling driver you do have to be careful with your logic, because if you do things like reset a usb device at the wrong time you can completely confuse various EHCI controllers.
My driver should be sufficient to work with any EHCI in a realatively clean state, and needs no special BIOS support just the hardware. This appears to be different than the way the windows drivers are using these debug devices.
Eric
To use the debug device I need to work through a port so I need to call ioreamp or similar. Since we are doing this very early I chose to use a fixmap (because we are unlikely to free the mapping) and because it is simple. If we preallocate the fixmap pud and pmd entries the existing fixmap codes works anytime from power up without modifications or memory allocations. So we don't need a special case.
--- arch/x86_64/kernel/head.S | 11 ++++++++++- 1 files changed, 10 insertions(+), 1 deletions(-)
diff --git a/arch/x86_64/kernel/head.S b/arch/x86_64/kernel/head.S index 2f65469..4004965 100644 --- a/arch/x86_64/kernel/head.S +++ b/arch/x86_64/kernel/head.S @@ -271,7 +271,16 @@ NEXT_PAGE(level3_kernel_pgt) .fill 510,8,0 /* (2^48-(2*1024*1024*1024)-((2^39)*511))/(2^30) = 510 */ .quad phys_level2_kernel_pgt | 0x007 - .fill 1,8,0 + .quad phys_level2_fixmap_pgt | 0x007 + +NEXT_PAGE(level2_fixmap_pgt) + .fill 506,8,0 + .quad phys_level1_fixmap_pgt | 0x007 + /* 8MB reserved for vsyscalls + a 2MB hole = 4 + 1 entries */ + .fill 5,8,0 + +NEXT_PAGE(level1_fixmap_pgt) + .fill 512,8,0
NEXT_PAGE(level2_ident_pgt) /* 40MB for bootup. */
This patch is still a little rough but it gets all of the major pieces correct. With a little more work this should become a maintainable driver.
In particular I believe the ehci debug port code is fairly solid (with rough edges). However there are several nasty bits that need to be resolved before we can push this upstream. Like my include of ehci.h And general early kernel space dependency problems.
But the code works for me so it should at least be a reasonable starting point for others looking to make the debug port work in a reasonable fashion.
Signed-off-by: Eric W. Biederman ebiederm@xmission.com --- arch/x86_64/kernel/early_printk.c | 574 +++++++++++++++++++++++++++++++++++++ drivers/usb/host/ehci.h | 8 + include/asm-x86_64/fixmap.h | 1 3 files changed, 583 insertions(+), 0 deletions(-)
diff --git a/arch/x86_64/kernel/early_printk.c b/arch/x86_64/kernel/early_printk.c index d4050a5..cb5182d 100644 --- a/arch/x86_64/kernel/early_printk.c +++ b/arch/x86_64/kernel/early_printk.c @@ -3,9 +3,19 @@ #include <linux/kernel.h> #include <linux/init.h> #include <linux/string.h> #include <linux/screen_info.h> +#include <linux/usb_ch9.h> +#include <linux/pci_regs.h> +#include <linux/pci_ids.h> +#include <linux/errno.h> #include <asm/io.h> #include <asm/processor.h> #include <asm/fcntl.h> +#include <asm/pci-direct.h> +#include <asm/pgtable.h> +#include <asm/fixmap.h> +#define EARLY_PRINTK +#include "../../../drivers/usb/host/ehci.h" +
/* Simple VGA output */
@@ -155,6 +165,558 @@ static struct console early_serial_conso .index = -1, };
+ +static struct ehci_caps __iomem *ehci_caps; +static struct ehci_regs __iomem *ehci_regs; +static struct ehci_dbg_port __iomem *ehci_debug; +static unsigned dbgp_endpoint_out; + +#define USB_DEBUG_DEVNUM 127 + +#define DBGP_DATA_TOGGLE 0x8800 +#define DBGP_PID_UPDATE(x, tok) \ + ((((x) ^ DBGP_DATA_TOGGLE) & 0xffff00) | ((tok) & 0xff)) + +#define DBGP_LEN_UPDATE(x, len) (((x) & ~0x0f) | ((len) & 0x0f)) +/* + * 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 + +static int dbgp_wait_until_complete(void) +{ + unsigned ctrl; + for (;;) { + ctrl = readl(&ehci_debug->control); + /* Stop when the transaction is finished */ + if (ctrl & DBGP_DONE) + break; + } + /* 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); +} + +static void dbgp_mdelay(int ms) +{ + int i; + while (ms--) { + for (i = 0; i < 1000; i++) + outb(0x1, 0x80); + } +} + +static void dbgp_breath(void) +{ + /* Sleep to give the debug port a chance to breathe */ +} + +static int dbgp_wait_until_done(unsigned ctrl) +{ + unsigned pids, lpid; + int ret; + +retry: + writel(ctrl | DBGP_GO, &ehci_debug->control); + ret = dbgp_wait_until_complete(); + pids = readl(&ehci_debug->pids); + lpid = DBGP_PID_GET(pids); + + if (ret < 0) + 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) + goto retry; + + return ret; +} + +static void dbgp_set_data(const void *buf, int size) +{ + const unsigned char *bytes = buf; + unsigned 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); +} + +static void dbgp_get_data(void *buf, int size) +{ + unsigned char *bytes = buf; + unsigned 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; +} + +static int dbgp_bulk_write(unsigned devnum, unsigned endpoint, const char *bytes, int size) +{ + unsigned pids, addr, ctrl; + int ret; + if (size > DBGP_MAX_PACKET) + return -1; + + addr = DBGP_EPADDR(devnum, endpoint); + + pids = readl(&ehci_debug->pids); + pids = DBGP_PID_UPDATE(pids, USB_PID_OUT); + + ctrl = readl(&ehci_debug->control); + ctrl = DBGP_LEN_UPDATE(ctrl, size); + ctrl |= DBGP_OUT; + ctrl |= DBGP_GO; + + dbgp_set_data(bytes, size); + writel(addr, &ehci_debug->address); + writel(pids, &ehci_debug->pids); + + ret = dbgp_wait_until_done(ctrl); + if (ret < 0) { + return ret; + } + return ret; +} + +static int dbgp_bulk_read(unsigned devnum, unsigned endpoint, void *data, int size) +{ + unsigned pids, addr, ctrl; + int ret; + + if (size > DBGP_MAX_PACKET) + return -1; + + addr = DBGP_EPADDR(devnum, endpoint); + + pids = readl(&ehci_debug->pids); + pids = DBGP_PID_UPDATE(pids, USB_PID_IN); + + ctrl = readl(&ehci_debug->control); + ctrl = DBGP_LEN_UPDATE(ctrl, size); + ctrl &= ~DBGP_OUT; + ctrl |= DBGP_GO; + + writel(addr, &ehci_debug->address); + writel(pids, &ehci_debug->pids); + ret = dbgp_wait_until_done(ctrl); + if (ret < 0) + return ret; + if (size > ret) + size = ret; + dbgp_get_data(data, size); + return ret; +} + +static int dbgp_control_msg(unsigned devnum, int requesttype, int request, + int value, int index, void *data, int size) +{ + unsigned pids, addr, ctrl; + struct usb_ctrlrequest req; + int read; + int ret; + + read = (requesttype & USB_DIR_IN) != 0; + if (size > (read?DBGP_MAX_PACKET:0)) + return -1; + + /* Compute the control message */ + req.bRequestType = requesttype; + req.bRequest = request; + req.wValue = value; + req.wIndex = index; + req.wLength = size; + + pids = DBGP_PID_SET(USB_PID_DATA0, USB_PID_SETUP); + addr = DBGP_EPADDR(devnum, 0); + + ctrl = readl(&ehci_debug->control); + ctrl = DBGP_LEN_UPDATE(ctrl, sizeof(req)); + ctrl |= DBGP_OUT; + ctrl |= DBGP_GO; + + /* Send the setup message */ + dbgp_set_data(&req, sizeof(req)); + writel(addr, &ehci_debug->address); + writel(pids, &ehci_debug->pids); + ret = dbgp_wait_until_done(ctrl); + if (ret < 0) + return ret; + + + /* Read the result */ + ret = dbgp_bulk_read(devnum, 0, data, size); + return ret; +} + + +/* Find a PCI capability */ +static __u32 __init find_cap(int num, int slot, int func, int cap) +{ + u8 pos; + int bytes; + if (!(read_pci_config_16(num,slot,func,PCI_STATUS) & PCI_STATUS_CAP_LIST)) + return 0; + pos = read_pci_config_byte(num,slot,func,PCI_CAPABILITY_LIST); + for (bytes = 0; bytes < 48 && pos >= 0x40; bytes++) { + u8 id; + pos &= ~3; + id = read_pci_config_byte(num,slot,func,pos+PCI_CAP_LIST_ID); + if (id == 0xff) + break; + if (id == cap) + return pos; + pos = read_pci_config_byte(num,slot,func,pos+PCI_CAP_LIST_NEXT); + } + return 0; +} + +static __u32 __init find_dbgp(int ehci_num, unsigned *rbus, unsigned *rslot, unsigned *rfunc) +{ + unsigned bus, slot, func; + + for (bus = 0; bus < 256; bus++) { + for (slot = 0; slot < 32; slot++) { + for (func = 0; func < 8; func++) { + u32 class; + unsigned cap; + class = read_pci_config(bus, slot, func, PCI_CLASS_REVISION); + if ((class >> 8) != PCI_CLASS_SERIAL_USB_EHCI) + continue; + cap = find_cap(bus, slot, func, PCI_CAP_ID_EHCI_DEBUG); + if (!cap) + continue; + if (ehci_num-- != 0) + continue; + *rbus = bus; + *rslot = slot; + *rfunc = func; + return cap; + } + } + } + return 0; +} + +static int ehci_reset_port(int port) +{ + unsigned portsc; + unsigned delay_time, delay; + + /* Reset the usb debug port */ + portsc = readl(&ehci_regs->port_status[port - 1]); + portsc &= ~PORT_PE; + portsc |= PORT_RESET; + writel(portsc, &ehci_regs->port_status[port - 1]); + + delay = HUB_ROOT_RESET_TIME; + for (delay_time = 0; delay_time < HUB_RESET_TIMEOUT; + delay_time += delay) { + dbgp_mdelay(delay); + + portsc = readl(&ehci_regs->port_status[port - 1]); + if (portsc & PORT_RESET) { + /* force reset to complete */ + writel(portsc & ~(PORT_RWC_BITS | PORT_RESET), + &ehci_regs->port_status[port - 1]); + while (portsc & PORT_RESET) + portsc = readl(&ehci_regs->port_status[port - 1]); + } + + /* Device went away? */ + if (!(portsc & PORT_CONNECT)) + return -ENOTCONN; + + /* bomb out completely if something weird happend */ + if ((portsc & PORT_CSC)) + return -EINVAL; + + /* If we've finished resetting, then break out of the loop */ + if (!(portsc & PORT_RESET) && (portsc & PORT_PE)) + return 0; + } + return -EBUSY; +} + +static int ehci_wait_for_port(int port) +{ + unsigned status; + int ret, reps; + for (reps = 0; reps >= 0; reps++) { + status = readl(&ehci_regs->status); + if (status & STS_PCD) { + ret = ehci_reset_port(port); + if (ret == 0) + return 0; + } + } + return -ENOTCONN; +} + + +#define DBGP_DEBUG 0 +#if DBGP_DEBUG +void early_printk(const char *fmt, ...); +# define dbgp_printk early_printk +#else +static inline void dbgp_printk(const char *fmt, ...) { } +#endif + +static int ehci_setup(void) +{ + unsigned cmd, ctrl, status, portsc, hcs_params, debug_port, n_ports; + int ret; + int i; + + hcs_params = readl(&ehci_caps->hcs_params); + debug_port = HCS_DEBUG_PORT(hcs_params); + n_ports = HCS_N_PORTS(hcs_params); + + dbgp_printk("debug_port: %d\n", debug_port); + dbgp_printk("n_ports: %d\n", n_ports); + + /* Reset the EHCI controller */ + cmd = readl(&ehci_regs->command); + cmd |=CMD_RESET; + writel(cmd, &ehci_regs->command); + while (cmd & CMD_RESET) + cmd = readl(&ehci_regs->command); + + /* Claim ownership, but do not enable yet */ + ctrl = readl(&ehci_debug->control); + ctrl |= DBGP_OWNER; + ctrl &= ~(DBGP_ENABLED | DBGP_INUSE); + writel(ctrl, &ehci_debug->control); + + /* Start the ehci running */ + cmd = readl(&ehci_regs->command); + cmd &= ~(CMD_LRESET | CMD_IAAD | CMD_PSE | CMD_ASE | CMD_RESET); + cmd |= CMD_RUN; + writel(cmd, &ehci_regs->command); + + /* Ensure everything is routed to the EHCI */ + writel(FLAG_CF, &ehci_regs->configured_flag); + + /* Wait until the controller is no longer halted */ + do { + status = readl(&ehci_regs->status); + } while (status & STS_HALT); + + /* Wait for a device to show up in the debug port */ + ret = ehci_wait_for_port(debug_port); + if (ret < 0) { + dbgp_printk("No device found in debug port\n"); + return -1; + } + + /* 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 -1; + + } + + /* Completely transfer the debug device to the debug controller */ + portsc = readl(&ehci_regs->port_status[debug_port - 1]); + portsc &= ~PORT_PE; + writel(portsc, &ehci_regs->port_status[debug_port - 1]); + + return 0; +} + +static __init void early_dbgp_init(char *s) +{ + struct usb_debug_descriptor dbgp_desc; + void __iomem *ehci_bar; + unsigned ctrl, devnum; + unsigned bus, slot, func, cap; + unsigned debug_port, bar, offset; + unsigned bar_val; + unsigned dbgp_num; + char *e; + int ret; + + if (!early_pci_allowed()) + return; + + dbgp_num = 0; + if (*s) { + dbgp_num = simple_strtoul(s, &e, 10); + } + dbgp_printk("dbgp_num: %d\n", dbgp_num); + cap = find_dbgp(dbgp_num, &bus, &slot, &func); + if (!cap) + return; + + dbgp_printk("Found EHCI debug port\n"); + + debug_port = read_pci_config(bus, slot, func, cap); + bar = (debug_port >> 29) & 0x7; + bar = (bar * 4) + 0xc; + offset = (debug_port >> 16) & 0xfff; + if (bar != PCI_BASE_ADDRESS_0) { + dbgp_printk("only debug ports on bar 1 handled.\n"); + return; + } + + /* FIXME this assumes the bar is a 32bit mmio bar */ + bar_val = read_pci_config(bus, slot, func, PCI_BASE_ADDRESS_0); + + /* 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; + + ehci_caps = ehci_bar; + ehci_regs = ehci_bar + HC_LENGTH(readl(&ehci_caps->hc_capbase)); + ehci_debug = ehci_bar + offset; + + ret = ehci_setup(); + if (ret < 0) { + dbgp_printk("ehci_setup failed\n"); + return; + } + + /* 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; + } + if (ret < 0) { + dbgp_printk("Attached device is not a debug device\n"); + goto err; + } + dbgp_endpoint_out = dbgp_desc.bDebugOutEndpoint; + + /* Move the device to 127 if it isn't already there */ + if (devnum != USB_DEBUG_DEVNUM) { + ret = dbgp_control_msg(devnum, + USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE, + USB_REQ_SET_ADDRESS, USB_DEBUG_DEVNUM, 0, NULL, 0); + if (ret < 0) { + dbgp_printk("Could not move attached device to %d\n", + USB_DEBUG_DEVNUM); + goto err; + } + devnum = USB_DEBUG_DEVNUM; + } + + /* 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; + } + + /* 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; + } + + + return; +err: + /* Things didn't work so remove my claim */ + ctrl = readl(&ehci_debug->control); + ctrl &= ~(DBGP_CLAIM | DBGP_OUT); + writel(ctrl, &ehci_debug->control); + return; +} + +static void early_dbgp_write(struct console *con, const char *str, unsigned n) +{ + int chunk, ret; + if (!ehci_debug) + return; + while (n > 0) { + chunk = n; + if (chunk > DBGP_MAX_PACKET) + chunk = DBGP_MAX_PACKET; + ret = dbgp_bulk_write(USB_DEBUG_DEVNUM, + dbgp_endpoint_out, str, chunk); + str += chunk; + n -= chunk; + } +} + +static struct console early_dbgp_console = { + .name = "earlydbg", + .write = early_dbgp_write, + .flags = CON_PRINTBUFFER, + .index = -1, +}; + +#endif + /* Console interface to a host file on AMD's SimNow! */
static int simnow_fd; @@ -242,8 +804,20 @@ static int __init setup_early_printk(cha simnow_init(buf + 6); early_console = &simnow_console; keep_early = 1; + } else if (!strncmp(buf, "dbgp", 4)) { + early_dbgp_init(buf + 4); + early_console = &early_dbgp_console; } register_console(early_console); +#if DBGP_DEBUG + { + static const char dbgp_test_str[] = + "The quick brown fox jumped over the lazy dog!\n"; + early_dbgp_init(""); + early_dbgp_write(&early_dbgp_console, + dbgp_test_str, sizeof(dbgp_test_str) - 1); + } +#endif return 0; }
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index bbc3082..0a67192 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -46,6 +46,7 @@ struct ehci_stats {
#define EHCI_MAX_ROOT_PORTS 15 /* see HCS_N_PORTS */
+#ifndef EARLY_PRINTK struct ehci_hcd { /* one per controller */ /* glue to PCI and HCD framework */ struct ehci_caps __iomem *caps; @@ -160,6 +161,7 @@ timer_action (struct ehci_hcd *ehci, enu mod_timer (&ehci->watchdog, t); } } +#endif /* EARLY_PRINTK */
/*-------------------------------------------------------------------------*/
@@ -384,6 +386,7 @@ union ehci_shadow { * These appear in both the async and (for interrupt) periodic schedules. */
+#ifndef EARLY_PRINTK struct ehci_qh { /* first part defined by EHCI spec */ __le32 hw_next; /* see EHCI 3.6.1 */ @@ -432,6 +435,7 @@ #define QH_STATE_COMPLETING 5 /* don't #define NO_FRAME ((unsigned short)~0) /* pick new start */ struct usb_device *dev; /* access to TT */ } __attribute__ ((aligned (32))); +#endif /* EARLY_PRITNK */
/*-------------------------------------------------------------------------*/
@@ -601,6 +605,8 @@ struct ehci_fstn { union ehci_shadow fstn_next; /* ptr to periodic q entry */ } __attribute__ ((aligned (32)));
+#ifndef EARLY_PRINTK + /*-------------------------------------------------------------------------*/
#ifdef CONFIG_USB_EHCI_ROOT_HUB_TT @@ -659,4 +665,6 @@ #endif /* DEBUG */
/*-------------------------------------------------------------------------*/
+#endif /* EARLY_PRINTK */ + #endif /* __LINUX_EHCI_HCD_H */ diff --git a/include/asm-x86_64/fixmap.h b/include/asm-x86_64/fixmap.h index 1b620db..1f2978a 100644 --- a/include/asm-x86_64/fixmap.h +++ b/include/asm-x86_64/fixmap.h @@ -36,6 +36,7 @@ enum fixed_addresses { VSYSCALL_LAST_PAGE, VSYSCALL_FIRST_PAGE = VSYSCALL_LAST_PAGE + ((VSYSCALL_END-VSYSCALL_START) >> PAGE_SHIFT) - 1, VSYSCALL_HPET, + FIX_DBGP_BASE, FIX_HPET_BASE, FIX_APIC_BASE, /* local (CPU) APIC) -- required for SMP or not */ FIX_IO_APIC_BASE_0,
On Sunday 03 December 2006 9:09 pm, Eric W. Biederman wrote:
My driver should be sufficient to work with any EHCI in a realatively clean state, and needs no special BIOS support just the hardware. This appears to be different than the way the windows drivers are using these debug devices.
I'm glad to see someone finally got progress on this ... :)
Separately, I forwarded some stuff I did last year ... maybe it'll help. You seem to have gotten further. Have you also observed that the NetChip device seems to have polarity issues, such that only one end behaves properly?
Note that this should **NOT** be specific to x86_64, since pretty much any PCI based EHCI can do this. I wouldn't be able to use this on my NForce2 box, for example ...
As for EHCI registers, if this really _needs_ to live outside of drivers/usb/host, then I'd suggest <linux/usb/ehci.h> for the relevant declarations ... the <linux/usb/*.h> headers are provided exactly for sharing such declaration between otherwise unrelated parts of the tree.
- Dave
Ok due to popular demands here is the slightly fixed patch that works on both i386 and x86_64. For the i386 version you must not have HIGHMEM64G enabled.
I just rolled it all into one patch as I'm to lazy to transmit all 3 of them.
Eric
arch/i386/kernel/head.S | 8 + arch/x86_64/kernel/early_printk.c | 580 +++++++++++++++++++++++++++++++++++++ arch/x86_64/kernel/head.S | 11 +- drivers/usb/host/ehci.h | 8 + include/asm-i386/fixmap.h | 1 + include/asm-x86_64/fixmap.h | 1 + 6 files changed, 608 insertions(+), 1 deletions(-)
diff --git a/arch/i386/kernel/head.S b/arch/i386/kernel/head.S index ca31f18..f683565 100644 --- a/arch/i386/kernel/head.S +++ b/arch/i386/kernel/head.S @@ -135,6 +135,12 @@ page_pde_offset = (__PAGE_OFFSET >> 20); jb 10b movl %edi,(init_pg_tables_end - __PAGE_OFFSET)
+ /* Do an early initialization of the fixmap area */ + movl $(swapper_pg_dir - __PAGE_OFFSET), %edx + movl $(swapper_pg_pmd - __PAGE_OFFSET), %eax + addl $0x007, %eax /* 0x007 = PRESENT+RW+USER */ + movl %eax, 4092(%edx) + #ifdef CONFIG_SMP xorl %ebx,%ebx /* This is the boot CPU (BSP) */ jmp 3f @@ -477,6 +483,8 @@ ENTRY(_stext) .section ".bss.page_aligned","w" ENTRY(swapper_pg_dir) .fill 1024,4,0 +ENTRY(swapper_pg_pmd) + .fill 1024,4,0 ENTRY(empty_zero_page) .fill 4096,1,0
diff --git a/arch/x86_64/kernel/early_printk.c b/arch/x86_64/kernel/early_printk.c index d4050a5..71f2f88 100644 --- a/arch/x86_64/kernel/early_printk.c +++ b/arch/x86_64/kernel/early_printk.c @@ -3,9 +3,19 @@ #include <linux/init.h> #include <linux/string.h> #include <linux/screen_info.h> +#include <linux/usb_ch9.h> +#include <linux/pci_regs.h> +#include <linux/pci_ids.h> +#include <linux/errno.h> #include <asm/io.h> #include <asm/processor.h> #include <asm/fcntl.h> +#include <asm/pci-direct.h> +#include <asm/pgtable.h> +#include <asm/fixmap.h> +#define EARLY_PRINTK +#include "../../../drivers/usb/host/ehci.h" +
/* Simple VGA output */
@@ -155,6 +165,564 @@ static struct console early_serial_console = { .index = -1, };
+ +static struct ehci_caps __iomem *ehci_caps; +static struct ehci_regs __iomem *ehci_regs; +static struct ehci_dbg_port __iomem *ehci_debug; +static unsigned dbgp_endpoint_out; + +#define USB_DEBUG_DEVNUM 127 + +#define DBGP_DATA_TOGGLE 0x8800 +#define DBGP_PID_UPDATE(x, tok) \ + ((((x) ^ DBGP_DATA_TOGGLE) & 0xffff00) | ((tok) & 0xff)) + +#define DBGP_LEN_UPDATE(x, len) (((x) & ~0x0f) | ((len) & 0x0f)) +/* + * 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 + +static int dbgp_wait_until_complete(void) +{ + unsigned ctrl; + for (;;) { + ctrl = readl(&ehci_debug->control); + /* Stop when the transaction is finished */ + if (ctrl & DBGP_DONE) + break; + } + /* 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); +} + +static void dbgp_mdelay(int ms) +{ + int i; + while (ms--) { + for (i = 0; i < 1000; i++) + outb(0x1, 0x80); + } +} + +static void dbgp_breath(void) +{ + /* Sleep to give the debug port a chance to breathe */ +} + +static int dbgp_wait_until_done(unsigned ctrl) +{ + unsigned pids, lpid; + int ret; + +retry: + writel(ctrl | DBGP_GO, &ehci_debug->control); + ret = dbgp_wait_until_complete(); + pids = readl(&ehci_debug->pids); + lpid = DBGP_PID_GET(pids); + + if (ret < 0) + 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) + goto retry; + + return ret; +} + +static void dbgp_set_data(const void *buf, int size) +{ + const unsigned char *bytes = buf; + unsigned 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); +} + +static void dbgp_get_data(void *buf, int size) +{ + unsigned char *bytes = buf; + unsigned 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; +} + +static int dbgp_bulk_write(unsigned devnum, unsigned endpoint, const char *bytes, int size) +{ + unsigned pids, addr, ctrl; + int ret; + if (size > DBGP_MAX_PACKET) + return -1; + + addr = DBGP_EPADDR(devnum, endpoint); + + pids = readl(&ehci_debug->pids); + pids = DBGP_PID_UPDATE(pids, USB_PID_OUT); + + ctrl = readl(&ehci_debug->control); + ctrl = DBGP_LEN_UPDATE(ctrl, size); + ctrl |= DBGP_OUT; + ctrl |= DBGP_GO; + + dbgp_set_data(bytes, size); + writel(addr, &ehci_debug->address); + writel(pids, &ehci_debug->pids); + + ret = dbgp_wait_until_done(ctrl); + if (ret < 0) { + return ret; + } + return ret; +} + +static int dbgp_bulk_read(unsigned devnum, unsigned endpoint, void *data, int size) +{ + unsigned pids, addr, ctrl; + int ret; + + if (size > DBGP_MAX_PACKET) + return -1; + + addr = DBGP_EPADDR(devnum, endpoint); + + pids = readl(&ehci_debug->pids); + pids = DBGP_PID_UPDATE(pids, USB_PID_IN); + + ctrl = readl(&ehci_debug->control); + ctrl = DBGP_LEN_UPDATE(ctrl, size); + ctrl &= ~DBGP_OUT; + ctrl |= DBGP_GO; + + writel(addr, &ehci_debug->address); + writel(pids, &ehci_debug->pids); + ret = dbgp_wait_until_done(ctrl); + if (ret < 0) + return ret; + if (size > ret) + size = ret; + dbgp_get_data(data, size); + return ret; +} + +static int dbgp_control_msg(unsigned devnum, int requesttype, int request, + int value, int index, void *data, int size) +{ + unsigned pids, addr, ctrl; + struct usb_ctrlrequest req; + int read; + int ret; + + read = (requesttype & USB_DIR_IN) != 0; + if (size > (read?DBGP_MAX_PACKET:0)) + return -1; + + /* Compute the control message */ + req.bRequestType = requesttype; + req.bRequest = request; + req.wValue = value; + req.wIndex = index; + req.wLength = size; + + pids = DBGP_PID_SET(USB_PID_DATA0, USB_PID_SETUP); + addr = DBGP_EPADDR(devnum, 0); + + ctrl = readl(&ehci_debug->control); + ctrl = DBGP_LEN_UPDATE(ctrl, sizeof(req)); + ctrl |= DBGP_OUT; + ctrl |= DBGP_GO; + + /* Send the setup message */ + dbgp_set_data(&req, sizeof(req)); + writel(addr, &ehci_debug->address); + writel(pids, &ehci_debug->pids); + ret = dbgp_wait_until_done(ctrl); + if (ret < 0) + return ret; + + + /* Read the result */ + ret = dbgp_bulk_read(devnum, 0, data, size); + return ret; +} + + +/* Find a PCI capability */ +static __u32 __init find_cap(int num, int slot, int func, int cap) +{ + u8 pos; + int bytes; + if (!(read_pci_config_16(num,slot,func,PCI_STATUS) & PCI_STATUS_CAP_LIST)) + return 0; + pos = read_pci_config_byte(num,slot,func,PCI_CAPABILITY_LIST); + for (bytes = 0; bytes < 48 && pos >= 0x40; bytes++) { + u8 id; + pos &= ~3; + id = read_pci_config_byte(num,slot,func,pos+PCI_CAP_LIST_ID); + if (id == 0xff) + break; + if (id == cap) + return pos; + pos = read_pci_config_byte(num,slot,func,pos+PCI_CAP_LIST_NEXT); + } + return 0; +} + +static __u32 __init find_dbgp(int ehci_num, unsigned *rbus, unsigned *rslot, unsigned *rfunc) +{ + unsigned bus, slot, func; + + for (bus = 0; bus < 256; bus++) { + for (slot = 0; slot < 32; slot++) { + for (func = 0; func < 8; func++) { + u32 class; + unsigned cap; + class = read_pci_config(bus, slot, func, PCI_CLASS_REVISION); + if ((class >> 8) != PCI_CLASS_SERIAL_USB_EHCI) + continue; + cap = find_cap(bus, slot, func, PCI_CAP_ID_EHCI_DEBUG); + if (!cap) + continue; + if (ehci_num-- != 0) + continue; + *rbus = bus; + *rslot = slot; + *rfunc = func; + return cap; + } + } + } + return 0; +} + +static int ehci_reset_port(int port) +{ + unsigned portsc; + unsigned delay_time, delay; + + /* Reset the usb debug port */ + portsc = readl(&ehci_regs->port_status[port - 1]); + portsc &= ~PORT_PE; + portsc |= PORT_RESET; + writel(portsc, &ehci_regs->port_status[port - 1]); + + delay = HUB_ROOT_RESET_TIME; + for (delay_time = 0; delay_time < HUB_RESET_TIMEOUT; + delay_time += delay) { + dbgp_mdelay(delay); + + portsc = readl(&ehci_regs->port_status[port - 1]); + if (portsc & PORT_RESET) { + /* force reset to complete */ + writel(portsc & ~(PORT_RWC_BITS | PORT_RESET), + &ehci_regs->port_status[port - 1]); + while (portsc & PORT_RESET) + portsc = readl(&ehci_regs->port_status[port - 1]); + } + + /* Device went away? */ + if (!(portsc & PORT_CONNECT)) + return -ENOTCONN; + + /* bomb out completely if something weird happend */ + if ((portsc & PORT_CSC)) + return -EINVAL; + + /* If we've finished resetting, then break out of the loop */ + if (!(portsc & PORT_RESET) && (portsc & PORT_PE)) + return 0; + } + return -EBUSY; +} + +static int ehci_wait_for_port(int port) +{ + unsigned status; + int ret, reps; + for (reps = 0; reps >= 0; reps++) { + status = readl(&ehci_regs->status); + if (status & STS_PCD) { + ret = ehci_reset_port(port); + if (ret == 0) + return 0; + } + } + return -ENOTCONN; +} + + +#define DBGP_DEBUG 0 +#if DBGP_DEBUG +void early_printk(const char *fmt, ...); +# define dbgp_printk early_printk +#else +static inline void dbgp_printk(const char *fmt, ...) { } +#endif + +static int ehci_setup(void) +{ + unsigned cmd, ctrl, status, portsc, hcs_params, debug_port, n_ports; + int ret; + + hcs_params = readl(&ehci_caps->hcs_params); + debug_port = HCS_DEBUG_PORT(hcs_params); + n_ports = HCS_N_PORTS(hcs_params); + + dbgp_printk("debug_port: %d\n", debug_port); + dbgp_printk("n_ports: %d\n", n_ports); + + /* Reset the EHCI controller */ + cmd = readl(&ehci_regs->command); + cmd |=CMD_RESET; + writel(cmd, &ehci_regs->command); + while (cmd & CMD_RESET) + cmd = readl(&ehci_regs->command); + + /* Claim ownership, but do not enable yet */ + ctrl = readl(&ehci_debug->control); + ctrl |= DBGP_OWNER; + ctrl &= ~(DBGP_ENABLED | DBGP_INUSE); + writel(ctrl, &ehci_debug->control); + + /* Start the ehci running */ + cmd = readl(&ehci_regs->command); + cmd &= ~(CMD_LRESET | CMD_IAAD | CMD_PSE | CMD_ASE | CMD_RESET); + cmd |= CMD_RUN; + writel(cmd, &ehci_regs->command); + + /* Ensure everything is routed to the EHCI */ + writel(FLAG_CF, &ehci_regs->configured_flag); + + /* Wait until the controller is no longer halted */ + do { + status = readl(&ehci_regs->status); + } while (status & STS_HALT); + + /* Wait for a device to show up in the debug port */ + ret = ehci_wait_for_port(debug_port); + if (ret < 0) { + dbgp_printk("No device found in debug port\n"); + return -1; + } + + /* 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 -1; + + } + + /* Completely transfer the debug device to the debug controller */ + portsc = readl(&ehci_regs->port_status[debug_port - 1]); + portsc &= ~PORT_PE; + writel(portsc, &ehci_regs->port_status[debug_port - 1]); + + return 0; +} + +static __init void early_dbgp_init(char *s) +{ + struct usb_debug_descriptor dbgp_desc; + void __iomem *ehci_bar; + unsigned ctrl, devnum; + unsigned bus, slot, func, cap; + unsigned debug_port, bar, offset; + unsigned bar_val; + unsigned dbgp_num; + char *e; + int ret; + + if (!early_pci_allowed()) + return; + + dbgp_num = 0; + if (*s) { + dbgp_num = simple_strtoul(s, &e, 10); + } + dbgp_printk("dbgp_num: %d\n", dbgp_num); + cap = find_dbgp(dbgp_num, &bus, &slot, &func); + if (!cap) + return; + + dbgp_printk("Found EHCI debug port\n"); + + debug_port = read_pci_config(bus, slot, func, cap); + bar = (debug_port >> 29) & 0x7; + bar = (bar * 4) + 0xc; + offset = (debug_port >> 16) & 0xfff; + dbgp_printk("bar: %02x offset: %03x\n", bar, offset); + if (bar != PCI_BASE_ADDRESS_0) { + dbgp_printk("only debug ports on bar 1 handled.\n"); + return; + } + + bar_val = read_pci_config(bus, slot, func, PCI_BASE_ADDRESS_0); + dbgp_printk("bar: %02x offset: %03x\n", bar, offset); + if (bar_val & ~PCI_BASE_ADDRESS_MEM_MASK) { + dbgp_printk("only simple 32bit mmio bars supported\n"); + return; + } + + + /* FIXME I don't have the bar size so just guess PAGE_SIZE is more + * than enough. 1K is the biggest I have seen. + */ + dbgp_printk("dbgp pre-set_fixmap_nocache\n"); + set_fixmap_nocache(FIX_DBGP_BASE, bar_val & PAGE_MASK); + dbgp_printk("dbgp post-set_fixmap_nocache\n"); + ehci_bar = (void __iomem *)__fix_to_virt(FIX_DBGP_BASE); + ehci_bar += bar_val & ~PAGE_MASK; + dbgp_printk("ehci_bar: %p\n", ehci_bar); + + ehci_caps = ehci_bar; + ehci_regs = ehci_bar + HC_LENGTH(readl(&ehci_caps->hc_capbase)); + ehci_debug = ehci_bar + offset; + + ret = ehci_setup(); + if (ret < 0) { + dbgp_printk("ehci_setup failed\n"); + return; + } + + /* 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; + } + if (ret < 0) { + dbgp_printk("Attached device is not a debug device\n"); + goto err; + } + dbgp_endpoint_out = dbgp_desc.bDebugOutEndpoint; + + /* Move the device to 127 if it isn't already there */ + if (devnum != USB_DEBUG_DEVNUM) { + ret = dbgp_control_msg(devnum, + USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE, + USB_REQ_SET_ADDRESS, USB_DEBUG_DEVNUM, 0, NULL, 0); + if (ret < 0) { + dbgp_printk("Could not move attached device to %d\n", + USB_DEBUG_DEVNUM); + goto err; + } + devnum = USB_DEBUG_DEVNUM; + } + + /* 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; + } + + /* 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; + } + + + return; +err: + /* Things didn't work so remove my claim */ + ctrl = readl(&ehci_debug->control); + ctrl &= ~(DBGP_CLAIM | DBGP_OUT); + writel(ctrl, &ehci_debug->control); + return; +} + +static void early_dbgp_write(struct console *con, const char *str, unsigned n) +{ + int chunk, ret; + if (!ehci_debug) + return; + while (n > 0) { + chunk = n; + if (chunk > DBGP_MAX_PACKET) + chunk = DBGP_MAX_PACKET; + ret = dbgp_bulk_write(USB_DEBUG_DEVNUM, + dbgp_endpoint_out, str, chunk); + str += chunk; + n -= chunk; + } +} + +static struct console early_dbgp_console = { + .name = "earlydbg", + .write = early_dbgp_write, + .flags = CON_PRINTBUFFER, + .index = -1, +}; + /* Console interface to a host file on AMD's SimNow! */
static int simnow_fd; @@ -242,8 +810,20 @@ static int __init setup_early_printk(char *buf) simnow_init(buf + 6); early_console = &simnow_console; keep_early = 1; + } else if (!strncmp(buf, "dbgp", 4)) { + early_dbgp_init(buf + 4); + early_console = &early_dbgp_console; } register_console(early_console); +#if DBGP_DEBUG + { + static const char dbgp_test_str[] = + "The quick brown fox jumped over the lazy dog!\n"; + early_dbgp_init(""); + early_dbgp_write(&early_dbgp_console, + dbgp_test_str, sizeof(dbgp_test_str) - 1); + } +#endif return 0; }
diff --git a/arch/x86_64/kernel/head.S b/arch/x86_64/kernel/head.S index 2f65469..4004965 100644 --- a/arch/x86_64/kernel/head.S +++ b/arch/x86_64/kernel/head.S @@ -271,7 +271,16 @@ NEXT_PAGE(level3_kernel_pgt) .fill 510,8,0 /* (2^48-(2*1024*1024*1024)-((2^39)*511))/(2^30) = 510 */ .quad phys_level2_kernel_pgt | 0x007 - .fill 1,8,0 + .quad phys_level2_fixmap_pgt | 0x007 + +NEXT_PAGE(level2_fixmap_pgt) + .fill 506,8,0 + .quad phys_level1_fixmap_pgt | 0x007 + /* 8MB reserved for vsyscalls + a 2MB hole = 4 + 1 entries */ + .fill 5,8,0 + +NEXT_PAGE(level1_fixmap_pgt) + .fill 512,8,0
NEXT_PAGE(level2_ident_pgt) /* 40MB for bootup. */ diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index bbc3082..0a67192 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -46,6 +46,7 @@ struct ehci_stats {
#define EHCI_MAX_ROOT_PORTS 15 /* see HCS_N_PORTS */
+#ifndef EARLY_PRINTK struct ehci_hcd { /* one per controller */ /* glue to PCI and HCD framework */ struct ehci_caps __iomem *caps; @@ -160,6 +161,7 @@ timer_action (struct ehci_hcd *ehci, enum ehci_timer_action action) mod_timer (&ehci->watchdog, t); } } +#endif /* EARLY_PRINTK */
/*-------------------------------------------------------------------------*/
@@ -384,6 +386,7 @@ union ehci_shadow { * These appear in both the async and (for interrupt) periodic schedules. */
+#ifndef EARLY_PRINTK struct ehci_qh { /* first part defined by EHCI spec */ __le32 hw_next; /* see EHCI 3.6.1 */ @@ -432,6 +435,7 @@ struct ehci_qh { #define NO_FRAME ((unsigned short)~0) /* pick new start */ struct usb_device *dev; /* access to TT */ } __attribute__ ((aligned (32))); +#endif /* EARLY_PRITNK */
/*-------------------------------------------------------------------------*/
@@ -601,6 +605,8 @@ struct ehci_fstn { union ehci_shadow fstn_next; /* ptr to periodic q entry */ } __attribute__ ((aligned (32)));
+#ifndef EARLY_PRINTK + /*-------------------------------------------------------------------------*/
#ifdef CONFIG_USB_EHCI_ROOT_HUB_TT @@ -659,4 +665,6 @@ ehci_port_speed(struct ehci_hcd *ehci, unsigned int portsc)
/*-------------------------------------------------------------------------*/
+#endif /* EARLY_PRINTK */ + #endif /* __LINUX_EHCI_HCD_H */ diff --git a/include/asm-i386/fixmap.h b/include/asm-i386/fixmap.h index 02428cb..ea08885 100644 --- a/include/asm-i386/fixmap.h +++ b/include/asm-i386/fixmap.h @@ -56,6 +56,7 @@ extern unsigned long __FIXADDR_TOP; enum fixed_addresses { FIX_HOLE, FIX_VDSO, + FIX_DBGP_BASE, #ifdef CONFIG_X86_LOCAL_APIC FIX_APIC_BASE, /* local (CPU) APIC) -- required for SMP or not */ #endif diff --git a/include/asm-x86_64/fixmap.h b/include/asm-x86_64/fixmap.h index 1b620db..1f2978a 100644 --- a/include/asm-x86_64/fixmap.h +++ b/include/asm-x86_64/fixmap.h @@ -36,6 +36,7 @@ enum fixed_addresses { VSYSCALL_LAST_PAGE, VSYSCALL_FIRST_PAGE = VSYSCALL_LAST_PAGE + ((VSYSCALL_END-VSYSCALL_START) >> PAGE_SHIFT) - 1, VSYSCALL_HPET, + FIX_DBGP_BASE, FIX_HPET_BASE, FIX_APIC_BASE, /* local (CPU) APIC) -- required for SMP or not */ FIX_IO_APIC_BASE_0,
On Tuesday 05 December 2006 12:01, Eric W. Biederman wrote:
Ok due to popular demands here is the slightly fixed patch that works on both i386 and x86_64. For the i386 version you must not have HIGHMEM64G enabled.
I just rolled it all into one patch as I'm to lazy to transmit all 3 of them.
You should definitely move the usb code to a separate file
Documentation/* needs to document the new option
Also for usb console keep should be made default because the output won't be duplicated.
-Andi
David Brownell david-b@pacbell.net writes:
On Sunday 03 December 2006 9:09 pm, Eric W. Biederman wrote:
My driver should be sufficient to work with any EHCI in a realatively clean state, and needs no special BIOS support just the hardware. This appears to be different than the way the windows drivers are using these debug devices.
I'm glad to see someone finally got progress on this ... :)
Separately, I forwarded some stuff I did last year ... maybe it'll help. You seem to have gotten further. Have you also observed that the NetChip device seems to have polarity issues, such that only one end behaves properly?
I haven't yet. But I don't think I have actually tried turning the cable around in a very meaningful way yet either. Possibly this is something that has been fixed. I know there are some odd issues that I have encountered. Like occasionally I would need to stop the software on one side, or I would need to unplug it when things got sufficiently confused.
Note that this should **NOT** be specific to x86_64, since pretty much any PCI based EHCI can do this. I wouldn't be able to use this on my NForce2 box, for example ...
So I took a quick look what it would take to do this truly generically and even initializing this generally when console code typically is registered looks like a problem. Although only because we don't get around to setting up pci_config space access helpers in a timely manner. To some extent that still sucks because you are still being initialized before the general ehci-hcd code.
Regardless an arch specific i386 variant was easy to throw together. It still needs a bit of work but it basically worked.
As for EHCI registers, if this really _needs_ to live outside of drivers/usb/host, then I'd suggest <linux/usb/ehci.h> for the relevant declarations ... the <linux/usb/*.h> headers are provided exactly for sharing such declaration between otherwise unrelated parts of the tree.
Yep that sounds like the right thing to do. I think I at least need to be called from something outside of drivers/usb and may need the code there.
Doing this in a truly generic fashion looks like a major pain. Because all of the infrastructure needs to be fixed.
Eric