--- zzzz-none-000/linux-2.6.39.4/drivers/tty/serial/8250.c 2011-08-03 19:43:28.000000000 +0000 +++ puma6-arm-6490-729/linux-2.6.39.4/drivers/tty/serial/8250.c 2021-11-10 13:23:10.000000000 +0000 @@ -17,6 +17,31 @@ * mapbase is the physical address of the IO port. * membase is an 'ioremapped' cookie. */ +/****************************************************************** + + Includes Intel Corporation's changes/modifications dated: 05/2012. + Changed/modified portions - Copyright(c) 2010 - 2012, Intel Corporation. + +******************************************************************/ +/* Copyright 2008, Texas Instruments Incorporated + * + * This program has been modified from its original operation by Texas Instruments + * to do the following: + * 1) Set uartclk in serial8250_resume_port to support arm frequency change in + * Puma5. + * + * THIS MODIFIED SOFTWARE AND DOCUMENTATION ARE PROVIDED + * "AS IS," AND TEXAS INSTRUMENTS MAKES NO REPRESENTATIONS + * OR WARRENTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO, WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY + * PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE OR + * DOCUMENTATION WILL NOT INFRINGE ANY THIRD PARTY PATENTS, + * COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. + * See The GNU General Public License for more details. + * + * These changes are covered under version 2 of the GNU General Public License, + * dated June 1991. + */ #if defined(CONFIG_SERIAL_8250_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) #define SUPPORT_SYSRQ @@ -40,6 +65,9 @@ #include #include #include +#ifdef CONFIG_GEN3_UART +#include +#endif #include #include @@ -49,7 +77,13 @@ #ifdef CONFIG_SPARC #include "suncore.h" #endif +#if defined(CONFIG_MACH_PUMA5) || defined(CONFIG_MACH_PUMA6) +#include +#endif/*--- #if defined(CONFIG_MACH_PUMA5) || defined(CONFIG_MACH_PUMA6) ---*/ +#if defined(CONFIG_MACH_PUMA6) +#include +#endif /* * Configuration: * share_irqs - whether we pass IRQF_SHARED to request_irq(). This option @@ -61,10 +95,22 @@ static struct uart_driver serial8250_reg; +/* + * Intel CE2600 have only two UARTS, while previous platform have three UARTS +*/ +#ifdef CONFIG_GEN3_UART +#define CE2600_SERIAL_8250_NR_UARTS 2 +#endif + static int serial_index(struct uart_port *port) { return (serial8250_reg.minor - 64) + port->line; } +#if defined(CONFIG_SERIAL_8250_CONSOLE_MUTE) +static unsigned int avm_serial_mute_flag = 1; +#else /*--- #if defined(CONFIG_SERIAL_8250_CONSOLE_MUTE) ---*/ +static unsigned int avm_serial_mute_flag = 0; +#endif /*--- #else ---*/ /*--- #if defined(CONFIG_SERIAL_8250_CONSOLE_MUTE) ---*/ static unsigned int skip_txen_test; /* force skip of txen test at init time */ @@ -155,6 +201,10 @@ unsigned char lsr_saved_flags; #define MSR_SAVE_FLAGS UART_MSR_ANY_DELTA unsigned char msr_saved_flags; + + volatile unsigned int tx_busy; + + unsigned int is_console; }; struct irq_info { @@ -199,6 +249,36 @@ .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, .flags = UART_CAP_FIFO, }, +#if 0 + [PORT_PUMA5] = { + .name = "PUMA5_UART", + .fifo_size = 16, + .tx_loadsz = 16, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, + .flags = UART_CAP_FIFO, + }, + [PORT_OHIO] = { + .name = "OHIO_UART", + .fifo_size = 16, + .tx_loadsz = 16, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_00, + .flags = UART_CAP_FIFO, + }, + [PORT_UR8] = { + .name = "UR8_UART", + .fifo_size = 16, + .tx_loadsz = 16, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_00, + .flags = UART_CAP_FIFO, + }, + [PORT_DAVINCI] = { + .name = "DAVINCI_UART", + .fifo_size = 16, + .tx_loadsz = 16, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_00 | UART_FCR_DMA_SELECT, + .flags = UART_CAP_FIFO, + }, +#endif [PORT_CIRRUS] = { .name = "Cirrus", .fifo_size = 1, @@ -269,10 +349,23 @@ .flags = UART_CAP_FIFO | UART_NATSEMI, }, [PORT_XSCALE] = { +/* + * The following code is for Intel Media SOC Gen3 base support. +*/ +#ifdef CONFIG_GEN3_UART +/* + * Since we have legal issue to use "Xscale", so change it to "GEN3_serial". +*/ + .name = "GEN3_serial", + .fifo_size = 64, + .tx_loadsz = 64, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_DMA_SELECT | UART_FCR_R_TRIG_10, +#else .name = "XScale", .fifo_size = 32, .tx_loadsz = 32, .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, +#endif .flags = UART_CAP_FIFO | UART_CAP_UUE, }, [PORT_RM9000] = { @@ -303,6 +396,13 @@ .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, .flags = UART_CAP_FIFO | UART_CAP_AFE, }, + [PORT_TI_16550A] = { + .name = "TI 16550A", + .fifo_size = 16, + .tx_loadsz = 16, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, + .flags = UART_CAP_FIFO | UART_CAP_UUE, + }, }; #if defined(CONFIG_MIPS_ALCHEMY) @@ -1087,6 +1187,15 @@ * already a 1 and maybe locked there before we even start start. */ iersave = serial_in(up, UART_IER); + + /* TI default UART_IER upper nibble to 0 */ + if((iersave & 0xF0) == 0) { + DEBUG_AUTOCONF("TI 16550A"); + up->port.type = PORT_TI_16550A; + up->capabilities |= UART_CAP_UUE; + return; + } + serial_outp(up, UART_IER, iersave & ~UART_IER_UUE); if (!(serial_in(up, UART_IER) & UART_IER_UUE)) { /* @@ -1135,6 +1244,9 @@ unsigned char status1, scratch, scratch2, scratch3; unsigned char save_lcr, save_mcr; unsigned long flags; +#ifdef CONFIG_GEN3_UART + unsigned int id; +#endif if (!up->port.iobase && !up->port.mapbase && !up->port.membase) return; @@ -1149,7 +1261,14 @@ spin_lock_irqsave(&up->port.lock, flags); up->capabilities = 0; - up->bugs = 0; +#ifdef CONFIG_GEN3_UART + /* do not enable modem status interrupt for IntelCE uart0 port */ + intelce_get_soc_info(&id, NULL); + if((CE4200_SOC_DEVICE_ID == id) && (0 == serial_index(&up->port))) + up->bugs = UART_BUG_NOMSR; + else +#endif + up->bugs = 0; if (!(up->port.flags & UPF_BUGGY_UART)) { /* @@ -1380,17 +1499,24 @@ struct uart_8250_port *up = container_of(port, struct uart_8250_port, port); +#if defined(CONFIG_DAVINCI_DRM_KUNDENVERSION) /*--- keine Ausgaben auf der seriellen ---*/ + if (port->irq == IRQ_UARTINT0) + return; +#endif + if (!(up->ier & UART_IER_THRI)) { up->ier |= UART_IER_THRI; serial_out(up, UART_IER, up->ier); if (up->bugs & UART_BUG_TXEN) { - unsigned char lsr; + unsigned char lsr, iir; lsr = serial_in(up, UART_LSR); up->lsr_saved_flags |= lsr & LSR_SAVE_FLAGS; + iir = serial_in(up, UART_IIR) & 0x0f; if ((up->port.type == PORT_RM9000) ? - (lsr & UART_LSR_THRE) : - (lsr & UART_LSR_TEMT)) + (lsr & UART_LSR_THRE && + (iir == UART_IIR_NO_INT || iir == UART_IIR_THRI)) : + (lsr & UART_LSR_TEMT && iir & UART_IIR_NO_INT)) transmit_chars(up); } } @@ -1451,6 +1577,10 @@ flag = TTY_NORMAL; up->port.icount.rx++; + if(up->is_console && avm_serial_mute_flag){ + goto ignore_char; + } + lsr |= up->lsr_saved_flags; up->lsr_saved_flags = 0; @@ -1497,6 +1627,14 @@ ignore_char: lsr = serial_inp(up, UART_LSR); } while ((lsr & (UART_LSR_DR | UART_LSR_BI)) && (max_count-- > 0)); + if (256 - max_count > 15) + { + up->port.histogram_RX_stats[15]++; + } + else + { + up->port.histogram_RX_stats[256 - max_count]++; + } spin_unlock(&up->port.lock); tty_flip_buffer_push(tty); spin_lock(&up->port.lock); @@ -1508,24 +1646,43 @@ struct circ_buf *xmit = &up->port.state->xmit; int count; + // drop all output on serial console if mute flag is set. This will be the default + // on customer firmware + if(up->is_console && likely(avm_serial_mute_flag)){ + if (up->port.x_char) { + up->port.icount.tx++; + up->port.x_char = 0; + + return; + } + + up->port.icount.tx += uart_circ_chars_pending(xmit); + uart_circ_clear(xmit); + } + if (up->port.x_char) { serial_outp(up, UART_TX, up->port.x_char); up->port.icount.tx++; up->port.x_char = 0; + return; } + if (uart_tx_stopped(&up->port)) { serial8250_stop_tx(&up->port); + printk("??? stoped ????\n"); return; } + if (uart_circ_empty(xmit)) { __stop_tx(up); + return; } count = up->tx_loadsz; do { - serial_out(up, UART_TX, xmit->buf[xmit->tail]); + serial_out(up, UART_TX, xmit->buf[xmit->tail]); xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); up->port.icount.tx++; if (uart_circ_empty(xmit)) @@ -1536,6 +1693,7 @@ uart_write_wakeup(&up->port); DEBUG_INTR("THRE..."); + /*--- printk("[THRE] ..."); ---*/ if (uart_circ_empty(xmit)) __stop_tx(up); @@ -1586,7 +1744,118 @@ spin_unlock_irqrestore(&up->port.lock, flags); } +/* + * The following code is for Intel Media SOC Gen3 B0 and B1 workaround. +*/ +#ifdef CONFIG_GEN3_UART +/* + * The UART Tx interrupts are not set under some conditions and therefore serial + * transmission hangs. This is a silicon issue and has not been root caused. The + * workaround for this silicon issue checks UART_LSR_THRE bit and UART_LSR_TEMT + * bit of LSR register in interrupt handler to see whether at least one of these + * two bits is set, if so then process the transmit request. If this workaround + * is not applied, then the serial transmission may hang. This workaround is for + * errata number 9 in Errata - B step. +*/ + +/* + * This is the serial driver's interrupt routine. + * + * Arjan thinks the old way was overly complex, so it got simplified. + * Alan disagrees, saying that need the complexity to handle the weird + * nature of ISA shared interrupts. (This is a special exception.) + * + * In order to handle ISA shared interrupts properly, we need to check + * that all ports have been serviced, and therefore the ISA interrupt + * line has been de-asserted. + * + * This means we need to loop through all ports. checking that they + * don't have an interrupt pending. + */ +static irqreturn_t serial8250_interrupt(int irq, void *dev_id) +{ + struct irq_info *i = dev_id; + struct list_head *l, *end = NULL; + int pass_counter = 0, handled = 0; + unsigned int my_flags, ier, lsr; + + DEBUG_INTR("serial8250_interrupt(%d)...", irq); + + spin_lock(&i->lock); + + l = i->head; + do { + struct uart_8250_port *up; + unsigned int iir; + my_flags = 0x00; + + up = list_entry(l, struct uart_8250_port, list); + + iir = serial_in(up, UART_IIR); + if (!(iir & UART_IIR_NO_INT)) { + serial8250_handle_port(up); + my_flags |= 0x01; + + handled = 1; + + end = NULL; + } else if (up->port.iotype == UPIO_DWAPB && + (iir & UART_IIR_BUSY) == UART_IIR_BUSY) { + /* The DesignWare APB UART has an Busy Detect (0x07) + * interrupt meaning an LCR write attempt occured while the + * UART was busy. The interrupt must be cleared by reading + * the UART status register (USR) and the LCR re-written. */ + unsigned int status __always_unused; + status = *(volatile u32 *)up->port.private_data; + serial_out(up, UART_LCR, up->lcr); + + handled = 1; + + end = NULL; + } + ier = serial_in(up, UART_IER); + /* see if the UART's XMIT interrupt is enabled */ + if(ier & UART_IER_THRI) { + lsr = serial_in(up, UART_LSR); + /* now check to see if the UART should be + generating an interrupt (but isn't) */ + if(lsr & (UART_LSR_THRE | UART_LSR_TEMT)) { + /* handle as though we really got the IRQ */ + spin_lock(&up->port.lock); + transmit_chars(up); /* XMIT only */ + spin_unlock(&up->port.lock); + + my_flags |= 0x02; /* handle the old else */ + + handled = 1; + + end = NULL; + } + } + /* this was the else in the old code */ + if(0x00 == my_flags) { + if (end == NULL) + end = l; + } + + l = l->next; + + if (l == i->head && pass_counter++ > PASS_LIMIT) { + /* If we hit this, we're dead. */ + printk_ratelimited(KERN_ERR "serial8250: too much work for " + "irq%d\n", irq); + break; + } + } while (l != end); + + spin_unlock(&i->lock); + + DEBUG_INTR("end.\n"); + + return IRQ_RETVAL(handled); +} +#else /* * This is the serial driver's interrupt routine. * @@ -1632,7 +1901,7 @@ * interrupt meaning an LCR write attempt occurred while the * UART was busy. The interrupt must be cleared by reading * the UART status register (USR) and the LCR re-written. */ - unsigned int status; + volatile u32 status; status = *(volatile u32 *)up->port.private_data; serial_out(up, UART_LCR, up->lcr); @@ -1659,6 +1928,7 @@ return IRQ_RETVAL(handled); } +#endif /* * To support ISA shared interrupts, we need to have one interrupt * handler that ensures that the IRQ line has been deasserted @@ -1738,7 +2008,7 @@ static void serial_unlink_irq_chain(struct uart_8250_port *up) { - struct irq_info *i; + struct irq_info *i = NULL; struct hlist_node *n; struct hlist_head *h; @@ -1752,6 +2022,7 @@ break; } + BUG_ON(i == NULL); BUG_ON(n == NULL); BUG_ON(i->head == NULL); @@ -2339,10 +2610,23 @@ if (up->capabilities & UART_CAP_FIFO && up->port.fifosize > 1) { if (baud < 2400) + { fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1; + } else + { +#if defined(CONFIG_MACH_PUMA6) + if (up->port.irq == AVALANCHE_UART1_INT) + { + fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_01; + } + else +#endif + { fcr = uart_config[up->port.type].fcr; } + } + } /* * MCR-based auto flow control. When AFE is enabled, RTS will be @@ -2357,7 +2641,10 @@ if (termios->c_cflag & CRTSCTS) up->mcr |= UART_MCR_AFE; } - + if (termios->c_cflag & CRTSCTS) + { + up->mcr |= UART_MCR_AFE; + } /* * Ok, we're now changing the port state. Do it with * interrupts disabled. @@ -2836,8 +3123,10 @@ struct uart_8250_port *up = container_of(port, struct uart_8250_port, port); - wait_for_xmitr(up, UART_LSR_THRE); - serial_out(up, UART_TX, ch); + wait_for_xmitr(up, BOTH_EMPTY); + if(!up->is_console || !avm_serial_mute_flag){ + serial_out(up, UART_TX, ch); + } } /* @@ -2847,7 +3136,7 @@ * The console_lock must be held when we get here. */ static void -serial8250_console_write(struct console *co, const char *s, unsigned int count) +serial8250_console_write1(struct console *co, const char *s, unsigned int count) { struct uart_8250_port *up = &serial8250_ports[co->index]; unsigned long flags; @@ -2870,11 +3159,20 @@ */ ier = serial_in(up, UART_IER); + /* + * The following code is for Intel Media SOC Gen3 base support. + */ +#ifdef CONFIG_GEN3_UART + /* + * Should enable UUE (Uart Unit Enable) bit. + */ + serial_out(up, UART_IER, UART_IER_UUE); +#else if (up->capabilities & UART_CAP_UUE) serial_out(up, UART_IER, UART_IER_UUE); else serial_out(up, UART_IER, 0); - +#endif uart_console_write(&up->port, s, count, serial8250_console_putchar); /* @@ -2899,6 +3197,17 @@ local_irq_restore(flags); } +static void +serial8250_console_write(struct console *co, const char *s, unsigned int count) +{ + int i; + struct uart_8250_port *up __always_unused = &serial8250_ports[co->index]; + for (i = 0; i < count; i++) + { + serial8250_console_write1(co, &s[i], 1); + } +} + static int __init serial8250_console_setup(struct console *co, char *options) { struct uart_port *port; @@ -2921,6 +3230,8 @@ if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); + serial8250_ports[co->index].is_console = 1; + return uart_set_options(port, co, baud, parity, bits, flow); } @@ -2971,7 +3282,7 @@ static struct uart_driver serial8250_reg = { .owner = THIS_MODULE, - .driver_name = "serial", + .driver_name = "serial", .dev_name = "ttyS", .major = TTY_MAJOR, .minor = 64, @@ -3036,7 +3347,12 @@ void serial8250_resume_port(int line) { struct uart_8250_port *up = &serial8250_ports[line]; - +#if defined (CONFIG_MACH_PUMA5) + extern unsigned int avalanche_get_vbus_freq(void); + serial8250_ports[line].port.uartclk = avalanche_get_vbus_freq (); +#elif defined(CONFIG_MACH_PUMA6) + serial8250_ports[line].port.uartclk = PAL_sysClkcGetFreq(PAL_SYS_CLKC_UART0); +#endif if (up->capabilities & UART_NATSEMI) { /* Ensure it's still in high speed mode */ serial_outp(up, UART_LCR, 0xE0); @@ -3233,6 +3549,7 @@ uart->port.flags = port->flags | UPF_BOOT_AUTOCONF; uart->port.mapbase = port->mapbase; uart->port.private_data = port->private_data; + uart->is_console = 0; if (port->dev) uart->port.dev = port->dev; @@ -3293,10 +3610,15 @@ static int __init serial8250_init(void) { int ret; + unsigned int id; if (nr_uarts > UART_NR) nr_uarts = UART_NR; - +#ifdef CONFIG_GEN3_UART + intelce_get_soc_info(&id, NULL); + if(CE2600_SOC_DEVICE_ID == id) + nr_uarts = CE2600_SERIAL_8250_NR_UARTS < nr_uarts ? CE2600_SERIAL_8250_NR_UARTS : nr_uarts; +#endif printk(KERN_INFO "Serial: 8250/16550 driver, " "%d ports, IRQ sharing %sabled\n", nr_uarts, share_irqs ? "en" : "dis"); @@ -3361,6 +3683,17 @@ #endif } +/*------------------------------------------------------------------------------------------*\ +\*------------------------------------------------------------------------------------------*/ +static int setup_avm_mute_flag(char *p) { + if(p) { + avm_serial_mute_flag = simple_strtol(p, NULL, 0); + } + return 0; +} + +__setup("mute=", setup_avm_mute_flag); + module_init(serial8250_init); module_exit(serial8250_exit);