alpm.c revision 59391
1/*- 2 * Copyright (c) 1998, 1999 Nicolas Souchu 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 * $FreeBSD: head/sys/pci/alpm.c 59391 2000-04-19 14:58:28Z phk $ 27 * 28 */ 29 30/* 31 * Power Management support for the Acer M15x3 chipsets 32 */ 33#include <sys/param.h> 34#include <sys/kernel.h> 35#include <sys/systm.h> 36#include <sys/module.h> 37#include <sys/bus.h> 38#include <sys/uio.h> 39 40#include <machine/clock.h> 41 42#include <machine/bus_pio.h> 43#include <machine/bus_memio.h> 44#include <machine/bus.h> 45 46#include <pci/pcivar.h> 47#include <pci/pcireg.h> 48 49#include <dev/iicbus/iiconf.h> 50#include <dev/smbus/smbconf.h> 51#include "smbus_if.h" 52 53#include "alpm.h" 54 55#ifndef COMPAT_OLDPCI 56#error "The alpm device requires the old pci compatibility shims" 57#endif 58 59#define ALPM_DEBUG(x) if (alpm_debug) (x) 60 61#ifdef DEBUG 62static int alpm_debug = 1; 63#else 64static int alpm_debug = 0; 65#endif 66 67#define ACER_M1543_PMU_ID 0x710110b9 68 69/* Uncomment this line to force another I/O base address for SMB */ 70/* #define ALPM_SMBIO_BASE_ADDR 0x3a80 */ 71 72/* I/O registers offsets - the base address is programmed via the 73 * SMBBA PCI configuration register 74 */ 75#define SMBSTS 0x0 /* SMBus host/slave status register */ 76#define SMBCMD 0x1 /* SMBus host/slave command register */ 77#define SMBSTART 0x2 /* start to generate programmed cycle */ 78#define SMBHADDR 0x3 /* host address register */ 79#define SMBHDATA 0x4 /* data A register for host controller */ 80#define SMBHDATB 0x5 /* data B register for host controller */ 81#define SMBHBLOCK 0x6 /* block register for host controller */ 82#define SMBHCMD 0x7 /* command register for host controller */ 83 84/* SMBSTS masks */ 85#define TERMINATE 0x80 86#define BUS_COLLI 0x40 87#define DEVICE_ERR 0x20 88#define SMI_I_STS 0x10 89#define HST_BSY 0x08 90#define IDL_STS 0x04 91#define HSTSLV_STS 0x02 92#define HSTSLV_BSY 0x01 93 94/* SMBCMD masks */ 95#define SMB_BLK_CLR 0x80 96#define T_OUT_CMD 0x08 97#define ABORT_HOST 0x04 98 99/* SMBus commands */ 100#define SMBQUICK 0x00 101#define SMBSRBYTE 0x10 /* send/receive byte */ 102#define SMBWRBYTE 0x20 /* write/read byte */ 103#define SMBWRWORD 0x30 /* write/read word */ 104#define SMBWRBLOCK 0x40 /* write/read block */ 105 106/* PCI configuration registers and masks 107 */ 108#define COM 0x4 109#define COM_ENABLE_IO 0x1 110 111#define SMBBA 0x14 112 113#define ATPC 0x5b 114#define ATPC_SMBCTRL 0x04 115 116#define SMBHSI 0xe0 117#define SMBHSI_SLAVE 0x2 118#define SMBHSI_HOST 0x1 119 120#define SMBHCBC 0xe2 121#define SMBHCBC_CLOCK 0x70 122 123#define SMBCLOCK_149K 0x0 124#define SMBCLOCK_74K 0x20 125#define SMBCLOCK_37K 0x40 126#define SMBCLOCK_223K 0x80 127#define SMBCLOCK_111K 0xa0 128#define SMBCLOCK_55K 0xc0 129 130struct alpm_data { 131 int base; 132 bus_space_tag_t smbst; 133 bus_space_handle_t smbsh; 134 pcici_t tag; 135}; 136struct alpm_data alpmdata[NALPM]; 137 138struct alsmb_softc { 139 int base; 140 device_t smbus; 141 struct alpm_data *alpm; 142}; 143 144#define ALPM_SMBINB(alsmb,register) \ 145 (bus_space_read_1(alsmb->alpm->smbst, alsmb->alpm->smbsh, register)) 146#define ALPM_SMBOUTB(alsmb,register,value) \ 147 (bus_space_write_1(alsmb->alpm->smbst, alsmb->alpm->smbsh, register, value)) 148 149static int alsmb_probe(device_t); 150static int alsmb_attach(device_t); 151static int alsmb_smb_callback(device_t, int, caddr_t *); 152static int alsmb_smb_quick(device_t dev, u_char slave, int how); 153static int alsmb_smb_sendb(device_t dev, u_char slave, char byte); 154static int alsmb_smb_recvb(device_t dev, u_char slave, char *byte); 155static int alsmb_smb_writeb(device_t dev, u_char slave, char cmd, char byte); 156static int alsmb_smb_readb(device_t dev, u_char slave, char cmd, char *byte); 157static int alsmb_smb_writew(device_t dev, u_char slave, char cmd, short word); 158static int alsmb_smb_readw(device_t dev, u_char slave, char cmd, short *word); 159static int alsmb_smb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf); 160static int alsmb_smb_bread(device_t dev, u_char slave, char cmd, u_char count, char *byte); 161 162static devclass_t alsmb_devclass; 163 164static device_method_t alsmb_methods[] = { 165 /* device interface */ 166 DEVMETHOD(device_probe, alsmb_probe), 167 DEVMETHOD(device_attach, alsmb_attach), 168 169 /* bus interface */ 170 DEVMETHOD(bus_print_child, bus_generic_print_child), 171 172 /* smbus interface */ 173 DEVMETHOD(smbus_callback, alsmb_smb_callback), 174 DEVMETHOD(smbus_quick, alsmb_smb_quick), 175 DEVMETHOD(smbus_sendb, alsmb_smb_sendb), 176 DEVMETHOD(smbus_recvb, alsmb_smb_recvb), 177 DEVMETHOD(smbus_writeb, alsmb_smb_writeb), 178 DEVMETHOD(smbus_readb, alsmb_smb_readb), 179 DEVMETHOD(smbus_writew, alsmb_smb_writew), 180 DEVMETHOD(smbus_readw, alsmb_smb_readw), 181 DEVMETHOD(smbus_bwrite, alsmb_smb_bwrite), 182 DEVMETHOD(smbus_bread, alsmb_smb_bread), 183 184 { 0, 0 } 185}; 186 187static driver_t alsmb_driver = { 188 "alsmb", 189 alsmb_methods, 190 sizeof(struct alsmb_softc), 191}; 192 193static const char* alpm_pci_probe(pcici_t tag, pcidi_t type); 194static void alpm_pci_attach(pcici_t tag, int unit); 195 196static u_long alpm_count; 197 198static struct pci_device alpm_device = { 199 "alpm", 200 alpm_pci_probe, 201 alpm_pci_attach, 202 &alpm_count 203}; 204 205COMPAT_PCI_DRIVER (alpm, alpm_device); 206 207static const char* 208alpm_pci_probe(pcici_t tag, pcidi_t type) 209{ 210 if (type == ACER_M1543_PMU_ID) 211 return ("AcerLabs M15x3 Power Management Unit"); 212 213 return ((char *)0); 214} 215 216static void 217alpm_pci_attach(pcici_t tag, int unit) 218{ 219 struct alpm_data *alpm; 220 u_long l; 221 222 if (unit >= NALPM) { 223 printf("alpm%d: attach: only %d units configured.\n", 224 unit, NALPM); 225 return; 226 } 227 alpm = &alpmdata[unit]; 228 229 alpm->tag = tag; 230 231 /* Unlock SMBIO base register access */ 232 l = pci_cfgread(tag, ATPC, 1); 233 pci_cfgwrite(tag, ATPC, l & ~ATPC_SMBCTRL, 1); 234 235 if (bootverbose) { 236 l = pci_cfgread(tag, SMBHSI, 1); 237 printf("alsmb%d: %s/%s", unit, 238 (l & SMBHSI_HOST) ? "host":"nohost", 239 (l & SMBHSI_SLAVE) ? "slave":"noslave"); 240 241 l = pci_cfgread(tag, SMBHCBC, 1); 242 switch (l & SMBHCBC_CLOCK) { 243 case SMBCLOCK_149K: 244 printf(" 149K"); 245 break; 246 case SMBCLOCK_74K: 247 printf(" 74K"); 248 break; 249 case SMBCLOCK_37K: 250 printf(" 37K"); 251 break; 252 case SMBCLOCK_223K: 253 printf(" 223K"); 254 break; 255 case SMBCLOCK_111K: 256 printf(" 111K"); 257 break; 258 case SMBCLOCK_55K: 259 printf(" 55K"); 260 break; 261 } 262 } 263 264 alpm->smbst = I386_BUS_SPACE_IO; 265 266#ifdef ALPM_SMBIO_BASE_ADDR 267 /* disable I/O */ 268 l = pci_cfgread(tag, COM, 2); 269 pci_cfgwrite(tag, COM, l & ~COM_ENABLE_IO, 2); 270 271 /* set the I/O base address */ 272 pci_cfgwrite(tag, SMBBA, ALPM_SMBIO_BASE_ADDR | 0x1, 4); 273 274 /* enable I/O */ 275 pci_cfgwrite(tag, COM, l | COM_ENABLE_IO, 2); 276 277 alpm->smbsh = ALPM_SMBIO_BASE_ADDR; 278#else 279 alpm->smbsh = pci_cfgread(tag, SMBBA, 4) & ~0x1; 280#endif 281 if (bootverbose) 282 printf(" at 0x%x\n", alpm->smbsh); 283 284 /* XXX add the I2C interface to the root_bus until pcibus is ready */ 285 device_add_child(root_bus, "alsmb", unit); 286 287 return; 288} 289 290/* 291 * Not a real probe, we know the device exists since the device has 292 * been added after the successfull pci probe. 293 */ 294static int 295alsmb_probe(device_t dev) 296{ 297 struct alsmb_softc *sc = (struct alsmb_softc *)device_get_softc(dev); 298 299 sc->alpm = &alpmdata[device_get_unit(dev)]; 300 301 device_set_desc(dev, "Aladdin IV/V/Pro2 SMBus controller"); 302 303 return (0); 304} 305 306static int 307alsmb_attach(device_t dev) 308{ 309 struct alsmb_softc *sc = (struct alsmb_softc *)device_get_softc(dev); 310 311 /* allocate a new smbus device */ 312 sc->smbus = smbus_alloc_bus(dev); 313 314 /* probe and attach the smbus */ 315 device_probe_and_attach(sc->smbus); 316 317 return (0); 318} 319 320static int 321alsmb_smb_callback(device_t dev, int index, caddr_t *data) 322{ 323 int error = 0; 324 325 switch (index) { 326 case SMB_REQUEST_BUS: 327 case SMB_RELEASE_BUS: 328 /* ok, bus allocation accepted */ 329 break; 330 default: 331 error = EINVAL; 332 } 333 334 return (error); 335} 336 337static int 338alsmb_clear(struct alsmb_softc *sc) 339{ 340 ALPM_SMBOUTB(sc, SMBSTS, 0xff); 341 DELAY(10); 342 343 return (0); 344} 345 346#if 0 347static int 348alsmb_abort(struct alsmb_softc *sc) 349{ 350 ALPM_SMBOUTB(sc, SMBCMD, T_OUT_CMD | ABORT_HOST); 351 352 return (0); 353} 354#endif 355 356static int 357alsmb_idle(struct alsmb_softc *sc) 358{ 359 u_char sts; 360 361 sts = ALPM_SMBINB(sc, SMBSTS); 362 363 ALPM_DEBUG(printf("alpm: idle? STS=0x%x\n", sts)); 364 365 return (sts & IDL_STS); 366} 367 368/* 369 * Poll the SMBus controller 370 */ 371static int 372alsmb_wait(struct alsmb_softc *sc) 373{ 374 int count = 10000; 375 u_char sts; 376 int error; 377 378 /* wait for command to complete and SMBus controller is idle */ 379 while(count--) { 380 DELAY(10); 381 sts = ALPM_SMBINB(sc, SMBSTS); 382 if (sts & SMI_I_STS) 383 break; 384 } 385 386 ALPM_DEBUG(printf("alpm: STS=0x%x\n", sts)); 387 388 error = SMB_ENOERR; 389 390 if (!count) 391 error |= SMB_ETIMEOUT; 392 393 if (sts & TERMINATE) 394 error |= SMB_EABORT; 395 396 if (sts & BUS_COLLI) 397 error |= SMB_ENOACK; 398 399 if (sts & DEVICE_ERR) 400 error |= SMB_EBUSERR; 401 402 if (error != SMB_ENOERR) 403 alsmb_clear(sc); 404 405 return (error); 406} 407 408static int 409alsmb_smb_quick(device_t dev, u_char slave, int how) 410{ 411 struct alsmb_softc *sc = (struct alsmb_softc *)device_get_softc(dev); 412 int error; 413 414 alsmb_clear(sc); 415 if (!alsmb_idle(sc)) 416 return (EBUSY); 417 418 switch (how) { 419 case SMB_QWRITE: 420 ALPM_DEBUG(printf("alpm: QWRITE to 0x%x", slave)); 421 ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB); 422 break; 423 case SMB_QREAD: 424 ALPM_DEBUG(printf("alpm: QREAD to 0x%x", slave)); 425 ALPM_SMBOUTB(sc, SMBHADDR, slave | LSB); 426 break; 427 default: 428 panic("%s: unknown QUICK command (%x)!", __FUNCTION__, 429 how); 430 } 431 ALPM_SMBOUTB(sc, SMBCMD, SMBQUICK); 432 ALPM_SMBOUTB(sc, SMBSTART, 0xff); 433 434 error = alsmb_wait(sc); 435 436 ALPM_DEBUG(printf(", error=0x%x\n", error)); 437 438 return (error); 439} 440 441static int 442alsmb_smb_sendb(device_t dev, u_char slave, char byte) 443{ 444 struct alsmb_softc *sc = (struct alsmb_softc *)device_get_softc(dev); 445 int error; 446 447 alsmb_clear(sc); 448 if (!alsmb_idle(sc)) 449 return (SMB_EBUSY); 450 451 ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB); 452 ALPM_SMBOUTB(sc, SMBCMD, SMBSRBYTE); 453 ALPM_SMBOUTB(sc, SMBHDATA, byte); 454 ALPM_SMBOUTB(sc, SMBSTART, 0xff); 455 456 error = alsmb_wait(sc); 457 458 ALPM_DEBUG(printf("alpm: SENDB to 0x%x, byte=0x%x, error=0x%x\n", slave, byte, error)); 459 460 return (error); 461} 462 463static int 464alsmb_smb_recvb(device_t dev, u_char slave, char *byte) 465{ 466 struct alsmb_softc *sc = (struct alsmb_softc *)device_get_softc(dev); 467 int error; 468 469 alsmb_clear(sc); 470 if (!alsmb_idle(sc)) 471 return (SMB_EBUSY); 472 473 ALPM_SMBOUTB(sc, SMBHADDR, slave | LSB); 474 ALPM_SMBOUTB(sc, SMBCMD, SMBSRBYTE); 475 ALPM_SMBOUTB(sc, SMBSTART, 0xff); 476 477 if ((error = alsmb_wait(sc)) == SMB_ENOERR) 478 *byte = ALPM_SMBINB(sc, SMBHDATA); 479 480 ALPM_DEBUG(printf("alpm: RECVB from 0x%x, byte=0x%x, error=0x%x\n", slave, *byte, error)); 481 482 return (error); 483} 484 485static int 486alsmb_smb_writeb(device_t dev, u_char slave, char cmd, char byte) 487{ 488 struct alsmb_softc *sc = (struct alsmb_softc *)device_get_softc(dev); 489 int error; 490 491 alsmb_clear(sc); 492 if (!alsmb_idle(sc)) 493 return (SMB_EBUSY); 494 495 ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB); 496 ALPM_SMBOUTB(sc, SMBCMD, SMBWRBYTE); 497 ALPM_SMBOUTB(sc, SMBHDATA, byte); 498 ALPM_SMBOUTB(sc, SMBHCMD, cmd); 499 ALPM_SMBOUTB(sc, SMBSTART, 0xff); 500 501 error = alsmb_wait(sc); 502 503 ALPM_DEBUG(printf("alpm: WRITEB to 0x%x, cmd=0x%x, byte=0x%x, error=0x%x\n", slave, cmd, byte, error)); 504 505 return (error); 506} 507 508static int 509alsmb_smb_readb(device_t dev, u_char slave, char cmd, char *byte) 510{ 511 struct alsmb_softc *sc = (struct alsmb_softc *)device_get_softc(dev); 512 int error; 513 514 alsmb_clear(sc); 515 if (!alsmb_idle(sc)) 516 return (SMB_EBUSY); 517 518 ALPM_SMBOUTB(sc, SMBHADDR, slave | LSB); 519 ALPM_SMBOUTB(sc, SMBCMD, SMBWRBYTE); 520 ALPM_SMBOUTB(sc, SMBHCMD, cmd); 521 ALPM_SMBOUTB(sc, SMBSTART, 0xff); 522 523 if ((error = alsmb_wait(sc)) == SMB_ENOERR) 524 *byte = ALPM_SMBINB(sc, SMBHDATA); 525 526 ALPM_DEBUG(printf("alpm: READB from 0x%x, cmd=0x%x, byte=0x%x, error=0x%x\n", slave, cmd, *byte, error)); 527 528 return (error); 529} 530 531static int 532alsmb_smb_writew(device_t dev, u_char slave, char cmd, short word) 533{ 534 struct alsmb_softc *sc = (struct alsmb_softc *)device_get_softc(dev); 535 int error; 536 537 alsmb_clear(sc); 538 if (!alsmb_idle(sc)) 539 return (SMB_EBUSY); 540 541 ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB); 542 ALPM_SMBOUTB(sc, SMBCMD, SMBWRWORD); 543 ALPM_SMBOUTB(sc, SMBHDATA, word & 0x00ff); 544 ALPM_SMBOUTB(sc, SMBHDATB, (word & 0xff00) >> 8); 545 ALPM_SMBOUTB(sc, SMBHCMD, cmd); 546 ALPM_SMBOUTB(sc, SMBSTART, 0xff); 547 548 error = alsmb_wait(sc); 549 550 ALPM_DEBUG(printf("alpm: WRITEW to 0x%x, cmd=0x%x, word=0x%x, error=0x%x\n", slave, cmd, word, error)); 551 552 return (error); 553} 554 555static int 556alsmb_smb_readw(device_t dev, u_char slave, char cmd, short *word) 557{ 558 struct alsmb_softc *sc = (struct alsmb_softc *)device_get_softc(dev); 559 int error; 560 u_char high, low; 561 562 alsmb_clear(sc); 563 if (!alsmb_idle(sc)) 564 return (SMB_EBUSY); 565 566 ALPM_SMBOUTB(sc, SMBHADDR, slave | LSB); 567 ALPM_SMBOUTB(sc, SMBCMD, SMBWRWORD); 568 ALPM_SMBOUTB(sc, SMBHCMD, cmd); 569 ALPM_SMBOUTB(sc, SMBSTART, 0xff); 570 571 if ((error = alsmb_wait(sc)) == SMB_ENOERR) { 572 low = ALPM_SMBINB(sc, SMBHDATA); 573 high = ALPM_SMBINB(sc, SMBHDATB); 574 575 *word = ((high & 0xff) << 8) | (low & 0xff); 576 } 577 578 ALPM_DEBUG(printf("alpm: READW from 0x%x, cmd=0x%x, word=0x%x, error=0x%x\n", slave, cmd, *word, error)); 579 580 return (error); 581} 582 583static int 584alsmb_smb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf) 585{ 586 struct alsmb_softc *sc = (struct alsmb_softc *)device_get_softc(dev); 587 u_char remain, len, i; 588 int error = SMB_ENOERR; 589 590 alsmb_clear(sc); 591 if(!alsmb_idle(sc)) 592 return (SMB_EBUSY); 593 594 remain = count; 595 while (remain) { 596 len = min(remain, 32); 597 598 ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB); 599 600 /* set the cmd and reset the 601 * 32-byte long internal buffer */ 602 ALPM_SMBOUTB(sc, SMBCMD, SMBWRBLOCK | SMB_BLK_CLR); 603 604 ALPM_SMBOUTB(sc, SMBHDATA, len); 605 606 /* fill the 32-byte internal buffer */ 607 for (i=0; i<len; i++) { 608 ALPM_SMBOUTB(sc, SMBHBLOCK, buf[count-remain+i]); 609 DELAY(2); 610 } 611 ALPM_SMBOUTB(sc, SMBHCMD, cmd); 612 ALPM_SMBOUTB(sc, SMBSTART, 0xff); 613 614 if ((error = alsmb_wait(sc)) != SMB_ENOERR) 615 goto error; 616 617 remain -= len; 618 } 619 620error: 621 ALPM_DEBUG(printf("alpm: WRITEBLK to 0x%x, count=0x%x, cmd=0x%x, error=0x%x", slave, count, cmd, error)); 622 623 return (error); 624} 625 626static int 627alsmb_smb_bread(device_t dev, u_char slave, char cmd, u_char count, char *buf) 628{ 629 struct alsmb_softc *sc = (struct alsmb_softc *)device_get_softc(dev); 630 u_char remain, len, i; 631 int error = SMB_ENOERR; 632 633 alsmb_clear(sc); 634 if (!alsmb_idle(sc)) 635 return (SMB_EBUSY); 636 637 remain = count; 638 while (remain) { 639 ALPM_SMBOUTB(sc, SMBHADDR, slave | LSB); 640 641 /* set the cmd and reset the 642 * 32-byte long internal buffer */ 643 ALPM_SMBOUTB(sc, SMBCMD, SMBWRBLOCK | SMB_BLK_CLR); 644 645 ALPM_SMBOUTB(sc, SMBHCMD, cmd); 646 ALPM_SMBOUTB(sc, SMBSTART, 0xff); 647 648 if ((error = alsmb_wait(sc)) != SMB_ENOERR) 649 goto error; 650 651 len = ALPM_SMBINB(sc, SMBHDATA); 652 653 /* read the 32-byte internal buffer */ 654 for (i=0; i<len; i++) { 655 buf[count-remain+i] = ALPM_SMBINB(sc, SMBHBLOCK); 656 DELAY(2); 657 } 658 659 remain -= len; 660 } 661error: 662 ALPM_DEBUG(printf("alpm: READBLK to 0x%x, count=0x%x, cmd=0x%x, error=0x%x", slave, count, cmd, error)); 663 664 return (error); 665} 666 667DRIVER_MODULE(alsmb, root, alsmb_driver, alsmb_devclass, 0, 0); 668