--- zzzz-none-000/linux-2.4.17/drivers/char/serial.c 2001-12-21 17:41:54.000000000 +0000 +++ sangam-fb-322/linux-2.4.17/drivers/char/serial.c 2004-11-24 13:23:20.000000000 +0000 @@ -57,6 +57,11 @@ * 10/00: add in optional software flow control for serial console. * Kanoj Sarcar (Modified by Theodore Ts'o) * + * 10/00: Added suport for MIPS Atlas board. + * 11/00: Hooks for serial kernel debug port support added. + * Kevin D. Kissell, kevink@mips.com and Carsten Langgaard, + * carstenl@mips.com + * Copyright (C) 2000 MIPS Technologies, Inc. All rights reserved. */ static char *serial_version = "5.05c"; @@ -128,6 +133,16 @@ #endif #endif +#ifdef CONFIG_ARCH_PXA +#define pxa_port(x) ((x) == PORT_PXA) +#define pxa_buggy_port(x) ({ \ + int cpu_ver; asm("mrc%? p15, 0, %0, c0, c0" : "=r" (cpu_ver)); \ + ((x) == PORT_PXA && (cpu_ver & ~1) == 0x69052100); }) +#else +#define pxa_port(x) (0) +#define pxa_buggy_port(x) (0) +#endif + /* Set of debugging defines */ #undef SERIAL_DEBUG_INTR @@ -231,9 +246,7 @@ #include #include -#ifdef CONFIG_MAC_SERIAL -#define SERIAL_DEV_OFFSET 2 -#else +#ifndef SERIAL_DEV_OFFSET #define SERIAL_DEV_OFFSET 0 #endif @@ -243,6 +256,10 @@ #define _INLINE_ #endif +#if defined(CONFIG_AVALANCHE_SERIAL) +extern unsigned long cpu_freq; +#endif /* CONFIG_AVALANCHE_SERIAL */ + static char *serial_name = "Serial driver"; static DECLARE_TASK_QUEUE(tq_serial); @@ -252,6 +269,12 @@ static struct timer_list serial_timer; +#ifdef SERIAL_IRQ0_VALID +#define IRQ_VALID(irq) ((irq >= 0) && (irq < NR_IRQS)) +#else +#define IRQ_VALID(irq) ((irq > 0) && (irq < NR_IRQS)) +#endif + /* serial subtype definitions */ #ifndef SERIAL_TYPE_NORMAL #define SERIAL_TYPE_NORMAL 1 @@ -306,6 +329,7 @@ { "XR16850", 128, UART_CLEAR_FIFO | UART_USE_FIFO | UART_STARTECH }, { "RSA", 2048, UART_CLEAR_FIFO | UART_USE_FIFO }, + { "PXA UART", 64, UART_CLEAR_FIFO | UART_USE_FIFO }, { 0, 0} }; @@ -407,6 +431,71 @@ return 0; } + +/*---------------------------*/ +/* SEAD board serial */ +/*---------------------------*/ +#if defined(CONFIG_MIPS_SEAD2) +#define SIO_REG_OFFSET 8 /* 32 bit aligned boundary (evm III)*/ +#endif +/* + * Texas Instruments Avalanche ASIC + * + */ +#if defined(CONFIG_AVALANCHE_SERIAL) +#define SIO_REG_OFFSET 4 /* 32 bit aligned boundary (evm III)*/ +#endif + +#if defined(CONFIG_AVALANCHE_SERIAL) || defined(CONFIG_MIPS_SEAD2) +static _INLINE_ unsigned int serial_in(struct async_struct *info, int offset) +{ + return (inb(info->port + (offset * SIO_REG_OFFSET)) & 0xff); +} + + +static inline unsigned int serial_inp(struct async_struct *info, int offset) +{ +#ifdef CONFIG_SERIAL_NOPAUSE_IO + return (inb(info->port + (offset * SIO_REG_OFFSET)) & 0xff); +#else /* CONFIG_SERIAL_NOPAUSE_IO */ + return (inb_p(info->port + (offset * SIO_REG_OFFSET)) & 0xff); +#endif /* CONFIG_SERIAL_NOPAUSE_IO */ +} + +static inline void serial_out(struct async_struct *info, int offset, int value) +{ + outb(value, info->port + (offset * SIO_REG_OFFSET)); +} + + +static inline void serial_outp(struct async_struct *info, int offset, + int value) +{ +#ifdef CONFIG_SERIAL_NOPAUSE_IO + outb(value, info->port + (offset * SIO_REG_OFFSET)); +#else /* CONFIG_SERIAL_NOPAUSE_IO */ + outb_p(value, info->port + (offset * SIO_REG_OFFSET)); +#endif /* CONFIG_SERIAL_NOPAUSE_IO */ +} + +#else /* !CONFIG_AVALANCHE_SERIAL || CONFIG_MIPS_SEAD2 */ + +#ifdef CONFIG_MIPS_ATLAS +extern unsigned int atlas_serial_in(struct async_struct *info, int offset); +extern void atlas_serial_out(struct async_struct *info, int offset, int value); + +static _INLINE_ unsigned int serial_in(struct async_struct *info, int offset) +{ + return (atlas_serial_in(info, offset) & 0xff); +} + +static _INLINE_ void serial_out(struct async_struct *info, int offset, int value) +{ + atlas_serial_out(info, offset, value); +} + +#else + static _INLINE_ unsigned int serial_in(struct async_struct *info, int offset) { switch (info->io_type) { @@ -416,7 +505,11 @@ return inb(info->port+1); #endif case SERIAL_IO_MEM: - return readb((unsigned long) info->iomem_base + + if (pxa_port(info->state->type)) + return readl((unsigned long) info->iomem_base + + (offset<iomem_reg_shift)); + else + return readb((unsigned long) info->iomem_base + (offset<iomem_reg_shift)); #ifdef CONFIG_SERIAL_GSC case SERIAL_IO_GSC: @@ -438,8 +531,12 @@ break; #endif case SERIAL_IO_MEM: - writeb(value, (unsigned long) info->iomem_base + - (offset<iomem_reg_shift)); + if (pxa_port(info->state->type)) + writel(value, (unsigned long) info->iomem_base + + (offset<iomem_reg_shift)); + else + writeb(value, (unsigned long) info->iomem_base + + (offset<iomem_reg_shift)); break; #ifdef CONFIG_SERIAL_GSC case SERIAL_IO_GSC: @@ -450,6 +547,8 @@ outb(value, info->port+offset); } } +#endif +#endif /* CONFIG_AVALANCHE_SERIAL || CONFIG_MIPS_SEAD2 */ /* * We used to support using pause I/O for certain machines. We @@ -457,9 +556,12 @@ * needed for certain old 386 machines, I've left these #define's * in.... */ +#if defined(CONFIG_MIPS_SEAD2) || defined(CONFIG_AVALANCHE_SERIAL) + /* nothing */ +#else #define serial_inp(info, offset) serial_in(info, offset) #define serial_outp(info, offset, value) serial_out(info, offset, value) - +#endif /* CONFIG_MIPS_SEAD2 || CONFIG_AVALANCHE_SERIAL*/ /* * For the 16C950 @@ -556,6 +658,10 @@ * This routine is used by the interrupt handler to schedule * processing in the software interrupt portion of the driver. */ +#if 0 +int serial_irq_count = 0; +#endif + static _INLINE_ void rs_sched_event(struct async_struct *info, int event) { @@ -701,7 +807,11 @@ return; } - count = info->xmit_fifo_size; + if (pxa_port(info->state->type)) + count = info->xmit_fifo_size / 2; + else + count = info->xmit_fifo_size; + do { serial_out(info, UART_TX, info->xmit.buf[info->xmit.tail]); info->xmit.tail = (info->xmit.tail + 1) & (SERIAL_XMIT_SIZE-1); @@ -1332,7 +1442,7 @@ /* * Allocate the IRQ if necessary */ - if (state->irq && (!IRQ_ports[state->irq] || + if (IRQ_VALID(state->irq) && (!IRQ_ports[state->irq] || !IRQ_ports[state->irq]->next_port)) { if (IRQ_ports[state->irq]) { #ifdef CONFIG_SERIAL_SHARE_IRQ @@ -1390,6 +1500,8 @@ { if (state->irq != 0) info->MCR |= UART_MCR_OUT2; + if (pxa_buggy_port(state->type) && state->irq != 0) + info->MCR &= ~UART_MCR_OUT2; } info->MCR |= ALPHA_KLUDGE_MCR; /* Don't ask */ serial_outp(info, UART_MCR, info->MCR); @@ -1398,6 +1510,8 @@ * Finally, enable interrupts */ info->IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI; + if (pxa_port(state->type)) + info->IER |= UART_IER_UUE | UART_IER_RTOIE; serial_outp(info, UART_IER, info->IER); /* enable interrupts */ #ifdef CONFIG_SERIAL_MANY_PORTS @@ -1447,6 +1561,15 @@ */ change_speed(info, 0); + /* Avalanche D has no flow control on the serial port */ + +#if defined(CONFIG_MIPS_SEAD2) || defined(CONFIG_AVALANCHE_SERIAL) + +#if defined(CONFIG_AVALANCHE_SERIAL_AUTOFLOW) + serial_outp(info, UART_MCR, TI_AUTOFLOW_ENABLE); +#endif +#endif /* CONFIG_MIPS_AVALANCHE || CONFIG_MIPS_SEAD2*/ + info->flags |= ASYNC_INITIALIZED; restore_flags(flags); return 0; @@ -1498,7 +1621,7 @@ /* * Free the IRQ, if necessary */ - if (state->irq && (!IRQ_ports[state->irq] || + if (IRQ_VALID(state->irq) && (!IRQ_ports[state->irq] || !IRQ_ports[state->irq]->next_port)) { if (IRQ_ports[state->irq]) { free_irq(state->irq, &IRQ_ports[state->irq]); @@ -1529,6 +1652,8 @@ } else #endif info->MCR &= ~UART_MCR_OUT2; + if (pxa_buggy_port(state->type)) + info->MCR |= UART_MCR_OUT2; info->MCR |= ALPHA_KLUDGE_MCR; /* Don't ask */ /* disable break condition */ @@ -1577,6 +1702,15 @@ info->flags &= ~ASYNC_INITIALIZED; restore_flags(flags); } +#ifdef CONFIG_KGDB +void shutdown_for_kgdb(struct async_struct * info) +{ + int irq = info->state->irq; + while(IRQ_ports[irq]){ + shutdown(IRQ_ports[irq]) ; + } +} +#endif #if (LINUX_VERSION_CODE < 131394) /* Linux 2.1.66 */ static int baud_table[] = { @@ -1617,6 +1751,9 @@ struct termios *old_termios) { int quot = 0, baud_base, baud; +#if defined(CONFIG_MIPS_SEAD2) || defined(CONFIG_AVALANCHE_SERIAL) + int avbaud; +#endif /* CONFIG_MIPS_AVALANCHE || CONFIG_MIPS_AVALANCHE_SERIAL */ unsigned cflag, cval, fcr = 0; int bits; unsigned long flags; @@ -1682,7 +1819,21 @@ /* Special case since 134 is really 134.5 */ quot = (2*baud_base / 269); else if (baud) - quot = baud_base / baud; + { +#if defined(CONFIG_AVALANCHE_SERIAL) + avbaud = (avalanche_get_vbus_freq()/baud); +#endif +#if defined(CONFIG_MIPS_SEAD2) + avbaud = ((3683400)/baud); +#endif +#if defined(CONFIG_MIPS_SEAD2) || defined(CONFIG_AVALANCHE_SERIAL) + if((avbaud%16)>7) avbaud+=8; + avbaud /=16; + quot = avbaud; +#else + quot = baud_base / baud; +#endif /* CONFIG_MIPS_SEAD2 || CONFIG_AVALANCHE_SERIAL */ + } } /* If the quotient is zero refuse the change */ if (!quot && old_termios) { @@ -1844,6 +1995,8 @@ save_flags(flags); cli(); info->IER |= UART_IER_THRI; serial_out(info, UART_IER, info->IER); + if (pxa_port(info->state->type)) + rs_interrupt_single(info->state->irq, NULL, NULL); restore_flags(flags); } @@ -1920,6 +2073,11 @@ && !(info->IER & UART_IER_THRI)) { info->IER |= UART_IER_THRI; serial_out(info, UART_IER, info->IER); + if (pxa_port(info->state->type)) { + save_flags(flags); cli(); + rs_interrupt_single(info->state->irq, NULL, NULL); + restore_flags(flags); + } } return ret; } @@ -1977,6 +2135,8 @@ /* Make sure transmit interrupts are on */ info->IER |= UART_IER_THRI; serial_out(info, UART_IER, info->IER); + if (pxa_port(info->state->type)) + rs_interrupt_single(info->state->irq, NULL, NULL); } } @@ -2365,7 +2525,7 @@ (info->state->port != 0 || info->state->iomem_base != 0) && (info->state->type != PORT_UNKNOWN)) { irq = detect_uart_irq(info->state); - if (irq > 0) + if (IRQ_VALID(irq)) info->state->irq = irq; } @@ -2466,7 +2626,7 @@ sizeof(struct serial_multiport_struct))) return -EFAULT; - if (new_multi.irq != state->irq || state->irq == 0 || + if (new_multi.irq != state->irq || !IRQ_VALID(state->irq) || !IRQ_ports[state->irq]) return -EINVAL; @@ -5393,6 +5553,7 @@ #endif serial_driver.major = TTY_MAJOR; serial_driver.minor_start = 64 + SERIAL_DEV_OFFSET; + serial_driver.name_base = SERIAL_DEV_OFFSET; serial_driver.num = NR_PORTS; serial_driver.type = TTY_DRIVER_TYPE_SERIAL; serial_driver.subtype = SERIAL_TYPE_NORMAL; @@ -5454,7 +5615,6 @@ for (i = 0, state = rs_table; i < NR_PORTS; i++,state++) { state->magic = SSTATE_MAGIC; state->line = i; - state->type = PORT_UNKNOWN; state->custom_divisor = 0; state->close_delay = 5*HZ/10; state->closing_wait = 30*HZ; @@ -5468,14 +5628,24 @@ state->irq = irq_cannonicalize(state->irq); if (state->hub6) state->io_type = SERIAL_IO_HUB6; - if (state->port && check_region(state->port,8)) +#if defined(CONFIG_AVALANCHE_SERIAL) || defined(CONFIG_MIPS_SEAD2) + /*nothing*/ +#else + if (state->port && check_region(state->port,8)) { + state->type = PORT_UNKNOWN; continue; + } + +#endif /* CONFIG_AVALANCHE_SERIAL || CONFIG_MIPS_SEAD2 */ + // } #ifdef CONFIG_MCA if ((state->flags & ASYNC_BOOT_ONLYMCA) && !MCA_bus) continue; #endif - if (state->flags & ASYNC_BOOT_AUTOCONF) + if (state->flags & ASYNC_BOOT_AUTOCONF) { + state->type = PORT_UNKNOWN; autoconfig(state); + } } for (i = 0, state = rs_table; i < NR_PORTS; i++,state++) { if (state->type == PORT_UNKNOWN) @@ -5485,7 +5655,7 @@ && (state->port != 0 || state->iomem_base != 0)) state->irq = detect_uart_irq(state); if (state->io_type == SERIAL_IO_MEM) { - printk(KERN_INFO"ttyS%02d%s at 0x%px (irq = %d) is a %s\n", + printk(KERN_INFO"ttyS%02d%s at 0x%p (irq = %d) is a %s\n", state->line + SERIAL_DEV_OFFSET, (state->flags & ASYNC_FOURPORT) ? " FourPort" : "", state->iomem_base, state->irq, @@ -5756,8 +5926,11 @@ */ static inline void wait_for_xmitr(struct async_struct *info) { +#if defined(CONFIG_AVALANCHE_SERIAL) || defined(CONFIG_MIPS_SEAD2) + unsigned int status, tmout = 100000; +#else unsigned int status, tmout = 1000000; - +#endif /* CONFIG_AVALANCHE_SERIAL || CONFIG_MIPS_SEAD2 */ do { status = serial_in(info, UART_LSR); @@ -5794,7 +5967,10 @@ * First save the IER then disable the interrupts */ ier = serial_in(info, UART_IER); - serial_out(info, UART_IER, 0x00); + if (pxa_port(info->state->type)) + serial_out(info, UART_IER, UART_IER_UUE); + else + serial_out(info, UART_IER, 0); /* * Now, do each character @@ -5837,7 +6013,10 @@ * character. */ ier = serial_in(info, UART_IER); - serial_out(info, UART_IER, 0x00); + if (pxa_port(info->state->type)) + serial_out(info, UART_IER, UART_IER_UUE); + else + serial_out(info, UART_IER, 0); while ((serial_in(info, UART_LSR) & UART_LSR_DR) == 0); c = serial_in(info, UART_RX); @@ -5861,7 +6040,11 @@ * - initialize the serial port * Return non-zero if we didn't find a serial port. */ +#if defined(CONFIG_AVALANCHE_CPU_FREQUENCY_SWITCHING) +int serial_console_setup(struct console *co, char *options) +#else /*--- #if defined(CONFIG_AVALANCHE_CPU_FREQUENCY_SWITCHING) ---*/ static int __init serial_console_setup(struct console *co, char *options) +#endif /*--- #else ---*/ /*--- #if defined(CONFIG_AVALANCHE_CPU_FREQUENCY_SWITCHING) ---*/ { static struct async_struct *info; struct serial_state *state; @@ -5873,6 +6056,21 @@ int cflag = CREAD | HUPCL | CLOCAL; int quot = 0; char *s; +#if defined(CONFIG_AVALANCHE_CPU_FREQUENCY_SWITCHING) + static struct console save_co; + static char save_options[128]; + if(co == NULL) + co = &save_co; + else + save_co = *co; + if(options == NULL) + options = save_options; + else + memcpy(save_options, options, sizeof(save_options)); +#endif /*--- #if defined(CONFIG_AVALANCHE_CPU_FREQUENCY_SWITCHING) ---*/ +#if defined(CONFIG_AVALANCHE_SERIAL) || defined(CONFIG_MIPS_SEAD2) + int avbaud; +#endif /* CONFIG_AVALANCHE_SERIAL || CONFIG_MIPS_SEAD2 */ if (options) { baud = simple_strtoul(options, NULL, 10); @@ -5887,6 +6085,9 @@ /* * Now construct a cflag setting. */ + /*--- baud = 38400; ---*/ + + switch(baud) { case 1200: cflag |= B1200; @@ -5954,7 +6155,21 @@ info->io_type = state->io_type; info->iomem_base = state->iomem_base; info->iomem_reg_shift = state->iomem_reg_shift; + +#if defined(CONFIG_AVALANCHE_SERIAL) + avbaud = (avalanche_get_vbus_freq()/baud); +#endif +#if defined(CONFIG_MIPS_SEAD2) + avbaud = ((3683400)/baud); +#endif +#if defined(CONFIG_MIPS_SEAD2) || defined(CONFIG_AVALANCHE_SERIAL) + if((avbaud%16)>7) avbaud+=8; + avbaud /=16; + quot = avbaud; +#else quot = state->baud_base / baud; +#endif /* CONFIG_MIPS_SEAD2 || CONFIG_AVALANCHE_SERIAL */ + cval = cflag & (CSIZE | CSTOPB); #if defined(__powerpc__) || defined(__alpha__) cval >>= 8; @@ -5974,7 +6189,10 @@ serial_out(info, UART_DLL, quot & 0xff); /* LS of divisor */ serial_out(info, UART_DLM, quot >> 8); /* MS of divisor */ serial_out(info, UART_LCR, cval); /* reset DLAB */ - serial_out(info, UART_IER, 0); + if (pxa_port(info->state->type)) + serial_out(info, UART_IER, UART_IER_UUE); + else + serial_out(info, UART_IER, 0); serial_out(info, UART_MCR, UART_MCR_DTR | UART_MCR_RTS); /*