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