1155517Sambrisko/*- 2155517Sambrisko * Copyright (c) 2006 IronPort Systems Inc. <ambrisko@ironport.com> 3155517Sambrisko * All rights reserved. 4155517Sambrisko * 5155517Sambrisko * Redistribution and use in source and binary forms, with or without 6155517Sambrisko * modification, are permitted provided that the following conditions 7155517Sambrisko * are met: 8155517Sambrisko * 1. Redistributions of source code must retain the above copyright 9155517Sambrisko * notice, this list of conditions and the following disclaimer. 10155517Sambrisko * 2. Redistributions in binary form must reproduce the above copyright 11155517Sambrisko * notice, this list of conditions and the following disclaimer in the 12155517Sambrisko * documentation and/or other materials provided with the distribution. 13155517Sambrisko * 14155517Sambrisko * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15155517Sambrisko * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16155517Sambrisko * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17155517Sambrisko * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18155517Sambrisko * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19155517Sambrisko * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20155517Sambrisko * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21155517Sambrisko * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22155517Sambrisko * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23155517Sambrisko * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24155517Sambrisko * SUCH DAMAGE. 25155517Sambrisko */ 26155517Sambrisko 27155517Sambrisko#include <sys/cdefs.h> 28155517Sambrisko__FBSDID("$FreeBSD$"); 29155517Sambrisko 30155517Sambrisko#include <sys/param.h> 31155517Sambrisko#include <sys/systm.h> 32162562Sjhb#include <sys/bus.h> 33162562Sjhb#include <sys/condvar.h> 34162562Sjhb#include <sys/eventhandler.h> 35155517Sambrisko#include <sys/kernel.h> 36155517Sambrisko#include <sys/module.h> 37162562Sjhb#include <sys/rman.h> 38155517Sambrisko#include <sys/selinfo.h> 39155517Sambrisko 40155517Sambrisko#include <dev/pci/pcireg.h> 41155517Sambrisko#include <dev/pci/pcivar.h> 42155517Sambrisko 43155517Sambrisko#ifdef LOCAL_MODULE 44155517Sambrisko#include <ipmivars.h> 45155517Sambrisko#else 46155517Sambrisko#include <dev/ipmi/ipmivars.h> 47155517Sambrisko#endif 48155517Sambrisko 49155517Sambriskostatic int ipmi_pci_probe(device_t dev); 50155517Sambriskostatic int ipmi_pci_attach(device_t dev); 51155517Sambrisko 52162562Sjhbstatic struct ipmi_ident 53155517Sambrisko{ 54155517Sambrisko u_int16_t vendor; 55155517Sambrisko u_int16_t device; 56155517Sambrisko char *desc; 57155517Sambrisko} ipmi_identifiers[] = { 58155517Sambrisko {0x1028, 0x000d, "Dell PE2650 SMIC interface"}, 59155517Sambrisko {0, 0, 0} 60155517Sambrisko}; 61155517Sambrisko 62162562Sjhbconst char * 63162562Sjhbipmi_pci_match(uint16_t vendor, uint16_t device) 64162562Sjhb{ 65155517Sambrisko struct ipmi_ident *m; 66155517Sambrisko 67162562Sjhb for (m = ipmi_identifiers; m->vendor != 0; m++) 68162562Sjhb if (m->vendor == vendor && m->device == device) 69162562Sjhb return (m->desc); 70162562Sjhb return (NULL); 71162562Sjhb} 72162562Sjhb 73162562Sjhbstatic int 74162562Sjhbipmi_pci_probe(device_t dev) 75162562Sjhb{ 76162562Sjhb const char *desc; 77162562Sjhb 78155517Sambrisko if (ipmi_attached) 79162562Sjhb return (ENXIO); 80155517Sambrisko 81162562Sjhb desc = ipmi_pci_match(pci_get_vendor(dev), pci_get_device(dev)); 82162562Sjhb if (desc != NULL) { 83162562Sjhb device_set_desc(dev, desc); 84162562Sjhb return (BUS_PROBE_DEFAULT); 85155517Sambrisko } 86155517Sambrisko 87162562Sjhb return (ENXIO); 88155517Sambrisko} 89155517Sambrisko 90155517Sambriskostatic int 91162562Sjhbipmi_pci_attach(device_t dev) 92162562Sjhb{ 93155517Sambrisko struct ipmi_softc *sc = device_get_softc(dev); 94162562Sjhb struct ipmi_get_info info; 95162562Sjhb const char *mode; 96162562Sjhb int error, type; 97155517Sambrisko 98162562Sjhb /* Look for an IPMI entry in the SMBIOS table. */ 99162562Sjhb if (!ipmi_smbios_identify(&info)) 100162562Sjhb return (ENXIO); 101155517Sambrisko 102162562Sjhb sc->ipmi_dev = dev; 103162562Sjhb 104162562Sjhb switch (info.iface_type) { 105162562Sjhb case KCS_MODE: 106162562Sjhb mode = "KCS"; 107162562Sjhb break; 108162562Sjhb case SMIC_MODE: 109162562Sjhb mode = "SMIC"; 110162562Sjhb break; 111162562Sjhb case BT_MODE: 112162562Sjhb device_printf(dev, "BT mode is unsupported\n"); 113162562Sjhb return (ENXIO); 114162562Sjhb default: 115162562Sjhb device_printf(dev, "No IPMI interface found\n"); 116162562Sjhb return (ENXIO); 117155517Sambrisko } 118155517Sambrisko 119162562Sjhb device_printf(dev, "%s mode found at %s 0x%jx alignment 0x%x on %s\n", 120162562Sjhb mode, info.io_mode ? "io" : "mem", 121162562Sjhb (uintmax_t)info.address, info.offset, 122162562Sjhb device_get_name(device_get_parent(dev))); 123162562Sjhb if (info.io_mode) 124162562Sjhb type = SYS_RES_IOPORT; 125162562Sjhb else 126162562Sjhb type = SYS_RES_MEMORY; 127162562Sjhb 128162562Sjhb sc->ipmi_io_rid = PCIR_BAR(0); 129162562Sjhb sc->ipmi_io_res[0] = bus_alloc_resource_any(dev, type, 130162562Sjhb &sc->ipmi_io_rid, RF_ACTIVE); 131162562Sjhb sc->ipmi_io_type = type; 132162562Sjhb sc->ipmi_io_spacing = info.offset; 133162562Sjhb 134162562Sjhb if (sc->ipmi_io_res[0] == NULL) { 135162562Sjhb device_printf(dev, "couldn't configure pci io res\n"); 136162562Sjhb return (ENXIO); 137155517Sambrisko } 138162562Sjhb 139162562Sjhb sc->ipmi_irq_rid = 0; 140162562Sjhb sc->ipmi_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, 141162562Sjhb &sc->ipmi_irq_rid, RF_SHAREABLE | RF_ACTIVE); 142162562Sjhb 143162562Sjhb switch (info.iface_type) { 144162562Sjhb case KCS_MODE: 145162562Sjhb error = ipmi_kcs_attach(sc); 146162562Sjhb if (error) 147162562Sjhb goto bad; 148162562Sjhb break; 149162562Sjhb case SMIC_MODE: 150162562Sjhb error = ipmi_smic_attach(sc); 151162562Sjhb if (error) 152162562Sjhb goto bad; 153162562Sjhb break; 154155517Sambrisko } 155162562Sjhb error = ipmi_attach(dev); 156162562Sjhb if (error) 157155517Sambrisko goto bad; 158155517Sambrisko 159162562Sjhb return (0); 160162562Sjhbbad: 161162562Sjhb ipmi_release_resources(dev); 162162562Sjhb return (error); 163162562Sjhb} 164155517Sambrisko 165162562Sjhbstatic device_method_t ipmi_methods[] = { 166162562Sjhb /* Device interface */ 167162562Sjhb DEVMETHOD(device_probe, ipmi_pci_probe), 168162562Sjhb DEVMETHOD(device_attach, ipmi_pci_attach), 169162562Sjhb DEVMETHOD(device_detach, ipmi_detach), 170162562Sjhb { 0, 0 } 171162562Sjhb}; 172155517Sambrisko 173162562Sjhbstatic driver_t ipmi_pci_driver = { 174162562Sjhb "ipmi", 175162562Sjhb ipmi_methods, 176162562Sjhb sizeof(struct ipmi_softc) 177162562Sjhb}; 178155517Sambrisko 179162562SjhbDRIVER_MODULE(ipmi_pci, pci, ipmi_pci_driver, ipmi_devclass, 0, 0); 180155517Sambrisko 181162562Sjhb/* Native IPMI on PCI driver. */ 182155517Sambrisko 183162562Sjhbstatic int 184162562Sjhbipmi2_pci_probe(device_t dev) 185162562Sjhb{ 186155517Sambrisko 187162562Sjhb if (pci_get_class(dev) == PCIC_SERIALBUS && 188168157Sjhb pci_get_subclass(dev) == PCIS_SERIALBUS_IPMI) { 189162562Sjhb device_set_desc(dev, "IPMI System Interface"); 190162562Sjhb return (BUS_PROBE_GENERIC); 191162562Sjhb } 192155517Sambrisko 193162562Sjhb return (ENXIO); 194162562Sjhb} 195155517Sambrisko 196162562Sjhbstatic int 197162562Sjhbipmi2_pci_attach(device_t dev) 198162562Sjhb{ 199162562Sjhb struct ipmi_softc *sc; 200162562Sjhb int error, iface, type; 201162562Sjhb 202162562Sjhb sc = device_get_softc(dev); 203162562Sjhb sc->ipmi_dev = dev; 204162562Sjhb 205162562Sjhb /* Interface is determined by progif. */ 206162562Sjhb switch (pci_get_progif(dev)) { 207168157Sjhb case PCIP_SERIALBUS_IPMI_SMIC: 208162562Sjhb iface = SMIC_MODE; 209162562Sjhb break; 210168157Sjhb case PCIP_SERIALBUS_IPMI_KCS: 211162562Sjhb iface = KCS_MODE; 212162562Sjhb break; 213168157Sjhb case PCIP_SERIALBUS_IPMI_BT: 214162562Sjhb iface = BT_MODE; 215162562Sjhb device_printf(dev, "BT interface unsupported\n"); 216162562Sjhb return (ENXIO); 217162562Sjhb default: 218162562Sjhb device_printf(dev, "Unsupported interface: %d\n", 219162562Sjhb pci_get_progif(dev)); 220162562Sjhb return (ENXIO); 221155517Sambrisko } 222155517Sambrisko 223168162Sjhb /* Check the BAR to determine our resource type. */ 224162562Sjhb sc->ipmi_io_rid = PCIR_BAR(0); 225168162Sjhb if (PCI_BAR_IO(pci_read_config(dev, PCIR_BAR(0), 4))) 226162562Sjhb type = SYS_RES_IOPORT; 227162562Sjhb else 228162562Sjhb type = SYS_RES_MEMORY; 229162562Sjhb sc->ipmi_io_type = type; 230162562Sjhb sc->ipmi_io_spacing = 1; 231162562Sjhb sc->ipmi_io_res[0] = bus_alloc_resource_any(dev, type, 232162562Sjhb &sc->ipmi_io_rid, RF_ACTIVE); 233162562Sjhb if (sc->ipmi_io_res[0] == NULL) { 234162562Sjhb device_printf(dev, "couldn't map ports/memory\n"); 235162562Sjhb return (ENXIO); 236162562Sjhb } 237162562Sjhb 238155517Sambrisko sc->ipmi_irq_rid = 0; 239162562Sjhb sc->ipmi_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, 240155517Sambrisko &sc->ipmi_irq_rid, RF_SHAREABLE | RF_ACTIVE); 241155517Sambrisko 242162562Sjhb switch (iface) { 243162562Sjhb case KCS_MODE: 244162562Sjhb device_printf(dev, "using KSC interface\n"); 245155517Sambrisko 246162562Sjhb /* 247162562Sjhb * We have to examine the resource directly to determine the 248162562Sjhb * alignment. 249162562Sjhb */ 250162562Sjhb if (!ipmi_kcs_probe_align(sc)) { 251162562Sjhb device_printf(dev, "Unable to determine alignment\n"); 252162562Sjhb error = ENXIO; 253162562Sjhb goto bad; 254162562Sjhb } 255155517Sambrisko 256162562Sjhb error = ipmi_kcs_attach(sc); 257162562Sjhb if (error) 258162562Sjhb goto bad; 259162562Sjhb break; 260162562Sjhb case SMIC_MODE: 261162562Sjhb device_printf(dev, "using SMIC interface\n"); 262155517Sambrisko 263162562Sjhb error = ipmi_smic_attach(sc); 264162562Sjhb if (error) 265162562Sjhb goto bad; 266162562Sjhb break; 267162562Sjhb } 268162562Sjhb error = ipmi_attach(dev); 269162562Sjhb if (error) 270162562Sjhb goto bad; 271155517Sambrisko 272162562Sjhb return (0); 273162562Sjhbbad: 274162562Sjhb ipmi_release_resources(dev); 275162562Sjhb return (error); 276155517Sambrisko} 277155517Sambrisko 278162562Sjhbstatic device_method_t ipmi2_methods[] = { 279162562Sjhb /* Device interface */ 280162562Sjhb DEVMETHOD(device_probe, ipmi2_pci_probe), 281162562Sjhb DEVMETHOD(device_attach, ipmi2_pci_attach), 282162562Sjhb DEVMETHOD(device_detach, ipmi_detach), 283162562Sjhb { 0, 0 } 284155517Sambrisko}; 285155517Sambrisko 286162562Sjhbstatic driver_t ipmi2_pci_driver = { 287162562Sjhb "ipmi", 288162562Sjhb ipmi2_methods, 289162562Sjhb sizeof(struct ipmi_softc) 290162562Sjhb}; 291162562Sjhb 292162562SjhbDRIVER_MODULE(ipmi2_pci, pci, ipmi2_pci_driver, ipmi_devclass, 0, 0); 293