Back to home page

LXR

 
 

    


0001 /*
0002  * AVR32 Performance Counter Driver
0003  *
0004  * Copyright (C) 2005-2007 Atmel Corporation
0005  *
0006  * This program is free software; you can redistribute it and/or modify
0007  * it under the terms of the GNU General Public License version 2 as
0008  * published by the Free Software Foundation.
0009  *
0010  * Author: Ronny Pedersen
0011  */
0012 #include <linux/errno.h>
0013 #include <linux/interrupt.h>
0014 #include <linux/irq.h>
0015 #include <linux/oprofile.h>
0016 #include <linux/sched.h>
0017 #include <linux/types.h>
0018 
0019 #include <asm/sysreg.h>
0020 
0021 #define AVR32_PERFCTR_IRQ_GROUP 0
0022 #define AVR32_PERFCTR_IRQ_LINE  1
0023 
0024 void avr32_backtrace(struct pt_regs * const regs, unsigned int depth);
0025 
0026 enum { PCCNT, PCNT0, PCNT1, NR_counter };
0027 
0028 struct avr32_perf_counter {
0029     unsigned long   enabled;
0030     unsigned long   event;
0031     unsigned long   count;
0032     unsigned long   unit_mask;
0033     unsigned long   kernel;
0034     unsigned long   user;
0035 
0036     u32     ie_mask;
0037     u32     flag_mask;
0038 };
0039 
0040 static struct avr32_perf_counter counter[NR_counter] = {
0041     {
0042         .ie_mask    = SYSREG_BIT(IEC),
0043         .flag_mask  = SYSREG_BIT(FC),
0044     }, {
0045         .ie_mask    = SYSREG_BIT(IE0),
0046         .flag_mask  = SYSREG_BIT(F0),
0047     }, {
0048         .ie_mask    = SYSREG_BIT(IE1),
0049         .flag_mask  = SYSREG_BIT(F1),
0050     },
0051 };
0052 
0053 static void avr32_perf_counter_reset(void)
0054 {
0055     /* Reset all counter and disable/clear all interrupts */
0056     sysreg_write(PCCR, (SYSREG_BIT(PCCR_R)
0057                 | SYSREG_BIT(PCCR_C)
0058                 | SYSREG_BIT(FC)
0059                 | SYSREG_BIT(F0)
0060                 | SYSREG_BIT(F1)));
0061 }
0062 
0063 static irqreturn_t avr32_perf_counter_interrupt(int irq, void *dev_id)
0064 {
0065     struct avr32_perf_counter *ctr = dev_id;
0066     struct pt_regs *regs;
0067     u32 pccr;
0068 
0069     if (likely(!(intc_get_pending(AVR32_PERFCTR_IRQ_GROUP)
0070                     & (1 << AVR32_PERFCTR_IRQ_LINE))))
0071         return IRQ_NONE;
0072 
0073     regs = get_irq_regs();
0074     pccr = sysreg_read(PCCR);
0075 
0076     /* Clear the interrupt flags we're about to handle */
0077     sysreg_write(PCCR, pccr);
0078 
0079     /* PCCNT */
0080     if (ctr->enabled && (pccr & ctr->flag_mask)) {
0081         sysreg_write(PCCNT, -ctr->count);
0082         oprofile_add_sample(regs, PCCNT);
0083     }
0084     ctr++;
0085     /* PCNT0 */
0086     if (ctr->enabled && (pccr & ctr->flag_mask)) {
0087         sysreg_write(PCNT0, -ctr->count);
0088         oprofile_add_sample(regs, PCNT0);
0089     }
0090     ctr++;
0091     /* PCNT1 */
0092     if (ctr->enabled && (pccr & ctr->flag_mask)) {
0093         sysreg_write(PCNT1, -ctr->count);
0094         oprofile_add_sample(regs, PCNT1);
0095     }
0096 
0097     return IRQ_HANDLED;
0098 }
0099 
0100 static int avr32_perf_counter_create_files(struct dentry *root)
0101 {
0102     struct dentry *dir;
0103     unsigned int i;
0104     char filename[4];
0105 
0106     for (i = 0; i < NR_counter; i++) {
0107         snprintf(filename, sizeof(filename), "%u", i);
0108         dir = oprofilefs_mkdir(root, filename);
0109 
0110         oprofilefs_create_ulong(dir, "enabled",
0111                 &counter[i].enabled);
0112         oprofilefs_create_ulong(dir, "event",
0113                 &counter[i].event);
0114         oprofilefs_create_ulong(dir, "count",
0115                 &counter[i].count);
0116 
0117         /* Dummy entries */
0118         oprofilefs_create_ulong(dir, "kernel",
0119                 &counter[i].kernel);
0120         oprofilefs_create_ulong(dir, "user",
0121                 &counter[i].user);
0122         oprofilefs_create_ulong(dir, "unit_mask",
0123                 &counter[i].unit_mask);
0124     }
0125 
0126     return 0;
0127 }
0128 
0129 static int avr32_perf_counter_setup(void)
0130 {
0131     struct avr32_perf_counter *ctr;
0132     u32 pccr;
0133     int ret;
0134     int i;
0135 
0136     pr_debug("avr32_perf_counter_setup\n");
0137 
0138     if (sysreg_read(PCCR) & SYSREG_BIT(PCCR_E)) {
0139         printk(KERN_ERR
0140             "oprofile: setup: perf counter already enabled\n");
0141         return -EBUSY;
0142     }
0143 
0144     ret = request_irq(AVR32_PERFCTR_IRQ_GROUP,
0145             avr32_perf_counter_interrupt, IRQF_SHARED,
0146             "oprofile", counter);
0147     if (ret)
0148         return ret;
0149 
0150     avr32_perf_counter_reset();
0151 
0152     pccr = 0;
0153     for (i = PCCNT; i < NR_counter; i++) {
0154         ctr = &counter[i];
0155         if (!ctr->enabled)
0156             continue;
0157 
0158         pr_debug("enabling counter %d...\n", i);
0159 
0160         pccr |= ctr->ie_mask;
0161 
0162         switch (i) {
0163         case PCCNT:
0164             /* PCCNT always counts cycles, so no events */
0165             sysreg_write(PCCNT, -ctr->count);
0166             break;
0167         case PCNT0:
0168             pccr |= SYSREG_BF(CONF0, ctr->event);
0169             sysreg_write(PCNT0, -ctr->count);
0170             break;
0171         case PCNT1:
0172             pccr |= SYSREG_BF(CONF1, ctr->event);
0173             sysreg_write(PCNT1, -ctr->count);
0174             break;
0175         }
0176     }
0177 
0178     pr_debug("oprofile: writing 0x%x to PCCR...\n", pccr);
0179 
0180     sysreg_write(PCCR, pccr);
0181 
0182     return 0;
0183 }
0184 
0185 static void avr32_perf_counter_shutdown(void)
0186 {
0187     pr_debug("avr32_perf_counter_shutdown\n");
0188 
0189     avr32_perf_counter_reset();
0190     free_irq(AVR32_PERFCTR_IRQ_GROUP, counter);
0191 }
0192 
0193 static int avr32_perf_counter_start(void)
0194 {
0195     pr_debug("avr32_perf_counter_start\n");
0196 
0197     sysreg_write(PCCR, sysreg_read(PCCR) | SYSREG_BIT(PCCR_E));
0198 
0199     return 0;
0200 }
0201 
0202 static void avr32_perf_counter_stop(void)
0203 {
0204     pr_debug("avr32_perf_counter_stop\n");
0205 
0206     sysreg_write(PCCR, sysreg_read(PCCR) & ~SYSREG_BIT(PCCR_E));
0207 }
0208 
0209 static struct oprofile_operations avr32_perf_counter_ops __initdata = {
0210     .create_files   = avr32_perf_counter_create_files,
0211     .setup      = avr32_perf_counter_setup,
0212     .shutdown   = avr32_perf_counter_shutdown,
0213     .start      = avr32_perf_counter_start,
0214     .stop       = avr32_perf_counter_stop,
0215     .cpu_type   = "avr32",
0216 };
0217 
0218 int __init oprofile_arch_init(struct oprofile_operations *ops)
0219 {
0220     if (!(current_cpu_data.features & AVR32_FEATURE_PCTR))
0221         return -ENODEV;
0222 
0223     memcpy(ops, &avr32_perf_counter_ops,
0224             sizeof(struct oprofile_operations));
0225 
0226     ops->backtrace = avr32_backtrace;
0227 
0228     printk(KERN_INFO "oprofile: using AVR32 performance monitoring.\n");
0229 
0230     return 0;
0231 }
0232 
0233 void oprofile_arch_exit(void)
0234 {
0235 
0236 }