Cleanup some of the newbus infrastructure.
[dragonfly.git] / sys / platform / pc32 / i386 / pnpbios.c
1 /*-
2  * Copyright (c) 2004 Joerg Sonnenberger <joerg@bec.de>
3  *
4  * Copyright (c) 1997 Michael Smith
5  * Copyright (c) 1998 Jonathan Lemon
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * $DragonFly: src/sys/platform/pc32/i386/pnpbios.c,v 1.3 2005/10/28 03:25:57 dillon Exp $
30  */
31
32 /*
33  * Code for dealing with the PnP-BIOS in x86 PC systems.
34  */
35
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/kernel.h>
39 #include <sys/malloc.h>
40 #include <sys/bus.h>
41
42 #include <vm/vm.h>
43 #include <vm/pmap.h>
44
45 #include <machine/md_var.h>
46 #include <machine/pc/bios.h>
47
48 #include <bus/isa/pnpreg.h>
49 #include <bus/isa/isavar.h>
50
51 /*
52  * PnP BIOS interface; enumerate devices only known to the system
53  * BIOS and save information about them for later use.
54  */
55
56 struct pnp_sysdev 
57 {
58     uint16_t    size;
59     uint8_t     handle;
60     uint32_t    devid;
61     uint8_t     type[3];
62     uint16_t    attrib;
63     /* device-specific data comes here */
64     uint8_t     devdata[0];
65 } __attribute__((__packed__));
66
67 #define PNPATTR_NODISABLE       (1<<0)  /* can't be disabled */
68 #define PNPATTR_NOCONFIG        (1<<1)  /* can't be configured */
69 #define PNPATTR_OUTPUT          (1<<2)  /* can be primary output */
70 #define PNPATTR_INPUT           (1<<3)  /* can be primary input */
71 #define PNPATTR_BOOTABLE        (1<<4)  /* can be booted from */
72 #define PNPATTR_DOCK            (1<<5)  /* is a docking station */
73 #define PNPATTR_REMOVEABLE      (1<<6)  /* device is removeable */
74
75 #define PNPATTR_CONFIG(a)       (((a) >> 7) & 0x03)
76 #define PNPATTR_CONFIG_STATIC   0x00
77 #define PNPATTR_CONFIG_DYNAMIC  0x01
78 #define PNPATTR_CONFIG_DYNONLY  0x03
79
80 /* We have to cluster arguments within a 64k range for the bios16 call */
81 struct pnp_sysdevargs
82 {
83     uint16_t    next;
84     uint16_t    pad;
85     struct pnp_sysdev node;
86 };
87
88 /*
89  * This function is called after the bus has assigned resource
90  * locations for a logical device.
91  */
92 static void
93 pnpbios_set_config(void *arg, struct isa_config *config, int enable)
94 {
95 }
96
97 /*
98  * Quiz the PnP BIOS, build a list of PNP IDs and resource data.
99  */
100 static int
101 pnpbios_identify(driver_t *driver, device_t parent)
102 {
103     struct PnPBIOS_table        *pt = PnPBIOStable;
104     struct bios_args            args;
105     struct pnp_sysdev           *pd;
106     struct pnp_sysdevargs       *pda;
107     uint16_t                    ndevs, bigdev;
108     int                         error, currdev;
109     uint8_t                     *devnodebuf, tag;
110     uint32_t                    *devid, *compid;
111     int                         idx, left;
112     device_t                    dev;
113
114     /*
115      * Umm, we aren't going to rescan the PnP BIOS to look for new additions.
116      */
117     if (device_get_state(parent) == DS_ATTACHED)
118         return (0);
119         
120     /* no PnP BIOS information */
121     if (pt == NULL)
122         return (ENXIO);
123
124     /* ACPI already active */
125     if (devclass_get_softc(devclass_find("ACPI"), 0) != NULL)
126         return (ENXIO);
127     
128     bzero(&args, sizeof(args));
129     args.seg.code16.base = BIOS_PADDRTOVADDR(pt->pmentrybase);
130     args.seg.code16.limit = 0xffff;             /* XXX ? */
131     args.seg.data.base = BIOS_PADDRTOVADDR(pt->pmdataseg);
132     args.seg.data.limit = 0xffff;
133     args.entry = pt->pmentryoffset;
134
135     if ((error = bios16(&args, PNP_COUNT_DEVNODES, &ndevs, &bigdev)) || (args.r.eax & 0xff))
136         printf("pnpbios: error %d/%x getting device count/size limit\n", error, args.r.eax);
137     ndevs &= 0xff;                              /* clear high byte garbage */
138     if (bootverbose)
139         printf("pnpbios: %d devices, largest %d bytes\n", ndevs, bigdev);
140
141     devnodebuf = malloc(bigdev + (sizeof(struct pnp_sysdevargs) - sizeof(struct pnp_sysdev)), M_DEVBUF, M_INTWAIT);
142     pda = (struct pnp_sysdevargs *)devnodebuf;
143     pd = &pda->node;
144
145     for (currdev = 0, left = ndevs; (currdev != 0xff) && (left > 0); left--) {
146         bzero(pd, bigdev);
147         pda->next = currdev;
148
149         /* get current configuration */
150         if ((error = bios16(&args, PNP_GET_DEVNODE, &pda->next, &pda->node, 1))) {
151             printf("pnpbios: error %d making BIOS16 call\n", error);
152             break;
153         }
154         if (bootverbose)
155             printf("pnp_get_devnode cd=%d nxt=%d size=%d handle=%d devid=%08x type=%02x%02x%02x, attrib=%04x\n", currdev, pda->next, pd->size, pd->handle, pd->devid, pd->type[0], pd->type[1], pd->type[2], pd->attrib);
156
157         if ((error = (args.r.eax & 0xff))) {
158             if (bootverbose)
159                 printf("pnpbios: %s 0x%x fetching node %d\n", error & 0x80 ? "error" : "warning", error, currdev);
160             if (error & 0x80) 
161                 break;
162         }
163         currdev = pda->next;
164         if (pd->size < sizeof(struct pnp_sysdev)) {
165             printf("pnpbios: bogus system node data, aborting scan\n");
166             break;
167         }
168         
169         /*
170          * Ignore PICs so that we don't have to worry about the PICs
171          * claiming IRQs to prevent their use.  The PIC drivers
172          * already ensure that invalid IRQs are not used.
173          */
174         if (!strcmp(pnp_eisaformat(pd->devid), "PNP0000"))      /* ISA PIC */
175             continue;
176         if (!strcmp(pnp_eisaformat(pd->devid), "PNP0003"))      /* APIC */
177             continue;
178
179         /* Add the device and parse its resources */
180         dev = BUS_ADD_CHILD(parent, ISA_ORDER_PNP, NULL, -1);
181         isa_set_vendorid(dev, pd->devid);
182         isa_set_logicalid(dev, pd->devid);
183
184         /*
185          * It appears that some PnP BIOS doesn't allow us to re-enable
186          * the embedded system device once it is disabled.  We shall
187          * mark all system device nodes as "cannot be disabled", regardless
188          * of actual settings in the device attribute byte.  XXX
189          */
190 #if 0
191         isa_set_configattr(dev, 
192             ((pd->attrib & PNPATTR_NODISABLE) ?  0 : ISACFGATTR_CANDISABLE) |
193             ((!(pd->attrib & PNPATTR_NOCONFIG) && 
194                 PNPATTR_CONFIG(pd->attrib) != PNPATTR_CONFIG_STATIC)
195                 ? ISACFGATTR_DYNAMIC : 0));
196 #endif
197         isa_set_configattr(dev, 
198             (!(pd->attrib & PNPATTR_NOCONFIG) && 
199                 PNPATTR_CONFIG(pd->attrib) != PNPATTR_CONFIG_STATIC)
200                 ? ISACFGATTR_DYNAMIC : 0);
201
202         ISA_SET_CONFIG_CALLBACK(parent, dev, pnpbios_set_config, 0);
203         pnp_parse_resources(dev, &pd->devdata[0],
204                             pd->size - sizeof(struct pnp_sysdev), 0);
205         if (!device_get_desc(dev))
206             device_set_desc_copy(dev, pnp_eisaformat(pd->devid));
207
208         /* Find device IDs */
209         devid = &pd->devid;
210         compid = NULL;
211
212         /* look for a compatible device ID too */
213         left = pd->size - sizeof(struct pnp_sysdev);
214         idx = 0;
215         while (idx < left) {
216             tag = pd->devdata[idx++];
217             if (PNP_RES_TYPE(tag) == 0) {
218                 /* Small resource */
219                 switch (PNP_SRES_NUM(tag)) {
220                 case PNP_TAG_COMPAT_DEVICE:
221                     compid = (uint32_t *)(pd->devdata + idx);
222                     if (bootverbose)
223                         printf("pnpbios: node %d compat ID 0x%08x\n", pd->handle, *compid);
224                     /* FALLTHROUGH */
225                 case PNP_TAG_END:
226                     idx = left;
227                     break;
228                 default:
229                     idx += PNP_SRES_LEN(tag);
230                     break;
231                 }
232             } else
233                 /* Large resource, skip it */
234                 idx += *(uint16_t *)(pd->devdata + idx) + 2;
235         }
236         if (bootverbose) {
237             printf("pnpbios: handle %d device ID %s (%08x)", 
238                    pd->handle, pnp_eisaformat(*devid), *devid);
239             if (compid != NULL)
240                 printf(" compat ID %s (%08x)",
241                        pnp_eisaformat(*compid), *compid);
242             printf("\n");
243         }
244     }
245     return (0);
246 }
247
248 static device_method_t pnpbios_methods[] = {
249         /* Device interface */
250         DEVMETHOD(device_identify,      pnpbios_identify),
251
252         { 0, 0 }
253 };
254
255 static driver_t pnpbios_driver = {
256         "pnpbios",
257         pnpbios_methods,
258         1,                      /* no softc */
259 };
260
261 static devclass_t pnpbios_devclass;
262
263 DRIVER_MODULE(pnpbios, isa, pnpbios_driver, pnpbios_devclass, 0, 0);