diff options
author | 2025-03-08 22:04:20 +0800 | |
---|---|---|
committer | 2025-03-08 22:04:20 +0800 | |
commit | a07bb8fd1299070229f0e8f3dcb57ffd5ef9870a (patch) | |
tree | 84f21bd0bf7071bc5fc7dd989e77d7ceb5476682 /arch/mips/sni | |
download | ohosKernel-a07bb8fd1299070229f0e8f3dcb57ffd5ef9870a.tar.gz ohosKernel-a07bb8fd1299070229f0e8f3dcb57ffd5ef9870a.zip |
Initial commit: OpenHarmony-v4.0-ReleaseOpenHarmony-v4.0-Release
Diffstat (limited to 'arch/mips/sni')
-rw-r--r-- | arch/mips/sni/Makefile | 7 | ||||
-rw-r--r-- | arch/mips/sni/Platform | 10 | ||||
-rw-r--r-- | arch/mips/sni/a20r.c | 256 | ||||
-rw-r--r-- | arch/mips/sni/eisa.c | 49 | ||||
-rw-r--r-- | arch/mips/sni/irq.c | 76 | ||||
-rw-r--r-- | arch/mips/sni/pcimt.c | 332 | ||||
-rw-r--r-- | arch/mips/sni/pcit.c | 295 | ||||
-rw-r--r-- | arch/mips/sni/reset.c | 48 | ||||
-rw-r--r-- | arch/mips/sni/rm200.c | 485 | ||||
-rw-r--r-- | arch/mips/sni/setup.c | 263 | ||||
-rw-r--r-- | arch/mips/sni/time.c | 167 |
11 files changed, 1988 insertions, 0 deletions
diff --git a/arch/mips/sni/Makefile b/arch/mips/sni/Makefile new file mode 100644 index 000000000..6d97c3e96 --- /dev/null +++ b/arch/mips/sni/Makefile | |||
@@ -0,0 +1,7 @@ | |||
1 | # SPDX-License-Identifier: GPL-2.0-only | ||
2 | # | ||
3 | # Makefile for the SNI specific part of the kernel | ||
4 | # | ||
5 | |||
6 | obj-y += irq.o reset.o setup.o a20r.o rm200.o pcimt.o pcit.o time.o | ||
7 | obj-$(CONFIG_EISA) += eisa.o | ||
diff --git a/arch/mips/sni/Platform b/arch/mips/sni/Platform new file mode 100644 index 000000000..b0b3dde0b --- /dev/null +++ b/arch/mips/sni/Platform | |||
@@ -0,0 +1,10 @@ | |||
1 | # | ||
2 | # SNI RM | ||
3 | # | ||
4 | cflags-$(CONFIG_SNI_RM) += -I$(srctree)/arch/mips/include/asm/mach-rm | ||
5 | ifdef CONFIG_CPU_LITTLE_ENDIAN | ||
6 | load-$(CONFIG_SNI_RM) += 0xffffffff80600000 | ||
7 | else | ||
8 | load-$(CONFIG_SNI_RM) += 0xffffffff80030000 | ||
9 | endif | ||
10 | all-$(CONFIG_SNI_RM) := $(COMPRESSION_FNAME).ecoff | ||
diff --git a/arch/mips/sni/a20r.c b/arch/mips/sni/a20r.c new file mode 100644 index 000000000..eeeec18c4 --- /dev/null +++ b/arch/mips/sni/a20r.c | |||
@@ -0,0 +1,256 @@ | |||
1 | /* | ||
2 | * A20R specific code | ||
3 | * | ||
4 | * This file is subject to the terms and conditions of the GNU General Public | ||
5 | * License. See the file "COPYING" in the main directory of this archive | ||
6 | * for more details. | ||
7 | * | ||
8 | * Copyright (C) 2006 Thomas Bogendoerfer (tsbogend@alpha.franken.de) | ||
9 | */ | ||
10 | |||
11 | #include <linux/init.h> | ||
12 | #include <linux/interrupt.h> | ||
13 | #include <linux/irq.h> | ||
14 | #include <linux/platform_device.h> | ||
15 | #include <linux/serial_8250.h> | ||
16 | |||
17 | #include <asm/sni.h> | ||
18 | #include <asm/time.h> | ||
19 | |||
20 | #define PORT(_base,_irq) \ | ||
21 | { \ | ||
22 | .iobase = _base, \ | ||
23 | .irq = _irq, \ | ||
24 | .uartclk = 1843200, \ | ||
25 | .iotype = UPIO_PORT, \ | ||
26 | .flags = UPF_BOOT_AUTOCONF, \ | ||
27 | } | ||
28 | |||
29 | static struct plat_serial8250_port a20r_data[] = { | ||
30 | PORT(0x3f8, 4), | ||
31 | PORT(0x2f8, 3), | ||
32 | { }, | ||
33 | }; | ||
34 | |||
35 | static struct platform_device a20r_serial8250_device = { | ||
36 | .name = "serial8250", | ||
37 | .id = PLAT8250_DEV_PLATFORM, | ||
38 | .dev = { | ||
39 | .platform_data = a20r_data, | ||
40 | }, | ||
41 | }; | ||
42 | |||
43 | static struct resource a20r_ds1216_rsrc[] = { | ||
44 | { | ||
45 | .start = 0x1c081ffc, | ||
46 | .end = 0x1c081fff, | ||
47 | .flags = IORESOURCE_MEM | ||
48 | } | ||
49 | }; | ||
50 | |||
51 | static struct platform_device a20r_ds1216_device = { | ||
52 | .name = "rtc-ds1216", | ||
53 | .num_resources = ARRAY_SIZE(a20r_ds1216_rsrc), | ||
54 | .resource = a20r_ds1216_rsrc | ||
55 | }; | ||
56 | |||
57 | static struct resource snirm_82596_rsrc[] = { | ||
58 | { | ||
59 | .start = 0x18000000, | ||
60 | .end = 0x18000004, | ||
61 | .flags = IORESOURCE_MEM | ||
62 | }, | ||
63 | { | ||
64 | .start = 0x18010000, | ||
65 | .end = 0x18010004, | ||
66 | .flags = IORESOURCE_MEM | ||
67 | }, | ||
68 | { | ||
69 | .start = 0x1ff00000, | ||
70 | .end = 0x1ff00020, | ||
71 | .flags = IORESOURCE_MEM | ||
72 | }, | ||
73 | { | ||
74 | .start = 22, | ||
75 | .end = 22, | ||
76 | .flags = IORESOURCE_IRQ | ||
77 | }, | ||
78 | { | ||
79 | .flags = 0x01 /* 16bit mpu port access */ | ||
80 | } | ||
81 | }; | ||
82 | |||
83 | static struct platform_device snirm_82596_pdev = { | ||
84 | .name = "snirm_82596", | ||
85 | .num_resources = ARRAY_SIZE(snirm_82596_rsrc), | ||
86 | .resource = snirm_82596_rsrc | ||
87 | }; | ||
88 | |||
89 | static struct resource snirm_53c710_rsrc[] = { | ||
90 | { | ||
91 | .start = 0x19000000, | ||
92 | .end = 0x190fffff, | ||
93 | .flags = IORESOURCE_MEM | ||
94 | }, | ||
95 | { | ||
96 | .start = 19, | ||
97 | .end = 19, | ||
98 | .flags = IORESOURCE_IRQ | ||
99 | } | ||
100 | }; | ||
101 | |||
102 | static struct platform_device snirm_53c710_pdev = { | ||
103 | .name = "snirm_53c710", | ||
104 | .num_resources = ARRAY_SIZE(snirm_53c710_rsrc), | ||
105 | .resource = snirm_53c710_rsrc | ||
106 | }; | ||
107 | |||
108 | static struct resource sc26xx_rsrc[] = { | ||
109 | { | ||
110 | .start = 0x1c070000, | ||
111 | .end = 0x1c0700ff, | ||
112 | .flags = IORESOURCE_MEM | ||
113 | }, | ||
114 | { | ||
115 | .start = 20, | ||
116 | .end = 20, | ||
117 | .flags = IORESOURCE_IRQ | ||
118 | } | ||
119 | }; | ||
120 | |||
121 | #include <linux/platform_data/serial-sccnxp.h> | ||
122 | |||
123 | static struct sccnxp_pdata sccnxp_data = { | ||
124 | .reg_shift = 2, | ||
125 | .mctrl_cfg[0] = MCTRL_SIG(DTR_OP, LINE_OP7) | | ||
126 | MCTRL_SIG(RTS_OP, LINE_OP3) | | ||
127 | MCTRL_SIG(DSR_IP, LINE_IP5) | | ||
128 | MCTRL_SIG(DCD_IP, LINE_IP6), | ||
129 | .mctrl_cfg[1] = MCTRL_SIG(DTR_OP, LINE_OP2) | | ||
130 | MCTRL_SIG(RTS_OP, LINE_OP1) | | ||
131 | MCTRL_SIG(DSR_IP, LINE_IP0) | | ||
132 | MCTRL_SIG(CTS_IP, LINE_IP1) | | ||
133 | MCTRL_SIG(DCD_IP, LINE_IP2) | | ||
134 | MCTRL_SIG(RNG_IP, LINE_IP3), | ||
135 | }; | ||
136 | |||
137 | static struct platform_device sc26xx_pdev = { | ||
138 | .name = "sc2681", | ||
139 | .resource = sc26xx_rsrc, | ||
140 | .num_resources = ARRAY_SIZE(sc26xx_rsrc), | ||
141 | .dev = { | ||
142 | .platform_data = &sccnxp_data, | ||
143 | }, | ||
144 | }; | ||
145 | |||
146 | /* | ||
147 | * Trigger chipset to update CPU's CAUSE IP field | ||
148 | */ | ||
149 | static u32 a20r_update_cause_ip(void) | ||
150 | { | ||
151 | u32 status = read_c0_status(); | ||
152 | |||
153 | write_c0_status(status | 0x00010000); | ||
154 | asm volatile( | ||
155 | " .set push \n" | ||
156 | " .set noat \n" | ||
157 | " .set noreorder \n" | ||
158 | " lw $1, 0(%0) \n" | ||
159 | " sb $0, 0(%1) \n" | ||
160 | " sync \n" | ||
161 | " lb %1, 0(%1) \n" | ||
162 | " b 1f \n" | ||
163 | " ori %1, $1, 2 \n" | ||
164 | " .align 8 \n" | ||
165 | "1: \n" | ||
166 | " nop \n" | ||
167 | " sw %1, 0(%0) \n" | ||
168 | " sync \n" | ||
169 | " li %1, 0x20 \n" | ||
170 | "2: \n" | ||
171 | " nop \n" | ||
172 | " bnez %1,2b \n" | ||
173 | " addiu %1, -1 \n" | ||
174 | " sw $1, 0(%0) \n" | ||
175 | " sync \n" | ||
176 | ".set pop \n" | ||
177 | : | ||
178 | : "Jr" (PCIMT_UCONF), "Jr" (0xbc000000)); | ||
179 | write_c0_status(status); | ||
180 | |||
181 | return status; | ||
182 | } | ||
183 | |||
184 | static inline void unmask_a20r_irq(struct irq_data *d) | ||
185 | { | ||
186 | set_c0_status(0x100 << (d->irq - SNI_A20R_IRQ_BASE)); | ||
187 | irq_enable_hazard(); | ||
188 | } | ||
189 | |||
190 | static inline void mask_a20r_irq(struct irq_data *d) | ||
191 | { | ||
192 | clear_c0_status(0x100 << (d->irq - SNI_A20R_IRQ_BASE)); | ||
193 | irq_disable_hazard(); | ||
194 | } | ||
195 | |||
196 | static struct irq_chip a20r_irq_type = { | ||
197 | .name = "A20R", | ||
198 | .irq_mask = mask_a20r_irq, | ||
199 | .irq_unmask = unmask_a20r_irq, | ||
200 | }; | ||
201 | |||
202 | /* | ||
203 | * hwint 0 receive all interrupts | ||
204 | */ | ||
205 | static void a20r_hwint(void) | ||
206 | { | ||
207 | u32 cause, status; | ||
208 | int irq; | ||
209 | |||
210 | clear_c0_status(IE_IRQ0); | ||
211 | status = a20r_update_cause_ip(); | ||
212 | cause = read_c0_cause(); | ||
213 | |||
214 | irq = ffs(((cause & status) >> 8) & 0xf8); | ||
215 | if (likely(irq > 0)) | ||
216 | do_IRQ(SNI_A20R_IRQ_BASE + irq - 1); | ||
217 | |||
218 | a20r_update_cause_ip(); | ||
219 | set_c0_status(IE_IRQ0); | ||
220 | } | ||
221 | |||
222 | void __init sni_a20r_irq_init(void) | ||
223 | { | ||
224 | int i; | ||
225 | |||
226 | for (i = SNI_A20R_IRQ_BASE + 2 ; i < SNI_A20R_IRQ_BASE + 8; i++) | ||
227 | irq_set_chip_and_handler(i, &a20r_irq_type, handle_level_irq); | ||
228 | sni_hwint = a20r_hwint; | ||
229 | change_c0_status(ST0_IM, IE_IRQ0); | ||
230 | if (request_irq(SNI_A20R_IRQ_BASE + 3, sni_isa_irq_handler, | ||
231 | IRQF_SHARED, "ISA", sni_isa_irq_handler)) | ||
232 | pr_err("Failed to register ISA interrupt\n"); | ||
233 | } | ||
234 | |||
235 | void sni_a20r_init(void) | ||
236 | { | ||
237 | /* FIXME, remove if not needed */ | ||
238 | } | ||
239 | |||
240 | static int __init snirm_a20r_setup_devinit(void) | ||
241 | { | ||
242 | switch (sni_brd_type) { | ||
243 | case SNI_BRD_TOWER_OASIC: | ||
244 | case SNI_BRD_MINITOWER: | ||
245 | platform_device_register(&snirm_82596_pdev); | ||
246 | platform_device_register(&snirm_53c710_pdev); | ||
247 | platform_device_register(&sc26xx_pdev); | ||
248 | platform_device_register(&a20r_serial8250_device); | ||
249 | platform_device_register(&a20r_ds1216_device); | ||
250 | sni_eisa_root_init(); | ||
251 | break; | ||
252 | } | ||
253 | return 0; | ||
254 | } | ||
255 | |||
256 | device_initcall(snirm_a20r_setup_devinit); | ||
diff --git a/arch/mips/sni/eisa.c b/arch/mips/sni/eisa.c new file mode 100644 index 000000000..483a43d07 --- /dev/null +++ b/arch/mips/sni/eisa.c | |||
@@ -0,0 +1,49 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0-only | ||
2 | /* | ||
3 | * Virtual EISA root driver. | ||
4 | * Acts as a placeholder if we don't have a proper EISA bridge. | ||
5 | * | ||
6 | * (C) 2003 Marc Zyngier <maz@wild-wind.fr.eu.org> | ||
7 | * modified for SNI usage by Thomas Bogendoerfer | ||
8 | */ | ||
9 | |||
10 | #include <linux/kernel.h> | ||
11 | #include <linux/platform_device.h> | ||
12 | #include <linux/eisa.h> | ||
13 | #include <linux/init.h> | ||
14 | |||
15 | /* The default EISA device parent (virtual root device). | ||
16 | * Now use a platform device, since that's the obvious choice. */ | ||
17 | |||
18 | static struct platform_device eisa_root_dev = { | ||
19 | .name = "eisa", | ||
20 | .id = 0, | ||
21 | }; | ||
22 | |||
23 | static struct eisa_root_device eisa_bus_root = { | ||
24 | .dev = &eisa_root_dev.dev, | ||
25 | .bus_base_addr = 0, | ||
26 | .res = &ioport_resource, | ||
27 | .slots = EISA_MAX_SLOTS, | ||
28 | .dma_mask = 0xffffffff, | ||
29 | .force_probe = 1, | ||
30 | }; | ||
31 | |||
32 | int __init sni_eisa_root_init(void) | ||
33 | { | ||
34 | int r; | ||
35 | |||
36 | r = platform_device_register(&eisa_root_dev); | ||
37 | if (!r) | ||
38 | return r; | ||
39 | |||
40 | dev_set_drvdata(&eisa_root_dev.dev, &eisa_bus_root); | ||
41 | |||
42 | if (eisa_root_register(&eisa_bus_root)) { | ||
43 | /* A real bridge may have been registered before | ||
44 | * us. So quietly unregister. */ | ||
45 | platform_device_unregister(&eisa_root_dev); | ||
46 | return -1; | ||
47 | } | ||
48 | return 0; | ||
49 | } | ||
diff --git a/arch/mips/sni/irq.c b/arch/mips/sni/irq.c new file mode 100644 index 000000000..dec89afc9 --- /dev/null +++ b/arch/mips/sni/irq.c | |||
@@ -0,0 +1,76 @@ | |||
1 | /* | ||
2 | * This file is subject to the terms and conditions of the GNU General Public | ||
3 | * License. See the file "COPYING" in the main directory of this archive | ||
4 | * for more details. | ||
5 | * | ||
6 | * Copyright (C) 1992 Linus Torvalds | ||
7 | * Copyright (C) 1994 - 2000 Ralf Baechle | ||
8 | * Copyright (C) 2006 Thomas Bogendoerfer | ||
9 | */ | ||
10 | #include <linux/delay.h> | ||
11 | #include <linux/init.h> | ||
12 | #include <linux/interrupt.h> | ||
13 | #include <linux/irq.h> | ||
14 | #include <linux/kernel.h> | ||
15 | |||
16 | #include <asm/i8259.h> | ||
17 | #include <asm/io.h> | ||
18 | #include <asm/sni.h> | ||
19 | #include <asm/irq.h> | ||
20 | #include <asm/irq_cpu.h> | ||
21 | |||
22 | void (*sni_hwint)(void); | ||
23 | |||
24 | asmlinkage void plat_irq_dispatch(void) | ||
25 | { | ||
26 | sni_hwint(); | ||
27 | } | ||
28 | |||
29 | /* ISA irq handler */ | ||
30 | irqreturn_t sni_isa_irq_handler(int dummy, void *p) | ||
31 | { | ||
32 | int irq; | ||
33 | |||
34 | irq = i8259_irq(); | ||
35 | if (unlikely(irq < 0)) | ||
36 | return IRQ_NONE; | ||
37 | |||
38 | generic_handle_irq(irq); | ||
39 | return IRQ_HANDLED; | ||
40 | } | ||
41 | |||
42 | /* | ||
43 | * On systems with i8259-style interrupt controllers we assume for | ||
44 | * driver compatibility reasons interrupts 0 - 15 to be the i8295 | ||
45 | * interrupts even if the hardware uses a different interrupt numbering. | ||
46 | */ | ||
47 | void __init arch_init_irq(void) | ||
48 | { | ||
49 | init_i8259_irqs(); /* Integrated i8259 */ | ||
50 | switch (sni_brd_type) { | ||
51 | case SNI_BRD_10: | ||
52 | case SNI_BRD_10NEW: | ||
53 | case SNI_BRD_TOWER_OASIC: | ||
54 | case SNI_BRD_MINITOWER: | ||
55 | sni_a20r_irq_init(); | ||
56 | break; | ||
57 | |||
58 | case SNI_BRD_PCI_TOWER: | ||
59 | sni_pcit_irq_init(); | ||
60 | break; | ||
61 | |||
62 | case SNI_BRD_PCI_TOWER_CPLUS: | ||
63 | sni_pcit_cplus_irq_init(); | ||
64 | break; | ||
65 | |||
66 | case SNI_BRD_RM200: | ||
67 | sni_rm200_irq_init(); | ||
68 | break; | ||
69 | |||
70 | case SNI_BRD_PCI_MTOWER: | ||
71 | case SNI_BRD_PCI_DESKTOP: | ||
72 | case SNI_BRD_PCI_MTOWER_CPLUS: | ||
73 | sni_pcimt_irq_init(); | ||
74 | break; | ||
75 | } | ||
76 | } | ||
diff --git a/arch/mips/sni/pcimt.c b/arch/mips/sni/pcimt.c new file mode 100644 index 000000000..12336c2a6 --- /dev/null +++ b/arch/mips/sni/pcimt.c | |||
@@ -0,0 +1,332 @@ | |||
1 | /* | ||
2 | * PCIMT specific code | ||
3 | * | ||
4 | * This file is subject to the terms and conditions of the GNU General Public | ||
5 | * License. See the file "COPYING" in the main directory of this archive | ||
6 | * for more details. | ||
7 | * | ||
8 | * Copyright (C) 1996, 97, 98, 2000, 03, 04, 06 Ralf Baechle (ralf@linux-mips.org) | ||
9 | * Copyright (C) 2006,2007 Thomas Bogendoerfer (tsbogend@alpha.franken.de) | ||
10 | */ | ||
11 | |||
12 | #include <linux/init.h> | ||
13 | #include <linux/interrupt.h> | ||
14 | #include <linux/irq.h> | ||
15 | #include <linux/pci.h> | ||
16 | #include <linux/serial_8250.h> | ||
17 | |||
18 | #include <asm/sni.h> | ||
19 | #include <asm/time.h> | ||
20 | #include <asm/i8259.h> | ||
21 | #include <asm/irq_cpu.h> | ||
22 | |||
23 | #define cacheconf (*(volatile unsigned int *)PCIMT_CACHECONF) | ||
24 | #define invspace (*(volatile unsigned int *)PCIMT_INVSPACE) | ||
25 | |||
26 | static void __init sni_pcimt_sc_init(void) | ||
27 | { | ||
28 | unsigned int scsiz, sc_size; | ||
29 | |||
30 | scsiz = cacheconf & 7; | ||
31 | if (scsiz == 0) { | ||
32 | printk("Second level cache is deactivated.\n"); | ||
33 | return; | ||
34 | } | ||
35 | if (scsiz >= 6) { | ||
36 | printk("Invalid second level cache size configured, " | ||
37 | "deactivating second level cache.\n"); | ||
38 | cacheconf = 0; | ||
39 | return; | ||
40 | } | ||
41 | |||
42 | sc_size = 128 << scsiz; | ||
43 | printk("%dkb second level cache detected, deactivating.\n", sc_size); | ||
44 | cacheconf = 0; | ||
45 | } | ||
46 | |||
47 | |||
48 | /* | ||
49 | * A bit more gossip about the iron we're running on ... | ||
50 | */ | ||
51 | static inline void sni_pcimt_detect(void) | ||
52 | { | ||
53 | char boardtype[80]; | ||
54 | unsigned char csmsr; | ||
55 | char *p = boardtype; | ||
56 | unsigned int asic; | ||
57 | |||
58 | csmsr = *(volatile unsigned char *)PCIMT_CSMSR; | ||
59 | |||
60 | p += sprintf(p, "%s PCI", (csmsr & 0x80) ? "RM200" : "RM300"); | ||
61 | if ((csmsr & 0x80) == 0) | ||
62 | p += sprintf(p, ", board revision %s", | ||
63 | (csmsr & 0x20) ? "D" : "C"); | ||
64 | asic = csmsr & 0x80; | ||
65 | asic = (csmsr & 0x08) ? asic : !asic; | ||
66 | p += sprintf(p, ", ASIC PCI Rev %s", asic ? "1.0" : "1.1"); | ||
67 | printk("%s.\n", boardtype); | ||
68 | } | ||
69 | |||
70 | #define PORT(_base,_irq) \ | ||
71 | { \ | ||
72 | .iobase = _base, \ | ||
73 | .irq = _irq, \ | ||
74 | .uartclk = 1843200, \ | ||
75 | .iotype = UPIO_PORT, \ | ||
76 | .flags = UPF_BOOT_AUTOCONF, \ | ||
77 | } | ||
78 | |||
79 | static struct plat_serial8250_port pcimt_data[] = { | ||
80 | PORT(0x3f8, 4), | ||
81 | PORT(0x2f8, 3), | ||
82 | { }, | ||
83 | }; | ||
84 | |||
85 | static struct platform_device pcimt_serial8250_device = { | ||
86 | .name = "serial8250", | ||
87 | .id = PLAT8250_DEV_PLATFORM, | ||
88 | .dev = { | ||
89 | .platform_data = pcimt_data, | ||
90 | }, | ||
91 | }; | ||
92 | |||
93 | static struct resource pcimt_cmos_rsrc[] = { | ||
94 | { | ||
95 | .start = 0x70, | ||
96 | .end = 0x71, | ||
97 | .flags = IORESOURCE_IO | ||
98 | }, | ||
99 | { | ||
100 | .start = 8, | ||
101 | .end = 8, | ||
102 | .flags = IORESOURCE_IRQ | ||
103 | } | ||
104 | }; | ||
105 | |||
106 | static struct platform_device pcimt_cmos_device = { | ||
107 | .name = "rtc_cmos", | ||
108 | .num_resources = ARRAY_SIZE(pcimt_cmos_rsrc), | ||
109 | .resource = pcimt_cmos_rsrc | ||
110 | }; | ||
111 | |||
112 | |||
113 | static struct resource sni_io_resource = { | ||
114 | .start = 0x00000000UL, | ||
115 | .end = 0x03bfffffUL, | ||
116 | .name = "PCIMT IO MEM", | ||
117 | .flags = IORESOURCE_IO, | ||
118 | }; | ||
119 | |||
120 | static struct resource pcimt_io_resources[] = { | ||
121 | { | ||
122 | .start = 0x00, | ||
123 | .end = 0x1f, | ||
124 | .name = "dma1", | ||
125 | .flags = IORESOURCE_BUSY | ||
126 | }, { | ||
127 | .start = 0x40, | ||
128 | .end = 0x5f, | ||
129 | .name = "timer", | ||
130 | .flags = IORESOURCE_BUSY | ||
131 | }, { | ||
132 | .start = 0x60, | ||
133 | .end = 0x6f, | ||
134 | .name = "keyboard", | ||
135 | .flags = IORESOURCE_BUSY | ||
136 | }, { | ||
137 | .start = 0x80, | ||
138 | .end = 0x8f, | ||
139 | .name = "dma page reg", | ||
140 | .flags = IORESOURCE_BUSY | ||
141 | }, { | ||
142 | .start = 0xc0, | ||
143 | .end = 0xdf, | ||
144 | .name = "dma2", | ||
145 | .flags = IORESOURCE_BUSY | ||
146 | }, { | ||
147 | .start = 0xcfc, | ||
148 | .end = 0xcff, | ||
149 | .name = "PCI config data", | ||
150 | .flags = IORESOURCE_BUSY | ||
151 | } | ||
152 | }; | ||
153 | |||
154 | static struct resource pcimt_mem_resources[] = { | ||
155 | { | ||
156 | /* | ||
157 | * this region should only be 4 bytes long, | ||
158 | * but it's 16MB on all RM300C I've checked | ||
159 | */ | ||
160 | .start = 0x1a000000, | ||
161 | .end = 0x1affffff, | ||
162 | .name = "PCI INT ACK", | ||
163 | .flags = IORESOURCE_BUSY | ||
164 | } | ||
165 | }; | ||
166 | |||
167 | static struct resource sni_mem_resource = { | ||
168 | .start = 0x18000000UL, | ||
169 | .end = 0x1fbfffffUL, | ||
170 | .name = "PCIMT PCI MEM", | ||
171 | .flags = IORESOURCE_MEM | ||
172 | }; | ||
173 | |||
174 | static void __init sni_pcimt_resource_init(void) | ||
175 | { | ||
176 | int i; | ||
177 | |||
178 | /* request I/O space for devices used on all i[345]86 PCs */ | ||
179 | for (i = 0; i < ARRAY_SIZE(pcimt_io_resources); i++) | ||
180 | request_resource(&sni_io_resource, pcimt_io_resources + i); | ||
181 | /* request MEM space for devices used on all i[345]86 PCs */ | ||
182 | for (i = 0; i < ARRAY_SIZE(pcimt_mem_resources); i++) | ||
183 | request_resource(&sni_mem_resource, pcimt_mem_resources + i); | ||
184 | } | ||
185 | |||
186 | extern struct pci_ops sni_pcimt_ops; | ||
187 | |||
188 | #ifdef CONFIG_PCI | ||
189 | static struct pci_controller sni_controller = { | ||
190 | .pci_ops = &sni_pcimt_ops, | ||
191 | .mem_resource = &sni_mem_resource, | ||
192 | .mem_offset = 0x00000000UL, | ||
193 | .io_resource = &sni_io_resource, | ||
194 | .io_offset = 0x00000000UL, | ||
195 | .io_map_base = SNI_PORT_BASE | ||
196 | }; | ||
197 | #endif | ||
198 | |||
199 | static void enable_pcimt_irq(struct irq_data *d) | ||
200 | { | ||
201 | unsigned int mask = 1 << (d->irq - PCIMT_IRQ_INT2); | ||
202 | |||
203 | *(volatile u8 *) PCIMT_IRQSEL |= mask; | ||
204 | } | ||
205 | |||
206 | void disable_pcimt_irq(struct irq_data *d) | ||
207 | { | ||
208 | unsigned int mask = ~(1 << (d->irq - PCIMT_IRQ_INT2)); | ||
209 | |||
210 | *(volatile u8 *) PCIMT_IRQSEL &= mask; | ||
211 | } | ||
212 | |||
213 | static struct irq_chip pcimt_irq_type = { | ||
214 | .name = "PCIMT", | ||
215 | .irq_mask = disable_pcimt_irq, | ||
216 | .irq_unmask = enable_pcimt_irq, | ||
217 | }; | ||
218 | |||
219 | /* | ||
220 | * hwint0 should deal with MP agent, ASIC PCI, EISA NMI and debug | ||
221 | * button interrupts. Later ... | ||
222 | */ | ||
223 | static void pcimt_hwint0(void) | ||
224 | { | ||
225 | panic("Received int0 but no handler yet ..."); | ||
226 | } | ||
227 | |||
228 | /* | ||
229 | * hwint 1 deals with EISA and SCSI interrupts, | ||
230 | * | ||
231 | * The EISA_INT bit in CSITPEND is high active, all others are low active. | ||
232 | */ | ||
233 | static void pcimt_hwint1(void) | ||
234 | { | ||
235 | u8 pend = *(volatile char *)PCIMT_CSITPEND; | ||
236 | unsigned long flags; | ||
237 | |||
238 | if (pend & IT_EISA) { | ||
239 | int irq; | ||
240 | /* | ||
241 | * Note: ASIC PCI's builtin interrupt acknowledge feature is | ||
242 | * broken. Using it may result in loss of some or all i8259 | ||
243 | * interrupts, so don't use PCIMT_INT_ACKNOWLEDGE ... | ||
244 | */ | ||
245 | irq = i8259_irq(); | ||
246 | if (unlikely(irq < 0)) | ||
247 | return; | ||
248 | |||
249 | do_IRQ(irq); | ||
250 | } | ||
251 | |||
252 | if (!(pend & IT_SCSI)) { | ||
253 | flags = read_c0_status(); | ||
254 | clear_c0_status(ST0_IM); | ||
255 | do_IRQ(PCIMT_IRQ_SCSI); | ||
256 | write_c0_status(flags); | ||
257 | } | ||
258 | } | ||
259 | |||
260 | /* | ||
261 | * hwint 3 should deal with the PCI A - D interrupts, | ||
262 | */ | ||
263 | static void pcimt_hwint3(void) | ||
264 | { | ||
265 | u8 pend = *(volatile char *)PCIMT_CSITPEND; | ||
266 | int irq; | ||
267 | |||
268 | pend &= (IT_INTA | IT_INTB | IT_INTC | IT_INTD); | ||
269 | pend ^= (IT_INTA | IT_INTB | IT_INTC | IT_INTD); | ||
270 | clear_c0_status(IE_IRQ3); | ||
271 | irq = PCIMT_IRQ_INT2 + ffs(pend) - 1; | ||
272 | do_IRQ(irq); | ||
273 | set_c0_status(IE_IRQ3); | ||
274 | } | ||
275 | |||
276 | static void sni_pcimt_hwint(void) | ||
277 | { | ||
278 | u32 pending = read_c0_cause() & read_c0_status(); | ||
279 | |||
280 | if (pending & C_IRQ5) | ||
281 | do_IRQ(MIPS_CPU_IRQ_BASE + 7); | ||
282 | else if (pending & C_IRQ4) | ||
283 | do_IRQ(MIPS_CPU_IRQ_BASE + 6); | ||
284 | else if (pending & C_IRQ3) | ||
285 | pcimt_hwint3(); | ||
286 | else if (pending & C_IRQ1) | ||
287 | pcimt_hwint1(); | ||
288 | else if (pending & C_IRQ0) { | ||
289 | pcimt_hwint0(); | ||
290 | } | ||
291 | } | ||
292 | |||
293 | void __init sni_pcimt_irq_init(void) | ||
294 | { | ||
295 | int i; | ||
296 | |||
297 | *(volatile u8 *) PCIMT_IRQSEL = IT_ETH | IT_EISA; | ||
298 | mips_cpu_irq_init(); | ||
299 | /* Actually we've got more interrupts to handle ... */ | ||
300 | for (i = PCIMT_IRQ_INT2; i <= PCIMT_IRQ_SCSI; i++) | ||
301 | irq_set_chip_and_handler(i, &pcimt_irq_type, handle_level_irq); | ||
302 | sni_hwint = sni_pcimt_hwint; | ||
303 | change_c0_status(ST0_IM, IE_IRQ1|IE_IRQ3); | ||
304 | } | ||
305 | |||
306 | void __init sni_pcimt_init(void) | ||
307 | { | ||
308 | sni_pcimt_detect(); | ||
309 | sni_pcimt_sc_init(); | ||
310 | ioport_resource.end = sni_io_resource.end; | ||
311 | #ifdef CONFIG_PCI | ||
312 | PCIBIOS_MIN_IO = 0x9000; | ||
313 | register_pci_controller(&sni_controller); | ||
314 | #endif | ||
315 | sni_pcimt_resource_init(); | ||
316 | } | ||
317 | |||
318 | static int __init snirm_pcimt_setup_devinit(void) | ||
319 | { | ||
320 | switch (sni_brd_type) { | ||
321 | case SNI_BRD_PCI_MTOWER: | ||
322 | case SNI_BRD_PCI_DESKTOP: | ||
323 | case SNI_BRD_PCI_MTOWER_CPLUS: | ||
324 | platform_device_register(&pcimt_serial8250_device); | ||
325 | platform_device_register(&pcimt_cmos_device); | ||
326 | break; | ||
327 | } | ||
328 | |||
329 | return 0; | ||
330 | } | ||
331 | |||
332 | device_initcall(snirm_pcimt_setup_devinit); | ||
diff --git a/arch/mips/sni/pcit.c b/arch/mips/sni/pcit.c new file mode 100644 index 000000000..b331fe22c --- /dev/null +++ b/arch/mips/sni/pcit.c | |||
@@ -0,0 +1,295 @@ | |||
1 | /* | ||
2 | * PCI Tower specific code | ||
3 | * | ||
4 | * This file is subject to the terms and conditions of the GNU General Public | ||
5 | * License. See the file "COPYING" in the main directory of this archive | ||
6 | * for more details. | ||
7 | * | ||
8 | * Copyright (C) 2006 Thomas Bogendoerfer (tsbogend@alpha.franken.de) | ||
9 | */ | ||
10 | |||
11 | #include <linux/init.h> | ||
12 | #include <linux/interrupt.h> | ||
13 | #include <linux/irq.h> | ||
14 | #include <linux/pci.h> | ||
15 | #include <linux/serial_8250.h> | ||
16 | |||
17 | #include <asm/sni.h> | ||
18 | #include <asm/time.h> | ||
19 | #include <asm/irq_cpu.h> | ||
20 | |||
21 | |||
22 | #define PORT(_base,_irq) \ | ||
23 | { \ | ||
24 | .iobase = _base, \ | ||
25 | .irq = _irq, \ | ||
26 | .uartclk = 1843200, \ | ||
27 | .iotype = UPIO_PORT, \ | ||
28 | .flags = UPF_BOOT_AUTOCONF, \ | ||
29 | } | ||
30 | |||
31 | static struct plat_serial8250_port pcit_data[] = { | ||
32 | PORT(0x3f8, 0), | ||
33 | PORT(0x2f8, 3), | ||
34 | { }, | ||
35 | }; | ||
36 | |||
37 | static struct platform_device pcit_serial8250_device = { | ||
38 | .name = "serial8250", | ||
39 | .id = PLAT8250_DEV_PLATFORM, | ||
40 | .dev = { | ||
41 | .platform_data = pcit_data, | ||
42 | }, | ||
43 | }; | ||
44 | |||
45 | static struct plat_serial8250_port pcit_cplus_data[] = { | ||
46 | PORT(0x3f8, 0), | ||
47 | PORT(0x2f8, 3), | ||
48 | PORT(0x3e8, 4), | ||
49 | PORT(0x2e8, 3), | ||
50 | { }, | ||
51 | }; | ||
52 | |||
53 | static struct platform_device pcit_cplus_serial8250_device = { | ||
54 | .name = "serial8250", | ||
55 | .id = PLAT8250_DEV_PLATFORM, | ||
56 | .dev = { | ||
57 | .platform_data = pcit_cplus_data, | ||
58 | }, | ||
59 | }; | ||
60 | |||
61 | static struct resource pcit_cmos_rsrc[] = { | ||
62 | { | ||
63 | .start = 0x70, | ||
64 | .end = 0x71, | ||
65 | .flags = IORESOURCE_IO | ||
66 | }, | ||
67 | { | ||
68 | .start = 8, | ||
69 | .end = 8, | ||
70 | .flags = IORESOURCE_IRQ | ||
71 | } | ||
72 | }; | ||
73 | |||
74 | static struct platform_device pcit_cmos_device = { | ||
75 | .name = "rtc_cmos", | ||
76 | .num_resources = ARRAY_SIZE(pcit_cmos_rsrc), | ||
77 | .resource = pcit_cmos_rsrc | ||
78 | }; | ||
79 | |||
80 | static struct platform_device pcit_pcspeaker_pdev = { | ||
81 | .name = "pcspkr", | ||
82 | .id = -1, | ||
83 | }; | ||
84 | |||
85 | static struct resource sni_io_resource = { | ||
86 | .start = 0x00000000UL, | ||
87 | .end = 0x03bfffffUL, | ||
88 | .name = "PCIT IO", | ||
89 | .flags = IORESOURCE_IO, | ||
90 | }; | ||
91 | |||
92 | static struct resource pcit_io_resources[] = { | ||
93 | { | ||
94 | .start = 0x00, | ||
95 | .end = 0x1f, | ||
96 | .name = "dma1", | ||
97 | .flags = IORESOURCE_BUSY | ||
98 | }, { | ||
99 | .start = 0x40, | ||
100 | .end = 0x5f, | ||
101 | .name = "timer", | ||
102 | .flags = IORESOURCE_BUSY | ||
103 | }, { | ||
104 | .start = 0x60, | ||
105 | .end = 0x6f, | ||
106 | .name = "keyboard", | ||
107 | .flags = IORESOURCE_BUSY | ||
108 | }, { | ||
109 | .start = 0x80, | ||
110 | .end = 0x8f, | ||
111 | .name = "dma page reg", | ||
112 | .flags = IORESOURCE_BUSY | ||
113 | }, { | ||
114 | .start = 0xc0, | ||
115 | .end = 0xdf, | ||
116 | .name = "dma2", | ||
117 | .flags = IORESOURCE_BUSY | ||
118 | }, { | ||
119 | .start = 0xcf8, | ||
120 | .end = 0xcfb, | ||
121 | .name = "PCI config addr", | ||
122 | .flags = IORESOURCE_BUSY | ||
123 | }, { | ||
124 | .start = 0xcfc, | ||
125 | .end = 0xcff, | ||
126 | .name = "PCI config data", | ||
127 | .flags = IORESOURCE_BUSY | ||
128 | } | ||
129 | }; | ||
130 | |||
131 | static void __init sni_pcit_resource_init(void) | ||
132 | { | ||
133 | int i; | ||
134 | |||
135 | /* request I/O space for devices used on all i[345]86 PCs */ | ||
136 | for (i = 0; i < ARRAY_SIZE(pcit_io_resources); i++) | ||
137 | request_resource(&sni_io_resource, pcit_io_resources + i); | ||
138 | } | ||
139 | |||
140 | |||
141 | extern struct pci_ops sni_pcit_ops; | ||
142 | |||
143 | #ifdef CONFIG_PCI | ||
144 | static struct resource sni_mem_resource = { | ||
145 | .start = 0x18000000UL, | ||
146 | .end = 0x1fbfffffUL, | ||
147 | .name = "PCIT PCI MEM", | ||
148 | .flags = IORESOURCE_MEM | ||
149 | }; | ||
150 | |||
151 | static struct pci_controller sni_pcit_controller = { | ||
152 | .pci_ops = &sni_pcit_ops, | ||
153 | .mem_resource = &sni_mem_resource, | ||
154 | .mem_offset = 0x00000000UL, | ||
155 | .io_resource = &sni_io_resource, | ||
156 | .io_offset = 0x00000000UL, | ||
157 | .io_map_base = SNI_PORT_BASE | ||
158 | }; | ||
159 | #endif /* CONFIG_PCI */ | ||
160 | |||
161 | static void enable_pcit_irq(struct irq_data *d) | ||
162 | { | ||
163 | u32 mask = 1 << (d->irq - SNI_PCIT_INT_START + 24); | ||
164 | |||
165 | *(volatile u32 *)SNI_PCIT_INT_REG |= mask; | ||
166 | } | ||
167 | |||
168 | void disable_pcit_irq(struct irq_data *d) | ||
169 | { | ||
170 | u32 mask = 1 << (d->irq - SNI_PCIT_INT_START + 24); | ||
171 | |||
172 | *(volatile u32 *)SNI_PCIT_INT_REG &= ~mask; | ||
173 | } | ||
174 | |||
175 | static struct irq_chip pcit_irq_type = { | ||
176 | .name = "PCIT", | ||
177 | .irq_mask = disable_pcit_irq, | ||
178 | .irq_unmask = enable_pcit_irq, | ||
179 | }; | ||
180 | |||
181 | static void pcit_hwint1(void) | ||
182 | { | ||
183 | u32 pending = *(volatile u32 *)SNI_PCIT_INT_REG; | ||
184 | int irq; | ||
185 | |||
186 | clear_c0_status(IE_IRQ1); | ||
187 | irq = ffs((pending >> 16) & 0x7f); | ||
188 | |||
189 | if (likely(irq > 0)) | ||
190 | do_IRQ(irq + SNI_PCIT_INT_START - 1); | ||
191 | set_c0_status(IE_IRQ1); | ||
192 | } | ||
193 | |||
194 | static void pcit_hwint0(void) | ||
195 | { | ||
196 | u32 pending = *(volatile u32 *)SNI_PCIT_INT_REG; | ||
197 | int irq; | ||
198 | |||
199 | clear_c0_status(IE_IRQ0); | ||
200 | irq = ffs((pending >> 16) & 0x3f); | ||
201 | |||
202 | if (likely(irq > 0)) | ||
203 | do_IRQ(irq + SNI_PCIT_INT_START - 1); | ||
204 | set_c0_status(IE_IRQ0); | ||
205 | } | ||
206 | |||
207 | static void sni_pcit_hwint(void) | ||
208 | { | ||
209 | u32 pending = read_c0_cause() & read_c0_status(); | ||
210 | |||
211 | if (pending & C_IRQ1) | ||
212 | pcit_hwint1(); | ||
213 | else if (pending & C_IRQ2) | ||
214 | do_IRQ(MIPS_CPU_IRQ_BASE + 4); | ||
215 | else if (pending & C_IRQ3) | ||
216 | do_IRQ(MIPS_CPU_IRQ_BASE + 5); | ||
217 | else if (pending & C_IRQ5) | ||
218 | do_IRQ(MIPS_CPU_IRQ_BASE + 7); | ||
219 | } | ||
220 | |||
221 | static void sni_pcit_hwint_cplus(void) | ||
222 | { | ||
223 | u32 pending = read_c0_cause() & read_c0_status(); | ||
224 | |||
225 | if (pending & C_IRQ0) | ||
226 | pcit_hwint0(); | ||
227 | else if (pending & C_IRQ1) | ||
228 | do_IRQ(MIPS_CPU_IRQ_BASE + 3); | ||
229 | else if (pending & C_IRQ2) | ||
230 | do_IRQ(MIPS_CPU_IRQ_BASE + 4); | ||
231 | else if (pending & C_IRQ3) | ||
232 | do_IRQ(MIPS_CPU_IRQ_BASE + 5); | ||
233 | else if (pending & C_IRQ5) | ||
234 | do_IRQ(MIPS_CPU_IRQ_BASE + 7); | ||
235 | } | ||
236 | |||
237 | void __init sni_pcit_irq_init(void) | ||
238 | { | ||
239 | int i; | ||
240 | |||
241 | mips_cpu_irq_init(); | ||
242 | for (i = SNI_PCIT_INT_START; i <= SNI_PCIT_INT_END; i++) | ||
243 | irq_set_chip_and_handler(i, &pcit_irq_type, handle_level_irq); | ||
244 | *(volatile u32 *)SNI_PCIT_INT_REG = 0; | ||
245 | sni_hwint = sni_pcit_hwint; | ||
246 | change_c0_status(ST0_IM, IE_IRQ1); | ||
247 | if (request_irq(SNI_PCIT_INT_START + 6, sni_isa_irq_handler, 0, "ISA", | ||
248 | NULL)) | ||
249 | pr_err("Failed to register ISA interrupt\n"); | ||
250 | } | ||
251 | |||
252 | void __init sni_pcit_cplus_irq_init(void) | ||
253 | { | ||
254 | int i; | ||
255 | |||
256 | mips_cpu_irq_init(); | ||
257 | for (i = SNI_PCIT_INT_START; i <= SNI_PCIT_INT_END; i++) | ||
258 | irq_set_chip_and_handler(i, &pcit_irq_type, handle_level_irq); | ||
259 | *(volatile u32 *)SNI_PCIT_INT_REG = 0x40000000; | ||
260 | sni_hwint = sni_pcit_hwint_cplus; | ||
261 | change_c0_status(ST0_IM, IE_IRQ0); | ||
262 | if (request_irq(MIPS_CPU_IRQ_BASE + 3, sni_isa_irq_handler, 0, "ISA", | ||
263 | NULL)) | ||
264 | pr_err("Failed to register ISA interrupt\n"); | ||
265 | } | ||
266 | |||
267 | void __init sni_pcit_init(void) | ||
268 | { | ||
269 | ioport_resource.end = sni_io_resource.end; | ||
270 | #ifdef CONFIG_PCI | ||
271 | PCIBIOS_MIN_IO = 0x9000; | ||
272 | register_pci_controller(&sni_pcit_controller); | ||
273 | #endif | ||
274 | sni_pcit_resource_init(); | ||
275 | } | ||
276 | |||
277 | static int __init snirm_pcit_setup_devinit(void) | ||
278 | { | ||
279 | switch (sni_brd_type) { | ||
280 | case SNI_BRD_PCI_TOWER: | ||
281 | platform_device_register(&pcit_serial8250_device); | ||
282 | platform_device_register(&pcit_cmos_device); | ||
283 | platform_device_register(&pcit_pcspeaker_pdev); | ||
284 | break; | ||
285 | |||
286 | case SNI_BRD_PCI_TOWER_CPLUS: | ||
287 | platform_device_register(&pcit_cplus_serial8250_device); | ||
288 | platform_device_register(&pcit_cmos_device); | ||
289 | platform_device_register(&pcit_pcspeaker_pdev); | ||
290 | break; | ||
291 | } | ||
292 | return 0; | ||
293 | } | ||
294 | |||
295 | device_initcall(snirm_pcit_setup_devinit); | ||
diff --git a/arch/mips/sni/reset.c b/arch/mips/sni/reset.c new file mode 100644 index 000000000..66f963d8d --- /dev/null +++ b/arch/mips/sni/reset.c | |||
@@ -0,0 +1,48 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * linux/arch/mips/sni/process.c | ||
4 | * | ||
5 | * Reset a SNI machine. | ||
6 | */ | ||
7 | #include <linux/delay.h> | ||
8 | |||
9 | #include <asm/io.h> | ||
10 | #include <asm/reboot.h> | ||
11 | #include <asm/sni.h> | ||
12 | |||
13 | /* | ||
14 | * This routine reboots the machine by asking the keyboard | ||
15 | * controller to pulse the reset-line low. We try that for a while, | ||
16 | * and if it doesn't work, we do some other stupid things. | ||
17 | */ | ||
18 | static inline void kb_wait(void) | ||
19 | { | ||
20 | int i; | ||
21 | |||
22 | for (i = 0; i < 0x10000; i++) | ||
23 | if ((inb_p(0x64) & 0x02) == 0) | ||
24 | break; | ||
25 | } | ||
26 | |||
27 | /* XXX This ends up at the ARC firmware prompt ... */ | ||
28 | void sni_machine_restart(char *command) | ||
29 | { | ||
30 | int i; | ||
31 | |||
32 | /* This does a normal via the keyboard controller like a PC. | ||
33 | We can do that easier ... */ | ||
34 | local_irq_disable(); | ||
35 | for (;;) { | ||
36 | for (i = 0; i < 100; i++) { | ||
37 | kb_wait(); | ||
38 | udelay(50); | ||
39 | outb_p(0xfe, 0x64); /* pulse reset low */ | ||
40 | udelay(50); | ||
41 | } | ||
42 | } | ||
43 | } | ||
44 | |||
45 | void sni_machine_power_off(void) | ||
46 | { | ||
47 | *(volatile unsigned char *)PCIMT_CSWCSM = 0xfd; | ||
48 | } | ||
diff --git a/arch/mips/sni/rm200.c b/arch/mips/sni/rm200.c new file mode 100644 index 000000000..d84744ca8 --- /dev/null +++ b/arch/mips/sni/rm200.c | |||
@@ -0,0 +1,485 @@ | |||
1 | /* | ||
2 | * RM200 specific code | ||
3 | * | ||
4 | * This file is subject to the terms and conditions of the GNU General Public | ||
5 | * License. See the file "COPYING" in the main directory of this archive | ||
6 | * for more details. | ||
7 | * | ||
8 | * Copyright (C) 2006,2007 Thomas Bogendoerfer (tsbogend@alpha.franken.de) | ||
9 | * | ||
10 | * i8259 parts ripped out of arch/mips/kernel/i8259.c | ||
11 | */ | ||
12 | |||
13 | #include <linux/delay.h> | ||
14 | #include <linux/init.h> | ||
15 | #include <linux/interrupt.h> | ||
16 | #include <linux/irq.h> | ||
17 | #include <linux/platform_device.h> | ||
18 | #include <linux/serial_8250.h> | ||
19 | #include <linux/io.h> | ||
20 | |||
21 | #include <asm/sni.h> | ||
22 | #include <asm/time.h> | ||
23 | #include <asm/irq_cpu.h> | ||
24 | |||
25 | #define RM200_I8259A_IRQ_BASE 32 | ||
26 | |||
27 | #define MEMPORT(_base,_irq) \ | ||
28 | { \ | ||
29 | .mapbase = _base, \ | ||
30 | .irq = _irq, \ | ||
31 | .uartclk = 1843200, \ | ||
32 | .iotype = UPIO_MEM, \ | ||
33 | .flags = UPF_BOOT_AUTOCONF|UPF_IOREMAP, \ | ||
34 | } | ||
35 | |||
36 | static struct plat_serial8250_port rm200_data[] = { | ||
37 | MEMPORT(0x160003f8, RM200_I8259A_IRQ_BASE + 4), | ||
38 | MEMPORT(0x160002f8, RM200_I8259A_IRQ_BASE + 3), | ||
39 | { }, | ||
40 | }; | ||
41 | |||
42 | static struct platform_device rm200_serial8250_device = { | ||
43 | .name = "serial8250", | ||
44 | .id = PLAT8250_DEV_PLATFORM, | ||
45 | .dev = { | ||
46 | .platform_data = rm200_data, | ||
47 | }, | ||
48 | }; | ||
49 | |||
50 | static struct resource rm200_ds1216_rsrc[] = { | ||
51 | { | ||
52 | .start = 0x1cd41ffc, | ||
53 | .end = 0x1cd41fff, | ||
54 | .flags = IORESOURCE_MEM | ||
55 | } | ||
56 | }; | ||
57 | |||
58 | static struct platform_device rm200_ds1216_device = { | ||
59 | .name = "rtc-ds1216", | ||
60 | .num_resources = ARRAY_SIZE(rm200_ds1216_rsrc), | ||
61 | .resource = rm200_ds1216_rsrc | ||
62 | }; | ||
63 | |||
64 | static struct resource snirm_82596_rm200_rsrc[] = { | ||
65 | { | ||
66 | .start = 0x18000000, | ||
67 | .end = 0x180fffff, | ||
68 | .flags = IORESOURCE_MEM | ||
69 | }, | ||
70 | { | ||
71 | .start = 0x1b000000, | ||
72 | .end = 0x1b000004, | ||
73 | .flags = IORESOURCE_MEM | ||
74 | }, | ||
75 | { | ||
76 | .start = 0x1ff00000, | ||
77 | .end = 0x1ff00020, | ||
78 | .flags = IORESOURCE_MEM | ||
79 | }, | ||
80 | { | ||
81 | .start = 27, | ||
82 | .end = 27, | ||
83 | .flags = IORESOURCE_IRQ | ||
84 | }, | ||
85 | { | ||
86 | .flags = 0x00 | ||
87 | } | ||
88 | }; | ||
89 | |||
90 | static struct platform_device snirm_82596_rm200_pdev = { | ||
91 | .name = "snirm_82596", | ||
92 | .num_resources = ARRAY_SIZE(snirm_82596_rm200_rsrc), | ||
93 | .resource = snirm_82596_rm200_rsrc | ||
94 | }; | ||
95 | |||
96 | static struct resource snirm_53c710_rm200_rsrc[] = { | ||
97 | { | ||
98 | .start = 0x19000000, | ||
99 | .end = 0x190fffff, | ||
100 | .flags = IORESOURCE_MEM | ||
101 | }, | ||
102 | { | ||
103 | .start = 26, | ||
104 | .end = 26, | ||
105 | .flags = IORESOURCE_IRQ | ||
106 | } | ||
107 | }; | ||
108 | |||
109 | static struct platform_device snirm_53c710_rm200_pdev = { | ||
110 | .name = "snirm_53c710", | ||
111 | .num_resources = ARRAY_SIZE(snirm_53c710_rm200_rsrc), | ||
112 | .resource = snirm_53c710_rm200_rsrc | ||
113 | }; | ||
114 | |||
115 | static int __init snirm_setup_devinit(void) | ||
116 | { | ||
117 | if (sni_brd_type == SNI_BRD_RM200) { | ||
118 | platform_device_register(&rm200_serial8250_device); | ||
119 | platform_device_register(&rm200_ds1216_device); | ||
120 | platform_device_register(&snirm_82596_rm200_pdev); | ||
121 | platform_device_register(&snirm_53c710_rm200_pdev); | ||
122 | sni_eisa_root_init(); | ||
123 | } | ||
124 | return 0; | ||
125 | } | ||
126 | |||
127 | device_initcall(snirm_setup_devinit); | ||
128 | |||
129 | /* | ||
130 | * RM200 has an ISA and an EISA bus. The iSA bus is only used | ||
131 | * for onboard devices and also has twi i8259 PICs. Since these | ||
132 | * PICs are no accessible via inb/outb the following code uses | ||
133 | * readb/writeb to access them | ||
134 | */ | ||
135 | |||
136 | static DEFINE_RAW_SPINLOCK(sni_rm200_i8259A_lock); | ||
137 | #define PIC_CMD 0x00 | ||
138 | #define PIC_IMR 0x01 | ||
139 | #define PIC_ISR PIC_CMD | ||
140 | #define PIC_POLL PIC_ISR | ||
141 | #define PIC_OCW3 PIC_ISR | ||
142 | |||
143 | /* i8259A PIC related value */ | ||
144 | #define PIC_CASCADE_IR 2 | ||
145 | #define MASTER_ICW4_DEFAULT 0x01 | ||
146 | #define SLAVE_ICW4_DEFAULT 0x01 | ||
147 | |||
148 | /* | ||
149 | * This contains the irq mask for both 8259A irq controllers, | ||
150 | */ | ||
151 | static unsigned int rm200_cached_irq_mask = 0xffff; | ||
152 | static __iomem u8 *rm200_pic_master; | ||
153 | static __iomem u8 *rm200_pic_slave; | ||
154 | |||
155 | #define cached_master_mask (rm200_cached_irq_mask) | ||
156 | #define cached_slave_mask (rm200_cached_irq_mask >> 8) | ||
157 | |||
158 | static void sni_rm200_disable_8259A_irq(struct irq_data *d) | ||
159 | { | ||
160 | unsigned int mask, irq = d->irq - RM200_I8259A_IRQ_BASE; | ||
161 | unsigned long flags; | ||
162 | |||
163 | mask = 1 << irq; | ||
164 | raw_spin_lock_irqsave(&sni_rm200_i8259A_lock, flags); | ||
165 | rm200_cached_irq_mask |= mask; | ||
166 | if (irq & 8) | ||
167 | writeb(cached_slave_mask, rm200_pic_slave + PIC_IMR); | ||
168 | else | ||
169 | writeb(cached_master_mask, rm200_pic_master + PIC_IMR); | ||
170 | raw_spin_unlock_irqrestore(&sni_rm200_i8259A_lock, flags); | ||
171 | } | ||
172 | |||
173 | static void sni_rm200_enable_8259A_irq(struct irq_data *d) | ||
174 | { | ||
175 | unsigned int mask, irq = d->irq - RM200_I8259A_IRQ_BASE; | ||
176 | unsigned long flags; | ||
177 | |||
178 | mask = ~(1 << irq); | ||
179 | raw_spin_lock_irqsave(&sni_rm200_i8259A_lock, flags); | ||
180 | rm200_cached_irq_mask &= mask; | ||
181 | if (irq & 8) | ||
182 | writeb(cached_slave_mask, rm200_pic_slave + PIC_IMR); | ||
183 | else | ||
184 | writeb(cached_master_mask, rm200_pic_master + PIC_IMR); | ||
185 | raw_spin_unlock_irqrestore(&sni_rm200_i8259A_lock, flags); | ||
186 | } | ||
187 | |||
188 | static inline int sni_rm200_i8259A_irq_real(unsigned int irq) | ||
189 | { | ||
190 | int value; | ||
191 | int irqmask = 1 << irq; | ||
192 | |||
193 | if (irq < 8) { | ||
194 | writeb(0x0B, rm200_pic_master + PIC_CMD); | ||
195 | value = readb(rm200_pic_master + PIC_CMD) & irqmask; | ||
196 | writeb(0x0A, rm200_pic_master + PIC_CMD); | ||
197 | return value; | ||
198 | } | ||
199 | writeb(0x0B, rm200_pic_slave + PIC_CMD); /* ISR register */ | ||
200 | value = readb(rm200_pic_slave + PIC_CMD) & (irqmask >> 8); | ||
201 | writeb(0x0A, rm200_pic_slave + PIC_CMD); | ||
202 | return value; | ||
203 | } | ||
204 | |||
205 | /* | ||
206 | * Careful! The 8259A is a fragile beast, it pretty | ||
207 | * much _has_ to be done exactly like this (mask it | ||
208 | * first, _then_ send the EOI, and the order of EOI | ||
209 | * to the two 8259s is important! | ||
210 | */ | ||
211 | void sni_rm200_mask_and_ack_8259A(struct irq_data *d) | ||
212 | { | ||
213 | unsigned int irqmask, irq = d->irq - RM200_I8259A_IRQ_BASE; | ||
214 | unsigned long flags; | ||
215 | |||
216 | irqmask = 1 << irq; | ||
217 | raw_spin_lock_irqsave(&sni_rm200_i8259A_lock, flags); | ||
218 | /* | ||
219 | * Lightweight spurious IRQ detection. We do not want | ||
220 | * to overdo spurious IRQ handling - it's usually a sign | ||
221 | * of hardware problems, so we only do the checks we can | ||
222 | * do without slowing down good hardware unnecessarily. | ||
223 | * | ||
224 | * Note that IRQ7 and IRQ15 (the two spurious IRQs | ||
225 | * usually resulting from the 8259A-1|2 PICs) occur | ||
226 | * even if the IRQ is masked in the 8259A. Thus we | ||
227 | * can check spurious 8259A IRQs without doing the | ||
228 | * quite slow i8259A_irq_real() call for every IRQ. | ||
229 | * This does not cover 100% of spurious interrupts, | ||
230 | * but should be enough to warn the user that there | ||
231 | * is something bad going on ... | ||
232 | */ | ||
233 | if (rm200_cached_irq_mask & irqmask) | ||
234 | goto spurious_8259A_irq; | ||
235 | rm200_cached_irq_mask |= irqmask; | ||
236 | |||
237 | handle_real_irq: | ||
238 | if (irq & 8) { | ||
239 | readb(rm200_pic_slave + PIC_IMR); | ||
240 | writeb(cached_slave_mask, rm200_pic_slave + PIC_IMR); | ||
241 | writeb(0x60+(irq & 7), rm200_pic_slave + PIC_CMD); | ||
242 | writeb(0x60+PIC_CASCADE_IR, rm200_pic_master + PIC_CMD); | ||
243 | } else { | ||
244 | readb(rm200_pic_master + PIC_IMR); | ||
245 | writeb(cached_master_mask, rm200_pic_master + PIC_IMR); | ||
246 | writeb(0x60+irq, rm200_pic_master + PIC_CMD); | ||
247 | } | ||
248 | raw_spin_unlock_irqrestore(&sni_rm200_i8259A_lock, flags); | ||
249 | return; | ||
250 | |||
251 | spurious_8259A_irq: | ||
252 | /* | ||
253 | * this is the slow path - should happen rarely. | ||
254 | */ | ||
255 | if (sni_rm200_i8259A_irq_real(irq)) | ||
256 | /* | ||
257 | * oops, the IRQ _is_ in service according to the | ||
258 | * 8259A - not spurious, go handle it. | ||
259 | */ | ||
260 | goto handle_real_irq; | ||
261 | |||
262 | { | ||
263 | static int spurious_irq_mask; | ||
264 | /* | ||
265 | * At this point we can be sure the IRQ is spurious, | ||
266 | * let's ACK and report it. [once per IRQ] | ||
267 | */ | ||
268 | if (!(spurious_irq_mask & irqmask)) { | ||
269 | printk(KERN_DEBUG | ||
270 | "spurious RM200 8259A interrupt: IRQ%d.\n", irq); | ||
271 | spurious_irq_mask |= irqmask; | ||
272 | } | ||
273 | atomic_inc(&irq_err_count); | ||
274 | /* | ||
275 | * Theoretically we do not have to handle this IRQ, | ||
276 | * but in Linux this does not cause problems and is | ||
277 | * simpler for us. | ||
278 | */ | ||
279 | goto handle_real_irq; | ||
280 | } | ||
281 | } | ||
282 | |||
283 | static struct irq_chip sni_rm200_i8259A_chip = { | ||
284 | .name = "RM200-XT-PIC", | ||
285 | .irq_mask = sni_rm200_disable_8259A_irq, | ||
286 | .irq_unmask = sni_rm200_enable_8259A_irq, | ||
287 | .irq_mask_ack = sni_rm200_mask_and_ack_8259A, | ||
288 | }; | ||
289 | |||
290 | /* | ||
291 | * Do the traditional i8259 interrupt polling thing. This is for the few | ||
292 | * cases where no better interrupt acknowledge method is available and we | ||
293 | * absolutely must touch the i8259. | ||
294 | */ | ||
295 | static inline int sni_rm200_i8259_irq(void) | ||
296 | { | ||
297 | int irq; | ||
298 | |||
299 | raw_spin_lock(&sni_rm200_i8259A_lock); | ||
300 | |||
301 | /* Perform an interrupt acknowledge cycle on controller 1. */ | ||
302 | writeb(0x0C, rm200_pic_master + PIC_CMD); /* prepare for poll */ | ||
303 | irq = readb(rm200_pic_master + PIC_CMD) & 7; | ||
304 | if (irq == PIC_CASCADE_IR) { | ||
305 | /* | ||
306 | * Interrupt is cascaded so perform interrupt | ||
307 | * acknowledge on controller 2. | ||
308 | */ | ||
309 | writeb(0x0C, rm200_pic_slave + PIC_CMD); /* prepare for poll */ | ||
310 | irq = (readb(rm200_pic_slave + PIC_CMD) & 7) + 8; | ||
311 | } | ||
312 | |||
313 | if (unlikely(irq == 7)) { | ||
314 | /* | ||
315 | * This may be a spurious interrupt. | ||
316 | * | ||
317 | * Read the interrupt status register (ISR). If the most | ||
318 | * significant bit is not set then there is no valid | ||
319 | * interrupt. | ||
320 | */ | ||
321 | writeb(0x0B, rm200_pic_master + PIC_ISR); /* ISR register */ | ||
322 | if (~readb(rm200_pic_master + PIC_ISR) & 0x80) | ||
323 | irq = -1; | ||
324 | } | ||
325 | |||
326 | raw_spin_unlock(&sni_rm200_i8259A_lock); | ||
327 | |||
328 | return likely(irq >= 0) ? irq + RM200_I8259A_IRQ_BASE : irq; | ||
329 | } | ||
330 | |||
331 | void sni_rm200_init_8259A(void) | ||
332 | { | ||
333 | unsigned long flags; | ||
334 | |||
335 | raw_spin_lock_irqsave(&sni_rm200_i8259A_lock, flags); | ||
336 | |||
337 | writeb(0xff, rm200_pic_master + PIC_IMR); | ||
338 | writeb(0xff, rm200_pic_slave + PIC_IMR); | ||
339 | |||
340 | writeb(0x11, rm200_pic_master + PIC_CMD); | ||
341 | writeb(0, rm200_pic_master + PIC_IMR); | ||
342 | writeb(1U << PIC_CASCADE_IR, rm200_pic_master + PIC_IMR); | ||
343 | writeb(MASTER_ICW4_DEFAULT, rm200_pic_master + PIC_IMR); | ||
344 | writeb(0x11, rm200_pic_slave + PIC_CMD); | ||
345 | writeb(8, rm200_pic_slave + PIC_IMR); | ||
346 | writeb(PIC_CASCADE_IR, rm200_pic_slave + PIC_IMR); | ||
347 | writeb(SLAVE_ICW4_DEFAULT, rm200_pic_slave + PIC_IMR); | ||
348 | udelay(100); /* wait for 8259A to initialize */ | ||
349 | |||
350 | writeb(cached_master_mask, rm200_pic_master + PIC_IMR); | ||
351 | writeb(cached_slave_mask, rm200_pic_slave + PIC_IMR); | ||
352 | |||
353 | raw_spin_unlock_irqrestore(&sni_rm200_i8259A_lock, flags); | ||
354 | } | ||
355 | |||
356 | /* | ||
357 | * IRQ2 is cascade interrupt to second interrupt controller | ||
358 | */ | ||
359 | |||
360 | static struct resource sni_rm200_pic1_resource = { | ||
361 | .name = "onboard ISA pic1", | ||
362 | .start = 0x16000020, | ||
363 | .end = 0x16000023, | ||
364 | .flags = IORESOURCE_BUSY | ||
365 | }; | ||
366 | |||
367 | static struct resource sni_rm200_pic2_resource = { | ||
368 | .name = "onboard ISA pic2", | ||
369 | .start = 0x160000a0, | ||
370 | .end = 0x160000a3, | ||
371 | .flags = IORESOURCE_BUSY | ||
372 | }; | ||
373 | |||
374 | /* ISA irq handler */ | ||
375 | static irqreturn_t sni_rm200_i8259A_irq_handler(int dummy, void *p) | ||
376 | { | ||
377 | int irq; | ||
378 | |||
379 | irq = sni_rm200_i8259_irq(); | ||
380 | if (unlikely(irq < 0)) | ||
381 | return IRQ_NONE; | ||
382 | |||
383 | do_IRQ(irq); | ||
384 | return IRQ_HANDLED; | ||
385 | } | ||
386 | |||
387 | void __init sni_rm200_i8259_irqs(void) | ||
388 | { | ||
389 | int i; | ||
390 | |||
391 | rm200_pic_master = ioremap(0x16000020, 4); | ||
392 | if (!rm200_pic_master) | ||
393 | return; | ||
394 | rm200_pic_slave = ioremap(0x160000a0, 4); | ||
395 | if (!rm200_pic_slave) { | ||
396 | iounmap(rm200_pic_master); | ||
397 | return; | ||
398 | } | ||
399 | |||
400 | insert_resource(&iomem_resource, &sni_rm200_pic1_resource); | ||
401 | insert_resource(&iomem_resource, &sni_rm200_pic2_resource); | ||
402 | |||
403 | sni_rm200_init_8259A(); | ||
404 | |||
405 | for (i = RM200_I8259A_IRQ_BASE; i < RM200_I8259A_IRQ_BASE + 16; i++) | ||
406 | irq_set_chip_and_handler(i, &sni_rm200_i8259A_chip, | ||
407 | handle_level_irq); | ||
408 | |||
409 | if (request_irq(RM200_I8259A_IRQ_BASE + PIC_CASCADE_IR, no_action, | ||
410 | IRQF_NO_THREAD, "cascade", NULL)) | ||
411 | pr_err("Failed to register cascade interrupt\n"); | ||
412 | } | ||
413 | |||
414 | |||
415 | #define SNI_RM200_INT_STAT_REG CKSEG1ADDR(0xbc000000) | ||
416 | #define SNI_RM200_INT_ENA_REG CKSEG1ADDR(0xbc080000) | ||
417 | |||
418 | #define SNI_RM200_INT_START 24 | ||
419 | #define SNI_RM200_INT_END 28 | ||
420 | |||
421 | static void enable_rm200_irq(struct irq_data *d) | ||
422 | { | ||
423 | unsigned int mask = 1 << (d->irq - SNI_RM200_INT_START); | ||
424 | |||
425 | *(volatile u8 *)SNI_RM200_INT_ENA_REG &= ~mask; | ||
426 | } | ||
427 | |||
428 | void disable_rm200_irq(struct irq_data *d) | ||
429 | { | ||
430 | unsigned int mask = 1 << (d->irq - SNI_RM200_INT_START); | ||
431 | |||
432 | *(volatile u8 *)SNI_RM200_INT_ENA_REG |= mask; | ||
433 | } | ||
434 | |||
435 | static struct irq_chip rm200_irq_type = { | ||
436 | .name = "RM200", | ||
437 | .irq_mask = disable_rm200_irq, | ||
438 | .irq_unmask = enable_rm200_irq, | ||
439 | }; | ||
440 | |||
441 | static void sni_rm200_hwint(void) | ||
442 | { | ||
443 | u32 pending = read_c0_cause() & read_c0_status(); | ||
444 | u8 mask; | ||
445 | u8 stat; | ||
446 | int irq; | ||
447 | |||
448 | if (pending & C_IRQ5) | ||
449 | do_IRQ(MIPS_CPU_IRQ_BASE + 7); | ||
450 | else if (pending & C_IRQ0) { | ||
451 | clear_c0_status(IE_IRQ0); | ||
452 | mask = *(volatile u8 *)SNI_RM200_INT_ENA_REG ^ 0x1f; | ||
453 | stat = *(volatile u8 *)SNI_RM200_INT_STAT_REG ^ 0x14; | ||
454 | irq = ffs(stat & mask & 0x1f); | ||
455 | |||
456 | if (likely(irq > 0)) | ||
457 | do_IRQ(irq + SNI_RM200_INT_START - 1); | ||
458 | set_c0_status(IE_IRQ0); | ||
459 | } | ||
460 | } | ||
461 | |||
462 | void __init sni_rm200_irq_init(void) | ||
463 | { | ||
464 | int i; | ||
465 | |||
466 | * (volatile u8 *)SNI_RM200_INT_ENA_REG = 0x1f; | ||
467 | |||
468 | sni_rm200_i8259_irqs(); | ||
469 | mips_cpu_irq_init(); | ||
470 | /* Actually we've got more interrupts to handle ... */ | ||
471 | for (i = SNI_RM200_INT_START; i <= SNI_RM200_INT_END; i++) | ||
472 | irq_set_chip_and_handler(i, &rm200_irq_type, handle_level_irq); | ||
473 | sni_hwint = sni_rm200_hwint; | ||
474 | change_c0_status(ST0_IM, IE_IRQ0); | ||
475 | if (request_irq(SNI_RM200_INT_START + 0, sni_rm200_i8259A_irq_handler, | ||
476 | 0, "onboard ISA", NULL)) | ||
477 | pr_err("Failed to register onboard ISA interrupt\n"); | ||
478 | if (request_irq(SNI_RM200_INT_START + 1, sni_isa_irq_handler, 0, "ISA", | ||
479 | NULL)) | ||
480 | pr_err("Failed to register ISA interrupt\n"); | ||
481 | } | ||
482 | |||
483 | void __init sni_rm200_init(void) | ||
484 | { | ||
485 | } | ||
diff --git a/arch/mips/sni/setup.c b/arch/mips/sni/setup.c new file mode 100644 index 000000000..efad85c8c --- /dev/null +++ b/arch/mips/sni/setup.c | |||
@@ -0,0 +1,263 @@ | |||
1 | /* | ||
2 | * Setup pointers to hardware-dependent routines. | ||
3 | * | ||
4 | * This file is subject to the terms and conditions of the GNU General Public | ||
5 | * License. See the file "COPYING" in the main directory of this archive | ||
6 | * for more details. | ||
7 | * | ||
8 | * Copyright (C) 1996, 97, 98, 2000, 03, 04, 06 Ralf Baechle (ralf@linux-mips.org) | ||
9 | * Copyright (C) 2006,2007 Thomas Bogendoerfer (tsbogend@alpha.franken.de) | ||
10 | */ | ||
11 | #include <linux/eisa.h> | ||
12 | #include <linux/init.h> | ||
13 | #include <linux/export.h> | ||
14 | #include <linux/console.h> | ||
15 | #include <linux/fb.h> | ||
16 | #include <linux/screen_info.h> | ||
17 | |||
18 | #ifdef CONFIG_FW_ARC | ||
19 | #include <asm/fw/arc/types.h> | ||
20 | #include <asm/sgialib.h> | ||
21 | #endif | ||
22 | |||
23 | #ifdef CONFIG_FW_SNIPROM | ||
24 | #include <asm/mipsprom.h> | ||
25 | #endif | ||
26 | |||
27 | #include <asm/bootinfo.h> | ||
28 | #include <asm/cpu.h> | ||
29 | #include <asm/io.h> | ||
30 | #include <asm/reboot.h> | ||
31 | #include <asm/sni.h> | ||
32 | |||
33 | unsigned int sni_brd_type; | ||
34 | EXPORT_SYMBOL(sni_brd_type); | ||
35 | |||
36 | extern void sni_machine_restart(char *command); | ||
37 | extern void sni_machine_power_off(void); | ||
38 | |||
39 | static void __init sni_display_setup(void) | ||
40 | { | ||
41 | #if defined(CONFIG_VT) && defined(CONFIG_VGA_CONSOLE) && defined(CONFIG_FW_ARC) | ||
42 | struct screen_info *si = &screen_info; | ||
43 | DISPLAY_STATUS *di; | ||
44 | |||
45 | di = ArcGetDisplayStatus(1); | ||
46 | |||
47 | if (di) { | ||
48 | si->orig_x = di->CursorXPosition; | ||
49 | si->orig_y = di->CursorYPosition; | ||
50 | si->orig_video_cols = di->CursorMaxXPosition; | ||
51 | si->orig_video_lines = di->CursorMaxYPosition; | ||
52 | si->orig_video_isVGA = VIDEO_TYPE_VGAC; | ||
53 | si->orig_video_points = 16; | ||
54 | } | ||
55 | #endif | ||
56 | } | ||
57 | |||
58 | static void __init sni_console_setup(void) | ||
59 | { | ||
60 | #ifndef CONFIG_FW_ARC | ||
61 | char *ctype; | ||
62 | char *cdev; | ||
63 | char *baud; | ||
64 | int port; | ||
65 | static char options[8] __initdata; | ||
66 | |||
67 | cdev = prom_getenv("console_dev"); | ||
68 | if (strncmp(cdev, "tty", 3) == 0) { | ||
69 | ctype = prom_getenv("console"); | ||
70 | switch (*ctype) { | ||
71 | default: | ||
72 | case 'l': | ||
73 | port = 0; | ||
74 | baud = prom_getenv("lbaud"); | ||
75 | break; | ||
76 | case 'r': | ||
77 | port = 1; | ||
78 | baud = prom_getenv("rbaud"); | ||
79 | break; | ||
80 | } | ||
81 | if (baud) | ||
82 | strcpy(options, baud); | ||
83 | if (strncmp(cdev, "tty552", 6) == 0) | ||
84 | add_preferred_console("ttyS", port, | ||
85 | baud ? options : NULL); | ||
86 | else | ||
87 | add_preferred_console("ttySC", port, | ||
88 | baud ? options : NULL); | ||
89 | } | ||
90 | #endif | ||
91 | } | ||
92 | |||
93 | #ifdef DEBUG | ||
94 | static void __init sni_idprom_dump(void) | ||
95 | { | ||
96 | int i; | ||
97 | |||
98 | pr_debug("SNI IDProm dump:\n"); | ||
99 | for (i = 0; i < 256; i++) { | ||
100 | if (i%16 == 0) | ||
101 | pr_debug("%04x ", i); | ||
102 | |||
103 | printk("%02x ", *(unsigned char *) (SNI_IDPROM_BASE + i)); | ||
104 | |||
105 | if (i % 16 == 15) | ||
106 | printk("\n"); | ||
107 | } | ||
108 | } | ||
109 | #endif | ||
110 | |||
111 | void __init plat_mem_setup(void) | ||
112 | { | ||
113 | int cputype; | ||
114 | |||
115 | set_io_port_base(SNI_PORT_BASE); | ||
116 | // ioport_resource.end = sni_io_resource.end; | ||
117 | |||
118 | /* | ||
119 | * Setup (E)ISA I/O memory access stuff | ||
120 | */ | ||
121 | #ifdef CONFIG_EISA | ||
122 | EISA_bus = 1; | ||
123 | #endif | ||
124 | |||
125 | sni_brd_type = *(unsigned char *)SNI_IDPROM_BRDTYPE; | ||
126 | cputype = *(unsigned char *)SNI_IDPROM_CPUTYPE; | ||
127 | switch (sni_brd_type) { | ||
128 | case SNI_BRD_TOWER_OASIC: | ||
129 | switch (cputype) { | ||
130 | case SNI_CPU_M8030: | ||
131 | system_type = "RM400-330"; | ||
132 | break; | ||
133 | case SNI_CPU_M8031: | ||
134 | system_type = "RM400-430"; | ||
135 | break; | ||
136 | case SNI_CPU_M8037: | ||
137 | system_type = "RM400-530"; | ||
138 | break; | ||
139 | case SNI_CPU_M8034: | ||
140 | system_type = "RM400-730"; | ||
141 | break; | ||
142 | default: | ||
143 | system_type = "RM400-xxx"; | ||
144 | break; | ||
145 | } | ||
146 | break; | ||
147 | case SNI_BRD_MINITOWER: | ||
148 | switch (cputype) { | ||
149 | case SNI_CPU_M8021: | ||
150 | case SNI_CPU_M8043: | ||
151 | system_type = "RM400-120"; | ||
152 | break; | ||
153 | case SNI_CPU_M8040: | ||
154 | system_type = "RM400-220"; | ||
155 | break; | ||
156 | case SNI_CPU_M8053: | ||
157 | system_type = "RM400-225"; | ||
158 | break; | ||
159 | case SNI_CPU_M8050: | ||
160 | system_type = "RM400-420"; | ||
161 | break; | ||
162 | default: | ||
163 | system_type = "RM400-xxx"; | ||
164 | break; | ||
165 | } | ||
166 | break; | ||
167 | case SNI_BRD_PCI_TOWER: | ||
168 | system_type = "RM400-Cxx"; | ||
169 | break; | ||
170 | case SNI_BRD_RM200: | ||
171 | system_type = "RM200-xxx"; | ||
172 | break; | ||
173 | case SNI_BRD_PCI_MTOWER: | ||
174 | system_type = "RM300-Cxx"; | ||
175 | break; | ||
176 | case SNI_BRD_PCI_DESKTOP: | ||
177 | switch (read_c0_prid() & PRID_IMP_MASK) { | ||
178 | case PRID_IMP_R4600: | ||
179 | case PRID_IMP_R4700: | ||
180 | system_type = "RM200-C20"; | ||
181 | break; | ||
182 | case PRID_IMP_R5000: | ||
183 | system_type = "RM200-C40"; | ||
184 | break; | ||
185 | default: | ||
186 | system_type = "RM200-Cxx"; | ||
187 | break; | ||
188 | } | ||
189 | break; | ||
190 | case SNI_BRD_PCI_TOWER_CPLUS: | ||
191 | system_type = "RM400-Exx"; | ||
192 | break; | ||
193 | case SNI_BRD_PCI_MTOWER_CPLUS: | ||
194 | system_type = "RM300-Exx"; | ||
195 | break; | ||
196 | } | ||
197 | pr_debug("Found SNI brdtype %02x name %s\n", sni_brd_type, system_type); | ||
198 | |||
199 | #ifdef DEBUG | ||
200 | sni_idprom_dump(); | ||
201 | #endif | ||
202 | |||
203 | switch (sni_brd_type) { | ||
204 | case SNI_BRD_10: | ||
205 | case SNI_BRD_10NEW: | ||
206 | case SNI_BRD_TOWER_OASIC: | ||
207 | case SNI_BRD_MINITOWER: | ||
208 | sni_a20r_init(); | ||
209 | break; | ||
210 | |||
211 | case SNI_BRD_PCI_TOWER: | ||
212 | case SNI_BRD_PCI_TOWER_CPLUS: | ||
213 | sni_pcit_init(); | ||
214 | break; | ||
215 | |||
216 | case SNI_BRD_RM200: | ||
217 | sni_rm200_init(); | ||
218 | break; | ||
219 | |||
220 | case SNI_BRD_PCI_MTOWER: | ||
221 | case SNI_BRD_PCI_DESKTOP: | ||
222 | case SNI_BRD_PCI_MTOWER_CPLUS: | ||
223 | sni_pcimt_init(); | ||
224 | break; | ||
225 | } | ||
226 | |||
227 | _machine_restart = sni_machine_restart; | ||
228 | pm_power_off = sni_machine_power_off; | ||
229 | |||
230 | sni_display_setup(); | ||
231 | sni_console_setup(); | ||
232 | } | ||
233 | |||
234 | #ifdef CONFIG_PCI | ||
235 | |||
236 | #include <linux/pci.h> | ||
237 | #include <video/vga.h> | ||
238 | #include <video/cirrus.h> | ||
239 | |||
240 | static void quirk_cirrus_ram_size(struct pci_dev *dev) | ||
241 | { | ||
242 | u16 cmd; | ||
243 | |||
244 | /* | ||
245 | * firmware doesn't set the ram size correct, so we | ||
246 | * need to do it here, otherwise we get screen corruption | ||
247 | * on older Cirrus chips | ||
248 | */ | ||
249 | pci_read_config_word(dev, PCI_COMMAND, &cmd); | ||
250 | if ((cmd & (PCI_COMMAND_IO|PCI_COMMAND_MEMORY)) | ||
251 | == (PCI_COMMAND_IO|PCI_COMMAND_MEMORY)) { | ||
252 | vga_wseq(NULL, CL_SEQR6, 0x12); /* unlock all extension registers */ | ||
253 | vga_wseq(NULL, CL_SEQRF, 0x18); | ||
254 | } | ||
255 | } | ||
256 | |||
257 | DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CIRRUS, PCI_DEVICE_ID_CIRRUS_5434_8, | ||
258 | quirk_cirrus_ram_size); | ||
259 | DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CIRRUS, PCI_DEVICE_ID_CIRRUS_5436, | ||
260 | quirk_cirrus_ram_size); | ||
261 | DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CIRRUS, PCI_DEVICE_ID_CIRRUS_5446, | ||
262 | quirk_cirrus_ram_size); | ||
263 | #endif | ||
diff --git a/arch/mips/sni/time.c b/arch/mips/sni/time.c new file mode 100644 index 000000000..ff3ba7e77 --- /dev/null +++ b/arch/mips/sni/time.c | |||
@@ -0,0 +1,167 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | #include <linux/types.h> | ||
3 | #include <linux/i8253.h> | ||
4 | #include <linux/interrupt.h> | ||
5 | #include <linux/irq.h> | ||
6 | #include <linux/smp.h> | ||
7 | #include <linux/time.h> | ||
8 | #include <linux/clockchips.h> | ||
9 | |||
10 | #include <asm/sni.h> | ||
11 | #include <asm/time.h> | ||
12 | |||
13 | #define SNI_CLOCK_TICK_RATE 3686400 | ||
14 | #define SNI_COUNTER2_DIV 64 | ||
15 | #define SNI_COUNTER0_DIV ((SNI_CLOCK_TICK_RATE / SNI_COUNTER2_DIV) / HZ) | ||
16 | |||
17 | static int a20r_set_periodic(struct clock_event_device *evt) | ||
18 | { | ||
19 | *(volatile u8 *)(A20R_PT_CLOCK_BASE + 12) = 0x34; | ||
20 | wmb(); | ||
21 | *(volatile u8 *)(A20R_PT_CLOCK_BASE + 0) = SNI_COUNTER0_DIV & 0xff; | ||
22 | wmb(); | ||
23 | *(volatile u8 *)(A20R_PT_CLOCK_BASE + 0) = SNI_COUNTER0_DIV >> 8; | ||
24 | wmb(); | ||
25 | |||
26 | *(volatile u8 *)(A20R_PT_CLOCK_BASE + 12) = 0xb4; | ||
27 | wmb(); | ||
28 | *(volatile u8 *)(A20R_PT_CLOCK_BASE + 8) = SNI_COUNTER2_DIV & 0xff; | ||
29 | wmb(); | ||
30 | *(volatile u8 *)(A20R_PT_CLOCK_BASE + 8) = SNI_COUNTER2_DIV >> 8; | ||
31 | wmb(); | ||
32 | return 0; | ||
33 | } | ||
34 | |||
35 | static struct clock_event_device a20r_clockevent_device = { | ||
36 | .name = "a20r-timer", | ||
37 | .features = CLOCK_EVT_FEAT_PERIODIC, | ||
38 | |||
39 | /* .mult, .shift, .max_delta_ns and .min_delta_ns left uninitialized */ | ||
40 | |||
41 | .rating = 300, | ||
42 | .irq = SNI_A20R_IRQ_TIMER, | ||
43 | .set_state_periodic = a20r_set_periodic, | ||
44 | }; | ||
45 | |||
46 | static irqreturn_t a20r_interrupt(int irq, void *dev_id) | ||
47 | { | ||
48 | struct clock_event_device *cd = dev_id; | ||
49 | |||
50 | *(volatile u8 *)A20R_PT_TIM0_ACK = 0; | ||
51 | wmb(); | ||
52 | |||
53 | cd->event_handler(cd); | ||
54 | |||
55 | return IRQ_HANDLED; | ||
56 | } | ||
57 | |||
58 | /* | ||
59 | * a20r platform uses 2 counters to divide the input frequency. | ||
60 | * Counter 2 output is connected to Counter 0 & 1 input. | ||
61 | */ | ||
62 | static void __init sni_a20r_timer_setup(void) | ||
63 | { | ||
64 | struct clock_event_device *cd = &a20r_clockevent_device; | ||
65 | unsigned int cpu = smp_processor_id(); | ||
66 | |||
67 | cd->cpumask = cpumask_of(cpu); | ||
68 | clockevents_register_device(cd); | ||
69 | if (request_irq(SNI_A20R_IRQ_TIMER, a20r_interrupt, | ||
70 | IRQF_PERCPU | IRQF_TIMER, "a20r-timer", cd)) | ||
71 | pr_err("Failed to register a20r-timer interrupt\n"); | ||
72 | } | ||
73 | |||
74 | #define SNI_8254_TICK_RATE 1193182UL | ||
75 | |||
76 | #define SNI_8254_TCSAMP_COUNTER ((SNI_8254_TICK_RATE / HZ) + 255) | ||
77 | |||
78 | static __init unsigned long dosample(void) | ||
79 | { | ||
80 | u32 ct0, ct1; | ||
81 | volatile u8 msb; | ||
82 | |||
83 | /* Start the counter. */ | ||
84 | outb_p(0x34, 0x43); | ||
85 | outb_p(SNI_8254_TCSAMP_COUNTER & 0xff, 0x40); | ||
86 | outb(SNI_8254_TCSAMP_COUNTER >> 8, 0x40); | ||
87 | |||
88 | /* Get initial counter invariant */ | ||
89 | ct0 = read_c0_count(); | ||
90 | |||
91 | /* Latch and spin until top byte of counter0 is zero */ | ||
92 | do { | ||
93 | outb(0x00, 0x43); | ||
94 | (void) inb(0x40); | ||
95 | msb = inb(0x40); | ||
96 | ct1 = read_c0_count(); | ||
97 | } while (msb); | ||
98 | |||
99 | /* Stop the counter. */ | ||
100 | outb(0x38, 0x43); | ||
101 | /* | ||
102 | * Return the difference, this is how far the r4k counter increments | ||
103 | * for every 1/HZ seconds. We round off the nearest 1 MHz of master | ||
104 | * clock (= 1000000 / HZ / 2). | ||
105 | */ | ||
106 | /*return (ct1 - ct0 + (500000/HZ/2)) / (500000/HZ) * (500000/HZ);*/ | ||
107 | return (ct1 - ct0) / (500000/HZ) * (500000/HZ); | ||
108 | } | ||
109 | |||
110 | /* | ||
111 | * Here we need to calibrate the cycle counter to at least be close. | ||
112 | */ | ||
113 | void __init plat_time_init(void) | ||
114 | { | ||
115 | unsigned long r4k_ticks[3]; | ||
116 | unsigned long r4k_tick; | ||
117 | |||
118 | /* | ||
119 | * Figure out the r4k offset, the algorithm is very simple and works in | ||
120 | * _all_ cases as long as the 8254 counter register itself works ok (as | ||
121 | * an interrupt driving timer it does not because of bug, this is why | ||
122 | * we are using the onchip r4k counter/compare register to serve this | ||
123 | * purpose, but for r4k_offset calculation it will work ok for us). | ||
124 | * There are other very complicated ways of performing this calculation | ||
125 | * but this one works just fine so I am not going to futz around. ;-) | ||
126 | */ | ||
127 | printk(KERN_INFO "Calibrating system timer... "); | ||
128 | dosample(); /* Prime cache. */ | ||
129 | dosample(); /* Prime cache. */ | ||
130 | /* Zero is NOT an option. */ | ||
131 | do { | ||
132 | r4k_ticks[0] = dosample(); | ||
133 | } while (!r4k_ticks[0]); | ||
134 | do { | ||
135 | r4k_ticks[1] = dosample(); | ||
136 | } while (!r4k_ticks[1]); | ||
137 | |||
138 | if (r4k_ticks[0] != r4k_ticks[1]) { | ||
139 | printk("warning: timer counts differ, retrying... "); | ||
140 | r4k_ticks[2] = dosample(); | ||
141 | if (r4k_ticks[2] == r4k_ticks[0] | ||
142 | || r4k_ticks[2] == r4k_ticks[1]) | ||
143 | r4k_tick = r4k_ticks[2]; | ||
144 | else { | ||
145 | printk("disagreement, using average... "); | ||
146 | r4k_tick = (r4k_ticks[0] + r4k_ticks[1] | ||
147 | + r4k_ticks[2]) / 3; | ||
148 | } | ||
149 | } else | ||
150 | r4k_tick = r4k_ticks[0]; | ||
151 | |||
152 | printk("%d [%d.%04d MHz CPU]\n", (int) r4k_tick, | ||
153 | (int) (r4k_tick / (500000 / HZ)), | ||
154 | (int) (r4k_tick % (500000 / HZ))); | ||
155 | |||
156 | mips_hpt_frequency = r4k_tick * HZ; | ||
157 | |||
158 | switch (sni_brd_type) { | ||
159 | case SNI_BRD_10: | ||
160 | case SNI_BRD_10NEW: | ||
161 | case SNI_BRD_TOWER_OASIC: | ||
162 | case SNI_BRD_MINITOWER: | ||
163 | sni_a20r_timer_setup(); | ||
164 | break; | ||
165 | } | ||
166 | setup_pit_timer(); | ||
167 | } | ||