/* $NetBSD: if_ie_gsc.c,v 1.5 2002/08/25 20:20:00 fredette Exp $ */ /* $OpenBSD: if_ie_gsc.c,v 1.6 2001/01/12 22:57:04 mickey Exp $ */ /* * Copyright (c) 1998,1999 Michael Shalayeff * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Michael Shalayeff. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF MIND, * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * Referencies: * 1. 82596DX and 82596SX High-Perfomance 32-bit Local Area Network Coprocessor * Intel Corporation, November 1996, Order Number: 290219-006 * * 2. 712 I/O Subsystem ERS Rev 1.0 * Hewlett-Packard, June 17 1992, Dwg No. A-A2263-66510-31 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define I82596_DEBUG I82586_DEBUG /* * XXX fredette - I'm defining these on a hunch. When things * appear to be working, remove these. */ #if 1 #define fdcache_small fdcache #define pdcache_small pdcache #endif #ifdef __for_reference_only struct ie_gsc_regs { u_int32_t ie_reset; u_int32_t ie_port; u_int32_t ie_attn; }; #endif #define IE_GSC_BANK_SZ (12) #define IE_GSC_REG_RESET (0) #define IE_GSC_REG_PORT (4) #define IE_GSC_REG_ATTN (8) #define IE_GSC_ALIGN(v) ((((u_int) (v)) + 0xf) & ~0xf) #define IE_GSC_SYSBUS (IE_SYSBUS_596_RSVD_SET | \ IE_SYSBUS_596_82586 | \ IE_SYSBUS_596_INTLOW | \ IE_SYSBUS_596_TRGEXT | \ IE_SYSBUS_596_BE) #define IE_SIZE 0x8000 struct ie_gsc_softc { struct ie_softc ie; /* tag and handle to hp700-specific adapter registers. */ bus_space_tag_t iot; bus_space_handle_t ioh; /* bus_dma_tag_t for the memory used by the adapter. */ bus_dma_tag_t iemt; /* interrupt handle. */ void *sc_ih; /* miscellaneous flags. */ int flags; #define IEGSC_GECKO (1 << 0) }; int ie_gsc_probe __P((struct device *, struct cfdata *, void *)); void ie_gsc_attach __P((struct device *, struct device *, void *)); struct cfattach ie_gsc_ca = { sizeof(struct ie_gsc_softc), ie_gsc_probe, ie_gsc_attach }; static int ie_gsc_media[] = { IFM_ETHER | IFM_10_2, }; #define IE_NMEDIA (sizeof(ie_gsc_media) / sizeof(ie_gsc_media[0])) void ie_gsc_reset __P((struct ie_softc *sc, int what)); void ie_gsc_attend __P((struct ie_softc *, int)); void ie_gsc_run __P((struct ie_softc *sc)); void ie_gsc_port __P((struct ie_softc *sc, u_int)); u_int16_t ie_gsc_read16 __P((struct ie_softc *sc, int offset)); void ie_gsc_write16 __P((struct ie_softc *sc, int offset, u_int16_t v)); void ie_gsc_write24 __P((struct ie_softc *sc, int offset, int addr)); void ie_gsc_memcopyin __P((struct ie_softc *sc, void *p, int offset, size_t)); void ie_gsc_memcopyout __P((struct ie_softc *sc, const void *p, int, size_t)); /* Reset the adapter. */ void ie_gsc_reset(sc, what) struct ie_softc *sc; int what; { struct ie_gsc_softc *gsc = (struct ie_gsc_softc *) sc; register int i; switch (what) { case CHIP_PROBE: bus_space_write_4(gsc->iot, gsc->ioh, IE_GSC_REG_RESET, 0); break; case CARD_RESET: bus_space_write_4(gsc->iot, gsc->ioh, IE_GSC_REG_RESET, 0); /* * per [2] 4.6.2.1 * delay for 10 system clocks + 5 transmit clocks, * NB: works for system clocks over 10MHz */ DELAY(1000); /* * after the hardware reset: * inform i825[89]6 about new SCP address, * which must be at least 16-byte aligned */ ie_gsc_port(sc, IE_PORT_ALT_SCP); ie_gsc_attend(sc, what); for (i = 9000; i-- && ie_gsc_read16(sc, IE_ISCP_BUSY(sc->iscp)); DELAY(100)) pdcache(0, (vaddr_t)sc->sc_maddr + sc->iscp, IE_ISCP_SZ); #if I82596_DEBUG if (i < 0) { printf("timeout for PORT command (%x)%s\n", ie_gsc_read16(sc, IE_ISCP_BUSY(sc->iscp)), (gsc->flags & IEGSC_GECKO)? " on gecko":""); return; } #endif break; } } /* Do a channel attention on the adapter. */ void ie_gsc_attend(sc, why) struct ie_softc *sc; int why; { struct ie_gsc_softc *gsc = (struct ie_gsc_softc *) sc; bus_space_write_4(gsc->iot, gsc->ioh, IE_GSC_REG_ATTN, 0); } /* Enable the adapter. */ void ie_gsc_run(sc) struct ie_softc *sc; { } /* Run an i82596 PORT command on the adapter. */ void ie_gsc_port(sc, cmd) struct ie_softc *sc; u_int cmd; { struct ie_gsc_softc *gsc = (struct ie_gsc_softc *) sc; switch (cmd) { case IE_PORT_RESET: case IE_PORT_DUMP: break; case IE_PORT_SELF_TEST: cmd |= (sc->sc_dmamap->dm_segs[0].ds_addr + 0); break; case IE_PORT_ALT_SCP: cmd |= (sc->sc_dmamap->dm_segs[0].ds_addr + sc->scp); break; } if (gsc->flags & IEGSC_GECKO) { bus_space_write_4(gsc->iot, gsc->ioh, IE_GSC_REG_PORT, (cmd & 0xffff)); DELAY(1000); bus_space_write_4(gsc->iot, gsc->ioh, IE_GSC_REG_PORT, (cmd >> 16)); DELAY(1000); } else { bus_space_write_4(gsc->iot, gsc->ioh, IE_GSC_REG_PORT, (cmd >> 16)); DELAY(1000); bus_space_write_4(gsc->iot, gsc->ioh, IE_GSC_REG_PORT, (cmd & 0xffff)); DELAY(1000); } } u_int16_t ie_gsc_read16(sc, offset) struct ie_softc *sc; int offset; { u_int16_t val; __asm __volatile( " ldh 0(%1), %0 \n" " fdc %%r0(%1) \n" : "=&r" (val) : "r" ((caddr_t)sc->sc_maddr + offset)); return (val); } void ie_gsc_write16(sc, offset, v) struct ie_softc *sc; int offset; u_int16_t v; { __asm __volatile( " sth %0, 0(%1) \n" " fdc %%r0(%1) \n" : /* no outputs */ : "r" (v), "r" ((caddr_t)sc->sc_maddr + offset)); } void ie_gsc_write24(sc, offset, addr) struct ie_softc *sc; int offset; int addr; { /* * i82586.c assumes that the chip address space starts at * zero, so we have to add in the appropriate offset here. */ addr += sc->sc_dmamap->dm_segs[0].ds_addr; __asm __volatile( " ldi 2, %%r21 \n" " extru %0, 15, 16, %%r22 \n" " sth %0, 0(%1) \n" " sth %%r22, 2(%1) \n" " fdc %%r0(%1) \n" " fdc %%r21(%1) \n" : /* No outputs */ : "r" (addr), "r" ((caddr_t)sc->sc_maddr + offset) : "r21", "r22"); } void ie_gsc_memcopyin(sc, p, offset, size) struct ie_softc *sc; void *p; int offset; size_t size; { struct ie_gsc_softc *gsc = (struct ie_gsc_softc *) sc; memcpy (p, (void *)((caddr_t)sc->sc_maddr + offset), size); bus_dmamap_sync(gsc->iemt, sc->sc_dmamap, offset, size, BUS_DMASYNC_PREREAD); hp700_led_blink(HP700_LED_NETRCV); } void ie_gsc_memcopyout(sc, p, offset, size) struct ie_softc *sc; const void *p; int offset; size_t size; { struct ie_gsc_softc *gsc = (struct ie_gsc_softc *) sc; memcpy ((void *)((caddr_t)sc->sc_maddr + offset), p, size); bus_dmamap_sync(gsc->iemt, sc->sc_dmamap, offset, size, BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); hp700_led_blink(HP700_LED_NETSND); } /* * i82596 probe routine */ int i82596_probe __P((struct ie_softc *)); int i82596_probe(sc) struct ie_softc *sc; { struct ie_gsc_softc *gsc = (struct ie_gsc_softc *) sc; int i; /* Set up the SCP. */ sc->ie_bus_write16(sc, IE_SCP_BUS_USE(sc->scp), IE_GSC_SYSBUS); sc->ie_bus_write24(sc, IE_SCP_ISCP(sc->scp), sc->iscp); /* Set up the ISCP. */ sc->ie_bus_write16(sc, IE_ISCP_SCB(sc->iscp), sc->scb); sc->ie_bus_write24(sc, IE_ISCP_BASE(sc->iscp), 0); /* Set BUSY in the ISCP. */ sc->ie_bus_write16(sc, IE_ISCP_BUSY(sc->iscp), 1); /* Reset the adapter. */ bus_dmamap_sync(gsc->iemt, sc->sc_dmamap, 0, sc->sc_msize, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); sc->hwreset(sc, CARD_RESET); /* Make sure that BUSY got cleared. */ if (sc->ie_bus_read16(sc, IE_ISCP_BUSY(sc->iscp))) { #if I82596_DEBUG printf ("%s: ISCP set failed\n", sc->sc_dev.dv_xname); #endif return 0; } /* Run the chip self-test. */ sc->ie_bus_write24(sc, 0, -sc->sc_dmamap->dm_segs[0].ds_addr); sc->ie_bus_write24(sc, 4, -(sc->sc_dmamap->dm_segs[0].ds_addr + 1)); bus_dmamap_sync(gsc->iemt, sc->sc_dmamap, 0, sc->sc_msize, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); ie_gsc_port(sc, IE_PORT_SELF_TEST); for (i = 9000; i-- && sc->ie_bus_read16(sc, 4); DELAY(100)) pdcache(0, (vaddr_t)sc->sc_maddr, sc->sc_msize); #if I82596_DEBUG printf (": test %x:%x\n%s", *((volatile int32_t *)((caddr_t)sc->sc_maddr + 0)), *((volatile int32_t *)((caddr_t)sc->sc_maddr + 4)), sc->sc_dev.dv_xname); #endif return 1; } int ie_gsc_probe(parent, match, aux) struct device *parent; struct cfdata *match; void *aux; { register struct gsc_attach_args *ga = aux; if (ga->ga_type.iodc_type != HPPA_TYPE_FIO || (ga->ga_type.iodc_sv_model != HPPA_FIO_LAN && ga->ga_type.iodc_sv_model != HPPA_FIO_GLAN)) return 0; return 1; } void ie_gsc_attach(parent, self, aux) struct device *parent, *self; void *aux; { u_int8_t myaddr[ETHER_ADDR_LEN]; register struct ie_gsc_softc *gsc = (struct ie_gsc_softc *)self; register struct ie_softc *sc = &gsc->ie; register struct gsc_attach_args *ga = aux; bus_dma_segment_t seg; int rseg; int rv; #ifdef PMAPDEBUG extern int pmapdebug; int opmapdebug = pmapdebug; pmapdebug = 0; #endif if (ga->ga_type.iodc_sv_model == HPPA_FIO_GLAN) gsc->flags |= IEGSC_GECKO; /* * Map the GSC registers. */ if (bus_space_map(ga->ga_iot, ga->ga_hpa, IE_GSC_BANK_SZ, 0, &gsc->ioh)) { printf(": can't map i/o space\n"); return; } /* Set up some initial glue. */ gsc->iot = ga->ga_iot; gsc->iemt = ga->ga_dmatag; sc->bt = ga->ga_iot; sc->sc_msize = IE_SIZE; /* * Allocate one contiguous segment of physical memory * to be used with the i82596. Since we're running the * chip in i82586 mode, we're restricted to 24-bit * physical addresses. */ if (bus_dmamem_alloc(gsc->iemt, sc->sc_msize, NBPG, 0, &seg, 1, &rseg, BUS_DMA_NOWAIT | BUS_DMA_24BIT)) { printf (": cannot allocate %d bytes of DMA memory\n", sc->sc_msize); return; } /* * Map that physical memory into kernel virtual space. */ if (bus_dmamem_map(gsc->iemt, &seg, rseg, sc->sc_msize, (caddr_t *)&sc->sc_maddr, BUS_DMA_NOWAIT)) { printf (": cannot map DMA memory\n"); bus_dmamem_free(gsc->iemt, &seg, rseg); return; } /* * Create a DMA map for the memory. */ if (bus_dmamap_create(gsc->iemt, sc->sc_msize, rseg, sc->sc_msize, 0, BUS_DMA_NOWAIT, &sc->sc_dmamap)) { printf(": cannot create DMA map\n"); bus_dmamem_unmap(gsc->iemt, (caddr_t)sc->sc_maddr, sc->sc_msize); bus_dmamem_free(gsc->iemt, &seg, rseg); return; } /* * Load the mapped DMA memory into the DMA map. */ if (bus_dmamap_load(gsc->iemt, sc->sc_dmamap, sc->sc_maddr, sc->sc_msize, NULL, BUS_DMA_NOWAIT)) { printf(": cannot load DMA map\n"); bus_dmamap_destroy(gsc->iemt, sc->sc_dmamap); bus_dmamem_unmap(gsc->iemt, (caddr_t)sc->sc_maddr, sc->sc_msize); bus_dmamem_free(gsc->iemt, &seg, rseg); return; } #if 1 /* XXX - this should go away. */ sc->bh = (bus_space_handle_t) sc->sc_maddr; #endif #if I82596_DEBUG printf(" mem %x[%p]/%x\n%s", (u_int)sc->sc_dmamap->dm_segs[0].ds_addr, sc->sc_maddr, sc->sc_msize, sc->sc_dev.dv_xname); sc->sc_debug = IED_ALL; #endif /* Initialize our bus glue. */ sc->hwreset = ie_gsc_reset; sc->chan_attn = ie_gsc_attend; sc->hwinit = ie_gsc_run; sc->memcopyout = ie_gsc_memcopyout; sc->memcopyin = ie_gsc_memcopyin; sc->ie_bus_read16 = ie_gsc_read16; sc->ie_bus_write16 = ie_gsc_write16; sc->ie_bus_write24 = ie_gsc_write24; sc->intrhook = NULL; /* Clear all RAM. */ memset(sc->sc_maddr, 0, sc->sc_msize); bus_dmamap_sync(gsc->iemt, sc->sc_dmamap, 0, sc->sc_msize, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); /* * We use low memory to set up SCP, ICSP and SCB data * structures. The remaining pages become the buffer area * (managed in i82586.c). */ /* * Since we have an i82596, we can control where where * the chip looks for SCP. We plan to use the first * two 32-bit words of memory for the self-test, so the * SCP can go after that. */ sc->scp = IE_GSC_ALIGN(8); /* ISCP follows SCP */ sc->iscp = IE_GSC_ALIGN(sc->scp + IE_SCP_SZ); /* SCB follows ISCP */ sc->scb = IE_GSC_ALIGN(sc->iscp + IE_ISCP_SZ); /* The remainder of the memory is for buffers. */ sc->buf_area = IE_GSC_ALIGN(sc->scb + IE_SCB_SZ); sc->buf_area_sz = sc->sc_msize - sc->buf_area; /* Finally, we can probe the chip. */ rv = i82596_probe(sc); if (!rv) { bus_dmamap_destroy(gsc->iemt, sc->sc_dmamap); bus_dmamem_unmap(gsc->iemt, (caddr_t)sc->sc_maddr, sc->sc_msize); bus_dmamem_free(gsc->iemt, &seg, rseg); return; } #ifdef PMAPDEBUG pmapdebug = opmapdebug; #endif if (!rv) return; /* Get our Ethernet address. */ memcpy(myaddr, ga->ga_ether_address, ETHER_ADDR_LEN); /* Set up the SCP. */ sc->ie_bus_write16(sc, IE_SCP_BUS_USE(sc->scp), IE_GSC_SYSBUS); sc->ie_bus_write24(sc, IE_SCP_ISCP(sc->scp), sc->iscp); /* Set up the ISCP. */ sc->ie_bus_write16(sc, IE_ISCP_SCB(sc->iscp), sc->scb); sc->ie_bus_write24(sc, IE_ISCP_BASE(sc->iscp), 0); /* Set BUSY in the ISCP. */ sc->ie_bus_write16(sc, IE_ISCP_BUSY(sc->iscp), 1); /* Reset the adapter. */ bus_dmamap_sync(gsc->iemt, sc->sc_dmamap, 0, sc->sc_msize, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); sc->hwreset(sc, CARD_RESET); bus_dmamap_sync(gsc->iemt, sc->sc_dmamap, 0, sc->sc_msize, BUS_DMASYNC_PREREAD); /* Now call the MI attachment. */ printf(": v%d.%d", ga->ga_type.iodc_model, ga->ga_type.iodc_sv_rev); i82586_attach(sc, (gsc->flags & IEGSC_GECKO) ? "LASI/i82596CA" : "i82596DX", myaddr, ie_gsc_media, IE_NMEDIA, ie_gsc_media[0]); gsc->sc_ih = hp700_intr_establish(&sc->sc_dev, IPL_NET, i82586_intr, sc, ga->ga_int_reg, ga->ga_irq); }