137b1ce13SDoug Ambrisko /*- 237b1ce13SDoug Ambrisko * Copyright (c) 2006 IronPort Systems Inc. <ambrisko@ironport.com> 337b1ce13SDoug Ambrisko * All rights reserved. 437b1ce13SDoug Ambrisko * 537b1ce13SDoug Ambrisko * Redistribution and use in source and binary forms, with or without 637b1ce13SDoug Ambrisko * modification, are permitted provided that the following conditions 737b1ce13SDoug Ambrisko * are met: 837b1ce13SDoug Ambrisko * 1. Redistributions of source code must retain the above copyright 937b1ce13SDoug Ambrisko * notice, this list of conditions and the following disclaimer. 1037b1ce13SDoug Ambrisko * 2. Redistributions in binary form must reproduce the above copyright 1137b1ce13SDoug Ambrisko * notice, this list of conditions and the following disclaimer in the 1237b1ce13SDoug Ambrisko * documentation and/or other materials provided with the distribution. 1337b1ce13SDoug Ambrisko * 1437b1ce13SDoug Ambrisko * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1537b1ce13SDoug Ambrisko * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1637b1ce13SDoug Ambrisko * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1737b1ce13SDoug Ambrisko * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1837b1ce13SDoug Ambrisko * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1937b1ce13SDoug Ambrisko * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2037b1ce13SDoug Ambrisko * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2137b1ce13SDoug Ambrisko * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2237b1ce13SDoug Ambrisko * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2337b1ce13SDoug Ambrisko * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2437b1ce13SDoug Ambrisko * SUCH DAMAGE. 2537b1ce13SDoug Ambrisko */ 2637b1ce13SDoug Ambrisko 2737b1ce13SDoug Ambrisko #include <sys/cdefs.h> 2837b1ce13SDoug Ambrisko __FBSDID("$FreeBSD$"); 2937b1ce13SDoug Ambrisko 3037b1ce13SDoug Ambrisko #include <sys/param.h> 3137b1ce13SDoug Ambrisko #include <sys/systm.h> 32d72a0786SJohn Baldwin #include <sys/bus.h> 33d72a0786SJohn Baldwin #include <sys/condvar.h> 34d72a0786SJohn Baldwin #include <sys/eventhandler.h> 3537b1ce13SDoug Ambrisko #include <sys/kernel.h> 3637b1ce13SDoug Ambrisko #include <sys/module.h> 3737b1ce13SDoug Ambrisko #include <sys/rman.h> 38d72a0786SJohn Baldwin #include <sys/selinfo.h> 3937b1ce13SDoug Ambrisko 4037b1ce13SDoug Ambrisko #include <dev/pci/pcireg.h> 4137b1ce13SDoug Ambrisko #include <dev/pci/pcivar.h> 4237b1ce13SDoug Ambrisko 4337b1ce13SDoug Ambrisko #ifdef LOCAL_MODULE 4437b1ce13SDoug Ambrisko #include <ipmivars.h> 4537b1ce13SDoug Ambrisko #else 4637b1ce13SDoug Ambrisko #include <dev/ipmi/ipmivars.h> 4737b1ce13SDoug Ambrisko #endif 4837b1ce13SDoug Ambrisko 4937b1ce13SDoug Ambrisko static int ipmi_pci_probe(device_t dev); 5037b1ce13SDoug Ambrisko static int ipmi_pci_attach(device_t dev); 5137b1ce13SDoug Ambrisko 52d72a0786SJohn Baldwin static struct ipmi_ident 5337b1ce13SDoug Ambrisko { 5437b1ce13SDoug Ambrisko u_int16_t vendor; 5537b1ce13SDoug Ambrisko u_int16_t device; 5637b1ce13SDoug Ambrisko char *desc; 5737b1ce13SDoug Ambrisko } ipmi_identifiers[] = { 5837b1ce13SDoug Ambrisko {0x1028, 0x000d, "Dell PE2650 SMIC interface"}, 5937b1ce13SDoug Ambrisko {0, 0, 0} 6037b1ce13SDoug Ambrisko }; 6137b1ce13SDoug Ambrisko 62d72a0786SJohn Baldwin const char * 63d72a0786SJohn Baldwin ipmi_pci_match(uint16_t vendor, uint16_t device) 64d72a0786SJohn Baldwin { 6537b1ce13SDoug Ambrisko struct ipmi_ident *m; 6637b1ce13SDoug Ambrisko 67d72a0786SJohn Baldwin for (m = ipmi_identifiers; m->vendor != 0; m++) 68d72a0786SJohn Baldwin if (m->vendor == vendor && m->device == device) 69d72a0786SJohn Baldwin return (m->desc); 70d72a0786SJohn Baldwin return (NULL); 7137b1ce13SDoug Ambrisko } 7237b1ce13SDoug Ambrisko 7337b1ce13SDoug Ambrisko static int 74d72a0786SJohn Baldwin ipmi_pci_probe(device_t dev) 75d72a0786SJohn Baldwin { 76d72a0786SJohn Baldwin const char *desc; 77d72a0786SJohn Baldwin 78d72a0786SJohn Baldwin if (ipmi_attached) 79d72a0786SJohn Baldwin return (ENXIO); 80d72a0786SJohn Baldwin 81d72a0786SJohn Baldwin desc = ipmi_pci_match(pci_get_vendor(dev), pci_get_device(dev)); 82d72a0786SJohn Baldwin if (desc != NULL) { 83d72a0786SJohn Baldwin device_set_desc(dev, desc); 84d72a0786SJohn Baldwin return (BUS_PROBE_DEFAULT); 85d72a0786SJohn Baldwin } 86d72a0786SJohn Baldwin 87d72a0786SJohn Baldwin return (ENXIO); 88d72a0786SJohn Baldwin } 89d72a0786SJohn Baldwin 90d72a0786SJohn Baldwin static int 91d72a0786SJohn Baldwin ipmi_pci_attach(device_t dev) 92d72a0786SJohn Baldwin { 9337b1ce13SDoug Ambrisko struct ipmi_softc *sc = device_get_softc(dev); 94d72a0786SJohn Baldwin struct ipmi_get_info info; 95d72a0786SJohn Baldwin const char *mode; 96d72a0786SJohn Baldwin int error, type; 9737b1ce13SDoug Ambrisko 98d72a0786SJohn Baldwin /* Look for an IPMI entry in the SMBIOS table. */ 99d72a0786SJohn Baldwin if (!ipmi_smbios_identify(&info)) 100d72a0786SJohn Baldwin return (ENXIO); 10137b1ce13SDoug Ambrisko 10237b1ce13SDoug Ambrisko sc->ipmi_dev = dev; 10337b1ce13SDoug Ambrisko 104d72a0786SJohn Baldwin switch (info.iface_type) { 105d72a0786SJohn Baldwin case KCS_MODE: 106d72a0786SJohn Baldwin mode = "KCS"; 107d72a0786SJohn Baldwin break; 108d72a0786SJohn Baldwin case SMIC_MODE: 109d72a0786SJohn Baldwin mode = "SMIC"; 110d72a0786SJohn Baldwin break; 111d72a0786SJohn Baldwin case BT_MODE: 112d72a0786SJohn Baldwin device_printf(dev, "BT mode is unsupported\n"); 113d72a0786SJohn Baldwin return (ENXIO); 114d72a0786SJohn Baldwin default: 11537b1ce13SDoug Ambrisko device_printf(dev, "No IPMI interface found\n"); 116d72a0786SJohn Baldwin return (ENXIO); 11737b1ce13SDoug Ambrisko } 118d72a0786SJohn Baldwin 119d72a0786SJohn Baldwin device_printf(dev, "%s mode found at %s 0x%jx alignment 0x%x on %s\n", 120d72a0786SJohn Baldwin mode, info.io_mode ? "io" : "mem", 121d72a0786SJohn Baldwin (uintmax_t)info.address, info.offset, 122d72a0786SJohn Baldwin device_get_name(device_get_parent(dev))); 123d72a0786SJohn Baldwin if (info.io_mode) 124d72a0786SJohn Baldwin type = SYS_RES_IOPORT; 125d72a0786SJohn Baldwin else 126d72a0786SJohn Baldwin type = SYS_RES_MEMORY; 127d72a0786SJohn Baldwin 128d72a0786SJohn Baldwin sc->ipmi_io_rid = PCIR_BAR(0); 129d72a0786SJohn Baldwin sc->ipmi_io_res[0] = bus_alloc_resource_any(dev, type, 130d72a0786SJohn Baldwin &sc->ipmi_io_rid, RF_ACTIVE); 131d72a0786SJohn Baldwin sc->ipmi_io_type = type; 132d72a0786SJohn Baldwin sc->ipmi_io_spacing = info.offset; 133d72a0786SJohn Baldwin 134d72a0786SJohn Baldwin if (sc->ipmi_io_res[0] == NULL) { 135d72a0786SJohn Baldwin device_printf(dev, "couldn't configure pci io res\n"); 136d72a0786SJohn Baldwin return (ENXIO); 137d72a0786SJohn Baldwin } 13837b1ce13SDoug Ambrisko 13937b1ce13SDoug Ambrisko sc->ipmi_irq_rid = 0; 140d72a0786SJohn Baldwin sc->ipmi_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, 14137b1ce13SDoug Ambrisko &sc->ipmi_irq_rid, RF_SHAREABLE | RF_ACTIVE); 14237b1ce13SDoug Ambrisko 143d72a0786SJohn Baldwin switch (info.iface_type) { 144d72a0786SJohn Baldwin case KCS_MODE: 145d72a0786SJohn Baldwin error = ipmi_kcs_attach(sc); 146d72a0786SJohn Baldwin if (error) 147d72a0786SJohn Baldwin goto bad; 148d72a0786SJohn Baldwin break; 149d72a0786SJohn Baldwin case SMIC_MODE: 150d72a0786SJohn Baldwin error = ipmi_smic_attach(sc); 151d72a0786SJohn Baldwin if (error) 152d72a0786SJohn Baldwin goto bad; 153d72a0786SJohn Baldwin break; 154d72a0786SJohn Baldwin } 155d72a0786SJohn Baldwin error = ipmi_attach(dev); 156d72a0786SJohn Baldwin if (error) 157d72a0786SJohn Baldwin goto bad; 158d72a0786SJohn Baldwin 159d72a0786SJohn Baldwin return (0); 16037b1ce13SDoug Ambrisko bad: 161d72a0786SJohn Baldwin ipmi_release_resources(dev); 162d72a0786SJohn Baldwin return (error); 16337b1ce13SDoug Ambrisko } 16437b1ce13SDoug Ambrisko 165d72a0786SJohn Baldwin static device_method_t ipmi_methods[] = { 166d72a0786SJohn Baldwin /* Device interface */ 167d72a0786SJohn Baldwin DEVMETHOD(device_probe, ipmi_pci_probe), 168d72a0786SJohn Baldwin DEVMETHOD(device_attach, ipmi_pci_attach), 169d72a0786SJohn Baldwin DEVMETHOD(device_detach, ipmi_detach), 170d72a0786SJohn Baldwin { 0, 0 } 171d72a0786SJohn Baldwin }; 17237b1ce13SDoug Ambrisko 17337b1ce13SDoug Ambrisko static driver_t ipmi_pci_driver = { 17437b1ce13SDoug Ambrisko "ipmi", 17537b1ce13SDoug Ambrisko ipmi_methods, 17637b1ce13SDoug Ambrisko sizeof(struct ipmi_softc) 17737b1ce13SDoug Ambrisko }; 17837b1ce13SDoug Ambrisko 179d72a0786SJohn Baldwin DRIVER_MODULE(ipmi_pci, pci, ipmi_pci_driver, ipmi_devclass, 0, 0); 180d72a0786SJohn Baldwin 181d72a0786SJohn Baldwin /* Native IPMI on PCI driver. */ 182d72a0786SJohn Baldwin 183d72a0786SJohn Baldwin static int 184d72a0786SJohn Baldwin ipmi2_pci_probe(device_t dev) 185d72a0786SJohn Baldwin { 186d72a0786SJohn Baldwin 187d72a0786SJohn Baldwin if (pci_get_class(dev) == PCIC_SERIALBUS && 188d72a0786SJohn Baldwin pci_get_subclass(dev) == 0x07) { 189d72a0786SJohn Baldwin device_set_desc(dev, "IPMI System Interface"); 190d72a0786SJohn Baldwin return (BUS_PROBE_GENERIC); 191d72a0786SJohn Baldwin } 192d72a0786SJohn Baldwin 193d72a0786SJohn Baldwin return (ENXIO); 194d72a0786SJohn Baldwin } 195d72a0786SJohn Baldwin 196d72a0786SJohn Baldwin static int 197d72a0786SJohn Baldwin ipmi2_pci_attach(device_t dev) 198d72a0786SJohn Baldwin { 199d72a0786SJohn Baldwin struct ipmi_softc *sc; 200d72a0786SJohn Baldwin int error, iface, type; 201d72a0786SJohn Baldwin 202d72a0786SJohn Baldwin sc = device_get_softc(dev); 203d72a0786SJohn Baldwin sc->ipmi_dev = dev; 204d72a0786SJohn Baldwin 205d72a0786SJohn Baldwin /* Interface is determined by progif. */ 206d72a0786SJohn Baldwin switch (pci_get_progif(dev)) { 207d72a0786SJohn Baldwin case 0: 208d72a0786SJohn Baldwin iface = SMIC_MODE; 209d72a0786SJohn Baldwin break; 210d72a0786SJohn Baldwin case 1: 211d72a0786SJohn Baldwin iface = KCS_MODE; 212d72a0786SJohn Baldwin break; 213d72a0786SJohn Baldwin case 2: 214d72a0786SJohn Baldwin iface = BT_MODE; 215d72a0786SJohn Baldwin device_printf(dev, "BT interface unsupported\n"); 216d72a0786SJohn Baldwin return (ENXIO); 217d72a0786SJohn Baldwin default: 218d72a0786SJohn Baldwin device_printf(dev, "Unsupported interface: %d\n", 219d72a0786SJohn Baldwin pci_get_progif(dev)); 220d72a0786SJohn Baldwin return (ENXIO); 221d72a0786SJohn Baldwin } 222d72a0786SJohn Baldwin 223d72a0786SJohn Baldwin /* 224d72a0786SJohn Baldwin * Bottom bit of bar indicates resouce type. There should be 225d72a0786SJohn Baldwin * constants in pcireg.h for fields in a BAR. 226d72a0786SJohn Baldwin */ 227d72a0786SJohn Baldwin sc->ipmi_io_rid = PCIR_BAR(0); 228d72a0786SJohn Baldwin if (pci_read_config(dev, PCIR_BAR(0), 4) & 0x1) 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