ixp425.c revision 189641
1/* $NetBSD: ixp425.c,v 1.10 2005/12/11 12:16:51 christos Exp $ */ 2 3/* 4 * Copyright (c) 2003 5 * Ichiro FUKUHARA <ichiro@ichiro.org>. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by Ichiro FUKUHARA. 19 * 4. The name of the company nor the name of the author may be used to 20 * endorse or promote products derived from this software without specific 21 * prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY ICHIRO FUKUHARA ``AS IS'' AND ANY EXPRESS OR 24 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 25 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 26 * IN NO EVENT SHALL ICHIRO FUKUHARA OR THE VOICES IN HIS HEAD BE LIABLE FOR 27 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36#include <sys/cdefs.h> 37__FBSDID("$FreeBSD: head/sys/arm/xscale/ixp425/ixp425.c 189641 2009-03-10 19:15:35Z sam $"); 38 39#include "opt_ddb.h" 40 41#define _ARM32_BUS_DMA_PRIVATE 42#include <sys/param.h> 43#include <sys/systm.h> 44#include <sys/bus.h> 45#include <sys/kernel.h> 46#include <sys/module.h> 47#include <sys/malloc.h> 48#include <sys/rman.h> 49#include <machine/bus.h> 50#include <machine/intr.h> 51 52#include <vm/vm.h> 53#include <vm/pmap.h> 54#include <arm/xscale/ixp425/ixp425reg.h> 55#include <arm/xscale/ixp425/ixp425var.h> 56#include <arm/xscale/ixp425/ixp425_intr.h> 57 58#include <dev/pci/pcireg.h> 59 60volatile uint32_t intr_enabled; 61uint32_t intr_steer = 0; 62 63/* ixp43x et. al have +32 IRQ's */ 64volatile uint32_t intr_enabled2; 65uint32_t intr_steer2 = 0; 66 67struct ixp425_softc *ixp425_softc = NULL; 68 69static int ixp425_probe(device_t); 70static void ixp425_identify(driver_t *, device_t); 71static int ixp425_attach(device_t); 72 73/* 74 * Return a mask of the "fuse" bits that identify 75 * which h/w features are present. 76 * NB: assumes the expansion bus is mapped. 77 */ 78uint32_t 79ixp4xx_read_feature_bits(void) 80{ 81 uint32_t bits = ~IXPREG(IXP425_EXP_VBASE + EXP_FCTRL_OFFSET); 82 bits &= ~EXP_FCTRL_RESVD; 83 if (!cpu_is_ixp46x()) 84 bits &= ~EXP_FCTRL_IXP46X_ONLY; 85 return bits; 86} 87 88struct arm32_dma_range * 89bus_dma_get_range(void) 90{ 91 return (NULL); 92} 93 94int 95bus_dma_get_range_nb(void) 96{ 97 return (0); 98} 99 100static const uint8_t int2gpio[32] __attribute__ ((aligned(32))) = { 101 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* INT#0 -> INT#5 */ 102 0x00, 0x01, /* GPIO#0 -> GPIO#1 */ 103 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* INT#8 -> INT#13 */ 104 0xff, 0xff, 0xff, 0xff, 0xff, /* INT#14 -> INT#18 */ 105 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* GPIO#2 -> GPIO#7 */ 106 0x08, 0x09, 0x0a, 0x0b, 0x0c, /* GPIO#8 -> GPIO#12 */ 107 0xff, 0xff /* INT#30 -> INT#31 */ 108}; 109 110static __inline u_int32_t 111ixp425_irq2gpio_bit(int irq) 112{ 113 return (1U << int2gpio[irq]); 114} 115 116#ifdef DDB 117#include <ddb/ddb.h> 118 119DB_SHOW_COMMAND(gpio, db_show_gpio) 120{ 121 static const char *itype[8] = { 122 [GPIO_TYPE_ACT_HIGH] = "act-high", 123 [GPIO_TYPE_ACT_LOW] = "act-low", 124 [GPIO_TYPE_EDG_RISING] = "edge-rising", 125 [GPIO_TYPE_EDG_FALLING] = "edge-falling", 126 [GPIO_TYPE_TRANSITIONAL]= "transitional", 127 [5] = "type-5", [6] = "type-6", [7] = "type-7" 128 }; 129 uint32_t gpoutr = GPIO_CONF_READ_4(ixp425_softc, IXP425_GPIO_GPOUTR); 130 uint32_t gpoer = GPIO_CONF_READ_4(ixp425_softc, IXP425_GPIO_GPOER); 131 uint32_t gpinr = GPIO_CONF_READ_4(ixp425_softc, IXP425_GPIO_GPINR); 132 uint32_t gpit1r = GPIO_CONF_READ_4(ixp425_softc, IXP425_GPIO_GPIT1R); 133 uint32_t gpit2r = GPIO_CONF_READ_4(ixp425_softc, IXP425_GPIO_GPIT2R); 134 int i, j; 135 136 db_printf("GPOUTR %08x GPOER %08x GPINR %08x GPISR %08x\n", 137 gpoutr, gpoer, gpinr, 138 GPIO_CONF_READ_4(ixp425_softc, IXP425_GPIO_GPISR)); 139 db_printf("GPIT1R %08x GPIT2R %08x GPCLKR %08x\n", 140 gpit1r, gpit2r, GPIO_CONF_READ_4(ixp425_softc, IXP425_GPIO_GPCLKR)); 141 for (i = 0; i < 16; i++) { 142 db_printf("[%2d] out %u in %u %-3s", i, 143 (gpoutr>>i)&1, (gpinr>>i)&1, (gpoer>>i)&1 ? "in" : "out"); 144 for (j = 0; j < 32; j++) 145 if (int2gpio[j] == i) { 146 db_printf(" irq %2u %s", j, itype[ 147 (((i & 8) ? gpit2r : gpit1r) >> (3*(i&7))) 148 & 7]); 149 break; 150 } 151 db_printf("\n"); 152 } 153} 154#endif 155 156void 157arm_mask_irq(uintptr_t nb) 158{ 159 int i; 160 161 i = disable_interrupts(I32_bit); 162 if (nb < 32) { 163 intr_enabled &= ~(1 << nb); 164 ixp425_set_intrmask(); 165 } else { 166 intr_enabled2 &= ~(1 << (nb - 32)); 167 ixp435_set_intrmask(); 168 } 169 restore_interrupts(i); 170 /*XXX; If it's a GPIO interrupt, ACK it know. Can it be a problem ?*/ 171 if (nb < 32 && ((1 << nb) & IXP425_INT_GPIOMASK)) 172 IXPREG(IXP425_GPIO_VBASE + IXP425_GPIO_GPISR) = 173 ixp425_irq2gpio_bit(nb); 174} 175 176void 177arm_unmask_irq(uintptr_t nb) 178{ 179 int i; 180 181 i = disable_interrupts(I32_bit); 182 if (nb < 32) { 183 intr_enabled |= (1 << nb); 184 ixp425_set_intrmask(); 185 } else { 186 intr_enabled2 |= (1 << (nb - 32)); 187 ixp435_set_intrmask(); 188 } 189 restore_interrupts(i); 190} 191 192static __inline uint32_t 193ixp425_irq_read(void) 194{ 195 return IXPREG(IXP425_INT_STATUS) & intr_enabled; 196} 197 198static __inline uint32_t 199ixp435_irq_read(void) 200{ 201 return IXPREG(IXP435_INT_STATUS2) & intr_enabled2; 202} 203 204int 205arm_get_next_irq(void) 206{ 207 uint32_t irq; 208 209 if ((irq = ixp425_irq_read())) 210 return (ffs(irq) - 1); 211 if (cpu_is_ixp43x() && (irq = ixp435_irq_read())) 212 return (32 + ffs(irq) - 1); 213 return (-1); 214} 215 216void 217cpu_reset(void) 218{ 219 220 bus_space_write_4(&ixp425_bs_tag, IXP425_TIMER_VBASE, 221 IXP425_OST_WDOG_KEY, OST_WDOG_KEY_MAJICK); 222 bus_space_write_4(&ixp425_bs_tag, IXP425_TIMER_VBASE, 223 IXP425_OST_WDOG, 0); 224 bus_space_write_4(&ixp425_bs_tag, IXP425_TIMER_VBASE, 225 IXP425_OST_WDOG_ENAB, OST_WDOG_ENAB_RST_ENA | 226 OST_WDOG_ENAB_CNT_ENA); 227 printf("Reset failed!\n"); 228 for(;;); 229} 230 231static void 232ixp425_identify(driver_t *driver, device_t parent) 233{ 234 BUS_ADD_CHILD(parent, 0, "ixp", 0); 235} 236 237static int 238ixp425_probe(device_t dev) 239{ 240 device_set_desc(dev, "Intel IXP4XX"); 241 return (0); 242} 243 244static int 245ixp425_attach(device_t dev) 246{ 247 struct ixp425_softc *sc; 248 249 device_printf(dev, "%b\n", ixp4xx_read_feature_bits(), EXP_FCTRL_BITS); 250 251 sc = device_get_softc(dev); 252 sc->sc_iot = &ixp425_bs_tag; 253 KASSERT(ixp425_softc == NULL, ("%s called twice?", __func__)); 254 ixp425_softc = sc; 255 256 intr_enabled = 0; 257 ixp425_set_intrmask(); 258 ixp425_set_intrsteer(); 259 if (cpu_is_ixp43x()) { 260 intr_enabled2 = 0; 261 ixp435_set_intrmask(); 262 ixp435_set_intrsteer(); 263 } 264 265 if (bus_dma_tag_create(NULL, 1, 0, BUS_SPACE_MAXADDR_32BIT, 266 BUS_SPACE_MAXADDR, NULL, NULL, 0xffffffff, 0xff, 0xffffffff, 0, 267 NULL, NULL, &sc->sc_dmat)) 268 panic("%s: failed to create dma tag", __func__); 269 270 sc->sc_irq_rman.rm_type = RMAN_ARRAY; 271 sc->sc_irq_rman.rm_descr = "IXP4XX IRQs"; 272 if (rman_init(&sc->sc_irq_rman) != 0 || 273 rman_manage_region(&sc->sc_irq_rman, 0, cpu_is_ixp43x() ? 63 : 31) != 0) 274 panic("%s: failed to set up IRQ rman", __func__); 275 276 sc->sc_mem_rman.rm_type = RMAN_ARRAY; 277 sc->sc_mem_rman.rm_descr = "IXP4XX Memory"; 278 if (rman_init(&sc->sc_mem_rman) != 0 || 279 rman_manage_region(&sc->sc_mem_rman, 0, ~0) != 0) 280 panic("%s: failed to set up memory rman", __func__); 281 282 BUS_ADD_CHILD(dev, 0, "pcib", 0); 283 BUS_ADD_CHILD(dev, 0, "ixpclk", 0); 284 BUS_ADD_CHILD(dev, 0, "ixpiic", 0); 285 /* XXX move to hints? */ 286 BUS_ADD_CHILD(dev, 0, "ixpwdog", 0); 287 288 /* attach wired devices via hints */ 289 bus_enumerate_hinted_children(dev); 290 291 if (bus_space_map(sc->sc_iot, IXP425_GPIO_HWBASE, IXP425_GPIO_SIZE, 292 0, &sc->sc_gpio_ioh)) 293 panic("%s: unable to map GPIO registers", __func__); 294 if (bus_space_map(sc->sc_iot, IXP425_EXP_HWBASE, IXP425_EXP_SIZE, 295 0, &sc->sc_exp_ioh)) 296 panic("%s: unable to map Expansion Bus registers", __func__); 297 298 bus_generic_probe(dev); 299 bus_generic_attach(dev); 300 301 return (0); 302} 303 304static void 305ixp425_hinted_child(device_t bus, const char *dname, int dunit) 306{ 307 device_t child; 308 struct ixp425_ivar *ivar; 309 310 child = BUS_ADD_CHILD(bus, 0, dname, dunit); 311 ivar = IXP425_IVAR(child); 312 resource_int_value(dname, dunit, "addr", &ivar->addr); 313 resource_int_value(dname, dunit, "irq", &ivar->irq); 314} 315 316static device_t 317ixp425_add_child(device_t dev, int order, const char *name, int unit) 318{ 319 device_t child; 320 struct ixp425_ivar *ivar; 321 322 child = device_add_child_ordered(dev, order, name, unit); 323 if (child == NULL) 324 return NULL; 325 ivar = malloc(sizeof(struct ixp425_ivar), M_DEVBUF, M_NOWAIT); 326 if (ivar == NULL) { 327 device_delete_child(dev, child); 328 return NULL; 329 } 330 ivar->addr = 0; 331 ivar->irq = -1; 332 device_set_ivars(child, ivar); 333 return child; 334} 335 336static int 337ixp425_read_ivar(device_t bus, device_t child, int which, u_char *result) 338{ 339 struct ixp425_ivar *ivar = IXP425_IVAR(child); 340 341 switch (which) { 342 case IXP425_IVAR_ADDR: 343 if (ivar->addr != 0) { 344 *(uint32_t *)result = ivar->addr; 345 return 0; 346 } 347 break; 348 case IXP425_IVAR_IRQ: 349 if (ivar->irq != -1) { 350 *(int *)result = ivar->irq; 351 return 0; 352 } 353 break; 354 } 355 return EINVAL; 356} 357 358/* 359 * NB: This table handles P->V translations for regions setup with 360 * static mappings in initarm. This is used solely for calls to 361 * bus_alloc_resource_any; anything done with bus_space_map is 362 * handled elsewhere and does not require an entry here. 363 * 364 * XXX this table is also used by uart_cpu_getdev via getvbase 365 * (hence the public api) 366 */ 367struct hwvtrans { 368 uint32_t hwbase; 369 uint32_t size; 370 uint32_t vbase; 371 int isa4x; /* XXX needs special bus space tag */ 372}; 373 374static const struct hwvtrans * 375gethwvtrans(uint32_t hwbase, uint32_t size) 376{ 377 static const struct hwvtrans hwvtrans[] = { 378 /* NB: needed only for uart_cpu_getdev */ 379 { .hwbase = IXP425_UART0_HWBASE, 380 .size = IXP425_REG_SIZE, 381 .vbase = IXP425_UART0_VBASE, 382 .isa4x = 1 }, 383 { .hwbase = IXP425_UART1_HWBASE, 384 .size = IXP425_REG_SIZE, 385 .vbase = IXP425_UART1_VBASE, 386 .isa4x = 1 }, 387 { .hwbase = IXP425_PCI_HWBASE, 388 .size = IXP425_PCI_SIZE, 389 .vbase = IXP425_PCI_VBASE }, 390 { .hwbase = IXP425_PCI_MEM_HWBASE, 391 .size = IXP425_PCI_MEM_SIZE, 392 .vbase = IXP425_PCI_MEM_VBASE }, 393 { .hwbase = IXP425_EXP_BUS_CS0_HWBASE, 394 .size = IXP425_EXP_BUS_CS0_SIZE, 395 .vbase = IXP425_EXP_BUS_CS0_VBASE }, 396 /* NB: needed for ixp435 ehci controllers */ 397 { .hwbase = IXP435_USB1_HWBASE, 398 .size = IXP435_USB1_SIZE, 399 .vbase = IXP435_USB1_VBASE }, 400 { .hwbase = IXP435_USB2_HWBASE, 401 .size = IXP435_USB2_SIZE, 402 .vbase = IXP435_USB2_VBASE }, 403#ifdef CAMBRIA_GPS_VBASE 404 { .hwbase = CAMBRIA_GPS_HWBASE, 405 .size = CAMBRIA_GPS_SIZE, 406 .vbase = CAMBRIA_GPS_VBASE }, 407#endif 408#ifdef CAMBRIA_RS485_VBASE 409 { .hwbase = CAMBRIA_RS485_HWBASE, 410 .size = CAMBRIA_RS485_SIZE, 411 .vbase = CAMBRIA_RS485_VBASE }, 412#endif 413 }; 414 int i; 415 416 for (i = 0; i < sizeof hwvtrans / sizeof *hwvtrans; i++) { 417 if (hwbase >= hwvtrans[i].hwbase && 418 hwbase + size <= hwvtrans[i].hwbase + hwvtrans[i].size) 419 return &hwvtrans[i]; 420 } 421 return NULL; 422} 423 424/* XXX for uart_cpu_getdev */ 425int 426getvbase(uint32_t hwbase, uint32_t size, uint32_t *vbase) 427{ 428 const struct hwvtrans *hw; 429 430 hw = gethwvtrans(hwbase, size); 431 if (hw == NULL) 432 return (ENOENT); 433 *vbase = hwbase - hw->hwbase + hw->vbase; 434 return (0); 435} 436 437static struct resource * 438ixp425_alloc_resource(device_t dev, device_t child, int type, int *rid, 439 u_long start, u_long end, u_long count, u_int flags) 440{ 441 struct ixp425_softc *sc = device_get_softc(dev); 442 const struct hwvtrans *vtrans; 443 struct resource *rv; 444 uint32_t addr; 445 int needactivate = flags & RF_ACTIVE; 446 int irq; 447 448 flags &= ~RF_ACTIVE; 449 switch (type) { 450 case SYS_RES_IRQ: 451 /* override per hints */ 452 if (BUS_READ_IVAR(dev, child, IXP425_IVAR_IRQ, &irq) == 0) 453 start = end = irq; 454 rv = rman_reserve_resource(&sc->sc_irq_rman, start, end, count, 455 flags, child); 456 if (rv != NULL) 457 rman_set_rid(rv, *rid); 458 break; 459 460 case SYS_RES_MEMORY: 461 /* override per hints */ 462 if (BUS_READ_IVAR(dev, child, IXP425_IVAR_ADDR, &addr) == 0) { 463 start = addr; 464 /* XXX use nominal window to check for mapping */ 465 vtrans = gethwvtrans(start, 0x1000); 466 if (vtrans != NULL) { 467 /* 468 * Assign the entire mapped region; this may 469 * not be correct but without more info from 470 * the caller we cannot tell. 471 */ 472 end = start + vtrans->size - 473 (start - vtrans->hwbase); 474 if (bootverbose) 475 device_printf(child, 476 "%s: assign 0x%lx:0x%lx%s\n", 477 __func__, start, end - start, 478 vtrans->isa4x ? " A4X" : ""); 479 } 480 } else 481 vtrans = gethwvtrans(start, end - start); 482 if (vtrans == NULL) { 483 /* likely means above table needs to be updated */ 484 device_printf(child, "%s: no mapping for 0x%lx:0x%lx\n", 485 __func__, start, end - start); 486 return NULL; 487 } 488 rv = rman_reserve_resource(&sc->sc_mem_rman, start, end, 489 end - start, flags, child); 490 if (rv == NULL) { 491 device_printf(child, "%s: cannot reserve 0x%lx:0x%lx\n", 492 __func__, start, end - start); 493 return NULL; 494 } 495 rman_set_rid(rv, *rid); 496 break; 497 default: 498 rv = NULL; 499 break; 500 } 501 if (rv != NULL && needactivate) { 502 if (bus_activate_resource(child, type, *rid, rv)) { 503 rman_release_resource(rv); 504 return (NULL); 505 } 506 } 507 return (rv); 508} 509 510static int 511ixp425_release_resource(device_t bus, device_t child, int type, int rid, 512 struct resource *r) 513{ 514 /* NB: no private resources, just release */ 515 return rman_release_resource(r); 516} 517 518static int 519ixp425_activate_resource(device_t dev, device_t child, int type, int rid, 520 struct resource *r) 521{ 522 struct ixp425_softc *sc = device_get_softc(dev); 523 const struct hwvtrans *vtrans; 524 525 if (type == SYS_RES_MEMORY) { 526 vtrans = gethwvtrans(rman_get_start(r), rman_get_size(r)); 527 if (vtrans == NULL) { /* NB: should not happen */ 528 device_printf(child, "%s: no mapping for 0x%lx:0x%lx\n", 529 __func__, rman_get_start(r), rman_get_size(r)); 530 return (ENOENT); 531 } 532 if (vtrans->isa4x) 533 rman_set_bustag(r, &ixp425_a4x_bs_tag); 534 else 535 rman_set_bustag(r, sc->sc_iot); 536 rman_set_bushandle(r, vtrans->vbase); 537 } 538 return (rman_activate_resource(r)); 539} 540 541static int 542ixp425_deactivate_resource(device_t bus, device_t child, int type, int rid, 543 struct resource *r) 544{ 545 /* NB: no private resources, just deactive */ 546 return (rman_deactivate_resource(r)); 547} 548 549static __inline void 550get_masks(struct resource *res, uint32_t *mask, uint32_t *mask2) 551{ 552 int i; 553 554 *mask = 0; 555 for (i = rman_get_start(res); i < 32 && i <= rman_get_end(res); i++) 556 *mask |= 1 << i; 557 *mask2 = 0; 558 for (; i <= rman_get_end(res); i++) 559 *mask2 |= 1 << (i - 32); 560} 561 562static __inline void 563update_masks(uint32_t mask, uint32_t mask2) 564{ 565 566 intr_enabled = mask; 567 ixp425_set_intrmask(); 568 if (cpu_is_ixp43x()) { 569 intr_enabled2 = mask2; 570 ixp435_set_intrmask(); 571 } 572} 573 574static int 575ixp425_setup_intr(device_t dev, device_t child, 576 struct resource *res, int flags, driver_filter_t *filt, 577 driver_intr_t *intr, void *arg, void **cookiep) 578{ 579 uint32_t mask, mask2; 580 581 BUS_SETUP_INTR(device_get_parent(dev), child, res, flags, filt, intr, 582 arg, cookiep); 583 584 get_masks(res, &mask, &mask2); 585 update_masks(intr_enabled | mask, intr_enabled2 | mask2); 586 587 return (0); 588} 589 590static int 591ixp425_teardown_intr(device_t dev, device_t child, struct resource *res, 592 void *cookie) 593{ 594 uint32_t mask, mask2; 595 596 get_masks(res, &mask, &mask2); 597 update_masks(intr_enabled &~ mask, intr_enabled2 &~ mask2); 598 599 return (BUS_TEARDOWN_INTR(device_get_parent(dev), child, res, cookie)); 600} 601 602static device_method_t ixp425_methods[] = { 603 /* Device interface */ 604 DEVMETHOD(device_probe, ixp425_probe), 605 DEVMETHOD(device_attach, ixp425_attach), 606 DEVMETHOD(device_identify, ixp425_identify), 607 608 /* Bus interface */ 609 DEVMETHOD(bus_add_child, ixp425_add_child), 610 DEVMETHOD(bus_hinted_child, ixp425_hinted_child), 611 DEVMETHOD(bus_read_ivar, ixp425_read_ivar), 612 613 DEVMETHOD(bus_alloc_resource, ixp425_alloc_resource), 614 DEVMETHOD(bus_release_resource, ixp425_release_resource), 615 DEVMETHOD(bus_activate_resource, ixp425_activate_resource), 616 DEVMETHOD(bus_deactivate_resource, ixp425_deactivate_resource), 617 DEVMETHOD(bus_setup_intr, ixp425_setup_intr), 618 DEVMETHOD(bus_teardown_intr, ixp425_teardown_intr), 619 620 {0, 0}, 621}; 622 623static driver_t ixp425_driver = { 624 "ixp", 625 ixp425_methods, 626 sizeof(struct ixp425_softc), 627}; 628static devclass_t ixp425_devclass; 629 630DRIVER_MODULE(ixp, nexus, ixp425_driver, ixp425_devclass, 0, 0); 631