Initial commit; kernel source import

This commit is contained in:
Nathan
2025-04-06 23:50:55 -05:00
commit 25c6d769f4
45093 changed files with 18199410 additions and 0 deletions

View File

@@ -0,0 +1,11 @@
#
# Makefile for the SGI specific kernel interface routines
# under Linux.
#
obj-y += ip22-mc.o ip22-hpc.o ip22-int.o ip22-time.o ip22-nvram.o \
ip22-platform.o ip22-reset.o ip22-setup.o ip22-gio.o
obj-$(CONFIG_SGI_IP22) += ip22-berr.o
obj-$(CONFIG_SGI_IP28) += ip28-berr.o
obj-$(CONFIG_EISA) += ip22-eisa.o

View File

@@ -0,0 +1,34 @@
#
# SGI IP22 (Indy/Indigo2)
#
# Set the load address to >= 0xffffffff88069000 if you want to leave space for
# symmon, 0xffffffff80002000 for production kernels. Note that the value must
# be aligned to a multiple of the kernel stack size or the handling of the
# current variable will break so for 64-bit kernels we have to raise the start
# address by 8kb.
#
platform-$(CONFIG_SGI_IP22) += sgi-ip22/
cflags-$(CONFIG_SGI_IP22) += -I$(srctree)/arch/mips/include/asm/mach-ip22
ifdef CONFIG_32BIT
load-$(CONFIG_SGI_IP22) += 0xffffffff88002000
endif
ifdef CONFIG_64BIT
load-$(CONFIG_SGI_IP22) += 0xffffffff88004000
endif
#
# SGI IP28 (Indigo2 R10k)
#
# Set the load address to >= 0xa800000020080000 if you want to leave space for
# symmon, 0xa800000020004000 for production kernels ? Note that the value must
# be 16kb aligned or the handling of the current variable will break.
# Simplified: what IP22 does at 128MB+ in ksegN, IP28 does at 512MB+ in xkphys
#
ifdef CONFIG_SGI_IP28
ifeq ($(call cc-option-yn,-mr10k-cache-barrier=store), n)
$(error gcc doesn't support needed option -mr10k-cache-barrier=store)
endif
endif
platform-$(CONFIG_SGI_IP28) += sgi-ip22/
cflags-$(CONFIG_SGI_IP28) += -mr10k-cache-barrier=store -I$(srctree)/arch/mips/include/asm/mach-ip28
load-$(CONFIG_SGI_IP28) += 0xa800000020004000

View File

@@ -0,0 +1,115 @@
/*
* ip22-berr.c: Bus error handling.
*
* Copyright (C) 2002, 2003 Ladislav Michl (ladis@linux-mips.org)
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <asm/addrspace.h>
#include <asm/traps.h>
#include <asm/branch.h>
#include <asm/irq_regs.h>
#include <asm/sgi/mc.h>
#include <asm/sgi/hpc3.h>
#include <asm/sgi/ioc.h>
#include <asm/sgi/ip22.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 unsigned int extio_stat;
static unsigned int hpc3_berr_stat; /* Bus error interrupt status */
static void save_and_clear_buserr(void)
{
/* save status registers */
cpu_err_addr = sgimc->cerr;
cpu_err_stat = sgimc->cstat;
gio_err_addr = sgimc->gerr;
gio_err_stat = sgimc->gstat;
extio_stat = ip22_is_fullhouse() ? sgioc->extio : (sgint->errstat << 4);
hpc3_berr_stat = hpc3c0->bestat;
sgimc->cstat = sgimc->gstat = 0;
}
#define GIO_ERRMASK 0xff00
#define CPU_ERRMASK 0x3f00
static void print_buserr(void)
{
if (extio_stat & EXTIO_MC_BUSERR)
printk(KERN_ERR "MC Bus Error\n");
if (extio_stat & EXTIO_HPC3_BUSERR)
printk(KERN_ERR "HPC3 Bus Error 0x%x:<id=0x%x,%s,lane=0x%x>\n",
hpc3_berr_stat,
(hpc3_berr_stat & HPC3_BESTAT_PIDMASK) >>
HPC3_BESTAT_PIDSHIFT,
(hpc3_berr_stat & HPC3_BESTAT_CTYPE) ? "PIO" : "DMA",
hpc3_berr_stat & HPC3_BESTAT_BLMASK);
if (extio_stat & EXTIO_EISA_BUSERR)
printk(KERN_ERR "EISA Bus Error\n");
if (cpu_err_stat & CPU_ERRMASK)
printk(KERN_ERR "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_ERR "GIO error 0x%x:<%s%s%s%s%s%s%s%s> @ 0x%08x\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)
{
const int field = 2 * sizeof(unsigned long);
struct pt_regs *regs = get_irq_regs();
save_and_clear_buserr();
print_buserr();
printk(KERN_ALERT "%s bus error, epc == %0*lx, ra == %0*lx\n",
(regs->cp0_cause & 4) ? "Data" : "Instruction",
field, regs->cp0_epc, field, regs->regs[31]);
/* Assume it would be too dangerous to continue ... */
die_if_kernel("Oops", regs);
force_sig(SIGBUS, current);
}
static 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;
}

View File

@@ -0,0 +1,145 @@
/*
* 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/eisa.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/io.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>
#include <asm/i8259.h>
/* I2 has four EISA slots. */
#define IP22_EISA_MAX_SLOTS 4
#define EISA_MAX_IRQ 16
#define EIU_MODE_REG 0x0001ffc0
#define EIU_STAT_REG 0x0001ffc4
#define EIU_PREMPT_REG 0x0001ffc8
#define EIU_QUIET_REG 0x0001ffcc
#define EIU_INTRPT_ACK 0x00010004
static char __init *decode_eisa_sig(unsigned long addr)
{
static char sig_str[EISA_SIG_LEN] __initdata;
u8 sig[4];
u16 rev;
int i;
for (i = 0; i < 4; i++) {
sig[i] = inb(addr + i);
if (!i && (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 irqreturn_t ip22_eisa_intr(int irq, void *dev_id)
{
u8 eisa_irq = inb(EIU_INTRPT_ACK);
inb(EISA_DMA1_STATUS);
inb(EISA_DMA2_STATUS);
if (eisa_irq < EISA_MAX_IRQ) {
do_IRQ(eisa_irq);
return IRQ_HANDLED;
}
/* Oops, Bad Stuff Happened... */
printk(KERN_ERR "eisa_irq %d out of bound\n", eisa_irq);
outb(0x20, EISA_INT2_CTRL);
outb(0x20, EISA_INT1_CTRL);
return IRQ_NONE;
}
static struct irqaction eisa_action = {
.handler = ip22_eisa_intr,
.name = "EISA",
};
int __init ip22_eisa_init(void)
{
int i, c;
char *str;
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 <= IP22_EISA_MAX_SLOTS; i++) {
if ((str = decode_eisa_sig(0x1000 * i + EISA_VENDOR_ID_OFFSET))) {
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 */
outl(0x0000FFFF, EIU_PREMPT_REG);
outl(1, EIU_QUIET_REG);
outl(0x40f3c07F, EIU_MODE_REG);
/* Now be nice to the EISA chipset */
outb(1, EISA_EXT_NMI_RESET_CTRL);
udelay(50); /* Wait long enough for the dust to settle */
outb(0, EISA_EXT_NMI_RESET_CTRL);
outb(0, EISA_DMA2_WRITE_SINGLE);
init_i8259_irqs();
/* 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);
EISA_bus = 1;
return 0;
}

View File

@@ -0,0 +1,428 @@
#include <linux/export.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <asm/addrspace.h>
#include <asm/paccess.h>
#include <asm/gio_device.h>
#include <asm/sgi/gio.h>
#include <asm/sgi/hpc3.h>
#include <asm/sgi/mc.h>
#include <asm/sgi/ip22.h>
static struct bus_type gio_bus_type;
static struct {
const char *name;
__u8 id;
} gio_name_table[] = {
{ .name = "SGI Impact", .id = 0x10 },
{ .name = "Phobos G160", .id = 0x35 },
/* fake IDs */
{ .name = "SGI Newport", .id = 0x7e },
{ .name = "SGI GR2/GR3", .id = 0x7f },
};
static struct device gio_bus = {
.init_name = "gio",
};
/**
* gio_match_device - Tell if an of_device structure has a matching
* gio_match structure
* @ids: array of of device match structures to search in
* @dev: the of device structure to match against
*
* Used by a driver to check whether an of_device present in the
* system is in its list of supported devices.
*/
const struct gio_device_id *gio_match_device(const struct gio_device_id *match,
const struct gio_device *dev)
{
const struct gio_device_id *ids;
for (ids = match; ids->id != 0xff; ids++)
if (ids->id == dev->id.id)
return ids;
return NULL;
}
EXPORT_SYMBOL_GPL(gio_match_device);
struct gio_device *gio_dev_get(struct gio_device *dev)
{
struct device *tmp;
if (!dev)
return NULL;
tmp = get_device(&dev->dev);
if (tmp)
return to_gio_device(tmp);
else
return NULL;
}
EXPORT_SYMBOL_GPL(gio_dev_get);
void gio_dev_put(struct gio_device *dev)
{
if (dev)
put_device(&dev->dev);
}
EXPORT_SYMBOL_GPL(gio_dev_put);
/**
* gio_release_dev - free an gio device structure when all users of it are finished.
* @dev: device that's been disconnected
*
* Will be called only by the device core when all users of this gio device are
* done.
*/
void gio_release_dev(struct device *dev)
{
struct gio_device *giodev;
giodev = to_gio_device(dev);
kfree(giodev);
}
EXPORT_SYMBOL_GPL(gio_release_dev);
int gio_device_register(struct gio_device *giodev)
{
giodev->dev.bus = &gio_bus_type;
giodev->dev.parent = &gio_bus;
return device_register(&giodev->dev);
}
EXPORT_SYMBOL_GPL(gio_device_register);
void gio_device_unregister(struct gio_device *giodev)
{
device_unregister(&giodev->dev);
}
EXPORT_SYMBOL_GPL(gio_device_unregister);
static int gio_bus_match(struct device *dev, struct device_driver *drv)
{
struct gio_device *gio_dev = to_gio_device(dev);
struct gio_driver *gio_drv = to_gio_driver(drv);
return gio_match_device(gio_drv->id_table, gio_dev) != NULL;
}
static int gio_device_probe(struct device *dev)
{
int error = -ENODEV;
struct gio_driver *drv;
struct gio_device *gio_dev;
const struct gio_device_id *match;
drv = to_gio_driver(dev->driver);
gio_dev = to_gio_device(dev);
if (!drv->probe)
return error;
gio_dev_get(gio_dev);
match = gio_match_device(drv->id_table, gio_dev);
if (match)
error = drv->probe(gio_dev, match);
if (error)
gio_dev_put(gio_dev);
return error;
}
static int gio_device_remove(struct device *dev)
{
struct gio_device *gio_dev = to_gio_device(dev);
struct gio_driver *drv = to_gio_driver(dev->driver);
if (dev->driver && drv->remove)
drv->remove(gio_dev);
return 0;
}
static int gio_device_suspend(struct device *dev, pm_message_t state)
{
struct gio_device *gio_dev = to_gio_device(dev);
struct gio_driver *drv = to_gio_driver(dev->driver);
int error = 0;
if (dev->driver && drv->suspend)
error = drv->suspend(gio_dev, state);
return error;
}
static int gio_device_resume(struct device *dev)
{
struct gio_device *gio_dev = to_gio_device(dev);
struct gio_driver *drv = to_gio_driver(dev->driver);
int error = 0;
if (dev->driver && drv->resume)
error = drv->resume(gio_dev);
return error;
}
static void gio_device_shutdown(struct device *dev)
{
struct gio_device *gio_dev = to_gio_device(dev);
struct gio_driver *drv = to_gio_driver(dev->driver);
if (dev->driver && drv->shutdown)
drv->shutdown(gio_dev);
}
static ssize_t modalias_show(struct device *dev, struct device_attribute *a,
char *buf)
{
struct gio_device *gio_dev = to_gio_device(dev);
int len = snprintf(buf, PAGE_SIZE, "gio:%x\n", gio_dev->id.id);
return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len;
}
static ssize_t name_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct gio_device *giodev;
giodev = to_gio_device(dev);
return sprintf(buf, "%s", giodev->name);
}
static ssize_t id_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct gio_device *giodev;
giodev = to_gio_device(dev);
return sprintf(buf, "%x", giodev->id.id);
}
static struct device_attribute gio_dev_attrs[] = {
__ATTR_RO(modalias),
__ATTR_RO(name),
__ATTR_RO(id),
__ATTR_NULL,
};
static int gio_device_uevent(struct device *dev, struct kobj_uevent_env *env)
{
struct gio_device *gio_dev = to_gio_device(dev);
add_uevent_var(env, "MODALIAS=gio:%x", gio_dev->id.id);
return 0;
}
int gio_register_driver(struct gio_driver *drv)
{
/* initialize common driver fields */
if (!drv->driver.name)
drv->driver.name = drv->name;
if (!drv->driver.owner)
drv->driver.owner = drv->owner;
drv->driver.bus = &gio_bus_type;
/* register with core */
return driver_register(&drv->driver);
}
EXPORT_SYMBOL_GPL(gio_register_driver);
void gio_unregister_driver(struct gio_driver *drv)
{
driver_unregister(&drv->driver);
}
EXPORT_SYMBOL_GPL(gio_unregister_driver);
void gio_set_master(struct gio_device *dev)
{
u32 tmp = sgimc->giopar;
switch (dev->slotno) {
case 0:
tmp |= SGIMC_GIOPAR_MASTERGFX;
break;
case 1:
tmp |= SGIMC_GIOPAR_MASTEREXP0;
break;
case 2:
tmp |= SGIMC_GIOPAR_MASTEREXP1;
break;
}
sgimc->giopar = tmp;
}
EXPORT_SYMBOL_GPL(gio_set_master);
void ip22_gio_set_64bit(int slotno)
{
u32 tmp = sgimc->giopar;
switch (slotno) {
case 0:
tmp |= SGIMC_GIOPAR_GFX64;
break;
case 1:
tmp |= SGIMC_GIOPAR_EXP064;
break;
case 2:
tmp |= SGIMC_GIOPAR_EXP164;
break;
}
sgimc->giopar = tmp;
}
static int ip22_gio_id(unsigned long addr, u32 *res)
{
u8 tmp8;
u8 tmp16;
u32 tmp32;
u8 *ptr8;
u16 *ptr16;
u32 *ptr32;
ptr32 = (void *)CKSEG1ADDR(addr);
if (!get_dbe(tmp32, ptr32)) {
/*
* We got no DBE, but this doesn't mean anything.
* If GIO is pipelined (which can't be disabled
* for GFX slot) we don't get a DBE, but we see
* the transfer size as data. So we do an 8bit
* and a 16bit access and check whether the common
* data matches
*/
ptr8 = (void *)CKSEG1ADDR(addr + 3);
get_dbe(tmp8, ptr8);
ptr16 = (void *)CKSEG1ADDR(addr + 2);
get_dbe(tmp16, ptr16);
if (tmp8 == (tmp16 & 0xff) &&
tmp8 == (tmp32 & 0xff) &&
tmp16 == (tmp32 & 0xffff)) {
*res = tmp32;
return 1;
}
}
return 0; /* nothing here */
}
#define HQ2_MYSTERY_OFFS 0x6A07C
#define NEWPORT_USTATUS_OFFS 0xF133C
static int ip22_is_gr2(unsigned long addr)
{
u32 tmp;
u32 *ptr;
/* HQ2 only allows 32bit accesses */
ptr = (void *)CKSEG1ADDR(addr + HQ2_MYSTERY_OFFS);
if (!get_dbe(tmp, ptr)) {
if (tmp == 0xdeadbeef)
return 1;
}
return 0;
}
static void ip22_check_gio(int slotno, unsigned long addr)
{
const char *name = "Unknown";
struct gio_device *gio_dev;
u32 tmp;
__u8 id;
int i;
/* first look for GR2/GR3 by checking mystery register */
if (ip22_is_gr2(addr))
tmp = 0x7f;
else {
if (!ip22_gio_id(addr, &tmp)) {
/*
* no GIO signature at start address of slot, but
* Newport doesn't have one, so let's check usea
* status register
*/
if (ip22_gio_id(addr + NEWPORT_USTATUS_OFFS, &tmp))
tmp = 0x7e;
else
tmp = 0;
}
}
if (tmp) {
id = GIO_ID(tmp);
if (tmp & GIO_32BIT_ID) {
if (tmp & GIO_64BIT_IFACE)
ip22_gio_set_64bit(slotno);
}
for (i = 0; i < ARRAY_SIZE(gio_name_table); i++) {
if (id == gio_name_table[i].id) {
name = gio_name_table[i].name;
break;
}
}
printk(KERN_INFO "GIO: slot %d : %s (id %x)\n",
slotno, name, id);
gio_dev = kzalloc(sizeof *gio_dev, GFP_KERNEL);
gio_dev->name = name;
gio_dev->slotno = slotno;
gio_dev->id.id = id;
gio_dev->resource.start = addr;
gio_dev->resource.end = addr + 0x3fffff;
gio_dev->resource.flags = IORESOURCE_MEM;
dev_set_name(&gio_dev->dev, "%d", slotno);
gio_device_register(gio_dev);
} else
printk(KERN_INFO "GIO: slot %d : Empty\n", slotno);
}
static struct bus_type gio_bus_type = {
.name = "gio",
.dev_attrs = gio_dev_attrs,
.match = gio_bus_match,
.probe = gio_device_probe,
.remove = gio_device_remove,
.suspend = gio_device_suspend,
.resume = gio_device_resume,
.shutdown = gio_device_shutdown,
.uevent = gio_device_uevent,
};
static struct resource gio_bus_resource = {
.start = GIO_SLOT_GFX_BASE,
.end = GIO_SLOT_GFX_BASE + 0x9fffff,
.name = "GIO Bus",
.flags = IORESOURCE_MEM,
};
int __init ip22_gio_init(void)
{
unsigned int pbdma __maybe_unused;
int ret;
ret = device_register(&gio_bus);
if (ret)
return ret;
ret = bus_register(&gio_bus_type);
if (!ret) {
request_resource(&iomem_resource, &gio_bus_resource);
printk(KERN_INFO "GIO: Probing bus...\n");
if (ip22_is_fullhouse() ||
!get_dbe(pbdma, (unsigned int *)&hpc3c1->pbdma[1])) {
/* Indigo2 and ChallengeS */
ip22_check_gio(0, GIO_SLOT_GFX_BASE);
ip22_check_gio(1, GIO_SLOT_EXP0_BASE);
} else {
/* Indy */
ip22_check_gio(0, GIO_SLOT_GFX_BASE);
ip22_check_gio(1, GIO_SLOT_EXP0_BASE);
ip22_check_gio(2, GIO_SLOT_EXP1_BASE);
}
} else
device_unregister(&gio_bus);
return ret;
}
subsys_initcall(ip22_gio_init);

View File

@@ -0,0 +1,63 @@
/*
* ip22-hpc.c: Routines for generic manipulation of the HPC controllers.
*
* Copyright (C) 1996 David S. Miller (davem@davemloft.net)
* Copyright (C) 1998 Ralf Baechle
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
#include <asm/io.h>
#include <asm/sgi/hpc3.h>
#include <asm/sgi/ioc.h>
#include <asm/sgi/ip22.h>
struct hpc3_regs *hpc3c0, *hpc3c1;
EXPORT_SYMBOL(hpc3c0);
EXPORT_SYMBOL(hpc3c1);
struct sgioc_regs *sgioc;
EXPORT_SYMBOL(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)
{
/* ioremap can't fail */
hpc3c0 = (struct hpc3_regs *)
ioremap(HPC3_CHIP0_BASE, sizeof(struct hpc3_regs));
hpc3c1 = (struct hpc3_regs *)
ioremap(HPC3_CHIP1_BASE, sizeof(struct hpc3_regs));
/* 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;
}

View File

@@ -0,0 +1,333 @@
/*
* ip22-int.c: Routines for generic manipulation of the INT[23] ASIC
* found on INDY and Indigo2 workstations.
*
* Copyright (C) 1996 David S. Miller (davem@davemloft.net)
* Copyright (C) 1997, 1998 Ralf Baechle (ralf@gnu.org)
* Copyright (C) 1999 Andrew R. Baker (andrewb@uab.edu)
* - Indigo2 changes
* - Interrupt handling fixes
* Copyright (C) 2001, 2003 Ladislav Michl (ladis@linux-mips.org)
*/
#include <linux/types.h>
#include <linux/init.h>
#include <linux/kernel_stat.h>
#include <linux/interrupt.h>
#include <linux/ftrace.h>
#include <asm/irq_cpu.h>
#include <asm/sgi/hpc3.h>
#include <asm/sgi/ip22.h>
/* So far nothing hangs here */
#undef USE_LIO3_IRQ
struct sgint_regs *sgint;
static char lc0msk_to_irqnr[256];
static char lc1msk_to_irqnr[256];
static char lc2msk_to_irqnr[256];
static char lc3msk_to_irqnr[256];
extern int ip22_eisa_init(void);
static void enable_local0_irq(struct irq_data *d)
{
/* don't allow mappable interrupt to be enabled from setup_irq,
* we have our own way to do so */
if (d->irq != SGI_MAP_0_IRQ)
sgint->imask0 |= (1 << (d->irq - SGINT_LOCAL0));
}
static void disable_local0_irq(struct irq_data *d)
{
sgint->imask0 &= ~(1 << (d->irq - SGINT_LOCAL0));
}
static struct irq_chip ip22_local0_irq_type = {
.name = "IP22 local 0",
.irq_mask = disable_local0_irq,
.irq_unmask = enable_local0_irq,
};
static void enable_local1_irq(struct irq_data *d)
{
/* don't allow mappable interrupt to be enabled from setup_irq,
* we have our own way to do so */
if (d->irq != SGI_MAP_1_IRQ)
sgint->imask1 |= (1 << (d->irq - SGINT_LOCAL1));
}
static void disable_local1_irq(struct irq_data *d)
{
sgint->imask1 &= ~(1 << (d->irq - SGINT_LOCAL1));
}
static struct irq_chip ip22_local1_irq_type = {
.name = "IP22 local 1",
.irq_mask = disable_local1_irq,
.irq_unmask = enable_local1_irq,
};
static void enable_local2_irq(struct irq_data *d)
{
sgint->imask0 |= (1 << (SGI_MAP_0_IRQ - SGINT_LOCAL0));
sgint->cmeimask0 |= (1 << (d->irq - SGINT_LOCAL2));
}
static void disable_local2_irq(struct irq_data *d)
{
sgint->cmeimask0 &= ~(1 << (d->irq - SGINT_LOCAL2));
if (!sgint->cmeimask0)
sgint->imask0 &= ~(1 << (SGI_MAP_0_IRQ - SGINT_LOCAL0));
}
static struct irq_chip ip22_local2_irq_type = {
.name = "IP22 local 2",
.irq_mask = disable_local2_irq,
.irq_unmask = enable_local2_irq,
};
static void enable_local3_irq(struct irq_data *d)
{
sgint->imask1 |= (1 << (SGI_MAP_1_IRQ - SGINT_LOCAL1));
sgint->cmeimask1 |= (1 << (d->irq - SGINT_LOCAL3));
}
static void disable_local3_irq(struct irq_data *d)
{
sgint->cmeimask1 &= ~(1 << (d->irq - SGINT_LOCAL3));
if (!sgint->cmeimask1)
sgint->imask1 &= ~(1 << (SGI_MAP_1_IRQ - SGINT_LOCAL1));
}
static struct irq_chip ip22_local3_irq_type = {
.name = "IP22 local 3",
.irq_mask = disable_local3_irq,
.irq_unmask = enable_local3_irq,
};
static void indy_local0_irqdispatch(void)
{
u8 mask = sgint->istat0 & sgint->imask0;
u8 mask2;
int irq;
if (mask & SGINT_ISTAT0_LIO2) {
mask2 = sgint->vmeistat & sgint->cmeimask0;
irq = lc2msk_to_irqnr[mask2];
} else
irq = lc0msk_to_irqnr[mask];
/* if irq == 0, then the interrupt has already been cleared */
if (irq)
do_IRQ(irq);
}
static void indy_local1_irqdispatch(void)
{
u8 mask = sgint->istat1 & sgint->imask1;
u8 mask2;
int irq;
if (mask & SGINT_ISTAT1_LIO3) {
mask2 = sgint->vmeistat & sgint->cmeimask1;
irq = lc3msk_to_irqnr[mask2];
} else
irq = lc1msk_to_irqnr[mask];
/* if irq == 0, then the interrupt has already been cleared */
if (irq)
do_IRQ(irq);
}
extern void ip22_be_interrupt(int irq);
static void __irq_entry indy_buserror_irq(void)
{
int irq = SGI_BUSERR_IRQ;
irq_enter();
kstat_incr_irqs_this_cpu(irq, irq_to_desc(irq));
ip22_be_interrupt(irq);
irq_exit();
}
static struct irqaction local0_cascade = {
.handler = no_action,
.flags = IRQF_NO_THREAD,
.name = "local0 cascade",
};
static struct irqaction local1_cascade = {
.handler = no_action,
.flags = IRQF_NO_THREAD,
.name = "local1 cascade",
};
static struct irqaction buserr = {
.handler = no_action,
.flags = IRQF_NO_THREAD,
.name = "Bus Error",
};
static struct irqaction map0_cascade = {
.handler = no_action,
.flags = IRQF_NO_THREAD,
.name = "mapable0 cascade",
};
#ifdef USE_LIO3_IRQ
static struct irqaction map1_cascade = {
.handler = no_action,
.flags = IRQF_NO_THREAD,
.name = "mapable1 cascade",
};
#define SGI_INTERRUPTS SGINT_END
#else
#define SGI_INTERRUPTS SGINT_LOCAL3
#endif
extern void indy_8254timer_irq(void);
/*
* 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.
*/
asmlinkage void plat_irq_dispatch(void)
{
unsigned int pending = read_c0_status() & read_c0_cause();
/*
* First we check for r4k counter/timer IRQ.
*/
if (pending & CAUSEF_IP7)
do_IRQ(SGI_TIMER_IRQ);
else if (pending & CAUSEF_IP2)
indy_local0_irqdispatch();
else if (pending & CAUSEF_IP3)
indy_local1_irqdispatch();
else if (pending & CAUSEF_IP6)
indy_buserror_irq();
else if (pending & (CAUSEF_IP4 | CAUSEF_IP5))
indy_8254timer_irq();
}
void __init arch_init_irq(void)
{
int i;
/* Init local mask --> irq tables. */
for (i = 0; i < 256; i++) {
if (i & 0x80) {
lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 7;
lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 7;
lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 7;
lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 7;
} else if (i & 0x40) {
lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 6;
lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 6;
lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 6;
lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 6;
} else if (i & 0x20) {
lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 5;
lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 5;
lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 5;
lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 5;
} else if (i & 0x10) {
lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 4;
lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 4;
lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 4;
lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 4;
} else if (i & 0x08) {
lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 3;
lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 3;
lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 3;
lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 3;
} else if (i & 0x04) {
lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 2;
lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 2;
lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 2;
lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 2;
} else if (i & 0x02) {
lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 1;
lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 1;
lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 1;
lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 1;
} else if (i & 0x01) {
lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 0;
lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 0;
lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 0;
lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 0;
} else {
lc0msk_to_irqnr[i] = 0;
lc1msk_to_irqnr[i] = 0;
lc2msk_to_irqnr[i] = 0;
lc3msk_to_irqnr[i] = 0;
}
}
/* Mask out all interrupts. */
sgint->imask0 = 0;
sgint->imask1 = 0;
sgint->cmeimask0 = 0;
sgint->cmeimask1 = 0;
/* init CPU irqs */
mips_cpu_irq_init();
for (i = SGINT_LOCAL0; i < SGI_INTERRUPTS; i++) {
struct irq_chip *handler;
if (i < SGINT_LOCAL1)
handler = &ip22_local0_irq_type;
else if (i < SGINT_LOCAL2)
handler = &ip22_local1_irq_type;
else if (i < SGINT_LOCAL3)
handler = &ip22_local2_irq_type;
else
handler = &ip22_local3_irq_type;
irq_set_chip_and_handler(i, handler, handle_level_irq);
}
/* vector handler. this register the IRQ as non-sharable */
setup_irq(SGI_LOCAL_0_IRQ, &local0_cascade);
setup_irq(SGI_LOCAL_1_IRQ, &local1_cascade);
setup_irq(SGI_BUSERR_IRQ, &buserr);
/* cascade in cascade. i love Indy ;-) */
setup_irq(SGI_MAP_0_IRQ, &map0_cascade);
#ifdef USE_LIO3_IRQ
setup_irq(SGI_MAP_1_IRQ, &map1_cascade);
#endif
#ifdef CONFIG_EISA
if (ip22_is_fullhouse()) /* Only Indigo-2 has EISA stuff */
ip22_eisa_init();
#endif
}

View File

@@ -0,0 +1,237 @@
/*
* ip22-mc.c: Routines for manipulating SGI Memory Controller.
*
* Copyright (C) 1996 David S. Miller (davem@davemloft.net)
* Copyright (C) 1999 Andrew R. Baker (andrewb@uab.edu) - Indigo2 changes
* Copyright (C) 2003 Ladislav Michl (ladis@linux-mips.org)
* Copyright (C) 2004 Peter Fuerst (pf@net.alphadv.de) - IP28
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <asm/io.h>
#include <asm/bootinfo.h>
#include <asm/sgialib.h>
#include <asm/sgi/mc.h>
#include <asm/sgi/hpc3.h>
#include <asm/sgi/ip22.h>
struct sgimc_regs *sgimc;
EXPORT_SYMBOL(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 __init 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;
/* ioremap can't fail */
sgimc = (struct sgimc_regs *)
ioremap(SGIMC_BASE, sizeof(struct sgimc_regs));
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.
*/
/* don't touch parity settings for IP28 */
tmp = sgimc->cpuctrl0;
#ifndef CONFIG_SGI_IP28
tmp |= SGIMC_CCTRL0_EPERRGIO | SGIMC_CCTRL0_EPERRMEM;
#endif
tmp |= 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 & SGIMC_GIOPAR_GFX64; /* keep gfx 64bit settings */
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 */
}
} 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)
{
#ifdef CONFIG_SGI_IP28
u32 mconfig1;
unsigned long flags;
spinlock_t lock;
/*
* because ARCS accesses memory uncached we wait until ARCS
* isn't needed any longer, before we switch from slow to
* normal mode
*/
spin_lock_irqsave(&lock, flags);
mconfig1 = sgimc->mconfig1;
/* map ECC register */
sgimc->mconfig1 = (mconfig1 & 0xffff0000) | 0x2060;
iob();
/* switch to normal mode */
*(unsigned long *)PHYS_TO_XKSEG_UNCACHED(0x60000000) = 0;
iob();
/* reduce WR_COL */
sgimc->cmacc = (sgimc->cmacc & ~0xf) | 4;
iob();
/* restore old config */
sgimc->mconfig1 = mconfig1;
iob();
spin_unlock_irqrestore(&lock, flags);
#endif
}

View File

@@ -0,0 +1,121 @@
/*
* ip22-nvram.c: NVRAM and serial EEPROM handling.
*
* Copyright (C) 2003 Ladislav Michl (ladis@linux-mips.org)
*/
#include <linux/module.h>
#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 these functions early... */
#define delay() ({ \
int x; \
for (x=0; x<100000; x++) __asm__ __volatile__(""); })
#define eeprom_cs_on(ptr) ({ \
__raw_writel(__raw_readl(ptr) & ~EEPROM_DATO, ptr); \
__raw_writel(__raw_readl(ptr) & ~EEPROM_ECLK, ptr); \
__raw_writel(__raw_readl(ptr) & ~EEPROM_EPROT, ptr); \
delay(); \
__raw_writel(__raw_readl(ptr) | EEPROM_CSEL, ptr); \
__raw_writel(__raw_readl(ptr) | EEPROM_ECLK, ptr); })
#define eeprom_cs_off(ptr) ({ \
__raw_writel(__raw_readl(ptr) & ~EEPROM_ECLK, ptr); \
__raw_writel(__raw_readl(ptr) & ~EEPROM_CSEL, ptr); \
__raw_writel(__raw_readl(ptr) | EEPROM_EPROT, ptr); \
__raw_writel(__raw_readl(ptr) | EEPROM_ECLK, ptr); })
#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(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 */
__raw_writel(__raw_readl(ctrl) | EEPROM_DATO, ctrl);
else
__raw_writel(__raw_readl(ctrl) & ~EEPROM_DATO, ctrl);
__raw_writel(__raw_readl(ctrl) & ~EEPROM_ECLK, ctrl);
delay();
__raw_writel(__raw_readl(ctrl) | EEPROM_ECLK, ctrl);
delay();
ser_cmd <<= 1;
}
/* see data sheet timing diagram */
__raw_writel(__raw_readl(ctrl) & ~EEPROM_DATO, ctrl);
}
unsigned short ip22_eeprom_read(unsigned int *ctrl, int reg)
{
unsigned short res = 0;
int i;
__raw_writel(__raw_readl(ctrl) & ~EEPROM_EPROT, ctrl);
eeprom_cs_on(ctrl);
eeprom_cmd(ctrl, EEPROM_READ, reg);
/* clock the data ouf of serial mem */
for (i = 0; i < 16; i++) {
__raw_writel(__raw_readl(ctrl) & ~EEPROM_ECLK, ctrl);
delay();
__raw_writel(__raw_readl(ctrl) | EEPROM_ECLK, ctrl);
delay();
res <<= 1;
if (__raw_readl(ctrl) & EEPROM_DATI)
res |= 1;
}
eeprom_cs_off(ctrl);
return res;
}
EXPORT_SYMBOL(ip22_eeprom_read);
/*
* 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);
}
}
EXPORT_SYMBOL(ip22_nvram_read);

View File

@@ -0,0 +1,209 @@
#include <linux/init.h>
#include <linux/if_ether.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <asm/paccess.h>
#include <asm/sgi/ip22.h>
#include <asm/sgi/hpc3.h>
#include <asm/sgi/mc.h>
#include <asm/sgi/seeq.h>
#include <asm/sgi/wd.h>
static struct resource sgiwd93_0_resources[] = {
{
.name = "eth0 irq",
.start = SGI_WD93_0_IRQ,
.end = SGI_WD93_0_IRQ,
.flags = IORESOURCE_IRQ
}
};
static struct sgiwd93_platform_data sgiwd93_0_pd = {
.unit = 0,
.irq = SGI_WD93_0_IRQ,
};
static struct platform_device sgiwd93_0_device = {
.name = "sgiwd93",
.id = 0,
.num_resources = ARRAY_SIZE(sgiwd93_0_resources),
.resource = sgiwd93_0_resources,
.dev = {
.platform_data = &sgiwd93_0_pd,
},
};
static struct resource sgiwd93_1_resources[] = {
{
.name = "eth0 irq",
.start = SGI_WD93_1_IRQ,
.end = SGI_WD93_1_IRQ,
.flags = IORESOURCE_IRQ
}
};
static struct sgiwd93_platform_data sgiwd93_1_pd = {
.unit = 1,
.irq = SGI_WD93_1_IRQ,
};
static struct platform_device sgiwd93_1_device = {
.name = "sgiwd93",
.id = 1,
.num_resources = ARRAY_SIZE(sgiwd93_1_resources),
.resource = sgiwd93_1_resources,
.dev = {
.platform_data = &sgiwd93_1_pd,
},
};
/*
* Create a platform device for the GPI port that receives the
* image data from the embedded camera.
*/
static int __init sgiwd93_devinit(void)
{
int res;
sgiwd93_0_pd.hregs = &hpc3c0->scsi_chan0;
sgiwd93_0_pd.wdregs = (unsigned char *) hpc3c0->scsi0_ext;
res = platform_device_register(&sgiwd93_0_device);
if (res)
return res;
if (!ip22_is_fullhouse())
return 0;
sgiwd93_1_pd.hregs = &hpc3c0->scsi_chan1;
sgiwd93_1_pd.wdregs = (unsigned char *) hpc3c0->scsi1_ext;
return platform_device_register(&sgiwd93_1_device);
}
device_initcall(sgiwd93_devinit);
static struct resource sgiseeq_0_resources[] = {
{
.name = "eth0 irq",
.start = SGI_ENET_IRQ,
.end = SGI_ENET_IRQ,
.flags = IORESOURCE_IRQ
}
};
static struct sgiseeq_platform_data eth0_pd;
static struct platform_device eth0_device = {
.name = "sgiseeq",
.id = 0,
.num_resources = ARRAY_SIZE(sgiseeq_0_resources),
.resource = sgiseeq_0_resources,
.dev = {
.platform_data = &eth0_pd,
},
};
static struct resource sgiseeq_1_resources[] = {
{
.name = "eth1 irq",
.start = SGI_GIO_0_IRQ,
.end = SGI_GIO_0_IRQ,
.flags = IORESOURCE_IRQ
}
};
static struct sgiseeq_platform_data eth1_pd;
static struct platform_device eth1_device = {
.name = "sgiseeq",
.id = 1,
.num_resources = ARRAY_SIZE(sgiseeq_1_resources),
.resource = sgiseeq_1_resources,
.dev = {
.platform_data = &eth1_pd,
},
};
/*
* Create a platform device for the GPI port that receives the
* image data from the embedded camera.
*/
static int __init sgiseeq_devinit(void)
{
unsigned int pbdma __maybe_unused;
int res, i;
eth0_pd.hpc = hpc3c0;
eth0_pd.irq = SGI_ENET_IRQ;
#define EADDR_NVOFS 250
for (i = 0; i < 3; i++) {
unsigned short tmp = ip22_nvram_read(EADDR_NVOFS / 2 + i);
eth0_pd.mac[2 * i] = tmp >> 8;
eth0_pd.mac[2 * i + 1] = tmp & 0xff;
}
res = platform_device_register(&eth0_device);
if (res)
return res;
/* Second HPC is missing? */
if (ip22_is_fullhouse() ||
get_dbe(pbdma, (unsigned int *)&hpc3c1->pbdma[1]))
return 0;
sgimc->giopar |= SGIMC_GIOPAR_MASTEREXP1 | SGIMC_GIOPAR_EXP164 |
SGIMC_GIOPAR_HPC264;
hpc3c1->pbus_piocfg[0][0] = 0x3ffff;
/* interrupt/config register on Challenge S Mezz board */
hpc3c1->pbus_extregs[0][0] = 0x30;
eth1_pd.hpc = hpc3c1;
eth1_pd.irq = SGI_GIO_0_IRQ;
#define EADDR_NVOFS 250
for (i = 0; i < 3; i++) {
unsigned short tmp = ip22_eeprom_read(&hpc3c1->eeprom,
EADDR_NVOFS / 2 + i);
eth1_pd.mac[2 * i] = tmp >> 8;
eth1_pd.mac[2 * i + 1] = tmp & 0xff;
}
return platform_device_register(&eth1_device);
}
device_initcall(sgiseeq_devinit);
static int __init sgi_hal2_devinit(void)
{
return IS_ERR(platform_device_register_simple("sgihal2", 0, NULL, 0));
}
device_initcall(sgi_hal2_devinit);
static int __init sgi_button_devinit(void)
{
if (ip22_is_fullhouse())
return 0; /* full house has no volume buttons */
return IS_ERR(platform_device_register_simple("sgibtns", -1, NULL, 0));
}
device_initcall(sgi_button_devinit);
static int __init sgi_ds1286_devinit(void)
{
struct resource res;
memset(&res, 0, sizeof(res));
res.start = HPC3_CHIP0_BASE + offsetof(struct hpc3_regs, rtcregs);
res.end = res.start + sizeof(hpc3c0->rtcregs) - 1;
res.flags = IORESOURCE_MEM;
return IS_ERR(platform_device_register_simple("rtc-ds1286", -1,
&res, 1));
}
device_initcall(sgi_ds1286_devinit);

View File

@@ -0,0 +1,206 @@
/*
* 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.
*
* Copyright (C) 1997, 1998, 2001, 03, 05, 06 by Ralf Baechle
*/
#include <linux/linkage.h>
#include <linux/init.h>
#include <linux/ds1286.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/notifier.h>
#include <linux/pm.h>
#include <linux/timer.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/reboot.h>
#include <asm/sgialib.h>
#include <asm/sgi/ioc.h>
#include <asm/sgi/hpc3.h>
#include <asm/sgi/mc.h>
#include <asm/sgi/ip22.h>
/*
* Just powerdown if init hasn't done after POWERDOWN_TIMEOUT seconds.
* 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.
*/
#define POWERDOWN_TIMEOUT 120
/*
* Blink frequency during reboot grace period and when panicked.
*/
#define POWERDOWN_FREQ (HZ / 4)
#define PANIC_FREQ (HZ / 8)
static struct timer_list power_timer, blink_timer, debounce_timer;
#define MACHINE_PANICED 1
#define MACHINE_SHUTTING_DOWN 2
static int machine_state;
static void __noreturn sgi_machine_power_off(void)
{
unsigned int tmp;
local_irq_disable();
/* Disable watchdog */
tmp = hpc3c0->rtcregs[RTC_CMD] & 0xff;
hpc3c0->rtcregs[RTC_CMD] = tmp | RTC_WAM;
hpc3c0->rtcregs[RTC_WSEC] = 0;
hpc3c0->rtcregs[RTC_WHSEC] = 0;
while (1) {
sgioc->panel = ~SGIOC_PANEL_POWERON;
/* Good bye cruel world ... */
/* If we're still running, we probably got sent an alarm
interrupt. Read the flag to clear it. */
tmp = hpc3c0->rtcregs[RTC_HOURS_ALARM];
}
}
static void __noreturn sgi_machine_restart(char *command)
{
if (machine_state & MACHINE_SHUTTING_DOWN)
sgi_machine_power_off();
sgimc->cpuctrl0 |= SGIMC_CCTRL0_SYSINIT;
while (1);
}
static void __noreturn sgi_machine_halt(void)
{
if (machine_state & MACHINE_SHUTTING_DOWN)
sgi_machine_power_off();
ArcEnterInteractiveMode();
}
static void power_timeout(unsigned long data)
{
sgi_machine_power_off();
}
static void blink_timeout(unsigned long data)
{
/* XXX fix this for fullhouse */
sgi_ioc_reset ^= (SGIOC_RESET_LC0OFF|SGIOC_RESET_LC1OFF);
sgioc->reset = sgi_ioc_reset;
mod_timer(&blink_timer, jiffies + data);
}
static void debounce(unsigned long data)
{
del_timer(&debounce_timer);
if (sgint->istat1 & SGINT_ISTAT1_PWR) {
/* Interrupt still being sent. */
debounce_timer.expires = jiffies + (HZ / 20); /* 0.05s */
add_timer(&debounce_timer);
sgioc->panel = SGIOC_PANEL_POWERON | SGIOC_PANEL_POWERINTR |
SGIOC_PANEL_VOLDNINTR | SGIOC_PANEL_VOLDNHOLD |
SGIOC_PANEL_VOLUPINTR | SGIOC_PANEL_VOLUPHOLD;
return;
}
if (machine_state & MACHINE_PANICED)
sgimc->cpuctrl0 |= SGIMC_CCTRL0_SYSINIT;
enable_irq(SGI_PANEL_IRQ);
}
static inline void power_button(void)
{
if (machine_state & MACHINE_PANICED)
return;
if ((machine_state & MACHINE_SHUTTING_DOWN) ||
kill_cad_pid(SIGINT, 1)) {
/* No init process or button pressed twice. */
sgi_machine_power_off();
}
machine_state |= MACHINE_SHUTTING_DOWN;
blink_timer.data = POWERDOWN_FREQ;
blink_timeout(POWERDOWN_FREQ);
init_timer(&power_timer);
power_timer.function = power_timeout;
power_timer.expires = jiffies + POWERDOWN_TIMEOUT * HZ;
add_timer(&power_timer);
}
static irqreturn_t panel_int(int irq, void *dev_id)
{
unsigned int buttons;
buttons = sgioc->panel;
sgioc->panel = SGIOC_PANEL_POWERON | SGIOC_PANEL_POWERINTR;
if (sgint->istat1 & SGINT_ISTAT1_PWR) {
/* Wait until interrupt goes away */
disable_irq_nosync(SGI_PANEL_IRQ);
init_timer(&debounce_timer);
debounce_timer.function = debounce;
debounce_timer.expires = jiffies + 5;
add_timer(&debounce_timer);
}
/* 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 (!(buttons & SGIOC_PANEL_POWERINTR))
power_button();
return IRQ_HANDLED;
}
static int panic_event(struct notifier_block *this, unsigned long event,
void *ptr)
{
if (machine_state & MACHINE_PANICED)
return NOTIFY_DONE;
machine_state |= MACHINE_PANICED;
blink_timer.data = PANIC_FREQ;
blink_timeout(PANIC_FREQ);
return NOTIFY_DONE;
}
static struct notifier_block panic_block = {
.notifier_call = panic_event,
};
static int __init reboot_setup(void)
{
int res;
_machine_restart = sgi_machine_restart;
_machine_halt = sgi_machine_halt;
pm_power_off = sgi_machine_power_off;
res = request_irq(SGI_PANEL_IRQ, panel_int, 0, "Front Panel", NULL);
if (res) {
printk(KERN_ERR "Allocation of front panel IRQ failed\n");
return res;
}
init_timer(&blink_timer);
blink_timer.function = blink_timeout;
atomic_notifier_chain_register(&panic_notifier_list, &panic_block);
return 0;
}
subsys_initcall(reboot_setup);

View File

@@ -0,0 +1,78 @@
/*
* ip22-setup.c: SGI specific setup, including init of the feature struct.
*
* Copyright (C) 1996 David S. Miller (davem@davemloft.net)
* Copyright (C) 1997, 1998 Ralf Baechle (ralf@gnu.org)
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/kdev_t.h>
#include <linux/types.h>
#include <linux/module.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/time.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>
extern void ip22_be_init(void) __init;
void __init plat_mem_setup(void)
{
char *ctype;
char *cserial;
board_be_init = ip22_be_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
/* Set EISA IO port base for Indigo2
* ioremap cannot fail */
set_io_port_base((unsigned long)ioremap(0x00080000,
0x1fffffff - 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.
*
* Need to check if the case is 'g' but no keyboard:
* (ConsoleIn/Out = serial)
*/
ctype = ArcGetEnvironmentVariable("console");
cserial = ArcGetEnvironmentVariable("ConsoleOut");
if ((ctype && *ctype == 'd') || (cserial && *cserial == 's')) {
static char options[8] __initdata;
char *baud = ArcGetEnvironmentVariable("dbaud");
if (baud)
strcpy(options, baud);
add_preferred_console("ttyS", *(ctype + 1) == '2' ? 1 : 0,
baud ? options : NULL);
} else if (!ctype || *ctype != 'g') {
/* Use ARC if we don't want serial ('d') or graphics ('g'). */
prom_flags |= PROM_FLAG_USE_AS_CONSOLE;
add_preferred_console("arc", 0, NULL);
}
}

View File

@@ -0,0 +1,131 @@
/*
* 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
* Copyright (C) 2003, 06 Ralf Baechle (ralf@linux-mips.org)
*/
#include <linux/bcd.h>
#include <linux/i8253.h>
#include <linux/init.h>
#include <linux/irq.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/kernel_stat.h>
#include <linux/time.h>
#include <linux/ftrace.h>
#include <asm/cpu.h>
#include <asm/mipsregs.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/time.h>
#include <asm/sgialib.h>
#include <asm/sgi/ioc.h>
#include <asm/sgi/hpc3.h>
#include <asm/sgi/ip22.h>
static unsigned long dosample(void)
{
u32 ct0, ct1;
u8 msb;
/* 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 {
writeb(SGINT_TCWORD_CNT2 | SGINT_TCWORD_CLAT, &sgint->tcword);
(void) readb(&sgint->tcnt2);
msb = readb(&sgint->tcnt2);
ct1 = read_c0_count();
} while (msb);
/* Stop the counter. */
writeb(SGINT_TCWORD_CNT2 | SGINT_TCWORD_CALL | SGINT_TCWORD_MSWST,
&sgint->tcword);
/*
* 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 / HZ / 2).
*/
return (ct1 - ct0) / (500000/HZ) * (500000/HZ);
}
/*
* Here we need to calibrate the cycle counter to at least be close.
*/
__init void plat_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.%04d MHz CPU]\n", (int) r4k_tick,
(int) (r4k_tick / (500000 / HZ)),
(int) (r4k_tick % (500000 / HZ)));
mips_hpt_frequency = r4k_tick * HZ;
if (ip22_is_fullhouse())
setup_pit_timer();
}
/* Generic SGI handler for (spurious) 8254 interrupts */
void __irq_entry indy_8254timer_irq(void)
{
int irq = SGI_8254_0_IRQ;
ULONG cnt;
char c;
irq_enter();
kstat_incr_irqs_this_cpu(irq, irq_to_desc(irq));
printk(KERN_ALERT "Oops, got 8254 interrupt.\n");
ArcRead(0, &c, 1, &cnt);
ArcEnterInteractiveMode();
irq_exit();
}

View File

@@ -0,0 +1,501 @@
/*
* ip28-berr.c: Bus error handling.
*
* Copyright (C) 2002, 2003 Ladislav Michl (ladis@linux-mips.org)
* Copyright (C) 2005 Peter Fuerst (pf@net.alphadv.de) - IP28
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/seq_file.h>
#include <asm/addrspace.h>
#include <asm/traps.h>
#include <asm/branch.h>
#include <asm/irq_regs.h>
#include <asm/sgi/mc.h>
#include <asm/sgi/hpc3.h>
#include <asm/sgi/ioc.h>
#include <asm/sgi/ip22.h>
#include <asm/r4kcache.h>
#include <asm/uaccess.h>
#include <asm/bootinfo.h>
static unsigned int count_be_is_fixup;
static unsigned int count_be_handler;
static unsigned int count_be_interrupt;
static int debug_be_interrupt;
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 unsigned int extio_stat;
static unsigned int hpc3_berr_stat; /* Bus error interrupt status */
struct hpc3_stat {
unsigned long addr;
unsigned int ctrl;
unsigned int cbp;
unsigned int ndptr;
};
static struct {
struct hpc3_stat pbdma[8];
struct hpc3_stat scsi[2];
struct hpc3_stat ethrx, ethtx;
} hpc3;
static struct {
unsigned long err_addr;
struct {
u32 lo;
u32 hi;
} tags[1][2], tagd[4][2], tagi[4][2]; /* Way 0/1 */
} cache_tags;
static inline void save_cache_tags(unsigned busaddr)
{
unsigned long addr = CAC_BASE | busaddr;
int i;
cache_tags.err_addr = addr;
/*
* Starting with a bus-address, save secondary cache (indexed by
* PA[23..18:7..6]) tags first.
*/
addr &= ~1L;
#define tag cache_tags.tags[0]
cache_op(Index_Load_Tag_S, addr);
tag[0].lo = read_c0_taglo(); /* PA[35:18], VA[13:12] */
tag[0].hi = read_c0_taghi(); /* PA[39:36] */
cache_op(Index_Load_Tag_S, addr | 1L);
tag[1].lo = read_c0_taglo(); /* PA[35:18], VA[13:12] */
tag[1].hi = read_c0_taghi(); /* PA[39:36] */
#undef tag
/*
* Save all primary data cache (indexed by VA[13:5]) tags which
* might fit to this bus-address, knowing that VA[11:0] == PA[11:0].
* Saving all tags and evaluating them later is easier and safer
* than relying on VA[13:12] from the secondary cache tags to pick
* matching primary tags here already.
*/
addr &= (0xffL << 56) | ((1 << 12) - 1);
#define tag cache_tags.tagd[i]
for (i = 0; i < 4; ++i, addr += (1 << 12)) {
cache_op(Index_Load_Tag_D, addr);
tag[0].lo = read_c0_taglo(); /* PA[35:12] */
tag[0].hi = read_c0_taghi(); /* PA[39:36] */
cache_op(Index_Load_Tag_D, addr | 1L);
tag[1].lo = read_c0_taglo(); /* PA[35:12] */
tag[1].hi = read_c0_taghi(); /* PA[39:36] */
}
#undef tag
/*
* Save primary instruction cache (indexed by VA[13:6]) tags
* the same way.
*/
addr &= (0xffL << 56) | ((1 << 12) - 1);
#define tag cache_tags.tagi[i]
for (i = 0; i < 4; ++i, addr += (1 << 12)) {
cache_op(Index_Load_Tag_I, addr);
tag[0].lo = read_c0_taglo(); /* PA[35:12] */
tag[0].hi = read_c0_taghi(); /* PA[39:36] */
cache_op(Index_Load_Tag_I, addr | 1L);
tag[1].lo = read_c0_taglo(); /* PA[35:12] */
tag[1].hi = read_c0_taghi(); /* PA[39:36] */
}
#undef tag
}
#define GIO_ERRMASK 0xff00
#define CPU_ERRMASK 0x3f00
static void save_and_clear_buserr(void)
{
int i;
/* save status registers */
cpu_err_addr = sgimc->cerr;
cpu_err_stat = sgimc->cstat;
gio_err_addr = sgimc->gerr;
gio_err_stat = sgimc->gstat;
extio_stat = sgioc->extio;
hpc3_berr_stat = hpc3c0->bestat;
hpc3.scsi[0].addr = (unsigned long)&hpc3c0->scsi_chan0;
hpc3.scsi[0].ctrl = hpc3c0->scsi_chan0.ctrl; /* HPC3_SCTRL_ACTIVE ? */
hpc3.scsi[0].cbp = hpc3c0->scsi_chan0.cbptr;
hpc3.scsi[0].ndptr = hpc3c0->scsi_chan0.ndptr;
hpc3.scsi[1].addr = (unsigned long)&hpc3c0->scsi_chan1;
hpc3.scsi[1].ctrl = hpc3c0->scsi_chan1.ctrl; /* HPC3_SCTRL_ACTIVE ? */
hpc3.scsi[1].cbp = hpc3c0->scsi_chan1.cbptr;
hpc3.scsi[1].ndptr = hpc3c0->scsi_chan1.ndptr;
hpc3.ethrx.addr = (unsigned long)&hpc3c0->ethregs.rx_cbptr;
hpc3.ethrx.ctrl = hpc3c0->ethregs.rx_ctrl; /* HPC3_ERXCTRL_ACTIVE ? */
hpc3.ethrx.cbp = hpc3c0->ethregs.rx_cbptr;
hpc3.ethrx.ndptr = hpc3c0->ethregs.rx_ndptr;
hpc3.ethtx.addr = (unsigned long)&hpc3c0->ethregs.tx_cbptr;
hpc3.ethtx.ctrl = hpc3c0->ethregs.tx_ctrl; /* HPC3_ETXCTRL_ACTIVE ? */
hpc3.ethtx.cbp = hpc3c0->ethregs.tx_cbptr;
hpc3.ethtx.ndptr = hpc3c0->ethregs.tx_ndptr;
for (i = 0; i < 8; ++i) {
/* HPC3_PDMACTRL_ISACT ? */
hpc3.pbdma[i].addr = (unsigned long)&hpc3c0->pbdma[i];
hpc3.pbdma[i].ctrl = hpc3c0->pbdma[i].pbdma_ctrl;
hpc3.pbdma[i].cbp = hpc3c0->pbdma[i].pbdma_bptr;
hpc3.pbdma[i].ndptr = hpc3c0->pbdma[i].pbdma_dptr;
}
i = 0;
if (gio_err_stat & CPU_ERRMASK)
i = gio_err_addr;
if (cpu_err_stat & CPU_ERRMASK)
i = cpu_err_addr;
save_cache_tags(i);
sgimc->cstat = sgimc->gstat = 0;
}
static void print_cache_tags(void)
{
u32 scb, scw;
int i;
printk(KERN_ERR "Cache tags @ %08x:\n", (unsigned)cache_tags.err_addr);
/* PA[31:12] shifted to PTag0 (PA[35:12]) format */
scw = (cache_tags.err_addr >> 4) & 0x0fffff00;
scb = cache_tags.err_addr & ((1 << 12) - 1) & ~((1 << 5) - 1);
for (i = 0; i < 4; ++i) { /* for each possible VA[13:12] value */
if ((cache_tags.tagd[i][0].lo & 0x0fffff00) != scw &&
(cache_tags.tagd[i][1].lo & 0x0fffff00) != scw)
continue;
printk(KERN_ERR
"D: 0: %08x %08x, 1: %08x %08x (VA[13:5] %04x)\n",
cache_tags.tagd[i][0].hi, cache_tags.tagd[i][0].lo,
cache_tags.tagd[i][1].hi, cache_tags.tagd[i][1].lo,
scb | (1 << 12)*i);
}
scb = cache_tags.err_addr & ((1 << 12) - 1) & ~((1 << 6) - 1);
for (i = 0; i < 4; ++i) { /* for each possible VA[13:12] value */
if ((cache_tags.tagi[i][0].lo & 0x0fffff00) != scw &&
(cache_tags.tagi[i][1].lo & 0x0fffff00) != scw)
continue;
printk(KERN_ERR
"I: 0: %08x %08x, 1: %08x %08x (VA[13:6] %04x)\n",
cache_tags.tagi[i][0].hi, cache_tags.tagi[i][0].lo,
cache_tags.tagi[i][1].hi, cache_tags.tagi[i][1].lo,
scb | (1 << 12)*i);
}
i = read_c0_config();
scb = i & (1 << 13) ? 7:6; /* scblksize = 2^[7..6] */
scw = ((i >> 16) & 7) + 19 - 1; /* scwaysize = 2^[24..19] / 2 */
i = ((1 << scw) - 1) & ~((1 << scb) - 1);
printk(KERN_ERR "S: 0: %08x %08x, 1: %08x %08x (PA[%u:%u] %05x)\n",
cache_tags.tags[0][0].hi, cache_tags.tags[0][0].lo,
cache_tags.tags[0][1].hi, cache_tags.tags[0][1].lo,
scw-1, scb, i & (unsigned)cache_tags.err_addr);
}
static inline const char *cause_excode_text(int cause)
{
static const char *txt[32] =
{ "Interrupt",
"TLB modification",
"TLB (load or instruction fetch)",
"TLB (store)",
"Address error (load or instruction fetch)",
"Address error (store)",
"Bus error (instruction fetch)",
"Bus error (data: load or store)",
"Syscall",
"Breakpoint",
"Reserved instruction",
"Coprocessor unusable",
"Arithmetic Overflow",
"Trap",
"14",
"Floating-Point",
"16", "17", "18", "19", "20", "21", "22",
"Watch Hi/Lo",
"24", "25", "26", "27", "28", "29", "30", "31",
};
return txt[(cause & 0x7c) >> 2];
}
static void print_buserr(const struct pt_regs *regs)
{
const int field = 2 * sizeof(unsigned long);
int error = 0;
if (extio_stat & EXTIO_MC_BUSERR) {
printk(KERN_ERR "MC Bus Error\n");
error |= 1;
}
if (extio_stat & EXTIO_HPC3_BUSERR) {
printk(KERN_ERR "HPC3 Bus Error 0x%x:<id=0x%x,%s,lane=0x%x>\n",
hpc3_berr_stat,
(hpc3_berr_stat & HPC3_BESTAT_PIDMASK) >>
HPC3_BESTAT_PIDSHIFT,
(hpc3_berr_stat & HPC3_BESTAT_CTYPE) ? "PIO" : "DMA",
hpc3_berr_stat & HPC3_BESTAT_BLMASK);
error |= 2;
}
if (extio_stat & EXTIO_EISA_BUSERR) {
printk(KERN_ERR "EISA Bus Error\n");
error |= 4;
}
if (cpu_err_stat & CPU_ERRMASK) {
printk(KERN_ERR "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);
error |= 8;
}
if (gio_err_stat & GIO_ERRMASK) {
printk(KERN_ERR "GIO error 0x%x:<%s%s%s%s%s%s%s%s> @ 0x%08x\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);
error |= 16;
}
if (!error)
printk(KERN_ERR "MC: Hmm, didn't find any error condition.\n");
else {
printk(KERN_ERR "CP0: config %08x, "
"MC: cpuctrl0/1: %08x/%05x, giopar: %04x\n"
"MC: cpu/gio_memacc: %08x/%05x, memcfg0/1: %08x/%08x\n",
read_c0_config(),
sgimc->cpuctrl0, sgimc->cpuctrl0, sgimc->giopar,
sgimc->cmacc, sgimc->gmacc,
sgimc->mconfig0, sgimc->mconfig1);
print_cache_tags();
}
printk(KERN_ALERT "%s, epc == %0*lx, ra == %0*lx\n",
cause_excode_text(regs->cp0_cause),
field, regs->cp0_epc, field, regs->regs[31]);
}
/*
* Check, whether MC's (virtual) DMA address caused the bus error.
* See "Virtual DMA Specification", Draft 1.5, Feb 13 1992, SGI
*/
static int addr_is_ram(unsigned long addr, unsigned sz)
{
int i;
for (i = 0; i < boot_mem_map.nr_map; i++) {
unsigned long a = boot_mem_map.map[i].addr;
if (a <= addr && addr+sz <= a+boot_mem_map.map[i].size)
return 1;
}
return 0;
}
static int check_microtlb(u32 hi, u32 lo, unsigned long vaddr)
{
/* This is likely rather similar to correct code ;-) */
vaddr &= 0x7fffffff; /* Doc. states that top bit is ignored */
/* If tlb-entry is valid and VPN-high (bits [30:21] ?) matches... */
if ((lo & 2) && (vaddr >> 21) == ((hi<<1) >> 22)) {
u32 ctl = sgimc->dma_ctrl;
if (ctl & 1) {
unsigned int pgsz = (ctl & 2) ? 14:12; /* 16k:4k */
/* PTEIndex is VPN-low (bits [22:14]/[20:12] ?) */
unsigned long pte = (lo >> 6) << 12; /* PTEBase */
pte += 8*((vaddr >> pgsz) & 0x1ff);
if (addr_is_ram(pte, 8)) {
/*
* Note: Since DMA hardware does look up
* translation on its own, this PTE *must*
* match the TLB/EntryLo-register format !
*/
unsigned long a = *(unsigned long *)
PHYS_TO_XKSEG_UNCACHED(pte);
a = (a & 0x3f) << 6; /* PFN */
a += vaddr & ((1 << pgsz) - 1);
return (cpu_err_addr == a);
}
}
}
return 0;
}
static int check_vdma_memaddr(void)
{
if (cpu_err_stat & CPU_ERRMASK) {
u32 a = sgimc->maddronly;
if (!(sgimc->dma_ctrl & 0x100)) /* Xlate-bit clear ? */
return (cpu_err_addr == a);
if (check_microtlb(sgimc->dtlb_hi0, sgimc->dtlb_lo0, a) ||
check_microtlb(sgimc->dtlb_hi1, sgimc->dtlb_lo1, a) ||
check_microtlb(sgimc->dtlb_hi2, sgimc->dtlb_lo2, a) ||
check_microtlb(sgimc->dtlb_hi3, sgimc->dtlb_lo3, a))
return 1;
}
return 0;
}
static int check_vdma_gioaddr(void)
{
if (gio_err_stat & GIO_ERRMASK) {
u32 a = sgimc->gio_dma_trans;
a = (sgimc->gmaddronly & ~a) | (sgimc->gio_dma_sbits & a);
return (gio_err_addr == a);
}
return 0;
}
/*
* 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.
*/
static int ip28_be_interrupt(const struct pt_regs *regs)
{
int i;
save_and_clear_buserr();
/*
* Try to find out, whether we got here by a mispredicted speculative
* load/store operation. If so, it's not fatal, we can go on.
*/
/* Any cause other than "Interrupt" (ExcCode 0) is fatal. */
if (regs->cp0_cause & CAUSEF_EXCCODE)
goto mips_be_fatal;
/* Any cause other than "Bus error interrupt" (IP6) is weird. */
if ((regs->cp0_cause & CAUSEF_IP6) != CAUSEF_IP6)
goto mips_be_fatal;
if (extio_stat & (EXTIO_HPC3_BUSERR | EXTIO_EISA_BUSERR))
goto mips_be_fatal;
/* Any state other than "Memory bus error" is fatal. */
if (cpu_err_stat & CPU_ERRMASK & ~SGIMC_CSTAT_ADDR)
goto mips_be_fatal;
/* GIO errors other than timeouts are fatal */
if (gio_err_stat & GIO_ERRMASK & ~SGIMC_GSTAT_TIME)
goto mips_be_fatal;
/*
* Now we have an asynchronous bus error, speculatively or DMA caused.
* Need to search all DMA descriptors for the error address.
*/
for (i = 0; i < sizeof(hpc3)/sizeof(struct hpc3_stat); ++i) {
struct hpc3_stat *hp = (struct hpc3_stat *)&hpc3 + i;
if ((cpu_err_stat & CPU_ERRMASK) &&
(cpu_err_addr == hp->ndptr || cpu_err_addr == hp->cbp))
break;
if ((gio_err_stat & GIO_ERRMASK) &&
(gio_err_addr == hp->ndptr || gio_err_addr == hp->cbp))
break;
}
if (i < sizeof(hpc3)/sizeof(struct hpc3_stat)) {
struct hpc3_stat *hp = (struct hpc3_stat *)&hpc3 + i;
printk(KERN_ERR "at DMA addresses: HPC3 @ %08lx:"
" ctl %08x, ndp %08x, cbp %08x\n",
CPHYSADDR(hp->addr), hp->ctrl, hp->ndptr, hp->cbp);
goto mips_be_fatal;
}
/* Check MC's virtual DMA stuff. */
if (check_vdma_memaddr()) {
printk(KERN_ERR "at GIO DMA: mem address 0x%08x.\n",
sgimc->maddronly);
goto mips_be_fatal;
}
if (check_vdma_gioaddr()) {
printk(KERN_ERR "at GIO DMA: gio address 0x%08x.\n",
sgimc->gmaddronly);
goto mips_be_fatal;
}
/* A speculative bus error... */
if (debug_be_interrupt) {
print_buserr(regs);
printk(KERN_ERR "discarded!\n");
}
return MIPS_BE_DISCARD;
mips_be_fatal:
print_buserr(regs);
return MIPS_BE_FATAL;
}
void ip22_be_interrupt(int irq)
{
struct pt_regs *regs = get_irq_regs();
count_be_interrupt++;
if (ip28_be_interrupt(regs) != MIPS_BE_DISCARD) {
/* Assume it would be too dangerous to continue ... */
die_if_kernel("Oops", regs);
force_sig(SIGBUS, current);
} else if (debug_be_interrupt)
show_regs((struct pt_regs *)regs);
}
static int ip28_be_handler(struct pt_regs *regs, int is_fixup)
{
/*
* We arrive here only in the unusual case of do_be() invocation,
* i.e. by a bus error exception without a bus error interrupt.
*/
if (is_fixup) {
count_be_is_fixup++;
save_and_clear_buserr();
return MIPS_BE_FIXUP;
}
count_be_handler++;
return ip28_be_interrupt(regs);
}
void __init ip22_be_init(void)
{
board_be_handler = ip28_be_handler;
}
int ip28_show_be_info(struct seq_file *m)
{
seq_printf(m, "IP28 be fixups\t\t: %u\n", count_be_is_fixup);
seq_printf(m, "IP28 be interrupts\t: %u\n", count_be_interrupt);
seq_printf(m, "IP28 be handler\t\t: %u\n", count_be_handler);
return 0;
}
static int __init debug_be_setup(char *str)
{
debug_be_interrupt++;
return 1;
}
__setup("ip28_debug_be", debug_be_setup);