2 * SN1 Platform specific synergy Support
4 * Copyright (C) 2000-2002 Silicon Graphics, Inc. All rights reserved.
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of version 2 of the GNU General Public License
8 * as published by the Free Software Foundation.
10 * This program is distributed in the hope that it would be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14 * Further, this software is distributed without any warranty that it is
15 * free of the rightful claim of any third person regarding infringement
16 * or the like. Any license provided herein, whether implied or
17 * otherwise, applies only to this software file. Patent licenses, if
18 * any, provided herein do not apply to combinations of this program with
19 * other software, or any other product whatsoever.
21 * You should have received a copy of the GNU General Public
22 * License along with this program; if not, write the Free Software
23 * Foundation, Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
25 * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
26 * Mountain View, CA 94043, or:
30 * For further information regarding this notice, see:
32 * http://oss.sgi.com/projects/GenInfo/NoticeExplan
35 #include <linux/kernel.h>
36 #include <linux/sched.h>
38 #include <linux/spinlock.h>
39 #include <linux/proc_fs.h>
41 #include <asm/ptrace.h>
42 #include <linux/devfs_fs_kernel.h>
44 #include <asm/sn/sn_cpuid.h>
45 #include <asm/sn/sn1/bedrock.h>
46 #include <asm/sn/intr.h>
47 #include <asm/sn/addrs.h>
48 #include <asm/sn/nodepda.h>
49 #include <asm/sn/sn1/synergy.h>
50 #include <asm/sn/sndrv.h>
52 int bit_pos_to_irq(int bit);
53 void setclear_mask_b(int irq, int cpuid, int set);
54 void setclear_mask_a(int irq, int cpuid, int set);
55 void * kmalloc(size_t size, int flags);
57 static int synergy_perf_initialized = 0;
60 synergy_intr_alloc(int bit, int cpuid) {
65 synergy_intr_connect(int bit,
71 irq = bit_pos_to_irq(bit);
73 is_b = (cpuid_to_slice(cpuid)) & 1;
75 setclear_mask_b(irq,cpuid,1);
76 setclear_mask_a(irq,cpuid, 0);
78 setclear_mask_a(irq, cpuid, 1);
79 setclear_mask_b(irq, cpuid, 0);
84 setclear_mask_a(int irq, int cpuid, int set)
93 int my_cnode, my_synergy;
94 int target_cnode, target_synergy;
97 * Perform some idiot checks ..
99 if ( (irq < 0) || (irq > 255) ||
100 (cpuid < 0) || (cpuid > 512) ) {
101 printk("clear_mask_a: Invalid parameter irq %d cpuid %d\n", irq, cpuid);
105 target_cnode = cpuid_to_cnodeid(cpuid);
106 target_synergy = cpuid_to_synergy(cpuid);
107 my_cnode = cpuid_to_cnodeid(smp_processor_id());
108 my_synergy = cpuid_to_synergy(smp_processor_id());
116 addr = VEC_MASK0A_ADDR;
120 addr = VEC_MASK1A_ADDR;
124 addr = VEC_MASK2A_ADDR;
128 addr = VEC_MASK3A_ADDR;
134 if (my_cnode == target_cnode && my_synergy == target_synergy) {
136 val = READ_LOCAL_SYNERGY_REG(addr);
142 WRITE_LOCAL_SYNERGY_REG(addr, val);
143 val = READ_LOCAL_SYNERGY_REG(addr);
144 } else { /* remote synergy */
145 synergy = cpuid_to_synergy(cpuid);
146 nasid = cpuid_to_nasid(cpuid);
147 val = REMOTE_SYNERGY_LOAD(nasid, synergy, reg);
153 REMOTE_SYNERGY_STORE(nasid, synergy, reg, val);
158 setclear_mask_b(int irq, int cpuid, int set)
167 int my_cnode, my_synergy;
168 int target_cnode, target_synergy;
171 * Perform some idiot checks ..
173 if ( (irq < 0) || (irq > 255) ||
174 (cpuid < 0) || (cpuid > 512) ) {
175 printk("clear_mask_b: Invalid parameter irq %d cpuid %d\n", irq, cpuid);
179 target_cnode = cpuid_to_cnodeid(cpuid);
180 target_synergy = cpuid_to_synergy(cpuid);
181 my_cnode = cpuid_to_cnodeid(smp_processor_id());
182 my_synergy = cpuid_to_synergy(smp_processor_id());
190 addr = VEC_MASK0B_ADDR;
194 addr = VEC_MASK1B_ADDR;
198 addr = VEC_MASK2B_ADDR;
202 addr = VEC_MASK3B_ADDR;
208 if (my_cnode == target_cnode && my_synergy == target_synergy) {
210 val = READ_LOCAL_SYNERGY_REG(addr);
216 WRITE_LOCAL_SYNERGY_REG(addr, val);
217 val = READ_LOCAL_SYNERGY_REG(addr);
218 } else { /* remote synergy */
219 synergy = cpuid_to_synergy(cpuid);
220 nasid = cpuid_to_nasid(cpuid);
221 val = REMOTE_SYNERGY_LOAD(nasid, synergy, reg);
227 REMOTE_SYNERGY_STORE(nasid, synergy, reg, val);
232 * Synergy perf stats. Multiplexed via timer_interrupt.
236 synergy_perf_append(uint64_t modesel)
245 /* bit 45 is enable */
246 modesel |= (1UL << 45);
248 for (cnode=0; cnode < numnodes; cnode++) {
249 /* for each node, insert a new synergy_perf entry */
250 if ((npdap = NODEPDA(cnode)) == NULL) {
251 printk("synergy_perf_append: cnode=%d NODEPDA(cnode)==NULL, nodepda=%p\n", cnode, (void *)nodepda);
255 if (npdap->synergy_perf_enabled) {
256 /* user must disable counting to append new events */
261 if (!checked && npdap->synergy_perf_data != NULL) {
263 for (p = npdap->synergy_perf_first; ;) {
264 if (p->modesel == modesel)
265 return 0; /* event already registered */
266 if ((p = p->next) == npdap->synergy_perf_first)
271 /* XX use kmem_alloc_node() when it is implemented */
272 p = (synergy_perf_t *)kmalloc(sizeof(synergy_perf_t), GFP_KERNEL);
273 if ((((uint64_t)p) & 7UL) != 0)
274 BUG(); /* bad alignment */
280 memset(p, 0, sizeof(synergy_perf_t));
281 p->modesel = modesel;
283 spin_lock_irqsave(&npdap->synergy_perf_lock, flags);
284 if (npdap->synergy_perf_data == NULL) {
287 npdap->synergy_perf_first = p;
288 npdap->synergy_perf_data = p;
291 p->next = npdap->synergy_perf_data->next;
292 npdap->synergy_perf_data->next = p;
294 spin_unlock_irqrestore(&npdap->synergy_perf_lock, flags);
302 synergy_perf_set_freq(int freq)
307 for (cnode=0; cnode < numnodes; cnode++) {
308 if ((npdap = NODEPDA(cnode)) != NULL)
309 npdap->synergy_perf_freq = freq;
314 synergy_perf_set_enable(int enable)
319 for (cnode=0; cnode < numnodes; cnode++) {
320 if ((npdap = NODEPDA(cnode)) != NULL)
321 npdap->synergy_perf_enabled = enable;
323 printk("NOTICE: synergy perf counting %sabled on all nodes\n", enable ? "en" : "dis");
327 synergy_perf_size(nodepda_t *npdap)
332 if (npdap->synergy_perf_enabled == 0) {
333 /* no stats to return */
337 spin_lock_irq(&npdap->synergy_perf_lock);
338 for (n=0, p = npdap->synergy_perf_first; p;) {
341 if (p == npdap->synergy_perf_first)
344 spin_unlock_irq(&npdap->synergy_perf_lock);
346 /* bytes == n pairs of {event,counter} */
347 return n * 2 * sizeof(uint64_t);
351 synergy_perf_ioctl(struct inode *inode, struct file *file,
352 unsigned int cmd, unsigned long arg)
363 arbitrary_info_t info;
365 if ((d = devfs_get_handle_from_inode(inode)) == NULL)
367 info = hwgraph_fastinfo_get(d);
369 cnode = SYNERGY_PERF_INFO_CNODE(info);
370 fsb = SYNERGY_PERF_INFO_FSB(info);
371 npdap = NODEPDA(cnode);
374 case SNDRV_GET_SYNERGY_VERSION:
375 /* return int, version of data structure for SNDRV_GET_SYNERGYINFO */
376 intarg = 1; /* version 1 */
377 if (copy_to_user((void *)arg, &intarg, sizeof(intarg)))
381 case SNDRV_GET_INFOSIZE:
382 /* return int, sizeof buf needed for SYNERGY_PERF_GET_STATS */
383 intarg = synergy_perf_size(npdap);
384 if (copy_to_user((void *)arg, &intarg, sizeof(intarg)))
388 case SNDRV_GET_SYNERGYINFO:
389 /* return array of event/value pairs, this node only */
390 if ((intarg = synergy_perf_size(npdap)) <= 0)
392 if ((stats = (uint64_t *)kmalloc(intarg, GFP_KERNEL)) == NULL)
394 spin_lock_irq(&npdap->synergy_perf_lock);
395 for (n=0, p = npdap->synergy_perf_first; p;) {
396 stats[n++] = p->modesel;
397 if (p->intervals > 0)
398 stats[n++] = p->counts[fsb] * p->total_intervals / p->intervals;
402 if (p == npdap->synergy_perf_first)
405 spin_unlock_irq(&npdap->synergy_perf_lock);
407 if (copy_to_user((void *)arg, stats, intarg)) {
415 case SNDRV_SYNERGY_APPEND:
416 /* reads 64bit event, append synergy perf event to all nodes */
417 if (copy_from_user(&longarg, (void *)arg, sizeof(longarg)))
419 return synergy_perf_append(longarg);
422 case SNDRV_GET_SYNERGY_STATUS:
423 /* return int, 1 if enabled else 0 */
424 intarg = npdap->synergy_perf_enabled;
425 if (copy_to_user((void *)arg, &intarg, sizeof(intarg)))
429 case SNDRV_SYNERGY_ENABLE:
430 /* read int, if true enable counting else disable */
431 if (copy_from_user(&intarg, (void *)arg, sizeof(intarg)))
433 synergy_perf_set_enable(intarg);
436 case SNDRV_SYNERGY_FREQ:
437 /* read int, set jiffies per update */
438 if (copy_from_user(&intarg, (void *)arg, sizeof(intarg)))
440 if (intarg < 0 || intarg >= HZ)
442 synergy_perf_set_freq(intarg);
446 printk("Warning: invalid ioctl %d on synergy mon for cnode=%d fsb=%d\n", cmd, cnode, fsb);
452 struct file_operations synergy_mon_fops = {
453 .ioctl = synergy_perf_ioctl,
457 synergy_perf_update(int cpu)
461 struct nodepda_s *npdap;
464 * synergy_perf_initialized is set by synergy_perf_init()
465 * which is called last thing by sn_mp_setup(), i.e. well
466 * after nodepda has been initialized.
468 if (!synergy_perf_initialized)
471 cnode = cpuid_to_cnodeid(cpu);
472 npdap = NODEPDA(cnode);
474 if (npdap == NULL || cnode < 0 || cnode >= numnodes)
475 /* this should not happen: still in early io init */
479 /* use this to check nodepda initialization */
480 if (((uint64_t)npdap) & 0x7) {
481 printk("\nERROR on cpu %d : cnode=%d, npdap == %p, not aligned\n", cpu, cnode, npdap);
486 if (npdap->synergy_perf_enabled == 0 || npdap->synergy_perf_data == NULL) {
487 /* Not enabled, or no events to monitor */
491 if (npdap->synergy_inactive_intervals++ % npdap->synergy_perf_freq != 0) {
492 /* don't multiplex on every timer interrupt */
497 * Read registers for last interval and increment counters.
498 * Hold the per-node synergy_perf_lock so concurrent readers get
501 spin_lock_irq(&npdap->synergy_perf_lock);
503 nasid = cpuid_to_nasid(cpu);
504 npdap->synergy_active_intervals++;
505 npdap->synergy_perf_data->intervals++;
506 npdap->synergy_perf_data->total_intervals = npdap->synergy_active_intervals;
508 npdap->synergy_perf_data->counts[0] += 0xffffffffffUL &
509 REMOTE_SYNERGY_LOAD(nasid, 0, PERF_CNTR0_A);
511 npdap->synergy_perf_data->counts[1] += 0xffffffffffUL &
512 REMOTE_SYNERGY_LOAD(nasid, 1, PERF_CNTR0_B);
514 /* skip to next in circular list */
515 npdap->synergy_perf_data = npdap->synergy_perf_data->next;
517 spin_unlock_irq(&npdap->synergy_perf_lock);
519 /* set the counter 0 selection modes for both A and B */
520 REMOTE_SYNERGY_STORE(nasid, 0, PERF_CNTL0_A, npdap->synergy_perf_data->modesel);
521 REMOTE_SYNERGY_STORE(nasid, 1, PERF_CNTL0_B, npdap->synergy_perf_data->modesel);
523 /* and reset the counter registers to zero */
524 REMOTE_SYNERGY_STORE(nasid, 0, PERF_CNTR0_A, 0UL);
525 REMOTE_SYNERGY_STORE(nasid, 1, PERF_CNTR0_B, 0UL);
529 synergy_perf_init(void)
531 printk("synergy_perf_init(), counting is initially disabled\n");
532 synergy_perf_initialized++;