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 #define PCI_CHIP_LYNXPT_LP_I2C_1 0x9c618086 64 #define PCI_CHIP_LYNXPT_LP_I2C_2 0x9c628086 65 66 static 67 int 68 ig4iic_pci_probe(device_t dev) 69 { 70 switch(pci_get_devid(dev)) { 71 case PCI_CHIP_LYNXPT_LP_I2C_1: 72 device_set_desc(dev, "Intel Lynx Point-LP I2C Controller-1"); 73 break; 74 case PCI_CHIP_LYNXPT_LP_I2C_2: 75 device_set_desc(dev, "Intel Lynx Point-LP I2C Controller-2"); 76 break; 77 default: 78 return(ENXIO); 79 } 80 return BUS_PROBE_DEFAULT; 81 } 82 83 static 84 int 85 ig4iic_pci_attach(device_t dev) 86 { 87 ig4iic_softc_t *sc = device_get_softc(dev); 88 u_int irq_flags; 89 int msi_enable = 1; 90 int error; 91 92 bzero(sc, sizeof(*sc)); 93 94 lockinit(&sc->lk, "ig4iic", 0, LK_CANRECURSE); 95 96 sc->dev = dev; 97 sc->regs_rid = PCIR_BAR(0); 98 sc->regs_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, 99 &sc->regs_rid, RF_ACTIVE); 100 if (sc->regs_res == NULL) { 101 device_printf(dev, "unable to map registers"); 102 ig4iic_pci_detach(dev); 103 return (ENXIO); 104 } 105 sc->intr_type = pci_alloc_1intr(dev, msi_enable, 106 &sc->intr_rid, &irq_flags); 107 sc->intr_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, 108 &sc->intr_rid, irq_flags); 109 if (sc->intr_res == NULL) { 110 device_printf(dev, "unable to map interrupt"); 111 ig4iic_pci_detach(dev); 112 return (ENXIO); 113 } 114 sc->regs_t = rman_get_bustag(sc->regs_res); 115 sc->regs_h = rman_get_bushandle(sc->regs_res); 116 sc->pci_attached = 1; 117 118 error = ig4iic_attach(sc); 119 if (error) 120 ig4iic_pci_detach(dev); 121 122 return error; 123 } 124 125 static 126 int 127 ig4iic_pci_detach(device_t dev) 128 { 129 ig4iic_softc_t *sc = device_get_softc(dev); 130 int error; 131 132 if (sc->pci_attached) { 133 error = ig4iic_detach(sc); 134 if (error) 135 return error; 136 sc->pci_attached = 0; 137 } 138 139 if (sc->intr_res) { 140 bus_release_resource(dev, SYS_RES_IRQ, 141 sc->intr_rid, sc->intr_res); 142 sc->intr_res = NULL; 143 } 144 if (sc->intr_type == PCI_INTR_TYPE_MSI) 145 pci_release_msi(dev); 146 if (sc->regs_res) { 147 bus_release_resource(dev, SYS_RES_MEMORY, 148 sc->regs_rid, sc->regs_res); 149 sc->regs_res = NULL; 150 } 151 sc->regs_t = 0; 152 sc->regs_h = 0; 153 lockuninit(&sc->lk); 154 155 return 0; 156 } 157 158 static device_method_t ig4iic_pci_methods[] = { 159 /* Device interface */ 160 DEVMETHOD(device_probe, ig4iic_pci_probe), 161 DEVMETHOD(device_attach, ig4iic_pci_attach), 162 DEVMETHOD(device_detach, ig4iic_pci_detach), 163 164 /* Bus methods */ 165 DEVMETHOD(bus_print_child, bus_generic_print_child), 166 167 /* SMBus methods from ig4_smb.c */ 168 DEVMETHOD(smbus_callback, ig4iic_smb_callback), 169 DEVMETHOD(smbus_quick, ig4iic_smb_quick), 170 DEVMETHOD(smbus_sendb, ig4iic_smb_sendb), 171 DEVMETHOD(smbus_recvb, ig4iic_smb_recvb), 172 DEVMETHOD(smbus_writeb, ig4iic_smb_writeb), 173 DEVMETHOD(smbus_writew, ig4iic_smb_writew), 174 DEVMETHOD(smbus_readb, ig4iic_smb_readb), 175 DEVMETHOD(smbus_readw, ig4iic_smb_readw), 176 DEVMETHOD(smbus_pcall, ig4iic_smb_pcall), 177 DEVMETHOD(smbus_bwrite, ig4iic_smb_bwrite), 178 DEVMETHOD(smbus_bread, ig4iic_smb_bread), 179 DEVMETHOD(smbus_trans, ig4iic_smb_trans), 180 DEVMETHOD_END 181 }; 182 183 static driver_t ig4iic_pci_driver = { 184 "ig4iic", 185 ig4iic_pci_methods, 186 sizeof(struct ig4iic_softc) 187 }; 188 189 static devclass_t ig4iic_pci_devclass; 190 191 DRIVER_MODULE(ig4iic, pci, ig4iic_pci_driver, ig4iic_pci_devclass, NULL, NULL); 192 MODULE_DEPEND(ig4iic, pci, 1, 1, 1); 193 MODULE_DEPEND(ig4iic, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER); 194 MODULE_VERSION(ig4iic, 1); 195