Home | History | Annotate | Line # | Download | only in pci
pcib.c revision 1.16
      1 /*	$NetBSD: pcib.c,v 1.16 2004/08/30 15:05:15 drochner Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2000, 2001 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * This code is derived from software contributed to The NetBSD Foundation
      8  * by Jason R. Thorpe.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  * 3. All advertising materials mentioning features or use of this software
     19  *    must display the following acknowledgement:
     20  *	This product includes software developed by the NetBSD
     21  *	Foundation, Inc. and its contributors.
     22  * 4. Neither the name of The NetBSD Foundation nor the names of its
     23  *    contributors may be used to endorse or promote products derived
     24  *    from this software without specific prior written permission.
     25  *
     26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     36  * POSSIBILITY OF SUCH DAMAGE.
     37  */
     38 
     39 #include <sys/cdefs.h>			/* RCS ID & Copyright macro defns */
     40 
     41 __KERNEL_RCSID(0, "$NetBSD: pcib.c,v 1.16 2004/08/30 15:05:15 drochner Exp $");
     42 
     43 #include "opt_algor_p5064.h"
     44 #include "opt_algor_p6032.h"
     45 
     46 #include <sys/param.h>
     47 #include <sys/systm.h>
     48 #include <sys/kernel.h>
     49 #include <sys/device.h>
     50 #include <sys/malloc.h>
     51 
     52 #include <machine/intr.h>
     53 #include <machine/bus.h>
     54 
     55 #include <dev/isa/isareg.h>
     56 #include <dev/isa/isavar.h>
     57 
     58 #include <dev/pci/pcireg.h>
     59 #include <dev/pci/pcivar.h>
     60 #include <dev/pci/pcidevs.h>
     61 
     62 #include <dev/ic/i8259reg.h>
     63 
     64 #ifdef ALGOR_P5064
     65 #include <algor/algor/algor_p5064var.h>
     66 #endif
     67 
     68 #ifdef ALGOR_P6032
     69 #include <algor/algor/algor_p6032var.h>
     70 #endif
     71 
     72 const char *pcib_intrnames[16] = {
     73 	"irq 0",
     74 	"irq 1",
     75 	"irq 2",
     76 	"irq 3",
     77 	"irq 4",
     78 	"irq 5",
     79 	"irq 6",
     80 	"irq 7",
     81 	"irq 8",
     82 	"irq 9",
     83 	"irq 10",
     84 	"irq 11",
     85 	"irq 12",
     86 	"irq 13",
     87 	"irq 14",
     88 	"irq 15",
     89 };
     90 
     91 struct pcib_intrhead {
     92 	LIST_HEAD(, algor_intrhand) intr_q;
     93 	struct evcnt intr_count;
     94 	int intr_type;
     95 };
     96 
     97 struct pcib_softc {
     98 	struct device	sc_dev;
     99 
    100 	bus_space_tag_t	sc_iot;
    101 	bus_space_handle_t sc_ioh_icu1;
    102 	bus_space_handle_t sc_ioh_icu2;
    103 	bus_space_handle_t sc_ioh_elcr;
    104 
    105 	struct algor_isa_chipset sc_ic;
    106 
    107 	struct pcib_intrhead sc_intrtab[16];
    108 
    109 	u_int16_t	sc_imask;
    110 	u_int16_t	sc_elcr;
    111 
    112 #if defined(ALGOR_P5064)
    113 	isa_chipset_tag_t sc_parent_ic;
    114 #endif
    115 
    116 	u_int16_t	sc_reserved;
    117 
    118 	void		*sc_ih;
    119 };
    120 
    121 int	pcib_match(struct device *, struct cfdata *, void *);
    122 void	pcib_attach(struct device *, struct device *, void *);
    123 
    124 CFATTACH_DECL(pcib, sizeof(struct pcib_softc),
    125     pcib_match, pcib_attach, NULL, NULL);
    126 
    127 void	pcib_isa_attach_hook(struct device *, struct device *,
    128 	    struct isabus_attach_args *);
    129 
    130 int	pcib_intr(void *);
    131 
    132 void	pcib_bridge_callback(struct device *);
    133 
    134 const struct evcnt *pcib_isa_intr_evcnt(void *, int);
    135 void	*pcib_isa_intr_establish(void *, int, int, int,
    136 	    int (*)(void *), void *);
    137 void	pcib_isa_intr_disestablish(void *, void *);
    138 int	pcib_isa_intr_alloc(void *, int, int, int *);
    139 
    140 void	pcib_set_icus(struct pcib_softc *);
    141 
    142 int
    143 pcib_match(struct device *parent, struct cfdata *match, void *aux)
    144 {
    145 	struct pci_attach_args *pa = aux;
    146 
    147 	if (PCI_CLASS(pa->pa_class) == PCI_CLASS_BRIDGE &&
    148 	    PCI_SUBCLASS(pa->pa_class) == PCI_SUBCLASS_BRIDGE_ISA)
    149 		return (1);
    150 
    151 	return (0);
    152 }
    153 
    154 void
    155 pcib_attach(struct device *parent, struct device *self, void *aux)
    156 {
    157 	struct pcib_softc *sc = (void *) self;
    158 	struct pci_attach_args *pa = aux;
    159 	char devinfo[256];
    160 	int i;
    161 
    162 	pci_devinfo(pa->pa_id, pa->pa_class, 0, devinfo, sizeof(devinfo));
    163 	printf(": %s (rev. 0x%02x)\n", devinfo,
    164 	    PCI_REVISION(pa->pa_class));
    165 
    166 	sc->sc_iot = pa->pa_iot;
    167 
    168 	/*
    169 	 * Map the PIC/ELCR registers.
    170 	 */
    171 	if (bus_space_map(sc->sc_iot, 0x4d0, 2, 0, &sc->sc_ioh_elcr) != 0)
    172 		printf("%s: unable to map ELCR registers\n",
    173 		    sc->sc_dev.dv_xname);
    174 	if (bus_space_map(sc->sc_iot, IO_ICU1, 2, 0, &sc->sc_ioh_icu1) != 0)
    175 		printf("%s: unable to map ICU1 registers\n",
    176 		    sc->sc_dev.dv_xname);
    177 	if (bus_space_map(sc->sc_iot, IO_ICU2, 2, 0, &sc->sc_ioh_icu2) != 0)
    178 		printf("%s: unable to map ICU2 registers\n",
    179 		    sc->sc_dev.dv_xname);
    180 
    181 	/* All interrupts default to "masked off". */
    182 	sc->sc_imask = 0xffff;
    183 
    184 	/* All interrupts default to edge-triggered. */
    185 	sc->sc_elcr = 0;
    186 
    187 	/*
    188 	 * Initialize the 8259s.
    189 	 */
    190 
    191 	/* reset, program device, 4 bytes */
    192 	bus_space_write_1(sc->sc_iot, sc->sc_ioh_icu1, PIC_ICW1,
    193 	    ICW1_SELECT | ICW1_IC4);
    194 	bus_space_write_1(sc->sc_iot, sc->sc_ioh_icu1, PIC_ICW2,
    195 	    ICW2_VECTOR(0)/*XXX*/);
    196 	bus_space_write_1(sc->sc_iot, sc->sc_ioh_icu1, PIC_ICW3,
    197 	    ICW3_CASCADE(2));
    198 	bus_space_write_1(sc->sc_iot, sc->sc_ioh_icu1, PIC_ICW4,
    199 	    ICW4_8086);
    200 
    201 	/* mask all interrupts */
    202 	bus_space_write_1(sc->sc_iot, sc->sc_ioh_icu1, PIC_OCW1,
    203 	    sc->sc_imask & 0xff);
    204 
    205 	/* enable special mask mode */
    206 	bus_space_write_1(sc->sc_iot, sc->sc_ioh_icu1, PIC_OCW3,
    207 	    OCW3_SELECT | OCW3_SSMM | OCW3_SMM);
    208 
    209 	/* read IRR by default */
    210 	bus_space_write_1(sc->sc_iot, sc->sc_ioh_icu1, PIC_OCW3,
    211 	    OCW3_SELECT | OCW3_RR);
    212 
    213 	/* reset; program device, 4 bytes */
    214 	bus_space_write_1(sc->sc_iot, sc->sc_ioh_icu2, PIC_ICW1,
    215 	    ICW1_SELECT | ICW1_IC4);
    216 	bus_space_write_1(sc->sc_iot, sc->sc_ioh_icu2, PIC_ICW2,
    217 	    ICW2_VECTOR(0)/*XXX*/);
    218 	bus_space_write_1(sc->sc_iot, sc->sc_ioh_icu2, PIC_ICW3,
    219 	    ICW3_SIC(2));
    220 	bus_space_write_1(sc->sc_iot, sc->sc_ioh_icu2, PIC_ICW4,
    221 	    ICW4_8086);
    222 
    223 	/* mask all interrupts */
    224 	bus_space_write_1(sc->sc_iot, sc->sc_ioh_icu2, PIC_OCW1,
    225 	    (sc->sc_imask >> 8) & 0xff);
    226 
    227 	/* enable special mask mode */
    228 	bus_space_write_1(sc->sc_iot, sc->sc_ioh_icu2, PIC_OCW3,
    229 	    OCW3_SELECT | OCW3_SSMM | OCW3_SMM);
    230 
    231 	/* read IRR by default */
    232 	bus_space_write_1(sc->sc_iot, sc->sc_ioh_icu2, PIC_OCW3,
    233 	    OCW3_SELECT | OCW3_RR);
    234 
    235 	/*
    236 	 * Default all interrupts to edge-triggered.
    237 	 */
    238 	bus_space_write_1(sc->sc_iot, sc->sc_ioh_elcr, 0,
    239 	    sc->sc_elcr & 0xff);
    240 	bus_space_write_1(sc->sc_iot, sc->sc_ioh_elcr, 1,
    241 	    (sc->sc_elcr >> 8) & 0xff);
    242 
    243 	/*
    244 	 * Some ISA interrupts are reserved for devices that
    245 	 * we know are hard-wired to certain IRQs.
    246 	 */
    247 	sc->sc_reserved =
    248 		(1U << 0) |	/* timer */
    249 		(1U << 1) |	/* keyboard controller */
    250 		(1U << 2) |	/* PIC cascade */
    251 		(1U << 3) |	/* COM 2 */
    252 		(1U << 4) |	/* COM 1 */
    253 		(1U << 6) |	/* floppy */
    254 		(1U << 7) |	/* centronics */
    255 		(1U << 8) |	/* RTC */
    256 		(1U << 12) |	/* keyboard controller */
    257 		(1U << 14) |	/* IDE 0 */
    258 		(1U << 15);	/* IDE 1 */
    259 
    260 #if defined(ALGOR_P5064)
    261 	/*
    262 	 * Some "ISA" interrupts are a little wacky, wired up directly
    263 	 * to the P-5064 interrupt controller.
    264 	 */
    265 	sc->sc_parent_ic = &p5064_configuration.ac_ic;
    266 #endif /* ALGOR_P5064 */
    267 
    268 	/* Set up our ISA chipset. */
    269 	sc->sc_ic.ic_v = sc;
    270 	sc->sc_ic.ic_intr_evcnt = pcib_isa_intr_evcnt;
    271 	sc->sc_ic.ic_intr_establish = pcib_isa_intr_establish;
    272 	sc->sc_ic.ic_intr_disestablish = pcib_isa_intr_disestablish;
    273 	sc->sc_ic.ic_intr_alloc = pcib_isa_intr_alloc;
    274 
    275 	/* Initialize our interrupt table. */
    276 	for (i = 0; i < 16; i++) {
    277 		LIST_INIT(&sc->sc_intrtab[i].intr_q);
    278 		evcnt_attach_dynamic(&sc->sc_intrtab[i].intr_count,
    279 		    EVCNT_TYPE_INTR, NULL, "pcib", pcib_intrnames[i]);
    280 		sc->sc_intrtab[i].intr_type = IST_NONE;
    281 	}
    282 
    283 	/* Hook up our interrupt handler. */
    284 #if defined(ALGOR_P5064)
    285 	sc->sc_ih = (*algor_intr_establish)(P5064_IRQ_ISABRIDGE,
    286 	    pcib_intr, sc);
    287 #elif defined(ALGOR_P6032)
    288 	sc->sc_ih = (*algor_intr_establish)(P6032_IRQ_ISABRIDGE,
    289 	    pcib_intr, sc);
    290 #endif
    291 	if (sc->sc_ih == NULL)
    292 		printf("%s: WARNING: unable to register interrupt handler\n",
    293 		    sc->sc_dev.dv_xname);
    294 
    295 	config_defer(self, pcib_bridge_callback);
    296 }
    297 
    298 void
    299 pcib_bridge_callback(self)
    300 	struct device *self;
    301 {
    302 	struct pcib_softc *sc = (struct pcib_softc *)self;
    303 	struct isabus_attach_args iba;
    304 
    305 	memset(&iba, 0, sizeof(iba));
    306 
    307 #if defined(ALGOR_P5064)
    308 	    {
    309 		struct p5064_config *acp = &p5064_configuration;
    310 
    311 		iba.iba_iot = &acp->ac_iot;
    312 		iba.iba_memt = &acp->ac_memt;
    313 		iba.iba_dmat = &acp->ac_isa_dmat;
    314 	    }
    315 #elif defined(ALGOR_P6032)
    316 	    {
    317 		struct p6032_config *acp = &p6032_configuration;
    318 
    319 		iba.iba_iot = &acp->ac_iot;
    320 		iba.iba_memt = &acp->ac_memt;
    321 		iba.iba_dmat = &acp->ac_isa_dmat;
    322 	    }
    323 #endif
    324 
    325 	iba.iba_ic = &sc->sc_ic;
    326 	iba.iba_ic->ic_attach_hook = pcib_isa_attach_hook;
    327 
    328 	(void) config_found_ia(&sc->sc_dev, "isabus", &iba, isabusprint);
    329 }
    330 
    331 void
    332 pcib_isa_attach_hook(struct device *parent, struct device *self,
    333     struct isabus_attach_args *iba)
    334 {
    335 
    336 	/* Nothing to do. */
    337 }
    338 
    339 void
    340 pcib_set_icus(struct pcib_softc *sc)
    341 {
    342 
    343 	/* Enable the cascade IRQ (2) if 8-15 is enabled. */
    344 	if ((sc->sc_imask & 0xff00) != 0xff00)
    345 		sc->sc_imask &= ~(1U << 2);
    346 	else
    347 		sc->sc_imask |= (1U << 2);
    348 
    349 	bus_space_write_1(sc->sc_iot, sc->sc_ioh_icu1, PIC_OCW1,
    350 	    sc->sc_imask & 0xff);
    351 	bus_space_write_1(sc->sc_iot, sc->sc_ioh_icu2, PIC_OCW1,
    352 	    (sc->sc_imask >> 8) & 0xff);
    353 
    354 	bus_space_write_1(sc->sc_iot, sc->sc_ioh_elcr, 0,
    355 	    sc->sc_elcr & 0xff);
    356 	bus_space_write_1(sc->sc_iot, sc->sc_ioh_elcr, 1,
    357 	    (sc->sc_elcr >> 8) & 0xff);
    358 }
    359 
    360 int
    361 pcib_intr(void *v)
    362 {
    363 	struct pcib_softc *sc = v;
    364 	struct algor_intrhand *ih;
    365 	int irq;
    366 
    367 	for (;;) {
    368 		bus_space_write_1(sc->sc_iot, sc->sc_ioh_icu1, PIC_OCW3,
    369 		    OCW3_SELECT | OCW3_POLL);
    370 		irq = bus_space_read_1(sc->sc_iot, sc->sc_ioh_icu1, PIC_OCW3);
    371 		if ((irq & OCW3_POLL_PENDING) == 0)
    372 			return (1);
    373 
    374 		irq = OCW3_POLL_IRQ(irq);
    375 
    376 		if (irq == 2) {
    377 			bus_space_write_1(sc->sc_iot, sc->sc_ioh_icu2,
    378 			    PIC_OCW3, OCW3_SELECT | OCW3_POLL);
    379 			irq = bus_space_read_1(sc->sc_iot, sc->sc_ioh_icu2,
    380 			    PIC_OCW3);
    381 			if (irq & OCW3_POLL_PENDING)
    382 				irq = OCW3_POLL_IRQ(irq) + 8;
    383 			else
    384 				irq = 2;
    385 		}
    386 
    387 		sc->sc_intrtab[irq].intr_count.ev_count++;
    388 		for (ih = LIST_FIRST(&sc->sc_intrtab[irq].intr_q);
    389 		     ih != NULL; ih = LIST_NEXT(ih, ih_q)) {
    390 			(*ih->ih_func)(ih->ih_arg);
    391 		}
    392 
    393 		/* Send a specific EOI to the 8259. */
    394 		if (irq > 7) {
    395 			bus_space_write_1(sc->sc_iot, sc->sc_ioh_icu2,
    396 			    PIC_OCW2, OCW2_SELECT | OCW3_EOI | OCW3_SL |
    397 			    OCW2_ILS(irq & 7));
    398 			irq = 2;
    399 		}
    400 
    401 		bus_space_write_1(sc->sc_iot, sc->sc_ioh_icu1, PIC_OCW2,
    402 		    OCW2_SELECT | OCW3_EOI | OCW3_SL | OCW2_ILS(irq));
    403 	}
    404 }
    405 
    406 const struct evcnt *
    407 pcib_isa_intr_evcnt(void *v, int irq)
    408 {
    409 	struct pcib_softc *sc = v;
    410 
    411 #if defined(ALGOR_P5064)
    412 	if (p5064_isa_to_irqmap[irq] != -1)
    413 		return (isa_intr_evcnt(sc->sc_parent_ic, irq));
    414 #endif
    415 
    416 	return (&sc->sc_intrtab[irq].intr_count);
    417 }
    418 
    419 void *
    420 pcib_isa_intr_establish(void *v, int irq, int type, int level,
    421     int (*func)(void *), void *arg)
    422 {
    423 	struct pcib_softc *sc = v;
    424 	struct algor_intrhand *ih;
    425 	int s;
    426 
    427 	if (irq > 15 || irq == 2 || type == IST_NONE)
    428 		panic("pcib_isa_intr_establish: bad irq or type");
    429 
    430 #if defined(ALGOR_P5064)
    431 	if (p5064_isa_to_irqmap[irq] != -1)
    432 		return (isa_intr_establish(sc->sc_parent_ic, irq, type,
    433 		    level, func, arg));
    434 #endif
    435 
    436 	switch (sc->sc_intrtab[irq].intr_type) {
    437 	case IST_NONE:
    438 		sc->sc_intrtab[irq].intr_type = type;
    439 		break;
    440 
    441 	case IST_EDGE:
    442 	case IST_LEVEL:
    443 		if (type == sc->sc_intrtab[irq].intr_type)
    444 			break;
    445 		/* FALLTHROUGH */
    446 	case IST_PULSE:
    447 		/*
    448 		 * We can't share interrupts in this case.
    449 		 */
    450 		return (NULL);
    451 	}
    452 
    453 	ih = malloc(sizeof(*ih), M_DEVBUF, M_NOWAIT);
    454 	if (ih == NULL)
    455 		return (NULL);
    456 
    457 	ih->ih_func = func;
    458 	ih->ih_arg = arg;
    459 	ih->ih_irq = irq;
    460 	ih->ih_irqmap = NULL;
    461 
    462 	s = splhigh();
    463 
    464 	/* Insert the handler into the table. */
    465 	LIST_INSERT_HEAD(&sc->sc_intrtab[irq].intr_q, ih, ih_q);
    466 	sc->sc_intrtab[irq].intr_type = type;
    467 
    468 	/* Enable it, set trigger mode. */
    469 	sc->sc_imask &= ~(1 << irq);
    470 	if (sc->sc_intrtab[irq].intr_type == IST_LEVEL)
    471 		sc->sc_elcr |= (1 << irq);
    472 	else
    473 		sc->sc_elcr &= ~(1 << irq);
    474 
    475 	pcib_set_icus(sc);
    476 
    477 	splx(s);
    478 
    479 	return (ih);
    480 }
    481 
    482 void
    483 pcib_isa_intr_disestablish(void *v, void *arg)
    484 {
    485 	struct pcib_softc *sc = v;
    486 	struct algor_intrhand *ih = arg;
    487 	int s;
    488 
    489 #if defined(ALGOR_P5064)
    490 	if (p5064_isa_to_irqmap[ih->ih_irq] != -1) {
    491 		isa_intr_disestablish(sc->sc_parent_ic, ih);
    492 		return;
    493 	}
    494 #endif
    495 
    496 	s = splhigh();
    497 
    498 	LIST_REMOVE(ih, ih_q);
    499 
    500 	/* If there are no more handlers on this IRQ, disable it. */
    501 	if (LIST_FIRST(&sc->sc_intrtab[ih->ih_irq].intr_q) == NULL) {
    502 		sc->sc_imask |= (1 << ih->ih_irq);
    503 		pcib_set_icus(sc);
    504 	}
    505 
    506 	splx(s);
    507 
    508 	free(ih, M_DEVBUF);
    509 }
    510 
    511 int
    512 pcib_isa_intr_alloc(void *v, int mask, int type, int *irq)
    513 {
    514 	struct pcib_softc *sc = v;
    515 	int i, tmp, bestirq, count;
    516 	struct algor_intrhand *ih;
    517 
    518 	if (type == IST_NONE)
    519 		panic("pcib_intr_alloc: bogus type");
    520 
    521 	bestirq = -1;
    522 	count = -1;
    523 
    524 	mask &= ~sc->sc_reserved;
    525 
    526 #if 0
    527 	printf("pcib_intr_alloc: mask = 0x%04x\n", mask);
    528 #endif
    529 
    530 	for (i = 0; i < 16; i++) {
    531 		if ((mask & (1 << i)) == 0)
    532 			continue;
    533 
    534 		switch (sc->sc_intrtab[i].intr_type) {
    535 		case IST_NONE:
    536 			/*
    537 			 * If nothing's using the IRQ, just return it.
    538 			 */
    539 			*irq = i;
    540 			return (0);
    541 
    542 		case IST_EDGE:
    543 		case IST_LEVEL:
    544 			if (type != sc->sc_intrtab[i].intr_type)
    545 				continue;
    546 			/*
    547 			 * If the IRQ is sharable, count the number of
    548 			 * other handlers, and if it's smaller than the
    549 			 * last IRQ like this, remember it.
    550 			 */
    551 			tmp = 0;
    552 			for (ih = LIST_FIRST(&sc->sc_intrtab[i].intr_q);
    553 			     ih != NULL; ih = LIST_NEXT(ih, ih_q))
    554 				tmp++;
    555 			if (bestirq == -1 || count > tmp) {
    556 				bestirq = i;
    557 				count = tmp;
    558 			}
    559 			break;
    560 
    561 		case IST_PULSE:
    562 			/* This just isn't sharable. */
    563 			continue;
    564 		}
    565 	}
    566 
    567 	if (bestirq == -1)
    568 		return (1);
    569 
    570 	*irq = bestirq;
    571 	return (0);
    572 }
    573