intpm.c revision 43166
1238603Sjoerg/*- 2238603Sjoerg * Copyright (c) 1998, 1999 Takanori Watanabe 3238603Sjoerg * All rights reserved. 4238603Sjoerg * 5238603Sjoerg * Redistribution and use in source and binary forms, with or without 6238603Sjoerg * modification, are permitted provided that the following conditions 7238603Sjoerg * are met: 8238603Sjoerg * 1. Redistributions of source code must retain the above copyright 9238603Sjoerg * notice, this list of conditions and the following disclaimer. 10238603Sjoerg * 2. Redistributions in binary form must reproduce the above copyright 11238603Sjoerg * notice, this list of conditions and the following disclaimer in the 12238603Sjoerg * documentation and/or other materials provided with the distribution. 13238603Sjoerg * 14238603Sjoerg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15238603Sjoerg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16238603Sjoerg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17238603Sjoerg * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18238603Sjoerg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19238603Sjoerg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20238603Sjoerg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21238603Sjoerg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22238603Sjoerg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23238603Sjoerg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24238603Sjoerg * SUCH DAMAGE. 25238603Sjoerg * 26238603Sjoerg * $Id$ 27238603Sjoerg */ 28238603Sjoerg 29238603Sjoerg#include "pci.h" 30238603Sjoerg#include "intpm.h" 31238603Sjoerg 32238603Sjoerg#if NPCI > 0 33238603Sjoerg#if NINTPM >0 34238603Sjoerg/* I don't think the chip is used in other architecture. :-)*/ 35238603Sjoerg#include <sys/param.h> 36238603Sjoerg#include <sys/systm.h> 37238603Sjoerg#include <sys/kernel.h> 38238603Sjoerg 39257779Shselasky#include <machine/bus_pio.h> 40238603Sjoerg#include <machine/bus_memio.h> 41238603Sjoerg#include <machine/bus.h> 42238603Sjoerg 43238603Sjoerg#include <machine/clock.h> 44238603Sjoerg#include <sys/uio.h> 45238603Sjoerg#include <sys/module.h> 46257779Shselasky#include <sys/bus.h> 47257779Shselasky#include <sys/conf.h> 48238603Sjoerg#include <sys/malloc.h> 49238603Sjoerg#include <sys/buf.h> 50238603Sjoerg 51238603Sjoerg#include <dev/smbus/smbconf.h> 52238603Sjoerg 53238603Sjoerg#include "smbus_if.h" 54238603Sjoerg 55238603Sjoerg/*This should be removed if force_pci_map_int supported*/ 56238603Sjoerg#include <sys/interrupt.h> 57238603Sjoerg 58238603Sjoerg#include <pci/pcireg.h> 59238603Sjoerg#include <pci/pcivar.h> 60238603Sjoerg#include <pci/intpmreg.h> 61238603Sjoerg 62238603Sjoerg#include "opt_intpm.h" 63238603Sjoerg 64238603Sjoergstatic struct _pcsid 65238603Sjoerg{ 66238603Sjoerg pcidi_t type; 67238603Sjoerg char *desc; 68238603Sjoerg} pci_ids[] = 69238603Sjoerg{ 70238603Sjoerg { 0x71138086,"Intel 82371AB Power management controller"}, 71238603Sjoerg 72238603Sjoerg { 0x00000000, NULL } 73238603Sjoerg}; 74238603Sjoergstatic int intsmb_probe(device_t); 75238603Sjoergstatic int intsmb_attach(device_t); 76238603Sjoergstatic void intsmb_print_child(device_t, device_t); 77238603Sjoerg 78238603Sjoergstatic int intsmb_intr(device_t dev); 79238603Sjoergstatic int intsmb_slvintr(device_t dev); 80238603Sjoergstatic void intsmb_alrintr(device_t dev); 81238603Sjoergstatic int intsmb_callback(device_t dev, int index, caddr_t data); 82238603Sjoergstatic int intsmb_quick(device_t dev, u_char slave, int how); 83238603Sjoergstatic int intsmb_sendb(device_t dev, u_char slave, char byte); 84238603Sjoergstatic int intsmb_recvb(device_t dev, u_char slave, char *byte); 85238603Sjoergstatic int intsmb_writeb(device_t dev, u_char slave, char cmd, char byte); 86238603Sjoergstatic int intsmb_writew(device_t dev, u_char slave, char cmd, short word); 87238603Sjoergstatic int intsmb_readb(device_t dev, u_char slave, char cmd, char *byte); 88238603Sjoergstatic int intsmb_readw(device_t dev, u_char slave, char cmd, short *word); 89238603Sjoergstatic int intsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata); 90257779Shselaskystatic int intsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf); 91238603Sjoergstatic int intsmb_bread(device_t dev, u_char slave, char cmd, u_char count, char *buf); 92238603Sjoergstatic void intsmb_start(device_t dev,u_char cmd,int nointr); 93238603Sjoergstatic int intsmb_stop(device_t dev); 94238603Sjoergstatic int intsmb_stop_poll(device_t dev); 95238603Sjoergstatic int intsmb_free(device_t dev); 96238603Sjoergstatic struct intpm_pci_softc *intpm_alloc(int unit); 97238603Sjoergstatic const char* intpm_probe __P((pcici_t tag, pcidi_t type)); 98238603Sjoergstatic void intpm_attach __P((pcici_t config_id, int unit)); 99238603Sjoergstatic devclass_t intsmb_devclass; 100257779Shselasky 101238603Sjoergstatic device_method_t intpm_methods[]={ 102238603Sjoerg DEVMETHOD(device_probe,intsmb_probe), 103238603Sjoerg DEVMETHOD(device_attach,intsmb_attach), 104238603Sjoerg 105238603Sjoerg DEVMETHOD(bus_print_child, intsmb_print_child), 106238603Sjoerg 107238603Sjoerg DEVMETHOD(smbus_callback,intsmb_callback), 108238603Sjoerg DEVMETHOD(smbus_quick,intsmb_quick), 109238603Sjoerg DEVMETHOD(smbus_sendb,intsmb_sendb), 110238603Sjoerg DEVMETHOD(smbus_recvb,intsmb_recvb), 111238603Sjoerg DEVMETHOD(smbus_writeb,intsmb_writeb), 112238603Sjoerg DEVMETHOD(smbus_writew,intsmb_writew), 113238603Sjoerg DEVMETHOD(smbus_readb,intsmb_readb), 114238603Sjoerg DEVMETHOD(smbus_readw,intsmb_readw), 115238603Sjoerg DEVMETHOD(smbus_pcall,intsmb_pcall), 116238603Sjoerg DEVMETHOD(smbus_bwrite,intsmb_bwrite), 117238603Sjoerg DEVMETHOD(smbus_bread,intsmb_bread), 118238603Sjoerg {0,0} 119238603Sjoerg}; 120238603Sjoerg 121238603Sjoergstatic struct intpm_pci_softc{ 122238603Sjoerg bus_space_tag_t smbst; 123238603Sjoerg bus_space_handle_t smbsh; 124238603Sjoerg bus_space_tag_t pmst; 125238603Sjoerg bus_space_handle_t pmsh; 126238603Sjoerg pcici_t cfg; 127238603Sjoerg device_t smbus; 128238603Sjoerg}intpm_pci[NINTPM]; 129238603Sjoerg 130257779Shselasky 131238603Sjoergstruct intsmb_softc{ 132238603Sjoerg struct intpm_pci_softc *pci_sc; 133238603Sjoerg bus_space_tag_t st; 134238603Sjoerg bus_space_handle_t sh; 135238603Sjoerg device_t smbus; 136238603Sjoerg int isbusy; 137238603Sjoerg}; 138238603Sjoergstatic driver_t intpm_driver = { 139238603Sjoerg "intsmb", 140238603Sjoerg intpm_methods, 141238603Sjoerg DRIVER_TYPE_MISC, 142238603Sjoerg sizeof(struct intsmb_softc), 143238603Sjoerg}; 144238603Sjoergstatic u_long intpm_count ; 145238603Sjoerg 146238603Sjoergstatic struct pci_device intpm_device = { 147238603Sjoerg "intpm", 148238603Sjoerg intpm_probe, 149238603Sjoerg intpm_attach, 150257779Shselasky &intpm_count 151238603Sjoerg}; 152238603Sjoerg 153238603SjoergDATA_SET (pcidevice_set, intpm_device); 154238603Sjoerg 155238603Sjoergstatic int 156238603Sjoergintsmb_probe(device_t dev) 157238603Sjoerg{ 158238603Sjoerg struct intsmb_softc *sc =(struct intsmb_softc *) device_get_softc(dev); 159257779Shselasky sc->smbus=smbus_alloc_bus(dev); 160238603Sjoerg if (!sc->smbus) 161238603Sjoerg return (EINVAL); /* XXX don't know what to return else */ 162238603Sjoerg device_set_desc(dev,"Intel PIIX4 SMBUS Interface"); 163238603Sjoerg 164238603Sjoerg return (0); /* XXX don't know what to return else */ 165238603Sjoerg} 166238603Sjoergstatic int 167238603Sjoergintsmb_attach(device_t dev) 168238603Sjoerg{ 169257779Shselasky struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev); 170238603Sjoerg sc->pci_sc=&intpm_pci[device_get_unit(dev)]; 171238603Sjoerg sc->isbusy=0; 172238603Sjoerg sc->sh=sc->pci_sc->smbsh; 173238603Sjoerg sc->st=sc->pci_sc->smbst; 174238603Sjoerg sc->pci_sc->smbus=dev; 175238603Sjoerg device_probe_and_attach(sc->smbus); 176238603Sjoerg#ifdef ENABLE_ALART 177238603Sjoerg /*Enable Arart*/ 178238603Sjoerg bus_space_write_1(sc->st,sc->sh,PIIX4_SMBSLVCNT, 179238603Sjoerg PIIX4_SMBSLVCNT_ALTEN); 180238603Sjoerg#endif 181238603Sjoerg return (0); 182238603Sjoerg} 183238603Sjoerg 184238603Sjoergstatic void 185238603Sjoergintsmb_print_child(device_t bus, device_t dev) 186238603Sjoerg{ 187238603Sjoerg printf(" on %s%d", device_get_name(bus), device_get_unit(bus)); 188238603Sjoerg return; 189238603Sjoerg} 190238603Sjoergstatic int 191238603Sjoergintsmb_callback(device_t dev, int index, caddr_t data) 192238603Sjoerg{ 193238603Sjoerg int error = 0; 194238603Sjoerg intrmask_t s; 195238603Sjoerg s=splnet(); 196238603Sjoerg switch (index) { 197238603Sjoerg case SMB_REQUEST_BUS: 198238603Sjoerg break; 199238603Sjoerg case SMB_RELEASE_BUS: 200238603Sjoerg break; 201238603Sjoerg default: 202238603Sjoerg error = EINVAL; 203238603Sjoerg } 204238603Sjoerg splx(s); 205238603Sjoerg return (error); 206238603Sjoerg} 207238603Sjoerg/*counterpart of smbtx_smb_free*/ 208238603Sjoergstatic int 209238603Sjoergintsmb_free(device_t dev){ 210238603Sjoerg struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev); 211238603Sjoerg if((bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTSTS)& 212238603Sjoerg PIIX4_SMBHSTSTAT_BUSY) 213238603Sjoerg#ifdef ENABLE_ALART 214238603Sjoerg ||(bus_space_read_1(sc->st,sc->sh,PIIX4_SMBSLVSTS)& 215238603Sjoerg PIIX4_SMBSLVSTS_BUSY) 216238603Sjoerg#endif 217238603Sjoerg || sc->isbusy) 218238603Sjoerg return EBUSY; 219238603Sjoerg sc->isbusy=1; 220238603Sjoerg /*Disable Intrrupt in slave part*/ 221238603Sjoerg#ifndef ENABLE_ALART 222238603Sjoerg bus_space_write_1(sc->st,sc->sh,PIIX4_SMBSLVCNT,0); 223238603Sjoerg#endif 224238603Sjoerg /*Reset INTR Flag to prepare INTR*/ 225238603Sjoerg bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTSTS, 226238603Sjoerg (PIIX4_SMBHSTSTAT_INTR| 227238603Sjoerg PIIX4_SMBHSTSTAT_ERR| 228238603Sjoerg PIIX4_SMBHSTSTAT_BUSC| 229238603Sjoerg PIIX4_SMBHSTSTAT_FAIL) 230238603Sjoerg ); 231238603Sjoerg return 0; 232238603Sjoerg} 233238603Sjoerg 234238603Sjoergstatic int 235238603Sjoergintsmb_intr(device_t dev) 236238603Sjoerg{ 237238603Sjoerg struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev); 238238603Sjoerg int status; 239238603Sjoerg intrmask_t s; 240238603Sjoerg status=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTSTS); 241238603Sjoerg if(status&PIIX4_SMBHSTSTAT_BUSY){ 242238603Sjoerg return 1; 243238603Sjoerg 244238603Sjoerg } 245238603Sjoerg s=splhigh(); 246238603Sjoerg if(sc->isbusy&&(status&(PIIX4_SMBHSTSTAT_INTR| 247238603Sjoerg PIIX4_SMBHSTSTAT_ERR| 248238603Sjoerg PIIX4_SMBHSTSTAT_BUSC| 249238603Sjoerg PIIX4_SMBHSTSTAT_FAIL))){ 250238603Sjoerg int tmp; 251238603Sjoerg sc->isbusy=0; 252238603Sjoerg tmp=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTCNT); 253238603Sjoerg bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCNT, 254238603Sjoerg tmp&~PIIX4_SMBHSTCNT_INTREN); 255238603Sjoerg splx(s); 256238603Sjoerg wakeup(sc); 257238603Sjoerg return 0; 258238603Sjoerg } 259238603Sjoerg splx(s); 260238603Sjoerg return 1;/* Not Completed*/ 261238603Sjoerg} 262238603Sjoergstatic int 263238603Sjoergintsmb_slvintr(device_t dev) 264238603Sjoerg{ 265238603Sjoerg struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev); 266238603Sjoerg int status,retval; 267238603Sjoerg retval=1; 268238603Sjoerg status=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBSLVSTS); 269238603Sjoerg if(status&PIIX4_SMBSLVSTS_BUSY) 270238603Sjoerg return retval; 271238603Sjoerg if(status&PIIX4_SMBSLVSTS_ALART){ 272238603Sjoerg intsmb_alrintr(dev); 273238603Sjoerg retval=0; 274238603Sjoerg }else if(status&~(PIIX4_SMBSLVSTS_ALART|PIIX4_SMBSLVSTS_SDW2 275238603Sjoerg |PIIX4_SMBSLVSTS_SDW1)){ 276238603Sjoerg retval=0; 277238603Sjoerg } 278238603Sjoerg /*Reset Status Register*/ 279238603Sjoerg bus_space_write_1(sc->st,sc->sh,PIIX4_SMBSLVSTS,PIIX4_SMBSLVSTS_ALART| 280238603Sjoerg PIIX4_SMBSLVSTS_SDW2|PIIX4_SMBSLVSTS_SDW1| 281238603Sjoerg PIIX4_SMBSLVSTS_SLV); 282238603Sjoerg return retval; 283238603Sjoerg} 284238603Sjoerg 285238603Sjoergstatic void intsmb_alrintr(device_t dev) 286238603Sjoerg{ 287238603Sjoerg struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev); 288238603Sjoerg int slvcnt; 289238603Sjoerg /*stop generating INTR from ALART*/ 290238603Sjoerg slvcnt=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBSLVCNT); 291238603Sjoerg#ifdef ENABLE_ALART 292238603Sjoerg bus_space_write_1(sc->st,sc->sh,PIIX4_SMBSLVCNT, 293238603Sjoerg slvcnt&~PIIX4_SMBSLVCNT_ALTEN) ; 294238603Sjoerg#endif 295238603Sjoerg DELAY(5); 296238603Sjoerg /*ask bus who assert it and then ask it what's the matter. */ 297238603Sjoerg#ifdef ENABLE_ALART 298238603Sjoerg error=intsmb_free(dev); 299238603Sjoerg if(!error){ 300238603Sjoerg bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,SMBALTRESP 301238603Sjoerg |LSB); 302238603Sjoerg intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_BYTE,1); 303238603Sjoerg if(!(error=intsmb_stop_poll(dev))){ 304238603Sjoerg addr=bus_space_read_1(sc->st,sc->sh, 305238603Sjoerg PIIX4_SMBHSTDAT0); 306238603Sjoerg printf("ALART_RESPONSE: %x\n",(int) addr); 307238603Sjoerg } 308238603Sjoerg }else{ 309238603Sjoerg printf("ERROR\n"); 310238603Sjoerg } 311238603Sjoerg 312238603Sjoerg /*Re-enable INTR from ALART*/ 313238603Sjoerg bus_space_write_1(sc->st,sc->sh,PIIX4_SMBSLVCNT, 314238603Sjoerg slvcnt|PIIX4_SMBSLVCNT_ALTEN) ; 315238603Sjoerg DELAY(5); 316238603Sjoerg#endif 317238603Sjoerg 318238603Sjoerg return; 319238603Sjoerg} 320238603Sjoergstatic void 321238603Sjoergintsmb_start(device_t dev,unsigned char cmd,int nointr) 322238603Sjoerg{ 323238603Sjoerg struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev); 324238603Sjoerg unsigned char tmp; 325238603Sjoerg tmp=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTCNT); 326238603Sjoerg tmp&= 0xe0; 327238603Sjoerg tmp |= cmd; 328238603Sjoerg tmp |=PIIX4_SMBHSTCNT_START; 329238603Sjoerg /*While not in autoconfiguration Intrrupt Enabled*/ 330238603Sjoerg if(!cold||!nointr) 331238603Sjoerg tmp |=PIIX4_SMBHSTCNT_INTREN; 332238603Sjoerg bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCNT,tmp); 333238603Sjoerg} 334238603Sjoerg 335238603Sjoerg/*Polling Code. Polling is not encouraged 336238603Sjoerg * because It is required to wait for the device get busy. 337238603Sjoerg *(29063505.pdf from Intel) 338238603Sjoerg * But during boot,intrrupt cannot be used. 339238603Sjoerg * so use polling code while in autoconfiguration. 340238603Sjoerg */ 341238603Sjoerg 342238603Sjoergstatic int 343238603Sjoergintsmb_stop_poll(device_t dev){ 344238603Sjoerg int error,i; 345238603Sjoerg struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev); 346238603Sjoerg /* 347238603Sjoerg * In smbtx driver ,Simply waiting. 348238603Sjoerg * This loops 100-200 times. 349238603Sjoerg */ 350238603Sjoerg for(i=0;i<0x7fff;i++){ 351238603Sjoerg if((bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTSTS) 352238603Sjoerg &PIIX4_SMBHSTSTAT_BUSY)){ 353238603Sjoerg break; 354238603Sjoerg } 355238603Sjoerg } 356238603Sjoerg for(i=0;i<0x7fff;i++){ 357238603Sjoerg int status; 358238603Sjoerg status=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTSTS); 359238603Sjoerg if(!(status&PIIX4_SMBHSTSTAT_BUSY)){ 360238603Sjoerg sc->isbusy=0; 361238603Sjoerg error=(status&PIIX4_SMBHSTSTAT_ERR)?EIO : 362238603Sjoerg (status&PIIX4_SMBHSTSTAT_BUSC)?EBUSY: 363238603Sjoerg (status&PIIX4_SMBHSTSTAT_FAIL)?EIO:0; 364238603Sjoerg if(error==0&&!(status&PIIX4_SMBHSTSTAT_INTR)){ 365238603Sjoerg printf("unknown cause why?"); 366238603Sjoerg } 367238603Sjoerg return error; 368238603Sjoerg } 369238603Sjoerg } 370238603Sjoerg sc->isbusy=0; 371238603Sjoerg return EIO; 372238603Sjoerg} 373238603Sjoerg/* 374238603Sjoerg *wait for completion and return result. 375238603Sjoerg */ 376238603Sjoergstatic int 377238603Sjoergintsmb_stop(device_t dev){ 378238603Sjoerg int error; 379238603Sjoerg struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev); 380238603Sjoerg if(cold){ 381238603Sjoerg /*So that it can use device during probing device on SMBus.*/ 382238603Sjoerg error=intsmb_stop_poll(dev); 383238603Sjoerg return error; 384238603Sjoerg }else{ 385238603Sjoerg if(!tsleep(sc,(PWAIT)|PCATCH,"SMBWAI",hz/8)){ 386238603Sjoerg int status; 387238603Sjoerg status=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTSTS); 388238603Sjoerg if(!(status&PIIX4_SMBHSTSTAT_BUSY)){ 389238603Sjoerg error=(status&PIIX4_SMBHSTSTAT_ERR)?EIO : 390238603Sjoerg (status&PIIX4_SMBHSTSTAT_BUSC)?EBUSY: 391238603Sjoerg (status&PIIX4_SMBHSTSTAT_FAIL)?EIO:0; 392238603Sjoerg if(error==0&&!(status&PIIX4_SMBHSTSTAT_INTR)){ 393238603Sjoerg printf("intsmb%d:unknown cause why?\n", 394238603Sjoerg device_get_unit(dev)); 395238603Sjoerg } 396238603Sjoerg#ifdef ENABLE_ALART 397238603Sjoerg bus_space_write_1(sc->st,sc->sh, 398238603Sjoerg PIIX4_SMBSLVCNT,PIIX4_SMBSLVCNT_ALTEN); 399238603Sjoerg#endif 400238603Sjoerg return error; 401238603Sjoerg } 402238603Sjoerg } 403238603Sjoerg } 404238603Sjoerg /*Timeout Procedure*/ 405238603Sjoerg sc->isbusy=0; 406238603Sjoerg /*Re-enable supressed intrrupt from slave part*/ 407238603Sjoerg bus_space_write_1(sc->st,sc->sh, 408238603Sjoerg PIIX4_SMBSLVCNT,PIIX4_SMBSLVCNT_ALTEN); 409238603Sjoerg return EIO; 410238603Sjoerg} 411238603Sjoerg 412238603Sjoergstatic int 413238603Sjoergintsmb_quick(device_t dev, u_char slave, int how) 414238603Sjoerg{ 415238603Sjoerg int error=0; 416 u_char data; 417 struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev); 418 data=slave; 419 /*Quick command is part of Address, I think*/ 420 switch(how){ 421 case SMB_QWRITE: 422 data&=~LSB; 423 break; 424 case SMB_QREAD: 425 data|=LSB; 426 break; 427 default: 428 error=EINVAL; 429 } 430 if(!error){ 431 error=intsmb_free(dev); 432 if(!error){ 433 bus_space_write_1(sc->st,sc->sh, 434 PIIX4_SMBHSTADD,data); 435 intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_QUICK,0); 436 error=intsmb_stop(dev); 437 } 438 } 439 440 return (error); 441} 442 443static int 444intsmb_sendb(device_t dev, u_char slave, char byte) 445{ 446 int error; 447 struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev); 448 error=intsmb_free(dev); 449 if(!error){ 450 bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave&~LSB); 451 bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,byte); 452 intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_BYTE,0); 453 error=intsmb_stop(dev); 454 } 455 return (error); 456} 457static int 458intsmb_recvb(device_t dev, u_char slave, char *byte) 459{ 460 int error; 461 struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev); 462 error=intsmb_free(dev); 463 if(!error){ 464 bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave 465 |LSB); 466 intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_BYTE,0); 467 if(!(error=intsmb_stop(dev))){ 468#ifdef RECV_IS_IN_CMD 469 /*Linux SMBus stuff also troubles 470 Because Intel's datasheet will not make clear. 471 */ 472 *byte=bus_space_read_1(sc->st,sc->sh, 473 PIIX4_SMBHSTCMD); 474#else 475 *byte=bus_space_read_1(sc->st,sc->sh, 476 PIIX4_SMBHSTDAT0); 477#endif 478 } 479 } 480 return (error); 481} 482static int 483intsmb_writeb(device_t dev, u_char slave, char cmd, char byte) 484{ 485 int error; 486 struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev); 487 error=intsmb_free(dev); 488 if(!error){ 489 bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave&~LSB); 490 bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,cmd); 491 bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTDAT0,byte); 492 intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_BDATA,0); 493 error=intsmb_stop(dev); 494 } 495 return (error); 496} 497static int 498intsmb_writew(device_t dev, u_char slave, char cmd, short word) 499{ 500 int error; 501 struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev); 502 error=intsmb_free(dev); 503 if(!error){ 504 bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave&~LSB); 505 bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,cmd); 506 bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTDAT0, 507 word&0xff); 508 bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTDAT1, 509 (word>>8)&0xff); 510 intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_WDATA,0); 511 error=intsmb_stop(dev); 512 } 513 return (error); 514} 515 516static int 517intsmb_readb(device_t dev, u_char slave, char cmd, char *byte) 518{ 519 int error; 520 struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev); 521 error=intsmb_free(dev); 522 if(!error){ 523 bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave|LSB); 524 bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,cmd); 525 intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_BDATA,0); 526 if(!(error=intsmb_stop(dev))){ 527 *byte=bus_space_read_1(sc->st,sc->sh, 528 PIIX4_SMBHSTDAT0); 529 } 530 } 531 return (error); 532} 533static int 534intsmb_readw(device_t dev, u_char slave, char cmd, short *word) 535{ 536 int error; 537 struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev); 538 error=intsmb_free(dev); 539 if(!error){ 540 bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave|LSB); 541 bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,cmd); 542 intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_WDATA,0); 543 if(!(error=intsmb_stop(dev))){ 544 *word=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTDAT0)&0xff; 545 *word|=(bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTDAT1)&0xff)<<8; 546 } 547 } 548 return (error); 549} 550/* 551 * Data sheet claims that it implements all function, but also claims 552 * that it implements 7 function and not mention PCALL. So I don't know 553 * whether it will work. 554 */ 555static int 556intsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata) 557{ 558#ifdef PROCCALL_TEST 559 int error; 560 struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev); 561 error=intsmb_free(dev); 562 if(!error){ 563 bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave&~LSB); 564 bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,cmd); 565 bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTDAT0,sdata&0xff); 566 bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTDAT1,(sdata&0xff)>>8); 567 intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_WDATA,0); 568 } 569 if(!(error=intsmb_stop(dev))){ 570 *rdata=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTDAT0)&0xff; 571 *rdata|=(bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTDAT1)&0xff)<<8; 572 } 573 return error; 574#else 575 return 0; 576#endif 577} 578static int 579intsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf) 580{ 581 int error,i; 582 struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev); 583 error=intsmb_free(dev); 584 if(count>SMBBLOCKTRANS_MAX||count==0) 585 error=EINVAL; 586 if(!error){ 587 /*Reset internal array index*/ 588 bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTCNT); 589 590 bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave&~LSB); 591 bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,cmd); 592 for(i=0;i<count;i++){ 593 bus_space_write_1(sc->st,sc->sh,PIIX4_SMBBLKDAT,buf[i]); 594 } 595 bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTDAT0,count); 596 intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_BLOCK,0); 597 error=intsmb_stop(dev); 598 } 599 return (error); 600} 601 602static int 603intsmb_bread(device_t dev, u_char slave, char cmd, u_char count, char *buf) 604{ 605 int error,i; 606 struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev); 607 error=intsmb_free(dev); 608 if(count>SMBBLOCKTRANS_MAX||count==0) 609 error=EINVAL; 610 if(!error){ 611 /*Reset internal array index*/ 612 bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTCNT); 613 614 bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave|LSB); 615 bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,cmd); 616 bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTDAT0,count); 617 intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_BLOCK,0); 618 error=intsmb_stop(dev); 619 if(!error){ 620 bzero(buf,count);/*Is it needed?*/ 621 count= bus_space_read_1(sc->st,sc->sh, 622 PIIX4_SMBHSTDAT0); 623 if(count!=0&&count<=SMBBLOCKTRANS_MAX){ 624 for(i=0;i<count;i++){ 625 buf[i]=bus_space_read_1(sc->st, 626 sc->sh, 627 PIIX4_SMBBLKDAT); 628 } 629 } 630 else{ 631 error=EIO; 632 } 633 } 634 } 635 return (error); 636} 637 638DRIVER_MODULE(intsmb, root , intpm_driver, intsmb_devclass, 0, 0); 639 640 641static void intpm_intr __P((void *arg)); 642 643static const char* 644intpm_probe (pcici_t tag, pcidi_t type) 645{ 646 struct _pcsid *ep =pci_ids; 647 while (ep->type && ep->type != type) 648 ++ep; 649 return (ep->desc); 650} 651 652static struct intpm_pci_softc *intpm_alloc(int unit){ 653 if(unit<NINTPM) 654 return &intpm_pci[unit]; 655 else 656 return NULL; 657} 658 659/*Same as pci_map_int but this ignores INTPIN*/ 660static int force_pci_map_int(pcici_t cfg, pci_inthand_t *func, void *arg, unsigned *maskptr) 661{ 662 int error; 663#ifdef APIC_IO 664 int nextpin, muxcnt; 665#endif 666 /* Spec sheet claims that it use IRQ 9*/ 667 int irq = 9; 668 void *dev_instance = (void *)-1; /* XXX use cfg->devdata */ 669 void *idesc; 670 671 idesc = intr_create(dev_instance, irq, func, arg, maskptr, 0); 672 error = intr_connect(idesc); 673 if (error != 0) 674 return 0; 675#ifdef APIC_IO 676 nextpin = next_apic_irq(irq); 677 678 if (nextpin < 0) 679 return 1; 680 681 /* 682 * Attempt handling of some broken mp tables. 683 * 684 * It's OK to yell (since the mp tables are broken). 685 * 686 * Hanging in the boot is not OK 687 */ 688 689 muxcnt = 2; 690 nextpin = next_apic_irq(nextpin); 691 while (muxcnt < 5 && nextpin >= 0) { 692 muxcnt++; 693 nextpin = next_apic_irq(nextpin); 694 } 695 if (muxcnt >= 5) { 696 printf("bogus MP table, more than 4 IO APIC pins connected to the same PCI device or ISA/EISA interrupt\n"); 697 return 0; 698 } 699 700 printf("bogus MP table, %d IO APIC pins connected to the same PCI device or ISA/EISA interrupt\n", muxcnt); 701 702 nextpin = next_apic_irq(irq); 703 while (nextpin >= 0) { 704 idesc = intr_create(dev_instance, nextpin, func, arg, 705 maskptr, 0); 706 error = intr_connect(idesc); 707 if (error != 0) 708 return 0; 709 printf("Registered extra interrupt handler for int %d (in addition to int %d)\n", nextpin, irq); 710 nextpin = next_apic_irq(nextpin); 711 } 712#endif 713 return 1; 714} 715static void 716intpm_attach(config_id, unit) 717 pcici_t config_id; 718 int unit; 719{ 720 int value; 721 722 char * str; 723 { 724 struct intpm_pci_softc *sciic; 725 device_t smbinterface; 726 value=pci_cfgread(config_id,PCI_BASE_ADDR_SMB,4); 727 sciic=intpm_alloc(unit); 728 if(sciic==NULL){ 729 return; 730 } 731 732 sciic->smbst=(value&1)?I386_BUS_SPACE_IO:I386_BUS_SPACE_MEM; 733 734 /*Calling pci_map_port is better.But bus_space_handle_t != 735 * pci_port_t, so I don't call support routine while 736 * bus_space_??? support routine will be appear. 737 */ 738 sciic->smbsh=value&(~1); 739 if(sciic->smbsh==I386_BUS_SPACE_MEM){ 740 /*According to the spec, this will not occur*/ 741 int dummy; 742 pci_map_mem(config_id,PCI_BASE_ADDR_SMB,&sciic->smbsh,&dummy); 743 } 744 printf("intpm%d: %s %x ",unit, 745 (sciic->smbst==I386_BUS_SPACE_IO)?"I/O mapped":"Memory", 746 sciic->smbsh); 747#ifndef NO_CHANGE_PCICONF 748 pci_cfgwrite(config_id,PCIR_INTLINE,0x09,1); 749 pci_cfgwrite(config_id,PCI_HST_CFG_SMB, 750 PCI_INTR_SMB_IRQ9|PCI_INTR_SMB_ENABLE,1); 751#endif 752 config_id->intline=pci_cfgread(config_id,PCIR_INTLINE,1); 753 printf("ALLOCED IRQ %d ",config_id->intline); 754 value=pci_cfgread(config_id,PCI_HST_CFG_SMB,1); 755 switch(value&0xe){ 756 case PCI_INTR_SMB_SMI: 757 str="SMI"; 758 break; 759 case PCI_INTR_SMB_IRQ9: 760 str="IRQ 9"; 761 break; 762 default: 763 str="BOGUS"; 764 } 765 printf("intr %s %s ",str,((value&1)? "enabled":"disabled")); 766 value=pci_cfgread(config_id,PCI_REVID_SMB,1); 767 printf("revision %d\n",value); 768 /* 769 * Install intr HANDLER here 770 */ 771 if(force_pci_map_int(config_id,intpm_intr,sciic,&net_imask)==0){ 772 printf("intpm%d: Failed to map intr\n",unit); 773 } 774 smbinterface=device_add_child(root_bus,"intsmb",unit,NULL); 775 device_probe_and_attach(smbinterface); 776 } 777 value=pci_cfgread(config_id,PCI_BASE_ADDR_PM,4); 778 printf("intpm%d: PM %s %x \n",unit,(value&1)?"I/O mapped":"Memory",value&0xfffe); 779 return; 780} 781static void intpm_intr(void *arg) 782{ 783 struct intpm_pci_softc *sc; 784 sc=(struct intpm_pci_softc *)arg; 785 intsmb_intr(sc->smbus); 786 intsmb_slvintr(sc->smbus); 787} 788#endif /* NPCI > 0 */ 789#endif 790