* RFC: performance monitor/oprofile support for e500
@ 2004-10-30 1:16 Andy Fleming
2004-10-30 3:29 ` Eugene Surovegin
2004-11-01 5:43 ` Kumar Gala
0 siblings, 2 replies; 5+ messages in thread
From: Andy Fleming @ 2004-10-30 1:16 UTC (permalink / raw)
To: <linuxppc-embedded@ozlabs.org> <linuxppc-embedded@ozlabs.org>
[-- Attachment #1: Type: text/plain, Size: 392 bytes --]
This patch:
* Adds support for using the performance monitor counters and the
associated interrupt, but only enables it currently for e500
* Removes the old 604 performance monitor code, suggested by various
people on IRC
* Adds oprofile support for the e500, with potential for expansion to
support other ppc32 processors
Andy Fleming
NCSG Open Source Team
Freescale Semiconductor, Inc
[-- Attachment #2: oprofile-kernel-patch --]
[-- Type: application/octet-stream, Size: 77030 bytes --]
diff -Nru a/arch/ppc/kernel/Makefile b/arch/ppc/kernel/Makefile
--- a/arch/ppc/kernel/Makefile 2004-10-29 19:58:14 -05:00
+++ b/arch/ppc/kernel/Makefile 2004-10-29 19:58:14 -05:00
@@ -14,7 +14,7 @@
obj-y := entry.o traps.o irq.o idle.o time.o misc.o \
process.o signal.o ptrace.o align.o \
semaphore.o syscalls.o setup.o \
- cputable.o ppc_htab.o
+ cputable.o ppc_htab.o perfmon.o
obj-$(CONFIG_6xx) += l2cr.o cpu_setup_6xx.o
obj-$(CONFIG_POWER4) += cpu_setup_power4.o
obj-$(CONFIG_MODULES) += module.o ppc_ksyms.o
diff -Nru a/arch/ppc/kernel/asm-offsets.c b/arch/ppc/kernel/asm-offsets.c
--- a/arch/ppc/kernel/asm-offsets.c 2004-10-29 19:58:14 -05:00
+++ b/arch/ppc/kernel/asm-offsets.c 2004-10-29 19:58:14 -05:00
@@ -118,6 +118,7 @@
DEFINE(ORIG_GPR3, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, orig_gpr3));
DEFINE(RESULT, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, result));
DEFINE(TRAP, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, trap));
+ DEFINE(SIA, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, sia));
DEFINE(CLONE_VM, CLONE_VM);
DEFINE(CLONE_UNTRACED, CLONE_UNTRACED);
DEFINE(MM_PGD, offsetof(struct mm_struct, pgd));
diff -Nru a/arch/ppc/kernel/cputable.c b/arch/ppc/kernel/cputable.c
--- a/arch/ppc/kernel/cputable.c 2004-10-29 19:58:14 -05:00
+++ b/arch/ppc/kernel/cputable.c 2004-10-29 19:58:14 -05:00
@@ -114,8 +114,7 @@
{ /* 604 */
0xffff0000, 0x00040000, "604",
CPU_FTR_COMMON |
- CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | CPU_FTR_604_PERF_MON |
- CPU_FTR_HPTE_TABLE,
+ CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | CPU_FTR_HPTE_TABLE,
COMMON_PPC,
32, 32,
__setup_cpu_604
@@ -123,8 +122,7 @@
{ /* 604e */
0xfffff000, 0x00090000, "604e",
CPU_FTR_COMMON |
- CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | CPU_FTR_604_PERF_MON |
- CPU_FTR_HPTE_TABLE,
+ CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | CPU_FTR_HPTE_TABLE,
COMMON_PPC,
32, 32,
__setup_cpu_604
@@ -132,8 +130,7 @@
{ /* 604r */
0xffff0000, 0x00090000, "604r",
CPU_FTR_COMMON |
- CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | CPU_FTR_604_PERF_MON |
- CPU_FTR_HPTE_TABLE,
+ CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | CPU_FTR_HPTE_TABLE,
COMMON_PPC,
32, 32,
__setup_cpu_604
@@ -141,8 +138,7 @@
{ /* 604ev */
0xffff0000, 0x000a0000, "604ev",
CPU_FTR_COMMON |
- CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | CPU_FTR_604_PERF_MON |
- CPU_FTR_HPTE_TABLE,
+ CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | CPU_FTR_HPTE_TABLE,
COMMON_PPC,
32, 32,
__setup_cpu_604
@@ -612,7 +608,9 @@
{ /* e500 */
0xffff0000, 0x80200000, "e500",
/* xxx - galak: add CPU_FTR_MAYBE_CAN_DOZE */
- CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB,
+ CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB
+ | CPU_FTR_CAN_USE_PMON_INTR
+ | CPU_FTR_FSL_BOOKE_PMON,
PPC_FEATURE_32 | PPC_FEATURE_HAS_MMU,
32, 32,
0, /*__setup_cpu_e500 */
diff -Nru a/arch/ppc/kernel/head_e500.S b/arch/ppc/kernel/head_e500.S
--- a/arch/ppc/kernel/head_e500.S 2004-10-29 19:58:14 -05:00
+++ b/arch/ppc/kernel/head_e500.S 2004-10-29 19:58:14 -05:00
@@ -666,7 +666,14 @@
EXCEPTION(0x2050, SPEFloatingPointRound, UnknownException, EXC_XFER_EE)
/* Performance Monitor */
- EXCEPTION(0x2060, PerformanceMonitor, UnknownException, EXC_XFER_EE)
+# EXCEPTION(0x2060, PerformanceMonitor, PerformanceMonitorException, EXC_XFER_STD)
+ START_EXCEPTION(PerformanceMonitor)
+ NORMAL_EXCEPTION_PROLOG
+ stw r12,SIA(r11) /* Save the old SRR0 */
+ addi r3,r1,STACK_FRAME_OVERHEAD
+ EXC_XFER_STD(0x2060, PerformanceMonitorException)
+
+
/* Check for a single step debug exception while in an exception
* handler before state has been saved. This is to catch the case
diff -Nru a/arch/ppc/kernel/perfmon.c b/arch/ppc/kernel/perfmon.c
--- /dev/null Wed Dec 31 16:00:00 196900
+++ b/arch/ppc/kernel/perfmon.c 2004-10-29 19:58:14 -05:00
@@ -0,0 +1,291 @@
+/* kernel/perfmon.c
+ * PPC 32 Performance Monitor Infrastructure
+ *
+ * Author: Andy Fleming
+ * Copyright (c) 2004 Freescale Semiconductor, Inc
+ *
+ * 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 of the License, or (at your option) any later version.
+ */
+
+/* A lock to regulate grabbing the interrupt */
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/stddef.h>
+#include <linux/unistd.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/user.h>
+#include <linux/a.out.h>
+#include <linux/interrupt.h>
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/prctl.h>
+
+#include <asm/pgtable.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/reg.h>
+#include <asm/xmon.h>
+
+spinlock_t perfmon_lock = SPIN_LOCK_UNLOCKED;
+
+#ifdef CONFIG_FSL_BOOKE
+void init_pmc_stop(int ctr);
+void set_pmc_event(int ctr, int event);
+void set_pmc_user_kernel(int ctr, int user, int kernel);
+void set_pmc_marked(int ctr, int mark0, int mark1);
+void pmc_start_ctr(int ctr, int enable);
+void pmc_start_ctrs(int enable);
+void pmc_stop_ctrs(void);
+void dump_pmcs(void);
+
+static inline u32 get_pmlca(int ctr);
+static inline void set_pmlca(int ctr, u32 pmlca);
+
+static void dummy_perf(struct pt_regs *regs)
+{
+ unsigned int pmgc0 = mfpmr(PMRN_PMGC0);
+
+ pmgc0 &= ~PMGC0_PMIE;
+ mtpmr(PMRN_PMGC0, pmgc0);
+}
+
+#else
+/* Ensure exceptions are disabled */
+#define MMCR0_PMXE (1UL << (31 - 5))
+
+static void dummy_perf(struct pt_regs *regs)
+{
+ unsigned int mmcr0 = mfspr(SPRN_MMCR0);
+
+ mmcr0 &= ~MMCR0_PMXE;
+ mtspr(SPRN_MMCR0, mmcr0);
+}
+
+#endif
+
+void (*perf_irq)(struct pt_regs *) = dummy_perf;
+
+/* Grab the interrupt, if it's free.
+ * Returns 0 on success, -1 if the interrupt is taken already */
+int request_perfmon_irq(void (*handler)(struct pt_regs *))
+{
+ int err = 0;
+
+ spin_lock(&perfmon_lock);
+
+ if (perf_irq == dummy_perf)
+ perf_irq = handler;
+ else
+ err = -1;
+
+ spin_unlock(&perfmon_lock);
+
+ return err;
+}
+
+void free_perfmon_irq(void)
+{
+ spin_lock(&perfmon_lock);
+
+ perf_irq = dummy_perf;
+
+ spin_unlock(&perfmon_lock);
+}
+
+#ifdef CONFIG_FSL_BOOKE
+static inline u32 get_pmlca(int ctr)
+{
+ u32 pmlca;
+
+ switch (ctr) {
+ case 0:
+ pmlca = mfpmr(PMRN_PMLCA0);
+ break;
+ case 1:
+ pmlca = mfpmr(PMRN_PMLCA1);
+ break;
+ case 2:
+ pmlca = mfpmr(PMRN_PMLCA2);
+ break;
+ case 3:
+ pmlca = mfpmr(PMRN_PMLCA3);
+ break;
+ default:
+ panic("Bad ctr number\n");
+ }
+
+ return pmlca;
+}
+
+static inline void set_pmlca(int ctr, u32 pmlca)
+{
+ switch (ctr) {
+ case 0:
+ mtpmr(PMRN_PMLCA0, pmlca);
+ break;
+ case 1:
+ mtpmr(PMRN_PMLCA1, pmlca);
+ break;
+ case 2:
+ mtpmr(PMRN_PMLCA2, pmlca);
+ break;
+ case 3:
+ mtpmr(PMRN_PMLCA3, pmlca);
+ break;
+ default:
+ panic("Bad ctr number\n");
+ }
+}
+
+void init_pmc_stop(int ctr)
+{
+ u32 pmlca = (PMLCA_FC | PMLCA_FCS | PMLCA_FCU |
+ PMLCA_FCM1 | PMLCA_FCM0);
+ u32 pmlcb = 0;
+
+ switch (ctr) {
+ case 0:
+ mtpmr(PMRN_PMLCA0, pmlca);
+ mtpmr(PMRN_PMLCB0, pmlcb);
+ break;
+ case 1:
+ mtpmr(PMRN_PMLCA1, pmlca);
+ mtpmr(PMRN_PMLCB1, pmlcb);
+ break;
+ case 2:
+ mtpmr(PMRN_PMLCA2, pmlca);
+ mtpmr(PMRN_PMLCB2, pmlcb);
+ break;
+ case 3:
+ mtpmr(PMRN_PMLCA3, pmlca);
+ mtpmr(PMRN_PMLCB3, pmlcb);
+ break;
+ default:
+ panic("Bad ctr number!\n");
+ }
+}
+
+void set_pmc_event(int ctr, int event)
+{
+ u32 pmlca;
+
+ pmlca = get_pmlca(ctr);
+
+ pmlca = (pmlca & ~PMLCA_EVENT_MASK) |
+ ((event << PMLCA_EVENT_SHIFT) &
+ PMLCA_EVENT_MASK);
+
+ set_pmlca(ctr, pmlca);
+}
+
+void set_pmc_user_kernel(int ctr, int user, int kernel)
+{
+ u32 pmlca;
+
+ pmlca = get_pmlca(ctr);
+
+ if(user)
+ pmlca &= ~PMLCA_FCU;
+ else
+ pmlca |= PMLCA_FCU;
+
+ if(kernel)
+ pmlca &= ~PMLCA_FCS;
+ else
+ pmlca |= PMLCA_FCS;
+
+ set_pmlca(ctr, pmlca);
+}
+
+void set_pmc_marked(int ctr, int mark0, int mark1)
+{
+ u32 pmlca = get_pmlca(ctr);
+
+ if(mark0)
+ pmlca &= ~PMLCA_FCM0;
+ else
+ pmlca |= PMLCA_FCM0;
+
+ if(mark1)
+ pmlca &= ~PMLCA_FCM1;
+ else
+ pmlca |= PMLCA_FCM1;
+
+ set_pmlca(ctr, pmlca);
+}
+
+void pmc_start_ctr(int ctr, int enable)
+{
+ u32 pmlca = get_pmlca(ctr);
+
+ pmlca &= ~PMLCA_FC;
+
+ if (enable)
+ pmlca |= PMLCA_CE;
+ else
+ pmlca &= ~PMLCA_CE;
+
+ set_pmlca(ctr, pmlca);
+}
+
+void pmc_start_ctrs(int enable)
+{
+ u32 pmgc0 = mfpmr(PMRN_PMGC0);
+
+ pmgc0 &= ~PMGC0_FAC;
+ pmgc0 |= PMGC0_FCECE;
+
+ if (enable)
+ pmgc0 |= PMGC0_PMIE;
+ else
+ pmgc0 &= ~PMGC0_PMIE;
+
+ mtpmr(PMRN_PMGC0, pmgc0);
+}
+
+void pmc_stop_ctrs(void)
+{
+ u32 pmgc0 = mfpmr(PMRN_PMGC0);
+
+ pmgc0 |= PMGC0_FAC;
+
+ pmgc0 &= ~(PMGC0_PMIE | PMGC0_FCECE);
+
+ mtpmr(PMRN_PMGC0, pmgc0);
+}
+
+void dump_pmcs(void)
+{
+ printk("pmgc0: %x\n", mfpmr(PMRN_PMGC0));
+ printk("pmc\t\tpmlca\t\tpmlcb\n");
+ printk("%8x\t%8x\t%8x\n", mfpmr(PMRN_PMC0),
+ mfpmr(PMRN_PMLCA0), mfpmr(PMRN_PMLCB0));
+ printk("%8x\t%8x\t%8x\n", mfpmr(PMRN_PMC1),
+ mfpmr(PMRN_PMLCA1), mfpmr(PMRN_PMLCB1));
+ printk("%8x\t%8x\t%8x\n", mfpmr(PMRN_PMC2),
+ mfpmr(PMRN_PMLCA2), mfpmr(PMRN_PMLCB2));
+ printk("%8x\t%8x\t%8x\n", mfpmr(PMRN_PMC3),
+ mfpmr(PMRN_PMLCA3), mfpmr(PMRN_PMLCB3));
+}
+
+EXPORT_SYMBOL(init_pmc_stop);
+EXPORT_SYMBOL(set_pmc_event);
+EXPORT_SYMBOL(set_pmc_user_kernel);
+EXPORT_SYMBOL(set_pmc_marked);
+EXPORT_SYMBOL(pmc_start_ctr);
+EXPORT_SYMBOL(pmc_start_ctrs);
+EXPORT_SYMBOL(pmc_stop_ctrs);
+EXPORT_SYMBOL(dump_pmcs);
+
+#endif
+EXPORT_SYMBOL(perf_irq);
+EXPORT_SYMBOL(request_perfmon_irq);
+EXPORT_SYMBOL(free_perfmon_irq);
diff -Nru a/arch/ppc/kernel/ppc_htab.c b/arch/ppc/kernel/ppc_htab.c
--- a/arch/ppc/kernel/ppc_htab.c 2004-10-29 19:58:14 -05:00
+++ b/arch/ppc/kernel/ppc_htab.c 2004-10-29 19:58:14 -05:00
@@ -59,42 +59,6 @@
.release = single_release,
};
-static char *pmc1_lookup(unsigned long mmcr0)
-{
- switch ( mmcr0 & (0x7f<<7) )
- {
- case 0x0:
- return "none";
- case MMCR0_PMC1_CYCLES:
- return "cycles";
- case MMCR0_PMC1_ICACHEMISS:
- return "ic miss";
- case MMCR0_PMC1_DTLB:
- return "dtlb miss";
- default:
- return "unknown";
- }
-}
-
-static char *pmc2_lookup(unsigned long mmcr0)
-{
- switch ( mmcr0 & 0x3f )
- {
- case 0x0:
- return "none";
- case MMCR0_PMC2_CYCLES:
- return "cycles";
- case MMCR0_PMC2_DCACHEMISS:
- return "dc miss";
- case MMCR0_PMC2_ITLB:
- return "itlb miss";
- case MMCR0_PMC2_LOADMISSTIME:
- return "load miss time";
- default:
- return "unknown";
- }
-}
-
/*
* print some useful info about the hash table. This function
* is _REALLY_ slow (see the nested for loops below) but nothing
@@ -102,29 +66,11 @@
*/
static int ppc_htab_show(struct seq_file *m, void *v)
{
- unsigned long mmcr0 = 0, pmc1 = 0, pmc2 = 0;
#if defined(CONFIG_PPC_STD_MMU) && !defined(CONFIG_PPC64BRIDGE)
unsigned int kptes = 0, uptes = 0;
PTE *ptr;
#endif /* CONFIG_PPC_STD_MMU */
- if (cur_cpu_spec[0]->cpu_features & CPU_FTR_604_PERF_MON) {
- mmcr0 = mfspr(SPRN_MMCR0);
- pmc1 = mfspr(SPRN_PMC1);
- pmc2 = mfspr(SPRN_PMC2);
- seq_printf(m,
- "604 Performance Monitoring\n"
- "MMCR0\t\t: %08lx %s%s ",
- mmcr0,
- ( mmcr0>>28 & 0x2 ) ? "(user mode counted)" : "",
- ( mmcr0>>28 & 0x4 ) ? "(kernel mode counted)" : "");
- seq_printf(m,
- "\nPMC1\t\t: %08lx (%s)\n"
- "PMC2\t\t: %08lx (%s)\n",
- pmc1, pmc1_lookup(mmcr0),
- pmc2, pmc2_lookup(mmcr0));
- }
-
#ifdef CONFIG_PPC_STD_MMU
/* if we don't have a htab */
if ( Hash_size == 0 ) {
@@ -188,7 +134,7 @@
}
/*
- * Allow user to define performance counters and resize the hash table
+ * Allow user to resize the hash table
*/
static ssize_t ppc_htab_write(struct file * file, const char __user * ubuffer,
size_t count, loff_t *ppos)
@@ -209,106 +155,10 @@
if ( !strncmp( buffer, "reset", 5) )
{
- if (cur_cpu_spec[0]->cpu_features & CPU_FTR_604_PERF_MON) {
- /* reset PMC1 and PMC2 */
- mtspr(SPRN_PMC1, 0);
- mtspr(SPRN_PMC2, 0);
- }
htab_reloads = 0;
htab_evicts = 0;
pte_misses = 0;
pte_errors = 0;
- }
-
- /* Everything below here requires the performance monitor feature. */
- if ( !cur_cpu_spec[0]->cpu_features & CPU_FTR_604_PERF_MON )
- return count;
-
- /* turn off performance monitoring */
- if ( !strncmp( buffer, "off", 3) )
- {
- mtspr(SPRN_MMCR0, 0);
- mtspr(SPRN_PMC1, 0);
- mtspr(SPRN_PMC2, 0);
- }
-
- if ( !strncmp( buffer, "user", 4) )
- {
- /* setup mmcr0 and clear the correct pmc */
- tmp = (mfspr(SPRN_MMCR0) & ~(0x60000000)) | 0x20000000;
- mtspr(SPRN_MMCR0, tmp);
- mtspr(SPRN_PMC1, 0);
- mtspr(SPRN_PMC2, 0);
- }
-
- if ( !strncmp( buffer, "kernel", 6) )
- {
- /* setup mmcr0 and clear the correct pmc */
- tmp = (mfspr(SPRN_MMCR0) & ~(0x60000000)) | 0x40000000;
- mtspr(SPRN_MMCR0, tmp);
- mtspr(SPRN_PMC1, 0);
- mtspr(SPRN_PMC2, 0);
- }
-
- /* PMC1 values */
- if ( !strncmp( buffer, "dtlb", 4) )
- {
- /* setup mmcr0 and clear the correct pmc */
- tmp = (mfspr(SPRN_MMCR0) & ~(0x7F << 7)) | MMCR0_PMC1_DTLB;
- mtspr(SPRN_MMCR0, tmp);
- mtspr(SPRN_PMC1, 0);
- }
-
- if ( !strncmp( buffer, "ic miss", 7) )
- {
- /* setup mmcr0 and clear the correct pmc */
- tmp = (mfspr(SPRN_MMCR0) & ~(0x7F<<7)) | MMCR0_PMC1_ICACHEMISS;
- mtspr(SPRN_MMCR0, tmp);
- mtspr(SPRN_PMC1, 0);
- }
-
- /* PMC2 values */
- if ( !strncmp( buffer, "load miss time", 14) )
- {
- /* setup mmcr0 and clear the correct pmc */
- asm volatile(
- "mfspr %0,%1\n\t" /* get current mccr0 */
- "rlwinm %0,%0,0,0,31-6\n\t" /* clear bits [26-31] */
- "ori %0,%0,%2 \n\t" /* or in mmcr0 settings */
- "mtspr %1,%0 \n\t" /* set new mccr0 */
- "mtspr %3,%4 \n\t" /* reset the pmc */
- : "=r" (tmp)
- : "i" (SPRN_MMCR0),
- "i" (MMCR0_PMC2_LOADMISSTIME),
- "i" (SPRN_PMC2), "r" (0) );
- }
-
- if ( !strncmp( buffer, "itlb", 4) )
- {
- /* setup mmcr0 and clear the correct pmc */
- asm volatile(
- "mfspr %0,%1\n\t" /* get current mccr0 */
- "rlwinm %0,%0,0,0,31-6\n\t" /* clear bits [26-31] */
- "ori %0,%0,%2 \n\t" /* or in mmcr0 settings */
- "mtspr %1,%0 \n\t" /* set new mccr0 */
- "mtspr %3,%4 \n\t" /* reset the pmc */
- : "=r" (tmp)
- : "i" (SPRN_MMCR0), "i" (MMCR0_PMC2_ITLB),
- "i" (SPRN_PMC2), "r" (0) );
- }
-
- if ( !strncmp( buffer, "dc miss", 7) )
- {
- /* setup mmcr0 and clear the correct pmc */
- asm volatile(
- "mfspr %0,%1\n\t" /* get current mccr0 */
- "rlwinm %0,%0,0,0,31-6\n\t" /* clear bits [26-31] */
- "ori %0,%0,%2 \n\t" /* or in mmcr0 settings */
- "mtspr %1,%0 \n\t" /* set new mccr0 */
- "mtspr %3,%4 \n\t" /* reset the pmc */
- : "=r" (tmp)
- : "i" (SPRN_MMCR0), "i" (MMCR0_PMC2_DCACHEMISS),
- "i" (SPRN_PMC2), "r" (0) );
}
return count;
diff -Nru a/arch/ppc/kernel/traps.c b/arch/ppc/kernel/traps.c
--- a/arch/ppc/kernel/traps.c 2004-10-29 19:58:14 -05:00
+++ b/arch/ppc/kernel/traps.c 2004-10-29 19:58:14 -05:00
@@ -71,6 +71,7 @@
* Trap & Exception support
*/
+extern void (*perf_irq)(struct pt_regs *);
spinlock_t die_lock = SPIN_LOCK_UNLOCKED;
@@ -725,6 +726,11 @@
}
}
#endif /* CONFIG_ALTIVEC */
+
+void PerformanceMonitorException(struct pt_regs *regs)
+{
+ perf_irq(regs);
+}
#ifdef CONFIG_FSL_BOOKE
void CacheLockingException(struct pt_regs *regs, unsigned long address,
diff -Nru a/arch/ppc/oprofile/Makefile b/arch/ppc/oprofile/Makefile
--- a/arch/ppc/oprofile/Makefile 2004-10-29 19:58:14 -05:00
+++ b/arch/ppc/oprofile/Makefile 2004-10-29 19:58:14 -05:00
@@ -6,4 +6,4 @@
oprofilefs.o oprofile_stats.o \
timer_int.o )
-oprofile-y := $(DRIVER_OBJS) init.o
+oprofile-y := $(DRIVER_OBJS) common.o op_model_e500.o
diff -Nru a/arch/ppc/oprofile/common.c b/arch/ppc/oprofile/common.c
--- /dev/null Wed Dec 31 16:00:00 196900
+++ b/arch/ppc/oprofile/common.c 2004-10-29 19:58:14 -05:00
@@ -0,0 +1,152 @@
+/*
+ * PPC 32 oprofile support
+ * Based on PPC64 oprofile support
+ * Copyright (C) 2004 Anton Blanchard <anton@au.ibm.com>, IBM
+ *
+ * Copyright (C) Freescale Semiconductor, Inc 2004
+ *
+ * Author: Andy Fleming
+ *
+ * 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 of the License, or (at your option) any later version.
+ */
+
+#include <linux/oprofile.h>
+#include <linux/init.h>
+#include <linux/smp.h>
+#include <linux/errno.h>
+#include <asm/ptrace.h>
+#include <asm/system.h>
+#include <asm/perfmon.h>
+
+#include "op_impl.h"
+
+extern struct op_ppc32_model op_model_e500;
+static struct op_ppc32_model *model;
+
+static struct op_counter_config ctr[OP_MAX_COUNTER];
+static struct op_system_config sys;
+
+static void op_handle_interrupt(struct pt_regs *regs)
+{
+ model->handle_interrupt(regs, ctr);
+}
+
+static int op_ppc32_setup(void)
+{
+ /* Install our interrupt handler into the existing hook. */
+ if(request_perfmon_irq(&op_handle_interrupt))
+ return -EBUSY;
+
+ mb();
+
+ /* Pre-compute the values to stuff in the hardware registers. */
+ model->reg_setup(ctr, &sys, model->num_counters);
+
+#if 0
+ /* FIXME: Make multi-cpu work */
+ /* Configure the registers on all cpus. */
+ on_each_cpu(model->reg_setup, NULL, 0, 1);
+#endif
+
+ return 0;
+}
+
+static void op_ppc32_shutdown(void)
+{
+ mb();
+
+ /* Remove our interrupt handler. We may be removing this module. */
+ free_perfmon_irq();
+}
+
+static void op_ppc32_cpu_start(void *dummy)
+{
+ model->start(ctr);
+}
+
+static int op_ppc32_start(void)
+{
+ on_each_cpu(op_ppc32_cpu_start, NULL, 0, 1);
+ return 0;
+}
+
+static inline void op_ppc32_cpu_stop(void *dummy)
+{
+ model->stop();
+}
+
+static void op_ppc32_stop(void)
+{
+ on_each_cpu(op_ppc32_cpu_stop, NULL, 0, 1);
+}
+
+static int op_ppc32_create_files(struct super_block *sb, struct dentry *root)
+{
+ int i;
+
+ for (i = 0; i < model->num_counters; ++i) {
+ struct dentry *dir;
+ char buf[3];
+
+ snprintf(buf, sizeof buf, "%d", i);
+ dir = oprofilefs_mkdir(sb, root, buf);
+
+ oprofilefs_create_ulong(sb, dir, "enabled", &ctr[i].enabled);
+ oprofilefs_create_ulong(sb, dir, "event", &ctr[i].event);
+ oprofilefs_create_ulong(sb, dir, "count", &ctr[i].count);
+ oprofilefs_create_ulong(sb, dir, "kernel", &ctr[i].kernel);
+ oprofilefs_create_ulong(sb, dir, "user", &ctr[i].user);
+
+ /* FIXME: Not sure if this is used */
+ oprofilefs_create_ulong(sb, dir, "unit_mask", &ctr[i].unit_mask);
+ }
+
+ oprofilefs_create_ulong(sb, root, "enable_kernel", &sys.enable_kernel);
+ oprofilefs_create_ulong(sb, root, "enable_user", &sys.enable_user);
+
+ /* Default to tracing both kernel and user */
+ sys.enable_kernel = 1;
+ sys.enable_user = 1;
+
+ return 0;
+}
+
+static struct oprofile_operations oprof_ppc32_ops = {
+ .create_files = op_ppc32_create_files,
+ .setup = op_ppc32_setup,
+ .shutdown = op_ppc32_shutdown,
+ .start = op_ppc32_start,
+ .stop = op_ppc32_stop,
+ .cpu_type = NULL /* To be filled in below. */
+};
+
+int __init oprofile_arch_init(struct oprofile_operations **ops)
+{
+ unsigned int pvr;
+
+ pvr = mfspr(SPRN_PVR);
+
+ switch (PVR_VER(pvr)) {
+ case PVR_VER(PVR_8540): /* Same as 8560 */
+ model = &op_model_e500;
+ model->num_counters = 4;
+ oprof_ppc32_ops.cpu_type = "ppc/e500";
+ break;
+ default:
+ return -ENODEV;
+ }
+
+ *ops = &oprof_ppc32_ops;
+
+ printk(KERN_INFO "oprofile: using %s performance monitoring.\n",
+ oprof_ppc32_ops.cpu_type);
+
+ return 0;
+}
+
+void oprofile_arch_exit(void)
+{
+}
diff -Nru a/arch/ppc/oprofile/init.c b/arch/ppc/oprofile/init.c
--- a/arch/ppc/oprofile/init.c 2004-10-29 19:58:14 -05:00
+++ /dev/null Wed Dec 31 16:00:00 196900
@@ -1,23 +0,0 @@
-/**
- * @file init.c
- *
- * @remark Copyright 2002 OProfile authors
- * @remark Read the file COPYING
- *
- * @author John Levon <levon@movementarian.org>
- */
-
-#include <linux/kernel.h>
-#include <linux/oprofile.h>
-#include <linux/init.h>
-#include <linux/errno.h>
-
-int __init oprofile_arch_init(struct oprofile_operations ** ops)
-{
- return -ENODEV;
-}
-
-
-void oprofile_arch_exit(void)
-{
-}
diff -Nru a/arch/ppc/oprofile/op_impl.h b/arch/ppc/oprofile/op_impl.h
--- /dev/null Wed Dec 31 16:00:00 196900
+++ b/arch/ppc/oprofile/op_impl.h 2004-10-29 19:58:14 -05:00
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2004 Anton Blanchard <anton@au.ibm.com>, IBM
+ *
+ * Based on alpha version.
+ *
+ * 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 of the License, or (at your option) any later version.
+ */
+
+#ifndef OP_IMPL_H
+#define OP_IMPL_H 1
+
+#define OP_MAX_COUNTER 8
+
+/* Per-counter configuration as set via oprofilefs. */
+struct op_counter_config {
+ unsigned long enabled;
+ unsigned long event;
+ unsigned long count;
+ unsigned long kernel;
+ unsigned long user;
+ unsigned long unit_mask;
+};
+
+/* System-wide configuration as set via oprofilefs. */
+struct op_system_config {
+ unsigned long enable_kernel;
+ unsigned long enable_user;
+};
+
+/* Per-arch configuration */
+struct op_ppc32_model {
+ void (*reg_setup) (struct op_counter_config *,
+ struct op_system_config *,
+ int num_counters);
+ void (*start) (struct op_counter_config *);
+ void (*stop) (void);
+ void (*handle_interrupt) (struct pt_regs *,
+ struct op_counter_config *);
+ int num_counters;
+};
+
+static inline unsigned int ctr_read(unsigned int i)
+{
+ switch(i) {
+ case 0:
+ return mfpmr(PMRN_PMC0);
+ case 1:
+ return mfpmr(PMRN_PMC1);
+ case 2:
+ return mfpmr(PMRN_PMC2);
+ case 3:
+ return mfpmr(PMRN_PMC3);
+ default:
+ return 0;
+ }
+}
+
+static inline void ctr_write(unsigned int i, unsigned int val)
+{
+ switch(i) {
+ case 0:
+ mtpmr(PMRN_PMC0, val);
+ break;
+ case 1:
+ mtpmr(PMRN_PMC1, val);
+ break;
+ case 2:
+ mtpmr(PMRN_PMC2, val);
+ break;
+ case 3:
+ mtpmr(PMRN_PMC3, val);
+ break;
+ default:
+ break;
+ }
+}
+
+#endif
diff -Nru a/arch/ppc/oprofile/op_model_e500.c b/arch/ppc/oprofile/op_model_e500.c
--- /dev/null Wed Dec 31 16:00:00 196900
+++ b/arch/ppc/oprofile/op_model_e500.c 2004-10-29 19:58:15 -05:00
@@ -0,0 +1,159 @@
+/*
+ * oprofile/op_model_e500.c
+ *
+ * e500 oprofile support, based on ppc64 oprofile support
+ * Copyright (C) 2004 Anton Blanchard <anton@au.ibm.com>, IBM
+ *
+ * Copyright (c) 2004 Freescale Semiconductor, Inc
+ *
+ * Author: Andy Fleming
+ * Maintainer: Kumar Gala <Kumar.Gala@freescale.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 of the License, or (at your option) any later version.
+ */
+
+#include <linux/oprofile.h>
+#include <linux/init.h>
+#include <linux/smp.h>
+#include <asm/ptrace.h>
+#include <asm/system.h>
+#include <asm/processor.h>
+#include <asm/cputable.h>
+#include <asm/reg_booke.h>
+#include <asm/page.h>
+#include <asm/perfmon.h>
+
+/*#define dbg(args...) printk(args) */
+#define dbg
+
+#include "op_impl.h"
+
+static unsigned long reset_value[OP_MAX_COUNTER];
+
+static int num_counters;
+static int oprofile_running;
+
+static void e500_reg_setup(struct op_counter_config *ctr,
+ struct op_system_config *sys,
+ int num_ctrs)
+{
+ int i;
+
+ num_counters = num_ctrs;
+
+ /* freeze all counters */
+ pmc_stop_ctrs();
+
+ /* Our counters count up, and "count" refers to
+ * how much before the next interrupt, and we interrupt
+ * on overflow. So we calculate the starting value
+ * which will give us "count" until overflow.
+ * Then we set the events on the enabled counters */
+ for (i = 0; i < num_counters; ++i) {
+ reset_value[i] = 0x80000000UL - ctr[i].count;
+
+ init_pmc_stop(i);
+
+ set_pmc_event(i, ctr[i].event);
+
+ set_pmc_user_kernel(i, ctr[i].user, ctr[i].kernel);
+ }
+}
+
+static void e500_start(struct op_counter_config *ctr)
+{
+ int i;
+
+ mtmsr(mfmsr() | MSR_PMM);
+
+ for (i = 0; i < num_counters; ++i) {
+ if (ctr[i].enabled) {
+ ctr_write(i, reset_value[i]);
+ /* Set Each enabled counterd to only
+ * count when the Mark bit is not set */
+ set_pmc_marked(i, 1, 0);
+ pmc_start_ctr(i, 1);
+ } else {
+ ctr_write(i, 0);
+
+ /* Set the ctr to be stopped */
+ pmc_start_ctr(i, 0);
+ }
+ }
+
+ /* Clear the freeze bit, and enable the interrupt.
+ * The counters won't actually start until the rfi clears
+ * the PMM bit */
+ pmc_start_ctrs(1);
+
+ oprofile_running = 1;
+
+ dbg("start on cpu %d, pmgc0 %x\n", smp_processor_id(),
+ mfpmr(PMRN_PMGC0));
+}
+
+static void e500_stop(void)
+{
+ /* freeze counters */
+ pmc_stop_ctrs();
+
+ oprofile_running = 0;
+
+ dbg("stop on cpu %d, pmgc0 %x\n", smp_processor_id(),
+ mfpmr(PMRN_PMGC0));
+
+ mb();
+}
+
+static inline int get_kernel(unsigned long pc)
+{
+ int is_kernel;
+
+ is_kernel = (pc >= KERNELBASE);
+
+ return is_kernel;
+}
+
+static void e500_handle_interrupt(struct pt_regs *regs,
+ struct op_counter_config *ctr)
+{
+ unsigned long pc;
+ int is_kernel;
+ int val;
+ int i;
+ unsigned int cpu = smp_processor_id();
+
+ /* set the PMM bit (see comment below) */
+ mtmsr(mfmsr() | MSR_PMM);
+
+ pc = regs->sia;
+ is_kernel = get_kernel(pc);
+
+ for (i = 0; i < num_counters; ++i) {
+ val = ctr_read(i);
+ if (val < 0) {
+ if (oprofile_running && ctr[i].enabled) {
+ oprofile_add_sample(pc, is_kernel, i, cpu);
+ ctr_write(i, reset_value[i]);
+ } else {
+ ctr_write(i, 0);
+ }
+ }
+ }
+
+ /* The freeze bit was set by the interrupt. */
+ /* Clear the freeze bit, and reenable the interrupt.
+ * The counters won't actually start until the rfi clears
+ * the PMM bit */
+ pmc_start_ctrs(1);
+}
+
+struct op_ppc32_model op_model_e500 = {
+ .reg_setup = e500_reg_setup,
+ .start = e500_start,
+ .stop = e500_stop,
+ .handle_interrupt = e500_handle_interrupt,
+};
-
- return 0;
-}
-static int marvell_read_status(struct gfar_mii_info *mii_info)
-{
- u16 status;
- int err;
-
- /* Update the link, but return if there
- * was an error */
- err = genmii_update_link(mii_info);
- if (err)
- return err;
-
- /* If the link is up, read the speed and duplex */
- /* If we aren't autonegotiating, assume speeds
- * are as set */
- if (mii_info->autoneg && mii_info->link) {
- int speed;
- status = phy_read(mii_info, MII_M1011_PHY_SPEC_STATUS);
-
-#if 0
- /* If speed and duplex aren't resolved,
- * return an error. Isn't this handled
- * by checking aneg?
- */
- if ((status & MII_M1011_PHY_SPEC_STATUS_RESOLVED) == 0)
- return -EAGAIN;
-#endif
-
- /* Get the duplexity */
- if (status & MII_M1011_PHY_SPEC_STATUS_FULLDUPLEX)
- mii_info->duplex = DUPLEX_FULL;
- else
- mii_info->duplex = DUPLEX_HALF;
-
- /* Get the speed */
- speed = status & MII_M1011_PHY_SPEC_STATUS_SPD_MASK;
- switch(speed) {
- case MII_M1011_PHY_SPEC_STATUS_1000:
- mii_info->speed = SPEED_1000;
- break;
- case MII_M1011_PHY_SPEC_STATUS_100:
- mii_info->speed = SPEED_100;
- break;
- default:
- mii_info->speed = SPEED_10;
- break;
- }
- mii_info->pause = 0;
- }
-
- return 0;
-}
-
-
-static int cis820x_read_status(struct gfar_mii_info *mii_info)
-{
- u16 status;
- int err;
-
- /* Update the link, but return if there
- * was an error */
- err = genmii_update_link(mii_info);
- if (err)
- return err;
-
- /* If the link is up, read the speed and duplex */
- /* If we aren't autonegotiating, assume speeds
- * are as set */
- if (mii_info->autoneg && mii_info->link) {
- int speed;
-
- status = phy_read(mii_info, MII_CIS8201_AUX_CONSTAT);
- if (status & MII_CIS8201_AUXCONSTAT_DUPLEX)
- mii_info->duplex = DUPLEX_FULL;
- else
- mii_info->duplex = DUPLEX_HALF;
-
- speed = status & MII_CIS8201_AUXCONSTAT_SPEED;
-
- switch (speed) {
- case MII_CIS8201_AUXCONSTAT_GBIT:
- mii_info->speed = SPEED_1000;
- break;
- case MII_CIS8201_AUXCONSTAT_100:
- mii_info->speed = SPEED_100;
- break;
- default:
- mii_info->speed = SPEED_10;
- break;
- }
- }
-
- return 0;
-}
-
-static int marvell_ack_interrupt(struct gfar_mii_info *mii_info)
-{
- /* Clear the interrupts by reading the reg */
- phy_read(mii_info, MII_M1011_IEVENT);
-
- return 0;
-}
-
-static int marvell_config_intr(struct gfar_mii_info *mii_info)
-{
- if(mii_info->interrupts == MII_INTERRUPT_ENABLED)
- phy_write(mii_info, MII_M1011_IMASK, MII_M1011_IMASK_INIT);
- else
- phy_write(mii_info, MII_M1011_IMASK, MII_M1011_IMASK_CLEAR);
-
- return 0;
-}
-
-static int cis820x_init(struct gfar_mii_info *mii_info)
-{
- phy_write(mii_info, MII_CIS8201_AUX_CONSTAT,
- MII_CIS8201_AUXCONSTAT_INIT);
- phy_write(mii_info, MII_CIS8201_EXT_CON1,
- MII_CIS8201_EXTCON1_INIT);
-
- return 0;
-}
-
-static int cis820x_ack_interrupt(struct gfar_mii_info *mii_info)
-{
- phy_read(mii_info, MII_CIS8201_ISTAT);
-
- return 0;
-}
-
-static int cis820x_config_intr(struct gfar_mii_info *mii_info)
-{
- if(mii_info->interrupts == MII_INTERRUPT_ENABLED)
- phy_write(mii_info, MII_CIS8201_IMASK, MII_CIS8201_IMASK_MASK);
- else
- phy_write(mii_info, MII_CIS8201_IMASK, 0);
-
- return 0;
-}
-
-#define DM9161_DELAY 10
-
-static int dm9161_read_status(struct gfar_mii_info *mii_info)
-{
- u16 status;
- int err;
-
- /* Update the link, but return if there
- * was an error */
- err = genmii_update_link(mii_info);
- if (err)
- return err;
-
- /* If the link is up, read the speed and duplex */
- /* If we aren't autonegotiating, assume speeds
- * are as set */
- if (mii_info->autoneg && mii_info->link) {
- status = phy_read(mii_info, MII_DM9161_SCSR);
- if (status & (MII_DM9161_SCSR_100F | MII_DM9161_SCSR_100H))
- mii_info->speed = SPEED_100;
- else
- mii_info->speed = SPEED_10;
-
- if (status & (MII_DM9161_SCSR_100F | MII_DM9161_SCSR_10F))
- mii_info->duplex = DUPLEX_FULL;
- else
- mii_info->duplex = DUPLEX_HALF;
- }
-
- return 0;
-}
-
-
-static int dm9161_config_aneg(struct gfar_mii_info *mii_info)
-{
- struct dm9161_private *priv = mii_info->priv;
-
- if(0 == priv->resetdone)
- return -EAGAIN;
-
- return 0;
-}
-
-static void dm9161_timer(unsigned long data)
-{
- struct gfar_mii_info *mii_info = (struct gfar_mii_info *)data;
- struct dm9161_private *priv = mii_info->priv;
- u16 status = phy_read(mii_info, MII_BMSR);
-
- if (status & BMSR_ANEGCOMPLETE) {
- priv->resetdone = 1;
- } else
- mod_timer(&priv->timer, jiffies + DM9161_DELAY * HZ);
-}
-
-static int dm9161_init(struct gfar_mii_info *mii_info)
-{
- struct dm9161_private *priv;
-
- /* Allocate the private data structure */
- priv = kmalloc(sizeof(struct dm9161_private), GFP_KERNEL);
-
- if (NULL == priv)
- return -ENOMEM;
-
- mii_info->priv = priv;
-
- /* Reset is not done yet */
- priv->resetdone = 0;
-
- /* Isolate the PHY */
- phy_write(mii_info, MII_BMCR, BMCR_ISOLATE);
-
- /* Do not bypass the scrambler/descrambler */
- phy_write(mii_info, MII_DM9161_SCR, MII_DM9161_SCR_INIT);
-
- /* Clear 10BTCSR to default */
- phy_write(mii_info, MII_DM9161_10BTCSR, MII_DM9161_10BTCSR_INIT);
-
- /* Reconnect the PHY, and enable Autonegotiation */
- phy_write(mii_info, MII_BMCR, BMCR_ANENABLE);
-
- /* Start a timer for DM9161_DELAY seconds to wait
- * for the PHY to be ready */
- init_timer(&priv->timer);
- priv->timer.function = &dm9161_timer;
- priv->timer.data = (unsigned long) mii_info;
- mod_timer(&priv->timer, jiffies + DM9161_DELAY * HZ);
-
- return 0;
-}
-
-static void dm9161_close(struct gfar_mii_info *mii_info)
-{
- struct dm9161_private *priv = mii_info->priv;
-
- del_timer_sync(&priv->timer);
- kfree(priv);
-}
-
-#if 0
-static int dm9161_ack_interrupt(struct gfar_mii_info *mii_info)
-{
- phy_read(mii_info, MII_DM9161_INTR);
-
- return 0;
-}
-#endif
-
-/* Cicada 820x */
-static struct phy_info phy_info_cis820x = {
- 0x000fc440,
- "Cicada Cis8204",
- 0x000fffc0,
- .features = MII_GBIT_FEATURES,
- .init = &cis820x_init,
- .config_aneg = &gbit_config_aneg,
- .read_status = &cis820x_read_status,
- .ack_interrupt = &cis820x_ack_interrupt,
- .config_intr = &cis820x_config_intr,
-};
-
-static struct phy_info phy_info_dm9161 = {
- .phy_id = 0x0181b880,
- .name = "Davicom DM9161E",
- .phy_id_mask = 0x0ffffff0,
- .init = dm9161_init,
- .config_aneg = dm9161_config_aneg,
- .read_status = dm9161_read_status,
- .close = dm9161_close,
-};
-
-static struct phy_info phy_info_marvell = {
- .phy_id = 0x01410c00,
- .phy_id_mask = 0xffffff00,
- .name = "Marvell 88E1101",
- .features = MII_GBIT_FEATURES,
- .config_aneg = &marvell_config_aneg,
- .read_status = &marvell_read_status,
- .ack_interrupt = &marvell_ack_interrupt,
- .config_intr = &marvell_config_intr,
-};
-
-static struct phy_info phy_info_genmii= {
- .phy_id = 0x00000000,
- .phy_id_mask = 0x00000000,
- .name = "Generic MII",
- .features = MII_BASIC_FEATURES,
- .config_aneg = genmii_config_aneg,
- .read_status = genmii_read_status,
-};
-
-static struct phy_info *phy_info[] = {
- &phy_info_cis820x,
- &phy_info_marvell,
- &phy_info_dm9161,
- &phy_info_genmii,
- NULL
-};
-
-u16 phy_read(struct gfar_mii_info *mii_info, u16 regnum)
-{
- u16 retval;
- unsigned long flags;
-
- spin_lock_irqsave(&mii_info->mdio_lock, flags);
- retval = mii_info->mdio_read(mii_info->dev, mii_info->mii_id, regnum);
- spin_unlock_irqrestore(&mii_info->mdio_lock, flags);
-
- return retval;
-}
-
-void phy_write(struct gfar_mii_info *mii_info, u16 regnum, u16 val)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&mii_info->mdio_lock, flags);
- mii_info->mdio_write(mii_info->dev,
- mii_info->mii_id,
- regnum, val);
- spin_unlock_irqrestore(&mii_info->mdio_lock, flags);
-}
-
-/* Use the PHY ID registers to determine what type of PHY is attached
- * to device dev. return a struct phy_info structure describing that PHY
- */
-struct phy_info * get_phy_info(struct gfar_mii_info *mii_info)
-{
- u16 phy_reg;
- u32 phy_ID;
- int i;
- struct phy_info *theInfo = NULL;
- struct net_device *dev = mii_info->dev;
-
- /* Grab the bits from PHYIR1, and put them in the upper half */
- phy_reg = phy_read(mii_info, MII_PHYSID1);
- phy_ID = (phy_reg & 0xffff) << 16;
-
- /* Grab the bits from PHYIR2, and put them in the lower half */
- phy_reg = phy_read(mii_info, MII_PHYSID2);
- phy_ID |= (phy_reg & 0xffff);
-
- /* loop through all the known PHY types, and find one that */
- /* matches the ID we read from the PHY. */
- for (i = 0; phy_info[i]; i++)
- if (phy_info[i]->phy_id ==
- (phy_ID & phy_info[i]->phy_id_mask)) {
- theInfo = phy_info[i];
- break;
- }
-
- /* This shouldn't happen, as we have generic PHY support */
- if (theInfo == NULL) {
- printk("%s: PHY id %x is not supported!\n", dev->name, phy_ID);
- return NULL;
- } else {
- printk("%s: PHY is %s (%x)\n", dev->name, theInfo->name,
- phy_ID);
- }
-
- return theInfo;
}
diff -Nru a/drivers/net/gianfar_phy.h b/drivers/net/gianfar_phy.h
--- a/drivers/net/gianfar_phy.h 2004-10-29 19:58:14 -05:00
+++ b/drivers/net/gianfar_phy.h 2004-10-29 19:58:14 -05:00
@@ -19,195 +19,13 @@
#ifndef __GIANFAR_PHY_H
#define __GIANFAR_PHY_H
-#define MII_end ((u32)-2)
-#define MII_read ((u32)-1)
-
#define MIIMIND_BUSY 0x00000001
#define MIIMIND_NOTVALID 0x00000004
#define GFAR_AN_TIMEOUT 2000
-/* 1000BT control (Marvell & BCM54xx at least) */
-#define MII_1000BASETCONTROL 0x09
-#define MII_1000BASETCONTROL_FULLDUPLEXCAP 0x0200
-#define MII_1000BASETCONTROL_HALFDUPLEXCAP 0x0100
-
-/* Cicada Extended Control Register 1 */
-#define MII_CIS8201_EXT_CON1 0x17
-#define MII_CIS8201_EXTCON1_INIT 0x0000
-
-/* Cicada Interrupt Mask Register */
-#define MII_CIS8201_IMASK 0x19
-#define MII_CIS8201_IMASK_IEN 0x8000
-#define MII_CIS8201_IMASK_SPEED 0x4000
-#define MII_CIS8201_IMASK_LINK 0x2000
-#define MII_CIS8201_IMASK_DUPLEX 0x1000
-#define MII_CIS8201_IMASK_MASK 0xf000
-
-/* Cicada Interrupt Status Register */
-#define MII_CIS8201_ISTAT 0x1a
-#define MII_CIS8201_ISTAT_STATUS 0x8000
-#define MII_CIS8201_ISTAT_SPEED 0x4000
-#define MII_CIS8201_ISTAT_LINK 0x2000
-#define MII_CIS8201_ISTAT_DUPLEX 0x1000
-
-/* Cicada Auxiliary Control/Status Register */
-#define MII_CIS8201_AUX_CONSTAT 0x1c
-#define MII_CIS8201_AUXCONSTAT_INIT 0x0004
-#define MII_CIS8201_AUXCONSTAT_DUPLEX 0x0020
-#define MII_CIS8201_AUXCONSTAT_SPEED 0x0018
-#define MII_CIS8201_AUXCONSTAT_GBIT 0x0010
-#define MII_CIS8201_AUXCONSTAT_100 0x0008
-
-/* 88E1011 PHY Status Register */
-#define MII_M1011_PHY_SPEC_STATUS 0x11
-#define MII_M1011_PHY_SPEC_STATUS_1000 0x8000
-#define MII_M1011_PHY_SPEC_STATUS_100 0x4000
-#define MII_M1011_PHY_SPEC_STATUS_SPD_MASK 0xc000
-#define MII_M1011_PHY_SPEC_STATUS_FULLDUPLEX 0x2000
-#define MII_M1011_PHY_SPEC_STATUS_RESOLVED 0x0800
-#define MII_M1011_PHY_SPEC_STATUS_LINK 0x0400
-
-#define MII_M1011_IEVENT 0x13
-#define MII_M1011_IEVENT_CLEAR 0x0000
-
-#define MII_M1011_IMASK 0x12
-#define MII_M1011_IMASK_INIT 0x6400
-#define MII_M1011_IMASK_CLEAR 0x0000
-
-#define MII_DM9161_SCR 0x10
-#define MII_DM9161_SCR_INIT 0x0610
-
-/* DM9161 Specified Configuration and Status Register */
-#define MII_DM9161_SCSR 0x11
-#define MII_DM9161_SCSR_100F 0x8000
-#define MII_DM9161_SCSR_100H 0x4000
-#define MII_DM9161_SCSR_10F 0x2000
-#define MII_DM9161_SCSR_10H 0x1000
-
-/* DM9161 Interrupt Register */
-#define MII_DM9161_INTR 0x15
-#define MII_DM9161_INTR_PEND 0x8000
-#define MII_DM9161_INTR_DPLX_MASK 0x0800
-#define MII_DM9161_INTR_SPD_MASK 0x0400
-#define MII_DM9161_INTR_LINK_MASK 0x0200
-#define MII_DM9161_INTR_MASK 0x0100
-#define MII_DM9161_INTR_DPLX_CHANGE 0x0010
-#define MII_DM9161_INTR_SPD_CHANGE 0x0008
-#define MII_DM9161_INTR_LINK_CHANGE 0x0004
-#define MII_DM9161_INTR_INIT 0x0000
-#define MII_DM9161_INTR_STOP \
-(MII_DM9161_INTR_DPLX_MASK | MII_DM9161_INTR_SPD_MASK \
- | MII_DM9161_INTR_LINK_MASK | MII_DM9161_INTR_MASK)
-
-/* DM9161 10BT Configuration/Status */
-#define MII_DM9161_10BTCSR 0x12
-#define MII_DM9161_10BTCSR_INIT 0x7800
-
-#define MII_BASIC_FEATURES (SUPPORTED_10baseT_Half | \
- SUPPORTED_10baseT_Full | \
- SUPPORTED_100baseT_Half | \
- SUPPORTED_100baseT_Full | \
- SUPPORTED_Autoneg | \
- SUPPORTED_TP | \
- SUPPORTED_MII)
-
-#define MII_GBIT_FEATURES (MII_BASIC_FEATURES | \
- SUPPORTED_1000baseT_Half | \
- SUPPORTED_1000baseT_Full)
-
#define MII_READ_COMMAND 0x00000001
-#define MII_INTERRUPT_DISABLED 0x0
-#define MII_INTERRUPT_ENABLED 0x1
-/* Taken from mii_if_info and sungem_phy.h */
-struct gfar_mii_info {
- /* Information about the PHY type */
- /* And management functions */
- struct phy_info *phyinfo;
-
- /* forced speed & duplex (no autoneg)
- * partner speed & duplex & pause (autoneg)
- */
- int speed;
- int duplex;
- int pause;
-
- /* The most recently read link state */
- int link;
-
- /* Enabled Interrupts */
- u32 interrupts;
-
- u32 advertising;
- int autoneg;
- int mii_id;
-
- /* private data pointer */
- /* For use by PHYs to maintain extra state */
- void *priv;
-
- /* Provided by host chip */
- struct net_device *dev;
-
- /* A lock to ensure that only one thing can read/write
- * the MDIO bus at a time */
- spinlock_t mdio_lock;
-
- /* Provided by ethernet driver */
- int (*mdio_read) (struct net_device *dev, int mii_id, int reg);
- void (*mdio_write) (struct net_device *dev, int mii_id, int reg, int val);
-};
-
-/* struct phy_info: a structure which defines attributes for a PHY
- *
- * id will contain a number which represents the PHY. During
- * startup, the driver will poll the PHY to find out what its
- * UID--as defined by registers 2 and 3--is. The 32-bit result
- * gotten from the PHY will be ANDed with phy_id_mask to
- * discard any bits which may change based on revision numbers
- * unimportant to functionality
- *
- * There are 6 commands which take a gfar_mii_info structure.
- * Each PHY must declare config_aneg, and read_status.
- */
-struct phy_info {
- u32 phy_id;
- char *name;
- unsigned int phy_id_mask;
- u32 features;
-
- /* Called to initialize the PHY */
- int (*init)(struct gfar_mii_info *mii_info);
-
- /* Called to suspend the PHY for power */
- int (*suspend)(struct gfar_mii_info *mii_info);
-
- /* Reconfigures autonegotiation (or disables it) */
- int (*config_aneg)(struct gfar_mii_info *mii_info);
-
- /* Determines the negotiated speed and duplex */
- int (*read_status)(struct gfar_mii_info *mii_info);
-
- /* Clears any pending interrupts */
- int (*ack_interrupt)(struct gfar_mii_info *mii_info);
-
- /* Enables or disables interrupts */
- int (*config_intr)(struct gfar_mii_info *mii_info);
-
- /* Clears up any memory if needed */
- void (*close)(struct gfar_mii_info *mii_info);
-};
-
-struct phy_info *get_phy_info(struct gfar_mii_info *mii_info);
-int read_phy_reg(struct net_device *dev, int mii_id, int regnum);
-void write_phy_reg(struct net_device *dev, int mii_id, int regnum, int value);
-void mii_clear_phy_interrupt(struct gfar_mii_info *mii_info);
-void mii_configure_phy_interrupt(struct gfar_mii_info *mii_info, u32 interrupts);
-
-struct dm9161_private {
- struct timer_list timer;
- int resetdone;
-};
-
+int gfar_read_phy_reg(struct net_device *dev, int mii_id, int regnum);
+void gfar_write_phy_reg(struct net_device *dev, int mii_id, int regnum, int value);
#endif /* GIANFAR_PHY_H */
diff -Nru a/drivers/net/phy.c b/drivers/net/phy.c
--- /dev/null Wed Dec 31 16:00:00 196900
+++ b/drivers/net/phy.c 2004-10-29 19:58:15 -05:00
@@ -0,0 +1,890 @@
+/*
+ * drivers/net/phy.c
+ *
+ * Framework and drivers for configuring and reading different PHYs
+ * Based on code in sungem_phy.c and gianfar_phy.c
+ *
+ * Author: Andy Fleming
+ *
+ * Copyright (c) 2004 Freescale Semiconductor, Inc.
+ *
+ * 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 of the License, or (at your
+ * option) any later version.
+ *
+ */
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/mii.h>
+#include <linux/ethtool.h>
+#include <linux/phy.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+
+u16 phy_read(struct phy_mii_info *mii_info, u16 regnum);
+void phy_write(struct phy_mii_info *mii_info, u16 regnum, u16 val);
+static void genmii_config_advert(struct phy_mii_info *mii_info);
+static void genmii_setup_forced(struct phy_mii_info *mii_info);
+static void genmii_restart_aneg(struct phy_mii_info *mii_info);
+static int genmii_config_aneg(struct phy_mii_info *mii_info);
+static int gbit_config_aneg(struct phy_mii_info *mii_info);
+static int genmii_update_link(struct phy_mii_info *mii_info);
+static int genmii_read_status(struct phy_mii_info *mii_info);
+
+static struct phy_info *phy_info[];
+/* Use the PHY ID registers to determine what type of PHY is
+ * attached to device dev. return a struct phy_info structure
+ * describing that PHY
+ */
+struct phy_info * get_phy_info(struct phy_mii_info *mii_info)
+{
+ u16 phy_reg;
+ u32 phy_ID;
+ int i;
+ struct phy_info *theInfo = NULL;
+ struct net_device *dev = mii_info->dev;
+
+ /* Grab the bits from PHYIR1, and put them
+ * in the upper half */
+ phy_reg = phy_read(mii_info, MII_PHYSID1);
+ phy_ID = (phy_reg & 0xffff) << 16;
+
+ /* Grab the bits from PHYIR2, and put them in the lower half */
+ phy_reg = phy_read(mii_info, MII_PHYSID2);
+ phy_ID |= (phy_reg & 0xffff);
+
+ /* loop through all the known PHY types, and find
+ * one that matches the ID we read from the PHY. */
+ for (i = 0; phy_info[i]; i++)
+ if (phy_info[i]->phy_id ==
+ (phy_ID & phy_info[i]->phy_id_mask)) {
+ theInfo = phy_info[i];
+ break;
+ }
+
+ /* This shouldn't happen, as we have generic PHY support */
+ if (theInfo == NULL) {
+ printk("%s: PHY id %x is not supported!\n", dev->name,
+ phy_ID);
+ return NULL;
+ } else {
+ printk("%s: PHY is %s (%x)\n", dev->name,
+ theInfo->name, phy_ID);
+ }
+
+ return theInfo;
+}
+
+u16 phy_read(struct phy_mii_info *mii_info, u16 regnum)
+{
+ u16 retval;
+ unsigned long flags;
+
+ spin_lock_irqsave(&mii_info->mdio_lock, flags);
+ retval = mii_info->mdio_read(mii_info->dev, mii_info->mii_id, regnum);
+ spin_unlock_irqrestore(&mii_info->mdio_lock, flags);
+
+ return retval;
+}
+
+void phy_write(struct phy_mii_info *mii_info, u16 regnum, u16 val)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&mii_info->mdio_lock, flags);
+ mii_info->mdio_write(mii_info->dev, mii_info->mii_id, regnum, val);
+ spin_unlock_irqrestore(&mii_info->mdio_lock, flags);
+}
+
+
+void mii_clear_phy_interrupt(struct phy_mii_info *mii_info)
+{
+ if(mii_info->phyinfo->ack_interrupt)
+ mii_info->phyinfo->ack_interrupt(mii_info);
+}
+
+
+void mii_configure_phy_interrupt(struct phy_mii_info *mii_info,
+ u32 interrupts)
+{
+ mii_info->interrupts = interrupts;
+ if(mii_info->phyinfo->config_intr)
+ mii_info->phyinfo->config_intr(mii_info);
+}
+
+/* Writes MII_ADVERTISE with the appropriate values, after
+ * sanitizing advertise to make sure only supported features
+ * are advertised
+ */
+void genmii_config_advert(struct phy_mii_info *mii_info)
+{
+ u32 advertise;
+ u16 adv;
+
+ /* Only allow advertising what
+ * this PHY supports */
+ mii_info->advertising &= mii_info->phyinfo->features;
+ advertise = mii_info->advertising;
+
+ /* Setup standard advertisement */
+ adv = phy_read(mii_info, MII_ADVERTISE);
+ adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4);
+ if (advertise & ADVERTISED_10baseT_Half)
+ adv |= ADVERTISE_10HALF;
+ if (advertise & ADVERTISED_10baseT_Full)
+ adv |= ADVERTISE_10FULL;
+ if (advertise & ADVERTISED_100baseT_Half)
+ adv |= ADVERTISE_100HALF;
+ if (advertise & ADVERTISED_100baseT_Full)
+ adv |= ADVERTISE_100FULL;
+ phy_write(mii_info, MII_ADVERTISE, adv);
+}
+
+
+
+/* Configures MII_BMCR to force speed/duplex to the
+ * values in mii_info */
+static void genmii_setup_forced(struct phy_mii_info *mii_info)
+{
+ u16 ctrl;
+ u32 features = mii_info->phyinfo->features;
+
+ ctrl = phy_read(mii_info, MII_BMCR);
+
+ ctrl &= ~(BMCR_FULLDPLX|BMCR_SPEED100|BMCR_SPEED1000|BMCR_ANENABLE);
+ ctrl |= BMCR_RESET;
+
+ switch(mii_info->speed) {
+ case SPEED_1000:
+ if(features & (SUPPORTED_1000baseT_Half
+ | SUPPORTED_1000baseT_Full)) {
+ ctrl |= BMCR_SPEED1000;
+ break;
+ }
+ mii_info->speed = SPEED_100;
+ case SPEED_100:
+ if (features & (SUPPORTED_100baseT_Half
+ | SUPPORTED_100baseT_Full)) {
+ ctrl |= BMCR_SPEED100;
+ break;
+ }
+ mii_info->speed = SPEED_10;
+ case SPEED_10:
+ if (features & (SUPPORTED_10baseT_Half
+ | SUPPORTED_10baseT_Full))
+ break;
+ default: /* Unsupported speed! */
+ printk(KERN_ERR "%s: Bad speed!\n",
+ mii_info->dev->name);
+ break;
+ }
+
+ phy_write(mii_info, MII_BMCR, ctrl);
+}
+
+
+/* Enable and Restart Autonegotiation */
+static void genmii_restart_aneg(struct phy_mii_info *mii_info)
+{
+ u16 ctl;
+
+ ctl = phy_read(mii_info, MII_BMCR);
+ ctl |= (BMCR_ANENABLE | BMCR_ANRESTART);
+ phy_write(mii_info, MII_BMCR, ctl);
+}
+
+
+static int gbit_config_aneg(struct phy_mii_info *mii_info)
+{
+ u16 adv;
+ u32 advertise;
+
+ if(mii_info->autoneg) {
+ /* Configure the ADVERTISE register */
+ genmii_config_advert(mii_info);
+ advertise = mii_info->advertising;
+
+ adv = phy_read(mii_info, MII_1000BASETCONTROL);
+ adv &= ~(MII_1000BASETCONTROL_FULLDUPLEXCAP |
+ MII_1000BASETCONTROL_HALFDUPLEXCAP);
+ if (advertise & SUPPORTED_1000baseT_Half)
+ adv |= MII_1000BASETCONTROL_HALFDUPLEXCAP;
+ if (advertise & SUPPORTED_1000baseT_Full)
+ adv |= MII_1000BASETCONTROL_FULLDUPLEXCAP;
+ phy_write(mii_info, MII_1000BASETCONTROL, adv);
+
+ /* Start/Restart aneg */
+ genmii_restart_aneg(mii_info);
+ } else
+ genmii_setup_forced(mii_info);
+
+ return 0;
+}
+
+
+static int genmii_config_aneg(struct phy_mii_info *mii_info)
+{
+ if (mii_info->autoneg) {
+ genmii_config_advert(mii_info);
+ genmii_restart_aneg(mii_info);
+ } else
+ genmii_setup_forced(mii_info);
+
+ return 0;
+}
+
+
+static int genmii_update_link(struct phy_mii_info *mii_info)
+{
+ u16 status;
+
+ /* Do a fake read */
+ phy_read(mii_info, MII_BMSR);
+
+ /* Read link and autonegotiation status */
+ status = phy_read(mii_info, MII_BMSR);
+ if ((status & BMSR_LSTATUS) == 0)
+ mii_info->link = 0;
+ else
+ mii_info->link = 1;
+
+ /* If we are autonegotiating, and not done,
+ * return an error */
+ if (mii_info->autoneg && !(status & BMSR_ANEGCOMPLETE))
+ return -EAGAIN;
+
+ return 0;
+}
+
+static int genmii_read_status(struct phy_mii_info *mii_info)
+{
+ u16 status;
+ int err;
+
+ /* Update the link, but return if there
+ * was an error */
+ err = genmii_update_link(mii_info);
+ if (err)
+ return err;
+
+ if (mii_info->autoneg) {
+ status = phy_read(mii_info, MII_LPA);
+
+ if (status & (LPA_10FULL | LPA_100FULL))
+ mii_info->duplex = DUPLEX_FULL;
+ else
+ mii_info->duplex = DUPLEX_HALF;
+ if (status & (LPA_100FULL | LPA_100HALF))
+ mii_info->speed = SPEED_100;
+ else
+ mii_info->speed = SPEED_10;
+ mii_info->pause = 0;
+ }
+ /* On non-aneg, we assume what we put in BMCR is the speed,
+ * though magic-aneg shouldn't prevent this case from occurring
+ */
+
+ return 0;
+}
+
+static int marvell_read_status(struct phy_mii_info *mii_info)
+{
+ u16 status;
+ int err;
+
+ /* Update the link, but return if there
+ * was an error */
+ err = genmii_update_link(mii_info);
+ if (err)
+ return err;
+
+ /* If the link is up, read the speed and duplex */
+ /* If we aren't autonegotiating, assume speeds
+ * are as set */
+ if (mii_info->autoneg && mii_info->link) {
+ int speed;
+ status = phy_read(mii_info, MII_M1011_PHY_SPEC_STATUS);
+
+#if 0
+ /* If speed and duplex aren't resolved,
+ * return an error. Isn't this handled
+ * by checking aneg?
+ */
+ if ((status & MII_M1011_PHY_SPEC_STATUS_RESOLVED) == 0)
+ return -EAGAIN;
+#endif
+
+ /* Get the duplexity */
+ if (status & MII_M1011_PHY_SPEC_STATUS_FULLDUPLEX)
+ mii_info->duplex = DUPLEX_FULL;
+ else
+ mii_info->duplex = DUPLEX_HALF;
+
+ /* Get the speed */
+ speed = status & MII_M1011_PHY_SPEC_STATUS_SPD_MASK;
+ switch(speed) {
+ case MII_M1011_PHY_SPEC_STATUS_1000:
+ mii_info->speed = SPEED_1000;
+ break;
+ case MII_M1011_PHY_SPEC_STATUS_100:
+ mii_info->speed = SPEED_100;
+ break;
+ default:
+ mii_info->speed = SPEED_10;
+ break;
+ }
+ mii_info->pause = 0;
+ }
+
+ return 0;
+}
+
+static int marvell_ack_interrupt(struct phy_mii_info *mii_info)
+{
+ /* Clear the interrupts by reading the reg */
+ phy_read(mii_info, MII_M1011_IEVENT);
+
+ return 0;
+}
+
+static int marvell_config_intr(struct phy_mii_info *mii_info)
+{
+ if(mii_info->interrupts == MII_INTERRUPT_ENABLED)
+ phy_write(mii_info, MII_M1011_IMASK, MII_M1011_IMASK_INIT);
+ else
+ phy_write(mii_info, MII_M1011_IMASK, MII_M1011_IMASK_CLEAR);
+
+ return 0;
+}
+
+static int marvell_config_aneg(struct phy_mii_info *mii_info)
+{
+ /* The Marvell PHY has an errata which requires
+ * that certain registers get written in order
+ * to restart autonegotiation */
+ phy_write(mii_info, MII_BMCR, BMCR_RESET);
+
+ phy_write(mii_info, 0x1d, 0x1f);
+ phy_write(mii_info, 0x1e, 0x200c);
+ phy_write(mii_info, 0x1d, 0x5);
+ phy_write(mii_info, 0x1e, 0);
+ phy_write(mii_info, 0x1e, 0x100);
+
+ gbit_config_aneg(mii_info);
+
+ return 0;
+}
+
+
+static int cis820x_read_status(struct phy_mii_info *mii_info)
+{
+ u16 status;
+ int err;
+
+ /* Update the link, but return if there
+ * was an error */
+ err = genmii_update_link(mii_info);
+ if (err)
+ return err;
+
+ /* If the link is up, read the speed and duplex */
+ /* If we aren't autonegotiating, assume speeds
+ * are as set */
+ if (mii_info->autoneg && mii_info->link) {
+ int speed;
+
+ status = phy_read(mii_info, MII_CIS8201_AUX_CONSTAT);
+ if (status & MII_CIS8201_AUXCONSTAT_DUPLEX)
+ mii_info->duplex = DUPLEX_FULL;
+ else
+ mii_info->duplex = DUPLEX_HALF;
+
+ speed = status & MII_CIS8201_AUXCONSTAT_SPEED;
+
+ switch (speed) {
+ case MII_CIS8201_AUXCONSTAT_GBIT:
+ mii_info->speed = SPEED_1000;
+ break;
+ case MII_CIS8201_AUXCONSTAT_100:
+ mii_info->speed = SPEED_100;
+ break;
+ default:
+ mii_info->speed = SPEED_10;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int cis820x_init(struct phy_mii_info *mii_info)
+{
+ phy_write(mii_info, MII_CIS8201_AUX_CONSTAT,
+ MII_CIS8201_AUXCONSTAT_INIT);
+ phy_write(mii_info, MII_CIS8201_EXT_CON1,
+ MII_CIS8201_EXTCON1_INIT);
+
+ return 0;
+}
+
+static int cis820x_ack_interrupt(struct phy_mii_info *mii_info)
+{
+ phy_read(mii_info, MII_CIS8201_ISTAT);
+
+ return 0;
+}
+
+static int cis820x_config_intr(struct phy_mii_info *mii_info)
+{
+ if(mii_info->interrupts == MII_INTERRUPT_ENABLED)
+ phy_write(mii_info, MII_CIS8201_IMASK, MII_CIS8201_IMASK_MASK);
+ else
+ phy_write(mii_info, MII_CIS8201_IMASK, 0);
+
+ return 0;
+}
+
+#define DM9161_DELAY 10
+
+static int dm9161_read_status(struct phy_mii_info *mii_info)
+{
+ u16 status;
+ int err;
+
+ /* Update the link, but return if there
+ * was an error */
+ err = genmii_update_link(mii_info);
+ if (err)
+ return err;
+
+ /* If the link is up, read the speed and duplex */
+ /* If we aren't autonegotiating, assume speeds
+ * are as set */
+ if (mii_info->autoneg && mii_info->link) {
+ status = phy_read(mii_info, MII_DM9161_SCSR);
+ if (status & (MII_DM9161_SCSR_100F | MII_DM9161_SCSR_100H))
+ mii_info->speed = SPEED_100;
+ else
+ mii_info->speed = SPEED_10;
+
+ if (status & (MII_DM9161_SCSR_100F | MII_DM9161_SCSR_10F))
+ mii_info->duplex = DUPLEX_FULL;
+ else
+ mii_info->duplex = DUPLEX_HALF;
+ }
+
+ return 0;
+}
+
+
+static int dm9161_config_aneg(struct phy_mii_info *mii_info)
+{
+ struct dm9161_private *priv = mii_info->priv;
+
+ if(0 == priv->resetdone)
+ return -EAGAIN;
+
+ return 0;
+}
+
+static void dm9161_timer(unsigned long data)
+{
+ struct phy_mii_info *mii_info = (struct phy_mii_info *)data;
+ struct dm9161_private *priv = mii_info->priv;
+ u16 status = phy_read(mii_info, MII_BMSR);
+
+ if (status & BMSR_ANEGCOMPLETE) {
+ priv->resetdone = 1;
+ } else
+ mod_timer(&priv->timer, jiffies + DM9161_DELAY * HZ);
+}
+
+static int dm9161_init(struct phy_mii_info *mii_info)
+{
+ struct dm9161_private *priv;
+
+ /* Allocate the private data structure */
+ priv = kmalloc(sizeof(struct dm9161_private), GFP_KERNEL);
+
+ if (NULL == priv)
+ return -ENOMEM;
+
+ mii_info->priv = priv;
+
+ /* Reset is not done yet */
+ priv->resetdone = 0;
+
+ /* Isolate the PHY */
+ phy_write(mii_info, MII_BMCR, BMCR_ISOLATE);
+
+ /* Do not bypass the scrambler/descrambler */
+ phy_write(mii_info, MII_DM9161_SCR, MII_DM9161_SCR_INIT);
+
+ /* Clear 10BTCSR to default */
+ phy_write(mii_info, MII_DM9161_10BTCSR, MII_DM9161_10BTCSR_INIT);
+
+ /* Reconnect the PHY, and enable Autonegotiation */
+ phy_write(mii_info, MII_BMCR, BMCR_ANENABLE);
+
+ /* Start a timer for DM9161_DELAY seconds to wait
+ * for the PHY to be ready */
+ init_timer(&priv->timer);
+ priv->timer.function = &dm9161_timer;
+ priv->timer.data = (unsigned long) mii_info;
+ mod_timer(&priv->timer, jiffies + DM9161_DELAY * HZ);
+
+ return 0;
+}
+
+static void dm9161_close(struct phy_mii_info *mii_info)
+{
+ struct dm9161_private *priv = mii_info->priv;
+
+ del_timer_sync(&priv->timer);
+ kfree(priv);
+}
+
+static int dm9161_ack_interrupt(struct phy_mii_info *mii_info)
+{
+ phy_read(mii_info, MII_DM9161_INTR);
+
+ return 0;
+}
+
+static int lxt970_read_status(struct phy_mii_info *mii_info)
+{
+ u16 status;
+ int err;
+
+ /* Update the link, but return if there
+ * was an error */
+ err = genmii_update_link(mii_info);
+ if (err)
+ return err;
+
+ /* If the link is up, read the speed and duplex */
+ /* If we aren't autonegotiating, assume speeds
+ * are as set */
+ if (mii_info->autoneg && mii_info->link) {
+ status = phy_read(mii_info, MII_LXT970_CSR);
+ if (status & MII_LXT970_CSR_DUPLEX)
+ mii_info->duplex = DUPLEX_FULL;
+ else
+ mii_info->duplex = DUPLEX_HALF;
+
+ if (status & MII_LXT970_CSR_SPEED)
+ mii_info->speed = SPEED_100;
+ else
+ mii_info->speed = SPEED_10;
+ }
+
+ return 0;
+}
+
+static int lxt970_ack_interrupt(struct phy_mii_info *mii_info)
+{
+ phy_read(mii_info, MII_BMSR);
+ phy_read(mii_info, MII_LXT970_ISR);
+
+ return 0;
+}
+
+static int lxt970_config_intr(struct phy_mii_info *mii_info)
+{
+ if(mii_info->interrupts == MII_INTERRUPT_ENABLED)
+ phy_write(mii_info, MII_LXT970_IER, MII_LXT970_IER_IEN);
+ else
+ phy_write(mii_info, MII_LXT970_IER, 0);
+
+ return 0;
+}
+
+static int lxt970_init(struct phy_mii_info *mii_info)
+{
+ phy_write(mii_info, MII_LXT970_CONFIG, 0);
+
+ return 0;
+}
+
+
+static int lxt971_read_status(struct phy_mii_info *mii_info)
+{
+ u16 status;
+ int err;
+
+ /* Update the link, but return if there
+ * was an error */
+ err = genmii_update_link(mii_info);
+ if (err)
+ return err;
+
+ /* If the link is up, read the speed and duplex */
+ /* If we aren't autonegotiating, assume speeds
+ * are as set */
+ if (mii_info->autoneg && mii_info->link) {
+ status = phy_read(mii_info, MII_LXT971_SR2);
+ if (status & MII_LXT971_SR2_DUPLEX)
+ mii_info->duplex = DUPLEX_FULL;
+ else
+ mii_info->duplex= DUPLEX_HALF;
+
+ if (status & MII_LXT971_SR2_SPEED)
+ mii_info->speed = SPEED_100;
+ else
+ mii_info->speed = SPEED_10;
+ }
+
+ return 0;
+}
+
+static int lxt971_ack_interrupt(struct phy_mii_info *mii_info)
+{
+ phy_read(mii_info, MII_LXT971_ISR);
+
+ return 0;
+}
+
+static int lxt971_config_intr(struct phy_mii_info *mii_info)
+{
+ if(mii_info->interrupts == MII_INTERRUPT_ENABLED)
+ phy_write(mii_info, MII_LXT971_IER, MII_LXT971_IER_IEN);
+ else
+ phy_write(mii_info, MII_LXT971_IER, 0);
+
+ return 0;
+}
+
+static int qs6612_read_status(struct phy_mii_info *mii_info)
+{
+ u16 status;
+ int err;
+
+ /* Update the link, but return if there
+ * was an error */
+ err = genmii_update_link(mii_info);
+ if (err)
+ return err;
+
+ /* If the link is up, read the speed and duplex */
+ /* If we aren't autonegotiating, assume speeds
+ * are as set */
+ if (mii_info->autoneg && mii_info->link) {
+ status = phy_read(mii_info, MII_QS6612_PCR);
+ switch((status >> 2) & 7) {
+ case 1: mii_info->speed = 10;
+ mii_info->duplex = 0;
+ break;
+ case 2: mii_info->speed = 100;
+ mii_info->duplex = 0;
+ break;
+ case 5: mii_info->speed = 10;
+ mii_info->duplex = 1;
+ break;
+ case 6: mii_info->speed = 100;
+ mii_info->duplex = 1;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+int qs6612_init(struct phy_mii_info *mii_info)
+{
+ /* The PHY powers up isolated on the RPX,
+ * so send a command to allow operation.
+ * XXX - I don't have the docs, so for
+ * now this constant is "magic" -Andy
+ */
+ phy_write(mii_info, MII_QS6612_PCR, 0x0dc0);
+
+ return 0;
+}
+
+int qs6612_ack_interrupt(struct phy_mii_info *mii_info)
+{
+ phy_read(mii_info, MII_QS6612_ISR);
+ phy_read(mii_info, MII_BMSR);
+ phy_read(mii_info, MII_EXPANSION);
+
+ return 0;
+}
+
+int qs6612_config_intr(struct phy_mii_info *mii_info)
+{
+ if(mii_info->interrupts == MII_INTERRUPT_ENABLED)
+ phy_write(mii_info, MII_QS6612_IMR,
+ MII_QS6612_IMR_INIT);
+ else
+ phy_write(mii_info, MII_QS6612_IMR, 0);
+
+ return 0;
+
+}
+
+#if 0
+static struct phy_info phy_info_qs6612 = {
+ 0x00181440,
+ "QS6612",
+ 0,
+
+ (const struct phy_cmd []) { /* config */
+ { MII_QS6612_PCR, 0x0dc0, NULL },
+ { miim_end, }
+ },
+ (const struct phy_cmd []) { /* startup - enable interrupts */
+ /* parse cr and anar to get some info */
+ { MII_QS6612_IMR, 0x003a, NULL },
+ { MII_REG_CR, 0x1200, NULL }, /* autonegotiate */
+ { miim_end, }
+ },
+ (const struct phy_cmd []) { /* ack_int */
+ /* we need to read ISR, SR and ANER to acknowledge */
+ { MII_QS6612_ISR, miim_read, NULL },
+ { MII_REG_SR, miim_read, mii_parse_sr },
+ { MII_REG_ANER, miim_read, NULL },
+ { miim_end, }
+ },
+ (const struct phy_cmd []) { /* handle_int */
+ /* read pcr to get info */
+ { MII_REG_SR, miim_read, mii_parse_sr },
+ { MII_QS6612_PCR, miim_read, mii_parse_qs6612_pcr },
+ { miim_end, }
+ },
+ (const struct phy_cmd []) { /* shutdown - disable interrupts */
+ { MII_QS6612_IMR, 0x0000, NULL },
+ { miim_end, }
+ },
+};
+#endif
+
+int dm9161_config_intr(struct phy_mii_info *mii_info)
+{
+ u16 temp;
+
+ temp = phy_read(mii_info, MII_DM9161_INTR);
+
+ if(mii_info->interrupts == MII_INTERRUPT_ENABLED)
+ temp &= ~(MII_DM9161_INTR_STOP);
+ else
+ temp |= MII_DM9161_INTR_STOP;
+
+ phy_write(mii_info, MII_DM9161_INTR, temp);
+
+ return 0;
+}
+
+static struct phy_info phy_info_marvell = {
+ .phy_id = 0x01410c00,
+ .phy_id_mask = 0xffffff00,
+ .name = "Marvell 88E1101",
+ .features = MII_GBIT_FEATURES,
+ .config_aneg = &marvell_config_aneg,
+ .read_status = &marvell_read_status,
+ .ack_interrupt = &marvell_ack_interrupt,
+ .config_intr = &marvell_config_intr,
+};
+
+/* Cicada 820x */
+static struct phy_info phy_info_cis820x = {
+ 0x000fc440,
+ "Cicada Cis8204",
+ 0x000fffc0,
+ .features = MII_GBIT_FEATURES,
+ .init = &cis820x_init,
+ .config_aneg = &gbit_config_aneg,
+ .read_status = &cis820x_read_status,
+ .ack_interrupt = &cis820x_ack_interrupt,
+ .config_intr = &cis820x_config_intr,
+};
+
+static struct phy_info phy_info_lxt970 = {
+ .phy_id = 0x07810000,
+ .name = "LXT970",
+ .phy_id_mask = 0x0fffffff,
+ .init = lxt970_init,
+ .config_aneg = genmii_config_aneg,
+ .read_status = lxt970_read_status,
+ .ack_interrupt = lxt970_ack_interrupt,
+ .config_intr = lxt970_config_intr,
+};
+
+static struct phy_info phy_info_lxt971 = {
+ .phy_id = 0x0001378e,
+ .name = "LXT971",
+ .phy_id_mask = 0x0fffffff,
+ .config_aneg = genmii_config_aneg,
+ .read_status = lxt971_read_status,
+ .ack_interrupt = lxt971_ack_interrupt,
+ .config_intr = lxt971_config_intr,
+};
+
+static struct phy_info phy_info_dm9161 = {
+ .phy_id = 0x0181b880,
+ .name = "Davicom DM9161E",
+ .phy_id_mask = 0x0ffffff0,
+ .init = dm9161_init,
+ .config_aneg = dm9161_config_aneg,
+ .read_status = dm9161_read_status,
+ .close = dm9161_close,
+};
+
+static struct phy_info phy_info_dm9131 = {
+ .phy_id = 0x00181b80,
+ .name = "Davicom DM9131",
+ .phy_id_mask = 0x0ffffff0,
+ .config_aneg = genmii_config_aneg,
+ .read_status = dm9161_read_status,
+ .ack_interrupt = dm9161_ack_interrupt,
+ .config_intr = dm9161_config_intr,
+};
+
+static struct phy_info phy_info_qs6612 = {
+ .phy_id = 0x00181440,
+ .name = "QS6612",
+ .phy_id_mask = 0xfffffff0,
+ .init = qs6612_init,
+ .config_aneg = genmii_config_aneg,
+ .read_status = qs6612_read_status,
+ .ack_interrupt = qs6612_ack_interrupt,
+ .config_intr = qs6612_config_intr,
+};
+
+static struct phy_info phy_info_genmii= {
+ .phy_id = 0x00000000,
+ .phy_id_mask = 0x00000000,
+ .name = "Generic MII",
+ .features = MII_BASIC_FEATURES,
+ .config_aneg = genmii_config_aneg,
+ .read_status = genmii_read_status,
+};
+
+static struct phy_info *phy_info[] = {
+ &phy_info_cis820x,
+ &phy_info_marvell,
+ &phy_info_lxt970,
+ &phy_info_lxt971,
+ &phy_info_dm9131,
+ &phy_info_dm9161,
+ &phy_info_qs6612,
+ &phy_info_genmii,
+ NULL
+};
+
diff -Nru a/include/asm-ppc/cputable.h b/include/asm-ppc/cputable.h
--- a/include/asm-ppc/cputable.h 2004-10-29 19:58:14 -05:00
+++ b/include/asm-ppc/cputable.h 2004-10-29 19:58:14 -05:00
@@ -65,7 +65,7 @@
#define CPU_FTR_TAU 0x00000010
#define CPU_FTR_CAN_DOZE 0x00000020
#define CPU_FTR_USE_TB 0x00000040
-#define CPU_FTR_604_PERF_MON 0x00000080
+#define CPU_FTR_CAN_USE_PMON_INTR 0x00000080
#define CPU_FTR_601 0x00000100
#define CPU_FTR_HPTE_TABLE 0x00000200
#define CPU_FTR_CAN_NAP 0x00000400
@@ -77,6 +77,7 @@
#define CPU_FTR_HAS_HIGH_BATS 0x00010000
#define CPU_FTR_NEED_COHERENT 0x00020000
#define CPU_FTR_NO_BTIC 0x00040000
+#define CPU_FTR_FSL_BOOKE_PMON 0x00080000
#ifdef __ASSEMBLY__
diff -Nru a/include/asm-ppc/perfmon.h b/include/asm-ppc/perfmon.h
--- /dev/null Wed Dec 31 16:00:00 196900
+++ b/include/asm-ppc/perfmon.h 2004-10-29 19:58:14 -05:00
@@ -0,0 +1,18 @@
+#ifndef __PERFMON_H
+#define __PERFMON_H
+
+int request_perfmon_irq(void (*handler)(struct pt_regs *));
+void free_perfmon_irq(void);
+
+#ifdef CONFIG_FSL_BOOKE
+void init_pmc_stop(int ctr);
+void set_pmc_event(int ctr, int event);
+void set_pmc_user_kernel(int ctr, int user, int kernel);
+void set_pmc_marked(int ctr, int mark0, int mark1);
+void pmc_start_ctr(int ctr, int enable);
+void pmc_start_ctrs(int enable);
+void pmc_stop_ctrs(void);
+void dump_pmcs(void);
+#endif
+
+#endif /* __PERFMON_H */
diff -Nru a/include/asm-ppc/ptrace.h b/include/asm-ppc/ptrace.h
--- a/include/asm-ppc/ptrace.h 2004-10-29 19:58:14 -05:00
+++ b/include/asm-ppc/ptrace.h 2004-10-29 19:58:14 -05:00
@@ -35,6 +35,10 @@
unsigned long dar; /* Fault registers */
unsigned long dsisr; /* on 4xx/Book-E used for ESR */
unsigned long result; /* Result of a system call */
+
+ /* Address of instruction which caused a performance
+ * monitor interrupt */
+ unsigned long sia;
};
#endif /* __ASSEMBLY__ */
diff -Nru a/include/asm-ppc/reg_booke.h b/include/asm-ppc/reg_booke.h
--- a/include/asm-ppc/reg_booke.h 2004-10-29 19:58:14 -05:00
+++ b/include/asm-ppc/reg_booke.h 2004-10-29 19:58:14 -05:00
@@ -51,6 +51,59 @@
#define mtpmr(rn, v) asm volatile("mtpmr " __stringify(rn) ",%0" : : "r" (v))
#endif /* __ASSEMBLY__ */
+/* Freescale Book E Performance Monitor APU Registers */
+#define PMRN_PMC0 0x010 /* Performance Monitor Counter 0 */
+#define PMRN_PMC1 0x011 /* Performance Monitor Counter 1 */
+#define PMRN_PMC2 0x012 /* Performance Monitor Counter 1 */
+#define PMRN_PMC3 0x013 /* Performance Monitor Counter 1 */
+#define PMRN_PMLCA0 0x090 /* PM Local Control A0 */
+#define PMRN_PMLCA1 0x091 /* PM Local Control A1 */
+#define PMRN_PMLCA2 0x092 /* PM Local Control A2 */
+#define PMRN_PMLCA3 0x093 /* PM Local Control A3 */
+
+#define PMLCA_FC 0x80000000 /* Freeze Counter */
+#define PMLCA_FCS 0x40000000 /* Freeze in Supervisor */
+#define PMLCA_FCU 0x20000000 /* Freeze in User */
+#define PMLCA_FCM1 0x10000000 /* Freeze when PMM==1 */
+#define PMLCA_FCM0 0x08000000 /* Freeze when PMM==0 */
+#define PMLCA_CE 0x04000000 /* Condition Enable */
+
+#define PMLCA_EVENT_MASK 0x007f0000 /* Event field */
+#define PMLCA_EVENT_SHIFT 16
+
+#define PMRN_PMLCB0 0x110 /* PM Local Control B0 */
+#define PMRN_PMLCB1 0x111 /* PM Local Control B1 */
+#define PMRN_PMLCB2 0x112 /* PM Local Control B2 */
+#define PMRN_PMLCB3 0x113 /* PM Local Control B3 */
+
+#define PMLCB_THRESHMUL_MASK 0x0700 /* Threshhold Multiple Field */
+#define PMLCB_THRESHMUL_SHIFT 8
+
+#define PMLCB_THRESHOLD_MASK 0x003f /* Threshold Field */
+#define PMLCB_THRESHOLD_SHIFT 0
+
+#define PMRN_PMGC0 0x190 /* PM Global Control 0 */
+
+#define PMGC0_FAC 0x80000000 /* Freeze all Counters */
+#define PMGC0_PMIE 0x40000000 /* Interrupt Enable */
+#define PMGC0_FCECE 0x20000000 /* Freeze countes on
+ Enabled Condition or
+ Event */
+
+#define PMRN_UPMC0 0x000 /* User Performance Monitor Counter 0 */
+#define PMRN_UPMC1 0x001 /* User Performance Monitor Counter 1 */
+#define PMRN_UPMC2 0x002 /* User Performance Monitor Counter 1 */
+#define PMRN_UPMC3 0x003 /* User Performance Monitor Counter 1 */
+#define PMRN_UPMLCA0 0x080 /* User PM Local Control A0 */
+#define PMRN_UPMLCA1 0x081 /* User PM Local Control A1 */
+#define PMRN_UPMLCA2 0x082 /* User PM Local Control A2 */
+#define PMRN_UPMLCA3 0x083 /* User PM Local Control A3 */
+#define PMRN_UPMLCB0 0x100 /* User PM Local Control B0 */
+#define PMRN_UPMLCB1 0x101 /* User PM Local Control B1 */
+#define PMRN_UPMLCB2 0x102 /* User PM Local Control B2 */
+#define PMRN_UPMLCB3 0x103 /* User PM Local Control B3 */
+#define PMRN_UPMGC0 0x180 /* User PM Global Control 0 */
+
/* Machine State Register (MSR) Fields */
#define MSR_UCLE (1<<26) /* User-mode cache lock enable */
diff -Nru a/include/linux/phy.h b/include/linux/phy.h
--- /dev/null Wed Dec 31 16:00:00 196900
+++ b/include/linux/phy.h 2004-10-29 19:58:15 -05:00
@@ -0,0 +1,251 @@
+/*
+ * include/linux/phy.h
+ *
+ * Framework and drivers for configuring and reading different PHYs
+ * Based on code in sungem_phy.c and gianfar_phy.c
+ *
+ * Author: Andy Fleming
+ *
+ * Copyright (c) 2004 Freescale Semiconductor, Inc.
+ *
+ * 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 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#ifndef __PHY_H
+#define __PHY_H
+
+/* 1000BT control (Marvell & BCM54xx at least) */
+#define MII_1000BASETCONTROL 0x09
+#define MII_1000BASETCONTROL_FULLDUPLEXCAP 0x0200
+#define MII_1000BASETCONTROL_HALFDUPLEXCAP 0x0100
+
+/* Cicada Extended Control Register 1 */
+#define MII_CIS8201_EXT_CON1 0x17
+#define MII_CIS8201_EXTCON1_INIT 0x0000
+
+/* Cicada Interrupt Mask Register */
+#define MII_CIS8201_IMASK 0x19
+#define MII_CIS8201_IMASK_IEN 0x8000
+#define MII_CIS8201_IMASK_SPEED 0x4000
+#define MII_CIS8201_IMASK_LINK 0x2000
+#define MII_CIS8201_IMASK_DUPLEX 0x1000
+#define MII_CIS8201_IMASK_MASK 0xf000
+
+/* Cicada Interrupt Status Register */
+#define MII_CIS8201_ISTAT 0x1a
+#define MII_CIS8201_ISTAT_STATUS 0x8000
+#define MII_CIS8201_ISTAT_SPEED 0x4000
+#define MII_CIS8201_ISTAT_LINK 0x2000
+#define MII_CIS8201_ISTAT_DUPLEX 0x1000
+
+/* Cicada Auxiliary Control/Status Register */
+#define MII_CIS8201_AUX_CONSTAT 0x1c
+#define MII_CIS8201_AUXCONSTAT_INIT 0x0004
+#define MII_CIS8201_AUXCONSTAT_DUPLEX 0x0020
+#define MII_CIS8201_AUXCONSTAT_SPEED 0x0018
+#define MII_CIS8201_AUXCONSTAT_GBIT 0x0010
+#define MII_CIS8201_AUXCONSTAT_100 0x0008
+
+/* 88E1011 PHY Status Register */
+#define MII_M1011_PHY_SPEC_STATUS 0x11
+#define MII_M1011_PHY_SPEC_STATUS_1000 0x8000
+#define MII_M1011_PHY_SPEC_STATUS_100 0x4000
+#define MII_M1011_PHY_SPEC_STATUS_SPD_MASK 0xc000
+#define MII_M1011_PHY_SPEC_STATUS_FULLDUPLEX 0x2000
+#define MII_M1011_PHY_SPEC_STATUS_RESOLVED 0x0800
+#define MII_M1011_PHY_SPEC_STATUS_LINK 0x0400
+
+#define MII_M1011_IEVENT 0x13
+#define MII_M1011_IEVENT_CLEAR 0x0000
+
+#define MII_M1011_IMASK 0x12
+#define MII_M1011_IMASK_INIT 0x6400
+#define MII_M1011_IMASK_CLEAR 0x0000
+
+#define MII_DM9161_SCR 0x10
+#define MII_DM9161_SCR_INIT 0x0610
+
+/* DM9161 Specified Configuration and Status Register */
+#define MII_DM9161_SCSR 0x11
+#define MII_DM9161_SCSR_100F 0x8000
+#define MII_DM9161_SCSR_100H 0x4000
+#define MII_DM9161_SCSR_10F 0x2000
+#define MII_DM9161_SCSR_10H 0x1000
+
+/* DM9161 Interrupt Register */
+#define MII_DM9161_INTR 0x15
+#define MII_DM9161_INTR_PEND 0x8000
+#define MII_DM9161_INTR_DPLX_MASK 0x0800
+#define MII_DM9161_INTR_SPD_MASK 0x0400
+#define MII_DM9161_INTR_LINK_MASK 0x0200
+#define MII_DM9161_INTR_MASK 0x0100
+#define MII_DM9161_INTR_DPLX_CHANGE 0x0010
+#define MII_DM9161_INTR_SPD_CHANGE 0x0008
+#define MII_DM9161_INTR_LINK_CHANGE 0x0004
+#define MII_DM9161_INTR_INIT 0x0000
+#define MII_DM9161_INTR_STOP \
+(MII_DM9161_INTR_DPLX_MASK | MII_DM9161_INTR_SPD_MASK \
+ | MII_DM9161_INTR_LINK_MASK | MII_DM9161_INTR_MASK)
+
+/* DM9161 10BT Configuration/Status */
+#define MII_DM9161_10BTCSR 0x12
+#define MII_DM9161_10BTCSR_INIT 0x7800
+
+/* The Level one LXT970 is used by many boards */
+
+#define MII_LXT970_MIRROR 16 /* Mirror register */
+#define MII_LXT970_IER 17 /* Interrupt Enable Register */
+
+#define MII_LXT970_IER_IEN 0x0002
+
+#define MII_LXT970_ISR 18 /* Interrupt Status Register */
+
+#define MII_LXT970_CONFIG 19 /* Configuration Register */
+#define MII_LXT970_CSR 20 /* Chip Status Register */
+
+#define MII_LXT970_CSR_DUPLEX 0x1000
+#define MII_LXT970_CSR_SPEED 0x0800
+
+/* ------------------------------------------------------------------------- */
+/* The Level one LXT971 is used on some of my custom boards */
+
+/* register definitions for the 971 */
+
+#define MII_LXT971_PCR 16 /* Port Control Register */
+
+#define MII_LXT971_SR2 17 /* Status Register 2 */
+#define MII_LXT971_SR2_DUPLEX 0x0200
+#define MII_LXT971_SR2_SPEED 0x4000
+
+#define MII_LXT971_IER 18 /* Interrupt Enable Register */
+#define MII_LXT971_IER_IEN 0x00f2
+
+#define MII_LXT971_ISR 19 /* Interrupt Status Register */
+
+#define MII_LXT971_LCR 20 /* LED Control Register */
+
+#define MII_LXT971_TCR 30 /* Transmit Control Register */
+
+
+/* ------------------------------------------------------------------------- */
+/* The Quality Semiconductor QS6612 is used on the RPX CLLF */
+
+/* register definitions */
+
+#define MII_QS6612_MCR 17 /* Mode Control Register */
+#define MII_QS6612_FTR 27 /* Factory Test Register */
+#define MII_QS6612_MCO 28 /* Misc. Control Register */
+#define MII_QS6612_ISR 29 /* Interrupt Source Register */
+#define MII_QS6612_IMR 30 /* Interrupt Mask Register */
+#define MII_QS6612_IMR_INIT 0x003a
+#define MII_QS6612_PCR 31 /* 100BaseTx PHY Control Reg. */
+
+#define MII_BASIC_FEATURES (SUPPORTED_10baseT_Half | \
+ SUPPORTED_10baseT_Full | \
+ SUPPORTED_100baseT_Half | \
+ SUPPORTED_100baseT_Full | \
+ SUPPORTED_Autoneg | \
+ SUPPORTED_TP | \
+ SUPPORTED_MII)
+
+#define MII_GBIT_FEATURES (MII_BASIC_FEATURES | \
+ SUPPORTED_1000baseT_Half | \
+ SUPPORTED_1000baseT_Full)
+
+#define MII_INTERRUPT_DISABLED 0x0
+#define MII_INTERRUPT_ENABLED 0x1
+/* Taken from mii_if_info and sungem_phy.h */
+struct phy_mii_info {
+ /* Information about the PHY type */
+ /* And management functions */
+ struct phy_info *phyinfo;
+
+ /* forced speed & duplex (no autoneg)
+ * partner speed & duplex & pause (autoneg)
+ */
+ int speed;
+ int duplex;
+ int pause;
+
+ /* The most recently read link state */
+ int link;
+
+ /* Enabled Interrupts */
+ u32 interrupts;
+
+ u32 advertising;
+ int autoneg;
+ int mii_id;
+
+ /* private data pointer */
+ /* For use by PHYs to maintain extra state */
+ void *priv;
+
+ /* Provided by host chip */
+ struct net_device *dev;
+
+ /* A lock to ensure that only one thing can read/write
+ * the MDIO bus at a time */
+ spinlock_t mdio_lock;
+
+ /* Provided by ethernet driver */
+ int (*mdio_read) (struct net_device *dev, int mii_id, int reg);
+ void (*mdio_write) (struct net_device *dev, int mii_id, int reg, int val);
+};
+
+/* struct phy_info: a structure which defines attributes for a PHY
+ *
+ * id will contain a number which represents the PHY. During
+ * startup, the driver will poll the PHY to find out what its
+ * UID--as defined by registers 2 and 3--is. The 32-bit result
+ * gotten from the PHY will be ANDed with phy_id_mask to
+ * discard any bits which may change based on revision numbers
+ * unimportant to functionality
+ *
+ * There are 7 commands which take a phy_mii_info structure.
+ * Each PHY must declare config_aneg, and read_status.
+ */
+struct phy_info {
+ u32 phy_id;
+ char *name;
+ unsigned int phy_id_mask;
+ u32 features;
+
+ /* Called to initialize the PHY */
+ int (*init)(struct phy_mii_info *mii_info);
+
+ /* Called to suspend the PHY for power */
+ int (*suspend)(struct phy_mii_info *mii_info);
+
+ /* Reconfigures autonegotiation (or disables it) */
+ int (*config_aneg)(struct phy_mii_info *mii_info);
+
+ /* Determines the negotiated speed and duplex */
+ int (*read_status)(struct phy_mii_info *mii_info);
+
+ /* Clears any pending interrupts */
+ int (*ack_interrupt)(struct phy_mii_info *mii_info);
+
+ /* Enables or disables interrupts */
+ int (*config_intr)(struct phy_mii_info *mii_info);
+
+ /* Clears up any memory if needed */
+ void (*close)(struct phy_mii_info *mii_info);
+};
+
+struct dm9161_private {
+ struct timer_list timer;
+ int resetdone;
+};
+
+u16 phy_read(struct phy_mii_info *mii_info, u16 regnum);
+void phy_write(struct phy_mii_info *mii_info, u16 regnum, u16 val);
+struct phy_info * get_phy_info(struct phy_mii_info *mii_info);
+void mii_clear_phy_interrupt(struct phy_mii_info *mii_info);
+void mii_configure_phy_interrupt(struct phy_mii_info *mii_info, u32 interrupts);
+
+#endif /* __PHY_H */
^ permalink raw reply [flat|nested] 5+ messages in thread* Re: RFC: performance monitor/oprofile support for e500
2004-10-30 1:16 RFC: performance monitor/oprofile support for e500 Andy Fleming
@ 2004-10-30 3:29 ` Eugene Surovegin
2004-11-01 5:43 ` Kumar Gala
1 sibling, 0 replies; 5+ messages in thread
From: Eugene Surovegin @ 2004-10-30 3:29 UTC (permalink / raw)
To: linuxppc-embedded
On Fri, Oct 29, 2004 at 08:16:56PM -0500, Andy Fleming wrote:
[snip]
+static int genmii_read_status(struct phy_mii_info *mii_info)
+{
+ u16 status;
+ int err;
+
+ /* Update the link, but return if there
+ * was an error */
+ err = genmii_update_link(mii_info);
+ if (err)
+ return err;
+
+ if (mii_info->autoneg) {
+ status = phy_read(mii_info, MII_LPA);
MII_LPA should be masked with MII_ADVERTISE here.
E.g.
status = phy_read(mii_info, MII_LPA) & phy_read(mii_info, MII_ADVERTISE);
--
Eugene
^ permalink raw reply [flat|nested] 5+ messages in thread* Re: RFC: performance monitor/oprofile support for e500
2004-10-30 1:16 RFC: performance monitor/oprofile support for e500 Andy Fleming
2004-10-30 3:29 ` Eugene Surovegin
@ 2004-11-01 5:43 ` Kumar Gala
2004-11-01 17:03 ` Andy Fleming
1 sibling, 1 reply; 5+ messages in thread
From: Kumar Gala @ 2004-11-01 5:43 UTC (permalink / raw)
To: Fleming Andy-afleming; +Cc: linuxppc-embedded
Andy,
Some feedback:
* Any reason to get ride of the 604 code now,w/o replacing it with
something?
* Why have you introduce two CPU features (CPU_FTR_CAN_USE_PMON_INTR &
CPU_FTR_FSL_BOOKE_PMON) you dont use ?
* In the PerformanceMonitor can't you use regs->nip for the same
purpose of regs->sia?
* Does arch/ppc/kernel/perfmon.c really need all the headers you are
including?
* Does perfmon.c make more sense as perfmon_fsl_booke.c
* op_ppc32_setup, I'd prefer the saving & restoring of the perf_irq
like arch/ppc64/oprofile/common.c does
* a number of issues related to SMP
* any reason get_kernel is a function?
* Do ctr_read & ctr_write really belong in op_impl.h, especially since
different implementations will be needed for classic ppc.
I sent a note to Paul M. about putting num_counters into cpu_specs
table.
- kumar
On Oct 29, 2004, at 8:16 PM, Fleming Andy-afleming wrote:
> This patch:
> * Adds support for using the performance monitor counters and the
> associated interrupt, but only enables it currently for e500
> * Removes the old 604 performance monitor code, suggested by various
> people on IRC
> * Adds oprofile support for the e500, with potential for expansion to
> support other ppc32 processors
>
> Andy Fleming
> NCSG Open Source Team
> Freescale Semiconductor, Inc
> <oprofile-kernel-patch><ATT2027925.txt>
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: RFC: performance monitor/oprofile support for e500
2004-11-01 5:43 ` Kumar Gala
@ 2004-11-01 17:03 ` Andy Fleming
2004-11-01 17:27 ` Kumar Gala
0 siblings, 1 reply; 5+ messages in thread
From: Andy Fleming @ 2004-11-01 17:03 UTC (permalink / raw)
To: Kumar Gala; +Cc: linuxppc-embedded
[-- Attachment #1: Type: text/plain, Size: 3042 bytes --]
Well, it looks like I messed up the patch, and included some stuff I'm
working on for generalizing the PHY interface. Strange, because I
thought I had hand-inspected that patch... Ah, I see: I copied the
wrong patch, which was done against linux-2.6, rather than my internal
tree. Please ignore the patch, since much of that code is obsolete.
Anyway, I will respond to Kumar's comments, and have attached the
CORRECT patch:
On Oct 31, 2004, at 23:43, Kumar Gala wrote:
> Andy,
>
> Some feedback:
>
> * Any reason to get ride of the 604 code now,w/o replacing it with
> something?
Well, it doesn't really do much, and I was told that no one was using
it. If people want, I can add it back.
> * Why have you introduce two CPU features (CPU_FTR_CAN_USE_PMON_INTR &
> CPU_FTR_FSL_BOOKE_PMON) you dont use ?
Ah, this is for the near future, when I implement support for 74xx
processors. The non-Freescale-Book-E parts use a different perfmon
scheme, and I wanted a way to distinguish. It may not be useful,
though, I agree, since I am using CONFIG_FSL_BOOKE to do this.
CAN_USE_PMON_INTR is important, though, to detect the errata which
could send the processor into never-neverland. Eventually, some 74xx
processors will have this bit set, while some will not.
> * In the PerformanceMonitor can't you use regs->nip for the same
> purpose of regs->sia?
Hmmm.... somehow I missed this when I was looking for a way to get the
sampled address. I will change this if no one thinks of a good reason
to do otherwise.
> * Does arch/ppc/kernel/perfmon.c really need all the headers you are
> including?
Probably not. Does anyone know an easy way to determine which headers
are needed?
> * Does perfmon.c make more sense as perfmon_fsl_booke.c
Well, I thought about that, but I figured that it was ok to put all of
the code into one file, and use #ifdef to distinguish. I'm willing to
divide it into two (one for FSL booke, and one for classic), though.
> * op_ppc32_setup, I'd prefer the saving & restoring of the perf_irq
> like arch/ppc64/oprofile/common.c does
Well, does this really make sense? If one driver grabs the interrupt,
and a subsequent driver tries to grab it, do we want the second driver
to win, but nicely restore the handler later, or do we want the second
driver to fail, and give the user the chance to disable the other
driver? I definitely won't use the method in ppc64, since that isn't
thread-safe if more than one driver wants the perfmon interrupt.
> * a number of issues related to SMP
I admit, I haven't carefully worked this out, as there aren't any SMP
e500 systems out there.
> * any reason get_kernel is a function?
Nope. Just legacy from having copied the ppc64 code.
> * Do ctr_read & ctr_write really belong in op_impl.h, especially since
> different implementations will be needed for classic ppc.
Good point. I will move these to op_model_e500.c
>
>
> I sent a note to Paul M. about putting num_counters into cpu_specs
> table.
That would be good.
[-- Attachment #2: oprofile-kernel-patch2 --]
[-- Type: application/octet-stream, Size: 31038 bytes --]
diff -Nru a/arch/ppc/kernel/Makefile b/arch/ppc/kernel/Makefile
--- a/arch/ppc/kernel/Makefile 2004-10-29 20:05:30 -05:00
+++ b/arch/ppc/kernel/Makefile 2004-10-29 20:05:30 -05:00
@@ -14,7 +14,7 @@
obj-y := entry.o traps.o irq.o idle.o time.o misc.o \
process.o signal.o ptrace.o align.o \
semaphore.o syscalls.o setup.o \
- cputable.o ppc_htab.o
+ cputable.o ppc_htab.o perfmon.o
obj-$(CONFIG_6xx) += l2cr.o cpu_setup_6xx.o
obj-$(CONFIG_POWER4) += cpu_setup_power4.o
obj-$(CONFIG_MODULES) += module.o ppc_ksyms.o
diff -Nru a/arch/ppc/kernel/asm-offsets.c b/arch/ppc/kernel/asm-offsets.c
--- a/arch/ppc/kernel/asm-offsets.c 2004-10-29 20:05:30 -05:00
+++ b/arch/ppc/kernel/asm-offsets.c 2004-10-29 20:05:30 -05:00
@@ -118,6 +118,7 @@
DEFINE(ORIG_GPR3, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, orig_gpr3));
DEFINE(RESULT, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, result));
DEFINE(TRAP, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, trap));
+ DEFINE(SIA, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, sia));
DEFINE(CLONE_VM, CLONE_VM);
DEFINE(CLONE_UNTRACED, CLONE_UNTRACED);
DEFINE(MM_PGD, offsetof(struct mm_struct, pgd));
diff -Nru a/arch/ppc/kernel/cputable.c b/arch/ppc/kernel/cputable.c
--- a/arch/ppc/kernel/cputable.c 2004-10-29 20:05:30 -05:00
+++ b/arch/ppc/kernel/cputable.c 2004-10-29 20:05:30 -05:00
@@ -114,8 +114,7 @@
{ /* 604 */
0xffff0000, 0x00040000, "604",
CPU_FTR_COMMON |
- CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | CPU_FTR_604_PERF_MON |
- CPU_FTR_HPTE_TABLE,
+ CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | CPU_FTR_HPTE_TABLE,
COMMON_PPC,
32, 32,
__setup_cpu_604
@@ -123,8 +122,7 @@
{ /* 604e */
0xfffff000, 0x00090000, "604e",
CPU_FTR_COMMON |
- CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | CPU_FTR_604_PERF_MON |
- CPU_FTR_HPTE_TABLE,
+ CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | CPU_FTR_HPTE_TABLE,
COMMON_PPC,
32, 32,
__setup_cpu_604
@@ -132,8 +130,7 @@
{ /* 604r */
0xffff0000, 0x00090000, "604r",
CPU_FTR_COMMON |
- CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | CPU_FTR_604_PERF_MON |
- CPU_FTR_HPTE_TABLE,
+ CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | CPU_FTR_HPTE_TABLE,
COMMON_PPC,
32, 32,
__setup_cpu_604
@@ -141,8 +138,7 @@
{ /* 604ev */
0xffff0000, 0x000a0000, "604ev",
CPU_FTR_COMMON |
- CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | CPU_FTR_604_PERF_MON |
- CPU_FTR_HPTE_TABLE,
+ CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | CPU_FTR_HPTE_TABLE,
COMMON_PPC,
32, 32,
__setup_cpu_604
@@ -612,7 +608,9 @@
{ /* e500 */
0xffff0000, 0x80200000, "e500",
/* xxx - galak: add CPU_FTR_MAYBE_CAN_DOZE */
- CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB,
+ CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB
+ | CPU_FTR_CAN_USE_PMON_INTR
+ | CPU_FTR_FSL_BOOKE_PMON,
PPC_FEATURE_32 | PPC_FEATURE_HAS_MMU,
32, 32,
0, /*__setup_cpu_e500 */
diff -Nru a/arch/ppc/kernel/head_e500.S b/arch/ppc/kernel/head_e500.S
--- a/arch/ppc/kernel/head_e500.S 2004-10-29 20:05:30 -05:00
+++ b/arch/ppc/kernel/head_e500.S 2004-10-29 20:05:30 -05:00
@@ -666,7 +666,14 @@
EXCEPTION(0x2050, SPEFloatingPointRound, UnknownException, EXC_XFER_EE)
/* Performance Monitor */
- EXCEPTION(0x2060, PerformanceMonitor, UnknownException, EXC_XFER_EE)
+# EXCEPTION(0x2060, PerformanceMonitor, PerformanceMonitorException, EXC_XFER_STD)
+ START_EXCEPTION(PerformanceMonitor)
+ NORMAL_EXCEPTION_PROLOG
+ stw r12,SIA(r11) /* Save the old SRR0 */
+ addi r3,r1,STACK_FRAME_OVERHEAD
+ EXC_XFER_STD(0x2060, PerformanceMonitorException)
+
+
/* Check for a single step debug exception while in an exception
* handler before state has been saved. This is to catch the case
diff -Nru a/arch/ppc/kernel/perfmon.c b/arch/ppc/kernel/perfmon.c
--- /dev/null Wed Dec 31 16:00:00 196900
+++ b/arch/ppc/kernel/perfmon.c 2004-10-29 20:05:30 -05:00
@@ -0,0 +1,291 @@
+/* kernel/perfmon.c
+ * PPC 32 Performance Monitor Infrastructure
+ *
+ * Author: Andy Fleming
+ * Copyright (c) 2004 Freescale Semiconductor, Inc
+ *
+ * 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 of the License, or (at your option) any later version.
+ */
+
+/* A lock to regulate grabbing the interrupt */
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/stddef.h>
+#include <linux/unistd.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/user.h>
+#include <linux/a.out.h>
+#include <linux/interrupt.h>
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/prctl.h>
+
+#include <asm/pgtable.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/reg.h>
+#include <asm/xmon.h>
+
+spinlock_t perfmon_lock = SPIN_LOCK_UNLOCKED;
+
+#ifdef CONFIG_FSL_BOOKE
+void init_pmc_stop(int ctr);
+void set_pmc_event(int ctr, int event);
+void set_pmc_user_kernel(int ctr, int user, int kernel);
+void set_pmc_marked(int ctr, int mark0, int mark1);
+void pmc_start_ctr(int ctr, int enable);
+void pmc_start_ctrs(int enable);
+void pmc_stop_ctrs(void);
+void dump_pmcs(void);
+
+static inline u32 get_pmlca(int ctr);
+static inline void set_pmlca(int ctr, u32 pmlca);
+
+static void dummy_perf(struct pt_regs *regs)
+{
+ unsigned int pmgc0 = mfpmr(PMRN_PMGC0);
+
+ pmgc0 &= ~PMGC0_PMIE;
+ mtpmr(PMRN_PMGC0, pmgc0);
+}
+
+#else
+/* Ensure exceptions are disabled */
+#define MMCR0_PMXE (1UL << (31 - 5))
+
+static void dummy_perf(struct pt_regs *regs)
+{
+ unsigned int mmcr0 = mfspr(SPRN_MMCR0);
+
+ mmcr0 &= ~MMCR0_PMXE;
+ mtspr(SPRN_MMCR0, mmcr0);
+}
+
+#endif
+
+void (*perf_irq)(struct pt_regs *) = dummy_perf;
+
+/* Grab the interrupt, if it's free.
+ * Returns 0 on success, -1 if the interrupt is taken already */
+int request_perfmon_irq(void (*handler)(struct pt_regs *))
+{
+ int err = 0;
+
+ spin_lock(&perfmon_lock);
+
+ if (perf_irq == dummy_perf)
+ perf_irq = handler;
+ else
+ err = -1;
+
+ spin_unlock(&perfmon_lock);
+
+ return err;
+}
+
+void free_perfmon_irq(void)
+{
+ spin_lock(&perfmon_lock);
+
+ perf_irq = dummy_perf;
+
+ spin_unlock(&perfmon_lock);
+}
+
+#ifdef CONFIG_FSL_BOOKE
+static inline u32 get_pmlca(int ctr)
+{
+ u32 pmlca;
+
+ switch (ctr) {
+ case 0:
+ pmlca = mfpmr(PMRN_PMLCA0);
+ break;
+ case 1:
+ pmlca = mfpmr(PMRN_PMLCA1);
+ break;
+ case 2:
+ pmlca = mfpmr(PMRN_PMLCA2);
+ break;
+ case 3:
+ pmlca = mfpmr(PMRN_PMLCA3);
+ break;
+ default:
+ panic("Bad ctr number\n");
+ }
+
+ return pmlca;
+}
+
+static inline void set_pmlca(int ctr, u32 pmlca)
+{
+ switch (ctr) {
+ case 0:
+ mtpmr(PMRN_PMLCA0, pmlca);
+ break;
+ case 1:
+ mtpmr(PMRN_PMLCA1, pmlca);
+ break;
+ case 2:
+ mtpmr(PMRN_PMLCA2, pmlca);
+ break;
+ case 3:
+ mtpmr(PMRN_PMLCA3, pmlca);
+ break;
+ default:
+ panic("Bad ctr number\n");
+ }
+}
+
+void init_pmc_stop(int ctr)
+{
+ u32 pmlca = (PMLCA_FC | PMLCA_FCS | PMLCA_FCU |
+ PMLCA_FCM1 | PMLCA_FCM0);
+ u32 pmlcb = 0;
+
+ switch (ctr) {
+ case 0:
+ mtpmr(PMRN_PMLCA0, pmlca);
+ mtpmr(PMRN_PMLCB0, pmlcb);
+ break;
+ case 1:
+ mtpmr(PMRN_PMLCA1, pmlca);
+ mtpmr(PMRN_PMLCB1, pmlcb);
+ break;
+ case 2:
+ mtpmr(PMRN_PMLCA2, pmlca);
+ mtpmr(PMRN_PMLCB2, pmlcb);
+ break;
+ case 3:
+ mtpmr(PMRN_PMLCA3, pmlca);
+ mtpmr(PMRN_PMLCB3, pmlcb);
+ break;
+ default:
+ panic("Bad ctr number!\n");
+ }
+}
+
+void set_pmc_event(int ctr, int event)
+{
+ u32 pmlca;
+
+ pmlca = get_pmlca(ctr);
+
+ pmlca = (pmlca & ~PMLCA_EVENT_MASK) |
+ ((event << PMLCA_EVENT_SHIFT) &
+ PMLCA_EVENT_MASK);
+
+ set_pmlca(ctr, pmlca);
+}
+
+void set_pmc_user_kernel(int ctr, int user, int kernel)
+{
+ u32 pmlca;
+
+ pmlca = get_pmlca(ctr);
+
+ if(user)
+ pmlca &= ~PMLCA_FCU;
+ else
+ pmlca |= PMLCA_FCU;
+
+ if(kernel)
+ pmlca &= ~PMLCA_FCS;
+ else
+ pmlca |= PMLCA_FCS;
+
+ set_pmlca(ctr, pmlca);
+}
+
+void set_pmc_marked(int ctr, int mark0, int mark1)
+{
+ u32 pmlca = get_pmlca(ctr);
+
+ if(mark0)
+ pmlca &= ~PMLCA_FCM0;
+ else
+ pmlca |= PMLCA_FCM0;
+
+ if(mark1)
+ pmlca &= ~PMLCA_FCM1;
+ else
+ pmlca |= PMLCA_FCM1;
+
+ set_pmlca(ctr, pmlca);
+}
+
+void pmc_start_ctr(int ctr, int enable)
+{
+ u32 pmlca = get_pmlca(ctr);
+
+ pmlca &= ~PMLCA_FC;
+
+ if (enable)
+ pmlca |= PMLCA_CE;
+ else
+ pmlca &= ~PMLCA_CE;
+
+ set_pmlca(ctr, pmlca);
+}
+
+void pmc_start_ctrs(int enable)
+{
+ u32 pmgc0 = mfpmr(PMRN_PMGC0);
+
+ pmgc0 &= ~PMGC0_FAC;
+ pmgc0 |= PMGC0_FCECE;
+
+ if (enable)
+ pmgc0 |= PMGC0_PMIE;
+ else
+ pmgc0 &= ~PMGC0_PMIE;
+
+ mtpmr(PMRN_PMGC0, pmgc0);
+}
+
+void pmc_stop_ctrs(void)
+{
+ u32 pmgc0 = mfpmr(PMRN_PMGC0);
+
+ pmgc0 |= PMGC0_FAC;
+
+ pmgc0 &= ~(PMGC0_PMIE | PMGC0_FCECE);
+
+ mtpmr(PMRN_PMGC0, pmgc0);
+}
+
+void dump_pmcs(void)
+{
+ printk("pmgc0: %x\n", mfpmr(PMRN_PMGC0));
+ printk("pmc\t\tpmlca\t\tpmlcb\n");
+ printk("%8x\t%8x\t%8x\n", mfpmr(PMRN_PMC0),
+ mfpmr(PMRN_PMLCA0), mfpmr(PMRN_PMLCB0));
+ printk("%8x\t%8x\t%8x\n", mfpmr(PMRN_PMC1),
+ mfpmr(PMRN_PMLCA1), mfpmr(PMRN_PMLCB1));
+ printk("%8x\t%8x\t%8x\n", mfpmr(PMRN_PMC2),
+ mfpmr(PMRN_PMLCA2), mfpmr(PMRN_PMLCB2));
+ printk("%8x\t%8x\t%8x\n", mfpmr(PMRN_PMC3),
+ mfpmr(PMRN_PMLCA3), mfpmr(PMRN_PMLCB3));
+}
+
+EXPORT_SYMBOL(init_pmc_stop);
+EXPORT_SYMBOL(set_pmc_event);
+EXPORT_SYMBOL(set_pmc_user_kernel);
+EXPORT_SYMBOL(set_pmc_marked);
+EXPORT_SYMBOL(pmc_start_ctr);
+EXPORT_SYMBOL(pmc_start_ctrs);
+EXPORT_SYMBOL(pmc_stop_ctrs);
+EXPORT_SYMBOL(dump_pmcs);
+
+#endif
+EXPORT_SYMBOL(perf_irq);
+EXPORT_SYMBOL(request_perfmon_irq);
+EXPORT_SYMBOL(free_perfmon_irq);
diff -Nru a/arch/ppc/kernel/ppc_htab.c b/arch/ppc/kernel/ppc_htab.c
--- a/arch/ppc/kernel/ppc_htab.c 2004-10-29 20:05:30 -05:00
+++ b/arch/ppc/kernel/ppc_htab.c 2004-10-29 20:05:30 -05:00
@@ -59,42 +59,6 @@
.release = single_release,
};
-static char *pmc1_lookup(unsigned long mmcr0)
-{
- switch ( mmcr0 & (0x7f<<7) )
- {
- case 0x0:
- return "none";
- case MMCR0_PMC1_CYCLES:
- return "cycles";
- case MMCR0_PMC1_ICACHEMISS:
- return "ic miss";
- case MMCR0_PMC1_DTLB:
- return "dtlb miss";
- default:
- return "unknown";
- }
-}
-
-static char *pmc2_lookup(unsigned long mmcr0)
-{
- switch ( mmcr0 & 0x3f )
- {
- case 0x0:
- return "none";
- case MMCR0_PMC2_CYCLES:
- return "cycles";
- case MMCR0_PMC2_DCACHEMISS:
- return "dc miss";
- case MMCR0_PMC2_ITLB:
- return "itlb miss";
- case MMCR0_PMC2_LOADMISSTIME:
- return "load miss time";
- default:
- return "unknown";
- }
-}
-
/*
* print some useful info about the hash table. This function
* is _REALLY_ slow (see the nested for loops below) but nothing
@@ -102,29 +66,11 @@
*/
static int ppc_htab_show(struct seq_file *m, void *v)
{
- unsigned long mmcr0 = 0, pmc1 = 0, pmc2 = 0;
#if defined(CONFIG_PPC_STD_MMU) && !defined(CONFIG_PPC64BRIDGE)
unsigned int kptes = 0, uptes = 0;
PTE *ptr;
#endif /* CONFIG_PPC_STD_MMU */
- if (cur_cpu_spec[0]->cpu_features & CPU_FTR_604_PERF_MON) {
- mmcr0 = mfspr(SPRN_MMCR0);
- pmc1 = mfspr(SPRN_PMC1);
- pmc2 = mfspr(SPRN_PMC2);
- seq_printf(m,
- "604 Performance Monitoring\n"
- "MMCR0\t\t: %08lx %s%s ",
- mmcr0,
- ( mmcr0>>28 & 0x2 ) ? "(user mode counted)" : "",
- ( mmcr0>>28 & 0x4 ) ? "(kernel mode counted)" : "");
- seq_printf(m,
- "\nPMC1\t\t: %08lx (%s)\n"
- "PMC2\t\t: %08lx (%s)\n",
- pmc1, pmc1_lookup(mmcr0),
- pmc2, pmc2_lookup(mmcr0));
- }
-
#ifdef CONFIG_PPC_STD_MMU
/* if we don't have a htab */
if ( Hash_size == 0 ) {
@@ -188,7 +134,7 @@
}
/*
- * Allow user to define performance counters and resize the hash table
+ * Allow user to resize the hash table
*/
static ssize_t ppc_htab_write(struct file * file, const char __user * ubuffer,
size_t count, loff_t *ppos)
@@ -209,106 +155,10 @@
if ( !strncmp( buffer, "reset", 5) )
{
- if (cur_cpu_spec[0]->cpu_features & CPU_FTR_604_PERF_MON) {
- /* reset PMC1 and PMC2 */
- mtspr(SPRN_PMC1, 0);
- mtspr(SPRN_PMC2, 0);
- }
htab_reloads = 0;
htab_evicts = 0;
pte_misses = 0;
pte_errors = 0;
- }
-
- /* Everything below here requires the performance monitor feature. */
- if ( !cur_cpu_spec[0]->cpu_features & CPU_FTR_604_PERF_MON )
- return count;
-
- /* turn off performance monitoring */
- if ( !strncmp( buffer, "off", 3) )
- {
- mtspr(SPRN_MMCR0, 0);
- mtspr(SPRN_PMC1, 0);
- mtspr(SPRN_PMC2, 0);
- }
-
- if ( !strncmp( buffer, "user", 4) )
- {
- /* setup mmcr0 and clear the correct pmc */
- tmp = (mfspr(SPRN_MMCR0) & ~(0x60000000)) | 0x20000000;
- mtspr(SPRN_MMCR0, tmp);
- mtspr(SPRN_PMC1, 0);
- mtspr(SPRN_PMC2, 0);
- }
-
- if ( !strncmp( buffer, "kernel", 6) )
- {
- /* setup mmcr0 and clear the correct pmc */
- tmp = (mfspr(SPRN_MMCR0) & ~(0x60000000)) | 0x40000000;
- mtspr(SPRN_MMCR0, tmp);
- mtspr(SPRN_PMC1, 0);
- mtspr(SPRN_PMC2, 0);
- }
-
- /* PMC1 values */
- if ( !strncmp( buffer, "dtlb", 4) )
- {
- /* setup mmcr0 and clear the correct pmc */
- tmp = (mfspr(SPRN_MMCR0) & ~(0x7F << 7)) | MMCR0_PMC1_DTLB;
- mtspr(SPRN_MMCR0, tmp);
- mtspr(SPRN_PMC1, 0);
- }
-
- if ( !strncmp( buffer, "ic miss", 7) )
- {
- /* setup mmcr0 and clear the correct pmc */
- tmp = (mfspr(SPRN_MMCR0) & ~(0x7F<<7)) | MMCR0_PMC1_ICACHEMISS;
- mtspr(SPRN_MMCR0, tmp);
- mtspr(SPRN_PMC1, 0);
- }
-
- /* PMC2 values */
- if ( !strncmp( buffer, "load miss time", 14) )
- {
- /* setup mmcr0 and clear the correct pmc */
- asm volatile(
- "mfspr %0,%1\n\t" /* get current mccr0 */
- "rlwinm %0,%0,0,0,31-6\n\t" /* clear bits [26-31] */
- "ori %0,%0,%2 \n\t" /* or in mmcr0 settings */
- "mtspr %1,%0 \n\t" /* set new mccr0 */
- "mtspr %3,%4 \n\t" /* reset the pmc */
- : "=r" (tmp)
- : "i" (SPRN_MMCR0),
- "i" (MMCR0_PMC2_LOADMISSTIME),
- "i" (SPRN_PMC2), "r" (0) );
- }
-
- if ( !strncmp( buffer, "itlb", 4) )
- {
- /* setup mmcr0 and clear the correct pmc */
- asm volatile(
- "mfspr %0,%1\n\t" /* get current mccr0 */
- "rlwinm %0,%0,0,0,31-6\n\t" /* clear bits [26-31] */
- "ori %0,%0,%2 \n\t" /* or in mmcr0 settings */
- "mtspr %1,%0 \n\t" /* set new mccr0 */
- "mtspr %3,%4 \n\t" /* reset the pmc */
- : "=r" (tmp)
- : "i" (SPRN_MMCR0), "i" (MMCR0_PMC2_ITLB),
- "i" (SPRN_PMC2), "r" (0) );
- }
-
- if ( !strncmp( buffer, "dc miss", 7) )
- {
- /* setup mmcr0 and clear the correct pmc */
- asm volatile(
- "mfspr %0,%1\n\t" /* get current mccr0 */
- "rlwinm %0,%0,0,0,31-6\n\t" /* clear bits [26-31] */
- "ori %0,%0,%2 \n\t" /* or in mmcr0 settings */
- "mtspr %1,%0 \n\t" /* set new mccr0 */
- "mtspr %3,%4 \n\t" /* reset the pmc */
- : "=r" (tmp)
- : "i" (SPRN_MMCR0), "i" (MMCR0_PMC2_DCACHEMISS),
- "i" (SPRN_PMC2), "r" (0) );
}
return count;
diff -Nru a/arch/ppc/kernel/traps.c b/arch/ppc/kernel/traps.c
--- a/arch/ppc/kernel/traps.c 2004-10-29 20:05:30 -05:00
+++ b/arch/ppc/kernel/traps.c 2004-10-29 20:05:30 -05:00
@@ -71,6 +71,7 @@
* Trap & Exception support
*/
+extern void (*perf_irq)(struct pt_regs *);
spinlock_t die_lock = SPIN_LOCK_UNLOCKED;
@@ -725,6 +726,11 @@
}
}
#endif /* CONFIG_ALTIVEC */
+
+void PerformanceMonitorException(struct pt_regs *regs)
+{
+ perf_irq(regs);
+}
#ifdef CONFIG_FSL_BOOKE
void CacheLockingException(struct pt_regs *regs, unsigned long address,
diff -Nru a/arch/ppc/oprofile/Makefile b/arch/ppc/oprofile/Makefile
--- a/arch/ppc/oprofile/Makefile 2004-10-29 20:05:30 -05:00
+++ b/arch/ppc/oprofile/Makefile 2004-10-29 20:05:30 -05:00
@@ -6,4 +6,4 @@
oprofilefs.o oprofile_stats.o \
timer_int.o )
-oprofile-y := $(DRIVER_OBJS) init.o
+oprofile-y := $(DRIVER_OBJS) common.o op_model_e500.o
diff -Nru a/arch/ppc/oprofile/common.c b/arch/ppc/oprofile/common.c
--- /dev/null Wed Dec 31 16:00:00 196900
+++ b/arch/ppc/oprofile/common.c 2004-10-29 20:05:30 -05:00
@@ -0,0 +1,152 @@
+/*
+ * PPC 32 oprofile support
+ * Based on PPC64 oprofile support
+ * Copyright (C) 2004 Anton Blanchard <anton@au.ibm.com>, IBM
+ *
+ * Copyright (C) Freescale Semiconductor, Inc 2004
+ *
+ * Author: Andy Fleming
+ *
+ * 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 of the License, or (at your option) any later version.
+ */
+
+#include <linux/oprofile.h>
+#include <linux/init.h>
+#include <linux/smp.h>
+#include <linux/errno.h>
+#include <asm/ptrace.h>
+#include <asm/system.h>
+#include <asm/perfmon.h>
+
+#include "op_impl.h"
+
+extern struct op_ppc32_model op_model_e500;
+static struct op_ppc32_model *model;
+
+static struct op_counter_config ctr[OP_MAX_COUNTER];
+static struct op_system_config sys;
+
+static void op_handle_interrupt(struct pt_regs *regs)
+{
+ model->handle_interrupt(regs, ctr);
+}
+
+static int op_ppc32_setup(void)
+{
+ /* Install our interrupt handler into the existing hook. */
+ if(request_perfmon_irq(&op_handle_interrupt))
+ return -EBUSY;
+
+ mb();
+
+ /* Pre-compute the values to stuff in the hardware registers. */
+ model->reg_setup(ctr, &sys, model->num_counters);
+
+#if 0
+ /* FIXME: Make multi-cpu work */
+ /* Configure the registers on all cpus. */
+ on_each_cpu(model->reg_setup, NULL, 0, 1);
+#endif
+
+ return 0;
+}
+
+static void op_ppc32_shutdown(void)
+{
+ mb();
+
+ /* Remove our interrupt handler. We may be removing this module. */
+ free_perfmon_irq();
+}
+
+static void op_ppc32_cpu_start(void *dummy)
+{
+ model->start(ctr);
+}
+
+static int op_ppc32_start(void)
+{
+ on_each_cpu(op_ppc32_cpu_start, NULL, 0, 1);
+ return 0;
+}
+
+static inline void op_ppc32_cpu_stop(void *dummy)
+{
+ model->stop();
+}
+
+static void op_ppc32_stop(void)
+{
+ on_each_cpu(op_ppc32_cpu_stop, NULL, 0, 1);
+}
+
+static int op_ppc32_create_files(struct super_block *sb, struct dentry *root)
+{
+ int i;
+
+ for (i = 0; i < model->num_counters; ++i) {
+ struct dentry *dir;
+ char buf[3];
+
+ snprintf(buf, sizeof buf, "%d", i);
+ dir = oprofilefs_mkdir(sb, root, buf);
+
+ oprofilefs_create_ulong(sb, dir, "enabled", &ctr[i].enabled);
+ oprofilefs_create_ulong(sb, dir, "event", &ctr[i].event);
+ oprofilefs_create_ulong(sb, dir, "count", &ctr[i].count);
+ oprofilefs_create_ulong(sb, dir, "kernel", &ctr[i].kernel);
+ oprofilefs_create_ulong(sb, dir, "user", &ctr[i].user);
+
+ /* FIXME: Not sure if this is used */
+ oprofilefs_create_ulong(sb, dir, "unit_mask", &ctr[i].unit_mask);
+ }
+
+ oprofilefs_create_ulong(sb, root, "enable_kernel", &sys.enable_kernel);
+ oprofilefs_create_ulong(sb, root, "enable_user", &sys.enable_user);
+
+ /* Default to tracing both kernel and user */
+ sys.enable_kernel = 1;
+ sys.enable_user = 1;
+
+ return 0;
+}
+
+static struct oprofile_operations oprof_ppc32_ops = {
+ .create_files = op_ppc32_create_files,
+ .setup = op_ppc32_setup,
+ .shutdown = op_ppc32_shutdown,
+ .start = op_ppc32_start,
+ .stop = op_ppc32_stop,
+ .cpu_type = NULL /* To be filled in below. */
+};
+
+int __init oprofile_arch_init(struct oprofile_operations **ops)
+{
+ unsigned int pvr;
+
+ pvr = mfspr(SPRN_PVR);
+
+ switch (PVR_VER(pvr)) {
+ case PVR_VER(PVR_8540): /* Same as 8560 */
+ model = &op_model_e500;
+ model->num_counters = 4;
+ oprof_ppc32_ops.cpu_type = "ppc/e500";
+ break;
+ default:
+ return -ENODEV;
+ }
+
+ *ops = &oprof_ppc32_ops;
+
+ printk(KERN_INFO "oprofile: using %s performance monitoring.\n",
+ oprof_ppc32_ops.cpu_type);
+
+ return 0;
+}
+
+void oprofile_arch_exit(void)
+{
+}
diff -Nru a/arch/ppc/oprofile/init.c b/arch/ppc/oprofile/init.c
--- a/arch/ppc/oprofile/init.c 2004-10-29 20:05:30 -05:00
+++ /dev/null Wed Dec 31 16:00:00 196900
@@ -1,23 +0,0 @@
-/**
- * @file init.c
- *
- * @remark Copyright 2002 OProfile authors
- * @remark Read the file COPYING
- *
- * @author John Levon <levon@movementarian.org>
- */
-
-#include <linux/kernel.h>
-#include <linux/oprofile.h>
-#include <linux/init.h>
-#include <linux/errno.h>
-
-int __init oprofile_arch_init(struct oprofile_operations ** ops)
-{
- return -ENODEV;
-}
-
-
-void oprofile_arch_exit(void)
-{
-}
diff -Nru a/arch/ppc/oprofile/op_impl.h b/arch/ppc/oprofile/op_impl.h
--- /dev/null Wed Dec 31 16:00:00 196900
+++ b/arch/ppc/oprofile/op_impl.h 2004-10-29 20:05:30 -05:00
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2004 Anton Blanchard <anton@au.ibm.com>, IBM
+ *
+ * Based on alpha version.
+ *
+ * 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 of the License, or (at your option) any later version.
+ */
+
+#ifndef OP_IMPL_H
+#define OP_IMPL_H 1
+
+#define OP_MAX_COUNTER 8
+
+/* Per-counter configuration as set via oprofilefs. */
+struct op_counter_config {
+ unsigned long enabled;
+ unsigned long event;
+ unsigned long count;
+ unsigned long kernel;
+ unsigned long user;
+ unsigned long unit_mask;
+};
+
+/* System-wide configuration as set via oprofilefs. */
+struct op_system_config {
+ unsigned long enable_kernel;
+ unsigned long enable_user;
+};
+
+/* Per-arch configuration */
+struct op_ppc32_model {
+ void (*reg_setup) (struct op_counter_config *,
+ struct op_system_config *,
+ int num_counters);
+ void (*start) (struct op_counter_config *);
+ void (*stop) (void);
+ void (*handle_interrupt) (struct pt_regs *,
+ struct op_counter_config *);
+ int num_counters;
+};
+
+static inline unsigned int ctr_read(unsigned int i)
+{
+ switch(i) {
+ case 0:
+ return mfpmr(PMRN_PMC0);
+ case 1:
+ return mfpmr(PMRN_PMC1);
+ case 2:
+ return mfpmr(PMRN_PMC2);
+ case 3:
+ return mfpmr(PMRN_PMC3);
+ default:
+ return 0;
+ }
+}
+
+static inline void ctr_write(unsigned int i, unsigned int val)
+{
+ switch(i) {
+ case 0:
+ mtpmr(PMRN_PMC0, val);
+ break;
+ case 1:
+ mtpmr(PMRN_PMC1, val);
+ break;
+ case 2:
+ mtpmr(PMRN_PMC2, val);
+ break;
+ case 3:
+ mtpmr(PMRN_PMC3, val);
+ break;
+ default:
+ break;
+ }
+}
+
+#endif
diff -Nru a/arch/ppc/oprofile/op_model_e500.c b/arch/ppc/oprofile/op_model_e500.c
--- /dev/null Wed Dec 31 16:00:00 196900
+++ b/arch/ppc/oprofile/op_model_e500.c 2004-10-29 20:05:30 -05:00
@@ -0,0 +1,159 @@
+/*
+ * oprofile/op_model_e500.c
+ *
+ * e500 oprofile support, based on ppc64 oprofile support
+ * Copyright (C) 2004 Anton Blanchard <anton@au.ibm.com>, IBM
+ *
+ * Copyright (c) 2004 Freescale Semiconductor, Inc
+ *
+ * Author: Andy Fleming
+ * Maintainer: Kumar Gala <Kumar.Gala@freescale.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 of the License, or (at your option) any later version.
+ */
+
+#include <linux/oprofile.h>
+#include <linux/init.h>
+#include <linux/smp.h>
+#include <asm/ptrace.h>
+#include <asm/system.h>
+#include <asm/processor.h>
+#include <asm/cputable.h>
+#include <asm/reg_booke.h>
+#include <asm/page.h>
+#include <asm/perfmon.h>
+
+/*#define dbg(args...) printk(args) */
+#define dbg
+
+#include "op_impl.h"
+
+static unsigned long reset_value[OP_MAX_COUNTER];
+
+static int num_counters;
+static int oprofile_running;
+
+static void e500_reg_setup(struct op_counter_config *ctr,
+ struct op_system_config *sys,
+ int num_ctrs)
+{
+ int i;
+
+ num_counters = num_ctrs;
+
+ /* freeze all counters */
+ pmc_stop_ctrs();
+
+ /* Our counters count up, and "count" refers to
+ * how much before the next interrupt, and we interrupt
+ * on overflow. So we calculate the starting value
+ * which will give us "count" until overflow.
+ * Then we set the events on the enabled counters */
+ for (i = 0; i < num_counters; ++i) {
+ reset_value[i] = 0x80000000UL - ctr[i].count;
+
+ init_pmc_stop(i);
+
+ set_pmc_event(i, ctr[i].event);
+
+ set_pmc_user_kernel(i, ctr[i].user, ctr[i].kernel);
+ }
+}
+
+static void e500_start(struct op_counter_config *ctr)
+{
+ int i;
+
+ mtmsr(mfmsr() | MSR_PMM);
+
+ for (i = 0; i < num_counters; ++i) {
+ if (ctr[i].enabled) {
+ ctr_write(i, reset_value[i]);
+ /* Set Each enabled counterd to only
+ * count when the Mark bit is not set */
+ set_pmc_marked(i, 1, 0);
+ pmc_start_ctr(i, 1);
+ } else {
+ ctr_write(i, 0);
+
+ /* Set the ctr to be stopped */
+ pmc_start_ctr(i, 0);
+ }
+ }
+
+ /* Clear the freeze bit, and enable the interrupt.
+ * The counters won't actually start until the rfi clears
+ * the PMM bit */
+ pmc_start_ctrs(1);
+
+ oprofile_running = 1;
+
+ dbg("start on cpu %d, pmgc0 %x\n", smp_processor_id(),
+ mfpmr(PMRN_PMGC0));
+}
+
+static void e500_stop(void)
+{
+ /* freeze counters */
+ pmc_stop_ctrs();
+
+ oprofile_running = 0;
+
+ dbg("stop on cpu %d, pmgc0 %x\n", smp_processor_id(),
+ mfpmr(PMRN_PMGC0));
+
+ mb();
+}
+
+static inline int get_kernel(unsigned long pc)
+{
+ int is_kernel;
+
+ is_kernel = (pc >= KERNELBASE);
+
+ return is_kernel;
+}
+
+static void e500_handle_interrupt(struct pt_regs *regs,
+ struct op_counter_config *ctr)
+{
+ unsigned long pc;
+ int is_kernel;
+ int val;
+ int i;
+ unsigned int cpu = smp_processor_id();
+
+ /* set the PMM bit (see comment below) */
+ mtmsr(mfmsr() | MSR_PMM);
+
+ pc = regs->sia;
+ is_kernel = get_kernel(pc);
+
+ for (i = 0; i < num_counters; ++i) {
+ val = ctr_read(i);
+ if (val < 0) {
+ if (oprofile_running && ctr[i].enabled) {
+ oprofile_add_sample(pc, is_kernel, i, cpu);
+ ctr_write(i, reset_value[i]);
+ } else {
+ ctr_write(i, 0);
+ }
+ }
+ }
+
+ /* The freeze bit was set by the interrupt. */
+ /* Clear the freeze bit, and reenable the interrupt.
+ * The counters won't actually start until the rfi clears
+ * the PMM bit */
+ pmc_start_ctrs(1);
+}
+
+struct op_ppc32_model op_model_e500 = {
+ .reg_setup = e500_reg_setup,
+ .start = e500_start,
+ .stop = e500_stop,
+ .handle_interrupt = e500_handle_interrupt,
+};
diff -Nru a/include/asm-ppc/cputable.h b/include/asm-ppc/cputable.h
--- a/include/asm-ppc/cputable.h 2004-10-29 20:05:30 -05:00
+++ b/include/asm-ppc/cputable.h 2004-10-29 20:05:30 -05:00
@@ -65,7 +65,7 @@
#define CPU_FTR_TAU 0x00000010
#define CPU_FTR_CAN_DOZE 0x00000020
#define CPU_FTR_USE_TB 0x00000040
-#define CPU_FTR_604_PERF_MON 0x00000080
+#define CPU_FTR_CAN_USE_PMON_INTR 0x00000080
#define CPU_FTR_601 0x00000100
#define CPU_FTR_HPTE_TABLE 0x00000200
#define CPU_FTR_CAN_NAP 0x00000400
@@ -77,6 +77,7 @@
#define CPU_FTR_HAS_HIGH_BATS 0x00010000
#define CPU_FTR_NEED_COHERENT 0x00020000
#define CPU_FTR_NO_BTIC 0x00040000
+#define CPU_FTR_FSL_BOOKE_PMON 0x00080000
#ifdef __ASSEMBLY__
diff -Nru a/include/asm-ppc/perfmon.h b/include/asm-ppc/perfmon.h
--- /dev/null Wed Dec 31 16:00:00 196900
+++ b/include/asm-ppc/perfmon.h 2004-10-29 20:05:30 -05:00
@@ -0,0 +1,18 @@
+#ifndef __PERFMON_H
+#define __PERFMON_H
+
+int request_perfmon_irq(void (*handler)(struct pt_regs *));
+void free_perfmon_irq(void);
+
+#ifdef CONFIG_FSL_BOOKE
+void init_pmc_stop(int ctr);
+void set_pmc_event(int ctr, int event);
+void set_pmc_user_kernel(int ctr, int user, int kernel);
+void set_pmc_marked(int ctr, int mark0, int mark1);
+void pmc_start_ctr(int ctr, int enable);
+void pmc_start_ctrs(int enable);
+void pmc_stop_ctrs(void);
+void dump_pmcs(void);
+#endif
+
+#endif /* __PERFMON_H */
diff -Nru a/include/asm-ppc/ptrace.h b/include/asm-ppc/ptrace.h
--- a/include/asm-ppc/ptrace.h 2004-10-29 20:05:30 -05:00
+++ b/include/asm-ppc/ptrace.h 2004-10-29 20:05:30 -05:00
@@ -35,6 +35,10 @@
unsigned long dar; /* Fault registers */
unsigned long dsisr; /* on 4xx/Book-E used for ESR */
unsigned long result; /* Result of a system call */
+
+ /* Address of instruction which caused a performance
+ * monitor interrupt */
+ unsigned long sia;
};
#endif /* __ASSEMBLY__ */
diff -Nru a/include/asm-ppc/reg_booke.h b/include/asm-ppc/reg_booke.h
--- a/include/asm-ppc/reg_booke.h 2004-10-29 20:05:30 -05:00
+++ b/include/asm-ppc/reg_booke.h 2004-10-29 20:05:30 -05:00
@@ -51,6 +51,59 @@
#define mtpmr(rn, v) asm volatile("mtpmr " __stringify(rn) ",%0" : : "r" (v))
#endif /* __ASSEMBLY__ */
+/* Freescale Book E Performance Monitor APU Registers */
+#define PMRN_PMC0 0x010 /* Performance Monitor Counter 0 */
+#define PMRN_PMC1 0x011 /* Performance Monitor Counter 1 */
+#define PMRN_PMC2 0x012 /* Performance Monitor Counter 1 */
+#define PMRN_PMC3 0x013 /* Performance Monitor Counter 1 */
+#define PMRN_PMLCA0 0x090 /* PM Local Control A0 */
+#define PMRN_PMLCA1 0x091 /* PM Local Control A1 */
+#define PMRN_PMLCA2 0x092 /* PM Local Control A2 */
+#define PMRN_PMLCA3 0x093 /* PM Local Control A3 */
+
+#define PMLCA_FC 0x80000000 /* Freeze Counter */
+#define PMLCA_FCS 0x40000000 /* Freeze in Supervisor */
+#define PMLCA_FCU 0x20000000 /* Freeze in User */
+#define PMLCA_FCM1 0x10000000 /* Freeze when PMM==1 */
+#define PMLCA_FCM0 0x08000000 /* Freeze when PMM==0 */
+#define PMLCA_CE 0x04000000 /* Condition Enable */
+
+#define PMLCA_EVENT_MASK 0x007f0000 /* Event field */
+#define PMLCA_EVENT_SHIFT 16
+
+#define PMRN_PMLCB0 0x110 /* PM Local Control B0 */
+#define PMRN_PMLCB1 0x111 /* PM Local Control B1 */
+#define PMRN_PMLCB2 0x112 /* PM Local Control B2 */
+#define PMRN_PMLCB3 0x113 /* PM Local Control B3 */
+
+#define PMLCB_THRESHMUL_MASK 0x0700 /* Threshhold Multiple Field */
+#define PMLCB_THRESHMUL_SHIFT 8
+
+#define PMLCB_THRESHOLD_MASK 0x003f /* Threshold Field */
+#define PMLCB_THRESHOLD_SHIFT 0
+
+#define PMRN_PMGC0 0x190 /* PM Global Control 0 */
+
+#define PMGC0_FAC 0x80000000 /* Freeze all Counters */
+#define PMGC0_PMIE 0x40000000 /* Interrupt Enable */
+#define PMGC0_FCECE 0x20000000 /* Freeze countes on
+ Enabled Condition or
+ Event */
+
+#define PMRN_UPMC0 0x000 /* User Performance Monitor Counter 0 */
+#define PMRN_UPMC1 0x001 /* User Performance Monitor Counter 1 */
+#define PMRN_UPMC2 0x002 /* User Performance Monitor Counter 1 */
+#define PMRN_UPMC3 0x003 /* User Performance Monitor Counter 1 */
+#define PMRN_UPMLCA0 0x080 /* User PM Local Control A0 */
+#define PMRN_UPMLCA1 0x081 /* User PM Local Control A1 */
+#define PMRN_UPMLCA2 0x082 /* User PM Local Control A2 */
+#define PMRN_UPMLCA3 0x083 /* User PM Local Control A3 */
+#define PMRN_UPMLCB0 0x100 /* User PM Local Control B0 */
+#define PMRN_UPMLCB1 0x101 /* User PM Local Control B1 */
+#define PMRN_UPMLCB2 0x102 /* User PM Local Control B2 */
+#define PMRN_UPMLCB3 0x103 /* User PM Local Control B3 */
+#define PMRN_UPMGC0 0x180 /* User PM Global Control 0 */
+
/* Machine State Register (MSR) Fields */
#define MSR_UCLE (1<<26) /* User-mode cache lock enable */
^ permalink raw reply [flat|nested] 5+ messages in thread* Re: RFC: performance monitor/oprofile support for e500
2004-11-01 17:03 ` Andy Fleming
@ 2004-11-01 17:27 ` Kumar Gala
0 siblings, 0 replies; 5+ messages in thread
From: Kumar Gala @ 2004-11-01 17:27 UTC (permalink / raw)
To: Fleming Andy-afleming; +Cc: linuxppc-embedded
On Nov 1, 2004, at 11:03 AM, Fleming Andy-afleming wrote:
> Well, it looks like I messed up the patch, and included some stuff I'm
> working on for generalizing the PHY interface.=A0 Strange, because I
> thought I had hand-inspected that patch...=A0 Ah, I see: I copied the
> wrong patch, which was done against linux-2.6, rather than my internal
> tree.=A0 Please ignore the patch, since much of that code is obsolete.
>
> Anyway, I will respond to Kumar's comments, and have attached the
> CORRECT patch:
>
> On Oct 31, 2004, at 23:43, Kumar Gala wrote:
>
> > Andy,
> >
> > Some feedback:
> >
> > * Any reason to get ride of the 604 code now,w/o replacing it with
> > something?
>
> Well, it doesn't really do much, and I was told that no one was using
> it.=A0 If people want, I can add it back.
>
> > * Why have you introduce two CPU features (CPU_FTR_CAN_USE_PMON_INTR=20=
> &
> > CPU_FTR_FSL_BOOKE_PMON) you dont use ?
>
> Ah, this is for the near future, when I implement support for 74xx
> processors.=A0 The non-Freescale-Book-E parts use a different perfmon
> scheme, and I wanted a way to distinguish.=A0 It may not be useful,
> though, I agree, since I am using CONFIG_FSL_BOOKE to do this.=A0
> CAN_USE_PMON_INTR is important, though, to detect the errata which
> could send the processor into never-neverland.=A0 Eventually, some =
74xx
> processors will have this bit set, while some will not.
Ok, add this when you add the code, not before then.
> > * In the PerformanceMonitor can't you use regs->nip for the same
> > purpose of regs->sia?
>
> Hmmm.... somehow I missed this when I was looking for a way to get the
> sampled address.=A0 I will change this if no one thinks of a good =
reason
> to do otherwise.
>
> > * Does arch/ppc/kernel/perfmon.c really need all the headers you are
> > including?
>
> Probably not.=A0 Does anyone know an easy way to determine which =
headers
> are needed?
>
> > * Does perfmon.c make more sense as perfmon_fsl_booke.c
>
> Well, I thought about that, but I figured that it was ok to put all of
> the code into one file, and use #ifdef to distinguish.=A0 I'm willing =
to
> divide it into two (one for FSL booke, and one for classic), though.
Unless there is a fair amount of shared code, I would recommend=20
separating the files. We are not going to have a single kernel image=20
that supports both book-e and classic PPC so we can make this a=20
compile/makefile decision instead of #ifdef'd.
> > * op_ppc32_setup, I'd prefer the saving & restoring of the perf_irq
> > like arch/ppc64/oprofile/common.c does
>
> Well, does this really make sense?=A0 If one driver grabs the =
interrupt,
> and a subsequent driver tries to grab it, do we want the second driver
> to win, but nicely restore the handler later, or do we want the second
> driver to fail, and give the user the chance to disable the other
> driver?=A0 I definitely won't use the method in ppc64, since that =
isn't
> thread-safe if more than one driver wants the perfmon interrupt.
Hmm, ok. I'm not sure what we should do here. Maybe others have some=20=
comments.
> > * a number of issues related to SMP
>
> I admit, I haven't carefully worked this out, as there aren't any SMP
> e500 systems out there.
True, but if you are going share infrastructure with PPC classic, there=20=
are a number of SMP systems there.
[snip]
- kumar=
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2004-11-01 17:27 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2004-10-30 1:16 RFC: performance monitor/oprofile support for e500 Andy Fleming
2004-10-30 3:29 ` Eugene Surovegin
2004-11-01 5:43 ` Kumar Gala
2004-11-01 17:03 ` Andy Fleming
2004-11-01 17:27 ` Kumar Gala
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.