intpm.c revision 43288
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.1 1999/01/24 18:13:31 nsouch 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
153DATA_SET (pcidevice_set, intpm_device);
154
155static int
156intsmb_probe(device_t dev)
157{
158        struct intsmb_softc *sc =(struct intsmb_softc *) device_get_softc(dev);
159        sc->smbus=smbus_alloc_bus(dev);
160        if (!sc->smbus)
161                return (EINVAL);    /* XXX don't know what to return else */
162        device_set_desc(dev,"Intel PIIX4 SMBUS Interface");
163
164        return (0);          /* XXX don't know what to return else */
165}
166static int
167intsmb_attach(device_t dev)
168{
169        struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
170        sc->pci_sc=&intpm_pci[device_get_unit(dev)];
171        sc->isbusy=0;
172	sc->sh=sc->pci_sc->smbsh;
173	sc->st=sc->pci_sc->smbst;
174	sc->pci_sc->smbus=dev;
175        device_probe_and_attach(sc->smbus);
176#ifdef ENABLE_ALART
177	/*Enable Arart*/
178	bus_space_write_1(sc->st,sc->sh,PIIX4_SMBSLVCNT,
179			  PIIX4_SMBSLVCNT_ALTEN);
180#endif
181        return (0);
182}
183
184static void
185intsmb_print_child(device_t bus, device_t dev)
186{
187	printf(" on %s%d", device_get_name(bus), device_get_unit(bus));
188	return;
189}
190static int
191intsmb_callback(device_t dev, int index, caddr_t data)
192{
193	int error = 0;
194	intrmask_t s;
195	s=splnet();
196	switch (index) {
197	case SMB_REQUEST_BUS:
198		break;
199	case SMB_RELEASE_BUS:
200		break;
201	default:
202		error = EINVAL;
203	}
204	splx(s);
205	return (error);
206}
207/*counterpart of smbtx_smb_free*/
208static        int
209intsmb_free(device_t dev){
210        struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
211        if((bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTSTS)&
212	    PIIX4_SMBHSTSTAT_BUSY)
213#ifdef ENABLE_ALART
214	   ||(bus_space_read_1(sc->st,sc->sh,PIIX4_SMBSLVSTS)&
215	      PIIX4_SMBSLVSTS_BUSY)
216#endif
217	   || sc->isbusy)
218                return EBUSY;
219        sc->isbusy=1;
220	/*Disable Intrrupt in slave part*/
221#ifndef ENABLE_ALART
222	bus_space_write_1(sc->st,sc->sh,PIIX4_SMBSLVCNT,0);
223#endif
224        /*Reset INTR Flag to prepare INTR*/
225	bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTSTS,
226			  (PIIX4_SMBHSTSTAT_INTR|
227			   PIIX4_SMBHSTSTAT_ERR|
228			   PIIX4_SMBHSTSTAT_BUSC|
229			   PIIX4_SMBHSTSTAT_FAIL)
230		);
231        return 0;
232}
233
234static int
235intsmb_intr(device_t dev)
236{
237	struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
238	int status;
239	intrmask_t s;
240	status=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTSTS);
241	if(status&PIIX4_SMBHSTSTAT_BUSY){
242		return 1;
243
244	}
245	s=splhigh();
246	if(sc->isbusy&&(status&(PIIX4_SMBHSTSTAT_INTR|
247				PIIX4_SMBHSTSTAT_ERR|
248				PIIX4_SMBHSTSTAT_BUSC|
249				PIIX4_SMBHSTSTAT_FAIL))){
250		int tmp;
251		sc->isbusy=0;
252		tmp=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTCNT);
253		bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCNT,
254				  tmp&~PIIX4_SMBHSTCNT_INTREN);
255		splx(s);
256		wakeup(sc);
257		return 0;
258	}
259	splx(s);
260	return 1;/* Not Completed*/
261}
262static int
263intsmb_slvintr(device_t dev)
264{
265	struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
266        int status,retval;
267	retval=1;
268        status=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBSLVSTS);
269	if(status&PIIX4_SMBSLVSTS_BUSY)
270		return retval;
271	if(status&PIIX4_SMBSLVSTS_ALART){
272		intsmb_alrintr(dev);
273		retval=0;
274	}else if(status&~(PIIX4_SMBSLVSTS_ALART|PIIX4_SMBSLVSTS_SDW2
275			  |PIIX4_SMBSLVSTS_SDW1)){
276		retval=0;
277	}
278	/*Reset Status Register*/
279	bus_space_write_1(sc->st,sc->sh,PIIX4_SMBSLVSTS,PIIX4_SMBSLVSTS_ALART|
280			  PIIX4_SMBSLVSTS_SDW2|PIIX4_SMBSLVSTS_SDW1|
281			  PIIX4_SMBSLVSTS_SLV);
282	return retval;
283}
284
285static void intsmb_alrintr(device_t dev)
286{
287	struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
288	int slvcnt;
289#ifdef ENABLE_ALART
290	int error;
291#endif
292
293	/*stop generating INTR from ALART*/
294	slvcnt=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBSLVCNT);
295#ifdef ENABLE_ALART
296	bus_space_write_1(sc->st,sc->sh,PIIX4_SMBSLVCNT,
297			  slvcnt&~PIIX4_SMBSLVCNT_ALTEN) ;
298#endif
299	DELAY(5);
300	/*ask bus who assert it and then ask it what's the matter. */
301#ifdef ENABLE_ALART
302	error=intsmb_free(dev);
303	if(!error){
304                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,SMBALTRESP
305                                  |LSB);
306		intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_BYTE,1);
307		if(!(error=intsmb_stop_poll(dev))){
308			volatile u_int8_t *addr;
309			addr=bus_space_read_1(sc->st,sc->sh,
310					      PIIX4_SMBHSTDAT0);
311			printf("ALART_RESPONSE: %x\n",(int) addr);
312		}
313	}else{
314	        printf("ERROR\n");
315	}
316
317	/*Re-enable INTR from ALART*/
318	bus_space_write_1(sc->st,sc->sh,PIIX4_SMBSLVCNT,
319			  slvcnt|PIIX4_SMBSLVCNT_ALTEN) ;
320	DELAY(5);
321#endif
322
323	return;
324}
325static void
326intsmb_start(device_t dev,unsigned char cmd,int nointr)
327{
328	struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
329	unsigned char tmp;
330	tmp=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTCNT);
331	tmp&= 0xe0;
332	tmp |= cmd;
333	tmp |=PIIX4_SMBHSTCNT_START;
334	/*While not in autoconfiguration Intrrupt Enabled*/
335	if(!cold||!nointr)
336		tmp |=PIIX4_SMBHSTCNT_INTREN;
337	bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCNT,tmp);
338}
339
340/*Polling Code. Polling is not encouraged
341 * because It is required to wait for the device get busy.
342 *(29063505.pdf from Intel)
343 * But during boot,intrrupt cannot be used.
344 * so use polling code while in autoconfiguration.
345 */
346
347static        int
348intsmb_stop_poll(device_t dev){
349        int error,i;
350        struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
351	/*
352	 *  In smbtx driver ,Simply waiting.
353	 *  This loops 100-200 times.
354	 */
355	for(i=0;i<0x7fff;i++){
356                if((bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTSTS)
357		    &PIIX4_SMBHSTSTAT_BUSY)){
358                        break;
359                }
360	}
361	for(i=0;i<0x7fff;i++){
362		int status;
363		status=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTSTS);
364		if(!(status&PIIX4_SMBHSTSTAT_BUSY)){
365			sc->isbusy=0;
366			error=(status&PIIX4_SMBHSTSTAT_ERR)?EIO :
367				(status&PIIX4_SMBHSTSTAT_BUSC)?EBUSY:
368				(status&PIIX4_SMBHSTSTAT_FAIL)?EIO:0;
369			if(error==0&&!(status&PIIX4_SMBHSTSTAT_INTR)){
370				printf("unknown cause why?");
371			}
372			return error;
373		}
374	}
375	sc->isbusy=0;
376	return EIO;
377}
378/*
379 *wait for completion and return result.
380 */
381static        int
382intsmb_stop(device_t dev){
383        int error;
384        struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
385	if(cold){
386		/*So that it can use device during probing device on SMBus.*/
387		error=intsmb_stop_poll(dev);
388		return error;
389	}else{
390		if(!tsleep(sc,(PWAIT)|PCATCH,"SMBWAI",hz/8)){
391			int status;
392			status=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTSTS);
393			if(!(status&PIIX4_SMBHSTSTAT_BUSY)){
394				error=(status&PIIX4_SMBHSTSTAT_ERR)?EIO :
395					(status&PIIX4_SMBHSTSTAT_BUSC)?EBUSY:
396					(status&PIIX4_SMBHSTSTAT_FAIL)?EIO:0;
397				if(error==0&&!(status&PIIX4_SMBHSTSTAT_INTR)){
398					printf("intsmb%d:unknown cause why?\n",
399					       device_get_unit(dev));
400				}
401#ifdef ENABLE_ALART
402				bus_space_write_1(sc->st,sc->sh,
403						  PIIX4_SMBSLVCNT,PIIX4_SMBSLVCNT_ALTEN);
404#endif
405				return error;
406			}
407		}
408	}
409	/*Timeout Procedure*/
410	sc->isbusy=0;
411	/*Re-enable supressed intrrupt from slave part*/
412	bus_space_write_1(sc->st,sc->sh,
413			  PIIX4_SMBSLVCNT,PIIX4_SMBSLVCNT_ALTEN);
414        return EIO;
415}
416
417static int
418intsmb_quick(device_t dev, u_char slave, int how)
419{
420        int error=0;
421        u_char data;
422        struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
423        data=slave;
424	/*Quick command is part of Address, I think*/
425        switch(how){
426        case SMB_QWRITE:
427                data&=~LSB;
428		break;
429        case SMB_QREAD:
430                data|=LSB;
431                break;
432        default:
433                error=EINVAL;
434        }
435        if(!error){
436	        error=intsmb_free(dev);
437                if(!error){
438                        bus_space_write_1(sc->st,sc->sh,
439					  PIIX4_SMBHSTADD,data);
440			intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_QUICK,0);
441                        error=intsmb_stop(dev);
442                }
443        }
444
445        return (error);
446}
447
448static int
449intsmb_sendb(device_t dev, u_char slave, char byte)
450{
451        int error;
452        struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
453        error=intsmb_free(dev);
454        if(!error){
455                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave&~LSB);
456                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,byte);
457		intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_BYTE,0);
458                error=intsmb_stop(dev);
459        }
460        return (error);
461}
462static int
463intsmb_recvb(device_t dev, u_char slave, char *byte)
464{
465        int error;
466        struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
467        error=intsmb_free(dev);
468        if(!error){
469                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave
470				  |LSB);
471                intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_BYTE,0);
472                if(!(error=intsmb_stop(dev))){
473#ifdef RECV_IS_IN_CMD
474		        /*Linux SMBus stuff also troubles
475			  Because Intel's datasheet will not make clear.
476			 */
477                        *byte=bus_space_read_1(sc->st,sc->sh,
478					       PIIX4_SMBHSTCMD);
479#else
480                        *byte=bus_space_read_1(sc->st,sc->sh,
481					       PIIX4_SMBHSTDAT0);
482#endif
483                }
484        }
485        return (error);
486}
487static int
488intsmb_writeb(device_t dev, u_char slave, char cmd, char byte)
489{
490        int error;
491        struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
492        error=intsmb_free(dev);
493        if(!error){
494                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave&~LSB);
495                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,cmd);
496                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTDAT0,byte);
497		intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_BDATA,0);
498                error=intsmb_stop(dev);
499        }
500        return (error);
501}
502static int
503intsmb_writew(device_t dev, u_char slave, char cmd, short word)
504{
505        int error;
506        struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
507        error=intsmb_free(dev);
508        if(!error){
509                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave&~LSB);
510                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,cmd);
511                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTDAT0,
512				  word&0xff);
513                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTDAT1,
514				  (word>>8)&0xff);
515		intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_WDATA,0);
516                error=intsmb_stop(dev);
517        }
518        return (error);
519}
520
521static int
522intsmb_readb(device_t dev, u_char slave, char cmd, char *byte)
523{
524        int error;
525        struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
526        error=intsmb_free(dev);
527        if(!error){
528                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave|LSB);
529                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,cmd);
530		intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_BDATA,0);
531                if(!(error=intsmb_stop(dev))){
532		        *byte=bus_space_read_1(sc->st,sc->sh,
533					       PIIX4_SMBHSTDAT0);
534                }
535        }
536        return (error);
537}
538static int
539intsmb_readw(device_t dev, u_char slave, char cmd, short *word)
540{
541        int error;
542        struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
543        error=intsmb_free(dev);
544        if(!error){
545                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave|LSB);
546                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,cmd);
547		intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_WDATA,0);
548                if(!(error=intsmb_stop(dev))){
549                        *word=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTDAT0)&0xff;
550                        *word|=(bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTDAT1)&0xff)<<8;
551                }
552        }
553        return (error);
554}
555/*
556 * Data sheet claims that it implements all function, but also claims
557 * that it implements 7 function and not mention PCALL. So I don't know
558 * whether it will work.
559 */
560static int
561intsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata)
562{
563#ifdef PROCCALL_TEST
564        int error;
565        struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
566        error=intsmb_free(dev);
567        if(!error){
568                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave&~LSB);
569                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,cmd);
570                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTDAT0,sdata&0xff);
571                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTDAT1,(sdata&0xff)>>8);
572                intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_WDATA,0);
573        }
574        if(!(error=intsmb_stop(dev))){
575                *rdata=bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTDAT0)&0xff;
576                *rdata|=(bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTDAT1)&0xff)<<8;
577        }
578        return error;
579#else
580	return 0;
581#endif
582}
583static int
584intsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf)
585{
586        int error,i;
587        struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
588        error=intsmb_free(dev);
589        if(count>SMBBLOCKTRANS_MAX||count==0)
590                error=EINVAL;
591        if(!error){
592                /*Reset internal array index*/
593                bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTCNT);
594
595                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave&~LSB);
596                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,cmd);
597                for(i=0;i<count;i++){
598                        bus_space_write_1(sc->st,sc->sh,PIIX4_SMBBLKDAT,buf[i]);
599                }
600                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTDAT0,count);
601                intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_BLOCK,0);
602                error=intsmb_stop(dev);
603        }
604        return (error);
605}
606
607static int
608intsmb_bread(device_t dev, u_char slave, char cmd, u_char count, char *buf)
609{
610        int error,i;
611        struct intsmb_softc *sc = (struct intsmb_softc *)device_get_softc(dev);
612        error=intsmb_free(dev);
613        if(count>SMBBLOCKTRANS_MAX||count==0)
614                error=EINVAL;
615        if(!error){
616                /*Reset internal array index*/
617                bus_space_read_1(sc->st,sc->sh,PIIX4_SMBHSTCNT);
618
619                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTADD,slave|LSB);
620                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTCMD,cmd);
621                bus_space_write_1(sc->st,sc->sh,PIIX4_SMBHSTDAT0,count);
622                intsmb_start(dev,PIIX4_SMBHSTCNT_PROT_BLOCK,0);
623                error=intsmb_stop(dev);
624                if(!error){
625                        bzero(buf,count);/*Is it needed?*/
626                        count= bus_space_read_1(sc->st,sc->sh,
627						PIIX4_SMBHSTDAT0);
628                        if(count!=0&&count<=SMBBLOCKTRANS_MAX){
629			        for(i=0;i<count;i++){
630				        buf[i]=bus_space_read_1(sc->st,
631								sc->sh,
632								PIIX4_SMBBLKDAT);
633				}
634			}
635                        else{
636				error=EIO;
637                        }
638		}
639	}
640        return (error);
641}
642
643DRIVER_MODULE(intsmb, root , intpm_driver, intsmb_devclass, 0, 0);
644
645
646static void intpm_intr __P((void *arg));
647
648static const char*
649intpm_probe (pcici_t tag, pcidi_t type)
650{
651        struct _pcsid	*ep =pci_ids;
652        while (ep->type && ep->type != type)
653                ++ep;
654        return (ep->desc);
655}
656
657static struct intpm_pci_softc *intpm_alloc(int unit){
658        if(unit<NINTPM)
659                return &intpm_pci[unit];
660        else
661                return NULL;
662}
663
664/*Same as pci_map_int but this ignores INTPIN*/
665static int force_pci_map_int(pcici_t cfg, pci_inthand_t *func, void *arg, unsigned *maskptr)
666{
667        int error;
668#ifdef APIC_IO
669        int nextpin, muxcnt;
670#endif
671	/* Spec sheet claims that it use IRQ 9*/
672        int irq = 9;
673        void *dev_instance = (void *)-1; /* XXX use cfg->devdata        */
674        void *idesc;
675
676        idesc = intr_create(dev_instance, irq, func, arg, maskptr, 0);
677        error = intr_connect(idesc);
678        if (error != 0)
679                return 0;
680#ifdef APIC_IO
681        nextpin = next_apic_irq(irq);
682
683        if (nextpin < 0)
684                return 1;
685
686        /*
687         * Attempt handling of some broken mp tables.
688         *
689         * It's OK to yell (since the mp tables are broken).
690         *
691         * Hanging in the boot is not OK
692         */
693
694        muxcnt = 2;
695        nextpin = next_apic_irq(nextpin);
696        while (muxcnt < 5 && nextpin >= 0) {
697                muxcnt++;
698                nextpin = next_apic_irq(nextpin);
699        }
700        if (muxcnt >= 5) {
701                printf("bogus MP table, more than 4 IO APIC pins connected to the same PCI device or ISA/EISA interrupt\n");
702                return 0;
703        }
704
705        printf("bogus MP table, %d IO APIC pins connected to the same PCI device or ISA/EISA interrupt\n", muxcnt);
706
707        nextpin = next_apic_irq(irq);
708        while (nextpin >= 0) {
709                idesc = intr_create(dev_instance, nextpin, func, arg,
710				    maskptr, 0);
711                error = intr_connect(idesc);
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