Commit 2395133b authored by Ralf Bächle's avatar Ralf Bächle Committed by Linus Torvalds

[PATCH] SGI IP22 bits

An update for the Indy aka IP22 support.  Consolidates the 32-bit and
64-bit copies of the support code into one directory, so in total this
patch deletes quite a bit of code.
parent f19e2d9e
This diff is collapsed.
This diff is collapsed.
...@@ -3,7 +3,10 @@ ...@@ -3,7 +3,10 @@
# under Linux. # under Linux.
# #
obj-y += indy_mc.o indy_sc.o indy_hpc.o indy_int.o indy_rtc.o system.o \ obj-y += ip22-mc.o ip22-hpc.o ip22-int.o ip22-irq.o ip22-berr.o \
indyIRQ.o reset.o setup.o time.o ip22-time.o ip22-rtc.o ip22-nvram.o ip22-reset.o \
ip22-setup.o ip22-ksyms.o
obj-$(CONFIG_EISA) += ip22-eisa.o
EXTRA_AFLAGS := $(CFLAGS) EXTRA_AFLAGS := $(CFLAGS)
/*
* ip22-berr.c: Bus error handling.
*
* Copyright (C) 2002 Ladislav Michl
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <asm/addrspace.h>
#include <asm/system.h>
#include <asm/traps.h>
#include <asm/branch.h>
#include <asm/sgi/mc.h>
#include <asm/sgi/hpc3.h>
static unsigned int cpu_err_stat; /* Status reg for CPU */
static unsigned int gio_err_stat; /* Status reg for GIO */
static unsigned int cpu_err_addr; /* Error address reg for CPU */
static unsigned int gio_err_addr; /* Error address reg for GIO */
static void save_and_clear_buserr(void)
{
/* save memory controler's error status registers */
cpu_err_addr = sgimc->cerr;
cpu_err_stat = sgimc->cstat;
gio_err_addr = sgimc->gerr;
gio_err_stat = sgimc->gstat;
sgimc->cstat = sgimc->gstat = 0;
}
#define GIO_ERRMASK 0xff00
#define CPU_ERRMASK 0x3f00
static void print_buserr(void)
{
if (cpu_err_stat & CPU_ERRMASK)
printk(KERN_ALERT "CPU error 0x%x<%s%s%s%s%s%s> @ 0x%08x\n",
cpu_err_stat,
cpu_err_stat & SGIMC_CSTAT_RD ? "RD " : "",
cpu_err_stat & SGIMC_CSTAT_PAR ? "PAR " : "",
cpu_err_stat & SGIMC_CSTAT_ADDR ? "ADDR " : "",
cpu_err_stat & SGIMC_CSTAT_SYSAD_PAR ? "SYSAD " : "",
cpu_err_stat & SGIMC_CSTAT_SYSCMD_PAR ? "SYSCMD " : "",
cpu_err_stat & SGIMC_CSTAT_BAD_DATA ? "BAD_DATA " : "",
cpu_err_addr);
if (gio_err_stat & GIO_ERRMASK)
printk(KERN_ALERT "GIO error 0x%x:<%s%s%s%s%s%s%s%s> @ 0x08%x\n",
gio_err_stat,
gio_err_stat & SGIMC_GSTAT_RD ? "RD " : "",
gio_err_stat & SGIMC_GSTAT_WR ? "WR " : "",
gio_err_stat & SGIMC_GSTAT_TIME ? "TIME " : "",
gio_err_stat & SGIMC_GSTAT_PROM ? "PROM " : "",
gio_err_stat & SGIMC_GSTAT_ADDR ? "ADDR " : "",
gio_err_stat & SGIMC_GSTAT_BC ? "BC " : "",
gio_err_stat & SGIMC_GSTAT_PIO_RD ? "PIO_RD " : "",
gio_err_stat & SGIMC_GSTAT_PIO_WR ? "PIO_WR " : "",
gio_err_addr);
}
/*
* MC sends an interrupt whenever bus or parity errors occur. In addition,
* if the error happened during a CPU read, it also asserts the bus error
* pin on the R4K. Code in bus error handler save the MC bus error registers
* and then clear the interrupt when this happens.
*/
void ip22_be_interrupt(int irq, struct pt_regs *regs)
{
save_and_clear_buserr();
print_buserr();
panic("Bus error, epc == %08lx, ra == %08lx",
regs->cp0_epc, regs->regs[31]);
}
int ip22_be_handler(struct pt_regs *regs, int is_fixup)
{
save_and_clear_buserr();
if (is_fixup)
return MIPS_BE_FIXUP;
print_buserr();
return MIPS_BE_FATAL;
}
void __init ip22_be_init(void)
{
board_be_handler = ip22_be_handler;
}
/*
* Basic EISA bus support for the SGI Indigo-2.
*
* (C) 2002 Pascal Dameme <netinet@freesurf.fr>
* and Marc Zyngier <mzyngier@freesurf.fr>
*
* This code is released under both the GPL version 2 and BSD
* licenses. Either license may be used.
*
* This code offers a very basic support for this EISA bus present in
* the SGI Indigo-2. It currently only supports PIO (forget about DMA
* for the time being). This is enough for a low-end ethernet card,
* but forget about your favorite SCSI card...
*
* TODO :
* - Fix bugs...
* - Add ISA support
* - Add DMA (yeah, right...).
* - Fix more bugs.
*/
#include <linux/config.h>
#include <linux/types.h>
#include <linux/init.h>
#include <linux/irq.h>
#include <linux/kernel_stat.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <asm/irq.h>
#include <asm/mipsregs.h>
#include <asm/addrspace.h>
#include <asm/processor.h>
#include <asm/sgi/ioc.h>
#include <asm/sgi/mc.h>
#include <asm/sgi/ip22.h>
#define EISA_MAX_SLOTS 4
#define EISA_MAX_IRQ 16
#define EISA_TO_PHYS(x) (0x00080000 | (x))
#define EISA_TO_KSEG1(x) ((void *) KSEG1ADDR(EISA_TO_PHYS((x))))
#define EIU_MODE_REG 0x0009ffc0
#define EIU_STAT_REG 0x0009ffc4
#define EIU_PREMPT_REG 0x0009ffc8
#define EIU_QUIET_REG 0x0009ffcc
#define EIU_INTRPT_ACK 0x00090004
#define EISA_DMA1_STATUS 8
#define EISA_INT1_CTRL 0x20
#define EISA_INT1_MASK 0x21
#define EISA_INT2_CTRL 0xA0
#define EISA_INT2_MASK 0xA1
#define EISA_DMA2_STATUS 0xD0
#define EISA_DMA2_WRITE_SINGLE 0xD4
#define EISA_EXT_NMI_RESET_CTRL 0x461
#define EISA_INT1_EDGE_LEVEL 0x4D0
#define EISA_INT2_EDGE_LEVEL 0x4D1
#define EISA_VENDOR_ID_OFFSET 0xC80
#define EIU_WRITE_32(x,y) { *((u32 *) KSEG1ADDR(x)) = (u32) (y); mb(); }
#define EIU_READ_8(x) *((u8 *) KSEG1ADDR(x))
#define EISA_WRITE_8(x,y) { *((u8 *) EISA_TO_KSEG1(x)) = (u8) (y); mb(); }
#define EISA_READ_8(x) *((u8 *) EISA_TO_KSEG1(x))
static char *decode_eisa_sig(u8 * sig)
{
static char sig_str[8];
u16 rev;
if (sig[0] & 0x80)
return NULL;
sig_str[0] = ((sig[0] >> 2) & 0x1f) + ('A' - 1);
sig_str[1] = (((sig[0] & 3) << 3) | (sig[1] >> 5)) + ('A' - 1);
sig_str[2] = (sig[1] & 0x1f) + ('A' - 1);
rev = (sig[2] << 8) | sig[3];
sprintf(sig_str + 3, "%04X", rev);
return sig_str;
}
static void ip22_eisa_intr(int irq, void *dev_id, struct pt_regs *regs)
{
u8 eisa_irq;
u8 dma1, dma2;
eisa_irq = EIU_READ_8(EIU_INTRPT_ACK);
dma1 = EISA_READ_8(EISA_DMA1_STATUS);
dma2 = EISA_READ_8(EISA_DMA2_STATUS);
if (eisa_irq >= EISA_MAX_IRQ) {
/* Oops, Bad Stuff Happened... */
printk(KERN_ERR "eisa_irq %d out of bound\n", eisa_irq);
EISA_WRITE_8(EISA_INT2_CTRL, 0x20);
EISA_WRITE_8(EISA_INT1_CTRL, 0x20);
} else
do_IRQ(eisa_irq, regs);
}
static void enable_eisa1_irq(unsigned int irq)
{
unsigned long flags;
u8 mask;
local_irq_save(flags);
mask = EISA_READ_8(EISA_INT1_MASK);
mask &= ~((u8) (1 << irq));
EISA_WRITE_8(EISA_INT1_MASK, mask);
local_irq_restore(flags);
}
static unsigned int startup_eisa1_irq(unsigned int irq)
{
u8 edge;
/* Only use edge interrupts for EISA */
edge = EISA_READ_8(EISA_INT1_EDGE_LEVEL);
edge &= ~((u8) (1 << irq));
EISA_WRITE_8(EISA_INT1_EDGE_LEVEL, edge);
enable_eisa1_irq(irq);
return 0;
}
static void disable_eisa1_irq(unsigned int irq)
{
u8 mask;
mask = EISA_READ_8(EISA_INT1_MASK);
mask |= ((u8) (1 << irq));
EISA_WRITE_8(EISA_INT1_MASK, mask);
}
#define shutdown_eisa1_irq disable_eisa1_irq
static void mask_and_ack_eisa1_irq(unsigned int irq)
{
disable_eisa1_irq(irq);
EISA_WRITE_8(EISA_INT1_CTRL, 0x20);
}
static void end_eisa1_irq(unsigned int irq)
{
if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS)))
enable_eisa1_irq(irq);
}
static struct hw_interrupt_type ip22_eisa1_irq_type = {
.typename = "IP22 EISA",
.startup = startup_eisa1_irq,
.shutdown = shutdown_eisa1_irq,
.enable = enable_eisa1_irq,
.disable = disable_eisa1_irq,
.ack = mask_and_ack_eisa1_irq,
.end = end_eisa1_irq,
};
static void enable_eisa2_irq(unsigned int irq)
{
unsigned long flags;
u8 mask;
local_irq_save(flags);
mask = EISA_READ_8(EISA_INT2_MASK);
mask &= ~((u8) (1 << (irq - 8)));
EISA_WRITE_8(EISA_INT2_MASK, mask);
local_irq_restore(flags);
}
static unsigned int startup_eisa2_irq(unsigned int irq)
{
u8 edge;
/* Only use edge interrupts for EISA */
edge = EISA_READ_8(EISA_INT2_EDGE_LEVEL);
edge &= ~((u8) (1 << (irq - 8)));
EISA_WRITE_8(EISA_INT2_EDGE_LEVEL, edge);
enable_eisa2_irq(irq);
return 0;
}
static void disable_eisa2_irq(unsigned int irq)
{
u8 mask;
mask = EISA_READ_8(EISA_INT2_MASK);
mask |= ((u8) (1 << (irq - 8)));
EISA_WRITE_8(EISA_INT2_MASK, mask);
}
#define shutdown_eisa2_irq disable_eisa2_irq
static void mask_and_ack_eisa2_irq(unsigned int irq)
{
disable_eisa2_irq(irq);
EISA_WRITE_8(EISA_INT2_CTRL, 0x20);
EISA_WRITE_8(EISA_INT1_CTRL, 0x20);
}
static void end_eisa2_irq(unsigned int irq)
{
if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS)))
enable_eisa2_irq(irq);
}
static struct hw_interrupt_type ip22_eisa2_irq_type = {
.typename = "IP22 EISA",
.startup = startup_eisa2_irq,
.shutdown = shutdown_eisa2_irq,
.enable = enable_eisa2_irq,
.disable = disable_eisa2_irq,
.ack = mask_and_ack_eisa2_irq,
.end = end_eisa2_irq,
};
static struct irqaction eisa_action = {
.handler = ip22_eisa_intr,
.name = "EISA",
};
static struct irqaction cascade_action = {
.handler = no_action,
.name = "EISA cascade",
};
int __init ip22_eisa_init(void)
{
int i, c;
char *str;
u8 *slot_addr;
if (!(sgimc->systemid & SGIMC_SYSID_EPRESENT)) {
printk(KERN_INFO "EISA: bus not present.\n");
return 1;
}
printk(KERN_INFO "EISA: Probing bus...\n");
for (c = 0, i = 1; i <= EISA_MAX_SLOTS; i++) {
slot_addr =
(u8 *) EISA_TO_KSEG1((0x1000 * i) +
EISA_VENDOR_ID_OFFSET);
if ((str = decode_eisa_sig(slot_addr))) {
printk(KERN_INFO "EISA: slot %d : %s detected.\n",
i, str);
c++;
}
}
printk(KERN_INFO "EISA: Detected %d card%s.\n", c, c < 2 ? "" : "s");
#ifdef CONFIG_ISA
printk(KERN_INFO "ISA support compiled in.\n");
#endif
/* Warning : BlackMagicAhead(tm).
Please wave your favorite dead chicken over the busses */
/* First say hello to the EIU */
EIU_WRITE_32(EIU_PREMPT_REG, 0x0000FFFF);
EIU_WRITE_32(EIU_QUIET_REG, 1);
EIU_WRITE_32(EIU_MODE_REG, 0x40f3c07F);
/* Now be nice to the EISA chipset */
EISA_WRITE_8(EISA_EXT_NMI_RESET_CTRL, 1);
for (i = 0; i < 10000; i++); /* Wait long enough for the dust to settle */
EISA_WRITE_8(EISA_EXT_NMI_RESET_CTRL, 0);
EISA_WRITE_8(EISA_INT1_CTRL, 0x11);
EISA_WRITE_8(EISA_INT2_CTRL, 0x11);
EISA_WRITE_8(EISA_INT1_MASK, 0);
EISA_WRITE_8(EISA_INT2_MASK, 8);
EISA_WRITE_8(EISA_INT1_MASK, 4);
EISA_WRITE_8(EISA_INT2_MASK, 2);
EISA_WRITE_8(EISA_INT1_MASK, 1);
EISA_WRITE_8(EISA_INT2_MASK, 1);
EISA_WRITE_8(EISA_INT1_MASK, 0xfb);
EISA_WRITE_8(EISA_INT2_MASK, 0xff);
EISA_WRITE_8(EISA_DMA2_WRITE_SINGLE, 0);
for (i = SGINT_EISA; i < (SGINT_EISA + EISA_MAX_IRQ); i++) {
irq_desc[i].status = IRQ_DISABLED;
irq_desc[i].action = 0;
irq_desc[i].depth = 1;
if (i < (SGINT_EISA + 8))
irq_desc[i].handler = &ip22_eisa1_irq_type;
else
irq_desc[i].handler = &ip22_eisa2_irq_type;
}
/* Cannot use request_irq because of kmalloc not being ready at such
* an early stage. Yes, I've been bitten... */
setup_irq(SGI_EISA_IRQ, &eisa_action);
setup_irq(SGINT_EISA + 2, &cascade_action);
EISA_bus = 1;
return 0;
}
/*
* ip22-hpc.c: Routines for generic manipulation of the HPC controllers.
*
* Copyright (C) 1996 David S. Miller (dm@engr.sgi.com)
* Copyright (C) 1998 Ralf Baechle
*/
#include <linux/init.h>
#include <linux/types.h>
#include <asm/addrspace.h>
#include <asm/sgi/hpc3.h>
#include <asm/sgi/ioc.h>
#include <asm/sgi/ip22.h>
struct hpc3_regs *hpc3c0, *hpc3c1;
struct sgioc_regs *sgioc;
/* We need software copies of these because they are write only. */
u8 sgi_ioc_reset, sgi_ioc_write;
extern char *system_type;
void __init sgihpc_init(void)
{
hpc3c0 = (struct hpc3_regs *)(KSEG1 + HPC3_CHIP0_BASE);
hpc3c1 = (struct hpc3_regs *)(KSEG1 + HPC3_CHIP1_BASE);
/* IOC lives in PBUS PIO channel 6 */
sgioc = (struct sgioc_regs *)hpc3c0->pbus_extregs[6];
hpc3c0->pbus_piocfg[6][0] |= HPC3_PIOCFG_DS16;
if (ip22_is_fullhouse()) {
/* Full House comes with INT2 which lives in PBUS PIO
* channel 4 */
sgint = (struct sgint_regs *)hpc3c0->pbus_extregs[4];
system_type = "SGI Indigo2";
} else {
/* Guiness comes with INT3 which is part of IOC */
sgint = &sgioc->int3;
system_type = "SGI Indy";
}
sgi_ioc_reset = (SGIOC_RESET_PPORT | SGIOC_RESET_KBDMOUSE |
SGIOC_RESET_EISA | SGIOC_RESET_ISDN |
SGIOC_RESET_LC0OFF);
sgi_ioc_write = (SGIOC_WRITE_EASEL | SGIOC_WRITE_NTHRESH |
SGIOC_WRITE_TPSPEED | SGIOC_WRITE_EPSEL |
SGIOC_WRITE_U0AMODE | SGIOC_WRITE_U1AMODE);
sgioc->reset = sgi_ioc_reset;
sgioc->write = sgi_ioc_write;
}
/* /*
* This file is subject to the terms and conditions of the GNU General Public * ip22-irq.S: Interrupt exception dispatch code for FullHouse and
* License. See the file "COPYING" in the main directory of this archive * Guiness.
* for more details.
*
* indyIRQ.S: Interrupt exception dispatch code for FullHouse and
* Guiness.
* *
* Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com)
*/ */
#include <asm/asm.h> #include <asm/asm.h>
#include <asm/mipsregs.h> #include <asm/mipsregs.h>
#include <asm/regdef.h> #include <asm/regdef.h>
...@@ -68,10 +65,8 @@ ...@@ -68,10 +65,8 @@
andi a0, s0, CAUSEF_IP2 # delay slot, check local level zero andi a0, s0, CAUSEF_IP2 # delay slot, check local level zero
/* Wheee, a timer interrupt. */ /* Wheee, a timer interrupt. */
move a0, sp jal indy_r4k_timer_interrupt
jal indy_timer_interrupt move a0, sp # delay slot
nop # delay slot
j ret_from_irq j ret_from_irq
nop # delay slot nop # delay slot
...@@ -91,38 +86,33 @@ ...@@ -91,38 +86,33 @@
andi a0, s0, CAUSEF_IP6 # delay slot, check bus error andi a0, s0, CAUSEF_IP6 # delay slot, check bus error
/* Wheee, local level one interrupt. */ /* Wheee, local level one interrupt. */
move a0, sp
jal indy_local1_irqdispatch jal indy_local1_irqdispatch
nop move a0, sp # delay slot
j ret_from_irq j ret_from_irq
nop nop # delay slot
1: 1:
beq a0, zero, 1f beq a0, zero, 1f
nop andi a0, s0, (CAUSEF_IP4 | CAUSEF_IP5) # delay slot
/* Wheee, an asynchronous bus error... */ /* Wheee, an asynchronous bus error... */
move a0, sp
jal indy_buserror_irq jal indy_buserror_irq
nop move a0, sp # delay slot
j ret_from_irq j ret_from_irq
nop nop # delay slot
1: 1:
/* Here by mistake? This is possible, what can happen /* Here by mistake? It is possible, that by the time we take
* is that by the time we take the exception the IRQ * the exception the IRQ pin goes low, so just leave if this
* pin goes low, so just leave if this is the case. * is the case.
*/ */
andi a0, s0, (CAUSEF_IP4 | CAUSEF_IP5)
beq a0, zero, 1f beq a0, zero, 1f
nop # delay slot
/* Must be one of the 8254 timers... */ /* Must be one of the 8254 timers... */
move a0, sp
jal indy_8254timer_irq jal indy_8254timer_irq
nop move a0, sp # delay slot
1: 1:
j ret_from_irq j ret_from_irq
nop nop # delay slot
END(indyIRQ) END(indyIRQ)
/*
* ip22-ksyms.c: IP22 specific exports
*/
#include <linux/module.h>
#include <asm/sgi/mc.h>
#include <asm/sgi/hpc3.h>
#include <asm/sgi/ioc.h>
#include <asm/sgi/ip22.h>
EXPORT_SYMBOL(sgimc);
EXPORT_SYMBOL(hpc3c0);
EXPORT_SYMBOL(hpc3c1);
EXPORT_SYMBOL(sgioc);
extern void (*indy_volume_button)(int);
EXPORT_SYMBOL(indy_volume_button);
EXPORT_SYMBOL(ip22_eeprom_read);
EXPORT_SYMBOL(ip22_nvram_read);
/*
* ip22-mc.c: Routines for manipulating SGI Memory Controller.
*
* Copyright (C) 1996 David S. Miller (dm@engr.sgi.com)
* Copyright (C) 1999 Andrew R. Baker (andrewb@uab.edu) - Indigo2 changes
* Copyright (C) 2003 Ladislav Michl (ladis@linux-mips.org)
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <asm/addrspace.h>
#include <asm/bootinfo.h>
#include <asm/ptrace.h>
#include <asm/sgialib.h>
#include <asm/sgi/mc.h>
#include <asm/sgi/hpc3.h>
#include <asm/sgi/ip22.h>
struct sgimc_regs *sgimc;
static inline unsigned long get_bank_addr(unsigned int memconfig)
{
return ((memconfig & SGIMC_MCONFIG_BASEADDR) <<
((sgimc->systemid & SGIMC_SYSID_MASKREV) >= 5 ? 24 : 22));
}
static inline unsigned long get_bank_size(unsigned int memconfig)
{
return ((memconfig & SGIMC_MCONFIG_RMASK) + 0x0100) <<
((sgimc->systemid & SGIMC_SYSID_MASKREV) >= 5 ? 16 : 14);
}
static inline unsigned int get_bank_config(int bank)
{
unsigned int res = bank > 1 ? sgimc->mconfig1 : sgimc->mconfig0;
return bank % 2 ? res & 0xffff : res >> 16;
}
struct mem {
unsigned long addr;
unsigned long size;
};
/*
* Detect installed memory, do some sanity checks and notify kernel about it
*/
static void probe_memory(void)
{
int i, j, found, cnt = 0;
struct mem bank[4];
struct mem space[2] = {{SGIMC_SEG0_BADDR, 0}, {SGIMC_SEG1_BADDR, 0}};
printk(KERN_INFO "MC: Probing memory configuration:\n");
for (i = 0; i < ARRAY_SIZE(bank); i++) {
unsigned int tmp = get_bank_config(i);
if (!(tmp & SGIMC_MCONFIG_BVALID))
continue;
bank[cnt].size = get_bank_size(tmp);
bank[cnt].addr = get_bank_addr(tmp);
printk(KERN_INFO " bank%d: %3ldM @ %08lx\n",
i, bank[cnt].size / 1024 / 1024, bank[cnt].addr);
cnt++;
}
/* And you thought bubble sort is dead algorithm... */
do {
unsigned long addr, size;
found = 0;
for (i = 1; i < cnt; i++)
if (bank[i-1].addr > bank[i].addr) {
addr = bank[i].addr;
size = bank[i].size;
bank[i].addr = bank[i-1].addr;
bank[i].size = bank[i-1].size;
bank[i-1].addr = addr;
bank[i-1].size = size;
found = 1;
}
} while (found);
/* Figure out how are memory banks mapped into spaces */
for (i = 0; i < cnt; i++) {
found = 0;
for (j = 0; j < ARRAY_SIZE(space) && !found; j++)
if (space[j].addr + space[j].size == bank[i].addr) {
space[j].size += bank[i].size;
found = 1;
}
/* There is either hole or overlapping memory */
if (!found)
printk(KERN_CRIT "MC: Memory configuration mismatch "
"(%08lx), expect Bus Error soon\n",
bank[i].addr);
}
for (i = 0; i < ARRAY_SIZE(space); i++)
if (space[i].size)
add_memory_region(space[i].addr, space[i].size,
BOOT_MEM_RAM);
}
void __init sgimc_init(void)
{
u32 tmp;
sgimc = (struct sgimc_regs *)(KSEG1 + SGIMC_BASE);
printk(KERN_INFO "MC: SGI memory controller Revision %d\n",
(int) sgimc->systemid & SGIMC_SYSID_MASKREV);
/* Place the MC into a known state. This must be done before
* interrupts are first enabled etc.
*/
/* Step 0: Make sure we turn off the watchdog in case it's
* still running (which might be the case after a
* soft reboot).
*/
tmp = sgimc->cpuctrl0;
tmp &= ~SGIMC_CCTRL0_WDOG;
sgimc->cpuctrl0 = tmp;
/* Step 1: The CPU/GIO error status registers will not latch
* up a new error status until the register has been
* cleared by the cpu. These status registers are
* cleared by writing any value to them.
*/
sgimc->cstat = sgimc->gstat = 0;
/* Step 2: Enable all parity checking in cpu control register
* zero.
*/
tmp = sgimc->cpuctrl0;
tmp |= (SGIMC_CCTRL0_EPERRGIO | SGIMC_CCTRL0_EPERRMEM |
SGIMC_CCTRL0_R4KNOCHKPARR);
sgimc->cpuctrl0 = tmp;
/* Step 3: Setup the MC write buffer depth, this is controlled
* in cpu control register 1 in the lower 4 bits.
*/
tmp = sgimc->cpuctrl1;
tmp &= ~0xf;
tmp |= 0xd;
sgimc->cpuctrl1 = tmp;
/* Step 4: Initialize the RPSS divider register to run as fast
* as it can correctly operate. The register is laid
* out as follows:
*
* ----------------------------------------
* | RESERVED | INCREMENT | DIVIDER |
* ----------------------------------------
* 31 16 15 8 7 0
*
* DIVIDER determines how often a 'tick' happens,
* INCREMENT determines by how the RPSS increment
* registers value increases at each 'tick'. Thus,
* for IP22 we get INCREMENT=1, DIVIDER=1 == 0x101
*/
sgimc->divider = 0x101;
/* Step 5: Initialize GIO64 arbitrator configuration register.
*
* NOTE: HPC init code in sgihpc_init() must run before us because
* we need to know Guiness vs. FullHouse and the board
* revision on this machine. You have been warned.
*/
/* First the basic invariants across all GIO64 implementations. */
tmp = SGIMC_GIOPAR_HPC64; /* All 1st HPC's interface at 64bits */
tmp |= SGIMC_GIOPAR_ONEBUS; /* Only one physical GIO bus exists */
if (ip22_is_fullhouse()) {
/* Fullhouse specific settings. */
if (SGIOC_SYSID_BOARDREV(sgioc->sysid) < 2) {
tmp |= SGIMC_GIOPAR_HPC264; /* 2nd HPC at 64bits */
tmp |= SGIMC_GIOPAR_PLINEEXP0; /* exp0 pipelines */
tmp |= SGIMC_GIOPAR_MASTEREXP1; /* exp1 masters */
tmp |= SGIMC_GIOPAR_RTIMEEXP0; /* exp0 is realtime */
} else {
tmp |= SGIMC_GIOPAR_HPC264; /* 2nd HPC 64bits */
tmp |= SGIMC_GIOPAR_PLINEEXP0; /* exp[01] pipelined */
tmp |= SGIMC_GIOPAR_PLINEEXP1;
tmp |= SGIMC_GIOPAR_MASTEREISA; /* EISA masters */
tmp |= SGIMC_GIOPAR_GFX64; /* GFX at 64 bits */
}
} else {
/* Guiness specific settings. */
tmp |= SGIMC_GIOPAR_EISA64; /* MC talks to EISA at 64bits */
tmp |= SGIMC_GIOPAR_MASTEREISA; /* EISA bus can act as master */
}
sgimc->giopar = tmp; /* poof */
probe_memory();
}
void __init prom_meminit(void) {}
void __init prom_free_prom_memory (void) {}
/*
* ip22-nvram.c: NVRAM and serial EEPROM handling.
*
* Copyright (C) 2003 Ladislav Michl (ladis@linux-mips.org)
*/
#include <asm/sgi/hpc3.h>
#include <asm/sgi/ip22.h>
/* Control opcode for serial eeprom */
#define EEPROM_READ 0xc000 /* serial memory read */
#define EEPROM_WEN 0x9800 /* write enable before prog modes */
#define EEPROM_WRITE 0xa000 /* serial memory write */
#define EEPROM_WRALL 0x8800 /* write all registers */
#define EEPROM_WDS 0x8000 /* disable all programming */
#define EEPROM_PRREAD 0xc000 /* read protect register */
#define EEPROM_PREN 0x9800 /* enable protect register mode */
#define EEPROM_PRCLEAR 0xffff /* clear protect register */
#define EEPROM_PRWRITE 0xa000 /* write protect register */
#define EEPROM_PRDS 0x8000 /* disable protect register, forever */
#define EEPROM_EPROT 0x01 /* Protect register enable */
#define EEPROM_CSEL 0x02 /* Chip select */
#define EEPROM_ECLK 0x04 /* EEPROM clock */
#define EEPROM_DATO 0x08 /* Data out */
#define EEPROM_DATI 0x10 /* Data in */
/* We need to use this functions early... */
#define delay() ({ \
int x; \
for (x=0; x<100000; x++) __asm__ __volatile__(""); })
#define eeprom_cs_on(ptr) ({ \
*ptr &= ~EEPROM_DATO; \
*ptr &= ~EEPROM_ECLK; \
*ptr &= ~EEPROM_EPROT; \
delay(); \
*ptr |= EEPROM_CSEL; \
*ptr |= EEPROM_ECLK; })
#define eeprom_cs_off(ptr) ({ \
*ptr &= ~EEPROM_ECLK; \
*ptr &= ~EEPROM_CSEL; \
*ptr |= EEPROM_EPROT; \
*ptr |= EEPROM_ECLK; })
#define BITS_IN_COMMAND 11
/*
* clock in the nvram command and the register number. For the
* national semiconductor nv ram chip the op code is 3 bits and
* the address is 6/8 bits.
*/
static inline void eeprom_cmd(volatile unsigned int *ctrl, unsigned cmd,
unsigned reg)
{
unsigned short ser_cmd;
int i;
ser_cmd = cmd | (reg << (16 - BITS_IN_COMMAND));
for (i = 0; i < BITS_IN_COMMAND; i++) {
if (ser_cmd & (1<<15)) /* if high order bit set */
*ctrl |= EEPROM_DATO;
else
*ctrl &= ~EEPROM_DATO;
*ctrl &= ~EEPROM_ECLK;
*ctrl |= EEPROM_ECLK;
ser_cmd <<= 1;
}
*ctrl &= ~EEPROM_DATO; /* see data sheet timing diagram */
}
unsigned short ip22_eeprom_read(volatile unsigned int *ctrl, int reg)
{
unsigned short res = 0;
int i;
*ctrl &= ~EEPROM_EPROT;
eeprom_cs_on(ctrl);
eeprom_cmd(ctrl, EEPROM_READ, reg);
/* clock the data ouf of serial mem */
for (i = 0; i < 16; i++) {
*ctrl &= ~EEPROM_ECLK;
delay();
*ctrl |= EEPROM_ECLK;
delay();
res <<= 1;
if (*ctrl & EEPROM_DATI)
res |= 1;
}
eeprom_cs_off(ctrl);
return res;
}
/*
* Read specified register from main NVRAM
*/
unsigned short ip22_nvram_read(int reg)
{
if (ip22_is_fullhouse())
/* IP22 (Indigo2 aka FullHouse) stores env variables into
* 93CS56 Microwire Bus EEPROM 2048 Bit (128x16) */
return ip22_eeprom_read(&hpc3c0->eeprom, reg);
else {
unsigned short tmp;
/* IP24 (Indy aka Guiness) uses DS1386 8K version */
reg <<= 1;
tmp = hpc3c0->bbram[reg++] & 0xff;
return (tmp << 8) | (hpc3c0->bbram[reg] & 0xff);
}
}
...@@ -3,23 +3,28 @@ ...@@ -3,23 +3,28 @@
* License. See the file "COPYING" in the main directory of this archive * License. See the file "COPYING" in the main directory of this archive
* for more details. * for more details.
* *
* Copyright (C) 1997, 1998, 2001 by Ralf Baechle * Copyright (C) 1997, 1998, 2001, 2003 by Ralf Baechle
*/ */
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/notifier.h> #include <linux/notifier.h>
#include <linux/timer.h> #include <linux/timer.h>
#include <asm/io.h> #include <asm/io.h>
#include <asm/irq.h> #include <asm/irq.h>
#include <asm/system.h> #include <asm/system.h>
#include <asm/reboot.h> #include <asm/reboot.h>
#include <asm/ds1286.h>
#include <asm/sgialib.h> #include <asm/sgialib.h>
#include <asm/sgi/sgihpc.h> #include <asm/sgi/ioc.h>
#include <asm/sgi/sgint23.h> #include <asm/sgi/hpc3.h>
#include <asm/sgi/ip22.h>
/* /*
* Just powerdown if init hasn't done after POWERDOWN_TIMEOUT seconds. * Just powerdown if init hasn't done after POWERDOWN_TIMEOUT seconds.
* I'm not shure if this feature is a good idea, for now it's here just to * I'm not sure if this feature is a good idea, for now it's here just to
* make the power button make behave just like under IRIX. * make the power button make behave just like under IRIX.
*/ */
#define POWERDOWN_TIMEOUT 120 #define POWERDOWN_TIMEOUT 120
...@@ -30,10 +35,11 @@ ...@@ -30,10 +35,11 @@
#define POWERDOWN_FREQ (HZ / 4) #define POWERDOWN_FREQ (HZ / 4)
#define PANIC_FREQ (HZ / 8) #define PANIC_FREQ (HZ / 8)
static unsigned char sgi_volume;
static struct timer_list power_timer, blink_timer, debounce_timer, volume_timer; static struct timer_list power_timer, blink_timer, debounce_timer, volume_timer;
static int shuting_down, has_paniced;
#define MACHINE_PANICED 1
#define MACHINE_SHUTTING_DOWN 2
static int machine_state = 0;
static void sgi_machine_restart(char *command) __attribute__((noreturn)); static void sgi_machine_restart(char *command) __attribute__((noreturn));
static void sgi_machine_halt(void) __attribute__((noreturn)); static void sgi_machine_halt(void) __attribute__((noreturn));
...@@ -42,35 +48,37 @@ static void sgi_machine_power_off(void) __attribute__((noreturn)); ...@@ -42,35 +48,37 @@ static void sgi_machine_power_off(void) __attribute__((noreturn));
/* XXX How to pass the reboot command to the firmware??? */ /* XXX How to pass the reboot command to the firmware??? */
static void sgi_machine_restart(char *command) static void sgi_machine_restart(char *command)
{ {
if (shuting_down) if (machine_state & MACHINE_SHUTTING_DOWN)
sgi_machine_power_off(); sgi_machine_power_off();
prom_reboot(); ArcReboot();
} }
static void sgi_machine_halt(void) static void sgi_machine_halt(void)
{ {
if (shuting_down) if (machine_state & MACHINE_SHUTTING_DOWN)
sgi_machine_power_off(); sgi_machine_power_off();
ArcEnterInteractiveMode(); ArcEnterInteractiveMode();
} }
static void sgi_machine_power_off(void) static void sgi_machine_power_off(void)
{ {
struct indy_clock *clock = (struct indy_clock *)INDY_CLOCK_REGS; unsigned char val;
cli(); local_irq_disable();
clock->cmd |= 0x08; /* Disable watchdog */ /* Disable watchdog */
clock->whsec = 0; val = CMOS_READ(RTC_CMD);
clock->wsec = 0; CMOS_WRITE(val | RTC_WAM, RTC_CMD);
CMOS_WRITE(0, RTC_WSEC);
CMOS_WRITE(0, RTC_WHSEC);
while(1) { while(1) {
hpc3mregs->panel=0xfe; sgioc->panel = ~SGIOC_PANEL_POWERON;
/* Good bye cruel world ... */ /* Good bye cruel world ... */
/* If we're still running, we probably got sent an alarm /* If we're still running, we probably got sent an alarm
interrupt. Read the flag to clear it. */ interrupt. Read the flag to clear it. */
clock->halarm; val = CMOS_READ(RTC_HOURS_ALARM);
} }
} }
...@@ -82,8 +90,8 @@ static void power_timeout(unsigned long data) ...@@ -82,8 +90,8 @@ static void power_timeout(unsigned long data)
static void blink_timeout(unsigned long data) static void blink_timeout(unsigned long data)
{ {
/* XXX fix this for fullhouse */ /* XXX fix this for fullhouse */
sgi_hpc_write1 ^= (HPC3_WRITE1_LC0OFF|HPC3_WRITE1_LC1OFF); sgi_ioc_reset ^= (SGIOC_RESET_LC0OFF|SGIOC_RESET_LC1OFF);
hpc3mregs->write1 = sgi_hpc_write1; sgioc->reset = sgi_ioc_reset;
mod_timer(&blink_timer, jiffies+data); mod_timer(&blink_timer, jiffies+data);
} }
...@@ -91,32 +99,35 @@ static void blink_timeout(unsigned long data) ...@@ -91,32 +99,35 @@ static void blink_timeout(unsigned long data)
static void debounce(unsigned long data) static void debounce(unsigned long data)
{ {
del_timer(&debounce_timer); del_timer(&debounce_timer);
if (ioc_icontrol->istat1 & 2) { /* Interrupt still being sent. */ if (sgint->istat1 & SGINT_ISTAT1_PWR) {
/* Interrupt still being sent. */
debounce_timer.expires = jiffies + 5; /* 0.05s */ debounce_timer.expires = jiffies + 5; /* 0.05s */
add_timer(&debounce_timer); add_timer(&debounce_timer);
hpc3mregs->panel = 0xf3; sgioc->panel = SGIOC_PANEL_POWERON | SGIOC_PANEL_POWERINTR |
SGIOC_PANEL_VOLDNINTR | SGIOC_PANEL_VOLDNHOLD |
SGIOC_PANEL_VOLUPINTR | SGIOC_PANEL_VOLUPHOLD;
return; return;
} }
if (has_paniced) if (machine_state & MACHINE_PANICED)
prom_reboot(); ArcReboot();
enable_irq(SGI_PANEL_IRQ); enable_irq(SGI_PANEL_IRQ);
} }
static inline void power_button(void) static inline void power_button(void)
{ {
if (has_paniced) if (machine_state & MACHINE_PANICED)
return; return;
if (shuting_down || kill_proc(1, SIGINT, 1)) { if ((machine_state & MACHINE_SHUTTING_DOWN) || kill_proc(1,SIGINT,1)) {
/* No init process or button pressed twice. */ /* No init process or button pressed twice. */
sgi_machine_power_off(); sgi_machine_power_off();
} }
shuting_down = 1; machine_state |= MACHINE_SHUTTING_DOWN;
blink_timer.data = POWERDOWN_FREQ; blink_timer.data = POWERDOWN_FREQ;
blink_timeout(POWERDOWN_FREQ); blink_timeout(POWERDOWN_FREQ);
...@@ -126,60 +137,43 @@ static inline void power_button(void) ...@@ -126,60 +137,43 @@ static inline void power_button(void)
add_timer(&power_timer); add_timer(&power_timer);
} }
inline void sgi_volume_set(unsigned char volume) void (*indy_volume_button)(int) = NULL;
{
sgi_volume = volume;
hpc3c0->pbus_extregs[2][0] = sgi_volume;
hpc3c0->pbus_extregs[2][1] = sgi_volume;
}
inline void sgi_volume_get(unsigned char *volume)
{
*volume = sgi_volume;
}
static inline void volume_up_button(unsigned long data) static inline void volume_up_button(unsigned long data)
{ {
del_timer(&volume_timer); del_timer(&volume_timer);
if (sgi_volume < 0xff) if (indy_volume_button)
sgi_volume++; indy_volume_button(1);
hpc3c0->pbus_extregs[2][0] = sgi_volume;
hpc3c0->pbus_extregs[2][1] = sgi_volume;
if (ioc_icontrol->istat1 & 2) { if (sgint->istat1 & SGINT_ISTAT1_PWR) {
volume_timer.expires = jiffies + 1; volume_timer.expires = jiffies + 1;
add_timer(&volume_timer); add_timer(&volume_timer);
} }
} }
static inline void volume_down_button(unsigned long data) static inline void volume_down_button(unsigned long data)
{ {
del_timer(&volume_timer); del_timer(&volume_timer);
if (sgi_volume > 0) if (indy_volume_button)
sgi_volume--; indy_volume_button(-1);
hpc3c0->pbus_extregs[2][0] = sgi_volume;
hpc3c0->pbus_extregs[2][1] = sgi_volume;
if (ioc_icontrol->istat1 & 2) { if (sgint->istat1 & SGINT_ISTAT1_PWR) {
volume_timer.expires = jiffies + 1; volume_timer.expires = jiffies + 1;
add_timer(&volume_timer); add_timer(&volume_timer);
} }
} }
static void panel_int(int irq, void *dev_id, struct pt_regs *regs) static irqreturn_t panel_int(int irq, void *dev_id, struct pt_regs *regs)
{ {
unsigned int buttons; unsigned int buttons;
buttons = hpc3mregs->panel; buttons = sgioc->panel;
hpc3mregs->panel = 3; /* power_interrupt | power_supply_on */ sgioc->panel = SGIOC_PANEL_POWERON | SGIOC_PANEL_POWERINTR;
if (ioc_icontrol->istat1 & 2) { /* Wait until interrupt goes away */ if (sgint->istat1 & SGINT_ISTAT1_PWR) {
/* Wait until interrupt goes away */
disable_irq(SGI_PANEL_IRQ); disable_irq(SGI_PANEL_IRQ);
init_timer(&debounce_timer); init_timer(&debounce_timer);
debounce_timer.function = debounce; debounce_timer.function = debounce;
...@@ -187,28 +181,42 @@ static void panel_int(int irq, void *dev_id, struct pt_regs *regs) ...@@ -187,28 +181,42 @@ static void panel_int(int irq, void *dev_id, struct pt_regs *regs)
add_timer(&debounce_timer); add_timer(&debounce_timer);
} }
if (!(buttons & 2)) /* Power button was pressed */ /* Power button was pressed
*
* ioc.ps page 22: "The Panel Register is called Power Control by Full
* House. Only lowest 2 bits are used. Guiness uses upper four bits
* for volume control". This is not true, all bits are pulled high
* on fullhouse
*/
if (ip22_is_fullhouse() || !(buttons & SGIOC_PANEL_POWERINTR)) {
power_button(); power_button();
if (!(buttons & 0x40)) { /* Volume up button was pressed */ return IRQ_HANDLED;
}
/* TODO: mute/unmute */
/* Volume up button was pressed */
if (!(buttons & SGIOC_PANEL_VOLUPINTR)) {
init_timer(&volume_timer); init_timer(&volume_timer);
volume_timer.function = volume_up_button; volume_timer.function = volume_up_button;
volume_timer.expires = jiffies + 1; volume_timer.expires = jiffies + 1;
add_timer(&volume_timer); add_timer(&volume_timer);
} }
if (!(buttons & 0x10)) { /* Volume down button was pressed */ /* Volume down button was pressed */
if (!(buttons & SGIOC_PANEL_VOLDNINTR)) {
init_timer(&volume_timer); init_timer(&volume_timer);
volume_timer.function = volume_down_button; volume_timer.function = volume_down_button;
volume_timer.expires = jiffies + 1; volume_timer.expires = jiffies + 1;
add_timer(&volume_timer); add_timer(&volume_timer);
} }
return IRQ_HANDLED;
} }
static int panic_event(struct notifier_block *this, unsigned long event, static int panic_event(struct notifier_block *this, unsigned long event,
void *ptr) void *ptr)
{ {
if (has_paniced) if (machine_state & MACHINE_PANICED)
return NOTIFY_DONE; return NOTIFY_DONE;
has_paniced = 1; machine_state |= MACHINE_PANICED;
blink_timer.data = PANIC_FREQ; blink_timer.data = PANIC_FREQ;
blink_timeout(PANIC_FREQ); blink_timeout(PANIC_FREQ);
...@@ -217,19 +225,11 @@ static int panic_event(struct notifier_block *this, unsigned long event, ...@@ -217,19 +225,11 @@ static int panic_event(struct notifier_block *this, unsigned long event,
} }
static struct notifier_block panic_block = { static struct notifier_block panic_block = {
panic_event, .notifier_call = panic_event,
NULL,
0
}; };
void indy_reboot_setup(void) static int __init reboot_setup(void)
{ {
static int setup_done;
if (setup_done)
return;
setup_done = 1;
_machine_restart = sgi_machine_restart; _machine_restart = sgi_machine_restart;
_machine_halt = sgi_machine_halt; _machine_halt = sgi_machine_halt;
_machine_power_off = sgi_machine_power_off; _machine_power_off = sgi_machine_power_off;
...@@ -238,4 +238,8 @@ void indy_reboot_setup(void) ...@@ -238,4 +238,8 @@ void indy_reboot_setup(void)
init_timer(&blink_timer); init_timer(&blink_timer);
blink_timer.function = blink_timeout; blink_timer.function = blink_timeout;
notifier_chain_register(&panic_notifier_list, &panic_block); notifier_chain_register(&panic_notifier_list, &panic_block);
return 0;
} }
subsys_initcall(reboot_setup);
...@@ -7,30 +7,26 @@ ...@@ -7,30 +7,26 @@
* *
* Copyright (C) 1998, 2001 by Ralf Baechle * Copyright (C) 1998, 2001 by Ralf Baechle
*/ */
#include <linux/mc146818rtc.h> #include <asm/ds1286.h>
#include <asm/sgi/sgihpc.h> #include <asm/sgi/hpc3.h>
static unsigned char indy_rtc_read_data(unsigned long addr) static unsigned char ip22_rtc_read_data(unsigned long addr)
{ {
volatile unsigned int *rtcregs = (void *)INDY_CLOCK_REGS; return hpc3c0->rtcregs[addr];
return rtcregs[addr];
} }
static void indy_rtc_write_data(unsigned char data, unsigned long addr) static void ip22_rtc_write_data(unsigned char data, unsigned long addr)
{ {
volatile unsigned int *rtcregs = (void *)INDY_CLOCK_REGS; hpc3c0->rtcregs[addr] = data;
rtcregs[addr] = data;
} }
static int indy_rtc_bcd_mode(void) static int ip22_rtc_bcd_mode(void)
{ {
return 0; return 0;
} }
struct rtc_ops indy_rtc_ops = { struct rtc_ops ip22_rtc_ops = {
&indy_rtc_read_data, &ip22_rtc_read_data,
&indy_rtc_write_data, &ip22_rtc_write_data,
&indy_rtc_bcd_mode &ip22_rtc_bcd_mode
}; };
/*
* ip22-setup.c: SGI specific setup, including init of the feature struct.
*
* Copyright (C) 1996 David S. Miller (dm@engr.sgi.com)
* Copyright (C) 1997, 1998 Ralf Baechle (ralf@gnu.org)
*/
#include <linux/config.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/kdev_t.h>
#include <linux/types.h>
#include <linux/console.h>
#include <linux/sched.h>
#include <linux/tty.h>
#include <asm/addrspace.h>
#include <asm/bcache.h>
#include <asm/bootinfo.h>
#include <asm/irq.h>
#include <asm/reboot.h>
#include <asm/ds1286.h>
#include <asm/time.h>
#include <asm/gdb-stub.h>
#include <asm/io.h>
#include <asm/traps.h>
#include <asm/sgialib.h>
#include <asm/sgi/mc.h>
#include <asm/sgi/hpc3.h>
#include <asm/sgi/ip22.h>
#ifdef CONFIG_KGDB
extern void rs_kgdb_hook(int);
extern void breakpoint(void);
static int remote_debug = 0;
#endif
#if defined(CONFIG_IP22_SERIAL_CONSOLE) || defined(CONFIG_ARC_CONSOLE)
extern void console_setup(char *);
#endif
extern struct rtc_ops ip22_rtc_ops;
#define KBD_STAT_IBF 0x02 /* Keyboard input buffer full */
unsigned long sgi_gfxaddr;
/*
* Stop-A is originally a Sun thing that isn't standard on IP22 so to avoid
* accidents it's disabled by default on IP22.
*
* FIXME: provide a mechanism to change the value of stop_a_enabled.
*/
int serial_console;
int stop_a_enabled;
void ip22_do_break(void)
{
if (!stop_a_enabled)
return;
printk("\n");
ArcEnterInteractiveMode();
}
extern void ip22_be_init(void) __init;
extern void ip22_time_init(void) __init;
void __init ip22_setup(void)
{
char *ctype;
#ifdef CONFIG_KGDB
char *kgdb_ttyd;
#endif
board_be_init = ip22_be_init;
ip22_time_init();
/* Init the INDY HPC I/O controller. Need to call this before
* fucking with the memory controller because it needs to know the
* boardID and whether this is a Guiness or a FullHouse machine.
*/
sgihpc_init();
/* Init INDY memory controller. */
sgimc_init();
#ifdef CONFIG_BOARD_SCACHE
/* Now enable boardcaches, if any. */
indy_sc_init();
#endif
#ifdef CONFIG_VT
conswitchp = NULL;
#endif
/* Set the IO space to some sane value */
set_io_port_base (KSEG1ADDR (0x00080000));
/* ARCS console environment variable is set to "g?" for
* graphics console, it is set to "d" for the first serial
* line and "d2" for the second serial line.
*/
ctype = ArcGetEnvironmentVariable("console");
if (ctype && *ctype == 'd') {
#ifdef CONFIG_IP22_SERIAL_CONSOLE
if (*(ctype + 1) == '2')
console_setup("ttyS1");
else
console_setup("ttyS0");
#endif
}
#ifdef CONFIG_ARC_CONSOLE
else if (!ctype || *ctype != 'g') {
/* Use ARC if we don't want serial ('d') or Newport ('g'). */
prom_flags |= PROM_FLAG_USE_AS_CONSOLE;
console_setup("arc");
}
#endif
#ifdef CONFIG_KGDB
kgdb_ttyd = prom_getcmdline();
if ((kgdb_ttyd = strstr(kgdb_ttyd, "kgdb=ttyd")) != NULL) {
int line;
kgdb_ttyd += strlen("kgdb=ttyd");
if (*kgdb_ttyd != '1' && *kgdb_ttyd != '2')
printk(KERN_INFO "KGDB: Uknown serial line /dev/ttyd%c"
", falling back to /dev/ttyd1\n", *kgdb_ttyd);
line = *kgdb_ttyd == '2' ? 0 : 1;
printk(KERN_INFO "KGDB: Using serial line /dev/ttyd%d for "
"session\n", line ? 1 : 2);
rs_kgdb_hook(line);
printk(KERN_INFO "KGDB: Using serial line /dev/ttyd%d for "
"session, please connect your debugger\n", line ? 1:2);
remote_debug = 1;
/* Breakpoints and stuff are in sgi_irq_setup() */
}
#endif
#ifdef CONFIG_VT
#ifdef CONFIG_SGI_NEWPORT_CONSOLE
if (ctype && *ctype == 'g'){
unsigned long *gfxinfo;
long (*__vec)(void) =
(void *) *(long *)(long)((PROMBLOCK)->pvector + 0x20);
gfxinfo = (unsigned long *)__vec();
sgi_gfxaddr = ((gfxinfo[1] >= 0xa0000000
&& gfxinfo[1] <= 0xc0000000)
? gfxinfo[1] - 0xa0000000 : 0);
/* newport addresses? */
if (sgi_gfxaddr == 0x1f0f0000 || sgi_gfxaddr == 0x1f4f0000) {
conswitchp = &newport_con;
screen_info = (struct screen_info) {
0, 0, /* orig-x, orig-y */
0, /* unused */
0, /* orig_video_page */
0, /* orig_video_mode */
160, /* orig_video_cols */
0, 0, 0, /* unused, ega_bx, unused */
64, /* orig_video_lines */
0, /* orig_video_isVGA */
16 /* orig_video_points */
};
}
}
#endif
#ifdef CONFIG_DUMMY_CONSOLE
/* Either if newport console wasn't used or failed to initialize. */
#ifdef CONFIG_SGI_NEWPORT_CONSOLE
if(conswitchp != &newport_con)
#endif
conswitchp = &dummy_con;
#endif
#endif
rtc_ops = &ip22_rtc_ops;
#ifdef CONFIG_PSMOUSE
aux_device_present = 0xaa;
#endif
}
/*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Time operations for IP22 machines. Original code may come from
* Ralf Baechle or David S. Miller (sorry guys, i'm really not sure)
*
* Copyright (C) 2001 by Ladislav Michl
*/
#include <linux/bcd.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/kernel_stat.h>
#include <linux/time.h>
#include <asm/cpu.h>
#include <asm/mipsregs.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/time.h>
#include <asm/ds1286.h>
#include <asm/sgialib.h>
#include <asm/sgi/ioc.h>
#include <asm/sgi/hpc3.h>
#include <asm/sgi/ip22.h>
/*
* note that mktime uses month from 1 to 12 while to_tm
* uses 0 to 11.
*/
static unsigned long indy_rtc_get_time(void)
{
unsigned int yrs, mon, day, hrs, min, sec;
unsigned int save_control;
save_control = hpc3c0->rtcregs[RTC_CMD] & 0xff;
hpc3c0->rtcregs[RTC_CMD] = save_control | RTC_TE;
sec = BCD2BIN(hpc3c0->rtcregs[RTC_SECONDS] & 0xff);
min = BCD2BIN(hpc3c0->rtcregs[RTC_MINUTES] & 0xff);
hrs = BCD2BIN(hpc3c0->rtcregs[RTC_HOURS] & 0x1f);
day = BCD2BIN(hpc3c0->rtcregs[RTC_DATE] & 0xff);
mon = BCD2BIN(hpc3c0->rtcregs[RTC_MONTH] & 0x1f);
yrs = BCD2BIN(hpc3c0->rtcregs[RTC_YEAR] & 0xff);
hpc3c0->rtcregs[RTC_CMD] = save_control;
if (yrs < 45)
yrs += 30;
if ((yrs += 40) < 70)
yrs += 100;
return mktime(yrs + 1900, mon, day, hrs, min, sec);
}
static int indy_rtc_set_time(unsigned long tim)
{
struct rtc_time tm;
unsigned int save_control;
to_tm(tim, &tm);
tm.tm_mon += 1; /* tm_mon starts at zero */
tm.tm_year -= 1940;
if (tm.tm_year >= 100)
tm.tm_year -= 100;
save_control = hpc3c0->rtcregs[RTC_CMD] & 0xff;
hpc3c0->rtcregs[RTC_CMD] = save_control | RTC_TE;
hpc3c0->rtcregs[RTC_YEAR] = BIN2BCD(tm.tm_sec);
hpc3c0->rtcregs[RTC_MONTH] = BIN2BCD(tm.tm_mon);
hpc3c0->rtcregs[RTC_DATE] = BIN2BCD(tm.tm_mday);
hpc3c0->rtcregs[RTC_HOURS] = BIN2BCD(tm.tm_hour);
hpc3c0->rtcregs[RTC_MINUTES] = BIN2BCD(tm.tm_min);
hpc3c0->rtcregs[RTC_SECONDS] = BIN2BCD(tm.tm_sec);
hpc3c0->rtcregs[RTC_HUNDREDTH_SECOND] = 0;
hpc3c0->rtcregs[RTC_CMD] = save_control;
return 0;
}
static unsigned long dosample(void)
{
u32 ct0, ct1;
volatile u8 msb, lsb;
/* Start the counter. */
sgint->tcword = (SGINT_TCWORD_CNT2 | SGINT_TCWORD_CALL |
SGINT_TCWORD_MRGEN);
sgint->tcnt2 = (SGINT_TCSAMP_COUNTER & 0xff);
sgint->tcnt2 = (SGINT_TCSAMP_COUNTER >> 8);
/* Get initial counter invariant */
ct0 = read_c0_count();
/* Latch and spin until top byte of counter2 is zero */
do {
sgint->tcword = (SGINT_TCWORD_CNT2 | SGINT_TCWORD_CLAT);
lsb = sgint->tcnt2;
msb = sgint->tcnt2;
ct1 = read_c0_count();
} while (msb);
/* Stop the counter. */
sgint->tcword = (SGINT_TCWORD_CNT2 | SGINT_TCWORD_CALL |
SGINT_TCWORD_MSWST);
/*
* Return the difference, this is how far the r4k counter increments
* for every 1/HZ seconds. We round off the nearest 1 MHz of master
* clock (= 1000000 / 100 / 2 = 5000 count).
*/
return ((ct1 - ct0) / 5000) * 5000;
}
/*
* Here we need to calibrate the cycle counter to at least be close.
*/
static __init void indy_time_init(void)
{
unsigned long r4k_ticks[3];
unsigned long r4k_tick;
/*
* Figure out the r4k offset, the algorithm is very simple
* and works in _all_ cases as long as the 8254 counter
* register itself works ok (as an interrupt driving timer
* it does not because of bug, this is why we are using
* the onchip r4k counter/compare register to serve this
* purpose, but for r4k_offset calculation it will work
* ok for us). There are other very complicated ways
* of performing this calculation but this one works just
* fine so I am not going to futz around. ;-)
*/
printk(KERN_INFO "Calibrating system timer... ");
dosample(); /* Prime cache. */
dosample(); /* Prime cache. */
/* Zero is NOT an option. */
do {
r4k_ticks[0] = dosample();
} while (!r4k_ticks[0]);
do {
r4k_ticks[1] = dosample();
} while (!r4k_ticks[1]);
if (r4k_ticks[0] != r4k_ticks[1]) {
printk("warning: timer counts differ, retrying... ");
r4k_ticks[2] = dosample();
if (r4k_ticks[2] == r4k_ticks[0]
|| r4k_ticks[2] == r4k_ticks[1])
r4k_tick = r4k_ticks[2];
else {
printk("disagreement, using average... ");
r4k_tick = (r4k_ticks[0] + r4k_ticks[1]
+ r4k_ticks[2]) / 3;
}
} else
r4k_tick = r4k_ticks[0];
printk("%d [%d.%02d MHz CPU]\n", (int) r4k_tick,
(int) (r4k_tick / 5000), (int) (r4k_tick % 5000) / 50);
mips_counter_frequency = r4k_tick * HZ;
}
/* Generic SGI handler for (spurious) 8254 interrupts */
void indy_8254timer_irq(struct pt_regs *regs)
{
int cpu = smp_processor_id();
int irq = SGI_8254_0_IRQ;
ULONG cnt;
char c;
irq_enter();
kstat_cpu(cpu).irqs[irq]++;
printk(KERN_ALERT "Oops, got 8254 interrupt.\n");
ArcRead(0, &c, 1, &cnt);
ArcEnterInteractiveMode();
irq_exit();
}
void indy_r4k_timer_interrupt(struct pt_regs *regs)
{
int cpu = smp_processor_id();
int irq = SGI_TIMER_IRQ;
irq_enter();
kstat_cpu(cpu).irqs[irq]++;
timer_interrupt(irq, NULL, regs);
irq_exit();
if (softirq_pending(cpu))
do_softirq();
}
extern int setup_irq(unsigned int irq, struct irqaction *irqaction);
static void indy_timer_setup(struct irqaction *irq)
{
/* over-write the handler, we use our own way */
irq->handler = no_action;
/* setup irqaction */
setup_irq(SGI_TIMER_IRQ, irq);
}
void __init ip22_time_init(void)
{
/* setup hookup functions */
rtc_get_time = indy_rtc_get_time;
rtc_set_time = indy_rtc_set_time;
board_time_init = indy_time_init;
board_timer_setup = indy_timer_setup;
}
/*
* indyIRQ.S: Interrupt exception dispatch code for FullHouse and
* Guiness.
*
* Copyright (C) 1996 David S. Miller (dm@engr.sgi.com)
*/
#include <asm/asm.h>
#include <asm/mipsregs.h>
#include <asm/regdef.h>
#include <asm/stackframe.h>
/* A lot of complication here is taken away because:
*
* 1) We handle one interrupt and return, sitting in a loop
* and moving across all the pending IRQ bits in the cause
* register is _NOT_ the answer, the common case is one
* pending IRQ so optimize in that direction.
*
* 2) We need not check against bits in the status register
* IRQ mask, that would make this routine slow as hell.
*
* 3) Linux only thinks in terms of all IRQs on or all IRQs
* off, nothing in between like BSD spl() brain-damage.
*
* Furthermore, the IRQs on the INDY look basically (barring
* software IRQs which we don't use at all) like:
*
* MIPS IRQ Source
* -------- ------
* 0 Software (ignored)
* 1 Software (ignored)
* 2 Local IRQ level zero
* 3 Local IRQ level one
* 4 8254 Timer zero
* 5 8254 Timer one
* 6 Bus Error
* 7 R4k timer (what we use)
*
* We handle the IRQ according to _our_ priority which is:
*
* Highest ---- R4k Timer
* Local IRQ zero
* Local IRQ one
* Bus Error
* 8254 Timer zero
* Lowest ---- 8254 Timer one
*
* then we just return, if multiple IRQs are pending then
* we will just take another exception, big deal.
*/
.text
.set noreorder
.set noat
.align 5
NESTED(indyIRQ, PT_SIZE, sp)
SAVE_ALL
CLI
.set at
mfc0 s0, CP0_CAUSE # get irq mask
/* First we check for r4k counter/timer IRQ. */
andi a0, s0, CAUSEF_IP7
beq a0, zero, 1f
andi a0, s0, CAUSEF_IP2 # delay slot, check local level zero
/* Wheee, a timer interrupt. */
move a0, sp
jal indy_r4k_timer_interrupt
nop # delay slot
j ret_from_irq
nop # delay slot
1:
beq a0, zero, 1f
andi a0, s0, CAUSEF_IP3 # delay slot, check local level one
/* Wheee, local level zero interrupt. */
jal indy_local0_irqdispatch
move a0, sp # delay slot
j ret_from_irq
nop # delay slot
1:
beq a0, zero, 1f
andi a0, s0, CAUSEF_IP6 # delay slot, check bus error
/* Wheee, local level one interrupt. */
move a0, sp
jal indy_local1_irqdispatch
nop
j ret_from_irq
nop
1:
beq a0, zero, 1f
nop
/* Wheee, an asynchronous bus error... */
move a0, sp
jal indy_buserror_irq
nop
j ret_from_irq
nop
1:
/* Here by mistake? This is possible, what can happen
* is that by the time we take the exception the IRQ
* pin goes low, so just leave if this is the case.
*/
andi a0, s0, (CAUSEF_IP4 | CAUSEF_IP5)
beq a0, zero, 1f
/* Must be one of the 8254 timers... */
move a0, sp
jal indy_8254timer_irq
nop
1:
j ret_from_irq
nop
END(indyIRQ)
/* $Id: indy_hpc.c,v 1.9 1999/12/04 03:59:00 ralf Exp $
*
* indy_hpc.c: Routines for generic manipulation of the HPC controllers.
*
* Copyright (C) 1996 David S. Miller (dm@engr.sgi.com)
* Copyright (C) 1998 Ralf Baechle
*/
#include <linux/init.h>
#include <linux/types.h>
#include <asm/addrspace.h>
#include <asm/sgi/sgihpc.h>
#include <asm/sgi/sgint23.h>
#include <asm/sgialib.h>
#include <asm/bootinfo.h>
/* #define DEBUG_SGIHPC */
struct hpc3_regs *hpc3c0, *hpc3c1;
struct hpc3_miscregs *hpc3mregs;
/* We need software copies of these because they are write only. */
unsigned int sgi_hpc_write1, sgi_hpc_write2;
/* Machine specific identifier knobs. */
int sgi_has_ioc2 = 0;
int sgi_guiness = 0;
int sgi_boardid;
void __init sgihpc_init(void)
{
unsigned long sid, crev, brev;
hpc3c0 = (struct hpc3_regs *) (KSEG1 + HPC3_CHIP0_PBASE);
hpc3c1 = (struct hpc3_regs *) (KSEG1 + HPC3_CHIP1_PBASE);
hpc3mregs = (struct hpc3_miscregs *) (KSEG1 + HPC3_MREGS_PBASE);
sid = hpc3mregs->sysid;
sid &= 0xff;
crev = (sid & 0xe0) >> 5;
brev = (sid & 0x1e) >> 1;
#ifdef DEBUG_SGIHPC
prom_printf("sgihpc_init: crev<%2x> brev<%2x>\n", crev, brev);
prom_printf("sgihpc_init: ");
#endif
/* This test works now thanks to William J. Earl */
if ((sid & 1) == 0 ) {
#ifdef DEBUG_SGIHPC
prom_printf("GUINESS ");
#endif
sgi_guiness = 1;
mips_machtype = MACH_SGI_INDY;
} else {
#ifdef DEBUG_SGIHPC
prom_printf("FULLHOUSE ");
#endif
mips_machtype = MACH_SGI_INDIGO2;
sgi_guiness = 0;
}
sgi_boardid = brev;
#ifdef DEBUG_SGIHPC
prom_printf("sgi_boardid<%d> ", sgi_boardid);
#endif
if(crev == 1) {
if((sid & 1) || (brev >= 2)) {
#ifdef DEBUG_SGIHPC
prom_printf("IOC2 ");
#endif
sgi_has_ioc2 = 1;
} else {
#ifdef DEBUG_SGIHPC
prom_printf("IOC1 revision 1 ");
#endif
}
} else {
#ifdef DEBUG_SGIHPC
prom_printf("IOC1 revision 0 ");
#endif
}
#ifdef DEBUG_SGIHPC
prom_printf("\n");
#endif
sgi_hpc_write1 = (HPC3_WRITE1_PRESET |
HPC3_WRITE1_KMRESET |
HPC3_WRITE1_ERESET |
HPC3_WRITE1_LC0OFF);
sgi_hpc_write2 = (HPC3_WRITE2_EASEL |
HPC3_WRITE2_NTHRESH |
HPC3_WRITE2_TPSPEED |
HPC3_WRITE2_EPSEL |
HPC3_WRITE2_U0AMODE |
HPC3_WRITE2_U1AMODE);
if(!sgi_guiness)
sgi_hpc_write1 |= HPC3_WRITE1_GRESET;
hpc3mregs->write1 = sgi_hpc_write1;
hpc3mregs->write2 = sgi_hpc_write2;
hpc3c0->pbus_piocfgs[0][6] |= HPC3_PIOPCFG_HW;
}
/*
* indy_mc.c: Routines for manipulating the INDY memory controller.
*
* Copyright (C) 1996 David S. Miller (dm@engr.sgi.com)
* Copyright (C) 1999 Andrew R. Baker (andrewb@uab.edu) - Indigo2 changes
*
* $Id: indy_mc.c,v 1.7 1999/12/04 03:59:00 ralf Exp $
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <asm/addrspace.h>
#include <asm/ptrace.h>
#include <asm/sgi/sgimc.h>
#include <asm/sgi/sgihpc.h>
#include <asm/sgialib.h>
/* #define DEBUG_SGIMC */
struct sgimc_misc_ctrl *mcmisc_regs;
u32 *rpsscounter;
struct sgimc_dma_ctrl *dmactrlregs;
static inline char *mconfig_string(unsigned long val)
{
switch(val & SGIMC_MCONFIG_RMASK) {
case SGIMC_MCONFIG_FOURMB:
return "4MB";
case SGIMC_MCONFIG_EIGHTMB:
return "8MB";
case SGIMC_MCONFIG_SXTEENMB:
return "16MB";
case SGIMC_MCONFIG_TTWOMB:
return "32MB";
case SGIMC_MCONFIG_SFOURMB:
return "64MB";
case SGIMC_MCONFIG_OTEIGHTMB:
return "128MB";
default:
return "wheee, unknown";
};
}
void __init sgimc_init(void)
{
unsigned long tmpreg;
mcmisc_regs = (struct sgimc_misc_ctrl *)(KSEG1+0x1fa00000);
rpsscounter = (unsigned int *) (KSEG1 + 0x1fa01004);
dmactrlregs = (struct sgimc_dma_ctrl *) (KSEG1+0x1fa02000);
printk("MC: SGI memory controller Revision %d\n",
(int) mcmisc_regs->systemid & SGIMC_SYSID_MASKREV);
#if 0 /* XXX Until I figure out what this bit really indicates XXX */
/* XXX Is this systemid bit reliable? */
if(mcmisc_regs->systemid & SGIMC_SYSID_EPRESENT) {
EISA_bus = 1;
printk("with EISA\n");
} else {
EISA_bus = 0;
printk("no EISA\n");
}
#endif
#ifdef DEBUG_SGIMC
prom_printf("sgimc_init: memconfig0<%s> mconfig1<%s>\n",
mconfig_string(mcmisc_regs->mconfig0),
mconfig_string(mcmisc_regs->mconfig1));
prom_printf("mcdump: cpuctrl0<%08lx> cpuctrl1<%08lx>\n",
mcmisc_regs->cpuctrl0, mcmisc_regs->cpuctrl1);
prom_printf("mcdump: divider<%08lx>, gioparm<%04x>\n",
mcmisc_regs->divider, mcmisc_regs->gioparm);
#endif
/* Place the MC into a known state. This must be done before
* interrupts are first enabled etc.
*/
/* Step 1: The CPU/GIO error status registers will not latch
* up a new error status until the register has been
* cleared by the cpu. These status registers are
* cleared by writing any value to them.
*/
mcmisc_regs->cstat = mcmisc_regs->gstat = 0;
/* Step 2: Enable all parity checking in cpu control register
* zero.
*/
tmpreg = mcmisc_regs->cpuctrl0;
tmpreg |= (SGIMC_CCTRL0_EPERRGIO | SGIMC_CCTRL0_EPERRMEM |
SGIMC_CCTRL0_R4KNOCHKPARR);
mcmisc_regs->cpuctrl0 = tmpreg;
/* Step 3: Setup the MC write buffer depth, this is controlled
* in cpu control register 1 in the lower 4 bits.
*/
tmpreg = mcmisc_regs->cpuctrl1;
tmpreg &= ~0xf;
tmpreg |= 0xd;
mcmisc_regs->cpuctrl1 = tmpreg;
/* Step 4: Initialize the RPSS divider register to run as fast
* as it can correctly operate. The register is laid
* out as follows:
*
* ----------------------------------------
* | RESERVED | INCREMENT | DIVIDER |
* ----------------------------------------
* 31 16 15 8 7 0
*
* DIVIDER determines how often a 'tick' happens,
* INCREMENT determines by how the RPSS increment
* registers value increases at each 'tick'. Thus,
* for IP22 we get INCREMENT=1, DIVIDER=1 == 0x101
*/
mcmisc_regs->divider = 0x101;
/* Step 5: Initialize GIO64 arbitrator configuration register.
*
* NOTE: If you dork with startup code the HPC init code in
* sgihpc_init() must run before us because of how we
* need to know Guiness vs. FullHouse and the board
* revision on this machine. You have been warned.
*/
/* First the basic invariants across all gio64 implementations. */
tmpreg = SGIMC_GIOPARM_HPC64; /* All 1st HPC's interface at 64bits. */
tmpreg |= SGIMC_GIOPARM_ONEBUS; /* Only one physical GIO bus exists. */
if(sgi_guiness) {
/* Guiness specific settings. */
tmpreg |= SGIMC_GIOPARM_EISA64; /* MC talks to EISA at 64bits */
tmpreg |= SGIMC_GIOPARM_MASTEREISA; /* EISA bus can act as master */
} else {
/* Fullhouse specific settings. */
if(sgi_boardid < 2) {
tmpreg |= SGIMC_GIOPARM_HPC264; /* 2nd HPC at 64bits */
tmpreg |= SGIMC_GIOPARM_PLINEEXP0; /* exp0 pipelines */
tmpreg |= SGIMC_GIOPARM_MASTEREXP1;/* exp1 masters */
tmpreg |= SGIMC_GIOPARM_RTIMEEXP0; /* exp0 is realtime */
} else {
tmpreg |= SGIMC_GIOPARM_HPC264; /* 2nd HPC 64bits */
tmpreg |= SGIMC_GIOPARM_PLINEEXP0; /* exp[01] pipelined */
tmpreg |= SGIMC_GIOPARM_PLINEEXP1;
tmpreg |= SGIMC_GIOPARM_MASTEREISA;/* EISA masters */
/* someone forgot this poor little guy... */
tmpreg |= SGIMC_GIOPARM_GFX64; /* GFX at 64 bits */
}
}
mcmisc_regs->gioparm = tmpreg; /* poof */
}
/*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* RTC routines for Indy style attached Dallas chip.
*
* Copyright (C) 1998, 2001 by Ralf Baechle
*/
#include <linux/mc146818rtc.h>
#include <asm/sgi/sgihpc.h>
static unsigned char indy_rtc_read_data(unsigned long addr)
{
volatile unsigned int *rtcregs = (void *)INDY_CLOCK_REGS;
return rtcregs[addr];
}
static void indy_rtc_write_data(unsigned char data, unsigned long addr)
{
volatile unsigned int *rtcregs = (void *)INDY_CLOCK_REGS;
rtcregs[addr] = data;
}
static int indy_rtc_bcd_mode(void)
{
return 0;
}
struct rtc_ops indy_rtc_ops = {
&indy_rtc_read_data,
&indy_rtc_write_data,
&indy_rtc_bcd_mode
};
/*
* indy_sc.c: Indy cache management functions.
*
* Copyright (C) 1997, 2001 Ralf Baechle (ralf@gnu.org),
* derived from r4xx0.c by David S. Miller (dm@engr.sgi.com).
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <asm/bcache.h>
#include <asm/sgi/sgi.h>
#include <asm/sgi/sgimc.h>
#include <asm/page.h>
#include <asm/pgtable.h>
#include <asm/system.h>
#include <asm/bootinfo.h>
#include <asm/sgialib.h>
#include <asm/mmu_context.h>
/* Secondary cache size in bytes, if present. */
static unsigned long scache_size;
#undef DEBUG_CACHE
#define SC_SIZE 0x00080000
#define SC_LINE 32
#define CI_MASK (SC_SIZE - SC_LINE)
#define SC_INDEX(n) ((n) & CI_MASK)
static inline void indy_sc_wipe(unsigned long first, unsigned long last)
{
unsigned long tmp;
__asm__ __volatile__(
".set\tnoreorder\t\t\t# indy_sc_wipe\n\t"
".set\tmips3\n\t"
".set\tnoat\n\t"
"mfc0\t%2, $12\n\t"
"li\t$1, 0x80\t\t\t# Go 64 bit\n\t"
"mtc0\t$1, $12\n\t"
"dli\t$1, 0x9000000080000000\n\t"
"or\t%0, $1\t\t\t# first line to flush\n\t"
"or\t%1, $1\t\t\t# last line to flush\n\t"
".set\tat\n\t"
"1:\tsw\t$0, 0(%0)\n\t"
"bne\t%0, %1, 1b\n\t"
"daddu\t%0, 32\n\t"
"mtc0\t%2, $12\t\t\t# Back to 32 bit\n\t"
"nop; nop; nop; nop;\n\t"
".set\tmips0\n\t"
".set\treorder"
: "=r" (first), "=r" (last), "=&r" (tmp)
: "0" (first), "1" (last)
: "$1");
}
static void indy_sc_wback_invalidate(unsigned long addr, unsigned long size)
{
unsigned long first_line, last_line;
unsigned int flags;
#ifdef DEBUG_CACHE
printk("indy_sc_wback_invalidate[%08lx,%08lx]", addr, size);
#endif
if (!size)
return;
/* Which lines to flush? */
first_line = SC_INDEX(addr);
last_line = SC_INDEX(addr + size - 1);
local_irq_save(flags);
if (first_line <= last_line) {
indy_sc_wipe(first_line, last_line);
goto out;
}
indy_sc_wipe(first_line, SC_SIZE - SC_LINE);
indy_sc_wipe(0, last_line);
out:
local_irq_restore(flags);
}
static void indy_sc_enable(void)
{
unsigned long addr, tmp1, tmp2;
/* This is really cool... */
#ifdef DEBUG_CACHE
printk("Enabling R4600 SCACHE\n");
#endif
__asm__ __volatile__(
".set\tpush\n\t"
".set\tnoreorder\n\t"
".set\tmips3\n\t"
"mfc0\t%2, $12\n\t"
"nop; nop; nop; nop;\n\t"
"li\t%1, 0x80\n\t"
"mtc0\t%1, $12\n\t"
"nop; nop; nop; nop;\n\t"
"li\t%0, 0x1\n\t"
"dsll\t%0, 31\n\t"
"lui\t%1, 0x9000\n\t"
"dsll32\t%1, 0\n\t"
"or\t%0, %1, %0\n\t"
"sb\t$0, 0(%0)\n\t"
"mtc0\t$0, $12\n\t"
"nop; nop; nop; nop;\n\t"
"mtc0\t%2, $12\n\t"
"nop; nop; nop; nop;\n\t"
".set\tpop"
: "=r" (tmp1), "=r" (tmp2), "=r" (addr));
}
static void indy_sc_disable(void)
{
unsigned long tmp1, tmp2, tmp3;
#ifdef DEBUG_CACHE
printk("Disabling R4600 SCACHE\n");
#endif
__asm__ __volatile__(
".set\tpush\n\t"
".set\tnoreorder\n\t"
".set\tmips3\n\t"
"li\t%0, 0x1\n\t"
"dsll\t%0, 31\n\t"
"lui\t%1, 0x9000\n\t"
"dsll32\t%1, 0\n\t"
"or\t%0, %1, %0\n\t"
"mfc0\t%2, $12\n\t"
"nop; nop; nop; nop\n\t"
"li\t%1, 0x80\n\t"
"mtc0\t%1, $12\n\t"
"nop; nop; nop; nop\n\t"
"sh\t$0, 0(%0)\n\t"
"mtc0\t$0, $12\n\t"
"nop; nop; nop; nop\n\t"
"mtc0\t%2, $12\n\t"
"nop; nop; nop; nop\n\t"
".set\tpop"
: "=r" (tmp1), "=r" (tmp2), "=r" (tmp3));
}
static inline int __init indy_sc_probe(void)
{
volatile unsigned int *cpu_control;
unsigned short cmd = 0xc220;
unsigned long data = 0;
int i, n;
#ifdef __MIPSEB__
cpu_control = (volatile unsigned int *) KSEG1ADDR(0x1fa00034);
#else
cpu_control = (volatile unsigned int *) KSEG1ADDR(0x1fa00030);
#endif
#define DEASSERT(bit) (*(cpu_control) &= (~(bit)))
#define ASSERT(bit) (*(cpu_control) |= (bit))
#define DELAY for(n = 0; n < 100000; n++) __asm__ __volatile__("")
DEASSERT(SGIMC_EEPROM_PRE);
DEASSERT(SGIMC_EEPROM_SDATAO);
DEASSERT(SGIMC_EEPROM_SECLOCK);
DEASSERT(SGIMC_EEPROM_PRE);
DELAY;
ASSERT(SGIMC_EEPROM_CSEL); ASSERT(SGIMC_EEPROM_SECLOCK);
for(i = 0; i < 11; i++) {
if(cmd & (1<<15))
ASSERT(SGIMC_EEPROM_SDATAO);
else
DEASSERT(SGIMC_EEPROM_SDATAO);
DEASSERT(SGIMC_EEPROM_SECLOCK);
ASSERT(SGIMC_EEPROM_SECLOCK);
cmd <<= 1;
}
DEASSERT(SGIMC_EEPROM_SDATAO);
for(i = 0; i < (sizeof(unsigned short) * 8); i++) {
unsigned int tmp;
DEASSERT(SGIMC_EEPROM_SECLOCK);
DELAY;
ASSERT(SGIMC_EEPROM_SECLOCK);
DELAY;
data <<= 1;
tmp = *cpu_control;
if(tmp & SGIMC_EEPROM_SDATAI)
data |= 1;
}
DEASSERT(SGIMC_EEPROM_SECLOCK);
DEASSERT(SGIMC_EEPROM_CSEL);
ASSERT(SGIMC_EEPROM_PRE);
ASSERT(SGIMC_EEPROM_SECLOCK);
data <<= PAGE_SHIFT;
if (data == 0)
return 0;
scache_size = data;
printk("R4600/R5000 SCACHE size %ldK, linesize 32 bytes.\n",
scache_size >> 10);
return 1;
}
/* XXX Check with wje if the Indy caches can differenciate between
writeback + invalidate and just invalidate. */
struct bcache_ops indy_sc_ops = {
indy_sc_enable,
indy_sc_disable,
indy_sc_wback_invalidate,
indy_sc_wback_invalidate
};
void __init indy_sc_init(void)
{
if (indy_sc_probe()) {
indy_sc_enable();
bcops = &indy_sc_ops;
}
}
This diff is collapsed.
/*
* system.c: Probe the system type using ARCS prom interface library.
*
* Copyright (C) 1996 David S. Miller (dm@engr.sgi.com)
*
* $Id: system.c,v 1.8 1999/10/09 00:00:59 ralf Exp $
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/string.h>
#include <asm/sgi/sgi.h>
#include <asm/sgialib.h>
#include <asm/bootinfo.h>
enum sgi_mach sgimach;
struct smatch {
char *name;
int type;
};
static struct smatch sgi_cputable[] = {
{ "MIPS-R2000", CPU_R2000 },
{ "MIPS-R3000", CPU_R3000 },
{ "MIPS-R3000A", CPU_R3000A },
{ "MIPS-R4000", CPU_R4000SC },
{ "MIPS-R4400", CPU_R4400SC },
{ "MIPS-R4600", CPU_R4600 },
{ "MIPS-R8000", CPU_R8000 },
{ "MIPS-R5000", CPU_R5000 },
{ "MIPS-R5000A", CPU_R5000A }
};
#define NUM_CPUS 9 /* for now */
static int __init string_to_cpu(char *s)
{
int i;
for(i = 0; i < NUM_CPUS; i++) {
if(!strcmp(s, sgi_cputable[i].name))
return sgi_cputable[i].type;
}
prom_printf("\nYeee, could not determine MIPS cpu type <%s>\n", s);
prom_printf("press a key to reboot\n");
prom_getchar();
romvec->imode();
return 0;
}
/*
* We' call this early before loadmmu(). If we do the other way around
* the firmware will crash and burn.
*/
void __init sgi_sysinit(void)
{
pcomponent *p, *toplev, *cpup = 0;
int cputype = -1;
/* The root component tells us what machine architecture we
* have here.
*/
p = prom_getchild(PROM_NULL_COMPONENT);
/* Now scan for cpu(s). */
toplev = p = prom_getchild(p);
while(p) {
int ncpus = 0;
if(p->type == Cpu) {
if(++ncpus > 1) {
prom_printf("\nYeee, SGI MP not ready yet\n");
prom_printf("press a key to reboot\n");
prom_getchar();
romvec->imode();
}
printk("CPU: %s ", p->iname);
cpup = p;
cputype = string_to_cpu(cpup->iname);
}
p = prom_getsibling(p);
}
if(cputype == -1) {
prom_printf("\nYeee, could not find cpu ARCS component\n");
prom_printf("press a key to reboot\n");
prom_getchar();
romvec->imode();
}
p = prom_getchild(cpup);
while(p) {
switch(p->class) {
case processor:
switch(p->type) {
case Fpu:
printk("FPU<%s> ", p->iname);
break;
default:
break;
};
break;
case cache:
switch(p->type) {
case picache:
printk("ICACHE ");
break;
case pdcache:
printk("DCACHE ");
break;
case sccache:
printk("SCACHE ");
break;
default:
break;
};
break;
default:
break;
};
p = prom_getsibling(p);
}
printk("\n");
}
/*
* time.c: Generic SGI handler for (spurious) 8254 interrupts
*
* Copyright (C) 1996 David S. Miller (dm@engr.sgi.com)
*/
#include <linux/interrupt.h>
#include <linux/kernel_stat.h>
#include <asm/sgialib.h>
void indy_8254timer_irq(void)
{
int cpu = smp_processor_id();
int irq = 4;
irq_enter(cpu, irq);
kstat_cpu(0).irqs[irq]++;
printk("indy_8254timer_irq: Whoops, should not have gotten this IRQ\n");
prom_getchar();
ArcEnterInteractiveMode();
irq_exit(cpu, irq);
}
This diff is collapsed.
#
# Makefile for the SGI specific kernel interface routines
# under Linux.
#
EXTRA_AFLAGS := $(CFLAGS)
lib-y += ip22-berr.o ip22-mc.o ip22-sc.o ip22-hpc.o ip22-int.o ip22-rtc.o \
ip22-setup.o system.o ip22-timer.o ip22-irq.o ip22-reset.o time.o
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment