1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Intel CE4100 platform specific setup code |
4 | * |
5 | * (C) Copyright 2010 Intel Corporation |
6 | */ |
7 | #include <linux/init.h> |
8 | #include <linux/kernel.h> |
9 | #include <linux/irq.h> |
10 | #include <linux/reboot.h> |
11 | #include <linux/serial_reg.h> |
12 | #include <linux/serial_8250.h> |
13 | |
14 | #include <asm/ce4100.h> |
15 | #include <asm/prom.h> |
16 | #include <asm/setup.h> |
17 | #include <asm/i8259.h> |
18 | #include <asm/io.h> |
19 | #include <asm/io_apic.h> |
20 | #include <asm/emergency-restart.h> |
21 | |
22 | /* |
23 | * The CE4100 platform has an internal 8051 Microcontroller which is |
24 | * responsible for signaling to the external Power Management Unit the |
25 | * intention to reset, reboot or power off the system. This 8051 device has |
26 | * its command register mapped at I/O port 0xcf9 and the value 0x4 is used |
27 | * to power off the system. |
28 | */ |
29 | static void ce4100_power_off(void) |
30 | { |
31 | outb(value: 0x4, port: 0xcf9); |
32 | } |
33 | |
34 | #ifdef CONFIG_SERIAL_8250 |
35 | |
36 | static unsigned int mem_serial_in(struct uart_port *p, int offset) |
37 | { |
38 | offset = offset << p->regshift; |
39 | return readl(addr: p->membase + offset); |
40 | } |
41 | |
42 | /* |
43 | * The UART Tx interrupts are not set under some conditions and therefore serial |
44 | * transmission hangs. This is a silicon issue and has not been root caused. The |
45 | * workaround for this silicon issue checks UART_LSR_THRE bit and UART_LSR_TEMT |
46 | * bit of LSR register in interrupt handler to see whether at least one of these |
47 | * two bits is set, if so then process the transmit request. If this workaround |
48 | * is not applied, then the serial transmission may hang. This workaround is for |
49 | * errata number 9 in Errata - B step. |
50 | */ |
51 | |
52 | static unsigned int ce4100_mem_serial_in(struct uart_port *p, int offset) |
53 | { |
54 | unsigned int ret, ier, lsr; |
55 | |
56 | if (offset == UART_IIR) { |
57 | offset = offset << p->regshift; |
58 | ret = readl(addr: p->membase + offset); |
59 | if (ret & UART_IIR_NO_INT) { |
60 | /* see if the TX interrupt should have really set */ |
61 | ier = mem_serial_in(p, UART_IER); |
62 | /* see if the UART's XMIT interrupt is enabled */ |
63 | if (ier & UART_IER_THRI) { |
64 | lsr = mem_serial_in(p, UART_LSR); |
65 | /* now check to see if the UART should be |
66 | generating an interrupt (but isn't) */ |
67 | if (lsr & (UART_LSR_THRE | UART_LSR_TEMT)) |
68 | ret &= ~UART_IIR_NO_INT; |
69 | } |
70 | } |
71 | } else |
72 | ret = mem_serial_in(p, offset); |
73 | return ret; |
74 | } |
75 | |
76 | static void ce4100_mem_serial_out(struct uart_port *p, int offset, int value) |
77 | { |
78 | offset = offset << p->regshift; |
79 | writel(val: value, addr: p->membase + offset); |
80 | } |
81 | |
82 | static void ce4100_serial_fixup(int port, struct uart_port *up, |
83 | u32 *capabilities) |
84 | { |
85 | #ifdef CONFIG_EARLY_PRINTK |
86 | /* |
87 | * Over ride the legacy port configuration that comes from |
88 | * asm/serial.h. Using the ioport driver then switching to the |
89 | * PCI memmaped driver hangs the IOAPIC |
90 | */ |
91 | if (up->iotype != UPIO_MEM32) { |
92 | up->uartclk = 14745600; |
93 | up->mapbase = 0xdffe0200; |
94 | set_fixmap_nocache(FIX_EARLYCON_MEM_BASE, |
95 | up->mapbase & PAGE_MASK); |
96 | up->membase = |
97 | (void __iomem *)__fix_to_virt(FIX_EARLYCON_MEM_BASE); |
98 | up->membase += up->mapbase & ~PAGE_MASK; |
99 | up->mapbase += port * 0x100; |
100 | up->membase += port * 0x100; |
101 | up->iotype = UPIO_MEM32; |
102 | up->regshift = 2; |
103 | up->irq = 4; |
104 | } |
105 | #endif |
106 | up->iobase = 0; |
107 | up->serial_in = ce4100_mem_serial_in; |
108 | up->serial_out = ce4100_mem_serial_out; |
109 | |
110 | *capabilities |= (1 << 12); |
111 | } |
112 | |
113 | static __init void sdv_serial_fixup(void) |
114 | { |
115 | serial8250_set_isa_configurator(v: ce4100_serial_fixup); |
116 | } |
117 | |
118 | #else |
119 | static inline void sdv_serial_fixup(void) {}; |
120 | #endif |
121 | |
122 | static void __init sdv_arch_setup(void) |
123 | { |
124 | sdv_serial_fixup(); |
125 | } |
126 | |
127 | static void sdv_pci_init(void) |
128 | { |
129 | x86_of_pci_init(); |
130 | } |
131 | |
132 | /* |
133 | * CE4100 specific x86_init function overrides and early setup |
134 | * calls. |
135 | */ |
136 | void __init x86_ce4100_early_setup(void) |
137 | { |
138 | x86_init.oem.arch_setup = sdv_arch_setup; |
139 | x86_init.resources.probe_roms = x86_init_noop; |
140 | x86_init.mpparse.find_mptable = x86_init_noop; |
141 | x86_init.mpparse.early_parse_smp_cfg = x86_init_noop; |
142 | x86_init.mpparse.parse_smp_cfg = x86_dtb_parse_smp_config; |
143 | x86_init.pci.init = ce4100_pci_init; |
144 | x86_init.pci.init_irq = sdv_pci_init; |
145 | |
146 | /* |
147 | * By default, the reboot method is ACPI which is supported by the |
148 | * CE4100 bootloader CEFDK using FADT.ResetReg Address and ResetValue |
149 | * the bootloader will however issue a system power off instead of |
150 | * reboot. By using BOOT_KBD we ensure proper system reboot as |
151 | * expected. |
152 | */ |
153 | reboot_type = BOOT_KBD; |
154 | |
155 | pm_power_off = ce4100_power_off; |
156 | } |
157 | |