Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
op_model_avr32.c
Go to the documentation of this file.
1 /*
2  * AVR32 Performance Counter Driver
3  *
4  * Copyright (C) 2005-2007 Atmel Corporation
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 as
8  * published by the Free Software Foundation.
9  *
10  * Author: Ronny Pedersen
11  */
12 #include <linux/errno.h>
13 #include <linux/interrupt.h>
14 #include <linux/irq.h>
15 #include <linux/oprofile.h>
16 #include <linux/sched.h>
17 #include <linux/types.h>
18 
19 #include <asm/sysreg.h>
20 
21 #define AVR32_PERFCTR_IRQ_GROUP 0
22 #define AVR32_PERFCTR_IRQ_LINE 1
23 
24 void avr32_backtrace(struct pt_regs * const regs, unsigned int depth);
25 
27 
29  unsigned long enabled;
30  unsigned long event;
31  unsigned long count;
32  unsigned long unit_mask;
33  unsigned long kernel;
34  unsigned long user;
35 
38 };
39 
40 static struct avr32_perf_counter counter[NR_counter] = {
41  {
42  .ie_mask = SYSREG_BIT(IEC),
43  .flag_mask = SYSREG_BIT(FC),
44  }, {
45  .ie_mask = SYSREG_BIT(IE0),
46  .flag_mask = SYSREG_BIT(F0),
47  }, {
48  .ie_mask = SYSREG_BIT(IE1),
49  .flag_mask = SYSREG_BIT(F1),
50  },
51 };
52 
53 static void avr32_perf_counter_reset(void)
54 {
55  /* Reset all counter and disable/clear all interrupts */
56  sysreg_write(PCCR, (SYSREG_BIT(PCCR_R)
57  | SYSREG_BIT(PCCR_C)
58  | SYSREG_BIT(FC)
59  | SYSREG_BIT(F0)
60  | SYSREG_BIT(F1)));
61 }
62 
63 static irqreturn_t avr32_perf_counter_interrupt(int irq, void *dev_id)
64 {
65  struct avr32_perf_counter *ctr = dev_id;
66  struct pt_regs *regs;
67  u32 pccr;
68 
70  & (1 << AVR32_PERFCTR_IRQ_LINE))))
71  return IRQ_NONE;
72 
73  regs = get_irq_regs();
74  pccr = sysreg_read(PCCR);
75 
76  /* Clear the interrupt flags we're about to handle */
77  sysreg_write(PCCR, pccr);
78 
79  /* PCCNT */
80  if (ctr->enabled && (pccr & ctr->flag_mask)) {
81  sysreg_write(PCCNT, -ctr->count);
83  }
84  ctr++;
85  /* PCNT0 */
86  if (ctr->enabled && (pccr & ctr->flag_mask)) {
87  sysreg_write(PCNT0, -ctr->count);
89  }
90  ctr++;
91  /* PCNT1 */
92  if (ctr->enabled && (pccr & ctr->flag_mask)) {
93  sysreg_write(PCNT1, -ctr->count);
95  }
96 
97  return IRQ_HANDLED;
98 }
99 
100 static int avr32_perf_counter_create_files(struct super_block *sb,
101  struct dentry *root)
102 {
103  struct dentry *dir;
104  unsigned int i;
105  char filename[4];
106 
107  for (i = 0; i < NR_counter; i++) {
108  snprintf(filename, sizeof(filename), "%u", i);
109  dir = oprofilefs_mkdir(sb, root, filename);
110 
111  oprofilefs_create_ulong(sb, dir, "enabled",
112  &counter[i].enabled);
113  oprofilefs_create_ulong(sb, dir, "event",
114  &counter[i].event);
115  oprofilefs_create_ulong(sb, dir, "count",
116  &counter[i].count);
117 
118  /* Dummy entries */
119  oprofilefs_create_ulong(sb, dir, "kernel",
120  &counter[i].kernel);
121  oprofilefs_create_ulong(sb, dir, "user",
122  &counter[i].user);
123  oprofilefs_create_ulong(sb, dir, "unit_mask",
124  &counter[i].unit_mask);
125  }
126 
127  return 0;
128 }
129 
130 static int avr32_perf_counter_setup(void)
131 {
132  struct avr32_perf_counter *ctr;
133  u32 pccr;
134  int ret;
135  int i;
136 
137  pr_debug("avr32_perf_counter_setup\n");
138 
139  if (sysreg_read(PCCR) & SYSREG_BIT(PCCR_E)) {
141  "oprofile: setup: perf counter already enabled\n");
142  return -EBUSY;
143  }
144 
146  avr32_perf_counter_interrupt, IRQF_SHARED,
147  "oprofile", counter);
148  if (ret)
149  return ret;
150 
151  avr32_perf_counter_reset();
152 
153  pccr = 0;
154  for (i = PCCNT; i < NR_counter; i++) {
155  ctr = &counter[i];
156  if (!ctr->enabled)
157  continue;
158 
159  pr_debug("enabling counter %d...\n", i);
160 
161  pccr |= ctr->ie_mask;
162 
163  switch (i) {
164  case PCCNT:
165  /* PCCNT always counts cycles, so no events */
166  sysreg_write(PCCNT, -ctr->count);
167  break;
168  case PCNT0:
169  pccr |= SYSREG_BF(CONF0, ctr->event);
170  sysreg_write(PCNT0, -ctr->count);
171  break;
172  case PCNT1:
173  pccr |= SYSREG_BF(CONF1, ctr->event);
174  sysreg_write(PCNT1, -ctr->count);
175  break;
176  }
177  }
178 
179  pr_debug("oprofile: writing 0x%x to PCCR...\n", pccr);
180 
181  sysreg_write(PCCR, pccr);
182 
183  return 0;
184 }
185 
186 static void avr32_perf_counter_shutdown(void)
187 {
188  pr_debug("avr32_perf_counter_shutdown\n");
189 
190  avr32_perf_counter_reset();
192 }
193 
194 static int avr32_perf_counter_start(void)
195 {
196  pr_debug("avr32_perf_counter_start\n");
197 
199 
200  return 0;
201 }
202 
203 static void avr32_perf_counter_stop(void)
204 {
205  pr_debug("avr32_perf_counter_stop\n");
206 
208 }
209 
210 static struct oprofile_operations avr32_perf_counter_ops __initdata = {
211  .create_files = avr32_perf_counter_create_files,
212  .setup = avr32_perf_counter_setup,
213  .shutdown = avr32_perf_counter_shutdown,
214  .start = avr32_perf_counter_start,
215  .stop = avr32_perf_counter_stop,
216  .cpu_type = "avr32",
217 };
218 
220 {
221  if (!(current_cpu_data.features & AVR32_FEATURE_PCTR))
222  return -ENODEV;
223 
224  memcpy(ops, &avr32_perf_counter_ops,
225  sizeof(struct oprofile_operations));
226 
227  ops->backtrace = avr32_backtrace;
228 
229  printk(KERN_INFO "oprofile: using AVR32 performance monitoring.\n");
230 
231  return 0;
232 }
233 
235 {
236 
237 }