ipmi_pci.c revision 155517
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: head/sys/dev/ipmi/ipmi_pci.c 155517 2006-02-10 20:51:35Z ambrisko $"); 29155517Sambrisko 30155517Sambrisko#include <sys/param.h> 31155517Sambrisko#include <sys/malloc.h> 32155517Sambrisko#include <sys/systm.h> 33155517Sambrisko#include <sys/kernel.h> 34155517Sambrisko#include <sys/module.h> 35155517Sambrisko#include <sys/selinfo.h> 36155517Sambrisko 37155517Sambrisko#include <sys/bus.h> 38155517Sambrisko#include <sys/conf.h> 39155517Sambrisko 40155517Sambrisko#include <machine/bus.h> 41155517Sambrisko#include <machine/resource.h> 42155517Sambrisko#include <sys/rman.h> 43155517Sambrisko 44155517Sambrisko#include <dev/pci/pcireg.h> 45155517Sambrisko#include <dev/pci/pcivar.h> 46155517Sambrisko 47155517Sambrisko#ifdef LOCAL_MODULE 48155517Sambrisko#include <ipmivars.h> 49155517Sambrisko#else 50155517Sambrisko#include <dev/ipmi/ipmivars.h> 51155517Sambrisko#endif 52155517Sambrisko 53155517Sambriskostatic int ipmi_pci_probe(device_t dev); 54155517Sambriskostatic int ipmi_pci_attach(device_t dev); 55155517Sambriskostatic int ipmi_pci_detach(device_t dev); 56155517Sambrisko 57155517Sambriskostatic device_method_t ipmi_methods[] = { 58155517Sambrisko /* Device interface */ 59155517Sambrisko DEVMETHOD(device_probe, ipmi_pci_probe), 60155517Sambrisko DEVMETHOD(device_attach, ipmi_pci_attach), 61155517Sambrisko DEVMETHOD(device_detach, ipmi_pci_detach), 62155517Sambrisko { 0, 0 } 63155517Sambrisko}; 64155517Sambrisko 65155517Sambriskostruct ipmi_ident 66155517Sambrisko{ 67155517Sambrisko u_int16_t vendor; 68155517Sambrisko u_int16_t device; 69155517Sambrisko char *desc; 70155517Sambrisko} ipmi_identifiers[] = { 71155517Sambrisko {0x1028, 0x000d, "Dell PE2650 SMIC interface"}, 72155517Sambrisko {0, 0, 0} 73155517Sambrisko}; 74155517Sambrisko 75155517Sambriskostatic int 76155517Sambriskoipmi_pci_probe(device_t dev) { 77155517Sambrisko struct ipmi_ident *m; 78155517Sambrisko 79155517Sambrisko if (ipmi_attached) 80155517Sambrisko return ENXIO; 81155517Sambrisko 82155517Sambrisko for (m = ipmi_identifiers; m->vendor != 0; m++) { 83155517Sambrisko if ((m->vendor == pci_get_vendor(dev)) && 84155517Sambrisko (m->device == pci_get_device(dev))) { 85155517Sambrisko device_set_desc(dev, m->desc); 86155517Sambrisko return (BUS_PROBE_DEFAULT); 87155517Sambrisko } 88155517Sambrisko } 89155517Sambrisko 90155517Sambrisko return ENXIO; 91155517Sambrisko} 92155517Sambrisko 93155517Sambriskostatic int 94155517Sambriskoipmi_pci_attach(device_t dev) { 95155517Sambrisko struct ipmi_softc *sc = device_get_softc(dev); 96155517Sambrisko device_t parent, smbios_attach_dev = NULL; 97155517Sambrisko devclass_t dc; 98155517Sambrisko int status, flags; 99155517Sambrisko int error; 100155517Sambrisko 101155517Sambrisko 102155517Sambrisko /* 103155517Sambrisko * We need to attach to something that can address the BIOS/ 104155517Sambrisko * SMBIOS memory range. This is usually the isa bus however 105155517Sambrisko * during a static kernel boot the isa bus is not available 106155517Sambrisko * so we run up the tree to the nexus bus. A module load 107155517Sambrisko * will use the isa bus attachment. If neither work bail 108155517Sambrisko * since the SMBIOS defines stuff we need to know to attach to 109155517Sambrisko * this device. 110155517Sambrisko */ 111155517Sambrisko dc = devclass_find("isa"); 112155517Sambrisko if (dc != NULL) { 113155517Sambrisko smbios_attach_dev = devclass_get_device(dc, 0); 114155517Sambrisko } 115155517Sambrisko 116155517Sambrisko if (smbios_attach_dev == NULL) { 117155517Sambrisko smbios_attach_dev = dev; 118155517Sambrisko for (;;) { 119155517Sambrisko parent = device_get_parent(smbios_attach_dev); 120155517Sambrisko if (parent == NULL) 121155517Sambrisko break; 122155517Sambrisko if (strcmp(device_get_name(smbios_attach_dev), 123155517Sambrisko "nexus") == 0) 124155517Sambrisko break; 125155517Sambrisko smbios_attach_dev = parent; 126155517Sambrisko } 127155517Sambrisko } 128155517Sambrisko 129155517Sambrisko if (smbios_attach_dev == NULL) { 130155517Sambrisko device_printf(dev, "Couldn't find isa/nexus device\n"); 131155517Sambrisko goto bad; 132155517Sambrisko } 133155517Sambrisko sc->ipmi_smbios_dev = ipmi_smbios_identify(NULL, smbios_attach_dev); 134155517Sambrisko if (sc->ipmi_smbios_dev == NULL) { 135155517Sambrisko device_printf(dev, "Couldn't find isa device\n"); 136155517Sambrisko goto bad; 137155517Sambrisko } 138155517Sambrisko error = ipmi_smbios_probe(sc->ipmi_smbios_dev); 139155517Sambrisko if (error != 0) { 140155517Sambrisko goto bad; 141155517Sambrisko } 142155517Sambrisko sc->ipmi_dev = dev; 143155517Sambrisko error = ipmi_smbios_query(dev); 144155517Sambrisko device_delete_child(dev, sc->ipmi_smbios_dev); 145155517Sambrisko if (error != 0) 146155517Sambrisko goto bad; 147155517Sambrisko 148155517Sambrisko /* Now we know about the IPMI attachment info. */ 149155517Sambrisko if (sc->ipmi_bios_info.kcs_mode) { 150155517Sambrisko if (sc->ipmi_bios_info.io_mode) 151155517Sambrisko device_printf(dev, "KCS mode found at io 0x%llx " 152155517Sambrisko "alignment 0x%x on %s\n", 153155517Sambrisko (long long)sc->ipmi_bios_info.address, 154155517Sambrisko sc->ipmi_bios_info.offset, 155155517Sambrisko device_get_name(device_get_parent(sc->ipmi_dev))); 156155517Sambrisko else 157155517Sambrisko device_printf(dev, "KCS mode found at mem 0x%llx " 158155517Sambrisko "alignment 0x%x on %s\n", 159155517Sambrisko (long long)sc->ipmi_bios_info.address, 160155517Sambrisko sc->ipmi_bios_info.offset, 161155517Sambrisko device_get_name(device_get_parent(sc->ipmi_dev))); 162155517Sambrisko 163155517Sambrisko sc->ipmi_kcs_status_reg = sc->ipmi_bios_info.offset; 164155517Sambrisko sc->ipmi_kcs_command_reg = sc->ipmi_bios_info.offset; 165155517Sambrisko sc->ipmi_kcs_data_out_reg = 0; 166155517Sambrisko sc->ipmi_kcs_data_in_reg = 0; 167155517Sambrisko 168155517Sambrisko if (sc->ipmi_bios_info.io_mode) { 169155517Sambrisko sc->ipmi_io_rid = PCIR_BAR(0); 170155517Sambrisko sc->ipmi_io_res = bus_alloc_resource(dev, 171155517Sambrisko SYS_RES_IOPORT, &sc->ipmi_io_rid, 172155517Sambrisko sc->ipmi_bios_info.address, 173155517Sambrisko sc->ipmi_bios_info.address + 174155517Sambrisko (sc->ipmi_bios_info.offset * 2), 175155517Sambrisko sc->ipmi_bios_info.offset * 2, 176155517Sambrisko RF_ACTIVE); 177155517Sambrisko } else { 178155517Sambrisko sc->ipmi_mem_rid = PCIR_BAR(0); 179155517Sambrisko sc->ipmi_mem_res = bus_alloc_resource(dev, 180155517Sambrisko SYS_RES_MEMORY, &sc->ipmi_mem_rid, 181155517Sambrisko sc->ipmi_bios_info.address, 182155517Sambrisko sc->ipmi_bios_info.address + 183155517Sambrisko (sc->ipmi_bios_info.offset * 2), 184155517Sambrisko sc->ipmi_bios_info.offset * 2, 185155517Sambrisko RF_ACTIVE); 186155517Sambrisko } 187155517Sambrisko 188155517Sambrisko if (!sc->ipmi_io_res){ 189155517Sambrisko device_printf(dev, "couldn't configure pci io res\n"); 190155517Sambrisko goto bad; 191155517Sambrisko } 192155517Sambrisko 193155517Sambrisko status = INB(sc, sc->ipmi_kcs_status_reg); 194155517Sambrisko if (status == 0xff) { 195155517Sambrisko device_printf(dev, "couldn't find it\n"); 196155517Sambrisko goto bad; 197155517Sambrisko } 198155517Sambrisko if(status & KCS_STATUS_OBF){ 199155517Sambrisko ipmi_read(dev, NULL, 0); 200155517Sambrisko } 201155517Sambrisko } else if (sc->ipmi_bios_info.smic_mode) { 202155517Sambrisko if (sc->ipmi_bios_info.io_mode) 203155517Sambrisko device_printf(dev, "SMIC mode found at io 0x%llx " 204155517Sambrisko "alignment 0x%x on %s\n", 205155517Sambrisko (long long)sc->ipmi_bios_info.address, 206155517Sambrisko sc->ipmi_bios_info.offset, 207155517Sambrisko device_get_name(device_get_parent(sc->ipmi_dev))); 208155517Sambrisko else 209155517Sambrisko device_printf(dev, "SMIC mode found at mem 0x%llx " 210155517Sambrisko "alignment 0x%x on %s\n", 211155517Sambrisko (long long)sc->ipmi_bios_info.address, 212155517Sambrisko sc->ipmi_bios_info.offset, 213155517Sambrisko device_get_name(device_get_parent(sc->ipmi_dev))); 214155517Sambrisko 215155517Sambrisko sc->ipmi_smic_data = 0; 216155517Sambrisko sc->ipmi_smic_ctl_sts = sc->ipmi_bios_info.offset; 217155517Sambrisko sc->ipmi_smic_flags = sc->ipmi_bios_info.offset * 2; 218155517Sambrisko 219155517Sambrisko if (sc->ipmi_bios_info.io_mode) { 220155517Sambrisko sc->ipmi_io_rid = PCIR_BAR(0); 221155517Sambrisko sc->ipmi_io_res = bus_alloc_resource(dev, 222155517Sambrisko SYS_RES_IOPORT, &sc->ipmi_io_rid, 223155517Sambrisko sc->ipmi_bios_info.address, 224155517Sambrisko sc->ipmi_bios_info.address + 225155517Sambrisko (sc->ipmi_bios_info.offset * 3), 226155517Sambrisko sc->ipmi_bios_info.offset * 3, 227155517Sambrisko RF_ACTIVE); 228155517Sambrisko } else { 229155517Sambrisko sc->ipmi_mem_rid = PCIR_BAR(0); 230155517Sambrisko sc->ipmi_mem_res = bus_alloc_resource(dev, 231155517Sambrisko SYS_RES_MEMORY, &sc->ipmi_mem_rid, 232155517Sambrisko sc->ipmi_bios_info.address, 233155517Sambrisko sc->ipmi_bios_info.address + 234155517Sambrisko (sc->ipmi_bios_info.offset * 2), 235155517Sambrisko sc->ipmi_bios_info.offset * 2, 236155517Sambrisko RF_ACTIVE); 237155517Sambrisko } 238155517Sambrisko 239155517Sambrisko if (!sc->ipmi_io_res && !sc->ipmi_mem_res){ 240155517Sambrisko device_printf(dev, "couldn't configure pci res\n"); 241155517Sambrisko goto bad; 242155517Sambrisko } 243155517Sambrisko 244155517Sambrisko flags = INB(sc, sc->ipmi_smic_flags); 245155517Sambrisko if (flags == 0xff) { 246155517Sambrisko device_printf(dev, "couldn't find it\n"); 247155517Sambrisko goto bad; 248155517Sambrisko } 249155517Sambrisko if ((flags & SMIC_STATUS_SMS_ATN) 250155517Sambrisko && (flags & SMIC_STATUS_RX_RDY)){ 251155517Sambrisko ipmi_read(dev, NULL, 0); 252155517Sambrisko } 253155517Sambrisko } else { 254155517Sambrisko device_printf(dev, "No IPMI interface found\n"); 255155517Sambrisko goto bad; 256155517Sambrisko } 257155517Sambrisko ipmi_attach(dev); 258155517Sambrisko 259155517Sambrisko sc->ipmi_irq_rid = 0; 260155517Sambrisko sc->ipmi_irq_res = bus_alloc_resource_any(sc->ipmi_dev, SYS_RES_IRQ, 261155517Sambrisko &sc->ipmi_irq_rid, RF_SHAREABLE | RF_ACTIVE); 262155517Sambrisko if (sc->ipmi_irq_res == NULL) { 263155517Sambrisko device_printf(sc->ipmi_dev, "can't allocate interrupt\n"); 264155517Sambrisko } else { 265155517Sambrisko if (bus_setup_intr(sc->ipmi_dev, sc->ipmi_irq_res, 266155517Sambrisko INTR_TYPE_MISC, ipmi_intr, 267155517Sambrisko sc->ipmi_dev, &sc->ipmi_irq)) { 268155517Sambrisko device_printf(sc->ipmi_dev, 269155517Sambrisko "can't set up interrupt\n"); 270155517Sambrisko return (EINVAL); 271155517Sambrisko } 272155517Sambrisko } 273155517Sambrisko 274155517Sambrisko return 0; 275155517Sambriskobad: 276155517Sambrisko return ENXIO; 277155517Sambrisko} 278155517Sambrisko 279155517Sambriskostatic int ipmi_pci_detach(device_t dev) { 280155517Sambrisko struct ipmi_softc *sc; 281155517Sambrisko 282155517Sambrisko sc = device_get_softc(dev); 283155517Sambrisko ipmi_detach(dev); 284155517Sambrisko if (sc->ipmi_ev_tag) 285155517Sambrisko EVENTHANDLER_DEREGISTER(watchdog_list, sc->ipmi_ev_tag); 286155517Sambrisko 287155517Sambrisko if (sc->ipmi_mem_res) 288155517Sambrisko bus_release_resource(dev, SYS_RES_MEMORY, sc->ipmi_mem_rid, 289155517Sambrisko sc->ipmi_mem_res); 290155517Sambrisko if (sc->ipmi_io_res) 291155517Sambrisko bus_release_resource(dev, SYS_RES_IOPORT, sc->ipmi_io_rid, 292155517Sambrisko sc->ipmi_io_res); 293155517Sambrisko if (sc->ipmi_irq) 294155517Sambrisko bus_teardown_intr(sc->ipmi_dev, sc->ipmi_irq_res, 295155517Sambrisko sc->ipmi_irq); 296155517Sambrisko if (sc->ipmi_irq_res) 297155517Sambrisko bus_release_resource(sc->ipmi_dev, SYS_RES_IRQ, 298155517Sambrisko sc->ipmi_irq_rid, sc->ipmi_irq_res); 299155517Sambrisko 300155517Sambrisko return 0; 301155517Sambrisko} 302155517Sambrisko 303155517Sambriskostatic driver_t ipmi_pci_driver = { 304155517Sambrisko "ipmi", 305155517Sambrisko ipmi_methods, 306155517Sambrisko sizeof(struct ipmi_softc) 307155517Sambrisko}; 308155517Sambrisko 309155517SambriskoDRIVER_MODULE(ipmi_foo, pci, ipmi_pci_driver, ipmi_devclass, 0, 0); 310