1 /*
2  * PowerPC hash table management proc entry.  Will show information
3  * about the current hash table and will allow changes to it.
4  *
5  * Written by Cort Dougan (cort@cs.nmt.edu)
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version
10  * 2 of the License, or (at your option) any later version.
11  */
12 
13 #include <linux/config.h>
14 #include <linux/errno.h>
15 #include <linux/sched.h>
16 #include <linux/proc_fs.h>
17 #include <linux/stat.h>
18 #include <linux/sysctl.h>
19 #include <linux/ctype.h>
20 #include <linux/threads.h>
21 
22 #include <asm/uaccess.h>
23 #include <asm/bitops.h>
24 #include <asm/mmu.h>
25 #include <asm/processor.h>
26 #include <asm/residual.h>
27 #include <asm/io.h>
28 #include <asm/pgtable.h>
29 #include <asm/cputable.h>
30 #include <asm/system.h>
31 
32 static ssize_t ppc_htab_read(struct file * file, char * buf,
33 			     size_t count, loff_t *ppos);
34 static ssize_t ppc_htab_write(struct file * file, const char * buffer,
35 			      size_t count, loff_t *ppos);
36 static long long ppc_htab_lseek(struct file * file, loff_t offset, int orig);
37 int proc_dol2crvec(ctl_table *table, int write, struct file *filp,
38 		  void *buffer, size_t *lenp);
39 
40 extern PTE *Hash, *Hash_end;
41 extern unsigned long Hash_size, Hash_mask;
42 extern unsigned long _SDR1;
43 extern unsigned long htab_reloads;
44 extern unsigned long htab_preloads;
45 extern unsigned long htab_evicts;
46 extern unsigned long pte_misses;
47 extern unsigned long pte_errors;
48 extern unsigned int primary_pteg_full;
49 extern unsigned int htab_hash_searches;
50 
51 /* these will go into processor.h when I'm done debugging -- Cort */
52 #define MMCR0 952
53 #define MMCR0_PMC1_CYCLES (0x1<<7)
54 #define MMCR0_PMC1_ICACHEMISS (0x5<<7)
55 #define MMCR0_PMC1_DTLB (0x6<<7)
56 #define MMCR0_PMC2_DCACHEMISS (0x6)
57 #define MMCR0_PMC2_CYCLES (0x1)
58 #define MMCR0_PMC2_ITLB (0x7)
59 #define MMCR0_PMC2_LOADMISSTIME (0x5)
60 
61 #define PMC1 953
62 #define PMC2 954
63 
64 struct file_operations ppc_htab_operations = {
65         llseek:         ppc_htab_lseek,
66         read:           ppc_htab_read,
67         write:          ppc_htab_write,
68 };
69 
pmc1_lookup(unsigned long mmcr0)70 static char *pmc1_lookup(unsigned long mmcr0)
71 {
72 	switch ( mmcr0 & (0x7f<<7) )
73 	{
74 	case 0x0:
75 		return "none";
76 	case MMCR0_PMC1_CYCLES:
77 		return "cycles";
78 	case MMCR0_PMC1_ICACHEMISS:
79 		return "ic miss";
80 	case MMCR0_PMC1_DTLB:
81 		return "dtlb miss";
82 	default:
83 		return "unknown";
84 	}
85 }
86 
pmc2_lookup(unsigned long mmcr0)87 static char *pmc2_lookup(unsigned long mmcr0)
88 {
89 	switch ( mmcr0 & 0x3f )
90 	{
91 	case 0x0:
92 		return "none";
93 	case MMCR0_PMC2_CYCLES:
94 		return "cycles";
95 	case MMCR0_PMC2_DCACHEMISS:
96 		return "dc miss";
97 	case MMCR0_PMC2_ITLB:
98 		return "itlb miss";
99 	case MMCR0_PMC2_LOADMISSTIME:
100 		return "load miss time";
101 	default:
102 		return "unknown";
103 	}
104 }
105 
106 /*
107  * print some useful info about the hash table.  This function
108  * is _REALLY_ slow (see the nested for loops below) but nothing
109  * in here should be really timing critical. -- Cort
110  */
ppc_htab_read(struct file * file,char * buf,size_t count,loff_t * ppos)111 static ssize_t ppc_htab_read(struct file * file, char * buf,
112 			     size_t count, loff_t *ppos)
113 {
114 	unsigned long mmcr0 = 0, pmc1 = 0, pmc2 = 0;
115 	loff_t pos = *ppos;
116 	int n = 0;
117 #if defined(CONFIG_PPC_STD_MMU) && !defined(CONFIG_PPC64BRIDGE)
118 	int valid;
119 	unsigned int kptes = 0, uptes = 0, zombie_ptes = 0;
120 	PTE *ptr;
121 	struct task_struct *p;
122 #endif /* CONFIG_PPC_STD_MMU */
123 	char buffer[512];
124 
125 	if (count < 0)
126 		return -EINVAL;
127 
128 	if (cur_cpu_spec[0]->cpu_features & CPU_FTR_604_PERF_MON) {
129 		asm volatile ("mfspr %0,952 \n\t"
130 		    "mfspr %1,953 \n\t"
131 		    "mfspr %2,954 \n\t"
132 		    : "=r" (mmcr0), "=r" (pmc1), "=r" (pmc2) );
133 		n += sprintf( buffer + n,
134 			      "604 Performance Monitoring\n"
135 			      "MMCR0\t\t: %08lx %s%s ",
136 			      mmcr0,
137 			      ( mmcr0>>28 & 0x2 ) ? "(user mode counted)" : "",
138 			      ( mmcr0>>28 & 0x4 ) ? "(kernel mode counted)" : "");
139 		n += sprintf( buffer + n,
140 			      "\nPMC1\t\t: %08lx (%s)\n"
141 			      "PMC2\t\t: %08lx (%s)\n",
142 			      pmc1, pmc1_lookup(mmcr0),
143 			      pmc2, pmc2_lookup(mmcr0));
144 	}
145 
146 #ifdef CONFIG_PPC_STD_MMU
147 	/* if we don't have a htab */
148 	if ( Hash_size == 0 )
149 	{
150 		n += sprintf( buffer + n, "No Hash Table used\n");
151 		goto return_string;
152 	}
153 
154 #ifndef CONFIG_PPC64BRIDGE
155 	for ( ptr = Hash ; ptr < Hash_end ; ptr++)
156 	{
157 		unsigned int ctx, mctx, vsid;
158 
159 		if (!ptr->v)
160 			continue;
161 		/* make sure someone is using this context/vsid */
162 		/* first undo the esid skew */
163 		vsid = ptr->vsid;
164 		mctx = ((vsid - (vsid & 0xf) * 0x111) >> 4) & 0xfffff;
165 		if (mctx == 0) {
166 			kptes++;
167 			continue;
168 		}
169 		/* now undo the context skew; 801921 * 897 == 1 mod 2^20 */
170 		ctx = (mctx * 801921) & 0xfffff;
171 		valid = 0;
172 		for_each_task(p) {
173 			if (p->mm != NULL && ctx == p->mm->context) {
174 				valid = 1;
175 				uptes++;
176 				break;
177 			}
178 		}
179 		if (!valid)
180 			zombie_ptes++;
181 	}
182 #endif
183 
184 	n += sprintf( buffer + n,
185 		      "PTE Hash Table Information\n"
186 		      "Size\t\t: %luKb\n"
187 		      "Buckets\t\t: %lu\n"
188  		      "Address\t\t: %08lx\n"
189 		      "Entries\t\t: %lu\n"
190 #ifndef CONFIG_PPC64BRIDGE
191 		      "User ptes\t: %u\n"
192 		      "Kernel ptes\t: %u\n"
193 		      "Zombies\t\t: %u\n"
194 		      "Percent full\t: %lu%%\n"
195 #endif
196                       , (unsigned long)(Hash_size>>10),
197 		      (Hash_size/(sizeof(PTE)*8)),
198 		      (unsigned long)Hash,
199 		      Hash_size/sizeof(PTE)
200 #ifndef CONFIG_PPC64BRIDGE
201                       , uptes,
202 		      kptes,
203 		      zombie_ptes,
204 		      ((kptes+uptes)*100) / (Hash_size/sizeof(PTE))
205 #endif
206 		);
207 
208 	n += sprintf( buffer + n,
209 		      "Reloads\t\t: %lu\n"
210 		      "Preloads\t: %lu\n"
211 		      "Searches\t: %u\n"
212 		      "Overflows\t: %u\n"
213 		      "Evicts\t\t: %lu\n",
214 		      htab_reloads, htab_preloads, htab_hash_searches,
215 		      primary_pteg_full, htab_evicts);
216 return_string:
217 #endif /* CONFIG_PPC_STD_MMU */
218 
219 	n += sprintf( buffer + n,
220 		      "Non-error misses: %lu\n"
221 		      "Error misses\t: %lu\n",
222 		      pte_misses, pte_errors);
223 	if (pos != (unsigned)pos || pos >= strlen(buffer))
224 		return 0;
225 	if (n > strlen(buffer) - pos)
226 		n = strlen(buffer) - pos;
227 	if (n > count)
228 		n = count;
229 	copy_to_user(buf, buffer + pos, n);
230 	*ppos = pos + n;
231 	return n;
232 }
233 
234 /*
235  * Allow user to define performance counters and resize the hash table
236  */
ppc_htab_write(struct file * file,const char * buffer,size_t count,loff_t * ppos)237 static ssize_t ppc_htab_write(struct file * file, const char * buffer,
238 			      size_t count, loff_t *ppos)
239 {
240 #ifdef CONFIG_PPC_STD_MMU
241 	unsigned long tmp;
242 	if ( current->uid != 0 )
243 		return -EACCES;
244 	/* don't set the htab size for now */
245 	if ( !strncmp( buffer, "size ", 5) )
246 		return -EBUSY;
247 
248 	/* turn off performance monitoring */
249 	if ( !strncmp( buffer, "off", 3) )
250 	{
251 		if (cur_cpu_spec[0]->cpu_features & CPU_FTR_604_PERF_MON) {
252 			asm volatile ("mtspr %0, %3 \n\t"
253 			    "mtspr %1, %3 \n\t"
254 			    "mtspr %2, %3 \n\t"
255 			    :: "i" (MMCR0), "i" (PMC1), "i" (PMC2), "r" (0));
256 		}
257 	}
258 
259 	if ( !strncmp( buffer, "reset", 5) )
260 	{
261 		if (cur_cpu_spec[0]->cpu_features & CPU_FTR_604_PERF_MON) {
262 			/* reset PMC1 and PMC2 */
263 			asm volatile (
264 				"mtspr 953, %0 \n\t"
265 				"mtspr 954, %0 \n\t"
266 				:: "r" (0));
267 		}
268 		htab_reloads = 0;
269 		htab_evicts = 0;
270 		pte_misses = 0;
271 		pte_errors = 0;
272 	}
273 
274 	if ( !strncmp( buffer, "user", 4) )
275 	{
276 		if (cur_cpu_spec[0]->cpu_features & CPU_FTR_604_PERF_MON) {
277 			/* setup mmcr0 and clear the correct pmc */
278 			asm("mfspr %0,%1\n\t"  : "=r" (tmp) : "i" (MMCR0));
279 			tmp &= ~(0x60000000);
280 			tmp |= 0x20000000;
281 			asm volatile (
282 				"mtspr %1,%0 \n\t"    /* set new mccr0 */
283 				"mtspr %3,%4 \n\t"    /* reset the pmc */
284 				"mtspr %5,%4 \n\t"    /* reset the pmc2 */
285 				:: "r" (tmp), "i" (MMCR0), "i" (0),
286 				"i" (PMC1),  "r" (0), "i"(PMC2) );
287 		}
288 	}
289 
290 	if ( !strncmp( buffer, "kernel", 6) )
291 	{
292 		if (cur_cpu_spec[0]->cpu_features & CPU_FTR_604_PERF_MON) {
293 			/* setup mmcr0 and clear the correct pmc */
294 			asm("mfspr %0,%1\n\t"  : "=r" (tmp) : "i" (MMCR0));
295 			tmp &= ~(0x60000000);
296 			tmp |= 0x40000000;
297 			asm volatile (
298 				"mtspr %1,%0 \n\t"    /* set new mccr0 */
299 				"mtspr %3,%4 \n\t"    /* reset the pmc */
300 				"mtspr %5,%4 \n\t"    /* reset the pmc2 */
301 				:: "r" (tmp), "i" (MMCR0), "i" (0),
302 				"i" (PMC1),  "r" (0), "i"(PMC2) );
303 		}
304 	}
305 
306 	/* PMC1 values */
307 	if ( !strncmp( buffer, "dtlb", 4) )
308 	{
309 		if (cur_cpu_spec[0]->cpu_features & CPU_FTR_604_PERF_MON) {
310 			/* setup mmcr0 and clear the correct pmc */
311 			asm("mfspr %0,%1\n\t"  : "=r" (tmp) : "i" (MMCR0));
312 			tmp &= ~(0x7f<<7);
313 			tmp |= MMCR0_PMC1_DTLB;
314 			asm volatile (
315 				"mtspr %1,%0 \n\t"    /* set new mccr0 */
316 				"mtspr %3,%4 \n\t"    /* reset the pmc */
317 				:: "r" (tmp), "i" (MMCR0), "i" (MMCR0_PMC1_DTLB),
318 				"i" (PMC1),  "r" (0) );
319 		}
320 	}
321 
322 	if ( !strncmp( buffer, "ic miss", 7) )
323 	{
324 		if (cur_cpu_spec[0]->cpu_features & CPU_FTR_604_PERF_MON) {
325 			/* setup mmcr0 and clear the correct pmc */
326 			asm("mfspr %0,%1\n\t"  : "=r" (tmp) : "i" (MMCR0));
327 			tmp &= ~(0x7f<<7);
328 			tmp |= MMCR0_PMC1_ICACHEMISS;
329 			asm volatile (
330 				"mtspr %1,%0 \n\t"    /* set new mccr0 */
331 				"mtspr %3,%4 \n\t"    /* reset the pmc */
332 				:: "r" (tmp), "i" (MMCR0),
333 				"i" (MMCR0_PMC1_ICACHEMISS), "i" (PMC1),  "r" (0));
334 		}
335 	}
336 
337 	/* PMC2 values */
338 	if ( !strncmp( buffer, "load miss time", 14) )
339 	{
340 		if (cur_cpu_spec[0]->cpu_features & CPU_FTR_604_PERF_MON) {
341 			/* setup mmcr0 and clear the correct pmc */
342 		       asm volatile(
343 			       "mfspr %0,%1\n\t"     /* get current mccr0 */
344 			       "rlwinm %0,%0,0,0,31-6\n\t"  /* clear bits [26-31] */
345 			       "ori   %0,%0,%2 \n\t" /* or in mmcr0 settings */
346 			       "mtspr %1,%0 \n\t"    /* set new mccr0 */
347 			       "mtspr %3,%4 \n\t"    /* reset the pmc */
348 			       : "=r" (tmp)
349 			       : "i" (MMCR0), "i" (MMCR0_PMC2_LOADMISSTIME),
350 			       "i" (PMC2),  "r" (0) );
351 		}
352 	}
353 
354 	if ( !strncmp( buffer, "itlb", 4) )
355 	{
356 		if (cur_cpu_spec[0]->cpu_features & CPU_FTR_604_PERF_MON) {
357 			/* setup mmcr0 and clear the correct pmc */
358 		       asm volatile(
359 			       "mfspr %0,%1\n\t"     /* get current mccr0 */
360 			       "rlwinm %0,%0,0,0,31-6\n\t"  /* clear bits [26-31] */
361 			       "ori   %0,%0,%2 \n\t" /* or in mmcr0 settings */
362 			       "mtspr %1,%0 \n\t"    /* set new mccr0 */
363 			       "mtspr %3,%4 \n\t"    /* reset the pmc */
364 			       : "=r" (tmp)
365 			       : "i" (MMCR0), "i" (MMCR0_PMC2_ITLB),
366 			       "i" (PMC2),  "r" (0) );
367 		}
368 	}
369 
370 	if ( !strncmp( buffer, "dc miss", 7) )
371 	{
372 		if (cur_cpu_spec[0]->cpu_features & CPU_FTR_604_PERF_MON) {
373 			/* setup mmcr0 and clear the correct pmc */
374 		       asm volatile(
375 			       "mfspr %0,%1\n\t"     /* get current mccr0 */
376 			       "rlwinm %0,%0,0,0,31-6\n\t"  /* clear bits [26-31] */
377 			       "ori   %0,%0,%2 \n\t" /* or in mmcr0 settings */
378 			       "mtspr %1,%0 \n\t"    /* set new mccr0 */
379 			       "mtspr %3,%4 \n\t"    /* reset the pmc */
380 			       : "=r" (tmp)
381 			       : "i" (MMCR0), "i" (MMCR0_PMC2_DCACHEMISS),
382 			       "i" (PMC2),  "r" (0) );
383 		}
384 	}
385 
386 
387 	return count;
388 
389 #if 0 /* resizing htab is a bit difficult right now -- Cort */
390 	unsigned long size;
391 	extern void reset_SDR1(void);
392 
393 	/* only know how to set size right now */
394 	if ( strncmp( buffer, "size ", 5) )
395 		return -EINVAL;
396 
397 	size = simple_strtoul( &buffer[5], NULL, 10 );
398 
399 	/* only allow to shrink */
400 	if ( size >= Hash_size>>10 )
401 		return -EINVAL;
402 
403 	/* minimum size of htab */
404 	if ( size < 64 )
405 		return -EINVAL;
406 
407 	/* make sure it's a multiple of 64k */
408 	if ( size % 64 )
409 		return -EINVAL;
410 
411 	printk("Hash table resize to %luk\n", size);
412 	/*
413 	 * We need to rehash all kernel entries for the new htab size.
414 	 * Kernel only since we do a flush_tlb_all().  Since it's kernel
415 	 * we only need to bother with vsids 0-15.  To avoid problems of
416 	 * clobbering un-rehashed values we put the htab at a new spot
417 	 * and put everything there.
418 	 * -- Cort
419 	 */
420 	Hash_size = size<<10;
421 	Hash_mask = (Hash_size >> 6) - 1;
422         _SDR1 = __pa(Hash) | (Hash_mask >> 10);
423 	flush_tlb_all();
424 
425 	reset_SDR1();
426 #endif
427 	return count;
428 #else /* CONFIG_PPC_STD_MMU */
429 	return 0;
430 #endif /* CONFIG_PPC_STD_MMU */
431 }
432 
433 
434 static long long
ppc_htab_lseek(struct file * file,loff_t offset,int orig)435 ppc_htab_lseek(struct file * file, loff_t offset, int orig)
436 {
437     switch (orig) {
438     case 0:
439 	file->f_pos = offset;
440 	return(file->f_pos);
441     case 1:
442 	file->f_pos += offset;
443 	return(file->f_pos);
444     case 2:
445 	return(-EINVAL);
446     default:
447 	return(-EINVAL);
448     }
449 }
450 
451 #define TMPBUFLEN 512
proc_dol2crvec(ctl_table * table,int write,struct file * filp,void * buffer,size_t * lenp)452 int proc_dol2crvec(ctl_table *table, int write, struct file *filp,
453 		  void *buffer, size_t *lenp)
454 {
455 	int vleft, first=1, len, left, val;
456 	char buf[TMPBUFLEN], *p;
457 	static const char *sizestrings[4] = {
458 		"2MB", "256KB", "512KB", "1MB"
459 	};
460 	static const char *clockstrings[8] = {
461 		"clock disabled", "+1 clock", "+1.5 clock", "+3.5 clock",
462 		"+2 clock", "+2.5 clock", "+3 clock", "+4 clock"
463 	};
464 	static const char *typestrings[4] = {
465 		"flow-through burst SRAM", "reserved SRAM",
466 		"pipelined burst SRAM", "pipelined late-write SRAM"
467 	};
468 	static const char *holdstrings[4] = {
469 		"0.5", "1.0", "(reserved2)", "(reserved3)"
470 	};
471 
472 	if (!(cur_cpu_spec[0]->cpu_features & CPU_FTR_L2CR))
473 		return -EFAULT;
474 
475 	if ( /*!table->maxlen ||*/ (filp->f_pos && !write)) {
476 		*lenp = 0;
477 		return 0;
478 	}
479 
480 	vleft = table->maxlen / sizeof(int);
481 	left = *lenp;
482 
483 	for (; left /*&& vleft--*/; first=0) {
484 		if (write) {
485 			while (left) {
486 				char c;
487 				if(get_user(c,(char *) buffer))
488 					return -EFAULT;
489 				if (!isspace(c))
490 					break;
491 				left--;
492 				buffer++;
493 			}
494 			if (!left)
495 				break;
496 			len = left;
497 			if (len > TMPBUFLEN-1)
498 				len = TMPBUFLEN-1;
499 			if(copy_from_user(buf, buffer, len))
500 				return -EFAULT;
501 			buf[len] = 0;
502 			p = buf;
503 			if (*p < '0' || *p > '9')
504 				break;
505 			val = simple_strtoul(p, &p, 0);
506 			len = p-buf;
507 			if ((len < left) && *p && !isspace(*p))
508 				break;
509 			buffer += len;
510 			left -= len;
511 			_set_L2CR(val);
512 		} else {
513 			int is750fx = cur_cpu_spec[0]->cpu_features & CPU_FTR_750FX;
514 			p = buf;
515 			if (!first)
516 				*p++ = '\t';
517 			val = _get_L2CR();
518 			p += sprintf(p, "0x%08x: ", val);
519 			p += sprintf(p, " L2 %s, ", (val >> 31) & 1 ? "enabled" :
520 				     	"disabled");
521 			if (!(val>>30&1))
522 				p += sprintf(p, "no ");
523 			if (is750fx)
524 				p += sprintf(p, "ECC checkstop");
525 			else
526 				p += sprintf(p, "parity");
527 
528 			/* 75x & 74x0 have different L2CR than 745x */
529 			if (!(cur_cpu_spec[0]->cpu_features &
530 						CPU_FTR_SPEC7450)) {
531 				if (!is750fx) {
532 					p += sprintf(p, ", %s",
533 						     sizestrings[(val >> 28) & 3]);
534 					p += sprintf(p, ", %s",
535 						     clockstrings[(val >> 25) & 7]);
536 					p += sprintf(p, ", %s",
537 						     typestrings[(val >> 23) & 3]);
538 				}
539 				p += sprintf(p, "%s", (val>>22)&1 ?
540 					     ", data only" : "");
541 				if (!is750fx) {
542 					p += sprintf(p, "%s", (val>>20)&1 ?
543 						     ", ZZ enabled": "");
544 				}
545 				p += sprintf(p, ", %s", (val>>19)&1 ?
546 					"write-through" : "copy-back");
547 				p += sprintf(p, "%s", (val>>18)&1 ?
548 					", testing" : "");
549 				if (!is750fx) {
550 					p += sprintf(p, ", %sns hold",
551 						     holdstrings[(val>>16)&3]);
552 					p += sprintf(p, "%s", (val>>15)&1 ?
553 						     ", DLL slow" : "");
554 					p += sprintf(p, "%s", (val>>14)&1 ?
555 						     ", diff clock" :"");
556 					p += sprintf(p, "%s", (val>>13)&1 ?
557 						     ", DLL bypass" :"");
558 				} else {
559 					if ((val>>11)&1)
560 						p += sprintf(p, ", lock way 0");
561 					if ((val>>10)&1)
562 						p += sprintf(p, ", lock way 1");
563 					if ((val>>9)&1)
564 						p += sprintf(p, ", Snoop Hit in Locked Line Error Enabled");
565 				}
566 			} else { /* 745x */
567 				p += sprintf(p, ", %sinstn only", (val>>20)&1 ?
568 					"" : "no ");
569 				p += sprintf(p, ", %sdata only", (val>>16)&1 ?
570 					"" : "no ");
571 				p += sprintf(p, ", %s replacement",
572 					(val>>12)&1 ?  "secondary" : "default");
573 			}
574 
575 			p += sprintf(p,"\n");
576 
577 			len = strlen(buf);
578 			if (len > left)
579 				len = left;
580 			if(copy_to_user(buffer, buf, len))
581 				return -EFAULT;
582 			left -= len;
583 			buffer += len;
584 			break;
585 		}
586 	}
587 
588 	if (!write && !first && left) {
589 		if(put_user('\n', (char *) buffer))
590 			return -EFAULT;
591 		left--, buffer++;
592 	}
593 	if (write) {
594 		p = (char *) buffer;
595 		while (left) {
596 			char c;
597 			if(get_user(c, p++))
598 				return -EFAULT;
599 			if (!isspace(c))
600 				break;
601 			left--;
602 		}
603 	}
604 	if (write && first)
605 		return -EINVAL;
606 	*lenp -= left;
607 	filp->f_pos += *lenp;
608 	return 0;
609 }
610 
proc_dol3crvec(ctl_table * table,int write,struct file * filp,void * buffer,size_t * lenp)611 int proc_dol3crvec(ctl_table *table, int write, struct file *filp,
612 		  void *buffer, size_t *lenp)
613 {
614 	int vleft, first=1, len, left, val;
615 	char buf[TMPBUFLEN], *p;
616 	static const char *clockstrings[8] = {
617 		"+6 clock", "reserved(1)", "+2 clock", "+2.5 clock",
618 		"+3 clock", "+3.5 clock", "+4 clock", "+5 clock"
619 	};
620 	static const char *clocksampstrings[4] = {
621 		"2 clock", "3 clock", "4 clock", "5 clock"
622 	};
623 	static const char *pclocksampstrings[8] = {
624 		"0 P-clock", "1 P-clock", "2 P-clock", "3 P-clock",
625 		"4 P-clock", "5 P-clock", "reserved(6)", "reserved(7)"
626 	};
627 	static const char *typestrings[4] = {
628 		"MSUG2 DDR SRAM",
629 		"Pipelined synchronous late-write SRAM",
630 		"Reserved", "PB2 SRAM"
631 	};
632 
633 	if (!(cur_cpu_spec[0]->cpu_features & CPU_FTR_L3CR))
634 		return -EFAULT;
635 	if (write)
636 		return -EFAULT;
637 
638 	if (filp->f_pos && !write) {
639 		*lenp = 0;
640 		return 0;
641 	}
642 
643 	vleft = table->maxlen / sizeof(int);
644 	left = *lenp;
645 
646 	for (; left; first=0) {
647 		p = buf;
648 		if (!first)
649 			*p++ = '\t';
650 		val = _get_L3CR();
651 		p += sprintf(p, "0x%08x: ", val);
652 		p += sprintf(p, " L3 %s", (val >> 31) & 1 ? "enabled" :
653 			     	"disabled");
654 		p += sprintf(p, ", %sdata parity", (val>>30)&1 ? "" :
655 				"no ");
656 		p += sprintf(p, ", %saddr parity", (val>>29)&1 ? "" :
657 				"no ");
658 		p += sprintf(p, ", %s", (val>>28)&1 ? "2MB" : "1MB");
659 		p += sprintf(p, ", clocks %s", (val>>27)&1 ? "enabled" :
660 				"disabled");
661 		p += sprintf(p, ", %s", clockstrings[(val >> 23) & 7]);
662 		p += sprintf(p, ", %sinstn only", (val>>22)&1 ? "" :
663 				"no ");
664 		p += sprintf(p, ", %ssample point override",
665 				(val>>18)&1 ? "" : "no ");
666 		p += sprintf(p, ", %s sample point",
667 				clocksampstrings[(val>>16)&3]);
668 		p += sprintf(p, ", %s sample point",
669 				pclocksampstrings[(val>>13)&7]);
670 		p += sprintf(p, ", %s replacement", (val>>12)&1 ?
671 				"secondary" : "default");
672 		p += sprintf(p, ", %s", typestrings[(val >> 8) & 3]);
673 		p += sprintf(p, ", %sclock cntl", (val>>7)&1 ? "" :
674 				"no ");
675 		p += sprintf(p, ", %sdata only", (val>>6)&1 ? "" :
676 				"no ");
677 		p += sprintf(p, ", private mem %s", (val>>2)&1 ?
678 				"enabled" : "disabled");
679 		p += sprintf(p, ", %sprivate mem", val&1 ? "2MB " :
680 				"1MB ");
681 		p += sprintf(p,"\n");
682 
683 		len = strlen(buf);
684 		if (len > left)
685 			len = left;
686 		if(copy_to_user(buffer, buf, len))
687 			return -EFAULT;
688 		left -= len;
689 		buffer += len;
690 		break;
691 	}
692 
693 	if (!write && !first && left) {
694 		if(put_user('\n', (char *) buffer))
695 			return -EFAULT;
696 		left--, buffer++;
697 	}
698 	*lenp -= left;
699 	filp->f_pos += *lenp;
700 	return 0;
701 }
702