aboutsummaryrefslogtreecommitdiffstats
path: root/arch/mips/sgi-ip30
diff options
context:
space:
mode:
authorWe-unite <3205135446@qq.com>2025-03-08 22:04:20 +0800
committerWe-unite <3205135446@qq.com>2025-03-08 22:04:20 +0800
commita07bb8fd1299070229f0e8f3dcb57ffd5ef9870a (patch)
tree84f21bd0bf7071bc5fc7dd989e77d7ceb5476682 /arch/mips/sgi-ip30
downloadohosKernel-a07bb8fd1299070229f0e8f3dcb57ffd5ef9870a.tar.gz
ohosKernel-a07bb8fd1299070229f0e8f3dcb57ffd5ef9870a.zip
Initial commit: OpenHarmony-v4.0-ReleaseOpenHarmony-v4.0-Release
Diffstat (limited to 'arch/mips/sgi-ip30')
-rw-r--r--arch/mips/sgi-ip30/Makefile9
-rw-r--r--arch/mips/sgi-ip30/Platform5
-rw-r--r--arch/mips/sgi-ip30/ip30-common.h23
-rw-r--r--arch/mips/sgi-ip30/ip30-console.c23
-rw-r--r--arch/mips/sgi-ip30/ip30-irq.c331
-rw-r--r--arch/mips/sgi-ip30/ip30-power.c41
-rw-r--r--arch/mips/sgi-ip30/ip30-setup.c138
-rw-r--r--arch/mips/sgi-ip30/ip30-smp.c149
-rw-r--r--arch/mips/sgi-ip30/ip30-timer.c63
-rw-r--r--arch/mips/sgi-ip30/ip30-xtalk.c152
10 files changed, 934 insertions, 0 deletions
diff --git a/arch/mips/sgi-ip30/Makefile b/arch/mips/sgi-ip30/Makefile
new file mode 100644
index 000000000..18cf561b3
--- /dev/null
+++ b/arch/mips/sgi-ip30/Makefile
@@ -0,0 +1,9 @@
1# SPDX-License-Identifier: GPL-2.0
2#
3# Makefile for the IP30 specific kernel interface routines under Linux.
4#
5
6obj-y := ip30-irq.o ip30-power.o ip30-setup.o ip30-timer.o ip30-xtalk.o
7
8obj-$(CONFIG_EARLY_PRINTK) += ip30-console.o
9obj-$(CONFIG_SMP) += ip30-smp.o
diff --git a/arch/mips/sgi-ip30/Platform b/arch/mips/sgi-ip30/Platform
new file mode 100644
index 000000000..f6f11517e
--- /dev/null
+++ b/arch/mips/sgi-ip30/Platform
@@ -0,0 +1,5 @@
1#
2# SGI-IP30 (Octane/Octane2)
3#
4cflags-$(CONFIG_SGI_IP30) += -I$(srctree)/arch/mips/include/asm/mach-ip30
5load-$(CONFIG_SGI_IP30) += 0xa800000020004000
diff --git a/arch/mips/sgi-ip30/ip30-common.h b/arch/mips/sgi-ip30/ip30-common.h
new file mode 100644
index 000000000..7b5db24b6
--- /dev/null
+++ b/arch/mips/sgi-ip30/ip30-common.h
@@ -0,0 +1,23 @@
1/* SPDX-License-Identifier: GPL-2.0-only */
2
3#ifndef __IP30_COMMON_H
4#define __IP30_COMMON_H
5
6/*
7 * Power Switch is wired via BaseIO BRIDGE slot #6.
8 *
9 * ACFail is wired via BaseIO BRIDGE slot #7.
10 */
11#define IP30_POWER_IRQ HEART_L2_INT_POWER_BTN
12
13#define IP30_HEART_L0_IRQ (MIPS_CPU_IRQ_BASE + 2)
14#define IP30_HEART_L1_IRQ (MIPS_CPU_IRQ_BASE + 3)
15#define IP30_HEART_L2_IRQ (MIPS_CPU_IRQ_BASE + 4)
16#define IP30_HEART_TIMER_IRQ (MIPS_CPU_IRQ_BASE + 5)
17#define IP30_HEART_ERR_IRQ (MIPS_CPU_IRQ_BASE + 6)
18
19extern void __init ip30_install_ipi(void);
20extern struct plat_smp_ops ip30_smp_ops;
21extern void __init ip30_per_cpu_init(void);
22
23#endif /* __IP30_COMMON_H */
diff --git a/arch/mips/sgi-ip30/ip30-console.c b/arch/mips/sgi-ip30/ip30-console.c
new file mode 100644
index 000000000..b91f8c4fd
--- /dev/null
+++ b/arch/mips/sgi-ip30/ip30-console.c
@@ -0,0 +1,23 @@
1// SPDX-License-Identifier: GPL-2.0
2
3#include <linux/io.h>
4
5#include <asm/sn/ioc3.h>
6
7static inline struct ioc3_uartregs *console_uart(void)
8{
9 struct ioc3 *ioc3;
10
11 ioc3 = (struct ioc3 *)((void *)(0x900000001f600000));
12 return &ioc3->sregs.uarta;
13}
14
15void prom_putchar(char c)
16{
17 struct ioc3_uartregs *uart = console_uart();
18
19 while ((readb(&uart->iu_lsr) & 0x20) == 0)
20 cpu_relax();
21
22 writeb(c, &uart->iu_thr);
23}
diff --git a/arch/mips/sgi-ip30/ip30-irq.c b/arch/mips/sgi-ip30/ip30-irq.c
new file mode 100644
index 000000000..e8374e4c7
--- /dev/null
+++ b/arch/mips/sgi-ip30/ip30-irq.c
@@ -0,0 +1,331 @@
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * ip30-irq.c: Highlevel interrupt handling for IP30 architecture.
4 */
5#include <linux/errno.h>
6#include <linux/init.h>
7#include <linux/interrupt.h>
8#include <linux/irq.h>
9#include <linux/percpu.h>
10#include <linux/spinlock.h>
11#include <linux/tick.h>
12#include <linux/types.h>
13
14#include <asm/irq_cpu.h>
15#include <asm/sgi/heart.h>
16
17#include "ip30-common.h"
18
19struct heart_irq_data {
20 u64 *irq_mask;
21 int cpu;
22};
23
24static DECLARE_BITMAP(heart_irq_map, HEART_NUM_IRQS);
25
26static DEFINE_PER_CPU(unsigned long, irq_enable_mask);
27
28static inline int heart_alloc_int(void)
29{
30 int bit;
31
32again:
33 bit = find_first_zero_bit(heart_irq_map, HEART_NUM_IRQS);
34 if (bit >= HEART_NUM_IRQS)
35 return -ENOSPC;
36
37 if (test_and_set_bit(bit, heart_irq_map))
38 goto again;
39
40 return bit;
41}
42
43static void ip30_error_irq(struct irq_desc *desc)
44{
45 u64 pending, mask, cause, error_irqs, err_reg;
46 int cpu = smp_processor_id();
47 int i;
48
49 pending = heart_read(&heart_regs->isr);
50 mask = heart_read(&heart_regs->imr[cpu]);
51 cause = heart_read(&heart_regs->cause);
52 error_irqs = (pending & HEART_L4_INT_MASK & mask);
53
54 /* Bail if there's nothing to process (how did we get here, then?) */
55 if (unlikely(!error_irqs))
56 return;
57
58 /* Prevent any of the error IRQs from firing again. */
59 heart_write(mask & ~(pending), &heart_regs->imr[cpu]);
60
61 /* Ack all error IRQs. */
62 heart_write(HEART_L4_INT_MASK, &heart_regs->clear_isr);
63
64 /*
65 * If we also have a cause value, then something happened, so loop
66 * through the error IRQs and report a "heart attack" for each one
67 * and print the value of the HEART cause register. This is really
68 * primitive right now, but it should hopefully work until a more
69 * robust error handling routine can be put together.
70 *
71 * Refer to heart.h for the HC_* macros to work out the cause
72 * that got us here.
73 */
74 if (cause) {
75 pr_alert("IP30: CPU%d: HEART ATTACK! ISR = 0x%.16llx, IMR = 0x%.16llx, CAUSE = 0x%.16llx\n",
76 cpu, pending, mask, cause);
77
78 if (cause & HC_COR_MEM_ERR) {
79 err_reg = heart_read(&heart_regs->mem_err_addr);
80 pr_alert(" HEART_MEMERR_ADDR = 0x%.16llx\n", err_reg);
81 }
82
83 /* i = 63; i >= 51; i-- */
84 for (i = HEART_ERR_MASK_END; i >= HEART_ERR_MASK_START; i--)
85 if ((pending >> i) & 1)
86 pr_alert(" HEART Error IRQ #%d\n", i);
87
88 /* XXX: Seems possible to loop forever here, so panic(). */
89 panic("IP30: Fatal Error !\n");
90 }
91
92 /* Unmask the error IRQs. */
93 heart_write(mask, &heart_regs->imr[cpu]);
94}
95
96static void ip30_normal_irq(struct irq_desc *desc)
97{
98 int cpu = smp_processor_id();
99 struct irq_domain *domain;
100 u64 pend, mask;
101 int irq;
102
103 pend = heart_read(&heart_regs->isr);
104 mask = (heart_read(&heart_regs->imr[cpu]) &
105 (HEART_L0_INT_MASK | HEART_L1_INT_MASK | HEART_L2_INT_MASK));
106
107 pend &= mask;
108 if (unlikely(!pend))
109 return;
110
111#ifdef CONFIG_SMP
112 if (pend & BIT_ULL(HEART_L2_INT_RESCHED_CPU_0)) {
113 heart_write(BIT_ULL(HEART_L2_INT_RESCHED_CPU_0),
114 &heart_regs->clear_isr);
115 scheduler_ipi();
116 } else if (pend & BIT_ULL(HEART_L2_INT_RESCHED_CPU_1)) {
117 heart_write(BIT_ULL(HEART_L2_INT_RESCHED_CPU_1),
118 &heart_regs->clear_isr);
119 scheduler_ipi();
120 } else if (pend & BIT_ULL(HEART_L2_INT_CALL_CPU_0)) {
121 heart_write(BIT_ULL(HEART_L2_INT_CALL_CPU_0),
122 &heart_regs->clear_isr);
123 generic_smp_call_function_interrupt();
124 } else if (pend & BIT_ULL(HEART_L2_INT_CALL_CPU_1)) {
125 heart_write(BIT_ULL(HEART_L2_INT_CALL_CPU_1),
126 &heart_regs->clear_isr);
127 generic_smp_call_function_interrupt();
128 } else
129#endif
130 {
131 domain = irq_desc_get_handler_data(desc);
132 irq = irq_linear_revmap(domain, __ffs(pend));
133 if (irq)
134 generic_handle_irq(irq);
135 else
136 spurious_interrupt();
137 }
138}
139
140static void ip30_ack_heart_irq(struct irq_data *d)
141{
142 heart_write(BIT_ULL(d->hwirq), &heart_regs->clear_isr);
143}
144
145static void ip30_mask_heart_irq(struct irq_data *d)
146{
147 struct heart_irq_data *hd = irq_data_get_irq_chip_data(d);
148 unsigned long *mask = &per_cpu(irq_enable_mask, hd->cpu);
149
150 clear_bit(d->hwirq, mask);
151 heart_write(*mask, &heart_regs->imr[hd->cpu]);
152}
153
154static void ip30_mask_and_ack_heart_irq(struct irq_data *d)
155{
156 struct heart_irq_data *hd = irq_data_get_irq_chip_data(d);
157 unsigned long *mask = &per_cpu(irq_enable_mask, hd->cpu);
158
159 clear_bit(d->hwirq, mask);
160 heart_write(*mask, &heart_regs->imr[hd->cpu]);
161 heart_write(BIT_ULL(d->hwirq), &heart_regs->clear_isr);
162}
163
164static void ip30_unmask_heart_irq(struct irq_data *d)
165{
166 struct heart_irq_data *hd = irq_data_get_irq_chip_data(d);
167 unsigned long *mask = &per_cpu(irq_enable_mask, hd->cpu);
168
169 set_bit(d->hwirq, mask);
170 heart_write(*mask, &heart_regs->imr[hd->cpu]);
171}
172
173static int ip30_set_heart_irq_affinity(struct irq_data *d,
174 const struct cpumask *mask, bool force)
175{
176 struct heart_irq_data *hd = irq_data_get_irq_chip_data(d);
177
178 if (!hd)
179 return -EINVAL;
180
181 if (irqd_is_started(d))
182 ip30_mask_and_ack_heart_irq(d);
183
184 hd->cpu = cpumask_first_and(mask, cpu_online_mask);
185
186 if (irqd_is_started(d))
187 ip30_unmask_heart_irq(d);
188
189 irq_data_update_effective_affinity(d, cpumask_of(hd->cpu));
190
191 return 0;
192}
193
194static struct irq_chip heart_irq_chip = {
195 .name = "HEART",
196 .irq_ack = ip30_ack_heart_irq,
197 .irq_mask = ip30_mask_heart_irq,
198 .irq_mask_ack = ip30_mask_and_ack_heart_irq,
199 .irq_unmask = ip30_unmask_heart_irq,
200 .irq_set_affinity = ip30_set_heart_irq_affinity,
201};
202
203static int heart_domain_alloc(struct irq_domain *domain, unsigned int virq,
204 unsigned int nr_irqs, void *arg)
205{
206 struct irq_alloc_info *info = arg;
207 struct heart_irq_data *hd;
208 int hwirq;
209
210 if (nr_irqs > 1 || !info)
211 return -EINVAL;
212
213 hd = kzalloc(sizeof(*hd), GFP_KERNEL);
214 if (!hd)
215 return -ENOMEM;
216
217 hwirq = heart_alloc_int();
218 if (hwirq < 0) {
219 kfree(hd);
220 return -EAGAIN;
221 }
222 irq_domain_set_info(domain, virq, hwirq, &heart_irq_chip, hd,
223 handle_level_irq, NULL, NULL);
224
225 return 0;
226}
227
228static void heart_domain_free(struct irq_domain *domain,
229 unsigned int virq, unsigned int nr_irqs)
230{
231 struct irq_data *irqd;
232
233 if (nr_irqs > 1)
234 return;
235
236 irqd = irq_domain_get_irq_data(domain, virq);
237 if (irqd) {
238 clear_bit(irqd->hwirq, heart_irq_map);
239 kfree(irqd->chip_data);
240 }
241}
242
243static const struct irq_domain_ops heart_domain_ops = {
244 .alloc = heart_domain_alloc,
245 .free = heart_domain_free,
246};
247
248void __init ip30_install_ipi(void)
249{
250 int cpu = smp_processor_id();
251 unsigned long *mask = &per_cpu(irq_enable_mask, cpu);
252
253 set_bit(HEART_L2_INT_RESCHED_CPU_0 + cpu, mask);
254 heart_write(BIT_ULL(HEART_L2_INT_RESCHED_CPU_0 + cpu),
255 &heart_regs->clear_isr);
256 set_bit(HEART_L2_INT_CALL_CPU_0 + cpu, mask);
257 heart_write(BIT_ULL(HEART_L2_INT_CALL_CPU_0 + cpu),
258 &heart_regs->clear_isr);
259
260 heart_write(*mask, &heart_regs->imr[cpu]);
261}
262
263void __init arch_init_irq(void)
264{
265 struct irq_domain *domain;
266 struct fwnode_handle *fn;
267 unsigned long *mask;
268 int i;
269
270 mips_cpu_irq_init();
271
272 /* Mask all IRQs. */
273 heart_write(HEART_CLR_ALL_MASK, &heart_regs->imr[0]);
274 heart_write(HEART_CLR_ALL_MASK, &heart_regs->imr[1]);
275 heart_write(HEART_CLR_ALL_MASK, &heart_regs->imr[2]);
276 heart_write(HEART_CLR_ALL_MASK, &heart_regs->imr[3]);
277
278 /* Ack everything. */
279 heart_write(HEART_ACK_ALL_MASK, &heart_regs->clear_isr);
280
281 /* Enable specific HEART error IRQs for each CPU. */
282 mask = &per_cpu(irq_enable_mask, 0);
283 *mask |= HEART_CPU0_ERR_MASK;
284 heart_write(*mask, &heart_regs->imr[0]);
285 mask = &per_cpu(irq_enable_mask, 1);
286 *mask |= HEART_CPU1_ERR_MASK;
287 heart_write(*mask, &heart_regs->imr[1]);
288
289 /*
290 * Some HEART bits are reserved by hardware or by software convention.
291 * Mark these as reserved right away so they won't be accidentally
292 * used later.
293 */
294 set_bit(HEART_L0_INT_GENERIC, heart_irq_map);
295 set_bit(HEART_L0_INT_FLOW_CTRL_HWTR_0, heart_irq_map);
296 set_bit(HEART_L0_INT_FLOW_CTRL_HWTR_1, heart_irq_map);
297 set_bit(HEART_L2_INT_RESCHED_CPU_0, heart_irq_map);
298 set_bit(HEART_L2_INT_RESCHED_CPU_1, heart_irq_map);
299 set_bit(HEART_L2_INT_CALL_CPU_0, heart_irq_map);
300 set_bit(HEART_L2_INT_CALL_CPU_1, heart_irq_map);
301 set_bit(HEART_L3_INT_TIMER, heart_irq_map);
302
303 /* Reserve the error interrupts (#51 to #63). */
304 for (i = HEART_L4_INT_XWID_ERR_9; i <= HEART_L4_INT_HEART_EXCP; i++)
305 set_bit(i, heart_irq_map);
306
307 fn = irq_domain_alloc_named_fwnode("HEART");
308 WARN_ON(fn == NULL);
309 if (!fn)
310 return;
311 domain = irq_domain_create_linear(fn, HEART_NUM_IRQS,
312 &heart_domain_ops, NULL);
313 WARN_ON(domain == NULL);
314 if (!domain)
315 return;
316
317 irq_set_default_host(domain);
318
319 irq_set_percpu_devid(IP30_HEART_L0_IRQ);
320 irq_set_chained_handler_and_data(IP30_HEART_L0_IRQ, ip30_normal_irq,
321 domain);
322 irq_set_percpu_devid(IP30_HEART_L1_IRQ);
323 irq_set_chained_handler_and_data(IP30_HEART_L1_IRQ, ip30_normal_irq,
324 domain);
325 irq_set_percpu_devid(IP30_HEART_L2_IRQ);
326 irq_set_chained_handler_and_data(IP30_HEART_L2_IRQ, ip30_normal_irq,
327 domain);
328 irq_set_percpu_devid(IP30_HEART_ERR_IRQ);
329 irq_set_chained_handler_and_data(IP30_HEART_ERR_IRQ, ip30_error_irq,
330 domain);
331}
diff --git a/arch/mips/sgi-ip30/ip30-power.c b/arch/mips/sgi-ip30/ip30-power.c
new file mode 100644
index 000000000..120b3f3d5
--- /dev/null
+++ b/arch/mips/sgi-ip30/ip30-power.c
@@ -0,0 +1,41 @@
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * ip30-power.c: Software powerdown and reset handling for IP30 architecture.
4 *
5 * Copyright (C) 2004-2007 Stanislaw Skowronek <skylark@unaligned.org>
6 * 2014 Joshua Kinard <kumba@gentoo.org>
7 * 2009 Johannes Dickgreber <tanzy@gmx.de>
8 */
9
10#include <linux/init.h>
11#include <linux/kernel.h>
12#include <linux/module.h>
13#include <linux/sched.h>
14#include <linux/notifier.h>
15#include <linux/delay.h>
16#include <linux/rtc/ds1685.h>
17#include <linux/interrupt.h>
18#include <linux/pm.h>
19
20#include <asm/reboot.h>
21#include <asm/sgi/heart.h>
22
23static void __noreturn ip30_machine_restart(char *cmd)
24{
25 /*
26 * Execute HEART cold reset
27 * Yes, it's cold-HEARTed!
28 */
29 heart_write((heart_read(&heart_regs->mode) | HM_COLD_RST),
30 &heart_regs->mode);
31 unreachable();
32}
33
34static int __init ip30_reboot_setup(void)
35{
36 _machine_restart = ip30_machine_restart;
37
38 return 0;
39}
40
41subsys_initcall(ip30_reboot_setup);
diff --git a/arch/mips/sgi-ip30/ip30-setup.c b/arch/mips/sgi-ip30/ip30-setup.c
new file mode 100644
index 000000000..44b1607e9
--- /dev/null
+++ b/arch/mips/sgi-ip30/ip30-setup.c
@@ -0,0 +1,138 @@
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * SGI IP30 miscellaneous setup bits.
4 *
5 * Copyright (C) 2004-2007 Stanislaw Skowronek <skylark@unaligned.org>
6 * 2007 Joshua Kinard <kumba@gentoo.org>
7 * 2009 Johannes Dickgreber <tanzy@gmx.de>
8 */
9
10#include <linux/init.h>
11#include <linux/io.h>
12#include <linux/kernel.h>
13#include <linux/types.h>
14#include <linux/percpu.h>
15#include <linux/memblock.h>
16
17#include <asm/smp-ops.h>
18#include <asm/sgialib.h>
19#include <asm/time.h>
20#include <asm/sgi/heart.h>
21
22#include "ip30-common.h"
23
24/* Structure of accessible HEART registers located in XKPHYS space. */
25struct ip30_heart_regs __iomem *heart_regs = HEART_XKPHYS_BASE;
26
27/*
28 * ARCS will report up to the first 1GB of
29 * memory if queried. Anything beyond that
30 * is marked as reserved.
31 */
32#define IP30_MAX_PROM_MEMORY _AC(0x40000000, UL)
33
34/*
35 * Memory in the Octane starts at 512MB
36 */
37#define IP30_MEMORY_BASE _AC(0x20000000, UL)
38
39/*
40 * If using ARCS to probe for memory, then
41 * remaining memory will start at this offset.
42 */
43#define IP30_REAL_MEMORY_START (IP30_MEMORY_BASE + IP30_MAX_PROM_MEMORY)
44
45#define MEM_SHIFT(x) ((x) >> 20)
46
47static void __init ip30_mem_init(void)
48{
49 unsigned long total_mem;
50 phys_addr_t addr;
51 phys_addr_t size;
52 u32 memcfg;
53 int i;
54
55 total_mem = 0;
56 for (i = 0; i < HEART_MEMORY_BANKS; i++) {
57 memcfg = __raw_readl(&heart_regs->mem_cfg.l[i]);
58 if (!(memcfg & HEART_MEMCFG_VALID))
59 continue;
60
61 addr = memcfg & HEART_MEMCFG_ADDR_MASK;
62 addr <<= HEART_MEMCFG_UNIT_SHIFT;
63 addr += IP30_MEMORY_BASE;
64 size = memcfg & HEART_MEMCFG_SIZE_MASK;
65 size >>= HEART_MEMCFG_SIZE_SHIFT;
66 size += 1;
67 size <<= HEART_MEMCFG_UNIT_SHIFT;
68
69 total_mem += size;
70
71 if (addr >= IP30_REAL_MEMORY_START)
72 memblock_free(addr, size);
73 else if ((addr + size) > IP30_REAL_MEMORY_START)
74 memblock_free(IP30_REAL_MEMORY_START,
75 size - IP30_MAX_PROM_MEMORY);
76 }
77 pr_info("Detected %luMB of physical memory.\n", MEM_SHIFT(total_mem));
78}
79
80/**
81 * ip30_cpu_time_init - platform time initialization.
82 */
83static void __init ip30_cpu_time_init(void)
84{
85 int cpu = smp_processor_id();
86 u64 heart_compare;
87 unsigned int start, end;
88 int time_diff;
89
90 heart_compare = (heart_read(&heart_regs->count) +
91 (HEART_CYCLES_PER_SEC / 10));
92 start = read_c0_count();
93 while ((heart_read(&heart_regs->count) - heart_compare) & 0x800000)
94 cpu_relax();
95
96 end = read_c0_count();
97 time_diff = (int)end - (int)start;
98 mips_hpt_frequency = time_diff * 10;
99 pr_info("IP30: CPU%d: %d MHz CPU detected.\n", cpu,
100 (mips_hpt_frequency * 2) / 1000000);
101}
102
103void __init ip30_per_cpu_init(void)
104{
105 /* Disable all interrupts. */
106 clear_c0_status(ST0_IM);
107
108 ip30_cpu_time_init();
109#ifdef CONFIG_SMP
110 ip30_install_ipi();
111#endif
112
113 enable_percpu_irq(IP30_HEART_L0_IRQ, IRQ_TYPE_NONE);
114 enable_percpu_irq(IP30_HEART_L1_IRQ, IRQ_TYPE_NONE);
115 enable_percpu_irq(IP30_HEART_L2_IRQ, IRQ_TYPE_NONE);
116 enable_percpu_irq(IP30_HEART_ERR_IRQ, IRQ_TYPE_NONE);
117}
118
119/**
120 * plat_mem_setup - despite the name, misc setup happens here.
121 */
122void __init plat_mem_setup(void)
123{
124 ip30_mem_init();
125
126 /* XXX: Hard lock on /sbin/init if this flag isn't specified. */
127 prom_flags |= PROM_FLAG_DONT_FREE_TEMP;
128
129#ifdef CONFIG_SMP
130 register_smp_ops(&ip30_smp_ops);
131#else
132 ip30_per_cpu_init();
133#endif
134
135 ioport_resource.start = 0;
136 ioport_resource.end = ~0UL;
137 set_io_port_base(IO_BASE);
138}
diff --git a/arch/mips/sgi-ip30/ip30-smp.c b/arch/mips/sgi-ip30/ip30-smp.c
new file mode 100644
index 000000000..4bfe65460
--- /dev/null
+++ b/arch/mips/sgi-ip30/ip30-smp.c
@@ -0,0 +1,149 @@
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * ip30-smp.c: SMP on IP30 architecture.
4 * Based off of the original IP30 SMP code, with inspiration from ip27-smp.c
5 * and smp-bmips.c.
6 *
7 * Copyright (C) 2005-2007 Stanislaw Skowronek <skylark@unaligned.org>
8 * 2006-2007, 2014-2015 Joshua Kinard <kumba@gentoo.org>
9 * 2009 Johannes Dickgreber <tanzy@gmx.de>
10 */
11
12#include <linux/init.h>
13#include <linux/sched.h>
14#include <linux/sched/task_stack.h>
15
16#include <asm/time.h>
17#include <asm/sgi/heart.h>
18
19#include "ip30-common.h"
20
21#define MPCONF_MAGIC 0xbaddeed2
22#define MPCONF_ADDR 0xa800000000000600L
23#define MPCONF_SIZE 0x80
24#define MPCONF(x) (MPCONF_ADDR + (x) * MPCONF_SIZE)
25
26/* HEART can theoretically do 4 CPUs, but only 2 are physically possible */
27#define MP_NCPU 2
28
29struct mpconf {
30 u32 magic;
31 u32 prid;
32 u32 physid;
33 u32 virtid;
34 u32 scachesz;
35 u16 fanloads;
36 u16 res;
37 void *launch;
38 void *rendezvous;
39 u64 res2[3];
40 void *stackaddr;
41 void *lnch_parm;
42 void *rndv_parm;
43 u32 idleflag;
44};
45
46static void ip30_smp_send_ipi_single(int cpu, u32 action)
47{
48 int irq;
49
50 switch (action) {
51 case SMP_RESCHEDULE_YOURSELF:
52 irq = HEART_L2_INT_RESCHED_CPU_0;
53 break;
54 case SMP_CALL_FUNCTION:
55 irq = HEART_L2_INT_CALL_CPU_0;
56 break;
57 default:
58 panic("IP30: Unknown action value in %s!\n", __func__);
59 }
60
61 irq += cpu;
62
63 /* Poke the other CPU -- it's got mail! */
64 heart_write(BIT_ULL(irq), &heart_regs->set_isr);
65}
66
67static void ip30_smp_send_ipi_mask(const struct cpumask *mask, u32 action)
68{
69 u32 i;
70
71 for_each_cpu(i, mask)
72 ip30_smp_send_ipi_single(i, action);
73}
74
75static void __init ip30_smp_setup(void)
76{
77 int i;
78 int ncpu = 0;
79 struct mpconf *mpc;
80
81 init_cpu_possible(cpumask_of(0));
82
83 /* Scan the MPCONF structure and enumerate available CPUs. */
84 for (i = 0; i < MP_NCPU; i++) {
85 mpc = (struct mpconf *)MPCONF(i);
86 if (mpc->magic == MPCONF_MAGIC) {
87 set_cpu_possible(i, true);
88 __cpu_number_map[i] = ++ncpu;
89 __cpu_logical_map[ncpu] = i;
90 pr_info("IP30: Slot: %d, PrID: %.8x, PhyID: %d, VirtID: %d\n",
91 i, mpc->prid, mpc->physid, mpc->virtid);
92 }
93 }
94 pr_info("IP30: Detected %d CPU(s) present.\n", ncpu);
95
96 /*
97 * Set the coherency algorithm to '5' (cacheable coherent
98 * exclusive on write). This is needed on IP30 SMP, especially
99 * for R14000 CPUs, otherwise, instruction bus errors will
100 * occur upon reaching userland.
101 */
102 change_c0_config(CONF_CM_CMASK, CONF_CM_CACHABLE_COW);
103}
104
105static void __init ip30_smp_prepare_cpus(unsigned int max_cpus)
106{
107 /* nothing to do here */
108}
109
110static int __init ip30_smp_boot_secondary(int cpu, struct task_struct *idle)
111{
112 struct mpconf *mpc = (struct mpconf *)MPCONF(cpu);
113
114 /* Stack pointer (sp). */
115 mpc->stackaddr = (void *)__KSTK_TOS(idle);
116
117 /* Global pointer (gp). */
118 mpc->lnch_parm = task_thread_info(idle);
119
120 mb(); /* make sure stack and lparm are written */
121
122 /* Boot CPUx. */
123 mpc->launch = smp_bootstrap;
124
125 /* CPUx now executes smp_bootstrap, then ip30_smp_finish */
126 return 0;
127}
128
129static void __init ip30_smp_init_cpu(void)
130{
131 ip30_per_cpu_init();
132}
133
134static void __init ip30_smp_finish(void)
135{
136 enable_percpu_irq(get_c0_compare_int(), IRQ_TYPE_NONE);
137 local_irq_enable();
138}
139
140struct plat_smp_ops __read_mostly ip30_smp_ops = {
141 .send_ipi_single = ip30_smp_send_ipi_single,
142 .send_ipi_mask = ip30_smp_send_ipi_mask,
143 .smp_setup = ip30_smp_setup,
144 .prepare_cpus = ip30_smp_prepare_cpus,
145 .boot_secondary = ip30_smp_boot_secondary,
146 .init_secondary = ip30_smp_init_cpu,
147 .smp_finish = ip30_smp_finish,
148 .prepare_boot_cpu = ip30_smp_init_cpu,
149};
diff --git a/arch/mips/sgi-ip30/ip30-timer.c b/arch/mips/sgi-ip30/ip30-timer.c
new file mode 100644
index 000000000..d13e10547
--- /dev/null
+++ b/arch/mips/sgi-ip30/ip30-timer.c
@@ -0,0 +1,63 @@
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * ip30-timer.c: Clocksource/clockevent support for the
4 * HEART chip in SGI Octane (IP30) systems.
5 *
6 * Copyright (C) 2004-2007 Stanislaw Skowronek <skylark@unaligned.org>
7 * Copyright (C) 2009 Johannes Dickgreber <tanzy@gmx.de>
8 * Copyright (C) 2011 Joshua Kinard <kumba@gentoo.org>
9 */
10
11#include <linux/clocksource.h>
12#include <linux/cpumask.h>
13#include <linux/init.h>
14#include <linux/kernel.h>
15#include <linux/percpu.h>
16#include <linux/sched_clock.h>
17
18#include <asm/time.h>
19#include <asm/cevt-r4k.h>
20#include <asm/sgi/heart.h>
21
22static u64 ip30_heart_counter_read(struct clocksource *cs)
23{
24 return heart_read(&heart_regs->count);
25}
26
27struct clocksource ip30_heart_clocksource = {
28 .name = "HEART",
29 .rating = 400,
30 .read = ip30_heart_counter_read,
31 .mask = CLOCKSOURCE_MASK(52),
32 .flags = (CLOCK_SOURCE_IS_CONTINUOUS | CLOCK_SOURCE_VALID_FOR_HRES),
33};
34
35static u64 notrace ip30_heart_read_sched_clock(void)
36{
37 return heart_read(&heart_regs->count);
38}
39
40static void __init ip30_heart_clocksource_init(void)
41{
42 struct clocksource *cs = &ip30_heart_clocksource;
43
44 clocksource_register_hz(cs, HEART_CYCLES_PER_SEC);
45
46 sched_clock_register(ip30_heart_read_sched_clock, 52,
47 HEART_CYCLES_PER_SEC);
48}
49
50void __init plat_time_init(void)
51{
52 int irq = get_c0_compare_int();
53
54 cp0_timer_irq_installed = 1;
55 c0_compare_irqaction.percpu_dev_id = &mips_clockevent_device;
56 c0_compare_irqaction.flags &= ~IRQF_SHARED;
57 irq_set_handler(irq, handle_percpu_devid_irq);
58 irq_set_percpu_devid(irq);
59 setup_percpu_irq(irq, &c0_compare_irqaction);
60 enable_percpu_irq(irq, IRQ_TYPE_NONE);
61
62 ip30_heart_clocksource_init();
63}
diff --git a/arch/mips/sgi-ip30/ip30-xtalk.c b/arch/mips/sgi-ip30/ip30-xtalk.c
new file mode 100644
index 000000000..8a2894645
--- /dev/null
+++ b/arch/mips/sgi-ip30/ip30-xtalk.c
@@ -0,0 +1,152 @@
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * ip30-xtalk.c - Very basic Crosstalk (XIO) detection support.
4 * Copyright (C) 2004-2007 Stanislaw Skowronek <skylark@unaligned.org>
5 * Copyright (C) 2009 Johannes Dickgreber <tanzy@gmx.de>
6 * Copyright (C) 2007, 2014-2016 Joshua Kinard <kumba@gentoo.org>
7 */
8
9#include <linux/init.h>
10#include <linux/kernel.h>
11#include <linux/platform_device.h>
12#include <linux/platform_data/sgi-w1.h>
13#include <linux/platform_data/xtalk-bridge.h>
14
15#include <asm/xtalk/xwidget.h>
16#include <asm/pci/bridge.h>
17
18#define IP30_SWIN_BASE(widget) \
19 (0x0000000010000000 | (((unsigned long)(widget)) << 24))
20
21#define IP30_RAW_SWIN_BASE(widget) (IO_BASE + IP30_SWIN_BASE(widget))
22
23#define IP30_SWIN_SIZE (1 << 24)
24
25#define IP30_WIDGET_XBOW _AC(0x0, UL) /* XBow is always 0 */
26#define IP30_WIDGET_HEART _AC(0x8, UL) /* HEART is always 8 */
27#define IP30_WIDGET_PCI_BASE _AC(0xf, UL) /* BaseIO PCI is always 15 */
28
29#define XTALK_NODEV 0xffffffff
30
31#define XBOW_REG_LINK_STAT_0 0x114
32#define XBOW_REG_LINK_BLK_SIZE 0x40
33#define XBOW_REG_LINK_ALIVE 0x80000000
34
35#define HEART_INTR_ADDR 0x00000080
36
37#define xtalk_read __raw_readl
38
39static void bridge_platform_create(int widget, int masterwid)
40{
41 struct xtalk_bridge_platform_data *bd;
42 struct sgi_w1_platform_data *wd;
43 struct platform_device *pdev;
44 struct resource w1_res;
45
46 wd = kzalloc(sizeof(*wd), GFP_KERNEL);
47 if (!wd)
48 goto no_mem;
49
50 snprintf(wd->dev_id, sizeof(wd->dev_id), "bridge-%012lx",
51 IP30_SWIN_BASE(widget));
52
53 memset(&w1_res, 0, sizeof(w1_res));
54 w1_res.start = IP30_SWIN_BASE(widget) +
55 offsetof(struct bridge_regs, b_nic);
56 w1_res.end = w1_res.start + 3;
57 w1_res.flags = IORESOURCE_MEM;
58
59 pdev = platform_device_alloc("sgi_w1", PLATFORM_DEVID_AUTO);
60 if (!pdev) {
61 kfree(wd);
62 goto no_mem;
63 }
64 platform_device_add_resources(pdev, &w1_res, 1);
65 platform_device_add_data(pdev, wd, sizeof(*wd));
66 platform_device_add(pdev);
67
68 bd = kzalloc(sizeof(*bd), GFP_KERNEL);
69 if (!bd)
70 goto no_mem;
71 pdev = platform_device_alloc("xtalk-bridge", PLATFORM_DEVID_AUTO);
72 if (!pdev) {
73 kfree(bd);
74 goto no_mem;
75 }
76
77 bd->bridge_addr = IP30_RAW_SWIN_BASE(widget);
78 bd->intr_addr = HEART_INTR_ADDR;
79 bd->nasid = 0;
80 bd->masterwid = masterwid;
81
82 bd->mem.name = "Bridge PCI MEM";
83 bd->mem.start = IP30_SWIN_BASE(widget) + BRIDGE_DEVIO0;
84 bd->mem.end = IP30_SWIN_BASE(widget) + IP30_SWIN_SIZE - 1;
85 bd->mem.flags = IORESOURCE_MEM;
86 bd->mem_offset = IP30_SWIN_BASE(widget);
87
88 bd->io.name = "Bridge PCI IO";
89 bd->io.start = IP30_SWIN_BASE(widget) + BRIDGE_DEVIO0;
90 bd->io.end = IP30_SWIN_BASE(widget) + IP30_SWIN_SIZE - 1;
91 bd->io.flags = IORESOURCE_IO;
92 bd->io_offset = IP30_SWIN_BASE(widget);
93
94 platform_device_add_data(pdev, bd, sizeof(*bd));
95 platform_device_add(pdev);
96 pr_info("xtalk:%x bridge widget\n", widget);
97 return;
98
99no_mem:
100 pr_warn("xtalk:%x bridge create out of memory\n", widget);
101}
102
103static unsigned int __init xbow_widget_active(s8 wid)
104{
105 unsigned int link_stat;
106
107 link_stat = xtalk_read((void *)(IP30_RAW_SWIN_BASE(IP30_WIDGET_XBOW) +
108 XBOW_REG_LINK_STAT_0 +
109 XBOW_REG_LINK_BLK_SIZE *
110 (wid - 8)));
111
112 return (link_stat & XBOW_REG_LINK_ALIVE) ? 1 : 0;
113}
114
115static void __init xtalk_init_widget(s8 wid, s8 masterwid)
116{
117 xwidget_part_num_t partnum;
118 widgetreg_t widget_id;
119
120 if (!xbow_widget_active(wid))
121 return;
122
123 widget_id = xtalk_read((void *)(IP30_RAW_SWIN_BASE(wid) + WIDGET_ID));
124
125 partnum = XWIDGET_PART_NUM(widget_id);
126
127 switch (partnum) {
128 case BRIDGE_WIDGET_PART_NUM:
129 case XBRIDGE_WIDGET_PART_NUM:
130 bridge_platform_create(wid, masterwid);
131 break;
132 default:
133 pr_info("xtalk:%x unknown widget (0x%x)\n", wid, partnum);
134 break;
135 }
136}
137
138static int __init ip30_xtalk_init(void)
139{
140 int i;
141
142 /*
143 * Walk widget IDs backwards so that BaseIO is probed first. This
144 * ensures that the BaseIO IOC3 is always detected as eth0.
145 */
146 for (i = IP30_WIDGET_PCI_BASE; i > IP30_WIDGET_HEART; i--)
147 xtalk_init_widget(i, IP30_WIDGET_HEART);
148
149 return 0;
150}
151
152arch_initcall(ip30_xtalk_init);