]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - sys/pci/intpm.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.git] / sys / pci / intpm.c
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
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/bus.h>
33 #include <sys/kernel.h>
34 #include <sys/lock.h>
35 #include <sys/module.h>
36 #include <sys/mutex.h>
37 #include <sys/rman.h>
38 #include <machine/bus.h>
39 #include <dev/smbus/smbconf.h>
40
41 #include "smbus_if.h"
42
43 #include <dev/pci/pcireg.h>
44 #include <dev/pci/pcivar.h>
45 #include <pci/intpmreg.h>
46
47 #include "opt_intpm.h"
48
49 struct intsmb_softc {
50         device_t                dev;
51         struct resource         *io_res;
52         struct resource         *irq_res;
53         void                    *irq_hand;
54         device_t                smbus;
55         int                     isbusy;
56         int                     cfg_irq9;
57         int                     poll;
58         struct mtx              lock;
59 };
60
61 #define INTSMB_LOCK(sc)         mtx_lock(&(sc)->lock)
62 #define INTSMB_UNLOCK(sc)       mtx_unlock(&(sc)->lock)
63 #define INTSMB_LOCK_ASSERT(sc)  mtx_assert(&(sc)->lock, MA_OWNED)
64
65 static int intsmb_probe(device_t);
66 static int intsmb_attach(device_t);
67 static int intsmb_detach(device_t);
68 static int intsmb_intr(struct intsmb_softc *sc);
69 static int intsmb_slvintr(struct intsmb_softc *sc);
70 static void intsmb_alrintr(struct intsmb_softc *sc);
71 static int intsmb_callback(device_t dev, int index, void *data);
72 static int intsmb_quick(device_t dev, u_char slave, int how);
73 static int intsmb_sendb(device_t dev, u_char slave, char byte);
74 static int intsmb_recvb(device_t dev, u_char slave, char *byte);
75 static int intsmb_writeb(device_t dev, u_char slave, char cmd, char byte);
76 static int intsmb_writew(device_t dev, u_char slave, char cmd, short word);
77 static int intsmb_readb(device_t dev, u_char slave, char cmd, char *byte);
78 static int intsmb_readw(device_t dev, u_char slave, char cmd, short *word);
79 static int intsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata);
80 static int intsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf);
81 static int intsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf);
82 static void intsmb_start(struct intsmb_softc *sc, u_char cmd, int nointr);
83 static int intsmb_stop(struct intsmb_softc *sc);
84 static int intsmb_stop_poll(struct intsmb_softc *sc);
85 static int intsmb_free(struct intsmb_softc *sc);
86 static void intsmb_rawintr(void *arg);
87
88 static int
89 intsmb_probe(device_t dev)
90 {
91
92         switch (pci_get_devid(dev)) {
93         case 0x71138086:        /* Intel 82371AB */
94         case 0x719b8086:        /* Intel 82443MX */
95 #if 0
96         /* Not a good idea yet, this stops isab0 functioning */
97         case 0x02001166:        /* ServerWorks OSB4 */
98 #endif
99                 device_set_desc(dev, "Intel PIIX4 SMBUS Interface");
100                 break;
101         case 0x43851002:
102                 device_set_desc(dev, "AMD SB600/700/710/750 SMBus Controller");
103                 /* XXX Maybe force polling right here? */
104                 break;
105         default:
106                 return (ENXIO);
107         }
108
109         return (BUS_PROBE_DEFAULT);
110 }
111
112 static int
113 intsmb_attach(device_t dev)
114 {
115         struct intsmb_softc *sc = device_get_softc(dev);
116         int error, rid, value;
117         int intr;
118         char *str;
119
120         sc->dev = dev;
121
122         mtx_init(&sc->lock, device_get_nameunit(dev), "intsmb", MTX_DEF);
123
124         sc->cfg_irq9 = 0;
125 #ifndef NO_CHANGE_PCICONF
126         switch (pci_get_devid(dev)) {
127         case 0x71138086:        /* Intel 82371AB */
128         case 0x719b8086:        /* Intel 82443MX */
129                 /* Changing configuration is allowed. */
130                 sc->cfg_irq9 = 1;
131                 break;
132         }
133 #endif
134
135         rid = PCI_BASE_ADDR_SMB;
136         sc->io_res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid,
137             RF_ACTIVE);
138         if (sc->io_res == NULL) {
139                 device_printf(dev, "Could not allocate I/O space\n");
140                 error = ENXIO;
141                 goto fail;
142         }
143
144         if (sc->cfg_irq9) {
145                 pci_write_config(dev, PCIR_INTLINE, 0x9, 1);
146                 pci_write_config(dev, PCI_HST_CFG_SMB,
147                     PCI_INTR_SMB_IRQ9 | PCI_INTR_SMB_ENABLE, 1);
148         }
149         value = pci_read_config(dev, PCI_HST_CFG_SMB, 1);
150         sc->poll = (value & PCI_INTR_SMB_ENABLE) == 0;
151         intr = value & PCI_INTR_SMB_MASK;
152         switch (intr) {
153         case PCI_INTR_SMB_SMI:
154                 str = "SMI";
155                 break;
156         case PCI_INTR_SMB_IRQ9:
157                 str = "IRQ 9";
158                 break;
159         case PCI_INTR_SMB_IRQ_PCI:
160                 str = "PCI IRQ";
161                 break;
162         default:
163                 str = "BOGUS";
164         }
165
166         device_printf(dev, "intr %s %s ", str,
167             sc->poll == 0 ? "enabled" : "disabled");
168         printf("revision %d\n", pci_read_config(dev, PCI_REVID_SMB, 1));
169
170         if (!sc->poll && intr == PCI_INTR_SMB_SMI) {
171                 device_printf(dev,
172                     "using polling mode when configured interrupt is SMI\n");
173                 sc->poll = 1;
174         }
175
176         if (sc->poll)
177             goto no_intr;
178
179         if (intr != PCI_INTR_SMB_IRQ9 && intr != PCI_INTR_SMB_IRQ_PCI) {
180                 device_printf(dev, "Unsupported interrupt mode\n");
181                 error = ENXIO;
182                 goto fail;
183         }
184
185         /* Force IRQ 9. */
186         rid = 0;
187         if (sc->cfg_irq9)
188                 bus_set_resource(dev, SYS_RES_IRQ, rid, 9, 1);
189
190         sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
191             RF_SHAREABLE | RF_ACTIVE);
192         if (sc->irq_res == NULL) {
193                 device_printf(dev, "Could not allocate irq\n");
194                 error = ENXIO;
195                 goto fail;
196         }
197
198         error = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
199             NULL, intsmb_rawintr, sc, &sc->irq_hand);
200         if (error) {
201                 device_printf(dev, "Failed to map intr\n");
202                 goto fail;
203         }
204
205 no_intr:
206         sc->isbusy = 0;
207         sc->smbus = device_add_child(dev, "smbus", -1);
208         if (sc->smbus == NULL) {
209                 error = ENXIO;
210                 goto fail;
211         }
212         error = device_probe_and_attach(sc->smbus);
213         if (error)
214                 goto fail;
215
216 #ifdef ENABLE_ALART
217         /* Enable Arart */
218         bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, PIIX4_SMBSLVCNT_ALTEN);
219 #endif
220         return (0);
221
222 fail:
223         intsmb_detach(dev);
224         return (error);
225 }
226
227 static int
228 intsmb_detach(device_t dev)
229 {
230         struct intsmb_softc *sc = device_get_softc(dev);
231         int error;
232
233         error = bus_generic_detach(dev);
234         if (error)
235                 return (error);
236
237         if (sc->smbus)
238                 device_delete_child(dev, sc->smbus);
239         if (sc->irq_hand)
240                 bus_teardown_intr(dev, sc->irq_res, sc->irq_hand);
241         if (sc->irq_res)
242                 bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
243         if (sc->io_res)
244                 bus_release_resource(dev, SYS_RES_IOPORT, PCI_BASE_ADDR_SMB,
245                     sc->io_res);
246         mtx_destroy(&sc->lock);
247         return (0);
248 }
249
250 static void
251 intsmb_rawintr(void *arg)
252 {
253         struct intsmb_softc *sc = arg;
254
255         INTSMB_LOCK(sc);
256         intsmb_intr(sc);
257         intsmb_slvintr(sc);
258         INTSMB_UNLOCK(sc);
259 }
260
261 static int
262 intsmb_callback(device_t dev, int index, void *data)
263 {
264         int error = 0;
265
266         switch (index) {
267         case SMB_REQUEST_BUS:
268                 break;
269         case SMB_RELEASE_BUS:
270                 break;
271         default:
272                 error = EINVAL;
273         }
274
275         return (error);
276 }
277
278 /* Counterpart of smbtx_smb_free(). */
279 static int
280 intsmb_free(struct intsmb_softc *sc)
281 {
282
283         INTSMB_LOCK_ASSERT(sc);
284         if ((bus_read_1(sc->io_res, PIIX4_SMBHSTSTS) & PIIX4_SMBHSTSTAT_BUSY) ||
285 #ifdef ENABLE_ALART
286             (bus_read_1(sc->io_res, PIIX4_SMBSLVSTS) & PIIX4_SMBSLVSTS_BUSY) ||
287 #endif
288             sc->isbusy)
289                 return (SMB_EBUSY);
290
291         sc->isbusy = 1;
292         /* Disable Interrupt in slave part. */
293 #ifndef ENABLE_ALART
294         bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, 0);
295 #endif
296         /* Reset INTR Flag to prepare INTR. */
297         bus_write_1(sc->io_res, PIIX4_SMBHSTSTS,
298             PIIX4_SMBHSTSTAT_INTR | PIIX4_SMBHSTSTAT_ERR |
299             PIIX4_SMBHSTSTAT_BUSC | PIIX4_SMBHSTSTAT_FAIL);
300         return (0);
301 }
302
303 static int
304 intsmb_intr(struct intsmb_softc *sc)
305 {
306         int status, tmp;
307
308         status = bus_read_1(sc->io_res, PIIX4_SMBHSTSTS);
309         if (status & PIIX4_SMBHSTSTAT_BUSY)
310                 return (1);
311
312         if (status & (PIIX4_SMBHSTSTAT_INTR | PIIX4_SMBHSTSTAT_ERR |
313             PIIX4_SMBHSTSTAT_BUSC | PIIX4_SMBHSTSTAT_FAIL)) {
314
315                 tmp = bus_read_1(sc->io_res, PIIX4_SMBHSTCNT);
316                 bus_write_1(sc->io_res, PIIX4_SMBHSTCNT,
317                     tmp & ~PIIX4_SMBHSTCNT_INTREN);
318                 if (sc->isbusy) {
319                         sc->isbusy = 0;
320                         wakeup(sc);
321                 }
322                 return (0);
323         }
324         return (1); /* Not Completed */
325 }
326
327 static int
328 intsmb_slvintr(struct intsmb_softc *sc)
329 {
330         int status;
331
332         status = bus_read_1(sc->io_res, PIIX4_SMBSLVSTS);
333         if (status & PIIX4_SMBSLVSTS_BUSY)
334                 return (1);
335         if (status & PIIX4_SMBSLVSTS_ALART)
336                 intsmb_alrintr(sc);
337         else if (status & ~(PIIX4_SMBSLVSTS_ALART | PIIX4_SMBSLVSTS_SDW2
338                 | PIIX4_SMBSLVSTS_SDW1)) {
339         }
340
341         /* Reset Status Register */
342         bus_write_1(sc->io_res, PIIX4_SMBSLVSTS,
343             PIIX4_SMBSLVSTS_ALART | PIIX4_SMBSLVSTS_SDW2 |
344             PIIX4_SMBSLVSTS_SDW1 | PIIX4_SMBSLVSTS_SLV);
345         return (0);
346 }
347
348 static void
349 intsmb_alrintr(struct intsmb_softc *sc)
350 {
351         int slvcnt;
352 #ifdef ENABLE_ALART
353         int error;
354         uint8_t addr;
355 #endif
356
357         /* Stop generating INTR from ALART. */
358         slvcnt = bus_read_1(sc->io_res, PIIX4_SMBSLVCNT);
359 #ifdef ENABLE_ALART
360         bus_write_1(sc->io_res, PIIX4_SMBSLVCNT,
361             slvcnt & ~PIIX4_SMBSLVCNT_ALTEN);
362 #endif
363         DELAY(5);
364
365         /* Ask bus who asserted it and then ask it what's the matter. */
366 #ifdef ENABLE_ALART
367         error = intsmb_free(sc);
368         if (error)
369                 return;
370
371         bus_write_1(sc->io_res, PIIX4_SMBHSTADD, SMBALTRESP | LSB);
372         intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BYTE, 1);
373         error = intsmb_stop_poll(sc);
374         if (error)
375                 device_printf(sc->dev, "ALART: ERROR\n");
376         else {
377                 addr = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0);
378                 device_printf(sc->dev, "ALART_RESPONSE: 0x%x\n", addr);
379         }
380
381         /* Re-enable INTR from ALART. */
382         bus_write_1(sc->io_res, PIIX4_SMBSLVCNT,
383             slvcnt | PIIX4_SMBSLVCNT_ALTEN);
384         DELAY(5);
385 #endif
386 }
387
388 static void
389 intsmb_start(struct intsmb_softc *sc, unsigned char cmd, int nointr)
390 {
391         unsigned char tmp;
392
393         INTSMB_LOCK_ASSERT(sc);
394         tmp = bus_read_1(sc->io_res, PIIX4_SMBHSTCNT);
395         tmp &= 0xe0;
396         tmp |= cmd;
397         tmp |= PIIX4_SMBHSTCNT_START;
398
399         /* While not in autoconfiguration enable interrupts. */
400         if (!sc->poll && !cold && !nointr)
401                 tmp |= PIIX4_SMBHSTCNT_INTREN;
402         bus_write_1(sc->io_res, PIIX4_SMBHSTCNT, tmp);
403 }
404
405 static int
406 intsmb_error(device_t dev, int status)
407 {
408         int error = 0;
409
410         if (status & PIIX4_SMBHSTSTAT_ERR)
411                 error |= SMB_EBUSERR;
412         if (status & PIIX4_SMBHSTSTAT_BUSC)
413                 error |= SMB_ECOLLI;
414         if (status & PIIX4_SMBHSTSTAT_FAIL)
415                 error |= SMB_ENOACK;
416
417         if (error != 0 && bootverbose)
418                 device_printf(dev, "error = %d, status = %#x\n", error, status);
419
420         return (error);
421 }
422
423 /*
424  * Polling Code.
425  *
426  * Polling is not encouraged because it requires waiting for the
427  * device if it is busy.
428  * (29063505.pdf from Intel) But during boot, interrupt cannot be used, so use
429  * polling code then.
430  */
431 static int
432 intsmb_stop_poll(struct intsmb_softc *sc)
433 {
434         int error, i, status, tmp;
435
436         INTSMB_LOCK_ASSERT(sc);
437
438         /* First, wait for busy to be set. */
439         for (i = 0; i < 0x7fff; i++)
440                 if (bus_read_1(sc->io_res, PIIX4_SMBHSTSTS) &
441                     PIIX4_SMBHSTSTAT_BUSY)
442                         break;
443
444         /* Wait for busy to clear. */
445         for (i = 0; i < 0x7fff; i++) {
446                 status = bus_read_1(sc->io_res, PIIX4_SMBHSTSTS);
447                 if (!(status & PIIX4_SMBHSTSTAT_BUSY)) {
448                         sc->isbusy = 0;
449                         error = intsmb_error(sc->dev, status);
450                         return (error);
451                 }
452         }
453
454         /* Timed out waiting for busy to clear. */
455         sc->isbusy = 0;
456         tmp = bus_read_1(sc->io_res, PIIX4_SMBHSTCNT);
457         bus_write_1(sc->io_res, PIIX4_SMBHSTCNT, tmp & ~PIIX4_SMBHSTCNT_INTREN);
458         return (SMB_ETIMEOUT);
459 }
460
461 /*
462  * Wait for completion and return result.
463  */
464 static int
465 intsmb_stop(struct intsmb_softc *sc)
466 {
467         int error, status;
468
469         INTSMB_LOCK_ASSERT(sc);
470
471         if (sc->poll || cold)
472                 /* So that it can use device during device probe on SMBus. */
473                 return (intsmb_stop_poll(sc));
474
475         error = msleep(sc, &sc->lock, PWAIT | PCATCH, "SMBWAI", hz / 8);
476         if (error == 0) {
477                 status = bus_read_1(sc->io_res, PIIX4_SMBHSTSTS);
478                 if (!(status & PIIX4_SMBHSTSTAT_BUSY)) {
479                         error = intsmb_error(sc->dev, status);
480                         if (error == 0 && !(status & PIIX4_SMBHSTSTAT_INTR))
481                                 device_printf(sc->dev, "unknown cause why?\n");
482 #ifdef ENABLE_ALART
483                         bus_write_1(sc->io_res, PIIX4_SMBSLVCNT,
484                             PIIX4_SMBSLVCNT_ALTEN);
485 #endif
486                         return (error);
487                 }
488         }
489
490         /* Timeout Procedure. */
491         sc->isbusy = 0;
492
493         /* Re-enable supressed interrupt from slave part. */
494         bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, PIIX4_SMBSLVCNT_ALTEN);
495         if (error == EWOULDBLOCK)
496                 return (SMB_ETIMEOUT);
497         else
498                 return (SMB_EABORT);
499 }
500
501 static int
502 intsmb_quick(device_t dev, u_char slave, int how)
503 {
504         struct intsmb_softc *sc = device_get_softc(dev);
505         int error;
506         u_char data;
507
508         data = slave;
509
510         /* Quick command is part of Address, I think. */
511         switch(how) {
512         case SMB_QWRITE:
513                 data &= ~LSB;
514                 break;
515         case SMB_QREAD:
516                 data |= LSB;
517                 break;
518         default:
519                 return (EINVAL);
520         }
521
522         INTSMB_LOCK(sc);
523         error = intsmb_free(sc);
524         if (error) {
525                 INTSMB_UNLOCK(sc);
526                 return (error);
527         }
528         bus_write_1(sc->io_res, PIIX4_SMBHSTADD, data);
529         intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_QUICK, 0);
530         error = intsmb_stop(sc);
531         INTSMB_UNLOCK(sc);
532         return (error);
533 }
534
535 static int
536 intsmb_sendb(device_t dev, u_char slave, char byte)
537 {
538         struct intsmb_softc *sc = device_get_softc(dev);
539         int error;
540
541         INTSMB_LOCK(sc);
542         error = intsmb_free(sc);
543         if (error) {
544                 INTSMB_UNLOCK(sc);
545                 return (error);
546         }
547         bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB);
548         bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, byte);
549         intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BYTE, 0);
550         error = intsmb_stop(sc);
551         INTSMB_UNLOCK(sc);
552         return (error);
553 }
554
555 static int
556 intsmb_recvb(device_t dev, u_char slave, char *byte)
557 {
558         struct intsmb_softc *sc = device_get_softc(dev);
559         int error;
560
561         INTSMB_LOCK(sc);
562         error = intsmb_free(sc);
563         if (error) {
564                 INTSMB_UNLOCK(sc);
565                 return (error);
566         }
567         bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave | LSB);
568         intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BYTE, 0);
569         error = intsmb_stop(sc);
570         if (error == 0) {
571 #ifdef RECV_IS_IN_CMD
572                 /*
573                  * Linux SMBus stuff also troubles
574                  * Because Intel's datasheet does not make clear.
575                  */
576                 *byte = bus_read_1(sc->io_res, PIIX4_SMBHSTCMD);
577 #else
578                 *byte = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0);
579 #endif
580         }
581         INTSMB_UNLOCK(sc);
582         return (error);
583 }
584
585 static int
586 intsmb_writeb(device_t dev, u_char slave, char cmd, char byte)
587 {
588         struct intsmb_softc *sc = device_get_softc(dev);
589         int error;
590
591         INTSMB_LOCK(sc);
592         error = intsmb_free(sc);
593         if (error) {
594                 INTSMB_UNLOCK(sc);
595                 return (error);
596         }
597         bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB);
598         bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd);
599         bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, byte);
600         intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BDATA, 0);
601         error = intsmb_stop(sc);
602         INTSMB_UNLOCK(sc);
603         return (error);
604 }
605
606 static int
607 intsmb_writew(device_t dev, u_char slave, char cmd, short word)
608 {
609         struct intsmb_softc *sc = device_get_softc(dev);
610         int error;
611
612         INTSMB_LOCK(sc);
613         error = intsmb_free(sc);
614         if (error) {
615                 INTSMB_UNLOCK(sc);
616                 return (error);
617         }
618         bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB);
619         bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd);
620         bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, word & 0xff);
621         bus_write_1(sc->io_res, PIIX4_SMBHSTDAT1, (word >> 8) & 0xff);
622         intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_WDATA, 0);
623         error = intsmb_stop(sc);
624         INTSMB_UNLOCK(sc);
625         return (error);
626 }
627
628 static int
629 intsmb_readb(device_t dev, u_char slave, char cmd, char *byte)
630 {
631         struct intsmb_softc *sc = device_get_softc(dev);
632         int error;
633
634         INTSMB_LOCK(sc);
635         error = intsmb_free(sc);
636         if (error) {
637                 INTSMB_UNLOCK(sc);
638                 return (error);
639         }
640         bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave | LSB);
641         bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd);
642         intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BDATA, 0);
643         error = intsmb_stop(sc);
644         if (error == 0)
645                 *byte = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0);
646         INTSMB_UNLOCK(sc);
647         return (error);
648 }
649
650 static int
651 intsmb_readw(device_t dev, u_char slave, char cmd, short *word)
652 {
653         struct intsmb_softc *sc = device_get_softc(dev);
654         int error;
655
656         INTSMB_LOCK(sc);
657         error = intsmb_free(sc);
658         if (error) {
659                 INTSMB_UNLOCK(sc);
660                 return (error);
661         }
662         bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave | LSB);
663         bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd);
664         intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_WDATA, 0);
665         error = intsmb_stop(sc);
666         if (error == 0) {
667                 *word = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0);
668                 *word |= bus_read_1(sc->io_res, PIIX4_SMBHSTDAT1) << 8;
669         }
670         INTSMB_UNLOCK(sc);
671         return (error);
672 }
673
674 /*
675  * Data sheet claims that it implements all function, but also claims
676  * that it implements 7 function and not mention PCALL. So I don't know
677  * whether it will work.
678  */
679 static int
680 intsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata)
681 {
682 #ifdef PROCCALL_TEST
683         struct intsmb_softc *sc = device_get_softc(dev);
684         int error;
685
686         INTSMB_LOCK(sc);
687         error = intsmb_free(sc);
688         if (error) {
689                 INTSMB_UNLOCK(sc);
690                 return (error);
691         }
692         bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB);
693         bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd);
694         bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, sdata & 0xff);
695         bus_write_1(sc->io_res, PIIX4_SMBHSTDAT1, (sdata & 0xff) >> 8);
696         intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_WDATA, 0);
697         error = intsmb_stop(sc);
698         if (error == 0) {
699                 *rdata = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0);
700                 *rdata |= bus_read_1(sc->io_res, PIIX4_SMBHSTDAT1) << 8;
701         }
702         INTSMB_UNLOCK(sc);
703         return (error);
704 #else
705         return (SMB_ENOTSUPP);
706 #endif
707 }
708
709 static int
710 intsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf)
711 {
712         struct intsmb_softc *sc = device_get_softc(dev);
713         int error, i;
714
715         if (count > SMBBLOCKTRANS_MAX || count == 0)
716                 return (SMB_EINVAL);
717
718         INTSMB_LOCK(sc);
719         error = intsmb_free(sc);
720         if (error) {
721                 INTSMB_UNLOCK(sc);
722                 return (error);
723         }
724
725         /* Reset internal array index. */
726         bus_read_1(sc->io_res, PIIX4_SMBHSTCNT);
727
728         bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB);
729         bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd);
730         for (i = 0; i < count; i++)
731                 bus_write_1(sc->io_res, PIIX4_SMBBLKDAT, buf[i]);
732         bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, count);
733         intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BLOCK, 0);
734         error = intsmb_stop(sc);
735         INTSMB_UNLOCK(sc);
736         return (error);
737 }
738
739 static int
740 intsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf)
741 {
742         struct intsmb_softc *sc = device_get_softc(dev);
743         int error, i;
744         u_char data, nread;
745
746         if (*count > SMBBLOCKTRANS_MAX || *count == 0)
747                 return (SMB_EINVAL);
748
749         INTSMB_LOCK(sc);
750         error = intsmb_free(sc);
751         if (error) {
752                 INTSMB_UNLOCK(sc);
753                 return (error);
754         }
755
756         /* Reset internal array index. */
757         bus_read_1(sc->io_res, PIIX4_SMBHSTCNT);
758
759         bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave | LSB);
760         bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd);
761         bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, *count);
762         intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BLOCK, 0);
763         error = intsmb_stop(sc);
764         if (error == 0) {
765                 nread = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0);
766                 if (nread != 0 && nread <= SMBBLOCKTRANS_MAX) {
767                         for (i = 0; i < nread; i++) {
768                                 data = bus_read_1(sc->io_res, PIIX4_SMBBLKDAT);
769                                 if (i < *count)
770                                         buf[i] = data;
771                         }
772                         *count = nread;
773                 } else
774                         error = EIO;
775         }
776         INTSMB_UNLOCK(sc);
777         return (error);
778 }
779
780 static devclass_t intsmb_devclass;
781
782 static device_method_t intsmb_methods[] = {
783         /* Device interface */
784         DEVMETHOD(device_probe,         intsmb_probe),
785         DEVMETHOD(device_attach,        intsmb_attach),
786         DEVMETHOD(device_detach,        intsmb_detach),
787
788         /* Bus interface */
789         DEVMETHOD(bus_print_child,      bus_generic_print_child),
790
791         /* SMBus interface */
792         DEVMETHOD(smbus_callback,       intsmb_callback),
793         DEVMETHOD(smbus_quick,          intsmb_quick),
794         DEVMETHOD(smbus_sendb,          intsmb_sendb),
795         DEVMETHOD(smbus_recvb,          intsmb_recvb),
796         DEVMETHOD(smbus_writeb,         intsmb_writeb),
797         DEVMETHOD(smbus_writew,         intsmb_writew),
798         DEVMETHOD(smbus_readb,          intsmb_readb),
799         DEVMETHOD(smbus_readw,          intsmb_readw),
800         DEVMETHOD(smbus_pcall,          intsmb_pcall),
801         DEVMETHOD(smbus_bwrite,         intsmb_bwrite),
802         DEVMETHOD(smbus_bread,          intsmb_bread),
803
804         { 0, 0 }
805 };
806
807 static driver_t intsmb_driver = {
808         "intsmb",
809         intsmb_methods,
810         sizeof(struct intsmb_softc),
811 };
812
813 DRIVER_MODULE(intsmb, pci, intsmb_driver, intsmb_devclass, 0, 0);
814 DRIVER_MODULE(smbus, intsmb, smbus_driver, smbus_devclass, 0, 0);
815 MODULE_DEPEND(intsmb, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER);
816 MODULE_VERSION(intsmb, 1);