| Commit | Line | Data |
|---|---|---|
| 2ac05e91 SZ |
1 | /*- |
| 2 | * Copyright 1998 Massachusetts Institute of Technology | |
| 3 | * | |
| 4 | * Permission to use, copy, modify, and distribute this software and | |
| 5 | * its documentation for any purpose and without fee is hereby | |
| 6 | * granted, provided that both the above copyright notice and this | |
| 7 | * permission notice appear in all copies, that both the above | |
| 8 | * copyright notice and this permission notice appear in all | |
| 9 | * supporting documentation, and that the name of M.I.T. not be used | |
| 10 | * in advertising or publicity pertaining to distribution of the | |
| 11 | * software without specific, written prior permission. M.I.T. makes | |
| 12 | * no representations about the suitability of this software for any | |
| 13 | * purpose. It is provided "as is" without express or implied | |
| 14 | * warranty. | |
| 15 | * | |
| 16 | * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS | |
| 17 | * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, | |
| 18 | * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |
| 19 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT | |
| 20 | * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
| 21 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
| 22 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | |
| 23 | * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |
| 24 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |
| 25 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT | |
| 26 | * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
| 27 | * SUCH DAMAGE. | |
| 28 | * | |
| 29 | * $FreeBSD: src/sys/i386/i386/legacy.c,v 1.63.2.1.4.1 2009/04/15 03:14:26 kensmith Exp $ | |
| 30 | */ | |
| 31 | ||
| 32 | /* | |
| 33 | * This code implements a system driver for legacy systems that do not | |
| 34 | * support ACPI or when ACPI support is not present in the kernel. | |
| 35 | */ | |
| 36 | ||
| 37 | #include <sys/param.h> | |
| 38 | #include <sys/systm.h> | |
| 39 | #include <sys/bus.h> | |
| 40 | #include <sys/kernel.h> | |
| 41 | #include <sys/malloc.h> | |
| 42 | #include <sys/module.h> | |
| 43 | #include <sys/rman.h> | |
| 44 | ||
| 45 | #undef DEV_MCA | |
| 46 | #ifdef DEV_MCA | |
| 47 | #include <i386/bios/mca_machdep.h> | |
| 48 | #endif | |
| 49 | ||
| 50 | #include "legacyvar.h" | |
| 51 | ||
| 52 | static MALLOC_DEFINE(M_LEGACYDEV, "legacydrv", "legacy system device"); | |
| 53 | struct legacy_device { | |
| 54 | int lg_pcibus; | |
| 55 | }; | |
| 56 | ||
| 57 | #define DEVTOAT(dev) ((struct legacy_device *)device_get_ivars(dev)) | |
| 58 | ||
| 59 | static int legacy_identify(driver_t *, device_t); | |
| 60 | static int legacy_probe(device_t); | |
| 61 | static int legacy_attach(device_t); | |
| 62 | static int legacy_print_child(device_t, device_t); | |
| 63 | static device_t legacy_add_child(device_t, device_t, int, const char *, int); | |
| 64 | static int legacy_read_ivar(device_t, device_t, int, uintptr_t *); | |
| 65 | static int legacy_write_ivar(device_t, device_t, int, uintptr_t); | |
| 66 | ||
| 67 | static device_method_t legacy_methods[] = { | |
| 68 | /* Device interface */ | |
| 69 | DEVMETHOD(device_identify, legacy_identify), | |
| 70 | DEVMETHOD(device_probe, legacy_probe), | |
| 71 | DEVMETHOD(device_attach, legacy_attach), | |
| 72 | DEVMETHOD(device_detach, bus_generic_detach), | |
| 73 | DEVMETHOD(device_shutdown, bus_generic_shutdown), | |
| 74 | DEVMETHOD(device_suspend, bus_generic_suspend), | |
| 75 | DEVMETHOD(device_resume, bus_generic_resume), | |
| 76 | ||
| 77 | /* Bus interface */ | |
| 78 | DEVMETHOD(bus_print_child, legacy_print_child), | |
| 79 | DEVMETHOD(bus_add_child, legacy_add_child), | |
| 80 | DEVMETHOD(bus_read_ivar, legacy_read_ivar), | |
| 81 | DEVMETHOD(bus_write_ivar, legacy_write_ivar), | |
| 82 | DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource), | |
| 83 | DEVMETHOD(bus_release_resource, bus_generic_release_resource), | |
| 84 | DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), | |
| 85 | DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), | |
| 86 | DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), | |
| 87 | DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), | |
| 88 | ||
| 89 | { 0, 0 } | |
| 90 | }; | |
| 91 | ||
| 92 | static driver_t legacy_driver = { | |
| 93 | "legacy", | |
| 94 | legacy_methods, | |
| 95 | 1, /* no softc */ | |
| 96 | }; | |
| 97 | static devclass_t legacy_devclass; | |
| 98 | ||
| 99 | DRIVER_MODULE(legacy, nexus, legacy_driver, legacy_devclass, 0, 0); | |
| 100 | ||
| 101 | static int | |
| 102 | legacy_identify(driver_t *driver, device_t parent) | |
| 103 | { | |
| 104 | /* | |
| 105 | * Basically a static device, there's no point reinstalling it | |
| 106 | * on rescan. | |
| 107 | */ | |
| 108 | if (device_get_state(parent) == DS_ATTACHED) | |
| 109 | return (0); | |
| 110 | if (device_get_state(parent) == DS_INPROGRESS) | |
| 111 | return (0); | |
| 112 | ||
| 113 | /* | |
| 114 | * Add child device with order of 11 so it gets probed | |
| 115 | * after ACPI (which is at order 10). | |
| 116 | */ | |
| 117 | if (BUS_ADD_CHILD(parent, parent, 11, "legacy", 0) == NULL) | |
| 118 | panic("legacy: could not attach"); | |
| 119 | return (0); | |
| 120 | } | |
| 121 | ||
| 122 | static int | |
| 123 | legacy_probe(device_t dev) | |
| 124 | { | |
| 125 | #ifdef ACPI_ENABLE_PCI | |
| 126 | device_t acpi; | |
| 127 | ||
| 128 | /* | |
| 129 | * Fail to probe if ACPI is ok. | |
| 130 | */ | |
| 131 | acpi = devclass_get_device(devclass_find("acpi"), 0); | |
| 132 | if (acpi != NULL && device_is_alive(acpi)) | |
| 133 | return (ENXIO); | |
| 134 | #endif /* ACPI_ENABLE_PCI */ | |
| 135 | device_set_desc(dev, "legacy system"); | |
| 136 | device_quiet(dev); | |
| 137 | return (0); | |
| 138 | } | |
| 139 | ||
| 140 | static int | |
| 141 | legacy_attach(device_t dev) | |
| 142 | { | |
| 143 | device_t child; | |
| 144 | ||
| 145 | /* | |
| 146 | * Let our child drivers identify any child devices that they | |
| 147 | * can find. Once that is done attach any devices that we | |
| 148 | * found. | |
| 149 | */ | |
| 150 | bus_generic_probe(dev); | |
| 151 | bus_generic_attach(dev); | |
| 152 | ||
| 153 | /* | |
| 154 | * If we didn't see EISA or ISA on a pci bridge, create some | |
| 155 | * connection points now so they show up "on motherboard". | |
| 156 | */ | |
| 157 | if (!devclass_get_device(devclass_find("eisa"), 0)) { | |
| 158 | child = BUS_ADD_CHILD(dev, dev, 0, "eisa", 0); | |
| 159 | if (child == NULL) | |
| 160 | panic("legacy_attach eisa"); | |
| 161 | device_probe_and_attach(child); | |
| 162 | } | |
| 163 | #ifdef DEV_MCA | |
| 164 | if (MCA_system && !devclass_get_device(devclass_find("mca"), 0)) { | |
| 165 | child = BUS_ADD_CHILD(dev, dev, 0, "mca", 0); | |
| 166 | if (child == 0) | |
| 167 | panic("legacy_probe mca"); | |
| 168 | device_probe_and_attach(child); | |
| 169 | } | |
| 170 | #endif | |
| 171 | if (!devclass_get_device(devclass_find("isa"), 0)) { | |
| 172 | child = BUS_ADD_CHILD(dev, dev, 0, "isa", 0); | |
| 173 | if (child == NULL) | |
| 174 | panic("legacy_attach isa"); | |
| 175 | device_probe_and_attach(child); | |
| 176 | } | |
| 177 | ||
| 178 | return 0; | |
| 179 | } | |
| 180 | ||
| 181 | static int | |
| 182 | legacy_print_child(device_t bus, device_t child) | |
| 183 | { | |
| 184 | struct legacy_device *atdev = DEVTOAT(child); | |
| 185 | int retval = 0; | |
| 186 | ||
| 187 | retval += bus_print_child_header(bus, child); | |
| 188 | if (atdev->lg_pcibus != -1) | |
| 189 | retval += kprintf(" pcibus %d", atdev->lg_pcibus); | |
| 190 | retval += kprintf(" on motherboard\n"); /* XXX "motherboard", ick */ | |
| 191 | ||
| 192 | return (retval); | |
| 193 | } | |
| 194 | ||
| 195 | static device_t | |
| 196 | legacy_add_child(device_t bus, device_t parent, int order, const char *name, int unit) | |
| 197 | { | |
| 198 | device_t child; | |
| 199 | struct legacy_device *atdev; | |
| 200 | atdev = kmalloc(sizeof(struct legacy_device), M_LEGACYDEV, | |
| 201 | M_NOWAIT | M_ZERO); | |
| 202 | if (atdev == NULL) | |
| 203 | return(NULL); | |
| 204 | atdev->lg_pcibus = -1; | |
| 205 | ||
| 206 | child = device_add_child_ordered(bus, order, name, unit); | |
| 207 | if (child == NULL) { | |
| 208 | kfree(atdev, M_LEGACYDEV); | |
| 209 | } | |
| 210 | else { | |
| 211 | /* should we kfree this in legacy_child_detached? */ | |
| 212 | device_set_ivars(child, atdev); | |
| 213 | } | |
| 214 | return (child); | |
| 215 | } | |
| 216 | ||
| 217 | static int | |
| 218 | legacy_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) | |
| 219 | { | |
| 220 | struct legacy_device *atdev = DEVTOAT(child); | |
| 221 | ||
| 222 | switch (which) { | |
| 223 | case LEGACY_IVAR_PCIDOMAIN: | |
| 224 | *result = 0; | |
| 225 | break; | |
| 226 | case LEGACY_IVAR_PCIBUS: | |
| 227 | *result = atdev->lg_pcibus; | |
| 228 | break; | |
| 229 | default: | |
| 230 | return ENOENT; | |
| 231 | } | |
| 232 | return 0; | |
| 233 | } | |
| 234 | ||
| 235 | static int | |
| 236 | legacy_write_ivar(device_t dev, device_t child, int which, uintptr_t value) | |
| 237 | { | |
| 238 | struct legacy_device *atdev = DEVTOAT(child); | |
| 239 | ||
| 240 | switch (which) { | |
| 241 | case LEGACY_IVAR_PCIDOMAIN: | |
| 242 | return EINVAL; | |
| 243 | case LEGACY_IVAR_PCIBUS: | |
| 244 | atdev->lg_pcibus = value; | |
| 245 | break; | |
| 246 | default: | |
| 247 | return ENOENT; | |
| 248 | } | |
| 249 | return 0; | |
| 250 | } | |
| 251 | ||
| 252 | #ifdef notyet | |
| 253 | /* | |
| 254 | * Legacy CPU attachment when ACPI is not available. Drivers like | |
| 255 | * cpufreq(4) hang off this. | |
| 256 | */ | |
| 257 | static void cpu_identify(driver_t *driver, device_t parent); | |
| 258 | static int cpu_read_ivar(device_t dev, device_t child, int index, | |
| 259 | uintptr_t *result); | |
| 260 | static device_t cpu_add_child(device_t bus, int order, const char *name, | |
| 261 | int unit); | |
| 262 | static struct resource_list *cpu_get_rlist(device_t dev, device_t child); | |
| 263 | ||
| 264 | struct cpu_device { | |
| 265 | struct resource_list cd_rl; | |
| 266 | struct pcpu *cd_pcpu; | |
| 267 | }; | |
| 268 | ||
| 269 | static device_method_t cpu_methods[] = { | |
| 270 | /* Device interface */ | |
| 271 | DEVMETHOD(device_identify, cpu_identify), | |
| 272 | DEVMETHOD(device_probe, bus_generic_probe), | |
| 273 | DEVMETHOD(device_attach, bus_generic_attach), | |
| 274 | DEVMETHOD(device_detach, bus_generic_detach), | |
| 275 | DEVMETHOD(device_shutdown, bus_generic_shutdown), | |
| 276 | DEVMETHOD(device_suspend, bus_generic_suspend), | |
| 277 | DEVMETHOD(device_resume, bus_generic_resume), | |
| 278 | ||
| 279 | /* Bus interface */ | |
| 280 | DEVMETHOD(bus_add_child, cpu_add_child), | |
| 281 | DEVMETHOD(bus_read_ivar, cpu_read_ivar), | |
| 282 | DEVMETHOD(bus_print_child, bus_generic_print_child), | |
| 283 | DEVMETHOD(bus_get_resource_list, cpu_get_rlist), | |
| 284 | DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource), | |
| 285 | DEVMETHOD(bus_set_resource, bus_generic_rl_set_resource), | |
| 286 | DEVMETHOD(bus_alloc_resource, bus_generic_rl_alloc_resource), | |
| 287 | DEVMETHOD(bus_release_resource, bus_generic_rl_release_resource), | |
| 288 | DEVMETHOD(bus_driver_added, bus_generic_driver_added), | |
| 289 | DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), | |
| 290 | DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), | |
| 291 | DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), | |
| 292 | DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), | |
| 293 | ||
| 294 | { 0, 0 } | |
| 295 | }; | |
| 296 | ||
| 297 | static driver_t cpu_driver = { | |
| 298 | "cpu", | |
| 299 | cpu_methods, | |
| 300 | 1, /* no softc */ | |
| 301 | }; | |
| 302 | static devclass_t cpu_devclass; | |
| 303 | DRIVER_MODULE(cpu, legacy, cpu_driver, cpu_devclass, 0, 0); | |
| 304 | ||
| 305 | static void | |
| 306 | cpu_identify(driver_t *driver, device_t parent) | |
| 307 | { | |
| 308 | device_t child; | |
| 309 | int i; | |
| 310 | ||
| 311 | /* | |
| 312 | * Attach a cpuX device for each CPU. We use an order of 150 | |
| 313 | * so that these devices are attached after the Host-PCI | |
| 314 | * bridges (which are added at order 100). | |
| 315 | */ | |
| 316 | for (i = 0; i <= mp_maxid; i++) | |
| 317 | if (!CPU_ABSENT(i)) { | |
| 318 | child = BUS_ADD_CHILD(parent, 150, "cpu", i); | |
| 319 | if (child == NULL) | |
| 320 | panic("legacy_attach cpu"); | |
| 321 | } | |
| 322 | } | |
| 323 | ||
| 324 | static device_t | |
| 325 | cpu_add_child(device_t bus, int order, const char *name, int unit) | |
| 326 | { | |
| 327 | struct cpu_device *cd; | |
| 328 | device_t child; | |
| 329 | struct pcpu *pc; | |
| 330 | ||
| 331 | if ((cd = kmalloc(sizeof(*cd), M_DEVBUF, M_NOWAIT | M_ZERO)) == NULL) | |
| 332 | return (NULL); | |
| 333 | ||
| 334 | resource_list_init(&cd->cd_rl); | |
| 335 | pc = pcpu_find(device_get_unit(bus)); | |
| 336 | cd->cd_pcpu = pc; | |
| 337 | ||
| 338 | child = device_add_child_ordered(bus, order, name, unit); | |
| 339 | if (child != NULL) { | |
| 340 | pc->pc_device = child; | |
| 341 | device_set_ivars(child, cd); | |
| 342 | } else | |
| 343 | kfree(cd, M_DEVBUF); | |
| 344 | return (child); | |
| 345 | } | |
| 346 | ||
| 347 | static struct resource_list * | |
| 348 | cpu_get_rlist(device_t dev, device_t child) | |
| 349 | { | |
| 350 | struct cpu_device *cpdev; | |
| 351 | ||
| 352 | cpdev = device_get_ivars(child); | |
| 353 | return (&cpdev->cd_rl); | |
| 354 | } | |
| 355 | ||
| 356 | static int | |
| 357 | cpu_read_ivar(device_t dev, device_t child, int index, uintptr_t *result) | |
| 358 | { | |
| 359 | struct cpu_device *cpdev; | |
| 360 | ||
| 361 | if (index != CPU_IVAR_PCPU) | |
| 362 | return (ENOENT); | |
| 363 | cpdev = device_get_ivars(child); | |
| 364 | *result = (uintptr_t)cpdev->cd_pcpu; | |
| 365 | return (0); | |
| 366 | } | |
| 367 | #endif |