Commit 57bd45fd authored by Anton Blanchard's avatar Anton Blanchard

ppc64: EEH updates and FWNMI handler from Todd Inglett

parent b8dd4c32
......@@ -24,7 +24,6 @@ if [ "$CONFIG_SMP" = "y" ]; then
bool ' Distribute interrupts on all CPUs by default' CONFIG_IRQ_ALL_CPUS
if [ "$CONFIG_PPC_PSERIES" = "y" ]; then
bool ' Hardware multithreading' CONFIG_HMT
bool ' PCI Enhanced Error Handling' CONFIG_PPC_EEH
fi
fi
define_bool CONFIG_PREEMPT n
......
......@@ -30,13 +30,12 @@ obj-y := ppc_ksyms.o setup.o entry.o traps.o irq.o idle.o \
rtc.o init_task.o pSeries_htab.o
obj-$(CONFIG_PCI) += pci.o pci_dn.o pci_dma.o
obj-$(CONFIG_PPC_EEH) += eeh.o
ifeq ($(CONFIG_PPC_ISERIES),y)
obj-$(CONFIG_PCI) += iSeries_pci.o iSeries_pci_reset.o iSeries_IoMmTable.o iSeries_irq.o iSeries_VpdInfo.o XmPciLpEvent.o
endif
ifeq ($(CONFIG_PPC_PSERIES),y)
obj-$(CONFIG_PCI) += pSeries_pci.o pSeries_lpar.o pSeries_hvCall.o
obj-$(CONFIG_PCI) += pSeries_pci.o pSeries_lpar.o pSeries_hvCall.o eeh.o
obj-y += rtasd.o nvram.o
endif
......
......@@ -90,6 +90,9 @@ extern void iSeries_pcibios_fixup(void);
extern void pSeries_get_rtc_time(struct rtc_time *rtc_time);
extern int pSeries_set_rtc_time(struct rtc_time *rtc_time);
void pSeries_calibrate_decr(void);
static void fwnmi_init(void);
extern void SystemReset_FWNMI(void), MachineCheck_FWNMI(void); /* from head.S */
int fwnmi_active; /* TRUE if an FWNMI handler is present */
kdev_t boot_dev;
unsigned long virtPython0Facilities = 0; // python0 facility area (memory mapped io) (64-bit format) VIRTUAL address.
......@@ -152,6 +155,8 @@ chrp_setup_arch(void)
printk("Boot arguments: %s\n", cmd_line);
fwnmi_init();
/* Find and initialize PCI host bridges */
/* iSeries needs to be done much later. */
#ifndef CONFIG_PPC_ISERIES
......@@ -189,6 +194,23 @@ chrp_init2(void)
ppc_md.progress(UTS_RELEASE, 0x7777);
}
/* Initialize firmware assisted non-maskable interrupts if
* the firmware supports this feature.
*
*/
static void __init fwnmi_init(void)
{
long ret;
int ibm_nmi_register = rtas_token("ibm,nmi-register");
if (ibm_nmi_register == RTAS_UNKNOWN_SERVICE)
return;
ret = rtas_call(ibm_nmi_register, 2, 1, NULL,
__pa((unsigned long)SystemReset_FWNMI),
__pa((unsigned long)MachineCheck_FWNMI));
if (ret == 0)
fwnmi_active = 1;
}
/* Early initialization. Relocation is on but do not reference unbolted pages */
void __init pSeries_init_early(void)
......
......@@ -36,7 +36,6 @@
#define BUID_LO(buid) ((buid) & 0xffffffff)
#define CONFIG_ADDR(busno, devfn) (((((busno) & 0xff) << 8) | ((devfn) & 0xf8)) << 8)
unsigned long eeh_total_mmio_reads;
unsigned long eeh_total_mmio_ffs;
unsigned long eeh_false_positives;
/* RTAS tokens */
......@@ -44,11 +43,11 @@ static int ibm_set_eeh_option;
static int ibm_set_slot_reset;
static int ibm_read_slot_reset_state;
static int eeh_implemented;
int eeh_implemented;
#define EEH_MAX_OPTS 4096
static char *eeh_opts;
static int eeh_opts_last;
static int eeh_check_opts_config(struct pci_dev *dev);
static int eeh_check_opts_config(struct pci_dev *dev, int default_state);
unsigned long eeh_token(unsigned long phb, unsigned long bus, unsigned long devfn, unsigned long offset)
......@@ -86,33 +85,58 @@ unsigned long eeh_check_failure(void *token, unsigned long val)
panic("EEH: checking token %p phb index of %ld is greater than max of %d\n", token, phbidx, global_phb_number-1);
}
phb = phbtab[phbidx];
eeh_false_positives++;
ret = rtas_call(ibm_read_slot_reset_state, 3, 3, rets,
config_addr, BUID_HI(phb->buid), BUID_LO(phb->buid));
if (ret == 0 && rets[1] == 1 && rets[2] != 0) {
if (ret == 0 && rets[1] == 1 && rets[0] >= 2) {
struct pci_dev *dev;
int bus = ((unsigned long)token >> 40) & 0xffff; /* include PHB# in bus */
int devfn = (config_addr >> 8) & 0xff;
dev = pci_find_slot(bus, devfn);
if (dev)
if (dev) {
udbg_printf("EEH: MMIO failure (%ld) on device:\n %s %s\n",
rets[0], dev->slot_name, dev->name);
printk("EEH: MMIO failure (%ld) on device:\n %s %s\n",
rets[0], dev->slot_name, dev->name);
PPCDBG_ENTER_DEBUGGER();
panic("EEH: MMIO failure (%ld) on device:\n %s %s\n",
rets[2], dev->slot_name, dev->name);
else
panic("EEH: MMIO failure (%ld) on device buid %lx, config_addr %lx\n", rets[2], phb->buid, config_addr);
rets[0], dev->slot_name, dev->name);
} else {
udbg_printf("EEH: MMIO failure (%ld) on device buid %lx, config_addr %lx\n", rets[0], phb->buid, config_addr);
printk("EEH: MMIO failure (%ld) on device buid %lx, config_addr %lx\n", rets[0], phb->buid, config_addr);
PPCDBG_ENTER_DEBUGGER();
panic("EEH: MMIO failure (%ld) on device buid %lx, config_addr %lx\n", rets[0], phb->buid, config_addr);
}
}
eeh_false_positives++;
return val; /* good case */
}
void eeh_init(void) {
extern char cmd_line[]; /* Very early cmd line parse. Cheap, but works. */
char *eeh_force_off = strstr(cmd_line, "eeh-force-off");
char *eeh_force_on = strstr(cmd_line, "eeh-force-on");
ibm_set_eeh_option = rtas_token("ibm,set-eeh-option");
ibm_set_slot_reset = rtas_token("ibm,set-slot-reset");
ibm_read_slot_reset_state = rtas_token("ibm,read-slot-reset-state");
if (ibm_set_eeh_option != RTAS_UNKNOWN_SERVICE) {
printk("PCI Enhanced I/O Error Handling Enabled\n");
if (ibm_set_eeh_option != RTAS_UNKNOWN_SERVICE && _machine == _MACH_pSeriesLP)
eeh_implemented = 1;
if (eeh_force_off > eeh_force_on) {
/* User is forcing EEH off. Be noisy if it is implemented. */
if (eeh_implemented)
printk("EEH: WARNING: PCI Enhanced I/O Error Handling is user disabled\n");
eeh_implemented = 0;
return;
}
if (eeh_force_on > eeh_force_off)
eeh_implemented = 1; /* User is forcing it on. */
if (eeh_implemented)
printk("EEH: PCI Enhanced I/O Error Handling Enabled\n");
}
......@@ -124,27 +148,35 @@ int is_eeh_configured(struct pci_dev *dev)
struct device_node *dn = pci_device_to_OF_node(dev);
struct pci_controller *phb = PCI_GET_PHB_PTR(dev);
unsigned long ret, rets[2];
int eeh_capable;
int default_state = 1; /* default enable EEH if we can. */
if (dn == NULL || phb == NULL || phb->buid == 0 || !eeh_implemented)
if (dn == NULL || phb == NULL || !eeh_implemented)
return 0;
/* Hack: turn off eeh for display class devices.
/* Hack: turn off eeh for display class devices by default.
* This fixes matrox accel framebuffer.
*/
if ((dev->class >> 16) == PCI_BASE_CLASS_DISPLAY)
return 0;
default_state = 0;
if (!eeh_check_opts_config(dev))
/* Ignore known PHBs and EADs bridges */
if (dev->vendor == PCI_VENDOR_ID_IBM &&
(dev->device == 0x0102 || dev->device == 0x008b))
default_state = 0;
if (!eeh_check_opts_config(dev, default_state)) {
if (default_state)
printk("EEH: %s %s user requested to run without EEH.\n", dev->slot_name, dev->name);
return 0;
}
ret = rtas_call(ibm_read_slot_reset_state, 3, 3, rets,
CONFIG_ADDR(dn->busno, dn->devfn),
BUID_HI(phb->buid), BUID_LO(phb->buid));
if (ret == 0 && rets[1] == 1) {
printk("EEH: %s %s is EEH capable.\n", dev->slot_name, dev->name);
return 1;
}
return 0;
eeh_capable = (ret == 0 && rets[1] == 1);
printk("EEH: %s %s is%s EEH capable.\n", dev->slot_name, dev->name, eeh_capable ? "" : " not");
return eeh_capable;
}
int eeh_set_option(struct pci_dev *dev, int option)
......@@ -166,41 +198,41 @@ static int eeh_proc_falsepositive_read(char *page, char **start, off_t off,
{
int len;
len = sprintf(page, "eeh_false_positives=%ld\n"
"eeh_total_mmio_ffs=%ld\n"
"eeh_total_mmio_reads=%ld\n",
eeh_false_positives, eeh_total_mmio_ffs, eeh_total_mmio_reads);
"eeh_total_mmio_ffs=%ld\n",
eeh_false_positives, eeh_total_mmio_ffs);
return len;
}
/* Implementation of /proc/ppc64/eeh
* For now it is one file showing false positives.
*/
void eeh_init_proc(struct proc_dir_entry *top)
static int __init eeh_init_proc(void)
{
struct proc_dir_entry *ent = create_proc_entry("eeh", S_IRUGO, top);
struct proc_dir_entry *ent = create_proc_entry("ppc64/eeh", S_IRUGO, 0);
if (ent) {
ent->nlink = 1;
ent->data = NULL;
ent->read_proc = (void *)eeh_proc_falsepositive_read;
}
return 0;
}
/*
* Test if "dev" should be configured on or off.
* This processes the options literally from right to left.
* This processes the options literally from left to right.
* This lets the user specify stupid combinations of options,
* but at least the result should be very predictable.
*/
static int eeh_check_opts_config(struct pci_dev *dev)
static int eeh_check_opts_config(struct pci_dev *dev, int default_state)
{
struct device_node *dn = pci_device_to_OF_node(dev);
struct pci_controller *phb = PCI_GET_PHB_PTR(dev);
char devname[32], classname[32], phbname[32];
char *strs[8], *s;
int nstrs, i;
int ret = 0;
int ret = default_state;
if (dn == NULL || phb == NULL || phb->buid == 0 || !eeh_implemented)
if (dn == NULL || phb == NULL)
return 0;
/* Build list of strings to match */
nstrs = 0;
......@@ -221,7 +253,7 @@ static int eeh_check_opts_config(struct pci_dev *dev)
for (s = eeh_opts; s && (s < (eeh_opts + eeh_opts_last)); s += strlen(s)+1) {
for (i = 0; i < nstrs; i++) {
if (strcasecmp(strs[i], s+1) == 0) {
ret = (strs[0] == '+') ? 1 : 0;
ret = (strs[i][0] == '+') ? 1 : 0;
}
}
}
......@@ -234,7 +266,7 @@ static int eeh_check_opts_config(struct pci_dev *dev)
* eeh-off=loc1,loc2,loc3...
*
* and this option can be repeated so
* eeh-off=loc1,loc2 eeh=loc3
* eeh-off=loc1,loc2 eeh-off=loc3
* is the same as eeh-off=loc1,loc2,loc3
*
* loc is an IBM location code that can be found in a manual or
......@@ -285,7 +317,6 @@ static int __init eeh_parm(char *str, int state)
curend = cur + strlen(cur);
if (*cur) {
int curlen = curend-cur;
char *sym = eeh_opts+eeh_opts_last;
if (eeh_opts_last + curlen > EEH_MAX_OPTS-2) {
printk("EEH: sorry...too many eeh cmd line options\n");
return 1;
......@@ -308,6 +339,6 @@ static int __init eehon_parm(char *str)
return eeh_parm(str, 1);
}
__initcall(eeh_init_proc);
__setup("eeh-off", eehoff_parm);
__setup("eeh-on", eehon_parm);
/*
* arch/ppc/kernel/entry.S
* arch/ppc64/kernel/entry.S
*
* PowerPC version
* Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
......
......@@ -490,6 +490,24 @@ maskable_exception_exit:
mfspr r20,SPRG2
rfid
/*
* Data area reserved for FWNMI option.
*/
.= 0x7000
.globl fwnmi_data_area
fwnmi_data_area:
/*
* Vectors for the FWNMI option. Share common code.
*/
. = 0x8000
.globl SystemReset_FWNMI
SystemReset_FWNMI:
//EXCEPTION_PROLOG_PSERIES(0x100, SystemReset_common)
.globl MachineCheck_FWNMI
MachineCheck_FWNMI:
//EXCEPTION_PROLOG_PSERIES(0x200, MachineCheck_common)
/*** Common interrupt handlers ***/
STD_EXCEPTION_COMMON( 0x100, SystemReset, .SystemResetException )
......@@ -1285,7 +1303,7 @@ _STATIC(__after_prom_start)
/* this includes the code being */
/* executed here. */
li r0,4f@l /* Jump to the copy of this code */
LOADADDR(r0, 4f) /* Jump to the copy of this code */
mtctr r0 /* that we just made */
bctr
......
......@@ -41,9 +41,7 @@
#include <asm/ppcdebug.h>
#include <asm/naca.h>
#include <asm/pci_dma.h>
#ifdef CONFIG_PPC_EEH
#include <asm/eeh.h>
#endif
#include "xics.h"
#include "open_pic.h"
......@@ -256,9 +254,8 @@ find_and_init_phbs(void)
write_pci_config = rtas_token("write-pci-config");
ibm_read_pci_config = rtas_token("ibm,read-pci-config");
ibm_write_pci_config = rtas_token("ibm,write-pci-config");
#ifdef CONFIG_PPC_EEH
eeh_init();
#endif
if (naca->interrupt_controller == IC_OPEN_PIC) {
opprop = (unsigned int *)get_property(find_path_device("/"),
......@@ -358,14 +355,14 @@ find_and_init_phbs(void)
res = &phb->io_resource;
res->name = Pci_Node->full_name;
res->flags = IORESOURCE_IO;
#ifdef CONFIG_PPC_EEH
if (is_eeh_implemented()) {
if (!isa_io_base && has_isa) {
/* map a page for ISA ports. Not EEH protected. */
isa_io_base = (unsigned long)__ioremap(phb->io_base_phys, PAGE_SIZE, _PAGE_NO_CACHE);
}
res->start = phb->io_base_virt = eeh_token(index, 0, 0, 0);
res->end = eeh_token(index, 0xff, 0xff, 0xffffffff);
#else
} else {
phb->io_base_virt = ioremap(phb->io_base_phys, range.size);
if (!pci_io_base) {
pci_io_base = (unsigned long)phb->io_base_virt;
......@@ -375,7 +372,7 @@ find_and_init_phbs(void)
res->start = ((((unsigned long) range.child_addr.a_mid) << 32) | (range.child_addr.a_lo));
res->start += (unsigned long)phb->io_base_virt;
res->end = res->start + range.size - 1;
#endif
}
res->parent = NULL;
res->sibling = NULL;
res->child = NULL;
......@@ -399,13 +396,13 @@ find_and_init_phbs(void)
++memno;
res->name = Pci_Node->full_name;
res->flags = IORESOURCE_MEM;
#ifdef CONFIG_PPC_EEH
if (is_eeh_implemented()) {
res->start = eeh_token(index, 0, 0, 0);
res->end = eeh_token(index, 0xff, 0xff, 0xffffffff);
#else
} else {
res->start = range.parent_addr;
res->end = range.parent_addr + range.size - 1;
#endif
}
res->parent = NULL;
res->sibling = NULL;
res->child = NULL;
......@@ -566,14 +563,11 @@ alloc_phb(struct device_node *dev, char *model, unsigned int addr_size_words)
phb->ops = &rtas_pci_ops;
buid_vals = (int *) get_property(dev, "ibm,fw-phb-id", &len);
if (buid_vals == NULL || len < 2 * sizeof(int)) {
if (buid_vals == NULL) {
phb->buid = 0;
} else {
/* Big bus system. These systems start new bus numbers under
* each phb. Until pci domains are standard, we depend on a
* patch which makes bus numbers ints and we shift the phb
* number into the upper bits.
*/
}
else {
struct pci_bus check;
if (sizeof(check.number) == 1 || sizeof(check.primary) == 1 ||
sizeof(check.secondary) == 1 || sizeof(check.subordinate) == 1) {
......@@ -584,8 +578,13 @@ alloc_phb(struct device_node *dev, char *model, unsigned int addr_size_words)
"built with the patch that fixes include/linux/pci.h struct pci_bus so\n"
"number, primary, secondary and subordinate are ints.\n");
}
if (len < 2 * sizeof(int))
phb->buid = (unsigned long)buid_vals[0]; // Support for new OF that only has 1 integer for buid.
else
phb->buid = (((unsigned long)buid_vals[0]) << 32UL) |
(((unsigned long)buid_vals[1]) & 0xffffffff);
phb->first_busno += (phb->global_number << 8);
phb->last_busno += (phb->global_number << 8);
}
......@@ -601,7 +600,6 @@ fixup_resources(struct pci_dev *dev)
{
int i;
struct pci_controller *phb = PCI_GET_PHB_PTR(dev);
#ifdef CONFIG_PPC_EEH
struct device_node *dn;
unsigned long eeh_disable_bit;
......@@ -620,11 +618,11 @@ fixup_resources(struct pci_dev *dev)
}
}
if (is_eeh_implemented()) {
if (is_eeh_configured(dev)) {
eeh_disable_bit = 0;
printk("PCI: eeh configured for %s %s\n", dev->slot_name, dev->name);
if (eeh_set_option(dev, EEH_ENABLE) != 0) {
printk("PCI: failed to enable eeh for %s %s\n", dev->slot_name, dev->name);
printk("PCI: failed to enable EEH for %s %s\n", dev->slot_name, dev->name);
eeh_disable_bit = EEH_TOKEN_DISABLED;
}
} else {
......@@ -632,7 +630,7 @@ fixup_resources(struct pci_dev *dev)
printk("PCI: eeh NOT configured for %s %s\n", dev->slot_name, dev->name);
eeh_disable_bit = EEH_TOKEN_DISABLED;
}
#endif
}
PPCDBG(PPCDBG_PHBINIT, "fixup_resources:\n");
PPCDBG(PPCDBG_PHBINIT, "\tphb = 0x%016LX\n", phb);
......@@ -657,7 +655,7 @@ fixup_resources(struct pci_dev *dev)
}
if (dev->resource[i].flags & IORESOURCE_IO) {
#ifdef CONFIG_PPC_EEH
if (is_eeh_implemented()) {
unsigned int busno = dev->bus ? dev->bus->number : 0;
unsigned long size = dev->resource[i].end - dev->resource[i].start;
unsigned long addr = (unsigned long)__ioremap(dev->resource[i].start + phb->io_base_phys, size, _PAGE_NO_CACHE);
......@@ -665,11 +663,11 @@ fixup_resources(struct pci_dev *dev)
panic("fixup_resources: ioremap failed!\n");
dev->resource[i].start = eeh_token(phb->global_number, busno, dev->devfn, addr) | eeh_disable_bit;
dev->resource[i].end = dev->resource[i].start + size;
#else
} else {
unsigned long offset = (unsigned long)phb->io_base_virt;
dev->resource[i].start += offset;
dev->resource[i].end += offset;
#endif
}
PPCDBG(PPCDBG_PHBINIT, "\t\t-> now [%lx .. %lx]\n",
dev->resource[i].start, dev->resource[i].end);
} else if (dev->resource[i].flags & IORESOURCE_MEM) {
......@@ -677,7 +675,7 @@ fixup_resources(struct pci_dev *dev)
/* Bogus. Probably an unused bridge. */
dev->resource[i].end = 0;
} else {
#ifdef CONFIG_PPC_EEH
if (is_eeh_implemented()) {
unsigned int busno = dev->bus ? dev->bus->number : 0;
unsigned long size = dev->resource[i].end - dev->resource[i].start;
unsigned long addr = (unsigned long)__ioremap(dev->resource[i].start + phb->pci_mem_offset, size, _PAGE_NO_CACHE);
......@@ -685,10 +683,10 @@ fixup_resources(struct pci_dev *dev)
panic("fixup_resources: ioremap failed!\n");
dev->resource[i].start = eeh_token(phb->global_number, busno, dev->devfn, addr) | eeh_disable_bit;
dev->resource[i].end = dev->resource[i].start + size;
#else
} else {
dev->resource[i].start += phb->pci_mem_offset;
dev->resource[i].end += phb->pci_mem_offset;
#endif
}
}
PPCDBG(PPCDBG_PHBINIT, "\t\t-> now [%lx..%lx]\n",
dev->resource[i].start, dev->resource[i].end);
......
......@@ -33,9 +33,7 @@
#include <asm/naca.h>
#include <asm/pci_dma.h>
#include <asm/machdep.h>
#ifdef CONFIG_PPC_EEH
#include <asm/eeh.h>
#endif
#include "pci.h"
......@@ -62,7 +60,6 @@ void iSeries_pcibios_init(void);
void pSeries_pcibios_init(void);
int pci_assign_all_busses = 0;
struct pci_controller* hose_head;
struct pci_controller** hose_tail = &hose_head;
......@@ -514,7 +511,6 @@ void __init pcibios_fixup_bus(struct pci_bus *bus)
{
struct pci_controller *phb = PCI_GET_PHB_PTR(bus);
struct resource *res;
unsigned long io_offset;
int i;
#ifndef CONFIG_PPC_ISERIES
......@@ -546,12 +542,12 @@ void __init pcibios_fixup_bus(struct pci_bus *bus)
/* Transparent resource -- don't try to "fix" it. */
continue;
}
#ifdef CONFIG_PPC_EEH
if (is_eeh_implemented()) {
if (res->flags & (IORESOURCE_IO|IORESOURCE_MEM)) {
res->start = eeh_token(phb->global_number, bus->number, 0, 0);
res->end = eeh_token(phb->global_number, bus->number, 0xff, 0xffffffff);
}
#else
} else {
if (res->flags & IORESOURCE_IO) {
res->start += (unsigned long)phb->io_base_virt;
res->end += (unsigned long)phb->io_base_virt;
......@@ -562,7 +558,7 @@ void __init pcibios_fixup_bus(struct pci_bus *bus)
res->end += phb->pci_mem_offset;
}
}
#endif
}
}
}
#endif
......
/*
* arch/ppc/kernel/ppc_asm.h
* arch/ppc64/kernel/ppc_asm.h
*
* Definitions used by various bits of low-level assembly code on PowerPC.
*
......
......@@ -191,13 +191,11 @@ EXPORT_SYMBOL(iSeries_memcpy_fromio);
EXPORT_SYMBOL(iSeries_Read_Word);
EXPORT_SYMBOL(iSeries_Read_Byte);
EXPORT_SYMBOL(iSeries_Write_Byte);
#endif /* CONFIG_PPC_ISERIES */
#ifdef CONFIG_PPC_EEH
#ifndef CONFIG_PPC_ISERIES
EXPORT_SYMBOL(eeh_check_failure);
EXPORT_SYMBOL(eeh_total_mmio_ffs);
EXPORT_SYMBOL(eeh_total_mmio_reads);
#endif /* CONFIG_PPC_EEH */
#endif /* CONFIG_PPC_ISERIES */
#endif /* CONFIG_PCI */
EXPORT_SYMBOL(iSeries_veth_dev);
......@@ -241,10 +239,6 @@ EXPORT_SYMBOL(machine_is_compatible);
EXPORT_SYMBOL(find_all_nodes);
EXPORT_SYMBOL(get_property);
#ifdef CONFIG_NVRAM
EXPORT_SYMBOL(nvram_read_byte);
EXPORT_SYMBOL(nvram_write_byte);
#endif /* CONFIG_NVRAM */
EXPORT_SYMBOL_NOVERS(__ashrdi3);
EXPORT_SYMBOL_NOVERS(__ashldi3);
......
......@@ -40,6 +40,9 @@
extern int fix_alignment(struct pt_regs *);
extern void bad_page_fault(struct pt_regs *, unsigned long);
/* This is true if we are using the firmware NMI handler (typically LPAR) */
extern int fwnmi_active;
#ifdef CONFIG_XMON
extern void xmon(struct pt_regs *regs);
extern int xmon_bpt(struct pt_regs *regs);
......@@ -91,13 +94,55 @@ _exception(int signr, struct pt_regs *regs)
force_sig(signr, current);
}
/* Get the error information for errors coming through the
* FWNMI vectors. The pt_regs' r3 will be updated to reflect
* the actual r3 if possible, and a ptr to the error log entry
* will be returned if found.
*/
static struct rtas_error_log *FWNMI_get_errinfo(struct pt_regs *regs)
{
unsigned long errdata = regs->gpr[3];
struct rtas_error_log *errhdr = NULL;
unsigned long *savep;
if ((errdata >= 0x7000 && errdata < 0x7fff0) ||
(errdata >= rtas.base && errdata < rtas.base + rtas.size - 16)) {
savep = __va(errdata);
regs->gpr[3] = savep[0]; /* restore original r3 */
errhdr = (struct rtas_error_log *)(savep + 1);
} else {
printk("FWNMI: corrupt r3\n");
}
return errhdr;
}
/* Call this when done with the data returned by FWNMI_get_errinfo.
* It will release the saved data area for other CPUs in the
* partition to receive FWNMI errors.
*/
static void FWNMI_release_errinfo(void)
{
unsigned long ret = rtas_call(rtas_token("ibm,nmi-interlock"), 0, 1, NULL);
if (ret != 0)
printk("FWNMI: nmi-interlock failed: %ld\n", ret);
}
void
SystemResetException(struct pt_regs *regs)
{
udbg_printf("System Reset in kernel mode.\n");
printk("System Reset in kernel mode.\n");
char *msg = "System Reset in kernel mode.\n";
udbg_printf(msg); printk(msg);
if (fwnmi_active) {
unsigned long *r3 = __va(regs->gpr[3]); /* for FWNMI debug */
struct rtas_error_log *errlog;
msg = "FWNMI is active with save area at %016lx\n";
udbg_printf(msg, r3); printk(msg, r3);
errlog = FWNMI_get_errinfo(regs);
}
#if defined(CONFIG_XMON)
xmon(regs);
udbg_printf("leaving xmon...\n");
#else
for(;;);
#endif
......@@ -106,6 +151,13 @@ SystemResetException(struct pt_regs *regs)
void
MachineCheckException(struct pt_regs *regs)
{
if (fwnmi_active) {
struct rtas_error_log *errhdr = FWNMI_get_errinfo(regs);
if (errhdr) {
/* ToDo: attempt to recover from some errors here */
}
FWNMI_release_errinfo();
}
if ( !user_mode(regs) )
{
#if defined(CONFIG_XMON) || defined(CONFIG_KGDB)
......
......@@ -155,13 +155,11 @@ ioremap(unsigned long addr, unsigned long size)
#ifdef CONFIG_PPC_ISERIES
return (void*)addr;
#else
#ifdef CONFIG_PPC_EEH
if(mem_init_done && (addr >> 60UL)) {
if (IS_EEH_TOKEN_DISABLED(addr))
return IO_TOKEN_TO_ADDR(addr);
return (void*)addr; /* already mapped address or EEH token. */
}
#endif
return __ioremap(addr, size, _PAGE_NO_CACHE);
#endif
}
......
......@@ -37,11 +37,13 @@ struct pci_dev;
#define EEH_STATE_OVERRIDE 1 /* IOA does not require eeh traps */
#define EEH_STATE_FAILURE 16 /* */
/* This is for profiling only and should be removed */
extern unsigned long eeh_total_mmio_reads;
/* This is for profiling only */
extern unsigned long eeh_total_mmio_ffs;
extern int eeh_implemented;
void eeh_init(void);
static inline int is_eeh_implemented(void) { return eeh_implemented; }
int eeh_get_state(unsigned long ea);
unsigned long eeh_check_failure(void *token, unsigned long val);
......@@ -83,7 +85,7 @@ extern void *memset(void *,int, unsigned long);
*/
/* #define EEH_POSSIBLE_ERROR(addr, vaddr, val) ((vaddr) != (addr) && ~(val) == 0 && !IS_EEH_TOKEN_DISABLED(addr)) */
/* This version is rearranged to collect some profiling data */
#define EEH_POSSIBLE_ERROR(addr, vaddr, val) (++eeh_total_mmio_reads, (~(val) == 0 && (++eeh_total_mmio_ffs, (vaddr) != (addr) && !IS_EEH_TOKEN_DISABLED(addr))))
#define EEH_POSSIBLE_ERROR(addr, vaddr, val) (~(val) == 0 && (++eeh_total_mmio_ffs, (vaddr) != (addr) && !IS_EEH_TOKEN_DISABLED(addr)))
/*
* MMIO read/write operations with EEH support.
......
......@@ -51,7 +51,6 @@ extern int have_print;
#define outl(data,addr) writel(data,((unsigned long)(addr)))
#else
#define IS_MAPPED_VADDR(port) ((unsigned long)(port) >> 60UL)
#ifdef CONFIG_PPC_EEH
#define readb(addr) eeh_readb((void*)(addr))
#define readw(addr) eeh_readw((void*)(addr))
#define readl(addr) eeh_readl((void*)(addr))
......@@ -61,17 +60,6 @@ extern int have_print;
#define memset_io(a,b,c) eeh_memset((void *)(a),(b),(c))
#define memcpy_fromio(a,b,c) eeh_memcpy_fromio((a),(void *)(b),(c))
#define memcpy_toio(a,b,c) eeh_memcpy_toio((void *)(a),(b),(c))
#else
#define readb(addr) in_8((volatile u8 *)(addr))
#define writeb(b,addr) out_8((volatile u8 *)(addr), (b))
#define readw(addr) in_le16((volatile u16 *)(addr))
#define readl(addr) in_le32((volatile u32 *)(addr))
#define writew(b,addr) out_le16((volatile u16 *)(addr),(b))
#define writel(b,addr) out_le32((volatile u32 *)(addr),(b))
#define memset_io(a,b,c) memset((void *)(a),(b),(c))
#define memcpy_fromio(a,b,c) memcpy((a),(void *)(b),(c))
#define memcpy_toio(a,b,c) memcpy((void *)(a),(b),(c))
#endif
#define inb(port) _inb((unsigned long)port)
#define outb(val, port) _outb(val, (unsigned long)port)
#define inw(port) _inw((unsigned long)port)
......@@ -259,11 +247,9 @@ extern inline void out_be32(volatile unsigned *addr, int val)
__asm__ __volatile__("stw%U0%X0 %1,%0" : "=m" (*addr) : "r" (val));
}
#ifdef CONFIG_PPC_EEH
#ifndef CONFIG_PPC_ISERIES
#include <asm/eeh.h>
#endif
#ifndef CONFIG_PPC_ISERIES
static inline u8 _inb(unsigned long port) {
if (IS_MAPPED_VADDR(port))
return readb((void *)port);
......
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