1d72a0786SJohn Baldwin /*- 24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 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/param.h> 30d72a0786SJohn Baldwin #include <sys/systm.h> 31d72a0786SJohn Baldwin #include <sys/bus.h> 32d72a0786SJohn Baldwin #include <sys/condvar.h> 33d72a0786SJohn Baldwin #include <sys/eventhandler.h> 34d72a0786SJohn Baldwin #include <sys/kernel.h> 35d72a0786SJohn Baldwin #include <sys/module.h> 36d72a0786SJohn Baldwin #include <sys/rman.h> 37d72a0786SJohn Baldwin #include <sys/selinfo.h> 38ee8b757aSYinlong Lu #include <sys/efi.h> 39d72a0786SJohn Baldwin 40d72a0786SJohn Baldwin #include <machine/pci_cfgreg.h> 41d72a0786SJohn Baldwin #include <dev/pci/pcireg.h> 42d72a0786SJohn Baldwin 43d72a0786SJohn Baldwin #include <isa/isavar.h> 44d72a0786SJohn Baldwin 45d72a0786SJohn Baldwin #ifdef LOCAL_MODULE 46d72a0786SJohn Baldwin #include <ipmi.h> 47d72a0786SJohn Baldwin #include <ipmivars.h> 48d72a0786SJohn Baldwin #else 49d72a0786SJohn Baldwin #include <sys/ipmi.h> 50d72a0786SJohn Baldwin #include <dev/ipmi/ipmivars.h> 51d72a0786SJohn Baldwin #endif 52d72a0786SJohn Baldwin 53d72a0786SJohn Baldwin static void 54d72a0786SJohn Baldwin ipmi_isa_identify(driver_t *driver, device_t parent) 55d72a0786SJohn Baldwin { 56d72a0786SJohn Baldwin struct ipmi_get_info info; 57d72a0786SJohn Baldwin uint32_t devid; 58d72a0786SJohn Baldwin 599b256674SSean Bruno if (ipmi_smbios_identify(&info) && info.iface_type != SSIF_MODE && 609b256674SSean Bruno device_find_child(parent, "ipmi", -1) == NULL) { 61d72a0786SJohn Baldwin /* 62d72a0786SJohn Baldwin * XXX: Hack alert. On some broken systems, the IPMI 63d72a0786SJohn Baldwin * interface is described via SMBIOS, but the actual 64d72a0786SJohn Baldwin * IO resource is in a PCI device BAR, so we have to let 65d72a0786SJohn Baldwin * the PCI device attach ipmi instead. In that case don't 66d72a0786SJohn Baldwin * create an isa ipmi device. For now we hardcode the list 67d72a0786SJohn Baldwin * of bus, device, function tuples. 68d72a0786SJohn Baldwin */ 691587a9dbSJohn Baldwin devid = pci_cfgregread(0, 0, 4, 2, PCIR_DEVVENDOR, 4); 70d72a0786SJohn Baldwin if (devid != 0xffffffff && 71d72a0786SJohn Baldwin ipmi_pci_match(devid & 0xffff, devid >> 16) != NULL) 72d72a0786SJohn Baldwin return; 73*a05a6804SWarner Losh BUS_ADD_CHILD(parent, 0, "ipmi", DEVICE_UNIT_ANY); 74d72a0786SJohn Baldwin } 75d72a0786SJohn Baldwin } 76d72a0786SJohn Baldwin 77d72a0786SJohn Baldwin static int 78d72a0786SJohn Baldwin ipmi_isa_probe(device_t dev) 79d72a0786SJohn Baldwin { 80d72a0786SJohn Baldwin 81ef3103bbSSean Bruno /* 82ef3103bbSSean Bruno * Give other drivers precedence. Unfortunately, this doesn't 83ef3103bbSSean Bruno * work if we have an SMBIOS table that duplicates a PCI device 84ef3103bbSSean Bruno * that's later on the bus than the PCI-ISA bridge. 85ef3103bbSSean Bruno */ 86ef3103bbSSean Bruno if (ipmi_attached) 87ef3103bbSSean Bruno return (ENXIO); 88ef3103bbSSean Bruno 89d72a0786SJohn Baldwin /* Skip any PNP devices. */ 90d72a0786SJohn Baldwin if (isa_get_logicalid(dev) != 0) 91d72a0786SJohn Baldwin return (ENXIO); 92d72a0786SJohn Baldwin 93d72a0786SJohn Baldwin device_set_desc(dev, "IPMI System Interface"); 94d72a0786SJohn Baldwin return (BUS_PROBE_DEFAULT); 95d72a0786SJohn Baldwin } 96d72a0786SJohn Baldwin 97d72a0786SJohn Baldwin static int 9872d73315SDoug Ambrisko ipmi_hint_identify(device_t dev, struct ipmi_get_info *info) 9972d73315SDoug Ambrisko { 10072d73315SDoug Ambrisko const char *mode, *name; 10172d73315SDoug Ambrisko int i, unit, val; 10272d73315SDoug Ambrisko 10372d73315SDoug Ambrisko /* We require at least a "mode" hint. */ 10472d73315SDoug Ambrisko name = device_get_name(dev); 10572d73315SDoug Ambrisko unit = device_get_unit(dev); 10672d73315SDoug Ambrisko if (resource_string_value(name, unit, "mode", &mode) != 0) 10772d73315SDoug Ambrisko return (0); 10872d73315SDoug Ambrisko 10972d73315SDoug Ambrisko /* Set the mode and default I/O resources for each mode. */ 11072d73315SDoug Ambrisko bzero(info, sizeof(struct ipmi_get_info)); 11172d73315SDoug Ambrisko if (strcasecmp(mode, "KCS") == 0) { 11272d73315SDoug Ambrisko info->iface_type = KCS_MODE; 11372d73315SDoug Ambrisko info->address = 0xca2; 11472d73315SDoug Ambrisko info->io_mode = 1; 11572d73315SDoug Ambrisko info->offset = 1; 11672d73315SDoug Ambrisko } else if (strcasecmp(mode, "SMIC") == 0) { 11772d73315SDoug Ambrisko info->iface_type = SMIC_MODE; 11872d73315SDoug Ambrisko info->address = 0xca9; 11972d73315SDoug Ambrisko info->io_mode = 1; 12072d73315SDoug Ambrisko info->offset = 1; 12172d73315SDoug Ambrisko } else if (strcasecmp(mode, "BT") == 0) { 12272d73315SDoug Ambrisko info->iface_type = BT_MODE; 12372d73315SDoug Ambrisko info->address = 0xe4; 12472d73315SDoug Ambrisko info->io_mode = 1; 12572d73315SDoug Ambrisko info->offset = 1; 12672d73315SDoug Ambrisko } else { 12772d73315SDoug Ambrisko device_printf(dev, "Invalid mode %s\n", mode); 12872d73315SDoug Ambrisko return (0); 12972d73315SDoug Ambrisko } 13072d73315SDoug Ambrisko 13172d73315SDoug Ambrisko /* 13272d73315SDoug Ambrisko * Kill any resources that isahint.c might have setup for us 13372d73315SDoug Ambrisko * since it will conflict with how we do resources. 13472d73315SDoug Ambrisko */ 13572d73315SDoug Ambrisko for (i = 0; i < 2; i++) { 13672d73315SDoug Ambrisko bus_delete_resource(dev, SYS_RES_MEMORY, i); 13772d73315SDoug Ambrisko bus_delete_resource(dev, SYS_RES_IOPORT, i); 13872d73315SDoug Ambrisko } 13972d73315SDoug Ambrisko 14005e1ac3cSGordon Bergling /* Allow the I/O address to be overridden via hints. */ 14172d73315SDoug Ambrisko if (resource_int_value(name, unit, "port", &val) == 0 && val != 0) { 14272d73315SDoug Ambrisko info->address = val; 14372d73315SDoug Ambrisko info->io_mode = 1; 14472d73315SDoug Ambrisko } else if (resource_int_value(name, unit, "maddr", &val) == 0 && 14572d73315SDoug Ambrisko val != 0) { 14672d73315SDoug Ambrisko info->address = val; 14772d73315SDoug Ambrisko info->io_mode = 0; 14872d73315SDoug Ambrisko } 14972d73315SDoug Ambrisko 15005e1ac3cSGordon Bergling /* Allow the spacing to be overridden. */ 15172d73315SDoug Ambrisko if (resource_int_value(name, unit, "spacing", &val) == 0) { 15272d73315SDoug Ambrisko switch (val) { 15372d73315SDoug Ambrisko case 8: 15472d73315SDoug Ambrisko info->offset = 1; 15572d73315SDoug Ambrisko break; 15672d73315SDoug Ambrisko case 16: 15772d73315SDoug Ambrisko info->offset = 2; 15872d73315SDoug Ambrisko break; 15972d73315SDoug Ambrisko case 32: 16072d73315SDoug Ambrisko info->offset = 4; 16172d73315SDoug Ambrisko break; 16272d73315SDoug Ambrisko default: 16372d73315SDoug Ambrisko device_printf(dev, "Invalid register spacing\n"); 16472d73315SDoug Ambrisko return (0); 16572d73315SDoug Ambrisko } 16672d73315SDoug Ambrisko } 16772d73315SDoug Ambrisko return (1); 16872d73315SDoug Ambrisko } 16972d73315SDoug Ambrisko 17072d73315SDoug Ambrisko static int 171d72a0786SJohn Baldwin ipmi_isa_attach(device_t dev) 172d72a0786SJohn Baldwin { 173d72a0786SJohn Baldwin struct ipmi_softc *sc = device_get_softc(dev); 174d72a0786SJohn Baldwin struct ipmi_get_info info; 175d72a0786SJohn Baldwin const char *mode; 176d72a0786SJohn Baldwin int count, error, i, type; 177d72a0786SJohn Baldwin 17872d73315SDoug Ambrisko /* 17972d73315SDoug Ambrisko * Pull info out of the SMBIOS table. If that doesn't work, use 18072d73315SDoug Ambrisko * hints to enumerate a device. 18172d73315SDoug Ambrisko */ 18272d73315SDoug Ambrisko if (!ipmi_smbios_identify(&info) && 18372d73315SDoug Ambrisko !ipmi_hint_identify(dev, &info)) 184d72a0786SJohn Baldwin return (ENXIO); 185d72a0786SJohn Baldwin 186d72a0786SJohn Baldwin switch (info.iface_type) { 187d72a0786SJohn Baldwin case KCS_MODE: 1881f166509SAndrey V. Elsukov count = IPMI_IF_KCS_NRES; 189d72a0786SJohn Baldwin mode = "KCS"; 190d72a0786SJohn Baldwin break; 191d72a0786SJohn Baldwin case SMIC_MODE: 1921f166509SAndrey V. Elsukov count = IPMI_IF_SMIC_NRES; 193d72a0786SJohn Baldwin mode = "SMIC"; 194d72a0786SJohn Baldwin break; 195d72a0786SJohn Baldwin case BT_MODE: 1961f166509SAndrey V. Elsukov count = IPMI_IF_BT_NRES; 1971f166509SAndrey V. Elsukov mode = "BT"; 1981f166509SAndrey V. Elsukov break; 199d72a0786SJohn Baldwin default: 200d72a0786SJohn Baldwin return (ENXIO); 201d72a0786SJohn Baldwin } 202d72a0786SJohn Baldwin error = 0; 203d72a0786SJohn Baldwin sc->ipmi_dev = dev; 204d72a0786SJohn Baldwin 205d72a0786SJohn Baldwin device_printf(dev, "%s mode found at %s 0x%jx alignment 0x%x on %s\n", 206d72a0786SJohn Baldwin mode, info.io_mode ? "io" : "mem", 207d72a0786SJohn Baldwin (uintmax_t)info.address, info.offset, 208d72a0786SJohn Baldwin device_get_name(device_get_parent(dev))); 209d72a0786SJohn Baldwin if (info.io_mode) 210d72a0786SJohn Baldwin type = SYS_RES_IOPORT; 211d72a0786SJohn Baldwin else 212d72a0786SJohn Baldwin type = SYS_RES_MEMORY; 213d72a0786SJohn Baldwin 214d72a0786SJohn Baldwin sc->ipmi_io_type = type; 215d72a0786SJohn Baldwin sc->ipmi_io_spacing = info.offset; 216d72a0786SJohn Baldwin if (info.offset == 1) { 217d72a0786SJohn Baldwin sc->ipmi_io_rid = 0; 218d72a0786SJohn Baldwin sc->ipmi_io_res[0] = bus_alloc_resource(dev, type, 219d72a0786SJohn Baldwin &sc->ipmi_io_rid, info.address, info.address + count - 1, 220d72a0786SJohn Baldwin count, RF_ACTIVE); 221d72a0786SJohn Baldwin if (sc->ipmi_io_res[0] == NULL) { 222d72a0786SJohn Baldwin device_printf(dev, "couldn't configure I/O resource\n"); 223d72a0786SJohn Baldwin return (ENXIO); 224d72a0786SJohn Baldwin } 225d72a0786SJohn Baldwin } else { 226d72a0786SJohn Baldwin for (i = 0; i < count; i++) { 227d72a0786SJohn Baldwin sc->ipmi_io_rid = i; 228d72a0786SJohn Baldwin sc->ipmi_io_res[i] = bus_alloc_resource(dev, type, 229d72a0786SJohn Baldwin &sc->ipmi_io_rid, info.address + i * info.offset, 230d72a0786SJohn Baldwin info.address + i * info.offset, 1, RF_ACTIVE); 231d72a0786SJohn Baldwin if (sc->ipmi_io_res[i] == NULL) { 232d72a0786SJohn Baldwin device_printf(dev, 233d72a0786SJohn Baldwin "couldn't configure I/O resource\n"); 234d72a0786SJohn Baldwin error = ENXIO; 235d72a0786SJohn Baldwin sc->ipmi_io_rid = 0; 236d72a0786SJohn Baldwin goto bad; 237d72a0786SJohn Baldwin } 238d72a0786SJohn Baldwin } 239d72a0786SJohn Baldwin sc->ipmi_io_rid = 0; 240d72a0786SJohn Baldwin } 241d72a0786SJohn Baldwin 242d72a0786SJohn Baldwin if (info.irq != 0) { 243d72a0786SJohn Baldwin sc->ipmi_irq_rid = 0; 244d72a0786SJohn Baldwin sc->ipmi_irq_res = bus_alloc_resource(dev, SYS_RES_IRQ, 245d72a0786SJohn Baldwin &sc->ipmi_irq_rid, info.irq, info.irq, 1, 246d72a0786SJohn Baldwin RF_SHAREABLE | RF_ACTIVE); 247d72a0786SJohn Baldwin } 248d72a0786SJohn Baldwin 2491f166509SAndrey V. Elsukov error = ENXIO; 250d72a0786SJohn Baldwin switch (info.iface_type) { 251d72a0786SJohn Baldwin case KCS_MODE: 252d72a0786SJohn Baldwin error = ipmi_kcs_attach(sc); 253d72a0786SJohn Baldwin break; 254d72a0786SJohn Baldwin case SMIC_MODE: 255d72a0786SJohn Baldwin error = ipmi_smic_attach(sc); 2561f166509SAndrey V. Elsukov break; 2571f166509SAndrey V. Elsukov case BT_MODE: 2581f166509SAndrey V. Elsukov error = ipmi_bt_attach(sc); 259d72a0786SJohn Baldwin break; 260d72a0786SJohn Baldwin } 261d72a0786SJohn Baldwin 2621f166509SAndrey V. Elsukov if (error) 2631f166509SAndrey V. Elsukov goto bad; 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 289fd773e2bSJohn Baldwin DRIVER_MODULE(ipmi_isa, isa, ipmi_isa_driver, 0, 0); 290ee8b757aSYinlong Lu #ifdef ARCH_MAY_USE_EFI 291ee8b757aSYinlong Lu MODULE_DEPEND(ipmi_isa, efirt, 1, 1, 1); 292ee8b757aSYinlong Lu #endif 293