intpm.c revision 162234
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
27116192Sobrien#include <sys/cdefs.h>
28116192Sobrien__FBSDID("$FreeBSD: head/sys/pci/intpm.c 162234 2006-09-11 20:52:41Z jhb $");
29116192Sobrien
3043166Snsouch#include <sys/param.h>
3143166Snsouch#include <sys/systm.h>
3243166Snsouch#include <sys/kernel.h>
3343166Snsouch#include <machine/bus.h>
3443166Snsouch
3543166Snsouch#include <sys/uio.h>
3643166Snsouch#include <sys/module.h>
3743166Snsouch#include <sys/bus.h>
3846651Speter#include <sys/rman.h>
3946651Speter#include <machine/resource.h>
4043166Snsouch#include <dev/smbus/smbconf.h>
4143166Snsouch
4243166Snsouch#include "smbus_if.h"
4343166Snsouch
4443166Snsouch/*This should be removed if force_pci_map_int supported*/
4543166Snsouch#include <sys/interrupt.h>
4643166Snsouch
47119288Simp#include <dev/pci/pcireg.h>
48119288Simp#include <dev/pci/pcivar.h>
4943166Snsouch#include <pci/intpmreg.h>
5043166Snsouch
5143166Snsouch#include "opt_intpm.h"
5243166Snsouch
5343166Snsouchstatic struct _pcsid
5443166Snsouch{
5561042Speter        u_int32_t type;
5643166Snsouch	char	*desc;
5743166Snsouch} pci_ids[] =
5843166Snsouch{
5943166Snsouch	{ 0x71138086,"Intel 82371AB Power management controller"},
6088323Spirzyk	{ 0x719b8086,"Intel 82443MX Power management controller"},
6174285Speter#if 0
6274285Speter	/* Not a good idea yet, this stops isab0 functioning */
6374285Speter	{ 0x02001166,"ServerWorks OSB4 PCI to ISA Bridge"},
6474285Speter#endif
6543166Snsouch
6643166Snsouch	{ 0x00000000,	NULL					}
6743166Snsouch};
6843166Snsouchstatic int intsmb_probe(device_t);
6943166Snsouchstatic int intsmb_attach(device_t);
7043166Snsouch
7143166Snsouchstatic int intsmb_intr(device_t dev);
7243166Snsouchstatic int intsmb_slvintr(device_t dev);
7343166Snsouchstatic void  intsmb_alrintr(device_t dev);
74162234Sjhbstatic int intsmb_callback(device_t dev, int index, void *data);
7543166Snsouchstatic int intsmb_quick(device_t dev, u_char slave, int how);
7643166Snsouchstatic int intsmb_sendb(device_t dev, u_char slave, char byte);
7743166Snsouchstatic int intsmb_recvb(device_t dev, u_char slave, char *byte);
7843166Snsouchstatic int intsmb_writeb(device_t dev, u_char slave, char cmd, char byte);
7943166Snsouchstatic int intsmb_writew(device_t dev, u_char slave, char cmd, short word);
8043166Snsouchstatic int intsmb_readb(device_t dev, u_char slave, char cmd, char *byte);
8143166Snsouchstatic int intsmb_readw(device_t dev, u_char slave, char cmd, short *word);
8243166Snsouchstatic int intsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata);
8343166Snsouchstatic int intsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf);
84162234Sjhbstatic int intsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf);
8543166Snsouchstatic void intsmb_start(device_t dev,u_char cmd,int nointr);
8643166Snsouchstatic int intsmb_stop(device_t dev);
8743166Snsouchstatic int intsmb_stop_poll(device_t dev);
8843166Snsouchstatic int intsmb_free(device_t dev);
8946651Speterstatic int intpm_probe (device_t dev);
9046651Speterstatic int intpm_attach (device_t dev);
9143166Snsouchstatic devclass_t intsmb_devclass;
9243166Snsouch
9343166Snsouchstatic device_method_t intpm_methods[]={
9443166Snsouch        DEVMETHOD(device_probe,intsmb_probe),
9543166Snsouch        DEVMETHOD(device_attach,intsmb_attach),
9643166Snsouch
9749195Smdodd        DEVMETHOD(bus_print_child, bus_generic_print_child),
9843166Snsouch
9943166Snsouch        DEVMETHOD(smbus_callback,intsmb_callback),
10043166Snsouch        DEVMETHOD(smbus_quick,intsmb_quick),
10143166Snsouch        DEVMETHOD(smbus_sendb,intsmb_sendb),
10243166Snsouch        DEVMETHOD(smbus_recvb,intsmb_recvb),
10343166Snsouch        DEVMETHOD(smbus_writeb,intsmb_writeb),
10443166Snsouch        DEVMETHOD(smbus_writew,intsmb_writew),
10543166Snsouch        DEVMETHOD(smbus_readb,intsmb_readb),
10643166Snsouch        DEVMETHOD(smbus_readw,intsmb_readw),
10743166Snsouch        DEVMETHOD(smbus_pcall,intsmb_pcall),
10843166Snsouch        DEVMETHOD(smbus_bwrite,intsmb_bwrite),
10943166Snsouch        DEVMETHOD(smbus_bread,intsmb_bread),
11043166Snsouch        {0,0}
11143166Snsouch};
11243166Snsouch
11346651Speterstruct intpm_pci_softc{
11443166Snsouch        bus_space_tag_t smbst;
11543166Snsouch        bus_space_handle_t smbsh;
11643166Snsouch	bus_space_tag_t pmst;
11743166Snsouch	bus_space_handle_t pmsh;
11843166Snsouch	device_t  smbus;
11946651Speter};
12043166Snsouch
12143166Snsouch
12243166Snsouchstruct intsmb_softc{
12343166Snsouch        struct intpm_pci_softc *pci_sc;
12443166Snsouch        bus_space_tag_t st;
12543166Snsouch        bus_space_handle_t sh;
12643166Snsouch        device_t smbus;
12743166Snsouch        int isbusy;
12843166Snsouch};
12946651Speter
13043166Snsouchstatic driver_t intpm_driver = {
13143166Snsouch        "intsmb",
13243166Snsouch        intpm_methods,
13343166Snsouch        sizeof(struct intsmb_softc),
13443166Snsouch};
13543166Snsouch
13646651Speterstatic devclass_t intpm_devclass;
13746651Speterstatic device_method_t intpm_pci_methods[] = {
13846651Speter  DEVMETHOD(device_probe,intpm_probe),
13946651Speter  DEVMETHOD(device_attach,intpm_attach),
14046651Speter  {0,0}
14143166Snsouch};
14246651Speterstatic driver_t intpm_pci_driver = {
14346651Speter  "intpm",
14446651Speter  intpm_pci_methods,
14546651Speter  sizeof(struct intpm_pci_softc)
14646651Speter};
14743166Snsouch
14843166Snsouchstatic int
14943166Snsouchintsmb_probe(device_t dev)
15043166Snsouch{
15143166Snsouch        struct intsmb_softc *sc =(struct intsmb_softc *) device_get_softc(dev);
15293023Snsouch        sc->smbus=device_add_child(dev, "smbus", -1);
15343166Snsouch        if (!sc->smbus)
15443166Snsouch                return (EINVAL);    /* XXX don't know what to return else */
15543166Snsouch        device_set_desc(dev,"Intel PIIX4 SMBUS Interface");
15643166Snsouch
157142398Simp        return (BUS_PROBE_DEFAULT); /* XXX don't know what to return else */
15843166Snsouch}
15943166Snsouchstatic int
16043166Snsouchintsmb_attach(device_t dev)
16143166Snsouch{
16243166Snsouch        struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
16346651Speter        sc->pci_sc=device_get_softc(device_get_parent(dev));
16443166Snsouch        sc->isbusy=0;
16543166Snsouch	sc->sh=sc->pci_sc->smbsh;
16643166Snsouch	sc->st=sc->pci_sc->smbst;
16743166Snsouch	sc->pci_sc->smbus=dev;
16843166Snsouch        device_probe_and_attach(sc->smbus);
16943166Snsouch#ifdef ENABLE_ALART
17043166Snsouch	/*Enable Arart*/
17143166Snsouch	bus_space_write_1(sc->st,sc->sh,PIIX4_SMBSLVCNT,
17243166Snsouch			  PIIX4_SMBSLVCNT_ALTEN);
17343166Snsouch#endif
17443166Snsouch        return (0);
17543166Snsouch}
17643166Snsouch
17743166Snsouchstatic int
178162234Sjhbintsmb_callback(device_t dev, int index, void *data)
17943166Snsouch{
18043166Snsouch	int error = 0;
18143166Snsouch	intrmask_t s;
18243166Snsouch	s=splnet();
18343166Snsouch	switch (index) {
18443166Snsouch	case SMB_REQUEST_BUS:
18543166Snsouch		break;
18643166Snsouch	case SMB_RELEASE_BUS:
18743166Snsouch		break;
18843166Snsouch	default:
18943166Snsouch		error = EINVAL;
19043166Snsouch	}
19143166Snsouch	splx(s);
19243166Snsouch	return (error);
19343166Snsouch}
19443166Snsouch/*counterpart of smbtx_smb_free*/
19543166Snsouchstatic        int
19643166Snsouchintsmb_free(device_t dev){
19746651Speter        intrmask_t s;
19843166Snsouch        struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
19943166Snsouch        if((bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTSTS)&
20043166Snsouch	    PIIX4_SMBHSTSTAT_BUSY)
20143166Snsouch#ifdef ENABLE_ALART
20243166Snsouch	   ||(bus_space_read_1(sc->st,sc->sh,PIIX4_SMBSLVSTS)&
20343166Snsouch	      PIIX4_SMBSLVSTS_BUSY)
20443166Snsouch#endif
20543166Snsouch	   || sc->isbusy)
20643166Snsouch                return EBUSY;
20746651Speter	s=splhigh();
20843166Snsouch        sc->isbusy=1;
20943166Snsouch	/*Disable Intrrupt in slave part*/
21043166Snsouch#ifndef ENABLE_ALART
21143166Snsouch	bus_space_write_1(sc->st,sc->sh,PIIX4_SMBSLVCNT,0);
21243166Snsouch#endif
21343166Snsouch        /*Reset INTR Flag to prepare INTR*/
21443166Snsouch	bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTSTS,
21543166Snsouch			  (PIIX4_SMBHSTSTAT_INTR|
21643166Snsouch			   PIIX4_SMBHSTSTAT_ERR|
21743166Snsouch			   PIIX4_SMBHSTSTAT_BUSC|
21843166Snsouch			   PIIX4_SMBHSTSTAT_FAIL)
21943166Snsouch		);
22046651Speter	splx(s);
22143166Snsouch        return 0;
22243166Snsouch}
22343166Snsouch
22443166Snsouchstatic int
22543166Snsouchintsmb_intr(device_t dev)
22643166Snsouch{
22743166Snsouch	struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
22843166Snsouch	int status;
22943166Snsouch	status=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTSTS);
23043166Snsouch	if(status&PIIX4_SMBHSTSTAT_BUSY){
23143166Snsouch		return 1;
23243166Snsouch
23343166Snsouch	}
23449064Snsouch	if(status&(PIIX4_SMBHSTSTAT_INTR|
23543166Snsouch				PIIX4_SMBHSTSTAT_ERR|
23643166Snsouch				PIIX4_SMBHSTSTAT_BUSC|
23749064Snsouch				PIIX4_SMBHSTSTAT_FAIL)){
23843166Snsouch		int tmp;
23943166Snsouch		tmp=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTCNT);
24043166Snsouch		bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCNT,
24143166Snsouch				  tmp&~PIIX4_SMBHSTCNT_INTREN);
24249064Snsouch		if(sc->isbusy){
24349064Snsouch		  sc->isbusy=0;
24449064Snsouch		  wakeup(sc);
24549064Snsouch		}
24643166Snsouch		return 0;
24743166Snsouch	}
24843166Snsouch	return 1;/* Not Completed*/
24943166Snsouch}
25043166Snsouchstatic int
25143166Snsouchintsmb_slvintr(device_t dev)
25243166Snsouch{
25343166Snsouch	struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
25443166Snsouch        int status,retval;
25543166Snsouch	retval=1;
25643166Snsouch        status=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBSLVSTS);
25743166Snsouch	if(status&PIIX4_SMBSLVSTS_BUSY)
25843166Snsouch		return retval;
25943166Snsouch	if(status&PIIX4_SMBSLVSTS_ALART){
26043166Snsouch		intsmb_alrintr(dev);
26143166Snsouch		retval=0;
26243166Snsouch	}else if(status&~(PIIX4_SMBSLVSTS_ALART|PIIX4_SMBSLVSTS_SDW2
26343166Snsouch			  |PIIX4_SMBSLVSTS_SDW1)){
26443166Snsouch		retval=0;
26543166Snsouch	}
26643166Snsouch	/*Reset Status Register*/
26743166Snsouch	bus_space_write_1(sc->st,sc->sh,PIIX4_SMBSLVSTS,PIIX4_SMBSLVSTS_ALART|
26843166Snsouch			  PIIX4_SMBSLVSTS_SDW2|PIIX4_SMBSLVSTS_SDW1|
26943166Snsouch			  PIIX4_SMBSLVSTS_SLV);
27043166Snsouch	return retval;
27143166Snsouch}
27243166Snsouch
27343166Snsouchstatic void intsmb_alrintr(device_t dev)
27443166Snsouch{
27543166Snsouch	struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
27643166Snsouch	int slvcnt;
27743288Sdillon#ifdef ENABLE_ALART
27843288Sdillon	int error;
27943288Sdillon#endif
28043288Sdillon
28143166Snsouch	/*stop generating INTR from ALART*/
28243166Snsouch	slvcnt=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBSLVCNT);
28343166Snsouch#ifdef ENABLE_ALART
28443166Snsouch	bus_space_write_1(sc->st,sc->sh,PIIX4_SMBSLVCNT,
28543166Snsouch			  slvcnt&~PIIX4_SMBSLVCNT_ALTEN) ;
28643166Snsouch#endif
28743166Snsouch	DELAY(5);
28843166Snsouch	/*ask bus who assert it and then ask it what's the matter. */
28943166Snsouch#ifdef ENABLE_ALART
29043166Snsouch	error=intsmb_free(dev);
29143166Snsouch	if(!error){
29243166Snsouch                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,SMBALTRESP
29343166Snsouch                                  |LSB);
29443166Snsouch		intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_BYTE,1);
29543166Snsouch		if(!(error=intsmb_stop_poll(dev))){
29678255Speter			u_int8_t addr;
29743166Snsouch			addr=bus_space_read_1(sc->st,sc->sh,
29843166Snsouch					      PIIX4_SMBHSTDAT0);
29978255Speter			printf("ALART_RESPONSE: 0x%x\n", addr);
30043166Snsouch		}
30143166Snsouch	}else{
30243166Snsouch	        printf("ERROR\n");
30343166Snsouch	}
30443166Snsouch
30543166Snsouch	/*Re-enable INTR from ALART*/
30643166Snsouch	bus_space_write_1(sc->st,sc->sh,PIIX4_SMBSLVCNT,
30743166Snsouch			  slvcnt|PIIX4_SMBSLVCNT_ALTEN) ;
30843166Snsouch	DELAY(5);
30943166Snsouch#endif
31043166Snsouch
31143166Snsouch	return;
31243166Snsouch}
31343166Snsouchstatic void
31443166Snsouchintsmb_start(device_t dev,unsigned char cmd,int nointr)
31543166Snsouch{
31643166Snsouch	struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
31743166Snsouch	unsigned char tmp;
31843166Snsouch	tmp=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTCNT);
31943166Snsouch	tmp&= 0xe0;
32043166Snsouch	tmp |= cmd;
32143166Snsouch	tmp |=PIIX4_SMBHSTCNT_START;
32243166Snsouch	/*While not in autoconfiguration Intrrupt Enabled*/
32343166Snsouch	if(!cold||!nointr)
32443166Snsouch		tmp |=PIIX4_SMBHSTCNT_INTREN;
32543166Snsouch	bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCNT,tmp);
32643166Snsouch}
32743166Snsouch
32843166Snsouch/*Polling Code. Polling is not encouraged
32943166Snsouch * because It is required to wait for the device get busy.
33043166Snsouch *(29063505.pdf from Intel)
33143166Snsouch * But during boot,intrrupt cannot be used.
33243166Snsouch * so use polling code while in autoconfiguration.
33343166Snsouch */
33443166Snsouch
33543166Snsouchstatic        int
33643166Snsouchintsmb_stop_poll(device_t dev){
33743166Snsouch        int error,i;
33843166Snsouch        struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
33949064Snsouch
34043166Snsouch	/*
34143166Snsouch	 *  In smbtx driver ,Simply waiting.
34243166Snsouch	 *  This loops 100-200 times.
34343166Snsouch	 */
34443166Snsouch	for(i=0;i<0x7fff;i++){
34543166Snsouch                if((bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTSTS)
34643166Snsouch		    &PIIX4_SMBHSTSTAT_BUSY)){
34743166Snsouch                        break;
34843166Snsouch                }
34943166Snsouch	}
35043166Snsouch	for(i=0;i<0x7fff;i++){
35143166Snsouch		int status;
35243166Snsouch		status=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTSTS);
35343166Snsouch		if(!(status&PIIX4_SMBHSTSTAT_BUSY)){
35443166Snsouch			sc->isbusy=0;
35543166Snsouch			error=(status&PIIX4_SMBHSTSTAT_ERR)?EIO :
35643166Snsouch				(status&PIIX4_SMBHSTSTAT_BUSC)?EBUSY:
35743166Snsouch				(status&PIIX4_SMBHSTSTAT_FAIL)?EIO:0;
35843166Snsouch			if(error==0&&!(status&PIIX4_SMBHSTSTAT_INTR)){
35943166Snsouch				printf("unknown cause why?");
36043166Snsouch			}
36143166Snsouch			return error;
36243166Snsouch		}
36343166Snsouch	}
36449064Snsouch	{
36549064Snsouch	  int tmp;
36649064Snsouch	  sc->isbusy=0;
36749064Snsouch	  tmp=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTCNT);
36849064Snsouch	  bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCNT,
36949064Snsouch			    tmp&~PIIX4_SMBHSTCNT_INTREN);
37049064Snsouch	}
37143166Snsouch	return EIO;
37243166Snsouch}
37343166Snsouch/*
37443166Snsouch *wait for completion and return result.
37543166Snsouch */
37643166Snsouchstatic        int
37743166Snsouchintsmb_stop(device_t dev){
37843166Snsouch        int error;
37946651Speter	intrmask_t s;
38043166Snsouch        struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
38143166Snsouch	if(cold){
38243166Snsouch		/*So that it can use device during probing device on SMBus.*/
38343166Snsouch		error=intsmb_stop_poll(dev);
38443166Snsouch		return error;
38543166Snsouch	}else{
38643166Snsouch		if(!tsleep(sc,(PWAIT)|PCATCH,"SMBWAI",hz/8)){
38743166Snsouch			int status;
38843166Snsouch			status=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTSTS);
38943166Snsouch			if(!(status&PIIX4_SMBHSTSTAT_BUSY)){
39043166Snsouch				error=(status&PIIX4_SMBHSTSTAT_ERR)?EIO :
39143166Snsouch					(status&PIIX4_SMBHSTSTAT_BUSC)?EBUSY:
39243166Snsouch					(status&PIIX4_SMBHSTSTAT_FAIL)?EIO:0;
39343166Snsouch				if(error==0&&!(status&PIIX4_SMBHSTSTAT_INTR)){
39443166Snsouch					printf("intsmb%d:unknown cause why?\n",
39543166Snsouch					       device_get_unit(dev));
39643166Snsouch				}
39743166Snsouch#ifdef ENABLE_ALART
39843166Snsouch				bus_space_write_1(sc->st,sc->sh,
39943166Snsouch						  PIIX4_SMBSLVCNT,PIIX4_SMBSLVCNT_ALTEN);
40043166Snsouch#endif
40143166Snsouch				return error;
40243166Snsouch			}
40343166Snsouch		}
40443166Snsouch	}
40543166Snsouch	/*Timeout Procedure*/
40646651Speter	s=splhigh();
40743166Snsouch	sc->isbusy=0;
40843166Snsouch	/*Re-enable supressed intrrupt from slave part*/
40943166Snsouch	bus_space_write_1(sc->st,sc->sh,
41043166Snsouch			  PIIX4_SMBSLVCNT,PIIX4_SMBSLVCNT_ALTEN);
41146651Speter	splx(s);
41243166Snsouch        return EIO;
41343166Snsouch}
41443166Snsouch
41543166Snsouchstatic int
41643166Snsouchintsmb_quick(device_t dev, u_char slave, int how)
41743166Snsouch{
41843166Snsouch        int error=0;
41943166Snsouch        u_char data;
42043166Snsouch        struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
42143166Snsouch        data=slave;
42243166Snsouch	/*Quick command is part of Address, I think*/
42343166Snsouch        switch(how){
42443166Snsouch        case SMB_QWRITE:
42543166Snsouch                data&=~LSB;
42643166Snsouch		break;
42743166Snsouch        case SMB_QREAD:
42843166Snsouch                data|=LSB;
42943166Snsouch                break;
43043166Snsouch        default:
43143166Snsouch                error=EINVAL;
43243166Snsouch        }
43343166Snsouch        if(!error){
43443166Snsouch	        error=intsmb_free(dev);
43543166Snsouch                if(!error){
43643166Snsouch                        bus_space_write_1(sc->st,sc->sh,
43743166Snsouch					  PIIX4_SMBHSTADD,data);
43843166Snsouch			intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_QUICK,0);
43943166Snsouch                        error=intsmb_stop(dev);
44043166Snsouch                }
44143166Snsouch        }
44243166Snsouch
44343166Snsouch        return (error);
44443166Snsouch}
44543166Snsouch
44643166Snsouchstatic int
44743166Snsouchintsmb_sendb(device_t dev, u_char slave, char byte)
44843166Snsouch{
44943166Snsouch        int error;
45043166Snsouch        struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
45143166Snsouch        error=intsmb_free(dev);
45243166Snsouch        if(!error){
45343166Snsouch                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave&~LSB);
45443166Snsouch                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,byte);
45543166Snsouch		intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_BYTE,0);
45643166Snsouch                error=intsmb_stop(dev);
45743166Snsouch        }
45843166Snsouch        return (error);
45943166Snsouch}
46043166Snsouchstatic int
46143166Snsouchintsmb_recvb(device_t dev, u_char slave, char *byte)
46243166Snsouch{
46343166Snsouch        int error;
46443166Snsouch        struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
46543166Snsouch        error=intsmb_free(dev);
46643166Snsouch        if(!error){
46743166Snsouch                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave
46843166Snsouch				  |LSB);
46943166Snsouch                intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_BYTE,0);
47043166Snsouch                if(!(error=intsmb_stop(dev))){
47143166Snsouch#ifdef RECV_IS_IN_CMD
47243166Snsouch		        /*Linux SMBus stuff also troubles
47343166Snsouch			  Because Intel's datasheet will not make clear.
47443166Snsouch			 */
47543166Snsouch                        *byte=bus_space_read_1(sc->st,sc->sh,
47643166Snsouch					       PIIX4_SMBHSTCMD);
47743166Snsouch#else
47843166Snsouch                        *byte=bus_space_read_1(sc->st,sc->sh,
47943166Snsouch					       PIIX4_SMBHSTDAT0);
48043166Snsouch#endif
48143166Snsouch                }
48243166Snsouch        }
48343166Snsouch        return (error);
48443166Snsouch}
48543166Snsouchstatic int
48643166Snsouchintsmb_writeb(device_t dev, u_char slave, char cmd, char byte)
48743166Snsouch{
48843166Snsouch        int error;
48943166Snsouch        struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
49043166Snsouch        error=intsmb_free(dev);
49143166Snsouch        if(!error){
49243166Snsouch                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave&~LSB);
49343166Snsouch                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,cmd);
49443166Snsouch                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTDAT0,byte);
49543166Snsouch		intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_BDATA,0);
49643166Snsouch                error=intsmb_stop(dev);
49743166Snsouch        }
49843166Snsouch        return (error);
49943166Snsouch}
50043166Snsouchstatic int
50143166Snsouchintsmb_writew(device_t dev, u_char slave, char cmd, short word)
50243166Snsouch{
50343166Snsouch        int error;
50443166Snsouch        struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
50543166Snsouch        error=intsmb_free(dev);
50643166Snsouch        if(!error){
50743166Snsouch                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave&~LSB);
50843166Snsouch                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,cmd);
50943166Snsouch                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTDAT0,
51043166Snsouch				  word&0xff);
51143166Snsouch                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTDAT1,
51243166Snsouch				  (word>>8)&0xff);
51343166Snsouch		intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_WDATA,0);
51443166Snsouch                error=intsmb_stop(dev);
51543166Snsouch        }
51643166Snsouch        return (error);
51743166Snsouch}
51843166Snsouch
51943166Snsouchstatic int
52043166Snsouchintsmb_readb(device_t dev, u_char slave, char cmd, char *byte)
52143166Snsouch{
52243166Snsouch        int error;
52343166Snsouch        struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
52443166Snsouch        error=intsmb_free(dev);
52543166Snsouch        if(!error){
52643166Snsouch                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave|LSB);
52743166Snsouch                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,cmd);
52843166Snsouch		intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_BDATA,0);
52943166Snsouch                if(!(error=intsmb_stop(dev))){
53043166Snsouch		        *byte=bus_space_read_1(sc->st,sc->sh,
53143166Snsouch					       PIIX4_SMBHSTDAT0);
53243166Snsouch                }
53343166Snsouch        }
53443166Snsouch        return (error);
53543166Snsouch}
53643166Snsouchstatic int
53743166Snsouchintsmb_readw(device_t dev, u_char slave, char cmd, short *word)
53843166Snsouch{
53943166Snsouch        int error;
54043166Snsouch        struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
54143166Snsouch        error=intsmb_free(dev);
54243166Snsouch        if(!error){
54343166Snsouch                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave|LSB);
54443166Snsouch                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,cmd);
54543166Snsouch		intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_WDATA,0);
54643166Snsouch                if(!(error=intsmb_stop(dev))){
54743166Snsouch                        *word=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTDAT0)&0xff;
54843166Snsouch                        *word|=(bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTDAT1)&0xff)<<8;
54943166Snsouch                }
55043166Snsouch        }
55143166Snsouch        return (error);
55243166Snsouch}
55343166Snsouch/*
55443166Snsouch * Data sheet claims that it implements all function, but also claims
55543166Snsouch * that it implements 7 function and not mention PCALL. So I don't know
55643166Snsouch * whether it will work.
55743166Snsouch */
55843166Snsouchstatic int
55943166Snsouchintsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata)
56043166Snsouch{
56143166Snsouch#ifdef PROCCALL_TEST
56243166Snsouch        int error;
56343166Snsouch        struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
56443166Snsouch        error=intsmb_free(dev);
56543166Snsouch        if(!error){
56643166Snsouch                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave&~LSB);
56743166Snsouch                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,cmd);
56843166Snsouch                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTDAT0,sdata&0xff);
56943166Snsouch                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTDAT1,(sdata&0xff)>>8);
57043166Snsouch                intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_WDATA,0);
57143166Snsouch        }
57243166Snsouch        if(!(error=intsmb_stop(dev))){
57343166Snsouch                *rdata=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTDAT0)&0xff;
57443166Snsouch                *rdata|=(bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTDAT1)&0xff)<<8;
57543166Snsouch        }
57643166Snsouch        return error;
57743166Snsouch#else
57843166Snsouch	return 0;
57943166Snsouch#endif
58043166Snsouch}
58143166Snsouchstatic int
58243166Snsouchintsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf)
58343166Snsouch{
58443166Snsouch        int error,i;
58543166Snsouch        struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
58643166Snsouch        error=intsmb_free(dev);
58743166Snsouch        if(count>SMBBLOCKTRANS_MAX||count==0)
588162234Sjhb                error=SMB_EINVAL;
58943166Snsouch        if(!error){
59043166Snsouch                /*Reset internal array index*/
59143166Snsouch                bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTCNT);
59243166Snsouch
59343166Snsouch                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave&~LSB);
59443166Snsouch                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,cmd);
59543166Snsouch                for(i=0;i<count;i++){
59643166Snsouch                        bus_space_write_1(sc->st,sc->sh,PIIX4_SMBBLKDAT,buf[i]);
59743166Snsouch                }
59843166Snsouch                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTDAT0,count);
59943166Snsouch                intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_BLOCK,0);
60043166Snsouch                error=intsmb_stop(dev);
60143166Snsouch        }
60243166Snsouch        return (error);
60343166Snsouch}
60443166Snsouch
60543166Snsouchstatic int
606162234Sjhbintsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf)
60743166Snsouch{
60843166Snsouch        int error,i;
609162234Sjhb	u_char data, nread;
61043166Snsouch        struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
61143166Snsouch        error=intsmb_free(dev);
612162234Sjhb        if(*count>SMBBLOCKTRANS_MAX||*count==0)
613162234Sjhb                error=SMB_EINVAL;
61443166Snsouch        if(!error){
61543166Snsouch                /*Reset internal array index*/
61643166Snsouch                bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTCNT);
61743166Snsouch
61843166Snsouch                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave|LSB);
61943166Snsouch                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,cmd);
620162234Sjhb                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTDAT0,*count);
62143166Snsouch                intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_BLOCK,0);
62243166Snsouch                error=intsmb_stop(dev);
62343166Snsouch                if(!error){
624162234Sjhb                        nread= bus_space_read_1(sc->st,sc->sh,
62543166Snsouch						PIIX4_SMBHSTDAT0);
626162234Sjhb                        if(nread!=0&&nread<=SMBBLOCKTRANS_MAX){
627162234Sjhb			        for(i=0;i<nread;i++){
628162234Sjhb					data = bus_space_read_1(sc->st,
62943166Snsouch								sc->sh,
63043166Snsouch								PIIX4_SMBBLKDAT);
631162234Sjhb					if (i < *count)
632162234Sjhb						buf[i] = data;
63343166Snsouch				}
634162234Sjhb				*count = nread;
63543166Snsouch			}
63643166Snsouch                        else{
63743166Snsouch				error=EIO;
63843166Snsouch                        }
63943166Snsouch		}
64043166Snsouch	}
64143166Snsouch        return (error);
64243166Snsouch}
64343166Snsouch
64446651SpeterDRIVER_MODULE(intsmb, intpm , intpm_driver, intsmb_devclass, 0, 0);
64543166Snsouch
64643166Snsouch
64792739Salfredstatic void intpm_intr(void *arg);
64846651Speterstatic int
64946651Speterintpm_attach(device_t dev)
65043166Snsouch{
65143166Snsouch        int value;
65246651Speter        int unit=device_get_unit(dev);
65346651Speter	void *ih;
65446651Speter	int error;
65543166Snsouch        char * str;
65643166Snsouch        {
65743166Snsouch                struct intpm_pci_softc *sciic;
65843166Snsouch                device_t smbinterface;
65946651Speter		int rid;
66046651Speter		struct resource *res;
66146651Speter
66246651Speter                sciic=device_get_softc(dev);
66343166Snsouch                if(sciic==NULL){
66446651Speter                        return ENOMEM;
66543166Snsouch                }
66643166Snsouch
66746651Speter		rid=PCI_BASE_ADDR_SMB;
668127135Snjl		res=bus_alloc_resource_any(dev,SYS_RES_IOPORT,&rid,RF_ACTIVE);
66946651Speter		if(res==NULL){
67046651Speter		  device_printf(dev,"Could not allocate Bus space\n");
67146651Speter		  return ENXIO;
67246651Speter		}
67346651Speter		sciic->smbst=rman_get_bustag(res);
67446651Speter		sciic->smbsh=rman_get_bushandle(res);
67546651Speter
676106628Sjhb#ifdef __i386__
677111547Snyan		device_printf(dev,"%s %lx\n",
67846651Speter			      (sciic->smbst==I386_BUS_SPACE_IO)?
67946651Speter			      "I/O mapped":"Memory",
680111547Snyan			      rman_get_start(res));
681106628Sjhb#endif
68246651Speter
68343166Snsouch
68443166Snsouch#ifndef NO_CHANGE_PCICONF
68546651Speter		pci_write_config(dev,PCIR_INTLINE,0x9,1);
68646651Speter		pci_write_config(dev,PCI_HST_CFG_SMB,
68746651Speter				 PCI_INTR_SMB_IRQ9|PCI_INTR_SMB_ENABLE,1);
68843166Snsouch#endif
68946651Speter                value=pci_read_config(dev,PCI_HST_CFG_SMB,1);
69043166Snsouch                switch(value&0xe){
69143166Snsouch                case PCI_INTR_SMB_SMI:
69246651Speter		        str="SMI";
69343166Snsouch                        break;
69443166Snsouch                case PCI_INTR_SMB_IRQ9:
69543166Snsouch                        str="IRQ 9";
69643166Snsouch                        break;
69743166Snsouch                default:
69843166Snsouch                        str="BOGUS";
69943166Snsouch                }
70046651Speter                device_printf(dev,"intr %s %s ",str,((value&1)? "enabled":"disabled"));
70146651Speter                value=pci_read_config(dev,PCI_REVID_SMB,1);
70243166Snsouch                printf("revision %d\n",value);
70343166Snsouch                /*
70443166Snsouch                 * Install intr HANDLER here
70543166Snsouch                 */
70646651Speter		rid=0;
70746651Speter		res=bus_alloc_resource(dev,SYS_RES_IRQ,&rid,9,9,1,RF_SHAREABLE|RF_ACTIVE);
70846651Speter		if(res==NULL){
70946651Speter		  device_printf(dev,"could not allocate irq");
71046651Speter		  return ENOMEM;
71146651Speter		}
71246777Sphk		error=bus_setup_intr(dev,res,INTR_TYPE_MISC, (driver_intr_t *) intpm_intr,sciic,&ih);
71346651Speter                if(error){
71446651Speter                        device_printf(dev,"Failed to map intr\n");
71546651Speter			return error;
71643166Snsouch                }
71754073Smdodd                smbinterface=device_add_child(dev,"intsmb",unit);
71846651Speter		if(!smbinterface){
71946651Speter		     printf("intsmb%d:could not add SMBus device\n",unit);
72046651Speter		}
72143166Snsouch                device_probe_and_attach(smbinterface);
72243166Snsouch        }
72346651Speter
72446651Speter        value=pci_read_config(dev,PCI_BASE_ADDR_PM,4);
72543166Snsouch        printf("intpm%d: PM %s %x \n",unit,(value&1)?"I/O mapped":"Memory",value&0xfffe);
72646651Speter        return 0;
72743166Snsouch}
72846651Speterstatic int
72946651Speterintpm_probe(device_t dev)
73046651Speter{
73146651Speter    struct _pcsid *ep =pci_ids;
73246651Speter    u_int32_t device_id=pci_get_devid(dev);
73349064Snsouch
73446651Speter    while (ep->type && ep->type != device_id)
73546651Speter	  ++ep;
73646651Speter    if(ep->desc!=NULL){
73746651Speter      device_set_desc(dev,ep->desc);
73852590Sdfr      bus_set_resource(dev,SYS_RES_IRQ,0,9,1); /* XXX setup intr resource */
739142398Simp      return (BUS_PROBE_DEFAULT);
74046651Speter    }else{
74146651Speter      return ENXIO;
74246651Speter    }
74346651Speter}
74446651SpeterDRIVER_MODULE(intpm, pci , intpm_pci_driver, intpm_devclass, 0, 0);
745162234SjhbDRIVER_MODULE(smbus, intsmb, smbus_driver, smbus_devclass, 0, 0);
74693023SnsouchMODULE_DEPEND(intpm, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER);
74793023SnsouchMODULE_VERSION(intpm, 1);
74846651Speter
74943166Snsouchstatic void intpm_intr(void *arg)
75043166Snsouch{
75143166Snsouch        struct intpm_pci_softc *sc;
75243166Snsouch        sc=(struct intpm_pci_softc *)arg;
75343166Snsouch	intsmb_intr(sc->smbus);
75443166Snsouch	intsmb_slvintr(sc->smbus);
75549064Snsouch
75643166Snsouch}
757