Merge branch 'next-eeh' of git://git.kernel.org/pub/scm/linux/kernel/git/benh/powerpc into next
This commit is contained in:
commit
428d4d6520
28 changed files with 1663 additions and 1659 deletions
|
@ -8,6 +8,9 @@
|
||||||
|
|
||||||
struct dma_map_ops;
|
struct dma_map_ops;
|
||||||
struct device_node;
|
struct device_node;
|
||||||
|
#ifdef CONFIG_PPC64
|
||||||
|
struct pci_dn;
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Arch extensions to struct device.
|
* Arch extensions to struct device.
|
||||||
|
@ -34,6 +37,9 @@ struct dev_archdata {
|
||||||
#ifdef CONFIG_SWIOTLB
|
#ifdef CONFIG_SWIOTLB
|
||||||
dma_addr_t max_direct_dma_addr;
|
dma_addr_t max_direct_dma_addr;
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef CONFIG_PPC64
|
||||||
|
struct pci_dn *pci_data;
|
||||||
|
#endif
|
||||||
#ifdef CONFIG_EEH
|
#ifdef CONFIG_EEH
|
||||||
struct eeh_dev *edev;
|
struct eeh_dev *edev;
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
|
|
||||||
struct pci_dev;
|
struct pci_dev;
|
||||||
struct pci_bus;
|
struct pci_bus;
|
||||||
struct device_node;
|
struct pci_dn;
|
||||||
|
|
||||||
#ifdef CONFIG_EEH
|
#ifdef CONFIG_EEH
|
||||||
|
|
||||||
|
@ -136,14 +136,14 @@ struct eeh_dev {
|
||||||
struct eeh_pe *pe; /* Associated PE */
|
struct eeh_pe *pe; /* Associated PE */
|
||||||
struct list_head list; /* Form link list in the PE */
|
struct list_head list; /* Form link list in the PE */
|
||||||
struct pci_controller *phb; /* Associated PHB */
|
struct pci_controller *phb; /* Associated PHB */
|
||||||
struct device_node *dn; /* Associated device node */
|
struct pci_dn *pdn; /* Associated PCI device node */
|
||||||
struct pci_dev *pdev; /* Associated PCI device */
|
struct pci_dev *pdev; /* Associated PCI device */
|
||||||
struct pci_bus *bus; /* PCI bus for partial hotplug */
|
struct pci_bus *bus; /* PCI bus for partial hotplug */
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline struct device_node *eeh_dev_to_of_node(struct eeh_dev *edev)
|
static inline struct pci_dn *eeh_dev_to_pdn(struct eeh_dev *edev)
|
||||||
{
|
{
|
||||||
return edev ? edev->dn : NULL;
|
return edev ? edev->pdn : NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline struct pci_dev *eeh_dev_to_pci_dev(struct eeh_dev *edev)
|
static inline struct pci_dev *eeh_dev_to_pci_dev(struct eeh_dev *edev)
|
||||||
|
@ -200,8 +200,7 @@ struct eeh_ops {
|
||||||
char *name;
|
char *name;
|
||||||
int (*init)(void);
|
int (*init)(void);
|
||||||
int (*post_init)(void);
|
int (*post_init)(void);
|
||||||
void* (*of_probe)(struct device_node *dn, void *flag);
|
void* (*probe)(struct pci_dn *pdn, void *data);
|
||||||
int (*dev_probe)(struct pci_dev *dev, void *flag);
|
|
||||||
int (*set_option)(struct eeh_pe *pe, int option);
|
int (*set_option)(struct eeh_pe *pe, int option);
|
||||||
int (*get_pe_addr)(struct eeh_pe *pe);
|
int (*get_pe_addr)(struct eeh_pe *pe);
|
||||||
int (*get_state)(struct eeh_pe *pe, int *state);
|
int (*get_state)(struct eeh_pe *pe, int *state);
|
||||||
|
@ -211,10 +210,10 @@ struct eeh_ops {
|
||||||
int (*configure_bridge)(struct eeh_pe *pe);
|
int (*configure_bridge)(struct eeh_pe *pe);
|
||||||
int (*err_inject)(struct eeh_pe *pe, int type, int func,
|
int (*err_inject)(struct eeh_pe *pe, int type, int func,
|
||||||
unsigned long addr, unsigned long mask);
|
unsigned long addr, unsigned long mask);
|
||||||
int (*read_config)(struct device_node *dn, int where, int size, u32 *val);
|
int (*read_config)(struct pci_dn *pdn, int where, int size, u32 *val);
|
||||||
int (*write_config)(struct device_node *dn, int where, int size, u32 val);
|
int (*write_config)(struct pci_dn *pdn, int where, int size, u32 val);
|
||||||
int (*next_error)(struct eeh_pe **pe);
|
int (*next_error)(struct eeh_pe **pe);
|
||||||
int (*restore_config)(struct device_node *dn);
|
int (*restore_config)(struct pci_dn *pdn);
|
||||||
};
|
};
|
||||||
|
|
||||||
extern int eeh_subsystem_flags;
|
extern int eeh_subsystem_flags;
|
||||||
|
@ -272,7 +271,7 @@ void eeh_pe_restore_bars(struct eeh_pe *pe);
|
||||||
const char *eeh_pe_loc_get(struct eeh_pe *pe);
|
const char *eeh_pe_loc_get(struct eeh_pe *pe);
|
||||||
struct pci_bus *eeh_pe_bus_get(struct eeh_pe *pe);
|
struct pci_bus *eeh_pe_bus_get(struct eeh_pe *pe);
|
||||||
|
|
||||||
void *eeh_dev_init(struct device_node *dn, void *data);
|
void *eeh_dev_init(struct pci_dn *pdn, void *data);
|
||||||
void eeh_dev_phb_init_dynamic(struct pci_controller *phb);
|
void eeh_dev_phb_init_dynamic(struct pci_controller *phb);
|
||||||
int eeh_init(void);
|
int eeh_init(void);
|
||||||
int __init eeh_ops_register(struct eeh_ops *ops);
|
int __init eeh_ops_register(struct eeh_ops *ops);
|
||||||
|
@ -280,8 +279,8 @@ int __exit eeh_ops_unregister(const char *name);
|
||||||
int eeh_check_failure(const volatile void __iomem *token);
|
int eeh_check_failure(const volatile void __iomem *token);
|
||||||
int eeh_dev_check_failure(struct eeh_dev *edev);
|
int eeh_dev_check_failure(struct eeh_dev *edev);
|
||||||
void eeh_addr_cache_build(void);
|
void eeh_addr_cache_build(void);
|
||||||
void eeh_add_device_early(struct device_node *);
|
void eeh_add_device_early(struct pci_dn *);
|
||||||
void eeh_add_device_tree_early(struct device_node *);
|
void eeh_add_device_tree_early(struct pci_dn *);
|
||||||
void eeh_add_device_late(struct pci_dev *);
|
void eeh_add_device_late(struct pci_dev *);
|
||||||
void eeh_add_device_tree_late(struct pci_bus *);
|
void eeh_add_device_tree_late(struct pci_bus *);
|
||||||
void eeh_add_sysfs_files(struct pci_bus *);
|
void eeh_add_sysfs_files(struct pci_bus *);
|
||||||
|
@ -323,7 +322,7 @@ static inline int eeh_init(void)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void *eeh_dev_init(struct device_node *dn, void *data)
|
static inline void *eeh_dev_init(struct pci_dn *pdn, void *data)
|
||||||
{
|
{
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -339,9 +338,9 @@ static inline int eeh_check_failure(const volatile void __iomem *token)
|
||||||
|
|
||||||
static inline void eeh_addr_cache_build(void) { }
|
static inline void eeh_addr_cache_build(void) { }
|
||||||
|
|
||||||
static inline void eeh_add_device_early(struct device_node *dn) { }
|
static inline void eeh_add_device_early(struct pci_dn *pdn) { }
|
||||||
|
|
||||||
static inline void eeh_add_device_tree_early(struct device_node *dn) { }
|
static inline void eeh_add_device_tree_early(struct pci_dn *pdn) { }
|
||||||
|
|
||||||
static inline void eeh_add_device_late(struct pci_dev *dev) { }
|
static inline void eeh_add_device_late(struct pci_dev *dev) { }
|
||||||
|
|
||||||
|
|
|
@ -125,7 +125,7 @@ struct machdep_calls {
|
||||||
unsigned int (*get_irq)(void);
|
unsigned int (*get_irq)(void);
|
||||||
|
|
||||||
/* PCI stuff */
|
/* PCI stuff */
|
||||||
/* Called after scanning the bus, before allocating resources */
|
/* Called after allocating resources */
|
||||||
void (*pcibios_fixup)(void);
|
void (*pcibios_fixup)(void);
|
||||||
int (*pci_probe_mode)(struct pci_bus *);
|
int (*pci_probe_mode)(struct pci_bus *);
|
||||||
void (*pci_irq_fixup)(struct pci_dev *dev);
|
void (*pci_irq_fixup)(struct pci_dev *dev);
|
||||||
|
|
|
@ -89,6 +89,7 @@ struct pci_controller {
|
||||||
|
|
||||||
#ifdef CONFIG_PPC64
|
#ifdef CONFIG_PPC64
|
||||||
unsigned long buid;
|
unsigned long buid;
|
||||||
|
struct pci_dn *pci_data;
|
||||||
#endif /* CONFIG_PPC64 */
|
#endif /* CONFIG_PPC64 */
|
||||||
|
|
||||||
void *private_data;
|
void *private_data;
|
||||||
|
@ -154,9 +155,15 @@ static inline int isa_vaddr_is_ioport(void __iomem *address)
|
||||||
struct iommu_table;
|
struct iommu_table;
|
||||||
|
|
||||||
struct pci_dn {
|
struct pci_dn {
|
||||||
|
int flags;
|
||||||
|
|
||||||
int busno; /* pci bus number */
|
int busno; /* pci bus number */
|
||||||
int devfn; /* pci device and function number */
|
int devfn; /* pci device and function number */
|
||||||
|
int vendor_id; /* Vendor ID */
|
||||||
|
int device_id; /* Device ID */
|
||||||
|
int class_code; /* Device class code */
|
||||||
|
|
||||||
|
struct pci_dn *parent;
|
||||||
struct pci_controller *phb; /* for pci devices */
|
struct pci_controller *phb; /* for pci devices */
|
||||||
struct iommu_table *iommu_table; /* for phb's or bridges */
|
struct iommu_table *iommu_table; /* for phb's or bridges */
|
||||||
struct device_node *node; /* back-pointer to the device_node */
|
struct device_node *node; /* back-pointer to the device_node */
|
||||||
|
@ -171,13 +178,16 @@ struct pci_dn {
|
||||||
#ifdef CONFIG_PPC_POWERNV
|
#ifdef CONFIG_PPC_POWERNV
|
||||||
int pe_number;
|
int pe_number;
|
||||||
#endif
|
#endif
|
||||||
|
struct list_head child_list;
|
||||||
|
struct list_head list;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Get the pointer to a device_node's pci_dn */
|
/* Get the pointer to a device_node's pci_dn */
|
||||||
#define PCI_DN(dn) ((struct pci_dn *) (dn)->data)
|
#define PCI_DN(dn) ((struct pci_dn *) (dn)->data)
|
||||||
|
|
||||||
|
extern struct pci_dn *pci_get_pdn_by_devfn(struct pci_bus *bus,
|
||||||
|
int devfn);
|
||||||
extern struct pci_dn *pci_get_pdn(struct pci_dev *pdev);
|
extern struct pci_dn *pci_get_pdn(struct pci_dev *pdev);
|
||||||
|
|
||||||
extern void *update_dn_pci_info(struct device_node *dn, void *data);
|
extern void *update_dn_pci_info(struct device_node *dn, void *data);
|
||||||
|
|
||||||
static inline int pci_device_from_OF_node(struct device_node *np,
|
static inline int pci_device_from_OF_node(struct device_node *np,
|
||||||
|
@ -191,20 +201,12 @@ static inline int pci_device_from_OF_node(struct device_node *np,
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(CONFIG_EEH)
|
#if defined(CONFIG_EEH)
|
||||||
static inline struct eeh_dev *of_node_to_eeh_dev(struct device_node *dn)
|
static inline struct eeh_dev *pdn_to_eeh_dev(struct pci_dn *pdn)
|
||||||
{
|
{
|
||||||
/*
|
return pdn ? pdn->edev : NULL;
|
||||||
* For those OF nodes whose parent isn't PCI bridge, they
|
|
||||||
* don't have PCI_DN actually. So we have to skip them for
|
|
||||||
* any EEH operations.
|
|
||||||
*/
|
|
||||||
if (!dn || !PCI_DN(dn))
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
return PCI_DN(dn)->edev;
|
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
#define of_node_to_eeh_dev(x) (NULL)
|
#define pdn_to_eeh_dev(x) (NULL)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/** Find the bus corresponding to the indicated device node */
|
/** Find the bus corresponding to the indicated device node */
|
||||||
|
|
|
@ -33,9 +33,14 @@ extern struct pci_dev *isa_bridge_pcidev; /* may be NULL if no ISA bus */
|
||||||
|
|
||||||
/* PCI device_node operations */
|
/* PCI device_node operations */
|
||||||
struct device_node;
|
struct device_node;
|
||||||
|
struct pci_dn;
|
||||||
|
|
||||||
typedef void *(*traverse_func)(struct device_node *me, void *data);
|
typedef void *(*traverse_func)(struct device_node *me, void *data);
|
||||||
void *traverse_pci_devices(struct device_node *start, traverse_func pre,
|
void *traverse_pci_devices(struct device_node *start, traverse_func pre,
|
||||||
void *data);
|
void *data);
|
||||||
|
void *traverse_pci_dn(struct pci_dn *root,
|
||||||
|
void *(*fn)(struct pci_dn *, void *),
|
||||||
|
void *data);
|
||||||
|
|
||||||
extern void pci_devs_phb_init(void);
|
extern void pci_devs_phb_init(void);
|
||||||
extern void pci_devs_phb_init_dynamic(struct pci_controller *phb);
|
extern void pci_devs_phb_init_dynamic(struct pci_controller *phb);
|
||||||
|
|
|
@ -164,30 +164,34 @@ __setup("eeh=", eeh_setup);
|
||||||
*/
|
*/
|
||||||
static size_t eeh_dump_dev_log(struct eeh_dev *edev, char *buf, size_t len)
|
static size_t eeh_dump_dev_log(struct eeh_dev *edev, char *buf, size_t len)
|
||||||
{
|
{
|
||||||
struct device_node *dn = eeh_dev_to_of_node(edev);
|
struct pci_dn *pdn = eeh_dev_to_pdn(edev);
|
||||||
u32 cfg;
|
u32 cfg;
|
||||||
int cap, i;
|
int cap, i;
|
||||||
int n = 0, l = 0;
|
int n = 0, l = 0;
|
||||||
char buffer[128];
|
char buffer[128];
|
||||||
|
|
||||||
n += scnprintf(buf+n, len-n, "%s\n", dn->full_name);
|
n += scnprintf(buf+n, len-n, "%04x:%02x:%02x:%01x\n",
|
||||||
pr_warn("EEH: of node=%s\n", dn->full_name);
|
edev->phb->global_number, pdn->busno,
|
||||||
|
PCI_SLOT(pdn->devfn), PCI_FUNC(pdn->devfn));
|
||||||
|
pr_warn("EEH: of node=%04x:%02x:%02x:%01x\n",
|
||||||
|
edev->phb->global_number, pdn->busno,
|
||||||
|
PCI_SLOT(pdn->devfn), PCI_FUNC(pdn->devfn));
|
||||||
|
|
||||||
eeh_ops->read_config(dn, PCI_VENDOR_ID, 4, &cfg);
|
eeh_ops->read_config(pdn, PCI_VENDOR_ID, 4, &cfg);
|
||||||
n += scnprintf(buf+n, len-n, "dev/vend:%08x\n", cfg);
|
n += scnprintf(buf+n, len-n, "dev/vend:%08x\n", cfg);
|
||||||
pr_warn("EEH: PCI device/vendor: %08x\n", cfg);
|
pr_warn("EEH: PCI device/vendor: %08x\n", cfg);
|
||||||
|
|
||||||
eeh_ops->read_config(dn, PCI_COMMAND, 4, &cfg);
|
eeh_ops->read_config(pdn, PCI_COMMAND, 4, &cfg);
|
||||||
n += scnprintf(buf+n, len-n, "cmd/stat:%x\n", cfg);
|
n += scnprintf(buf+n, len-n, "cmd/stat:%x\n", cfg);
|
||||||
pr_warn("EEH: PCI cmd/status register: %08x\n", cfg);
|
pr_warn("EEH: PCI cmd/status register: %08x\n", cfg);
|
||||||
|
|
||||||
/* Gather bridge-specific registers */
|
/* Gather bridge-specific registers */
|
||||||
if (edev->mode & EEH_DEV_BRIDGE) {
|
if (edev->mode & EEH_DEV_BRIDGE) {
|
||||||
eeh_ops->read_config(dn, PCI_SEC_STATUS, 2, &cfg);
|
eeh_ops->read_config(pdn, PCI_SEC_STATUS, 2, &cfg);
|
||||||
n += scnprintf(buf+n, len-n, "sec stat:%x\n", cfg);
|
n += scnprintf(buf+n, len-n, "sec stat:%x\n", cfg);
|
||||||
pr_warn("EEH: Bridge secondary status: %04x\n", cfg);
|
pr_warn("EEH: Bridge secondary status: %04x\n", cfg);
|
||||||
|
|
||||||
eeh_ops->read_config(dn, PCI_BRIDGE_CONTROL, 2, &cfg);
|
eeh_ops->read_config(pdn, PCI_BRIDGE_CONTROL, 2, &cfg);
|
||||||
n += scnprintf(buf+n, len-n, "brdg ctl:%x\n", cfg);
|
n += scnprintf(buf+n, len-n, "brdg ctl:%x\n", cfg);
|
||||||
pr_warn("EEH: Bridge control: %04x\n", cfg);
|
pr_warn("EEH: Bridge control: %04x\n", cfg);
|
||||||
}
|
}
|
||||||
|
@ -195,11 +199,11 @@ static size_t eeh_dump_dev_log(struct eeh_dev *edev, char *buf, size_t len)
|
||||||
/* Dump out the PCI-X command and status regs */
|
/* Dump out the PCI-X command and status regs */
|
||||||
cap = edev->pcix_cap;
|
cap = edev->pcix_cap;
|
||||||
if (cap) {
|
if (cap) {
|
||||||
eeh_ops->read_config(dn, cap, 4, &cfg);
|
eeh_ops->read_config(pdn, cap, 4, &cfg);
|
||||||
n += scnprintf(buf+n, len-n, "pcix-cmd:%x\n", cfg);
|
n += scnprintf(buf+n, len-n, "pcix-cmd:%x\n", cfg);
|
||||||
pr_warn("EEH: PCI-X cmd: %08x\n", cfg);
|
pr_warn("EEH: PCI-X cmd: %08x\n", cfg);
|
||||||
|
|
||||||
eeh_ops->read_config(dn, cap+4, 4, &cfg);
|
eeh_ops->read_config(pdn, cap+4, 4, &cfg);
|
||||||
n += scnprintf(buf+n, len-n, "pcix-stat:%x\n", cfg);
|
n += scnprintf(buf+n, len-n, "pcix-stat:%x\n", cfg);
|
||||||
pr_warn("EEH: PCI-X status: %08x\n", cfg);
|
pr_warn("EEH: PCI-X status: %08x\n", cfg);
|
||||||
}
|
}
|
||||||
|
@ -211,7 +215,7 @@ static size_t eeh_dump_dev_log(struct eeh_dev *edev, char *buf, size_t len)
|
||||||
pr_warn("EEH: PCI-E capabilities and status follow:\n");
|
pr_warn("EEH: PCI-E capabilities and status follow:\n");
|
||||||
|
|
||||||
for (i=0; i<=8; i++) {
|
for (i=0; i<=8; i++) {
|
||||||
eeh_ops->read_config(dn, cap+4*i, 4, &cfg);
|
eeh_ops->read_config(pdn, cap+4*i, 4, &cfg);
|
||||||
n += scnprintf(buf+n, len-n, "%02x:%x\n", 4*i, cfg);
|
n += scnprintf(buf+n, len-n, "%02x:%x\n", 4*i, cfg);
|
||||||
|
|
||||||
if ((i % 4) == 0) {
|
if ((i % 4) == 0) {
|
||||||
|
@ -238,7 +242,7 @@ static size_t eeh_dump_dev_log(struct eeh_dev *edev, char *buf, size_t len)
|
||||||
pr_warn("EEH: PCI-E AER capability register set follows:\n");
|
pr_warn("EEH: PCI-E AER capability register set follows:\n");
|
||||||
|
|
||||||
for (i=0; i<=13; i++) {
|
for (i=0; i<=13; i++) {
|
||||||
eeh_ops->read_config(dn, cap+4*i, 4, &cfg);
|
eeh_ops->read_config(pdn, cap+4*i, 4, &cfg);
|
||||||
n += scnprintf(buf+n, len-n, "%02x:%x\n", 4*i, cfg);
|
n += scnprintf(buf+n, len-n, "%02x:%x\n", 4*i, cfg);
|
||||||
|
|
||||||
if ((i % 4) == 0) {
|
if ((i % 4) == 0) {
|
||||||
|
@ -414,11 +418,11 @@ int eeh_dev_check_failure(struct eeh_dev *edev)
|
||||||
int ret;
|
int ret;
|
||||||
int active_flags = (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE);
|
int active_flags = (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE);
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
struct device_node *dn;
|
struct pci_dn *pdn;
|
||||||
struct pci_dev *dev;
|
struct pci_dev *dev;
|
||||||
struct eeh_pe *pe, *parent_pe, *phb_pe;
|
struct eeh_pe *pe, *parent_pe, *phb_pe;
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
const char *location;
|
const char *location = NULL;
|
||||||
|
|
||||||
eeh_stats.total_mmio_ffs++;
|
eeh_stats.total_mmio_ffs++;
|
||||||
|
|
||||||
|
@ -429,15 +433,14 @@ int eeh_dev_check_failure(struct eeh_dev *edev)
|
||||||
eeh_stats.no_dn++;
|
eeh_stats.no_dn++;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
dn = eeh_dev_to_of_node(edev);
|
|
||||||
dev = eeh_dev_to_pci_dev(edev);
|
dev = eeh_dev_to_pci_dev(edev);
|
||||||
pe = eeh_dev_to_pe(edev);
|
pe = eeh_dev_to_pe(edev);
|
||||||
|
|
||||||
/* Access to IO BARs might get this far and still not want checking. */
|
/* Access to IO BARs might get this far and still not want checking. */
|
||||||
if (!pe) {
|
if (!pe) {
|
||||||
eeh_stats.ignored_check++;
|
eeh_stats.ignored_check++;
|
||||||
pr_debug("EEH: Ignored check for %s %s\n",
|
pr_debug("EEH: Ignored check for %s\n",
|
||||||
eeh_pci_name(dev), dn->full_name);
|
eeh_pci_name(dev));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -473,10 +476,13 @@ int eeh_dev_check_failure(struct eeh_dev *edev)
|
||||||
if (pe->state & EEH_PE_ISOLATED) {
|
if (pe->state & EEH_PE_ISOLATED) {
|
||||||
pe->check_count++;
|
pe->check_count++;
|
||||||
if (pe->check_count % EEH_MAX_FAILS == 0) {
|
if (pe->check_count % EEH_MAX_FAILS == 0) {
|
||||||
location = of_get_property(dn, "ibm,loc-code", NULL);
|
pdn = eeh_dev_to_pdn(edev);
|
||||||
|
if (pdn->node)
|
||||||
|
location = of_get_property(pdn->node, "ibm,loc-code", NULL);
|
||||||
printk(KERN_ERR "EEH: %d reads ignored for recovering device at "
|
printk(KERN_ERR "EEH: %d reads ignored for recovering device at "
|
||||||
"location=%s driver=%s pci addr=%s\n",
|
"location=%s driver=%s pci addr=%s\n",
|
||||||
pe->check_count, location,
|
pe->check_count,
|
||||||
|
location ? location : "unknown",
|
||||||
eeh_driver_name(dev), eeh_pci_name(dev));
|
eeh_driver_name(dev), eeh_pci_name(dev));
|
||||||
printk(KERN_ERR "EEH: Might be infinite loop in %s driver\n",
|
printk(KERN_ERR "EEH: Might be infinite loop in %s driver\n",
|
||||||
eeh_driver_name(dev));
|
eeh_driver_name(dev));
|
||||||
|
@ -667,6 +673,55 @@ int eeh_pci_enable(struct eeh_pe *pe, int function)
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void *eeh_disable_and_save_dev_state(void *data, void *userdata)
|
||||||
|
{
|
||||||
|
struct eeh_dev *edev = data;
|
||||||
|
struct pci_dev *pdev = eeh_dev_to_pci_dev(edev);
|
||||||
|
struct pci_dev *dev = userdata;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The caller should have disabled and saved the
|
||||||
|
* state for the specified device
|
||||||
|
*/
|
||||||
|
if (!pdev || pdev == dev)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* Ensure we have D0 power state */
|
||||||
|
pci_set_power_state(pdev, PCI_D0);
|
||||||
|
|
||||||
|
/* Save device state */
|
||||||
|
pci_save_state(pdev);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Disable device to avoid any DMA traffic and
|
||||||
|
* interrupt from the device
|
||||||
|
*/
|
||||||
|
pci_write_config_word(pdev, PCI_COMMAND, PCI_COMMAND_INTX_DISABLE);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *eeh_restore_dev_state(void *data, void *userdata)
|
||||||
|
{
|
||||||
|
struct eeh_dev *edev = data;
|
||||||
|
struct pci_dn *pdn = eeh_dev_to_pdn(edev);
|
||||||
|
struct pci_dev *pdev = eeh_dev_to_pci_dev(edev);
|
||||||
|
struct pci_dev *dev = userdata;
|
||||||
|
|
||||||
|
if (!pdev)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* Apply customization from firmware */
|
||||||
|
if (pdn && eeh_ops->restore_config)
|
||||||
|
eeh_ops->restore_config(pdn);
|
||||||
|
|
||||||
|
/* The caller should restore state for the specified device */
|
||||||
|
if (pdev != dev)
|
||||||
|
pci_save_state(pdev);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* pcibios_set_pcie_slot_reset - Set PCI-E reset state
|
* pcibios_set_pcie_slot_reset - Set PCI-E reset state
|
||||||
* @dev: pci device struct
|
* @dev: pci device struct
|
||||||
|
@ -689,13 +744,19 @@ int pcibios_set_pcie_reset_state(struct pci_dev *dev, enum pcie_reset_state stat
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case pcie_deassert_reset:
|
case pcie_deassert_reset:
|
||||||
eeh_ops->reset(pe, EEH_RESET_DEACTIVATE);
|
eeh_ops->reset(pe, EEH_RESET_DEACTIVATE);
|
||||||
|
eeh_unfreeze_pe(pe, false);
|
||||||
eeh_pe_state_clear(pe, EEH_PE_CFG_BLOCKED);
|
eeh_pe_state_clear(pe, EEH_PE_CFG_BLOCKED);
|
||||||
|
eeh_pe_dev_traverse(pe, eeh_restore_dev_state, dev);
|
||||||
break;
|
break;
|
||||||
case pcie_hot_reset:
|
case pcie_hot_reset:
|
||||||
|
eeh_ops->set_option(pe, EEH_OPT_FREEZE_PE);
|
||||||
|
eeh_pe_dev_traverse(pe, eeh_disable_and_save_dev_state, dev);
|
||||||
eeh_pe_state_mark(pe, EEH_PE_CFG_BLOCKED);
|
eeh_pe_state_mark(pe, EEH_PE_CFG_BLOCKED);
|
||||||
eeh_ops->reset(pe, EEH_RESET_HOT);
|
eeh_ops->reset(pe, EEH_RESET_HOT);
|
||||||
break;
|
break;
|
||||||
case pcie_warm_reset:
|
case pcie_warm_reset:
|
||||||
|
eeh_ops->set_option(pe, EEH_OPT_FREEZE_PE);
|
||||||
|
eeh_pe_dev_traverse(pe, eeh_disable_and_save_dev_state, dev);
|
||||||
eeh_pe_state_mark(pe, EEH_PE_CFG_BLOCKED);
|
eeh_pe_state_mark(pe, EEH_PE_CFG_BLOCKED);
|
||||||
eeh_ops->reset(pe, EEH_RESET_FUNDAMENTAL);
|
eeh_ops->reset(pe, EEH_RESET_FUNDAMENTAL);
|
||||||
break;
|
break;
|
||||||
|
@ -815,15 +876,15 @@ out:
|
||||||
*/
|
*/
|
||||||
void eeh_save_bars(struct eeh_dev *edev)
|
void eeh_save_bars(struct eeh_dev *edev)
|
||||||
{
|
{
|
||||||
|
struct pci_dn *pdn;
|
||||||
int i;
|
int i;
|
||||||
struct device_node *dn;
|
|
||||||
|
|
||||||
if (!edev)
|
pdn = eeh_dev_to_pdn(edev);
|
||||||
|
if (!pdn)
|
||||||
return;
|
return;
|
||||||
dn = eeh_dev_to_of_node(edev);
|
|
||||||
|
|
||||||
for (i = 0; i < 16; i++)
|
for (i = 0; i < 16; i++)
|
||||||
eeh_ops->read_config(dn, i * 4, 4, &edev->config_space[i]);
|
eeh_ops->read_config(pdn, i * 4, 4, &edev->config_space[i]);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* For PCI bridges including root port, we need enable bus
|
* For PCI bridges including root port, we need enable bus
|
||||||
|
@ -914,7 +975,7 @@ static struct notifier_block eeh_reboot_nb = {
|
||||||
int eeh_init(void)
|
int eeh_init(void)
|
||||||
{
|
{
|
||||||
struct pci_controller *hose, *tmp;
|
struct pci_controller *hose, *tmp;
|
||||||
struct device_node *phb;
|
struct pci_dn *pdn;
|
||||||
static int cnt = 0;
|
static int cnt = 0;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
|
@ -949,20 +1010,9 @@ int eeh_init(void)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
/* Enable EEH for all adapters */
|
/* Enable EEH for all adapters */
|
||||||
if (eeh_has_flag(EEH_PROBE_MODE_DEVTREE)) {
|
list_for_each_entry_safe(hose, tmp, &hose_list, list_node) {
|
||||||
list_for_each_entry_safe(hose, tmp,
|
pdn = hose->pci_data;
|
||||||
&hose_list, list_node) {
|
traverse_pci_dn(pdn, eeh_ops->probe, NULL);
|
||||||
phb = hose->dn;
|
|
||||||
traverse_pci_devices(phb, eeh_ops->of_probe, NULL);
|
|
||||||
}
|
|
||||||
} else if (eeh_has_flag(EEH_PROBE_MODE_DEV)) {
|
|
||||||
list_for_each_entry_safe(hose, tmp,
|
|
||||||
&hose_list, list_node)
|
|
||||||
pci_walk_bus(hose->bus, eeh_ops->dev_probe, NULL);
|
|
||||||
} else {
|
|
||||||
pr_warn("%s: Invalid probe mode %x",
|
|
||||||
__func__, eeh_subsystem_flags);
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -987,8 +1037,8 @@ int eeh_init(void)
|
||||||
core_initcall_sync(eeh_init);
|
core_initcall_sync(eeh_init);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* eeh_add_device_early - Enable EEH for the indicated device_node
|
* eeh_add_device_early - Enable EEH for the indicated device node
|
||||||
* @dn: device node for which to set up EEH
|
* @pdn: PCI device node for which to set up EEH
|
||||||
*
|
*
|
||||||
* This routine must be used to perform EEH initialization for PCI
|
* This routine must be used to perform EEH initialization for PCI
|
||||||
* devices that were added after system boot (e.g. hotplug, dlpar).
|
* devices that were added after system boot (e.g. hotplug, dlpar).
|
||||||
|
@ -998,44 +1048,41 @@ core_initcall_sync(eeh_init);
|
||||||
* on the CEC architecture, type of the device, on earlier boot
|
* on the CEC architecture, type of the device, on earlier boot
|
||||||
* command-line arguments & etc.
|
* command-line arguments & etc.
|
||||||
*/
|
*/
|
||||||
void eeh_add_device_early(struct device_node *dn)
|
void eeh_add_device_early(struct pci_dn *pdn)
|
||||||
{
|
{
|
||||||
struct pci_controller *phb;
|
struct pci_controller *phb;
|
||||||
|
struct eeh_dev *edev = pdn_to_eeh_dev(pdn);
|
||||||
|
|
||||||
/*
|
if (!edev)
|
||||||
* If we're doing EEH probe based on PCI device, we
|
|
||||||
* would delay the probe until late stage because
|
|
||||||
* the PCI device isn't available this moment.
|
|
||||||
*/
|
|
||||||
if (!eeh_has_flag(EEH_PROBE_MODE_DEVTREE))
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!of_node_to_eeh_dev(dn))
|
|
||||||
return;
|
|
||||||
phb = of_node_to_eeh_dev(dn)->phb;
|
|
||||||
|
|
||||||
/* USB Bus children of PCI devices will not have BUID's */
|
/* USB Bus children of PCI devices will not have BUID's */
|
||||||
if (NULL == phb || 0 == phb->buid)
|
phb = edev->phb;
|
||||||
|
if (NULL == phb ||
|
||||||
|
(eeh_has_flag(EEH_PROBE_MODE_DEVTREE) && 0 == phb->buid))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
eeh_ops->of_probe(dn, NULL);
|
eeh_ops->probe(pdn, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* eeh_add_device_tree_early - Enable EEH for the indicated device
|
* eeh_add_device_tree_early - Enable EEH for the indicated device
|
||||||
* @dn: device node
|
* @pdn: PCI device node
|
||||||
*
|
*
|
||||||
* This routine must be used to perform EEH initialization for the
|
* This routine must be used to perform EEH initialization for the
|
||||||
* indicated PCI device that was added after system boot (e.g.
|
* indicated PCI device that was added after system boot (e.g.
|
||||||
* hotplug, dlpar).
|
* hotplug, dlpar).
|
||||||
*/
|
*/
|
||||||
void eeh_add_device_tree_early(struct device_node *dn)
|
void eeh_add_device_tree_early(struct pci_dn *pdn)
|
||||||
{
|
{
|
||||||
struct device_node *sib;
|
struct pci_dn *n;
|
||||||
|
|
||||||
for_each_child_of_node(dn, sib)
|
if (!pdn)
|
||||||
eeh_add_device_tree_early(sib);
|
return;
|
||||||
eeh_add_device_early(dn);
|
|
||||||
|
list_for_each_entry(n, &pdn->child_list, list)
|
||||||
|
eeh_add_device_tree_early(n);
|
||||||
|
eeh_add_device_early(pdn);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(eeh_add_device_tree_early);
|
EXPORT_SYMBOL_GPL(eeh_add_device_tree_early);
|
||||||
|
|
||||||
|
@ -1048,7 +1095,7 @@ EXPORT_SYMBOL_GPL(eeh_add_device_tree_early);
|
||||||
*/
|
*/
|
||||||
void eeh_add_device_late(struct pci_dev *dev)
|
void eeh_add_device_late(struct pci_dev *dev)
|
||||||
{
|
{
|
||||||
struct device_node *dn;
|
struct pci_dn *pdn;
|
||||||
struct eeh_dev *edev;
|
struct eeh_dev *edev;
|
||||||
|
|
||||||
if (!dev || !eeh_enabled())
|
if (!dev || !eeh_enabled())
|
||||||
|
@ -1056,8 +1103,8 @@ void eeh_add_device_late(struct pci_dev *dev)
|
||||||
|
|
||||||
pr_debug("EEH: Adding device %s\n", pci_name(dev));
|
pr_debug("EEH: Adding device %s\n", pci_name(dev));
|
||||||
|
|
||||||
dn = pci_device_to_OF_node(dev);
|
pdn = pci_get_pdn_by_devfn(dev->bus, dev->devfn);
|
||||||
edev = of_node_to_eeh_dev(dn);
|
edev = pdn_to_eeh_dev(pdn);
|
||||||
if (edev->pdev == dev) {
|
if (edev->pdev == dev) {
|
||||||
pr_debug("EEH: Already referenced !\n");
|
pr_debug("EEH: Already referenced !\n");
|
||||||
return;
|
return;
|
||||||
|
@ -1089,13 +1136,6 @@ void eeh_add_device_late(struct pci_dev *dev)
|
||||||
edev->pdev = dev;
|
edev->pdev = dev;
|
||||||
dev->dev.archdata.edev = edev;
|
dev->dev.archdata.edev = edev;
|
||||||
|
|
||||||
/*
|
|
||||||
* We have to do the EEH probe here because the PCI device
|
|
||||||
* hasn't been created yet in the early stage.
|
|
||||||
*/
|
|
||||||
if (eeh_has_flag(EEH_PROBE_MODE_DEV))
|
|
||||||
eeh_ops->dev_probe(dev, NULL);
|
|
||||||
|
|
||||||
eeh_addr_cache_insert_dev(dev);
|
eeh_addr_cache_insert_dev(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -171,30 +171,27 @@ eeh_addr_cache_insert(struct pci_dev *dev, unsigned long alo,
|
||||||
|
|
||||||
static void __eeh_addr_cache_insert_dev(struct pci_dev *dev)
|
static void __eeh_addr_cache_insert_dev(struct pci_dev *dev)
|
||||||
{
|
{
|
||||||
struct device_node *dn;
|
struct pci_dn *pdn;
|
||||||
struct eeh_dev *edev;
|
struct eeh_dev *edev;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
dn = pci_device_to_OF_node(dev);
|
pdn = pci_get_pdn_by_devfn(dev->bus, dev->devfn);
|
||||||
if (!dn) {
|
if (!pdn) {
|
||||||
pr_warn("PCI: no pci dn found for dev=%s\n",
|
pr_warn("PCI: no pci dn found for dev=%s\n",
|
||||||
pci_name(dev));
|
pci_name(dev));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
edev = of_node_to_eeh_dev(dn);
|
edev = pdn_to_eeh_dev(pdn);
|
||||||
if (!edev) {
|
if (!edev) {
|
||||||
pr_warn("PCI: no EEH dev found for dn=%s\n",
|
pr_warn("PCI: no EEH dev found for %s\n",
|
||||||
dn->full_name);
|
pci_name(dev));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Skip any devices for which EEH is not enabled. */
|
/* Skip any devices for which EEH is not enabled. */
|
||||||
if (!edev->pe) {
|
if (!edev->pe) {
|
||||||
#ifdef DEBUG
|
dev_dbg(&dev->dev, "EEH: Skip building address cache\n");
|
||||||
pr_info("PCI: skip building address cache for=%s - %s\n",
|
|
||||||
pci_name(dev), dn->full_name);
|
|
||||||
#endif
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -282,18 +279,18 @@ void eeh_addr_cache_rmv_dev(struct pci_dev *dev)
|
||||||
*/
|
*/
|
||||||
void eeh_addr_cache_build(void)
|
void eeh_addr_cache_build(void)
|
||||||
{
|
{
|
||||||
struct device_node *dn;
|
struct pci_dn *pdn;
|
||||||
struct eeh_dev *edev;
|
struct eeh_dev *edev;
|
||||||
struct pci_dev *dev = NULL;
|
struct pci_dev *dev = NULL;
|
||||||
|
|
||||||
spin_lock_init(&pci_io_addr_cache_root.piar_lock);
|
spin_lock_init(&pci_io_addr_cache_root.piar_lock);
|
||||||
|
|
||||||
for_each_pci_dev(dev) {
|
for_each_pci_dev(dev) {
|
||||||
dn = pci_device_to_OF_node(dev);
|
pdn = pci_get_pdn_by_devfn(dev->bus, dev->devfn);
|
||||||
if (!dn)
|
if (!pdn)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
edev = of_node_to_eeh_dev(dn);
|
edev = pdn_to_eeh_dev(pdn);
|
||||||
if (!edev)
|
if (!edev)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
|
|
@ -43,13 +43,13 @@
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* eeh_dev_init - Create EEH device according to OF node
|
* eeh_dev_init - Create EEH device according to OF node
|
||||||
* @dn: device node
|
* @pdn: PCI device node
|
||||||
* @data: PHB
|
* @data: PHB
|
||||||
*
|
*
|
||||||
* It will create EEH device according to the given OF node. The function
|
* It will create EEH device according to the given OF node. The function
|
||||||
* might be called by PCI emunation, DR, PHB hotplug.
|
* might be called by PCI emunation, DR, PHB hotplug.
|
||||||
*/
|
*/
|
||||||
void *eeh_dev_init(struct device_node *dn, void *data)
|
void *eeh_dev_init(struct pci_dn *pdn, void *data)
|
||||||
{
|
{
|
||||||
struct pci_controller *phb = data;
|
struct pci_controller *phb = data;
|
||||||
struct eeh_dev *edev;
|
struct eeh_dev *edev;
|
||||||
|
@ -63,8 +63,8 @@ void *eeh_dev_init(struct device_node *dn, void *data)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Associate EEH device with OF node */
|
/* Associate EEH device with OF node */
|
||||||
PCI_DN(dn)->edev = edev;
|
pdn->edev = edev;
|
||||||
edev->dn = dn;
|
edev->pdn = pdn;
|
||||||
edev->phb = phb;
|
edev->phb = phb;
|
||||||
INIT_LIST_HEAD(&edev->list);
|
INIT_LIST_HEAD(&edev->list);
|
||||||
|
|
||||||
|
@ -80,16 +80,16 @@ void *eeh_dev_init(struct device_node *dn, void *data)
|
||||||
*/
|
*/
|
||||||
void eeh_dev_phb_init_dynamic(struct pci_controller *phb)
|
void eeh_dev_phb_init_dynamic(struct pci_controller *phb)
|
||||||
{
|
{
|
||||||
struct device_node *dn = phb->dn;
|
struct pci_dn *root = phb->pci_data;
|
||||||
|
|
||||||
/* EEH PE for PHB */
|
/* EEH PE for PHB */
|
||||||
eeh_phb_pe_create(phb);
|
eeh_phb_pe_create(phb);
|
||||||
|
|
||||||
/* EEH device for PHB */
|
/* EEH device for PHB */
|
||||||
eeh_dev_init(dn, phb);
|
eeh_dev_init(root, phb);
|
||||||
|
|
||||||
/* EEH devices for children OF nodes */
|
/* EEH devices for children OF nodes */
|
||||||
traverse_pci_devices(dn, eeh_dev_init, phb);
|
traverse_pci_dn(root, eeh_dev_init, phb);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -83,28 +83,6 @@ static inline void eeh_pcid_put(struct pci_dev *pdev)
|
||||||
module_put(pdev->driver->driver.owner);
|
module_put(pdev->driver->driver.owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
|
||||||
static void print_device_node_tree(struct pci_dn *pdn, int dent)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
struct device_node *pc;
|
|
||||||
|
|
||||||
if (!pdn)
|
|
||||||
return;
|
|
||||||
for (i = 0; i < dent; i++)
|
|
||||||
printk(" ");
|
|
||||||
printk("dn=%s mode=%x \tcfg_addr=%x pe_addr=%x \tfull=%s\n",
|
|
||||||
pdn->node->name, pdn->eeh_mode, pdn->eeh_config_addr,
|
|
||||||
pdn->eeh_pe_config_addr, pdn->node->full_name);
|
|
||||||
dent += 3;
|
|
||||||
pc = pdn->node->child;
|
|
||||||
while (pc) {
|
|
||||||
print_device_node_tree(PCI_DN(pc), dent);
|
|
||||||
pc = pc->sibling;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* eeh_disable_irq - Disable interrupt for the recovering device
|
* eeh_disable_irq - Disable interrupt for the recovering device
|
||||||
* @dev: PCI device
|
* @dev: PCI device
|
||||||
|
|
|
@ -291,27 +291,25 @@ struct eeh_pe *eeh_pe_get(struct eeh_dev *edev)
|
||||||
*/
|
*/
|
||||||
static struct eeh_pe *eeh_pe_get_parent(struct eeh_dev *edev)
|
static struct eeh_pe *eeh_pe_get_parent(struct eeh_dev *edev)
|
||||||
{
|
{
|
||||||
struct device_node *dn;
|
|
||||||
struct eeh_dev *parent;
|
struct eeh_dev *parent;
|
||||||
|
struct pci_dn *pdn = eeh_dev_to_pdn(edev);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* It might have the case for the indirect parent
|
* It might have the case for the indirect parent
|
||||||
* EEH device already having associated PE, but
|
* EEH device already having associated PE, but
|
||||||
* the direct parent EEH device doesn't have yet.
|
* the direct parent EEH device doesn't have yet.
|
||||||
*/
|
*/
|
||||||
dn = edev->dn->parent;
|
pdn = pdn ? pdn->parent : NULL;
|
||||||
while (dn) {
|
while (pdn) {
|
||||||
/* We're poking out of PCI territory */
|
/* We're poking out of PCI territory */
|
||||||
if (!PCI_DN(dn)) return NULL;
|
parent = pdn_to_eeh_dev(pdn);
|
||||||
|
if (!parent)
|
||||||
parent = of_node_to_eeh_dev(dn);
|
return NULL;
|
||||||
/* We're poking out of PCI territory */
|
|
||||||
if (!parent) return NULL;
|
|
||||||
|
|
||||||
if (parent->pe)
|
if (parent->pe)
|
||||||
return parent->pe;
|
return parent->pe;
|
||||||
|
|
||||||
dn = dn->parent;
|
pdn = pdn->parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -330,6 +328,13 @@ int eeh_add_to_parent_pe(struct eeh_dev *edev)
|
||||||
{
|
{
|
||||||
struct eeh_pe *pe, *parent;
|
struct eeh_pe *pe, *parent;
|
||||||
|
|
||||||
|
/* Check if the PE number is valid */
|
||||||
|
if (!eeh_has_flag(EEH_VALID_PE_ZERO) && !edev->pe_config_addr) {
|
||||||
|
pr_err("%s: Invalid PE#0 for edev 0x%x on PHB#%d\n",
|
||||||
|
__func__, edev->config_addr, edev->phb->global_number);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Search the PE has been existing or not according
|
* Search the PE has been existing or not according
|
||||||
* to the PE address. If that has been existing, the
|
* to the PE address. If that has been existing, the
|
||||||
|
@ -338,21 +343,18 @@ int eeh_add_to_parent_pe(struct eeh_dev *edev)
|
||||||
*/
|
*/
|
||||||
pe = eeh_pe_get(edev);
|
pe = eeh_pe_get(edev);
|
||||||
if (pe && !(pe->type & EEH_PE_INVALID)) {
|
if (pe && !(pe->type & EEH_PE_INVALID)) {
|
||||||
if (!edev->pe_config_addr) {
|
|
||||||
pr_err("%s: PE with addr 0x%x already exists\n",
|
|
||||||
__func__, edev->config_addr);
|
|
||||||
return -EEXIST;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Mark the PE as type of PCI bus */
|
/* Mark the PE as type of PCI bus */
|
||||||
pe->type = EEH_PE_BUS;
|
pe->type = EEH_PE_BUS;
|
||||||
edev->pe = pe;
|
edev->pe = pe;
|
||||||
|
|
||||||
/* Put the edev to PE */
|
/* Put the edev to PE */
|
||||||
list_add_tail(&edev->list, &pe->edevs);
|
list_add_tail(&edev->list, &pe->edevs);
|
||||||
pr_debug("EEH: Add %s to Bus PE#%x\n",
|
pr_debug("EEH: Add %04x:%02x:%02x.%01x to Bus PE#%x\n",
|
||||||
edev->dn->full_name, pe->addr);
|
edev->phb->global_number,
|
||||||
|
edev->config_addr >> 8,
|
||||||
|
PCI_SLOT(edev->config_addr & 0xFF),
|
||||||
|
PCI_FUNC(edev->config_addr & 0xFF),
|
||||||
|
pe->addr);
|
||||||
return 0;
|
return 0;
|
||||||
} else if (pe && (pe->type & EEH_PE_INVALID)) {
|
} else if (pe && (pe->type & EEH_PE_INVALID)) {
|
||||||
list_add_tail(&edev->list, &pe->edevs);
|
list_add_tail(&edev->list, &pe->edevs);
|
||||||
|
@ -368,9 +370,14 @@ int eeh_add_to_parent_pe(struct eeh_dev *edev)
|
||||||
parent->type &= ~(EEH_PE_INVALID | EEH_PE_KEEP);
|
parent->type &= ~(EEH_PE_INVALID | EEH_PE_KEEP);
|
||||||
parent = parent->parent;
|
parent = parent->parent;
|
||||||
}
|
}
|
||||||
pr_debug("EEH: Add %s to Device PE#%x, Parent PE#%x\n",
|
|
||||||
edev->dn->full_name, pe->addr, pe->parent->addr);
|
|
||||||
|
|
||||||
|
pr_debug("EEH: Add %04x:%02x:%02x.%01x to Device "
|
||||||
|
"PE#%x, Parent PE#%x\n",
|
||||||
|
edev->phb->global_number,
|
||||||
|
edev->config_addr >> 8,
|
||||||
|
PCI_SLOT(edev->config_addr & 0xFF),
|
||||||
|
PCI_FUNC(edev->config_addr & 0xFF),
|
||||||
|
pe->addr, pe->parent->addr);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -409,8 +416,13 @@ int eeh_add_to_parent_pe(struct eeh_dev *edev)
|
||||||
list_add_tail(&pe->child, &parent->child_list);
|
list_add_tail(&pe->child, &parent->child_list);
|
||||||
list_add_tail(&edev->list, &pe->edevs);
|
list_add_tail(&edev->list, &pe->edevs);
|
||||||
edev->pe = pe;
|
edev->pe = pe;
|
||||||
pr_debug("EEH: Add %s to Device PE#%x, Parent PE#%x\n",
|
pr_debug("EEH: Add %04x:%02x:%02x.%01x to "
|
||||||
edev->dn->full_name, pe->addr, pe->parent->addr);
|
"Device PE#%x, Parent PE#%x\n",
|
||||||
|
edev->phb->global_number,
|
||||||
|
edev->config_addr >> 8,
|
||||||
|
PCI_SLOT(edev->config_addr & 0xFF),
|
||||||
|
PCI_FUNC(edev->config_addr & 0xFF),
|
||||||
|
pe->addr, pe->parent->addr);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -430,8 +442,11 @@ int eeh_rmv_from_parent_pe(struct eeh_dev *edev)
|
||||||
int cnt;
|
int cnt;
|
||||||
|
|
||||||
if (!edev->pe) {
|
if (!edev->pe) {
|
||||||
pr_debug("%s: No PE found for EEH device %s\n",
|
pr_debug("%s: No PE found for device %04x:%02x:%02x.%01x\n",
|
||||||
__func__, edev->dn->full_name);
|
__func__, edev->phb->global_number,
|
||||||
|
edev->config_addr >> 8,
|
||||||
|
PCI_SLOT(edev->config_addr & 0xFF),
|
||||||
|
PCI_FUNC(edev->config_addr & 0xFF));
|
||||||
return -EEXIST;
|
return -EEXIST;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -653,9 +668,9 @@ void eeh_pe_state_clear(struct eeh_pe *pe, int state)
|
||||||
* blocked on normal path during the stage. So we need utilize
|
* blocked on normal path during the stage. So we need utilize
|
||||||
* eeh operations, which is always permitted.
|
* eeh operations, which is always permitted.
|
||||||
*/
|
*/
|
||||||
static void eeh_bridge_check_link(struct eeh_dev *edev,
|
static void eeh_bridge_check_link(struct eeh_dev *edev)
|
||||||
struct device_node *dn)
|
|
||||||
{
|
{
|
||||||
|
struct pci_dn *pdn = eeh_dev_to_pdn(edev);
|
||||||
int cap;
|
int cap;
|
||||||
uint32_t val;
|
uint32_t val;
|
||||||
int timeout = 0;
|
int timeout = 0;
|
||||||
|
@ -675,32 +690,32 @@ static void eeh_bridge_check_link(struct eeh_dev *edev,
|
||||||
|
|
||||||
/* Check slot status */
|
/* Check slot status */
|
||||||
cap = edev->pcie_cap;
|
cap = edev->pcie_cap;
|
||||||
eeh_ops->read_config(dn, cap + PCI_EXP_SLTSTA, 2, &val);
|
eeh_ops->read_config(pdn, cap + PCI_EXP_SLTSTA, 2, &val);
|
||||||
if (!(val & PCI_EXP_SLTSTA_PDS)) {
|
if (!(val & PCI_EXP_SLTSTA_PDS)) {
|
||||||
pr_debug(" No card in the slot (0x%04x) !\n", val);
|
pr_debug(" No card in the slot (0x%04x) !\n", val);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check power status if we have the capability */
|
/* Check power status if we have the capability */
|
||||||
eeh_ops->read_config(dn, cap + PCI_EXP_SLTCAP, 2, &val);
|
eeh_ops->read_config(pdn, cap + PCI_EXP_SLTCAP, 2, &val);
|
||||||
if (val & PCI_EXP_SLTCAP_PCP) {
|
if (val & PCI_EXP_SLTCAP_PCP) {
|
||||||
eeh_ops->read_config(dn, cap + PCI_EXP_SLTCTL, 2, &val);
|
eeh_ops->read_config(pdn, cap + PCI_EXP_SLTCTL, 2, &val);
|
||||||
if (val & PCI_EXP_SLTCTL_PCC) {
|
if (val & PCI_EXP_SLTCTL_PCC) {
|
||||||
pr_debug(" In power-off state, power it on ...\n");
|
pr_debug(" In power-off state, power it on ...\n");
|
||||||
val &= ~(PCI_EXP_SLTCTL_PCC | PCI_EXP_SLTCTL_PIC);
|
val &= ~(PCI_EXP_SLTCTL_PCC | PCI_EXP_SLTCTL_PIC);
|
||||||
val |= (0x0100 & PCI_EXP_SLTCTL_PIC);
|
val |= (0x0100 & PCI_EXP_SLTCTL_PIC);
|
||||||
eeh_ops->write_config(dn, cap + PCI_EXP_SLTCTL, 2, val);
|
eeh_ops->write_config(pdn, cap + PCI_EXP_SLTCTL, 2, val);
|
||||||
msleep(2 * 1000);
|
msleep(2 * 1000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Enable link */
|
/* Enable link */
|
||||||
eeh_ops->read_config(dn, cap + PCI_EXP_LNKCTL, 2, &val);
|
eeh_ops->read_config(pdn, cap + PCI_EXP_LNKCTL, 2, &val);
|
||||||
val &= ~PCI_EXP_LNKCTL_LD;
|
val &= ~PCI_EXP_LNKCTL_LD;
|
||||||
eeh_ops->write_config(dn, cap + PCI_EXP_LNKCTL, 2, val);
|
eeh_ops->write_config(pdn, cap + PCI_EXP_LNKCTL, 2, val);
|
||||||
|
|
||||||
/* Check link */
|
/* Check link */
|
||||||
eeh_ops->read_config(dn, cap + PCI_EXP_LNKCAP, 4, &val);
|
eeh_ops->read_config(pdn, cap + PCI_EXP_LNKCAP, 4, &val);
|
||||||
if (!(val & PCI_EXP_LNKCAP_DLLLARC)) {
|
if (!(val & PCI_EXP_LNKCAP_DLLLARC)) {
|
||||||
pr_debug(" No link reporting capability (0x%08x) \n", val);
|
pr_debug(" No link reporting capability (0x%08x) \n", val);
|
||||||
msleep(1000);
|
msleep(1000);
|
||||||
|
@ -713,7 +728,7 @@ static void eeh_bridge_check_link(struct eeh_dev *edev,
|
||||||
msleep(20);
|
msleep(20);
|
||||||
timeout += 20;
|
timeout += 20;
|
||||||
|
|
||||||
eeh_ops->read_config(dn, cap + PCI_EXP_LNKSTA, 2, &val);
|
eeh_ops->read_config(pdn, cap + PCI_EXP_LNKSTA, 2, &val);
|
||||||
if (val & PCI_EXP_LNKSTA_DLLLA)
|
if (val & PCI_EXP_LNKSTA_DLLLA)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -728,9 +743,9 @@ static void eeh_bridge_check_link(struct eeh_dev *edev,
|
||||||
#define BYTE_SWAP(OFF) (8*((OFF)/4)+3-(OFF))
|
#define BYTE_SWAP(OFF) (8*((OFF)/4)+3-(OFF))
|
||||||
#define SAVED_BYTE(OFF) (((u8 *)(edev->config_space))[BYTE_SWAP(OFF)])
|
#define SAVED_BYTE(OFF) (((u8 *)(edev->config_space))[BYTE_SWAP(OFF)])
|
||||||
|
|
||||||
static void eeh_restore_bridge_bars(struct eeh_dev *edev,
|
static void eeh_restore_bridge_bars(struct eeh_dev *edev)
|
||||||
struct device_node *dn)
|
|
||||||
{
|
{
|
||||||
|
struct pci_dn *pdn = eeh_dev_to_pdn(edev);
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -738,49 +753,49 @@ static void eeh_restore_bridge_bars(struct eeh_dev *edev,
|
||||||
* Bus numbers and windows: 0x18 - 0x30
|
* Bus numbers and windows: 0x18 - 0x30
|
||||||
*/
|
*/
|
||||||
for (i = 4; i < 13; i++)
|
for (i = 4; i < 13; i++)
|
||||||
eeh_ops->write_config(dn, i*4, 4, edev->config_space[i]);
|
eeh_ops->write_config(pdn, i*4, 4, edev->config_space[i]);
|
||||||
/* Rom: 0x38 */
|
/* Rom: 0x38 */
|
||||||
eeh_ops->write_config(dn, 14*4, 4, edev->config_space[14]);
|
eeh_ops->write_config(pdn, 14*4, 4, edev->config_space[14]);
|
||||||
|
|
||||||
/* Cache line & Latency timer: 0xC 0xD */
|
/* Cache line & Latency timer: 0xC 0xD */
|
||||||
eeh_ops->write_config(dn, PCI_CACHE_LINE_SIZE, 1,
|
eeh_ops->write_config(pdn, PCI_CACHE_LINE_SIZE, 1,
|
||||||
SAVED_BYTE(PCI_CACHE_LINE_SIZE));
|
SAVED_BYTE(PCI_CACHE_LINE_SIZE));
|
||||||
eeh_ops->write_config(dn, PCI_LATENCY_TIMER, 1,
|
eeh_ops->write_config(pdn, PCI_LATENCY_TIMER, 1,
|
||||||
SAVED_BYTE(PCI_LATENCY_TIMER));
|
SAVED_BYTE(PCI_LATENCY_TIMER));
|
||||||
/* Max latency, min grant, interrupt ping and line: 0x3C */
|
/* Max latency, min grant, interrupt ping and line: 0x3C */
|
||||||
eeh_ops->write_config(dn, 15*4, 4, edev->config_space[15]);
|
eeh_ops->write_config(pdn, 15*4, 4, edev->config_space[15]);
|
||||||
|
|
||||||
/* PCI Command: 0x4 */
|
/* PCI Command: 0x4 */
|
||||||
eeh_ops->write_config(dn, PCI_COMMAND, 4, edev->config_space[1]);
|
eeh_ops->write_config(pdn, PCI_COMMAND, 4, edev->config_space[1]);
|
||||||
|
|
||||||
/* Check the PCIe link is ready */
|
/* Check the PCIe link is ready */
|
||||||
eeh_bridge_check_link(edev, dn);
|
eeh_bridge_check_link(edev);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void eeh_restore_device_bars(struct eeh_dev *edev,
|
static void eeh_restore_device_bars(struct eeh_dev *edev)
|
||||||
struct device_node *dn)
|
|
||||||
{
|
{
|
||||||
|
struct pci_dn *pdn = eeh_dev_to_pdn(edev);
|
||||||
int i;
|
int i;
|
||||||
u32 cmd;
|
u32 cmd;
|
||||||
|
|
||||||
for (i = 4; i < 10; i++)
|
for (i = 4; i < 10; i++)
|
||||||
eeh_ops->write_config(dn, i*4, 4, edev->config_space[i]);
|
eeh_ops->write_config(pdn, i*4, 4, edev->config_space[i]);
|
||||||
/* 12 == Expansion ROM Address */
|
/* 12 == Expansion ROM Address */
|
||||||
eeh_ops->write_config(dn, 12*4, 4, edev->config_space[12]);
|
eeh_ops->write_config(pdn, 12*4, 4, edev->config_space[12]);
|
||||||
|
|
||||||
eeh_ops->write_config(dn, PCI_CACHE_LINE_SIZE, 1,
|
eeh_ops->write_config(pdn, PCI_CACHE_LINE_SIZE, 1,
|
||||||
SAVED_BYTE(PCI_CACHE_LINE_SIZE));
|
SAVED_BYTE(PCI_CACHE_LINE_SIZE));
|
||||||
eeh_ops->write_config(dn, PCI_LATENCY_TIMER, 1,
|
eeh_ops->write_config(pdn, PCI_LATENCY_TIMER, 1,
|
||||||
SAVED_BYTE(PCI_LATENCY_TIMER));
|
SAVED_BYTE(PCI_LATENCY_TIMER));
|
||||||
|
|
||||||
/* max latency, min grant, interrupt pin and line */
|
/* max latency, min grant, interrupt pin and line */
|
||||||
eeh_ops->write_config(dn, 15*4, 4, edev->config_space[15]);
|
eeh_ops->write_config(pdn, 15*4, 4, edev->config_space[15]);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Restore PERR & SERR bits, some devices require it,
|
* Restore PERR & SERR bits, some devices require it,
|
||||||
* don't touch the other command bits
|
* don't touch the other command bits
|
||||||
*/
|
*/
|
||||||
eeh_ops->read_config(dn, PCI_COMMAND, 4, &cmd);
|
eeh_ops->read_config(pdn, PCI_COMMAND, 4, &cmd);
|
||||||
if (edev->config_space[1] & PCI_COMMAND_PARITY)
|
if (edev->config_space[1] & PCI_COMMAND_PARITY)
|
||||||
cmd |= PCI_COMMAND_PARITY;
|
cmd |= PCI_COMMAND_PARITY;
|
||||||
else
|
else
|
||||||
|
@ -789,7 +804,7 @@ static void eeh_restore_device_bars(struct eeh_dev *edev,
|
||||||
cmd |= PCI_COMMAND_SERR;
|
cmd |= PCI_COMMAND_SERR;
|
||||||
else
|
else
|
||||||
cmd &= ~PCI_COMMAND_SERR;
|
cmd &= ~PCI_COMMAND_SERR;
|
||||||
eeh_ops->write_config(dn, PCI_COMMAND, 4, cmd);
|
eeh_ops->write_config(pdn, PCI_COMMAND, 4, cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -804,16 +819,16 @@ static void eeh_restore_device_bars(struct eeh_dev *edev,
|
||||||
static void *eeh_restore_one_device_bars(void *data, void *flag)
|
static void *eeh_restore_one_device_bars(void *data, void *flag)
|
||||||
{
|
{
|
||||||
struct eeh_dev *edev = (struct eeh_dev *)data;
|
struct eeh_dev *edev = (struct eeh_dev *)data;
|
||||||
struct device_node *dn = eeh_dev_to_of_node(edev);
|
struct pci_dn *pdn = eeh_dev_to_pdn(edev);
|
||||||
|
|
||||||
/* Do special restore for bridges */
|
/* Do special restore for bridges */
|
||||||
if (edev->mode & EEH_DEV_BRIDGE)
|
if (edev->mode & EEH_DEV_BRIDGE)
|
||||||
eeh_restore_bridge_bars(edev, dn);
|
eeh_restore_bridge_bars(edev);
|
||||||
else
|
else
|
||||||
eeh_restore_device_bars(edev, dn);
|
eeh_restore_device_bars(edev);
|
||||||
|
|
||||||
if (eeh_ops->restore_config)
|
if (eeh_ops->restore_config && pdn)
|
||||||
eeh_ops->restore_config(dn);
|
eeh_ops->restore_config(pdn);
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,7 +72,7 @@ static int of_pci_phb_probe(struct platform_device *dev)
|
||||||
|
|
||||||
/* Register devices with EEH */
|
/* Register devices with EEH */
|
||||||
if (dev->dev.of_node->child)
|
if (dev->dev.of_node->child)
|
||||||
eeh_add_device_tree_early(dev->dev.of_node);
|
eeh_add_device_tree_early(PCI_DN(dev->dev.of_node));
|
||||||
|
|
||||||
/* Scan the bus */
|
/* Scan the bus */
|
||||||
pcibios_scan_phb(phb);
|
pcibios_scan_phb(phb);
|
||||||
|
|
|
@ -75,7 +75,7 @@ void pcibios_add_pci_devices(struct pci_bus * bus)
|
||||||
struct pci_dev *dev;
|
struct pci_dev *dev;
|
||||||
struct device_node *dn = pci_bus_to_OF_node(bus);
|
struct device_node *dn = pci_bus_to_OF_node(bus);
|
||||||
|
|
||||||
eeh_add_device_tree_early(dn);
|
eeh_add_device_tree_early(PCI_DN(dn));
|
||||||
|
|
||||||
mode = PCI_PROBE_NORMAL;
|
mode = PCI_PROBE_NORMAL;
|
||||||
if (ppc_md.pci_probe_mode)
|
if (ppc_md.pci_probe_mode)
|
||||||
|
|
|
@ -32,12 +32,108 @@
|
||||||
#include <asm/ppc-pci.h>
|
#include <asm/ppc-pci.h>
|
||||||
#include <asm/firmware.h>
|
#include <asm/firmware.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The function is used to find the firmware data of one
|
||||||
|
* specific PCI device, which is attached to the indicated
|
||||||
|
* PCI bus. For VFs, their firmware data is linked to that
|
||||||
|
* one of PF's bridge. For other devices, their firmware
|
||||||
|
* data is linked to that of their bridge.
|
||||||
|
*/
|
||||||
|
static struct pci_dn *pci_bus_to_pdn(struct pci_bus *bus)
|
||||||
|
{
|
||||||
|
struct pci_bus *pbus;
|
||||||
|
struct device_node *dn;
|
||||||
|
struct pci_dn *pdn;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We probably have virtual bus which doesn't
|
||||||
|
* have associated bridge.
|
||||||
|
*/
|
||||||
|
pbus = bus;
|
||||||
|
while (pbus) {
|
||||||
|
if (pci_is_root_bus(pbus) || pbus->self)
|
||||||
|
break;
|
||||||
|
|
||||||
|
pbus = pbus->parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Except virtual bus, all PCI buses should
|
||||||
|
* have device nodes.
|
||||||
|
*/
|
||||||
|
dn = pci_bus_to_OF_node(pbus);
|
||||||
|
pdn = dn ? PCI_DN(dn) : NULL;
|
||||||
|
|
||||||
|
return pdn;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct pci_dn *pci_get_pdn_by_devfn(struct pci_bus *bus,
|
||||||
|
int devfn)
|
||||||
|
{
|
||||||
|
struct device_node *dn = NULL;
|
||||||
|
struct pci_dn *parent, *pdn;
|
||||||
|
struct pci_dev *pdev = NULL;
|
||||||
|
|
||||||
|
/* Fast path: fetch from PCI device */
|
||||||
|
list_for_each_entry(pdev, &bus->devices, bus_list) {
|
||||||
|
if (pdev->devfn == devfn) {
|
||||||
|
if (pdev->dev.archdata.pci_data)
|
||||||
|
return pdev->dev.archdata.pci_data;
|
||||||
|
|
||||||
|
dn = pci_device_to_OF_node(pdev);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fast path: fetch from device node */
|
||||||
|
pdn = dn ? PCI_DN(dn) : NULL;
|
||||||
|
if (pdn)
|
||||||
|
return pdn;
|
||||||
|
|
||||||
|
/* Slow path: fetch from firmware data hierarchy */
|
||||||
|
parent = pci_bus_to_pdn(bus);
|
||||||
|
if (!parent)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
list_for_each_entry(pdn, &parent->child_list, list) {
|
||||||
|
if (pdn->busno == bus->number &&
|
||||||
|
pdn->devfn == devfn)
|
||||||
|
return pdn;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
struct pci_dn *pci_get_pdn(struct pci_dev *pdev)
|
struct pci_dn *pci_get_pdn(struct pci_dev *pdev)
|
||||||
{
|
{
|
||||||
struct device_node *dn = pci_device_to_OF_node(pdev);
|
struct device_node *dn;
|
||||||
if (!dn)
|
struct pci_dn *parent, *pdn;
|
||||||
|
|
||||||
|
/* Search device directly */
|
||||||
|
if (pdev->dev.archdata.pci_data)
|
||||||
|
return pdev->dev.archdata.pci_data;
|
||||||
|
|
||||||
|
/* Check device node */
|
||||||
|
dn = pci_device_to_OF_node(pdev);
|
||||||
|
pdn = dn ? PCI_DN(dn) : NULL;
|
||||||
|
if (pdn)
|
||||||
|
return pdn;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* VFs don't have device nodes. We hook their
|
||||||
|
* firmware data to PF's bridge.
|
||||||
|
*/
|
||||||
|
parent = pci_bus_to_pdn(pdev->bus);
|
||||||
|
if (!parent)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
list_for_each_entry(pdn, &parent->child_list, list) {
|
||||||
|
if (pdn->busno == pdev->bus->number &&
|
||||||
|
pdn->devfn == pdev->devfn)
|
||||||
|
return pdn;
|
||||||
|
}
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
return PCI_DN(dn);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -49,6 +145,7 @@ void *update_dn_pci_info(struct device_node *dn, void *data)
|
||||||
struct pci_controller *phb = data;
|
struct pci_controller *phb = data;
|
||||||
const __be32 *type = of_get_property(dn, "ibm,pci-config-space-type", NULL);
|
const __be32 *type = of_get_property(dn, "ibm,pci-config-space-type", NULL);
|
||||||
const __be32 *regs;
|
const __be32 *regs;
|
||||||
|
struct device_node *parent;
|
||||||
struct pci_dn *pdn;
|
struct pci_dn *pdn;
|
||||||
|
|
||||||
pdn = zalloc_maybe_bootmem(sizeof(*pdn), GFP_KERNEL);
|
pdn = zalloc_maybe_bootmem(sizeof(*pdn), GFP_KERNEL);
|
||||||
|
@ -69,7 +166,25 @@ void *update_dn_pci_info(struct device_node *dn, void *data)
|
||||||
pdn->devfn = (addr >> 8) & 0xff;
|
pdn->devfn = (addr >> 8) & 0xff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* vendor/device IDs and class code */
|
||||||
|
regs = of_get_property(dn, "vendor-id", NULL);
|
||||||
|
pdn->vendor_id = regs ? of_read_number(regs, 1) : 0;
|
||||||
|
regs = of_get_property(dn, "device-id", NULL);
|
||||||
|
pdn->device_id = regs ? of_read_number(regs, 1) : 0;
|
||||||
|
regs = of_get_property(dn, "class-code", NULL);
|
||||||
|
pdn->class_code = regs ? of_read_number(regs, 1) : 0;
|
||||||
|
|
||||||
|
/* Extended config space */
|
||||||
pdn->pci_ext_config_space = (type && of_read_number(type, 1) == 1);
|
pdn->pci_ext_config_space = (type && of_read_number(type, 1) == 1);
|
||||||
|
|
||||||
|
/* Attach to parent node */
|
||||||
|
INIT_LIST_HEAD(&pdn->child_list);
|
||||||
|
INIT_LIST_HEAD(&pdn->list);
|
||||||
|
parent = of_get_parent(dn);
|
||||||
|
pdn->parent = parent ? PCI_DN(parent) : NULL;
|
||||||
|
if (pdn->parent)
|
||||||
|
list_add_tail(&pdn->list, &pdn->parent->child_list);
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,6 +246,46 @@ void *traverse_pci_devices(struct device_node *start, traverse_func pre,
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct pci_dn *pci_dn_next_one(struct pci_dn *root,
|
||||||
|
struct pci_dn *pdn)
|
||||||
|
{
|
||||||
|
struct list_head *next = pdn->child_list.next;
|
||||||
|
|
||||||
|
if (next != &pdn->child_list)
|
||||||
|
return list_entry(next, struct pci_dn, list);
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
if (pdn == root)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
next = pdn->list.next;
|
||||||
|
if (next != &pdn->parent->child_list)
|
||||||
|
break;
|
||||||
|
|
||||||
|
pdn = pdn->parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return list_entry(next, struct pci_dn, list);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *traverse_pci_dn(struct pci_dn *root,
|
||||||
|
void *(*fn)(struct pci_dn *, void *),
|
||||||
|
void *data)
|
||||||
|
{
|
||||||
|
struct pci_dn *pdn = root;
|
||||||
|
void *ret;
|
||||||
|
|
||||||
|
/* Only scan the child nodes */
|
||||||
|
for (pdn = pci_dn_next_one(root, pdn); pdn;
|
||||||
|
pdn = pci_dn_next_one(root, pdn)) {
|
||||||
|
ret = fn(pdn, data);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* pci_devs_phb_init_dynamic - setup pci devices under this PHB
|
* pci_devs_phb_init_dynamic - setup pci devices under this PHB
|
||||||
* phb: pci-to-host bridge (top-level bridge connecting to cpu)
|
* phb: pci-to-host bridge (top-level bridge connecting to cpu)
|
||||||
|
@ -147,8 +302,12 @@ void pci_devs_phb_init_dynamic(struct pci_controller *phb)
|
||||||
/* PHB nodes themselves must not match */
|
/* PHB nodes themselves must not match */
|
||||||
update_dn_pci_info(dn, phb);
|
update_dn_pci_info(dn, phb);
|
||||||
pdn = dn->data;
|
pdn = dn->data;
|
||||||
if (pdn)
|
if (pdn) {
|
||||||
pdn->devfn = pdn->busno = -1;
|
pdn->devfn = pdn->busno = -1;
|
||||||
|
pdn->vendor_id = pdn->device_id = pdn->class_code = 0;
|
||||||
|
pdn->phb = phb;
|
||||||
|
phb->pci_data = pdn;
|
||||||
|
}
|
||||||
|
|
||||||
/* Update dn->phb ptrs for new phb and children devices */
|
/* Update dn->phb ptrs for new phb and children devices */
|
||||||
traverse_pci_devices(dn, update_dn_pci_info, phb);
|
traverse_pci_devices(dn, update_dn_pci_info, phb);
|
||||||
|
@ -171,3 +330,16 @@ void __init pci_devs_phb_init(void)
|
||||||
list_for_each_entry_safe(phb, tmp, &hose_list, list_node)
|
list_for_each_entry_safe(phb, tmp, &hose_list, list_node)
|
||||||
pci_devs_phb_init_dynamic(phb);
|
pci_devs_phb_init_dynamic(phb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void pci_dev_pdn_setup(struct pci_dev *pdev)
|
||||||
|
{
|
||||||
|
struct pci_dn *pdn;
|
||||||
|
|
||||||
|
if (pdev->dev.archdata.pci_data)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Setup the fast path */
|
||||||
|
pdn = pci_get_pdn(pdev);
|
||||||
|
pdev->dev.archdata.pci_data = pdn;
|
||||||
|
}
|
||||||
|
DECLARE_PCI_FIXUP_EARLY(PCI_ANY_ID, PCI_ANY_ID, pci_dev_pdn_setup);
|
||||||
|
|
|
@ -305,7 +305,7 @@ static struct pci_dev *of_scan_pci_dev(struct pci_bus *bus,
|
||||||
const __be32 *reg;
|
const __be32 *reg;
|
||||||
int reglen, devfn;
|
int reglen, devfn;
|
||||||
#ifdef CONFIG_EEH
|
#ifdef CONFIG_EEH
|
||||||
struct eeh_dev *edev = of_node_to_eeh_dev(dn);
|
struct eeh_dev *edev = pdn_to_eeh_dev(PCI_DN(dn));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
pr_debug(" * %s\n", dn->full_name);
|
pr_debug(" * %s\n", dn->full_name);
|
||||||
|
|
|
@ -113,7 +113,7 @@ static int rtas_pci_read_config(struct pci_bus *bus,
|
||||||
|
|
||||||
ret = rtas_read_config(pdn, where, size, val);
|
ret = rtas_read_config(pdn, where, size, val);
|
||||||
if (*val == EEH_IO_ERROR_VALUE(size) &&
|
if (*val == EEH_IO_ERROR_VALUE(size) &&
|
||||||
eeh_dev_check_failure(of_node_to_eeh_dev(dn)))
|
eeh_dev_check_failure(pdn_to_eeh_dev(pdn)))
|
||||||
return PCIBIOS_DEVICE_NOT_FOUND;
|
return PCIBIOS_DEVICE_NOT_FOUND;
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|
|
@ -5,7 +5,7 @@ obj-y += opal-msglog.o opal-hmi.o opal-power.o
|
||||||
|
|
||||||
obj-$(CONFIG_SMP) += smp.o subcore.o subcore-asm.o
|
obj-$(CONFIG_SMP) += smp.o subcore.o subcore-asm.o
|
||||||
obj-$(CONFIG_PCI) += pci.o pci-p5ioc2.o pci-ioda.o
|
obj-$(CONFIG_PCI) += pci.o pci-p5ioc2.o pci-ioda.o
|
||||||
obj-$(CONFIG_EEH) += eeh-ioda.o eeh-powernv.o
|
obj-$(CONFIG_EEH) += eeh-powernv.o
|
||||||
obj-$(CONFIG_PPC_SCOM) += opal-xscom.o
|
obj-$(CONFIG_PPC_SCOM) += opal-xscom.o
|
||||||
obj-$(CONFIG_MEMORY_FAILURE) += opal-memory-errors.o
|
obj-$(CONFIG_MEMORY_FAILURE) += opal-memory-errors.o
|
||||||
obj-$(CONFIG_TRACEPOINTS) += opal-tracepoints.o
|
obj-$(CONFIG_TRACEPOINTS) += opal-tracepoints.o
|
||||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -1777,7 +1777,8 @@ static void pnv_ioda_setup_pe_seg(struct pci_controller *hose,
|
||||||
region.start += phb->ioda.io_segsize;
|
region.start += phb->ioda.io_segsize;
|
||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
} else if (res->flags & IORESOURCE_MEM) {
|
} else if ((res->flags & IORESOURCE_MEM) &&
|
||||||
|
!pnv_pci_is_mem_pref_64(res->flags)) {
|
||||||
region.start = res->start -
|
region.start = res->start -
|
||||||
hose->mem_offset[0] -
|
hose->mem_offset[0] -
|
||||||
phb->ioda.m32_pci_base;
|
phb->ioda.m32_pci_base;
|
||||||
|
@ -2078,9 +2079,6 @@ static void __init pnv_pci_init_ioda_phb(struct device_node *np,
|
||||||
phb->get_pe_state = pnv_ioda_get_pe_state;
|
phb->get_pe_state = pnv_ioda_get_pe_state;
|
||||||
phb->freeze_pe = pnv_ioda_freeze_pe;
|
phb->freeze_pe = pnv_ioda_freeze_pe;
|
||||||
phb->unfreeze_pe = pnv_ioda_unfreeze_pe;
|
phb->unfreeze_pe = pnv_ioda_unfreeze_pe;
|
||||||
#ifdef CONFIG_EEH
|
|
||||||
phb->eeh_ops = &ioda_eeh_ops;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Setup RID -> PE mapping function */
|
/* Setup RID -> PE mapping function */
|
||||||
phb->bdfn_to_pe = pnv_ioda_bdfn_to_pe;
|
phb->bdfn_to_pe = pnv_ioda_bdfn_to_pe;
|
||||||
|
@ -2121,8 +2119,8 @@ static void __init pnv_pci_init_ioda_phb(struct device_node *np,
|
||||||
*/
|
*/
|
||||||
if (is_kdump_kernel()) {
|
if (is_kdump_kernel()) {
|
||||||
pr_info(" Issue PHB reset ...\n");
|
pr_info(" Issue PHB reset ...\n");
|
||||||
ioda_eeh_phb_reset(hose, EEH_RESET_FUNDAMENTAL);
|
pnv_eeh_phb_reset(hose, EEH_RESET_FUNDAMENTAL);
|
||||||
ioda_eeh_phb_reset(hose, EEH_RESET_DEACTIVATE);
|
pnv_eeh_phb_reset(hose, EEH_RESET_DEACTIVATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Remove M64 resource if we can't configure it successfully */
|
/* Remove M64 resource if we can't configure it successfully */
|
||||||
|
|
|
@ -366,9 +366,9 @@ static void pnv_pci_handle_eeh_config(struct pnv_phb *phb, u32 pe_no)
|
||||||
spin_unlock_irqrestore(&phb->lock, flags);
|
spin_unlock_irqrestore(&phb->lock, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pnv_pci_config_check_eeh(struct pnv_phb *phb,
|
static void pnv_pci_config_check_eeh(struct pci_dn *pdn)
|
||||||
struct device_node *dn)
|
|
||||||
{
|
{
|
||||||
|
struct pnv_phb *phb = pdn->phb->private_data;
|
||||||
u8 fstate;
|
u8 fstate;
|
||||||
__be16 pcierr;
|
__be16 pcierr;
|
||||||
int pe_no;
|
int pe_no;
|
||||||
|
@ -379,7 +379,7 @@ static void pnv_pci_config_check_eeh(struct pnv_phb *phb,
|
||||||
* setup that yet. So all ER errors should be mapped to
|
* setup that yet. So all ER errors should be mapped to
|
||||||
* reserved PE.
|
* reserved PE.
|
||||||
*/
|
*/
|
||||||
pe_no = PCI_DN(dn)->pe_number;
|
pe_no = pdn->pe_number;
|
||||||
if (pe_no == IODA_INVALID_PE) {
|
if (pe_no == IODA_INVALID_PE) {
|
||||||
if (phb->type == PNV_PHB_P5IOC2)
|
if (phb->type == PNV_PHB_P5IOC2)
|
||||||
pe_no = 0;
|
pe_no = 0;
|
||||||
|
@ -407,8 +407,7 @@ static void pnv_pci_config_check_eeh(struct pnv_phb *phb,
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg_dbg(" -> EEH check, bdfn=%04x PE#%d fstate=%x\n",
|
cfg_dbg(" -> EEH check, bdfn=%04x PE#%d fstate=%x\n",
|
||||||
(PCI_DN(dn)->busno << 8) | (PCI_DN(dn)->devfn),
|
(pdn->busno << 8) | (pdn->devfn), pe_no, fstate);
|
||||||
pe_no, fstate);
|
|
||||||
|
|
||||||
/* Clear the frozen state if applicable */
|
/* Clear the frozen state if applicable */
|
||||||
if (fstate == OPAL_EEH_STOPPED_MMIO_FREEZE ||
|
if (fstate == OPAL_EEH_STOPPED_MMIO_FREEZE ||
|
||||||
|
@ -425,10 +424,9 @@ static void pnv_pci_config_check_eeh(struct pnv_phb *phb,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int pnv_pci_cfg_read(struct device_node *dn,
|
int pnv_pci_cfg_read(struct pci_dn *pdn,
|
||||||
int where, int size, u32 *val)
|
int where, int size, u32 *val)
|
||||||
{
|
{
|
||||||
struct pci_dn *pdn = PCI_DN(dn);
|
|
||||||
struct pnv_phb *phb = pdn->phb->private_data;
|
struct pnv_phb *phb = pdn->phb->private_data;
|
||||||
u32 bdfn = (pdn->busno << 8) | pdn->devfn;
|
u32 bdfn = (pdn->busno << 8) | pdn->devfn;
|
||||||
s64 rc;
|
s64 rc;
|
||||||
|
@ -462,10 +460,9 @@ int pnv_pci_cfg_read(struct device_node *dn,
|
||||||
return PCIBIOS_SUCCESSFUL;
|
return PCIBIOS_SUCCESSFUL;
|
||||||
}
|
}
|
||||||
|
|
||||||
int pnv_pci_cfg_write(struct device_node *dn,
|
int pnv_pci_cfg_write(struct pci_dn *pdn,
|
||||||
int where, int size, u32 val)
|
int where, int size, u32 val)
|
||||||
{
|
{
|
||||||
struct pci_dn *pdn = PCI_DN(dn);
|
|
||||||
struct pnv_phb *phb = pdn->phb->private_data;
|
struct pnv_phb *phb = pdn->phb->private_data;
|
||||||
u32 bdfn = (pdn->busno << 8) | pdn->devfn;
|
u32 bdfn = (pdn->busno << 8) | pdn->devfn;
|
||||||
|
|
||||||
|
@ -489,18 +486,17 @@ int pnv_pci_cfg_write(struct device_node *dn,
|
||||||
}
|
}
|
||||||
|
|
||||||
#if CONFIG_EEH
|
#if CONFIG_EEH
|
||||||
static bool pnv_pci_cfg_check(struct pci_controller *hose,
|
static bool pnv_pci_cfg_check(struct pci_dn *pdn)
|
||||||
struct device_node *dn)
|
|
||||||
{
|
{
|
||||||
struct eeh_dev *edev = NULL;
|
struct eeh_dev *edev = NULL;
|
||||||
struct pnv_phb *phb = hose->private_data;
|
struct pnv_phb *phb = pdn->phb->private_data;
|
||||||
|
|
||||||
/* EEH not enabled ? */
|
/* EEH not enabled ? */
|
||||||
if (!(phb->flags & PNV_PHB_FLAG_EEH))
|
if (!(phb->flags & PNV_PHB_FLAG_EEH))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
/* PE reset or device removed ? */
|
/* PE reset or device removed ? */
|
||||||
edev = of_node_to_eeh_dev(dn);
|
edev = pdn->edev;
|
||||||
if (edev) {
|
if (edev) {
|
||||||
if (edev->pe &&
|
if (edev->pe &&
|
||||||
(edev->pe->state & EEH_PE_CFG_BLOCKED))
|
(edev->pe->state & EEH_PE_CFG_BLOCKED))
|
||||||
|
@ -513,8 +509,7 @@ static bool pnv_pci_cfg_check(struct pci_controller *hose,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
static inline pnv_pci_cfg_check(struct pci_controller *hose,
|
static inline pnv_pci_cfg_check(struct pci_dn *pdn)
|
||||||
struct device_node *dn)
|
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -524,32 +519,26 @@ static int pnv_pci_read_config(struct pci_bus *bus,
|
||||||
unsigned int devfn,
|
unsigned int devfn,
|
||||||
int where, int size, u32 *val)
|
int where, int size, u32 *val)
|
||||||
{
|
{
|
||||||
struct device_node *dn, *busdn = pci_bus_to_OF_node(bus);
|
|
||||||
struct pci_dn *pdn;
|
struct pci_dn *pdn;
|
||||||
struct pnv_phb *phb;
|
struct pnv_phb *phb;
|
||||||
bool found = false;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
*val = 0xFFFFFFFF;
|
*val = 0xFFFFFFFF;
|
||||||
for (dn = busdn->child; dn; dn = dn->sibling) {
|
pdn = pci_get_pdn_by_devfn(bus, devfn);
|
||||||
pdn = PCI_DN(dn);
|
if (!pdn)
|
||||||
if (pdn && pdn->devfn == devfn) {
|
|
||||||
phb = pdn->phb->private_data;
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!found || !pnv_pci_cfg_check(pdn->phb, dn))
|
|
||||||
return PCIBIOS_DEVICE_NOT_FOUND;
|
return PCIBIOS_DEVICE_NOT_FOUND;
|
||||||
|
|
||||||
ret = pnv_pci_cfg_read(dn, where, size, val);
|
if (!pnv_pci_cfg_check(pdn))
|
||||||
if (phb->flags & PNV_PHB_FLAG_EEH) {
|
return PCIBIOS_DEVICE_NOT_FOUND;
|
||||||
|
|
||||||
|
ret = pnv_pci_cfg_read(pdn, where, size, val);
|
||||||
|
phb = pdn->phb->private_data;
|
||||||
|
if (phb->flags & PNV_PHB_FLAG_EEH && pdn->edev) {
|
||||||
if (*val == EEH_IO_ERROR_VALUE(size) &&
|
if (*val == EEH_IO_ERROR_VALUE(size) &&
|
||||||
eeh_dev_check_failure(of_node_to_eeh_dev(dn)))
|
eeh_dev_check_failure(pdn->edev))
|
||||||
return PCIBIOS_DEVICE_NOT_FOUND;
|
return PCIBIOS_DEVICE_NOT_FOUND;
|
||||||
} else {
|
} else {
|
||||||
pnv_pci_config_check_eeh(phb, dn);
|
pnv_pci_config_check_eeh(pdn);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -559,27 +548,21 @@ static int pnv_pci_write_config(struct pci_bus *bus,
|
||||||
unsigned int devfn,
|
unsigned int devfn,
|
||||||
int where, int size, u32 val)
|
int where, int size, u32 val)
|
||||||
{
|
{
|
||||||
struct device_node *dn, *busdn = pci_bus_to_OF_node(bus);
|
|
||||||
struct pci_dn *pdn;
|
struct pci_dn *pdn;
|
||||||
struct pnv_phb *phb;
|
struct pnv_phb *phb;
|
||||||
bool found = false;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
for (dn = busdn->child; dn; dn = dn->sibling) {
|
pdn = pci_get_pdn_by_devfn(bus, devfn);
|
||||||
pdn = PCI_DN(dn);
|
if (!pdn)
|
||||||
if (pdn && pdn->devfn == devfn) {
|
|
||||||
phb = pdn->phb->private_data;
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!found || !pnv_pci_cfg_check(pdn->phb, dn))
|
|
||||||
return PCIBIOS_DEVICE_NOT_FOUND;
|
return PCIBIOS_DEVICE_NOT_FOUND;
|
||||||
|
|
||||||
ret = pnv_pci_cfg_write(dn, where, size, val);
|
if (!pnv_pci_cfg_check(pdn))
|
||||||
|
return PCIBIOS_DEVICE_NOT_FOUND;
|
||||||
|
|
||||||
|
ret = pnv_pci_cfg_write(pdn, where, size, val);
|
||||||
|
phb = pdn->phb->private_data;
|
||||||
if (!(phb->flags & PNV_PHB_FLAG_EEH))
|
if (!(phb->flags & PNV_PHB_FLAG_EEH))
|
||||||
pnv_pci_config_check_eeh(phb, dn);
|
pnv_pci_config_check_eeh(pdn);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,22 +75,6 @@ struct pnv_ioda_pe {
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* IOC dependent EEH operations */
|
|
||||||
#ifdef CONFIG_EEH
|
|
||||||
struct pnv_eeh_ops {
|
|
||||||
int (*post_init)(struct pci_controller *hose);
|
|
||||||
int (*set_option)(struct eeh_pe *pe, int option);
|
|
||||||
int (*get_state)(struct eeh_pe *pe);
|
|
||||||
int (*reset)(struct eeh_pe *pe, int option);
|
|
||||||
int (*get_log)(struct eeh_pe *pe, int severity,
|
|
||||||
char *drv_log, unsigned long len);
|
|
||||||
int (*configure_bridge)(struct eeh_pe *pe);
|
|
||||||
int (*err_inject)(struct eeh_pe *pe, int type, int func,
|
|
||||||
unsigned long addr, unsigned long mask);
|
|
||||||
int (*next_error)(struct eeh_pe **pe);
|
|
||||||
};
|
|
||||||
#endif /* CONFIG_EEH */
|
|
||||||
|
|
||||||
#define PNV_PHB_FLAG_EEH (1 << 0)
|
#define PNV_PHB_FLAG_EEH (1 << 0)
|
||||||
|
|
||||||
struct pnv_phb {
|
struct pnv_phb {
|
||||||
|
@ -104,10 +88,6 @@ struct pnv_phb {
|
||||||
int initialized;
|
int initialized;
|
||||||
spinlock_t lock;
|
spinlock_t lock;
|
||||||
|
|
||||||
#ifdef CONFIG_EEH
|
|
||||||
struct pnv_eeh_ops *eeh_ops;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef CONFIG_DEBUG_FS
|
#ifdef CONFIG_DEBUG_FS
|
||||||
int has_dbgfs;
|
int has_dbgfs;
|
||||||
struct dentry *dbgfs;
|
struct dentry *dbgfs;
|
||||||
|
@ -213,15 +193,12 @@ struct pnv_phb {
|
||||||
};
|
};
|
||||||
|
|
||||||
extern struct pci_ops pnv_pci_ops;
|
extern struct pci_ops pnv_pci_ops;
|
||||||
#ifdef CONFIG_EEH
|
|
||||||
extern struct pnv_eeh_ops ioda_eeh_ops;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void pnv_pci_dump_phb_diag_data(struct pci_controller *hose,
|
void pnv_pci_dump_phb_diag_data(struct pci_controller *hose,
|
||||||
unsigned char *log_buff);
|
unsigned char *log_buff);
|
||||||
int pnv_pci_cfg_read(struct device_node *dn,
|
int pnv_pci_cfg_read(struct pci_dn *pdn,
|
||||||
int where, int size, u32 *val);
|
int where, int size, u32 *val);
|
||||||
int pnv_pci_cfg_write(struct device_node *dn,
|
int pnv_pci_cfg_write(struct pci_dn *pdn,
|
||||||
int where, int size, u32 val);
|
int where, int size, u32 val);
|
||||||
extern void pnv_pci_setup_iommu_table(struct iommu_table *tbl,
|
extern void pnv_pci_setup_iommu_table(struct iommu_table *tbl,
|
||||||
void *tce_mem, u64 tce_size,
|
void *tce_mem, u64 tce_size,
|
||||||
|
@ -232,6 +209,6 @@ extern void pnv_pci_init_ioda2_phb(struct device_node *np);
|
||||||
extern void pnv_pci_ioda_tce_invalidate(struct iommu_table *tbl,
|
extern void pnv_pci_ioda_tce_invalidate(struct iommu_table *tbl,
|
||||||
__be64 *startp, __be64 *endp, bool rm);
|
__be64 *startp, __be64 *endp, bool rm);
|
||||||
extern void pnv_pci_reset_secondary_bus(struct pci_dev *dev);
|
extern void pnv_pci_reset_secondary_bus(struct pci_dev *dev);
|
||||||
extern int ioda_eeh_phb_reset(struct pci_controller *hose, int option);
|
extern int pnv_eeh_phb_reset(struct pci_controller *hose, int option);
|
||||||
|
|
||||||
#endif /* __POWERNV_PCI_H */
|
#endif /* __POWERNV_PCI_H */
|
||||||
|
|
|
@ -118,9 +118,8 @@ static int pseries_eeh_init(void)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int pseries_eeh_cap_start(struct device_node *dn)
|
static int pseries_eeh_cap_start(struct pci_dn *pdn)
|
||||||
{
|
{
|
||||||
struct pci_dn *pdn = PCI_DN(dn);
|
|
||||||
u32 status;
|
u32 status;
|
||||||
|
|
||||||
if (!pdn)
|
if (!pdn)
|
||||||
|
@ -134,10 +133,9 @@ static int pseries_eeh_cap_start(struct device_node *dn)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int pseries_eeh_find_cap(struct device_node *dn, int cap)
|
static int pseries_eeh_find_cap(struct pci_dn *pdn, int cap)
|
||||||
{
|
{
|
||||||
struct pci_dn *pdn = PCI_DN(dn);
|
int pos = pseries_eeh_cap_start(pdn);
|
||||||
int pos = pseries_eeh_cap_start(dn);
|
|
||||||
int cnt = 48; /* Maximal number of capabilities */
|
int cnt = 48; /* Maximal number of capabilities */
|
||||||
u32 id;
|
u32 id;
|
||||||
|
|
||||||
|
@ -160,10 +158,9 @@ static int pseries_eeh_find_cap(struct device_node *dn, int cap)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int pseries_eeh_find_ecap(struct device_node *dn, int cap)
|
static int pseries_eeh_find_ecap(struct pci_dn *pdn, int cap)
|
||||||
{
|
{
|
||||||
struct pci_dn *pdn = PCI_DN(dn);
|
struct eeh_dev *edev = pdn_to_eeh_dev(pdn);
|
||||||
struct eeh_dev *edev = of_node_to_eeh_dev(dn);
|
|
||||||
u32 header;
|
u32 header;
|
||||||
int pos = 256;
|
int pos = 256;
|
||||||
int ttl = (4096 - 256) / 8;
|
int ttl = (4096 - 256) / 8;
|
||||||
|
@ -191,53 +188,44 @@ static int pseries_eeh_find_ecap(struct device_node *dn, int cap)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* pseries_eeh_of_probe - EEH probe on the given device
|
* pseries_eeh_probe - EEH probe on the given device
|
||||||
* @dn: OF node
|
* @pdn: PCI device node
|
||||||
* @flag: Unused
|
* @data: Unused
|
||||||
*
|
*
|
||||||
* When EEH module is installed during system boot, all PCI devices
|
* When EEH module is installed during system boot, all PCI devices
|
||||||
* are checked one by one to see if it supports EEH. The function
|
* are checked one by one to see if it supports EEH. The function
|
||||||
* is introduced for the purpose.
|
* is introduced for the purpose.
|
||||||
*/
|
*/
|
||||||
static void *pseries_eeh_of_probe(struct device_node *dn, void *flag)
|
static void *pseries_eeh_probe(struct pci_dn *pdn, void *data)
|
||||||
{
|
{
|
||||||
struct eeh_dev *edev;
|
struct eeh_dev *edev;
|
||||||
struct eeh_pe pe;
|
struct eeh_pe pe;
|
||||||
struct pci_dn *pdn = PCI_DN(dn);
|
|
||||||
const __be32 *classp, *vendorp, *devicep;
|
|
||||||
u32 class_code;
|
|
||||||
const __be32 *regs;
|
|
||||||
u32 pcie_flags;
|
u32 pcie_flags;
|
||||||
int enable = 0;
|
int enable = 0;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
/* Retrieve OF node and eeh device */
|
/* Retrieve OF node and eeh device */
|
||||||
edev = of_node_to_eeh_dev(dn);
|
edev = pdn_to_eeh_dev(pdn);
|
||||||
if (edev->pe || !of_device_is_available(dn))
|
if (!edev || edev->pe)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
/* Retrieve class/vendor/device IDs */
|
/* Check class/vendor/device IDs */
|
||||||
classp = of_get_property(dn, "class-code", NULL);
|
if (!pdn->vendor_id || !pdn->device_id || !pdn->class_code)
|
||||||
vendorp = of_get_property(dn, "vendor-id", NULL);
|
|
||||||
devicep = of_get_property(dn, "device-id", NULL);
|
|
||||||
|
|
||||||
/* Skip for bad OF node or PCI-ISA bridge */
|
|
||||||
if (!classp || !vendorp || !devicep)
|
|
||||||
return NULL;
|
|
||||||
if (dn->type && !strcmp(dn->type, "isa"))
|
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
class_code = of_read_number(classp, 1);
|
/* Skip for PCI-ISA bridge */
|
||||||
|
if ((pdn->class_code >> 8) == PCI_CLASS_BRIDGE_ISA)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Update class code and mode of eeh device. We need
|
* Update class code and mode of eeh device. We need
|
||||||
* correctly reflects that current device is root port
|
* correctly reflects that current device is root port
|
||||||
* or PCIe switch downstream port.
|
* or PCIe switch downstream port.
|
||||||
*/
|
*/
|
||||||
edev->class_code = class_code;
|
edev->class_code = pdn->class_code;
|
||||||
edev->pcix_cap = pseries_eeh_find_cap(dn, PCI_CAP_ID_PCIX);
|
edev->pcix_cap = pseries_eeh_find_cap(pdn, PCI_CAP_ID_PCIX);
|
||||||
edev->pcie_cap = pseries_eeh_find_cap(dn, PCI_CAP_ID_EXP);
|
edev->pcie_cap = pseries_eeh_find_cap(pdn, PCI_CAP_ID_EXP);
|
||||||
edev->aer_cap = pseries_eeh_find_ecap(dn, PCI_EXT_CAP_ID_ERR);
|
edev->aer_cap = pseries_eeh_find_ecap(pdn, PCI_EXT_CAP_ID_ERR);
|
||||||
edev->mode &= 0xFFFFFF00;
|
edev->mode &= 0xFFFFFF00;
|
||||||
if ((edev->class_code >> 8) == PCI_CLASS_BRIDGE_PCI) {
|
if ((edev->class_code >> 8) == PCI_CLASS_BRIDGE_PCI) {
|
||||||
edev->mode |= EEH_DEV_BRIDGE;
|
edev->mode |= EEH_DEV_BRIDGE;
|
||||||
|
@ -252,24 +240,16 @@ static void *pseries_eeh_of_probe(struct device_node *dn, void *flag)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Retrieve the device address */
|
|
||||||
regs = of_get_property(dn, "reg", NULL);
|
|
||||||
if (!regs) {
|
|
||||||
pr_warn("%s: OF node property %s::reg not found\n",
|
|
||||||
__func__, dn->full_name);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Initialize the fake PE */
|
/* Initialize the fake PE */
|
||||||
memset(&pe, 0, sizeof(struct eeh_pe));
|
memset(&pe, 0, sizeof(struct eeh_pe));
|
||||||
pe.phb = edev->phb;
|
pe.phb = edev->phb;
|
||||||
pe.config_addr = of_read_number(regs, 1);
|
pe.config_addr = (pdn->busno << 16) | (pdn->devfn << 8);
|
||||||
|
|
||||||
/* Enable EEH on the device */
|
/* Enable EEH on the device */
|
||||||
ret = eeh_ops->set_option(&pe, EEH_OPT_ENABLE);
|
ret = eeh_ops->set_option(&pe, EEH_OPT_ENABLE);
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
edev->config_addr = of_read_number(regs, 1);
|
|
||||||
/* Retrieve PE address */
|
/* Retrieve PE address */
|
||||||
|
edev->config_addr = (pdn->busno << 16) | (pdn->devfn << 8);
|
||||||
edev->pe_config_addr = eeh_ops->get_pe_addr(&pe);
|
edev->pe_config_addr = eeh_ops->get_pe_addr(&pe);
|
||||||
pe.addr = edev->pe_config_addr;
|
pe.addr = edev->pe_config_addr;
|
||||||
|
|
||||||
|
@ -285,16 +265,17 @@ static void *pseries_eeh_of_probe(struct device_node *dn, void *flag)
|
||||||
eeh_add_flag(EEH_ENABLED);
|
eeh_add_flag(EEH_ENABLED);
|
||||||
eeh_add_to_parent_pe(edev);
|
eeh_add_to_parent_pe(edev);
|
||||||
|
|
||||||
pr_debug("%s: EEH enabled on %s PHB#%d-PE#%x, config addr#%x\n",
|
pr_debug("%s: EEH enabled on %02x:%02x.%01x PHB#%d-PE#%x\n",
|
||||||
__func__, dn->full_name, pe.phb->global_number,
|
__func__, pdn->busno, PCI_SLOT(pdn->devfn),
|
||||||
pe.addr, pe.config_addr);
|
PCI_FUNC(pdn->devfn), pe.phb->global_number,
|
||||||
} else if (dn->parent && of_node_to_eeh_dev(dn->parent) &&
|
pe.addr);
|
||||||
(of_node_to_eeh_dev(dn->parent))->pe) {
|
} else if (pdn->parent && pdn_to_eeh_dev(pdn->parent) &&
|
||||||
|
(pdn_to_eeh_dev(pdn->parent))->pe) {
|
||||||
/* This device doesn't support EEH, but it may have an
|
/* This device doesn't support EEH, but it may have an
|
||||||
* EEH parent, in which case we mark it as supported.
|
* EEH parent, in which case we mark it as supported.
|
||||||
*/
|
*/
|
||||||
edev->config_addr = of_node_to_eeh_dev(dn->parent)->config_addr;
|
edev->config_addr = pdn_to_eeh_dev(pdn->parent)->config_addr;
|
||||||
edev->pe_config_addr = of_node_to_eeh_dev(dn->parent)->pe_config_addr;
|
edev->pe_config_addr = pdn_to_eeh_dev(pdn->parent)->pe_config_addr;
|
||||||
eeh_add_to_parent_pe(edev);
|
eeh_add_to_parent_pe(edev);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -670,45 +651,36 @@ static int pseries_eeh_configure_bridge(struct eeh_pe *pe)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* pseries_eeh_read_config - Read PCI config space
|
* pseries_eeh_read_config - Read PCI config space
|
||||||
* @dn: device node
|
* @pdn: PCI device node
|
||||||
* @where: PCI address
|
* @where: PCI address
|
||||||
* @size: size to read
|
* @size: size to read
|
||||||
* @val: return value
|
* @val: return value
|
||||||
*
|
*
|
||||||
* Read config space from the speicifed device
|
* Read config space from the speicifed device
|
||||||
*/
|
*/
|
||||||
static int pseries_eeh_read_config(struct device_node *dn, int where, int size, u32 *val)
|
static int pseries_eeh_read_config(struct pci_dn *pdn, int where, int size, u32 *val)
|
||||||
{
|
{
|
||||||
struct pci_dn *pdn;
|
|
||||||
|
|
||||||
pdn = PCI_DN(dn);
|
|
||||||
|
|
||||||
return rtas_read_config(pdn, where, size, val);
|
return rtas_read_config(pdn, where, size, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* pseries_eeh_write_config - Write PCI config space
|
* pseries_eeh_write_config - Write PCI config space
|
||||||
* @dn: device node
|
* @pdn: PCI device node
|
||||||
* @where: PCI address
|
* @where: PCI address
|
||||||
* @size: size to write
|
* @size: size to write
|
||||||
* @val: value to be written
|
* @val: value to be written
|
||||||
*
|
*
|
||||||
* Write config space to the specified device
|
* Write config space to the specified device
|
||||||
*/
|
*/
|
||||||
static int pseries_eeh_write_config(struct device_node *dn, int where, int size, u32 val)
|
static int pseries_eeh_write_config(struct pci_dn *pdn, int where, int size, u32 val)
|
||||||
{
|
{
|
||||||
struct pci_dn *pdn;
|
|
||||||
|
|
||||||
pdn = PCI_DN(dn);
|
|
||||||
|
|
||||||
return rtas_write_config(pdn, where, size, val);
|
return rtas_write_config(pdn, where, size, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct eeh_ops pseries_eeh_ops = {
|
static struct eeh_ops pseries_eeh_ops = {
|
||||||
.name = "pseries",
|
.name = "pseries",
|
||||||
.init = pseries_eeh_init,
|
.init = pseries_eeh_init,
|
||||||
.of_probe = pseries_eeh_of_probe,
|
.probe = pseries_eeh_probe,
|
||||||
.dev_probe = NULL,
|
|
||||||
.set_option = pseries_eeh_set_option,
|
.set_option = pseries_eeh_set_option,
|
||||||
.get_pe_addr = pseries_eeh_get_pe_addr,
|
.get_pe_addr = pseries_eeh_get_pe_addr,
|
||||||
.get_state = pseries_eeh_get_state,
|
.get_state = pseries_eeh_get_state,
|
||||||
|
|
|
@ -195,6 +195,7 @@ static struct device_node *find_pe_total_msi(struct pci_dev *dev, int *total)
|
||||||
static struct device_node *find_pe_dn(struct pci_dev *dev, int *total)
|
static struct device_node *find_pe_dn(struct pci_dev *dev, int *total)
|
||||||
{
|
{
|
||||||
struct device_node *dn;
|
struct device_node *dn;
|
||||||
|
struct pci_dn *pdn;
|
||||||
struct eeh_dev *edev;
|
struct eeh_dev *edev;
|
||||||
|
|
||||||
/* Found our PE and assume 8 at that point. */
|
/* Found our PE and assume 8 at that point. */
|
||||||
|
@ -204,10 +205,11 @@ static struct device_node *find_pe_dn(struct pci_dev *dev, int *total)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
/* Get the top level device in the PE */
|
/* Get the top level device in the PE */
|
||||||
edev = of_node_to_eeh_dev(dn);
|
edev = pdn_to_eeh_dev(PCI_DN(dn));
|
||||||
if (edev->pe)
|
if (edev->pe)
|
||||||
edev = list_first_entry(&edev->pe->edevs, struct eeh_dev, list);
|
edev = list_first_entry(&edev->pe->edevs, struct eeh_dev, list);
|
||||||
dn = eeh_dev_to_of_node(edev);
|
pdn = eeh_dev_to_pdn(edev);
|
||||||
|
dn = pdn ? pdn->node : NULL;
|
||||||
if (!dn)
|
if (!dn)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
|
|
@ -82,7 +82,7 @@ struct pci_controller *init_phb_dynamic(struct device_node *dn)
|
||||||
eeh_dev_phb_init_dynamic(phb);
|
eeh_dev_phb_init_dynamic(phb);
|
||||||
|
|
||||||
if (dn->child)
|
if (dn->child)
|
||||||
eeh_add_device_tree_early(dn);
|
eeh_add_device_tree_early(PCI_DN(dn));
|
||||||
|
|
||||||
pcibios_scan_phb(phb);
|
pcibios_scan_phb(phb);
|
||||||
pcibios_finish_adding_to_bus(phb->bus);
|
pcibios_finish_adding_to_bus(phb->bus);
|
||||||
|
|
|
@ -265,7 +265,7 @@ static int pci_dn_reconfig_notifier(struct notifier_block *nb, unsigned long act
|
||||||
update_dn_pci_info(np, pci->phb);
|
update_dn_pci_info(np, pci->phb);
|
||||||
|
|
||||||
/* Create EEH device for the OF node */
|
/* Create EEH device for the OF node */
|
||||||
eeh_dev_init(np, pci->phb);
|
eeh_dev_init(PCI_DN(np), pci->phb);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -2523,9 +2523,7 @@ int efx_try_recovery(struct efx_nic *efx)
|
||||||
* schedule a 'recover or reset', leading to this recovery handler.
|
* schedule a 'recover or reset', leading to this recovery handler.
|
||||||
* Manually call the eeh failure check function.
|
* Manually call the eeh failure check function.
|
||||||
*/
|
*/
|
||||||
struct eeh_dev *eehdev =
|
struct eeh_dev *eehdev = pci_dev_to_eeh_dev(efx->pci_dev);
|
||||||
of_node_to_eeh_dev(pci_device_to_OF_node(efx->pci_dev));
|
|
||||||
|
|
||||||
if (eeh_dev_check_failure(eehdev)) {
|
if (eeh_dev_check_failure(eehdev)) {
|
||||||
/* The EEH mechanisms will handle the error and reset the
|
/* The EEH mechanisms will handle the error and reset the
|
||||||
* device if necessary.
|
* device if necessary.
|
||||||
|
|
|
@ -205,8 +205,7 @@ static int siena_map_reset_flags(u32 *flags)
|
||||||
*/
|
*/
|
||||||
static void siena_monitor(struct efx_nic *efx)
|
static void siena_monitor(struct efx_nic *efx)
|
||||||
{
|
{
|
||||||
struct eeh_dev *eehdev =
|
struct eeh_dev *eehdev = pci_dev_to_eeh_dev(efx->pci_dev);
|
||||||
of_node_to_eeh_dev(pci_device_to_OF_node(efx->pci_dev));
|
|
||||||
|
|
||||||
eeh_dev_check_failure(eehdev);
|
eeh_dev_check_failure(eehdev);
|
||||||
}
|
}
|
||||||
|
|
|
@ -146,7 +146,7 @@ static void dlpar_pci_add_bus(struct device_node *dn)
|
||||||
struct pci_controller *phb = pdn->phb;
|
struct pci_controller *phb = pdn->phb;
|
||||||
struct pci_dev *dev = NULL;
|
struct pci_dev *dev = NULL;
|
||||||
|
|
||||||
eeh_add_device_tree_early(dn);
|
eeh_add_device_tree_early(pdn);
|
||||||
|
|
||||||
/* Add EADS device to PHB bus, adding new entry to bus->devices */
|
/* Add EADS device to PHB bus, adding new entry to bus->devices */
|
||||||
dev = of_create_pci_dev(dn, phb->bus, pdn->devfn);
|
dev = of_create_pci_dev(dn, phb->bus, pdn->devfn);
|
||||||
|
|
Loading…
Add table
Reference in a new issue