Initial PS3 perfmon2 support.
On PS3, the PS3 PMU(arch/powerpc/platform/ps3/pmu.c) is used instead of
the Cell PMU(arch/powerpc/platform/cell/pmu.c) to control the HW PMU.
This patch basically replaces cbe_***() with ps3_***() to use the PS3 PMU.
To reduce platform dependency,
this patch adds the pfm_cell_platform_pmu_info structure which has
PMU function pointers for each Cell platform.
And this patch also adds aquire_pmu()/release_pmu() to pfm_arch_pmu_info
structure. They are needed to create/delete the PS3 PMU at PFM PMU
acquisition/
release.
Signed-off-by: Takashi Yamamoto <TakashiA.Yamamoto at jp.sony.com>
---
arch/powerpc/perfmon/perfmon_cell.c | 218
++++++++++++++++++++++++++++++++----
include/asm-powerpc/perfmon.h | 17 ++
2 files changed, 215 insertions(+), 20 deletions(-)
--- a/arch/powerpc/perfmon/perfmon_cell.c
+++ b/arch/powerpc/perfmon/perfmon_cell.c
@@ -31,12 +31,30 @@
#include <asm/io.h>
#include <asm/machdep.h>
#include <asm/rtas.h>
+#include <asm/ps3.h>
MODULE_AUTHOR("Kevin Corry <[EMAIL PROTECTED]>, "
"Carl Love <[EMAIL PROTECTED]>");
MODULE_DESCRIPTION("Cell PMU description table");
MODULE_LICENSE("GPL");
+struct pfm_cell_platform_pmu_info {
+ u32 (*read_ctr)(u32 cpu, u32 ctr);
+ void (*write_ctr)(u32 cpu, u32 ctr, u32 val);
+ void (*write_pm07_control)(u32 cpu, u32 ctr, u32 val);
+ void (*write_pm)(u32 cpu, enum pm_reg_name reg, u32 val);
+ void (*enable_pm)(u32 cpu);
+ void (*disable_pm)(u32 cpu);
+ void (*enable_pm_interrupts)(u32 cpu, u32 thread, u32 mask);
+ u32 (*get_and_clear_pm_interrupts)(u32 cpu);
+ u32 (*get_hw_thread_id)(int cpu);
+ struct cbe_ppe_priv_regs __iomem *(*get_cpu_ppe_priv_regs)(int cpu);
+ struct cbe_pmd_regs __iomem *(*get_cpu_pmd_regs)(int cpu);
+ struct cbe_mic_tm_regs __iomem *(*get_cpu_mic_tm_regs)(int cpu);
+ int (*rtas_token)(const char *service);
+ int (*rtas_call)(int token, int param1, int param2, int *param3,
...);
+};
+
/*
* Mapping from Perfmon logical control registers to Cell hardware
registers.
*/
@@ -154,10 +172,13 @@ static int rtas_reset_signals(u32 cpu)
struct cell_rtas_arg signal;
u64 real_addr = virt_to_phys(&signal);
int rc;
+ struct pfm_cell_platform_pmu_info *info =
+ ((struct pfm_arch_pmu_info *)
+ (pfm_pmu_conf->arch_info))->platform_info;
memset(&signal, 0, sizeof(signal));
signal.cpu = RTAS_CPU(cpu);
- rc = rtas_call(rtas_token("ibm,cbe-perftools"),
+ rc = info->rtas_call(info->rtas_token("ibm,cbe-perftools"),
5, 1, NULL,
subfunc_RESET,
passthru_DISABLE,
@@ -179,8 +200,11 @@ static int rtas_activate_signals(struct
{
u64 real_addr = virt_to_phys(signals);
int rc;
+ struct pfm_cell_platform_pmu_info *info =
+ ((struct pfm_arch_pmu_info *)
+ (pfm_pmu_conf->arch_info))->platform_info;
- rc = rtas_call(rtas_token("ibm,cbe-perftools"),
+ rc = info->rtas_call(info->rtas_token("ibm,cbe-perftools"),
5, 1, NULL,
subfunc_ACTIVATE,
passthru_ENABLE,
@@ -267,10 +291,13 @@ 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);
+ struct pfm_cell_platform_pmu_info *info =
+ ((struct pfm_arch_pmu_info *)
+ (pfm_pmu_conf->arch_info))->platform_info;
+
+ ppe_priv_regs = info->get_cpu_ppe_priv_regs(cpu);
+ pmd_regs = info->get_cpu_pmd_regs(cpu);
+ mic_tm_regs = info->get_cpu_mic_tm_regs(cpu);
if (!ppe_priv_regs || !pmd_regs || !mic_tm_regs) {
PFM_ERR("Error getting Cell PPE, PMD, and MIC "
@@ -402,6 +429,39 @@ static int celleb_activate_signals(struc
}
/**
+ * ps3_reset_signals
+ *
+ * ps3 version of resetting the debug-bus signals.
+ **/
+static int ps3_reset_signals(u32 cpu)
+{
+#ifdef CONFIG_PPC_PS3
+ return ps3_set_signal(0, 0, 0, 0);
+#else
+ return 0;
+#endif
+}
+
+/**
+ * ps3_activate_signals
+ *
+ * ps3 version of activating the debug-bus signals.
+ **/
+static int ps3_activate_signals(struct cell_rtas_arg *signals,
+ int num_signals)
+{
+#ifdef CONFIG_PPC_PS3
+ int i;
+
+ for (i = 0; i < num_signals; i++)
+ ps3_set_signal(signals[i].signal_group, signals[i].bit,
+ signals[i].sub_unit, signals[i].bus_word);
+#endif
+ return 0;
+}
+
+
+/**
* reset_signals
*
* Call to the firmware (if available) to reset the debug-bus signals.
@@ -413,6 +473,8 @@ int reset_signals(u32 cpu)
if (machine_is(celleb))
rc = celleb_reset_signals(cpu);
+ else if (machine_is(ps3))
+ rc = ps3_reset_signals(cpu);
else
rc = rtas_reset_signals(cpu);
@@ -431,6 +493,8 @@ int activate_signals(struct cell_rtas_ar
if (machine_is(celleb))
rc = celleb_activate_signals(signals, num_signals);
+ else if (machine_is(ps3))
+ rc = ps3_activate_signals(signals, num_signals);
else
rc = rtas_activate_signals(signals, num_signals);
@@ -539,9 +603,12 @@ static int pfm_cell_probe_pmu(void)
static void pfm_cell_write_pmc(unsigned int cnum, u64 value)
{
int cpu = smp_processor_id();
+ struct pfm_cell_platform_pmu_info *info =
+ ((struct pfm_arch_pmu_info *)
+ (pfm_pmu_conf->arch_info))->platform_info;
if (cnum < NR_CTRS) {
- cbe_write_pm07_control(cpu, cnum, value);
+ info->write_pm07_control(cpu, cnum, value);
} else if (cnum < NR_CTRS * 2) {
write_pm07_event(cpu, cnum - NR_CTRS, value);
@@ -552,10 +619,11 @@ static void pfm_cell_write_pmc(unsigned
* the interrupts are routed to the correct CPU, as well
* as writing the desired value to the pm_status register.
*/
- cbe_enable_pm_interrupts(cpu, cbe_get_hw_thread_id(cpu),
value);
+ info->enable_pm_interrupts(cpu, info->get_hw_thread_id(cpu),
+ value);
} else if (cnum < PFM_PM_NUM_PMCS) {
- cbe_write_pm(cpu, cnum - (NR_CTRS * 2), value);
+ info->write_pm(cpu, cnum - (NR_CTRS * 2), value);
}
}
@@ -565,9 +633,12 @@ static void pfm_cell_write_pmc(unsigned
static void pfm_cell_write_pmd(unsigned int cnum, u64 value)
{
int cpu = smp_processor_id();
+ struct pfm_cell_platform_pmu_info *info =
+ ((struct pfm_arch_pmu_info *)
+ (pfm_pmu_conf->arch_info))->platform_info;
if (cnum < NR_CTRS) {
- cbe_write_ctr(cpu, cnum, value);
+ info->write_ctr(cpu, cnum, value);
}
}
@@ -577,9 +648,12 @@ static void pfm_cell_write_pmd(unsigned
static u64 pfm_cell_read_pmd(unsigned int cnum)
{
int cpu = smp_processor_id();
+ struct pfm_cell_platform_pmu_info *info =
+ ((struct pfm_arch_pmu_info *)
+ (pfm_pmu_conf->arch_info))->platform_info;
if (cnum < NR_CTRS) {
- return cbe_read_ctr(cpu, cnum);
+ return info->read_ctr(cpu, cnum);
}
return -EINVAL;
@@ -593,7 +667,11 @@ static u64 pfm_cell_read_pmd(unsigned in
static void pfm_cell_enable_counters(struct pfm_context *ctx,
struct pfm_event_set *set)
{
- cbe_enable_pm(smp_processor_id());
+ struct pfm_cell_platform_pmu_info *info =
+ ((struct pfm_arch_pmu_info *)
+ (pfm_pmu_conf->arch_info))->platform_info;
+
+ info->enable_pm(smp_processor_id());
}
/**
@@ -604,7 +682,13 @@ static void pfm_cell_enable_counters(str
static void pfm_cell_disable_counters(struct pfm_context *ctx,
struct pfm_event_set *set)
{
- cbe_disable_pm(smp_processor_id());
+ struct pfm_cell_platform_pmu_info *info =
+ ((struct pfm_arch_pmu_info *)
+ (pfm_pmu_conf->arch_info))->platform_info;
+
+ info->disable_pm(smp_processor_id());
+ if (machine_is(ps3))
+ reset_signals(smp_processor_id());
}
/**
@@ -622,6 +706,9 @@ void pfm_cell_restore_pmcs(struct pfm_ev
u64 value, *used_pmcs = set->used_pmcs;
int i, rc, num_used = 0, cpu = smp_processor_id();
s32 signal_number;
+ struct pfm_cell_platform_pmu_info *info =
+ ((struct pfm_arch_pmu_info *)
+ (pfm_pmu_conf->arch_info))->platform_info;
memset(signals, 0, sizeof(signals));
@@ -630,7 +717,7 @@ void pfm_cell_restore_pmcs(struct pfm_ev
* in use, then it will simply clear the register, which
will
* disable the associated counter.
*/
- cbe_write_pm07_control(cpu, i, set->pmcs[i]);
+ info->write_pm07_control(cpu, i, set->pmcs[i]);
/* Set up the next RTAS array entry for this counter. Only
* include pm07_event registers that are in use by this set
@@ -720,6 +807,9 @@ static void pfm_cell_get_ovfl_pmds(struc
u32 pm_status, ovfl_ctrs;
u64 povfl_pmds = 0;
int i;
+ struct pfm_cell_platform_pmu_info *info =
+ ((struct pfm_arch_pmu_info *)
+ (pfm_pmu_conf->arch_info))->platform_info;
if (!ctx_arch->last_read_updated)
/* This routine was not called via the interrupt handler.
@@ -727,7 +817,7 @@ static void pfm_cell_get_ovfl_pmds(struc
* last_read_pm_status.
*/
ctx_arch->last_read_pm_status =
- cbe_get_and_clear_pm_interrupts(smp_processor_id());
+
info->get_and_clear_pm_interrupts(smp_processor_id());
/* Reset the flag that the interrupt handler last read pm_status. */
ctx_arch->last_read_updated = 0;
@@ -753,6 +843,45 @@ static void pfm_cell_get_ovfl_pmds(struc
}
/**
+ * pfm_cell_acquire_pmu
+ *
+ * acquire PMU resource.
+ * This acquisition is done when the first context is created.
+ **/
+int pfm_cell_acquire_pmu(void)
+{
+#ifdef CONFIG_PPC_PS3
+ int ret;
+
+ if (machine_is(ps3)) {
+ PFM_DBG("");
+ ret = ps3_create_lpm(1, 0, 0, 1);
+ if (ret) {
+ PFM_ERR("Can't create PS3 lpm. error:%d", ret);
+ return -EFAULT;
+ }
+ }
+#endif
+ return 0;
+}
+
+/**
+ * pfm_cell_release_pmu
+ *
+ * release PMU resource.
+ * actual release happens when last context is destroyed
+ **/
+void pfm_cell_release_pmu(void)
+{
+#ifdef CONFIG_PPC_PS3
+ if (machine_is(ps3)) {
+ if (ps3_delete_lpm())
+ PFM_ERR("Can't delete PS3 lpm.");
+ }
+#endif
+}
+
+/**
* handle_trace_buffer_interrupts
*
* This routine is for processing just the interval timer and trace buffer
@@ -780,12 +909,15 @@ static void pfm_cell_irq_handler(struct
struct pfm_arch_context *ctx_arch = pfm_ctx_arch(ctx);
u32 last_read_pm_status;
int cpu = smp_processor_id();
+ struct pfm_cell_platform_pmu_info *info =
+ ((struct pfm_arch_pmu_info *)
+ (pfm_pmu_conf->arch_info))->platform_info;
/* Need to disable and reenable the performance counters to get the
* desired behavior from the hardware. This is specific to the Cell
* PMU hardware.
*/
- cbe_disable_pm(cpu);
+ info->disable_pm(cpu);
/* Read the pm_status register to get the interrupt bits. If a
* perfmormance counter overflow interrupt occurred, call the core
@@ -807,7 +939,7 @@ static void pfm_cell_irq_handler(struct
* - The pmd0 bit is the MSB of the 32 bit register.
*/
ctx_arch->last_read_pm_status = last_read_pm_status =
-
cbe_get_and_clear_pm_interrupts(cpu);
+ info->get_and_clear_pm_interrupts(cpu);
/* Set flag for pfm_cell_get_ovfl_pmds() routine so it knows
* last_read_pm_status was updated by the interrupt handler.
@@ -829,8 +961,8 @@ static void pfm_cell_irq_handler(struct
* register. It is saved in the context when the register is
* written.
*/
- cbe_enable_pm_interrupts(cpu, cbe_get_hw_thread_id(cpu),
- ctx->active_set->pmcs[CELL_PMC_PM_STATUS]);
+ info->enable_pm_interrupts(cpu, info->get_hw_thread_id(cpu),
+
ctx->active_set->pmcs[CELL_PMC_PM_STATUS]);
/* The writes to the various performance counters only writes to a
* latch. The new values (interrupt setting bits, reset counter
value
@@ -839,11 +971,52 @@ static void pfm_cell_irq_handler(struct
* permormance monitor needs to be disabled while writting to the
* latches. This is a HW design issue.
*/
- cbe_enable_pm(cpu);
+ info->enable_pm(cpu);
}
+
+static struct pfm_cell_platform_pmu_info ps3_platform_pmu_info = {
+#ifdef CONFIG_PPC_PS3
+ .read_ctr = ps3_read_ctr,
+ .write_ctr = ps3_write_ctr,
+ .write_pm07_control = ps3_write_pm07_control,
+ .write_pm = ps3_write_pm,
+ .enable_pm = ps3_enable_pm,
+ .disable_pm = ps3_disable_pm,
+ .enable_pm_interrupts = ps3_enable_pm_interrupts,
+ .get_and_clear_pm_interrupts = ps3_get_and_clear_pm_interrupts,
+ .get_hw_thread_id = ps3_get_hw_thread_id,
+ .get_cpu_ppe_priv_regs = NULL,
+ .get_cpu_pmd_regs = NULL,
+ .get_cpu_mic_tm_regs = NULL,
+ .rtas_token = NULL,
+ .rtas_call = NULL,
+#endif
+};
+
+static struct pfm_cell_platform_pmu_info native_platform_pmu_info = {
+#ifdef CONFIG_PPC_CELL_NATIVE
+ .read_ctr = cbe_read_ctr,
+ .write_ctr = cbe_write_ctr,
+ .write_pm07_control = cbe_write_pm07_control,
+ .write_pm = cbe_write_pm,
+ .enable_pm = cbe_enable_pm,
+ .disable_pm = cbe_disable_pm,
+ .enable_pm_interrupts = cbe_enable_pm_interrupts,
+ .get_and_clear_pm_interrupts = cbe_get_and_clear_pm_interrupts,
+ .get_hw_thread_id = cbe_get_hw_thread_id,
+ .get_cpu_ppe_priv_regs = cbe_get_cpu_ppe_priv_regs,
+ .get_cpu_pmd_regs = cbe_get_cpu_pmd_regs,
+ .get_cpu_mic_tm_regs = cbe_get_cpu_mic_tm_regs,
+ .rtas_token = rtas_token,
+ .rtas_call = rtas_call,
+#endif
+};
+
static struct pfm_arch_pmu_info pfm_cell_pmu_info = {
.pmu_style = PFM_POWERPC_PMU_CELL,
+ .acquire_pmu = pfm_cell_acquire_pmu,
+ .release_pmu = pfm_cell_release_pmu,
.write_pmc = pfm_cell_write_pmc,
.write_pmd = pfm_cell_write_pmd,
.read_pmd = pfm_cell_read_pmd,
@@ -884,6 +1057,11 @@ static void pfm_cell_platform_probe(void
for (cnum = NR_CTRS; cnum < (NR_CTRS * 2); cnum++)
pfm_cell_pmc_desc[cnum].type |= PFM_REG_WC;
}
+
+ if (machine_is(ps3))
+ pfm_cell_pmu_info.platform_info = &ps3_platform_pmu_info;
+ else
+ pfm_cell_pmu_info.platform_info = &native_platform_pmu_info;
}
static int __init pfm_cell_pmu_init_module(void)
--- a/include/asm-powerpc/perfmon.h
+++ b/include/asm-powerpc/perfmon.h
@@ -74,6 +74,9 @@ struct pfm_arch_pmu_info {
struct task_struct *task);
int (*unload_context)(struct pfm_context *ctx,
struct task_struct *task);
+ int (*acquire_pmu)(void);
+ void (*release_pmu)(void);
+ void *platform_info;
};
#ifdef CONFIG_PPC32
@@ -340,11 +343,25 @@ static inline ssize_t pfm_arch_compat_re
static inline int pfm_arch_pmu_acquire(void)
{
+ struct pfm_arch_pmu_info *arch_info = pfm_pmu_conf->arch_info;
+ int rc = 0;
+
+ if (arch_info->acquire_pmu) {
+ rc = arch_info->acquire_pmu();
+ if (rc)
+ return rc;
+ }
+
return reserve_pmc_hardware(powerpc_irq_handler);
}
static inline void pfm_arch_pmu_release(void)
{
+ struct pfm_arch_pmu_info *arch_info = pfm_pmu_conf->arch_info;
+
+ if (arch_info->release_pmu)
+ arch_info->release_pmu();
+
release_pmc_hardware();
}
_______________________________________________
perfmon mailing list
[email protected]
http://www.hpl.hp.com/hosted/linux/mail-archives/perfmon/