Initial import from FreeBSD RELENG_4:
[dragonfly.git] / sys / bus / mca / mca_bus.c
1 /*-
2  * Copyright (c) 1999 Matthew N. Dodd <winter@jurai.net>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD: src/sys/dev/mca/mca_bus.c,v 1.7.2.1 2000/03/17 23:55:48 peter Exp $
27  */
28
29 /*
30  * References:
31  *              The CMU Mach3 microkernel
32  *              NetBSD MCA patches by Scott Telford
33  *              Linux MCA code.
34  */
35
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/queue.h>
39 #include <sys/malloc.h>
40 #include <sys/kernel.h>
41 #include <sys/module.h>
42 #include <sys/bus.h>
43  
44 #include <machine/limits.h>
45 #include <machine/bus.h>              
46 #include <machine/resource.h>
47 #include <sys/rman.h>
48
49 #include <dev/mca/mca_busreg.h>
50 #include <dev/mca/mca_busvar.h>
51
52 #include <sys/interrupt.h>
53
54 #define MAX_COL  79
55
56 static void     mca_reg_print   (device_t, char *, char *, int *);
57
58 struct mca_device {
59         struct resource_list rl;        /* Resources */
60
61         mca_id_t        id;
62         u_int8_t        slot;
63         u_int8_t        enabled;
64         u_int8_t        pos[8];         /* Programable Option Select Regs. */
65 };
66
67 /* Not supposed to use this function! */
68 void
69 mca_pos_set (dev, reg, data)
70         device_t        dev;
71         u_int8_t        reg;
72         u_int8_t        data;
73 {
74         struct mca_device *     m_dev = device_get_ivars(dev);
75         u_int8_t                slot = mca_get_slot(dev);
76
77         if ((slot > MCA_MAX_ADAPTERS) || (reg > MCA_POS7))
78                 return;
79
80         /* Disable motherboard setup */
81         outb(MCA_MB_SETUP_REG, MCA_MB_SETUP_DIS);
82
83         /* Select adapter setup regs */
84         outb(MCA_ADAP_SETUP_REG, ((slot & 0x0f) | MCA_ADAP_SET));
85
86         /* Write the register */
87         outb(MCA_POS_REG(reg), data); 
88
89         /* Disable adapter setup */
90         outb(MCA_ADAP_SETUP_REG, MCA_ADAP_SETUP_DIS);
91
92         /* Update the IVAR copy */
93         m_dev->pos[reg] = data;
94
95         return;
96 }
97
98 u_int8_t
99 mca_pos_get (dev, reg)
100         device_t        dev;
101         u_int8_t        reg;
102 {
103         u_int8_t        slot = mca_get_slot(dev);
104         u_int8_t        data = 0;
105
106         if ((slot > MCA_MAX_ADAPTERS) || (reg > MCA_POS7))
107                 return (0);
108
109         /* Disable motherboard setup */
110         outb(MCA_MB_SETUP_REG, MCA_MB_SETUP_DIS);
111
112         switch (slot) {
113                 case MCA_MB_SCSI_SLOT:
114
115                         /* Disable adapter setup */
116                         outb(MCA_ADAP_SETUP_REG, MCA_ADAP_SETUP_DIS);
117
118                         /* Select motherboard video setup regs */
119                         outb(MCA_MB_SETUP_REG, MCA_MB_SETUP_SCSI);
120
121                         /* read the register */
122                         data = inb(MCA_POS_REG(reg));
123
124                         /* Disable motherboard setup */
125                         outb(MCA_MB_SETUP_REG, MCA_MB_SETUP_DIS);
126
127                         break;
128                 case MCA_MB_VIDEO_SLOT:
129                         /* Disable adapter setup */
130                         outb(MCA_ADAP_SETUP_REG, MCA_ADAP_SETUP_DIS);
131
132                         /* Select motherboard scsi setup regs */
133                         outb(MCA_MB_SETUP_REG, MCA_MB_SETUP_VIDEO);
134
135                         /* read the register */
136                         data = inb(MCA_POS_REG(reg));
137
138                         /* Disable motherboard setup */
139                         outb(MCA_MB_SETUP_REG, MCA_MB_SETUP_DIS);
140                         break;
141                 default:
142
143                         /* Select adapter setup regs */
144                         outb(MCA_ADAP_SETUP_REG,
145                              ((slot & 0x0f) | MCA_ADAP_SET));
146
147                         /* read the register */
148                         data = inb(MCA_POS_REG(reg));
149
150                         /* Disable adapter setup */
151                         outb(MCA_ADAP_SETUP_REG, MCA_ADAP_SETUP_DIS);
152                         break;
153         }
154
155         return (data);
156 }
157
158 const char *
159 mca_match_id (id, mca_devs)
160         u_int16_t               id;
161         struct mca_ident *      mca_devs;
162 {
163         struct mca_ident *      m = mca_devs;
164         while(m->name != NULL) {
165                 if (id == m->id)
166                         return (m->name);
167                 m++;
168         }
169         return (NULL);
170 }
171
172 u_int8_t
173 mca_pos_read (dev, reg)
174         device_t                dev;
175         u_int8_t                reg;
176 {
177         struct mca_device *     m_dev = device_get_ivars(dev);
178
179         if (reg > MCA_POS7)
180                 return (0);
181
182         return (m_dev->pos[reg]);
183 }
184
185 void
186 mca_add_irq (dev, irq)
187         device_t                dev;
188         int                     irq;
189 {
190         struct mca_device *     m_dev = device_get_ivars(dev);
191         int                     rid = 0;
192
193         while (resource_list_find(&(m_dev->rl), SYS_RES_IRQ, rid)) rid++;
194         resource_list_add(&(m_dev->rl), SYS_RES_IRQ, rid, irq, irq, 1);
195
196         return;
197 }
198
199 void
200 mca_add_drq (dev, drq)
201         device_t                dev;
202         int                     drq;
203 {
204         struct mca_device *     m_dev = device_get_ivars(dev);
205         int                     rid = 0;
206
207         while (resource_list_find(&(m_dev->rl), SYS_RES_DRQ, rid)) rid++;
208         resource_list_add(&(m_dev->rl), SYS_RES_DRQ, rid, drq, drq, 1);
209
210         return;
211 }
212
213 void
214 mca_add_mspace (dev, mbase, msize) 
215         device_t                dev;
216         u_long                  mbase;
217         u_long                  msize;
218 {
219         struct mca_device *     m_dev = device_get_ivars(dev);
220         int                     rid = 0;
221
222         while (resource_list_find(&(m_dev->rl), SYS_RES_MEMORY, rid)) rid++;
223         resource_list_add(&(m_dev->rl), SYS_RES_MEMORY, rid,
224                 mbase, (mbase + msize), msize);
225
226         return;
227 }
228
229 void
230 mca_add_iospace (dev, iobase, iosize) 
231         device_t                dev;
232         u_long                  iobase;
233         u_long                  iosize;
234 {
235         struct mca_device *     m_dev = device_get_ivars(dev);
236         int                     rid = 0;
237
238         while (resource_list_find(&(m_dev->rl), SYS_RES_IOPORT, rid)) rid++;
239         resource_list_add(&(m_dev->rl), SYS_RES_IOPORT, rid,
240                 iobase, (iobase + iosize), iosize);
241
242         return;
243 }
244
245 static int
246 mca_probe (device_t dev)
247 {
248         device_t                child;
249         struct mca_device *     m_dev = NULL;
250         int                     devices_found = 0;
251         u_int8_t                slot;
252         u_int8_t                reg;
253
254         device_set_desc(dev, "MCA bus");
255
256         /* Disable adapter setup */
257         outb(MCA_ADAP_SETUP_REG, MCA_ADAP_SETUP_DIS);
258         /* Disable motherboard setup */
259         outb(MCA_MB_SETUP_REG, MCA_MB_SETUP_DIS);
260
261         if (bootverbose) {
262                 printf("POS REG     00 01 02 03 04 05 06 07\n");
263                 printf("-----------------------------------\n");
264         }
265
266         for (slot = 0; slot < MCA_MAX_SLOTS; slot++) {
267
268                 if (!m_dev) {
269                         m_dev = (struct mca_device *)malloc(sizeof(*m_dev),
270                                                             M_DEVBUF, M_NOWAIT);
271                         if (!m_dev) {
272                                 device_printf(dev, "cannot malloc mca_device");
273                                 break;
274                         }
275                 }
276                 bzero(m_dev, sizeof(*m_dev));
277
278                 /* Select adapter setup regs */
279                 outb(MCA_ADAP_SETUP_REG, ((slot & 0x0f) | MCA_ADAP_SET));
280
281                 /* Read the POS registers */
282                 for (reg = MCA_POS0; reg <= MCA_POS7; reg++) {
283                         m_dev->pos[reg] = inb(MCA_POS_REG(reg));
284                 }
285
286                 /* Disable adapter setup */
287                 outb(MCA_ADAP_SETUP_REG, MCA_ADAP_SETUP_DIS);
288
289                 if (bootverbose) {
290                         printf("mca slot %d:", slot + 1);       
291                         for (reg = MCA_POS0; reg <= MCA_POS7; reg++) {
292                                 printf(" %02x", m_dev->pos[reg]);
293                         }
294                         printf("\n");
295                 }
296
297                 m_dev->id = (u_int16_t)m_dev->pos[MCA_POS0] |
298                             ((u_int16_t)m_dev->pos[MCA_POS1] << 8);
299
300                 if (m_dev->id == 0xffff) {
301                         continue;
302                 }
303
304                 devices_found++;
305
306                 m_dev->enabled = (m_dev->pos[MCA_POS2] & MCA_POS2_ENABLE);
307                 m_dev->slot = slot;
308
309                 resource_list_init(&(m_dev->rl));
310
311                 child = device_add_child(dev, NULL, -1);
312                 device_set_ivars(child, m_dev);
313
314                 m_dev = NULL;
315         }
316
317         if (m_dev) {
318                 free(m_dev, M_DEVBUF);
319         }
320
321         return (devices_found ? 0 : ENXIO);
322 }
323
324 static void
325 mca_reg_print (dev, string, separator, column)
326         device_t        dev;
327         char *          string;
328         char *          separator;
329         int *           column;
330 {
331         int             length = strlen(string);
332
333         length += (separator ? 2 : 1);
334
335         if (((*column) + length) >= MAX_COL) {
336                 printf("\n");
337                 (*column) = 0;
338         } else if ((*column) != 0) {
339                 if (separator) {
340                         printf("%c", *separator);
341                         (*column)++;
342                 }
343                 printf(" ");
344                 (*column)++;
345         }
346
347         if ((*column) == 0) {
348                 (*column) += device_printf(dev, "%s", string);
349         } else {
350                 (*column) += printf("%s", string);
351         }
352
353         return;
354 }
355
356 static int
357 mca_print_child (device_t dev, device_t child)
358 {
359         char                            buf[MAX_COL+1];
360         struct mca_device *             m_dev = device_get_ivars(child);
361         int                             rid;
362         struct resource_list_entry *    rle;
363         char                            separator = ',';
364         int                             column = 0;
365         int                             retval = 0;
366
367         if (device_get_desc(child)) {
368                 snprintf(buf, sizeof(buf), "<%s>", device_get_desc(child));
369                 mca_reg_print(child, buf, NULL, &column);
370         }
371
372         rid = 0;
373         while ((rle = resource_list_find(&(m_dev->rl), SYS_RES_IOPORT, rid++))) {
374                 if (rle->count == 1) {
375                         snprintf(buf, sizeof(buf), "%s%lx",
376                                 ((rid == 1) ? "io 0x" : "0x"),
377                                 rle->start);
378                 } else {
379                         snprintf(buf, sizeof(buf), "%s%lx-0x%lx",
380                                 ((rid == 1) ? "io 0x" : "0x"),
381                                 rle->start,
382                                 (rle->start + rle->count));
383                 }
384                 mca_reg_print(child, buf,
385                         ((rid == 2) ? &separator : NULL), &column);
386         }
387
388         rid = 0;
389         while ((rle = resource_list_find(&(m_dev->rl), SYS_RES_MEMORY, rid++))) {
390                 if (rle->count == 1) {
391                         snprintf(buf, sizeof(buf), "%s%lx",
392                                 ((rid == 1) ? "mem 0x" : "0x"),
393                                 rle->start);
394                 } else {
395                         snprintf(buf, sizeof(buf), "%s%lx-0x%lx",
396                                 ((rid == 1) ? "mem 0x" : "0x"),
397                                 rle->start,
398                                 (rle->start + rle->count));
399                 }
400                 mca_reg_print(child, buf,
401                         ((rid == 2) ? &separator : NULL), &column);
402         }
403
404         rid = 0;
405         while ((rle = resource_list_find(&(m_dev->rl), SYS_RES_IRQ, rid++))) {
406                 snprintf(buf, sizeof(buf), "irq %ld", rle->start);
407                 mca_reg_print(child, buf,
408                         ((rid == 1) ? &separator : NULL), &column);
409         }
410
411         rid = 0;
412         while ((rle = resource_list_find(&(m_dev->rl), SYS_RES_DRQ, rid++))) {
413                 snprintf(buf, sizeof(buf), "drq %lx", rle->start);
414                 mca_reg_print(child, buf,
415                         ((rid == 1) ? &separator : NULL), &column);
416         }
417
418         snprintf(buf, sizeof(buf), "on %s id %04x slot %d\n",
419                 device_get_nameunit(dev),
420                 mca_get_id(child), mca_get_slot(child)+1);
421         mca_reg_print(child, buf, NULL, &column);
422
423         return (retval);
424 }
425
426 static void
427 mca_probe_nomatch (device_t dev, device_t child)
428 {
429         mca_id_t        mca_id = mca_get_id(child);
430         u_int8_t        slot = mca_get_slot(child);
431         u_int8_t        enabled = mca_get_enabled(child);
432
433         device_printf(dev, "unknown card (id 0x%04x, %s) at slot %d\n",
434                 mca_id,
435                 (enabled ? "enabled" : "disabled"),
436                 slot + 1);
437
438         return;
439 }
440
441 static int
442 mca_read_ivar (device_t dev, device_t child, int which, u_long * result)
443 {
444         struct mca_device *             m_dev = device_get_ivars(child);
445
446         switch (which) {
447                 case MCA_IVAR_SLOT:
448                         *result = m_dev->slot;
449                         break;
450                 case MCA_IVAR_ID:
451                         *result = m_dev->id;
452                         break;
453                 case MCA_IVAR_ENABLED:
454                         *result = m_dev->enabled;
455                         break;
456                 default:
457                         return (ENOENT);
458                         break;
459         }
460
461         return (0);
462 }
463
464 static int
465 mca_write_ivar(device_t dev, device_t child, int which, uintptr_t value)
466 {
467         return (EINVAL);
468 }
469
470 static struct resource *
471 mca_alloc_resource (device_t dev, device_t child, int type, int *rid,
472                     u_long start, u_long end, u_long count, u_int flags)
473 {
474         struct mca_device *             m_dev = device_get_ivars(child);
475         struct resource_list_entry *    rle;
476         int                             isdefault;
477         int                             passthrough;
478
479         isdefault = (start == 0UL && end == ~0UL);
480         passthrough = (device_get_parent(child) != dev);
481
482         if (!passthrough && !isdefault) {
483                 rle = resource_list_find(&(m_dev->rl), type, *rid);
484                 if (!rle) {
485                         resource_list_add(&(m_dev->rl), type, *rid,
486                                           start, end, count);
487                 }
488         }
489
490         if (type == SYS_RES_IRQ) {
491                 flags |= RF_SHAREABLE;
492         }
493
494         return (resource_list_alloc(&(m_dev->rl), dev, child, type, rid,
495                                     start, end, count, flags));
496 }
497
498 static int
499 mca_release_resource (device_t dev, device_t child, int type, int rid,
500                       struct resource * r)
501 {
502         struct mca_device *             m_dev = device_get_ivars(child);
503
504         return (resource_list_release(&(m_dev->rl), dev, child, type, rid, r));
505 }
506
507 static int
508 mca_get_resource(device_t dev, device_t child, int type, int rid,
509                  u_long *startp, u_long *countp)
510 {
511         struct mca_device *             m_dev = device_get_ivars(child);
512         struct resource_list *          rl = &(m_dev->rl);
513         struct resource_list_entry *    rle;
514
515         rle = resource_list_find(rl, type, rid);
516         if (!rle)
517                 return ENOENT;
518         
519         *startp = rle->start;
520         *countp = rle->count;
521
522         return (0);
523 }
524
525 static int
526 mca_set_resource(device_t dev, device_t child, int type, int rid,
527                  u_long start, u_long count)
528 {
529         struct mca_device *             m_dev = device_get_ivars(child);
530         struct resource_list *          rl = &(m_dev->rl);
531
532         resource_list_add(rl, type, rid, start, start + count - 1, count);
533         return (0);
534 }
535
536 static void
537 mca_delete_resource(device_t dev, device_t child, int type, int rid)
538 {
539         struct mca_device *             m_dev = device_get_ivars(child);
540         struct resource_list *          rl = &(m_dev->rl);
541
542         resource_list_delete(rl, type, rid);
543 }
544
545 static device_method_t mca_methods[] = {
546         /* Device interface */
547         DEVMETHOD(device_probe,         mca_probe),
548         DEVMETHOD(device_attach,        bus_generic_attach),
549         DEVMETHOD(device_shutdown,      bus_generic_shutdown),
550         DEVMETHOD(device_suspend,       bus_generic_suspend),
551         DEVMETHOD(device_resume,        bus_generic_resume),
552
553         /* Bus interface */
554         DEVMETHOD(bus_print_child,      mca_print_child),
555         DEVMETHOD(bus_probe_nomatch,    mca_probe_nomatch),
556         DEVMETHOD(bus_read_ivar,        mca_read_ivar),
557         DEVMETHOD(bus_write_ivar,       mca_write_ivar),
558         DEVMETHOD(bus_driver_added,     bus_generic_driver_added),
559         DEVMETHOD(bus_alloc_resource,   mca_alloc_resource),
560         DEVMETHOD(bus_release_resource, mca_release_resource),
561         DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
562         DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
563         DEVMETHOD(bus_setup_intr,       bus_generic_setup_intr),
564         DEVMETHOD(bus_teardown_intr,    bus_generic_teardown_intr),     
565
566         DEVMETHOD(bus_set_resource,     mca_set_resource),
567         DEVMETHOD(bus_get_resource,     mca_get_resource),
568         DEVMETHOD(bus_delete_resource,  mca_delete_resource),
569
570         { 0, 0 }
571 };
572
573 static driver_t mca_driver = {       
574         "mca",
575         mca_methods,
576         1,              /* no softc */
577 };
578
579 static devclass_t mca_devclass;
580
581 DRIVER_MODULE(mca, nexus, mca_driver, mca_devclass, 0, 0);