Add read_pmd and write_pmc/pmd function pointers to the pfm_arch_pmu_info
structure. This will allow the powerpc common code to call the model-specific
routines to read/write the desired registers. Then we can remove the
model-specific code from the powerpc common code.
For Cell, the control registers are split into three "groups" - the per-counter
control registers, the per-counter event registers, and the global control
registers. This also adds the routines for making the RTAS calls to the
firmware, which is used for writing the per-counter event registers.
Signed-off-by: Kevin Corry <[EMAIL PROTECTED]>
Signed-off-by: Carl Love <[EMAIL PROTECTED]>
Index: linux-2.6.20-arnd3-perfmon4/include/asm-powerpc/perfmon.h
===================================================================
--- linux-2.6.20-arnd3-perfmon4.orig/include/asm-powerpc/perfmon.h
+++ linux-2.6.20-arnd3-perfmon4/include/asm-powerpc/perfmon.h
@@ -44,6 +44,11 @@ enum powerpc_pmu_type {
struct pfm_arch_pmu_info {
enum powerpc_pmu_type pmu_style;
+
+ void (*write_pmc)(unsigned int cnum, u64 value);
+ void (*write_pmd)(unsigned int cnum, u64 value);
+
+ u64 (*read_pmd)(unsigned int cnum);
};
#ifdef CONFIG_PPC32
@@ -64,114 +69,39 @@ static inline void pfm_arch_write_pmc(st
unsigned int cnum,
u64 value)
{
+ struct pfm_arch_pmu_info *arch_info = pfm_pmu_conf->arch_info;
+
/*
* we only write to the actual register when monitoring is
* active (pfm_start was issued)
*/
- if (ctx && (ctx->flags.started == 0))
+ if (ctx && ctx->flags.started == 0)
return;
- switch (pfm_pmu_conf->pmc_desc[cnum].hw_addr) {
- case SPRN_MMCR0:
- mtspr(SPRN_MMCR0, value);
- break;
- case SPRN_MMCR1:
- mtspr(SPRN_MMCR1, value);
- break;
-#ifdef CONFIG_PPC64
- case SPRN_MMCRA:
- mtspr(SPRN_MMCRA, value);
- break;
-#else
- case SPRN_MMCR2:
- mtspr(SPRN_MMCR2, value);
- break;
-#endif
- default:
- BUG();
- }
-}
+ BUG_ON(!arch_info->write_pmc);
-static inline u64 pfm_arch_read_pmc(struct pfm_context *ctx, unsigned int cnum)
-{
- switch (pfm_pmu_conf->pmc_desc[cnum].hw_addr) {
- case SPRN_MMCR0:
- return mfspr(SPRN_MMCR0);
- case SPRN_MMCR1:
- return mfspr(SPRN_MMCR1);
-#ifdef CONFIG_PPC64
- case SPRN_MMCRA:
- return mfspr(SPRN_MMCRA);
-#else
- case SPRN_MMCR2:
- return mfspr(SPRN_MMCR2);
-#endif
- default:
- BUG();
- }
+ arch_info->write_pmc(cnum, value);
}
static inline void pfm_arch_write_pmd(struct pfm_context *ctx,
unsigned int cnum, u64 value)
{
+ struct pfm_arch_pmu_info *arch_info = pfm_pmu_conf->arch_info;
+
value &= pfm_pmu_conf->ovfl_mask;
- switch (pfm_pmu_conf->pmd_desc[cnum].hw_addr) {
- case SPRN_PMC1:
- mtspr(SPRN_PMC1, value);
- break;
- case SPRN_PMC2:
- mtspr(SPRN_PMC2, value);
- break;
- case SPRN_PMC3:
- mtspr(SPRN_PMC3, value);
- break;
- case SPRN_PMC4:
- mtspr(SPRN_PMC4, value);
- break;
- case SPRN_PMC5:
- mtspr(SPRN_PMC5, value);
- break;
- case SPRN_PMC6:
- mtspr(SPRN_PMC6, value);
- break;
-#ifdef CONFIG_PPC64
- case SPRN_PMC7:
- mtspr(SPRN_PMC7, value);
- break;
- case SPRN_PMC8:
- mtspr(SPRN_PMC8, value);
- break;
-#endif
- default:
- BUG();
- }
+ BUG_ON(!arch_info->write_pmd);
+
+ arch_info->write_pmd(cnum, value);
}
static inline u64 pfm_arch_read_pmd(struct pfm_context *ctx, unsigned int cnum)
{
- switch (pfm_pmu_conf->pmd_desc[cnum].hw_addr) {
- case SPRN_PMC1:
- return mfspr(SPRN_PMC1);
- case SPRN_PMC2:
- return mfspr(SPRN_PMC2);
- case SPRN_PMC3:
- return mfspr(SPRN_PMC3);
- case SPRN_PMC4:
- return mfspr(SPRN_PMC4);
- case SPRN_PMC5:
- return mfspr(SPRN_PMC5);
- case SPRN_PMC6:
- return mfspr(SPRN_PMC6);
-#ifdef CONFIG_PPC64
- case SPRN_PMC7:
- return mfspr(SPRN_PMC7);
- case SPRN_PMC8:
- return mfspr(SPRN_PMC8);
-#endif
- default:
- BUG();
- }
+ struct pfm_arch_pmu_info *arch_info = pfm_pmu_conf->arch_info;
+
+ BUG_ON(!arch_info->read_pmd);
+
+ return arch_info->read_pmd(cnum);
}
/*
Index: linux-2.6.20-arnd3-perfmon4/arch/powerpc/perfmon/perfmon_power5.c
===================================================================
--- linux-2.6.20-arnd3-perfmon4.orig/arch/powerpc/perfmon/perfmon_power5.c
+++ linux-2.6.20-arnd3-perfmon4/arch/powerpc/perfmon/perfmon_power5.c
@@ -29,10 +29,6 @@ MODULE_AUTHOR("David Gibson <[EMAIL PROTECTED]
MODULE_DESCRIPTION("POWER5 PMU description table");
MODULE_LICENSE("GPL");
-struct pfm_arch_pmu_info pfm_power5_pmu_info = {
- .pmu_style = PFM_POWERPC_PMU_POWER5,
-};
-
static struct pfm_reg_desc pfm_power5_pmc_desc[]={
/* mmcr0 */ PMC_D(PFM_REG_I, "MMCR0", MMCR0_FC, 0, 0, SPRN_MMCR0),
/* mmcr1 */ PMC_D(PFM_REG_I, "MMCR1", 0, 0, 0, SPRN_MMCR1),
@@ -66,6 +62,97 @@ static int pfm_power5_probe_pmu(void)
return 0;
}
+static void pfm_power5_write_pmc(unsigned int cnum, u64 value)
+{
+ switch (pfm_pmu_conf->pmc_desc[cnum].hw_addr) {
+ case SPRN_MMCR0:
+ mtspr(SPRN_MMCR0, value);
+ break;
+ case SPRN_MMCR1:
+ mtspr(SPRN_MMCR1, value);
+ break;
+ case SPRN_MMCRA:
+ mtspr(SPRN_MMCRA, value);
+ break;
+ default:
+ BUG();
+ }
+}
+
+static void pfm_power5_write_pmd(unsigned int cnum, u64 value)
+{
+ switch (pfm_pmu_conf->pmd_desc[cnum].hw_addr) {
+ case SPRN_PMC1:
+ mtspr(SPRN_PMC1, value);
+ break;
+ case SPRN_PMC2:
+ mtspr(SPRN_PMC2, value);
+ break;
+ case SPRN_PMC3:
+ mtspr(SPRN_PMC3, value);
+ break;
+ case SPRN_PMC4:
+ mtspr(SPRN_PMC4, value);
+ break;
+ case SPRN_PMC5:
+ mtspr(SPRN_PMC5, value);
+ break;
+ case SPRN_PMC6:
+ mtspr(SPRN_PMC6, value);
+ break;
+ case SPRN_PMC7:
+ mtspr(SPRN_PMC7, value);
+ break;
+ case SPRN_PMC8:
+ mtspr(SPRN_PMC8, value);
+ break;
+ case SPRN_TBRL:
+ case SPRN_PURR:
+ /* Ignore writes to read-only registers. */
+ break;
+ default:
+ BUG();
+ }
+}
+
+static u64 pfm_power5_read_pmd(unsigned int cnum)
+{
+ switch (pfm_pmu_conf->pmd_desc[cnum].hw_addr) {
+ case SPRN_PMC1:
+ return mfspr(SPRN_PMC1);
+ case SPRN_PMC2:
+ return mfspr(SPRN_PMC2);
+ case SPRN_PMC3:
+ return mfspr(SPRN_PMC3);
+ case SPRN_PMC4:
+ return mfspr(SPRN_PMC4);
+ case SPRN_PMC5:
+ return mfspr(SPRN_PMC5);
+ case SPRN_PMC6:
+ return mfspr(SPRN_PMC6);
+ case SPRN_PMC7:
+ return mfspr(SPRN_PMC7);
+ case SPRN_PMC8:
+ return mfspr(SPRN_PMC8);
+ case SPRN_TBRL:
+ return ((u64)mfspr(SPRN_TBRU) << 32) | mfspr(SPRN_TBRL);
+ case SPRN_PURR:
+ if (cpu_has_feature(CPU_FTR_PURR))
+ return mfspr(SPRN_PURR);
+ else
+ return 0;
+ default:
+ BUG();
+ }
+}
+
+struct pfm_arch_pmu_info pfm_power5_pmu_info = {
+ .pmu_style = PFM_POWERPC_PMU_POWER5,
+ .write_pmc = pfm_power5_write_pmc,
+ .write_pmd = pfm_power5_write_pmd,
+ .read_pmd = pfm_power5_read_pmd,
+};
+
/*
* impl_pmcs, impl_pmds are computed at runtime to minimize errors!
*/
Index: linux-2.6.20-arnd3-perfmon4/arch/powerpc/perfmon/perfmon_ppc32.c
===================================================================
--- linux-2.6.20-arnd3-perfmon4.orig/arch/powerpc/perfmon/perfmon_ppc32.c
+++ linux-2.6.20-arnd3-perfmon4/arch/powerpc/perfmon/perfmon_ppc32.c
@@ -33,10 +33,6 @@ MODULE_AUTHOR("Philip Mucci <[EMAIL PROTECTED]
MODULE_DESCRIPTION("PPC32 PMU description table");
MODULE_LICENSE("GPL");
-struct pfm_arch_pmu_info pfm_ppc32_pmu_info = {
- .pmu_style = PFM_POWERPC_PMU_NONE,
-};
-
static struct pfm_pmu_config pfm_ppc32_pmu_conf;
static struct pfm_reg_desc pfm_ppc32_pmc_desc[] = {
@@ -181,6 +177,76 @@ static int pfm_ppc32_probe_pmu(void)
return reserve_pmc_hardware(perfmon_perf_irq);
}
+static void pfm_ppc32_write_pmc(unsigned int cnum, u64 value)
+{
+ switch (pfm_pmu_conf->pmc_desc[cnum].hw_addr) {
+ case SPRN_MMCR0:
+ mtspr(SPRN_MMCR0, value);
+ break;
+ case SPRN_MMCR1:
+ mtspr(SPRN_MMCR1, value);
+ break;
+ case SPRN_MMCR2:
+ mtspr(SPRN_MMCR2, value);
+ break;
+ default:
+ BUG();
+ }
+}
+
+static void pfm_ppc32_write_pmd(unsigned int cnum, u64 value)
+{
+ switch (pfm_pmu_conf->pmd_desc[cnum].hw_addr) {
+ case SPRN_PMC1:
+ mtspr(SPRN_PMC1, value);
+ break;
+ case SPRN_PMC2:
+ mtspr(SPRN_PMC2, value);
+ break;
+ case SPRN_PMC3:
+ mtspr(SPRN_PMC3, value);
+ break;
+ case SPRN_PMC4:
+ mtspr(SPRN_PMC4, value);
+ break;
+ case SPRN_PMC5:
+ mtspr(SPRN_PMC5, value);
+ break;
+ case SPRN_PMC6:
+ mtspr(SPRN_PMC6, value);
+ break;
+ default:
+ BUG();
+ }
+}
+
+static u64 pfm_ppc32_read_pmd(unsigned int cnum)
+{
+ switch (pfm_pmu_conf->pmd_desc[cnum].hw_addr) {
+ case SPRN_PMC1:
+ return mfspr(SPRN_PMC1);
+ case SPRN_PMC2:
+ return mfspr(SPRN_PMC2);
+ case SPRN_PMC3:
+ return mfspr(SPRN_PMC3);
+ case SPRN_PMC4:
+ return mfspr(SPRN_PMC4);
+ case SPRN_PMC5:
+ return mfspr(SPRN_PMC5);
+ case SPRN_PMC6:
+ return mfspr(SPRN_PMC6);
+ default:
+ BUG();
+ }
+}
+
+struct pfm_arch_pmu_info pfm_ppc32_pmu_info = {
+ .pmu_style = PFM_POWERPC_PMU_NONE,
+ .write_pmc = pfm_ppc32_write_pmc,
+ .write_pmd = pfm_ppc32_write_pmd,
+ .read_pmd = pfm_ppc32_read_pmd,
+};
+
static struct pfm_pmu_config pfm_ppc32_pmu_conf = {
.counter_width = 31,
.pmd_desc = pfm_ppc32_pmd_desc,
Index: linux-2.6.20-arnd3-perfmon4/arch/powerpc/perfmon/perfmon_cell.c
===================================================================
--- linux-2.6.20-arnd3-perfmon4.orig/arch/powerpc/perfmon/perfmon_cell.c
+++ linux-2.6.20-arnd3-perfmon4/arch/powerpc/perfmon/perfmon_cell.c
@@ -25,6 +25,10 @@
#include <linux/module.h>
#include <linux/perfmon.h>
+#include <asm/cell-pmu.h>
+#include <asm/io.h>
+#include <asm/rtas.h>
+#include "../platforms/cell/cbe_regs.h"
MODULE_AUTHOR("Kevin Corry <[EMAIL PROTECTED]>, "
"Carl Love <[EMAIL PROTECTED]>");
@@ -87,6 +91,124 @@ static struct pfm_reg_desc pfm_cell_pmd_
};
#define PFM_PM_NUM_PMDS ARRAY_SIZE(pfm_cell_pmd_desc)
+/* The firmware only sees physical CPUs, so divide by 2 if SMT is on. */
+#ifdef CONFIG_SCHED_SMT
+#define RTAS_CPU(cpu) ((cpu) / 2)
+#else
+#define RTAS_CPU(cpu) (cpu)
+#endif
+#define RTAS_BUS_WORD(x) (((x) >> 48) & 0x00ff)
+#define RTAS_SUB_UNIT(x) (((x) >> 32) & 0x00ff)
+#define RTAS_SIGNAL_NUMBER(x) ( (x) & 0xffff)
+
+#define subfunc_RESET 1
+#define subfunc_ACTIVATE 2
+
+#define passthru_ENABLE 1
+#define passthru_DISABLE 2
+
+/**
+ * struct cell_rtas_arg
+ *
+ * @cpu: Processor to modify. Linux numbers CPUs based on SMT IDs, but the
+ * firmware only sees the physical CPUs. So this value should be the
+ * SMT ID (from smp_processor_id() or get_cpu()) divided by 2.
+ * @sub_unit: Hardware subunit this applies to (if applicable).
+ * @signal_group: Signal group to enable/disable on the trace bus.
+ * @bus_word: For signal groups that propagate via the trace bus, this trace
+ * bus word will be used. This is a mask of (1 << TraceBusWord).
+ * For other signal groups, this specifies the trigger or event bus.
+ * @bit: Trigger/Event bit, if applicable for the signal group.
+ *
+ * An array of these structures are passed to rtas_call() to set up the
+ * signals on the debug bus.
+ **/
+struct cell_rtas_arg {
+ u16 cpu;
+ u16 sub_unit;
+ s16 signal_group;
+ u8 bus_word;
+ u8 bit;
+};
+
+/**
+ * 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.
+ *
+ * FIX: When are we going to call this routine? Before each context-switch-out?
+ * After each context-switch-in?
+ **/
+static int rtas_reset_signals(u32 cpu)
+{
+ struct cell_rtas_arg signal;
+ u64 real_addr = virt_to_phys(&signal);
+ int rc;
+
+ memset(&signal, 0, sizeof(signal));
+ signal.cpu = RTAS_CPU(cpu);
+ rc = rtas_call(rtas_token("ibm,cbe-perftools"),
+ 5, 1, NULL,
+ subfunc_RESET,
+ passthru_DISABLE,
+ real_addr >> 32,
+ real_addr & 0xffffffff,
+ sizeof(signal));
+
+ return rc;
+}
+
+/**
+ * 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().
+ **/
+static int rtas_activate_signals(struct cell_rtas_arg *signals,
+ int num_signals)
+{
+ u64 real_addr = virt_to_phys(signals);
+ int rc;
+
+ rc = rtas_call(rtas_token("ibm,cbe-perftools"),
+ 5, 1, NULL,
+ subfunc_ACTIVATE,
+ passthru_ENABLE,
+ real_addr >> 32,
+ real_addr & 0xffffffff,
+ num_signals * sizeof(*signals));
+
+ return rc;
+}
+
+/**
+ * write_pm07_event
+ *
+ * Pull out the RTAS arguments from the 64-bit register value and make the
+ * RTAS activate-signals call.
+ **/
+static void write_pm07_event(int cpu, unsigned int ctr, u64 value)
+{
+ struct cell_rtas_arg signal;
+ int rc;
+
+ signal.cpu = RTAS_CPU(cpu);
+ signal.bus_word = 1 << RTAS_BUS_WORD(value);
+ signal.sub_unit = RTAS_SUB_UNIT(value);
+ signal.signal_group = RTAS_SIGNAL_NUMBER(value) / 100;
+ signal.bit = RTAS_SIGNAL_NUMBER(value) % 100;
+
+ rc = rtas_activate_signals(&signal, 1);
+ if (rc) {
+ PFM_WARN("%s(%d, %u, %lu): Error calling "
+ "rtas_activate_signal(): %d\n", __FUNCTION__,
+ cpu, ctr, (unsigned long)value, rc);
+ /* FIX: Could we change this routine to return an error? */
+ }
+}
+
/**
* pfm_cell_probe_pmu
*
@@ -103,8 +225,55 @@ static int pfm_cell_probe_pmu(void)
return 0;
}
+/**
+ * pfm_cell_write_pmc
+ **/
+static void pfm_cell_write_pmc(unsigned int cnum, u64 value)
+{
+ int cpu = smp_processor_id();
+
+ if (cnum < NR_CTRS) {
+ cbe_write_pm07_control(cpu, cnum, value);
+
+ } else if (cnum < NR_CTRS * 2) {
+ write_pm07_event(cpu, cnum - NR_CTRS, value);
+
+ } else if (cnum < PFM_PM_NUM_PMCS) {
+ cbe_write_pm(cpu, cnum - (NR_CTRS * 2), value);
+ }
+}
+
+/**
+ * pfm_cell_write_pmd
+ **/
+static void pfm_cell_write_pmd(unsigned int cnum, u64 value)
+{
+ int cpu = smp_processor_id();
+
+ if (cnum < NR_CTRS) {
+ cbe_write_ctr(cpu, cnum, value);
+ }
+}
+
+/**
+ * pfm_cell_read_pmd
+ **/
+static u64 pfm_cell_read_pmd(unsigned int cnum)
+{
+ int cpu = smp_processor_id();
+
+ if (cnum < NR_CTRS) {
+ return cbe_read_ctr(cpu, cnum);
+ }
+
+ return -EINVAL;
+}
+
static struct pfm_arch_pmu_info pfm_cell_pmu_info = {
.pmu_style = PFM_POWERPC_PMU_CELL,
+ .write_pmc = pfm_cell_write_pmc,
+ .write_pmd = pfm_cell_write_pmd,
+ .read_pmd = pfm_cell_read_pmd,
};
static struct pfm_pmu_config pfm_cell_pmu_conf = {
_______________________________________________
perfmon mailing list
[email protected]
http://www.hpl.hp.com/hosted/linux/mail-archives/perfmon/