/**************************************************************************
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"
#include "cards.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 < ETH_ALEN; ++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_FRAME_LEN)
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 + ETH_ALEN, nic->node_addr, ETH_ALEN) == 0)
return (0);
nic->packetlen = len;
return (1);
}
/**************************************************************************
TRANSMIT - Transmit a frame
***************************************************************************/
static void tiara_transmit(
struct nic *nic,
const char *d, /* Destination */
unsigned int t, /* Type */
unsigned int s, /* size */
const char *p) /* Packet */
{
unsigned int len;
unsigned long time;
len = s + ETH_HLEN;
if (len < ETH_ZLEN)
len = ETH_ZLEN;
t = htons(t);
outsw(ioaddr + BMPR_MEM_PORT, d, ETH_ALEN / 2);
outsw(ioaddr + BMPR_MEM_PORT, nic->node_addr, ETH_ALEN / 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_ZLEN - ETH_HLEN) /* 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;
for (i = 0; i < ETH_ALEN; ++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 %#hX, addr %!\n", ioaddr, nic->node_addr);
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);
}