* [PATCH] powerpc: Jump label misalignment causes oops at boot
From: Anton Blanchard @ 2011-07-27 0:11 UTC (permalink / raw)
To: benh, paulus, michael; +Cc: linuxppc-dev
I hit an oops at boot on the first instruction of timer_cpu_notify:
Oops: Exception in kernel mode, sig: 4 [#1]
...
NIP [c000000000722f88] .timer_cpu_notify+0x0/0x388
The code should look like:
c000000000722f78: eb e9 00 30 ld r31,48(r9)
c000000000722f7c: 2f bf 00 00 cmpdi cr7,r31,0
c000000000722f80: 40 9e ff 44 bne+ cr7,c000000000722ec4
c000000000722f84: 4b ff ff 74 b c000000000722ef8
c000000000722f88 <.timer_cpu_notify>:
c000000000722f88: 7c 08 02 a6 mflr r0
c000000000722f8c: 2f a4 00 07 cmpdi cr7,r4,7
c000000000722f90: fb c1 ff f0 std r30,-16(r1)
c000000000722f94: fb 61 ff d8 std r27,-40(r1)
But the oops output shows these instructions:
eb61ffd8 eb81ffe0 eba1ffe8 ebc1fff0 7c0803a6 ebe1fff8 4e800020 00000000
ebe90030 c0000000 00ad0a28 00000000 2fa40007 fbc1fff0 fb61ffd8
So we scribbled over our instructions with c000000000ad0a28 which
is an address inside the jump_table ELF section.
It turns out the jump_table section is only aligned to 8 bytes but
we are aligning our entries within the section to 16 bytes. This
means our entries can be offset from the table:
c000000000acd4a8 <__start___jump_table>:
...
c000000000ad0a10: c0 00 00 00 lfs f0,0(0)
c000000000ad0a14: 00 70 cd 5c .long 0x70cd5c
c000000000ad0a18: c0 00 00 00 lfs f0,0(0)
c000000000ad0a1c: 00 70 cd 90 .long 0x70cd90
c000000000ad0a20: c0 00 00 00 lfs f0,0(0)
c000000000ad0a24: 00 ac a4 20 .long 0xaca420
And the jump table sort code gets very confused and writes into the
wrong spot. Remove the alignment and also remove the padding since
we it saves some space and we shouldn't need it.
Signed-off-by: Anton Blanchard <anton@samba.org>
---
Index: linux-powerpc/arch/powerpc/include/asm/jump_label.h
===================================================================
--- linux-powerpc.orig/arch/powerpc/include/asm/jump_label.h
2011-07-27 09:35:04.266872078 +1000 +++
linux-powerpc/arch/powerpc/include/asm/jump_label.h 2011-07-27
09:39:32.581719669 +1000 @@ -22,7 +22,6 @@ static __always_inline bool
arch_static_ asm goto("1:\n\t" "nop\n\t"
".pushsection __jump_table, \"aw\"\n\t"
- ".align 4\n\t"
JUMP_ENTRY_TYPE "1b, %l[l_yes], %c0\n\t"
".popsection \n\t"
: : "i" (key) : : l_yes);
@@ -41,7 +40,6 @@ struct jump_entry {
jump_label_t code;
jump_label_t target;
jump_label_t key;
- jump_label_t pad;
};
#endif /* _ASM_POWERPC_JUMP_LABEL_H */
^ permalink raw reply
* Re: [PATCH RFC] perf event: Torrent, add support for the various PMUs on the Torrent chip.
From: Kumar Gala @ 2011-07-26 22:24 UTC (permalink / raw)
To: Carl E. Love; +Cc: linuxppc-dev, ltc-interlock
In-Reply-To: <1311698974.28685.4.camel@oc5652146517.ibm.com>
On Jul 26, 2011, at 11:49 AM, Carl E. Love wrote:
>=20
>=20
> We are requesting your help to review the following patch prior to =
posting it=20
> upstream. The patch is against the 2.6.39 tree (which is already out =
of date).
> Thank your for your help and input.
>=20
> Carl Love
> =
--------------------------------------------------------------------------=
> [PATCH RFC] perf event: Torrent, add support for the various PMUs on =
the Torrent chip.
>=20
> Support for the Torrent hardware performance monitor units (PMU) on =
the LL
> link, WXYZ links, MCD bus, and CAU units is added. These PMUs are =
specific
> to the Torrent system which is built using POWER7 processors. These =
PMUs
> are specifc to the IBM P7IH system's Torrent chips, which are used to
> interconnect POWER7 processors. Hence the platform specific files to
> support these PMUs are in the platform-specific directory
> arch/powerpc/platforms/torrent. The include files are added to the
> standard powerpc include directory. The Torrent PMU support uses the
> multiple PMU perf_events support. A single perf_events PMU type is
> created to cover all of the various Torrent hardware PMUs. The =
generic
> Torrent PMU type allows all of the specific Torrent hardware PMU =
events to
> be included into a group of events within the perf_events Torrent PMU =
type.
> The advantage of this model is that all of the events for the various
> links are guaranteed to be measured at the same time, providing good
> correlation between the activity on each of the different Torrent =
links.
>=20
> This patch is a forward port of the initial Torrent PMU patch for =
2.6.32
> that was done as part of the development effort on Torrent and never =
pushed
> upstream.
>=20
> Signed-off-by: Carl Love <carll@us.ibm.com>
> ---
> arch/powerpc/include/asm/cau_pmu.h | 39 +
> arch/powerpc/include/asm/hvcall.h | 14 +
> arch/powerpc/include/asm/mmu_pmu.h | 39 +
> arch/powerpc/include/asm/power_torrent_events.h | 106 ++
> arch/powerpc/include/asm/powerbus_bus_util_pmu.h | 39 +
> arch/powerpc/include/asm/powerbus_ll_pmu.h | 39 +
> arch/powerpc/include/asm/powerbus_mcd_pmu.h | 44 +
> arch/powerpc/include/asm/powerbus_wxyz_pmu.h | 38 +
> arch/powerpc/include/asm/torrent_nest_pmu.h | 192 +++
Seems like a lot of these headers could move to plarforms/p7ih/
> arch/powerpc/kernel/perf_event.c | 4 +
> arch/powerpc/platforms/Makefile | 1 +
> arch/powerpc/platforms/p7ih/Makefile | 10 +
> arch/powerpc/platforms/p7ih/cau_pmu.c | 160 ++
> arch/powerpc/platforms/p7ih/mmu_pmu.c | 156 ++
> .../powerpc/platforms/p7ih/powerbus_bus_util_pmu.c | 410 +++++
> arch/powerpc/platforms/p7ih/powerbus_ll_pmu.c | 273 ++++
> arch/powerpc/platforms/p7ih/powerbus_mcd_pmu.c | 492 ++++++
> arch/powerpc/platforms/p7ih/powerbus_wxyz_pmu.c | 244 +++
> arch/powerpc/platforms/p7ih/torrent_pmu.c | 1582 =
++++++++++++++++++++
> arch/powerpc/platforms/pseries/Kconfig | 5 +
> 20 files changed, 3887 insertions(+), 0 deletions(-)
>=20
> +#define PMU_SPACE_MASK 0xFF000000
> +#define POWERPC_CORE_SPACE 0x00000000
> +#define TORRENT_SPACE 0x01000000
> +#define IS_CORE_EVENT(x) ((x & PMU_SPACE_MASK) =3D=3D =
POWERPC_CORE_SPACE)
> +#define IS_TORRENT_EVENT(x) ((x & PMU_SPACE_MASK) =3D=3D =
TORRENT_SPACE)
>=20
> diff --git a/arch/powerpc/kernel/perf_event.c =
b/arch/powerpc/kernel/perf_event.c
> index 822f630..aa1eb43 100644
> --- a/arch/powerpc/kernel/perf_event.c
> +++ b/arch/powerpc/kernel/perf_event.c
> @@ -19,6 +19,8 @@
> #include <asm/firmware.h>
> #include <asm/ptrace.h>
>=20
> +#include <asm/torrent_nest_pmu.h>
> +
> struct cpu_hw_events {
> int n_events;
> int n_percpu;
> @@ -1092,6 +1094,8 @@ static int power_pmu_event_init(struct =
perf_event *event)
> break;
> case PERF_TYPE_RAW:
> ev =3D event->attr.config;
> + if (!IS_CORE_EVENT(ev))
> + return -ENOENT;
seems like we should put what is needed for IS_CORE_EVENT() into =
asm/pmc.h if we believe its reasonable..
pulling in <asm/torrent_nest_pmu.h> seems the torrent support is pretty =
specific.
> break;
> default:
> return -ENOENT;
- k=
^ permalink raw reply
* Re: [PATCH v3] mtd: eLBC NAND: update ecc_stats.corrected when lteccr available
From: Scott Wood @ 2011-07-26 20:10 UTC (permalink / raw)
To: MichaelHench; +Cc: linuxppc-dev, linux-mtd, mlcreech, mhench
In-Reply-To: <CAHyXW61rWcoE0Wcd9K7BsP3+VKzmQo=RPMM9+1DTw+QdF9MVew@mail.gmail.com>
On Tue, 26 Jul 2011 15:07:42 -0500
Michael Hench <michaelhench@gmail.com> wrote:
> update ecc_stats.corrected if LTECCR register is available.
>
> v2: kernel standard C formatting
>
> v3: kernel standard C formatting again, changed a comment to get under 80 chars
>
> Signed-off-by: Michael Hench <MichaelHench@gmail.com>
Acked-by: Scott Wood <scottwood@freescale.com>
-Scott
^ permalink raw reply
* [PATCH v3] mtd: eLBC NAND: update ecc_stats.corrected when lteccr available
From: Michael Hench @ 2011-07-26 20:07 UTC (permalink / raw)
To: scottwood, linux-mtd, linuxppc-dev, mhench, mlcreech
update ecc_stats.corrected if LTECCR register is available.
v2: kernel standard C formatting
v3: kernel standard C formatting again, changed a comment to get under 80 chars
Signed-off-by: Michael Hench <MichaelHench@gmail.com>
---
diff -purN orig/drivers/mtd/nand/fsl_elbc_nand.c
linux-3.0/drivers/mtd/nand/fsl_elbc_nand.c
--- orig/drivers/mtd/nand/fsl_elbc_nand.c 2011-07-25 14:50:56.838326055 -0500
+++ linux-3.0/drivers/mtd/nand/fsl_elbc_nand.c 2011-07-25
14:48:35.680940052 -0500
@@ -256,6 +256,25 @@ static int fsl_elbc_run_command(struct m
return -EIO;
}
+ if (chip->ecc.mode != NAND_ECC_HW)
+ return 0;
+
+ if (elbc_fcm_ctrl->read_bytes == mtd->writesize + mtd->oobsize) {
+ uint32_t lteccr = in_be32(&lbc->lteccr);
+ /*
+ * if command was a full page read and the ELBC
+ * has the LTECCR register, then bits 12-15 (ppc order) of
+ * LTECCR indicates which 512 byte sub-pages had fixed errors.
+ * bits 28-31 are uncorrectable errors, marked elsewhere.
+ * for small page nand only 1 bit is used.
+ * if the ELBC doesn't have the lteccr register it reads 0
+ */
+ if (lteccr & 0x000F000F)
+ out_be32(&lbc->lteccr, 0x000F000F); /* clear lteccr */
+ if (lteccr & 0x000F0000)
+ mtd->ecc_stats.corrected++;
+ }
+
return 0;
}
--
^ permalink raw reply
* Re: [PATCH v2] mtd: eLBC NAND: update ecc_stats.corrected when lteccr available
From: Scott Wood @ 2011-07-26 19:05 UTC (permalink / raw)
To: MichaelHench; +Cc: linuxppc-dev, linux-mtd, mlcreech, mhench
In-Reply-To: <CAHyXW604AxBGO=AEAyOcba2VfsjVve+RV7_A8QXaUpJRfAsq3Q@mail.gmail.com>
On Tue, 26 Jul 2011 13:59:25 -0500
Michael Hench <michaelhench@gmail.com> wrote:
> update ecc_stats.corrected if LTECCR register is available.
>
> v2: kernel standard C formatting
Missed a couple:
> + if(lteccr & 0x000F000F)
> + out_be32(&lbc->lteccr, 0x000F000F); /* clear lteccr */
> + if(lteccr & 0x000F0000)
> + mtd->ecc_stats.corrected++;
-Scott
^ permalink raw reply
* [PATCH v2] mtd: eLBC NAND: update ecc_stats.corrected when lteccr available
From: Michael Hench @ 2011-07-26 18:59 UTC (permalink / raw)
To: scottwood, linux-mtd, linuxppc-dev, mhench, mlcreech
update ecc_stats.corrected if LTECCR register is available.
v2: kernel standard C formatting
Signed-off-by: Michael Hench <MichaelHench@gmail.com>
---
diff -purN orig/drivers/mtd/nand/fsl_elbc_nand.c
linux-3.0/drivers/mtd/nand/fsl_elbc_nand.c
--- orig/drivers/mtd/nand/fsl_elbc_nand.c 2011-07-25 14:50:56.838326055 -0500
+++ linux-3.0/drivers/mtd/nand/fsl_elbc_nand.c 2011-07-25
14:48:35.680940052 -0500
@@ -256,6 +256,25 @@ static int fsl_elbc_run_command(struct m
return -EIO;
}
+ if (chip->ecc.mode != NAND_ECC_HW)
+ return 0;
+
+ if (elbc_fcm_ctrl->read_bytes == mtd->writesize + mtd->oobsize) {
+ uint32_t lteccr = in_be32(&lbc->lteccr);
+ /*
+ * if command was a full page read and the ELBC
+ * has the LTECCR register, then bits 12-15 (ppc order) of
+ * LTECCR indicate which 512 byte sub-pages had corrected errors.
+ * bits 28-31 are uncorrectable errors, marked elsewhere.
+ * for small page nand only 1 bit is used.
+ * if the ELBC doesn't have the lteccr register it reads 0
+ */
+ if(lteccr & 0x000F000F)
+ out_be32(&lbc->lteccr, 0x000F000F); /* clear lteccr */
+ if(lteccr & 0x000F0000)
+ mtd->ecc_stats.corrected++;
+ }
+
return 0;
}
--
^ permalink raw reply
* [PATCH] RapidIO: Fix use of non-compatible registers
From: Alexandre Bounine @ 2011-07-26 18:07 UTC (permalink / raw)
To: akpm, linux-kernel, linuxppc-dev
Cc: Chul Kim, Alexandre Bounine, Thomas Moll, stable
Replace/remove use of RIO v.1.2 registers/bits that are not forward-compatible
with newer versions of RapidIO specification.
RapidIO specification v. 1.3 removed Write Port CSR, Doorbell CSR,
Mailbox CSR and Mailbox and Doorbell bits of the PEF CAR.
Signed-off-by: Alexandre Bounine <alexandre.bounine@idt.com>
Cc: Kumar Gala <galak@kernel.crashing.org>
Cc: Matt Porter <mporter@kernel.crashing.org>
Cc: Li Yang <leoli@freescale.com>
Cc: Thomas Moll <thomas.moll@sysgo.com>
Cc: Chul Kim <chul.kim@idt.com>
Cc: <stable@kernel.org>
---
drivers/net/rionet.c | 23 ++++++++---------------
drivers/rapidio/rio-scan.c | 3 +--
include/linux/rio_regs.h | 18 +++++++++---------
3 files changed, 18 insertions(+), 26 deletions(-)
diff --git a/drivers/net/rionet.c b/drivers/net/rionet.c
index 86ac38c..3bb1311 100644
--- a/drivers/net/rionet.c
+++ b/drivers/net/rionet.c
@@ -80,13 +80,13 @@ static int rionet_capable = 1;
*/
static struct rio_dev **rionet_active;
-#define is_rionet_capable(pef, src_ops, dst_ops) \
- ((pef & RIO_PEF_INB_MBOX) && \
- (pef & RIO_PEF_INB_DOORBELL) && \
+#define is_rionet_capable(src_ops, dst_ops) \
+ ((src_ops & RIO_SRC_OPS_DATA_MSG) && \
+ (dst_ops & RIO_DST_OPS_DATA_MSG) && \
(src_ops & RIO_SRC_OPS_DOORBELL) && \
(dst_ops & RIO_DST_OPS_DOORBELL))
#define dev_rionet_capable(dev) \
- is_rionet_capable(dev->pef, dev->src_ops, dev->dst_ops)
+ is_rionet_capable(dev->src_ops, dev->dst_ops)
#define RIONET_MAC_MATCH(x) (*(u32 *)x == 0x00010001)
#define RIONET_GET_DESTID(x) (*(u16 *)(x + 4))
@@ -282,7 +282,6 @@ static int rionet_open(struct net_device *ndev)
{
int i, rc = 0;
struct rionet_peer *peer, *tmp;
- u32 pwdcsr;
struct rionet_private *rnet = netdev_priv(ndev);
if (netif_msg_ifup(rnet))
@@ -332,13 +331,8 @@ static int rionet_open(struct net_device *ndev)
continue;
}
- /*
- * If device has initialized inbound doorbells,
- * send a join message
- */
- rio_read_config_32(peer->rdev, RIO_WRITE_PORT_CSR, &pwdcsr);
- if (pwdcsr & RIO_DOORBELL_AVAIL)
- rio_send_doorbell(peer->rdev, RIONET_DOORBELL_JOIN);
+ /* Send a join message */
+ rio_send_doorbell(peer->rdev, RIONET_DOORBELL_JOIN);
}
out:
@@ -492,7 +486,7 @@ static int rionet_setup_netdev(struct rio_mport *mport, struct net_device *ndev)
static int rionet_probe(struct rio_dev *rdev, const struct rio_device_id *id)
{
int rc = -ENODEV;
- u32 lpef, lsrc_ops, ldst_ops;
+ u32 lsrc_ops, ldst_ops;
struct rionet_peer *peer;
struct net_device *ndev = NULL;
@@ -515,12 +509,11 @@ static int rionet_probe(struct rio_dev *rdev, const struct rio_device_id *id)
* on later probes
*/
if (!rionet_check) {
- rio_local_read_config_32(rdev->net->hport, RIO_PEF_CAR, &lpef);
rio_local_read_config_32(rdev->net->hport, RIO_SRC_OPS_CAR,
&lsrc_ops);
rio_local_read_config_32(rdev->net->hport, RIO_DST_OPS_CAR,
&ldst_ops);
- if (!is_rionet_capable(lpef, lsrc_ops, ldst_ops)) {
+ if (!is_rionet_capable(lsrc_ops, ldst_ops)) {
printk(KERN_ERR
"%s: local device is not network capable\n",
DRV_NAME);
diff --git a/drivers/rapidio/rio-scan.c b/drivers/rapidio/rio-scan.c
index ee89358..ebe77dd 100644
--- a/drivers/rapidio/rio-scan.c
+++ b/drivers/rapidio/rio-scan.c
@@ -505,8 +505,7 @@ static struct rio_dev __devinit *rio_setup_device(struct rio_net *net,
rdev->dev.dma_mask = &rdev->dma_mask;
rdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
- if ((rdev->pef & RIO_PEF_INB_DOORBELL) &&
- (rdev->dst_ops & RIO_DST_OPS_DOORBELL))
+ if (rdev->dst_ops & RIO_DST_OPS_DOORBELL)
rio_init_dbell_res(&rdev->riores[RIO_DOORBELL_RESOURCE],
0, 0xffff);
diff --git a/include/linux/rio_regs.h b/include/linux/rio_regs.h
index 9026b30..218168a 100644
--- a/include/linux/rio_regs.h
+++ b/include/linux/rio_regs.h
@@ -36,12 +36,12 @@
#define RIO_PEF_PROCESSOR 0x20000000 /* [I] Processor */
#define RIO_PEF_SWITCH 0x10000000 /* [I] Switch */
#define RIO_PEF_MULTIPORT 0x08000000 /* [VI, 2.1] Multiport */
-#define RIO_PEF_INB_MBOX 0x00f00000 /* [II] Mailboxes */
-#define RIO_PEF_INB_MBOX0 0x00800000 /* [II] Mailbox 0 */
-#define RIO_PEF_INB_MBOX1 0x00400000 /* [II] Mailbox 1 */
-#define RIO_PEF_INB_MBOX2 0x00200000 /* [II] Mailbox 2 */
-#define RIO_PEF_INB_MBOX3 0x00100000 /* [II] Mailbox 3 */
-#define RIO_PEF_INB_DOORBELL 0x00080000 /* [II] Doorbells */
+#define RIO_PEF_INB_MBOX 0x00f00000 /* [II, <= 1.2] Mailboxes */
+#define RIO_PEF_INB_MBOX0 0x00800000 /* [II, <= 1.2] Mailbox 0 */
+#define RIO_PEF_INB_MBOX1 0x00400000 /* [II, <= 1.2] Mailbox 1 */
+#define RIO_PEF_INB_MBOX2 0x00200000 /* [II, <= 1.2] Mailbox 2 */
+#define RIO_PEF_INB_MBOX3 0x00100000 /* [II, <= 1.2] Mailbox 3 */
+#define RIO_PEF_INB_DOORBELL 0x00080000 /* [II, <= 1.2] Doorbells */
#define RIO_PEF_EXT_RT 0x00000200 /* [III, 1.3] Extended route table support */
#define RIO_PEF_STD_RT 0x00000100 /* [III, 1.3] Standard route table support */
#define RIO_PEF_CTLS 0x00000010 /* [III] CTLS */
@@ -102,7 +102,7 @@
#define RIO_SWITCH_RT_LIMIT 0x34 /* [III, 1.3] Switch Route Table Destination ID Limit CAR */
#define RIO_RT_MAX_DESTID 0x0000ffff
-#define RIO_MBOX_CSR 0x40 /* [II] Mailbox CSR */
+#define RIO_MBOX_CSR 0x40 /* [II, <= 1.2] Mailbox CSR */
#define RIO_MBOX0_AVAIL 0x80000000 /* [II] Mbox 0 avail */
#define RIO_MBOX0_FULL 0x40000000 /* [II] Mbox 0 full */
#define RIO_MBOX0_EMPTY 0x20000000 /* [II] Mbox 0 empty */
@@ -128,8 +128,8 @@
#define RIO_MBOX3_FAIL 0x00000008 /* [II] Mbox 3 fail */
#define RIO_MBOX3_ERROR 0x00000004 /* [II] Mbox 3 error */
-#define RIO_WRITE_PORT_CSR 0x44 /* [I] Write Port CSR */
-#define RIO_DOORBELL_CSR 0x44 /* [II] Doorbell CSR */
+#define RIO_WRITE_PORT_CSR 0x44 /* [I, <= 1.2] Write Port CSR */
+#define RIO_DOORBELL_CSR 0x44 /* [II, <= 1.2] Doorbell CSR */
#define RIO_DOORBELL_AVAIL 0x80000000 /* [II] Doorbell avail */
#define RIO_DOORBELL_FULL 0x40000000 /* [II] Doorbell full */
#define RIO_DOORBELL_EMPTY 0x20000000 /* [II] Doorbell empty */
--
1.7.6
^ permalink raw reply related
* Re: [PATCH] mtd: eLBC NAND: update ecc_stats.corrected when lteccr available
From: Scott Wood @ 2011-07-26 18:20 UTC (permalink / raw)
To: MichaelHench; +Cc: linuxppc-dev, linux-mtd, mlcreech, mhench
In-Reply-To: <CAHyXW61fOhuDcxCVQQ6inO_Uoz6m+hN_eB0pQ-rW0c=miJC=3w@mail.gmail.com>
On Tue, 26 Jul 2011 08:45:42 -0500
Michael Hench <michaelhench@gmail.com> wrote:
> update ecc_stats.corrected if LTECCR register is available.
>
> Signed-off-by: Michael Hench <MichaelHench@gmail.com>
> ---
> diff -purN orig/drivers/mtd/nand/fsl_elbc_nand.c
> linux-3.0/drivers/mtd/nand/fsl_elbc_nand.c
> --- orig/drivers/mtd/nand/fsl_elbc_nand.c 2011-07-25 14:50:56.838326055 -0500
> +++ linux-3.0/drivers/mtd/nand/fsl_elbc_nand.c 2011-07-25
> 14:48:35.680940052 -0500
> @@ -256,6 +256,25 @@ static int fsl_elbc_run_command(struct m
> return -EIO;
> }
>
> + if(chip->ecc.mode != NAND_ECC_HW)
> + return(0);
> +
> + if(elbc_fcm_ctrl->read_bytes == mtd->writesize + mtd->oobsize) {
> + uint32_t lteccr = in_be32(&lbc->lteccr);
> + /*
> + * if command was a full page read and the ELBC
> + * has the LTECCR register, then bits 12-15 (ppc order) of
> + * LTECCR indicate which 512 byte sub-pages had corrected errors.
> + * bits 28-31 are uncorrectable errors, marked elsewhere.
> + * for small page nand only 1 bit is used.
> + * if the ELBC doesn't have the lteccr register it reads 0
> + */
> + if(lteccr & 0x000F000F)
> + out_be32(&lbc->lteccr, 0x000F000F); /* clear lteccr */
> + if(lteccr & 0x000F0000)
> + mtd->ecc_stats.corrected++;
> + }
Add a space after "if" and remove the parentheses in "return(0)", but
otherwise ACK.
-Scott
^ permalink raw reply
* [PATCH RFC] perf event: Torrent, add support for the various PMUs on the Torrent chip.
From: Carl E. Love @ 2011-07-26 16:49 UTC (permalink / raw)
To: ltc-interlock, linuxppc-dev
We are requesting your help to review the following patch prior to posting it
upstream. The patch is against the 2.6.39 tree (which is already out of date).
Thank your for your help and input.
Carl Love
--------------------------------------------------------------------------
[PATCH RFC] perf event: Torrent, add support for the various PMUs on the Torrent chip.
Support for the Torrent hardware performance monitor units (PMU) on the LL
link, WXYZ links, MCD bus, and CAU units is added. These PMUs are specific
to the Torrent system which is built using POWER7 processors. These PMUs
are specifc to the IBM P7IH system's Torrent chips, which are used to
interconnect POWER7 processors. Hence the platform specific files to
support these PMUs are in the platform-specific directory
arch/powerpc/platforms/torrent. The include files are added to the
standard powerpc include directory. The Torrent PMU support uses the
multiple PMU perf_events support. A single perf_events PMU type is
created to cover all of the various Torrent hardware PMUs. The generic
Torrent PMU type allows all of the specific Torrent hardware PMU events to
be included into a group of events within the perf_events Torrent PMU type.
The advantage of this model is that all of the events for the various
links are guaranteed to be measured at the same time, providing good
correlation between the activity on each of the different Torrent links.
This patch is a forward port of the initial Torrent PMU patch for 2.6.32
that was done as part of the development effort on Torrent and never pushed
upstream.
Signed-off-by: Carl Love <carll@us.ibm.com>
---
arch/powerpc/include/asm/cau_pmu.h | 39 +
arch/powerpc/include/asm/hvcall.h | 14 +
arch/powerpc/include/asm/mmu_pmu.h | 39 +
arch/powerpc/include/asm/power_torrent_events.h | 106 ++
arch/powerpc/include/asm/powerbus_bus_util_pmu.h | 39 +
arch/powerpc/include/asm/powerbus_ll_pmu.h | 39 +
arch/powerpc/include/asm/powerbus_mcd_pmu.h | 44 +
arch/powerpc/include/asm/powerbus_wxyz_pmu.h | 38 +
arch/powerpc/include/asm/torrent_nest_pmu.h | 192 +++
arch/powerpc/kernel/perf_event.c | 4 +
arch/powerpc/platforms/Makefile | 1 +
arch/powerpc/platforms/p7ih/Makefile | 10 +
arch/powerpc/platforms/p7ih/cau_pmu.c | 160 ++
arch/powerpc/platforms/p7ih/mmu_pmu.c | 156 ++
.../powerpc/platforms/p7ih/powerbus_bus_util_pmu.c | 410 +++++
arch/powerpc/platforms/p7ih/powerbus_ll_pmu.c | 273 ++++
arch/powerpc/platforms/p7ih/powerbus_mcd_pmu.c | 492 ++++++
arch/powerpc/platforms/p7ih/powerbus_wxyz_pmu.c | 244 +++
arch/powerpc/platforms/p7ih/torrent_pmu.c | 1582 ++++++++++++++++++++
arch/powerpc/platforms/pseries/Kconfig | 5 +
20 files changed, 3887 insertions(+), 0 deletions(-)
create mode 100644 arch/powerpc/include/asm/cau_pmu.h
create mode 100644 arch/powerpc/include/asm/mmu_pmu.h
create mode 100644 arch/powerpc/include/asm/power_torrent_events.h
create mode 100644 arch/powerpc/include/asm/powerbus_bus_util_pmu.h
create mode 100644 arch/powerpc/include/asm/powerbus_ll_pmu.h
create mode 100644 arch/powerpc/include/asm/powerbus_mcd_pmu.h
create mode 100644 arch/powerpc/include/asm/powerbus_wxyz_pmu.h
create mode 100644 arch/powerpc/include/asm/torrent_nest_pmu.h
create mode 100644 arch/powerpc/platforms/p7ih/Makefile
create mode 100644 arch/powerpc/platforms/p7ih/cau_pmu.c
create mode 100644 arch/powerpc/platforms/p7ih/mmu_pmu.c
create mode 100644 arch/powerpc/platforms/p7ih/powerbus_bus_util_pmu.c
create mode 100644 arch/powerpc/platforms/p7ih/powerbus_ll_pmu.c
create mode 100644 arch/powerpc/platforms/p7ih/powerbus_mcd_pmu.c
create mode 100644 arch/powerpc/platforms/p7ih/powerbus_wxyz_pmu.c
create mode 100644 arch/powerpc/platforms/p7ih/torrent_pmu.c
diff --git a/arch/powerpc/include/asm/cau_pmu.h b/arch/powerpc/include/asm/cau_pmu.h
new file mode 100644
index 0000000..1fc6f48
--- /dev/null
+++ b/arch/powerpc/include/asm/cau_pmu.h
@@ -0,0 +1,39 @@
+/*
+ * Torrent Performance Monitor
+ *
+ * Copyright Carl Love, Corey Ashford IBM Corporation 2010, 2011
+ *
+ * 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __ASM_CAU_PMU_H_
+#define __ASM_CAU_PMU_H_
+
+extern int cau_compute_pmc_reg(struct torrent_pmu_events *torrent_pmu_events,
+ struct hcall_data *hcall_write);
+extern int cau_pmu_check_constraints(struct torrent_pmu_events
+ *torrent_pmu_events,
+ struct unit_config *unit_cfg);
+extern void cau_enable_disable_hw_cntr(int op,
+ struct torrent_pmu_events
+ *torrent_pmu_events,
+ struct hcall_data *hcall_write);
+extern void cau_pmd_write(struct torrent_pmu_events *torrent_pmu_events,
+ struct hcall_data *hcall_read);
+extern int cau_pmd_read(struct torrent_pmu_events *torrent_pmu_events,
+ struct hcall_data *hcall_read);
+extern int cau_get_phys_pmd_reg(u64 event_code);
+
+#endif
diff --git a/arch/powerpc/include/asm/hvcall.h b/arch/powerpc/include/asm/hvcall.h
index fd8201d..2343636 100644
--- a/arch/powerpc/include/asm/hvcall.h
+++ b/arch/powerpc/include/asm/hvcall.h
@@ -238,6 +238,20 @@
#define H_GET_MPP_X 0x314
#define MAX_HCALL_OPCODE H_GET_MPP_X
+/* PHyp calls for P7IH */
+#define H_TOR_ACCESS_PMU_SCOM_REGS 0xF048ULL
+#define H_HFI_DUMP_INFO 0xF01CULL
+
+/* Dump request codes for the H_HFI_DUMP_INFO PHyp call */
+/* request code is in bits 32..47 */
+#define DUMP_REQUEST_SHIFT (63 - 47)
+#define DUMP_WINDOW_REGS (0x1ULL << DUMP_REQUEST_SHIFT)
+#define DUMP_NON_WINDOW_REGS (0x2ULL << DUMP_REQUEST_SHIFT)
+#define DUMP_BROADCAST_COUNTERS (0x3ULL << DUMP_REQUEST_SHIFT)
+#define DUMP_PERFORMANCE_COUNTERS (0x4ULL << DUMP_REQUEST_SHIFT)
+#define DUMP_PHYP_INTERNALS (0x5ULL << DUMP_REQUEST_SHIFT)
+#define DUMP_MMIO_PERFORMANCE_COUNTERS (0x6ULL << DUMP_REQUEST_SHIFT)
+
#ifndef __ASSEMBLY__
/**
diff --git a/arch/powerpc/include/asm/mmu_pmu.h b/arch/powerpc/include/asm/mmu_pmu.h
new file mode 100644
index 0000000..ad433ea
--- /dev/null
+++ b/arch/powerpc/include/asm/mmu_pmu.h
@@ -0,0 +1,39 @@
+/*
+ * Torrent Performance Monitor
+ *
+ * Copyright Carl Love, Corey Ashford IBM Corporation 2010, 2011
+ *
+ * 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __ASM_MMU_PMU_H_
+#define __ASM_MMU_PMU_H_
+
+extern int mmu_compute_pmc_reg(struct torrent_pmu_events *torrent_pmu_events,
+ struct hcall_data *hcall_write);
+extern int mmu_pmu_check_constraints(struct torrent_pmu_events
+ *torrent_pmu_events,
+ struct unit_config *unit_cfg);
+extern void mmu_enable_disable_hw_cntr(int op,
+ struct torrent_pmu_events
+ *torrent_pmu_events,
+ struct hcall_data *hcall_write);
+extern void mmu_pmd_write(struct torrent_pmu_events *torrent_pmu_events,
+ struct hcall_data *hcall_read);
+extern int mmu_pmd_read(struct torrent_pmu_events *torrent_pmu_events,
+ struct hcall_data *hcall_read);
+extern int mmu_get_phys_pmd_reg(u64 event_code);
+
+#endif
diff --git a/arch/powerpc/include/asm/power_torrent_events.h b/arch/powerpc/include/asm/power_torrent_events.h
new file mode 100644
index 0000000..7d71cab
--- /dev/null
+++ b/arch/powerpc/include/asm/power_torrent_events.h
@@ -0,0 +1,106 @@
+/*
+ * Torrent Performance Monitor
+ *
+ * Copyright Carl Love, Corey Ashford IBM Corporation 2010, 2011
+ *
+ * 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* Power Torrent PMU event codes */
+
+#ifndef __POWER_TORRENT_EVENTS_H__
+#define __POWER_TORRENT_EVENTS_H__
+
+/* PRELIMINARY EVENT ENCODING
+ * 0x0000_0000 - 0x00FF_FFFF = POWER core events
+ * 0x0100_0000 - 0x01FF_FFFF = Torrent events
+ * 0x0200_0000 - 0xFFFF_FFFF = reserved
+ * For Torrent events:
+ * Reserve encodings 0x0..0x00FF_FFFF for core POWER events.
+ * For Torrent events
+ * 0x00F0_0000 = Torrent PMU id
+ * 0x000F_0000 = PMU unit number (e.g. 0 for MCD0, 1 for MCD1)
+ * 0x0000_FF00 = virtual counter number (unused on MCD)
+ * 0x0000_00FF = PMC event selector mux value (unused on Util, MMU, CAU)
+ * (Note that some of these fields are wider than necessary)
+ *
+ * The upper bits 0xFFFF_FFFF_0000_0000 are reserved for attribute
+ * fields.
+ */
+
+#define PMU_SPACE_MASK 0xFF000000
+#define POWERPC_CORE_SPACE 0x00000000
+#define TORRENT_SPACE 0x01000000
+#define IS_CORE_EVENT(x) ((x & PMU_SPACE_MASK) == POWERPC_CORE_SPACE)
+#define IS_TORRENT_EVENT(x) ((x & PMU_SPACE_MASK) == TORRENT_SPACE)
+#define TORRENT_PMU_SHIFT 20
+#define TORRENT_PMU_MASK (0xF << TORRENT_PMU_SHIFT)
+#define TORRENT_PMU_GET(x) ((x & TORRENT_PMU_MASK) >> TORRENT_PMU_SHIFT)
+#define TORRENT_UNIT_SHIFT 16
+#define TORRENT_UNIT_MASK (0xF << TORRENT_UNIT_SHIFT)
+#define TORRENT_UNIT_GET(x) ((x & TORRENT_UNIT_MASK) >> TORRENT_UNIT_SHIFT)
+#define TORRENT_VIRT_CTR_SHIFT 8
+#define TORRENT_VIRT_CTR_MASK (0xFF << TORRENT_VIRT_CTR_SHIFT)
+#define TORRENT_VIRT_CTR_GET(x) ((x & TORRENT_VIRT_CTR_MASK) >> \
+ TORRENT_VIRT_CTR_SHIFT)
+#define TORRENT_MUX_SHIFT 0
+#define TORRENT_MUX_MASK 0xFF
+#define TORRENT_MUX_GET(x) ((x & TORRENT_MUX_MASK) >> TORRENT_MUX_SHIFT)
+
+#define TORRENT_ATTR_UTIL_SEL_SHIFT 32
+#define TORRENT_ATTR_UTIL_SEL_MASK (0x3ULL << TORRENT_ATTR_UTIL_SEL_SHIFT)
+#define TORRENT_ATTR_UTIL_CMP_SHIFT 34
+#define TORRENT_ATTR_UTIL_CMP_MASK (0x1FULL << TORRENT_ATTR_UTIL_CMP_SHIFT)
+
+#define TORRENT_PBUS_WXYZ_ID 0x0
+#define TORRENT_PBUS_LL_ID 0x1
+#define TORRENT_PBUS_MCD_ID 0x2
+#define TORRENT_PBUS_UTIL_ID 0x3
+#define TORRENT_MMU_ID 0x4
+#define TORRENT_CAU_ID 0x5
+
+#define TORRENT_LAST_PMU_ID (TORRENT_CAU_ID)
+#define TORRENT_NUM_PMU_TYPES (TORRENT_LAST_PMU_ID + 1)
+#define TORRENT_LAST_PBUS_PMU_ID (TORRENT_PBUS_UTIL_ID)
+#define TORRENT_NUM_PBUS_PMU_TYPES (TORRENT_LAST_PBUS_PMU_ID + 1)
+
+#define TORRENT_PMU(pmu) (TORRENT_SPACE | \
+ TORRENT_##pmu##_ID << TORRENT_PMU_SHIFT)
+
+#define TORRENT_PBUS_WXYZ TORRENT_PMU(PBUS_WXYZ)
+#define TORRENT_PBUS_LL TORRENT_PMU(PBUS_LL)
+#define TORRENT_PBUS_MCD TORRENT_PMU(PBUS_MCD)
+#define TORRENT_PBUS_UTIL TORRENT_PMU(PBUS_UTIL)
+#define TORRENT_MMU TORRENT_PMU(MMU)
+#define TORRENT_CAU TORRENT_PMU(CAU)
+
+#define COUNTER_W (0 << TORRENT_VIRT_CTR_SHIFT)
+#define COUNTER_X (1 << TORRENT_VIRT_CTR_SHIFT)
+#define COUNTER_Y (2 << TORRENT_VIRT_CTR_SHIFT)
+#define COUNTER_Z (3 << TORRENT_VIRT_CTR_SHIFT)
+#define COUNTER_LL0 (0 << TORRENT_VIRT_CTR_SHIFT)
+#define COUNTER_LL1 (1 << TORRENT_VIRT_CTR_SHIFT)
+#define COUNTER_LL2 (2 << TORRENT_VIRT_CTR_SHIFT)
+#define COUNTER_LL3 (3 << TORRENT_VIRT_CTR_SHIFT)
+#define COUNTER_LL4 (4 << TORRENT_VIRT_CTR_SHIFT)
+#define COUNTER_LL5 (5 << TORRENT_VIRT_CTR_SHIFT)
+#define COUNTER_LL6 (6 << TORRENT_VIRT_CTR_SHIFT)
+
+/* Attributes */
+
+#define TORRENT_ATTR_MCD_TYPE_SHIFT 32
+#define TORRENT_ATTR_MCD_TYPE_MASK (0x3ULL << TORRENT_ATTR_MCD_TYPE_SHIFT)
+
+#endif
diff --git a/arch/powerpc/include/asm/powerbus_bus_util_pmu.h b/arch/powerpc/include/asm/powerbus_bus_util_pmu.h
new file mode 100644
index 0000000..17a30d3
--- /dev/null
+++ b/arch/powerpc/include/asm/powerbus_bus_util_pmu.h
@@ -0,0 +1,39 @@
+/*
+ * Torrent Performance Monitor
+ *
+ * Copyright Carl Love, Corey Ashford IBM Corporation 2010, 2011
+ *
+ * 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __ASM_POWERBUS_BUS_UTIL_PMU_H_
+#define __ASM_POWERBUS_BUS_UTIL_PMU_H_
+
+extern int bus_util_compute_pmc_reg(
+ struct torrent_pmu_events *torrent_pmu_events,
+ struct hcall_data *hcall_write);
+extern int bus_util_pmu_check_constraints(
+ struct torrent_pmu_events *torrent_pmu_events,
+ struct unit_config *unit_cfg);
+extern void bus_util_enable_disable_hw_cntr(int op,
+ struct torrent_pmu_events *torrent_pmu_events,
+ struct hcall_data *hcall_write);
+extern void bus_util_pmd_write(struct torrent_pmu_events *torrent_pmu_events,
+ struct hcall_data *hcall_read);
+extern int bus_util_pmd_read(struct torrent_pmu_events *torrent_pmu_events,
+ struct hcall_data *hcall_read);
+extern int bus_util_get_phys_pmd_reg(u64 event_code);
+
+#endif
diff --git a/arch/powerpc/include/asm/powerbus_ll_pmu.h b/arch/powerpc/include/asm/powerbus_ll_pmu.h
new file mode 100644
index 0000000..a3d289d
--- /dev/null
+++ b/arch/powerpc/include/asm/powerbus_ll_pmu.h
@@ -0,0 +1,39 @@
+/*
+ * Torrent Performance Monitor
+ *
+ * Copyright Carl Love, Corey Ashford IBM Corporation 2010, 2011
+ *
+ * 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __ASM_POWERBUS_LL_PMU_H_
+#define __ASM_POWERBUS_LL_PMU_H_
+
+extern int ll_link_compute_pmc_reg(
+ struct torrent_pmu_events *torrent_pmu_events,
+ struct hcall_data *hcall_write);
+extern int ll_link_pmu_check_constraints(
+ struct torrent_pmu_events *torrent_pmu_events,
+ struct unit_config *unit_cfg);
+extern void ll_link_enable_disable_hw_cntr(int op,
+ struct torrent_pmu_events *torrent_pmu_events,
+ struct hcall_data *hcall_write);
+extern void ll_link_pmd_write(struct torrent_pmu_events *torrent_pmu_events,
+ struct hcall_data *hcall_read);
+extern int ll_link_pmd_read(struct torrent_pmu_events *torrent_pmu_events,
+ struct hcall_data *hcall_read);
+extern int ll_link_get_phys_pmd_reg(u64 event_code);
+
+#endif
diff --git a/arch/powerpc/include/asm/powerbus_mcd_pmu.h b/arch/powerpc/include/asm/powerbus_mcd_pmu.h
new file mode 100644
index 0000000..acd2fcd
--- /dev/null
+++ b/arch/powerpc/include/asm/powerbus_mcd_pmu.h
@@ -0,0 +1,44 @@
+/*
+ * Torrent Performance Monitor
+ *
+ * Copyright Carl Love, Corey Ashford IBM Corporation 2010, 2011
+ *
+ * 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __ASM_POWERBUS_MCD_PMU_H_
+#define __ASM_POWERBUS_MCD_PMU_H_
+
+extern int mcd_compute_pmc_reg(struct torrent_pmu_events *torrent_pmu_events,
+ struct hcall_data *hcall_write);
+extern int mcd_pmu_check_constraints(
+ struct torrent_pmu_events *torrent_pmu_events,
+ struct unit_config *unit_cfg);
+extern void mcd_enable_disable_hw_cntr(int op,
+ struct torrent_pmu_events *torrent_pmu_events,
+ struct hcall_data *hcall_write);
+extern void mcd_pmd_write(struct torrent_pmu_events *torrent_pmu_events,
+ struct hcall_data *hcall_read);
+extern int mcd_pmd_read(struct torrent_pmu_events *torrent_pmu_events,
+ struct hcall_data *hcall_read);
+extern int mcd_get_phys_pmd_reg(u64 event_code);
+
+/* The following is the shift/mask for the physical register location */
+#define TORRENT_REG_ATTR_MCD_TYPE_SHIFT 61
+
+#define TORRENT_REG_ATTR_MCD_TYPE_MASK (0x3ULL << \
+ TORRENT_REG_ATTR_MCD_TYPE_SHIFT)
+
+#endif
diff --git a/arch/powerpc/include/asm/powerbus_wxyz_pmu.h b/arch/powerpc/include/asm/powerbus_wxyz_pmu.h
new file mode 100644
index 0000000..198ca71
--- /dev/null
+++ b/arch/powerpc/include/asm/powerbus_wxyz_pmu.h
@@ -0,0 +1,38 @@
+/*
+ * Torrent Performance Monitor
+ *
+ * Copyright Carl Love, Corey Ashford IBM Corporation 2010, 2011
+ *
+ * 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __ASM_POWERBUS_WXYZ_PMU_H_
+#define __ASM_POWERBUS_WXYZ_PMU_H_
+
+extern int wxyz_link_compute_pmc_reg(struct torrent_pmu_events *pmu_events,
+ struct hcall_data *hcall_write);
+extern int wxyz_link_pmu_check_constraints(
+ struct torrent_pmu_events *pmu_events,
+ struct unit_config *unit_cfg);
+extern void wxyz_link_enable_disable_hw_cntr(int op,
+ struct torrent_pmu_events *pmu_events,
+ struct hcall_data *hcall_write);
+extern void wxyz_link_pmd_write(struct torrent_pmu_events *pmu_events,
+ struct hcall_data *hcall_read);
+extern int wxyz_link_pmd_read(struct torrent_pmu_events *pmu_events,
+ struct hcall_data *hcall_read);
+extern int wxyz_link_get_phys_pmd_reg(u64 event_code);
+
+#endif
diff --git a/arch/powerpc/include/asm/torrent_nest_pmu.h b/arch/powerpc/include/asm/torrent_nest_pmu.h
new file mode 100644
index 0000000..88339cf
--- /dev/null
+++ b/arch/powerpc/include/asm/torrent_nest_pmu.h
@@ -0,0 +1,192 @@
+/*
+ * Torrent Performance Monitor
+ *
+ * Copyright Carl Love, Corey Ashford IBM Corporation 2010, 2011
+ *
+ * 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __ASM_POWERPC_TORRENT_PMU_H_
+#define __ASM_POWERPC_TORRENT_PMU_H_
+
+#include <asm/power_torrent_events.h>
+
+/* Currently, partitions never span octants, which means that exactly one
+ * Torrent chip is visible from each partition.
+ */
+#define MAX_TORRENT_CHIPS 1
+
+/* Max number of counters in each PMU type */
+#define MAX_CNTRS_PER_WXYZ_LINK_PMU 4
+#define MAX_CNTRS_PER_LL_LINK_PMU 7
+#define MAX_CNTRS_PER_BUS_UTIL_PMU 12
+#define MAX_CNTRS_PER_MCD_PMU 4
+#define MAX_CNTRS_PER_MMU_PMU 4
+#define MAX_CNTRS_PER_CAU_PMU 1
+
+/* Overall max of counters across all PMU types */
+#define MAX_CNTRS_TORRENT_PMUS 12
+
+/* Torrent HCall defines */
+#define H_WRITE_TORRENT_PMU_SCOM_REGS 0
+#define H_READ_TORRENT_PMU_SCOM_REGS 1
+
+/* Torrent PowerBus physical register */
+#define PBE_LINKS_TRACE 0
+#define PBE_LINKS_CTRS 1
+#define PBL_LINKS_TRACE_PERF_CTR_CFG 2
+#define PBL_LINKS_CTRS_1 3
+#define PBL_LINKS_CTRS_2 4
+#define PBUS_REG21_PERF_CTR 5
+#define PBUS_REG22_PERF_CTR 6
+#define PBUS_REG23_PERF_CTR 7
+#define PBUS_REG24_PERF_CTR 8
+#define PBUS_REG25_PERF_CTR 9
+#define PBUS_REG26_PERF_CTR 10
+#define PBUS_MCD_PERF_CTRS_CFG 11
+#define PBUS_MCD_PERF_CTRS_SEL 12
+#define PBUS_MCD_PERF_CTRS 13
+#define MAX_HCALL_REGS 14
+
+/* Hcall token IDs and argument array */
+#define PBUS_HCALL_READ 0
+#define PBUS_HCALL_WRITE 1
+#define PBUS_HCALL_LOCK 2
+#define PBUS_HCALL_UNLOCK 3
+
+/* counter use flags */
+#define CNTR_FREE 0
+#define CNTR_ALLOCATED 1 /* event assigned to physical cntr
+ * but not being counted yet.
+ */
+#define CNTR_IN_USE 2 /* event is being counted on its
+ * assigned physical counter.
+ */
+
+/* These two structures are defined by PHyp */
+struct tor_pmu_reg {
+ u64 regId;
+ u64 regValue;
+};
+
+struct dump_mmio_perf_counters {
+ /* CAU */
+ u64 cycles_waiting_on_a_credit;
+ /* MMU */
+ u64 G_MMCHIT;
+ u64 G_MMCMIS;
+ u64 G_MMATHIT;
+ u64 G_MMATMIS;
+};
+
+/* Torrent Hcall data structs */
+#define MAX_HCALL_WRITE_ARGS 4
+#define MAX_HCALL_READ_ARGS 8
+struct hcall_data {
+ int num_regs; /* number of physical registers to read */
+
+ /* Map each Torrent PMU register number to a request array index
+ * in the Hcall memory buffer, if there is currently is one for
+ * that register, and if not the mapped value is -1.
+ */
+ int request_idx[MAX_HCALL_REGS];
+
+ /* Which request index has the virtual PMD value for the
+ * counter.
+ */
+ int virt_cntr_to_request_idx
+ [TORRENT_NUM_PMU_TYPES][MAX_CNTRS_TORRENT_PMUS];
+
+ struct tor_pmu_reg tor_pmu_reg[MAX_HCALL_REGS];
+
+ int do_dump_mmio_perf_counters;
+ struct dump_mmio_perf_counters mmio_perf_counters;
+};
+
+#define MAX_TORRENT_PMC_REGS 2
+#define MAX_TORRENT_PMD_REGS 8 /* Maximum number for all PMUs */
+
+#define BUS_UTIL_CNTR_SEL_AVAIL 0xFFFFFFFF /* code for sel field not in use */
+struct unit_config {
+ u8 cntr_state[MAX_CNTRS_TORRENT_PMUS];
+ /* Place any needed PMU-specific config values here
+ * as a union of structs.
+ */
+ u64 mcd_cntr_attr;
+ u64 bus_util_enable_mask;
+ u64 bus_util_cntr_sel;
+};
+
+/* This structure tracks the events being counted and being added for an
+ * individual HW PMU.
+ */
+struct torrent_pmu_events {
+ int max_events; /* max physical counters in this PMU */
+ struct unit_config unit; /* PMU configuration array */
+
+ struct perf_event *event[MAX_CNTRS_TORRENT_PMUS];
+ int n_events; /* number of Torrent events being counted */
+ int n_add; /* number of Torrent events being added.
+ */
+ int update; /* boolean - The number of events has changed
+ * so the PMU hardware needs to be updated.
+ */
+ int disabled;
+
+ /* The rest of the entries are only used by PMUs whose registers are
+ * accessed via the Hcall.
+ */
+ u64 shadow_pmc_reg[MAX_TORRENT_PMC_REGS];
+};
+
+/* The Torrent PMU structure tracks the PMC value written to the HW register so
+ * it is not necessary to read the physical register when updating the value.
+ * This is done to minimize the number of hypervisor and SCOM register reads
+ * that are needed.
+ */
+struct torrent_pmu_counters {
+ int (*check_constraints)(struct torrent_pmu_events *torrent_pmu_events,
+ struct unit_config *unit_cfg);
+ int (*compute_pmc_regs)(struct torrent_pmu_events *torrent_pmu_events,
+ struct hcall_data *hcall_write);
+ void (*enable_disable_cntr)(int op,
+ struct torrent_pmu_events *torrent_pmu_events,
+ struct hcall_data *hcall_write);
+ int (*read_pmd)(struct torrent_pmu_events *torrent_pmu_events,
+ struct hcall_data *hcall_read);
+ void (*write_pmd)(struct torrent_pmu_events *torrent_pmu_events,
+ struct hcall_data *hcall_read);
+ int (*get_phys_pmd_reg)(u64 event_code);
+};
+
+/* This is the max number of instances of each type of Torrent PMU. Currently,
+ * all supported PMUs have a single instance. Supporting HFI non-windowed
+ * counters would require changing this to 2.
+ */
+#define MAX_TORRENT_PMUS_PER_TYPE 1
+
+struct torrent_events {
+ int count[TORRENT_NUM_PMU_TYPES][MAX_TORRENT_PMUS_PER_TYPE];
+ int max_count[TORRENT_NUM_PMU_TYPES];
+ struct torrent_pmu_counters
+ cntrs[MAX_TORRENT_CHIPS][TORRENT_NUM_PMU_TYPES];
+};
+
+extern void torrent_pmu_enable(struct pmu *pmu);
+extern void torrent_pmu_disable(struct pmu *pmu);
+extern void record_hcall_request_idx(int phys_register_index,
+ struct hcall_data *hcall_request,
+ u64 shadow_reg_index);
+#endif
diff --git a/arch/powerpc/kernel/perf_event.c b/arch/powerpc/kernel/perf_event.c
index 822f630..aa1eb43 100644
--- a/arch/powerpc/kernel/perf_event.c
+++ b/arch/powerpc/kernel/perf_event.c
@@ -19,6 +19,8 @@
#include <asm/firmware.h>
#include <asm/ptrace.h>
+#include <asm/torrent_nest_pmu.h>
+
struct cpu_hw_events {
int n_events;
int n_percpu;
@@ -1092,6 +1094,8 @@ static int power_pmu_event_init(struct perf_event *event)
break;
case PERF_TYPE_RAW:
ev = event->attr.config;
+ if (!IS_CORE_EVENT(ev))
+ return -ENOENT;
break;
default:
return -ENOENT;
diff --git a/arch/powerpc/platforms/Makefile b/arch/powerpc/platforms/Makefile
index 73e2116..d5db464 100644
--- a/arch/powerpc/platforms/Makefile
+++ b/arch/powerpc/platforms/Makefile
@@ -23,3 +23,4 @@ obj-$(CONFIG_PPC_PS3) += ps3/
obj-$(CONFIG_EMBEDDED6xx) += embedded6xx/
obj-$(CONFIG_AMIGAONE) += amigaone/
obj-$(CONFIG_PPC_WSP) += wsp/
+obj-$(CONFIG_PPC_P7IH) += p7ih/
diff --git a/arch/powerpc/platforms/p7ih/Makefile b/arch/powerpc/platforms/p7ih/Makefile
new file mode 100644
index 0000000..5b7b8db
--- /dev/null
+++ b/arch/powerpc/platforms/p7ih/Makefile
@@ -0,0 +1,10 @@
+
+subdir-ccflags-$(CONFIG_PPC_WERROR) := -Werror
+
+obj-$(CONFIG_PPC_P7IH) += mmu_pmu.o powerbus_bus_util_pmu.o \
+ powerbus_ll_pmu.o \
+ powerbus_mcd_pmu.o \
+ powerbus_wxyz_pmu.o \
+ mmu_pmu.o \
+ cau_pmu.o \
+ torrent_pmu.o
diff --git a/arch/powerpc/platforms/p7ih/cau_pmu.c b/arch/powerpc/platforms/p7ih/cau_pmu.c
new file mode 100644
index 0000000..3f70298
--- /dev/null
+++ b/arch/powerpc/platforms/p7ih/cau_pmu.c
@@ -0,0 +1,160 @@
+/*
+ * Performance counter support for IBM Torrent interconnect chip
+ *
+ * Copyright 2010, 2011 Carl Love, Corey Ashford, IBM Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/perf_event.h>
+#include <asm/torrent_nest_pmu.h>
+
+
+int cau_pmu_check_constraints(struct torrent_pmu_events *pmu_events,
+ struct unit_config *unit_cfg)
+{
+ int total_events, i;
+ u8 virt_cntr;
+ u64 event_code;
+
+ total_events = pmu_events->n_events + pmu_events->n_add;
+ for (i = pmu_events->n_events; i < total_events; i++) {
+ /*
+ * Get the event code from attr.config value as the hw.config
+ * field is not set yet since the event has not been added.
+ */
+ event_code = pmu_events->event[i]->attr.config;
+ virt_cntr = (u8)TORRENT_VIRT_CTR_GET(event_code);
+ if (virt_cntr != 0) {
+ pr_err("%s:%d, CAU virt_cntr is %d instead of 0\n",
+ __func__, __LINE__, virt_cntr);
+ return -1;
+ }
+ if (unit_cfg->cntr_state[virt_cntr] != CNTR_FREE)
+ return -1;
+ unit_cfg->cntr_state[virt_cntr] = CNTR_IN_USE;
+ }
+ return 0;
+}
+
+int cau_compute_pmc_reg(struct torrent_pmu_events *pmu_events,
+ struct hcall_data *hcall_write)
+{
+ int i, total_events;
+ u8 virt_cntr;
+ u64 event_code;
+
+ /*
+ *There are no control registers in the MMU PMU. Just
+ * assign the hw.idx field.
+ */
+ total_events = pmu_events->n_events + pmu_events->n_add;
+ for (i = 0; i < total_events; i++) {
+ event_code = pmu_events->event[i]->hw.config;
+ virt_cntr = (u8)TORRENT_VIRT_CTR_GET(event_code);
+ if (virt_cntr != 0) {
+ pr_err("%s:%d, CAU virt_cntr is %u instead of 0\n",
+ __func__, __LINE__, virt_cntr);
+ return -1;
+ }
+ pmu_events->event[i]->hw.idx = virt_cntr;
+ }
+ return 0;
+}
+
+void cau_enable_disable_hw_cntr(int op, struct torrent_pmu_events *pmu_events)
+{
+ /*
+ * The MMU performance counters are free running, and can't be
+ * disabled or enabled.
+ *
+ * Function is needed as it is an argument to the function
+ * torrent_pmu_initialize() which is used to initialize each of the
+ * physical PMUs in the Torrent chip.
+ */
+}
+
+void cau_pmd_write(struct torrent_pmu_events *pmu_events,
+ struct hcall_data *hcall_read)
+{
+ int i;
+ u8 virt_cntr;
+ u64 value, event_code;
+
+ for (i = 0; i < pmu_events->n_events; i++) {
+ event_code = pmu_events->event[i]->hw.config;
+ virt_cntr = (u8)TORRENT_VIRT_CTR_GET(event_code);
+ /* CAU PMU has just a single counter */
+ if (virt_cntr != 0) {
+ pr_err("%s:%d, CAU virt_cntr is %u instead of 0\n",
+ __func__, __LINE__, virt_cntr);
+ return;
+ }
+ if (pmu_events->unit.cntr_state[virt_cntr] != CNTR_FREE) {
+ /*
+ * The counters are stopped. The HCall to
+ * read the counters has been done. The
+ * current count must be set to the previous
+ * count. The counters will be enabled.
+ * When the counters are stopped and read
+ * again, the actual count will be the
+ * new count minus the previous count.
+ */
+ value = hcall_read->
+ mmio_perf_counters.cycles_waiting_on_a_credit;
+ local64_set(&pmu_events->event[i]->hw.prev_count,
+ value);
+ }
+ }
+}
+
+int cau_pmd_read(struct torrent_pmu_events *pmu_events,
+ struct hcall_data *hcall_read)
+{
+ /*
+ * This PMU is accessed via hypervisor calls. The HCall to read the
+ * counters has been done. The counter data is in hcall_read.
+ */
+ int i;
+ u8 virt_cntr;
+ u64 value, prev, delta;
+ struct perf_event *event;
+
+ for (i = 0; i < pmu_events->n_events; i++) {
+ event = pmu_events->event[i];
+ virt_cntr = (u8)TORRENT_VIRT_CTR_GET(event->hw.config);
+ /* CAU PMU has just a single counter */
+ if (virt_cntr != 0) {
+ pr_err("%s:%d, CAU virt_cntr is %d instead of 0\n",
+ __func__, __LINE__, virt_cntr);
+ return -1;
+ }
+ if (pmu_events->unit.cntr_state[virt_cntr] == CNTR_IN_USE) {
+ /*
+ *Get the initial value, which was saved by the pmd
+ * write call into prev_count.
+ */
+ prev = local64_read(
+ &event->hw.prev_count);
+
+ value = hcall_read->
+ mmio_perf_counters.cycles_waiting_on_a_credit;
+
+ /* calculate/save the count */
+ delta = value - prev;
+ local64_add(delta, &event->count);
+ local64_set(&event->hw.prev_count, value);
+ }
+ }
+ return 0;
+}
+
+int cau_get_phys_pmd_reg(u64 event_code)
+{
+ /* This function never should be called */
+ return -1;
+}
diff --git a/arch/powerpc/platforms/p7ih/mmu_pmu.c b/arch/powerpc/platforms/p7ih/mmu_pmu.c
new file mode 100644
index 0000000..4d6d5c2
--- /dev/null
+++ b/arch/powerpc/platforms/p7ih/mmu_pmu.c
@@ -0,0 +1,156 @@
+/*
+ * Performance counter support for IBM Torrent interconnect chip
+ *
+ * Copyright 2010, 2011 Carl Love, Corey Ashford, IBM Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/perf_event.h>
+#include <asm/torrent_nest_pmu.h>
+
+u64 get_mmu_counter_value(struct hcall_data *hcall_read, u8 virt_cntr)
+{
+ switch (virt_cntr) {
+ case 0:
+ return hcall_read->mmio_perf_counters.G_MMCHIT;
+ case 1:
+ return hcall_read->mmio_perf_counters.G_MMCMIS;
+ case 2:
+ return hcall_read->mmio_perf_counters.G_MMATHIT;
+ case 3:
+ return hcall_read->mmio_perf_counters.G_MMATMIS;
+ default:
+ pr_err("MMU event code has illegal virt counter field: %u",
+ virt_cntr);
+ return 0;
+ }
+}
+
+int mmu_pmu_check_constraints(struct torrent_pmu_events *pmu_events,
+ struct unit_config *unit_cfg)
+{
+ int total_events, i;
+ u8 virt_cntr;
+ u64 event_code;
+
+ total_events = pmu_events->n_events + pmu_events->n_add;
+ for (i = pmu_events->n_events; i < total_events; i++) {
+ /*
+ * Get the event code from attr.config value as the hw.config
+ * field is not set yet since the event has not been added.
+ */
+ event_code = pmu_events->event[i]->attr.config;
+ virt_cntr = (u8)TORRENT_VIRT_CTR_GET(event_code);
+
+ if (unit_cfg->cntr_state[virt_cntr] == CNTR_IN_USE)
+ return -1;
+
+ unit_cfg->cntr_state[virt_cntr] = CNTR_IN_USE;
+ }
+ return 0;
+}
+
+int mmu_compute_pmc_reg(struct torrent_pmu_events *pmu_events,
+ struct hcall_data *hcall_write)
+{
+ int i, total_events;
+ u8 virt_cntr;
+ u64 event_code;
+
+ /*
+ * There are no control registers in the MMU PMU. Just
+ * assign the hw.idx field.
+ */
+ total_events = pmu_events->n_events + pmu_events->n_add;
+ for (i = 0; i < total_events; i++) {
+ event_code = pmu_events->event[i]->hw.config;
+ virt_cntr = (u8)TORRENT_VIRT_CTR_GET(event_code);
+ pmu_events->event[i]->hw.idx = virt_cntr;
+ }
+ return 0;
+}
+
+void mmu_enable_disable_hw_cntr(int op, struct torrent_pmu_events *pmu_events)
+{
+ /*
+ * The MMU performance counters are free running, and can't be
+ * disabled or enabled.
+ *
+ * Function is needed as it is an argument to the function
+ * torrent_pmu_initialize() which is used to initialize each of the
+ * physical PMUs in the Torrent chip.
+ */
+}
+
+void mmu_pmd_write(struct torrent_pmu_events *pmu_events,
+ struct hcall_data *hcall_read)
+{
+ int i;
+ u8 virt_cntr;
+ u64 value, event_code;
+
+ for (i = 0; i < pmu_events->n_events; i++) {
+ event_code = pmu_events->event[i]->hw.config;
+ virt_cntr = (u8)TORRENT_VIRT_CTR_GET(event_code);
+
+ if (pmu_events->unit.cntr_state[virt_cntr] == CNTR_IN_USE) {
+ /*
+ * The counters are stopped. The HCall to
+ * read the counters has been done. The
+ * current count must be set to the previous
+ * count. The counters will be enabled.
+ * When the counters are stopped and read
+ * again, the actual count will be the
+ * new count minus the previous count.
+ */
+ value = get_mmu_counter_value(hcall_read, virt_cntr);
+ local64_set(&pmu_events->event[i]->hw.prev_count,
+ value);
+ }
+ }
+}
+
+int mmu_pmd_read(struct torrent_pmu_events *pmu_events,
+ struct hcall_data *hcall_read)
+{
+ /*
+ * This PMU is accessed via hypervisor calls. The HCall to read the
+ * counters has been done. The counter data is in hcall_read.
+ */
+ int i;
+ u8 virt_cntr;
+ u64 value, prev, delta;
+ struct perf_event *event;
+
+ for (i = 0; i < pmu_events->n_events; i++) {
+ event = pmu_events->event[i];
+ virt_cntr = (u8)TORRENT_VIRT_CTR_GET(event->hw.config);
+
+ if (pmu_events->unit.cntr_state[virt_cntr] == CNTR_IN_USE) {
+ /*
+ * Get the initial value, which was saved by the pmd
+ * write call into prev_count.
+ */
+ prev = local64_read(&event->hw.prev_count);
+
+ value = get_mmu_counter_value(hcall_read, virt_cntr);
+
+ /* calculate/save the count */
+ delta = value - prev;
+ local64_add(delta, &event->count);
+ local64_set(&event->hw.prev_count, value);
+ }
+ }
+ return 0;
+}
+
+int mmu_get_phys_pmd_reg(u64 event_code)
+{
+ /* This function never should be called */
+ return -1;
+}
diff --git a/arch/powerpc/platforms/p7ih/powerbus_bus_util_pmu.c b/arch/powerpc/platforms/p7ih/powerbus_bus_util_pmu.c
new file mode 100644
index 0000000..213c500
--- /dev/null
+++ b/arch/powerpc/platforms/p7ih/powerbus_bus_util_pmu.c
@@ -0,0 +1,410 @@
+/*
+ * Performance counter support for IBM Torrent interconnect chip
+ *
+ * Copyright 2010, 2011 Carl Love, Corey Ashford, IBM Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/perf_event.h>
+#include <asm/torrent_nest_pmu.h>
+#include <asm/powerbus_mcd_pmu.h>
+#include <asm/power_torrent_events.h>
+
+#define BUS_UTIL_PMC_FIELD_WIDTH 8
+#define NUM_BUS_UTIL_PMC_REGS 4
+#define BUS_UTIL_CNTR_SIZE 16
+#define BUS_UTIL_OPCODE_MASK 0xFF
+#define NUM_BUS_UTIL_COUNTERS 4
+
+#define BUS_UTIL_APM_ENABLE_MASK 0x8000000000000000UL
+#define BUS_UTIL_PMU_ENABLE_MASK 0x1000000000000000UL
+#define BUS_UTIL_16_BIT_CNTR 16
+#define BUS_UTIL_32_BIT_CNTR 32
+#define BUS_UTIL_16_BIT_CNTR_MASK 0xFFFF
+#define BUS_UTIL_32_BIT_CNTR_MASK 0xFFFFFFFF
+#define BUS_UTIL_SHADOW_REG21 0
+#define BUS_UTIL_SHADOW_REG22 1
+#define BUS_UTIL_SHADOW_REG23 2
+#define BUS_UTIL_SHADOW_REG24 3
+#define BUS_UTIL_SHADOW_REG25 4
+#define BUS_UTIL_SHADOW_REG26 5
+#define BUS_UTIL_REG21_SEL_BASE (63 - 2)
+#define BUS_UTIL_HI_COMP_BASE (63 - 10)
+#define BUS_UTIL_LO_COMP_BASE (63 - 15)
+
+/*
+ * Bus utilization counters consist of a couple of configurable counters and
+ * a set of 10 dedicated counters. Register 21 has two enable bits:
+ * pb_cfg_apm_en and pb_cfg_pmucnt_en. Only one of these bits can be set at
+ * a time. The pb_cfg_apm_en enables the two 16-bit counters to count event.
+ * The pb_cfg_pmucnt_en enable is used to enable the counters to count the
+ * events specified in the pb_cfg_pmucnt_sel field. The select field options
+ * are:
+ * 00 - rcmd0 (reflected command 0)
+ * 01 - rcmd1 (reflected command 1)
+ * 1x - rcmd0 or rcmd 1
+ *
+ * The counters in registers 22 to 26 are enabled if pb_cfg_apm_en OR
+ * pb_cfg_pmucnt_en are enabled.
+ */
+
+int virtual_cntr_to_phys_reg(u8 virt_cntr)
+{
+ int phys_reg_num;
+
+ /*
+ * Map event to the physical hardware register that has the counter to
+ * count the specified event.
+ */
+
+ switch (virt_cntr) {
+ case 0:
+ case 1:
+ phys_reg_num = PBUS_REG21_PERF_CTR;
+ break;
+
+ case 2:
+ case 3:
+ phys_reg_num = PBUS_REG22_PERF_CTR;
+ break;
+
+ case 4:
+ case 5:
+ phys_reg_num = PBUS_REG23_PERF_CTR;
+ break;
+
+ case 6:
+ case 7:
+ phys_reg_num = PBUS_REG24_PERF_CTR;
+ break;
+
+ case 8:
+ case 9:
+ phys_reg_num = PBUS_REG25_PERF_CTR;
+ break;
+
+ case 10:
+ case 11:
+ phys_reg_num = PBUS_REG26_PERF_CTR;
+ break;
+
+ default:
+ pr_err("%s, %d ERROR not able to map virt counter to physical register\n",
+ __func__, __LINE__);
+ phys_reg_num = -1;
+ break;
+ }
+ return phys_reg_num;
+}
+
+int virt_cntr_to_shadow_reg(u8 virt_cntr)
+{
+ int phys_reg;
+
+ /*
+ * Map the virtual counter that counts the specified event to the
+ * physical register that contains the counter for that event.
+ *
+ * virtual counters 0, 1 are in BUS_UTIL_SHADOW_REG21 (index 0)
+ * virtual counters 2, 3 are in BUS_UTIL_SHADOW_REG22
+ * virtual counters 4, 5 are in BUS_UTIL_SHADOW_REG23
+ * virtual counters 6, 7 are in BUS_UTIL_SHADOW_REG24
+ * virtual counters 8, 9 are in BUS_UTIL_SHADOW_REG25
+ * virtual counters 10, 11 are in BUS_UTIL_SHADOW_REG26 (index 5)
+ */
+
+ if (virt_cntr < 12) {
+ phys_reg = virt_cntr >> 1; /* just divide virt counter by 2 */
+ } else {
+ pr_err("%s, %d ERROR not able to map virtual counter to physical register number..\n",
+ __func__, __LINE__);
+ phys_reg = -1;
+ }
+ return phys_reg;
+}
+
+int bus_util_pmu_check_constraints(struct torrent_pmu_events *pmu_events,
+ struct unit_config *unit_cfg)
+{
+ int i, total_events;
+ u8 virt_cntr;
+ u64 event_code, sel, enable_mask;
+
+ total_events = pmu_events->n_events + pmu_events->n_add;
+
+ for (i = pmu_events->n_events; i < total_events; i++) {
+ if (pmu_events->unit.cntr_state[i] != CNTR_FREE)
+ /*
+ * Each event has an assigned counter that is used to
+ * count the event. If the counter is already in use,
+ * the constraint check fails.
+ */
+ return -1;
+
+ /*
+ * Get the event code from attr.config value as the hw.config
+ * field is not set yet since the event has not been added.
+ */
+ event_code = pmu_events->event[i]->attr.config;
+ virt_cntr = TORRENT_VIRT_CTR_GET(event_code);
+
+ if ((virt_cntr >= 0) && (virt_cntr <= 3)) {
+ /* Check constraints for the reg21 and reg22
+ * registers.
+ *
+ * The sel field is shared by counters 0 through 3.
+ * The enable bits define which set of events the
+ * counters can count.
+ */
+ if (TORRENT_MUX_GET(event_code) == 0)
+ /* APM event */
+ enable_mask = BUS_UTIL_APM_ENABLE_MASK;
+ else
+ /* PMU event */
+ enable_mask = BUS_UTIL_PMU_ENABLE_MASK;
+
+ if (unit_cfg->bus_util_enable_mask == 0)
+ /* PMU configuration is not set yet */
+ unit_cfg->bus_util_enable_mask = enable_mask;
+
+ else
+ if (unit_cfg->bus_util_enable_mask
+ != enable_mask)
+ /* event and PMU config conflict */
+ return -1;
+
+ sel = TORRENT_ATTR_UTIL_SEL_MASK & event_code;
+
+ if (unit_cfg->bus_util_cntr_sel
+ == BUS_UTIL_CNTR_SEL_AVAIL)
+ unit_cfg->bus_util_cntr_sel = sel;
+ else if (unit_cfg->bus_util_cntr_sel != sel)
+ /*
+ * PMU sel configuration and event sel
+ * value are not compatible.
+ */
+ return -1;
+ }
+
+ /*
+ * Assign the event to the counter but it has not been
+ * enabled yet.
+ */
+ unit_cfg->cntr_state[virt_cntr] = CNTR_ALLOCATED;
+ }
+ return 0;
+}
+
+int bus_util_compute_pmc_reg(struct torrent_pmu_events *pmu_events,
+ struct hcall_data *hcall_write)
+{
+
+ int i, phys_reg, total_events, shift;
+ u8 virt_cntr;
+ u64 event_code, sel, comp;
+
+ /*
+ * The assumption is that we have passed the constraint test before
+ * this routine is called so we know that for the counter configuration
+ * (two or four counters) there are enough counters available for the
+ * new events.
+ */
+
+ total_events = pmu_events->n_events + pmu_events->n_add;
+
+ for (i = 0; i < total_events; i++) {
+ event_code = pmu_events->event[i]->hw.config;
+ virt_cntr = TORRENT_VIRT_CTR_GET(event_code);
+
+ pmu_events->event[i]->hw.idx = virt_cntr;
+
+ phys_reg = virtual_cntr_to_phys_reg(virt_cntr);
+ if (phys_reg == -1)
+ return -1; /* could not map event to phys_reg */
+
+ /*
+ * Assign the event to the counter but it has not been
+ * enabled yet.
+ */
+ pmu_events->unit.cntr_state[virt_cntr] = CNTR_ALLOCATED;
+
+ if (phys_reg >= PBUS_REG23_PERF_CTR)
+ /* no additional configuration information to write */
+ return 0;
+
+ /* set the sel and comp fields */
+ shift = 63 + TORRENT_ATTR_UTIL_SEL_SHIFT
+ - BUS_UTIL_REG21_SEL_BASE;
+ pmu_events->shadow_pmc_reg[virt_cntr] &=
+ ~(TORRENT_ATTR_UTIL_SEL_MASK << shift);
+
+ sel = (pmu_events->unit.bus_util_cntr_sel) << shift;
+
+ comp = event_code && TORRENT_ATTR_UTIL_CMP_MASK;
+ switch (virt_cntr) {
+ case 0:
+ case 2:
+ shift = 63 + TORRENT_ATTR_UTIL_CMP_SHIFT
+ - BUS_UTIL_HI_COMP_BASE;
+ break;
+
+ case 1:
+ case 3:
+ shift = 63 + TORRENT_ATTR_UTIL_CMP_SHIFT
+ - BUS_UTIL_LO_COMP_BASE;
+ break;
+
+ default:
+ pr_err("%s, ERROR, virtual counter value not in expected range. virt_cntr = %u\n",
+ __func__, virt_cntr);
+ return -1;
+ break;
+
+ }
+ comp = comp << shift;
+ pmu_events->shadow_pmc_reg[virt_cntr]
+ &= ~(TORRENT_ATTR_UTIL_CMP_MASK << shift);
+
+ pmu_events->shadow_pmc_reg[virt_cntr] |= comp
+ || pmu_events->unit.bus_util_cntr_sel;
+
+ /* Configuration register needs to be written */
+ record_hcall_request_idx(phys_reg, hcall_write,
+ pmu_events->shadow_pmc_reg[virt_cntr]);
+ }
+ return 0;
+}
+
+void bus_util_enable_disable_hw_cntr(int op,
+ struct torrent_pmu_events *pmu_events,
+ struct hcall_data *hcall_write)
+{
+ int index, cntr_num, phys_reg;
+ u8 virt_cntr;
+ u64 enable_bit;
+
+ for (cntr_num = 0; cntr_num < pmu_events->max_events;
+ cntr_num++) {
+ if (pmu_events->unit.cntr_state[cntr_num] != CNTR_FREE) {
+ /*
+ * Which enable bit to use is based on what events
+ * the first two counters are counting. The compute
+ * function will set the needed enable bit based on
+ * the events being counted. The physical register
+ * for virtual counter 0 contains the enable bits used
+ * by all of the counters.
+ */
+ virt_cntr = 0;
+ if (pmu_events->unit.bus_util_enable_mask == 0)
+ /*
+ * No REG21 events are configured, set the
+ * enable to APM enable to enable the counters
+ * for the other registers.
+ */
+ enable_bit = BUS_UTIL_APM_ENABLE_MASK;
+ else
+ enable_bit = pmu_events->
+ unit.bus_util_enable_mask;
+
+ phys_reg = virtual_cntr_to_phys_reg(virt_cntr);
+ index = virt_cntr_to_shadow_reg(virt_cntr);
+
+ /*
+ * The enable bits for the first two counters are used
+ * to control all of the counters.
+ */
+ if (op) {
+ pmu_events->shadow_pmc_reg[index] |=
+ enable_bit;
+ pmu_events->unit.cntr_state[cntr_num] =
+ CNTR_IN_USE;
+ } else if (pmu_events->unit.cntr_state[cntr_num]
+ == CNTR_IN_USE)
+ /*
+ * Disable only if the counter is actually
+ * in use.
+ */
+ pmu_events->shadow_pmc_reg[index] &=
+ ~enable_bit;
+
+ /* get entry, record which PMC needs to be written */
+ record_hcall_request_idx(phys_reg, hcall_write,
+ pmu_events->
+ shadow_pmc_reg[index]);
+ }
+ }
+}
+
+void bus_util_pmd_write(struct torrent_pmu_events *pmu_events,
+ struct hcall_data *hcall_read)
+{
+ int i, index;
+ u8 virt_cntr;
+ u64 value, event_code;
+
+ for (i = 0; i < pmu_events->n_events; i++) {
+ event_code = pmu_events->event[i]->hw.config;
+ virt_cntr = (u8)pmu_events->event[i]->hw.idx;
+
+ if (pmu_events->unit.cntr_state[virt_cntr] == CNTR_IN_USE) {
+ index = hcall_read->virt_cntr_to_request_idx
+ [TORRENT_PBUS_UTIL_ID][virt_cntr];
+ value = hcall_read->tor_pmu_reg[index].regValue;
+
+ local64_set(&pmu_events->event[i]->hw.prev_count,
+ value);
+ }
+ }
+}
+
+int bus_util_pmd_read(struct torrent_pmu_events *pmu_events,
+ struct hcall_data *hcall_read)
+{
+ /*
+ * This PMU is accessed via hypervisor calls. The HCall to read the
+ * counters has been done. The counter data is in hcall_read.
+ */
+ int i, index;
+ u8 virt_cntr;
+ u64 value, prev, delta;
+ struct perf_event *event;
+
+ for (i = 0; i < pmu_events->n_events; i++) {
+ event = pmu_events->event[i];
+ virt_cntr = (u8)pmu_events->event[i]->hw.idx;
+
+ if (pmu_events->unit.cntr_state[virt_cntr] == CNTR_IN_USE) {
+ /*
+ * Read the PMD register. Its count value is the
+ * current value minus the previous value from when
+ * the counter was started.
+ */
+
+ /* get current count, from the HCall */
+ index = hcall_read->virt_cntr_to_request_idx
+ [TORRENT_PBUS_UTIL_ID][virt_cntr];
+
+ /*
+ * Get initial value, which was saved by the pmd write
+ * call into prev_count.
+ */
+ prev = local64_read(&event->hw.prev_count);
+ value = hcall_read->tor_pmu_reg[index].regValue;
+ delta = ((value - prev) & BUS_UTIL_16_BIT_CNTR_MASK);
+
+ local64_add(delta, &event->count);
+ local64_set(&event->hw.prev_count, value);
+ }
+ }
+ return 0;
+}
+
+int bus_util_get_phys_pmd_reg(u64 event_code)
+{
+ return virtual_cntr_to_phys_reg(TORRENT_VIRT_CTR_GET(event_code));
+}
diff --git a/arch/powerpc/platforms/p7ih/powerbus_ll_pmu.c b/arch/powerpc/platforms/p7ih/powerbus_ll_pmu.c
new file mode 100644
index 0000000..bf8d453
--- /dev/null
+++ b/arch/powerpc/platforms/p7ih/powerbus_ll_pmu.c
@@ -0,0 +1,273 @@
+/*
+ * Performance counter support for IBM Torrent interconnect chip
+ *
+ * Copyright 2010, 2011 Carl Love, Corey Ashford, IBM Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/perf_event.h>
+#include <asm/torrent_nest_pmu.h>
+#include <asm/powerbus_ll_pmu.h>
+#include <asm/power_torrent_events.h>
+
+#define LL_PMC_FIELD_WIDTH 4
+#define LL_NUM_VIRT_CTRS 7
+#define LL_CTR_SIZE 16
+#define LL_OPCODE_MASK 0x7ULL
+#define LL_PMD_MASK 0xFFFFULL
+#define LL_ENABLE_MASK (1ULL << (63 - 6))
+#define LL_SHADOW_IDX 0
+
+#define PB_CFG_PERF_CNT_MODE (1ULL << (63 - 7))
+
+int ll_link_pmu_check_constraints(struct torrent_pmu_events *pmu_events,
+ struct unit_config *unit_cfg)
+{
+ /*
+ * Check that there is only one event for each link (LL0..LL7)
+ * being counted at a time.
+ */
+ int i, total_events;
+ u8 virt_cntr;
+ u64 event_code;
+
+ total_events = pmu_events->n_events + pmu_events->n_add;
+
+ for (i = pmu_events->n_events; i < total_events; i++) {
+ /*
+ * Get the event code from attr.config value as the hw.config
+ * field is not set yet since the event has not been added.
+ */
+ event_code = pmu_events->event[i]->attr.config;
+ virt_cntr = (u8)TORRENT_VIRT_CTR_GET(event_code);
+ if (unit_cfg->cntr_state[virt_cntr] != CNTR_FREE)
+ return -1;
+
+ /*
+ * Assign the event to the counter but it has not been
+ * enabled yet.
+ */
+ unit_cfg->cntr_state[virt_cntr] = CNTR_ALLOCATED;
+ }
+ return 0;
+}
+
+int ll_link_compute_pmc_reg(struct torrent_pmu_events *pmu_events,
+ struct hcall_data *hcall_write)
+{
+ int i, total_events, shift_by;
+ u64 event_code;
+ u8 mux, virt_cntr;
+
+ total_events = pmu_events->n_events + pmu_events->n_add;
+
+ for (i = 0; i < total_events; i++) {
+ event_code = pmu_events->event[i]->hw.config;
+ virt_cntr = (u8)TORRENT_VIRT_CTR_GET(event_code);
+ pmu_events->event[i]->hw.idx = virt_cntr;
+ mux = (u8) TORRENT_MUX_GET(event_code);
+
+ /*
+ * The PBL_LINKS_TRACE_PERF_CTR_CFG event fields are:
+ * LL0 counter [52:49]
+ * LL1 counter [48:45]
+ * LL2 counter [44:41]
+ * LL3 counter [40:37]
+ * LL4 counter [36:33]
+ * LL5 counter [32:29]
+ * LL6 counter [28:25]
+ */
+ shift_by = 25 + (LL_NUM_VIRT_CTRS - virt_cntr - 1)
+ * LL_PMC_FIELD_WIDTH;
+
+ pmu_events->shadow_pmc_reg[LL_SHADOW_IDX]
+ &= ~(LL_OPCODE_MASK << shift_by);
+ pmu_events->shadow_pmc_reg[LL_SHADOW_IDX]
+ |= (u64)mux << shift_by | PB_CFG_PERF_CNT_MODE;
+
+ /* record which PMC needs to be written */
+ record_hcall_request_idx(PBL_LINKS_TRACE_PERF_CTR_CFG,
+ hcall_write,
+ pmu_events->
+ shadow_pmc_reg[LL_SHADOW_IDX]);
+ }
+ return 0;
+}
+
+void ll_link_enable_disable_hw_cntr(int op,
+ struct torrent_pmu_events *pmu_events,
+ struct hcall_data *hcall_write)
+{
+ int cntr_num;
+
+ for (cntr_num = 0; cntr_num < pmu_events->max_events;
+ cntr_num++) {
+ if (op) {
+ /* enable if cntr is in use or has been allocated */
+ if (pmu_events->unit.cntr_state[cntr_num] !=
+ CNTR_FREE) {
+ pmu_events->shadow_pmc_reg[LL_SHADOW_IDX] |=
+ LL_ENABLE_MASK;
+ pmu_events->unit.cntr_state[cntr_num] =
+ CNTR_IN_USE;
+ }
+ } else {
+ /* disable, only if the counter is actually in use */
+ if (pmu_events->unit.cntr_state[cntr_num] ==
+ CNTR_IN_USE)
+ pmu_events->shadow_pmc_reg[LL_SHADOW_IDX] &=
+ ~LL_ENABLE_MASK;
+ }
+
+ /* record which PMC needs to be written */
+ record_hcall_request_idx(PBL_LINKS_TRACE_PERF_CTR_CFG,
+ hcall_write,
+ pmu_events->shadow_pmc_reg
+ [LL_SHADOW_IDX]);
+ }
+}
+
+void ll_link_pmd_write(struct torrent_pmu_events *pmu_events,
+ struct hcall_data *hcall_read)
+{
+ /*
+ * The physical counters are 16 bits wide each.
+ * The eight counters are packed into two registers:
+ * LL0 counter -> PBL Links Counters 1 [47:32]
+ * LL1 counter -> PBL Links Counters 1 [31:16]
+ * LL2 counter -> PBL Links Counters 1 [15:0]
+ * LL3 counter -> PBL Links Counters 2 [63:48]
+ * LL4 counter -> PBL Links Counters 2 [47:32]
+ * LL5 counter -> PBL Links Counters 2 [31:16]
+ * LL6 counter -> PBL Links Counters 2 [15:0]
+ */
+
+ /*
+ * The PMD can only be accessed via an HCall. The four counters are
+ * packed into a single register. It is not practical to read the
+ * register, reset one counter field, then write the counter back.
+ * Secondly, being able to write a non-zero value is only needed when
+ * collecting a profile. Profiling on non-CPU counters doesn't make
+ * sense since there is no reliable way to associate the number of
+ * events that have occurred back to a specific instruction.
+ * Therefore, we will not support writing a value. The enable/disable
+ * HCall will need to sample the counts before the counter is enabled
+ * and then generate a delta count when the counters are disabled
+ */
+ int i, index;
+ u8 virt_cntr;
+ u64 event_code, value;
+
+ for (i = 0; i < pmu_events->n_events; i++) {
+ event_code = pmu_events->event[i]->hw.config;
+ virt_cntr = TORRENT_VIRT_CTR_GET(event_code);
+
+ if (pmu_events->unit.cntr_state[virt_cntr] == CNTR_IN_USE) {
+ /*
+ * The counters are stopped. The HCall to
+ * read the counters has been done. The
+ * current count must be set to the previous
+ * count. The counters will be enabled.
+ * When the counters are stopped and read
+ * again, the actual count will be the
+ * new count minus the previous count.
+ */
+
+ /*
+ * Get the request entry that contains the PMD for
+ * this virtual counter that is in use.
+ */
+ index = hcall_read->virt_cntr_to_request_idx
+ [TORRENT_PBUS_LL_ID][virt_cntr];
+
+ value = hcall_read->tor_pmu_reg[index].regValue;
+ if (virt_cntr < 3)
+ value = value >> ((2 - virt_cntr)
+ * LL_CTR_SIZE) & LL_PMD_MASK;
+ else
+ value = value >> ((6 - virt_cntr)
+ * LL_CTR_SIZE) & LL_PMD_MASK;
+ local64_set(&pmu_events->event[i]->hw.prev_count,
+ value);
+ }
+ }
+}
+
+int ll_link_pmd_read(struct torrent_pmu_events *pmu_events,
+ struct hcall_data *hcall_read)
+{
+ /*
+ * This PMU is accessed via hypervisor calls. The HCall to read the
+ * counters has been done. The counter data is in hcall_read.
+ */
+ int i, index;
+ u8 virt_cntr;
+ u64 value, prev, delta;
+ struct perf_event *event;
+
+ /*
+ * The physical counters are 16 bits wide each.
+ * The eight counters are packed into two registers:
+ * LL0 counter -> PBL Links Counters 1 [47:32]
+ * LL1 counter -> PBL Links Counters 1 [31:16]
+ * LL2 counter -> PBL Links Counters 1 [15:0]
+ * LL3 counter -> PBL Links Counters 2 [63:48]
+ * LL4 counter -> PBL Links Counters 2 [47:32]
+ * LL5 counter -> PBL Links Counters 2 [31:16]
+ * LL6 counter -> PBL Links Counters 2 [15:0]
+ */
+
+ for (i = 0; i < pmu_events->n_events; i++) {
+ event = pmu_events->event[i];
+ virt_cntr = (u8)TORRENT_VIRT_CTR_GET(event->hw.config);
+
+ if (pmu_events->unit.cntr_state[virt_cntr] == CNTR_IN_USE) {
+ /*
+ * Read the PMD register. Its count value is the
+ * current value minus the previous value from when
+ * the counter was started.
+ */
+
+ /* get current count, from the HCall */
+ index = hcall_read->virt_cntr_to_request_idx
+ [TORRENT_PBUS_LL_ID][virt_cntr];
+
+ /*
+ * Get initial value, which was saved by the pmd write
+ * call into prev_count.
+ */
+ prev = local64_read(&event->hw.prev_count);
+ value = hcall_read->tor_pmu_reg[index].regValue;
+
+ if (virt_cntr < 3)
+ value = value >> ((2 - virt_cntr)
+ * LL_CTR_SIZE) & LL_PMD_MASK;
+ else
+ value = value >> ((6 - virt_cntr)
+ * LL_CTR_SIZE) & LL_PMD_MASK;
+
+ /* calculate/save the count */
+ delta = ((value - prev) & 0xffffUL) << 16;
+ local64_add(delta, &event->count);
+ local64_set(&event->hw.prev_count, value);
+ }
+ }
+ return 0;
+}
+
+int ll_link_get_phys_pmd_reg(u64 event_code)
+{
+ int virt_cntr = TORRENT_VIRT_CTR_GET(event_code);
+
+ if (virt_cntr < 3)
+ /* LL0..LL2 */
+ return PBL_LINKS_CTRS_1;
+ else
+ /* LL3.. LL6 */
+ return PBL_LINKS_CTRS_2;
+}
diff --git a/arch/powerpc/platforms/p7ih/powerbus_mcd_pmu.c b/arch/powerpc/platforms/p7ih/powerbus_mcd_pmu.c
new file mode 100644
index 0000000..57070e5
--- /dev/null
+++ b/arch/powerpc/platforms/p7ih/powerbus_mcd_pmu.c
@@ -0,0 +1,492 @@
+/*
+ * Performance counter support for IBM Torrent interconnect chip
+ *
+ * Copyright 2010, 2011 Carl Love, Corey Ashford, IBM Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/perf_event.h>
+#include <asm/torrent_nest_pmu.h>
+#include <asm/powerbus_mcd_pmu.h>
+#include <asm/power_torrent_events.h>
+
+#define MCD_PMC_FIELD_WIDTH 8
+#define NUM_MCD_PMC_REGS 4
+#define MCD_CNTR_SIZE 16
+#define MCD_OPCODE_MASK 0xFF
+#define NUM_MCD_COUNTERS 4
+#define MCD_ENABLE_MASK 0x8000000000000000ULL
+#define MCD_UNIT_ROUTE_MASK 0x1L
+#define MCD_UNIT_ROUTE_CLEAR_MASK 0x11L
+#define MCD_16_BIT_CNTR 16
+#define MCD_32_BIT_CNTR 32
+#define MCD_16_BIT_CNTR_MASK 0xFFFF
+#define MCD_32_BIT_CNTR_MASK 0xFFFFFFFF
+#define MCD_CFG_SHADOW_REG_IDX 0
+#define MCD_SEL_SHADOW_REG_IDX 1
+
+u64 get_counter_attr(u64 event_code)
+{
+ u64 event_counter_type;
+
+ /* extract the counter type from the attribute field */
+ event_counter_type = TORRENT_ATTR_MCD_TYPE_MASK & event_code;
+
+ /*
+ * Move the type field to the location for the physical
+ * register.
+ */
+ event_counter_type >>= TORRENT_ATTR_MCD_TYPE_SHIFT;
+
+ return event_counter_type;
+}
+
+int mcd_get_prescale(u64 event_counter_type)
+{
+ switch (event_counter_type) {
+ case 0x0L: /* two 64-bit counters with 32-bit prescale */
+ return 32;
+ break;
+
+ /* four 32-bit counters with 16-bit prescale */
+ case 0x1L:
+ return 16;
+ break;
+
+ /* two 32-bit counters no prescale. */
+ case 0x2L:
+ /* four 16-bit counters no prescale. */
+ case 0x3L:
+ return 0;
+ break;
+
+ default:
+ pr_err("%s ERROR, UNKNOWN counter configuration setting 0x%Lx.\n",
+ __func__, event_counter_type);
+ return -1;
+ }
+}
+
+int mcd_get_num_ctrs(u64 event_counter_type)
+{
+ switch (event_counter_type) {
+ /* two 64-bit counters with 32-bit prescale */
+ case 0x0L:
+ /* two 32-bit counters no prescale. */
+ case 0x2L:
+ return 2;
+ break;
+
+ /* four 32-bit counters with 16-bit prescale */
+ case 0x1L:
+ /* four 16-bit counters no prescale. */
+ case 0x3L:
+ return 4;
+ break;
+
+ default:
+ pr_err("%s ERROR, UNKNOWN counter configuration setting, 0x%Lx.\n",
+ __func__, event_counter_type);
+ return -1;
+ }
+}
+
+int mcd_assign_counter(u8 *physical_counter_num,
+ struct torrent_pmu_events *pmu_events,
+ int num_config_cntrs)
+{
+ int i;
+ /*
+ * If the physical_counter_num is not equal to -1, then the event
+ * has already been assigned to a counter. Otherwise, assign
+ * physical_counter_num to the next available counter.
+ */
+
+ /* Look for first available counter, assign it */
+ for (i = 0; i < num_config_cntrs; i++) {
+ if (pmu_events->unit.cntr_state[i] == CNTR_FREE) {
+ pmu_events->unit.cntr_state[i] = CNTR_IN_USE;
+ *physical_counter_num = i;
+ return 0;
+ }
+ }
+
+ pr_err("%s, ERROR could not find an available counter to use.\n",
+ __func__);
+ return -1;
+}
+
+int mcd_pmu_check_constraints(struct torrent_pmu_events *pmu_events,
+ struct unit_config *unit_cfg)
+{
+ /*
+ * The MCD 0 and 1 units share the same physical counters. Therefore,
+ * copy_unit_config sends unit 0's unit config structure to check
+ * constraints so both units share the same counter reservation.
+ */
+
+ int i, total_events;
+ int config_max_counters;
+ u64 event_code;
+
+ total_events = pmu_events->n_events + pmu_events->n_add;
+
+ for (i = pmu_events->n_events; i < total_events; i++) {
+ /*
+ * Get the event code from attr.config value as the hw.config
+ * field is not set yet since the event has not been added.
+ */
+ event_code = pmu_events->event[i]->attr.config;
+
+ /*
+ * If the PMU is currently not counting any events, set the
+ * counter attribute. Make sure counter attribute for all
+ * events are consistent. Also, if the events are
+ * accepted, i.e. n_counters for this unit is updated, the
+ * counter attribute will also be set. If the events are not
+ * accepted, the counter attribute is not changed
+ */
+ if ((pmu_events->n_events == 0) && (i == 0))
+ /*
+ * If no counters are in use and this is the first
+ * event in the group, set the counter attribute and
+ * check that all the other events in the group are
+ * consistent.
+ */
+ unit_cfg->mcd_cntr_attr = get_counter_attr(event_code);
+
+ /*
+ * The event counter configuration must match the
+ * current counter configuration.
+ */
+ if (get_counter_attr(event_code) != unit_cfg->mcd_cntr_attr) {
+ pr_warning("%s Warning, rejecting MCD PMU events due to inconsistent counter attribute.\n",
+ __func__);
+ return -1;
+ }
+ }
+
+ /*
+ * Check that there are enough counters for the events given the
+ * number of counters configured as specified by the counter attribute.
+ */
+ config_max_counters = mcd_get_num_ctrs(unit_cfg->mcd_cntr_attr);
+
+ if (config_max_counters == -1)
+ return -1;
+
+ if (total_events > config_max_counters) {
+ pr_warning("%s Warning, rejecting PMU events due to insufficient number of counters\n",
+ __func__);
+ return -1;
+ }
+ return 0;
+}
+
+int mcd_compute_pmc_reg(struct torrent_pmu_events *pmu_events,
+ struct hcall_data *hcall_write)
+{
+ /* There are two MCD units however, they share the same physical
+ * PMU counters. Hence we only have one MCD PMU unit. The unit
+ * number is used to calculate the appropriate shift to write the
+ * unit specific counter event field.
+ */
+
+ int i, total_events, shift_by, input_unit, num_config_cntrs;
+ u8 virt_cntr;
+ u64 event_code, event_counter_type, err, mux;
+
+ /* The assumption is that we have passed the constraint test before
+ * this routine is called so we know that for the counter configuration
+ * (two or four counters) there are enough counters available for the
+ * new events.
+ */
+
+ total_events = pmu_events->n_events + pmu_events->n_add;
+
+ for (i = 0; i < total_events; i++) {
+ event_code = pmu_events->event[i]->hw.config;
+
+ /* extract the counter type from the attribute field */
+ event_counter_type = get_counter_attr(event_code);
+ num_config_cntrs = mcd_get_num_ctrs(event_counter_type);
+ err = mcd_assign_counter(&virt_cntr, pmu_events,
+ num_config_cntrs);
+ if (err)
+ return -1;
+
+ pmu_events->event[i]->hw.idx = virt_cntr;
+ pmu_events->unit.cntr_state[i] = CNTR_ALLOCATED;
+
+ /* determine which of the input MCD unit signals to count */
+ input_unit = TORRENT_UNIT_GET(event_code);
+
+ /*
+ * Setup the MCD config register fields:
+ * counter configuration field
+ * Unit signal routing field for the counter
+ */
+ shift_by = 52 + (NUM_MCD_PMC_REGS - virt_cntr - 1);
+
+ /* Clear signal routing bits for both units.e */
+ pmu_events->shadow_pmc_reg[MCD_CFG_SHADOW_REG_IDX] &=
+ ~(MCD_UNIT_ROUTE_CLEAR_MASK << shift_by);
+
+ if (input_unit == 0)
+ shift_by += 4;
+
+ pmu_events->shadow_pmc_reg[MCD_CFG_SHADOW_REG_IDX] |=
+ MCD_UNIT_ROUTE_MASK << shift_by;
+
+ /* set the counter number/prescale bits in config register */
+ pmu_events->shadow_pmc_reg[MCD_CFG_SHADOW_REG_IDX]
+ &= ~TORRENT_REG_ATTR_MCD_TYPE_MASK;
+
+ pmu_events->shadow_pmc_reg[MCD_CFG_SHADOW_REG_IDX]
+ |= event_counter_type
+ << TORRENT_REG_ATTR_MCD_TYPE_SHIFT;
+
+ /* Configuration register needs to be written */
+ record_hcall_request_idx(PBUS_MCD_PERF_CTRS_CFG, hcall_write,
+ pmu_events->shadow_pmc_reg
+ [MCD_CFG_SHADOW_REG_IDX]);
+
+ /* Setup the MCD selection register */
+ mux = (u64) TORRENT_MUX_GET(event_code);
+
+ /* The MCD event fields are:
+ * MCD 0 counter 0 [63:56]
+ * MCD 0 counter 1 [55:48]
+ * MCD 0 counter 2 [47:40]
+ * MCD 0 counter 3 [39:32]
+ * MCD 1 counter 0 [31:24]
+ * MCD 1 counter 1 [23:16]
+ * MCD 1 counter 2 [15:8]
+ * MCD 1 counter 3 [7:0]
+ */
+ if (input_unit == 0) /* adjust position based on input unit */
+ shift_by = 32;
+ else
+ shift_by = 0;
+
+ shift_by += (NUM_MCD_PMC_REGS - virt_cntr - 1)
+ * MCD_PMC_FIELD_WIDTH;
+
+ /*
+ * Set the event specifier field for the counter in the
+ * selection register
+ */
+ pmu_events->shadow_pmc_reg[MCD_SEL_SHADOW_REG_IDX] &=
+ MCD_OPCODE_MASK << shift_by;
+ pmu_events->shadow_pmc_reg[MCD_SEL_SHADOW_REG_IDX]
+ |= mux << shift_by;
+
+ /* Get request_idx and record which PMC needs to be written */
+ record_hcall_request_idx(PBUS_MCD_PERF_CTRS_SEL,
+ hcall_write,
+ pmu_events->shadow_pmc_reg
+ [MCD_SEL_SHADOW_REG_IDX]);
+ }
+ return 0;
+}
+
+void mcd_enable_disable_hw_cntr(int op, struct torrent_pmu_events *pmu_events,
+ struct hcall_data *hcall_write)
+{
+ int cntr_num;
+
+ /* There is a single PMU enable bit for all counters
+ *
+ * Note, the PMU can be configured with 2 or 4 physical counters.
+ */
+
+ for (cntr_num = 0; cntr_num < pmu_events->max_events;
+ cntr_num++) {
+ if (op) {
+ /* enable if cntr is in use or has been allocated */
+ if (pmu_events->unit.cntr_state[cntr_num] !=
+ CNTR_FREE) {
+ pmu_events->shadow_pmc_reg
+ [MCD_CFG_SHADOW_REG_IDX] |=
+ MCD_ENABLE_MASK;
+ pmu_events->unit.cntr_state[cntr_num] =
+ CNTR_IN_USE;
+ }
+ } else {
+ /* disable, only if the counter is actually in use */
+ if (pmu_events->unit.cntr_state[cntr_num] ==
+ CNTR_IN_USE)
+ pmu_events->shadow_pmc_reg
+ [MCD_CFG_SHADOW_REG_IDX] &=
+ ~MCD_ENABLE_MASK;
+ }
+ /* record which PMC needs to be written */
+ record_hcall_request_idx(PBUS_MCD_PERF_CTRS_CFG, hcall_write,
+ pmu_events->shadow_pmc_reg
+ [MCD_CFG_SHADOW_REG_IDX]);
+ }
+}
+
+void mcd_pmd_write(struct torrent_pmu_events *pmu_events,
+ struct hcall_data *hcall_read)
+{
+ /*
+ * All counters are packed into a single physical register:
+ * Four counter layout: (16-bit counters)
+ * counter 0 [63:48]
+ * counter 1 [47:32]
+ * counter 2 [31:16]
+ * counter 3 [15:0]
+ *
+ * Two counter layout: (32-bit counters)
+ * counter 0 [63:32]
+ * counter 1 [31:0]
+ */
+
+ /*
+ * The PMD can only be accessed via an HCall. The counters are
+ * packed into a single register. It is not practical to read the
+ * register, reset one counter field, then write the counter back.
+ * Secondly, being able to write a non-zero value is only needed when
+ * collecting a profile. Profiling on non-CPU counters doesn't make
+ * sense since there is no reliable way to associate the number of
+ * events that have occurred back to a specific instruction.
+ * Therefore, we will not support writing a value. The enable/disable
+ * HCall will need to sample the counts before the counter is enabled
+ * and then generate a delta count when the counters are disabled.
+ */
+ int i, index, num_config_cntrs;
+ u8 virt_cntr;
+ u64 value, event_code;
+
+ for (i = 0; i < pmu_events->n_events; i++) {
+ event_code = pmu_events->event[i]->hw.config;
+ virt_cntr = pmu_events->event[i]->hw.idx;
+ num_config_cntrs = mcd_get_num_ctrs(
+ pmu_events->unit.mcd_cntr_attr);
+
+ if (pmu_events->unit.cntr_state[virt_cntr] == CNTR_IN_USE) {
+ /*
+ * The counters are stopped. The HCall to
+ * read the counters has been done. The
+ * current count must be set to the previous
+ * count. The counters will be enabled.
+ * When the counters are stopped and read
+ * again, the actual count will be the
+ * new count minus the previous count.
+ */
+
+ /*
+ * Get the request entry that contains the PMD for
+ * this virtual counter.
+ */
+ index = hcall_read->virt_cntr_to_request_idx
+ [TORRENT_PBUS_MCD_ID][virt_cntr];
+ value = hcall_read->tor_pmu_reg[index].regValue;
+
+ if (num_config_cntrs == 2)
+ value >>= ((num_config_cntrs - 1 - virt_cntr)
+ * MCD_32_BIT_CNTR) &
+ MCD_32_BIT_CNTR_MASK;
+ else
+ value >>= ((num_config_cntrs - 1 - virt_cntr)
+ * MCD_16_BIT_CNTR) &
+ MCD_16_BIT_CNTR_MASK;
+ local64_set(&pmu_events->event[i]->hw.prev_count,
+ value);
+ }
+ }
+}
+
+int mcd_pmd_read(struct torrent_pmu_events *pmu_events,
+ struct hcall_data *hcall_read)
+{
+ /*
+ * This PMU is accessed via hypervisor calls. The HCall to read the
+ * counters has been done. The counter data is in hcall_read.
+ */
+ int i, index, num_config_cntrs, prescaler;
+ u8 virt_cntr;
+ u64 value, prev, delta;
+ struct perf_event *event;
+
+ /*
+ * All counters are packed into a single physical register:
+ * Four-counter layout: (16-bit counters)
+ * counter 0 [63:48]
+ * counter 1 [47:32]
+ * counter 2 [31:16]
+ * counter 3 [15:0]
+ *
+ * Two-counter layout: (32-bit counters)
+ * counter 0 [63:32]
+ * counter 1 [31:0]
+ */
+
+ for (i = 0; i < pmu_events->n_events; i++) {
+ event = pmu_events->event[i];
+ virt_cntr = pmu_events->event[i]->hw.idx;
+ prescaler = mcd_get_prescale(pmu_events->unit.mcd_cntr_attr);
+ num_config_cntrs = mcd_get_num_ctrs(
+ pmu_events->unit.mcd_cntr_attr);
+
+ if (prescaler == -1) {
+ pr_err("%s ERROR, Unknown counter configuration.\n",
+ __func__);
+ return -1;
+ }
+
+ if (pmu_events->unit.cntr_state[virt_cntr] == CNTR_IN_USE) {
+ /*
+ * Read the PMD register. Its count value is the
+ * current value minus the previous value from when
+ * the counter was started.
+ */
+
+ /* get current count, from the HCall */
+ index = hcall_read->virt_cntr_to_request_idx
+ [TORRENT_PBUS_MCD_ID][virt_cntr];
+
+ /*
+ * Get initial value, which was saved by the PMD write
+ * call into prev_count.
+ */
+ prev = local64_read(&event->hw.prev_count);
+ value = hcall_read->tor_pmu_reg[index].regValue;
+
+ if (num_config_cntrs == 2) {
+ value >>= ((num_config_cntrs - 1
+ - virt_cntr) * MCD_32_BIT_CNTR)
+ & MCD_32_BIT_CNTR_MASK;
+
+ delta = (value - prev) & MCD_32_BIT_CNTR_MASK;
+
+ } else {
+ value = value >> ((num_config_cntrs - 1
+ - virt_cntr)
+ * MCD_16_BIT_CNTR)
+ & MCD_16_BIT_CNTR_MASK;
+
+ delta = ((value - prev)
+ & MCD_16_BIT_CNTR_MASK);
+ }
+ delta = delta << prescaler;
+ local64_add(delta, &event->count);
+ local64_set(&event->hw.prev_count, value);
+ }
+ }
+ return 0;
+}
+
+
+int mcd_get_phys_pmd_reg(u64 event_code)
+{
+ /* All of the MCD counters use this single physical PMD
+ * register. */
+ return PBUS_MCD_PERF_CTRS;
+}
diff --git a/arch/powerpc/platforms/p7ih/powerbus_wxyz_pmu.c b/arch/powerpc/platforms/p7ih/powerbus_wxyz_pmu.c
new file mode 100644
index 0000000..e541469
--- /dev/null
+++ b/arch/powerpc/platforms/p7ih/powerbus_wxyz_pmu.c
@@ -0,0 +1,244 @@
+/*
+ * Performance counter support for IBM Torrent interconnect chip
+ *
+ * Copyright 2010, 2011 Carl Love, Corey Ashford, IBM Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/perf_event.h>
+#include <asm/torrent_nest_pmu.h>
+#include <asm/powerbus_wxyz_pmu.h>
+#include <asm/power_torrent_events.h>
+
+#define WXYZ_PMC_FIELD_WIDTH 3
+#define WXYZ_NUM_VIRT_CTRS 4
+#define WXYZ_CNTR_SIZE 16
+#define WXYZ_OPCODE_MASK 0x7ULL
+#define WXYZ_PMD_MASK 0xFFFFULL
+#define WXYZ_ENABLE_MASK (1ULL << (63 - 6))
+#define WXYZ_SHADOW_IDX 0
+
+int wxyz_link_pmu_check_constraints(struct torrent_pmu_events *pmu_events,
+ struct unit_config *unit_cfg)
+{
+ /*
+ * Check that there is only one event for each link (W, X,
+ * Y and Z) being counted at a time.
+ */
+ int i, total_events;
+ u8 virt_cntr;
+ u64 event_code;
+
+ total_events = pmu_events->n_events + pmu_events->n_add;
+
+ for (i = pmu_events->n_events; i < total_events; i++) {
+ /*
+ * Get the event code from attr.config value as the hw.config
+ * field is not set yet since the event has not been added.
+ */
+ event_code = pmu_events->event[i]->attr.config;
+ virt_cntr = (u8)TORRENT_VIRT_CTR_GET(event_code);
+ if (unit_cfg->cntr_state[virt_cntr] != CNTR_FREE)
+ return -1;
+
+ /*
+ * Assign the event to the counter but it has not been
+ * enabled yet.
+ */
+ unit_cfg->cntr_state[virt_cntr] = CNTR_ALLOCATED;
+ }
+ return 0;
+}
+
+int wxyz_link_compute_pmc_reg(struct torrent_pmu_events *pmu_events,
+ struct hcall_data *hcall_write)
+{
+ int i, total_events, shift_by;
+ u8 mux, virt_cntr;
+ u64 event_code;
+
+ total_events = pmu_events->n_events + pmu_events->n_add;
+
+ for (i = 0; i < total_events; i++) {
+ event_code = pmu_events->event[i]->hw.config;
+ virt_cntr = TORRENT_VIRT_CTR_GET(event_code);
+ pmu_events->event[i]->hw.idx = virt_cntr;
+ mux = (u8)TORRENT_MUX_GET(event_code);
+
+ /* The PBE_LINKS_TRACE event fields are:
+ * W counter [51:49]
+ * X counter [48:46]
+ * Y counter [45:43]
+ * Z counter [42:40]
+ */
+ shift_by = 40 +
+ (WXYZ_NUM_VIRT_CTRS - virt_cntr - 1) *
+ WXYZ_PMC_FIELD_WIDTH;
+
+ pmu_events->shadow_pmc_reg[WXYZ_SHADOW_IDX]
+ &= ~(WXYZ_OPCODE_MASK << shift_by);
+ pmu_events->shadow_pmc_reg[WXYZ_SHADOW_IDX]
+ |= (u64)mux << shift_by;
+
+ /* record which PMC needs to be written */
+ record_hcall_request_idx(PBE_LINKS_TRACE,
+ hcall_write,
+ pmu_events->
+ shadow_pmc_reg[WXYZ_SHADOW_IDX]);
+ }
+ return 0;
+}
+
+void wxyz_link_enable_disable_hw_cntr(int op,
+ struct torrent_pmu_events *pmu_events,
+ struct hcall_data *hcall_write)
+{
+ int cntr_num;
+
+ for (cntr_num = 0; cntr_num < pmu_events->max_events; cntr_num++) {
+ if (op) {
+ /* enable if cntr is in use or has been allocated */
+ if (pmu_events->unit.cntr_state[cntr_num] !=
+ CNTR_FREE) {
+ pmu_events->shadow_pmc_reg[WXYZ_SHADOW_IDX] |=
+ WXYZ_ENABLE_MASK;
+ pmu_events->unit.cntr_state[cntr_num] =
+ CNTR_IN_USE;
+ }
+ } else {
+ /* disable, only if the counter is actually in use */
+ if (pmu_events->unit.cntr_state[cntr_num]
+ == CNTR_IN_USE)
+ pmu_events->shadow_pmc_reg[WXYZ_SHADOW_IDX] &=
+ ~WXYZ_ENABLE_MASK;
+ }
+ /* record which PMC needs to be written */
+ record_hcall_request_idx(PBE_LINKS_TRACE,
+ hcall_write,
+ pmu_events->shadow_pmc_reg
+ [WXYZ_SHADOW_IDX]);
+ }
+}
+
+void wxyz_link_pmd_write(struct torrent_pmu_events *pmu_events,
+ struct hcall_data *hcall_read)
+{
+ /*
+ * The physical counters are 16 bits wide each.
+ * All four counters are packed into a single physical register:
+ * W counter [63:48]
+ * X counter [47:32]
+ * Y counter [31:16]
+ * Z counter [15:0]
+ */
+
+ /*
+ * The PMD can only be accessed via an HCall. The four counters are
+ * packed into a single register. It is not practical to read the
+ * register, reset one counter field, then write the counter back.
+ * Secondly, being able to write a non-zero value is only needed when
+ * collecting a profile. Profiling on non-CPU counters doesn't make
+ * sense since there is no reliable way to associate the number of
+ * events that have occurred back to a specific instruction.
+ * Therefore, we will not support writing a value. The enable/disable
+ * HCall will need to sample the counts before the counter is enabled
+ * and then generate a delta count when the counters are disabled
+ */
+
+ int i, index;
+ u8 virt_cntr;
+ u64 value, event_code;
+
+ for (i = 0; i < pmu_events->n_events; i++) {
+ event_code = pmu_events->event[i]->hw.config;
+ virt_cntr = TORRENT_VIRT_CTR_GET(event_code);
+
+ if (pmu_events->unit.cntr_state[virt_cntr] == CNTR_IN_USE) {
+ /*
+ * The counters are stopped. The HCall to
+ * read the counters has been done. The
+ * current count must be set to the previous
+ * count. The counters will be enabled.
+ * When the counters are stopped and read
+ * again, the actual count will be the
+ * new count minus the previous count.
+ */
+
+ /* Get the request entry that contains the PMD for
+ * this virtual counter.
+ */
+ index = hcall_read->virt_cntr_to_request_idx
+ [TORRENT_PBUS_WXYZ_ID][virt_cntr];
+ value = hcall_read->tor_pmu_reg[index].regValue;
+ value = value >> ((WXYZ_NUM_VIRT_CTRS - 1 - virt_cntr)
+ * WXYZ_CNTR_SIZE) & WXYZ_PMD_MASK;
+ local64_set(&pmu_events->event[i]->hw.prev_count,
+ value);
+ }
+ }
+}
+
+int wxyz_link_pmd_read(struct torrent_pmu_events *pmu_events,
+ struct hcall_data *hcall_read)
+{
+ /*
+ * This PMU is accessed via hypervisor calls. The HCall to read the
+ * counters has been done. The counter data is in hcall_read.
+ */
+ int i, index;
+ u8 virt_cntr;
+ u64 value, prev, delta;
+ struct perf_event *event;
+
+ /*
+ * The physical counters are 16 bits wide each.
+ * All four counters are packed into a single physical register:
+ * W counter [63:48]
+ * X counter [47:32]
+ * Y counter [31:16]
+ * Z counter [15:0]
+ */
+ for (i = 0; i < pmu_events->n_events; i++) {
+ event = pmu_events->event[i];
+ virt_cntr = TORRENT_VIRT_CTR_GET(event->hw.config);
+
+ if (pmu_events->unit.cntr_state[virt_cntr] == CNTR_IN_USE) {
+ /*
+ * Read the PMD register. Its count value is the
+ * current value minus the previous value from when
+ * the counter was started.
+ */
+ /* get current count, from the HCall */
+ index = hcall_read->virt_cntr_to_request_idx
+ [TORRENT_PBUS_WXYZ_ID][virt_cntr];
+
+ /*
+ * Get initial value, which was saved by the pmd write
+ * call into prev_count.
+ */
+ prev = local64_read(&event->hw.prev_count);
+ value = hcall_read->tor_pmu_reg[index].regValue;
+ value = value >> ((WXYZ_NUM_VIRT_CTRS - 1
+ - virt_cntr) * WXYZ_CNTR_SIZE)
+ & WXYZ_PMD_MASK;
+
+ /* calculate/save the count */
+ delta = ((value - prev) & 0xffffUL) << 16;
+ local64_add(delta, &event->count);
+ local64_set(&event->hw.prev_count, value);
+ }
+ }
+ return 0;
+}
+
+int wxyz_link_get_phys_pmd_reg(u64 event_code)
+{
+ /* All of the WXYZ link counters use this single physical PMD
+ * register. */
+ return PBE_LINKS_CTRS;
+}
diff --git a/arch/powerpc/platforms/p7ih/torrent_pmu.c b/arch/powerpc/platforms/p7ih/torrent_pmu.c
new file mode 100644
index 0000000..5005fd1
--- /dev/null
+++ b/arch/powerpc/platforms/p7ih/torrent_pmu.c
@@ -0,0 +1,1582 @@
+/*
+ * Performance event support for IBM Torrent interconnect chip
+ *
+ * Copyright 2010, 2011 Carl Love, Corey Ashford, IBM Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/hrtimer.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/vmalloc.h>
+#include <linux/of.h>
+#include <linux/perf_event.h>
+#include <linux/string.h>
+#include <asm/power_torrent_events.h>
+#include <asm/torrent_nest_pmu.h>
+#include <asm/powerbus_bus_util_pmu.h>
+#include <asm/powerbus_wxyz_pmu.h>
+#include <asm/powerbus_ll_pmu.h>
+#include <asm/powerbus_mcd_pmu.h>
+#include <asm/mmu_pmu.h>
+#include <asm/cau_pmu.h>
+#include <asm/cputable.h>
+#include <asm/reg.h>
+#include <asm/hvcall.h>
+
+/*
+ * There are a number of Torrent PMUs that count non-core-specific events.
+ * These are referred to as Torrent events.
+ */
+
+static u64 torrent_chip_id;
+static u64 hfi_unit_id;
+
+struct torrent_txns {
+ unsigned int group_flag;
+ int n_txn_start[TORRENT_NUM_PMU_TYPES];
+};
+
+static struct torrent_txns torrent_txn;
+struct torrent_pmu_events txn_all_events[TORRENT_NUM_PMU_TYPES];
+
+/*
+ * The variable torrent_pmu_cntrs is doubly-indexed array. The first index of
+ * the array is the PMU type. Then each instance of a given type is indexed
+ * by its unit number.
+ */
+static struct torrent_pmu_counters ***torrent_pmu_cntrs;
+
+static int num_torrent_chips = 1;
+static int pbus_pmu_reservation_cnt[TORRENT_NUM_PBUS_PMU_TYPES];
+
+/* Store the list of all events, being counted and those to be added */
+struct torrent_pmu_events all_torrent_events[TORRENT_NUM_PMU_TYPES];
+
+#define HFI_SHIFT_OCTANT 3
+#define HFI_MAX_OCTANT 7
+
+#define PLPAR_HCALL plpar_hcall_norets
+
+static DEFINE_SPINLOCK(torrent_lock);
+#define PAGE_4K (1ULL << 12)
+#define ALIGN_4K(x) ALIGN(x, PAGE_4K)
+
+static u64 unaligned_buffer[2 * PAGE_4K / sizeof(u64)];
+static u64 *aligned_buffer;
+
+/*
+ * The Torrent counter polling interval needs to be set so that there's a good
+ * margin between counting to 2^32 at the speed of the fastest possible event
+ * rate. Right now, the fastest rate is the WXYZ link idle which counts at
+ * 3 GHz. At 3 GHz, it will take about 1.4 seconds to count to 2^32. Note,
+ * the MCD counters can be setup as 16 bit, 32 or 64 bit counters. It is not
+ * clear what the maximum MCD event increment rate might be. Since the user
+ * can control the size of these counters, for now we will leave it to the user
+ * to select the correct size based on the event. To be conservative, the
+ * polling period is set to about 1/3 of the maximum counter-wrap period.
+ */
+#define POLLING_INTERVAL_SEC 0
+#define POLLING_INTERVAL_NS 500000000
+
+static ktime_t torrent_counter_poll_interval;
+struct hrtimer torrent_poll_timer;
+u64 poll_start_time;
+
+static void initialize_event_struct(struct torrent_pmu_events *event_struct)
+{
+ int pmu_type, i;
+
+ event_struct[TORRENT_PBUS_WXYZ_ID].max_events =
+ MAX_CNTRS_PER_WXYZ_LINK_PMU;
+ event_struct[TORRENT_PBUS_LL_ID].max_events =
+ MAX_CNTRS_PER_LL_LINK_PMU;
+ event_struct[TORRENT_PBUS_MCD_ID].max_events =
+ MAX_CNTRS_PER_MCD_PMU;
+ event_struct[TORRENT_PBUS_UTIL_ID].max_events =
+ MAX_CNTRS_PER_BUS_UTIL_PMU;
+ event_struct[TORRENT_MMU_ID].max_events = MAX_CNTRS_PER_MMU_PMU;
+ event_struct[TORRENT_CAU_ID].max_events = MAX_CNTRS_PER_CAU_PMU;
+
+ for (pmu_type = 0; pmu_type < TORRENT_NUM_PMU_TYPES; pmu_type++) {
+ event_struct[pmu_type].n_events = 0;
+ event_struct[pmu_type].n_add = 0;
+ event_struct[pmu_type].update = false;
+ event_struct[pmu_type].disabled = 0;
+ event_struct[pmu_type].unit.mcd_cntr_attr = 0;
+ event_struct[pmu_type].unit.bus_util_enable_mask = 0;
+ event_struct[pmu_type].unit.bus_util_cntr_sel = 0;
+
+ for (i = 0; i < MAX_CNTRS_TORRENT_PMUS; i++)
+ event_struct[pmu_type].unit.cntr_state[i] = CNTR_FREE;
+
+ /* Initialize the PMC shadow regs to all zero.
+ * *NOTE* this assumes that the correct initial value for
+ * every PMC reg is zero. If this turns out not to be the
+ * case for some PMU(s), we may want to pass in a pointer to a
+ * function which initializes the shadow regs correctly.
+ */
+ for (i = 0; i < MAX_TORRENT_PMC_REGS; i++)
+ event_struct[pmu_type].shadow_pmc_reg[i] = 0x0;
+
+ if (pmu_type == TORRENT_PBUS_MCD_ID)
+ /* MCD specific set up */
+ event_struct[pmu_type].unit.mcd_cntr_attr = 0;
+
+ if (pmu_type == TORRENT_PBUS_UTIL_ID) {
+ /* Bus util specific set up */
+ event_struct[pmu_type].unit.bus_util_enable_mask = 0;
+ event_struct[pmu_type].unit.bus_util_cntr_sel
+ = BUS_UTIL_CNTR_SEL_AVAIL;
+ }
+ }
+}
+
+static int wrap_hcall_hfi_dump_info(u64 torrent_chip_id,
+ struct dump_mmio_perf_counters *mmio_perf_counters)
+{
+ struct dump_mmio_perf_counters *aligned_mmio_perf_counters =
+ (struct dump_mmio_perf_counters *)aligned_buffer;
+ int ret = 0;
+
+ ret = PLPAR_HCALL(H_HFI_DUMP_INFO, hfi_unit_id,
+ DUMP_MMIO_PERFORMANCE_COUNTERS,
+ sizeof(struct dump_mmio_perf_counters),
+ __pa(aligned_buffer));
+
+ if (ret == H_SUCCESS)
+ /* Copy the data out of the aligned buffer into the caller's
+ * memory.
+ */
+ *mmio_perf_counters = *aligned_mmio_perf_counters;
+
+ return ret;
+}
+
+static int wrap_hcall_tor_access_pmu_scom_regs(u64 torrent_chip_id,
+ int req_type,
+ int num_regs,
+ struct tor_pmu_reg *tor_pmu_regs)
+{
+ struct tor_pmu_reg *aligned_tor_pmu_reg =
+ (struct tor_pmu_reg *)aligned_buffer;
+ int i, ret;
+
+ for (i = 0; i < num_regs; i++) {
+ aligned_tor_pmu_reg[i].regId = tor_pmu_regs[i].regId;
+ if (req_type == PBUS_HCALL_WRITE)
+ aligned_tor_pmu_reg[i].regValue =
+ tor_pmu_regs[i].regValue;
+ }
+ ret = PLPAR_HCALL(H_TOR_ACCESS_PMU_SCOM_REGS, torrent_chip_id,
+ (u64)req_type, (u64)num_regs,
+ __pa(aligned_buffer));
+
+ if (req_type == PBUS_HCALL_READ)
+ for (i = 0; i < num_regs; i++)
+ tor_pmu_regs[i].regValue =
+ aligned_tor_pmu_reg[i].regValue;
+ return ret;
+}
+
+/* Generic Torrent PMU functions
+ *
+ * These routines make calls via function pointers to the PMU-specific
+ * functions to do the PMU-specific PMU register accesses. The number and
+ * layout of the control registers, the PMD registers and the event constraints
+ * are very PMU-specific. Hence the use of function pointers to deal with the
+ * specifics of a given PMU.
+ */
+int get_chip(void)
+{
+ return 0; /* Currently only supporting a single Torrent chip system */
+}
+
+int get_max_nest_events(int pmu_type)
+{
+ /* The maximum number of PMU events is independent of the chip number.
+ * So, just get it for chip 0.
+ */
+ return all_torrent_events[pmu_type].max_events;
+}
+
+static void hcall_data_reset(struct hcall_data *hcall_data)
+{
+ int i, pmu_type;
+
+ hcall_data->num_regs = 0;
+ for (i = 0; i < MAX_HCALL_REGS; i++)
+ hcall_data->request_idx[i] = -1;
+
+ for (pmu_type = 0; pmu_type < TORRENT_NUM_PMU_TYPES; pmu_type++) {
+ for (i = 0; i < MAX_CNTRS_TORRENT_PMUS; i++)
+ hcall_data->virt_cntr_to_request_idx[pmu_type][i] =
+ 0xbadbeef;
+ }
+ hcall_data->do_dump_mmio_perf_counters = 0;
+}
+
+static int hcall_read_setup(struct torrent_pmu_events *torrent_pmu_events,
+ int pmu_type,
+ struct hcall_data *hcall_read)
+{
+ struct torrent_pmu_counters *torrent_pmu;
+ int num_events, i, phys_reg = 0, *num_regs_p, request_idx;
+ u8 virt_cntr;
+ u64 event_code;
+
+ torrent_pmu = torrent_pmu_cntrs[get_chip()][pmu_type];
+ num_events = torrent_pmu_events->n_events;
+
+ if (num_events == 0)
+ return 0; /* no counters to read on this PMU. */
+
+ switch (pmu_type) {
+ case TORRENT_MMU_ID:
+ case TORRENT_CAU_ID:
+ hcall_read->do_dump_mmio_perf_counters = 1;
+ return 0;
+ default:
+ /* it must be one of the Powerbus PMU's */
+ break;
+ }
+ num_regs_p = &hcall_read->num_regs;
+
+ for (i = 0; i < num_events; i++) {
+ event_code = torrent_pmu_events->event[i]->hw.config;
+ phys_reg = torrent_pmu->get_phys_pmd_reg(event_code);
+ virt_cntr = (u8)torrent_pmu_events->event[i]->hw.idx;
+
+ /* Check if the physical reg is already in the list
+ * to be read.
+ */
+ if (hcall_read->request_idx[phys_reg] == -1) {
+ request_idx = *num_regs_p;
+ hcall_read->request_idx[phys_reg] = *num_regs_p;
+ hcall_read->tor_pmu_reg[*num_regs_p].regId
+ = phys_reg;
+ (*num_regs_p)++;
+
+ if (*num_regs_p == MAX_HCALL_REGS) {
+ pr_err("%s, ERROR, "
+ "MAX_HCALL_REGS is "
+ "too small.\n", __func__);
+ return 1;
+ }
+ } else {
+ request_idx = hcall_read->request_idx[phys_reg];
+ }
+ hcall_read->virt_cntr_to_request_idx[pmu_type][virt_cntr]
+ = request_idx;
+ }
+ return 0;
+}
+
+void do_hcall_pmd_read(struct hcall_data *hcall_read,
+ struct torrent_pmu_events *torrent_pmu_events)
+{
+ int pmu_type, ret;
+
+ /*
+ * Set up variable hcall_read with all the physical registers to read.
+ * Then bundle up the registers to be read into as few HCalls as
+ * possible to minimize the HCall overhead.
+ */
+
+ hcall_data_reset(hcall_read);
+ for (pmu_type = 0; pmu_type < TORRENT_NUM_PMU_TYPES; pmu_type++)
+ hcall_read_setup(&torrent_pmu_events[pmu_type], pmu_type,
+ hcall_read);
+
+ if (hcall_read->num_regs != 0) {
+ ret = wrap_hcall_tor_access_pmu_scom_regs(torrent_chip_id,
+ PBUS_HCALL_READ, hcall_read->num_regs,
+ hcall_read->tor_pmu_reg);
+ if (ret != H_SUCCESS)
+ pr_err("%s, ERROR, HCall H_TOR_ACCESS_PMU_SCOM_REGS:PBUS_HCALL_READ returned an error %d\n", __func__,
+ ret);
+ }
+ if (hcall_read->do_dump_mmio_perf_counters) {
+ ret = wrap_hcall_hfi_dump_info(torrent_chip_id,
+ &hcall_read->mmio_perf_counters);
+ if (ret != H_SUCCESS)
+ pr_err("%s, ERROR, HCall H_HFI_DUMP_INFO:DUMP_MMIO_PERFORMANCE_COUNTERS HCall returned an error %d\n", __func__,
+ ret);
+ }
+}
+
+void do_hcall_pmc_write(struct hcall_data *hcall_write)
+{
+ int ret;
+
+ if (hcall_write->num_regs != 0) {
+ ret = wrap_hcall_tor_access_pmu_scom_regs(torrent_chip_id,
+ PBUS_HCALL_WRITE, hcall_write->num_regs,
+ hcall_write->tor_pmu_reg);
+
+ if (ret != H_SUCCESS)
+ pr_err("%s, ERROR, HCall H_TOR_ACCESS_PMU_SCOM_REGS:PBUS_HCALL_WRITE returned an error %d\n", __func__,
+ ret);
+ }
+}
+
+void record_hcall_request_idx(int phys_register_index,
+ struct hcall_data *hcall_request,
+ u64 shadow_reg_index)
+{
+ int request_idx;
+
+ /* Configuration register needs to be written */
+ request_idx = hcall_request->num_regs;
+
+ if (hcall_request->request_idx[phys_register_index] == -1) {
+ hcall_request->request_idx[phys_register_index]
+ = request_idx;
+ hcall_request->tor_pmu_reg[request_idx].regId
+ = phys_register_index;
+ hcall_request->num_regs++;
+ } else {
+ /* store the updated value to write */
+ request_idx = hcall_request->
+ request_idx[phys_register_index];
+ }
+
+ hcall_request->tor_pmu_reg[request_idx].regValue = shadow_reg_index;
+}
+
+static void copy_unit_config(struct unit_config *unit_cfg_src,
+ struct unit_config *unit_cfg_dest, int pmu_type)
+{
+ int i;
+
+ for (i = 0; i < MAX_CNTRS_TORRENT_PMUS; i++)
+ unit_cfg_dest->cntr_state[i] = unit_cfg_src->cntr_state[i];
+
+ if (pmu_type == TORRENT_PBUS_MCD_ID)
+ unit_cfg_dest->mcd_cntr_attr = unit_cfg_src->mcd_cntr_attr;
+
+ if (pmu_type == TORRENT_PBUS_UTIL_ID) {
+ unit_cfg_dest->bus_util_enable_mask =
+ unit_cfg_src->bus_util_enable_mask;
+ unit_cfg_dest->bus_util_cntr_sel =
+ unit_cfg_src->bus_util_cntr_sel;
+ }
+}
+
+static void copy_all_events(struct torrent_pmu_events *src_events,
+ struct torrent_pmu_events *dst_events)
+{
+ int pmu_type, i;
+
+ for (pmu_type = 0; pmu_type < TORRENT_NUM_PMU_TYPES; pmu_type++) {
+ dst_events[pmu_type].max_events =
+ src_events[pmu_type].max_events;
+ dst_events[pmu_type].n_events = src_events[pmu_type].n_events;
+ dst_events[pmu_type].n_add = src_events[pmu_type].n_add;
+
+ copy_unit_config(&src_events[pmu_type].unit,
+ &dst_events[pmu_type].unit, pmu_type);
+ for (i = 0; i < MAX_CNTRS_TORRENT_PMUS; i++)
+ dst_events[pmu_type].event[i] =
+ src_events[pmu_type].event[i];
+ }
+}
+
+int get_max_torrent_counters(int pmu_type)
+{
+ /* The maximum number of PMU events is independent of the chip number.
+ * So, just get it for chip 0.
+ */
+ return all_torrent_events[pmu_type].max_events;
+}
+
+static void collect_events(struct perf_event *group,
+ struct torrent_pmu_events *tmp_pmus)
+{
+ int pmu_type;
+ struct torrent_pmu_events *torrent_pmu_events;
+ struct perf_event *event;
+
+ /*
+ * Get the event code from attr.config value as the hw.config
+ * field is not set yet since the event has not been added.
+ */
+ pmu_type = TORRENT_PMU_GET(group->attr.config);
+
+ if (pmu_type < 0)
+ pr_err("%s: ERROR, collect_events: could not determine pmu_type or unit number.\n",
+ __func__);
+
+ torrent_pmu_events = &tmp_pmus[pmu_type];
+
+ BUG_ON(torrent_pmu_events == NULL);
+
+ list_for_each_entry(event, &group->sibling_list, group_entry) {
+ if (event->state != PERF_EVENT_STATE_OFF) {
+ int pmu_type, index;
+ struct torrent_pmu_events *torrent_pmu_events;
+
+ pmu_type = TORRENT_PMU_GET(event->attr.config);
+
+ torrent_pmu_events = &tmp_pmus[pmu_type];
+ /*
+ * Index is what is on the PMU plus pending
+ * events to add.
+ */
+ index = torrent_pmu_events->n_events +
+ torrent_pmu_events->n_add;
+
+ if (index >= get_max_nest_events(pmu_type))
+ return;
+
+ torrent_pmu_events->event[index] = event;
+ /*
+ * Initialize perf event as not yet
+ * being assigned to a physical counter.
+ * Physical counters are numbered from 0
+ * to n-1.
+ */
+ torrent_pmu_events->event[index]->hw.idx = -1;
+ torrent_pmu_events->n_add++;
+ }
+ }
+}
+
+void accept_torrent_events(struct torrent_pmu_events *new_all_events,
+ struct torrent_pmu_events *existing_all_events)
+{
+ int pmu_type, i, index, debug_total_events = 0;
+ struct torrent_pmu_events *new_events, *existing_events;
+
+ for (pmu_type = 0; pmu_type < TORRENT_NUM_PMU_TYPES;
+ pmu_type++) {
+
+ new_events = &new_all_events[pmu_type];
+ existing_events = &existing_all_events[pmu_type];
+
+ if (new_events->n_add) {
+ /*
+ * Move the events from the list of new events to the
+ * list of events being measured by the PMU.
+ */
+ for (i = 0; i < new_events->n_add; i++) {
+ index = i + existing_events->n_events;
+ existing_events->event[index] =
+ new_events->event[index];
+ }
+ existing_events->n_events += new_events->n_add;
+ debug_total_events += existing_events->n_events;
+ copy_unit_config(&new_events->unit,
+ &existing_events->unit, pmu_type);
+ existing_events->update = 1;
+ }
+ }
+}
+
+int hw_perf_group_sched_in_torrent_check(struct torrent_pmu_events
+ *new_pmu_events)
+{
+ struct torrent_pmu_counters *torrent_pmu;
+ struct torrent_pmu_events *torrent_pmu_events;
+ int pmu_type, n, n0, i, max_events;
+
+ for (pmu_type = 0; pmu_type < TORRENT_NUM_PMU_TYPES; pmu_type++) {
+ /*
+ * torrent_pmu is the structure that tracks what the physical
+ * PMU usage
+ */
+ torrent_pmu_events = &new_pmu_events[pmu_type];
+
+ /*
+ * We are just testing to see if the group is
+ * self-consistent, so use a PMU config which has no
+ * registers already reserved, nor other configuration
+ * bits preset.
+ */
+
+ n = torrent_pmu_events->n_add;
+ n0 = torrent_pmu_events->n_events; /* being counted */
+
+ if (n == 0)
+ continue;
+
+ /* Get the number of physical counters for the PMU */
+ max_events = torrent_pmu_events->max_events;
+
+ if (n + n0 > max_events) {
+ /*
+ * Current plus new events exceeds physical
+ * number of counters
+ */
+ pr_err("%s %d, CPU %d, !!! ERROR n %d exceeded max %d\n",
+ __func__, __LINE__, smp_processor_id(), n+n0,
+ max_events);
+ return -EAGAIN;
+ }
+
+ /*
+ * See if we can put the new events on with the
+ * existing events. We need to use the state of the real
+ * unit config structure so we can see what is being
+ * used. If we fail, we need to make sure we throw
+ * away any changes from the check constraints call.
+ */
+
+ torrent_pmu = torrent_pmu_cntrs[get_chip()][pmu_type];
+ i = torrent_pmu->check_constraints(torrent_pmu_events,
+ &torrent_pmu_events->unit);
+ if (i < 0)
+ return -EAGAIN;
+ }
+ return 0;
+}
+
+void torrent_pmu_enable(struct pmu *pmu)
+{
+ /*
+ * This function adds the new events to all of the PMUs. The n_add
+ * value for each PMU will specify how many new events are being
+ * added. This routine will add n_add to n__events then set n_add
+ * to zero to indicate the event has been added.
+ */
+ int pmu_type, ret, dump_ret;
+ unsigned long lock_flags;
+ struct torrent_pmu_counters *torrent_pmu;
+ struct torrent_pmu_events *torrent_pmu_events;
+ struct hcall_data hcall_read;
+ struct hcall_data hcall_write;
+ bool start_timer = false;
+
+ /*
+ * Need to do a single HCall to stop all the PMUs that need to have
+ * events programmed on them, i.e. n_add for the PMU is not zero.
+ */
+ spin_lock_irqsave(&torrent_lock, lock_flags); /* ensure only one CPU
+ * at a time access the
+ * HCall data structures
+ */
+ hcall_data_reset(&hcall_write);
+
+ /* Disable the PMUs with new events to add */
+ for (pmu_type = 0; pmu_type < TORRENT_NUM_PMU_TYPES; pmu_type++) {
+ torrent_pmu = torrent_pmu_cntrs[get_chip()][pmu_type];
+ torrent_pmu_events = &all_torrent_events[pmu_type];
+
+ /*
+ * We only need to stop PMUs where there are new
+ * events added to the PMU.
+ */
+ if (torrent_pmu_events->update)
+ torrent_pmu->enable_disable_cntr(0,
+ torrent_pmu_events,
+ &hcall_write);
+ }
+
+ if (hcall_write.num_regs != 0) {
+ do_hcall_pmc_write(&hcall_write);
+ hcall_data_reset(&hcall_write);
+ }
+
+ if (hcall_write.do_dump_mmio_perf_counters)
+ dump_ret = wrap_hcall_hfi_dump_info(torrent_chip_id,
+ &hcall_write.mmio_perf_counters);
+
+ for (pmu_type = 0; pmu_type < TORRENT_NUM_PMU_TYPES;
+ pmu_type++) {
+ torrent_pmu = torrent_pmu_cntrs[get_chip()][pmu_type];
+ torrent_pmu_events = &all_torrent_events[pmu_type];
+
+ if (torrent_pmu_events->update) {
+ torrent_pmu_events->update = 0;
+
+ if (pmu_type <= TORRENT_LAST_PBUS_PMU_ID) {
+ /* Reserve the Pbus PMU */
+
+ pbus_pmu_reservation_cnt[pmu_type] =
+ torrent_pmu_events->n_events;
+ }
+
+ /*
+ * Set up the physical PMCs with the new
+ * events and set the PMU enable bit,
+ * assign events to counters
+ */
+ ret = torrent_pmu->compute_pmc_regs(
+ torrent_pmu_events, &hcall_write);
+
+ if (ret) {
+ pr_debug("%s:%d, compute_pmc_regs returned error = %d\n",
+ __func__, __LINE__, ret);
+ return;
+ }
+
+ torrent_pmu->enable_disable_cntr(1, torrent_pmu_events,
+ &hcall_write);
+ torrent_pmu_events->disabled = 0;
+ start_timer = true;
+ }
+ }
+
+ /* Read the initial value of all of the PMDs that will be enabled */
+ do_hcall_pmd_read(&hcall_read, all_torrent_events);
+
+ /*
+ * Now read the virtual counter PMD values from the physical registers
+ * returned in variable hcall_read. Set up the new PMC values.
+ */
+ for (pmu_type = 0; pmu_type < TORRENT_NUM_PMU_TYPES;
+ pmu_type++) {
+ torrent_pmu = torrent_pmu_cntrs[get_chip()][pmu_type];
+ torrent_pmu_events = &all_torrent_events[pmu_type];
+ /*
+ * Call pmd_write to update the prev_count values
+ * with the current values.
+ */
+ torrent_pmu->write_pmd(torrent_pmu_events, &hcall_read);
+ }
+
+ /* Enable the PMUs with new events to add */
+ do_hcall_pmc_write(&hcall_write);
+
+ if (start_timer && !hrtimer_active(&torrent_poll_timer)) {
+ int ret;
+
+ poll_start_time = get_tb();
+ ret = hrtimer_start(&torrent_poll_timer,
+ torrent_counter_poll_interval,
+ HRTIMER_MODE_REL);
+ WARN_ON(ret != 0);
+ }
+
+ spin_unlock_irqrestore(&torrent_lock, lock_flags);
+}
+
+
+static void release_torrent_pmu_counters(struct perf_event *event,
+ struct torrent_pmu_events
+ *torrent_pmu_events,
+ int pmu_type)
+{
+ int hw_cntr;
+
+ /*
+ * The HCall has been done to stop the counters. The counters have
+ * been read. This function removes the counters from the PMU.
+ */
+
+ hw_cntr = event->hw.idx;
+ torrent_pmu_events->unit.cntr_state[hw_cntr] = CNTR_FREE;
+
+ if (pmu_type <= TORRENT_LAST_PBUS_PMU_ID)
+ /* Release the PBUS PMU */
+ pbus_pmu_reservation_cnt[pmu_type]--;
+
+ event->hw.idx = -1;
+}
+
+
+void torrent_pmu_disable(struct pmu *pmu)
+{
+ struct torrent_pmu_events *torrent_pmu_events;
+ struct torrent_pmu_counters *torrent_pmu;
+ struct hcall_data hcall_write;
+ unsigned long lock_flags;
+ int pmu_type;
+
+ /* Disable and read all of the counters on all of the PMUs */
+
+ /* Set up the HCall to write the PMC's to stop the counters. */
+ spin_lock_irqsave(&torrent_lock, lock_flags); /* Ensure only one CPU
+ * at a time access the
+ * HCall data structures
+ */
+ /*
+ * The first time that torrent_pmu_disable is called is when a
+ * measurement is started, the poll_timer won't be running yet, but
+ * all other times, it will be. So hrtimer_cancel will return 0 on the
+ * first call, and 1 subsequently.
+ */
+ hrtimer_cancel(&torrent_poll_timer);
+
+ hcall_data_reset(&hcall_write);
+
+ for (pmu_type = 0; pmu_type < TORRENT_NUM_PMU_TYPES;
+ pmu_type++) {
+ torrent_pmu = torrent_pmu_cntrs[get_chip()][pmu_type];
+
+ torrent_pmu_events = &all_torrent_events[pmu_type];
+ torrent_pmu->enable_disable_cntr(0, torrent_pmu_events,
+ &hcall_write);
+ torrent_pmu_events->disabled = 1;
+ }
+
+ /* Do the HCall to write the physical registers. */
+ do_hcall_pmc_write(&hcall_write);
+ spin_unlock_irqrestore(&torrent_lock, lock_flags);
+}
+
+struct torrent_pmu_counters *map_to_pmu(u64 event_code)
+{
+ /* Extract the PMU type from the event and return the pointer to the
+ * torrent_pmu_counters struct for that PMU type.
+ */
+ int pmu_type;
+ int chip = get_chip();
+
+ BUG_ON(!IS_TORRENT_EVENT(event_code));
+ pmu_type = TORRENT_PMU_GET(event_code);
+
+ BUG_ON(pmu_type > TORRENT_LAST_PMU_ID);
+ return torrent_pmu_cntrs[chip][pmu_type];
+}
+
+int torrent_pmu_add(struct perf_event *event, int ef_flags)
+{
+ /*
+ * This function adds the event to the list of events for
+ * the PMU that counts the specified event.
+ *
+ * This function is called directly from the arch-independent code.
+ */
+ int pmu_type, index, err, ret = -EAGAIN;
+ unsigned long lock_flags;
+ struct torrent_pmu_events tmp_all_events[TORRENT_NUM_PMU_TYPES];
+ struct torrent_pmu_events *torrent_pmu_events;
+
+ initialize_event_struct(tmp_all_events);
+
+ spin_lock_irqsave(&torrent_lock, lock_flags); /* Ensure only one CPU
+ * at a time access the
+ * data structures.
+ */
+
+ copy_all_events(all_torrent_events, tmp_all_events);
+
+ if (event->group_leader != event) {
+ collect_events(event->group_leader, tmp_all_events);
+ } else {
+
+ /*
+ * Add the new group leader event to the list of existing
+ * nest events.
+ */
+ pmu_type = TORRENT_PMU_GET(event->attr.config);
+
+ if (pmu_type < 0) {
+ pr_err("%s: ERROR, pmu_type < 0\n",
+ __func__);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ torrent_pmu_events = &tmp_all_events[pmu_type];
+
+ index = torrent_pmu_events->n_events
+ + torrent_pmu_events->n_add;
+
+ if (index > get_max_nest_events(pmu_type)) {
+ pr_err("%s: ERROR, index out of range\n",
+ __func__);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ torrent_pmu_events->event[index] = event;
+ torrent_pmu_events->n_add++;
+ }
+
+ if (torrent_txn.group_flag & PERF_EVENT_TXN) {
+ /*
+ * If group events scheduling transaction was started,
+ * skip the schedulability test here, it will be performed
+ * at commit time(->commit_txn) as a whole. Save the event
+ * added to the list of transaction events being added.
+ */
+ copy_all_events(tmp_all_events, txn_all_events);
+
+ } else {
+ /* check constraints for all pmus */
+ err = hw_perf_group_sched_in_torrent_check(tmp_all_events);
+
+ if (err) {
+ pr_err("%s, ERROR, Torrent sched in check failed\n",
+ __func__);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /*
+ * Can now accept the events from the tmp list into the list
+ * of current events for the Torrent PMU an add to list for
+ * each physical PMU.
+ */
+ accept_torrent_events(tmp_all_events, all_torrent_events);
+ }
+
+ /* Setup the group leader period info */
+ event->hw.config = event->attr.config;
+ event->hw.config_base = event->attr.config;
+ event->hw.event_base = 0; /* nest doesn't use the flags */
+ event->hw.last_period = event->hw.sample_period;
+ local64_set(&event->hw.period_left, event->hw.last_period);
+ ret = 0;
+
+out:
+ spin_unlock_irqrestore(&torrent_lock, lock_flags);
+ return ret;
+}
+
+void torrent_pmu_del(struct perf_event *event, int ef_flags)
+{
+ /*
+ * This function removes a single counter from a specific PMU.
+ * The function is called from the arch-independent perf code.
+ */
+
+ struct torrent_pmu_events *torrent_pmu_events;
+ struct torrent_pmu_counters *torrent_pmu;
+ struct hcall_data hcall_read;
+ struct hcall_data hcall_write;
+ struct unit_config *unit_config;
+ unsigned long lock_flags;
+ int i, pmu_type, events_released = 0, ret;
+ u64 event_code = event->hw.config;
+
+ /* Remove a single counter */
+ pmu_type = TORRENT_PMU_GET(event_code);
+
+ torrent_pmu = map_to_pmu(event_code);
+ if (!torrent_pmu)
+ return;
+
+ spin_lock_irqsave(&torrent_lock, lock_flags); /* Ensure only one CPU
+ * at a time access the
+ * HCall data structures
+ */
+ torrent_pmu_events = &all_torrent_events[pmu_type];
+
+ /* Set up variable hcall_write for the enable/disable call */
+ hcall_data_reset(&hcall_write);
+ torrent_pmu->enable_disable_cntr(0, torrent_pmu_events, &hcall_write);
+ do_hcall_pmc_write(&hcall_write);
+
+ /* Set up to do the PMD read call */
+ do_hcall_pmd_read(&hcall_read, all_torrent_events);
+
+ /* read the virtual counters */
+ torrent_pmu->read_pmd(torrent_pmu_events, &hcall_read);
+
+ /* Update the number of events being counted by the PMU */
+ unit_config = &torrent_pmu_events->unit;
+
+ /* Remove the event from the PMU list of events to count */
+ if (event->hw.idx >= 0)
+ /*
+ * Only need to release counter if event was assigned to a
+ * counter. If the event failed the constraint test, it was
+ * not assigned to a counter.
+ */
+ unit_config->cntr_state[event->hw.idx] = CNTR_FREE;
+
+ for (i = 0; i < torrent_pmu_events->n_events; i++) {
+ if (event == torrent_pmu_events->event[i]) {
+ release_torrent_pmu_counters(event, torrent_pmu_events,
+ pmu_type);
+
+ while (++i < torrent_pmu_events->n_events)
+ torrent_pmu_events->event[i - 1] =
+ torrent_pmu_events->event[i];
+
+ events_released++;
+ perf_event_update_userpage(event);
+ }
+ }
+
+ torrent_pmu_events->n_events -= events_released;
+ BUG_ON(torrent_pmu_events->n_events < 0);
+
+ if (pmu_type <= TORRENT_LAST_PBUS_PMU_ID)
+ /* Release the PBUS PMU */
+ pbus_pmu_reservation_cnt[pmu_type] =
+ torrent_pmu_events->n_events;
+
+ /* release PMU specific configuration values */
+ if (pmu_type == TORRENT_PBUS_MCD_ID)
+ unit_config->mcd_cntr_attr = 0x3;
+
+ if (pmu_type == TORRENT_PBUS_UTIL_ID) {
+ unit_config->bus_util_enable_mask = 0;
+ unit_config->bus_util_cntr_sel = BUS_UTIL_CNTR_SEL_AVAIL;
+ }
+
+ /* Enable the hardware counters on the specific PMU for the new list
+ * of events
+ */
+ ret = torrent_pmu->compute_pmc_regs(torrent_pmu_events, &hcall_write);
+ if (ret) {
+ pr_debug("%s:%d, compute_pmc_regs returned error = %d\n",
+ __func__, __LINE__, ret);
+ spin_unlock_irqrestore(&torrent_lock, lock_flags);
+ return;
+ }
+
+ torrent_pmu->enable_disable_cntr(1, torrent_pmu_events, &hcall_write);
+
+ do_hcall_pmc_write(&hcall_write);
+ spin_unlock_irqrestore(&torrent_lock, lock_flags);
+}
+
+static void poll_all_torrent_pmus(void)
+{
+ /*
+ * The counters need to be read and reset periodically to ensure the
+ * counters do not overflow. Most of the counters do not support
+ * interrupts so in general it is not possible to only update when the
+ * counters overflow. This routine will stop all the counters, read
+ * the physical PMD registers, update the virtual count for each event,
+ * reset the counter and then restart the counters.
+ */
+
+ int pmu_type, dump_ret;
+ unsigned long lock_flags;
+ struct torrent_pmu_counters *torrent_pmu;
+ struct torrent_pmu_events *torrent_pmu_events;
+ struct hcall_data hcall_read;
+ struct hcall_data hcall_write;
+
+ spin_lock_irqsave(&torrent_lock, lock_flags); /* Ensure only one CPU
+ * at a time access the
+ * HCall data structures
+ */
+ hcall_data_reset(&hcall_write);
+
+ /* Disable all of the active PMUs */
+ for (pmu_type = 0; pmu_type < TORRENT_NUM_PMU_TYPES; pmu_type++) {
+ torrent_pmu = torrent_pmu_cntrs[get_chip()][pmu_type];
+ torrent_pmu_events = &all_torrent_events[pmu_type];
+
+ if (torrent_pmu_events->n_events)
+ /* Only disable the PMUs with events being counted */
+ torrent_pmu->enable_disable_cntr(0, torrent_pmu_events,
+ &hcall_write);
+ }
+
+ if (hcall_write.num_regs != 0) {
+ do_hcall_pmc_write(&hcall_write);
+ hcall_data_reset(&hcall_write);
+ }
+
+ if (hcall_write.do_dump_mmio_perf_counters)
+ dump_ret = wrap_hcall_hfi_dump_info(torrent_chip_id,
+ &hcall_write.mmio_perf_counters);
+
+ /* Setup the HCall to enable all of the PMUs */
+ for (pmu_type = 0; pmu_type < TORRENT_NUM_PMU_TYPES; pmu_type++) {
+ torrent_pmu = torrent_pmu_cntrs[get_chip()][pmu_type];
+ torrent_pmu_events = &all_torrent_events[pmu_type];
+
+ if (torrent_pmu_events->n_events)
+ /* Only enablee the PMUs with events being counted */
+ torrent_pmu->enable_disable_cntr(1, torrent_pmu_events,
+ &hcall_write);
+ }
+
+ if (hcall_write.num_regs != 0) {
+ do_hcall_pmc_write(&hcall_write);
+ hcall_data_reset(&hcall_write);
+ }
+
+ if (hcall_write.do_dump_mmio_perf_counters)
+ dump_ret = wrap_hcall_hfi_dump_info(torrent_chip_id,
+ &hcall_write.mmio_perf_counters);
+
+ /* Read the initial value of all of the PMDs that will be enabled */
+ do_hcall_pmd_read(&hcall_read, all_torrent_events);
+
+ /* Now read the virtual counter PMD values from the physical registers
+ * returned in hcall_read. Set up the new PMC values.
+ */
+ for (pmu_type = 0; pmu_type < TORRENT_NUM_PMU_TYPES;
+ pmu_type++) {
+ torrent_pmu = torrent_pmu_cntrs[get_chip()][pmu_type];
+ torrent_pmu_events = &all_torrent_events[pmu_type];
+ /*
+ * Call pmd_write to update the prev_count values
+ * with the current values.
+ */
+ torrent_pmu->write_pmd(torrent_pmu_events, &hcall_read);
+ }
+
+ /* Enable the PMUs */
+ do_hcall_pmc_write(&hcall_write);
+ spin_unlock_irqrestore(&torrent_lock, lock_flags);
+}
+
+static enum hrtimer_restart poll_torrent_pmus(struct hrtimer *timer)
+{
+ poll_all_torrent_pmus();
+
+ poll_start_time = get_tb();
+ hrtimer_forward_now(timer, torrent_counter_poll_interval);
+ return HRTIMER_RESTART;
+}
+
+/*
+ * These typedefs are present mostly to clean up the declaration of
+ * torrent_pmu_initialize, keeping the lines from going beyond column 80.
+ */
+
+typedef int (*compute_pmc_regs_fptr)(struct torrent_pmu_events *pmu_events,
+ struct hcall_data *hcall_read);
+
+typedef int (*check_constraints_fptr)(struct torrent_pmu_events *pmu_events,
+ struct unit_config *unit_cfg);
+
+typedef void (*enable_disable_cntr_fptr)(int op,
+ struct torrent_pmu_events *pmu_events,
+ struct hcall_data *hcall_write);
+
+typedef int (*pmd_read_fptr)(struct torrent_pmu_events *torrent_pmu_events,
+ struct hcall_data *hcall_read);
+
+typedef void (*pmd_write_fptr)(struct torrent_pmu_events *torrent_pmu_events,
+ struct hcall_data *hcall_read);
+
+typedef int (*get_phys_pmd_reg_fptr)(u64 event_code);
+
+static int torrent_pmu_initialize(int chip, int pmu_type,
+ compute_pmc_regs_fptr compute_pmc_regs,
+ check_constraints_fptr check_constraints,
+ enable_disable_cntr_fptr enable_disable_cntr,
+ pmd_read_fptr pmd_read,
+ pmd_write_fptr pmd_write,
+ get_phys_pmd_reg_fptr get_phys_pmd_reg)
+{
+ struct torrent_pmu_counters *torrent_pmu;
+
+ /*
+ * This function allocates and initializes the data structure for the
+ * Torrent PMU. The PMU structure tracks the events, how many are on
+ * the PMU, what function to call to read/write the physical PMU data
+ * and control registers, etc.
+ *
+ * This routine is assumed to return 0 on no error or -ENOMEM.
+ */
+ torrent_pmu = torrent_pmu_cntrs[chip][pmu_type];
+
+ torrent_pmu->compute_pmc_regs = compute_pmc_regs;
+ torrent_pmu->check_constraints = check_constraints;
+ torrent_pmu->enable_disable_cntr = enable_disable_cntr;
+ torrent_pmu->read_pmd = pmd_read;
+ torrent_pmu->write_pmd = pmd_write;
+ torrent_pmu->get_phys_pmd_reg = get_phys_pmd_reg;
+
+ hrtimer_init(&torrent_poll_timer, CLOCK_MONOTONIC,
+ HRTIMER_MODE_REL);
+ torrent_poll_timer.function = &poll_torrent_pmus;
+
+ spin_lock_init(&torrent_lock);
+
+ return 0;
+}
+
+static const struct {
+ int counters;
+} per_pmu_info[TORRENT_NUM_PMU_TYPES] = {
+ [TORRENT_PBUS_WXYZ_ID] = {
+ .counters = MAX_CNTRS_PER_WXYZ_LINK_PMU
+ },
+ [TORRENT_PBUS_LL_ID] = {
+ .counters = MAX_CNTRS_PER_LL_LINK_PMU
+ },
+ [TORRENT_PBUS_MCD_ID] = {
+ .counters = MAX_CNTRS_PER_MCD_PMU
+ },
+ [TORRENT_PBUS_UTIL_ID] = {
+ .counters = MAX_CNTRS_PER_BUS_UTIL_PMU
+ },
+ [TORRENT_MMU_ID] = {
+ .counters = MAX_CNTRS_PER_MMU_PMU
+ },
+ [TORRENT_CAU_ID] = {
+ .counters = MAX_CNTRS_PER_CAU_PMU
+ }
+};
+
+static int alloc_torrent_pmu_cntrs(int num_torrent_chips)
+{
+ int chip, pmu_type;
+
+ /* Allocate space for the torrent_pmu_cntrs struct. */
+
+ /* Allocate space for the top level index. */
+ torrent_pmu_cntrs = kmalloc(sizeof(void *) * num_torrent_chips,
+ GFP_KERNEL);
+
+ if (!torrent_pmu_cntrs)
+ return -ENOMEM;
+
+ for (chip = 0; chip < num_torrent_chips; chip++) {
+ /* Allocate space for the node-level pointer array. */
+ torrent_pmu_cntrs[chip] = kmalloc(
+ sizeof(void *) * TORRENT_NUM_PMU_TYPES, GFP_KERNEL);
+
+ if (!torrent_pmu_cntrs[chip])
+ return -ENOMEM;
+
+ for (pmu_type = 0; pmu_type < TORRENT_NUM_PMU_TYPES;
+ pmu_type++) {
+ torrent_pmu_cntrs[chip][pmu_type] =
+ kmalloc(
+ sizeof(struct torrent_pmu_counters),
+ GFP_KERNEL);
+
+ if (!torrent_pmu_cntrs[chip][pmu_type]) {
+ pr_err("%s: ran out of memory for torrent_pmu_cntrs",
+ __func__);
+ return -ENOMEM;
+ }
+ }
+ }
+ return 0;
+}
+
+static void dealloc_torrent_pmu_cntrs(int num_torrent_chips)
+{
+ int chip, pmu_type;
+
+ /* Release the torrent_pmu_cntrs structs. */
+
+ for (chip = 0; chip < num_torrent_chips; chip++) {
+
+ for (pmu_type = 0; pmu_type < TORRENT_NUM_PMU_TYPES;
+ pmu_type++)
+ kfree(torrent_pmu_cntrs[chip][pmu_type]);
+
+ kfree(torrent_pmu_cntrs[chip]);
+ }
+ torrent_pmu_cntrs = kmalloc(sizeof(void *) * num_torrent_chips,
+ GFP_KERNEL);
+}
+
+static u64 get_torrent_chip_id(void)
+{
+ struct device_node *node;
+ u64 *lp;
+
+ node = of_find_node_by_name(NULL, "hfi-iohub");
+ if (!node) {
+ pr_err("%s: of_find_node_by_name"
+ " 'hfi-iohub' failed\n", __func__);
+ return -EINVAL;
+ }
+
+ lp = (u64 *)of_get_property(node, "reg", NULL);
+ if (!lp) {
+ pr_err("%s: of_get_property 'hfi-iohub/reg' failed\n",
+ __func__);
+ return -EINVAL;
+ }
+ return *lp;
+}
+
+static u64 get_hfi_unit_id(void)
+{
+ u8 octant, id;
+ u32 *p;
+ struct device_node *node;
+ struct device_node *child_node = NULL;
+
+ node = of_find_node_by_name(NULL, "hfi-iohub");
+ if (!node) {
+ pr_err("%s: of_find_node_by_name 'hfi-iohub' failed\n",
+ __func__);
+ return -EINVAL;
+ }
+ octant = (node->full_name[strlen(node->full_name) - 1] - '0');
+ if (octant > HFI_MAX_OCTANT) {
+ pr_err("%s: invalid hfi-iohub octant '%s'\n",
+ __func__, node->full_name);
+ return -EINVAL;
+ }
+
+ id = octant << HFI_SHIFT_OCTANT;
+
+ while ((child_node = of_get_next_child(node, child_node))) {
+ p = (u32 *)of_get_property(child_node, "reg", NULL);
+ if (!p) {
+ pr_err("%s: of_get_property 'reg' failed\n", __func__);
+ return -EINVAL;
+ }
+ if (id == (u8)*p)
+ return id;
+ }
+ pr_err("%s: can not find child\n", __func__);
+ return -EINVAL;
+}
+
+void torrent_pmu_start(struct perf_event *event, int ef_flags)
+{
+ /* Don't support enable/disable on individual hardware counters */
+
+ if (!event->hw.idx || !event->hw.sample_period)
+ return;
+
+ if (!(event->hw.state & PERF_HES_STOPPED))
+ return;
+
+ event->hw.state = 0;
+
+}
+
+void torrent_pmu_stop(struct perf_event *event, int ef_flags)
+{
+
+ if (!event->hw.idx || !event->hw.sample_period)
+ return;
+
+ if (event->hw.state & PERF_HES_STOPPED)
+ return;
+
+ event->hw.state |= PERF_HES_STOPPED | PERF_HES_UPTODATE;
+}
+
+void torrent_pmu_read(struct perf_event *event)
+{
+
+ s64 delta, prev, value;
+ unsigned long lock_flags;
+ u8 virt_cntr;
+ int pmu_type;
+ struct hcall_data hcall_read;
+ struct torrent_pmu_events *torrent_pmu_events;
+ struct torrent_pmu_counters *torrent_pmu;
+ u64 event_code = event->hw.config;
+
+ if (!event->hw.idx)
+ return;
+
+ spin_lock_irqsave(&torrent_lock, lock_flags); /* Ensure only one CPU
+ * at a time access the
+ * HCall data structures
+ */
+ pmu_type = TORRENT_PMU_GET(event_code);
+ torrent_pmu = map_to_pmu(event_code);
+ torrent_pmu_events = &all_torrent_events[pmu_type];
+
+ /* Set up variable hcall_write for the enable/disable call. */
+ do_hcall_pmd_read(&hcall_read, all_torrent_events);
+ virt_cntr = TORRENT_VIRT_CTR_GET(event->hw.config);
+
+ prev = local64_read(&event->hw.prev_count);
+ value = torrent_pmu->read_pmd(torrent_pmu_events, &hcall_read);
+
+ /* calculate/save the count */
+ delta = value - prev;
+ local64_add(delta, &event->count);
+ local64_set(&event->hw.prev_count, value);
+
+ local64_add(delta, &event->count);
+ local64_sub(delta, &event->hw.period_left);
+ spin_unlock_irqrestore(&torrent_lock, lock_flags);
+}
+
+/*
+ * The transaction scheduling is done across all of the physical PMUs within
+ * the Torrent chip. When using transaction scheduling, the compatibility
+ * check is skipped for the events as each event is being added.. Once the
+ * entire set of events has been added, the constraint check is done for all
+ * of the events that have been added.
+ *
+ * Set the flag to make pmu::enable() not perform the constraint check.
+ */
+void torrent_pmu_start_txn(struct pmu *pmu)
+{
+ perf_pmu_disable(pmu);
+
+ torrent_txn.group_flag |= PERF_EVENT_TXN;
+ copy_all_events(all_torrent_events, txn_all_events);
+}
+
+void torrent_pmu_cancel_txn(struct pmu *pmu)
+{
+ perf_pmu_disable(pmu);
+
+ torrent_txn.group_flag &= PERF_EVENT_TXN;
+}
+
+int torrent_pmu_commit_txn(struct pmu *pmu)
+{
+ /*
+ * Do constraint check for new events across all physical PMUs within
+ * the Torrent chip.
+ */
+ int err;
+ unsigned long lock_flags;
+
+ spin_lock_irqsave(&torrent_lock, lock_flags);
+
+ /* check constraints for all pmus */
+ err = hw_perf_group_sched_in_torrent_check(txn_all_events);
+
+ if (err) {
+ pr_err("%s, ERROR, Torrent sched in check failed\n", __func__);
+ spin_unlock_irqrestore(&torrent_lock, lock_flags);
+ return -EAGAIN;
+ }
+
+ /*
+ * Can now accept the events from the txn list into the list of
+ * current events for the Torrent PMU an add to list for each physical
+ * PMU.
+ */
+
+ accept_torrent_events(txn_all_events, all_torrent_events);
+ spin_unlock_irqrestore(&torrent_lock, lock_flags);
+
+ perf_pmu_enable(pmu);
+ return 0;
+}
+
+int torrent_pmu_event_init(struct perf_event *event)
+{
+ int pmu_type, index, err, rtn = 0;
+ unsigned long flags;
+ struct torrent_pmu_events *torrent_pmu_events;
+ struct torrent_pmu_events tmp_all_events[TORRENT_NUM_PMU_TYPES];
+
+ /*
+ * If this is in a group, check if it can go on with all the
+ * other hardware events in the group. We assume the event
+ * hasn't been linked into its leader's sibling list at this point.
+ */
+
+ local_irq_save(flags);
+ switch (event->attr.type) {
+ case PERF_TYPE_RAW:
+ if (!IS_TORRENT_EVENT(event->attr.config)) {
+ rtn = -ENOENT;
+ goto out;
+ }
+ break;
+
+ default:
+ rtn = -ENOENT;
+ goto out;
+ }
+
+ /*
+ * Use an empty data structure to see if the events are self
+ * consistent.
+ */
+ initialize_event_struct(tmp_all_events);
+
+ if (event->group_leader != event) {
+ /*
+ * Use a temporary array of pmu events in case the entire
+ * group needs to be rejected.
+ */
+ collect_events(event->group_leader, tmp_all_events);
+ }
+
+ /* Include the group leader event to list of existing nest events */
+ pmu_type = TORRENT_PMU_GET(event->attr.config);
+
+ if (pmu_type < 0) {
+ pr_err("%s: ERROR, pmu_type < 0\n", __func__);
+ rtn = -EINVAL;
+ goto out;
+ }
+
+ torrent_pmu_events = &tmp_all_events[pmu_type];
+ index = torrent_pmu_events->n_events + torrent_pmu_events->n_add;
+ if (index >= get_max_nest_events(pmu_type)) {
+ pr_err("%s: ERROR, index out of range\n",
+ __func__);
+ rtn = -EINVAL;
+ goto out;
+ }
+ torrent_pmu_events->event[index] = event;
+ torrent_pmu_events->n_add++;
+
+ /* check constraints of new events */
+ err = hw_perf_group_sched_in_torrent_check(tmp_all_events);
+ if (err) {
+ pr_err("%s, ERROR, Torrent sched in check failed\n", __func__);
+ rtn = -EINVAL;
+ goto out;
+ }
+
+out:
+ local_irq_restore(flags);
+ return rtn;
+}
+
+struct pmu torrent_pmu = {
+ .pmu_enable = torrent_pmu_enable,
+ .pmu_disable = torrent_pmu_disable,
+ .event_init = torrent_pmu_event_init,
+ .add = torrent_pmu_add,
+ .del = torrent_pmu_del,
+ .start = torrent_pmu_start,
+ .stop = torrent_pmu_stop,
+ .read = torrent_pmu_read,
+ .start_txn = torrent_pmu_start_txn,
+ .cancel_txn = torrent_pmu_cancel_txn,
+ .commit_txn = torrent_pmu_commit_txn,
+};
+
+int __init torrent_pmu_init(void)
+{
+ int chip, ret;
+
+ /*
+ * Set up the structure for each of the PMU types to track what
+ * events are being counted by that PMU.
+ */
+
+ /* Attempt to determine the correct Torrent chip id, the HFI unit id
+ * and lock the counter facility. If we can't do these things, then
+ * we are not on a P7IH system, exit and do not setup the Torrent PMU.
+ */
+ torrent_chip_id = get_torrent_chip_id();
+ if (torrent_chip_id < 0)
+ /* torrent_chip_id will be set to the errno if not valid */
+ return torrent_chip_id;
+
+ hfi_unit_id = get_hfi_unit_id();
+ if (hfi_unit_id < 0)
+ /* torrent_chip_id will be set to the errno if not valid */
+ return torrent_chip_id;
+
+ /*
+ * Lock the counter facility for this partition's access. Note that
+ * we don't ever unlock it.
+ */
+
+ ret = PLPAR_HCALL(H_TOR_ACCESS_PMU_SCOM_REGS, torrent_chip_id,
+ PBUS_HCALL_LOCK, 0, (unsigned long)NULL);
+ if (ret != H_SUCCESS) {
+ pr_err("%s, ERROR, lock counter facility HCall returned an error %d\n",
+ __func__, ret);
+ return -EINVAL;
+ }
+
+ /*
+ * Looks like we are on a Torrent system, allocate space for the
+ * torrent_pmu_cntrs struct.
+ */
+ pr_debug("Number of Torrent chips %d\n", num_torrent_chips);
+ ret = alloc_torrent_pmu_cntrs(num_torrent_chips);
+ if (ret) {
+ pr_err("%s,%d: Failed to allocate torrent_pmu_cntrs, retval = %d\n",
+ __func__, __LINE__, ret);
+ return ret;
+ }
+
+ /* Align the 4K buffer used for HCalls */
+ aligned_buffer = (u64 *)((u64)unaligned_buffer + 0xFFFULL);
+ aligned_buffer = (u64 *)((u64)aligned_buffer & ~0xFFFULL);
+
+ /* We currently only support one Torrent chip */
+ chip = get_chip();
+
+ pr_debug("%s: set up Torrent chip %d WXYZ link PMU\n", __func__, chip);
+
+ if (torrent_pmu_initialize(chip, TORRENT_PBUS_WXYZ_ID,
+ wxyz_link_compute_pmc_reg,
+ wxyz_link_pmu_check_constraints,
+ wxyz_link_enable_disable_hw_cntr,
+ wxyz_link_pmd_read,
+ wxyz_link_pmd_write,
+ wxyz_link_get_phys_pmd_reg)) {
+ pr_err("%s: ERROR WXYZ Link PMU initialize failed\n",
+ __func__);
+ goto out;
+ }
+
+ if (torrent_pmu_initialize(chip, TORRENT_PBUS_LL_ID,
+ ll_link_compute_pmc_reg,
+ ll_link_pmu_check_constraints,
+ ll_link_enable_disable_hw_cntr,
+ ll_link_pmd_read,
+ ll_link_pmd_write,
+ ll_link_get_phys_pmd_reg)) {
+ pr_err("%s: ERROR LL Link PMU initialize failed\n",
+ __func__);
+ goto out;
+ }
+
+ if (torrent_pmu_initialize(chip, TORRENT_PBUS_MCD_ID,
+ mcd_compute_pmc_reg,
+ mcd_pmu_check_constraints,
+ mcd_enable_disable_hw_cntr,
+ mcd_pmd_read,
+ mcd_pmd_write,
+ mcd_get_phys_pmd_reg)) {
+ pr_err("%s: ERROR MCD PMU initialize failed\n",
+ __func__);
+ goto out;
+ }
+
+ if (torrent_pmu_initialize(chip, TORRENT_PBUS_UTIL_ID,
+ bus_util_compute_pmc_reg,
+ bus_util_pmu_check_constraints,
+ bus_util_enable_disable_hw_cntr,
+ bus_util_pmd_read,
+ bus_util_pmd_write,
+ bus_util_get_phys_pmd_reg)) {
+ pr_err("%s: ERROR BUS UTIL PMU initialize failed\n",
+ __func__);
+ goto out;
+ }
+
+ if (torrent_pmu_initialize(chip, TORRENT_MMU_ID,
+ mmu_compute_pmc_reg,
+ mmu_pmu_check_constraints,
+ mmu_enable_disable_hw_cntr,
+ mmu_pmd_read,
+ mmu_pmd_write,
+ mmu_get_phys_pmd_reg)) {
+ pr_err("%s: ERROR MMU PMU initialize failed\n",
+ __func__);
+ goto out;
+ }
+
+ if (torrent_pmu_initialize(chip, TORRENT_CAU_ID,
+ cau_compute_pmc_reg,
+ cau_pmu_check_constraints,
+ cau_enable_disable_hw_cntr,
+ cau_pmd_read,
+ cau_pmd_write,
+ cau_get_phys_pmd_reg)) {
+ pr_err("%s: ERROR CAU PMU initialize failed\n",
+ __func__);
+ goto out;
+ }
+
+ /* Initialize the structure to track the events being counted by
+ * the physical Torrent PMUs.
+ */
+ initialize_event_struct(all_torrent_events);
+
+ /* Initialize the poll interval value. It's used as a constant
+ * elsewhere.
+ */
+ torrent_counter_poll_interval = ktime_set(POLLING_INTERVAL_SEC,
+ POLLING_INTERVAL_NS);
+ ret = perf_pmu_register(&torrent_pmu, "torrent", -1);
+ return 0;
+out:
+ dealloc_torrent_pmu_cntrs(num_torrent_chips);
+ return -ENOMEM;
+
+}
+
+arch_initcall(torrent_pmu_init);
diff --git a/arch/powerpc/platforms/pseries/Kconfig b/arch/powerpc/platforms/pseries/Kconfig
index 71af4c5..402a029 100644
--- a/arch/powerpc/platforms/pseries/Kconfig
+++ b/arch/powerpc/platforms/pseries/Kconfig
@@ -26,6 +26,11 @@ config PPC_SPLPAR
processors, that is, which share physical processors between
two or more partitions.
+config PPC_P7IH
+ bool "P7IH (w/Torrent interconnect)"
+ depends on PPC_PSERIES
+ default n
+
config EEH
bool "PCI Extended Error Handling (EEH)" if EXPERT
depends on PPC_PSERIES && PCI
--
1.7.0.4
^ permalink raw reply related
* [PATCH] mtd: eLBC NAND: update ecc_stats.corrected when lteccr available
From: Michael Hench @ 2011-07-26 13:45 UTC (permalink / raw)
To: scottwood, linux-mtd, linuxppc-dev, mhench, mlcreech
update ecc_stats.corrected if LTECCR register is available.
Signed-off-by: Michael Hench <MichaelHench@gmail.com>
---
diff -purN orig/drivers/mtd/nand/fsl_elbc_nand.c
linux-3.0/drivers/mtd/nand/fsl_elbc_nand.c
--- orig/drivers/mtd/nand/fsl_elbc_nand.c 2011-07-25 14:50:56.838326055 -0500
+++ linux-3.0/drivers/mtd/nand/fsl_elbc_nand.c 2011-07-25
14:48:35.680940052 -0500
@@ -256,6 +256,25 @@ static int fsl_elbc_run_command(struct m
return -EIO;
}
+ if(chip->ecc.mode != NAND_ECC_HW)
+ return(0);
+
+ if(elbc_fcm_ctrl->read_bytes == mtd->writesize + mtd->oobsize) {
+ uint32_t lteccr = in_be32(&lbc->lteccr);
+ /*
+ * if command was a full page read and the ELBC
+ * has the LTECCR register, then bits 12-15 (ppc order) of
+ * LTECCR indicate which 512 byte sub-pages had corrected errors.
+ * bits 28-31 are uncorrectable errors, marked elsewhere.
+ * for small page nand only 1 bit is used.
+ * if the ELBC doesn't have the lteccr register it reads 0
+ */
+ if(lteccr & 0x000F000F)
+ out_be32(&lbc->lteccr, 0x000F000F); /* clear lteccr */
+ if(lteccr & 0x000F0000)
+ mtd->ecc_stats.corrected++;
+ }
+
return 0;
}
--
^ permalink raw reply
* RE: [PATCH 2/2 v2] eSDHC: Fix errors when booting kernel with fsl esdhc
From: Zang Roy-R61911 @ 2011-07-26 10:29 UTC (permalink / raw)
To: Anton Vorontsov
Cc: Wood Scott-B07421, Xu Lei-B33228, linux-mmc@vger.kernel.org,
akpm@linux-foundation.org, linuxppc-dev@lists.ozlabs.org
In-Reply-To: <20110722101512.GA1576@oksana.dev.rtsoft.ru>
DQoNCj4gLS0tLS1PcmlnaW5hbCBNZXNzYWdlLS0tLS0NCj4gRnJvbTogbGludXgtbW1jLW93bmVy
QHZnZXIua2VybmVsLm9yZyBbbWFpbHRvOmxpbnV4LW1tYy1vd25lckB2Z2VyLmtlcm5lbC5vcmdd
DQo+IE9uIEJlaGFsZiBPZiBBbnRvbiBWb3JvbnRzb3YNCj4gU2VudDogRnJpZGF5LCBKdWx5IDIy
LCAyMDExIDE4OjE1IFBNDQo+IFRvOiBaYW5nIFJveS1SNjE5MTENCj4gQ2M6IGxpbnV4LW1tY0B2
Z2VyLmtlcm5lbC5vcmc7IGxpbnV4cHBjLWRldkBsaXN0cy5vemxhYnMub3JnOyBha3BtQGxpbnV4
LQ0KPiBmb3VuZGF0aW9uLm9yZzsgWHUgTGVpLUIzMzIyODsgS3VtYXIgR2FsYQ0KPiBTdWJqZWN0
OiBSZTogW1BBVENIIDIvMiB2Ml0gZVNESEM6IEZpeCBlcnJvcnMgd2hlbiBib290aW5nIGtlcm5l
bCB3aXRoIGZzbCBlc2RoYw0KPiANCj4gT24gRnJpLCBKdWwgMjIsIDIwMTEgYXQgMDY6MTU6MTdQ
TSArMDgwMCwgUm95IFphbmcgd3JvdGU6DQo+IFsuLi5dDQo+ID4gIAlpZiAoaG9zdC0+dmVyc2lv
biA+PSBTREhDSV9TUEVDXzIwMCkgew0KPiA+IC0JCWN0cmwgPSBzZGhjaV9yZWFkYihob3N0LCBT
REhDSV9IT1NUX0NPTlRST0wpOw0KPiA+IC0JCWN0cmwgJj0gflNESENJX0NUUkxfRE1BX01BU0s7
DQo+ID4gLQkJaWYgKChob3N0LT5mbGFncyAmIFNESENJX1JFUV9VU0VfRE1BKSAmJg0KPiA+IC0J
CQkoaG9zdC0+ZmxhZ3MgJiBTREhDSV9VU0VfQURNQSkpDQo+ID4gLQkJCWN0cmwgfD0gU0RIQ0lf
Q1RSTF9BRE1BMzI7DQo+ID4gLQkJZWxzZQ0KPiA+IC0JCQljdHJsIHw9IFNESENJX0NUUkxfU0RN
QTsNCj4gPiAtCQlzZGhjaV93cml0ZWIoaG9zdCwgY3RybCwgU0RIQ0lfSE9TVF9DT05UUk9MKTsN
Cj4gPiArCQlpZiAoaG9zdC0+cXVpcmtzICYgU0RIQ0lfUVVJUktfUU9SSVFfUFJPQ1RMX1dFSVJE
KSB7DQo+ID4gKyNkZWZpbmUgRVNESENJX1BST0NUTF9ETUFTX01BU0sJCTB4MDAwMDAzMDANCj4g
PiArI2RlZmluZSBFU0RIQ0lfUFJPQ1RMX0FETUEzMgkJMHgwMDAwMDIwMA0KPiA+ICsjZGVmaW5l
IEVTREhDSV9QUk9DVExfU0RNQQkJMHgwMDAwMDAwMA0KPiA+ICsJCQljdHJsID0gc2RoY2lfcmVh
ZGwoaG9zdCwgU0RIQ0lfSE9TVF9DT05UUk9MKTsNCj4gPiArCQkJY3RybCAmPSB+RVNESENJX1BS
T0NUTF9ETUFTX01BU0s7DQo+ID4gKwkJCWlmICgoaG9zdC0+ZmxhZ3MgJiBTREhDSV9SRVFfVVNF
X0RNQSkgJiYNCj4gPiArCQkJCShob3N0LT5mbGFncyAmIFNESENJX1VTRV9BRE1BKSkNCj4gPiAr
CQkJCWN0cmwgfD0gRVNESENJX1BST0NUTF9BRE1BMzI7DQo+ID4gKwkJCWVsc2UNCj4gPiArCQkJ
CWN0cmwgfD0gRVNESENJX1BST0NUTF9TRE1BOw0KPiA+ICsJCQlzZGhjaV93cml0ZWwoaG9zdCwg
Y3RybCwgU0RIQ0lfSE9TVF9DT05UUk9MKTsNCj4gPiArCQl9IGVsc2Ugew0KPiA+ICsJCQljdHJs
ID0gc2RoY2lfcmVhZGIoaG9zdCwgU0RIQ0lfSE9TVF9DT05UUk9MKTsNCj4gPiArCQkJY3RybCAm
PSB+U0RIQ0lfQ1RSTF9ETUFfTUFTSzsNCj4gPiArCQkJaWYgKChob3N0LT5mbGFncyAmIFNESENJ
X1JFUV9VU0VfRE1BKSAmJg0KPiA+ICsJCQkJKGhvc3QtPmZsYWdzICYgU0RIQ0lfVVNFX0FETUEp
KQ0KPiA+ICsJCQkJY3RybCB8PSBTREhDSV9DVFJMX0FETUEzMjsNCj4gPiArCQkJZWxzZQ0KPiA+
ICsJCQkJY3RybCB8PSBTREhDSV9DVFJMX1NETUE7DQo+ID4gKwkJCXNkaGNpX3dyaXRlYihob3N0
LCBjdHJsLCBTREhDSV9IT1NUX0NPTlRST0wpOw0KPiANCj4gV2UgdHJ5IHRvIG5vdCBwb2xsdXRl
IGdlbmVyaWMgc2RoY2kuYyBkcml2ZXIgd2l0aCBjaGlwLXNwZWNpZmljDQo+IHF1aXJrcy4NCj4g
DQo+IE1heWJlIHlvdSBjYW4gZG8gdGhlIGZpeHVwcyB2aWEgSU8gYWNjZXNzb3JzPyBPciBieSBp
bnRyb2R1Y2luZw0KPiBzb21lIGFkZGl0aW9uYWwgc2RoY2kgb3A/DQo+IA0KPiBbLi4uXQ0KPiA+
ICAJaWYgKHBvd2VyICE9ICh1bnNpZ25lZCBzaG9ydCktMSkgew0KPiA+ICAJCXN3aXRjaCAoMSA8
PCBwb3dlcikgew0KPiA+ICsjZGVmaW5lCUVTREhDSV9GU0xfUE9XRVJfTUFTSwkweDQwDQo+ID4g
KyNkZWZpbmUJRVNESENJX0ZTTF9QT1dFUl8xODAJMHgwMA0KPiA+ICsjZGVmaW5lCUVTREhDSV9G
U0xfUE9XRVJfMzAwCTB4NDANCj4gDQo+IFNhbWUgaGVyZS4gVGhlIGRyaXZlciB3aWxsIHJvdCBx
dWlja2x5IGlmIGV2ZXJ5b25lIHdvdWxkIHN0YXJ0DQo+IHB1dHRpbmcgY2hpcC1zcGVjaWZpYyBx
dWlya3MgaW50byBzZGhjaS5jLiBQbGVhc2UgZG9uJ3QuDQpJTyBhY2Nlc3NvcnMgb3Igc2RoY2kg
b3AgbWlnaHQgd29yaywgYnV0IGl0IHdpbGwgbG9vayB1Z2x5IA0KYW5kIGxlc3MgcGVyZm9ybWFu
Y2UgZWZmaWNpZW50IGZvciBpbyBhY2Nlc3MuDQoNCklmIHlvdSBzZWFyY2ggc2RoY2kuYywgeW91
IHdpbGwgc2VlIG90aGVyIGJvYXJkIHNwZWNpZmljIHF1aXJrcy4NClRoYW5rcy4NClJveQ0K
^ permalink raw reply
* RE: [PATCH 4/4] edac/85xx: PCI/PCIE error interrupt edac support.
From: Xie Shaohui-B21989 @ 2011-07-26 6:51 UTC (permalink / raw)
To: linuxppc-dev@lists.ozlabs.org, Kumar Gala
Cc: mm-commits@vger.kernel.org, avorontsov@mvista.com,
Jiang Kai-B18973, akpm@linux-foundation.org, davem@davemloft.net
In-Reply-To: <1311244404-4463-1-git-send-email-Shaohui.Xie@freescale.com>
I've verified this patch can apply for galak/powerpc.git 'next' branch with=
no change.
Best Regards,=20
Shaohui Xie=20
>-----Original Message-----
>From: Xie Shaohui-B21989
>Sent: Thursday, July 21, 2011 6:33 PM
>To: linuxppc-dev@lists.ozlabs.org
>Cc: Gala Kumar-B11780; mm-commits@vger.kernel.org; avorontsov@mvista.com;
>davem@davemloft.net; grant.likely@secretlab.ca; akpm@linux-foundation.org;
>Jiang Kai-B18973; Kumar Gala; Xie Shaohui-B21989
>Subject: [PATCH 4/4] edac/85xx: PCI/PCIE error interrupt edac support.
>
>From: Kai.Jiang <Kai.Jiang@freescale.com>
>
>Add pcie error interrupt edac support for mpc85xx and p4080.
>mpc85xx uses the legacy interrupt report mechanism - the error interrupts
>are reported directly to mpic. While, p4080 attaches most of error
>interrupts to interrupt 0. And report error interrupt to mpic via
>interrupt 0. This patch can handle both of them.
>
>
>Due to the error management register offset and definition
>
>difference between pci and pcie, use ccsr_pci structure to merge pci and
>pcie edac code into one.
>
>Signed-off-by: Kai.Jiang <Kai.Jiang@freescale.com>
>Signed-off-by: Kumar Gala <galak@kernel.crashing.org>
>Signed-off-by: Shaohui Xie <Shaohui.Xie@freescale.com>
>---
> drivers/edac/mpc85xx_edac.c | 239 ++++++++++++++++++++++++++++++++------
>----
> drivers/edac/mpc85xx_edac.h | 17 +--
> 2 files changed, 188 insertions(+), 68 deletions(-)
>
>diff --git a/drivers/edac/mpc85xx_edac.c b/drivers/edac/mpc85xx_edac.c
>index b048a5f..dde156f 100644
>--- a/drivers/edac/mpc85xx_edac.c
>+++ b/drivers/edac/mpc85xx_edac.c
>@@ -1,5 +1,6 @@
> /*
> * Freescale MPC85xx Memory Controller kenel module
>+ * Copyright (c) 2011 Freescale Semiconductor, Inc.
> *
> * Author: Dave Jiang <djiang@mvista.com>
> *
>@@ -21,6 +22,8 @@
>
> #include <linux/of_platform.h>
> #include <linux/of_device.h>
>+#include <include/asm/pci.h>
>+#include <sysdev/fsl_pci.h>
> #include "edac_module.h"
> #include "edac_core.h"
> #include "mpc85xx_edac.h"
>@@ -34,14 +37,6 @@ static int edac_mc_idx; static u32
>orig_ddr_err_disable; static u32 orig_ddr_err_sbe;
>
>-/*
>- * PCI Err defines
>- */
>-#ifdef CONFIG_PCI
>-static u32 orig_pci_err_cap_dr;
>-static u32 orig_pci_err_en;
>-#endif
>-
> static u32 orig_l2_err_disable;
> #ifdef CONFIG_FSL_SOC_BOOKE
> static u32 orig_hid1[2];
>@@ -151,37 +146,52 @@ static void mpc85xx_pci_check(struct
>edac_pci_ctl_info *pci) {
> struct mpc85xx_pci_pdata *pdata =3D pci->pvt_info;
> u32 err_detect;
>+ struct ccsr_pci *reg =3D pdata->pci_reg;
>+
>+ err_detect =3D in_be32(&pdata->pci_reg->pex_err_dr);
>+
>+ if (pdata->pcie_flag) {
>+ printk(KERN_ERR "PCIE error(s) detected\n");
>+ printk(KERN_ERR "PCIE ERR_DR register: 0x%08x\n", err_detect);
>+ printk(KERN_ERR "PCIE ERR_CAP_STAT register: 0x%08x\n",
>+ in_be32(®->pex_err_cap_stat));
>+ printk(KERN_ERR "PCIE ERR_CAP_R0 register: 0x%08x\n",
>+ in_be32(®->pex_err_cap_r0));
>+ printk(KERN_ERR "PCIE ERR_CAP_R1 register: 0x%08x\n",
>+ in_be32(®->pex_err_cap_r1));
>+ printk(KERN_ERR "PCIE ERR_CAP_R2 register: 0x%08x\n",
>+ in_be32(®->pex_err_cap_r2));
>+ printk(KERN_ERR "PCIE ERR_CAP_R3 register: 0x%08x\n",
>+ in_be32(®->pex_err_cap_r3));
>+ } else {
>+ /* master aborts can happen during PCI config cycles */
>+ if (!(err_detect & ~(PCI_EDE_MULTI_ERR | PCI_EDE_MST_ABRT))) {
>+ out_be32(®->pex_err_dr, err_detect);
>+ return;
>+ }
>
>- err_detect =3D in_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_DR);
>-
>- /* master aborts can happen during PCI config cycles */
>- if (!(err_detect & ~(PCI_EDE_MULTI_ERR | PCI_EDE_MST_ABRT))) {
>- out_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_DR, err_detect);
>- return;
>+ printk(KERN_ERR "PCI error(s) detected\n");
>+ printk(KERN_ERR "PCI/X ERR_DR register: 0x%08x\n", err_detect);
>+ printk(KERN_ERR "PCI/X ERR_ATTRIB register: 0x%08x\n",
>+ in_be32(®->pex_err_attrib));
>+ printk(KERN_ERR "PCI/X ERR_ADDR register: 0x%08x\n",
>+ in_be32(®->pex_err_disr));
>+ printk(KERN_ERR "PCI/X ERR_EXT_ADDR register: 0x%08x\n",
>+ in_be32(®->pex_err_ext_addr));
>+ printk(KERN_ERR "PCI/X ERR_DL register: 0x%08x\n",
>+ in_be32(®->pex_err_dl));
>+ printk(KERN_ERR "PCI/X ERR_DH register: 0x%08x\n",
>+ in_be32(®->pex_err_dh));
>+
>+ if (err_detect & PCI_EDE_PERR_MASK)
>+ edac_pci_handle_pe(pci, pci->ctl_name);
>+
>+ if ((err_detect & ~PCI_EDE_MULTI_ERR) & ~PCI_EDE_PERR_MASK)
>+ edac_pci_handle_npe(pci, pci->ctl_name);
> }
>
>- printk(KERN_ERR "PCI error(s) detected\n");
>- printk(KERN_ERR "PCI/X ERR_DR register: %#08x\n", err_detect);
>-
>- printk(KERN_ERR "PCI/X ERR_ATTRIB register: %#08x\n",
>- in_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_ATTRIB));
>- printk(KERN_ERR "PCI/X ERR_ADDR register: %#08x\n",
>- in_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_ADDR));
>- printk(KERN_ERR "PCI/X ERR_EXT_ADDR register: %#08x\n",
>- in_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_EXT_ADDR));
>- printk(KERN_ERR "PCI/X ERR_DL register: %#08x\n",
>- in_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_DL));
>- printk(KERN_ERR "PCI/X ERR_DH register: %#08x\n",
>- in_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_DH));
>-
> /* clear error bits */
>- out_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_DR, err_detect);
>-
>- if (err_detect & PCI_EDE_PERR_MASK)
>- edac_pci_handle_pe(pci, pci->ctl_name);
>-
>- if ((err_detect & ~PCI_EDE_MULTI_ERR) & ~PCI_EDE_PERR_MASK)
>- edac_pci_handle_npe(pci, pci->ctl_name);
>+ out_be32(®->pex_err_dr, err_detect);
> }
>
> static irqreturn_t mpc85xx_pci_isr(int irq, void *dev_id) @@ -190,7
>+200,7 @@ static irqreturn_t mpc85xx_pci_isr(int irq, void *dev_id)
> struct mpc85xx_pci_pdata *pdata =3D pci->pvt_info;
> u32 err_detect;
>
>- err_detect =3D in_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_DR);
>+ err_detect =3D in_be32(&pdata->pci_reg->pex_err_dr);
>
> if (!err_detect)
> return IRQ_NONE;
>@@ -200,11 +210,99 @@ static irqreturn_t mpc85xx_pci_isr(int irq, void
>*dev_id)
> return IRQ_HANDLED;
> }
>
>+#define MPC85XX_MPIC_EIMR0 0x3910
>+/*
>+ * This function is for error interrupt ORed mechanism.
>+ * This mechanism attaches most functions' error interrupts to interrupt
>0.
>+ * And report error interrupt to mpic via interrupt 0.
>+ * EIMR0 - Error Interrupt Mask Register 0.
>+ *
>+ * This function check whether the device support error interrupt ORed
>+ * mechanism via device tree. If supported, umask pcie error interrupt
>+ * bit in EIMR0.
>+ */
>+static int mpc85xx_err_int_en(struct device *op) {
>+ u32 *int_cell =3D NULL;
>+ struct device_node *np =3D NULL;
>+ void __iomem *mpic_base =3D NULL;
>+ u32 reg_tmp =3D 0;
>+ u32 int_len =3D 0;
>+ struct resource r;
>+ int res =3D 0;
>+
>+ if (!op->of_node)
>+ return -EINVAL;
>+ /*
>+ * Unmask pcie error interrupt bit in EIMR0
>+ * extend interrupt specifier has 4 cells. For the 3rd cell:
>+ * 0 -- normal interrupt; 1 -- error interrupt.
>+ */
>+ int_cell =3D (u32 *)of_get_property(op->of_node, "interrupts",
>&int_len);
>+ if ((int_len/sizeof(u32)) =3D=3D 4) {
>+ /* soc has error interrupt integration handling mechanism */
>+ if (*(int_cell + 2) =3D=3D 1) {
>+ np =3D of_find_node_by_type(NULL, "open-pic");
>+
>+ if (of_address_to_resource(np, 0, &r)) {
>+ printk(KERN_ERR
>+ "%s:Failed to map mpic regs\n", __func__);
>+ of_node_put(np);
>+ res =3D -ENOMEM;
>+ goto err;
>+ }
>+
>+ if (!request_mem_region(r.start,
>+ r.end - r.start + 1, "mpic")) {
>+ printk(KERN_ERR
>+ "%s:Error while requesting mem region\n",
>+ __func__);
>+ res =3D -EBUSY;
>+ goto err;
>+ }
>+
>+ mpic_base =3D ioremap(r.start, r.end - r.start + 1);
>+ if (!mpic_base) {
>+ printk(KERN_ERR
>+ "%s:Unable to map mpic regs\n", __func__);
>+ res =3D -ENOMEM;
>+ goto err_ioremap;
>+ }
>+
>+ reg_tmp =3D in_be32(mpic_base + MPC85XX_MPIC_EIMR0);
>+ out_be32(mpic_base + MPC85XX_MPIC_EIMR0,
>+ reg_tmp & ~(1 << (31 - *(int_cell + 3))));
>+ iounmap(mpic_base);
>+ release_mem_region(r.start, r.end - r.start + 1);
>+ of_node_put(np);
>+ }
>+ }
>+
>+ return 0;
>+err_ioremap:
>+ release_mem_region(r.start, r.end - r.start + 1);
>+err:
>+
>+ return res;
>+}
>+
>+static int mpc85xx_pcie_find_capability(struct device_node *np) {
>+ struct pci_controller *hose;
>+ if (!np)
>+ return -EINVAL;
>+
>+ hose =3D pci_find_hose_for_OF_device(np);
>+ return early_find_capability(hose, hose->bus->number,
>+ 0, PCI_CAP_ID_EXP);
>+}
>+
> static int __devinit mpc85xx_pci_err_probe(struct platform_device *op) {
> struct edac_pci_ctl_info *pci;
> struct mpc85xx_pci_pdata *pdata;
> struct resource r;
>+ struct ccsr_pci *reg =3D NULL;
> int res =3D 0;
>
> if (!devres_open_group(&op->dev, mpc85xx_pci_err_probe, GFP_KERNEL))
>@@ -217,6 +315,10 @@ static int __devinit mpc85xx_pci_err_probe(struct
>platform_device *op)
> pdata =3D pci->pvt_info;
> pdata->name =3D "mpc85xx_pci_err";
> pdata->irq =3D NO_IRQ;
>+
>+ if (mpc85xx_pcie_find_capability(op->dev.of_node) > 0)
>+ pdata->pcie_flag =3D 1;
>+
> dev_set_drvdata(&op->dev, pci);
> pci->dev =3D &op->dev;
> pci->mod_name =3D EDAC_MOD_STR;
>@@ -235,37 +337,40 @@ static int __devinit mpc85xx_pci_err_probe(struct
>platform_device *op)
> goto err;
> }
>
>- /* we only need the error registers */
>- r.start +=3D 0xe00;
>-
> if (!devm_request_mem_region(&op->dev, r.start, resource_size(&r),
> pdata->name)) {
>- printk(KERN_ERR "%s: Error while requesting mem region\n",
>- __func__);
>+ printk(KERN_ERR
>+ "%s:Error while requesting mem region\n", __func__);
> res =3D -EBUSY;
> goto err;
> }
>
>- pdata->pci_vbase =3D devm_ioremap(&op->dev, r.start,
>resource_size(&r));
>- if (!pdata->pci_vbase) {
>+ pdata->pci_reg =3D devm_ioremap(&op->dev, r.start, resource_size(&r));
>+ if (!pdata->pci_reg) {
> printk(KERN_ERR "%s: Unable to setup PCI err regs\n",
>__func__);
> res =3D -ENOMEM;
> goto err;
> }
>
>- orig_pci_err_cap_dr =3D
>- in_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_CAP_DR);
>-
>- /* PCI master abort is expected during config cycles */
>- out_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_CAP_DR, 0x40);
>+ if (mpc85xx_err_int_en(&op->dev) < 0)
>+ goto err;
>
>- orig_pci_err_en =3D in_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_EN);
>+ reg =3D pdata->pci_reg;
>+ /* disable pci/pcie error detect */
>+ if (pdata->pcie_flag) {
>+ pdata->orig_pci_err_dr =3D in_be32(®->pex_err_disr);
>+ out_be32(®->pex_err_disr, ~0);
>+ } else {
>+ pdata->orig_pci_err_dr =3D in_be32(®->pex_err_cap_dr);
>+ out_be32(®->pex_err_cap_dr, ~0);
>+ }
>
>- /* disable master abort reporting */
>- out_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_EN, ~0x40);
>+ /* disable all pcie error interrupt */
>+ pdata->orig_pci_err_en =3D in_be32(®->pex_err_en);
>+ out_be32(®->pex_err_en, 0);
>
>- /* clear error bits */
>- out_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_DR, ~0);
>+ /* clear all error bits */
>+ out_be32(®->pex_err_dr, ~0);
>
> if (edac_pci_add_device(pci, pdata->edac_idx) > 0) {
> debugf3("%s(): failed edac_pci_add_device()\n", __func__); @@
>-275,7 +380,7 @@ static int __devinit mpc85xx_pci_err_probe(struct
>platform_device *op)
> if (edac_op_state =3D=3D EDAC_OPSTATE_INT) {
> pdata->irq =3D irq_of_parse_and_map(op->dev.of_node, 0);
> res =3D devm_request_irq(&op->dev, pdata->irq,
>- mpc85xx_pci_isr, IRQF_DISABLED,
>+ mpc85xx_pci_isr, IRQF_SHARED,
> "[EDAC] PCI err", pci);
> if (res < 0) {
> printk(KERN_ERR
>@@ -290,6 +395,17 @@ static int __devinit mpc85xx_pci_err_probe(struct
>platform_device *op)
> pdata->irq);
> }
>
>+ if (pdata->pcie_flag) {
>+ /* enable all pcie error interrupt & error detect */
>+ out_be32(®->pex_err_en, ~0);
>+ out_be32(®->pex_err_disr, 0);
>+ } else {
>+ /* PCI master abort is expected during config cycles */
>+ out_be32(®->pex_err_cap_dr, PCI_ERR_CAP_DR_DIS_MST);
>+ /* disable master abort reporting */
>+ out_be32(®->pex_err_en, PCI_ERR_EN_DIS_MST);
>+ }
>+
> devres_remove_group(&op->dev, mpc85xx_pci_err_probe);
> debugf3("%s(): success\n", __func__);
> printk(KERN_INFO EDAC_MOD_STR " PCI err registered\n"); @@ -311,10
>+427,13 @@ static int mpc85xx_pci_err_remove(struct platform_device *op)
>
> debugf0("%s()\n", __func__);
>
>- out_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_CAP_DR,
>- orig_pci_err_cap_dr);
>+ if (pdata->pcie_flag)
>+ out_be32(&pdata->pci_reg->pex_err_disr, pdata-
>>orig_pci_err_dr);
>+ else
>+ out_be32(&pdata->pci_reg->pex_err_cap_dr,
>+ pdata->orig_pci_err_dr);
>
>- out_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_EN, orig_pci_err_en);
>+ out_be32(&pdata->pci_reg->pex_err_en, pdata->orig_pci_err_en);
>
> edac_pci_del_device(pci->dev);
>
>@@ -333,6 +452,12 @@ static struct of_device_id mpc85xx_pci_err_of_match[]
>=3D {
> {
> .compatible =3D "fsl,mpc8540-pci",
> },
>+ {
>+ .compatible =3D "fsl,mpc8548-pcie",
>+ },
>+ {
>+ .compatible =3D "fsl,p4080-pcie",
>+ },
> {},
> };
> MODULE_DEVICE_TABLE(of, mpc85xx_pci_err_of_match); diff --git
>a/drivers/edac/mpc85xx_edac.h b/drivers/edac/mpc85xx_edac.h index
>932016f..d0e7b11 100644
>--- a/drivers/edac/mpc85xx_edac.h
>+++ b/drivers/edac/mpc85xx_edac.h
>@@ -131,16 +131,8 @@
> #define PCI_EDE_PERR_MASK (PCI_EDE_TGT_PERR | PCI_EDE_MST_PERR | \
> PCI_EDE_ADDR_PERR)
>
>-#define MPC85XX_PCI_ERR_DR 0x0000
>-#define MPC85XX_PCI_ERR_CAP_DR 0x0004
>-#define MPC85XX_PCI_ERR_EN 0x0008
>-#define MPC85XX_PCI_ERR_ATTRIB 0x000c
>-#define MPC85XX_PCI_ERR_ADDR 0x0010
>-#define MPC85XX_PCI_ERR_EXT_ADDR 0x0014
>-#define MPC85XX_PCI_ERR_DL 0x0018
>-#define MPC85XX_PCI_ERR_DH 0x001c
>-#define MPC85XX_PCI_GAS_TIMR 0x0020
>-#define MPC85XX_PCI_PCIX_TIMR 0x0024
>+#define PCI_ERR_CAP_DR_DIS_MST 0x40
>+#define PCI_ERR_EN_DIS_MST (~0x40)
>
> struct mpc85xx_mc_pdata {
> char *name;
>@@ -159,8 +151,11 @@ struct mpc85xx_l2_pdata { struct mpc85xx_pci_pdata {
> char *name;
> int edac_idx;
>- void __iomem *pci_vbase;
> int irq;
>+ struct ccsr_pci *pci_reg;
>+ u8 pcie_flag;
>+ u32 orig_pci_err_dr;
>+ u32 orig_pci_err_en;
> };
>
> #endif
>--
>1.6.4
^ permalink raw reply
* RE: [PATCH 3/4] powerpc/85xx: Merge PCI/PCI Express error management registers
From: Xie Shaohui-B21989 @ 2011-07-26 6:48 UTC (permalink / raw)
To: Kumar Gala; +Cc: linuxppc-dev@lists.ozlabs.org, Jiang Kai-B18973
In-Reply-To: <1311244195-4418-1-git-send-email-Shaohui.Xie@freescale.com>
>-----Original Message-----
>From: Xie Shaohui-B21989
>Sent: Thursday, July 21, 2011 6:30 PM
>To: linuxppc-dev@lists.ozlabs.org
>Cc: Gala Kumar-B11780; Jiang Kai-B18973; Kumar Gala; Xie Shaohui-B21989
>Subject: [PATCH 3/4] powerpc/85xx: Merge PCI/PCI Express error management
>registers
>
>From: Kai.Jiang <Kai.Jiang@freescale.com>
>
>There are some differences of register offset and definition between pci
>and pcie error management registers. While, some other pci/pcie error
>management registers are nearly the same.
>
>Signed-off-by: Kai.Jiang <Kai.Jiang@freescale.com>
>Signed-off-by: Kumar Gala <galak@kernel.crashing.org>
>Signed-off-by: Shaohui Xie <Shaohui.Xie@freescale.com>
>---
> arch/powerpc/sysdev/fsl_pci.h | 31 +++++++++++++++++++++++++------
> 1 files changed, 25 insertions(+), 6 deletions(-)
>
>difg --gite a/arch/powerpc/sysdev/fsl_pci.h
>b/arch/powerpc/sysdev/fsl_pci.h index a39ed5c..60a76e9 100644
>--- a/arch/powerpc/sysdev/fsl_pci.h
>+++ b/arch/powerpc/sysdev/fsl_pci.h
>@@ -74,13 +74,32 @@ struct ccsr_pci {
> */
> struct pci_inbound_window_regs piw[4];
>
>+/* Merge PCI/PCI Express error management registers */
> __be32 pex_err_dr; /* 0x.e00 - PCI/PCIE error detect
>register */
>- u8 res21[4];
>- __be32 pex_err_en; /* 0x.e08 - PCI/PCIE error interrupt
>enable register */
>- u8 res22[4];
>- __be32 pex_err_disr; /* 0x.e10 - PCI/PCIE error
>disable register */
>- u8 res23[12];
>- __be32 pex_err_cap_stat; /* 0x.e20 - PCI/PCIE error capture
>status register */
>+ __be32 pex_err_cap_dr; /* 0x.e04 */
>+ /* - PCI error capture disabled register */
>+ /* - PCIE has no this register */
>+ __be32 pex_err_en; /* 0x.e08 */
>+ /* - PCI/PCIE error interrupt enable
>register*/
>+ __be32 pex_err_attrib; /* 0x.e0c */
>+ /* - PCI error attributes capture register
>*/
>+ /* - PCIE has no this register */
>+ __be32 pex_err_disr; /* 0x.e10 */
>+ /* - PCI error address capture register */
>+ /* - PCIE error disable register */
>+ __be32 pex_err_ext_addr; /* 0x.e14 */
>+ /* - PCI error extended addr capture
>register*/
>+ /* - PCIE has no this register */
>+ __be32 pex_err_dl; /* 0x.e18 */
>+ /* - PCI error data low capture register */
>+ /* - PCIE has no this register */
>+ __be32 pex_err_dh; /* 0x.e1c */
>+ /* - PCI error data high capture register */
>+ /* - PCIE has no this register */
>+ __be32 pex_err_cap_stat; /* 0x.e20 */
>+ /* - PCI gasket timer register */
>+ /* - PCIE error capture status register */
>+
> u8 res24[4];
> __be32 pex_err_cap_r0; /* 0x.e28 - PCIE error capture
>register 0 */
> __be32 pex_err_cap_r1; /* 0x.e2c - PCIE error capture
>register 0 */
>--
>1.6.4
[Xie Shaohui] I've verified this patch can apply for galak/powerpc.git 'nex=
t' branch with no change.
Best Regards,=20
Shaohui Xie
^ permalink raw reply
* [PATCH] powerpc: Clean up some panic messages in prom_init
From: Anton Blanchard @ 2011-07-26 6:47 UTC (permalink / raw)
To: benh, paulus; +Cc: linuxppc-dev
Add a newline to the panic messages in make_room. Also fix a
comment that suggested our chunk size is 4Mb. It's 1MB.
Signed-off-by: Anton Blanchard <anton@samba.org>
---
Index: linux-powerpc/arch/powerpc/kernel/prom_init.c
===================================================================
--- linux-powerpc.orig/arch/powerpc/kernel/prom_init.c 2011-07-26 16:32:14.692650932 +1000
+++ linux-powerpc/arch/powerpc/kernel/prom_init.c 2011-07-26 16:32:47.323298072 +1000
@@ -1830,10 +1830,12 @@ static void __init *make_room(unsigned l
if (room > DEVTREE_CHUNK_SIZE)
room = DEVTREE_CHUNK_SIZE;
if (room < PAGE_SIZE)
- prom_panic("No memory for flatten_device_tree (no room)");
+ prom_panic("No memory for flatten_device_tree "
+ "(no room)\n");
chunk = alloc_up(room, 0);
if (chunk == 0)
- prom_panic("No memory for flatten_device_tree (claim failed)");
+ prom_panic("No memory for flatten_device_tree "
+ "(claim failed)\n");
*mem_end = chunk + room;
}
@@ -2042,7 +2044,7 @@ static void __init flatten_device_tree(v
/*
* Check how much room we have between alloc top & bottom (+/- a
- * few pages), crop to 4Mb, as this is our "chuck" size
+ * few pages), crop to 1MB, as this is our "chunk" size
*/
room = RELOC(alloc_top) - RELOC(alloc_bottom) - 0x4000;
if (room > DEVTREE_CHUNK_SIZE)
^ permalink raw reply
* [PATCH] powerpc: Fix device tree claim code
From: Anton Blanchard @ 2011-07-26 6:47 UTC (permalink / raw)
To: benh, paulus; +Cc: linuxppc-dev
I have a box that fails in OF during boot with:
DEFAULT CATCH!, exception-handler=fff00400
at %SRR0: 49424d2c4c6f6768 %SRR1: 800000004000b002
ie "IBM,Logh". OF got corrupted with a device tree string.
Looking at make_room and alloc_up, we claim the first chunk (1 MB)
but we never claim any more. mem_end is always set to alloc_top
which is the top of our available address space, guaranteeing we will
never call alloc_up and claim more memory.
Also alloc_up wasn't setting alloc_bottom to the bottom of the
available address space.
This doesn't help the box to boot, but we at least fail with
an obvious error. We could relocate the device tree in a future
patch.
Signed-off-by: Anton Blanchard <anton@samba.org>
Cc: <stable@kernel.org>
---
Index: linux-powerpc/arch/powerpc/kernel/prom_init.c
===================================================================
--- linux-powerpc.orig/arch/powerpc/kernel/prom_init.c 2011-07-26 12:37:56.142915032 +1000
+++ linux-powerpc/arch/powerpc/kernel/prom_init.c 2011-07-26 16:31:31.481793248 +1000
@@ -1020,7 +1020,7 @@ static unsigned long __init alloc_up(uns
}
if (addr == 0)
return 0;
- RELOC(alloc_bottom) = addr;
+ RELOC(alloc_bottom) = addr + size;
prom_debug(" -> %x\n", addr);
prom_debug(" alloc_bottom : %x\n", RELOC(alloc_bottom));
@@ -1834,7 +1834,7 @@ static void __init *make_room(unsigned l
chunk = alloc_up(room, 0);
if (chunk == 0)
prom_panic("No memory for flatten_device_tree (claim failed)");
- *mem_end = RELOC(alloc_top);
+ *mem_end = chunk + room;
}
ret = (void *)*mem_start;
@@ -2053,7 +2053,7 @@ static void __init flatten_device_tree(v
mem_start = (unsigned long)alloc_up(room, PAGE_SIZE);
if (mem_start == 0)
prom_panic("Can't allocate initial device-tree chunk\n");
- mem_end = RELOC(alloc_top);
+ mem_end = mem_start + room;
/* Get root of tree */
root = call_prom("peer", 1, 1, (phandle)0);
^ permalink raw reply
* [PATCH] powerpc/85xx: fix memory controller compatible for edac
From: Shaohui Xie @ 2011-07-26 5:46 UTC (permalink / raw)
To: linuxppc-dev; +Cc: mm-commits, Shaohui Xie, kumar.gala, avorontsov, akpm, davem
compatible in dts has been changed, so driver need to update accordingly.
Signed-off-by: Shaohui Xie <Shaohui.Xie@freescale.com>
---
apply for http://git.kernel.org/pub/scm/linux/kernel/git/galak/powerpc.git
'next' branch.
drivers/edac/mpc85xx_edac.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/drivers/edac/mpc85xx_edac.c b/drivers/edac/mpc85xx_edac.c
index 13f6cc5..94c064a 100644
--- a/drivers/edac/mpc85xx_edac.c
+++ b/drivers/edac/mpc85xx_edac.c
@@ -1253,7 +1253,7 @@ static struct of_device_id mpc85xx_mc_err_of_match[] = {
{ .compatible = "fsl,p1020-memory-controller", },
{ .compatible = "fsl,p1021-memory-controller", },
{ .compatible = "fsl,p2020-memory-controller", },
- { .compatible = "fsl,p4080-memory-controller", },
+ { .compatible = "fsl,qoriq-memory-controller", },
{},
};
MODULE_DEVICE_TABLE(of, mpc85xx_mc_err_of_match);
--
1.6.4
^ permalink raw reply related
* Re: [PATCH 1/4] net, phy: am79c874 support
From: Heiko Schocher @ 2011-07-26 4:55 UTC (permalink / raw)
To: linuxppc-dev; +Cc: linux-netdev, Wolfgang Denk
In-Reply-To: <1308729311-15375-2-git-send-email-hs@denx.de>
Hello,
Heiko Schocher wrote:
> Signed-off-by: Heiko Schocher <hs@denx.de>
> cc: linux-netdev@vger.kernel.org
> cc: Wolfgang Denk <wd@denx.de>
> ---
> drivers/net/phy/Kconfig | 5 ++
> drivers/net/phy/Makefile | 1 +
> drivers/net/phy/amd79.c | 109 ++++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 115 insertions(+), 0 deletions(-)
> create mode 100644 drivers/net/phy/amd79.c
Are there some more comments or is this patch ready for going
to mainline?
bye,
Heiko
--
DENX Software Engineering GmbH, MD: Wolfgang Denk & Detlev Zundel
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
^ permalink raw reply
* Re: [PATCH v2 2/4] powerpc, mpc52xx: add a4m072 board support
From: Heiko Schocher @ 2011-07-26 4:52 UTC (permalink / raw)
To: linuxppc-dev; +Cc: devicetree-discuss, Wolfgang Denk
In-Reply-To: <1308739150-31527-1-git-send-email-hs@denx.de>
Hello,
Heiko Schocher wrote:
> Signed-off-by: Heiko Schocher <hs@denx.de>
> cc: Grant Likely <grant.likely@secretlab.ca>
> cc: devicetree-discuss@ozlabs.org
> cc: Wolfgang Denk <wd@denx.de>
> cc: Wolfram Sang <w.sang@pengutronix.de>
> ---
> For this patchseries following patch is needed:
>
> http://patchwork.ozlabs.org/patch/91919/
>
> Grant? Do you have some comments on that patch?
>
> changes for v2:
> add comment from Wolfram Sang:
> use mpc5200.dtsi
>
> arch/powerpc/boot/dts/a4m072.dts | 172 ++++++++++++++++++++++++++
> arch/powerpc/platforms/52xx/mpc5200_simple.c | 1 +
> 2 files changed, 173 insertions(+), 0 deletions(-)
> create mode 100644 arch/powerpc/boot/dts/a4m072.dts
Are there some more comments or is this ready for going
to mainline?
bye,
Heiko
--
DENX Software Engineering GmbH, MD: Wolfgang Denk & Detlev Zundel
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
^ permalink raw reply
* [git pull] Please pull powerpc.git next branch
From: Benjamin Herrenschmidt @ 2011-07-26 4:17 UTC (permalink / raw)
To: Linus Torvalds; +Cc: linuxppc-dev list, Andrew Morton, Linux Kernel list
Hi Linus !
Here's the powerpc batch for this merge window (there might still be a
handful of small fixes coming later this week depending on my mood but
here's the bulk at least). There's a couple of trivial merge conflicts
with your tree, let me know if you prefer that I fix them myself.
No big highlights, mostly a random collection of bug fixes and
improvements (the big highlight is the base KVM support for P7 and
970 which has been merged via Avi's tree).
Cheers,
Ben.
The following changes since commit 750e06992d49666a7589aac555eb3bb68e4dbb88:
Merge branch 'hwmon-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jdelvare/staging (2011-07-25 14:10:34 -0700)
are available in the git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/benh/powerpc.git next
Akinobu Mita (2):
powerpc/pseries: Introduce pSeries_reconfig_notify()
powerpc/pseries: Improve error code on reconfiguration notifier failure
Andrew Gabbasov (1):
powerpc: Exporting boot_cpuid_phys
Anton Blanchard (9):
powerpc: Use -mtraceback=no
powerpc/pseries/hvconsole: Fix dropped console output
powerpc: Sync pseries and ppc64 defconfigs
powerpc: Disable IRQs off tracer in ppc64 defconfig
powerpc: Add mpt2sas driver to pseries and ppc64 defconfig
powerpc: Enable lockup and hung task detectors in pseries and ppc64 defeconfigs
powerpc/irq: Quieten irq mapping printks
powerpc/pseries: Fix hvterm_raw_get_chars to accept < 16 chars, fixing xmon
hvc_console: Add kdb support
Ashish Kalra (2):
powerpc/85xx: Save scratch registers to thread info instead of using SPRGs.
powerpc: introduce the ePAPR embedded hypervisor vmpic driver
Baruch Siach (1):
MAINTAINERS: add arch/powerpc/platforms/85xx/ to the 85xx entry
Becky Bruce (3):
powerpc: Whitespace fix to include/asm/pgtable-ppc64.h
powerpc: mem_init should call memblock_is_reserved with phys_addr_t
powerpc: Create next_tlbcam_idx percpu variable for FSL_BOOKE
Benjamin Herrenschmidt (12):
Merge remote branch 'jwb/next' into next
powerpc/hvsi: Move HVSI protocol definitions to a header file
powerpc/pseries: Factor HVSI header struct in packet definitions
powerpc/udbg: Register udbg console generically
powerpc/pseries: Re-implement HVSI as part of hvc_vio
powerpc/pseries: Move hvsi support into a library
Merge remote branch 'origin/master' into next
powerpc: Fix build problem with default ppc_md.progress commit
powerpc/hvsi: Fix conflict with old HVSI driver
powerpc/pseries: Fix hvc_vio.c build due to recent changes
Merge remote-tracking branch 'jwb/next' into next
powerpc: Copy back TIF flags on return from softirq stack
Christian Kujau (1):
Document powerpc udbg-immortal
Dave Carroll (2):
powerpc: Move free_initmem to common code
powerpc: Add printk companion for ppc_md.progress
Dave Kleikamp (3):
powerpc/44x: don't use tlbivax on AMP systems
powerpc/44x: boot wrapper: allow kernel to load into non-zero address
powerpc/47x: allow kernel to be loaded in higher physical memory
Dmitry Eremin-Solenikov (7):
powerpc/mpic: Support compiling with DEBUG enabled
powerpc/85xx: tqm8540 - add description for onboard flash
powerpc/85xx: specify interrupt for pq3-localbus devices
powerpc/maple: Enable scom access functions on Maple
powerpc/maple: Register CPC925 EDAC device on all boards with CPC925
powerpc: Correct annotations of pmu registration functions
powerpc/cpufreq: Add cpufreq driver for Momentum Maple boards
Fabio Baltieri (1):
powerpc/85xx: fix mpic configuration in CAMP mode
Felix Radensky (1):
powerpc/p1022ds: Remove fixed-link property from ethernet nodes.
Hendrik Brueckner (1):
hvc_console: Improve tty/console put_chars handling
Josh Boyer (3):
ppc4xx: Add crypto and RNG entries to Sequoia DTS
MAINTAINERS: Update PowerPC 4xx entry
powerpc/4xx: Move PCIE printk to proper function
Kumar Gala (18):
powerpc/book3e: Clarify HW table walk enable/disable message
powerpc: Rename e55xx_smp_defconfig to corenet64_smp_defconfig
powerpc: Add a defconfig for 'corenet' 32-bit platforms
powerpc/85xx: Add P5020DS device tree
powerpc/85xx: Add P3041DS device tree
powerpc/85xx: Updates to P4080DS device tree
powerpc/85xx: Cleanup PCIe support on corenet_ds boards
powerpc/fsl_pci: Simplify matching logic for PCI_FIXUP_HEADER
powerpc/pci: Move FSL fixup from 32-bit to common
powerpc/85xx: Add PCI support in 64-bit mode on P5020DS
powerpc/qe: Limit QE support to ppc32
powerpc/85xx: Add P4080 SoC device tree include stub
powerpc/85xx: Add P3041 SoC device tree include stub
powerpc/85xx: Add P5020 SoC device tree include stub
driver core: Add ability for arch code to setup pdev_archdata
powerpc: implement arch_setup_pdev_archdata
powerpc: Dont require a dma_ops struct to set dma mask
powerpc/64: Make server perfmon only built on ppc64 server devices
Laurentiu TUDOR (1):
powerpc/85xx: Remove stale BUG_ON in mpc85xx_smp_init
Lei Xu (2):
powerpc/85xx: Update device tree to add nand info for p5020ds
powerpc/85xx: Update device tree to add nand info for p3041ds
Mathias Krause (1):
powerpc: Remove redundant set_fs(USER_DS)
Matt Evans (1):
powerpc: Fix early boot accounting of CPUs
Michael Ellerman (1):
powerpc: Add jump label support
Michael Neuling (3):
powerpc: Fix doorbell type shift
powerpc/kdump: Fix timeout in crash_kexec_wait_realmode
powerpc: Add CFAR to oops output
Mike Williams (1):
powerpc/4xx: Update Canyonlands and Glacier boards DTS to add HW RNG support
Mingkai Hu (1):
powerpc/85xx: Add p2040 RDB board support
Paul Mackerras (1):
powerpc: Avoid extra indirect function call in sending IPIs
Prabhakar Kushwaha (2):
powerpc/85xx: Add host-pci(e) bridge only for RC
powerpc/85xx: Add P1010RDB board support
Roy Zang (1):
powerpc/85xx: Add basic P1023RDS board support
Scott Wood (4):
powerpc/85xx: Set up doorbells even with no mpic
powerpc/e500mc: Add support for the wait instruction in e500_idle
powerpc/book3e-64: Reraise doorbell when masked by soft-irq-disable
powerpc/book3e-64: use a separate TLB handler when linear map is bolted
Stefan Roese (1):
powerpc/44x: Use correct phy-address dt nodes on taishan.dts
Stuart Yoder (1):
powerpc: make irq_choose_cpu() available to all PIC drivers
Timur Tabi (12):
powerpc: introduce ePAPR embedded hypervisor hcall interface
powerpc: add Freescale hypervisor partition control functions
powerpc/85xx: add board support for the Freescale hypervisor
powerpc/p1022ds: add missing iounmap calls to platform file
powerpc/85xx: clamp the P1022DS DIU pixel clock to allowed values
powerpc/85xx: enable the framebuffer console for the defconfigs
powerpc/86xx: improve calculation of DIU pixel clock on the MPC8610 HPCD
powerpc/86xx: enable the framebuffer console on the MPC8610 HPCD
powerpc/85xx: disable timebase synchronization under the hypervisor
drivers/virt: introduce Freescale hypervisor management driver
powerpc/85xx: add hypervisor config entries to corenet_smp_defconfig
drivers/virt: add missing linux/interrupt.h to fsl_hypervisor.c
Tony Breeds (2):
powerpc/4xx: Add check_link to struct ppc4xx_pciex_hwops
powerpc/mm: Fix output of total_ram.
Documentation/ioctl/ioctl-number.txt | 1 +
Documentation/kernel-parameters.txt | 5 +
MAINTAINERS | 3 +-
arch/powerpc/Kconfig | 3 +-
arch/powerpc/Kconfig.debug | 15 +
arch/powerpc/Makefile | 2 +-
arch/powerpc/boot/dts/canyonlands.dts | 5 +
arch/powerpc/boot/dts/glacier.dts | 8 +-
arch/powerpc/boot/dts/mpc8568mds.dts | 2 +
arch/powerpc/boot/dts/p1010rdb.dts | 280 ++++++
arch/powerpc/boot/dts/p1010si.dtsi | 376 ++++++++
arch/powerpc/boot/dts/p1022ds.dts | 2 -
arch/powerpc/boot/dts/p1023rds.dts | 546 ++++++++++++
arch/powerpc/boot/dts/p2040rdb.dts | 166 ++++
arch/powerpc/boot/dts/p2040si.dtsi | 623 +++++++++++++
arch/powerpc/boot/dts/p3041ds.dts | 214 +++++
arch/powerpc/boot/dts/p3041si.dtsi | 660 ++++++++++++++
arch/powerpc/boot/dts/p4080ds.dts | 533 +-----------
arch/powerpc/boot/dts/p4080si.dtsi | 661 ++++++++++++++
arch/powerpc/boot/dts/p5020ds.dts | 215 +++++
arch/powerpc/boot/dts/p5020si.dtsi | 652 ++++++++++++++
arch/powerpc/boot/dts/sequoia.dts | 12 +
arch/powerpc/boot/dts/socrates.dts | 2 +
arch/powerpc/boot/dts/taishan.dts | 4 +-
arch/powerpc/boot/dts/tqm8540.dts | 42 +
arch/powerpc/boot/dts/tqm8548-bigflash.dts | 2 +
arch/powerpc/boot/dts/tqm8548.dts | 2 +
arch/powerpc/boot/dts/tqm8560.dts | 2 +
arch/powerpc/boot/dts/xpedite5200.dts | 2 +
arch/powerpc/boot/dts/xpedite5200_xmon.dts | 2 +
arch/powerpc/boot/treeboot-iss4xx.c | 23 +-
arch/powerpc/configs/44x/iss476-smp_defconfig | 6 +-
arch/powerpc/configs/85xx/p1023rds_defconfig | 173 ++++
arch/powerpc/configs/86xx/mpc8610_hpcd_defconfig | 5 +
arch/powerpc/configs/corenet32_smp_defconfig | 187 ++++
...e55xx_smp_defconfig => corenet64_smp_defconfig} | 0
arch/powerpc/configs/mpc85xx_defconfig | 12 +-
arch/powerpc/configs/mpc85xx_smp_defconfig | 10 +-
arch/powerpc/configs/ppc64_defconfig | 16 +-
arch/powerpc/configs/pseries_defconfig | 3 +
arch/powerpc/include/asm/dbell.h | 2 +-
arch/powerpc/include/asm/ehv_pic.h | 40 +
arch/powerpc/include/asm/epapr_hcalls.h | 502 +++++++++++
arch/powerpc/include/asm/exception-64e.h | 52 +-
arch/powerpc/include/asm/fsl_hcalls.h | 655 ++++++++++++++
arch/powerpc/include/asm/hvsi.h | 94 ++
arch/powerpc/include/asm/irq.h | 2 +
arch/powerpc/include/asm/jump_label.h | 47 +
arch/powerpc/include/asm/mmu.h | 12 +-
arch/powerpc/include/asm/pSeries_reconfig.h | 2 +-
arch/powerpc/include/asm/paca.h | 7 +-
arch/powerpc/include/asm/pgtable-ppc64.h | 3 +-
arch/powerpc/include/asm/processor.h | 5 +
arch/powerpc/include/asm/reg.h | 4 +-
arch/powerpc/include/asm/setup.h | 4 +
arch/powerpc/include/asm/smp.h | 3 +-
arch/powerpc/include/asm/udbg.h | 1 +
arch/powerpc/kernel/Makefile | 1 +
arch/powerpc/kernel/asm-offsets.c | 3 +
arch/powerpc/kernel/crash.c | 6 +-
arch/powerpc/kernel/dma.c | 4 +-
arch/powerpc/kernel/exceptions-64e.S | 22 +-
arch/powerpc/kernel/head_44x.S | 42 +-
arch/powerpc/kernel/head_64.S | 2 +-
arch/powerpc/kernel/head_booke.h | 42 +-
arch/powerpc/kernel/head_fsl_booke.S | 49 +-
arch/powerpc/kernel/idle_e500.S | 12 +
arch/powerpc/kernel/irq.c | 50 +-
arch/powerpc/kernel/jump_label.c | 23 +
arch/powerpc/kernel/misc_64.S | 4 +-
arch/powerpc/kernel/mpc7450-pmu.c | 2 +-
arch/powerpc/kernel/pci-common.c | 18 +
arch/powerpc/kernel/pci_32.c | 19 -
arch/powerpc/kernel/perf_event.c | 2 +-
arch/powerpc/kernel/power4-pmu.c | 2 +-
arch/powerpc/kernel/power5+-pmu.c | 2 +-
arch/powerpc/kernel/power5-pmu.c | 2 +-
arch/powerpc/kernel/power6-pmu.c | 2 +-
arch/powerpc/kernel/power7-pmu.c | 2 +-
arch/powerpc/kernel/ppc970-pmu.c | 2 +-
arch/powerpc/kernel/process.c | 4 +-
arch/powerpc/kernel/prom.c | 16 +-
arch/powerpc/kernel/setup-common.c | 27 +-
arch/powerpc/kernel/setup_32.c | 4 +-
arch/powerpc/kernel/setup_64.c | 6 +-
arch/powerpc/kernel/smp.c | 30 +-
arch/powerpc/kernel/udbg.c | 5 +
arch/powerpc/mm/44x_mmu.c | 13 +-
arch/powerpc/mm/init_32.c | 32 -
arch/powerpc/mm/init_64.c | 16 -
arch/powerpc/mm/mem.c | 35 +-
arch/powerpc/mm/tlb_hash32.c | 4 +
arch/powerpc/mm/tlb_low_64e.S | 206 +++++
arch/powerpc/mm/tlb_nohash.c | 64 +-
arch/powerpc/platforms/85xx/Kconfig | 31 +
arch/powerpc/platforms/85xx/Makefile | 3 +
arch/powerpc/platforms/85xx/corenet_ds.c | 41 +-
arch/powerpc/platforms/85xx/mpc85xx_ds.c | 3 +-
arch/powerpc/platforms/85xx/mpc85xx_rdb.c | 5 +-
arch/powerpc/platforms/85xx/p1010rdb.c | 122 +++
arch/powerpc/platforms/85xx/p1022_ds.c | 18 +-
arch/powerpc/platforms/85xx/p1023_rds.c | 162 ++++
arch/powerpc/platforms/85xx/p2040_rdb.c | 88 ++
arch/powerpc/platforms/85xx/p3041_ds.c | 28 +-
arch/powerpc/platforms/85xx/p4080_ds.c | 38 +-
arch/powerpc/platforms/85xx/p5020_ds.c | 32 +-
arch/powerpc/platforms/85xx/smp.c | 30 +-
arch/powerpc/platforms/86xx/mpc8610_hpcd.c | 107 +--
arch/powerpc/platforms/Kconfig | 6 +-
arch/powerpc/platforms/Kconfig.cputype | 2 +-
arch/powerpc/platforms/iseries/smp.c | 2 +-
arch/powerpc/platforms/maple/setup.c | 41 +-
arch/powerpc/platforms/powermac/setup.c | 3 -
arch/powerpc/platforms/powermac/smp.c | 2 +-
arch/powerpc/platforms/pseries/dlpar.c | 10 +-
arch/powerpc/platforms/pseries/hotplug-cpu.c | 10 +-
arch/powerpc/platforms/pseries/hotplug-memory.c | 16 +-
arch/powerpc/platforms/pseries/hvconsole.c | 2 +-
arch/powerpc/platforms/pseries/lpar.c | 191 ----
arch/powerpc/platforms/pseries/pseries.h | 3 +-
arch/powerpc/platforms/pseries/reconfig.c | 28 +-
arch/powerpc/platforms/pseries/setup.c | 5 +-
arch/powerpc/platforms/pseries/smp.c | 2 +-
arch/powerpc/platforms/wsp/smp.c | 2 +-
arch/powerpc/sysdev/Makefile | 1 +
arch/powerpc/sysdev/ehv_pic.c | 302 +++++++
arch/powerpc/sysdev/fsl_pci.c | 83 +--
arch/powerpc/sysdev/fsl_soc.c | 27 +
arch/powerpc/sysdev/fsl_soc.h | 3 +
arch/powerpc/sysdev/mpic.c | 38 +-
arch/powerpc/sysdev/ppc4xx_pci.c | 147 ++--
drivers/Kconfig | 2 +
drivers/Makefile | 3 +
drivers/base/platform.c | 21 +
drivers/cpufreq/Kconfig | 5 +
drivers/cpufreq/Kconfig.powerpc | 7 +
drivers/cpufreq/Makefile | 5 +
drivers/cpufreq/maple-cpufreq.c | 309 +++++++
drivers/of/platform.c | 4 +-
drivers/tty/hvc/Kconfig | 5 +
drivers/tty/hvc/Makefile | 3 +-
drivers/tty/hvc/hvc_console.c | 70 ++-
drivers/tty/hvc/hvc_console.h | 4 +
drivers/tty/hvc/hvc_vio.c | 408 ++++++++-
drivers/tty/hvc/hvsi.c | 129 +--
drivers/tty/hvc/hvsi_lib.c | 426 +++++++++
drivers/virt/Kconfig | 32 +
drivers/virt/Makefile | 5 +
drivers/virt/fsl_hypervisor.c | 938 ++++++++++++++++++++
include/linux/Kbuild | 1 +
include/linux/fsl_hypervisor.h | 241 +++++
include/linux/platform_device.h | 1 +
152 files changed, 10499 insertions(+), 1441 deletions(-)
create mode 100644 arch/powerpc/boot/dts/p1010rdb.dts
create mode 100644 arch/powerpc/boot/dts/p1010si.dtsi
create mode 100644 arch/powerpc/boot/dts/p1023rds.dts
create mode 100644 arch/powerpc/boot/dts/p2040rdb.dts
create mode 100644 arch/powerpc/boot/dts/p2040si.dtsi
create mode 100644 arch/powerpc/boot/dts/p3041ds.dts
create mode 100644 arch/powerpc/boot/dts/p3041si.dtsi
create mode 100644 arch/powerpc/boot/dts/p4080si.dtsi
create mode 100644 arch/powerpc/boot/dts/p5020ds.dts
create mode 100644 arch/powerpc/boot/dts/p5020si.dtsi
create mode 100644 arch/powerpc/configs/85xx/p1023rds_defconfig
create mode 100644 arch/powerpc/configs/corenet32_smp_defconfig
rename arch/powerpc/configs/{e55xx_smp_defconfig => corenet64_smp_defconfig} (100%)
create mode 100644 arch/powerpc/include/asm/ehv_pic.h
create mode 100644 arch/powerpc/include/asm/epapr_hcalls.h
create mode 100644 arch/powerpc/include/asm/fsl_hcalls.h
create mode 100644 arch/powerpc/include/asm/hvsi.h
create mode 100644 arch/powerpc/include/asm/jump_label.h
create mode 100644 arch/powerpc/kernel/jump_label.c
create mode 100644 arch/powerpc/platforms/85xx/p1010rdb.c
create mode 100644 arch/powerpc/platforms/85xx/p1023_rds.c
create mode 100644 arch/powerpc/platforms/85xx/p2040_rdb.c
create mode 100644 arch/powerpc/sysdev/ehv_pic.c
create mode 100644 drivers/cpufreq/Kconfig.powerpc
create mode 100644 drivers/cpufreq/maple-cpufreq.c
create mode 100644 drivers/tty/hvc/hvsi_lib.c
create mode 100644 drivers/virt/Kconfig
create mode 100644 drivers/virt/Makefile
create mode 100644 drivers/virt/fsl_hypervisor.c
create mode 100644 include/linux/fsl_hypervisor.h
^ permalink raw reply
* [PATCH] powerpc/32: pass device tree address as u64 to machine_init
From: Scott Wood @ 2011-07-25 21:29 UTC (permalink / raw)
To: benh; +Cc: linuxppc-dev
u64 is used rather than phys_addr_t to keep things simple, as
this is called from assembly code.
Update callers to pass a 64-bit address in r3/r4. Other unused
register assignments that were once parameters to machine_init
are dropped.
For FSL BookE, look up the physical address of the device tree from the
effective address passed in r3 by the loader. This is required for
situations where memory does not start at zero (due to AMP or IOMMU-less
virtualization), and thus the IMA doesn't start at zero, and thus the
device tree effective address does not equal the physical address.
Signed-off-by: Scott Wood <scottwood@freescale.com>
---
Tested on fsl booke and 83xx.
arch/powerpc/kernel/head_32.S | 7 ++---
arch/powerpc/kernel/head_40x.S | 15 ++---------
arch/powerpc/kernel/head_44x.S | 16 ++----------
arch/powerpc/kernel/head_8xx.S | 13 ++--------
arch/powerpc/kernel/head_fsl_booke.S | 42 +++++++++++++++++++++-------------
arch/powerpc/kernel/setup_32.c | 2 +-
6 files changed, 39 insertions(+), 56 deletions(-)
diff --git a/arch/powerpc/kernel/head_32.S b/arch/powerpc/kernel/head_32.S
index ba250d5..0654dba 100644
--- a/arch/powerpc/kernel/head_32.S
+++ b/arch/powerpc/kernel/head_32.S
@@ -139,8 +139,7 @@ __start:
trap
#endif /* CONFIG_PPC_PMAC */
-1: mr r31,r3 /* save parameters */
- mr r30,r4
+1: mr r31,r3 /* save device tree ptr */
li r24,0 /* cpu # */
/*
@@ -964,8 +963,8 @@ start_here:
* Do early platform-specific initialization,
* and set up the MMU.
*/
- mr r3,r31
- mr r4,r30
+ li r3,0
+ mr r4,r31
bl machine_init
bl __save_cpu_setup
bl MMU_init
diff --git a/arch/powerpc/kernel/head_40x.S b/arch/powerpc/kernel/head_40x.S
index a91626d..872a6af 100644
--- a/arch/powerpc/kernel/head_40x.S
+++ b/arch/powerpc/kernel/head_40x.S
@@ -58,13 +58,7 @@
_ENTRY(_stext);
_ENTRY(_start);
- /* Save parameters we are passed.
- */
- mr r31,r3
- mr r30,r4
- mr r29,r5
- mr r28,r6
- mr r27,r7
+ mr r31,r3 /* save device tree ptr */
/* We have to turn on the MMU right away so we get cache modes
* set correctly.
@@ -849,11 +843,8 @@ start_here:
/*
* Decide what sort of machine this is and initialize the MMU.
*/
- mr r3,r31
- mr r4,r30
- mr r5,r29
- mr r6,r28
- mr r7,r27
+ li r3,0
+ mr r4,r31
bl machine_init
bl MMU_init
diff --git a/arch/powerpc/kernel/head_44x.S b/arch/powerpc/kernel/head_44x.S
index f8e971b..b725dab 100644
--- a/arch/powerpc/kernel/head_44x.S
+++ b/arch/powerpc/kernel/head_44x.S
@@ -61,14 +61,7 @@ _ENTRY(_start);
* of abatron_pteptrs
*/
nop
-/*
- * Save parameters we are passed
- */
- mr r31,r3
- mr r30,r4
- mr r29,r5
- mr r28,r6
- mr r27,r7
+ mr r31,r3 /* save device tree ptr */
li r24,0 /* CPU number */
bl init_cpu_state
@@ -120,11 +113,8 @@ _ENTRY(_start);
/*
* Decide what sort of machine this is and initialize the MMU.
*/
- mr r3,r31
- mr r4,r30
- mr r5,r29
- mr r6,r28
- mr r7,r27
+ li r3,0
+ mr r4,r31
bl machine_init
bl MMU_init
diff --git a/arch/powerpc/kernel/head_8xx.S b/arch/powerpc/kernel/head_8xx.S
index 1cbf64e..b68cb17 100644
--- a/arch/powerpc/kernel/head_8xx.S
+++ b/arch/powerpc/kernel/head_8xx.S
@@ -76,11 +76,7 @@ _ENTRY(_start);
*/
.globl __start
__start:
- mr r31,r3 /* save parameters */
- mr r30,r4
- mr r29,r5
- mr r28,r6
- mr r27,r7
+ mr r31,r3 /* save device tree ptr */
/* We have to turn on the MMU right away so we get cache modes
* set correctly.
@@ -723,11 +719,8 @@ start_here:
/*
* Decide what sort of machine this is and initialize the MMU.
*/
- mr r3,r31
- mr r4,r30
- mr r5,r29
- mr r6,r28
- mr r7,r27
+ li r3,0
+ mr r4,r31
bl machine_init
bl MMU_init
diff --git a/arch/powerpc/kernel/head_fsl_booke.S b/arch/powerpc/kernel/head_fsl_booke.S
index 985638d..c86f0eb 100644
--- a/arch/powerpc/kernel/head_fsl_booke.S
+++ b/arch/powerpc/kernel/head_fsl_booke.S
@@ -63,17 +63,30 @@ _ENTRY(_start);
* of abatron_pteptrs
*/
nop
-/*
- * Save parameters we are passed
- */
- mr r31,r3
- mr r30,r4
- mr r29,r5
- mr r28,r6
- mr r27,r7
- li r25,0 /* phys kernel start (low) */
- li r24,0 /* CPU number */
- li r23,0 /* phys kernel start (high) */
+
+ /* Translate device tree address to physical, save in r30/r31 */
+ mfmsr r16
+ mfspr r17,SPRN_PID
+ rlwinm r17,r17,16,0x3fff0000 /* turn PID into MAS6[SPID] */
+ rlwimi r17,r16,28,0x00000001 /* turn MSR[DS] into MAS6[SAS] */
+ mtspr SPRN_MAS6,r17
+
+ tlbsx 0,r3 /* must succeed */
+
+ mfspr r16,SPRN_MAS1
+ mfspr r20,SPRN_MAS3
+ rlwinm r17,r16,25,0x1f /* r17 = log2(page size) */
+ li r18,1024
+ slw r18,r18,r17 /* r18 = page size */
+ addi r18,r18,-1
+ and r19,r3,r18 /* r19 = page offset */
+ andc r31,r20,r18 /* r3 = page base */
+ or r31,r31,r19 /* r3 = devtree phys addr */
+ mfspr r30,SPRN_MAS7
+
+ li r25,0 /* phys kernel start (low) */
+ li r24,0 /* CPU number */
+ li r23,0 /* phys kernel start (high) */
/* We try to not make any assumptions about how the boot loader
* setup or used the TLBs. We invalidate all mappings from the
@@ -198,11 +211,8 @@ _ENTRY(__early_start)
/*
* Decide what sort of machine this is and initialize the MMU.
*/
- mr r3,r31
- mr r4,r30
- mr r5,r29
- mr r6,r28
- mr r7,r27
+ mr r3,r30
+ mr r4,r31
bl machine_init
bl MMU_init
diff --git a/arch/powerpc/kernel/setup_32.c b/arch/powerpc/kernel/setup_32.c
index 209135a..c1ce863 100644
--- a/arch/powerpc/kernel/setup_32.c
+++ b/arch/powerpc/kernel/setup_32.c
@@ -117,7 +117,7 @@ notrace unsigned long __init early_init(unsigned long dt_ptr)
* This is called very early on the boot process, after a minimal
* MMU environment has been set up but before MMU_init is called.
*/
-notrace void __init machine_init(unsigned long dt_ptr)
+notrace void __init machine_init(u64 dt_ptr)
{
lockdep_init();
--
1.7.4.1
^ permalink raw reply related
* [PATCH] powerpc: return the_cpu_ spec from identify_cpu
From: Scott Wood @ 2011-07-25 21:04 UTC (permalink / raw)
To: benh; +Cc: linuxppc-dev
Commit af9eef3c7b1ed004c378c89b87642f4937337d50 caused cpu_setup to see
the_cpu_spec, rather than the source struct. However, on 32-bit, the
return value of identify_cpu was being used for feature fixups, and
identify_cpu was returning the source struct. So if cpu_setup patches
the feature bits, the update won't affect the fixups.
Signed-off-by: Scott Wood <scottwood@freescale.com>
---
arch/powerpc/kernel/cputable.c | 11 ++++++-----
1 files changed, 6 insertions(+), 5 deletions(-)
diff --git a/arch/powerpc/kernel/cputable.c b/arch/powerpc/kernel/cputable.c
index 9fb9332..fa44ff5 100644
--- a/arch/powerpc/kernel/cputable.c
+++ b/arch/powerpc/kernel/cputable.c
@@ -2051,7 +2051,8 @@ static struct cpu_spec __initdata cpu_specs[] = {
static struct cpu_spec the_cpu_spec;
-static void __init setup_cpu_spec(unsigned long offset, struct cpu_spec *s)
+static struct cpu_spec * __init setup_cpu_spec(unsigned long offset,
+ struct cpu_spec *s)
{
struct cpu_spec *t = &the_cpu_spec;
struct cpu_spec old;
@@ -2114,6 +2115,8 @@ static void __init setup_cpu_spec(unsigned long offset, struct cpu_spec *s)
t->cpu_setup(offset, t);
}
#endif /* CONFIG_PPC64 || CONFIG_BOOKE */
+
+ return t;
}
struct cpu_spec * __init identify_cpu(unsigned long offset, unsigned int pvr)
@@ -2124,10 +2127,8 @@ struct cpu_spec * __init identify_cpu(unsigned long offset, unsigned int pvr)
s = PTRRELOC(s);
for (i = 0; i < ARRAY_SIZE(cpu_specs); i++,s++) {
- if ((pvr & s->pvr_mask) == s->pvr_value) {
- setup_cpu_spec(offset, s);
- return s;
- }
+ if ((pvr & s->pvr_mask) == s->pvr_value)
+ return setup_cpu_spec(offset, s);
}
BUG();
--
1.7.4.1
^ permalink raw reply related
* [PATCH] powerpc: mtspr/mtmsr should take an unsigned long
From: Scott Wood @ 2011-07-25 21:02 UTC (permalink / raw)
To: benh; +Cc: linuxppc-dev
Add a cast in case the caller passes in a different type, as it would
if mtspr/mtmsr were functions.
Previously, if a 64-bit type was passed in on 32-bit, GCC would bind the
constraint to a pair of registers, and would substitute the first register
in the pair in the asm code. This corresponds to the upper half of the
64-bit register, which is generally not the desired behavior.
Signed-off-by: Scott Wood <scottwood@freescale.com>
---
If you're wondering why you'd pass a 64-bit value to one of these macros
on 32-bit, it came up when trying to load an SPR from kvm_vcpu_arch_shared.
arch/powerpc/include/asm/reg.h | 7 +++++--
1 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/arch/powerpc/include/asm/reg.h b/arch/powerpc/include/asm/reg.h
index 213d1d7..1b45133 100644
--- a/arch/powerpc/include/asm/reg.h
+++ b/arch/powerpc/include/asm/reg.h
@@ -1007,13 +1007,16 @@
#define mtmsrd(v) __mtmsrd((v), 0)
#define mtmsr(v) mtmsrd(v)
#else
-#define mtmsr(v) asm volatile("mtmsr %0" : : "r" (v) : "memory")
+#define mtmsr(v) asm volatile("mtmsr %0" : \
+ : "r" ((unsigned long)(v)) \
+ : "memory")
#endif
#define mfspr(rn) ({unsigned long rval; \
asm volatile("mfspr %0," __stringify(rn) \
: "=r" (rval)); rval;})
-#define mtspr(rn, v) asm volatile("mtspr " __stringify(rn) ",%0" : : "r" (v)\
+#define mtspr(rn, v) asm volatile("mtspr " __stringify(rn) ",%0" : \
+ : "r" ((unsigned long)(v)) \
: "memory")
#ifdef __powerpc64__
--
1.7.4.1
^ permalink raw reply related
* [PATCH] powerpc/nvram: Add compression to fit more oops output into NVRAM
From: Jim Keniston @ 2011-07-25 17:54 UTC (permalink / raw)
To: benh, linuxppc-dev
Capture more than twice as much text from the printk buffer, and
compress it to fit it in the lnx,oops-log NVRAM partition. You
can view the compressed text using the new (as of July 20) --unzip
option of the nvram command in the powerpc-utils package.
Signed-off-by: Jim Keniston <jkenisto@us.ibm.com>
---
arch/powerpc/include/asm/rtas.h | 6 +
arch/powerpc/platforms/pseries/nvram.c | 171 +++++++++++++++++++++++++++++++-
2 files changed, 168 insertions(+), 9 deletions(-)
diff --git a/arch/powerpc/include/asm/rtas.h b/arch/powerpc/include/asm/rtas.h
index 58625d1..41f69ae 100644
--- a/arch/powerpc/include/asm/rtas.h
+++ b/arch/powerpc/include/asm/rtas.h
@@ -249,10 +249,12 @@ extern void pSeries_log_error(char *buf, unsigned int err_type, int fatal);
#define ERR_FLAG_ALREADY_LOGGED 0x0
#define ERR_FLAG_BOOT 0x1 /* log was pulled from NVRAM on boot */
#define ERR_TYPE_RTAS_LOG 0x2 /* from rtas event-scan */
-#define ERR_TYPE_KERNEL_PANIC 0x4 /* from panic() */
+#define ERR_TYPE_KERNEL_PANIC 0x4 /* from die()/panic() */
+#define ERR_TYPE_KERNEL_PANIC_GZ 0x8 /* ditto, compressed */
/* All the types and not flags */
-#define ERR_TYPE_MASK (ERR_TYPE_RTAS_LOG | ERR_TYPE_KERNEL_PANIC)
+#define ERR_TYPE_MASK \
+ (ERR_TYPE_RTAS_LOG | ERR_TYPE_KERNEL_PANIC | ERR_TYPE_KERNEL_PANIC_GZ)
#define RTAS_DEBUG KERN_DEBUG "RTAS: "
diff --git a/arch/powerpc/platforms/pseries/nvram.c b/arch/powerpc/platforms/pseries/nvram.c
index 00cc3a0..a76b228 100644
--- a/arch/powerpc/platforms/pseries/nvram.c
+++ b/arch/powerpc/platforms/pseries/nvram.c
@@ -18,6 +18,8 @@
#include <linux/spinlock.h>
#include <linux/slab.h>
#include <linux/kmsg_dump.h>
+#include <linux/ctype.h>
+#include <linux/zlib.h>
#include <asm/uaccess.h>
#include <asm/nvram.h>
#include <asm/rtas.h>
@@ -78,8 +80,41 @@ static struct kmsg_dumper nvram_kmsg_dumper = {
#define NVRAM_RTAS_READ_TIMEOUT 5 /* seconds */
static unsigned long last_unread_rtas_event; /* timestamp */
-/* We preallocate oops_buf during init to avoid kmalloc during oops/panic. */
-static char *oops_buf;
+/*
+ * For capturing and compressing an oops or panic report...
+
+ * big_oops_buf[] holds the uncompressed text we're capturing.
+ *
+ * oops_buf[] holds the compressed text, preceded by a prefix.
+ * The prefix is just a u16 holding the length of the compressed* text.
+ * (*Or uncompressed, if compression fails.) oops_buf[] gets written
+ * to NVRAM.
+ *
+ * oops_len points to the prefix. oops_data points to the compressed text.
+ *
+ * +- oops_buf
+ * | +- oops_data
+ * v v
+ * +------------+-----------------------------------------------+
+ * | length | text |
+ * | (2 bytes) | (oops_data_sz bytes) |
+ * +------------+-----------------------------------------------+
+ * ^
+ * +- oops_len
+ *
+ * We preallocate these buffers during init to avoid kmalloc during oops/panic.
+ */
+static size_t big_oops_buf_sz;
+static char *big_oops_buf, *oops_buf;
+static u16 *oops_len;
+static char *oops_data;
+static size_t oops_data_sz;
+
+/* Compression parameters */
+#define COMPR_LEVEL 6
+#define WINDOW_BITS 12
+#define MEM_LEVEL 4
+static struct z_stream_s stream;
static ssize_t pSeries_nvram_read(char *buf, size_t count, loff_t *index)
{
@@ -387,11 +422,44 @@ static void __init nvram_init_oops_partition(int rtas_partition_exists)
sizeof(rtas_log_partition));
}
oops_buf = kmalloc(oops_log_partition.size, GFP_KERNEL);
+ if (!oops_buf) {
+ pr_err("nvram: No memory for %s partition\n",
+ oops_log_partition.name);
+ return;
+ }
+ oops_len = (u16*) oops_buf;
+ oops_data = oops_buf + sizeof(u16);
+ oops_data_sz = oops_log_partition.size - sizeof(u16);
+
+ /*
+ * Figure compression (preceded by elimination of each line's <n>
+ * severity prefix) will reduce the oops/panic report to at most
+ * 45% of its original size.
+ */
+ big_oops_buf_sz = (oops_data_sz * 100) / 45;
+ big_oops_buf = kmalloc(big_oops_buf_sz, GFP_KERNEL);
+ if (big_oops_buf) {
+ stream.workspace = kmalloc(zlib_deflate_workspacesize(
+ WINDOW_BITS, MEM_LEVEL), GFP_KERNEL);
+ if (!stream.workspace) {
+ pr_err("nvram: No memory for compression workspace; "
+ "skipping compression of %s partition data\n",
+ oops_log_partition.name);
+ kfree(big_oops_buf);
+ big_oops_buf = NULL;
+ }
+ } else {
+ pr_err("No memory for uncompressed %s data; "
+ "skipping compression\n", oops_log_partition.name);
+ stream.workspace = NULL;
+ }
+
rc = kmsg_dump_register(&nvram_kmsg_dumper);
if (rc != 0) {
pr_err("nvram: kmsg_dump_register() failed; returned %d\n", rc);
kfree(oops_buf);
- return;
+ kfree(big_oops_buf);
+ kfree(stream.workspace);
}
}
@@ -473,7 +541,83 @@ static int clobbering_unread_rtas_event(void)
NVRAM_RTAS_READ_TIMEOUT);
}
-/* our kmsg_dump callback */
+/* Squeeze out each line's <n> severity prefix. */
+static size_t elide_severities(char *buf, size_t len)
+{
+ char *in, *out, *buf_end = buf + len;
+ /* Assume a <n> at the very beginning marks the start of a line. */
+ int newline = 1;
+
+ in = out = buf;
+ while (in < buf_end) {
+ if (newline && in+3 <= buf_end &&
+ *in == '<' && isdigit(in[1]) && in[2] == '>') {
+ in += 3;
+ newline = 0;
+ } else {
+ newline = (*in == '\n');
+ *out++ = *in++;
+ }
+ }
+ return out - buf;
+}
+
+/* Derived from logfs_compress() */
+static int nvram_compress(const void *in, void *out, size_t inlen,
+ size_t outlen)
+{
+ int err, ret;
+
+ ret = -EIO;
+ err = zlib_deflateInit2(&stream, COMPR_LEVEL, Z_DEFLATED, WINDOW_BITS,
+ MEM_LEVEL, Z_DEFAULT_STRATEGY);
+ if (err != Z_OK)
+ goto error;
+
+ stream.next_in = in;
+ stream.avail_in = inlen;
+ stream.total_in = 0;
+ stream.next_out = out;
+ stream.avail_out = outlen;
+ stream.total_out = 0;
+
+ err = zlib_deflate(&stream, Z_FINISH);
+ if (err != Z_STREAM_END)
+ goto error;
+
+ err = zlib_deflateEnd(&stream);
+ if (err != Z_OK)
+ goto error;
+
+ if (stream.total_out >= stream.total_in)
+ goto error;
+
+ ret = stream.total_out;
+error:
+ return ret;
+}
+
+/* Compress the text from big_oops_buf into oops_buf. */
+static int zip_oops(size_t text_len)
+{
+ int zipped_len = nvram_compress(big_oops_buf, oops_data, text_len,
+ oops_data_sz);
+ if (zipped_len < 0) {
+ pr_err("nvram: compression failed; returned %d\n", zipped_len);
+ pr_err("nvram: logging uncompressed oops/panic report\n");
+ return -1;
+ }
+ *oops_len = (u16) zipped_len;
+ return 0;
+}
+
+/*
+ * This is our kmsg_dump callback, called after an oops or panic report
+ * has been written to the printk buffer. We want to capture as much
+ * of the printk buffer as possible. First, capture as much as we can
+ * that we think will compress sufficiently to fit in the lnx,oops-log
+ * partition. If that's too much, go back and capture uncompressed text.
+ */
static void oops_to_nvram(struct kmsg_dumper *dumper,
enum kmsg_dump_reason reason,
const char *old_msgs, unsigned long old_len,
@@ -482,6 +626,8 @@ static void oops_to_nvram(struct kmsg_dumper *dumper,
static unsigned int oops_count = 0;
static bool panicking = false;
size_t text_len;
+ unsigned int err_type = ERR_TYPE_KERNEL_PANIC_GZ;
+ int rc = -1;
switch (reason) {
case KMSG_DUMP_RESTART:
@@ -509,8 +655,19 @@ static void oops_to_nvram(struct kmsg_dumper *dumper,
if (clobbering_unread_rtas_event())
return;
- text_len = capture_last_msgs(old_msgs, old_len, new_msgs, new_len,
- oops_buf, oops_log_partition.size);
+ if (big_oops_buf) {
+ text_len = capture_last_msgs(old_msgs, old_len,
+ new_msgs, new_len, big_oops_buf, big_oops_buf_sz);
+ text_len = elide_severities(big_oops_buf, text_len);
+ rc = zip_oops(text_len);
+ }
+ if (rc != 0) {
+ text_len = capture_last_msgs(old_msgs, old_len,
+ new_msgs, new_len, oops_data, oops_data_sz);
+ err_type = ERR_TYPE_KERNEL_PANIC;
+ *oops_len = (u16) text_len;
+ }
+
(void) nvram_write_os_partition(&oops_log_partition, oops_buf,
- (int) text_len, ERR_TYPE_KERNEL_PANIC, ++oops_count);
+ (int) (sizeof(*oops_len) + *oops_len), err_type, ++oops_count);
}
^ permalink raw reply related
* Re: [PATCH 13/14] 85xx: consolidate of_platform_bus_probe calls
From: Scott Wood @ 2011-07-25 15:40 UTC (permalink / raw)
To: Dmitry Eremin-Solenikov; +Cc: Paul Mackerras, Linux PPC Development
In-Reply-To: <CALT56yOZkLW=AmkyX77g5agxfBb0h7Fz8Q_enth-CJycJ3SkFA@mail.gmail.com>
On Sat, 23 Jul 2011 01:45:53 +0400
Dmitry Eremin-Solenikov <dbaryshkov@gmail.com> wrote:
> I see your point. I just wasn't thinking too much about ot-of-tree trees.
> My thought was that if someone updates the kernel, he can also update the dtb.
Sometimes there are firmware dependencies that make that difficult. And
even if it's just user laziness/forgetfulness, that still translates to
extra support requests.
> Could you please update the lbc.txt suggesting the compatibility
> with simple-bus for lbc? Or you thing that it would be wrong?
>
> I think we should define compatibility list as "fsl,mpcXXXX-localbus",
> "fsl,pqXXXXX-localbus", "simple-bus", noting that by default new
> platforms/boards should only use "simple-bus" internally. Does this
> look reasonable for you? I can then try to provide a patch.
I'm OK with saying that localbus nodes should have simple-bus in new trees,
and defining canonical compatible values (chips with eLBC should be
"fsl,XXXX-elbc", "fsl,elbc", "simple-bus"). I'm not sure what you mean by
"should only use simple-bus internally", especially in the context of the
binding.
> What do you suggest/prefer? To add .name="localbus" to generic code
> or to have board-specific hooks (like one for mpc834xemitx)?
Just add localbus to the generic table.
-Scott
^ permalink raw reply
* Re: perf PPC: kernel panic with callchains and context switch events
From: David Ahern @ 2011-07-25 15:38 UTC (permalink / raw)
To: Benjamin Herrenschmidt, Anton Blanchard
Cc: LKML, linux-perf-users, Paul Mackerras, linuxppc-dev
In-Reply-To: <1311558949.25044.614.camel@pasglop>
Hi Ben:
On 07/24/2011 07:55 PM, Benjamin Herrenschmidt wrote:
> On Sun, 2011-07-24 at 11:18 -0600, David Ahern wrote:
>> On 07/20/2011 03:57 PM, David Ahern wrote:
>>> I am hoping someone familiar with PPC can help understand a panic that
>>> is generated when capturing callchains with context switch events.
>>>
>>> Call trace is below. The short of it is that walking the callchain
>>> generates a page fault. To handle the page fault the mmap_sem is needed,
>>> but it is currently held by setup_arg_pages. setup_arg_pages calls
>>> shift_arg_pages with the mmap_sem held. shift_arg_pages then calls
>>> move_page_tables which has a cond_resched at the top of its for loop. If
>>> the cond_resched() is removed from move_page_tables everything works
>>> beautifully - no panics.
>>>
>>> So, the question: is it normal for walking the stack to trigger a page
>>> fault on PPC? The panic is not seen on x86 based systems.
>>
>> Can anyone confirm whether page faults while walking the stack are
>> normal for PPC? We really want to use the context switch event with
>> callchains and need to understand whether this behavior is normal. Of
>> course if it is normal, a way to address the problem without a panic
>> will be needed.
>
> Now that leads to interesting discoveries :-) Becky, can you read all
> the way and let me know what you think ?
>
> So, trying to walk the user stack directly will potentially cause page
> faults if it's done by direct access. So if you're going to do it in a
> spot where you can't afford it, you need to pagefault_disable() I
> suppose. I think the problem with our existing code is that it's missing
> those around __get_user_inatomic().
>
> In fact, arguably, we don't want the hash code from modifying the hash
> either (or even hashing things in). Our 64-bit code handles it today in
> perf_callchain.c in a way that involves pretty much duplicating the
> functionality of __get_user_pages_fast() as used by x86 (see below), but
> as a fallback from a direct access which misses the pagefault_disable()
> as well.
>
> I think it comes from an old assumption that this would always be called
> from an nmi, and the explicit tracepoints broke that assumption.
>
> In fact we probably want to bump the NMI count, not just the IRQ count
> as pagefault_disable() does, to make sure we prevent hashing.
>
> x86 does things differently, using __get_user_pages_fast() (a variant of
> get_user_page_fast() that doesn't fallback to normal get_user_pages()).
>
> Now, we could do the same (use __gup_fast too), but I can see a
> potential issue with ppc 32-bit platforms that have 64-bit PTEs, since
> we could end up GUP'ing in the middle of the two accesses.
>
> Becky: I think gup_fast is generally broken on 32-bit with 64-bit PTE
> because of that, the problem isn't specific to perf backtraces, I'll
> propose a solution further down.
>
> Now, on x86, there is a similar problem with PAE, which is handled by
>
> - having gup disable IRQs
> - rely on the fact that to change from a valid value to another valid
> value, the PTE will first get invalidated, which requires an IPI
> and thus will be blocked by our interrupts being off
>
> We do the first part, but the second part will break if we use HW TLB
> invalidation broadcast (yet another reason why those are bad, I think I
> will write a blog entry about it one of these days).
>
> I think we can work around this while keeping our broadcast TLB
> invalidations by having the invalidation code also increment a global
> generation count (using the existing lock used by the invalidation code,
> all 32-bit platforms have such a lock).
>
> From there, gup_fast can be changed to, with proper ordering, check the
> generation count around the loading of the PTE and loop if it has
> changed, kind-of a seqlock.
>
> We also need the NMI count bump if we are going to try to keep the
> attempt at doing a direct access first for perfs.
>
> Becky, do you feel like giving that a shot or should I find another
> victim ? (Or even do it myself ... ) :-)
Did you have something in mind besides the patch Anton sent? We'll give
that one a try and see how it works. (Thanks, Anton!)
David
>
> Cheers,
> Ben.
>
>> Thanks,
>> David
>>
>>>
>>> [<b0180e00>]rb_erase+0x1b4/0x3e8
>>> [<b00430f4>]__dequeue_entity+0x50/0xe8
>>> [<b0043304>]set_next_entity+0x178/0x1bc
>>> [<b0043440>]pick_next_task_fair+0xb0/0x118
>>> [<b02ada80>]schedule+0x500/0x614
>>> [<b02afaa8>]rwsem_down_failed_common+0xf0/0x264
>>> [<b02afca0>]rwsem_down_read_failed+0x34/0x54
>>> [<b02aed4c>]down_read+0x3c/0x54
>>> [<b0023b58>]do_page_fault+0x114/0x5e8
>>> [<b001e350>]handle_page_fault+0xc/0x80
>>> [<b0022dec>]perf_callchain+0x224/0x31c
>>> [<b009ba70>]perf_prepare_sample+0x240/0x2fc
>>> [<b009d760>]__perf_event_overflow+0x280/0x398
>>> [<b009d914>]perf_swevent_overflow+0x9c/0x10c
>>> [<b009db54>]perf_swevent_ctx_event+0x1d0/0x230
>>> [<b009dc38>]do_perf_sw_event+0x84/0xe4
>>> [<b009dde8>]perf_sw_event_context_switch+0x150/0x1b4
>>> [<b009de90>]perf_event_task_sched_out+0x44/0x2d4
>>> [<b02ad840>]schedule+0x2c0/0x614
>>> [<b0047dc0>]__cond_resched+0x34/0x90
>>> [<b02adcc8>]_cond_resched+0x4c/0x68
>>> [<b00bccf8>]move_page_tables+0xb0/0x418
>>> [<b00d7ee0>]setup_arg_pages+0x184/0x2a0
>>> [<b0110914>]load_elf_binary+0x394/0x1208
>>> [<b00d6e28>]search_binary_handler+0xe0/0x2c4
>>> [<b00d834c>]do_execve+0x1bc/0x268
>>> [<b0015394>]sys_execve+0x84/0xc8
>>> [<b001df10>]ret_from_syscall+0x0/0x3c
>>>
>>> Thanks,
>>> David
>> _______________________________________________
>> Linuxppc-dev mailing list
>> Linuxppc-dev@lists.ozlabs.org
>> https://lists.ozlabs.org/listinfo/linuxppc-dev
>
>
^ permalink raw reply
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