1139749Simp/*-
266703Sarchie * ichsmb.c
366703Sarchie *
4119418Sobrien * Author: Archie Cobbs <archie@freebsd.org>
566703Sarchie * Copyright (c) 2000 Whistle Communications, Inc.
666703Sarchie * All rights reserved.
766703Sarchie *
866703Sarchie * Subject to the following obligations and disclaimer of warranty, use and
966703Sarchie * redistribution of this software, in source or object code forms, with or
1066703Sarchie * without modifications are expressly permitted by Whistle Communications;
1166703Sarchie * provided, however, that:
1266703Sarchie * 1. Any and all reproductions of the source or object code must include the
1366703Sarchie *    copyright notice above and the following disclaimer of warranties; and
1466703Sarchie * 2. No rights are granted, in any manner or form, to use Whistle
1566703Sarchie *    Communications, Inc. trademarks, including the mark "WHISTLE
1666703Sarchie *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
1766703Sarchie *    such appears in the above copyright notice or in the software.
1866703Sarchie *
1966703Sarchie * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
2066703Sarchie * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
2166703Sarchie * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
2266703Sarchie * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
2366703Sarchie * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
2466703Sarchie * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
2566703Sarchie * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
2666703Sarchie * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
2766703Sarchie * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
2866703Sarchie * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
2966703Sarchie * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
3066703Sarchie * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
3166703Sarchie * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
3266703Sarchie * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3366703Sarchie * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
3466703Sarchie * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
3566703Sarchie * OF SUCH DAMAGE.
3666703Sarchie */
3766703Sarchie
38119418Sobrien#include <sys/cdefs.h>
39119418Sobrien__FBSDID("$FreeBSD$");
40119418Sobrien
4166703Sarchie/*
4266703Sarchie * Support for the SMBus controller logical device which is part of the
4366703Sarchie * Intel 81801AA (ICH) and 81801AB (ICH0) I/O controller hub chips.
4469689Sarchie *
4569689Sarchie * This driver assumes that the generic SMBus code will ensure that
4669689Sarchie * at most one process at a time calls into the SMBus methods below.
4766703Sarchie */
4866703Sarchie
4966703Sarchie#include <sys/param.h>
5066703Sarchie#include <sys/systm.h>
5166703Sarchie#include <sys/kernel.h>
5266703Sarchie#include <sys/errno.h>
5374914Sjhb#include <sys/lock.h>
54162234Sjhb#include <sys/module.h>
5569689Sarchie#include <sys/mutex.h>
5666703Sarchie#include <sys/syslog.h>
5766703Sarchie#include <sys/bus.h>
5866703Sarchie
5966703Sarchie#include <machine/bus.h>
6066703Sarchie#include <sys/rman.h>
6166703Sarchie#include <machine/resource.h>
6266703Sarchie
6366703Sarchie#include <dev/smbus/smbconf.h>
6466703Sarchie
6566703Sarchie#include <dev/ichsmb/ichsmb_var.h>
6666703Sarchie#include <dev/ichsmb/ichsmb_reg.h>
6766703Sarchie
6866703Sarchie/*
6966703Sarchie * Enable debugging by defining ICHSMB_DEBUG to a non-zero value.
7066703Sarchie */
7166703Sarchie#define ICHSMB_DEBUG	0
72143063Sjoerg#if ICHSMB_DEBUG != 0 && defined(__CC_SUPPORTS___FUNC__)
7366703Sarchie#define DBG(fmt, args...)	\
74165951Sjhb	do { printf("%s: " fmt, __func__ , ## args); } while (0)
7566703Sarchie#else
7666703Sarchie#define DBG(fmt, args...)	do { } while (0)
7766703Sarchie#endif
7866703Sarchie
7966703Sarchie/*
8066703Sarchie * Our child device driver name
8166703Sarchie */
8266703Sarchie#define DRIVER_SMBUS	"smbus"
8366703Sarchie
8466703Sarchie/*
8566703Sarchie * Internal functions
8666703Sarchie */
8766703Sarchiestatic int ichsmb_wait(sc_p sc);
8866703Sarchie
8966703Sarchie/********************************************************************
9066703Sarchie		BUS-INDEPENDENT BUS METHODS
9166703Sarchie********************************************************************/
9266703Sarchie
9366703Sarchie/*
9466703Sarchie * Handle probe-time duties that are independent of the bus
9566703Sarchie * our device lives on.
9666703Sarchie */
9766703Sarchieint
9866703Sarchieichsmb_probe(device_t dev)
9966703Sarchie{
100143160Simp	return (BUS_PROBE_DEFAULT);
10166703Sarchie}
10266703Sarchie
10366703Sarchie/*
10466703Sarchie * Handle attach-time duties that are independent of the bus
10566703Sarchie * our device lives on.
10666703Sarchie */
10766703Sarchieint
10866703Sarchieichsmb_attach(device_t dev)
10966703Sarchie{
11066703Sarchie	const sc_p sc = device_get_softc(dev);
11166703Sarchie	int error;
11266703Sarchie
113165951Sjhb	/* Create mutex */
114165951Sjhb	mtx_init(&sc->mutex, device_get_nameunit(dev), "ichsmb", MTX_DEF);
115165951Sjhb
116147253Stakawata	/* Add child: an instance of the "smbus" device */
117148497Sbrian	if ((sc->smb = device_add_child(dev, DRIVER_SMBUS, -1)) == NULL) {
118165951Sjhb		device_printf(dev, "no \"%s\" child found\n", DRIVER_SMBUS);
119165951Sjhb		error = ENXIO;
120165951Sjhb		goto fail;
121147253Stakawata	}
122147253Stakawata
12366703Sarchie	/* Clear interrupt conditions */
124179622Sjhb	bus_write_1(sc->io_res, ICH_HST_STA, 0xff);
12566703Sarchie
126165951Sjhb	/* Set up interrupt handler */
127179622Sjhb	error = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
128166901Spiso	    NULL, ichsmb_device_intr, sc, &sc->irq_handle);
129165951Sjhb	if (error != 0) {
130165951Sjhb		device_printf(dev, "can't setup irq\n");
131165951Sjhb		goto fail;
132165951Sjhb	}
133165951Sjhb
134165951Sjhb	/* Attach "smbus" child */
13566703Sarchie	if ((error = bus_generic_attach(dev)) != 0) {
136165951Sjhb		device_printf(dev, "failed to attach child: %d\n", error);
137165951Sjhb		goto fail;
13866703Sarchie	}
13966703Sarchie
14069689Sarchie	return (0);
141165951Sjhb
142165951Sjhbfail:
143165951Sjhb	mtx_destroy(&sc->mutex);
144165951Sjhb	return (error);
14566703Sarchie}
14666703Sarchie
14766703Sarchie/********************************************************************
14866703Sarchie			SMBUS METHODS
14966703Sarchie********************************************************************/
15066703Sarchie
15166703Sarchieint
152162234Sjhbichsmb_callback(device_t dev, int index, void *data)
15366703Sarchie{
15466703Sarchie	int smb_error = 0;
15566703Sarchie
15666703Sarchie	DBG("index=%d how=%d\n", index, data ? *(int *)data : -1);
15766703Sarchie	switch (index) {
15866703Sarchie	case SMB_REQUEST_BUS:
15966703Sarchie		break;
16066703Sarchie	case SMB_RELEASE_BUS:
16166703Sarchie		break;
16266703Sarchie	default:
16366703Sarchie		smb_error = SMB_EABORT;	/* XXX */
16466703Sarchie		break;
16566703Sarchie	}
16666703Sarchie	DBG("smb_error=%d\n", smb_error);
16766703Sarchie	return (smb_error);
16866703Sarchie}
16966703Sarchie
17066703Sarchieint
17166703Sarchieichsmb_quick(device_t dev, u_char slave, int how)
17266703Sarchie{
17366703Sarchie	const sc_p sc = device_get_softc(dev);
17466703Sarchie	int smb_error;
17566703Sarchie
17666703Sarchie	DBG("slave=0x%02x how=%d\n", slave, how);
17766703Sarchie	KASSERT(sc->ich_cmd == -1,
17887599Sobrien	    ("%s: ich_cmd=%d\n", __func__ , sc->ich_cmd));
17966703Sarchie	switch (how) {
18066703Sarchie	case SMB_QREAD:
18166703Sarchie	case SMB_QWRITE:
18272200Sbmilekic		mtx_lock(&sc->mutex);
18366703Sarchie		sc->ich_cmd = ICH_HST_CNT_SMB_CMD_QUICK;
184179622Sjhb		bus_write_1(sc->io_res, ICH_XMIT_SLVA,
185188077Sjhb		    slave | (how == SMB_QREAD ?
18666703Sarchie	    		ICH_XMIT_SLVA_READ : ICH_XMIT_SLVA_WRITE));
187179622Sjhb		bus_write_1(sc->io_res, ICH_HST_CNT,
18866703Sarchie		    ICH_HST_CNT_START | ICH_HST_CNT_INTREN | sc->ich_cmd);
18966703Sarchie		smb_error = ichsmb_wait(sc);
19072200Sbmilekic		mtx_unlock(&sc->mutex);
19166703Sarchie		break;
19266703Sarchie	default:
19366703Sarchie		smb_error = SMB_ENOTSUPP;
19466703Sarchie	}
19566703Sarchie	DBG("smb_error=%d\n", smb_error);
19666703Sarchie	return (smb_error);
19766703Sarchie}
19866703Sarchie
19966703Sarchieint
20066703Sarchieichsmb_sendb(device_t dev, u_char slave, char byte)
20166703Sarchie{
20266703Sarchie	const sc_p sc = device_get_softc(dev);
20366703Sarchie	int smb_error;
20466703Sarchie
20566703Sarchie	DBG("slave=0x%02x byte=0x%02x\n", slave, (u_char)byte);
20666703Sarchie	KASSERT(sc->ich_cmd == -1,
20787599Sobrien	    ("%s: ich_cmd=%d\n", __func__ , sc->ich_cmd));
20872200Sbmilekic	mtx_lock(&sc->mutex);
20966703Sarchie	sc->ich_cmd = ICH_HST_CNT_SMB_CMD_BYTE;
210179622Sjhb	bus_write_1(sc->io_res, ICH_XMIT_SLVA,
211188077Sjhb	    slave | ICH_XMIT_SLVA_WRITE);
212179622Sjhb	bus_write_1(sc->io_res, ICH_HST_CMD, byte);
213179622Sjhb	bus_write_1(sc->io_res, ICH_HST_CNT,
21466703Sarchie	    ICH_HST_CNT_START | ICH_HST_CNT_INTREN | sc->ich_cmd);
21566703Sarchie	smb_error = ichsmb_wait(sc);
21672200Sbmilekic	mtx_unlock(&sc->mutex);
21766703Sarchie	DBG("smb_error=%d\n", smb_error);
21866703Sarchie	return (smb_error);
21966703Sarchie}
22066703Sarchie
22166703Sarchieint
22266703Sarchieichsmb_recvb(device_t dev, u_char slave, char *byte)
22366703Sarchie{
22466703Sarchie	const sc_p sc = device_get_softc(dev);
22566703Sarchie	int smb_error;
22666703Sarchie
22766703Sarchie	DBG("slave=0x%02x\n", slave);
22866703Sarchie	KASSERT(sc->ich_cmd == -1,
22987599Sobrien	    ("%s: ich_cmd=%d\n", __func__ , sc->ich_cmd));
23072200Sbmilekic	mtx_lock(&sc->mutex);
23166703Sarchie	sc->ich_cmd = ICH_HST_CNT_SMB_CMD_BYTE;
232179622Sjhb	bus_write_1(sc->io_res, ICH_XMIT_SLVA,
233188077Sjhb	    slave | ICH_XMIT_SLVA_READ);
234179622Sjhb	bus_write_1(sc->io_res, ICH_HST_CNT,
23566703Sarchie	    ICH_HST_CNT_START | ICH_HST_CNT_INTREN | sc->ich_cmd);
23666703Sarchie	if ((smb_error = ichsmb_wait(sc)) == SMB_ENOERR)
237179622Sjhb		*byte = bus_read_1(sc->io_res, ICH_D0);
23872200Sbmilekic	mtx_unlock(&sc->mutex);
23966703Sarchie	DBG("smb_error=%d byte=0x%02x\n", smb_error, (u_char)*byte);
24066703Sarchie	return (smb_error);
24166703Sarchie}
24266703Sarchie
24366703Sarchieint
24466703Sarchieichsmb_writeb(device_t dev, u_char slave, char cmd, char byte)
24566703Sarchie{
24666703Sarchie	const sc_p sc = device_get_softc(dev);
24766703Sarchie	int smb_error;
24866703Sarchie
24966703Sarchie	DBG("slave=0x%02x cmd=0x%02x byte=0x%02x\n",
25066703Sarchie	    slave, (u_char)cmd, (u_char)byte);
25166703Sarchie	KASSERT(sc->ich_cmd == -1,
25287599Sobrien	    ("%s: ich_cmd=%d\n", __func__ , sc->ich_cmd));
25372200Sbmilekic	mtx_lock(&sc->mutex);
25466703Sarchie	sc->ich_cmd = ICH_HST_CNT_SMB_CMD_BYTE_DATA;
255179622Sjhb	bus_write_1(sc->io_res, ICH_XMIT_SLVA,
256188077Sjhb	    slave | ICH_XMIT_SLVA_WRITE);
257179622Sjhb	bus_write_1(sc->io_res, ICH_HST_CMD, cmd);
258179622Sjhb	bus_write_1(sc->io_res, ICH_D0, byte);
259179622Sjhb	bus_write_1(sc->io_res, ICH_HST_CNT,
26066703Sarchie	    ICH_HST_CNT_START | ICH_HST_CNT_INTREN | sc->ich_cmd);
26166703Sarchie	smb_error = ichsmb_wait(sc);
26272200Sbmilekic	mtx_unlock(&sc->mutex);
26366703Sarchie	DBG("smb_error=%d\n", smb_error);
26466703Sarchie	return (smb_error);
26566703Sarchie}
26666703Sarchie
26766703Sarchieint
26866703Sarchieichsmb_writew(device_t dev, u_char slave, char cmd, short word)
26966703Sarchie{
27066703Sarchie	const sc_p sc = device_get_softc(dev);
27166703Sarchie	int smb_error;
27266703Sarchie
27366703Sarchie	DBG("slave=0x%02x cmd=0x%02x word=0x%04x\n",
27466703Sarchie	    slave, (u_char)cmd, (u_int16_t)word);
27566703Sarchie	KASSERT(sc->ich_cmd == -1,
27687599Sobrien	    ("%s: ich_cmd=%d\n", __func__ , sc->ich_cmd));
27772200Sbmilekic	mtx_lock(&sc->mutex);
27866703Sarchie	sc->ich_cmd = ICH_HST_CNT_SMB_CMD_WORD_DATA;
279179622Sjhb	bus_write_1(sc->io_res, ICH_XMIT_SLVA,
280188077Sjhb	    slave | ICH_XMIT_SLVA_WRITE);
281179622Sjhb	bus_write_1(sc->io_res, ICH_HST_CMD, cmd);
282179622Sjhb	bus_write_1(sc->io_res, ICH_D0, word & 0xff);
283179622Sjhb	bus_write_1(sc->io_res, ICH_D1, word >> 8);
284179622Sjhb	bus_write_1(sc->io_res, ICH_HST_CNT,
28566703Sarchie	    ICH_HST_CNT_START | ICH_HST_CNT_INTREN | sc->ich_cmd);
28666703Sarchie	smb_error = ichsmb_wait(sc);
28772200Sbmilekic	mtx_unlock(&sc->mutex);
28866703Sarchie	DBG("smb_error=%d\n", smb_error);
28966703Sarchie	return (smb_error);
29066703Sarchie}
29166703Sarchie
29266703Sarchieint
29366703Sarchieichsmb_readb(device_t dev, u_char slave, char cmd, char *byte)
29466703Sarchie{
29566703Sarchie	const sc_p sc = device_get_softc(dev);
29666703Sarchie	int smb_error;
29766703Sarchie
29866703Sarchie	DBG("slave=0x%02x cmd=0x%02x\n", slave, (u_char)cmd);
29966703Sarchie	KASSERT(sc->ich_cmd == -1,
30087599Sobrien	    ("%s: ich_cmd=%d\n", __func__ , sc->ich_cmd));
30172200Sbmilekic	mtx_lock(&sc->mutex);
30266703Sarchie	sc->ich_cmd = ICH_HST_CNT_SMB_CMD_BYTE_DATA;
303179622Sjhb	bus_write_1(sc->io_res, ICH_XMIT_SLVA,
304188077Sjhb	    slave | ICH_XMIT_SLVA_READ);
305179622Sjhb	bus_write_1(sc->io_res, ICH_HST_CMD, cmd);
306179622Sjhb	bus_write_1(sc->io_res, ICH_HST_CNT,
30766703Sarchie	    ICH_HST_CNT_START | ICH_HST_CNT_INTREN | sc->ich_cmd);
30866703Sarchie	if ((smb_error = ichsmb_wait(sc)) == SMB_ENOERR)
309179622Sjhb		*byte = bus_read_1(sc->io_res, ICH_D0);
31072200Sbmilekic	mtx_unlock(&sc->mutex);
31166703Sarchie	DBG("smb_error=%d byte=0x%02x\n", smb_error, (u_char)*byte);
31266703Sarchie	return (smb_error);
31366703Sarchie}
31466703Sarchie
31566703Sarchieint
31666703Sarchieichsmb_readw(device_t dev, u_char slave, char cmd, short *word)
31766703Sarchie{
31866703Sarchie	const sc_p sc = device_get_softc(dev);
31966703Sarchie	int smb_error;
32066703Sarchie
32166703Sarchie	DBG("slave=0x%02x cmd=0x%02x\n", slave, (u_char)cmd);
32266703Sarchie	KASSERT(sc->ich_cmd == -1,
32387599Sobrien	    ("%s: ich_cmd=%d\n", __func__ , sc->ich_cmd));
32472200Sbmilekic	mtx_lock(&sc->mutex);
32566703Sarchie	sc->ich_cmd = ICH_HST_CNT_SMB_CMD_WORD_DATA;
326179622Sjhb	bus_write_1(sc->io_res, ICH_XMIT_SLVA,
327188077Sjhb	    slave | ICH_XMIT_SLVA_READ);
328179622Sjhb	bus_write_1(sc->io_res, ICH_HST_CMD, cmd);
329179622Sjhb	bus_write_1(sc->io_res, ICH_HST_CNT,
33066703Sarchie	    ICH_HST_CNT_START | ICH_HST_CNT_INTREN | sc->ich_cmd);
33166703Sarchie	if ((smb_error = ichsmb_wait(sc)) == SMB_ENOERR) {
332179622Sjhb		*word = (bus_read_1(sc->io_res,
333179622Sjhb			ICH_D0) & 0xff)
334179622Sjhb		  | (bus_read_1(sc->io_res,
335179622Sjhb			ICH_D1) << 8);
33666703Sarchie	}
33772200Sbmilekic	mtx_unlock(&sc->mutex);
33866703Sarchie	DBG("smb_error=%d word=0x%04x\n", smb_error, (u_int16_t)*word);
33966703Sarchie	return (smb_error);
34066703Sarchie}
34166703Sarchie
34266703Sarchieint
34366703Sarchieichsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata)
34466703Sarchie{
34566703Sarchie	const sc_p sc = device_get_softc(dev);
34666703Sarchie	int smb_error;
34766703Sarchie
34866703Sarchie	DBG("slave=0x%02x cmd=0x%02x sdata=0x%04x\n",
34966703Sarchie	    slave, (u_char)cmd, (u_int16_t)sdata);
35066703Sarchie	KASSERT(sc->ich_cmd == -1,
35187599Sobrien	    ("%s: ich_cmd=%d\n", __func__ , sc->ich_cmd));
35272200Sbmilekic	mtx_lock(&sc->mutex);
35366703Sarchie	sc->ich_cmd = ICH_HST_CNT_SMB_CMD_PROC_CALL;
354179622Sjhb	bus_write_1(sc->io_res, ICH_XMIT_SLVA,
355188077Sjhb	    slave | ICH_XMIT_SLVA_WRITE);
356179622Sjhb	bus_write_1(sc->io_res, ICH_HST_CMD, cmd);
357179622Sjhb	bus_write_1(sc->io_res, ICH_D0, sdata & 0xff);
358179622Sjhb	bus_write_1(sc->io_res, ICH_D1, sdata >> 8);
359179622Sjhb	bus_write_1(sc->io_res, ICH_HST_CNT,
36066703Sarchie	    ICH_HST_CNT_START | ICH_HST_CNT_INTREN | sc->ich_cmd);
36166703Sarchie	if ((smb_error = ichsmb_wait(sc)) == SMB_ENOERR) {
362179622Sjhb		*rdata = (bus_read_1(sc->io_res,
363179622Sjhb			ICH_D0) & 0xff)
364179622Sjhb		  | (bus_read_1(sc->io_res,
365179622Sjhb			ICH_D1) << 8);
36666703Sarchie	}
36772200Sbmilekic	mtx_unlock(&sc->mutex);
36866703Sarchie	DBG("smb_error=%d rdata=0x%04x\n", smb_error, (u_int16_t)*rdata);
36966703Sarchie	return (smb_error);
37066703Sarchie}
37166703Sarchie
37266703Sarchieint
37366703Sarchieichsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf)
37466703Sarchie{
37566703Sarchie	const sc_p sc = device_get_softc(dev);
37666703Sarchie	int smb_error;
37766703Sarchie
37866703Sarchie	DBG("slave=0x%02x cmd=0x%02x count=%d\n", slave, (u_char)cmd, count);
37966703Sarchie#if ICHSMB_DEBUG
38066703Sarchie#define DISP(ch)	(((ch) < 0x20 || (ch) >= 0x7e) ? '.' : (ch))
38166703Sarchie	{
38266703Sarchie	    u_char *p;
38366703Sarchie
38466703Sarchie	    for (p = (u_char *)buf; p - (u_char *)buf < 32; p += 8) {
38566703Sarchie		DBG("%02x: %02x %02x %02x %02x %02x %02x %02x %02x"
38666703Sarchie		    "  %c%c%c%c%c%c%c%c", (p - (u_char *)buf),
38766703Sarchie		    p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7],
38866703Sarchie		    DISP(p[0]), DISP(p[1]), DISP(p[2]), DISP(p[3]),
38966703Sarchie		    DISP(p[4]), DISP(p[5]), DISP(p[6]), DISP(p[7]));
39066703Sarchie	    }
39166703Sarchie	}
39266703Sarchie#undef DISP
39366703Sarchie#endif
39466703Sarchie	KASSERT(sc->ich_cmd == -1,
39587599Sobrien	    ("%s: ich_cmd=%d\n", __func__ , sc->ich_cmd));
39666703Sarchie	if (count < 1 || count > 32)
397162234Sjhb		return (SMB_EINVAL);
39866703Sarchie	bcopy(buf, sc->block_data, count);
39966703Sarchie	sc->block_count = count;
40066703Sarchie	sc->block_index = 1;
40166703Sarchie	sc->block_write = 1;
40266703Sarchie
40372200Sbmilekic	mtx_lock(&sc->mutex);
40466703Sarchie	sc->ich_cmd = ICH_HST_CNT_SMB_CMD_BLOCK;
405179622Sjhb	bus_write_1(sc->io_res, ICH_XMIT_SLVA,
406188077Sjhb	    slave | ICH_XMIT_SLVA_WRITE);
407179622Sjhb	bus_write_1(sc->io_res, ICH_HST_CMD, cmd);
408179622Sjhb	bus_write_1(sc->io_res, ICH_D0, count);
409179622Sjhb	bus_write_1(sc->io_res, ICH_BLOCK_DB, buf[0]);
410179622Sjhb	bus_write_1(sc->io_res, ICH_HST_CNT,
41166703Sarchie	    ICH_HST_CNT_START | ICH_HST_CNT_INTREN | sc->ich_cmd);
41266703Sarchie	smb_error = ichsmb_wait(sc);
41372200Sbmilekic	mtx_unlock(&sc->mutex);
41466703Sarchie	DBG("smb_error=%d\n", smb_error);
41566703Sarchie	return (smb_error);
41666703Sarchie}
41766703Sarchie
41866703Sarchieint
419162234Sjhbichsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf)
42066703Sarchie{
42166703Sarchie	const sc_p sc = device_get_softc(dev);
42266703Sarchie	int smb_error;
42366703Sarchie
42466703Sarchie	DBG("slave=0x%02x cmd=0x%02x count=%d\n", slave, (u_char)cmd, count);
42566703Sarchie	KASSERT(sc->ich_cmd == -1,
42687599Sobrien	    ("%s: ich_cmd=%d\n", __func__ , sc->ich_cmd));
427162234Sjhb	if (*count < 1 || *count > 32)
428162234Sjhb		return (SMB_EINVAL);
42966703Sarchie	bzero(sc->block_data, sizeof(sc->block_data));
430162234Sjhb	sc->block_count = 0;
43166703Sarchie	sc->block_index = 0;
43266703Sarchie	sc->block_write = 0;
43366703Sarchie
43472200Sbmilekic	mtx_lock(&sc->mutex);
43566703Sarchie	sc->ich_cmd = ICH_HST_CNT_SMB_CMD_BLOCK;
436179622Sjhb	bus_write_1(sc->io_res, ICH_XMIT_SLVA,
437188077Sjhb	    slave | ICH_XMIT_SLVA_READ);
438179622Sjhb	bus_write_1(sc->io_res, ICH_HST_CMD, cmd);
439179622Sjhb	bus_write_1(sc->io_res, ICH_D0, *count); /* XXX? */
440179622Sjhb	bus_write_1(sc->io_res, ICH_HST_CNT,
44166703Sarchie	    ICH_HST_CNT_START | ICH_HST_CNT_INTREN | sc->ich_cmd);
442162234Sjhb	if ((smb_error = ichsmb_wait(sc)) == SMB_ENOERR) {
443162234Sjhb		bcopy(sc->block_data, buf, min(sc->block_count, *count));
444162234Sjhb		*count = sc->block_count;
445162234Sjhb	}
44672200Sbmilekic	mtx_unlock(&sc->mutex);
44766703Sarchie	DBG("smb_error=%d\n", smb_error);
44866703Sarchie#if ICHSMB_DEBUG
44966703Sarchie#define DISP(ch)	(((ch) < 0x20 || (ch) >= 0x7e) ? '.' : (ch))
45066703Sarchie	{
45166703Sarchie	    u_char *p;
45266703Sarchie
45366703Sarchie	    for (p = (u_char *)buf; p - (u_char *)buf < 32; p += 8) {
45466703Sarchie		DBG("%02x: %02x %02x %02x %02x %02x %02x %02x %02x"
45566703Sarchie		    "  %c%c%c%c%c%c%c%c", (p - (u_char *)buf),
45666703Sarchie		    p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7],
45766703Sarchie		    DISP(p[0]), DISP(p[1]), DISP(p[2]), DISP(p[3]),
45866703Sarchie		    DISP(p[4]), DISP(p[5]), DISP(p[6]), DISP(p[7]));
45966703Sarchie	    }
46066703Sarchie	}
46166703Sarchie#undef DISP
46266703Sarchie#endif
46366703Sarchie	return (smb_error);
46466703Sarchie}
46566703Sarchie
46666703Sarchie/********************************************************************
46766703Sarchie			OTHER FUNCTIONS
46866703Sarchie********************************************************************/
46966703Sarchie
47066703Sarchie/*
47166703Sarchie * This table describes what interrupts we should ever expect to
47266703Sarchie * see after each ICH command, not including the SMBALERT interrupt.
47366703Sarchie */
47466703Sarchiestatic const u_int8_t ichsmb_state_irqs[] = {
47566703Sarchie	/* quick */
47666703Sarchie	(ICH_HST_STA_BUS_ERR | ICH_HST_STA_DEV_ERR | ICH_HST_STA_INTR),
47766703Sarchie	/* byte */
47866703Sarchie	(ICH_HST_STA_BUS_ERR | ICH_HST_STA_DEV_ERR | ICH_HST_STA_INTR),
47966703Sarchie	/* byte data */
48066703Sarchie	(ICH_HST_STA_BUS_ERR | ICH_HST_STA_DEV_ERR | ICH_HST_STA_INTR),
48166703Sarchie	/* word data */
48266703Sarchie	(ICH_HST_STA_BUS_ERR | ICH_HST_STA_DEV_ERR | ICH_HST_STA_INTR),
48366703Sarchie	/* process call */
48466703Sarchie	(ICH_HST_STA_BUS_ERR | ICH_HST_STA_DEV_ERR | ICH_HST_STA_INTR),
48566703Sarchie	/* block */
48666703Sarchie	(ICH_HST_STA_BUS_ERR | ICH_HST_STA_DEV_ERR | ICH_HST_STA_INTR
48766703Sarchie	    | ICH_HST_STA_BYTE_DONE_STS),
48866703Sarchie	/* i2c read (not used) */
48966703Sarchie	(ICH_HST_STA_BUS_ERR | ICH_HST_STA_DEV_ERR | ICH_HST_STA_INTR
49066703Sarchie	    | ICH_HST_STA_BYTE_DONE_STS)
49166703Sarchie};
49266703Sarchie
49366703Sarchie/*
49466703Sarchie * Interrupt handler. This handler is bus-independent. Note that our
49566703Sarchie * interrupt may be shared, so we must handle "false" interrupts.
49666703Sarchie */
49766703Sarchievoid
49866703Sarchieichsmb_device_intr(void *cookie)
49966703Sarchie{
50066703Sarchie	const sc_p sc = cookie;
50166703Sarchie	const device_t dev = sc->dev;
50266703Sarchie	const int maxloops = 16;
50366703Sarchie	u_int8_t status;
50466703Sarchie	u_int8_t ok_bits;
50566703Sarchie	int cmd_index;
50666703Sarchie        int count;
50766703Sarchie
50872200Sbmilekic	mtx_lock(&sc->mutex);
50966703Sarchie	for (count = 0; count < maxloops; count++) {
51066703Sarchie
51166703Sarchie		/* Get and reset status bits */
512179622Sjhb		status = bus_read_1(sc->io_res, ICH_HST_STA);
51366703Sarchie#if ICHSMB_DEBUG
51466703Sarchie		if ((status & ~(ICH_HST_STA_INUSE_STS | ICH_HST_STA_HOST_BUSY))
51566703Sarchie		    || count > 0) {
51666703Sarchie			DBG("%d stat=0x%02x\n", count, status);
51766703Sarchie		}
51866703Sarchie#endif
51966703Sarchie		status &= ~(ICH_HST_STA_INUSE_STS | ICH_HST_STA_HOST_BUSY);
52066703Sarchie		if (status == 0)
52166703Sarchie			break;
52266703Sarchie
52366703Sarchie		/* Check for unexpected interrupt */
52466703Sarchie		ok_bits = ICH_HST_STA_SMBALERT_STS;
52566703Sarchie		cmd_index = sc->ich_cmd >> 2;
52666703Sarchie		if (sc->ich_cmd != -1) {
52766703Sarchie			KASSERT(cmd_index < sizeof(ichsmb_state_irqs),
52866703Sarchie			    ("%s: ich_cmd=%d", device_get_nameunit(dev),
52966703Sarchie			    sc->ich_cmd));
53066703Sarchie			ok_bits |= ichsmb_state_irqs[cmd_index];
53166703Sarchie		}
53266703Sarchie		if ((status & ~ok_bits) != 0) {
533165951Sjhb			device_printf(dev, "irq 0x%02x during %d\n", status,
534165951Sjhb			    cmd_index);
535179622Sjhb			bus_write_1(sc->io_res,
53666703Sarchie			    ICH_HST_STA, (status & ~ok_bits));
53766703Sarchie			continue;
53866703Sarchie		}
53966703Sarchie
54066703Sarchie		/* Handle SMBALERT interrupt */
54166703Sarchie		if (status & ICH_HST_STA_SMBALERT_STS) {
54266703Sarchie			static int smbalert_count = 16;
54366703Sarchie			if (smbalert_count > 0) {
544165951Sjhb				device_printf(dev, "SMBALERT# rec'd\n");
54566703Sarchie				if (--smbalert_count == 0) {
546165951Sjhb					device_printf(dev,
547165951Sjhb					    "not logging anymore\n");
54866703Sarchie				}
54966703Sarchie			}
55066703Sarchie		}
55166703Sarchie
55266703Sarchie		/* Check for bus error */
55366703Sarchie		if (status & ICH_HST_STA_BUS_ERR) {
55466703Sarchie			sc->smb_error = SMB_ECOLLI;	/* XXX SMB_EBUSERR? */
55566703Sarchie			goto finished;
55666703Sarchie		}
55766703Sarchie
55866703Sarchie		/* Check for device error */
55966703Sarchie		if (status & ICH_HST_STA_DEV_ERR) {
56066703Sarchie			sc->smb_error = SMB_ENOACK;	/* or SMB_ETIMEOUT? */
56166703Sarchie			goto finished;
56266703Sarchie		}
56366703Sarchie
56466703Sarchie		/* Check for byte completion in block transfer */
56566703Sarchie		if (status & ICH_HST_STA_BYTE_DONE_STS) {
56666703Sarchie			if (sc->block_write) {
56766703Sarchie				if (sc->block_index < sc->block_count) {
56866703Sarchie
56966703Sarchie					/* Write next byte */
570179622Sjhb					bus_write_1(sc->io_res,
571179622Sjhb					    ICH_BLOCK_DB,
57266703Sarchie					    sc->block_data[sc->block_index++]);
57366703Sarchie				}
57466703Sarchie			} else {
57566703Sarchie
57666703Sarchie				/* First interrupt, get the count also */
57766703Sarchie				if (sc->block_index == 0) {
578179622Sjhb					sc->block_count = bus_read_1(
579179622Sjhb					    sc->io_res, ICH_D0);
58066703Sarchie				}
58166703Sarchie
58266703Sarchie				/* Get next byte, if any */
58366703Sarchie				if (sc->block_index < sc->block_count) {
58466703Sarchie
58566703Sarchie					/* Read next byte */
58666703Sarchie					sc->block_data[sc->block_index++] =
587179622Sjhb					    bus_read_1(sc->io_res,
588179622Sjhb					      ICH_BLOCK_DB);
58966703Sarchie
59066703Sarchie					/* Set "LAST_BYTE" bit before reading
59166703Sarchie					   the last byte of block data */
59266703Sarchie					if (sc->block_index
59366703Sarchie					    >= sc->block_count - 1) {
594179622Sjhb						bus_write_1(sc->io_res,
595179622Sjhb						    ICH_HST_CNT,
59666703Sarchie						    ICH_HST_CNT_LAST_BYTE
59766703Sarchie							| ICH_HST_CNT_INTREN
59866703Sarchie							| sc->ich_cmd);
59966703Sarchie					}
60066703Sarchie				}
60166703Sarchie			}
60266703Sarchie		}
60366703Sarchie
60466703Sarchie		/* Check command completion */
60566703Sarchie		if (status & ICH_HST_STA_INTR) {
60666703Sarchie			sc->smb_error = SMB_ENOERR;
60766703Sarchiefinished:
60866703Sarchie			sc->ich_cmd = -1;
609179622Sjhb			bus_write_1(sc->io_res,
61066703Sarchie			    ICH_HST_STA, status);
61166703Sarchie			wakeup(sc);
61266703Sarchie			break;
61366703Sarchie		}
61466703Sarchie
61566703Sarchie		/* Clear status bits and try again */
616179622Sjhb		bus_write_1(sc->io_res, ICH_HST_STA, status);
61766703Sarchie	}
61872200Sbmilekic	mtx_unlock(&sc->mutex);
61966703Sarchie
62066703Sarchie	/* Too many loops? */
62166703Sarchie	if (count == maxloops) {
622165951Sjhb		device_printf(dev, "interrupt loop, status=0x%02x\n",
623179622Sjhb		    bus_read_1(sc->io_res, ICH_HST_STA));
62466703Sarchie	}
62566703Sarchie}
62666703Sarchie
62766703Sarchie/*
62869689Sarchie * Wait for command completion. Assumes mutex is held.
62966703Sarchie * Returns an SMB_* error code.
63066703Sarchie */
63166703Sarchiestatic int
63266703Sarchieichsmb_wait(sc_p sc)
63366703Sarchie{
63466703Sarchie	const device_t dev = sc->dev;
63566703Sarchie	int error, smb_error;
63666703Sarchie
63766703Sarchie	KASSERT(sc->ich_cmd != -1,
63887599Sobrien	    ("%s: ich_cmd=%d\n", __func__ , sc->ich_cmd));
63969689Sarchie	mtx_assert(&sc->mutex, MA_OWNED);
640153993Sbrian	error = msleep(sc, &sc->mutex, PZERO, "ichsmb", hz / 4);
64169689Sarchie	DBG("msleep -> %d\n", error);
64266703Sarchie	switch (error) {
64366703Sarchie	case 0:
64466703Sarchie		smb_error = sc->smb_error;
64566703Sarchie		break;
64666703Sarchie	case EWOULDBLOCK:
647165951Sjhb		device_printf(dev, "device timeout, status=0x%02x\n",
648179622Sjhb		    bus_read_1(sc->io_res, ICH_HST_STA));
64966703Sarchie		sc->ich_cmd = -1;
65066703Sarchie		smb_error = SMB_ETIMEOUT;
65166703Sarchie		break;
65266703Sarchie	default:
65366703Sarchie		smb_error = SMB_EABORT;
65466703Sarchie		break;
65566703Sarchie	}
65666703Sarchie	return (smb_error);
65766703Sarchie}
65866703Sarchie
65966703Sarchie/*
66066703Sarchie * Release resources associated with device.
66166703Sarchie */
66266703Sarchievoid
66366703Sarchieichsmb_release_resources(sc_p sc)
66466703Sarchie{
66566703Sarchie	const device_t dev = sc->dev;
66666703Sarchie
66766703Sarchie	if (sc->irq_handle != NULL) {
66866703Sarchie		bus_teardown_intr(dev, sc->irq_res, sc->irq_handle);
66966703Sarchie		sc->irq_handle = NULL;
67066703Sarchie	}
67166703Sarchie	if (sc->irq_res != NULL) {
67266703Sarchie		bus_release_resource(dev,
67366703Sarchie		    SYS_RES_IRQ, sc->irq_rid, sc->irq_res);
67466703Sarchie		sc->irq_res = NULL;
67566703Sarchie	}
67666703Sarchie	if (sc->io_res != NULL) {
67766703Sarchie		bus_release_resource(dev,
67866703Sarchie		    SYS_RES_IOPORT, sc->io_rid, sc->io_res);
67966703Sarchie		sc->io_res = NULL;
68066703Sarchie	}
68166703Sarchie}
68266703Sarchie
683162234Sjhbint
684162234Sjhbichsmb_detach(device_t dev)
685147253Stakawata{
686147253Stakawata	const sc_p sc = device_get_softc(dev);
687162234Sjhb	int error;
688148497Sbrian
689162234Sjhb	error = bus_generic_detach(dev);
690162234Sjhb	if (error)
691162234Sjhb		return (error);
692147253Stakawata	device_delete_child(dev, sc->smb);
693147253Stakawata	ichsmb_release_resources(sc);
694162234Sjhb	mtx_destroy(&sc->mutex);
695147253Stakawata
696147253Stakawata	return 0;
697147253Stakawata}
698162234Sjhb
699162234SjhbDRIVER_MODULE(smbus, ichsmb, smbus_driver, smbus_devclass, 0, 0);
700