| /************************************************************************** |
| Etherboot - BOOTP/TFTP Bootstrap Program |
| |
| TIARA (Fujitsu Etherstar) NIC driver for Etherboot |
| Copyright (c) Ken Yap 1998 |
| |
| Information gleaned from: |
| |
| TIARA.ASM Packet driver by Brian Fisher, Queens U, Kingston, Ontario |
| Fujitsu MB86960 spec sheet (different chip but same family) |
| ***************************************************************************/ |
| |
| /* |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public License as |
| * published by the Free Software Foundation; either version 2, or (at |
| * your option) any later version. |
| */ |
| |
| /* to get some global routines like printf */ |
| #include "etherboot.h" |
| /* to get the interface to the body of the program */ |
| #include "nic.h" |
| |
| /* |
| EtherStar I/O Register offsets |
| */ |
| |
| /* Offsets of registers */ |
| #define DLCR_XMIT_STAT 0x00 |
| #define DLCR_XMIT_MASK 0x01 |
| #define DLCR_RECV_STAT 0x02 |
| #define DLCR_RECV_MASK 0x03 |
| #define DLCR_XMIT_MODE 0x04 |
| #define DLCR_RECV_MODE 0x05 |
| #define DLCR_ENABLE 0x06 |
| #define DLCR_TDR_LOW 0x07 |
| #define DLCR_NODE_ID 0x08 |
| #define DLCR_TDR_HIGH 0x0F |
| #define BMPR_MEM_PORT 0x10 |
| #define BMPR_PKT_LEN 0x12 |
| #define BMPR_DMA_ENABLE 0x14 |
| #define PROM_ID 0x18 |
| |
| #define TMST 0x80 |
| #define TMT_OK 0x80 |
| #define TMT_16COLL 0x02 |
| #define BUF_EMPTY 0x40 |
| |
| #define CARD_DISABLE 0x80 /* written to DLCR_ENABLE to disable card */ |
| #define CARD_ENABLE 0 /* written to DLCR_ENABLE to enable card */ |
| |
| #define CLEAR_STATUS 0x0F /* used to clear status info */ |
| /* |
| 00001111B |
| !!!!!!!!-------- |
| !!!!!!!+--------CLEAR BUS WRITE ERROR |
| !!!!!!+---------CLEAR 16 COLLISION |
| !!!!!+----------CLEAR COLLISION |
| !!!!+-----------CLEAR UNDERFLOW |
| !!!+------------NC |
| !!+-------------NC |
| !+--------------NC |
| +---------------NC |
| */ |
| |
| #define NO_TX_IRQS 0 /* written to clear transmit IRQs */ |
| |
| #define CLR_RCV_STATUS 0xCF /* clears receive status */ |
| |
| #define EN_RCV_IRQS 0x80 /* enable receive interrupts */ |
| /* |
| 10000000B |
| !!!!!!!!-------- |
| !!!!!!!+--------ENABLE OVERFLOW |
| !!!!!!+---------ENABLE CRC |
| !!!!!+----------ENABLE ALIGN |
| !!!!+-----------ENABLE SHORT PKT |
| !!!+------------DISABLE REMOTE RESET |
| !!+-------------RESERVED |
| !+--------------RESERVED |
| +---------------ENABLE PKT READY |
| */ |
| |
| #define XMIT_MODE 0x02 |
| /* |
| 00000010B |
| !!!!!!!!---------ENABLE CARRIER DETECT |
| !!!!!!!+---------DISABLE LOOPBACK |
| */ |
| |
| #define RECV_MODE 0x02 |
| /* |
| 00000010B |
| !!!!!!!!---------ACCEPT ALL PACKETS |
| !!!!!!!+---------ACCEPT PHYSICAL, MULTICAST, AND |
| !!!!!!+----------BROADCAST PACKETS |
| !!!!!+-----------DISABLE REMOTE RESET |
| !!!!+------------DISABLE SHORT PACKETS |
| !!!+-------------USE 6 BYTE ADDRESS |
| !!+--------------NC |
| !+---------------NC |
| +----------------DISABLE CRC TEST MODE |
| */ |
| |
| /* NIC specific static variables go here */ |
| |
| static unsigned short ioaddr; |
| |
| /************************************************************************** |
| RESET - Reset adapter |
| ***************************************************************************/ |
| static void tiara_reset(struct nic *nic) |
| { |
| int i; |
| |
| outb(CARD_DISABLE, ioaddr + DLCR_ENABLE); |
| outb(CLEAR_STATUS, ioaddr + DLCR_XMIT_STAT); |
| outb(NO_TX_IRQS, ioaddr + DLCR_XMIT_MASK); |
| outb(CLR_RCV_STATUS, ioaddr + DLCR_RECV_STAT); |
| outb(XMIT_MODE, ioaddr + DLCR_XMIT_MODE); |
| outb(RECV_MODE, ioaddr + DLCR_RECV_MODE); |
| /* Vacuum recv buffer */ |
| while ((inb(ioaddr + DLCR_RECV_MODE) & BUF_EMPTY) == 0) |
| inb(ioaddr + BMPR_MEM_PORT); |
| /* Set node address */ |
| for (i = 0; i < ETHER_ADDR_SIZE; ++i) |
| outb(nic->node_addr[i], ioaddr + DLCR_NODE_ID + i); |
| outb(CLR_RCV_STATUS, ioaddr + DLCR_RECV_STAT); |
| outb(CARD_ENABLE, ioaddr + DLCR_ENABLE); |
| } |
| |
| /************************************************************************** |
| POLL - Wait for a frame |
| ***************************************************************************/ |
| static int tiara_poll(struct nic *nic) |
| { |
| unsigned int len; |
| |
| if (inb(ioaddr + DLCR_RECV_MODE) & BUF_EMPTY) |
| return (0); |
| /* Ack packet */ |
| outw(CLR_RCV_STATUS, ioaddr + DLCR_RECV_STAT); |
| len = inw(ioaddr + BMPR_MEM_PORT); /* throw away status */ |
| len = inw(ioaddr + BMPR_MEM_PORT); |
| /* Drop overlength packets */ |
| if (len > ETH_MAX_PACKET) |
| return (0); /* should we drain the buffer? */ |
| insw(ioaddr + BMPR_MEM_PORT, nic->packet, len / 2); |
| /* If it's our own, drop it */ |
| if (memcmp(nic->packet + ETHER_ADDR_SIZE, nic->node_addr, ETHER_ADDR_SIZE) == 0) |
| return (0); |
| nic->packetlen = len; |
| return (1); |
| } |
| |
| /************************************************************************** |
| TRANSMIT - Transmit a frame |
| ***************************************************************************/ |
| static void tiara_transmit( |
| struct nic *nic, |
| char *d, /* Destination */ |
| unsigned int t, /* Type */ |
| unsigned int s, /* size */ |
| char *p) /* Packet */ |
| { |
| unsigned int len; |
| unsigned long time; |
| |
| len = s + ETHER_HDR_SIZE; |
| if (len < ETH_MIN_PACKET) |
| len = ETH_MIN_PACKET; |
| t = htons(t); |
| outsw(ioaddr + BMPR_MEM_PORT, d, ETHER_ADDR_SIZE / 2); |
| outsw(ioaddr + BMPR_MEM_PORT, nic->node_addr, ETHER_ADDR_SIZE / 2); |
| outw(t, ioaddr + BMPR_MEM_PORT); |
| outsw(ioaddr + BMPR_MEM_PORT, p, s / 2); |
| if (s & 1) /* last byte */ |
| outb(p[s-1], ioaddr + BMPR_MEM_PORT); |
| while (s++ < ETH_MIN_PACKET - ETHER_HDR_SIZE) /* pad */ |
| outb(0, ioaddr + BMPR_MEM_PORT); |
| outw(len | (TMST << 8), ioaddr + BMPR_PKT_LEN); |
| /* wait for transmit complete */ |
| time = currticks() + TICKS_PER_SEC; /* wait one second */ |
| while (currticks() < time && (inb(ioaddr) & (TMT_OK|TMT_16COLL)) == 0) |
| ; |
| if ((inb(ioaddr) & (TMT_OK|TMT_16COLL)) == 0) |
| printf("Tiara timed out on transmit\n"); |
| /* Do we need to ack the transmit? */ |
| } |
| |
| /************************************************************************** |
| DISABLE - Turn off ethernet interface |
| ***************************************************************************/ |
| static void tiara_disable(struct nic *nic) |
| { |
| /* Apparently only a power down can do this properly */ |
| outb(CARD_DISABLE, ioaddr + DLCR_ENABLE); |
| } |
| |
| static int tiara_probe1(struct nic *nic) |
| { |
| /* Hope all the Tiara cards have this vendor prefix */ |
| static char vendor_prefix[] = { 0x08, 0x00, 0x1A }; |
| static char all_ones[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; |
| int i, b; |
| |
| for (i = 0; i < ETHER_ADDR_SIZE; ++i) |
| nic->node_addr[i] = inb(ioaddr + PROM_ID + i); |
| if (memcmp(nic->node_addr, vendor_prefix, sizeof(vendor_prefix)) != 0) |
| return (0); |
| if (memcmp(nic->node_addr, all_ones, sizeof(all_ones)) == 0) |
| return (0); |
| printf("\nTiara ioaddr 0x%x, addr ", ioaddr); |
| for (i = 0; i < ETHER_ADDR_SIZE; ++i) |
| { |
| printf("%b", nic->node_addr[i]); |
| if (i < ETHER_ADDR_SIZE - 1) |
| printf(":"); |
| } |
| putchar('\n'); |
| return (1); |
| } |
| |
| /************************************************************************** |
| PROBE - Look for an adapter, this routine's visible to the outside |
| ***************************************************************************/ |
| struct nic *tiara_probe(struct nic *nic, unsigned short *probe_addrs) |
| { |
| /* missing entries are addresses usually already used */ |
| static unsigned short io_addrs[] = { |
| 0x100, 0x120, 0x140, 0x160, |
| 0x180, 0x1A0, 0x1C0, 0x1E0, |
| 0x200, 0x220, 0x240, /*Par*/ |
| 0x280, 0x2A0, 0x2C0, /*Ser*/ |
| 0x300, 0x320, 0x340, /*Par*/ |
| 0x380, /*Vid,Par*/ 0x3C0, /*Ser*/ |
| 0x0 |
| }; |
| unsigned short *p; |
| |
| /* if probe_addrs is 0, then routine can use a hardwired default */ |
| if (probe_addrs == 0) |
| probe_addrs = io_addrs; |
| for (p = probe_addrs; (ioaddr = *p) != 0; ++p) |
| if (tiara_probe1(nic)) |
| break; |
| /* if board found */ |
| if (ioaddr != 0) |
| { |
| tiara_reset(nic); |
| /* point to NIC specific routines */ |
| nic->reset = tiara_reset; |
| nic->poll = tiara_poll; |
| nic->transmit = tiara_transmit; |
| nic->disable = tiara_disable; |
| return nic; |
| } |
| else |
| return (0); |
| } |