1263936Sbr/*- 2263936Sbr * Copyright (c) 2014 Ruslan Bukin <br@bsdpad.com> 3263936Sbr * All rights reserved. 4263936Sbr * 5263936Sbr * Redistribution and use in source and binary forms, with or without 6263936Sbr * modification, are permitted provided that the following conditions 7263936Sbr * are met: 8263936Sbr * 1. Redistributions of source code must retain the above copyright 9263936Sbr * notice, this list of conditions and the following disclaimer. 10263936Sbr * 2. Redistributions in binary form must reproduce the above copyright 11263936Sbr * notice, this list of conditions and the following disclaimer in the 12263936Sbr * documentation and/or other materials provided with the distribution. 13263936Sbr * 14263936Sbr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15263936Sbr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16263936Sbr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17263936Sbr * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18263936Sbr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19263936Sbr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20263936Sbr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21263936Sbr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22263936Sbr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23263936Sbr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24263936Sbr * SUCH DAMAGE. 25263936Sbr */ 26263936Sbr 27263936Sbr/* 28263936Sbr * Samsung Exynos 5 Interrupt Combiner 29263936Sbr * Chapter 7, Exynos 5 Dual User's Manual Public Rev 1.00 30263936Sbr */ 31291405Szbb#ifdef USB_GLOBAL_INCLUDE_FILE 32291405Szbb#include USB_GLOBAL_INCLUDE_FILE 33291405Szbb#else 34263936Sbr#include <sys/cdefs.h> 35263936Sbr__FBSDID("$FreeBSD: releng/11.0/sys/arm/samsung/exynos/exynos5_combiner.c 291405 2015-11-27 18:22:04Z zbb $"); 36263936Sbr 37263936Sbr#include <sys/param.h> 38263936Sbr#include <sys/systm.h> 39263936Sbr#include <sys/bus.h> 40263936Sbr#include <sys/kernel.h> 41263936Sbr#include <sys/module.h> 42263936Sbr#include <sys/malloc.h> 43263936Sbr#include <sys/rman.h> 44263936Sbr#include <sys/timeet.h> 45263936Sbr#include <sys/timetc.h> 46263936Sbr#include <sys/watchdog.h> 47263936Sbr 48263936Sbr#include <dev/ofw/openfirm.h> 49263936Sbr#include <dev/ofw/ofw_bus.h> 50263936Sbr#include <dev/ofw/ofw_bus_subr.h> 51263936Sbr 52263936Sbr#include <machine/bus.h> 53263936Sbr#include <machine/cpu.h> 54263936Sbr#include <machine/intr.h> 55291405Szbb#endif 56263936Sbr 57263936Sbr#include <arm/samsung/exynos/exynos5_common.h> 58263936Sbr#include <arm/samsung/exynos/exynos5_combiner.h> 59263936Sbr 60263936Sbr#define NGRP 32 61263936Sbr 62263936Sbr#define IESR(n) (0x10 * n + 0x0) /* Interrupt enable set */ 63263936Sbr#define IECR(n) (0x10 * n + 0x4) /* Interrupt enable clear */ 64263936Sbr#define ISTR(n) (0x10 * n + 0x8) /* Interrupt status */ 65263936Sbr#define IMSR(n) (0x10 * n + 0xC) /* Interrupt masked status */ 66263936Sbr#define CIPSR 0x100 /* Combined interrupt pending */ 67263936Sbr 68263936Sbrstruct combiner_softc { 69263936Sbr struct resource *res[1 + NGRP]; 70263936Sbr bus_space_tag_t bst; 71263936Sbr bus_space_handle_t bsh; 72263936Sbr void *ih[NGRP]; 73263936Sbr device_t dev; 74263936Sbr}; 75263936Sbr 76263936Sbrstruct combiner_softc *combiner_sc; 77263936Sbr 78263936Sbrstatic struct resource_spec combiner_spec[] = { 79263936Sbr { SYS_RES_MEMORY, 0, RF_ACTIVE }, 80263936Sbr { SYS_RES_IRQ, 0, RF_ACTIVE }, 81263936Sbr { SYS_RES_IRQ, 1, RF_ACTIVE }, 82263936Sbr { SYS_RES_IRQ, 2, RF_ACTIVE }, 83263936Sbr { SYS_RES_IRQ, 3, RF_ACTIVE }, 84263936Sbr { SYS_RES_IRQ, 4, RF_ACTIVE }, 85263936Sbr { SYS_RES_IRQ, 5, RF_ACTIVE }, 86263936Sbr { SYS_RES_IRQ, 6, RF_ACTIVE }, 87263936Sbr { SYS_RES_IRQ, 7, RF_ACTIVE }, 88263936Sbr { SYS_RES_IRQ, 8, RF_ACTIVE }, 89263936Sbr { SYS_RES_IRQ, 9, RF_ACTIVE }, 90263936Sbr { SYS_RES_IRQ, 10, RF_ACTIVE }, 91263936Sbr { SYS_RES_IRQ, 11, RF_ACTIVE }, 92263936Sbr { SYS_RES_IRQ, 12, RF_ACTIVE }, 93263936Sbr { SYS_RES_IRQ, 13, RF_ACTIVE }, 94263936Sbr { SYS_RES_IRQ, 14, RF_ACTIVE }, 95263936Sbr { SYS_RES_IRQ, 15, RF_ACTIVE }, 96263936Sbr { SYS_RES_IRQ, 16, RF_ACTIVE }, 97263936Sbr { SYS_RES_IRQ, 17, RF_ACTIVE }, 98263936Sbr { SYS_RES_IRQ, 18, RF_ACTIVE }, 99263936Sbr { SYS_RES_IRQ, 19, RF_ACTIVE }, 100263936Sbr { SYS_RES_IRQ, 20, RF_ACTIVE }, 101263936Sbr { SYS_RES_IRQ, 21, RF_ACTIVE }, 102263936Sbr { SYS_RES_IRQ, 22, RF_ACTIVE }, 103263936Sbr { SYS_RES_IRQ, 23, RF_ACTIVE }, 104263936Sbr { SYS_RES_IRQ, 24, RF_ACTIVE }, 105263936Sbr { SYS_RES_IRQ, 25, RF_ACTIVE }, 106263936Sbr { SYS_RES_IRQ, 26, RF_ACTIVE }, 107263936Sbr { SYS_RES_IRQ, 27, RF_ACTIVE }, 108263936Sbr { SYS_RES_IRQ, 28, RF_ACTIVE }, 109263936Sbr { SYS_RES_IRQ, 29, RF_ACTIVE }, 110263936Sbr { SYS_RES_IRQ, 30, RF_ACTIVE }, 111263936Sbr { SYS_RES_IRQ, 31, RF_ACTIVE }, 112263936Sbr { -1, 0 } 113263936Sbr}; 114263936Sbr 115263936Sbrstruct combiner_entry { 116263936Sbr int combiner_id; 117263936Sbr int bit; 118263936Sbr char *source_name; 119263936Sbr}; 120263936Sbr 121269178Sbrstatic struct combiner_entry interrupt_table[] = { 122263936Sbr { 63, 1, "EINT[15]" }, 123263936Sbr { 63, 0, "EINT[14]" }, 124263936Sbr { 62, 1, "EINT[13]" }, 125263936Sbr { 62, 0, "EINT[12]" }, 126263936Sbr { 61, 1, "EINT[11]" }, 127263936Sbr { 61, 0, "EINT[10]" }, 128263936Sbr { 60, 1, "EINT[9]" }, 129263936Sbr { 60, 0, "EINT[8]" }, 130263936Sbr { 59, 1, "EINT[7]" }, 131263936Sbr { 59, 0, "EINT[6]" }, 132263936Sbr { 58, 1, "EINT[5]" }, 133263936Sbr { 58, 0, "EINT[4]" }, 134263936Sbr { 57, 3, "MCT_G3" }, 135263936Sbr { 57, 2, "MCT_G2" }, 136263936Sbr { 57, 1, "EINT[3]" }, 137263936Sbr { 57, 0, "EINT[2]" }, 138263936Sbr { 56, 6, "SYSMMU_G2D[1]" }, 139263936Sbr { 56, 5, "SYSMMU_G2D[0]" }, 140263936Sbr { 56, 2, "SYSMMU_FIMC_LITE1[1]" }, 141263936Sbr { 56, 1, "SYSMMU_FIMC_LITE1[0]" }, 142263936Sbr { 56, 0, "EINT[1]" }, 143263936Sbr { 55, 4, "MCT_G1" }, 144263936Sbr { 55, 3, "MCT_G0" }, 145263936Sbr { 55, 0, "EINT[0]" }, 146269178Sbr { 54, 7, "CPU_nCNTVIRQ[1]" }, 147269178Sbr { 54, 6, "CPU_nCTIIRQ[1]" }, 148269178Sbr { 54, 5, "CPU_nCNTPSIRQ[1]" }, 149269178Sbr { 54, 4, "CPU_nPMUIRQ[1]" }, 150269178Sbr { 54, 3, "CPU_nCNTPNSIRQ[1]" }, 151269178Sbr { 54, 2, "CPU_PARITYFAILSCU[1]" }, 152269178Sbr { 54, 1, "CPU_nCNTHPIRQ[1]" }, 153269178Sbr { 54, 0, "PARITYFAIL[1]" }, 154269178Sbr { 53, 1, "CPU_nIRQ[1]" }, 155269178Sbr { 52, 0, "CPU_nIRQ[0]" }, 156269178Sbr { 51, 7, "CPU_nRAMERRIRQ" }, 157269178Sbr { 51, 6, "CPU_nAXIERRIRQ" }, 158269178Sbr { 51, 4, "INT_COMB_ISP_GIC" }, 159269178Sbr { 51, 3, "INT_COMB_IOP_GIC" }, 160269178Sbr { 51, 2, "CCI_nERRORIRQ" }, 161269178Sbr { 51, 1, "INT_COMB_ARMISP_GIC" }, 162269178Sbr { 51, 0, "INT_COMB_ARMIOP_GIC" }, 163269178Sbr { 50, 7, "DISP1[3]" }, 164269178Sbr { 50, 6, "DISP1[2]" }, 165269178Sbr { 50, 5, "DISP1[1]" }, 166269178Sbr { 50, 4, "DISP1[0]" }, 167269178Sbr { 49, 3, "SSCM_PULSE_IRQ_C2CIF[1]" }, 168269178Sbr { 49, 2, "SSCM_PULSE_IRQ_C2CIF[0]" }, 169269178Sbr { 49, 1, "SSCM_IRQ_C2CIF[1]" }, 170269178Sbr { 49, 0, "SSCM_IRQ_C2CIF[0]" }, 171269178Sbr { 48, 3, "PEREV_M1_CDREX" }, 172269178Sbr { 48, 2, "PEREV_M0_CDREX" }, 173269178Sbr { 48, 1, "PEREV_A1_CDREX" }, 174269178Sbr { 48, 0, "PEREV_A0_CDREX" }, 175269178Sbr { 47, 3, "MDMA0_ABORT" }, 176269178Sbr /* 46 is fully reserved */ 177269178Sbr { 45, 1, "MDMA1_ABORT" }, 178269178Sbr /* 44 is fully reserved */ 179269178Sbr { 43, 7, "SYSMMU_DRCISP[1]" }, 180269178Sbr { 43, 6, "SYSMMU_DRCISP[0]" }, 181269178Sbr { 43, 1, "SYSMMU_ODC[1]" }, 182269178Sbr { 43, 0, "SYSMMU_ODC[0]" }, 183269178Sbr { 42, 7, "SYSMMU_ISP[1]" }, 184269178Sbr { 42, 6, "SYSMMU_ISP[0]" }, 185269178Sbr { 42, 5, "SYSMMU_DIS0[1]" }, 186269178Sbr { 42, 4, "SYSMMU_DIS0[0]" }, 187269178Sbr { 42, 3, "DP1" }, 188269178Sbr { 41, 5, "SYSMMU_DIS1[1]" }, 189269178Sbr { 41, 4, "SYSMMU_DIS1[0]" }, 190269178Sbr { 40, 6, "SYSMMU_MFCL[1]" }, 191269178Sbr { 40, 5, "SYSMMU_MFCL[0]" }, 192269178Sbr { 39, 5, "SYSMMU_TV_M0[1]" }, 193269178Sbr { 39, 4, "SYSMMU_TV_M0[0]" }, 194269178Sbr { 39, 3, "SYSMMU_MDMA1[1]" }, 195269178Sbr { 39, 2, "SYSMMU_MDMA1[0]" }, 196269178Sbr { 39, 1, "SYSMMU_MDMA0[1]" }, 197269178Sbr { 39, 0, "SYSMMU_MDMA0[0]" }, 198269178Sbr { 38, 7, "SYSMMU_SSS[1]" }, 199269178Sbr { 38, 6, "SYSMMU_SSS[0]" }, 200269178Sbr { 38, 5, "SYSMMU_RTIC[1]" }, 201269178Sbr { 38, 4, "SYSMMU_RTIC[0]" }, 202269178Sbr { 38, 3, "SYSMMU_MFCR[1]" }, 203269178Sbr { 38, 2, "SYSMMU_MFCR[0]" }, 204269178Sbr { 38, 1, "SYSMMU_ARM[1]" }, 205269178Sbr { 38, 0, "SYSMMU_ARM[0]" }, 206269178Sbr { 37, 7, "SYSMMU_3DNR[1]" }, 207269178Sbr { 37, 6, "SYSMMU_3DNR[0]" }, 208269178Sbr { 37, 5, "SYSMMU_MCUISP[1]" }, 209269178Sbr { 37, 4, "SYSMMU_MCUISP[0]" }, 210269178Sbr { 37, 3, "SYSMMU_SCALERCISP[1]" }, 211269178Sbr { 37, 2, "SYSMMU_SCALERCISP[0]" }, 212269178Sbr { 37, 1, "SYSMMU_FDISP[1]" }, 213269178Sbr { 37, 0, "SYSMMU_FDISP[0]" }, 214269178Sbr { 36, 7, "MCUIOP_CTIIRQ" }, 215269178Sbr { 36, 6, "MCUIOP_PMUIRQ" }, 216269178Sbr { 36, 5, "MCUISP_CTIIRQ" }, 217269178Sbr { 36, 4, "MCUISP_PMUIRQ" }, 218269178Sbr { 36, 3, "SYSMMU_JPEGX[1]" }, 219269178Sbr { 36, 2, "SYSMMU_JPEGX[0]" }, 220269178Sbr { 36, 1, "SYSMMU_ROTATOR[1]" }, 221269178Sbr { 36, 0, "SYSMMU_ROTATOR[0]" }, 222269178Sbr { 35, 7, "SYSMMU_SCALERPISP[1]" }, 223269178Sbr { 35, 6, "SYSMMU_SCALERPISP[0]" }, 224269178Sbr { 35, 5, "SYSMMU_FIMC_LITE0[1]" }, 225269178Sbr { 35, 4, "SYSMMU_FIMC_LITE0[0]" }, 226269178Sbr { 35, 3, "SYSMMU_DISP1_M0[1]" }, 227269178Sbr { 35, 2, "SYSMMU_DISP1_M0[0]" }, 228269178Sbr { 35, 1, "SYSMMU_FIMC_LITE2[1]" }, 229269178Sbr { 35, 0, "SYSMMU_FIMC_LITE2[0]" }, 230269178Sbr { 34, 7, "SYSMMU_GSCL3[1]" }, 231269178Sbr { 34, 6, "SYSMMU_GSCL3[0]" }, 232269178Sbr { 34, 5, "SYSMMU_GSCL2[1]" }, 233269178Sbr { 34, 4, "SYSMMU_GSCL2[0]" }, 234269178Sbr { 34, 3, "SYSMMU_GSCL1[1]" }, 235269178Sbr { 34, 2, "SYSMMU_GSCL1[0]" }, 236269178Sbr { 34, 1, "SYSMMU_GSCL0[1]" }, 237269178Sbr { 34, 0, "SYSMMU_GSCL0[0]" }, 238269178Sbr { 33, 7, "CPU_nCNTVIRQ[0]" }, 239269178Sbr { 33, 6, "CPU_nCNTPSIRQ[0]" }, 240269178Sbr { 33, 5, "CPU_nCNTPSNIRQ[0]" }, 241269178Sbr { 33, 4, "CPU_nCNTHPIRQ[0]" }, 242269178Sbr { 33, 3, "CPU_nCTIIRQ[0]" }, 243269178Sbr { 33, 2, "CPU_nPMUIRQ[0]" }, 244269178Sbr { 33, 1, "CPU_PARITYFAILSCU[0]" }, 245269178Sbr { 33, 0, "CPU_PARITYFAIL0" }, 246269178Sbr { 32, 7, "TZASC_XR1BXW" }, 247269178Sbr { 32, 6, "TZASC_XR1BXR" }, 248269178Sbr { 32, 5, "TZASC_XLBXW" }, 249269178Sbr { 32, 4, "TZASC_XLBXR" }, 250269178Sbr { 32, 3, "TZASC_DRBXW" }, 251269178Sbr { 32, 2, "TZASC_DRBXR" }, 252269178Sbr { 32, 1, "TZASC_CBXW" }, 253269178Sbr { 32, 0, "TZASC_CBXR" }, 254263936Sbr 255269178Sbr { -1, -1, NULL }, 256263936Sbr}; 257263936Sbr 258263936Sbrstruct combined_intr { 259263936Sbr uint32_t enabled; 260263936Sbr void (*ih) (void *); 261263936Sbr void *ih_user; 262263936Sbr}; 263263936Sbr 264263936Sbrstatic struct combined_intr intr_map[32][8]; 265263936Sbr 266263936Sbrstatic void 267263936Sbrcombiner_intr(void *arg) 268263936Sbr{ 269263936Sbr struct combiner_softc *sc; 270263936Sbr void (*ih) (void *); 271263936Sbr void *ih_user; 272263936Sbr int enabled; 273263936Sbr int intrs; 274263936Sbr int shift; 275263936Sbr int cirq; 276263936Sbr int grp; 277263936Sbr int i,n; 278263936Sbr 279263936Sbr sc = arg; 280263936Sbr 281263936Sbr intrs = READ4(sc, CIPSR); 282263936Sbr for (grp = 0; grp < 32; grp++) { 283263936Sbr if (intrs & (1 << grp)) { 284263936Sbr n = (grp / 4); 285263936Sbr shift = (grp % 4) * 8; 286263936Sbr 287263936Sbr cirq = READ4(sc, ISTR(n)); 288263936Sbr for (i = 0; i < 8; i++) { 289263936Sbr if (cirq & (1 << (i + shift))) { 290263936Sbr ih = intr_map[grp][i].ih; 291263936Sbr ih_user = intr_map[grp][i].ih_user; 292263936Sbr enabled = intr_map[grp][i].enabled; 293263936Sbr if (enabled && (ih != NULL)) { 294263936Sbr ih(ih_user); 295263936Sbr } 296263936Sbr } 297263936Sbr } 298263936Sbr } 299263936Sbr } 300263936Sbr} 301263936Sbr 302263936Sbrvoid 303263936Sbrcombiner_setup_intr(char *source_name, void (*ih)(void *), void *ih_user) 304263936Sbr{ 305263936Sbr struct combiner_entry *entry; 306263936Sbr struct combined_intr *cirq; 307263936Sbr struct combiner_softc *sc; 308263936Sbr int shift; 309263936Sbr int reg; 310263936Sbr int grp; 311263936Sbr int n; 312263936Sbr int i; 313263936Sbr 314263936Sbr sc = combiner_sc; 315263936Sbr 316263936Sbr if (sc == NULL) { 317263936Sbr device_printf(sc->dev, "Error: combiner is not attached\n"); 318263936Sbr return; 319263936Sbr } 320263936Sbr 321263936Sbr entry = NULL; 322263936Sbr 323269178Sbr for (i = 0; i < NGRP && interrupt_table[i].bit != -1; i++) { 324263936Sbr if (strcmp(interrupt_table[i].source_name, source_name) == 0) { 325263936Sbr entry = &interrupt_table[i]; 326263936Sbr } 327263936Sbr } 328263936Sbr 329263936Sbr if (entry == NULL) { 330263936Sbr device_printf(sc->dev, "Can't find interrupt name %s\n", 331263936Sbr source_name); 332263936Sbr return; 333263936Sbr } 334263936Sbr 335263936Sbr#if 0 336263936Sbr device_printf(sc->dev, "Setting up interrupt %s\n", source_name); 337263936Sbr#endif 338263936Sbr 339263936Sbr grp = entry->combiner_id - 32; 340263936Sbr 341263936Sbr cirq = &intr_map[grp][entry->bit]; 342263936Sbr cirq->enabled = 1; 343263936Sbr cirq->ih = ih; 344263936Sbr cirq->ih_user = ih_user; 345263936Sbr 346263936Sbr n = grp / 4; 347263936Sbr shift = (grp % 4) * 8 + entry->bit; 348263936Sbr 349263936Sbr reg = (1 << shift); 350263936Sbr WRITE4(sc, IESR(n), reg); 351263936Sbr} 352263936Sbr 353263936Sbrstatic int 354263936Sbrcombiner_probe(device_t dev) 355263936Sbr{ 356263936Sbr 357269369Sbr if (!ofw_bus_status_okay(dev)) 358269369Sbr return (ENXIO); 359269369Sbr 360263936Sbr if (!ofw_bus_is_compatible(dev, "exynos,combiner")) 361263936Sbr return (ENXIO); 362263936Sbr 363263936Sbr device_set_desc(dev, "Samsung Exynos 5 Interrupt Combiner"); 364263936Sbr return (BUS_PROBE_DEFAULT); 365263936Sbr} 366263936Sbr 367263936Sbrstatic int 368263936Sbrcombiner_attach(device_t dev) 369263936Sbr{ 370263936Sbr struct combiner_softc *sc; 371263936Sbr int err; 372263936Sbr int i; 373263936Sbr 374263936Sbr sc = device_get_softc(dev); 375263936Sbr sc->dev = dev; 376263936Sbr 377263936Sbr if (bus_alloc_resources(dev, combiner_spec, sc->res)) { 378263936Sbr device_printf(dev, "could not allocate resources\n"); 379263936Sbr return (ENXIO); 380263936Sbr } 381263936Sbr 382263936Sbr /* Memory interface */ 383263936Sbr sc->bst = rman_get_bustag(sc->res[0]); 384263936Sbr sc->bsh = rman_get_bushandle(sc->res[0]); 385263936Sbr 386263936Sbr combiner_sc = sc; 387263936Sbr 388263936Sbr /* Setup interrupt handler */ 389263936Sbr for (i = 0; i < NGRP; i++) { 390263936Sbr err = bus_setup_intr(dev, sc->res[1+i], INTR_TYPE_BIO | \ 391263936Sbr INTR_MPSAFE, NULL, combiner_intr, sc, &sc->ih[i]); 392263936Sbr if (err) { 393263936Sbr device_printf(dev, "Unable to alloc int resource.\n"); 394263936Sbr return (ENXIO); 395263936Sbr } 396263936Sbr } 397263936Sbr 398263936Sbr return (0); 399263936Sbr} 400263936Sbr 401263936Sbrstatic device_method_t combiner_methods[] = { 402263936Sbr DEVMETHOD(device_probe, combiner_probe), 403263936Sbr DEVMETHOD(device_attach, combiner_attach), 404263936Sbr { 0, 0 } 405263936Sbr}; 406263936Sbr 407263936Sbrstatic driver_t combiner_driver = { 408263936Sbr "combiner", 409263936Sbr combiner_methods, 410263936Sbr sizeof(struct combiner_softc), 411263936Sbr}; 412263936Sbr 413263936Sbrstatic devclass_t combiner_devclass; 414263936Sbr 415263936SbrDRIVER_MODULE(combiner, simplebus, combiner_driver, combiner_devclass, 0, 0); 416