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/lock.h> 46 #include <sys/mutex.h> 47 #include <sys/syslog.h> 48 #include <sys/bus.h> 49 50 #include <sys/rman.h> 51 52 #include <bus/pci/pcivar.h> 53 #include <bus/pci/pcireg.h> 54 #include <bus/smbus/smbconf.h> 55 56 #include "smbus_if.h" 57 58 #include "ig4_reg.h" 59 #include "ig4_var.h" 60 61 static int ig4iic_pci_detach(device_t dev); 62 63 static const struct { 64 uint16_t id; 65 char *name; 66 enum ig4_vers version; 67 } intel_i2c_ids[] = { 68 /* Haswell */ 69 { 0x9c61, "Intel Lynx Point-LP I2C Controller-1", IG4_HASWELL }, 70 { 0x9c62, "Intel Lynx Point-LP I2C Controller-2", IG4_HASWELL }, 71 72 /* Skylake-U/Y and Kaby Lake-U/Y CPUs */ 73 { 0x9d60, "Intel Sunrise Point-LP I2C Controller-0", IG4_SKYLAKE }, 74 { 0x9d61, "Intel Sunrise Point-LP I2C Controller-1", IG4_SKYLAKE }, 75 { 0x9d62, "Intel Sunrise Point-LP I2C Controller-2", IG4_SKYLAKE }, 76 { 0x9d63, "Intel Sunrise Point-LP I2C Controller-3", IG4_SKYLAKE }, 77 { 0x9d64, "Intel Sunrise Point-LP I2C Controller-4", IG4_SKYLAKE }, 78 { 0x9d65, "Intel Sunrise Point-LP I2C Controller-5", IG4_SKYLAKE }, 79 }; 80 81 static 82 int 83 ig4iic_pci_probe(device_t dev) 84 { 85 int i; 86 uint16_t device_id; 87 const char *name = NULL; 88 89 if (pci_get_vendor(dev) != PCI_VENDOR_INTEL) 90 return (ENXIO); 91 92 device_id = pci_get_device(dev); 93 for (i = 0; i < NELEM(intel_i2c_ids); i++) { 94 if (intel_i2c_ids[i].id == device_id) { 95 name = intel_i2c_ids[i].name; 96 break; 97 } 98 } 99 if (name != NULL) { 100 device_set_desc(dev, name); 101 } else { 102 return (ENXIO); 103 } 104 105 return BUS_PROBE_DEFAULT; 106 } 107 108 static 109 int 110 ig4iic_pci_attach(device_t dev) 111 { 112 ig4iic_softc_t *sc = device_get_softc(dev); 113 u_int irq_flags; 114 uint16_t device_id; 115 int msi_enable = 1; 116 int error, i; 117 118 bzero(sc, sizeof(*sc)); 119 120 lockinit(&sc->lk, "ig4iic", 0, LK_CANRECURSE); 121 122 device_id = pci_get_device(dev); 123 for (i = 0; i < NELEM(intel_i2c_ids); i++) { 124 if (intel_i2c_ids[i].id == device_id) { 125 sc->version = intel_i2c_ids[i].version; 126 break; 127 } 128 } 129 130 sc->dev = dev; 131 sc->regs_rid = PCIR_BAR(0); 132 sc->regs_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, 133 &sc->regs_rid, RF_ACTIVE); 134 if (sc->regs_res == NULL) { 135 device_printf(dev, "unable to map registers"); 136 ig4iic_pci_detach(dev); 137 return (ENXIO); 138 } 139 sc->intr_type = pci_alloc_1intr(dev, msi_enable, 140 &sc->intr_rid, &irq_flags); 141 sc->intr_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, 142 &sc->intr_rid, irq_flags); 143 if (sc->intr_res == NULL) { 144 device_printf(dev, "unable to map interrupt"); 145 ig4iic_pci_detach(dev); 146 return (ENXIO); 147 } 148 sc->regs_t = rman_get_bustag(sc->regs_res); 149 sc->regs_h = rman_get_bushandle(sc->regs_res); 150 sc->pci_attached = 1; 151 152 error = ig4iic_attach(sc); 153 if (error) 154 ig4iic_pci_detach(dev); 155 156 return error; 157 } 158 159 static 160 int 161 ig4iic_pci_detach(device_t dev) 162 { 163 ig4iic_softc_t *sc = device_get_softc(dev); 164 int error; 165 166 if (sc->pci_attached) { 167 error = ig4iic_detach(sc); 168 if (error) 169 return error; 170 sc->pci_attached = 0; 171 } 172 173 if (sc->intr_res) { 174 bus_release_resource(dev, SYS_RES_IRQ, 175 sc->intr_rid, sc->intr_res); 176 sc->intr_res = NULL; 177 } 178 if (sc->intr_type == PCI_INTR_TYPE_MSI) 179 pci_release_msi(dev); 180 if (sc->regs_res) { 181 bus_release_resource(dev, SYS_RES_MEMORY, 182 sc->regs_rid, sc->regs_res); 183 sc->regs_res = NULL; 184 } 185 sc->regs_t = 0; 186 sc->regs_h = 0; 187 lockuninit(&sc->lk); 188 189 return 0; 190 } 191 192 static device_method_t ig4iic_pci_methods[] = { 193 /* Device interface */ 194 DEVMETHOD(device_probe, ig4iic_pci_probe), 195 DEVMETHOD(device_attach, ig4iic_pci_attach), 196 DEVMETHOD(device_detach, ig4iic_pci_detach), 197 198 /* Bus methods */ 199 DEVMETHOD(bus_print_child, bus_generic_print_child), 200 201 /* SMBus methods from ig4_smb.c */ 202 DEVMETHOD(smbus_callback, ig4iic_smb_callback), 203 DEVMETHOD(smbus_quick, ig4iic_smb_quick), 204 DEVMETHOD(smbus_sendb, ig4iic_smb_sendb), 205 DEVMETHOD(smbus_recvb, ig4iic_smb_recvb), 206 DEVMETHOD(smbus_writeb, ig4iic_smb_writeb), 207 DEVMETHOD(smbus_writew, ig4iic_smb_writew), 208 DEVMETHOD(smbus_readb, ig4iic_smb_readb), 209 DEVMETHOD(smbus_readw, ig4iic_smb_readw), 210 DEVMETHOD(smbus_pcall, ig4iic_smb_pcall), 211 DEVMETHOD(smbus_bwrite, ig4iic_smb_bwrite), 212 DEVMETHOD(smbus_bread, ig4iic_smb_bread), 213 DEVMETHOD(smbus_trans, ig4iic_smb_trans), 214 DEVMETHOD_END 215 }; 216 217 static driver_t ig4iic_pci_driver = { 218 "ig4iic", 219 ig4iic_pci_methods, 220 sizeof(struct ig4iic_softc) 221 }; 222 223 static devclass_t ig4iic_pci_devclass; 224 225 DRIVER_MODULE(ig4iic, pci, ig4iic_pci_driver, ig4iic_pci_devclass, NULL, NULL); 226 MODULE_DEPEND(ig4iic, pci, 1, 1, 1); 227 MODULE_DEPEND(ig4iic, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER); 228 MODULE_VERSION(ig4iic, 1); 229