Add in-kernel debug-bus signal routing for Cell systems that do not include
firmware with the "ibm,cbe-perftools" RTAS call. This code only handles two
signal-groups: PPU Instruction Unit (Group 1) and PPU Execution Unit.
This is a modification of the patches posted by Takaki Azuma
([EMAIL PROTECTED]) and Takayuki Uchikawa
([EMAIL PROTECTED]). The code from perfmon_cell_signals.c
and perfmon_cell_signals.h has been merged into perfmon_cell.c. Having the
multiple files causes headaches with the kernel build when perfmon_cell is
configured as a module. The additional code is only about 300 lines, so I
think it's reasonable to include it in the same file as the existing code.
Other minor changes and bug-fixes were also made.
Signed-off-by: Kevin Corry <[EMAIL PROTECTED]>
---
arch/powerpc/perfmon/perfmon_cell.c | 341 ++++++++++++++++++++++++++++++++++-
1 files changed, 330 insertions(+), 11 deletions(-)
diff --git a/arch/powerpc/perfmon/perfmon_cell.c
b/arch/powerpc/perfmon/perfmon_cell.c
index 91c6f75..6f6eb8f 100644
--- a/arch/powerpc/perfmon/perfmon_cell.c
+++ b/arch/powerpc/perfmon/perfmon_cell.c
@@ -3,6 +3,7 @@
* and pmc checker used by perfmon.c.
*
* Copyright IBM Corporation 2007
+ * (C) Copyright 2007 TOSHIBA CORPORATION
*
* Based on other Perfmon2 PMU modules.
* Copyright (c) 2005-2006 Hewlett-Packard Development Company, L.P.
@@ -27,6 +28,7 @@
#include <linux/perfmon.h>
#include <asm/cell-pmu.h>
#include <asm/io.h>
+#include <asm/machdep.h>
#include <asm/rtas.h>
#include "../platforms/cell/cbe_regs.h"
#include <asm/perfmon_cell_hw_smpl.h>
@@ -92,6 +94,14 @@ static struct pfm_regmap_desc pfm_cell_pmd_desc[] = {
};
#define PFM_PM_NUM_PMDS ARRAY_SIZE(pfm_cell_pmd_desc)
+/*
+ * Debug-bus signal handling.
+ *
+ * Some Cell systems have firmware that can handle the debug-bus signal
+ * routing. For systems without this firmware, we have a minimal in-kernel
+ * implementation as well.
+ */
+
/* The firmware only sees physical CPUs, so divide by 2 if SMT is on. */
#ifdef CONFIG_SCHED_SMT
#define RTAS_CPU(cpu) ((cpu) / 2)
@@ -101,6 +111,7 @@ static struct pfm_regmap_desc pfm_cell_pmd_desc[] = {
#define RTAS_BUS_WORD(x) (u16)(((x) >> 48) & 0x0000ffff)
#define RTAS_SUB_UNIT(x) (u16)(((x) >> 32) & 0x0000ffff)
#define RTAS_SIGNAL_NUMBER(x) (s32)( (x) & 0xffffffff)
+#define RTAS_SIGNAL_GROUP(x) (RTAS_SIGNAL_NUMBER(x) / 100)
#define subfunc_RESET 1
#define subfunc_ACTIVATE 2
@@ -135,8 +146,8 @@ struct cell_rtas_arg {
/**
* rtas_reset_signals
*
- * Set up the RTAS arguments for a RESET command. The buffer will be only
- * the first entry in the rtas_args[cpu].signal[] array.
+ * Use the firmware RTAS call to disable signal pass-thru and to reset the
+ * debug-bus signals.
**/
static int rtas_reset_signals(u32 cpu)
{
@@ -160,9 +171,8 @@ static int rtas_reset_signals(u32 cpu)
/**
* rtas_activate_signals
*
- * Set up the RTAS arguments for an ACTIVATE command. The buffer will be the
- * number of entries in the rtas_args[cpu].signal[] array that were filled
- * in by attach_signal_to_counter().
+ * Use the firmware RTAS call to enable signal pass-thru and to activate the
+ * desired signal groups on the debug-bus.
**/
static int rtas_activate_signals(struct cell_rtas_arg *signals,
int num_signals)
@@ -181,6 +191,298 @@ static int rtas_activate_signals(struct cell_rtas_arg
*signals,
return rc;
}
+#define HID1_RESET_MASK (~0x00000001ffffffffUL)
+#define PPU_IU1_WORD0_HID1_EN_MASK (~0x00000001f0c0802cUL)
+#define PPU_IU1_WORD0_HID1_EN_WORD ( 0x00000001f0400000UL)
+#define PPU_IU1_WORD1_HID1_EN_MASK (~0x000000010fc08023UL)
+#define PPU_IU1_WORD1_HID1_EN_WORD ( 0x000000010f400001UL)
+#define PPU_XU_WORD0_HID1_EN_MASK (~0x00000001f038402cUL)
+#define PPU_XU_WORD0_HID1_EN_WORD ( 0x00000001f0080008UL)
+#define PPU_XU_WORD1_HID1_EN_MASK (~0x000000010f074023UL)
+#define PPU_XU_WORD1_HID1_EN_WORD ( 0x000000010f030002UL)
+
+/* The bus_word field in the cell_rtas_arg structure is a bit-mask
+ * indicating which debug-bus word(s) to use.
+ */
+enum {
+ BUS_WORD_0 = 1,
+ BUS_WORD_1 = 2,
+ BUS_WORD_2 = 4,
+ BUS_WORD_3 = 8,
+};
+
+/* Definitions of the signal-groups that the built-in signal-activation
+ * code can handle.
+ */
+enum {
+ SIG_GROUP_NONE = 0,
+
+ /* 2.x PowerPC Processor Unit (PPU) Signal Groups */
+ SIG_GROUP_PPU_IU1 = 21,
+ SIG_GROUP_PPU_XU = 22,
+};
+
+/**
+ * rmw_spr
+ *
+ * Read-modify-write for a special-purpose-register.
+ **/
+#define rmw_spr(spr_id, a_mask, o_mask) \
+ do { \
+ u64 value = mfspr(spr_id); \
+ value &= (u64)(a_mask); \
+ value |= (u64)(o_mask); \
+ mtspr((spr_id), value); \
+ } while (0)
+
+/**
+ * rmw_mmio_reg64
+ *
+ * Read-modify-write for a 64-bit MMIO register.
+ **/
+#define rmw_mmio_reg64(mem, a_mask, o_mask) \
+ do { \
+ u64 value = in_be64(&(mem)); \
+ value &= (u64)(a_mask); \
+ value |= (u64)(o_mask); \
+ out_be64(&(mem), value); \
+ } while (0)
+
+/**
+ * rmwb_mmio_reg64
+ *
+ * Set or unset a specified bit within a 64-bit MMIO register.
+ **/
+#define rmwb_mmio_reg64(mem, bit_num, set_bit) \
+ rmw_mmio_reg64((mem), ~(1UL << (63 - (bit_num))), \
+ ((set_bit) << (63 - (bit_num))))
+
+/**
+ * passthru
+ *
+ * Enable or disable passthru mode in all the Cell signal islands.
+ **/
+static int passthru(u32 cpu, u64 enable)
+{
+ struct cbe_ppe_priv_regs __iomem *ppe_priv_regs;
+ struct cbe_pmd_regs __iomem *pmd_regs;
+ struct cbe_mic_tm_regs __iomem *mic_tm_regs;
+
+ ppe_priv_regs = cbe_get_cpu_ppe_priv_regs(cpu);
+ pmd_regs = cbe_get_cpu_pmd_regs(cpu);
+ mic_tm_regs = cbe_get_cpu_mic_tm_regs(cpu);
+
+ if (!ppe_priv_regs || !pmd_regs || !mic_tm_regs) {
+ PFM_ERR("Error getting Cell PPE, PMD, and MIC "
+ "register maps: 0x%p, 0x%p, 0x%p",
+ ppe_priv_regs, pmd_regs, mic_tm_regs);
+ return -EINVAL;
+ }
+
+ rmwb_mmio_reg64(ppe_priv_regs->L2_debug1, 61, enable);
+ rmwb_mmio_reg64(ppe_priv_regs->ciu_dr1, 5, enable);
+ rmwb_mmio_reg64(pmd_regs->on_ramp_trace, 39, enable);
+ rmwb_mmio_reg64(mic_tm_regs->MBL_debug, 20, enable);
+
+ return 0;
+}
+
+#define passthru_enable(cpu) passthru(cpu, 1)
+#define passthru_disable(cpu) passthru(cpu, 0)
+
+static inline void reset_signal_registers(u32 cpu)
+{
+ rmw_spr(SPRN_HID1, HID1_RESET_MASK, 0);
+}
+
+/**
+ * celleb_reset_signals
+ *
+ * Non-rtas version of resetting the debug-bus signals.
+ **/
+static int celleb_reset_signals(u32 cpu)
+{
+ int rc;
+ rc = passthru_disable(cpu);
+ if (!rc)
+ reset_signal_registers(cpu);
+ return rc;
+}
+
+/**
+ * ppu_selection
+ *
+ * Write the HID1 register to connect the specified PPU signal-group to the
+ * debug-bus.
+ **/
+static int ppu_selection(struct cell_rtas_arg *signal)
+{
+ u64 hid1_enable_word = 0;
+ u64 hid1_enable_mask = 0;
+
+ switch (signal->signal_group) {
+
+ case SIG_GROUP_PPU_IU1: /* 2.1 PPU Instruction Unit - Group 1 */
+ switch (signal->bus_word) {
+ case BUS_WORD_0:
+ hid1_enable_mask = PPU_IU1_WORD0_HID1_EN_MASK;
+ hid1_enable_word = PPU_IU1_WORD0_HID1_EN_WORD;
+ break;
+ case BUS_WORD_1:
+ hid1_enable_mask = PPU_IU1_WORD1_HID1_EN_MASK;
+ hid1_enable_word = PPU_IU1_WORD1_HID1_EN_WORD;
+ break;
+ default:
+ PFM_ERR("Invalid bus-word (0x%x) for signal-group %d.",
+ signal->bus_word, signal->signal_group);
+ return -EINVAL;
+ }
+ break;
+
+ case SIG_GROUP_PPU_XU: /* 2.2 PPU Execution Unit */
+ switch (signal->bus_word) {
+ case BUS_WORD_0:
+ hid1_enable_mask = PPU_XU_WORD0_HID1_EN_MASK;
+ hid1_enable_word = PPU_XU_WORD0_HID1_EN_WORD;
+ break;
+ case BUS_WORD_1:
+ hid1_enable_mask = PPU_XU_WORD1_HID1_EN_MASK;
+ hid1_enable_word = PPU_XU_WORD1_HID1_EN_WORD;
+ break;
+ default:
+ PFM_ERR("Invalid bus-word (0x%x) for signal-group %d.",
+ signal->bus_word, signal->signal_group);
+ return -EINVAL;
+ }
+ break;
+
+ default:
+ PFM_ERR("Signal-group %d not implemented.",
+ signal->signal_group);
+ return -EINVAL;
+ }
+
+ rmw_spr(SPRN_HID1, hid1_enable_mask, hid1_enable_word);
+
+ return 0;
+}
+
+/**
+ * celleb_activate_signals
+ *
+ * Non-rtas version of activating the debug-bus signals.
+ **/
+static int celleb_activate_signals(struct cell_rtas_arg *signals,
+ int num_signals)
+{
+ int i, rc;
+
+ for (i = 0; i < num_signals; i++) {
+ switch (signals[i].signal_group) {
+
+ /* 2.x PowerPC Processor Unit (PPU) Signal Selection */
+ case SIG_GROUP_PPU_IU1:
+ case SIG_GROUP_PPU_XU:
+ rc = ppu_selection(signals + i);
+ if (rc)
+ return rc;
+ break;
+
+ default:
+ PFM_ERR("Signal-group %d not implemented.",
+ signals[i].signal_group);
+ return -EINVAL;
+ }
+ }
+
+ rc = passthru_enable(signals[i].cpu);
+
+ return rc;
+}
+
+/**
+ * reset_signals
+ *
+ * Call to the firmware (if available) to reset the debug-bus signals.
+ * Otherwise call the built-in version.
+ **/
+int reset_signals(u32 cpu)
+{
+ int rc;
+
+ if (machine_is(celleb))
+ rc = celleb_reset_signals(cpu);
+ else
+ rc = rtas_reset_signals(cpu);
+
+ return rc;
+}
+
+/**
+ * activate_signals
+ *
+ * Call to the firmware (if available) to activate the debug-bus signals.
+ * Otherwise call the built-in version.
+ **/
+int activate_signals(struct cell_rtas_arg *signals, int num_signals)
+{
+ int rc;
+
+ if (machine_is(celleb))
+ rc = celleb_activate_signals(signals, num_signals);
+ else
+ rc = rtas_activate_signals(signals, num_signals);
+
+ return rc;
+}
+
+/**
+ * pfm_cell_pmc_check
+ *
+ * Verify that we are going to write a valid value to the specified PMC.
+ **/
+int pfm_cell_pmc_check(struct pfm_context *ctx,
+ struct pfm_event_set *set,
+ struct pfarg_pmc *req)
+{
+ u16 cnum, reg_num = req->reg_num;
+ s16 signal_group = RTAS_SIGNAL_GROUP(req->reg_value);
+ u8 bus_word = RTAS_BUS_WORD(req->reg_value);
+
+ if (reg_num < NR_CTRS || reg_num >= (NR_CTRS * 2)) {
+ return -EINVAL;
+ }
+
+ switch (signal_group) {
+ case SIG_GROUP_PPU_IU1:
+ case SIG_GROUP_PPU_XU:
+ if ((bus_word != 0) && (bus_word != 1)) {
+ PFM_ERR("Invalid bus word (%d) for signal-group %d",
+ bus_word, signal_group);
+ return -EINVAL;
+ }
+ break;
+ default:
+ PFM_ERR("Signal-group %d not implemented.", signal_group);
+ return -EINVAL;
+ }
+
+ for (cnum = NR_CTRS; cnum < (NR_CTRS * 2); cnum++) {
+ if (test_bit(cnum, cast_ulp(set->used_pmcs)) &&
+ bus_word == RTAS_BUS_WORD(set->pmcs[cnum]) &&
+ signal_group != RTAS_SIGNAL_GROUP(set->pmcs[cnum])) {
+ PFM_ERR("Impossible signal-group combination: "
+ "(%u,%u,%d) (%u,%u,%d)",
+ reg_num, bus_word, signal_group, cnum,
+ RTAS_BUS_WORD(set->pmcs[cnum]),
+ RTAS_SIGNAL_GROUP(set->pmcs[cnum]));
+ return -EBUSY;
+ }
+ }
+
+ return 0;
+}
+
/**
* write_pm07_event
*
@@ -205,10 +507,10 @@ static void write_pm07_event(int cpu, unsigned int ctr,
u64 value)
signal.signal_group = signal_number / 100;
signal.bit = signal_number % 100;
- rc = rtas_activate_signals(&signal, 1);
+ rc = activate_signals(&signal, 1);
if (rc) {
PFM_WARN("%s(%d, %u, %lu): Error calling "
- "rtas_activate_signal(): %d\n", __FUNCTION__,
+ "activate_signals(): %d\n", __FUNCTION__,
cpu, ctr, (unsigned long)value, rc);
/* FIX: Could we change this routine to return an error? */
}
@@ -367,9 +669,9 @@ void pfm_cell_restore_pmcs(struct pfm_event_set *set)
num_used++;
}
- rc = rtas_activate_signals(signals, num_used);
+ rc = activate_signals(signals, num_used);
if (rc) {
- PFM_WARN("Error calling rtas_activate_signal(): %d\n", rc);
+ PFM_WARN("Error calling activate_signal(): %d\n", rc);
/* FIX: We will also need this routine to be able to return
* an error if Stephane agrees to change pfm_arch_write_pmc
* to return an error.
@@ -397,7 +699,7 @@ static int pfm_cell_unload_context(struct pfm_context *ctx,
struct task_struct *task)
{
if (task == current || ctx->flags.system) {
- rtas_reset_signals(smp_processor_id());
+ reset_signals(smp_processor_id());
}
return 0;
}
@@ -412,7 +714,7 @@ static int pfm_cell_unload_context(struct pfm_context *ctx,
int pfm_cell_ctxswout_thread(struct task_struct *task,
struct pfm_context *ctx, struct pfm_event_set *set)
{
- rtas_reset_signals(smp_processor_id());
+ reset_signals(smp_processor_id());
return 0;
}
@@ -614,8 +916,25 @@ static struct pfm_pmu_config pfm_cell_pmu_conf = {
.owner = THIS_MODULE,
};
+/**
+ * pfm_cell_platform_probe
+ *
+ * If we're on a system without the firmware rtas call available, set up the
+ * PMC write-checker for all the pmX_event control registers.
+ **/
+static void pfm_cell_platform_probe(void)
+{
+ if (machine_is(celleb)) {
+ int cnum;
+ pfm_cell_pmu_conf.pmc_write_check = pfm_cell_pmc_check;
+ for (cnum = NR_CTRS; cnum < (NR_CTRS * 2); cnum++)
+ pfm_cell_pmc_desc[cnum].type |= PFM_REG_WC;
+ }
+}
+
static int __init pfm_cell_pmu_init_module(void)
{
+ pfm_cell_platform_probe();
return pfm_pmu_register(&pfm_cell_pmu_conf);
}
_______________________________________________
perfmon mailing list
[email protected]
http://www.hpl.hp.com/hosted/linux/mail-archives/perfmon/