intpm.c revision 162289
1107120Sjulian/*- 2107120Sjulian * Copyright (c) 1998, 1999 Takanori Watanabe 3139823Simp * All rights reserved. 4139823Simp * 5139823Simp * Redistribution and use in source and binary forms, with or without 6107120Sjulian * modification, are permitted provided that the following conditions 7107120Sjulian * are met: 8107120Sjulian * 1. Redistributions of source code must retain the above copyright 9107120Sjulian * notice, this list of conditions and the following disclaimer. 10107120Sjulian * 2. Redistributions in binary form must reproduce the above copyright 11107120Sjulian * notice, this list of conditions and the following disclaimer in the 12107120Sjulian * documentation and/or other materials provided with the distribution. 13107120Sjulian * 14107120Sjulian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15107120Sjulian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16107120Sjulian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17107120Sjulian * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18107120Sjulian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19107120Sjulian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20107120Sjulian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21107120Sjulian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22107120Sjulian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23107120Sjulian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24107120Sjulian * SUCH DAMAGE. 25107120Sjulian */ 26107120Sjulian 27107120Sjulian#include <sys/cdefs.h> 28107120Sjulian__FBSDID("$FreeBSD: head/sys/pci/intpm.c 162289 2006-09-13 18:56:39Z jhb $"); 29107120Sjulian 30121054Semax#include <sys/param.h> 31107120Sjulian#include <sys/systm.h> 32107120Sjulian#include <sys/kernel.h> 33107120Sjulian#include <machine/bus.h> 34107120Sjulian 35107120Sjulian#include <sys/uio.h> 36107120Sjulian#include <sys/module.h> 37107120Sjulian#include <sys/bus.h> 38107120Sjulian#include <sys/rman.h> 39107120Sjulian#include <machine/resource.h> 40107120Sjulian#include <dev/smbus/smbconf.h> 41107120Sjulian 42107120Sjulian#include "smbus_if.h" 43128688Semax 44128688Semax/*This should be removed if force_pci_map_int supported*/ 45128688Semax#include <sys/interrupt.h> 46128688Semax 47128688Semax#include <dev/pci/pcireg.h> 48128688Semax#include <dev/pci/pcivar.h> 49128688Semax#include <pci/intpmreg.h> 50128688Semax 51128688Semax#include "opt_intpm.h" 52107120Sjulian 53107120Sjulianstatic struct _pcsid 54107120Sjulian{ 55107120Sjulian u_int32_t type; 56107120Sjulian char *desc; 57107120Sjulian} pci_ids[] = { 58107120Sjulian { 0x71138086, "Intel 82371AB Power management controller" }, 59107120Sjulian { 0x719b8086, "Intel 82443MX Power management controller" }, 60107120Sjulian#if 0 61107120Sjulian /* Not a good idea yet, this stops isab0 functioning */ 62107120Sjulian { 0x02001166, "ServerWorks OSB4 PCI to ISA Bridge" }, 63107120Sjulian#endif 64107120Sjulian 65107120Sjulian { 0x00000000, NULL } 66107120Sjulian}; 67107120Sjulian 68107120Sjulianstatic int intsmb_probe(device_t); 69107120Sjulianstatic int intsmb_attach(device_t); 70107120Sjulianstatic int intsmb_intr(device_t dev); 71107120Sjulianstatic int intsmb_slvintr(device_t dev); 72107120Sjulianstatic void intsmb_alrintr(device_t dev); 73107120Sjulianstatic int intsmb_callback(device_t dev, int index, void *data); 74107120Sjulianstatic int intsmb_quick(device_t dev, u_char slave, int how); 75107120Sjulianstatic int intsmb_sendb(device_t dev, u_char slave, char byte); 76107120Sjulianstatic int intsmb_recvb(device_t dev, u_char slave, char *byte); 77107120Sjulianstatic int intsmb_writeb(device_t dev, u_char slave, char cmd, char byte); 78107120Sjulianstatic int intsmb_writew(device_t dev, u_char slave, char cmd, short word); 79107120Sjulianstatic int intsmb_readb(device_t dev, u_char slave, char cmd, char *byte); 80107120Sjulianstatic int intsmb_readw(device_t dev, u_char slave, char cmd, short *word); 81107120Sjulianstatic int intsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata); 82107120Sjulianstatic int intsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf); 83107120Sjulianstatic int intsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf); 84107120Sjulianstatic void intsmb_start(device_t dev, u_char cmd, int nointr); 85107120Sjulianstatic int intsmb_stop(device_t dev); 86107120Sjulianstatic int intsmb_stop_poll(device_t dev); 87107120Sjulianstatic int intsmb_free(device_t dev); 88107120Sjulianstatic int intpm_probe (device_t dev); 89107120Sjulianstatic int intpm_attach (device_t dev); 90107120Sjulianstatic void intpm_intr(void *arg); 91107120Sjulian 92107120Sjulianstatic devclass_t intsmb_devclass; 93107120Sjulian 94107120Sjulianstatic device_method_t intpm_methods[] = { 95107120Sjulian /* Device interface */ 96107120Sjulian DEVMETHOD(device_probe, intsmb_probe), 97107120Sjulian DEVMETHOD(device_attach, intsmb_attach), 98107120Sjulian 99107120Sjulian /* Bus interface */ 100107120Sjulian DEVMETHOD(bus_print_child, bus_generic_print_child), 101107120Sjulian 102107120Sjulian /* SMBus interface */ 103107120Sjulian DEVMETHOD(smbus_callback, intsmb_callback), 104107120Sjulian DEVMETHOD(smbus_quick, intsmb_quick), 105107120Sjulian DEVMETHOD(smbus_sendb, intsmb_sendb), 106107120Sjulian DEVMETHOD(smbus_recvb, intsmb_recvb), 107107120Sjulian DEVMETHOD(smbus_writeb, intsmb_writeb), 108107120Sjulian DEVMETHOD(smbus_writew, intsmb_writew), 109107120Sjulian DEVMETHOD(smbus_readb, intsmb_readb), 110107120Sjulian DEVMETHOD(smbus_readw, intsmb_readw), 111107120Sjulian DEVMETHOD(smbus_pcall, intsmb_pcall), 112107120Sjulian DEVMETHOD(smbus_bwrite, intsmb_bwrite), 113107120Sjulian DEVMETHOD(smbus_bread, intsmb_bread), 114128076Semax 115107120Sjulian { 0, 0 } 116107120Sjulian}; 117107120Sjulian 118107120Sjulianstruct intpm_pci_softc { 119107120Sjulian bus_space_tag_t smbst; 120107120Sjulian bus_space_handle_t smbsh; 121107120Sjulian bus_space_tag_t pmst; 122107120Sjulian bus_space_handle_t pmsh; 123107120Sjulian device_t smbus; 124107120Sjulian}; 125107120Sjulian 126107120Sjulian 127107120Sjulianstruct intsmb_softc { 128107120Sjulian struct intpm_pci_softc *pci_sc; 129107120Sjulian bus_space_tag_t st; 130107120Sjulian bus_space_handle_t sh; 131107120Sjulian device_t smbus; 132107120Sjulian int isbusy; 133107120Sjulian}; 134107120Sjulian 135107120Sjulianstatic driver_t intpm_driver = { 136107120Sjulian "intsmb", 137107120Sjulian intpm_methods, 138107120Sjulian sizeof(struct intsmb_softc), 139107120Sjulian}; 140107120Sjulian 141107120Sjulianstatic devclass_t intpm_devclass; 142107120Sjulian 143107120Sjulianstatic device_method_t intpm_pci_methods[] = { 144107120Sjulian DEVMETHOD(device_probe, intpm_probe), 145107120Sjulian DEVMETHOD(device_attach, intpm_attach), 146107120Sjulian 147107120Sjulian { 0, 0 } 148107120Sjulian}; 149107120Sjulian 150107120Sjulianstatic driver_t intpm_pci_driver = { 151107120Sjulian "intpm", 152107120Sjulian intpm_pci_methods, 153107120Sjulian sizeof(struct intpm_pci_softc) 154107120Sjulian}; 155107120Sjulian 156107120Sjulianstatic int 157107120Sjulianintsmb_probe(device_t dev) 158107120Sjulian{ 159107120Sjulian struct intsmb_softc *sc = device_get_softc(dev); 160107120Sjulian 161107120Sjulian sc->smbus = device_add_child(dev, "smbus", -1); 162107120Sjulian if (!sc->smbus) 163107120Sjulian return (EINVAL); /* XXX don't know what to return else */ 164107120Sjulian device_set_desc(dev, "Intel PIIX4 SMBUS Interface"); 165107120Sjulian 166107120Sjulian return (BUS_PROBE_DEFAULT); /* XXX don't know what to return else */ 167107120Sjulian} 168107120Sjulianstatic int 169107120Sjulianintsmb_attach(device_t dev) 170107120Sjulian{ 171107120Sjulian struct intsmb_softc *sc = device_get_softc(dev); 172107120Sjulian 173107120Sjulian sc->pci_sc = device_get_softc(device_get_parent(dev)); 174107120Sjulian sc->isbusy = 0; 175107120Sjulian sc->sh = sc->pci_sc->smbsh; 176107120Sjulian sc->st = sc->pci_sc->smbst; 177107120Sjulian sc->pci_sc->smbus = dev; 178107120Sjulian device_probe_and_attach(sc->smbus); 179107120Sjulian#ifdef ENABLE_ALART 180107120Sjulian /*Enable Arart*/ 181107120Sjulian bus_space_write_1(sc->st, sc->sh, PIIX4_SMBSLVCNT, 182107120Sjulian PIIX4_SMBSLVCNT_ALTEN); 183107120Sjulian#endif 184107120Sjulian return (0); 185107120Sjulian} 186107120Sjulian 187107120Sjulianstatic int 188107120Sjulianintsmb_callback(device_t dev, int index, void *data) 189107120Sjulian{ 190107120Sjulian int error = 0; 191107120Sjulian intrmask_t s; 192107120Sjulian 193107120Sjulian s = splnet(); 194107120Sjulian switch (index) { 195243882Sglebius case SMB_REQUEST_BUS: 196107120Sjulian break; 197107120Sjulian case SMB_RELEASE_BUS: 198107120Sjulian break; 199107120Sjulian default: 200107120Sjulian error = EINVAL; 201107120Sjulian } 202107120Sjulian splx(s); 203107120Sjulian 204107120Sjulian return (error); 205107120Sjulian} 206107120Sjulian 207107120Sjulian/* Counterpart of smbtx_smb_free(). */ 208107120Sjulianstatic int 209107120Sjulianintsmb_free(device_t dev) 210107120Sjulian{ 211107120Sjulian struct intsmb_softc *sc = device_get_softc(dev); 212107120Sjulian intrmask_t s; 213107120Sjulian 214107120Sjulian if ((bus_space_read_1(sc->st, sc->sh, PIIX4_SMBHSTSTS) & 215107120Sjulian PIIX4_SMBHSTSTAT_BUSY) || 216107120Sjulian#ifdef ENABLE_ALART 217107120Sjulian (bus_space_read_1(sc->st, sc->sh, PIIX4_SMBSLVSTS) & 218107120Sjulian PIIX4_SMBSLVSTS_BUSY) || 219107120Sjulian#endif 220107120Sjulian sc->isbusy) 221107120Sjulian return (EBUSY); 222107120Sjulian s = splhigh(); 223107120Sjulian sc->isbusy = 1; 224107120Sjulian /* Disable Interrupt in slave part. */ 225107120Sjulian#ifndef ENABLE_ALART 226107120Sjulian bus_space_write_1(sc->st, sc->sh, PIIX4_SMBSLVCNT, 0); 227107120Sjulian#endif 228107120Sjulian /* Reset INTR Flag to prepare INTR. */ 229107120Sjulian bus_space_write_1(sc->st, sc->sh, PIIX4_SMBHSTSTS, 230107120Sjulian (PIIX4_SMBHSTSTAT_INTR | PIIX4_SMBHSTSTAT_ERR | 231107120Sjulian PIIX4_SMBHSTSTAT_BUSC | PIIX4_SMBHSTSTAT_FAIL)); 232107120Sjulian splx(s); 233107120Sjulian return (0); 234107120Sjulian} 235107120Sjulian 236107120Sjulianstatic int 237107120Sjulianintsmb_intr(device_t dev) 238107120Sjulian{ 239107120Sjulian struct intsmb_softc *sc = device_get_softc(dev); 240107120Sjulian int status; 241107120Sjulian 242107120Sjulian status = bus_space_read_1(sc->st, sc->sh, PIIX4_SMBHSTSTS); 243107120Sjulian if (status & PIIX4_SMBHSTSTAT_BUSY) 244107120Sjulian return (1); 245107120Sjulian 246107120Sjulian if (status & (PIIX4_SMBHSTSTAT_INTR | PIIX4_SMBHSTSTAT_ERR | 247107120Sjulian PIIX4_SMBHSTSTAT_BUSC | PIIX4_SMBHSTSTAT_FAIL)) { 248107120Sjulian int tmp; 249107120Sjulian 250107120Sjulian tmp = bus_space_read_1(sc->st, sc->sh, PIIX4_SMBHSTCNT); 251107120Sjulian bus_space_write_1(sc->st, sc->sh, PIIX4_SMBHSTCNT, 252107120Sjulian tmp & ~PIIX4_SMBHSTCNT_INTREN); 253107120Sjulian if (sc->isbusy) { 254107120Sjulian sc->isbusy = 0; 255107120Sjulian wakeup(sc); 256107120Sjulian } 257107120Sjulian return (0); 258107120Sjulian } 259107120Sjulian return (1); /* Not Completed */ 260107120Sjulian} 261107120Sjulian 262107120Sjulianstatic int 263107120Sjulianintsmb_slvintr(device_t dev) 264107120Sjulian{ 265107120Sjulian struct intsmb_softc *sc = device_get_softc(dev); 266107120Sjulian int status, retval; 267107120Sjulian 268107120Sjulian retval = 1; 269107120Sjulian status = bus_space_read_1(sc->st, sc->sh, PIIX4_SMBSLVSTS); 270107120Sjulian if (status & PIIX4_SMBSLVSTS_BUSY) 271107120Sjulian return (retval); 272107120Sjulian if (status & PIIX4_SMBSLVSTS_ALART) { 273107120Sjulian intsmb_alrintr(dev); 274107120Sjulian retval = 0; 275107120Sjulian } else if (status & ~(PIIX4_SMBSLVSTS_ALART | PIIX4_SMBSLVSTS_SDW2 276107120Sjulian | PIIX4_SMBSLVSTS_SDW1)) { 277107120Sjulian retval = 0; 278107120Sjulian } 279107120Sjulian 280107120Sjulian /* Reset Status Register */ 281107120Sjulian bus_space_write_1(sc->st, sc->sh, PIIX4_SMBSLVSTS, 282107120Sjulian PIIX4_SMBSLVSTS_ALART | PIIX4_SMBSLVSTS_SDW2 | 283107120Sjulian PIIX4_SMBSLVSTS_SDW1 | PIIX4_SMBSLVSTS_SLV); 284107120Sjulian return (retval); 285107120Sjulian} 286107120Sjulian 287107120Sjulianstatic void 288107120Sjulianintsmb_alrintr(device_t dev) 289107120Sjulian{ 290121054Semax struct intsmb_softc *sc = device_get_softc(dev); 291121054Semax int slvcnt; 292121054Semax#ifdef ENABLE_ALART 293121054Semax int error; 294121054Semax#endif 295107120Sjulian 296107120Sjulian /* Stop generating INTR from ALART. */ 297107120Sjulian slvcnt = bus_space_read_1(sc->st, sc->sh, PIIX4_SMBSLVCNT); 298107120Sjulian#ifdef ENABLE_ALART 299107120Sjulian bus_space_write_1(sc->st, sc->sh, PIIX4_SMBSLVCNT, 300107120Sjulian slvcnt & ~PIIX4_SMBSLVCNT_ALTEN); 301107120Sjulian#endif 302107120Sjulian DELAY(5); 303107120Sjulian 304107120Sjulian /* Ask bus who asserted it and then ask it what's the matter. */ 305107120Sjulian#ifdef ENABLE_ALART 306107120Sjulian error = intsmb_free(dev); 307107120Sjulian if (!error) { 308107120Sjulian bus_space_write_1(sc->st, sc->sh, PIIX4_SMBHSTADD, 309107120Sjulian SMBALTRESP | LSB); 310107120Sjulian intsmb_start(dev, PIIX4_SMBHSTCNT_PROT_BYTE, 1); 311107120Sjulian if (!(error = intsmb_stop_poll(dev))) { 312107120Sjulian u_int8_t addr; 313107120Sjulian 314107120Sjulian addr = bus_space_read_1(sc->st, sc->sh, 315107120Sjulian PIIX4_SMBHSTDAT0); 316107120Sjulian printf("ALART_RESPONSE: 0x%x\n", addr); 317107120Sjulian } 318107120Sjulian } else 319107120Sjulian printf("ERROR\n"); 320107120Sjulian 321107120Sjulian /* Re-enable INTR from ALART. */ 322107120Sjulian bus_space_write_1(sc->st, sc->sh, PIIX4_SMBSLVCNT, 323107120Sjulian slvcnt | PIIX4_SMBSLVCNT_ALTEN); 324107120Sjulian DELAY(5); 325107120Sjulian#endif 326107120Sjulian} 327107120Sjulian 328107120Sjulianstatic void 329107120Sjulianintsmb_start(device_t dev, unsigned char cmd, int nointr) 330107120Sjulian{ 331107120Sjulian struct intsmb_softc *sc = device_get_softc(dev); 332107120Sjulian unsigned char tmp; 333107120Sjulian 334107120Sjulian tmp = bus_space_read_1(sc->st, sc->sh, PIIX4_SMBHSTCNT); 335107120Sjulian tmp &= 0xe0; 336107120Sjulian tmp |= cmd; 337107120Sjulian tmp |= PIIX4_SMBHSTCNT_START; 338107120Sjulian 339107120Sjulian /* While not in autoconfiguration enable interrupts. */ 340107120Sjulian if (!cold || !nointr) 341107120Sjulian tmp |= PIIX4_SMBHSTCNT_INTREN; 342107120Sjulian bus_space_write_1(sc->st, sc->sh, PIIX4_SMBHSTCNT, tmp); 343107120Sjulian} 344107120Sjulian 345107120Sjulian/* 346107120Sjulian * Polling Code. 347107120Sjulian * 348107120Sjulian * Polling is not encouraged because it requires waiting for the 349107120Sjulian * device if it is busy. 350107120Sjulian * (29063505.pdf from Intel) But during boot, interrupt cannot be used, so use 351107120Sjulian * polling code then. 352107120Sjulian */ 353107120Sjulianstatic int 354107120Sjulianintsmb_stop_poll(device_t dev) 355107120Sjulian{ 356107120Sjulian struct intsmb_softc *sc = device_get_softc(dev); 357107120Sjulian int error, i; 358107120Sjulian int tmp; 359107120Sjulian 360107120Sjulian /* 361107120Sjulian * In smbtx driver, Simply waiting. 362107120Sjulian * This loops 100-200 times. 363107120Sjulian */ 364107120Sjulian for (i = 0; i < 0x7fff; i++) 365107120Sjulian if (bus_space_read_1(sc->st, sc->sh, PIIX4_SMBHSTSTS) & 366107120Sjulian PIIX4_SMBHSTSTAT_BUSY) 367107120Sjulian break; 368107120Sjulian 369107120Sjulian for (i = 0; i < 0x7fff; i++) { 370107120Sjulian int status; 371107120Sjulian 372107120Sjulian status = bus_space_read_1(sc->st, sc->sh, PIIX4_SMBHSTSTS); 373107120Sjulian if (!(status & PIIX4_SMBHSTSTAT_BUSY)) { 374107120Sjulian sc->isbusy = 0; 375107120Sjulian error = (status & PIIX4_SMBHSTSTAT_ERR) ? EIO : 376107120Sjulian (status & PIIX4_SMBHSTSTAT_BUSC) ? EBUSY : 377107120Sjulian (status & PIIX4_SMBHSTSTAT_FAIL) ? EIO : 0; 378107120Sjulian if (error == 0 && !(status & PIIX4_SMBHSTSTAT_INTR)) 379107120Sjulian printf("unknown cause why?"); 380107120Sjulian return (error); 381107120Sjulian } 382107120Sjulian } 383107120Sjulian 384107120Sjulian sc->isbusy = 0; 385107120Sjulian tmp = bus_space_read_1(sc->st, sc->sh, PIIX4_SMBHSTCNT); 386107120Sjulian bus_space_write_1(sc->st, sc->sh, PIIX4_SMBHSTCNT, 387107120Sjulian tmp & ~PIIX4_SMBHSTCNT_INTREN); 388107120Sjulian return (EIO); 389107120Sjulian} 390107120Sjulian 391107120Sjulian/* 392107120Sjulian * Wait for completion and return result. 393107120Sjulian */ 394107120Sjulianstatic int 395107120Sjulianintsmb_stop(device_t dev) 396107120Sjulian{ 397107120Sjulian struct intsmb_softc *sc = device_get_softc(dev); 398107120Sjulian int error; 399107120Sjulian intrmask_t s; 400107120Sjulian 401107120Sjulian if (cold) { 402107120Sjulian /* So that it can use device during device probe on SMBus. */ 403107120Sjulian error = intsmb_stop_poll(dev); 404107120Sjulian return (error); 405107120Sjulian } 406107120Sjulian 407107120Sjulian if (!tsleep(sc, (PWAIT) | PCATCH, "SMBWAI", hz/8)) { 408107120Sjulian int status; 409107120Sjulian 410107120Sjulian status = bus_space_read_1(sc->st, sc->sh, PIIX4_SMBHSTSTS); 411107120Sjulian if (!(status & PIIX4_SMBHSTSTAT_BUSY)) { 412107120Sjulian error = (status & PIIX4_SMBHSTSTAT_ERR) ? EIO : 413107120Sjulian (status & PIIX4_SMBHSTSTAT_BUSC) ? EBUSY : 414107120Sjulian (status & PIIX4_SMBHSTSTAT_FAIL) ? EIO : 0; 415107120Sjulian if (error == 0 && !(status & PIIX4_SMBHSTSTAT_INTR)) 416107120Sjulian printf("intsmb%d: unknown cause why?\n", 417107120Sjulian device_get_unit(dev)); 418107120Sjulian#ifdef ENABLE_ALART 419107120Sjulian bus_space_write_1(sc->st, sc->sh, PIIX4_SMBSLVCNT, 420107120Sjulian PIIX4_SMBSLVCNT_ALTEN); 421107120Sjulian#endif 422107120Sjulian return (error); 423107120Sjulian } 424107120Sjulian } 425107120Sjulian 426107120Sjulian /* Timeout Procedure. */ 427107120Sjulian s = splhigh(); 428107120Sjulian sc->isbusy = 0; 429107120Sjulian 430107120Sjulian /* Re-enable supressed interrupt from slave part. */ 431107120Sjulian bus_space_write_1(sc->st, sc->sh, PIIX4_SMBSLVCNT, 432107120Sjulian PIIX4_SMBSLVCNT_ALTEN); 433107120Sjulian splx(s); 434107120Sjulian return (EIO); 435107120Sjulian} 436107120Sjulian 437107120Sjulianstatic int 438107120Sjulianintsmb_quick(device_t dev, u_char slave, int how) 439107120Sjulian{ 440107120Sjulian struct intsmb_softc *sc = device_get_softc(dev); 441107120Sjulian int error = 0; 442107120Sjulian u_char data; 443107120Sjulian 444107120Sjulian data = slave; 445107120Sjulian 446107120Sjulian /* Quick command is part of Address, I think. */ 447107120Sjulian switch(how) { 448107120Sjulian case SMB_QWRITE: 449107120Sjulian data &= ~LSB; 450107120Sjulian break; 451107120Sjulian case SMB_QREAD: 452107120Sjulian data |= LSB; 453107120Sjulian break; 454107120Sjulian default: 455107120Sjulian error = EINVAL; 456107120Sjulian } 457121054Semax if (!error) { 458121054Semax error = intsmb_free(dev); 459107120Sjulian if (!error) { 460107120Sjulian bus_space_write_1(sc->st, sc->sh, PIIX4_SMBHSTADD, 461121054Semax data); 462121054Semax intsmb_start(dev, PIIX4_SMBHSTCNT_PROT_QUICK, 0); 463107120Sjulian error = intsmb_stop(dev); 464107120Sjulian } 465107120Sjulian } 466107120Sjulian 467107120Sjulian return (error); 468107120Sjulian} 469107120Sjulian 470107120Sjulianstatic int 471107120Sjulianintsmb_sendb(device_t dev, u_char slave, char byte) 472107120Sjulian{ 473107120Sjulian struct intsmb_softc *sc = device_get_softc(dev); 474107120Sjulian int error; 475107120Sjulian 476107120Sjulian error = intsmb_free(dev); 477107120Sjulian if (!error) { 478107120Sjulian bus_space_write_1(sc->st, sc->sh, PIIX4_SMBHSTADD, 479107120Sjulian slave & ~LSB); 480107120Sjulian bus_space_write_1(sc->st, sc->sh, PIIX4_SMBHSTCMD, byte); 481107120Sjulian intsmb_start(dev, PIIX4_SMBHSTCNT_PROT_BYTE, 0); 482107120Sjulian error = intsmb_stop(dev); 483107120Sjulian } 484107120Sjulian return (error); 485107120Sjulian} 486107120Sjulian 487107120Sjulianstatic int 488107120Sjulianintsmb_recvb(device_t dev, u_char slave, char *byte) 489107120Sjulian{ 490107120Sjulian struct intsmb_softc *sc = device_get_softc(dev); 491107120Sjulian int error; 492107120Sjulian 493107120Sjulian error = intsmb_free(dev); 494107120Sjulian if (!error) { 495107120Sjulian bus_space_write_1(sc->st, sc->sh, PIIX4_SMBHSTADD, slave | LSB); 496107120Sjulian intsmb_start(dev, PIIX4_SMBHSTCNT_PROT_BYTE, 0); 497107120Sjulian if (!(error = intsmb_stop(dev))) { 498107120Sjulian#ifdef RECV_IS_IN_CMD 499107120Sjulian /* 500107120Sjulian * Linux SMBus stuff also troubles 501107120Sjulian * Because Intel's datasheet does not make clear. 502107120Sjulian */ 503107120Sjulian *byte = bus_space_read_1(sc->st, sc->sh, 504107120Sjulian PIIX4_SMBHSTCMD); 505107120Sjulian#else 506107120Sjulian *byte = bus_space_read_1(sc->st, sc->sh, 507107120Sjulian PIIX4_SMBHSTDAT0); 508107120Sjulian#endif 509107120Sjulian } 510107120Sjulian } 511107120Sjulian return (error); 512107120Sjulian} 513107120Sjulian 514107120Sjulianstatic int 515107120Sjulianintsmb_writeb(device_t dev, u_char slave, char cmd, char byte) 516107120Sjulian{ 517107120Sjulian struct intsmb_softc *sc = device_get_softc(dev); 518107120Sjulian int error; 519107120Sjulian 520107120Sjulian error = intsmb_free(dev); 521107120Sjulian if (!error) { 522107120Sjulian bus_space_write_1(sc->st, sc->sh, PIIX4_SMBHSTADD, 523107120Sjulian slave & ~LSB); 524107120Sjulian bus_space_write_1(sc->st, sc->sh, PIIX4_SMBHSTCMD, cmd); 525107120Sjulian bus_space_write_1(sc->st, sc->sh, PIIX4_SMBHSTDAT0, byte); 526107120Sjulian intsmb_start(dev, PIIX4_SMBHSTCNT_PROT_BDATA, 0); 527107120Sjulian error = intsmb_stop(dev); 528107120Sjulian } 529107120Sjulian return (error); 530107120Sjulian} 531107120Sjulian 532107120Sjulianstatic int 533107120Sjulianintsmb_writew(device_t dev, u_char slave, char cmd, short word) 534107120Sjulian{ 535107120Sjulian struct intsmb_softc *sc = device_get_softc(dev); 536107120Sjulian int error; 537107120Sjulian 538107120Sjulian error = intsmb_free(dev); 539107120Sjulian if (!error) { 540107120Sjulian bus_space_write_1(sc->st, sc->sh, PIIX4_SMBHSTADD, 541107120Sjulian slave & ~LSB); 542107120Sjulian bus_space_write_1(sc->st, sc->sh, PIIX4_SMBHSTCMD, cmd); 543107120Sjulian bus_space_write_1(sc->st, sc->sh, PIIX4_SMBHSTDAT0, 544107120Sjulian word & 0xff); 545107120Sjulian bus_space_write_1(sc->st, sc->sh, PIIX4_SMBHSTDAT1, 546107120Sjulian (word >> 8) & 0xff); 547107120Sjulian intsmb_start(dev, PIIX4_SMBHSTCNT_PROT_WDATA, 0); 548107120Sjulian error = intsmb_stop(dev); 549107120Sjulian } 550107120Sjulian return (error); 551107120Sjulian} 552107120Sjulian 553107120Sjulianstatic int 554107120Sjulianintsmb_readb(device_t dev, u_char slave, char cmd, char *byte) 555107120Sjulian{ 556107120Sjulian struct intsmb_softc *sc = device_get_softc(dev); 557107120Sjulian int error; 558107120Sjulian 559107120Sjulian error = intsmb_free(dev); 560107120Sjulian if (!error) { 561107120Sjulian bus_space_write_1(sc->st, sc->sh, PIIX4_SMBHSTADD, slave | LSB); 562107120Sjulian bus_space_write_1(sc->st, sc->sh, PIIX4_SMBHSTCMD, cmd); 563107120Sjulian intsmb_start(dev, PIIX4_SMBHSTCNT_PROT_BDATA, 0); 564107120Sjulian if (!(error = intsmb_stop(dev))) 565107120Sjulian *byte = bus_space_read_1(sc->st, sc->sh, 566107120Sjulian PIIX4_SMBHSTDAT0); 567107120Sjulian } 568107120Sjulian return (error); 569107120Sjulian} 570107120Sjulianstatic int 571107120Sjulianintsmb_readw(device_t dev, u_char slave, char cmd, short *word) 572107120Sjulian{ 573107120Sjulian struct intsmb_softc *sc = device_get_softc(dev); 574107120Sjulian int error; 575107120Sjulian 576107120Sjulian error = intsmb_free(dev); 577107120Sjulian if (!error) { 578107120Sjulian bus_space_write_1(sc->st, sc->sh, PIIX4_SMBHSTADD, slave | LSB); 579107120Sjulian bus_space_write_1(sc->st, sc->sh, PIIX4_SMBHSTCMD, cmd); 580107120Sjulian intsmb_start(dev, PIIX4_SMBHSTCNT_PROT_WDATA, 0); 581107120Sjulian if (!(error = intsmb_stop(dev))) { 582107120Sjulian *word = bus_space_read_1(sc->st, sc->sh, 583107120Sjulian PIIX4_SMBHSTDAT0); 584107120Sjulian *word |= bus_space_read_1(sc->st, sc->sh, 585107120Sjulian PIIX4_SMBHSTDAT1) << 8; 586121054Semax } 587107120Sjulian } 588107120Sjulian return (error); 589107120Sjulian} 590107120Sjulian 591107120Sjulian/* 592107120Sjulian * Data sheet claims that it implements all function, but also claims 593107120Sjulian * that it implements 7 function and not mention PCALL. So I don't know 594107120Sjulian * whether it will work. 595107120Sjulian */ 596107120Sjulianstatic int 597107120Sjulianintsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata) 598107120Sjulian{ 599107120Sjulian#ifdef PROCCALL_TEST 600107120Sjulian struct intsmb_softc *sc = device_get_softc(dev); 601107120Sjulian int error; 602107120Sjulian 603107120Sjulian error = intsmb_free(dev); 604107120Sjulian if (!error) { 605107120Sjulian bus_space_write_1(sc->st, sc->sh, PIIX4_SMBHSTADD, 606107120Sjulian slave & ~LSB); 607107120Sjulian bus_space_write_1(sc->st, sc->sh, PIIX4_SMBHSTCMD, cmd); 608107120Sjulian bus_space_write_1(sc->st, sc->sh, PIIX4_SMBHSTDAT0, 609107120Sjulian sdata & 0xff); 610107120Sjulian bus_space_write_1(sc->st, sc->sh, PIIX4_SMBHSTDAT1, 611107120Sjulian (sdata & 0xff) >> 8); 612107120Sjulian intsmb_start(dev, PIIX4_SMBHSTCNT_PROT_WDATA, 0); 613107120Sjulian } 614107120Sjulian if (!(error = intsmb_stop(dev))) { 615107120Sjulian *rdata = bus_space_read_1(sc->st, sc->sh, PIIX4_SMBHSTDAT0); 616107120Sjulian *rdata |= bus_space_read_1(sc->st, sc->sh, PIIX4_SMBHSTDAT1) << 617107120Sjulian 8; 618107120Sjulian } 619107120Sjulian return (error); 620107120Sjulian#else 621107120Sjulian return (0); 622107120Sjulian#endif 623107120Sjulian} 624107120Sjulian 625107120Sjulianstatic int 626107120Sjulianintsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf) 627107120Sjulian{ 628107120Sjulian struct intsmb_softc *sc = device_get_softc(dev); 629107120Sjulian int error, i; 630107120Sjulian 631107120Sjulian error = intsmb_free(dev); 632107120Sjulian if (count > SMBBLOCKTRANS_MAX || count == 0) 633107120Sjulian error = SMB_EINVAL; 634107120Sjulian if (!error) { 635107120Sjulian /* Reset internal array index. */ 636107120Sjulian bus_space_read_1(sc->st, sc->sh, PIIX4_SMBHSTCNT); 637107120Sjulian 638107120Sjulian bus_space_write_1(sc->st, sc->sh, PIIX4_SMBHSTADD, 639107120Sjulian slave & ~LSB); 640107120Sjulian bus_space_write_1(sc->st, sc->sh, PIIX4_SMBHSTCMD, cmd); 641107120Sjulian for (i = 0; i < count; i++) 642107120Sjulian bus_space_write_1(sc->st, sc->sh, PIIX4_SMBBLKDAT, 643107120Sjulian buf[i]); 644107120Sjulian bus_space_write_1(sc->st, sc->sh, PIIX4_SMBHSTDAT0, count); 645107120Sjulian intsmb_start(dev, PIIX4_SMBHSTCNT_PROT_BLOCK, 0); 646107120Sjulian error = intsmb_stop(dev); 647107120Sjulian } 648107120Sjulian return (error); 649107120Sjulian} 650107120Sjulian 651107120Sjulianstatic int 652107120Sjulianintsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf) 653107120Sjulian{ 654107120Sjulian struct intsmb_softc *sc = device_get_softc(dev); 655107120Sjulian int error, i; 656107120Sjulian u_char data, nread; 657107120Sjulian 658107120Sjulian error = intsmb_free(dev); 659107120Sjulian if (*count > SMBBLOCKTRANS_MAX || *count == 0) 660107120Sjulian error = SMB_EINVAL; 661107120Sjulian if (!error) { 662107120Sjulian /* Reset internal array index. */ 663107120Sjulian bus_space_read_1(sc->st, sc->sh, PIIX4_SMBHSTCNT); 664107120Sjulian 665107120Sjulian bus_space_write_1(sc->st, sc->sh, PIIX4_SMBHSTADD, slave | LSB); 666107120Sjulian bus_space_write_1(sc->st, sc->sh, PIIX4_SMBHSTCMD, cmd); 667107120Sjulian bus_space_write_1(sc->st, sc->sh, PIIX4_SMBHSTDAT0, *count); 668107120Sjulian intsmb_start(dev, PIIX4_SMBHSTCNT_PROT_BLOCK, 0); 669107120Sjulian error = intsmb_stop(dev); 670107120Sjulian if (!error) { 671107120Sjulian nread= bus_space_read_1(sc->st, sc->sh, 672107120Sjulian PIIX4_SMBHSTDAT0); 673107120Sjulian if (nread != 0 && nread <= SMBBLOCKTRANS_MAX) { 674107120Sjulian for (i = 0; i < nread; i++) { 675107120Sjulian data = bus_space_read_1(sc->st, sc->sh, 676107120Sjulian PIIX4_SMBBLKDAT); 677107120Sjulian if (i < *count) 678107120Sjulian buf[i] = data; 679107120Sjulian } 680107120Sjulian *count = nread; 681107120Sjulian } else { 682107120Sjulian error = EIO; 683107120Sjulian } 684107120Sjulian } 685107120Sjulian } 686107120Sjulian return (error); 687107120Sjulian} 688107120Sjulian 689107120SjulianDRIVER_MODULE(intsmb, intpm, intpm_driver, intsmb_devclass, 0, 0); 690107120Sjulian 691107120Sjulianstatic int 692107120Sjulianintpm_attach(device_t dev) 693107120Sjulian{ 694107120Sjulian struct intpm_pci_softc *sc; 695107120Sjulian struct resource *res; 696107120Sjulian device_t smbinterface; 697107120Sjulian void *ih; 698107120Sjulian char *str; 699107120Sjulian int error, rid, value; 700107120Sjulian int unit = device_get_unit(dev); 701107120Sjulian 702107120Sjulian sc = device_get_softc(dev); 703107120Sjulian if (sc == NULL) 704107120Sjulian return (ENOMEM); 705107120Sjulian 706107120Sjulian rid = PCI_BASE_ADDR_SMB; 707107120Sjulian res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, RF_ACTIVE); 708107120Sjulian if (res == NULL) { 709107120Sjulian device_printf(dev, "Could not allocate Bus space\n"); 710107120Sjulian return (ENXIO); 711107120Sjulian } 712107120Sjulian sc->smbst = rman_get_bustag(res); 713107120Sjulian sc->smbsh = rman_get_bushandle(res); 714107120Sjulian 715121054Semax#ifdef __i386__ 716121054Semax device_printf(dev, "%s %lx\n", (sc->smbst == I386_BUS_SPACE_IO) ? 717107120Sjulian "I/O mapped" : "Memory", rman_get_start(res)); 718107120Sjulian#endif 719121054Semax 720121054Semax#ifndef NO_CHANGE_PCICONF 721121054Semax pci_write_config(dev, PCIR_INTLINE, 0x9, 1); 722121054Semax pci_write_config(dev, PCI_HST_CFG_SMB, 723107120Sjulian PCI_INTR_SMB_IRQ9 | PCI_INTR_SMB_ENABLE, 1); 724107120Sjulian#endif 725107120Sjulian value = pci_read_config(dev, PCI_HST_CFG_SMB, 1); 726107120Sjulian switch (value & 0xe) { 727107120Sjulian case PCI_INTR_SMB_SMI: 728107120Sjulian str = "SMI"; 729107120Sjulian break; 730107120Sjulian case PCI_INTR_SMB_IRQ9: 731107120Sjulian str = "IRQ 9"; 732107120Sjulian break; 733107120Sjulian default: 734107120Sjulian str = "BOGUS"; 735107120Sjulian } 736107120Sjulian device_printf(dev, "intr %s %s ", str, 737107120Sjulian (value & 1) ? "enabled" : "disabled"); 738107120Sjulian value = pci_read_config(dev, PCI_REVID_SMB, 1); 739107120Sjulian printf("revision %d\n", value); 740107120Sjulian 741107120Sjulian /* Install interrupt handler. */ 742107120Sjulian rid = 0; 743121054Semax res = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 9, 9, 1, 744107120Sjulian RF_SHAREABLE | RF_ACTIVE); 745107120Sjulian if (res == NULL) { 746107120Sjulian device_printf(dev, "could not allocate irq"); 747107120Sjulian return (ENOMEM); 748107120Sjulian } 749107120Sjulian error = bus_setup_intr(dev, res, INTR_TYPE_MISC, intpm_intr, sc, &ih); 750121054Semax if (error) { 751121054Semax device_printf(dev, "Failed to map intr\n"); 752121054Semax return (error); 753107120Sjulian } 754107120Sjulian smbinterface = device_add_child(dev, "intsmb", unit); 755107120Sjulian if (!smbinterface) 756107120Sjulian printf("intsmb%d: could not add SMBus device\n", unit); 757107120Sjulian device_probe_and_attach(smbinterface); 758107120Sjulian 759107120Sjulian value = pci_read_config(dev, PCI_BASE_ADDR_PM, 4); 760107120Sjulian printf("intpm%d: PM %s %x \n", unit, 761107120Sjulian (value & 1) ? "I/O mapped" : "Memory", value & 0xfffe); 762107120Sjulian return (0); 763107120Sjulian} 764107120Sjulian 765107120Sjulianstatic int 766107120Sjulianintpm_probe(device_t dev) 767107120Sjulian{ 768107120Sjulian struct _pcsid *ep = pci_ids; 769107120Sjulian uint32_t device_id = pci_get_devid(dev); 770107120Sjulian 771107120Sjulian while (ep->type && ep->type != device_id) 772107120Sjulian ++ep; 773107120Sjulian if (ep->desc != NULL) { 774107120Sjulian device_set_desc(dev, ep->desc); 775107120Sjulian bus_set_resource(dev, SYS_RES_IRQ, 0, 9, 1); /* XXX setup intr resource */ 776107120Sjulian return (BUS_PROBE_DEFAULT); 777107120Sjulian } else { 778107120Sjulian return (ENXIO); 779107120Sjulian } 780107120Sjulian} 781107120Sjulian 782107120Sjulianstatic void 783107120Sjulianintpm_intr(void *arg) 784107120Sjulian{ 785107120Sjulian struct intpm_pci_softc *sc = arg; 786107120Sjulian 787107120Sjulian intsmb_intr(sc->smbus); 788107120Sjulian intsmb_slvintr(sc->smbus); 789107120Sjulian} 790107120Sjulian 791107120SjulianDRIVER_MODULE(intpm, pci , intpm_pci_driver, intpm_devclass, 0, 0); 792107120SjulianDRIVER_MODULE(smbus, intsmb, smbus_driver, smbus_devclass, 0, 0); 793107120SjulianMODULE_DEPEND(intpm, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER); 794107120SjulianMODULE_VERSION(intpm, 1); 795107120Sjulian