1 /* 2 * Copyright (c) 2014 The DragonFly Project. All rights reserved. 3 * 4 * This code is derived from software contributed to The DragonFly Project 5 * by Matthew Dillon <dillon@backplane.com> 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * 3. Neither the name of The DragonFly Project nor the names of its 18 * contributors may be used to endorse or promote products derived 19 * from this software without specific, prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 /* 35 * Intel 4th generation mobile cpus integrated I2C device, smbus driver. 36 * 37 * See ig4_reg.h for datasheet reference and notes. 38 */ 39 40 #include <sys/param.h> 41 #include <sys/systm.h> 42 #include <sys/kernel.h> 43 #include <sys/module.h> 44 #include <sys/errno.h> 45 #include <sys/serialize.h> 46 #include <sys/syslog.h> 47 #include <sys/bus.h> 48 49 #include <sys/rman.h> 50 51 #include <bus/pci/pcivar.h> 52 #include <bus/pci/pcireg.h> 53 #include <bus/smbus/smbconf.h> 54 55 #include "smbus_if.h" 56 57 #include "ig4_reg.h" 58 #include "ig4_var.h" 59 60 static int ig4iic_pci_detach(device_t dev); 61 62 static const struct { 63 uint16_t id; 64 char *name; 65 enum ig4_vers version; 66 } intel_i2c_ids[] = { 67 /* Haswell */ 68 { 0x9c61, "Intel Lynx Point-LP I2C Controller-1", IG4_HASWELL }, 69 { 0x9c62, "Intel Lynx Point-LP I2C Controller-2", IG4_HASWELL }, 70 71 /* Skylake-U/Y and Kaby Lake-U/Y CPUs */ 72 { 0x9d60, "Intel Sunrise Point-LP I2C Controller-0", IG4_SKYLAKE }, 73 { 0x9d61, "Intel Sunrise Point-LP I2C Controller-1", IG4_SKYLAKE }, 74 { 0x9d62, "Intel Sunrise Point-LP I2C Controller-2", IG4_SKYLAKE }, 75 { 0x9d63, "Intel Sunrise Point-LP I2C Controller-3", IG4_SKYLAKE }, 76 { 0x9d64, "Intel Sunrise Point-LP I2C Controller-4", IG4_SKYLAKE }, 77 { 0x9d65, "Intel Sunrise Point-LP I2C Controller-5", IG4_SKYLAKE }, 78 }; 79 80 static 81 int 82 ig4iic_pci_probe(device_t dev) 83 { 84 int i; 85 uint16_t device_id; 86 const char *name = NULL; 87 88 if (pci_get_vendor(dev) != PCI_VENDOR_INTEL) 89 return (ENXIO); 90 91 device_id = pci_get_device(dev); 92 for (i = 0; i < NELEM(intel_i2c_ids); i++) { 93 if (intel_i2c_ids[i].id == device_id) { 94 name = intel_i2c_ids[i].name; 95 break; 96 } 97 } 98 if (name != NULL) { 99 device_set_desc(dev, name); 100 } else { 101 return (ENXIO); 102 } 103 104 return BUS_PROBE_DEFAULT; 105 } 106 107 static 108 int 109 ig4iic_pci_attach(device_t dev) 110 { 111 ig4iic_softc_t *sc = device_get_softc(dev); 112 u_int irq_flags; 113 uint16_t device_id; 114 int msi_enable = 1; 115 int error, i; 116 117 bzero(sc, sizeof(*sc)); 118 119 lwkt_serialize_init(&sc->slz); 120 121 device_id = pci_get_device(dev); 122 for (i = 0; i < NELEM(intel_i2c_ids); i++) { 123 if (intel_i2c_ids[i].id == device_id) { 124 sc->version = intel_i2c_ids[i].version; 125 break; 126 } 127 } 128 129 sc->dev = dev; 130 sc->regs_rid = PCIR_BAR(0); 131 sc->regs_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, 132 &sc->regs_rid, RF_ACTIVE); 133 if (sc->regs_res == NULL) { 134 device_printf(dev, "unable to map registers"); 135 ig4iic_pci_detach(dev); 136 return (ENXIO); 137 } 138 sc->intr_type = pci_alloc_1intr(dev, msi_enable, 139 &sc->intr_rid, &irq_flags); 140 sc->intr_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, 141 &sc->intr_rid, irq_flags); 142 if (sc->intr_res == NULL) { 143 device_printf(dev, "unable to map interrupt"); 144 ig4iic_pci_detach(dev); 145 return (ENXIO); 146 } 147 sc->regs_t = rman_get_bustag(sc->regs_res); 148 sc->regs_h = rman_get_bushandle(sc->regs_res); 149 sc->pci_attached = 1; 150 151 error = ig4iic_attach(sc); 152 if (error) 153 ig4iic_pci_detach(dev); 154 155 return error; 156 } 157 158 static 159 int 160 ig4iic_pci_detach(device_t dev) 161 { 162 ig4iic_softc_t *sc = device_get_softc(dev); 163 int error; 164 165 if (sc->pci_attached) { 166 error = ig4iic_detach(sc); 167 if (error) 168 return error; 169 sc->pci_attached = 0; 170 } 171 172 if (sc->intr_res) { 173 bus_release_resource(dev, SYS_RES_IRQ, 174 sc->intr_rid, sc->intr_res); 175 sc->intr_res = NULL; 176 } 177 if (sc->intr_type == PCI_INTR_TYPE_MSI) 178 pci_release_msi(dev); 179 if (sc->regs_res) { 180 bus_release_resource(dev, SYS_RES_MEMORY, 181 sc->regs_rid, sc->regs_res); 182 sc->regs_res = NULL; 183 } 184 sc->regs_t = 0; 185 sc->regs_h = 0; 186 187 return 0; 188 } 189 190 static device_method_t ig4iic_pci_methods[] = { 191 /* Device interface */ 192 DEVMETHOD(device_probe, ig4iic_pci_probe), 193 DEVMETHOD(device_attach, ig4iic_pci_attach), 194 DEVMETHOD(device_detach, ig4iic_pci_detach), 195 196 /* Bus methods */ 197 DEVMETHOD(bus_print_child, bus_generic_print_child), 198 199 /* SMBus methods from ig4_smb.c */ 200 DEVMETHOD(smbus_callback, ig4iic_smb_callback), 201 DEVMETHOD(smbus_quick, ig4iic_smb_quick), 202 DEVMETHOD(smbus_sendb, ig4iic_smb_sendb), 203 DEVMETHOD(smbus_recvb, ig4iic_smb_recvb), 204 DEVMETHOD(smbus_writeb, ig4iic_smb_writeb), 205 DEVMETHOD(smbus_writew, ig4iic_smb_writew), 206 DEVMETHOD(smbus_readb, ig4iic_smb_readb), 207 DEVMETHOD(smbus_readw, ig4iic_smb_readw), 208 DEVMETHOD(smbus_pcall, ig4iic_smb_pcall), 209 DEVMETHOD(smbus_bwrite, ig4iic_smb_bwrite), 210 DEVMETHOD(smbus_bread, ig4iic_smb_bread), 211 DEVMETHOD(smbus_trans, ig4iic_smb_trans), 212 DEVMETHOD_END 213 }; 214 215 static driver_t ig4iic_pci_driver = { 216 "ig4iic", 217 ig4iic_pci_methods, 218 sizeof(struct ig4iic_softc) 219 }; 220 221 static devclass_t ig4iic_pci_devclass; 222 223 DRIVER_MODULE(ig4iic, pci, ig4iic_pci_driver, ig4iic_pci_devclass, NULL, NULL); 224 MODULE_DEPEND(ig4iic, pci, 1, 1, 1); 225 MODULE_DEPEND(ig4iic, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER); 226 MODULE_VERSION(ig4iic, 1); 227