diff options
Diffstat (limited to 'sys/arch/vax/stand/boot/if_ze.c')
-rw-r--r-- | sys/arch/vax/stand/boot/if_ze.c | 304 |
1 files changed, 304 insertions, 0 deletions
diff --git a/sys/arch/vax/stand/boot/if_ze.c b/sys/arch/vax/stand/boot/if_ze.c new file mode 100644 index 00000000000..89a4c43290e --- /dev/null +++ b/sys/arch/vax/stand/boot/if_ze.c @@ -0,0 +1,304 @@ +/* $OpenBSD: if_ze.c,v 1.1 2000/04/27 02:26:25 bjc Exp $ */ +/* $NetBSD: if_ze.c,v 1.5 1999/08/23 19:09:27 ragge Exp $ */ +/* + * Copyright (c) 1998 James R. Maynard III. 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 James R. Maynard III. + * 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 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. + * + * Standalone routine for the SGEC Ethernet controller. + */ + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/queue.h> + +#include <net/if.h> + +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/if_ether.h> + +#include <lib/libkern/libkern.h> +#include <lib/libsa/netif.h> +#include <lib/libsa/stand.h> + +#include <arch/vax/if/sgecreg.h> + +#include "arch/vax/include/sid.h" + +int ze_probe(), ze_match(), ze_get(), ze_put(); +void ze_init(), ze_end(); + +struct netif_stats ze_stats; + +struct netif_dif ze_ifs[] = { +/* dif_unit dif_nsel dif_stats dif_private */ +{ 0, 1, &ze_stats, }, +}; + +struct netif_stats ze_stats; + +#define ETHER_MIN_LEN 64 +#define ETHER_MAX_LEN 1518 + +struct netif_driver ze_driver = { + "ze", ze_match, ze_probe, ze_init, ze_get, ze_put, ze_end, ze_ifs, 1, +}; + +#define NRCV 5 /* allocate 5 receive descriptors */ +#define NXMT 5 /* and 5 transmit - must be >1 */ +#define SETUP_FRAME_LEN 128 /* length of the setup frame */ + +/* allocate a buffer on an octaword boundary */ +#define OW_ALLOC(x) ((void *)((int)(alloc((x) + 15) + 15) & ~15)) + +static volatile struct zedevice *addr; + +struct ze_tdes *ze_tdes_list; /* transmit desc list */ +struct ze_rdes *ze_rdes_list; /* and receive desc list */ +u_char ze_myaddr[ETHER_ADDR_LEN]; /* my Ethernet address */ + +int +ze_match(nif, machdep_hint) + struct netif *nif; + void *machdep_hint; +{ + return strcmp(machdep_hint, "ze") == 0; +} + +int +ze_probe(nif, machdep_hint) + struct netif *nif; + void *machdep_hint; +{ + return 0; +} + +void +ze_init(desc, machdep_hint) + struct iodesc *desc; + void *machdep_hint; +{ + u_long nicsr0_work, *nisa_rom; + int i; + u_char *saved_buf; + struct ze_tdes *ze_setup_tdes_list; + + /* point to the device in memory */ + addr = (struct zedevice *)0x20008000; + + /* reset the device and wait for completion */ + addr->ze_nicsr6 = ZE_NICSR6_MBO | ZE_NICSR6_RE; + while ((addr->ze_nicsr5 & ZE_NICSR5_ID) == 0) + ; + if (addr->ze_nicsr5 & ZE_NICSR5_SF) { + printf("SGEC self-test failed...\n"); + } + + /* Get our Ethernet address */ + if (vax_boardtype == VAX_BTYP_49) { + nisa_rom = (u_long *)0x27800000; + for (i=0; i<ETHER_ADDR_LEN; i++) + ze_myaddr[i] = nisa_rom[i] & 0377; + } else { + nisa_rom = (u_long *)0x20084000; + for (i=0; i<ETHER_ADDR_LEN; i++) + ze_myaddr[i] = (nisa_rom[i] & 0x0000ff00) >> 8; + } + bcopy(ze_myaddr,desc->myea,ETHER_ADDR_LEN); + + /* initialize SGEC operating mode */ + /* disable interrupts here */ + nicsr0_work = ZE_NICSR0_IPL14 | ZE_NICSR0_SA | ZE_NICSR0_MBO | + (ZE_NICSR0_IV_MASK & 0x0108); + while (addr->ze_nicsr0 != nicsr0_work) + addr->ze_nicsr0 = nicsr0_work; + if (addr->ze_nicsr5 & ZE_NICSR5_ME) + addr->ze_nicsr5 |= ZE_NICSR5_ME; + /* reenable interrupts here */ + + /* Allocate space for descriptor lists and buffers, + then initialize them. Set up both lists as a ring. */ + ze_rdes_list = OW_ALLOC((NRCV+1) * sizeof(struct ze_rdes)); + ze_tdes_list = OW_ALLOC((NXMT+1) * sizeof(struct ze_tdes)); + for (i=0; i < NRCV; i++) { + bzero(ze_rdes_list+i,sizeof(struct ze_rdes)); + ze_rdes_list[i].ze_framelen = ZE_FRAMELEN_OW; + ze_rdes_list[i].ze_bufsize = ETHER_MAX_LEN; + ze_rdes_list[i].ze_bufaddr = alloc(ETHER_MAX_LEN); + } + bzero(ze_rdes_list+NRCV,sizeof(struct ze_rdes)); + ze_rdes_list[NRCV].ze_framelen = ZE_FRAMELEN_OW; + ze_rdes_list[NRCV].ze_rdes1 = ZE_RDES1_CA; + ze_rdes_list[NRCV].ze_bufaddr = (u_char *)ze_rdes_list; + for (i=0; i < NXMT; i++) { + bzero(ze_tdes_list+i,sizeof(struct ze_tdes)); + ze_tdes_list[i].ze_tdes1 = ZE_TDES1_FS | ZE_TDES1_LS; + ze_tdes_list[i].ze_bufsize = ETHER_MAX_LEN; + ze_tdes_list[i].ze_bufaddr = alloc(ETHER_MAX_LEN); + } + bzero(ze_tdes_list+NXMT,sizeof(struct ze_tdes)); + ze_tdes_list[NXMT].ze_tdes1 = ZE_TDES1_CA; + ze_tdes_list[NXMT].ze_tdr = ZE_TDR_OW; + ze_tdes_list[NXMT].ze_bufaddr = (u_char *)ze_tdes_list; + + /* Build setup frame. We set the SGEC to do a + perfect filter on our own address. */ + ze_setup_tdes_list = OW_ALLOC(2*sizeof(struct ze_tdes)); + bzero(ze_setup_tdes_list+0,2*sizeof(struct ze_tdes)); + ze_setup_tdes_list[0].ze_tdr = ZE_TDR_OW; + ze_setup_tdes_list[0].ze_tdes1 = ZE_TDES1_DT_SETUP; + ze_setup_tdes_list[0].ze_bufsize = SETUP_FRAME_LEN; + ze_setup_tdes_list[0].ze_bufaddr = alloc(SETUP_FRAME_LEN); + bzero(ze_setup_tdes_list[0].ze_bufaddr,SETUP_FRAME_LEN); + for (i=0; i < 16; i++) + bcopy(ze_myaddr,ze_setup_tdes_list[0].ze_bufaddr+(8*i), + ETHER_ADDR_LEN); + ze_setup_tdes_list[1].ze_tdes1 = ZE_TDES1_CA; + ze_setup_tdes_list[1].ze_bufaddr = (u_char *)ze_setup_tdes_list; + + /* Start the transmitter and initialize almost everything else. */ + addr->ze_nicsr4 = ze_setup_tdes_list; + addr->ze_nicsr6 = ZE_NICSR6_MBO | ZE_NICSR6_SE | ZE_NICSR6_ST | + ZE_NICSR6_DC | ZE_NICSR6_BL_4; + while ((addr->ze_nicsr5 & ZE_NICSR5_TS) != ZE_NICSR5_TS_SUSP) + ; /* wait for the frame to be processed */ + + /* Setup frame is done processing, initialize the receiver and + point the transmitter to the real tdes list. */ + addr->ze_nicsr4 = ze_tdes_list; + addr->ze_nicsr3 = ze_rdes_list; + addr->ze_nicsr6 |= ZE_NICSR6_SR; + + /* And away-y-y we go! */ +} + +int +ze_get(desc, pkt, maxlen, timeout) + struct iodesc *desc; + void *pkt; + int maxlen; + time_t timeout; +{ + int timeout_ctr=100000*timeout, len, rdes; + + while (timeout_ctr-- > 0) { + + /* If there's not a packet waiting for us, just decrement the + timeout counter. */ + if (!(addr->ze_nicsr5 & ZE_NICSR5_RI)) + continue; + + /* Look through the receive descriptor list for the packet. */ + for (rdes=0; rdes<NRCV; rdes++) { + if (ze_rdes_list[rdes].ze_framelen & ZE_FRAMELEN_OW) + continue; + + /* If the packet has an error, ignore it. */ + if (ze_rdes_list[rdes].ze_rdes0 & ZE_RDES0_ES) + len = 0; + + /* Copy the packet, up to the length supplied by the caller, to + the caller's buffer. */ + else { + if ((len = (ze_rdes_list[rdes].ze_framelen & + (~ ZE_FRAMELEN_OW))) > maxlen) + len = maxlen; + bcopy((void *)ze_rdes_list[rdes].ze_bufaddr, + pkt,len); + } + + /* Give ownership of this descriptor back to the SGEC. */ + ze_rdes_list[rdes].ze_framelen = ZE_FRAMELEN_OW; + + /* If we actually got a good packet, reset the error flags and + tell the SGEC to look for more before returning. */ + if (len > 0) { + addr->ze_nicsr5=ZE_NICSR5_RU | ZE_NICSR5_RI | + ZE_NICSR5_IS; + addr->ze_nicsr2=ZE_NICSR2_RXPD; + return len; + } + } + } + + /* If we're going to return an error indication, at least reset the + error flags and tell the SGEC to keep receiving first. */ + addr->ze_nicsr5=ZE_NICSR5_RU | ZE_NICSR5_RI | ZE_NICSR5_IS; + addr->ze_nicsr2=ZE_NICSR2_RXPD; + return 0; +} + +int +ze_put(desc, pkt, len) + struct iodesc *desc; + void *pkt; + int len; +{ + int timeout=100000; + + /* The SGEC maintains its position in the transmit descriptor list + for the next frame to transmit. Unfortunately, there's no way to tell + from software just where that is. We're forced to reset the position + whenever we send a frame, which requires waiting for the previous + frame to be sent. Argh. */ + while ((addr->ze_nicsr5 & ZE_NICSR5_TS) == ZE_NICSR5_TS_RUN) + ; + + /* Copy the packet to the buffer we allocated. */ + bcopy(pkt, (void *)ze_tdes_list[0].ze_bufaddr, len); + + /* Set the packet length in the descriptor, increasing it to the + minimum size if needed. */ + ze_tdes_list[0].ze_bufsize = len; + if (len < ETHER_MIN_LEN) + ze_tdes_list[0].ze_bufsize = ETHER_MIN_LEN; + + /* Give ownership of the descriptor to the SGEC and tell it to start + transmitting. */ + ze_tdes_list[0].ze_tdr = ZE_TDR_OW; + addr->ze_nicsr4 = ze_tdes_list; + addr->ze_nicsr1 = ZE_NICSR1_TXPD; + + /* Wait for the frame to be sent, but not too long. */ + timeout = 100000; + while ((addr->ze_nicsr5 & ZE_NICSR5_TI == 0) && (--timeout>0)) + ; + + /* Reset the transmitter interrupt pending flag. */ + addr->ze_nicsr5 |= ZE_NICSR5_TI; + + /* Return good if we didn't timeout, or error if we did. */ + if (timeout>0) return len; + return -1; +} + +void +ze_end() +{ + addr->ze_nicsr6 = ZE_NICSR6_RE; +} |