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