1d72a0786SJohn Baldwin /*- 2718cf2ccSPedro F. Giffuni * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3718cf2ccSPedro F. Giffuni * 4d72a0786SJohn Baldwin * Copyright (c) 2006 IronPort Systems Inc. <ambrisko@ironport.com> 5d72a0786SJohn Baldwin * All rights reserved. 6d72a0786SJohn Baldwin * 7d72a0786SJohn Baldwin * Redistribution and use in source and binary forms, with or without 8d72a0786SJohn Baldwin * modification, are permitted provided that the following conditions 9d72a0786SJohn Baldwin * are met: 10d72a0786SJohn Baldwin * 1. Redistributions of source code must retain the above copyright 11d72a0786SJohn Baldwin * notice, this list of conditions and the following disclaimer. 12d72a0786SJohn Baldwin * 2. Redistributions in binary form must reproduce the above copyright 13d72a0786SJohn Baldwin * notice, this list of conditions and the following disclaimer in the 14d72a0786SJohn Baldwin * documentation and/or other materials provided with the distribution. 15d72a0786SJohn Baldwin * 16d72a0786SJohn Baldwin * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17d72a0786SJohn Baldwin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18d72a0786SJohn Baldwin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19d72a0786SJohn Baldwin * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20d72a0786SJohn Baldwin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21d72a0786SJohn Baldwin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22d72a0786SJohn Baldwin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23d72a0786SJohn Baldwin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24d72a0786SJohn Baldwin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25d72a0786SJohn Baldwin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26d72a0786SJohn Baldwin * SUCH DAMAGE. 27d72a0786SJohn Baldwin */ 28d72a0786SJohn Baldwin 29d72a0786SJohn Baldwin #include <sys/cdefs.h> 30d72a0786SJohn Baldwin __FBSDID("$FreeBSD$"); 31d72a0786SJohn Baldwin 32d72a0786SJohn Baldwin #include <sys/param.h> 33d72a0786SJohn Baldwin #include <sys/systm.h> 34d72a0786SJohn Baldwin #include <sys/bus.h> 35d72a0786SJohn Baldwin #include <sys/condvar.h> 36d72a0786SJohn Baldwin #include <sys/eventhandler.h> 37d72a0786SJohn Baldwin #include <sys/kernel.h> 38d72a0786SJohn Baldwin #include <sys/module.h> 39d72a0786SJohn Baldwin #include <sys/rman.h> 40d72a0786SJohn Baldwin #include <sys/selinfo.h> 41*ee8b757aSYinlong Lu #include <sys/efi.h> 42d72a0786SJohn Baldwin 43d72a0786SJohn Baldwin #include <machine/pci_cfgreg.h> 44d72a0786SJohn Baldwin #include <dev/pci/pcireg.h> 45d72a0786SJohn Baldwin 46d72a0786SJohn Baldwin #include <isa/isavar.h> 47d72a0786SJohn Baldwin 48d72a0786SJohn Baldwin #ifdef LOCAL_MODULE 49d72a0786SJohn Baldwin #include <ipmi.h> 50d72a0786SJohn Baldwin #include <ipmivars.h> 51d72a0786SJohn Baldwin #else 52d72a0786SJohn Baldwin #include <sys/ipmi.h> 53d72a0786SJohn Baldwin #include <dev/ipmi/ipmivars.h> 54d72a0786SJohn Baldwin #endif 55d72a0786SJohn Baldwin 56d72a0786SJohn Baldwin static void 57d72a0786SJohn Baldwin ipmi_isa_identify(driver_t *driver, device_t parent) 58d72a0786SJohn Baldwin { 59d72a0786SJohn Baldwin struct ipmi_get_info info; 60d72a0786SJohn Baldwin uint32_t devid; 61d72a0786SJohn Baldwin 629b256674SSean Bruno if (ipmi_smbios_identify(&info) && info.iface_type != SSIF_MODE && 639b256674SSean Bruno device_find_child(parent, "ipmi", -1) == NULL) { 64d72a0786SJohn Baldwin /* 65d72a0786SJohn Baldwin * XXX: Hack alert. On some broken systems, the IPMI 66d72a0786SJohn Baldwin * interface is described via SMBIOS, but the actual 67d72a0786SJohn Baldwin * IO resource is in a PCI device BAR, so we have to let 68d72a0786SJohn Baldwin * the PCI device attach ipmi instead. In that case don't 69d72a0786SJohn Baldwin * create an isa ipmi device. For now we hardcode the list 70d72a0786SJohn Baldwin * of bus, device, function tuples. 71d72a0786SJohn Baldwin */ 72d72a0786SJohn Baldwin devid = pci_cfgregread(0, 4, 2, PCIR_DEVVENDOR, 4); 73d72a0786SJohn Baldwin if (devid != 0xffffffff && 74d72a0786SJohn Baldwin ipmi_pci_match(devid & 0xffff, devid >> 16) != NULL) 75d72a0786SJohn Baldwin return; 76d72a0786SJohn Baldwin BUS_ADD_CHILD(parent, 0, "ipmi", -1); 77d72a0786SJohn Baldwin } 78d72a0786SJohn Baldwin } 79d72a0786SJohn Baldwin 80d72a0786SJohn Baldwin static int 81d72a0786SJohn Baldwin ipmi_isa_probe(device_t dev) 82d72a0786SJohn Baldwin { 83d72a0786SJohn Baldwin 84ef3103bbSSean Bruno /* 85ef3103bbSSean Bruno * Give other drivers precedence. Unfortunately, this doesn't 86ef3103bbSSean Bruno * work if we have an SMBIOS table that duplicates a PCI device 87ef3103bbSSean Bruno * that's later on the bus than the PCI-ISA bridge. 88ef3103bbSSean Bruno */ 89ef3103bbSSean Bruno if (ipmi_attached) 90ef3103bbSSean Bruno return (ENXIO); 91ef3103bbSSean Bruno 92d72a0786SJohn Baldwin /* Skip any PNP devices. */ 93d72a0786SJohn Baldwin if (isa_get_logicalid(dev) != 0) 94d72a0786SJohn Baldwin return (ENXIO); 95d72a0786SJohn Baldwin 96d72a0786SJohn Baldwin device_set_desc(dev, "IPMI System Interface"); 97d72a0786SJohn Baldwin return (BUS_PROBE_DEFAULT); 98d72a0786SJohn Baldwin } 99d72a0786SJohn Baldwin 100d72a0786SJohn Baldwin static int 10172d73315SDoug Ambrisko ipmi_hint_identify(device_t dev, struct ipmi_get_info *info) 10272d73315SDoug Ambrisko { 10372d73315SDoug Ambrisko const char *mode, *name; 10472d73315SDoug Ambrisko int i, unit, val; 10572d73315SDoug Ambrisko 10672d73315SDoug Ambrisko /* We require at least a "mode" hint. */ 10772d73315SDoug Ambrisko name = device_get_name(dev); 10872d73315SDoug Ambrisko unit = device_get_unit(dev); 10972d73315SDoug Ambrisko if (resource_string_value(name, unit, "mode", &mode) != 0) 11072d73315SDoug Ambrisko return (0); 11172d73315SDoug Ambrisko 11272d73315SDoug Ambrisko /* Set the mode and default I/O resources for each mode. */ 11372d73315SDoug Ambrisko bzero(info, sizeof(struct ipmi_get_info)); 11472d73315SDoug Ambrisko if (strcasecmp(mode, "KCS") == 0) { 11572d73315SDoug Ambrisko info->iface_type = KCS_MODE; 11672d73315SDoug Ambrisko info->address = 0xca2; 11772d73315SDoug Ambrisko info->io_mode = 1; 11872d73315SDoug Ambrisko info->offset = 1; 11972d73315SDoug Ambrisko } else if (strcasecmp(mode, "SMIC") == 0) { 12072d73315SDoug Ambrisko info->iface_type = SMIC_MODE; 12172d73315SDoug Ambrisko info->address = 0xca9; 12272d73315SDoug Ambrisko info->io_mode = 1; 12372d73315SDoug Ambrisko info->offset = 1; 12472d73315SDoug Ambrisko } else if (strcasecmp(mode, "BT") == 0) { 12572d73315SDoug Ambrisko info->iface_type = BT_MODE; 12672d73315SDoug Ambrisko info->address = 0xe4; 12772d73315SDoug Ambrisko info->io_mode = 1; 12872d73315SDoug Ambrisko info->offset = 1; 12972d73315SDoug Ambrisko } else { 13072d73315SDoug Ambrisko device_printf(dev, "Invalid mode %s\n", mode); 13172d73315SDoug Ambrisko return (0); 13272d73315SDoug Ambrisko } 13372d73315SDoug Ambrisko 13472d73315SDoug Ambrisko /* 13572d73315SDoug Ambrisko * Kill any resources that isahint.c might have setup for us 13672d73315SDoug Ambrisko * since it will conflict with how we do resources. 13772d73315SDoug Ambrisko */ 13872d73315SDoug Ambrisko for (i = 0; i < 2; i++) { 13972d73315SDoug Ambrisko bus_delete_resource(dev, SYS_RES_MEMORY, i); 14072d73315SDoug Ambrisko bus_delete_resource(dev, SYS_RES_IOPORT, i); 14172d73315SDoug Ambrisko } 14272d73315SDoug Ambrisko 14372d73315SDoug Ambrisko /* Allow the I/O address to be overriden via hints. */ 14472d73315SDoug Ambrisko if (resource_int_value(name, unit, "port", &val) == 0 && val != 0) { 14572d73315SDoug Ambrisko info->address = val; 14672d73315SDoug Ambrisko info->io_mode = 1; 14772d73315SDoug Ambrisko } else if (resource_int_value(name, unit, "maddr", &val) == 0 && 14872d73315SDoug Ambrisko val != 0) { 14972d73315SDoug Ambrisko info->address = val; 15072d73315SDoug Ambrisko info->io_mode = 0; 15172d73315SDoug Ambrisko } 15272d73315SDoug Ambrisko 15372d73315SDoug Ambrisko /* Allow the spacing to be overriden. */ 15472d73315SDoug Ambrisko if (resource_int_value(name, unit, "spacing", &val) == 0) { 15572d73315SDoug Ambrisko switch (val) { 15672d73315SDoug Ambrisko case 8: 15772d73315SDoug Ambrisko info->offset = 1; 15872d73315SDoug Ambrisko break; 15972d73315SDoug Ambrisko case 16: 16072d73315SDoug Ambrisko info->offset = 2; 16172d73315SDoug Ambrisko break; 16272d73315SDoug Ambrisko case 32: 16372d73315SDoug Ambrisko info->offset = 4; 16472d73315SDoug Ambrisko break; 16572d73315SDoug Ambrisko default: 16672d73315SDoug Ambrisko device_printf(dev, "Invalid register spacing\n"); 16772d73315SDoug Ambrisko return (0); 16872d73315SDoug Ambrisko } 16972d73315SDoug Ambrisko } 17072d73315SDoug Ambrisko return (1); 17172d73315SDoug Ambrisko } 17272d73315SDoug Ambrisko 17372d73315SDoug Ambrisko static int 174d72a0786SJohn Baldwin ipmi_isa_attach(device_t dev) 175d72a0786SJohn Baldwin { 176d72a0786SJohn Baldwin struct ipmi_softc *sc = device_get_softc(dev); 177d72a0786SJohn Baldwin struct ipmi_get_info info; 178d72a0786SJohn Baldwin const char *mode; 179d72a0786SJohn Baldwin int count, error, i, type; 180d72a0786SJohn Baldwin 18172d73315SDoug Ambrisko /* 18272d73315SDoug Ambrisko * Pull info out of the SMBIOS table. If that doesn't work, use 18372d73315SDoug Ambrisko * hints to enumerate a device. 18472d73315SDoug Ambrisko */ 18572d73315SDoug Ambrisko if (!ipmi_smbios_identify(&info) && 18672d73315SDoug Ambrisko !ipmi_hint_identify(dev, &info)) 187d72a0786SJohn Baldwin return (ENXIO); 188d72a0786SJohn Baldwin 189d72a0786SJohn Baldwin switch (info.iface_type) { 190d72a0786SJohn Baldwin case KCS_MODE: 191d72a0786SJohn Baldwin count = 2; 192d72a0786SJohn Baldwin mode = "KCS"; 193d72a0786SJohn Baldwin break; 194d72a0786SJohn Baldwin case SMIC_MODE: 195d72a0786SJohn Baldwin count = 3; 196d72a0786SJohn Baldwin mode = "SMIC"; 197d72a0786SJohn Baldwin break; 198d72a0786SJohn Baldwin case BT_MODE: 199d72a0786SJohn Baldwin device_printf(dev, "BT mode is unsupported\n"); 200d72a0786SJohn Baldwin return (ENXIO); 201d72a0786SJohn Baldwin default: 202d72a0786SJohn Baldwin return (ENXIO); 203d72a0786SJohn Baldwin } 204d72a0786SJohn Baldwin error = 0; 205d72a0786SJohn Baldwin sc->ipmi_dev = dev; 206d72a0786SJohn Baldwin 207d72a0786SJohn Baldwin device_printf(dev, "%s mode found at %s 0x%jx alignment 0x%x on %s\n", 208d72a0786SJohn Baldwin mode, info.io_mode ? "io" : "mem", 209d72a0786SJohn Baldwin (uintmax_t)info.address, info.offset, 210d72a0786SJohn Baldwin device_get_name(device_get_parent(dev))); 211d72a0786SJohn Baldwin if (info.io_mode) 212d72a0786SJohn Baldwin type = SYS_RES_IOPORT; 213d72a0786SJohn Baldwin else 214d72a0786SJohn Baldwin type = SYS_RES_MEMORY; 215d72a0786SJohn Baldwin 216d72a0786SJohn Baldwin sc->ipmi_io_type = type; 217d72a0786SJohn Baldwin sc->ipmi_io_spacing = info.offset; 218d72a0786SJohn Baldwin if (info.offset == 1) { 219d72a0786SJohn Baldwin sc->ipmi_io_rid = 0; 220d72a0786SJohn Baldwin sc->ipmi_io_res[0] = bus_alloc_resource(dev, type, 221d72a0786SJohn Baldwin &sc->ipmi_io_rid, info.address, info.address + count - 1, 222d72a0786SJohn Baldwin count, RF_ACTIVE); 223d72a0786SJohn Baldwin if (sc->ipmi_io_res[0] == NULL) { 224d72a0786SJohn Baldwin device_printf(dev, "couldn't configure I/O resource\n"); 225d72a0786SJohn Baldwin return (ENXIO); 226d72a0786SJohn Baldwin } 227d72a0786SJohn Baldwin } else { 228d72a0786SJohn Baldwin for (i = 0; i < count; i++) { 229d72a0786SJohn Baldwin sc->ipmi_io_rid = i; 230d72a0786SJohn Baldwin sc->ipmi_io_res[i] = bus_alloc_resource(dev, type, 231d72a0786SJohn Baldwin &sc->ipmi_io_rid, info.address + i * info.offset, 232d72a0786SJohn Baldwin info.address + i * info.offset, 1, RF_ACTIVE); 233d72a0786SJohn Baldwin if (sc->ipmi_io_res[i] == NULL) { 234d72a0786SJohn Baldwin device_printf(dev, 235d72a0786SJohn Baldwin "couldn't configure I/O resource\n"); 236d72a0786SJohn Baldwin error = ENXIO; 237d72a0786SJohn Baldwin sc->ipmi_io_rid = 0; 238d72a0786SJohn Baldwin goto bad; 239d72a0786SJohn Baldwin } 240d72a0786SJohn Baldwin } 241d72a0786SJohn Baldwin sc->ipmi_io_rid = 0; 242d72a0786SJohn Baldwin } 243d72a0786SJohn Baldwin 244d72a0786SJohn Baldwin if (info.irq != 0) { 245d72a0786SJohn Baldwin sc->ipmi_irq_rid = 0; 246d72a0786SJohn Baldwin sc->ipmi_irq_res = bus_alloc_resource(dev, SYS_RES_IRQ, 247d72a0786SJohn Baldwin &sc->ipmi_irq_rid, info.irq, info.irq, 1, 248d72a0786SJohn Baldwin RF_SHAREABLE | RF_ACTIVE); 249d72a0786SJohn Baldwin } 250d72a0786SJohn Baldwin 251d72a0786SJohn Baldwin switch (info.iface_type) { 252d72a0786SJohn Baldwin case KCS_MODE: 253d72a0786SJohn Baldwin error = ipmi_kcs_attach(sc); 254d72a0786SJohn Baldwin if (error) 255d72a0786SJohn Baldwin goto bad; 256d72a0786SJohn Baldwin break; 257d72a0786SJohn Baldwin case SMIC_MODE: 258d72a0786SJohn Baldwin error = ipmi_smic_attach(sc); 259d72a0786SJohn Baldwin if (error) 260d72a0786SJohn Baldwin goto bad; 261d72a0786SJohn Baldwin break; 262d72a0786SJohn Baldwin } 263d72a0786SJohn Baldwin 264d72a0786SJohn Baldwin error = ipmi_attach(dev); 265d72a0786SJohn Baldwin if (error) 266d72a0786SJohn Baldwin goto bad; 267d72a0786SJohn Baldwin 268d72a0786SJohn Baldwin return (0); 269d72a0786SJohn Baldwin bad: 270d72a0786SJohn Baldwin ipmi_release_resources(dev); 271d72a0786SJohn Baldwin return (error); 272d72a0786SJohn Baldwin } 273d72a0786SJohn Baldwin 274d72a0786SJohn Baldwin static device_method_t ipmi_methods[] = { 275d72a0786SJohn Baldwin /* Device interface */ 276d72a0786SJohn Baldwin DEVMETHOD(device_identify, ipmi_isa_identify), 277d72a0786SJohn Baldwin DEVMETHOD(device_probe, ipmi_isa_probe), 278d72a0786SJohn Baldwin DEVMETHOD(device_attach, ipmi_isa_attach), 279d72a0786SJohn Baldwin DEVMETHOD(device_detach, ipmi_detach), 280d72a0786SJohn Baldwin { 0, 0 } 281d72a0786SJohn Baldwin }; 282d72a0786SJohn Baldwin 283d72a0786SJohn Baldwin static driver_t ipmi_isa_driver = { 284d72a0786SJohn Baldwin "ipmi", 285d72a0786SJohn Baldwin ipmi_methods, 286d72a0786SJohn Baldwin sizeof(struct ipmi_softc), 287d72a0786SJohn Baldwin }; 288d72a0786SJohn Baldwin 289d72a0786SJohn Baldwin DRIVER_MODULE(ipmi_isa, isa, ipmi_isa_driver, ipmi_devclass, 0, 0); 290*ee8b757aSYinlong Lu #ifdef ARCH_MAY_USE_EFI 291*ee8b757aSYinlong Lu MODULE_DEPEND(ipmi_isa, efirt, 1, 1, 1); 292*ee8b757aSYinlong Lu #endif 293