/* $Id: ppc.c,v 1.43 2007/10/06 13:02:07 mikpe Exp $ * PPC32 performance-monitoring counters driver. * * Copyright (C) 2004-2007 Mikael Pettersson */ #include #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) #include #endif #include #include #include #include #include #include /* tb_ticks_per_jiffy, get_tbl() */ #include "ppc_tests.h" /* Support for lazy evntsel and perfctr SPR updates. */ struct per_cpu_cache { /* roughly a subset of perfctr_cpu_state */ unsigned int id; /* cache owner id */ /* Physically indexed cache of the MMCRs. */ unsigned int ppc_mmcr[3]; }; static DEFINE_PER_CPU(struct per_cpu_cache, per_cpu_cache); #define __get_cpu_cache(cpu) (&per_cpu(per_cpu_cache, cpu)) #define get_cpu_cache() (&__get_cpu_var(per_cpu_cache)) /* Structure for counter snapshots, as 32-bit values. */ struct perfctr_low_ctrs { unsigned int tsc; unsigned int pmc[6]; }; enum pm_type { PM_NONE, PM_604, PM_604e, PM_750, /* XXX: Minor event set diffs between IBM and Moto. */ PM_7400, PM_7450, }; static enum pm_type pm_type; static unsigned int new_id(void) { static DEFINE_SPINLOCK(lock); static unsigned int counter; int id; spin_lock(&lock); id = ++counter; spin_unlock(&lock); return id; } #ifdef CONFIG_PERFCTR_INTERRUPT_SUPPORT static void perfctr_default_ihandler(unsigned long pc) { } static perfctr_ihandler_t perfctr_ihandler = perfctr_default_ihandler; void do_perfctr_interrupt(struct pt_regs *regs) { preempt_disable(); (*perfctr_ihandler)(instruction_pointer(regs)); preempt_enable_no_resched(); } static inline int perfctr_reserve_pmc_hardware(void) { return reserve_pmc_hardware(do_perfctr_interrupt); } void perfctr_cpu_set_ihandler(perfctr_ihandler_t ihandler) { perfctr_ihandler = ihandler ? ihandler : perfctr_default_ihandler; } #else static inline int perfctr_reserve_pmc_hardware(void) { return reserve_pmc_hardware(NULL); } #define perfctr_cstatus_has_ictrs(cstatus) 0 #endif static inline void perfctr_release_pmc_hardware(void) { release_pmc_hardware(); } #if defined(CONFIG_SMP) && defined(CONFIG_PERFCTR_INTERRUPT_SUPPORT) static inline void set_isuspend_cpu(struct perfctr_cpu_state *state, int cpu) { state->isuspend_cpu = cpu; } static inline int is_isuspend_cpu(const struct perfctr_cpu_state *state, int cpu) { return state->isuspend_cpu == cpu; } static inline void clear_isuspend_cpu(struct perfctr_cpu_state *state) { state->isuspend_cpu = NR_CPUS; } #else static inline void set_isuspend_cpu(struct perfctr_cpu_state *state, int cpu) { } static inline int is_isuspend_cpu(const struct perfctr_cpu_state *state, int cpu) { return 1; } static inline void clear_isuspend_cpu(struct perfctr_cpu_state *state) { } #endif /* The ppc driver internally uses cstatus & (1<<30) to record that a context has an asynchronously changing MMCR0. */ static inline unsigned int perfctr_cstatus_set_mmcr0_quirk(unsigned int cstatus) { return cstatus | (1 << 30); } static inline int perfctr_cstatus_has_mmcr0_quirk(unsigned int cstatus) { return cstatus & (1 << 30); } /**************************************************************** * * * Driver procedures. * * * ****************************************************************/ /* * The PowerPC 604/750/74xx family. * * Common features * --------------- * - Per counter event selection data in subfields of control registers. * MMCR0 contains both global control and PMC1/PMC2 event selectors. * - Overflow interrupt support is present in all processors, but an * erratum makes it difficult to use in 750/7400/7410 processors. * - There is no concept of per-counter qualifiers: * - User-mode/supervisor-mode restrictions are global. * - Two groups of counters, PMC1 and PMC2-PMC. Each group * has a single overflow interrupt/event enable/disable flag. * - The instructions used to read (mfspr) and write (mtspr) the control * and counter registers (SPRs) only support hardcoded register numbers. * There is no support for accessing an SPR via a runtime value. * - Each counter supports its own unique set of events. However, events * 0-1 are common for PMC1-PMC4, and events 2-4 are common for PMC1-PMC4. * - There is no separate high-resolution core clock counter. * The time-base counter is available, but it typically runs an order of * magnitude slower than the core clock. * Any performance counter can be programmed to count core clocks, but * doing this (a) reserves one PMC, and (b) needs indirect accesses * since the SPR number in general isn't known at compile-time. * * 604 * --- * 604 has MMCR0, PMC1, PMC2, SIA, and SDA. * * MMCR0[THRESHOLD] is not automatically multiplied. * * On the 604, software must always reset MMCR0[ENINT] after * taking a PMI. This is not the case for the 604e. * * 604e * ---- * 604e adds MMCR1, PMC3, and PMC4. * Bus-to-core multiplier is available via HID1[PLL_CFG]. * * MMCR0[THRESHOLD] is automatically multiplied by 4. * * When the 604e vectors to the PMI handler, it automatically * clears any pending PMIs. Unlike the 604, the 604e does not * require MMCR0[ENINT] to be cleared (and possibly reset) * before external interrupts can be re-enabled. * * 750 * --- * 750 adds user-readable MMCRn/PMCn/SIA registers, and removes SDA. * * MMCR0[THRESHOLD] is not automatically multiplied. * * Motorola MPC750UM.pdf, page C-78, states: "The performance monitor * of the MPC755 functions the same as that of the MPC750, (...), except * that for both the MPC750 and MPC755, no combination of the thermal * assist unit, the decrementer register, and the performance monitor * can be used at any one time. If exceptions for any two of these * functional blocks are enabled together, multiple exceptions caused * by any of these three blocks cause unpredictable results." * * IBM 750CXe_Err_DD2X.pdf, Erratum #13, states that a PMI which * occurs immediately after a delayed decrementer exception can * corrupt SRR0, causing the processor to hang. It also states that * PMIs via TB bit transitions can be used to simulate the decrementer. * * 750FX adds dual-PLL support and programmable core frequency switching. * * 750FX DD2.3 fixed the DEC/PMI SRR0 corruption erratum. * * 74xx * ---- * 7400 adds MMCR2 and BAMR. * * MMCR0[THRESHOLD] is multiplied by 2 or 32, as specified * by MMCR2[THRESHMULT]. * * 74xx changes the semantics of several MMCR0 control bits, * compared to 604/750. * * PPC7410 Erratum No. 10: Like the MPC750 TAU/DECR/PMI erratum. * Erratum No. 14 marks TAU as unsupported in 7410, but this leaves * perfmon and decrementer interrupts as being mutually exclusive. * Affects PPC7410 1.0-1.2 (PVR 0x800C1100-0x800C1102). 1.3 and up * (PVR 0x800C1103 up) are Ok. * * 7450 adds PMC5 and PMC6. * * 7455/7445 V3.3 (PVR 80010303) and later use the 7457 PLL table, * earlier revisions use the 7450 PLL table */ static inline unsigned int read_pmc(unsigned int pmc) { switch (pmc) { default: /* impossible, but silences gcc warning */ case 0: return mfspr(SPRN_PMC1); case 1: return mfspr(SPRN_PMC2); case 2: return mfspr(SPRN_PMC3); case 3: return mfspr(SPRN_PMC4); case 4: return mfspr(SPRN_PMC5); case 5: return mfspr(SPRN_PMC6); } } static void ppc_read_counters(struct perfctr_cpu_state *state, struct perfctr_low_ctrs *ctrs) { unsigned int cstatus, nrctrs, i; cstatus = state->user.cstatus; if (perfctr_cstatus_has_tsc(cstatus)) ctrs->tsc = get_tbl(); nrctrs = perfctr_cstatus_nractrs(cstatus); for(i = 0; i < nrctrs; ++i) { unsigned int pmc = state->control.pmc_map[i]; ctrs->pmc[i] = read_pmc(pmc); } } static unsigned int pmc_max_event(unsigned int pmc) { switch (pmc) { default: /* impossible, but silences gcc warning */ case 0: return 127; case 1: return 63; case 2: return 31; case 3: return 31; case 4: return 31; case 5: return 63; } } static unsigned int get_nr_pmcs(void) { switch (pm_type) { case PM_7450: return 6; case PM_7400: case PM_750: case PM_604e: return 4; case PM_604: return 2; default: /* PM_NONE, but silences gcc warning */ return 0; } } static int ppc_check_control(struct perfctr_cpu_state *state) { unsigned int i, nractrs, nrctrs, pmc_mask, pmi_mask, pmc; unsigned int nr_pmcs, evntsel[6]; nr_pmcs = get_nr_pmcs(); nractrs = state->control.header.nractrs; nrctrs = nractrs + state->control.header.nrictrs; if (nrctrs < nractrs || nrctrs > nr_pmcs) return -EINVAL; pmc_mask = 0; pmi_mask = 0; evntsel[1-1] = (state->control.mmcr0 >> (31-25)) & 0x7F; evntsel[2-1] = (state->control.mmcr0 >> (31-31)) & 0x3F; evntsel[3-1] = (state->control.mmcr1 >> (31- 4)) & 0x1F; evntsel[4-1] = (state->control.mmcr1 >> (31- 9)) & 0x1F; evntsel[5-1] = (state->control.mmcr1 >> (31-14)) & 0x1F; evntsel[6-1] = (state->control.mmcr1 >> (31-20)) & 0x3F; for(i = 0; i < nrctrs; ++i) { pmc = state->control.pmc_map[i]; if (pmc >= nr_pmcs || (pmc_mask & (1<= nractrs) pmi_mask |= (1< pmc_max_event(pmc)) return -EINVAL; } /* unused event selectors must be zero */ for(i = 0; i < ARRAY_SIZE(evntsel); ++i) if (!(pmc_mask & (1<control.mmcr2 & MMCR2_RESERVED) return -EINVAL; break; default: if (state->control.mmcr2) return -EINVAL; } /* check MMCR1; non-existent event selectors are taken care of by the "unused event selectors must be zero" check above */ if (state->control.mmcr1 & MMCR1__RESERVED) return -EINVAL; /* We do not yet handle TBEE as the only exception cause, so PMXE requires at least one interrupt-mode counter. */ if ((state->control.mmcr0 & MMCR0_PMXE) && !state->control.header.nrictrs) return -EINVAL; state->id = new_id(); /* * MMCR0[FC] and MMCR0[TRIGGER] may change on 74xx if FCECE or * TRIGGER is set. At suspends we must read MMCR0 back into * the state and the cache and then freeze the counters, and * at resumes we must unfreeze the counters and reload MMCR0. */ switch (pm_type) { case PM_7450: case PM_7400: if (state->control.mmcr0 & (MMCR0_FCECE | MMCR0_TRIGGER)) state->user.cstatus = perfctr_cstatus_set_mmcr0_quirk(state->user.cstatus); default: ; } /* The MMCR0 handling for FCECE and TRIGGER is also needed for PMXE. */ if (state->control.mmcr0 & (MMCR0_PMXE | MMCR0_FCECE | MMCR0_TRIGGER)) state->user.cstatus = perfctr_cstatus_set_mmcr0_quirk(state->user.cstatus); return 0; } #ifdef CONFIG_PERFCTR_INTERRUPT_SUPPORT /* PRE: perfctr_cstatus_has_ictrs(state->cstatus) != 0 */ /* PRE: counters frozen */ static void ppc_isuspend(struct perfctr_cpu_state *state) { struct per_cpu_cache *cache; unsigned int cstatus, nrctrs, i; int cpu; cpu = smp_processor_id(); set_isuspend_cpu(state, cpu); /* early to limit cpu's live range */ cache = __get_cpu_cache(cpu); cstatus = state->user.cstatus; nrctrs = perfctr_cstatus_nrctrs(cstatus); for(i = perfctr_cstatus_nractrs(cstatus); i < nrctrs; ++i) { unsigned int pmc = state->control.pmc_map[i]; unsigned int now = read_pmc(pmc); state->user.pmc[i].sum += now - state->user.pmc[i].start; state->user.pmc[i].start = now; } /* cache->id is still == state->id */ } static void ppc_iresume(const struct perfctr_cpu_state *state) { struct per_cpu_cache *cache; unsigned int cstatus, nrctrs, i; int cpu; unsigned int pmc[6]; cpu = smp_processor_id(); cache = __get_cpu_cache(cpu); if (cache->id == state->id) { /* Clearing cache->id to force write_control() to unfreeze MMCR0 would be done here, but it is subsumed by resume()'s MMCR0 reload logic. */ if (is_isuspend_cpu(state, cpu)) return; /* skip reload of PMCs */ } /* * The CPU state wasn't ours. * * The counters must be frozen before being reinitialised, * to prevent unexpected increments and missed overflows. * * All unused counters must be reset to a non-overflow state. */ if (!(cache->ppc_mmcr[0] & MMCR0_FC)) { cache->ppc_mmcr[0] |= MMCR0_FC; mtspr(SPRN_MMCR0, cache->ppc_mmcr[0]); } memset(&pmc[0], 0, sizeof pmc); cstatus = state->user.cstatus; nrctrs = perfctr_cstatus_nrctrs(cstatus); for(i = perfctr_cstatus_nractrs(cstatus); i < nrctrs; ++i) pmc[state->control.pmc_map[i]] = state->user.pmc[i].start; switch (pm_type) { case PM_7450: mtspr(SPRN_PMC6, pmc[6-1]); mtspr(SPRN_PMC5, pmc[5-1]); case PM_7400: case PM_750: case PM_604e: mtspr(SPRN_PMC4, pmc[4-1]); mtspr(SPRN_PMC3, pmc[3-1]); case PM_604: mtspr(SPRN_PMC2, pmc[2-1]); mtspr(SPRN_PMC1, pmc[1-1]); case PM_NONE: ; } /* cache->id remains != state->id */ } #endif static void ppc_write_control(const struct perfctr_cpu_state *state) { struct per_cpu_cache *cache; unsigned int value; cache = get_cpu_cache(); if (cache->id == state->id) return; /* * Order matters here: update threshmult and event * selectors before updating global control, which * potentially enables PMIs. * * Since mtspr doesn't accept a runtime value for the * SPR number, unroll the loop so each mtspr targets * a constant SPR. * * For processors without MMCR2, we ensure that the * cache and the state indicate the same value for it, * preventing any actual mtspr to it. Ditto for MMCR1. */ value = state->control.mmcr2; if (value != cache->ppc_mmcr[2]) { cache->ppc_mmcr[2] = value; mtspr(SPRN_MMCR2, value); } value = state->control.mmcr1; if (value != cache->ppc_mmcr[1]) { cache->ppc_mmcr[1] = value; mtspr(SPRN_MMCR1, value); } value = state->control.mmcr0; if (value != cache->ppc_mmcr[0]) { cache->ppc_mmcr[0] = value; mtspr(SPRN_MMCR0, value); } cache->id = state->id; } static void ppc_clear_counters(void) { switch (pm_type) { case PM_7450: case PM_7400: mtspr(SPRN_MMCR2, 0); mtspr(SPRN_BAMR, 0); case PM_750: case PM_604e: mtspr(SPRN_MMCR1, 0); case PM_604: mtspr(SPRN_MMCR0, 0); case PM_NONE: ; } switch (pm_type) { case PM_7450: mtspr(SPRN_PMC6, 0); mtspr(SPRN_PMC5, 0); case PM_7400: case PM_750: case PM_604e: mtspr(SPRN_PMC4, 0); mtspr(SPRN_PMC3, 0); case PM_604: mtspr(SPRN_PMC2, 0); mtspr(SPRN_PMC1, 0); case PM_NONE: ; } } /* * Driver methods, internal and exported. */ static void perfctr_cpu_write_control(const struct perfctr_cpu_state *state) { return ppc_write_control(state); } static void perfctr_cpu_read_counters(struct perfctr_cpu_state *state, struct perfctr_low_ctrs *ctrs) { return ppc_read_counters(state, ctrs); } #ifdef CONFIG_PERFCTR_INTERRUPT_SUPPORT static void perfctr_cpu_isuspend(struct perfctr_cpu_state *state) { return ppc_isuspend(state); } static void perfctr_cpu_iresume(const struct perfctr_cpu_state *state) { return ppc_iresume(state); } /* Call perfctr_cpu_ireload() just before perfctr_cpu_resume() to bypass internal caching and force a reload if the I-mode PMCs. */ void perfctr_cpu_ireload(struct perfctr_cpu_state *state) { state->control.mmcr0 |= MMCR0_PMXE; #ifdef CONFIG_SMP clear_isuspend_cpu(state); #else get_cpu_cache()->id = 0; #endif } /* PRE: the counters have been suspended and sampled by perfctr_cpu_suspend() */ unsigned int perfctr_cpu_identify_overflow(struct perfctr_cpu_state *state) { unsigned int cstatus, nrctrs, i, pmc_mask; cstatus = state->user.cstatus; nrctrs = perfctr_cstatus_nrctrs(cstatus); pmc_mask = 0; for(i = perfctr_cstatus_nractrs(cstatus); i < nrctrs; ++i) { if ((int)state->user.pmc[i].start < 0) { /* PPC-specific */ unsigned int pmc = state->control.pmc_map[i]; /* XXX: "+=" to correct for overshots */ state->user.pmc[i].start = state->control.ireset[pmc]; pmc_mask |= (1 << i); } } if (!pmc_mask && (state->control.mmcr0 & MMCR0_TBEE)) pmc_mask = (1<<8); /* fake TB bit flip indicator */ return pmc_mask; } static inline int check_ireset(struct perfctr_cpu_state *state) { unsigned int nrctrs, i; i = state->control.header.nractrs; nrctrs = i + state->control.header.nrictrs; for(; i < nrctrs; ++i) { unsigned int pmc = state->control.pmc_map[i]; if ((int)state->control.ireset[pmc] < 0) /* PPC-specific */ return -EINVAL; state->user.pmc[i].start = state->control.ireset[pmc]; } return 0; } #else /* CONFIG_PERFCTR_INTERRUPT_SUPPORT */ static inline void perfctr_cpu_isuspend(struct perfctr_cpu_state *state) { } static inline void perfctr_cpu_iresume(const struct perfctr_cpu_state *state) { } static inline int check_ireset(struct perfctr_cpu_state *state) { return 0; } #endif /* CONFIG_PERFCTR_INTERRUPT_SUPPORT */ static int check_control(struct perfctr_cpu_state *state) { return ppc_check_control(state); } int perfctr_cpu_update_control(struct perfctr_cpu_state *state, int is_global) { int err; clear_isuspend_cpu(state); state->user.cstatus = 0; /* disallow i-mode counters if we cannot catch the interrupts */ if (!(perfctr_info.cpu_features & PERFCTR_FEATURE_PCINT) && state->control.header.nrictrs) return -EPERM; err = check_control(state); /* may initialise state->cstatus */ if (err < 0) return err; err = check_ireset(state); if (err < 0) { state->user.cstatus = 0; return err; } state->user.cstatus |= perfctr_mk_cstatus(state->control.header.tsc_on, state->control.header.nractrs, state->control.header.nrictrs); return 0; } /* * get_reg_offset() maps SPR numbers to offsets into struct perfctr_cpu_control, * suitable for accessing control data of type unsigned int. */ static const struct { unsigned int spr; unsigned int offset; } reg_offsets[] = { { SPRN_MMCR0, offsetof(struct perfctr_cpu_control, mmcr0) }, { SPRN_MMCR1, offsetof(struct perfctr_cpu_control, mmcr1) }, { SPRN_MMCR2, offsetof(struct perfctr_cpu_control, mmcr2) }, { SPRN_PMC1, offsetof(struct perfctr_cpu_control, ireset[1-1]) }, { SPRN_PMC2, offsetof(struct perfctr_cpu_control, ireset[2-1]) }, { SPRN_PMC3, offsetof(struct perfctr_cpu_control, ireset[3-1]) }, { SPRN_PMC4, offsetof(struct perfctr_cpu_control, ireset[4-1]) }, { SPRN_PMC5, offsetof(struct perfctr_cpu_control, ireset[5-1]) }, { SPRN_PMC6, offsetof(struct perfctr_cpu_control, ireset[6-1]) }, }; static int get_reg_offset(unsigned int spr) { unsigned int i; for(i = 0; i < ARRAY_SIZE(reg_offsets); ++i) if (spr == reg_offsets[i].spr) return reg_offsets[i].offset; return -1; } static int access_regs(struct perfctr_cpu_control *control, void *argp, unsigned int argbytes, int do_write) { struct perfctr_cpu_reg *regs; unsigned int i, nr_regs, *where; int offset; nr_regs = argbytes / sizeof(struct perfctr_cpu_reg); if (nr_regs * sizeof(struct perfctr_cpu_reg) != argbytes) return -EINVAL; regs = (struct perfctr_cpu_reg*)argp; for(i = 0; i < nr_regs; ++i) { offset = get_reg_offset(regs[i].nr); if (offset < 0) return -EINVAL; where = (unsigned int*)((char*)control + offset); if (do_write) *where = regs[i].value; else regs[i].value = *where; } return argbytes; } int perfctr_cpu_control_write(struct perfctr_cpu_control *control, unsigned int domain, const void *srcp, unsigned int srcbytes) { if (domain != PERFCTR_DOMAIN_CPU_REGS) return -EINVAL; return access_regs(control, (void*)srcp, srcbytes, 1); } int perfctr_cpu_control_read(const struct perfctr_cpu_control *control, unsigned int domain, void *dstp, unsigned int dstbytes) { if (domain != PERFCTR_DOMAIN_CPU_REGS) return -EINVAL; return access_regs((struct perfctr_cpu_control*)control, dstp, dstbytes, 0); } void perfctr_cpu_suspend(struct perfctr_cpu_state *state) { unsigned int i, cstatus, nractrs; struct perfctr_low_ctrs now; write_perfseq_begin(&state->user.sequence); if (perfctr_cstatus_has_mmcr0_quirk(state->user.cstatus)) { unsigned int mmcr0 = mfspr(SPRN_MMCR0); mtspr(SPRN_MMCR0, mmcr0 | MMCR0_FC); get_cpu_cache()->ppc_mmcr[0] = mmcr0 | MMCR0_FC; state->control.mmcr0 = mmcr0; } if (perfctr_cstatus_has_ictrs(state->user.cstatus)) perfctr_cpu_isuspend(state); perfctr_cpu_read_counters(state, &now); cstatus = state->user.cstatus; if (perfctr_cstatus_has_tsc(cstatus)) state->user.tsc_sum += now.tsc - state->user.tsc_start; nractrs = perfctr_cstatus_nractrs(cstatus); for(i = 0; i < nractrs; ++i) state->user.pmc[i].sum += now.pmc[i] - state->user.pmc[i].start; write_perfseq_end(&state->user.sequence); } void perfctr_cpu_resume(struct perfctr_cpu_state *state) { write_perfseq_begin(&state->user.sequence); if (perfctr_cstatus_has_ictrs(state->user.cstatus)) perfctr_cpu_iresume(state); if (perfctr_cstatus_has_mmcr0_quirk(state->user.cstatus)) get_cpu_cache()->id = 0; /* force reload of MMCR0 */ perfctr_cpu_write_control(state); //perfctr_cpu_read_counters(state, &state->start); { struct perfctr_low_ctrs now; unsigned int i, cstatus, nrctrs; perfctr_cpu_read_counters(state, &now); cstatus = state->user.cstatus; if (perfctr_cstatus_has_tsc(cstatus)) state->user.tsc_start = now.tsc; nrctrs = perfctr_cstatus_nractrs(cstatus); for(i = 0; i < nrctrs; ++i) state->user.pmc[i].start = now.pmc[i]; } write_perfseq_end(&state->user.sequence); } void perfctr_cpu_sample(struct perfctr_cpu_state *state) { unsigned int i, cstatus, nractrs; struct perfctr_low_ctrs now; write_perfseq_begin(&state->user.sequence); perfctr_cpu_read_counters(state, &now); cstatus = state->user.cstatus; if (perfctr_cstatus_has_tsc(cstatus)) { state->user.tsc_sum += now.tsc - state->user.tsc_start; state->user.tsc_start = now.tsc; } nractrs = perfctr_cstatus_nractrs(cstatus); for(i = 0; i < nractrs; ++i) { state->user.pmc[i].sum += now.pmc[i] - state->user.pmc[i].start; state->user.pmc[i].start = now.pmc[i]; } write_perfseq_end(&state->user.sequence); } static void perfctr_cpu_clear_counters(void) { struct per_cpu_cache *cache; cache = get_cpu_cache(); memset(cache, 0, sizeof *cache); cache->id = -1; ppc_clear_counters(); } /**************************************************************** * * * Processor detection and initialisation procedures. * * * ****************************************************************/ /* Derive CPU core frequency from TB frequency and PLL_CFG. */ enum pll_type { PLL_NONE, /* for e.g. 604 which has no HID1[PLL_CFG] */ PLL_604e, PLL_750, PLL_750FX, PLL_7400, PLL_7450, PLL_7457, }; /* These are the known bus-to-core ratios, indexed by PLL_CFG. Multiplied by 2 since half-multiplier steps are present. */ static unsigned char cfg_ratio_604e[16] __initdata = { // *2 2, 2, 14, 2, 4, 13, 5, 9, 6, 11, 8, 10, 3, 12, 7, 0 }; static unsigned char cfg_ratio_750[16] __initdata = { // *2 5, 15, 14, 2, 4, 13, 20, 9, // 0b0110 is 18 if L1_TSTCLK=0, but that is abnormal 6, 11, 8, 10, 16, 12, 7, 0 }; static unsigned char cfg_ratio_750FX[32] __initdata = { // *2 0, 0, 2, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 0 }; static unsigned char cfg_ratio_7400[16] __initdata = { // *2 18, 15, 14, 2, 4, 13, 5, 9, 6, 11, 8, 10, 16, 12, 7, 0 }; static unsigned char cfg_ratio_7450[32] __initdata = { // *2 1, 0, 15, 30, 14, 0, 2, 0, 4, 0, 13, 26, 5, 0, 9, 18, 6, 0, 11, 22, 8, 20, 10, 24, 16, 28, 12, 32, 7, 0, 0, 0 }; static unsigned char cfg_ratio_7457[32] __initdata = { // *2 23, 34, 15, 30, 14, 36, 2, 40, 4, 42, 13, 26, 17, 48, 19, 18, 6, 21, 11, 22, 8, 20, 10, 24, 16, 28, 12, 32, 27, 56, 0, 25 }; static unsigned int __init tb_to_core_ratio(enum pll_type pll_type) { unsigned char *cfg_ratio; unsigned int shift = 28, mask = 0xF, hid1, pll_cfg, ratio; switch (pll_type) { case PLL_604e: cfg_ratio = cfg_ratio_604e; break; case PLL_750: cfg_ratio = cfg_ratio_750; break; case PLL_750FX: cfg_ratio = cfg_ratio_750FX; hid1 = mfspr(SPRN_HID1); switch ((hid1 >> 16) & 0x3) { /* HID1[PI0,PS] */ case 0: /* PLL0 with external config */ shift = 31-4; /* access HID1[PCE] */ break; case 2: /* PLL0 with internal config */ shift = 31-20; /* access HID1[PC0] */ break; case 1: case 3: /* PLL1 */ shift = 31-28; /* access HID1[PC1] */ break; } mask = 0x1F; break; case PLL_7400: cfg_ratio = cfg_ratio_7400; break; case PLL_7450: cfg_ratio = cfg_ratio_7450; shift = 12; mask = 0x1F; break; case PLL_7457: cfg_ratio = cfg_ratio_7457; shift = 12; mask = 0x1F; break; default: return 0; } hid1 = mfspr(SPRN_HID1); pll_cfg = (hid1 >> shift) & mask; ratio = cfg_ratio[pll_cfg]; if (!ratio) printk(KERN_WARNING "perfctr: unknown PLL_CFG 0x%x\n", pll_cfg); return (4/2) * ratio; } static unsigned int __init pll_to_core_khz(enum pll_type pll_type) { unsigned int tb_to_core = tb_to_core_ratio(pll_type); perfctr_info.tsc_to_cpu_mult = tb_to_core; return tb_ticks_per_jiffy * tb_to_core * (HZ/10) / (1000/10); } /* Extract core and timebase frequencies from Open Firmware. */ #ifdef CONFIG_PPC_OF static unsigned int __init of_to_core_khz(void) { struct device_node *cpu; unsigned int *fp, core, tb; cpu = find_type_devices("cpu"); if (!cpu) return 0; fp = (unsigned int*)get_property(cpu, "clock-frequency", NULL); if (!fp || !(core = *fp)) return 0; fp = (unsigned int*)get_property(cpu, "timebase-frequency", NULL); if (!fp || !(tb = *fp)) return 0; perfctr_info.tsc_to_cpu_mult = core / tb; return core / 1000; } #else static inline unsigned int of_to_core_khz(void) { return 0; } #endif static unsigned int __init detect_cpu_khz(enum pll_type pll_type) { unsigned int khz; khz = pll_to_core_khz(pll_type); if (khz) return khz; khz = of_to_core_khz(); if (khz) return khz; printk(KERN_WARNING "perfctr: unable to determine CPU speed\n"); return 0; } static int __init known_init(void) { static char known_name[] __initdata = "PowerPC 60x/7xx/74xx"; unsigned int features; enum pll_type pll_type; unsigned int pvr; int have_mmcr1; features = PERFCTR_FEATURE_RDTSC | PERFCTR_FEATURE_RDPMC; have_mmcr1 = 1; pvr = mfspr(SPRN_PVR); switch (PVR_VER(pvr)) { case 0x0004: /* 604 */ pm_type = PM_604; pll_type = PLL_NONE; features = PERFCTR_FEATURE_RDTSC; have_mmcr1 = 0; break; case 0x0009: /* 604e; */ case 0x000A: /* 604ev */ pm_type = PM_604e; pll_type = PLL_604e; features = PERFCTR_FEATURE_RDTSC; break; case 0x0008: /* 750/740 */ pm_type = PM_750; pll_type = PLL_750; break; case 0x7000: case 0x7001: /* IBM750FX */ if ((pvr & 0xFF0F) >= 0x0203) features |= PERFCTR_FEATURE_PCINT; pm_type = PM_750; pll_type = PLL_750FX; break; case 0x7002: /* IBM750GX */ features |= PERFCTR_FEATURE_PCINT; pm_type = PM_750; pll_type = PLL_750FX; break; case 0x000C: /* 7400 */ pm_type = PM_7400; pll_type = PLL_7400; break; case 0x800C: /* 7410 */ if ((pvr & 0xFFFF) >= 0x1103) features |= PERFCTR_FEATURE_PCINT; pm_type = PM_7400; pll_type = PLL_7400; break; case 0x8000: /* 7451/7441 */ features |= PERFCTR_FEATURE_PCINT; pm_type = PM_7450; pll_type = PLL_7450; break; case 0x8001: /* 7455/7445 */ features |= PERFCTR_FEATURE_PCINT; pm_type = PM_7450; pll_type = ((pvr & 0xFFFF) < 0x0303) ? PLL_7450 : PLL_7457; break; case 0x8002: /* 7457/7447 */ case 0x8003: /* 7447A */ features |= PERFCTR_FEATURE_PCINT; pm_type = PM_7450; pll_type = PLL_7457; break; case 0x8004: /* 7448 */ features |= PERFCTR_FEATURE_PCINT; pm_type = PM_7450; pll_type = PLL_NONE; /* known to differ from 7447A, no details yet */ break; default: return -ENODEV; } perfctr_info.cpu_features = features; perfctr_cpu_name = known_name; perfctr_info.cpu_khz = detect_cpu_khz(pll_type); perfctr_ppc_init_tests(have_mmcr1); return 0; } static int __init unknown_init(void) { static char unknown_name[] __initdata = "Generic PowerPC with TB"; unsigned int khz; khz = detect_cpu_khz(PLL_NONE); if (!khz) return -ENODEV; perfctr_info.cpu_features = PERFCTR_FEATURE_RDTSC; perfctr_cpu_name = unknown_name; perfctr_info.cpu_khz = khz; pm_type = PM_NONE; return 0; } static void perfctr_cpu_clear_one(void *ignore) { /* PREEMPT note: when called via on_each_cpu(), this is in IRQ context with preemption disabled. */ perfctr_cpu_clear_counters(); } static void perfctr_cpu_reset(void) { on_each_cpu(perfctr_cpu_clear_one, NULL, 1, 1); perfctr_cpu_set_ihandler(NULL); } static int init_done; int __init perfctr_cpu_init(void) { int err; perfctr_info.cpu_features = 0; err = known_init(); if (err) { err = unknown_init(); if (err) goto out; } init_done = 1; out: return err; } void __exit perfctr_cpu_exit(void) { } /**************************************************************** * * * Hardware reservation. * * * ****************************************************************/ static DEFINE_MUTEX(mutex); static const char *current_service = 0; const char *perfctr_cpu_reserve(const char *service) { const char *ret; if (!init_done) return "unsupported hardware"; mutex_lock(&mutex); ret = current_service; if (ret) goto out_unlock; ret = "unknown driver (oprofile?)"; if (perfctr_reserve_pmc_hardware() < 0) goto out_unlock; current_service = service; perfctr_cpu_reset(); ret = NULL; out_unlock: mutex_unlock(&mutex); return ret; } void perfctr_cpu_release(const char *service) { mutex_lock(&mutex); if (service != current_service) { printk(KERN_ERR "%s: attempt by %s to release while reserved by %s\n", __FUNCTION__, service, current_service); } else { /* power down the counters */ perfctr_cpu_reset(); current_service = 0; perfctr_release_pmc_hardware(); } mutex_unlock(&mutex); }