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