* Re: [PATCH 24/37] KVM: PPC: booke: rework rescheduling checks
From: Scott Wood @ 2012-02-28 17:21 UTC (permalink / raw)
To: Alexander Graf; +Cc: linuxppc-dev, kvm, kvm-ppc
In-Reply-To: <8C85FFCE-616B-4A3C-BD43-08997DB1E0B1@suse.de>
On 02/28/2012 05:03 AM, Alexander Graf wrote:
>
> On 27.02.2012, at 20:28, Scott Wood wrote:
>
>> If there is a signal pending and MSR[WE] is set, we'll loop forever
>> without reaching this check.
>
> Good point. How about something like this on top (will fold in later)?
>
> diff --git a/arch/powerpc/kvm/booke.c b/arch/powerpc/kvm/booke.c
> index 430055e..9f27258 100644
> --- a/arch/powerpc/kvm/booke.c
> +++ b/arch/powerpc/kvm/booke.c
> @@ -477,15 +477,17 @@ static int kvmppc_prepare_to_enter(struct kvm_vcpu *vcpu)
> continue;
> }
>
> + if (signal_pending(current)) {
> + r = 1;
> + break;
> + }
> +
> if (kvmppc_core_prepare_to_enter(vcpu)) {
> /* interrupts got enabled in between, so we
> are back at square 1 */
> continue;
> }
>
> - if (signal_pending(current))
> - r = 1;
> -
> break;
> }
Looks OK.
-Scott
^ permalink raw reply
* Re: [PATCH] sparsemem/bootmem: catch greater than section size allocations
From: Mel Gorman @ 2012-02-28 15:47 UTC (permalink / raw)
To: Nishanth Aravamudan
Cc: Anton Blanchard, Dave Hansen, linux-mm, Paul Mackerras,
Andrew Morton, Robert Jennings, linuxppc-dev
In-Reply-To: <1330112038-18951-1-git-send-email-nacc@us.ibm.com>
On Fri, Feb 24, 2012 at 11:33:58AM -0800, Nishanth Aravamudan wrote:
> While testing AMS (Active Memory Sharing) / CMO (Cooperative Memory
> Overcommit) on powerpc, we tripped the following:
>
> kernel BUG at mm/bootmem.c:483!
> cpu 0x0: Vector: 700 (Program Check) at [c000000000c03940]
> pc: c000000000a62bd8: .alloc_bootmem_core+0x90/0x39c
> lr: c000000000a64bcc: .sparse_early_usemaps_alloc_node+0x84/0x29c
> sp: c000000000c03bc0
> msr: 8000000000021032
> current = 0xc000000000b0cce0
> paca = 0xc000000001d80000
> pid = 0, comm = swapper
> kernel BUG at mm/bootmem.c:483!
> enter ? for help
> [c000000000c03c80] c000000000a64bcc
> .sparse_early_usemaps_alloc_node+0x84/0x29c
> [c000000000c03d50] c000000000a64f10 .sparse_init+0x12c/0x28c
> [c000000000c03e20] c000000000a474f4 .setup_arch+0x20c/0x294
> [c000000000c03ee0] c000000000a4079c .start_kernel+0xb4/0x460
> [c000000000c03f90] c000000000009670 .start_here_common+0x1c/0x2c
>
> This is
>
> BUG_ON(limit && goal + size > limit);
>
> and after some debugging, it seems that
>
> goal = 0x7ffff000000
> limit = 0x80000000000
>
> and sparse_early_usemaps_alloc_node ->
> sparse_early_usemaps_alloc_pgdat_section -> alloc_bootmem_section calls
>
> return alloc_bootmem_section(usemap_size() * count, section_nr);
>
> This is on a system with 8TB available via the AMS pool, and as a quirk
> of AMS in firmware, all of that memory shows up in node 0. So, we end up
> with an allocation that will fail the goal/limit constraints. In theory,
> we could "fall-back" to alloc_bootmem_node() in
> sparse_early_usemaps_alloc_node(), but since we actually have HOTREMOVE
> defined, we'll BUG_ON() instead. A simple solution appears to be to
> disable the limit check if the size of the allocation in
> alloc_bootmem_secition exceeds the section size.
>
> Signed-off-by: Nishanth Aravamudan <nacc@us.ibm.com>
> Cc: Dave Hansen <haveblue@us.ibm.com>
> Cc: Anton Blanchard <anton@au1.ibm.com>
> Cc: Paul Mackerras <paulus@samba.org>
> Cc: Ben Herrenschmidt <benh@kernel.crashing.org>
> Cc: Robert Jennings <rcj@linux.vnet.ibm.com>
> Cc: linux-mm@kvack.org
> Cc: linuxppc-dev@lists.ozlabs.org
> ---
> include/linux/mmzone.h | 2 ++
> mm/bootmem.c | 5 ++++-
> 2 files changed, 6 insertions(+), 1 deletions(-)
>
> diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h
> index 650ba2f..4176834 100644
> --- a/include/linux/mmzone.h
> +++ b/include/linux/mmzone.h
> @@ -967,6 +967,8 @@ static inline unsigned long early_pfn_to_nid(unsigned long pfn)
> * PA_SECTION_SHIFT physical address to/from section number
> * PFN_SECTION_SHIFT pfn to/from section number
> */
> +#define BYTES_PER_SECTION (1UL << SECTION_SIZE_BITS)
> +
> #define SECTIONS_SHIFT (MAX_PHYSMEM_BITS - SECTION_SIZE_BITS)
>
> #define PA_SECTION_SHIFT (SECTION_SIZE_BITS)
> diff --git a/mm/bootmem.c b/mm/bootmem.c
> index 668e94d..5cbbc76 100644
> --- a/mm/bootmem.c
> +++ b/mm/bootmem.c
> @@ -770,7 +770,10 @@ void * __init alloc_bootmem_section(unsigned long size,
>
> pfn = section_nr_to_pfn(section_nr);
> goal = pfn << PAGE_SHIFT;
> - limit = section_nr_to_pfn(section_nr + 1) << PAGE_SHIFT;
> + if (size > BYTES_PER_SECTION)
> + limit = 0;
> + else
> + limit = section_nr_to_pfn(section_nr + 1) << PAGE_SHIFT;
As it's ok to spill the allocation over to an adjacent section, why not
just make limit==0 unconditionally. That would avoid defining
BYTES_PER_SECTION.
--
Mel Gorman
SUSE Labs
^ permalink raw reply
* Re: [PATCH] sparsemem/bootmem: catch greater than section size allocations
From: Johannes Weiner @ 2012-02-28 13:53 UTC (permalink / raw)
To: Nishanth Aravamudan
Cc: Anton Blanchard, Dave Hansen, linux-mm, Paul Mackerras,
Andrew Morton, Robert Jennings, linuxppc-dev
In-Reply-To: <1330112038-18951-1-git-send-email-nacc@us.ibm.com>
On Fri, Feb 24, 2012 at 11:33:58AM -0800, Nishanth Aravamudan wrote:
> While testing AMS (Active Memory Sharing) / CMO (Cooperative Memory
> Overcommit) on powerpc, we tripped the following:
>
> kernel BUG at mm/bootmem.c:483!
> cpu 0x0: Vector: 700 (Program Check) at [c000000000c03940]
> pc: c000000000a62bd8: .alloc_bootmem_core+0x90/0x39c
> lr: c000000000a64bcc: .sparse_early_usemaps_alloc_node+0x84/0x29c
> sp: c000000000c03bc0
> msr: 8000000000021032
> current = 0xc000000000b0cce0
> paca = 0xc000000001d80000
> pid = 0, comm = swapper
> kernel BUG at mm/bootmem.c:483!
> enter ? for help
> [c000000000c03c80] c000000000a64bcc
> .sparse_early_usemaps_alloc_node+0x84/0x29c
> [c000000000c03d50] c000000000a64f10 .sparse_init+0x12c/0x28c
> [c000000000c03e20] c000000000a474f4 .setup_arch+0x20c/0x294
> [c000000000c03ee0] c000000000a4079c .start_kernel+0xb4/0x460
> [c000000000c03f90] c000000000009670 .start_here_common+0x1c/0x2c
>
> This is
>
> BUG_ON(limit && goal + size > limit);
>
> and after some debugging, it seems that
>
> goal = 0x7ffff000000
> limit = 0x80000000000
>
> and sparse_early_usemaps_alloc_node ->
> sparse_early_usemaps_alloc_pgdat_section -> alloc_bootmem_section calls
>
> return alloc_bootmem_section(usemap_size() * count, section_nr);
>
> This is on a system with 8TB available via the AMS pool, and as a quirk
> of AMS in firmware, all of that memory shows up in node 0. So, we end up
> with an allocation that will fail the goal/limit constraints. In theory,
> we could "fall-back" to alloc_bootmem_node() in
> sparse_early_usemaps_alloc_node(), but since we actually have HOTREMOVE
> defined, we'll BUG_ON() instead. A simple solution appears to be to
> disable the limit check if the size of the allocation in
> alloc_bootmem_secition exceeds the section size.
It makes sense to allow the usemaps to spill over to subsequent
sections instead of panicking, so FWIW:
Acked-by: Johannes Weiner <hannes@cmpxchg.org>
That being said, it would be good if check_usemap_section_nr() printed
the cross-dependencies between pgdats and sections when the usemaps of
a node spilled over to other sections than the ones holding the pgdat.
How about this?
---
From: Johannes Weiner <hannes@cmpxchg.org>
Subject: sparsemem/bootmem: catch greater than section size allocations fix
If alloc_bootmem_section() no longer guarantees section-locality, we
need check_usemap_section_nr() to print possible cross-dependencies
between node descriptors and the usemaps allocated through it.
Signed-off-by: Johannes Weiner <hannes@cmpxchg.org>
---
diff --git a/mm/sparse.c b/mm/sparse.c
index 61d7cde..9e032dc 100644
--- a/mm/sparse.c
+++ b/mm/sparse.c
@@ -359,6 +359,7 @@ static void __init sparse_early_usemaps_alloc_node(unsigned long**usemap_map,
continue;
usemap_map[pnum] = usemap;
usemap += size;
+ check_usemap_section_nr(nodeid, usemap_map[pnum]);
}
return;
}
---
Furthermore, I wonder if we can remove the sparse-specific stuff from
bootmem.c as well, as now even more so than before, calculating the
desired area is really none of bootmem's business.
Would something like this be okay?
---
From: Johannes Weiner <hannes@cmpxchg.org>
Subject: [patch] mm: remove sparsemem allocation details from the bootmem allocator
alloc_bootmem_section() derives allocation area constraints from the
specified sparsemem section. This is a bit specific for a generic
memory allocator like bootmem, though, so move it over to sparsemem.
Since __alloc_bootmem_node() already retries failed allocations with
relaxed area constraints, the fallback code in sparsemem.c can be
removed and the code becomes a bit more compact overall.
Signed-off-by: Johannes Weiner <hannes@cmpxchg.org>
---
include/linux/bootmem.h | 3 ---
mm/bootmem.c | 26 --------------------------
mm/sparse.c | 29 +++++++++--------------------
3 files changed, 9 insertions(+), 49 deletions(-)
diff --git a/include/linux/bootmem.h b/include/linux/bootmem.h
index ab344a5..001c248 100644
--- a/include/linux/bootmem.h
+++ b/include/linux/bootmem.h
@@ -135,9 +135,6 @@ extern void *__alloc_bootmem_low_node(pg_data_t *pgdat,
extern int reserve_bootmem_generic(unsigned long addr, unsigned long size,
int flags);
-extern void *alloc_bootmem_section(unsigned long size,
- unsigned long section_nr);
-
#ifdef CONFIG_HAVE_ARCH_ALLOC_REMAP
extern void *alloc_remap(int nid, unsigned long size);
#else
diff --git a/mm/bootmem.c b/mm/bootmem.c
index 7bc0557..d34026c 100644
--- a/mm/bootmem.c
+++ b/mm/bootmem.c
@@ -756,32 +756,6 @@ void * __init __alloc_bootmem_node_high(pg_data_t *pgdat, unsigned long size,
}
-#ifdef CONFIG_SPARSEMEM
-/**
- * alloc_bootmem_section - allocate boot memory from a specific section
- * @size: size of the request in bytes
- * @section_nr: sparse map section to allocate from
- *
- * Return NULL on failure.
- */
-void * __init alloc_bootmem_section(unsigned long size,
- unsigned long section_nr)
-{
- bootmem_data_t *bdata;
- unsigned long pfn, goal, limit;
-
- pfn = section_nr_to_pfn(section_nr);
- goal = pfn << PAGE_SHIFT;
- if (size > BYTES_PER_SECTION)
- limit = 0;
- else
- limit = section_nr_to_pfn(section_nr + 1) << PAGE_SHIFT;
- bdata = &bootmem_node_data[early_pfn_to_nid(pfn)];
-
- return alloc_bootmem_core(bdata, size, SMP_CACHE_BYTES, goal, limit);
-}
-#endif
-
void * __init __alloc_bootmem_node_nopanic(pg_data_t *pgdat, unsigned long size,
unsigned long align, unsigned long goal)
{
diff --git a/mm/sparse.c b/mm/sparse.c
index 9e032dc..ac0d5a3 100644
--- a/mm/sparse.c
+++ b/mm/sparse.c
@@ -273,10 +273,10 @@ static unsigned long *__kmalloc_section_usemap(void)
#ifdef CONFIG_MEMORY_HOTREMOVE
static unsigned long * __init
sparse_early_usemaps_alloc_pgdat_section(struct pglist_data *pgdat,
- unsigned long count)
+ unsigned long size)
{
- unsigned long section_nr;
-
+ pg_data_t *host_pgdat;
+ unsigned long goal;
/*
* A page may contain usemaps for other sections preventing the
* page being freed and making a section unremovable while
@@ -287,8 +287,9 @@ sparse_early_usemaps_alloc_pgdat_section(struct pglist_data *pgdat,
* from the same section as the pgdat where possible to avoid
* this problem.
*/
- section_nr = pfn_to_section_nr(__pa(pgdat) >> PAGE_SHIFT);
- return alloc_bootmem_section(usemap_size() * count, section_nr);
+ goal = __pa(pgdat) & PAGE_SECTION_MASK;
+ host_pgdat = NODE_DATA(early_pfn_to_nid(goal));
+ return __alloc_bootmem_node(host_pgdat, size, SMP_CACHE_BYTES, goal);
}
static void __init check_usemap_section_nr(int nid, unsigned long *usemap)
@@ -332,9 +333,9 @@ static void __init check_usemap_section_nr(int nid, unsigned long *usemap)
#else
static unsigned long * __init
sparse_early_usemaps_alloc_pgdat_section(struct pglist_data *pgdat,
- unsigned long count)
+ unsigned long size)
{
- return NULL;
+ return alloc_bootmem_node(pgdat, size);
}
static void __init check_usemap_section_nr(int nid, unsigned long *usemap)
@@ -352,19 +353,7 @@ static void __init sparse_early_usemaps_alloc_node(unsigned long**usemap_map,
int size = usemap_size();
usemap = sparse_early_usemaps_alloc_pgdat_section(NODE_DATA(nodeid),
- usemap_count);
- if (usemap) {
- for (pnum = pnum_begin; pnum < pnum_end; pnum++) {
- if (!present_section_nr(pnum))
- continue;
- usemap_map[pnum] = usemap;
- usemap += size;
- check_usemap_section_nr(nodeid, usemap_map[pnum]);
- }
- return;
- }
-
- usemap = alloc_bootmem_node(NODE_DATA(nodeid), size * usemap_count);
+ size * usemap_count);
if (usemap) {
for (pnum = pnum_begin; pnum < pnum_end; pnum++) {
if (!present_section_nr(pnum))
--
1.7.7.6
^ permalink raw reply related
* Re: [PATCH 24/37] KVM: PPC: booke: rework rescheduling checks
From: Alexander Graf @ 2012-02-28 11:03 UTC (permalink / raw)
To: Scott Wood; +Cc: linuxppc-dev, kvm, kvm-ppc
In-Reply-To: <4F4BD95B.9010809@freescale.com>
On 27.02.2012, at 20:28, Scott Wood wrote:
> On 02/24/2012 08:26 AM, Alexander Graf wrote:
>> -void kvmppc_core_prepare_to_enter(struct kvm_vcpu *vcpu)
>> +int kvmppc_core_prepare_to_enter(struct kvm_vcpu *vcpu)
>> {
>> unsigned long *pending =3D &vcpu->arch.pending_exceptions;
>> unsigned long old_pending =3D vcpu->arch.pending_exceptions;
>> @@ -283,6 +283,8 @@ void kvmppc_core_prepare_to_enter(struct kvm_vcpu =
*vcpu)
>>=20
>> /* Tell the guest about our interrupt status */
>> kvmppc_update_int_pending(vcpu, *pending, old_pending);
>> +
>> + return 0;
>> }
>>=20
>> pfn_t kvmppc_gfn_to_pfn(struct kvm_vcpu *vcpu, gfn_t gfn)
>> diff --git a/arch/powerpc/kvm/booke.c b/arch/powerpc/kvm/booke.c
>> index 9979be1..3fcec2c 100644
>> --- a/arch/powerpc/kvm/booke.c
>> +++ b/arch/powerpc/kvm/booke.c
>> @@ -439,8 +439,9 @@ static void kvmppc_core_check_exceptions(struct =
kvm_vcpu *vcpu)
>> }
>>=20
>> /* Check pending exceptions and deliver one, if possible. */
>> -void kvmppc_core_prepare_to_enter(struct kvm_vcpu *vcpu)
>> +int kvmppc_core_prepare_to_enter(struct kvm_vcpu *vcpu)
>> {
>> + int r =3D 0;
>> WARN_ON_ONCE(!irqs_disabled());
>>=20
>> kvmppc_core_check_exceptions(vcpu);
>> @@ -451,8 +452,44 @@ void kvmppc_core_prepare_to_enter(struct =
kvm_vcpu *vcpu)
>> local_irq_disable();
>>=20
>> kvmppc_set_exit_type(vcpu, EMULATED_MTMSRWE_EXITS);
>> - kvmppc_core_check_exceptions(vcpu);
>> + r =3D 1;
>> };
>> +
>> + return r;
>> +}
>> +
>> +/*
>> + * Common checks before entering the guest world. Call with =
interrupts
>> + * disabled.
>> + *
>> + * returns !0 if a signal is pending and check_signal is true
>> + */
>> +static int kvmppc_prepare_to_enter(struct kvm_vcpu *vcpu, bool =
check_signal)
>> +{
>> + int r =3D 0;
>> +
>> + WARN_ON_ONCE(!irqs_disabled());
>> + while (true) {
>> + if (need_resched()) {
>> + local_irq_enable();
>> + cond_resched();
>> + local_irq_disable();
>> + continue;
>> + }
>> +
>> + if (kvmppc_core_prepare_to_enter(vcpu)) {
>> + /* interrupts got enabled in between, so we
>> + are back at square 1 */
>> + continue;
>> + }
>> +
>> +
>> + if (check_signal && signal_pending(current))
>> + r =3D 1;
>=20
> If there is a signal pending and MSR[WE] is set, we'll loop forever
> without reaching this check.
Good point. How about something like this on top (will fold in later)?
diff --git a/arch/powerpc/kvm/booke.c b/arch/powerpc/kvm/booke.c
index 430055e..9f27258 100644
--- a/arch/powerpc/kvm/booke.c
+++ b/arch/powerpc/kvm/booke.c
@@ -477,15 +477,17 @@ static int kvmppc_prepare_to_enter(struct kvm_vcpu =
*vcpu)
continue;
}
=20
+ if (signal_pending(current)) {
+ r =3D 1;
+ break;
+ }
+
if (kvmppc_core_prepare_to_enter(vcpu)) {
/* interrupts got enabled in between, so we
are back at square 1 */
continue;
}
=20
- if (signal_pending(current))
- r =3D 1;
-
break;
}
=20
Alex
^ permalink raw reply related
* [PATCH V2 2/2] powerpc: Board support for GE IMP3A
From: Martyn Welch @ 2012-02-28 10:22 UTC (permalink / raw)
To: linuxppc-dev; +Cc: Martyn Welch, Wim Van Sebroeck, linux-kernel
In-Reply-To: <1330424541-5299-1-git-send-email-martyn.welch@ge.com>
Initial board support for the GE IMP3A, a 3U compactPCI card with a p2020
processor.
Signed-off-by: Martyn Welch <martyn.welch@ge.com>
---
v2: Rebase patch onto powerpc/next, taking work by Kyle Moffett into
account.
arch/powerpc/boot/dts/ge_imp3a.dts | 254 ++++++++++++++++++++++++++++++
arch/powerpc/configs/ge_imp3a_defconfig | 256 +++++++++++++++++++++++++++++++
arch/powerpc/platforms/85xx/Kconfig | 15 ++
arch/powerpc/platforms/85xx/Makefile | 1 +
arch/powerpc/platforms/85xx/ge_imp3a.c | 246 +++++++++++++++++++++++++++++
arch/powerpc/sysdev/ge/ge_gpio.c | 28 ++++
6 files changed, 800 insertions(+), 0 deletions(-)
create mode 100644 arch/powerpc/boot/dts/ge_imp3a.dts
create mode 100644 arch/powerpc/configs/ge_imp3a_defconfig
create mode 100644 arch/powerpc/platforms/85xx/ge_imp3a.c
diff --git a/arch/powerpc/boot/dts/ge_imp3a.dts b/arch/powerpc/boot/dts/ge_imp3a.dts
new file mode 100644
index 0000000..f30fadb
--- /dev/null
+++ b/arch/powerpc/boot/dts/ge_imp3a.dts
@@ -0,0 +1,254 @@
+/*
+ * GE IMP3A Device Tree Source
+ *
+ * Copyright 2010-2011 GE Intelligent Platforms Embedded Systems, Inc.
+ *
+ * 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.
+ *
+ * Based on: P2020 DS Device Tree Source
+ * Copyright 2009 Freescale Semiconductor Inc.
+ */
+
+/include/ "fsl/p2020si-pre.dtsi"
+
+/ {
+ model = "GE_IMP3A";
+ compatible = "ge,imp3a";
+
+ memory {
+ device_type = "memory";
+ };
+
+ lbc: localbus@fef05000 {
+ reg = <0 0xfef05000 0 0x1000>;
+
+ ranges = <0x0 0x0 0x0 0xff000000 0x01000000
+ 0x1 0x0 0x0 0xe0000000 0x08000000
+ 0x2 0x0 0x0 0xe8000000 0x08000000
+ 0x3 0x0 0x0 0xfc100000 0x00020000
+ 0x4 0x0 0x0 0xfc000000 0x00008000
+ 0x5 0x0 0x0 0xfc008000 0x00008000
+ 0x6 0x0 0x0 0xfee00000 0x00040000
+ 0x7 0x0 0x0 0xfee80000 0x00040000>;
+
+ /* nor@0,0 is a mirror of part of the memory in nor@1,0
+ nor@0,0 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "ge,imp3a-firmware-mirror", "cfi-flash";
+ reg = <0x0 0x0 0x1000000>;
+ bank-width = <2>;
+ device-width = <1>;
+
+ partition@0 {
+ label = "firmware";
+ reg = <0x0 0x1000000>;
+ read-only;
+ };
+ };
+ */
+
+ nor@1,0 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "ge,imp3a-paged-flash", "cfi-flash";
+ reg = <0x1 0x0 0x8000000>;
+ bank-width = <2>;
+ device-width = <1>;
+
+ partition@0 {
+ label = "user";
+ reg = <0x0 0x7800000>;
+ };
+
+ partition@7800000 {
+ label = "firmware";
+ reg = <0x7800000 0x800000>;
+ read-only;
+ };
+ };
+
+ nvram@3,0 {
+ device_type = "nvram";
+ compatible = "simtek,stk14ca8";
+ reg = <0x3 0x0 0x20000>;
+ };
+
+ fpga@4,0 {
+ compatible = "ge,imp3a-fpga-regs";
+ reg = <0x4 0x0 0x20>;
+ };
+
+ gef_pic: pic@4,20 {
+ #interrupt-cells = <1>;
+ interrupt-controller;
+ compatible = "ge,imp3a-fpga-pic", "gef,fpga-pic-1.00";
+ reg = <0x4 0x20 0x20>;
+ interrupts = <6 7 0 0>;
+ };
+
+ gef_gpio: gpio@4,400 {
+ #gpio-cells = <2>;
+ compatible = "ge,imp3a-gpio";
+ reg = <0x4 0x400 0x24>;
+ gpio-controller;
+ };
+
+ wdt@4,800 {
+ compatible = "ge,imp3a-fpga-wdt", "gef,fpga-wdt-1.00",
+ "gef,fpga-wdt";
+ reg = <0x4 0x800 0x8>;
+ interrupts = <10 4>;
+ interrupt-parent = <&gef_pic>;
+ };
+
+ /* Second watchdog available, driver currently supports one.
+ wdt@4,808 {
+ compatible = "gef,imp3a-fpga-wdt", "gef,fpga-wdt-1.00",
+ "gef,fpga-wdt";
+ reg = <0x4 0x808 0x8>;
+ interrupts = <9 4>;
+ interrupt-parent = <&gef_pic>;
+ };
+ */
+
+ nand@6,0 {
+ compatible = "fsl,elbc-fcm-nand";
+ reg = <0x6 0x0 0x40000>;
+ };
+
+ nand@7,0 {
+ compatible = "fsl,elbc-fcm-nand";
+ reg = <0x7 0x0 0x40000>;
+ };
+ };
+
+ soc: soc@fef00000 {
+ ranges = <0x0 0 0xfef00000 0x100000>;
+
+ i2c@3000 {
+ hwmon@48 {
+ compatible = "national,lm92";
+ reg = <0x48>;
+ };
+
+ hwmon@4c {
+ compatible = "adi,adt7461";
+ reg = <0x4c>;
+ };
+
+ rtc@51 {
+ compatible = "epson,rx8581";
+ reg = <0x51>;
+ };
+
+ eti@6b {
+ compatible = "dallas,ds1682";
+ reg = <0x6b>;
+ };
+ };
+
+ usb@22000 {
+ phy_type = "ulpi";
+ dr_mode = "host";
+ };
+
+ mdio@24520 {
+ phy0: ethernet-phy@0 {
+ interrupt-parent = <&gef_pic>;
+ interrupts = <0xc 0x4>;
+ reg = <0x1>;
+ };
+ phy1: ethernet-phy@1 {
+ interrupt-parent = <&gef_pic>;
+ interrupts = <0xb 0x4>;
+ reg = <0x2>;
+ };
+ tbi0: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
+ };
+
+ mdio@25520 {
+ tbi1: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
+ };
+
+ mdio@26520 {
+ status = "disabled";
+ };
+
+ enet0: ethernet@24000 {
+ tbi-handle = <&tbi0>;
+ phy-handle = <&phy0>;
+ phy-connection-type = "gmii";
+ };
+
+ enet1: ethernet@25000 {
+ tbi-handle = <&tbi1>;
+ phy-handle = <&phy1>;
+ phy-connection-type = "gmii";
+ };
+
+ enet2: ethernet@26000 {
+ status = "disabled";
+ };
+ };
+
+ pci0: pcie@fef08000 {
+ ranges = <0x2000000 0x0 0xc0000000 0 0xc0000000 0x0 0x20000000
+ 0x1000000 0x0 0x00000000 0 0xfe020000 0x0 0x10000>;
+ reg = <0 0xfef08000 0 0x1000>;
+
+ pcie@0 {
+ ranges = <0x2000000 0x0 0xc0000000
+ 0x2000000 0x0 0xc0000000
+ 0x0 0x20000000
+
+ 0x1000000 0x0 0x0
+ 0x1000000 0x0 0x0
+ 0x0 0x10000>;
+ };
+ };
+
+ pci1: pcie@fef09000 {
+ reg = <0 0xfef09000 0 0x1000>;
+ ranges = <0x2000000 0x0 0xa0000000 0 0xa0000000 0x0 0x20000000
+ 0x1000000 0x0 0x00000000 0 0xfe010000 0x0 0x10000>;
+
+ pcie@0 {
+ ranges = <0x2000000 0x0 0xa0000000
+ 0x2000000 0x0 0xa0000000
+ 0x0 0x20000000
+
+ 0x1000000 0x0 0x0
+ 0x1000000 0x0 0x0
+ 0x0 0x10000>;
+ };
+
+ };
+
+ pci2: pcie@fef0a000 {
+ reg = <0 0xfef0a000 0 0x1000>;
+ ranges = <0x2000000 0x0 0x80000000 0 0x80000000 0x0 0x20000000
+ 0x1000000 0x0 0x00000000 0 0xfe000000 0x0 0x10000>;
+
+ pcie@0 {
+ ranges = <0x2000000 0x0 0x80000000
+ 0x2000000 0x0 0x80000000
+ 0x0 0x20000000
+
+ 0x1000000 0x0 0x0
+ 0x1000000 0x0 0x0
+ 0x0 0x10000>;
+ };
+ };
+};
+
+/include/ "fsl/p2020si-post.dtsi"
diff --git a/arch/powerpc/configs/ge_imp3a_defconfig b/arch/powerpc/configs/ge_imp3a_defconfig
new file mode 100644
index 0000000..ca4c5cb
--- /dev/null
+++ b/arch/powerpc/configs/ge_imp3a_defconfig
@@ -0,0 +1,256 @@
+CONFIG_PPC_85xx=y
+CONFIG_SMP=y
+CONFIG_NR_CPUS=2
+CONFIG_EXPERIMENTAL=y
+CONFIG_SYSVIPC=y
+CONFIG_POSIX_MQUEUE=y
+CONFIG_BSD_PROCESS_ACCT=y
+CONFIG_BSD_PROCESS_ACCT_V3=y
+CONFIG_SPARSE_IRQ=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+# CONFIG_UTS_NS is not set
+# CONFIG_IPC_NS is not set
+# CONFIG_USER_NS is not set
+# CONFIG_PID_NS is not set
+# CONFIG_NET_NS is not set
+CONFIG_SYSFS_DEPRECATED=y
+CONFIG_SYSFS_DEPRECATED_V2=y
+CONFIG_RELAY=y
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_PERF_EVENTS=y
+CONFIG_SLAB=y
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+# CONFIG_BLK_DEV_BSG is not set
+CONFIG_GE_IMP3A=y
+CONFIG_QUICC_ENGINE=y
+CONFIG_QE_GPIO=y
+CONFIG_CPM2=y
+CONFIG_HIGHMEM=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_HZ_1000=y
+CONFIG_PREEMPT=y
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+CONFIG_BINFMT_MISC=m
+CONFIG_MATH_EMULATION=y
+CONFIG_IRQ_ALL_CPUS=y
+CONFIG_FORCE_MAX_ZONEORDER=17
+CONFIG_PCI=y
+CONFIG_PCIEPORTBUS=y
+CONFIG_PCI_MSI=y
+CONFIG_PCCARD=y
+# CONFIG_PCMCIA_LOAD_CIS is not set
+CONFIG_YENTA=y
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_XFRM_USER=m
+CONFIG_NET_KEY=y
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_ADVANCED_ROUTER=y
+CONFIG_IP_MULTIPLE_TABLES=y
+CONFIG_IP_ROUTE_MULTIPATH=y
+CONFIG_IP_ROUTE_VERBOSE=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+CONFIG_IP_PNP_RARP=y
+CONFIG_NET_IPIP=m
+CONFIG_IP_MROUTE=y
+CONFIG_IP_PIMSM_V1=y
+CONFIG_IP_PIMSM_V2=y
+CONFIG_SYN_COOKIES=y
+CONFIG_INET_AH=m
+CONFIG_INET_ESP=m
+CONFIG_INET_IPCOMP=m
+# CONFIG_INET_XFRM_MODE_BEET is not set
+CONFIG_INET6_AH=m
+CONFIG_INET6_IPCOMP=m
+CONFIG_IPV6_TUNNEL=m
+CONFIG_NET_PKTGEN=m
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_MTD=y
+CONFIG_MTD_OF_PARTS=y
+CONFIG_MTD_CHAR=y
+CONFIG_MTD_BLOCK=y
+CONFIG_MTD_CFI=y
+CONFIG_MTD_JEDECPROBE=y
+CONFIG_MTD_CFI_INTELEXT=y
+CONFIG_MTD_CFI_AMDSTD=y
+CONFIG_MTD_PHYSMAP_OF=y
+CONFIG_MTD_NAND=y
+CONFIG_MTD_NAND_FSL_ELBC=y
+CONFIG_PROC_DEVICETREE=y
+CONFIG_BLK_DEV_LOOP=m
+CONFIG_BLK_DEV_CRYPTOLOOP=m
+CONFIG_BLK_DEV_NBD=m
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_SIZE=131072
+CONFIG_MISC_DEVICES=y
+CONFIG_DS1682=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_CHR_DEV_ST=y
+CONFIG_BLK_DEV_SR=y
+CONFIG_ATA=y
+CONFIG_SATA_AHCI=y
+CONFIG_SATA_SIL24=y
+# CONFIG_ATA_SFF is not set
+CONFIG_NETDEVICES=y
+CONFIG_BONDING=m
+CONFIG_DUMMY=m
+CONFIG_NETCONSOLE=y
+CONFIG_NETPOLL_TRAP=y
+CONFIG_TUN=m
+# CONFIG_NET_VENDOR_3COM is not set
+CONFIG_FS_ENET=y
+CONFIG_UCC_GETH=y
+CONFIG_GIANFAR=y
+CONFIG_PPP=m
+CONFIG_PPP_BSDCOMP=m
+CONFIG_PPP_DEFLATE=m
+CONFIG_PPP_FILTER=y
+CONFIG_PPP_MULTILINK=y
+CONFIG_PPPOE=m
+CONFIG_PPP_ASYNC=m
+CONFIG_PPP_SYNC_TTY=m
+CONFIG_SLIP=m
+CONFIG_SLIP_COMPRESSED=y
+CONFIG_SLIP_SMART=y
+CONFIG_SLIP_MODE_SLIP6=y
+# CONFIG_INPUT_KEYBOARD is not set
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_SERIO is not set
+# CONFIG_LEGACY_PTYS is not set
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_SERIAL_8250_NR_UARTS=2
+CONFIG_SERIAL_8250_RUNTIME_UARTS=2
+CONFIG_SERIAL_8250_EXTENDED=y
+CONFIG_SERIAL_8250_MANY_PORTS=y
+CONFIG_SERIAL_8250_DETECT_IRQ=y
+CONFIG_SERIAL_8250_RSA=y
+CONFIG_SERIAL_QE=m
+CONFIG_NVRAM=y
+CONFIG_I2C=y
+CONFIG_I2C_CHARDEV=y
+CONFIG_I2C_CPM=m
+CONFIG_I2C_MPC=y
+CONFIG_GPIO_SYSFS=y
+CONFIG_SENSORS_LM90=y
+CONFIG_SENSORS_LM92=y
+CONFIG_WATCHDOG=y
+CONFIG_GEF_WDT=y
+CONFIG_VIDEO_OUTPUT_CONTROL=m
+CONFIG_HID_DRAGONRISE=y
+CONFIG_HID_GYRATION=y
+CONFIG_HID_TWINHAN=y
+CONFIG_HID_ORTEK=y
+CONFIG_HID_PANTHERLORD=y
+CONFIG_HID_PETALYNX=y
+CONFIG_HID_SAMSUNG=y
+CONFIG_HID_SONY=y
+CONFIG_HID_SUNPLUS=y
+CONFIG_HID_GREENASIA=y
+CONFIG_HID_SMARTJOYPLUS=y
+CONFIG_HID_TOPSEED=y
+CONFIG_HID_THRUSTMASTER=y
+CONFIG_HID_ZEROPLUS=y
+CONFIG_USB=y
+CONFIG_USB_DEVICEFS=y
+CONFIG_USB_EHCI_HCD=y
+# CONFIG_USB_EHCI_TT_NEWSCHED is not set
+CONFIG_USB_EHCI_FSL=y
+CONFIG_USB_OHCI_HCD=y
+CONFIG_USB_OHCI_HCD_PPC_OF_BE=y
+CONFIG_USB_OHCI_HCD_PPC_OF_LE=y
+CONFIG_USB_STORAGE=y
+CONFIG_EDAC=y
+CONFIG_EDAC_MM_EDAC=y
+CONFIG_EDAC_MPC85XX=y
+CONFIG_RTC_CLASS=y
+# CONFIG_RTC_INTF_PROC is not set
+CONFIG_RTC_DRV_RX8581=y
+CONFIG_DMADEVICES=y
+CONFIG_FSL_DMA=y
+# CONFIG_NET_DMA is not set
+CONFIG_EXT2_FS=y
+CONFIG_EXT2_FS_XATTR=y
+CONFIG_EXT2_FS_POSIX_ACL=y
+CONFIG_EXT3_FS=y
+# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
+CONFIG_EXT3_FS_POSIX_ACL=y
+CONFIG_EXT4_FS=y
+CONFIG_FUSE_FS=y
+CONFIG_ISO9660_FS=y
+CONFIG_JOLIET=y
+CONFIG_ZISOFS=y
+CONFIG_UDF_FS=y
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_FAT_DEFAULT_CODEPAGE=850
+CONFIG_FAT_DEFAULT_IOCHARSET="ascii"
+CONFIG_NTFS_FS=y
+CONFIG_PROC_KCORE=y
+CONFIG_TMPFS=y
+CONFIG_JFFS2_FS=y
+CONFIG_NFS_FS=y
+CONFIG_NFS_V3=y
+CONFIG_NFS_V4=y
+CONFIG_ROOT_NFS=y
+CONFIG_NFSD=y
+CONFIG_NFSD_V4=y
+CONFIG_CIFS=m
+CONFIG_CIFS_XATTR=y
+CONFIG_CIFS_POSIX=y
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_CODEPAGE_737=m
+CONFIG_NLS_CODEPAGE_775=m
+CONFIG_NLS_CODEPAGE_850=y
+CONFIG_NLS_CODEPAGE_852=m
+CONFIG_NLS_CODEPAGE_855=m
+CONFIG_NLS_CODEPAGE_857=m
+CONFIG_NLS_CODEPAGE_860=m
+CONFIG_NLS_CODEPAGE_861=m
+CONFIG_NLS_CODEPAGE_862=m
+CONFIG_NLS_CODEPAGE_863=m
+CONFIG_NLS_CODEPAGE_864=m
+CONFIG_NLS_CODEPAGE_865=m
+CONFIG_NLS_CODEPAGE_866=m
+CONFIG_NLS_CODEPAGE_869=m
+CONFIG_NLS_CODEPAGE_936=m
+CONFIG_NLS_CODEPAGE_950=m
+CONFIG_NLS_CODEPAGE_932=m
+CONFIG_NLS_CODEPAGE_949=m
+CONFIG_NLS_CODEPAGE_874=m
+CONFIG_NLS_ISO8859_8=m
+CONFIG_NLS_CODEPAGE_1250=m
+CONFIG_NLS_CODEPAGE_1251=m
+CONFIG_NLS_ASCII=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_NLS_ISO8859_2=m
+CONFIG_NLS_ISO8859_3=m
+CONFIG_NLS_ISO8859_4=m
+CONFIG_NLS_ISO8859_5=m
+CONFIG_NLS_ISO8859_6=m
+CONFIG_NLS_ISO8859_7=m
+CONFIG_NLS_ISO8859_9=m
+CONFIG_NLS_ISO8859_13=m
+CONFIG_NLS_ISO8859_14=m
+CONFIG_NLS_ISO8859_15=y
+CONFIG_NLS_KOI8_R=m
+CONFIG_NLS_KOI8_U=m
+CONFIG_NLS_UTF8=y
+CONFIG_CRC_CCITT=y
+CONFIG_CRC_T10DIF=y
+CONFIG_LIBCRC32C=y
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_SYSCTL_SYSCALL_CHECK=y
+CONFIG_CRYPTO_CBC=y
+CONFIG_CRYPTO_MD5=y
+CONFIG_CRYPTO_SHA256=m
+CONFIG_CRYPTO_SHA512=m
+CONFIG_CRYPTO_DES=y
+# CONFIG_CRYPTO_ANSI_CPRNG is not set
+CONFIG_CRYPTO_DEV_TALITOS=y
diff --git a/arch/powerpc/platforms/85xx/Kconfig b/arch/powerpc/platforms/85xx/Kconfig
index d7946be..db29803 100644
--- a/arch/powerpc/platforms/85xx/Kconfig
+++ b/arch/powerpc/platforms/85xx/Kconfig
@@ -171,6 +171,21 @@ config SBC8560
help
This option enables support for the Wind River SBC8560 board
+config GE_IMP3A
+ bool "GE Intelligent Platforms IMP3A"
+ select DEFAULT_UIMAGE
+ select SWIOTLB
+ select MMIO_NVRAM
+ select GENERIC_GPIO
+ select ARCH_REQUIRE_GPIOLIB
+ select GE_FPGA
+ help
+ This option enables support for the GE Intelligent Platforms IMP3A
+ board.
+
+ This board is a 3U CompactPCI Single Board Computer with a Freescale
+ P2020 processor.
+
config P2041_RDB
bool "Freescale P2041 RDB"
select DEFAULT_UIMAGE
diff --git a/arch/powerpc/platforms/85xx/Makefile b/arch/powerpc/platforms/85xx/Makefile
index 9cb2d43..2125d4c 100644
--- a/arch/powerpc/platforms/85xx/Makefile
+++ b/arch/powerpc/platforms/85xx/Makefile
@@ -27,3 +27,4 @@ obj-$(CONFIG_SBC8548) += sbc8548.o
obj-$(CONFIG_SOCRATES) += socrates.o socrates_fpga_pic.o
obj-$(CONFIG_KSI8560) += ksi8560.o
obj-$(CONFIG_XES_MPC85xx) += xes_mpc85xx.o
+obj-$(CONFIG_GE_IMP3A) += ge_imp3a.o
diff --git a/arch/powerpc/platforms/85xx/ge_imp3a.c b/arch/powerpc/platforms/85xx/ge_imp3a.c
new file mode 100644
index 0000000..b1ab759
--- /dev/null
+++ b/arch/powerpc/platforms/85xx/ge_imp3a.c
@@ -0,0 +1,246 @@
+/*
+ * GE IMP3A Board Setup
+ *
+ * Author Martyn Welch <martyn.welch@ge.com>
+ *
+ * Copyright 2010 GE Intelligent Platforms Embedded Systems, Inc.
+ *
+ * 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.
+ *
+ * Based on: mpc85xx_ds.c (MPC85xx DS Board Setup)
+ * Copyright 2007 Freescale Semiconductor Inc.
+ */
+
+#include <linux/stddef.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/kdev_t.h>
+#include <linux/delay.h>
+#include <linux/seq_file.h>
+#include <linux/interrupt.h>
+#include <linux/of_platform.h>
+#include <linux/memblock.h>
+
+#include <asm/system.h>
+#include <asm/time.h>
+#include <asm/machdep.h>
+#include <asm/pci-bridge.h>
+#include <mm/mmu_decl.h>
+#include <asm/prom.h>
+#include <asm/udbg.h>
+#include <asm/mpic.h>
+#include <asm/swiotlb.h>
+#include <asm/nvram.h>
+
+#include <sysdev/fsl_soc.h>
+#include <sysdev/fsl_pci.h>
+#include "smp.h"
+
+#include "mpc85xx.h"
+#include <sysdev/ge/ge_pic.h>
+
+void __iomem *imp3a_regs;
+
+void __init ge_imp3a_pic_init(void)
+{
+ struct mpic *mpic;
+ struct device_node *np;
+ struct device_node *cascade_node = NULL;
+ unsigned long root = of_get_flat_dt_root();
+
+ if (of_flat_dt_is_compatible(root, "fsl,MPC8572DS-CAMP")) {
+ mpic = mpic_alloc(NULL, 0,
+ MPIC_NO_RESET |
+ MPIC_BIG_ENDIAN |
+ MPIC_SINGLE_DEST_CPU,
+ 0, 256, " OpenPIC ");
+ } else {
+ mpic = mpic_alloc(NULL, 0,
+ MPIC_BIG_ENDIAN |
+ MPIC_SINGLE_DEST_CPU,
+ 0, 256, " OpenPIC ");
+ }
+
+ BUG_ON(mpic == NULL);
+ mpic_init(mpic);
+ /*
+ * There is a simple interrupt handler in the main FPGA, this needs
+ * to be cascaded into the MPIC
+ */
+ for_each_node_by_type(np, "interrupt-controller")
+ if (of_device_is_compatible(np, "gef,fpga-pic-1.00")) {
+ cascade_node = np;
+ break;
+ }
+
+ if (cascade_node == NULL) {
+ printk(KERN_WARNING "IMP3A: No FPGA PIC\n");
+ return;
+ }
+
+ gef_pic_init(cascade_node);
+ of_node_put(cascade_node);
+}
+
+#ifdef CONFIG_PCI
+static int primary_phb_addr;
+#endif /* CONFIG_PCI */
+
+/*
+ * Setup the architecture
+ */
+static void __init ge_imp3a_setup_arch(void)
+{
+ struct device_node *regs;
+#ifdef CONFIG_PCI
+ struct device_node *np;
+ struct pci_controller *hose;
+#endif
+ dma_addr_t max = 0xffffffff;
+
+ if (ppc_md.progress)
+ ppc_md.progress("ge_imp3a_setup_arch()", 0);
+
+#ifdef CONFIG_PCI
+ for_each_node_by_type(np, "pci") {
+ if (of_device_is_compatible(np, "fsl,mpc8540-pci") ||
+ of_device_is_compatible(np, "fsl,mpc8548-pcie") ||
+ of_device_is_compatible(np, "fsl,p2020-pcie")) {
+ struct resource rsrc;
+ of_address_to_resource(np, 0, &rsrc);
+ if ((rsrc.start & 0xfffff) == primary_phb_addr)
+ fsl_add_bridge(np, 1);
+ else
+ fsl_add_bridge(np, 0);
+
+ hose = pci_find_hose_for_OF_device(np);
+ max = min(max, hose->dma_window_base_cur +
+ hose->dma_window_size);
+ }
+ }
+#endif
+
+ mpc85xx_smp_init();
+
+#ifdef CONFIG_SWIOTLB
+ if (memblock_end_of_DRAM() > max) {
+ ppc_swiotlb_enable = 1;
+ set_pci_dma_ops(&swiotlb_dma_ops);
+ ppc_md.pci_dma_dev_setup = pci_dma_dev_setup_swiotlb;
+ }
+#endif
+
+ /* Remap basic board registers */
+ regs = of_find_compatible_node(NULL, NULL, "ge,imp3a-fpga-regs");
+ if (regs) {
+ imp3a_regs = of_iomap(regs, 0);
+ if (imp3a_regs == NULL)
+ printk(KERN_WARNING "Unable to map board registers\n");
+ of_node_put(regs);
+ }
+
+#if defined(CONFIG_MMIO_NVRAM)
+ mmio_nvram_init();
+#endif
+
+ printk(KERN_INFO "GE Intelligent Platforms IMP3A 3U cPCI SBC\n");
+}
+
+/* Return the PCB revision */
+static unsigned int ge_imp3a_get_pcb_rev(void)
+{
+ unsigned int reg;
+
+ reg = ioread16(imp3a_regs);
+ return (reg >> 8) & 0xff;
+}
+
+/* Return the board (software) revision */
+static unsigned int ge_imp3a_get_board_rev(void)
+{
+ unsigned int reg;
+
+ reg = ioread16(imp3a_regs + 0x2);
+ return reg & 0xff;
+}
+
+/* Return the FPGA revision */
+static unsigned int ge_imp3a_get_fpga_rev(void)
+{
+ unsigned int reg;
+
+ reg = ioread16(imp3a_regs + 0x2);
+ return (reg >> 8) & 0xff;
+}
+
+/* Return compactPCI Geographical Address */
+static unsigned int ge_imp3a_get_cpci_geo_addr(void)
+{
+ unsigned int reg;
+
+ reg = ioread16(imp3a_regs + 0x6);
+ return (reg & 0x0f00) >> 8;
+}
+
+/* Return compactPCI System Controller Status */
+static unsigned int ge_imp3a_get_cpci_is_syscon(void)
+{
+ unsigned int reg;
+
+ reg = ioread16(imp3a_regs + 0x6);
+ return reg & (1 << 12);
+}
+
+static void ge_imp3a_show_cpuinfo(struct seq_file *m)
+{
+ seq_printf(m, "Vendor\t\t: GE Intelligent Platforms\n");
+
+ seq_printf(m, "Revision\t: %u%c\n", ge_imp3a_get_pcb_rev(),
+ ('A' + ge_imp3a_get_board_rev() - 1));
+
+ seq_printf(m, "FPGA Revision\t: %u\n", ge_imp3a_get_fpga_rev());
+
+ seq_printf(m, "cPCI geo. addr\t: %u\n", ge_imp3a_get_cpci_geo_addr());
+
+ seq_printf(m, "cPCI syscon\t: %s\n",
+ ge_imp3a_get_cpci_is_syscon() ? "yes" : "no");
+}
+
+/*
+ * Called very early, device-tree isn't unflattened
+ */
+static int __init ge_imp3a_probe(void)
+{
+ unsigned long root = of_get_flat_dt_root();
+
+ if (of_flat_dt_is_compatible(root, "ge,IMP3A")) {
+#ifdef CONFIG_PCI
+ primary_phb_addr = 0x9000;
+#endif
+ return 1;
+ }
+
+ return 0;
+}
+
+machine_device_initcall(ge_imp3a, mpc85xx_common_publish_devices);
+
+machine_arch_initcall(ge_imp3a, swiotlb_setup_bus_notifier);
+
+define_machine(ge_imp3a) {
+ .name = "GE_IMP3A",
+ .probe = ge_imp3a_probe,
+ .setup_arch = ge_imp3a_setup_arch,
+ .init_IRQ = ge_imp3a_pic_init,
+ .show_cpuinfo = ge_imp3a_show_cpuinfo,
+#ifdef CONFIG_PCI
+ .pcibios_fixup_bus = fsl_pcibios_fixup_bus,
+#endif
+ .get_irq = mpic_get_irq,
+ .restart = fsl_rstcr_restart,
+ .calibrate_decr = generic_calibrate_decr,
+ .progress = udbg_progress,
+};
diff --git a/arch/powerpc/sysdev/ge/ge_gpio.c b/arch/powerpc/sysdev/ge/ge_gpio.c
index f8e6289..7b95a4a 100644
--- a/arch/powerpc/sysdev/ge/ge_gpio.c
+++ b/arch/powerpc/sysdev/ge/ge_gpio.c
@@ -162,6 +162,34 @@ static int __init gef_gpio_init(void)
}
}
+ for_each_compatible_node(np, NULL, "ge,imp3a-gpio") {
+
+ pr_debug("%s: Initialising GE GPIO\n", np->full_name);
+
+ /* Allocate chip structure */
+ gef_gpio_chip = kzalloc(sizeof(*gef_gpio_chip), GFP_KERNEL);
+ if (!gef_gpio_chip) {
+ pr_err("%s: Unable to allocate structure\n",
+ np->full_name);
+ continue;
+ }
+
+ /* Setup pointers to chip functions */
+ gef_gpio_chip->gc.of_gpio_n_cells = 2;
+ gef_gpio_chip->gc.ngpio = 16;
+ gef_gpio_chip->gc.direction_input = gef_gpio_dir_in;
+ gef_gpio_chip->gc.direction_output = gef_gpio_dir_out;
+ gef_gpio_chip->gc.get = gef_gpio_get;
+ gef_gpio_chip->gc.set = gef_gpio_set;
+
+ /* This function adds a memory mapped GPIO chip */
+ retval = of_mm_gpiochip_add(np, gef_gpio_chip);
+ if (retval) {
+ kfree(gef_gpio_chip);
+ pr_err("%s: Unable to add GPIO\n", np->full_name);
+ }
+ }
+
return 0;
};
arch_initcall(gef_gpio_init);
--
1.7.0.4
^ permalink raw reply related
* [PATCH V2 1/2] powerpc: Move GE GPIO and PIC drivers
From: Martyn Welch @ 2012-02-28 10:22 UTC (permalink / raw)
To: linuxppc-dev; +Cc: Martyn Welch, Wim Van Sebroeck, linux-kernel
In-Reply-To: <1330424541-5299-1-git-send-email-martyn.welch@ge.com>
Move the GE GPIO and PIC drivers to allow these to be used by non-86xx
boards.
Signed-off-by: Martyn Welch <martyn.welch@ge.com>
---
v2: Move GPIO and PIC drivers to sysdev/ge/ rather than platforms/.
arch/powerpc/platforms/86xx/Kconfig | 3 +++
arch/powerpc/platforms/86xx/Makefile | 7 +++----
arch/powerpc/platforms/86xx/gef_ppc9a.c | 2 +-
arch/powerpc/platforms/86xx/gef_sbc310.c | 2 +-
arch/powerpc/platforms/86xx/gef_sbc610.c | 2 +-
arch/powerpc/sysdev/Kconfig | 7 +++++++
arch/powerpc/sysdev/Makefile | 2 ++
arch/powerpc/sysdev/ge/Makefile | 2 ++
.../86xx/gef_gpio.c => sysdev/ge/ge_gpio.c} | 2 +-
.../86xx/gef_pic.c => sysdev/ge/ge_pic.c} | 2 +-
.../86xx/gef_pic.h => sysdev/ge/ge_pic.h} | 0
drivers/watchdog/Kconfig | 2 +-
12 files changed, 23 insertions(+), 10 deletions(-)
create mode 100644 arch/powerpc/sysdev/ge/Makefile
rename arch/powerpc/{platforms/86xx/gef_gpio.c => sysdev/ge/ge_gpio.c} (98%)
rename arch/powerpc/{platforms/86xx/gef_pic.c => sysdev/ge/ge_pic.c} (99%)
rename arch/powerpc/{platforms/86xx/gef_pic.h => sysdev/ge/ge_pic.h} (100%)
diff --git a/arch/powerpc/platforms/86xx/Kconfig b/arch/powerpc/platforms/86xx/Kconfig
index 8d6599d..2015022 100644
--- a/arch/powerpc/platforms/86xx/Kconfig
+++ b/arch/powerpc/platforms/86xx/Kconfig
@@ -39,6 +39,7 @@ config GEF_PPC9A
select MMIO_NVRAM
select GENERIC_GPIO
select ARCH_REQUIRE_GPIOLIB
+ select GE_FPGA
help
This option enables support for the GE PPC9A.
@@ -48,6 +49,7 @@ config GEF_SBC310
select MMIO_NVRAM
select GENERIC_GPIO
select ARCH_REQUIRE_GPIOLIB
+ select GE_FPGA
help
This option enables support for the GE SBC310.
@@ -58,6 +60,7 @@ config GEF_SBC610
select GENERIC_GPIO
select ARCH_REQUIRE_GPIOLIB
select HAS_RAPIDIO
+ select GE_FPGA
help
This option enables support for the GE SBC610.
diff --git a/arch/powerpc/platforms/86xx/Makefile b/arch/powerpc/platforms/86xx/Makefile
index 4b0d7b1..ede815d 100644
--- a/arch/powerpc/platforms/86xx/Makefile
+++ b/arch/powerpc/platforms/86xx/Makefile
@@ -7,7 +7,6 @@ obj-$(CONFIG_SMP) += mpc86xx_smp.o
obj-$(CONFIG_MPC8641_HPCN) += mpc86xx_hpcn.o
obj-$(CONFIG_SBC8641D) += sbc8641d.o
obj-$(CONFIG_MPC8610_HPCD) += mpc8610_hpcd.o
-gef-gpio-$(CONFIG_GPIOLIB) += gef_gpio.o
-obj-$(CONFIG_GEF_SBC610) += gef_sbc610.o gef_pic.o $(gef-gpio-y)
-obj-$(CONFIG_GEF_SBC310) += gef_sbc310.o gef_pic.o $(gef-gpio-y)
-obj-$(CONFIG_GEF_PPC9A) += gef_ppc9a.o gef_pic.o $(gef-gpio-y)
+obj-$(CONFIG_GEF_SBC610) += gef_sbc610.o
+obj-$(CONFIG_GEF_SBC310) += gef_sbc310.o
+obj-$(CONFIG_GEF_PPC9A) += gef_ppc9a.o
diff --git a/arch/powerpc/platforms/86xx/gef_ppc9a.c b/arch/powerpc/platforms/86xx/gef_ppc9a.c
index 60ce07e..ed58b6c 100644
--- a/arch/powerpc/platforms/86xx/gef_ppc9a.c
+++ b/arch/powerpc/platforms/86xx/gef_ppc9a.c
@@ -37,9 +37,9 @@
#include <sysdev/fsl_pci.h>
#include <sysdev/fsl_soc.h>
+#include <sysdev/ge/ge_pic.h>
#include "mpc86xx.h"
-#include "gef_pic.h"
#undef DEBUG
diff --git a/arch/powerpc/platforms/86xx/gef_sbc310.c b/arch/powerpc/platforms/86xx/gef_sbc310.c
index 3ecee25..710db69 100644
--- a/arch/powerpc/platforms/86xx/gef_sbc310.c
+++ b/arch/powerpc/platforms/86xx/gef_sbc310.c
@@ -37,9 +37,9 @@
#include <sysdev/fsl_pci.h>
#include <sysdev/fsl_soc.h>
+#include <sysdev/ge/ge_pic.h>
#include "mpc86xx.h"
-#include "gef_pic.h"
#undef DEBUG
diff --git a/arch/powerpc/platforms/86xx/gef_sbc610.c b/arch/powerpc/platforms/86xx/gef_sbc610.c
index 5090d60..4a13d2f 100644
--- a/arch/powerpc/platforms/86xx/gef_sbc610.c
+++ b/arch/powerpc/platforms/86xx/gef_sbc610.c
@@ -37,9 +37,9 @@
#include <sysdev/fsl_pci.h>
#include <sysdev/fsl_soc.h>
+#include <sysdev/ge/ge_pic.h>
#include "mpc86xx.h"
-#include "gef_pic.h"
#undef DEBUG
diff --git a/arch/powerpc/sysdev/Kconfig b/arch/powerpc/sysdev/Kconfig
index 7b4df37..cd0ef0b 100644
--- a/arch/powerpc/sysdev/Kconfig
+++ b/arch/powerpc/sysdev/Kconfig
@@ -29,3 +29,10 @@ config SCOM_DEBUGFS
bool "Expose SCOM controllers via debugfs"
depends on PPC_SCOM
default n
+
+config GE_FPGA
+ bool
+ default n
+ help
+ Support for common GPIO and interrupt routing functionality provided
+ on some GE Single Board Computers.
diff --git a/arch/powerpc/sysdev/Makefile b/arch/powerpc/sysdev/Makefile
index 5e37b47..f80ff9f 100644
--- a/arch/powerpc/sysdev/Makefile
+++ b/arch/powerpc/sysdev/Makefile
@@ -65,3 +65,5 @@ obj-$(CONFIG_PPC_SCOM) += scom.o
subdir-ccflags-$(CONFIG_PPC_WERROR) := -Werror
obj-$(CONFIG_PPC_XICS) += xics/
+
+obj-$(CONFIG_GE_FPGA) += ge/
diff --git a/arch/powerpc/sysdev/ge/Makefile b/arch/powerpc/sysdev/ge/Makefile
new file mode 100644
index 0000000..6a10372
--- /dev/null
+++ b/arch/powerpc/sysdev/ge/Makefile
@@ -0,0 +1,2 @@
+gef-gpio-$(CONFIG_GPIOLIB) += ge_gpio.o
+obj-$(CONFIG_GE_FPGA) += ge_pic.o $(gef-gpio-y)
diff --git a/arch/powerpc/platforms/86xx/gef_gpio.c b/arch/powerpc/sysdev/ge/ge_gpio.c
similarity index 98%
rename from arch/powerpc/platforms/86xx/gef_gpio.c
rename to arch/powerpc/sysdev/ge/ge_gpio.c
index 2a70336..f8e6289 100644
--- a/arch/powerpc/platforms/86xx/gef_gpio.c
+++ b/arch/powerpc/sysdev/ge/ge_gpio.c
@@ -14,7 +14,7 @@
*
* Configuration of output modes (totem-pole/open-drain)
* Interrupt configuration - interrupts are always generated the FPGA relies on
- * the I/O interrupt controllers mask to stop them propergating
+ * the I/O interrupt controllers mask to stop them propergating
*/
#include <linux/kernel.h>
diff --git a/arch/powerpc/platforms/86xx/gef_pic.c b/arch/powerpc/sysdev/ge/ge_pic.c
similarity index 99%
rename from arch/powerpc/platforms/86xx/gef_pic.c
rename to arch/powerpc/sysdev/ge/ge_pic.c
index 94594e5..002a562 100644
--- a/arch/powerpc/platforms/86xx/gef_pic.c
+++ b/arch/powerpc/sysdev/ge/ge_pic.c
@@ -22,7 +22,7 @@
#include <asm/prom.h>
#include <asm/irq.h>
-#include "gef_pic.h"
+#include "ge_pic.h"
#define DEBUG
#undef DEBUG
diff --git a/arch/powerpc/platforms/86xx/gef_pic.h b/arch/powerpc/sysdev/ge/ge_pic.h
similarity index 100%
rename from arch/powerpc/platforms/86xx/gef_pic.h
rename to arch/powerpc/sysdev/ge/ge_pic.h
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 877b107..2955c3f 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -1039,7 +1039,7 @@ config LANTIQ_WDT
config GEF_WDT
tristate "GE Watchdog Timer"
- depends on GEF_SBC610 || GEF_SBC310 || GEF_PPC9A
+ depends on GE_FPGA
---help---
Watchdog timer found in a number of GE single board computers.
--
1.7.0.4
^ permalink raw reply related
* [PATCH V2 0/2] powerpc: Add support for GE IMP3A
From: Martyn Welch @ 2012-02-28 10:22 UTC (permalink / raw)
To: linuxppc-dev; +Cc: Martyn Welch, Wim Van Sebroeck, linux-kernel
These patches add support for the GE IMP3A. This board (based on a Freescale
P2020) uses some support for FPGA logic common with the PPC9A and other 86xx
based boards, so this support has been moved out of the 86xx directory. A
config option (GE_FPGA) has been added to reduce churn on dependant drivers
(such as the watchdog timer) when further boards are added.
^ permalink raw reply
* RE: [PATCH 20/21] Introduce struct eeh_stats for EEH
From: David Laight @ 2012-02-28 10:04 UTC (permalink / raw)
To: Gavin Shan, linuxppc-dev
In-Reply-To: <1330409051-8941-21-git-send-email-shangw@linux.vnet.ibm.com>
=20
> +struct eeh_stats {
> + unsigned int no_device; /* PCI device not found */
...
> + "no device =3D%d\n"
...
Use %u (for all the stats), you really don't want negative
values printed.
I've NFI how long wrapping these counters might take!
If it is feasable (maybe much above 100Hz) then you
need 64bit counters.
David
^ permalink raw reply
* Re: [PATCH 08/18] PCI, powerpc: Register busn_res for root buses
From: Benjamin Herrenschmidt @ 2012-02-28 8:54 UTC (permalink / raw)
To: Bjorn Helgaas
Cc: linux-arch, Tony Luck, Yinghai Lu, linuxppc-dev, linux-pci,
linux-kernel, Dominik Brodowski, Paul Mackerras, Jesse Barnes,
Greg Kroah-Hartman, Andrew Morton, Linus Torvalds
In-Reply-To: <CAErSpo4bw-V=SM+h5iP4nfv3HaAnHjwvNwu+4Oz8iyQcY_3gOQ@mail.gmail.com>
On Mon, 2012-02-27 at 22:36 -0700, Bjorn Helgaas wrote:
>
> There's a lot of powerpc code that does this:
>
> bus_range = of_get_property(pcictrl, "bus-range", &len);
> hose->first_busno = bus_range[0];
> hose->last_busno = bus_range[1];
>
> That *looks* like it is discovering the bus number aperture. Is it?
> If it is, why are we using the largest bus number found by
> pci_scan_child_bus() rather than "last_busno"?
We do that but we somewhat -also- rely on the core bumping it if it
needs to make room :-)
As I said, we are swimming in dirty waters between reverse engineered
stuff we don't know 100% and "designed" stuff.
I think we should have ways to more explicitely define what we want tho,
ie whether hose->last_busno is just what happens to be the "current" bus
number assigned by the firmware or the hard max. Maybe a pci flag ?
On the other hand some platforms (all the ppc4xx ones for example) set
the flag to reassign all busses ... but have limit on bus numbers simply
because they have a memory mapped only config space and we don't have
enough address space to ioremap it all on 32-bit.
We need to fix them to use a fixmap entry to do atomic on-demand mapping
of the config space and lift that restriction, but that isn't done yet.
So I think those patches will need really careful handling on our side.
Cheers,
Ben.
^ permalink raw reply
* Recall: [PATCH 1/2] powerpc/e500: make load_up_spe a normal fuction
From: Yin Olivia-R63875 @ 2012-02-28 7:13 UTC (permalink / raw)
To: Wood Scott-B07421
Cc: linuxppc-dev@lists.ozlabs.org, kvm@vger.kernel.org,
kvm-ppc@vger.kernel.org
Yin Olivia-R63875 would like to recall the message, "[PATCH 1/2] powerpc/e5=
00: make load_up_spe a normal fuction".=
^ permalink raw reply
* RE: [PATCH 1/2] powerpc/e500: make load_up_spe a normal fuction
From: Yin Olivia-R63875 @ 2012-02-28 7:09 UTC (permalink / raw)
To: Wood Scott-B07421
Cc: linuxppc-dev@lists.ozlabs.org, kvm@vger.kernel.org,
kvm-ppc@vger.kernel.org
In-Reply-To: <4F4BD726.1030805@freescale.com>
SGkgU2NvdHQsDQoNClRoaXMgaGFkIGJlZW4gcmV2aWV3ZWQgYmVmb3JlIGFuZCBhY2NlcHRlZCBi
eSBpbnRlcm5hbCB0cmVlLg0KaHR0cDovL2xpbnV4LmZyZWVzY2FsZS5uZXQvcGF0Y2h3b3JrL3Bh
dGNoLzExMTAwLw0KaHR0cDovL2dpdC5hbS5mcmVlc2NhbGUubmV0L2dpdG9saXRlL2dpdHdlYi5j
Z2kvc2RrL2t2bS5naXQvY29tbWl0Lz9oPWZvci1zZGsxLjImaWQ9YzUwODg4NDRkYzY2NWRiZGFl
NGZhNTFiOGQ1OGRjMjAzYmFjYzE3ZQ0KDQpJIGRpZG4ndCBjaGFuZ2UgYW55dGhpbmcgZXhjZXB0
IHRoZSBsaW5lLg0KSSBqdXN0IGNvbW1pdCB0byBleHRlcm5hbCBrdm0tcHBjIG1haWxpbmcgbGlz
dC4gU2hvdWxkIEkgYWRkIG15IG93biBTaWduZWQtb2ZmLWJ5Pw0KDQpCZXN0IFJlZ2FyZHMsDQpP
bGl2aWENCg0KLS0tLS1PcmlnaW5hbCBNZXNzYWdlLS0tLS0NCkZyb206IFdvb2QgU2NvdHQtQjA3
NDIxIA0KU2VudDogVHVlc2RheSwgRmVicnVhcnkgMjgsIDIwMTIgMzoxOSBBTQ0KVG86IFlpbiBP
bGl2aWEtUjYzODc1DQpDYzoga3ZtLXBwY0B2Z2VyLmtlcm5lbC5vcmc7IGt2bUB2Z2VyLmtlcm5l
bC5vcmc7IGxpbnV4cHBjLWRldkBsaXN0cy5vemxhYnMub3JnOyBMaXUgWXUtQjEzMjAxDQpTdWJq
ZWN0OiBSZTogW1BBVENIIDEvMl0gcG93ZXJwYy9lNTAwOiBtYWtlIGxvYWRfdXBfc3BlIGEgbm9y
bWFsIGZ1Y3Rpb24NCg0KT24gMDIvMjcvMjAxMiAwNDo1OSBBTSwgT2xpdmlhIFlpbiB3cm90ZToN
Cj4gU28gdGhhdCB3ZSBjYW4gY2FsbCBpdCBpbiBrZXJuZWwuDQo+IA0KPiBTaWduZWQtb2ZmLWJ5
OiBMaXUgWXUgPHl1LmxpdUBmcmVlc2NhbGUuY29tPg0KDQpFeHBsYWluIHdoeSB3ZSB3YW50IHRo
aXMsIGFuZCBwb2ludCBvdXQgdGhhdCB0aGlzIG1ha2VzIGl0IHNpbWlsYXIgdG8gbG9hZF91cF9m
cHUuDQoNCj4gLS0tDQo+ICBhcmNoL3Bvd2VycGMva2VybmVsL2hlYWRfZnNsX2Jvb2tlLlMgfCAg
IDIzICsrKysrKy0tLS0tLS0tLS0tLS0tLS0tDQo+ICAxIGZpbGVzIGNoYW5nZWQsIDYgaW5zZXJ0
aW9ucygrKSwgMTcgZGVsZXRpb25zKC0pDQoNCldoZW4gcG9zdGluZyBhIHBhdGNoIGF1dGhvcmVk
IGJ5IHNvbWVvbmUgZWxzZSwgbW9yZSBvciBsZXNzIHVuY2hhbmdlZCwgeW91IHNob3VsZCBwdXQg
YSBGcm9tOiBsaW5lIGluIHRoZSBib2R5IG9mIHRoZSBlLW1haWwuDQoNCmdpdCBzZW5kLWVtYWls
IHdpbGwgZG8gdGhpcyBhdXRvbWF0aWNhbGx5IGlmIHlvdSBwcmVzZXJ2ZSB0aGUgYXV0aG9yc2hp
cCBpbiB0aGUgZ2l0IGNvbW1pdC4NCg0KQWxzbywgeW91IHNob3VsZCBhZGQgeW91ciBvd24gU2ln
bmVkLW9mZi1ieS4NCg0KLVNjb3R0DQo=
^ permalink raw reply
* [PATCH 18/21] Replace pci_dn with eeh_dev for EEH aux components
From: Gavin Shan @ 2012-02-28 6:04 UTC (permalink / raw)
To: linuxppc-dev; +Cc: shangw
In-Reply-To: <1330409051-8941-1-git-send-email-shangw@linux.vnet.ibm.com>
The original EEH implementation is heavily depending on struct pci_dn.
We have to put EEH related information to pci_dn. Actually, we could
split struct pci_dn so that the EEH sensitive information to form an
individual struct, then EEH looks more independent.
The patch replaces pci_dn with eeh_dev for EEH aux components like
event and driver. Also, the eeh_event struct has been adjusted for
a little bit since eeh_dev has linked the associated FDT (Flat Device
Tree) node and PCI device. It's not necessary for eeh_event struct to
trace FDT node and PCI device. We can just simply to trace eeh_dev in
eeh_event.
The patch also renames function pcid_name() to eeh_pcid_name(), which
should be missed in the previous patch where the EEH aux components
have been cleaned up.
Signed-off-by: Gavin Shan <shangw@linux.vnet.ibm.com>
---
arch/powerpc/include/asm/eeh_event.h | 7 +-
arch/powerpc/platforms/pseries/eeh.c | 2 +-
arch/powerpc/platforms/pseries/eeh_driver.c | 81 +++++++++++++--------------
arch/powerpc/platforms/pseries/eeh_event.c | 36 ++++++------
4 files changed, 63 insertions(+), 63 deletions(-)
diff --git a/arch/powerpc/include/asm/eeh_event.h b/arch/powerpc/include/asm/eeh_event.h
index 25ebf6a..c68b012 100644
--- a/arch/powerpc/include/asm/eeh_event.h
+++ b/arch/powerpc/include/asm/eeh_event.h
@@ -28,12 +28,11 @@
*/
struct eeh_event {
struct list_head list; /* to form event queue */
- struct device_node *dn; /* struct device node */
- struct pci_dev *dev; /* affected device */
+ struct eeh_dev *edev; /* EEH device */
};
-int eeh_send_failure_event(struct device_node *dn, struct pci_dev *dev);
-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 */
diff --git a/arch/powerpc/platforms/pseries/eeh.c b/arch/powerpc/platforms/pseries/eeh.c
index aec10f6..9b1fd0c 100644
--- a/arch/powerpc/platforms/pseries/eeh.c
+++ b/arch/powerpc/platforms/pseries/eeh.c
@@ -475,7 +475,7 @@ int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev)
eeh_mark_slot(dn, EEH_MODE_ISOLATED);
raw_spin_unlock_irqrestore(&confirm_error_lock, flags);
- eeh_send_failure_event(edev->dn, edev->pdev);
+ eeh_send_failure_event(edev);
/* Most EEH events are due to device driver bugs. Having
* a stack trace will help the device-driver authors figure
diff --git a/arch/powerpc/platforms/pseries/eeh_driver.c b/arch/powerpc/platforms/pseries/eeh_driver.c
index 3f25fab..baf92cd 100644
--- a/arch/powerpc/platforms/pseries/eeh_driver.c
+++ b/arch/powerpc/platforms/pseries/eeh_driver.c
@@ -40,7 +40,7 @@
* This routine is used to retrieve the name of PCI device driver
* if that's valid.
*/
-static inline const char *pcid_name(struct pci_dev *pdev)
+static inline const char *eeh_pcid_name(struct pci_dev *pdev)
{
if (pdev && pdev->dev.driver)
return pdev->dev.driver->name;
@@ -81,7 +81,7 @@ static void print_device_node_tree(struct pci_dn *pdn, int dent)
*/
static void eeh_disable_irq(struct pci_dev *dev)
{
- struct device_node *dn = pci_device_to_OF_node(dev);
+ struct eeh_dev *edev = pci_dev_to_eeh_dev(dev);
/* Don't disable MSI and MSI-X interrupts. They are
* effectively disabled by the DMA Stopped state
@@ -93,7 +93,7 @@ static void eeh_disable_irq(struct pci_dev *dev)
if (!irq_has_action(dev->irq))
return;
- PCI_DN(dn)->eeh_mode |= EEH_MODE_IRQ_DISABLED;
+ edev->mode |= EEH_MODE_IRQ_DISABLED;
disable_irq_nosync(dev->irq);
}
@@ -106,10 +106,10 @@ static void eeh_disable_irq(struct pci_dev *dev)
*/
static void eeh_enable_irq(struct pci_dev *dev)
{
- struct device_node *dn = pci_device_to_OF_node(dev);
+ struct eeh_dev *edev = pci_dev_to_eeh_dev(dev);
- if ((PCI_DN(dn)->eeh_mode) & EEH_MODE_IRQ_DISABLED) {
- PCI_DN(dn)->eeh_mode &= ~EEH_MODE_IRQ_DISABLED;
+ if ((edev->mode) & EEH_MODE_IRQ_DISABLED) {
+ edev->mode &= ~EEH_MODE_IRQ_DISABLED;
enable_irq(dev->irq);
}
}
@@ -270,20 +270,20 @@ static int eeh_report_failure(struct pci_dev *dev, void *userdata)
/**
* eeh_reset_device - Perform actual reset of a pci slot
- * @pe_dn: PE associated device node
+ * @edev: PE associated EEH device
* @bus: PCI bus corresponding to the isolcated slot
*
* This routine must be called to do reset on the indicated PE.
* During the reset, udev might be invoked because those affected
* PCI devices will be removed and then added.
*/
-static int eeh_reset_device(struct pci_dn *pe_dn, struct pci_bus *bus)
+static int eeh_reset_device(struct eeh_dev *edev, struct pci_bus *bus)
{
struct device_node *dn;
int cnt, rc;
/* pcibios will clear the counter; save the value */
- cnt = pe_dn->eeh_freeze_count;
+ cnt = edev->freeze_count;
if (bus)
pcibios_remove_pci_devices(bus);
@@ -292,21 +292,22 @@ static int eeh_reset_device(struct pci_dn *pe_dn, struct pci_bus *bus)
* Reconfigure bridges and devices. Don't try to bring the system
* up if the reset failed for some reason.
*/
- rc = eeh_reset_pe(pe_dn);
+ rc = eeh_reset_pe(edev);
if (rc)
return rc;
/* Walk over all functions on this device. */
- dn = pe_dn->node;
- if (!pcibios_find_pci_bus(dn) && PCI_DN(dn->parent))
+ dn = eeh_dev_to_of_node(edev);
+ if (!pcibios_find_pci_bus(dn) && of_node_to_eeh_dev(dn->parent))
dn = dn->parent->child;
while (dn) {
- struct pci_dn *ppe = PCI_DN(dn);
+ struct eeh_dev *pedev = of_node_to_eeh_dev(dn);
+
/* On Power4, always true because eeh_pe_config_addr=0 */
- if (pe_dn->eeh_pe_config_addr == ppe->eeh_pe_config_addr) {
+ if (edev->pe_config_addr == pedev->pe_config_addr) {
eeh_ops->configure_bridge(dn);
- eeh_restore_bars(ppe);
+ eeh_restore_bars(pedev);
}
dn = dn->sibling;
}
@@ -321,7 +322,7 @@ static int eeh_reset_device(struct pci_dn *pe_dn, struct pci_bus *bus)
ssleep(5);
pcibios_add_pci_devices(bus);
}
- pe_dn->eeh_freeze_count = cnt;
+ edev->freeze_count = cnt;
return 0;
}
@@ -348,23 +349,22 @@ static int eeh_reset_device(struct pci_dn *pe_dn, struct pci_bus *bus)
* drivers (which cause a second set of hotplug events to go out to
* userspace).
*/
-struct pci_dn *handle_eeh_events(struct eeh_event *event)
+struct eeh_dev *handle_eeh_events(struct eeh_event *event)
{
struct device_node *frozen_dn;
- struct pci_dn *frozen_pdn;
+ struct eeh_dev *frozen_edev;
struct pci_bus *frozen_bus;
int rc = 0;
enum pci_ers_result result = PCI_ERS_RESULT_NONE;
const char *location, *pci_str, *drv_str, *bus_pci_str, *bus_drv_str;
- frozen_dn = eeh_find_device_pe(event->dn);
+ frozen_dn = eeh_find_device_pe(eeh_dev_to_of_node(event->edev));
if (!frozen_dn) {
-
- location = of_get_property(event->dn, "ibm,loc-code", NULL);
+ location = of_get_property(eeh_dev_to_of_node(event->edev), "ibm,loc-code", NULL);
location = location ? location : "unknown";
printk(KERN_ERR "EEH: Error: Cannot find partition endpoint "
"for location=%s pci addr=%s\n",
- location, eeh_pci_name(event->dev));
+ location, eeh_pci_name(eeh_dev_to_pci_dev(event->edev)));
return NULL;
}
@@ -389,22 +389,21 @@ struct pci_dn *handle_eeh_events(struct eeh_event *event)
return NULL;
}
- frozen_pdn = PCI_DN(frozen_dn);
- frozen_pdn->eeh_freeze_count++;
+ frozen_edev = of_node_to_eeh_dev(frozen_dn);
+ frozen_edev->freeze_count++;
+ pci_str = eeh_pci_name(eeh_dev_to_pci_dev(event->edev));
+ drv_str = eeh_pcid_name(eeh_dev_to_pci_dev(event->edev));
- pci_str = eeh_pci_name(event->dev);
- drv_str = pcid_name(event->dev);
-
- if (frozen_pdn->eeh_freeze_count > EEH_MAX_ALLOWED_FREEZES)
+ if (frozen_edev->freeze_count > EEH_MAX_ALLOWED_FREEZES)
goto excess_failures;
printk(KERN_WARNING
"EEH: This PCI device has failed %d times in the last hour:\n",
- frozen_pdn->eeh_freeze_count);
+ frozen_edev->freeze_count);
- if (frozen_pdn->pcidev) {
- bus_pci_str = pci_name(frozen_pdn->pcidev);
- bus_drv_str = pcid_name(frozen_pdn->pcidev);
+ if (frozen_edev->pdev) {
+ bus_pci_str = pci_name(frozen_edev->pdev);
+ bus_drv_str = eeh_pcid_name(frozen_edev->pdev);
printk(KERN_WARNING
"EEH: Bus location=%s driver=%s pci addr=%s\n",
location, bus_drv_str, bus_pci_str);
@@ -425,7 +424,7 @@ struct pci_dn *handle_eeh_events(struct eeh_event *event)
/* Get the current PCI slot state. This can take a long time,
* sometimes over 3 seconds for certain systems.
*/
- rc = eeh_ops->wait_state(frozen_pdn->node, MAX_WAIT_FOR_RECOVERY*1000);
+ rc = eeh_ops->wait_state(eeh_dev_to_of_node(frozen_edev), MAX_WAIT_FOR_RECOVERY*1000);
if (rc < 0 || rc == EEH_STATE_NOT_SUPPORT) {
printk(KERN_WARNING "EEH: Permanent failure\n");
goto hard_fail;
@@ -435,14 +434,14 @@ struct pci_dn *handle_eeh_events(struct eeh_event *event)
* don't post the error log until after all dev drivers
* have been informed.
*/
- eeh_slot_error_detail(frozen_pdn, EEH_LOG_TEMP);
+ eeh_slot_error_detail(frozen_edev, EEH_LOG_TEMP);
/* 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);
+ rc = eeh_reset_device(frozen_edev, frozen_bus);
if (rc) {
printk(KERN_WARNING "EEH: Unable to reset, rc=%d\n", rc);
goto hard_fail;
@@ -451,7 +450,7 @@ struct pci_dn *handle_eeh_events(struct eeh_event *event)
/* If all devices reported they can proceed, then re-enable MMIO */
if (result == PCI_ERS_RESULT_CAN_RECOVER) {
- rc = eeh_pci_enable(frozen_pdn, EEH_OPT_THAW_MMIO);
+ rc = eeh_pci_enable(frozen_edev, EEH_OPT_THAW_MMIO);
if (rc < 0)
goto hard_fail;
@@ -465,7 +464,7 @@ struct pci_dn *handle_eeh_events(struct eeh_event *event)
/* If all devices reported they can proceed, then re-enable DMA */
if (result == PCI_ERS_RESULT_CAN_RECOVER) {
- rc = eeh_pci_enable(frozen_pdn, EEH_OPT_THAW_DMA);
+ rc = eeh_pci_enable(frozen_edev, EEH_OPT_THAW_DMA);
if (rc < 0)
goto hard_fail;
@@ -483,7 +482,7 @@ struct pci_dn *handle_eeh_events(struct eeh_event *event)
/* 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);
+ rc = eeh_reset_device(frozen_edev, NULL);
if (rc) {
printk(KERN_WARNING "EEH: Cannot reset, rc=%d\n", rc);
goto hard_fail;
@@ -502,7 +501,7 @@ struct pci_dn *handle_eeh_events(struct eeh_event *event)
/* Tell all device drivers that they can resume operations */
pci_walk_bus(frozen_bus, eeh_report_resume, NULL);
- return frozen_pdn;
+ return frozen_edev;
excess_failures:
/*
@@ -515,7 +514,7 @@ excess_failures:
"has failed %d times in the last hour "
"and has been permanently disabled.\n"
"Please try reseating this device or replacing it.\n",
- location, drv_str, pci_str, frozen_pdn->eeh_freeze_count);
+ location, drv_str, pci_str, frozen_edev->freeze_count);
goto perm_error;
hard_fail:
@@ -526,7 +525,7 @@ hard_fail:
location, drv_str, pci_str);
perm_error:
- eeh_slot_error_detail(frozen_pdn, EEH_LOG_PERM);
+ eeh_slot_error_detail(frozen_edev, EEH_LOG_PERM);
/* Notify all devices that they're about to go down. */
pci_walk_bus(frozen_bus, eeh_report_failure, NULL);
diff --git a/arch/powerpc/platforms/pseries/eeh_event.c b/arch/powerpc/platforms/pseries/eeh_event.c
index e98347c..4a47525 100644
--- a/arch/powerpc/platforms/pseries/eeh_event.c
+++ b/arch/powerpc/platforms/pseries/eeh_event.c
@@ -56,8 +56,8 @@ 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");
set_current_state(TASK_INTERRUPTIBLE);
@@ -77,23 +77,26 @@ 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) {
+ if (edev && edev->freeze_count>0) {
msleep_interruptible(3600*1000);
- if (pdn->eeh_freeze_count>0)
- pdn->eeh_freeze_count--;
+ if (edev->freeze_count>0)
+ edev->freeze_count--;
+
}
return 0;
@@ -114,17 +117,17 @@ static void eeh_thread_launcher(struct work_struct *dummy)
/**
* eeh_send_failure_event - Generate a PCI error event
- * @dev: pci device
+ * @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) {
@@ -140,11 +143,10 @@ int eeh_send_failure_event(struct device_node *dn,
return 1;
}
- if (dev)
- pci_dev_get(dev);
+ if (edev->pdev)
+ pci_dev_get(edev->pdev);
- 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);
--
1.7.5.4
^ permalink raw reply related
* [PATCH 15/21] Replace pci_dn with eeh_dev for EEH sysfs
From: Gavin Shan @ 2012-02-28 6:04 UTC (permalink / raw)
To: linuxppc-dev; +Cc: shangw
In-Reply-To: <1330409051-8941-1-git-send-email-shangw@linux.vnet.ibm.com>
With original EEH implementation, all EEH related statistics have
been put into struct pci_dn. We've introduced struct eeh_dev to
replace struct pci_dn in EEH core components, including EEH sysfs
component.
The patch shows EEH statistics from struct eeh_dev instead of struct
pci_dn in EEH sysfs component. Besides, it also fixed the EEH device
retrieval from PCI device, which was introduced by the previous patch
in the series of patch.
Signed-off-by: Gavin Shan <shangw@linux.vnet.ibm.com>
---
arch/powerpc/platforms/pseries/eeh_sysfs.c | 23 ++++++++++-------------
1 files changed, 10 insertions(+), 13 deletions(-)
diff --git a/arch/powerpc/platforms/pseries/eeh_sysfs.c b/arch/powerpc/platforms/pseries/eeh_sysfs.c
index 5e4eab1..243b351 100644
--- a/arch/powerpc/platforms/pseries/eeh_sysfs.c
+++ b/arch/powerpc/platforms/pseries/eeh_sysfs.c
@@ -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)
{
--
1.7.5.4
^ permalink raw reply related
* [PATCH 16/21] Replace pci_dn with eeh_dev for EEH address cache
From: Gavin Shan @ 2012-02-28 6:04 UTC (permalink / raw)
To: linuxppc-dev; +Cc: shangw
In-Reply-To: <1330409051-8941-1-git-send-email-shangw@linux.vnet.ibm.com>
With original EEH implementation, struct pci_dn is used while building
PCI I/O address cache, which helps on searching the corresponding
PCI device according to the given physical I/O address. Besides, pci_dn
is associated with the corresponding PCI device while building its
I/O cache.
The patch replaces struct pci_dn with struct eeh_dev so that EEH address
cache won't depend on struct pci_dn. That will help EEH to become an
independent module in future. Besides, the binding of eeh_dev and PCI
device is done while building PCI device I/O cache.
Signed-off-by: Gavin Shan <shangw@linux.vnet.ibm.com>
---
arch/powerpc/platforms/pseries/eeh_cache.c | 27 ++++++++++++++++++++-------
1 files changed, 20 insertions(+), 7 deletions(-)
diff --git a/arch/powerpc/platforms/pseries/eeh_cache.c b/arch/powerpc/platforms/pseries/eeh_cache.c
index 7c36a9c..e5ae1c6 100644
--- a/arch/powerpc/platforms/pseries/eeh_cache.c
+++ b/arch/powerpc/platforms/pseries/eeh_cache.c
@@ -175,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);
@@ -184,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
- printk(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;
}
@@ -281,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);
@@ -291,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);
}
--
1.7.5.4
^ permalink raw reply related
* [PATCH 20/21] Introduce struct eeh_stats for EEH
From: Gavin Shan @ 2012-02-28 6:04 UTC (permalink / raw)
To: linuxppc-dev; +Cc: shangw
In-Reply-To: <1330409051-8941-1-git-send-email-shangw@linux.vnet.ibm.com>
With the original EEH implementation, the EEH global statistics
are maintained by individual global variables. That makes the
code a little hard to maintain.
The patch introduces extra struct eeh_stats for the EEH global
statistics so that it can be maintained in collective fashion.
Signed-off-by: Gavin Shan <shangw@linux.vnet.ibm.com>
---
arch/powerpc/platforms/pseries/eeh.c | 65 ++++++++++++++++++++--------------
1 files changed, 38 insertions(+), 27 deletions(-)
diff --git a/arch/powerpc/platforms/pseries/eeh.c b/arch/powerpc/platforms/pseries/eeh.c
index 9b1fd0c..ca05890 100644
--- a/arch/powerpc/platforms/pseries/eeh.c
+++ b/arch/powerpc/platforms/pseries/eeh.c
@@ -102,14 +102,22 @@ static DEFINE_RAW_SPINLOCK(confirm_error_lock);
#define EEH_PCI_REGS_LOG_LEN 4096
static unsigned char pci_regs_buf[EEH_PCI_REGS_LOG_LEN];
-/* System monitoring statistics */
-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 slot_resets;
+/*
+ * The struct is used to maintain the EEH global statistic
+ * information. Besides, the EEH global statistics will be
+ * exported to user space through procfs
+ */
+struct eeh_stats {
+ unsigned int no_device; /* PCI device not found */
+ unsigned int no_dn; /* OF node not found */
+ unsigned int no_cfg_addr; /* Config address not found */
+ unsigned int ignored_check; /* EEH check skipped */
+ unsigned int total_mmio_ffs; /* Total EEH checks */
+ unsigned int false_positives; /* Unnecessary EEH checks */
+ unsigned int slot_resets; /* PE reset */
+};
+
+static struct eeh_stats eeh_stats;
#define IS_BRIDGE(class_code) (((class_code)<<16) == PCI_BASE_CLASS_BRIDGE)
@@ -392,13 +400,13 @@ int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev)
int rc = 0;
const char *location;
- total_mmio_ffs++;
+ eeh_stats.total_mmio_ffs++;
if (!eeh_subsystem_enabled)
return 0;
if (!dn) {
- no_dn++;
+ eeh_stats.no_dn++;
return 0;
}
dn = eeh_find_device_pe(dn);
@@ -407,14 +415,14 @@ 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 (!(edev->mode & EEH_MODE_SUPPORTED) ||
edev->mode & EEH_MODE_NOCHECK) {
- ignored_check++;
+ eeh_stats.ignored_check++;
pr_debug("EEH: Ignored check (%x) for %s %s\n",
edev->mode, eeh_pci_name(dev), dn->full_name);
return 0;
}
if (!edev->config_addr && !edev->pe_config_addr) {
- no_cfg_addr++;
+ eeh_stats.no_cfg_addr++;
return 0;
}
@@ -460,13 +468,13 @@ int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev)
(ret == EEH_STATE_NOT_SUPPORT) ||
(ret & (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE)) ==
(EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE)) {
- false_positives++;
+ eeh_stats.false_positives++;
edev->false_positives ++;
rc = 0;
goto dn_unlock;
}
- slot_resets++;
+ eeh_stats.slot_resets++;
/* Avoid repeated reports of this failure, including problems
* with other functions on this device, and functions under
@@ -513,7 +521,7 @@ unsigned long eeh_check_failure(const volatile void __iomem *token, unsigned lon
addr = eeh_token_to_phys((unsigned long __force) token);
dev = pci_addr_cache_get_device(addr);
if (!dev) {
- no_device++;
+ eeh_stats.no_device++;
return val;
}
@@ -1174,21 +1182,24 @@ static int proc_eeh_show(struct seq_file *m, void *v)
{
if (0 == eeh_subsystem_enabled) {
seq_printf(m, "EEH Subsystem is globally disabled\n");
- seq_printf(m, "eeh_total_mmio_ffs=%ld\n", total_mmio_ffs);
+ seq_printf(m, "eeh_total_mmio_ffs=%d\n", eeh_stats.total_mmio_ffs);
} else {
seq_printf(m, "EEH Subsystem is enabled\n");
seq_printf(m,
- "no device=%ld\n"
- "no device node=%ld\n"
- "no config address=%ld\n"
- "check not wanted=%ld\n"
- "eeh_total_mmio_ffs=%ld\n"
- "eeh_false_positives=%ld\n"
- "eeh_slot_resets=%ld\n",
- no_device, no_dn, no_cfg_addr,
- ignored_check, total_mmio_ffs,
- false_positives,
- slot_resets);
+ "no device =%d\n"
+ "no device node =%d\n"
+ "no config address =%d\n"
+ "check not wanted =%d\n"
+ "eeh_total_mmio_ffs =%d\n"
+ "eeh_false_positives =%d\n"
+ "eeh_slot_resets =%d\n",
+ eeh_stats.no_device,
+ eeh_stats.no_dn,
+ eeh_stats.no_cfg_addr,
+ eeh_stats.ignored_check,
+ eeh_stats.total_mmio_ffs,
+ eeh_stats.false_positives,
+ eeh_stats.slot_resets);
}
return 0;
--
1.7.5.4
^ permalink raw reply related
* [PATCH 11/21] pSeries platform EEH configure bridge
From: Gavin Shan @ 2012-02-28 6:04 UTC (permalink / raw)
To: linuxppc-dev; +Cc: shangw
In-Reply-To: <1330409051-8941-1-git-send-email-shangw@linux.vnet.ibm.com>
In order to enable particular PCI device, which has been included
in the parent PE. The involved PCI bridges should be enabled explicitly
if there has. On pSeries platform, there're dedicated RTAS calls
to fulfil the purpose.
The patch implements the function of configuring PCI bridges through
the dedicated RTAS calls. Besides, the function has been abstracted
by struct eeh_ops::configure_bridge so that the EEH core components
could support multiple platforms in future.
Signed-off-by: Gavin Shan <shangw@linux.vnet.ibm.com>
---
arch/powerpc/include/asm/ppc-pci.h | 1 -
arch/powerpc/platforms/pseries/eeh.c | 44 +-------------------------
arch/powerpc/platforms/pseries/eeh_driver.c | 2 +-
arch/powerpc/platforms/pseries/eeh_pseries.c | 29 ++++++++++++++++-
4 files changed, 30 insertions(+), 46 deletions(-)
diff --git a/arch/powerpc/include/asm/ppc-pci.h b/arch/powerpc/include/asm/ppc-pci.h
index bd1a84f..b4b18d8 100644
--- a/arch/powerpc/include/asm/ppc-pci.h
+++ b/arch/powerpc/include/asm/ppc-pci.h
@@ -57,7 +57,6 @@ void eeh_slot_error_detail (struct pci_dn *pdn, int severity);
int eeh_pci_enable(struct pci_dn *pdn, int function);
int eeh_reset_pe(struct pci_dn *);
void eeh_restore_bars(struct pci_dn *);
-void eeh_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);
void eeh_mark_slot(struct device_node *dn, int mode_flag);
diff --git a/arch/powerpc/platforms/pseries/eeh.c b/arch/powerpc/platforms/pseries/eeh.c
index 39fcecb..bd4ed83 100644
--- a/arch/powerpc/platforms/pseries/eeh.c
+++ b/arch/powerpc/platforms/pseries/eeh.c
@@ -86,10 +86,6 @@
/* Time to wait for a PCI slot to report status, in milliseconds */
#define PCI_BUS_RESET_WAIT_MSEC (60*1000)
-/* RTAS tokens */
-static int ibm_configure_bridge;
-static int ibm_configure_pe;
-
/* Platform dependent EEH operations */
struct eeh_ops *eeh_ops = NULL;
@@ -229,7 +225,7 @@ void eeh_slot_error_detail(struct pci_dn *pdn, int severity)
pci_regs_buf[0] = 0;
eeh_pci_enable(pdn, EEH_OPT_THAW_MMIO);
- eeh_configure_bridge(pdn);
+ eeh_ops->configure_bridge(pdn->node);
eeh_restore_bars(pdn);
loglen = eeh_gather_pci_data(pdn, pci_regs_buf, EEH_PCI_REGS_LOG_LEN);
@@ -810,41 +806,6 @@ static void eeh_save_bars(struct pci_dn *pdn)
}
/**
- * eeh_configure_bridge - Configure PCI bridges for the indicated PE
- * @pdn: PCI device node
- *
- * PCI bridges might be included in PE. In order to make the PE work
- * again. The included PCI bridges should be recovered after the PE
- * encounters frozen state.
- */
-void eeh_configure_bridge(struct pci_dn *pdn)
-{
- int config_addr;
- int rc;
- int token;
-
- /* 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;
-
- /* Use new configure-pe function, if supported */
- if (ibm_configure_pe != RTAS_UNKNOWN_SERVICE)
- token = ibm_configure_pe;
- else
- token = ibm_configure_bridge;
-
- rc = rtas_call(token, 3, 1, NULL,
- config_addr,
- BUID_HI(pdn->phb->buid),
- BUID_LO(pdn->phb->buid));
- if (rc) {
- printk(KERN_WARNING "EEH: Unable to configure device bridge (%d) for %s\n",
- rc, pdn->node->full_name);
- }
-}
-
-/**
* eeh_early_enable - Early enable EEH on the indicated device
* @dn: device node
* @data: BUID
@@ -1027,9 +988,6 @@ void __init eeh_init(void)
if (np == NULL)
return;
- ibm_configure_bridge = rtas_token("ibm,configure-bridge");
- ibm_configure_pe = rtas_token("ibm,configure-pe");
-
/* Enable EEH for all adapters. Note that eeh requires buid's */
for (phb = of_find_node_by_name(NULL, "pci"); phb;
phb = of_find_node_by_name(phb, "pci")) {
diff --git a/arch/powerpc/platforms/pseries/eeh_driver.c b/arch/powerpc/platforms/pseries/eeh_driver.c
index 6840357..61450e1 100644
--- a/arch/powerpc/platforms/pseries/eeh_driver.c
+++ b/arch/powerpc/platforms/pseries/eeh_driver.c
@@ -295,7 +295,7 @@ static int eeh_reset_device (struct pci_dn *pe_dn, struct pci_bus *bus)
struct pci_dn *ppe = PCI_DN(dn);
/* On Power4, always true because eeh_pe_config_addr=0 */
if (pe_dn->eeh_pe_config_addr == ppe->eeh_pe_config_addr) {
- eeh_configure_bridge(ppe);
+ eeh_ops->configure_bridge(dn);
eeh_restore_bars(ppe);
}
dn = dn->sibling;
diff --git a/arch/powerpc/platforms/pseries/eeh_pseries.c b/arch/powerpc/platforms/pseries/eeh_pseries.c
index 7c8434f..4ed06b2 100644
--- a/arch/powerpc/platforms/pseries/eeh_pseries.c
+++ b/arch/powerpc/platforms/pseries/eeh_pseries.c
@@ -473,7 +473,34 @@ static int pseries_eeh_get_log(struct device_node *dn, int severity, char *drv_l
*/
static int pseries_eeh_configure_bridge(struct device_node *dn)
{
- return 0;
+ struct pci_dn *pdn;
+ int config_addr;
+ int ret;
+
+ /* Figure out the PE address */
+ pdn = PCI_DN(dn);
+ config_addr = pdn->eeh_config_addr;
+ if (pdn->eeh_pe_config_addr)
+ config_addr = pdn->eeh_pe_config_addr;
+
+ /* Use new configure-pe function, if supported */
+ if (ibm_configure_pe != RTAS_UNKNOWN_SERVICE) {
+ ret = rtas_call(ibm_configure_pe, 3, 1, NULL,
+ config_addr, BUID_HI(pdn->phb->buid),
+ BUID_LO(pdn->phb->buid));
+ } else if (ibm_configure_bridge != RTAS_UNKNOWN_SERVICE) {
+ ret = rtas_call(ibm_configure_bridge, 3, 1, NULL,
+ config_addr, BUID_HI(pdn->phb->buid),
+ BUID_LO(pdn->phb->buid));
+ } else {
+ return -EFAULT;
+ }
+
+ if (ret)
+ pr_warning("%s: Unable to configure bridge %d for %s\n",
+ __func__, ret, dn->full_name);
+
+ return ret;
}
static struct eeh_ops pseries_eeh_ops = {
--
1.7.5.4
^ permalink raw reply related
* [PATCH 19/21] Replace pci_dn with eeh_dev for EEH on pSeries
From: Gavin Shan @ 2012-02-28 6:04 UTC (permalink / raw)
To: linuxppc-dev; +Cc: shangw
In-Reply-To: <1330409051-8941-1-git-send-email-shangw@linux.vnet.ibm.com>
The pci_dn has been replaced with eeh_dev. In order to comply with
the rule, the EEH platform implementation on pSeries should also
be adjusted for a little bit so that it will depend on eeh_dev instead
of pci_dn.
The patch replaces pci_dn with eeh_dev. The corresponding information
will be retrieved from eeh_dev instead of pci_dn.
Signed-off-by: Gavin Shan <shangw@linux.vnet.ibm.com>
---
arch/powerpc/platforms/pseries/eeh_pseries.c | 96 +++++++++++++-------------
1 files changed, 48 insertions(+), 48 deletions(-)
diff --git a/arch/powerpc/platforms/pseries/eeh_pseries.c b/arch/powerpc/platforms/pseries/eeh_pseries.c
index 4ed06b2..36a1af1 100644
--- a/arch/powerpc/platforms/pseries/eeh_pseries.c
+++ b/arch/powerpc/platforms/pseries/eeh_pseries.c
@@ -144,11 +144,11 @@ static int pseries_eeh_init(void)
static int pseries_eeh_set_option(struct device_node *dn, int option)
{
int ret = 0;
- struct pci_dn *pdn;
+ struct eeh_dev *edev;
const u32 *reg;
int config_addr;
- pdn = PCI_DN(dn);
+ edev = of_node_to_eeh_dev(dn);
/*
* When we're enabling or disabling EEH functioality on
@@ -165,9 +165,9 @@ static int pseries_eeh_set_option(struct device_node *dn, int option)
case EEH_OPT_THAW_MMIO:
case EEH_OPT_THAW_DMA:
- config_addr = pdn->eeh_config_addr;
- if (pdn->eeh_pe_config_addr)
- config_addr = pdn->eeh_pe_config_addr;
+ config_addr = edev->config_addr;
+ if (edev->pe_config_addr)
+ config_addr = edev->pe_config_addr;
break;
default:
@@ -177,8 +177,8 @@ static int pseries_eeh_set_option(struct device_node *dn, int option)
}
ret = rtas_call(ibm_set_eeh_option, 4, 1, NULL,
- config_addr, BUID_HI(pdn->phb->buid),
- BUID_LO(pdn->phb->buid), option);
+ config_addr, BUID_HI(edev->phb->buid),
+ BUID_LO(edev->phb->buid), option);
return ret;
}
@@ -198,11 +198,11 @@ static int pseries_eeh_set_option(struct device_node *dn, int option)
*/
static int pseries_eeh_get_pe_addr(struct device_node *dn)
{
- struct pci_dn *pdn;
+ struct eeh_dev *edev;
int ret = 0;
int rets[3];
- pdn = PCI_DN(dn);
+ edev = of_node_to_eeh_dev(dn);
if (ibm_get_config_addr_info2 != RTAS_UNKNOWN_SERVICE) {
/*
@@ -211,15 +211,15 @@ static int pseries_eeh_get_pe_addr(struct device_node *dn)
* meaningless.
*/
ret = rtas_call(ibm_get_config_addr_info2, 4, 2, rets,
- pdn->eeh_config_addr, BUID_HI(pdn->phb->buid),
- BUID_LO(pdn->phb->buid), 1);
+ edev->config_addr, BUID_HI(edev->phb->buid),
+ BUID_LO(edev->phb->buid), 1);
if (ret || (rets[0] == 0))
return 0;
/* Retrieve the associated PE config address */
ret = rtas_call(ibm_get_config_addr_info2, 4, 2, rets,
- pdn->eeh_config_addr, BUID_HI(pdn->phb->buid),
- BUID_LO(pdn->phb->buid), 0);
+ edev->config_addr, BUID_HI(edev->phb->buid),
+ BUID_LO(edev->phb->buid), 0);
if (ret) {
pr_warning("%s: Failed to get PE address for %s\n",
__func__, dn->full_name);
@@ -231,8 +231,8 @@ static int pseries_eeh_get_pe_addr(struct device_node *dn)
if (ibm_get_config_addr_info != RTAS_UNKNOWN_SERVICE) {
ret = rtas_call(ibm_get_config_addr_info, 4, 2, rets,
- pdn->eeh_config_addr, BUID_HI(pdn->phb->buid),
- BUID_LO(pdn->phb->buid), 0);
+ edev->config_addr, BUID_HI(edev->phb->buid),
+ BUID_LO(edev->phb->buid), 0);
if (ret) {
pr_warning("%s: Failed to get PE address for %s\n",
__func__, dn->full_name);
@@ -260,28 +260,28 @@ static int pseries_eeh_get_pe_addr(struct device_node *dn)
*/
static int pseries_eeh_get_state(struct device_node *dn, int *state)
{
- struct pci_dn *pdn;
+ struct eeh_dev *edev;
int config_addr;
int ret;
int rets[4];
int result;
/* Figure out PE config address if possible */
- pdn = PCI_DN(dn);
- config_addr = pdn->eeh_config_addr;
- if (pdn->eeh_pe_config_addr)
- config_addr = pdn->eeh_pe_config_addr;
+ edev = of_node_to_eeh_dev(dn);
+ config_addr = edev->config_addr;
+ if (edev->pe_config_addr)
+ config_addr = edev->pe_config_addr;
if (ibm_read_slot_reset_state2 != RTAS_UNKNOWN_SERVICE) {
ret = rtas_call(ibm_read_slot_reset_state2, 3, 4, rets,
- config_addr, BUID_HI(pdn->phb->buid),
- BUID_LO(pdn->phb->buid));
+ config_addr, BUID_HI(edev->phb->buid),
+ BUID_LO(edev->phb->buid));
} else if (ibm_read_slot_reset_state != RTAS_UNKNOWN_SERVICE) {
/* Fake PE unavailable info */
rets[2] = 0;
ret = rtas_call(ibm_read_slot_reset_state, 3, 3, rets,
- config_addr, BUID_HI(pdn->phb->buid),
- BUID_LO(pdn->phb->buid));
+ config_addr, BUID_HI(edev->phb->buid),
+ BUID_LO(edev->phb->buid));
} else {
return EEH_STATE_NOT_SUPPORT;
}
@@ -340,27 +340,27 @@ static int pseries_eeh_get_state(struct device_node *dn, int *state)
*/
static int pseries_eeh_reset(struct device_node *dn, int option)
{
- struct pci_dn *pdn;
+ struct eeh_dev *edev;
int config_addr;
int ret;
/* Figure out PE address */
- pdn = PCI_DN(dn);
- config_addr = pdn->eeh_config_addr;
- if (pdn->eeh_pe_config_addr)
- config_addr = pdn->eeh_pe_config_addr;
+ edev = of_node_to_eeh_dev(dn);
+ config_addr = edev->config_addr;
+ if (edev->pe_config_addr)
+ config_addr = edev->pe_config_addr;
/* Reset PE through RTAS call */
ret = rtas_call(ibm_set_slot_reset, 4, 1, NULL,
- config_addr, BUID_HI(pdn->phb->buid),
- BUID_LO(pdn->phb->buid), option);
+ config_addr, BUID_HI(edev->phb->buid),
+ BUID_LO(edev->phb->buid), option);
/* If fundamental-reset not supported, try hot-reset */
if (option == EEH_RESET_FUNDAMENTAL &&
ret == -8) {
ret = rtas_call(ibm_set_slot_reset, 4, 1, NULL,
- config_addr, BUID_HI(pdn->phb->buid),
- BUID_LO(pdn->phb->buid), EEH_RESET_HOT);
+ config_addr, BUID_HI(edev->phb->buid),
+ BUID_LO(edev->phb->buid), EEH_RESET_HOT);
}
return ret;
@@ -437,22 +437,22 @@ static int pseries_eeh_wait_state(struct device_node *dn, int max_wait)
*/
static int pseries_eeh_get_log(struct device_node *dn, int severity, char *drv_log, unsigned long len)
{
- struct pci_dn *pdn;
+ struct eeh_dev *edev;
int config_addr;
unsigned long flags;
int ret;
- pdn = PCI_DN(dn);
+ edev = of_node_to_eeh_dev(dn);
spin_lock_irqsave(&slot_errbuf_lock, flags);
memset(slot_errbuf, 0, eeh_error_buf_size);
/* Figure out the PE address */
- config_addr = pdn->eeh_config_addr;
- if (pdn->eeh_pe_config_addr)
- config_addr = pdn->eeh_pe_config_addr;
+ config_addr = edev->config_addr;
+ if (edev->pe_config_addr)
+ config_addr = edev->pe_config_addr;
ret = rtas_call(ibm_slot_error_detail, 8, 1, NULL, config_addr,
- BUID_HI(pdn->phb->buid), BUID_LO(pdn->phb->buid),
+ BUID_HI(edev->phb->buid), BUID_LO(edev->phb->buid),
virt_to_phys(drv_log), len,
virt_to_phys(slot_errbuf), eeh_error_buf_size,
severity);
@@ -473,25 +473,25 @@ static int pseries_eeh_get_log(struct device_node *dn, int severity, char *drv_l
*/
static int pseries_eeh_configure_bridge(struct device_node *dn)
{
- struct pci_dn *pdn;
+ struct eeh_dev *edev;
int config_addr;
int ret;
/* Figure out the PE address */
- pdn = PCI_DN(dn);
- config_addr = pdn->eeh_config_addr;
- if (pdn->eeh_pe_config_addr)
- config_addr = pdn->eeh_pe_config_addr;
+ edev = of_node_to_eeh_dev(dn);
+ config_addr = edev->config_addr;
+ if (edev->pe_config_addr)
+ config_addr = edev->pe_config_addr;
/* Use new configure-pe function, if supported */
if (ibm_configure_pe != RTAS_UNKNOWN_SERVICE) {
ret = rtas_call(ibm_configure_pe, 3, 1, NULL,
- config_addr, BUID_HI(pdn->phb->buid),
- BUID_LO(pdn->phb->buid));
+ config_addr, BUID_HI(edev->phb->buid),
+ BUID_LO(edev->phb->buid));
} else if (ibm_configure_bridge != RTAS_UNKNOWN_SERVICE) {
ret = rtas_call(ibm_configure_bridge, 3, 1, NULL,
- config_addr, BUID_HI(pdn->phb->buid),
- BUID_LO(pdn->phb->buid));
+ config_addr, BUID_HI(edev->phb->buid),
+ BUID_LO(edev->phb->buid));
} else {
return -EFAULT;
}
--
1.7.5.4
^ permalink raw reply related
* [PATCH 14/21] Introduce EEH device
From: Gavin Shan @ 2012-02-28 6:04 UTC (permalink / raw)
To: linuxppc-dev; +Cc: shangw
In-Reply-To: <1330409051-8941-1-git-send-email-shangw@linux.vnet.ibm.com>
Original EEH implementation depends on struct pci_dn heavily. However,
EEH shouldn't depend on that actually because EEH needn't share much
information with other PCI components. That's to say, EEH should have
worked independently.
The patch introduces struct eeh_dev so that EEH core components needn't
be working based on struct pci_dn in future. Also, struct pci_dn, struct
eeh_dev instances are created in dynamic fasion and the binding with EEH
device, OF node, PCI device is implemented as well.
The EEH devices are created after PHBs are detected and initialized, but
PCI emunation hasn't started yet. Apart from that, PHB might be created
dynamically through DLPAR component and the EEH devices should be creatd
as well. Another case might be OF node is created dynamically by DR
(Dynamic Reconfiguration), which has been defined by PAPR. For those OF
nodes created by DR, EEH devices should be also created accordingly. The
binding between EEH device and OF node is done while the EEH device is
initially created.
The binding between EEH device and PCI device should be done after PCI
emunation is done. Besides, PCI hotplug also needs the binding so that
the EEH devices could be traced from the newly coming PCI buses or PCI
devices.
Signed-off-by: Gavin Shan <shangw@linux.vnet.ibm.com>
---
arch/powerpc/include/asm/device.h | 3 +
arch/powerpc/include/asm/eeh.h | 57 ++++++++++++++--
arch/powerpc/kernel/of_platform.c | 3 +
arch/powerpc/kernel/rtas_pci.c | 3 +
arch/powerpc/platforms/pseries/Makefile | 3 +-
arch/powerpc/platforms/pseries/eeh_dev.c | 102 ++++++++++++++++++++++++++++
arch/powerpc/platforms/pseries/pci_dlpar.c | 3 +
arch/powerpc/platforms/pseries/setup.c | 6 ++-
include/linux/of.h | 10 +++
include/linux/pci.h | 7 ++
10 files changed, 188 insertions(+), 9 deletions(-)
create mode 100644 arch/powerpc/platforms/pseries/eeh_dev.c
diff --git a/arch/powerpc/include/asm/device.h b/arch/powerpc/include/asm/device.h
index d57c08a..63d5ca4 100644
--- a/arch/powerpc/include/asm/device.h
+++ b/arch/powerpc/include/asm/device.h
@@ -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 {
diff --git a/arch/powerpc/include/asm/eeh.h b/arch/powerpc/include/asm/eeh.h
index ad8f318..daaad91 100644
--- a/arch/powerpc/include/asm/eeh.h
+++ b/arch/powerpc/include/asm/eeh.h
@@ -32,6 +32,43 @@ struct device_node;
#ifdef CONFIG_EEH
/*
+ * 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;
+}
+
+/*
* The struct is used to trace the registered EEH operation
* callback functions. Actually, those operation callback
* functions are heavily platform dependent. That means the
@@ -70,19 +107,15 @@ struct eeh_ops {
extern struct eeh_ops *eeh_ops;
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_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.
*/
#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);
@@ -113,6 +146,16 @@ 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
diff --git a/arch/powerpc/kernel/of_platform.c b/arch/powerpc/kernel/of_platform.c
index e1612df..9239c3a 100644
--- a/arch/powerpc/kernel/of_platform.c
+++ b/arch/powerpc/kernel/of_platform.c
@@ -66,6 +66,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)
diff --git a/arch/powerpc/kernel/rtas_pci.c b/arch/powerpc/kernel/rtas_pci.c
index 6cd8f01..517bd86 100644
--- a/arch/powerpc/kernel/rtas_pci.c
+++ b/arch/powerpc/kernel/rtas_pci.c
@@ -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.
diff --git a/arch/powerpc/platforms/pseries/Makefile b/arch/powerpc/platforms/pseries/Makefile
index 9aa5581..12dae0b 100644
--- a/arch/powerpc/platforms/pseries/Makefile
+++ b/arch/powerpc/platforms/pseries/Makefile
@@ -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 eeh_pseries.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
diff --git a/arch/powerpc/platforms/pseries/eeh_dev.c b/arch/powerpc/platforms/pseries/eeh_dev.c
new file mode 100644
index 0000000..f3aed7d
--- /dev/null
+++ b/arch/powerpc/platforms/pseries/eeh_dev.c
@@ -0,0 +1,102 @@
+/*
+ * 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);
+}
diff --git a/arch/powerpc/platforms/pseries/pci_dlpar.c b/arch/powerpc/platforms/pseries/pci_dlpar.c
index 55d4ec1..fbb21fc 100644
--- a/arch/powerpc/platforms/pseries/pci_dlpar.c
+++ b/arch/powerpc/platforms/pseries/pci_dlpar.c
@@ -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);
diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c
index 809d9d9..60f9462 100644
--- a/arch/powerpc/platforms/pseries/setup.c
+++ b/arch/powerpc/platforms/pseries/setup.c
@@ -261,8 +261,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;
diff --git a/include/linux/of.h b/include/linux/of.h
index a75a831..3e710d8 100644
--- a/include/linux/of.h
+++ b/include/linux/of.h
@@ -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)
diff --git a/include/linux/pci.h b/include/linux/pci.h
index a16b1df..cfeee2a 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -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
--
1.7.5.4
^ permalink raw reply related
* [PATCH 21/21] pSeries platform config space access in EEH
From: Gavin Shan @ 2012-02-28 6:04 UTC (permalink / raw)
To: linuxppc-dev; +Cc: shangw
In-Reply-To: <1330409051-8941-1-git-send-email-shangw@linux.vnet.ibm.com>
With the original EEH implementation, the access to config space of
the corresponding PCI device is done by RTAS sensitive function. That
depends on pci_dn heavily. That would limit EEH extension to other
platforms like powernv because other platforms might have different
ways to access PCI config space.
The patch splits those functions used to access PCI config space
and implement them in platform related EEH component. It would be
helpful to support EEH on multiple platforms simutaneously in future.
Signed-off-by: Gavin Shan <shangw@linux.vnet.ibm.com>
---
arch/powerpc/include/asm/eeh.h | 2 +
arch/powerpc/platforms/pseries/eeh.c | 32 ++++++++++----------
arch/powerpc/platforms/pseries/eeh_pseries.c | 40 +++++++++++++++++++++++++-
3 files changed, 57 insertions(+), 17 deletions(-)
diff --git a/arch/powerpc/include/asm/eeh.h b/arch/powerpc/include/asm/eeh.h
index daaad91..d60f998 100644
--- a/arch/powerpc/include/asm/eeh.h
+++ b/arch/powerpc/include/asm/eeh.h
@@ -102,6 +102,8 @@ struct eeh_ops {
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;
diff --git a/arch/powerpc/platforms/pseries/eeh.c b/arch/powerpc/platforms/pseries/eeh.c
index ca05890..d653fc2 100644
--- a/arch/powerpc/platforms/pseries/eeh.c
+++ b/arch/powerpc/platforms/pseries/eeh.c
@@ -141,11 +141,11 @@ static size_t eeh_gather_pci_data(struct eeh_dev *edev, char * buf, size_t len)
n += scnprintf(buf+n, len-n, "%s\n", dn->full_name);
printk(KERN_WARNING "EEH: of node=%s\n", dn->full_name);
- rtas_read_config(PCI_DN(dn), PCI_VENDOR_ID, 4, &cfg);
+ eeh_ops->read_config(dn, PCI_VENDOR_ID, 4, &cfg);
n += scnprintf(buf+n, len-n, "dev/vend:%08x\n", cfg);
printk(KERN_WARNING "EEH: PCI device/vendor: %08x\n", cfg);
- rtas_read_config(PCI_DN(dn), PCI_COMMAND, 4, &cfg);
+ eeh_ops->read_config(dn, PCI_COMMAND, 4, &cfg);
n += scnprintf(buf+n, len-n, "cmd/stat:%x\n", cfg);
printk(KERN_WARNING "EEH: PCI cmd/status register: %08x\n", cfg);
@@ -156,11 +156,11 @@ static size_t eeh_gather_pci_data(struct eeh_dev *edev, char * buf, size_t len)
/* Gather bridge-specific registers */
if (dev->class >> 16 == PCI_BASE_CLASS_BRIDGE) {
- rtas_read_config(PCI_DN(dn), PCI_SEC_STATUS, 2, &cfg);
+ eeh_ops->read_config(dn, PCI_SEC_STATUS, 2, &cfg);
n += scnprintf(buf+n, len-n, "sec stat:%x\n", cfg);
printk(KERN_WARNING "EEH: Bridge secondary status: %04x\n", cfg);
- rtas_read_config(PCI_DN(dn), PCI_BRIDGE_CONTROL, 2, &cfg);
+ eeh_ops->read_config(dn, PCI_BRIDGE_CONTROL, 2, &cfg);
n += scnprintf(buf+n, len-n, "brdg ctl:%x\n", cfg);
printk(KERN_WARNING "EEH: Bridge control: %04x\n", cfg);
}
@@ -168,11 +168,11 @@ static size_t eeh_gather_pci_data(struct eeh_dev *edev, char * buf, size_t len)
/* Dump out the PCI-X command and status regs */
cap = pci_find_capability(dev, PCI_CAP_ID_PCIX);
if (cap) {
- rtas_read_config(PCI_DN(dn), cap, 4, &cfg);
+ eeh_ops->read_config(dn, cap, 4, &cfg);
n += scnprintf(buf+n, len-n, "pcix-cmd:%x\n", cfg);
printk(KERN_WARNING "EEH: PCI-X cmd: %08x\n", cfg);
- rtas_read_config(PCI_DN(dn), cap+4, 4, &cfg);
+ eeh_ops->read_config(dn, cap+4, 4, &cfg);
n += scnprintf(buf+n, len-n, "pcix-stat:%x\n", cfg);
printk(KERN_WARNING "EEH: PCI-X status: %08x\n", cfg);
}
@@ -185,7 +185,7 @@ static size_t eeh_gather_pci_data(struct eeh_dev *edev, char * buf, size_t len)
"EEH: PCI-E capabilities and status follow:\n");
for (i=0; i<=8; i++) {
- rtas_read_config(PCI_DN(dn), cap+4*i, 4, &cfg);
+ eeh_ops->read_config(dn, cap+4*i, 4, &cfg);
n += scnprintf(buf+n, len-n, "%02x:%x\n", 4*i, cfg);
printk(KERN_WARNING "EEH: PCI-E %02x: %08x\n", i, cfg);
}
@@ -197,7 +197,7 @@ static size_t eeh_gather_pci_data(struct eeh_dev *edev, char * buf, size_t len)
"EEH: PCI-E AER capability register set follows:\n");
for (i=0; i<14; i++) {
- rtas_read_config(PCI_DN(dn), cap+4*i, 4, &cfg);
+ eeh_ops->read_config(dn, cap+4*i, 4, &cfg);
n += scnprintf(buf+n, len-n, "%02x:%x\n", 4*i, cfg);
printk(KERN_WARNING "EEH: PCI-E AER %02x: %08x\n", i, cfg);
}
@@ -746,28 +746,28 @@ static inline void eeh_restore_one_device_bars(struct eeh_dev *edev)
return;
for (i=4; i<10; i++) {
- rtas_write_config(PCI_DN(dn), i*4, 4, edev->config_space[i]);
+ eeh_ops->write_config(dn, i*4, 4, edev->config_space[i]);
}
/* 12 == Expansion ROM Address */
- rtas_write_config(PCI_DN(dn), 12*4, 4, edev->config_space[12]);
+ eeh_ops->write_config(dn, 12*4, 4, edev->config_space[12]);
#define BYTE_SWAP(OFF) (8*((OFF)/4)+3-(OFF))
#define SAVED_BYTE(OFF) (((u8 *)(edev->config_space))[BYTE_SWAP(OFF)])
- rtas_write_config(PCI_DN(dn), PCI_CACHE_LINE_SIZE, 1,
+ eeh_ops->write_config(dn, PCI_CACHE_LINE_SIZE, 1,
SAVED_BYTE(PCI_CACHE_LINE_SIZE));
- rtas_write_config(PCI_DN(dn), PCI_LATENCY_TIMER, 1,
+ eeh_ops->write_config(dn, PCI_LATENCY_TIMER, 1,
SAVED_BYTE(PCI_LATENCY_TIMER));
/* max latency, min grant, interrupt pin and line */
- rtas_write_config(PCI_DN(dn), 15*4, 4, edev->config_space[15]);
+ eeh_ops->write_config(dn, 15*4, 4, edev->config_space[15]);
/* Restore PERR & SERR bits, some devices require it,
* don't touch the other command bits
*/
- rtas_read_config(PCI_DN(dn), PCI_COMMAND, 4, &cmd);
+ eeh_ops->read_config(dn, PCI_COMMAND, 4, &cmd);
if (edev->config_space[1] & PCI_COMMAND_PARITY)
cmd |= PCI_COMMAND_PARITY;
else
@@ -776,7 +776,7 @@ static inline void eeh_restore_one_device_bars(struct eeh_dev *edev)
cmd |= PCI_COMMAND_SERR;
else
cmd &= ~PCI_COMMAND_SERR;
- rtas_write_config(PCI_DN(dn), PCI_COMMAND, 4, cmd);
+ eeh_ops->write_config(dn, PCI_COMMAND, 4, cmd);
}
/**
@@ -818,7 +818,7 @@ static void eeh_save_bars(struct eeh_dev *edev)
dn = eeh_dev_to_of_node(edev);
for (i = 0; i < 16; i++)
- rtas_read_config(PCI_DN(dn), i * 4, 4, &edev->config_space[i]);
+ eeh_ops->read_config(dn, i * 4, 4, &edev->config_space[i]);
}
/**
diff --git a/arch/powerpc/platforms/pseries/eeh_pseries.c b/arch/powerpc/platforms/pseries/eeh_pseries.c
index 36a1af1..8752f79 100644
--- a/arch/powerpc/platforms/pseries/eeh_pseries.c
+++ b/arch/powerpc/platforms/pseries/eeh_pseries.c
@@ -503,6 +503,42 @@ static int pseries_eeh_configure_bridge(struct device_node *dn)
return ret;
}
+/**
+ * pseries_eeh_read_config - Read PCI config space
+ * @dn: device node
+ * @where: PCI address
+ * @size: size to read
+ * @val: return value
+ *
+ * Read config space from the speicifed device
+ */
+static int pseries_eeh_read_config(struct device_node *dn, int where, int size, u32 *val)
+{
+ struct pci_dn *pdn;
+
+ pdn = PCI_DN(dn);
+
+ return rtas_read_config(pdn, where, size, val);
+}
+
+/**
+ * pseries_eeh_write_config - Write PCI config space
+ * @dn: device node
+ * @where: PCI address
+ * @size: size to write
+ * @val: value to be written
+ *
+ * Write config space to the specified device
+ */
+static int pseries_eeh_write_config(struct device_node *dn, int where, int size, u32 val)
+{
+ struct pci_dn *pdn;
+
+ pdn = PCI_DN(dn);
+
+ return rtas_write_config(pdn, where, size, val);
+}
+
static struct eeh_ops pseries_eeh_ops = {
.name = "pseries",
.init = pseries_eeh_init,
@@ -512,7 +548,9 @@ static struct eeh_ops pseries_eeh_ops = {
.reset = pseries_eeh_reset,
.wait_state = pseries_eeh_wait_state,
.get_log = pseries_eeh_get_log,
- .configure_bridge = pseries_eeh_configure_bridge
+ .configure_bridge = pseries_eeh_configure_bridge,
+ .read_config = pseries_eeh_read_config,
+ .write_config = pseries_eeh_write_config
};
/**
--
1.7.5.4
^ permalink raw reply related
* [PATCH 03/21] Platform dependent EEH operations
From: Gavin Shan @ 2012-02-28 6:03 UTC (permalink / raw)
To: linuxppc-dev; +Cc: shangw
In-Reply-To: <1330409051-8941-1-git-send-email-shangw@linux.vnet.ibm.com>
EEH has been implemented on RTAS-compliant pSeries platform.
That's to say, the EEH operations will be implemented through RTAS
calls eventually. The situation limited feasible extension on EEH.
In order to support EEH on multiple platforms like pseries and powernv
simutaneously. We have to split the platform dependent EEH options
up out of current implementation.
The patch addresses supporting EEH on multiple platforms. The pseries
platform dependent EEH operations will be abstracted by struct eeh_ops.
EEH core components will be built based on the registered EEH operations.
With the mechanism, what the individual platform needs to do is implement
platform dependent EEH operations.
For now, the pseries platform is covered under the mechanism. That means
we have to think about other platforms to support EEH, like powernv.
Besides, we only have framework for the mechanism and we have to implement
it for pseries platform later.
Signed-off-by: Gavin Shan <shangw@linux.vnet.ibm.com>
---
arch/powerpc/include/asm/eeh.h | 32 +++++
arch/powerpc/platforms/pseries/Makefile | 2 +-
arch/powerpc/platforms/pseries/eeh.c | 53 ++++++++
arch/powerpc/platforms/pseries/eeh_pseries.c | 183 ++++++++++++++++++++++++++
arch/powerpc/platforms/pseries/setup.c | 1 +
5 files changed, 270 insertions(+), 1 deletions(-)
create mode 100644 arch/powerpc/platforms/pseries/eeh_pseries.c
diff --git a/arch/powerpc/include/asm/eeh.h b/arch/powerpc/include/asm/eeh.h
index 2328877..0666c52 100644
--- a/arch/powerpc/include/asm/eeh.h
+++ b/arch/powerpc/include/asm/eeh.h
@@ -31,6 +31,26 @@ struct device_node;
#ifdef CONFIG_EEH
+/*
+ * 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.
+ */
+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);
+};
+
+extern struct eeh_ops *eeh_ops;
extern int eeh_subsystem_enabled;
/* Values for eeh_mode bits in device_node */
@@ -47,6 +67,11 @@ extern int eeh_subsystem_enabled;
#define EEH_MAX_ALLOWED_FREEZES 5
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);
@@ -73,6 +98,13 @@ void eeh_remove_bus_device(struct pci_dev *);
#else /* !CONFIG_EEH */
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;
diff --git a/arch/powerpc/platforms/pseries/Makefile b/arch/powerpc/platforms/pseries/Makefile
index 236db46..9aa5581 100644
--- a/arch/powerpc/platforms/pseries/Makefile
+++ b/arch/powerpc/platforms/pseries/Makefile
@@ -6,7 +6,7 @@ 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_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
diff --git a/arch/powerpc/platforms/pseries/eeh.c b/arch/powerpc/platforms/pseries/eeh.c
index fa88589..b0e3fb0 100644
--- a/arch/powerpc/platforms/pseries/eeh.c
+++ b/arch/powerpc/platforms/pseries/eeh.c
@@ -97,6 +97,9 @@ static int ibm_get_config_addr_info2;
static int ibm_configure_bridge;
static int ibm_configure_pe;
+/* Platform dependent EEH operations */
+struct eeh_ops *eeh_ops = NULL;
+
int eeh_subsystem_enabled;
EXPORT_SYMBOL(eeh_subsystem_enabled);
@@ -1208,6 +1211,56 @@ static void *eeh_early_enable(struct device_node *dn, void *data)
}
/**
+ * eeh_ops_register - Register platform dependent EEH operations
+ * @ops: platform dependent EEH operations
+ *
+ * Register the platform dependent EEH operation callback
+ * functions. The platform should call this function before
+ * any other EEH operations.
+ */
+int __init eeh_ops_register(struct eeh_ops *ops)
+{
+ if (!ops->name) {
+ pr_warning("%s: Invalid EEH ops name for %p\n",
+ __func__, ops);
+ return -EINVAL;
+ }
+
+ if (eeh_ops && eeh_ops != ops) {
+ pr_warning("%s: EEH ops of platform %s already existing (%s)\n",
+ __func__, eeh_ops->name, ops->name);
+ return -EEXIST;
+ }
+
+ eeh_ops = ops;
+
+ return 0;
+}
+
+/**
+ * eeh_ops_unregister - Unreigster platform dependent EEH operations
+ * @name: name of EEH platform operations
+ *
+ * Unregister the platform dependent EEH operation callback
+ * functions.
+ */
+int __exit eeh_ops_unregister(const char *name)
+{
+ if (!name || !strlen(name)) {
+ pr_warning("%s: Invalid EEH ops name\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (eeh_ops && !strcmp(eeh_ops->name, name)) {
+ eeh_ops = NULL;
+ return 0;
+ }
+
+ return -EEXIST;
+}
+
+/**
* eeh_init - EEH initialization
*
* Initialize EEH by trying to enable it for all of the adapters in the system.
diff --git a/arch/powerpc/platforms/pseries/eeh_pseries.c b/arch/powerpc/platforms/pseries/eeh_pseries.c
new file mode 100644
index 0000000..61a9050
--- /dev/null
+++ b/arch/powerpc/platforms/pseries/eeh_pseries.c
@@ -0,0 +1,183 @@
+/*
+ * The file intends to implement the platform dependent EEH operations on pseries.
+ * Actually, the pseries platform is built based on RTAS heavily. That means the
+ * pseries platform dependent EEH operations will be built on RTAS calls. The functions
+ * are devired from arch/powerpc/platforms/pseries/eeh.c and necessary cleanup has
+ * been done.
+ *
+ * Copyright Benjamin Herrenschmidt & Gavin Shan, IBM Corporation 2011.
+ * Copyright IBM Corporation 2001, 2005, 2006
+ * Copyright Dave Engebretsen & Todd Inglett 2001
+ * Copyright Linas Vepstas 2005, 2006
+ *
+ * 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/atomic.h>
+#include <linux/delay.h>
+#include <linux/export.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/of.h>
+#include <linux/pci.h>
+#include <linux/proc_fs.h>
+#include <linux/rbtree.h>
+#include <linux/sched.h>
+#include <linux/seq_file.h>
+#include <linux/spinlock.h>
+
+#include <asm/eeh.h>
+#include <asm/eeh_event.h>
+#include <asm/io.h>
+#include <asm/machdep.h>
+#include <asm/ppc-pci.h>
+#include <asm/rtas.h>
+
+/**
+ * pseries_eeh_init - EEH platform dependent initialization
+ *
+ * EEH platform dependent initialization on pseries.
+ */
+static int pseries_eeh_init(void)
+{
+ return 0;
+}
+
+/**
+ * pseries_eeh_set_option - Initialize EEH or MMIO/DMA reenable
+ * @dn: device node
+ * @option: operation to be issued
+ *
+ * The function is used to control the EEH functionality globally.
+ * Currently, following options are support according to PAPR:
+ * Enable EEH, Disable EEH, Enable MMIO and Enable DMA
+ */
+static int pseries_eeh_set_option(struct device_node *dn, int option)
+{
+ return 0;
+}
+
+/**
+ * pseries_eeh_get_pe_addr - Retrieve PE address
+ * @dn: device node
+ *
+ * Retrieve the assocated PE address. Actually, there're 2 RTAS
+ * function calls dedicated for the purpose. We need implement
+ * it through the new function and then the old one. Besides,
+ * you should make sure the config address is figured out from
+ * FDT node before calling the function.
+ *
+ * It's notable that zero'ed return value means invalid PE config
+ * address.
+ */
+static int pseries_eeh_get_pe_addr(struct device_node *dn)
+{
+ return 0;
+}
+
+/**
+ * pseries_eeh_get_state - Retrieve PE state
+ * @dn: PE associated device node
+ * @state: return value
+ *
+ * Retrieve the state of the specified PE. On RTAS compliant
+ * pseries platform, there already has one dedicated RTAS function
+ * for the purpose. It's notable that the associated PE config address
+ * might be ready when calling the function. Therefore, endeavour to
+ * use the PE config address if possible. Further more, there're 2
+ * RTAS calls for the purpose, we need to try the new one and back
+ * to the old one if the new one couldn't work properly.
+ */
+static int pseries_eeh_get_state(struct device_node *dn, int *state)
+{
+ return 0;
+}
+
+/**
+ * pseries_eeh_reset - Reset the specified PE
+ * @dn: PE associated device node
+ * @option: reset option
+ *
+ * Reset the specified PE
+ */
+static int pseries_eeh_reset(struct device_node *dn, int option)
+{
+ return 0;
+}
+
+/**
+ * pseries_eeh_wait_state - Wait for PE state
+ * @dn: PE associated device node
+ * @max_wait: maximal period in microsecond
+ *
+ * Wait for the state of associated PE. It might take some time
+ * to retrieve the PE's state.
+ */
+static int pseries_eeh_wait_state(struct device_node *dn, int max_wait)
+{
+ return 0;
+}
+
+/**
+ * pseries_eeh_get_log - Retrieve error log
+ * @dn: device node
+ * @severity: temporary or permanent error log
+ * @drv_log: driver log to be combined with retrieved error log
+ * @len: length of driver log
+ *
+ * Retrieve the temporary or permanent error from the PE.
+ * Actually, the error will be retrieved through the dedicated
+ * RTAS call.
+ */
+static int pseries_eeh_get_log(struct device_node *dn, int severity, char *drv_log, unsigned long len)
+{
+ return 0;
+}
+
+/**
+ * pseries_eeh_configure_bridge - Configure PCI bridges in the indicated PE
+ * @dn: PE associated device node
+ *
+ * The function will be called to reconfigure the bridges included
+ * in the specified PE so that the mulfunctional PE would be recovered
+ * again.
+ */
+static int pseries_eeh_configure_bridge(struct device_node *dn)
+{
+ return 0;
+}
+
+static struct eeh_ops pseries_eeh_ops = {
+ .name = "pseries",
+ .init = pseries_eeh_init,
+ .set_option = pseries_eeh_set_option,
+ .get_pe_addr = pseries_eeh_get_pe_addr,
+ .get_state = pseries_eeh_get_state,
+ .reset = pseries_eeh_reset,
+ .wait_state = pseries_eeh_wait_state,
+ .get_log = pseries_eeh_get_log,
+ .configure_bridge = pseries_eeh_configure_bridge
+};
+
+/**
+ * eeh_pseries_init - Register platform dependent EEH operations
+ *
+ * EEH initialization on pseries platform. This function should be
+ * called before any EEH related functions.
+ */
+int __init eeh_pseries_init(void)
+{
+ return eeh_ops_register(&pseries_eeh_ops);
+}
diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c
index f79f127..809d9d9 100644
--- a/arch/powerpc/platforms/pseries/setup.c
+++ b/arch/powerpc/platforms/pseries/setup.c
@@ -382,6 +382,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();
--
1.7.5.4
^ permalink raw reply related
* [PATCH 17/21] Replace pci_dn with eeh_dev for EEH core
From: Gavin Shan @ 2012-02-28 6:04 UTC (permalink / raw)
To: linuxppc-dev; +Cc: shangw
In-Reply-To: <1330409051-8941-1-git-send-email-shangw@linux.vnet.ibm.com>
The original EEH implementation is heavily depending on struct pci_dn.
We have to put EEH related information to pci_dn. Actually, we could
split struct pci_dn so that the EEH sensitive information to form an
individual struct, then EEH looks more independent.
The patch replaces pci_dn with eeh_dev for EEH core.
Signed-off-by: Gavin Shan <shangw@linux.vnet.ibm.com>
---
arch/powerpc/include/asm/ppc-pci.h | 8 +-
arch/powerpc/platforms/pseries/eeh.c | 269 ++++++++++++++++++----------------
2 files changed, 144 insertions(+), 133 deletions(-)
diff --git a/arch/powerpc/include/asm/ppc-pci.h b/arch/powerpc/include/asm/ppc-pci.h
index c02d5a7..e660b37 100644
--- a/arch/powerpc/include/asm/ppc-pci.h
+++ b/arch/powerpc/include/asm/ppc-pci.h
@@ -53,10 +53,10 @@ 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);
struct pci_dev *pci_addr_cache_get_device(unsigned long addr);
-void eeh_slot_error_detail (struct pci_dn *pdn, int severity);
-int eeh_pci_enable(struct pci_dn *pdn, int function);
-int eeh_reset_pe(struct pci_dn *);
-void eeh_restore_bars(struct pci_dn *);
+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);
void eeh_mark_slot(struct device_node *dn, int mode_flag);
diff --git a/arch/powerpc/platforms/pseries/eeh.c b/arch/powerpc/platforms/pseries/eeh.c
index 646b520..aec10f6 100644
--- a/arch/powerpc/platforms/pseries/eeh.c
+++ b/arch/powerpc/platforms/pseries/eeh.c
@@ -115,28 +115,29 @@ static unsigned long slot_resets;
/**
* eeh_gather_pci_data - Copy assorted PCI config space registers to buff
- * @pdn: device to report data for
+ * @edev: device to report data for
* @buf: point to buffer in which to log
* @len: amount of room in buffer
*
* This routine captures assorted PCI configuration space data,
* and puts them into a buffer for RTAS error logging.
*/
-static size_t eeh_gather_pci_data(struct pci_dn *pdn, char * buf, size_t len)
+static size_t eeh_gather_pci_data(struct eeh_dev *edev, char * buf, size_t len)
{
- struct pci_dev *dev = pdn->pcidev;
+ struct device_node *dn = eeh_dev_to_of_node(edev);
+ struct pci_dev *dev = eeh_dev_to_pci_dev(edev);
u32 cfg;
int cap, i;
int n = 0;
- n += scnprintf(buf+n, len-n, "%s\n", pdn->node->full_name);
- printk(KERN_WARNING "EEH: of node=%s\n", pdn->node->full_name);
+ n += scnprintf(buf+n, len-n, "%s\n", dn->full_name);
+ printk(KERN_WARNING "EEH: of node=%s\n", dn->full_name);
- rtas_read_config(pdn, PCI_VENDOR_ID, 4, &cfg);
+ rtas_read_config(PCI_DN(dn), PCI_VENDOR_ID, 4, &cfg);
n += scnprintf(buf+n, len-n, "dev/vend:%08x\n", cfg);
printk(KERN_WARNING "EEH: PCI device/vendor: %08x\n", cfg);
- rtas_read_config(pdn, PCI_COMMAND, 4, &cfg);
+ rtas_read_config(PCI_DN(dn), PCI_COMMAND, 4, &cfg);
n += scnprintf(buf+n, len-n, "cmd/stat:%x\n", cfg);
printk(KERN_WARNING "EEH: PCI cmd/status register: %08x\n", cfg);
@@ -147,11 +148,11 @@ static size_t eeh_gather_pci_data(struct pci_dn *pdn, char * buf, size_t len)
/* Gather bridge-specific registers */
if (dev->class >> 16 == PCI_BASE_CLASS_BRIDGE) {
- rtas_read_config(pdn, PCI_SEC_STATUS, 2, &cfg);
+ rtas_read_config(PCI_DN(dn), PCI_SEC_STATUS, 2, &cfg);
n += scnprintf(buf+n, len-n, "sec stat:%x\n", cfg);
printk(KERN_WARNING "EEH: Bridge secondary status: %04x\n", cfg);
- rtas_read_config(pdn, PCI_BRIDGE_CONTROL, 2, &cfg);
+ rtas_read_config(PCI_DN(dn), PCI_BRIDGE_CONTROL, 2, &cfg);
n += scnprintf(buf+n, len-n, "brdg ctl:%x\n", cfg);
printk(KERN_WARNING "EEH: Bridge control: %04x\n", cfg);
}
@@ -159,11 +160,11 @@ static size_t eeh_gather_pci_data(struct pci_dn *pdn, char * buf, size_t len)
/* Dump out the PCI-X command and status regs */
cap = pci_find_capability(dev, PCI_CAP_ID_PCIX);
if (cap) {
- rtas_read_config(pdn, cap, 4, &cfg);
+ rtas_read_config(PCI_DN(dn), cap, 4, &cfg);
n += scnprintf(buf+n, len-n, "pcix-cmd:%x\n", cfg);
printk(KERN_WARNING "EEH: PCI-X cmd: %08x\n", cfg);
- rtas_read_config(pdn, cap+4, 4, &cfg);
+ rtas_read_config(PCI_DN(dn), cap+4, 4, &cfg);
n += scnprintf(buf+n, len-n, "pcix-stat:%x\n", cfg);
printk(KERN_WARNING "EEH: PCI-X status: %08x\n", cfg);
}
@@ -176,7 +177,7 @@ static size_t eeh_gather_pci_data(struct pci_dn *pdn, char * buf, size_t len)
"EEH: PCI-E capabilities and status follow:\n");
for (i=0; i<=8; i++) {
- rtas_read_config(pdn, cap+4*i, 4, &cfg);
+ rtas_read_config(PCI_DN(dn), cap+4*i, 4, &cfg);
n += scnprintf(buf+n, len-n, "%02x:%x\n", 4*i, cfg);
printk(KERN_WARNING "EEH: PCI-E %02x: %08x\n", i, cfg);
}
@@ -188,7 +189,7 @@ static size_t eeh_gather_pci_data(struct pci_dn *pdn, char * buf, size_t len)
"EEH: PCI-E AER capability register set follows:\n");
for (i=0; i<14; i++) {
- rtas_read_config(pdn, cap+4*i, 4, &cfg);
+ rtas_read_config(PCI_DN(dn), cap+4*i, 4, &cfg);
n += scnprintf(buf+n, len-n, "%02x:%x\n", 4*i, cfg);
printk(KERN_WARNING "EEH: PCI-E AER %02x: %08x\n", i, cfg);
}
@@ -197,12 +198,11 @@ static size_t eeh_gather_pci_data(struct pci_dn *pdn, char * buf, size_t len)
/* Gather status on devices under the bridge */
if (dev->class >> 16 == PCI_BASE_CLASS_BRIDGE) {
- struct device_node *dn;
+ struct device_node *child;
- for_each_child_of_node(pdn->node, dn) {
- pdn = PCI_DN(dn);
- if (pdn)
- n += eeh_gather_pci_data(pdn, buf+n, len-n);
+ for_each_child_of_node(dn, child) {
+ if (of_node_to_eeh_dev(child))
+ n += eeh_gather_pci_data(of_node_to_eeh_dev(child), buf+n, len-n);
}
}
@@ -211,7 +211,7 @@ static size_t eeh_gather_pci_data(struct pci_dn *pdn, char * buf, size_t len)
/**
* eeh_slot_error_detail - Generate combined log including driver log and error log
- * @pdn: device node
+ * @edev: device to report error log for
* @severity: temporary or permanent error log
*
* This routine should be called to generate the combined log, which
@@ -219,17 +219,17 @@ static size_t eeh_gather_pci_data(struct pci_dn *pdn, char * buf, size_t len)
* out from the config space of the corresponding PCI device, while
* the error log is fetched through platform dependent function call.
*/
-void eeh_slot_error_detail(struct pci_dn *pdn, int severity)
+void eeh_slot_error_detail(struct eeh_dev *edev, int severity)
{
size_t loglen = 0;
pci_regs_buf[0] = 0;
- eeh_pci_enable(pdn, EEH_OPT_THAW_MMIO);
- eeh_ops->configure_bridge(pdn->node);
- eeh_restore_bars(pdn);
- loglen = eeh_gather_pci_data(pdn, pci_regs_buf, EEH_PCI_REGS_LOG_LEN);
+ eeh_pci_enable(edev, EEH_OPT_THAW_MMIO);
+ eeh_ops->configure_bridge(eeh_dev_to_of_node(edev));
+ eeh_restore_bars(edev);
+ loglen = eeh_gather_pci_data(edev, pci_regs_buf, EEH_PCI_REGS_LOG_LEN);
- eeh_ops->get_log(pdn->node, severity, pci_regs_buf, loglen);
+ eeh_ops->get_log(eeh_dev_to_of_node(edev), severity, pci_regs_buf, loglen);
}
/**
@@ -260,8 +260,8 @@ static inline unsigned long eeh_token_to_phys(unsigned long token)
*/
struct device_node *eeh_find_device_pe(struct device_node *dn)
{
- while ((dn->parent) && PCI_DN(dn->parent) &&
- (PCI_DN(dn->parent)->eeh_mode & EEH_MODE_SUPPORTED)) {
+ while (dn->parent && of_node_to_eeh_dev(dn->parent) &&
+ (of_node_to_eeh_dev(dn->parent)->mode & EEH_MODE_SUPPORTED)) {
dn = dn->parent;
}
return dn;
@@ -284,11 +284,11 @@ static void __eeh_mark_slot(struct device_node *parent, int mode_flag)
struct device_node *dn;
for_each_child_of_node(parent, dn) {
- if (PCI_DN(dn)) {
+ if (of_node_to_eeh_dev(dn)) {
/* Mark the pci device driver too */
- struct pci_dev *dev = PCI_DN(dn)->pcidev;
+ struct pci_dev *dev = of_node_to_eeh_dev(dn)->pdev;
- PCI_DN(dn)->eeh_mode |= mode_flag;
+ of_node_to_eeh_dev(dn)->mode |= mode_flag;
if (dev && dev->driver)
dev->error_state = pci_channel_io_frozen;
@@ -312,13 +312,13 @@ void eeh_mark_slot(struct device_node *dn, int mode_flag)
dn = eeh_find_device_pe(dn);
/* Back up one, since config addrs might be shared */
- if (!pcibios_find_pci_bus(dn) && PCI_DN(dn->parent))
+ if (!pcibios_find_pci_bus(dn) && of_node_to_eeh_dev(dn->parent))
dn = dn->parent;
- PCI_DN(dn)->eeh_mode |= mode_flag;
+ of_node_to_eeh_dev(dn)->mode |= mode_flag;
/* Mark the pci device too */
- dev = PCI_DN(dn)->pcidev;
+ dev = of_node_to_eeh_dev(dn)->pdev;
if (dev)
dev->error_state = pci_channel_io_frozen;
@@ -337,9 +337,9 @@ static void __eeh_clear_slot(struct device_node *parent, int mode_flag)
struct device_node *dn;
for_each_child_of_node(parent, dn) {
- if (PCI_DN(dn)) {
- PCI_DN(dn)->eeh_mode &= ~mode_flag;
- PCI_DN(dn)->eeh_check_count = 0;
+ if (of_node_to_eeh_dev(dn)) {
+ of_node_to_eeh_dev(dn)->mode &= ~mode_flag;
+ of_node_to_eeh_dev(dn)->check_count = 0;
__eeh_clear_slot(dn, mode_flag);
}
}
@@ -360,11 +360,11 @@ void eeh_clear_slot(struct device_node *dn, int mode_flag)
dn = eeh_find_device_pe(dn);
/* Back up one, since config addrs might be shared */
- if (!pcibios_find_pci_bus(dn) && PCI_DN(dn->parent))
+ if (!pcibios_find_pci_bus(dn) && of_node_to_eeh_dev(dn->parent))
dn = dn->parent;
- PCI_DN(dn)->eeh_mode &= ~mode_flag;
- PCI_DN(dn)->eeh_check_count = 0;
+ of_node_to_eeh_dev(dn)->mode &= ~mode_flag;
+ of_node_to_eeh_dev(dn)->check_count = 0;
__eeh_clear_slot(dn, mode_flag);
raw_spin_unlock_irqrestore(&confirm_error_lock, flags);
}
@@ -388,7 +388,7 @@ int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev)
{
int ret;
unsigned long flags;
- struct pci_dn *pdn;
+ struct eeh_dev *edev;
int rc = 0;
const char *location;
@@ -402,18 +402,18 @@ int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev)
return 0;
}
dn = eeh_find_device_pe(dn);
- pdn = PCI_DN(dn);
+ edev = of_node_to_eeh_dev(dn);
/* 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) {
+ if (!(edev->mode & EEH_MODE_SUPPORTED) ||
+ edev->mode & EEH_MODE_NOCHECK) {
ignored_check++;
pr_debug("EEH: Ignored check (%x) for %s %s\n",
- pdn->eeh_mode, eeh_pci_name(dev), dn->full_name);
+ edev->mode, eeh_pci_name(dev), dn->full_name);
return 0;
}
- if (!pdn->eeh_config_addr && !pdn->eeh_pe_config_addr) {
+ if (!edev->config_addr && !edev->pe_config_addr) {
no_cfg_addr++;
return 0;
}
@@ -426,13 +426,13 @@ int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev)
*/
raw_spin_lock_irqsave(&confirm_error_lock, flags);
rc = 1;
- if (pdn->eeh_mode & EEH_MODE_ISOLATED) {
- pdn->eeh_check_count ++;
- if (pdn->eeh_check_count % EEH_MAX_FAILS == 0) {
+ if (edev->mode & EEH_MODE_ISOLATED) {
+ edev->check_count++;
+ if (edev->check_count % EEH_MAX_FAILS == 0) {
location = of_get_property(dn, "ibm,loc-code", NULL);
printk(KERN_ERR "EEH: %d reads ignored for recovering device at "
"location=%s driver=%s pci addr=%s\n",
- pdn->eeh_check_count, location,
+ edev->check_count, location,
eeh_driver_name(dev), eeh_pci_name(dev));
printk(KERN_ERR "EEH: Might be infinite loop in %s driver\n",
eeh_driver_name(dev));
@@ -448,7 +448,7 @@ int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev)
* function zero of a multi-function device.
* In any case they must share a common PHB.
*/
- ret = eeh_ops->get_state(pdn->node, NULL);
+ ret = eeh_ops->get_state(dn, NULL);
/* Note that config-io to empty slots may fail;
* they are empty when they don't have children.
@@ -461,7 +461,7 @@ int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev)
(ret & (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE)) ==
(EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE)) {
false_positives++;
- pdn->eeh_false_positives ++;
+ edev->false_positives ++;
rc = 0;
goto dn_unlock;
}
@@ -475,7 +475,7 @@ int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev)
eeh_mark_slot(dn, EEH_MODE_ISOLATED);
raw_spin_unlock_irqrestore(&confirm_error_lock, flags);
- eeh_send_failure_event(dn, dev);
+ eeh_send_failure_event(edev->dn, edev->pdev);
/* Most EEH events are due to device driver bugs. Having
* a stack trace will help the device-driver authors figure
@@ -529,22 +529,23 @@ EXPORT_SYMBOL(eeh_check_failure);
/**
* eeh_pci_enable - Enable MMIO or DMA transfers for this slot
- * @pdn pci device node
+ * @edev: pci device node
*
* This routine should be called to reenable frozen MMIO or DMA
* so that it would work correctly again. It's useful while doing
* recovery or log collection on the indicated device.
*/
-int eeh_pci_enable(struct pci_dn *pdn, int function)
+int eeh_pci_enable(struct eeh_dev *edev, int function)
{
int rc;
+ struct device_node *dn = eeh_dev_to_of_node(edev);
- rc = eeh_ops->set_option(pdn->node, function);
+ rc = eeh_ops->set_option(dn, function);
if (rc)
printk(KERN_WARNING "EEH: Unexpected state change %d, err=%d dn=%s\n",
- function, rc, pdn->node->full_name);
+ function, rc, dn->full_name);
- rc = eeh_ops->wait_state(pdn->node, PCI_BUS_RESET_WAIT_MSEC);
+ rc = eeh_ops->wait_state(dn, PCI_BUS_RESET_WAIT_MSEC);
if (rc > 0 && (rc & EEH_STATE_MMIO_ENABLED) &&
(function == EEH_OPT_THAW_MMIO))
return 0;
@@ -595,8 +596,8 @@ void __eeh_set_pe_freset(struct device_node *parent, unsigned int *freset)
struct device_node *dn;
for_each_child_of_node(parent, dn) {
- if (PCI_DN(dn)) {
- struct pci_dev *dev = PCI_DN(dn)->pcidev;
+ if (of_node_to_eeh_dev(dn)) {
+ struct pci_dev *dev = of_node_to_eeh_dev(dn)->pdev;
if (dev && dev->driver)
*freset |= dev->needs_freset;
@@ -622,10 +623,10 @@ void eeh_set_pe_freset(struct device_node *dn, unsigned int *freset)
dn = eeh_find_device_pe(dn);
/* Back up one, since config addrs might be shared */
- if (!pcibios_find_pci_bus(dn) && PCI_DN(dn->parent))
+ if (!pcibios_find_pci_bus(dn) && of_node_to_eeh_dev(dn->parent))
dn = dn->parent;
- dev = PCI_DN(dn)->pcidev;
+ dev = of_node_to_eeh_dev(dn)->pdev;
if (dev)
*freset |= dev->needs_freset;
@@ -634,13 +635,14 @@ void eeh_set_pe_freset(struct device_node *dn, unsigned int *freset)
/**
* eeh_reset_pe_once - Assert the pci #RST line for 1/4 second
- * @pdn: pci device node to be reset.
+ * @edev: pci device node to be reset.
*
* Assert the PCI #RST line for 1/4 second.
*/
-static void eeh_reset_pe_once(struct pci_dn *pdn)
+static void eeh_reset_pe_once(struct eeh_dev *edev)
{
unsigned int freset = 0;
+ struct device_node *dn = eeh_dev_to_of_node(edev);
/* Determine type of EEH reset required for
* Partitionable Endpoint, a hot-reset (1)
@@ -648,12 +650,12 @@ static void eeh_reset_pe_once(struct pci_dn *pdn)
* A fundamental reset required by any device under
* Partitionable Endpoint trumps hot-reset.
*/
- eeh_set_pe_freset(pdn->node, &freset);
+ eeh_set_pe_freset(dn, &freset);
if (freset)
- eeh_ops->reset(pdn->node, EEH_RESET_FUNDAMENTAL);
+ eeh_ops->reset(dn, EEH_RESET_FUNDAMENTAL);
else
- eeh_ops->reset(pdn->node, EEH_RESET_HOT);
+ eeh_ops->reset(dn, EEH_RESET_HOT);
/* The PCI bus requires that the reset be held high for at least
* a 100 milliseconds. We wait a bit longer 'just in case'.
@@ -665,9 +667,9 @@ static void eeh_reset_pe_once(struct pci_dn *pdn)
* pci slot reset line is dropped. Make sure we don't miss
* these, and clear the flag now.
*/
- eeh_clear_slot(pdn->node, EEH_MODE_ISOLATED);
+ eeh_clear_slot(dn, EEH_MODE_ISOLATED);
- eeh_ops->reset(pdn->node, EEH_RESET_DEACTIVATE);
+ eeh_ops->reset(dn, EEH_RESET_DEACTIVATE);
/* After a PCI slot has been reset, the PCI Express spec requires
* a 1.5 second idle time for the bus to stabilize, before starting
@@ -679,31 +681,32 @@ static void eeh_reset_pe_once(struct pci_dn *pdn)
/**
* eeh_reset_pe - Reset the indicated PE
- * @pdn: PCI device node
+ * @edev: PCI device associated EEH device
*
* This routine should be called to reset indicated device, including
* PE. A PE might include multiple PCI devices and sometimes PCI bridges
* might be involved as well.
*/
-int eeh_reset_pe(struct pci_dn *pdn)
+int eeh_reset_pe(struct eeh_dev *edev)
{
int i, rc;
+ struct device_node *dn = eeh_dev_to_of_node(edev);
/* Take three shots at resetting the bus */
for (i=0; i<3; i++) {
- eeh_reset_pe_once(pdn);
+ eeh_reset_pe_once(edev);
- rc = eeh_ops->wait_state(pdn->node, PCI_BUS_RESET_WAIT_MSEC);
+ rc = eeh_ops->wait_state(dn, PCI_BUS_RESET_WAIT_MSEC);
if (rc == (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE))
return 0;
if (rc < 0) {
printk(KERN_ERR "EEH: unrecoverable slot failure %s\n",
- pdn->node->full_name);
+ dn->full_name);
return -1;
}
printk(KERN_ERR "EEH: bus reset %d failed on slot %s, rc=%d\n",
- i+1, pdn->node->full_name, rc);
+ i+1, dn->full_name, rc);
}
return -1;
@@ -719,90 +722,95 @@ int eeh_reset_pe(struct pci_dn *pdn)
/**
* eeh_restore_one_device_bars - Restore the Base Address Registers for one device
- * @pdn: pci device node
+ * @edev: PCI device associated EEH device
*
* Loads the PCI configuration space base address registers,
* the expansion ROM base address, the latency timer, and etc.
* from the saved values in the device node.
*/
-static inline void eeh_restore_one_device_bars(struct pci_dn *pdn)
+static inline void eeh_restore_one_device_bars(struct eeh_dev *edev)
{
int i;
u32 cmd;
+ struct device_node *dn = eeh_dev_to_of_node(edev);
+
+ if (!edev->phb)
+ return;
- if (NULL==pdn->phb) return;
for (i=4; i<10; i++) {
- rtas_write_config(pdn, i*4, 4, pdn->config_space[i]);
+ rtas_write_config(PCI_DN(dn), i*4, 4, edev->config_space[i]);
}
/* 12 == Expansion ROM Address */
- rtas_write_config(pdn, 12*4, 4, pdn->config_space[12]);
+ rtas_write_config(PCI_DN(dn), 12*4, 4, edev->config_space[12]);
#define BYTE_SWAP(OFF) (8*((OFF)/4)+3-(OFF))
-#define SAVED_BYTE(OFF) (((u8 *)(pdn->config_space))[BYTE_SWAP(OFF)])
+#define SAVED_BYTE(OFF) (((u8 *)(edev->config_space))[BYTE_SWAP(OFF)])
- rtas_write_config(pdn, PCI_CACHE_LINE_SIZE, 1,
+ rtas_write_config(PCI_DN(dn), PCI_CACHE_LINE_SIZE, 1,
SAVED_BYTE(PCI_CACHE_LINE_SIZE));
- rtas_write_config(pdn, PCI_LATENCY_TIMER, 1,
+ rtas_write_config(PCI_DN(dn), PCI_LATENCY_TIMER, 1,
SAVED_BYTE(PCI_LATENCY_TIMER));
/* max latency, min grant, interrupt pin and line */
- rtas_write_config(pdn, 15*4, 4, pdn->config_space[15]);
+ rtas_write_config(PCI_DN(dn), 15*4, 4, edev->config_space[15]);
/* Restore PERR & SERR bits, some devices require it,
* don't touch the other command bits
*/
- rtas_read_config(pdn, PCI_COMMAND, 4, &cmd);
- if (pdn->config_space[1] & PCI_COMMAND_PARITY)
+ rtas_read_config(PCI_DN(dn), PCI_COMMAND, 4, &cmd);
+ if (edev->config_space[1] & PCI_COMMAND_PARITY)
cmd |= PCI_COMMAND_PARITY;
else
cmd &= ~PCI_COMMAND_PARITY;
- if (pdn->config_space[1] & PCI_COMMAND_SERR)
+ if (edev->config_space[1] & PCI_COMMAND_SERR)
cmd |= PCI_COMMAND_SERR;
else
cmd &= ~PCI_COMMAND_SERR;
- rtas_write_config(pdn, PCI_COMMAND, 4, cmd);
+ rtas_write_config(PCI_DN(dn), PCI_COMMAND, 4, cmd);
}
/**
* eeh_restore_bars - Restore the PCI config space info
- * @pdn: PCI device node
+ * @edev: EEH device
*
* This routine performs a recursive walk to the children
* of this device as well.
*/
-void eeh_restore_bars(struct pci_dn *pdn)
+void eeh_restore_bars(struct eeh_dev *edev)
{
struct device_node *dn;
- if (!pdn)
+ if (!edev)
return;
- if ((pdn->eeh_mode & EEH_MODE_SUPPORTED) && !IS_BRIDGE(pdn->class_code))
- eeh_restore_one_device_bars(pdn);
+ if ((edev->mode & EEH_MODE_SUPPORTED) && !IS_BRIDGE(edev->class_code))
+ eeh_restore_one_device_bars(edev);
- for_each_child_of_node(pdn->node, dn)
- eeh_restore_bars(PCI_DN(dn));
+ for_each_child_of_node(eeh_dev_to_of_node(edev), dn)
+ eeh_restore_bars(of_node_to_eeh_dev(dn));
}
/**
* eeh_save_bars - Save device bars
- * @pdn: PCI device node
+ * @edev: PCI device associated EEH device
*
* Save the values of the device bars. Unlike the restore
* routine, this routine is *not* recursive. This is because
* PCI devices are added individually; but, for the restore,
* an entire slot is reset at a time.
*/
-static void eeh_save_bars(struct pci_dn *pdn)
+static void eeh_save_bars(struct eeh_dev *edev)
{
int i;
+ struct device_node *dn;
- if (!pdn )
+ if (!edev)
return;
+ dn = eeh_dev_to_of_node(edev);
for (i = 0; i < 16; i++)
- rtas_read_config(pdn, i * 4, 4, &pdn->config_space[i]);
+ rtas_read_config(PCI_DN(dn), i * 4, 4, &edev->config_space[i]);
}
/**
@@ -822,13 +830,13 @@ static void *eeh_early_enable(struct device_node *dn, void *data)
const u32 *device_id = of_get_property(dn, "device-id", NULL);
const u32 *regs;
int enable;
- struct pci_dn *pdn = PCI_DN(dn);
+ struct eeh_dev *edev = of_node_to_eeh_dev(dn);
- pdn->class_code = 0;
- pdn->eeh_mode = 0;
- pdn->eeh_check_count = 0;
- pdn->eeh_freeze_count = 0;
- pdn->eeh_false_positives = 0;
+ edev->class_code = 0;
+ edev->mode = 0;
+ edev->check_count = 0;
+ edev->freeze_count = 0;
+ edev->false_positives = 0;
if (!of_device_is_available(dn))
return NULL;
@@ -839,10 +847,10 @@ static void *eeh_early_enable(struct device_node *dn, void *data)
/* There is nothing to check on PCI to ISA bridges */
if (dn->type && !strcmp(dn->type, "isa")) {
- pdn->eeh_mode |= EEH_MODE_NOCHECK;
+ edev->mode |= EEH_MODE_NOCHECK;
return NULL;
}
- pdn->class_code = *class_code;
+ edev->class_code = *class_code;
/* Ok... see if this device supports EEH. Some do, some don't,
* and the only way to find out is to check each and every one.
@@ -855,40 +863,40 @@ static void *eeh_early_enable(struct device_node *dn, void *data)
enable = 0;
if (ret == 0) {
- pdn->eeh_config_addr = regs[0];
+ edev->config_addr = regs[0];
/* If the newer, better, ibm,get-config-addr-info is supported,
* then use that instead.
*/
- pdn->eeh_pe_config_addr = eeh_ops->get_pe_addr(dn);
+ edev->pe_config_addr = eeh_ops->get_pe_addr(dn);
/* Some older systems (Power4) allow the
* ibm,set-eeh-option call to succeed even on nodes
* where EEH is not supported. Verify support
* explicitly.
*/
- ret = eeh_ops->get_state(pdn->node, NULL);
+ ret = eeh_ops->get_state(dn, NULL);
if (ret > 0 && ret != EEH_STATE_NOT_SUPPORT)
enable = 1;
}
if (enable) {
eeh_subsystem_enabled = 1;
- pdn->eeh_mode |= EEH_MODE_SUPPORTED;
+ edev->mode |= EEH_MODE_SUPPORTED;
pr_debug("EEH: %s: eeh enabled, config=%x pe_config=%x\n",
- dn->full_name, pdn->eeh_config_addr,
- pdn->eeh_pe_config_addr);
+ dn->full_name, edev->config_addr,
+ edev->pe_config_addr);
} else {
/* This device doesn't support EEH, but it may have an
* EEH parent, in which case we mark it as supported.
*/
- if (dn->parent && PCI_DN(dn->parent)
- && (PCI_DN(dn->parent)->eeh_mode & EEH_MODE_SUPPORTED)) {
+ if (dn->parent && of_node_to_eeh_dev(dn->parent) &&
+ (of_node_to_eeh_dev(dn->parent)->mode & EEH_MODE_SUPPORTED)) {
/* Parent supports EEH. */
- pdn->eeh_mode |= EEH_MODE_SUPPORTED;
- pdn->eeh_config_addr = PCI_DN(dn->parent)->eeh_config_addr;
+ edev->mode |= EEH_MODE_SUPPORTED;
+ edev->config_addr = of_node_to_eeh_dev(dn->parent)->config_addr;
return NULL;
}
}
@@ -897,7 +905,7 @@ static void *eeh_early_enable(struct device_node *dn, void *data)
dn->full_name);
}
- eeh_save_bars(pdn);
+ eeh_save_bars(edev);
return NULL;
}
@@ -994,7 +1002,7 @@ void __init eeh_init(void)
unsigned long buid;
buid = get_phb_buid(phb);
- if (buid == 0 || PCI_DN(phb) == NULL)
+ if (buid == 0 || !of_node_to_eeh_dev(phb))
continue;
traverse_pci_devices(phb, eeh_early_enable, NULL);
@@ -1022,9 +1030,9 @@ static void eeh_add_device_early(struct device_node *dn)
{
struct pci_controller *phb;
- if (!dn || !PCI_DN(dn))
+ if (!dn || !of_node_to_eeh_dev(dn))
return;
- phb = PCI_DN(dn)->phb;
+ phb = of_node_to_eeh_dev(dn)->phb;
/* USB Bus children of PCI devices will not have BUID's */
if (NULL == phb || 0 == phb->buid)
@@ -1061,7 +1069,7 @@ EXPORT_SYMBOL_GPL(eeh_add_device_tree_early);
static void eeh_add_device_late(struct pci_dev *dev)
{
struct device_node *dn;
- struct pci_dn *pdn;
+ struct eeh_dev *edev;
if (!dev || !eeh_subsystem_enabled)
return;
@@ -1069,15 +1077,16 @@ static void eeh_add_device_late(struct pci_dev *dev)
pr_debug("EEH: Adding device %s\n", pci_name(dev));
dn = pci_device_to_OF_node(dev);
- pdn = PCI_DN(dn);
- if (pdn->pcidev == dev) {
+ edev = pci_dev_to_eeh_dev(dev);
+ if (edev->pdev == dev) {
pr_debug("EEH: Already referenced !\n");
return;
}
- WARN_ON(pdn->pcidev);
+ WARN_ON(edev->pdev);
pci_dev_get(dev);
- pdn->pcidev = dev;
+ edev->pdev = dev;
+ dev->dev.archdata.edev = edev;
pci_addr_cache_insert_device(dev);
eeh_sysfs_add_device(dev);
@@ -1118,19 +1127,21 @@ EXPORT_SYMBOL_GPL(eeh_add_device_tree_late);
*/
static void eeh_remove_device(struct pci_dev *dev)
{
- struct device_node *dn;
+ struct eeh_dev *edev;
+
if (!dev || !eeh_subsystem_enabled)
return;
+ edev = pci_dev_to_eeh_dev(dev);
/* Unregister the device with the EEH/PCI address search system */
pr_debug("EEH: Removing device %s\n", pci_name(dev));
- dn = pci_device_to_OF_node(dev);
- if (PCI_DN(dn)->pcidev == NULL) {
+ if (!edev || !edev->pdev) {
pr_debug("EEH: Not referenced !\n");
return;
}
- PCI_DN(dn)->pcidev = NULL;
+ edev->pdev = NULL;
+ dev->dev.archdata.edev = NULL;
pci_dev_put(dev);
pci_addr_cache_remove_device(dev);
--
1.7.5.4
^ permalink raw reply related
* [PATCH 04/21] pSeries platform EEH initialization
From: Gavin Shan @ 2012-02-28 6:03 UTC (permalink / raw)
To: linuxppc-dev; +Cc: shangw
In-Reply-To: <1330409051-8941-1-git-send-email-shangw@linux.vnet.ibm.com>
The platform specific EEH operations have been abstracted by
struct eeh_ops. The individual platroms, including pSeries, needs
doing necessary initialization before the platform dependent EEH
operations work properly.
The patch is addressing that and do necessary platform initialization
for pSeries platform. More specificly, it will figure out the tokens
of EEH related RTAS calls.
Signed-off-by: Gavin Shan <shangw@linux.vnet.ibm.com>
---
arch/powerpc/platforms/pseries/eeh.c | 12 ++++++
arch/powerpc/platforms/pseries/eeh_pseries.c | 55 ++++++++++++++++++++++++++
2 files changed, 67 insertions(+), 0 deletions(-)
diff --git a/arch/powerpc/platforms/pseries/eeh.c b/arch/powerpc/platforms/pseries/eeh.c
index b0e3fb0..bb6de6c 100644
--- a/arch/powerpc/platforms/pseries/eeh.c
+++ b/arch/powerpc/platforms/pseries/eeh.c
@@ -1279,6 +1279,18 @@ void __init eeh_init(void)
{
struct device_node *phb, *np;
struct eeh_early_enable_info info;
+ int ret;
+
+ /* call platform initialization function */
+ if (!eeh_ops) {
+ pr_warning("%s: Platform EEH operation not found\n",
+ __func__);
+ return;
+ } else if ((ret = eeh_ops->init())) {
+ pr_warning("%s: Failed to call platform init function (%d)\n",
+ __func__, ret);
+ return;
+ }
raw_spin_lock_init(&confirm_error_lock);
spin_lock_init(&slot_errbuf_lock);
diff --git a/arch/powerpc/platforms/pseries/eeh_pseries.c b/arch/powerpc/platforms/pseries/eeh_pseries.c
index 61a9050..1a9410a 100644
--- a/arch/powerpc/platforms/pseries/eeh_pseries.c
+++ b/arch/powerpc/platforms/pseries/eeh_pseries.c
@@ -45,6 +45,17 @@
#include <asm/ppc-pci.h>
#include <asm/rtas.h>
+/* 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_get_config_addr_info2;
+static int ibm_configure_bridge;
+static int ibm_configure_pe;
+
/**
* pseries_eeh_init - EEH platform dependent initialization
*
@@ -52,6 +63,50 @@
*/
static int pseries_eeh_init(void)
{
+ /* figure out EEH RTAS function call tokens */
+ ibm_set_eeh_option = rtas_token("ibm,set-eeh-option");
+ ibm_set_slot_reset = rtas_token("ibm,set-slot-reset");
+ 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_info2 = rtas_token("ibm,get-config-addr-info2");
+ ibm_get_config_addr_info = rtas_token("ibm,get-config-addr-info");
+ ibm_configure_pe = rtas_token("ibm,configure-pe");
+ ibm_configure_bridge = rtas_token ("ibm,configure-bridge");
+
+ /* necessary sanity check */
+ if (ibm_set_eeh_option == RTAS_UNKNOWN_SERVICE) {
+ pr_warning("%s: RTAS service <ibm,set-eeh-option> invalid\n",
+ __func__);
+ return -EINVAL;
+ } else if (ibm_set_slot_reset == RTAS_UNKNOWN_SERVICE) {
+ pr_warning("%s: RTAS service <ibm, set-slot-reset> invalid\n",
+ __func__);
+ return -EINVAL;
+ } else if (ibm_read_slot_reset_state2 == RTAS_UNKNOWN_SERVICE &&
+ ibm_read_slot_reset_state == RTAS_UNKNOWN_SERVICE) {
+ pr_warning("%s: RTAS service <ibm,read-slot-reset-state2> and "
+ "<ibm,read-slot-reset-state> invalid\n",
+ __func__);
+ return -EINVAL;
+ } else if (ibm_slot_error_detail == RTAS_UNKNOWN_SERVICE) {
+ pr_warning("%s: RTAS service <ibm,slot-error-detail> invalid\n",
+ __func__);
+ return -EINVAL;
+ } else if (ibm_get_config_addr_info2 == RTAS_UNKNOWN_SERVICE &&
+ ibm_get_config_addr_info == RTAS_UNKNOWN_SERVICE) {
+ pr_warning("%s: RTAS service <ibm,get-config-addr-info2> and "
+ "<ibm,get-config-addr-info> invalid\n",
+ __func__);
+ return -EINVAL;
+ } else if (ibm_configure_pe == RTAS_UNKNOWN_SERVICE &&
+ ibm_configure_bridge == RTAS_UNKNOWN_SERVICE) {
+ pr_warning("%s: RTAS service <ibm,configure-pe> and "
+ "<ibm,configure-bridge> invalid\n",
+ __func__);
+ return -EINVAL;
+ }
+
return 0;
}
--
1.7.5.4
^ permalink raw reply related
* [PATCH 01/21] Cleanup on comments of EEH core
From: Gavin Shan @ 2012-02-28 6:03 UTC (permalink / raw)
To: linuxppc-dev; +Cc: shangw
In-Reply-To: <1330409051-8941-1-git-send-email-shangw@linux.vnet.ibm.com>
The EEH has been implemented on pSeries platform. The original
code looks a little bit nasty. The patch does cleanup on the
current EEH implementation so that it looks more clean.
* Duplicated comments have been removed from the corresponding
header files.
* Comments have been reorganized so that it looks more clean.
* The leading comments of functions are adjusted for a little
bit so that the result of "make pdfdocs" would be more
unified.
* Function definitions and calls have unified format as "xxx()".
That means the format "xxx ()" has been replaced by "xxx()".
* There're multiple functions implemented for resetting PE. The
position of those functions have been move around so that they
are adjacent to each other to reflect their relationship.
Signed-off-by: Gavin Shan <shangw@linux.vnet.ibm.com>
---
arch/powerpc/include/asm/eeh.h | 26 +--
arch/powerpc/include/asm/ppc-pci.h | 71 +------
arch/powerpc/platforms/pseries/eeh.c | 400 +++++++++++++++++++++++-----------
3 files changed, 276 insertions(+), 221 deletions(-)
diff --git a/arch/powerpc/include/asm/eeh.h b/arch/powerpc/include/asm/eeh.h
index 66ea9b8..2328877 100644
--- a/arch/powerpc/include/asm/eeh.h
+++ b/arch/powerpc/include/asm/eeh.h
@@ -1,6 +1,6 @@
/*
- * 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
@@ -40,8 +40,10 @@ extern int eeh_subsystem_enabled;
#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. */
+/*
+ * Max number of EEH freezes allowed before we consider the device
+ * to be permanently disabled.
+ */
#define EEH_MAX_ALLOWED_FREEZES 5
void __init eeh_init(void);
@@ -49,26 +51,8 @@ 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 *);
/**
diff --git a/arch/powerpc/include/asm/ppc-pci.h b/arch/powerpc/include/asm/ppc-pci.h
index 6d42297..221d82f 100644
--- a/arch/powerpc/include/asm/ppc-pci.h
+++ b/arch/powerpc/include/asm/ppc-pci.h
@@ -47,92 +47,27 @@ 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_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 *);
-
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 *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);
diff --git a/arch/powerpc/platforms/pseries/eeh.c b/arch/powerpc/platforms/pseries/eeh.c
index c0b40af..5f6d37b 100644
--- a/arch/powerpc/platforms/pseries/eeh.c
+++ b/arch/powerpc/platforms/pseries/eeh.c
@@ -1,8 +1,8 @@
/*
- * eeh.c
* Copyright IBM Corporation 2001, 2005, 2006
* Copyright Dave Engebretsen & Todd Inglett 2001
* Copyright Linas Vepstas 2005, 2006
+ * 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
@@ -22,7 +22,7 @@
*/
#include <linux/delay.h>
-#include <linux/sched.h> /* for init_mm */
+#include <linux/sched.h>
#include <linux/init.h>
#include <linux/list.h>
#include <linux/pci.h>
@@ -129,9 +129,16 @@ static unsigned long slot_resets;
#define IS_BRIDGE(class_code) (((class_code)<<16) == PCI_BASE_CLASS_BRIDGE)
-/* --------------------------------------------------------------- */
-/* Below lies the EEH event infrastructure */
-
+/**
+ * rtas_slot_error_detail - Retrieve error log through RTAS call
+ * @pdn: device node
+ * @severity: temporary or permanent error log
+ * @driver_log: driver log to be combined with the retrieved error log
+ * @loglen: length of driver log
+ *
+ * This routine should be called to retrieve error log through the dedicated
+ * RTAS call.
+ */
static void rtas_slot_error_detail(struct pci_dn *pdn, int severity,
char *driver_log, size_t loglen)
{
@@ -163,7 +170,7 @@ static void rtas_slot_error_detail(struct pci_dn *pdn, int severity,
}
/**
- * gather_pci_data - copy assorted PCI config space registers to buff
+ * gather_pci_data - Copy assorted PCI config space registers to buff
* @pdn: device to report data for
* @buf: point to buffer in which to log
* @len: amount of room in buffer
@@ -258,6 +265,16 @@ static size_t gather_pci_data(struct pci_dn *pdn, char * buf, size_t len)
return n;
}
+/**
+ * eeh_slot_error_detail - Generate combined log including driver log and error log
+ * @pdn: device node
+ * @severity: temporary or permanent error log
+ *
+ * This routine should be called to generate the combined log, which
+ * is comprised of driver log and error log. The driver log is figured
+ * out from the config space of the corresponding PCI device, while
+ * the error log is fetched through platform dependent function call.
+ */
void eeh_slot_error_detail(struct pci_dn *pdn, int severity)
{
size_t loglen = 0;
@@ -275,6 +292,9 @@ void eeh_slot_error_detail(struct pci_dn *pdn, int severity)
* read_slot_reset_state - Read the reset state of a device node's slot
* @dn: device node to read
* @rets: array to return results in
+ *
+ * Read the reset state of a device node's slot through platform dependent
+ * function call.
*/
static int read_slot_reset_state(struct pci_dn *pdn, int rets[])
{
@@ -300,9 +320,9 @@ static int read_slot_reset_state(struct pci_dn *pdn, int rets[])
}
/**
- * eeh_wait_for_slot_status - returns error status of slot
- * @pdn pci device node
- * @max_wait_msecs maximum number to millisecs to wait
+ * eeh_wait_for_slot_status - Returns error status of slot
+ * @pdn: pci device node
+ * @max_wait_msecs: maximum number to millisecs to wait
*
* Return negative value if a permanent error, else return
* Partition Endpoint (PE) status value.
@@ -332,16 +352,16 @@ eeh_wait_for_slot_status(struct pci_dn *pdn, int max_wait_msecs)
mwait = rets[2];
if (mwait <= 0) {
- printk (KERN_WARNING
- "EEH: Firmware returned bad wait value=%d\n", mwait);
+ printk(KERN_WARNING "EEH: Firmware returned bad wait value=%d\n",
+ mwait);
mwait = 1000;
} else if (mwait > 300*1000) {
- printk (KERN_WARNING
- "EEH: Firmware is taking too long, time=%d\n", mwait);
+ printk(KERN_WARNING "EEH: Firmware is taking too long, time=%d\n",
+ mwait);
mwait = 300*1000;
}
max_wait_msecs -= mwait;
- msleep (mwait);
+ msleep(mwait);
}
printk(KERN_WARNING "EEH: Timed out waiting for slot status\n");
@@ -349,8 +369,11 @@ eeh_wait_for_slot_status(struct pci_dn *pdn, int max_wait_msecs)
}
/**
- * eeh_token_to_phys - convert EEH address token to phys address
- * @token i/o token, should be address in the form 0xA....
+ * eeh_token_to_phys - Convert EEH address token to phys address
+ * @token: I/O token, should be address in the form 0xA....
+ *
+ * This routine should be called to convert virtual I/O address
+ * to physical one.
*/
static inline unsigned long eeh_token_to_phys(unsigned long token)
{
@@ -365,8 +388,11 @@ static inline unsigned long eeh_token_to_phys(unsigned long token)
return pa | (token & (PAGE_SIZE-1));
}
-/**
- * Return the "partitionable endpoint" (pe) under which this device lies
+/**
+ * find_device_pe - Retrieve the PE for the given device
+ * @dn: device node
+ *
+ * Return the PE under which this device lies
*/
struct device_node * find_device_pe(struct device_node *dn)
{
@@ -377,14 +403,18 @@ struct device_node * find_device_pe(struct device_node *dn)
return dn;
}
-/** Mark all devices that are children of this device as failed.
- * Mark the device driver too, so that it can see the failure
- * immediately; this is critical, since some drivers poll
- * status registers in interrupts ... If a driver is polling,
- * and the slot is frozen, then the driver can deadlock in
- * an interrupt context, which is bad.
+/**
+ * __eeh_mark_slot - Mark all child devices as failed
+ * @parent: parent device
+ * @mode_flag: failure flag
+ *
+ * Mark all devices that are children of this device as failed.
+ * Mark the device driver too, so that it can see the failure
+ * immediately; this is critical, since some drivers poll
+ * status registers in interrupts ... If a driver is polling,
+ * and the slot is frozen, then the driver can deadlock in
+ * an interrupt context, which is bad.
*/
-
static void __eeh_mark_slot(struct device_node *parent, int mode_flag)
{
struct device_node *dn;
@@ -404,10 +434,18 @@ static void __eeh_mark_slot(struct device_node *parent, int mode_flag)
}
}
-void eeh_mark_slot (struct device_node *dn, int mode_flag)
+/**
+ * eeh_mark_slot - Mark the indicated device and its children as failed
+ * @dn: parent device
+ * @mode_flag: failure flag
+ *
+ * Mark the indicated device and its child devices as failed.
+ * The device drivers are marked as failed as well.
+ */
+void eeh_mark_slot(struct device_node *dn, int mode_flag)
{
struct pci_dev *dev;
- dn = find_device_pe (dn);
+ dn = find_device_pe(dn);
/* Back up one, since config addrs might be shared */
if (!pcibios_find_pci_bus(dn) && PCI_DN(dn->parent))
@@ -423,6 +461,13 @@ void eeh_mark_slot (struct device_node *dn, int mode_flag)
__eeh_mark_slot(dn, mode_flag);
}
+/**
+ * __eeh_clear_slot - Clear failure flag for the child devices
+ * @parent: parent device
+ * @mode_flag: flag to be cleared
+ *
+ * Clear failure flag for the child devices.
+ */
static void __eeh_clear_slot(struct device_node *parent, int mode_flag)
{
struct device_node *dn;
@@ -436,12 +481,19 @@ static void __eeh_clear_slot(struct device_node *parent, int mode_flag)
}
}
-void eeh_clear_slot (struct device_node *dn, int mode_flag)
+/**
+ * eeh_clear_slot - Clear failure flag for the indicated device and its children
+ * @dn: parent device
+ * @mode_flag: flag to be cleared
+ *
+ * Clear failure flag for the indicated device and its children.
+ */
+void eeh_clear_slot(struct device_node *dn, int mode_flag)
{
unsigned long flags;
raw_spin_lock_irqsave(&confirm_error_lock, flags);
- dn = find_device_pe (dn);
+ dn = find_device_pe(dn);
/* Back up one, since config addrs might be shared */
if (!pcibios_find_pci_bus(dn) && PCI_DN(dn->parent))
@@ -453,43 +505,10 @@ void eeh_clear_slot (struct device_node *dn, int mode_flag)
raw_spin_unlock_irqrestore(&confirm_error_lock, flags);
}
-void __eeh_set_pe_freset(struct device_node *parent, unsigned int *freset)
-{
- struct device_node *dn;
-
- for_each_child_of_node(parent, dn) {
- if (PCI_DN(dn)) {
-
- struct pci_dev *dev = PCI_DN(dn)->pcidev;
-
- if (dev && dev->driver)
- *freset |= dev->needs_freset;
-
- __eeh_set_pe_freset(dn, freset);
- }
- }
-}
-
-void eeh_set_pe_freset(struct device_node *dn, unsigned int *freset)
-{
- struct pci_dev *dev;
- dn = find_device_pe(dn);
-
- /* Back up one, since config addrs might be shared */
- if (!pcibios_find_pci_bus(dn) && PCI_DN(dn->parent))
- dn = dn->parent;
-
- dev = PCI_DN(dn)->pcidev;
- if (dev)
- *freset |= dev->needs_freset;
-
- __eeh_set_pe_freset(dn, freset);
-}
-
/**
- * eeh_dn_check_failure - check if all 1's data is due to EEH slot freeze
- * @dn device node
- * @dev pci device, if known
+ * eeh_dn_check_failure - Check if all 1's data is due to EEH slot freeze
+ * @dn: device node
+ * @dev: pci device, if known
*
* Check for an EEH failure for the given device node. Call this
* routine if the result of a read was all 0xff's and you want to
@@ -548,11 +567,11 @@ int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev)
pdn->eeh_check_count ++;
if (pdn->eeh_check_count % EEH_MAX_FAILS == 0) {
location = of_get_property(dn, "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",
pdn->eeh_check_count, location,
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));
dump_stack();
}
@@ -579,7 +598,8 @@ int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev)
}
/* Note that config-io to empty slots may fail;
- * they are empty when they don't have children. */
+ * they are empty when they don't have children.
+ */
if ((rets[0] == 5) && (rets[2] == 0) && (dn->child == NULL)) {
false_positives++;
pdn->eeh_false_positives ++;
@@ -609,15 +629,17 @@ int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev)
/* Avoid repeated reports of this failure, including problems
* with other functions on this device, and functions under
- * bridges. */
- eeh_mark_slot (dn, EEH_MODE_ISOLATED);
+ * bridges.
+ */
+ eeh_mark_slot(dn, EEH_MODE_ISOLATED);
raw_spin_unlock_irqrestore(&confirm_error_lock, flags);
- eeh_send_failure_event (dn, dev);
+ eeh_send_failure_event(dn, dev);
/* 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. */
+ * out what happened. So print that out.
+ */
dump_stack();
return 1;
@@ -629,9 +651,9 @@ dn_unlock:
EXPORT_SYMBOL_GPL(eeh_dn_check_failure);
/**
- * eeh_check_failure - check if all 1's data is due to EEH slot freeze
- * @token i/o token, should be address in the form 0xA....
- * @val value, should be all 1's (XXX why do we need this arg??)
+ * eeh_check_failure - Check if all 1's data is due to EEH slot freeze
+ * @token: I/O token, should be address in the form 0xA....
+ * @val: value, should be all 1's (XXX why do we need this arg??)
*
* Check for an EEH failure at the given token address. Call this
* routine if the result of a read was all 0xff's and you want to
@@ -655,7 +677,7 @@ unsigned long eeh_check_failure(const volatile void __iomem *token, unsigned lon
}
dn = pci_device_to_OF_node(dev);
- eeh_dn_check_failure (dn, dev);
+ eeh_dn_check_failure(dn, dev);
pci_dev_put(dev);
return val;
@@ -663,14 +685,15 @@ unsigned long eeh_check_failure(const volatile void __iomem *token, unsigned lon
EXPORT_SYMBOL(eeh_check_failure);
-/* ------------------------------------------------------------- */
-/* The code below deals with error recovery */
/**
- * rtas_pci_enable - enable MMIO or DMA transfers for this slot
+ * rtas_pci_enable - Enable MMIO or DMA transfers for this slot
* @pdn pci device node
+ *
+ * This routine should be called to reenable frozen MMIO or DMA
+ * so that it would work correctly again. It's useful while doing
+ * recovery or log collection on the indicated device.
*/
-
int
rtas_pci_enable(struct pci_dn *pdn, int function)
{
@@ -692,7 +715,7 @@ rtas_pci_enable(struct pci_dn *pdn, int function)
printk(KERN_WARNING "EEH: Unexpected state change %d, err=%d dn=%s\n",
function, rc, pdn->node->full_name);
- rc = eeh_wait_for_slot_status (pdn, PCI_BUS_RESET_WAIT_MSEC);
+ rc = eeh_wait_for_slot_status(pdn, PCI_BUS_RESET_WAIT_MSEC);
if ((rc == 4) && (function == EEH_THAW_MMIO))
return 0;
@@ -700,27 +723,25 @@ rtas_pci_enable(struct pci_dn *pdn, int function)
}
/**
- * rtas_pci_slot_reset - raises/lowers the pci #RST line
- * @pdn pci device node
+ * rtas_pci_slot_reset - Raises/Lowers the pci #RST line
+ * @pdn: pci device node
* @state: 1/0 to raise/lower the #RST
*
* Clear the EEH-frozen condition on a slot. This routine
* asserts the PCI #RST line if the 'state' argument is '1',
* and drops the #RST line if 'state is '0'. This routine is
* safe to call in an interrupt context.
- *
*/
-
static void
rtas_pci_slot_reset(struct pci_dn *pdn, int state)
{
int config_addr;
int rc;
- BUG_ON (pdn==NULL);
+ BUG_ON(pdn==NULL);
if (!pdn->phb) {
- printk (KERN_WARNING "EEH: in slot reset, device node %s has no phb\n",
+ printk(KERN_WARNING "EEH: in slot reset, device node %s has no phb\n",
pdn->node->full_name);
return;
}
@@ -752,12 +773,12 @@ rtas_pci_slot_reset(struct pci_dn *pdn, int state)
/**
* pcibios_set_pcie_slot_reset - Set PCI-E reset state
- * @dev: pci device struct
- * @state: reset state to enter
+ * @dev: pci device struct
+ * @state: reset state to enter
*
* Return value:
* 0 if success
- **/
+ */
int pcibios_set_pcie_reset_state(struct pci_dev *dev, enum pcie_reset_state state)
{
struct device_node *dn = pci_device_to_OF_node(dev);
@@ -781,10 +802,62 @@ int pcibios_set_pcie_reset_state(struct pci_dev *dev, enum pcie_reset_state stat
}
/**
- * rtas_set_slot_reset -- assert the pci #RST line for 1/4 second
- * @pdn: pci device node to be reset.
+ * __eeh_set_pe_freset - Check the required reset for child devices
+ * @parent: parent device
+ * @freset: return value
+ *
+ * Each device might have its preferred reset type: fundamental or
+ * hot reset. The routine is used to collect the information from
+ * the child devices so that they could be reset accordingly.
*/
+void __eeh_set_pe_freset(struct device_node *parent, unsigned int *freset)
+{
+ struct device_node *dn;
+
+ for_each_child_of_node(parent, dn) {
+ if (PCI_DN(dn)) {
+ struct pci_dev *dev = PCI_DN(dn)->pcidev;
+
+ if (dev && dev->driver)
+ *freset |= dev->needs_freset;
+
+ __eeh_set_pe_freset(dn, freset);
+ }
+ }
+}
+
+/**
+ * eeh_set_pe_freset - Check the required reset for the indicated device and its children
+ * @dn: parent device
+ * @freset: return value
+ *
+ * Each device might have its preferred reset type: fundamental or
+ * hot reset. The routine is used to collected the information for
+ * the indicated device and its children so that the bunch of the
+ * devices could be reset properly.
+ */
+void eeh_set_pe_freset(struct device_node *dn, unsigned int *freset)
+{
+ struct pci_dev *dev;
+ dn = find_device_pe(dn);
+
+ /* Back up one, since config addrs might be shared */
+ if (!pcibios_find_pci_bus(dn) && PCI_DN(dn->parent))
+ dn = dn->parent;
+ dev = PCI_DN(dn)->pcidev;
+ if (dev)
+ *freset |= dev->needs_freset;
+
+ __eeh_set_pe_freset(dn, freset);
+}
+
+/**
+ * __rtas_set_slot_reset - Assert the pci #RST line for 1/4 second
+ * @pdn: pci device node to be reset.
+ *
+ * Assert the PCI #RST line for 1/4 second.
+ */
static void __rtas_set_slot_reset(struct pci_dn *pdn)
{
unsigned int freset = 0;
@@ -803,25 +876,35 @@ static void __rtas_set_slot_reset(struct pci_dn *pdn)
rtas_pci_slot_reset(pdn, 1);
/* The PCI bus requires that the reset be held high for at least
- * a 100 milliseconds. We wait a bit longer 'just in case'. */
-
+ * a 100 milliseconds. We wait a bit longer 'just in case'.
+ */
#define PCI_BUS_RST_HOLD_TIME_MSEC 250
- msleep (PCI_BUS_RST_HOLD_TIME_MSEC);
+ msleep(PCI_BUS_RST_HOLD_TIME_MSEC);
/* We might get hit with another EEH freeze as soon as the
* pci slot reset line is dropped. Make sure we don't miss
- * these, and clear the flag now. */
- eeh_clear_slot (pdn->node, EEH_MODE_ISOLATED);
+ * these, and clear the flag now.
+ */
+ eeh_clear_slot(pdn->node, EEH_MODE_ISOLATED);
- rtas_pci_slot_reset (pdn, 0);
+ rtas_pci_slot_reset(pdn, 0);
/* After a PCI slot has been reset, the PCI Express spec requires
* a 1.5 second idle time for the bus to stabilize, before starting
- * up traffic. */
+ * up traffic.
+ */
#define PCI_BUS_SETTLE_TIME_MSEC 1800
- msleep (PCI_BUS_SETTLE_TIME_MSEC);
+ msleep(PCI_BUS_SETTLE_TIME_MSEC);
}
+/**
+ * rtas_set_slot_reset - Reset the indicated PE
+ * @pdn: PCI device node
+ *
+ * This routine should be called to reset indicated device, including
+ * PE. A PE might include multiple PCI devices and sometimes PCI bridges
+ * might be involved as well.
+ */
int rtas_set_slot_reset(struct pci_dn *pdn)
{
int i, rc;
@@ -846,7 +929,6 @@ int rtas_set_slot_reset(struct pci_dn *pdn)
return -1;
}
-/* ------------------------------------------------------- */
/** Save and restore of PCI BARs
*
* Although firmware will set up BARs during boot, it doesn't
@@ -863,7 +945,7 @@ int rtas_set_slot_reset(struct pci_dn *pdn)
* the expansion ROM base address, the latency timer, and etc.
* from the saved values in the device node.
*/
-static inline void __restore_bars (struct pci_dn *pdn)
+static inline void __restore_bars(struct pci_dn *pdn)
{
int i;
u32 cmd;
@@ -879,17 +961,18 @@ static inline void __restore_bars (struct pci_dn *pdn)
#define BYTE_SWAP(OFF) (8*((OFF)/4)+3-(OFF))
#define SAVED_BYTE(OFF) (((u8 *)(pdn->config_space))[BYTE_SWAP(OFF)])
- rtas_write_config (pdn, PCI_CACHE_LINE_SIZE, 1,
+ rtas_write_config(pdn, PCI_CACHE_LINE_SIZE, 1,
SAVED_BYTE(PCI_CACHE_LINE_SIZE));
- rtas_write_config (pdn, PCI_LATENCY_TIMER, 1,
+ rtas_write_config(pdn, PCI_LATENCY_TIMER, 1,
SAVED_BYTE(PCI_LATENCY_TIMER));
/* max latency, min grant, interrupt pin and line */
rtas_write_config(pdn, 15*4, 4, pdn->config_space[15]);
/* Restore PERR & SERR bits, some devices require it,
- don't touch the other command bits */
+ * don't touch the other command bits
+ */
rtas_read_config(pdn, PCI_COMMAND, 4, &cmd);
if (pdn->config_space[1] & PCI_COMMAND_PARITY)
cmd |= PCI_COMMAND_PARITY;
@@ -903,7 +986,8 @@ static inline void __restore_bars (struct pci_dn *pdn)
}
/**
- * eeh_restore_bars - restore the PCI config space info
+ * eeh_restore_bars - Restore the PCI config space info
+ * @pdn: PCI device node
*
* This routine performs a recursive walk to the children
* of this device as well.
@@ -915,14 +999,15 @@ void eeh_restore_bars(struct pci_dn *pdn)
return;
if ((pdn->eeh_mode & EEH_MODE_SUPPORTED) && !IS_BRIDGE(pdn->class_code))
- __restore_bars (pdn);
+ __restore_bars(pdn);
for_each_child_of_node(pdn->node, dn)
- eeh_restore_bars (PCI_DN(dn));
+ eeh_restore_bars(PCI_DN(dn));
}
/**
- * eeh_save_bars - save device bars
+ * eeh_save_bars - Save device bars
+ * @pdn: PCI device node
*
* Save the values of the device bars. Unlike the restore
* routine, this routine is *not* recursive. This is because
@@ -940,6 +1025,14 @@ static void eeh_save_bars(struct pci_dn *pdn)
rtas_read_config(pdn, i * 4, 4, &pdn->config_space[i]);
}
+/**
+ * rtas_configure_bridge - Configure PCI bridges for the indicated PE
+ * @pdn: PCI device node
+ *
+ * PCI bridges might be included in PE. In order to make the PE work
+ * again. The included PCI bridges should be recovered after the PE
+ * encounters frozen state.
+ */
void
rtas_configure_bridge(struct pci_dn *pdn)
{
@@ -963,17 +1056,11 @@ rtas_configure_bridge(struct pci_dn *pdn)
BUID_HI(pdn->phb->buid),
BUID_LO(pdn->phb->buid));
if (rc) {
- printk (KERN_WARNING "EEH: Unable to configure device bridge (%d) for %s\n",
+ printk(KERN_WARNING "EEH: Unable to configure device bridge (%d) for %s\n",
rc, pdn->node->full_name);
}
}
-/* ------------------------------------------------------------- */
-/* The code below deals with enabling EEH for devices during the
- * early boot sequence. EEH must be enabled before any PCI probing
- * can be done.
- */
-
#define EEH_ENABLE 1
struct eeh_early_enable_info {
@@ -981,7 +1068,18 @@ struct eeh_early_enable_info {
unsigned int buid_lo;
};
-static int get_pe_addr (int config_addr,
+/**
+ * get_pe_addr - Retrieve PE address with given BDF address
+ * @config_addr: BDF address
+ * @info: BUID of the associated PHB
+ *
+ * There're 2 kinds of addresses existing in EEH core components:
+ * BDF address and PE address. Besides, there has dedicated platform
+ * dependent function call to retrieve the PE address according to
+ * the given BDF address. Further more, we prefer PE address on BDF
+ * address in EEH core components.
+ */
+static int get_pe_addr(int config_addr,
struct eeh_early_enable_info *info)
{
unsigned int rets[3];
@@ -990,12 +1088,12 @@ static int get_pe_addr (int config_addr,
/* Use latest config-addr token on power6 */
if (ibm_get_config_addr_info2 != RTAS_UNKNOWN_SERVICE) {
/* Make sure we have a PE in hand */
- ret = rtas_call (ibm_get_config_addr_info2, 4, 2, rets,
+ ret = rtas_call(ibm_get_config_addr_info2, 4, 2, rets,
config_addr, info->buid_hi, info->buid_lo, 1);
if (ret || (rets[0]==0))
return 0;
- ret = rtas_call (ibm_get_config_addr_info2, 4, 2, rets,
+ ret = rtas_call(ibm_get_config_addr_info2, 4, 2, rets,
config_addr, info->buid_hi, info->buid_lo, 0);
if (ret)
return 0;
@@ -1004,7 +1102,7 @@ static int get_pe_addr (int config_addr,
/* Use older config-addr token on power5 */
if (ibm_get_config_addr_info != RTAS_UNKNOWN_SERVICE) {
- ret = rtas_call (ibm_get_config_addr_info, 4, 2, rets,
+ ret = rtas_call(ibm_get_config_addr_info, 4, 2, rets,
config_addr, info->buid_hi, info->buid_lo, 0);
if (ret)
return 0;
@@ -1013,7 +1111,15 @@ static int get_pe_addr (int config_addr,
return 0;
}
-/* Enable eeh for the given device node. */
+/**
+ * early_enable_eeh - Early enable EEH on the indicated device
+ * @dn: device node
+ * @data: BUID
+ *
+ * Enable EEH functionality on the specified PCI device. The function
+ * is expected to be called before real PCI probing is done. However,
+ * the PHBs have been initialized at this point.
+ */
static void *early_enable_eeh(struct device_node *dn, void *data)
{
unsigned int rets[3];
@@ -1047,7 +1153,8 @@ static void *early_enable_eeh(struct device_node *dn, void *data)
pdn->class_code = *class_code;
/* Ok... see if this device supports EEH. Some do, some don't,
- * and the only way to find out is to check each and every one. */
+ * and the only way to find out is to check each and every one.
+ */
regs = of_get_property(dn, "reg", NULL);
if (regs) {
/* First register entry is addr (00BBSS00) */
@@ -1061,13 +1168,15 @@ static void *early_enable_eeh(struct device_node *dn, void *data)
pdn->eeh_config_addr = regs[0];
/* If the newer, better, ibm,get-config-addr-info is supported,
- * then use that instead. */
+ * then use that instead.
+ */
pdn->eeh_pe_config_addr = get_pe_addr(pdn->eeh_config_addr, info);
/* Some older systems (Power4) allow the
* ibm,set-eeh-option call to succeed even on nodes
* where EEH is not supported. Verify support
- * explicitly. */
+ * explicitly.
+ */
ret = read_slot_reset_state(pdn, rets);
if ((ret == 0) && (rets[1] == 1))
enable = 1;
@@ -1083,7 +1192,8 @@ static void *early_enable_eeh(struct device_node *dn, void *data)
} else {
/* 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.
+ */
if (dn->parent && PCI_DN(dn->parent)
&& (PCI_DN(dn->parent)->eeh_mode & EEH_MODE_SUPPORTED)) {
/* Parent supports EEH. */
@@ -1101,7 +1211,9 @@ static void *early_enable_eeh(struct device_node *dn, void *data)
return NULL;
}
-/*
+/**
+ * eeh_init - EEH initialization
+ *
* Initialize EEH by trying to enable it for all of the adapters in the system.
* As a side effect we can determine here if eeh is supported at all.
* Note that we leave EEH on so failed config cycles won't cause a machine
@@ -1133,7 +1245,7 @@ void __init eeh_init(void)
ibm_slot_error_detail = rtas_token("ibm,slot-error-detail");
ibm_get_config_addr_info = rtas_token("ibm,get-config-addr-info");
ibm_get_config_addr_info2 = rtas_token("ibm,get-config-addr-info2");
- ibm_configure_bridge = rtas_token ("ibm,configure-bridge");
+ ibm_configure_bridge = rtas_token("ibm,configure-bridge");
ibm_configure_pe = rtas_token("ibm,configure-pe");
if (ibm_set_eeh_option == RTAS_UNKNOWN_SERVICE)
@@ -1170,7 +1282,7 @@ void __init eeh_init(void)
}
/**
- * 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
*
* This routine must be used to perform EEH initialization for PCI
@@ -1199,6 +1311,14 @@ static void eeh_add_device_early(struct device_node *dn)
early_enable_eeh(dn, &info);
}
+/**
+ * eeh_add_device_tree_early - Enable EEH for the indicated device
+ * @dn: device node
+ *
+ * This routine must be used to perform EEH initialization for the
+ * indicated PCI device that was added after system boot (e.g.
+ * hotplug, dlpar).
+ */
void eeh_add_device_tree_early(struct device_node *dn)
{
struct device_node *sib;
@@ -1210,7 +1330,7 @@ void eeh_add_device_tree_early(struct device_node *dn)
EXPORT_SYMBOL_GPL(eeh_add_device_tree_early);
/**
- * eeh_add_device_late - perform EEH initialization for the indicated pci device
+ * eeh_add_device_late - Perform EEH initialization for the indicated pci device
* @dev: pci device for which to set up EEH
*
* This routine must be used to complete EEH initialization for PCI
@@ -1234,13 +1354,21 @@ static void eeh_add_device_late(struct pci_dev *dev)
}
WARN_ON(pdn->pcidev);
- pci_dev_get (dev);
+ pci_dev_get(dev);
pdn->pcidev = dev;
pci_addr_cache_insert_device(dev);
eeh_sysfs_add_device(dev);
}
+/**
+ * eeh_add_device_tree_late - Perform EEH initialization for the indicated PCI bus
+ * @bus: PCI bus
+ *
+ * This routine must be used to perform EEH initialization for PCI
+ * devices which are attached to the indicated PCI bus. The PCI bus
+ * is added after system boot through hotplug or dlpar.
+ */
void eeh_add_device_tree_late(struct pci_bus *bus)
{
struct pci_dev *dev;
@@ -1257,7 +1385,7 @@ void eeh_add_device_tree_late(struct pci_bus *bus)
EXPORT_SYMBOL_GPL(eeh_add_device_tree_late);
/**
- * eeh_remove_device - undo EEH setup for the indicated pci device
+ * eeh_remove_device - Undo EEH setup for the indicated pci device
* @dev: pci device to be removed
*
* This routine should be called when a device is removed from
@@ -1281,12 +1409,20 @@ static void eeh_remove_device(struct pci_dev *dev)
return;
}
PCI_DN(dn)->pcidev = NULL;
- pci_dev_put (dev);
+ pci_dev_put(dev);
pci_addr_cache_remove_device(dev);
eeh_sysfs_remove_device(dev);
}
+/**
+ * eeh_remove_bus_device - Undo EEH setup for the indicated PCI device
+ * @dev: PCI device
+ *
+ * This routine must be called when a device is removed from the
+ * running system through hotplug or dlpar. The corresponding
+ * PCI address cache will be removed.
+ */
void eeh_remove_bus_device(struct pci_dev *dev)
{
struct pci_bus *bus = dev->subordinate;
--
1.7.5.4
^ permalink raw reply related
* [PATCH 10/21] pSeries platform EEH error log retrieval
From: Gavin Shan @ 2012-02-28 6:04 UTC (permalink / raw)
To: linuxppc-dev; +Cc: shangw
In-Reply-To: <1330409051-8941-1-git-send-email-shangw@linux.vnet.ibm.com>
On RTAS compliant pSeries platform, one dedicated RTAS call has
been introduced to retrieve EEH temporary or permanent error log.
The patch implements the function of retriving EEH error log through
RTAS call. Besides, it has been abstracted by struct eeh_ops::get_log
so that EEH core components could support multiple platforms in future.
Signed-off-by: Gavin Shan <shangw@linux.vnet.ibm.com>
---
arch/powerpc/include/asm/eeh.h | 2 +
arch/powerpc/include/asm/ppc-pci.h | 2 -
arch/powerpc/platforms/pseries/eeh.c | 63 +-------------------------
arch/powerpc/platforms/pseries/eeh_driver.c | 4 +-
arch/powerpc/platforms/pseries/eeh_pseries.c | 47 +++++++++++++++++++-
5 files changed, 51 insertions(+), 67 deletions(-)
diff --git a/arch/powerpc/include/asm/eeh.h b/arch/powerpc/include/asm/eeh.h
index 894ea6c..ad8f318 100644
--- a/arch/powerpc/include/asm/eeh.h
+++ b/arch/powerpc/include/asm/eeh.h
@@ -52,6 +52,8 @@ struct device_node;
#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;
diff --git a/arch/powerpc/include/asm/ppc-pci.h b/arch/powerpc/include/asm/ppc-pci.h
index 1cfb2b0..bd1a84f 100644
--- a/arch/powerpc/include/asm/ppc-pci.h
+++ b/arch/powerpc/include/asm/ppc-pci.h
@@ -53,8 +53,6 @@ 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);
-#define EEH_LOG_TEMP_FAILURE 1
-#define EEH_LOG_PERM_FAILURE 2
void eeh_slot_error_detail (struct pci_dn *pdn, int severity);
int eeh_pci_enable(struct pci_dn *pdn, int function);
int eeh_reset_pe(struct pci_dn *);
diff --git a/arch/powerpc/platforms/pseries/eeh.c b/arch/powerpc/platforms/pseries/eeh.c
index 4f329f5..39fcecb 100644
--- a/arch/powerpc/platforms/pseries/eeh.c
+++ b/arch/powerpc/platforms/pseries/eeh.c
@@ -87,7 +87,6 @@
#define PCI_BUS_RESET_WAIT_MSEC (60*1000)
/* RTAS tokens */
-static int ibm_slot_error_detail;
static int ibm_configure_bridge;
static int ibm_configure_pe;
@@ -100,14 +99,6 @@ EXPORT_SYMBOL(eeh_subsystem_enabled);
/* Lock to avoid races due to multiple reports of an error */
static DEFINE_RAW_SPINLOCK(confirm_error_lock);
-/* Buffer for reporting slot-error-detail rtas calls. Its here
- * in BSS, and not dynamically alloced, so that it ends up in
- * RMO where RTAS can access it.
- */
-static unsigned char slot_errbuf[RTAS_ERROR_LOG_MAX];
-static DEFINE_SPINLOCK(slot_errbuf_lock);
-static int eeh_error_buf_size;
-
/* Buffer for reporting pci register dumps. Its here in BSS, and
* not dynamically alloced, so that it ends up in RMO where RTAS
* can access it.
@@ -127,46 +118,6 @@ static unsigned long slot_resets;
#define IS_BRIDGE(class_code) (((class_code)<<16) == PCI_BASE_CLASS_BRIDGE)
/**
- * eeh_rtas_slot_error_detail - Retrieve error log through RTAS call
- * @pdn: device node
- * @severity: temporary or permanent error log
- * @driver_log: driver log to be combined with the retrieved error log
- * @loglen: length of driver log
- *
- * This routine should be called to retrieve error log through the dedicated
- * RTAS call.
- */
-static void eeh_rtas_slot_error_detail(struct pci_dn *pdn, int severity,
- char *driver_log, size_t loglen)
-{
- int config_addr;
- unsigned long flags;
- int rc;
-
- /* Log the error with the rtas logger */
- 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, config_addr,
- BUID_HI(pdn->phb->buid),
- BUID_LO(pdn->phb->buid),
- virt_to_phys(driver_log), loglen,
- virt_to_phys(slot_errbuf),
- eeh_error_buf_size,
- severity);
-
- if (rc == 0)
- log_error(slot_errbuf, ERR_TYPE_RTAS_LOG, 0);
- spin_unlock_irqrestore(&slot_errbuf_lock, flags);
-}
-
-/**
* eeh_gather_pci_data - Copy assorted PCI config space registers to buff
* @pdn: device to report data for
* @buf: point to buffer in which to log
@@ -282,7 +233,7 @@ void eeh_slot_error_detail(struct pci_dn *pdn, int severity)
eeh_restore_bars(pdn);
loglen = eeh_gather_pci_data(pdn, pci_regs_buf, EEH_PCI_REGS_LOG_LEN);
- eeh_rtas_slot_error_detail(pdn, severity, pci_regs_buf, loglen);
+ eeh_ops->get_log(pdn->node, severity, pci_regs_buf, loglen);
}
/**
@@ -1071,26 +1022,14 @@ void __init eeh_init(void)
}
raw_spin_lock_init(&confirm_error_lock);
- spin_lock_init(&slot_errbuf_lock);
np = of_find_node_by_path("/rtas");
if (np == NULL)
return;
- ibm_slot_error_detail = rtas_token("ibm,slot-error-detail");
ibm_configure_bridge = rtas_token("ibm,configure-bridge");
ibm_configure_pe = rtas_token("ibm,configure-pe");
- eeh_error_buf_size = rtas_token("rtas-error-log-max");
- if (eeh_error_buf_size == RTAS_UNKNOWN_SERVICE) {
- eeh_error_buf_size = 1024;
- }
- if (eeh_error_buf_size > RTAS_ERROR_LOG_MAX) {
- printk(KERN_WARNING "EEH: rtas-error-log-max is bigger than allocated "
- "buffer ! (%d vs %d)", eeh_error_buf_size, RTAS_ERROR_LOG_MAX);
- eeh_error_buf_size = RTAS_ERROR_LOG_MAX;
- }
-
/* Enable EEH for all adapters. Note that eeh requires buid's */
for (phb = of_find_node_by_name(NULL, "pci"); phb;
phb = of_find_node_by_name(phb, "pci")) {
diff --git a/arch/powerpc/platforms/pseries/eeh_driver.c b/arch/powerpc/platforms/pseries/eeh_driver.c
index 584defe..6840357 100644
--- a/arch/powerpc/platforms/pseries/eeh_driver.c
+++ b/arch/powerpc/platforms/pseries/eeh_driver.c
@@ -406,7 +406,7 @@ struct pci_dn * handle_eeh_events (struct eeh_event *event)
* don't post the error log until after all dev drivers
* have been informed.
*/
- eeh_slot_error_detail(frozen_pdn, EEH_LOG_TEMP_FAILURE);
+ eeh_slot_error_detail(frozen_pdn, EEH_LOG_TEMP);
/* If all device drivers were EEH-unaware, then shut
* down all of the device drivers, and hope they
@@ -497,7 +497,7 @@ hard_fail:
location, drv_str, pci_str);
perm_error:
- eeh_slot_error_detail(frozen_pdn, EEH_LOG_PERM_FAILURE);
+ eeh_slot_error_detail(frozen_pdn, EEH_LOG_PERM);
/* Notify all devices that they're about to go down. */
pci_walk_bus(frozen_bus, eeh_report_failure, NULL);
diff --git a/arch/powerpc/platforms/pseries/eeh_pseries.c b/arch/powerpc/platforms/pseries/eeh_pseries.c
index 6643e06..7c8434f 100644
--- a/arch/powerpc/platforms/pseries/eeh_pseries.c
+++ b/arch/powerpc/platforms/pseries/eeh_pseries.c
@@ -56,6 +56,15 @@ static int ibm_get_config_addr_info2;
static int ibm_configure_bridge;
static int ibm_configure_pe;
+/*
+ * Buffer for reporting slot-error-detail rtas calls. Its here
+ * in BSS, and not dynamically alloced, so that it ends up in
+ * RMO where RTAS can access it.
+ */
+static unsigned char slot_errbuf[RTAS_ERROR_LOG_MAX];
+static DEFINE_SPINLOCK(slot_errbuf_lock);
+static int eeh_error_buf_size;
+
/**
* pseries_eeh_init - EEH platform dependent initialization
*
@@ -107,6 +116,19 @@ static int pseries_eeh_init(void)
return -EINVAL;
}
+ /* Initialize error log lock and size */
+ spin_lock_init(&slot_errbuf_lock);
+ eeh_error_buf_size = rtas_token("rtas-error-log-max");
+ if (eeh_error_buf_size == RTAS_UNKNOWN_SERVICE) {
+ pr_warning("%s: unknown EEH error log size\n",
+ __func__);
+ eeh_error_buf_size = 1024;
+ } else if (eeh_error_buf_size > RTAS_ERROR_LOG_MAX) {
+ pr_warning("%s: EEH error log size %d exceeds the maximal %d\n",
+ __func__, eeh_error_buf_size, RTAS_ERROR_LOG_MAX);
+ eeh_error_buf_size = RTAS_ERROR_LOG_MAX;
+ }
+
return 0;
}
@@ -415,7 +437,30 @@ static int pseries_eeh_wait_state(struct device_node *dn, int max_wait)
*/
static int pseries_eeh_get_log(struct device_node *dn, int severity, char *drv_log, unsigned long len)
{
- return 0;
+ struct pci_dn *pdn;
+ int config_addr;
+ unsigned long flags;
+ int ret;
+
+ pdn = PCI_DN(dn);
+ spin_lock_irqsave(&slot_errbuf_lock, flags);
+ memset(slot_errbuf, 0, eeh_error_buf_size);
+
+ /* Figure out the PE address */
+ config_addr = pdn->eeh_config_addr;
+ if (pdn->eeh_pe_config_addr)
+ config_addr = pdn->eeh_pe_config_addr;
+
+ ret = rtas_call(ibm_slot_error_detail, 8, 1, NULL, config_addr,
+ BUID_HI(pdn->phb->buid), BUID_LO(pdn->phb->buid),
+ virt_to_phys(drv_log), len,
+ virt_to_phys(slot_errbuf), eeh_error_buf_size,
+ severity);
+ if (!ret)
+ log_error(slot_errbuf, ERR_TYPE_RTAS_LOG, 0);
+ spin_unlock_irqrestore(&slot_errbuf_lock, flags);
+
+ return ret;
}
/**
--
1.7.5.4
^ permalink raw reply related
* [PATCH 07/21] pSeries platform PE state retrieval
From: Gavin Shan @ 2012-02-28 6:03 UTC (permalink / raw)
To: linuxppc-dev; +Cc: shangw
In-Reply-To: <1330409051-8941-1-git-send-email-shangw@linux.vnet.ibm.com>
On pSeries platform, there're 2 dedicated RTAS calls introduced to
retrieve the corresponding PE's state: ibm,read-slot-reset-state and
ibm,read-slot-reset-state2.
The patch implements the retrieval of PE's state according to the
given PE address. Besides, the implementation has been abstracted by
struct eeh_ops::get_state so that EEH core components could support
multiple platforms in future.
Signed-off-by: Gavin Shan <shangw@linux.vnet.ibm.com>
---
arch/powerpc/include/asm/eeh.h | 8 ++
arch/powerpc/platforms/pseries/eeh.c | 96 ++++---------------------
arch/powerpc/platforms/pseries/eeh_driver.c | 2 +-
arch/powerpc/platforms/pseries/eeh_pseries.c | 70 ++++++++++++++++++-
4 files changed, 94 insertions(+), 82 deletions(-)
diff --git a/arch/powerpc/include/asm/eeh.h b/arch/powerpc/include/asm/eeh.h
index 76f7b3f..1d3c9e5 100644
--- a/arch/powerpc/include/asm/eeh.h
+++ b/arch/powerpc/include/asm/eeh.h
@@ -42,6 +42,14 @@ struct device_node;
#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 */
+
struct eeh_ops {
char *name;
int (*init)(void);
diff --git a/arch/powerpc/platforms/pseries/eeh.c b/arch/powerpc/platforms/pseries/eeh.c
index 00797e0..8d11f1f 100644
--- a/arch/powerpc/platforms/pseries/eeh.c
+++ b/arch/powerpc/platforms/pseries/eeh.c
@@ -88,8 +88,6 @@
/* RTAS tokens */
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_configure_bridge;
static int ibm_configure_pe;
@@ -289,37 +287,6 @@ void eeh_slot_error_detail(struct pci_dn *pdn, int severity)
}
/**
- * eeh_read_slot_reset_state - Read the reset state of a device node's slot
- * @dn: device node to read
- * @rets: array to return results in
- *
- * Read the reset state of a device node's slot through platform dependent
- * function call.
- */
-static int eeh_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;
- outputs = 4;
- } else {
- token = ibm_read_slot_reset_state;
- rets[2] = 0; /* fake PE Unavailable info */
- outputs = 3;
- }
-
- /* 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));
-}
-
-/**
* eeh_wait_for_slot_status - Returns error status of slot
* @pdn: pci device node
* @max_wait_msecs: maximum number to millisecs to wait
@@ -335,21 +302,15 @@ static int eeh_read_slot_reset_state(struct pci_dn *pdn, int rets[])
int eeh_wait_for_slot_status(struct pci_dn *pdn, int max_wait_msecs)
{
int rc;
- int rets[3];
int mwait;
while (1) {
- rc = eeh_read_slot_reset_state(pdn, rets);
- if (rc) return rc;
- if (rets[1] == 0) return -1; /* EEH is not supported */
-
- if (rets[0] != 5) return rets[0]; /* return actual status */
-
- if (rets[2] == 0) return -1; /* permanently unavailable */
+ rc = eeh_ops->get_state(pdn->node, &mwait);
+ if (rc != EEH_STATE_UNAVAILABLE)
+ return rc;
if (max_wait_msecs <= 0) break;
- mwait = rets[2];
if (mwait <= 0) {
printk(KERN_WARNING "EEH: Firmware returned bad wait value=%d\n",
mwait);
@@ -522,7 +483,6 @@ void eeh_clear_slot(struct device_node *dn, int mode_flag)
int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev)
{
int ret;
- int rets[3];
unsigned long flags;
struct pci_dn *pdn;
int rc = 0;
@@ -584,40 +544,18 @@ int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev)
* function zero of a multi-function device.
* In any case they must share a common PHB.
*/
- ret = eeh_read_slot_reset_state(pdn, rets);
-
- /* If the call to firmware failed, punt */
- if (ret != 0) {
- printk(KERN_WARNING "EEH: eeh_read_slot_reset_state() failed; rc=%d dn=%s\n",
- ret, dn->full_name);
- false_positives++;
- pdn->eeh_false_positives ++;
- rc = 0;
- goto dn_unlock;
- }
+ ret = eeh_ops->get_state(pdn->node, NULL);
/* Note that config-io to empty slots may fail;
* they are empty when they don't have children.
+ * We will punt with the following conditions: Failure to get
+ * PE's state, EEH not support and Permanently unavailable
+ * state, PE is in good state.
*/
- if ((rets[0] == 5) && (rets[2] == 0) && (dn->child == NULL)) {
- false_positives++;
- pdn->eeh_false_positives ++;
- rc = 0;
- goto dn_unlock;
- }
-
- /* If EEH is not supported on this device, punt. */
- if (rets[1] != 1) {
- printk(KERN_WARNING "EEH: event on unsupported device, rc=%d dn=%s\n",
- ret, dn->full_name);
- false_positives++;
- pdn->eeh_false_positives ++;
- rc = 0;
- goto dn_unlock;
- }
-
- /* If not the kind of error we know about, punt. */
- if (rets[0] != 1 && rets[0] != 2 && rets[0] != 4 && rets[0] != 5) {
+ if ((ret < 0) ||
+ (ret == EEH_STATE_NOT_SUPPORT) ||
+ (ret & (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE)) ==
+ (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE)) {
false_positives++;
pdn->eeh_false_positives ++;
rc = 0;
@@ -703,7 +641,8 @@ int eeh_pci_enable(struct pci_dn *pdn, int function)
function, rc, pdn->node->full_name);
rc = eeh_wait_for_slot_status(pdn, PCI_BUS_RESET_WAIT_MSEC);
- if ((rc == 4) && (function == EEH_OPT_THAW_MMIO))
+ if (rc > 0 && (rc & EEH_STATE_MMIO_ENABLED) &&
+ (function == EEH_OPT_THAW_MMIO))
return 0;
return rc;
@@ -900,7 +839,7 @@ int eeh_reset_pe(struct pci_dn *pdn)
eeh_reset_pe_once(pdn);
rc = eeh_wait_for_slot_status(pdn, PCI_BUS_RESET_WAIT_MSEC);
- if (rc == 0)
+ if (rc == (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE))
return 0;
if (rc < 0) {
@@ -1057,7 +996,6 @@ void eeh_configure_bridge(struct pci_dn *pdn)
*/
static void *eeh_early_enable(struct device_node *dn, void *data)
{
- unsigned int rets[3];
int ret;
const u32 *class_code = of_get_property(dn, "class-code", NULL);
const u32 *vendor_id = of_get_property(dn, "vendor-id", NULL);
@@ -1109,8 +1047,8 @@ static void *eeh_early_enable(struct device_node *dn, void *data)
* where EEH is not supported. Verify support
* explicitly.
*/
- ret = eeh_read_slot_reset_state(pdn, rets);
- if ((ret == 0) && (rets[1] == 1))
+ ret = eeh_ops->get_state(pdn->node, NULL);
+ if (ret > 0 && ret != EEH_STATE_NOT_SUPPORT)
enable = 1;
}
@@ -1232,8 +1170,6 @@ void __init eeh_init(void)
return;
ibm_set_slot_reset = rtas_token("ibm,set-slot-reset");
- 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_configure_bridge = rtas_token("ibm,configure-bridge");
ibm_configure_pe = rtas_token("ibm,configure-pe");
diff --git a/arch/powerpc/platforms/pseries/eeh_driver.c b/arch/powerpc/platforms/pseries/eeh_driver.c
index 02eab3b..4c6e0c1c 100644
--- a/arch/powerpc/platforms/pseries/eeh_driver.c
+++ b/arch/powerpc/platforms/pseries/eeh_driver.c
@@ -397,7 +397,7 @@ struct pci_dn * handle_eeh_events (struct eeh_event *event)
/* Get the current PCI slot state. This can take a long time,
* sometimes over 3 seconds for certain systems. */
rc = eeh_wait_for_slot_status (frozen_pdn, MAX_WAIT_FOR_RECOVERY*1000);
- if (rc < 0) {
+ if (rc < 0 || rc == EEH_STATE_NOT_SUPPORT) {
printk(KERN_WARNING "EEH: Permanent failure\n");
goto hard_fail;
}
diff --git a/arch/powerpc/platforms/pseries/eeh_pseries.c b/arch/powerpc/platforms/pseries/eeh_pseries.c
index 2b9543a..39567b2 100644
--- a/arch/powerpc/platforms/pseries/eeh_pseries.c
+++ b/arch/powerpc/platforms/pseries/eeh_pseries.c
@@ -238,7 +238,75 @@ static int pseries_eeh_get_pe_addr(struct device_node *dn)
*/
static int pseries_eeh_get_state(struct device_node *dn, int *state)
{
- return 0;
+ struct pci_dn *pdn;
+ int config_addr;
+ int ret;
+ int rets[4];
+ int result;
+
+ /* Figure out PE config address if possible */
+ pdn = PCI_DN(dn);
+ config_addr = pdn->eeh_config_addr;
+ if (pdn->eeh_pe_config_addr)
+ config_addr = pdn->eeh_pe_config_addr;
+
+ if (ibm_read_slot_reset_state2 != RTAS_UNKNOWN_SERVICE) {
+ ret = rtas_call(ibm_read_slot_reset_state2, 3, 4, rets,
+ config_addr, BUID_HI(pdn->phb->buid),
+ BUID_LO(pdn->phb->buid));
+ } else if (ibm_read_slot_reset_state != RTAS_UNKNOWN_SERVICE) {
+ /* Fake PE unavailable info */
+ rets[2] = 0;
+ ret = rtas_call(ibm_read_slot_reset_state, 3, 3, rets,
+ config_addr, BUID_HI(pdn->phb->buid),
+ BUID_LO(pdn->phb->buid));
+ } else {
+ return EEH_STATE_NOT_SUPPORT;
+ }
+
+ if (ret)
+ return ret;
+
+ /* Parse the result out */
+ result = 0;
+ if (rets[1]) {
+ switch(rets[0]) {
+ case 0:
+ result &= ~EEH_STATE_RESET_ACTIVE;
+ result |= EEH_STATE_MMIO_ACTIVE;
+ result |= EEH_STATE_DMA_ACTIVE;
+ break;
+ case 1:
+ result |= EEH_STATE_RESET_ACTIVE;
+ result |= EEH_STATE_MMIO_ACTIVE;
+ result |= EEH_STATE_DMA_ACTIVE;
+ break;
+ case 2:
+ result &= ~EEH_STATE_RESET_ACTIVE;
+ result &= ~EEH_STATE_MMIO_ACTIVE;
+ result &= ~EEH_STATE_DMA_ACTIVE;
+ break;
+ case 4:
+ result &= ~EEH_STATE_RESET_ACTIVE;
+ result &= ~EEH_STATE_MMIO_ACTIVE;
+ result &= ~EEH_STATE_DMA_ACTIVE;
+ result |= EEH_STATE_MMIO_ENABLED;
+ break;
+ case 5:
+ if (rets[2]) {
+ if (state) *state = rets[2];
+ result = EEH_STATE_UNAVAILABLE;
+ } else {
+ result = EEH_STATE_NOT_SUPPORT;
+ }
+ default:
+ result = EEH_STATE_NOT_SUPPORT;
+ }
+ } else {
+ result = EEH_STATE_NOT_SUPPORT;
+ }
+
+ return result;
}
/**
--
1.7.5.4
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox