Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751144AbVKJQlM (ORCPT ); Thu, 10 Nov 2005 11:41:12 -0500 Received: (majordomo@vger.kernel.org) by vger.kernel.org id S1751146AbVKJQlL (ORCPT ); Thu, 10 Nov 2005 11:41:11 -0500 Received: from fed1rmmtao08.cox.net ([68.230.241.31]:49118 "EHLO fed1rmmtao08.cox.net") by vger.kernel.org with ESMTP id S1751144AbVKJQlK (ORCPT ); Thu, 10 Nov 2005 11:41:10 -0500 From: Tom Rini To: Andrew Morton Cc: Tom Rini , lkml Message-Id: <20051110164102.20950.3528.sendpatchset@localhost.localdomain> In-Reply-To: <20051110163906.20950.45704.sendpatchset@localhost.localdomain> References: <20051110163906.20950.45704.sendpatchset@localhost.localdomain> Subject: [PATCH,RFC 2.6.14 04/15] KGDB: 8250-like UART driver Date: Thu, 10 Nov 2005 11:40:17 -0500 Sender: linux-kernel-owner@vger.kernel.org X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 21245 Lines: 707 This is the I/O driver for any 8250-compatible UARTs. This also adds some small hooks into the normal serial core so that we can take away the uart and make sure only KGDB is trying to use it. We also make sure that if KGDB_8250 is enabled, SERIAL_8250_NR_UARTS is set to the default of 4. This is so that if we can always depend on that constant in the 8250 driver for giving our table a size. We add a hook to the main core for unregistering a port with a struct uart_port and add a hook into uart_add_one_port for discovered 8250-like uarts. drivers/serial/8250.c | 21 + drivers/serial/8250_kgdb.c | 504 +++++++++++++++++++++++++++++++++++++++++++ drivers/serial/Kconfig | 2 drivers/serial/Makefile | 1 drivers/serial/serial_core.c | 7 include/linux/serial_8250.h | 1 lib/Kconfig.debug | 59 ++++- 7 files changed, 593 insertions(+), 2 deletions(-) Index: linux-2.6.14/drivers/serial/8250.c =================================================================== --- linux-2.6.14.orig/drivers/serial/8250.c +++ linux-2.6.14/drivers/serial/8250.c @@ -2520,6 +2520,27 @@ void serial8250_unregister_port(int line } EXPORT_SYMBOL(serial8250_unregister_port); +/** + * serial8250_unregister_by_port - remove a 16x50 serial port + * at runtime. + * @port: A &struct uart_port that describes the port to remove. + * + * Remove one serial port. This may not be called from interrupt + * context. We hand the port back to the our control. + */ +void serial8250_unregister_by_port(struct uart_port *port) +{ + struct uart_8250_port *uart; + + down(&serial_sem); + uart = serial8250_find_match_or_unused(port); + up(&serial_sem); + + if (uart) + serial8250_unregister_port(uart->port.line); +} +EXPORT_SYMBOL(serial8250_unregister_by_port); + static int __init serial8250_init(void) { int ret, i; Index: linux-2.6.14/drivers/serial/Kconfig =================================================================== --- linux-2.6.14.orig/drivers/serial/Kconfig +++ linux-2.6.14/drivers/serial/Kconfig @@ -87,7 +87,7 @@ config SERIAL_8250_ACPI config SERIAL_8250_NR_UARTS int "Maximum number of 8250/16550 serial ports" - depends on SERIAL_8250 + depends on SERIAL_8250 || KGDB_8250 default "4" help Set this to the number of serial ports you want the driver Index: linux-2.6.14/drivers/serial/Makefile =================================================================== --- linux-2.6.14.orig/drivers/serial/Makefile +++ linux-2.6.14/drivers/serial/Makefile @@ -57,3 +57,4 @@ obj-$(CONFIG_SERIAL_JSM) += jsm/ obj-$(CONFIG_SERIAL_TXX9) += serial_txx9.o obj-$(CONFIG_SERIAL_VR41XX) += vr41xx_siu.o obj-$(CONFIG_SERIAL_SGI_IOC4) += ioc4_serial.o +obj-$(CONFIG_KGDB_8250) += 8250_kgdb.o Index: linux-2.6.14/lib/Kconfig.debug =================================================================== --- linux-2.6.14.orig/lib/Kconfig.debug +++ linux-2.6.14/lib/Kconfig.debug @@ -212,7 +212,7 @@ config KGDB_CONSOLE choice prompt "Method for KGDB communication" depends on KGDB - default KGDB_ONLY_MODULES + default KGDB_8250_NOMODULE default KGDB_MPSC if SERIAL_MPSC default KGDB_CPM_UART if (8xx || 8260) help @@ -232,6 +232,14 @@ config KGDB_ONLY_MODULES Use only kernel modules to configure KGDB I/O after the kernel is booted. +config KGDB_8250_NOMODULE + bool "KGDB: On generic serial port (8250)" + select KGDB_8250 + help + Uses generic serial port (8250) to communicate with the host + GDB. This is independent of the normal (SERIAL_8250) driver + for this chipset. + config KGDB_MPSC bool "KGDB on MV64x60 MPSC" depends on SERIAL_MPSC @@ -246,3 +254,52 @@ config KGDB_CPM_UART help Uses CPM UART to communicate with the host GDB. endchoice + +config KGDB_8250 + tristate "KGDB: On generic serial port (8250)" if !KGDB_8250_NOMODULE + depends on m && KGDB_ONLY_MODULES + help + Uses generic serial port (8250) to communicate with the host + GDB. This is independent of the normal (SERIAL_8250) driver + for this chipset. + +config KGDB_SIMPLE_SERIAL + bool "Simple selection of KGDB serial port" + depends on KGDB_8250 + default y + help + If you say Y here, you will only have to pick the baud rate + and port number that you wish to use for KGDB. Note that this + only works on architectures that register known serial ports + early on. If you say N, you will have to provide, either here + or on the command line, the type (I/O or MMIO), IRQ and + address to use. If in doubt, say Y. + +config KGDB_BAUDRATE + int "Debug serial port baud rate" + depends on (KGDB_8250 && KGDB_SIMPLE_SERIAL) + default "115200" + help + gdb and the kernel stub need to agree on the baud rate to be + used. Standard rates from 9600 to 115200 are allowed, and this + may be overridden via the commandline. + +config KGDB_PORT_NUM + int "Serial port number for KGDB" + range 0 1 if KGDB_MPSC + range 0 3 + depends on (KGDB_8250 && KGDB_SIMPLE_SERIAL) || KGDB_MPSC + default "1" + help + Pick the port number (0 based) for KGDB to use. + +config KGDB_8250_CONF_STRING + string "Configuration string for KGDB" + depends on KGDB_8250 && !KGDB_SIMPLE_SERIAL + default "io,2f8,115200,3" if X86 + help + The format of this string should be ,
,,. For example, to use the + serial port on an i386 box located at 0x2f8 and 115200 baud + on IRQ 3 at use: + io,2f8,115200,3 Index: linux-2.6.14/drivers/serial/8250_kgdb.c =================================================================== --- /dev/null +++ linux-2.6.14/drivers/serial/8250_kgdb.c @@ -0,0 +1,504 @@ +/* + * 8250 interface for kgdb. + * + * This is a merging of many different drivers, and all of the people have + * had an impact in some form or another: + * + * 2004-2005 (c) MontaVista Software, Inc. + * 2005 (c) Wind River Systems, Inc. + * + * Amit Kale , David Grothe , + * Scott Foehner , George Anzinger , + * Robert Walsh , wangdi , + * San Mehat, Tom Rini , + * Jason Wessel + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include /* For BASE_BAUD and SERIAL_PORT_DFNS */ + +#include "8250.h" + +#define GDB_BUF_SIZE 512 /* power of 2, please */ + +MODULE_DESCRIPTION("KGDB driver for the 8250"); +MODULE_LICENSE("GPL"); +/* These will conflict with early_param otherwise. */ +#ifdef CONFIG_KGDB_8250_MODULE +static char config[256]; +module_param_string(kgdb8250, config, 256, 0); +MODULE_PARM_DESC(kgdb8250, + " kgdb8250=,
,,\n"); +static struct kgdb_io local_kgdb_io_ops; +#endif /* CONFIG_KGDB_8250_MODULE */ + +/* Speed of the UART. */ +static int kgdb8250_baud; + +/* Flag for if we need to call request_mem_region */ +static int kgdb8250_needs_request_mem_region; + +static char kgdb8250_buf[GDB_BUF_SIZE]; +static atomic_t kgdb8250_buf_in_cnt; +static int kgdb8250_buf_out_inx; + +/* Old-style serial definitions, if existant, and a counter. */ +#ifdef CONFIG_KGDB_SIMPLE_SERIAL +static int __initdata should_copy_rs_table = 1; +static struct serial_state old_rs_table[] __initdata = { +#ifdef SERIAL_PORT_DFNS + SERIAL_PORT_DFNS +#endif +}; +#endif + +/* Our internal table of UARTS. */ +#define UART_NR CONFIG_SERIAL_8250_NR_UARTS +static struct uart_port kgdb8250_ports[UART_NR]; + +static struct uart_port *current_port; + +/* Base of the UART. */ +static void *kgdb8250_addr; + +/* Forward declarations. */ +static int kgdb8250_uart_init(void); +static int __init kgdb_init_io(void); +static int __init kgdb8250_opt(char *str); + +/* These are much shorter calls to ioread8/iowrite8 that take into + * account our shifts, etc. */ +static inline unsigned int kgdb_ioread(u8 mask) +{ + return ioread8(kgdb8250_addr + (mask << current_port->regshift)); +} + +static inline void kgdb_iowrite(u8 val, u8 mask) +{ + iowrite8(val, kgdb8250_addr + (mask << current_port->regshift)); +} + +/* + * Wait until the interface can accept a char, then write it. + */ +static void kgdb_put_debug_char(u8 chr) +{ + while (!(kgdb_ioread(UART_LSR) & UART_LSR_THRE)) ; + + kgdb_iowrite(chr, UART_TX); +} + +/* + * Get a byte from the hardware data buffer and return it + */ +static int read_data_bfr(void) +{ + char it = kgdb_ioread(UART_LSR); + + if (it & UART_LSR_DR) + return kgdb_ioread(UART_RX); + + /* + * If we have a framing error assume somebody messed with + * our uart. Reprogram it and send '-' both ways... + */ + if (it & 0xc) { + kgdb8250_uart_init(); + kgdb_put_debug_char('-'); + return '-'; + } + + return -1; +} + +/* + * Get a char if available, return -1 if nothing available. + * Empty the receive buffer first, then look at the interface hardware. + */ +static int kgdb_get_debug_char(void) +{ + int retchr; + + /* intr routine has q'd chars */ + if (atomic_read(&kgdb8250_buf_in_cnt) != 0) { + retchr = kgdb8250_buf[kgdb8250_buf_out_inx++]; + kgdb8250_buf_out_inx &= (GDB_BUF_SIZE - 1); + atomic_dec(&kgdb8250_buf_in_cnt); + return retchr; + } + + do { + retchr = read_data_bfr(); + } while (retchr < 0); + + return retchr; +} + +/* + * This is the receiver interrupt routine for the GDB stub. + * All that we need to do is verify that the interrupt happened on the + * line we're in charge of. If this is true, schedule a breakpoint and + * return. + */ +static irqreturn_t +kgdb8250_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + if (kgdb_ioread(UART_IIR) & UART_IIR_RDI) { + /* Throw away the data if another I/O routine is active. */ + if (kgdb_io_ops.read_char != kgdb_get_debug_char && + (kgdb_ioread(UART_LSR) & UART_LSR_DR)) + kgdb_ioread(UART_RX); + else + breakpoint(); + } + + return IRQ_HANDLED; +} + +/* + * Initializes the UART. + * Returns: + * 0 on success, 1 on failure. + */ +static int +kgdb8250_uart_init (void) +{ + unsigned int ier, base_baud = current_port->uartclk ? + current_port->uartclk / 16 : BASE_BAUD; + + /* test uart existance */ + if(kgdb_ioread(UART_LSR) == 0xff) + return -1; + + /* disable interrupts */ + kgdb_iowrite(0, UART_IER); + +#if defined(CONFIG_ARCH_OMAP1510) + /* Workaround to enable 115200 baud on OMAP1510 internal ports */ + if (cpu_is_omap1510() && is_omap_port((void *)kgdb8250_addr)) { + if (kgdb8250_baud == 115200) { + base_baud = 1; + kgdb8250_baud = 1; + kgdb_iowrite(1, UART_OMAP_OSC_12M_SEL); + } else + kgdb_iowrite(0, UART_OMAP_OSC_12M_SEL); + } +#endif + /* set DLAB */ + kgdb_iowrite(UART_LCR_DLAB, UART_LCR); + + /* set baud */ + kgdb_iowrite((base_baud / kgdb8250_baud) & 0xff, UART_DLL); + kgdb_iowrite((base_baud / kgdb8250_baud) >> 8, UART_DLM); + + /* reset DLAB, set LCR */ + kgdb_iowrite(UART_LCR_WLEN8, UART_LCR); + + /* set DTR and RTS */ + kgdb_iowrite(UART_MCR_OUT2 | UART_MCR_DTR | UART_MCR_RTS, UART_MCR); + + /* setup fifo */ + kgdb_iowrite(UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR + | UART_FCR_CLEAR_XMIT | UART_FCR_TRIGGER_8, + UART_FCR); + + /* clear pending interrupts */ + kgdb_ioread(UART_IIR); + kgdb_ioread(UART_RX); + kgdb_ioread(UART_LSR); + kgdb_ioread(UART_MSR); + + /* turn on RX interrupt only */ + kgdb_iowrite(UART_IER_RDI, UART_IER); + + /* + * Borrowed from the main 8250 driver. + * Try writing and reading the UART_IER_UUE bit (b6). + * If it works, this is probably one of the Xscale platform's + * internal UARTs. + * We're going to explicitly set the UUE bit to 0 before + * trying to write and read a 1 just to make sure it's not + * already a 1 and maybe locked there before we even start start. + */ + ier = kgdb_ioread(UART_IER); + kgdb_iowrite(ier & ~UART_IER_UUE, UART_IER); + if (!(kgdb_ioread(UART_IER) & UART_IER_UUE)) { + /* + * OK it's in a known zero state, try writing and reading + * without disturbing the current state of the other bits. + */ + kgdb_iowrite(ier | UART_IER_UUE, UART_IER); + if (kgdb_ioread(UART_IER) & UART_IER_UUE) + /* + * It's an Xscale. + */ + ier |= UART_IER_UUE | UART_IER_RTOIE; + } + kgdb_iowrite(ier, UART_IER); + return 0; +} + +/* + * Copy the old serial_state table to our uart_port table if we haven't + * had values specifically configured in. We need to make sure this only + * happens once. + */ +static void __init kgdb8250_copy_rs_table(void) +{ +#ifdef CONFIG_KGDB_SIMPLE_SERIAL + int i; + + if (!should_copy_rs_table) + return; + + for (i = 0; i < ARRAY_SIZE(old_rs_table); i++) { + kgdb8250_ports[i].iobase = old_rs_table[i].port; + kgdb8250_ports[i].irq = irq_canonicalize(old_rs_table[i].irq); + kgdb8250_ports[i].uartclk = old_rs_table[i].baud_base * 16; + kgdb8250_ports[i].membase = old_rs_table[i].iomem_base; + kgdb8250_ports[i].iotype = old_rs_table[i].io_type; + kgdb8250_ports[i].regshift = old_rs_table[i].iomem_reg_shift; + kgdb8250_ports[i].line = i; + } + + should_copy_rs_table = 0; +#endif +} + +/* + * Hookup our IRQ line now that it is safe to do so, after we grab any + * memory regions we might need to. If we haven't been initialized yet, + * go ahead and copy the old_rs_table in. + */ +static void __init kgdb8250_late_init(void) +{ + /* Try and copy the old_rs_table. */ + kgdb8250_copy_rs_table(); + +#if defined(CONFIG_SERIAL_8250) || defined (CONFIG_SERIAL_8250_MODULE) + /* Take the port away from the main driver. */ + serial8250_unregister_by_port(current_port); + + /* Now reinit the port as the above has disabled things. */ + kgdb8250_uart_init(); +#endif + /* We may need to call request_mem_region() first. */ + if (kgdb8250_needs_request_mem_region) + request_mem_region(current_port->mapbase, + 8 << current_port->regshift, "kgdb"); + if (request_irq(current_port->irq, kgdb8250_interrupt, SA_SHIRQ, + "GDB-stub", current_port) < 0) + printk(KERN_ERR "KGDB failed to request the serial IRQ (%d)\n", + current_port->irq); +} + +static __init int kgdb_init_io(void) +{ + /* Give us the basic table of uarts. */ + kgdb8250_copy_rs_table(); + +#ifdef CONFIG_KGDB_8250_MODULE + if (strlen(config)) { + if (kgdb8250_opt(config)) + return -EINVAL; + } else { + printk(KERN_ERR "kgdb8250: argument error, usage: " + "kgdb8250=,
," ",\n"); + return -EINVAL; + } +#endif /* CONFIG_KGDB_8250_MODULE */ +#ifdef CONFIG_KGDB_SIMPLE_SERIAL + kgdb8250_baud = CONFIG_KGDB_BAUDRATE; + + /* Setup our pointer to the serial port now. */ + current_port = &kgdb8250_ports[CONFIG_KGDB_PORT_NUM]; +#else + if (kgdb8250_opt(CONFIG_KGDB_8250_CONF_STRING)) + return -EINVAL; +#endif + + + /* Internal driver setup. */ + switch (current_port->iotype) { + case UPIO_MEM: + if (current_port->mapbase) + kgdb8250_needs_request_mem_region = 1; + if (current_port->flags & UPF_IOREMAP) { + current_port->membase = ioremap(current_port->mapbase, + 8 << current_port->regshift); + if (!current_port->membase) + return -EIO; /* Failed. */ + } + kgdb8250_addr = current_port->membase; + break; + case UPIO_PORT: + default: + kgdb8250_addr = ioport_map(current_port->iobase, + 8 << current_port->regshift); + if (!kgdb8250_addr) + return -EIO; /* Failed. */ + } + + if (kgdb8250_uart_init() == -1) { + printk(KERN_ERR "kgdb8250: init failed\n"); + return -EIO; + } +#ifdef CONFIG_KGDB_8250_MODULE + /* Attach the kgdb irq. When this is built into the kernel, it + * is called as a part of late_init sequence. + */ + kgdb8250_late_init(); + if (kgdb_register_io_module(&local_kgdb_io_ops)) + return -EINVAL; + + printk(KERN_INFO "kgdb8250: debugging enabled\n"); +#endif /* CONFIG_KGD_8250_MODULE */ + + return 0; +} + +#ifdef CONFIG_KGDB_8250_MODULE +/* If it is a module the kgdb_io_ops should be a static which + * is passed to the KGDB I/O initialization + */ +static struct kgdb_io local_kgdb_io_ops = { +#else /* ! CONFIG_KGDB_8250_MODULE */ +struct kgdb_io kgdb_io_ops = { +#endif /* ! CONFIG_KGD_8250_MODULE */ + .read_char = kgdb_get_debug_char, + .write_char = kgdb_put_debug_char, + .init = kgdb_init_io, + .late_init = kgdb8250_late_init, +}; + +/** + * kgdb8250_add_port - Define a serial port for use with KGDB + * @i: The index of the port being added + * @serial_req: The &struct uart_port describing the port + * + * On platforms where we must register the serial device + * dynamically, this is the best option if a platform also normally + * calls early_serial_setup(). + */ +void __init kgdb8250_add_port(int i, struct uart_port *serial_req) +{ + /* Make sure we've got the built-in data before we override. */ + kgdb8250_copy_rs_table(); + + /* Copy the whole thing over. */ + memcpy(&kgdb8250_ports[i], serial_req, sizeof(struct uart_port)); +} + +/** + * kgdb8250_add_platform_port - Define a serial port for use with KGDB + * @i: The index of the port being added + * @p: The &struct plat_serial8250_port describing the port + * + * On platforms where we must register the serial device + * dynamically, this is the best option if a platform normally + * handles uart setup with an array of &struct plat_serial8250_port. + */ +void __init kgdb8250_add_platform_port(int i, struct plat_serial8250_port *p) +{ + /* Make sure we've got the built-in data before we override. */ + kgdb8250_copy_rs_table(); + + kgdb8250_ports[i].iobase = p->iobase; + kgdb8250_ports[i].membase = p->membase; + kgdb8250_ports[i].irq = p->irq; + kgdb8250_ports[i].uartclk = p->uartclk; + kgdb8250_ports[i].regshift = p->regshift; + kgdb8250_ports[i].iotype = p->iotype; + kgdb8250_ports[i].flags = p->flags; + kgdb8250_ports[i].mapbase = p->mapbase; +} + +/* + * Syntax for this cmdline option is: + * kgdb8250=,
,," + */ +static int __init kgdb8250_opt(char *str) +{ + /* We'll fill out and use the first slot. */ + current_port = &kgdb8250_ports[0]; + + if (!strncmp(str, "io", 2)) { + current_port->iotype = UPIO_PORT; + str += 2; + } else if (!strncmp(str, "mmio", 4)) { + current_port->iotype = UPIO_MEM; + str += 4; + } else + goto errout; + + if (*str != ',') + goto errout; + str++; + + if (current_port->iotype == UPIO_PORT) + current_port->iobase = simple_strtoul(str, &str, 16); + else + current_port->membase = + (unsigned char *)simple_strtoul(str, &str, 16); + + if (*str != ',') + goto errout; + str++; + + kgdb8250_baud = simple_strtoul(str, &str, 10); + if (!kgdb8250_baud) + goto errout; + + if (*str != ',') + goto errout; + str++; + + current_port->irq = simple_strtoul(str, &str, 10); + +#ifdef CONFIG_KGDB_SIMPLE_SERIAL + should_copy_rs_table = 0; +#endif + + return 0; + + errout: + printk(KERN_ERR "Invalid syntax for option kgdb8250=\n"); + return 1; +} + +#ifdef CONFIG_KGDB_8250_MODULE +static void cleanup_kgdb8250(void) +{ + kgdb_unregister_io_module(&local_kgdb_io_ops); + + /* Clean up the irq and memory */ + free_irq(current_port->irq, current_port); + + if (kgdb8250_needs_request_mem_region) + release_mem_region(current_port->mapbase, + 8 << current_port->regshift); + /* Hook up the serial port back to what it was previously + * hooked up to. + */ +#if defined(CONFIG_SERIAL_8250) || defined(CONFIG_SERIAL_8250_MODULE) + /* Give the port back to the 8250 driver. */ + serial8250_register_port(current_port); +#endif +} + +module_init(kgdb_init_io); +module_exit(cleanup_kgdb8250); +#else /* ! CONFIG_KGDB_8250_MODULE */ +early_param("kgdb8250", kgdb8250_opt); +#endif /* ! CONFIG_KGDB_8250_MODULE */ Index: linux-2.6.14/include/linux/serial_8250.h =================================================================== --- linux-2.6.14.orig/include/linux/serial_8250.h +++ linux-2.6.14/include/linux/serial_8250.h @@ -54,6 +54,7 @@ struct uart_port; int serial8250_register_port(struct uart_port *); void serial8250_unregister_port(int line); +void serial8250_unregister_by_port(struct uart_port *port); void serial8250_suspend_port(int line); void serial8250_resume_port(int line); Index: linux-2.6.14/drivers/serial/serial_core.c =================================================================== --- linux-2.6.14.orig/drivers/serial/serial_core.c +++ linux-2.6.14/drivers/serial/serial_core.c @@ -33,6 +33,7 @@ #include #include /* for serial_state and serial_icounter_struct */ #include +#include #include #include @@ -2237,6 +2238,12 @@ int uart_add_one_port(struct uart_driver port->cons && !(port->cons->flags & CON_ENABLED)) register_console(port->cons); +#if defined(CONFIG_KGDB_8250) + /* Add any 8250-like ports we find later. */ + if (port->type <= PORT_MAX_8250) + kgdb8250_add_port(port->line, port); +#endif + out: up(&port_sem); -- Tom - To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/