1175533Sru/*-
2175533Sru * Copyright (c) 2005 Ruslan Ermilov
3175533Sru * All rights reserved.
4175533Sru *
5175533Sru * Redistribution and use in source and binary forms, with or without
6175533Sru * modification, are permitted provided that the following conditions
7175533Sru * are met:
8175533Sru * 1. Redistributions of source code must retain the above copyright
9175533Sru *    notice, this list of conditions and the following disclaimer.
10175533Sru * 2. Redistributions in binary form must reproduce the above copyright
11175533Sru *    notice, this list of conditions and the following disclaimer in the
12175533Sru *    documentation and/or other materials provided with the distribution.
13175533Sru *
14175533Sru * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15175533Sru * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16175533Sru * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17175533Sru * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18175533Sru * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19175533Sru * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20175533Sru * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21175533Sru * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22175533Sru * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23175533Sru * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24175533Sru * SUCH DAMAGE.
25175533Sru */
26175533Sru
27153618Sru#include <sys/cdefs.h>
28153618Sru__FBSDID("$FreeBSD$");
29153618Sru
30153618Sru#include <sys/param.h>
31165951Sjhb#include <sys/bus.h>
32153618Sru#include <sys/kernel.h>
33165951Sjhb#include <sys/lock.h>
34165951Sjhb#include <sys/module.h>
35165951Sjhb#include <sys/mutex.h>
36153618Sru#include <sys/systm.h>
37153618Sru
38153618Sru#include <machine/bus.h>
39153618Sru#include <machine/resource.h>
40153618Sru#include <sys/rman.h>
41153618Sru
42153618Sru#include <dev/pci/pcivar.h>
43153618Sru#include <dev/pci/pcireg.h>
44153618Sru
45153618Sru#include <dev/smbus/smbconf.h>
46153618Sru#include "smbus_if.h"
47153618Sru
48153618Sru#define	NFSMB_DEBUG(x)	if (nfsmb_debug) (x)
49153618Sru
50153618Sru#ifdef DEBUG
51153618Srustatic int nfsmb_debug = 1;
52153618Sru#else
53153618Srustatic int nfsmb_debug = 0;
54153618Sru#endif
55153618Sru
56153618Sru/* NVIDIA nForce2/3/4 MCP */
57153618Sru#define	NFSMB_VENDORID_NVIDIA		0x10de
58153618Sru#define	NFSMB_DEVICEID_NF2_SMB		0x0064
59153618Sru#define	NFSMB_DEVICEID_NF2_ULTRA_SMB	0x0084
60153618Sru#define	NFSMB_DEVICEID_NF3_PRO150_SMB	0x00d4
61153618Sru#define	NFSMB_DEVICEID_NF3_250GB_SMB	0x00e4
62153618Sru#define	NFSMB_DEVICEID_NF4_SMB		0x0052
63161450Sru#define	NFSMB_DEVICEID_NF4_04_SMB	0x0034
64161450Sru#define	NFSMB_DEVICEID_NF4_51_SMB	0x0264
65161450Sru#define	NFSMB_DEVICEID_NF4_55_SMB	0x0368
66173525Sremko#define	NFSMB_DEVICEID_NF4_61_SMB	0x03eb
67179860Sjoerg#define	NFSMB_DEVICEID_NF4_65_SMB	0x0446
68202931Sgavin#define	NFSMB_DEVICEID_NF4_67_SMB	0x0542
69202931Sgavin#define	NFSMB_DEVICEID_NF4_73_SMB	0x07d8
70202931Sgavin#define	NFSMB_DEVICEID_NF4_78S_SMB	0x0752
71202931Sgavin#define	NFSMB_DEVICEID_NF4_79_SMB	0x0aa2
72153618Sru
73153618Sru/* PCI Configuration space registers */
74153618Sru#define	NF2PCI_SMBASE_1		PCIR_BAR(4)
75153618Sru#define	NF2PCI_SMBASE_2		PCIR_BAR(5)
76153618Sru
77153618Sru/*
78153618Sru * ACPI 3.0, Chapter 12, SMBus Host Controller Interface.
79153618Sru */
80153618Sru#define	SMB_PRTCL		0x00	/* protocol */
81153618Sru#define	SMB_STS			0x01	/* status */
82153618Sru#define	SMB_ADDR		0x02	/* address */
83153618Sru#define	SMB_CMD			0x03	/* command */
84153618Sru#define	SMB_DATA		0x04	/* 32 data registers */
85153618Sru#define	SMB_BCNT		0x24	/* number of data bytes */
86153618Sru#define	SMB_ALRM_A		0x25	/* alarm address */
87153618Sru#define	SMB_ALRM_D		0x26	/* 2 bytes alarm data */
88153618Sru
89153618Sru#define	SMB_STS_DONE		0x80
90153618Sru#define	SMB_STS_ALRM		0x40
91153618Sru#define	SMB_STS_RES		0x20
92153618Sru#define	SMB_STS_STATUS		0x1f
93153618Sru#define	SMB_STS_OK		0x00	/* OK */
94153618Sru#define	SMB_STS_UF		0x07	/* Unknown Failure */
95153618Sru#define	SMB_STS_DANA		0x10	/* Device Address Not Acknowledged */
96153618Sru#define	SMB_STS_DED		0x11	/* Device Error Detected */
97153618Sru#define	SMB_STS_DCAD		0x12	/* Device Command Access Denied */
98153618Sru#define	SMB_STS_UE		0x13	/* Unknown Error */
99153618Sru#define	SMB_STS_DAD		0x17	/* Device Access Denied */
100153618Sru#define	SMB_STS_T		0x18	/* Timeout */
101153618Sru#define	SMB_STS_HUP		0x19	/* Host Unsupported Protocol */
102153618Sru#define	SMB_STS_B		0x1A	/* Busy */
103153618Sru#define	SMB_STS_PEC		0x1F	/* PEC (CRC-8) Error */
104153618Sru
105153618Sru#define	SMB_PRTCL_WRITE		0x00
106153618Sru#define	SMB_PRTCL_READ		0x01
107153618Sru#define	SMB_PRTCL_QUICK		0x02
108153618Sru#define	SMB_PRTCL_BYTE		0x04
109153618Sru#define	SMB_PRTCL_BYTE_DATA	0x06
110153618Sru#define	SMB_PRTCL_WORD_DATA	0x08
111153618Sru#define	SMB_PRTCL_BLOCK_DATA	0x0a
112153618Sru#define	SMB_PRTCL_PROC_CALL	0x0c
113153618Sru#define	SMB_PRTCL_BLOCK_PROC_CALL 0x0d
114153618Sru#define	SMB_PRTCL_PEC		0x80
115153618Sru
116153618Srustruct nfsmb_softc {
117153618Sru	int rid;
118153618Sru	struct resource *res;
119153618Sru	device_t smbus;
120153618Sru	device_t subdev;
121165951Sjhb	struct mtx lock;
122153618Sru};
123153618Sru
124165951Sjhb#define	NFSMB_LOCK(nfsmb)		mtx_lock(&(nfsmb)->lock)
125165951Sjhb#define	NFSMB_UNLOCK(nfsmb)		mtx_unlock(&(nfsmb)->lock)
126165951Sjhb#define	NFSMB_LOCK_ASSERT(nfsmb)	mtx_assert(&(nfsmb)->lock, MA_OWNED)
127165951Sjhb
128165951Sjhb#define	NFSMB_SMBINB(nfsmb, register)					\
129179622Sjhb	(bus_read_1(nfsmb->res, register))
130153618Sru#define	NFSMB_SMBOUTB(nfsmb, register, value) \
131179622Sjhb	(bus_write_1(nfsmb->res, register, value))
132153618Sru
133165951Sjhbstatic int	nfsmb_detach(device_t dev);
134165951Sjhbstatic int	nfsmbsub_detach(device_t dev);
135165951Sjhb
136153618Srustatic int
137153618Srunfsmbsub_probe(device_t dev)
138153618Sru{
139153618Sru
140153618Sru	device_set_desc(dev, "nForce2/3/4 MCP SMBus Controller");
141153618Sru	return (BUS_PROBE_DEFAULT);
142153618Sru}
143153618Sru
144153618Srustatic int
145153618Srunfsmb_probe(device_t dev)
146153618Sru{
147153618Sru	u_int16_t vid;
148153618Sru	u_int16_t did;
149153618Sru
150153618Sru	vid = pci_get_vendor(dev);
151153618Sru	did = pci_get_device(dev);
152153618Sru
153153618Sru	if (vid == NFSMB_VENDORID_NVIDIA) {
154153618Sru		switch(did) {
155153618Sru		case NFSMB_DEVICEID_NF2_SMB:
156153618Sru		case NFSMB_DEVICEID_NF2_ULTRA_SMB:
157153618Sru		case NFSMB_DEVICEID_NF3_PRO150_SMB:
158153618Sru		case NFSMB_DEVICEID_NF3_250GB_SMB:
159153618Sru		case NFSMB_DEVICEID_NF4_SMB:
160161450Sru		case NFSMB_DEVICEID_NF4_04_SMB:
161161450Sru		case NFSMB_DEVICEID_NF4_51_SMB:
162161450Sru		case NFSMB_DEVICEID_NF4_55_SMB:
163173525Sremko		case NFSMB_DEVICEID_NF4_61_SMB:
164179860Sjoerg		case NFSMB_DEVICEID_NF4_65_SMB:
165202931Sgavin		case NFSMB_DEVICEID_NF4_67_SMB:
166202931Sgavin		case NFSMB_DEVICEID_NF4_73_SMB:
167202931Sgavin		case NFSMB_DEVICEID_NF4_78S_SMB:
168202931Sgavin		case NFSMB_DEVICEID_NF4_79_SMB:
169153618Sru			device_set_desc(dev, "nForce2/3/4 MCP SMBus Controller");
170153618Sru			return (BUS_PROBE_DEFAULT);
171153618Sru		}
172153618Sru	}
173153618Sru
174153618Sru	return (ENXIO);
175153618Sru}
176153618Sru
177153618Srustatic int
178153618Srunfsmbsub_attach(device_t dev)
179153618Sru{
180153618Sru	device_t parent;
181153618Sru	struct nfsmb_softc *nfsmbsub_sc = device_get_softc(dev);
182153618Sru
183153618Sru	parent = device_get_parent(dev);
184153618Sru
185153618Sru	nfsmbsub_sc->rid = NF2PCI_SMBASE_2;
186153618Sru
187153618Sru	nfsmbsub_sc->res = bus_alloc_resource_any(parent, SYS_RES_IOPORT,
188153618Sru	    &nfsmbsub_sc->rid, RF_ACTIVE);
189153618Sru	if (nfsmbsub_sc->res == NULL) {
190163459Sru		/* Older incarnations of the device used non-standard BARs. */
191163459Sru		nfsmbsub_sc->rid = 0x54;
192163459Sru		nfsmbsub_sc->res = bus_alloc_resource_any(parent,
193163459Sru		    SYS_RES_IOPORT, &nfsmbsub_sc->rid, RF_ACTIVE);
194163459Sru		if (nfsmbsub_sc->res == NULL) {
195163459Sru			device_printf(dev, "could not map i/o space\n");
196163459Sru			return (ENXIO);
197163459Sru		}
198153618Sru	}
199165951Sjhb	mtx_init(&nfsmbsub_sc->lock, device_get_nameunit(dev), "nfsmb",
200165951Sjhb	    MTX_DEF);
201153618Sru
202153618Sru	nfsmbsub_sc->smbus = device_add_child(dev, "smbus", -1);
203165951Sjhb	if (nfsmbsub_sc->smbus == NULL) {
204165951Sjhb		nfsmbsub_detach(dev);
205153618Sru		return (EINVAL);
206165951Sjhb	}
207153618Sru
208153618Sru	bus_generic_attach(dev);
209153618Sru
210153618Sru	return (0);
211153618Sru}
212153618Sru
213153618Srustatic int
214153618Srunfsmb_attach(device_t dev)
215153618Sru{
216153618Sru	struct nfsmb_softc *nfsmb_sc = device_get_softc(dev);
217153618Sru
218153618Sru	/* Allocate I/O space */
219153618Sru	nfsmb_sc->rid = NF2PCI_SMBASE_1;
220153618Sru
221153618Sru	nfsmb_sc->res = bus_alloc_resource_any(dev, SYS_RES_IOPORT,
222153618Sru		&nfsmb_sc->rid, RF_ACTIVE);
223153618Sru
224153618Sru	if (nfsmb_sc->res == NULL) {
225163459Sru		/* Older incarnations of the device used non-standard BARs. */
226163459Sru		nfsmb_sc->rid = 0x50;
227163459Sru		nfsmb_sc->res = bus_alloc_resource_any(dev,
228163459Sru		    SYS_RES_IOPORT, &nfsmb_sc->rid, RF_ACTIVE);
229163459Sru		if (nfsmb_sc->res == NULL) {
230163459Sru			device_printf(dev, "could not map i/o space\n");
231163459Sru			return (ENXIO);
232163459Sru		}
233153618Sru	}
234153618Sru
235165951Sjhb	mtx_init(&nfsmb_sc->lock, device_get_nameunit(dev), "nfsmb", MTX_DEF);
236153618Sru
237153618Sru	/* Allocate a new smbus device */
238153618Sru	nfsmb_sc->smbus = device_add_child(dev, "smbus", -1);
239165951Sjhb	if (!nfsmb_sc->smbus) {
240165951Sjhb		nfsmb_detach(dev);
241153618Sru		return (EINVAL);
242165951Sjhb	}
243153618Sru
244153618Sru	nfsmb_sc->subdev = NULL;
245153618Sru	switch (pci_get_device(dev)) {
246153618Sru	case NFSMB_DEVICEID_NF2_SMB:
247153618Sru	case NFSMB_DEVICEID_NF2_ULTRA_SMB:
248153618Sru	case NFSMB_DEVICEID_NF3_PRO150_SMB:
249153618Sru	case NFSMB_DEVICEID_NF3_250GB_SMB:
250153618Sru	case NFSMB_DEVICEID_NF4_SMB:
251161450Sru	case NFSMB_DEVICEID_NF4_04_SMB:
252161450Sru	case NFSMB_DEVICEID_NF4_51_SMB:
253161450Sru	case NFSMB_DEVICEID_NF4_55_SMB:
254173525Sremko	case NFSMB_DEVICEID_NF4_61_SMB:
255179860Sjoerg	case NFSMB_DEVICEID_NF4_65_SMB:
256202931Sgavin	case NFSMB_DEVICEID_NF4_67_SMB:
257202931Sgavin	case NFSMB_DEVICEID_NF4_73_SMB:
258202931Sgavin	case NFSMB_DEVICEID_NF4_78S_SMB:
259202931Sgavin	case NFSMB_DEVICEID_NF4_79_SMB:
260153618Sru		/* Trying to add secondary device as slave */
261153618Sru		nfsmb_sc->subdev = device_add_child(dev, "nfsmb", -1);
262165951Sjhb		if (!nfsmb_sc->subdev) {
263165951Sjhb			nfsmb_detach(dev);
264153618Sru			return (EINVAL);
265165951Sjhb		}
266153618Sru		break;
267153618Sru	default:
268153618Sru		break;
269153618Sru	}
270153618Sru
271153618Sru	bus_generic_attach(dev);
272153618Sru
273153618Sru	return (0);
274153618Sru}
275153618Sru
276153618Srustatic int
277153618Srunfsmbsub_detach(device_t dev)
278153618Sru{
279153618Sru	device_t parent;
280153618Sru	struct nfsmb_softc *nfsmbsub_sc = device_get_softc(dev);
281153618Sru
282153618Sru	parent = device_get_parent(dev);
283153618Sru
284153618Sru	if (nfsmbsub_sc->smbus) {
285153618Sru		device_delete_child(dev, nfsmbsub_sc->smbus);
286153618Sru		nfsmbsub_sc->smbus = NULL;
287153618Sru	}
288165951Sjhb	mtx_destroy(&nfsmbsub_sc->lock);
289153618Sru	if (nfsmbsub_sc->res) {
290153618Sru		bus_release_resource(parent, SYS_RES_IOPORT, nfsmbsub_sc->rid,
291153618Sru		    nfsmbsub_sc->res);
292153618Sru		nfsmbsub_sc->res = NULL;
293153618Sru	}
294153618Sru	return (0);
295153618Sru}
296153618Sru
297153618Srustatic int
298153618Srunfsmb_detach(device_t dev)
299153618Sru{
300153618Sru	struct nfsmb_softc *nfsmb_sc = device_get_softc(dev);
301153618Sru
302153618Sru	if (nfsmb_sc->subdev) {
303153618Sru		device_delete_child(dev, nfsmb_sc->subdev);
304153618Sru		nfsmb_sc->subdev = NULL;
305153618Sru	}
306153618Sru
307153618Sru	if (nfsmb_sc->smbus) {
308153618Sru		device_delete_child(dev, nfsmb_sc->smbus);
309153618Sru		nfsmb_sc->smbus = NULL;
310153618Sru	}
311153618Sru
312165951Sjhb	mtx_destroy(&nfsmb_sc->lock);
313153618Sru	if (nfsmb_sc->res) {
314153618Sru		bus_release_resource(dev, SYS_RES_IOPORT, nfsmb_sc->rid,
315153618Sru		    nfsmb_sc->res);
316153618Sru		nfsmb_sc->res = NULL;
317153618Sru	}
318153618Sru
319153618Sru	return (0);
320153618Sru}
321153618Sru
322153618Srustatic int
323162234Sjhbnfsmb_callback(device_t dev, int index, void *data)
324153618Sru{
325153618Sru	int error = 0;
326153618Sru
327153618Sru	switch (index) {
328153618Sru	case SMB_REQUEST_BUS:
329153618Sru	case SMB_RELEASE_BUS:
330153618Sru		break;
331153618Sru	default:
332153618Sru		error = EINVAL;
333153618Sru	}
334153618Sru
335153618Sru	return (error);
336153618Sru}
337153618Sru
338153618Srustatic int
339153618Srunfsmb_wait(struct nfsmb_softc *sc)
340153618Sru{
341153618Sru	u_char sts;
342153618Sru	int error, count;
343153618Sru
344165951Sjhb	NFSMB_LOCK_ASSERT(sc);
345153618Sru	if (NFSMB_SMBINB(sc, SMB_PRTCL) != 0)
346153618Sru	{
347153618Sru		count = 10000;
348153618Sru		do {
349153618Sru			DELAY(500);
350153618Sru		} while (NFSMB_SMBINB(sc, SMB_PRTCL) != 0 && count--);
351153618Sru		if (count == 0)
352153618Sru			return (SMB_ETIMEOUT);
353153618Sru	}
354153618Sru
355153618Sru	sts = NFSMB_SMBINB(sc, SMB_STS) & SMB_STS_STATUS;
356153618Sru	NFSMB_DEBUG(printf("nfsmb: STS=0x%x\n", sts));
357153618Sru
358153618Sru	switch (sts) {
359153618Sru	case SMB_STS_OK:
360153618Sru		error = SMB_ENOERR;
361153618Sru		break;
362153618Sru	case SMB_STS_DANA:
363153618Sru		error = SMB_ENOACK;
364153618Sru		break;
365153618Sru	case SMB_STS_B:
366153618Sru		error = SMB_EBUSY;
367153618Sru		break;
368153618Sru	case SMB_STS_T:
369153618Sru		error = SMB_ETIMEOUT;
370153618Sru		break;
371153618Sru	case SMB_STS_DCAD:
372153618Sru	case SMB_STS_DAD:
373153618Sru	case SMB_STS_HUP:
374153618Sru		error = SMB_ENOTSUPP;
375153618Sru		break;
376153618Sru	default:
377153618Sru		error = SMB_EBUSERR;
378153618Sru		break;
379153618Sru	}
380153618Sru
381153618Sru	return (error);
382153618Sru}
383153618Sru
384153618Srustatic int
385153618Srunfsmb_quick(device_t dev, u_char slave, int how)
386153618Sru{
387153618Sru	struct nfsmb_softc *sc = (struct nfsmb_softc *)device_get_softc(dev);
388153618Sru	u_char protocol;
389153618Sru	int error;
390153618Sru
391153618Sru	protocol = SMB_PRTCL_QUICK;
392153618Sru
393153618Sru	switch (how) {
394153618Sru	case SMB_QWRITE:
395153618Sru		protocol |= SMB_PRTCL_WRITE;
396153618Sru		NFSMB_DEBUG(printf("nfsmb: QWRITE to 0x%x", slave));
397153618Sru		break;
398153618Sru	case SMB_QREAD:
399153618Sru		protocol |= SMB_PRTCL_READ;
400153618Sru		NFSMB_DEBUG(printf("nfsmb: QREAD to 0x%x", slave));
401153618Sru		break;
402153618Sru	default:
403153618Sru		panic("%s: unknown QUICK command (%x)!", __func__, how);
404153618Sru	}
405153618Sru
406165951Sjhb	NFSMB_LOCK(sc);
407153618Sru	NFSMB_SMBOUTB(sc, SMB_ADDR, slave);
408153618Sru	NFSMB_SMBOUTB(sc, SMB_PRTCL, protocol);
409153618Sru
410153618Sru	error = nfsmb_wait(sc);
411153618Sru
412153618Sru	NFSMB_DEBUG(printf(", error=0x%x\n", error));
413165951Sjhb	NFSMB_UNLOCK(sc);
414153618Sru
415153618Sru	return (error);
416153618Sru}
417153618Sru
418153618Srustatic int
419153618Srunfsmb_sendb(device_t dev, u_char slave, char byte)
420153618Sru{
421153618Sru	struct nfsmb_softc *sc = (struct nfsmb_softc *)device_get_softc(dev);
422153618Sru	int error;
423153618Sru
424165951Sjhb	NFSMB_LOCK(sc);
425153618Sru	NFSMB_SMBOUTB(sc, SMB_CMD, byte);
426153618Sru	NFSMB_SMBOUTB(sc, SMB_ADDR, slave);
427153618Sru	NFSMB_SMBOUTB(sc, SMB_PRTCL, SMB_PRTCL_WRITE | SMB_PRTCL_BYTE);
428153618Sru
429153618Sru	error = nfsmb_wait(sc);
430153618Sru
431153618Sru	NFSMB_DEBUG(printf("nfsmb: SENDB to 0x%x, byte=0x%x, error=0x%x\n", slave, byte, error));
432165951Sjhb	NFSMB_UNLOCK(sc);
433153618Sru
434153618Sru	return (error);
435153618Sru}
436153618Sru
437153618Srustatic int
438153618Srunfsmb_recvb(device_t dev, u_char slave, char *byte)
439153618Sru{
440153618Sru	struct nfsmb_softc *sc = (struct nfsmb_softc *)device_get_softc(dev);
441153618Sru	int error;
442153618Sru
443165951Sjhb	NFSMB_LOCK(sc);
444153618Sru	NFSMB_SMBOUTB(sc, SMB_ADDR, slave);
445153618Sru	NFSMB_SMBOUTB(sc, SMB_PRTCL, SMB_PRTCL_READ | SMB_PRTCL_BYTE);
446153618Sru
447153618Sru	if ((error = nfsmb_wait(sc)) == SMB_ENOERR)
448153618Sru		*byte = NFSMB_SMBINB(sc, SMB_DATA);
449153618Sru
450153618Sru	NFSMB_DEBUG(printf("nfsmb: RECVB from 0x%x, byte=0x%x, error=0x%x\n", slave, *byte, error));
451165951Sjhb	NFSMB_UNLOCK(sc);
452153618Sru
453153618Sru	return (error);
454153618Sru}
455153618Sru
456153618Srustatic int
457153618Srunfsmb_writeb(device_t dev, u_char slave, char cmd, char byte)
458153618Sru{
459153618Sru	struct nfsmb_softc *sc = (struct nfsmb_softc *)device_get_softc(dev);
460153618Sru	int error;
461153618Sru
462165951Sjhb	NFSMB_LOCK(sc);
463153618Sru	NFSMB_SMBOUTB(sc, SMB_CMD, cmd);
464153618Sru	NFSMB_SMBOUTB(sc, SMB_DATA, byte);
465153618Sru	NFSMB_SMBOUTB(sc, SMB_ADDR, slave);
466153618Sru	NFSMB_SMBOUTB(sc, SMB_PRTCL, SMB_PRTCL_WRITE | SMB_PRTCL_BYTE_DATA);
467153618Sru
468153618Sru	error = nfsmb_wait(sc);
469153618Sru
470153618Sru	NFSMB_DEBUG(printf("nfsmb: WRITEB to 0x%x, cmd=0x%x, byte=0x%x, error=0x%x\n", slave, cmd, byte, error));
471165951Sjhb	NFSMB_UNLOCK(sc);
472153618Sru
473153618Sru	return (error);
474153618Sru}
475153618Sru
476153618Srustatic int
477153618Srunfsmb_readb(device_t dev, u_char slave, char cmd, char *byte)
478153618Sru{
479153618Sru	struct nfsmb_softc *sc = (struct nfsmb_softc *)device_get_softc(dev);
480153618Sru	int error;
481153618Sru
482165951Sjhb	NFSMB_LOCK(sc);
483153618Sru	NFSMB_SMBOUTB(sc, SMB_CMD, cmd);
484153618Sru	NFSMB_SMBOUTB(sc, SMB_ADDR, slave);
485153618Sru	NFSMB_SMBOUTB(sc, SMB_PRTCL, SMB_PRTCL_READ | SMB_PRTCL_BYTE_DATA);
486153618Sru
487153618Sru	if ((error = nfsmb_wait(sc)) == SMB_ENOERR)
488153618Sru		*byte = NFSMB_SMBINB(sc, SMB_DATA);
489153618Sru
490153618Sru	NFSMB_DEBUG(printf("nfsmb: READB from 0x%x, cmd=0x%x, byte=0x%x, error=0x%x\n", slave, cmd, (unsigned char)*byte, error));
491165951Sjhb	NFSMB_UNLOCK(sc);
492153618Sru
493153618Sru	return (error);
494153618Sru}
495153618Sru
496153618Srustatic int
497153618Srunfsmb_writew(device_t dev, u_char slave, char cmd, short word)
498153618Sru{
499153618Sru	struct nfsmb_softc *sc = (struct nfsmb_softc *)device_get_softc(dev);
500153618Sru	int error;
501153618Sru
502165951Sjhb	NFSMB_LOCK(sc);
503153618Sru	NFSMB_SMBOUTB(sc, SMB_CMD, cmd);
504153618Sru	NFSMB_SMBOUTB(sc, SMB_DATA, word);
505153618Sru	NFSMB_SMBOUTB(sc, SMB_DATA + 1, word >> 8);
506153618Sru	NFSMB_SMBOUTB(sc, SMB_ADDR, slave);
507153618Sru	NFSMB_SMBOUTB(sc, SMB_PRTCL, SMB_PRTCL_WRITE | SMB_PRTCL_WORD_DATA);
508153618Sru
509153618Sru	error = nfsmb_wait(sc);
510153618Sru
511153618Sru	NFSMB_DEBUG(printf("nfsmb: WRITEW to 0x%x, cmd=0x%x, word=0x%x, error=0x%x\n", slave, cmd, word, error));
512165951Sjhb	NFSMB_UNLOCK(sc);
513153618Sru
514153618Sru	return (error);
515153618Sru}
516153618Sru
517153618Srustatic int
518153618Srunfsmb_readw(device_t dev, u_char slave, char cmd, short *word)
519153618Sru{
520153618Sru	struct nfsmb_softc *sc = (struct nfsmb_softc *)device_get_softc(dev);
521153618Sru	int error;
522153618Sru
523165951Sjhb	NFSMB_LOCK(sc);
524153618Sru	NFSMB_SMBOUTB(sc, SMB_CMD, cmd);
525153618Sru	NFSMB_SMBOUTB(sc, SMB_ADDR, slave);
526153618Sru	NFSMB_SMBOUTB(sc, SMB_PRTCL, SMB_PRTCL_READ | SMB_PRTCL_WORD_DATA);
527153618Sru
528153618Sru	if ((error = nfsmb_wait(sc)) == SMB_ENOERR)
529153618Sru		*word = NFSMB_SMBINB(sc, SMB_DATA) |
530153618Sru		    (NFSMB_SMBINB(sc, SMB_DATA + 1) << 8);
531153618Sru
532153618Sru	NFSMB_DEBUG(printf("nfsmb: READW from 0x%x, cmd=0x%x, word=0x%x, error=0x%x\n", slave, cmd, (unsigned short)*word, error));
533165951Sjhb	NFSMB_UNLOCK(sc);
534153618Sru
535153618Sru	return (error);
536153618Sru}
537153618Sru
538153618Srustatic int
539153618Srunfsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf)
540153618Sru{
541153618Sru	struct nfsmb_softc *sc = (struct nfsmb_softc *)device_get_softc(dev);
542162234Sjhb	u_char i;
543153618Sru	int error;
544153618Sru
545162234Sjhb	if (count < 1 || count > 32)
546162234Sjhb		return (SMB_EINVAL);
547165951Sjhb
548165951Sjhb	NFSMB_LOCK(sc);
549153618Sru	NFSMB_SMBOUTB(sc, SMB_CMD, cmd);
550162234Sjhb	NFSMB_SMBOUTB(sc, SMB_BCNT, count);
551162234Sjhb	for (i = 0; i < count; i++)
552153618Sru		NFSMB_SMBOUTB(sc, SMB_DATA + i, buf[i]);
553153618Sru	NFSMB_SMBOUTB(sc, SMB_ADDR, slave);
554153618Sru	NFSMB_SMBOUTB(sc, SMB_PRTCL, SMB_PRTCL_WRITE | SMB_PRTCL_BLOCK_DATA);
555153618Sru
556153618Sru	error = nfsmb_wait(sc);
557153618Sru
558153618Sru	NFSMB_DEBUG(printf("nfsmb: WRITEBLK to 0x%x, count=0x%x, cmd=0x%x, error=0x%x", slave, count, cmd, error));
559165951Sjhb	NFSMB_UNLOCK(sc);
560153618Sru
561153618Sru	return (error);
562153618Sru}
563153618Sru
564153618Srustatic int
565162234Sjhbnfsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf)
566153618Sru{
567153618Sru	struct nfsmb_softc *sc = (struct nfsmb_softc *)device_get_softc(dev);
568162234Sjhb	u_char data, len, i;
569153618Sru	int error;
570153618Sru
571162234Sjhb	if (*count < 1 || *count > 32)
572162234Sjhb		return (SMB_EINVAL);
573165951Sjhb
574165951Sjhb	NFSMB_LOCK(sc);
575153618Sru	NFSMB_SMBOUTB(sc, SMB_CMD, cmd);
576153618Sru	NFSMB_SMBOUTB(sc, SMB_ADDR, slave);
577153618Sru	NFSMB_SMBOUTB(sc, SMB_PRTCL, SMB_PRTCL_READ | SMB_PRTCL_BLOCK_DATA);
578153618Sru
579153618Sru	if ((error = nfsmb_wait(sc)) == SMB_ENOERR) {
580153618Sru		len = NFSMB_SMBINB(sc, SMB_BCNT);
581162234Sjhb		for (i = 0; i < len; i++) {
582162234Sjhb			data = NFSMB_SMBINB(sc, SMB_DATA + i);
583162234Sjhb			if (i < *count)
584162234Sjhb				buf[i] = data;
585162234Sjhb		}
586162234Sjhb		*count = len;
587153618Sru	}
588153618Sru
589162234Sjhb	NFSMB_DEBUG(printf("nfsmb: READBLK to 0x%x, count=0x%x, cmd=0x%x, error=0x%x", slave, *count, cmd, error));
590165951Sjhb	NFSMB_UNLOCK(sc);
591153618Sru
592153618Sru	return (error);
593153618Sru}
594153618Sru
595153618Srustatic device_method_t nfsmb_methods[] = {
596153618Sru	/* Device interface */
597153618Sru	DEVMETHOD(device_probe,		nfsmb_probe),
598153618Sru	DEVMETHOD(device_attach,	nfsmb_attach),
599153618Sru	DEVMETHOD(device_detach,	nfsmb_detach),
600153618Sru
601153618Sru	/* SMBus interface */
602153618Sru	DEVMETHOD(smbus_callback,	nfsmb_callback),
603153618Sru	DEVMETHOD(smbus_quick,		nfsmb_quick),
604153618Sru	DEVMETHOD(smbus_sendb,		nfsmb_sendb),
605153618Sru	DEVMETHOD(smbus_recvb,		nfsmb_recvb),
606153618Sru	DEVMETHOD(smbus_writeb,		nfsmb_writeb),
607153618Sru	DEVMETHOD(smbus_readb,		nfsmb_readb),
608153618Sru	DEVMETHOD(smbus_writew,		nfsmb_writew),
609153618Sru	DEVMETHOD(smbus_readw,		nfsmb_readw),
610153618Sru	DEVMETHOD(smbus_bwrite,		nfsmb_bwrite),
611153618Sru	DEVMETHOD(smbus_bread,		nfsmb_bread),
612153618Sru
613153618Sru	{ 0, 0 }
614153618Sru};
615153618Sru
616153618Srustatic device_method_t nfsmbsub_methods[] = {
617153618Sru	/* Device interface */
618153618Sru	DEVMETHOD(device_probe,		nfsmbsub_probe),
619153618Sru	DEVMETHOD(device_attach,	nfsmbsub_attach),
620153618Sru	DEVMETHOD(device_detach,	nfsmbsub_detach),
621153618Sru
622153618Sru	/* SMBus interface */
623153618Sru	DEVMETHOD(smbus_callback,	nfsmb_callback),
624153618Sru	DEVMETHOD(smbus_quick,		nfsmb_quick),
625153618Sru	DEVMETHOD(smbus_sendb,		nfsmb_sendb),
626153618Sru	DEVMETHOD(smbus_recvb,		nfsmb_recvb),
627153618Sru	DEVMETHOD(smbus_writeb,		nfsmb_writeb),
628153618Sru	DEVMETHOD(smbus_readb,		nfsmb_readb),
629153618Sru	DEVMETHOD(smbus_writew,		nfsmb_writew),
630153618Sru	DEVMETHOD(smbus_readw,		nfsmb_readw),
631153618Sru	DEVMETHOD(smbus_bwrite,		nfsmb_bwrite),
632153618Sru	DEVMETHOD(smbus_bread,		nfsmb_bread),
633153618Sru
634153618Sru	{ 0, 0 }
635153618Sru};
636153618Sru
637153618Srustatic devclass_t nfsmb_devclass;
638153618Sru
639153618Srustatic driver_t nfsmb_driver = {
640153618Sru	"nfsmb",
641153618Sru	nfsmb_methods,
642153618Sru	sizeof(struct nfsmb_softc),
643153618Sru};
644153618Sru
645153618Srustatic driver_t nfsmbsub_driver = {
646153618Sru	"nfsmb",
647153618Sru	nfsmbsub_methods,
648153618Sru	sizeof(struct nfsmb_softc),
649153618Sru};
650153618Sru
651153618SruDRIVER_MODULE(nfsmb, pci, nfsmb_driver, nfsmb_devclass, 0, 0);
652153618SruDRIVER_MODULE(nfsmb, nfsmb, nfsmbsub_driver, nfsmb_devclass, 0, 0);
653162234SjhbDRIVER_MODULE(smbus, nfsmb, smbus_driver, smbus_devclass, 0, 0);
654153618Sru
655153618SruMODULE_DEPEND(nfsmb, pci, 1, 1, 1);
656153618SruMODULE_DEPEND(nfsmb, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER);
657153618SruMODULE_VERSION(nfsmb, 1);
658