freebsd-dev/sys/pci/intpm.c
2005-02-24 21:32:56 +00:00

755 lines
23 KiB
C

/*-
* Copyright (c) 1998, 1999 Takanori Watanabe
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <machine/bus_pio.h>
#include <machine/bus_memio.h>
#include <machine/bus.h>
#include <sys/uio.h>
#include <sys/module.h>
#include <sys/bus.h>
#include <sys/rman.h>
#include <machine/resource.h>
#include <dev/smbus/smbconf.h>
#include "smbus_if.h"
/*This should be removed if force_pci_map_int supported*/
#include <sys/interrupt.h>
#include <dev/pci/pcireg.h>
#include <dev/pci/pcivar.h>
#include <pci/intpmreg.h>
#include "opt_intpm.h"
static struct _pcsid
{
u_int32_t type;
char *desc;
} pci_ids[] =
{
{ 0x71138086,"Intel 82371AB Power management controller"},
{ 0x719b8086,"Intel 82443MX Power management controller"},
#if 0
/* Not a good idea yet, this stops isab0 functioning */
{ 0x02001166,"ServerWorks OSB4 PCI to ISA Bridge"},
#endif
{ 0x00000000, NULL }
};
static int intsmb_probe(device_t);
static int intsmb_attach(device_t);
static int intsmb_intr(device_t dev);
static int intsmb_slvintr(device_t dev);
static void intsmb_alrintr(device_t dev);
static int intsmb_callback(device_t dev, int index, caddr_t data);
static int intsmb_quick(device_t dev, u_char slave, int how);
static int intsmb_sendb(device_t dev, u_char slave, char byte);
static int intsmb_recvb(device_t dev, u_char slave, char *byte);
static int intsmb_writeb(device_t dev, u_char slave, char cmd, char byte);
static int intsmb_writew(device_t dev, u_char slave, char cmd, short word);
static int intsmb_readb(device_t dev, u_char slave, char cmd, char *byte);
static int intsmb_readw(device_t dev, u_char slave, char cmd, short *word);
static int intsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata);
static int intsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf);
static int intsmb_bread(device_t dev, u_char slave, char cmd, u_char count, char *buf);
static void intsmb_start(device_t dev,u_char cmd,int nointr);
static int intsmb_stop(device_t dev);
static int intsmb_stop_poll(device_t dev);
static int intsmb_free(device_t dev);
static int intpm_probe (device_t dev);
static int intpm_attach (device_t dev);
static devclass_t intsmb_devclass;
static device_method_t intpm_methods[]={
DEVMETHOD(device_probe,intsmb_probe),
DEVMETHOD(device_attach,intsmb_attach),
DEVMETHOD(bus_print_child, bus_generic_print_child),
DEVMETHOD(smbus_callback,intsmb_callback),
DEVMETHOD(smbus_quick,intsmb_quick),
DEVMETHOD(smbus_sendb,intsmb_sendb),
DEVMETHOD(smbus_recvb,intsmb_recvb),
DEVMETHOD(smbus_writeb,intsmb_writeb),
DEVMETHOD(smbus_writew,intsmb_writew),
DEVMETHOD(smbus_readb,intsmb_readb),
DEVMETHOD(smbus_readw,intsmb_readw),
DEVMETHOD(smbus_pcall,intsmb_pcall),
DEVMETHOD(smbus_bwrite,intsmb_bwrite),
DEVMETHOD(smbus_bread,intsmb_bread),
{0,0}
};
struct intpm_pci_softc{
bus_space_tag_t smbst;
bus_space_handle_t smbsh;
bus_space_tag_t pmst;
bus_space_handle_t pmsh;
device_t smbus;
};
struct intsmb_softc{
struct intpm_pci_softc *pci_sc;
bus_space_tag_t st;
bus_space_handle_t sh;
device_t smbus;
int isbusy;
};
static driver_t intpm_driver = {
"intsmb",
intpm_methods,
sizeof(struct intsmb_softc),
};
static devclass_t intpm_devclass;
static device_method_t intpm_pci_methods[] = {
DEVMETHOD(device_probe,intpm_probe),
DEVMETHOD(device_attach,intpm_attach),
{0,0}
};
static driver_t intpm_pci_driver = {
"intpm",
intpm_pci_methods,
sizeof(struct intpm_pci_softc)
};
static int
intsmb_probe(device_t dev)
{
struct intsmb_softc *sc =(struct intsmb_softc *) device_get_softc(dev);
sc->smbus=device_add_child(dev, "smbus", -1);
if (!sc->smbus)
return (EINVAL); /* XXX don't know what to return else */
device_set_desc(dev,"Intel PIIX4 SMBUS Interface");
return (BUS_PROBE_DEFAULT); /* XXX don't know what to return else */
}
static int
intsmb_attach(device_t dev)
{
struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
sc->pci_sc=device_get_softc(device_get_parent(dev));
sc->isbusy=0;
sc->sh=sc->pci_sc->smbsh;
sc->st=sc->pci_sc->smbst;
sc->pci_sc->smbus=dev;
device_probe_and_attach(sc->smbus);
#ifdef ENABLE_ALART
/*Enable Arart*/
bus_space_write_1(sc->st,sc->sh,PIIX4_SMBSLVCNT,
PIIX4_SMBSLVCNT_ALTEN);
#endif
return (0);
}
static int
intsmb_callback(device_t dev, int index, caddr_t data)
{
int error = 0;
intrmask_t s;
s=splnet();
switch (index) {
case SMB_REQUEST_BUS:
break;
case SMB_RELEASE_BUS:
break;
default:
error = EINVAL;
}
splx(s);
return (error);
}
/*counterpart of smbtx_smb_free*/
static int
intsmb_free(device_t dev){
intrmask_t s;
struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
if((bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTSTS)&
PIIX4_SMBHSTSTAT_BUSY)
#ifdef ENABLE_ALART
||(bus_space_read_1(sc->st,sc->sh,PIIX4_SMBSLVSTS)&
PIIX4_SMBSLVSTS_BUSY)
#endif
|| sc->isbusy)
return EBUSY;
s=splhigh();
sc->isbusy=1;
/*Disable Intrrupt in slave part*/
#ifndef ENABLE_ALART
bus_space_write_1(sc->st,sc->sh,PIIX4_SMBSLVCNT,0);
#endif
/*Reset INTR Flag to prepare INTR*/
bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTSTS,
(PIIX4_SMBHSTSTAT_INTR|
PIIX4_SMBHSTSTAT_ERR|
PIIX4_SMBHSTSTAT_BUSC|
PIIX4_SMBHSTSTAT_FAIL)
);
splx(s);
return 0;
}
static int
intsmb_intr(device_t dev)
{
struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
int status;
status=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTSTS);
if(status&PIIX4_SMBHSTSTAT_BUSY){
return 1;
}
if(status&(PIIX4_SMBHSTSTAT_INTR|
PIIX4_SMBHSTSTAT_ERR|
PIIX4_SMBHSTSTAT_BUSC|
PIIX4_SMBHSTSTAT_FAIL)){
int tmp;
tmp=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTCNT);
bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCNT,
tmp&~PIIX4_SMBHSTCNT_INTREN);
if(sc->isbusy){
sc->isbusy=0;
wakeup(sc);
}
return 0;
}
return 1;/* Not Completed*/
}
static int
intsmb_slvintr(device_t dev)
{
struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
int status,retval;
retval=1;
status=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBSLVSTS);
if(status&PIIX4_SMBSLVSTS_BUSY)
return retval;
if(status&PIIX4_SMBSLVSTS_ALART){
intsmb_alrintr(dev);
retval=0;
}else if(status&~(PIIX4_SMBSLVSTS_ALART|PIIX4_SMBSLVSTS_SDW2
|PIIX4_SMBSLVSTS_SDW1)){
retval=0;
}
/*Reset Status Register*/
bus_space_write_1(sc->st,sc->sh,PIIX4_SMBSLVSTS,PIIX4_SMBSLVSTS_ALART|
PIIX4_SMBSLVSTS_SDW2|PIIX4_SMBSLVSTS_SDW1|
PIIX4_SMBSLVSTS_SLV);
return retval;
}
static void intsmb_alrintr(device_t dev)
{
struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
int slvcnt;
#ifdef ENABLE_ALART
int error;
#endif
/*stop generating INTR from ALART*/
slvcnt=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBSLVCNT);
#ifdef ENABLE_ALART
bus_space_write_1(sc->st,sc->sh,PIIX4_SMBSLVCNT,
slvcnt&~PIIX4_SMBSLVCNT_ALTEN) ;
#endif
DELAY(5);
/*ask bus who assert it and then ask it what's the matter. */
#ifdef ENABLE_ALART
error=intsmb_free(dev);
if(!error){
bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,SMBALTRESP
|LSB);
intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_BYTE,1);
if(!(error=intsmb_stop_poll(dev))){
u_int8_t addr;
addr=bus_space_read_1(sc->st,sc->sh,
PIIX4_SMBHSTDAT0);
printf("ALART_RESPONSE: 0x%x\n", addr);
}
}else{
printf("ERROR\n");
}
/*Re-enable INTR from ALART*/
bus_space_write_1(sc->st,sc->sh,PIIX4_SMBSLVCNT,
slvcnt|PIIX4_SMBSLVCNT_ALTEN) ;
DELAY(5);
#endif
return;
}
static void
intsmb_start(device_t dev,unsigned char cmd,int nointr)
{
struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
unsigned char tmp;
tmp=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTCNT);
tmp&= 0xe0;
tmp |= cmd;
tmp |=PIIX4_SMBHSTCNT_START;
/*While not in autoconfiguration Intrrupt Enabled*/
if(!cold||!nointr)
tmp |=PIIX4_SMBHSTCNT_INTREN;
bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCNT,tmp);
}
/*Polling Code. Polling is not encouraged
* because It is required to wait for the device get busy.
*(29063505.pdf from Intel)
* But during boot,intrrupt cannot be used.
* so use polling code while in autoconfiguration.
*/
static int
intsmb_stop_poll(device_t dev){
int error,i;
struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
/*
* In smbtx driver ,Simply waiting.
* This loops 100-200 times.
*/
for(i=0;i<0x7fff;i++){
if((bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTSTS)
&PIIX4_SMBHSTSTAT_BUSY)){
break;
}
}
for(i=0;i<0x7fff;i++){
int status;
status=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTSTS);
if(!(status&PIIX4_SMBHSTSTAT_BUSY)){
sc->isbusy=0;
error=(status&PIIX4_SMBHSTSTAT_ERR)?EIO :
(status&PIIX4_SMBHSTSTAT_BUSC)?EBUSY:
(status&PIIX4_SMBHSTSTAT_FAIL)?EIO:0;
if(error==0&&!(status&PIIX4_SMBHSTSTAT_INTR)){
printf("unknown cause why?");
}
return error;
}
}
{
int tmp;
sc->isbusy=0;
tmp=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTCNT);
bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCNT,
tmp&~PIIX4_SMBHSTCNT_INTREN);
}
return EIO;
}
/*
*wait for completion and return result.
*/
static int
intsmb_stop(device_t dev){
int error;
intrmask_t s;
struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
if(cold){
/*So that it can use device during probing device on SMBus.*/
error=intsmb_stop_poll(dev);
return error;
}else{
if(!tsleep(sc,(PWAIT)|PCATCH,"SMBWAI",hz/8)){
int status;
status=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTSTS);
if(!(status&PIIX4_SMBHSTSTAT_BUSY)){
error=(status&PIIX4_SMBHSTSTAT_ERR)?EIO :
(status&PIIX4_SMBHSTSTAT_BUSC)?EBUSY:
(status&PIIX4_SMBHSTSTAT_FAIL)?EIO:0;
if(error==0&&!(status&PIIX4_SMBHSTSTAT_INTR)){
printf("intsmb%d:unknown cause why?\n",
device_get_unit(dev));
}
#ifdef ENABLE_ALART
bus_space_write_1(sc->st,sc->sh,
PIIX4_SMBSLVCNT,PIIX4_SMBSLVCNT_ALTEN);
#endif
return error;
}
}
}
/*Timeout Procedure*/
s=splhigh();
sc->isbusy=0;
/*Re-enable supressed intrrupt from slave part*/
bus_space_write_1(sc->st,sc->sh,
PIIX4_SMBSLVCNT,PIIX4_SMBSLVCNT_ALTEN);
splx(s);
return EIO;
}
static int
intsmb_quick(device_t dev, u_char slave, int how)
{
int error=0;
u_char data;
struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
data=slave;
/*Quick command is part of Address, I think*/
switch(how){
case SMB_QWRITE:
data&=~LSB;
break;
case SMB_QREAD:
data|=LSB;
break;
default:
error=EINVAL;
}
if(!error){
error=intsmb_free(dev);
if(!error){
bus_space_write_1(sc->st,sc->sh,
PIIX4_SMBHSTADD,data);
intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_QUICK,0);
error=intsmb_stop(dev);
}
}
return (error);
}
static int
intsmb_sendb(device_t dev, u_char slave, char byte)
{
int error;
struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
error=intsmb_free(dev);
if(!error){
bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave&~LSB);
bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,byte);
intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_BYTE,0);
error=intsmb_stop(dev);
}
return (error);
}
static int
intsmb_recvb(device_t dev, u_char slave, char *byte)
{
int error;
struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
error=intsmb_free(dev);
if(!error){
bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave
|LSB);
intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_BYTE,0);
if(!(error=intsmb_stop(dev))){
#ifdef RECV_IS_IN_CMD
/*Linux SMBus stuff also troubles
Because Intel's datasheet will not make clear.
*/
*byte=bus_space_read_1(sc->st,sc->sh,
PIIX4_SMBHSTCMD);
#else
*byte=bus_space_read_1(sc->st,sc->sh,
PIIX4_SMBHSTDAT0);
#endif
}
}
return (error);
}
static int
intsmb_writeb(device_t dev, u_char slave, char cmd, char byte)
{
int error;
struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
error=intsmb_free(dev);
if(!error){
bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave&~LSB);
bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,cmd);
bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTDAT0,byte);
intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_BDATA,0);
error=intsmb_stop(dev);
}
return (error);
}
static int
intsmb_writew(device_t dev, u_char slave, char cmd, short word)
{
int error;
struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
error=intsmb_free(dev);
if(!error){
bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave&~LSB);
bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,cmd);
bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTDAT0,
word&0xff);
bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTDAT1,
(word>>8)&0xff);
intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_WDATA,0);
error=intsmb_stop(dev);
}
return (error);
}
static int
intsmb_readb(device_t dev, u_char slave, char cmd, char *byte)
{
int error;
struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
error=intsmb_free(dev);
if(!error){
bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave|LSB);
bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,cmd);
intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_BDATA,0);
if(!(error=intsmb_stop(dev))){
*byte=bus_space_read_1(sc->st,sc->sh,
PIIX4_SMBHSTDAT0);
}
}
return (error);
}
static int
intsmb_readw(device_t dev, u_char slave, char cmd, short *word)
{
int error;
struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
error=intsmb_free(dev);
if(!error){
bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave|LSB);
bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,cmd);
intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_WDATA,0);
if(!(error=intsmb_stop(dev))){
*word=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTDAT0)&0xff;
*word|=(bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTDAT1)&0xff)<<8;
}
}
return (error);
}
/*
* Data sheet claims that it implements all function, but also claims
* that it implements 7 function and not mention PCALL. So I don't know
* whether it will work.
*/
static int
intsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata)
{
#ifdef PROCCALL_TEST
int error;
struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
error=intsmb_free(dev);
if(!error){
bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave&~LSB);
bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,cmd);
bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTDAT0,sdata&0xff);
bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTDAT1,(sdata&0xff)>>8);
intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_WDATA,0);
}
if(!(error=intsmb_stop(dev))){
*rdata=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTDAT0)&0xff;
*rdata|=(bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTDAT1)&0xff)<<8;
}
return error;
#else
return 0;
#endif
}
static int
intsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf)
{
int error,i;
struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
error=intsmb_free(dev);
if(count>SMBBLOCKTRANS_MAX||count==0)
error=EINVAL;
if(!error){
/*Reset internal array index*/
bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTCNT);
bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave&~LSB);
bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,cmd);
for(i=0;i<count;i++){
bus_space_write_1(sc->st,sc->sh,PIIX4_SMBBLKDAT,buf[i]);
}
bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTDAT0,count);
intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_BLOCK,0);
error=intsmb_stop(dev);
}
return (error);
}
static int
intsmb_bread(device_t dev, u_char slave, char cmd, u_char count, char *buf)
{
int error,i;
struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
error=intsmb_free(dev);
if(count>SMBBLOCKTRANS_MAX||count==0)
error=EINVAL;
if(!error){
/*Reset internal array index*/
bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTCNT);
bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave|LSB);
bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,cmd);
bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTDAT0,count);
intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_BLOCK,0);
error=intsmb_stop(dev);
if(!error){
bzero(buf,count);/*Is it needed?*/
count= bus_space_read_1(sc->st,sc->sh,
PIIX4_SMBHSTDAT0);
if(count!=0&&count<=SMBBLOCKTRANS_MAX){
for(i=0;i<count;i++){
buf[i]=bus_space_read_1(sc->st,
sc->sh,
PIIX4_SMBBLKDAT);
}
}
else{
error=EIO;
}
}
}
return (error);
}
DRIVER_MODULE(intsmb, intpm , intpm_driver, intsmb_devclass, 0, 0);
static void intpm_intr(void *arg);
static int
intpm_attach(device_t dev)
{
int value;
int unit=device_get_unit(dev);
void *ih;
int error;
char * str;
{
struct intpm_pci_softc *sciic;
device_t smbinterface;
int rid;
struct resource *res;
sciic=device_get_softc(dev);
if(sciic==NULL){
return ENOMEM;
}
rid=PCI_BASE_ADDR_SMB;
res=bus_alloc_resource_any(dev,SYS_RES_IOPORT,&rid,RF_ACTIVE);
if(res==NULL){
device_printf(dev,"Could not allocate Bus space\n");
return ENXIO;
}
sciic->smbst=rman_get_bustag(res);
sciic->smbsh=rman_get_bushandle(res);
#ifdef __i386__
device_printf(dev,"%s %lx\n",
(sciic->smbst==I386_BUS_SPACE_IO)?
"I/O mapped":"Memory",
rman_get_start(res));
#endif
#ifndef NO_CHANGE_PCICONF
pci_write_config(dev,PCIR_INTLINE,0x9,1);
pci_write_config(dev,PCI_HST_CFG_SMB,
PCI_INTR_SMB_IRQ9|PCI_INTR_SMB_ENABLE,1);
#endif
value=pci_read_config(dev,PCI_HST_CFG_SMB,1);
switch(value&0xe){
case PCI_INTR_SMB_SMI:
str="SMI";
break;
case PCI_INTR_SMB_IRQ9:
str="IRQ 9";
break;
default:
str="BOGUS";
}
device_printf(dev,"intr %s %s ",str,((value&1)? "enabled":"disabled"));
value=pci_read_config(dev,PCI_REVID_SMB,1);
printf("revision %d\n",value);
/*
* Install intr HANDLER here
*/
rid=0;
res=bus_alloc_resource(dev,SYS_RES_IRQ,&rid,9,9,1,RF_SHAREABLE|RF_ACTIVE);
if(res==NULL){
device_printf(dev,"could not allocate irq");
return ENOMEM;
}
error=bus_setup_intr(dev,res,INTR_TYPE_MISC, (driver_intr_t *) intpm_intr,sciic,&ih);
if(error){
device_printf(dev,"Failed to map intr\n");
return error;
}
smbinterface=device_add_child(dev,"intsmb",unit);
if(!smbinterface){
printf("intsmb%d:could not add SMBus device\n",unit);
}
device_probe_and_attach(smbinterface);
}
value=pci_read_config(dev,PCI_BASE_ADDR_PM,4);
printf("intpm%d: PM %s %x \n",unit,(value&1)?"I/O mapped":"Memory",value&0xfffe);
return 0;
}
static int
intpm_probe(device_t dev)
{
struct _pcsid *ep =pci_ids;
u_int32_t device_id=pci_get_devid(dev);
while (ep->type && ep->type != device_id)
++ep;
if(ep->desc!=NULL){
device_set_desc(dev,ep->desc);
bus_set_resource(dev,SYS_RES_IRQ,0,9,1); /* XXX setup intr resource */
return (BUS_PROBE_DEFAULT);
}else{
return ENXIO;
}
}
DRIVER_MODULE(intpm, pci , intpm_pci_driver, intpm_devclass, 0, 0);
MODULE_DEPEND(intpm, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER);
MODULE_VERSION(intpm, 1);
static void intpm_intr(void *arg)
{
struct intpm_pci_softc *sc;
sc=(struct intpm_pci_softc *)arg;
intsmb_intr(sc->smbus);
intsmb_slvintr(sc->smbus);
}