x86: Detect and handle missing perf support
[akaros.git] / kern / arch / x86 / devarch.c
1 /* 
2  * This file is part of the UCB release of Plan 9. It is subject to the license
3  * terms in the LICENSE file found in the top-level directory of this
4  * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
5  * part of the UCB release of Plan 9, including this file, may be copied,
6  * modified, propagated, or distributed except according to the terms contained
7  * in the LICENSE file.
8  */
9
10 #include <ros/memops.h>
11 #include <vfs.h>
12 #include <kmalloc.h>
13 #include <kref.h>
14 #include <kthread.h>
15 #include <string.h>
16 #include <stdio.h>
17 #include <assert.h>
18 #include <err.h>
19 #include <pmap.h>
20 #include <umem.h>
21 #include <smp.h>
22 #include <ip.h>
23 #include <time.h>
24 #include <bitops.h>
25 #include <core_set.h>
26 #include <address_range.h>
27 #include <arch/ros/perfmon.h>
28 #include <arch/topology.h>
29 #include <arch/perfmon.h>
30 #include <arch/ros/msr-index.h>
31 #include <arch/msr.h>
32 #include <arch/devarch.h>
33
34 #define REAL_MEM_SIZE (1024 * 1024)
35
36 struct perf_context {
37         struct perfmon_session *ps;
38         size_t resp_size;
39         uint8_t *resp;
40 };
41
42 struct io_map {
43         struct io_map *next;
44         int reserved;
45         char tag[13];
46         uint32_t start;
47         uint32_t end;
48 };
49
50 static struct {
51         spinlock_t lock;
52         struct io_map *map;
53         struct io_map *free;
54         struct io_map maps[32];                         // some initial free maps
55         qlock_t ql;                                     // lock for reading map
56 } iomap;
57
58 enum {
59         Qdir = 0,
60         Qioalloc = 1,
61         Qiob,
62         Qiow,
63         Qiol,
64         Qgdb,
65         Qrealmem,
66         Qmsr,
67         Qperf,
68
69         Qmax,
70 };
71
72 enum {
73         Linelen = 31,
74 };
75
76 struct dev archdevtab;
77 static struct dirtab archdir[Qmax] = {
78         {".", {Qdir, 0, QTDIR}, 0, 0555},
79         {"ioalloc", {Qioalloc, 0}, 0, 0444},
80         {"iob", {Qiob, 0}, 0, 0666},
81         {"iow", {Qiow, 0}, 0, 0666},
82         {"iol", {Qiol, 0}, 0, 0666},
83         {"gdb", {Qgdb, 0}, 0, 0660},
84         {"realmem", {Qrealmem, 0}, 0, 0444},
85         {"msr", {Qmsr, 0}, 0, 0666},
86         {"perf", {Qperf, 0}, 0, 0666},
87 };
88 /* White list entries needs to be ordered by start address, and never overlap.
89  */
90 #define MSR_MAX_VAR_COUNTERS 16
91 #define MSR_MAX_FIX_COUNTERS 4
92
93 static const struct address_range msr_rd_wlist[] = {
94         ADDRESS_RANGE(0x00000000, 0xffffffff),
95 };
96 static const struct address_range msr_wr_wlist[] = {
97         ADDRESS_RANGE(MSR_IA32_PERFCTR0,
98                                   MSR_IA32_PERFCTR0 + MSR_MAX_VAR_COUNTERS - 1),
99         ADDRESS_RANGE(MSR_ARCH_PERFMON_EVENTSEL0,
100                                   MSR_ARCH_PERFMON_EVENTSEL0 + MSR_MAX_VAR_COUNTERS - 1),
101         ADDRESS_RANGE(MSR_IA32_PERF_CTL, MSR_IA32_PERF_CTL),
102         ADDRESS_RANGE(MSR_CORE_PERF_FIXED_CTR0,
103                                   MSR_CORE_PERF_FIXED_CTR0 + MSR_MAX_FIX_COUNTERS - 1),
104         ADDRESS_RANGE(MSR_CORE_PERF_FIXED_CTR_CTRL, MSR_CORE_PERF_GLOBAL_OVF_CTRL),
105 };
106 int gdbactive = 0;
107
108 //
109 //  alloc some io port space and remember who it was
110 //  alloced to.  if port < 0, find a free region.
111 //
112 int ioalloc(int port, int size, int align, char *tag)
113 {
114         struct io_map *map, **l;
115         int i;
116
117         spin_lock(&(&iomap)->lock);
118         if (port < 0) {
119                 // find a free port above 0x400 and below 0x1000
120                 port = 0x400;
121                 for (l = &iomap.map; *l; l = &(*l)->next) {
122                         map = *l;
123                         if (map->start < 0x400)
124                                 continue;
125                         i = map->start - port;
126                         if (i > size)
127                                 break;
128                         if (align > 0)
129                                 port = ((port + align - 1) / align) * align;
130                         else
131                                 port = map->end;
132                 }
133                 if (*l == NULL) {
134                         spin_unlock(&(&iomap)->lock);
135                         return -1;
136                 }
137         } else {
138                 // Only 64KB I/O space on the x86.
139                 if ((port + size) > 0x10000) {
140                         spin_unlock(&(&iomap)->lock);
141                         return -1;
142                 }
143                 // see if the space clashes with previously allocated ports
144                 for (l = &iomap.map; *l; l = &(*l)->next) {
145                         map = *l;
146                         if (map->end <= port)
147                                 continue;
148                         if (map->reserved && map->start == port && map->end == port + size) {
149                                 map->reserved = 0;
150                                 spin_unlock(&(&iomap)->lock);
151                                 return map->start;
152                         }
153                         if (map->start >= port + size)
154                                 break;
155                         spin_unlock(&(&iomap)->lock);
156                         return -1;
157                 }
158         }
159         map = iomap.free;
160         if (map == NULL) {
161                 printd("ioalloc: out of maps");
162                 spin_unlock(&(&iomap)->lock);
163                 return port;
164         }
165         iomap.free = map->next;
166         map->next = *l;
167         map->start = port;
168         map->end = port + size;
169         strlcpy(map->tag, tag, sizeof(map->tag));
170         *l = map;
171
172         archdir[0].qid.vers++;
173
174         spin_unlock(&(&iomap)->lock);
175         return map->start;
176 }
177
178 void iofree(int port)
179 {
180         struct io_map *map, **l;
181
182         spin_lock(&(&iomap)->lock);
183         for (l = &iomap.map; *l; l = &(*l)->next) {
184                 if ((*l)->start == port) {
185                         map = *l;
186                         *l = map->next;
187                         map->next = iomap.free;
188                         iomap.free = map;
189                         break;
190                 }
191                 if ((*l)->start > port)
192                         break;
193         }
194         archdir[0].qid.vers++;
195         spin_unlock(&(&iomap)->lock);
196 }
197
198 int iounused(int start, int end)
199 {
200         struct io_map *map;
201
202         for (map = iomap.map; map; map = map->next) {
203                 if (((start >= map->start) && (start < map->end))
204                         || ((start <= map->start) && (end > map->start)))
205                         return 0;
206         }
207         return 1;
208 }
209
210 void ioinit(void)
211 {
212         int i;
213         char *excluded = "";
214
215         panic("Akaros doesn't do IO port allocation yet.  Don't init.");
216         for (i = 0; i < ARRAY_SIZE(iomap.maps) - 1; i++)
217                 iomap.maps[i].next = &iomap.maps[i + 1];
218         iomap.maps[i].next = NULL;
219         iomap.free = iomap.maps;
220         char *s;
221
222         s = excluded;
223         while (s && *s != '\0' && *s != '\n') {
224                 char *ends;
225                 int io_s, io_e;
226
227                 io_s = (int)strtol(s, &ends, 0);
228                 if (ends == NULL || ends == s || *ends != '-') {
229                         printd("ioinit: cannot parse option string\n");
230                         break;
231                 }
232                 s = ++ends;
233
234                 io_e = (int)strtol(s, &ends, 0);
235                 if (ends && *ends == ',')
236                         *ends++ = '\0';
237                 s = ends;
238
239                 ioalloc(io_s, io_e - io_s + 1, 0, "pre-allocated");
240         }
241 }
242
243 // Reserve a range to be ioalloced later.
244 // This is in particular useful for exchangable cards, such
245 // as pcmcia and cardbus cards.
246 int ioreserve(int unused_int, int size, int align, char *tag)
247 {
248         struct io_map *map, **l;
249         int i, port;
250
251         spin_lock(&(&iomap)->lock);
252         // find a free port above 0x400 and below 0x1000
253         port = 0x400;
254         for (l = &iomap.map; *l; l = &(*l)->next) {
255                 map = *l;
256                 if (map->start < 0x400)
257                         continue;
258                 i = map->start - port;
259                 if (i > size)
260                         break;
261                 if (align > 0)
262                         port = ((port + align - 1) / align) * align;
263                 else
264                         port = map->end;
265         }
266         if (*l == NULL) {
267                 spin_unlock(&(&iomap)->lock);
268                 return -1;
269         }
270         map = iomap.free;
271         if (map == NULL) {
272                 printd("ioalloc: out of maps");
273                 spin_unlock(&(&iomap)->lock);
274                 return port;
275         }
276         iomap.free = map->next;
277         map->next = *l;
278         map->start = port;
279         map->end = port + size;
280         map->reserved = 1;
281         strlcpy(map->tag, tag, sizeof(map->tag));
282         *l = map;
283
284         archdir[0].qid.vers++;
285
286         spin_unlock(&(&iomap)->lock);
287         return map->start;
288 }
289
290 static void checkport(int start, int end)
291 {
292         /* standard vga regs are OK */
293         if (start >= 0x2b0 && end <= 0x2df + 1)
294                 return;
295         if (start >= 0x3c0 && end <= 0x3da + 1)
296                 return;
297
298         if (iounused(start, end))
299                 return;
300         error(EPERM, NULL);
301 }
302
303 static struct chan *archattach(char *spec)
304 {
305         return devattach(archdevtab.name, spec);
306 }
307
308 struct walkqid *archwalk(struct chan *c, struct chan *nc, char **name,
309                                                  int nname)
310 {
311         return devwalk(c, nc, name, nname, archdir, Qmax, devgen);
312 }
313
314 static int archstat(struct chan *c, uint8_t * dp, int n)
315 {
316         archdir[Qrealmem].length = REAL_MEM_SIZE;
317
318         return devstat(c, dp, n, archdir, Qmax, devgen);
319 }
320
321 static struct perf_context *arch_create_perf_context(void)
322 {
323         ERRSTACK(1);
324         struct perf_context *pc = kzmalloc(sizeof(struct perf_context),
325                                                                            KMALLOC_WAIT);
326
327         if (waserror()) {
328                 kfree(pc);
329                 nexterror();
330         }
331         pc->ps = perfmon_create_session();
332         poperror();
333
334         return pc;
335 }
336
337 static void arch_free_perf_context(struct perf_context *pc)
338 {
339         if (likely(pc)) {
340                 perfmon_close_session(pc->ps);
341                 kfree(pc->resp);
342                 kfree(pc);
343         }
344 }
345
346 static const uint8_t *arch_read_core_set(struct core_set *cset,
347                                                                                  const uint8_t *kptr,
348                                                                                  const uint8_t *ktop)
349 {
350         int i, nb;
351         uint32_t n;
352
353         error_assert(EBADMSG, (kptr + sizeof(uint32_t)) <= ktop);
354         kptr = get_le_u32(kptr, &n);
355         error_assert(EBADMSG, (kptr + n) <= ktop);
356         core_set_init(cset);
357         nb = MIN((int) n * 8, num_cores);
358         for (i = 0; i < nb; i++) {
359                 if (test_bit(i, (const unsigned long *) kptr))
360                         core_set_setcpu(cset, i);
361         }
362
363         return kptr + n;
364 }
365
366 static long arch_perf_write(struct perf_context *pc, const void *udata,
367                                                         long usize)
368 {
369         ERRSTACK(1);
370         void *kdata;
371         const uint8_t *kptr, *ktop;
372
373         kfree(pc->resp);
374         pc->resp = NULL;
375         pc->resp_size = 0;
376
377         kdata = user_memdup_errno(current, udata, usize);
378         if (unlikely(!kdata))
379                 return -1;
380         if (waserror()) {
381                 kfree(kdata);
382                 nexterror();
383         }
384         kptr = kdata;
385         ktop = kptr + usize;
386         error_assert(EBADMSG, (kptr + 1) <= ktop);
387         switch (*kptr++) {
388                 case PERFMON_CMD_COUNTER_OPEN: {
389                         int ped;
390                         struct perfmon_event pev;
391                         struct core_set cset;
392
393                         error_assert(EBADMSG, (kptr + 3 * sizeof(uint64_t)) <= ktop);
394                         perfmon_init_event(&pev);
395                         kptr = get_le_u64(kptr, &pev.event);
396                         kptr = get_le_u64(kptr, &pev.flags);
397                         kptr = get_le_u64(kptr, &pev.trigger_count);
398                         kptr = arch_read_core_set(&cset, kptr, ktop);
399
400                         ped = perfmon_open_event(&cset, pc->ps, &pev);
401
402                         pc->resp_size = sizeof(uint32_t);
403                         pc->resp = kmalloc(pc->resp_size, KMALLOC_WAIT);
404                         put_le_u32(pc->resp, (uint32_t) ped);
405                         break;
406                 }
407                 case PERFMON_CMD_COUNTER_STATUS: {
408                         int i;
409                         uint32_t ped;
410                         uint8_t *rptr;
411                         uint64_t *mvalues;
412                         struct perfmon_status *pef;
413
414                         error_assert(EBADMSG, (kptr + sizeof(uint32_t)) <= ktop);
415                         kptr = get_le_u32(kptr, &ped);
416
417                         pef = perfmon_get_event_status(pc->ps, (int) ped);
418
419                         mvalues = kzmalloc(num_cores * sizeof(mvalues), KMALLOC_WAIT);
420                         for (i = 0; i < num_cores; i++)
421                                 mvalues[i] = pef->cores_values[i];
422
423                         pc->resp_size = 3 * sizeof(uint64_t) + sizeof(uint32_t) +
424                                 num_cores * sizeof(uint64_t);
425                         pc->resp = kmalloc(pc->resp_size, KMALLOC_WAIT);
426
427                         rptr = put_le_u64(pc->resp, pef->ev.event);
428                         rptr = put_le_u64(rptr, pef->ev.flags);
429                         rptr = put_le_u64(rptr, pef->ev.trigger_count);
430                         rptr = put_le_u32(rptr, num_cores);
431                         for (i = 0; i < num_cores; i++)
432                                 rptr = put_le_u64(rptr, mvalues[i]);
433                         kfree(mvalues);
434                         perfmon_free_event_status(pef);
435                         break;
436                 }
437                 case PERFMON_CMD_COUNTER_CLOSE: {
438                         uint32_t ped;
439
440                         error_assert(EBADMSG, (kptr + sizeof(uint32_t)) <= ktop);
441                         kptr = get_le_u32(kptr, &ped);
442
443                         perfmon_close_event(pc->ps, (int) ped);
444                         break;
445                 }
446                 case PERFMON_CMD_CPU_CAPS: {
447                         uint8_t *rptr;
448                         struct perfmon_cpu_caps pcc;
449
450                         kptr++;
451                         perfmon_get_cpu_caps(&pcc);
452
453                         pc->resp_size = 6 * sizeof(uint32_t);
454                         pc->resp = kmalloc(pc->resp_size, KMALLOC_WAIT);
455
456                         rptr = put_le_u32(pc->resp, pcc.perfmon_version);
457                         rptr = put_le_u32(rptr, pcc.proc_arch_events);
458                         rptr = put_le_u32(rptr, pcc.bits_x_counter);
459                         rptr = put_le_u32(rptr, pcc.counters_x_proc);
460                         rptr = put_le_u32(rptr, pcc.bits_x_fix_counter);
461                         rptr = put_le_u32(rptr, pcc.fix_counters_x_proc);
462                         break;
463                 }
464                 default:
465                         error(EINVAL, "Invalid perfmon command: 0x%x", kptr[-1]);
466         }
467         poperror();
468         kfree(kdata);
469
470         return (long) (kptr - (const uint8_t *) kdata);
471 }
472
473 static struct chan *archopen(struct chan *c, int omode)
474 {
475         c = devopen(c, omode, archdir, Qmax, devgen);
476         switch ((uint32_t) c->qid.path) {
477                 case Qperf:
478                         if (!perfmon_supported())
479                                 error(ENODEV, "perf is not supported");
480                         assert(!c->aux);
481                         c->aux = arch_create_perf_context();
482                         break;
483         }
484
485         return c;
486 }
487
488 static void archclose(struct chan *c)
489 {
490         switch ((uint32_t) c->qid.path) {
491                 case Qperf:
492                         if (c->aux) {
493                                 arch_free_perf_context((struct perf_context *) c->aux);
494                                 c->aux = NULL;
495                         }
496                         break;
497         }
498 }
499
500 static long archread(struct chan *c, void *a, long n, int64_t offset)
501 {
502         char *buf, *p;
503         int err, port;
504         uint64_t *values;
505         uint16_t *sp;
506         uint32_t *lp;
507         struct io_map *map;
508         struct core_set cset;
509         struct msr_address msra;
510         struct msr_value msrv;
511
512         switch ((uint32_t) c->qid.path) {
513                 case Qdir:
514                         return devdirread(c, a, n, archdir, Qmax, devgen);
515                 case Qgdb:
516                         p = gdbactive ? "1" : "0";
517                         return readstr(offset, a, n, p);
518                 case Qiob:
519                         port = offset;
520                         checkport(offset, offset + n);
521                         for (p = a; port < offset + n; port++)
522                                 *p++ = inb(port);
523                         return n;
524                 case Qiow:
525                         if (n & 1)
526                                 error(EINVAL, NULL);
527                         checkport(offset, offset + n);
528                         sp = a;
529                         for (port = offset; port < offset + n; port += 2)
530                                 *sp++ = inw(port);
531                         return n;
532                 case Qiol:
533                         if (n & 3)
534                                 error(EINVAL, NULL);
535                         checkport(offset, offset + n);
536                         lp = a;
537                         for (port = offset; port < offset + n; port += 4)
538                                 *lp++ = inl(port);
539                         return n;
540                 case Qioalloc:
541                         break;
542                 case Qrealmem:
543                         return readmem(offset, a, n, KADDR(0), REAL_MEM_SIZE);
544                 case Qmsr:
545                         if (!address_range_find(msr_rd_wlist, ARRAY_SIZE(msr_rd_wlist),
546                                                                         (uintptr_t) offset))
547                                 error(EPERM, NULL);
548                         core_set_init(&cset);
549                         core_set_fill_available(&cset);
550                         msr_set_address(&msra, (uint32_t) offset);
551                         values = kzmalloc(num_cores * sizeof(uint64_t), KMALLOC_WAIT);
552                         if (!values)
553                                 error(ENOMEM, NULL);
554                         msr_set_values(&msrv, values, num_cores);
555
556                         err = msr_cores_read(&cset, &msra, &msrv);
557
558                         if (likely(!err)) {
559                                 if (n >= num_cores * sizeof(uint64_t)) {
560                                         if (!memcpy_to_user_errno(current, a, values,
561                                                                                           num_cores * sizeof(uint64_t)))
562                                                 n = num_cores * sizeof(uint64_t);
563                                         else
564                                                 n = -1;
565                                 } else {
566                                         kfree(values);
567                                         error(ERANGE, NULL);
568                                 }
569                         } else {
570                                 n = -1;
571                         }
572                         kfree(values);
573                         return n;
574                 case Qperf: {
575                         struct perf_context *pc = (struct perf_context *) c->aux;
576
577                         assert(pc);
578                         if (pc->resp && ((size_t) offset < pc->resp_size)) {
579                                 n = MIN(n, (long) pc->resp_size - (long) offset);
580                                 if (memcpy_to_user_errno(current, a, pc->resp + offset, n))
581                                         n = -1;
582                         } else {
583                                 n = 0;
584                         }
585
586                         return n;
587                 }
588                 default:
589                         error(EINVAL, NULL);
590         }
591
592         if ((buf = kzmalloc(n, 0)) == NULL)
593                 error(ENOMEM, NULL);
594         p = buf;
595         n = n / Linelen;
596         offset = offset / Linelen;
597
598         switch ((uint32_t) c->qid.path) {
599                 case Qioalloc:
600                         spin_lock(&(&iomap)->lock);
601                         for (map = iomap.map; n > 0 && map != NULL; map = map->next) {
602                                 if (offset-- > 0)
603                                         continue;
604                                 snprintf(p, n * Linelen, "%#8p %#8p %-12.12s\n", map->start,
605                                                  map->end - 1, map->tag);
606                                 p += Linelen;
607                                 n--;
608                         }
609                         spin_unlock(&(&iomap)->lock);
610                         break;
611         }
612
613         n = p - buf;
614         memmove(a, buf, n);
615         kfree(buf);
616
617         return n;
618 }
619
620 static long archwrite(struct chan *c, void *a, long n, int64_t offset)
621 {
622         char *p;
623         int port, err;
624         uint64_t value;
625         uint16_t *sp;
626         uint32_t *lp;
627         struct core_set cset;
628         struct msr_address msra;
629         struct msr_value msrv;
630
631         switch ((uint32_t) c->qid.path) {
632                 case Qgdb:
633                         p = a;
634                         if (n != 1)
635                                 error(EINVAL, "Gdb: Write one byte, '1' or '0'");
636                         if (*p == '1')
637                                 gdbactive = 1;
638                         else if (*p == '0')
639                                 gdbactive = 0;
640                         else
641                                 error(EINVAL, "Gdb: must be 1 or 0");
642                         return 1;
643                 case Qiob:
644                         p = a;
645                         checkport(offset, offset + n);
646                         for (port = offset; port < offset + n; port++)
647                                 outb(port, *p++);
648                         return n;
649                 case Qiow:
650                         if (n & 1)
651                                 error(EINVAL, NULL);
652                         checkport(offset, offset + n);
653                         sp = a;
654                         for (port = offset; port < offset + n; port += 2)
655                                 outw(port, *sp++);
656                         return n;
657                 case Qiol:
658                         if (n & 3)
659                                 error(EINVAL, NULL);
660                         checkport(offset, offset + n);
661                         lp = a;
662                         for (port = offset; port < offset + n; port += 4)
663                                 outl(port, *lp++);
664                         return n;
665                 case Qmsr:
666                         if (!address_range_find(msr_wr_wlist, ARRAY_SIZE(msr_wr_wlist),
667                                                                         (uintptr_t) offset))
668                                 error(EPERM, NULL);
669                         if (n != sizeof(uint64_t))
670                                 error(EINVAL, NULL);
671                         if (memcpy_from_user_errno(current, &value, a, sizeof(value)))
672                                 return -1;
673
674                         core_set_init(&cset);
675                         core_set_fill_available(&cset);
676                         msr_set_address(&msra, (uint32_t) offset);
677                         msr_set_value(&msrv, value);
678
679                         err = msr_cores_write(&cset, &msra, &msrv);
680                         if (unlikely(err))
681                                 error(-err, NULL);
682                         return sizeof(uint64_t);
683                 case Qperf: {
684                         struct perf_context *pc = (struct perf_context *) c->aux;
685
686                         assert(pc);
687
688                         return arch_perf_write(pc, a, n);
689                 }
690                 default:
691                         error(EINVAL, NULL);
692         }
693         return 0;
694 }
695
696 struct dev archdevtab __devtab = {
697         .name = "arch",
698
699         .reset = devreset,
700         .init = devinit,
701         .shutdown = devshutdown,
702         .attach = archattach,
703         .walk = archwalk,
704         .stat = archstat,
705         .open = archopen,
706         .create = devcreate,
707         .close = archclose,
708         .read = archread,
709         .bread = devbread,
710         .write = archwrite,
711         .bwrite = devbwrite,
712         .remove = devremove,
713         .wstat = devwstat,
714 };
715
716 void archreset(void)
717 {
718         int i;
719
720         /*
721          * And sometimes there is no keyboard...
722          *
723          * The reset register (0xcf9) is usually in one of the bridge
724          * chips. The actual location and sequence could be extracted from
725          * ACPI but why bother, this is the end of the line anyway.
726          print("Takes a licking and keeps on ticking...\n");
727          */
728         i = inb(0xcf9); /* ICHx reset control */
729         i &= 0x06;
730         outb(0xcf9, i | 0x02);  /* SYS_RST */
731         udelay(1000);
732         outb(0xcf9, i | 0x06);  /* RST_CPU transition */
733
734         udelay(100 * 1000);
735
736         /* some broken hardware -- as well as qemu -- might
737          * never reboot anyway with cf9. This is a standard
738          * keyboard reboot sequence known to work on really
739          * broken stuff -- like qemu. If there is no
740          * keyboard it will do no harm.
741          */
742         for (;;) {
743                 (void)inb(0x64);
744                 outb(0x64, 0xFE);
745                 udelay(100 * 1000);
746         }
747 }