xref: /freebsd/sys/dev/nfsmb/nfsmb.c (revision 2008043f386721d58158e37e0d7e50df8095942d)
1 /*-
2  * Copyright (c) 2005 Ruslan Ermilov
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 
27 #include <sys/cdefs.h>
28 #include <sys/param.h>
29 #include <sys/bus.h>
30 #include <sys/kernel.h>
31 #include <sys/lock.h>
32 #include <sys/module.h>
33 #include <sys/mutex.h>
34 #include <sys/systm.h>
35 
36 #include <machine/bus.h>
37 #include <machine/resource.h>
38 #include <sys/rman.h>
39 
40 #include <dev/pci/pcivar.h>
41 #include <dev/pci/pcireg.h>
42 
43 #include <dev/smbus/smbconf.h>
44 #include "smbus_if.h"
45 
46 #define	NFSMB_DEBUG(x)	if (nfsmb_debug) (x)
47 
48 #ifdef DEBUG
49 static int nfsmb_debug = 1;
50 #else
51 static int nfsmb_debug = 0;
52 #endif
53 
54 /* NVIDIA nForce2/3/4 MCP */
55 #define	NFSMB_VENDORID_NVIDIA		0x10de
56 #define	NFSMB_DEVICEID_NF2_SMB		0x0064
57 #define	NFSMB_DEVICEID_NF2_ULTRA_SMB	0x0084
58 #define	NFSMB_DEVICEID_NF3_PRO150_SMB	0x00d4
59 #define	NFSMB_DEVICEID_NF3_250GB_SMB	0x00e4
60 #define	NFSMB_DEVICEID_NF4_SMB		0x0052
61 #define	NFSMB_DEVICEID_NF4_04_SMB	0x0034
62 #define	NFSMB_DEVICEID_NF4_51_SMB	0x0264
63 #define	NFSMB_DEVICEID_NF4_55_SMB	0x0368
64 #define	NFSMB_DEVICEID_NF4_61_SMB	0x03eb
65 #define	NFSMB_DEVICEID_NF4_65_SMB	0x0446
66 #define	NFSMB_DEVICEID_NF4_67_SMB	0x0542
67 #define	NFSMB_DEVICEID_NF4_73_SMB	0x07d8
68 #define	NFSMB_DEVICEID_NF4_78S_SMB	0x0752
69 #define	NFSMB_DEVICEID_NF4_79_SMB	0x0aa2
70 
71 /* PCI Configuration space registers */
72 #define	NF2PCI_SMBASE_1		PCIR_BAR(4)
73 #define	NF2PCI_SMBASE_2		PCIR_BAR(5)
74 
75 /*
76  * ACPI 3.0, Chapter 12, SMBus Host Controller Interface.
77  */
78 #define	SMB_PRTCL		0x00	/* protocol */
79 #define	SMB_STS			0x01	/* status */
80 #define	SMB_ADDR		0x02	/* address */
81 #define	SMB_CMD			0x03	/* command */
82 #define	SMB_DATA		0x04	/* 32 data registers */
83 #define	SMB_BCNT		0x24	/* number of data bytes */
84 #define	SMB_ALRM_A		0x25	/* alarm address */
85 #define	SMB_ALRM_D		0x26	/* 2 bytes alarm data */
86 
87 #define	SMB_STS_DONE		0x80
88 #define	SMB_STS_ALRM		0x40
89 #define	SMB_STS_RES		0x20
90 #define	SMB_STS_STATUS		0x1f
91 #define	SMB_STS_OK		0x00	/* OK */
92 #define	SMB_STS_UF		0x07	/* Unknown Failure */
93 #define	SMB_STS_DANA		0x10	/* Device Address Not Acknowledged */
94 #define	SMB_STS_DED		0x11	/* Device Error Detected */
95 #define	SMB_STS_DCAD		0x12	/* Device Command Access Denied */
96 #define	SMB_STS_UE		0x13	/* Unknown Error */
97 #define	SMB_STS_DAD		0x17	/* Device Access Denied */
98 #define	SMB_STS_T		0x18	/* Timeout */
99 #define	SMB_STS_HUP		0x19	/* Host Unsupported Protocol */
100 #define	SMB_STS_B		0x1A	/* Busy */
101 #define	SMB_STS_PEC		0x1F	/* PEC (CRC-8) Error */
102 
103 #define	SMB_PRTCL_WRITE		0x00
104 #define	SMB_PRTCL_READ		0x01
105 #define	SMB_PRTCL_QUICK		0x02
106 #define	SMB_PRTCL_BYTE		0x04
107 #define	SMB_PRTCL_BYTE_DATA	0x06
108 #define	SMB_PRTCL_WORD_DATA	0x08
109 #define	SMB_PRTCL_BLOCK_DATA	0x0a
110 #define	SMB_PRTCL_PROC_CALL	0x0c
111 #define	SMB_PRTCL_BLOCK_PROC_CALL 0x0d
112 #define	SMB_PRTCL_PEC		0x80
113 
114 struct nfsmb_softc {
115 	int rid;
116 	struct resource *res;
117 	device_t smbus;
118 	device_t subdev;
119 	struct mtx lock;
120 };
121 
122 #define	NFSMB_LOCK(nfsmb)		mtx_lock(&(nfsmb)->lock)
123 #define	NFSMB_UNLOCK(nfsmb)		mtx_unlock(&(nfsmb)->lock)
124 #define	NFSMB_LOCK_ASSERT(nfsmb)	mtx_assert(&(nfsmb)->lock, MA_OWNED)
125 
126 #define	NFSMB_SMBINB(nfsmb, register)					\
127 	(bus_read_1(nfsmb->res, register))
128 #define	NFSMB_SMBOUTB(nfsmb, register, value) \
129 	(bus_write_1(nfsmb->res, register, value))
130 
131 static int	nfsmb_detach(device_t dev);
132 static int	nfsmbsub_detach(device_t dev);
133 
134 static int
135 nfsmbsub_probe(device_t dev)
136 {
137 
138 	device_set_desc(dev, "nForce2/3/4 MCP SMBus Controller");
139 	return (BUS_PROBE_DEFAULT);
140 }
141 
142 static int
143 nfsmb_probe(device_t dev)
144 {
145 	u_int16_t vid;
146 	u_int16_t did;
147 
148 	vid = pci_get_vendor(dev);
149 	did = pci_get_device(dev);
150 
151 	if (vid == NFSMB_VENDORID_NVIDIA) {
152 		switch(did) {
153 		case NFSMB_DEVICEID_NF2_SMB:
154 		case NFSMB_DEVICEID_NF2_ULTRA_SMB:
155 		case NFSMB_DEVICEID_NF3_PRO150_SMB:
156 		case NFSMB_DEVICEID_NF3_250GB_SMB:
157 		case NFSMB_DEVICEID_NF4_SMB:
158 		case NFSMB_DEVICEID_NF4_04_SMB:
159 		case NFSMB_DEVICEID_NF4_51_SMB:
160 		case NFSMB_DEVICEID_NF4_55_SMB:
161 		case NFSMB_DEVICEID_NF4_61_SMB:
162 		case NFSMB_DEVICEID_NF4_65_SMB:
163 		case NFSMB_DEVICEID_NF4_67_SMB:
164 		case NFSMB_DEVICEID_NF4_73_SMB:
165 		case NFSMB_DEVICEID_NF4_78S_SMB:
166 		case NFSMB_DEVICEID_NF4_79_SMB:
167 			device_set_desc(dev, "nForce2/3/4 MCP SMBus Controller");
168 			return (BUS_PROBE_DEFAULT);
169 		}
170 	}
171 
172 	return (ENXIO);
173 }
174 
175 static int
176 nfsmbsub_attach(device_t dev)
177 {
178 	device_t parent;
179 	struct nfsmb_softc *nfsmbsub_sc = device_get_softc(dev);
180 
181 	parent = device_get_parent(dev);
182 
183 	nfsmbsub_sc->rid = NF2PCI_SMBASE_2;
184 
185 	nfsmbsub_sc->res = bus_alloc_resource_any(parent, SYS_RES_IOPORT,
186 	    &nfsmbsub_sc->rid, RF_ACTIVE);
187 	if (nfsmbsub_sc->res == NULL) {
188 		/* Older incarnations of the device used non-standard BARs. */
189 		nfsmbsub_sc->rid = 0x54;
190 		nfsmbsub_sc->res = bus_alloc_resource_any(parent,
191 		    SYS_RES_IOPORT, &nfsmbsub_sc->rid, RF_ACTIVE);
192 		if (nfsmbsub_sc->res == NULL) {
193 			device_printf(dev, "could not map i/o space\n");
194 			return (ENXIO);
195 		}
196 	}
197 	mtx_init(&nfsmbsub_sc->lock, device_get_nameunit(dev), "nfsmb",
198 	    MTX_DEF);
199 
200 	nfsmbsub_sc->smbus = device_add_child(dev, "smbus", -1);
201 	if (nfsmbsub_sc->smbus == NULL) {
202 		nfsmbsub_detach(dev);
203 		return (EINVAL);
204 	}
205 
206 	bus_generic_attach(dev);
207 
208 	return (0);
209 }
210 
211 static int
212 nfsmb_attach(device_t dev)
213 {
214 	struct nfsmb_softc *nfsmb_sc = device_get_softc(dev);
215 
216 	/* Allocate I/O space */
217 	nfsmb_sc->rid = NF2PCI_SMBASE_1;
218 
219 	nfsmb_sc->res = bus_alloc_resource_any(dev, SYS_RES_IOPORT,
220 		&nfsmb_sc->rid, RF_ACTIVE);
221 
222 	if (nfsmb_sc->res == NULL) {
223 		/* Older incarnations of the device used non-standard BARs. */
224 		nfsmb_sc->rid = 0x50;
225 		nfsmb_sc->res = bus_alloc_resource_any(dev,
226 		    SYS_RES_IOPORT, &nfsmb_sc->rid, RF_ACTIVE);
227 		if (nfsmb_sc->res == NULL) {
228 			device_printf(dev, "could not map i/o space\n");
229 			return (ENXIO);
230 		}
231 	}
232 
233 	mtx_init(&nfsmb_sc->lock, device_get_nameunit(dev), "nfsmb", MTX_DEF);
234 
235 	/* Allocate a new smbus device */
236 	nfsmb_sc->smbus = device_add_child(dev, "smbus", -1);
237 	if (!nfsmb_sc->smbus) {
238 		nfsmb_detach(dev);
239 		return (EINVAL);
240 	}
241 
242 	nfsmb_sc->subdev = NULL;
243 	switch (pci_get_device(dev)) {
244 	case NFSMB_DEVICEID_NF2_SMB:
245 	case NFSMB_DEVICEID_NF2_ULTRA_SMB:
246 	case NFSMB_DEVICEID_NF3_PRO150_SMB:
247 	case NFSMB_DEVICEID_NF3_250GB_SMB:
248 	case NFSMB_DEVICEID_NF4_SMB:
249 	case NFSMB_DEVICEID_NF4_04_SMB:
250 	case NFSMB_DEVICEID_NF4_51_SMB:
251 	case NFSMB_DEVICEID_NF4_55_SMB:
252 	case NFSMB_DEVICEID_NF4_61_SMB:
253 	case NFSMB_DEVICEID_NF4_65_SMB:
254 	case NFSMB_DEVICEID_NF4_67_SMB:
255 	case NFSMB_DEVICEID_NF4_73_SMB:
256 	case NFSMB_DEVICEID_NF4_78S_SMB:
257 	case NFSMB_DEVICEID_NF4_79_SMB:
258 		/* Trying to add secondary device as slave */
259 		nfsmb_sc->subdev = device_add_child(dev, "nfsmb", -1);
260 		if (!nfsmb_sc->subdev) {
261 			nfsmb_detach(dev);
262 			return (EINVAL);
263 		}
264 		break;
265 	default:
266 		break;
267 	}
268 
269 	bus_generic_attach(dev);
270 
271 	return (0);
272 }
273 
274 static int
275 nfsmbsub_detach(device_t dev)
276 {
277 	device_t parent;
278 	struct nfsmb_softc *nfsmbsub_sc = device_get_softc(dev);
279 
280 	parent = device_get_parent(dev);
281 
282 	if (nfsmbsub_sc->smbus) {
283 		device_delete_child(dev, nfsmbsub_sc->smbus);
284 		nfsmbsub_sc->smbus = NULL;
285 	}
286 	mtx_destroy(&nfsmbsub_sc->lock);
287 	if (nfsmbsub_sc->res) {
288 		bus_release_resource(parent, SYS_RES_IOPORT, nfsmbsub_sc->rid,
289 		    nfsmbsub_sc->res);
290 		nfsmbsub_sc->res = NULL;
291 	}
292 	return (0);
293 }
294 
295 static int
296 nfsmb_detach(device_t dev)
297 {
298 	struct nfsmb_softc *nfsmb_sc = device_get_softc(dev);
299 
300 	if (nfsmb_sc->subdev) {
301 		device_delete_child(dev, nfsmb_sc->subdev);
302 		nfsmb_sc->subdev = NULL;
303 	}
304 
305 	if (nfsmb_sc->smbus) {
306 		device_delete_child(dev, nfsmb_sc->smbus);
307 		nfsmb_sc->smbus = NULL;
308 	}
309 
310 	mtx_destroy(&nfsmb_sc->lock);
311 	if (nfsmb_sc->res) {
312 		bus_release_resource(dev, SYS_RES_IOPORT, nfsmb_sc->rid,
313 		    nfsmb_sc->res);
314 		nfsmb_sc->res = NULL;
315 	}
316 
317 	return (0);
318 }
319 
320 static int
321 nfsmb_callback(device_t dev, int index, void *data)
322 {
323 	int error = 0;
324 
325 	switch (index) {
326 	case SMB_REQUEST_BUS:
327 	case SMB_RELEASE_BUS:
328 		break;
329 	default:
330 		error = EINVAL;
331 	}
332 
333 	return (error);
334 }
335 
336 static int
337 nfsmb_wait(struct nfsmb_softc *sc)
338 {
339 	u_char sts;
340 	int error, count;
341 
342 	NFSMB_LOCK_ASSERT(sc);
343 	if (NFSMB_SMBINB(sc, SMB_PRTCL) != 0)
344 	{
345 		count = 10000;
346 		do {
347 			DELAY(500);
348 		} while (NFSMB_SMBINB(sc, SMB_PRTCL) != 0 && count--);
349 		if (count == 0)
350 			return (SMB_ETIMEOUT);
351 	}
352 
353 	sts = NFSMB_SMBINB(sc, SMB_STS) & SMB_STS_STATUS;
354 	NFSMB_DEBUG(printf("nfsmb: STS=0x%x\n", sts));
355 
356 	switch (sts) {
357 	case SMB_STS_OK:
358 		error = SMB_ENOERR;
359 		break;
360 	case SMB_STS_DANA:
361 		error = SMB_ENOACK;
362 		break;
363 	case SMB_STS_B:
364 		error = SMB_EBUSY;
365 		break;
366 	case SMB_STS_T:
367 		error = SMB_ETIMEOUT;
368 		break;
369 	case SMB_STS_DCAD:
370 	case SMB_STS_DAD:
371 	case SMB_STS_HUP:
372 		error = SMB_ENOTSUPP;
373 		break;
374 	default:
375 		error = SMB_EBUSERR;
376 		break;
377 	}
378 
379 	return (error);
380 }
381 
382 static int
383 nfsmb_quick(device_t dev, u_char slave, int how)
384 {
385 	struct nfsmb_softc *sc = (struct nfsmb_softc *)device_get_softc(dev);
386 	u_char protocol;
387 	int error;
388 
389 	protocol = SMB_PRTCL_QUICK;
390 
391 	switch (how) {
392 	case SMB_QWRITE:
393 		protocol |= SMB_PRTCL_WRITE;
394 		NFSMB_DEBUG(printf("nfsmb: QWRITE to 0x%x", slave));
395 		break;
396 	case SMB_QREAD:
397 		protocol |= SMB_PRTCL_READ;
398 		NFSMB_DEBUG(printf("nfsmb: QREAD to 0x%x", slave));
399 		break;
400 	default:
401 		panic("%s: unknown QUICK command (%x)!", __func__, how);
402 	}
403 
404 	NFSMB_LOCK(sc);
405 	NFSMB_SMBOUTB(sc, SMB_ADDR, slave);
406 	NFSMB_SMBOUTB(sc, SMB_PRTCL, protocol);
407 
408 	error = nfsmb_wait(sc);
409 
410 	NFSMB_DEBUG(printf(", error=0x%x\n", error));
411 	NFSMB_UNLOCK(sc);
412 
413 	return (error);
414 }
415 
416 static int
417 nfsmb_sendb(device_t dev, u_char slave, char byte)
418 {
419 	struct nfsmb_softc *sc = (struct nfsmb_softc *)device_get_softc(dev);
420 	int error;
421 
422 	NFSMB_LOCK(sc);
423 	NFSMB_SMBOUTB(sc, SMB_CMD, byte);
424 	NFSMB_SMBOUTB(sc, SMB_ADDR, slave);
425 	NFSMB_SMBOUTB(sc, SMB_PRTCL, SMB_PRTCL_WRITE | SMB_PRTCL_BYTE);
426 
427 	error = nfsmb_wait(sc);
428 
429 	NFSMB_DEBUG(printf("nfsmb: SENDB to 0x%x, byte=0x%x, error=0x%x\n", slave, byte, error));
430 	NFSMB_UNLOCK(sc);
431 
432 	return (error);
433 }
434 
435 static int
436 nfsmb_recvb(device_t dev, u_char slave, char *byte)
437 {
438 	struct nfsmb_softc *sc = (struct nfsmb_softc *)device_get_softc(dev);
439 	int error;
440 
441 	NFSMB_LOCK(sc);
442 	NFSMB_SMBOUTB(sc, SMB_ADDR, slave);
443 	NFSMB_SMBOUTB(sc, SMB_PRTCL, SMB_PRTCL_READ | SMB_PRTCL_BYTE);
444 
445 	if ((error = nfsmb_wait(sc)) == SMB_ENOERR)
446 		*byte = NFSMB_SMBINB(sc, SMB_DATA);
447 
448 	NFSMB_DEBUG(printf("nfsmb: RECVB from 0x%x, byte=0x%x, error=0x%x\n", slave, *byte, error));
449 	NFSMB_UNLOCK(sc);
450 
451 	return (error);
452 }
453 
454 static int
455 nfsmb_writeb(device_t dev, u_char slave, char cmd, char byte)
456 {
457 	struct nfsmb_softc *sc = (struct nfsmb_softc *)device_get_softc(dev);
458 	int error;
459 
460 	NFSMB_LOCK(sc);
461 	NFSMB_SMBOUTB(sc, SMB_CMD, cmd);
462 	NFSMB_SMBOUTB(sc, SMB_DATA, byte);
463 	NFSMB_SMBOUTB(sc, SMB_ADDR, slave);
464 	NFSMB_SMBOUTB(sc, SMB_PRTCL, SMB_PRTCL_WRITE | SMB_PRTCL_BYTE_DATA);
465 
466 	error = nfsmb_wait(sc);
467 
468 	NFSMB_DEBUG(printf("nfsmb: WRITEB to 0x%x, cmd=0x%x, byte=0x%x, error=0x%x\n", slave, cmd, byte, error));
469 	NFSMB_UNLOCK(sc);
470 
471 	return (error);
472 }
473 
474 static int
475 nfsmb_readb(device_t dev, u_char slave, char cmd, char *byte)
476 {
477 	struct nfsmb_softc *sc = (struct nfsmb_softc *)device_get_softc(dev);
478 	int error;
479 
480 	NFSMB_LOCK(sc);
481 	NFSMB_SMBOUTB(sc, SMB_CMD, cmd);
482 	NFSMB_SMBOUTB(sc, SMB_ADDR, slave);
483 	NFSMB_SMBOUTB(sc, SMB_PRTCL, SMB_PRTCL_READ | SMB_PRTCL_BYTE_DATA);
484 
485 	if ((error = nfsmb_wait(sc)) == SMB_ENOERR)
486 		*byte = NFSMB_SMBINB(sc, SMB_DATA);
487 
488 	NFSMB_DEBUG(printf("nfsmb: READB from 0x%x, cmd=0x%x, byte=0x%x, error=0x%x\n", slave, cmd, (unsigned char)*byte, error));
489 	NFSMB_UNLOCK(sc);
490 
491 	return (error);
492 }
493 
494 static int
495 nfsmb_writew(device_t dev, u_char slave, char cmd, short word)
496 {
497 	struct nfsmb_softc *sc = (struct nfsmb_softc *)device_get_softc(dev);
498 	int error;
499 
500 	NFSMB_LOCK(sc);
501 	NFSMB_SMBOUTB(sc, SMB_CMD, cmd);
502 	NFSMB_SMBOUTB(sc, SMB_DATA, word);
503 	NFSMB_SMBOUTB(sc, SMB_DATA + 1, word >> 8);
504 	NFSMB_SMBOUTB(sc, SMB_ADDR, slave);
505 	NFSMB_SMBOUTB(sc, SMB_PRTCL, SMB_PRTCL_WRITE | SMB_PRTCL_WORD_DATA);
506 
507 	error = nfsmb_wait(sc);
508 
509 	NFSMB_DEBUG(printf("nfsmb: WRITEW to 0x%x, cmd=0x%x, word=0x%x, error=0x%x\n", slave, cmd, word, error));
510 	NFSMB_UNLOCK(sc);
511 
512 	return (error);
513 }
514 
515 static int
516 nfsmb_readw(device_t dev, u_char slave, char cmd, short *word)
517 {
518 	struct nfsmb_softc *sc = (struct nfsmb_softc *)device_get_softc(dev);
519 	int error;
520 
521 	NFSMB_LOCK(sc);
522 	NFSMB_SMBOUTB(sc, SMB_CMD, cmd);
523 	NFSMB_SMBOUTB(sc, SMB_ADDR, slave);
524 	NFSMB_SMBOUTB(sc, SMB_PRTCL, SMB_PRTCL_READ | SMB_PRTCL_WORD_DATA);
525 
526 	if ((error = nfsmb_wait(sc)) == SMB_ENOERR)
527 		*word = NFSMB_SMBINB(sc, SMB_DATA) |
528 		    (NFSMB_SMBINB(sc, SMB_DATA + 1) << 8);
529 
530 	NFSMB_DEBUG(printf("nfsmb: READW from 0x%x, cmd=0x%x, word=0x%x, error=0x%x\n", slave, cmd, (unsigned short)*word, error));
531 	NFSMB_UNLOCK(sc);
532 
533 	return (error);
534 }
535 
536 static int
537 nfsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf)
538 {
539 	struct nfsmb_softc *sc = (struct nfsmb_softc *)device_get_softc(dev);
540 	u_char i;
541 	int error;
542 
543 	if (count < 1 || count > 32)
544 		return (SMB_EINVAL);
545 
546 	NFSMB_LOCK(sc);
547 	NFSMB_SMBOUTB(sc, SMB_CMD, cmd);
548 	NFSMB_SMBOUTB(sc, SMB_BCNT, count);
549 	for (i = 0; i < count; i++)
550 		NFSMB_SMBOUTB(sc, SMB_DATA + i, buf[i]);
551 	NFSMB_SMBOUTB(sc, SMB_ADDR, slave);
552 	NFSMB_SMBOUTB(sc, SMB_PRTCL, SMB_PRTCL_WRITE | SMB_PRTCL_BLOCK_DATA);
553 
554 	error = nfsmb_wait(sc);
555 
556 	NFSMB_DEBUG(printf("nfsmb: WRITEBLK to 0x%x, count=0x%x, cmd=0x%x, error=0x%x", slave, count, cmd, error));
557 	NFSMB_UNLOCK(sc);
558 
559 	return (error);
560 }
561 
562 static int
563 nfsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf)
564 {
565 	struct nfsmb_softc *sc = (struct nfsmb_softc *)device_get_softc(dev);
566 	u_char data, len, i;
567 	int error;
568 
569 	if (*count < 1 || *count > 32)
570 		return (SMB_EINVAL);
571 
572 	NFSMB_LOCK(sc);
573 	NFSMB_SMBOUTB(sc, SMB_CMD, cmd);
574 	NFSMB_SMBOUTB(sc, SMB_ADDR, slave);
575 	NFSMB_SMBOUTB(sc, SMB_PRTCL, SMB_PRTCL_READ | SMB_PRTCL_BLOCK_DATA);
576 
577 	if ((error = nfsmb_wait(sc)) == SMB_ENOERR) {
578 		len = NFSMB_SMBINB(sc, SMB_BCNT);
579 		for (i = 0; i < len; i++) {
580 			data = NFSMB_SMBINB(sc, SMB_DATA + i);
581 			if (i < *count)
582 				buf[i] = data;
583 		}
584 		*count = len;
585 	}
586 
587 	NFSMB_DEBUG(printf("nfsmb: READBLK to 0x%x, count=0x%x, cmd=0x%x, error=0x%x", slave, *count, cmd, error));
588 	NFSMB_UNLOCK(sc);
589 
590 	return (error);
591 }
592 
593 static device_method_t nfsmb_methods[] = {
594 	/* Device interface */
595 	DEVMETHOD(device_probe,		nfsmb_probe),
596 	DEVMETHOD(device_attach,	nfsmb_attach),
597 	DEVMETHOD(device_detach,	nfsmb_detach),
598 
599 	/* SMBus interface */
600 	DEVMETHOD(smbus_callback,	nfsmb_callback),
601 	DEVMETHOD(smbus_quick,		nfsmb_quick),
602 	DEVMETHOD(smbus_sendb,		nfsmb_sendb),
603 	DEVMETHOD(smbus_recvb,		nfsmb_recvb),
604 	DEVMETHOD(smbus_writeb,		nfsmb_writeb),
605 	DEVMETHOD(smbus_readb,		nfsmb_readb),
606 	DEVMETHOD(smbus_writew,		nfsmb_writew),
607 	DEVMETHOD(smbus_readw,		nfsmb_readw),
608 	DEVMETHOD(smbus_bwrite,		nfsmb_bwrite),
609 	DEVMETHOD(smbus_bread,		nfsmb_bread),
610 	{ 0, 0 }
611 };
612 
613 static device_method_t nfsmbsub_methods[] = {
614 	/* Device interface */
615 	DEVMETHOD(device_probe,		nfsmbsub_probe),
616 	DEVMETHOD(device_attach,	nfsmbsub_attach),
617 	DEVMETHOD(device_detach,	nfsmbsub_detach),
618 
619 	/* SMBus interface */
620 	DEVMETHOD(smbus_callback,	nfsmb_callback),
621 	DEVMETHOD(smbus_quick,		nfsmb_quick),
622 	DEVMETHOD(smbus_sendb,		nfsmb_sendb),
623 	DEVMETHOD(smbus_recvb,		nfsmb_recvb),
624 	DEVMETHOD(smbus_writeb,		nfsmb_writeb),
625 	DEVMETHOD(smbus_readb,		nfsmb_readb),
626 	DEVMETHOD(smbus_writew,		nfsmb_writew),
627 	DEVMETHOD(smbus_readw,		nfsmb_readw),
628 	DEVMETHOD(smbus_bwrite,		nfsmb_bwrite),
629 	DEVMETHOD(smbus_bread,		nfsmb_bread),
630 	{ 0, 0 }
631 };
632 
633 static driver_t nfsmb_driver = {
634 	"nfsmb",
635 	nfsmb_methods,
636 	sizeof(struct nfsmb_softc),
637 };
638 
639 static driver_t nfsmbsub_driver = {
640 	"nfsmb",
641 	nfsmbsub_methods,
642 	sizeof(struct nfsmb_softc),
643 };
644 
645 DRIVER_MODULE(nfsmb, pci, nfsmb_driver, 0, 0);
646 DRIVER_MODULE(nfsmb, nfsmb, nfsmbsub_driver, 0, 0);
647 DRIVER_MODULE(smbus, nfsmb, smbus_driver, 0, 0);
648 
649 MODULE_DEPEND(nfsmb, pci, 1, 1, 1);
650 MODULE_DEPEND(nfsmb, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER);
651 MODULE_VERSION(nfsmb, 1);
652