File: /usr/src/linux/arch/ia64/sn/sn1/synergy.c
1
2 /*
3 * SN1 Platform specific synergy Support
4 *
5 * Copyright (C) 2000 Silicon Graphics, Inc.
6 * Copyright (C) 2000 Alan Mayer (ajm@sgi.com)
7 */
8
9
10
11 #include <linux/config.h>
12 #include <linux/kernel.h>
13 #include <linux/sched.h>
14 #include <linux/mm.h>
15 #include <linux/spinlock.h>
16 #include <linux/proc_fs.h>
17
18 #include <asm/ptrace.h>
19 #include <linux/devfs_fs_kernel.h>
20 #include <asm/smp.h>
21 #include <asm/sn/sn_cpuid.h>
22 #include <asm/sn/sn1/bedrock.h>
23 #include <asm/sn/intr.h>
24 #include <asm/sn/addrs.h>
25 #include <asm/sn/synergy.h>
26
27 int bit_pos_to_irq(int bit);
28 void setclear_mask_b(int irq, int cpuid, int set);
29 void setclear_mask_a(int irq, int cpuid, int set);
30 void * kmalloc(size_t size, int flags);
31
32
33 void
34 synergy_intr_alloc(int bit, int cpuid) {
35 return;
36 }
37
38 int
39 synergy_intr_connect(int bit,
40 int cpuid)
41 {
42 int irq;
43 unsigned is_b;
44
45 irq = bit_pos_to_irq(bit);
46
47 is_b = (cpuid_to_slice(cpuid)) & 1;
48 if (is_b) {
49 setclear_mask_b(irq,cpuid,1);
50 setclear_mask_a(irq,cpuid, 0);
51 } else {
52 setclear_mask_a(irq, cpuid, 1);
53 setclear_mask_b(irq, cpuid, 0);
54 }
55 return 0;
56 }
57 void
58 setclear_mask_a(int irq, int cpuid, int set)
59 {
60 int synergy;
61 int nasid;
62 int reg_num;
63 unsigned long mask;
64 unsigned long addr;
65 unsigned long reg;
66 unsigned long val;
67 int my_cnode, my_synergy;
68 int target_cnode, target_synergy;
69
70 /*
71 * Perform some idiot checks ..
72 */
73 if ( (irq < 0) || (irq > 255) ||
74 (cpuid < 0) || (cpuid > 512) ) {
75 printk("clear_mask_a: Invalid parameter irq %d cpuid %d\n", irq, cpuid);
76 return;
77 }
78
79 target_cnode = cpuid_to_cnodeid(cpuid);
80 target_synergy = cpuid_to_synergy(cpuid);
81 my_cnode = cpuid_to_cnodeid(smp_processor_id());
82 my_synergy = cpuid_to_synergy(smp_processor_id());
83
84 reg_num = irq / 64;
85 mask = 1;
86 mask <<= (irq % 64);
87 switch (reg_num) {
88 case 0:
89 reg = VEC_MASK0A;
90 addr = VEC_MASK0A_ADDR;
91 break;
92 case 1:
93 reg = VEC_MASK1A;
94 addr = VEC_MASK1A_ADDR;
95 break;
96 case 2:
97 reg = VEC_MASK2A;
98 addr = VEC_MASK2A_ADDR;
99 break;
100 case 3:
101 reg = VEC_MASK3A;
102 addr = VEC_MASK3A_ADDR;
103 break;
104 default:
105 reg = addr = 0;
106 break;
107 }
108 if (my_cnode == target_cnode && my_synergy == target_synergy) {
109 // local synergy
110 val = READ_LOCAL_SYNERGY_REG(addr);
111 if (set) {
112 val |= mask;
113 } else {
114 val &= ~mask;
115 }
116 WRITE_LOCAL_SYNERGY_REG(addr, val);
117 val = READ_LOCAL_SYNERGY_REG(addr);
118 } else { /* remote synergy */
119 synergy = cpuid_to_synergy(cpuid);
120 nasid = cpuid_to_nasid(cpuid);
121 val = REMOTE_SYNERGY_LOAD(nasid, synergy, reg);
122 if (set) {
123 val |= mask;
124 } else {
125 val &= ~mask;
126 }
127 REMOTE_SYNERGY_STORE(nasid, synergy, reg, val);
128 }
129 }
130
131 void
132 setclear_mask_b(int irq, int cpuid, int set)
133 {
134 int synergy;
135 int nasid;
136 int reg_num;
137 unsigned long mask;
138 unsigned long addr;
139 unsigned long reg;
140 unsigned long val;
141 int my_cnode, my_synergy;
142 int target_cnode, target_synergy;
143
144 /*
145 * Perform some idiot checks ..
146 */
147 if ( (irq < 0) || (irq > 255) ||
148 (cpuid < 0) || (cpuid > 512) ) {
149 printk("clear_mask_b: Invalid parameter irq %d cpuid %d\n", irq, cpuid);
150 return;
151 }
152
153 target_cnode = cpuid_to_cnodeid(cpuid);
154 target_synergy = cpuid_to_synergy(cpuid);
155 my_cnode = cpuid_to_cnodeid(smp_processor_id());
156 my_synergy = cpuid_to_synergy(smp_processor_id());
157
158 reg_num = irq / 64;
159 mask = 1;
160 mask <<= (irq % 64);
161 switch (reg_num) {
162 case 0:
163 reg = VEC_MASK0B;
164 addr = VEC_MASK0B_ADDR;
165 break;
166 case 1:
167 reg = VEC_MASK1B;
168 addr = VEC_MASK1B_ADDR;
169 break;
170 case 2:
171 reg = VEC_MASK2B;
172 addr = VEC_MASK2B_ADDR;
173 break;
174 case 3:
175 reg = VEC_MASK3B;
176 addr = VEC_MASK3B_ADDR;
177 break;
178 default:
179 reg = addr = 0;
180 break;
181 }
182 if (my_cnode == target_cnode && my_synergy == target_synergy) {
183 // local synergy
184 val = READ_LOCAL_SYNERGY_REG(addr);
185 if (set) {
186 val |= mask;
187 } else {
188 val &= ~mask;
189 }
190 WRITE_LOCAL_SYNERGY_REG(addr, val);
191 val = READ_LOCAL_SYNERGY_REG(addr);
192 } else { /* remote synergy */
193 synergy = cpuid_to_synergy(cpuid);
194 nasid = cpuid_to_nasid(cpuid);
195 val = REMOTE_SYNERGY_LOAD(nasid, synergy, reg);
196 if (set) {
197 val |= mask;
198 } else {
199 val &= ~mask;
200 }
201 REMOTE_SYNERGY_STORE(nasid, synergy, reg, val);
202 }
203 }
204
205 #if defined(CONFIG_IA64_SGI_SYNERGY_PERF)
206
207 /*
208 * Synergy perf registers. Multiplexed via timer_interrupt
209 */
210 static struct proc_dir_entry *synergy_perf_proc = NULL;
211
212 /*
213 * read handler for /proc/synergy
214 */
215 static int
216 synergy_perf_read_proc (char *page, char **start, off_t off,
217 int count, int *eof, void *data)
218 {
219 cnodeid_t cnode;
220 nodepda_t *npdap;
221 synergy_perf_t *p;
222 int len = 0;
223
224 len += sprintf(page+len, "# cnode module slot event synergy-A synergy-B\n");
225
226 /* walk the event list for each node */
227 for (cnode=0; cnode < numnodes; cnode++) {
228 npdap = NODEPDA(cnode);
229 if (npdap->synergy_perf_enabled == 0) {
230 len += sprintf(page+len, "# DISABLED\n");
231 break;
232 }
233
234 spin_lock_irq(&npdap->synergy_perf_lock);
235 for (p = npdap->synergy_perf_first; p;) {
236 uint64_t cnt_a=0, cnt_b=0;
237
238 if (p->intervals > 0) {
239 cnt_a = p->counts[0] * npdap->synergy_active_intervals / p->intervals;
240 cnt_b = p->counts[1] * npdap->synergy_active_intervals / p->intervals;
241 }
242
243 len += sprintf(page+len, "%d %d %d %12lx %lu %lu\n",
244 (int)cnode, (int)npdap->module_id, (int)npdap->slotdesc,
245 p->modesel, cnt_a, cnt_b);
246
247 p = p->next;
248 if (p == npdap->synergy_perf_first)
249 break;
250 }
251 spin_unlock_irq(&npdap->synergy_perf_lock);
252 }
253
254 if (len <= off+count) *eof = 1;
255 *start = page + off;
256 len -= off;
257 if (len>count) len = count;
258 if (len<0) len = 0;
259
260 return len;
261 }
262
263 static int
264 synergy_perf_append(uint64_t modesel)
265 {
266 int cnode;
267 nodepda_t *npdap;
268 synergy_perf_t *p;
269 int err = 0;
270
271 /* bit 45 is enable */
272 modesel |= (1UL << 45);
273
274 for (cnode=0; cnode < numnodes; cnode++) {
275 /* for each node, insert a new synergy_perf entry */
276 if ((npdap = NODEPDA(cnode)) == NULL) {
277 printk("synergy_perf_append: cnode=%d NODEPDA(cnode)==NULL, nodepda=%p\n", cnode, nodepda);
278 continue;
279 }
280
281 /* XX use kmem_alloc_node() when it is implemented */
282 p = (synergy_perf_t *)kmalloc(sizeof(synergy_perf_t), GFP_KERNEL);
283 if (p == NULL)
284 err = -ENOMEM;
285 else {
286 memset(p, 0, sizeof(synergy_perf_t));
287 p->modesel = modesel;
288 if (npdap->synergy_perf_data == NULL) {
289 /* circular list */
290 p->next = p;
291 npdap->synergy_perf_data = p;
292 npdap->synergy_perf_first = p;
293 }
294 else {
295 /*
296 * Jumble up the insertion order so we get better sampling.
297 * Once the list is complete, "first" stays the same so the
298 * reporting order is consistent.
299 */
300 p->next = npdap->synergy_perf_first->next;
301 npdap->synergy_perf_first->next = p;
302 npdap->synergy_perf_first = p->next;
303 }
304 }
305 }
306
307 return err;
308 }
309
310 static int
311 synergy_perf_write_proc (struct file *file, const char *buffer,
312 unsigned long count, void *data)
313 {
314 int cnode;
315 nodepda_t *npdap;
316 uint64_t modesel;
317 char cmd[64];
318 extern long atoi(char *);
319
320 if (count == sizeof(uint64_t)) {
321 if (copy_from_user(&modesel, buffer, sizeof(uint64_t)))
322 return -EFAULT;
323 synergy_perf_append(modesel);
324 }
325 else {
326 if (copy_from_user(cmd, buffer, count < sizeof(cmd) ? count : sizeof(cmd)))
327 return -EFAULT;
328 if (strncmp(cmd, "enable", 6) == 0) {
329 /* enable counting */
330 for (cnode=0; cnode < numnodes; cnode++) {
331 npdap = NODEPDA(cnode);
332 npdap->synergy_perf_enabled = 1;
333 }
334 printk("NOTICE: synergy perf counting enabled\n");
335 }
336 else
337 if (strncmp(cmd, "disable", 7) == 0) {
338 /* disable counting */
339 for (cnode=0; cnode < numnodes; cnode++) {
340 npdap = NODEPDA(cnode);
341 npdap->synergy_perf_enabled = 0;
342 }
343 printk("NOTICE: synergy perf counting disabled\n");
344 }
345 else
346 if (strncmp(cmd, "frequency", 9) == 0) {
347 /* set the update frequency (timer-interrupts per update) */
348 int freq;
349
350 if (count < 12)
351 return -EINVAL;
352 freq = atoi(cmd + 10);
353 if (freq <= 0 || freq > 100)
354 return -EINVAL;
355 for (cnode=0; cnode < numnodes; cnode++) {
356 npdap = NODEPDA(cnode);
357 npdap->synergy_perf_freq = (uint64_t)freq;
358 }
359 printk("NOTICE: synergy perf freq set to %d\n", freq);
360 }
361 else
362 return -EINVAL;
363 }
364
365 return count;
366 }
367
368 void
369 synergy_perf_update(int cpu)
370 {
371 nasid_t nasid;
372 cnodeid_t cnode = cpuid_to_cnodeid(cpu);
373 struct nodepda_s *npdap;
374 extern struct nodepda_s *nodepda;
375
376 if (nodepda == NULL || (npdap=NODEPDA(cnode)) == NULL || npdap->synergy_perf_enabled == 0 ||
377 npdap->synergy_perf_data == NULL) {
378 /* I/O not initialized, or not enabled, or no events to monitor */
379 return;
380 }
381
382 if (npdap->synergy_inactive_intervals++ % npdap->synergy_perf_freq != 0) {
383 /* don't multiplex on every timer interrupt */
384 return;
385 }
386
387 /*
388 * Read registers for last interval and increment counters.
389 * Hold the per-node synergy_perf_lock so concurrent readers get
390 * consistent values.
391 */
392 spin_lock_irq(&npdap->synergy_perf_lock);
393
394 nasid = cpuid_to_nasid(cpu);
395 npdap->synergy_active_intervals++;
396 npdap->synergy_perf_data->intervals++;
397
398 npdap->synergy_perf_data->counts[0] += 0xffffffffffUL &
399 REMOTE_SYNERGY_LOAD(nasid, 0, PERF_CNTR0_A);
400
401 npdap->synergy_perf_data->counts[1] += 0xffffffffffUL &
402 REMOTE_SYNERGY_LOAD(nasid, 1, PERF_CNTR0_B);
403
404 /* skip to next in circular list */
405 npdap->synergy_perf_data = npdap->synergy_perf_data->next;
406
407 spin_unlock_irq(&npdap->synergy_perf_lock);
408
409 /* set the counter 0 selection modes for both A and B */
410 REMOTE_SYNERGY_STORE(nasid, 0, PERF_CNTL0_A, npdap->synergy_perf_data->modesel);
411 REMOTE_SYNERGY_STORE(nasid, 1, PERF_CNTL0_B, npdap->synergy_perf_data->modesel);
412
413 /* and reset the counter registers to zero */
414 REMOTE_SYNERGY_STORE(nasid, 0, PERF_CNTR0_A, 0UL);
415 REMOTE_SYNERGY_STORE(nasid, 1, PERF_CNTR0_B, 0UL);
416 }
417
418 void
419 synergy_perf_init(void)
420 {
421 if ((synergy_perf_proc = create_proc_entry("synergy", 0644, NULL)) != NULL) {
422 synergy_perf_proc->read_proc = synergy_perf_read_proc;
423 synergy_perf_proc->write_proc = synergy_perf_write_proc;
424 printk("markgw: synergy_perf_init()\n");
425 }
426 }
427
428 #endif /* CONFIG_IA64_SGI_SYNERGY_PERF */
429
430