1 /* 2 * Intel CE4100 platform specific setup code 3 * 4 * (C) Copyright 2010 Intel Corporation 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License 8 * as published by the Free Software Foundation; version 2 9 * of the License. 10 */ 11 #include <linux/init.h> 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/prom.h> 19 #include <asm/setup.h> 20 #include <asm/i8259.h> 21 #include <asm/io.h> 22 #include <asm/io_apic.h> 23 24 static int ce4100_i8042_detect(void) 25 { 26 return 0; 27 } 28 29 #ifdef CONFIG_SERIAL_8250 30 31 static unsigned int mem_serial_in(struct uart_port *p, int offset) 32 { 33 offset = offset << p->regshift; 34 return readl(p->membase + offset); 35 } 36 37 /* 38 * The UART Tx interrupts are not set under some conditions and therefore serial 39 * transmission hangs. This is a silicon issue and has not been root caused. The 40 * workaround for this silicon issue checks UART_LSR_THRE bit and UART_LSR_TEMT 41 * bit of LSR register in interrupt handler to see whether at least one of these 42 * two bits is set, if so then process the transmit request. If this workaround 43 * is not applied, then the serial transmission may hang. This workaround is for 44 * errata number 9 in Errata - B step. 45 */ 46 47 static unsigned int ce4100_mem_serial_in(struct uart_port *p, int offset) 48 { 49 unsigned int ret, ier, lsr; 50 51 if (offset == UART_IIR) { 52 offset = offset << p->regshift; 53 ret = readl(p->membase + offset); 54 if (ret & UART_IIR_NO_INT) { 55 /* see if the TX interrupt should have really set */ 56 ier = mem_serial_in(p, UART_IER); 57 /* see if the UART's XMIT interrupt is enabled */ 58 if (ier & UART_IER_THRI) { 59 lsr = mem_serial_in(p, UART_LSR); 60 /* now check to see if the UART should be 61 generating an interrupt (but isn't) */ 62 if (lsr & (UART_LSR_THRE | UART_LSR_TEMT)) 63 ret &= ~UART_IIR_NO_INT; 64 } 65 } 66 } else 67 ret = mem_serial_in(p, offset); 68 return ret; 69 } 70 71 static void ce4100_mem_serial_out(struct uart_port *p, int offset, int value) 72 { 73 offset = offset << p->regshift; 74 writel(value, p->membase + offset); 75 } 76 77 static void ce4100_serial_fixup(int port, struct uart_port *up, 78 unsigned short *capabilites) 79 { 80 #ifdef CONFIG_EARLY_PRINTK 81 /* 82 * Over ride the legacy port configuration that comes from 83 * asm/serial.h. Using the ioport driver then switching to the 84 * PCI memmaped driver hangs the IOAPIC 85 */ 86 if (up->iotype != UPIO_MEM32) { 87 up->uartclk = 14745600; 88 up->mapbase = 0xdffe0200; 89 set_fixmap_nocache(FIX_EARLYCON_MEM_BASE, 90 up->mapbase & PAGE_MASK); 91 up->membase = 92 (void __iomem *)__fix_to_virt(FIX_EARLYCON_MEM_BASE); 93 up->membase += up->mapbase & ~PAGE_MASK; 94 up->iotype = UPIO_MEM32; 95 up->regshift = 2; 96 } 97 #endif 98 up->iobase = 0; 99 up->serial_in = ce4100_mem_serial_in; 100 up->serial_out = ce4100_mem_serial_out; 101 102 *capabilites |= (1 << 12); 103 } 104 105 static __init void sdv_serial_fixup(void) 106 { 107 serial8250_set_isa_configurator(ce4100_serial_fixup); 108 } 109 110 #else 111 static inline void sdv_serial_fixup(void); 112 #endif 113 114 static void __init sdv_arch_setup(void) 115 { 116 sdv_serial_fixup(); 117 } 118 119 #ifdef CONFIG_X86_IO_APIC 120 static void __cpuinit sdv_pci_init(void) 121 { 122 x86_of_pci_init(); 123 /* We can't set this earlier, because we need to calibrate the timer */ 124 legacy_pic = &null_legacy_pic; 125 } 126 #endif 127 128 /* 129 * CE4100 specific x86_init function overrides and early setup 130 * calls. 131 */ 132 void __init x86_ce4100_early_setup(void) 133 { 134 x86_init.oem.arch_setup = sdv_arch_setup; 135 x86_platform.i8042_detect = ce4100_i8042_detect; 136 x86_init.resources.probe_roms = x86_init_noop; 137 x86_init.mpparse.get_smp_config = x86_dtb_get_config; 138 x86_init.mpparse.find_smp_config = x86_dtb_find_config; 139 140 #ifdef CONFIG_X86_IO_APIC 141 x86_init.pci.init_irq = sdv_pci_init; 142 x86_init.mpparse.setup_ioapic_ids = setup_ioapic_ids_from_mpc_nocheck; 143 #endif 144 } 145