1a9656fbcSSascha Wildner /*- 2a9656fbcSSascha Wildner * Copyright (c) 1998, 1999 Takanori Watanabe 3a9656fbcSSascha Wildner * All rights reserved. 4a9656fbcSSascha Wildner * 5a9656fbcSSascha Wildner * Redistribution and use in source and binary forms, with or without 6a9656fbcSSascha Wildner * modification, are permitted provided that the following conditions 7a9656fbcSSascha Wildner * are met: 8a9656fbcSSascha Wildner * 1. Redistributions of source code must retain the above copyright 9a9656fbcSSascha Wildner * notice, this list of conditions and the following disclaimer. 10a9656fbcSSascha Wildner * 2. Redistributions in binary form must reproduce the above copyright 11a9656fbcSSascha Wildner * notice, this list of conditions and the following disclaimer in the 12a9656fbcSSascha Wildner * documentation and/or other materials provided with the distribution. 13a9656fbcSSascha Wildner * 14a9656fbcSSascha Wildner * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15a9656fbcSSascha Wildner * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16a9656fbcSSascha Wildner * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17a9656fbcSSascha Wildner * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18a9656fbcSSascha Wildner * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19a9656fbcSSascha Wildner * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20a9656fbcSSascha Wildner * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21a9656fbcSSascha Wildner * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22a9656fbcSSascha Wildner * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23a9656fbcSSascha Wildner * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24a9656fbcSSascha Wildner * SUCH DAMAGE. 25a9656fbcSSascha Wildner * 26a9656fbcSSascha Wildner * $FreeBSD: src/sys/pci/intpm.c,v 1.45 2009/09/19 08:56:28 avg Exp $ 27a9656fbcSSascha Wildner */ 28a9656fbcSSascha Wildner 29a9656fbcSSascha Wildner #include <sys/param.h> 30a9656fbcSSascha Wildner #include <sys/systm.h> 31a9656fbcSSascha Wildner #include <sys/bus.h> 32a9656fbcSSascha Wildner #include <sys/globaldata.h> 33a9656fbcSSascha Wildner #include <sys/kernel.h> 34a9656fbcSSascha Wildner #include <sys/lock.h> 35a9656fbcSSascha Wildner #include <sys/module.h> 36a9656fbcSSascha Wildner #include <sys/mutex.h> 37a9656fbcSSascha Wildner #include <sys/rman.h> 38b47b3275SSepherosa Ziehau #include <sys/machintr.h> 39a9656fbcSSascha Wildner #include <bus/smbus/smbconf.h> 40a9656fbcSSascha Wildner 41a9656fbcSSascha Wildner #include "smbus_if.h" 42a9656fbcSSascha Wildner 43a9656fbcSSascha Wildner #include <bus/pci/pcireg.h> 44a9656fbcSSascha Wildner #include <bus/pci/pcivar.h> 45a9656fbcSSascha Wildner #include <dev/powermng/intpm/intpmreg.h> 46a9656fbcSSascha Wildner 47a9656fbcSSascha Wildner #include "opt_intpm.h" 48a9656fbcSSascha Wildner 49a9656fbcSSascha Wildner struct intsmb_softc { 50a9656fbcSSascha Wildner device_t dev; 51a9656fbcSSascha Wildner struct resource *io_res; 52a9656fbcSSascha Wildner struct resource *irq_res; 53a9656fbcSSascha Wildner void *irq_hand; 54a9656fbcSSascha Wildner device_t smbus; 55a9656fbcSSascha Wildner int isbusy; 56a9656fbcSSascha Wildner int cfg_irq9; 57a9656fbcSSascha Wildner int poll; 58a9656fbcSSascha Wildner struct lock lock; 59a9656fbcSSascha Wildner }; 60a9656fbcSSascha Wildner 61a9656fbcSSascha Wildner #define INTSMB_LOCK(sc) lockmgr(&(sc)->lock, LK_EXCLUSIVE) 62a9656fbcSSascha Wildner #define INTSMB_UNLOCK(sc) lockmgr(&(sc)->lock, LK_RELEASE) 63a9656fbcSSascha Wildner #define INTSMB_LOCK_ASSERT(sc) KKASSERT(lockstatus(&(sc)->lock, curthread) != 0) 64a9656fbcSSascha Wildner 65a9656fbcSSascha Wildner static int intsmb_probe(device_t); 66a9656fbcSSascha Wildner static int intsmb_attach(device_t); 67a9656fbcSSascha Wildner static int intsmb_detach(device_t); 68a9656fbcSSascha Wildner static int intsmb_intr(struct intsmb_softc *sc); 69a9656fbcSSascha Wildner static int intsmb_slvintr(struct intsmb_softc *sc); 70a9656fbcSSascha Wildner static void intsmb_alrintr(struct intsmb_softc *sc); 71a9656fbcSSascha Wildner static int intsmb_callback(device_t dev, int index, void *data); 72a9656fbcSSascha Wildner static int intsmb_quick(device_t dev, u_char slave, int how); 73a9656fbcSSascha Wildner static int intsmb_sendb(device_t dev, u_char slave, char byte); 74a9656fbcSSascha Wildner static int intsmb_recvb(device_t dev, u_char slave, char *byte); 75a9656fbcSSascha Wildner static int intsmb_writeb(device_t dev, u_char slave, char cmd, char byte); 76a9656fbcSSascha Wildner static int intsmb_writew(device_t dev, u_char slave, char cmd, short word); 77a9656fbcSSascha Wildner static int intsmb_readb(device_t dev, u_char slave, char cmd, char *byte); 78a9656fbcSSascha Wildner static int intsmb_readw(device_t dev, u_char slave, char cmd, short *word); 79a9656fbcSSascha Wildner static int intsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata); 80a9656fbcSSascha Wildner static int intsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf); 81a9656fbcSSascha Wildner static int intsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf); 82a9656fbcSSascha Wildner static void intsmb_start(struct intsmb_softc *sc, u_char cmd, int nointr); 83a9656fbcSSascha Wildner static int intsmb_stop(struct intsmb_softc *sc); 84a9656fbcSSascha Wildner static int intsmb_stop_poll(struct intsmb_softc *sc); 85a9656fbcSSascha Wildner static int intsmb_free(struct intsmb_softc *sc); 86a9656fbcSSascha Wildner static void intsmb_rawintr(void *arg); 87a9656fbcSSascha Wildner 88a9656fbcSSascha Wildner static int 89a9656fbcSSascha Wildner intsmb_probe(device_t dev) 90a9656fbcSSascha Wildner { 91a9656fbcSSascha Wildner 92a9656fbcSSascha Wildner switch (pci_get_devid(dev)) { 93a9656fbcSSascha Wildner case 0x71138086: /* Intel 82371AB */ 94a9656fbcSSascha Wildner case 0x719b8086: /* Intel 82443MX */ 95a9656fbcSSascha Wildner #if 0 96a9656fbcSSascha Wildner /* Not a good idea yet, this stops isab0 functioning */ 97a9656fbcSSascha Wildner case 0x02001166: /* ServerWorks OSB4 */ 98a9656fbcSSascha Wildner #endif 99a9656fbcSSascha Wildner device_set_desc(dev, "Intel PIIX4 SMBUS Interface"); 100a9656fbcSSascha Wildner break; 101a9656fbcSSascha Wildner case 0x43851002: 102a9656fbcSSascha Wildner device_set_desc(dev, "AMD SB600/700/710/750 SMBus Controller"); 103a9656fbcSSascha Wildner /* XXX Maybe force polling right here? */ 104a9656fbcSSascha Wildner break; 105a9656fbcSSascha Wildner default: 106a9656fbcSSascha Wildner return (ENXIO); 107a9656fbcSSascha Wildner } 108a9656fbcSSascha Wildner 109a9656fbcSSascha Wildner return (BUS_PROBE_DEFAULT); 110a9656fbcSSascha Wildner } 111a9656fbcSSascha Wildner 112a9656fbcSSascha Wildner static int 113a9656fbcSSascha Wildner intsmb_attach(device_t dev) 114a9656fbcSSascha Wildner { 115a9656fbcSSascha Wildner struct intsmb_softc *sc = device_get_softc(dev); 116a9656fbcSSascha Wildner int error, rid, value; 117a9656fbcSSascha Wildner int intr; 118a9656fbcSSascha Wildner char *str; 119a9656fbcSSascha Wildner 120a9656fbcSSascha Wildner sc->dev = dev; 121a9656fbcSSascha Wildner 122a9656fbcSSascha Wildner lockinit(&sc->lock, "intsmb", 0, LK_CANRECURSE); 123a9656fbcSSascha Wildner 124a9656fbcSSascha Wildner sc->cfg_irq9 = 0; 125a9656fbcSSascha Wildner #ifndef NO_CHANGE_PCICONF 126a9656fbcSSascha Wildner switch (pci_get_devid(dev)) { 127a9656fbcSSascha Wildner case 0x71138086: /* Intel 82371AB */ 128a9656fbcSSascha Wildner case 0x719b8086: /* Intel 82443MX */ 129a9656fbcSSascha Wildner /* Changing configuration is allowed. */ 130a9656fbcSSascha Wildner sc->cfg_irq9 = 1; 131a9656fbcSSascha Wildner break; 132a9656fbcSSascha Wildner } 133a9656fbcSSascha Wildner #endif 134a9656fbcSSascha Wildner 135a9656fbcSSascha Wildner rid = PCI_BASE_ADDR_SMB; 136a9656fbcSSascha Wildner sc->io_res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, 137a9656fbcSSascha Wildner RF_ACTIVE); 138a9656fbcSSascha Wildner if (sc->io_res == NULL) { 139a9656fbcSSascha Wildner device_printf(dev, "Could not allocate I/O space\n"); 140a9656fbcSSascha Wildner error = ENXIO; 141a9656fbcSSascha Wildner goto fail; 142a9656fbcSSascha Wildner } 143a9656fbcSSascha Wildner 144a9656fbcSSascha Wildner if (sc->cfg_irq9) { 145a9656fbcSSascha Wildner pci_write_config(dev, PCIR_INTLINE, 0x9, 1); 146a9656fbcSSascha Wildner pci_write_config(dev, PCI_HST_CFG_SMB, 147a9656fbcSSascha Wildner PCI_INTR_SMB_IRQ9 | PCI_INTR_SMB_ENABLE, 1); 148a9656fbcSSascha Wildner } 149a9656fbcSSascha Wildner value = pci_read_config(dev, PCI_HST_CFG_SMB, 1); 150a9656fbcSSascha Wildner sc->poll = (value & PCI_INTR_SMB_ENABLE) == 0; 151a9656fbcSSascha Wildner intr = value & PCI_INTR_SMB_MASK; 152a9656fbcSSascha Wildner switch (intr) { 153a9656fbcSSascha Wildner case PCI_INTR_SMB_SMI: 154a9656fbcSSascha Wildner str = "SMI"; 155a9656fbcSSascha Wildner break; 156a9656fbcSSascha Wildner case PCI_INTR_SMB_IRQ9: 157a9656fbcSSascha Wildner str = "IRQ 9"; 158a9656fbcSSascha Wildner break; 159a9656fbcSSascha Wildner case PCI_INTR_SMB_IRQ_PCI: 160a9656fbcSSascha Wildner str = "PCI IRQ"; 161a9656fbcSSascha Wildner break; 162a9656fbcSSascha Wildner default: 163a9656fbcSSascha Wildner str = "BOGUS"; 164a9656fbcSSascha Wildner } 165a9656fbcSSascha Wildner 166a9656fbcSSascha Wildner device_printf(dev, "intr %s %s ", str, 167a9656fbcSSascha Wildner sc->poll == 0 ? "enabled" : "disabled"); 168a9656fbcSSascha Wildner kprintf("revision %d\n", pci_read_config(dev, PCI_REVID_SMB, 1)); 169a9656fbcSSascha Wildner 170a9656fbcSSascha Wildner if (!sc->poll && intr == PCI_INTR_SMB_SMI) { 171a9656fbcSSascha Wildner device_printf(dev, 172a9656fbcSSascha Wildner "using polling mode when configured interrupt is SMI\n"); 173a9656fbcSSascha Wildner sc->poll = 1; 174a9656fbcSSascha Wildner } 175a9656fbcSSascha Wildner 176a9656fbcSSascha Wildner if (sc->poll) 177a9656fbcSSascha Wildner goto no_intr; 178a9656fbcSSascha Wildner 179a9656fbcSSascha Wildner if (intr != PCI_INTR_SMB_IRQ9 && intr != PCI_INTR_SMB_IRQ_PCI) { 180a9656fbcSSascha Wildner device_printf(dev, "Unsupported interrupt mode\n"); 181a9656fbcSSascha Wildner error = ENXIO; 182a9656fbcSSascha Wildner goto fail; 183a9656fbcSSascha Wildner } 184a9656fbcSSascha Wildner 185a9656fbcSSascha Wildner /* Force IRQ 9. */ 186a9656fbcSSascha Wildner rid = 0; 187b47b3275SSepherosa Ziehau if (sc->cfg_irq9) { 188b47b3275SSepherosa Ziehau bus_set_resource(dev, SYS_RES_IRQ, rid, 9, 1, 189*bec969afSSepherosa Ziehau machintr_legacy_intr_cpuid(9)); 190b47b3275SSepherosa Ziehau } 191a9656fbcSSascha Wildner 192a9656fbcSSascha Wildner sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 193a9656fbcSSascha Wildner RF_SHAREABLE | RF_ACTIVE); 194a9656fbcSSascha Wildner if (sc->irq_res == NULL) { 195a9656fbcSSascha Wildner device_printf(dev, "Could not allocate irq\n"); 196a9656fbcSSascha Wildner error = ENXIO; 197a9656fbcSSascha Wildner goto fail; 198a9656fbcSSascha Wildner } 199a9656fbcSSascha Wildner 200a9656fbcSSascha Wildner error = bus_setup_intr(dev, sc->irq_res, 0, 201a9656fbcSSascha Wildner intsmb_rawintr, sc, &sc->irq_hand, NULL); 202a9656fbcSSascha Wildner if (error) { 203a9656fbcSSascha Wildner device_printf(dev, "Failed to map intr\n"); 204a9656fbcSSascha Wildner goto fail; 205a9656fbcSSascha Wildner } 206a9656fbcSSascha Wildner 207a9656fbcSSascha Wildner no_intr: 208a9656fbcSSascha Wildner sc->isbusy = 0; 209a9656fbcSSascha Wildner sc->smbus = device_add_child(dev, "smbus", -1); 210a9656fbcSSascha Wildner if (sc->smbus == NULL) { 211a9656fbcSSascha Wildner error = ENXIO; 212a9656fbcSSascha Wildner goto fail; 213a9656fbcSSascha Wildner } 214a9656fbcSSascha Wildner error = device_probe_and_attach(sc->smbus); 215a9656fbcSSascha Wildner if (error) 216a9656fbcSSascha Wildner goto fail; 217a9656fbcSSascha Wildner 218a9656fbcSSascha Wildner #ifdef ENABLE_ALART 219a9656fbcSSascha Wildner /* Enable Arart */ 220a9656fbcSSascha Wildner bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, PIIX4_SMBSLVCNT_ALTEN); 221a9656fbcSSascha Wildner #endif 222a9656fbcSSascha Wildner return (0); 223a9656fbcSSascha Wildner 224a9656fbcSSascha Wildner fail: 225a9656fbcSSascha Wildner intsmb_detach(dev); 226a9656fbcSSascha Wildner return (error); 227a9656fbcSSascha Wildner } 228a9656fbcSSascha Wildner 229a9656fbcSSascha Wildner static int 230a9656fbcSSascha Wildner intsmb_detach(device_t dev) 231a9656fbcSSascha Wildner { 232a9656fbcSSascha Wildner struct intsmb_softc *sc = device_get_softc(dev); 233a9656fbcSSascha Wildner int error; 234a9656fbcSSascha Wildner 235a9656fbcSSascha Wildner error = bus_generic_detach(dev); 236a9656fbcSSascha Wildner if (error) 237a9656fbcSSascha Wildner return (error); 238a9656fbcSSascha Wildner 239a9656fbcSSascha Wildner if (sc->smbus) 240a9656fbcSSascha Wildner device_delete_child(dev, sc->smbus); 241a9656fbcSSascha Wildner if (sc->irq_hand) 242a9656fbcSSascha Wildner bus_teardown_intr(dev, sc->irq_res, sc->irq_hand); 243a9656fbcSSascha Wildner if (sc->irq_res) 244a9656fbcSSascha Wildner bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); 245a9656fbcSSascha Wildner if (sc->io_res) 246a9656fbcSSascha Wildner bus_release_resource(dev, SYS_RES_IOPORT, PCI_BASE_ADDR_SMB, 247a9656fbcSSascha Wildner sc->io_res); 248a9656fbcSSascha Wildner lockuninit(&sc->lock); 249a9656fbcSSascha Wildner return (0); 250a9656fbcSSascha Wildner } 251a9656fbcSSascha Wildner 252a9656fbcSSascha Wildner static void 253a9656fbcSSascha Wildner intsmb_rawintr(void *arg) 254a9656fbcSSascha Wildner { 255a9656fbcSSascha Wildner struct intsmb_softc *sc = arg; 256a9656fbcSSascha Wildner 257a9656fbcSSascha Wildner INTSMB_LOCK(sc); 258a9656fbcSSascha Wildner intsmb_intr(sc); 259a9656fbcSSascha Wildner intsmb_slvintr(sc); 260a9656fbcSSascha Wildner INTSMB_UNLOCK(sc); 261a9656fbcSSascha Wildner } 262a9656fbcSSascha Wildner 263a9656fbcSSascha Wildner static int 264a9656fbcSSascha Wildner intsmb_callback(device_t dev, int index, void *data) 265a9656fbcSSascha Wildner { 266a9656fbcSSascha Wildner int error = 0; 267a9656fbcSSascha Wildner 268a9656fbcSSascha Wildner switch (index) { 269a9656fbcSSascha Wildner case SMB_REQUEST_BUS: 270a9656fbcSSascha Wildner break; 271a9656fbcSSascha Wildner case SMB_RELEASE_BUS: 272a9656fbcSSascha Wildner break; 273a9656fbcSSascha Wildner default: 274a9656fbcSSascha Wildner error = EINVAL; 275a9656fbcSSascha Wildner } 276a9656fbcSSascha Wildner 277a9656fbcSSascha Wildner return (error); 278a9656fbcSSascha Wildner } 279a9656fbcSSascha Wildner 280a9656fbcSSascha Wildner /* Counterpart of smbtx_smb_free(). */ 281a9656fbcSSascha Wildner static int 282a9656fbcSSascha Wildner intsmb_free(struct intsmb_softc *sc) 283a9656fbcSSascha Wildner { 284a9656fbcSSascha Wildner 285a9656fbcSSascha Wildner INTSMB_LOCK_ASSERT(sc); 286a9656fbcSSascha Wildner if ((bus_read_1(sc->io_res, PIIX4_SMBHSTSTS) & PIIX4_SMBHSTSTAT_BUSY) || 287a9656fbcSSascha Wildner #ifdef ENABLE_ALART 288a9656fbcSSascha Wildner (bus_read_1(sc->io_res, PIIX4_SMBSLVSTS) & PIIX4_SMBSLVSTS_BUSY) || 289a9656fbcSSascha Wildner #endif 290a9656fbcSSascha Wildner sc->isbusy) 291a9656fbcSSascha Wildner return (SMB_EBUSY); 292a9656fbcSSascha Wildner 293a9656fbcSSascha Wildner sc->isbusy = 1; 294a9656fbcSSascha Wildner /* Disable Interrupt in slave part. */ 295a9656fbcSSascha Wildner #ifndef ENABLE_ALART 296a9656fbcSSascha Wildner bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, 0); 297a9656fbcSSascha Wildner #endif 298a9656fbcSSascha Wildner /* Reset INTR Flag to prepare INTR. */ 299a9656fbcSSascha Wildner bus_write_1(sc->io_res, PIIX4_SMBHSTSTS, 300a9656fbcSSascha Wildner PIIX4_SMBHSTSTAT_INTR | PIIX4_SMBHSTSTAT_ERR | 301a9656fbcSSascha Wildner PIIX4_SMBHSTSTAT_BUSC | PIIX4_SMBHSTSTAT_FAIL); 302a9656fbcSSascha Wildner return (0); 303a9656fbcSSascha Wildner } 304a9656fbcSSascha Wildner 305a9656fbcSSascha Wildner static int 306a9656fbcSSascha Wildner intsmb_intr(struct intsmb_softc *sc) 307a9656fbcSSascha Wildner { 308a9656fbcSSascha Wildner int status, tmp; 309a9656fbcSSascha Wildner 310a9656fbcSSascha Wildner status = bus_read_1(sc->io_res, PIIX4_SMBHSTSTS); 311a9656fbcSSascha Wildner if (status & PIIX4_SMBHSTSTAT_BUSY) 312a9656fbcSSascha Wildner return (1); 313a9656fbcSSascha Wildner 314a9656fbcSSascha Wildner if (status & (PIIX4_SMBHSTSTAT_INTR | PIIX4_SMBHSTSTAT_ERR | 315a9656fbcSSascha Wildner PIIX4_SMBHSTSTAT_BUSC | PIIX4_SMBHSTSTAT_FAIL)) { 316a9656fbcSSascha Wildner 317a9656fbcSSascha Wildner tmp = bus_read_1(sc->io_res, PIIX4_SMBHSTCNT); 318a9656fbcSSascha Wildner bus_write_1(sc->io_res, PIIX4_SMBHSTCNT, 319a9656fbcSSascha Wildner tmp & ~PIIX4_SMBHSTCNT_INTREN); 320a9656fbcSSascha Wildner if (sc->isbusy) { 321a9656fbcSSascha Wildner sc->isbusy = 0; 322a9656fbcSSascha Wildner wakeup(sc); 323a9656fbcSSascha Wildner } 324a9656fbcSSascha Wildner return (0); 325a9656fbcSSascha Wildner } 326a9656fbcSSascha Wildner return (1); /* Not Completed */ 327a9656fbcSSascha Wildner } 328a9656fbcSSascha Wildner 329a9656fbcSSascha Wildner static int 330a9656fbcSSascha Wildner intsmb_slvintr(struct intsmb_softc *sc) 331a9656fbcSSascha Wildner { 332a9656fbcSSascha Wildner int status; 333a9656fbcSSascha Wildner 334a9656fbcSSascha Wildner status = bus_read_1(sc->io_res, PIIX4_SMBSLVSTS); 335a9656fbcSSascha Wildner if (status & PIIX4_SMBSLVSTS_BUSY) 336a9656fbcSSascha Wildner return (1); 337a9656fbcSSascha Wildner if (status & PIIX4_SMBSLVSTS_ALART) 338a9656fbcSSascha Wildner intsmb_alrintr(sc); 339a9656fbcSSascha Wildner else if (status & ~(PIIX4_SMBSLVSTS_ALART | PIIX4_SMBSLVSTS_SDW2 340a9656fbcSSascha Wildner | PIIX4_SMBSLVSTS_SDW1)) { 341a9656fbcSSascha Wildner } 342a9656fbcSSascha Wildner 343a9656fbcSSascha Wildner /* Reset Status Register */ 344a9656fbcSSascha Wildner bus_write_1(sc->io_res, PIIX4_SMBSLVSTS, 345a9656fbcSSascha Wildner PIIX4_SMBSLVSTS_ALART | PIIX4_SMBSLVSTS_SDW2 | 346a9656fbcSSascha Wildner PIIX4_SMBSLVSTS_SDW1 | PIIX4_SMBSLVSTS_SLV); 347a9656fbcSSascha Wildner return (0); 348a9656fbcSSascha Wildner } 349a9656fbcSSascha Wildner 350a9656fbcSSascha Wildner static void 351a9656fbcSSascha Wildner intsmb_alrintr(struct intsmb_softc *sc) 352a9656fbcSSascha Wildner { 353a9656fbcSSascha Wildner int slvcnt; 354a9656fbcSSascha Wildner #ifdef ENABLE_ALART 355a9656fbcSSascha Wildner int error; 356a9656fbcSSascha Wildner uint8_t addr; 357a9656fbcSSascha Wildner #endif 358a9656fbcSSascha Wildner 359a9656fbcSSascha Wildner /* Stop generating INTR from ALART. */ 360a9656fbcSSascha Wildner slvcnt = bus_read_1(sc->io_res, PIIX4_SMBSLVCNT); 361a9656fbcSSascha Wildner #ifdef ENABLE_ALART 362a9656fbcSSascha Wildner bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, 363a9656fbcSSascha Wildner slvcnt & ~PIIX4_SMBSLVCNT_ALTEN); 364a9656fbcSSascha Wildner #endif 365a9656fbcSSascha Wildner DELAY(5); 366a9656fbcSSascha Wildner 367a9656fbcSSascha Wildner /* Ask bus who asserted it and then ask it what's the matter. */ 368a9656fbcSSascha Wildner #ifdef ENABLE_ALART 369a9656fbcSSascha Wildner error = intsmb_free(sc); 370a9656fbcSSascha Wildner if (error) 371a9656fbcSSascha Wildner return; 372a9656fbcSSascha Wildner 373a9656fbcSSascha Wildner bus_write_1(sc->io_res, PIIX4_SMBHSTADD, SMBALTRESP | LSB); 374a9656fbcSSascha Wildner intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BYTE, 1); 375a9656fbcSSascha Wildner error = intsmb_stop_poll(sc); 376a9656fbcSSascha Wildner if (error) 377a9656fbcSSascha Wildner device_printf(sc->dev, "ALART: ERROR\n"); 378a9656fbcSSascha Wildner else { 379a9656fbcSSascha Wildner addr = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0); 380a9656fbcSSascha Wildner device_printf(sc->dev, "ALART_RESPONSE: 0x%x\n", addr); 381a9656fbcSSascha Wildner } 382a9656fbcSSascha Wildner 383a9656fbcSSascha Wildner /* Re-enable INTR from ALART. */ 384a9656fbcSSascha Wildner bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, 385a9656fbcSSascha Wildner slvcnt | PIIX4_SMBSLVCNT_ALTEN); 386a9656fbcSSascha Wildner DELAY(5); 387a9656fbcSSascha Wildner #endif 388a9656fbcSSascha Wildner } 389a9656fbcSSascha Wildner 390a9656fbcSSascha Wildner static void 391a9656fbcSSascha Wildner intsmb_start(struct intsmb_softc *sc, unsigned char cmd, int nointr) 392a9656fbcSSascha Wildner { 393a9656fbcSSascha Wildner unsigned char tmp; 394a9656fbcSSascha Wildner 395a9656fbcSSascha Wildner INTSMB_LOCK_ASSERT(sc); 396a9656fbcSSascha Wildner tmp = bus_read_1(sc->io_res, PIIX4_SMBHSTCNT); 397a9656fbcSSascha Wildner tmp &= 0xe0; 398a9656fbcSSascha Wildner tmp |= cmd; 399a9656fbcSSascha Wildner tmp |= PIIX4_SMBHSTCNT_START; 400a9656fbcSSascha Wildner 401a9656fbcSSascha Wildner /* While not in autoconfiguration enable interrupts. */ 402a9656fbcSSascha Wildner if (!sc->poll && !cold && !nointr) 403a9656fbcSSascha Wildner tmp |= PIIX4_SMBHSTCNT_INTREN; 404a9656fbcSSascha Wildner bus_write_1(sc->io_res, PIIX4_SMBHSTCNT, tmp); 405a9656fbcSSascha Wildner } 406a9656fbcSSascha Wildner 407a9656fbcSSascha Wildner static int 408a9656fbcSSascha Wildner intsmb_error(device_t dev, int status) 409a9656fbcSSascha Wildner { 410a9656fbcSSascha Wildner int error = 0; 411a9656fbcSSascha Wildner 412a9656fbcSSascha Wildner if (status & PIIX4_SMBHSTSTAT_ERR) 413a9656fbcSSascha Wildner error |= SMB_EBUSERR; 414a9656fbcSSascha Wildner if (status & PIIX4_SMBHSTSTAT_BUSC) 415a9656fbcSSascha Wildner error |= SMB_ECOLLI; 416a9656fbcSSascha Wildner if (status & PIIX4_SMBHSTSTAT_FAIL) 417a9656fbcSSascha Wildner error |= SMB_ENOACK; 418a9656fbcSSascha Wildner 419a9656fbcSSascha Wildner if (error != 0 && bootverbose) 420a9656fbcSSascha Wildner device_printf(dev, "error = %d, status = %#x\n", error, status); 421a9656fbcSSascha Wildner 422a9656fbcSSascha Wildner return (error); 423a9656fbcSSascha Wildner } 424a9656fbcSSascha Wildner 425a9656fbcSSascha Wildner /* 426a9656fbcSSascha Wildner * Polling Code. 427a9656fbcSSascha Wildner * 428a9656fbcSSascha Wildner * Polling is not encouraged because it requires waiting for the 429a9656fbcSSascha Wildner * device if it is busy. 430a9656fbcSSascha Wildner * (29063505.pdf from Intel) But during boot, interrupt cannot be used, so use 431a9656fbcSSascha Wildner * polling code then. 432a9656fbcSSascha Wildner */ 433a9656fbcSSascha Wildner static int 434a9656fbcSSascha Wildner intsmb_stop_poll(struct intsmb_softc *sc) 435a9656fbcSSascha Wildner { 436a9656fbcSSascha Wildner int error, i, status, tmp; 437a9656fbcSSascha Wildner 438a9656fbcSSascha Wildner INTSMB_LOCK_ASSERT(sc); 439a9656fbcSSascha Wildner 440a9656fbcSSascha Wildner /* First, wait for busy to be set. */ 441a9656fbcSSascha Wildner for (i = 0; i < 0x7fff; i++) 442a9656fbcSSascha Wildner if (bus_read_1(sc->io_res, PIIX4_SMBHSTSTS) & 443a9656fbcSSascha Wildner PIIX4_SMBHSTSTAT_BUSY) 444a9656fbcSSascha Wildner break; 445a9656fbcSSascha Wildner 446a9656fbcSSascha Wildner /* Wait for busy to clear. */ 447a9656fbcSSascha Wildner for (i = 0; i < 0x7fff; i++) { 448a9656fbcSSascha Wildner status = bus_read_1(sc->io_res, PIIX4_SMBHSTSTS); 449a9656fbcSSascha Wildner if (!(status & PIIX4_SMBHSTSTAT_BUSY)) { 450a9656fbcSSascha Wildner sc->isbusy = 0; 451a9656fbcSSascha Wildner error = intsmb_error(sc->dev, status); 452a9656fbcSSascha Wildner return (error); 453a9656fbcSSascha Wildner } 454a9656fbcSSascha Wildner } 455a9656fbcSSascha Wildner 456a9656fbcSSascha Wildner /* Timed out waiting for busy to clear. */ 457a9656fbcSSascha Wildner sc->isbusy = 0; 458a9656fbcSSascha Wildner tmp = bus_read_1(sc->io_res, PIIX4_SMBHSTCNT); 459a9656fbcSSascha Wildner bus_write_1(sc->io_res, PIIX4_SMBHSTCNT, tmp & ~PIIX4_SMBHSTCNT_INTREN); 460a9656fbcSSascha Wildner return (SMB_ETIMEOUT); 461a9656fbcSSascha Wildner } 462a9656fbcSSascha Wildner 463a9656fbcSSascha Wildner /* 464a9656fbcSSascha Wildner * Wait for completion and return result. 465a9656fbcSSascha Wildner */ 466a9656fbcSSascha Wildner static int 467a9656fbcSSascha Wildner intsmb_stop(struct intsmb_softc *sc) 468a9656fbcSSascha Wildner { 469a9656fbcSSascha Wildner int error, status; 470a9656fbcSSascha Wildner 471a9656fbcSSascha Wildner INTSMB_LOCK_ASSERT(sc); 472a9656fbcSSascha Wildner 473a9656fbcSSascha Wildner if (sc->poll || cold) 474a9656fbcSSascha Wildner /* So that it can use device during device probe on SMBus. */ 475a9656fbcSSascha Wildner return (intsmb_stop_poll(sc)); 476a9656fbcSSascha Wildner 477a9656fbcSSascha Wildner error = lksleep(sc, &sc->lock, PCATCH, "SMBWAI", hz / 8); 478a9656fbcSSascha Wildner if (error == 0) { 479a9656fbcSSascha Wildner status = bus_read_1(sc->io_res, PIIX4_SMBHSTSTS); 480a9656fbcSSascha Wildner if (!(status & PIIX4_SMBHSTSTAT_BUSY)) { 481a9656fbcSSascha Wildner error = intsmb_error(sc->dev, status); 482a9656fbcSSascha Wildner if (error == 0 && !(status & PIIX4_SMBHSTSTAT_INTR)) 483a9656fbcSSascha Wildner device_printf(sc->dev, "unknown cause why?\n"); 484a9656fbcSSascha Wildner #ifdef ENABLE_ALART 485a9656fbcSSascha Wildner bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, 486a9656fbcSSascha Wildner PIIX4_SMBSLVCNT_ALTEN); 487a9656fbcSSascha Wildner #endif 488a9656fbcSSascha Wildner return (error); 489a9656fbcSSascha Wildner } 490a9656fbcSSascha Wildner } 491a9656fbcSSascha Wildner 492a9656fbcSSascha Wildner /* Timeout Procedure. */ 493a9656fbcSSascha Wildner sc->isbusy = 0; 494a9656fbcSSascha Wildner 495a9656fbcSSascha Wildner /* Re-enable supressed interrupt from slave part. */ 496a9656fbcSSascha Wildner bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, PIIX4_SMBSLVCNT_ALTEN); 497a9656fbcSSascha Wildner if (error == EWOULDBLOCK) 498a9656fbcSSascha Wildner return (SMB_ETIMEOUT); 499a9656fbcSSascha Wildner else 500a9656fbcSSascha Wildner return (SMB_EABORT); 501a9656fbcSSascha Wildner } 502a9656fbcSSascha Wildner 503a9656fbcSSascha Wildner static int 504a9656fbcSSascha Wildner intsmb_quick(device_t dev, u_char slave, int how) 505a9656fbcSSascha Wildner { 506a9656fbcSSascha Wildner struct intsmb_softc *sc = device_get_softc(dev); 507a9656fbcSSascha Wildner int error; 508a9656fbcSSascha Wildner u_char data; 509a9656fbcSSascha Wildner 510a9656fbcSSascha Wildner data = slave; 511a9656fbcSSascha Wildner 512a9656fbcSSascha Wildner /* Quick command is part of Address, I think. */ 513a9656fbcSSascha Wildner switch(how) { 514a9656fbcSSascha Wildner case SMB_QWRITE: 515a9656fbcSSascha Wildner data &= ~LSB; 516a9656fbcSSascha Wildner break; 517a9656fbcSSascha Wildner case SMB_QREAD: 518a9656fbcSSascha Wildner data |= LSB; 519a9656fbcSSascha Wildner break; 520a9656fbcSSascha Wildner default: 521a9656fbcSSascha Wildner return (EINVAL); 522a9656fbcSSascha Wildner } 523a9656fbcSSascha Wildner 524a9656fbcSSascha Wildner INTSMB_LOCK(sc); 525a9656fbcSSascha Wildner error = intsmb_free(sc); 526a9656fbcSSascha Wildner if (error) { 527a9656fbcSSascha Wildner INTSMB_UNLOCK(sc); 528a9656fbcSSascha Wildner return (error); 529a9656fbcSSascha Wildner } 530a9656fbcSSascha Wildner bus_write_1(sc->io_res, PIIX4_SMBHSTADD, data); 531a9656fbcSSascha Wildner intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_QUICK, 0); 532a9656fbcSSascha Wildner error = intsmb_stop(sc); 533a9656fbcSSascha Wildner INTSMB_UNLOCK(sc); 534a9656fbcSSascha Wildner return (error); 535a9656fbcSSascha Wildner } 536a9656fbcSSascha Wildner 537a9656fbcSSascha Wildner static int 538a9656fbcSSascha Wildner intsmb_sendb(device_t dev, u_char slave, char byte) 539a9656fbcSSascha Wildner { 540a9656fbcSSascha Wildner struct intsmb_softc *sc = device_get_softc(dev); 541a9656fbcSSascha Wildner int error; 542a9656fbcSSascha Wildner 543a9656fbcSSascha Wildner INTSMB_LOCK(sc); 544a9656fbcSSascha Wildner error = intsmb_free(sc); 545a9656fbcSSascha Wildner if (error) { 546a9656fbcSSascha Wildner INTSMB_UNLOCK(sc); 547a9656fbcSSascha Wildner return (error); 548a9656fbcSSascha Wildner } 549a9656fbcSSascha Wildner bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB); 550a9656fbcSSascha Wildner bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, byte); 551a9656fbcSSascha Wildner intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BYTE, 0); 552a9656fbcSSascha Wildner error = intsmb_stop(sc); 553a9656fbcSSascha Wildner INTSMB_UNLOCK(sc); 554a9656fbcSSascha Wildner return (error); 555a9656fbcSSascha Wildner } 556a9656fbcSSascha Wildner 557a9656fbcSSascha Wildner static int 558a9656fbcSSascha Wildner intsmb_recvb(device_t dev, u_char slave, char *byte) 559a9656fbcSSascha Wildner { 560a9656fbcSSascha Wildner struct intsmb_softc *sc = device_get_softc(dev); 561a9656fbcSSascha Wildner int error; 562a9656fbcSSascha Wildner 563a9656fbcSSascha Wildner INTSMB_LOCK(sc); 564a9656fbcSSascha Wildner error = intsmb_free(sc); 565a9656fbcSSascha Wildner if (error) { 566a9656fbcSSascha Wildner INTSMB_UNLOCK(sc); 567a9656fbcSSascha Wildner return (error); 568a9656fbcSSascha Wildner } 569a9656fbcSSascha Wildner bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave | LSB); 570a9656fbcSSascha Wildner intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BYTE, 0); 571a9656fbcSSascha Wildner error = intsmb_stop(sc); 572a9656fbcSSascha Wildner if (error == 0) { 573a9656fbcSSascha Wildner #ifdef RECV_IS_IN_CMD 574a9656fbcSSascha Wildner /* 575a9656fbcSSascha Wildner * Linux SMBus stuff also troubles 576a9656fbcSSascha Wildner * Because Intel's datasheet does not make clear. 577a9656fbcSSascha Wildner */ 578a9656fbcSSascha Wildner *byte = bus_read_1(sc->io_res, PIIX4_SMBHSTCMD); 579a9656fbcSSascha Wildner #else 580a9656fbcSSascha Wildner *byte = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0); 581a9656fbcSSascha Wildner #endif 582a9656fbcSSascha Wildner } 583a9656fbcSSascha Wildner INTSMB_UNLOCK(sc); 584a9656fbcSSascha Wildner return (error); 585a9656fbcSSascha Wildner } 586a9656fbcSSascha Wildner 587a9656fbcSSascha Wildner static int 588a9656fbcSSascha Wildner intsmb_writeb(device_t dev, u_char slave, char cmd, char byte) 589a9656fbcSSascha Wildner { 590a9656fbcSSascha Wildner struct intsmb_softc *sc = device_get_softc(dev); 591a9656fbcSSascha Wildner int error; 592a9656fbcSSascha Wildner 593a9656fbcSSascha Wildner INTSMB_LOCK(sc); 594a9656fbcSSascha Wildner error = intsmb_free(sc); 595a9656fbcSSascha Wildner if (error) { 596a9656fbcSSascha Wildner INTSMB_UNLOCK(sc); 597a9656fbcSSascha Wildner return (error); 598a9656fbcSSascha Wildner } 599a9656fbcSSascha Wildner bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB); 600a9656fbcSSascha Wildner bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd); 601a9656fbcSSascha Wildner bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, byte); 602a9656fbcSSascha Wildner intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BDATA, 0); 603a9656fbcSSascha Wildner error = intsmb_stop(sc); 604a9656fbcSSascha Wildner INTSMB_UNLOCK(sc); 605a9656fbcSSascha Wildner return (error); 606a9656fbcSSascha Wildner } 607a9656fbcSSascha Wildner 608a9656fbcSSascha Wildner static int 609a9656fbcSSascha Wildner intsmb_writew(device_t dev, u_char slave, char cmd, short word) 610a9656fbcSSascha Wildner { 611a9656fbcSSascha Wildner struct intsmb_softc *sc = device_get_softc(dev); 612a9656fbcSSascha Wildner int error; 613a9656fbcSSascha Wildner 614a9656fbcSSascha Wildner INTSMB_LOCK(sc); 615a9656fbcSSascha Wildner error = intsmb_free(sc); 616a9656fbcSSascha Wildner if (error) { 617a9656fbcSSascha Wildner INTSMB_UNLOCK(sc); 618a9656fbcSSascha Wildner return (error); 619a9656fbcSSascha Wildner } 620a9656fbcSSascha Wildner bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB); 621a9656fbcSSascha Wildner bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd); 622a9656fbcSSascha Wildner bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, word & 0xff); 623a9656fbcSSascha Wildner bus_write_1(sc->io_res, PIIX4_SMBHSTDAT1, (word >> 8) & 0xff); 624a9656fbcSSascha Wildner intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_WDATA, 0); 625a9656fbcSSascha Wildner error = intsmb_stop(sc); 626a9656fbcSSascha Wildner INTSMB_UNLOCK(sc); 627a9656fbcSSascha Wildner return (error); 628a9656fbcSSascha Wildner } 629a9656fbcSSascha Wildner 630a9656fbcSSascha Wildner static int 631a9656fbcSSascha Wildner intsmb_readb(device_t dev, u_char slave, char cmd, char *byte) 632a9656fbcSSascha Wildner { 633a9656fbcSSascha Wildner struct intsmb_softc *sc = device_get_softc(dev); 634a9656fbcSSascha Wildner int error; 635a9656fbcSSascha Wildner 636a9656fbcSSascha Wildner INTSMB_LOCK(sc); 637a9656fbcSSascha Wildner error = intsmb_free(sc); 638a9656fbcSSascha Wildner if (error) { 639a9656fbcSSascha Wildner INTSMB_UNLOCK(sc); 640a9656fbcSSascha Wildner return (error); 641a9656fbcSSascha Wildner } 642a9656fbcSSascha Wildner bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave | LSB); 643a9656fbcSSascha Wildner bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd); 644a9656fbcSSascha Wildner intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BDATA, 0); 645a9656fbcSSascha Wildner error = intsmb_stop(sc); 646a9656fbcSSascha Wildner if (error == 0) 647a9656fbcSSascha Wildner *byte = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0); 648a9656fbcSSascha Wildner INTSMB_UNLOCK(sc); 649a9656fbcSSascha Wildner return (error); 650a9656fbcSSascha Wildner } 651a9656fbcSSascha Wildner 652a9656fbcSSascha Wildner static int 653a9656fbcSSascha Wildner intsmb_readw(device_t dev, u_char slave, char cmd, short *word) 654a9656fbcSSascha Wildner { 655a9656fbcSSascha Wildner struct intsmb_softc *sc = device_get_softc(dev); 656a9656fbcSSascha Wildner int error; 657a9656fbcSSascha Wildner 658a9656fbcSSascha Wildner INTSMB_LOCK(sc); 659a9656fbcSSascha Wildner error = intsmb_free(sc); 660a9656fbcSSascha Wildner if (error) { 661a9656fbcSSascha Wildner INTSMB_UNLOCK(sc); 662a9656fbcSSascha Wildner return (error); 663a9656fbcSSascha Wildner } 664a9656fbcSSascha Wildner bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave | LSB); 665a9656fbcSSascha Wildner bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd); 666a9656fbcSSascha Wildner intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_WDATA, 0); 667a9656fbcSSascha Wildner error = intsmb_stop(sc); 668a9656fbcSSascha Wildner if (error == 0) { 669a9656fbcSSascha Wildner *word = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0); 670a9656fbcSSascha Wildner *word |= bus_read_1(sc->io_res, PIIX4_SMBHSTDAT1) << 8; 671a9656fbcSSascha Wildner } 672a9656fbcSSascha Wildner INTSMB_UNLOCK(sc); 673a9656fbcSSascha Wildner return (error); 674a9656fbcSSascha Wildner } 675a9656fbcSSascha Wildner 676a9656fbcSSascha Wildner /* 677a9656fbcSSascha Wildner * Data sheet claims that it implements all function, but also claims 678a9656fbcSSascha Wildner * that it implements 7 function and not mention PCALL. So I don't know 679a9656fbcSSascha Wildner * whether it will work. 680a9656fbcSSascha Wildner */ 681a9656fbcSSascha Wildner static int 682a9656fbcSSascha Wildner intsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata) 683a9656fbcSSascha Wildner { 684a9656fbcSSascha Wildner #ifdef PROCCALL_TEST 685a9656fbcSSascha Wildner struct intsmb_softc *sc = device_get_softc(dev); 686a9656fbcSSascha Wildner int error; 687a9656fbcSSascha Wildner 688a9656fbcSSascha Wildner INTSMB_LOCK(sc); 689a9656fbcSSascha Wildner error = intsmb_free(sc); 690a9656fbcSSascha Wildner if (error) { 691a9656fbcSSascha Wildner INTSMB_UNLOCK(sc); 692a9656fbcSSascha Wildner return (error); 693a9656fbcSSascha Wildner } 694a9656fbcSSascha Wildner bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB); 695a9656fbcSSascha Wildner bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd); 696a9656fbcSSascha Wildner bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, sdata & 0xff); 697a9656fbcSSascha Wildner bus_write_1(sc->io_res, PIIX4_SMBHSTDAT1, (sdata & 0xff) >> 8); 698a9656fbcSSascha Wildner intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_WDATA, 0); 699a9656fbcSSascha Wildner error = intsmb_stop(sc); 700a9656fbcSSascha Wildner if (error == 0) { 701a9656fbcSSascha Wildner *rdata = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0); 702a9656fbcSSascha Wildner *rdata |= bus_read_1(sc->io_res, PIIX4_SMBHSTDAT1) << 8; 703a9656fbcSSascha Wildner } 704a9656fbcSSascha Wildner INTSMB_UNLOCK(sc); 705a9656fbcSSascha Wildner return (error); 706a9656fbcSSascha Wildner #else 707a9656fbcSSascha Wildner return (SMB_ENOTSUPP); 708a9656fbcSSascha Wildner #endif 709a9656fbcSSascha Wildner } 710a9656fbcSSascha Wildner 711a9656fbcSSascha Wildner static int 712a9656fbcSSascha Wildner intsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf) 713a9656fbcSSascha Wildner { 714a9656fbcSSascha Wildner struct intsmb_softc *sc = device_get_softc(dev); 715a9656fbcSSascha Wildner int error, i; 716a9656fbcSSascha Wildner 717a9656fbcSSascha Wildner if (count > SMBBLOCKTRANS_MAX || count == 0) 718a9656fbcSSascha Wildner return (SMB_EINVAL); 719a9656fbcSSascha Wildner 720a9656fbcSSascha Wildner INTSMB_LOCK(sc); 721a9656fbcSSascha Wildner error = intsmb_free(sc); 722a9656fbcSSascha Wildner if (error) { 723a9656fbcSSascha Wildner INTSMB_UNLOCK(sc); 724a9656fbcSSascha Wildner return (error); 725a9656fbcSSascha Wildner } 726a9656fbcSSascha Wildner 727a9656fbcSSascha Wildner /* Reset internal array index. */ 728a9656fbcSSascha Wildner bus_read_1(sc->io_res, PIIX4_SMBHSTCNT); 729a9656fbcSSascha Wildner 730a9656fbcSSascha Wildner bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB); 731a9656fbcSSascha Wildner bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd); 732a9656fbcSSascha Wildner for (i = 0; i < count; i++) 733a9656fbcSSascha Wildner bus_write_1(sc->io_res, PIIX4_SMBBLKDAT, buf[i]); 734a9656fbcSSascha Wildner bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, count); 735a9656fbcSSascha Wildner intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BLOCK, 0); 736a9656fbcSSascha Wildner error = intsmb_stop(sc); 737a9656fbcSSascha Wildner INTSMB_UNLOCK(sc); 738a9656fbcSSascha Wildner return (error); 739a9656fbcSSascha Wildner } 740a9656fbcSSascha Wildner 741a9656fbcSSascha Wildner static int 742a9656fbcSSascha Wildner intsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf) 743a9656fbcSSascha Wildner { 744a9656fbcSSascha Wildner struct intsmb_softc *sc = device_get_softc(dev); 745a9656fbcSSascha Wildner int error, i; 746a9656fbcSSascha Wildner u_char data, nread; 747a9656fbcSSascha Wildner 748a9656fbcSSascha Wildner if (*count > SMBBLOCKTRANS_MAX || *count == 0) 749a9656fbcSSascha Wildner return (SMB_EINVAL); 750a9656fbcSSascha Wildner 751a9656fbcSSascha Wildner INTSMB_LOCK(sc); 752a9656fbcSSascha Wildner error = intsmb_free(sc); 753a9656fbcSSascha Wildner if (error) { 754a9656fbcSSascha Wildner INTSMB_UNLOCK(sc); 755a9656fbcSSascha Wildner return (error); 756a9656fbcSSascha Wildner } 757a9656fbcSSascha Wildner 758a9656fbcSSascha Wildner /* Reset internal array index. */ 759a9656fbcSSascha Wildner bus_read_1(sc->io_res, PIIX4_SMBHSTCNT); 760a9656fbcSSascha Wildner 761a9656fbcSSascha Wildner bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave | LSB); 762a9656fbcSSascha Wildner bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd); 763a9656fbcSSascha Wildner bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, *count); 764a9656fbcSSascha Wildner intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BLOCK, 0); 765a9656fbcSSascha Wildner error = intsmb_stop(sc); 766a9656fbcSSascha Wildner if (error == 0) { 767a9656fbcSSascha Wildner nread = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0); 768a9656fbcSSascha Wildner if (nread != 0 && nread <= SMBBLOCKTRANS_MAX) { 769a9656fbcSSascha Wildner for (i = 0; i < nread; i++) { 770a9656fbcSSascha Wildner data = bus_read_1(sc->io_res, PIIX4_SMBBLKDAT); 771a9656fbcSSascha Wildner if (i < *count) 772a9656fbcSSascha Wildner buf[i] = data; 773a9656fbcSSascha Wildner } 774a9656fbcSSascha Wildner *count = nread; 775a9656fbcSSascha Wildner } else 776a9656fbcSSascha Wildner error = EIO; 777a9656fbcSSascha Wildner } 778a9656fbcSSascha Wildner INTSMB_UNLOCK(sc); 779a9656fbcSSascha Wildner return (error); 780a9656fbcSSascha Wildner } 781a9656fbcSSascha Wildner 782a9656fbcSSascha Wildner static devclass_t intsmb_devclass; 783a9656fbcSSascha Wildner 784a9656fbcSSascha Wildner static device_method_t intsmb_methods[] = { 785a9656fbcSSascha Wildner /* Device interface */ 786a9656fbcSSascha Wildner DEVMETHOD(device_probe, intsmb_probe), 787a9656fbcSSascha Wildner DEVMETHOD(device_attach, intsmb_attach), 788a9656fbcSSascha Wildner DEVMETHOD(device_detach, intsmb_detach), 789a9656fbcSSascha Wildner 790a9656fbcSSascha Wildner /* Bus interface */ 791a9656fbcSSascha Wildner DEVMETHOD(bus_print_child, bus_generic_print_child), 792a9656fbcSSascha Wildner 793a9656fbcSSascha Wildner /* SMBus interface */ 794a9656fbcSSascha Wildner DEVMETHOD(smbus_callback, intsmb_callback), 795a9656fbcSSascha Wildner DEVMETHOD(smbus_quick, intsmb_quick), 796a9656fbcSSascha Wildner DEVMETHOD(smbus_sendb, intsmb_sendb), 797a9656fbcSSascha Wildner DEVMETHOD(smbus_recvb, intsmb_recvb), 798a9656fbcSSascha Wildner DEVMETHOD(smbus_writeb, intsmb_writeb), 799a9656fbcSSascha Wildner DEVMETHOD(smbus_writew, intsmb_writew), 800a9656fbcSSascha Wildner DEVMETHOD(smbus_readb, intsmb_readb), 801a9656fbcSSascha Wildner DEVMETHOD(smbus_readw, intsmb_readw), 802a9656fbcSSascha Wildner DEVMETHOD(smbus_pcall, intsmb_pcall), 803a9656fbcSSascha Wildner DEVMETHOD(smbus_bwrite, intsmb_bwrite), 804a9656fbcSSascha Wildner DEVMETHOD(smbus_bread, intsmb_bread), 805a9656fbcSSascha Wildner 806a9656fbcSSascha Wildner { 0, 0 } 807a9656fbcSSascha Wildner }; 808a9656fbcSSascha Wildner 809a9656fbcSSascha Wildner static driver_t intsmb_driver = { 810a9656fbcSSascha Wildner "intsmb", 811a9656fbcSSascha Wildner intsmb_methods, 812a9656fbcSSascha Wildner sizeof(struct intsmb_softc), 813a9656fbcSSascha Wildner }; 814a9656fbcSSascha Wildner 815aa2b9d05SSascha Wildner DRIVER_MODULE(intsmb, pci, intsmb_driver, intsmb_devclass, NULL, NULL); 816aa2b9d05SSascha Wildner DRIVER_MODULE(smbus, intsmb, smbus_driver, smbus_devclass, NULL, NULL); 817a9656fbcSSascha Wildner MODULE_DEPEND(intsmb, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER); 818a9656fbcSSascha Wildner MODULE_VERSION(intsmb, 1); 819