2.5.70 update
[linux-flexiantxendom0-3.2.10.git] / arch / ia64 / sn / kernel / sn1 / synergy.c
1 /*
2  * SN1 Platform specific synergy Support
3  *
4  * Copyright (C) 2000-2002 Silicon Graphics, Inc. All rights reserved.
5  * 
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.
9  * 
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. 
13  * 
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.
20  * 
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.
24  * 
25  * Contact information:  Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, 
26  * Mountain View, CA  94043, or:
27  * 
28  * http://www.sgi.com 
29  * 
30  * For further information regarding this notice, see: 
31  * 
32  * http://oss.sgi.com/projects/GenInfo/NoticeExplan
33  */
34
35 #include <linux/kernel.h>
36 #include <linux/sched.h>
37 #include <linux/mm.h>
38 #include <linux/spinlock.h>
39 #include <linux/proc_fs.h>
40
41 #include <asm/ptrace.h>
42 #include <linux/devfs_fs_kernel.h>
43 #include <asm/smp.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>
51
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);
56
57 static int synergy_perf_initialized = 0;
58
59 void
60 synergy_intr_alloc(int bit, int cpuid) {
61         return;
62 }
63
64 int
65 synergy_intr_connect(int bit, 
66                 int cpuid)
67 {
68         int irq;
69         unsigned is_b;
70
71         irq = bit_pos_to_irq(bit);
72
73         is_b = (cpuid_to_slice(cpuid)) & 1;
74         if (is_b) {
75                 setclear_mask_b(irq,cpuid,1);
76                 setclear_mask_a(irq,cpuid, 0);
77         } else {
78                 setclear_mask_a(irq, cpuid, 1);
79                 setclear_mask_b(irq, cpuid, 0);
80         }
81         return 0;
82 }
83 void
84 setclear_mask_a(int irq, int cpuid, int set)
85 {
86         int synergy;
87         int nasid;
88         int reg_num;
89         unsigned long mask;
90         unsigned long addr;
91         unsigned long reg;
92         unsigned long val;
93         int my_cnode, my_synergy;
94         int target_cnode, target_synergy;
95
96         /*
97          * Perform some idiot checks ..
98          */
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);
102                 return;
103         }
104
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());
109
110         reg_num = irq / 64;
111         mask = 1;
112         mask <<= (irq % 64);
113         switch (reg_num) {
114                 case 0: 
115                         reg = VEC_MASK0A;
116                         addr = VEC_MASK0A_ADDR;
117                         break;
118                 case 1: 
119                         reg = VEC_MASK1A;
120                         addr = VEC_MASK1A_ADDR;
121                         break;
122                 case 2: 
123                         reg = VEC_MASK2A;
124                         addr = VEC_MASK2A_ADDR;
125                         break;
126                 case 3: 
127                         reg = VEC_MASK3A;
128                         addr = VEC_MASK3A_ADDR;
129                         break;
130                 default:
131                         reg = addr = 0;
132                         break;
133         }
134         if (my_cnode == target_cnode && my_synergy == target_synergy) {
135                 // local synergy
136                 val = READ_LOCAL_SYNERGY_REG(addr);
137                 if (set) {
138                         val |= mask;
139                 } else {
140                         val &= ~mask;
141                 }
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);
148                 if (set) {
149                         val |= mask;
150                 } else {
151                         val &= ~mask;
152                 }
153                 REMOTE_SYNERGY_STORE(nasid, synergy, reg, val);
154         }
155 }
156
157 void
158 setclear_mask_b(int irq, int cpuid, int set)
159 {
160         int synergy;
161         int nasid;
162         int reg_num;
163         unsigned long mask;
164         unsigned long addr;
165         unsigned long reg;
166         unsigned long val;
167         int my_cnode, my_synergy;
168         int target_cnode, target_synergy;
169
170         /*
171          * Perform some idiot checks ..
172          */
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);
176                 return;
177         }
178
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());
183
184         reg_num = irq / 64;
185         mask = 1;
186         mask <<= (irq % 64);
187         switch (reg_num) {
188                 case 0: 
189                         reg = VEC_MASK0B;
190                         addr = VEC_MASK0B_ADDR;
191                         break;
192                 case 1: 
193                         reg = VEC_MASK1B;
194                         addr = VEC_MASK1B_ADDR;
195                         break;
196                 case 2: 
197                         reg = VEC_MASK2B;
198                         addr = VEC_MASK2B_ADDR;
199                         break;
200                 case 3: 
201                         reg = VEC_MASK3B;
202                         addr = VEC_MASK3B_ADDR;
203                         break;
204                 default:
205                         reg = addr = 0;
206                         break;
207         }
208         if (my_cnode == target_cnode && my_synergy == target_synergy) {
209                 // local synergy
210                 val = READ_LOCAL_SYNERGY_REG(addr);
211                 if (set) {
212                         val |= mask;
213                 } else {
214                         val &= ~mask;
215                 }
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);
222                 if (set) {
223                         val |= mask;
224                 } else {
225                         val &= ~mask;
226                 }
227                 REMOTE_SYNERGY_STORE(nasid, synergy, reg, val);
228         }
229 }
230
231 /*
232  * Synergy perf stats. Multiplexed via timer_interrupt.
233  */
234
235 static int
236 synergy_perf_append(uint64_t modesel)
237 {
238         int             cnode;
239         nodepda_t       *npdap;
240         synergy_perf_t  *p;
241         int             checked = 0;
242         int             err = 0;
243         unsigned long   flags;
244
245         /* bit 45 is enable */
246         modesel |= (1UL << 45);
247
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);
252                         continue;
253                 }
254
255                 if (npdap->synergy_perf_enabled) {
256                         /* user must disable counting to append new events */
257                         err = -EBUSY;
258                         break;
259                 }
260
261                 if (!checked && npdap->synergy_perf_data != NULL) {
262                         checked = 1;
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)
267                                         break;
268                         }
269                 }
270
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 */
275                 if (p == NULL) {
276                         err = -ENOMEM;
277                         break;
278                 }
279                 else {
280                         memset(p, 0, sizeof(synergy_perf_t));
281                         p->modesel = modesel;
282
283                         spin_lock_irqsave(&npdap->synergy_perf_lock, flags);
284                         if (npdap->synergy_perf_data == NULL) {
285                                 /* circular list */
286                                 p->next = p;
287                                 npdap->synergy_perf_first = p;
288                                 npdap->synergy_perf_data = p;
289                         }
290                         else {
291                                 p->next = npdap->synergy_perf_data->next;
292                                 npdap->synergy_perf_data->next = p;
293                         }
294                         spin_unlock_irqrestore(&npdap->synergy_perf_lock, flags);
295                 }
296         }
297
298         return err;
299 }
300
301 static void
302 synergy_perf_set_freq(int freq)
303 {
304         int             cnode;
305         nodepda_t       *npdap;
306
307         for (cnode=0; cnode < numnodes; cnode++) {
308                 if ((npdap = NODEPDA(cnode)) != NULL)
309                         npdap->synergy_perf_freq = freq;
310         }
311 }
312
313 static void
314 synergy_perf_set_enable(int enable)
315 {
316         int             cnode;
317         nodepda_t       *npdap;
318
319         for (cnode=0; cnode < numnodes; cnode++) {
320                 if ((npdap = NODEPDA(cnode)) != NULL)
321                         npdap->synergy_perf_enabled = enable;
322         }
323         printk("NOTICE: synergy perf counting %sabled on all nodes\n", enable ? "en" : "dis");
324 }
325
326 static int
327 synergy_perf_size(nodepda_t *npdap)
328 {
329         synergy_perf_t  *p;
330         int             n;
331
332         if (npdap->synergy_perf_enabled == 0) {
333                 /* no stats to return */
334                 return 0;
335         }
336
337         spin_lock_irq(&npdap->synergy_perf_lock);
338         for (n=0, p = npdap->synergy_perf_first; p;) {
339                 n++;
340                 p = p->next;
341                 if (p == npdap->synergy_perf_first)
342                         break;
343         }
344         spin_unlock_irq(&npdap->synergy_perf_lock);
345
346         /* bytes == n pairs of {event,counter} */
347         return n * 2 * sizeof(uint64_t);
348 }
349
350 static int
351 synergy_perf_ioctl(struct inode *inode, struct file *file,
352         unsigned int cmd, unsigned long arg)
353 {
354         int             cnode;
355         nodepda_t       *npdap;
356         synergy_perf_t  *p;
357         int             intarg;
358         int             fsb;
359         uint64_t        longarg;
360         uint64_t        *stats;
361         int             n;
362         devfs_handle_t  d;
363         arbitrary_info_t info;
364         
365         if ((d = devfs_get_handle_from_inode(inode)) == NULL)
366                 return -ENODEV;
367         info = hwgraph_fastinfo_get(d);
368
369         cnode = SYNERGY_PERF_INFO_CNODE(info);
370         fsb = SYNERGY_PERF_INFO_FSB(info);
371         npdap = NODEPDA(cnode);
372
373         switch (cmd) {
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)))
378                     return -EFAULT;
379                 break;
380
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)))
385                     return -EFAULT;
386                 break;
387
388         case SNDRV_GET_SYNERGYINFO:
389                 /* return array of event/value pairs, this node only */
390                 if ((intarg = synergy_perf_size(npdap)) <= 0)
391                         return -ENODATA;
392                 if ((stats = (uint64_t *)kmalloc(intarg, GFP_KERNEL)) == NULL)
393                         return -ENOMEM;
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;
399                         else
400                             stats[n++] = 0;
401                         p = p->next;
402                         if (p == npdap->synergy_perf_first)
403                                 break;
404                 }
405                 spin_unlock_irq(&npdap->synergy_perf_lock);
406
407                 if (copy_to_user((void *)arg, stats, intarg)) {
408                     kfree(stats);
409                     return -EFAULT;
410                 }
411
412                 kfree(stats);
413                 break;
414
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)))
418                     return -EFAULT;
419                 return synergy_perf_append(longarg);
420                 break;
421
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)))
426                     return -EFAULT;
427                 break;
428
429         case SNDRV_SYNERGY_ENABLE:
430                 /* read int, if true enable counting else disable */
431                 if (copy_from_user(&intarg, (void *)arg, sizeof(intarg)))
432                     return -EFAULT;
433                 synergy_perf_set_enable(intarg);
434                 break;
435
436         case SNDRV_SYNERGY_FREQ:
437                 /* read int, set jiffies per update */ 
438                 if (copy_from_user(&intarg, (void *)arg, sizeof(intarg)))
439                     return -EFAULT;
440                 if (intarg < 0 || intarg >= HZ)
441                         return -EINVAL;
442                 synergy_perf_set_freq(intarg);
443                 break;
444
445         default:
446                 printk("Warning: invalid ioctl %d on synergy mon for cnode=%d fsb=%d\n", cmd, cnode, fsb);
447                 return -EINVAL;
448         }
449         return(0);
450 }
451
452 struct file_operations synergy_mon_fops = {
453         .ioctl          = synergy_perf_ioctl,
454 };
455
456 void
457 synergy_perf_update(int cpu)
458 {
459         nasid_t         nasid;
460         cnodeid_t       cnode;
461         struct nodepda_s *npdap;
462
463         /*
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.
467          */
468         if (!synergy_perf_initialized)
469                 return;
470
471         cnode = cpuid_to_cnodeid(cpu);
472         npdap = NODEPDA(cnode);
473
474         if (npdap == NULL || cnode < 0 || cnode >= numnodes)
475                 /* this should not happen: still in early io init */
476                 return;
477
478 #if 0
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);
482                 BUG();
483         }
484 #endif
485
486         if (npdap->synergy_perf_enabled == 0 || npdap->synergy_perf_data == NULL) {
487                 /* Not enabled, or no events to monitor */
488                 return;
489         }
490
491         if (npdap->synergy_inactive_intervals++ % npdap->synergy_perf_freq != 0) {
492                 /* don't multiplex on every timer interrupt */
493                 return;
494         }
495
496         /*
497          * Read registers for last interval and increment counters.
498          * Hold the per-node synergy_perf_lock so concurrent readers get
499          * consistent values.
500          */
501         spin_lock_irq(&npdap->synergy_perf_lock);
502
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;
507
508         npdap->synergy_perf_data->counts[0] += 0xffffffffffUL &
509                 REMOTE_SYNERGY_LOAD(nasid, 0, PERF_CNTR0_A);
510
511         npdap->synergy_perf_data->counts[1] += 0xffffffffffUL &
512                 REMOTE_SYNERGY_LOAD(nasid, 1, PERF_CNTR0_B);
513
514         /* skip to next in circular list */
515         npdap->synergy_perf_data = npdap->synergy_perf_data->next;
516
517         spin_unlock_irq(&npdap->synergy_perf_lock);
518
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);
522
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);
526 }
527
528 void
529 synergy_perf_init(void)
530 {
531         printk("synergy_perf_init(), counting is initially disabled\n");
532         synergy_perf_initialized++;
533 }