3a591b6728de06ff2a6110e13a541aa049dee30c
[dragonfly.git] / sys / i386 / 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/i386/i386/Attic/pnpbios.c,v 1.2 2004/10/14 03:05:52 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 void
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     /* no PnP BIOS information */
115     if (pt == NULL)
116         return;
117
118     /* ACPI already active */
119     if (devclass_get_softc(devclass_find("ACPI"), 0) != NULL)
120         return;
121     
122     bzero(&args, sizeof(args));
123     args.seg.code16.base = BIOS_PADDRTOVADDR(pt->pmentrybase);
124     args.seg.code16.limit = 0xffff;             /* XXX ? */
125     args.seg.data.base = BIOS_PADDRTOVADDR(pt->pmdataseg);
126     args.seg.data.limit = 0xffff;
127     args.entry = pt->pmentryoffset;
128
129     if ((error = bios16(&args, PNP_COUNT_DEVNODES, &ndevs, &bigdev)) || (args.r.eax & 0xff))
130         printf("pnpbios: error %d/%x getting device count/size limit\n", error, args.r.eax);
131     ndevs &= 0xff;                              /* clear high byte garbage */
132     if (bootverbose)
133         printf("pnpbios: %d devices, largest %d bytes\n", ndevs, bigdev);
134
135     devnodebuf = malloc(bigdev + (sizeof(struct pnp_sysdevargs) - sizeof(struct pnp_sysdev)), M_DEVBUF, M_INTWAIT);
136     pda = (struct pnp_sysdevargs *)devnodebuf;
137     pd = &pda->node;
138
139     for (currdev = 0, left = ndevs; (currdev != 0xff) && (left > 0); left--) {
140         bzero(pd, bigdev);
141         pda->next = currdev;
142
143         /* get current configuration */
144         if ((error = bios16(&args, PNP_GET_DEVNODE, &pda->next, &pda->node, 1))) {
145             printf("pnpbios: error %d making BIOS16 call\n", error);
146             break;
147         }
148         if (bootverbose)
149             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);
150
151         if ((error = (args.r.eax & 0xff))) {
152             if (bootverbose)
153                 printf("pnpbios: %s 0x%x fetching node %d\n", error & 0x80 ? "error" : "warning", error, currdev);
154             if (error & 0x80) 
155                 break;
156         }
157         currdev = pda->next;
158         if (pd->size < sizeof(struct pnp_sysdev)) {
159             printf("pnpbios: bogus system node data, aborting scan\n");
160             break;
161         }
162         
163         /*
164          * Ignore PICs so that we don't have to worry about the PICs
165          * claiming IRQs to prevent their use.  The PIC drivers
166          * already ensure that invalid IRQs are not used.
167          */
168         if (!strcmp(pnp_eisaformat(pd->devid), "PNP0000"))      /* ISA PIC */
169             continue;
170         if (!strcmp(pnp_eisaformat(pd->devid), "PNP0003"))      /* APIC */
171             continue;
172
173         /* Add the device and parse its resources */
174         dev = BUS_ADD_CHILD(parent, ISA_ORDER_PNP, NULL, -1);
175         isa_set_vendorid(dev, pd->devid);
176         isa_set_logicalid(dev, pd->devid);
177
178         /*
179          * It appears that some PnP BIOS doesn't allow us to re-enable
180          * the embedded system device once it is disabled.  We shall
181          * mark all system device nodes as "cannot be disabled", regardless
182          * of actual settings in the device attribute byte.  XXX
183          */
184 #if 0
185         isa_set_configattr(dev, 
186             ((pd->attrib & PNPATTR_NODISABLE) ?  0 : ISACFGATTR_CANDISABLE) |
187             ((!(pd->attrib & PNPATTR_NOCONFIG) && 
188                 PNPATTR_CONFIG(pd->attrib) != PNPATTR_CONFIG_STATIC)
189                 ? ISACFGATTR_DYNAMIC : 0));
190 #endif
191         isa_set_configattr(dev, 
192             (!(pd->attrib & PNPATTR_NOCONFIG) && 
193                 PNPATTR_CONFIG(pd->attrib) != PNPATTR_CONFIG_STATIC)
194                 ? ISACFGATTR_DYNAMIC : 0);
195
196         ISA_SET_CONFIG_CALLBACK(parent, dev, pnpbios_set_config, 0);
197         pnp_parse_resources(dev, &pd->devdata[0],
198                             pd->size - sizeof(struct pnp_sysdev), 0);
199         if (!device_get_desc(dev))
200             device_set_desc_copy(dev, pnp_eisaformat(pd->devid));
201
202         /* Find device IDs */
203         devid = &pd->devid;
204         compid = NULL;
205
206         /* look for a compatible device ID too */
207         left = pd->size - sizeof(struct pnp_sysdev);
208         idx = 0;
209         while (idx < left) {
210             tag = pd->devdata[idx++];
211             if (PNP_RES_TYPE(tag) == 0) {
212                 /* Small resource */
213                 switch (PNP_SRES_NUM(tag)) {
214                 case PNP_TAG_COMPAT_DEVICE:
215                     compid = (uint32_t *)(pd->devdata + idx);
216                     if (bootverbose)
217                         printf("pnpbios: node %d compat ID 0x%08x\n", pd->handle, *compid);
218                     /* FALLTHROUGH */
219                 case PNP_TAG_END:
220                     idx = left;
221                     break;
222                 default:
223                     idx += PNP_SRES_LEN(tag);
224                     break;
225                 }
226             } else
227                 /* Large resource, skip it */
228                 idx += *(uint16_t *)(pd->devdata + idx) + 2;
229         }
230         if (bootverbose) {
231             printf("pnpbios: handle %d device ID %s (%08x)", 
232                    pd->handle, pnp_eisaformat(*devid), *devid);
233             if (compid != NULL)
234                 printf(" compat ID %s (%08x)",
235                        pnp_eisaformat(*compid), *compid);
236             printf("\n");
237         }
238     }
239 }
240
241 static device_method_t pnpbios_methods[] = {
242         /* Device interface */
243         DEVMETHOD(device_identify,      pnpbios_identify),
244
245         { 0, 0 }
246 };
247
248 static driver_t pnpbios_driver = {
249         "pnpbios",
250         pnpbios_methods,
251         1,                      /* no softc */
252 };
253
254 static devclass_t pnpbios_devclass;
255
256 DRIVER_MODULE(pnpbios, isa, pnpbios_driver, pnpbios_devclass, 0, 0);