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