intpm.c revision 46590
11573Srgrimes/*-
21573Srgrimes * Copyright (c) 1998, 1999 Takanori Watanabe
31573Srgrimes * All rights reserved.
41573Srgrimes *
51573Srgrimes * Redistribution and use in source and binary forms, with or without
61573Srgrimes * modification, are permitted provided that the following conditions
71573Srgrimes * are met:
81573Srgrimes * 1. Redistributions of source code must retain the above copyright
91573Srgrimes *        notice, this list of conditions and the following disclaimer.
101573Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111573Srgrimes *        notice, this list of conditions and the following disclaimer in the
121573Srgrimes *        documentation and/or other materials provided with the distribution.
131573Srgrimes *
141573Srgrimes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
151573Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
161573Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
171573Srgrimes * ARE DISCLAIMED.    IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
181573Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
191573Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
201573Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
211573Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
221573Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
231573Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
241573Srgrimes * SUCH DAMAGE.
251573Srgrimes *
261573Srgrimes *	$Id: intpm.c,v 1.6 1999/04/24 20:14:02 peter Exp $
271573Srgrimes */
281573Srgrimes
291573Srgrimes#include "pci.h"
301573Srgrimes#include "intpm.h"
311573Srgrimes
321573Srgrimes#if NPCI > 0
331573Srgrimes#if NINTPM >0
341573Srgrimes/* I don't think the chip is used in other architecture. :-)*/
351573Srgrimes#include <sys/param.h>
361573Srgrimes#include <sys/systm.h>
371573Srgrimes#include <sys/kernel.h>
381573Srgrimes
391573Srgrimes#include <machine/bus_pio.h>
401573Srgrimes#include <machine/bus_memio.h>
411573Srgrimes#include <machine/bus.h>
421573Srgrimes
431573Srgrimes#include <machine/clock.h>
441573Srgrimes#include <sys/uio.h>
451573Srgrimes#include <sys/module.h>
4611659Sphk#include <sys/bus.h>
471573Srgrimes#include <sys/conf.h>
481573Srgrimes#include <sys/malloc.h>
491573Srgrimes#include <sys/buf.h>
501573Srgrimes
511573Srgrimes#include <dev/smbus/smbconf.h>
521573Srgrimes
531573Srgrimes#include "smbus_if.h"
541573Srgrimes
551573Srgrimes/*This should be removed if force_pci_map_int supported*/
561573Srgrimes#include <sys/interrupt.h>
571573Srgrimes
581573Srgrimes#include <pci/pcireg.h>
591573Srgrimes#include <pci/pcivar.h>
601573Srgrimes#include <pci/intpmreg.h>
611573Srgrimes
621573Srgrimes#include "opt_intpm.h"
631573Srgrimes
641573Srgrimesstatic struct _pcsid
651573Srgrimes{
661573Srgrimes        pcidi_t type;
671573Srgrimes	char	*desc;
681573Srgrimes} pci_ids[] =
6942353Sdes{
7042353Sdes	{ 0x71138086,"Intel 82371AB Power management controller"},
7142353Sdes
7242353Sdes	{ 0x00000000,	NULL					}
731573Srgrimes};
741573Srgrimesstatic int intsmb_probe(device_t);
751573Srgrimesstatic int intsmb_attach(device_t);
761573Srgrimesstatic void intsmb_print_child(device_t, device_t);
771573Srgrimes
781573Srgrimesstatic int intsmb_intr(device_t dev);
7942353Sdesstatic int intsmb_slvintr(device_t dev);
8042353Sdesstatic void  intsmb_alrintr(device_t dev);
8142353Sdesstatic int intsmb_callback(device_t dev, int index, caddr_t data);
8242353Sdesstatic int intsmb_quick(device_t dev, u_char slave, int how);
831573Srgrimesstatic int intsmb_sendb(device_t dev, u_char slave, char byte);
841573Srgrimesstatic int intsmb_recvb(device_t dev, u_char slave, char *byte);
851573Srgrimesstatic int intsmb_writeb(device_t dev, u_char slave, char cmd, char byte);
861573Srgrimesstatic int intsmb_writew(device_t dev, u_char slave, char cmd, short word);
871573Srgrimesstatic int intsmb_readb(device_t dev, u_char slave, char cmd, char *byte);
881573Srgrimesstatic int intsmb_readw(device_t dev, u_char slave, char cmd, short *word);
891573Srgrimesstatic int intsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata);
901573Srgrimesstatic int intsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf);
911573Srgrimesstatic int intsmb_bread(device_t dev, u_char slave, char cmd, u_char count, char *buf);
921573Srgrimesstatic void intsmb_start(device_t dev,u_char cmd,int nointr);
931573Srgrimesstatic int intsmb_stop(device_t dev);
941573Srgrimesstatic int intsmb_stop_poll(device_t dev);
951573Srgrimesstatic int intsmb_free(device_t dev);
961573Srgrimesstatic struct intpm_pci_softc *intpm_alloc(int unit);
971573Srgrimesstatic const char* intpm_probe __P((pcici_t tag, pcidi_t type));
981573Srgrimesstatic void intpm_attach __P((pcici_t config_id, int unit));
991573Srgrimesstatic devclass_t intsmb_devclass;
1001573Srgrimes
1011573Srgrimesstatic device_method_t intpm_methods[]={
1021573Srgrimes        DEVMETHOD(device_probe,intsmb_probe),
1031573Srgrimes        DEVMETHOD(device_attach,intsmb_attach),
1041573Srgrimes
1051573Srgrimes        DEVMETHOD(bus_print_child, intsmb_print_child),
1061573Srgrimes
1071573Srgrimes        DEVMETHOD(smbus_callback,intsmb_callback),
1081573Srgrimes        DEVMETHOD(smbus_quick,intsmb_quick),
1091573Srgrimes        DEVMETHOD(smbus_sendb,intsmb_sendb),
1101573Srgrimes        DEVMETHOD(smbus_recvb,intsmb_recvb),
1111573Srgrimes        DEVMETHOD(smbus_writeb,intsmb_writeb),
1121573Srgrimes        DEVMETHOD(smbus_writew,intsmb_writew),
1131573Srgrimes        DEVMETHOD(smbus_readb,intsmb_readb),
1141573Srgrimes        DEVMETHOD(smbus_readw,intsmb_readw),
1151573Srgrimes        DEVMETHOD(smbus_pcall,intsmb_pcall),
1161573Srgrimes        DEVMETHOD(smbus_bwrite,intsmb_bwrite),
1171573Srgrimes        DEVMETHOD(smbus_bread,intsmb_bread),
1181573Srgrimes        {0,0}
1191573Srgrimes};
1201573Srgrimes
1211573Srgrimesstatic struct intpm_pci_softc{
1221573Srgrimes        bus_space_tag_t smbst;
1231573Srgrimes        bus_space_handle_t smbsh;
1241573Srgrimes	bus_space_tag_t pmst;
1251573Srgrimes	bus_space_handle_t pmsh;
1261573Srgrimes        pcici_t cfg;
1271573Srgrimes	device_t  smbus;
1281573Srgrimes}intpm_pci[NINTPM];
1291573Srgrimes
1301573Srgrimes
1311573Srgrimesstruct intsmb_softc{
1321573Srgrimes        struct intpm_pci_softc *pci_sc;
1331573Srgrimes        bus_space_tag_t st;
1341573Srgrimes        bus_space_handle_t sh;
1351573Srgrimes        device_t smbus;
1361573Srgrimes        int isbusy;
1371573Srgrimes};
1381573Srgrimesstatic driver_t intpm_driver = {
1391573Srgrimes        "intsmb",
1401573Srgrimes        intpm_methods,
1411573Srgrimes        DRIVER_TYPE_MISC,
1421573Srgrimes        sizeof(struct intsmb_softc),
1431573Srgrimes};
1441573Srgrimesstatic u_long intpm_count ;
1451573Srgrimes
1461573Srgrimesstatic struct	pci_device intpm_device = {
1471573Srgrimes	"intpm",
1481573Srgrimes 	intpm_probe,
1491573Srgrimes	intpm_attach,
1501573Srgrimes	&intpm_count
1511573Srgrimes};
1521573Srgrimes
1531573Srgrimes#ifdef COMPAT_PCI_DRIVER
1541573SrgrimesCOMPAT_PCI_DRIVER (intpm, intpm_device);
1551573Srgrimes#else
1561573SrgrimesDATA_SET (pcidevice_set, intpm_device);
1571573Srgrimes#endif /* COMPAT_PCI_DRIVER */
1581573Srgrimes
1591573Srgrimesstatic int
1601573Srgrimesintsmb_probe(device_t dev)
1611573Srgrimes{
1621573Srgrimes        struct intsmb_softc *sc =(struct intsmb_softc *) device_get_softc(dev);
1631573Srgrimes        sc->smbus=smbus_alloc_bus(dev);
1641573Srgrimes        if (!sc->smbus)
1651573Srgrimes                return (EINVAL);    /* XXX don't know what to return else */
1661573Srgrimes        device_set_desc(dev,"Intel PIIX4 SMBUS Interface");
1671573Srgrimes
1681573Srgrimes        return (0);          /* XXX don't know what to return else */
1691573Srgrimes}
1701573Srgrimesstatic int
1711573Srgrimesintsmb_attach(device_t dev)
1721573Srgrimes{
1731573Srgrimes        struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
1741573Srgrimes        sc->pci_sc=&intpm_pci[device_get_unit(dev)];
1751573Srgrimes        sc->isbusy=0;
1761573Srgrimes	sc->sh=sc->pci_sc->smbsh;
1771573Srgrimes	sc->st=sc->pci_sc->smbst;
1781573Srgrimes	sc->pci_sc->smbus=dev;
1791573Srgrimes        device_probe_and_attach(sc->smbus);
1801573Srgrimes#ifdef ENABLE_ALART
1811573Srgrimes	/*Enable Arart*/
1821573Srgrimes	bus_space_write_1(sc->st,sc->sh,PIIX4_SMBSLVCNT,
183			  PIIX4_SMBSLVCNT_ALTEN);
184#endif
185        return (0);
186}
187
188static void
189intsmb_print_child(device_t bus, device_t dev)
190{
191	printf(" on %s%d", device_get_name(bus), device_get_unit(bus));
192	return;
193}
194static int
195intsmb_callback(device_t dev, int index, caddr_t data)
196{
197	int error = 0;
198	intrmask_t s;
199	s=splnet();
200	switch (index) {
201	case SMB_REQUEST_BUS:
202		break;
203	case SMB_RELEASE_BUS:
204		break;
205	default:
206		error = EINVAL;
207	}
208	splx(s);
209	return (error);
210}
211/*counterpart of smbtx_smb_free*/
212static        int
213intsmb_free(device_t dev){
214        struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
215        if((bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTSTS)&
216	    PIIX4_SMBHSTSTAT_BUSY)
217#ifdef ENABLE_ALART
218	   ||(bus_space_read_1(sc->st,sc->sh,PIIX4_SMBSLVSTS)&
219	      PIIX4_SMBSLVSTS_BUSY)
220#endif
221	   || sc->isbusy)
222                return EBUSY;
223        sc->isbusy=1;
224	/*Disable Intrrupt in slave part*/
225#ifndef ENABLE_ALART
226	bus_space_write_1(sc->st,sc->sh,PIIX4_SMBSLVCNT,0);
227#endif
228        /*Reset INTR Flag to prepare INTR*/
229	bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTSTS,
230			  (PIIX4_SMBHSTSTAT_INTR|
231			   PIIX4_SMBHSTSTAT_ERR|
232			   PIIX4_SMBHSTSTAT_BUSC|
233			   PIIX4_SMBHSTSTAT_FAIL)
234		);
235        return 0;
236}
237
238static int
239intsmb_intr(device_t dev)
240{
241	struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
242	int status;
243	intrmask_t s;
244	status=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTSTS);
245	if(status&PIIX4_SMBHSTSTAT_BUSY){
246		return 1;
247
248	}
249	s=splhigh();
250	if(sc->isbusy&&(status&(PIIX4_SMBHSTSTAT_INTR|
251				PIIX4_SMBHSTSTAT_ERR|
252				PIIX4_SMBHSTSTAT_BUSC|
253				PIIX4_SMBHSTSTAT_FAIL))){
254		int tmp;
255		sc->isbusy=0;
256		tmp=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTCNT);
257		bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCNT,
258				  tmp&~PIIX4_SMBHSTCNT_INTREN);
259		splx(s);
260		wakeup(sc);
261		return 0;
262	}
263	splx(s);
264	return 1;/* Not Completed*/
265}
266static int
267intsmb_slvintr(device_t dev)
268{
269	struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
270        int status,retval;
271	retval=1;
272        status=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBSLVSTS);
273	if(status&PIIX4_SMBSLVSTS_BUSY)
274		return retval;
275	if(status&PIIX4_SMBSLVSTS_ALART){
276		intsmb_alrintr(dev);
277		retval=0;
278	}else if(status&~(PIIX4_SMBSLVSTS_ALART|PIIX4_SMBSLVSTS_SDW2
279			  |PIIX4_SMBSLVSTS_SDW1)){
280		retval=0;
281	}
282	/*Reset Status Register*/
283	bus_space_write_1(sc->st,sc->sh,PIIX4_SMBSLVSTS,PIIX4_SMBSLVSTS_ALART|
284			  PIIX4_SMBSLVSTS_SDW2|PIIX4_SMBSLVSTS_SDW1|
285			  PIIX4_SMBSLVSTS_SLV);
286	return retval;
287}
288
289static void intsmb_alrintr(device_t dev)
290{
291	struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
292	int slvcnt;
293#ifdef ENABLE_ALART
294	int error;
295#endif
296
297	/*stop generating INTR from ALART*/
298	slvcnt=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBSLVCNT);
299#ifdef ENABLE_ALART
300	bus_space_write_1(sc->st,sc->sh,PIIX4_SMBSLVCNT,
301			  slvcnt&~PIIX4_SMBSLVCNT_ALTEN) ;
302#endif
303	DELAY(5);
304	/*ask bus who assert it and then ask it what's the matter. */
305#ifdef ENABLE_ALART
306	error=intsmb_free(dev);
307	if(!error){
308                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,SMBALTRESP
309                                  |LSB);
310		intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_BYTE,1);
311		if(!(error=intsmb_stop_poll(dev))){
312			volatile u_int8_t *addr;
313			addr=bus_space_read_1(sc->st,sc->sh,
314					      PIIX4_SMBHSTDAT0);
315			printf("ALART_RESPONSE: %p\n", addr);
316		}
317	}else{
318	        printf("ERROR\n");
319	}
320
321	/*Re-enable INTR from ALART*/
322	bus_space_write_1(sc->st,sc->sh,PIIX4_SMBSLVCNT,
323			  slvcnt|PIIX4_SMBSLVCNT_ALTEN) ;
324	DELAY(5);
325#endif
326
327	return;
328}
329static void
330intsmb_start(device_t dev,unsigned char cmd,int nointr)
331{
332	struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
333	unsigned char tmp;
334	tmp=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTCNT);
335	tmp&= 0xe0;
336	tmp |= cmd;
337	tmp |=PIIX4_SMBHSTCNT_START;
338	/*While not in autoconfiguration Intrrupt Enabled*/
339	if(!cold||!nointr)
340		tmp |=PIIX4_SMBHSTCNT_INTREN;
341	bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCNT,tmp);
342}
343
344/*Polling Code. Polling is not encouraged
345 * because It is required to wait for the device get busy.
346 *(29063505.pdf from Intel)
347 * But during boot,intrrupt cannot be used.
348 * so use polling code while in autoconfiguration.
349 */
350
351static        int
352intsmb_stop_poll(device_t dev){
353        int error,i;
354        struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
355	/*
356	 *  In smbtx driver ,Simply waiting.
357	 *  This loops 100-200 times.
358	 */
359	for(i=0;i<0x7fff;i++){
360                if((bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTSTS)
361		    &PIIX4_SMBHSTSTAT_BUSY)){
362                        break;
363                }
364	}
365	for(i=0;i<0x7fff;i++){
366		int status;
367		status=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTSTS);
368		if(!(status&PIIX4_SMBHSTSTAT_BUSY)){
369			sc->isbusy=0;
370			error=(status&PIIX4_SMBHSTSTAT_ERR)?EIO :
371				(status&PIIX4_SMBHSTSTAT_BUSC)?EBUSY:
372				(status&PIIX4_SMBHSTSTAT_FAIL)?EIO:0;
373			if(error==0&&!(status&PIIX4_SMBHSTSTAT_INTR)){
374				printf("unknown cause why?");
375			}
376			return error;
377		}
378	}
379	sc->isbusy=0;
380	return EIO;
381}
382/*
383 *wait for completion and return result.
384 */
385static        int
386intsmb_stop(device_t dev){
387        int error;
388        struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
389	if(cold){
390		/*So that it can use device during probing device on SMBus.*/
391		error=intsmb_stop_poll(dev);
392		return error;
393	}else{
394		if(!tsleep(sc,(PWAIT)|PCATCH,"SMBWAI",hz/8)){
395			int status;
396			status=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTSTS);
397			if(!(status&PIIX4_SMBHSTSTAT_BUSY)){
398				error=(status&PIIX4_SMBHSTSTAT_ERR)?EIO :
399					(status&PIIX4_SMBHSTSTAT_BUSC)?EBUSY:
400					(status&PIIX4_SMBHSTSTAT_FAIL)?EIO:0;
401				if(error==0&&!(status&PIIX4_SMBHSTSTAT_INTR)){
402					printf("intsmb%d:unknown cause why?\n",
403					       device_get_unit(dev));
404				}
405#ifdef ENABLE_ALART
406				bus_space_write_1(sc->st,sc->sh,
407						  PIIX4_SMBSLVCNT,PIIX4_SMBSLVCNT_ALTEN);
408#endif
409				return error;
410			}
411		}
412	}
413	/*Timeout Procedure*/
414	sc->isbusy=0;
415	/*Re-enable supressed intrrupt from slave part*/
416	bus_space_write_1(sc->st,sc->sh,
417			  PIIX4_SMBSLVCNT,PIIX4_SMBSLVCNT_ALTEN);
418        return EIO;
419}
420
421static int
422intsmb_quick(device_t dev, u_char slave, int how)
423{
424        int error=0;
425        u_char data;
426        struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
427        data=slave;
428	/*Quick command is part of Address, I think*/
429        switch(how){
430        case SMB_QWRITE:
431                data&=~LSB;
432		break;
433        case SMB_QREAD:
434                data|=LSB;
435                break;
436        default:
437                error=EINVAL;
438        }
439        if(!error){
440	        error=intsmb_free(dev);
441                if(!error){
442                        bus_space_write_1(sc->st,sc->sh,
443					  PIIX4_SMBHSTADD,data);
444			intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_QUICK,0);
445                        error=intsmb_stop(dev);
446                }
447        }
448
449        return (error);
450}
451
452static int
453intsmb_sendb(device_t dev, u_char slave, char byte)
454{
455        int error;
456        struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
457        error=intsmb_free(dev);
458        if(!error){
459                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave&~LSB);
460                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,byte);
461		intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_BYTE,0);
462                error=intsmb_stop(dev);
463        }
464        return (error);
465}
466static int
467intsmb_recvb(device_t dev, u_char slave, char *byte)
468{
469        int error;
470        struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
471        error=intsmb_free(dev);
472        if(!error){
473                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave
474				  |LSB);
475                intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_BYTE,0);
476                if(!(error=intsmb_stop(dev))){
477#ifdef RECV_IS_IN_CMD
478		        /*Linux SMBus stuff also troubles
479			  Because Intel's datasheet will not make clear.
480			 */
481                        *byte=bus_space_read_1(sc->st,sc->sh,
482					       PIIX4_SMBHSTCMD);
483#else
484                        *byte=bus_space_read_1(sc->st,sc->sh,
485					       PIIX4_SMBHSTDAT0);
486#endif
487                }
488        }
489        return (error);
490}
491static int
492intsmb_writeb(device_t dev, u_char slave, char cmd, char byte)
493{
494        int error;
495        struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
496        error=intsmb_free(dev);
497        if(!error){
498                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave&~LSB);
499                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,cmd);
500                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTDAT0,byte);
501		intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_BDATA,0);
502                error=intsmb_stop(dev);
503        }
504        return (error);
505}
506static int
507intsmb_writew(device_t dev, u_char slave, char cmd, short word)
508{
509        int error;
510        struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
511        error=intsmb_free(dev);
512        if(!error){
513                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave&~LSB);
514                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,cmd);
515                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTDAT0,
516				  word&0xff);
517                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTDAT1,
518				  (word>>8)&0xff);
519		intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_WDATA,0);
520                error=intsmb_stop(dev);
521        }
522        return (error);
523}
524
525static int
526intsmb_readb(device_t dev, u_char slave, char cmd, char *byte)
527{
528        int error;
529        struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
530        error=intsmb_free(dev);
531        if(!error){
532                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave|LSB);
533                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,cmd);
534		intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_BDATA,0);
535                if(!(error=intsmb_stop(dev))){
536		        *byte=bus_space_read_1(sc->st,sc->sh,
537					       PIIX4_SMBHSTDAT0);
538                }
539        }
540        return (error);
541}
542static int
543intsmb_readw(device_t dev, u_char slave, char cmd, short *word)
544{
545        int error;
546        struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
547        error=intsmb_free(dev);
548        if(!error){
549                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave|LSB);
550                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,cmd);
551		intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_WDATA,0);
552                if(!(error=intsmb_stop(dev))){
553                        *word=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTDAT0)&0xff;
554                        *word|=(bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTDAT1)&0xff)<<8;
555                }
556        }
557        return (error);
558}
559/*
560 * Data sheet claims that it implements all function, but also claims
561 * that it implements 7 function and not mention PCALL. So I don't know
562 * whether it will work.
563 */
564static int
565intsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata)
566{
567#ifdef PROCCALL_TEST
568        int error;
569        struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
570        error=intsmb_free(dev);
571        if(!error){
572                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave&~LSB);
573                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,cmd);
574                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTDAT0,sdata&0xff);
575                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTDAT1,(sdata&0xff)>>8);
576                intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_WDATA,0);
577        }
578        if(!(error=intsmb_stop(dev))){
579                *rdata=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTDAT0)&0xff;
580                *rdata|=(bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTDAT1)&0xff)<<8;
581        }
582        return error;
583#else
584	return 0;
585#endif
586}
587static int
588intsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf)
589{
590        int error,i;
591        struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
592        error=intsmb_free(dev);
593        if(count>SMBBLOCKTRANS_MAX||count==0)
594                error=EINVAL;
595        if(!error){
596                /*Reset internal array index*/
597                bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTCNT);
598
599                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave&~LSB);
600                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,cmd);
601                for(i=0;i<count;i++){
602                        bus_space_write_1(sc->st,sc->sh,PIIX4_SMBBLKDAT,buf[i]);
603                }
604                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTDAT0,count);
605                intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_BLOCK,0);
606                error=intsmb_stop(dev);
607        }
608        return (error);
609}
610
611static int
612intsmb_bread(device_t dev, u_char slave, char cmd, u_char count, char *buf)
613{
614        int error,i;
615        struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
616        error=intsmb_free(dev);
617        if(count>SMBBLOCKTRANS_MAX||count==0)
618                error=EINVAL;
619        if(!error){
620                /*Reset internal array index*/
621                bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTCNT);
622
623                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave|LSB);
624                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,cmd);
625                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTDAT0,count);
626                intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_BLOCK,0);
627                error=intsmb_stop(dev);
628                if(!error){
629                        bzero(buf,count);/*Is it needed?*/
630                        count= bus_space_read_1(sc->st,sc->sh,
631						PIIX4_SMBHSTDAT0);
632                        if(count!=0&&count<=SMBBLOCKTRANS_MAX){
633			        for(i=0;i<count;i++){
634				        buf[i]=bus_space_read_1(sc->st,
635								sc->sh,
636								PIIX4_SMBBLKDAT);
637				}
638			}
639                        else{
640				error=EIO;
641                        }
642		}
643	}
644        return (error);
645}
646
647DRIVER_MODULE(intsmb, root , intpm_driver, intsmb_devclass, 0, 0);
648
649
650static void intpm_intr __P((void *arg));
651
652static const char*
653intpm_probe (pcici_t tag, pcidi_t type)
654{
655        struct _pcsid	*ep =pci_ids;
656        while (ep->type && ep->type != type)
657                ++ep;
658        return (ep->desc);
659}
660
661static struct intpm_pci_softc *intpm_alloc(int unit){
662        if(unit<NINTPM)
663                return &intpm_pci[unit];
664        else
665                return NULL;
666}
667
668/*Same as pci_map_int but this ignores INTPIN*/
669static int force_pci_map_int(pcici_t cfg, pci_inthand_t *func, void *arg, unsigned *maskptr)
670{
671#ifdef APIC_IO
672        int nextpin, muxcnt;
673#endif
674	/* Spec sheet claims that it use IRQ 9*/
675        int irq = 9;
676        void *idesc;
677
678        idesc = inthand_add(NULL, irq, func, arg, maskptr, 0);
679        if (idesc == 0)
680                return 0;
681#ifdef APIC_IO
682        nextpin = next_apic_irq(irq);
683
684        if (nextpin < 0)
685                return 1;
686
687        /*
688         * Attempt handling of some broken mp tables.
689         *
690         * It's OK to yell (since the mp tables are broken).
691         *
692         * Hanging in the boot is not OK
693         */
694
695        muxcnt = 2;
696        nextpin = next_apic_irq(nextpin);
697        while (muxcnt < 5 && nextpin >= 0) {
698                muxcnt++;
699                nextpin = next_apic_irq(nextpin);
700        }
701        if (muxcnt >= 5) {
702                printf("bogus MP table, more than 4 IO APIC pins connected to the same PCI device or ISA/EISA interrupt\n");
703                return 0;
704        }
705
706        printf("bogus MP table, %d IO APIC pins connected to the same PCI device or ISA/EISA interrupt\n", muxcnt);
707
708        nextpin = next_apic_irq(irq);
709        while (nextpin >= 0) {
710                idesc = inthand_add(NULL, nextpin, func, arg, maskptr, 0);
711                if (idesc == 0)
712                        return 0;
713                printf("Registered extra interrupt handler for int %d (in addition to int %d)\n", nextpin, irq);
714                nextpin = next_apic_irq(nextpin);
715        }
716#endif
717        return 1;
718}
719static void
720intpm_attach(config_id, unit)
721     pcici_t config_id;
722     int	unit;
723{
724        int value;
725
726        char * str;
727        {
728                struct intpm_pci_softc *sciic;
729                device_t smbinterface;
730                value=pci_cfgread(config_id,PCI_BASE_ADDR_SMB,4);
731                sciic=intpm_alloc(unit);
732                if(sciic==NULL){
733                        return;
734                }
735
736		sciic->smbst=(value&1)?I386_BUS_SPACE_IO:I386_BUS_SPACE_MEM;
737
738		/*Calling pci_map_port is better.But bus_space_handle_t !=
739		 * pci_port_t, so I don't call support routine while
740		 * bus_space_??? support routine will be appear.
741		 */
742                sciic->smbsh=value&(~1);
743		if(sciic->smbsh==I386_BUS_SPACE_MEM){
744		       /*According to the spec, this will not occur*/
745                       int dummy;
746		       pci_map_mem(config_id,PCI_BASE_ADDR_SMB,&sciic->smbsh,&dummy);
747		}
748                printf("intpm%d: %s %x ",unit,
749		       (sciic->smbst==I386_BUS_SPACE_IO)?"I/O mapped":"Memory",
750		       sciic->smbsh);
751#ifndef NO_CHANGE_PCICONF
752		pci_cfgwrite(config_id,PCIR_INTLINE,0x09,1);
753                pci_cfgwrite(config_id,PCI_HST_CFG_SMB,
754			     PCI_INTR_SMB_IRQ9|PCI_INTR_SMB_ENABLE,1);
755#endif
756		config_id->intline=pci_cfgread(config_id,PCIR_INTLINE,1);
757		printf("ALLOCED IRQ %d ",config_id->intline);
758                value=pci_cfgread(config_id,PCI_HST_CFG_SMB,1);
759                switch(value&0xe){
760                case PCI_INTR_SMB_SMI:
761                        str="SMI";
762                        break;
763                case PCI_INTR_SMB_IRQ9:
764                        str="IRQ 9";
765                        break;
766                default:
767                        str="BOGUS";
768                }
769                printf("intr %s %s ",str,((value&1)? "enabled":"disabled"));
770                value=pci_cfgread(config_id,PCI_REVID_SMB,1);
771                printf("revision %d\n",value);
772                /*
773                 * Install intr HANDLER here
774                 */
775                if(force_pci_map_int(config_id,intpm_intr,sciic,&net_imask)==0){
776                        printf("intpm%d: Failed to map intr\n",unit);
777                }
778                smbinterface=device_add_child(root_bus,"intsmb",unit,NULL);
779                device_probe_and_attach(smbinterface);
780        }
781        value=pci_cfgread(config_id,PCI_BASE_ADDR_PM,4);
782        printf("intpm%d: PM %s %x \n",unit,(value&1)?"I/O mapped":"Memory",value&0xfffe);
783        return;
784}
785static void intpm_intr(void *arg)
786{
787        struct intpm_pci_softc *sc;
788        sc=(struct intpm_pci_softc *)arg;
789	intsmb_intr(sc->smbus);
790	intsmb_slvintr(sc->smbus);
791}
792#endif /* NPCI > 0 */
793#endif
794