1c751e17b5371ad86cdde6cf5c0175e06f3ff0347Thomas Gleixner/*
2c751e17b5371ad86cdde6cf5c0175e06f3ff0347Thomas Gleixner * Intel CE4100  platform specific setup code
3c751e17b5371ad86cdde6cf5c0175e06f3ff0347Thomas Gleixner *
4c751e17b5371ad86cdde6cf5c0175e06f3ff0347Thomas Gleixner * (C) Copyright 2010 Intel Corporation
5c751e17b5371ad86cdde6cf5c0175e06f3ff0347Thomas Gleixner *
6c751e17b5371ad86cdde6cf5c0175e06f3ff0347Thomas Gleixner * This program is free software; you can redistribute it and/or
7c751e17b5371ad86cdde6cf5c0175e06f3ff0347Thomas Gleixner * modify it under the terms of the GNU General Public License
8c751e17b5371ad86cdde6cf5c0175e06f3ff0347Thomas Gleixner * as published by the Free Software Foundation; version 2
9c751e17b5371ad86cdde6cf5c0175e06f3ff0347Thomas Gleixner * of the License.
10c751e17b5371ad86cdde6cf5c0175e06f3ff0347Thomas Gleixner */
11c751e17b5371ad86cdde6cf5c0175e06f3ff0347Thomas Gleixner#include <linux/init.h>
12c751e17b5371ad86cdde6cf5c0175e06f3ff0347Thomas Gleixner#include <linux/kernel.h>
13c751e17b5371ad86cdde6cf5c0175e06f3ff0347Thomas Gleixner#include <linux/irq.h>
14c751e17b5371ad86cdde6cf5c0175e06f3ff0347Thomas Gleixner#include <linux/module.h>
155ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie#include <linux/serial_reg.h>
165ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie#include <linux/serial_8250.h>
17c751e17b5371ad86cdde6cf5c0175e06f3ff0347Thomas Gleixner
1803150171dcf9492a96f57cbb2aef088bafcfcd2eSebastian Andrzej Siewior#include <asm/ce4100.h>
191fa4163bdc199a0b80f9e333d718b3f65e901593Sebastian Andrzej Siewior#include <asm/prom.h>
20c751e17b5371ad86cdde6cf5c0175e06f3ff0347Thomas Gleixner#include <asm/setup.h>
211fa4163bdc199a0b80f9e333d718b3f65e901593Sebastian Andrzej Siewior#include <asm/i8259.h>
225ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie#include <asm/io.h>
231fa4163bdc199a0b80f9e333d718b3f65e901593Sebastian Andrzej Siewior#include <asm/io_apic.h>
24c751e17b5371ad86cdde6cf5c0175e06f3ff0347Thomas Gleixner
25c751e17b5371ad86cdde6cf5c0175e06f3ff0347Thomas Gleixnerstatic int ce4100_i8042_detect(void)
26c751e17b5371ad86cdde6cf5c0175e06f3ff0347Thomas Gleixner{
27c751e17b5371ad86cdde6cf5c0175e06f3ff0347Thomas Gleixner	return 0;
28c751e17b5371ad86cdde6cf5c0175e06f3ff0347Thomas Gleixner}
29c751e17b5371ad86cdde6cf5c0175e06f3ff0347Thomas Gleixner
305ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie#ifdef CONFIG_SERIAL_8250
315ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie
325ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewiestatic unsigned int mem_serial_in(struct uart_port *p, int offset)
335ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie{
345ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie	offset = offset << p->regshift;
355ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie	return readl(p->membase + offset);
365ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie}
375ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie
385ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie/*
395ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie * The UART Tx interrupts are not set under some conditions and therefore serial
405ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie * transmission hangs. This is a silicon issue and has not been root caused. The
415ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie * workaround for this silicon issue checks UART_LSR_THRE bit and UART_LSR_TEMT
425ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie * bit of LSR register in interrupt handler to see whether at least one of these
435ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie * two bits is set, if so then process the transmit request. If this workaround
445ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie * is not applied, then the serial transmission may hang. This workaround is for
455ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie * errata number 9 in Errata - B step.
465ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie*/
475ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie
485ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewiestatic unsigned int ce4100_mem_serial_in(struct uart_port *p, int offset)
495ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie{
505ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie	unsigned int ret, ier, lsr;
515ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie
525ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie	if (offset == UART_IIR) {
535ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie		offset = offset << p->regshift;
545ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie		ret = readl(p->membase + offset);
555ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie		if (ret & UART_IIR_NO_INT) {
565ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie			/* see if the TX interrupt should have really set */
575ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie			ier = mem_serial_in(p, UART_IER);
585ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie			/* see if the UART's XMIT interrupt is enabled */
595ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie			if (ier & UART_IER_THRI) {
605ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie				lsr = mem_serial_in(p, UART_LSR);
615ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie				/* now check to see if the UART should be
625ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie				   generating an interrupt (but isn't) */
635ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie				if (lsr & (UART_LSR_THRE | UART_LSR_TEMT))
645ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie					ret &= ~UART_IIR_NO_INT;
655ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie			}
665ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie		}
675ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie	} else
685ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie		ret =  mem_serial_in(p, offset);
695ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie	return ret;
705ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie}
715ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie
725ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewiestatic void ce4100_mem_serial_out(struct uart_port *p, int offset, int value)
735ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie{
745ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie	offset = offset << p->regshift;
755ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie	writel(value, p->membase + offset);
765ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie}
775ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie
785ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewiestatic void ce4100_serial_fixup(int port, struct uart_port *up,
795ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie	unsigned short *capabilites)
805ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie{
815ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie#ifdef CONFIG_EARLY_PRINTK
825ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie	/*
835ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie	 * Over ride the legacy port configuration that comes from
845ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie	 * asm/serial.h. Using the ioport driver then switching to the
855ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie	 * PCI memmaped driver hangs the IOAPIC
865ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie	 */
875ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie	if (up->iotype !=  UPIO_MEM32) {
885ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie		up->uartclk  = 14745600;
895ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie		up->mapbase = 0xdffe0200;
905ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie		set_fixmap_nocache(FIX_EARLYCON_MEM_BASE,
915ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie				up->mapbase & PAGE_MASK);
925ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie		up->membase =
935ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie			(void __iomem *)__fix_to_virt(FIX_EARLYCON_MEM_BASE);
945ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie		up->membase += up->mapbase & ~PAGE_MASK;
955ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie		up->iotype   = UPIO_MEM32;
965ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie		up->regshift = 2;
975ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie	}
985ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie#endif
995ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie	up->iobase = 0;
1005ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie	up->serial_in = ce4100_mem_serial_in;
1015ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie	up->serial_out = ce4100_mem_serial_out;
1025ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie
1035ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie	*capabilites |= (1 << 12);
1045ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie}
1055ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie
1065ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewiestatic __init void sdv_serial_fixup(void)
1075ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie{
1085ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie	serial8250_set_isa_configurator(ce4100_serial_fixup);
1095ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie}
1105ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie
1115ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie#else
112f2ee442115c9b6219083c019939a9cc0c9abb2f8Zhang Ruistatic inline void sdv_serial_fixup(void) {};
1135ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie#endif
1145ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie
1155ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewiestatic void __init sdv_arch_setup(void)
116c751e17b5371ad86cdde6cf5c0175e06f3ff0347Thomas Gleixner{
1175ec6960f6f0c7be9cc6e5506fdf0070add3b6e08Dirk Brandewie	sdv_serial_fixup();
118c751e17b5371ad86cdde6cf5c0175e06f3ff0347Thomas Gleixner}
119c751e17b5371ad86cdde6cf5c0175e06f3ff0347Thomas Gleixner
1201fa4163bdc199a0b80f9e333d718b3f65e901593Sebastian Andrzej Siewior#ifdef CONFIG_X86_IO_APIC
1211fa4163bdc199a0b80f9e333d718b3f65e901593Sebastian Andrzej Siewiorstatic void __cpuinit sdv_pci_init(void)
1221fa4163bdc199a0b80f9e333d718b3f65e901593Sebastian Andrzej Siewior{
1231fa4163bdc199a0b80f9e333d718b3f65e901593Sebastian Andrzej Siewior	x86_of_pci_init();
1241fa4163bdc199a0b80f9e333d718b3f65e901593Sebastian Andrzej Siewior	/* We can't set this earlier, because we need to calibrate the timer */
1251fa4163bdc199a0b80f9e333d718b3f65e901593Sebastian Andrzej Siewior	legacy_pic = &null_legacy_pic;
1261fa4163bdc199a0b80f9e333d718b3f65e901593Sebastian Andrzej Siewior}
1271fa4163bdc199a0b80f9e333d718b3f65e901593Sebastian Andrzej Siewior#endif
1281fa4163bdc199a0b80f9e333d718b3f65e901593Sebastian Andrzej Siewior
129c751e17b5371ad86cdde6cf5c0175e06f3ff0347Thomas Gleixner/*
130c751e17b5371ad86cdde6cf5c0175e06f3ff0347Thomas Gleixner * CE4100 specific x86_init function overrides and early setup
131c751e17b5371ad86cdde6cf5c0175e06f3ff0347Thomas Gleixner * calls.
132c751e17b5371ad86cdde6cf5c0175e06f3ff0347Thomas Gleixner */
133c751e17b5371ad86cdde6cf5c0175e06f3ff0347Thomas Gleixnervoid __init x86_ce4100_early_setup(void)
134c751e17b5371ad86cdde6cf5c0175e06f3ff0347Thomas Gleixner{
135c751e17b5371ad86cdde6cf5c0175e06f3ff0347Thomas Gleixner	x86_init.oem.arch_setup = sdv_arch_setup;
136c751e17b5371ad86cdde6cf5c0175e06f3ff0347Thomas Gleixner	x86_platform.i8042_detect = ce4100_i8042_detect;
137c751e17b5371ad86cdde6cf5c0175e06f3ff0347Thomas Gleixner	x86_init.resources.probe_roms = x86_init_noop;
138c751e17b5371ad86cdde6cf5c0175e06f3ff0347Thomas Gleixner	x86_init.mpparse.get_smp_config = x86_init_uint_noop;
139a906fdaacca49917d83e5032dfc31f694249ad10Thomas Gleixner	x86_init.mpparse.find_smp_config = x86_init_noop;
14003150171dcf9492a96f57cbb2aef088bafcfcd2eSebastian Andrzej Siewior	x86_init.pci.init = ce4100_pci_init;
1411fa4163bdc199a0b80f9e333d718b3f65e901593Sebastian Andrzej Siewior
1421fa4163bdc199a0b80f9e333d718b3f65e901593Sebastian Andrzej Siewior#ifdef CONFIG_X86_IO_APIC
1431fa4163bdc199a0b80f9e333d718b3f65e901593Sebastian Andrzej Siewior	x86_init.pci.init_irq = sdv_pci_init;
1441fa4163bdc199a0b80f9e333d718b3f65e901593Sebastian Andrzej Siewior	x86_init.mpparse.setup_ioapic_ids = setup_ioapic_ids_from_mpc_nocheck;
1451fa4163bdc199a0b80f9e333d718b3f65e901593Sebastian Andrzej Siewior#endif
146c751e17b5371ad86cdde6cf5c0175e06f3ff0347Thomas Gleixner}
147