Subject: Performance Monitor support for Cell From: David Erb This provides basic performance monitor functions for Cell Signed-off-by: Arnd Bergmann Index: linus-2.6/arch/powerpc/platforms/cell/Kconfig =================================================================== --- linus-2.6.orig/arch/powerpc/platforms/cell/Kconfig +++ linus-2.6/arch/powerpc/platforms/cell/Kconfig @@ -25,4 +25,12 @@ config CBE_RAS bool "RAS features for bare metal Cell BE" default y +config BE_PERF + bool "CBE Performance Monitor" + default y + depends on PPC_CELL + help + This provides access to the performance monitor hardware features + of the Cell Broadband Engine. + endmenu Index: linus-2.6/arch/powerpc/platforms/cell/Makefile =================================================================== --- linus-2.6.orig/arch/powerpc/platforms/cell/Makefile +++ linus-2.6/arch/powerpc/platforms/cell/Makefile @@ -13,3 +13,5 @@ spu-priv1-$(CONFIG_PPC_CELL_NATIVE) += s obj-$(CONFIG_SPU_BASE) += spu_callbacks.o spu_base.o \ $(spufs-modular-m) \ $(spu-priv1-y) spufs/ + +obj-$(CONFIG_BE_PERF) += perfmon.o Index: linus-2.6/arch/powerpc/platforms/cell/cbe_regs.h =================================================================== --- linus-2.6.orig/arch/powerpc/platforms/cell/cbe_regs.h +++ linus-2.6/arch/powerpc/platforms/cell/cbe_regs.h @@ -4,6 +4,11 @@ * This file is intended to hold the various register definitions for CBE * on-chip system devices (memory controller, IO controller, etc...) * + * (C) Copyright IBM Corporation 2001,2006 + * + * Authors: Maximino Aguilar (maguilar@us.ibm.com) + * David J. Erb (djerb@us.ibm.com) + * * (c) 2006 Benjamin Herrenschmidt , IBM Corp. */ @@ -22,6 +27,7 @@ #define HID0_CBE_THERM_INT_EN 0x0000000400000000ul #define HID0_CBE_SYSERR_INT_EN 0x0000000200000000ul +#define MAX_CBE 2 /* * @@ -29,45 +35,178 @@ * */ +struct pm { + u32 val; + u32 pad; +}; + +struct pm_status { + union { + u32 val; + struct { + u32 ctr_overflow:8; +#define CBE_CTR_OVERFLOW(ctr) (1 << (7 - ((ctr) & 7))) + + u32 interval_timeout:1; + u32 trace_buffer_full:1; + u32 trace_buffer_underflow:1; + u32 reserved:21; + }; + }; + u32 pad; +}; + +struct pm_control { + union { + u32 val; + struct { + u32 ena_perf_mon:1; + u32 stop_at_max:1; + u32 trace_mode:2; +#define CBE_TRC_MODE_NONE 0 +#define CBE_TRC_MODE_COUNT 1 +#define CBE_TRC_MODE_OCCURRENCE 2 +#define CBE_TRC_MODE_THRESHOLD 3 + + u32 count_qualifiers:2; + u32 thermal_trace_en:1; + u32 sixteen_bit_ctr:4; +#define CBE_16BIT_CTR(ctr) (1 << (3 - ((ctr) & 3))) + + u32 freeze:1; + u32 ppu_count_mode:2; +#define CBE_COUNT_SUPERVISOR_MODE 0 +#define CBE_COUNT_HYPERVISOR_MODE 1 +#define CBE_COUNT_PROBLEM_MODE 2 +#define CBE_COUNT_ALL_MODES 3 + + u32 trace_buffer_overwrite:1; + u32 trace_8_bits:1; + u32 ext_tr_enable:1; + u32 ppu_th0_addr_tr_enable:1; + u32 ppu_th1_addr_tr_enable:1; + u32 ppu_th0_bkmk_tr_enable:1; + u32 ppu_th1_bkmk_tr_enable:1; + u32 spu_addr_tr_enable_0:1; + u32 spu_addr_tr_enable_1:1; + u32 spu_bkmk_tr_enable_0:1; + u32 spu_bkmk_tr_enable_1:1; + u32 reserved:7; + }; + }; + u32 pad; +}; + +struct pm07_control { + union { + u32 val; +#define CBE_ENABLE_CTR 0x00400000 +#define CBE_COUNT_ALL_CYCLES 0x42800000 + + struct { + u32 multiplexor:6; + u32 input_control:1; + u32 polarity:1; + u32 count_cycles:1; + u32 count_enable:1; + u32 reserved:22; + }; + }; + u32 pad; +}; + +struct trace_address { + union { + u64 val; + struct { + u64 write_addr:10; + u64 read_addr:10; + u64 full:1; + u64 empty:1; + u64 data_count:10; +#define CBE_TA_MAX_COUNT 0x400 + + u64 reserved:32; + }; + }; +}; + +struct ext_tr_timer { + union { + u64 val; + struct { + u64 ext_tr_timer:16; + u64 reserved:48; + }; + }; +}; + struct cbe_pmd_regs { - u8 pad_0x0000_0x0800[0x0800 - 0x0000]; /* 0x0000 */ + /* Trace Logic Analyzer */ + u64 pad_0x0000; /* 0x0000 */ + + struct pm group_control; /* 0x0008 */ + + u8 pad_0x0010_0x00a8 [0x00a8 - 0x0010]; /* 0x0010 */ + + struct pm debug_bus_control; /* 0x00a8 */ + + u8 pad_0x00b0_0x0100 [0x0100 - 0x00b0]; /* 0x00b0 */ + + u64 trace_aux_data; /* 0x0100 */ + u64 trace_buffer_0_63; /* 0x0108 */ + u64 trace_buffer_64_127; /* 0x0110 */ + struct trace_address trace_address; /* 0x0118 */ + struct ext_tr_timer ext_tr_timer; /* 0x0120 */ + + u8 pad_0x0128_0x0400 [0x0400 - 0x0128]; /* 0x0128 */ + + /* Performance Monitor */ + struct pm_status pm_status; /* 0x0400 */ + struct pm_control pm_control; /* 0x0408 */ + struct pm pm_interval; /* 0x0410 */ + struct pm pm_ctr[4]; /* 0x0418 */ + struct pm pm_start_stop; /* 0x0438 */ + struct pm07_control pm07_control[8]; /* 0x0440 */ + + u8 pad_0x0480_0x0800 [0x0800 - 0x0480]; /* 0x0480 */ /* Thermal Sensor Registers */ - u64 ts_ctsr1; /* 0x0800 */ - u64 ts_ctsr2; /* 0x0808 */ - u64 ts_mtsr1; /* 0x0810 */ - u64 ts_mtsr2; /* 0x0818 */ - u64 ts_itr1; /* 0x0820 */ - u64 ts_itr2; /* 0x0828 */ - u64 ts_gitr; /* 0x0830 */ - u64 ts_isr; /* 0x0838 */ - u64 ts_imr; /* 0x0840 */ - u64 tm_cr1; /* 0x0848 */ - u64 tm_cr2; /* 0x0850 */ - u64 tm_simr; /* 0x0858 */ - u64 tm_tpr; /* 0x0860 */ - u64 tm_str1; /* 0x0868 */ - u64 tm_str2; /* 0x0870 */ - u64 tm_tsr; /* 0x0878 */ + u64 ts_ctsr1; /* 0x0800 */ + u64 ts_ctsr2; /* 0x0808 */ + u64 ts_mtsr1; /* 0x0810 */ + u64 ts_mtsr2; /* 0x0818 */ + u64 ts_itr1; /* 0x0820 */ + u64 ts_itr2; /* 0x0828 */ + u64 ts_gitr; /* 0x0830 */ + u64 ts_isr; /* 0x0838 */ + u64 ts_imr; /* 0x0840 */ + u64 tm_cr1; /* 0x0848 */ + u64 tm_cr2; /* 0x0850 */ + u64 tm_simr; /* 0x0858 */ + u64 tm_tpr; /* 0x0860 */ + u64 tm_str1; /* 0x0868 */ + u64 tm_str2; /* 0x0870 */ + u64 tm_tsr; /* 0x0878 */ /* Power Management */ - u64 pm_control; /* 0x0880 */ -#define CBE_PMD_PAUSE_ZERO_CONTROL 0x10000 - u64 pm_status; /* 0x0888 */ + u64 pmcr; /* 0x0880 */ +#define CBE_PMD_PAUSE_ZERO_CONTROL 0x10000 + u64 pmsr; /* 0x0888 */ /* Time Base Register */ - u64 tbr; /* 0x0890 */ + u64 tbr; /* 0x0890 */ - u8 pad_0x0898_0x0c00 [0x0c00 - 0x0898]; /* 0x0898 */ + u8 pad_0x0898_0x0c00 [0x0c00 - 0x0898]; /* 0x0898 */ /* Fault Isolation Registers */ - u64 checkstop_fir; /* 0x0c00 */ - u64 recoverable_fir; - u64 spec_att_mchk_fir; - u64 fir_mode_reg; - u64 fir_enable_mask; + u64 checkstop_fir; /* 0x0c00 */ + u64 recoverable_fir; /* 0x0c08 */ + u64 spec_att_mchk_fir; /* 0x0c10 */ + u64 fir_mode_reg; /* 0x0c18 */ + u64 fir_enable_mask; /* 0x0c20 */ - u8 pad_0x0c28_0x1000 [0x1000 - 0x0c28]; /* 0x0c28 */ + u8 pad_0x0c28_0x1000 [0x1000 - 0x0c28]; /* 0x0c28 */ }; extern struct cbe_pmd_regs __iomem *cbe_get_pmd_regs(struct device_node *np); @@ -97,23 +236,40 @@ struct cbe_iic_thread_regs { u64 prio; }; +struct cbe_iic_ir { + union { + u64 val; + struct { + u64 reserved_1:48; + u64 priority:4; + u64 reserved_2:4; + u64 dst_node_id:4; + u64 dst_unit_id:4; +#define CBE_IIC_IR_PT0 0xe +#define CBE_IIC_IR_PT1 0xf + }; + }; +}; + struct cbe_iic_regs { u8 pad_0x0000_0x0400[0x0400 - 0x0000]; /* 0x0000 */ /* IIC interrupt registers */ struct cbe_iic_thread_regs thread[2]; /* 0x0400 */ - u64 iic_ir; /* 0x0440 */ - u64 iic_is; /* 0x0448 */ + + struct cbe_iic_ir iic_ir; /* 0x0440 */ + u64 iic_is; /* 0x0448 */ +#define CBE_IIC_IS_PMI 0x2 u8 pad_0x0450_0x0500[0x0500 - 0x0450]; /* 0x0450 */ /* IOC FIR */ u64 ioc_fir_reset; /* 0x0500 */ - u64 ioc_fir_set; - u64 ioc_checkstop_enable; - u64 ioc_fir_error_mask; - u64 ioc_syserr_enable; - u64 ioc_fir; + u64 ioc_fir_set; /* 0x0508 */ + u64 ioc_checkstop_enable; /* 0x0510 */ + u64 ioc_fir_error_mask; /* 0x0518 */ + u64 ioc_syserr_enable; /* 0x0520 */ + u64 ioc_fir; /* 0x0528 */ u8 pad_0x0530_0x1000[0x1000 - 0x0530]; /* 0x0530 */ }; @@ -122,6 +278,39 @@ extern struct cbe_iic_regs __iomem *cbe_ extern struct cbe_iic_regs __iomem *cbe_get_cpu_iic_regs(int cpu); +struct cbe_mic_tm_regs { + u8 pad_0x0000_0x0040[0x0040 - 0x0000]; /* 0x0000 */ + + u64 mic_ctl_cnfg2; /* 0x0040 */ +#define CBE_MIC_ENABLE_AUX_TRC 0x8000000000000000LL +#define CBE_MIC_DISABLE_PWR_SAV_2 0x0200000000000000LL +#define CBE_MIC_DISABLE_AUX_TRC_WRAP 0x0100000000000000LL +#define CBE_MIC_ENABLE_AUX_TRC_INT 0x0080000000000000LL + + u64 pad_0x0048; /* 0x0048 */ + + u64 mic_aux_trc_base; /* 0x0050 */ + u64 mic_aux_trc_max_addr; /* 0x0058 */ + u64 mic_aux_trc_cur_addr; /* 0x0060 */ + u64 mic_aux_trc_grf_addr; /* 0x0068 */ + u64 mic_aux_trc_grf_data; /* 0x0070 */ + + u64 pad_0x0078; /* 0x0078 */ + + u64 mic_ctl_cnfg_0; /* 0x0080 */ +#define CBE_MIC_DISABLE_PWR_SAV_0 0x8000000000000000LL + + u8 pad_0x0088_0x01c0[0x01c0 - 0x0088]; /* 0x0088 */ + + u64 mic_ctl_cnfg_1; /* 0x01c0 */ +#define CBE_MIC_DISABLE_PWR_SAV_1 0x8000000000000000LL + + u8 pad_0x01c8_0x1000[0x1000 - 0x01c8]; /* 0x01c8 */ +}; + +extern struct cbe_mic_tm_regs __iomem *cbe_get_mic_tm_regs(struct device_node *np); +extern struct cbe_mic_tm_regs __iomem *cbe_get_cpu_mic_tm_regs(int cpu); + /* Init this module early */ extern void cbe_regs_init(void); Index: linus-2.6/arch/powerpc/platforms/cell/perfmon.c =================================================================== --- /dev/null +++ linus-2.6/arch/powerpc/platforms/cell/perfmon.c @@ -0,0 +1,294 @@ +/* + * Cell Broadband Engine Performance Monitor + * + * (C) Copyright IBM Corporation 2001,2006 + * + * Author: David Erb (djerb@us.ibm.com) + * + * 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. + */ + +#include +#include + +#include +#include +#include +#include + +#include "cbe_regs.h" +#include "interrupt.h" +#include "perfmon.h" + + +/* Most PM registers are write-only, so shadow the values written */ + +static struct cbe_shadow_regs { + struct pm group_control; + struct pm debug_bus_control; + struct pm_status pm_status; + struct pm_control pm_control; + struct pm pm_interval; + struct pm pm_start_stop; + struct pm07_control pm07_control[NR_PHYS_CTRS*2]; +} cbe_shadow_regs[MAX_CBE]; + +static struct cbe_shadow_regs *shadow[NR_CPUS] = { + &cbe_shadow_regs[0], &cbe_shadow_regs[0], + &cbe_shadow_regs[1], &cbe_shadow_regs[1] +}; + +#define write_wo_mmio(register,x) \ +{ \ + shadow[cpu]->register.val = x; \ + out_be32 (&pmd_regs->register.val, x); \ +} + +/* Set the size of a physical counter */ +void set_cbe_ctr_size(u32 cpu, u32 phys_ctr, u32 ctr_size) +{ + if (phys_ctr >= NR_PHYS_CTRS) + return; + + switch (ctr_size) { + case 16: + shadow[cpu]->pm_control.sixteen_bit_ctr |= + CBE_16BIT_CTR(phys_ctr); + break; + + case 32: + shadow[cpu]->pm_control.sixteen_bit_ctr &= + ~CBE_16BIT_CTR(phys_ctr); + break; + } +} + + +/* Return the size of a physical counter */ +u32 get_cbe_ctr_size(u32 cpu, u32 phys_ctr) +{ + if (phys_ctr < NR_PHYS_CTRS) + return 0; + + if (shadow[cpu]->pm_control.sixteen_bit_ctr & CBE_16BIT_CTR(phys_ctr)) + return 16; + else + return 32; +} + +/* Read a physical counter (either 1 32-bit or 2 16-bit) */ +u32 cbe_read_phys_ctr(u32 cpu, u32 phys_ctr) +{ + struct cbe_pmd_regs __iomem *pmd_regs; + + if (phys_ctr >= NR_PHYS_CTRS) + return 0; + + pmd_regs = cbe_get_cpu_pmd_regs(cpu); + + return in_be32(&pmd_regs->pm_ctr[phys_ctr].val); +} + +/* Read a counter (either 32-bit or 16-bit) */ +u32 cbe_read_ctr(u32 cpu, u32 ctr) +{ + u32 val; + u32 phys_ctr; + + phys_ctr = ctr & (NR_PHYS_CTRS - 1); + + val = cbe_read_phys_ctr(cpu, phys_ctr); + + if (get_cbe_ctr_size(cpu, phys_ctr) == 16) + val = (ctr < NR_PHYS_CTRS) ? (val >> 16) : (val & 0xffff); + + return val; +} + + +/* Set the value of a physical counter (either 1 32-bit or 2 16-bit) */ +void cbe_write_phys_ctr(u32 cpu, u32 phys_ctr, u32 val) +{ + struct cbe_pmd_regs __iomem *pmd_regs; + + if (phys_ctr >= NR_PHYS_CTRS) + return; + + pmd_regs = cbe_get_cpu_pmd_regs(cpu); + out_be32(&(pmd_regs->pm_ctr[phys_ctr].val), val); +} + + +/* Set the value of a counter (either 32-bit or 16-bit) */ +void cbe_write_ctr(u32 cpu, u32 ctr, u32 val) +{ + u32 phys_ctr; + u32 phys_val; + + phys_ctr = ctr & (NR_PHYS_CTRS - 1); + + if (get_cbe_ctr_size(cpu, phys_ctr) == 16) { + phys_val = cbe_read_phys_ctr(cpu, phys_ctr); + + if (ctr < NR_PHYS_CTRS) + val = (val << 16) | (phys_val & 0xffff); + else + val = (val & 0xffff) | (phys_val & 0xffff0000); + } +} + +u32 cbe_clear_pm_interrupts(u32 cpu) +{ + struct cbe_pmd_regs __iomem *pmd_regs; + struct cbe_iic_regs __iomem *iic_regs; + u32 mask; + + pmd_regs = cbe_get_cpu_pmd_regs(cpu); + iic_regs = cbe_get_cpu_iic_regs(cpu); + + /* 1) Read pm_status to clear the interrupt -- order is important! */ + mask = in_be32 (&pmd_regs->pm_status.val); + + /* 2) Write 1 to PMI bit to clear the interrupt */ + out_be64 (&iic_regs->iic_is, CBE_IIC_IS_PMI); + + return mask; +} + +void cbe_enable_pm_interrupts(u32 cpu, u32 thread, u32 mask) +{ + struct cbe_pmd_regs __iomem *pmd_regs; + struct cbe_iic_regs __iomem *iic_regs; + struct cbe_iic_ir iic_ir; + u32 node = cpu >> 1; + + pmd_regs = cbe_get_cpu_pmd_regs(cpu); + iic_regs = cbe_get_cpu_iic_regs(cpu); + + /* Set which node and thread will handle the next interrupt */ + iic_ir.val = 0; + iic_ir.dst_node_id = node; + iic_ir.dst_unit_id = ((thread == 0) ? CBE_IIC_IR_PT0 + : CBE_IIC_IR_PT1); + + out_be64 (&iic_regs->iic_ir.val, iic_ir.val); + + /* Enable pm interrupts */ + if (mask) + write_wo_mmio (pm_status, mask); +} + +void cbe_disable_pm_interrupts(u32 cpu) +{ + struct cbe_pmd_regs __iomem *pmd_regs; + + pmd_regs = cbe_get_cpu_pmd_regs(cpu); + + out_be32 (&pmd_regs->pm_status.val, 0); + + cbe_clear_pm_interrupts(cpu); +} + +u32 cbe_query_pm_interrupts(u32 cpu) +{ + struct cbe_pmd_regs __iomem *pmd_regs; + u32 mask; + + pmd_regs = cbe_get_cpu_pmd_regs(cpu); + mask = in_be32 (&pmd_regs->pm_status.val); + return mask; +} + +void cbe_enable_pm(u32 cpu) +{ + struct cbe_pmd_regs __iomem *pmd_regs; + + pmd_regs = cbe_get_cpu_pmd_regs(cpu); + shadow[cpu]->pm_control.ena_perf_mon = 1; + out_be32 (&pmd_regs->pm_control.val, shadow[cpu]->pm_control.val); +} + +void cbe_disable_pm(u32 cpu) +{ + struct cbe_pmd_regs __iomem *pmd_regs; + + pmd_regs = cbe_get_cpu_pmd_regs(cpu); + shadow[cpu]->pm_control.ena_perf_mon = 0; + out_be32 (&pmd_regs->pm_control.val, shadow[cpu]->pm_control.val); +} + +void cbe_write_pm (u32 cpu, pm_reg_name reg, u32 val) +{ + struct cbe_pmd_regs __iomem *pmd_regs = cbe_get_cpu_pmd_regs(cpu); + + switch (reg) { + case group_control: + write_wo_mmio(group_control, val); + break; + + case debug_bus_control: + write_wo_mmio(debug_bus_control, val); + break; + + case pm_control: + write_wo_mmio(pm_control, val); + break; + + case pm_interval: + write_wo_mmio(pm_interval, val); + break; + + case pm_start_stop: + write_wo_mmio(pm_start_stop, val); + break; + } +} + +void cbe_write_pm07_control(u32 cpu, u32 ctr, u32 val) +{ + struct cbe_pmd_regs __iomem *pmd_regs = cbe_get_cpu_pmd_regs(cpu); + + if (ctr < (NR_PHYS_CTRS * 2)) + write_wo_mmio(pm07_control[ctr], val); +} + +u32 cbe_read_pm (u32 cpu, pm_reg_name reg) +{ + switch (reg) { + case group_control: + return shadow[cpu]->group_control.val; + + case debug_bus_control: + return shadow[cpu]->debug_bus_control.val; + + case pm_control: + return shadow[cpu]->pm_control.val; + + case pm_interval: + return shadow[cpu]->pm_interval.val; + + case pm_start_stop: + return shadow[cpu]->pm_start_stop.val; + + default: + return 0; + } +} + +u32 cbe_read_pm07_control(u32 cpu, u32 ctr) +{ + return (ctr < (NR_PHYS_CTRS * 2)) ? + shadow[cpu]->pm07_control[ctr].val : 0; +} Index: linus-2.6/arch/powerpc/platforms/cell/pervasive.c =================================================================== --- linus-2.6.orig/arch/powerpc/platforms/cell/pervasive.c +++ linus-2.6/arch/powerpc/platforms/cell/pervasive.c @@ -55,9 +55,9 @@ static void __init cbe_enable_pause_zero pr_debug("Power Management: CPU %d\n", smp_processor_id()); /* Enable Pause(0) control bit */ - temp_register = in_be64(&pregs->pm_control); + temp_register = in_be64(&pregs->pmcr); - out_be64(&pregs->pm_control, + out_be64(&pregs->pmcr, temp_register | CBE_PMD_PAUSE_ZERO_CONTROL); /* Enable DEC and EE interrupt request */ @@ -88,7 +88,7 @@ static void cbe_idle(void) unsigned long ctrl; /* Why do we do that on every idle ? Couldn't that be done once for - * all or do we lose the state some way ? Also, the pm_control + * all or do we lose the state some way ? Also, the pmcr * register setting, that can't be set once at boot ? We really want * to move that away in order to implement a simple powersave */ Index: linus-2.6/arch/powerpc/platforms/cell/cbe_regs.c =================================================================== --- linus-2.6.orig/arch/powerpc/platforms/cell/cbe_regs.c +++ linus-2.6/arch/powerpc/platforms/cell/cbe_regs.c @@ -18,8 +18,6 @@ #include "cbe_regs.h" -#define MAX_CBE 2 - /* * Current implementation uses "cpu" nodes. We build our own mapping * array of cpu numbers to cpu nodes locally for now to allow interrupt @@ -32,6 +30,7 @@ static struct cbe_regs_map struct device_node *cpu_node; struct cbe_pmd_regs __iomem *pmd_regs; struct cbe_iic_regs __iomem *iic_regs; + struct cbe_mic_tm_regs __iomem *mic_tm_regs; } cbe_regs_maps[MAX_CBE]; static int cbe_regs_map_count; @@ -75,6 +74,7 @@ struct cbe_iic_regs __iomem *cbe_get_iic return NULL; return map->iic_regs; } + struct cbe_iic_regs __iomem *cbe_get_cpu_iic_regs(int cpu) { struct cbe_regs_map *map = cbe_thread_map[cpu].regs; @@ -83,6 +83,22 @@ struct cbe_iic_regs __iomem *cbe_get_cpu return map->iic_regs; } +struct cbe_mic_tm_regs __iomem *cbe_get_mic_tm_regs(struct device_node *np) +{ + struct cbe_regs_map *map = cbe_find_map(np); + if (map == NULL) + return NULL; + return map->mic_tm_regs; +} + +struct cbe_mic_tm_regs __iomem *cbe_get_cpu_mic_tm_regs(int cpu) +{ + struct cbe_regs_map *map = cbe_thread_map[cpu].regs; + if (map == NULL) + return NULL; + return map->mic_tm_regs; +} + void __init cbe_regs_init(void) { int i; @@ -123,6 +139,11 @@ void __init cbe_regs_init(void) NULL); if (prop != NULL) map->iic_regs = ioremap(prop->address, prop->len); + + prop = (struct address_prop *)get_property(cpu, "mic-tm", + NULL); + if (prop != NULL) + map->mic_tm_regs = ioremap(prop->address, prop->len); } } Index: linus-2.6/arch/powerpc/platforms/cell/perfmon.h =================================================================== --- /dev/null +++ linus-2.6/arch/powerpc/platforms/cell/perfmon.h @@ -0,0 +1,57 @@ +/* + * Cell Broadband Engine Performance Monitor + * + * (C) Copyright IBM Corporation 2001,2006 + * + * Author: David Erb (djerb@us.ibm.com) + * + * 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 __PERFMON_H__ +#define __PERFMON_H__ + +#define NR_PHYS_CTRS 4 + +typedef enum { + group_control, + debug_bus_control, + pm_control, + pm_interval, + pm_start_stop +} pm_reg_name; + +extern void set_cbe_ctr_size(u32 cpu, u32 phys_ctr, u32 ctr_size); +extern u32 get_cbe_ctr_size(u32 cpu, u32 phys_ctr); + +extern u32 cbe_read_phys_ctr(u32 cpu, u32 phys_ctr); +extern u32 cbe_read_ctr(u32 cpu, u32 ctr); +extern void cbe_write_phys_ctr(u32 cpu, u32 phys_ctr, u32 val); +extern void cbe_write_ctr(u32 cpu, u32 ctr, u32 val); + +extern void cbe_enable_pm_interrupts(u32 cpu, u32 thread, u32 mask); +extern void cbe_disable_pm_interrupts(u32 cpu); +extern u32 cbe_query_pm_interrupts(u32 cpu); +extern u32 cbe_clear_pm_interrupts(u32 cpu); + +extern void cbe_enable_pm(u32 cpu); +extern void cbe_disable_pm(u32 cpu); + +extern void cbe_write_pm (u32 cpu, pm_reg_name reg, u32 val); +extern void cbe_write_pm07_control(u32 cpu, u32 ctr, u32 val); +extern u32 cbe_read_pm (u32 cpu, pm_reg_name reg); +extern u32 cbe_read_pm07_control(u32 cpu, u32 ctr); + +#endif Index: linus-2.6/arch/powerpc/platforms/cell/interrupt.c =================================================================== --- linus-2.6.orig/arch/powerpc/platforms/cell/interrupt.c +++ linus-2.6/arch/powerpc/platforms/cell/interrupt.c @@ -32,6 +32,8 @@ #include #include +#include + #include "interrupt.h" #include "cbe_regs.h" @@ -135,6 +137,15 @@ static int iic_external_get_irq(struct c + node * IIC_NODE_STRIDE + unit; break; + case 0x0e: + /* + * IIC_IS interrupts (includes Performance Monitor) + */ + if (pending.class != 1) + break; + irq = IIC_IS_OFFSET + + node * IIC_NODE_STRIDE; + break; } if (irq == -1) printk(KERN_WARNING "Unexpected interrupt class %02x, " @@ -328,13 +339,66 @@ static void iic_setup_spe_handlers(void) /* Assume two threads per BE are present */ for (be=0; be < num_present_cpus() / 2; be++) { - for (isrc = 0; isrc < IIC_CLASS_STRIDE * 3; isrc++) { + for (isrc = 1; isrc < IIC_CLASS_STRIDE * 3; isrc++) { int irq = IIC_NODE_STRIDE * be + IIC_SPE_OFFSET + isrc; get_irq_desc(irq)->handler = &iic_pic; } } } +static irqreturn_t iic_is_action(int irq, void *dev_id, struct pt_regs *regs) +{ + struct cbe_iic_regs __iomem *iic_regs; + u32 node; + u32 cpu; + u64 val; + + node = (irq == IIC_IS_OFFSET) ? 0 : 1; + cpu = node << 1; + + iic_regs = cbe_get_cpu_iic_regs(cpu); + + val = in_be64 (&iic_regs->iic_is); + + if (val & CBE_IIC_IS_PMI) { + perf_irq(regs); + val &= ~CBE_IIC_IS_PMI; + } + + if (val) { + printk(KERN_WARNING "Unexpected iic_is interrupt:" + "on node %d, val = 0x%lx\n", node, val); + } + + return IRQ_HANDLED; +} + +void iic_setup_iic_is(void) +{ + int be; + static int irq_set = 0; + + if (irq_set) return; + irq_set = 1; + + /* Assume two threads per BE are present */ + for (be=0; be < num_present_cpus() / 2; be++) { + int irq = (IIC_NODE_STRIDE * be) + IIC_IS_OFFSET; + request_irq(irq, iic_is_action, SA_INTERRUPT, "IIC-IS", NULL); + } +} + +static void iic_setup_iic_is_handler(void) +{ + int be; + + /* Assume two threads per BE are present */ + for (be=0; be < num_present_cpus() / 2; be++) { + int irq = (IIC_NODE_STRIDE * be) + IIC_IS_OFFSET; + get_irq_desc(irq)->handler = &iic_pic; + } +} + void iic_init_IRQ(void) { int cpu, irq_offset; @@ -350,4 +414,5 @@ void iic_init_IRQ(void) out_be64(&iic->regs->prio, 0xff); } iic_setup_spe_handlers(); + iic_setup_iic_is_handler(); } Index: linus-2.6/arch/powerpc/platforms/cell/interrupt.h =================================================================== --- linus-2.6.orig/arch/powerpc/platforms/cell/interrupt.h +++ linus-2.6/arch/powerpc/platforms/cell/interrupt.h @@ -9,6 +9,7 @@ * * 00-3f 80 02 +0 00 - 80 02 +0 3f South Bridge * 00-3f 80 02 +b 00 - 80 02 +b 3f South Bridge + * 40 80 01 +e 00 - 80 01 +e 00 IIC_IS * 41-4a 80 00 +1 ** - 80 00 +a ** SPU Class 0 * 51-5a 80 01 +1 ** - 80 01 +a ** SPU Class 1 * 61-6a 80 02 +1 ** - 80 02 +a ** SPU Class 2 @@ -39,6 +40,7 @@ enum { IIC_EXT_OFFSET = 0x00, /* Start of south bridge IRQs */ IIC_NUM_EXT = 0x40, /* Number of south bridge IRQs */ + IIC_IS_OFFSET = 0x40, /* IRQ reserved for IIC_IS */ IIC_SPE_OFFSET = 0x40, /* Start of SPE interrupts */ IIC_CLASS_STRIDE = 0x10, /* SPE IRQs per class */ IIC_IPI_OFFSET = 0x70, /* Start of IPI IRQs */ @@ -59,5 +61,7 @@ extern u8 iic_get_target_id(int cpu); extern void spider_init_IRQ(void); extern int spider_get_irq(int node); +extern void iic_setup_iic_is(void); + #endif #endif /* ASM_CELL_PIC_H */ Index: linus-2.6/arch/powerpc/kernel/head_64.S =================================================================== --- linus-2.6.orig/arch/powerpc/kernel/head_64.S +++ linus-2.6/arch/powerpc/kernel/head_64.S @@ -549,6 +549,7 @@ system_call_pSeries: STD_EXCEPTION_PSERIES(0xd00, single_step) STD_EXCEPTION_PSERIES(0xe00, trap_0e) +#ifndef CONFIG_PPC_CELL /* We need to deal with the Altivec unavailable exception * here which is at 0xf20, thus in the middle of the * prolog code of the PerformanceMonitor one. A little @@ -556,6 +557,7 @@ system_call_pSeries: */ . = 0xf00 b performance_monitor_pSeries +#endif STD_EXCEPTION_PSERIES(0xf20, altivec_unavailable) @@ -573,10 +575,12 @@ system_call_pSeries: . = 0x3000 +#ifndef CONFIG_PPC_CELL /*** pSeries interrupt support ***/ /* moved from 0xf00 */ STD_EXCEPTION_PSERIES(., performance_monitor) +#endif .align 7 _GLOBAL(do_stab_bolted_pSeries)