* RE: [PATCH 3/4] powerpc/p3060qds: Add support for P3060QDS board
From: Liu Shengzhou-B36685 @ 2011-09-15 2:22 UTC (permalink / raw)
To: Tabi Timur-B04825, Kumar Gala; +Cc: linuxppc-dev@ozlabs.org
In-Reply-To: <CAOZdJXW54KiExVB3PBj31WfQxgct4Tfk3eh3F0Fw_YCoL_AUTw@mail.gmail.com>
This patch was obsolete, please see http://linux.freescale.net/patchwork/pa=
tch/12224/ posted on Sep 9.
> -----Original Message-----
> From: Tabi Timur-B04825
> Sent: Thursday, September 15, 2011 5:18 AM
> To: Kumar Gala
> Cc: linuxppc-dev@ozlabs.org; Liu Shengzhou-B36685
> Subject: Re: [PATCH 3/4] powerpc/p3060qds: Add support for P3060QDS board
>=20
> On Thu, Sep 1, 2011 at 2:26 PM, Kumar Gala <galak@kernel.crashing.org>
> wrote:
>=20
> > + =A0 =A0 =A0 localbus@ffe124000 {
> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 reg =3D <0xf 0xfe124000 0 0x1000>;
> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 ranges =3D <0 0 0xf 0xe8000000 0x08000000=
>;
> > +
> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 flash@0,0 {
> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 compatible =3D "cfi-flash=
";
> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 reg =3D <0 0 0x08000000>;
> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 bank-width =3D <2>;
> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 device-width =3D <2>;
> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 };
> > +
> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 nand@2,0 {
> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 #address-cells =3D <1>;
> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 #size-cells =3D <1>;
> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 compatible =3D "fsl,elbc-=
fcm-nand";
> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 reg =3D <0x2 0x0 0x40000>=
;
>=20
> The 'ranges' property is missing a line for the NAND device. You need
> something like this:
>=20
> ranges =3D <0 0 0xf 0xe8000000 0x08000000
> 2 0 0xf 0xffa00000 0x00040000>;
>=20
> I don't know if these are the right numbers, but the "2" line is missing.
>=20
> --
> Timur Tabi
> Linux kernel developer at Freescale
^ permalink raw reply
* [PATCH] [v2] powerpc/85xx: clean up FPGA device tree nodes for Freecsale QorIQ boards
From: Timur Tabi @ 2011-09-14 21:42 UTC (permalink / raw)
To: kumar.gala, linuxppc-dev, devicetree-discuss
Standarize and document the FPGA nodes used on Freescale QorIQ reference
boards. There are different kinds of FPGAs used on the boards, but
only two are currently standard: "pixis" and "qixis". Although there are
minor differences among the boards that have one kind of FPGA, most of the
functionality is the same, so it makes sense to create common compatibility
strings.
Signed-off-by: Timur Tabi <timur@freescale.com>
---
v2: removed references to the CPLD
This patch applies on top of "[PATCH 3/4] powerpc/p3060qds: Add support for
P3060QDS board", but it also assumes that the 'ranges' property for the
localbus node on the P3060QDS device tree includes the missing line for
the NAND devices.
.../devicetree/bindings/powerpc/fsl/board.txt | 30 ++++++++++++--------
arch/powerpc/boot/dts/p1022ds.dts | 2 +-
arch/powerpc/boot/dts/p2020ds.dts | 5 +++
arch/powerpc/boot/dts/p3041ds.dts | 4 +-
arch/powerpc/boot/dts/p3060qds.dts | 8 ++++-
arch/powerpc/boot/dts/p4080ds.dts | 8 ++++-
arch/powerpc/boot/dts/p5020ds.dts | 4 +-
7 files changed, 42 insertions(+), 19 deletions(-)
diff --git a/Documentation/devicetree/bindings/powerpc/fsl/board.txt b/Documentation/devicetree/bindings/powerpc/fsl/board.txt
index 39e9415..ba46a7a 100644
--- a/Documentation/devicetree/bindings/powerpc/fsl/board.txt
+++ b/Documentation/devicetree/bindings/powerpc/fsl/board.txt
@@ -1,3 +1,8 @@
+Freescale Reference Board Bindings
+
+This document describes device tree bindings for various devices that
+exist on some Freescale reference boards.
+
* Board Control and Status (BCSR)
Required properties:
@@ -12,25 +17,26 @@ Example:
reg = <f8000000 8000>;
};
-* Freescale on board FPGA
+* Freescale on-board FPGA
This is the memory-mapped registers for on board FPGA.
Required properities:
-- compatible : should be "fsl,fpga-pixis".
-- reg : should contain the address and the length of the FPPGA register
- set.
+- compatible: should be a board-specific string followed by a string
+ indicating the type of FPGA. Example:
+ "fsl,<board>-pixis", "fsl,fpga-pixis"
+- reg: should contain the address and the length of the FPGA register set.
- interrupt-parent: should specify phandle for the interrupt controller.
-- interrupts : should specify event (wakeup) IRQ.
+- interrupts: should specify event (wakeup) IRQ.
-Example (MPC8610HPCD):
+Example (P1022DS):
- board-control@e8000000 {
- compatible = "fsl,fpga-pixis";
- reg = <0xe8000000 32>;
- interrupt-parent = <&mpic>;
- interrupts = <8 8>;
- };
+ board-control@3,0 {
+ compatible = "fsl,p1022ds-pixis", "fsl,fpga-pixis";
+ reg = <3 0 0x30>;
+ interrupt-parent = <&mpic>;
+ interrupts = <8 8 0 0>;
+ };
* Freescale BCSR GPIO banks
diff --git a/arch/powerpc/boot/dts/p1022ds.dts b/arch/powerpc/boot/dts/p1022ds.dts
index 1be9743..97a0b87 100644
--- a/arch/powerpc/boot/dts/p1022ds.dts
+++ b/arch/powerpc/boot/dts/p1022ds.dts
@@ -150,7 +150,7 @@
};
board-control@3,0 {
- compatible = "fsl,p1022ds-pixis";
+ compatible = "fsl,p1022ds-pixis", "fsl,fpga-pixis";
reg = <3 0 0x30>;
interrupt-parent = <&mpic>;
/*
diff --git a/arch/powerpc/boot/dts/p2020ds.dts b/arch/powerpc/boot/dts/p2020ds.dts
index dae4031..d1e52f3 100644
--- a/arch/powerpc/boot/dts/p2020ds.dts
+++ b/arch/powerpc/boot/dts/p2020ds.dts
@@ -118,6 +118,11 @@
};
};
+ board-control@3,0 {
+ compatible = "fsl,p2020ds-pixis", "fsl,fpga-pixis";
+ reg = <0x3 0x0 0x30>;
+ };
+
nand@4,0 {
compatible = "fsl,elbc-fcm-nand";
reg = <0x4 0x0 0x40000>;
diff --git a/arch/powerpc/boot/dts/p3041ds.dts b/arch/powerpc/boot/dts/p3041ds.dts
index 69cae67..92937ce 100644
--- a/arch/powerpc/boot/dts/p3041ds.dts
+++ b/arch/powerpc/boot/dts/p3041ds.dts
@@ -147,8 +147,8 @@
};
board-control@3,0 {
- compatible = "fsl,p3041ds-pixis";
- reg = <3 0 0x20>;
+ compatible = "fsl,p3041ds-pixis", "fsl,fpga-pixis";
+ reg = <3 0 0x30>;
};
};
diff --git a/arch/powerpc/boot/dts/p3060qds.dts b/arch/powerpc/boot/dts/p3060qds.dts
index 55edfd0..6758852 100644
--- a/arch/powerpc/boot/dts/p3060qds.dts
+++ b/arch/powerpc/boot/dts/p3060qds.dts
@@ -109,7 +109,8 @@
localbus@ffe124000 {
reg = <0xf 0xfe124000 0 0x1000>;
ranges = <0 0 0xf 0xe8000000 0x08000000
- 2 0 0xf 0xffa00000 0x00040000>;
+ 2 0 0xf 0xffa00000 0x00040000
+ 3 0 0xf 0xffdf0000 0x00008000>;
flash@0,0 {
compatible = "cfi-flash";
@@ -155,6 +156,11 @@
reg = <0x1f000000 0x21000000>;
};
};
+
+ board-control@3,0 {
+ compatible = "fsl,p3060qds-qixis", "fsl,fpga-qixis";
+ reg = <3 0 0x100>;
+ };
};
pci0: pcie@ffe200000 {
diff --git a/arch/powerpc/boot/dts/p4080ds.dts b/arch/powerpc/boot/dts/p4080ds.dts
index eb11098..a26cf15 100644
--- a/arch/powerpc/boot/dts/p4080ds.dts
+++ b/arch/powerpc/boot/dts/p4080ds.dts
@@ -108,7 +108,8 @@
localbus@ffe124000 {
reg = <0xf 0xfe124000 0 0x1000>;
- ranges = <0 0 0xf 0xe8000000 0x08000000>;
+ ranges = <0 0 0xf 0xe8000000 0x08000000
+ 3 0 0xf 0xffdf0000 0x00008000>;
flash@0,0 {
compatible = "cfi-flash";
@@ -116,6 +117,11 @@
bank-width = <2>;
device-width = <2>;
};
+
+ board-control@3,0 {
+ compatible = "fsl,p4080ds-pixis", "fsl,fpga-pixis";
+ reg = <3 0 0x30>;
+ };
};
pci0: pcie@ffe200000 {
diff --git a/arch/powerpc/boot/dts/p5020ds.dts b/arch/powerpc/boot/dts/p5020ds.dts
index 8366e2f..b959986 100644
--- a/arch/powerpc/boot/dts/p5020ds.dts
+++ b/arch/powerpc/boot/dts/p5020ds.dts
@@ -147,8 +147,8 @@
};
board-control@3,0 {
- compatible = "fsl,p5020ds-pixis";
- reg = <3 0 0x20>;
+ compatible = "fsl,p5020ds-pixis", "fsl,fpga-pixis";
+ reg = <3 0 0x30>;
};
};
--
1.7.3.4
^ permalink raw reply related
* Re: [PATCH 3/4] powerpc/p3060qds: Add support for P3060QDS board
From: Tabi Timur-B04825 @ 2011-09-14 21:17 UTC (permalink / raw)
To: Kumar Gala; +Cc: linuxppc-dev@ozlabs.org, Liu Shengzhou-B36685
In-Reply-To: <1314905180-4417-1-git-send-email-galak@kernel.crashing.org>
On Thu, Sep 1, 2011 at 2:26 PM, Kumar Gala <galak@kernel.crashing.org> wrot=
e:
> + =A0 =A0 =A0 localbus@ffe124000 {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 reg =3D <0xf 0xfe124000 0 0x1000>;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 ranges =3D <0 0 0xf 0xe8000000 0x08000000>;
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 flash@0,0 {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 compatible =3D "cfi-flash";
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 reg =3D <0 0 0x08000000>;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 bank-width =3D <2>;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 device-width =3D <2>;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 };
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 nand@2,0 {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 #address-cells =3D <1>;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 #size-cells =3D <1>;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 compatible =3D "fsl,elbc-fc=
m-nand";
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 reg =3D <0x2 0x0 0x40000>;
The 'ranges' property is missing a line for the NAND device. You need
something like this:
ranges =3D <0 0 0xf 0xe8000000 0x08000000
2 0 0xf 0xffa00000 0x00040000>;
I don't know if these are the right numbers, but the "2" line is missing.
--=20
Timur Tabi
Linux kernel developer at Freescale=
^ permalink raw reply
* [PATCH] memory hotplug: Refuse to add unaligned memory regions
From: Anton Blanchard @ 2011-09-14 20:26 UTC (permalink / raw)
To: gregkh, nfont; +Cc: linuxppc-dev, linux-kernel
The sysfs memory probe interface allows unaligned regions
to be added:
# echo 0xffffff > /sys/devices/system/memory/probe
# cat /proc/iomem
00ffffff-01fffffe : System RAM
01ffffff-02fffffe : System RAM
02ffffff-03fffffe : System RAM
03ffffff-04fffffe : System RAM
04ffffff-05fffffe : System RAM
Return -EINVAL instead of creating these bad regions.
Signed-off-by: Anton Blanchard <anton@samba.org>
---
Index: linux-build/drivers/base/memory.c
===================================================================
--- linux-build.orig/drivers/base/memory.c 2011-08-11 08:25:55.005941391 +1000
+++ linux-build/drivers/base/memory.c 2011-08-11 08:28:27.938580440 +1000
@@ -380,9 +380,13 @@ memory_probe_store(struct class *class,
u64 phys_addr;
int nid;
int i, ret;
+ unsigned long pages_per_block = PAGES_PER_SECTION * sections_per_block;
phys_addr = simple_strtoull(buf, NULL, 0);
+ if (phys_addr & ((pages_per_block << PAGE_SHIFT) - 1))
+ return -EINVAL;
+
for (i = 0; i < sections_per_block; i++) {
nid = memory_add_physaddr_to_nid(phys_addr);
ret = add_memory(nid, phys_addr,
^ permalink raw reply
* [PATCH] powerpc: Fix deadlock in icswx code
From: Anton Blanchard @ 2011-09-14 19:43 UTC (permalink / raw)
To: benh, paulus; +Cc: linuxppc-dev
In-Reply-To: <20110915052313.7127520d@kryten>
The icswx code introduced an A-B B-A deadlock:
CPU0 CPU1
---- ----
lock(&anon_vma->mutex);
lock(&mm->mmap_sem);
lock(&anon_vma->mutex);
lock(&mm->mmap_sem);
Instead of using the mmap_sem to keep mm_users constant, take the
page table spinlock.
Signed-off-by: Anton Blanchard <anton@samba.org>
Cc: <stable@kernel.org>
---
diff --git a/arch/powerpc/mm/mmu_context_hash64.c b/arch/powerpc/mm/mmu_context_hash64.c
index 3bafc3d..4ff587e 100644
--- a/arch/powerpc/mm/mmu_context_hash64.c
+++ b/arch/powerpc/mm/mmu_context_hash64.c
@@ -136,8 +136,8 @@ int use_cop(unsigned long acop, struct mm_struct *mm)
if (!mm || !acop)
return -EINVAL;
- /* We need to make sure mm_users doesn't change */
- down_read(&mm->mmap_sem);
+ /* The page_table_lock ensures mm_users won't change under us */
+ spin_lock(&mm->page_table_lock);
spin_lock(mm->context.cop_lockp);
if (mm->context.cop_pid == COP_PID_NONE) {
@@ -164,7 +164,7 @@ int use_cop(unsigned long acop, struct mm_struct *mm)
out:
spin_unlock(mm->context.cop_lockp);
- up_read(&mm->mmap_sem);
+ spin_unlock(&mm->page_table_lock);
return ret;
}
@@ -185,8 +185,8 @@ void drop_cop(unsigned long acop, struct mm_struct *mm)
if (WARN_ON_ONCE(!mm))
return;
- /* We need to make sure mm_users doesn't change */
- down_read(&mm->mmap_sem);
+ /* The page_table_lock ensures mm_users won't change under us */
+ spin_lock(&mm->page_table_lock);
spin_lock(mm->context.cop_lockp);
mm->context.acop &= ~acop;
@@ -213,7 +213,7 @@ void drop_cop(unsigned long acop, struct mm_struct *mm)
}
spin_unlock(mm->context.cop_lockp);
- up_read(&mm->mmap_sem);
+ spin_unlock(&mm->page_table_lock);
}
EXPORT_SYMBOL_GPL(drop_cop);
^ permalink raw reply related
* Re: [PATCH] powerpc: Fix build dependencies for epapr.c which needs libfdt.h
From: McClintock Matthew-B29882 @ 2011-09-13 22:43 UTC (permalink / raw)
To: David Gibson; +Cc: linuxppc-dev@lists.ozlabs.org
In-Reply-To: <20110720134907.GN6399@yookeroo.fritz.box>
On Wed, Jul 20, 2011 at 8:49 AM, David Gibson
<david@gibson.dropbear.id.au> wrote:
> On Tue, Jul 19, 2011 at 11:22:44AM -0500, Matthew McClintock wrote:
>> Currently, the build can (very rarely) fail to build because libfdt.h ha=
s
>> not been created or is in the process of being copied.
>>
>> Signed-off-by: Matthew McClintock <msm@freescale.com>
>
> Looks sane to me.
ping? I'm still seeing this issue.
-M=
^ permalink raw reply
* [PATCH 1/1] hvcs: Ensure page aligned partner info buffer
From: Brian King @ 2011-09-13 21:22 UTC (permalink / raw)
To: benh; +Cc: brking, linuxppc-dev
The Power platform requires the partner info buffer to be page aligned
otherwise it will fail the partner info hcall with H_PARAMETER. Switch
from using kmalloc to allocate this buffer to __get_free_page to ensure
page alignment.
Signed-off-by: Brian King <brking@linux.vnet.ibm.com>
---
drivers/tty/hvc/hvcs.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff -puN drivers/tty/hvc/hvcs.c~hvcs_pi_buf_alloc drivers/tty/hvc/hvcs.c
--- linux-2.6/drivers/tty/hvc/hvcs.c~hvcs_pi_buf_alloc 2011-09-09 16:00:25.000000000 -0500
+++ linux-2.6-bjking1/drivers/tty/hvc/hvcs.c 2011-09-09 16:07:08.000000000 -0500
@@ -1532,7 +1532,7 @@ static int __devinit hvcs_initialize(voi
goto register_fail;
}
- hvcs_pi_buff = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ hvcs_pi_buff = (unsigned long *) __get_free_page(GFP_KERNEL);
if (!hvcs_pi_buff) {
rc = -ENOMEM;
goto buff_alloc_fail;
@@ -1548,7 +1548,7 @@ static int __devinit hvcs_initialize(voi
return 0;
kthread_fail:
- kfree(hvcs_pi_buff);
+ free_page((unsigned long)hvcs_pi_buff);
buff_alloc_fail:
tty_unregister_driver(hvcs_tty_driver);
register_fail:
@@ -1597,7 +1597,7 @@ static void __exit hvcs_module_exit(void
kthread_stop(hvcs_task);
spin_lock(&hvcs_pi_lock);
- kfree(hvcs_pi_buff);
+ free_page((unsigned long)hvcs_pi_buff);
hvcs_pi_buff = NULL;
spin_unlock(&hvcs_pi_lock);
_
^ permalink raw reply
* [PATCH] powerpc/fsl_msi: fix support for multiple MSI ranges
From: Timur Tabi @ 2011-09-13 21:17 UTC (permalink / raw)
To: kumar.gala, scottwood, benh, linuxppc-dev
Commit 6820fead ("powerpc/fsl_msi: Handle msi-available-ranges better") added
support for multiple ranges in the msi-available-ranges property, but it
miscalculated the MSIR index when multiple ranges are used.
Signed-off-by: Timur Tabi <timur@freescale.com>
---
arch/powerpc/sysdev/fsl_msi.c | 8 +++++---
1 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/arch/powerpc/sysdev/fsl_msi.c b/arch/powerpc/sysdev/fsl_msi.c
index 419a772..1cca251 100644
--- a/arch/powerpc/sysdev/fsl_msi.c
+++ b/arch/powerpc/sysdev/fsl_msi.c
@@ -296,7 +296,7 @@ static int __devinit fsl_msi_setup_hwirq(struct fsl_msi *msi,
}
msi->msi_virqs[irq_index] = virt_msir;
- cascade_data->index = offset + irq_index;
+ cascade_data->index = offset;
cascade_data->msi_data = msi;
irq_set_handler_data(virt_msir, cascade_data);
irq_set_chained_handler(virt_msir, fsl_msi_cascade);
@@ -376,8 +376,10 @@ static int __devinit fsl_of_msi_probe(struct platform_device *dev)
goto error_out;
}
- if (!p)
+ if (!p) {
p = all_avail;
+ len = sizeof(all_avail);
+ }
for (irq_index = 0, i = 0; i < len / (2 * sizeof(u32)); i++) {
if (p[i * 2] % IRQS_PER_MSI_REG ||
@@ -393,7 +395,7 @@ static int __devinit fsl_of_msi_probe(struct platform_device *dev)
count = p[i * 2 + 1] / IRQS_PER_MSI_REG;
for (j = 0; j < count; j++, irq_index++) {
- err = fsl_msi_setup_hwirq(msi, dev, offset, irq_index);
+ err = fsl_msi_setup_hwirq(msi, dev, offset + j, irq_index);
if (err)
goto error_out;
}
--
1.7.3.4
^ permalink raw reply related
* Re: [PATCH] powerpc/fsl_msi: Handle msi-available-ranges better
From: Scott Wood @ 2011-09-13 18:33 UTC (permalink / raw)
To: Tabi Timur-B04825
Cc: Wood Scott-B07421, devicetree-discuss@lists.ozlabs.org,
linuxppc-dev@lists.ozlabs.org
In-Reply-To: <CAOZdJXVm1YL-T8i26O0BiB-uTkXUDnYQdacU8k52aG-K0pZ1Lg@mail.gmail.com>
On 09/07/2011 11:20 AM, Tabi Timur-B04825 wrote:
> The problem is that both offset and irq_index are being incremented in
> the loop, and cascade_data->index is set to the sum of the two.
>
> Perhaps you meant this:
>
> err = fsl_msi_setup_hwirq(msi, dev, offset, j);
That's not right either, it would retrieve the wrong IRQ from the
interrupts property if you have holes -- try with something like
{ 0 64, 128, 64 }. The desired behavior there is:
offset = 0 irq_index = 0
offset = 1 irq_index = 1
offset = 4 irq_index = 2
offset = 5 irq_index = 3
I think the right code (untested) might be:
err = fsl_msi_setup_hwirq(msi, dev, offset + j, irq_index);
and
cascade_data->index = offset;
-Scott
^ permalink raw reply
* [PATCH] KVM: PPC: Fix build failure with HV KVM and CBE
From: Alexander Graf @ 2011-09-13 14:15 UTC (permalink / raw)
To: kvm-ppc; +Cc: linuxppc-dev, dvaleev, paulus, kvm
When running with HV KVM and CBE config options enabled, I get
build failures like the following:
arch/powerpc/kernel/head_64.o: In function `cbe_system_error_hv':
(.text+0x1228): undefined reference to `do_kvm_0x1202'
arch/powerpc/kernel/head_64.o: In function `cbe_maintenance_hv':
(.text+0x1628): undefined reference to `do_kvm_0x1602'
arch/powerpc/kernel/head_64.o: In function `cbe_thermal_hv':
(.text+0x1828): undefined reference to `do_kvm_0x1802'
This is because we jump to a KVM handler when HV is enabled, but we
only generate the handler with PR KVM mode.
Signed-off-by: Alexander Graf <agraf@suse.de>
---
arch/powerpc/kernel/exceptions-64s.S | 6 +++---
1 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/arch/powerpc/kernel/exceptions-64s.S b/arch/powerpc/kernel/exceptions-64s.S
index 29ddd8b..396d080 100644
--- a/arch/powerpc/kernel/exceptions-64s.S
+++ b/arch/powerpc/kernel/exceptions-64s.S
@@ -267,7 +267,7 @@ vsx_unavailable_pSeries_1:
#ifdef CONFIG_CBE_RAS
STD_EXCEPTION_HV(0x1200, 0x1202, cbe_system_error)
- KVM_HANDLER_PR_SKIP(PACA_EXGEN, EXC_HV, 0x1202)
+ KVM_HANDLER_SKIP(PACA_EXGEN, EXC_HV, 0x1202)
#endif /* CONFIG_CBE_RAS */
STD_EXCEPTION_PSERIES(0x1300, 0x1300, instruction_breakpoint)
@@ -275,7 +275,7 @@ vsx_unavailable_pSeries_1:
#ifdef CONFIG_CBE_RAS
STD_EXCEPTION_HV(0x1600, 0x1602, cbe_maintenance)
- KVM_HANDLER_PR_SKIP(PACA_EXGEN, EXC_HV, 0x1602)
+ KVM_HANDLER_SKIP(PACA_EXGEN, EXC_HV, 0x1602)
#endif /* CONFIG_CBE_RAS */
STD_EXCEPTION_PSERIES(0x1700, 0x1700, altivec_assist)
@@ -283,7 +283,7 @@ vsx_unavailable_pSeries_1:
#ifdef CONFIG_CBE_RAS
STD_EXCEPTION_HV(0x1800, 0x1802, cbe_thermal)
- KVM_HANDLER_PR_SKIP(PACA_EXGEN, EXC_HV, 0x1802)
+ KVM_HANDLER_SKIP(PACA_EXGEN, EXC_HV, 0x1802)
#endif /* CONFIG_CBE_RAS */
. = 0x3000
--
1.6.0.2
^ permalink raw reply related
* RE: [PATCH] eSDHC: Access Freescale eSDHC registers by 32-bit
From: Zang Roy-R61911 @ 2011-09-13 4:10 UTC (permalink / raw)
To: Wolfram Sang
Cc: Xu Lei-B33228, linux-mmc@vger.kernel.org,
akpm@linux-foundation.org, cjb@laptop.org,
linuxppc-dev@lists.ozlabs.org
In-Reply-To: <20110909130855.GR1912@pengutronix.de>
> -----Original Message-----
> From: linux-mmc-owner@vger.kernel.org [mailto:linux-mmc-owner@vger.kernel=
.org]
> On Behalf Of Wolfram Sang
> Sent: Friday, September 09, 2011 21:09 PM
> To: Zang Roy-R61911
> Cc: cjb@laptop.org; linux-mmc@vger.kernel.org; linuxppc-dev@lists.ozlabs.=
org;
> akpm@linux-foundation.org; cbouatmailru@gmail.com; Xu Lei-B33228; Kumar G=
ala
> Subject: Re: [PATCH] eSDHC: Access Freescale eSDHC registers by 32-bit
>=20
>=20
> > Previously, I sent this patch together with another one. But
> > technically they are separated patches. I got some comments from
> > Anton about the patch. I will try to address the comment. but for this
> > one, it is a stand along patch. I'd like to push it first.
>=20
> Okay.
Good.
>=20
> > I do not see any comment about byte swapping about this patch.
> > you may have mis-understanding here.
>=20
> I was misguided, yes, sorry. BE/LE issues with additional byteswapping
> are a mind twister :/ Having a second look, it looks okay to me. One
> could think about putting this into sdhci_platform as well, but this can
> be done later if needed. Sadly, I currently don't have hardware to test
> it.
I tested on 8536 and P4080/P5020/P3041 platform.
Anyone can help to pull it in?
Thanks.
Roy
^ permalink raw reply
* Re: [PATCH 06/21] powerpc: Add skeleton PowerNV platform
From: Geert Uytterhoeven @ 2011-09-12 6:02 UTC (permalink / raw)
To: Michael Neuling; +Cc: linuxppc-dev
In-Reply-To: <9312.1315790251@neuling.org>
Hi Mikey,
On Mon, Sep 12, 2011 at 03:17, Michael Neuling <mikey@neuling.org> wrote:
>> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
>
> Giggity-Giggity-by: Michael Neuling <mikey@neuling.org>
Burps?
Gr{oetje,eeting}s,
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A0 Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k=
.org
In personal conversations with technical people, I call myself a hacker. Bu=
t
when I'm talking to journalists I just say "programmer" or something like t=
hat.
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A0 =C2=A0 =C2=A0=C2=A0 =C2=A0=C2=A0 -- Linus Torvalds
^ permalink raw reply
* Re: [PATCH 06/21] powerpc: Add skeleton PowerNV platform
From: Michael Neuling @ 2011-09-12 1:17 UTC (permalink / raw)
To: Benjamin Herrenschmidt; +Cc: linuxppc-dev
In-Reply-To: <1315664408-16797-6-git-send-email-benh@kernel.crashing.org>
In message <1315664408-16797-6-git-send-email-benh@kernel.crashing.org> you wro
te:
> This adds a skeletton for the new Power "Non Virtualized"
> platform which will be used by machines supporting running
> without an hypervisor, for example in order to run KVM.
>
> These machines will be using a new firmware called OPAL
> for which the support will be provided by later patches.
>
> The PowerNV platform is intended to be also usable under
> the BML environment used internally for early CPU bringup
> which is why the code also supports using RTAS instead of
> OPAL in various places.
>
> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Giggity-Giggity-by: Michael Neuling <mikey@neuling.org>
> ---
> arch/powerpc/boot/Makefile | 1 +
> arch/powerpc/platforms/Kconfig | 1 +
> arch/powerpc/platforms/Makefile | 1 +
> arch/powerpc/platforms/powernv/Kconfig | 12 +++
> arch/powerpc/platforms/powernv/Makefile | 2 +
> arch/powerpc/platforms/powernv/powernv.h | 10 ++
> arch/powerpc/platforms/powernv/setup.c | 153 ++++++++++++++++++++++++++++
++
> arch/powerpc/platforms/powernv/smp.c | 83 ++++++++++++++++
> 8 files changed, 263 insertions(+), 0 deletions(-)
> create mode 100644 arch/powerpc/platforms/powernv/Kconfig
> create mode 100644 arch/powerpc/platforms/powernv/Makefile
> create mode 100644 arch/powerpc/platforms/powernv/powernv.h
> create mode 100644 arch/powerpc/platforms/powernv/setup.c
> create mode 100644 arch/powerpc/platforms/powernv/smp.c
>
> diff --git a/arch/powerpc/boot/Makefile b/arch/powerpc/boot/Makefile
> index c26200b..52cde90 100644
> --- a/arch/powerpc/boot/Makefile
> +++ b/arch/powerpc/boot/Makefile
> @@ -171,6 +171,7 @@ quiet_cmd_wrap = WRAP $@
> $(if $3, -s $3)$(if $4, -d $4)$(if $5, -i $5) vmlinux
>
> image-$(CONFIG_PPC_PSERIES) += zImage.pseries
> +image-$(CONFIG_PPC_POWERNV) += zImage.pseries
> image-$(CONFIG_PPC_MAPLE) += zImage.maple
> image-$(CONFIG_PPC_IBM_CELL_BLADE) += zImage.pseries
> image-$(CONFIG_PPC_PS3) += dtbImage.ps3
> diff --git a/arch/powerpc/platforms/Kconfig b/arch/powerpc/platforms/Kconfig
> index b9ba861..6de27d2 100644
> --- a/arch/powerpc/platforms/Kconfig
> +++ b/arch/powerpc/platforms/Kconfig
> @@ -1,5 +1,6 @@
> menu "Platform support"
>
> +source "arch/powerpc/platforms/powernv/Kconfig"
> source "arch/powerpc/platforms/pseries/Kconfig"
> source "arch/powerpc/platforms/iseries/Kconfig"
> source "arch/powerpc/platforms/chrp/Kconfig"
> diff --git a/arch/powerpc/platforms/Makefile b/arch/powerpc/platforms/Makefil
e
> index 73e2116..2635a22 100644
> --- a/arch/powerpc/platforms/Makefile
> +++ b/arch/powerpc/platforms/Makefile
> @@ -14,6 +14,7 @@ obj-$(CONFIG_PPC_82xx) += 82xx/
> obj-$(CONFIG_PPC_83xx) += 83xx/
> obj-$(CONFIG_FSL_SOC_BOOKE) += 85xx/
> obj-$(CONFIG_PPC_86xx) += 86xx/
> +obj-$(CONFIG_PPC_POWERNV) += powernv/
> obj-$(CONFIG_PPC_PSERIES) += pseries/
> obj-$(CONFIG_PPC_ISERIES) += iseries/
> obj-$(CONFIG_PPC_MAPLE) += maple/
> diff --git a/arch/powerpc/platforms/powernv/Kconfig b/arch/powerpc/platforms/
powernv/Kconfig
> new file mode 100644
> index 0000000..5cd04f5
> --- /dev/null
> +++ b/arch/powerpc/platforms/powernv/Kconfig
> @@ -0,0 +1,12 @@
> +config PPC_POWERNV
> + depends on PPC64 && PPC_BOOK3S
> + bool "IBM PowerNV (Non-Virtualized) platform support"
> + select PPC_RTAS
> + select PPC_NATIVE
> + select PPC_XICS
> + select PPC_ICP_NATIVE
> + select PPC_ICS_RTAS
> + select PPC_P7_NAP
> + select PPC_PCI_CHOICE if EMBEDDED
> + default y
> +
> diff --git a/arch/powerpc/platforms/powernv/Makefile b/arch/powerpc/platforms
/powernv/Makefile
> new file mode 100644
> index 0000000..1c43250
> --- /dev/null
> +++ b/arch/powerpc/platforms/powernv/Makefile
> @@ -0,0 +1,2 @@
> +obj-y += setup.o
> +obj-$(CONFIG_SMP) += smp.o
> diff --git a/arch/powerpc/platforms/powernv/powernv.h b/arch/powerpc/platform
s/powernv/powernv.h
> new file mode 100644
> index 0000000..35b7160
> --- /dev/null
> +++ b/arch/powerpc/platforms/powernv/powernv.h
> @@ -0,0 +1,10 @@
> +#ifndef _POWERNV_H
> +#define _POWERNV_H
> +
> +#ifdef CONFIG_SMP
> +extern void pnv_smp_init(void);
> +#else
> +static inline void pnv_smp_init(void) { }
> +#endif
> +
> +#endif /* _POWERNV_H */
> diff --git a/arch/powerpc/platforms/powernv/setup.c b/arch/powerpc/platforms/
powernv/setup.c
> new file mode 100644
> index 0000000..569f9cc
> --- /dev/null
> +++ b/arch/powerpc/platforms/powernv/setup.c
> @@ -0,0 +1,153 @@
> +/*
> + * PowerNV setup code.
> + *
> + * Copyright 2011 IBM Corp.
> + *
> + * 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.
> + */
> +
> +#undef DEBUG
> +
> +#include <linux/cpu.h>
> +#include <linux/errno.h>
> +#include <linux/sched.h>
> +#include <linux/kernel.h>
> +#include <linux/tty.h>
> +#include <linux/reboot.h>
> +#include <linux/init.h>
> +#include <linux/console.h>
> +#include <linux/delay.h>
> +#include <linux/irq.h>
> +#include <linux/seq_file.h>
> +#include <linux/of.h>
> +#include <linux/interrupt.h>
> +#include <linux/bug.h>
> +
> +#include <asm/machdep.h>
> +#include <asm/firmware.h>
> +#include <asm/xics.h>
> +
> +#include "powernv.h"
> +
> +static void __init pnv_setup_arch(void)
> +{
> + /* Force console to hvc for now until we have sorted out the
> + * real console situation for the platform. This will make
> + * hvc_udbg work at least.
> + */
> + add_preferred_console("hvc", 0, NULL);
> +
> + /* Initialize SMP */
> + pnv_smp_init();
> +
> + /* XXX PCI */
> +
> + /* XXX NVRAM */
> +
> + /* Enable NAP mode */
> + powersave_nap = 1;
> +
> + /* XXX PMCS */
> +}
> +
> +static void __init pnv_init_early(void)
> +{
> + /* XXX IOMMU */
> +}
> +
> +static void __init pnv_init_IRQ(void)
> +{
> + xics_init();
> +
> + WARN_ON(!ppc_md.get_irq);
> +}
> +
> +static void pnv_show_cpuinfo(struct seq_file *m)
> +{
> + struct device_node *root;
> + const char *model = "";
> +
> + root = of_find_node_by_path("/");
> + if (root)
> + model = of_get_property(root, "model", NULL);
> + seq_printf(m, "machine\t\t: PowerNV %s\n", model);
> + of_node_put(root);
> +}
> +
> +static void pnv_restart(char *cmd)
> +{
> + for (;;);
> +}
> +
> +static void pnv_power_off(void)
> +{
> + for (;;);
> +}
> +
> +static void pnv_halt(void)
> +{
> + for (;;);
> +}
> +
> +static unsigned long __init pnv_get_boot_time(void)
> +{
> + return 0;
> +}
> +
> +static void pnv_get_rtc_time(struct rtc_time *rtc_tm)
> +{
> +}
> +
> +static int pnv_set_rtc_time(struct rtc_time *tm)
> +{
> + return 0;
> +}
> +
> +static void pnv_progress(char *s, unsigned short hex)
> +{
> +}
> +
> +#ifdef CONFIG_KEXEC
> +static void pnv_kexec_cpu_down(int crash_shutdown, int secondary)
> +{
> + xics_kexec_teardown_cpu(secondary);
> +}
> +#endif /* CONFIG_KEXEC */
> +
> +static int __init pnv_probe(void)
> +{
> + unsigned long root = of_get_flat_dt_root();
> +
> + if (!of_flat_dt_is_compatible(root, "ibm,powernv"))
> + return 0;
> +
> + hpte_init_native();
> +
> + pr_debug("PowerNV detected !\n");
> +
> + return 1;
> +}
> +
> +define_machine(powernv) {
> + .name = "PowerNV",
> + .probe = pnv_probe,
> + .setup_arch = pnv_setup_arch,
> + .init_early = pnv_init_early,
> + .init_IRQ = pnv_init_IRQ,
> + .show_cpuinfo = pnv_show_cpuinfo,
> + .restart = pnv_restart,
> + .power_off = pnv_power_off,
> + .halt = pnv_halt,
> + .get_boot_time = pnv_get_boot_time,
> + .get_rtc_time = pnv_get_rtc_time,
> + .set_rtc_time = pnv_set_rtc_time,
> + .progress = pnv_progress,
> + .power_save = power7_idle,
> + .calibrate_decr = generic_calibrate_decr,
> +#ifdef CONFIG_KEXEC
> + .kexec_cpu_down = pnv_kexec_cpu_down,
> +#endif
> +};
> diff --git a/arch/powerpc/platforms/powernv/smp.c b/arch/powerpc/platforms/po
wernv/smp.c
> new file mode 100644
> index 0000000..36c7151
> --- /dev/null
> +++ b/arch/powerpc/platforms/powernv/smp.c
> @@ -0,0 +1,83 @@
> +/*
> + * SMP support for PowerNV machines.
> + *
> + * Copyright 2011 IBM Corp.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; either version
> + * 2 of the License, or (at your option) any later version.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/sched.h>
> +#include <linux/smp.h>
> +#include <linux/interrupt.h>
> +#include <linux/delay.h>
> +#include <linux/init.h>
> +#include <linux/spinlock.h>
> +#include <linux/cpu.h>
> +
> +#include <asm/irq.h>
> +#include <asm/smp.h>
> +#include <asm/paca.h>
> +#include <asm/machdep.h>
> +#include <asm/cputable.h>
> +#include <asm/firmware.h>
> +#include <asm/system.h>
> +#include <asm/rtas.h>
> +#include <asm/vdso_datapage.h>
> +#include <asm/cputhreads.h>
> +#include <asm/xics.h>
> +
> +#include "powernv.h"
> +
> +static void __devinit pnv_smp_setup_cpu(int cpu)
> +{
> + if (cpu != boot_cpuid)
> + xics_setup_cpu();
> +}
> +
> +static int pnv_smp_cpu_bootable(unsigned int nr)
> +{
> + /* Special case - we inhibit secondary thread startup
> + * during boot if the user requests it.
> + */
> + if (system_state < SYSTEM_RUNNING && cpu_has_feature(CPU_FTR_SMT)) {
> + if (!smt_enabled_at_boot && cpu_thread_in_core(nr) != 0)
> + return 0;
> + if (smt_enabled_at_boot
> + && cpu_thread_in_core(nr) >= smt_enabled_at_boot)
> + return 0;
> + }
> +
> + return 1;
> +}
> +
> +static struct smp_ops_t pnv_smp_ops = {
> + .message_pass = smp_muxed_ipi_message_pass,
> + .cause_ipi = NULL, /* Filled at runtime by xics_smp_probe() */
> + .probe = xics_smp_probe,
> + .kick_cpu = smp_generic_kick_cpu,
> + .setup_cpu = pnv_smp_setup_cpu,
> + .cpu_bootable = pnv_smp_cpu_bootable,
> +};
> +
> +/* This is called very early during platform setup_arch */
> +void __init pnv_smp_init(void)
> +{
> + smp_ops = &pnv_smp_ops;
> +
> + /* XXX We don't yet have a proper entry point from HAL, for
> + * now we rely on kexec-style entry from BML
> + */
> +
> +#ifdef CONFIG_PPC_RTAS
> + /* Non-lpar has additional take/give timebase */
> + if (rtas_token("freeze-time-base") != RTAS_UNKNOWN_SERVICE) {
> + smp_ops->give_timebase = rtas_give_timebase;
> + smp_ops->take_timebase = rtas_take_timebase;
> + }
> +#endif /* CONFIG_PPC_RTAS */
> +}
> --
> 1.7.4.1
>
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@lists.ozlabs.org
> https://lists.ozlabs.org/listinfo/linuxppc-dev
>
^ permalink raw reply
* Re: [PATCH] powerpc: Optimize __arch_swab32 and __arch_swab16
From: Joakim Tjernlund @ 2011-09-11 9:32 UTC (permalink / raw)
To: Benjamin Herrenschmidt; +Cc: linuxppc-dev
In-Reply-To: <1315646679.455.38.camel@pasglop>
Benjamin Herrenschmidt <benh@kernel.crashing.org> wrote on 2011/09/10 11:24:39:
>
> On Fri, 2011-09-09 at 14:10 +0200, Joakim Tjernlund wrote:
> > PPC __arch_swab32 and __arch_swab16 generates non optimal code.
> > They do not schedule very well, need to copy its input register
> > and swab16 needs an extra insn to clear its upper bits.
> > Fix this with better inline ASM.
> >
> > Signed-off-by: Joakim Tjernlund <Joakim.Tjernlund@transmode.se>
> > ---
> > arch/powerpc/include/asm/swab.h | 28 ++++++++++++++--------------
> > 1 files changed, 14 insertions(+), 14 deletions(-)
> >
> > diff --git a/arch/powerpc/include/asm/swab.h b/arch/powerpc/include/asm/swab.h
> > index c581e3e..3b9a200 100644
> > --- a/arch/powerpc/include/asm/swab.h
> > +++ b/arch/powerpc/include/asm/swab.h
> > @@ -61,25 +61,25 @@ static inline void __arch_swab32s(__u32 *addr)
> >
> > static inline __attribute_const__ __u16 __arch_swab16(__u16 value)
> > {
> > - __u16 result;
> > -
> > - __asm__("rlwimi %0,%1,8,16,23"
> > - : "=r" (result)
> > - : "r" (value), "0" (value >> 8));
> > - return result;
> > + __asm__("rlwimi %0,%0,16,0x00ff0000\n\t"
> > + "rlwinm %0,%0,24,0x0000ffff"
> > + : "+r"(value));
> > + return value;
> > }
> > #define __arch_swab16 __arch_swab16
>
> I don't quite get the thing about needing to clear the high bits.
>
> Value is a u16 to start with, %0 is pre-filled with value >> 8 which
> won't add anything to the upper bits, neither will rlwimi, so why would
> you need to clear upper bits ?
>
> Now I do see why gcc might generate something sub-optimal here, but can
> you provide examples of asm output before/after in the patch commit ?
I did a simple test earlier like so:
unsigned short __arch_swab16(unsigned short value)
{
unsigned short result;
__asm__("rlwimi %0,%1,8,16,23"
: "=r" (result)
: "r" (value), "0" (value >> 8));
return result;
}
unsigned short my__arch_swab16(unsigned short value)
{
__asm__("rlwimi %0,%0,16,0x00ff0000\n\t"
"rlwinm %0,%0,24,0x0000ffff"
: "+r"(value));
return value;
}
gcc -O2 -S outputs:
.file "my_swab.c"
.section ".text"
.align 2
.globl __arch_swab16
.type __arch_swab16, @function
__arch_swab16:
mr 0,3
srwi 3,3,8
#APP
rlwimi 3,0,8,16,23
#NO_APP
rlwinm 3,3,0,0xffff
blr
.size __arch_swab16, .-__arch_swab16
.align 2
.globl my__arch_swab16
.type my__arch_swab16, @function
my__arch_swab16:
#APP
rlwimi 3,3,16,0x00ff0000
rlwinm 3,3,24,0x0000ffff
#NO_APP
blr
.size my__arch_swab16, .-my__arch_swab16
.section .note.GNU-stack,"",@progbits
.ident "GCC: (GNU) 3.4.6 (Gentoo 3.4.6-r2, ssp-3.4.6-1.0, pie-8.7.9)"
However I recently installed gcc 4.5.2 to do some tests and that gives
me this instead:
.file "my_swab.c"
.gnu_attribute 4, 2
.gnu_attribute 8, 1
.gnu_attribute 12, 2
.section ".text"
.align 2
.globl __arch_swab16
.type __arch_swab16, @function
__arch_swab16:
srwi 0,3,8
#APP
# 5 "my_swab.c" 1
rlwimi 0,3,8,16,23
# 0 "" 2
#NO_APP
rlwinm 3,0,0,0xffff
blr
.size __arch_swab16, .-__arch_swab16
.align 2
.globl my__arch_swab16
.type my__arch_swab16, @function
my__arch_swab16:
#APP
# 13 "my_swab.c" 1
rlwimi 3,3,16,0x00ff0000
rlwinm 3,3,24,0x0000ffff
# 0 "" 2
#NO_APP
rlwinm 3,3,0,0xffff
blr
.size my__arch_swab16, .-my__arch_swab16
.ident "GCC: (Gentoo 4.5.2 p1.1, pie-0.4.5) 4.5.2"
.section .note.GNU-stack,"",@progbits
So now both versions mask off the high bits, strange.
Not sure what is going on here?
Jocke
^ permalink raw reply
* [PATCH 17/21] powerpc/powernv: Register and handle OPAL interrupts
From: Benjamin Herrenschmidt @ 2011-09-10 14:20 UTC (permalink / raw)
To: linuxppc-dev
In-Reply-To: <1315664408-16797-1-git-send-email-benh@kernel.crashing.org>
We do the minimum which is to "pass" interrupts to HAL, which
makes the console smoother and will allow us to implement
interrupt based completion and console.
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---
arch/powerpc/platforms/powernv/opal.c | 31 +++++++++++++++++++++++++++++++
1 files changed, 31 insertions(+), 0 deletions(-)
diff --git a/arch/powerpc/platforms/powernv/opal.c b/arch/powerpc/platforms/powernv/opal.c
index 7887733..5a598ca 100644
--- a/arch/powerpc/platforms/powernv/opal.c
+++ b/arch/powerpc/platforms/powernv/opal.c
@@ -14,6 +14,7 @@
#include <linux/types.h>
#include <linux/of.h>
#include <linux/of_platform.h>
+#include <linux/interrupt.h>
#include <asm/opal.h>
#include <asm/firmware.h>
@@ -135,9 +136,22 @@ int opal_put_chars(uint32_t vtermno, const char *data, int total_len)
return written;
}
+static irqreturn_t opal_interrupt(int irq, void *data)
+{
+ uint64_t events;
+
+ opal_handle_interrupt(virq_to_hw(irq), &events);
+
+ /* XXX TODO: Do something with the events */
+
+ return IRQ_HANDLED;
+}
+
static int __init opal_init(void)
{
struct device_node *np, *consoles;
+ const u32 *irqs;
+ int rc, i, irqlen;
opal_node = of_find_node_by_path("/ibm,opal");
if (!opal_node) {
@@ -156,6 +170,23 @@ static int __init opal_init(void)
of_platform_device_create(np, NULL, NULL);
}
of_node_put(consoles);
+
+ /* Find all OPAL interrupts and request them */
+ irqs = of_get_property(opal_node, "opal-interrupts", &irqlen);
+ pr_debug("opal: Found %d interrupts reserved for OPAL\n",
+ irqs ? (irqlen / 4) : 0);
+ for (i = 0; irqs && i < (irqlen / 4); i++, irqs++) {
+ unsigned int hwirq = be32_to_cpup(irqs);
+ unsigned int irq = irq_create_mapping(NULL, hwirq);
+ if (irq == NO_IRQ) {
+ pr_warning("opal: Failed to map irq 0x%x\n", hwirq);
+ continue;
+ }
+ rc = request_irq(irq, opal_interrupt, 0, "opal", NULL);
+ if (rc)
+ pr_warning("opal: Error %d requesting irq %d"
+ " (0x%x)\n", rc, irq, hwirq);
+ }
return 0;
}
subsys_initcall(opal_init);
--
1.7.4.1
^ permalink raw reply related
* [PATCH 16/21] powerpc/powernv: Add OPAL ICS backend
From: Benjamin Herrenschmidt @ 2011-09-10 14:20 UTC (permalink / raw)
To: linuxppc-dev
In-Reply-To: <1315664408-16797-1-git-send-email-benh@kernel.crashing.org>
OPAL handles HW access to the various ICS or equivalent chips
for us (with the exception of p5ioc2 based HEA which uses a
different backend) similarily to what RTAS does on pSeries.
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---
arch/powerpc/include/asm/xics.h | 19 +++
arch/powerpc/sysdev/xics/Makefile | 1 +
arch/powerpc/sysdev/xics/ics-opal.c | 244 ++++++++++++++++++++++++++++++++
arch/powerpc/sysdev/xics/xics-common.c | 8 +-
4 files changed, 266 insertions(+), 6 deletions(-)
create mode 100644 arch/powerpc/sysdev/xics/ics-opal.c
diff --git a/arch/powerpc/include/asm/xics.h b/arch/powerpc/include/asm/xics.h
index b183a40..bd6c401 100644
--- a/arch/powerpc/include/asm/xics.h
+++ b/arch/powerpc/include/asm/xics.h
@@ -27,10 +27,18 @@
#define MAX_NUM_PRIORITIES 3
/* Native ICP */
+#ifdef CONFIG_PPC_ICP_NATIVE
extern int icp_native_init(void);
+#else
+static inline int icp_native_init(void) { return -ENODEV; }
+#endif
/* PAPR ICP */
+#ifdef CONFIG_PPC_ICP_HV
extern int icp_hv_init(void);
+#else
+static inline int icp_hv_init(void) { return -ENODEV; }
+#endif
/* ICP ops */
struct icp_ops {
@@ -51,7 +59,18 @@ extern const struct icp_ops *icp_ops;
extern int ics_native_init(void);
/* RTAS ICS */
+#ifdef CONFIG_PPC_ICS_RTAS
extern int ics_rtas_init(void);
+#else
+static inline int ics_rtas_init(void) { return -ENODEV; }
+#endif
+
+/* HAL ICS */
+#ifdef CONFIG_PPC_POWERNV
+extern int ics_opal_init(void);
+#else
+static inline int ics_opal_init(void) { return -ENODEV; }
+#endif
/* ICS instance, hooked up to chip_data of an irq */
struct ics {
diff --git a/arch/powerpc/sysdev/xics/Makefile b/arch/powerpc/sysdev/xics/Makefile
index b75a605..c606aa8 100644
--- a/arch/powerpc/sysdev/xics/Makefile
+++ b/arch/powerpc/sysdev/xics/Makefile
@@ -4,3 +4,4 @@ obj-y += xics-common.o
obj-$(CONFIG_PPC_ICP_NATIVE) += icp-native.o
obj-$(CONFIG_PPC_ICP_HV) += icp-hv.o
obj-$(CONFIG_PPC_ICS_RTAS) += ics-rtas.o
+obj-$(CONFIG_PPC_POWERNV) += ics-opal.o
diff --git a/arch/powerpc/sysdev/xics/ics-opal.c b/arch/powerpc/sysdev/xics/ics-opal.c
new file mode 100644
index 0000000..1f3a676
--- /dev/null
+++ b/arch/powerpc/sysdev/xics/ics-opal.c
@@ -0,0 +1,244 @@
+/*
+ * ICS backend for OPAL managed interrupts.
+ *
+ * Copyright 2011 IBM Corp.
+ *
+ * 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.
+ */
+
+#undef DEBUG
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/irq.h>
+#include <linux/smp.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/cpu.h>
+#include <linux/of.h>
+#include <linux/spinlock.h>
+#include <linux/msi.h>
+
+#include <asm/prom.h>
+#include <asm/smp.h>
+#include <asm/machdep.h>
+#include <asm/irq.h>
+#include <asm/errno.h>
+#include <asm/xics.h>
+#include <asm/opal.h>
+#include <asm/firmware.h>
+
+static int ics_opal_mangle_server(int server)
+{
+ /* No link for now */
+ return server << 2;
+}
+
+static int ics_opal_unmangle_server(int server)
+{
+ /* No link for now */
+ return server >> 2;
+}
+
+static void ics_opal_unmask_irq(struct irq_data *d)
+{
+ unsigned int hw_irq = (unsigned int)irqd_to_hwirq(d);
+ int64_t rc;
+ int server;
+
+ pr_devel("ics-hal: unmask virq %d [hw 0x%x]\n", d->irq, hw_irq);
+
+ if (hw_irq == XICS_IPI || hw_irq == XICS_IRQ_SPURIOUS)
+ return;
+
+ server = xics_get_irq_server(d->irq, d->affinity, 0);
+ server = ics_opal_mangle_server(server);
+
+ rc = opal_set_xive(hw_irq, server, DEFAULT_PRIORITY);
+ if (rc != OPAL_SUCCESS)
+ pr_err("%s: opal_set_xive(irq=%d [hw 0x%x] server=%x)"
+ " error %lld\n",
+ __func__, d->irq, hw_irq, server, rc);
+}
+
+static unsigned int ics_opal_startup(struct irq_data *d)
+{
+#ifdef CONFIG_PCI_MSI
+ /*
+ * The generic MSI code returns with the interrupt disabled on the
+ * card, using the MSI mask bits. Firmware doesn't appear to unmask
+ * at that level, so we do it here by hand.
+ */
+ if (d->msi_desc)
+ unmask_msi_irq(d);
+#endif
+
+ /* unmask it */
+ ics_opal_unmask_irq(d);
+ return 0;
+}
+
+static void ics_opal_mask_real_irq(unsigned int hw_irq)
+{
+ int server = ics_opal_mangle_server(xics_default_server);
+ int64_t rc;
+
+ if (hw_irq == XICS_IPI)
+ return;
+
+ /* Have to set XIVE to 0xff to be able to remove a slot */
+ rc = opal_set_xive(hw_irq, server, 0xff);
+ if (rc != OPAL_SUCCESS)
+ pr_err("%s: opal_set_xive(0xff) irq=%u returned %lld\n",
+ __func__, hw_irq, rc);
+}
+
+static void ics_opal_mask_irq(struct irq_data *d)
+{
+ unsigned int hw_irq = (unsigned int)irqd_to_hwirq(d);
+
+ pr_devel("ics-hal: mask virq %d [hw 0x%x]\n", d->irq, hw_irq);
+
+ if (hw_irq == XICS_IPI || hw_irq == XICS_IRQ_SPURIOUS)
+ return;
+ ics_opal_mask_real_irq(hw_irq);
+}
+
+static int ics_opal_set_affinity(struct irq_data *d,
+ const struct cpumask *cpumask,
+ bool force)
+{
+ unsigned int hw_irq = (unsigned int)irqd_to_hwirq(d);
+ int16_t server;
+ int8_t priority;
+ int64_t rc;
+ int wanted_server;
+
+ if (hw_irq == XICS_IPI || hw_irq == XICS_IRQ_SPURIOUS)
+ return -1;
+
+ rc = opal_get_xive(hw_irq, &server, &priority);
+ if (rc != OPAL_SUCCESS) {
+ pr_err("%s: opal_set_xive(irq=%d [hw 0x%x] server=%x)"
+ " error %lld\n",
+ __func__, d->irq, hw_irq, server, rc);
+ return -1;
+ }
+
+ wanted_server = xics_get_irq_server(d->irq, cpumask, 1);
+ if (wanted_server < 0) {
+ char cpulist[128];
+ cpumask_scnprintf(cpulist, sizeof(cpulist), cpumask);
+ pr_warning("%s: No online cpus in the mask %s for irq %d\n",
+ __func__, cpulist, d->irq);
+ return -1;
+ }
+ server = ics_opal_mangle_server(wanted_server);
+
+ pr_devel("ics-hal: set-affinity irq %d [hw 0x%x] server: 0x%x/0x%x\n",
+ d->irq, hw_irq, wanted_server, server);
+
+ rc = opal_set_xive(hw_irq, server, priority);
+ if (rc != OPAL_SUCCESS) {
+ pr_err("%s: opal_set_xive(irq=%d [hw 0x%x] server=%x)"
+ " error %lld\n",
+ __func__, d->irq, hw_irq, server, rc);
+ return -1;
+ }
+ return 0;
+}
+
+static struct irq_chip ics_opal_irq_chip = {
+ .name = "HAL ICS",
+ .irq_startup = ics_opal_startup,
+ .irq_mask = ics_opal_mask_irq,
+ .irq_unmask = ics_opal_unmask_irq,
+ .irq_eoi = NULL, /* Patched at init time */
+ .irq_set_affinity = ics_opal_set_affinity
+};
+
+static int ics_opal_map(struct ics *ics, unsigned int virq);
+static void ics_opal_mask_unknown(struct ics *ics, unsigned long vec);
+static long ics_opal_get_server(struct ics *ics, unsigned long vec);
+
+static int ics_opal_host_match(struct ics *ics, struct device_node *node)
+{
+ return 1;
+}
+
+/* Only one global & state struct ics */
+static struct ics ics_hal = {
+ .map = ics_opal_map,
+ .mask_unknown = ics_opal_mask_unknown,
+ .get_server = ics_opal_get_server,
+ .host_match = ics_opal_host_match,
+};
+
+static int ics_opal_map(struct ics *ics, unsigned int virq)
+{
+ unsigned int hw_irq = (unsigned int)virq_to_hw(virq);
+ int64_t rc;
+ int16_t server;
+ int8_t priority;
+
+ if (WARN_ON(hw_irq == XICS_IPI || hw_irq == XICS_IRQ_SPURIOUS))
+ return -EINVAL;
+
+ /* Check if HAL knows about this interrupt */
+ rc = opal_get_xive(hw_irq, &server, &priority);
+ if (rc != OPAL_SUCCESS)
+ return -ENXIO;
+
+ irq_set_chip_and_handler(virq, &ics_opal_irq_chip, handle_fasteoi_irq);
+ irq_set_chip_data(virq, &ics_hal);
+
+ return 0;
+}
+
+static void ics_opal_mask_unknown(struct ics *ics, unsigned long vec)
+{
+ int64_t rc;
+ int16_t server;
+ int8_t priority;
+
+ /* Check if HAL knows about this interrupt */
+ rc = opal_get_xive(vec, &server, &priority);
+ if (rc != OPAL_SUCCESS)
+ return;
+
+ ics_opal_mask_real_irq(vec);
+}
+
+static long ics_opal_get_server(struct ics *ics, unsigned long vec)
+{
+ int64_t rc;
+ int16_t server;
+ int8_t priority;
+
+ /* Check if HAL knows about this interrupt */
+ rc = opal_get_xive(vec, &server, &priority);
+ if (rc != OPAL_SUCCESS)
+ return -1;
+ return ics_opal_unmangle_server(server);
+}
+
+int __init ics_opal_init(void)
+{
+ if (!firmware_has_feature(FW_FEATURE_OPAL))
+ return -ENODEV;
+
+ /* We need to patch our irq chip's EOI to point to the
+ * right ICP
+ */
+ ics_opal_irq_chip.irq_eoi = icp_ops->eoi;
+
+ /* Register ourselves */
+ xics_register_ics(&ics_hal);
+
+ pr_info("ICS OPAL backend registered\n");
+
+ return 0;
+}
diff --git a/arch/powerpc/sysdev/xics/xics-common.c b/arch/powerpc/sysdev/xics/xics-common.c
index 445c5a0..3d93a8d 100644
--- a/arch/powerpc/sysdev/xics/xics-common.c
+++ b/arch/powerpc/sysdev/xics/xics-common.c
@@ -409,14 +409,10 @@ void __init xics_init(void)
int rc = -1;
/* Fist locate ICP */
-#ifdef CONFIG_PPC_ICP_HV
if (firmware_has_feature(FW_FEATURE_LPAR))
rc = icp_hv_init();
-#endif
-#ifdef CONFIG_PPC_ICP_NATIVE
if (rc < 0)
rc = icp_native_init();
-#endif
if (rc < 0) {
pr_warning("XICS: Cannot find a Presentation Controller !\n");
return;
@@ -429,9 +425,9 @@ void __init xics_init(void)
xics_ipi_chip.irq_eoi = icp_ops->eoi;
/* Now locate ICS */
-#ifdef CONFIG_PPC_ICS_RTAS
rc = ics_rtas_init();
-#endif
+ if (rc < 0)
+ rc = ics_opal_init();
if (rc < 0)
pr_warning("XICS: Cannot find a Source Controller !\n");
--
1.7.4.1
^ permalink raw reply related
* [PATCH 21/21] powerpc/powernv: Handle PCI-X/PCIe reset delay
From: Benjamin Herrenschmidt @ 2011-09-10 14:20 UTC (permalink / raw)
To: linuxppc-dev
In-Reply-To: <1315664408-16797-1-git-send-email-benh@kernel.crashing.org>
The firmware doesn't wait after lifting the PCI reset. However it does
timestamp it in the device tree. We use that to ensure we wait long
enough (3s is our current arbitrary setting) from that timestamp to
actually probing the bus.
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---
arch/powerpc/platforms/powernv/pci.c | 32 ++++++++++++++++++++++++++++++++
1 files changed, 32 insertions(+), 0 deletions(-)
diff --git a/arch/powerpc/platforms/powernv/pci.c b/arch/powerpc/platforms/powernv/pci.c
index f85fb13..1835db5 100644
--- a/arch/powerpc/platforms/powernv/pci.c
+++ b/arch/powerpc/platforms/powernv/pci.c
@@ -35,6 +35,8 @@
#include "powernv.h"
#include "pci.h"
+/* Delay in usec */
+#define PCI_RESET_DELAY_US 3000000
#define cfg_dbg(fmt...) do { } while(0)
//#define cfg_dbg(fmt...) printk(fmt)
@@ -353,6 +355,35 @@ static void __devinit pnv_pci_dma_dev_setup(struct pci_dev *pdev)
pnv_pci_dma_fallback_setup(hose, pdev);
}
+static int pnv_pci_probe_mode(struct pci_bus *bus)
+{
+ struct pci_controller *hose = pci_bus_to_host(bus);
+ const __be64 *tstamp;
+ u64 now, target;
+
+
+ /* We hijack this as a way to ensure we have waited long
+ * enough since the reset was lifted on the PCI bus
+ */
+ if (bus != hose->bus)
+ return PCI_PROBE_NORMAL;
+ tstamp = of_get_property(hose->dn, "reset-clear-timestamp", NULL);
+ if (!tstamp || !*tstamp)
+ return PCI_PROBE_NORMAL;
+
+ now = mftb() / tb_ticks_per_usec;
+ target = (be64_to_cpup(tstamp) / tb_ticks_per_usec)
+ + PCI_RESET_DELAY_US;
+
+ pr_devel("pci %04d: Reset target: 0x%llx now: 0x%llx\n",
+ hose->global_number, target, now);
+
+ if (now < target)
+ msleep((target - now + 999) / 1000);
+
+ return PCI_PROBE_NORMAL;
+}
+
void __init pnv_pci_init(void)
{
struct device_node *np;
@@ -383,6 +414,7 @@ void __init pnv_pci_init(void)
ppc_md.pci_dma_dev_setup = pnv_pci_dma_dev_setup;
ppc_md.tce_build = pnv_tce_build;
ppc_md.tce_free = pnv_tce_free;
+ ppc_md.pci_probe_mode = pnv_pci_probe_mode;
set_pci_dma_ops(&dma_iommu_ops);
/* Configure MSIs */
--
1.7.4.1
^ permalink raw reply related
* [PATCH 20/21] powerpc/powernv: Implement MSI support for p5ioc2 PCIe
From: Benjamin Herrenschmidt @ 2011-09-10 14:20 UTC (permalink / raw)
To: linuxppc-dev
In-Reply-To: <1315664408-16797-1-git-send-email-benh@kernel.crashing.org>
This implements support for MSIs on p5ioc2 PHBs. We only support
MSIs on the PCIe PHBs, not the PCI-X ones as the later hasn't been
properly verified in HW.
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---
arch/powerpc/platforms/powernv/pci-p5ioc2.c | 49 ++++++++++++
arch/powerpc/platforms/powernv/pci.c | 108 +++++++++++++++++++++++++++
arch/powerpc/platforms/powernv/pci.h | 10 +++
3 files changed, 167 insertions(+), 0 deletions(-)
diff --git a/arch/powerpc/platforms/powernv/pci-p5ioc2.c b/arch/powerpc/platforms/powernv/pci-p5ioc2.c
index afabc2b..fd1509a 100644
--- a/arch/powerpc/platforms/powernv/pci-p5ioc2.c
+++ b/arch/powerpc/platforms/powernv/pci-p5ioc2.c
@@ -19,6 +19,7 @@
#include <linux/bootmem.h>
#include <linux/irq.h>
#include <linux/io.h>
+#include <linux/msi.h>
#include <asm/sections.h>
#include <asm/io.h>
@@ -39,6 +40,51 @@
*/
#define P5IOC2_TCE_MEMORY 0x01000000
+#ifdef CONFIG_PCI_MSI
+static int pnv_pci_p5ioc2_msi_setup(struct pnv_phb *phb, struct pci_dev *dev,
+ unsigned int hwirq, unsigned int is_64,
+ struct msi_msg *msg)
+{
+ if (WARN_ON(!is_64))
+ return -ENXIO;
+ msg->data = hwirq - phb->msi_base;
+ msg->address_hi = 0x10000000;
+ msg->address_lo = 0;
+
+ return 0;
+}
+
+static void pnv_pci_init_p5ioc2_msis(struct pnv_phb *phb)
+{
+ unsigned int bmap_size;
+ const __be32 *prop = of_get_property(phb->hose->dn,
+ "ibm,opal-msi-ranges", NULL);
+ if (!prop)
+ return;
+
+ /* Don't do MSI's on p5ioc2 PCI-X are they are not properly
+ * verified in HW
+ */
+ if (of_device_is_compatible(phb->hose->dn, "ibm,p5ioc2-pcix"))
+ return;
+ phb->msi_base = be32_to_cpup(prop);
+ phb->msi_count = be32_to_cpup(prop + 1);
+ bmap_size = BITS_TO_LONGS(phb->msi_count) * sizeof(unsigned long);
+ phb->msi_map = zalloc_maybe_bootmem(bmap_size, GFP_KERNEL);
+ if (!phb->msi_map) {
+ pr_err("PCI %d: Failed to allocate MSI bitmap !\n",
+ phb->hose->global_number);
+ return;
+ }
+ phb->msi_setup = pnv_pci_p5ioc2_msi_setup;
+ phb->msi32_support = 0;
+ pr_info(" Allocated bitmap for %d MSIs (base IRQ 0x%x)\n",
+ phb->msi_count, phb->msi_base);
+}
+#else
+static void pnv_pci_setup_p5ioc2_msis(struct pnv_phb *phb) { }
+#endif /* CONFIG_PCI_MSI */
+
static void __devinit pnv_pci_p5ioc2_dma_dev_setup(struct pnv_phb *phb,
struct pci_dev *pdev)
{
@@ -117,6 +163,9 @@ static void __init pnv_pci_init_p5ioc2_phb(struct device_node *np,
phb->hose->ops = &pnv_pci_ops;
+ /* Setup MSI support */
+ pnv_pci_init_p5ioc2_msis(phb);
+
/* Setup TCEs */
phb->dma_dev_setup = pnv_pci_p5ioc2_dma_dev_setup;
pnv_pci_setup_iommu_table(&phb->p5ioc2.iommu_table,
diff --git a/arch/powerpc/platforms/powernv/pci.c b/arch/powerpc/platforms/powernv/pci.c
index b512489..f85fb13 100644
--- a/arch/powerpc/platforms/powernv/pci.c
+++ b/arch/powerpc/platforms/powernv/pci.c
@@ -19,6 +19,7 @@
#include <linux/bootmem.h>
#include <linux/irq.h>
#include <linux/io.h>
+#include <linux/msi.h>
#include <asm/sections.h>
#include <asm/io.h>
@@ -38,7 +39,108 @@
#define cfg_dbg(fmt...) do { } while(0)
//#define cfg_dbg(fmt...) printk(fmt)
+#ifdef CONFIG_PCI_MSI
+static int pnv_msi_check_device(struct pci_dev* pdev, int nvec, int type)
+{
+ struct pci_controller *hose = pci_bus_to_host(pdev->bus);
+ struct pnv_phb *phb = hose->private_data;
+
+ return (phb && phb->msi_map) ? 0 : -ENODEV;
+}
+
+static unsigned int pnv_get_one_msi(struct pnv_phb *phb)
+{
+ unsigned int id;
+
+ spin_lock(&phb->lock);
+ id = find_next_zero_bit(phb->msi_map, phb->msi_count, phb->msi_next);
+ if (id >= phb->msi_count && phb->msi_next)
+ id = find_next_zero_bit(phb->msi_map, phb->msi_count, 0);
+ if (id >= phb->msi_count) {
+ spin_unlock(&phb->lock);
+ return 0;
+ }
+ __set_bit(id, phb->msi_map);
+ spin_unlock(&phb->lock);
+ return id + phb->msi_base;
+}
+
+static void pnv_put_msi(struct pnv_phb *phb, unsigned int hwirq)
+{
+ unsigned int id;
+
+ if (WARN_ON(hwirq < phb->msi_base ||
+ hwirq >= (phb->msi_base + phb->msi_count)))
+ return;
+ id = hwirq - phb->msi_base;
+ spin_lock(&phb->lock);
+ __clear_bit(id, phb->msi_map);
+ spin_unlock(&phb->lock);
+}
+
+static int pnv_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type)
+{
+ struct pci_controller *hose = pci_bus_to_host(pdev->bus);
+ struct pnv_phb *phb = hose->private_data;
+ struct msi_desc *entry;
+ struct msi_msg msg;
+ unsigned int hwirq, virq;
+ int rc;
+
+ if (WARN_ON(!phb))
+ return -ENODEV;
+
+ list_for_each_entry(entry, &pdev->msi_list, list) {
+ if (!entry->msi_attrib.is_64 && !phb->msi32_support) {
+ pr_warn("%s: Supports only 64-bit MSIs\n",
+ pci_name(pdev));
+ return -ENXIO;
+ }
+ hwirq = pnv_get_one_msi(phb);
+ if (!hwirq) {
+ pr_warn("%s: Failed to find a free MSI\n",
+ pci_name(pdev));
+ return -ENOSPC;
+ }
+ virq = irq_create_mapping(NULL, hwirq);
+ if (virq == NO_IRQ) {
+ pr_warn("%s: Failed to map MSI to linux irq\n",
+ pci_name(pdev));
+ pnv_put_msi(phb, hwirq);
+ return -ENOMEM;
+ }
+ rc = phb->msi_setup(phb, pdev, hwirq, entry->msi_attrib.is_64,
+ &msg);
+ if (rc) {
+ pr_warn("%s: Failed to setup MSI\n", pci_name(pdev));
+ irq_dispose_mapping(virq);
+ pnv_put_msi(phb, hwirq);
+ return rc;
+ }
+ irq_set_msi_desc(virq, entry);
+ write_msi_msg(virq, &msg);
+ }
+ return 0;
+}
+static void pnv_teardown_msi_irqs(struct pci_dev *pdev)
+{
+ struct pci_controller *hose = pci_bus_to_host(pdev->bus);
+ struct pnv_phb *phb = hose->private_data;
+ struct msi_desc *entry;
+
+ if (WARN_ON(!phb))
+ return;
+
+ list_for_each_entry(entry, &pdev->msi_list, list) {
+ if (entry->irq == NO_IRQ)
+ continue;
+ irq_set_msi_desc(entry->irq, NULL);
+ pnv_put_msi(phb, virq_to_hw(entry->irq));
+ irq_dispose_mapping(entry->irq);
+ }
+}
+#endif /* CONFIG_PCI_MSI */
static void pnv_pci_config_check_eeh(struct pnv_phb *phb, struct pci_bus *bus,
u32 bdfn)
{
@@ -283,4 +385,10 @@ void __init pnv_pci_init(void)
ppc_md.tce_free = pnv_tce_free;
set_pci_dma_ops(&dma_iommu_ops);
+ /* Configure MSIs */
+#ifdef CONFIG_PCI_MSI
+ ppc_md.msi_check_device = pnv_msi_check_device;
+ ppc_md.setup_msi_irqs = pnv_setup_msi_irqs;
+ ppc_md.teardown_msi_irqs = pnv_teardown_msi_irqs;
+#endif
}
diff --git a/arch/powerpc/platforms/powernv/pci.h b/arch/powerpc/platforms/powernv/pci.h
index b8be721..a468c9b 100644
--- a/arch/powerpc/platforms/powernv/pci.h
+++ b/arch/powerpc/platforms/powernv/pci.h
@@ -16,6 +16,16 @@ struct pnv_phb {
void __iomem *regs;
spinlock_t lock;
+#ifdef CONFIG_PCI_MSI
+ unsigned long *msi_map;
+ unsigned int msi_base;
+ unsigned int msi_count;
+ unsigned int msi_next;
+ unsigned int msi32_support;
+#endif
+ int (*msi_setup)(struct pnv_phb *phb, struct pci_dev *dev,
+ unsigned int hwirq, unsigned int is_64,
+ struct msi_msg *msg);
void (*dma_dev_setup)(struct pnv_phb *phb, struct pci_dev *pdev);
void (*fixup_phb)(struct pci_controller *hose);
u32 (*bdfn_to_pe)(struct pnv_phb *phb, struct pci_bus *bus, u32 devfn);
--
1.7.4.1
^ permalink raw reply related
* [PATCH 19/21] powerpc/powernv: Add support for p5ioc2 PCI-X and PCIe
From: Benjamin Herrenschmidt @ 2011-09-10 14:20 UTC (permalink / raw)
To: linuxppc-dev
In-Reply-To: <1315664408-16797-1-git-send-email-benh@kernel.crashing.org>
This adds support for PCI-X and PCIe on the p5ioc2 IO hub using
OPAL. This includes allocating & setting up TCE tables and config
space access routines.
This also supports fallbacks via RTAS when OPAL is absent, using
legacy TCE format pre-allocated via the device-tree (BML style)
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---
arch/powerpc/platforms/powernv/Makefile | 2 +
arch/powerpc/platforms/powernv/pci-p5ioc2.c | 185 +++++++++++++++++
arch/powerpc/platforms/powernv/pci.c | 286 +++++++++++++++++++++++++++
arch/powerpc/platforms/powernv/pci.h | 38 ++++
arch/powerpc/platforms/powernv/powernv.h | 6 +
arch/powerpc/platforms/powernv/setup.c | 3 +-
6 files changed, 519 insertions(+), 1 deletions(-)
create mode 100644 arch/powerpc/platforms/powernv/pci-p5ioc2.c
create mode 100644 arch/powerpc/platforms/powernv/pci.c
create mode 100644 arch/powerpc/platforms/powernv/pci.h
diff --git a/arch/powerpc/platforms/powernv/Makefile b/arch/powerpc/platforms/powernv/Makefile
index 618ad83..cf9dec8 100644
--- a/arch/powerpc/platforms/powernv/Makefile
+++ b/arch/powerpc/platforms/powernv/Makefile
@@ -2,3 +2,5 @@ obj-y += setup.o opal-takeover.o opal-wrappers.o opal.o
obj-y += opal-rtc.o opal-nvram.o
obj-$(CONFIG_SMP) += smp.o
+obj-$(CONFIG_PCI) += pci.o pci-p5ioc2.o
+
diff --git a/arch/powerpc/platforms/powernv/pci-p5ioc2.c b/arch/powerpc/platforms/powernv/pci-p5ioc2.c
new file mode 100644
index 0000000..afabc2b
--- /dev/null
+++ b/arch/powerpc/platforms/powernv/pci-p5ioc2.c
@@ -0,0 +1,185 @@
+/*
+ * Support PCI/PCIe on PowerNV platforms
+ *
+ * Currently supports only P5IOC2
+ *
+ * Copyright 2011 Benjamin Herrenschmidt, IBM Corp.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/bootmem.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+
+#include <asm/sections.h>
+#include <asm/io.h>
+#include <asm/prom.h>
+#include <asm/pci-bridge.h>
+#include <asm/machdep.h>
+#include <asm/ppc-pci.h>
+#include <asm/opal.h>
+#include <asm/iommu.h>
+#include <asm/tce.h>
+#include <asm/abs_addr.h>
+
+#include "powernv.h"
+#include "pci.h"
+
+/* For now, use a fixed amount of TCE memory for each p5ioc2
+ * hub, 16M will do
+ */
+#define P5IOC2_TCE_MEMORY 0x01000000
+
+static void __devinit pnv_pci_p5ioc2_dma_dev_setup(struct pnv_phb *phb,
+ struct pci_dev *pdev)
+{
+ if (phb->p5ioc2.iommu_table.it_map == NULL)
+ iommu_init_table(&phb->p5ioc2.iommu_table, phb->hose->node);
+
+ set_iommu_table_base(&pdev->dev, &phb->p5ioc2.iommu_table);
+}
+
+static void __init pnv_pci_init_p5ioc2_phb(struct device_node *np,
+ void *tce_mem, u64 tce_size)
+{
+ struct pnv_phb *phb;
+ const u64 *prop64;
+ u64 phb_id;
+ int64_t rc;
+ static int primary = 1;
+
+ pr_info(" Initializing p5ioc2 PHB %s\n", np->full_name);
+
+ prop64 = of_get_property(np, "ibm,opal-phbid", NULL);
+ if (!prop64) {
+ pr_err(" Missing \"ibm,opal-phbid\" property !\n");
+ return;
+ }
+ phb_id = be64_to_cpup(prop64);
+ pr_devel(" PHB-ID : 0x%016llx\n", phb_id);
+ pr_devel(" TCE AT : 0x%016lx\n", __pa(tce_mem));
+ pr_devel(" TCE SZ : 0x%016llx\n", tce_size);
+
+ rc = opal_pci_set_phb_tce_memory(phb_id, __pa(tce_mem), tce_size);
+ if (rc != OPAL_SUCCESS) {
+ pr_err(" Failed to set TCE memory, OPAL error %lld\n", rc);
+ return;
+ }
+
+ phb = alloc_bootmem(sizeof(struct pnv_phb));
+ if (phb) {
+ memset(phb, 0, sizeof(struct pnv_phb));
+ phb->hose = pcibios_alloc_controller(np);
+ }
+ if (!phb || !phb->hose) {
+ pr_err(" Failed to allocate PCI controller\n");
+ return;
+ }
+
+ spin_lock_init(&phb->lock);
+ phb->hose->first_busno = 0;
+ phb->hose->last_busno = 0xff;
+ phb->hose->private_data = phb;
+ phb->opal_id = phb_id;
+ phb->type = PNV_PHB_P5IOC2;
+
+ phb->regs = of_iomap(np, 0);
+
+ if (phb->regs == NULL)
+ pr_err(" Failed to map registers !\n");
+ else {
+ pr_devel(" P_BUID = 0x%08x\n", in_be32(phb->regs + 0x100));
+ pr_devel(" P_IOSZ = 0x%08x\n", in_be32(phb->regs + 0x1b0));
+ pr_devel(" P_IO_ST = 0x%08x\n", in_be32(phb->regs + 0x1e0));
+ pr_devel(" P_MEM1_H = 0x%08x\n", in_be32(phb->regs + 0x1a0));
+ pr_devel(" P_MEM1_L = 0x%08x\n", in_be32(phb->regs + 0x190));
+ pr_devel(" P_MSZ1_L = 0x%08x\n", in_be32(phb->regs + 0x1c0));
+ pr_devel(" P_MEM_ST = 0x%08x\n", in_be32(phb->regs + 0x1d0));
+ pr_devel(" P_MEM2_H = 0x%08x\n", in_be32(phb->regs + 0x2c0));
+ pr_devel(" P_MEM2_L = 0x%08x\n", in_be32(phb->regs + 0x2b0));
+ pr_devel(" P_MSZ2_H = 0x%08x\n", in_be32(phb->regs + 0x2d0));
+ pr_devel(" P_MSZ2_L = 0x%08x\n", in_be32(phb->regs + 0x2e0));
+ }
+
+ /* Interpret the "ranges" property */
+ /* This also maps the I/O region and sets isa_io/mem_base */
+ pci_process_bridge_OF_ranges(phb->hose, np, primary);
+ primary = 0;
+
+ phb->hose->ops = &pnv_pci_ops;
+
+ /* Setup TCEs */
+ phb->dma_dev_setup = pnv_pci_p5ioc2_dma_dev_setup;
+ pnv_pci_setup_iommu_table(&phb->p5ioc2.iommu_table,
+ tce_mem, tce_size, 0);
+}
+
+void __init pnv_pci_init_p5ioc2_hub(struct device_node *np)
+{
+ struct device_node *phbn;
+ const u64 *prop64;
+ u64 hub_id;
+ void *tce_mem;
+ uint64_t tce_per_phb;
+ int64_t rc;
+ int phb_count = 0;
+
+ pr_info("Probing p5ioc2 IO-Hub %s\n", np->full_name);
+
+ prop64 = of_get_property(np, "ibm,opal-hubid", NULL);
+ if (!prop64) {
+ pr_err(" Missing \"ibm,opal-hubid\" property !\n");
+ return;
+ }
+ hub_id = be64_to_cpup(prop64);
+ pr_info(" HUB-ID : 0x%016llx\n", hub_id);
+
+ /* Currently allocate 16M of TCE memory for every Hub
+ *
+ * XXX TODO: Make it chip local if possible
+ */
+ tce_mem = __alloc_bootmem(P5IOC2_TCE_MEMORY, P5IOC2_TCE_MEMORY,
+ __pa(MAX_DMA_ADDRESS));
+ if (!tce_mem) {
+ pr_err(" Failed to allocate TCE Memory !\n");
+ return;
+ }
+ pr_debug(" TCE : 0x%016lx..0x%016lx\n",
+ __pa(tce_mem), __pa(tce_mem) + P5IOC2_TCE_MEMORY - 1);
+ rc = opal_pci_set_hub_tce_memory(hub_id, __pa(tce_mem),
+ P5IOC2_TCE_MEMORY);
+ if (rc != OPAL_SUCCESS) {
+ pr_err(" Failed to allocate TCE memory, OPAL error %lld\n", rc);
+ return;
+ }
+
+ /* Count child PHBs */
+ for_each_child_of_node(np, phbn) {
+ if (of_device_is_compatible(phbn, "ibm,p5ioc2-pcix") ||
+ of_device_is_compatible(phbn, "ibm,p5ioc2-pciex"))
+ phb_count++;
+ }
+
+ /* Calculate how much TCE space we can give per PHB */
+ tce_per_phb = __rounddown_pow_of_two(P5IOC2_TCE_MEMORY / phb_count);
+ pr_info(" Allocating %lld MB of TCE memory per PHB\n",
+ tce_per_phb >> 20);
+
+ /* Initialize PHBs */
+ for_each_child_of_node(np, phbn) {
+ if (of_device_is_compatible(phbn, "ibm,p5ioc2-pcix") ||
+ of_device_is_compatible(phbn, "ibm,p5ioc2-pciex")) {
+ pnv_pci_init_p5ioc2_phb(phbn, tce_mem, tce_per_phb);
+ tce_mem += tce_per_phb;
+ }
+ }
+}
diff --git a/arch/powerpc/platforms/powernv/pci.c b/arch/powerpc/platforms/powernv/pci.c
new file mode 100644
index 0000000..b512489
--- /dev/null
+++ b/arch/powerpc/platforms/powernv/pci.c
@@ -0,0 +1,286 @@
+/*
+ * Support PCI/PCIe on PowerNV platforms
+ *
+ * Currently supports only P5IOC2
+ *
+ * Copyright 2011 Benjamin Herrenschmidt, IBM Corp.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/bootmem.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+
+#include <asm/sections.h>
+#include <asm/io.h>
+#include <asm/prom.h>
+#include <asm/pci-bridge.h>
+#include <asm/machdep.h>
+#include <asm/ppc-pci.h>
+#include <asm/opal.h>
+#include <asm/iommu.h>
+#include <asm/tce.h>
+#include <asm/abs_addr.h>
+
+#include "powernv.h"
+#include "pci.h"
+
+
+#define cfg_dbg(fmt...) do { } while(0)
+//#define cfg_dbg(fmt...) printk(fmt)
+
+
+static void pnv_pci_config_check_eeh(struct pnv_phb *phb, struct pci_bus *bus,
+ u32 bdfn)
+{
+ s64 rc;
+ u8 fstate;
+ u16 pcierr;
+ u32 pe_no;
+
+ /* Get PE# if we support IODA */
+ pe_no = phb->bdfn_to_pe ? phb->bdfn_to_pe(phb, bus, bdfn & 0xff) : 0;
+
+ /* Read freeze status */
+ rc = opal_pci_eeh_freeze_status(phb->opal_id, pe_no, &fstate, &pcierr,
+ NULL);
+ if (rc) {
+ pr_warning("PCI %d: Failed to read EEH status for PE#%d,"
+ " err %lld\n", phb->hose->global_number, pe_no, rc);
+ return;
+ }
+ cfg_dbg(" -> EEH check, bdfn=%04x PE%d fstate=%x\n",
+ bdfn, pe_no, fstate);
+ if (fstate != 0) {
+ rc = opal_pci_eeh_freeze_clear(phb->opal_id, pe_no,
+ OPAL_EEH_ACTION_CLEAR_FREEZE_ALL);
+ if (rc) {
+ pr_warning("PCI %d: Failed to clear EEH freeze state"
+ " for PE#%d, err %lld\n",
+ phb->hose->global_number, pe_no, rc);
+ }
+ }
+}
+
+static int pnv_pci_read_config(struct pci_bus *bus,
+ unsigned int devfn,
+ int where, int size, u32 *val)
+{
+ struct pci_controller *hose = pci_bus_to_host(bus);
+ struct pnv_phb *phb = hose->private_data;
+ u32 bdfn = (((uint64_t)bus->number) << 8) | devfn;
+ s64 rc;
+
+ if (hose == NULL)
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
+ switch (size) {
+ case 1: {
+ u8 v8;
+ rc = opal_pci_config_read_byte(phb->opal_id, bdfn, where, &v8);
+ *val = (rc == OPAL_SUCCESS) ? v8 : 0xff;
+ break;
+ }
+ case 2: {
+ u16 v16;
+ rc = opal_pci_config_read_half_word(phb->opal_id, bdfn, where,
+ &v16);
+ *val = (rc == OPAL_SUCCESS) ? v16 : 0xffff;
+ break;
+ }
+ case 4: {
+ u32 v32;
+ rc = opal_pci_config_read_word(phb->opal_id, bdfn, where, &v32);
+ *val = (rc == OPAL_SUCCESS) ? v32 : 0xffffffff;
+ break;
+ }
+ default:
+ return PCIBIOS_FUNC_NOT_SUPPORTED;
+ }
+ cfg_dbg("pnv_pci_read_config bus: %x devfn: %x +%x/%x -> %08x\n",
+ bus->number, devfn, where, size, *val);
+
+ /* Check if the PHB got frozen due to an error (no response) */
+ pnv_pci_config_check_eeh(phb, bus, bdfn);
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static int pnv_pci_write_config(struct pci_bus *bus,
+ unsigned int devfn,
+ int where, int size, u32 val)
+{
+ struct pci_controller *hose = pci_bus_to_host(bus);
+ struct pnv_phb *phb = hose->private_data;
+ u32 bdfn = (((uint64_t)bus->number) << 8) | devfn;
+
+ if (hose == NULL)
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
+ cfg_dbg("pnv_pci_write_config bus: %x devfn: %x +%x/%x -> %08x\n",
+ bus->number, devfn, where, size, val);
+ switch (size) {
+ case 1:
+ opal_pci_config_write_byte(phb->opal_id, bdfn, where, val);
+ break;
+ case 2:
+ opal_pci_config_write_half_word(phb->opal_id, bdfn, where, val);
+ break;
+ case 4:
+ opal_pci_config_write_word(phb->opal_id, bdfn, where, val);
+ break;
+ default:
+ return PCIBIOS_FUNC_NOT_SUPPORTED;
+ }
+ /* Check if the PHB got frozen due to an error (no response) */
+ pnv_pci_config_check_eeh(phb, bus, bdfn);
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+struct pci_ops pnv_pci_ops = {
+ .read = pnv_pci_read_config,
+ .write = pnv_pci_write_config,
+};
+
+static int pnv_tce_build(struct iommu_table *tbl, long index, long npages,
+ unsigned long uaddr, enum dma_data_direction direction,
+ struct dma_attrs *attrs)
+{
+ u64 proto_tce;
+ u64 *tcep;
+ u64 rpn;
+
+ proto_tce = TCE_PCI_READ; // Read allowed
+
+ if (direction != DMA_TO_DEVICE)
+ proto_tce |= TCE_PCI_WRITE;
+
+ tcep = ((u64 *)tbl->it_base) + index;
+
+ while (npages--) {
+ /* can't move this out since we might cross LMB boundary */
+ rpn = (virt_to_abs(uaddr)) >> TCE_SHIFT;
+ *tcep = proto_tce | (rpn & TCE_RPN_MASK) << TCE_RPN_SHIFT;
+
+ uaddr += TCE_PAGE_SIZE;
+ tcep++;
+ }
+ return 0;
+}
+
+static void pnv_tce_free(struct iommu_table *tbl, long index, long npages)
+{
+ u64 *tcep = ((u64 *)tbl->it_base) + index;
+
+ while (npages--)
+ *(tcep++) = 0;
+}
+
+void pnv_pci_setup_iommu_table(struct iommu_table *tbl,
+ void *tce_mem, u64 tce_size,
+ u64 dma_offset)
+{
+ tbl->it_blocksize = 16;
+ tbl->it_base = (unsigned long)tce_mem;
+ tbl->it_offset = dma_offset >> IOMMU_PAGE_SHIFT;
+ tbl->it_index = 0;
+ tbl->it_size = tce_size >> 3;
+ tbl->it_busno = 0;
+ tbl->it_type = TCE_PCI;
+}
+
+static struct iommu_table * __devinit
+pnv_pci_setup_bml_iommu(struct pci_controller *hose)
+{
+ struct iommu_table *tbl;
+ const __be64 *basep;
+ const __be32 *sizep;
+
+ basep = of_get_property(hose->dn, "linux,tce-base", NULL);
+ sizep = of_get_property(hose->dn, "linux,tce-size", NULL);
+ if (basep == NULL || sizep == NULL) {
+ pr_err("PCI: %s has missing tce entries !\n", hose->dn->full_name);
+ return NULL;
+ }
+ tbl = kzalloc_node(sizeof(struct iommu_table), GFP_KERNEL, hose->node);
+ if (WARN_ON(!tbl))
+ return NULL;
+ pnv_pci_setup_iommu_table(tbl, __va(be64_to_cpup(basep)),
+ be32_to_cpup(sizep), 0);
+ iommu_init_table(tbl, hose->node);
+ return tbl;
+}
+
+static void __devinit pnv_pci_dma_fallback_setup(struct pci_controller *hose,
+ struct pci_dev *pdev)
+{
+ struct device_node *np = pci_bus_to_OF_node(hose->bus);
+ struct pci_dn *pdn;
+
+ if (np == NULL)
+ return;
+ pdn = PCI_DN(np);
+ if (!pdn->iommu_table)
+ pdn->iommu_table = pnv_pci_setup_bml_iommu(hose);
+ if (!pdn->iommu_table)
+ return;
+ set_iommu_table_base(&pdev->dev, pdn->iommu_table);
+}
+
+static void __devinit pnv_pci_dma_dev_setup(struct pci_dev *pdev)
+{
+ struct pci_controller *hose = pci_bus_to_host(pdev->bus);
+ struct pnv_phb *phb = hose->private_data;
+
+ /* If we have no phb structure, try to setup a fallback based on
+ * the device-tree (RTAS PCI for example)
+ */
+ if (phb && phb->dma_dev_setup)
+ phb->dma_dev_setup(phb, pdev);
+ else
+ pnv_pci_dma_fallback_setup(hose, pdev);
+}
+
+void __init pnv_pci_init(void)
+{
+ struct device_node *np;
+
+ pci_set_flags(PCI_CAN_SKIP_ISA_ALIGN);
+
+ /* We do not want to just probe */
+ pci_probe_only = 0;
+
+ /* OPAL absent, try POPAL first then RTAS detection of PHBs */
+ if (!firmware_has_feature(FW_FEATURE_OPAL)) {
+#ifdef CONFIG_PPC_POWERNV_RTAS
+ init_pci_config_tokens();
+ find_and_init_phbs();
+#endif /* CONFIG_PPC_POWERNV_RTAS */
+ } else {
+ /* OPAL is here, do our normal stuff */
+
+ /* Look for p5ioc2 IO-Hubs */
+ for_each_compatible_node(np, NULL, "ibm,p5ioc2")
+ pnv_pci_init_p5ioc2_hub(np);
+ }
+
+ /* Setup the linkage between OF nodes and PHBs */
+ pci_devs_phb_init();
+
+ /* Configure IOMMU DMA hooks */
+ ppc_md.pci_dma_dev_setup = pnv_pci_dma_dev_setup;
+ ppc_md.tce_build = pnv_tce_build;
+ ppc_md.tce_free = pnv_tce_free;
+ set_pci_dma_ops(&dma_iommu_ops);
+
+}
diff --git a/arch/powerpc/platforms/powernv/pci.h b/arch/powerpc/platforms/powernv/pci.h
new file mode 100644
index 0000000..b8be721
--- /dev/null
+++ b/arch/powerpc/platforms/powernv/pci.h
@@ -0,0 +1,38 @@
+#ifndef __POWERNV_PCI_H
+#define __POWERNV_PCI_H
+
+struct pci_dn;
+
+enum pnv_phb_type {
+ PNV_PHB_P5IOC2,
+ PNV_PHB_IODA1,
+ PNV_PHB_IODA2,
+};
+
+struct pnv_phb {
+ struct pci_controller *hose;
+ enum pnv_phb_type type;
+ u64 opal_id;
+ void __iomem *regs;
+ spinlock_t lock;
+
+ void (*dma_dev_setup)(struct pnv_phb *phb, struct pci_dev *pdev);
+ void (*fixup_phb)(struct pci_controller *hose);
+ u32 (*bdfn_to_pe)(struct pnv_phb *phb, struct pci_bus *bus, u32 devfn);
+
+ union {
+ struct {
+ struct iommu_table iommu_table;
+ } p5ioc2;
+ };
+};
+
+extern struct pci_ops pnv_pci_ops;
+
+extern void pnv_pci_setup_iommu_table(struct iommu_table *tbl,
+ void *tce_mem, u64 tce_size,
+ u64 dma_offset);
+extern void pnv_pci_init_p5ioc2_hub(struct device_node *np);
+
+
+#endif /* __POWERNV_PCI_H */
diff --git a/arch/powerpc/platforms/powernv/powernv.h b/arch/powerpc/platforms/powernv/powernv.h
index 35b7160..8a9df7f 100644
--- a/arch/powerpc/platforms/powernv/powernv.h
+++ b/arch/powerpc/platforms/powernv/powernv.h
@@ -7,4 +7,10 @@ extern void pnv_smp_init(void);
static inline void pnv_smp_init(void) { }
#endif
+#ifdef CONFIG_PCI
+extern void pnv_pci_init(void);
+#else
+static inline void pnv_pci_init(void) { }
+#endif
+
#endif /* _POWERNV_H */
diff --git a/arch/powerpc/platforms/powernv/setup.c b/arch/powerpc/platforms/powernv/setup.c
index f0242f3..467bd4a 100644
--- a/arch/powerpc/platforms/powernv/setup.c
+++ b/arch/powerpc/platforms/powernv/setup.c
@@ -40,7 +40,8 @@ static void __init pnv_setup_arch(void)
/* Initialize SMP */
pnv_smp_init();
- /* XXX PCI */
+ /* Setup PCI */
+ pnv_pci_init();
/* Setup RTC and NVRAM callbacks */
if (firmware_has_feature(FW_FEATURE_OPAL))
--
1.7.4.1
^ permalink raw reply related
* [PATCH 18/21] powerpc/powernv: Machine check and other system interrupts
From: Benjamin Herrenschmidt @ 2011-09-10 14:20 UTC (permalink / raw)
To: linuxppc-dev
In-Reply-To: <1315664408-16797-1-git-send-email-benh@kernel.crashing.org>
OPAL can handle various interrupt for us such as Machine Checks (it
performs all sorts of recovery tasks and passes back control to us with
informations about the error), Hardware Management Interrupts and Softpatch
interrupts.
This wires up the mechanisms and prints out specific informations returned
by HAL when a machine check occurs.
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---
arch/powerpc/include/asm/opal.h | 2 +
arch/powerpc/include/asm/paca.h | 8 ++
arch/powerpc/kernel/asm-offsets.c | 10 +++
arch/powerpc/kernel/exceptions-64s.S | 27 ++++++-
arch/powerpc/platforms/powernv/opal.c | 130 ++++++++++++++++++++++++++++++++
arch/powerpc/platforms/powernv/setup.c | 1 +
6 files changed, 174 insertions(+), 4 deletions(-)
diff --git a/arch/powerpc/include/asm/opal.h b/arch/powerpc/include/asm/opal.h
index 9bb0efd..fca611b 100644
--- a/arch/powerpc/include/asm/opal.h
+++ b/arch/powerpc/include/asm/opal.h
@@ -436,6 +436,8 @@ extern void opal_get_rtc_time(struct rtc_time *tm);
extern unsigned long opal_get_boot_time(void);
extern void opal_nvram_init(void);
+extern int opal_machine_check(struct pt_regs *regs);
+
#endif /* __ASSEMBLY__ */
#endif /* __OPAL_H */
diff --git a/arch/powerpc/include/asm/paca.h b/arch/powerpc/include/asm/paca.h
index 516bfb3..17722c7 100644
--- a/arch/powerpc/include/asm/paca.h
+++ b/arch/powerpc/include/asm/paca.h
@@ -43,6 +43,7 @@ extern unsigned int debug_smp_processor_id(void); /* from linux/smp.h */
#define get_slb_shadow() (get_paca()->slb_shadow_ptr)
struct task_struct;
+struct opal_machine_check_event;
/*
* Defines the layout of the paca.
@@ -135,6 +136,13 @@ struct paca_struct {
u8 io_sync; /* writel() needs spin_unlock sync */
u8 irq_work_pending; /* IRQ_WORK interrupt while soft-disable */
+#ifdef CONFIG_PPC_POWERNV
+ /* Pointer to OPAL machine check event structure set by the
+ * early exception handler for use by high level C handler
+ */
+ struct opal_machine_check_event *opal_mc_evt;
+#endif
+
/* Stuff for accurate time accounting */
u64 user_time; /* accumulated usermode TB ticks */
u64 system_time; /* accumulated system TB ticks */
diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c
index 5f078bc..536ffa8 100644
--- a/arch/powerpc/kernel/asm-offsets.c
+++ b/arch/powerpc/kernel/asm-offsets.c
@@ -48,6 +48,9 @@
#ifdef CONFIG_PPC_ISERIES
#include <asm/iseries/alpaca.h>
#endif
+#ifdef CONFIG_PPC_POWERNV
+#include <asm/opal.h>
+#endif
#if defined(CONFIG_KVM) || defined(CONFIG_KVM_GUEST)
#include <linux/kvm_host.h>
#endif
@@ -609,5 +612,12 @@ int main(void)
arch.timing_last_enter.tv32.tbl));
#endif
+#ifdef CONFIG_PPC_POWERNV
+ DEFINE(OPAL_MC_GPR3, offsetof(struct opal_machine_check_event, gpr3));
+ DEFINE(OPAL_MC_SRR0, offsetof(struct opal_machine_check_event, srr0));
+ DEFINE(OPAL_MC_SRR1, offsetof(struct opal_machine_check_event, srr1));
+ DEFINE(PACA_OPAL_MC_EVT, offsetof(struct paca_struct, opal_mc_evt));
+#endif
+
return 0;
}
diff --git a/arch/powerpc/kernel/exceptions-64s.S b/arch/powerpc/kernel/exceptions-64s.S
index 41b02c7..d51458f 100644
--- a/arch/powerpc/kernel/exceptions-64s.S
+++ b/arch/powerpc/kernel/exceptions-64s.S
@@ -1143,7 +1143,7 @@ _GLOBAL(do_stab_bolted)
rfid
b . /* prevent speculative execution */
-#ifdef CONFIG_PPC_PSERIES
+#if defined(CONFIG_PPC_PSERIES) || defined(CONFIG_PPC_POWERNV)
/*
* Data area reserved for FWNMI option.
* This address (0x7000) is fixed by the RPA.
@@ -1151,7 +1151,7 @@ _GLOBAL(do_stab_bolted)
.= 0x7000
.globl fwnmi_data_area
fwnmi_data_area:
-#endif /* CONFIG_PPC_PSERIES */
+#endif /* defined(CONFIG_PPC_PSERIES) || defined(CONFIG_PPC_POWERNV) */
/* iSeries does not use the FWNMI stuff, so it is safe to put
* this here, even if we later allow kernels that will boot on
@@ -1176,9 +1176,12 @@ xLparMap:
#endif /* CONFIG_PPC_ISERIES */
-#ifdef CONFIG_PPC_PSERIES
+#if defined(CONFIG_PPC_PSERIES) || defined(CONFIG_PPC_POWERNV)
+ /* pseries and powernv need to keep the whole page from
+ * 0x7000 to 0x8000 free for use by the firmware
+ */
. = 0x8000
-#endif /* CONFIG_PPC_PSERIES */
+#endif /* defined(CONFIG_PPC_PSERIES) || defined(CONFIG_PPC_POWERNV) */
/*
* Space for CPU0's segment table.
@@ -1193,3 +1196,19 @@ xLparMap:
.globl initial_stab
initial_stab:
.space 4096
+#ifdef CONFIG_PPC_POWERNV
+_GLOBAL(opal_mc_secondary_handler)
+ HMT_MEDIUM
+ SET_SCRATCH0(r13)
+ GET_PACA(r13)
+ clrldi r3,r3,2
+ tovirt(r3,r3)
+ std r3,PACA_OPAL_MC_EVT(r13)
+ ld r13,OPAL_MC_SRR0(r3)
+ mtspr SPRN_SRR0,r13
+ ld r13,OPAL_MC_SRR1(r3)
+ mtspr SPRN_SRR1,r13
+ ld r3,OPAL_MC_GPR3(r3)
+ GET_SCRATCH0(r13)
+ b machine_check_pSeries
+#endif /* CONFIG_PPC_POWERNV */
diff --git a/arch/powerpc/platforms/powernv/opal.c b/arch/powerpc/platforms/powernv/opal.c
index 5a598ca..aaa0dba 100644
--- a/arch/powerpc/platforms/powernv/opal.c
+++ b/arch/powerpc/platforms/powernv/opal.c
@@ -27,12 +27,14 @@ struct opal {
static struct device_node *opal_node;
static DEFINE_SPINLOCK(opal_write_lock);
+extern u64 opal_mc_secondary_handler[];
int __init early_init_dt_scan_opal(unsigned long node,
const char *uname, int depth, void *data)
{
const void *basep, *entryp;
unsigned long basesz, entrysz;
+ u64 glue;
if (depth != 1 || strcmp(uname, "ibm,opal") != 0)
return 0;
@@ -59,6 +61,19 @@ int __init early_init_dt_scan_opal(unsigned long node,
printk("OPAL V1 detected !\n");
}
+ /* Hookup some exception handlers. We use the fwnmi area at 0x7000
+ * to provide the glue space to OPAL
+ */
+ glue = 0x7000;
+ opal_register_exception_handler(OPAL_MACHINE_CHECK_HANDLER,
+ __pa(opal_mc_secondary_handler[0]),
+ glue);
+ glue += 128;
+ opal_register_exception_handler(OPAL_HYPERVISOR_MAINTENANCE_HANDLER,
+ 0, glue);
+ glue += 128;
+ opal_register_exception_handler(OPAL_SOFTPATCH_HANDLER, 0, glue);
+
return 1;
}
@@ -136,6 +151,121 @@ int opal_put_chars(uint32_t vtermno, const char *data, int total_len)
return written;
}
+int opal_machine_check(struct pt_regs *regs)
+{
+ struct opal_machine_check_event *opal_evt = get_paca()->opal_mc_evt;
+ struct opal_machine_check_event evt;
+ const char *level, *sevstr, *subtype;
+ static const char *opal_mc_ue_types[] = {
+ "Indeterminate",
+ "Instruction fetch",
+ "Page table walk ifetch",
+ "Load/Store",
+ "Page table walk Load/Store",
+ };
+ static const char *opal_mc_slb_types[] = {
+ "Indeterminate",
+ "Parity",
+ "Multihit",
+ };
+ static const char *opal_mc_erat_types[] = {
+ "Indeterminate",
+ "Parity",
+ "Multihit",
+ };
+ static const char *opal_mc_tlb_types[] = {
+ "Indeterminate",
+ "Parity",
+ "Multihit",
+ };
+
+ /* Copy the event structure and release the original */
+ evt = *opal_evt;
+ opal_evt->in_use = 0;
+
+ /* Print things out */
+ if (evt.version != OpalMCE_V1) {
+ pr_err("Machine Check Exception, Unknown event version %d !\n",
+ evt.version);
+ return 0;
+ }
+ switch(evt.severity) {
+ case OpalMCE_SEV_NO_ERROR:
+ level = KERN_INFO;
+ sevstr = "Harmless";
+ break;
+ case OpalMCE_SEV_WARNING:
+ level = KERN_WARNING;
+ sevstr = "";
+ break;
+ case OpalMCE_SEV_ERROR_SYNC:
+ level = KERN_ERR;
+ sevstr = "Severe";
+ break;
+ case OpalMCE_SEV_FATAL:
+ default:
+ level = KERN_ERR;
+ sevstr = "Fatal";
+ break;
+ }
+
+ printk("%s%s Machine check interrupt [%s]\n", level, sevstr,
+ evt.disposition == OpalMCE_DISPOSITION_RECOVERED ?
+ "Recovered" : "[Not recovered");
+ printk("%s Initiator: %s\n", level,
+ evt.initiator == OpalMCE_INITIATOR_CPU ? "CPU" : "Unknown");
+ switch(evt.error_type) {
+ case OpalMCE_ERROR_TYPE_UE:
+ subtype = evt.u.ue_error.ue_error_type <
+ ARRAY_SIZE(opal_mc_ue_types) ?
+ opal_mc_ue_types[evt.u.ue_error.ue_error_type]
+ : "Unknown";
+ printk("%s Error type: UE [%s]\n", level, subtype);
+ if (evt.u.ue_error.effective_address_provided)
+ printk("%s Effective address: %016llx\n",
+ level, evt.u.ue_error.effective_address);
+ if (evt.u.ue_error.physical_address_provided)
+ printk("%s Physial address: %016llx\n",
+ level, evt.u.ue_error.physical_address);
+ break;
+ case OpalMCE_ERROR_TYPE_SLB:
+ subtype = evt.u.slb_error.slb_error_type <
+ ARRAY_SIZE(opal_mc_slb_types) ?
+ opal_mc_slb_types[evt.u.slb_error.slb_error_type]
+ : "Unknown";
+ printk("%s Error type: SLB [%s]\n", level, subtype);
+ if (evt.u.slb_error.effective_address_provided)
+ printk("%s Effective address: %016llx\n",
+ level, evt.u.slb_error.effective_address);
+ break;
+ case OpalMCE_ERROR_TYPE_ERAT:
+ subtype = evt.u.erat_error.erat_error_type <
+ ARRAY_SIZE(opal_mc_erat_types) ?
+ opal_mc_erat_types[evt.u.erat_error.erat_error_type]
+ : "Unknown";
+ printk("%s Error type: ERAT [%s]\n", level, subtype);
+ if (evt.u.erat_error.effective_address_provided)
+ printk("%s Effective address: %016llx\n",
+ level, evt.u.erat_error.effective_address);
+ break;
+ case OpalMCE_ERROR_TYPE_TLB:
+ subtype = evt.u.tlb_error.tlb_error_type <
+ ARRAY_SIZE(opal_mc_tlb_types) ?
+ opal_mc_tlb_types[evt.u.tlb_error.tlb_error_type]
+ : "Unknown";
+ printk("%s Error type: TLB [%s]\n", level, subtype);
+ if (evt.u.tlb_error.effective_address_provided)
+ printk("%s Effective address: %016llx\n",
+ level, evt.u.tlb_error.effective_address);
+ break;
+ default:
+ case OpalMCE_ERROR_TYPE_UNKNOWN:
+ printk("%s Error type: Unknown\n", level);
+ break;
+ }
+ return evt.severity == OpalMCE_SEV_FATAL ? 0 : 1;
+}
+
static irqreturn_t opal_interrupt(int irq, void *data)
{
uint64_t events;
diff --git a/arch/powerpc/platforms/powernv/setup.c b/arch/powerpc/platforms/powernv/setup.c
index 4a2b2e2..f0242f3 100644
--- a/arch/powerpc/platforms/powernv/setup.c
+++ b/arch/powerpc/platforms/powernv/setup.c
@@ -141,6 +141,7 @@ static void __init pnv_setup_machdep_opal(void)
ppc_md.restart = pnv_restart;
ppc_md.power_off = pnv_power_off;
ppc_md.halt = pnv_halt;
+ ppc_md.machine_check_exception = opal_machine_check;
}
#ifdef CONFIG_PPC_POWERNV_RTAS
--
1.7.4.1
^ permalink raw reply related
* [PATCH 15/21] powerpc/powernv: Add RTC and NVRAM support plus RTAS fallbacks
From: Benjamin Herrenschmidt @ 2011-09-10 14:20 UTC (permalink / raw)
To: linuxppc-dev
In-Reply-To: <1315664408-16797-1-git-send-email-benh@kernel.crashing.org>
Implements OPAL RTC and NVRAM support and wire all that up to
the powernv platform.
We use RTAS for RTC as a fallback if available. Using RTAS for nvram
is not supported yet, pending some rework/cleanup and generalization
of the pSeries & CHRP code. We also use RTAS fallbacks for power off
and reboot
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---
arch/powerpc/include/asm/opal.h | 6 ++
arch/powerpc/platforms/powernv/Makefile | 2 +
arch/powerpc/platforms/powernv/opal-nvram.c | 88 ++++++++++++++++++++++++
arch/powerpc/platforms/powernv/opal-rtc.c | 97 +++++++++++++++++++++++++++
arch/powerpc/platforms/powernv/setup.c | 57 ++++++++++------
5 files changed, 229 insertions(+), 21 deletions(-)
create mode 100644 arch/powerpc/platforms/powernv/opal-nvram.c
create mode 100644 arch/powerpc/platforms/powernv/opal-rtc.c
diff --git a/arch/powerpc/include/asm/opal.h b/arch/powerpc/include/asm/opal.h
index dbeabea..9bb0efd 100644
--- a/arch/powerpc/include/asm/opal.h
+++ b/arch/powerpc/include/asm/opal.h
@@ -430,6 +430,12 @@ extern int opal_put_chars(uint32_t vtermno, const char *buf, int total_len);
extern void hvc_opal_init_early(void);
+struct rtc_time;
+extern int opal_set_rtc_time(struct rtc_time *tm);
+extern void opal_get_rtc_time(struct rtc_time *tm);
+extern unsigned long opal_get_boot_time(void);
+extern void opal_nvram_init(void);
+
#endif /* __ASSEMBLY__ */
#endif /* __OPAL_H */
diff --git a/arch/powerpc/platforms/powernv/Makefile b/arch/powerpc/platforms/powernv/Makefile
index 8f69c0d..618ad83 100644
--- a/arch/powerpc/platforms/powernv/Makefile
+++ b/arch/powerpc/platforms/powernv/Makefile
@@ -1,2 +1,4 @@
obj-y += setup.o opal-takeover.o opal-wrappers.o opal.o
+obj-y += opal-rtc.o opal-nvram.o
+
obj-$(CONFIG_SMP) += smp.o
diff --git a/arch/powerpc/platforms/powernv/opal-nvram.c b/arch/powerpc/platforms/powernv/opal-nvram.c
new file mode 100644
index 0000000..3f83e1a
--- /dev/null
+++ b/arch/powerpc/platforms/powernv/opal-nvram.c
@@ -0,0 +1,88 @@
+/*
+ * PowerNV nvram code.
+ *
+ * Copyright 2011 IBM Corp.
+ *
+ * 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.
+ */
+
+#define DEBUG
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/of.h>
+
+#include <asm/opal.h>
+#include <asm/machdep.h>
+
+static unsigned int nvram_size;
+
+static ssize_t opal_nvram_size(void)
+{
+ return nvram_size;
+}
+
+static ssize_t opal_nvram_read(char *buf, size_t count, loff_t *index)
+{
+ s64 rc;
+ int off;
+
+ if (*index >= nvram_size)
+ return 0;
+ off = *index;
+ if ((off + count) > nvram_size)
+ count = nvram_size - off;
+ rc = opal_read_nvram(__pa(buf), count, off);
+ if (rc != OPAL_SUCCESS)
+ return -EIO;
+ *index += count;
+ return count;
+}
+
+static ssize_t opal_nvram_write(char *buf, size_t count, loff_t *index)
+{
+ s64 rc = OPAL_BUSY;
+ int off;
+
+ if (*index >= nvram_size)
+ return 0;
+ off = *index;
+ if ((off + count) > nvram_size)
+ count = nvram_size - off;
+
+ while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) {
+ rc = opal_write_nvram(__pa(buf), count, off);
+ if (rc == OPAL_BUSY_EVENT)
+ opal_poll_events(NULL);
+ }
+ *index += count;
+ return count;
+}
+
+void __init opal_nvram_init(void)
+{
+ struct device_node *np;
+ const u32 *nbytes_p;
+
+ np = of_find_compatible_node(NULL, NULL, "ibm,opal-nvram");
+ if (np == NULL)
+ return;
+
+ nbytes_p = of_get_property(np, "#bytes", NULL);
+ if (!nbytes_p) {
+ of_node_put(np);
+ return;
+ }
+ nvram_size = *nbytes_p;
+
+ printk(KERN_INFO "OPAL nvram setup, %u bytes\n", nvram_size);
+ of_node_put(np);
+
+ ppc_md.nvram_read = opal_nvram_read;
+ ppc_md.nvram_write = opal_nvram_write;
+ ppc_md.nvram_size = opal_nvram_size;
+}
+
diff --git a/arch/powerpc/platforms/powernv/opal-rtc.c b/arch/powerpc/platforms/powernv/opal-rtc.c
new file mode 100644
index 0000000..2aa7641
--- /dev/null
+++ b/arch/powerpc/platforms/powernv/opal-rtc.c
@@ -0,0 +1,97 @@
+/*
+ * PowerNV Real Time Clock.
+ *
+ * Copyright 2011 IBM Corp.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/time.h>
+#include <linux/bcd.h>
+#include <linux/rtc.h>
+#include <linux/delay.h>
+
+#include <asm/opal.h>
+#include <asm/firmware.h>
+
+static void opal_to_tm(u32 y_m_d, u64 h_m_s_ms, struct rtc_time *tm)
+{
+ tm->tm_year = ((bcd2bin(y_m_d >> 24) * 100) +
+ bcd2bin((y_m_d >> 16) & 0xff)) - 1900;
+ tm->tm_mon = bcd2bin((y_m_d >> 8) & 0xff) - 1;
+ tm->tm_mday = bcd2bin(y_m_d & 0xff);
+ tm->tm_hour = bcd2bin((h_m_s_ms >> 56) & 0xff);
+ tm->tm_min = bcd2bin((h_m_s_ms >> 48) & 0xff);
+ tm->tm_sec = bcd2bin((h_m_s_ms >> 40) & 0xff);
+
+ GregorianDay(tm);
+}
+
+unsigned long __init opal_get_boot_time(void)
+{
+ struct rtc_time tm;
+ u32 y_m_d;
+ u64 h_m_s_ms;
+ long rc = OPAL_BUSY;
+
+ while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) {
+ rc = opal_rtc_read(&y_m_d, &h_m_s_ms);
+ if (rc == OPAL_BUSY_EVENT)
+ opal_poll_events(NULL);
+ else
+ mdelay(10);
+ }
+ if (rc != OPAL_SUCCESS)
+ return 0;
+ opal_to_tm(y_m_d, h_m_s_ms, &tm);
+ return mktime(tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
+ tm.tm_hour, tm.tm_min, tm.tm_sec);
+}
+
+void opal_get_rtc_time(struct rtc_time *tm)
+{
+ long rc = OPAL_BUSY;
+ u32 y_m_d;
+ u64 h_m_s_ms;
+
+ while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) {
+ rc = opal_rtc_read(&y_m_d, &h_m_s_ms);
+ if (rc == OPAL_BUSY_EVENT)
+ opal_poll_events(NULL);
+ else
+ mdelay(10);
+ }
+ if (rc != OPAL_SUCCESS)
+ return;
+ opal_to_tm(y_m_d, h_m_s_ms, tm);
+}
+
+int opal_set_rtc_time(struct rtc_time *tm)
+{
+ long rc = OPAL_BUSY;
+ u32 y_m_d = 0;
+ u64 h_m_s_ms = 0;
+
+ y_m_d |= ((u32)bin2bcd((tm->tm_year + 1900) / 100)) << 24;
+ y_m_d |= ((u32)bin2bcd((tm->tm_year + 1900) % 100)) << 16;
+ y_m_d |= ((u32)bin2bcd((tm->tm_mon + 1))) << 8;
+ y_m_d |= ((u32)bin2bcd(tm->tm_mday));
+
+ h_m_s_ms |= ((u64)bin2bcd(tm->tm_hour)) << 56;
+ h_m_s_ms |= ((u64)bin2bcd(tm->tm_min)) << 48;
+ h_m_s_ms |= ((u64)bin2bcd(tm->tm_sec)) << 40;
+
+ while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) {
+ rc = opal_rtc_write(y_m_d, h_m_s_ms);
+ if (rc == OPAL_BUSY_EVENT)
+ opal_poll_events(NULL);
+ else
+ mdelay(10);
+ }
+ return rc == OPAL_SUCCESS ? 0 : -EIO;
+}
diff --git a/arch/powerpc/platforms/powernv/setup.c b/arch/powerpc/platforms/powernv/setup.c
index 0fac0a6..4a2b2e2 100644
--- a/arch/powerpc/platforms/powernv/setup.c
+++ b/arch/powerpc/platforms/powernv/setup.c
@@ -29,7 +29,9 @@
#include <asm/machdep.h>
#include <asm/firmware.h>
#include <asm/xics.h>
+#include <asm/rtas.h>
#include <asm/opal.h>
+#include <asm/xics.h>
#include "powernv.h"
@@ -40,7 +42,9 @@ static void __init pnv_setup_arch(void)
/* XXX PCI */
- /* XXX NVRAM */
+ /* Setup RTC and NVRAM callbacks */
+ if (firmware_has_feature(FW_FEATURE_OPAL))
+ opal_nvram_init();
/* Enable NAP mode */
powersave_nap = 1;
@@ -118,30 +122,40 @@ static void __noreturn pnv_halt(void)
pnv_power_off();
}
-static unsigned long __init pnv_get_boot_time(void)
-{
- return 0;
-}
-
-static void pnv_get_rtc_time(struct rtc_time *rtc_tm)
+static void pnv_progress(char *s, unsigned short hex)
{
}
-static int pnv_set_rtc_time(struct rtc_time *tm)
+#ifdef CONFIG_KEXEC
+static void pnv_kexec_cpu_down(int crash_shutdown, int secondary)
{
- return 0;
+ xics_kexec_teardown_cpu(secondary);
}
+#endif /* CONFIG_KEXEC */
-static void pnv_progress(char *s, unsigned short hex)
+static void __init pnv_setup_machdep_opal(void)
{
+ ppc_md.get_boot_time = opal_get_boot_time;
+ ppc_md.get_rtc_time = opal_get_rtc_time;
+ ppc_md.set_rtc_time = opal_set_rtc_time;
+ ppc_md.restart = pnv_restart;
+ ppc_md.power_off = pnv_power_off;
+ ppc_md.halt = pnv_halt;
}
-#ifdef CONFIG_KEXEC
-static void pnv_kexec_cpu_down(int crash_shutdown, int secondary)
+#ifdef CONFIG_PPC_POWERNV_RTAS
+static void __init pnv_setup_machdep_rtas(void)
{
- xics_kexec_teardown_cpu(secondary);
+ if (rtas_token("get-time-of-day") != RTAS_UNKNOWN_SERVICE) {
+ ppc_md.get_boot_time = rtas_get_boot_time;
+ ppc_md.get_rtc_time = rtas_get_rtc_time;
+ ppc_md.set_rtc_time = rtas_set_rtc_time;
+ }
+ ppc_md.restart = rtas_restart;
+ ppc_md.power_off = rtas_power_off;
+ ppc_md.halt = rtas_halt;
}
-#endif /* CONFIG_KEXEC */
+#endif /* CONFIG_PPC_POWERNV_RTAS */
static int __init pnv_probe(void)
{
@@ -152,6 +166,13 @@ static int __init pnv_probe(void)
hpte_init_native();
+ if (firmware_has_feature(FW_FEATURE_OPAL))
+ pnv_setup_machdep_opal();
+#ifdef CONFIG_PPC_POWERNV_RTAS
+ else if (rtas.base)
+ pnv_setup_machdep_rtas();
+#endif /* CONFIG_PPC_POWERNV_RTAS */
+
pr_debug("PowerNV detected !\n");
return 1;
@@ -160,16 +181,10 @@ static int __init pnv_probe(void)
define_machine(powernv) {
.name = "PowerNV",
.probe = pnv_probe,
- .setup_arch = pnv_setup_arch,
.init_early = pnv_init_early,
+ .setup_arch = pnv_setup_arch,
.init_IRQ = pnv_init_IRQ,
.show_cpuinfo = pnv_show_cpuinfo,
- .restart = pnv_restart,
- .power_off = pnv_power_off,
- .halt = pnv_halt,
- .get_boot_time = pnv_get_boot_time,
- .get_rtc_time = pnv_get_rtc_time,
- .set_rtc_time = pnv_set_rtc_time,
.progress = pnv_progress,
.power_save = power7_idle,
.calibrate_decr = generic_calibrate_decr,
--
1.7.4.1
^ permalink raw reply related
* [PATCH 14/21] powerpc/powernv: Hookup reboot and poweroff functions
From: Benjamin Herrenschmidt @ 2011-09-10 14:20 UTC (permalink / raw)
To: linuxppc-dev
In-Reply-To: <1315664408-16797-1-git-send-email-benh@kernel.crashing.org>
This calls the respective HAL functions, and spin on hal_poll_event()
to ensure the HAL has a chance to communicate with the FSP to trigger
the reboot or shutdown operation
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---
arch/powerpc/platforms/powernv/setup.c | 32 ++++++++++++++++++++++++++------
1 files changed, 26 insertions(+), 6 deletions(-)
diff --git a/arch/powerpc/platforms/powernv/setup.c b/arch/powerpc/platforms/powernv/setup.c
index 07ba1ec..0fac0a6 100644
--- a/arch/powerpc/platforms/powernv/setup.c
+++ b/arch/powerpc/platforms/powernv/setup.c
@@ -83,19 +83,39 @@ static void pnv_show_cpuinfo(struct seq_file *m)
of_node_put(root);
}
-static void pnv_restart(char *cmd)
+static void __noreturn pnv_restart(char *cmd)
{
- for (;;);
+ long rc = OPAL_BUSY;
+
+ while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) {
+ rc = opal_cec_reboot();
+ if (rc == OPAL_BUSY_EVENT)
+ opal_poll_events(NULL);
+ else
+ mdelay(10);
+ }
+ for (;;)
+ opal_poll_events(NULL);
}
-static void pnv_power_off(void)
+static void __noreturn pnv_power_off(void)
{
- for (;;);
+ long rc = OPAL_BUSY;
+
+ while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) {
+ rc = opal_cec_power_down(0);
+ if (rc == OPAL_BUSY_EVENT)
+ opal_poll_events(NULL);
+ else
+ mdelay(10);
+ }
+ for (;;)
+ opal_poll_events(NULL);
}
-static void pnv_halt(void)
+static void __noreturn pnv_halt(void)
{
- for (;;);
+ pnv_power_off();
}
static unsigned long __init pnv_get_boot_time(void)
--
1.7.4.1
^ permalink raw reply related
* [PATCH 13/21] powerpc/powernv: Support for OPAL console
From: Benjamin Herrenschmidt @ 2011-09-10 14:20 UTC (permalink / raw)
To: linuxppc-dev
In-Reply-To: <1315664408-16797-1-git-send-email-benh@kernel.crashing.org>
This adds a udbg and an hvc console backend for supporting a console
using the OPAL console interfaces.
On OPAL v1 we have hvc0 mapped to whatever console the system was
configured for (network or hvsi serial port) via the service
processor.
On OPAL v2 we have hvcN mapped to the Nth console provided by OPAL
which generally corresponds to:
hvc0 : network console (raw protocol)
hvc1 : serial port S1 (hvsi)
hvc2 : serial port S2 (hvsi)
Note: At this point, early debug console only works with OPAL v1
and shouldn't be enabled in a normal kernel.
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---
arch/powerpc/Kconfig.debug | 31 +++
arch/powerpc/include/asm/opal.h | 5 +
arch/powerpc/include/asm/udbg.h | 2 +
arch/powerpc/kernel/head_64.S | 14 +-
arch/powerpc/kernel/udbg.c | 4 +
arch/powerpc/platforms/powernv/opal.c | 31 ++-
arch/powerpc/platforms/powernv/setup.c | 14 +-
drivers/tty/hvc/Kconfig | 9 +
drivers/tty/hvc/Makefile | 1 +
drivers/tty/hvc/hvc_opal.c | 424 ++++++++++++++++++++++++++++++++
drivers/tty/hvc/hvsi_lib.c | 4 +-
11 files changed, 517 insertions(+), 22 deletions(-)
create mode 100644 drivers/tty/hvc/hvc_opal.c
diff --git a/arch/powerpc/Kconfig.debug b/arch/powerpc/Kconfig.debug
index cc01f1d..e61830f 100644
--- a/arch/powerpc/Kconfig.debug
+++ b/arch/powerpc/Kconfig.debug
@@ -257,8 +257,27 @@ config PPC_EARLY_DEBUG_WSP
depends on PPC_WSP
select PPC_UDBG_16550
+config PPC_EARLY_DEBUG_OPAL_RAW
+ bool "OPAL raw console"
+ depends on HVC_OPAL
+ help
+ Select this to enable early debugging for the PowerNV platform
+ using a "raw" console
+
+config PPC_EARLY_DEBUG_OPAL_HVSI
+ bool "OPAL hvsi console"
+ depends on HVC_OPAL
+ help
+ Select this to enable early debugging for the PowerNV platform
+ using an "hvsi" console
+
endchoice
+config PPC_EARLY_DEBUG_OPAL
+ def_bool y
+ depends on PPC_EARLY_DEBUG_OPAL_RAW || PPC_EARLY_DEBUG_OPAL_HVSI
+
+
config PPC_EARLY_DEBUG_HVSI_VTERMNO
hex "vterm number to use with early debug HVSI"
depends on PPC_EARLY_DEBUG_LPAR_HVSI
@@ -267,6 +286,18 @@ config PPC_EARLY_DEBUG_HVSI_VTERMNO
You probably want 0x30000000 for your first serial port and
0x30000001 for your second one
+config PPC_EARLY_DEBUG_OPAL_VTERMNO
+ hex "vterm number to use with OPAL early debug"
+ depends on PPC_EARLY_DEBUG_OPAL
+ default "0"
+ help
+ This correspond to which /dev/hvcN you want to use for early
+ debug.
+
+ On OPAL v1 (takeover) this should always be 0
+ On OPAL v2, this will be 0 for network console and 1 or 2 for
+ the machine built-in serial ports.
+
config PPC_EARLY_DEBUG_44x_PHYSLOW
hex "Low 32 bits of early debug UART physical address"
depends on PPC_EARLY_DEBUG_44x
diff --git a/arch/powerpc/include/asm/opal.h b/arch/powerpc/include/asm/opal.h
index 53cda41..dbeabea 100644
--- a/arch/powerpc/include/asm/opal.h
+++ b/arch/powerpc/include/asm/opal.h
@@ -425,6 +425,11 @@ extern void hvc_opal_init_early(void);
extern int early_init_dt_scan_opal(unsigned long node, const char *uname,
int depth, void *data);
+extern int opal_get_chars(uint32_t vtermno, char *buf, int count);
+extern int opal_put_chars(uint32_t vtermno, const char *buf, int total_len);
+
+extern void hvc_opal_init_early(void);
+
#endif /* __ASSEMBLY__ */
#endif /* __OPAL_H */
diff --git a/arch/powerpc/include/asm/udbg.h b/arch/powerpc/include/asm/udbg.h
index 93e05d1..2ac1753 100644
--- a/arch/powerpc/include/asm/udbg.h
+++ b/arch/powerpc/include/asm/udbg.h
@@ -54,6 +54,8 @@ extern void __init udbg_init_40x_realmode(void);
extern void __init udbg_init_cpm(void);
extern void __init udbg_init_usbgecko(void);
extern void __init udbg_init_wsp(void);
+extern void __init udbg_init_debug_opal_raw(void);
+extern void __init udbg_init_debug_opal_hvsi(void);
#endif /* __KERNEL__ */
#endif /* _ASM_POWERPC_UDBG_H */
diff --git a/arch/powerpc/kernel/head_64.S b/arch/powerpc/kernel/head_64.S
index dea8191..06c7251 100644
--- a/arch/powerpc/kernel/head_64.S
+++ b/arch/powerpc/kernel/head_64.S
@@ -53,7 +53,8 @@
* 2. The kernel is entered at __start
* -or- For OPAL entry:
* 1. The MMU is off, processor in HV mode, primary CPU enters at 0
- * with device-tree in gpr3
+ * with device-tree in gpr3. We also get OPAL base in r8 and
+ * entry in r9 for debugging purposes
* 2. Secondary processors enter at 0x60 with PIR in gpr3
*
* For iSeries:
@@ -335,6 +336,11 @@ _GLOBAL(__start_initialization_multiplatform)
/* Save parameters */
mr r31,r3
mr r30,r4
+#ifdef CONFIG_PPC_EARLY_DEBUG_OPAL
+ /* Save OPAL entry */
+ mr r28,r8
+ mr r29,r9
+#endif
#ifdef CONFIG_PPC_BOOK3E
bl .start_initialization_book3e
@@ -711,6 +717,12 @@ _INIT_STATIC(start_here_multiplatform)
bdnz 3b
4:
+#ifdef CONFIG_PPC_EARLY_DEBUG_OPAL
+ /* Setup OPAL entry */
+ std r28,0(r11);
+ std r29,8(r11);
+#endif
+
#ifndef CONFIG_PPC_BOOK3E
mfmsr r6
ori r6,r6,MSR_RI
diff --git a/arch/powerpc/kernel/udbg.c b/arch/powerpc/kernel/udbg.c
index faa82c1..ea82faf 100644
--- a/arch/powerpc/kernel/udbg.c
+++ b/arch/powerpc/kernel/udbg.c
@@ -67,6 +67,10 @@ void __init udbg_early_init(void)
udbg_init_usbgecko();
#elif defined(CONFIG_PPC_EARLY_DEBUG_WSP)
udbg_init_wsp();
+#elif defined(CONFIG_PPC_EARLY_DEBUG_OPAL_RAW)
+ udbg_init_debug_opal_raw();
+#elif defined(CONFIG_PPC_EARLY_DEBUG_OPAL_HVSI)
+ udbg_init_debug_opal_hvsi();
#endif
#ifdef CONFIG_PPC_EARLY_DEBUG
diff --git a/arch/powerpc/platforms/powernv/opal.c b/arch/powerpc/platforms/powernv/opal.c
index 8d55107..7887733 100644
--- a/arch/powerpc/platforms/powernv/opal.c
+++ b/arch/powerpc/platforms/powernv/opal.c
@@ -67,7 +67,7 @@ int opal_get_chars(uint32_t vtermno, char *buf, int count)
u64 evt;
if (!opal.entry)
- return 0;
+ return -ENODEV;
opal_poll_events(&evt);
if ((evt & OPAL_EVENT_CONSOLE_INPUT) == 0)
return 0;
@@ -81,31 +81,38 @@ int opal_get_chars(uint32_t vtermno, char *buf, int count)
int opal_put_chars(uint32_t vtermno, const char *data, int total_len)
{
int written = 0;
- s64 len, rc = OPAL_BUSY;
+ s64 len, rc;
unsigned long flags;
u64 evt;
if (!opal.entry)
- return 0;
+ return -ENODEV;
/* We want put_chars to be atomic to avoid mangling of hvsi
* packets. To do that, we first test for room and return
- * -EAGAIN if there isn't enough
+ * -EAGAIN if there isn't enough.
+ *
+ * Unfortunately, opal_console_write_buffer_space() doesn't
+ * appear to work on opal v1, so we just assume there is
+ * enough room and be done with it
*/
spin_lock_irqsave(&opal_write_lock, flags);
- rc = opal_console_write_buffer_space(vtermno, &len);
- if (rc || len < total_len) {
- spin_unlock_irqrestore(&opal_write_lock, flags);
- /* Closed -> drop characters */
- if (rc)
- return total_len;
- opal_poll_events(&evt);
- return -EAGAIN;
+ if (firmware_has_feature(FW_FEATURE_OPALv2)) {
+ rc = opal_console_write_buffer_space(vtermno, &len);
+ if (rc || len < total_len) {
+ spin_unlock_irqrestore(&opal_write_lock, flags);
+ /* Closed -> drop characters */
+ if (rc)
+ return total_len;
+ opal_poll_events(&evt);
+ return -EAGAIN;
+ }
}
/* We still try to handle partial completions, though they
* should no longer happen.
*/
+ rc = OPAL_BUSY;
while(total_len > 0 && (rc == OPAL_BUSY ||
rc == OPAL_BUSY_EVENT || rc == OPAL_SUCCESS)) {
len = total_len;
diff --git a/arch/powerpc/platforms/powernv/setup.c b/arch/powerpc/platforms/powernv/setup.c
index b6e5ff8..07ba1ec 100644
--- a/arch/powerpc/platforms/powernv/setup.c
+++ b/arch/powerpc/platforms/powernv/setup.c
@@ -29,17 +29,12 @@
#include <asm/machdep.h>
#include <asm/firmware.h>
#include <asm/xics.h>
+#include <asm/opal.h>
#include "powernv.h"
static void __init pnv_setup_arch(void)
{
- /* Force console to hvc for now until we have sorted out the
- * real console situation for the platform. This will make
- * hvc_udbg work at least.
- */
- add_preferred_console("hvc", 0, NULL);
-
/* Initialize SMP */
pnv_smp_init();
@@ -55,7 +50,12 @@ static void __init pnv_setup_arch(void)
static void __init pnv_init_early(void)
{
- /* XXX IOMMU */
+#ifdef CONFIG_HVC_OPAL
+ if (firmware_has_feature(FW_FEATURE_OPAL))
+ hvc_opal_init_early();
+ else
+#endif
+ add_preferred_console("hvc", 0, NULL);
}
static void __init pnv_init_IRQ(void)
diff --git a/drivers/tty/hvc/Kconfig b/drivers/tty/hvc/Kconfig
index e371753..4222035 100644
--- a/drivers/tty/hvc/Kconfig
+++ b/drivers/tty/hvc/Kconfig
@@ -34,6 +34,15 @@ config HVC_ISERIES
help
iSeries machines support a hypervisor virtual console.
+config HVC_OPAL
+ bool "OPAL Console support"
+ depends on PPC_POWERNV
+ select HVC_DRIVER
+ select HVC_IRQ
+ default y
+ help
+ PowerNV machines running under OPAL need that driver to get a console
+
config HVC_RTAS
bool "IBM RTAS Console support"
depends on PPC_RTAS
diff --git a/drivers/tty/hvc/Makefile b/drivers/tty/hvc/Makefile
index e292053..89abf40b 100644
--- a/drivers/tty/hvc/Makefile
+++ b/drivers/tty/hvc/Makefile
@@ -1,4 +1,5 @@
obj-$(CONFIG_HVC_CONSOLE) += hvc_vio.o hvsi_lib.o
+obj-$(CONFIG_HVC_OPAL) += hvc_opal.o hvsi_lib.o
obj-$(CONFIG_HVC_OLD_HVSI) += hvsi.o
obj-$(CONFIG_HVC_ISERIES) += hvc_iseries.o
obj-$(CONFIG_HVC_RTAS) += hvc_rtas.o
diff --git a/drivers/tty/hvc/hvc_opal.c b/drivers/tty/hvc/hvc_opal.c
new file mode 100644
index 0000000..7b38512
--- /dev/null
+++ b/drivers/tty/hvc/hvc_opal.c
@@ -0,0 +1,424 @@
+/*
+ * opal driver interface to hvc_console.c
+ *
+ * Copyright 2011 Benjamin Herrenschmidt <benh@kernel.crashing.org>, IBM Corp.
+ *
+ * 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
+ *
+ */
+
+#undef DEBUG
+
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/console.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+
+#include <asm/hvconsole.h>
+#include <asm/prom.h>
+#include <asm/firmware.h>
+#include <asm/hvsi.h>
+#include <asm/udbg.h>
+#include <asm/opal.h>
+
+#include "hvc_console.h"
+
+static const char hvc_opal_name[] = "hvc_opal";
+
+static struct of_device_id hvc_opal_match[] __devinitdata = {
+ { .name = "serial", .compatible = "ibm,opal-console-raw" },
+ { .name = "serial", .compatible = "ibm,opal-console-hvsi" },
+ { },
+};
+
+typedef enum hv_protocol {
+ HV_PROTOCOL_RAW,
+ HV_PROTOCOL_HVSI
+} hv_protocol_t;
+
+struct hvc_opal_priv {
+ hv_protocol_t proto; /* Raw data or HVSI packets */
+ struct hvsi_priv hvsi; /* HVSI specific data */
+};
+static struct hvc_opal_priv *hvc_opal_privs[MAX_NR_HVC_CONSOLES];
+
+/* For early boot console */
+static struct hvc_opal_priv hvc_opal_boot_priv;
+static u32 hvc_opal_boot_termno;
+
+static const struct hv_ops hvc_opal_raw_ops = {
+ .get_chars = opal_get_chars,
+ .put_chars = opal_put_chars,
+ .notifier_add = notifier_add_irq,
+ .notifier_del = notifier_del_irq,
+ .notifier_hangup = notifier_hangup_irq,
+};
+
+static int hvc_opal_hvsi_get_chars(uint32_t vtermno, char *buf, int count)
+{
+ struct hvc_opal_priv *pv = hvc_opal_privs[vtermno];
+
+ if (WARN_ON(!pv))
+ return -ENODEV;
+
+ return hvsilib_get_chars(&pv->hvsi, buf, count);
+}
+
+static int hvc_opal_hvsi_put_chars(uint32_t vtermno, const char *buf, int count)
+{
+ struct hvc_opal_priv *pv = hvc_opal_privs[vtermno];
+
+ if (WARN_ON(!pv))
+ return -ENODEV;
+
+ return hvsilib_put_chars(&pv->hvsi, buf, count);
+}
+
+static int hvc_opal_hvsi_open(struct hvc_struct *hp, int data)
+{
+ struct hvc_opal_priv *pv = hvc_opal_privs[hp->vtermno];
+ int rc;
+
+ pr_devel("HVSI@%x: do open !\n", hp->vtermno);
+
+ rc = notifier_add_irq(hp, data);
+ if (rc)
+ return rc;
+
+ return hvsilib_open(&pv->hvsi, hp);
+}
+
+static void hvc_opal_hvsi_close(struct hvc_struct *hp, int data)
+{
+ struct hvc_opal_priv *pv = hvc_opal_privs[hp->vtermno];
+
+ pr_devel("HVSI@%x: do close !\n", hp->vtermno);
+
+ hvsilib_close(&pv->hvsi, hp);
+
+ notifier_del_irq(hp, data);
+}
+
+void hvc_opal_hvsi_hangup(struct hvc_struct *hp, int data)
+{
+ struct hvc_opal_priv *pv = hvc_opal_privs[hp->vtermno];
+
+ pr_devel("HVSI@%x: do hangup !\n", hp->vtermno);
+
+ hvsilib_close(&pv->hvsi, hp);
+
+ notifier_hangup_irq(hp, data);
+}
+
+static int hvc_opal_hvsi_tiocmget(struct hvc_struct *hp)
+{
+ struct hvc_opal_priv *pv = hvc_opal_privs[hp->vtermno];
+
+ if (!pv)
+ return -EINVAL;
+ return pv->hvsi.mctrl;
+}
+
+static int hvc_opal_hvsi_tiocmset(struct hvc_struct *hp, unsigned int set,
+ unsigned int clear)
+{
+ struct hvc_opal_priv *pv = hvc_opal_privs[hp->vtermno];
+
+ pr_devel("HVSI@%x: Set modem control, set=%x,clr=%x\n",
+ hp->vtermno, set, clear);
+
+ if (set & TIOCM_DTR)
+ hvsilib_write_mctrl(&pv->hvsi, 1);
+ else if (clear & TIOCM_DTR)
+ hvsilib_write_mctrl(&pv->hvsi, 0);
+
+ return 0;
+}
+
+static const struct hv_ops hvc_opal_hvsi_ops = {
+ .get_chars = hvc_opal_hvsi_get_chars,
+ .put_chars = hvc_opal_hvsi_put_chars,
+ .notifier_add = hvc_opal_hvsi_open,
+ .notifier_del = hvc_opal_hvsi_close,
+ .notifier_hangup = hvc_opal_hvsi_hangup,
+ .tiocmget = hvc_opal_hvsi_tiocmget,
+ .tiocmset = hvc_opal_hvsi_tiocmset,
+};
+
+static int __devinit hvc_opal_probe(struct platform_device *dev)
+{
+ const struct hv_ops *ops;
+ struct hvc_struct *hp;
+ struct hvc_opal_priv *pv;
+ hv_protocol_t proto;
+ unsigned int termno, boot = 0;
+ const __be32 *reg;
+
+ if (of_device_is_compatible(dev->dev.of_node, "ibm,opal-console-raw")) {
+ proto = HV_PROTOCOL_RAW;
+ ops = &hvc_opal_raw_ops;
+ } else if (of_device_is_compatible(dev->dev.of_node,
+ "ibm,opal-console-hvsi")) {
+ proto = HV_PROTOCOL_HVSI;
+ ops = &hvc_opal_hvsi_ops;
+ } else {
+ pr_err("hvc_opal: Unkown protocol for %s\n",
+ dev->dev.of_node->full_name);
+ return -ENXIO;
+ }
+
+ reg = of_get_property(dev->dev.of_node, "reg", NULL);
+ termno = reg ? be32_to_cpup(reg) : 0;
+
+ /* Is it our boot one ? */
+ if (hvc_opal_privs[termno] == &hvc_opal_boot_priv) {
+ pv = hvc_opal_privs[termno];
+ boot = 1;
+ } else if (hvc_opal_privs[termno] == NULL) {
+ pv = kzalloc(sizeof(struct hvc_opal_priv), GFP_KERNEL);
+ if (!pv)
+ return -ENOMEM;
+ pv->proto = proto;
+ hvc_opal_privs[termno] = pv;
+ if (proto == HV_PROTOCOL_HVSI)
+ hvsilib_init(&pv->hvsi, opal_get_chars, opal_put_chars,
+ termno, 0);
+
+ /* Instanciate now to establish a mapping index==vtermno */
+ hvc_instantiate(termno, termno, ops);
+ } else {
+ pr_err("hvc_opal: Device %s has duplicate terminal number #%d\n",
+ dev->dev.of_node->full_name, termno);
+ return -ENXIO;
+ }
+
+ pr_info("hvc%d: %s protocol on %s%s\n", termno,
+ proto == HV_PROTOCOL_RAW ? "raw" : "hvsi",
+ dev->dev.of_node->full_name,
+ boot ? " (boot console)" : "");
+
+ /* We don't do IRQ yet */
+ hp = hvc_alloc(termno, 0, ops, MAX_VIO_PUT_CHARS);
+ if (IS_ERR(hp))
+ return PTR_ERR(hp);
+ dev_set_drvdata(&dev->dev, hp);
+
+ return 0;
+}
+
+static int __devexit hvc_opal_remove(struct platform_device *dev)
+{
+ struct hvc_struct *hp = dev_get_drvdata(&dev->dev);
+ int rc, termno;
+
+ termno = hp->vtermno;
+ rc = hvc_remove(hp);
+ if (rc == 0) {
+ if (hvc_opal_privs[termno] != &hvc_opal_boot_priv)
+ kfree(hvc_opal_privs[termno]);
+ hvc_opal_privs[termno] = NULL;
+ }
+ return rc;
+}
+
+static struct platform_driver hvc_opal_driver = {
+ .probe = hvc_opal_probe,
+ .remove = __devexit_p(hvc_opal_remove),
+ .driver = {
+ .name = hvc_opal_name,
+ .owner = THIS_MODULE,
+ .of_match_table = hvc_opal_match,
+ }
+};
+
+static int __init hvc_opal_init(void)
+{
+ if (!firmware_has_feature(FW_FEATURE_OPAL))
+ return -ENODEV;
+
+ /* Register as a vio device to receive callbacks */
+ return platform_driver_register(&hvc_opal_driver);
+}
+module_init(hvc_opal_init);
+
+static void __exit hvc_opal_exit(void)
+{
+ platform_driver_unregister(&hvc_opal_driver);
+}
+module_exit(hvc_opal_exit);
+
+static void udbg_opal_putc(char c)
+{
+ unsigned int termno = hvc_opal_boot_termno;
+ int count = -1;
+
+ if (c == '\n')
+ udbg_opal_putc('\r');
+
+ do {
+ switch(hvc_opal_boot_priv.proto) {
+ case HV_PROTOCOL_RAW:
+ count = opal_put_chars(termno, &c, 1);
+ break;
+ case HV_PROTOCOL_HVSI:
+ count = hvc_opal_hvsi_put_chars(termno, &c, 1);
+ break;
+ }
+ } while(count == 0 || count == -EAGAIN);
+}
+
+static int udbg_opal_getc_poll(void)
+{
+ unsigned int termno = hvc_opal_boot_termno;
+ int rc = 0;
+ char c;
+
+ switch(hvc_opal_boot_priv.proto) {
+ case HV_PROTOCOL_RAW:
+ rc = opal_get_chars(termno, &c, 1);
+ break;
+ case HV_PROTOCOL_HVSI:
+ rc = hvc_opal_hvsi_get_chars(termno, &c, 1);
+ break;
+ }
+ if (!rc)
+ return -1;
+ return c;
+}
+
+static int udbg_opal_getc(void)
+{
+ int ch;
+ for (;;) {
+ ch = udbg_opal_getc_poll();
+ if (ch == -1) {
+ /* This shouldn't be needed...but... */
+ volatile unsigned long delay;
+ for (delay=0; delay < 2000000; delay++)
+ ;
+ } else {
+ return ch;
+ }
+ }
+}
+
+static void udbg_init_opal_common(void)
+{
+ udbg_putc = udbg_opal_putc;
+ udbg_getc = udbg_opal_getc;
+ udbg_getc_poll = udbg_opal_getc_poll;
+ tb_ticks_per_usec = 0x200; /* Make udelay not suck */
+}
+
+void __init hvc_opal_init_early(void)
+{
+ struct device_node *stdout_node = NULL;
+ const u32 *termno;
+ const char *name = NULL;
+ const struct hv_ops *ops;
+ u32 index;
+
+ /* find the boot console from /chosen/stdout */
+ if (of_chosen)
+ name = of_get_property(of_chosen, "linux,stdout-path", NULL);
+ if (name) {
+ stdout_node = of_find_node_by_path(name);
+ if (!stdout_node) {
+ pr_err("hvc_opal: Failed to locate default console!\n");
+ return;
+ }
+ } else {
+ struct device_node *opal, *np;
+
+ /* Current OPAL takeover doesn't provide the stdout
+ * path, so we hard wire it
+ */
+ opal = of_find_node_by_path("/ibm,opal/consoles");
+ if (opal)
+ pr_devel("hvc_opal: Found consoles in new location\n");
+ if (!opal) {
+ opal = of_find_node_by_path("/ibm,opal");
+ if (opal)
+ pr_devel("hvc_opal: "
+ "Found consoles in old location\n");
+ }
+ if (!opal)
+ return;
+ for_each_child_of_node(opal, np) {
+ if (!strcmp(np->name, "serial")) {
+ stdout_node = np;
+ break;
+ }
+ }
+ of_node_put(opal);
+ }
+ if (!stdout_node)
+ return;
+ termno = of_get_property(stdout_node, "reg", NULL);
+ index = termno ? *termno : 0;
+ if (index >= MAX_NR_HVC_CONSOLES)
+ return;
+ hvc_opal_privs[index] = &hvc_opal_boot_priv;
+
+ /* Check the protocol */
+ if (of_device_is_compatible(stdout_node, "ibm,opal-console-raw")) {
+ hvc_opal_boot_priv.proto = HV_PROTOCOL_RAW;
+ ops = &hvc_opal_raw_ops;
+ pr_devel("hvc_opal: Found RAW console\n");
+ }
+ else if (of_device_is_compatible(stdout_node,"ibm,opal-console-hvsi")) {
+ hvc_opal_boot_priv.proto = HV_PROTOCOL_HVSI;
+ ops = &hvc_opal_hvsi_ops;
+ hvsilib_init(&hvc_opal_boot_priv.hvsi, opal_get_chars,
+ opal_put_chars, index, 1);
+ /* HVSI, perform the handshake now */
+ hvsilib_establish(&hvc_opal_boot_priv.hvsi);
+ pr_devel("hvc_opal: Found HVSI console\n");
+ } else
+ goto out;
+ hvc_opal_boot_termno = index;
+ udbg_init_opal_common();
+ add_preferred_console("hvc", index, NULL);
+ hvc_instantiate(index, index, ops);
+out:
+ of_node_put(stdout_node);
+}
+
+#ifdef CONFIG_PPC_EARLY_DEBUG_OPAL_RAW
+void __init udbg_init_debug_opal(void)
+{
+ u32 index = CONFIG_PPC_EARLY_DEBUG_OPAL_VTERMNO;
+ hvc_opal_privs[index] = &hvc_opal_boot_priv;
+ hvc_opal_boot_priv.proto = HV_PROTOCOL_RAW;
+ hvc_opal_boot_termno = index;
+ udbg_init_opal_common();
+}
+#endif /* CONFIG_PPC_EARLY_DEBUG_OPAL_RAW */
+
+#ifdef CONFIG_PPC_EARLY_DEBUG_OPAL_HVSI
+void __init udbg_init_debug_opal_hvsi(void)
+{
+ u32 index = CONFIG_PPC_EARLY_DEBUG_OPAL_VTERMNO;
+ hvc_opal_privs[index] = &hvc_opal_boot_priv;
+ hvc_opal_boot_termno = index;
+ udbg_init_opal_common();
+ hvsilib_init(&hvc_opal_boot_priv.hvsi, opal_get_chars, opal_put_chars,
+ index, 1);
+ hvsilib_establish(&hvc_opal_boot_priv.hvsi);
+}
+#endif /* CONFIG_PPC_EARLY_DEBUG_OPAL_HVSI */
diff --git a/drivers/tty/hvc/hvsi_lib.c b/drivers/tty/hvc/hvsi_lib.c
index 08e3e90..215df5b 100644
--- a/drivers/tty/hvc/hvsi_lib.c
+++ b/drivers/tty/hvc/hvsi_lib.c
@@ -183,7 +183,7 @@ int hvsilib_get_chars(struct hvsi_priv *pv, char *buf, int count)
unsigned int tries, read = 0;
if (WARN_ON(!pv))
- return 0;
+ return -ENXIO;
/* If we aren't open, don't do anything in order to avoid races
* with connection establishment. The hvc core will call this
@@ -234,7 +234,7 @@ int hvsilib_put_chars(struct hvsi_priv *pv, const char *buf, int count)
int rc, adjcount = min(count, HVSI_MAX_OUTGOING_DATA);
if (WARN_ON(!pv))
- return 0;
+ return -ENODEV;
dp.hdr.type = VS_DATA_PACKET_HEADER;
dp.hdr.len = adjcount + sizeof(struct hvsi_header);
--
1.7.4.1
^ permalink raw reply related
* [PATCH 11/21] powerpc/powernv: Basic support for OPAL
From: Benjamin Herrenschmidt @ 2011-09-10 14:19 UTC (permalink / raw)
To: linuxppc-dev
In-Reply-To: <1315664408-16797-1-git-send-email-benh@kernel.crashing.org>
Add definition of OPAL interfaces along with the wrappers to call
into OPAL runtime and the early device-tree parsing hook to locate
the OPAL runtime firmware.
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---
arch/powerpc/include/asm/firmware.h | 10 +
arch/powerpc/include/asm/opal.h | 382 +++++++++++++++++++++++-
arch/powerpc/kernel/prom.c | 7 +
arch/powerpc/platforms/powernv/Kconfig | 8 +-
arch/powerpc/platforms/powernv/Makefile | 2 +-
arch/powerpc/platforms/powernv/opal-wrappers.S | 101 +++++++
arch/powerpc/platforms/powernv/opal.c | 154 ++++++++++
arch/powerpc/platforms/powernv/setup.c | 6 +
arch/powerpc/platforms/powernv/smp.c | 25 ++-
9 files changed, 690 insertions(+), 5 deletions(-)
create mode 100644 arch/powerpc/platforms/powernv/opal-wrappers.S
create mode 100644 arch/powerpc/platforms/powernv/opal.c
diff --git a/arch/powerpc/include/asm/firmware.h b/arch/powerpc/include/asm/firmware.h
index 3a6c586..14db29b 100644
--- a/arch/powerpc/include/asm/firmware.h
+++ b/arch/powerpc/include/asm/firmware.h
@@ -48,6 +48,8 @@
#define FW_FEATURE_CMO ASM_CONST(0x0000000002000000)
#define FW_FEATURE_VPHN ASM_CONST(0x0000000004000000)
#define FW_FEATURE_XCMO ASM_CONST(0x0000000008000000)
+#define FW_FEATURE_OPAL ASM_CONST(0x0000000010000000)
+#define FW_FEATURE_OPALv2 ASM_CONST(0x0000000020000000)
#ifndef __ASSEMBLY__
@@ -65,6 +67,8 @@ enum {
FW_FEATURE_PSERIES_ALWAYS = 0,
FW_FEATURE_ISERIES_POSSIBLE = FW_FEATURE_ISERIES | FW_FEATURE_LPAR,
FW_FEATURE_ISERIES_ALWAYS = FW_FEATURE_ISERIES | FW_FEATURE_LPAR,
+ FW_FEATURE_POWERNV_POSSIBLE = FW_FEATURE_OPAL | FW_FEATURE_OPALv2,
+ FW_FEATURE_POWERNV_ALWAYS = 0,
FW_FEATURE_PS3_POSSIBLE = FW_FEATURE_LPAR | FW_FEATURE_PS3_LV1,
FW_FEATURE_PS3_ALWAYS = FW_FEATURE_LPAR | FW_FEATURE_PS3_LV1,
FW_FEATURE_CELLEB_POSSIBLE = FW_FEATURE_LPAR | FW_FEATURE_BEAT,
@@ -78,6 +82,9 @@ enum {
#ifdef CONFIG_PPC_ISERIES
FW_FEATURE_ISERIES_POSSIBLE |
#endif
+#ifdef CONFIG_PPC_POWERNV
+ FW_FEATURE_POWERNV_POSSIBLE |
+#endif
#ifdef CONFIG_PPC_PS3
FW_FEATURE_PS3_POSSIBLE |
#endif
@@ -95,6 +102,9 @@ enum {
#ifdef CONFIG_PPC_ISERIES
FW_FEATURE_ISERIES_ALWAYS &
#endif
+#ifdef CONFIG_PPC_POWERNV
+ FW_FEATURE_POWERNV_ALWAYS &
+#endif
#ifdef CONFIG_PPC_PS3
FW_FEATURE_PS3_ALWAYS &
#endif
diff --git a/arch/powerpc/include/asm/opal.h b/arch/powerpc/include/asm/opal.h
index ecdb283..53cda41 100644
--- a/arch/powerpc/include/asm/opal.h
+++ b/arch/powerpc/include/asm/opal.h
@@ -37,14 +37,394 @@ extern long opal_query_takeover(u64 *hal_size, u64 *hal_align);
extern long opal_do_takeover(struct opal_takeover_args *args);
+struct rtas_args;
extern int opal_enter_rtas(struct rtas_args *args,
unsigned long data,
unsigned long entry);
-
#endif /* __ASSEMBLY__ */
/****** OPAL APIs ******/
+/* Return codes */
+#define OPAL_SUCCESS 0
+#define OPAL_PARAMETER -1
+#define OPAL_BUSY -2
+#define OPAL_PARTIAL -3
+#define OPAL_CONSTRAINED -4
+#define OPAL_CLOSED -5
+#define OPAL_HARDWARE -6
+#define OPAL_UNSUPPORTED -7
+#define OPAL_PERMISSION -8
+#define OPAL_NO_MEM -9
+#define OPAL_RESOURCE -10
+#define OPAL_INTERNAL_ERROR -11
+#define OPAL_BUSY_EVENT -12
+#define OPAL_HARDWARE_FROZEN -13
+
+/* API Tokens (in r0) */
+#define OPAL_CONSOLE_WRITE 1
+#define OPAL_CONSOLE_READ 2
+#define OPAL_RTC_READ 3
+#define OPAL_RTC_WRITE 4
+#define OPAL_CEC_POWER_DOWN 5
+#define OPAL_CEC_REBOOT 6
+#define OPAL_READ_NVRAM 7
+#define OPAL_WRITE_NVRAM 8
+#define OPAL_HANDLE_INTERRUPT 9
+#define OPAL_POLL_EVENTS 10
+#define OPAL_PCI_SET_HUB_TCE_MEMORY 11
+#define OPAL_PCI_SET_PHB_TCE_MEMORY 12
+#define OPAL_PCI_CONFIG_READ_BYTE 13
+#define OPAL_PCI_CONFIG_READ_HALF_WORD 14
+#define OPAL_PCI_CONFIG_READ_WORD 15
+#define OPAL_PCI_CONFIG_WRITE_BYTE 16
+#define OPAL_PCI_CONFIG_WRITE_HALF_WORD 17
+#define OPAL_PCI_CONFIG_WRITE_WORD 18
+#define OPAL_SET_XIVE 19
+#define OPAL_GET_XIVE 20
+#define OPAL_GET_COMPLETION_TOKEN_STATUS 21 /* obsolete */
+#define OPAL_REGISTER_OPAL_EXCEPTION_HANDLER 22
+#define OPAL_PCI_EEH_FREEZE_STATUS 23
+#define OPAL_PCI_SHPC 24
+#define OPAL_CONSOLE_WRITE_BUFFER_SPACE 25
+#define OPAL_PCI_EEH_FREEZE_CLEAR 26
+#define OPAL_PCI_PHB_MMIO_ENABLE 27
+#define OPAL_PCI_SET_PHB_MEM_WINDOW 28
+#define OPAL_PCI_MAP_PE_MMIO_WINDOW 29
+#define OPAL_PCI_SET_PHB_TABLE_MEMORY 30
+#define OPAL_PCI_SET_PE 31
+#define OPAL_PCI_SET_PELTV 32
+#define OPAL_PCI_SET_MVE 33
+#define OPAL_PCI_SET_MVE_ENABLE 34
+#define OPAL_PCI_GET_XIVE_REISSUE 35
+#define OPAL_PCI_SET_XIVE_REISSUE 36
+#define OPAL_PCI_SET_XIVE_PE 37
+#define OPAL_GET_XIVE_SOURCE 38
+#define OPAL_GET_MSI_32 39
+#define OPAL_GET_MSI_64 40
+#define OPAL_START_CPU 41
+#define OPAL_QUERY_CPU_STATUS 42
+#define OPAL_WRITE_OPPANEL 43
+#define OPAL_PCI_MAP_PE_DMA_WINDOW 44
+#define OPAL_PCI_MAP_PE_DMA_WINDOW_REAL 45
+#define OPAL_PCI_RESET 49
+
+#ifndef __ASSEMBLY__
+
+/* Other enums */
+enum OpalVendorApiTokens {
+ OPAL_START_VENDOR_API_RANGE = 1000, OPAL_END_VENDOR_API_RANGE = 1999
+};
+enum OpalFreezeState {
+ OPAL_EEH_STOPPED_NOT_FROZEN = 0,
+ OPAL_EEH_STOPPED_MMIO_FREEZE = 1,
+ OPAL_EEH_STOPPED_DMA_FREEZE = 2,
+ OPAL_EEH_STOPPED_MMIO_DMA_FREEZE = 3,
+ OPAL_EEH_STOPPED_RESET = 4,
+ OPAL_EEH_STOPPED_TEMP_UNAVAIL = 5,
+ OPAL_EEH_STOPPED_PERM_UNAVAIL = 6
+};
+enum OpalEehFreezeActionToken {
+ OPAL_EEH_ACTION_CLEAR_FREEZE_MMIO = 1,
+ OPAL_EEH_ACTION_CLEAR_FREEZE_DMA = 2,
+ OPAL_EEH_ACTION_CLEAR_FREEZE_ALL = 3
+};
+enum OpalPciStatusToken {
+ OPAL_EEH_PHB_NO_ERROR = 0,
+ OPAL_EEH_PHB_FATAL = 1,
+ OPAL_EEH_PHB_RECOVERABLE = 2,
+ OPAL_EEH_PHB_BUS_ERROR = 3,
+ OPAL_EEH_PCI_NO_DEVSEL = 4,
+ OPAL_EEH_PCI_TA = 5,
+ OPAL_EEH_PCIEX_UR = 6,
+ OPAL_EEH_PCIEX_CA = 7,
+ OPAL_EEH_PCI_MMIO_ERROR = 8,
+ OPAL_EEH_PCI_DMA_ERROR = 9
+};
+enum OpalShpcAction {
+ OPAL_SHPC_GET_LINK_STATE = 0,
+ OPAL_SHPC_GET_SLOT_STATE = 1
+};
+enum OpalShpcLinkState {
+ OPAL_SHPC_LINK_DOWN = 0,
+ OPAL_SHPC_LINK_UP = 1
+};
+enum OpalMmioWindowType {
+ OPAL_M32_WINDOW_TYPE = 1,
+ OPAL_M64_WINDOW_TYPE = 2,
+ OPAL_IO_WINDOW_TYPE = 3
+};
+enum OpalShpcSlotState {
+ OPAL_SHPC_DEV_NOT_PRESENT = 0,
+ OPAL_SHPC_DEV_PRESENT = 1
+};
+enum OpalExceptionHandler {
+ OPAL_MACHINE_CHECK_HANDLER = 1,
+ OPAL_HYPERVISOR_MAINTENANCE_HANDLER = 2,
+ OPAL_SOFTPATCH_HANDLER = 3
+};
+enum OpalPendingState {
+ OPAL_EVENT_OPAL_INTERNAL = 0x1,
+ OPAL_EVENT_NVRAM = 0x2,
+ OPAL_EVENT_RTC = 0x4,
+ OPAL_EVENT_CONSOLE_OUTPUT = 0x8,
+ OPAL_EVENT_CONSOLE_INPUT = 0x10
+};
+
+/* Machine check related definitions */
+enum OpalMCE_Version {
+ OpalMCE_V1 = 1,
+};
+
+enum OpalMCE_Severity {
+ OpalMCE_SEV_NO_ERROR = 0,
+ OpalMCE_SEV_WARNING = 1,
+ OpalMCE_SEV_ERROR_SYNC = 2,
+ OpalMCE_SEV_FATAL = 3,
+};
+
+enum OpalMCE_Disposition {
+ OpalMCE_DISPOSITION_RECOVERED = 0,
+ OpalMCE_DISPOSITION_NOT_RECOVERED = 1,
+};
+
+enum OpalMCE_Initiator {
+ OpalMCE_INITIATOR_UNKNOWN = 0,
+ OpalMCE_INITIATOR_CPU = 1,
+};
+
+enum OpalMCE_ErrorType {
+ OpalMCE_ERROR_TYPE_UNKNOWN = 0,
+ OpalMCE_ERROR_TYPE_UE = 1,
+ OpalMCE_ERROR_TYPE_SLB = 2,
+ OpalMCE_ERROR_TYPE_ERAT = 3,
+ OpalMCE_ERROR_TYPE_TLB = 4,
+};
+
+enum OpalMCE_UeErrorType {
+ OpalMCE_UE_ERROR_INDETERMINATE = 0,
+ OpalMCE_UE_ERROR_IFETCH = 1,
+ OpalMCE_UE_ERROR_PAGE_TABLE_WALK_IFETCH = 2,
+ OpalMCE_UE_ERROR_LOAD_STORE = 3,
+ OpalMCE_UE_ERROR_PAGE_TABLE_WALK_LOAD_STORE = 4,
+};
+
+enum OpalMCE_SlbErrorType {
+ OpalMCE_SLB_ERROR_INDETERMINATE = 0,
+ OpalMCE_SLB_ERROR_PARITY = 1,
+ OpalMCE_SLB_ERROR_MULTIHIT = 2,
+};
+
+enum OpalMCE_EratErrorType {
+ OpalMCE_ERAT_ERROR_INDETERMINATE = 0,
+ OpalMCE_ERAT_ERROR_PARITY = 1,
+ OpalMCE_ERAT_ERROR_MULTIHIT = 2,
+};
+
+enum OpalMCE_TlbErrorType {
+ OpalMCE_TLB_ERROR_INDETERMINATE = 0,
+ OpalMCE_TLB_ERROR_PARITY = 1,
+ OpalMCE_TLB_ERROR_MULTIHIT = 2,
+};
+
+enum OpalThreadStatus {
+ OPAL_THREAD_INACTIVE = 0x0,
+ OPAL_THREAD_STARTED = 0x1
+};
+
+enum OpalPciBusCompare {
+ OpalPciBusAny = 0, /* Any bus number match */
+ OpalPciBus3Bits = 2, /* Match top 3 bits of bus number */
+ OpalPciBus4Bits = 3, /* Match top 4 bits of bus number */
+ OpalPciBus5Bits = 4, /* Match top 5 bits of bus number */
+ OpalPciBus6Bits = 5, /* Match top 6 bits of bus number */
+ OpalPciBus7Bits = 6, /* Match top 7 bits of bus number */
+ OpalPciBusAll = 7, /* Match bus number exactly */
+};
+
+enum OpalDeviceCompare {
+ OPAL_IGNORE_RID_DEVICE_NUMBER = 0,
+ OPAL_COMPARE_RID_DEVICE_NUMBER = 1
+};
+
+enum OpalFuncCompare {
+ OPAL_IGNORE_RID_FUNCTION_NUMBER = 0,
+ OPAL_COMPARE_RID_FUNCTION_NUMBER = 1
+};
+
+enum OpalPeAction {
+ OPAL_UNMAP_PE = 0,
+ OPAL_MAP_PE = 1
+};
+
+enum OpalPciResetAndReinitScope {
+ OPAL_PHB_COMPLETE = 1, OPAL_PCI_LINK = 2, OPAL_PHB_ERROR = 3,
+ OPAL_PCI_HOT_RESET = 4, OPAL_PCI_FUNDAMENTAL_RESET = 5,
+ OPAL_PCI_IODA_RESET = 6,
+};
+
+enum OpalPciResetState { OPAL_DEASSERT_RESET = 0, OPAL_ASSERT_RESET = 1 };
+
+struct opal_machine_check_event {
+ enum OpalMCE_Version version:8; /* 0x00 */
+ uint8_t in_use; /* 0x01 */
+ enum OpalMCE_Severity severity:8; /* 0x02 */
+ enum OpalMCE_Initiator initiator:8; /* 0x03 */
+ enum OpalMCE_ErrorType error_type:8; /* 0x04 */
+ enum OpalMCE_Disposition disposition:8; /* 0x05 */
+ uint8_t reserved_1[2]; /* 0x06 */
+ uint64_t gpr3; /* 0x08 */
+ uint64_t srr0; /* 0x10 */
+ uint64_t srr1; /* 0x18 */
+ union { /* 0x20 */
+ struct {
+ enum OpalMCE_UeErrorType ue_error_type:8;
+ uint8_t effective_address_provided;
+ uint8_t physical_address_provided;
+ uint8_t reserved_1[5];
+ uint64_t effective_address;
+ uint64_t physical_address;
+ uint8_t reserved_2[8];
+ } ue_error;
+
+ struct {
+ enum OpalMCE_SlbErrorType slb_error_type:8;
+ uint8_t effective_address_provided;
+ uint8_t reserved_1[6];
+ uint64_t effective_address;
+ uint8_t reserved_2[16];
+ } slb_error;
+
+ struct {
+ enum OpalMCE_EratErrorType erat_error_type:8;
+ uint8_t effective_address_provided;
+ uint8_t reserved_1[6];
+ uint64_t effective_address;
+ uint8_t reserved_2[16];
+ } erat_error;
+
+ struct {
+ enum OpalMCE_TlbErrorType tlb_error_type:8;
+ uint8_t effective_address_provided;
+ uint8_t reserved_1[6];
+ uint64_t effective_address;
+ uint8_t reserved_2[16];
+ } tlb_error;
+ } u;
+};
+
+typedef struct oppanel_line {
+ /* XXX */
+} oppanel_line_t;
+
+/* API functions */
+int64_t opal_console_write(int64_t term_number, int64_t *length,
+ const uint8_t *buffer);
+int64_t opal_console_read(int64_t term_number, int64_t *length,
+ uint8_t *buffer);
+int64_t opal_console_write_buffer_space(int64_t term_number,
+ int64_t *length);
+int64_t opal_rtc_read(uint32_t *year_month_day,
+ uint64_t *hour_minute_second_millisecond);
+int64_t opal_rtc_write(uint32_t year_month_day,
+ uint64_t hour_minute_second_millisecond);
+int64_t opal_cec_power_down(uint64_t request);
+int64_t opal_cec_reboot(void);
+int64_t opal_read_nvram(uint64_t buffer, uint64_t size, uint64_t offset);
+int64_t opal_write_nvram(uint64_t buffer, uint64_t size, uint64_t offset);
+int64_t opal_handle_interrupt(uint64_t isn, uint64_t *outstanding_event_mask);
+int64_t opal_poll_events(uint64_t *outstanding_event_mask);
+int64_t opal_pci_set_hub_tce_memory(uint64_t hub_id, uint64_t tce_mem_addr,
+ uint64_t tce_mem_size);
+int64_t opal_pci_set_phb_tce_memory(uint64_t phb_id, uint64_t tce_mem_addr,
+ uint64_t tce_mem_size);
+int64_t opal_pci_config_read_byte(uint64_t phb_id, uint64_t bus_dev_func,
+ uint64_t offset, uint8_t *data);
+int64_t opal_pci_config_read_half_word(uint64_t phb_id, uint64_t bus_dev_func,
+ uint64_t offset, uint16_t *data);
+int64_t opal_pci_config_read_word(uint64_t phb_id, uint64_t bus_dev_func,
+ uint64_t offset, uint32_t *data);
+int64_t opal_pci_config_write_byte(uint64_t phb_id, uint64_t bus_dev_func,
+ uint64_t offset, uint8_t data);
+int64_t opal_pci_config_write_half_word(uint64_t phb_id, uint64_t bus_dev_func,
+ uint64_t offset, uint16_t data);
+int64_t opal_pci_config_write_word(uint64_t phb_id, uint64_t bus_dev_func,
+ uint64_t offset, uint32_t data);
+int64_t opal_set_xive(uint32_t isn, uint16_t server, uint8_t priority);
+int64_t opal_get_xive(uint32_t isn, uint16_t *server, uint8_t *priority);
+int64_t opal_register_exception_handler(uint64_t opal_exception,
+ uint64_t handler_address,
+ uint64_t glue_cache_line);
+int64_t opal_pci_eeh_freeze_status(uint64_t phb_id, uint64_t pe_number,
+ uint8_t *freeze_state,
+ uint16_t *pci_error_type,
+ uint64_t *phb_status);
+int64_t opal_pci_eeh_freeze_clear(uint64_t phb_id, uint64_t pe_number,
+ uint64_t eeh_action_token);
+int64_t opal_pci_shpc(uint64_t phb_id, uint64_t shpc_action, uint8_t *state);
+
+
+
+int64_t opal_pci_phb_mmio_enable(uint64_t phb_id, uint16_t window_type,
+ uint16_t window_num, uint16_t enable);
+int64_t opal_pci_set_phb_mem_window(uint64_t phb_id, uint16_t window_type,
+ uint16_t window_num,
+ uint64_t starting_real_address,
+ uint64_t starting_pci_address,
+ uint16_t segment_size);
+int64_t opal_pci_map_pe_mmio_window(uint64_t phb_id, uint16_t pe_number,
+ uint16_t window_type, uint16_t window_num,
+ uint16_t segment_num);
+int64_t opal_pci_set_phb_table_memory(uint64_t phb_id, uint64_t rtt_addr,
+ uint64_t ivt_addr, uint64_t ivt_len,
+ uint64_t reject_array_addr,
+ uint64_t peltv_addr);
+int64_t opal_pci_set_pe(uint64_t phb_id, uint64_t pe_number, uint64_t bus_dev_func,
+ uint8_t bus_compare, uint8_t dev_compare, uint8_t func_compare,
+ uint8_t pe_action);
+int64_t opal_pci_set_peltv(uint64_t phb_id, uint32_t parent_pe, uint32_t child_pe,
+ uint8_t state);
+int64_t opal_pci_set_mve(uint64_t phb_id, uint32_t mve_number, uint32_t pe_number);
+int64_t opal_pci_set_mve_enable(uint64_t phb_id, uint32_t mve_number,
+ uint32_t state);
+int64_t opal_pci_get_xive_reissue(uint64_t phb_id, uint32_t xive_number,
+ uint8_t *p_bit, uint8_t *q_bit);
+int64_t opal_pci_set_xive_reissue(uint64_t phb_id, uint32_t xive_number,
+ uint8_t p_bit, uint8_t q_bit);
+int64_t opal_pci_set_xive_pe(uint64_t phb_id, uint32_t pe_number,
+ uint32_t xive_num);
+int64_t opal_get_xive_source(uint64_t phb_id, uint32_t xive_num,
+ int32_t *interrupt_source_number);
+int64_t opal_get_msi_32(uint64_t phb_id, uint32_t mve_number, uint32_t xive_num,
+ uint8_t msi_range, uint32_t *msi_address,
+ uint32_t *message_data);
+int64_t opal_get_msi_64(uint64_t phb_id, uint32_t mve_number,
+ uint32_t xive_num, uint8_t msi_range,
+ uint64_t *msi_address, uint32_t *message_data);
+int64_t opal_start_cpu(uint64_t thread_number, uint64_t start_address);
+int64_t opal_query_cpu_status(uint64_t thread_number, uint8_t *thread_status);
+int64_t opal_write_oppanel(oppanel_line_t *lines, uint64_t num_lines);
+int64_t opal_pci_map_pe_dma_window(uint64_t phb_id, uint16_t pe_number, uint16_t window_id,
+ uint16_t tce_levels, uint64_t tce_table_addr,
+ uint64_t tce_table_size, uint64_t tce_page_size);
+int64_t opal_pci_map_pe_dma_window_real(uint64_t phb_id, uint16_t pe_number,
+ uint16_t dma_window_number, uint64_t pci_start_addr,
+ uint64_t pci_mem_size);
+int64_t opal_pci_reset(uint64_t phb_id, uint8_t reset_scope, uint8_t assert_state);
+
+/* Internal functions */
+extern int early_init_dt_scan_opal(unsigned long node, const char *uname, int depth, void *data);
+
+extern int opal_get_chars(uint32_t vtermno, char *buf, int count);
+extern int opal_put_chars(uint32_t vtermno, const char *buf, int total_len);
+
+extern void hvc_opal_init_early(void);
+
+/* Internal functions */
+extern int early_init_dt_scan_opal(unsigned long node, const char *uname,
+ int depth, void *data);
+
+#endif /* __ASSEMBLY__ */
#endif /* __OPAL_H */
diff --git a/arch/powerpc/kernel/prom.c b/arch/powerpc/kernel/prom.c
index 7b90c56..831a201 100644
--- a/arch/powerpc/kernel/prom.c
+++ b/arch/powerpc/kernel/prom.c
@@ -54,6 +54,8 @@
#include <asm/pci-bridge.h>
#include <asm/phyp_dump.h>
#include <asm/kexec.h>
+#include <asm/opal.h>
+
#include <mm/mmu_decl.h>
#ifdef DEBUG
@@ -707,6 +709,11 @@ void __init early_init_devtree(void *params)
of_scan_flat_dt(early_init_dt_scan_rtas, NULL);
#endif
+#ifdef CONFIG_PPC_POWERNV
+ /* Some machines might need OPAL info for debugging, grab it now. */
+ of_scan_flat_dt(early_init_dt_scan_opal, NULL);
+#endif
+
#ifdef CONFIG_PHYP_DUMP
/* scan tree to see if dump occurred during last boot */
of_scan_flat_dt(early_init_dt_scan_phyp_dump, NULL);
diff --git a/arch/powerpc/platforms/powernv/Kconfig b/arch/powerpc/platforms/powernv/Kconfig
index 5cd04f5..268cadb 100644
--- a/arch/powerpc/platforms/powernv/Kconfig
+++ b/arch/powerpc/platforms/powernv/Kconfig
@@ -1,12 +1,16 @@
config PPC_POWERNV
depends on PPC64 && PPC_BOOK3S
bool "IBM PowerNV (Non-Virtualized) platform support"
- select PPC_RTAS
select PPC_NATIVE
select PPC_XICS
select PPC_ICP_NATIVE
- select PPC_ICS_RTAS
select PPC_P7_NAP
select PPC_PCI_CHOICE if EMBEDDED
default y
+config PPC_POWERNV_RTAS
+ depends on PPC_POWERNV
+ bool "Support for RTAS based PowerNV platforms such as BML"
+ default y
+ select PPC_ICS_RTAS
+ select PPC_RTAS
diff --git a/arch/powerpc/platforms/powernv/Makefile b/arch/powerpc/platforms/powernv/Makefile
index 4971330..8f69c0d 100644
--- a/arch/powerpc/platforms/powernv/Makefile
+++ b/arch/powerpc/platforms/powernv/Makefile
@@ -1,2 +1,2 @@
-obj-y += setup.o opal-takeover.o
+obj-y += setup.o opal-takeover.o opal-wrappers.o opal.o
obj-$(CONFIG_SMP) += smp.o
diff --git a/arch/powerpc/platforms/powernv/opal-wrappers.S b/arch/powerpc/platforms/powernv/opal-wrappers.S
new file mode 100644
index 0000000..4a3f46d
--- /dev/null
+++ b/arch/powerpc/platforms/powernv/opal-wrappers.S
@@ -0,0 +1,101 @@
+/*
+ * PowerNV OPAL API wrappers
+ *
+ * Copyright 2011 IBM Corp.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <asm/ppc_asm.h>
+#include <asm/hvcall.h>
+#include <asm/asm-offsets.h>
+#include <asm/opal.h>
+
+/* TODO:
+ *
+ * - Trace irqs in/off (needs saving/restoring all args, argh...)
+ * - Get r11 feed up by Dave so I can have better register usage
+ */
+#define OPAL_CALL(name, token) \
+ _GLOBAL(name); \
+ mflr r0; \
+ mfcr r12; \
+ std r0,16(r1); \
+ std r12,8(r1); \
+ std r1,PACAR1(r13); \
+ li r0,0; \
+ mfmsr r12; \
+ ori r0,r0,MSR_EE; \
+ std r12,PACASAVEDMSR(r13); \
+ andc r12,r12,r0; \
+ mtmsrd r12,1; \
+ LOAD_REG_ADDR(r0,.opal_return); \
+ mtlr r0; \
+ li r0,MSR_DR|MSR_IR; \
+ andc r12,r12,r0; \
+ li r0,token; \
+ mtspr SPRN_HSRR1,r12; \
+ LOAD_REG_ADDR(r11,opal); \
+ ld r12,8(r11); \
+ ld r2,0(r11); \
+ mtspr SPRN_HSRR0,r12; \
+ hrfid
+
+_STATIC(opal_return)
+ ld r2,PACATOC(r13);
+ ld r4,8(r1);
+ ld r5,16(r1);
+ ld r6,PACASAVEDMSR(r13);
+ mtspr SPRN_SRR0,r5;
+ mtspr SPRN_SRR1,r6;
+ mtcr r4;
+ rfid
+
+OPAL_CALL(opal_console_write, OPAL_CONSOLE_WRITE);
+OPAL_CALL(opal_console_read, OPAL_CONSOLE_READ);
+OPAL_CALL(opal_console_write_buffer_space, OPAL_CONSOLE_WRITE_BUFFER_SPACE);
+OPAL_CALL(opal_rtc_read, OPAL_RTC_READ);
+OPAL_CALL(opal_rtc_write, OPAL_RTC_WRITE);
+OPAL_CALL(opal_cec_power_down, OPAL_CEC_POWER_DOWN);
+OPAL_CALL(opal_cec_reboot, OPAL_CEC_REBOOT);
+OPAL_CALL(opal_read_nvram, OPAL_READ_NVRAM);
+OPAL_CALL(opal_write_nvram, OPAL_WRITE_NVRAM);
+OPAL_CALL(opal_handle_interrupt, OPAL_HANDLE_INTERRUPT);
+OPAL_CALL(opal_poll_events, OPAL_POLL_EVENTS);
+OPAL_CALL(opal_pci_set_hub_tce_memory, OPAL_PCI_SET_HUB_TCE_MEMORY);
+OPAL_CALL(opal_pci_set_phb_tce_memory, OPAL_PCI_SET_PHB_TCE_MEMORY);
+OPAL_CALL(opal_pci_config_read_byte, OPAL_PCI_CONFIG_READ_BYTE);
+OPAL_CALL(opal_pci_config_read_half_word, OPAL_PCI_CONFIG_READ_HALF_WORD);
+OPAL_CALL(opal_pci_config_read_word, OPAL_PCI_CONFIG_READ_WORD);
+OPAL_CALL(opal_pci_config_write_byte, OPAL_PCI_CONFIG_WRITE_BYTE);
+OPAL_CALL(opal_pci_config_write_half_word, OPAL_PCI_CONFIG_WRITE_HALF_WORD);
+OPAL_CALL(opal_pci_config_write_word, OPAL_PCI_CONFIG_WRITE_WORD);
+OPAL_CALL(opal_set_xive, OPAL_SET_XIVE);
+OPAL_CALL(opal_get_xive, OPAL_GET_XIVE);
+OPAL_CALL(opal_register_exception_handler, OPAL_REGISTER_OPAL_EXCEPTION_HANDLER);
+OPAL_CALL(opal_pci_eeh_freeze_status, OPAL_PCI_EEH_FREEZE_STATUS);
+OPAL_CALL(opal_pci_eeh_freeze_clear, OPAL_PCI_EEH_FREEZE_CLEAR);
+OPAL_CALL(opal_pci_shpc, OPAL_PCI_SHPC);
+OPAL_CALL(opal_pci_phb_mmio_enable, OPAL_PCI_PHB_MMIO_ENABLE);
+OPAL_CALL(opal_pci_set_phb_mem_window, OPAL_PCI_SET_PHB_MEM_WINDOW);
+OPAL_CALL(opal_pci_map_pe_mmio_window, OPAL_PCI_MAP_PE_MMIO_WINDOW);
+OPAL_CALL(opal_pci_set_phb_table_memory, OPAL_PCI_SET_PHB_TABLE_MEMORY);
+OPAL_CALL(opal_pci_set_pe, OPAL_PCI_SET_PE);
+OPAL_CALL(opal_pci_set_peltv, OPAL_PCI_SET_PELTV);
+OPAL_CALL(opal_pci_set_mve, OPAL_PCI_SET_MVE);
+OPAL_CALL(opal_pci_set_mve_enable, OPAL_PCI_SET_MVE_ENABLE);
+OPAL_CALL(opal_pci_get_xive_reissue, OPAL_PCI_GET_XIVE_REISSUE);
+OPAL_CALL(opal_pci_set_xive_reissue, OPAL_PCI_SET_XIVE_REISSUE);
+OPAL_CALL(opal_pci_set_xive_pe, OPAL_PCI_SET_XIVE_PE);
+OPAL_CALL(opal_get_xive_source, OPAL_GET_XIVE_SOURCE);
+OPAL_CALL(opal_get_msi_32, OPAL_GET_MSI_32);
+OPAL_CALL(opal_get_msi_64, OPAL_GET_MSI_64);
+OPAL_CALL(opal_start_cpu, OPAL_START_CPU);
+OPAL_CALL(opal_query_cpu_status, OPAL_QUERY_CPU_STATUS);
+OPAL_CALL(opal_write_oppanel, OPAL_WRITE_OPPANEL);
+OPAL_CALL(opal_pci_map_pe_dma_window, OPAL_PCI_MAP_PE_DMA_WINDOW);
+OPAL_CALL(opal_pci_map_pe_dma_window_real, OPAL_PCI_MAP_PE_DMA_WINDOW_REAL);
+OPAL_CALL(opal_pci_reset, OPAL_PCI_RESET);
diff --git a/arch/powerpc/platforms/powernv/opal.c b/arch/powerpc/platforms/powernv/opal.c
new file mode 100644
index 0000000..8d55107
--- /dev/null
+++ b/arch/powerpc/platforms/powernv/opal.c
@@ -0,0 +1,154 @@
+/*
+ * PowerNV OPAL high level interfaces
+ *
+ * Copyright 2011 IBM Corp.
+ *
+ * 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.
+ */
+
+#undef DEBUG
+
+#include <linux/types.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <asm/opal.h>
+#include <asm/firmware.h>
+
+#include "powernv.h"
+
+struct opal {
+ u64 base;
+ u64 entry;
+} opal;
+
+static struct device_node *opal_node;
+static DEFINE_SPINLOCK(opal_write_lock);
+
+int __init early_init_dt_scan_opal(unsigned long node,
+ const char *uname, int depth, void *data)
+{
+ const void *basep, *entryp;
+ unsigned long basesz, entrysz;
+
+ if (depth != 1 || strcmp(uname, "ibm,opal") != 0)
+ return 0;
+
+ basep = of_get_flat_dt_prop(node, "opal-base-address", &basesz);
+ entryp = of_get_flat_dt_prop(node, "opal-entry-address", &entrysz);
+
+ if (!basep || !entryp)
+ return 1;
+
+ opal.base = of_read_number(basep, basesz/4);
+ opal.entry = of_read_number(entryp, entrysz/4);
+
+ pr_debug("OPAL Base = 0x%llx (basep=%p basesz=%ld)\n",
+ opal.base, basep, basesz);
+ pr_debug("OPAL Entry = 0x%llx (entryp=%p basesz=%ld)\n",
+ opal.entry, entryp, entrysz);
+
+ powerpc_firmware_features |= FW_FEATURE_OPAL;
+ if (of_flat_dt_is_compatible(node, "ibm,opal-v2")) {
+ powerpc_firmware_features |= FW_FEATURE_OPALv2;
+ printk("OPAL V2 detected !\n");
+ } else {
+ printk("OPAL V1 detected !\n");
+ }
+
+ return 1;
+}
+
+int opal_get_chars(uint32_t vtermno, char *buf, int count)
+{
+ s64 len, rc;
+ u64 evt;
+
+ if (!opal.entry)
+ return 0;
+ opal_poll_events(&evt);
+ if ((evt & OPAL_EVENT_CONSOLE_INPUT) == 0)
+ return 0;
+ len = count;
+ rc = opal_console_read(vtermno, &len, buf);
+ if (rc == OPAL_SUCCESS)
+ return len;
+ return 0;
+}
+
+int opal_put_chars(uint32_t vtermno, const char *data, int total_len)
+{
+ int written = 0;
+ s64 len, rc = OPAL_BUSY;
+ unsigned long flags;
+ u64 evt;
+
+ if (!opal.entry)
+ return 0;
+
+ /* We want put_chars to be atomic to avoid mangling of hvsi
+ * packets. To do that, we first test for room and return
+ * -EAGAIN if there isn't enough
+ */
+ spin_lock_irqsave(&opal_write_lock, flags);
+ rc = opal_console_write_buffer_space(vtermno, &len);
+ if (rc || len < total_len) {
+ spin_unlock_irqrestore(&opal_write_lock, flags);
+ /* Closed -> drop characters */
+ if (rc)
+ return total_len;
+ opal_poll_events(&evt);
+ return -EAGAIN;
+ }
+
+ /* We still try to handle partial completions, though they
+ * should no longer happen.
+ */
+ while(total_len > 0 && (rc == OPAL_BUSY ||
+ rc == OPAL_BUSY_EVENT || rc == OPAL_SUCCESS)) {
+ len = total_len;
+ rc = opal_console_write(vtermno, &len, data);
+ if (rc == OPAL_SUCCESS) {
+ total_len -= len;
+ data += len;
+ written += len;
+ }
+ /* This is a bit nasty but we need that for the console to
+ * flush when there aren't any interrupts. We will clean
+ * things a bit later to limit that to synchronous path
+ * such as the kernel console and xmon/udbg
+ */
+ do
+ opal_poll_events(&evt);
+ while(rc == OPAL_SUCCESS && (evt & OPAL_EVENT_CONSOLE_OUTPUT));
+ }
+ spin_unlock_irqrestore(&opal_write_lock, flags);
+ return written;
+}
+
+static int __init opal_init(void)
+{
+ struct device_node *np, *consoles;
+
+ opal_node = of_find_node_by_path("/ibm,opal");
+ if (!opal_node) {
+ pr_warn("opal: Node not found\n");
+ return -ENODEV;
+ }
+ if (firmware_has_feature(FW_FEATURE_OPALv2))
+ consoles = of_find_node_by_path("/ibm,opal/consoles");
+ else
+ consoles = of_node_get(opal_node);
+
+ /* Register serial ports */
+ for_each_child_of_node(consoles, np) {
+ if (strcmp(np->name, "serial"))
+ continue;
+ of_platform_device_create(np, NULL, NULL);
+ }
+ of_node_put(consoles);
+ return 0;
+}
+subsys_initcall(opal_init);
diff --git a/arch/powerpc/platforms/powernv/setup.c b/arch/powerpc/platforms/powernv/setup.c
index 569f9cc..b6e5ff8 100644
--- a/arch/powerpc/platforms/powernv/setup.c
+++ b/arch/powerpc/platforms/powernv/setup.c
@@ -74,6 +74,12 @@ static void pnv_show_cpuinfo(struct seq_file *m)
if (root)
model = of_get_property(root, "model", NULL);
seq_printf(m, "machine\t\t: PowerNV %s\n", model);
+ if (firmware_has_feature(FW_FEATURE_OPALv2))
+ seq_printf(m, "firmware\t: OPAL v2\n");
+ else if (firmware_has_feature(FW_FEATURE_OPAL))
+ seq_printf(m, "firmware\t: OPAL v1\n");
+ else
+ seq_printf(m, "firmware\t: BML\n");
of_node_put(root);
}
diff --git a/arch/powerpc/platforms/powernv/smp.c b/arch/powerpc/platforms/powernv/smp.c
index 4f4ec37..e877366 100644
--- a/arch/powerpc/platforms/powernv/smp.c
+++ b/arch/powerpc/platforms/powernv/smp.c
@@ -30,6 +30,7 @@
#include <asm/vdso_datapage.h>
#include <asm/cputhreads.h>
#include <asm/xics.h>
+#include <asm/opal.h>
#include "powernv.h"
@@ -62,6 +63,28 @@ static int pnv_smp_cpu_bootable(unsigned int nr)
return 1;
}
+int __devinit pnv_smp_kick_cpu(int nr)
+{
+ unsigned int pcpu = get_hard_smp_processor_id(nr);
+ unsigned long start_here = __pa(*((unsigned long *)
+ generic_secondary_smp_init));
+ long rc;
+
+ BUG_ON(nr < 0 || nr >= NR_CPUS);
+
+ /* On OPAL v2 the CPU are still spinning inside OPAL itself,
+ * get them back now
+ */
+ if (firmware_has_feature(FW_FEATURE_OPALv2)) {
+ pr_devel("OPAL: Starting CPU %d (HW 0x%x)...\n", nr, pcpu);
+ rc = opal_start_cpu(pcpu, start_here);
+ if (rc != OPAL_SUCCESS)
+ pr_warn("OPAL Error %ld starting CPU %d\n",
+ rc, nr);
+ }
+ return smp_generic_kick_cpu(nr);
+}
+
#ifdef CONFIG_HOTPLUG_CPU
static int pnv_smp_cpu_disable(void)
@@ -127,7 +150,7 @@ static struct smp_ops_t pnv_smp_ops = {
.message_pass = smp_muxed_ipi_message_pass,
.cause_ipi = NULL, /* Filled at runtime by xics_smp_probe() */
.probe = xics_smp_probe,
- .kick_cpu = smp_generic_kick_cpu,
+ .kick_cpu = pnv_smp_kick_cpu,
.setup_cpu = pnv_smp_setup_cpu,
.cpu_bootable = pnv_smp_cpu_bootable,
#ifdef CONFIG_HOTPLUG_CPU
--
1.7.4.1
^ permalink raw reply related
* [PATCH 10/21] powerpc/powernv: Get kernel command line accross OPAL takeover
From: Benjamin Herrenschmidt @ 2011-09-10 14:19 UTC (permalink / raw)
To: linuxppc-dev
In-Reply-To: <1315664408-16797-1-git-send-email-benh@kernel.crashing.org>
We stash it in boot_command_line which isn't in BSS and so won't
be overwritten. We then use that as a default cmd_line before
we walk the device-tree.
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---
arch/powerpc/kernel/prom.c | 7 +++++++
arch/powerpc/kernel/prom_init.c | 4 ++++
arch/powerpc/kernel/prom_init_check.sh | 3 ++-
3 files changed, 13 insertions(+), 1 deletions(-)
diff --git a/arch/powerpc/kernel/prom.c b/arch/powerpc/kernel/prom.c
index 174e1e9..7b90c56 100644
--- a/arch/powerpc/kernel/prom.c
+++ b/arch/powerpc/kernel/prom.c
@@ -712,6 +712,13 @@ void __init early_init_devtree(void *params)
of_scan_flat_dt(early_init_dt_scan_phyp_dump, NULL);
#endif
+ /* Pre-initialize the cmd_line with the content of boot_commmand_line,
+ * which will be empty except when the content of the variable has
+ * been overriden by a bootloading mechanism. This happens typically
+ * with HAL takeover
+ */
+ strlcpy(cmd_line, boot_command_line, COMMAND_LINE_SIZE);
+
/* Retrieve various informations from the /chosen node of the
* device-tree, including the platform type, initrd location and
* size, TCE reserve, and more ...
diff --git a/arch/powerpc/kernel/prom_init.c b/arch/powerpc/kernel/prom_init.c
index 9369287..e3f3904 100644
--- a/arch/powerpc/kernel/prom_init.c
+++ b/arch/powerpc/kernel/prom_init.c
@@ -1449,6 +1449,10 @@ static void prom_opal_takeover(void)
opal_addr = top_addr;
args->hal_addr = opal_addr;
+ /* Copy the command line to the kernel image */
+ strlcpy(RELOC(boot_command_line), RELOC(prom_cmd_line),
+ COMMAND_LINE_SIZE);
+
prom_debug(" k_image = 0x%lx\n", args->k_image);
prom_debug(" k_size = 0x%lx\n", args->k_size);
prom_debug(" k_entry = 0x%lx\n", args->k_entry);
diff --git a/arch/powerpc/kernel/prom_init_check.sh b/arch/powerpc/kernel/prom_init_check.sh
index 20af6aa..70f4286 100644
--- a/arch/powerpc/kernel/prom_init_check.sh
+++ b/arch/powerpc/kernel/prom_init_check.sh
@@ -21,7 +21,8 @@ _end enter_prom memcpy memset reloc_offset __secondary_hold
__secondary_hold_acknowledge __secondary_hold_spinloop __start
strcmp strcpy strlcpy strlen strncmp strstr logo_linux_clut224
reloc_got2 kernstart_addr memstart_addr linux_banner _stext
-opal_query_takeover opal_do_takeover opal_enter_rtas opal_secondary_entry"
+opal_query_takeover opal_do_takeover opal_enter_rtas opal_secondary_entry
+boot_command_line"
NM="$1"
OBJ="$2"
--
1.7.4.1
^ 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