intpm.c revision 162289
1107120Sjulian/*-
2107120Sjulian * Copyright (c) 1998, 1999 Takanori Watanabe
3139823Simp * All rights reserved.
4139823Simp *
5139823Simp * Redistribution and use in source and binary forms, with or without
6107120Sjulian * modification, are permitted provided that the following conditions
7107120Sjulian * are met:
8107120Sjulian * 1. Redistributions of source code must retain the above copyright
9107120Sjulian *        notice, this list of conditions and the following disclaimer.
10107120Sjulian * 2. Redistributions in binary form must reproduce the above copyright
11107120Sjulian *        notice, this list of conditions and the following disclaimer in the
12107120Sjulian *        documentation and/or other materials provided with the distribution.
13107120Sjulian *
14107120Sjulian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15107120Sjulian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16107120Sjulian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17107120Sjulian * ARE DISCLAIMED.    IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18107120Sjulian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19107120Sjulian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20107120Sjulian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21107120Sjulian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22107120Sjulian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23107120Sjulian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24107120Sjulian * SUCH DAMAGE.
25107120Sjulian */
26107120Sjulian
27107120Sjulian#include <sys/cdefs.h>
28107120Sjulian__FBSDID("$FreeBSD: head/sys/pci/intpm.c 162289 2006-09-13 18:56:39Z jhb $");
29107120Sjulian
30121054Semax#include <sys/param.h>
31107120Sjulian#include <sys/systm.h>
32107120Sjulian#include <sys/kernel.h>
33107120Sjulian#include <machine/bus.h>
34107120Sjulian
35107120Sjulian#include <sys/uio.h>
36107120Sjulian#include <sys/module.h>
37107120Sjulian#include <sys/bus.h>
38107120Sjulian#include <sys/rman.h>
39107120Sjulian#include <machine/resource.h>
40107120Sjulian#include <dev/smbus/smbconf.h>
41107120Sjulian
42107120Sjulian#include "smbus_if.h"
43128688Semax
44128688Semax/*This should be removed if force_pci_map_int supported*/
45128688Semax#include <sys/interrupt.h>
46128688Semax
47128688Semax#include <dev/pci/pcireg.h>
48128688Semax#include <dev/pci/pcivar.h>
49128688Semax#include <pci/intpmreg.h>
50128688Semax
51128688Semax#include "opt_intpm.h"
52107120Sjulian
53107120Sjulianstatic struct _pcsid
54107120Sjulian{
55107120Sjulian	u_int32_t type;
56107120Sjulian	char	*desc;
57107120Sjulian} pci_ids[] = {
58107120Sjulian	{ 0x71138086, "Intel 82371AB Power management controller" },
59107120Sjulian	{ 0x719b8086, "Intel 82443MX Power management controller" },
60107120Sjulian#if 0
61107120Sjulian	/* Not a good idea yet, this stops isab0 functioning */
62107120Sjulian	{ 0x02001166, "ServerWorks OSB4 PCI to ISA Bridge" },
63107120Sjulian#endif
64107120Sjulian
65107120Sjulian	{ 0x00000000, NULL }
66107120Sjulian};
67107120Sjulian
68107120Sjulianstatic int intsmb_probe(device_t);
69107120Sjulianstatic int intsmb_attach(device_t);
70107120Sjulianstatic int intsmb_intr(device_t dev);
71107120Sjulianstatic int intsmb_slvintr(device_t dev);
72107120Sjulianstatic void  intsmb_alrintr(device_t dev);
73107120Sjulianstatic int intsmb_callback(device_t dev, int index, void *data);
74107120Sjulianstatic int intsmb_quick(device_t dev, u_char slave, int how);
75107120Sjulianstatic int intsmb_sendb(device_t dev, u_char slave, char byte);
76107120Sjulianstatic int intsmb_recvb(device_t dev, u_char slave, char *byte);
77107120Sjulianstatic int intsmb_writeb(device_t dev, u_char slave, char cmd, char byte);
78107120Sjulianstatic int intsmb_writew(device_t dev, u_char slave, char cmd, short word);
79107120Sjulianstatic int intsmb_readb(device_t dev, u_char slave, char cmd, char *byte);
80107120Sjulianstatic int intsmb_readw(device_t dev, u_char slave, char cmd, short *word);
81107120Sjulianstatic int intsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata);
82107120Sjulianstatic int intsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf);
83107120Sjulianstatic int intsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf);
84107120Sjulianstatic void intsmb_start(device_t dev, u_char cmd, int nointr);
85107120Sjulianstatic int intsmb_stop(device_t dev);
86107120Sjulianstatic int intsmb_stop_poll(device_t dev);
87107120Sjulianstatic int intsmb_free(device_t dev);
88107120Sjulianstatic int intpm_probe (device_t dev);
89107120Sjulianstatic int intpm_attach (device_t dev);
90107120Sjulianstatic void intpm_intr(void *arg);
91107120Sjulian
92107120Sjulianstatic devclass_t intsmb_devclass;
93107120Sjulian
94107120Sjulianstatic device_method_t intpm_methods[] = {
95107120Sjulian	/* Device interface */
96107120Sjulian	DEVMETHOD(device_probe,		intsmb_probe),
97107120Sjulian	DEVMETHOD(device_attach,	intsmb_attach),
98107120Sjulian
99107120Sjulian	/* Bus interface */
100107120Sjulian	DEVMETHOD(bus_print_child,	bus_generic_print_child),
101107120Sjulian
102107120Sjulian	/* SMBus interface */
103107120Sjulian	DEVMETHOD(smbus_callback,	intsmb_callback),
104107120Sjulian	DEVMETHOD(smbus_quick,		intsmb_quick),
105107120Sjulian	DEVMETHOD(smbus_sendb,		intsmb_sendb),
106107120Sjulian	DEVMETHOD(smbus_recvb,		intsmb_recvb),
107107120Sjulian	DEVMETHOD(smbus_writeb,		intsmb_writeb),
108107120Sjulian	DEVMETHOD(smbus_writew,		intsmb_writew),
109107120Sjulian	DEVMETHOD(smbus_readb,		intsmb_readb),
110107120Sjulian	DEVMETHOD(smbus_readw,		intsmb_readw),
111107120Sjulian	DEVMETHOD(smbus_pcall,		intsmb_pcall),
112107120Sjulian	DEVMETHOD(smbus_bwrite,		intsmb_bwrite),
113107120Sjulian	DEVMETHOD(smbus_bread,		intsmb_bread),
114128076Semax
115107120Sjulian	{ 0, 0 }
116107120Sjulian};
117107120Sjulian
118107120Sjulianstruct intpm_pci_softc {
119107120Sjulian	bus_space_tag_t		smbst;
120107120Sjulian	bus_space_handle_t	smbsh;
121107120Sjulian	bus_space_tag_t		pmst;
122107120Sjulian	bus_space_handle_t	pmsh;
123107120Sjulian	device_t		smbus;
124107120Sjulian};
125107120Sjulian
126107120Sjulian
127107120Sjulianstruct intsmb_softc {
128107120Sjulian	struct intpm_pci_softc	*pci_sc;
129107120Sjulian	bus_space_tag_t		st;
130107120Sjulian	bus_space_handle_t	sh;
131107120Sjulian	device_t		smbus;
132107120Sjulian	int			isbusy;
133107120Sjulian};
134107120Sjulian
135107120Sjulianstatic driver_t intpm_driver = {
136107120Sjulian	"intsmb",
137107120Sjulian	intpm_methods,
138107120Sjulian	sizeof(struct intsmb_softc),
139107120Sjulian};
140107120Sjulian
141107120Sjulianstatic devclass_t intpm_devclass;
142107120Sjulian
143107120Sjulianstatic device_method_t intpm_pci_methods[] = {
144107120Sjulian	DEVMETHOD(device_probe,		intpm_probe),
145107120Sjulian	DEVMETHOD(device_attach,	intpm_attach),
146107120Sjulian
147107120Sjulian	{ 0, 0 }
148107120Sjulian};
149107120Sjulian
150107120Sjulianstatic driver_t intpm_pci_driver = {
151107120Sjulian	"intpm",
152107120Sjulian	intpm_pci_methods,
153107120Sjulian	sizeof(struct intpm_pci_softc)
154107120Sjulian};
155107120Sjulian
156107120Sjulianstatic int
157107120Sjulianintsmb_probe(device_t dev)
158107120Sjulian{
159107120Sjulian	struct intsmb_softc *sc = device_get_softc(dev);
160107120Sjulian
161107120Sjulian	sc->smbus = device_add_child(dev, "smbus", -1);
162107120Sjulian	if (!sc->smbus)
163107120Sjulian		return (EINVAL);    /* XXX don't know what to return else */
164107120Sjulian	device_set_desc(dev, "Intel PIIX4 SMBUS Interface");
165107120Sjulian
166107120Sjulian	return (BUS_PROBE_DEFAULT); /* XXX don't know what to return else */
167107120Sjulian}
168107120Sjulianstatic int
169107120Sjulianintsmb_attach(device_t dev)
170107120Sjulian{
171107120Sjulian	struct intsmb_softc *sc = device_get_softc(dev);
172107120Sjulian
173107120Sjulian	sc->pci_sc = device_get_softc(device_get_parent(dev));
174107120Sjulian	sc->isbusy = 0;
175107120Sjulian	sc->sh = sc->pci_sc->smbsh;
176107120Sjulian	sc->st = sc->pci_sc->smbst;
177107120Sjulian	sc->pci_sc->smbus = dev;
178107120Sjulian	device_probe_and_attach(sc->smbus);
179107120Sjulian#ifdef ENABLE_ALART
180107120Sjulian	/*Enable Arart*/
181107120Sjulian	bus_space_write_1(sc->st, sc->sh, PIIX4_SMBSLVCNT,
182107120Sjulian	    PIIX4_SMBSLVCNT_ALTEN);
183107120Sjulian#endif
184107120Sjulian	return (0);
185107120Sjulian}
186107120Sjulian
187107120Sjulianstatic int
188107120Sjulianintsmb_callback(device_t dev, int index, void *data)
189107120Sjulian{
190107120Sjulian	int error = 0;
191107120Sjulian	intrmask_t s;
192107120Sjulian
193107120Sjulian	s = splnet();
194107120Sjulian	switch (index) {
195243882Sglebius	case SMB_REQUEST_BUS:
196107120Sjulian		break;
197107120Sjulian	case SMB_RELEASE_BUS:
198107120Sjulian		break;
199107120Sjulian	default:
200107120Sjulian		error = EINVAL;
201107120Sjulian	}
202107120Sjulian	splx(s);
203107120Sjulian
204107120Sjulian	return (error);
205107120Sjulian}
206107120Sjulian
207107120Sjulian/* Counterpart of smbtx_smb_free(). */
208107120Sjulianstatic int
209107120Sjulianintsmb_free(device_t dev)
210107120Sjulian{
211107120Sjulian	struct intsmb_softc *sc = device_get_softc(dev);
212107120Sjulian	intrmask_t s;
213107120Sjulian
214107120Sjulian	if ((bus_space_read_1(sc->st, sc->sh, PIIX4_SMBHSTSTS) &
215107120Sjulian	    PIIX4_SMBHSTSTAT_BUSY) ||
216107120Sjulian#ifdef ENABLE_ALART
217107120Sjulian	    (bus_space_read_1(sc->st, sc->sh, PIIX4_SMBSLVSTS) &
218107120Sjulian	    PIIX4_SMBSLVSTS_BUSY) ||
219107120Sjulian#endif
220107120Sjulian	    sc->isbusy)
221107120Sjulian		return (EBUSY);
222107120Sjulian	s = splhigh();
223107120Sjulian	sc->isbusy = 1;
224107120Sjulian	/* Disable Interrupt in slave part. */
225107120Sjulian#ifndef ENABLE_ALART
226107120Sjulian	bus_space_write_1(sc->st, sc->sh, PIIX4_SMBSLVCNT, 0);
227107120Sjulian#endif
228107120Sjulian	/* Reset INTR Flag to prepare INTR. */
229107120Sjulian	bus_space_write_1(sc->st, sc->sh, PIIX4_SMBHSTSTS,
230107120Sjulian	    (PIIX4_SMBHSTSTAT_INTR | PIIX4_SMBHSTSTAT_ERR |
231107120Sjulian	    PIIX4_SMBHSTSTAT_BUSC | PIIX4_SMBHSTSTAT_FAIL));
232107120Sjulian	splx(s);
233107120Sjulian	return (0);
234107120Sjulian}
235107120Sjulian
236107120Sjulianstatic int
237107120Sjulianintsmb_intr(device_t dev)
238107120Sjulian{
239107120Sjulian	struct intsmb_softc *sc = device_get_softc(dev);
240107120Sjulian	int status;
241107120Sjulian
242107120Sjulian	status = bus_space_read_1(sc->st, sc->sh, PIIX4_SMBHSTSTS);
243107120Sjulian	if (status & PIIX4_SMBHSTSTAT_BUSY)
244107120Sjulian		return (1);
245107120Sjulian
246107120Sjulian	if (status & (PIIX4_SMBHSTSTAT_INTR | PIIX4_SMBHSTSTAT_ERR |
247107120Sjulian	    PIIX4_SMBHSTSTAT_BUSC | PIIX4_SMBHSTSTAT_FAIL)) {
248107120Sjulian		int tmp;
249107120Sjulian
250107120Sjulian		tmp = bus_space_read_1(sc->st, sc->sh, PIIX4_SMBHSTCNT);
251107120Sjulian		bus_space_write_1(sc->st, sc->sh, PIIX4_SMBHSTCNT,
252107120Sjulian		    tmp & ~PIIX4_SMBHSTCNT_INTREN);
253107120Sjulian		if (sc->isbusy) {
254107120Sjulian			sc->isbusy = 0;
255107120Sjulian			wakeup(sc);
256107120Sjulian		}
257107120Sjulian		return (0);
258107120Sjulian	}
259107120Sjulian	return (1); /* Not Completed */
260107120Sjulian}
261107120Sjulian
262107120Sjulianstatic int
263107120Sjulianintsmb_slvintr(device_t dev)
264107120Sjulian{
265107120Sjulian	struct intsmb_softc *sc = device_get_softc(dev);
266107120Sjulian	int status, retval;
267107120Sjulian
268107120Sjulian	retval = 1;
269107120Sjulian	status = bus_space_read_1(sc->st, sc->sh, PIIX4_SMBSLVSTS);
270107120Sjulian	if (status & PIIX4_SMBSLVSTS_BUSY)
271107120Sjulian		return (retval);
272107120Sjulian	if (status & PIIX4_SMBSLVSTS_ALART) {
273107120Sjulian		intsmb_alrintr(dev);
274107120Sjulian		retval = 0;
275107120Sjulian	} else if (status & ~(PIIX4_SMBSLVSTS_ALART | PIIX4_SMBSLVSTS_SDW2
276107120Sjulian		| PIIX4_SMBSLVSTS_SDW1)) {
277107120Sjulian		retval = 0;
278107120Sjulian	}
279107120Sjulian
280107120Sjulian	/* Reset Status Register */
281107120Sjulian	bus_space_write_1(sc->st, sc->sh, PIIX4_SMBSLVSTS,
282107120Sjulian	    PIIX4_SMBSLVSTS_ALART | PIIX4_SMBSLVSTS_SDW2 |
283107120Sjulian	    PIIX4_SMBSLVSTS_SDW1 | PIIX4_SMBSLVSTS_SLV);
284107120Sjulian	return (retval);
285107120Sjulian}
286107120Sjulian
287107120Sjulianstatic void
288107120Sjulianintsmb_alrintr(device_t dev)
289107120Sjulian{
290121054Semax	struct intsmb_softc *sc = device_get_softc(dev);
291121054Semax	int slvcnt;
292121054Semax#ifdef ENABLE_ALART
293121054Semax	int error;
294121054Semax#endif
295107120Sjulian
296107120Sjulian	/* Stop generating INTR from ALART. */
297107120Sjulian	slvcnt = bus_space_read_1(sc->st, sc->sh, PIIX4_SMBSLVCNT);
298107120Sjulian#ifdef ENABLE_ALART
299107120Sjulian	bus_space_write_1(sc->st, sc->sh, PIIX4_SMBSLVCNT,
300107120Sjulian	    slvcnt & ~PIIX4_SMBSLVCNT_ALTEN);
301107120Sjulian#endif
302107120Sjulian	DELAY(5);
303107120Sjulian
304107120Sjulian	/* Ask bus who asserted it and then ask it what's the matter. */
305107120Sjulian#ifdef ENABLE_ALART
306107120Sjulian	error = intsmb_free(dev);
307107120Sjulian	if (!error) {
308107120Sjulian		bus_space_write_1(sc->st, sc->sh, PIIX4_SMBHSTADD,
309107120Sjulian		    SMBALTRESP | LSB);
310107120Sjulian		intsmb_start(dev, PIIX4_SMBHSTCNT_PROT_BYTE, 1);
311107120Sjulian		if (!(error = intsmb_stop_poll(dev))) {
312107120Sjulian			u_int8_t addr;
313107120Sjulian
314107120Sjulian			addr = bus_space_read_1(sc->st, sc->sh,
315107120Sjulian			    PIIX4_SMBHSTDAT0);
316107120Sjulian			printf("ALART_RESPONSE: 0x%x\n", addr);
317107120Sjulian		}
318107120Sjulian	} else
319107120Sjulian		printf("ERROR\n");
320107120Sjulian
321107120Sjulian	/* Re-enable INTR from ALART. */
322107120Sjulian	bus_space_write_1(sc->st, sc->sh, PIIX4_SMBSLVCNT,
323107120Sjulian	    slvcnt | PIIX4_SMBSLVCNT_ALTEN);
324107120Sjulian	DELAY(5);
325107120Sjulian#endif
326107120Sjulian}
327107120Sjulian
328107120Sjulianstatic void
329107120Sjulianintsmb_start(device_t dev, unsigned char cmd, int nointr)
330107120Sjulian{
331107120Sjulian	struct intsmb_softc *sc = device_get_softc(dev);
332107120Sjulian	unsigned char tmp;
333107120Sjulian
334107120Sjulian	tmp = bus_space_read_1(sc->st, sc->sh, PIIX4_SMBHSTCNT);
335107120Sjulian	tmp &= 0xe0;
336107120Sjulian	tmp |= cmd;
337107120Sjulian	tmp |= PIIX4_SMBHSTCNT_START;
338107120Sjulian
339107120Sjulian	/* While not in autoconfiguration enable interrupts. */
340107120Sjulian	if (!cold || !nointr)
341107120Sjulian		tmp |= PIIX4_SMBHSTCNT_INTREN;
342107120Sjulian	bus_space_write_1(sc->st, sc->sh, PIIX4_SMBHSTCNT, tmp);
343107120Sjulian}
344107120Sjulian
345107120Sjulian/*
346107120Sjulian * Polling Code.
347107120Sjulian *
348107120Sjulian * Polling is not encouraged because it requires waiting for the
349107120Sjulian * device if it is busy.
350107120Sjulian * (29063505.pdf from Intel) But during boot, interrupt cannot be used, so use
351107120Sjulian * polling code then.
352107120Sjulian */
353107120Sjulianstatic int
354107120Sjulianintsmb_stop_poll(device_t dev)
355107120Sjulian{
356107120Sjulian	struct intsmb_softc *sc = device_get_softc(dev);
357107120Sjulian	int error, i;
358107120Sjulian	int tmp;
359107120Sjulian
360107120Sjulian	/*
361107120Sjulian	 *  In smbtx driver, Simply waiting.
362107120Sjulian	 *  This loops 100-200 times.
363107120Sjulian	 */
364107120Sjulian	for (i = 0; i < 0x7fff; i++)
365107120Sjulian		if (bus_space_read_1(sc->st, sc->sh, PIIX4_SMBHSTSTS) &
366107120Sjulian		    PIIX4_SMBHSTSTAT_BUSY)
367107120Sjulian			break;
368107120Sjulian
369107120Sjulian	for (i = 0; i < 0x7fff; i++) {
370107120Sjulian		int status;
371107120Sjulian
372107120Sjulian		status = bus_space_read_1(sc->st, sc->sh, PIIX4_SMBHSTSTS);
373107120Sjulian		if (!(status & PIIX4_SMBHSTSTAT_BUSY)) {
374107120Sjulian			sc->isbusy = 0;
375107120Sjulian			error = (status & PIIX4_SMBHSTSTAT_ERR) ? EIO :
376107120Sjulian			    (status & PIIX4_SMBHSTSTAT_BUSC) ? EBUSY :
377107120Sjulian			    (status & PIIX4_SMBHSTSTAT_FAIL) ? EIO : 0;
378107120Sjulian			if (error == 0 && !(status & PIIX4_SMBHSTSTAT_INTR))
379107120Sjulian				printf("unknown cause why?");
380107120Sjulian			return (error);
381107120Sjulian		}
382107120Sjulian	}
383107120Sjulian
384107120Sjulian	sc->isbusy = 0;
385107120Sjulian	tmp = bus_space_read_1(sc->st, sc->sh, PIIX4_SMBHSTCNT);
386107120Sjulian	bus_space_write_1(sc->st, sc->sh, PIIX4_SMBHSTCNT,
387107120Sjulian	    tmp & ~PIIX4_SMBHSTCNT_INTREN);
388107120Sjulian	return (EIO);
389107120Sjulian}
390107120Sjulian
391107120Sjulian/*
392107120Sjulian * Wait for completion and return result.
393107120Sjulian */
394107120Sjulianstatic int
395107120Sjulianintsmb_stop(device_t dev)
396107120Sjulian{
397107120Sjulian	struct intsmb_softc *sc = device_get_softc(dev);
398107120Sjulian	int error;
399107120Sjulian	intrmask_t s;
400107120Sjulian
401107120Sjulian	if (cold) {
402107120Sjulian		/* So that it can use device during device probe on SMBus. */
403107120Sjulian		error = intsmb_stop_poll(dev);
404107120Sjulian		return (error);
405107120Sjulian	}
406107120Sjulian
407107120Sjulian	if (!tsleep(sc, (PWAIT) | PCATCH, "SMBWAI", hz/8)) {
408107120Sjulian		int status;
409107120Sjulian
410107120Sjulian		status = bus_space_read_1(sc->st, sc->sh, PIIX4_SMBHSTSTS);
411107120Sjulian		if (!(status & PIIX4_SMBHSTSTAT_BUSY)) {
412107120Sjulian			error = (status & PIIX4_SMBHSTSTAT_ERR) ? EIO :
413107120Sjulian			    (status & PIIX4_SMBHSTSTAT_BUSC) ? EBUSY :
414107120Sjulian			    (status & PIIX4_SMBHSTSTAT_FAIL) ? EIO : 0;
415107120Sjulian			if (error == 0 && !(status & PIIX4_SMBHSTSTAT_INTR))
416107120Sjulian				printf("intsmb%d: unknown cause why?\n",
417107120Sjulian				    device_get_unit(dev));
418107120Sjulian#ifdef ENABLE_ALART
419107120Sjulian			bus_space_write_1(sc->st, sc->sh, PIIX4_SMBSLVCNT,
420107120Sjulian			    PIIX4_SMBSLVCNT_ALTEN);
421107120Sjulian#endif
422107120Sjulian			return (error);
423107120Sjulian		}
424107120Sjulian	}
425107120Sjulian
426107120Sjulian	/* Timeout Procedure. */
427107120Sjulian	s = splhigh();
428107120Sjulian	sc->isbusy = 0;
429107120Sjulian
430107120Sjulian	/* Re-enable supressed interrupt from slave part. */
431107120Sjulian	bus_space_write_1(sc->st, sc->sh, PIIX4_SMBSLVCNT,
432107120Sjulian	    PIIX4_SMBSLVCNT_ALTEN);
433107120Sjulian	splx(s);
434107120Sjulian	return (EIO);
435107120Sjulian}
436107120Sjulian
437107120Sjulianstatic int
438107120Sjulianintsmb_quick(device_t dev, u_char slave, int how)
439107120Sjulian{
440107120Sjulian	struct intsmb_softc *sc = device_get_softc(dev);
441107120Sjulian	int error = 0;
442107120Sjulian	u_char data;
443107120Sjulian
444107120Sjulian	data = slave;
445107120Sjulian
446107120Sjulian	/* Quick command is part of Address, I think. */
447107120Sjulian	switch(how) {
448107120Sjulian	case SMB_QWRITE:
449107120Sjulian		data &= ~LSB;
450107120Sjulian		break;
451107120Sjulian	case SMB_QREAD:
452107120Sjulian		data |= LSB;
453107120Sjulian		break;
454107120Sjulian	default:
455107120Sjulian		error = EINVAL;
456107120Sjulian	}
457121054Semax	if (!error) {
458121054Semax		error = intsmb_free(dev);
459107120Sjulian		if (!error) {
460107120Sjulian			bus_space_write_1(sc->st, sc->sh, PIIX4_SMBHSTADD,
461121054Semax			    data);
462121054Semax			intsmb_start(dev, PIIX4_SMBHSTCNT_PROT_QUICK, 0);
463107120Sjulian			error = intsmb_stop(dev);
464107120Sjulian		}
465107120Sjulian	}
466107120Sjulian
467107120Sjulian	return (error);
468107120Sjulian}
469107120Sjulian
470107120Sjulianstatic int
471107120Sjulianintsmb_sendb(device_t dev, u_char slave, char byte)
472107120Sjulian{
473107120Sjulian	struct intsmb_softc *sc = device_get_softc(dev);
474107120Sjulian	int error;
475107120Sjulian
476107120Sjulian	error = intsmb_free(dev);
477107120Sjulian	if (!error) {
478107120Sjulian		bus_space_write_1(sc->st, sc->sh, PIIX4_SMBHSTADD,
479107120Sjulian		    slave & ~LSB);
480107120Sjulian		bus_space_write_1(sc->st, sc->sh, PIIX4_SMBHSTCMD, byte);
481107120Sjulian		intsmb_start(dev, PIIX4_SMBHSTCNT_PROT_BYTE, 0);
482107120Sjulian		error = intsmb_stop(dev);
483107120Sjulian	}
484107120Sjulian	return (error);
485107120Sjulian}
486107120Sjulian
487107120Sjulianstatic int
488107120Sjulianintsmb_recvb(device_t dev, u_char slave, char *byte)
489107120Sjulian{
490107120Sjulian	struct intsmb_softc *sc = device_get_softc(dev);
491107120Sjulian	int error;
492107120Sjulian
493107120Sjulian	error = intsmb_free(dev);
494107120Sjulian	if (!error) {
495107120Sjulian		bus_space_write_1(sc->st, sc->sh, PIIX4_SMBHSTADD, slave | LSB);
496107120Sjulian		intsmb_start(dev, PIIX4_SMBHSTCNT_PROT_BYTE, 0);
497107120Sjulian		if (!(error = intsmb_stop(dev))) {
498107120Sjulian#ifdef RECV_IS_IN_CMD
499107120Sjulian			/*
500107120Sjulian			 * Linux SMBus stuff also troubles
501107120Sjulian			 * Because Intel's datasheet does not make clear.
502107120Sjulian			 */
503107120Sjulian			*byte = bus_space_read_1(sc->st, sc->sh,
504107120Sjulian			    PIIX4_SMBHSTCMD);
505107120Sjulian#else
506107120Sjulian			*byte = bus_space_read_1(sc->st, sc->sh,
507107120Sjulian			    PIIX4_SMBHSTDAT0);
508107120Sjulian#endif
509107120Sjulian		}
510107120Sjulian	}
511107120Sjulian	return (error);
512107120Sjulian}
513107120Sjulian
514107120Sjulianstatic int
515107120Sjulianintsmb_writeb(device_t dev, u_char slave, char cmd, char byte)
516107120Sjulian{
517107120Sjulian	struct intsmb_softc *sc = device_get_softc(dev);
518107120Sjulian	int error;
519107120Sjulian
520107120Sjulian	error = intsmb_free(dev);
521107120Sjulian	if (!error) {
522107120Sjulian		bus_space_write_1(sc->st, sc->sh, PIIX4_SMBHSTADD,
523107120Sjulian		    slave & ~LSB);
524107120Sjulian		bus_space_write_1(sc->st, sc->sh, PIIX4_SMBHSTCMD, cmd);
525107120Sjulian		bus_space_write_1(sc->st, sc->sh, PIIX4_SMBHSTDAT0, byte);
526107120Sjulian		intsmb_start(dev, PIIX4_SMBHSTCNT_PROT_BDATA, 0);
527107120Sjulian		error = intsmb_stop(dev);
528107120Sjulian	}
529107120Sjulian	return (error);
530107120Sjulian}
531107120Sjulian
532107120Sjulianstatic int
533107120Sjulianintsmb_writew(device_t dev, u_char slave, char cmd, short word)
534107120Sjulian{
535107120Sjulian	struct intsmb_softc *sc = device_get_softc(dev);
536107120Sjulian	int error;
537107120Sjulian
538107120Sjulian	error = intsmb_free(dev);
539107120Sjulian	if (!error) {
540107120Sjulian		bus_space_write_1(sc->st, sc->sh, PIIX4_SMBHSTADD,
541107120Sjulian		    slave & ~LSB);
542107120Sjulian		bus_space_write_1(sc->st, sc->sh, PIIX4_SMBHSTCMD, cmd);
543107120Sjulian		bus_space_write_1(sc->st, sc->sh, PIIX4_SMBHSTDAT0,
544107120Sjulian		    word & 0xff);
545107120Sjulian		bus_space_write_1(sc->st, sc->sh, PIIX4_SMBHSTDAT1,
546107120Sjulian		    (word >> 8) & 0xff);
547107120Sjulian		intsmb_start(dev, PIIX4_SMBHSTCNT_PROT_WDATA, 0);
548107120Sjulian		error = intsmb_stop(dev);
549107120Sjulian	}
550107120Sjulian	return (error);
551107120Sjulian}
552107120Sjulian
553107120Sjulianstatic int
554107120Sjulianintsmb_readb(device_t dev, u_char slave, char cmd, char *byte)
555107120Sjulian{
556107120Sjulian	struct intsmb_softc *sc = device_get_softc(dev);
557107120Sjulian	int error;
558107120Sjulian
559107120Sjulian	error = intsmb_free(dev);
560107120Sjulian	if (!error) {
561107120Sjulian		bus_space_write_1(sc->st, sc->sh, PIIX4_SMBHSTADD, slave | LSB);
562107120Sjulian		bus_space_write_1(sc->st, sc->sh, PIIX4_SMBHSTCMD, cmd);
563107120Sjulian		intsmb_start(dev, PIIX4_SMBHSTCNT_PROT_BDATA, 0);
564107120Sjulian		if (!(error = intsmb_stop(dev)))
565107120Sjulian			*byte = bus_space_read_1(sc->st, sc->sh,
566107120Sjulian			    PIIX4_SMBHSTDAT0);
567107120Sjulian	}
568107120Sjulian	return (error);
569107120Sjulian}
570107120Sjulianstatic int
571107120Sjulianintsmb_readw(device_t dev, u_char slave, char cmd, short *word)
572107120Sjulian{
573107120Sjulian	struct intsmb_softc *sc = device_get_softc(dev);
574107120Sjulian	int error;
575107120Sjulian
576107120Sjulian	error = intsmb_free(dev);
577107120Sjulian	if (!error) {
578107120Sjulian		bus_space_write_1(sc->st, sc->sh, PIIX4_SMBHSTADD, slave | LSB);
579107120Sjulian		bus_space_write_1(sc->st, sc->sh, PIIX4_SMBHSTCMD, cmd);
580107120Sjulian		intsmb_start(dev, PIIX4_SMBHSTCNT_PROT_WDATA, 0);
581107120Sjulian		if (!(error = intsmb_stop(dev))) {
582107120Sjulian			*word = bus_space_read_1(sc->st, sc->sh,
583107120Sjulian			    PIIX4_SMBHSTDAT0);
584107120Sjulian			*word |= bus_space_read_1(sc->st, sc->sh,
585107120Sjulian			    PIIX4_SMBHSTDAT1) << 8;
586121054Semax		}
587107120Sjulian	}
588107120Sjulian	return (error);
589107120Sjulian}
590107120Sjulian
591107120Sjulian/*
592107120Sjulian * Data sheet claims that it implements all function, but also claims
593107120Sjulian * that it implements 7 function and not mention PCALL. So I don't know
594107120Sjulian * whether it will work.
595107120Sjulian */
596107120Sjulianstatic int
597107120Sjulianintsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata)
598107120Sjulian{
599107120Sjulian#ifdef PROCCALL_TEST
600107120Sjulian	struct intsmb_softc *sc = device_get_softc(dev);
601107120Sjulian	int error;
602107120Sjulian
603107120Sjulian	error = intsmb_free(dev);
604107120Sjulian	if (!error) {
605107120Sjulian		bus_space_write_1(sc->st, sc->sh, PIIX4_SMBHSTADD,
606107120Sjulian 		    slave & ~LSB);
607107120Sjulian		bus_space_write_1(sc->st, sc->sh, PIIX4_SMBHSTCMD, cmd);
608107120Sjulian		bus_space_write_1(sc->st, sc->sh, PIIX4_SMBHSTDAT0,
609107120Sjulian		    sdata & 0xff);
610107120Sjulian		bus_space_write_1(sc->st, sc->sh, PIIX4_SMBHSTDAT1,
611107120Sjulian		    (sdata & 0xff) >> 8);
612107120Sjulian		intsmb_start(dev, PIIX4_SMBHSTCNT_PROT_WDATA, 0);
613107120Sjulian	}
614107120Sjulian	if (!(error = intsmb_stop(dev))) {
615107120Sjulian		*rdata = bus_space_read_1(sc->st, sc->sh, PIIX4_SMBHSTDAT0);
616107120Sjulian		*rdata |= bus_space_read_1(sc->st, sc->sh, PIIX4_SMBHSTDAT1) <<
617107120Sjulian		    8;
618107120Sjulian	}
619107120Sjulian	return (error);
620107120Sjulian#else
621107120Sjulian	return (0);
622107120Sjulian#endif
623107120Sjulian}
624107120Sjulian
625107120Sjulianstatic int
626107120Sjulianintsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf)
627107120Sjulian{
628107120Sjulian	struct intsmb_softc *sc = device_get_softc(dev);
629107120Sjulian	int error, i;
630107120Sjulian
631107120Sjulian	error = intsmb_free(dev);
632107120Sjulian	if (count > SMBBLOCKTRANS_MAX || count == 0)
633107120Sjulian		error = SMB_EINVAL;
634107120Sjulian	if (!error) {
635107120Sjulian		/* Reset internal array index. */
636107120Sjulian		bus_space_read_1(sc->st, sc->sh, PIIX4_SMBHSTCNT);
637107120Sjulian
638107120Sjulian		bus_space_write_1(sc->st, sc->sh, PIIX4_SMBHSTADD,
639107120Sjulian		    slave & ~LSB);
640107120Sjulian		bus_space_write_1(sc->st, sc->sh, PIIX4_SMBHSTCMD, cmd);
641107120Sjulian		for (i = 0; i < count; i++)
642107120Sjulian			bus_space_write_1(sc->st, sc->sh, PIIX4_SMBBLKDAT,
643107120Sjulian			    buf[i]);
644107120Sjulian		bus_space_write_1(sc->st, sc->sh, PIIX4_SMBHSTDAT0, count);
645107120Sjulian		intsmb_start(dev, PIIX4_SMBHSTCNT_PROT_BLOCK, 0);
646107120Sjulian		error = intsmb_stop(dev);
647107120Sjulian	}
648107120Sjulian	return (error);
649107120Sjulian}
650107120Sjulian
651107120Sjulianstatic int
652107120Sjulianintsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf)
653107120Sjulian{
654107120Sjulian	struct intsmb_softc *sc = device_get_softc(dev);
655107120Sjulian	int error, i;
656107120Sjulian	u_char data, nread;
657107120Sjulian
658107120Sjulian	error = intsmb_free(dev);
659107120Sjulian	if (*count > SMBBLOCKTRANS_MAX || *count == 0)
660107120Sjulian		error = SMB_EINVAL;
661107120Sjulian	if (!error) {
662107120Sjulian		/* Reset internal array index. */
663107120Sjulian		bus_space_read_1(sc->st, sc->sh, PIIX4_SMBHSTCNT);
664107120Sjulian
665107120Sjulian		bus_space_write_1(sc->st, sc->sh, PIIX4_SMBHSTADD, slave | LSB);
666107120Sjulian		bus_space_write_1(sc->st, sc->sh, PIIX4_SMBHSTCMD, cmd);
667107120Sjulian		bus_space_write_1(sc->st, sc->sh, PIIX4_SMBHSTDAT0, *count);
668107120Sjulian		intsmb_start(dev, PIIX4_SMBHSTCNT_PROT_BLOCK, 0);
669107120Sjulian		error = intsmb_stop(dev);
670107120Sjulian		if (!error) {
671107120Sjulian			nread= bus_space_read_1(sc->st, sc->sh,
672107120Sjulian			    PIIX4_SMBHSTDAT0);
673107120Sjulian			if (nread != 0 && nread <= SMBBLOCKTRANS_MAX) {
674107120Sjulian				for (i = 0; i < nread; i++) {
675107120Sjulian					data = bus_space_read_1(sc->st, sc->sh,
676107120Sjulian					    PIIX4_SMBBLKDAT);
677107120Sjulian					if (i < *count)
678107120Sjulian						buf[i] = data;
679107120Sjulian				}
680107120Sjulian				*count = nread;
681107120Sjulian			} else {
682107120Sjulian				error = EIO;
683107120Sjulian			}
684107120Sjulian		}
685107120Sjulian	}
686107120Sjulian	return (error);
687107120Sjulian}
688107120Sjulian
689107120SjulianDRIVER_MODULE(intsmb, intpm, intpm_driver, intsmb_devclass, 0, 0);
690107120Sjulian
691107120Sjulianstatic int
692107120Sjulianintpm_attach(device_t dev)
693107120Sjulian{
694107120Sjulian	struct intpm_pci_softc *sc;
695107120Sjulian	struct resource *res;
696107120Sjulian	device_t smbinterface;
697107120Sjulian	void *ih;
698107120Sjulian	char *str;
699107120Sjulian	int error, rid, value;
700107120Sjulian	int unit = device_get_unit(dev);
701107120Sjulian
702107120Sjulian	sc = device_get_softc(dev);
703107120Sjulian	if (sc == NULL)
704107120Sjulian		return (ENOMEM);
705107120Sjulian
706107120Sjulian	rid = PCI_BASE_ADDR_SMB;
707107120Sjulian	res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, RF_ACTIVE);
708107120Sjulian	if (res == NULL) {
709107120Sjulian		device_printf(dev, "Could not allocate Bus space\n");
710107120Sjulian		return (ENXIO);
711107120Sjulian	}
712107120Sjulian	sc->smbst = rman_get_bustag(res);
713107120Sjulian	sc->smbsh = rman_get_bushandle(res);
714107120Sjulian
715121054Semax#ifdef __i386__
716121054Semax	device_printf(dev, "%s %lx\n", (sc->smbst == I386_BUS_SPACE_IO) ?
717107120Sjulian	    "I/O mapped" : "Memory", rman_get_start(res));
718107120Sjulian#endif
719121054Semax
720121054Semax#ifndef NO_CHANGE_PCICONF
721121054Semax	pci_write_config(dev, PCIR_INTLINE, 0x9, 1);
722121054Semax	pci_write_config(dev, PCI_HST_CFG_SMB,
723107120Sjulian	    PCI_INTR_SMB_IRQ9 | PCI_INTR_SMB_ENABLE, 1);
724107120Sjulian#endif
725107120Sjulian	value = pci_read_config(dev, PCI_HST_CFG_SMB, 1);
726107120Sjulian	switch (value & 0xe) {
727107120Sjulian	case PCI_INTR_SMB_SMI:
728107120Sjulian		str = "SMI";
729107120Sjulian		break;
730107120Sjulian	case PCI_INTR_SMB_IRQ9:
731107120Sjulian		str = "IRQ 9";
732107120Sjulian		break;
733107120Sjulian	default:
734107120Sjulian		str = "BOGUS";
735107120Sjulian	}
736107120Sjulian	device_printf(dev, "intr %s %s ", str,
737107120Sjulian	    (value & 1) ? "enabled" : "disabled");
738107120Sjulian	value = pci_read_config(dev, PCI_REVID_SMB, 1);
739107120Sjulian	printf("revision %d\n", value);
740107120Sjulian
741107120Sjulian	/* Install interrupt handler. */
742107120Sjulian	rid = 0;
743121054Semax	res = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 9, 9, 1,
744107120Sjulian	    RF_SHAREABLE | RF_ACTIVE);
745107120Sjulian	if (res == NULL) {
746107120Sjulian		device_printf(dev, "could not allocate irq");
747107120Sjulian		return (ENOMEM);
748107120Sjulian	}
749107120Sjulian	error = bus_setup_intr(dev, res, INTR_TYPE_MISC, intpm_intr, sc, &ih);
750121054Semax	if (error) {
751121054Semax		device_printf(dev, "Failed to map intr\n");
752121054Semax		return (error);
753107120Sjulian	}
754107120Sjulian	smbinterface = device_add_child(dev, "intsmb", unit);
755107120Sjulian	if (!smbinterface)
756107120Sjulian		printf("intsmb%d: could not add SMBus device\n", unit);
757107120Sjulian	device_probe_and_attach(smbinterface);
758107120Sjulian
759107120Sjulian	value = pci_read_config(dev, PCI_BASE_ADDR_PM, 4);
760107120Sjulian	printf("intpm%d: PM %s %x \n", unit,
761107120Sjulian	    (value & 1) ? "I/O mapped" : "Memory", value & 0xfffe);
762107120Sjulian	return (0);
763107120Sjulian}
764107120Sjulian
765107120Sjulianstatic int
766107120Sjulianintpm_probe(device_t dev)
767107120Sjulian{
768107120Sjulian	struct _pcsid *ep = pci_ids;
769107120Sjulian	uint32_t device_id = pci_get_devid(dev);
770107120Sjulian
771107120Sjulian	while (ep->type && ep->type != device_id)
772107120Sjulian		++ep;
773107120Sjulian	if (ep->desc != NULL) {
774107120Sjulian		device_set_desc(dev, ep->desc);
775107120Sjulian		bus_set_resource(dev, SYS_RES_IRQ, 0, 9, 1); /* XXX setup intr resource */
776107120Sjulian		return (BUS_PROBE_DEFAULT);
777107120Sjulian	} else {
778107120Sjulian		return (ENXIO);
779107120Sjulian	}
780107120Sjulian}
781107120Sjulian
782107120Sjulianstatic void
783107120Sjulianintpm_intr(void *arg)
784107120Sjulian{
785107120Sjulian	struct intpm_pci_softc *sc = arg;
786107120Sjulian
787107120Sjulian	intsmb_intr(sc->smbus);
788107120Sjulian	intsmb_slvintr(sc->smbus);
789107120Sjulian}
790107120Sjulian
791107120SjulianDRIVER_MODULE(intpm, pci , intpm_pci_driver, intpm_devclass, 0, 0);
792107120SjulianDRIVER_MODULE(smbus, intsmb, smbus_driver, smbus_devclass, 0, 0);
793107120SjulianMODULE_DEPEND(intpm, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER);
794107120SjulianMODULE_VERSION(intpm, 1);
795107120Sjulian