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/sgi-ip22 | |
download | ohosKernel-a07bb8fd1299070229f0e8f3dcb57ffd5ef9870a.tar.gz ohosKernel-a07bb8fd1299070229f0e8f3dcb57ffd5ef9870a.zip |
Initial commit: OpenHarmony-v4.0-ReleaseOpenHarmony-v4.0-Release
Diffstat (limited to 'arch/mips/sgi-ip22')
-rw-r--r-- | arch/mips/sgi-ip22/Makefile | 12 | ||||
-rw-r--r-- | arch/mips/sgi-ip22/Platform | 32 | ||||
-rw-r--r-- | arch/mips/sgi-ip22/ip22-berr.c | 116 | ||||
-rw-r--r-- | arch/mips/sgi-ip22/ip22-eisa.c | 139 | ||||
-rw-r--r-- | arch/mips/sgi-ip22/ip22-gio.c | 431 | ||||
-rw-r--r-- | arch/mips/sgi-ip22/ip22-hpc.c | 64 | ||||
-rw-r--r-- | arch/mips/sgi-ip22/ip22-int.c | 320 | ||||
-rw-r--r-- | arch/mips/sgi-ip22/ip22-mc.c | 203 | ||||
-rw-r--r-- | arch/mips/sgi-ip22/ip22-nvram.c | 122 | ||||
-rw-r--r-- | arch/mips/sgi-ip22/ip22-platform.c | 223 | ||||
-rw-r--r-- | arch/mips/sgi-ip22/ip22-reset.c | 203 | ||||
-rw-r--r-- | arch/mips/sgi-ip22/ip22-setup.c | 78 | ||||
-rw-r--r-- | arch/mips/sgi-ip22/ip22-time.c | 131 | ||||
-rw-r--r-- | arch/mips/sgi-ip22/ip28-berr.c | 488 |
14 files changed, 2562 insertions, 0 deletions
diff --git a/arch/mips/sgi-ip22/Makefile b/arch/mips/sgi-ip22/Makefile new file mode 100644 index 000000000..45f42fa08 --- /dev/null +++ b/arch/mips/sgi-ip22/Makefile | |||
@@ -0,0 +1,12 @@ | |||
1 | # SPDX-License-Identifier: GPL-2.0 | ||
2 | # | ||
3 | # Makefile for the SGI specific kernel interface routines | ||
4 | # under Linux. | ||
5 | # | ||
6 | |||
7 | obj-y += ip22-mc.o ip22-hpc.o ip22-int.o ip22-time.o ip22-nvram.o \ | ||
8 | ip22-platform.o ip22-reset.o ip22-setup.o ip22-gio.o | ||
9 | |||
10 | obj-$(CONFIG_SGI_IP22) += ip22-berr.o | ||
11 | obj-$(CONFIG_SGI_IP28) += ip28-berr.o | ||
12 | obj-$(CONFIG_EISA) += ip22-eisa.o | ||
diff --git a/arch/mips/sgi-ip22/Platform b/arch/mips/sgi-ip22/Platform new file mode 100644 index 000000000..62fa30bb9 --- /dev/null +++ b/arch/mips/sgi-ip22/Platform | |||
@@ -0,0 +1,32 @@ | |||
1 | # | ||
2 | # SGI IP22 (Indy/Indigo2) | ||
3 | # | ||
4 | # Set the load address to >= 0xffffffff88069000 if you want to leave space for | ||
5 | # symmon, 0xffffffff80002000 for production kernels. Note that the value must | ||
6 | # be aligned to a multiple of the kernel stack size or the handling of the | ||
7 | # current variable will break so for 64-bit kernels we have to raise the start | ||
8 | # address by 8kb. | ||
9 | # | ||
10 | cflags-$(CONFIG_SGI_IP22) += -I$(srctree)/arch/mips/include/asm/mach-ip22 | ||
11 | ifdef CONFIG_32BIT | ||
12 | load-$(CONFIG_SGI_IP22) += 0xffffffff88002000 | ||
13 | endif | ||
14 | ifdef CONFIG_64BIT | ||
15 | load-$(CONFIG_SGI_IP22) += 0xffffffff88004000 | ||
16 | endif | ||
17 | |||
18 | # | ||
19 | # SGI IP28 (Indigo2 R10k) | ||
20 | # | ||
21 | # Set the load address to >= 0xa800000020080000 if you want to leave space for | ||
22 | # symmon, 0xa800000020004000 for production kernels ? Note that the value must | ||
23 | # be 16kb aligned or the handling of the current variable will break. | ||
24 | # Simplified: what IP22 does at 128MB+ in ksegN, IP28 does at 512MB+ in xkphys | ||
25 | # | ||
26 | ifdef CONFIG_SGI_IP28 | ||
27 | ifeq ($(call cc-option-yn,-march=r10000 -mr10k-cache-barrier=store), n) | ||
28 | $(error gcc doesn't support needed option -mr10k-cache-barrier=store) | ||
29 | endif | ||
30 | endif | ||
31 | cflags-$(CONFIG_SGI_IP28) += -mr10k-cache-barrier=store -I$(srctree)/arch/mips/include/asm/mach-ip28 | ||
32 | load-$(CONFIG_SGI_IP28) += 0xa800000020004000 | ||
diff --git a/arch/mips/sgi-ip22/ip22-berr.c b/arch/mips/sgi-ip22/ip22-berr.c new file mode 100644 index 000000000..dc0110a60 --- /dev/null +++ b/arch/mips/sgi-ip22/ip22-berr.c | |||
@@ -0,0 +1,116 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * ip22-berr.c: Bus error handling. | ||
4 | * | ||
5 | * Copyright (C) 2002, 2003 Ladislav Michl (ladis@linux-mips.org) | ||
6 | */ | ||
7 | |||
8 | #include <linux/init.h> | ||
9 | #include <linux/kernel.h> | ||
10 | #include <linux/sched/signal.h> | ||
11 | |||
12 | #include <asm/addrspace.h> | ||
13 | #include <asm/traps.h> | ||
14 | #include <asm/branch.h> | ||
15 | #include <asm/irq_regs.h> | ||
16 | #include <asm/sgi/mc.h> | ||
17 | #include <asm/sgi/hpc3.h> | ||
18 | #include <asm/sgi/ioc.h> | ||
19 | #include <asm/sgi/ip22.h> | ||
20 | |||
21 | |||
22 | static unsigned int cpu_err_stat; /* Status reg for CPU */ | ||
23 | static unsigned int gio_err_stat; /* Status reg for GIO */ | ||
24 | static unsigned int cpu_err_addr; /* Error address reg for CPU */ | ||
25 | static unsigned int gio_err_addr; /* Error address reg for GIO */ | ||
26 | static unsigned int extio_stat; | ||
27 | static unsigned int hpc3_berr_stat; /* Bus error interrupt status */ | ||
28 | |||
29 | static void save_and_clear_buserr(void) | ||
30 | { | ||
31 | /* save status registers */ | ||
32 | cpu_err_addr = sgimc->cerr; | ||
33 | cpu_err_stat = sgimc->cstat; | ||
34 | gio_err_addr = sgimc->gerr; | ||
35 | gio_err_stat = sgimc->gstat; | ||
36 | extio_stat = ip22_is_fullhouse() ? sgioc->extio : (sgint->errstat << 4); | ||
37 | hpc3_berr_stat = hpc3c0->bestat; | ||
38 | |||
39 | sgimc->cstat = sgimc->gstat = 0; | ||
40 | } | ||
41 | |||
42 | #define GIO_ERRMASK 0xff00 | ||
43 | #define CPU_ERRMASK 0x3f00 | ||
44 | |||
45 | static void print_buserr(void) | ||
46 | { | ||
47 | if (extio_stat & EXTIO_MC_BUSERR) | ||
48 | printk(KERN_ERR "MC Bus Error\n"); | ||
49 | if (extio_stat & EXTIO_HPC3_BUSERR) | ||
50 | printk(KERN_ERR "HPC3 Bus Error 0x%x:<id=0x%x,%s,lane=0x%x>\n", | ||
51 | hpc3_berr_stat, | ||
52 | (hpc3_berr_stat & HPC3_BESTAT_PIDMASK) >> | ||
53 | HPC3_BESTAT_PIDSHIFT, | ||
54 | (hpc3_berr_stat & HPC3_BESTAT_CTYPE) ? "PIO" : "DMA", | ||
55 | hpc3_berr_stat & HPC3_BESTAT_BLMASK); | ||
56 | if (extio_stat & EXTIO_EISA_BUSERR) | ||
57 | printk(KERN_ERR "EISA Bus Error\n"); | ||
58 | if (cpu_err_stat & CPU_ERRMASK) | ||
59 | printk(KERN_ERR "CPU error 0x%x<%s%s%s%s%s%s> @ 0x%08x\n", | ||
60 | cpu_err_stat, | ||
61 | cpu_err_stat & SGIMC_CSTAT_RD ? "RD " : "", | ||
62 | cpu_err_stat & SGIMC_CSTAT_PAR ? "PAR " : "", | ||
63 | cpu_err_stat & SGIMC_CSTAT_ADDR ? "ADDR " : "", | ||
64 | cpu_err_stat & SGIMC_CSTAT_SYSAD_PAR ? "SYSAD " : "", | ||
65 | cpu_err_stat & SGIMC_CSTAT_SYSCMD_PAR ? "SYSCMD " : "", | ||
66 | cpu_err_stat & SGIMC_CSTAT_BAD_DATA ? "BAD_DATA " : "", | ||
67 | cpu_err_addr); | ||
68 | if (gio_err_stat & GIO_ERRMASK) | ||
69 | printk(KERN_ERR "GIO error 0x%x:<%s%s%s%s%s%s%s%s> @ 0x%08x\n", | ||
70 | gio_err_stat, | ||
71 | gio_err_stat & SGIMC_GSTAT_RD ? "RD " : "", | ||
72 | gio_err_stat & SGIMC_GSTAT_WR ? "WR " : "", | ||
73 | gio_err_stat & SGIMC_GSTAT_TIME ? "TIME " : "", | ||
74 | gio_err_stat & SGIMC_GSTAT_PROM ? "PROM " : "", | ||
75 | gio_err_stat & SGIMC_GSTAT_ADDR ? "ADDR " : "", | ||
76 | gio_err_stat & SGIMC_GSTAT_BC ? "BC " : "", | ||
77 | gio_err_stat & SGIMC_GSTAT_PIO_RD ? "PIO_RD " : "", | ||
78 | gio_err_stat & SGIMC_GSTAT_PIO_WR ? "PIO_WR " : "", | ||
79 | gio_err_addr); | ||
80 | } | ||
81 | |||
82 | /* | ||
83 | * MC sends an interrupt whenever bus or parity errors occur. In addition, | ||
84 | * if the error happened during a CPU read, it also asserts the bus error | ||
85 | * pin on the R4K. Code in bus error handler save the MC bus error registers | ||
86 | * and then clear the interrupt when this happens. | ||
87 | */ | ||
88 | |||
89 | void ip22_be_interrupt(int irq) | ||
90 | { | ||
91 | const int field = 2 * sizeof(unsigned long); | ||
92 | struct pt_regs *regs = get_irq_regs(); | ||
93 | |||
94 | save_and_clear_buserr(); | ||
95 | print_buserr(); | ||
96 | printk(KERN_ALERT "%s bus error, epc == %0*lx, ra == %0*lx\n", | ||
97 | (regs->cp0_cause & 4) ? "Data" : "Instruction", | ||
98 | field, regs->cp0_epc, field, regs->regs[31]); | ||
99 | /* Assume it would be too dangerous to continue ... */ | ||
100 | die_if_kernel("Oops", regs); | ||
101 | force_sig(SIGBUS); | ||
102 | } | ||
103 | |||
104 | static int ip22_be_handler(struct pt_regs *regs, int is_fixup) | ||
105 | { | ||
106 | save_and_clear_buserr(); | ||
107 | if (is_fixup) | ||
108 | return MIPS_BE_FIXUP; | ||
109 | print_buserr(); | ||
110 | return MIPS_BE_FATAL; | ||
111 | } | ||
112 | |||
113 | void __init ip22_be_init(void) | ||
114 | { | ||
115 | board_be_handler = ip22_be_handler; | ||
116 | } | ||
diff --git a/arch/mips/sgi-ip22/ip22-eisa.c b/arch/mips/sgi-ip22/ip22-eisa.c new file mode 100644 index 000000000..f3b0e90e0 --- /dev/null +++ b/arch/mips/sgi-ip22/ip22-eisa.c | |||
@@ -0,0 +1,139 @@ | |||
1 | /* | ||
2 | * Basic EISA bus support for the SGI Indigo-2. | ||
3 | * | ||
4 | * (C) 2002 Pascal Dameme <netinet@freesurf.fr> | ||
5 | * and Marc Zyngier <mzyngier@freesurf.fr> | ||
6 | * | ||
7 | * This code is released under both the GPL version 2 and BSD | ||
8 | * licenses. Either license may be used. | ||
9 | * | ||
10 | * This code offers a very basic support for this EISA bus present in | ||
11 | * the SGI Indigo-2. It currently only supports PIO (forget about DMA | ||
12 | * for the time being). This is enough for a low-end ethernet card, | ||
13 | * but forget about your favorite SCSI card... | ||
14 | * | ||
15 | * TODO : | ||
16 | * - Fix bugs... | ||
17 | * - Add ISA support | ||
18 | * - Add DMA (yeah, right...). | ||
19 | * - Fix more bugs. | ||
20 | */ | ||
21 | |||
22 | #include <linux/eisa.h> | ||
23 | #include <linux/types.h> | ||
24 | #include <linux/init.h> | ||
25 | #include <linux/irq.h> | ||
26 | #include <linux/kernel_stat.h> | ||
27 | #include <linux/signal.h> | ||
28 | #include <linux/sched.h> | ||
29 | #include <linux/interrupt.h> | ||
30 | #include <linux/delay.h> | ||
31 | #include <asm/io.h> | ||
32 | #include <asm/irq.h> | ||
33 | #include <asm/mipsregs.h> | ||
34 | #include <asm/addrspace.h> | ||
35 | #include <asm/processor.h> | ||
36 | #include <asm/sgi/ioc.h> | ||
37 | #include <asm/sgi/mc.h> | ||
38 | #include <asm/sgi/ip22.h> | ||
39 | #include <asm/i8259.h> | ||
40 | |||
41 | /* I2 has four EISA slots. */ | ||
42 | #define IP22_EISA_MAX_SLOTS 4 | ||
43 | #define EISA_MAX_IRQ 16 | ||
44 | |||
45 | #define EIU_MODE_REG 0x0001ffc0 | ||
46 | #define EIU_STAT_REG 0x0001ffc4 | ||
47 | #define EIU_PREMPT_REG 0x0001ffc8 | ||
48 | #define EIU_QUIET_REG 0x0001ffcc | ||
49 | #define EIU_INTRPT_ACK 0x00010004 | ||
50 | |||
51 | static char __init *decode_eisa_sig(unsigned long addr) | ||
52 | { | ||
53 | static char sig_str[EISA_SIG_LEN] __initdata; | ||
54 | u8 sig[4]; | ||
55 | u16 rev; | ||
56 | int i; | ||
57 | |||
58 | for (i = 0; i < 4; i++) { | ||
59 | sig[i] = inb(addr + i); | ||
60 | |||
61 | if (!i && (sig[0] & 0x80)) | ||
62 | return NULL; | ||
63 | } | ||
64 | |||
65 | sig_str[0] = ((sig[0] >> 2) & 0x1f) + ('A' - 1); | ||
66 | sig_str[1] = (((sig[0] & 3) << 3) | (sig[1] >> 5)) + ('A' - 1); | ||
67 | sig_str[2] = (sig[1] & 0x1f) + ('A' - 1); | ||
68 | rev = (sig[2] << 8) | sig[3]; | ||
69 | sprintf(sig_str + 3, "%04X", rev); | ||
70 | |||
71 | return sig_str; | ||
72 | } | ||
73 | |||
74 | static irqreturn_t ip22_eisa_intr(int irq, void *dev_id) | ||
75 | { | ||
76 | u8 eisa_irq = inb(EIU_INTRPT_ACK); | ||
77 | |||
78 | inb(EISA_DMA1_STATUS); | ||
79 | inb(EISA_DMA2_STATUS); | ||
80 | |||
81 | if (eisa_irq < EISA_MAX_IRQ) { | ||
82 | do_IRQ(eisa_irq); | ||
83 | return IRQ_HANDLED; | ||
84 | } | ||
85 | |||
86 | /* Oops, Bad Stuff Happened... */ | ||
87 | printk(KERN_ERR "eisa_irq %d out of bound\n", eisa_irq); | ||
88 | |||
89 | outb(0x20, EISA_INT2_CTRL); | ||
90 | outb(0x20, EISA_INT1_CTRL); | ||
91 | |||
92 | return IRQ_NONE; | ||
93 | } | ||
94 | |||
95 | int __init ip22_eisa_init(void) | ||
96 | { | ||
97 | int i, c; | ||
98 | char *str; | ||
99 | |||
100 | if (!(sgimc->systemid & SGIMC_SYSID_EPRESENT)) { | ||
101 | printk(KERN_INFO "EISA: bus not present.\n"); | ||
102 | return 1; | ||
103 | } | ||
104 | |||
105 | printk(KERN_INFO "EISA: Probing bus...\n"); | ||
106 | for (c = 0, i = 1; i <= IP22_EISA_MAX_SLOTS; i++) { | ||
107 | if ((str = decode_eisa_sig(0x1000 * i + EISA_VENDOR_ID_OFFSET))) { | ||
108 | printk(KERN_INFO "EISA: slot %d : %s detected.\n", | ||
109 | i, str); | ||
110 | c++; | ||
111 | } | ||
112 | } | ||
113 | printk(KERN_INFO "EISA: Detected %d card%s.\n", c, c < 2 ? "" : "s"); | ||
114 | #ifdef CONFIG_ISA | ||
115 | printk(KERN_INFO "ISA support compiled in.\n"); | ||
116 | #endif | ||
117 | |||
118 | /* Warning : BlackMagicAhead(tm). | ||
119 | Please wave your favorite dead chicken over the busses */ | ||
120 | |||
121 | /* First say hello to the EIU */ | ||
122 | outl(0x0000FFFF, EIU_PREMPT_REG); | ||
123 | outl(1, EIU_QUIET_REG); | ||
124 | outl(0x40f3c07F, EIU_MODE_REG); | ||
125 | |||
126 | /* Now be nice to the EISA chipset */ | ||
127 | outb(1, EISA_EXT_NMI_RESET_CTRL); | ||
128 | udelay(50); /* Wait long enough for the dust to settle */ | ||
129 | outb(0, EISA_EXT_NMI_RESET_CTRL); | ||
130 | outb(0, EISA_DMA2_WRITE_SINGLE); | ||
131 | |||
132 | init_i8259_irqs(); | ||
133 | |||
134 | if (request_irq(SGI_EISA_IRQ, ip22_eisa_intr, 0, "EISA", NULL)) | ||
135 | pr_err("Failed to request irq %d (EISA)\n", SGI_EISA_IRQ); | ||
136 | |||
137 | EISA_bus = 1; | ||
138 | return 0; | ||
139 | } | ||
diff --git a/arch/mips/sgi-ip22/ip22-gio.c b/arch/mips/sgi-ip22/ip22-gio.c new file mode 100644 index 000000000..de0768a49 --- /dev/null +++ b/arch/mips/sgi-ip22/ip22-gio.c | |||
@@ -0,0 +1,431 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0-only | ||
2 | #include <linux/export.h> | ||
3 | #include <linux/kernel.h> | ||
4 | #include <linux/init.h> | ||
5 | #include <linux/slab.h> | ||
6 | |||
7 | #include <asm/addrspace.h> | ||
8 | #include <asm/paccess.h> | ||
9 | #include <asm/gio_device.h> | ||
10 | #include <asm/sgi/gio.h> | ||
11 | #include <asm/sgi/hpc3.h> | ||
12 | #include <asm/sgi/mc.h> | ||
13 | #include <asm/sgi/ip22.h> | ||
14 | |||
15 | static struct bus_type gio_bus_type; | ||
16 | |||
17 | static struct { | ||
18 | const char *name; | ||
19 | __u8 id; | ||
20 | } gio_name_table[] = { | ||
21 | { .name = "SGI Impact", .id = 0x10 }, | ||
22 | { .name = "Phobos G160", .id = 0x35 }, | ||
23 | { .name = "Phobos G130", .id = 0x36 }, | ||
24 | { .name = "Phobos G100", .id = 0x37 }, | ||
25 | { .name = "Set Engineering GFE", .id = 0x38 }, | ||
26 | /* fake IDs */ | ||
27 | { .name = "SGI Newport", .id = 0x7e }, | ||
28 | { .name = "SGI GR2/GR3", .id = 0x7f }, | ||
29 | }; | ||
30 | |||
31 | static void gio_bus_release(struct device *dev) | ||
32 | { | ||
33 | kfree(dev); | ||
34 | } | ||
35 | |||
36 | static struct device gio_bus = { | ||
37 | .init_name = "gio", | ||
38 | .release = &gio_bus_release, | ||
39 | }; | ||
40 | |||
41 | /** | ||
42 | * gio_match_device - Tell if an of_device structure has a matching | ||
43 | * gio_match structure | ||
44 | * @ids: array of of device match structures to search in | ||
45 | * @dev: the of device structure to match against | ||
46 | * | ||
47 | * Used by a driver to check whether an of_device present in the | ||
48 | * system is in its list of supported devices. | ||
49 | */ | ||
50 | static const struct gio_device_id * | ||
51 | gio_match_device(const struct gio_device_id *match, | ||
52 | const struct gio_device *dev) | ||
53 | { | ||
54 | const struct gio_device_id *ids; | ||
55 | |||
56 | for (ids = match; ids->id != 0xff; ids++) | ||
57 | if (ids->id == dev->id.id) | ||
58 | return ids; | ||
59 | |||
60 | return NULL; | ||
61 | } | ||
62 | |||
63 | struct gio_device *gio_dev_get(struct gio_device *dev) | ||
64 | { | ||
65 | struct device *tmp; | ||
66 | |||
67 | if (!dev) | ||
68 | return NULL; | ||
69 | tmp = get_device(&dev->dev); | ||
70 | if (tmp) | ||
71 | return to_gio_device(tmp); | ||
72 | else | ||
73 | return NULL; | ||
74 | } | ||
75 | EXPORT_SYMBOL_GPL(gio_dev_get); | ||
76 | |||
77 | void gio_dev_put(struct gio_device *dev) | ||
78 | { | ||
79 | if (dev) | ||
80 | put_device(&dev->dev); | ||
81 | } | ||
82 | EXPORT_SYMBOL_GPL(gio_dev_put); | ||
83 | |||
84 | /** | ||
85 | * gio_release_dev - free an gio device structure when all users of it are finished. | ||
86 | * @dev: device that's been disconnected | ||
87 | * | ||
88 | * Will be called only by the device core when all users of this gio device are | ||
89 | * done. | ||
90 | */ | ||
91 | void gio_release_dev(struct device *dev) | ||
92 | { | ||
93 | struct gio_device *giodev; | ||
94 | |||
95 | giodev = to_gio_device(dev); | ||
96 | kfree(giodev); | ||
97 | } | ||
98 | EXPORT_SYMBOL_GPL(gio_release_dev); | ||
99 | |||
100 | int gio_device_register(struct gio_device *giodev) | ||
101 | { | ||
102 | giodev->dev.bus = &gio_bus_type; | ||
103 | giodev->dev.parent = &gio_bus; | ||
104 | return device_register(&giodev->dev); | ||
105 | } | ||
106 | EXPORT_SYMBOL_GPL(gio_device_register); | ||
107 | |||
108 | void gio_device_unregister(struct gio_device *giodev) | ||
109 | { | ||
110 | device_unregister(&giodev->dev); | ||
111 | } | ||
112 | EXPORT_SYMBOL_GPL(gio_device_unregister); | ||
113 | |||
114 | static int gio_bus_match(struct device *dev, struct device_driver *drv) | ||
115 | { | ||
116 | struct gio_device *gio_dev = to_gio_device(dev); | ||
117 | struct gio_driver *gio_drv = to_gio_driver(drv); | ||
118 | |||
119 | return gio_match_device(gio_drv->id_table, gio_dev) != NULL; | ||
120 | } | ||
121 | |||
122 | static int gio_device_probe(struct device *dev) | ||
123 | { | ||
124 | int error = -ENODEV; | ||
125 | struct gio_driver *drv; | ||
126 | struct gio_device *gio_dev; | ||
127 | const struct gio_device_id *match; | ||
128 | |||
129 | drv = to_gio_driver(dev->driver); | ||
130 | gio_dev = to_gio_device(dev); | ||
131 | |||
132 | if (!drv->probe) | ||
133 | return error; | ||
134 | |||
135 | gio_dev_get(gio_dev); | ||
136 | |||
137 | match = gio_match_device(drv->id_table, gio_dev); | ||
138 | if (match) | ||
139 | error = drv->probe(gio_dev, match); | ||
140 | if (error) | ||
141 | gio_dev_put(gio_dev); | ||
142 | |||
143 | return error; | ||
144 | } | ||
145 | |||
146 | static int gio_device_remove(struct device *dev) | ||
147 | { | ||
148 | struct gio_device *gio_dev = to_gio_device(dev); | ||
149 | struct gio_driver *drv = to_gio_driver(dev->driver); | ||
150 | |||
151 | if (dev->driver && drv->remove) | ||
152 | drv->remove(gio_dev); | ||
153 | return 0; | ||
154 | } | ||
155 | |||
156 | static void gio_device_shutdown(struct device *dev) | ||
157 | { | ||
158 | struct gio_device *gio_dev = to_gio_device(dev); | ||
159 | struct gio_driver *drv = to_gio_driver(dev->driver); | ||
160 | |||
161 | if (dev->driver && drv->shutdown) | ||
162 | drv->shutdown(gio_dev); | ||
163 | } | ||
164 | |||
165 | static ssize_t modalias_show(struct device *dev, struct device_attribute *a, | ||
166 | char *buf) | ||
167 | { | ||
168 | struct gio_device *gio_dev = to_gio_device(dev); | ||
169 | int len = snprintf(buf, PAGE_SIZE, "gio:%x\n", gio_dev->id.id); | ||
170 | |||
171 | return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len; | ||
172 | } | ||
173 | static DEVICE_ATTR_RO(modalias); | ||
174 | |||
175 | static ssize_t name_show(struct device *dev, | ||
176 | struct device_attribute *attr, char *buf) | ||
177 | { | ||
178 | struct gio_device *giodev; | ||
179 | |||
180 | giodev = to_gio_device(dev); | ||
181 | return sprintf(buf, "%s", giodev->name); | ||
182 | } | ||
183 | static DEVICE_ATTR_RO(name); | ||
184 | |||
185 | static ssize_t id_show(struct device *dev, | ||
186 | struct device_attribute *attr, char *buf) | ||
187 | { | ||
188 | struct gio_device *giodev; | ||
189 | |||
190 | giodev = to_gio_device(dev); | ||
191 | return sprintf(buf, "%x", giodev->id.id); | ||
192 | } | ||
193 | static DEVICE_ATTR_RO(id); | ||
194 | |||
195 | static struct attribute *gio_dev_attrs[] = { | ||
196 | &dev_attr_modalias.attr, | ||
197 | &dev_attr_name.attr, | ||
198 | &dev_attr_id.attr, | ||
199 | NULL, | ||
200 | }; | ||
201 | ATTRIBUTE_GROUPS(gio_dev); | ||
202 | |||
203 | static int gio_device_uevent(struct device *dev, struct kobj_uevent_env *env) | ||
204 | { | ||
205 | struct gio_device *gio_dev = to_gio_device(dev); | ||
206 | |||
207 | add_uevent_var(env, "MODALIAS=gio:%x", gio_dev->id.id); | ||
208 | return 0; | ||
209 | } | ||
210 | |||
211 | int gio_register_driver(struct gio_driver *drv) | ||
212 | { | ||
213 | /* initialize common driver fields */ | ||
214 | if (!drv->driver.name) | ||
215 | drv->driver.name = drv->name; | ||
216 | if (!drv->driver.owner) | ||
217 | drv->driver.owner = drv->owner; | ||
218 | drv->driver.bus = &gio_bus_type; | ||
219 | |||
220 | /* register with core */ | ||
221 | return driver_register(&drv->driver); | ||
222 | } | ||
223 | EXPORT_SYMBOL_GPL(gio_register_driver); | ||
224 | |||
225 | void gio_unregister_driver(struct gio_driver *drv) | ||
226 | { | ||
227 | driver_unregister(&drv->driver); | ||
228 | } | ||
229 | EXPORT_SYMBOL_GPL(gio_unregister_driver); | ||
230 | |||
231 | void gio_set_master(struct gio_device *dev) | ||
232 | { | ||
233 | u32 tmp = sgimc->giopar; | ||
234 | |||
235 | switch (dev->slotno) { | ||
236 | case 0: | ||
237 | tmp |= SGIMC_GIOPAR_MASTERGFX; | ||
238 | break; | ||
239 | case 1: | ||
240 | tmp |= SGIMC_GIOPAR_MASTEREXP0; | ||
241 | break; | ||
242 | case 2: | ||
243 | tmp |= SGIMC_GIOPAR_MASTEREXP1; | ||
244 | break; | ||
245 | } | ||
246 | sgimc->giopar = tmp; | ||
247 | } | ||
248 | EXPORT_SYMBOL_GPL(gio_set_master); | ||
249 | |||
250 | void ip22_gio_set_64bit(int slotno) | ||
251 | { | ||
252 | u32 tmp = sgimc->giopar; | ||
253 | |||
254 | switch (slotno) { | ||
255 | case 0: | ||
256 | tmp |= SGIMC_GIOPAR_GFX64; | ||
257 | break; | ||
258 | case 1: | ||
259 | tmp |= SGIMC_GIOPAR_EXP064; | ||
260 | break; | ||
261 | case 2: | ||
262 | tmp |= SGIMC_GIOPAR_EXP164; | ||
263 | break; | ||
264 | } | ||
265 | sgimc->giopar = tmp; | ||
266 | } | ||
267 | |||
268 | static int ip22_gio_id(unsigned long addr, u32 *res) | ||
269 | { | ||
270 | u8 tmp8; | ||
271 | u8 tmp16; | ||
272 | u32 tmp32; | ||
273 | u8 *ptr8; | ||
274 | u16 *ptr16; | ||
275 | u32 *ptr32; | ||
276 | |||
277 | ptr32 = (void *)CKSEG1ADDR(addr); | ||
278 | if (!get_dbe(tmp32, ptr32)) { | ||
279 | /* | ||
280 | * We got no DBE, but this doesn't mean anything. | ||
281 | * If GIO is pipelined (which can't be disabled | ||
282 | * for GFX slot) we don't get a DBE, but we see | ||
283 | * the transfer size as data. So we do an 8bit | ||
284 | * and a 16bit access and check whether the common | ||
285 | * data matches | ||
286 | */ | ||
287 | ptr8 = (void *)CKSEG1ADDR(addr + 3); | ||
288 | if (get_dbe(tmp8, ptr8)) { | ||
289 | /* | ||
290 | * 32bit access worked, but 8bit doesn't | ||
291 | * so we don't see phantom reads on | ||
292 | * a pipelined bus, but a real card which | ||
293 | * doesn't support 8 bit reads | ||
294 | */ | ||
295 | *res = tmp32; | ||
296 | return 1; | ||
297 | } | ||
298 | ptr16 = (void *)CKSEG1ADDR(addr + 2); | ||
299 | get_dbe(tmp16, ptr16); | ||
300 | if (tmp8 == (tmp16 & 0xff) && | ||
301 | tmp8 == (tmp32 & 0xff) && | ||
302 | tmp16 == (tmp32 & 0xffff)) { | ||
303 | *res = tmp32; | ||
304 | return 1; | ||
305 | } | ||
306 | } | ||
307 | return 0; /* nothing here */ | ||
308 | } | ||
309 | |||
310 | #define HQ2_MYSTERY_OFFS 0x6A07C | ||
311 | #define NEWPORT_USTATUS_OFFS 0xF133C | ||
312 | |||
313 | static int ip22_is_gr2(unsigned long addr) | ||
314 | { | ||
315 | u32 tmp; | ||
316 | u32 *ptr; | ||
317 | |||
318 | /* HQ2 only allows 32bit accesses */ | ||
319 | ptr = (void *)CKSEG1ADDR(addr + HQ2_MYSTERY_OFFS); | ||
320 | if (!get_dbe(tmp, ptr)) { | ||
321 | if (tmp == 0xdeadbeef) | ||
322 | return 1; | ||
323 | } | ||
324 | return 0; | ||
325 | } | ||
326 | |||
327 | |||
328 | static void ip22_check_gio(int slotno, unsigned long addr, int irq) | ||
329 | { | ||
330 | const char *name = "Unknown"; | ||
331 | struct gio_device *gio_dev; | ||
332 | u32 tmp; | ||
333 | __u8 id; | ||
334 | int i; | ||
335 | |||
336 | /* first look for GR2/GR3 by checking mystery register */ | ||
337 | if (ip22_is_gr2(addr)) | ||
338 | tmp = 0x7f; | ||
339 | else { | ||
340 | if (!ip22_gio_id(addr, &tmp)) { | ||
341 | /* | ||
342 | * no GIO signature at start address of slot | ||
343 | * since Newport doesn't have one, we check if | ||
344 | * user status register is readable | ||
345 | */ | ||
346 | if (ip22_gio_id(addr + NEWPORT_USTATUS_OFFS, &tmp)) | ||
347 | tmp = 0x7e; | ||
348 | else | ||
349 | tmp = 0; | ||
350 | } | ||
351 | } | ||
352 | if (tmp) { | ||
353 | id = GIO_ID(tmp); | ||
354 | if (tmp & GIO_32BIT_ID) { | ||
355 | if (tmp & GIO_64BIT_IFACE) | ||
356 | ip22_gio_set_64bit(slotno); | ||
357 | } | ||
358 | for (i = 0; i < ARRAY_SIZE(gio_name_table); i++) { | ||
359 | if (id == gio_name_table[i].id) { | ||
360 | name = gio_name_table[i].name; | ||
361 | break; | ||
362 | } | ||
363 | } | ||
364 | printk(KERN_INFO "GIO: slot %d : %s (id %x)\n", | ||
365 | slotno, name, id); | ||
366 | gio_dev = kzalloc(sizeof *gio_dev, GFP_KERNEL); | ||
367 | gio_dev->name = name; | ||
368 | gio_dev->slotno = slotno; | ||
369 | gio_dev->id.id = id; | ||
370 | gio_dev->resource.start = addr; | ||
371 | gio_dev->resource.end = addr + 0x3fffff; | ||
372 | gio_dev->resource.flags = IORESOURCE_MEM; | ||
373 | gio_dev->irq = irq; | ||
374 | dev_set_name(&gio_dev->dev, "%d", slotno); | ||
375 | gio_device_register(gio_dev); | ||
376 | } else | ||
377 | printk(KERN_INFO "GIO: slot %d : Empty\n", slotno); | ||
378 | } | ||
379 | |||
380 | static struct bus_type gio_bus_type = { | ||
381 | .name = "gio", | ||
382 | .dev_groups = gio_dev_groups, | ||
383 | .match = gio_bus_match, | ||
384 | .probe = gio_device_probe, | ||
385 | .remove = gio_device_remove, | ||
386 | .shutdown = gio_device_shutdown, | ||
387 | .uevent = gio_device_uevent, | ||
388 | }; | ||
389 | |||
390 | static struct resource gio_bus_resource = { | ||
391 | .start = GIO_SLOT_GFX_BASE, | ||
392 | .end = GIO_SLOT_GFX_BASE + 0x9fffff, | ||
393 | .name = "GIO Bus", | ||
394 | .flags = IORESOURCE_MEM, | ||
395 | }; | ||
396 | |||
397 | int __init ip22_gio_init(void) | ||
398 | { | ||
399 | unsigned int pbdma __maybe_unused; | ||
400 | int ret; | ||
401 | |||
402 | ret = device_register(&gio_bus); | ||
403 | if (ret) { | ||
404 | put_device(&gio_bus); | ||
405 | return ret; | ||
406 | } | ||
407 | |||
408 | ret = bus_register(&gio_bus_type); | ||
409 | if (!ret) { | ||
410 | request_resource(&iomem_resource, &gio_bus_resource); | ||
411 | printk(KERN_INFO "GIO: Probing bus...\n"); | ||
412 | |||
413 | if (ip22_is_fullhouse()) { | ||
414 | /* Indigo2 */ | ||
415 | ip22_check_gio(0, GIO_SLOT_GFX_BASE, SGI_GIO_1_IRQ); | ||
416 | ip22_check_gio(1, GIO_SLOT_EXP0_BASE, SGI_GIO_1_IRQ); | ||
417 | } else { | ||
418 | /* Indy/Challenge S */ | ||
419 | if (get_dbe(pbdma, (unsigned int *)&hpc3c1->pbdma[1])) | ||
420 | ip22_check_gio(0, GIO_SLOT_GFX_BASE, | ||
421 | SGI_GIO_0_IRQ); | ||
422 | ip22_check_gio(1, GIO_SLOT_EXP0_BASE, SGI_GIOEXP0_IRQ); | ||
423 | ip22_check_gio(2, GIO_SLOT_EXP1_BASE, SGI_GIOEXP1_IRQ); | ||
424 | } | ||
425 | } else | ||
426 | device_unregister(&gio_bus); | ||
427 | |||
428 | return ret; | ||
429 | } | ||
430 | |||
431 | subsys_initcall(ip22_gio_init); | ||
diff --git a/arch/mips/sgi-ip22/ip22-hpc.c b/arch/mips/sgi-ip22/ip22-hpc.c new file mode 100644 index 000000000..49922e86c --- /dev/null +++ b/arch/mips/sgi-ip22/ip22-hpc.c | |||
@@ -0,0 +1,64 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * ip22-hpc.c: Routines for generic manipulation of the HPC controllers. | ||
4 | * | ||
5 | * Copyright (C) 1996 David S. Miller (davem@davemloft.net) | ||
6 | * Copyright (C) 1998 Ralf Baechle | ||
7 | */ | ||
8 | |||
9 | #include <linux/export.h> | ||
10 | #include <linux/init.h> | ||
11 | #include <linux/types.h> | ||
12 | |||
13 | #include <asm/io.h> | ||
14 | #include <asm/sgi/hpc3.h> | ||
15 | #include <asm/sgi/ioc.h> | ||
16 | #include <asm/sgi/ip22.h> | ||
17 | |||
18 | struct hpc3_regs *hpc3c0, *hpc3c1; | ||
19 | |||
20 | EXPORT_SYMBOL(hpc3c0); | ||
21 | EXPORT_SYMBOL(hpc3c1); | ||
22 | |||
23 | struct sgioc_regs *sgioc; | ||
24 | |||
25 | EXPORT_SYMBOL(sgioc); | ||
26 | |||
27 | /* We need software copies of these because they are write only. */ | ||
28 | u8 sgi_ioc_reset, sgi_ioc_write; | ||
29 | |||
30 | extern char *system_type; | ||
31 | |||
32 | void __init sgihpc_init(void) | ||
33 | { | ||
34 | /* ioremap can't fail */ | ||
35 | hpc3c0 = (struct hpc3_regs *) | ||
36 | ioremap(HPC3_CHIP0_BASE, sizeof(struct hpc3_regs)); | ||
37 | hpc3c1 = (struct hpc3_regs *) | ||
38 | ioremap(HPC3_CHIP1_BASE, sizeof(struct hpc3_regs)); | ||
39 | /* IOC lives in PBUS PIO channel 6 */ | ||
40 | sgioc = (struct sgioc_regs *)hpc3c0->pbus_extregs[6]; | ||
41 | |||
42 | hpc3c0->pbus_piocfg[6][0] |= HPC3_PIOCFG_DS16; | ||
43 | if (ip22_is_fullhouse()) { | ||
44 | /* Full House comes with INT2 which lives in PBUS PIO | ||
45 | * channel 4 */ | ||
46 | sgint = (struct sgint_regs *)hpc3c0->pbus_extregs[4]; | ||
47 | system_type = "SGI Indigo2"; | ||
48 | } else { | ||
49 | /* Guiness comes with INT3 which is part of IOC */ | ||
50 | sgint = &sgioc->int3; | ||
51 | system_type = "SGI Indy"; | ||
52 | } | ||
53 | |||
54 | sgi_ioc_reset = (SGIOC_RESET_PPORT | SGIOC_RESET_KBDMOUSE | | ||
55 | SGIOC_RESET_EISA | SGIOC_RESET_ISDN | | ||
56 | SGIOC_RESET_LC0OFF); | ||
57 | |||
58 | sgi_ioc_write = (SGIOC_WRITE_EASEL | SGIOC_WRITE_NTHRESH | | ||
59 | SGIOC_WRITE_TPSPEED | SGIOC_WRITE_EPSEL | | ||
60 | SGIOC_WRITE_U0AMODE | SGIOC_WRITE_U1AMODE); | ||
61 | |||
62 | sgioc->reset = sgi_ioc_reset; | ||
63 | sgioc->write = sgi_ioc_write; | ||
64 | } | ||
diff --git a/arch/mips/sgi-ip22/ip22-int.c b/arch/mips/sgi-ip22/ip22-int.c new file mode 100644 index 000000000..96798a4ab --- /dev/null +++ b/arch/mips/sgi-ip22/ip22-int.c | |||
@@ -0,0 +1,320 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * ip22-int.c: Routines for generic manipulation of the INT[23] ASIC | ||
4 | * found on INDY and Indigo2 workstations. | ||
5 | * | ||
6 | * Copyright (C) 1996 David S. Miller (davem@davemloft.net) | ||
7 | * Copyright (C) 1997, 1998 Ralf Baechle (ralf@gnu.org) | ||
8 | * Copyright (C) 1999 Andrew R. Baker (andrewb@uab.edu) | ||
9 | * - Indigo2 changes | ||
10 | * - Interrupt handling fixes | ||
11 | * Copyright (C) 2001, 2003 Ladislav Michl (ladis@linux-mips.org) | ||
12 | */ | ||
13 | #include <linux/types.h> | ||
14 | #include <linux/init.h> | ||
15 | #include <linux/kernel_stat.h> | ||
16 | #include <linux/interrupt.h> | ||
17 | #include <linux/ftrace.h> | ||
18 | |||
19 | #include <asm/irq_cpu.h> | ||
20 | #include <asm/sgi/hpc3.h> | ||
21 | #include <asm/sgi/ip22.h> | ||
22 | |||
23 | /* So far nothing hangs here */ | ||
24 | #undef USE_LIO3_IRQ | ||
25 | |||
26 | struct sgint_regs *sgint; | ||
27 | |||
28 | static char lc0msk_to_irqnr[256]; | ||
29 | static char lc1msk_to_irqnr[256]; | ||
30 | static char lc2msk_to_irqnr[256]; | ||
31 | static char lc3msk_to_irqnr[256]; | ||
32 | |||
33 | extern int ip22_eisa_init(void); | ||
34 | |||
35 | static void enable_local0_irq(struct irq_data *d) | ||
36 | { | ||
37 | /* don't allow mappable interrupt to be enabled from setup_irq, | ||
38 | * we have our own way to do so */ | ||
39 | if (d->irq != SGI_MAP_0_IRQ) | ||
40 | sgint->imask0 |= (1 << (d->irq - SGINT_LOCAL0)); | ||
41 | } | ||
42 | |||
43 | static void disable_local0_irq(struct irq_data *d) | ||
44 | { | ||
45 | sgint->imask0 &= ~(1 << (d->irq - SGINT_LOCAL0)); | ||
46 | } | ||
47 | |||
48 | static struct irq_chip ip22_local0_irq_type = { | ||
49 | .name = "IP22 local 0", | ||
50 | .irq_mask = disable_local0_irq, | ||
51 | .irq_unmask = enable_local0_irq, | ||
52 | }; | ||
53 | |||
54 | static void enable_local1_irq(struct irq_data *d) | ||
55 | { | ||
56 | /* don't allow mappable interrupt to be enabled from setup_irq, | ||
57 | * we have our own way to do so */ | ||
58 | if (d->irq != SGI_MAP_1_IRQ) | ||
59 | sgint->imask1 |= (1 << (d->irq - SGINT_LOCAL1)); | ||
60 | } | ||
61 | |||
62 | static void disable_local1_irq(struct irq_data *d) | ||
63 | { | ||
64 | sgint->imask1 &= ~(1 << (d->irq - SGINT_LOCAL1)); | ||
65 | } | ||
66 | |||
67 | static struct irq_chip ip22_local1_irq_type = { | ||
68 | .name = "IP22 local 1", | ||
69 | .irq_mask = disable_local1_irq, | ||
70 | .irq_unmask = enable_local1_irq, | ||
71 | }; | ||
72 | |||
73 | static void enable_local2_irq(struct irq_data *d) | ||
74 | { | ||
75 | sgint->imask0 |= (1 << (SGI_MAP_0_IRQ - SGINT_LOCAL0)); | ||
76 | sgint->cmeimask0 |= (1 << (d->irq - SGINT_LOCAL2)); | ||
77 | } | ||
78 | |||
79 | static void disable_local2_irq(struct irq_data *d) | ||
80 | { | ||
81 | sgint->cmeimask0 &= ~(1 << (d->irq - SGINT_LOCAL2)); | ||
82 | if (!sgint->cmeimask0) | ||
83 | sgint->imask0 &= ~(1 << (SGI_MAP_0_IRQ - SGINT_LOCAL0)); | ||
84 | } | ||
85 | |||
86 | static struct irq_chip ip22_local2_irq_type = { | ||
87 | .name = "IP22 local 2", | ||
88 | .irq_mask = disable_local2_irq, | ||
89 | .irq_unmask = enable_local2_irq, | ||
90 | }; | ||
91 | |||
92 | static void enable_local3_irq(struct irq_data *d) | ||
93 | { | ||
94 | sgint->imask1 |= (1 << (SGI_MAP_1_IRQ - SGINT_LOCAL1)); | ||
95 | sgint->cmeimask1 |= (1 << (d->irq - SGINT_LOCAL3)); | ||
96 | } | ||
97 | |||
98 | static void disable_local3_irq(struct irq_data *d) | ||
99 | { | ||
100 | sgint->cmeimask1 &= ~(1 << (d->irq - SGINT_LOCAL3)); | ||
101 | if (!sgint->cmeimask1) | ||
102 | sgint->imask1 &= ~(1 << (SGI_MAP_1_IRQ - SGINT_LOCAL1)); | ||
103 | } | ||
104 | |||
105 | static struct irq_chip ip22_local3_irq_type = { | ||
106 | .name = "IP22 local 3", | ||
107 | .irq_mask = disable_local3_irq, | ||
108 | .irq_unmask = enable_local3_irq, | ||
109 | }; | ||
110 | |||
111 | static void indy_local0_irqdispatch(void) | ||
112 | { | ||
113 | u8 mask = sgint->istat0 & sgint->imask0; | ||
114 | u8 mask2; | ||
115 | int irq; | ||
116 | |||
117 | if (mask & SGINT_ISTAT0_LIO2) { | ||
118 | mask2 = sgint->vmeistat & sgint->cmeimask0; | ||
119 | irq = lc2msk_to_irqnr[mask2]; | ||
120 | } else | ||
121 | irq = lc0msk_to_irqnr[mask]; | ||
122 | |||
123 | /* | ||
124 | * workaround for INT2 bug; if irq == 0, INT2 has seen a fifo full | ||
125 | * irq, but failed to latch it into status register | ||
126 | */ | ||
127 | if (irq) | ||
128 | do_IRQ(irq); | ||
129 | else | ||
130 | do_IRQ(SGINT_LOCAL0 + 0); | ||
131 | } | ||
132 | |||
133 | static void indy_local1_irqdispatch(void) | ||
134 | { | ||
135 | u8 mask = sgint->istat1 & sgint->imask1; | ||
136 | u8 mask2; | ||
137 | int irq; | ||
138 | |||
139 | if (mask & SGINT_ISTAT1_LIO3) { | ||
140 | mask2 = sgint->vmeistat & sgint->cmeimask1; | ||
141 | irq = lc3msk_to_irqnr[mask2]; | ||
142 | } else | ||
143 | irq = lc1msk_to_irqnr[mask]; | ||
144 | |||
145 | /* if irq == 0, then the interrupt has already been cleared */ | ||
146 | if (irq) | ||
147 | do_IRQ(irq); | ||
148 | } | ||
149 | |||
150 | extern void ip22_be_interrupt(int irq); | ||
151 | |||
152 | static void __irq_entry indy_buserror_irq(void) | ||
153 | { | ||
154 | int irq = SGI_BUSERR_IRQ; | ||
155 | |||
156 | irq_enter(); | ||
157 | kstat_incr_irq_this_cpu(irq); | ||
158 | ip22_be_interrupt(irq); | ||
159 | irq_exit(); | ||
160 | } | ||
161 | |||
162 | #ifdef USE_LIO3_IRQ | ||
163 | #define SGI_INTERRUPTS SGINT_END | ||
164 | #else | ||
165 | #define SGI_INTERRUPTS SGINT_LOCAL3 | ||
166 | #endif | ||
167 | |||
168 | extern void indy_8254timer_irq(void); | ||
169 | |||
170 | /* | ||
171 | * IRQs on the INDY look basically (barring software IRQs which we don't use | ||
172 | * at all) like: | ||
173 | * | ||
174 | * MIPS IRQ Source | ||
175 | * -------- ------ | ||
176 | * 0 Software (ignored) | ||
177 | * 1 Software (ignored) | ||
178 | * 2 Local IRQ level zero | ||
179 | * 3 Local IRQ level one | ||
180 | * 4 8254 Timer zero | ||
181 | * 5 8254 Timer one | ||
182 | * 6 Bus Error | ||
183 | * 7 R4k timer (what we use) | ||
184 | * | ||
185 | * We handle the IRQ according to _our_ priority which is: | ||
186 | * | ||
187 | * Highest ---- R4k Timer | ||
188 | * Local IRQ zero | ||
189 | * Local IRQ one | ||
190 | * Bus Error | ||
191 | * 8254 Timer zero | ||
192 | * Lowest ---- 8254 Timer one | ||
193 | * | ||
194 | * then we just return, if multiple IRQs are pending then we will just take | ||
195 | * another exception, big deal. | ||
196 | */ | ||
197 | |||
198 | asmlinkage void plat_irq_dispatch(void) | ||
199 | { | ||
200 | unsigned int pending = read_c0_status() & read_c0_cause(); | ||
201 | |||
202 | /* | ||
203 | * First we check for r4k counter/timer IRQ. | ||
204 | */ | ||
205 | if (pending & CAUSEF_IP7) | ||
206 | do_IRQ(SGI_TIMER_IRQ); | ||
207 | else if (pending & CAUSEF_IP2) | ||
208 | indy_local0_irqdispatch(); | ||
209 | else if (pending & CAUSEF_IP3) | ||
210 | indy_local1_irqdispatch(); | ||
211 | else if (pending & CAUSEF_IP6) | ||
212 | indy_buserror_irq(); | ||
213 | else if (pending & (CAUSEF_IP4 | CAUSEF_IP5)) | ||
214 | indy_8254timer_irq(); | ||
215 | } | ||
216 | |||
217 | void __init arch_init_irq(void) | ||
218 | { | ||
219 | int i; | ||
220 | |||
221 | /* Init local mask --> irq tables. */ | ||
222 | for (i = 0; i < 256; i++) { | ||
223 | if (i & 0x80) { | ||
224 | lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 7; | ||
225 | lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 7; | ||
226 | lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 7; | ||
227 | lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 7; | ||
228 | } else if (i & 0x40) { | ||
229 | lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 6; | ||
230 | lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 6; | ||
231 | lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 6; | ||
232 | lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 6; | ||
233 | } else if (i & 0x20) { | ||
234 | lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 5; | ||
235 | lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 5; | ||
236 | lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 5; | ||
237 | lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 5; | ||
238 | } else if (i & 0x10) { | ||
239 | lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 4; | ||
240 | lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 4; | ||
241 | lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 4; | ||
242 | lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 4; | ||
243 | } else if (i & 0x08) { | ||
244 | lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 3; | ||
245 | lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 3; | ||
246 | lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 3; | ||
247 | lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 3; | ||
248 | } else if (i & 0x04) { | ||
249 | lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 2; | ||
250 | lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 2; | ||
251 | lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 2; | ||
252 | lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 2; | ||
253 | } else if (i & 0x02) { | ||
254 | lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 1; | ||
255 | lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 1; | ||
256 | lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 1; | ||
257 | lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 1; | ||
258 | } else if (i & 0x01) { | ||
259 | lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 0; | ||
260 | lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 0; | ||
261 | lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 0; | ||
262 | lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 0; | ||
263 | } else { | ||
264 | lc0msk_to_irqnr[i] = 0; | ||
265 | lc1msk_to_irqnr[i] = 0; | ||
266 | lc2msk_to_irqnr[i] = 0; | ||
267 | lc3msk_to_irqnr[i] = 0; | ||
268 | } | ||
269 | } | ||
270 | |||
271 | /* Mask out all interrupts. */ | ||
272 | sgint->imask0 = 0; | ||
273 | sgint->imask1 = 0; | ||
274 | sgint->cmeimask0 = 0; | ||
275 | sgint->cmeimask1 = 0; | ||
276 | |||
277 | /* init CPU irqs */ | ||
278 | mips_cpu_irq_init(); | ||
279 | |||
280 | for (i = SGINT_LOCAL0; i < SGI_INTERRUPTS; i++) { | ||
281 | struct irq_chip *handler; | ||
282 | |||
283 | if (i < SGINT_LOCAL1) | ||
284 | handler = &ip22_local0_irq_type; | ||
285 | else if (i < SGINT_LOCAL2) | ||
286 | handler = &ip22_local1_irq_type; | ||
287 | else if (i < SGINT_LOCAL3) | ||
288 | handler = &ip22_local2_irq_type; | ||
289 | else | ||
290 | handler = &ip22_local3_irq_type; | ||
291 | |||
292 | irq_set_chip_and_handler(i, handler, handle_level_irq); | ||
293 | } | ||
294 | |||
295 | /* vector handler. this register the IRQ as non-sharable */ | ||
296 | if (request_irq(SGI_LOCAL_0_IRQ, no_action, IRQF_NO_THREAD, | ||
297 | "local0 cascade", NULL)) | ||
298 | pr_err("Failed to register local0 cascade interrupt\n"); | ||
299 | if (request_irq(SGI_LOCAL_1_IRQ, no_action, IRQF_NO_THREAD, | ||
300 | "local1 cascade", NULL)) | ||
301 | pr_err("Failed to register local1 cascade interrupt\n"); | ||
302 | if (request_irq(SGI_BUSERR_IRQ, no_action, IRQF_NO_THREAD, | ||
303 | "Bus Error", NULL)) | ||
304 | pr_err("Failed to register Bus Error interrupt\n"); | ||
305 | |||
306 | /* cascade in cascade. i love Indy ;-) */ | ||
307 | if (request_irq(SGI_MAP_0_IRQ, no_action, IRQF_NO_THREAD, | ||
308 | "mapable0 cascade", NULL)) | ||
309 | pr_err("Failed to register mapable0 cascade interrupt\n"); | ||
310 | #ifdef USE_LIO3_IRQ | ||
311 | if (request_irq(SGI_MAP_1_IRQ, no_action, IRQF_NO_THREAD, | ||
312 | "mapable1 cascade", NULL)) | ||
313 | pr_err("Failed to register mapable1 cascade interrupt\n"); | ||
314 | #endif | ||
315 | |||
316 | #ifdef CONFIG_EISA | ||
317 | if (ip22_is_fullhouse()) /* Only Indigo-2 has EISA stuff */ | ||
318 | ip22_eisa_init(); | ||
319 | #endif | ||
320 | } | ||
diff --git a/arch/mips/sgi-ip22/ip22-mc.c b/arch/mips/sgi-ip22/ip22-mc.c new file mode 100644 index 000000000..74e5b9e27 --- /dev/null +++ b/arch/mips/sgi-ip22/ip22-mc.c | |||
@@ -0,0 +1,203 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * ip22-mc.c: Routines for manipulating SGI Memory Controller. | ||
4 | * | ||
5 | * Copyright (C) 1996 David S. Miller (davem@davemloft.net) | ||
6 | * Copyright (C) 1999 Andrew R. Baker (andrewb@uab.edu) - Indigo2 changes | ||
7 | * Copyright (C) 2003 Ladislav Michl (ladis@linux-mips.org) | ||
8 | * Copyright (C) 2004 Peter Fuerst (pf@net.alphadv.de) - IP28 | ||
9 | */ | ||
10 | |||
11 | #include <linux/init.h> | ||
12 | #include <linux/export.h> | ||
13 | #include <linux/kernel.h> | ||
14 | #include <linux/memblock.h> | ||
15 | #include <linux/spinlock.h> | ||
16 | |||
17 | #include <asm/io.h> | ||
18 | #include <asm/bootinfo.h> | ||
19 | #include <asm/sgialib.h> | ||
20 | #include <asm/sgi/mc.h> | ||
21 | #include <asm/sgi/hpc3.h> | ||
22 | #include <asm/sgi/ip22.h> | ||
23 | |||
24 | struct sgimc_regs *sgimc; | ||
25 | |||
26 | EXPORT_SYMBOL(sgimc); | ||
27 | |||
28 | static inline unsigned long get_bank_addr(unsigned int memconfig) | ||
29 | { | ||
30 | return (memconfig & SGIMC_MCONFIG_BASEADDR) << ((sgimc->systemid & SGIMC_SYSID_MASKREV) >= 5 ? 24 : 22); | ||
31 | } | ||
32 | |||
33 | static inline unsigned long get_bank_size(unsigned int memconfig) | ||
34 | { | ||
35 | return ((memconfig & SGIMC_MCONFIG_RMASK) + 0x0100) << ((sgimc->systemid & SGIMC_SYSID_MASKREV) >= 5 ? 16 : 14); | ||
36 | } | ||
37 | |||
38 | static inline unsigned int get_bank_config(int bank) | ||
39 | { | ||
40 | unsigned int res = bank > 1 ? sgimc->mconfig1 : sgimc->mconfig0; | ||
41 | return bank % 2 ? res & 0xffff : res >> 16; | ||
42 | } | ||
43 | |||
44 | #if defined(CONFIG_SGI_IP28) || defined(CONFIG_32BIT) | ||
45 | static void __init probe_memory(void) | ||
46 | { | ||
47 | /* prom detects all usable memory */ | ||
48 | } | ||
49 | #else | ||
50 | /* | ||
51 | * Detect installed memory, which PROM misses | ||
52 | */ | ||
53 | static void __init probe_memory(void) | ||
54 | { | ||
55 | unsigned long addr, size; | ||
56 | int i; | ||
57 | |||
58 | printk(KERN_INFO "MC: Probing memory configuration:\n"); | ||
59 | for (i = 0; i < 4; i++) { | ||
60 | unsigned int tmp = get_bank_config(i); | ||
61 | if (!(tmp & SGIMC_MCONFIG_BVALID)) | ||
62 | continue; | ||
63 | |||
64 | size = get_bank_size(tmp); | ||
65 | addr = get_bank_addr(tmp); | ||
66 | printk(KERN_INFO " bank%d: %3ldM @ %08lx\n", | ||
67 | i, size / 1024 / 1024, addr); | ||
68 | |||
69 | if (addr >= SGIMC_SEG1_BADDR) | ||
70 | memblock_add(addr, size); | ||
71 | } | ||
72 | } | ||
73 | #endif | ||
74 | |||
75 | void __init sgimc_init(void) | ||
76 | { | ||
77 | u32 tmp; | ||
78 | |||
79 | /* ioremap can't fail */ | ||
80 | sgimc = (struct sgimc_regs *) | ||
81 | ioremap(SGIMC_BASE, sizeof(struct sgimc_regs)); | ||
82 | |||
83 | printk(KERN_INFO "MC: SGI memory controller Revision %d\n", | ||
84 | (int) sgimc->systemid & SGIMC_SYSID_MASKREV); | ||
85 | |||
86 | /* Place the MC into a known state. This must be done before | ||
87 | * interrupts are first enabled etc. | ||
88 | */ | ||
89 | |||
90 | /* Step 0: Make sure we turn off the watchdog in case it's | ||
91 | * still running (which might be the case after a | ||
92 | * soft reboot). | ||
93 | */ | ||
94 | tmp = sgimc->cpuctrl0; | ||
95 | tmp &= ~SGIMC_CCTRL0_WDOG; | ||
96 | sgimc->cpuctrl0 = tmp; | ||
97 | |||
98 | /* Step 1: The CPU/GIO error status registers will not latch | ||
99 | * up a new error status until the register has been | ||
100 | * cleared by the cpu. These status registers are | ||
101 | * cleared by writing any value to them. | ||
102 | */ | ||
103 | sgimc->cstat = sgimc->gstat = 0; | ||
104 | |||
105 | /* Step 2: Enable all parity checking in cpu control register | ||
106 | * zero. | ||
107 | */ | ||
108 | /* don't touch parity settings for IP28 */ | ||
109 | tmp = sgimc->cpuctrl0; | ||
110 | #ifndef CONFIG_SGI_IP28 | ||
111 | tmp |= SGIMC_CCTRL0_EPERRGIO | SGIMC_CCTRL0_EPERRMEM; | ||
112 | #endif | ||
113 | tmp |= SGIMC_CCTRL0_R4KNOCHKPARR; | ||
114 | sgimc->cpuctrl0 = tmp; | ||
115 | |||
116 | /* Step 3: Setup the MC write buffer depth, this is controlled | ||
117 | * in cpu control register 1 in the lower 4 bits. | ||
118 | */ | ||
119 | tmp = sgimc->cpuctrl1; | ||
120 | tmp &= ~0xf; | ||
121 | tmp |= 0xd; | ||
122 | sgimc->cpuctrl1 = tmp; | ||
123 | |||
124 | /* Step 4: Initialize the RPSS divider register to run as fast | ||
125 | * as it can correctly operate. The register is laid | ||
126 | * out as follows: | ||
127 | * | ||
128 | * ---------------------------------------- | ||
129 | * | RESERVED | INCREMENT | DIVIDER | | ||
130 | * ---------------------------------------- | ||
131 | * 31 16 15 8 7 0 | ||
132 | * | ||
133 | * DIVIDER determines how often a 'tick' happens, | ||
134 | * INCREMENT determines by how the RPSS increment | ||
135 | * registers value increases at each 'tick'. Thus, | ||
136 | * for IP22 we get INCREMENT=1, DIVIDER=1 == 0x101 | ||
137 | */ | ||
138 | sgimc->divider = 0x101; | ||
139 | |||
140 | /* Step 5: Initialize GIO64 arbitrator configuration register. | ||
141 | * | ||
142 | * NOTE: HPC init code in sgihpc_init() must run before us because | ||
143 | * we need to know Guiness vs. FullHouse and the board | ||
144 | * revision on this machine. You have been warned. | ||
145 | */ | ||
146 | |||
147 | /* First the basic invariants across all GIO64 implementations. */ | ||
148 | tmp = sgimc->giopar & SGIMC_GIOPAR_GFX64; /* keep gfx 64bit settings */ | ||
149 | tmp |= SGIMC_GIOPAR_HPC64; /* All 1st HPC's interface at 64bits */ | ||
150 | tmp |= SGIMC_GIOPAR_ONEBUS; /* Only one physical GIO bus exists */ | ||
151 | |||
152 | if (ip22_is_fullhouse()) { | ||
153 | /* Fullhouse specific settings. */ | ||
154 | if (SGIOC_SYSID_BOARDREV(sgioc->sysid) < 2) { | ||
155 | tmp |= SGIMC_GIOPAR_HPC264; /* 2nd HPC at 64bits */ | ||
156 | tmp |= SGIMC_GIOPAR_PLINEEXP0; /* exp0 pipelines */ | ||
157 | tmp |= SGIMC_GIOPAR_MASTEREXP1; /* exp1 masters */ | ||
158 | tmp |= SGIMC_GIOPAR_RTIMEEXP0; /* exp0 is realtime */ | ||
159 | } else { | ||
160 | tmp |= SGIMC_GIOPAR_HPC264; /* 2nd HPC 64bits */ | ||
161 | tmp |= SGIMC_GIOPAR_PLINEEXP0; /* exp[01] pipelined */ | ||
162 | tmp |= SGIMC_GIOPAR_PLINEEXP1; | ||
163 | tmp |= SGIMC_GIOPAR_MASTEREISA; /* EISA masters */ | ||
164 | } | ||
165 | } else { | ||
166 | /* Guiness specific settings. */ | ||
167 | tmp |= SGIMC_GIOPAR_EISA64; /* MC talks to EISA at 64bits */ | ||
168 | tmp |= SGIMC_GIOPAR_MASTEREISA; /* EISA bus can act as master */ | ||
169 | } | ||
170 | sgimc->giopar = tmp; /* poof */ | ||
171 | |||
172 | probe_memory(); | ||
173 | } | ||
174 | |||
175 | #ifdef CONFIG_SGI_IP28 | ||
176 | void __init prom_cleanup(void) | ||
177 | { | ||
178 | u32 mconfig1; | ||
179 | unsigned long flags; | ||
180 | spinlock_t lock; | ||
181 | |||
182 | /* | ||
183 | * because ARCS accesses memory uncached we wait until ARCS | ||
184 | * isn't needed any longer, before we switch from slow to | ||
185 | * normal mode | ||
186 | */ | ||
187 | spin_lock_irqsave(&lock, flags); | ||
188 | mconfig1 = sgimc->mconfig1; | ||
189 | /* map ECC register */ | ||
190 | sgimc->mconfig1 = (mconfig1 & 0xffff0000) | 0x2060; | ||
191 | iob(); | ||
192 | /* switch to normal mode */ | ||
193 | *(unsigned long *)PHYS_TO_XKSEG_UNCACHED(0x60000000) = 0; | ||
194 | iob(); | ||
195 | /* reduce WR_COL */ | ||
196 | sgimc->cmacc = (sgimc->cmacc & ~0xf) | 4; | ||
197 | iob(); | ||
198 | /* restore old config */ | ||
199 | sgimc->mconfig1 = mconfig1; | ||
200 | iob(); | ||
201 | spin_unlock_irqrestore(&lock, flags); | ||
202 | } | ||
203 | #endif | ||
diff --git a/arch/mips/sgi-ip22/ip22-nvram.c b/arch/mips/sgi-ip22/ip22-nvram.c new file mode 100644 index 000000000..e727ef519 --- /dev/null +++ b/arch/mips/sgi-ip22/ip22-nvram.c | |||
@@ -0,0 +1,122 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * ip22-nvram.c: NVRAM and serial EEPROM handling. | ||
4 | * | ||
5 | * Copyright (C) 2003 Ladislav Michl (ladis@linux-mips.org) | ||
6 | */ | ||
7 | #include <linux/export.h> | ||
8 | |||
9 | #include <asm/sgi/hpc3.h> | ||
10 | #include <asm/sgi/ip22.h> | ||
11 | |||
12 | /* Control opcode for serial eeprom */ | ||
13 | #define EEPROM_READ 0xc000 /* serial memory read */ | ||
14 | #define EEPROM_WEN 0x9800 /* write enable before prog modes */ | ||
15 | #define EEPROM_WRITE 0xa000 /* serial memory write */ | ||
16 | #define EEPROM_WRALL 0x8800 /* write all registers */ | ||
17 | #define EEPROM_WDS 0x8000 /* disable all programming */ | ||
18 | #define EEPROM_PRREAD 0xc000 /* read protect register */ | ||
19 | #define EEPROM_PREN 0x9800 /* enable protect register mode */ | ||
20 | #define EEPROM_PRCLEAR 0xffff /* clear protect register */ | ||
21 | #define EEPROM_PRWRITE 0xa000 /* write protect register */ | ||
22 | #define EEPROM_PRDS 0x8000 /* disable protect register, forever */ | ||
23 | |||
24 | #define EEPROM_EPROT 0x01 /* Protect register enable */ | ||
25 | #define EEPROM_CSEL 0x02 /* Chip select */ | ||
26 | #define EEPROM_ECLK 0x04 /* EEPROM clock */ | ||
27 | #define EEPROM_DATO 0x08 /* Data out */ | ||
28 | #define EEPROM_DATI 0x10 /* Data in */ | ||
29 | |||
30 | /* We need to use these functions early... */ | ||
31 | #define delay() ({ \ | ||
32 | int x; \ | ||
33 | for (x=0; x<100000; x++) __asm__ __volatile__(""); }) | ||
34 | |||
35 | #define eeprom_cs_on(ptr) ({ \ | ||
36 | __raw_writel(__raw_readl(ptr) & ~EEPROM_DATO, ptr); \ | ||
37 | __raw_writel(__raw_readl(ptr) & ~EEPROM_ECLK, ptr); \ | ||
38 | __raw_writel(__raw_readl(ptr) & ~EEPROM_EPROT, ptr); \ | ||
39 | delay(); \ | ||
40 | __raw_writel(__raw_readl(ptr) | EEPROM_CSEL, ptr); \ | ||
41 | __raw_writel(__raw_readl(ptr) | EEPROM_ECLK, ptr); }) | ||
42 | |||
43 | |||
44 | #define eeprom_cs_off(ptr) ({ \ | ||
45 | __raw_writel(__raw_readl(ptr) & ~EEPROM_ECLK, ptr); \ | ||
46 | __raw_writel(__raw_readl(ptr) & ~EEPROM_CSEL, ptr); \ | ||
47 | __raw_writel(__raw_readl(ptr) | EEPROM_EPROT, ptr); \ | ||
48 | __raw_writel(__raw_readl(ptr) | EEPROM_ECLK, ptr); }) | ||
49 | |||
50 | #define BITS_IN_COMMAND 11 | ||
51 | /* | ||
52 | * clock in the nvram command and the register number. For the | ||
53 | * national semiconductor nv ram chip the op code is 3 bits and | ||
54 | * the address is 6/8 bits. | ||
55 | */ | ||
56 | static inline void eeprom_cmd(unsigned int *ctrl, unsigned cmd, unsigned reg) | ||
57 | { | ||
58 | unsigned short ser_cmd; | ||
59 | int i; | ||
60 | |||
61 | ser_cmd = cmd | (reg << (16 - BITS_IN_COMMAND)); | ||
62 | for (i = 0; i < BITS_IN_COMMAND; i++) { | ||
63 | if (ser_cmd & (1<<15)) /* if high order bit set */ | ||
64 | __raw_writel(__raw_readl(ctrl) | EEPROM_DATO, ctrl); | ||
65 | else | ||
66 | __raw_writel(__raw_readl(ctrl) & ~EEPROM_DATO, ctrl); | ||
67 | __raw_writel(__raw_readl(ctrl) & ~EEPROM_ECLK, ctrl); | ||
68 | delay(); | ||
69 | __raw_writel(__raw_readl(ctrl) | EEPROM_ECLK, ctrl); | ||
70 | delay(); | ||
71 | ser_cmd <<= 1; | ||
72 | } | ||
73 | /* see data sheet timing diagram */ | ||
74 | __raw_writel(__raw_readl(ctrl) & ~EEPROM_DATO, ctrl); | ||
75 | } | ||
76 | |||
77 | unsigned short ip22_eeprom_read(unsigned int *ctrl, int reg) | ||
78 | { | ||
79 | unsigned short res = 0; | ||
80 | int i; | ||
81 | |||
82 | __raw_writel(__raw_readl(ctrl) & ~EEPROM_EPROT, ctrl); | ||
83 | eeprom_cs_on(ctrl); | ||
84 | eeprom_cmd(ctrl, EEPROM_READ, reg); | ||
85 | |||
86 | /* clock the data ouf of serial mem */ | ||
87 | for (i = 0; i < 16; i++) { | ||
88 | __raw_writel(__raw_readl(ctrl) & ~EEPROM_ECLK, ctrl); | ||
89 | delay(); | ||
90 | __raw_writel(__raw_readl(ctrl) | EEPROM_ECLK, ctrl); | ||
91 | delay(); | ||
92 | res <<= 1; | ||
93 | if (__raw_readl(ctrl) & EEPROM_DATI) | ||
94 | res |= 1; | ||
95 | } | ||
96 | |||
97 | eeprom_cs_off(ctrl); | ||
98 | |||
99 | return res; | ||
100 | } | ||
101 | |||
102 | EXPORT_SYMBOL(ip22_eeprom_read); | ||
103 | |||
104 | /* | ||
105 | * Read specified register from main NVRAM | ||
106 | */ | ||
107 | unsigned short ip22_nvram_read(int reg) | ||
108 | { | ||
109 | if (ip22_is_fullhouse()) | ||
110 | /* IP22 (Indigo2 aka FullHouse) stores env variables into | ||
111 | * 93CS56 Microwire Bus EEPROM 2048 Bit (128x16) */ | ||
112 | return ip22_eeprom_read(&hpc3c0->eeprom, reg); | ||
113 | else { | ||
114 | unsigned short tmp; | ||
115 | /* IP24 (Indy aka Guiness) uses DS1386 8K version */ | ||
116 | reg <<= 1; | ||
117 | tmp = hpc3c0->bbram[reg++] & 0xff; | ||
118 | return (tmp << 8) | (hpc3c0->bbram[reg] & 0xff); | ||
119 | } | ||
120 | } | ||
121 | |||
122 | EXPORT_SYMBOL(ip22_nvram_read); | ||
diff --git a/arch/mips/sgi-ip22/ip22-platform.c b/arch/mips/sgi-ip22/ip22-platform.c new file mode 100644 index 000000000..0b2002e02 --- /dev/null +++ b/arch/mips/sgi-ip22/ip22-platform.c | |||
@@ -0,0 +1,223 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | #include <linux/init.h> | ||
3 | #include <linux/if_ether.h> | ||
4 | #include <linux/kernel.h> | ||
5 | #include <linux/platform_device.h> | ||
6 | #include <linux/dma-mapping.h> | ||
7 | |||
8 | #include <asm/paccess.h> | ||
9 | #include <asm/sgi/ip22.h> | ||
10 | #include <asm/sgi/hpc3.h> | ||
11 | #include <asm/sgi/mc.h> | ||
12 | #include <asm/sgi/seeq.h> | ||
13 | #include <asm/sgi/wd.h> | ||
14 | |||
15 | static struct resource sgiwd93_0_resources[] = { | ||
16 | { | ||
17 | .name = "eth0 irq", | ||
18 | .start = SGI_WD93_0_IRQ, | ||
19 | .end = SGI_WD93_0_IRQ, | ||
20 | .flags = IORESOURCE_IRQ | ||
21 | } | ||
22 | }; | ||
23 | |||
24 | static struct sgiwd93_platform_data sgiwd93_0_pd = { | ||
25 | .unit = 0, | ||
26 | .irq = SGI_WD93_0_IRQ, | ||
27 | }; | ||
28 | |||
29 | static u64 sgiwd93_0_dma_mask = DMA_BIT_MASK(32); | ||
30 | |||
31 | static struct platform_device sgiwd93_0_device = { | ||
32 | .name = "sgiwd93", | ||
33 | .id = 0, | ||
34 | .num_resources = ARRAY_SIZE(sgiwd93_0_resources), | ||
35 | .resource = sgiwd93_0_resources, | ||
36 | .dev = { | ||
37 | .platform_data = &sgiwd93_0_pd, | ||
38 | .dma_mask = &sgiwd93_0_dma_mask, | ||
39 | .coherent_dma_mask = DMA_BIT_MASK(32), | ||
40 | }, | ||
41 | }; | ||
42 | |||
43 | static struct resource sgiwd93_1_resources[] = { | ||
44 | { | ||
45 | .name = "eth0 irq", | ||
46 | .start = SGI_WD93_1_IRQ, | ||
47 | .end = SGI_WD93_1_IRQ, | ||
48 | .flags = IORESOURCE_IRQ | ||
49 | } | ||
50 | }; | ||
51 | |||
52 | static struct sgiwd93_platform_data sgiwd93_1_pd = { | ||
53 | .unit = 1, | ||
54 | .irq = SGI_WD93_1_IRQ, | ||
55 | }; | ||
56 | |||
57 | static u64 sgiwd93_1_dma_mask = DMA_BIT_MASK(32); | ||
58 | |||
59 | static struct platform_device sgiwd93_1_device = { | ||
60 | .name = "sgiwd93", | ||
61 | .id = 1, | ||
62 | .num_resources = ARRAY_SIZE(sgiwd93_1_resources), | ||
63 | .resource = sgiwd93_1_resources, | ||
64 | .dev = { | ||
65 | .platform_data = &sgiwd93_1_pd, | ||
66 | .dma_mask = &sgiwd93_1_dma_mask, | ||
67 | .coherent_dma_mask = DMA_BIT_MASK(32), | ||
68 | }, | ||
69 | }; | ||
70 | |||
71 | /* | ||
72 | * Create a platform device for the GPI port that receives the | ||
73 | * image data from the embedded camera. | ||
74 | */ | ||
75 | static int __init sgiwd93_devinit(void) | ||
76 | { | ||
77 | int res; | ||
78 | |||
79 | sgiwd93_0_pd.hregs = &hpc3c0->scsi_chan0; | ||
80 | sgiwd93_0_pd.wdregs = (unsigned char *) hpc3c0->scsi0_ext; | ||
81 | |||
82 | res = platform_device_register(&sgiwd93_0_device); | ||
83 | if (res) | ||
84 | return res; | ||
85 | |||
86 | if (!ip22_is_fullhouse()) | ||
87 | return 0; | ||
88 | |||
89 | sgiwd93_1_pd.hregs = &hpc3c0->scsi_chan1; | ||
90 | sgiwd93_1_pd.wdregs = (unsigned char *) hpc3c0->scsi1_ext; | ||
91 | |||
92 | return platform_device_register(&sgiwd93_1_device); | ||
93 | } | ||
94 | |||
95 | device_initcall(sgiwd93_devinit); | ||
96 | |||
97 | static struct resource sgiseeq_0_resources[] = { | ||
98 | { | ||
99 | .name = "eth0 irq", | ||
100 | .start = SGI_ENET_IRQ, | ||
101 | .end = SGI_ENET_IRQ, | ||
102 | .flags = IORESOURCE_IRQ | ||
103 | } | ||
104 | }; | ||
105 | |||
106 | static struct sgiseeq_platform_data eth0_pd; | ||
107 | |||
108 | static u64 sgiseeq_dma_mask = DMA_BIT_MASK(32); | ||
109 | |||
110 | static struct platform_device eth0_device = { | ||
111 | .name = "sgiseeq", | ||
112 | .id = 0, | ||
113 | .num_resources = ARRAY_SIZE(sgiseeq_0_resources), | ||
114 | .resource = sgiseeq_0_resources, | ||
115 | .dev = { | ||
116 | .platform_data = ð0_pd, | ||
117 | .dma_mask = &sgiseeq_dma_mask, | ||
118 | .coherent_dma_mask = DMA_BIT_MASK(32), | ||
119 | }, | ||
120 | }; | ||
121 | |||
122 | static struct resource sgiseeq_1_resources[] = { | ||
123 | { | ||
124 | .name = "eth1 irq", | ||
125 | .start = SGI_GIO_0_IRQ, | ||
126 | .end = SGI_GIO_0_IRQ, | ||
127 | .flags = IORESOURCE_IRQ | ||
128 | } | ||
129 | }; | ||
130 | |||
131 | static struct sgiseeq_platform_data eth1_pd; | ||
132 | |||
133 | static struct platform_device eth1_device = { | ||
134 | .name = "sgiseeq", | ||
135 | .id = 1, | ||
136 | .num_resources = ARRAY_SIZE(sgiseeq_1_resources), | ||
137 | .resource = sgiseeq_1_resources, | ||
138 | .dev = { | ||
139 | .platform_data = ð1_pd, | ||
140 | }, | ||
141 | }; | ||
142 | |||
143 | /* | ||
144 | * Create a platform device for the GPI port that receives the | ||
145 | * image data from the embedded camera. | ||
146 | */ | ||
147 | static int __init sgiseeq_devinit(void) | ||
148 | { | ||
149 | unsigned int pbdma __maybe_unused; | ||
150 | int res, i; | ||
151 | |||
152 | eth0_pd.hpc = hpc3c0; | ||
153 | eth0_pd.irq = SGI_ENET_IRQ; | ||
154 | #define EADDR_NVOFS 250 | ||
155 | for (i = 0; i < 3; i++) { | ||
156 | unsigned short tmp = ip22_nvram_read(EADDR_NVOFS / 2 + i); | ||
157 | |||
158 | eth0_pd.mac[2 * i] = tmp >> 8; | ||
159 | eth0_pd.mac[2 * i + 1] = tmp & 0xff; | ||
160 | } | ||
161 | |||
162 | res = platform_device_register(ð0_device); | ||
163 | if (res) | ||
164 | return res; | ||
165 | |||
166 | /* Second HPC is missing? */ | ||
167 | if (ip22_is_fullhouse() || | ||
168 | get_dbe(pbdma, (unsigned int *)&hpc3c1->pbdma[1])) | ||
169 | return 0; | ||
170 | |||
171 | sgimc->giopar |= SGIMC_GIOPAR_MASTEREXP1 | SGIMC_GIOPAR_EXP164 | | ||
172 | SGIMC_GIOPAR_HPC264; | ||
173 | hpc3c1->pbus_piocfg[0][0] = 0x3ffff; | ||
174 | /* interrupt/config register on Challenge S Mezz board */ | ||
175 | hpc3c1->pbus_extregs[0][0] = 0x30; | ||
176 | |||
177 | eth1_pd.hpc = hpc3c1; | ||
178 | eth1_pd.irq = SGI_GIO_0_IRQ; | ||
179 | #define EADDR_NVOFS 250 | ||
180 | for (i = 0; i < 3; i++) { | ||
181 | unsigned short tmp = ip22_eeprom_read(&hpc3c1->eeprom, | ||
182 | EADDR_NVOFS / 2 + i); | ||
183 | |||
184 | eth1_pd.mac[2 * i] = tmp >> 8; | ||
185 | eth1_pd.mac[2 * i + 1] = tmp & 0xff; | ||
186 | } | ||
187 | |||
188 | return platform_device_register(ð1_device); | ||
189 | } | ||
190 | |||
191 | device_initcall(sgiseeq_devinit); | ||
192 | |||
193 | static int __init sgi_hal2_devinit(void) | ||
194 | { | ||
195 | return IS_ERR(platform_device_register_simple("sgihal2", 0, NULL, 0)); | ||
196 | } | ||
197 | |||
198 | device_initcall(sgi_hal2_devinit); | ||
199 | |||
200 | static int __init sgi_button_devinit(void) | ||
201 | { | ||
202 | if (ip22_is_fullhouse()) | ||
203 | return 0; /* full house has no volume buttons */ | ||
204 | |||
205 | return IS_ERR(platform_device_register_simple("sgibtns", -1, NULL, 0)); | ||
206 | } | ||
207 | |||
208 | device_initcall(sgi_button_devinit); | ||
209 | |||
210 | static int __init sgi_ds1286_devinit(void) | ||
211 | { | ||
212 | struct resource res; | ||
213 | |||
214 | memset(&res, 0, sizeof(res)); | ||
215 | res.start = HPC3_CHIP0_BASE + offsetof(struct hpc3_regs, rtcregs); | ||
216 | res.end = res.start + sizeof(hpc3c0->rtcregs) - 1; | ||
217 | res.flags = IORESOURCE_MEM; | ||
218 | |||
219 | return IS_ERR(platform_device_register_simple("rtc-ds1286", -1, | ||
220 | &res, 1)); | ||
221 | } | ||
222 | |||
223 | device_initcall(sgi_ds1286_devinit); | ||
diff --git a/arch/mips/sgi-ip22/ip22-reset.c b/arch/mips/sgi-ip22/ip22-reset.c new file mode 100644 index 000000000..c374f3cee --- /dev/null +++ b/arch/mips/sgi-ip22/ip22-reset.c | |||
@@ -0,0 +1,203 @@ | |||
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) 1997, 1998, 2001, 03, 05, 06 by Ralf Baechle | ||
7 | */ | ||
8 | #include <linux/linkage.h> | ||
9 | #include <linux/init.h> | ||
10 | #include <linux/rtc/ds1286.h> | ||
11 | #include <linux/interrupt.h> | ||
12 | #include <linux/kernel.h> | ||
13 | #include <linux/sched/signal.h> | ||
14 | #include <linux/notifier.h> | ||
15 | #include <linux/pm.h> | ||
16 | #include <linux/timer.h> | ||
17 | |||
18 | #include <asm/io.h> | ||
19 | #include <asm/irq.h> | ||
20 | #include <asm/reboot.h> | ||
21 | #include <asm/sgialib.h> | ||
22 | #include <asm/sgi/ioc.h> | ||
23 | #include <asm/sgi/hpc3.h> | ||
24 | #include <asm/sgi/mc.h> | ||
25 | #include <asm/sgi/ip22.h> | ||
26 | |||
27 | /* | ||
28 | * Just powerdown if init hasn't done after POWERDOWN_TIMEOUT seconds. | ||
29 | * I'm not sure if this feature is a good idea, for now it's here just to | ||
30 | * make the power button make behave just like under IRIX. | ||
31 | */ | ||
32 | #define POWERDOWN_TIMEOUT 120 | ||
33 | |||
34 | /* | ||
35 | * Blink frequency during reboot grace period and when panicked. | ||
36 | */ | ||
37 | #define POWERDOWN_FREQ (HZ / 4) | ||
38 | #define PANIC_FREQ (HZ / 8) | ||
39 | |||
40 | static struct timer_list power_timer, blink_timer, debounce_timer; | ||
41 | static unsigned long blink_timer_timeout; | ||
42 | |||
43 | #define MACHINE_PANICED 1 | ||
44 | #define MACHINE_SHUTTING_DOWN 2 | ||
45 | |||
46 | static int machine_state; | ||
47 | |||
48 | static void __noreturn sgi_machine_power_off(void) | ||
49 | { | ||
50 | unsigned int tmp; | ||
51 | |||
52 | local_irq_disable(); | ||
53 | |||
54 | /* Disable watchdog */ | ||
55 | tmp = hpc3c0->rtcregs[RTC_CMD] & 0xff; | ||
56 | hpc3c0->rtcregs[RTC_CMD] = tmp | RTC_WAM; | ||
57 | hpc3c0->rtcregs[RTC_WSEC] = 0; | ||
58 | hpc3c0->rtcregs[RTC_WHSEC] = 0; | ||
59 | |||
60 | while (1) { | ||
61 | sgioc->panel = ~SGIOC_PANEL_POWERON; | ||
62 | /* Good bye cruel world ... */ | ||
63 | |||
64 | /* If we're still running, we probably got sent an alarm | ||
65 | interrupt. Read the flag to clear it. */ | ||
66 | tmp = hpc3c0->rtcregs[RTC_HOURS_ALARM]; | ||
67 | } | ||
68 | } | ||
69 | |||
70 | static void __noreturn sgi_machine_restart(char *command) | ||
71 | { | ||
72 | if (machine_state & MACHINE_SHUTTING_DOWN) | ||
73 | sgi_machine_power_off(); | ||
74 | sgimc->cpuctrl0 |= SGIMC_CCTRL0_SYSINIT; | ||
75 | while (1); | ||
76 | } | ||
77 | |||
78 | static void __noreturn sgi_machine_halt(void) | ||
79 | { | ||
80 | if (machine_state & MACHINE_SHUTTING_DOWN) | ||
81 | sgi_machine_power_off(); | ||
82 | ArcEnterInteractiveMode(); | ||
83 | } | ||
84 | |||
85 | static void power_timeout(struct timer_list *unused) | ||
86 | { | ||
87 | sgi_machine_power_off(); | ||
88 | } | ||
89 | |||
90 | static void blink_timeout(struct timer_list *unused) | ||
91 | { | ||
92 | /* XXX fix this for fullhouse */ | ||
93 | sgi_ioc_reset ^= (SGIOC_RESET_LC0OFF|SGIOC_RESET_LC1OFF); | ||
94 | sgioc->reset = sgi_ioc_reset; | ||
95 | |||
96 | mod_timer(&blink_timer, jiffies + blink_timer_timeout); | ||
97 | } | ||
98 | |||
99 | static void debounce(struct timer_list *unused) | ||
100 | { | ||
101 | del_timer(&debounce_timer); | ||
102 | if (sgint->istat1 & SGINT_ISTAT1_PWR) { | ||
103 | /* Interrupt still being sent. */ | ||
104 | debounce_timer.expires = jiffies + (HZ / 20); /* 0.05s */ | ||
105 | add_timer(&debounce_timer); | ||
106 | |||
107 | sgioc->panel = SGIOC_PANEL_POWERON | SGIOC_PANEL_POWERINTR | | ||
108 | SGIOC_PANEL_VOLDNINTR | SGIOC_PANEL_VOLDNHOLD | | ||
109 | SGIOC_PANEL_VOLUPINTR | SGIOC_PANEL_VOLUPHOLD; | ||
110 | |||
111 | return; | ||
112 | } | ||
113 | |||
114 | if (machine_state & MACHINE_PANICED) | ||
115 | sgimc->cpuctrl0 |= SGIMC_CCTRL0_SYSINIT; | ||
116 | |||
117 | enable_irq(SGI_PANEL_IRQ); | ||
118 | } | ||
119 | |||
120 | static inline void power_button(void) | ||
121 | { | ||
122 | if (machine_state & MACHINE_PANICED) | ||
123 | return; | ||
124 | |||
125 | if ((machine_state & MACHINE_SHUTTING_DOWN) || | ||
126 | kill_cad_pid(SIGINT, 1)) { | ||
127 | /* No init process or button pressed twice. */ | ||
128 | sgi_machine_power_off(); | ||
129 | } | ||
130 | |||
131 | machine_state |= MACHINE_SHUTTING_DOWN; | ||
132 | blink_timer_timeout = POWERDOWN_FREQ; | ||
133 | blink_timeout(&blink_timer); | ||
134 | |||
135 | timer_setup(&power_timer, power_timeout, 0); | ||
136 | power_timer.expires = jiffies + POWERDOWN_TIMEOUT * HZ; | ||
137 | add_timer(&power_timer); | ||
138 | } | ||
139 | |||
140 | static irqreturn_t panel_int(int irq, void *dev_id) | ||
141 | { | ||
142 | unsigned int buttons; | ||
143 | |||
144 | buttons = sgioc->panel; | ||
145 | sgioc->panel = SGIOC_PANEL_POWERON | SGIOC_PANEL_POWERINTR; | ||
146 | |||
147 | if (sgint->istat1 & SGINT_ISTAT1_PWR) { | ||
148 | /* Wait until interrupt goes away */ | ||
149 | disable_irq_nosync(SGI_PANEL_IRQ); | ||
150 | timer_setup(&debounce_timer, debounce, 0); | ||
151 | debounce_timer.expires = jiffies + 5; | ||
152 | add_timer(&debounce_timer); | ||
153 | } | ||
154 | |||
155 | /* Power button was pressed | ||
156 | * ioc.ps page 22: "The Panel Register is called Power Control by Full | ||
157 | * House. Only lowest 2 bits are used. Guiness uses upper four bits | ||
158 | * for volume control". This is not true, all bits are pulled high | ||
159 | * on fullhouse */ | ||
160 | if (!(buttons & SGIOC_PANEL_POWERINTR)) | ||
161 | power_button(); | ||
162 | |||
163 | return IRQ_HANDLED; | ||
164 | } | ||
165 | |||
166 | static int panic_event(struct notifier_block *this, unsigned long event, | ||
167 | void *ptr) | ||
168 | { | ||
169 | if (machine_state & MACHINE_PANICED) | ||
170 | return NOTIFY_DONE; | ||
171 | machine_state |= MACHINE_PANICED; | ||
172 | |||
173 | blink_timer_timeout = PANIC_FREQ; | ||
174 | blink_timeout(&blink_timer); | ||
175 | |||
176 | return NOTIFY_DONE; | ||
177 | } | ||
178 | |||
179 | static struct notifier_block panic_block = { | ||
180 | .notifier_call = panic_event, | ||
181 | }; | ||
182 | |||
183 | static int __init reboot_setup(void) | ||
184 | { | ||
185 | int res; | ||
186 | |||
187 | _machine_restart = sgi_machine_restart; | ||
188 | _machine_halt = sgi_machine_halt; | ||
189 | pm_power_off = sgi_machine_power_off; | ||
190 | |||
191 | res = request_irq(SGI_PANEL_IRQ, panel_int, 0, "Front Panel", NULL); | ||
192 | if (res) { | ||
193 | printk(KERN_ERR "Allocation of front panel IRQ failed\n"); | ||
194 | return res; | ||
195 | } | ||
196 | |||
197 | timer_setup(&blink_timer, blink_timeout, 0); | ||
198 | atomic_notifier_chain_register(&panic_notifier_list, &panic_block); | ||
199 | |||
200 | return 0; | ||
201 | } | ||
202 | |||
203 | subsys_initcall(reboot_setup); | ||
diff --git a/arch/mips/sgi-ip22/ip22-setup.c b/arch/mips/sgi-ip22/ip22-setup.c new file mode 100644 index 000000000..b69daa024 --- /dev/null +++ b/arch/mips/sgi-ip22/ip22-setup.c | |||
@@ -0,0 +1,78 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * ip22-setup.c: SGI specific setup, including init of the feature struct. | ||
4 | * | ||
5 | * Copyright (C) 1996 David S. Miller (davem@davemloft.net) | ||
6 | * Copyright (C) 1997, 1998 Ralf Baechle (ralf@gnu.org) | ||
7 | */ | ||
8 | #include <linux/init.h> | ||
9 | #include <linux/kernel.h> | ||
10 | #include <linux/kdev_t.h> | ||
11 | #include <linux/types.h> | ||
12 | #include <linux/console.h> | ||
13 | #include <linux/sched.h> | ||
14 | #include <linux/tty.h> | ||
15 | |||
16 | #include <asm/addrspace.h> | ||
17 | #include <asm/bcache.h> | ||
18 | #include <asm/bootinfo.h> | ||
19 | #include <asm/irq.h> | ||
20 | #include <asm/reboot.h> | ||
21 | #include <asm/time.h> | ||
22 | #include <asm/io.h> | ||
23 | #include <asm/traps.h> | ||
24 | #include <asm/sgialib.h> | ||
25 | #include <asm/sgi/mc.h> | ||
26 | #include <asm/sgi/hpc3.h> | ||
27 | #include <asm/sgi/ip22.h> | ||
28 | |||
29 | extern void ip22_be_init(void) __init; | ||
30 | |||
31 | void __init plat_mem_setup(void) | ||
32 | { | ||
33 | char *ctype; | ||
34 | char *cserial; | ||
35 | |||
36 | board_be_init = ip22_be_init; | ||
37 | |||
38 | /* Init the INDY HPC I/O controller. Need to call this before | ||
39 | * fucking with the memory controller because it needs to know the | ||
40 | * boardID and whether this is a Guiness or a FullHouse machine. | ||
41 | */ | ||
42 | sgihpc_init(); | ||
43 | |||
44 | /* Init INDY memory controller. */ | ||
45 | sgimc_init(); | ||
46 | |||
47 | #ifdef CONFIG_BOARD_SCACHE | ||
48 | /* Now enable boardcaches, if any. */ | ||
49 | indy_sc_init(); | ||
50 | #endif | ||
51 | |||
52 | /* Set EISA IO port base for Indigo2 | ||
53 | * ioremap cannot fail */ | ||
54 | set_io_port_base((unsigned long)ioremap(0x00080000, | ||
55 | 0x1fffffff - 0x00080000)); | ||
56 | /* ARCS console environment variable is set to "g?" for | ||
57 | * graphics console, it is set to "d" for the first serial | ||
58 | * line and "d2" for the second serial line. | ||
59 | * | ||
60 | * Need to check if the case is 'g' but no keyboard: | ||
61 | * (ConsoleIn/Out = serial) | ||
62 | */ | ||
63 | ctype = ArcGetEnvironmentVariable("console"); | ||
64 | cserial = ArcGetEnvironmentVariable("ConsoleOut"); | ||
65 | |||
66 | if ((ctype && *ctype == 'd') || (cserial && *cserial == 's')) { | ||
67 | static char options[8] __initdata; | ||
68 | char *baud = ArcGetEnvironmentVariable("dbaud"); | ||
69 | if (baud) | ||
70 | strcpy(options, baud); | ||
71 | add_preferred_console("ttyS", *(ctype + 1) == '2' ? 1 : 0, | ||
72 | baud ? options : NULL); | ||
73 | } else if (!ctype || *ctype != 'g') { | ||
74 | /* Use ARC if we don't want serial ('d') or graphics ('g'). */ | ||
75 | prom_flags |= PROM_FLAG_USE_AS_CONSOLE; | ||
76 | add_preferred_console("arc", 0, NULL); | ||
77 | } | ||
78 | } | ||
diff --git a/arch/mips/sgi-ip22/ip22-time.c b/arch/mips/sgi-ip22/ip22-time.c new file mode 100644 index 000000000..045aa89f2 --- /dev/null +++ b/arch/mips/sgi-ip22/ip22-time.c | |||
@@ -0,0 +1,131 @@ | |||
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 | * Time operations for IP22 machines. Original code may come from | ||
7 | * Ralf Baechle or David S. Miller (sorry guys, i'm really not sure) | ||
8 | * | ||
9 | * Copyright (C) 2001 by Ladislav Michl | ||
10 | * Copyright (C) 2003, 06 Ralf Baechle (ralf@linux-mips.org) | ||
11 | */ | ||
12 | #include <linux/bcd.h> | ||
13 | #include <linux/i8253.h> | ||
14 | #include <linux/init.h> | ||
15 | #include <linux/irq.h> | ||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/interrupt.h> | ||
18 | #include <linux/kernel_stat.h> | ||
19 | #include <linux/time.h> | ||
20 | #include <linux/ftrace.h> | ||
21 | |||
22 | #include <asm/cpu.h> | ||
23 | #include <asm/mipsregs.h> | ||
24 | #include <asm/io.h> | ||
25 | #include <asm/irq.h> | ||
26 | #include <asm/time.h> | ||
27 | #include <asm/sgialib.h> | ||
28 | #include <asm/sgi/ioc.h> | ||
29 | #include <asm/sgi/hpc3.h> | ||
30 | #include <asm/sgi/ip22.h> | ||
31 | |||
32 | static unsigned long dosample(void) | ||
33 | { | ||
34 | u32 ct0, ct1; | ||
35 | u8 msb; | ||
36 | |||
37 | /* Start the counter. */ | ||
38 | sgint->tcword = (SGINT_TCWORD_CNT2 | SGINT_TCWORD_CALL | | ||
39 | SGINT_TCWORD_MRGEN); | ||
40 | sgint->tcnt2 = SGINT_TCSAMP_COUNTER & 0xff; | ||
41 | sgint->tcnt2 = SGINT_TCSAMP_COUNTER >> 8; | ||
42 | |||
43 | /* Get initial counter invariant */ | ||
44 | ct0 = read_c0_count(); | ||
45 | |||
46 | /* Latch and spin until top byte of counter2 is zero */ | ||
47 | do { | ||
48 | writeb(SGINT_TCWORD_CNT2 | SGINT_TCWORD_CLAT, &sgint->tcword); | ||
49 | (void) readb(&sgint->tcnt2); | ||
50 | msb = readb(&sgint->tcnt2); | ||
51 | ct1 = read_c0_count(); | ||
52 | } while (msb); | ||
53 | |||
54 | /* Stop the counter. */ | ||
55 | writeb(SGINT_TCWORD_CNT2 | SGINT_TCWORD_CALL | SGINT_TCWORD_MSWST, | ||
56 | &sgint->tcword); | ||
57 | /* | ||
58 | * Return the difference, this is how far the r4k counter increments | ||
59 | * for every 1/HZ seconds. We round off the nearest 1 MHz of master | ||
60 | * clock (= 1000000 / HZ / 2). | ||
61 | */ | ||
62 | |||
63 | return (ct1 - ct0) / (500000/HZ) * (500000/HZ); | ||
64 | } | ||
65 | |||
66 | /* | ||
67 | * Here we need to calibrate the cycle counter to at least be close. | ||
68 | */ | ||
69 | __init void plat_time_init(void) | ||
70 | { | ||
71 | unsigned long r4k_ticks[3]; | ||
72 | unsigned long r4k_tick; | ||
73 | |||
74 | /* | ||
75 | * Figure out the r4k offset, the algorithm is very simple and works in | ||
76 | * _all_ cases as long as the 8254 counter register itself works ok (as | ||
77 | * an interrupt driving timer it does not because of bug, this is why | ||
78 | * we are using the onchip r4k counter/compare register to serve this | ||
79 | * purpose, but for r4k_offset calculation it will work ok for us). | ||
80 | * There are other very complicated ways of performing this calculation | ||
81 | * but this one works just fine so I am not going to futz around. ;-) | ||
82 | */ | ||
83 | printk(KERN_INFO "Calibrating system timer... "); | ||
84 | dosample(); /* Prime cache. */ | ||
85 | dosample(); /* Prime cache. */ | ||
86 | /* Zero is NOT an option. */ | ||
87 | do { | ||
88 | r4k_ticks[0] = dosample(); | ||
89 | } while (!r4k_ticks[0]); | ||
90 | do { | ||
91 | r4k_ticks[1] = dosample(); | ||
92 | } while (!r4k_ticks[1]); | ||
93 | |||
94 | if (r4k_ticks[0] != r4k_ticks[1]) { | ||
95 | printk("warning: timer counts differ, retrying... "); | ||
96 | r4k_ticks[2] = dosample(); | ||
97 | if (r4k_ticks[2] == r4k_ticks[0] | ||
98 | || r4k_ticks[2] == r4k_ticks[1]) | ||
99 | r4k_tick = r4k_ticks[2]; | ||
100 | else { | ||
101 | printk("disagreement, using average... "); | ||
102 | r4k_tick = (r4k_ticks[0] + r4k_ticks[1] | ||
103 | + r4k_ticks[2]) / 3; | ||
104 | } | ||
105 | } else | ||
106 | r4k_tick = r4k_ticks[0]; | ||
107 | |||
108 | printk("%d [%d.%04d MHz CPU]\n", (int) r4k_tick, | ||
109 | (int) (r4k_tick / (500000 / HZ)), | ||
110 | (int) (r4k_tick % (500000 / HZ))); | ||
111 | |||
112 | mips_hpt_frequency = r4k_tick * HZ; | ||
113 | |||
114 | if (ip22_is_fullhouse()) | ||
115 | setup_pit_timer(); | ||
116 | } | ||
117 | |||
118 | /* Generic SGI handler for (spurious) 8254 interrupts */ | ||
119 | void __irq_entry indy_8254timer_irq(void) | ||
120 | { | ||
121 | int irq = SGI_8254_0_IRQ; | ||
122 | ULONG cnt; | ||
123 | char c; | ||
124 | |||
125 | irq_enter(); | ||
126 | kstat_incr_irq_this_cpu(irq); | ||
127 | printk(KERN_ALERT "Oops, got 8254 interrupt.\n"); | ||
128 | ArcRead(0, &c, 1, &cnt); | ||
129 | ArcEnterInteractiveMode(); | ||
130 | irq_exit(); | ||
131 | } | ||
diff --git a/arch/mips/sgi-ip22/ip28-berr.c b/arch/mips/sgi-ip22/ip28-berr.c new file mode 100644 index 000000000..c61362d9e --- /dev/null +++ b/arch/mips/sgi-ip22/ip28-berr.c | |||
@@ -0,0 +1,488 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * ip28-berr.c: Bus error handling. | ||
4 | * | ||
5 | * Copyright (C) 2002, 2003 Ladislav Michl (ladis@linux-mips.org) | ||
6 | * Copyright (C) 2005 Peter Fuerst (pf@net.alphadv.de) - IP28 | ||
7 | */ | ||
8 | |||
9 | #include <linux/init.h> | ||
10 | #include <linux/kernel.h> | ||
11 | #include <linux/mm.h> | ||
12 | #include <linux/sched.h> | ||
13 | #include <linux/sched/debug.h> | ||
14 | #include <linux/sched/signal.h> | ||
15 | #include <linux/seq_file.h> | ||
16 | |||
17 | #include <asm/addrspace.h> | ||
18 | #include <asm/traps.h> | ||
19 | #include <asm/branch.h> | ||
20 | #include <asm/irq_regs.h> | ||
21 | #include <asm/sgi/mc.h> | ||
22 | #include <asm/sgi/hpc3.h> | ||
23 | #include <asm/sgi/ioc.h> | ||
24 | #include <asm/sgi/ip22.h> | ||
25 | #include <asm/r4kcache.h> | ||
26 | #include <linux/uaccess.h> | ||
27 | #include <asm/bootinfo.h> | ||
28 | |||
29 | static unsigned int count_be_is_fixup; | ||
30 | static unsigned int count_be_handler; | ||
31 | static unsigned int count_be_interrupt; | ||
32 | static int debug_be_interrupt; | ||
33 | |||
34 | static unsigned int cpu_err_stat; /* Status reg for CPU */ | ||
35 | static unsigned int gio_err_stat; /* Status reg for GIO */ | ||
36 | static unsigned int cpu_err_addr; /* Error address reg for CPU */ | ||
37 | static unsigned int gio_err_addr; /* Error address reg for GIO */ | ||
38 | static unsigned int extio_stat; | ||
39 | static unsigned int hpc3_berr_stat; /* Bus error interrupt status */ | ||
40 | |||
41 | struct hpc3_stat { | ||
42 | unsigned long addr; | ||
43 | unsigned int ctrl; | ||
44 | unsigned int cbp; | ||
45 | unsigned int ndptr; | ||
46 | }; | ||
47 | |||
48 | static struct { | ||
49 | struct hpc3_stat pbdma[8]; | ||
50 | struct hpc3_stat scsi[2]; | ||
51 | struct hpc3_stat ethrx, ethtx; | ||
52 | } hpc3; | ||
53 | |||
54 | static struct { | ||
55 | unsigned long err_addr; | ||
56 | struct { | ||
57 | u32 lo; | ||
58 | u32 hi; | ||
59 | } tags[1][2], tagd[4][2], tagi[4][2]; /* Way 0/1 */ | ||
60 | } cache_tags; | ||
61 | |||
62 | static inline void save_cache_tags(unsigned busaddr) | ||
63 | { | ||
64 | unsigned long addr = CAC_BASE | busaddr; | ||
65 | int i; | ||
66 | cache_tags.err_addr = addr; | ||
67 | |||
68 | /* | ||
69 | * Starting with a bus-address, save secondary cache (indexed by | ||
70 | * PA[23..18:7..6]) tags first. | ||
71 | */ | ||
72 | addr &= ~1L; | ||
73 | #define tag cache_tags.tags[0] | ||
74 | cache_op(Index_Load_Tag_S, addr); | ||
75 | tag[0].lo = read_c0_taglo(); /* PA[35:18], VA[13:12] */ | ||
76 | tag[0].hi = read_c0_taghi(); /* PA[39:36] */ | ||
77 | cache_op(Index_Load_Tag_S, addr | 1L); | ||
78 | tag[1].lo = read_c0_taglo(); /* PA[35:18], VA[13:12] */ | ||
79 | tag[1].hi = read_c0_taghi(); /* PA[39:36] */ | ||
80 | #undef tag | ||
81 | |||
82 | /* | ||
83 | * Save all primary data cache (indexed by VA[13:5]) tags which | ||
84 | * might fit to this bus-address, knowing that VA[11:0] == PA[11:0]. | ||
85 | * Saving all tags and evaluating them later is easier and safer | ||
86 | * than relying on VA[13:12] from the secondary cache tags to pick | ||
87 | * matching primary tags here already. | ||
88 | */ | ||
89 | addr &= (0xffL << 56) | ((1 << 12) - 1); | ||
90 | #define tag cache_tags.tagd[i] | ||
91 | for (i = 0; i < 4; ++i, addr += (1 << 12)) { | ||
92 | cache_op(Index_Load_Tag_D, addr); | ||
93 | tag[0].lo = read_c0_taglo(); /* PA[35:12] */ | ||
94 | tag[0].hi = read_c0_taghi(); /* PA[39:36] */ | ||
95 | cache_op(Index_Load_Tag_D, addr | 1L); | ||
96 | tag[1].lo = read_c0_taglo(); /* PA[35:12] */ | ||
97 | tag[1].hi = read_c0_taghi(); /* PA[39:36] */ | ||
98 | } | ||
99 | #undef tag | ||
100 | |||
101 | /* | ||
102 | * Save primary instruction cache (indexed by VA[13:6]) tags | ||
103 | * the same way. | ||
104 | */ | ||
105 | addr &= (0xffL << 56) | ((1 << 12) - 1); | ||
106 | #define tag cache_tags.tagi[i] | ||
107 | for (i = 0; i < 4; ++i, addr += (1 << 12)) { | ||
108 | cache_op(Index_Load_Tag_I, addr); | ||
109 | tag[0].lo = read_c0_taglo(); /* PA[35:12] */ | ||
110 | tag[0].hi = read_c0_taghi(); /* PA[39:36] */ | ||
111 | cache_op(Index_Load_Tag_I, addr | 1L); | ||
112 | tag[1].lo = read_c0_taglo(); /* PA[35:12] */ | ||
113 | tag[1].hi = read_c0_taghi(); /* PA[39:36] */ | ||
114 | } | ||
115 | #undef tag | ||
116 | } | ||
117 | |||
118 | #define GIO_ERRMASK 0xff00 | ||
119 | #define CPU_ERRMASK 0x3f00 | ||
120 | |||
121 | static void save_and_clear_buserr(void) | ||
122 | { | ||
123 | int i; | ||
124 | |||
125 | /* save status registers */ | ||
126 | cpu_err_addr = sgimc->cerr; | ||
127 | cpu_err_stat = sgimc->cstat; | ||
128 | gio_err_addr = sgimc->gerr; | ||
129 | gio_err_stat = sgimc->gstat; | ||
130 | extio_stat = sgioc->extio; | ||
131 | hpc3_berr_stat = hpc3c0->bestat; | ||
132 | |||
133 | hpc3.scsi[0].addr = (unsigned long)&hpc3c0->scsi_chan0; | ||
134 | hpc3.scsi[0].ctrl = hpc3c0->scsi_chan0.ctrl; /* HPC3_SCTRL_ACTIVE ? */ | ||
135 | hpc3.scsi[0].cbp = hpc3c0->scsi_chan0.cbptr; | ||
136 | hpc3.scsi[0].ndptr = hpc3c0->scsi_chan0.ndptr; | ||
137 | |||
138 | hpc3.scsi[1].addr = (unsigned long)&hpc3c0->scsi_chan1; | ||
139 | hpc3.scsi[1].ctrl = hpc3c0->scsi_chan1.ctrl; /* HPC3_SCTRL_ACTIVE ? */ | ||
140 | hpc3.scsi[1].cbp = hpc3c0->scsi_chan1.cbptr; | ||
141 | hpc3.scsi[1].ndptr = hpc3c0->scsi_chan1.ndptr; | ||
142 | |||
143 | hpc3.ethrx.addr = (unsigned long)&hpc3c0->ethregs.rx_cbptr; | ||
144 | hpc3.ethrx.ctrl = hpc3c0->ethregs.rx_ctrl; /* HPC3_ERXCTRL_ACTIVE ? */ | ||
145 | hpc3.ethrx.cbp = hpc3c0->ethregs.rx_cbptr; | ||
146 | hpc3.ethrx.ndptr = hpc3c0->ethregs.rx_ndptr; | ||
147 | |||
148 | hpc3.ethtx.addr = (unsigned long)&hpc3c0->ethregs.tx_cbptr; | ||
149 | hpc3.ethtx.ctrl = hpc3c0->ethregs.tx_ctrl; /* HPC3_ETXCTRL_ACTIVE ? */ | ||
150 | hpc3.ethtx.cbp = hpc3c0->ethregs.tx_cbptr; | ||
151 | hpc3.ethtx.ndptr = hpc3c0->ethregs.tx_ndptr; | ||
152 | |||
153 | for (i = 0; i < 8; ++i) { | ||
154 | /* HPC3_PDMACTRL_ISACT ? */ | ||
155 | hpc3.pbdma[i].addr = (unsigned long)&hpc3c0->pbdma[i]; | ||
156 | hpc3.pbdma[i].ctrl = hpc3c0->pbdma[i].pbdma_ctrl; | ||
157 | hpc3.pbdma[i].cbp = hpc3c0->pbdma[i].pbdma_bptr; | ||
158 | hpc3.pbdma[i].ndptr = hpc3c0->pbdma[i].pbdma_dptr; | ||
159 | } | ||
160 | i = 0; | ||
161 | if (gio_err_stat & CPU_ERRMASK) | ||
162 | i = gio_err_addr; | ||
163 | if (cpu_err_stat & CPU_ERRMASK) | ||
164 | i = cpu_err_addr; | ||
165 | save_cache_tags(i); | ||
166 | |||
167 | sgimc->cstat = sgimc->gstat = 0; | ||
168 | } | ||
169 | |||
170 | static void print_cache_tags(void) | ||
171 | { | ||
172 | u32 scb, scw; | ||
173 | int i; | ||
174 | |||
175 | printk(KERN_ERR "Cache tags @ %08x:\n", (unsigned)cache_tags.err_addr); | ||
176 | |||
177 | /* PA[31:12] shifted to PTag0 (PA[35:12]) format */ | ||
178 | scw = (cache_tags.err_addr >> 4) & 0x0fffff00; | ||
179 | |||
180 | scb = cache_tags.err_addr & ((1 << 12) - 1) & ~((1 << 5) - 1); | ||
181 | for (i = 0; i < 4; ++i) { /* for each possible VA[13:12] value */ | ||
182 | if ((cache_tags.tagd[i][0].lo & 0x0fffff00) != scw && | ||
183 | (cache_tags.tagd[i][1].lo & 0x0fffff00) != scw) | ||
184 | continue; | ||
185 | printk(KERN_ERR | ||
186 | "D: 0: %08x %08x, 1: %08x %08x (VA[13:5] %04x)\n", | ||
187 | cache_tags.tagd[i][0].hi, cache_tags.tagd[i][0].lo, | ||
188 | cache_tags.tagd[i][1].hi, cache_tags.tagd[i][1].lo, | ||
189 | scb | (1 << 12)*i); | ||
190 | } | ||
191 | scb = cache_tags.err_addr & ((1 << 12) - 1) & ~((1 << 6) - 1); | ||
192 | for (i = 0; i < 4; ++i) { /* for each possible VA[13:12] value */ | ||
193 | if ((cache_tags.tagi[i][0].lo & 0x0fffff00) != scw && | ||
194 | (cache_tags.tagi[i][1].lo & 0x0fffff00) != scw) | ||
195 | continue; | ||
196 | printk(KERN_ERR | ||
197 | "I: 0: %08x %08x, 1: %08x %08x (VA[13:6] %04x)\n", | ||
198 | cache_tags.tagi[i][0].hi, cache_tags.tagi[i][0].lo, | ||
199 | cache_tags.tagi[i][1].hi, cache_tags.tagi[i][1].lo, | ||
200 | scb | (1 << 12)*i); | ||
201 | } | ||
202 | i = read_c0_config(); | ||
203 | scb = i & (1 << 13) ? 7:6; /* scblksize = 2^[7..6] */ | ||
204 | scw = ((i >> 16) & 7) + 19 - 1; /* scwaysize = 2^[24..19] / 2 */ | ||
205 | |||
206 | i = ((1 << scw) - 1) & ~((1 << scb) - 1); | ||
207 | printk(KERN_ERR "S: 0: %08x %08x, 1: %08x %08x (PA[%u:%u] %05x)\n", | ||
208 | cache_tags.tags[0][0].hi, cache_tags.tags[0][0].lo, | ||
209 | cache_tags.tags[0][1].hi, cache_tags.tags[0][1].lo, | ||
210 | scw-1, scb, i & (unsigned)cache_tags.err_addr); | ||
211 | } | ||
212 | |||
213 | static inline const char *cause_excode_text(int cause) | ||
214 | { | ||
215 | static const char *txt[32] = | ||
216 | { "Interrupt", | ||
217 | "TLB modification", | ||
218 | "TLB (load or instruction fetch)", | ||
219 | "TLB (store)", | ||
220 | "Address error (load or instruction fetch)", | ||
221 | "Address error (store)", | ||
222 | "Bus error (instruction fetch)", | ||
223 | "Bus error (data: load or store)", | ||
224 | "Syscall", | ||
225 | "Breakpoint", | ||
226 | "Reserved instruction", | ||
227 | "Coprocessor unusable", | ||
228 | "Arithmetic Overflow", | ||
229 | "Trap", | ||
230 | "14", | ||
231 | "Floating-Point", | ||
232 | "16", "17", "18", "19", "20", "21", "22", | ||
233 | "Watch Hi/Lo", | ||
234 | "24", "25", "26", "27", "28", "29", "30", "31", | ||
235 | }; | ||
236 | return txt[(cause & 0x7c) >> 2]; | ||
237 | } | ||
238 | |||
239 | static void print_buserr(const struct pt_regs *regs) | ||
240 | { | ||
241 | const int field = 2 * sizeof(unsigned long); | ||
242 | int error = 0; | ||
243 | |||
244 | if (extio_stat & EXTIO_MC_BUSERR) { | ||
245 | printk(KERN_ERR "MC Bus Error\n"); | ||
246 | error |= 1; | ||
247 | } | ||
248 | if (extio_stat & EXTIO_HPC3_BUSERR) { | ||
249 | printk(KERN_ERR "HPC3 Bus Error 0x%x:<id=0x%x,%s,lane=0x%x>\n", | ||
250 | hpc3_berr_stat, | ||
251 | (hpc3_berr_stat & HPC3_BESTAT_PIDMASK) >> | ||
252 | HPC3_BESTAT_PIDSHIFT, | ||
253 | (hpc3_berr_stat & HPC3_BESTAT_CTYPE) ? "PIO" : "DMA", | ||
254 | hpc3_berr_stat & HPC3_BESTAT_BLMASK); | ||
255 | error |= 2; | ||
256 | } | ||
257 | if (extio_stat & EXTIO_EISA_BUSERR) { | ||
258 | printk(KERN_ERR "EISA Bus Error\n"); | ||
259 | error |= 4; | ||
260 | } | ||
261 | if (cpu_err_stat & CPU_ERRMASK) { | ||
262 | printk(KERN_ERR "CPU error 0x%x<%s%s%s%s%s%s> @ 0x%08x\n", | ||
263 | cpu_err_stat, | ||
264 | cpu_err_stat & SGIMC_CSTAT_RD ? "RD " : "", | ||
265 | cpu_err_stat & SGIMC_CSTAT_PAR ? "PAR " : "", | ||
266 | cpu_err_stat & SGIMC_CSTAT_ADDR ? "ADDR " : "", | ||
267 | cpu_err_stat & SGIMC_CSTAT_SYSAD_PAR ? "SYSAD " : "", | ||
268 | cpu_err_stat & SGIMC_CSTAT_SYSCMD_PAR ? "SYSCMD " : "", | ||
269 | cpu_err_stat & SGIMC_CSTAT_BAD_DATA ? "BAD_DATA " : "", | ||
270 | cpu_err_addr); | ||
271 | error |= 8; | ||
272 | } | ||
273 | if (gio_err_stat & GIO_ERRMASK) { | ||
274 | printk(KERN_ERR "GIO error 0x%x:<%s%s%s%s%s%s%s%s> @ 0x%08x\n", | ||
275 | gio_err_stat, | ||
276 | gio_err_stat & SGIMC_GSTAT_RD ? "RD " : "", | ||
277 | gio_err_stat & SGIMC_GSTAT_WR ? "WR " : "", | ||
278 | gio_err_stat & SGIMC_GSTAT_TIME ? "TIME " : "", | ||
279 | gio_err_stat & SGIMC_GSTAT_PROM ? "PROM " : "", | ||
280 | gio_err_stat & SGIMC_GSTAT_ADDR ? "ADDR " : "", | ||
281 | gio_err_stat & SGIMC_GSTAT_BC ? "BC " : "", | ||
282 | gio_err_stat & SGIMC_GSTAT_PIO_RD ? "PIO_RD " : "", | ||
283 | gio_err_stat & SGIMC_GSTAT_PIO_WR ? "PIO_WR " : "", | ||
284 | gio_err_addr); | ||
285 | error |= 16; | ||
286 | } | ||
287 | if (!error) | ||
288 | printk(KERN_ERR "MC: Hmm, didn't find any error condition.\n"); | ||
289 | else { | ||
290 | printk(KERN_ERR "CP0: config %08x, " | ||
291 | "MC: cpuctrl0/1: %08x/%05x, giopar: %04x\n" | ||
292 | "MC: cpu/gio_memacc: %08x/%05x, memcfg0/1: %08x/%08x\n", | ||
293 | read_c0_config(), | ||
294 | sgimc->cpuctrl0, sgimc->cpuctrl0, sgimc->giopar, | ||
295 | sgimc->cmacc, sgimc->gmacc, | ||
296 | sgimc->mconfig0, sgimc->mconfig1); | ||
297 | print_cache_tags(); | ||
298 | } | ||
299 | printk(KERN_ALERT "%s, epc == %0*lx, ra == %0*lx\n", | ||
300 | cause_excode_text(regs->cp0_cause), | ||
301 | field, regs->cp0_epc, field, regs->regs[31]); | ||
302 | } | ||
303 | |||
304 | static int check_microtlb(u32 hi, u32 lo, unsigned long vaddr) | ||
305 | { | ||
306 | /* This is likely rather similar to correct code ;-) */ | ||
307 | |||
308 | vaddr &= 0x7fffffff; /* Doc. states that top bit is ignored */ | ||
309 | |||
310 | /* If tlb-entry is valid and VPN-high (bits [30:21] ?) matches... */ | ||
311 | if ((lo & 2) && (vaddr >> 21) == ((hi<<1) >> 22)) { | ||
312 | u32 ctl = sgimc->dma_ctrl; | ||
313 | if (ctl & 1) { | ||
314 | unsigned int pgsz = (ctl & 2) ? 14:12; /* 16k:4k */ | ||
315 | /* PTEIndex is VPN-low (bits [22:14]/[20:12] ?) */ | ||
316 | unsigned long pte = (lo >> 6) << 12; /* PTEBase */ | ||
317 | pte += 8*((vaddr >> pgsz) & 0x1ff); | ||
318 | if (page_is_ram(PFN_DOWN(pte))) { | ||
319 | /* | ||
320 | * Note: Since DMA hardware does look up | ||
321 | * translation on its own, this PTE *must* | ||
322 | * match the TLB/EntryLo-register format ! | ||
323 | */ | ||
324 | unsigned long a = *(unsigned long *) | ||
325 | PHYS_TO_XKSEG_UNCACHED(pte); | ||
326 | a = (a & 0x3f) << 6; /* PFN */ | ||
327 | a += vaddr & ((1 << pgsz) - 1); | ||
328 | return cpu_err_addr == a; | ||
329 | } | ||
330 | } | ||
331 | } | ||
332 | return 0; | ||
333 | } | ||
334 | |||
335 | static int check_vdma_memaddr(void) | ||
336 | { | ||
337 | if (cpu_err_stat & CPU_ERRMASK) { | ||
338 | u32 a = sgimc->maddronly; | ||
339 | |||
340 | if (!(sgimc->dma_ctrl & 0x100)) /* Xlate-bit clear ? */ | ||
341 | return cpu_err_addr == a; | ||
342 | |||
343 | if (check_microtlb(sgimc->dtlb_hi0, sgimc->dtlb_lo0, a) || | ||
344 | check_microtlb(sgimc->dtlb_hi1, sgimc->dtlb_lo1, a) || | ||
345 | check_microtlb(sgimc->dtlb_hi2, sgimc->dtlb_lo2, a) || | ||
346 | check_microtlb(sgimc->dtlb_hi3, sgimc->dtlb_lo3, a)) | ||
347 | return 1; | ||
348 | } | ||
349 | return 0; | ||
350 | } | ||
351 | |||
352 | static int check_vdma_gioaddr(void) | ||
353 | { | ||
354 | if (gio_err_stat & GIO_ERRMASK) { | ||
355 | u32 a = sgimc->gio_dma_trans; | ||
356 | a = (sgimc->gmaddronly & ~a) | (sgimc->gio_dma_sbits & a); | ||
357 | return gio_err_addr == a; | ||
358 | } | ||
359 | return 0; | ||
360 | } | ||
361 | |||
362 | /* | ||
363 | * MC sends an interrupt whenever bus or parity errors occur. In addition, | ||
364 | * if the error happened during a CPU read, it also asserts the bus error | ||
365 | * pin on the R4K. Code in bus error handler save the MC bus error registers | ||
366 | * and then clear the interrupt when this happens. | ||
367 | */ | ||
368 | |||
369 | static int ip28_be_interrupt(const struct pt_regs *regs) | ||
370 | { | ||
371 | int i; | ||
372 | |||
373 | save_and_clear_buserr(); | ||
374 | /* | ||
375 | * Try to find out, whether we got here by a mispredicted speculative | ||
376 | * load/store operation. If so, it's not fatal, we can go on. | ||
377 | */ | ||
378 | /* Any cause other than "Interrupt" (ExcCode 0) is fatal. */ | ||
379 | if (regs->cp0_cause & CAUSEF_EXCCODE) | ||
380 | goto mips_be_fatal; | ||
381 | |||
382 | /* Any cause other than "Bus error interrupt" (IP6) is weird. */ | ||
383 | if ((regs->cp0_cause & CAUSEF_IP6) != CAUSEF_IP6) | ||
384 | goto mips_be_fatal; | ||
385 | |||
386 | if (extio_stat & (EXTIO_HPC3_BUSERR | EXTIO_EISA_BUSERR)) | ||
387 | goto mips_be_fatal; | ||
388 | |||
389 | /* Any state other than "Memory bus error" is fatal. */ | ||
390 | if (cpu_err_stat & CPU_ERRMASK & ~SGIMC_CSTAT_ADDR) | ||
391 | goto mips_be_fatal; | ||
392 | |||
393 | /* GIO errors other than timeouts are fatal */ | ||
394 | if (gio_err_stat & GIO_ERRMASK & ~SGIMC_GSTAT_TIME) | ||
395 | goto mips_be_fatal; | ||
396 | |||
397 | /* | ||
398 | * Now we have an asynchronous bus error, speculatively or DMA caused. | ||
399 | * Need to search all DMA descriptors for the error address. | ||
400 | */ | ||
401 | for (i = 0; i < sizeof(hpc3)/sizeof(struct hpc3_stat); ++i) { | ||
402 | struct hpc3_stat *hp = (struct hpc3_stat *)&hpc3 + i; | ||
403 | if ((cpu_err_stat & CPU_ERRMASK) && | ||
404 | (cpu_err_addr == hp->ndptr || cpu_err_addr == hp->cbp)) | ||
405 | break; | ||
406 | if ((gio_err_stat & GIO_ERRMASK) && | ||
407 | (gio_err_addr == hp->ndptr || gio_err_addr == hp->cbp)) | ||
408 | break; | ||
409 | } | ||
410 | if (i < sizeof(hpc3)/sizeof(struct hpc3_stat)) { | ||
411 | struct hpc3_stat *hp = (struct hpc3_stat *)&hpc3 + i; | ||
412 | printk(KERN_ERR "at DMA addresses: HPC3 @ %08lx:" | ||
413 | " ctl %08x, ndp %08x, cbp %08x\n", | ||
414 | CPHYSADDR(hp->addr), hp->ctrl, hp->ndptr, hp->cbp); | ||
415 | goto mips_be_fatal; | ||
416 | } | ||
417 | /* Check MC's virtual DMA stuff. */ | ||
418 | if (check_vdma_memaddr()) { | ||
419 | printk(KERN_ERR "at GIO DMA: mem address 0x%08x.\n", | ||
420 | sgimc->maddronly); | ||
421 | goto mips_be_fatal; | ||
422 | } | ||
423 | if (check_vdma_gioaddr()) { | ||
424 | printk(KERN_ERR "at GIO DMA: gio address 0x%08x.\n", | ||
425 | sgimc->gmaddronly); | ||
426 | goto mips_be_fatal; | ||
427 | } | ||
428 | /* A speculative bus error... */ | ||
429 | if (debug_be_interrupt) { | ||
430 | print_buserr(regs); | ||
431 | printk(KERN_ERR "discarded!\n"); | ||
432 | } | ||
433 | return MIPS_BE_DISCARD; | ||
434 | |||
435 | mips_be_fatal: | ||
436 | print_buserr(regs); | ||
437 | return MIPS_BE_FATAL; | ||
438 | } | ||
439 | |||
440 | void ip22_be_interrupt(int irq) | ||
441 | { | ||
442 | struct pt_regs *regs = get_irq_regs(); | ||
443 | |||
444 | count_be_interrupt++; | ||
445 | |||
446 | if (ip28_be_interrupt(regs) != MIPS_BE_DISCARD) { | ||
447 | /* Assume it would be too dangerous to continue ... */ | ||
448 | die_if_kernel("Oops", regs); | ||
449 | force_sig(SIGBUS); | ||
450 | } else if (debug_be_interrupt) | ||
451 | show_regs(regs); | ||
452 | } | ||
453 | |||
454 | static int ip28_be_handler(struct pt_regs *regs, int is_fixup) | ||
455 | { | ||
456 | /* | ||
457 | * We arrive here only in the unusual case of do_be() invocation, | ||
458 | * i.e. by a bus error exception without a bus error interrupt. | ||
459 | */ | ||
460 | if (is_fixup) { | ||
461 | count_be_is_fixup++; | ||
462 | save_and_clear_buserr(); | ||
463 | return MIPS_BE_FIXUP; | ||
464 | } | ||
465 | count_be_handler++; | ||
466 | return ip28_be_interrupt(regs); | ||
467 | } | ||
468 | |||
469 | void __init ip22_be_init(void) | ||
470 | { | ||
471 | board_be_handler = ip28_be_handler; | ||
472 | } | ||
473 | |||
474 | int ip28_show_be_info(struct seq_file *m) | ||
475 | { | ||
476 | seq_printf(m, "IP28 be fixups\t\t: %u\n", count_be_is_fixup); | ||
477 | seq_printf(m, "IP28 be interrupts\t: %u\n", count_be_interrupt); | ||
478 | seq_printf(m, "IP28 be handler\t\t: %u\n", count_be_handler); | ||
479 | |||
480 | return 0; | ||
481 | } | ||
482 | |||
483 | static int __init debug_be_setup(char *str) | ||
484 | { | ||
485 | debug_be_interrupt++; | ||
486 | return 1; | ||
487 | } | ||
488 | __setup("ip28_debug_be", debug_be_setup); | ||