intpm.c revision 43166
143166Snsouch/*- 243166Snsouch * Copyright (c) 1998, 1999 Takanori Watanabe 343166Snsouch * All rights reserved. 443166Snsouch * 543166Snsouch * Redistribution and use in source and binary forms, with or without 643166Snsouch * modification, are permitted provided that the following conditions 743166Snsouch * are met: 843166Snsouch * 1. Redistributions of source code must retain the above copyright 943166Snsouch * notice, this list of conditions and the following disclaimer. 1043166Snsouch * 2. Redistributions in binary form must reproduce the above copyright 1143166Snsouch * notice, this list of conditions and the following disclaimer in the 1243166Snsouch * documentation and/or other materials provided with the distribution. 1343166Snsouch * 1443166Snsouch * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1543166Snsouch * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1643166Snsouch * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1743166Snsouch * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1843166Snsouch * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1943166Snsouch * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2043166Snsouch * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2143166Snsouch * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2243166Snsouch * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2343166Snsouch * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2443166Snsouch * SUCH DAMAGE. 2543166Snsouch * 2643166Snsouch * $Id$ 2743166Snsouch */ 2843166Snsouch 2943166Snsouch#include "pci.h" 3043166Snsouch#include "intpm.h" 3143166Snsouch 3243166Snsouch#if NPCI > 0 3343166Snsouch#if NINTPM >0 3443166Snsouch/* I don't think the chip is used in other architecture. :-)*/ 3543166Snsouch#include <sys/param.h> 3643166Snsouch#include <sys/systm.h> 3743166Snsouch#include <sys/kernel.h> 3843166Snsouch 3943166Snsouch#include <machine/bus_pio.h> 4043166Snsouch#include <machine/bus_memio.h> 4143166Snsouch#include <machine/bus.h> 4243166Snsouch 4343166Snsouch#include <machine/clock.h> 4443166Snsouch#include <sys/uio.h> 4543166Snsouch#include <sys/module.h> 4643166Snsouch#include <sys/bus.h> 4743166Snsouch#include <sys/conf.h> 4843166Snsouch#include <sys/malloc.h> 4943166Snsouch#include <sys/buf.h> 5043166Snsouch 5143166Snsouch#include <dev/smbus/smbconf.h> 5243166Snsouch 5343166Snsouch#include "smbus_if.h" 5443166Snsouch 5543166Snsouch/*This should be removed if force_pci_map_int supported*/ 5643166Snsouch#include <sys/interrupt.h> 5743166Snsouch 5843166Snsouch#include <pci/pcireg.h> 5943166Snsouch#include <pci/pcivar.h> 6043166Snsouch#include <pci/intpmreg.h> 6143166Snsouch 6243166Snsouch#include "opt_intpm.h" 6343166Snsouch 6443166Snsouchstatic struct _pcsid 6543166Snsouch{ 6643166Snsouch pcidi_t type; 6743166Snsouch char *desc; 6843166Snsouch} pci_ids[] = 6943166Snsouch{ 7043166Snsouch { 0x71138086,"Intel 82371AB Power management controller"}, 7143166Snsouch 7243166Snsouch { 0x00000000, NULL } 7343166Snsouch}; 7443166Snsouchstatic int intsmb_probe(device_t); 7543166Snsouchstatic int intsmb_attach(device_t); 7643166Snsouchstatic void intsmb_print_child(device_t, device_t); 7743166Snsouch 7843166Snsouchstatic int intsmb_intr(device_t dev); 7943166Snsouchstatic int intsmb_slvintr(device_t dev); 8043166Snsouchstatic void intsmb_alrintr(device_t dev); 8143166Snsouchstatic int intsmb_callback(device_t dev, int index, caddr_t data); 8243166Snsouchstatic int intsmb_quick(device_t dev, u_char slave, int how); 8343166Snsouchstatic int intsmb_sendb(device_t dev, u_char slave, char byte); 8443166Snsouchstatic int intsmb_recvb(device_t dev, u_char slave, char *byte); 8543166Snsouchstatic int intsmb_writeb(device_t dev, u_char slave, char cmd, char byte); 8643166Snsouchstatic int intsmb_writew(device_t dev, u_char slave, char cmd, short word); 8743166Snsouchstatic int intsmb_readb(device_t dev, u_char slave, char cmd, char *byte); 8843166Snsouchstatic int intsmb_readw(device_t dev, u_char slave, char cmd, short *word); 8943166Snsouchstatic int intsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata); 9043166Snsouchstatic int intsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf); 9143166Snsouchstatic int intsmb_bread(device_t dev, u_char slave, char cmd, u_char count, char *buf); 9243166Snsouchstatic void intsmb_start(device_t dev,u_char cmd,int nointr); 9343166Snsouchstatic int intsmb_stop(device_t dev); 9443166Snsouchstatic int intsmb_stop_poll(device_t dev); 9543166Snsouchstatic int intsmb_free(device_t dev); 9643166Snsouchstatic struct intpm_pci_softc *intpm_alloc(int unit); 9743166Snsouchstatic const char* intpm_probe __P((pcici_t tag, pcidi_t type)); 9843166Snsouchstatic void intpm_attach __P((pcici_t config_id, int unit)); 9943166Snsouchstatic devclass_t intsmb_devclass; 10043166Snsouch 10143166Snsouchstatic device_method_t intpm_methods[]={ 10243166Snsouch DEVMETHOD(device_probe,intsmb_probe), 10343166Snsouch DEVMETHOD(device_attach,intsmb_attach), 10443166Snsouch 10543166Snsouch DEVMETHOD(bus_print_child, intsmb_print_child), 10643166Snsouch 10743166Snsouch DEVMETHOD(smbus_callback,intsmb_callback), 10843166Snsouch DEVMETHOD(smbus_quick,intsmb_quick), 10943166Snsouch DEVMETHOD(smbus_sendb,intsmb_sendb), 11043166Snsouch DEVMETHOD(smbus_recvb,intsmb_recvb), 11143166Snsouch DEVMETHOD(smbus_writeb,intsmb_writeb), 11243166Snsouch DEVMETHOD(smbus_writew,intsmb_writew), 11343166Snsouch DEVMETHOD(smbus_readb,intsmb_readb), 11443166Snsouch DEVMETHOD(smbus_readw,intsmb_readw), 11543166Snsouch DEVMETHOD(smbus_pcall,intsmb_pcall), 11643166Snsouch DEVMETHOD(smbus_bwrite,intsmb_bwrite), 11743166Snsouch DEVMETHOD(smbus_bread,intsmb_bread), 11843166Snsouch {0,0} 11943166Snsouch}; 12043166Snsouch 12143166Snsouchstatic struct intpm_pci_softc{ 12243166Snsouch bus_space_tag_t smbst; 12343166Snsouch bus_space_handle_t smbsh; 12443166Snsouch bus_space_tag_t pmst; 12543166Snsouch bus_space_handle_t pmsh; 12643166Snsouch pcici_t cfg; 12743166Snsouch device_t smbus; 12843166Snsouch}intpm_pci[NINTPM]; 12943166Snsouch 13043166Snsouch 13143166Snsouchstruct intsmb_softc{ 13243166Snsouch struct intpm_pci_softc *pci_sc; 13343166Snsouch bus_space_tag_t st; 13443166Snsouch bus_space_handle_t sh; 13543166Snsouch device_t smbus; 13643166Snsouch int isbusy; 13743166Snsouch}; 13843166Snsouchstatic driver_t intpm_driver = { 13943166Snsouch "intsmb", 14043166Snsouch intpm_methods, 14143166Snsouch DRIVER_TYPE_MISC, 14243166Snsouch sizeof(struct intsmb_softc), 14343166Snsouch}; 14443166Snsouchstatic u_long intpm_count ; 14543166Snsouch 14643166Snsouchstatic struct pci_device intpm_device = { 14743166Snsouch "intpm", 14843166Snsouch intpm_probe, 14943166Snsouch intpm_attach, 15043166Snsouch &intpm_count 15143166Snsouch}; 15243166Snsouch 15343166SnsouchDATA_SET (pcidevice_set, intpm_device); 15443166Snsouch 15543166Snsouchstatic int 15643166Snsouchintsmb_probe(device_t dev) 15743166Snsouch{ 15843166Snsouch struct intsmb_softc *sc =(struct intsmb_softc *) device_get_softc(dev); 15943166Snsouch sc->smbus=smbus_alloc_bus(dev); 16043166Snsouch if (!sc->smbus) 16143166Snsouch return (EINVAL); /* XXX don't know what to return else */ 16243166Snsouch device_set_desc(dev,"Intel PIIX4 SMBUS Interface"); 16343166Snsouch 16443166Snsouch return (0); /* XXX don't know what to return else */ 16543166Snsouch} 16643166Snsouchstatic int 16743166Snsouchintsmb_attach(device_t dev) 16843166Snsouch{ 16943166Snsouch struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev); 17043166Snsouch sc->pci_sc=&intpm_pci[device_get_unit(dev)]; 17143166Snsouch sc->isbusy=0; 17243166Snsouch sc->sh=sc->pci_sc->smbsh; 17343166Snsouch sc->st=sc->pci_sc->smbst; 17443166Snsouch sc->pci_sc->smbus=dev; 17543166Snsouch device_probe_and_attach(sc->smbus); 17643166Snsouch#ifdef ENABLE_ALART 17743166Snsouch /*Enable Arart*/ 17843166Snsouch bus_space_write_1(sc->st,sc->sh,PIIX4_SMBSLVCNT, 17943166Snsouch PIIX4_SMBSLVCNT_ALTEN); 18043166Snsouch#endif 18143166Snsouch return (0); 18243166Snsouch} 18343166Snsouch 18443166Snsouchstatic void 18543166Snsouchintsmb_print_child(device_t bus, device_t dev) 18643166Snsouch{ 18743166Snsouch printf(" on %s%d", device_get_name(bus), device_get_unit(bus)); 18843166Snsouch return; 18943166Snsouch} 19043166Snsouchstatic int 19143166Snsouchintsmb_callback(device_t dev, int index, caddr_t data) 19243166Snsouch{ 19343166Snsouch int error = 0; 19443166Snsouch intrmask_t s; 19543166Snsouch s=splnet(); 19643166Snsouch switch (index) { 19743166Snsouch case SMB_REQUEST_BUS: 19843166Snsouch break; 19943166Snsouch case SMB_RELEASE_BUS: 20043166Snsouch break; 20143166Snsouch default: 20243166Snsouch error = EINVAL; 20343166Snsouch } 20443166Snsouch splx(s); 20543166Snsouch return (error); 20643166Snsouch} 20743166Snsouch/*counterpart of smbtx_smb_free*/ 20843166Snsouchstatic int 20943166Snsouchintsmb_free(device_t dev){ 21043166Snsouch struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev); 21143166Snsouch if((bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTSTS)& 21243166Snsouch PIIX4_SMBHSTSTAT_BUSY) 21343166Snsouch#ifdef ENABLE_ALART 21443166Snsouch ||(bus_space_read_1(sc->st,sc->sh,PIIX4_SMBSLVSTS)& 21543166Snsouch PIIX4_SMBSLVSTS_BUSY) 21643166Snsouch#endif 21743166Snsouch || sc->isbusy) 21843166Snsouch return EBUSY; 21943166Snsouch sc->isbusy=1; 22043166Snsouch /*Disable Intrrupt in slave part*/ 22143166Snsouch#ifndef ENABLE_ALART 22243166Snsouch bus_space_write_1(sc->st,sc->sh,PIIX4_SMBSLVCNT,0); 22343166Snsouch#endif 22443166Snsouch /*Reset INTR Flag to prepare INTR*/ 22543166Snsouch bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTSTS, 22643166Snsouch (PIIX4_SMBHSTSTAT_INTR| 22743166Snsouch PIIX4_SMBHSTSTAT_ERR| 22843166Snsouch PIIX4_SMBHSTSTAT_BUSC| 22943166Snsouch PIIX4_SMBHSTSTAT_FAIL) 23043166Snsouch ); 23143166Snsouch return 0; 23243166Snsouch} 23343166Snsouch 23443166Snsouchstatic int 23543166Snsouchintsmb_intr(device_t dev) 23643166Snsouch{ 23743166Snsouch struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev); 23843166Snsouch int status; 23943166Snsouch intrmask_t s; 24043166Snsouch status=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTSTS); 24143166Snsouch if(status&PIIX4_SMBHSTSTAT_BUSY){ 24243166Snsouch return 1; 24343166Snsouch 24443166Snsouch } 24543166Snsouch s=splhigh(); 24643166Snsouch if(sc->isbusy&&(status&(PIIX4_SMBHSTSTAT_INTR| 24743166Snsouch PIIX4_SMBHSTSTAT_ERR| 24843166Snsouch PIIX4_SMBHSTSTAT_BUSC| 24943166Snsouch PIIX4_SMBHSTSTAT_FAIL))){ 25043166Snsouch int tmp; 25143166Snsouch sc->isbusy=0; 25243166Snsouch tmp=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTCNT); 25343166Snsouch bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCNT, 25443166Snsouch tmp&~PIIX4_SMBHSTCNT_INTREN); 25543166Snsouch splx(s); 25643166Snsouch wakeup(sc); 25743166Snsouch return 0; 25843166Snsouch } 25943166Snsouch splx(s); 26043166Snsouch return 1;/* Not Completed*/ 26143166Snsouch} 26243166Snsouchstatic int 26343166Snsouchintsmb_slvintr(device_t dev) 26443166Snsouch{ 26543166Snsouch struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev); 26643166Snsouch int status,retval; 26743166Snsouch retval=1; 26843166Snsouch status=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBSLVSTS); 26943166Snsouch if(status&PIIX4_SMBSLVSTS_BUSY) 27043166Snsouch return retval; 27143166Snsouch if(status&PIIX4_SMBSLVSTS_ALART){ 27243166Snsouch intsmb_alrintr(dev); 27343166Snsouch retval=0; 27443166Snsouch }else if(status&~(PIIX4_SMBSLVSTS_ALART|PIIX4_SMBSLVSTS_SDW2 27543166Snsouch |PIIX4_SMBSLVSTS_SDW1)){ 27643166Snsouch retval=0; 27743166Snsouch } 27843166Snsouch /*Reset Status Register*/ 27943166Snsouch bus_space_write_1(sc->st,sc->sh,PIIX4_SMBSLVSTS,PIIX4_SMBSLVSTS_ALART| 28043166Snsouch PIIX4_SMBSLVSTS_SDW2|PIIX4_SMBSLVSTS_SDW1| 28143166Snsouch PIIX4_SMBSLVSTS_SLV); 28243166Snsouch return retval; 28343166Snsouch} 28443166Snsouch 28543166Snsouchstatic void intsmb_alrintr(device_t dev) 28643166Snsouch{ 28743166Snsouch struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev); 28843166Snsouch int slvcnt; 28943166Snsouch /*stop generating INTR from ALART*/ 29043166Snsouch slvcnt=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBSLVCNT); 29143166Snsouch#ifdef ENABLE_ALART 29243166Snsouch bus_space_write_1(sc->st,sc->sh,PIIX4_SMBSLVCNT, 29343166Snsouch slvcnt&~PIIX4_SMBSLVCNT_ALTEN) ; 29443166Snsouch#endif 29543166Snsouch DELAY(5); 29643166Snsouch /*ask bus who assert it and then ask it what's the matter. */ 29743166Snsouch#ifdef ENABLE_ALART 29843166Snsouch error=intsmb_free(dev); 29943166Snsouch if(!error){ 30043166Snsouch bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,SMBALTRESP 30143166Snsouch |LSB); 30243166Snsouch intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_BYTE,1); 30343166Snsouch if(!(error=intsmb_stop_poll(dev))){ 30443166Snsouch addr=bus_space_read_1(sc->st,sc->sh, 30543166Snsouch PIIX4_SMBHSTDAT0); 30643166Snsouch printf("ALART_RESPONSE: %x\n",(int) addr); 30743166Snsouch } 30843166Snsouch }else{ 30943166Snsouch printf("ERROR\n"); 31043166Snsouch } 31143166Snsouch 31243166Snsouch /*Re-enable INTR from ALART*/ 31343166Snsouch bus_space_write_1(sc->st,sc->sh,PIIX4_SMBSLVCNT, 31443166Snsouch slvcnt|PIIX4_SMBSLVCNT_ALTEN) ; 31543166Snsouch DELAY(5); 31643166Snsouch#endif 31743166Snsouch 31843166Snsouch return; 31943166Snsouch} 32043166Snsouchstatic void 32143166Snsouchintsmb_start(device_t dev,unsigned char cmd,int nointr) 32243166Snsouch{ 32343166Snsouch struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev); 32443166Snsouch unsigned char tmp; 32543166Snsouch tmp=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTCNT); 32643166Snsouch tmp&= 0xe0; 32743166Snsouch tmp |= cmd; 32843166Snsouch tmp |=PIIX4_SMBHSTCNT_START; 32943166Snsouch /*While not in autoconfiguration Intrrupt Enabled*/ 33043166Snsouch if(!cold||!nointr) 33143166Snsouch tmp |=PIIX4_SMBHSTCNT_INTREN; 33243166Snsouch bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCNT,tmp); 33343166Snsouch} 33443166Snsouch 33543166Snsouch/*Polling Code. Polling is not encouraged 33643166Snsouch * because It is required to wait for the device get busy. 33743166Snsouch *(29063505.pdf from Intel) 33843166Snsouch * But during boot,intrrupt cannot be used. 33943166Snsouch * so use polling code while in autoconfiguration. 34043166Snsouch */ 34143166Snsouch 34243166Snsouchstatic int 34343166Snsouchintsmb_stop_poll(device_t dev){ 34443166Snsouch int error,i; 34543166Snsouch struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev); 34643166Snsouch /* 34743166Snsouch * In smbtx driver ,Simply waiting. 34843166Snsouch * This loops 100-200 times. 34943166Snsouch */ 35043166Snsouch for(i=0;i<0x7fff;i++){ 35143166Snsouch if((bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTSTS) 35243166Snsouch &PIIX4_SMBHSTSTAT_BUSY)){ 35343166Snsouch break; 35443166Snsouch } 35543166Snsouch } 35643166Snsouch for(i=0;i<0x7fff;i++){ 35743166Snsouch int status; 35843166Snsouch status=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTSTS); 35943166Snsouch if(!(status&PIIX4_SMBHSTSTAT_BUSY)){ 36043166Snsouch sc->isbusy=0; 36143166Snsouch error=(status&PIIX4_SMBHSTSTAT_ERR)?EIO : 36243166Snsouch (status&PIIX4_SMBHSTSTAT_BUSC)?EBUSY: 36343166Snsouch (status&PIIX4_SMBHSTSTAT_FAIL)?EIO:0; 36443166Snsouch if(error==0&&!(status&PIIX4_SMBHSTSTAT_INTR)){ 36543166Snsouch printf("unknown cause why?"); 36643166Snsouch } 36743166Snsouch return error; 36843166Snsouch } 36943166Snsouch } 37043166Snsouch sc->isbusy=0; 37143166Snsouch return EIO; 37243166Snsouch} 37343166Snsouch/* 37443166Snsouch *wait for completion and return result. 37543166Snsouch */ 37643166Snsouchstatic int 37743166Snsouchintsmb_stop(device_t dev){ 37843166Snsouch int error; 37943166Snsouch struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev); 38043166Snsouch if(cold){ 38143166Snsouch /*So that it can use device during probing device on SMBus.*/ 38243166Snsouch error=intsmb_stop_poll(dev); 38343166Snsouch return error; 38443166Snsouch }else{ 38543166Snsouch if(!tsleep(sc,(PWAIT)|PCATCH,"SMBWAI",hz/8)){ 38643166Snsouch int status; 38743166Snsouch status=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTSTS); 38843166Snsouch if(!(status&PIIX4_SMBHSTSTAT_BUSY)){ 38943166Snsouch error=(status&PIIX4_SMBHSTSTAT_ERR)?EIO : 39043166Snsouch (status&PIIX4_SMBHSTSTAT_BUSC)?EBUSY: 39143166Snsouch (status&PIIX4_SMBHSTSTAT_FAIL)?EIO:0; 39243166Snsouch if(error==0&&!(status&PIIX4_SMBHSTSTAT_INTR)){ 39343166Snsouch printf("intsmb%d:unknown cause why?\n", 39443166Snsouch device_get_unit(dev)); 39543166Snsouch } 39643166Snsouch#ifdef ENABLE_ALART 39743166Snsouch bus_space_write_1(sc->st,sc->sh, 39843166Snsouch PIIX4_SMBSLVCNT,PIIX4_SMBSLVCNT_ALTEN); 39943166Snsouch#endif 40043166Snsouch return error; 40143166Snsouch } 40243166Snsouch } 40343166Snsouch } 40443166Snsouch /*Timeout Procedure*/ 40543166Snsouch sc->isbusy=0; 40643166Snsouch /*Re-enable supressed intrrupt from slave part*/ 40743166Snsouch bus_space_write_1(sc->st,sc->sh, 40843166Snsouch PIIX4_SMBSLVCNT,PIIX4_SMBSLVCNT_ALTEN); 40943166Snsouch return EIO; 41043166Snsouch} 41143166Snsouch 41243166Snsouchstatic int 41343166Snsouchintsmb_quick(device_t dev, u_char slave, int how) 41443166Snsouch{ 41543166Snsouch int error=0; 41643166Snsouch u_char data; 41743166Snsouch struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev); 41843166Snsouch data=slave; 41943166Snsouch /*Quick command is part of Address, I think*/ 42043166Snsouch switch(how){ 42143166Snsouch case SMB_QWRITE: 42243166Snsouch data&=~LSB; 42343166Snsouch break; 42443166Snsouch case SMB_QREAD: 42543166Snsouch data|=LSB; 42643166Snsouch break; 42743166Snsouch default: 42843166Snsouch error=EINVAL; 42943166Snsouch } 43043166Snsouch if(!error){ 43143166Snsouch error=intsmb_free(dev); 43243166Snsouch if(!error){ 43343166Snsouch bus_space_write_1(sc->st,sc->sh, 43443166Snsouch PIIX4_SMBHSTADD,data); 43543166Snsouch intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_QUICK,0); 43643166Snsouch error=intsmb_stop(dev); 43743166Snsouch } 43843166Snsouch } 43943166Snsouch 44043166Snsouch return (error); 44143166Snsouch} 44243166Snsouch 44343166Snsouchstatic int 44443166Snsouchintsmb_sendb(device_t dev, u_char slave, char byte) 44543166Snsouch{ 44643166Snsouch int error; 44743166Snsouch struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev); 44843166Snsouch error=intsmb_free(dev); 44943166Snsouch if(!error){ 45043166Snsouch bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave&~LSB); 45143166Snsouch bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,byte); 45243166Snsouch intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_BYTE,0); 45343166Snsouch error=intsmb_stop(dev); 45443166Snsouch } 45543166Snsouch return (error); 45643166Snsouch} 45743166Snsouchstatic int 45843166Snsouchintsmb_recvb(device_t dev, u_char slave, char *byte) 45943166Snsouch{ 46043166Snsouch int error; 46143166Snsouch struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev); 46243166Snsouch error=intsmb_free(dev); 46343166Snsouch if(!error){ 46443166Snsouch bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave 46543166Snsouch |LSB); 46643166Snsouch intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_BYTE,0); 46743166Snsouch if(!(error=intsmb_stop(dev))){ 46843166Snsouch#ifdef RECV_IS_IN_CMD 46943166Snsouch /*Linux SMBus stuff also troubles 47043166Snsouch Because Intel's datasheet will not make clear. 47143166Snsouch */ 47243166Snsouch *byte=bus_space_read_1(sc->st,sc->sh, 47343166Snsouch PIIX4_SMBHSTCMD); 47443166Snsouch#else 47543166Snsouch *byte=bus_space_read_1(sc->st,sc->sh, 47643166Snsouch PIIX4_SMBHSTDAT0); 47743166Snsouch#endif 47843166Snsouch } 47943166Snsouch } 48043166Snsouch return (error); 48143166Snsouch} 48243166Snsouchstatic int 48343166Snsouchintsmb_writeb(device_t dev, u_char slave, char cmd, char byte) 48443166Snsouch{ 48543166Snsouch int error; 48643166Snsouch struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev); 48743166Snsouch error=intsmb_free(dev); 48843166Snsouch if(!error){ 48943166Snsouch bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave&~LSB); 49043166Snsouch bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,cmd); 49143166Snsouch bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTDAT0,byte); 49243166Snsouch intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_BDATA,0); 49343166Snsouch error=intsmb_stop(dev); 49443166Snsouch } 49543166Snsouch return (error); 49643166Snsouch} 49743166Snsouchstatic int 49843166Snsouchintsmb_writew(device_t dev, u_char slave, char cmd, short word) 49943166Snsouch{ 50043166Snsouch int error; 50143166Snsouch struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev); 50243166Snsouch error=intsmb_free(dev); 50343166Snsouch if(!error){ 50443166Snsouch bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave&~LSB); 50543166Snsouch bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,cmd); 50643166Snsouch bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTDAT0, 50743166Snsouch word&0xff); 50843166Snsouch bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTDAT1, 50943166Snsouch (word>>8)&0xff); 51043166Snsouch intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_WDATA,0); 51143166Snsouch error=intsmb_stop(dev); 51243166Snsouch } 51343166Snsouch return (error); 51443166Snsouch} 51543166Snsouch 51643166Snsouchstatic int 51743166Snsouchintsmb_readb(device_t dev, u_char slave, char cmd, char *byte) 51843166Snsouch{ 51943166Snsouch int error; 52043166Snsouch struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev); 52143166Snsouch error=intsmb_free(dev); 52243166Snsouch if(!error){ 52343166Snsouch bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave|LSB); 52443166Snsouch bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,cmd); 52543166Snsouch intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_BDATA,0); 52643166Snsouch if(!(error=intsmb_stop(dev))){ 52743166Snsouch *byte=bus_space_read_1(sc->st,sc->sh, 52843166Snsouch PIIX4_SMBHSTDAT0); 52943166Snsouch } 53043166Snsouch } 53143166Snsouch return (error); 53243166Snsouch} 53343166Snsouchstatic int 53443166Snsouchintsmb_readw(device_t dev, u_char slave, char cmd, short *word) 53543166Snsouch{ 53643166Snsouch int error; 53743166Snsouch struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev); 53843166Snsouch error=intsmb_free(dev); 53943166Snsouch if(!error){ 54043166Snsouch bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave|LSB); 54143166Snsouch bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,cmd); 54243166Snsouch intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_WDATA,0); 54343166Snsouch if(!(error=intsmb_stop(dev))){ 54443166Snsouch *word=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTDAT0)&0xff; 54543166Snsouch *word|=(bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTDAT1)&0xff)<<8; 54643166Snsouch } 54743166Snsouch } 54843166Snsouch return (error); 54943166Snsouch} 55043166Snsouch/* 55143166Snsouch * Data sheet claims that it implements all function, but also claims 55243166Snsouch * that it implements 7 function and not mention PCALL. So I don't know 55343166Snsouch * whether it will work. 55443166Snsouch */ 55543166Snsouchstatic int 55643166Snsouchintsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata) 55743166Snsouch{ 55843166Snsouch#ifdef PROCCALL_TEST 55943166Snsouch int error; 56043166Snsouch struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev); 56143166Snsouch error=intsmb_free(dev); 56243166Snsouch if(!error){ 56343166Snsouch bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave&~LSB); 56443166Snsouch bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,cmd); 56543166Snsouch bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTDAT0,sdata&0xff); 56643166Snsouch bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTDAT1,(sdata&0xff)>>8); 56743166Snsouch intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_WDATA,0); 56843166Snsouch } 56943166Snsouch if(!(error=intsmb_stop(dev))){ 57043166Snsouch *rdata=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTDAT0)&0xff; 57143166Snsouch *rdata|=(bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTDAT1)&0xff)<<8; 57243166Snsouch } 57343166Snsouch return error; 57443166Snsouch#else 57543166Snsouch return 0; 57643166Snsouch#endif 57743166Snsouch} 57843166Snsouchstatic int 57943166Snsouchintsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf) 58043166Snsouch{ 58143166Snsouch int error,i; 58243166Snsouch struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev); 58343166Snsouch error=intsmb_free(dev); 58443166Snsouch if(count>SMBBLOCKTRANS_MAX||count==0) 58543166Snsouch error=EINVAL; 58643166Snsouch if(!error){ 58743166Snsouch /*Reset internal array index*/ 58843166Snsouch bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTCNT); 58943166Snsouch 59043166Snsouch bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave&~LSB); 59143166Snsouch bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,cmd); 59243166Snsouch for(i=0;i<count;i++){ 59343166Snsouch bus_space_write_1(sc->st,sc->sh,PIIX4_SMBBLKDAT,buf[i]); 59443166Snsouch } 59543166Snsouch bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTDAT0,count); 59643166Snsouch intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_BLOCK,0); 59743166Snsouch error=intsmb_stop(dev); 59843166Snsouch } 59943166Snsouch return (error); 60043166Snsouch} 60143166Snsouch 60243166Snsouchstatic int 60343166Snsouchintsmb_bread(device_t dev, u_char slave, char cmd, u_char count, char *buf) 60443166Snsouch{ 60543166Snsouch int error,i; 60643166Snsouch struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev); 60743166Snsouch error=intsmb_free(dev); 60843166Snsouch if(count>SMBBLOCKTRANS_MAX||count==0) 60943166Snsouch error=EINVAL; 61043166Snsouch if(!error){ 61143166Snsouch /*Reset internal array index*/ 61243166Snsouch bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTCNT); 61343166Snsouch 61443166Snsouch bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave|LSB); 61543166Snsouch bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,cmd); 61643166Snsouch bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTDAT0,count); 61743166Snsouch intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_BLOCK,0); 61843166Snsouch error=intsmb_stop(dev); 61943166Snsouch if(!error){ 62043166Snsouch bzero(buf,count);/*Is it needed?*/ 62143166Snsouch count= bus_space_read_1(sc->st,sc->sh, 62243166Snsouch PIIX4_SMBHSTDAT0); 62343166Snsouch if(count!=0&&count<=SMBBLOCKTRANS_MAX){ 62443166Snsouch for(i=0;i<count;i++){ 62543166Snsouch buf[i]=bus_space_read_1(sc->st, 62643166Snsouch sc->sh, 62743166Snsouch PIIX4_SMBBLKDAT); 62843166Snsouch } 62943166Snsouch } 63043166Snsouch else{ 63143166Snsouch error=EIO; 63243166Snsouch } 63343166Snsouch } 63443166Snsouch } 63543166Snsouch return (error); 63643166Snsouch} 63743166Snsouch 63843166SnsouchDRIVER_MODULE(intsmb, root , intpm_driver, intsmb_devclass, 0, 0); 63943166Snsouch 64043166Snsouch 64143166Snsouchstatic void intpm_intr __P((void *arg)); 64243166Snsouch 64343166Snsouchstatic const char* 64443166Snsouchintpm_probe (pcici_t tag, pcidi_t type) 64543166Snsouch{ 64643166Snsouch struct _pcsid *ep =pci_ids; 64743166Snsouch while (ep->type && ep->type != type) 64843166Snsouch ++ep; 64943166Snsouch return (ep->desc); 65043166Snsouch} 65143166Snsouch 65243166Snsouchstatic struct intpm_pci_softc *intpm_alloc(int unit){ 65343166Snsouch if(unit<NINTPM) 65443166Snsouch return &intpm_pci[unit]; 65543166Snsouch else 65643166Snsouch return NULL; 65743166Snsouch} 65843166Snsouch 65943166Snsouch/*Same as pci_map_int but this ignores INTPIN*/ 66043166Snsouchstatic int force_pci_map_int(pcici_t cfg, pci_inthand_t *func, void *arg, unsigned *maskptr) 66143166Snsouch{ 66243166Snsouch int error; 66343166Snsouch#ifdef APIC_IO 66443166Snsouch int nextpin, muxcnt; 66543166Snsouch#endif 66643166Snsouch /* Spec sheet claims that it use IRQ 9*/ 66743166Snsouch int irq = 9; 66843166Snsouch void *dev_instance = (void *)-1; /* XXX use cfg->devdata */ 66943166Snsouch void *idesc; 67043166Snsouch 67143166Snsouch idesc = intr_create(dev_instance, irq, func, arg, maskptr, 0); 67243166Snsouch error = intr_connect(idesc); 67343166Snsouch if (error != 0) 67443166Snsouch return 0; 67543166Snsouch#ifdef APIC_IO 67643166Snsouch nextpin = next_apic_irq(irq); 67743166Snsouch 67843166Snsouch if (nextpin < 0) 67943166Snsouch return 1; 68043166Snsouch 68143166Snsouch /* 68243166Snsouch * Attempt handling of some broken mp tables. 68343166Snsouch * 68443166Snsouch * It's OK to yell (since the mp tables are broken). 68543166Snsouch * 68643166Snsouch * Hanging in the boot is not OK 68743166Snsouch */ 68843166Snsouch 68943166Snsouch muxcnt = 2; 69043166Snsouch nextpin = next_apic_irq(nextpin); 69143166Snsouch while (muxcnt < 5 && nextpin >= 0) { 69243166Snsouch muxcnt++; 69343166Snsouch nextpin = next_apic_irq(nextpin); 69443166Snsouch } 69543166Snsouch if (muxcnt >= 5) { 69643166Snsouch printf("bogus MP table, more than 4 IO APIC pins connected to the same PCI device or ISA/EISA interrupt\n"); 69743166Snsouch return 0; 69843166Snsouch } 69943166Snsouch 70043166Snsouch printf("bogus MP table, %d IO APIC pins connected to the same PCI device or ISA/EISA interrupt\n", muxcnt); 70143166Snsouch 70243166Snsouch nextpin = next_apic_irq(irq); 70343166Snsouch while (nextpin >= 0) { 70443166Snsouch idesc = intr_create(dev_instance, nextpin, func, arg, 70543166Snsouch maskptr, 0); 70643166Snsouch error = intr_connect(idesc); 70743166Snsouch if (error != 0) 70843166Snsouch return 0; 70943166Snsouch printf("Registered extra interrupt handler for int %d (in addition to int %d)\n", nextpin, irq); 71043166Snsouch nextpin = next_apic_irq(nextpin); 71143166Snsouch } 71243166Snsouch#endif 71343166Snsouch return 1; 71443166Snsouch} 71543166Snsouchstatic void 71643166Snsouchintpm_attach(config_id, unit) 71743166Snsouch pcici_t config_id; 71843166Snsouch int unit; 71943166Snsouch{ 72043166Snsouch int value; 72143166Snsouch 72243166Snsouch char * str; 72343166Snsouch { 72443166Snsouch struct intpm_pci_softc *sciic; 72543166Snsouch device_t smbinterface; 72643166Snsouch value=pci_cfgread(config_id,PCI_BASE_ADDR_SMB,4); 72743166Snsouch sciic=intpm_alloc(unit); 72843166Snsouch if(sciic==NULL){ 72943166Snsouch return; 73043166Snsouch } 73143166Snsouch 73243166Snsouch sciic->smbst=(value&1)?I386_BUS_SPACE_IO:I386_BUS_SPACE_MEM; 73343166Snsouch 73443166Snsouch /*Calling pci_map_port is better.But bus_space_handle_t != 73543166Snsouch * pci_port_t, so I don't call support routine while 73643166Snsouch * bus_space_??? support routine will be appear. 73743166Snsouch */ 73843166Snsouch sciic->smbsh=value&(~1); 73943166Snsouch if(sciic->smbsh==I386_BUS_SPACE_MEM){ 74043166Snsouch /*According to the spec, this will not occur*/ 74143166Snsouch int dummy; 74243166Snsouch pci_map_mem(config_id,PCI_BASE_ADDR_SMB,&sciic->smbsh,&dummy); 74343166Snsouch } 74443166Snsouch printf("intpm%d: %s %x ",unit, 74543166Snsouch (sciic->smbst==I386_BUS_SPACE_IO)?"I/O mapped":"Memory", 74643166Snsouch sciic->smbsh); 74743166Snsouch#ifndef NO_CHANGE_PCICONF 74843166Snsouch pci_cfgwrite(config_id,PCIR_INTLINE,0x09,1); 74943166Snsouch pci_cfgwrite(config_id,PCI_HST_CFG_SMB, 75043166Snsouch PCI_INTR_SMB_IRQ9|PCI_INTR_SMB_ENABLE,1); 75143166Snsouch#endif 75243166Snsouch config_id->intline=pci_cfgread(config_id,PCIR_INTLINE,1); 75343166Snsouch printf("ALLOCED IRQ %d ",config_id->intline); 75443166Snsouch value=pci_cfgread(config_id,PCI_HST_CFG_SMB,1); 75543166Snsouch switch(value&0xe){ 75643166Snsouch case PCI_INTR_SMB_SMI: 75743166Snsouch str="SMI"; 75843166Snsouch break; 75943166Snsouch case PCI_INTR_SMB_IRQ9: 76043166Snsouch str="IRQ 9"; 76143166Snsouch break; 76243166Snsouch default: 76343166Snsouch str="BOGUS"; 76443166Snsouch } 76543166Snsouch printf("intr %s %s ",str,((value&1)? "enabled":"disabled")); 76643166Snsouch value=pci_cfgread(config_id,PCI_REVID_SMB,1); 76743166Snsouch printf("revision %d\n",value); 76843166Snsouch /* 76943166Snsouch * Install intr HANDLER here 77043166Snsouch */ 77143166Snsouch if(force_pci_map_int(config_id,intpm_intr,sciic,&net_imask)==0){ 77243166Snsouch printf("intpm%d: Failed to map intr\n",unit); 77343166Snsouch } 77443166Snsouch smbinterface=device_add_child(root_bus,"intsmb",unit,NULL); 77543166Snsouch device_probe_and_attach(smbinterface); 77643166Snsouch } 77743166Snsouch value=pci_cfgread(config_id,PCI_BASE_ADDR_PM,4); 77843166Snsouch printf("intpm%d: PM %s %x \n",unit,(value&1)?"I/O mapped":"Memory",value&0xfffe); 77943166Snsouch return; 78043166Snsouch} 78143166Snsouchstatic void intpm_intr(void *arg) 78243166Snsouch{ 78343166Snsouch struct intpm_pci_softc *sc; 78443166Snsouch sc=(struct intpm_pci_softc *)arg; 78543166Snsouch intsmb_intr(sc->smbus); 78643166Snsouch intsmb_slvintr(sc->smbus); 78743166Snsouch} 78843166Snsouch#endif /* NPCI > 0 */ 78943166Snsouch#endif 790