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