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/

Reply via email to