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 52int bit_pos_to_irq(int bit); 53void setclear_mask_b(int irq, int cpuid, int set); 54void setclear_mask_a(int irq, int cpuid, int set); 55void * kmalloc(size_t size, int flags); 56 57static int synergy_perf_initialized = 0; 58 59void 60synergy_intr_alloc(int bit, int cpuid) { 61 return; 62} 63 64int 65synergy_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} 83void 84setclear_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 157void 158setclear_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 235static int 236synergy_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 244 /* bit 45 is enable */ 245 modesel |= (1UL << 45); 246 247 for (cnode=0; cnode < numnodes; cnode++) { 248 /* for each node, insert a new synergy_perf entry */ 249 if ((npdap = NODEPDA(cnode)) == NULL) { 250 printk("synergy_perf_append: cnode=%d NODEPDA(cnode)==NULL, nodepda=%p\n", cnode, (void *)nodepda); 251 continue; 252 } 253 254 if (npdap->synergy_perf_enabled) { 255 /* user must disable counting to append new events */ 256 err = -EBUSY; 257 break; 258 } 259 260 if (!checked && npdap->synergy_perf_data != NULL) { 261 checked = 1; 262 for (p = npdap->synergy_perf_first; ;) { 263 if (p->modesel == modesel) 264 return 0; /* event already registered */ 265 if ((p = p->next) == npdap->synergy_perf_first) 266 break; 267 } 268 } 269 270 /* XX use kmem_alloc_node() when it is implemented */ 271 p = (synergy_perf_t *)kmalloc(sizeof(synergy_perf_t), GFP_KERNEL); 272 if ((((uint64_t)p) & 7UL) != 0) 273 BUG(); /* bad alignment */ 274 if (p == NULL) { 275 err = -ENOMEM; 276 break; 277 } 278 else { 279 memset(p, 0, sizeof(synergy_perf_t)); 280 p->modesel = modesel; 281 282 spin_lock_irq(&npdap->synergy_perf_lock); 283 if (npdap->synergy_perf_data == NULL) { 284 /* circular list */ 285 p->next = p; 286 npdap->synergy_perf_first = p; 287 npdap->synergy_perf_data = p; 288 } 289 else { 290 p->next = npdap->synergy_perf_data->next; 291 npdap->synergy_perf_data->next = p; 292 } 293 spin_unlock_irq(&npdap->synergy_perf_lock); 294 } 295 } 296 297 return err; 298} 299 300static void 301synergy_perf_set_freq(int freq) 302{ 303 int cnode; 304 nodepda_t *npdap; 305 306 for (cnode=0; cnode < numnodes; cnode++) { 307 if ((npdap = NODEPDA(cnode)) != NULL) 308 npdap->synergy_perf_freq = freq; 309 } 310} 311 312static void 313synergy_perf_set_enable(int enable) 314{ 315 int cnode; 316 nodepda_t *npdap; 317 318 for (cnode=0; cnode < numnodes; cnode++) { 319 if ((npdap = NODEPDA(cnode)) != NULL) 320 npdap->synergy_perf_enabled = enable; 321 } 322 printk("NOTICE: synergy perf counting %sabled on all nodes\n", enable ? "en" : "dis"); 323} 324 325static int 326synergy_perf_size(nodepda_t *npdap) 327{ 328 synergy_perf_t *p; 329 int n; 330 331 if (npdap->synergy_perf_enabled == 0) { 332 /* no stats to return */ 333 return 0; 334 } 335 336 spin_lock_irq(&npdap->synergy_perf_lock); 337 for (n=0, p = npdap->synergy_perf_first; p;) { 338 n++; 339 p = p->next; 340 if (p == npdap->synergy_perf_first) 341 break; 342 } 343 spin_unlock_irq(&npdap->synergy_perf_lock); 344 345 /* bytes == n pairs of {event,counter} */ 346 return n * 2 * sizeof(uint64_t); 347} 348 349static int 350synergy_perf_ioctl(struct inode *inode, struct file *file, 351 unsigned int cmd, unsigned long arg) 352{ 353 int cnode; 354 nodepda_t *npdap; 355 synergy_perf_t *p; 356 int intarg; 357 int fsb; 358 uint64_t longarg; 359 uint64_t *stats; 360 int n; 361 devfs_handle_t d; 362 arbitrary_info_t info; 363 364 if ((d = devfs_get_handle_from_inode(inode)) == NULL) 365 return -ENODEV; 366 info = hwgraph_fastinfo_get(d); 367 368 cnode = SYNERGY_PERF_INFO_CNODE(info); 369 fsb = SYNERGY_PERF_INFO_FSB(info); 370 npdap = NODEPDA(cnode); 371 372 switch (cmd) { 373 case SNDRV_GET_SYNERGY_VERSION: 374 /* return int, version of data structure for SNDRV_GET_SYNERGYINFO */ 375 intarg = 1; /* version 1 */ 376 if (copy_to_user((void *)arg, &intarg, sizeof(intarg))) 377 return -EFAULT; 378 break; 379 380 case SNDRV_GET_INFOSIZE: 381 /* return int, sizeof buf needed for SYNERGY_PERF_GET_STATS */ 382 intarg = synergy_perf_size(npdap); 383 if (copy_to_user((void *)arg, &intarg, sizeof(intarg))) 384 return -EFAULT; 385 break; 386 387 case SNDRV_GET_SYNERGYINFO: 388 /* return array of event/value pairs, this node only */ 389 if ((intarg = synergy_perf_size(npdap)) <= 0) 390 return -ENODATA; 391 if ((stats = (uint64_t *)kmalloc(intarg, GFP_KERNEL)) == NULL) 392 return -ENOMEM; 393 spin_lock_irq(&npdap->synergy_perf_lock); 394 for (n=0, p = npdap->synergy_perf_first; p;) { 395 stats[n++] = p->modesel; 396 if (p->intervals > 0) 397 stats[n++] = p->counts[fsb] * p->total_intervals / p->intervals; 398 else 399 stats[n++] = 0; 400 p = p->next; 401 if (p == npdap->synergy_perf_first) 402 break; 403 } 404 spin_unlock_irq(&npdap->synergy_perf_lock); 405 406 if (copy_to_user((void *)arg, stats, intarg)) { 407 kfree(stats); 408 return -EFAULT; 409 } 410 411 kfree(stats); 412 break; 413 414 case SNDRV_SYNERGY_APPEND: 415 /* reads 64bit event, append synergy perf event to all nodes */ 416 if (copy_from_user(&longarg, (void *)arg, sizeof(longarg))) 417 return -EFAULT; 418 return synergy_perf_append(longarg); 419 break; 420 421 case SNDRV_GET_SYNERGY_STATUS: 422 /* return int, 1 if enabled else 0 */ 423 intarg = npdap->synergy_perf_enabled; 424 if (copy_to_user((void *)arg, &intarg, sizeof(intarg))) 425 return -EFAULT; 426 break; 427 428 case SNDRV_SYNERGY_ENABLE: 429 /* read int, if true enable counting else disable */ 430 if (copy_from_user(&intarg, (void *)arg, sizeof(intarg))) 431 return -EFAULT; 432 synergy_perf_set_enable(intarg); 433 break; 434 435 case SNDRV_SYNERGY_FREQ: 436 /* read int, set jiffies per update */ 437 if (copy_from_user(&intarg, (void *)arg, sizeof(intarg))) 438 return -EFAULT; 439 if (intarg < 0 || intarg >= HZ) 440 return -EINVAL; 441 synergy_perf_set_freq(intarg); 442 break; 443 444 default: 445 printk("Warning: invalid ioctl %d on synergy mon for cnode=%d fsb=%d\n", cmd, cnode, fsb); 446 return -EINVAL; 447 } 448 return(0); 449} 450 451struct file_operations synergy_mon_fops = { 452 ioctl: synergy_perf_ioctl, 453}; 454 455void 456synergy_perf_update(int cpu) 457{ 458 nasid_t nasid; 459 cnodeid_t cnode; 460 struct nodepda_s *npdap; 461 462 /* 463 * synergy_perf_initialized is set by synergy_perf_init() 464 * which is called last thing by sn_mp_setup(), i.e. well 465 * after nodepda has been initialized. 466 */ 467 if (!synergy_perf_initialized) 468 return; 469 470 cnode = cpuid_to_cnodeid(cpu); 471 npdap = NODEPDA(cnode); 472 473 if (npdap == NULL || cnode < 0 || cnode >= numnodes) 474 /* this should not happen: still in early io init */ 475 return; 476 477 478 if (npdap->synergy_perf_enabled == 0 || npdap->synergy_perf_data == NULL) { 479 /* Not enabled, or no events to monitor */ 480 return; 481 } 482 483 if (npdap->synergy_inactive_intervals++ % npdap->synergy_perf_freq != 0) { 484 /* don't multiplex on every timer interrupt */ 485 return; 486 } 487 488 /* 489 * Read registers for last interval and increment counters. 490 * Hold the per-node synergy_perf_lock so concurrent readers get 491 * consistent values. 492 */ 493 spin_lock_irq(&npdap->synergy_perf_lock); 494 495 nasid = cpuid_to_nasid(cpu); 496 npdap->synergy_active_intervals++; 497 npdap->synergy_perf_data->intervals++; 498 npdap->synergy_perf_data->total_intervals = npdap->synergy_active_intervals; 499 500 npdap->synergy_perf_data->counts[0] += 0xffffffffffUL & 501 REMOTE_SYNERGY_LOAD(nasid, 0, PERF_CNTR0_A); 502 503 npdap->synergy_perf_data->counts[1] += 0xffffffffffUL & 504 REMOTE_SYNERGY_LOAD(nasid, 1, PERF_CNTR0_B); 505 506 /* skip to next in circular list */ 507 npdap->synergy_perf_data = npdap->synergy_perf_data->next; 508 509 spin_unlock_irq(&npdap->synergy_perf_lock); 510 511 /* set the counter 0 selection modes for both A and B */ 512 REMOTE_SYNERGY_STORE(nasid, 0, PERF_CNTL0_A, npdap->synergy_perf_data->modesel); 513 REMOTE_SYNERGY_STORE(nasid, 1, PERF_CNTL0_B, npdap->synergy_perf_data->modesel); 514 515 /* and reset the counter registers to zero */ 516 REMOTE_SYNERGY_STORE(nasid, 0, PERF_CNTR0_A, 0UL); 517 REMOTE_SYNERGY_STORE(nasid, 1, PERF_CNTR0_B, 0UL); 518} 519 520void 521synergy_perf_init(void) 522{ 523 printk("synergy_perf_init(), counting is initially disabled\n"); 524 synergy_perf_initialized++; 525} 526