Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
L
linux-davinci-2.6.23
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Redmine
Redmine
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Operations
Operations
Metrics
Environments
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
linux
linux-davinci-2.6.23
Commits
a62e6848
Commit
a62e6848
authored
Jan 10, 2006
by
Linus Torvalds
Browse files
Options
Browse Files
Download
Plain Diff
Merge
git://git.kernel.org/pub/scm/linux/kernel/git/paulus/powerpc-merge
parents
ab396e91
3b212db9
Changes
22
Hide whitespace changes
Inline
Side-by-side
Showing
22 changed files
with
896 additions
and
436 deletions
+896
-436
arch/powerpc/Kconfig
arch/powerpc/Kconfig
+10
-1
arch/powerpc/kernel/Makefile
arch/powerpc/kernel/Makefile
+1
-1
arch/powerpc/kernel/pci_64.c
arch/powerpc/kernel/pci_64.c
+1
-1
arch/powerpc/kernel/ppc_ksyms.c
arch/powerpc/kernel/ppc_ksyms.c
+0
-1
arch/powerpc/kernel/rtas_pci.c
arch/powerpc/kernel/rtas_pci.c
+1
-1
arch/powerpc/kernel/setup_32.c
arch/powerpc/kernel/setup_32.c
+0
-2
arch/powerpc/kernel/setup_64.c
arch/powerpc/kernel/setup_64.c
+0
-2
arch/powerpc/kernel/sys_ppc32.c
arch/powerpc/kernel/sys_ppc32.c
+0
-24
arch/powerpc/kernel/systbl.S
arch/powerpc/kernel/systbl.S
+3
-3
arch/powerpc/mm/pgtable_64.c
arch/powerpc/mm/pgtable_64.c
+1
-1
arch/powerpc/platforms/maple/setup.c
arch/powerpc/platforms/maple/setup.c
+0
-3
arch/powerpc/platforms/pseries/Makefile
arch/powerpc/platforms/pseries/Makefile
+1
-1
arch/powerpc/platforms/pseries/eeh.c
arch/powerpc/platforms/pseries/eeh.c
+132
-357
arch/powerpc/platforms/pseries/eeh_cache.c
arch/powerpc/platforms/pseries/eeh_cache.c
+316
-0
arch/powerpc/platforms/pseries/eeh_driver.c
arch/powerpc/platforms/pseries/eeh_driver.c
+376
-0
arch/powerpc/platforms/pseries/eeh_event.c
arch/powerpc/platforms/pseries/eeh_event.c
+12
-27
drivers/ide/pci/via82cxxx.c
drivers/ide/pci/via82cxxx.c
+1
-1
include/asm-powerpc/eeh.h
include/asm-powerpc/eeh.h
+5
-3
include/asm-powerpc/eeh_event.h
include/asm-powerpc/eeh_event.h
+5
-2
include/asm-powerpc/pci-bridge.h
include/asm-powerpc/pci-bridge.h
+5
-4
include/asm-powerpc/ppc-pci.h
include/asm-powerpc/ppc-pci.h
+22
-1
include/asm-powerpc/serial.h
include/asm-powerpc/serial.h
+4
-0
No files found.
arch/powerpc/Kconfig
View file @
a62e6848
...
...
@@ -275,6 +275,7 @@ config PPC_PSERIES
select PPC_I8259
select PPC_RTAS
select RTAS_ERROR_LOGGING
select PPC_UDBG_16550
default y
config PPC_CHRP
...
...
@@ -284,6 +285,7 @@ config PPC_CHRP
select PPC_INDIRECT_PCI
select PPC_RTAS
select PPC_MPC106
select PPC_UDBG_16550
default y
config PPC_PMAC
...
...
@@ -306,6 +308,7 @@ config PPC_PREP
depends on PPC_MULTIPLATFORM && PPC32 && BROKEN
select PPC_I8259
select PPC_INDIRECT_PCI
select PPC_UDBG_16550
default y
config PPC_MAPLE
...
...
@@ -314,6 +317,7 @@ config PPC_MAPLE
select U3_DART
select MPIC_BROKEN_U3
select GENERIC_TBSYNC
select PPC_UDBG_16550
default n
help
This option enables support for the Maple 970FX Evaluation Board.
...
...
@@ -324,6 +328,7 @@ config PPC_CELL
depends on PPC_MULTIPLATFORM && PPC64
select PPC_RTAS
select MMIO_NVRAM
select PPC_UDBG_16550
config PPC_OF
def_bool y
...
...
@@ -370,6 +375,10 @@ config MPIC_BROKEN_U3
depends on PPC_MAPLE
default y
config PPC_UDBG_16550
bool
default n
config CELL_IIC
depends on PPC_CELL
bool
...
...
@@ -403,7 +412,7 @@ config PPC_MPC106
config GENERIC_TBSYNC
bool
default y if
CONFIG_PPC32 && CONFIG_
SMP
default y if
PPC32 &&
SMP
default n
source "drivers/cpufreq/Kconfig"
...
...
arch/powerpc/kernel/Makefile
View file @
a62e6848
...
...
@@ -54,7 +54,7 @@ obj-$(CONFIG_BOOTX_TEXT) += btext.o
obj-$(CONFIG_6xx)
+=
idle_6xx.o
obj-$(CONFIG_SMP)
+=
smp.o
obj-$(CONFIG_KPROBES)
+=
kprobes.o
obj-$(CONFIG_
SERIAL_82
50)
+=
legacy_serial.o udbg_16550.o
obj-$(CONFIG_
PPC_UDBG_165
50)
+=
legacy_serial.o udbg_16550.o
module-$(CONFIG_PPC64)
+=
module_64.o
obj-$(CONFIG_MODULES)
+=
$
(
module-y
)
...
...
arch/powerpc/kernel/pci_64.c
View file @
a62e6848
...
...
@@ -381,7 +381,7 @@ struct pci_dev *of_create_pci_dev(struct device_node *node,
dev
->
subsystem_vendor
=
get_int_prop
(
node
,
"subsystem-vendor-id"
,
0
);
dev
->
subsystem_device
=
get_int_prop
(
node
,
"subsystem-id"
,
0
);
dev
->
cfg_size
=
256
;
/*pci_cfg_space_size(dev);*/
dev
->
cfg_size
=
pci_cfg_space_size
(
dev
);
sprintf
(
pci_name
(
dev
),
"%04x:%02x:%02x.%d"
,
pci_domain_nr
(
bus
),
dev
->
bus
->
number
,
PCI_SLOT
(
devfn
),
PCI_FUNC
(
devfn
));
...
...
arch/powerpc/kernel/ppc_ksyms.c
View file @
a62e6848
...
...
@@ -244,7 +244,6 @@ EXPORT_SYMBOL(set_context);
extern
long
mol_trampoline
;
EXPORT_SYMBOL
(
mol_trampoline
);
/* For MOL */
EXPORT_SYMBOL
(
flush_hash_pages
);
/* For MOL */
EXPORT_SYMBOL_GPL
(
__handle_mm_fault
);
/* For MOL */
#ifdef CONFIG_SMP
extern
int
mmu_hash_lock
;
EXPORT_SYMBOL
(
mmu_hash_lock
);
/* For MOL */
...
...
arch/powerpc/kernel/rtas_pci.c
View file @
a62e6848
...
...
@@ -72,7 +72,7 @@ static int of_device_available(struct device_node * dn)
return
0
;
}
static
int
rtas_read_config
(
struct
pci_dn
*
pdn
,
int
where
,
int
size
,
u32
*
val
)
int
rtas_read_config
(
struct
pci_dn
*
pdn
,
int
where
,
int
size
,
u32
*
val
)
{
int
returnval
=
-
1
;
unsigned
long
buid
,
addr
;
...
...
arch/powerpc/kernel/setup_32.c
View file @
a62e6848
...
...
@@ -299,9 +299,7 @@ void __init setup_arch(char **cmdline_p)
if
(
ppc_md
.
init_early
)
ppc_md
.
init_early
();
#ifdef CONFIG_SERIAL_8250
find_legacy_serial_ports
();
#endif
finish_device_tree
();
smp_setup_cpu_maps
();
...
...
arch/powerpc/kernel/setup_64.c
View file @
a62e6848
...
...
@@ -472,9 +472,7 @@ void __init setup_system(void)
* hash table management for us, thus ioremap works. We do that early
* so that further code can be debugged
*/
#ifdef CONFIG_SERIAL_8250
find_legacy_serial_ports
();
#endif
/*
* "Finish" the device-tree, that is do the actual parsing of
...
...
arch/powerpc/kernel/sys_ppc32.c
View file @
a62e6848
...
...
@@ -552,30 +552,6 @@ asmlinkage long compat_sys_sched_rr_get_interval(u32 pid, struct compat_timespec
return
ret
;
}
asmlinkage
int
compat_sys_pciconfig_read
(
u32
bus
,
u32
dfn
,
u32
off
,
u32
len
,
u32
ubuf
)
{
return
sys_pciconfig_read
((
unsigned
long
)
bus
,
(
unsigned
long
)
dfn
,
(
unsigned
long
)
off
,
(
unsigned
long
)
len
,
compat_ptr
(
ubuf
));
}
asmlinkage
int
compat_sys_pciconfig_write
(
u32
bus
,
u32
dfn
,
u32
off
,
u32
len
,
u32
ubuf
)
{
return
sys_pciconfig_write
((
unsigned
long
)
bus
,
(
unsigned
long
)
dfn
,
(
unsigned
long
)
off
,
(
unsigned
long
)
len
,
compat_ptr
(
ubuf
));
}
asmlinkage
int
compat_sys_pciconfig_iobase
(
u32
which
,
u32
in_bus
,
u32
in_devfn
)
{
return
sys_pciconfig_iobase
(
which
,
in_bus
,
in_devfn
);
}
/* Note: it is necessary to treat mode as an unsigned int,
* with the corresponding cast to a signed int to insure that the
* proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode)
...
...
arch/powerpc/kernel/systbl.S
View file @
a62e6848
...
...
@@ -239,9 +239,9 @@ SYS32ONLY(ftruncate64)
SYSX
(
sys_ni_syscall
,
sys_stat64
,
sys_stat64
)
SYSX
(
sys_ni_syscall
,
sys_lstat64
,
sys_lstat64
)
SYSX
(
sys_ni_syscall
,
sys_fstat64
,
sys_fstat64
)
COMPAT_SYS
(
pciconfig_read
)
COMPAT_SYS
(
pciconfig_write
)
COMPAT_SYS
(
pciconfig_iobase
)
SYSCALL
(
pciconfig_read
)
SYSCALL
(
pciconfig_write
)
SYSCALL
(
pciconfig_iobase
)
SYSCALL
(
ni_syscall
)
SYSCALL
(
getdents64
)
SYSCALL
(
pivot_root
)
...
...
arch/powerpc/mm/pgtable_64.c
View file @
a62e6848
...
...
@@ -174,7 +174,7 @@ void __iomem * __ioremap(unsigned long addr, unsigned long size,
pa
=
addr
&
PAGE_MASK
;
size
=
PAGE_ALIGN
(
addr
+
size
)
-
pa
;
if
(
size
==
0
)
if
(
(
size
==
0
)
||
(
pa
==
0
)
)
return
NULL
;
if
(
mem_init_done
)
{
...
...
arch/powerpc/platforms/maple/setup.c
View file @
a62e6848
...
...
@@ -71,9 +71,6 @@
#define DBG(fmt...)
#endif
extern
void
generic_find_legacy_serial_ports
(
u64
*
physport
,
unsigned
int
*
default_speed
);
static
void
maple_restart
(
char
*
cmd
)
{
unsigned
int
maple_nvram_base
;
...
...
arch/powerpc/platforms/pseries/Makefile
View file @
a62e6848
...
...
@@ -4,7 +4,7 @@ obj-$(CONFIG_SMP) += smp.o
obj-$(CONFIG_IBMVIO)
+=
vio.o
obj-$(CONFIG_XICS)
+=
xics.o
obj-$(CONFIG_SCANLOG)
+=
scanlog.o
obj-$(CONFIG_EEH)
+=
eeh.o eeh_event.o
obj-$(CONFIG_EEH)
+=
eeh.o eeh_
cache.o eeh_driver.o eeh_
event.o
obj-$(CONFIG_HVC_CONSOLE)
+=
hvconsole.o
obj-$(CONFIG_HVCS)
+=
hvcserver.o
arch/powerpc/platforms/pseries/eeh.c
View file @
a62e6848
...
...
@@ -76,15 +76,14 @@
*/
#define EEH_MAX_FAILS 100000
/* Misc forward declaraions */
static
void
eeh_save_bars
(
struct
pci_dev
*
pdev
,
struct
pci_dn
*
pdn
);
/* RTAS tokens */
static
int
ibm_set_eeh_option
;
static
int
ibm_set_slot_reset
;
static
int
ibm_read_slot_reset_state
;
static
int
ibm_read_slot_reset_state2
;
static
int
ibm_slot_error_detail
;
static
int
ibm_get_config_addr_info
;
static
int
ibm_configure_bridge
;
int
eeh_subsystem_enabled
;
EXPORT_SYMBOL
(
eeh_subsystem_enabled
);
...
...
@@ -98,308 +97,23 @@ static DEFINE_SPINLOCK(slot_errbuf_lock);
static
int
eeh_error_buf_size
;
/* System monitoring statistics */
static
DEFINE_PER_CPU
(
unsigned
long
,
no_device
);
static
DEFINE_PER_CPU
(
unsigned
long
,
no_dn
);
static
DEFINE_PER_CPU
(
unsigned
long
,
no_cfg_addr
);
static
DEFINE_PER_CPU
(
unsigned
long
,
ignored_check
);
static
DEFINE_PER_CPU
(
unsigned
long
,
total_mmio_ffs
);
static
DEFINE_PER_CPU
(
unsigned
long
,
false_positives
);
static
DEFINE_PER_CPU
(
unsigned
long
,
ignored_failures
);
static
DEFINE_PER_CPU
(
unsigned
long
,
slot_resets
);
/**
* The pci address cache subsystem. This subsystem places
* PCI device address resources into a red-black tree, sorted
* according to the address range, so that given only an i/o
* address, the corresponding PCI device can be **quickly**
* found. It is safe to perform an address lookup in an interrupt
* context; this ability is an important feature.
*
* Currently, the only customer of this code is the EEH subsystem;
* thus, this code has been somewhat tailored to suit EEH better.
* In particular, the cache does *not* hold the addresses of devices
* for which EEH is not enabled.
*
* (Implementation Note: The RB tree seems to be better/faster
* 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
rb_node
rb_node
;
unsigned
long
addr_lo
;
unsigned
long
addr_hi
;
struct
pci_dev
*
pcidev
;
unsigned
int
flags
;
};
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
)
{
struct
rb_node
*
n
=
pci_io_addr_cache_root
.
rb_root
.
rb_node
;
while
(
n
)
{
struct
pci_io_addr_range
*
piar
;
piar
=
rb_entry
(
n
,
struct
pci_io_addr_range
,
rb_node
);
if
(
addr
<
piar
->
addr_lo
)
{
n
=
n
->
rb_left
;
}
else
{
if
(
addr
>
piar
->
addr_hi
)
{
n
=
n
->
rb_right
;
}
else
{
pci_dev_get
(
piar
->
pcidev
);
return
piar
->
pcidev
;
}
}
}
return
NULL
;
}
/**
* pci_get_device_by_addr - 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
* that implements this address. Be sure to pci_dev_put the device
* when finished. I/O port numbers are assumed to be offset
* from zero (that is, they do *not* have pci_io_addr added in).
* It is safe to call this function within an interrupt.
*/
static
struct
pci_dev
*
pci_get_device_by_addr
(
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
);
spin_unlock_irqrestore
(
&
pci_io_addr_cache_root
.
piar_lock
,
flags
);
return
dev
;
}
#ifdef DEBUG
/*
* Handy-dandy debug print routine, does nothing more
* than print out the contents of our addr cache.
*/
static
void
pci_addr_cache_print
(
struct
pci_io_addr_cache
*
cache
)
{
struct
rb_node
*
n
;
int
cnt
=
0
;
n
=
rb_first
(
&
cache
->
rb_root
);
while
(
n
)
{
struct
pci_io_addr_range
*
piar
;
piar
=
rb_entry
(
n
,
struct
pci_io_addr_range
,
rb_node
);
printk
(
KERN_DEBUG
"PCI: %s addr range %d [%lx-%lx]: %s
\n
"
,
(
piar
->
flags
&
IORESOURCE_IO
)
?
"i/o"
:
"mem"
,
cnt
,
piar
->
addr_lo
,
piar
->
addr_hi
,
pci_name
(
piar
->
pcidev
));
cnt
++
;
n
=
rb_next
(
n
);
}
}
#endif
/* Insert address range into the rb tree. */
static
struct
pci_io_addr_range
*
pci_addr_cache_insert
(
struct
pci_dev
*
dev
,
unsigned
long
alo
,
unsigned
long
ahi
,
unsigned
int
flags
)
{
struct
rb_node
**
p
=
&
pci_io_addr_cache_root
.
rb_root
.
rb_node
;
struct
rb_node
*
parent
=
NULL
;
struct
pci_io_addr_range
*
piar
;
/* Walk tree, find a place to insert into tree */
while
(
*
p
)
{
parent
=
*
p
;
piar
=
rb_entry
(
parent
,
struct
pci_io_addr_range
,
rb_node
);
if
(
ahi
<
piar
->
addr_lo
)
{
p
=
&
parent
->
rb_left
;
}
else
if
(
alo
>
piar
->
addr_hi
)
{
p
=
&
parent
->
rb_right
;
}
else
{
if
(
dev
!=
piar
->
pcidev
||
alo
!=
piar
->
addr_lo
||
ahi
!=
piar
->
addr_hi
)
{
printk
(
KERN_WARNING
"PIAR: overlapping address range
\n
"
);
}
return
piar
;
}
}
piar
=
(
struct
pci_io_addr_range
*
)
kmalloc
(
sizeof
(
struct
pci_io_addr_range
),
GFP_ATOMIC
);
if
(
!
piar
)
return
NULL
;
static
unsigned
long
no_device
;
static
unsigned
long
no_dn
;
static
unsigned
long
no_cfg_addr
;
static
unsigned
long
ignored_check
;
static
unsigned
long
total_mmio_ffs
;
static
unsigned
long
false_positives
;
static
unsigned
long
ignored_failures
;
static
unsigned
long
slot_resets
;
piar
->
addr_lo
=
alo
;
piar
->
addr_hi
=
ahi
;
piar
->
pcidev
=
dev
;
piar
->
flags
=
flags
;
#ifdef DEBUG
printk
(
KERN_DEBUG
"PIAR: insert range=[%lx:%lx] dev=%s
\n
"
,
alo
,
ahi
,
pci_name
(
dev
));
#endif
rb_link_node
(
&
piar
->
rb_node
,
parent
,
p
);
rb_insert_color
(
&
piar
->
rb_node
,
&
pci_io_addr_cache_root
.
rb_root
);
return
piar
;
}
static
void
__pci_addr_cache_insert_device
(
struct
pci_dev
*
dev
)
{
struct
device_node
*
dn
;
struct
pci_dn
*
pdn
;
int
i
;
int
inserted
=
0
;
dn
=
pci_device_to_OF_node
(
dev
);
if
(
!
dn
)
{
printk
(
KERN_WARNING
"PCI: no pci dn found for dev=%s
\n
"
,
pci_name
(
dev
));
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
)
{
#ifdef DEBUG
printk
(
KERN_INFO
"PCI: skip building address cache for=%s - %s
\n
"
,
pci_name
(
dev
),
pdn
->
node
->
full_name
);
#endif
return
;
}
/* The cache holds a reference to the device... */
pci_dev_get
(
dev
);
/* Walk resources on this device, poke them into the tree */
for
(
i
=
0
;
i
<
DEVICE_COUNT_RESOURCE
;
i
++
)
{
unsigned
long
start
=
pci_resource_start
(
dev
,
i
);
unsigned
long
end
=
pci_resource_end
(
dev
,
i
);
unsigned
int
flags
=
pci_resource_flags
(
dev
,
i
);
/* We are interested only bus addresses, not dma or other stuff */
if
(
0
==
(
flags
&
(
IORESOURCE_IO
|
IORESOURCE_MEM
)))
continue
;
if
(
start
==
0
||
~
start
==
0
||
end
==
0
||
~
end
==
0
)
continue
;
pci_addr_cache_insert
(
dev
,
start
,
end
,
flags
);
inserted
=
1
;
}
/* If there was nothing to add, the cache has no reference... */
if
(
!
inserted
)
pci_dev_put
(
dev
);
}
/**
* pci_addr_cache_insert_device - Add a device to the address cache
* @dev: PCI device whose I/O addresses we are interested in.
*
* In order to support the fast lookup of devices based on addresses,
* we maintain a cache of devices that can be quickly searched.
* This routine adds a device to that cache.
*/
static
void
pci_addr_cache_insert_device
(
struct
pci_dev
*
dev
)
{
unsigned
long
flags
;
spin_lock_irqsave
(
&
pci_io_addr_cache_root
.
piar_lock
,
flags
);
__pci_addr_cache_insert_device
(
dev
);
spin_unlock_irqrestore
(
&
pci_io_addr_cache_root
.
piar_lock
,
flags
);
}
static
inline
void
__pci_addr_cache_remove_device
(
struct
pci_dev
*
dev
)
{
struct
rb_node
*
n
;
int
removed
=
0
;
restart:
n
=
rb_first
(
&
pci_io_addr_cache_root
.
rb_root
);
while
(
n
)
{
struct
pci_io_addr_range
*
piar
;
piar
=
rb_entry
(
n
,
struct
pci_io_addr_range
,
rb_node
);
if
(
piar
->
pcidev
==
dev
)
{
rb_erase
(
n
,
&
pci_io_addr_cache_root
.
rb_root
);
removed
=
1
;
kfree
(
piar
);
goto
restart
;
}
n
=
rb_next
(
n
);
}
/* The cache no longer holds its reference to this device... */
if
(
removed
)
pci_dev_put
(
dev
);
}
/**
* pci_addr_cache_remove_device - remove pci device from addr cache
* @dev: device to remove
*
* Remove a device from the addr-cache tree.
* This is potentially expensive, since it will walk
* the tree multiple times (once per resource).
* But so what; device removal doesn't need to be that fast.
*/
static
void
pci_addr_cache_remove_device
(
struct
pci_dev
*
dev
)
{
unsigned
long
flags
;
spin_lock_irqsave
(
&
pci_io_addr_cache_root
.
piar_lock
,
flags
);
__pci_addr_cache_remove_device
(
dev
);
spin_unlock_irqrestore
(
&
pci_io_addr_cache_root
.
piar_lock
,
flags
);
}
/**
* pci_addr_cache_build - Build a cache of I/O addresses
*
* Build a cache of pci i/o addresses. This cache will be used to
* find the pci device that corresponds to a given address.
* This routine scans all pci busses to build the cache.
* Must be run late in boot process, after the pci controllers
* have been scaned for devices (after all device resources are known).
*/
void
__init
pci_addr_cache_build
(
void
)
{
struct
device_node
*
dn
;
struct
pci_dev
*
dev
=
NULL
;
if
(
!
eeh_subsystem_enabled
)
return
;
spin_lock_init
(
&
pci_io_addr_cache_root
.
piar_lock
);
while
((
dev
=
pci_get_device
(
PCI_ANY_ID
,
PCI_ANY_ID
,
dev
))
!=
NULL
)
{
/* Ignore PCI bridges ( XXX why ??) */
if
((
dev
->
class
>>
16
)
==
PCI_BASE_CLASS_BRIDGE
)
{
continue
;
}
pci_addr_cache_insert_device
(
dev
);
/* Save the BAR's; firmware doesn't restore these after EEH reset */
dn
=
pci_device_to_OF_node
(
dev
);
eeh_save_bars
(
dev
,
PCI_DN
(
dn
));
}
#ifdef DEBUG
/* Verify tree built up above, echo back the list of addrs. */
pci_addr_cache_print
(
&
pci_io_addr_cache_root
);
#endif
}
#define IS_BRIDGE(class_code) (((class_code)<<16) == PCI_BASE_CLASS_BRIDGE)
/* --------------------------------------------------------------- */
/*
Above lies the PCI Address Cache.
Below lies the EEH event infrastructure */
/* Below lies the EEH event infrastructure */
void
eeh_slot_error_detail
(
struct
pci_dn
*
pdn
,
int
severity
)
{
int
config_addr
;
unsigned
long
flags
;
int
rc
;
...
...
@@ -407,8 +121,13 @@ void eeh_slot_error_detail (struct pci_dn *pdn, int severity)
spin_lock_irqsave
(
&
slot_errbuf_lock
,
flags
);
memset
(
slot_errbuf
,
0
,
eeh_error_buf_size
);
/* Use PE configuration address, if present */
config_addr
=
pdn
->
eeh_config_addr
;
if
(
pdn
->
eeh_pe_config_addr
)
config_addr
=
pdn
->
eeh_pe_config_addr
;
rc
=
rtas_call
(
ibm_slot_error_detail
,
8
,
1
,
NULL
,
pdn
->
eeh_
config_addr
,
8
,
1
,
NULL
,
config_addr
,
BUID_HI
(
pdn
->
phb
->
buid
),
BUID_LO
(
pdn
->
phb
->
buid
),
NULL
,
0
,
virt_to_phys
(
slot_errbuf
),
...
...
@@ -428,6 +147,7 @@ void eeh_slot_error_detail (struct pci_dn *pdn, int severity)
static
int
read_slot_reset_state
(
struct
pci_dn
*
pdn
,
int
rets
[])
{
int
token
,
outputs
;
int
config_addr
;
if
(
ibm_read_slot_reset_state2
!=
RTAS_UNKNOWN_SERVICE
)
{
token
=
ibm_read_slot_reset_state2
;
...
...
@@ -438,7 +158,12 @@ static int read_slot_reset_state(struct pci_dn *pdn, int rets[])
outputs
=
3
;
}
return
rtas_call
(
token
,
3
,
outputs
,
rets
,
pdn
->
eeh_config_addr
,
/* Use PE configuration address, if present */
config_addr
=
pdn
->
eeh_config_addr
;
if
(
pdn
->
eeh_pe_config_addr
)
config_addr
=
pdn
->
eeh_pe_config_addr
;
return
rtas_call
(
token
,
3
,
outputs
,
rets
,
config_addr
,
BUID_HI
(
pdn
->
phb
->
buid
),
BUID_LO
(
pdn
->
phb
->
buid
));
}
...
...
@@ -462,7 +187,7 @@ static inline unsigned long eeh_token_to_phys(unsigned long token)
/**
* Return the "partitionable endpoint" (pe) under which this device lies
*/
st
atic
st
ruct
device_node
*
find_device_pe
(
struct
device_node
*
dn
)
struct
device_node
*
find_device_pe
(
struct
device_node
*
dn
)
{
while
((
dn
->
parent
)
&&
PCI_DN
(
dn
->
parent
)
&&
(
PCI_DN
(
dn
->
parent
)
->
eeh_mode
&
EEH_MODE_SUPPORTED
))
{
...
...
@@ -485,6 +210,11 @@ static void __eeh_mark_slot (struct device_node *dn, int mode_flag)
if
(
PCI_DN
(
dn
))
{
PCI_DN
(
dn
)
->
eeh_mode
|=
mode_flag
;
/* Mark the pci device driver too */
struct
pci_dev
*
dev
=
PCI_DN
(
dn
)
->
pcidev
;
if
(
dev
&&
dev
->
driver
)
dev
->
error_state
=
pci_channel_io_frozen
;
if
(
dn
->
child
)
__eeh_mark_slot
(
dn
->
child
,
mode_flag
);
}
...
...
@@ -495,6 +225,11 @@ static void __eeh_mark_slot (struct device_node *dn, int mode_flag)
void
eeh_mark_slot
(
struct
device_node
*
dn
,
int
mode_flag
)
{
dn
=
find_device_pe
(
dn
);
/* Back up one, since config addrs might be shared */
if
(
PCI_DN
(
dn
)
&&
PCI_DN
(
dn
)
->
eeh_pe_config_addr
)
dn
=
dn
->
parent
;
PCI_DN
(
dn
)
->
eeh_mode
|=
mode_flag
;
__eeh_mark_slot
(
dn
->
child
,
mode_flag
);
}
...
...
@@ -516,7 +251,13 @@ void eeh_clear_slot (struct device_node *dn, int mode_flag)
{
unsigned
long
flags
;
spin_lock_irqsave
(
&
confirm_error_lock
,
flags
);
dn
=
find_device_pe
(
dn
);
/* Back up one, since config addrs might be shared */
if
(
PCI_DN
(
dn
)
&&
PCI_DN
(
dn
)
->
eeh_pe_config_addr
)
dn
=
dn
->
parent
;
PCI_DN
(
dn
)
->
eeh_mode
&=
~
mode_flag
;
PCI_DN
(
dn
)
->
eeh_check_count
=
0
;
__eeh_clear_slot
(
dn
->
child
,
mode_flag
);
...
...
@@ -544,15 +285,16 @@ int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev)
int
rets
[
3
];
unsigned
long
flags
;
struct
pci_dn
*
pdn
;
enum
pci_channel_state
state
;
int
rc
=
0
;
__get_cpu_var
(
total_mmio_ffs
)
++
;
total_mmio_ffs
++
;
if
(
!
eeh_subsystem_enabled
)
return
0
;
if
(
!
dn
)
{
__get_cpu_var
(
no_dn
)
++
;
no_dn
++
;
return
0
;
}
pdn
=
PCI_DN
(
dn
);
...
...
@@ -560,7 +302,7 @@ int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev)
/* Access to IO BARs might get this far and still not want checking. */
if
(
!
(
pdn
->
eeh_mode
&
EEH_MODE_SUPPORTED
)
||
pdn
->
eeh_mode
&
EEH_MODE_NOCHECK
)
{
__get_cpu_var
(
ignored_check
)
++
;
ignored_check
++
;
#ifdef DEBUG
printk
(
"EEH:ignored check (%x) for %s %s
\n
"
,
pdn
->
eeh_mode
,
pci_name
(
dev
),
dn
->
full_name
);
...
...
@@ -568,8 +310,8 @@ int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev)
return
0
;
}
if
(
!
pdn
->
eeh_config_addr
)
{
__get_cpu_var
(
no_cfg_addr
)
++
;
if
(
!
pdn
->
eeh_config_addr
&&
!
pdn
->
eeh_pe_config_addr
)
{
no_cfg_addr
++
;
return
0
;
}
...
...
@@ -611,7 +353,7 @@ int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev)
if
(
ret
!=
0
)
{
printk
(
KERN_WARNING
"EEH: read_slot_reset_state() failed; rc=%d dn=%s
\n
"
,
ret
,
dn
->
full_name
);
__get_cpu_var
(
false_positives
)
++
;
false_positives
++
;
rc
=
0
;
goto
dn_unlock
;
}
...
...
@@ -620,14 +362,14 @@ int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev)
if
(
rets
[
1
]
!=
1
)
{
printk
(
KERN_WARNING
"EEH: event on unsupported device, rc=%d dn=%s
\n
"
,
ret
,
dn
->
full_name
);
__get_cpu_var
(
false_positives
)
++
;
false_positives
++
;
rc
=
0
;
goto
dn_unlock
;
}
/* If not the kind of error we know about, punt. */
if
(
rets
[
0
]
!=
2
&&
rets
[
0
]
!=
4
&&
rets
[
0
]
!=
5
)
{
__get_cpu_var
(
false_positives
)
++
;
false_positives
++
;
rc
=
0
;
goto
dn_unlock
;
}
...
...
@@ -635,12 +377,12 @@ int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev)
/* Note that config-io to empty slots may fail;
* we recognize empty because they don't have children. */
if
((
rets
[
0
]
==
5
)
&&
(
dn
->
child
==
NULL
))
{
__get_cpu_var
(
false_positives
)
++
;
false_positives
++
;
rc
=
0
;
goto
dn_unlock
;
}
__get_cpu_var
(
slot_resets
)
++
;
slot_resets
++
;
/* Avoid repeated reports of this failure, including problems
* with other functions on this device, and functions under
...
...
@@ -648,8 +390,13 @@ int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev)
eeh_mark_slot
(
dn
,
EEH_MODE_ISOLATED
);
spin_unlock_irqrestore
(
&
confirm_error_lock
,
flags
);
eeh_send_failure_event
(
dn
,
dev
,
rets
[
0
],
rets
[
2
]);
state
=
pci_channel_io_normal
;
if
((
rets
[
0
]
==
2
)
||
(
rets
[
0
]
==
4
))
state
=
pci_channel_io_frozen
;
if
(
rets
[
0
]
==
5
)
state
=
pci_channel_io_perm_failure
;
eeh_send_failure_event
(
dn
,
dev
,
state
,
rets
[
2
]);
/* Most EEH events are due to device driver bugs. Having
* a stack trace will help the device-driver authors figure
* out what happened. So print that out. */
...
...
@@ -685,7 +432,7 @@ unsigned long eeh_check_failure(const volatile void __iomem *token, unsigned lon
addr
=
eeh_token_to_phys
((
unsigned
long
__force
)
token
);
dev
=
pci_get_device_by_addr
(
addr
);
if
(
!
dev
)
{
__get_cpu_var
(
no_device
)
++
;
no_device
++
;
return
val
;
}
...
...
@@ -716,11 +463,16 @@ eeh_slot_availability(struct pci_dn *pdn)
if
(
rc
)
return
rc
;
if
(
rets
[
1
]
==
0
)
return
-
1
;
/* EEH is not supported */
if
(
rets
[
0
]
==
0
)
return
0
;
/* Oll Korrect */
if
(
rets
[
0
]
==
0
)
return
0
;
/* Oll Korrect */
if
(
rets
[
0
]
==
5
)
{
if
(
rets
[
2
]
==
0
)
return
-
1
;
/* permanently unavailable */
return
rets
[
2
];
/* number of millisecs to wait */
}
if
(
rets
[
0
]
==
1
)
return
250
;
printk
(
KERN_ERR
"EEH: Slot unavailable: rc=%d, rets=%d %d %d
\n
"
,
rc
,
rets
[
0
],
rets
[
1
],
rets
[
2
]);
return
-
1
;
}
...
...
@@ -737,6 +489,7 @@ eeh_slot_availability(struct pci_dn *pdn)
static
void
rtas_pci_slot_reset
(
struct
pci_dn
*
pdn
,
int
state
)
{
int
config_addr
;
int
rc
;
BUG_ON
(
pdn
==
NULL
);
...
...
@@ -747,8 +500,13 @@ rtas_pci_slot_reset(struct pci_dn *pdn, int state)
return
;
}
/* Use PE configuration address, if present */
config_addr
=
pdn
->
eeh_config_addr
;
if
(
pdn
->
eeh_pe_config_addr
)
config_addr
=
pdn
->
eeh_pe_config_addr
;
rc
=
rtas_call
(
ibm_set_slot_reset
,
4
,
1
,
NULL
,
pdn
->
eeh_
config_addr
,
config_addr
,
BUID_HI
(
pdn
->
phb
->
buid
),
BUID_LO
(
pdn
->
phb
->
buid
),
state
);
...
...
@@ -761,9 +519,11 @@ rtas_pci_slot_reset(struct pci_dn *pdn, int state)
/** rtas_set_slot_reset -- assert the pci #RST line for 1/4 second
* dn -- device node to be reset.
*
* Return 0 if success, else a non-zero value.
*/
void
int
rtas_set_slot_reset
(
struct
pci_dn
*
pdn
)
{
int
i
,
rc
;
...
...
@@ -793,10 +553,21 @@ rtas_set_slot_reset(struct pci_dn *pdn)
* ready to be used; if not, wait for recovery. */
for
(
i
=
0
;
i
<
10
;
i
++
)
{
rc
=
eeh_slot_availability
(
pdn
);
if
(
rc
<=
0
)
break
;
if
(
rc
<
0
)
printk
(
KERN_ERR
"EEH: failed (%d) to reset slot %s
\n
"
,
rc
,
pdn
->
node
->
full_name
);
if
(
rc
==
0
)
return
0
;
if
(
rc
<
0
)
return
-
1
;
msleep
(
rc
+
100
);
}
rc
=
eeh_slot_availability
(
pdn
);
if
(
rc
)
printk
(
KERN_ERR
"EEH: timeout resetting slot %s
\n
"
,
pdn
->
node
->
full_name
);
return
rc
;
}
/* ------------------------------------------------------- */
...
...
@@ -851,7 +622,7 @@ void eeh_restore_bars(struct pci_dn *pdn)
if
(
!
pdn
)
return
;
if
(
!
pdn
->
eeh_is_bridge
)
if
(
(
pdn
->
eeh_mode
&
EEH_MODE_SUPPORTED
)
&&
!
IS_BRIDGE
(
pdn
->
class_code
)
)
__restore_bars
(
pdn
);
dn
=
pdn
->
node
->
child
;
...
...
@@ -869,30 +640,30 @@ void eeh_restore_bars(struct pci_dn *pdn)
* PCI devices are added individuallly; but, for the restore,
* an entire slot is reset at a time.
*/
static
void
eeh_save_bars
(
struct
pci_d
ev
*
pdev
,
struct
pci_d
n
*
pdn
)
static
void
eeh_save_bars
(
struct
pci_dn
*
pdn
)
{
int
i
;
if
(
!
pd
ev
||
!
pd
n
)
if
(
!
pdn
)
return
;
for
(
i
=
0
;
i
<
16
;
i
++
)
pci_read_config_dword
(
pdev
,
i
*
4
,
&
pdn
->
config_space
[
i
]);
if
(
pdev
->
hdr_type
==
PCI_HEADER_TYPE_BRIDGE
)
pdn
->
eeh_is_bridge
=
1
;
rtas_read_config
(
pdn
,
i
*
4
,
4
,
&
pdn
->
config_space
[
i
]);
}
void
rtas_configure_bridge
(
struct
pci_dn
*
pdn
)
{
int
token
=
rtas_token
(
"ibm,configure-bridge"
)
;
int
config_addr
;
int
rc
;
if
(
token
==
RTAS_UNKNOWN_SERVICE
)
return
;
rc
=
rtas_call
(
token
,
3
,
1
,
NULL
,
pdn
->
eeh_config_addr
,
/* Use PE configuration address, if present */
config_addr
=
pdn
->
eeh_config_addr
;
if
(
pdn
->
eeh_pe_config_addr
)
config_addr
=
pdn
->
eeh_pe_config_addr
;
rc
=
rtas_call
(
ibm_configure_bridge
,
3
,
1
,
NULL
,
config_addr
,
BUID_HI
(
pdn
->
phb
->
buid
),
BUID_LO
(
pdn
->
phb
->
buid
));
if
(
rc
)
{
...
...
@@ -927,6 +698,7 @@ static void *early_enable_eeh(struct device_node *dn, void *data)
int
enable
;
struct
pci_dn
*
pdn
=
PCI_DN
(
dn
);
pdn
->
class_code
=
0
;
pdn
->
eeh_mode
=
0
;
pdn
->
eeh_check_count
=
0
;
pdn
->
eeh_freeze_count
=
0
;
...
...
@@ -943,6 +715,7 @@ static void *early_enable_eeh(struct device_node *dn, void *data)
pdn
->
eeh_mode
|=
EEH_MODE_NOCHECK
;
return
NULL
;
}
pdn
->
class_code
=
*
class_code
;
/*
* Now decide if we are going to "Disable" EEH checking
...
...
@@ -953,8 +726,10 @@ static void *early_enable_eeh(struct device_node *dn, void *data)
* But there are a few cases like display devices that make sense.
*/
enable
=
1
;
/* i.e. we will do checking */
#if 0
if ((*class_code >> 16) == PCI_BASE_CLASS_DISPLAY)
enable = 0;
#endif
if
(
!
enable
)
pdn
->
eeh_mode
|=
EEH_MODE_NOCHECK
;
...
...
@@ -973,8 +748,22 @@ static void *early_enable_eeh(struct device_node *dn, void *data)
eeh_subsystem_enabled
=
1
;
pdn
->
eeh_mode
|=
EEH_MODE_SUPPORTED
;
pdn
->
eeh_config_addr
=
regs
[
0
];
/* If the newer, better, ibm,get-config-addr-info is supported,
* then use that instead. */
pdn
->
eeh_pe_config_addr
=
0
;
if
(
ibm_get_config_addr_info
!=
RTAS_UNKNOWN_SERVICE
)
{
unsigned
int
rets
[
2
];
ret
=
rtas_call
(
ibm_get_config_addr_info
,
4
,
2
,
rets
,
pdn
->
eeh_config_addr
,
info
->
buid_hi
,
info
->
buid_lo
,
0
);
if
(
ret
==
0
)
pdn
->
eeh_pe_config_addr
=
rets
[
0
];
}
#ifdef DEBUG
printk
(
KERN_DEBUG
"EEH: %s: eeh enabled
\n
"
,
dn
->
full_name
);
printk
(
KERN_DEBUG
"EEH: %s: eeh enabled, config=%x pe_config=%x
\n
"
,
dn
->
full_name
,
pdn
->
eeh_config_addr
,
pdn
->
eeh_pe_config_addr
);
#endif
}
else
{
...
...
@@ -993,6 +782,7 @@ static void *early_enable_eeh(struct device_node *dn, void *data)
dn
->
full_name
);
}
eeh_save_bars
(
pdn
);
return
NULL
;
}
...
...
@@ -1026,6 +816,8 @@ void __init eeh_init(void)
ibm_read_slot_reset_state2
=
rtas_token
(
"ibm,read-slot-reset-state2"
);
ibm_read_slot_reset_state
=
rtas_token
(
"ibm,read-slot-reset-state"
);
ibm_slot_error_detail
=
rtas_token
(
"ibm,slot-error-detail"
);
ibm_get_config_addr_info
=
rtas_token
(
"ibm,get-config-addr-info"
);
ibm_configure_bridge
=
rtas_token
(
"ibm,configure-bridge"
);
if
(
ibm_set_eeh_option
==
RTAS_UNKNOWN_SERVICE
)
return
;
...
...
@@ -1080,12 +872,10 @@ void eeh_add_device_early(struct device_node *dn)
if
(
!
dn
||
!
PCI_DN
(
dn
))
return
;
phb
=
PCI_DN
(
dn
)
->
phb
;
if
(
NULL
==
phb
||
0
==
phb
->
buid
)
{
printk
(
KERN_WARNING
"EEH: Expected buid but found none for %s
\n
"
,
dn
->
full_name
);
dump_stack
();
/* USB Bus children of PCI devices will not have BUID's */
if
(
NULL
==
phb
||
0
==
phb
->
buid
)
return
;
}
info
.
buid_hi
=
BUID_HI
(
phb
->
buid
);
info
.
buid_lo
=
BUID_LO
(
phb
->
buid
);
...
...
@@ -1127,7 +917,6 @@ void eeh_add_device_late(struct pci_dev *dev)
pdn
->
pcidev
=
dev
;
pci_addr_cache_insert_device
(
dev
);
eeh_save_bars
(
dev
,
pdn
);
}
EXPORT_SYMBOL_GPL
(
eeh_add_device_late
);
...
...
@@ -1175,25 +964,9 @@ EXPORT_SYMBOL_GPL(eeh_remove_bus_device);
static
int
proc_eeh_show
(
struct
seq_file
*
m
,
void
*
v
)
{
unsigned
int
cpu
;
unsigned
long
ffs
=
0
,
positives
=
0
,
failures
=
0
;
unsigned
long
resets
=
0
;
unsigned
long
no_dev
=
0
,
no_dn
=
0
,
no_cfg
=
0
,
no_check
=
0
;
for_each_cpu
(
cpu
)
{
ffs
+=
per_cpu
(
total_mmio_ffs
,
cpu
);
positives
+=
per_cpu
(
false_positives
,
cpu
);
failures
+=
per_cpu
(
ignored_failures
,
cpu
);
resets
+=
per_cpu
(
slot_resets
,
cpu
);
no_dev
+=
per_cpu
(
no_device
,
cpu
);
no_dn
+=
per_cpu
(
no_dn
,
cpu
);
no_cfg
+=
per_cpu
(
no_cfg_addr
,
cpu
);
no_check
+=
per_cpu
(
ignored_check
,
cpu
);
}
if
(
0
==
eeh_subsystem_enabled
)
{
seq_printf
(
m
,
"EEH Subsystem is globally disabled
\n
"
);
seq_printf
(
m
,
"eeh_total_mmio_ffs=%ld
\n
"
,
ffs
);
seq_printf
(
m
,
"eeh_total_mmio_ffs=%ld
\n
"
,
total_mmio_
ffs
);
}
else
{
seq_printf
(
m
,
"EEH Subsystem is enabled
\n
"
);
seq_printf
(
m
,
...
...
@@ -1205,8 +978,10 @@ static int proc_eeh_show(struct seq_file *m, void *v)
"eeh_false_positives=%ld
\n
"
"eeh_ignored_failures=%ld
\n
"
"eeh_slot_resets=%ld
\n
"
,
no_dev
,
no_dn
,
no_cfg
,
no_check
,
ffs
,
positives
,
failures
,
resets
);
no_device
,
no_dn
,
no_cfg_addr
,
ignored_check
,
total_mmio_ffs
,
false_positives
,
ignored_failures
,
slot_resets
);
}
return
0
;
...
...
arch/powerpc/platforms/pseries/eeh_cache.c
0 → 100644
View file @
a62e6848
/*
* eeh_cache.c
* PCI address cache; allows the lookup of PCI devices based on I/O address
*
* Copyright (C) 2004 Linas Vepstas <linas@austin.ibm.com> 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
* 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/list.h>
#include <linux/pci.h>
#include <linux/rbtree.h>
#include <linux/spinlock.h>
#include <asm/atomic.h>
#include <asm/pci-bridge.h>
#include <asm/ppc-pci.h>
#undef DEBUG
/**
* The pci address cache subsystem. This subsystem places
* PCI device address resources into a red-black tree, sorted
* according to the address range, so that given only an i/o
* address, the corresponding PCI device can be **quickly**
* found. It is safe to perform an address lookup in an interrupt
* context; this ability is an important feature.
*
* Currently, the only customer of this code is the EEH subsystem;
* thus, this code has been somewhat tailored to suit EEH better.
* In particular, the cache does *not* hold the addresses of devices
* for which EEH is not enabled.
*
* (Implementation Note: The RB tree seems to be better/faster
* 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
rb_node
rb_node
;
unsigned
long
addr_lo
;
unsigned
long
addr_hi
;
struct
pci_dev
*
pcidev
;
unsigned
int
flags
;
};
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
)
{
struct
rb_node
*
n
=
pci_io_addr_cache_root
.
rb_root
.
rb_node
;
while
(
n
)
{
struct
pci_io_addr_range
*
piar
;
piar
=
rb_entry
(
n
,
struct
pci_io_addr_range
,
rb_node
);
if
(
addr
<
piar
->
addr_lo
)
{
n
=
n
->
rb_left
;
}
else
{
if
(
addr
>
piar
->
addr_hi
)
{
n
=
n
->
rb_right
;
}
else
{
pci_dev_get
(
piar
->
pcidev
);
return
piar
->
pcidev
;
}
}
}
return
NULL
;
}
/**
* pci_get_device_by_addr - 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
* that implements this address. Be sure to pci_dev_put the device
* when finished. I/O port numbers are assumed to be offset
* 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
*
dev
;
unsigned
long
flags
;
spin_lock_irqsave
(
&
pci_io_addr_cache_root
.
piar_lock
,
flags
);
dev
=
__pci_get_device_by_addr
(
addr
);
spin_unlock_irqrestore
(
&
pci_io_addr_cache_root
.
piar_lock
,
flags
);
return
dev
;
}
#ifdef DEBUG
/*
* Handy-dandy debug print routine, does nothing more
* than print out the contents of our addr cache.
*/
static
void
pci_addr_cache_print
(
struct
pci_io_addr_cache
*
cache
)
{
struct
rb_node
*
n
;
int
cnt
=
0
;
n
=
rb_first
(
&
cache
->
rb_root
);
while
(
n
)
{
struct
pci_io_addr_range
*
piar
;
piar
=
rb_entry
(
n
,
struct
pci_io_addr_range
,
rb_node
);
printk
(
KERN_DEBUG
"PCI: %s addr range %d [%lx-%lx]: %s
\n
"
,
(
piar
->
flags
&
IORESOURCE_IO
)
?
"i/o"
:
"mem"
,
cnt
,
piar
->
addr_lo
,
piar
->
addr_hi
,
pci_name
(
piar
->
pcidev
));
cnt
++
;
n
=
rb_next
(
n
);
}
}
#endif
/* Insert address range into the rb tree. */
static
struct
pci_io_addr_range
*
pci_addr_cache_insert
(
struct
pci_dev
*
dev
,
unsigned
long
alo
,
unsigned
long
ahi
,
unsigned
int
flags
)
{
struct
rb_node
**
p
=
&
pci_io_addr_cache_root
.
rb_root
.
rb_node
;
struct
rb_node
*
parent
=
NULL
;
struct
pci_io_addr_range
*
piar
;
/* Walk tree, find a place to insert into tree */
while
(
*
p
)
{
parent
=
*
p
;
piar
=
rb_entry
(
parent
,
struct
pci_io_addr_range
,
rb_node
);
if
(
ahi
<
piar
->
addr_lo
)
{
p
=
&
parent
->
rb_left
;
}
else
if
(
alo
>
piar
->
addr_hi
)
{
p
=
&
parent
->
rb_right
;
}
else
{
if
(
dev
!=
piar
->
pcidev
||
alo
!=
piar
->
addr_lo
||
ahi
!=
piar
->
addr_hi
)
{
printk
(
KERN_WARNING
"PIAR: overlapping address range
\n
"
);
}
return
piar
;
}
}
piar
=
(
struct
pci_io_addr_range
*
)
kmalloc
(
sizeof
(
struct
pci_io_addr_range
),
GFP_ATOMIC
);
if
(
!
piar
)
return
NULL
;
piar
->
addr_lo
=
alo
;
piar
->
addr_hi
=
ahi
;
piar
->
pcidev
=
dev
;
piar
->
flags
=
flags
;
#ifdef DEBUG
printk
(
KERN_DEBUG
"PIAR: insert range=[%lx:%lx] dev=%s
\n
"
,
alo
,
ahi
,
pci_name
(
dev
));
#endif
rb_link_node
(
&
piar
->
rb_node
,
parent
,
p
);
rb_insert_color
(
&
piar
->
rb_node
,
&
pci_io_addr_cache_root
.
rb_root
);
return
piar
;
}
static
void
__pci_addr_cache_insert_device
(
struct
pci_dev
*
dev
)
{
struct
device_node
*
dn
;
struct
pci_dn
*
pdn
;
int
i
;
int
inserted
=
0
;
dn
=
pci_device_to_OF_node
(
dev
);
if
(
!
dn
)
{
printk
(
KERN_WARNING
"PCI: no pci dn found for dev=%s
\n
"
,
pci_name
(
dev
));
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
)
{
#ifdef DEBUG
printk
(
KERN_INFO
"PCI: skip building address cache for=%s - %s
\n
"
,
pci_name
(
dev
),
pdn
->
node
->
full_name
);
#endif
return
;
}
/* The cache holds a reference to the device... */
pci_dev_get
(
dev
);
/* Walk resources on this device, poke them into the tree */
for
(
i
=
0
;
i
<
DEVICE_COUNT_RESOURCE
;
i
++
)
{
unsigned
long
start
=
pci_resource_start
(
dev
,
i
);
unsigned
long
end
=
pci_resource_end
(
dev
,
i
);
unsigned
int
flags
=
pci_resource_flags
(
dev
,
i
);
/* We are interested only bus addresses, not dma or other stuff */
if
(
0
==
(
flags
&
(
IORESOURCE_IO
|
IORESOURCE_MEM
)))
continue
;
if
(
start
==
0
||
~
start
==
0
||
end
==
0
||
~
end
==
0
)
continue
;
pci_addr_cache_insert
(
dev
,
start
,
end
,
flags
);
inserted
=
1
;
}
/* If there was nothing to add, the cache has no reference... */
if
(
!
inserted
)
pci_dev_put
(
dev
);
}
/**
* pci_addr_cache_insert_device - Add a device to the address cache
* @dev: PCI device whose I/O addresses we are interested in.
*
* In order to support the fast lookup of devices based on addresses,
* we maintain a cache of devices that can be quickly searched.
* This routine adds a device to that cache.
*/
void
pci_addr_cache_insert_device
(
struct
pci_dev
*
dev
)
{
unsigned
long
flags
;
spin_lock_irqsave
(
&
pci_io_addr_cache_root
.
piar_lock
,
flags
);
__pci_addr_cache_insert_device
(
dev
);
spin_unlock_irqrestore
(
&
pci_io_addr_cache_root
.
piar_lock
,
flags
);
}
static
inline
void
__pci_addr_cache_remove_device
(
struct
pci_dev
*
dev
)
{
struct
rb_node
*
n
;
int
removed
=
0
;
restart:
n
=
rb_first
(
&
pci_io_addr_cache_root
.
rb_root
);
while
(
n
)
{
struct
pci_io_addr_range
*
piar
;
piar
=
rb_entry
(
n
,
struct
pci_io_addr_range
,
rb_node
);
if
(
piar
->
pcidev
==
dev
)
{
rb_erase
(
n
,
&
pci_io_addr_cache_root
.
rb_root
);
removed
=
1
;
kfree
(
piar
);
goto
restart
;
}
n
=
rb_next
(
n
);
}
/* The cache no longer holds its reference to this device... */
if
(
removed
)
pci_dev_put
(
dev
);
}
/**
* pci_addr_cache_remove_device - remove pci device from addr cache
* @dev: device to remove
*
* Remove a device from the addr-cache tree.
* This is potentially expensive, since it will walk
* the tree multiple times (once per resource).
* But so what; device removal doesn't need to be that fast.
*/
void
pci_addr_cache_remove_device
(
struct
pci_dev
*
dev
)
{
unsigned
long
flags
;
spin_lock_irqsave
(
&
pci_io_addr_cache_root
.
piar_lock
,
flags
);
__pci_addr_cache_remove_device
(
dev
);
spin_unlock_irqrestore
(
&
pci_io_addr_cache_root
.
piar_lock
,
flags
);
}
/**
* pci_addr_cache_build - Build a cache of I/O addresses
*
* Build a cache of pci i/o addresses. This cache will be used to
* find the pci device that corresponds to a given address.
* This routine scans all pci busses to build the cache.
* Must be run late in boot process, after the pci controllers
* have been scaned for devices (after all device resources are known).
*/
void
__init
pci_addr_cache_build
(
void
)
{
struct
device_node
*
dn
;
struct
pci_dev
*
dev
=
NULL
;
spin_lock_init
(
&
pci_io_addr_cache_root
.
piar_lock
);
while
((
dev
=
pci_get_device
(
PCI_ANY_ID
,
PCI_ANY_ID
,
dev
))
!=
NULL
)
{
/* Ignore PCI bridges */
if
((
dev
->
class
>>
16
)
==
PCI_BASE_CLASS_BRIDGE
)
continue
;
pci_addr_cache_insert_device
(
dev
);
dn
=
pci_device_to_OF_node
(
dev
);
pci_dev_get
(
dev
);
/* matching put is in eeh_remove_device() */
PCI_DN
(
dn
)
->
pcidev
=
dev
;
}
#ifdef DEBUG
/* Verify tree built up above, echo back the list of addrs. */
pci_addr_cache_print
(
&
pci_io_addr_cache_root
);
#endif
}
arch/powerpc/platforms/pseries/eeh_driver.c
0 → 100644
View file @
a62e6848
/*
* PCI Error Recovery Driver for RPA-compliant PPC64 platform.
* Copyright (C) 2004, 2005 Linas Vepstas <linas@linas.org>
*
* All rights reserved.
*
* 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, GOOD TITLE or
* NON INFRINGEMENT. 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Send feedback to <linas@us.ibm.com>
*
*/
#include <linux/delay.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/notifier.h>
#include <linux/pci.h>
#include <asm/eeh.h>
#include <asm/eeh_event.h>
#include <asm/ppc-pci.h>
#include <asm/pci-bridge.h>
#include <asm/prom.h>
#include <asm/rtas.h>
static
inline
const
char
*
pcid_name
(
struct
pci_dev
*
pdev
)
{
if
(
pdev
->
dev
.
driver
)
return
pdev
->
dev
.
driver
->
name
;
return
""
;
}
#ifdef DEBUG
static
void
print_device_node_tree
(
struct
pci_dn
*
pdn
,
int
dent
)
{
int
i
;
if
(
!
pdn
)
return
;
for
(
i
=
0
;
i
<
dent
;
i
++
)
printk
(
" "
);
printk
(
"dn=%s mode=%x
\t
cfg_addr=%x pe_addr=%x
\t
full=%s
\n
"
,
pdn
->
node
->
name
,
pdn
->
eeh_mode
,
pdn
->
eeh_config_addr
,
pdn
->
eeh_pe_config_addr
,
pdn
->
node
->
full_name
);
dent
+=
3
;
struct
device_node
*
pc
=
pdn
->
node
->
child
;
while
(
pc
)
{
print_device_node_tree
(
PCI_DN
(
pc
),
dent
);
pc
=
pc
->
sibling
;
}
}
#endif
/**
* irq_in_use - return true if this irq is being used
*/
static
int
irq_in_use
(
unsigned
int
irq
)
{
int
rc
=
0
;
unsigned
long
flags
;
struct
irq_desc
*
desc
=
irq_desc
+
irq
;
spin_lock_irqsave
(
&
desc
->
lock
,
flags
);
if
(
desc
->
action
)
rc
=
1
;
spin_unlock_irqrestore
(
&
desc
->
lock
,
flags
);
return
rc
;
}
/* ------------------------------------------------------- */
/** eeh_report_error - report an EEH error to each device,
* collect up and merge the device responses.
*/
static
void
eeh_report_error
(
struct
pci_dev
*
dev
,
void
*
userdata
)
{
enum
pci_ers_result
rc
,
*
res
=
userdata
;
struct
pci_driver
*
driver
=
dev
->
driver
;
dev
->
error_state
=
pci_channel_io_frozen
;
if
(
!
driver
)
return
;
if
(
irq_in_use
(
dev
->
irq
))
{
struct
device_node
*
dn
=
pci_device_to_OF_node
(
dev
);
PCI_DN
(
dn
)
->
eeh_mode
|=
EEH_MODE_IRQ_DISABLED
;
disable_irq_nosync
(
dev
->
irq
);
}
if
(
!
driver
->
err_handler
)
return
;
if
(
!
driver
->
err_handler
->
error_detected
)
return
;
rc
=
driver
->
err_handler
->
error_detected
(
dev
,
pci_channel_io_frozen
);
if
(
*
res
==
PCI_ERS_RESULT_NONE
)
*
res
=
rc
;
if
(
*
res
==
PCI_ERS_RESULT_NEED_RESET
)
return
;
if
(
*
res
==
PCI_ERS_RESULT_DISCONNECT
&&
rc
==
PCI_ERS_RESULT_NEED_RESET
)
*
res
=
rc
;
}
/** eeh_report_reset -- tell this device that the pci slot
* has been reset.
*/
static
void
eeh_report_reset
(
struct
pci_dev
*
dev
,
void
*
userdata
)
{
struct
pci_driver
*
driver
=
dev
->
driver
;
struct
device_node
*
dn
=
pci_device_to_OF_node
(
dev
);
if
(
!
driver
)
return
;
if
((
PCI_DN
(
dn
)
->
eeh_mode
)
&
EEH_MODE_IRQ_DISABLED
)
{
PCI_DN
(
dn
)
->
eeh_mode
&=
~
EEH_MODE_IRQ_DISABLED
;
enable_irq
(
dev
->
irq
);
}
if
(
!
driver
->
err_handler
)
return
;
if
(
!
driver
->
err_handler
->
slot_reset
)
return
;
driver
->
err_handler
->
slot_reset
(
dev
);
}
static
void
eeh_report_resume
(
struct
pci_dev
*
dev
,
void
*
userdata
)
{
struct
pci_driver
*
driver
=
dev
->
driver
;
dev
->
error_state
=
pci_channel_io_normal
;
if
(
!
driver
)
return
;
if
(
!
driver
->
err_handler
)
return
;
if
(
!
driver
->
err_handler
->
resume
)
return
;
driver
->
err_handler
->
resume
(
dev
);
}
static
void
eeh_report_failure
(
struct
pci_dev
*
dev
,
void
*
userdata
)
{
struct
pci_driver
*
driver
=
dev
->
driver
;
dev
->
error_state
=
pci_channel_io_perm_failure
;
if
(
!
driver
)
return
;
if
(
irq_in_use
(
dev
->
irq
))
{
struct
device_node
*
dn
=
pci_device_to_OF_node
(
dev
);
PCI_DN
(
dn
)
->
eeh_mode
|=
EEH_MODE_IRQ_DISABLED
;
disable_irq_nosync
(
dev
->
irq
);
}
if
(
!
driver
->
err_handler
)
return
;
if
(
!
driver
->
err_handler
->
error_detected
)
return
;
driver
->
err_handler
->
error_detected
(
dev
,
pci_channel_io_perm_failure
);
}
/* ------------------------------------------------------- */
/**
* handle_eeh_events -- reset a PCI device after hard lockup.
*
* pSeries systems will isolate a PCI slot if the PCI-Host
* bridge detects address or data parity errors, DMA's
* occuring to wild addresses (which usually happen due to
* bugs in device drivers or in PCI adapter firmware).
* Slot isolations also occur if #SERR, #PERR or other misc
* PCI-related errors are detected.
*
* Recovery process consists of unplugging the device driver
* (which generated hotplug events to userspace), then issuing
* a PCI #RST to the device, then reconfiguring the PCI config
* space for all bridges & devices under this slot, and then
* finally restarting the device drivers (which cause a second
* set of hotplug events to go out to userspace).
*/
/**
* eeh_reset_device() -- perform actual reset of a pci slot
* Args: bus: pointer to the pci bus structure corresponding
* to the isolated slot. A non-null value will
* cause all devices under the bus to be removed
* and then re-added.
* pe_dn: pointer to a "Partionable Endpoint" device node.
* This is the top-level structure on which pci
* bus resets can be performed.
*/
static
int
eeh_reset_device
(
struct
pci_dn
*
pe_dn
,
struct
pci_bus
*
bus
)
{
int
rc
;
if
(
bus
)
pcibios_remove_pci_devices
(
bus
);
/* Reset the pci controller. (Asserts RST#; resets config space).
* Reconfigure bridges and devices. Don't try to bring the system
* up if the reset failed for some reason. */
rc
=
rtas_set_slot_reset
(
pe_dn
);
if
(
rc
)
return
rc
;
/* New-style config addrs might be shared across multiple devices,
* Walk over all functions on this device */
if
(
pe_dn
->
eeh_pe_config_addr
)
{
struct
device_node
*
pe
=
pe_dn
->
node
;
pe
=
pe
->
parent
->
child
;
while
(
pe
)
{
struct
pci_dn
*
ppe
=
PCI_DN
(
pe
);
if
(
pe_dn
->
eeh_pe_config_addr
==
ppe
->
eeh_pe_config_addr
)
{
rtas_configure_bridge
(
ppe
);
eeh_restore_bars
(
ppe
);
}
pe
=
pe
->
sibling
;
}
}
else
{
rtas_configure_bridge
(
pe_dn
);
eeh_restore_bars
(
pe_dn
);
}
/* Give the system 5 seconds to finish running the user-space
* hotplug shutdown scripts, e.g. ifdown for ethernet. Yes,
* this is a hack, but if we don't do this, and try to bring
* the device up before the scripts have taken it down,
* potentially weird things happen.
*/
if
(
bus
)
{
ssleep
(
5
);
pcibios_add_pci_devices
(
bus
);
}
return
0
;
}
/* The longest amount of time to wait for a pci device
* to come back on line, in seconds.
*/
#define MAX_WAIT_FOR_RECOVERY 15
void
handle_eeh_events
(
struct
eeh_event
*
event
)
{
struct
device_node
*
frozen_dn
;
struct
pci_dn
*
frozen_pdn
;
struct
pci_bus
*
frozen_bus
;
int
rc
=
0
;
enum
pci_ers_result
result
=
PCI_ERS_RESULT_NONE
;
frozen_dn
=
find_device_pe
(
event
->
dn
);
frozen_bus
=
pcibios_find_pci_bus
(
frozen_dn
);
if
(
!
frozen_dn
)
{
printk
(
KERN_ERR
"EEH: Error: Cannot find partition endpoint for %s
\n
"
,
pci_name
(
event
->
dev
));
return
;
}
/* There are two different styles for coming up with the PE.
* In the old style, it was the highest EEH-capable device
* which was always an EADS pci bridge. In the new style,
* there might not be any EADS bridges, and even when there are,
* the firmware marks them as "EEH incapable". So another
* two-step is needed to find the pci bus.. */
if
(
!
frozen_bus
)
frozen_bus
=
pcibios_find_pci_bus
(
frozen_dn
->
parent
);
if
(
!
frozen_bus
)
{
printk
(
KERN_ERR
"EEH: Cannot find PCI bus for %s
\n
"
,
frozen_dn
->
full_name
);
return
;
}
#if 0
/* We may get "permanent failure" messages on empty slots.
* These are false alarms. Empty slots have no child dn. */
if ((event->state == pci_channel_io_perm_failure) && (frozen_device == NULL))
return;
#endif
frozen_pdn
=
PCI_DN
(
frozen_dn
);
frozen_pdn
->
eeh_freeze_count
++
;
if
(
frozen_pdn
->
eeh_freeze_count
>
EEH_MAX_ALLOWED_FREEZES
)
goto
hard_fail
;
/* If the reset state is a '5' and the time to reset is 0 (infinity)
* or is more then 15 seconds, then mark this as a permanent failure.
*/
if
((
event
->
state
==
pci_channel_io_perm_failure
)
&&
((
event
->
time_unavail
<=
0
)
||
(
event
->
time_unavail
>
MAX_WAIT_FOR_RECOVERY
*
1000
)))
goto
hard_fail
;
eeh_slot_error_detail
(
frozen_pdn
,
1
/* Temporary Error */
);
printk
(
KERN_WARNING
"EEH: This PCI device has failed %d times since last reboot: %s - %s
\n
"
,
frozen_pdn
->
eeh_freeze_count
,
pci_name
(
frozen_pdn
->
pcidev
),
pcid_name
(
frozen_pdn
->
pcidev
));
/* Walk the various device drivers attached to this slot through
* a reset sequence, giving each an opportunity to do what it needs
* to accomplish the reset. Each child gets a report of the
* status ... if any child can't handle the reset, then the entire
* slot is dlpar removed and added.
*/
pci_walk_bus
(
frozen_bus
,
eeh_report_error
,
&
result
);
/* If all device drivers were EEH-unaware, then shut
* down all of the device drivers, and hope they
* go down willingly, without panicing the system.
*/
if
(
result
==
PCI_ERS_RESULT_NONE
)
{
rc
=
eeh_reset_device
(
frozen_pdn
,
frozen_bus
);
if
(
rc
)
goto
hard_fail
;
}
/* If any device called out for a reset, then reset the slot */
if
(
result
==
PCI_ERS_RESULT_NEED_RESET
)
{
rc
=
eeh_reset_device
(
frozen_pdn
,
NULL
);
if
(
rc
)
goto
hard_fail
;
pci_walk_bus
(
frozen_bus
,
eeh_report_reset
,
0
);
}
/* If all devices reported they can proceed, the re-enable PIO */
if
(
result
==
PCI_ERS_RESULT_CAN_RECOVER
)
{
/* XXX Not supported; we brute-force reset the device */
rc
=
eeh_reset_device
(
frozen_pdn
,
NULL
);
if
(
rc
)
goto
hard_fail
;
pci_walk_bus
(
frozen_bus
,
eeh_report_reset
,
0
);
}
/* Tell all device drivers that they can resume operations */
pci_walk_bus
(
frozen_bus
,
eeh_report_resume
,
0
);
return
;
hard_fail:
/*
* About 90% of all real-life EEH failures in the field
* are due to poorly seated PCI cards. Only 10% or so are
* due to actual, failed cards.
*/
printk
(
KERN_ERR
"EEH: PCI device %s - %s has failed %d times
\n
"
"and has been permanently disabled. Please try reseating
\n
"
"this device or replacing it.
\n
"
,
pci_name
(
frozen_pdn
->
pcidev
),
pcid_name
(
frozen_pdn
->
pcidev
),
frozen_pdn
->
eeh_freeze_count
);
eeh_slot_error_detail
(
frozen_pdn
,
2
/* Permanent Error */
);
/* Notify all devices that they're about to go down. */
pci_walk_bus
(
frozen_bus
,
eeh_report_failure
,
0
);
/* Shut down the device drivers for good. */
pcibios_remove_pci_devices
(
frozen_bus
);
}
/* ---------- end of file ---------- */
arch/powerpc/platforms/pseries/eeh_event.c
View file @
a62e6848
...
...
@@ -21,6 +21,7 @@
#include <linux/list.h>
#include <linux/pci.h>
#include <asm/eeh_event.h>
#include <asm/ppc-pci.h>
/** Overview:
* EEH error states may be detected within exception handlers;
...
...
@@ -36,31 +37,6 @@ LIST_HEAD(eeh_eventlist);
static
void
eeh_thread_launcher
(
void
*
);
DECLARE_WORK
(
eeh_event_wq
,
eeh_thread_launcher
,
NULL
);
/**
* eeh_panic - call panic() for an eeh event that cannot be handled.
* The philosophy of this routine is that it is better to panic and
* halt the OS than it is to risk possible data corruption by
* oblivious device drivers that don't know better.
*
* @dev pci device that had an eeh event
* @reset_state current reset state of the device slot
*/
static
void
eeh_panic
(
struct
pci_dev
*
dev
,
int
reset_state
)
{
/*
* Since the panic_on_oops sysctl is used to halt the system
* in light of potential corruption, we can use it here.
*/
if
(
panic_on_oops
)
{
panic
(
"EEH: MMIO failure (%d) on device:%s
\n
"
,
reset_state
,
pci_name
(
dev
));
}
else
{
printk
(
KERN_INFO
"EEH: Ignored MMIO failure (%d) on device:%s
\n
"
,
reset_state
,
pci_name
(
dev
));
}
}
/**
* eeh_event_handler - dispatch EEH events. The detection of a frozen
* slot can occur inside an interrupt, where it can be hard to do
...
...
@@ -82,10 +58,16 @@ static int eeh_event_handler(void * dummy)
spin_lock_irqsave
(
&
eeh_eventlist_lock
,
flags
);
event
=
NULL
;
/* Unqueue the event, get ready to process. */
if
(
!
list_empty
(
&
eeh_eventlist
))
{
event
=
list_entry
(
eeh_eventlist
.
next
,
struct
eeh_event
,
list
);
list_del
(
&
event
->
list
);
}
if
(
event
)
eeh_mark_slot
(
event
->
dn
,
EEH_MODE_RECOVERING
);
spin_unlock_irqrestore
(
&
eeh_eventlist_lock
,
flags
);
if
(
event
==
NULL
)
break
;
...
...
@@ -93,8 +75,11 @@ static int eeh_event_handler(void * dummy)
printk
(
KERN_INFO
"EEH: Detected PCI bus error on device %s
\n
"
,
pci_name
(
event
->
dev
));
eeh_panic
(
event
->
dev
,
event
->
state
);
handle_eeh_events
(
event
);
eeh_clear_slot
(
event
->
dn
,
EEH_MODE_RECOVERING
);
pci_dev_put
(
event
->
dev
);
kfree
(
event
);
}
...
...
@@ -122,7 +107,7 @@ static void eeh_thread_launcher(void *dummy)
*/
int
eeh_send_failure_event
(
struct
device_node
*
dn
,
struct
pci_dev
*
dev
,
int
state
,
enum
pci_channel_state
state
,
int
time_unavail
)
{
unsigned
long
flags
;
...
...
drivers/ide/pci/via82cxxx.c
View file @
a62e6848
...
...
@@ -439,7 +439,7 @@ static void __devinit init_hwif_via82cxxx(ide_hwif_t *hwif)
hwif
->
speedproc
=
&
via_set_drive
;
#if defined(CONFIG_PPC_
MULTIPLATFORM
) && defined(CONFIG_PPC32)
#if defined(CONFIG_PPC_
CHRP
) && defined(CONFIG_PPC32)
if
(
_machine
==
_MACH_chrp
&&
_chrp_type
==
_CHRP_Pegasos
)
{
hwif
->
irq
=
hwif
->
channel
?
15
:
14
;
}
...
...
include/asm-powerpc/eeh.h
View file @
a62e6848
...
...
@@ -34,9 +34,11 @@ struct device_node;
extern
int
eeh_subsystem_enabled
;
/* 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_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)
/* Max number of EEH freezes allowed before we consider the device
* to be permanently disabled. */
...
...
include/asm-powerpc/eeh_event.h
View file @
a62e6848
...
...
@@ -30,7 +30,7 @@ struct eeh_event {
struct
list_head
list
;
struct
device_node
*
dn
;
/* struct device node */
struct
pci_dev
*
dev
;
/* affected device */
int
state
;
enum
pci_channel_state
state
;
/* PCI bus state for the affected device */
int
time_unavail
;
/* milliseconds until device might be available */
};
...
...
@@ -47,8 +47,11 @@ struct eeh_event {
*/
int
eeh_send_failure_event
(
struct
device_node
*
dn
,
struct
pci_dev
*
dev
,
int
reset_
state
,
enum
pci_channel_state
state
,
int
time_unavail
);
/* Main recovery function */
void
handle_eeh_events
(
struct
eeh_event
*
);
#endif
/* __KERNEL__ */
#endif
/* ASM_PPC64_EEH_EVENT_H */
include/asm-powerpc/pci-bridge.h
View file @
a62e6848
...
...
@@ -61,16 +61,17 @@ struct pci_controller;
struct
iommu_table
;
struct
pci_dn
{
int
busno
;
/* for pci devices */
int
bussubno
;
/* for pci devices */
int
devfn
;
/* for pci devices */
int
busno
;
/* pci bus number */
int
bussubno
;
/* pci subordinate bus number */
int
devfn
;
/* pci device and function number */
int
class_code
;
/* pci device class */
#ifdef CONFIG_PPC_PSERIES
int
eeh_mode
;
/* See eeh.h for possible EEH_MODEs */
int
eeh_config_addr
;
int
eeh_pe_config_addr
;
/* new-style partition endpoint address */
int
eeh_check_count
;
/* # times driver ignored error */
int
eeh_freeze_count
;
/* # times this device froze up. */
int
eeh_is_bridge
;
/* device is pci-to-pci bridge */
#endif
int
pci_ext_config_space
;
/* for pci devices */
struct
pci_controller
*
phb
;
/* for pci devices */
...
...
include/asm-powerpc/ppc-pci.h
View file @
a62e6848
...
...
@@ -52,6 +52,21 @@ extern unsigned long pci_probe_only;
/* ---- EEH internal-use-only related routines ---- */
#ifdef CONFIG_EEH
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
* @severity: 1 if temporary, 2 if permanent failure.
*
* Obtains the the EEH error details from the RTAS subsystem,
* and then logs these details with the RTAS error log system.
*/
void
eeh_slot_error_detail
(
struct
pci_dn
*
pdn
,
int
severity
);
/**
* rtas_set_slot_reset -- unfreeze a frozen slot
*
...
...
@@ -59,8 +74,10 @@ extern unsigned long pci_probe_only;
* 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.
*/
void
rtas_set_slot_reset
(
struct
pci_dn
*
);
int
rtas_set_slot_reset
(
struct
pci_dn
*
);
/**
* eeh_restore_bars - Restore device configuration info.
...
...
@@ -84,6 +101,7 @@ void eeh_restore_bars(struct pci_dn *);
void
rtas_configure_bridge
(
struct
pci_dn
*
);
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
);
/**
* mark and clear slots: find "partition endpoint" PE and set or
...
...
@@ -92,6 +110,9 @@ int rtas_write_config(struct pci_dn *, int where, int size, u32 val);
void
eeh_mark_slot
(
struct
device_node
*
dn
,
int
mode_flag
);
void
eeh_clear_slot
(
struct
device_node
*
dn
,
int
mode_flag
);
/* Find the associated "Partiationable Endpoint" PE */
struct
device_node
*
find_device_pe
(
struct
device_node
*
dn
);
#endif
#endif
/* __KERNEL__ */
...
...
include/asm-powerpc/serial.h
View file @
a62e6848
...
...
@@ -15,6 +15,10 @@
/* Default baud base if not found in device-tree */
#define BASE_BAUD ( 1843200 / 16 )
#ifdef CONFIG_PPC_UDBG_16550
extern
void
find_legacy_serial_ports
(
void
);
#else
#define find_legacy_serial_ports() do { } while (0)
#endif
#endif
/* _PPC64_SERIAL_H */
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