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