aboutsummaryrefslogtreecommitdiffstats
path: root/arch/mips/sgi-ip22
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 18:20:36 -0400
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 18:20:36 -0400
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/mips/sgi-ip22
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'arch/mips/sgi-ip22')
-rw-r--r--arch/mips/sgi-ip22/Makefile11
-rw-r--r--arch/mips/sgi-ip22/ip22-berr.c114
-rw-r--r--arch/mips/sgi-ip22/ip22-eisa.c308
-rw-r--r--arch/mips/sgi-ip22/ip22-hpc.c63
-rw-r--r--arch/mips/sgi-ip22/ip22-int.c410
-rw-r--r--arch/mips/sgi-ip22/ip22-irq.S118
-rw-r--r--arch/mips/sgi-ip22/ip22-mc.c208
-rw-r--r--arch/mips/sgi-ip22/ip22-nvram.c119
-rw-r--r--arch/mips/sgi-ip22/ip22-reset.c247
-rw-r--r--arch/mips/sgi-ip22/ip22-setup.c144
-rw-r--r--arch/mips/sgi-ip22/ip22-time.c214
11 files changed, 1956 insertions, 0 deletions
diff --git a/arch/mips/sgi-ip22/Makefile b/arch/mips/sgi-ip22/Makefile
new file mode 100644
index 000000000000..eb0820fe50bd
--- /dev/null
+++ b/arch/mips/sgi-ip22/Makefile
@@ -0,0 +1,11 @@
1#
2# Makefile for the SGI specific kernel interface routines
3# under Linux.
4#
5
6obj-y += ip22-mc.o ip22-hpc.o ip22-int.o ip22-irq.o ip22-berr.o \
7 ip22-time.o ip22-nvram.o ip22-reset.o ip22-setup.o
8
9obj-$(CONFIG_EISA) += ip22-eisa.o
10
11EXTRA_AFLAGS := $(CFLAGS)
diff --git a/arch/mips/sgi-ip22/ip22-berr.c b/arch/mips/sgi-ip22/ip22-berr.c
new file mode 100644
index 000000000000..a28dc7800072
--- /dev/null
+++ b/arch/mips/sgi-ip22/ip22-berr.c
@@ -0,0 +1,114 @@
1/*
2 * ip22-berr.c: Bus error handling.
3 *
4 * Copyright (C) 2002, 2003 Ladislav Michl (ladis@linux-mips.org)
5 */
6
7#include <linux/init.h>
8#include <linux/kernel.h>
9#include <linux/sched.h>
10
11#include <asm/addrspace.h>
12#include <asm/system.h>
13#include <asm/traps.h>
14#include <asm/branch.h>
15#include <asm/sgi/mc.h>
16#include <asm/sgi/hpc3.h>
17#include <asm/sgi/ioc.h>
18#include <asm/sgi/ip22.h>
19
20
21static unsigned int cpu_err_stat; /* Status reg for CPU */
22static unsigned int gio_err_stat; /* Status reg for GIO */
23static unsigned int cpu_err_addr; /* Error address reg for CPU */
24static unsigned int gio_err_addr; /* Error address reg for GIO */
25static unsigned int extio_stat;
26static unsigned int hpc3_berr_stat; /* Bus error interrupt status */
27
28static void save_and_clear_buserr(void)
29{
30 /* save status registers */
31 cpu_err_addr = sgimc->cerr;
32 cpu_err_stat = sgimc->cstat;
33 gio_err_addr = sgimc->gerr;
34 gio_err_stat = sgimc->gstat;
35 extio_stat = ip22_is_fullhouse() ? sgioc->extio : (sgint->errstat << 4);
36 hpc3_berr_stat = hpc3c0->bestat;
37
38 sgimc->cstat = sgimc->gstat = 0;
39}
40
41#define GIO_ERRMASK 0xff00
42#define CPU_ERRMASK 0x3f00
43
44static void print_buserr(void)
45{
46 if (extio_stat & EXTIO_MC_BUSERR)
47 printk(KERN_ERR "MC Bus Error\n");
48 if (extio_stat & EXTIO_HPC3_BUSERR)
49 printk(KERN_ERR "HPC3 Bus Error 0x%x:<id=0x%x,%s,lane=0x%x>\n",
50 hpc3_berr_stat,
51 (hpc3_berr_stat & HPC3_BESTAT_PIDMASK) >>
52 HPC3_BESTAT_PIDSHIFT,
53 (hpc3_berr_stat & HPC3_BESTAT_CTYPE) ? "PIO" : "DMA",
54 hpc3_berr_stat & HPC3_BESTAT_BLMASK);
55 if (extio_stat & EXTIO_EISA_BUSERR)
56 printk(KERN_ERR "EISA Bus Error\n");
57 if (cpu_err_stat & CPU_ERRMASK)
58 printk(KERN_ERR "CPU error 0x%x<%s%s%s%s%s%s> @ 0x%08x\n",
59 cpu_err_stat,
60 cpu_err_stat & SGIMC_CSTAT_RD ? "RD " : "",
61 cpu_err_stat & SGIMC_CSTAT_PAR ? "PAR " : "",
62 cpu_err_stat & SGIMC_CSTAT_ADDR ? "ADDR " : "",
63 cpu_err_stat & SGIMC_CSTAT_SYSAD_PAR ? "SYSAD " : "",
64 cpu_err_stat & SGIMC_CSTAT_SYSCMD_PAR ? "SYSCMD " : "",
65 cpu_err_stat & SGIMC_CSTAT_BAD_DATA ? "BAD_DATA " : "",
66 cpu_err_addr);
67 if (gio_err_stat & GIO_ERRMASK)
68 printk(KERN_ERR "GIO error 0x%x:<%s%s%s%s%s%s%s%s> @ 0x%08x\n",
69 gio_err_stat,
70 gio_err_stat & SGIMC_GSTAT_RD ? "RD " : "",
71 gio_err_stat & SGIMC_GSTAT_WR ? "WR " : "",
72 gio_err_stat & SGIMC_GSTAT_TIME ? "TIME " : "",
73 gio_err_stat & SGIMC_GSTAT_PROM ? "PROM " : "",
74 gio_err_stat & SGIMC_GSTAT_ADDR ? "ADDR " : "",
75 gio_err_stat & SGIMC_GSTAT_BC ? "BC " : "",
76 gio_err_stat & SGIMC_GSTAT_PIO_RD ? "PIO_RD " : "",
77 gio_err_stat & SGIMC_GSTAT_PIO_WR ? "PIO_WR " : "",
78 gio_err_addr);
79}
80
81/*
82 * MC sends an interrupt whenever bus or parity errors occur. In addition,
83 * if the error happened during a CPU read, it also asserts the bus error
84 * pin on the R4K. Code in bus error handler save the MC bus error registers
85 * and then clear the interrupt when this happens.
86 */
87
88void ip22_be_interrupt(int irq, struct pt_regs *regs)
89{
90 const int field = 2 * sizeof(unsigned long);
91
92 save_and_clear_buserr();
93 print_buserr();
94 printk(KERN_ALERT "%s bus error, epc == %0*lx, ra == %0*lx\n",
95 (regs->cp0_cause & 4) ? "Data" : "Instruction",
96 field, regs->cp0_epc, field, regs->regs[31]);
97 /* Assume it would be too dangerous to continue ... */
98 die_if_kernel("Oops", regs);
99 force_sig(SIGBUS, current);
100}
101
102static int ip22_be_handler(struct pt_regs *regs, int is_fixup)
103{
104 save_and_clear_buserr();
105 if (is_fixup)
106 return MIPS_BE_FIXUP;
107 print_buserr();
108 return MIPS_BE_FATAL;
109}
110
111void __init ip22_be_init(void)
112{
113 board_be_handler = ip22_be_handler;
114}
diff --git a/arch/mips/sgi-ip22/ip22-eisa.c b/arch/mips/sgi-ip22/ip22-eisa.c
new file mode 100644
index 000000000000..0ab4abf65d58
--- /dev/null
+++ b/arch/mips/sgi-ip22/ip22-eisa.c
@@ -0,0 +1,308 @@
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/config.h>
23#include <linux/eisa.h>
24#include <linux/types.h>
25#include <linux/init.h>
26#include <linux/irq.h>
27#include <linux/kernel_stat.h>
28#include <linux/signal.h>
29#include <linux/sched.h>
30#include <linux/interrupt.h>
31#include <linux/delay.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
40#define EISA_MAX_SLOTS 4
41#define EISA_MAX_IRQ 16
42
43#define EISA_TO_PHYS(x) (0x00080000 | (x))
44#define EISA_TO_KSEG1(x) ((void *) KSEG1ADDR(EISA_TO_PHYS((x))))
45
46#define EIU_MODE_REG 0x0009ffc0
47#define EIU_STAT_REG 0x0009ffc4
48#define EIU_PREMPT_REG 0x0009ffc8
49#define EIU_QUIET_REG 0x0009ffcc
50#define EIU_INTRPT_ACK 0x00090004
51
52#define EISA_DMA1_STATUS 8
53#define EISA_INT1_CTRL 0x20
54#define EISA_INT1_MASK 0x21
55#define EISA_INT2_CTRL 0xA0
56#define EISA_INT2_MASK 0xA1
57#define EISA_DMA2_STATUS 0xD0
58#define EISA_DMA2_WRITE_SINGLE 0xD4
59#define EISA_EXT_NMI_RESET_CTRL 0x461
60#define EISA_INT1_EDGE_LEVEL 0x4D0
61#define EISA_INT2_EDGE_LEVEL 0x4D1
62#define EISA_VENDOR_ID_OFFSET 0xC80
63
64#define EIU_WRITE_32(x,y) { *((u32 *) KSEG1ADDR(x)) = (u32) (y); mb(); }
65#define EIU_READ_8(x) *((u8 *) KSEG1ADDR(x))
66#define EISA_WRITE_8(x,y) { *((u8 *) EISA_TO_KSEG1(x)) = (u8) (y); mb(); }
67#define EISA_READ_8(x) *((u8 *) EISA_TO_KSEG1(x))
68
69static char *decode_eisa_sig(u8 * sig)
70{
71 static char sig_str[8];
72 u16 rev;
73
74 if (sig[0] & 0x80)
75 return NULL;
76
77 sig_str[0] = ((sig[0] >> 2) & 0x1f) + ('A' - 1);
78 sig_str[1] = (((sig[0] & 3) << 3) | (sig[1] >> 5)) + ('A' - 1);
79 sig_str[2] = (sig[1] & 0x1f) + ('A' - 1);
80 rev = (sig[2] << 8) | sig[3];
81 sprintf(sig_str + 3, "%04X", rev);
82
83 return sig_str;
84}
85
86static void ip22_eisa_intr(int irq, void *dev_id, struct pt_regs *regs)
87{
88 u8 eisa_irq;
89 u8 dma1, dma2;
90
91 eisa_irq = EIU_READ_8(EIU_INTRPT_ACK);
92 dma1 = EISA_READ_8(EISA_DMA1_STATUS);
93 dma2 = EISA_READ_8(EISA_DMA2_STATUS);
94
95 if (eisa_irq >= EISA_MAX_IRQ) {
96 /* Oops, Bad Stuff Happened... */
97 printk(KERN_ERR "eisa_irq %d out of bound\n", eisa_irq);
98
99 EISA_WRITE_8(EISA_INT2_CTRL, 0x20);
100 EISA_WRITE_8(EISA_INT1_CTRL, 0x20);
101 } else
102 do_IRQ(eisa_irq, regs);
103}
104
105static void enable_eisa1_irq(unsigned int irq)
106{
107 unsigned long flags;
108 u8 mask;
109
110 local_irq_save(flags);
111
112 mask = EISA_READ_8(EISA_INT1_MASK);
113 mask &= ~((u8) (1 << irq));
114 EISA_WRITE_8(EISA_INT1_MASK, mask);
115
116 local_irq_restore(flags);
117}
118
119static unsigned int startup_eisa1_irq(unsigned int irq)
120{
121 u8 edge;
122
123 /* Only use edge interrupts for EISA */
124
125 edge = EISA_READ_8(EISA_INT1_EDGE_LEVEL);
126 edge &= ~((u8) (1 << irq));
127 EISA_WRITE_8(EISA_INT1_EDGE_LEVEL, edge);
128
129 enable_eisa1_irq(irq);
130 return 0;
131}
132
133static void disable_eisa1_irq(unsigned int irq)
134{
135 u8 mask;
136
137 mask = EISA_READ_8(EISA_INT1_MASK);
138 mask |= ((u8) (1 << irq));
139 EISA_WRITE_8(EISA_INT1_MASK, mask);
140}
141
142#define shutdown_eisa1_irq disable_eisa1_irq
143
144static void mask_and_ack_eisa1_irq(unsigned int irq)
145{
146 disable_eisa1_irq(irq);
147
148 EISA_WRITE_8(EISA_INT1_CTRL, 0x20);
149}
150
151static void end_eisa1_irq(unsigned int irq)
152{
153 if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS)))
154 enable_eisa1_irq(irq);
155}
156
157static struct hw_interrupt_type ip22_eisa1_irq_type = {
158 .typename = "IP22 EISA",
159 .startup = startup_eisa1_irq,
160 .shutdown = shutdown_eisa1_irq,
161 .enable = enable_eisa1_irq,
162 .disable = disable_eisa1_irq,
163 .ack = mask_and_ack_eisa1_irq,
164 .end = end_eisa1_irq,
165};
166
167static void enable_eisa2_irq(unsigned int irq)
168{
169 unsigned long flags;
170 u8 mask;
171
172 local_irq_save(flags);
173
174 mask = EISA_READ_8(EISA_INT2_MASK);
175 mask &= ~((u8) (1 << (irq - 8)));
176 EISA_WRITE_8(EISA_INT2_MASK, mask);
177
178 local_irq_restore(flags);
179}
180
181static unsigned int startup_eisa2_irq(unsigned int irq)
182{
183 u8 edge;
184
185 /* Only use edge interrupts for EISA */
186
187 edge = EISA_READ_8(EISA_INT2_EDGE_LEVEL);
188 edge &= ~((u8) (1 << (irq - 8)));
189 EISA_WRITE_8(EISA_INT2_EDGE_LEVEL, edge);
190
191 enable_eisa2_irq(irq);
192 return 0;
193}
194
195static void disable_eisa2_irq(unsigned int irq)
196{
197 u8 mask;
198
199 mask = EISA_READ_8(EISA_INT2_MASK);
200 mask |= ((u8) (1 << (irq - 8)));
201 EISA_WRITE_8(EISA_INT2_MASK, mask);
202}
203
204#define shutdown_eisa2_irq disable_eisa2_irq
205
206static void mask_and_ack_eisa2_irq(unsigned int irq)
207{
208 disable_eisa2_irq(irq);
209
210 EISA_WRITE_8(EISA_INT2_CTRL, 0x20);
211 EISA_WRITE_8(EISA_INT1_CTRL, 0x20);
212}
213
214static void end_eisa2_irq(unsigned int irq)
215{
216 if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS)))
217 enable_eisa2_irq(irq);
218}
219
220static struct hw_interrupt_type ip22_eisa2_irq_type = {
221 .typename = "IP22 EISA",
222 .startup = startup_eisa2_irq,
223 .shutdown = shutdown_eisa2_irq,
224 .enable = enable_eisa2_irq,
225 .disable = disable_eisa2_irq,
226 .ack = mask_and_ack_eisa2_irq,
227 .end = end_eisa2_irq,
228};
229
230static struct irqaction eisa_action = {
231 .handler = ip22_eisa_intr,
232 .name = "EISA",
233};
234
235static struct irqaction cascade_action = {
236 .handler = no_action,
237 .name = "EISA cascade",
238};
239
240int __init ip22_eisa_init(void)
241{
242 int i, c;
243 char *str;
244 u8 *slot_addr;
245
246 if (!(sgimc->systemid & SGIMC_SYSID_EPRESENT)) {
247 printk(KERN_INFO "EISA: bus not present.\n");
248 return 1;
249 }
250
251 printk(KERN_INFO "EISA: Probing bus...\n");
252 for (c = 0, i = 1; i <= EISA_MAX_SLOTS; i++) {
253 slot_addr =
254 (u8 *) EISA_TO_KSEG1((0x1000 * i) +
255 EISA_VENDOR_ID_OFFSET);
256 if ((str = decode_eisa_sig(slot_addr))) {
257 printk(KERN_INFO "EISA: slot %d : %s detected.\n",
258 i, str);
259 c++;
260 }
261 }
262 printk(KERN_INFO "EISA: Detected %d card%s.\n", c, c < 2 ? "" : "s");
263#ifdef CONFIG_ISA
264 printk(KERN_INFO "ISA support compiled in.\n");
265#endif
266
267 /* Warning : BlackMagicAhead(tm).
268 Please wave your favorite dead chicken over the busses */
269
270 /* First say hello to the EIU */
271 EIU_WRITE_32(EIU_PREMPT_REG, 0x0000FFFF);
272 EIU_WRITE_32(EIU_QUIET_REG, 1);
273 EIU_WRITE_32(EIU_MODE_REG, 0x40f3c07F);
274
275 /* Now be nice to the EISA chipset */
276 EISA_WRITE_8(EISA_EXT_NMI_RESET_CTRL, 1);
277 for (i = 0; i < 10000; i++); /* Wait long enough for the dust to settle */
278 EISA_WRITE_8(EISA_EXT_NMI_RESET_CTRL, 0);
279 EISA_WRITE_8(EISA_INT1_CTRL, 0x11);
280 EISA_WRITE_8(EISA_INT2_CTRL, 0x11);
281 EISA_WRITE_8(EISA_INT1_MASK, 0);
282 EISA_WRITE_8(EISA_INT2_MASK, 8);
283 EISA_WRITE_8(EISA_INT1_MASK, 4);
284 EISA_WRITE_8(EISA_INT2_MASK, 2);
285 EISA_WRITE_8(EISA_INT1_MASK, 1);
286 EISA_WRITE_8(EISA_INT2_MASK, 1);
287 EISA_WRITE_8(EISA_INT1_MASK, 0xfb);
288 EISA_WRITE_8(EISA_INT2_MASK, 0xff);
289 EISA_WRITE_8(EISA_DMA2_WRITE_SINGLE, 0);
290
291 for (i = SGINT_EISA; i < (SGINT_EISA + EISA_MAX_IRQ); i++) {
292 irq_desc[i].status = IRQ_DISABLED;
293 irq_desc[i].action = 0;
294 irq_desc[i].depth = 1;
295 if (i < (SGINT_EISA + 8))
296 irq_desc[i].handler = &ip22_eisa1_irq_type;
297 else
298 irq_desc[i].handler = &ip22_eisa2_irq_type;
299 }
300
301 /* Cannot use request_irq because of kmalloc not being ready at such
302 * an early stage. Yes, I've been bitten... */
303 setup_irq(SGI_EISA_IRQ, &eisa_action);
304 setup_irq(SGINT_EISA + 2, &cascade_action);
305
306 EISA_bus = 1;
307 return 0;
308}
diff --git a/arch/mips/sgi-ip22/ip22-hpc.c b/arch/mips/sgi-ip22/ip22-hpc.c
new file mode 100644
index 000000000000..c0afeccb08c4
--- /dev/null
+++ b/arch/mips/sgi-ip22/ip22-hpc.c
@@ -0,0 +1,63 @@
1/*
2 * ip22-hpc.c: Routines for generic manipulation of the HPC controllers.
3 *
4 * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com)
5 * Copyright (C) 1998 Ralf Baechle
6 */
7
8#include <linux/init.h>
9#include <linux/module.h>
10#include <linux/types.h>
11
12#include <asm/io.h>
13#include <asm/sgi/hpc3.h>
14#include <asm/sgi/ioc.h>
15#include <asm/sgi/ip22.h>
16
17struct hpc3_regs *hpc3c0, *hpc3c1;
18
19EXPORT_SYMBOL(hpc3c0);
20EXPORT_SYMBOL(hpc3c1);
21
22struct sgioc_regs *sgioc;
23
24EXPORT_SYMBOL(sgioc);
25
26/* We need software copies of these because they are write only. */
27u8 sgi_ioc_reset, sgi_ioc_write;
28
29extern char *system_type;
30
31void __init sgihpc_init(void)
32{
33 /* ioremap can't fail */
34 hpc3c0 = (struct hpc3_regs *)
35 ioremap(HPC3_CHIP0_BASE, sizeof(struct hpc3_regs));
36 hpc3c1 = (struct hpc3_regs *)
37 ioremap(HPC3_CHIP1_BASE, sizeof(struct hpc3_regs));
38 /* IOC lives in PBUS PIO channel 6 */
39 sgioc = (struct sgioc_regs *)hpc3c0->pbus_extregs[6];
40
41 hpc3c0->pbus_piocfg[6][0] |= HPC3_PIOCFG_DS16;
42 if (ip22_is_fullhouse()) {
43 /* Full House comes with INT2 which lives in PBUS PIO
44 * channel 4 */
45 sgint = (struct sgint_regs *)hpc3c0->pbus_extregs[4];
46 system_type = "SGI Indigo2";
47 } else {
48 /* Guiness comes with INT3 which is part of IOC */
49 sgint = &sgioc->int3;
50 system_type = "SGI Indy";
51 }
52
53 sgi_ioc_reset = (SGIOC_RESET_PPORT | SGIOC_RESET_KBDMOUSE |
54 SGIOC_RESET_EISA | SGIOC_RESET_ISDN |
55 SGIOC_RESET_LC0OFF);
56
57 sgi_ioc_write = (SGIOC_WRITE_EASEL | SGIOC_WRITE_NTHRESH |
58 SGIOC_WRITE_TPSPEED | SGIOC_WRITE_EPSEL |
59 SGIOC_WRITE_U0AMODE | SGIOC_WRITE_U1AMODE);
60
61 sgioc->reset = sgi_ioc_reset;
62 sgioc->write = sgi_ioc_write;
63}
diff --git a/arch/mips/sgi-ip22/ip22-int.c b/arch/mips/sgi-ip22/ip22-int.c
new file mode 100644
index 000000000000..ea2844d29e6e
--- /dev/null
+++ b/arch/mips/sgi-ip22/ip22-int.c
@@ -0,0 +1,410 @@
1/*
2 * ip22-int.c: Routines for generic manipulation of the INT[23] ASIC
3 * found on INDY and Indigo2 workstations.
4 *
5 * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com)
6 * Copyright (C) 1997, 1998 Ralf Baechle (ralf@gnu.org)
7 * Copyright (C) 1999 Andrew R. Baker (andrewb@uab.edu)
8 * - Indigo2 changes
9 * - Interrupt handling fixes
10 * Copyright (C) 2001, 2003 Ladislav Michl (ladis@linux-mips.org)
11 */
12#include <linux/config.h>
13#include <linux/types.h>
14#include <linux/init.h>
15#include <linux/kernel_stat.h>
16#include <linux/signal.h>
17#include <linux/sched.h>
18#include <linux/interrupt.h>
19#include <linux/irq.h>
20
21#include <asm/mipsregs.h>
22#include <asm/addrspace.h>
23
24#include <asm/sgi/ioc.h>
25#include <asm/sgi/hpc3.h>
26#include <asm/sgi/ip22.h>
27
28/* #define DEBUG_SGINT */
29
30/* So far nothing hangs here */
31#undef USE_LIO3_IRQ
32
33struct sgint_regs *sgint;
34
35static char lc0msk_to_irqnr[256];
36static char lc1msk_to_irqnr[256];
37static char lc2msk_to_irqnr[256];
38static char lc3msk_to_irqnr[256];
39
40extern asmlinkage void indyIRQ(void);
41extern int ip22_eisa_init(void);
42
43static void enable_local0_irq(unsigned int irq)
44{
45 unsigned long flags;
46
47 local_irq_save(flags);
48 /* don't allow mappable interrupt to be enabled from setup_irq,
49 * we have our own way to do so */
50 if (irq != SGI_MAP_0_IRQ)
51 sgint->imask0 |= (1 << (irq - SGINT_LOCAL0));
52 local_irq_restore(flags);
53}
54
55static unsigned int startup_local0_irq(unsigned int irq)
56{
57 enable_local0_irq(irq);
58 return 0; /* Never anything pending */
59}
60
61static void disable_local0_irq(unsigned int irq)
62{
63 unsigned long flags;
64
65 local_irq_save(flags);
66 sgint->imask0 &= ~(1 << (irq - SGINT_LOCAL0));
67 local_irq_restore(flags);
68}
69
70#define shutdown_local0_irq disable_local0_irq
71#define mask_and_ack_local0_irq disable_local0_irq
72
73static void end_local0_irq (unsigned int irq)
74{
75 if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
76 enable_local0_irq(irq);
77}
78
79static struct hw_interrupt_type ip22_local0_irq_type = {
80 .typename = "IP22 local 0",
81 .startup = startup_local0_irq,
82 .shutdown = shutdown_local0_irq,
83 .enable = enable_local0_irq,
84 .disable = disable_local0_irq,
85 .ack = mask_and_ack_local0_irq,
86 .end = end_local0_irq,
87};
88
89static void enable_local1_irq(unsigned int irq)
90{
91 unsigned long flags;
92
93 local_irq_save(flags);
94 /* don't allow mappable interrupt to be enabled from setup_irq,
95 * we have our own way to do so */
96 if (irq != SGI_MAP_1_IRQ)
97 sgint->imask1 |= (1 << (irq - SGINT_LOCAL1));
98 local_irq_restore(flags);
99}
100
101static unsigned int startup_local1_irq(unsigned int irq)
102{
103 enable_local1_irq(irq);
104 return 0; /* Never anything pending */
105}
106
107void disable_local1_irq(unsigned int irq)
108{
109 unsigned long flags;
110
111 local_irq_save(flags);
112 sgint->imask1 &= ~(1 << (irq - SGINT_LOCAL1));
113 local_irq_restore(flags);
114}
115
116#define shutdown_local1_irq disable_local1_irq
117#define mask_and_ack_local1_irq disable_local1_irq
118
119static void end_local1_irq (unsigned int irq)
120{
121 if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
122 enable_local1_irq(irq);
123}
124
125static struct hw_interrupt_type ip22_local1_irq_type = {
126 .typename = "IP22 local 1",
127 .startup = startup_local1_irq,
128 .shutdown = shutdown_local1_irq,
129 .enable = enable_local1_irq,
130 .disable = disable_local1_irq,
131 .ack = mask_and_ack_local1_irq,
132 .end = end_local1_irq,
133};
134
135static void enable_local2_irq(unsigned int irq)
136{
137 unsigned long flags;
138
139 local_irq_save(flags);
140 sgint->imask0 |= (1 << (SGI_MAP_0_IRQ - SGINT_LOCAL0));
141 sgint->cmeimask0 |= (1 << (irq - SGINT_LOCAL2));
142 local_irq_restore(flags);
143}
144
145static unsigned int startup_local2_irq(unsigned int irq)
146{
147 enable_local2_irq(irq);
148 return 0; /* Never anything pending */
149}
150
151void disable_local2_irq(unsigned int irq)
152{
153 unsigned long flags;
154
155 local_irq_save(flags);
156 sgint->cmeimask0 &= ~(1 << (irq - SGINT_LOCAL2));
157 if (!sgint->cmeimask0)
158 sgint->imask0 &= ~(1 << (SGI_MAP_0_IRQ - SGINT_LOCAL0));
159 local_irq_restore(flags);
160}
161
162#define shutdown_local2_irq disable_local2_irq
163#define mask_and_ack_local2_irq disable_local2_irq
164
165static void end_local2_irq (unsigned int irq)
166{
167 if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
168 enable_local2_irq(irq);
169}
170
171static struct hw_interrupt_type ip22_local2_irq_type = {
172 .typename = "IP22 local 2",
173 .startup = startup_local2_irq,
174 .shutdown = shutdown_local2_irq,
175 .enable = enable_local2_irq,
176 .disable = disable_local2_irq,
177 .ack = mask_and_ack_local2_irq,
178 .end = end_local2_irq,
179};
180
181static void enable_local3_irq(unsigned int irq)
182{
183 unsigned long flags;
184
185 local_irq_save(flags);
186 sgint->imask1 |= (1 << (SGI_MAP_1_IRQ - SGINT_LOCAL1));
187 sgint->cmeimask1 |= (1 << (irq - SGINT_LOCAL3));
188 local_irq_restore(flags);
189}
190
191static unsigned int startup_local3_irq(unsigned int irq)
192{
193 enable_local3_irq(irq);
194 return 0; /* Never anything pending */
195}
196
197void disable_local3_irq(unsigned int irq)
198{
199 unsigned long flags;
200
201 local_irq_save(flags);
202 sgint->cmeimask1 &= ~(1 << (irq - SGINT_LOCAL3));
203 if (!sgint->cmeimask1)
204 sgint->imask1 &= ~(1 << (SGI_MAP_1_IRQ - SGINT_LOCAL1));
205 local_irq_restore(flags);
206}
207
208#define shutdown_local3_irq disable_local3_irq
209#define mask_and_ack_local3_irq disable_local3_irq
210
211static void end_local3_irq (unsigned int irq)
212{
213 if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
214 enable_local3_irq(irq);
215}
216
217static struct hw_interrupt_type ip22_local3_irq_type = {
218 .typename = "IP22 local 3",
219 .startup = startup_local3_irq,
220 .shutdown = shutdown_local3_irq,
221 .enable = enable_local3_irq,
222 .disable = disable_local3_irq,
223 .ack = mask_and_ack_local3_irq,
224 .end = end_local3_irq,
225};
226
227void indy_local0_irqdispatch(struct pt_regs *regs)
228{
229 u8 mask = sgint->istat0 & sgint->imask0;
230 u8 mask2;
231 int irq;
232
233 if (mask & SGINT_ISTAT0_LIO2) {
234 mask2 = sgint->vmeistat & sgint->cmeimask0;
235 irq = lc2msk_to_irqnr[mask2];
236 } else
237 irq = lc0msk_to_irqnr[mask];
238
239 /* if irq == 0, then the interrupt has already been cleared */
240 if (irq)
241 do_IRQ(irq, regs);
242 return;
243}
244
245void indy_local1_irqdispatch(struct pt_regs *regs)
246{
247 u8 mask = sgint->istat1 & sgint->imask1;
248 u8 mask2;
249 int irq;
250
251 if (mask & SGINT_ISTAT1_LIO3) {
252 mask2 = sgint->vmeistat & sgint->cmeimask1;
253 irq = lc3msk_to_irqnr[mask2];
254 } else
255 irq = lc1msk_to_irqnr[mask];
256
257 /* if irq == 0, then the interrupt has already been cleared */
258 if (irq)
259 do_IRQ(irq, regs);
260 return;
261}
262
263extern void ip22_be_interrupt(int irq, struct pt_regs *regs);
264
265void indy_buserror_irq(struct pt_regs *regs)
266{
267 int irq = SGI_BUSERR_IRQ;
268
269 irq_enter();
270 kstat_this_cpu.irqs[irq]++;
271 ip22_be_interrupt(irq, regs);
272 irq_exit();
273}
274
275static struct irqaction local0_cascade = {
276 .handler = no_action,
277 .flags = SA_INTERRUPT,
278 .name = "local0 cascade",
279};
280
281static struct irqaction local1_cascade = {
282 .handler = no_action,
283 .flags = SA_INTERRUPT,
284 .name = "local1 cascade",
285};
286
287static struct irqaction buserr = {
288 .handler = no_action,
289 .flags = SA_INTERRUPT,
290 .name = "Bus Error",
291};
292
293static struct irqaction map0_cascade = {
294 .handler = no_action,
295 .flags = SA_INTERRUPT,
296 .name = "mapable0 cascade",
297};
298
299#ifdef USE_LIO3_IRQ
300static struct irqaction map1_cascade = {
301 .handler = no_action,
302 .flags = SA_INTERRUPT,
303 .name = "mapable1 cascade",
304};
305#define SGI_INTERRUPTS SGINT_END
306#else
307#define SGI_INTERRUPTS SGINT_LOCAL3
308#endif
309
310extern void mips_cpu_irq_init(unsigned int irq_base);
311
312void __init arch_init_irq(void)
313{
314 int i;
315
316 /* Init local mask --> irq tables. */
317 for (i = 0; i < 256; i++) {
318 if (i & 0x80) {
319 lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 7;
320 lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 7;
321 lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 7;
322 lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 7;
323 } else if (i & 0x40) {
324 lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 6;
325 lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 6;
326 lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 6;
327 lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 6;
328 } else if (i & 0x20) {
329 lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 5;
330 lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 5;
331 lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 5;
332 lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 5;
333 } else if (i & 0x10) {
334 lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 4;
335 lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 4;
336 lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 4;
337 lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 4;
338 } else if (i & 0x08) {
339 lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 3;
340 lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 3;
341 lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 3;
342 lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 3;
343 } else if (i & 0x04) {
344 lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 2;
345 lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 2;
346 lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 2;
347 lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 2;
348 } else if (i & 0x02) {
349 lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 1;
350 lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 1;
351 lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 1;
352 lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 1;
353 } else if (i & 0x01) {
354 lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 0;
355 lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 0;
356 lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 0;
357 lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 0;
358 } else {
359 lc0msk_to_irqnr[i] = 0;
360 lc1msk_to_irqnr[i] = 0;
361 lc2msk_to_irqnr[i] = 0;
362 lc3msk_to_irqnr[i] = 0;
363 }
364 }
365
366 /* Mask out all interrupts. */
367 sgint->imask0 = 0;
368 sgint->imask1 = 0;
369 sgint->cmeimask0 = 0;
370 sgint->cmeimask1 = 0;
371
372 set_except_vector(0, indyIRQ);
373
374 /* init CPU irqs */
375 mips_cpu_irq_init(SGINT_CPU);
376
377 for (i = SGINT_LOCAL0; i < SGI_INTERRUPTS; i++) {
378 hw_irq_controller *handler;
379
380 if (i < SGINT_LOCAL1)
381 handler = &ip22_local0_irq_type;
382 else if (i < SGINT_LOCAL2)
383 handler = &ip22_local1_irq_type;
384 else if (i < SGINT_LOCAL3)
385 handler = &ip22_local2_irq_type;
386 else
387 handler = &ip22_local3_irq_type;
388
389 irq_desc[i].status = IRQ_DISABLED;
390 irq_desc[i].action = 0;
391 irq_desc[i].depth = 1;
392 irq_desc[i].handler = handler;
393 }
394
395 /* vector handler. this register the IRQ as non-sharable */
396 setup_irq(SGI_LOCAL_0_IRQ, &local0_cascade);
397 setup_irq(SGI_LOCAL_1_IRQ, &local1_cascade);
398 setup_irq(SGI_BUSERR_IRQ, &buserr);
399
400 /* cascade in cascade. i love Indy ;-) */
401 setup_irq(SGI_MAP_0_IRQ, &map0_cascade);
402#ifdef USE_LIO3_IRQ
403 setup_irq(SGI_MAP_1_IRQ, &map1_cascade);
404#endif
405
406#ifdef CONFIG_EISA
407 if (ip22_is_fullhouse()) /* Only Indigo-2 has EISA stuff */
408 ip22_eisa_init ();
409#endif
410}
diff --git a/arch/mips/sgi-ip22/ip22-irq.S b/arch/mips/sgi-ip22/ip22-irq.S
new file mode 100644
index 000000000000..6ccbd9e1d967
--- /dev/null
+++ b/arch/mips/sgi-ip22/ip22-irq.S
@@ -0,0 +1,118 @@
1/*
2 * ip22-irq.S: Interrupt exception dispatch code for FullHouse and
3 * Guiness.
4 *
5 * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com)
6 */
7
8#include <asm/asm.h>
9#include <asm/mipsregs.h>
10#include <asm/regdef.h>
11#include <asm/stackframe.h>
12
13/* A lot of complication here is taken away because:
14 *
15 * 1) We handle one interrupt and return, sitting in a loop and moving across
16 * all the pending IRQ bits in the cause register is _NOT_ the answer, the
17 * common case is one pending IRQ so optimize in that direction.
18 *
19 * 2) We need not check against bits in the status register IRQ mask, that
20 * would make this routine slow as hell.
21 *
22 * 3) Linux only thinks in terms of all IRQs on or all IRQs off, nothing in
23 * between like BSD spl() brain-damage.
24 *
25 * Furthermore, the IRQs on the INDY look basically (barring software IRQs
26 * which we don't use at all) like:
27 *
28 * MIPS IRQ Source
29 * -------- ------
30 * 0 Software (ignored)
31 * 1 Software (ignored)
32 * 2 Local IRQ level zero
33 * 3 Local IRQ level one
34 * 4 8254 Timer zero
35 * 5 8254 Timer one
36 * 6 Bus Error
37 * 7 R4k timer (what we use)
38 *
39 * We handle the IRQ according to _our_ priority which is:
40 *
41 * Highest ---- R4k Timer
42 * Local IRQ zero
43 * Local IRQ one
44 * Bus Error
45 * 8254 Timer zero
46 * Lowest ---- 8254 Timer one
47 *
48 * then we just return, if multiple IRQs are pending then we will just take
49 * another exception, big deal.
50 */
51
52 .text
53 .set noreorder
54 .set noat
55 .align 5
56 NESTED(indyIRQ, PT_SIZE, sp)
57 SAVE_ALL
58 CLI
59 .set at
60 mfc0 s0, CP0_CAUSE # get irq mask
61
62 /* First we check for r4k counter/timer IRQ. */
63 andi a0, s0, CAUSEF_IP7
64 beq a0, zero, 1f
65 andi a0, s0, CAUSEF_IP2 # delay slot, check local level zero
66
67 /* Wheee, a timer interrupt. */
68 jal indy_r4k_timer_interrupt
69 move a0, sp # delay slot
70 j ret_from_irq
71 nop # delay slot
72
731:
74 beq a0, zero, 1f
75 andi a0, s0, CAUSEF_IP3 # delay slot, check local level one
76
77 /* Wheee, local level zero interrupt. */
78 jal indy_local0_irqdispatch
79 move a0, sp # delay slot
80
81 j ret_from_irq
82 nop # delay slot
83
841:
85 beq a0, zero, 1f
86 andi a0, s0, CAUSEF_IP6 # delay slot, check bus error
87
88 /* Wheee, local level one interrupt. */
89 jal indy_local1_irqdispatch
90 move a0, sp # delay slot
91 j ret_from_irq
92 nop # delay slot
93
941:
95 beq a0, zero, 1f
96 andi a0, s0, (CAUSEF_IP4 | CAUSEF_IP5) # delay slot
97
98 /* Wheee, an asynchronous bus error... */
99 jal indy_buserror_irq
100 move a0, sp # delay slot
101 j ret_from_irq
102 nop # delay slot
103
1041:
105 /* Here by mistake? It is possible, that by the time we take
106 * the exception the IRQ pin goes low, so just leave if this
107 * is the case.
108 */
109 beq a0, zero, 1f
110 nop # delay slot
111
112 /* Must be one of the 8254 timers... */
113 jal indy_8254timer_irq
114 move a0, sp # delay slot
1151:
116 j ret_from_irq
117 nop # delay slot
118 END(indyIRQ)
diff --git a/arch/mips/sgi-ip22/ip22-mc.c b/arch/mips/sgi-ip22/ip22-mc.c
new file mode 100644
index 000000000000..b58bd522262b
--- /dev/null
+++ b/arch/mips/sgi-ip22/ip22-mc.c
@@ -0,0 +1,208 @@
1/*
2 * ip22-mc.c: Routines for manipulating SGI Memory Controller.
3 *
4 * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com)
5 * Copyright (C) 1999 Andrew R. Baker (andrewb@uab.edu) - Indigo2 changes
6 * Copyright (C) 2003 Ladislav Michl (ladis@linux-mips.org)
7 */
8
9#include <linux/init.h>
10#include <linux/module.h>
11#include <linux/kernel.h>
12
13#include <asm/io.h>
14#include <asm/bootinfo.h>
15#include <asm/sgialib.h>
16#include <asm/sgi/mc.h>
17#include <asm/sgi/hpc3.h>
18#include <asm/sgi/ip22.h>
19
20struct sgimc_regs *sgimc;
21
22EXPORT_SYMBOL(sgimc);
23
24static inline unsigned long get_bank_addr(unsigned int memconfig)
25{
26 return ((memconfig & SGIMC_MCONFIG_BASEADDR) <<
27 ((sgimc->systemid & SGIMC_SYSID_MASKREV) >= 5 ? 24 : 22));
28}
29
30static inline unsigned long get_bank_size(unsigned int memconfig)
31{
32 return ((memconfig & SGIMC_MCONFIG_RMASK) + 0x0100) <<
33 ((sgimc->systemid & SGIMC_SYSID_MASKREV) >= 5 ? 16 : 14);
34}
35
36static inline unsigned int get_bank_config(int bank)
37{
38 unsigned int res = bank > 1 ? sgimc->mconfig1 : sgimc->mconfig0;
39 return bank % 2 ? res & 0xffff : res >> 16;
40}
41
42struct mem {
43 unsigned long addr;
44 unsigned long size;
45};
46
47/*
48 * Detect installed memory, do some sanity checks and notify kernel about it
49 */
50static void probe_memory(void)
51{
52 int i, j, found, cnt = 0;
53 struct mem bank[4];
54 struct mem space[2] = {{SGIMC_SEG0_BADDR, 0}, {SGIMC_SEG1_BADDR, 0}};
55
56 printk(KERN_INFO "MC: Probing memory configuration:\n");
57 for (i = 0; i < ARRAY_SIZE(bank); i++) {
58 unsigned int tmp = get_bank_config(i);
59 if (!(tmp & SGIMC_MCONFIG_BVALID))
60 continue;
61
62 bank[cnt].size = get_bank_size(tmp);
63 bank[cnt].addr = get_bank_addr(tmp);
64 printk(KERN_INFO " bank%d: %3ldM @ %08lx\n",
65 i, bank[cnt].size / 1024 / 1024, bank[cnt].addr);
66 cnt++;
67 }
68
69 /* And you thought bubble sort is dead algorithm... */
70 do {
71 unsigned long addr, size;
72
73 found = 0;
74 for (i = 1; i < cnt; i++)
75 if (bank[i-1].addr > bank[i].addr) {
76 addr = bank[i].addr;
77 size = bank[i].size;
78 bank[i].addr = bank[i-1].addr;
79 bank[i].size = bank[i-1].size;
80 bank[i-1].addr = addr;
81 bank[i-1].size = size;
82 found = 1;
83 }
84 } while (found);
85
86 /* Figure out how are memory banks mapped into spaces */
87 for (i = 0; i < cnt; i++) {
88 found = 0;
89 for (j = 0; j < ARRAY_SIZE(space) && !found; j++)
90 if (space[j].addr + space[j].size == bank[i].addr) {
91 space[j].size += bank[i].size;
92 found = 1;
93 }
94 /* There is either hole or overlapping memory */
95 if (!found)
96 printk(KERN_CRIT "MC: Memory configuration mismatch "
97 "(%08lx), expect Bus Error soon\n",
98 bank[i].addr);
99 }
100
101 for (i = 0; i < ARRAY_SIZE(space); i++)
102 if (space[i].size)
103 add_memory_region(space[i].addr, space[i].size,
104 BOOT_MEM_RAM);
105}
106
107void __init sgimc_init(void)
108{
109 u32 tmp;
110
111 /* ioremap can't fail */
112 sgimc = (struct sgimc_regs *)
113 ioremap(SGIMC_BASE, sizeof(struct sgimc_regs));
114
115 printk(KERN_INFO "MC: SGI memory controller Revision %d\n",
116 (int) sgimc->systemid & SGIMC_SYSID_MASKREV);
117
118 /* Place the MC into a known state. This must be done before
119 * interrupts are first enabled etc.
120 */
121
122 /* Step 0: Make sure we turn off the watchdog in case it's
123 * still running (which might be the case after a
124 * soft reboot).
125 */
126 tmp = sgimc->cpuctrl0;
127 tmp &= ~SGIMC_CCTRL0_WDOG;
128 sgimc->cpuctrl0 = tmp;
129
130 /* Step 1: The CPU/GIO error status registers will not latch
131 * up a new error status until the register has been
132 * cleared by the cpu. These status registers are
133 * cleared by writing any value to them.
134 */
135 sgimc->cstat = sgimc->gstat = 0;
136
137 /* Step 2: Enable all parity checking in cpu control register
138 * zero.
139 */
140 tmp = sgimc->cpuctrl0;
141 tmp |= (SGIMC_CCTRL0_EPERRGIO | SGIMC_CCTRL0_EPERRMEM |
142 SGIMC_CCTRL0_R4KNOCHKPARR);
143 sgimc->cpuctrl0 = tmp;
144
145 /* Step 3: Setup the MC write buffer depth, this is controlled
146 * in cpu control register 1 in the lower 4 bits.
147 */
148 tmp = sgimc->cpuctrl1;
149 tmp &= ~0xf;
150 tmp |= 0xd;
151 sgimc->cpuctrl1 = tmp;
152
153 /* Step 4: Initialize the RPSS divider register to run as fast
154 * as it can correctly operate. The register is laid
155 * out as follows:
156 *
157 * ----------------------------------------
158 * | RESERVED | INCREMENT | DIVIDER |
159 * ----------------------------------------
160 * 31 16 15 8 7 0
161 *
162 * DIVIDER determines how often a 'tick' happens,
163 * INCREMENT determines by how the RPSS increment
164 * registers value increases at each 'tick'. Thus,
165 * for IP22 we get INCREMENT=1, DIVIDER=1 == 0x101
166 */
167 sgimc->divider = 0x101;
168
169 /* Step 5: Initialize GIO64 arbitrator configuration register.
170 *
171 * NOTE: HPC init code in sgihpc_init() must run before us because
172 * we need to know Guiness vs. FullHouse and the board
173 * revision on this machine. You have been warned.
174 */
175
176 /* First the basic invariants across all GIO64 implementations. */
177 tmp = SGIMC_GIOPAR_HPC64; /* All 1st HPC's interface at 64bits */
178 tmp |= SGIMC_GIOPAR_ONEBUS; /* Only one physical GIO bus exists */
179
180 if (ip22_is_fullhouse()) {
181 /* Fullhouse specific settings. */
182 if (SGIOC_SYSID_BOARDREV(sgioc->sysid) < 2) {
183 tmp |= SGIMC_GIOPAR_HPC264; /* 2nd HPC at 64bits */
184 tmp |= SGIMC_GIOPAR_PLINEEXP0; /* exp0 pipelines */
185 tmp |= SGIMC_GIOPAR_MASTEREXP1; /* exp1 masters */
186 tmp |= SGIMC_GIOPAR_RTIMEEXP0; /* exp0 is realtime */
187 } else {
188 tmp |= SGIMC_GIOPAR_HPC264; /* 2nd HPC 64bits */
189 tmp |= SGIMC_GIOPAR_PLINEEXP0; /* exp[01] pipelined */
190 tmp |= SGIMC_GIOPAR_PLINEEXP1;
191 tmp |= SGIMC_GIOPAR_MASTEREISA; /* EISA masters */
192 tmp |= SGIMC_GIOPAR_GFX64; /* GFX at 64 bits */
193 }
194 } else {
195 /* Guiness specific settings. */
196 tmp |= SGIMC_GIOPAR_EISA64; /* MC talks to EISA at 64bits */
197 tmp |= SGIMC_GIOPAR_MASTEREISA; /* EISA bus can act as master */
198 }
199 sgimc->giopar = tmp; /* poof */
200
201 probe_memory();
202}
203
204void __init prom_meminit(void) {}
205unsigned long __init prom_free_prom_memory(void)
206{
207 return 0;
208}
diff --git a/arch/mips/sgi-ip22/ip22-nvram.c b/arch/mips/sgi-ip22/ip22-nvram.c
new file mode 100644
index 000000000000..de43e86fa17c
--- /dev/null
+++ b/arch/mips/sgi-ip22/ip22-nvram.c
@@ -0,0 +1,119 @@
1/*
2 * ip22-nvram.c: NVRAM and serial EEPROM handling.
3 *
4 * Copyright (C) 2003 Ladislav Michl (ladis@linux-mips.org)
5 */
6#include <linux/module.h>
7
8#include <asm/sgi/hpc3.h>
9#include <asm/sgi/ip22.h>
10
11/* Control opcode for serial eeprom */
12#define EEPROM_READ 0xc000 /* serial memory read */
13#define EEPROM_WEN 0x9800 /* write enable before prog modes */
14#define EEPROM_WRITE 0xa000 /* serial memory write */
15#define EEPROM_WRALL 0x8800 /* write all registers */
16#define EEPROM_WDS 0x8000 /* disable all programming */
17#define EEPROM_PRREAD 0xc000 /* read protect register */
18#define EEPROM_PREN 0x9800 /* enable protect register mode */
19#define EEPROM_PRCLEAR 0xffff /* clear protect register */
20#define EEPROM_PRWRITE 0xa000 /* write protect register */
21#define EEPROM_PRDS 0x8000 /* disable protect register, forever */
22
23#define EEPROM_EPROT 0x01 /* Protect register enable */
24#define EEPROM_CSEL 0x02 /* Chip select */
25#define EEPROM_ECLK 0x04 /* EEPROM clock */
26#define EEPROM_DATO 0x08 /* Data out */
27#define EEPROM_DATI 0x10 /* Data in */
28
29/* We need to use these functions early... */
30#define delay() ({ \
31 int x; \
32 for (x=0; x<100000; x++) __asm__ __volatile__(""); })
33
34#define eeprom_cs_on(ptr) ({ \
35 *ptr &= ~EEPROM_DATO; \
36 *ptr &= ~EEPROM_ECLK; \
37 *ptr &= ~EEPROM_EPROT; \
38 delay(); \
39 *ptr |= EEPROM_CSEL; \
40 *ptr |= EEPROM_ECLK; })
41
42
43#define eeprom_cs_off(ptr) ({ \
44 *ptr &= ~EEPROM_ECLK; \
45 *ptr &= ~EEPROM_CSEL; \
46 *ptr |= EEPROM_EPROT; \
47 *ptr |= EEPROM_ECLK; })
48
49#define BITS_IN_COMMAND 11
50/*
51 * clock in the nvram command and the register number. For the
52 * national semiconductor nv ram chip the op code is 3 bits and
53 * the address is 6/8 bits.
54 */
55static inline void eeprom_cmd(volatile unsigned int *ctrl, unsigned cmd,
56 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 *ctrl |= EEPROM_DATO;
65 else
66 *ctrl &= ~EEPROM_DATO;
67 *ctrl &= ~EEPROM_ECLK;
68 *ctrl |= EEPROM_ECLK;
69 ser_cmd <<= 1;
70 }
71 *ctrl &= ~EEPROM_DATO; /* see data sheet timing diagram */
72}
73
74unsigned short ip22_eeprom_read(volatile unsigned int *ctrl, int reg)
75{
76 unsigned short res = 0;
77 int i;
78
79 *ctrl &= ~EEPROM_EPROT;
80 eeprom_cs_on(ctrl);
81 eeprom_cmd(ctrl, EEPROM_READ, reg);
82
83 /* clock the data ouf of serial mem */
84 for (i = 0; i < 16; i++) {
85 *ctrl &= ~EEPROM_ECLK;
86 delay();
87 *ctrl |= EEPROM_ECLK;
88 delay();
89 res <<= 1;
90 if (*ctrl & EEPROM_DATI)
91 res |= 1;
92 }
93
94 eeprom_cs_off(ctrl);
95
96 return res;
97}
98
99EXPORT_SYMBOL(ip22_eeprom_read);
100
101/*
102 * Read specified register from main NVRAM
103 */
104unsigned short ip22_nvram_read(int reg)
105{
106 if (ip22_is_fullhouse())
107 /* IP22 (Indigo2 aka FullHouse) stores env variables into
108 * 93CS56 Microwire Bus EEPROM 2048 Bit (128x16) */
109 return ip22_eeprom_read(&hpc3c0->eeprom, reg);
110 else {
111 unsigned short tmp;
112 /* IP24 (Indy aka Guiness) uses DS1386 8K version */
113 reg <<= 1;
114 tmp = hpc3c0->bbram[reg++] & 0xff;
115 return (tmp << 8) | (hpc3c0->bbram[reg] & 0xff);
116 }
117}
118
119EXPORT_SYMBOL(ip22_nvram_read);
diff --git a/arch/mips/sgi-ip22/ip22-reset.c b/arch/mips/sgi-ip22/ip22-reset.c
new file mode 100644
index 000000000000..ed5c60adce63
--- /dev/null
+++ b/arch/mips/sgi-ip22/ip22-reset.c
@@ -0,0 +1,247 @@
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, 2003 by Ralf Baechle
7 */
8#include <linux/init.h>
9#include <linux/ds1286.h>
10#include <linux/module.h>
11#include <linux/interrupt.h>
12#include <linux/kernel.h>
13#include <linux/sched.h>
14#include <linux/notifier.h>
15#include <linux/timer.h>
16
17#include <asm/io.h>
18#include <asm/irq.h>
19#include <asm/system.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 paniced.
36 */
37#define POWERDOWN_FREQ (HZ / 4)
38#define PANIC_FREQ (HZ / 8)
39
40static struct timer_list power_timer, blink_timer, debounce_timer, volume_timer;
41
42#define MACHINE_PANICED 1
43#define MACHINE_SHUTTING_DOWN 2
44static int machine_state = 0;
45
46static void sgi_machine_restart(char *command) __attribute__((noreturn));
47static void sgi_machine_halt(void) __attribute__((noreturn));
48static void sgi_machine_power_off(void) __attribute__((noreturn));
49
50static void sgi_machine_restart(char *command)
51{
52 if (machine_state & MACHINE_SHUTTING_DOWN)
53 sgi_machine_power_off();
54 sgimc->cpuctrl0 |= SGIMC_CCTRL0_SYSINIT;
55 while (1);
56}
57
58static void sgi_machine_halt(void)
59{
60 if (machine_state & MACHINE_SHUTTING_DOWN)
61 sgi_machine_power_off();
62 ArcEnterInteractiveMode();
63}
64
65static void sgi_machine_power_off(void)
66{
67 unsigned int tmp;
68
69 local_irq_disable();
70
71 /* Disable watchdog */
72 tmp = hpc3c0->rtcregs[RTC_CMD] & 0xff;
73 hpc3c0->rtcregs[RTC_CMD] = tmp | RTC_WAM;
74 hpc3c0->rtcregs[RTC_WSEC] = 0;
75 hpc3c0->rtcregs[RTC_WHSEC] = 0;
76
77 while (1) {
78 sgioc->panel = ~SGIOC_PANEL_POWERON;
79 /* Good bye cruel world ... */
80
81 /* If we're still running, we probably got sent an alarm
82 interrupt. Read the flag to clear it. */
83 tmp = hpc3c0->rtcregs[RTC_HOURS_ALARM];
84 }
85}
86
87static void power_timeout(unsigned long data)
88{
89 sgi_machine_power_off();
90}
91
92static void blink_timeout(unsigned long data)
93{
94 /* XXX fix this for fullhouse */
95 sgi_ioc_reset ^= (SGIOC_RESET_LC0OFF|SGIOC_RESET_LC1OFF);
96 sgioc->reset = sgi_ioc_reset;
97
98 mod_timer(&blink_timer, jiffies+data);
99}
100
101static void debounce(unsigned long data)
102{
103 del_timer(&debounce_timer);
104 if (sgint->istat1 & SGINT_ISTAT1_PWR) {
105 /* Interrupt still being sent. */
106 debounce_timer.expires = jiffies + 5; /* 0.05s */
107 add_timer(&debounce_timer);
108
109 sgioc->panel = SGIOC_PANEL_POWERON | SGIOC_PANEL_POWERINTR |
110 SGIOC_PANEL_VOLDNINTR | SGIOC_PANEL_VOLDNHOLD |
111 SGIOC_PANEL_VOLUPINTR | SGIOC_PANEL_VOLUPHOLD;
112
113 return;
114 }
115
116 if (machine_state & MACHINE_PANICED)
117 sgimc->cpuctrl0 |= SGIMC_CCTRL0_SYSINIT;
118
119 enable_irq(SGI_PANEL_IRQ);
120}
121
122static inline void power_button(void)
123{
124 if (machine_state & MACHINE_PANICED)
125 return;
126
127 if ((machine_state & MACHINE_SHUTTING_DOWN) || kill_proc(1,SIGINT,1)) {
128 /* No init process or button pressed twice. */
129 sgi_machine_power_off();
130 }
131
132 machine_state |= MACHINE_SHUTTING_DOWN;
133 blink_timer.data = POWERDOWN_FREQ;
134 blink_timeout(POWERDOWN_FREQ);
135
136 init_timer(&power_timer);
137 power_timer.function = power_timeout;
138 power_timer.expires = jiffies + POWERDOWN_TIMEOUT * HZ;
139 add_timer(&power_timer);
140}
141
142void (*indy_volume_button)(int) = NULL;
143
144EXPORT_SYMBOL(indy_volume_button);
145
146static inline void volume_up_button(unsigned long data)
147{
148 del_timer(&volume_timer);
149
150 if (indy_volume_button)
151 indy_volume_button(1);
152
153 if (sgint->istat1 & SGINT_ISTAT1_PWR) {
154 volume_timer.expires = jiffies + 1;
155 add_timer(&volume_timer);
156 }
157}
158
159static inline void volume_down_button(unsigned long data)
160{
161 del_timer(&volume_timer);
162
163 if (indy_volume_button)
164 indy_volume_button(-1);
165
166 if (sgint->istat1 & SGINT_ISTAT1_PWR) {
167 volume_timer.expires = jiffies + 1;
168 add_timer(&volume_timer);
169 }
170}
171
172static irqreturn_t panel_int(int irq, void *dev_id, struct pt_regs *regs)
173{
174 unsigned int buttons;
175
176 buttons = sgioc->panel;
177 sgioc->panel = SGIOC_PANEL_POWERON | SGIOC_PANEL_POWERINTR;
178
179 if (sgint->istat1 & SGINT_ISTAT1_PWR) {
180 /* Wait until interrupt goes away */
181 disable_irq(SGI_PANEL_IRQ);
182 init_timer(&debounce_timer);
183 debounce_timer.function = debounce;
184 debounce_timer.expires = jiffies + 5;
185 add_timer(&debounce_timer);
186 }
187
188 /* Power button was pressed
189 * ioc.ps page 22: "The Panel Register is called Power Control by Full
190 * House. Only lowest 2 bits are used. Guiness uses upper four bits
191 * for volume control". This is not true, all bits are pulled high
192 * on fullhouse */
193 if (ip22_is_fullhouse() || !(buttons & SGIOC_PANEL_POWERINTR)) {
194 power_button();
195 return IRQ_HANDLED;
196 }
197 /* TODO: mute/unmute */
198 /* Volume up button was pressed */
199 if (!(buttons & SGIOC_PANEL_VOLUPINTR)) {
200 init_timer(&volume_timer);
201 volume_timer.function = volume_up_button;
202 volume_timer.expires = jiffies + 1;
203 add_timer(&volume_timer);
204 }
205 /* Volume down button was pressed */
206 if (!(buttons & SGIOC_PANEL_VOLDNINTR)) {
207 init_timer(&volume_timer);
208 volume_timer.function = volume_down_button;
209 volume_timer.expires = jiffies + 1;
210 add_timer(&volume_timer);
211 }
212
213 return IRQ_HANDLED;
214}
215
216static int panic_event(struct notifier_block *this, unsigned long event,
217 void *ptr)
218{
219 if (machine_state & MACHINE_PANICED)
220 return NOTIFY_DONE;
221 machine_state |= MACHINE_PANICED;
222
223 blink_timer.data = PANIC_FREQ;
224 blink_timeout(PANIC_FREQ);
225
226 return NOTIFY_DONE;
227}
228
229static struct notifier_block panic_block = {
230 .notifier_call = panic_event,
231};
232
233static int __init reboot_setup(void)
234{
235 _machine_restart = sgi_machine_restart;
236 _machine_halt = sgi_machine_halt;
237 _machine_power_off = sgi_machine_power_off;
238
239 request_irq(SGI_PANEL_IRQ, panel_int, 0, "Front Panel", NULL);
240 init_timer(&blink_timer);
241 blink_timer.function = blink_timeout;
242 notifier_chain_register(&panic_notifier_list, &panic_block);
243
244 return 0;
245}
246
247subsys_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 000000000000..0e96a5d67993
--- /dev/null
+++ b/arch/mips/sgi-ip22/ip22-setup.c
@@ -0,0 +1,144 @@
1/*
2 * ip22-setup.c: SGI specific setup, including init of the feature struct.
3 *
4 * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com)
5 * Copyright (C) 1997, 1998 Ralf Baechle (ralf@gnu.org)
6 */
7#include <linux/config.h>
8#include <linux/ds1286.h>
9#include <linux/init.h>
10#include <linux/kernel.h>
11#include <linux/kdev_t.h>
12#include <linux/types.h>
13#include <linux/module.h>
14#include <linux/console.h>
15#include <linux/sched.h>
16#include <linux/tty.h>
17
18#include <asm/addrspace.h>
19#include <asm/bcache.h>
20#include <asm/bootinfo.h>
21#include <asm/irq.h>
22#include <asm/reboot.h>
23#include <asm/time.h>
24#include <asm/gdb-stub.h>
25#include <asm/io.h>
26#include <asm/traps.h>
27#include <asm/sgialib.h>
28#include <asm/sgi/mc.h>
29#include <asm/sgi/hpc3.h>
30#include <asm/sgi/ip22.h>
31
32unsigned long sgi_gfxaddr;
33
34/*
35 * Stop-A is originally a Sun thing that isn't standard on IP22 so to avoid
36 * accidents it's disabled by default on IP22.
37 *
38 * FIXME: provide a mechanism to change the value of stop_a_enabled.
39 */
40int stop_a_enabled;
41
42void ip22_do_break(void)
43{
44 if (!stop_a_enabled)
45 return;
46
47 printk("\n");
48 ArcEnterInteractiveMode();
49}
50
51EXPORT_SYMBOL(ip22_do_break);
52
53extern void ip22_be_init(void) __init;
54extern void ip22_time_init(void) __init;
55
56static int __init ip22_setup(void)
57{
58 char *ctype;
59
60 board_be_init = ip22_be_init;
61 ip22_time_init();
62
63 /* Init the INDY HPC I/O controller. Need to call this before
64 * fucking with the memory controller because it needs to know the
65 * boardID and whether this is a Guiness or a FullHouse machine.
66 */
67 sgihpc_init();
68
69 /* Init INDY memory controller. */
70 sgimc_init();
71
72#ifdef CONFIG_BOARD_SCACHE
73 /* Now enable boardcaches, if any. */
74 indy_sc_init();
75#endif
76
77 /* Set EISA IO port base for Indigo2
78 * ioremap cannot fail */
79 set_io_port_base((unsigned long)ioremap(0x00080000,
80 0x1fffffff - 0x00080000));
81 /* ARCS console environment variable is set to "g?" for
82 * graphics console, it is set to "d" for the first serial
83 * line and "d2" for the second serial line.
84 */
85 ctype = ArcGetEnvironmentVariable("console");
86 if (ctype && *ctype == 'd') {
87 static char options[8];
88 char *baud = ArcGetEnvironmentVariable("dbaud");
89 if (baud)
90 strcpy(options, baud);
91 add_preferred_console("ttyS", *(ctype + 1) == '2' ? 1 : 0,
92 baud ? options : NULL);
93 } else if (!ctype || *ctype != 'g') {
94 /* Use ARC if we don't want serial ('d') or Newport ('g'). */
95 prom_flags |= PROM_FLAG_USE_AS_CONSOLE;
96 add_preferred_console("arc", 0, NULL);
97 }
98
99#ifdef CONFIG_KGDB
100 {
101 char *kgdb_ttyd = prom_getcmdline();
102
103 if ((kgdb_ttyd = strstr(kgdb_ttyd, "kgdb=ttyd")) != NULL) {
104 int line;
105 kgdb_ttyd += strlen("kgdb=ttyd");
106 if (*kgdb_ttyd != '1' && *kgdb_ttyd != '2')
107 printk(KERN_INFO "KGDB: Uknown serial line /dev/ttyd%c"
108 ", falling back to /dev/ttyd1\n", *kgdb_ttyd);
109 line = *kgdb_ttyd == '2' ? 0 : 1;
110 printk(KERN_INFO "KGDB: Using serial line /dev/ttyd%d for "
111 "session\n", line ? 1 : 2);
112 rs_kgdb_hook(line);
113
114 printk(KERN_INFO "KGDB: Using serial line /dev/ttyd%d for "
115 "session, please connect your debugger\n", line ? 1:2);
116
117 kgdb_enabled = 1;
118 /* Breakpoints and stuff are in sgi_irq_setup() */
119 }
120 }
121#endif
122
123#if defined(CONFIG_VT) && defined(CONFIG_SGI_NEWPORT_CONSOLE)
124 {
125 ULONG *gfxinfo;
126 ULONG * (*__vec)(void) = (void *) (long)
127 *((_PULONG *)(long)((PROMBLOCK)->pvector + 0x20));
128
129 gfxinfo = __vec();
130 sgi_gfxaddr = ((gfxinfo[1] >= 0xa0000000
131 && gfxinfo[1] <= 0xc0000000)
132 ? gfxinfo[1] - 0xa0000000 : 0);
133
134 /* newport addresses? */
135 if (sgi_gfxaddr == 0x1f0f0000 || sgi_gfxaddr == 0x1f4f0000) {
136 conswitchp = &newport_con;
137 }
138 }
139#endif
140
141 return 0;
142}
143
144early_initcall(ip22_setup);
diff --git a/arch/mips/sgi-ip22/ip22-time.c b/arch/mips/sgi-ip22/ip22-time.c
new file mode 100644
index 000000000000..173f76805ea3
--- /dev/null
+++ b/arch/mips/sgi-ip22/ip22-time.c
@@ -0,0 +1,214 @@
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 Ralf Baechle (ralf@linux-mips.org)
11 */
12#include <linux/bcd.h>
13#include <linux/ds1286.h>
14#include <linux/init.h>
15#include <linux/kernel.h>
16#include <linux/interrupt.h>
17#include <linux/kernel_stat.h>
18#include <linux/time.h>
19
20#include <asm/cpu.h>
21#include <asm/mipsregs.h>
22#include <asm/io.h>
23#include <asm/irq.h>
24#include <asm/time.h>
25#include <asm/sgialib.h>
26#include <asm/sgi/ioc.h>
27#include <asm/sgi/hpc3.h>
28#include <asm/sgi/ip22.h>
29
30/*
31 * note that mktime uses month from 1 to 12 while to_tm
32 * uses 0 to 11.
33 */
34static unsigned long indy_rtc_get_time(void)
35{
36 unsigned int yrs, mon, day, hrs, min, sec;
37 unsigned int save_control;
38
39 save_control = hpc3c0->rtcregs[RTC_CMD] & 0xff;
40 hpc3c0->rtcregs[RTC_CMD] = save_control | RTC_TE;
41
42 sec = BCD2BIN(hpc3c0->rtcregs[RTC_SECONDS] & 0xff);
43 min = BCD2BIN(hpc3c0->rtcregs[RTC_MINUTES] & 0xff);
44 hrs = BCD2BIN(hpc3c0->rtcregs[RTC_HOURS] & 0x3f);
45 day = BCD2BIN(hpc3c0->rtcregs[RTC_DATE] & 0xff);
46 mon = BCD2BIN(hpc3c0->rtcregs[RTC_MONTH] & 0x1f);
47 yrs = BCD2BIN(hpc3c0->rtcregs[RTC_YEAR] & 0xff);
48
49 hpc3c0->rtcregs[RTC_CMD] = save_control;
50
51 if (yrs < 45)
52 yrs += 30;
53 if ((yrs += 40) < 70)
54 yrs += 100;
55
56 return mktime(yrs + 1900, mon, day, hrs, min, sec);
57}
58
59static int indy_rtc_set_time(unsigned long tim)
60{
61 struct rtc_time tm;
62 unsigned int save_control;
63
64 to_tm(tim, &tm);
65
66 tm.tm_mon += 1; /* tm_mon starts at zero */
67 tm.tm_year -= 1940;
68 if (tm.tm_year >= 100)
69 tm.tm_year -= 100;
70
71 save_control = hpc3c0->rtcregs[RTC_CMD] & 0xff;
72 hpc3c0->rtcregs[RTC_CMD] = save_control | RTC_TE;
73
74 hpc3c0->rtcregs[RTC_YEAR] = BIN2BCD(tm.tm_sec);
75 hpc3c0->rtcregs[RTC_MONTH] = BIN2BCD(tm.tm_mon);
76 hpc3c0->rtcregs[RTC_DATE] = BIN2BCD(tm.tm_mday);
77 hpc3c0->rtcregs[RTC_HOURS] = BIN2BCD(tm.tm_hour);
78 hpc3c0->rtcregs[RTC_MINUTES] = BIN2BCD(tm.tm_min);
79 hpc3c0->rtcregs[RTC_SECONDS] = BIN2BCD(tm.tm_sec);
80 hpc3c0->rtcregs[RTC_HUNDREDTH_SECOND] = 0;
81
82 hpc3c0->rtcregs[RTC_CMD] = save_control;
83
84 return 0;
85}
86
87static unsigned long dosample(void)
88{
89 u32 ct0, ct1;
90 volatile u8 msb, lsb;
91
92 /* Start the counter. */
93 sgint->tcword = (SGINT_TCWORD_CNT2 | SGINT_TCWORD_CALL |
94 SGINT_TCWORD_MRGEN);
95 sgint->tcnt2 = SGINT_TCSAMP_COUNTER & 0xff;
96 sgint->tcnt2 = SGINT_TCSAMP_COUNTER >> 8;
97
98 /* Get initial counter invariant */
99 ct0 = read_c0_count();
100
101 /* Latch and spin until top byte of counter2 is zero */
102 do {
103 sgint->tcword = SGINT_TCWORD_CNT2 | SGINT_TCWORD_CLAT;
104 lsb = sgint->tcnt2;
105 msb = sgint->tcnt2;
106 ct1 = read_c0_count();
107 } while (msb);
108
109 /* Stop the counter. */
110 sgint->tcword = (SGINT_TCWORD_CNT2 | SGINT_TCWORD_CALL |
111 SGINT_TCWORD_MSWST);
112 /*
113 * Return the difference, this is how far the r4k counter increments
114 * for every 1/HZ seconds. We round off the nearest 1 MHz of master
115 * clock (= 1000000 / HZ / 2).
116 */
117 /*return (ct1 - ct0 + (500000/HZ/2)) / (500000/HZ) * (500000/HZ);*/
118 return (ct1 - ct0) / (500000/HZ) * (500000/HZ);
119}
120
121/*
122 * Here we need to calibrate the cycle counter to at least be close.
123 */
124static __init void indy_time_init(void)
125{
126 unsigned long r4k_ticks[3];
127 unsigned long r4k_tick;
128
129 /*
130 * Figure out the r4k offset, the algorithm is very simple and works in
131 * _all_ cases as long as the 8254 counter register itself works ok (as
132 * an interrupt driving timer it does not because of bug, this is why
133 * we are using the onchip r4k counter/compare register to serve this
134 * purpose, but for r4k_offset calculation it will work ok for us).
135 * There are other very complicated ways of performing this calculation
136 * but this one works just fine so I am not going to futz around. ;-)
137 */
138 printk(KERN_INFO "Calibrating system timer... ");
139 dosample(); /* Prime cache. */
140 dosample(); /* Prime cache. */
141 /* Zero is NOT an option. */
142 do {
143 r4k_ticks[0] = dosample();
144 } while (!r4k_ticks[0]);
145 do {
146 r4k_ticks[1] = dosample();
147 } while (!r4k_ticks[1]);
148
149 if (r4k_ticks[0] != r4k_ticks[1]) {
150 printk("warning: timer counts differ, retrying... ");
151 r4k_ticks[2] = dosample();
152 if (r4k_ticks[2] == r4k_ticks[0]
153 || r4k_ticks[2] == r4k_ticks[1])
154 r4k_tick = r4k_ticks[2];
155 else {
156 printk("disagreement, using average... ");
157 r4k_tick = (r4k_ticks[0] + r4k_ticks[1]
158 + r4k_ticks[2]) / 3;
159 }
160 } else
161 r4k_tick = r4k_ticks[0];
162
163 printk("%d [%d.%04d MHz CPU]\n", (int) r4k_tick,
164 (int) (r4k_tick / (500000 / HZ)),
165 (int) (r4k_tick % (500000 / HZ)));
166
167 mips_hpt_frequency = r4k_tick * HZ;
168}
169
170/* Generic SGI handler for (spurious) 8254 interrupts */
171void indy_8254timer_irq(struct pt_regs *regs)
172{
173 int irq = SGI_8254_0_IRQ;
174 ULONG cnt;
175 char c;
176
177 irq_enter();
178 kstat_this_cpu.irqs[irq]++;
179 printk(KERN_ALERT "Oops, got 8254 interrupt.\n");
180 ArcRead(0, &c, 1, &cnt);
181 ArcEnterInteractiveMode();
182 irq_exit();
183}
184
185void indy_r4k_timer_interrupt(struct pt_regs *regs)
186{
187 int irq = SGI_TIMER_IRQ;
188
189 irq_enter();
190 kstat_this_cpu.irqs[irq]++;
191 timer_interrupt(irq, NULL, regs);
192 irq_exit();
193}
194
195extern int setup_irq(unsigned int irq, struct irqaction *irqaction);
196
197static void indy_timer_setup(struct irqaction *irq)
198{
199 /* over-write the handler, we use our own way */
200 irq->handler = no_action;
201
202 /* setup irqaction */
203 setup_irq(SGI_TIMER_IRQ, irq);
204}
205
206void __init ip22_time_init(void)
207{
208 /* setup hookup functions */
209 rtc_get_time = indy_rtc_get_time;
210 rtc_set_time = indy_rtc_set_time;
211
212 board_time_init = indy_time_init;
213 board_timer_setup = indy_timer_setup;
214}