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