summaryrefslogtreecommitdiffstats
path: root/sys/pci/intpm.c
diff options
context:
space:
mode:
authornsouch <nsouch@FreeBSD.org>1999-01-24 18:13:31 +0000
committernsouch <nsouch@FreeBSD.org>1999-01-24 18:13:31 +0000
commit0ded74477d966d3ca4c3085f9110f25a93a36815 (patch)
treed9deb967b0437b31aa190bde95d35fd49821cefa /sys/pci/intpm.c
parentad846f3a15a55c4929c3a24cf1276a7f8a845c5d (diff)
downloadFreeBSD-src-0ded74477d966d3ca4c3085f9110f25a93a36815.zip
FreeBSD-src-0ded74477d966d3ca4c3085f9110f25a93a36815.tar.gz
SMBus support for the Intel PIIX4 power management unit. See smbus(4),
iicbus(4) and smb(4). User programs are available to retrieve SDRAM and sensor info, contact the author. Submitted by: Takanori Watanabe <takawata@shidahara1.planet.sci.kobe-u.ac.jp> Reviewed by: Mike Smith <msmith@freebsd.org>
Diffstat (limited to 'sys/pci/intpm.c')
-rw-r--r--sys/pci/intpm.c789
1 files changed, 789 insertions, 0 deletions
diff --git a/sys/pci/intpm.c b/sys/pci/intpm.c
new file mode 100644
index 0000000..9d3eccc
--- /dev/null
+++ b/sys/pci/intpm.c
@@ -0,0 +1,789 @@
+/*-
+ * Copyright (c) 1998, 1999 Takanori Watanabe
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id$
+ */
+
+#include "pci.h"
+#include "intpm.h"
+
+#if NPCI > 0
+#if NINTPM >0
+/* I don't think the chip is used in other architecture. :-)*/
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+
+#include <machine/bus_pio.h>
+#include <machine/bus_memio.h>
+#include <machine/bus.h>
+
+#include <machine/clock.h>
+#include <sys/uio.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/malloc.h>
+#include <sys/buf.h>
+
+#include <dev/smbus/smbconf.h>
+
+#include "smbus_if.h"
+
+/*This should be removed if force_pci_map_int supported*/
+#include <sys/interrupt.h>
+
+#include <pci/pcireg.h>
+#include <pci/pcivar.h>
+#include <pci/intpmreg.h>
+
+#include "opt_intpm.h"
+
+static struct _pcsid
+{
+ pcidi_t type;
+ char *desc;
+} pci_ids[] =
+{
+ { 0x71138086,"Intel 82371AB Power management controller"},
+
+ { 0x00000000, NULL }
+};
+static int intsmb_probe(device_t);
+static int intsmb_attach(device_t);
+static void intsmb_print_child(device_t, device_t);
+
+static int intsmb_intr(device_t dev);
+static int intsmb_slvintr(device_t dev);
+static void intsmb_alrintr(device_t dev);
+static int intsmb_callback(device_t dev, int index, caddr_t data);
+static int intsmb_quick(device_t dev, u_char slave, int how);
+static int intsmb_sendb(device_t dev, u_char slave, char byte);
+static int intsmb_recvb(device_t dev, u_char slave, char *byte);
+static int intsmb_writeb(device_t dev, u_char slave, char cmd, char byte);
+static int intsmb_writew(device_t dev, u_char slave, char cmd, short word);
+static int intsmb_readb(device_t dev, u_char slave, char cmd, char *byte);
+static int intsmb_readw(device_t dev, u_char slave, char cmd, short *word);
+static int intsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata);
+static int intsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf);
+static int intsmb_bread(device_t dev, u_char slave, char cmd, u_char count, char *buf);
+static void intsmb_start(device_t dev,u_char cmd,int nointr);
+static int intsmb_stop(device_t dev);
+static int intsmb_stop_poll(device_t dev);
+static int intsmb_free(device_t dev);
+static struct intpm_pci_softc *intpm_alloc(int unit);
+static const char* intpm_probe __P((pcici_t tag, pcidi_t type));
+static void intpm_attach __P((pcici_t config_id, int unit));
+static devclass_t intsmb_devclass;
+
+static device_method_t intpm_methods[]={
+ DEVMETHOD(device_probe,intsmb_probe),
+ DEVMETHOD(device_attach,intsmb_attach),
+
+ DEVMETHOD(bus_print_child, intsmb_print_child),
+
+ DEVMETHOD(smbus_callback,intsmb_callback),
+ DEVMETHOD(smbus_quick,intsmb_quick),
+ DEVMETHOD(smbus_sendb,intsmb_sendb),
+ DEVMETHOD(smbus_recvb,intsmb_recvb),
+ DEVMETHOD(smbus_writeb,intsmb_writeb),
+ DEVMETHOD(smbus_writew,intsmb_writew),
+ DEVMETHOD(smbus_readb,intsmb_readb),
+ DEVMETHOD(smbus_readw,intsmb_readw),
+ DEVMETHOD(smbus_pcall,intsmb_pcall),
+ DEVMETHOD(smbus_bwrite,intsmb_bwrite),
+ DEVMETHOD(smbus_bread,intsmb_bread),
+ {0,0}
+};
+
+static struct intpm_pci_softc{
+ bus_space_tag_t smbst;
+ bus_space_handle_t smbsh;
+ bus_space_tag_t pmst;
+ bus_space_handle_t pmsh;
+ pcici_t cfg;
+ device_t smbus;
+}intpm_pci[NINTPM];
+
+
+struct intsmb_softc{
+ struct intpm_pci_softc *pci_sc;
+ bus_space_tag_t st;
+ bus_space_handle_t sh;
+ device_t smbus;
+ int isbusy;
+};
+static driver_t intpm_driver = {
+ "intsmb",
+ intpm_methods,
+ DRIVER_TYPE_MISC,
+ sizeof(struct intsmb_softc),
+};
+static u_long intpm_count ;
+
+static struct pci_device intpm_device = {
+ "intpm",
+ intpm_probe,
+ intpm_attach,
+ &intpm_count
+};
+
+DATA_SET (pcidevice_set, intpm_device);
+
+static int
+intsmb_probe(device_t dev)
+{
+ struct intsmb_softc *sc =(struct intsmb_softc *) device_get_softc(dev);
+ sc->smbus=smbus_alloc_bus(dev);
+ if (!sc->smbus)
+ return (EINVAL); /* XXX don't know what to return else */
+ device_set_desc(dev,"Intel PIIX4 SMBUS Interface");
+
+ return (0); /* XXX don't know what to return else */
+}
+static int
+intsmb_attach(device_t dev)
+{
+ struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
+ sc->pci_sc=&intpm_pci[device_get_unit(dev)];
+ sc->isbusy=0;
+ sc->sh=sc->pci_sc->smbsh;
+ sc->st=sc->pci_sc->smbst;
+ sc->pci_sc->smbus=dev;
+ device_probe_and_attach(sc->smbus);
+#ifdef ENABLE_ALART
+ /*Enable Arart*/
+ bus_space_write_1(sc->st,sc->sh,PIIX4_SMBSLVCNT,
+ PIIX4_SMBSLVCNT_ALTEN);
+#endif
+ return (0);
+}
+
+static void
+intsmb_print_child(device_t bus, device_t dev)
+{
+ printf(" on %s%d", device_get_name(bus), device_get_unit(bus));
+ return;
+}
+static int
+intsmb_callback(device_t dev, int index, caddr_t data)
+{
+ int error = 0;
+ intrmask_t s;
+ s=splnet();
+ switch (index) {
+ case SMB_REQUEST_BUS:
+ break;
+ case SMB_RELEASE_BUS:
+ break;
+ default:
+ error = EINVAL;
+ }
+ splx(s);
+ return (error);
+}
+/*counterpart of smbtx_smb_free*/
+static int
+intsmb_free(device_t dev){
+ struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
+ if((bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTSTS)&
+ PIIX4_SMBHSTSTAT_BUSY)
+#ifdef ENABLE_ALART
+ ||(bus_space_read_1(sc->st,sc->sh,PIIX4_SMBSLVSTS)&
+ PIIX4_SMBSLVSTS_BUSY)
+#endif
+ || sc->isbusy)
+ return EBUSY;
+ sc->isbusy=1;
+ /*Disable Intrrupt in slave part*/
+#ifndef ENABLE_ALART
+ bus_space_write_1(sc->st,sc->sh,PIIX4_SMBSLVCNT,0);
+#endif
+ /*Reset INTR Flag to prepare INTR*/
+ bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTSTS,
+ (PIIX4_SMBHSTSTAT_INTR|
+ PIIX4_SMBHSTSTAT_ERR|
+ PIIX4_SMBHSTSTAT_BUSC|
+ PIIX4_SMBHSTSTAT_FAIL)
+ );
+ return 0;
+}
+
+static int
+intsmb_intr(device_t dev)
+{
+ struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
+ int status;
+ intrmask_t s;
+ status=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTSTS);
+ if(status&PIIX4_SMBHSTSTAT_BUSY){
+ return 1;
+
+ }
+ s=splhigh();
+ if(sc->isbusy&&(status&(PIIX4_SMBHSTSTAT_INTR|
+ PIIX4_SMBHSTSTAT_ERR|
+ PIIX4_SMBHSTSTAT_BUSC|
+ PIIX4_SMBHSTSTAT_FAIL))){
+ int tmp;
+ sc->isbusy=0;
+ tmp=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTCNT);
+ bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCNT,
+ tmp&~PIIX4_SMBHSTCNT_INTREN);
+ splx(s);
+ wakeup(sc);
+ return 0;
+ }
+ splx(s);
+ return 1;/* Not Completed*/
+}
+static int
+intsmb_slvintr(device_t dev)
+{
+ struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
+ int status,retval;
+ retval=1;
+ status=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBSLVSTS);
+ if(status&PIIX4_SMBSLVSTS_BUSY)
+ return retval;
+ if(status&PIIX4_SMBSLVSTS_ALART){
+ intsmb_alrintr(dev);
+ retval=0;
+ }else if(status&~(PIIX4_SMBSLVSTS_ALART|PIIX4_SMBSLVSTS_SDW2
+ |PIIX4_SMBSLVSTS_SDW1)){
+ retval=0;
+ }
+ /*Reset Status Register*/
+ bus_space_write_1(sc->st,sc->sh,PIIX4_SMBSLVSTS,PIIX4_SMBSLVSTS_ALART|
+ PIIX4_SMBSLVSTS_SDW2|PIIX4_SMBSLVSTS_SDW1|
+ PIIX4_SMBSLVSTS_SLV);
+ return retval;
+}
+
+static void intsmb_alrintr(device_t dev)
+{
+ struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
+ int slvcnt;
+ /*stop generating INTR from ALART*/
+ slvcnt=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBSLVCNT);
+#ifdef ENABLE_ALART
+ bus_space_write_1(sc->st,sc->sh,PIIX4_SMBSLVCNT,
+ slvcnt&~PIIX4_SMBSLVCNT_ALTEN) ;
+#endif
+ DELAY(5);
+ /*ask bus who assert it and then ask it what's the matter. */
+#ifdef ENABLE_ALART
+ error=intsmb_free(dev);
+ if(!error){
+ bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,SMBALTRESP
+ |LSB);
+ intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_BYTE,1);
+ if(!(error=intsmb_stop_poll(dev))){
+ addr=bus_space_read_1(sc->st,sc->sh,
+ PIIX4_SMBHSTDAT0);
+ printf("ALART_RESPONSE: %x\n",(int) addr);
+ }
+ }else{
+ printf("ERROR\n");
+ }
+
+ /*Re-enable INTR from ALART*/
+ bus_space_write_1(sc->st,sc->sh,PIIX4_SMBSLVCNT,
+ slvcnt|PIIX4_SMBSLVCNT_ALTEN) ;
+ DELAY(5);
+#endif
+
+ return;
+}
+static void
+intsmb_start(device_t dev,unsigned char cmd,int nointr)
+{
+ struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
+ unsigned char tmp;
+ tmp=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTCNT);
+ tmp&= 0xe0;
+ tmp |= cmd;
+ tmp |=PIIX4_SMBHSTCNT_START;
+ /*While not in autoconfiguration Intrrupt Enabled*/
+ if(!cold||!nointr)
+ tmp |=PIIX4_SMBHSTCNT_INTREN;
+ bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCNT,tmp);
+}
+
+/*Polling Code. Polling is not encouraged
+ * because It is required to wait for the device get busy.
+ *(29063505.pdf from Intel)
+ * But during boot,intrrupt cannot be used.
+ * so use polling code while in autoconfiguration.
+ */
+
+static int
+intsmb_stop_poll(device_t dev){
+ int error,i;
+ struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
+ /*
+ * In smbtx driver ,Simply waiting.
+ * This loops 100-200 times.
+ */
+ for(i=0;i<0x7fff;i++){
+ if((bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTSTS)
+ &PIIX4_SMBHSTSTAT_BUSY)){
+ break;
+ }
+ }
+ for(i=0;i<0x7fff;i++){
+ int status;
+ status=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTSTS);
+ if(!(status&PIIX4_SMBHSTSTAT_BUSY)){
+ sc->isbusy=0;
+ error=(status&PIIX4_SMBHSTSTAT_ERR)?EIO :
+ (status&PIIX4_SMBHSTSTAT_BUSC)?EBUSY:
+ (status&PIIX4_SMBHSTSTAT_FAIL)?EIO:0;
+ if(error==0&&!(status&PIIX4_SMBHSTSTAT_INTR)){
+ printf("unknown cause why?");
+ }
+ return error;
+ }
+ }
+ sc->isbusy=0;
+ return EIO;
+}
+/*
+ *wait for completion and return result.
+ */
+static int
+intsmb_stop(device_t dev){
+ int error;
+ struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
+ if(cold){
+ /*So that it can use device during probing device on SMBus.*/
+ error=intsmb_stop_poll(dev);
+ return error;
+ }else{
+ if(!tsleep(sc,(PWAIT)|PCATCH,"SMBWAI",hz/8)){
+ int status;
+ status=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTSTS);
+ if(!(status&PIIX4_SMBHSTSTAT_BUSY)){
+ error=(status&PIIX4_SMBHSTSTAT_ERR)?EIO :
+ (status&PIIX4_SMBHSTSTAT_BUSC)?EBUSY:
+ (status&PIIX4_SMBHSTSTAT_FAIL)?EIO:0;
+ if(error==0&&!(status&PIIX4_SMBHSTSTAT_INTR)){
+ printf("intsmb%d:unknown cause why?\n",
+ device_get_unit(dev));
+ }
+#ifdef ENABLE_ALART
+ bus_space_write_1(sc->st,sc->sh,
+ PIIX4_SMBSLVCNT,PIIX4_SMBSLVCNT_ALTEN);
+#endif
+ return error;
+ }
+ }
+ }
+ /*Timeout Procedure*/
+ sc->isbusy=0;
+ /*Re-enable supressed intrrupt from slave part*/
+ bus_space_write_1(sc->st,sc->sh,
+ PIIX4_SMBSLVCNT,PIIX4_SMBSLVCNT_ALTEN);
+ return EIO;
+}
+
+static int
+intsmb_quick(device_t dev, u_char slave, int how)
+{
+ int error=0;
+ u_char data;
+ struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
+ data=slave;
+ /*Quick command is part of Address, I think*/
+ switch(how){
+ case SMB_QWRITE:
+ data&=~LSB;
+ break;
+ case SMB_QREAD:
+ data|=LSB;
+ break;
+ default:
+ error=EINVAL;
+ }
+ if(!error){
+ error=intsmb_free(dev);
+ if(!error){
+ bus_space_write_1(sc->st,sc->sh,
+ PIIX4_SMBHSTADD,data);
+ intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_QUICK,0);
+ error=intsmb_stop(dev);
+ }
+ }
+
+ return (error);
+}
+
+static int
+intsmb_sendb(device_t dev, u_char slave, char byte)
+{
+ int error;
+ struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
+ error=intsmb_free(dev);
+ if(!error){
+ bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave&~LSB);
+ bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,byte);
+ intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_BYTE,0);
+ error=intsmb_stop(dev);
+ }
+ return (error);
+}
+static int
+intsmb_recvb(device_t dev, u_char slave, char *byte)
+{
+ int error;
+ struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
+ error=intsmb_free(dev);
+ if(!error){
+ bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave
+ |LSB);
+ intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_BYTE,0);
+ if(!(error=intsmb_stop(dev))){
+#ifdef RECV_IS_IN_CMD
+ /*Linux SMBus stuff also troubles
+ Because Intel's datasheet will not make clear.
+ */
+ *byte=bus_space_read_1(sc->st,sc->sh,
+ PIIX4_SMBHSTCMD);
+#else
+ *byte=bus_space_read_1(sc->st,sc->sh,
+ PIIX4_SMBHSTDAT0);
+#endif
+ }
+ }
+ return (error);
+}
+static int
+intsmb_writeb(device_t dev, u_char slave, char cmd, char byte)
+{
+ int error;
+ struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
+ error=intsmb_free(dev);
+ if(!error){
+ bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave&~LSB);
+ bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,cmd);
+ bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTDAT0,byte);
+ intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_BDATA,0);
+ error=intsmb_stop(dev);
+ }
+ return (error);
+}
+static int
+intsmb_writew(device_t dev, u_char slave, char cmd, short word)
+{
+ int error;
+ struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
+ error=intsmb_free(dev);
+ if(!error){
+ bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave&~LSB);
+ bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,cmd);
+ bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTDAT0,
+ word&0xff);
+ bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTDAT1,
+ (word>>8)&0xff);
+ intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_WDATA,0);
+ error=intsmb_stop(dev);
+ }
+ return (error);
+}
+
+static int
+intsmb_readb(device_t dev, u_char slave, char cmd, char *byte)
+{
+ int error;
+ struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
+ error=intsmb_free(dev);
+ if(!error){
+ bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave|LSB);
+ bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,cmd);
+ intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_BDATA,0);
+ if(!(error=intsmb_stop(dev))){
+ *byte=bus_space_read_1(sc->st,sc->sh,
+ PIIX4_SMBHSTDAT0);
+ }
+ }
+ return (error);
+}
+static int
+intsmb_readw(device_t dev, u_char slave, char cmd, short *word)
+{
+ int error;
+ struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
+ error=intsmb_free(dev);
+ if(!error){
+ bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave|LSB);
+ bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,cmd);
+ intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_WDATA,0);
+ if(!(error=intsmb_stop(dev))){
+ *word=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTDAT0)&0xff;
+ *word|=(bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTDAT1)&0xff)<<8;
+ }
+ }
+ return (error);
+}
+/*
+ * Data sheet claims that it implements all function, but also claims
+ * that it implements 7 function and not mention PCALL. So I don't know
+ * whether it will work.
+ */
+static int
+intsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata)
+{
+#ifdef PROCCALL_TEST
+ int error;
+ struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
+ error=intsmb_free(dev);
+ if(!error){
+ bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave&~LSB);
+ bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,cmd);
+ bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTDAT0,sdata&0xff);
+ bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTDAT1,(sdata&0xff)>>8);
+ intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_WDATA,0);
+ }
+ if(!(error=intsmb_stop(dev))){
+ *rdata=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTDAT0)&0xff;
+ *rdata|=(bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTDAT1)&0xff)<<8;
+ }
+ return error;
+#else
+ return 0;
+#endif
+}
+static int
+intsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf)
+{
+ int error,i;
+ struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
+ error=intsmb_free(dev);
+ if(count>SMBBLOCKTRANS_MAX||count==0)
+ error=EINVAL;
+ if(!error){
+ /*Reset internal array index*/
+ bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTCNT);
+
+ bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave&~LSB);
+ bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,cmd);
+ for(i=0;i<count;i++){
+ bus_space_write_1(sc->st,sc->sh,PIIX4_SMBBLKDAT,buf[i]);
+ }
+ bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTDAT0,count);
+ intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_BLOCK,0);
+ error=intsmb_stop(dev);
+ }
+ return (error);
+}
+
+static int
+intsmb_bread(device_t dev, u_char slave, char cmd, u_char count, char *buf)
+{
+ int error,i;
+ struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
+ error=intsmb_free(dev);
+ if(count>SMBBLOCKTRANS_MAX||count==0)
+ error=EINVAL;
+ if(!error){
+ /*Reset internal array index*/
+ bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTCNT);
+
+ bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave|LSB);
+ bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,cmd);
+ bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTDAT0,count);
+ intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_BLOCK,0);
+ error=intsmb_stop(dev);
+ if(!error){
+ bzero(buf,count);/*Is it needed?*/
+ count= bus_space_read_1(sc->st,sc->sh,
+ PIIX4_SMBHSTDAT0);
+ if(count!=0&&count<=SMBBLOCKTRANS_MAX){
+ for(i=0;i<count;i++){
+ buf[i]=bus_space_read_1(sc->st,
+ sc->sh,
+ PIIX4_SMBBLKDAT);
+ }
+ }
+ else{
+ error=EIO;
+ }
+ }
+ }
+ return (error);
+}
+
+DRIVER_MODULE(intsmb, root , intpm_driver, intsmb_devclass, 0, 0);
+
+
+static void intpm_intr __P((void *arg));
+
+static const char*
+intpm_probe (pcici_t tag, pcidi_t type)
+{
+ struct _pcsid *ep =pci_ids;
+ while (ep->type && ep->type != type)
+ ++ep;
+ return (ep->desc);
+}
+
+static struct intpm_pci_softc *intpm_alloc(int unit){
+ if(unit<NINTPM)
+ return &intpm_pci[unit];
+ else
+ return NULL;
+}
+
+/*Same as pci_map_int but this ignores INTPIN*/
+static int force_pci_map_int(pcici_t cfg, pci_inthand_t *func, void *arg, unsigned *maskptr)
+{
+ int error;
+#ifdef APIC_IO
+ int nextpin, muxcnt;
+#endif
+ /* Spec sheet claims that it use IRQ 9*/
+ int irq = 9;
+ void *dev_instance = (void *)-1; /* XXX use cfg->devdata */
+ void *idesc;
+
+ idesc = intr_create(dev_instance, irq, func, arg, maskptr, 0);
+ error = intr_connect(idesc);
+ if (error != 0)
+ return 0;
+#ifdef APIC_IO
+ nextpin = next_apic_irq(irq);
+
+ if (nextpin < 0)
+ return 1;
+
+ /*
+ * Attempt handling of some broken mp tables.
+ *
+ * It's OK to yell (since the mp tables are broken).
+ *
+ * Hanging in the boot is not OK
+ */
+
+ muxcnt = 2;
+ nextpin = next_apic_irq(nextpin);
+ while (muxcnt < 5 && nextpin >= 0) {
+ muxcnt++;
+ nextpin = next_apic_irq(nextpin);
+ }
+ if (muxcnt >= 5) {
+ printf("bogus MP table, more than 4 IO APIC pins connected to the same PCI device or ISA/EISA interrupt\n");
+ return 0;
+ }
+
+ printf("bogus MP table, %d IO APIC pins connected to the same PCI device or ISA/EISA interrupt\n", muxcnt);
+
+ nextpin = next_apic_irq(irq);
+ while (nextpin >= 0) {
+ idesc = intr_create(dev_instance, nextpin, func, arg,
+ maskptr, 0);
+ error = intr_connect(idesc);
+ if (error != 0)
+ return 0;
+ printf("Registered extra interrupt handler for int %d (in addition to int %d)\n", nextpin, irq);
+ nextpin = next_apic_irq(nextpin);
+ }
+#endif
+ return 1;
+}
+static void
+intpm_attach(config_id, unit)
+ pcici_t config_id;
+ int unit;
+{
+ int value;
+
+ char * str;
+ {
+ struct intpm_pci_softc *sciic;
+ device_t smbinterface;
+ value=pci_cfgread(config_id,PCI_BASE_ADDR_SMB,4);
+ sciic=intpm_alloc(unit);
+ if(sciic==NULL){
+ return;
+ }
+
+ sciic->smbst=(value&1)?I386_BUS_SPACE_IO:I386_BUS_SPACE_MEM;
+
+ /*Calling pci_map_port is better.But bus_space_handle_t !=
+ * pci_port_t, so I don't call support routine while
+ * bus_space_??? support routine will be appear.
+ */
+ sciic->smbsh=value&(~1);
+ if(sciic->smbsh==I386_BUS_SPACE_MEM){
+ /*According to the spec, this will not occur*/
+ int dummy;
+ pci_map_mem(config_id,PCI_BASE_ADDR_SMB,&sciic->smbsh,&dummy);
+ }
+ printf("intpm%d: %s %x ",unit,
+ (sciic->smbst==I386_BUS_SPACE_IO)?"I/O mapped":"Memory",
+ sciic->smbsh);
+#ifndef NO_CHANGE_PCICONF
+ pci_cfgwrite(config_id,PCIR_INTLINE,0x09,1);
+ pci_cfgwrite(config_id,PCI_HST_CFG_SMB,
+ PCI_INTR_SMB_IRQ9|PCI_INTR_SMB_ENABLE,1);
+#endif
+ config_id->intline=pci_cfgread(config_id,PCIR_INTLINE,1);
+ printf("ALLOCED IRQ %d ",config_id->intline);
+ value=pci_cfgread(config_id,PCI_HST_CFG_SMB,1);
+ switch(value&0xe){
+ case PCI_INTR_SMB_SMI:
+ str="SMI";
+ break;
+ case PCI_INTR_SMB_IRQ9:
+ str="IRQ 9";
+ break;
+ default:
+ str="BOGUS";
+ }
+ printf("intr %s %s ",str,((value&1)? "enabled":"disabled"));
+ value=pci_cfgread(config_id,PCI_REVID_SMB,1);
+ printf("revision %d\n",value);
+ /*
+ * Install intr HANDLER here
+ */
+ if(force_pci_map_int(config_id,intpm_intr,sciic,&net_imask)==0){
+ printf("intpm%d: Failed to map intr\n",unit);
+ }
+ smbinterface=device_add_child(root_bus,"intsmb",unit,NULL);
+ device_probe_and_attach(smbinterface);
+ }
+ value=pci_cfgread(config_id,PCI_BASE_ADDR_PM,4);
+ printf("intpm%d: PM %s %x \n",unit,(value&1)?"I/O mapped":"Memory",value&0xfffe);
+ return;
+}
+static void intpm_intr(void *arg)
+{
+ struct intpm_pci_softc *sc;
+ sc=(struct intpm_pci_softc *)arg;
+ intsmb_intr(sc->smbus);
+ intsmb_slvintr(sc->smbus);
+}
+#endif /* NPCI > 0 */
+#endif
OpenPOWER on IntegriCloud