/*
* The file intends to implement the functions needed by EEH, which is
* built on IODA compliant chip. Actually, lots of functions related
* to EEH would be built based on the OPAL APIs.
*
* Copyright Benjamin Herrenschmidt & Gavin Shan, IBM Corporation 2013.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/bootmem.h>
#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/kernel.h>
#include <linux/msi.h>
#include <linux/notifier.h>
#include <linux/pci.h>
#include <linux/string.h>
#include <asm/eeh.h>
#include <asm/eeh_event.h>
#include <asm/io.h>
#include <asm/iommu.h>
#include <asm/msi_bitmap.h>
#include <asm/opal.h>
#include <asm/pci-bridge.h>
#include <asm/ppc-pci.h>
#include <asm/tce.h>
#include "powernv.h"
#include "pci.h"
static int ioda_eeh_nb_init = 0;
static int ioda_eeh_event(struct notifier_block *nb,
unsigned long events, void *change)
{
uint64_t changed_evts = (uint64_t)change;
/* We simply send special EEH event */
if ((changed_evts & OPAL_EVENT_PCI_ERROR) &&
(events & OPAL_EVENT_PCI_ERROR))
eeh_send_failure_event(NULL);
return 0;
}
static struct notifier_block ioda_eeh_nb = {
.notifier_call = ioda_eeh_event,
.next = NULL,
.priority = 0
};
#ifdef CONFIG_DEBUG_FS
static int ioda_eeh_dbgfs_set(void *data, int offset, u64 val)
{
struct pci_controller *hose = data;
struct pnv_phb *phb = hose->private_data;
out_be64(phb->regs + offset, val);
return 0;
}
static int ioda_eeh_dbgfs_get(void *data, int offset, u64 *val)
{
struct pci_controller *hose = data;
struct pnv_phb *phb = hose->private_data;
*val = in_be64(phb->regs + offset);
return 0;
}
static int ioda_eeh_outb_dbgfs_set(void *data, u64 val)
{
return ioda_eeh_dbgfs_set(data, 0xD10, val);
}
static int ioda_eeh_outb_dbgfs_get(void *data, u64 *val)
{
return ioda_eeh_dbgfs_get(data, 0xD10, val);
}
static int ioda_eeh_inbA_dbgfs_set(void *data, u64 val)
{
return ioda_eeh_dbgfs_set(data, 0xD90, val);
}
static int ioda_eeh_inbA_dbgfs_get(void *data, u64 *val)
{
return ioda_eeh_dbgfs_get(data, 0xD90, val);
}
static int ioda_eeh_inbB_dbgfs_set(void *data, u64 val)
{
return ioda_eeh_dbgfs_set(data, 0xE10, val);
}
static int ioda_eeh_inbB_dbgfs_get(void *data, u64 *val)
{
return ioda_eeh_dbgfs_get(data, 0xE10, val);
}
DEFINE_SIMPLE_ATTRIBUTE(ioda_eeh_outb_dbgfs_ops, ioda_eeh_outb_dbgfs_get,
ioda_eeh_outb_dbgfs_set, "0x%llx\n");
DEFINE_SIMPLE_ATTRIBUTE(ioda_eeh_inbA_dbgfs_ops, ioda_eeh_inbA_dbgfs_get,
ioda_eeh_inbA_dbgfs_set, "0x%llx\n");
DEFINE_SIMPLE_ATTRIBUTE(ioda_eeh_inbB_dbgfs_ops, ioda_eeh_inbB_dbgfs_get,
ioda_eeh_inbB_dbgfs_set, "0x%llx\n");
#endif /* CONFIG_DEBUG_FS */
/**
* ioda_eeh_post_init - Chip dependent post initialization
* @hose: PCI controller
*
* The function will be called after eeh PEs and devices
* have been built. That means the EEH is ready to supply
* service with I/O cache.
*/
static int ioda_eeh_post_init(struct pci_controller *hose)
{
struct pnv_phb *phb = hose->private_data;
int ret;
/* Register OPAL event notifier */
if (!ioda_eeh_nb_init) {
ret = opal_notifier_register(&ioda_eeh_nb);
if (ret) {
pr_err("%s: Can't register OPAL event notifier (%d)\n",
__func__, ret);
return ret;
}
ioda_eeh_nb_init = 1;
}
#ifdef CONFIG_DEBUG_FS
if (phb->dbgfs) {
debugfs_create_file("err_injct_outbound", 0600,
phb->dbgfs, hose,
&ioda_eeh_outb_dbgfs_ops);
debugfs_create_file("err_injct_inboundA", 0600,
phb->dbgfs, hose,
&ioda_eeh_inbA_dbgfs_ops);
debugfs_create_file("err_injct_inboundB", 0600,
phb->dbgfs, hose,
&ioda_eeh_inbB_dbgfs_ops);
}
#endif
phb->eeh_state |= PNV_EEH_STATE_ENABLED;
return 0;
}
/**
* ioda_eeh_set_option - Set EEH operation or I/O setting
* @pe: EEH PE
* @option: options
*
* Enable or disable EEH option for the indicated PE. The
* function also can be used to enable I/O or DMA for the
* PE.
*/
static