Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
linux
Commits
aba0eb84
Commit
aba0eb84
authored
Mar 13, 2012
by
Benjamin Herrenschmidt
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'eeh' into next
parents
7230c564
3780444c
Changes
19
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
19 changed files
with
1479 additions
and
869 deletions
+1479
-869
arch/powerpc/include/asm/device.h
arch/powerpc/include/asm/device.h
+3
-0
arch/powerpc/include/asm/eeh.h
arch/powerpc/include/asm/eeh.h
+106
-28
arch/powerpc/include/asm/eeh_event.h
arch/powerpc/include/asm/eeh_event.h
+9
-24
arch/powerpc/include/asm/ppc-pci.h
arch/powerpc/include/asm/ppc-pci.h
+9
-80
arch/powerpc/kernel/of_platform.c
arch/powerpc/kernel/of_platform.c
+5
-1
arch/powerpc/kernel/rtas_pci.c
arch/powerpc/kernel/rtas_pci.c
+3
-0
arch/powerpc/platforms/pseries/Makefile
arch/powerpc/platforms/pseries/Makefile
+2
-1
arch/powerpc/platforms/pseries/eeh.c
arch/powerpc/platforms/pseries/eeh.c
+462
-582
arch/powerpc/platforms/pseries/eeh_cache.c
arch/powerpc/platforms/pseries/eeh_cache.c
+27
-17
arch/powerpc/platforms/pseries/eeh_dev.c
arch/powerpc/platforms/pseries/eeh_dev.c
+102
-0
arch/powerpc/platforms/pseries/eeh_driver.c
arch/powerpc/platforms/pseries/eeh_driver.c
+120
-93
arch/powerpc/platforms/pseries/eeh_event.c
arch/powerpc/platforms/pseries/eeh_event.c
+28
-27
arch/powerpc/platforms/pseries/eeh_pseries.c
arch/powerpc/platforms/pseries/eeh_pseries.c
+565
-0
arch/powerpc/platforms/pseries/eeh_sysfs.c
arch/powerpc/platforms/pseries/eeh_sysfs.c
+11
-14
arch/powerpc/platforms/pseries/msi.c
arch/powerpc/platforms/pseries/msi.c
+1
-1
arch/powerpc/platforms/pseries/pci_dlpar.c
arch/powerpc/platforms/pseries/pci_dlpar.c
+3
-0
arch/powerpc/platforms/pseries/setup.c
arch/powerpc/platforms/pseries/setup.c
+6
-1
include/linux/of.h
include/linux/of.h
+10
-0
include/linux/pci.h
include/linux/pci.h
+7
-0
No files found.
arch/powerpc/include/asm/device.h
View file @
aba0eb84
...
...
@@ -31,6 +31,9 @@ struct dev_archdata {
#ifdef CONFIG_SWIOTLB
dma_addr_t
max_direct_dma_addr
;
#endif
#ifdef CONFIG_EEH
struct
eeh_dev
*
edev
;
#endif
};
struct
pdev_archdata
{
...
...
arch/powerpc/include/asm/eeh.h
View file @
aba0eb84
/*
* eeh.h
* Copyright (C) 2001 Dave Engebretsen & Todd Inglett IBM Corporation.
* Copyright 2001-2012 IBM Corporation.
*
* 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
...
...
@@ -31,44 +31,105 @@ struct device_node;
#ifdef CONFIG_EEH
extern
int
eeh_subsystem_enabled
;
/*
* The struct is used to trace EEH state for the associated
* PCI device node or PCI device. In future, it might
* represent PE as well so that the EEH device to form
* another tree except the currently existing tree of PCI
* buses and PCI devices
*/
#define EEH_MODE_SUPPORTED (1<<0)
/* EEH supported on the device */
#define EEH_MODE_NOCHECK (1<<1)
/* EEH check should be skipped */
#define EEH_MODE_ISOLATED (1<<2)
/* The device has been isolated */
#define EEH_MODE_RECOVERING (1<<3)
/* Recovering the device */
#define EEH_MODE_IRQ_DISABLED (1<<4)
/* Interrupt disabled */
struct
eeh_dev
{
int
mode
;
/* EEH mode */
int
class_code
;
/* Class code of the device */
int
config_addr
;
/* Config address */
int
pe_config_addr
;
/* PE config address */
int
check_count
;
/* Times of ignored error */
int
freeze_count
;
/* Times of froze up */
int
false_positives
;
/* Times of reported #ff's */
u32
config_space
[
16
];
/* Saved PCI config space */
struct
pci_controller
*
phb
;
/* Associated PHB */
struct
device_node
*
dn
;
/* Associated device node */
struct
pci_dev
*
pdev
;
/* Associated PCI device */
};
static
inline
struct
device_node
*
eeh_dev_to_of_node
(
struct
eeh_dev
*
edev
)
{
return
edev
->
dn
;
}
static
inline
struct
pci_dev
*
eeh_dev_to_pci_dev
(
struct
eeh_dev
*
edev
)
{
return
edev
->
pdev
;
}
/* Values for eeh_mode bits in device_node */
#define EEH_MODE_SUPPORTED (1<<0)
#define EEH_MODE_NOCHECK (1<<1)
#define EEH_MODE_ISOLATED (1<<2)
#define EEH_MODE_RECOVERING (1<<3)
#define EEH_MODE_IRQ_DISABLED (1<<4)
/*
* The struct is used to trace the registered EEH operation
* callback functions. Actually, those operation callback
* functions are heavily platform dependent. That means the
* platform should register its own EEH operation callback
* functions before any EEH further operations.
*/
#define EEH_OPT_DISABLE 0
/* EEH disable */
#define EEH_OPT_ENABLE 1
/* EEH enable */
#define EEH_OPT_THAW_MMIO 2
/* MMIO enable */
#define EEH_OPT_THAW_DMA 3
/* DMA enable */
#define EEH_STATE_UNAVAILABLE (1 << 0)
/* State unavailable */
#define EEH_STATE_NOT_SUPPORT (1 << 1)
/* EEH not supported */
#define EEH_STATE_RESET_ACTIVE (1 << 2)
/* Active reset */
#define EEH_STATE_MMIO_ACTIVE (1 << 3)
/* Active MMIO */
#define EEH_STATE_DMA_ACTIVE (1 << 4)
/* Active DMA */
#define EEH_STATE_MMIO_ENABLED (1 << 5)
/* MMIO enabled */
#define EEH_STATE_DMA_ENABLED (1 << 6)
/* DMA enabled */
#define EEH_RESET_DEACTIVATE 0
/* Deactivate the PE reset */
#define EEH_RESET_HOT 1
/* Hot reset */
#define EEH_RESET_FUNDAMENTAL 3
/* Fundamental reset */
#define EEH_LOG_TEMP 1
/* EEH temporary error log */
#define EEH_LOG_PERM 2
/* EEH permanent error log */
struct
eeh_ops
{
char
*
name
;
int
(
*
init
)(
void
);
int
(
*
set_option
)(
struct
device_node
*
dn
,
int
option
);
int
(
*
get_pe_addr
)(
struct
device_node
*
dn
);
int
(
*
get_state
)(
struct
device_node
*
dn
,
int
*
state
);
int
(
*
reset
)(
struct
device_node
*
dn
,
int
option
);
int
(
*
wait_state
)(
struct
device_node
*
dn
,
int
max_wait
);
int
(
*
get_log
)(
struct
device_node
*
dn
,
int
severity
,
char
*
drv_log
,
unsigned
long
len
);
int
(
*
configure_bridge
)(
struct
device_node
*
dn
);
int
(
*
read_config
)(
struct
device_node
*
dn
,
int
where
,
int
size
,
u32
*
val
);
int
(
*
write_config
)(
struct
device_node
*
dn
,
int
where
,
int
size
,
u32
val
);
};
extern
struct
eeh_ops
*
eeh_ops
;
extern
int
eeh_subsystem_enabled
;
/* Max number of EEH freezes allowed before we consider the device
* to be permanently disabled. */
/*
* Max number of EEH freezes allowed before we consider the device
* to be permanently disabled.
*/
#define EEH_MAX_ALLOWED_FREEZES 5
void
*
__devinit
eeh_dev_init
(
struct
device_node
*
dn
,
void
*
data
);
void
__devinit
eeh_dev_phb_init_dynamic
(
struct
pci_controller
*
phb
);
void
__init
eeh_dev_phb_init
(
void
);
void
__init
eeh_init
(
void
);
#ifdef CONFIG_PPC_PSERIES
int
__init
eeh_pseries_init
(
void
);
#endif
int
__init
eeh_ops_register
(
struct
eeh_ops
*
ops
);
int
__exit
eeh_ops_unregister
(
const
char
*
name
);
unsigned
long
eeh_check_failure
(
const
volatile
void
__iomem
*
token
,
unsigned
long
val
);
int
eeh_dn_check_failure
(
struct
device_node
*
dn
,
struct
pci_dev
*
dev
);
void
__init
pci_addr_cache_build
(
void
);
/**
* eeh_add_device_early
* eeh_add_device_late
*
* Perform eeh initialization for devices added after boot.
* Call eeh_add_device_early before doing any i/o to the
* device (including config space i/o). Call eeh_add_device_late
* to finish the eeh setup for this device.
*/
void
eeh_add_device_tree_early
(
struct
device_node
*
);
void
eeh_add_device_tree_late
(
struct
pci_bus
*
);
/**
* eeh_remove_device_recursive - undo EEH for device & children.
* @dev: pci device to be removed
*
* As above, this removes the device; it also removes child
* pci devices as well.
*/
void
eeh_remove_bus_device
(
struct
pci_dev
*
);
/**
...
...
@@ -87,8 +148,25 @@ void eeh_remove_bus_device(struct pci_dev *);
#define EEH_IO_ERROR_VALUE(size) (~0U >> ((4 - (size)) * 8))
#else
/* !CONFIG_EEH */
static
inline
void
*
eeh_dev_init
(
struct
device_node
*
dn
,
void
*
data
)
{
return
NULL
;
}
static
inline
void
eeh_dev_phb_init_dynamic
(
struct
pci_controller
*
phb
)
{
}
static
inline
void
eeh_dev_phb_init
(
void
)
{
}
static
inline
void
eeh_init
(
void
)
{
}
#ifdef CONFIG_PPC_PSERIES
static
inline
int
eeh_pseries_init
(
void
)
{
return
0
;
}
#endif
/* CONFIG_PPC_PSERIES */
static
inline
unsigned
long
eeh_check_failure
(
const
volatile
void
__iomem
*
token
,
unsigned
long
val
)
{
return
val
;
...
...
arch/powerpc/include/asm/eeh_event.h
View file @
aba0eb84
/*
* eeh_event.h
*
* 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
...
...
@@ -22,32 +20,19 @@
#define ASM_POWERPC_EEH_EVENT_H
#ifdef __KERNEL__
/** EEH event -- structure holding pci controller data that describes
* a change in the isolation status of a PCI slot. A pointer
* to this struct is passed as the data pointer in a notify callback.
/*
* structure holding pci controller data that describes a
* change in the isolation status of a PCI slot. A pointer
* to this struct is passed as the data pointer in a notify
* callback.
*/
struct
eeh_event
{
struct
list_head
list
;
struct
device_node
*
dn
;
/* struct device node */
struct
pci_dev
*
dev
;
/* affected device */
struct
list_head
list
;
/* to form event queue */
struct
eeh_dev
*
edev
;
/* EEH device */
};
/**
* eeh_send_failure_event - generate a PCI error event
* @dev pci device
*
* This routine builds a PCI error event which will be delivered
* to all listeners on the eeh_notifier_chain.
*
* This routine can be called within an interrupt context;
* the actual event will be delivered in a normal context
* (from a workqueue).
*/
int
eeh_send_failure_event
(
struct
device_node
*
dn
,
struct
pci_dev
*
dev
);
/* Main recovery function */
struct
pci_dn
*
handle_eeh_events
(
struct
eeh_event
*
);
int
eeh_send_failure_event
(
struct
eeh_dev
*
edev
);
struct
eeh_dev
*
handle_eeh_events
(
struct
eeh_event
*
);
#endif
/* __KERNEL__ */
#endif
/* ASM_POWERPC_EEH_EVENT_H */
arch/powerpc/include/asm/ppc-pci.h
View file @
aba0eb84
...
...
@@ -47,92 +47,21 @@ extern int rtas_setup_phb(struct pci_controller *phb);
extern
unsigned
long
pci_probe_only
;
/* ---- EEH internal-use-only related routines ---- */
#ifdef CONFIG_EEH
void
pci_addr_cache_build
(
void
);
void
pci_addr_cache_insert_device
(
struct
pci_dev
*
dev
);
void
pci_addr_cache_remove_device
(
struct
pci_dev
*
dev
);
void
pci_addr_cache_build
(
void
);
struct
pci_dev
*
pci_get_device_by_addr
(
unsigned
long
addr
);
/**
* eeh_slot_error_detail -- record and EEH error condition to the log
* @pdn: pci device node
* @severity: EEH_LOG_TEMP_FAILURE or EEH_LOG_PERM_FAILURE
*
* Obtains the EEH error details from the RTAS subsystem,
* and then logs these details with the RTAS error log system.
*/
#define EEH_LOG_TEMP_FAILURE 1
#define EEH_LOG_PERM_FAILURE 2
void
eeh_slot_error_detail
(
struct
pci_dn
*
pdn
,
int
severity
);
/**
* rtas_pci_enable - enable IO transfers for this slot
* @pdn: pci device node
* @function: either EEH_THAW_MMIO or EEH_THAW_DMA
*
* Enable I/O transfers to this slot
*/
#define EEH_THAW_MMIO 2
#define EEH_THAW_DMA 3
int
rtas_pci_enable
(
struct
pci_dn
*
pdn
,
int
function
);
/**
* rtas_set_slot_reset -- unfreeze a frozen slot
* @pdn: pci device node
*
* Clear the EEH-frozen condition on a slot. This routine
* does this by asserting the PCI #RST line for 1/8th of
* a second; this routine will sleep while the adapter is
* being reset.
*
* Returns a non-zero value if the reset failed.
*/
int
rtas_set_slot_reset
(
struct
pci_dn
*
);
int
eeh_wait_for_slot_status
(
struct
pci_dn
*
pdn
,
int
max_wait_msecs
);
/**
* eeh_restore_bars - Restore device configuration info.
* @pdn: pci device node
*
* A reset of a PCI device will clear out its config space.
* This routines will restore the config space for this
* device, and is children, to values previously obtained
* from the firmware.
*/
void
eeh_restore_bars
(
struct
pci_dn
*
);
/**
* rtas_configure_bridge -- firmware initialization of pci bridge
* @pdn: pci device node
*
* Ask the firmware to configure all PCI bridges devices
* located behind the indicated node. Required after a
* pci device reset. Does essentially the same hing as
* eeh_restore_bars, but for brdges, and lets firmware
* do the work.
*/
void
rtas_configure_bridge
(
struct
pci_dn
*
);
struct
pci_dev
*
pci_addr_cache_get_device
(
unsigned
long
addr
);
void
eeh_slot_error_detail
(
struct
eeh_dev
*
edev
,
int
severity
);
int
eeh_pci_enable
(
struct
eeh_dev
*
edev
,
int
function
);
int
eeh_reset_pe
(
struct
eeh_dev
*
);
void
eeh_restore_bars
(
struct
eeh_dev
*
);
int
rtas_write_config
(
struct
pci_dn
*
,
int
where
,
int
size
,
u32
val
);
int
rtas_read_config
(
struct
pci_dn
*
,
int
where
,
int
size
,
u32
*
val
);
/**
* eeh_mark_slot -- set mode flags for pertition endpoint
* @pdn: pci device node
*
* mark and clear slots: find "partition endpoint" PE and set or
* clear the flags for each subnode of the PE.
*/
void
eeh_mark_slot
(
struct
device_node
*
dn
,
int
mode_flag
);
void
eeh_clear_slot
(
struct
device_node
*
dn
,
int
mode_flag
);
/**
* find_device_pe -- Find the associated "Partiationable Endpoint" PE
* @pdn: pci device node
*/
struct
device_node
*
find_device_pe
(
struct
device_node
*
dn
);
void
eeh_mark_slot
(
struct
device_node
*
dn
,
int
mode_flag
);
void
eeh_clear_slot
(
struct
device_node
*
dn
,
int
mode_flag
);
struct
device_node
*
eeh_find_device_pe
(
struct
device_node
*
dn
);
void
eeh_sysfs_add_device
(
struct
pci_dev
*
pdev
);
void
eeh_sysfs_remove_device
(
struct
pci_dev
*
pdev
);
...
...
arch/powerpc/kernel/of_platform.c
View file @
aba0eb84
...
...
@@ -21,12 +21,13 @@
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>
#include <linux/atomic.h>
#include <asm/errno.h>
#include <asm/topology.h>
#include <asm/pci-bridge.h>
#include <asm/ppc-pci.h>
#include <
linux/atomic
.h>
#include <
asm/eeh
.h>
#ifdef CONFIG_PPC_OF_PLATFORM_PCI
...
...
@@ -66,6 +67,9 @@ static int __devinit of_pci_phb_probe(struct platform_device *dev)
/* Init pci_dn data structures */
pci_devs_phb_init_dynamic
(
phb
);
/* Create EEH devices for the PHB */
eeh_dev_phb_init_dynamic
(
phb
);
/* Register devices with EEH */
#ifdef CONFIG_EEH
if
(
dev
->
dev
.
of_node
->
child
)
...
...
arch/powerpc/kernel/rtas_pci.c
View file @
aba0eb84
...
...
@@ -275,6 +275,9 @@ void __init find_and_init_phbs(void)
of_node_put
(
root
);
pci_devs_phb_init
();
/* Create EEH devices for all PHBs */
eeh_dev_phb_init
();
/*
* pci_probe_only and pci_assign_all_buses can be set via properties
* in chosen.
...
...
arch/powerpc/platforms/pseries/Makefile
View file @
aba0eb84
...
...
@@ -6,7 +6,8 @@ obj-y := lpar.o hvCall.o nvram.o reconfig.o \
firmware.o power.o dlpar.o mobility.o
obj-$(CONFIG_SMP)
+=
smp.o
obj-$(CONFIG_SCANLOG)
+=
scanlog.o
obj-$(CONFIG_EEH)
+=
eeh.o eeh_cache.o eeh_driver.o eeh_event.o eeh_sysfs.o
obj-$(CONFIG_EEH)
+=
eeh.o eeh_dev.o eeh_cache.o eeh_driver.o
\
eeh_event.o eeh_sysfs.o eeh_pseries.o
obj-$(CONFIG_KEXEC)
+=
kexec.o
obj-$(CONFIG_PCI)
+=
pci.o pci_dlpar.o
obj-$(CONFIG_PSERIES_MSI)
+=
msi.o
...
...
arch/powerpc/platforms/pseries/eeh.c
View file @
aba0eb84
This diff is collapsed.
Click to expand it.
arch/powerpc/platforms/pseries/eeh_cache.c
View file @
aba0eb84
/*
* eeh_cache.c
* PCI address cache; allows the lookup of PCI devices based on I/O address
*
* Copyright IBM Corporation 2004
...
...
@@ -47,8 +46,7 @@
* than any hash algo I could think of for this problem, even
* with the penalty of slow pointer chases for d-cache misses).
*/
struct
pci_io_addr_range
{
struct
pci_io_addr_range
{
struct
rb_node
rb_node
;
unsigned
long
addr_lo
;
unsigned
long
addr_hi
;
...
...
@@ -56,13 +54,12 @@ struct pci_io_addr_range
unsigned
int
flags
;
};
static
struct
pci_io_addr_cache
{
static
struct
pci_io_addr_cache
{
struct
rb_root
rb_root
;
spinlock_t
piar_lock
;
}
pci_io_addr_cache_root
;
static
inline
struct
pci_dev
*
__pci_
get_device_by_addr
(
unsigned
long
addr
)
static
inline
struct
pci_dev
*
__pci_
addr_cache_get_device
(
unsigned
long
addr
)
{
struct
rb_node
*
n
=
pci_io_addr_cache_root
.
rb_root
.
rb_node
;
...
...
@@ -86,7 +83,7 @@ static inline struct pci_dev *__pci_get_device_by_addr(unsigned long addr)
}
/**
* pci_
get_device_by_addr
- Get device, given only address
* pci_
addr_cache_get_device
- Get device, given only address
* @addr: mmio (PIO) phys address or i/o port number
*
* Given an mmio phys address, or a port number, find a pci device
...
...
@@ -95,13 +92,13 @@ static inline struct pci_dev *__pci_get_device_by_addr(unsigned long addr)
* from zero (that is, they do *not* have pci_io_addr added in).
* It is safe to call this function within an interrupt.
*/
struct
pci_dev
*
pci_
get_device_by_addr
(
unsigned
long
addr
)
struct
pci_dev
*
pci_
addr_cache_get_device
(
unsigned
long
addr
)
{
struct
pci_dev
*
dev
;
unsigned
long
flags
;
spin_lock_irqsave
(
&
pci_io_addr_cache_root
.
piar_lock
,
flags
);
dev
=
__pci_
get_device_by_addr
(
addr
);
dev
=
__pci_
addr_cache_get_device
(
addr
);
spin_unlock_irqrestore
(
&
pci_io_addr_cache_root
.
piar_lock
,
flags
);
return
dev
;
}
...
...
@@ -166,7 +163,7 @@ pci_addr_cache_insert(struct pci_dev *dev, unsigned long alo,
#ifdef DEBUG
printk
(
KERN_DEBUG
"PIAR: insert range=[%lx:%lx] dev=%s
\n
"
,
alo
,
ahi
,
pci_name
(
dev
));
alo
,
ahi
,
pci_name
(
dev
));
#endif
rb_link_node
(
&
piar
->
rb_node
,
parent
,
p
);
...
...
@@ -178,7 +175,7 @@ pci_addr_cache_insert(struct pci_dev *dev, unsigned long alo,
static
void
__pci_addr_cache_insert_device
(
struct
pci_dev
*
dev
)
{
struct
device_node
*
dn
;
struct
pci_dn
*
pdn
;
struct
eeh_dev
*
edev
;
int
i
;
dn
=
pci_device_to_OF_node
(
dev
);
...
...
@@ -187,13 +184,19 @@ static void __pci_addr_cache_insert_device(struct pci_dev *dev)
return
;
}
edev
=
of_node_to_eeh_dev
(
dn
);
if
(
!
edev
)
{
pr_warning
(
"PCI: no EEH dev found for dn=%s
\n
"
,
dn
->
full_name
);
return
;
}
/* Skip any devices for which EEH is not enabled. */
pdn
=
PCI_DN
(
dn
);
if
(
!
(
pdn
->
eeh_mode
&
EEH_MODE_SUPPORTED
)
||
pdn
->
eeh_mode
&
EEH_MODE_NOCHECK
)
{
if
(
!
(
edev
->
mode
&
EEH_MODE_SUPPORTED
)
||
edev
->
mode
&
EEH_MODE_NOCHECK
)
{
#ifdef DEBUG
pr
intk
(
KERN_INFO
"PCI: skip building address cache for=%s - %s
\n
"
,
pci_name
(
dev
),
pdn
->
node
->
full_name
);
pr
_info
(
"PCI: skip building address cache for=%s - %s
\n
"
,
pci_name
(
dev
),
dn
->
full_name
);
#endif
return
;
}
...
...
@@ -284,6 +287,7 @@ void pci_addr_cache_remove_device(struct pci_dev *dev)
void
__init
pci_addr_cache_build
(
void
)
{
struct
device_node
*
dn
;
struct
eeh_dev
*
edev
;
struct
pci_dev
*
dev
=
NULL
;
spin_lock_init
(
&
pci_io_addr_cache_root
.
piar_lock
);
...
...
@@ -294,8 +298,14 @@ void __init pci_addr_cache_build(void)
dn
=
pci_device_to_OF_node
(
dev
);
if
(
!
dn
)
continue
;
edev
=
of_node_to_eeh_dev
(
dn
);
if
(
!
edev
)
continue
;
pci_dev_get
(
dev
);
/* matching put is in eeh_remove_device() */
PCI_DN
(
dn
)
->
pcidev
=
dev
;
dev
->
dev
.
archdata
.
edev
=
edev
;
edev
->
pdev
=
dev
;
eeh_sysfs_add_device
(
dev
);
}
...
...
arch/powerpc/platforms/pseries/eeh_dev.c
0 → 100644
View file @
aba0eb84
/*
* The file intends to implement dynamic creation of EEH device, which will
* be bound with OF node and PCI device simutaneously. The EEH devices would
* be foundamental information for EEH core components to work proerly. Besides,
* We have to support multiple situations where dynamic creation of EEH device
* is required:
*
* 1) Before PCI emunation starts, we need create EEH devices according to the
* PCI sensitive OF nodes.
* 2) When PCI emunation is done, we need do the binding between PCI device and
* the associated EEH device.
* 3) DR (Dynamic Reconfiguration) would create PCI sensitive OF node. EEH device
* will be created while PCI sensitive OF node is detected from DR.
* 4) PCI hotplug needs redoing the binding between PCI device and EEH device. If
* PHB is newly inserted, we also need create EEH devices accordingly.
*
* Copyright Benjamin Herrenschmidt & Gavin Shan, IBM Corporation 2012.
*
* 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/export.h>
#include <linux/gfp.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/string.h>
#include <asm/pci-bridge.h>
#include <asm/ppc-pci.h>
/**
* eeh_dev_init - Create EEH device according to OF node
* @dn: device node
* @data: PHB
*
* It will create EEH device according to the given OF node. The function
* might be called by PCI emunation, DR, PHB hotplug.
*/
void
*
__devinit
eeh_dev_init
(
struct
device_node
*
dn
,
void
*
data
)
{
struct
pci_controller
*
phb
=
data
;
struct
eeh_dev
*
edev
;
/* Allocate EEH device */
edev
=
zalloc_maybe_bootmem
(
sizeof
(
*
edev
),
GFP_KERNEL
);
if
(
!
edev
)
{
pr_warning
(
"%s: out of memory
\n
"
,
__func__
);
return
NULL
;
}
/* Associate EEH device with OF node */
dn
->
edev
=
edev
;
edev
->
dn
=
dn
;
edev
->
phb
=
phb
;
return
NULL
;
}
/**
* eeh_dev_phb_init_dynamic - Create EEH devices for devices included in PHB
* @phb: PHB
*
* Scan the PHB OF node and its child association, then create the
* EEH devices accordingly
*/
void
__devinit
eeh_dev_phb_init_dynamic
(
struct
pci_controller
*
phb
)
{
struct
device_node
*
dn
=
phb
->
dn
;
/* EEH device for PHB */
eeh_dev_init
(
dn
,
phb
);
/* EEH devices for children OF nodes */
traverse_pci_devices
(
dn
,
eeh_dev_init
,
phb
);
}
/**
* eeh_dev_phb_init - Create EEH devices for devices included in existing PHBs
*
* Scan all the existing PHBs and create EEH devices for their OF
* nodes and their children OF nodes
*/
void
__init
eeh_dev_phb_init
(
void
)
{
struct
pci_controller
*
phb
,
*
tmp
;
list_for_each_entry_safe
(
phb
,
tmp
,
&
hose_list
,
list_node
)
eeh_dev_phb_init_dynamic
(
phb
);
}
arch/powerpc/platforms/pseries/eeh_driver.c
View file @
aba0eb84
This diff is collapsed.
Click to expand it.
arch/powerpc/platforms/pseries/eeh_event.c
View file @
aba0eb84
/*
* eeh_event.c
*
* 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
...
...
@@ -46,7 +44,7 @@ DECLARE_WORK(eeh_event_wq, eeh_thread_launcher);
DEFINE_MUTEX
(
eeh_event_mutex
);
/**
* eeh_event_handler -
d
ispatch EEH events.
* eeh_event_handler -
D
ispatch EEH events.
* @dummy - unused
*
* The detection of a frozen slot can occur inside an interrupt,
...
...
@@ -58,10 +56,10 @@ DEFINE_MUTEX(eeh_event_mutex);
static
int
eeh_event_handler
(
void
*
dummy
)
{
unsigned
long
flags
;
struct
eeh_event
*
event
;
struct
pci_dn
*
pdn
;
struct
eeh_event
*
event
;
struct
eeh_dev
*
edev
;
daemonize
(
"eehd"
);
daemonize
(
"eehd"
);
set_current_state
(
TASK_INTERRUPTIBLE
);
spin_lock_irqsave
(
&
eeh_eventlist_lock
,
flags
);
...
...
@@ -79,31 +77,37 @@ static int eeh_event_handler(void * dummy)
/* Serialize processing of EEH events */
mutex_lock
(
&
eeh_event_mutex
);
eeh_mark_slot
(
event
->
dn
,
EEH_MODE_RECOVERING
);
edev
=
event
->
edev
;
eeh_mark_slot
(
eeh_dev_to_of_node
(
edev
),
EEH_MODE_RECOVERING
);
printk
(
KERN_INFO
"EEH: Detected PCI bus error on device %s
\n
"
,
eeh_pci_name
(
event
->
dev
));
eeh_pci_name
(
edev
->
pdev
));
edev
=
handle_eeh_events
(
event
);
pdn
=
handle_eeh_events
(
event
);
eeh_clear_slot
(
eeh_dev_to_of_node
(
edev
),
EEH_MODE_RECOVERING
);
pci_dev_put
(
edev
->
pdev
);
eeh_clear_slot
(
event
->
dn
,
EEH_MODE_RECOVERING
);
pci_dev_put
(
event
->
dev
);
kfree
(
event
);
mutex_unlock
(
&
eeh_event_mutex
);
/* If there are no new errors after an hour, clear the counter. */
if
(
pdn
&&
pdn
->
eeh_freeze_count
>
0
)
{
msleep_interruptible
(
3600
*
1000
);
if
(
pdn
->
eeh_freeze_count
>
0
)
pdn
->
eeh_freeze_count
--
;
if
(
edev
&&
edev
->
freeze_count
>
0
)
{
msleep_interruptible
(
3600
*
1000
);
if
(
edev
->
freeze_count
>
0
)
edev
->
freeze_count
--
;
}
return
0
;
}
/**
* eeh_thread_launcher
* eeh_thread_launcher
- Start kernel thread to handle EEH events
* @dummy - unused
*
* This routine is called to start the kernel thread for processing
* EEH event.
*/
static
void
eeh_thread_launcher
(
struct
work_struct
*
dummy
)
{
...
...
@@ -112,18 +116,18 @@ static void eeh_thread_launcher(struct work_struct *dummy)
}
/**
* eeh_send_failure_event -
g
enerate a PCI error event
* @
dev pci
device
* eeh_send_failure_event -
G
enerate a PCI error event
* @
edev: EEH
device
*
* This routine can be called within an interrupt context;
* the actual event will be delivered in a normal context
* (from a workqueue).
*/
int
eeh_send_failure_event
(
struct
device_node
*
dn
,
struct
pci_dev
*
dev
)
int
eeh_send_failure_event
(
struct
eeh_dev
*
edev
)
{
unsigned
long
flags
;
struct
eeh_event
*
event
;
struct
device_node
*
dn
=
eeh_dev_to_of_node
(
edev
);
const
char
*
location
;
if
(
!
mem_init_done
)
{
...
...
@@ -135,15 +139,14 @@ int eeh_send_failure_event (struct device_node *dn,
}
event
=
kmalloc
(
sizeof
(
*
event
),
GFP_ATOMIC
);
if
(
event
==
NULL
)
{
printk
(
KERN_ERR
"EEH: out of memory, event not handled
\n
"
);
printk
(
KERN_ERR
"EEH: out of memory, event not handled
\n
"
);
return
1
;
}
if
(
dev
)
pci_dev_get
(
dev
);
if
(
edev
->
p
dev
)
pci_dev_get
(
edev
->
p
dev
);
event
->
dn
=
dn
;
event
->
dev
=
dev
;
event
->
edev
=
edev
;
/* We may or may not be called in an interrupt context */
spin_lock_irqsave
(
&
eeh_eventlist_lock
,
flags
);
...
...
@@ -154,5 +157,3 @@ int eeh_send_failure_event (struct device_node *dn,
return
0
;
}
/********************** END OF FILE ******************************/
arch/powerpc/platforms/pseries/eeh_pseries.c
0 → 100644
View file @
aba0eb84
This diff is collapsed.
Click to expand it.
arch/powerpc/platforms/pseries/eeh_sysfs.c
View file @
aba0eb84
...
...
@@ -28,7 +28,7 @@
#include <asm/pci-bridge.h>
/**
* EEH_SHOW_ATTR --
c
reate sysfs entry for eeh statistic
* EEH_SHOW_ATTR --
C
reate sysfs entry for eeh statistic
* @_name: name of file in sysfs directory
* @_memb: name of member in struct pci_dn to access
* @_format: printf format for display
...
...
@@ -41,24 +41,21 @@ static ssize_t eeh_show_##_name(struct device *dev, \
struct device_attribute *attr, char *buf) \
{ \
struct pci_dev *pdev = to_pci_dev(dev); \
struct device_node *dn = pci_device_to_OF_node(pdev); \
struct pci_dn *pdn; \
struct eeh_dev *edev = pci_dev_to_eeh_dev(pdev); \
\
if (!
dn || PCI_DN(dn) == NULL)
\
return 0;
\
if (!
edev)
\
return 0; \
\
pdn = PCI_DN(dn); \
return sprintf(buf, _format "\n", pdn->_memb); \
return sprintf(buf, _format "\n", edev->_memb); \
} \
static DEVICE_ATTR(_name, S_IRUGO, eeh_show_##_name, NULL);
EEH_SHOW_ATTR
(
eeh_mode
,
eeh_mode
,
"0x%x"
);
EEH_SHOW_ATTR
(
eeh_config_addr
,
eeh_config_addr
,
"0x%x"
);
EEH_SHOW_ATTR
(
eeh_pe_config_addr
,
eeh_pe_config_addr
,
"0x%x"
);
EEH_SHOW_ATTR
(
eeh_check_count
,
eeh_check_count
,
"%d"
);
EEH_SHOW_ATTR
(
eeh_freeze_count
,
eeh_freeze_count
,
"%d"
);
EEH_SHOW_ATTR
(
eeh_false_positives
,
eeh_false_positives
,
"%d"
);
EEH_SHOW_ATTR
(
eeh_mode
,
mode
,
"0x%x"
);
EEH_SHOW_ATTR
(
eeh_config_addr
,
config_addr
,
"0x%x"
);
EEH_SHOW_ATTR
(
eeh_pe_config_addr
,
pe_config_addr
,
"0x%x"
);
EEH_SHOW_ATTR
(
eeh_check_count
,
check_count
,
"%d"
);
EEH_SHOW_ATTR
(
eeh_freeze_count
,
freeze_count
,
"%d"
);
EEH_SHOW_ATTR
(
eeh_false_positives
,
false_positives
,
"%d"
);
void
eeh_sysfs_add_device
(
struct
pci_dev
*
pdev
)
{
...
...
arch/powerpc/platforms/pseries/msi.c
View file @
aba0eb84
...
...
@@ -217,7 +217,7 @@ static struct device_node *find_pe_dn(struct pci_dev *dev, int *total)
if
(
!
dn
)
return
NULL
;
dn
=
find_device_pe
(
dn
);
dn
=
eeh_
find_device_pe
(
dn
);
if
(
!
dn
)
return
NULL
;
...
...
arch/powerpc/platforms/pseries/pci_dlpar.c
View file @
aba0eb84
...
...
@@ -147,6 +147,9 @@ struct pci_controller * __devinit init_phb_dynamic(struct device_node *dn)
pci_devs_phb_init_dynamic
(
phb
);
/* Create EEH devices for the PHB */
eeh_dev_phb_init_dynamic
(
phb
);
if
(
dn
->
child
)
eeh_add_device_tree_early
(
dn
);
...
...
arch/powerpc/platforms/pseries/setup.c
View file @
aba0eb84
...
...
@@ -260,8 +260,12 @@ static int pci_dn_reconfig_notifier(struct notifier_block *nb, unsigned long act
switch
(
action
)
{
case
PSERIES_RECONFIG_ADD
:
pci
=
np
->
parent
->
data
;
if
(
pci
)
if
(
pci
)
{
update_dn_pci_info
(
np
,
pci
->
phb
);
/* Create EEH device for the OF node */
eeh_dev_init
(
np
,
pci
->
phb
);
}
break
;
default:
err
=
NOTIFY_DONE
;
...
...
@@ -381,6 +385,7 @@ static void __init pSeries_setup_arch(void)
/* Find and initialize PCI host bridges */
init_pci_config_tokens
();
eeh_pseries_init
();
find_and_init_phbs
();
pSeries_reconfig_notifier_register
(
&
pci_dn_reconfig_nb
);
eeh_init
();
...
...
include/linux/of.h
View file @
aba0eb84
...
...
@@ -58,6 +58,9 @@ struct device_node {
struct
kref
kref
;
unsigned
long
_flags
;
void
*
data
;
#if defined(CONFIG_EEH)
struct
eeh_dev
*
edev
;
#endif
#if defined(CONFIG_SPARC)
char
*
path_component_name
;
unsigned
int
unique_id
;
...
...
@@ -72,6 +75,13 @@ struct of_phandle_args {
uint32_t
args
[
MAX_PHANDLE_ARGS
];
};
#if defined(CONFIG_EEH)
static
inline
struct
eeh_dev
*
of_node_to_eeh_dev
(
struct
device_node
*
dn
)
{
return
dn
->
edev
;
}
#endif
#if defined(CONFIG_SPARC) || !defined(CONFIG_OF)
/* Dummy ref counting routines - to be implemented later */
static
inline
struct
device_node
*
of_node_get
(
struct
device_node
*
node
)
...
...
include/linux/pci.h
View file @
aba0eb84
...
...
@@ -1647,6 +1647,13 @@ static inline void pci_set_bus_of_node(struct pci_bus *bus) { }
static
inline
void
pci_release_bus_of_node
(
struct
pci_bus
*
bus
)
{
}
#endif
/* CONFIG_OF */
#ifdef CONFIG_EEH
static
inline
struct
eeh_dev
*
pci_dev_to_eeh_dev
(
struct
pci_dev
*
pdev
)
{
return
pdev
->
dev
.
archdata
.
edev
;
}
#endif
/**
* pci_find_upstream_pcie_bridge - find upstream PCIe-to-PCI bridge of a device
* @pdev: the PCI device
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment