ce4100: Add errata fixes for UART on CE4100

This patch enables the UART on the CE4100. The UART has a couple of
issues that need to be worked around. First the UART is mostly PC
compatible except that it is clocked eight times faster than a
standard PC so the default configuration provided in
arch/x86/include/asm/serial.h needs to be overridden. Second the TX
interrupt may not be set correctly all the time. Lastly accessing the
UART via I/O space for early_prink() hangs the chip when the IOAPIC is
enabled.

A custom mem_serial_in() is provided to work around the TX interrupt
issue. The configuration issues are dealt with in the call back
registered with the 8250 driver via serial8250_set_isa_configurator()

Signed-off-by: Dirk Brandewie <dirk.brandewie@gmail.com>
LKML-Reference: <1290436128-17958-1-git-send-email-dirk.brandewie@gmail.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>

authored by Dirk Brandewie and committed by Thomas Gleixner 5ec6960f 9cdca869

+92 -2
+92 -2
arch/x86/platform/ce4100/ce4100.c
··· 12 #include <linux/kernel.h> 13 #include <linux/irq.h> 14 #include <linux/module.h> 15 16 #include <asm/setup.h> 17 18 static int ce4100_i8042_detect(void) 19 { 20 return 0; 21 } 22 23 - static void __init sdv_arch_setup(void) 24 { 25 } 26 27 - static void __init sdv_find_smp_config(void) 28 { 29 } 30 31 /*
··· 12 #include <linux/kernel.h> 13 #include <linux/irq.h> 14 #include <linux/module.h> 15 + #include <linux/serial_reg.h> 16 + #include <linux/serial_8250.h> 17 18 #include <asm/setup.h> 19 + #include <asm/io.h> 20 21 static int ce4100_i8042_detect(void) 22 { 23 return 0; 24 } 25 26 + static void __init sdv_find_smp_config(void) 27 { 28 } 29 30 + #ifdef CONFIG_SERIAL_8250 31 + 32 + 33 + static unsigned int mem_serial_in(struct uart_port *p, int offset) 34 { 35 + offset = offset << p->regshift; 36 + return readl(p->membase + offset); 37 + } 38 + 39 + /* 40 + * The UART Tx interrupts are not set under some conditions and therefore serial 41 + * transmission hangs. This is a silicon issue and has not been root caused. The 42 + * workaround for this silicon issue checks UART_LSR_THRE bit and UART_LSR_TEMT 43 + * bit of LSR register in interrupt handler to see whether at least one of these 44 + * two bits is set, if so then process the transmit request. If this workaround 45 + * is not applied, then the serial transmission may hang. This workaround is for 46 + * errata number 9 in Errata - B step. 47 + */ 48 + 49 + static unsigned int ce4100_mem_serial_in(struct uart_port *p, int offset) 50 + { 51 + unsigned int ret, ier, lsr; 52 + 53 + if (offset == UART_IIR) { 54 + offset = offset << p->regshift; 55 + ret = readl(p->membase + offset); 56 + if (ret & UART_IIR_NO_INT) { 57 + /* see if the TX interrupt should have really set */ 58 + ier = mem_serial_in(p, UART_IER); 59 + /* see if the UART's XMIT interrupt is enabled */ 60 + if (ier & UART_IER_THRI) { 61 + lsr = mem_serial_in(p, UART_LSR); 62 + /* now check to see if the UART should be 63 + generating an interrupt (but isn't) */ 64 + if (lsr & (UART_LSR_THRE | UART_LSR_TEMT)) 65 + ret &= ~UART_IIR_NO_INT; 66 + } 67 + } 68 + } else 69 + ret = mem_serial_in(p, offset); 70 + return ret; 71 + } 72 + 73 + static void ce4100_mem_serial_out(struct uart_port *p, int offset, int value) 74 + { 75 + offset = offset << p->regshift; 76 + writel(value, p->membase + offset); 77 + } 78 + 79 + static void ce4100_serial_fixup(int port, struct uart_port *up, 80 + unsigned short *capabilites) 81 + { 82 + #ifdef CONFIG_EARLY_PRINTK 83 + /* 84 + * Over ride the legacy port configuration that comes from 85 + * asm/serial.h. Using the ioport driver then switching to the 86 + * PCI memmaped driver hangs the IOAPIC 87 + */ 88 + if (up->iotype != UPIO_MEM32) { 89 + up->uartclk = 14745600; 90 + up->mapbase = 0xdffe0200; 91 + set_fixmap_nocache(FIX_EARLYCON_MEM_BASE, 92 + up->mapbase & PAGE_MASK); 93 + up->membase = 94 + (void __iomem *)__fix_to_virt(FIX_EARLYCON_MEM_BASE); 95 + up->membase += up->mapbase & ~PAGE_MASK; 96 + up->iotype = UPIO_MEM32; 97 + up->regshift = 2; 98 + } 99 + #endif 100 + up->iobase = 0; 101 + up->serial_in = ce4100_mem_serial_in; 102 + up->serial_out = ce4100_mem_serial_out; 103 + 104 + *capabilites |= (1 << 12); 105 + } 106 + 107 + static __init void sdv_serial_fixup(void) 108 + { 109 + serial8250_set_isa_configurator(ce4100_serial_fixup); 110 + } 111 + 112 + #else 113 + static inline void sdv_serial_fixup(void); 114 + #endif 115 + 116 + static void __init sdv_arch_setup(void) 117 + { 118 + sdv_serial_fixup(); 119 } 120 121 /*