intpm.c revision 119288
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 119288 2003-08-22 07:20:27Z imp $");
29116192Sobrien
3043166Snsouch#include <sys/param.h>
3143166Snsouch#include <sys/systm.h>
3243166Snsouch#include <sys/kernel.h>
3343166Snsouch#include <machine/bus_pio.h>
3443166Snsouch#include <machine/bus_memio.h>
3543166Snsouch#include <machine/bus.h>
3643166Snsouch
3743166Snsouch#include <sys/uio.h>
3843166Snsouch#include <sys/module.h>
3943166Snsouch#include <sys/bus.h>
4046651Speter#include <sys/rman.h>
4146651Speter#include <machine/resource.h>
4243166Snsouch#include <dev/smbus/smbconf.h>
4343166Snsouch
4443166Snsouch#include "smbus_if.h"
4543166Snsouch
4643166Snsouch/*This should be removed if force_pci_map_int supported*/
4743166Snsouch#include <sys/interrupt.h>
4843166Snsouch
49119288Simp#include <dev/pci/pcireg.h>
50119288Simp#include <dev/pci/pcivar.h>
5143166Snsouch#include <pci/intpmreg.h>
5243166Snsouch
5343166Snsouch#include "opt_intpm.h"
5443166Snsouch
5543166Snsouchstatic struct _pcsid
5643166Snsouch{
5761042Speter        u_int32_t type;
5843166Snsouch	char	*desc;
5943166Snsouch} pci_ids[] =
6043166Snsouch{
6143166Snsouch	{ 0x71138086,"Intel 82371AB Power management controller"},
6288323Spirzyk	{ 0x719b8086,"Intel 82443MX Power management controller"},
6374285Speter#if 0
6474285Speter	/* Not a good idea yet, this stops isab0 functioning */
6574285Speter	{ 0x02001166,"ServerWorks OSB4 PCI to ISA Bridge"},
6674285Speter#endif
6743166Snsouch
6843166Snsouch	{ 0x00000000,	NULL					}
6943166Snsouch};
7043166Snsouchstatic int intsmb_probe(device_t);
7143166Snsouchstatic int intsmb_attach(device_t);
7243166Snsouch
7343166Snsouchstatic int intsmb_intr(device_t dev);
7443166Snsouchstatic int intsmb_slvintr(device_t dev);
7543166Snsouchstatic void  intsmb_alrintr(device_t dev);
7643166Snsouchstatic int intsmb_callback(device_t dev, int index, caddr_t data);
7743166Snsouchstatic int intsmb_quick(device_t dev, u_char slave, int how);
7843166Snsouchstatic int intsmb_sendb(device_t dev, u_char slave, char byte);
7943166Snsouchstatic int intsmb_recvb(device_t dev, u_char slave, char *byte);
8043166Snsouchstatic int intsmb_writeb(device_t dev, u_char slave, char cmd, char byte);
8143166Snsouchstatic int intsmb_writew(device_t dev, u_char slave, char cmd, short word);
8243166Snsouchstatic int intsmb_readb(device_t dev, u_char slave, char cmd, char *byte);
8343166Snsouchstatic int intsmb_readw(device_t dev, u_char slave, char cmd, short *word);
8443166Snsouchstatic int intsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata);
8543166Snsouchstatic int intsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf);
8643166Snsouchstatic int intsmb_bread(device_t dev, u_char slave, char cmd, u_char count, char *buf);
8743166Snsouchstatic void intsmb_start(device_t dev,u_char cmd,int nointr);
8843166Snsouchstatic int intsmb_stop(device_t dev);
8943166Snsouchstatic int intsmb_stop_poll(device_t dev);
9043166Snsouchstatic int intsmb_free(device_t dev);
9146651Speterstatic int intpm_probe (device_t dev);
9246651Speterstatic int intpm_attach (device_t dev);
9343166Snsouchstatic devclass_t intsmb_devclass;
9443166Snsouch
9543166Snsouchstatic device_method_t intpm_methods[]={
9643166Snsouch        DEVMETHOD(device_probe,intsmb_probe),
9743166Snsouch        DEVMETHOD(device_attach,intsmb_attach),
9843166Snsouch
9949195Smdodd        DEVMETHOD(bus_print_child, bus_generic_print_child),
10043166Snsouch
10143166Snsouch        DEVMETHOD(smbus_callback,intsmb_callback),
10243166Snsouch        DEVMETHOD(smbus_quick,intsmb_quick),
10343166Snsouch        DEVMETHOD(smbus_sendb,intsmb_sendb),
10443166Snsouch        DEVMETHOD(smbus_recvb,intsmb_recvb),
10543166Snsouch        DEVMETHOD(smbus_writeb,intsmb_writeb),
10643166Snsouch        DEVMETHOD(smbus_writew,intsmb_writew),
10743166Snsouch        DEVMETHOD(smbus_readb,intsmb_readb),
10843166Snsouch        DEVMETHOD(smbus_readw,intsmb_readw),
10943166Snsouch        DEVMETHOD(smbus_pcall,intsmb_pcall),
11043166Snsouch        DEVMETHOD(smbus_bwrite,intsmb_bwrite),
11143166Snsouch        DEVMETHOD(smbus_bread,intsmb_bread),
11243166Snsouch        {0,0}
11343166Snsouch};
11443166Snsouch
11546651Speterstruct intpm_pci_softc{
11643166Snsouch        bus_space_tag_t smbst;
11743166Snsouch        bus_space_handle_t smbsh;
11843166Snsouch	bus_space_tag_t pmst;
11943166Snsouch	bus_space_handle_t pmsh;
12043166Snsouch	device_t  smbus;
12146651Speter};
12243166Snsouch
12343166Snsouch
12443166Snsouchstruct intsmb_softc{
12543166Snsouch        struct intpm_pci_softc *pci_sc;
12643166Snsouch        bus_space_tag_t st;
12743166Snsouch        bus_space_handle_t sh;
12843166Snsouch        device_t smbus;
12943166Snsouch        int isbusy;
13043166Snsouch};
13146651Speter
13243166Snsouchstatic driver_t intpm_driver = {
13343166Snsouch        "intsmb",
13443166Snsouch        intpm_methods,
13543166Snsouch        sizeof(struct intsmb_softc),
13643166Snsouch};
13743166Snsouch
13846651Speterstatic devclass_t intpm_devclass;
13946651Speterstatic device_method_t intpm_pci_methods[] = {
14046651Speter  DEVMETHOD(device_probe,intpm_probe),
14146651Speter  DEVMETHOD(device_attach,intpm_attach),
14246651Speter  {0,0}
14343166Snsouch};
14446651Speterstatic driver_t intpm_pci_driver = {
14546651Speter  "intpm",
14646651Speter  intpm_pci_methods,
14746651Speter  sizeof(struct intpm_pci_softc)
14846651Speter};
14943166Snsouch
15043166Snsouchstatic int
15143166Snsouchintsmb_probe(device_t dev)
15243166Snsouch{
15343166Snsouch        struct intsmb_softc *sc =(struct intsmb_softc *) device_get_softc(dev);
15493023Snsouch        sc->smbus=device_add_child(dev, "smbus", -1);
15543166Snsouch        if (!sc->smbus)
15643166Snsouch                return (EINVAL);    /* XXX don't know what to return else */
15743166Snsouch        device_set_desc(dev,"Intel PIIX4 SMBUS Interface");
15843166Snsouch
15943166Snsouch        return (0);          /* XXX don't know what to return else */
16043166Snsouch}
16143166Snsouchstatic int
16243166Snsouchintsmb_attach(device_t dev)
16343166Snsouch{
16443166Snsouch        struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
16546651Speter        sc->pci_sc=device_get_softc(device_get_parent(dev));
16643166Snsouch        sc->isbusy=0;
16743166Snsouch	sc->sh=sc->pci_sc->smbsh;
16843166Snsouch	sc->st=sc->pci_sc->smbst;
16943166Snsouch	sc->pci_sc->smbus=dev;
17043166Snsouch        device_probe_and_attach(sc->smbus);
17143166Snsouch#ifdef ENABLE_ALART
17243166Snsouch	/*Enable Arart*/
17343166Snsouch	bus_space_write_1(sc->st,sc->sh,PIIX4_SMBSLVCNT,
17443166Snsouch			  PIIX4_SMBSLVCNT_ALTEN);
17543166Snsouch#endif
17643166Snsouch        return (0);
17743166Snsouch}
17843166Snsouch
17943166Snsouchstatic int
18043166Snsouchintsmb_callback(device_t dev, int index, caddr_t data)
18143166Snsouch{
18243166Snsouch	int error = 0;
18343166Snsouch	intrmask_t s;
18443166Snsouch	s=splnet();
18543166Snsouch	switch (index) {
18643166Snsouch	case SMB_REQUEST_BUS:
18743166Snsouch		break;
18843166Snsouch	case SMB_RELEASE_BUS:
18943166Snsouch		break;
19043166Snsouch	default:
19143166Snsouch		error = EINVAL;
19243166Snsouch	}
19343166Snsouch	splx(s);
19443166Snsouch	return (error);
19543166Snsouch}
19643166Snsouch/*counterpart of smbtx_smb_free*/
19743166Snsouchstatic        int
19843166Snsouchintsmb_free(device_t dev){
19946651Speter        intrmask_t s;
20043166Snsouch        struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
20143166Snsouch        if((bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTSTS)&
20243166Snsouch	    PIIX4_SMBHSTSTAT_BUSY)
20343166Snsouch#ifdef ENABLE_ALART
20443166Snsouch	   ||(bus_space_read_1(sc->st,sc->sh,PIIX4_SMBSLVSTS)&
20543166Snsouch	      PIIX4_SMBSLVSTS_BUSY)
20643166Snsouch#endif
20743166Snsouch	   || sc->isbusy)
20843166Snsouch                return EBUSY;
20946651Speter	s=splhigh();
21043166Snsouch        sc->isbusy=1;
21143166Snsouch	/*Disable Intrrupt in slave part*/
21243166Snsouch#ifndef ENABLE_ALART
21343166Snsouch	bus_space_write_1(sc->st,sc->sh,PIIX4_SMBSLVCNT,0);
21443166Snsouch#endif
21543166Snsouch        /*Reset INTR Flag to prepare INTR*/
21643166Snsouch	bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTSTS,
21743166Snsouch			  (PIIX4_SMBHSTSTAT_INTR|
21843166Snsouch			   PIIX4_SMBHSTSTAT_ERR|
21943166Snsouch			   PIIX4_SMBHSTSTAT_BUSC|
22043166Snsouch			   PIIX4_SMBHSTSTAT_FAIL)
22143166Snsouch		);
22246651Speter	splx(s);
22343166Snsouch        return 0;
22443166Snsouch}
22543166Snsouch
22643166Snsouchstatic int
22743166Snsouchintsmb_intr(device_t dev)
22843166Snsouch{
22943166Snsouch	struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
23043166Snsouch	int status;
23143166Snsouch	status=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTSTS);
23243166Snsouch	if(status&PIIX4_SMBHSTSTAT_BUSY){
23343166Snsouch		return 1;
23443166Snsouch
23543166Snsouch	}
23649064Snsouch	if(status&(PIIX4_SMBHSTSTAT_INTR|
23743166Snsouch				PIIX4_SMBHSTSTAT_ERR|
23843166Snsouch				PIIX4_SMBHSTSTAT_BUSC|
23949064Snsouch				PIIX4_SMBHSTSTAT_FAIL)){
24043166Snsouch		int tmp;
24143166Snsouch		tmp=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTCNT);
24243166Snsouch		bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCNT,
24343166Snsouch				  tmp&~PIIX4_SMBHSTCNT_INTREN);
24449064Snsouch		if(sc->isbusy){
24549064Snsouch		  sc->isbusy=0;
24649064Snsouch		  wakeup(sc);
24749064Snsouch		}
24843166Snsouch		return 0;
24943166Snsouch	}
25043166Snsouch	return 1;/* Not Completed*/
25143166Snsouch}
25243166Snsouchstatic int
25343166Snsouchintsmb_slvintr(device_t dev)
25443166Snsouch{
25543166Snsouch	struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
25643166Snsouch        int status,retval;
25743166Snsouch	retval=1;
25843166Snsouch        status=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBSLVSTS);
25943166Snsouch	if(status&PIIX4_SMBSLVSTS_BUSY)
26043166Snsouch		return retval;
26143166Snsouch	if(status&PIIX4_SMBSLVSTS_ALART){
26243166Snsouch		intsmb_alrintr(dev);
26343166Snsouch		retval=0;
26443166Snsouch	}else if(status&~(PIIX4_SMBSLVSTS_ALART|PIIX4_SMBSLVSTS_SDW2
26543166Snsouch			  |PIIX4_SMBSLVSTS_SDW1)){
26643166Snsouch		retval=0;
26743166Snsouch	}
26843166Snsouch	/*Reset Status Register*/
26943166Snsouch	bus_space_write_1(sc->st,sc->sh,PIIX4_SMBSLVSTS,PIIX4_SMBSLVSTS_ALART|
27043166Snsouch			  PIIX4_SMBSLVSTS_SDW2|PIIX4_SMBSLVSTS_SDW1|
27143166Snsouch			  PIIX4_SMBSLVSTS_SLV);
27243166Snsouch	return retval;
27343166Snsouch}
27443166Snsouch
27543166Snsouchstatic void intsmb_alrintr(device_t dev)
27643166Snsouch{
27743166Snsouch	struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
27843166Snsouch	int slvcnt;
27943288Sdillon#ifdef ENABLE_ALART
28043288Sdillon	int error;
28143288Sdillon#endif
28243288Sdillon
28343166Snsouch	/*stop generating INTR from ALART*/
28443166Snsouch	slvcnt=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBSLVCNT);
28543166Snsouch#ifdef ENABLE_ALART
28643166Snsouch	bus_space_write_1(sc->st,sc->sh,PIIX4_SMBSLVCNT,
28743166Snsouch			  slvcnt&~PIIX4_SMBSLVCNT_ALTEN) ;
28843166Snsouch#endif
28943166Snsouch	DELAY(5);
29043166Snsouch	/*ask bus who assert it and then ask it what's the matter. */
29143166Snsouch#ifdef ENABLE_ALART
29243166Snsouch	error=intsmb_free(dev);
29343166Snsouch	if(!error){
29443166Snsouch                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,SMBALTRESP
29543166Snsouch                                  |LSB);
29643166Snsouch		intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_BYTE,1);
29743166Snsouch		if(!(error=intsmb_stop_poll(dev))){
29878255Speter			u_int8_t addr;
29943166Snsouch			addr=bus_space_read_1(sc->st,sc->sh,
30043166Snsouch					      PIIX4_SMBHSTDAT0);
30178255Speter			printf("ALART_RESPONSE: 0x%x\n", addr);
30243166Snsouch		}
30343166Snsouch	}else{
30443166Snsouch	        printf("ERROR\n");
30543166Snsouch	}
30643166Snsouch
30743166Snsouch	/*Re-enable INTR from ALART*/
30843166Snsouch	bus_space_write_1(sc->st,sc->sh,PIIX4_SMBSLVCNT,
30943166Snsouch			  slvcnt|PIIX4_SMBSLVCNT_ALTEN) ;
31043166Snsouch	DELAY(5);
31143166Snsouch#endif
31243166Snsouch
31343166Snsouch	return;
31443166Snsouch}
31543166Snsouchstatic void
31643166Snsouchintsmb_start(device_t dev,unsigned char cmd,int nointr)
31743166Snsouch{
31843166Snsouch	struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
31943166Snsouch	unsigned char tmp;
32043166Snsouch	tmp=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTCNT);
32143166Snsouch	tmp&= 0xe0;
32243166Snsouch	tmp |= cmd;
32343166Snsouch	tmp |=PIIX4_SMBHSTCNT_START;
32443166Snsouch	/*While not in autoconfiguration Intrrupt Enabled*/
32543166Snsouch	if(!cold||!nointr)
32643166Snsouch		tmp |=PIIX4_SMBHSTCNT_INTREN;
32743166Snsouch	bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCNT,tmp);
32843166Snsouch}
32943166Snsouch
33043166Snsouch/*Polling Code. Polling is not encouraged
33143166Snsouch * because It is required to wait for the device get busy.
33243166Snsouch *(29063505.pdf from Intel)
33343166Snsouch * But during boot,intrrupt cannot be used.
33443166Snsouch * so use polling code while in autoconfiguration.
33543166Snsouch */
33643166Snsouch
33743166Snsouchstatic        int
33843166Snsouchintsmb_stop_poll(device_t dev){
33943166Snsouch        int error,i;
34043166Snsouch        struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
34149064Snsouch
34243166Snsouch	/*
34343166Snsouch	 *  In smbtx driver ,Simply waiting.
34443166Snsouch	 *  This loops 100-200 times.
34543166Snsouch	 */
34643166Snsouch	for(i=0;i<0x7fff;i++){
34743166Snsouch                if((bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTSTS)
34843166Snsouch		    &PIIX4_SMBHSTSTAT_BUSY)){
34943166Snsouch                        break;
35043166Snsouch                }
35143166Snsouch	}
35243166Snsouch	for(i=0;i<0x7fff;i++){
35343166Snsouch		int status;
35443166Snsouch		status=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTSTS);
35543166Snsouch		if(!(status&PIIX4_SMBHSTSTAT_BUSY)){
35643166Snsouch			sc->isbusy=0;
35743166Snsouch			error=(status&PIIX4_SMBHSTSTAT_ERR)?EIO :
35843166Snsouch				(status&PIIX4_SMBHSTSTAT_BUSC)?EBUSY:
35943166Snsouch				(status&PIIX4_SMBHSTSTAT_FAIL)?EIO:0;
36043166Snsouch			if(error==0&&!(status&PIIX4_SMBHSTSTAT_INTR)){
36143166Snsouch				printf("unknown cause why?");
36243166Snsouch			}
36343166Snsouch			return error;
36443166Snsouch		}
36543166Snsouch	}
36649064Snsouch	{
36749064Snsouch	  int tmp;
36849064Snsouch	  sc->isbusy=0;
36949064Snsouch	  tmp=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTCNT);
37049064Snsouch	  bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCNT,
37149064Snsouch			    tmp&~PIIX4_SMBHSTCNT_INTREN);
37249064Snsouch	}
37343166Snsouch	return EIO;
37443166Snsouch}
37543166Snsouch/*
37643166Snsouch *wait for completion and return result.
37743166Snsouch */
37843166Snsouchstatic        int
37943166Snsouchintsmb_stop(device_t dev){
38043166Snsouch        int error;
38146651Speter	intrmask_t s;
38243166Snsouch        struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
38343166Snsouch	if(cold){
38443166Snsouch		/*So that it can use device during probing device on SMBus.*/
38543166Snsouch		error=intsmb_stop_poll(dev);
38643166Snsouch		return error;
38743166Snsouch	}else{
38843166Snsouch		if(!tsleep(sc,(PWAIT)|PCATCH,"SMBWAI",hz/8)){
38943166Snsouch			int status;
39043166Snsouch			status=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTSTS);
39143166Snsouch			if(!(status&PIIX4_SMBHSTSTAT_BUSY)){
39243166Snsouch				error=(status&PIIX4_SMBHSTSTAT_ERR)?EIO :
39343166Snsouch					(status&PIIX4_SMBHSTSTAT_BUSC)?EBUSY:
39443166Snsouch					(status&PIIX4_SMBHSTSTAT_FAIL)?EIO:0;
39543166Snsouch				if(error==0&&!(status&PIIX4_SMBHSTSTAT_INTR)){
39643166Snsouch					printf("intsmb%d:unknown cause why?\n",
39743166Snsouch					       device_get_unit(dev));
39843166Snsouch				}
39943166Snsouch#ifdef ENABLE_ALART
40043166Snsouch				bus_space_write_1(sc->st,sc->sh,
40143166Snsouch						  PIIX4_SMBSLVCNT,PIIX4_SMBSLVCNT_ALTEN);
40243166Snsouch#endif
40343166Snsouch				return error;
40443166Snsouch			}
40543166Snsouch		}
40643166Snsouch	}
40743166Snsouch	/*Timeout Procedure*/
40846651Speter	s=splhigh();
40943166Snsouch	sc->isbusy=0;
41043166Snsouch	/*Re-enable supressed intrrupt from slave part*/
41143166Snsouch	bus_space_write_1(sc->st,sc->sh,
41243166Snsouch			  PIIX4_SMBSLVCNT,PIIX4_SMBSLVCNT_ALTEN);
41346651Speter	splx(s);
41443166Snsouch        return EIO;
41543166Snsouch}
41643166Snsouch
41743166Snsouchstatic int
41843166Snsouchintsmb_quick(device_t dev, u_char slave, int how)
41943166Snsouch{
42043166Snsouch        int error=0;
42143166Snsouch        u_char data;
42243166Snsouch        struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
42343166Snsouch        data=slave;
42443166Snsouch	/*Quick command is part of Address, I think*/
42543166Snsouch        switch(how){
42643166Snsouch        case SMB_QWRITE:
42743166Snsouch                data&=~LSB;
42843166Snsouch		break;
42943166Snsouch        case SMB_QREAD:
43043166Snsouch                data|=LSB;
43143166Snsouch                break;
43243166Snsouch        default:
43343166Snsouch                error=EINVAL;
43443166Snsouch        }
43543166Snsouch        if(!error){
43643166Snsouch	        error=intsmb_free(dev);
43743166Snsouch                if(!error){
43843166Snsouch                        bus_space_write_1(sc->st,sc->sh,
43943166Snsouch					  PIIX4_SMBHSTADD,data);
44043166Snsouch			intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_QUICK,0);
44143166Snsouch                        error=intsmb_stop(dev);
44243166Snsouch                }
44343166Snsouch        }
44443166Snsouch
44543166Snsouch        return (error);
44643166Snsouch}
44743166Snsouch
44843166Snsouchstatic int
44943166Snsouchintsmb_sendb(device_t dev, u_char slave, char byte)
45043166Snsouch{
45143166Snsouch        int error;
45243166Snsouch        struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
45343166Snsouch        error=intsmb_free(dev);
45443166Snsouch        if(!error){
45543166Snsouch                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave&~LSB);
45643166Snsouch                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,byte);
45743166Snsouch		intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_BYTE,0);
45843166Snsouch                error=intsmb_stop(dev);
45943166Snsouch        }
46043166Snsouch        return (error);
46143166Snsouch}
46243166Snsouchstatic int
46343166Snsouchintsmb_recvb(device_t dev, u_char slave, char *byte)
46443166Snsouch{
46543166Snsouch        int error;
46643166Snsouch        struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
46743166Snsouch        error=intsmb_free(dev);
46843166Snsouch        if(!error){
46943166Snsouch                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave
47043166Snsouch				  |LSB);
47143166Snsouch                intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_BYTE,0);
47243166Snsouch                if(!(error=intsmb_stop(dev))){
47343166Snsouch#ifdef RECV_IS_IN_CMD
47443166Snsouch		        /*Linux SMBus stuff also troubles
47543166Snsouch			  Because Intel's datasheet will not make clear.
47643166Snsouch			 */
47743166Snsouch                        *byte=bus_space_read_1(sc->st,sc->sh,
47843166Snsouch					       PIIX4_SMBHSTCMD);
47943166Snsouch#else
48043166Snsouch                        *byte=bus_space_read_1(sc->st,sc->sh,
48143166Snsouch					       PIIX4_SMBHSTDAT0);
48243166Snsouch#endif
48343166Snsouch                }
48443166Snsouch        }
48543166Snsouch        return (error);
48643166Snsouch}
48743166Snsouchstatic int
48843166Snsouchintsmb_writeb(device_t dev, u_char slave, char cmd, char byte)
48943166Snsouch{
49043166Snsouch        int error;
49143166Snsouch        struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
49243166Snsouch        error=intsmb_free(dev);
49343166Snsouch        if(!error){
49443166Snsouch                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave&~LSB);
49543166Snsouch                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,cmd);
49643166Snsouch                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTDAT0,byte);
49743166Snsouch		intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_BDATA,0);
49843166Snsouch                error=intsmb_stop(dev);
49943166Snsouch        }
50043166Snsouch        return (error);
50143166Snsouch}
50243166Snsouchstatic int
50343166Snsouchintsmb_writew(device_t dev, u_char slave, char cmd, short word)
50443166Snsouch{
50543166Snsouch        int error;
50643166Snsouch        struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
50743166Snsouch        error=intsmb_free(dev);
50843166Snsouch        if(!error){
50943166Snsouch                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave&~LSB);
51043166Snsouch                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,cmd);
51143166Snsouch                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTDAT0,
51243166Snsouch				  word&0xff);
51343166Snsouch                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTDAT1,
51443166Snsouch				  (word>>8)&0xff);
51543166Snsouch		intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_WDATA,0);
51643166Snsouch                error=intsmb_stop(dev);
51743166Snsouch        }
51843166Snsouch        return (error);
51943166Snsouch}
52043166Snsouch
52143166Snsouchstatic int
52243166Snsouchintsmb_readb(device_t dev, u_char slave, char cmd, char *byte)
52343166Snsouch{
52443166Snsouch        int error;
52543166Snsouch        struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
52643166Snsouch        error=intsmb_free(dev);
52743166Snsouch        if(!error){
52843166Snsouch                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave|LSB);
52943166Snsouch                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,cmd);
53043166Snsouch		intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_BDATA,0);
53143166Snsouch                if(!(error=intsmb_stop(dev))){
53243166Snsouch		        *byte=bus_space_read_1(sc->st,sc->sh,
53343166Snsouch					       PIIX4_SMBHSTDAT0);
53443166Snsouch                }
53543166Snsouch        }
53643166Snsouch        return (error);
53743166Snsouch}
53843166Snsouchstatic int
53943166Snsouchintsmb_readw(device_t dev, u_char slave, char cmd, short *word)
54043166Snsouch{
54143166Snsouch        int error;
54243166Snsouch        struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
54343166Snsouch        error=intsmb_free(dev);
54443166Snsouch        if(!error){
54543166Snsouch                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave|LSB);
54643166Snsouch                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,cmd);
54743166Snsouch		intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_WDATA,0);
54843166Snsouch                if(!(error=intsmb_stop(dev))){
54943166Snsouch                        *word=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTDAT0)&0xff;
55043166Snsouch                        *word|=(bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTDAT1)&0xff)<<8;
55143166Snsouch                }
55243166Snsouch        }
55343166Snsouch        return (error);
55443166Snsouch}
55543166Snsouch/*
55643166Snsouch * Data sheet claims that it implements all function, but also claims
55743166Snsouch * that it implements 7 function and not mention PCALL. So I don't know
55843166Snsouch * whether it will work.
55943166Snsouch */
56043166Snsouchstatic int
56143166Snsouchintsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata)
56243166Snsouch{
56343166Snsouch#ifdef PROCCALL_TEST
56443166Snsouch        int error;
56543166Snsouch        struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
56643166Snsouch        error=intsmb_free(dev);
56743166Snsouch        if(!error){
56843166Snsouch                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave&~LSB);
56943166Snsouch                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,cmd);
57043166Snsouch                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTDAT0,sdata&0xff);
57143166Snsouch                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTDAT1,(sdata&0xff)>>8);
57243166Snsouch                intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_WDATA,0);
57343166Snsouch        }
57443166Snsouch        if(!(error=intsmb_stop(dev))){
57543166Snsouch                *rdata=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTDAT0)&0xff;
57643166Snsouch                *rdata|=(bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTDAT1)&0xff)<<8;
57743166Snsouch        }
57843166Snsouch        return error;
57943166Snsouch#else
58043166Snsouch	return 0;
58143166Snsouch#endif
58243166Snsouch}
58343166Snsouchstatic int
58443166Snsouchintsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf)
58543166Snsouch{
58643166Snsouch        int error,i;
58743166Snsouch        struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
58843166Snsouch        error=intsmb_free(dev);
58943166Snsouch        if(count>SMBBLOCKTRANS_MAX||count==0)
59043166Snsouch                error=EINVAL;
59143166Snsouch        if(!error){
59243166Snsouch                /*Reset internal array index*/
59343166Snsouch                bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTCNT);
59443166Snsouch
59543166Snsouch                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave&~LSB);
59643166Snsouch                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,cmd);
59743166Snsouch                for(i=0;i<count;i++){
59843166Snsouch                        bus_space_write_1(sc->st,sc->sh,PIIX4_SMBBLKDAT,buf[i]);
59943166Snsouch                }
60043166Snsouch                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTDAT0,count);
60143166Snsouch                intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_BLOCK,0);
60243166Snsouch                error=intsmb_stop(dev);
60343166Snsouch        }
60443166Snsouch        return (error);
60543166Snsouch}
60643166Snsouch
60743166Snsouchstatic int
60843166Snsouchintsmb_bread(device_t dev, u_char slave, char cmd, u_char count, char *buf)
60943166Snsouch{
61043166Snsouch        int error,i;
61143166Snsouch        struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
61243166Snsouch        error=intsmb_free(dev);
61343166Snsouch        if(count>SMBBLOCKTRANS_MAX||count==0)
61443166Snsouch                error=EINVAL;
61543166Snsouch        if(!error){
61643166Snsouch                /*Reset internal array index*/
61743166Snsouch                bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTCNT);
61843166Snsouch
61943166Snsouch                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave|LSB);
62043166Snsouch                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,cmd);
62143166Snsouch                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTDAT0,count);
62243166Snsouch                intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_BLOCK,0);
62343166Snsouch                error=intsmb_stop(dev);
62443166Snsouch                if(!error){
62543166Snsouch                        bzero(buf,count);/*Is it needed?*/
62643166Snsouch                        count= bus_space_read_1(sc->st,sc->sh,
62743166Snsouch						PIIX4_SMBHSTDAT0);
62843166Snsouch                        if(count!=0&&count<=SMBBLOCKTRANS_MAX){
62943166Snsouch			        for(i=0;i<count;i++){
63043166Snsouch				        buf[i]=bus_space_read_1(sc->st,
63143166Snsouch								sc->sh,
63243166Snsouch								PIIX4_SMBBLKDAT);
63343166Snsouch				}
63443166Snsouch			}
63543166Snsouch                        else{
63643166Snsouch				error=EIO;
63743166Snsouch                        }
63843166Snsouch		}
63943166Snsouch	}
64043166Snsouch        return (error);
64143166Snsouch}
64243166Snsouch
64346651SpeterDRIVER_MODULE(intsmb, intpm , intpm_driver, intsmb_devclass, 0, 0);
64443166Snsouch
64543166Snsouch
64692739Salfredstatic void intpm_intr(void *arg);
64746651Speterstatic int
64846651Speterintpm_attach(device_t dev)
64943166Snsouch{
65043166Snsouch        int value;
65146651Speter        int unit=device_get_unit(dev);
65246651Speter	void *ih;
65346651Speter	int error;
65443166Snsouch        char * str;
65543166Snsouch        {
65643166Snsouch                struct intpm_pci_softc *sciic;
65743166Snsouch                device_t smbinterface;
65846651Speter		int rid;
65946651Speter		struct resource *res;
66046651Speter
66146651Speter                sciic=device_get_softc(dev);
66243166Snsouch                if(sciic==NULL){
66346651Speter                        return ENOMEM;
66443166Snsouch                }
66543166Snsouch
66646651Speter		rid=PCI_BASE_ADDR_SMB;
66746651Speter		res=bus_alloc_resource(dev,SYS_RES_IOPORT,&rid,
66846651Speter				       0,~0,1,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 */
73946651Speter      return 0;
74046651Speter    }else{
74146651Speter      return ENXIO;
74246651Speter    }
74346651Speter}
74446651SpeterDRIVER_MODULE(intpm, pci , intpm_pci_driver, intpm_devclass, 0, 0);
74593023SnsouchMODULE_DEPEND(intpm, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER);
74693023SnsouchMODULE_VERSION(intpm, 1);
74746651Speter
74843166Snsouchstatic void intpm_intr(void *arg)
74943166Snsouch{
75043166Snsouch        struct intpm_pci_softc *sc;
75143166Snsouch        sc=(struct intpm_pci_softc *)arg;
75243166Snsouch	intsmb_intr(sc->smbus);
75343166Snsouch	intsmb_slvintr(sc->smbus);
75449064Snsouch
75543166Snsouch}
756