1*b63b081bSriastradh /* $NetBSD: imc.c,v 1.6 2023/05/10 00:07:49 riastradh Exp $ */
282ae8dd1Spgoyette
382ae8dd1Spgoyette /*-
482ae8dd1Spgoyette * Copyright (c) 2018 The NetBSD Foundation, Inc.
582ae8dd1Spgoyette * All rights reserved.
682ae8dd1Spgoyette *
782ae8dd1Spgoyette * This code is derived from software contributed to The NetBSD Foundation
882ae8dd1Spgoyette * by Paul Goyette
982ae8dd1Spgoyette *
1082ae8dd1Spgoyette * Redistribution and use in source and binary forms, with or without
1182ae8dd1Spgoyette * modification, are permitted provided that the following conditions
1282ae8dd1Spgoyette * are met:
1382ae8dd1Spgoyette * 1. Redistributions of source code must retain the above copyright
1482ae8dd1Spgoyette * notice, this list of conditions and the following disclaimer.
1582ae8dd1Spgoyette * 2. Redistributions in binary form must reproduce the above copyright
1682ae8dd1Spgoyette * notice, this list of conditions and the following disclaimer in the
1782ae8dd1Spgoyette * documentation and/or other materials provided with the distribution.
1882ae8dd1Spgoyette *
1982ae8dd1Spgoyette * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
2082ae8dd1Spgoyette * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
2182ae8dd1Spgoyette * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2282ae8dd1Spgoyette * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
2382ae8dd1Spgoyette * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2482ae8dd1Spgoyette * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2582ae8dd1Spgoyette * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2682ae8dd1Spgoyette * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2782ae8dd1Spgoyette * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2882ae8dd1Spgoyette * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2982ae8dd1Spgoyette * POSSIBILITY OF SUCH DAMAGE.
3082ae8dd1Spgoyette */
3182ae8dd1Spgoyette
3282ae8dd1Spgoyette /*-
3382ae8dd1Spgoyette * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3482ae8dd1Spgoyette *
3582ae8dd1Spgoyette * Authors: Joe Kloss; Ravi Pokala (rpokala@freebsd.org)
3682ae8dd1Spgoyette *
3782ae8dd1Spgoyette * Copyright (c) 2017-2018 Panasas
3882ae8dd1Spgoyette * All rights reserved.
3982ae8dd1Spgoyette *
4082ae8dd1Spgoyette * Redistribution and use in source and binary forms, with or without
4182ae8dd1Spgoyette * modification, are permitted provided that the following conditions
4282ae8dd1Spgoyette * are met:
4382ae8dd1Spgoyette * 1. Redistributions of source code must retain the above copyright
4482ae8dd1Spgoyette * notice, this list of conditions and the following disclaimer.
4582ae8dd1Spgoyette * 2. Redistributions in binary form must reproduce the above copyright
4682ae8dd1Spgoyette * notice, this list of conditions and the following disclaimer in the
4782ae8dd1Spgoyette * documentation and/or other materials provided with the distribution.
4882ae8dd1Spgoyette *
4982ae8dd1Spgoyette * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
5082ae8dd1Spgoyette * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
5182ae8dd1Spgoyette * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
5282ae8dd1Spgoyette * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
5382ae8dd1Spgoyette * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
5482ae8dd1Spgoyette * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
5582ae8dd1Spgoyette * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
5682ae8dd1Spgoyette * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
5782ae8dd1Spgoyette * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
5882ae8dd1Spgoyette * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
5982ae8dd1Spgoyette * SUCH DAMAGE.
6082ae8dd1Spgoyette */
6182ae8dd1Spgoyette
6282ae8dd1Spgoyette /*
6382ae8dd1Spgoyette * Driver to expose the SMBus controllers in Intel's Integrated
6482ae8dd1Spgoyette * Memory Controllers in certain CPUs.
6582ae8dd1Spgoyette */
6682ae8dd1Spgoyette
6782ae8dd1Spgoyette #include <sys/cdefs.h>
68*b63b081bSriastradh __KERNEL_RCSID(0, "$NetBSD: imc.c,v 1.6 2023/05/10 00:07:49 riastradh Exp $");
6982ae8dd1Spgoyette
7082ae8dd1Spgoyette #include <sys/param.h>
7182ae8dd1Spgoyette #include <sys/kernel.h>
7282ae8dd1Spgoyette #include <sys/module.h>
7382ae8dd1Spgoyette #include <sys/errno.h>
7482ae8dd1Spgoyette #include <sys/mutex.h>
7582ae8dd1Spgoyette #include <sys/bus.h>
7682ae8dd1Spgoyette
7782ae8dd1Spgoyette #include <dev/pci/pcidevs.h>
7882ae8dd1Spgoyette #include <dev/pci/pcivar.h>
7982ae8dd1Spgoyette #include <dev/pci/pcireg.h>
8082ae8dd1Spgoyette
8182ae8dd1Spgoyette #include "imcsmb_reg.h"
8282ae8dd1Spgoyette #include "imcsmb_var.h"
8382ae8dd1Spgoyette
8482ae8dd1Spgoyette #include "ioconf.h"
8582ae8dd1Spgoyette
8682ae8dd1Spgoyette /* (Sandy,Ivy)bridge-Xeon and (Has,Broad)well-Xeon CPUs contain one or two
8782ae8dd1Spgoyette * "Integrated Memory Controllers" (iMCs), and each iMC contains two separate
8882ae8dd1Spgoyette * SMBus controllers. These are used for reading SPD data from the DIMMs, and
8982ae8dd1Spgoyette * for reading the "Thermal Sensor on DIMM" (TSODs). The iMC SMBus controllers
9082ae8dd1Spgoyette * are very simple devices, and have limited functionality compared to
9182ae8dd1Spgoyette * full-fledged SMBus controllers, like the one in Intel ICHs and PCHs.
9282ae8dd1Spgoyette *
9382ae8dd1Spgoyette * The publicly available documentation for the iMC SMBus controllers can be
9482ae8dd1Spgoyette * found in the CPU datasheets for (Sandy,Ivy)bridge-Xeon and
9582ae8dd1Spgoyette * (Has,broad)well-Xeon, respectively:
9682ae8dd1Spgoyette *
9782ae8dd1Spgoyette * https://www.intel.com/content/dam/www/public/us/en/documents/datasheets/
9882ae8dd1Spgoyette * Sandybridge xeon-e5-1600-2600-vol-2-datasheet.pdf
9982ae8dd1Spgoyette * Ivybridge xeon-e5-v2-datasheet-vol-2.pdf
10082ae8dd1Spgoyette * Haswell xeon-e5-v3-datasheet-vol-2.pdf
10182ae8dd1Spgoyette * Broadwell xeon-e5-v4-datasheet-vol-2.pdf
10282ae8dd1Spgoyette *
10382ae8dd1Spgoyette * Another useful resource is the Linux driver. It is not in the main tree.
10482ae8dd1Spgoyette *
10582ae8dd1Spgoyette * https://www.mail-archive.com/linux-kernel@vger.kernel.org/msg840043.html
10682ae8dd1Spgoyette *
10782ae8dd1Spgoyette * The iMC SMBus controllers do not support interrupts (thus, they must be
10882ae8dd1Spgoyette * polled for IO completion). All of the iMC registers are in PCI configuration
10982ae8dd1Spgoyette * space; there is no support for PIO or MMIO. As a result, this driver does
11082ae8dd1Spgoyette * not need to perform and newbus resource manipulation.
11182ae8dd1Spgoyette *
11282ae8dd1Spgoyette * Because there are multiple SMBus controllers sharing the same PCI device,
11382ae8dd1Spgoyette * this driver is actually *two* drivers:
11482ae8dd1Spgoyette *
11582ae8dd1Spgoyette * - "imcsmb" is an smbus(4)-compliant SMBus controller driver
11682ae8dd1Spgoyette *
11782ae8dd1Spgoyette * - "imcsmb_pci" recognizes the PCI device and assigns the appropriate set of
11882ae8dd1Spgoyette * PCI config registers to a specific "imcsmb" instance.
11982ae8dd1Spgoyette */
12082ae8dd1Spgoyette
12182ae8dd1Spgoyette /* Depending on the motherboard and firmware, the TSODs might be polled by
12282ae8dd1Spgoyette * firmware. Therefore, when this driver accesses these SMBus controllers, the
12382ae8dd1Spgoyette * firmware polling must be disabled as part of requesting the bus, and
12482ae8dd1Spgoyette * re-enabled when releasing the bus. Unfortunately, the details of how to do
12582ae8dd1Spgoyette * this are vendor-specific. Contact your motherboard vendor to get the
12682ae8dd1Spgoyette * information you need to do proper implementations.
12782ae8dd1Spgoyette *
12882ae8dd1Spgoyette * For NVDIMMs which conform to the ACPI "NFIT" standard, the ACPI firmware
12982ae8dd1Spgoyette * manages the NVDIMM; for those which pre-date the standard, the operating
13082ae8dd1Spgoyette * system interacts with the NVDIMM controller using a vendor-proprietary API
13182ae8dd1Spgoyette * over the SMBus. In that case, the NVDIMM driver would be an SMBus slave
13282ae8dd1Spgoyette * device driver, and would interface with the hardware via an SMBus controller
13382ae8dd1Spgoyette * driver such as this one.
13482ae8dd1Spgoyette */
13582ae8dd1Spgoyette
13682ae8dd1Spgoyette /* PCIe device IDs for (Sandy,Ivy)bridge)-Xeon and (Has,Broad)well-Xeon */
13782ae8dd1Spgoyette
13882ae8dd1Spgoyette #define IMCSMB_PCI_DEV_ID_IMC0_SBX 0x3ca8
13982ae8dd1Spgoyette #define IMCSMB_PCI_DEV_ID_IMC0_IBX 0x0ea8
140f36f4b59Smsaitoh #define IMCSMB_PCI_DEV_ID_IMC0_HSX PCI_PRODUCT_INTEL_XE5_V3_IMC0_TATRR
14182ae8dd1Spgoyette #define IMCSMB_PCI_DEV_ID_IMC0_BDX PCI_PRODUCT_INTEL_XEOND_MEM_0_TTR_1
14282ae8dd1Spgoyette
14382ae8dd1Spgoyette /* (Sandy,Ivy)bridge-Xeon only have a single memory controller per socket */
14482ae8dd1Spgoyette
145f36f4b59Smsaitoh #define IMCSMB_PCI_DEV_ID_IMC1_HSX PCI_PRODUCT_INTEL_XE5_V3_IMC1_TATRR
14682ae8dd1Spgoyette #define IMCSMB_PCI_DEV_ID_IMC1_BDX PCI_PRODUCT_INTEL_COREI76K_IMC_0
14782ae8dd1Spgoyette
14882ae8dd1Spgoyette /* There are two SMBus controllers in each device. These define the registers
14982ae8dd1Spgoyette * for each of these devices.
15082ae8dd1Spgoyette */
15182ae8dd1Spgoyette static struct imcsmb_reg_set imcsmb_regs[] = {
15282ae8dd1Spgoyette {
15382ae8dd1Spgoyette .smb_stat = IMCSMB_REG_STATUS0,
15482ae8dd1Spgoyette .smb_cmd = IMCSMB_REG_COMMAND0,
15582ae8dd1Spgoyette .smb_cntl = IMCSMB_REG_CONTROL0
15682ae8dd1Spgoyette },
15782ae8dd1Spgoyette {
15882ae8dd1Spgoyette .smb_stat = IMCSMB_REG_STATUS1,
15982ae8dd1Spgoyette .smb_cmd = IMCSMB_REG_COMMAND1,
16082ae8dd1Spgoyette .smb_cntl = IMCSMB_REG_CONTROL1
16182ae8dd1Spgoyette },
16282ae8dd1Spgoyette };
16382ae8dd1Spgoyette
16482ae8dd1Spgoyette static struct imcsmb_pci_device {
16582ae8dd1Spgoyette uint16_t id;
16682ae8dd1Spgoyette const char *name;
16782ae8dd1Spgoyette } imcsmb_pci_devices[] = {
16882ae8dd1Spgoyette {IMCSMB_PCI_DEV_ID_IMC0_SBX,
16982ae8dd1Spgoyette "Intel Sandybridge Xeon iMC 0 SMBus controllers" },
17082ae8dd1Spgoyette {IMCSMB_PCI_DEV_ID_IMC0_IBX,
17182ae8dd1Spgoyette "Intel Ivybridge Xeon iMC 0 SMBus controllers" },
17282ae8dd1Spgoyette {IMCSMB_PCI_DEV_ID_IMC0_HSX,
17382ae8dd1Spgoyette "Intel Haswell Xeon iMC 0 SMBus controllers" },
17482ae8dd1Spgoyette {IMCSMB_PCI_DEV_ID_IMC1_HSX,
17582ae8dd1Spgoyette "Intel Haswell Xeon iMC 1 SMBus controllers" },
17682ae8dd1Spgoyette {IMCSMB_PCI_DEV_ID_IMC0_BDX,
17782ae8dd1Spgoyette "Intel Broadwell Xeon iMC 0 SMBus controllers" },
17882ae8dd1Spgoyette {IMCSMB_PCI_DEV_ID_IMC1_BDX,
17982ae8dd1Spgoyette "Intel Broadwell Xeon iMC 1 SMBus controllers" },
18082ae8dd1Spgoyette {0, NULL},
18182ae8dd1Spgoyette };
18282ae8dd1Spgoyette
18382ae8dd1Spgoyette /* Device methods. */
18482ae8dd1Spgoyette static void imc_attach(device_t, device_t, void *);
18582ae8dd1Spgoyette static int imc_rescan(device_t, const char *, const int *);
18682ae8dd1Spgoyette static int imc_detach(device_t, int);
18782ae8dd1Spgoyette static int imc_probe(device_t, cfdata_t, void *);
18882ae8dd1Spgoyette static void imc_chdet(device_t, device_t);
18982ae8dd1Spgoyette
19082ae8dd1Spgoyette CFATTACH_DECL3_NEW(imc, sizeof(struct imc_softc),
19182ae8dd1Spgoyette imc_probe, imc_attach, imc_detach, NULL, imc_rescan, imc_chdet, 0);
19282ae8dd1Spgoyette
19382ae8dd1Spgoyette /**
19482ae8dd1Spgoyette * device_attach() method. Set up the PCI device's softc, then explicitly create
19582ae8dd1Spgoyette * children for the actual imcsmbX controllers. Set up the child's ivars to
19682ae8dd1Spgoyette * point to the proper set of the PCI device's config registers. Finally, probe
19782ae8dd1Spgoyette * and attach anything which might be downstream.
19882ae8dd1Spgoyette *
19982ae8dd1Spgoyette * @author Joe Kloss, rpokala
20082ae8dd1Spgoyette *
20182ae8dd1Spgoyette * @param[in,out] dev
20282ae8dd1Spgoyette * Device being attached.
20382ae8dd1Spgoyette */
20482ae8dd1Spgoyette static void
imc_attach(device_t parent,device_t self,void * aux)20582ae8dd1Spgoyette imc_attach(device_t parent, device_t self, void *aux)
20682ae8dd1Spgoyette {
20782ae8dd1Spgoyette struct imc_softc *sc = device_private(self);
20882ae8dd1Spgoyette struct pci_attach_args *pa = aux;
2092685996bSthorpej int i;
21082ae8dd1Spgoyette
21182ae8dd1Spgoyette sc->sc_dev = self;
21282ae8dd1Spgoyette sc->sc_pci_tag = pa->pa_tag;
21382ae8dd1Spgoyette sc->sc_pci_chipset_tag = pa->pa_pc;
21482ae8dd1Spgoyette
21582ae8dd1Spgoyette pci_aprint_devinfo(pa, NULL);
21682ae8dd1Spgoyette
21782ae8dd1Spgoyette for (i = 0; imcsmb_pci_devices[i].id != 0; i++) {
21882ae8dd1Spgoyette if (PCI_PRODUCT(pa->pa_id) == imcsmb_pci_devices[i].id) {
21982ae8dd1Spgoyette aprint_normal_dev(self, "%s\n",
22082ae8dd1Spgoyette imcsmb_pci_devices[i].name);
22182ae8dd1Spgoyette break;
22282ae8dd1Spgoyette }
22382ae8dd1Spgoyette }
22482ae8dd1Spgoyette
22582ae8dd1Spgoyette if (!pmf_device_register(self, NULL, NULL))
22682ae8dd1Spgoyette aprint_error_dev(self, "couldn't establish power handler\n");
22782ae8dd1Spgoyette
2282685996bSthorpej imc_rescan(self, NULL, NULL);
22982ae8dd1Spgoyette }
23082ae8dd1Spgoyette
23182ae8dd1Spgoyette /* Create the imcsmbX children */
23282ae8dd1Spgoyette
23382ae8dd1Spgoyette static int
imc_rescan(device_t self,const char * ifattr,const int * locs)2342685996bSthorpej imc_rescan(device_t self, const char *ifattr, const int *locs)
23582ae8dd1Spgoyette {
23682ae8dd1Spgoyette struct imc_softc *sc = device_private(self);
23782ae8dd1Spgoyette struct imc_attach_args imca;
23882ae8dd1Spgoyette int unit;
23982ae8dd1Spgoyette
24082ae8dd1Spgoyette for (unit = 0; unit < 2; unit++) {
24182ae8dd1Spgoyette if (sc->sc_smbchild[unit] != NULL)
24282ae8dd1Spgoyette continue;
24382ae8dd1Spgoyette
24482ae8dd1Spgoyette imca.ia_unit = unit;
24582ae8dd1Spgoyette imca.ia_regs = &imcsmb_regs[unit];
24682ae8dd1Spgoyette imca.ia_pci_tag = sc->sc_pci_tag;
24782ae8dd1Spgoyette imca.ia_pci_chipset_tag = sc->sc_pci_chipset_tag;
2482685996bSthorpej sc->sc_smbchild[unit] =
249c7fb772bSthorpej config_found(self, &imca, NULL, CFARGS_NONE);
25082ae8dd1Spgoyette }
25182ae8dd1Spgoyette
25282ae8dd1Spgoyette return 0;
25382ae8dd1Spgoyette }
25482ae8dd1Spgoyette
25582ae8dd1Spgoyette /*
25682ae8dd1Spgoyette * device_detach() method. attach() didn't do any allocations, so there's
25782ae8dd1Spgoyette * nothing special needed
25882ae8dd1Spgoyette */
25982ae8dd1Spgoyette static int
imc_detach(device_t self,int flags)26082ae8dd1Spgoyette imc_detach(device_t self, int flags)
26182ae8dd1Spgoyette {
262*b63b081bSriastradh int error;
26382ae8dd1Spgoyette
264*b63b081bSriastradh error = config_detach_children(self, flags);
26582ae8dd1Spgoyette if (error)
26682ae8dd1Spgoyette return error;
26782ae8dd1Spgoyette
26882ae8dd1Spgoyette pmf_device_deregister(self);
26982ae8dd1Spgoyette return 0;
27082ae8dd1Spgoyette }
27182ae8dd1Spgoyette
27282ae8dd1Spgoyette /**
27382ae8dd1Spgoyette * device_probe() method. Look for the right PCI vendor/device IDs.
27482ae8dd1Spgoyette *
27582ae8dd1Spgoyette * @author Joe Kloss, rpokala
27682ae8dd1Spgoyette *
27782ae8dd1Spgoyette * @param[in,out] dev
27882ae8dd1Spgoyette * Device being probed.
27982ae8dd1Spgoyette */
28082ae8dd1Spgoyette static int
imc_probe(device_t dev,cfdata_t cf,void * aux)28182ae8dd1Spgoyette imc_probe(device_t dev, cfdata_t cf, void *aux)
28282ae8dd1Spgoyette {
28382ae8dd1Spgoyette struct pci_attach_args *pa = aux;
28482ae8dd1Spgoyette
28582ae8dd1Spgoyette if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_INTEL) {
28682ae8dd1Spgoyette switch(PCI_PRODUCT(pa->pa_id)) {
28782ae8dd1Spgoyette case PCI_PRODUCT_INTEL_COREI76K_IMC_0:
28882ae8dd1Spgoyette case PCI_PRODUCT_INTEL_XEOND_MEM_0_TTR_1:
289f36f4b59Smsaitoh case PCI_PRODUCT_INTEL_XE5_V3_IMC0_TATRR:
290f36f4b59Smsaitoh case PCI_PRODUCT_INTEL_XE5_V3_IMC1_TATRR:
29182ae8dd1Spgoyette case PCI_PRODUCT_INTEL_E5_IMC_TA:
29282ae8dd1Spgoyette case PCI_PRODUCT_INTEL_E5V2_IMC_TA:
29382ae8dd1Spgoyette return 1;
29482ae8dd1Spgoyette }
29582ae8dd1Spgoyette }
29682ae8dd1Spgoyette return 0;
29782ae8dd1Spgoyette }
29882ae8dd1Spgoyette
29982ae8dd1Spgoyette /*
30082ae8dd1Spgoyette * child_detach() method
30182ae8dd1Spgoyette */
30282ae8dd1Spgoyette static void
imc_chdet(device_t self,device_t child)30382ae8dd1Spgoyette imc_chdet(device_t self, device_t child)
30482ae8dd1Spgoyette {
30582ae8dd1Spgoyette struct imc_softc *sc = device_private(self);
30682ae8dd1Spgoyette int unit;
30782ae8dd1Spgoyette
30882ae8dd1Spgoyette for (unit = 0; unit < 2; unit++)
30982ae8dd1Spgoyette if (sc->sc_smbchild[unit] == child)
31082ae8dd1Spgoyette sc->sc_smbchild[unit] = NULL;
31182ae8dd1Spgoyette return;
31282ae8dd1Spgoyette }
31382ae8dd1Spgoyette
31482ae8dd1Spgoyette /*
31582ae8dd1Spgoyette * bios/motherboard access control
31682ae8dd1Spgoyette *
31782ae8dd1Spgoyette * XXX
31882ae8dd1Spgoyette * If necessary, add the code here to prevent concurrent access to the
31982ae8dd1Spgoyette * IMC controllers. The softc argument is for the imcsmb child device
32082ae8dd1Spgoyette * (for the specific i2cbus instance); if you need to disable all
32182ae8dd1Spgoyette * i2cbus instances on a given IMC (or all instances on all IMCs), you
32282ae8dd1Spgoyette * may need to examine the softc's parent.
32382ae8dd1Spgoyette * XXX
32482ae8dd1Spgoyette */
32582ae8dd1Spgoyette
32682ae8dd1Spgoyette kmutex_t imc_access_mutex;
32782ae8dd1Spgoyette static int imc_access_count = 0;
32882ae8dd1Spgoyette
32982ae8dd1Spgoyette void
imc_callback(struct imcsmb_softc * sc,imc_bios_control action)33082ae8dd1Spgoyette imc_callback(struct imcsmb_softc *sc, imc_bios_control action)
33182ae8dd1Spgoyette {
33282ae8dd1Spgoyette
33382ae8dd1Spgoyette mutex_enter(&imc_access_mutex);
33482ae8dd1Spgoyette switch (action) {
33582ae8dd1Spgoyette case IMC_BIOS_ENABLE:
33682ae8dd1Spgoyette imc_access_count--;
33782ae8dd1Spgoyette if (imc_access_count == 0) {
33882ae8dd1Spgoyette /*
33982ae8dd1Spgoyette * Insert motherboard-specific enable code here!
34082ae8dd1Spgoyette */
34182ae8dd1Spgoyette }
34282ae8dd1Spgoyette break;
34382ae8dd1Spgoyette case IMC_BIOS_DISABLE:
34482ae8dd1Spgoyette if (imc_access_count == 0) {
34582ae8dd1Spgoyette /*
34682ae8dd1Spgoyette * Insert motherboard-specific disable code here!
34782ae8dd1Spgoyette */
34882ae8dd1Spgoyette }
34982ae8dd1Spgoyette imc_access_count++;
35082ae8dd1Spgoyette break;
35182ae8dd1Spgoyette }
35282ae8dd1Spgoyette mutex_exit(&imc_access_mutex);
35382ae8dd1Spgoyette }
35482ae8dd1Spgoyette
35582ae8dd1Spgoyette MODULE(MODULE_CLASS_DRIVER, imc, "pci");
35682ae8dd1Spgoyette
35782ae8dd1Spgoyette #ifdef _MODULE
35882ae8dd1Spgoyette #include "ioconf.c"
35982ae8dd1Spgoyette #endif
36082ae8dd1Spgoyette
36182ae8dd1Spgoyette static int
imc_modcmd(modcmd_t cmd,void * opaque)36282ae8dd1Spgoyette imc_modcmd(modcmd_t cmd, void *opaque)
36382ae8dd1Spgoyette {
36482ae8dd1Spgoyette int error = 0;
36582ae8dd1Spgoyette
36682ae8dd1Spgoyette switch (cmd) {
36782ae8dd1Spgoyette case MODULE_CMD_INIT:
36882ae8dd1Spgoyette mutex_init(&imc_access_mutex, MUTEX_DEFAULT, IPL_NONE);
36982ae8dd1Spgoyette #ifdef _MODULE
37082ae8dd1Spgoyette error = config_init_component(cfdriver_ioconf_imc,
37182ae8dd1Spgoyette cfattach_ioconf_imc, cfdata_ioconf_imc);
37282ae8dd1Spgoyette #endif
37382ae8dd1Spgoyette break;
37482ae8dd1Spgoyette
37582ae8dd1Spgoyette case MODULE_CMD_FINI:
37682ae8dd1Spgoyette #ifdef _MODULE
37782ae8dd1Spgoyette error = config_fini_component(cfdriver_ioconf_imc,
37882ae8dd1Spgoyette cfattach_ioconf_imc, cfdata_ioconf_imc);
37982ae8dd1Spgoyette #endif
38082ae8dd1Spgoyette if (error == 0)
38182ae8dd1Spgoyette mutex_destroy(&imc_access_mutex);
38282ae8dd1Spgoyette break;
38382ae8dd1Spgoyette default:
38482ae8dd1Spgoyette error = ENOTTY;
38582ae8dd1Spgoyette break;
38682ae8dd1Spgoyette }
38782ae8dd1Spgoyette
38882ae8dd1Spgoyette return error;
38982ae8dd1Spgoyette }
390