Module Name: src Committed By: pgoyette Date: Thu Mar 1 04:45:06 UTC 2018
Modified Files: src/sys/arch/x86/conf: files.x86 Added Files: src/sys/arch/x86/pci/imcsmb: imc.c imcsmb.c imcsmb_reg.h imcsmb_var.h Log Message: Move the imc(4) and imcsmb(4) sources into architecture-specific directory (for previous CVS history see the sys/dev/pci/imcsmb/ Attic) To generate a diff of this commit: cvs rdiff -u -r1.95 -r1.96 src/sys/arch/x86/conf/files.x86 cvs rdiff -u -r0 -r1.1 src/sys/arch/x86/pci/imcsmb/imc.c \ src/sys/arch/x86/pci/imcsmb/imcsmb.c \ src/sys/arch/x86/pci/imcsmb/imcsmb_reg.h \ src/sys/arch/x86/pci/imcsmb/imcsmb_var.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/arch/x86/conf/files.x86 diff -u src/sys/arch/x86/conf/files.x86:1.95 src/sys/arch/x86/conf/files.x86:1.96 --- src/sys/arch/x86/conf/files.x86:1.95 Thu Mar 1 04:35:04 2018 +++ src/sys/arch/x86/conf/files.x86 Thu Mar 1 04:45:05 2018 @@ -1,4 +1,4 @@ -# $NetBSD: files.x86,v 1.95 2018/03/01 04:35:04 pgoyette Exp $ +# $NetBSD: files.x86,v 1.96 2018/03/01 04:45:05 pgoyette Exp $ # options for MP configuration through the MP spec defflag opt_mpbios.h MPBIOS MPVERBOSE MPDEBUG MPBIOS_SCANPCI @@ -167,8 +167,8 @@ file arch/x86/pci/pci_addr_fixup.c pci_a device imc {}: imcsmb attach imc at pci -file dev/imcsmb/imc.c imc +file arch/x86/pci/imcsmb/imc.c imc device imcsmb: i2cbus attach imcsmb at imc -file dev/imcsmb/imcsmb.c imcsmb +file arch/x86/pci/imcsmb/imcsmb.c imcsmb Added files: Index: src/sys/arch/x86/pci/imcsmb/imc.c diff -u /dev/null src/sys/arch/x86/pci/imcsmb/imc.c:1.1 --- /dev/null Thu Mar 1 04:45:06 2018 +++ src/sys/arch/x86/pci/imcsmb/imc.c Thu Mar 1 04:45:06 2018 @@ -0,0 +1,404 @@ +/* $NetBSD: imc.c,v 1.1 2018/03/01 04:45:06 pgoyette Exp $ */ + +/*- + * Copyright (c) 2018 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Paul Goyette + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Authors: Joe Kloss; Ravi Pokala (rpok...@freebsd.org) + * + * Copyright (c) 2017-2018 Panasas + * 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. + */ + +/* + * Driver to expose the SMBus controllers in Intel's Integrated + * Memory Controllers in certain CPUs. + */ + +#include <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: imc.c,v 1.1 2018/03/01 04:45:06 pgoyette Exp $"); + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/errno.h> +#include <sys/mutex.h> +#include <sys/bus.h> + +#include <dev/pci/pcidevs.h> +#include <dev/pci/pcivar.h> +#include <dev/pci/pcireg.h> + +#include "imcsmb_reg.h" +#include "imcsmb_var.h" + +#include "ioconf.h" + +/* (Sandy,Ivy)bridge-Xeon and (Has,Broad)well-Xeon CPUs contain one or two + * "Integrated Memory Controllers" (iMCs), and each iMC contains two separate + * SMBus controllers. These are used for reading SPD data from the DIMMs, and + * for reading the "Thermal Sensor on DIMM" (TSODs). The iMC SMBus controllers + * are very simple devices, and have limited functionality compared to + * full-fledged SMBus controllers, like the one in Intel ICHs and PCHs. + * + * The publicly available documentation for the iMC SMBus controllers can be + * found in the CPU datasheets for (Sandy,Ivy)bridge-Xeon and + * (Has,broad)well-Xeon, respectively: + * + * https://www.intel.com/content/dam/www/public/us/en/documents/datasheets/ + * Sandybridge xeon-e5-1600-2600-vol-2-datasheet.pdf + * Ivybridge xeon-e5-v2-datasheet-vol-2.pdf + * Haswell xeon-e5-v3-datasheet-vol-2.pdf + * Broadwell xeon-e5-v4-datasheet-vol-2.pdf + * + * Another useful resource is the Linux driver. It is not in the main tree. + * + * https://www.mail-archive.com/linux-kernel@vger.kernel.org/msg840043.html + * + * The iMC SMBus controllers do not support interrupts (thus, they must be + * polled for IO completion). All of the iMC registers are in PCI configuration + * space; there is no support for PIO or MMIO. As a result, this driver does + * not need to perform and newbus resource manipulation. + * + * Because there are multiple SMBus controllers sharing the same PCI device, + * this driver is actually *two* drivers: + * + * - "imcsmb" is an smbus(4)-compliant SMBus controller driver + * + * - "imcsmb_pci" recognizes the PCI device and assigns the appropriate set of + * PCI config registers to a specific "imcsmb" instance. + */ + +/* Depending on the motherboard and firmware, the TSODs might be polled by + * firmware. Therefore, when this driver accesses these SMBus controllers, the + * firmware polling must be disabled as part of requesting the bus, and + * re-enabled when releasing the bus. Unfortunately, the details of how to do + * this are vendor-specific. Contact your motherboard vendor to get the + * information you need to do proper implementations. + * + * For NVDIMMs which conform to the ACPI "NFIT" standard, the ACPI firmware + * manages the NVDIMM; for those which pre-date the standard, the operating + * system interacts with the NVDIMM controller using a vendor-proprietary API + * over the SMBus. In that case, the NVDIMM driver would be an SMBus slave + * device driver, and would interface with the hardware via an SMBus controller + * driver such as this one. + */ + +/* PCIe device IDs for (Sandy,Ivy)bridge)-Xeon and (Has,Broad)well-Xeon */ + +#define IMCSMB_PCI_DEV_ID_IMC0_SBX 0x3ca8 +#define IMCSMB_PCI_DEV_ID_IMC0_IBX 0x0ea8 +#define IMCSMB_PCI_DEV_ID_IMC0_HSX PCI_PRODUCT_INTEL_XE5_V3_IMC0_MAIN +#define IMCSMB_PCI_DEV_ID_IMC0_BDX PCI_PRODUCT_INTEL_XEOND_MEM_0_TTR_1 + +/* (Sandy,Ivy)bridge-Xeon only have a single memory controller per socket */ + +#define IMCSMB_PCI_DEV_ID_IMC1_HSX PCI_PRODUCT_INTEL_XE5_V3_IMC1_MAIN +#define IMCSMB_PCI_DEV_ID_IMC1_BDX PCI_PRODUCT_INTEL_COREI76K_IMC_0 + +/* There are two SMBus controllers in each device. These define the registers + * for each of these devices. + */ +static struct imcsmb_reg_set imcsmb_regs[] = { + { + .smb_stat = IMCSMB_REG_STATUS0, + .smb_cmd = IMCSMB_REG_COMMAND0, + .smb_cntl = IMCSMB_REG_CONTROL0 + }, + { + .smb_stat = IMCSMB_REG_STATUS1, + .smb_cmd = IMCSMB_REG_COMMAND1, + .smb_cntl = IMCSMB_REG_CONTROL1 + }, +}; + +static struct imcsmb_pci_device { + uint16_t id; + const char *name; +} imcsmb_pci_devices[] = { + {IMCSMB_PCI_DEV_ID_IMC0_SBX, + "Intel Sandybridge Xeon iMC 0 SMBus controllers" }, + {IMCSMB_PCI_DEV_ID_IMC0_IBX, + "Intel Ivybridge Xeon iMC 0 SMBus controllers" }, + {IMCSMB_PCI_DEV_ID_IMC0_HSX, + "Intel Haswell Xeon iMC 0 SMBus controllers" }, + {IMCSMB_PCI_DEV_ID_IMC1_HSX, + "Intel Haswell Xeon iMC 1 SMBus controllers" }, + {IMCSMB_PCI_DEV_ID_IMC0_BDX, + "Intel Broadwell Xeon iMC 0 SMBus controllers" }, + {IMCSMB_PCI_DEV_ID_IMC1_BDX, + "Intel Broadwell Xeon iMC 1 SMBus controllers" }, + {0, NULL}, +}; + +/* Device methods. */ +static void imc_attach(device_t, device_t, void *); +static int imc_rescan(device_t, const char *, const int *); +static int imc_detach(device_t, int); +static int imc_probe(device_t, cfdata_t, void *); +static void imc_chdet(device_t, device_t); + +CFATTACH_DECL3_NEW(imc, sizeof(struct imc_softc), + imc_probe, imc_attach, imc_detach, NULL, imc_rescan, imc_chdet, 0); + +/** + * device_attach() method. Set up the PCI device's softc, then explicitly create + * children for the actual imcsmbX controllers. Set up the child's ivars to + * point to the proper set of the PCI device's config registers. Finally, probe + * and attach anything which might be downstream. + * + * @author Joe Kloss, rpokala + * + * @param[in,out] dev + * Device being attached. + */ +static void +imc_attach(device_t parent, device_t self, void *aux) +{ + struct imc_softc *sc = device_private(self); + struct pci_attach_args *pa = aux; + int flags, i; + + sc->sc_dev = self; + sc->sc_pci_tag = pa->pa_tag; + sc->sc_pci_chipset_tag = pa->pa_pc; + + pci_aprint_devinfo(pa, NULL); + + for (i = 0; imcsmb_pci_devices[i].id != 0; i++) { + if (PCI_PRODUCT(pa->pa_id) == imcsmb_pci_devices[i].id) { + aprint_normal_dev(self, "%s\n", + imcsmb_pci_devices[i].name); + break; + } + } + + flags = 0; + + if (!pmf_device_register(self, NULL, NULL)) + aprint_error_dev(self, "couldn't establish power handler\n"); + + imc_rescan(self, "imc", &flags); +} + +/* Create the imcsmbX children */ + +static int +imc_rescan(device_t self, const char * ifattr, const int *flags) +{ + struct imc_softc *sc = device_private(self); + struct imc_attach_args imca; + device_t child; + int unit; + + for (unit = 0; unit < 2; unit++) { + if (sc->sc_smbchild[unit] != NULL) + continue; + + imca.ia_unit = unit; + imca.ia_regs = &imcsmb_regs[unit]; + imca.ia_pci_tag = sc->sc_pci_tag; + imca.ia_pci_chipset_tag = sc->sc_pci_chipset_tag; + child = config_found_ia(self, "imc", &imca, NULL); + + if (child == NULL) { + aprint_debug_dev(self, "Child %d imcsmb not added\n", + unit); + } + sc->sc_smbchild[unit] = child; + } + + return 0; +} + +/* + * device_detach() method. attach() didn't do any allocations, so there's + * nothing special needed + */ +static int +imc_detach(device_t self, int flags) +{ + struct imc_softc *sc = device_private(self); + int i, error; + + for (i = 0; i < 2; i++) { + if (sc->sc_smbchild[i] != NULL) { + error = config_detach(sc->sc_smbchild[i], flags); + if (error) + return error; + } + } + + pmf_device_deregister(self); + return 0; +} + +/** + * device_probe() method. Look for the right PCI vendor/device IDs. + * + * @author Joe Kloss, rpokala + * + * @param[in,out] dev + * Device being probed. + */ +static int +imc_probe(device_t dev, cfdata_t cf, void *aux) +{ + struct pci_attach_args *pa = aux; + + if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_INTEL) { + switch(PCI_PRODUCT(pa->pa_id)) { + case PCI_PRODUCT_INTEL_COREI76K_IMC_0: + case PCI_PRODUCT_INTEL_XEOND_MEM_0_TTR_1: + case PCI_PRODUCT_INTEL_XE5_V3_IMC0_MAIN: + case PCI_PRODUCT_INTEL_XE5_V3_IMC1_MAIN: + case PCI_PRODUCT_INTEL_E5_IMC_TA: + case PCI_PRODUCT_INTEL_E5V2_IMC_TA: + return 1; + } + } + return 0; +} + +/* + * child_detach() method + */ +static void +imc_chdet(device_t self, device_t child) +{ + struct imc_softc *sc = device_private(self); + int unit; + + for (unit = 0; unit < 2; unit++) + if (sc->sc_smbchild[unit] == child) + sc->sc_smbchild[unit] = NULL; + return; +} + +/* + * bios/motherboard access control + * + * XXX + * If necessary, add the code here to prevent concurrent access to the + * IMC controllers. The softc argument is for the imcsmb child device + * (for the specific i2cbus instance); if you need to disable all + * i2cbus instances on a given IMC (or all instances on all IMCs), you + * may need to examine the softc's parent. + * XXX + */ + +kmutex_t imc_access_mutex; +static int imc_access_count = 0; + +void +imc_callback(struct imcsmb_softc *sc, imc_bios_control action) +{ + + mutex_enter(&imc_access_mutex); + switch (action) { + case IMC_BIOS_ENABLE: + imc_access_count--; + if (imc_access_count == 0) { + /* + * Insert motherboard-specific enable code here! + */ + } + break; + case IMC_BIOS_DISABLE: + if (imc_access_count == 0) { + /* + * Insert motherboard-specific disable code here! + */ + } + imc_access_count++; + break; + } + mutex_exit(&imc_access_mutex); +} + +MODULE(MODULE_CLASS_DRIVER, imc, "pci"); + +#ifdef _MODULE +#include "ioconf.c" +#endif + +static int +imc_modcmd(modcmd_t cmd, void *opaque) +{ + int error = 0; + + switch (cmd) { + case MODULE_CMD_INIT: + mutex_init(&imc_access_mutex, MUTEX_DEFAULT, IPL_NONE); +#ifdef _MODULE + error = config_init_component(cfdriver_ioconf_imc, + cfattach_ioconf_imc, cfdata_ioconf_imc); +#endif + break; + + case MODULE_CMD_FINI: +#ifdef _MODULE + error = config_fini_component(cfdriver_ioconf_imc, + cfattach_ioconf_imc, cfdata_ioconf_imc); +#endif + if (error == 0) + mutex_destroy(&imc_access_mutex); + break; + default: +#ifdef _MODULE + error = ENOTTY; + break; +#endif + } + + return error; +} Index: src/sys/arch/x86/pci/imcsmb/imcsmb.c diff -u /dev/null src/sys/arch/x86/pci/imcsmb/imcsmb.c:1.1 --- /dev/null Thu Mar 1 04:45:06 2018 +++ src/sys/arch/x86/pci/imcsmb/imcsmb.c Thu Mar 1 04:45:06 2018 @@ -0,0 +1,484 @@ +/* $NetBSD: imcsmb.c,v 1.1 2018/03/01 04:45:06 pgoyette Exp $ */ + +/*- + * Copyright (c) 2018 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Paul Goyette + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Authors: Joe Kloss; Ravi Pokala (rpok...@freebsd.org) + * + * Copyright (c) 2017-2018 Panasas + * 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. + */ + +/* + * Driver for the SMBus controllers in Intel's Integrated Memory Controllers + * in certain CPUs. A more detailed description of this device is present + * in imc.c + */ + +#include <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: imcsmb.c,v 1.1 2018/03/01 04:45:06 pgoyette Exp $"); + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/endian.h> +#include <sys/errno.h> +#include <sys/mutex.h> +#include <sys/bus.h> + +#include <dev/pci/pcidevs.h> +#include <dev/pci/pcivar.h> +#include <dev/pci/pcireg.h> + +#include <dev/i2c/i2cvar.h> + +#include "imcsmb_reg.h" +#include "imcsmb_var.h" + +/* Device methods */ +static int imcsmb_probe(device_t, cfdata_t, void *); +static void imcsmb_attach(device_t, device_t, void *); +static int imcsmb_detach(device_t, int flags); +static int imcsmb_rescan(device_t, const char *, const int *); +static void imcsmb_chdet(device_t, device_t); + +CFATTACH_DECL3_NEW(imcsmb, sizeof(struct imcsmb_softc), + imcsmb_probe, imcsmb_attach, imcsmb_detach, NULL, imcsmb_rescan, + imcsmb_chdet, 0); + +/* Bus access control methods */ +static int imcsmb_acquire_bus(void *cookie, int flags); +static void imcsmb_release_bus(void *cookie, int flags); + +/* SMBus methods */ +static int imcsmb_exec(void *cookie, i2c_op_t, i2c_addr_t, const void *, + size_t, void *, size_t, int); + +/** + * device_attach() method. Set up the softc, including getting the set of the + * parent imcsmb_pci's registers that we will use. Create the smbus(4) device, + * which any SMBus slave device drivers will connect to. Probe and attach + * anything which might be downstream. + * + * @author rpokala + * + * @param[in,out] dev + * Device being attached. + */ + +static void +imcsmb_attach(device_t parent, device_t self, void *aux) +{ + struct imcsmb_softc *sc = device_private(self); + struct imc_attach_args *imca = aux; + + aprint_naive("\n"); + aprint_normal("SMBus controller\n"); + + /* Initialize private state */ + sc->sc_dev = self; + sc->sc_regs = imca->ia_regs; + sc->sc_pci_tag = imca->ia_pci_tag; + sc->sc_pci_chipset_tag = imca->ia_pci_chipset_tag; + mutex_init(&sc->sc_i2c_mutex, MUTEX_DEFAULT, IPL_NONE); + + if (!pmf_device_register(self, NULL, NULL)) + aprint_error_dev(self, "couldn't establish power handler\n"); + + imcsmb_rescan(self, "i2cbus", 0); +} + +static int +imcsmb_rescan(device_t self, const char *ifattr, const int *flags) +{ + struct imcsmb_softc *sc = device_private(self); + struct i2cbus_attach_args iba; + + if (!ifattr_match(ifattr, "i2cbus")) + return 0; + + /* Create the i2cbus child */ + if (sc->sc_smbus != NULL) + return 0; + + sc->sc_i2c_tag.ic_cookie = sc; + sc->sc_i2c_tag.ic_acquire_bus = imcsmb_acquire_bus; + sc->sc_i2c_tag.ic_release_bus = imcsmb_release_bus; + sc->sc_i2c_tag.ic_exec = imcsmb_exec; + + memset(&iba, 0, sizeof(iba)); + iba.iba_type = I2C_TYPE_SMBUS; + iba.iba_tag = &sc->sc_i2c_tag; + sc->sc_smbus = config_found_ia(self, ifattr, &iba, iicbus_print); + + if (sc->sc_smbus == NULL) { + aprint_normal_dev(self, "no child found\n"); + return ENXIO; + } + + return 0; +} + +static void +imcsmb_chdet(device_t self, device_t child) +{ + struct imcsmb_softc *sc = device_private(self); + + if (child == sc->sc_smbus) + sc->sc_smbus = NULL; + else KASSERT(child == NULL); +} + +/** + * device_detach() method. attach() didn't do any allocations, so there's + * nothing special needed + */ +static int +imcsmb_detach(device_t self, int flags) +{ + int error; + struct imcsmb_softc *sc = device_private(self); + + if (sc->sc_smbus != NULL) { + error = config_detach(sc->sc_smbus, flags); + if (error) + return error; + } + + pmf_device_deregister(self); + mutex_destroy(&sc->sc_i2c_mutex); + return 0; +} + +/** + * device_probe() method. All the actual probing was done by the imc + * parent, so just report success. + * + * @author Joe Kloss + * + * @param[in,out] dev + * Device being probed. + */ +static int +imcsmb_probe(device_t parent, cfdata_t match, void *aux) +{ + + return 1; +} + +static int +imcsmb_acquire_bus(void *cookie, int flags) +{ + struct imcsmb_softc *sc = cookie; + + if (cold) + return 0; + + mutex_enter(&sc->sc_i2c_mutex); + + imc_callback(sc, IMC_BIOS_DISABLE); + + return 0; +} + +static void +imcsmb_release_bus(void *cookie, int flags) +{ + struct imcsmb_softc *sc = cookie; + + if (cold) + return; + + imc_callback(sc, IMC_BIOS_ENABLE); + + mutex_exit(&sc->sc_i2c_mutex); +} + +static int +imcsmb_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, const void *cmdbuf, + size_t cmdlen, void *buf, size_t len, int flags) +{ + struct imcsmb_softc *sc = cookie; + int i; + int rc = 0; + uint32_t cmd_val; + uint32_t cntl_val; + uint32_t orig_cntl_val; + uint32_t stat_val; + uint16_t *word; + uint16_t lword; + uint8_t *byte; + uint8_t lbyte; + uint8_t cmd; + + if ((cmdlen != 1) || (len > 2) || (len < 1)) + return EINVAL; + + byte = (uint8_t *) buf; + word = (uint16_t *) buf; + lbyte = *byte; + lword = *word; + + /* We modify the value of the control register; save the original, so + * we can restore it later + */ + orig_cntl_val = pci_conf_read(sc->sc_pci_chipset_tag, sc->sc_pci_tag, + sc->sc_regs->smb_cntl); + + cntl_val = orig_cntl_val; + + /* + * Set up the SMBCNTL register + */ + + /* [31:28] Clear the existing value of the DTI bits, then set them to + * the four high bits of the slave address. + */ + cntl_val &= ~IMCSMB_CNTL_DTI_MASK; + cntl_val |= ((uint32_t) addr & 0x78) << 25; + + /* [27:27] Set the CLK_OVERRIDE bit, to enable normal operation */ + cntl_val |= IMCSMB_CNTL_CLK_OVERRIDE; + + /* [26:26] Clear the WRITE_DISABLE bit; the datasheet says this isn't + * necessary, but empirically, it is. + */ + cntl_val &= ~IMCSMB_CNTL_WRITE_DISABLE_BIT; + + /* [9:9] Clear the POLL_EN bit, to stop the hardware TSOD polling. */ + cntl_val &= ~IMCSMB_CNTL_POLL_EN; + + /* + * Set up the SMBCMD register + */ + + /* [31:31] Set the TRIGGER bit; when this gets written, the controller + * will issue the command. + */ + cmd_val = IMCSMB_CMD_TRIGGER_BIT; + + /* [29:29] For word operations, set the WORD_ACCESS bit. */ + if (len == 2) { + cmd_val |= IMCSMB_CMD_WORD_ACCESS; + } + + /* [27:27] For write operations, set the WRITE bit. */ + if (I2C_OP_WRITE_P(op)) { + cmd_val |= IMCSMB_CMD_WRITE_BIT; + } + + /* [26:24] The three non-DTI, non-R/W bits of the slave address. */ + cmd_val |= (uint32_t) ((addr & 0x7) << 24); + + /* [23:16] The command (offset in the case of an EEPROM, or register in + * the case of TSOD or NVDIMM controller). + */ + cmd = *((const uint8_t *) cmdbuf); + cmd_val |= (uint32_t) (cmd << 16); + + /* [15:0] The data to be written for a write operation. */ + if (I2C_OP_WRITE_P(op)) { + if (len == 2) { + /* The datasheet says the controller uses different + * endianness for word operations on I2C vs SMBus! + * I2C: [15:8] = MSB; [7:0] = LSB + * SMB: [15:8] = LSB; [7:0] = MSB + * As a practical matter, this controller is very + * specifically for use with DIMMs, the SPD (and + * NVDIMM controllers) are only accessed as bytes, + * the temperature sensor is only accessed as words, and + * the temperature sensors are I2C. Thus, byte-swap the + * word. + */ + lword = htobe16(*(uint16_t *)buf); + } else { + /* For byte operations, the data goes in the LSB, and + * the MSB is a don't care. + */ + lword = *(uint8_t *)buf; + } + cmd_val |= lword; + } + + /* Write the updated value to the control register first, to disable + * the hardware TSOD polling. + */ + pci_conf_write(sc->sc_pci_chipset_tag, sc->sc_pci_tag, + sc->sc_regs->smb_cntl, cntl_val); + + /* Poll on the BUSY bit in the status register until clear, or timeout. + * We just cleared the auto-poll bit, so we need to make sure the device + * is idle before issuing a command. We can safely timeout after 35 ms, + * as this is the maximum time the SMBus spec allows for a transaction. + */ + for (i = 4; i != 0; i--) { + stat_val = pci_conf_read(sc->sc_pci_chipset_tag, + sc->sc_pci_tag, sc->sc_regs->smb_stat); + if (! (stat_val & IMCSMB_STATUS_BUSY_BIT)) { + break; + } + delay(100); /* wait 10ms */ + } + + if (i == 0) { + aprint_debug_dev(sc->sc_dev, + "transfer: timeout waiting for device to settle\n"); + } + + /* Now that polling has stopped, we can write the command register. This + * starts the SMBus command. + */ + pci_conf_write(sc->sc_pci_chipset_tag, sc->sc_pci_tag, + sc->sc_regs->smb_cmd, cmd_val); + + /* Wait for WRITE_DATA_DONE/READ_DATA_VALID to be set, or timeout and + * fail. We wait up to 35ms. + */ + for (i = 35000; i != 0; i -= 10) + { + delay(10); + stat_val = pci_conf_read(sc->sc_pci_chipset_tag, + sc->sc_pci_tag, sc->sc_regs->smb_stat); + /* + * For a write, the bits holding the data contain the data + * being written. You would think that would cause the + * READ_DATA_VALID bit to be cleared, because the data bits + * no longer contain valid data from the most recent read + * operation. While that would be logical, that's not the + * case here: READ_DATA_VALID is only cleared when starting + * a read operation, and WRITE_DATA_DONE is only cleared + * when starting a write operation. + */ + if (I2C_OP_WRITE_P(op)) { + if (stat_val & IMCSMB_STATUS_WRITE_DATA_DONE) { + break; + } + } else { + if (stat_val & IMCSMB_STATUS_READ_DATA_VALID) { + break; + } + } + } + if (i == 0) { + rc = ETIMEDOUT; + aprint_debug_dev(sc->sc_dev, "transfer timeout\n"); + goto out; + } + + /* It is generally the case that this bit indicates non-ACK, but it + * could also indicate other bus errors. There's no way to tell the + * difference. + */ + if (stat_val & IMCSMB_STATUS_BUS_ERROR_BIT) { + /* While it is not documented, empirically, SPD page-change + * commands (writes with DTI = 0x30) always complete with the + * error bit set. So, ignore it in those cases. + */ + if ((addr & 0x78) != 0x30) { + rc = ENODEV; + goto out; + } + } + + /* For a read operation, copy the data out */ + if (I2C_OP_READ_P(op)) { + if (len == 2) { + /* The data is returned in bits [15:0]; as discussed + * above, byte-swap. + */ + lword = (uint16_t) (stat_val & 0xffff); + lword = htobe16(lword); + *(uint16_t *)buf = lword; + } else { + /* The data is returned in bits [7:0] */ + lbyte = (uint8_t) (stat_val & 0xff); + *(uint8_t *)buf = lbyte; + } + } + +out: + /* Restore the original value of the control register. */ + pci_conf_write(sc->sc_pci_chipset_tag, sc->sc_pci_tag, + sc->sc_regs->smb_cntl, orig_cntl_val); + return rc; +}; + +MODULE(MODULE_CLASS_DRIVER, imcsmb, "imc,iic"); + +#ifdef _MODULE +#include "ioconf.c" +#endif + +static int +imcsmb_modcmd(modcmd_t cmd, void *opaque) +{ + int error = 0; + +#ifdef _MODULE + switch (cmd) { + case MODULE_CMD_INIT: + error = config_init_component(cfdriver_ioconf_imcsmb, + cfattach_ioconf_imcsmb, cfdata_ioconf_imcsmb); + break; + case MODULE_CMD_FINI: + error = config_fini_component(cfdriver_ioconf_imcsmb, + cfattach_ioconf_imcsmb, cfdata_ioconf_imcsmb); + break; + default: + error = ENOTTY; + break; + } +#endif + + return error; +} Index: src/sys/arch/x86/pci/imcsmb/imcsmb_reg.h diff -u /dev/null src/sys/arch/x86/pci/imcsmb/imcsmb_reg.h:1.1 --- /dev/null Thu Mar 1 04:45:06 2018 +++ src/sys/arch/x86/pci/imcsmb/imcsmb_reg.h Thu Mar 1 04:45:06 2018 @@ -0,0 +1,94 @@ +/* $NetBSD: imcsmb_reg.h,v 1.1 2018/03/01 04:45:06 pgoyette Exp $ */ + +/*- + * Copyright (c) 2018 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Paul Goyette + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Authors: Joe Kloss; Ravi Pokala (rpok...@freebsd.org) + * + * Copyright (c) 2017-2018 Panasas + * 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. + */ + +#ifndef _DEV__IMCSMB__IMCSMB_REG_H_ +#define _DEV__IMCSMB__IMCSMB_REG_H_ + +/* Intel (Sandy,Ivy)bridge and (Has,Broad)well CPUs have integrated memory + * controllers (iMCs), each of which having up to two SMBus controllers. They + * are programmed via sets of registers in the same PCI device, which are + * identical other than the register numbers. + * + * The full documentation for these registers can be found in volume two of the + * datasheets for the CPUs. Refer to the links in imcsmb_pci.c + */ + +#define IMCSMB_REG_STATUS0 0x0180 +#define IMCSMB_REG_STATUS1 0x0190 +#define IMCSMB_STATUS_BUSY_BIT 0x10000000 +#define IMCSMB_STATUS_BUS_ERROR_BIT 0x20000000 +#define IMCSMB_STATUS_WRITE_DATA_DONE 0x40000000 +#define IMCSMB_STATUS_READ_DATA_VALID 0x80000000 + +#define IMCSMB_REG_COMMAND0 0x0184 +#define IMCSMB_REG_COMMAND1 0x0194 +#define IMCSMB_CMD_WORD_ACCESS 0x20000000 +#define IMCSMB_CMD_WRITE_BIT 0x08000000 +#define IMCSMB_CMD_TRIGGER_BIT 0x80000000 + +#define IMCSMB_REG_CONTROL0 0x0188 +#define IMCSMB_REG_CONTROL1 0x0198 +#define IMCSMB_CNTL_POLL_EN 0x00000100 +#define IMCSMB_CNTL_CLK_OVERRIDE 0x08000000 +#define IMCSMB_CNTL_DTI_MASK 0xf0000000 +#define IMCSMB_CNTL_WRITE_DISABLE_BIT 0x04000000 + +#endif /* _DEV__IMCSMB__IMCSMB_REG_H_ */ Index: src/sys/arch/x86/pci/imcsmb/imcsmb_var.h diff -u /dev/null src/sys/arch/x86/pci/imcsmb/imcsmb_var.h:1.1 --- /dev/null Thu Mar 1 04:45:06 2018 +++ src/sys/arch/x86/pci/imcsmb/imcsmb_var.h Thu Mar 1 04:45:06 2018 @@ -0,0 +1,145 @@ +/* $NetBSD: imcsmb_var.h,v 1.1 2018/03/01 04:45:06 pgoyette Exp $ */ + +/*- + * Copyright (c) 2018 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Paul Goyette + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Authors: Joe Kloss; Ravi Pokala (rpok...@freebsd.org) + * + * Copyright (c) 2017-2018 Panasas + * 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. + */ + +#ifndef _DEV__IMCSMB__IMCSMB_VAR_H_ +#define _DEV__IMCSMB__IMCSMB_VAR_H_ + +#include <sys/param.h> +#include <sys/mutex.h> +#include <sys/bus.h> + +#include <dev/i2c/i2cvar.h> + +#include <dev/pci/pcivar.h> +/* #include <dev/pci/pcireg.h> PRG */ + +/* A detailed description of this device is present in imcsmb_pci.c */ + +/** + * The softc for a particular instance of the PCI device associated with a pair + * of iMC-SMB controllers. + * + * Ordinarily, locking would be done with a mutex. However, we might have an + * NVDIMM connected to this SMBus, and we might need to issue the SAVE command + * to the NVDIMM from a panic context. Mutex operations are not allowed while + * the scheduler is stopped, so just use a simple semaphore. + * + * If, as described in the manpage, additional steps are needed to stop/restart + * firmware operations before/after using the controller, then additional fields + * can be added to this softc. + */ +struct imc_softc { + device_t sc_dev; + device_t sc_smbchild[2]; + pcitag_t sc_pci_tag; /* pci config space info */ + pci_chipset_tag_t sc_pci_chipset_tag; +}; + +void imcsmb_pci_release_bus(device_t dev); +int imcsmb_pci_request_bus(device_t dev); + +/** + * PCI config registers for each individual SMBus controller within the iMC. + * Each iMC-SMB has a separate set of registers. There is an array of these + * structures for the PCI device, and one of them is passed to driver for the + * actual iMC-SMB as the IVAR. + */ +struct imcsmb_reg_set { + uint16_t smb_stat; + uint16_t smb_cmd; + uint16_t smb_cntl; +}; + +/** + * The softc for the device associated with a particular iMC-SMB controller. + * There are two such controllers for each of the PCI devices. The PCI driver + * tells the iMC-SMB driver which set of registers to use via the IVAR. This + * technique was suggested by John Baldwin (jhb@). + */ +struct imcsmb_softc { + device_t sc_dev; + device_t sc_smbus; /* child smbusX interface */ + struct imcsmb_reg_set *sc_regs; /* regs for this controller */ + struct i2c_controller sc_i2c_tag; /* i2c tag info */ + pcitag_t sc_pci_tag; /* pci config space info */ + pci_chipset_tag_t sc_pci_chipset_tag; + kmutex_t sc_i2c_mutex; +}; + +struct imc_attach_args { + int ia_unit; + struct imcsmb_reg_set *ia_regs; + pci_chipset_tag_t ia_pci_pc; + pcitag_t ia_pci_tag; + pci_chipset_tag_t ia_pci_chipset_tag; + +}; + +/* Interface for enable/disable BIOS or other motherboard access to IMC */ + +typedef enum { + IMC_BIOS_ENABLE, + IMC_BIOS_DISABLE +} imc_bios_control ; + +void imc_callback(struct imcsmb_softc *, imc_bios_control); + +#endif /* _DEV__IMCSMB__IMCSMB_VAR_H_ */