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