Update the ipmi(4) driver:
- Split out the communication protocols into their own files and use a couple of function pointers in the softc that the commuication protocols setup in their own attach routine. - Add support for the SSIF interface (talking to IPMI over SMBus). - Add an ACPI attachment. - Add a PCI attachment that attaches to devices with the IPMI interface subclass. - Split the ISA attachment out into its own file: ipmi_isa.c. - Change the code to probe the SMBIOS table for an IPMI entry to just use pmap_mapbios() to map the table in rather than trying to setup a fake resource on an isa device and then activating the resource to map in the table. - Make bus attachments leaner by adding attach functions for each communication interface (ipmi_kcs_attach(), ipmi_smic_attach(), etc.) that setup per-interface data. - Formalize the model used by the driver to handle requests by adding an explicit struct ipmi_request object that holds the state of a given request and reply for the entire lifetime of the request. By bundling the request into an object, it is easier to add retry logic to the various communication backends (as well as eventually support BT mode which uses a slightly different message format than KCS, SMIC, and SSIF). - Add a per-softc lock and remove D_NEEDGIANT as the driver is now MPSAFE. - Add 32-bit compatibility ioctl shims so you can use a 32-bit ipmitool on FreeBSD/amd64. - Add ipmi(4) to i386 and amd64 NOTES. Submitted by: ambrisko (large portions of 2 and 3) Sponsored by: IronPort Systems, Yahoo! MFC after: 6 days
This commit is contained in:
parent
0a6f8a5050
commit
d72a078647
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=162562
@ -346,6 +346,7 @@ options SAFE_RNDTEST # enable rndtest support
|
||||
#
|
||||
# Miscellaneous hardware:
|
||||
#
|
||||
# ipmi: Intelligent Platform Management Interface
|
||||
# smbios: DMI/SMBIOS entry point
|
||||
# vpd: Vital Product Data kernel interface
|
||||
# cy: Cyclades serial driver
|
||||
@ -368,6 +369,7 @@ device digi_EPCX_PCI
|
||||
device digi_Xe
|
||||
device digi_Xem
|
||||
device digi_Xr
|
||||
device ipmi
|
||||
# Parallel (8255 PPI) basic I/O (mode 0) port (e.g. Advantech PCL-724)
|
||||
device pbio
|
||||
hint.pbio.0.at="isa"
|
||||
|
@ -161,7 +161,13 @@ dev/if_ndis/if_ndis_pci.c optional ndis cardbus | ndis pci
|
||||
dev/if_ndis/if_ndis_usb.c optional ndis usb
|
||||
dev/io/iodev.c optional io
|
||||
dev/ipmi/ipmi.c optional ipmi
|
||||
dev/ipmi/ipmi_smbios.c optional ipmi isa
|
||||
dev/ipmi/ipmi_acpi.c optional ipmi acpi
|
||||
dev/ipmi/ipmi_isa.c optional ipmi isa
|
||||
dev/ipmi/ipmi_kcs.c optional ipmi
|
||||
dev/ipmi/ipmi_smic.c optional ipmi
|
||||
dev/ipmi/ipmi_smbus.c optional ipmi smbus
|
||||
dev/ipmi/ipmi_smbios.c optional ipmi
|
||||
dev/ipmi/ipmi_ssif.c optional ipmi smbus
|
||||
dev/ipmi/ipmi_pci.c optional ipmi pci
|
||||
dev/fdc/fdc.c optional fdc
|
||||
dev/fdc/fdc_acpi.c optional fdc
|
||||
|
@ -196,7 +196,13 @@ dev/if_ndis/if_ndis_pci.c optional ndis cardbus | ndis pci
|
||||
dev/if_ndis/if_ndis_usb.c optional ndis usb
|
||||
dev/io/iodev.c optional io
|
||||
dev/ipmi/ipmi.c optional ipmi
|
||||
dev/ipmi/ipmi_smbios.c optional ipmi isa
|
||||
dev/ipmi/ipmi_acpi.c optional ipmi acpi
|
||||
dev/ipmi/ipmi_isa.c optional ipmi isa
|
||||
dev/ipmi/ipmi_kcs.c optional ipmi
|
||||
dev/ipmi/ipmi_smic.c optional ipmi
|
||||
dev/ipmi/ipmi_smbus.c optional ipmi smbus
|
||||
dev/ipmi/ipmi_smbios.c optional ipmi
|
||||
dev/ipmi/ipmi_ssif.c optional ipmi smbus
|
||||
dev/ipmi/ipmi_pci.c optional ipmi pci
|
||||
dev/kbd/kbd.c optional atkbd | sc | ukbd | vt
|
||||
dev/le/if_le_isa.c optional le isa
|
||||
|
1469
sys/dev/ipmi/ipmi.c
1469
sys/dev/ipmi/ipmi.c
File diff suppressed because it is too large
Load Diff
210
sys/dev/ipmi/ipmi_acpi.c
Normal file
210
sys/dev/ipmi/ipmi_acpi.c
Normal file
@ -0,0 +1,210 @@
|
||||
/*-
|
||||
* Copyright (c) 2006 IronPort Systems Inc. <ambrisko@ironport.com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/bus.h>
|
||||
#include <sys/condvar.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/module.h>
|
||||
#include <sys/rman.h>
|
||||
#include <sys/selinfo.h>
|
||||
|
||||
#include <contrib/dev/acpica/acpi.h>
|
||||
#include <dev/acpica/acpivar.h>
|
||||
|
||||
/* Hooks for the ACPI CA debugging infrastructure */
|
||||
#define _COMPONENT ACPI_BUTTON
|
||||
ACPI_MODULE_NAME("IPMI")
|
||||
|
||||
#ifdef LOCAL_MODULE
|
||||
#include <ipmi.h>
|
||||
#include <ipmivars.h>
|
||||
#else
|
||||
#include <sys/ipmi.h>
|
||||
#include <dev/ipmi/ipmivars.h>
|
||||
#endif
|
||||
|
||||
static int ipmi_acpi_probe(device_t);
|
||||
static int ipmi_acpi_attach(device_t);
|
||||
|
||||
int
|
||||
ipmi_acpi_probe(device_t dev)
|
||||
{
|
||||
static char *ipmi_ids[] = {"IPI0001", NULL};
|
||||
|
||||
if (ipmi_attached)
|
||||
return (EBUSY);
|
||||
|
||||
if (acpi_disabled("ipmi") ||
|
||||
ACPI_ID_PROBE(device_get_parent(dev), dev, ipmi_ids) == NULL)
|
||||
return (ENXIO);
|
||||
|
||||
device_set_desc(dev, "IPMI System Interface");
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
ipmi_acpi_attach(device_t dev)
|
||||
{
|
||||
ACPI_HANDLE devh;
|
||||
const char *mode;
|
||||
struct ipmi_get_info info;
|
||||
struct ipmi_softc *sc = device_get_softc(dev);
|
||||
int count, error, flags, i, type;
|
||||
int interface_type = 0, interface_version = 0;
|
||||
|
||||
error = 0;
|
||||
devh = acpi_get_handle(dev);
|
||||
if (ACPI_FAILURE(acpi_GetInteger(devh, "_IFT", &interface_type)))
|
||||
return (ENXIO);
|
||||
|
||||
if (ACPI_FAILURE(acpi_GetInteger(devh, "_SRV", &interface_version)))
|
||||
return (ENXIO);
|
||||
|
||||
switch (interface_type) {
|
||||
case KCS_MODE:
|
||||
count = 2;
|
||||
mode = "KCS";
|
||||
break;
|
||||
case SMIC_MODE:
|
||||
count = 3;
|
||||
mode = "SMIC";
|
||||
break;
|
||||
case BT_MODE:
|
||||
device_printf(dev, "BT interface not supported\n");
|
||||
return (ENXIO);
|
||||
case SSIF_MODE:
|
||||
if (ACPI_FAILURE(acpi_GetInteger(devh, "_ADR", &flags)))
|
||||
return (ENXIO);
|
||||
info.address = flags >> 1;
|
||||
device_printf(dev, "SSIF interface not supported on ACPI\n");
|
||||
return (0);
|
||||
default:
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
if (bus_get_resource(dev, SYS_RES_IOPORT, 0, NULL, NULL) == 0)
|
||||
type = SYS_RES_IOPORT;
|
||||
else if (bus_get_resource(dev, SYS_RES_MEMORY, 0, NULL, NULL) == 0)
|
||||
type = SYS_RES_MEMORY;
|
||||
else {
|
||||
device_printf(dev, "unknown resource type\n");
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
sc->ipmi_io_rid = 0;
|
||||
sc->ipmi_io_res[0] = bus_alloc_resource_any(dev, type,
|
||||
&sc->ipmi_io_rid, RF_ACTIVE);
|
||||
sc->ipmi_io_type = type;
|
||||
sc->ipmi_io_spacing = 1;
|
||||
if (sc->ipmi_io_res[0] == NULL) {
|
||||
device_printf(dev, "couldn't configure I/O resource\n");
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
/* If we have multiple resources, allocate up to MAX_RES. */
|
||||
for (i = 1; i < MAX_RES; i++) {
|
||||
sc->ipmi_io_rid = i;
|
||||
sc->ipmi_io_res[i] = bus_alloc_resource_any(dev, type,
|
||||
&sc->ipmi_io_rid, RF_ACTIVE);
|
||||
if (sc->ipmi_io_res[i] == NULL)
|
||||
break;
|
||||
}
|
||||
sc->ipmi_io_rid = 0;
|
||||
|
||||
/* If we have multiple resources, make sure we have enough of them. */
|
||||
if (sc->ipmi_io_res[1] != NULL && sc->ipmi_io_res[count - 1] == NULL) {
|
||||
device_printf(dev, "too few I/O resources\n");
|
||||
error = ENXIO;
|
||||
goto bad;
|
||||
}
|
||||
|
||||
device_printf(dev, "%s mode found at %s 0x%jx on %s\n",
|
||||
mode, type == SYS_RES_IOPORT ? "io" : "mem",
|
||||
(uintmax_t)rman_get_start(sc->ipmi_io_res[0]),
|
||||
device_get_name(device_get_parent(dev)));
|
||||
|
||||
sc->ipmi_dev = dev;
|
||||
|
||||
/*
|
||||
* Setup an interrupt if we have an interrupt resource. We
|
||||
* don't support GPE interrupts via _GPE yet.
|
||||
*/
|
||||
sc->ipmi_irq_rid = 0;
|
||||
sc->ipmi_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
|
||||
&sc->ipmi_irq_rid, RF_SHAREABLE | RF_ACTIVE);
|
||||
|
||||
/* Warn if _GPE exists. */
|
||||
if (ACPI_SUCCESS(AcpiEvaluateObject(devh, "_GPE", NULL, NULL)))
|
||||
device_printf(dev, "_GPE support not implemented\n");
|
||||
|
||||
/*
|
||||
* We assume an alignment of 1 byte as currently the IPMI spec
|
||||
* doesn't provide any way to determine the alignment via ACPI.
|
||||
*/
|
||||
switch (interface_type) {
|
||||
case KCS_MODE:
|
||||
error = ipmi_kcs_attach(sc);
|
||||
if (error)
|
||||
goto bad;
|
||||
break;
|
||||
case SMIC_MODE:
|
||||
error = ipmi_smic_attach(sc);
|
||||
if (error)
|
||||
goto bad;
|
||||
break;
|
||||
}
|
||||
error = ipmi_attach(dev);
|
||||
if (error)
|
||||
goto bad;
|
||||
|
||||
return (0);
|
||||
bad:
|
||||
ipmi_release_resources(dev);
|
||||
return (error);
|
||||
}
|
||||
|
||||
static device_method_t ipmi_methods[] = {
|
||||
/* Device interface */
|
||||
DEVMETHOD(device_probe, ipmi_acpi_probe),
|
||||
DEVMETHOD(device_attach, ipmi_acpi_attach),
|
||||
DEVMETHOD(device_detach, ipmi_detach),
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
static driver_t ipmi_acpi_driver = {
|
||||
"ipmi",
|
||||
ipmi_methods,
|
||||
sizeof(struct ipmi_softc),
|
||||
};
|
||||
|
||||
DRIVER_MODULE(ipmi_acpi, acpi, ipmi_acpi_driver, ipmi_devclass, 0, 0);
|
||||
MODULE_DEPEND(ipmi_acpi, acpi, 1, 1, 1);
|
209
sys/dev/ipmi/ipmi_isa.c
Normal file
209
sys/dev/ipmi/ipmi_isa.c
Normal file
@ -0,0 +1,209 @@
|
||||
/*-
|
||||
* Copyright (c) 2006 IronPort Systems Inc. <ambrisko@ironport.com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/bus.h>
|
||||
#include <sys/condvar.h>
|
||||
#include <sys/eventhandler.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/module.h>
|
||||
#include <sys/rman.h>
|
||||
#include <sys/selinfo.h>
|
||||
|
||||
#include <machine/pci_cfgreg.h>
|
||||
#include <dev/pci/pcireg.h>
|
||||
|
||||
#include <isa/isavar.h>
|
||||
|
||||
#ifdef LOCAL_MODULE
|
||||
#include <ipmi.h>
|
||||
#include <ipmivars.h>
|
||||
#else
|
||||
#include <sys/ipmi.h>
|
||||
#include <dev/ipmi/ipmivars.h>
|
||||
#endif
|
||||
|
||||
static void
|
||||
ipmi_isa_identify(driver_t *driver, device_t parent)
|
||||
{
|
||||
struct ipmi_get_info info;
|
||||
uint32_t devid;
|
||||
|
||||
if (ipmi_smbios_identify(&info) && info.iface_type != SSIF_MODE &&
|
||||
device_find_child(parent, "ipmi", -1) == NULL) {
|
||||
/*
|
||||
* XXX: Hack alert. On some broken systems, the IPMI
|
||||
* interface is described via SMBIOS, but the actual
|
||||
* IO resource is in a PCI device BAR, so we have to let
|
||||
* the PCI device attach ipmi instead. In that case don't
|
||||
* create an isa ipmi device. For now we hardcode the list
|
||||
* of bus, device, function tuples.
|
||||
*/
|
||||
devid = pci_cfgregread(0, 4, 2, PCIR_DEVVENDOR, 4);
|
||||
if (devid != 0xffffffff &&
|
||||
ipmi_pci_match(devid & 0xffff, devid >> 16) != NULL)
|
||||
return;
|
||||
BUS_ADD_CHILD(parent, 0, "ipmi", -1);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
ipmi_isa_probe(device_t dev)
|
||||
{
|
||||
|
||||
/* Skip any PNP devices. */
|
||||
if (isa_get_logicalid(dev) != 0)
|
||||
return (ENXIO);
|
||||
|
||||
device_set_desc(dev, "IPMI System Interface");
|
||||
return (BUS_PROBE_DEFAULT);
|
||||
}
|
||||
|
||||
static int
|
||||
ipmi_isa_attach(device_t dev)
|
||||
{
|
||||
struct ipmi_softc *sc = device_get_softc(dev);
|
||||
struct ipmi_get_info info;
|
||||
const char *mode;
|
||||
int count, error, i, type;
|
||||
|
||||
/* This should never fail. */
|
||||
if (!ipmi_smbios_identify(&info))
|
||||
return (ENXIO);
|
||||
|
||||
/*
|
||||
* Give other drivers precedence. Unfortunately, this doesn't
|
||||
* work if we have an SMBIOS table that duplicates a PCI device
|
||||
* that's later on the bus than the PCI-ISA bridge.
|
||||
*/
|
||||
if (ipmi_attached)
|
||||
return (EBUSY);
|
||||
|
||||
switch (info.iface_type) {
|
||||
case KCS_MODE:
|
||||
count = 2;
|
||||
mode = "KCS";
|
||||
break;
|
||||
case SMIC_MODE:
|
||||
count = 3;
|
||||
mode = "SMIC";
|
||||
break;
|
||||
case BT_MODE:
|
||||
device_printf(dev, "BT mode is unsupported\n");
|
||||
return (ENXIO);
|
||||
default:
|
||||
return (ENXIO);
|
||||
}
|
||||
error = 0;
|
||||
sc->ipmi_dev = dev;
|
||||
|
||||
device_printf(dev, "%s mode found at %s 0x%jx alignment 0x%x on %s\n",
|
||||
mode, info.io_mode ? "io" : "mem",
|
||||
(uintmax_t)info.address, info.offset,
|
||||
device_get_name(device_get_parent(dev)));
|
||||
if (info.io_mode)
|
||||
type = SYS_RES_IOPORT;
|
||||
else
|
||||
type = SYS_RES_MEMORY;
|
||||
|
||||
sc->ipmi_io_type = type;
|
||||
sc->ipmi_io_spacing = info.offset;
|
||||
if (info.offset == 1) {
|
||||
sc->ipmi_io_rid = 0;
|
||||
sc->ipmi_io_res[0] = bus_alloc_resource(dev, type,
|
||||
&sc->ipmi_io_rid, info.address, info.address + count - 1,
|
||||
count, RF_ACTIVE);
|
||||
if (sc->ipmi_io_res[0] == NULL) {
|
||||
device_printf(dev, "couldn't configure I/O resource\n");
|
||||
return (ENXIO);
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i < count; i++) {
|
||||
sc->ipmi_io_rid = i;
|
||||
sc->ipmi_io_res[i] = bus_alloc_resource(dev, type,
|
||||
&sc->ipmi_io_rid, info.address + i * info.offset,
|
||||
info.address + i * info.offset, 1, RF_ACTIVE);
|
||||
if (sc->ipmi_io_res[i] == NULL) {
|
||||
device_printf(dev,
|
||||
"couldn't configure I/O resource\n");
|
||||
error = ENXIO;
|
||||
sc->ipmi_io_rid = 0;
|
||||
goto bad;
|
||||
}
|
||||
}
|
||||
sc->ipmi_io_rid = 0;
|
||||
}
|
||||
|
||||
if (info.irq != 0) {
|
||||
sc->ipmi_irq_rid = 0;
|
||||
sc->ipmi_irq_res = bus_alloc_resource(dev, SYS_RES_IRQ,
|
||||
&sc->ipmi_irq_rid, info.irq, info.irq, 1,
|
||||
RF_SHAREABLE | RF_ACTIVE);
|
||||
}
|
||||
|
||||
switch (info.iface_type) {
|
||||
case KCS_MODE:
|
||||
error = ipmi_kcs_attach(sc);
|
||||
if (error)
|
||||
goto bad;
|
||||
break;
|
||||
case SMIC_MODE:
|
||||
error = ipmi_smic_attach(sc);
|
||||
if (error)
|
||||
goto bad;
|
||||
break;
|
||||
}
|
||||
|
||||
error = ipmi_attach(dev);
|
||||
if (error)
|
||||
goto bad;
|
||||
|
||||
return (0);
|
||||
bad:
|
||||
ipmi_release_resources(dev);
|
||||
return (error);
|
||||
}
|
||||
|
||||
static device_method_t ipmi_methods[] = {
|
||||
/* Device interface */
|
||||
DEVMETHOD(device_identify, ipmi_isa_identify),
|
||||
DEVMETHOD(device_probe, ipmi_isa_probe),
|
||||
DEVMETHOD(device_attach, ipmi_isa_attach),
|
||||
DEVMETHOD(device_detach, ipmi_detach),
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
static driver_t ipmi_isa_driver = {
|
||||
"ipmi",
|
||||
ipmi_methods,
|
||||
sizeof(struct ipmi_softc),
|
||||
};
|
||||
|
||||
DRIVER_MODULE(ipmi_isa, isa, ipmi_isa_driver, ipmi_devclass, 0, 0);
|
607
sys/dev/ipmi/ipmi_kcs.c
Normal file
607
sys/dev/ipmi/ipmi_kcs.c
Normal file
@ -0,0 +1,607 @@
|
||||
/*-
|
||||
* Copyright (c) 2006 IronPort Systems Inc. <ambrisko@ironport.com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/bus.h>
|
||||
#include <sys/condvar.h>
|
||||
#include <sys/eventhandler.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/kthread.h>
|
||||
#include <sys/rman.h>
|
||||
#include <sys/selinfo.h>
|
||||
#include <machine/bus.h>
|
||||
|
||||
#ifdef LOCAL_MODULE
|
||||
#include <ipmi.h>
|
||||
#include <ipmivars.h>
|
||||
#else
|
||||
#include <sys/ipmi.h>
|
||||
#include <dev/ipmi/ipmivars.h>
|
||||
#endif
|
||||
|
||||
static void kcs_clear_obf(struct ipmi_softc *, int);
|
||||
static void kcs_error(struct ipmi_softc *);
|
||||
static int kcs_wait_for_ibf(struct ipmi_softc *, int);
|
||||
static int kcs_wait_for_obf(struct ipmi_softc *, int);
|
||||
|
||||
static int
|
||||
kcs_wait_for_ibf(struct ipmi_softc *sc, int state)
|
||||
{
|
||||
int status, start = ticks;
|
||||
|
||||
status = INB(sc, KCS_CTL_STS);
|
||||
if (state == 0) {
|
||||
/* WAIT FOR IBF = 0 */
|
||||
while (ticks - start < MAX_TIMEOUT && status & KCS_STATUS_IBF) {
|
||||
DELAY(100);
|
||||
status = INB(sc, KCS_CTL_STS);
|
||||
}
|
||||
} else {
|
||||
/* WAIT FOR IBF = 1 */
|
||||
while (ticks - start < MAX_TIMEOUT &&
|
||||
!(status & KCS_STATUS_IBF)) {
|
||||
DELAY(100);
|
||||
status = INB(sc, KCS_CTL_STS);
|
||||
}
|
||||
}
|
||||
return (status);
|
||||
}
|
||||
|
||||
static int
|
||||
kcs_wait_for_obf(struct ipmi_softc *sc, int state)
|
||||
{
|
||||
int status, start = ticks;
|
||||
|
||||
status = INB(sc, KCS_CTL_STS);
|
||||
if (state == 0) {
|
||||
/* WAIT FOR OBF = 0 */
|
||||
while (ticks - start < MAX_TIMEOUT && status & KCS_STATUS_OBF) {
|
||||
DELAY(100);
|
||||
status = INB(sc, KCS_CTL_STS);
|
||||
}
|
||||
} else {
|
||||
/* WAIT FOR OBF = 1 */
|
||||
while (ticks - start < MAX_TIMEOUT &&
|
||||
!(status & KCS_STATUS_OBF)) {
|
||||
DELAY(100);
|
||||
status = INB(sc, KCS_CTL_STS);
|
||||
}
|
||||
}
|
||||
return (status);
|
||||
}
|
||||
|
||||
static void
|
||||
kcs_clear_obf(struct ipmi_softc *sc, int status)
|
||||
{
|
||||
int data;
|
||||
|
||||
/* Clear OBF */
|
||||
if (status & KCS_STATUS_OBF) {
|
||||
data = INB(sc, KCS_DATA);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
kcs_error(struct ipmi_softc *sc)
|
||||
{
|
||||
int retry, status;
|
||||
u_char data;
|
||||
|
||||
for (retry = 0; retry < 2; retry++) {
|
||||
|
||||
/* Wait for IBF = 0 */
|
||||
status = kcs_wait_for_ibf(sc, 0);
|
||||
|
||||
/* ABORT */
|
||||
OUTB(sc, KCS_CTL_STS, KCS_CONTROL_GET_STATUS_ABORT);
|
||||
|
||||
/* Wait for IBF = 0 */
|
||||
status = kcs_wait_for_ibf(sc, 0);
|
||||
|
||||
/* Clear OBF */
|
||||
kcs_clear_obf(sc, status);
|
||||
|
||||
if (status & KCS_STATUS_OBF) {
|
||||
data = INB(sc, KCS_DATA);
|
||||
if (data != 0)
|
||||
device_printf(sc->ipmi_dev,
|
||||
"KCS Error Data %02x\n", data);
|
||||
}
|
||||
|
||||
/* 0x00 to DATA_IN */
|
||||
OUTB(sc, KCS_DATA, 0x00);
|
||||
|
||||
/* Wait for IBF = 0 */
|
||||
status = kcs_wait_for_ibf(sc, 0);
|
||||
|
||||
if (KCS_STATUS_STATE(status) == KCS_STATUS_STATE_READ) {
|
||||
|
||||
/* Wait for OBF = 1 */
|
||||
status = kcs_wait_for_obf(sc, 1);
|
||||
|
||||
/* Read error status */
|
||||
data = INB(sc, KCS_DATA);
|
||||
if (data != 0)
|
||||
device_printf(sc->ipmi_dev, "KCS error: %02x\n",
|
||||
data);
|
||||
|
||||
/* Write READ into Data_in */
|
||||
OUTB(sc, KCS_DATA, KCS_DATA_IN_READ);
|
||||
|
||||
/* Wait for IBF = 0 */
|
||||
status = kcs_wait_for_ibf(sc, 0);
|
||||
}
|
||||
|
||||
/* IDLE STATE */
|
||||
if (KCS_STATUS_STATE(status) == KCS_STATUS_STATE_IDLE) {
|
||||
/* Wait for OBF = 1 */
|
||||
status = kcs_wait_for_obf(sc, 1);
|
||||
|
||||
/* Clear OBF */
|
||||
kcs_clear_obf(sc, status);
|
||||
return;
|
||||
}
|
||||
}
|
||||
device_printf(sc->ipmi_dev, "KCS Error retry exhausted\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Start to write a request. Waits for IBF to clear and then sends the
|
||||
* WR_START command.
|
||||
*/
|
||||
static int
|
||||
kcs_start_write(struct ipmi_softc *sc)
|
||||
{
|
||||
int retry, status;
|
||||
|
||||
for (retry = 0; retry < 10; retry++) {
|
||||
/* Wait for IBF = 0 */
|
||||
status = kcs_wait_for_ibf(sc, 0);
|
||||
|
||||
/* Clear OBF */
|
||||
kcs_clear_obf(sc, status);
|
||||
|
||||
/* Write start to command */
|
||||
OUTB(sc, KCS_CTL_STS, KCS_CONTROL_WRITE_START);
|
||||
|
||||
/* Wait for IBF = 0 */
|
||||
status = kcs_wait_for_ibf(sc, 0);
|
||||
if (KCS_STATUS_STATE(status) == KCS_STATUS_STATE_WRITE)
|
||||
break;
|
||||
DELAY(1000000);
|
||||
}
|
||||
|
||||
if (KCS_STATUS_STATE(status) != KCS_STATUS_STATE_WRITE)
|
||||
/* error state */
|
||||
return (0);
|
||||
|
||||
/* Clear OBF */
|
||||
kcs_clear_obf(sc, status);
|
||||
|
||||
return (1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Write a byte of the request message, excluding the last byte of the
|
||||
* message which requires special handling.
|
||||
*/
|
||||
static int
|
||||
kcs_write_byte(struct ipmi_softc *sc, u_char data)
|
||||
{
|
||||
int status;
|
||||
|
||||
/* Data to Data */
|
||||
OUTB(sc, KCS_DATA, data);
|
||||
|
||||
/* Wait for IBF = 0 */
|
||||
status = kcs_wait_for_ibf(sc, 0);
|
||||
|
||||
if (KCS_STATUS_STATE(status) != KCS_STATUS_STATE_WRITE)
|
||||
return (0);
|
||||
|
||||
/* Clear OBF */
|
||||
kcs_clear_obf(sc, status);
|
||||
return (1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Write the last byte of a request message.
|
||||
*/
|
||||
static int
|
||||
kcs_write_last_byte(struct ipmi_softc *sc, u_char data)
|
||||
{
|
||||
int status;
|
||||
|
||||
/* Write end to command */
|
||||
OUTB(sc, KCS_CTL_STS, KCS_CONTROL_WRITE_END);
|
||||
|
||||
/* Wait for IBF = 0 */
|
||||
status = kcs_wait_for_ibf(sc, 0);
|
||||
|
||||
if (KCS_STATUS_STATE(status) != KCS_STATUS_STATE_WRITE)
|
||||
/* error state */
|
||||
return (0);
|
||||
|
||||
/* Clear OBF */
|
||||
kcs_clear_obf(sc, status);
|
||||
|
||||
/* Send data byte to DATA. */
|
||||
OUTB(sc, KCS_DATA, data);
|
||||
return (1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read one byte of the reply message.
|
||||
*/
|
||||
static int
|
||||
kcs_read_byte(struct ipmi_softc *sc, u_char *data)
|
||||
{
|
||||
int status;
|
||||
u_char dummy;
|
||||
|
||||
/* Wait for IBF = 0 */
|
||||
status = kcs_wait_for_ibf(sc, 0);
|
||||
|
||||
/* Read State */
|
||||
if (KCS_STATUS_STATE(status) == KCS_STATUS_STATE_READ) {
|
||||
|
||||
/* Wait for OBF = 1 */
|
||||
status = kcs_wait_for_obf(sc, 1);
|
||||
|
||||
/* Read Data_out */
|
||||
*data = INB(sc, KCS_DATA);
|
||||
|
||||
/* Write READ into Data_in */
|
||||
OUTB(sc, KCS_DATA, KCS_DATA_IN_READ);
|
||||
return (1);
|
||||
}
|
||||
|
||||
/* Idle State */
|
||||
if (KCS_STATUS_STATE(status) == KCS_STATUS_STATE_IDLE) {
|
||||
|
||||
/* Wait for OBF = 1*/
|
||||
status = kcs_wait_for_obf(sc, 1);
|
||||
|
||||
/* Read Dummy */
|
||||
dummy = INB(sc, KCS_DATA);
|
||||
return (2);
|
||||
}
|
||||
|
||||
/* Error State */
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Send a request message and collect the reply. Returns true if we
|
||||
* succeed.
|
||||
*/
|
||||
static int
|
||||
kcs_polled_request(struct ipmi_softc *sc, struct ipmi_request *req)
|
||||
{
|
||||
u_char *cp, data;
|
||||
int i, state;
|
||||
|
||||
/* Send the request. */
|
||||
if (!kcs_start_write(sc)) {
|
||||
device_printf(sc->ipmi_dev, "KCS: Failed to start write\n");
|
||||
goto fail;
|
||||
}
|
||||
#ifdef KCS_DEBUG
|
||||
device_printf(sc->ipmi_dev, "KCS: WRITE_START... ok\n");
|
||||
#endif
|
||||
|
||||
if (!kcs_write_byte(sc, req->ir_addr)) {
|
||||
device_printf(sc->ipmi_dev, "KCS: Failed to write address\n");
|
||||
goto fail;
|
||||
}
|
||||
#ifdef KCS_DEBUG
|
||||
device_printf(sc->ipmi_dev, "KCS: Wrote address: %02x\n", req->ir_addr);
|
||||
#endif
|
||||
|
||||
if (req->ir_requestlen == 0) {
|
||||
if (!kcs_write_last_byte(sc, req->ir_command)) {
|
||||
device_printf(sc->ipmi_dev,
|
||||
"KCS: Failed to write command\n");
|
||||
goto fail;
|
||||
}
|
||||
#ifdef KCS_DEBUG
|
||||
device_printf(sc->ipmi_dev, "KCS: Wrote command: %02x\n",
|
||||
req->ir_command);
|
||||
#endif
|
||||
} else {
|
||||
if (!kcs_write_byte(sc, req->ir_command)) {
|
||||
device_printf(sc->ipmi_dev,
|
||||
"KCS: Failed to write command\n");
|
||||
goto fail;
|
||||
}
|
||||
#ifdef KCS_DEBUG
|
||||
device_printf(sc->ipmi_dev, "KCS: Wrote command: %02x\n",
|
||||
req->ir_command);
|
||||
#endif
|
||||
|
||||
cp = req->ir_request;
|
||||
for (i = 0; i < req->ir_requestlen - 1; i++) {
|
||||
if (!kcs_write_byte(sc, *cp++)) {
|
||||
device_printf(sc->ipmi_dev,
|
||||
"KCS: Failed to write data byte %d\n",
|
||||
i + 1);
|
||||
goto fail;
|
||||
}
|
||||
#ifdef KCS_DEBUG
|
||||
device_printf(sc->ipmi_dev, "KCS: Wrote data: %02x\n",
|
||||
cp[-1]);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!kcs_write_last_byte(sc, *cp)) {
|
||||
device_printf(sc->ipmi_dev,
|
||||
"KCS: Failed to write last dta byte\n");
|
||||
goto fail;
|
||||
}
|
||||
#ifdef KCS_DEBUG
|
||||
device_printf(sc->ipmi_dev, "KCS: Wrote last data: %02x\n",
|
||||
*cp);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Read the reply. First, read the NetFn/LUN. */
|
||||
if (kcs_read_byte(sc, &data) != 1) {
|
||||
device_printf(sc->ipmi_dev, "KCS: Failed to read address\n");
|
||||
goto fail;
|
||||
}
|
||||
#ifdef KCS_DEBUG
|
||||
device_printf(sc->ipmi_dev, "KCS: Read address: %02x\n", data);
|
||||
#endif
|
||||
if (data != IPMI_REPLY_ADDR(req->ir_addr)) {
|
||||
device_printf(sc->ipmi_dev, "KCS: Reply address mismatch\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Next we read the command. */
|
||||
if (kcs_read_byte(sc, &data) != 1) {
|
||||
device_printf(sc->ipmi_dev, "KCS: Failed to read command\n");
|
||||
goto fail;
|
||||
}
|
||||
#ifdef KCS_DEBUG
|
||||
device_printf(sc->ipmi_dev, "KCS: Read command: %02x\n", data);
|
||||
#endif
|
||||
if (data != req->ir_command) {
|
||||
device_printf(sc->ipmi_dev, "KCS: Command mismatch\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Next we read the completion code. */
|
||||
if (kcs_read_byte(sc, &req->ir_compcode) != 1) {
|
||||
device_printf(sc->ipmi_dev,
|
||||
"KCS: Failed to read completion code\n");
|
||||
goto fail;
|
||||
}
|
||||
#ifdef KCS_DEBUG
|
||||
device_printf(sc->ipmi_dev, "KCS: Read completion code: %02x\n",
|
||||
req->ir_compcode);
|
||||
#endif
|
||||
|
||||
/* Finally, read the reply from the BMC. */
|
||||
i = 0;
|
||||
for (;;) {
|
||||
state = kcs_read_byte(sc, &data);
|
||||
if (state == 0) {
|
||||
device_printf(sc->ipmi_dev,
|
||||
"KCS: Read failed on byte %d\n", i + 1);
|
||||
goto fail;
|
||||
}
|
||||
if (state == 2)
|
||||
break;
|
||||
if (i < req->ir_replybuflen) {
|
||||
req->ir_reply[i] = data;
|
||||
#ifdef KCS_DEBUG
|
||||
device_printf(sc->ipmi_dev, "KCS: Read data %02x\n",
|
||||
data);
|
||||
} else {
|
||||
device_printf(sc->ipmi_dev,
|
||||
"KCS: Read short %02x byte %d\n", data, i + 1);
|
||||
#endif
|
||||
}
|
||||
i++;
|
||||
}
|
||||
req->ir_replylen = i;
|
||||
#ifdef KCS_DEBUG
|
||||
device_printf(sc->ipmi_dev, "KCS: READ finished (%d bytes)\n", i);
|
||||
if (req->ir_replybuflen < i)
|
||||
#else
|
||||
if (req->ir_replybuflen < i && req->ir_replybuflen != 0)
|
||||
#endif
|
||||
device_printf(sc->ipmi_dev,
|
||||
"KCS: Read short: %zd buffer, %d actual\n",
|
||||
req->ir_replybuflen, i);
|
||||
return (1);
|
||||
fail:
|
||||
kcs_error(sc);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
kcs_loop(void *arg)
|
||||
{
|
||||
struct ipmi_softc *sc = arg;
|
||||
struct ipmi_request *req;
|
||||
int i, ok;
|
||||
|
||||
IPMI_LOCK(sc);
|
||||
while ((req = ipmi_dequeue_request(sc)) != NULL) {
|
||||
ok = 0;
|
||||
for (i = 0; i < 3 && !ok; i++)
|
||||
ok = kcs_polled_request(sc, req);
|
||||
if (ok)
|
||||
req->ir_error = 0;
|
||||
else
|
||||
req->ir_error = EIO;
|
||||
ipmi_complete_request(sc, req);
|
||||
}
|
||||
IPMI_UNLOCK(sc);
|
||||
kthread_exit(0);
|
||||
}
|
||||
|
||||
static int
|
||||
kcs_startup(struct ipmi_softc *sc)
|
||||
{
|
||||
|
||||
return (kthread_create(kcs_loop, sc, &sc->ipmi_kthread, 0, 0, "%s: kcs",
|
||||
device_get_nameunit(sc->ipmi_dev)));
|
||||
}
|
||||
|
||||
int
|
||||
ipmi_kcs_attach(struct ipmi_softc *sc)
|
||||
{
|
||||
int status;
|
||||
|
||||
/* Setup function pointers. */
|
||||
sc->ipmi_startup = kcs_startup;
|
||||
sc->ipmi_enqueue_request = ipmi_polled_enqueue_request;
|
||||
|
||||
/* See if we can talk to the controller. */
|
||||
status = INB(sc, KCS_CTL_STS);
|
||||
if (status == 0xff) {
|
||||
device_printf(sc->ipmi_dev, "couldn't find it\n");
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
#ifdef KCS_DEBUG
|
||||
device_printf(sc->ipmi_dev, "KCS: initial state: %02x\n", status);
|
||||
#endif
|
||||
if (status & KCS_STATUS_OBF ||
|
||||
KCS_STATUS_STATE(status) != KCS_STATUS_STATE_IDLE)
|
||||
kcs_error(sc);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Determine the alignment automatically for a PCI attachment. In this case,
|
||||
* any unused bytes will return 0x00 when read. We make use of the C/D bit
|
||||
* in the CTL_STS register to try to start a GET_STATUS transaction. When
|
||||
* we write the command, that bit should be set, so we should get a non-zero
|
||||
* value back when we read CTL_STS if the offset we are testing is the CTL_STS
|
||||
* register.
|
||||
*/
|
||||
int
|
||||
ipmi_kcs_probe_align(struct ipmi_softc *sc)
|
||||
{
|
||||
int data, status;
|
||||
|
||||
sc->ipmi_io_spacing = 1;
|
||||
retry:
|
||||
#ifdef KCS_DEBUG
|
||||
device_printf(sc->ipmi_dev, "Trying KCS align %d... ", sc->ipmi_io_spacing);
|
||||
#endif
|
||||
|
||||
/* Wait for IBF = 0 */
|
||||
status = INB(sc, KCS_CTL_STS);
|
||||
while (status & KCS_STATUS_IBF) {
|
||||
DELAY(100);
|
||||
status = INB(sc, KCS_CTL_STS);
|
||||
}
|
||||
|
||||
OUTB(sc, KCS_CTL_STS, KCS_CONTROL_GET_STATUS_ABORT);
|
||||
|
||||
/* Wait for IBF = 0 */
|
||||
status = INB(sc, KCS_CTL_STS);
|
||||
while (status & KCS_STATUS_IBF) {
|
||||
DELAY(100);
|
||||
status = INB(sc, KCS_CTL_STS);
|
||||
}
|
||||
|
||||
/* If we got 0x00 back, then this must not be the CTL_STS register. */
|
||||
if (status == 0) {
|
||||
#ifdef KCS_DEBUG
|
||||
printf("failed\n");
|
||||
#endif
|
||||
sc->ipmi_io_spacing <<= 1;
|
||||
if (sc->ipmi_io_spacing > 4)
|
||||
return (0);
|
||||
goto retry;
|
||||
}
|
||||
#ifdef KCS_DEBUG
|
||||
printf("ok\n");
|
||||
#endif
|
||||
|
||||
/* Finish out the transaction. */
|
||||
|
||||
/* Clear OBF */
|
||||
if (status && KCS_STATUS_OBF)
|
||||
data = INB(sc, KCS_DATA);
|
||||
|
||||
/* 0x00 to DATA_IN */
|
||||
OUTB(sc, KCS_DATA, 0);
|
||||
|
||||
/* Wait for IBF = 0 */
|
||||
status = INB(sc, KCS_CTL_STS);
|
||||
while (status & KCS_STATUS_IBF) {
|
||||
DELAY(100);
|
||||
status = INB(sc, KCS_CTL_STS);
|
||||
}
|
||||
|
||||
if (KCS_STATUS_STATE(status) == KCS_STATUS_STATE_READ) {
|
||||
/* Wait for IBF = 1 */
|
||||
while (!(status & KCS_STATUS_OBF)) {
|
||||
DELAY(100);
|
||||
status = INB(sc, KCS_CTL_STS);
|
||||
}
|
||||
|
||||
/* Read error status. */
|
||||
data = INB(sc, KCS_DATA);
|
||||
|
||||
/* Write dummy READ to DATA_IN. */
|
||||
OUTB(sc, KCS_DATA, KCS_DATA_IN_READ);
|
||||
|
||||
/* Wait for IBF = 0 */
|
||||
status = INB(sc, KCS_CTL_STS);
|
||||
while (status & KCS_STATUS_IBF) {
|
||||
DELAY(100);
|
||||
status = INB(sc, KCS_CTL_STS);
|
||||
}
|
||||
}
|
||||
|
||||
if (KCS_STATUS_STATE(status) == KCS_STATUS_STATE_IDLE) {
|
||||
/* Wait for IBF = 1 */
|
||||
while (!(status & KCS_STATUS_OBF)) {
|
||||
DELAY(100);
|
||||
status = INB(sc, KCS_CTL_STS);
|
||||
}
|
||||
|
||||
/* Clear OBF */
|
||||
if (status && KCS_STATUS_OBF)
|
||||
data = INB(sc, KCS_DATA);
|
||||
} else
|
||||
device_printf(sc->ipmi_dev, "KCS probe: end state %x\n",
|
||||
KCS_STATUS_STATE(status));
|
||||
|
||||
return (1);
|
||||
}
|
@ -28,18 +28,14 @@
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/bus.h>
|
||||
#include <sys/condvar.h>
|
||||
#include <sys/eventhandler.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/module.h>
|
||||
#include <sys/selinfo.h>
|
||||
|
||||
#include <sys/bus.h>
|
||||
#include <sys/conf.h>
|
||||
|
||||
#include <machine/bus.h>
|
||||
#include <machine/resource.h>
|
||||
#include <sys/rman.h>
|
||||
#include <sys/selinfo.h>
|
||||
|
||||
#include <dev/pci/pcireg.h>
|
||||
#include <dev/pci/pcivar.h>
|
||||
@ -52,17 +48,8 @@ __FBSDID("$FreeBSD$");
|
||||
|
||||
static int ipmi_pci_probe(device_t dev);
|
||||
static int ipmi_pci_attach(device_t dev);
|
||||
static int ipmi_pci_detach(device_t dev);
|
||||
|
||||
static device_method_t ipmi_methods[] = {
|
||||
/* Device interface */
|
||||
DEVMETHOD(device_probe, ipmi_pci_probe),
|
||||
DEVMETHOD(device_attach, ipmi_pci_attach),
|
||||
DEVMETHOD(device_detach, ipmi_pci_detach),
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
struct ipmi_ident
|
||||
static struct ipmi_ident
|
||||
{
|
||||
u_int16_t vendor;
|
||||
u_int16_t device;
|
||||
@ -72,238 +59,237 @@ struct ipmi_ident
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
static int
|
||||
ipmi_pci_probe(device_t dev) {
|
||||
const char *
|
||||
ipmi_pci_match(uint16_t vendor, uint16_t device)
|
||||
{
|
||||
struct ipmi_ident *m;
|
||||
|
||||
if (ipmi_attached)
|
||||
return ENXIO;
|
||||
|
||||
for (m = ipmi_identifiers; m->vendor != 0; m++) {
|
||||
if ((m->vendor == pci_get_vendor(dev)) &&
|
||||
(m->device == pci_get_device(dev))) {
|
||||
device_set_desc(dev, m->desc);
|
||||
return (BUS_PROBE_DEFAULT);
|
||||
}
|
||||
}
|
||||
|
||||
return ENXIO;
|
||||
for (m = ipmi_identifiers; m->vendor != 0; m++)
|
||||
if (m->vendor == vendor && m->device == device)
|
||||
return (m->desc);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
static int
|
||||
ipmi_pci_attach(device_t dev) {
|
||||
ipmi_pci_probe(device_t dev)
|
||||
{
|
||||
const char *desc;
|
||||
|
||||
if (ipmi_attached)
|
||||
return (ENXIO);
|
||||
|
||||
desc = ipmi_pci_match(pci_get_vendor(dev), pci_get_device(dev));
|
||||
if (desc != NULL) {
|
||||
device_set_desc(dev, desc);
|
||||
return (BUS_PROBE_DEFAULT);
|
||||
}
|
||||
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
static int
|
||||
ipmi_pci_attach(device_t dev)
|
||||
{
|
||||
struct ipmi_softc *sc = device_get_softc(dev);
|
||||
device_t parent, smbios_attach_dev = NULL;
|
||||
devclass_t dc;
|
||||
int status, flags;
|
||||
int error;
|
||||
struct ipmi_get_info info;
|
||||
const char *mode;
|
||||
int error, type;
|
||||
|
||||
/* Look for an IPMI entry in the SMBIOS table. */
|
||||
if (!ipmi_smbios_identify(&info))
|
||||
return (ENXIO);
|
||||
|
||||
/*
|
||||
* We need to attach to something that can address the BIOS/
|
||||
* SMBIOS memory range. This is usually the isa bus however
|
||||
* during a static kernel boot the isa bus is not available
|
||||
* so we run up the tree to the nexus bus. A module load
|
||||
* will use the isa bus attachment. If neither work bail
|
||||
* since the SMBIOS defines stuff we need to know to attach to
|
||||
* this device.
|
||||
*/
|
||||
dc = devclass_find("isa");
|
||||
if (dc != NULL) {
|
||||
smbios_attach_dev = devclass_get_device(dc, 0);
|
||||
}
|
||||
|
||||
if (smbios_attach_dev == NULL) {
|
||||
smbios_attach_dev = dev;
|
||||
for (;;) {
|
||||
parent = device_get_parent(smbios_attach_dev);
|
||||
if (parent == NULL)
|
||||
break;
|
||||
if (strcmp(device_get_name(smbios_attach_dev),
|
||||
"nexus") == 0)
|
||||
break;
|
||||
smbios_attach_dev = parent;
|
||||
}
|
||||
}
|
||||
|
||||
if (smbios_attach_dev == NULL) {
|
||||
device_printf(dev, "Couldn't find isa/nexus device\n");
|
||||
goto bad;
|
||||
}
|
||||
sc->ipmi_smbios_dev = ipmi_smbios_identify(NULL, smbios_attach_dev);
|
||||
if (sc->ipmi_smbios_dev == NULL) {
|
||||
device_printf(dev, "Couldn't find isa device\n");
|
||||
goto bad;
|
||||
}
|
||||
error = ipmi_smbios_probe(sc->ipmi_smbios_dev);
|
||||
if (error != 0) {
|
||||
goto bad;
|
||||
}
|
||||
sc->ipmi_dev = dev;
|
||||
error = ipmi_smbios_query(dev);
|
||||
device_delete_child(dev, sc->ipmi_smbios_dev);
|
||||
if (error != 0)
|
||||
goto bad;
|
||||
|
||||
/* Now we know about the IPMI attachment info. */
|
||||
if (sc->ipmi_bios_info.kcs_mode) {
|
||||
if (sc->ipmi_bios_info.io_mode)
|
||||
device_printf(dev, "KCS mode found at io 0x%llx "
|
||||
"alignment 0x%x on %s\n",
|
||||
(long long)sc->ipmi_bios_info.address,
|
||||
sc->ipmi_bios_info.offset,
|
||||
device_get_name(device_get_parent(sc->ipmi_dev)));
|
||||
else
|
||||
device_printf(dev, "KCS mode found at mem 0x%llx "
|
||||
"alignment 0x%x on %s\n",
|
||||
(long long)sc->ipmi_bios_info.address,
|
||||
sc->ipmi_bios_info.offset,
|
||||
device_get_name(device_get_parent(sc->ipmi_dev)));
|
||||
|
||||
sc->ipmi_kcs_status_reg = sc->ipmi_bios_info.offset;
|
||||
sc->ipmi_kcs_command_reg = sc->ipmi_bios_info.offset;
|
||||
sc->ipmi_kcs_data_out_reg = 0;
|
||||
sc->ipmi_kcs_data_in_reg = 0;
|
||||
|
||||
if (sc->ipmi_bios_info.io_mode) {
|
||||
sc->ipmi_io_rid = PCIR_BAR(0);
|
||||
sc->ipmi_io_res = bus_alloc_resource(dev,
|
||||
SYS_RES_IOPORT, &sc->ipmi_io_rid,
|
||||
sc->ipmi_bios_info.address,
|
||||
sc->ipmi_bios_info.address +
|
||||
(sc->ipmi_bios_info.offset * 2),
|
||||
sc->ipmi_bios_info.offset * 2,
|
||||
RF_ACTIVE);
|
||||
} else {
|
||||
sc->ipmi_mem_rid = PCIR_BAR(0);
|
||||
sc->ipmi_mem_res = bus_alloc_resource(dev,
|
||||
SYS_RES_MEMORY, &sc->ipmi_mem_rid,
|
||||
sc->ipmi_bios_info.address,
|
||||
sc->ipmi_bios_info.address +
|
||||
(sc->ipmi_bios_info.offset * 2),
|
||||
sc->ipmi_bios_info.offset * 2,
|
||||
RF_ACTIVE);
|
||||
}
|
||||
|
||||
if (!sc->ipmi_io_res){
|
||||
device_printf(dev, "couldn't configure pci io res\n");
|
||||
goto bad;
|
||||
}
|
||||
|
||||
status = INB(sc, sc->ipmi_kcs_status_reg);
|
||||
if (status == 0xff) {
|
||||
device_printf(dev, "couldn't find it\n");
|
||||
goto bad;
|
||||
}
|
||||
if(status & KCS_STATUS_OBF){
|
||||
ipmi_read(dev, NULL, 0);
|
||||
}
|
||||
} else if (sc->ipmi_bios_info.smic_mode) {
|
||||
if (sc->ipmi_bios_info.io_mode)
|
||||
device_printf(dev, "SMIC mode found at io 0x%llx "
|
||||
"alignment 0x%x on %s\n",
|
||||
(long long)sc->ipmi_bios_info.address,
|
||||
sc->ipmi_bios_info.offset,
|
||||
device_get_name(device_get_parent(sc->ipmi_dev)));
|
||||
else
|
||||
device_printf(dev, "SMIC mode found at mem 0x%llx "
|
||||
"alignment 0x%x on %s\n",
|
||||
(long long)sc->ipmi_bios_info.address,
|
||||
sc->ipmi_bios_info.offset,
|
||||
device_get_name(device_get_parent(sc->ipmi_dev)));
|
||||
|
||||
sc->ipmi_smic_data = 0;
|
||||
sc->ipmi_smic_ctl_sts = sc->ipmi_bios_info.offset;
|
||||
sc->ipmi_smic_flags = sc->ipmi_bios_info.offset * 2;
|
||||
|
||||
if (sc->ipmi_bios_info.io_mode) {
|
||||
sc->ipmi_io_rid = PCIR_BAR(0);
|
||||
sc->ipmi_io_res = bus_alloc_resource(dev,
|
||||
SYS_RES_IOPORT, &sc->ipmi_io_rid,
|
||||
sc->ipmi_bios_info.address,
|
||||
sc->ipmi_bios_info.address +
|
||||
(sc->ipmi_bios_info.offset * 3),
|
||||
sc->ipmi_bios_info.offset * 3,
|
||||
RF_ACTIVE);
|
||||
} else {
|
||||
sc->ipmi_mem_rid = PCIR_BAR(0);
|
||||
sc->ipmi_mem_res = bus_alloc_resource(dev,
|
||||
SYS_RES_MEMORY, &sc->ipmi_mem_rid,
|
||||
sc->ipmi_bios_info.address,
|
||||
sc->ipmi_bios_info.address +
|
||||
(sc->ipmi_bios_info.offset * 2),
|
||||
sc->ipmi_bios_info.offset * 2,
|
||||
RF_ACTIVE);
|
||||
}
|
||||
|
||||
if (!sc->ipmi_io_res && !sc->ipmi_mem_res){
|
||||
device_printf(dev, "couldn't configure pci res\n");
|
||||
goto bad;
|
||||
}
|
||||
|
||||
flags = INB(sc, sc->ipmi_smic_flags);
|
||||
if (flags == 0xff) {
|
||||
device_printf(dev, "couldn't find it\n");
|
||||
goto bad;
|
||||
}
|
||||
if ((flags & SMIC_STATUS_SMS_ATN)
|
||||
&& (flags & SMIC_STATUS_RX_RDY)){
|
||||
ipmi_read(dev, NULL, 0);
|
||||
}
|
||||
} else {
|
||||
switch (info.iface_type) {
|
||||
case KCS_MODE:
|
||||
mode = "KCS";
|
||||
break;
|
||||
case SMIC_MODE:
|
||||
mode = "SMIC";
|
||||
break;
|
||||
case BT_MODE:
|
||||
device_printf(dev, "BT mode is unsupported\n");
|
||||
return (ENXIO);
|
||||
default:
|
||||
device_printf(dev, "No IPMI interface found\n");
|
||||
goto bad;
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
device_printf(dev, "%s mode found at %s 0x%jx alignment 0x%x on %s\n",
|
||||
mode, info.io_mode ? "io" : "mem",
|
||||
(uintmax_t)info.address, info.offset,
|
||||
device_get_name(device_get_parent(dev)));
|
||||
if (info.io_mode)
|
||||
type = SYS_RES_IOPORT;
|
||||
else
|
||||
type = SYS_RES_MEMORY;
|
||||
|
||||
sc->ipmi_io_rid = PCIR_BAR(0);
|
||||
sc->ipmi_io_res[0] = bus_alloc_resource_any(dev, type,
|
||||
&sc->ipmi_io_rid, RF_ACTIVE);
|
||||
sc->ipmi_io_type = type;
|
||||
sc->ipmi_io_spacing = info.offset;
|
||||
|
||||
if (sc->ipmi_io_res[0] == NULL) {
|
||||
device_printf(dev, "couldn't configure pci io res\n");
|
||||
return (ENXIO);
|
||||
}
|
||||
ipmi_attach(dev);
|
||||
|
||||
sc->ipmi_irq_rid = 0;
|
||||
sc->ipmi_irq_res = bus_alloc_resource_any(sc->ipmi_dev, SYS_RES_IRQ,
|
||||
sc->ipmi_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
|
||||
&sc->ipmi_irq_rid, RF_SHAREABLE | RF_ACTIVE);
|
||||
if (sc->ipmi_irq_res == NULL) {
|
||||
device_printf(sc->ipmi_dev, "can't allocate interrupt\n");
|
||||
} else {
|
||||
if (bus_setup_intr(sc->ipmi_dev, sc->ipmi_irq_res,
|
||||
INTR_TYPE_MISC, ipmi_intr,
|
||||
sc->ipmi_dev, &sc->ipmi_irq)) {
|
||||
device_printf(sc->ipmi_dev,
|
||||
"can't set up interrupt\n");
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
switch (info.iface_type) {
|
||||
case KCS_MODE:
|
||||
error = ipmi_kcs_attach(sc);
|
||||
if (error)
|
||||
goto bad;
|
||||
break;
|
||||
case SMIC_MODE:
|
||||
error = ipmi_smic_attach(sc);
|
||||
if (error)
|
||||
goto bad;
|
||||
break;
|
||||
}
|
||||
error = ipmi_attach(dev);
|
||||
if (error)
|
||||
goto bad;
|
||||
|
||||
return 0;
|
||||
return (0);
|
||||
bad:
|
||||
return ENXIO;
|
||||
ipmi_release_resources(dev);
|
||||
return (error);
|
||||
}
|
||||
|
||||
static int ipmi_pci_detach(device_t dev) {
|
||||
struct ipmi_softc *sc;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
ipmi_detach(dev);
|
||||
if (sc->ipmi_ev_tag)
|
||||
EVENTHANDLER_DEREGISTER(watchdog_list, sc->ipmi_ev_tag);
|
||||
|
||||
if (sc->ipmi_mem_res)
|
||||
bus_release_resource(dev, SYS_RES_MEMORY, sc->ipmi_mem_rid,
|
||||
sc->ipmi_mem_res);
|
||||
if (sc->ipmi_io_res)
|
||||
bus_release_resource(dev, SYS_RES_IOPORT, sc->ipmi_io_rid,
|
||||
sc->ipmi_io_res);
|
||||
if (sc->ipmi_irq)
|
||||
bus_teardown_intr(sc->ipmi_dev, sc->ipmi_irq_res,
|
||||
sc->ipmi_irq);
|
||||
if (sc->ipmi_irq_res)
|
||||
bus_release_resource(sc->ipmi_dev, SYS_RES_IRQ,
|
||||
sc->ipmi_irq_rid, sc->ipmi_irq_res);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static driver_t ipmi_pci_driver = {
|
||||
"ipmi",
|
||||
ipmi_methods,
|
||||
sizeof(struct ipmi_softc)
|
||||
static device_method_t ipmi_methods[] = {
|
||||
/* Device interface */
|
||||
DEVMETHOD(device_probe, ipmi_pci_probe),
|
||||
DEVMETHOD(device_attach, ipmi_pci_attach),
|
||||
DEVMETHOD(device_detach, ipmi_detach),
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
DRIVER_MODULE(ipmi_foo, pci, ipmi_pci_driver, ipmi_devclass, 0, 0);
|
||||
static driver_t ipmi_pci_driver = {
|
||||
"ipmi",
|
||||
ipmi_methods,
|
||||
sizeof(struct ipmi_softc)
|
||||
};
|
||||
|
||||
DRIVER_MODULE(ipmi_pci, pci, ipmi_pci_driver, ipmi_devclass, 0, 0);
|
||||
|
||||
/* Native IPMI on PCI driver. */
|
||||
|
||||
static int
|
||||
ipmi2_pci_probe(device_t dev)
|
||||
{
|
||||
|
||||
if (pci_get_class(dev) == PCIC_SERIALBUS &&
|
||||
pci_get_subclass(dev) == 0x07) {
|
||||
device_set_desc(dev, "IPMI System Interface");
|
||||
return (BUS_PROBE_GENERIC);
|
||||
}
|
||||
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
static int
|
||||
ipmi2_pci_attach(device_t dev)
|
||||
{
|
||||
struct ipmi_softc *sc;
|
||||
int error, iface, type;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
sc->ipmi_dev = dev;
|
||||
|
||||
/* Interface is determined by progif. */
|
||||
switch (pci_get_progif(dev)) {
|
||||
case 0:
|
||||
iface = SMIC_MODE;
|
||||
break;
|
||||
case 1:
|
||||
iface = KCS_MODE;
|
||||
break;
|
||||
case 2:
|
||||
iface = BT_MODE;
|
||||
device_printf(dev, "BT interface unsupported\n");
|
||||
return (ENXIO);
|
||||
default:
|
||||
device_printf(dev, "Unsupported interface: %d\n",
|
||||
pci_get_progif(dev));
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
/*
|
||||
* Bottom bit of bar indicates resouce type. There should be
|
||||
* constants in pcireg.h for fields in a BAR.
|
||||
*/
|
||||
sc->ipmi_io_rid = PCIR_BAR(0);
|
||||
if (pci_read_config(dev, PCIR_BAR(0), 4) & 0x1)
|
||||
type = SYS_RES_IOPORT;
|
||||
else
|
||||
type = SYS_RES_MEMORY;
|
||||
sc->ipmi_io_type = type;
|
||||
sc->ipmi_io_spacing = 1;
|
||||
sc->ipmi_io_res[0] = bus_alloc_resource_any(dev, type,
|
||||
&sc->ipmi_io_rid, RF_ACTIVE);
|
||||
if (sc->ipmi_io_res[0] == NULL) {
|
||||
device_printf(dev, "couldn't map ports/memory\n");
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
sc->ipmi_irq_rid = 0;
|
||||
sc->ipmi_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
|
||||
&sc->ipmi_irq_rid, RF_SHAREABLE | RF_ACTIVE);
|
||||
|
||||
switch (iface) {
|
||||
case KCS_MODE:
|
||||
device_printf(dev, "using KSC interface\n");
|
||||
|
||||
/*
|
||||
* We have to examine the resource directly to determine the
|
||||
* alignment.
|
||||
*/
|
||||
if (!ipmi_kcs_probe_align(sc)) {
|
||||
device_printf(dev, "Unable to determine alignment\n");
|
||||
error = ENXIO;
|
||||
goto bad;
|
||||
}
|
||||
|
||||
error = ipmi_kcs_attach(sc);
|
||||
if (error)
|
||||
goto bad;
|
||||
break;
|
||||
case SMIC_MODE:
|
||||
device_printf(dev, "using SMIC interface\n");
|
||||
|
||||
error = ipmi_smic_attach(sc);
|
||||
if (error)
|
||||
goto bad;
|
||||
break;
|
||||
}
|
||||
error = ipmi_attach(dev);
|
||||
if (error)
|
||||
goto bad;
|
||||
|
||||
return (0);
|
||||
bad:
|
||||
ipmi_release_resources(dev);
|
||||
return (error);
|
||||
}
|
||||
|
||||
static device_method_t ipmi2_methods[] = {
|
||||
/* Device interface */
|
||||
DEVMETHOD(device_probe, ipmi2_pci_probe),
|
||||
DEVMETHOD(device_attach, ipmi2_pci_attach),
|
||||
DEVMETHOD(device_detach, ipmi_detach),
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
static driver_t ipmi2_pci_driver = {
|
||||
"ipmi",
|
||||
ipmi2_methods,
|
||||
sizeof(struct ipmi_softc)
|
||||
};
|
||||
|
||||
DRIVER_MODULE(ipmi2_pci, pci, ipmi2_pci_driver, ipmi_devclass, 0, 0);
|
||||
|
@ -29,20 +29,13 @@ __FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/bus.h>
|
||||
#include <sys/condvar.h>
|
||||
#include <sys/eventhandler.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/poll.h>
|
||||
#include <sys/selinfo.h>
|
||||
|
||||
#include <sys/module.h>
|
||||
#include <sys/bus.h>
|
||||
|
||||
#include <machine/bus.h>
|
||||
#include <machine/resource.h>
|
||||
#include <sys/rman.h>
|
||||
#include <sys/watchdog.h>
|
||||
|
||||
#include <vm/vm.h>
|
||||
#include <vm/vm_param.h>
|
||||
#include <vm/pmap.h>
|
||||
#include <machine/pc/bios.h>
|
||||
|
||||
@ -77,7 +70,7 @@ struct structure_header {
|
||||
uint16_t handle;
|
||||
};
|
||||
|
||||
struct ipmi_device {
|
||||
struct ipmi_entry {
|
||||
uint8_t type;
|
||||
uint8_t length;
|
||||
uint16_t handle;
|
||||
@ -90,50 +83,88 @@ struct ipmi_device {
|
||||
uint8_t interrupt_number;
|
||||
};
|
||||
|
||||
/* Fields in the base_address field of an IPMI entry. */
|
||||
#define IPMI_BAR_MODE(ba) ((ba) & 0x0000000000000001)
|
||||
#define IPMI_BAR_ADDR(ba) ((ba) & 0xfffffffffffffffe)
|
||||
|
||||
/* Fields in the base_address_modifier field of an IPMI entry. */
|
||||
#define IPMI_BAM_IRQ_TRIGGER 0x01
|
||||
#define IPMI_BAM_IRQ_POLARITY 0x02
|
||||
#define IPMI_BAM_IRQ_VALID 0x08
|
||||
#define IPMI_BAM_ADDR_LSB(bam) (((bam) & 0x10) >> 4)
|
||||
#define IPMI_BAM_REG_SPACING(bam) (((bam) & 0xc0) >> 6)
|
||||
#define SPACING_8 0x0
|
||||
#define SPACING_32 0x1
|
||||
#define SPACING_16 0x2
|
||||
|
||||
#define SMBIOS_START 0xf0000
|
||||
#define SMBIOS_STEP 0x10
|
||||
#define SMBIOS_OFF 0
|
||||
#define SMBIOS_LEN 4
|
||||
#define SMBIOS_SIG "_SM_"
|
||||
|
||||
devclass_t ipmi_devclass;
|
||||
typedef void (*dispatchproc_t)(uint8_t *p, char **table,
|
||||
struct ipmi_get_info *info);
|
||||
|
||||
static void smbios_run_table(uint8_t *, int, dispatchproc_t *,
|
||||
struct ipmi_get_info *);
|
||||
static char *get_strings(char *, char **);
|
||||
static int smbios_cksum (struct smbios_table_entry *);
|
||||
static void smbios_t38_proc_info(uint8_t *, char **, struct ipmi_get_info *);
|
||||
static int ipmi_smbios_attach (device_t);
|
||||
static int ipmi_smbios_modevent(module_t, int, void *);
|
||||
static struct ipmi_get_info ipmi_info;
|
||||
static int ipmi_probed;
|
||||
static struct mtx ipmi_info_mtx;
|
||||
MTX_SYSINIT(ipmi_info, &ipmi_info_mtx, "ipmi info", MTX_DEF);
|
||||
|
||||
static char *get_strings(char *, char **);
|
||||
static void ipmi_smbios_probe(struct ipmi_get_info *);
|
||||
static int smbios_cksum (struct smbios_table_entry *);
|
||||
static void smbios_run_table(uint8_t *, int, dispatchproc_t *,
|
||||
struct ipmi_get_info *);
|
||||
static void smbios_t38_proc_info(uint8_t *, char **,
|
||||
struct ipmi_get_info *);
|
||||
|
||||
static void
|
||||
smbios_t38_proc_info(uint8_t *p, char **table, struct ipmi_get_info *info)
|
||||
{
|
||||
struct ipmi_device *s = (struct ipmi_device *) p;
|
||||
struct ipmi_entry *s = (struct ipmi_entry *)p;
|
||||
|
||||
bzero(info, sizeof(struct ipmi_get_info));
|
||||
if (s->interface_type == 0x01)
|
||||
info->kcs_mode = 1;
|
||||
if (s->interface_type == 0x02)
|
||||
info->smic_mode = 1;
|
||||
info->address = s->base_address & ~1;
|
||||
switch (s->base_address_modifier >> 6) {
|
||||
case 0x00:
|
||||
info->offset = 1;
|
||||
switch (s->interface_type) {
|
||||
case KCS_MODE:
|
||||
case SMIC_MODE:
|
||||
info->address = IPMI_BAR_ADDR(s->base_address) |
|
||||
IPMI_BAM_ADDR_LSB(s->base_address_modifier);
|
||||
info->io_mode = IPMI_BAR_MODE(s->base_address);
|
||||
switch (IPMI_BAM_REG_SPACING(s->base_address_modifier)) {
|
||||
case SPACING_8:
|
||||
info->offset = 1;
|
||||
break;
|
||||
case SPACING_32:
|
||||
info->offset = 4;
|
||||
break;
|
||||
case SPACING_16:
|
||||
info->offset = 2;
|
||||
break;
|
||||
default:
|
||||
printf("SMBIOS: Invalid register spacing\n");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case 0x01:
|
||||
info->offset = 4;
|
||||
break;
|
||||
case 0x10:
|
||||
info->offset = 2;
|
||||
break;
|
||||
case 0x11:
|
||||
info->offset = 0;
|
||||
case SSIF_MODE:
|
||||
if ((s->base_address & 0xffffffffffffff00) != 0) {
|
||||
printf("SMBIOS: Invalid SSIF SMBus address, using BMC I2C slave address instead\n");
|
||||
info->address = s->i2c_slave_address >> 1;
|
||||
break;
|
||||
}
|
||||
info->address = IPMI_BAR_ADDR(s->base_address) >> 1;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
info->io_mode = s->base_address & 1;
|
||||
if (s->length > offsetof(struct ipmi_entry, interrupt_number)) {
|
||||
if (s->interrupt_number > 15)
|
||||
printf("SMBIOS: Non-ISA IRQ %d for IPMI\n",
|
||||
s->interrupt_number);
|
||||
else
|
||||
info->irq = s->interrupt_number;
|
||||
}
|
||||
info->iface_type = s->interface_type;
|
||||
}
|
||||
|
||||
static char *
|
||||
@ -147,7 +178,7 @@ get_strings(char *p, char **table)
|
||||
*table = 0;
|
||||
|
||||
/* Skip past terminating null byte */
|
||||
return p + 1;
|
||||
return (p + 1);
|
||||
}
|
||||
|
||||
|
||||
@ -177,282 +208,88 @@ smbios_run_table(uint8_t *p, int entries, dispatchproc_t *dispatchstatus,
|
||||
}
|
||||
}
|
||||
|
||||
device_t
|
||||
ipmi_smbios_identify (driver_t *driver, device_t parent)
|
||||
/*
|
||||
* Walk the SMBIOS table looking for an IPMI (type 38) entry. If we find
|
||||
* one, return the parsed data in the passed in ipmi_get_info structure and
|
||||
* return true. If we don't find one, return false.
|
||||
*/
|
||||
static void
|
||||
ipmi_smbios_probe(struct ipmi_get_info *info)
|
||||
{
|
||||
device_t child = NULL;
|
||||
dispatchproc_t dispatch_smbios_ipmi[256];
|
||||
struct smbios_table_entry *header;
|
||||
void *table;
|
||||
u_int32_t addr;
|
||||
int length;
|
||||
int rid1, rid2;
|
||||
|
||||
bzero(info, sizeof(struct ipmi_get_info));
|
||||
|
||||
/* Find the SMBIOS table header. */
|
||||
addr = bios_sigsearch(SMBIOS_START, SMBIOS_SIG, SMBIOS_LEN,
|
||||
SMBIOS_STEP, SMBIOS_OFF);
|
||||
if (addr != 0) {
|
||||
rid1 = 0;
|
||||
length = ((struct smbios_table_entry *)BIOS_PADDRTOVADDR(addr))
|
||||
->length;
|
||||
if (addr == 0)
|
||||
return;
|
||||
|
||||
child = BUS_ADD_CHILD(parent, 0, "ipmi", -1);
|
||||
if (driver != NULL)
|
||||
device_set_driver(child, driver);
|
||||
bus_set_resource(child, SYS_RES_MEMORY, rid1, addr, length);
|
||||
rid2 = 1;
|
||||
length = ((struct smbios_table_entry *)BIOS_PADDRTOVADDR(addr))
|
||||
->structure_table_length;
|
||||
addr = ((struct smbios_table_entry *)BIOS_PADDRTOVADDR(addr))
|
||||
->structure_table_address;
|
||||
bus_set_resource(child, SYS_RES_MEMORY, rid2, addr, length);
|
||||
device_set_desc(child, "System Management BIOS");
|
||||
} else {
|
||||
device_printf(parent, "Failed to find SMBIOS signature\n");
|
||||
/*
|
||||
* Map the header. We first map a fixed size to get the actual
|
||||
* length and then map it a second time with the actual length so
|
||||
* we can verify the checksum.
|
||||
*/
|
||||
header = pmap_mapbios(addr, sizeof(struct smbios_table_entry));
|
||||
table = pmap_mapbios(addr, header->length);
|
||||
pmap_unmapbios((vm_offset_t)header, sizeof(struct smbios_table_entry));
|
||||
header = table;
|
||||
if (smbios_cksum(header) != 0) {
|
||||
pmap_unmapbios((vm_offset_t)header, header->length);
|
||||
return;
|
||||
}
|
||||
|
||||
return child;
|
||||
}
|
||||
|
||||
int
|
||||
ipmi_smbios_probe(device_t dev)
|
||||
{
|
||||
struct resource *res1 = NULL, *res2 = NULL;
|
||||
int rid1, rid2;
|
||||
int error;
|
||||
|
||||
if (ipmi_attached)
|
||||
return(EBUSY);
|
||||
|
||||
error = 0;
|
||||
rid1 = 0;
|
||||
rid2 = 1;
|
||||
res1 = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid1,
|
||||
0ul, ~0ul, 1, RF_ACTIVE | RF_SHAREABLE);
|
||||
|
||||
if (res1 == NULL) {
|
||||
device_printf(dev, "Unable to allocate memory resource.\n");
|
||||
error = ENOMEM;
|
||||
goto bad;
|
||||
}
|
||||
res2 = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid2,
|
||||
0ul, ~0ul, 1, RF_ACTIVE | RF_SHAREABLE);
|
||||
if (res2 == NULL) {
|
||||
device_printf(dev, "Unable to allocate memory resource.\n");
|
||||
error = ENOMEM;
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if (smbios_cksum(
|
||||
(struct smbios_table_entry *)rman_get_virtual(res1))) {
|
||||
device_printf(dev, "SMBIOS checksum failed.\n");
|
||||
error = ENXIO;
|
||||
goto bad;
|
||||
}
|
||||
|
||||
bad:
|
||||
if (res1)
|
||||
bus_release_resource(dev, SYS_RES_MEMORY, rid1, res1);
|
||||
if (res2)
|
||||
bus_release_resource(dev, SYS_RES_MEMORY, rid2, res2);
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
ipmi_smbios_query(device_t dev)
|
||||
{
|
||||
struct ipmi_softc *sc = device_get_softc(dev);
|
||||
dispatchproc_t dispatch_smbios_ipmi[256];
|
||||
struct resource *res = NULL , *res2 = NULL;
|
||||
int rid, rid2;
|
||||
int error;
|
||||
|
||||
error = 0;
|
||||
|
||||
rid = 0;
|
||||
res = bus_alloc_resource(sc->ipmi_smbios_dev, SYS_RES_MEMORY, &rid,
|
||||
0ul, ~0ul, 1, RF_ACTIVE | RF_SHAREABLE );
|
||||
if (res == NULL) {
|
||||
device_printf(dev, "Unable to allocate memory resource.\n");
|
||||
error = ENOMEM;
|
||||
goto bad;
|
||||
}
|
||||
rid2 = 1;
|
||||
res2 = bus_alloc_resource(sc->ipmi_smbios_dev, SYS_RES_MEMORY, &rid2,
|
||||
0ul, ~0ul, 1, RF_ACTIVE | RF_SHAREABLE);
|
||||
if (res2 == NULL) {
|
||||
device_printf(dev, "Unable to allocate memory resource.\n");
|
||||
error = ENOMEM;
|
||||
goto bad;
|
||||
}
|
||||
|
||||
sc->ipmi_smbios =
|
||||
(struct smbios_table_entry *)rman_get_virtual(res);
|
||||
|
||||
sc->ipmi_busy = 0;
|
||||
|
||||
device_printf(sc->ipmi_smbios_dev, "SMBIOS Version: %d.%02d",
|
||||
sc->ipmi_smbios->major_version,
|
||||
sc->ipmi_smbios->minor_version);
|
||||
if (bcd2bin(sc->ipmi_smbios->BCD_revision))
|
||||
printf(", revision: %d.%02d",
|
||||
bcd2bin(sc->ipmi_smbios->BCD_revision >> 4),
|
||||
bcd2bin(sc->ipmi_smbios->BCD_revision & 0x0f));
|
||||
printf("\n");
|
||||
|
||||
bzero(&sc->ipmi_bios_info, sizeof(sc->ipmi_bios_info));
|
||||
|
||||
/* Now map the actual table and walk it looking for an IPMI entry. */
|
||||
table = pmap_mapbios(header->structure_table_address,
|
||||
header->structure_table_length);
|
||||
bzero((void *)dispatch_smbios_ipmi, sizeof(dispatch_smbios_ipmi));
|
||||
dispatch_smbios_ipmi[38] = (void *)smbios_t38_proc_info;
|
||||
smbios_run_table(
|
||||
(void *)rman_get_virtual(res2),
|
||||
sc->ipmi_smbios->number_structures,
|
||||
dispatch_smbios_ipmi,
|
||||
(void*)&sc->ipmi_bios_info);
|
||||
smbios_run_table(table, header->number_structures, dispatch_smbios_ipmi,
|
||||
info);
|
||||
|
||||
bad:
|
||||
if (res)
|
||||
bus_release_resource(sc->ipmi_smbios_dev, SYS_RES_MEMORY,
|
||||
rid, res);
|
||||
res = NULL;
|
||||
if (res2)
|
||||
bus_release_resource(sc->ipmi_smbios_dev, SYS_RES_MEMORY,
|
||||
rid2, res2);
|
||||
res2 = NULL;
|
||||
return 0;
|
||||
/* Unmap everything. */
|
||||
pmap_unmapbios((vm_offset_t)table, header->structure_table_length);
|
||||
pmap_unmapbios((vm_offset_t)header, header->length);
|
||||
}
|
||||
|
||||
static int
|
||||
ipmi_smbios_attach(device_t dev)
|
||||
/*
|
||||
* Return the SMBIOS IPMI table entry info to the caller. If we haven't
|
||||
* searched the IPMI table yet, search it. Otherwise, return a cached
|
||||
* copy of the data.
|
||||
*/
|
||||
int
|
||||
ipmi_smbios_identify(struct ipmi_get_info *info)
|
||||
{
|
||||
struct ipmi_softc *sc = device_get_softc(dev);
|
||||
int error;
|
||||
int status, flags;
|
||||
|
||||
error = 0;
|
||||
sc->ipmi_smbios_dev = dev;
|
||||
sc->ipmi_dev = dev;
|
||||
ipmi_smbios_query(dev);
|
||||
|
||||
if (sc->ipmi_bios_info.kcs_mode) {
|
||||
if (sc->ipmi_bios_info.io_mode)
|
||||
device_printf(dev, "KCS mode found at io 0x%llx "
|
||||
"alignment 0x%x on %s\n",
|
||||
(long long)sc->ipmi_bios_info.address,
|
||||
sc->ipmi_bios_info.offset,
|
||||
device_get_name(device_get_parent(sc->ipmi_dev)));
|
||||
else
|
||||
device_printf(dev, "KCS mode found at mem 0x%llx "
|
||||
"alignment 0x%x on %s\n",
|
||||
(long long)sc->ipmi_bios_info.address,
|
||||
sc->ipmi_bios_info.offset,
|
||||
device_get_name(device_get_parent(sc->ipmi_dev)));
|
||||
|
||||
sc->ipmi_kcs_status_reg = sc->ipmi_bios_info.offset;
|
||||
sc->ipmi_kcs_command_reg = sc->ipmi_bios_info.offset;
|
||||
sc->ipmi_kcs_data_out_reg = 0;
|
||||
sc->ipmi_kcs_data_in_reg = 0;
|
||||
|
||||
if (sc->ipmi_bios_info.io_mode) {
|
||||
sc->ipmi_io_rid = 2;
|
||||
sc->ipmi_io_res = bus_alloc_resource(dev,
|
||||
SYS_RES_IOPORT, &sc->ipmi_io_rid,
|
||||
sc->ipmi_bios_info.address,
|
||||
sc->ipmi_bios_info.address +
|
||||
(sc->ipmi_bios_info.offset * 2),
|
||||
sc->ipmi_bios_info.offset * 2,
|
||||
RF_ACTIVE);
|
||||
} else {
|
||||
sc->ipmi_mem_rid = 2;
|
||||
sc->ipmi_mem_res = bus_alloc_resource(dev,
|
||||
SYS_RES_MEMORY, &sc->ipmi_mem_rid,
|
||||
sc->ipmi_bios_info.address,
|
||||
sc->ipmi_bios_info.address +
|
||||
(sc->ipmi_bios_info.offset * 2),
|
||||
sc->ipmi_bios_info.offset * 2,
|
||||
RF_ACTIVE);
|
||||
}
|
||||
|
||||
if (!sc->ipmi_io_res){
|
||||
device_printf(dev,
|
||||
"couldn't configure smbios io res\n");
|
||||
goto bad;
|
||||
}
|
||||
|
||||
status = INB(sc, sc->ipmi_kcs_status_reg);
|
||||
if (status == 0xff) {
|
||||
device_printf(dev, "couldn't find it\n");
|
||||
error = ENXIO;
|
||||
goto bad;
|
||||
}
|
||||
if(status & KCS_STATUS_OBF){
|
||||
ipmi_read(dev, NULL, 0);
|
||||
}
|
||||
} else if (sc->ipmi_bios_info.smic_mode) {
|
||||
if (sc->ipmi_bios_info.io_mode)
|
||||
device_printf(dev, "SMIC mode found at io 0x%llx "
|
||||
"alignment 0x%x on %s\n",
|
||||
(long long)sc->ipmi_bios_info.address,
|
||||
sc->ipmi_bios_info.offset,
|
||||
device_get_name(device_get_parent(sc->ipmi_dev)));
|
||||
else
|
||||
device_printf(dev, "SMIC mode found at mem 0x%llx "
|
||||
"alignment 0x%x on %s\n",
|
||||
(long long)sc->ipmi_bios_info.address,
|
||||
sc->ipmi_bios_info.offset,
|
||||
device_get_name(device_get_parent(sc->ipmi_dev)));
|
||||
|
||||
sc->ipmi_smic_data = 0;
|
||||
sc->ipmi_smic_ctl_sts = sc->ipmi_bios_info.offset;
|
||||
sc->ipmi_smic_flags = sc->ipmi_bios_info.offset * 2;
|
||||
|
||||
if (sc->ipmi_bios_info.io_mode) {
|
||||
sc->ipmi_io_rid = 2;
|
||||
sc->ipmi_io_res = bus_alloc_resource(dev,
|
||||
SYS_RES_IOPORT, &sc->ipmi_io_rid,
|
||||
sc->ipmi_bios_info.address,
|
||||
sc->ipmi_bios_info.address +
|
||||
(sc->ipmi_bios_info.offset * 3),
|
||||
sc->ipmi_bios_info.offset * 3,
|
||||
RF_ACTIVE);
|
||||
} else {
|
||||
sc->ipmi_mem_res = bus_alloc_resource(dev,
|
||||
SYS_RES_MEMORY, &sc->ipmi_mem_rid,
|
||||
sc->ipmi_bios_info.address,
|
||||
sc->ipmi_bios_info.address +
|
||||
(sc->ipmi_bios_info.offset * 2),
|
||||
sc->ipmi_bios_info.offset * 2,
|
||||
RF_ACTIVE);
|
||||
}
|
||||
|
||||
if (!sc->ipmi_io_res && !sc->ipmi_mem_res){
|
||||
device_printf(dev, "couldn't configure smbios res\n");
|
||||
error = ENXIO;
|
||||
goto bad;
|
||||
}
|
||||
|
||||
flags = INB(sc, sc->ipmi_smic_flags);
|
||||
if (flags == 0xff) {
|
||||
device_printf(dev, "couldn't find it\n");
|
||||
error = ENXIO;
|
||||
goto bad;
|
||||
}
|
||||
if ((flags & SMIC_STATUS_SMS_ATN)
|
||||
&& (flags & SMIC_STATUS_RX_RDY)){
|
||||
/* skip in smbio mode
|
||||
ipmi_read(dev, NULL, 0);
|
||||
*/
|
||||
}
|
||||
} else {
|
||||
device_printf(dev, "No IPMI interface found\n");
|
||||
error = ENXIO;
|
||||
goto bad;
|
||||
mtx_lock(&ipmi_info_mtx);
|
||||
switch (ipmi_probed) {
|
||||
case 0:
|
||||
/* Need to probe the SMBIOS table. */
|
||||
ipmi_probed++;
|
||||
mtx_unlock(&ipmi_info_mtx);
|
||||
ipmi_smbios_probe(&ipmi_info);
|
||||
mtx_lock(&ipmi_info_mtx);
|
||||
ipmi_probed++;
|
||||
wakeup(&ipmi_info);
|
||||
break;
|
||||
case 1:
|
||||
/* Another thread is currently probing the table, so wait. */
|
||||
while (ipmi_probed == 1)
|
||||
msleep(&ipmi_info, &ipmi_info_mtx, 0, "ipmi info", 0);
|
||||
break;
|
||||
default:
|
||||
/* The cached data is available. */
|
||||
break;
|
||||
}
|
||||
ipmi_attach(dev);
|
||||
|
||||
return 0;
|
||||
bad:
|
||||
/*
|
||||
device_delete_child(device_get_parent(dev), dev);
|
||||
*/
|
||||
return error;
|
||||
bcopy(&ipmi_info, info, sizeof(ipmi_info));
|
||||
mtx_unlock(&ipmi_info_mtx);
|
||||
|
||||
return (info->iface_type != 0);
|
||||
}
|
||||
|
||||
static int
|
||||
@ -468,71 +305,5 @@ smbios_cksum (struct smbios_table_entry *e)
|
||||
cksum += ptr[i];
|
||||
}
|
||||
|
||||
return cksum;
|
||||
return (cksum);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
ipmi_smbios_detach (device_t dev)
|
||||
{
|
||||
struct ipmi_softc *sc;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
ipmi_detach(dev);
|
||||
if (sc->ipmi_ev_tag)
|
||||
EVENTHANDLER_DEREGISTER(watchdog_list, sc->ipmi_ev_tag);
|
||||
|
||||
if (sc->ipmi_mem_res)
|
||||
bus_release_resource(dev, SYS_RES_MEMORY, sc->ipmi_mem_rid,
|
||||
sc->ipmi_mem_res);
|
||||
if (sc->ipmi_io_res)
|
||||
bus_release_resource(dev, SYS_RES_IOPORT, sc->ipmi_io_rid,
|
||||
sc->ipmi_io_res);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static device_method_t ipmi_methods[] = {
|
||||
/* Device interface */
|
||||
DEVMETHOD(device_identify, ipmi_smbios_identify),
|
||||
DEVMETHOD(device_probe, ipmi_smbios_probe),
|
||||
DEVMETHOD(device_attach, ipmi_smbios_attach),
|
||||
DEVMETHOD(device_detach, ipmi_smbios_detach),
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
static driver_t ipmi_smbios_driver = {
|
||||
"ipmi",
|
||||
ipmi_methods,
|
||||
sizeof(struct ipmi_softc),
|
||||
};
|
||||
|
||||
static int
|
||||
ipmi_smbios_modevent (mod, what, arg)
|
||||
module_t mod;
|
||||
int what;
|
||||
void * arg;
|
||||
{
|
||||
device_t * devs;
|
||||
int count;
|
||||
int i;
|
||||
|
||||
switch (what) {
|
||||
case MOD_LOAD:
|
||||
return 0;
|
||||
case MOD_UNLOAD:
|
||||
devclass_get_devices(ipmi_devclass, &devs, &count);
|
||||
for (i = 0; i < count; i++) {
|
||||
device_delete_child(device_get_parent(devs[i]),
|
||||
devs[i]);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DRIVER_MODULE(ipmi, isa, ipmi_smbios_driver, ipmi_devclass,
|
||||
ipmi_smbios_modevent, 0);
|
||||
|
131
sys/dev/ipmi/ipmi_smbus.c
Normal file
131
sys/dev/ipmi/ipmi_smbus.c
Normal file
@ -0,0 +1,131 @@
|
||||
/*-
|
||||
* Copyright (c) 2006 IronPort Systems Inc. <ambrisko@ironport.com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/bus.h>
|
||||
#include <sys/condvar.h>
|
||||
#include <sys/eventhandler.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/module.h>
|
||||
#include <sys/rman.h>
|
||||
#include <sys/selinfo.h>
|
||||
|
||||
#include <dev/smbus/smbconf.h>
|
||||
#include <dev/smbus/smbus.h>
|
||||
#include <dev/smbus/smb.h>
|
||||
|
||||
#include "smbus_if.h"
|
||||
|
||||
#ifdef LOCAL_MODULE
|
||||
#include <ipmivars.h>
|
||||
#else
|
||||
#include <dev/ipmi/ipmivars.h>
|
||||
#endif
|
||||
|
||||
static void ipmi_smbus_identify(driver_t *driver, device_t parent);
|
||||
static int ipmi_smbus_probe(device_t dev);
|
||||
static int ipmi_smbus_attach(device_t dev);
|
||||
|
||||
static void
|
||||
ipmi_smbus_identify(driver_t *driver, device_t parent)
|
||||
{
|
||||
struct ipmi_get_info info;
|
||||
|
||||
if (ipmi_smbios_identify(&info) && info.iface_type == SSIF_MODE &&
|
||||
device_find_child(parent, "ipmi", -1) == NULL)
|
||||
BUS_ADD_CHILD(parent, 0, "ipmi", -1);
|
||||
}
|
||||
|
||||
static int
|
||||
ipmi_smbus_probe(device_t dev)
|
||||
{
|
||||
|
||||
device_set_desc(dev, "IPMI System Interface");
|
||||
return (BUS_PROBE_DEFAULT);
|
||||
}
|
||||
|
||||
static int
|
||||
ipmi_smbus_attach(device_t dev)
|
||||
{
|
||||
struct ipmi_softc *sc = device_get_softc(dev);
|
||||
struct ipmi_get_info info;
|
||||
int error;
|
||||
|
||||
/* This should never fail. */
|
||||
if (!ipmi_smbios_identify(&info))
|
||||
return (ENXIO);
|
||||
|
||||
if (info.iface_type != SSIF_MODE) {
|
||||
device_printf(dev, "No SSIF IPMI interface found\n");
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
sc->ipmi_dev = dev;
|
||||
|
||||
if (info.irq != 0) {
|
||||
sc->ipmi_irq_rid = 0;
|
||||
sc->ipmi_irq_res = bus_alloc_resource(dev, SYS_RES_IRQ,
|
||||
&sc->ipmi_irq_rid, info.irq, info.irq, 1,
|
||||
RF_SHAREABLE | RF_ACTIVE);
|
||||
}
|
||||
|
||||
device_printf(dev, "SSIF mode found at address 0x%llx on %s\n",
|
||||
(long long)info.address, device_get_name(device_get_parent(dev)));
|
||||
error = ipmi_ssif_attach(sc, device_get_parent(dev), info.address);
|
||||
if (error)
|
||||
goto bad;
|
||||
|
||||
error = ipmi_attach(dev);
|
||||
if (error)
|
||||
goto bad;
|
||||
|
||||
return (0);
|
||||
bad:
|
||||
ipmi_release_resources(dev);
|
||||
return (error);
|
||||
}
|
||||
|
||||
static device_method_t ipmi_methods[] = {
|
||||
/* Device interface */
|
||||
DEVMETHOD(device_identify, ipmi_smbus_identify),
|
||||
DEVMETHOD(device_probe, ipmi_smbus_probe),
|
||||
DEVMETHOD(device_attach, ipmi_smbus_attach),
|
||||
DEVMETHOD(device_detach, ipmi_detach),
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
static driver_t ipmi_smbus_driver = {
|
||||
"ipmi",
|
||||
ipmi_methods,
|
||||
sizeof(struct ipmi_softc)
|
||||
};
|
||||
|
||||
DRIVER_MODULE(ipmi_smbus, smbus, ipmi_smbus_driver, ipmi_devclass, 0, 0);
|
||||
MODULE_DEPEND(ipmi_smbus, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER);
|
361
sys/dev/ipmi/ipmi_smic.c
Normal file
361
sys/dev/ipmi/ipmi_smic.c
Normal file
@ -0,0 +1,361 @@
|
||||
/*-
|
||||
* Copyright (c) 2006 IronPort Systems Inc. <ambrisko@ironport.com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/bus.h>
|
||||
#include <sys/condvar.h>
|
||||
#include <sys/eventhandler.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/kthread.h>
|
||||
#include <sys/module.h>
|
||||
#include <sys/rman.h>
|
||||
#include <sys/selinfo.h>
|
||||
#include <machine/bus.h>
|
||||
|
||||
#ifdef LOCAL_MODULE
|
||||
#include <ipmi.h>
|
||||
#include <ipmivars.h>
|
||||
#else
|
||||
#include <sys/ipmi.h>
|
||||
#include <dev/ipmi/ipmivars.h>
|
||||
#endif
|
||||
|
||||
static void smic_wait_for_tx_okay(struct ipmi_softc *);
|
||||
static void smic_wait_for_rx_okay(struct ipmi_softc *);
|
||||
static void smic_wait_for_not_busy(struct ipmi_softc *);
|
||||
static void smic_set_busy(struct ipmi_softc *);
|
||||
|
||||
static void
|
||||
smic_wait_for_tx_okay(struct ipmi_softc *sc)
|
||||
{
|
||||
int flags;
|
||||
|
||||
do {
|
||||
flags = INB(sc, SMIC_FLAGS);
|
||||
} while (!(flags & SMIC_STATUS_TX_RDY));
|
||||
}
|
||||
|
||||
static void
|
||||
smic_wait_for_rx_okay(struct ipmi_softc *sc)
|
||||
{
|
||||
int flags;
|
||||
|
||||
do {
|
||||
flags = INB(sc, SMIC_FLAGS);
|
||||
} while (!(flags & SMIC_STATUS_RX_RDY));
|
||||
}
|
||||
|
||||
static void
|
||||
smic_wait_for_not_busy(struct ipmi_softc *sc)
|
||||
{
|
||||
int flags;
|
||||
|
||||
do {
|
||||
flags = INB(sc, SMIC_FLAGS);
|
||||
} while (flags & SMIC_STATUS_BUSY);
|
||||
}
|
||||
|
||||
static void
|
||||
smic_set_busy(struct ipmi_softc *sc)
|
||||
{
|
||||
int flags;
|
||||
|
||||
flags = INB(sc, SMIC_FLAGS);
|
||||
flags |= SMIC_STATUS_BUSY;
|
||||
flags &= ~SMIC_STATUS_RESERVED;
|
||||
OUTB(sc, SMIC_FLAGS, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* Start a transfer with a WR_START transaction that sends the NetFn/LUN
|
||||
* address.
|
||||
*/
|
||||
static int
|
||||
smic_start_write(struct ipmi_softc *sc, u_char data)
|
||||
{
|
||||
u_char error, status;
|
||||
|
||||
smic_wait_for_not_busy(sc);
|
||||
|
||||
OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_WR_START);
|
||||
OUTB(sc, SMIC_DATA, data);
|
||||
smic_set_busy(sc);
|
||||
smic_wait_for_not_busy(sc);
|
||||
status = INB(sc, SMIC_CTL_STS);
|
||||
if (status != SMIC_SC_SMS_WR_START) {
|
||||
error = INB(sc, SMIC_DATA);
|
||||
device_printf(sc->ipmi_dev, "SMIC: Write did not start %02x\n",
|
||||
error);
|
||||
return (0);
|
||||
}
|
||||
return (1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Write a byte in the middle of the message (either the command or one of
|
||||
* the data bytes) using a WR_NEXT transaction.
|
||||
*/
|
||||
static int
|
||||
smic_write_next(struct ipmi_softc *sc, u_char data)
|
||||
{
|
||||
u_char error, status;
|
||||
|
||||
OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_WR_NEXT);
|
||||
smic_wait_for_tx_okay(sc);
|
||||
OUTB(sc, SMIC_DATA, data);
|
||||
smic_set_busy(sc);
|
||||
smic_wait_for_not_busy(sc);
|
||||
status = INB(sc, SMIC_CTL_STS);
|
||||
if (status != SMIC_SC_SMS_WR_NEXT) {
|
||||
error = INB(sc, SMIC_DATA);
|
||||
device_printf(sc->ipmi_dev, "SMIC: Write did not next %02x\n",
|
||||
error);
|
||||
return (0);
|
||||
}
|
||||
return (1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Write the last byte of a transfer to end the write phase via a WR_END
|
||||
* transaction.
|
||||
*/
|
||||
static int
|
||||
smic_write_last(struct ipmi_softc *sc, u_char data)
|
||||
{
|
||||
u_char error, status;
|
||||
|
||||
OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_WR_END);
|
||||
smic_wait_for_tx_okay(sc);
|
||||
OUTB(sc, SMIC_DATA, data);
|
||||
smic_set_busy(sc);
|
||||
smic_wait_for_not_busy(sc);
|
||||
status = INB(sc, SMIC_CTL_STS);
|
||||
if (status != SMIC_SC_SMS_WR_END) {
|
||||
error = INB(sc, SMIC_DATA);
|
||||
device_printf(sc->ipmi_dev, "SMIC: Write did not end %02x\n",
|
||||
error);
|
||||
return (0);
|
||||
}
|
||||
return (1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Start the read phase of a transfer with a RD_START transaction.
|
||||
*/
|
||||
static int
|
||||
smic_start_read(struct ipmi_softc *sc, u_char *data)
|
||||
{
|
||||
u_char error, status;
|
||||
|
||||
smic_wait_for_not_busy(sc);
|
||||
|
||||
OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_RD_START);
|
||||
smic_wait_for_rx_okay(sc);
|
||||
smic_set_busy(sc);
|
||||
smic_wait_for_not_busy(sc);
|
||||
status = INB(sc, SMIC_CTL_STS);
|
||||
if (status != SMIC_SC_SMS_RD_START) {
|
||||
error = INB(sc, SMIC_DATA);
|
||||
device_printf(sc->ipmi_dev, "SMIC: Read did not start %02x\n",
|
||||
error);
|
||||
return (0);
|
||||
}
|
||||
*data = INB(sc, SMIC_DATA);
|
||||
return (1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read a byte via a RD_NEXT transaction. If this was the last byte, return
|
||||
* 2 rather than 1.
|
||||
*/
|
||||
static int
|
||||
smic_read_byte(struct ipmi_softc *sc, u_char *data)
|
||||
{
|
||||
u_char error, status;
|
||||
|
||||
OUTB(sc, SMIC_CTL_STS, SMIC_SC_SMS_RD_NEXT);
|
||||
smic_wait_for_rx_okay(sc);
|
||||
smic_set_busy(sc);
|
||||
smic_wait_for_not_busy(sc);
|
||||
status = INB(sc, SMIC_CTL_STS);
|
||||
if (status != SMIC_SC_SMS_RD_NEXT &&
|
||||
status != SMIC_SC_SMS_RD_END) {
|
||||
error = INB(sc, SMIC_DATA);
|
||||
device_printf(sc->ipmi_dev, "SMIC: Read did not next %02x\n",
|
||||
error);
|
||||
return (0);
|
||||
}
|
||||
*data = INB(sc, SMIC_DATA);
|
||||
if (status == SMIC_SC_SMS_RD_NEXT)
|
||||
return (1);
|
||||
else
|
||||
return (2);
|
||||
}
|
||||
|
||||
/* Complete a transfer via a RD_END transaction after reading the last byte. */
|
||||
static int
|
||||
smic_read_end(struct ipmi_softc *sc)
|
||||
{
|
||||
u_char error, status;
|
||||
|
||||
OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_RD_END);
|
||||
smic_set_busy(sc);
|
||||
smic_wait_for_not_busy(sc);
|
||||
status = INB(sc, SMIC_CTL_STS);
|
||||
if (status != SMIC_SC_SMS_RDY) {
|
||||
error = INB(sc, SMIC_DATA);
|
||||
device_printf(sc->ipmi_dev, "SMIC: Read did not end %02x\n",
|
||||
error);
|
||||
return (0);
|
||||
}
|
||||
return (1);
|
||||
}
|
||||
|
||||
static int
|
||||
smic_polled_request(struct ipmi_softc *sc, struct ipmi_request *req)
|
||||
{
|
||||
u_char *cp, data;
|
||||
int i, state;
|
||||
|
||||
/* First, start the message with the address. */
|
||||
if (!smic_start_write(sc, req->ir_addr))
|
||||
return (0);
|
||||
|
||||
if (req->ir_requestlen == 0) {
|
||||
/* Send the command as the last byte. */
|
||||
if (!smic_write_last(sc, req->ir_command))
|
||||
return (0);
|
||||
} else {
|
||||
/* Send the command. */
|
||||
if (!smic_write_next(sc, req->ir_command))
|
||||
return (0);
|
||||
|
||||
/* Send the payload. */
|
||||
cp = req->ir_request;
|
||||
for (i = 0; i < req->ir_requestlen - 1; i++)
|
||||
if (!smic_write_next(sc, *cp++))
|
||||
return (0);
|
||||
if (!smic_write_last(sc, *cp))
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Start the read phase by reading the NetFn/LUN. */
|
||||
if (smic_start_read(sc, &data) != 1)
|
||||
return (0);
|
||||
if (data != IPMI_REPLY_ADDR(req->ir_addr)) {
|
||||
device_printf(sc->ipmi_dev, "SMIC: Reply address mismatch\n");
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Read the command. */
|
||||
if (smic_read_byte(sc, &data) != 1)
|
||||
return (0);
|
||||
if (data != req->ir_command) {
|
||||
device_printf(sc->ipmi_dev, "SMIC: Command mismatch\n");
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Read the completion code. */
|
||||
state = smic_read_byte(sc, &req->ir_compcode);
|
||||
if (state == 0)
|
||||
return (0);
|
||||
|
||||
/* Finally, read the reply from the BMC. */
|
||||
i = 0;
|
||||
while (state == 1) {
|
||||
state = smic_read_byte(sc, &data);
|
||||
if (state == 0)
|
||||
return (0);
|
||||
if (state == 2)
|
||||
break;
|
||||
if (i < req->ir_replybuflen)
|
||||
req->ir_reply[i] = data;
|
||||
i++;
|
||||
}
|
||||
|
||||
/* Terminate the transfer. */
|
||||
if (!smic_read_end(sc))
|
||||
return (0);
|
||||
req->ir_replylen = i;
|
||||
if (req->ir_replybuflen < i && req->ir_replybuflen != 0)
|
||||
device_printf(sc->ipmi_dev,
|
||||
"SMIC: Read short: %zd buffer, %d actual\n",
|
||||
req->ir_replybuflen, i);
|
||||
return (1);
|
||||
}
|
||||
|
||||
static void
|
||||
smic_loop(void *arg)
|
||||
{
|
||||
struct ipmi_softc *sc = arg;
|
||||
struct ipmi_request *req;
|
||||
int i, ok;
|
||||
|
||||
IPMI_LOCK(sc);
|
||||
while ((req = ipmi_dequeue_request(sc)) != NULL) {
|
||||
ok = 0;
|
||||
for (i = 0; i < 3 && !ok; i++)
|
||||
ok = smic_polled_request(sc, req);
|
||||
if (ok)
|
||||
req->ir_error = 0;
|
||||
else
|
||||
req->ir_error = EIO;
|
||||
ipmi_complete_request(sc, req);
|
||||
}
|
||||
IPMI_UNLOCK(sc);
|
||||
kthread_exit(0);
|
||||
}
|
||||
|
||||
static int
|
||||
smic_startup(struct ipmi_softc *sc)
|
||||
{
|
||||
|
||||
return (kthread_create(smic_loop, sc, &sc->ipmi_kthread, 0, 0,
|
||||
"%s: smic", device_get_nameunit(sc->ipmi_dev)));
|
||||
}
|
||||
|
||||
int
|
||||
ipmi_smic_attach(struct ipmi_softc *sc)
|
||||
{
|
||||
int flags;
|
||||
|
||||
/* Setup function pointers. */
|
||||
sc->ipmi_startup = smic_startup;
|
||||
sc->ipmi_enqueue_request = ipmi_polled_enqueue_request;
|
||||
|
||||
/* See if we can talk to the controller. */
|
||||
flags = INB(sc, SMIC_FLAGS);
|
||||
if (flags == 0xff) {
|
||||
device_printf(sc->ipmi_dev, "couldn't find it\n");
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
375
sys/dev/ipmi/ipmi_ssif.c
Normal file
375
sys/dev/ipmi/ipmi_ssif.c
Normal file
@ -0,0 +1,375 @@
|
||||
/*-
|
||||
* Copyright (c) 2006 IronPort Systems Inc. <ambrisko@ironport.com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/bus.h>
|
||||
#include <sys/condvar.h>
|
||||
#include <sys/eventhandler.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/kthread.h>
|
||||
#include <sys/module.h>
|
||||
#include <sys/selinfo.h>
|
||||
|
||||
#include <dev/smbus/smbconf.h>
|
||||
#include <dev/smbus/smb.h>
|
||||
|
||||
#include "smbus_if.h"
|
||||
|
||||
#ifdef LOCAL_MODULE
|
||||
#include <ipmivars.h>
|
||||
#else
|
||||
#include <dev/ipmi/ipmivars.h>
|
||||
#endif
|
||||
|
||||
#define SMBUS_WRITE_SINGLE 0x02
|
||||
#define SMBUS_WRITE_START 0x06
|
||||
#define SMBUS_WRITE_CONT 0x07
|
||||
#define SMBUS_READ_START 0x03
|
||||
#define SMBUS_READ_CONT 0x09
|
||||
#define SMBUS_DATA_SIZE 32
|
||||
|
||||
#ifdef SSIF_DEBUG
|
||||
static void
|
||||
dump_buffer(device_t dev, const char *msg, u_char *bytes, int len)
|
||||
{
|
||||
int i;
|
||||
|
||||
device_printf(dev, "%s:", msg);
|
||||
for (i = 0; i < len; i++)
|
||||
printf(" %02x", bytes[i]);
|
||||
printf("\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
static int
|
||||
ssif_polled_request(struct ipmi_softc *sc, struct ipmi_request *req)
|
||||
{
|
||||
u_char ssif_buf[SMBUS_DATA_SIZE];
|
||||
device_t dev = sc->ipmi_dev;
|
||||
device_t smbus = sc->ipmi_ssif_smbus;
|
||||
u_char *cp, block, count, offset;
|
||||
size_t len;
|
||||
int error;
|
||||
|
||||
/* Acquire the bus while we send the request. */
|
||||
if (smbus_request_bus(smbus, dev, SMB_WAIT) != 0)
|
||||
return (0);
|
||||
|
||||
/*
|
||||
* First, send out the request. Begin by filling out the first
|
||||
* packet which includes the NetFn/LUN and command.
|
||||
*/
|
||||
ssif_buf[0] = req->ir_addr;
|
||||
ssif_buf[1] = req->ir_command;
|
||||
if (req->ir_requestlen > 0)
|
||||
bcopy(req->ir_request, &ssif_buf[2],
|
||||
min(req->ir_requestlen, SMBUS_DATA_SIZE - 2));
|
||||
|
||||
/* Small requests are sent with a single command. */
|
||||
if (req->ir_requestlen <= 30) {
|
||||
#ifdef SSIF_DEBUG
|
||||
dump_buffer(dev, "WRITE_SINGLE", ssif_buf,
|
||||
req->ir_requestlen + 2);
|
||||
#endif
|
||||
error = smbus_error(smbus_bwrite(smbus,
|
||||
sc->ipmi_ssif_smbus_address, SMBUS_WRITE_SINGLE,
|
||||
req->ir_requestlen + 2, ssif_buf));
|
||||
if (error) {
|
||||
#ifdef SSIF_ERROR_DEBUG
|
||||
device_printf(dev, "SSIF: WRITE_SINGLE error %d\n",
|
||||
error);
|
||||
#endif
|
||||
goto fail;
|
||||
}
|
||||
} else {
|
||||
/* Longer requests are sent out in 32-byte messages. */
|
||||
#ifdef SSIF_DEBUG
|
||||
dump_buffer(dev, "WRITE_START", ssif_buf, SMBUS_DATA_SIZE);
|
||||
#endif
|
||||
error = smbus_error(smbus_bwrite(smbus,
|
||||
sc->ipmi_ssif_smbus_address, SMBUS_WRITE_START,
|
||||
SMBUS_DATA_SIZE, ssif_buf));
|
||||
if (error) {
|
||||
#ifdef SSIF_ERROR_DEBUG
|
||||
device_printf(dev, "SSIF: WRITE_START error %d\n",
|
||||
error);
|
||||
#endif
|
||||
goto fail;
|
||||
}
|
||||
|
||||
len = req->ir_requestlen - (SMBUS_DATA_SIZE - 2);
|
||||
cp = req->ir_request + (SMBUS_DATA_SIZE - 2);
|
||||
while (len > 0) {
|
||||
#ifdef SSIF_DEBUG
|
||||
dump_buffer(dev, "WRITE_CONT", cp,
|
||||
min(len, SMBUS_DATA_SIZE));
|
||||
#endif
|
||||
error = smbus_error(smbus_bwrite(smbus,
|
||||
sc->ipmi_ssif_smbus_address, SMBUS_WRITE_CONT,
|
||||
min(len, SMBUS_DATA_SIZE), cp));
|
||||
if (error) {
|
||||
#ifdef SSIF_ERROR_DEBUG
|
||||
device_printf(dev, "SSIF: WRITE_CONT error %d\n",
|
||||
error);
|
||||
#endif
|
||||
goto fail;
|
||||
}
|
||||
cp += SMBUS_DATA_SIZE;
|
||||
len -= SMBUS_DATA_SIZE;
|
||||
}
|
||||
|
||||
/*
|
||||
* The final WRITE_CONT transaction has to have a non-zero
|
||||
* length that is also not SMBUS_DATA_SIZE. If our last
|
||||
* WRITE_CONT transaction in the loop sent SMBUS_DATA_SIZE
|
||||
* bytes, then len will be 0, and we send an extra 0x00 byte
|
||||
* to terminate the transaction.
|
||||
*/
|
||||
if (len == 0) {
|
||||
char c = 0;
|
||||
|
||||
#ifdef SSIF_DEBUG
|
||||
dump_buffer(dev, "WRITE_CONT", &c, 1);
|
||||
#endif
|
||||
error = smbus_error(smbus_bwrite(smbus,
|
||||
sc->ipmi_ssif_smbus_address, SMBUS_WRITE_CONT,
|
||||
1, &c));
|
||||
if (error) {
|
||||
#ifdef SSIF_ERROR_DEBUG
|
||||
device_printf(dev, "SSIF: WRITE_CONT error %d\n",
|
||||
error);
|
||||
#endif
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Release the bus. */
|
||||
smbus_release_bus(smbus, dev);
|
||||
|
||||
/* Give the BMC 100ms to chew on the request. */
|
||||
tsleep(&error, 0, "ssifwt", hz / 10);
|
||||
|
||||
/* Try to read the first packet. */
|
||||
read_start:
|
||||
if (smbus_request_bus(smbus, dev, SMB_WAIT) != 0)
|
||||
return (0);
|
||||
count = SMBUS_DATA_SIZE;
|
||||
error = smbus_error(smbus_bread(smbus,
|
||||
sc->ipmi_ssif_smbus_address, SMBUS_READ_START, &count, ssif_buf));
|
||||
if (error == ENXIO || error == EBUSY) {
|
||||
smbus_release_bus(smbus, dev);
|
||||
#ifdef SSIF_DEBUG
|
||||
device_printf(dev, "SSIF: READ_START retry\n");
|
||||
#endif
|
||||
/* Give the BMC another 10ms. */
|
||||
tsleep(&error, 0, "ssifwt", hz / 100);
|
||||
goto read_start;
|
||||
}
|
||||
if (error) {
|
||||
#ifdef SSIF_ERROR_DEBUG
|
||||
device_printf(dev, "SSIF: READ_START failed: %d\n", error);
|
||||
#endif
|
||||
goto fail;
|
||||
}
|
||||
#ifdef SSIF_DEBUG
|
||||
device_printf("SSIF: READ_START: ok\n");
|
||||
#endif
|
||||
|
||||
/*
|
||||
* If this is the first part of a multi-part read, then we need to
|
||||
* skip the first two bytes.
|
||||
*/
|
||||
if (count == SMBUS_DATA_SIZE && ssif_buf[0] == 0 && ssif_buf[1] == 1)
|
||||
offset = 2;
|
||||
else
|
||||
offset = 0;
|
||||
|
||||
/* We had better get the reply header. */
|
||||
if (count < 3) {
|
||||
device_printf(dev, "SSIF: Short reply packet\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Verify the NetFn/LUN. */
|
||||
if (ssif_buf[offset] != IPMI_REPLY_ADDR(req->ir_addr)) {
|
||||
device_printf(dev, "SSIF: Reply address mismatch\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Verify the command. */
|
||||
if (ssif_buf[offset + 1] != req->ir_command) {
|
||||
device_printf(dev, "SMIC: Command mismatch\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Read the completion code. */
|
||||
req->ir_compcode = ssif_buf[offset + 2];
|
||||
|
||||
/* If this is a single read, just copy the data and return. */
|
||||
if (offset == 0) {
|
||||
#ifdef SSIF_DEBUG
|
||||
dump_buffer(dev, "READ_SINGLE", ssif_buf, count);
|
||||
#endif
|
||||
len = count - 3;
|
||||
bcopy(&ssif_buf[3], req->ir_reply,
|
||||
min(req->ir_replybuflen, len));
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is the first part of a multi-read transaction, so copy
|
||||
* out the payload and start looping.
|
||||
*/
|
||||
#ifdef SSIF_DEBUG
|
||||
dump_buffer(dev, "READ_START", ssif_buf + 2, count - 2);
|
||||
#endif
|
||||
bcopy(&ssif_buf[5], req->ir_reply, min(req->ir_replybuflen, count - 5));
|
||||
len = count - 5;
|
||||
block = 1;
|
||||
|
||||
for (;;) {
|
||||
/* Read another packet via READ_CONT. */
|
||||
count = SMBUS_DATA_SIZE;
|
||||
error = smbus_error(smbus_bread(smbus,
|
||||
sc->ipmi_ssif_smbus_address, SMBUS_READ_CONT, &count,
|
||||
ssif_buf));
|
||||
if (error) {
|
||||
#ifdef SSIF_ERROR_DEBUG
|
||||
printf("SSIF: READ_CONT failed: %d\n", error);
|
||||
#endif
|
||||
goto fail;
|
||||
}
|
||||
#ifdef SSIF_DEBUG
|
||||
device_printf(dev, "SSIF: READ_CONT... ok\n");
|
||||
#endif
|
||||
|
||||
/* Verify the block number. 0xff marks the last block. */
|
||||
if (ssif_buf[0] != 0xff && ssif_buf[0] != block) {
|
||||
device_printf(dev, "SSIF: Read wrong block %d %d\n",
|
||||
ssif_buf[0], block);
|
||||
goto fail;
|
||||
}
|
||||
if (ssif_buf[0] != 0xff && count < SMBUS_DATA_SIZE) {
|
||||
device_printf(dev,
|
||||
"SSIF: Read short middle block, length %d\n",
|
||||
count);
|
||||
goto fail;
|
||||
}
|
||||
#ifdef SSIF_DEBUG
|
||||
if (ssif_buf[0] == 0xff)
|
||||
dump_buffer(dev, "READ_END", ssif_buf + 1, count - 1);
|
||||
else
|
||||
dump_buffer(dev, "READ_CONT", ssif_buf + 1, count - 1);
|
||||
#endif
|
||||
if (len < req->ir_replybuflen)
|
||||
bcopy(&ssif_buf[1], &req->ir_reply[len],
|
||||
min(req->ir_replybuflen - len, count - 1));
|
||||
len += count - 1;
|
||||
|
||||
/* If this was the last block we are done. */
|
||||
if (ssif_buf[0] != 0xff)
|
||||
break;
|
||||
block++;
|
||||
}
|
||||
|
||||
done:
|
||||
/* Save the total length and return success. */
|
||||
req->ir_replylen = len;
|
||||
smbus_release_bus(smbus, dev);
|
||||
return (1);
|
||||
|
||||
fail:
|
||||
smbus_release_bus(smbus, dev);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
ssif_loop(void *arg)
|
||||
{
|
||||
struct ipmi_softc *sc = arg;
|
||||
struct ipmi_request *req;
|
||||
int i, ok;
|
||||
|
||||
IPMI_LOCK(sc);
|
||||
while ((req = ipmi_dequeue_request(sc)) != NULL) {
|
||||
IPMI_UNLOCK(sc);
|
||||
ok = 0;
|
||||
for (i = 0; i < 5; i++) {
|
||||
ok = ssif_polled_request(sc, req);
|
||||
if (ok)
|
||||
break;
|
||||
|
||||
/* Wait 60 ms between retries. */
|
||||
tsleep(&ok, 0, "retry", 60 * hz / 1000);
|
||||
#ifdef SSIF_RETRY_DEBUG
|
||||
device_printf(sc->ipmi_dev,
|
||||
"SSIF: Retrying request (%d)\n", i + 1);
|
||||
#endif
|
||||
}
|
||||
if (ok)
|
||||
req->ir_error = 0;
|
||||
else
|
||||
req->ir_error = EIO;
|
||||
IPMI_LOCK(sc);
|
||||
ipmi_complete_request(sc, req);
|
||||
IPMI_UNLOCK(sc);
|
||||
|
||||
/* Enforce 10ms between requests. */
|
||||
tsleep(&ok, 0, "delay", hz / 100);
|
||||
|
||||
IPMI_LOCK(sc);
|
||||
}
|
||||
IPMI_UNLOCK(sc);
|
||||
kthread_exit(0);
|
||||
}
|
||||
|
||||
static int
|
||||
ssif_startup(struct ipmi_softc *sc)
|
||||
{
|
||||
|
||||
return (kthread_create(ssif_loop, sc, &sc->ipmi_kthread, 0, 0,
|
||||
"%s: ssif", device_get_nameunit(sc->ipmi_dev)));
|
||||
}
|
||||
|
||||
int
|
||||
ipmi_ssif_attach(struct ipmi_softc *sc, device_t smbus, int smbus_address)
|
||||
{
|
||||
|
||||
/* Setup smbus address. */
|
||||
sc->ipmi_ssif_smbus = smbus;
|
||||
sc->ipmi_ssif_smbus_address = smbus_address;
|
||||
|
||||
/* Setup function pointers. */
|
||||
sc->ipmi_startup = ssif_startup;
|
||||
sc->ipmi_enqueue_request = ipmi_polled_enqueue_request;
|
||||
|
||||
return (0);
|
||||
}
|
@ -26,50 +26,116 @@
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef __IPMIVARS_H__
|
||||
#define __IPMIVARS_H__
|
||||
|
||||
struct ipmi_get_info {
|
||||
int kcs_mode;
|
||||
int smic_mode;
|
||||
int iface_type;
|
||||
uint64_t address;
|
||||
int offset;
|
||||
int io_mode;
|
||||
int irq;
|
||||
};
|
||||
|
||||
struct ipmi_device;
|
||||
|
||||
struct ipmi_request {
|
||||
TAILQ_ENTRY(ipmi_request) ir_link;
|
||||
struct ipmi_device *ir_owner; /* Driver uses NULL. */
|
||||
u_char *ir_request; /* Request is data to send to BMC. */
|
||||
size_t ir_requestlen;
|
||||
u_char *ir_reply; /* Reply is data read from BMC. */
|
||||
size_t ir_replybuflen; /* Length of ir_reply[] buffer. */
|
||||
int ir_replylen; /* Length of reply from BMC. */
|
||||
int ir_error;
|
||||
long ir_msgid;
|
||||
uint8_t ir_addr;
|
||||
uint8_t ir_command;
|
||||
uint8_t ir_compcode;
|
||||
};
|
||||
|
||||
#define MAX_RES 3
|
||||
#define KCS_DATA 0
|
||||
#define KCS_CTL_STS 1
|
||||
#define SMIC_DATA 0
|
||||
#define SMIC_CTL_STS 1
|
||||
#define SMIC_FLAGS 2
|
||||
|
||||
struct ipmi_softc;
|
||||
|
||||
/* Per struct cdev data. */
|
||||
struct ipmi_device {
|
||||
TAILQ_ENTRY(ipmi_device) ipmi_link;
|
||||
TAILQ_HEAD(,ipmi_request) ipmi_completed_requests;
|
||||
struct selinfo ipmi_select;
|
||||
struct ipmi_softc *ipmi_softc;
|
||||
struct cdev *ipmi_cdev;
|
||||
int ipmi_open;
|
||||
int ipmi_closing;
|
||||
int ipmi_requests;
|
||||
u_char ipmi_address; /* IPMB address. */
|
||||
u_char ipmi_lun;
|
||||
};
|
||||
|
||||
struct ipmi_kcs {
|
||||
};
|
||||
|
||||
struct ipmi_smic {
|
||||
};
|
||||
|
||||
struct ipmi_ssif {
|
||||
device_t smbus;
|
||||
int smbus_address;
|
||||
};
|
||||
|
||||
struct ipmi_softc {
|
||||
device_t ipmi_dev;
|
||||
device_t ipmi_smbios_dev;
|
||||
struct cdev *ipmi_dev_t;
|
||||
int ipmi_refcnt;
|
||||
struct smbios_table_entry *ipmi_smbios;
|
||||
struct ipmi_get_info ipmi_bios_info;
|
||||
int ipmi_kcs_status_reg;
|
||||
int ipmi_kcs_command_reg;
|
||||
int ipmi_kcs_data_out_reg;
|
||||
int ipmi_kcs_data_in_reg;
|
||||
int ipmi_smic_data;
|
||||
int ipmi_smic_ctl_sts;
|
||||
int ipmi_smic_flags;
|
||||
union {
|
||||
struct ipmi_kcs kcs;
|
||||
struct ipmi_smic smic;
|
||||
struct ipmi_ssif ssif;
|
||||
} _iface;
|
||||
int ipmi_io_rid;
|
||||
struct resource * ipmi_io_res;
|
||||
int ipmi_mem_rid;
|
||||
struct resource * ipmi_mem_res;
|
||||
int ipmi_io_type;
|
||||
struct resource *ipmi_io_res[MAX_RES];
|
||||
int ipmi_io_spacing;
|
||||
int ipmi_irq_rid;
|
||||
struct resource * ipmi_irq_res;
|
||||
struct resource *ipmi_irq_res;
|
||||
void *ipmi_irq;
|
||||
u_char ipmi_address;
|
||||
u_char ipmi_lun;
|
||||
int ipmi_busy;
|
||||
struct selinfo ipmi_select;
|
||||
int ipmi_timestamp;
|
||||
int ipmi_requests;
|
||||
struct callout_handle ipmi_timeout_handle;
|
||||
TAILQ_HEAD(,ipmi_done_list) ipmi_done;
|
||||
eventhandler_tag ipmi_ev_tag;
|
||||
int ipmi_detaching;
|
||||
#ifdef CLONING
|
||||
int ipmi_cloning;
|
||||
u_int ipmi_cdev_mask;
|
||||
TAILQ_HEAD(,ipmi_device) ipmi_cdevs;
|
||||
#else
|
||||
struct ipmi_device ipmi_idev;
|
||||
#endif
|
||||
TAILQ_HEAD(,ipmi_request) ipmi_pending_requests;
|
||||
#ifdef CLONING
|
||||
eventhandler_tag ipmi_clone_tag;
|
||||
#endif
|
||||
eventhandler_tag ipmi_watchdog_tag;
|
||||
struct intr_config_hook ipmi_ich;
|
||||
struct mtx ipmi_lock;
|
||||
struct cv ipmi_request_added;
|
||||
struct proc *ipmi_kthread;
|
||||
driver_intr_t *ipmi_intr;
|
||||
int (*ipmi_startup)(struct ipmi_softc *);
|
||||
int (*ipmi_enqueue_request)(struct ipmi_softc *, struct ipmi_request *);
|
||||
};
|
||||
|
||||
#define ipmi_ssif_smbus_address _iface.ssif.smbus_address
|
||||
#define ipmi_ssif_smbus _iface.ssif.smbus
|
||||
|
||||
struct ipmi_ipmb {
|
||||
u_char foo;
|
||||
};
|
||||
|
||||
#define KCS_MODE 0x01
|
||||
#define SMIC_MODE 0x02
|
||||
#define BT_MODE 0x03
|
||||
#define SSIF_MODE 0x04
|
||||
|
||||
/* KCS status flags */
|
||||
#define KCS_STATUS_OBF 0x01 /* Data Out ready from BMC */
|
||||
#define KCS_STATUS_IBF 0x02 /* Data In from System */
|
||||
@ -84,11 +150,13 @@ struct ipmi_ipmb {
|
||||
#define KCS_STATUS_STATE_READ 0x1
|
||||
#define KCS_STATUS_STATE_WRITE 0x2
|
||||
#define KCS_STATUS_STATE_ERROR 0x3
|
||||
#define KCS_IFACE_STATUS_OK 0x00
|
||||
#define KCS_IFACE_STATUS_ABORT 0x01
|
||||
#define KCS_IFACE_STATUS_ILLEGAL 0x02
|
||||
#define KCS_IFACE_STATUS_LENGTH_ERR 0x06
|
||||
#define KCS_IFACE_STATUS_UNKNOWN_ERR 0xff
|
||||
|
||||
/* KCD control codes */
|
||||
/* KCS control codes */
|
||||
#define KCS_CONTROL_GET_STATUS_ABORT 0x60
|
||||
#define KCS_CONTROL_WRITE_START 0x61
|
||||
#define KCS_CONTROL_WRITE_END 0x62
|
||||
@ -98,9 +166,10 @@ struct ipmi_ipmb {
|
||||
#define SMIC_STATUS_BUSY 0x01 /* System set and BMC clears it */
|
||||
#define SMIC_STATUS_SMS_ATN 0x04 /* BMC has a message */
|
||||
#define SMIC_STATUS_EVT_ATN 0x08 /* Event has been RX */
|
||||
#define SMIC_STATUS_SMI 0x08 /* asserted SMI */
|
||||
#define SMIC_STATUS_SMI 0x10 /* asserted SMI */
|
||||
#define SMIC_STATUS_TX_RDY 0x40 /* Ready to accept WRITE */
|
||||
#define SMIC_STATUS_RX_RDY 0x80 /* Ready to read */
|
||||
#define SMIC_STATUS_RESERVED 0x22
|
||||
|
||||
/* SMIC control codes */
|
||||
#define SMIC_CC_SMS_GET_STATUS 0x40
|
||||
@ -120,19 +189,78 @@ struct ipmi_ipmb {
|
||||
#define SMIC_SC_SMS_RD_NEXT 0xc5
|
||||
#define SMIC_SC_SMS_RD_END 0xc6
|
||||
|
||||
#define RES(x) (x)->ipmi_io_res ? (x)->ipmi_io_res : (x)->ipmi_mem_res
|
||||
#define INB(sc, x) bus_space_read_1(rman_get_bustag(RES(sc)), \
|
||||
rman_get_bushandle(RES(sc)), (x))
|
||||
#define OUTB(sc, x, value) bus_space_write_1(rman_get_bustag(RES(sc)), \
|
||||
rman_get_bushandle(RES(sc)), (x), value)
|
||||
#define IPMI_ADDR(netfn, lun) ((netfn) << 2 | (lun))
|
||||
#define IPMI_REPLY_ADDR(addr) ((addr) + 0x4)
|
||||
|
||||
int ipmi_attach(device_t);
|
||||
int ipmi_detach(device_t);
|
||||
int ipmi_smbios_query(device_t);
|
||||
int ipmi_smbios_probe(device_t);
|
||||
int ipmi_read(device_t, u_char *, int);
|
||||
void ipmi_intr(void *);
|
||||
#define IPMI_LOCK(sc) mtx_lock(&(sc)->ipmi_lock)
|
||||
#define IPMI_UNLOCK(sc) mtx_unlock(&(sc)->ipmi_lock)
|
||||
#define IPMI_LOCK_ASSERT(sc) mtx_assert(&(sc)->ipmi_lock, MA_OWNED)
|
||||
|
||||
#define ipmi_alloc_driver_request(addr, cmd, reqlen, replylen) \
|
||||
ipmi_alloc_request(NULL, 0, (addr), (cmd), (reqlen), (replylen))
|
||||
|
||||
#if __FreeBSD_version < 601105
|
||||
#define bus_read_1(r, o) \
|
||||
bus_space_read_1(rman_get_bustag(r), rman_get_bushandle(r), (o))
|
||||
#define bus_write_1(r, o, v) \
|
||||
bus_space_write_1(rman_get_bustag(r), rman_get_bushandle(r), (o), (v))
|
||||
#endif
|
||||
|
||||
/* I/O to a single I/O resource. */
|
||||
#define INB_SINGLE(sc, x) \
|
||||
bus_read_1((sc)->ipmi_io_res[0], (sc)->ipmi_io_spacing * (x))
|
||||
#define OUTB_SINGLE(sc, x, value) \
|
||||
bus_write_1((sc)->ipmi_io_res[0], (sc)->ipmi_io_spacing * (x), value)
|
||||
|
||||
/* I/O with each register in its in I/O resource. */
|
||||
#define INB_MULTIPLE(sc, x) \
|
||||
bus_read_1((sc)->ipmi_io_res[(x)], 0)
|
||||
#define OUTB_MULTIPLE(sc, x, value) \
|
||||
bus_write_1((sc)->ipmi_io_res[(x)], 0, value)
|
||||
|
||||
/*
|
||||
* Determine I/O method based on whether or not we have more than one I/O
|
||||
* resource.
|
||||
*/
|
||||
#define INB(sc, x) \
|
||||
((sc)->ipmi_io_res[1] != NULL ? INB_MULTIPLE(sc, x) : INB_SINGLE(sc, x))
|
||||
#define OUTB(sc, x, value) \
|
||||
((sc)->ipmi_io_res[1] != NULL ? OUTB_MULTIPLE(sc, x, value) : \
|
||||
OUTB_SINGLE(sc, x, value))
|
||||
|
||||
#define MAX_TIMEOUT 3 * hz
|
||||
|
||||
int ipmi_attach(device_t);
|
||||
int ipmi_detach(device_t);
|
||||
void ipmi_release_resources(device_t);
|
||||
|
||||
/* Manage requests. */
|
||||
struct ipmi_request *ipmi_alloc_request(struct ipmi_device *, long, uint8_t,
|
||||
uint8_t, size_t, size_t);
|
||||
void ipmi_complete_request(struct ipmi_softc *, struct ipmi_request *);
|
||||
struct ipmi_request *ipmi_dequeue_request(struct ipmi_softc *);
|
||||
void ipmi_free_request(struct ipmi_request *);
|
||||
int ipmi_polled_enqueue_request(struct ipmi_softc *, struct ipmi_request *);
|
||||
int ipmi_submit_driver_request(struct ipmi_softc *, struct ipmi_request *,
|
||||
int);
|
||||
|
||||
/* Identify BMC interface via SMBIOS. */
|
||||
int ipmi_smbios_identify(struct ipmi_get_info *);
|
||||
|
||||
/* Match BMC PCI device listed in SMBIOS. */
|
||||
const char *ipmi_pci_match(uint16_t, uint16_t);
|
||||
|
||||
/* Interface attach routines. */
|
||||
int ipmi_kcs_attach(struct ipmi_softc *);
|
||||
int ipmi_kcs_probe_align(struct ipmi_softc *);
|
||||
int ipmi_smic_attach(struct ipmi_softc *);
|
||||
int ipmi_ssif_attach(struct ipmi_softc *, device_t, int);
|
||||
|
||||
#ifdef IPMB
|
||||
int ipmi_handle_attn(struct ipmi_softc *);
|
||||
#endif
|
||||
|
||||
device_t ipmi_smbios_identify (driver_t *driver, device_t parent);
|
||||
extern devclass_t ipmi_devclass;
|
||||
extern int ipmi_attached;
|
||||
|
||||
#endif /* !__IPMIVARS_H__ */
|
||||
|
@ -698,6 +698,7 @@ options SAFE_RNDTEST # enable rndtest support
|
||||
# Miscellaneous hardware:
|
||||
#
|
||||
# apm: Laptop Advanced Power Management (experimental)
|
||||
# ipmi: Intelligent Platform Management Interface
|
||||
# pmtimer: Timer device driver for power management events (APM or ACPI)
|
||||
# smapi: System Management Application Program Interface driver
|
||||
# smbios: DMI/SMBIOS entry point
|
||||
@ -730,6 +731,7 @@ options SAFE_RNDTEST # enable rndtest support
|
||||
|
||||
device apm
|
||||
hint.apm.0.flags="0x20"
|
||||
device ipmi
|
||||
device smapi
|
||||
device smbios
|
||||
device vpd
|
||||
|
@ -2,8 +2,12 @@
|
||||
|
||||
.PATH: ${.CURDIR}/../../dev/ipmi
|
||||
|
||||
# XXX - ipmi_smbus and ipmi_ssif depend on smbus
|
||||
# XXX - ipmi_acpi depends on acpi
|
||||
KMOD= ipmi
|
||||
SRCS= ipmi.c ipmi_pci.c ipmi_smbios.c
|
||||
SRCS+= bus_if.h device_if.h pci_if.h vnode_if.h
|
||||
SRCS= ipmi.c ipmi_kcs.c ipmi_smic.c ipmi_smbios.c ipmi_ssif.c
|
||||
SRCS+= ipmi_acpi.c ipmi_isa.c ipmi_pci.c ipmi_smbus.c
|
||||
SRCS+= opt_acpi.h
|
||||
SRCS+= acpi_if.h bus_if.h device_if.h isa_if.h pci_if.h smbus_if.h
|
||||
|
||||
.include <bsd.kmod.mk>
|
||||
|
@ -26,6 +26,9 @@
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef __SYS_IPMI_H__
|
||||
#define __SYS_IPMI_H__
|
||||
|
||||
#define IPMI_MAX_ADDR_SIZE 0x20
|
||||
#define IPMI_MAX_RX 1024
|
||||
#define IPMI_BMC_SLAVE_ADDR 0x20 /* Linux Default slave address */
|
||||
@ -117,3 +120,36 @@ struct ipmi_ipmb_addr {
|
||||
unsigned char slave_addr;
|
||||
unsigned char lun;
|
||||
};
|
||||
|
||||
#if defined(__amd64__)
|
||||
/* Compatiblity with 32-bit binaries. */
|
||||
|
||||
#define IPMICTL_RECEIVE_MSG_TRUNC_32 _IOWR(IPMI_IOC_MAGIC, 11, struct ipmi_recv32)
|
||||
#define IPMICTL_RECEIVE_MSG_32 _IOWR(IPMI_IOC_MAGIC, 12, struct ipmi_recv32)
|
||||
#define IPMICTL_SEND_COMMAND_32 _IOW(IPMI_IOC_MAGIC, 13, struct ipmi_req32)
|
||||
|
||||
struct ipmi_msg32 {
|
||||
unsigned char netfn;
|
||||
unsigned char cmd;
|
||||
unsigned short data_len;
|
||||
uint32_t data;
|
||||
};
|
||||
|
||||
struct ipmi_req32 {
|
||||
uint32_t addr;
|
||||
unsigned int addr_len;
|
||||
int32_t msgid;
|
||||
struct ipmi_msg32 msg;
|
||||
};
|
||||
|
||||
struct ipmi_recv32 {
|
||||
int recv_type;
|
||||
uint32_t addr;
|
||||
unsigned int addr_len;
|
||||
int32_t msgid;
|
||||
struct ipmi_msg32 msg;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* !__SYS_IPMI_H__ */
|
||||
|
Loading…
Reference in New Issue
Block a user