137b1ce13SDoug Ambrisko /*- 2718cf2ccSPedro F. Giffuni * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3718cf2ccSPedro F. Giffuni * 437b1ce13SDoug Ambrisko * Copyright (c) 2006 IronPort Systems Inc. <ambrisko@ironport.com> 537b1ce13SDoug Ambrisko * All rights reserved. 637b1ce13SDoug Ambrisko * 737b1ce13SDoug Ambrisko * Redistribution and use in source and binary forms, with or without 837b1ce13SDoug Ambrisko * modification, are permitted provided that the following conditions 937b1ce13SDoug Ambrisko * are met: 1037b1ce13SDoug Ambrisko * 1. Redistributions of source code must retain the above copyright 1137b1ce13SDoug Ambrisko * notice, this list of conditions and the following disclaimer. 1237b1ce13SDoug Ambrisko * 2. Redistributions in binary form must reproduce the above copyright 1337b1ce13SDoug Ambrisko * notice, this list of conditions and the following disclaimer in the 1437b1ce13SDoug Ambrisko * documentation and/or other materials provided with the distribution. 1537b1ce13SDoug Ambrisko * 1637b1ce13SDoug Ambrisko * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1737b1ce13SDoug Ambrisko * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1837b1ce13SDoug Ambrisko * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1937b1ce13SDoug Ambrisko * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2037b1ce13SDoug Ambrisko * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2137b1ce13SDoug Ambrisko * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2237b1ce13SDoug Ambrisko * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2337b1ce13SDoug Ambrisko * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2437b1ce13SDoug Ambrisko * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2537b1ce13SDoug Ambrisko * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2637b1ce13SDoug Ambrisko * SUCH DAMAGE. 2737b1ce13SDoug Ambrisko */ 2837b1ce13SDoug Ambrisko 2937b1ce13SDoug Ambrisko #include <sys/cdefs.h> 3037b1ce13SDoug Ambrisko __FBSDID("$FreeBSD$"); 3137b1ce13SDoug Ambrisko 3237b1ce13SDoug Ambrisko #include <sys/param.h> 3337b1ce13SDoug Ambrisko #include <sys/systm.h> 34d72a0786SJohn Baldwin #include <sys/bus.h> 35d72a0786SJohn Baldwin #include <sys/condvar.h> 36d72a0786SJohn Baldwin #include <sys/eventhandler.h> 3737b1ce13SDoug Ambrisko #include <sys/kernel.h> 3837b1ce13SDoug Ambrisko #include <sys/module.h> 3937b1ce13SDoug Ambrisko #include <sys/rman.h> 40d72a0786SJohn Baldwin #include <sys/selinfo.h> 41*ee8b757aSYinlong Lu #include <sys/efi.h> 4237b1ce13SDoug Ambrisko 4337b1ce13SDoug Ambrisko #include <dev/pci/pcireg.h> 4437b1ce13SDoug Ambrisko #include <dev/pci/pcivar.h> 4537b1ce13SDoug Ambrisko 4637b1ce13SDoug Ambrisko #ifdef LOCAL_MODULE 4737b1ce13SDoug Ambrisko #include <ipmivars.h> 4837b1ce13SDoug Ambrisko #else 4937b1ce13SDoug Ambrisko #include <dev/ipmi/ipmivars.h> 5037b1ce13SDoug Ambrisko #endif 5137b1ce13SDoug Ambrisko 5237b1ce13SDoug Ambrisko static int ipmi_pci_probe(device_t dev); 5337b1ce13SDoug Ambrisko static int ipmi_pci_attach(device_t dev); 5437b1ce13SDoug Ambrisko 55d72a0786SJohn Baldwin static struct ipmi_ident 5637b1ce13SDoug Ambrisko { 5737b1ce13SDoug Ambrisko u_int16_t vendor; 5837b1ce13SDoug Ambrisko u_int16_t device; 5937b1ce13SDoug Ambrisko char *desc; 6037b1ce13SDoug Ambrisko } ipmi_identifiers[] = { 6137b1ce13SDoug Ambrisko {0x1028, 0x000d, "Dell PE2650 SMIC interface"}, 6237b1ce13SDoug Ambrisko {0, 0, 0} 6337b1ce13SDoug Ambrisko }; 6437b1ce13SDoug Ambrisko 65d72a0786SJohn Baldwin const char * 66d72a0786SJohn Baldwin ipmi_pci_match(uint16_t vendor, uint16_t device) 67d72a0786SJohn Baldwin { 6837b1ce13SDoug Ambrisko struct ipmi_ident *m; 6937b1ce13SDoug Ambrisko 70d72a0786SJohn Baldwin for (m = ipmi_identifiers; m->vendor != 0; m++) 71d72a0786SJohn Baldwin if (m->vendor == vendor && m->device == device) 72d72a0786SJohn Baldwin return (m->desc); 73d72a0786SJohn Baldwin return (NULL); 7437b1ce13SDoug Ambrisko } 7537b1ce13SDoug Ambrisko 7637b1ce13SDoug Ambrisko static int 77d72a0786SJohn Baldwin ipmi_pci_probe(device_t dev) 78d72a0786SJohn Baldwin { 79d72a0786SJohn Baldwin const char *desc; 80d72a0786SJohn Baldwin 81d72a0786SJohn Baldwin if (ipmi_attached) 82d72a0786SJohn Baldwin return (ENXIO); 83d72a0786SJohn Baldwin 84d72a0786SJohn Baldwin desc = ipmi_pci_match(pci_get_vendor(dev), pci_get_device(dev)); 85d72a0786SJohn Baldwin if (desc != NULL) { 86d72a0786SJohn Baldwin device_set_desc(dev, desc); 87d72a0786SJohn Baldwin return (BUS_PROBE_DEFAULT); 88d72a0786SJohn Baldwin } 89d72a0786SJohn Baldwin 90d72a0786SJohn Baldwin return (ENXIO); 91d72a0786SJohn Baldwin } 92d72a0786SJohn Baldwin 93d72a0786SJohn Baldwin static int 94d72a0786SJohn Baldwin ipmi_pci_attach(device_t dev) 95d72a0786SJohn Baldwin { 9637b1ce13SDoug Ambrisko struct ipmi_softc *sc = device_get_softc(dev); 97d72a0786SJohn Baldwin struct ipmi_get_info info; 98d72a0786SJohn Baldwin const char *mode; 99d72a0786SJohn Baldwin int error, type; 10037b1ce13SDoug Ambrisko 101d72a0786SJohn Baldwin /* Look for an IPMI entry in the SMBIOS table. */ 102d72a0786SJohn Baldwin if (!ipmi_smbios_identify(&info)) 103d72a0786SJohn Baldwin return (ENXIO); 10437b1ce13SDoug Ambrisko 10537b1ce13SDoug Ambrisko sc->ipmi_dev = dev; 10637b1ce13SDoug Ambrisko 107d72a0786SJohn Baldwin switch (info.iface_type) { 108d72a0786SJohn Baldwin case KCS_MODE: 109d72a0786SJohn Baldwin mode = "KCS"; 110d72a0786SJohn Baldwin break; 111d72a0786SJohn Baldwin case SMIC_MODE: 112d72a0786SJohn Baldwin mode = "SMIC"; 113d72a0786SJohn Baldwin break; 114d72a0786SJohn Baldwin case BT_MODE: 115d72a0786SJohn Baldwin device_printf(dev, "BT mode is unsupported\n"); 116d72a0786SJohn Baldwin return (ENXIO); 117d72a0786SJohn Baldwin default: 11837b1ce13SDoug Ambrisko device_printf(dev, "No IPMI interface found\n"); 119d72a0786SJohn Baldwin return (ENXIO); 12037b1ce13SDoug Ambrisko } 121d72a0786SJohn Baldwin 122d72a0786SJohn Baldwin device_printf(dev, "%s mode found at %s 0x%jx alignment 0x%x on %s\n", 123d72a0786SJohn Baldwin mode, info.io_mode ? "io" : "mem", 124d72a0786SJohn Baldwin (uintmax_t)info.address, info.offset, 125d72a0786SJohn Baldwin device_get_name(device_get_parent(dev))); 126d72a0786SJohn Baldwin if (info.io_mode) 127d72a0786SJohn Baldwin type = SYS_RES_IOPORT; 128d72a0786SJohn Baldwin else 129d72a0786SJohn Baldwin type = SYS_RES_MEMORY; 130d72a0786SJohn Baldwin 131d72a0786SJohn Baldwin sc->ipmi_io_rid = PCIR_BAR(0); 132d72a0786SJohn Baldwin sc->ipmi_io_res[0] = bus_alloc_resource_any(dev, type, 133d72a0786SJohn Baldwin &sc->ipmi_io_rid, RF_ACTIVE); 134d72a0786SJohn Baldwin sc->ipmi_io_type = type; 135d72a0786SJohn Baldwin sc->ipmi_io_spacing = info.offset; 136d72a0786SJohn Baldwin 137d72a0786SJohn Baldwin if (sc->ipmi_io_res[0] == NULL) { 138d72a0786SJohn Baldwin device_printf(dev, "couldn't configure pci io res\n"); 139d72a0786SJohn Baldwin return (ENXIO); 140d72a0786SJohn Baldwin } 14137b1ce13SDoug Ambrisko 14237b1ce13SDoug Ambrisko sc->ipmi_irq_rid = 0; 143d72a0786SJohn Baldwin sc->ipmi_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, 14437b1ce13SDoug Ambrisko &sc->ipmi_irq_rid, RF_SHAREABLE | RF_ACTIVE); 14537b1ce13SDoug Ambrisko 146d72a0786SJohn Baldwin switch (info.iface_type) { 147d72a0786SJohn Baldwin case KCS_MODE: 148d72a0786SJohn Baldwin error = ipmi_kcs_attach(sc); 149d72a0786SJohn Baldwin if (error) 150d72a0786SJohn Baldwin goto bad; 151d72a0786SJohn Baldwin break; 152d72a0786SJohn Baldwin case SMIC_MODE: 153d72a0786SJohn Baldwin error = ipmi_smic_attach(sc); 154d72a0786SJohn Baldwin if (error) 155d72a0786SJohn Baldwin goto bad; 156d72a0786SJohn Baldwin break; 157d72a0786SJohn Baldwin } 158d72a0786SJohn Baldwin error = ipmi_attach(dev); 159d72a0786SJohn Baldwin if (error) 160d72a0786SJohn Baldwin goto bad; 161d72a0786SJohn Baldwin 162d72a0786SJohn Baldwin return (0); 16337b1ce13SDoug Ambrisko bad: 164d72a0786SJohn Baldwin ipmi_release_resources(dev); 165d72a0786SJohn Baldwin return (error); 16637b1ce13SDoug Ambrisko } 16737b1ce13SDoug Ambrisko 168d72a0786SJohn Baldwin static device_method_t ipmi_methods[] = { 169d72a0786SJohn Baldwin /* Device interface */ 170d72a0786SJohn Baldwin DEVMETHOD(device_probe, ipmi_pci_probe), 171d72a0786SJohn Baldwin DEVMETHOD(device_attach, ipmi_pci_attach), 172d72a0786SJohn Baldwin DEVMETHOD(device_detach, ipmi_detach), 173d72a0786SJohn Baldwin { 0, 0 } 174d72a0786SJohn Baldwin }; 17537b1ce13SDoug Ambrisko 17637b1ce13SDoug Ambrisko static driver_t ipmi_pci_driver = { 17737b1ce13SDoug Ambrisko "ipmi", 17837b1ce13SDoug Ambrisko ipmi_methods, 17937b1ce13SDoug Ambrisko sizeof(struct ipmi_softc) 18037b1ce13SDoug Ambrisko }; 18137b1ce13SDoug Ambrisko 182d72a0786SJohn Baldwin DRIVER_MODULE(ipmi_pci, pci, ipmi_pci_driver, ipmi_devclass, 0, 0); 183d72a0786SJohn Baldwin 184d72a0786SJohn Baldwin /* Native IPMI on PCI driver. */ 185d72a0786SJohn Baldwin 186d72a0786SJohn Baldwin static int 187d72a0786SJohn Baldwin ipmi2_pci_probe(device_t dev) 188d72a0786SJohn Baldwin { 189d72a0786SJohn Baldwin 190d72a0786SJohn Baldwin if (pci_get_class(dev) == PCIC_SERIALBUS && 191657d9f9fSJohn Baldwin pci_get_subclass(dev) == PCIS_SERIALBUS_IPMI) { 192d72a0786SJohn Baldwin device_set_desc(dev, "IPMI System Interface"); 193d72a0786SJohn Baldwin return (BUS_PROBE_GENERIC); 194d72a0786SJohn Baldwin } 195d72a0786SJohn Baldwin 196d72a0786SJohn Baldwin return (ENXIO); 197d72a0786SJohn Baldwin } 198d72a0786SJohn Baldwin 199d72a0786SJohn Baldwin static int 200d72a0786SJohn Baldwin ipmi2_pci_attach(device_t dev) 201d72a0786SJohn Baldwin { 202d72a0786SJohn Baldwin struct ipmi_softc *sc; 203d72a0786SJohn Baldwin int error, iface, type; 204d72a0786SJohn Baldwin 205d72a0786SJohn Baldwin sc = device_get_softc(dev); 206d72a0786SJohn Baldwin sc->ipmi_dev = dev; 207d72a0786SJohn Baldwin 208d72a0786SJohn Baldwin /* Interface is determined by progif. */ 209d72a0786SJohn Baldwin switch (pci_get_progif(dev)) { 210657d9f9fSJohn Baldwin case PCIP_SERIALBUS_IPMI_SMIC: 211d72a0786SJohn Baldwin iface = SMIC_MODE; 212d72a0786SJohn Baldwin break; 213657d9f9fSJohn Baldwin case PCIP_SERIALBUS_IPMI_KCS: 214d72a0786SJohn Baldwin iface = KCS_MODE; 215d72a0786SJohn Baldwin break; 216657d9f9fSJohn Baldwin case PCIP_SERIALBUS_IPMI_BT: 217d72a0786SJohn Baldwin iface = BT_MODE; 218d72a0786SJohn Baldwin device_printf(dev, "BT interface unsupported\n"); 219d72a0786SJohn Baldwin return (ENXIO); 220d72a0786SJohn Baldwin default: 221d72a0786SJohn Baldwin device_printf(dev, "Unsupported interface: %d\n", 222d72a0786SJohn Baldwin pci_get_progif(dev)); 223d72a0786SJohn Baldwin return (ENXIO); 224d72a0786SJohn Baldwin } 225d72a0786SJohn Baldwin 2264dc5078fSJohn Baldwin /* Check the BAR to determine our resource type. */ 227d72a0786SJohn Baldwin sc->ipmi_io_rid = PCIR_BAR(0); 2284dc5078fSJohn Baldwin if (PCI_BAR_IO(pci_read_config(dev, PCIR_BAR(0), 4))) 229d72a0786SJohn Baldwin type = SYS_RES_IOPORT; 230d72a0786SJohn Baldwin else 231d72a0786SJohn Baldwin type = SYS_RES_MEMORY; 232d72a0786SJohn Baldwin sc->ipmi_io_type = type; 233d72a0786SJohn Baldwin sc->ipmi_io_spacing = 1; 234d72a0786SJohn Baldwin sc->ipmi_io_res[0] = bus_alloc_resource_any(dev, type, 235d72a0786SJohn Baldwin &sc->ipmi_io_rid, RF_ACTIVE); 236d72a0786SJohn Baldwin if (sc->ipmi_io_res[0] == NULL) { 237d72a0786SJohn Baldwin device_printf(dev, "couldn't map ports/memory\n"); 238d72a0786SJohn Baldwin return (ENXIO); 239d72a0786SJohn Baldwin } 240d72a0786SJohn Baldwin 241d72a0786SJohn Baldwin sc->ipmi_irq_rid = 0; 242d72a0786SJohn Baldwin sc->ipmi_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, 243d72a0786SJohn Baldwin &sc->ipmi_irq_rid, RF_SHAREABLE | RF_ACTIVE); 244d72a0786SJohn Baldwin 245d72a0786SJohn Baldwin switch (iface) { 246d72a0786SJohn Baldwin case KCS_MODE: 247d72a0786SJohn Baldwin device_printf(dev, "using KSC interface\n"); 248d72a0786SJohn Baldwin 249d72a0786SJohn Baldwin /* 250d72a0786SJohn Baldwin * We have to examine the resource directly to determine the 251d72a0786SJohn Baldwin * alignment. 252d72a0786SJohn Baldwin */ 253d72a0786SJohn Baldwin if (!ipmi_kcs_probe_align(sc)) { 254d72a0786SJohn Baldwin device_printf(dev, "Unable to determine alignment\n"); 255d72a0786SJohn Baldwin error = ENXIO; 256d72a0786SJohn Baldwin goto bad; 257d72a0786SJohn Baldwin } 258d72a0786SJohn Baldwin 259d72a0786SJohn Baldwin error = ipmi_kcs_attach(sc); 260d72a0786SJohn Baldwin if (error) 261d72a0786SJohn Baldwin goto bad; 262d72a0786SJohn Baldwin break; 263d72a0786SJohn Baldwin case SMIC_MODE: 264d72a0786SJohn Baldwin device_printf(dev, "using SMIC interface\n"); 265d72a0786SJohn Baldwin 266d72a0786SJohn Baldwin error = ipmi_smic_attach(sc); 267d72a0786SJohn Baldwin if (error) 268d72a0786SJohn Baldwin goto bad; 269d72a0786SJohn Baldwin break; 270d72a0786SJohn Baldwin } 271d72a0786SJohn Baldwin error = ipmi_attach(dev); 272d72a0786SJohn Baldwin if (error) 273d72a0786SJohn Baldwin goto bad; 274d72a0786SJohn Baldwin 275d72a0786SJohn Baldwin return (0); 276d72a0786SJohn Baldwin bad: 277d72a0786SJohn Baldwin ipmi_release_resources(dev); 278d72a0786SJohn Baldwin return (error); 279d72a0786SJohn Baldwin } 280d72a0786SJohn Baldwin 281d72a0786SJohn Baldwin static device_method_t ipmi2_methods[] = { 282d72a0786SJohn Baldwin /* Device interface */ 283d72a0786SJohn Baldwin DEVMETHOD(device_probe, ipmi2_pci_probe), 284d72a0786SJohn Baldwin DEVMETHOD(device_attach, ipmi2_pci_attach), 285d72a0786SJohn Baldwin DEVMETHOD(device_detach, ipmi_detach), 286d72a0786SJohn Baldwin { 0, 0 } 287d72a0786SJohn Baldwin }; 288d72a0786SJohn Baldwin 289d72a0786SJohn Baldwin static driver_t ipmi2_pci_driver = { 290d72a0786SJohn Baldwin "ipmi", 291d72a0786SJohn Baldwin ipmi2_methods, 292d72a0786SJohn Baldwin sizeof(struct ipmi_softc) 293d72a0786SJohn Baldwin }; 294d72a0786SJohn Baldwin 295d72a0786SJohn Baldwin DRIVER_MODULE(ipmi2_pci, pci, ipmi2_pci_driver, ipmi_devclass, 0, 0); 296*ee8b757aSYinlong Lu #ifdef ARCH_MAY_USE_EFI 297*ee8b757aSYinlong Lu MODULE_DEPEND(ipmi2_pci, efirt, 1, 1, 1); 298*ee8b757aSYinlong Lu #endif 299