intpm.c revision 46024
1/*-
2 * Copyright (c) 1998, 1999 Takanori Watanabe
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *        notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *        notice, this list of conditions and the following disclaimer in the
12 *        documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.    IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 *	$Id: intpm.c,v 1.5 1999/04/21 07:26:29 peter Exp $
27 */
28
29#include "pci.h"
30#include "intpm.h"
31
32#if NPCI > 0
33#if NINTPM >0
34/* I don't think the chip is used in other architecture. :-)*/
35#include <sys/param.h>
36#include <sys/systm.h>
37#include <sys/kernel.h>
38
39#include <machine/bus_pio.h>
40#include <machine/bus_memio.h>
41#include <machine/bus.h>
42
43#include <machine/clock.h>
44#include <sys/uio.h>
45#include <sys/module.h>
46#include <sys/bus.h>
47#include <sys/conf.h>
48#include <sys/malloc.h>
49#include <sys/buf.h>
50
51#include <dev/smbus/smbconf.h>
52
53#include "smbus_if.h"
54
55/*This should be removed if force_pci_map_int supported*/
56#include <sys/interrupt.h>
57
58#include <pci/pcireg.h>
59#include <pci/pcivar.h>
60#include <pci/intpmreg.h>
61
62#include "opt_intpm.h"
63
64static struct _pcsid
65{
66        pcidi_t type;
67	char	*desc;
68} pci_ids[] =
69{
70	{ 0x71138086,"Intel 82371AB Power management controller"},
71
72	{ 0x00000000,	NULL					}
73};
74static int intsmb_probe(device_t);
75static int intsmb_attach(device_t);
76static void intsmb_print_child(device_t, device_t);
77
78static int intsmb_intr(device_t dev);
79static int intsmb_slvintr(device_t dev);
80static void  intsmb_alrintr(device_t dev);
81static int intsmb_callback(device_t dev, int index, caddr_t data);
82static int intsmb_quick(device_t dev, u_char slave, int how);
83static int intsmb_sendb(device_t dev, u_char slave, char byte);
84static int intsmb_recvb(device_t dev, u_char slave, char *byte);
85static int intsmb_writeb(device_t dev, u_char slave, char cmd, char byte);
86static int intsmb_writew(device_t dev, u_char slave, char cmd, short word);
87static int intsmb_readb(device_t dev, u_char slave, char cmd, char *byte);
88static int intsmb_readw(device_t dev, u_char slave, char cmd, short *word);
89static int intsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata);
90static int intsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf);
91static int intsmb_bread(device_t dev, u_char slave, char cmd, u_char count, char *buf);
92static void intsmb_start(device_t dev,u_char cmd,int nointr);
93static int intsmb_stop(device_t dev);
94static int intsmb_stop_poll(device_t dev);
95static int intsmb_free(device_t dev);
96static struct intpm_pci_softc *intpm_alloc(int unit);
97static const char* intpm_probe __P((pcici_t tag, pcidi_t type));
98static void intpm_attach __P((pcici_t config_id, int unit));
99static devclass_t intsmb_devclass;
100
101static device_method_t intpm_methods[]={
102        DEVMETHOD(device_probe,intsmb_probe),
103        DEVMETHOD(device_attach,intsmb_attach),
104
105        DEVMETHOD(bus_print_child, intsmb_print_child),
106
107        DEVMETHOD(smbus_callback,intsmb_callback),
108        DEVMETHOD(smbus_quick,intsmb_quick),
109        DEVMETHOD(smbus_sendb,intsmb_sendb),
110        DEVMETHOD(smbus_recvb,intsmb_recvb),
111        DEVMETHOD(smbus_writeb,intsmb_writeb),
112        DEVMETHOD(smbus_writew,intsmb_writew),
113        DEVMETHOD(smbus_readb,intsmb_readb),
114        DEVMETHOD(smbus_readw,intsmb_readw),
115        DEVMETHOD(smbus_pcall,intsmb_pcall),
116        DEVMETHOD(smbus_bwrite,intsmb_bwrite),
117        DEVMETHOD(smbus_bread,intsmb_bread),
118        {0,0}
119};
120
121static struct intpm_pci_softc{
122        bus_space_tag_t smbst;
123        bus_space_handle_t smbsh;
124	bus_space_tag_t pmst;
125	bus_space_handle_t pmsh;
126        pcici_t cfg;
127	device_t  smbus;
128}intpm_pci[NINTPM];
129
130
131struct intsmb_softc{
132        struct intpm_pci_softc *pci_sc;
133        bus_space_tag_t st;
134        bus_space_handle_t sh;
135        device_t smbus;
136        int isbusy;
137};
138static driver_t intpm_driver = {
139        "intsmb",
140        intpm_methods,
141        DRIVER_TYPE_MISC,
142        sizeof(struct intsmb_softc),
143};
144static u_long intpm_count ;
145
146static struct	pci_device intpm_device = {
147	"intpm",
148 	intpm_probe,
149	intpm_attach,
150	&intpm_count
151};
152
153#ifdef COMPAT_PCI_DRIVER
154COMPAT_PCI_DRIVER (intpm, intpm_device);
155#else
156DATA_SET (pcidevice_set, intpm_device);
157#endif /* COMPAT_PCI_DRIVER */
158
159static int
160intsmb_probe(device_t dev)
161{
162        struct intsmb_softc *sc =(struct intsmb_softc *) device_get_softc(dev);
163        sc->smbus=smbus_alloc_bus(dev);
164        if (!sc->smbus)
165                return (EINVAL);    /* XXX don't know what to return else */
166        device_set_desc(dev,"Intel PIIX4 SMBUS Interface");
167
168        return (0);          /* XXX don't know what to return else */
169}
170static int
171intsmb_attach(device_t dev)
172{
173        struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
174        sc->pci_sc=&intpm_pci[device_get_unit(dev)];
175        sc->isbusy=0;
176	sc->sh=sc->pci_sc->smbsh;
177	sc->st=sc->pci_sc->smbst;
178	sc->pci_sc->smbus=dev;
179        device_probe_and_attach(sc->smbus);
180#ifdef ENABLE_ALART
181	/*Enable Arart*/
182	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        int error;
672#ifdef APIC_IO
673        int nextpin, muxcnt;
674#endif
675	/* Spec sheet claims that it use IRQ 9*/
676        int irq = 9;
677        void *idesc;
678
679        idesc = inthand_add(NULL, irq, func, arg, maskptr, 0);
680        if (idesc == 0)
681                return 0;
682#ifdef APIC_IO
683        nextpin = next_apic_irq(irq);
684
685        if (nextpin < 0)
686                return 1;
687
688        /*
689         * Attempt handling of some broken mp tables.
690         *
691         * It's OK to yell (since the mp tables are broken).
692         *
693         * Hanging in the boot is not OK
694         */
695
696        muxcnt = 2;
697        nextpin = next_apic_irq(nextpin);
698        while (muxcnt < 5 && nextpin >= 0) {
699                muxcnt++;
700                nextpin = next_apic_irq(nextpin);
701        }
702        if (muxcnt >= 5) {
703                printf("bogus MP table, more than 4 IO APIC pins connected to the same PCI device or ISA/EISA interrupt\n");
704                return 0;
705        }
706
707        printf("bogus MP table, %d IO APIC pins connected to the same PCI device or ISA/EISA interrupt\n", muxcnt);
708
709        nextpin = next_apic_irq(irq);
710        while (nextpin >= 0) {
711                idesc = inthand_add(NULL, nextpin, func, arg, maskptr, 0);
712                if (error != 0)
713                        return 0;
714                printf("Registered extra interrupt handler for int %d (in addition to int %d)\n", nextpin, irq);
715                nextpin = next_apic_irq(nextpin);
716        }
717#endif
718        return 1;
719}
720static void
721intpm_attach(config_id, unit)
722     pcici_t config_id;
723     int	unit;
724{
725        int value;
726
727        char * str;
728        {
729                struct intpm_pci_softc *sciic;
730                device_t smbinterface;
731                value=pci_cfgread(config_id,PCI_BASE_ADDR_SMB,4);
732                sciic=intpm_alloc(unit);
733                if(sciic==NULL){
734                        return;
735                }
736
737		sciic->smbst=(value&1)?I386_BUS_SPACE_IO:I386_BUS_SPACE_MEM;
738
739		/*Calling pci_map_port is better.But bus_space_handle_t !=
740		 * pci_port_t, so I don't call support routine while
741		 * bus_space_??? support routine will be appear.
742		 */
743                sciic->smbsh=value&(~1);
744		if(sciic->smbsh==I386_BUS_SPACE_MEM){
745		       /*According to the spec, this will not occur*/
746                       int dummy;
747		       pci_map_mem(config_id,PCI_BASE_ADDR_SMB,&sciic->smbsh,&dummy);
748		}
749                printf("intpm%d: %s %x ",unit,
750		       (sciic->smbst==I386_BUS_SPACE_IO)?"I/O mapped":"Memory",
751		       sciic->smbsh);
752#ifndef NO_CHANGE_PCICONF
753		pci_cfgwrite(config_id,PCIR_INTLINE,0x09,1);
754                pci_cfgwrite(config_id,PCI_HST_CFG_SMB,
755			     PCI_INTR_SMB_IRQ9|PCI_INTR_SMB_ENABLE,1);
756#endif
757		config_id->intline=pci_cfgread(config_id,PCIR_INTLINE,1);
758		printf("ALLOCED IRQ %d ",config_id->intline);
759                value=pci_cfgread(config_id,PCI_HST_CFG_SMB,1);
760                switch(value&0xe){
761                case PCI_INTR_SMB_SMI:
762                        str="SMI";
763                        break;
764                case PCI_INTR_SMB_IRQ9:
765                        str="IRQ 9";
766                        break;
767                default:
768                        str="BOGUS";
769                }
770                printf("intr %s %s ",str,((value&1)? "enabled":"disabled"));
771                value=pci_cfgread(config_id,PCI_REVID_SMB,1);
772                printf("revision %d\n",value);
773                /*
774                 * Install intr HANDLER here
775                 */
776                if(force_pci_map_int(config_id,intpm_intr,sciic,&net_imask)==0){
777                        printf("intpm%d: Failed to map intr\n",unit);
778                }
779                smbinterface=device_add_child(root_bus,"intsmb",unit,NULL);
780                device_probe_and_attach(smbinterface);
781        }
782        value=pci_cfgread(config_id,PCI_BASE_ADDR_PM,4);
783        printf("intpm%d: PM %s %x \n",unit,(value&1)?"I/O mapped":"Memory",value&0xfffe);
784        return;
785}
786static void intpm_intr(void *arg)
787{
788        struct intpm_pci_softc *sc;
789        sc=(struct intpm_pci_softc *)arg;
790	intsmb_intr(sc->smbus);
791	intsmb_slvintr(sc->smbus);
792}
793#endif /* NPCI > 0 */
794#endif
795