449 lines
10 KiB
C
449 lines
10 KiB
C
/* $NetBSD: 3c90xb.c,v 1.14 2008/12/14 18:46:33 christos Exp $ */
|
|
|
|
/*
|
|
* Copyright (c) 1999
|
|
* Matthias Drochner. 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.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
|
|
*/
|
|
|
|
#include <sys/types.h>
|
|
#include <machine/pio.h>
|
|
|
|
struct mbuf; /* XXX */
|
|
typedef int bus_dmamap_t; /* XXX */
|
|
#include <dev/ic/elink3reg.h>
|
|
#include <dev/ic/elinkxlreg.h>
|
|
|
|
#include <lib/libsa/stand.h>
|
|
|
|
#include <libi386.h>
|
|
#include <pcivar.h>
|
|
|
|
#if defined(_STANDALONE) && !defined(SUPPORT_NO_NETBSD)
|
|
#include <lib/libkern/libkern.h>
|
|
#include <bootinfo.h>
|
|
#endif
|
|
|
|
#include "etherdrv.h"
|
|
|
|
#define RECVBUF_SIZE 1600 /* struct ex_upd + packet */
|
|
|
|
#ifdef _STANDALONE
|
|
|
|
static pcihdl_t mytag;
|
|
static char recvbuf[RECVBUF_SIZE];
|
|
#define RECVBUF_PHYS vtophys(recvbuf)
|
|
#define RECVBUF_VIRT ((void *)recvbuf)
|
|
static struct ex_dpd sndbuf;
|
|
#define SNDBUF_PHYS vtophys(&sndbuf)
|
|
#define SNDBUF_VIRT ((void *)&sndbuf)
|
|
|
|
#else /* !standalone, userspace testing environment */
|
|
|
|
#define PCI_MODE1_ENABLE 0x80000000UL
|
|
#define PCIBUSNO 1
|
|
#define PCIDEVNO 4
|
|
static pcihdl_t mytag = PCI_MODE1_ENABLE | (PCIBUSNO << 16) | (PCIDEVNO << 11);
|
|
|
|
extern void *mapmem(int, int);
|
|
void *dmamem; /* virtual */
|
|
#define DMABASE 0x3ffd800
|
|
#define DMASIZE 10240
|
|
#define RECVBUF_PHYS DMABASE
|
|
#define RECVBUF_VIRT dmamem
|
|
#define SNDBUF_PHYS (DMABASE + RECVBUF_SIZE)
|
|
#define SNDBUF_VIRT ((void *)(((char *)dmamem) + RECVBUF_SIZE))
|
|
|
|
#endif /* _STANDALONE */
|
|
|
|
|
|
#define CSR_READ_1(reg) inb(iobase + (reg))
|
|
#define CSR_READ_2(reg) inw(iobase + (reg))
|
|
#define CSR_READ_4(reg) inl(iobase + (reg))
|
|
#define CSR_WRITE_1(reg, val) outb(iobase + (reg), val)
|
|
#define CSR_WRITE_2(reg, val) outw(iobase + (reg), val)
|
|
#define CSR_WRITE_4(reg, val) outl(iobase + (reg), val)
|
|
|
|
#undef GO_WINDOW
|
|
#define GO_WINDOW(x) CSR_WRITE_2(ELINK_COMMAND, WINDOW_SELECT | x)
|
|
|
|
static int iobase;
|
|
static u_char myethaddr[6];
|
|
unsigned ether_medium;
|
|
|
|
static struct {
|
|
int did;
|
|
int mii;
|
|
} excards[] = {
|
|
{0x9005, 0}, /* 3c900b Combo */
|
|
{0x9055, 1}, /* 3c905b TP */
|
|
{0x9058, 0}, /* 3c905b Combo */
|
|
{-1}
|
|
}, *excard;
|
|
|
|
static struct mtabentry {
|
|
int address_cfg; /* configured connector */
|
|
int config_bit; /* connector present */
|
|
char *name;
|
|
} mediatab[] = { /* indexed by media type - etherdrv.h */
|
|
{ELINKMEDIA_10BASE_2, ELINK_PCI_BNC, "BNC"},
|
|
{ELINKMEDIA_10BASE_T, ELINK_PCI_10BASE_T, "UTP"},
|
|
{ELINKMEDIA_AUI, ELINK_PCI_AUI, "AUI"},
|
|
{ELINKMEDIA_MII, ELINK_PCI_100BASE_MII, "MII"},
|
|
{ELINKMEDIA_100BASE_TX, ELINK_PCI_100BASE_TX, "100TX"},
|
|
};
|
|
|
|
#if defined(_STANDALONE) && !defined(SUPPORT_NO_NETBSD)
|
|
static struct btinfo_netif bi_netif;
|
|
#endif
|
|
|
|
#define ex_waitcmd() \
|
|
do { \
|
|
while (CSR_READ_2(ELINK_STATUS) & COMMAND_IN_PROGRESS) \
|
|
continue; \
|
|
} while (0)
|
|
|
|
void ex_reset(void);
|
|
uint16_t ex_read_eeprom(int);
|
|
static int ex_eeprom_busy(void);
|
|
void ex_init(void);
|
|
void ex_set_media(void);
|
|
|
|
void
|
|
ex_reset(void)
|
|
{
|
|
CSR_WRITE_2(ELINK_COMMAND, GLOBAL_RESET);
|
|
delay(100000);
|
|
ex_waitcmd();
|
|
}
|
|
|
|
/*
|
|
* Read EEPROM data.
|
|
* XXX what to do if EEPROM doesn't unbusy?
|
|
*/
|
|
uint16_t
|
|
ex_read_eeprom(int offset)
|
|
{
|
|
uint16_t data = 0;
|
|
|
|
GO_WINDOW(0);
|
|
if (ex_eeprom_busy())
|
|
goto out;
|
|
CSR_WRITE_1(ELINK_W0_EEPROM_COMMAND, READ_EEPROM | (offset & 0x3f));
|
|
if (ex_eeprom_busy())
|
|
goto out;
|
|
data = CSR_READ_2(ELINK_W0_EEPROM_DATA);
|
|
out:
|
|
return data;
|
|
}
|
|
|
|
static int
|
|
ex_eeprom_busy(void)
|
|
{
|
|
int i = 100;
|
|
|
|
while (i--) {
|
|
if (!(CSR_READ_2(ELINK_W0_EEPROM_COMMAND) & EEPROM_BUSY))
|
|
return 0;
|
|
delay(100);
|
|
}
|
|
printf("\nex: eeprom stays busy.\n");
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Bring device up.
|
|
*/
|
|
void
|
|
ex_init(void)
|
|
{
|
|
int i;
|
|
|
|
ex_waitcmd();
|
|
EtherStop();
|
|
|
|
/*
|
|
* Set the station address and clear the station mask. The latter
|
|
* is needed for 90x cards, 0 is the default for 90xB cards.
|
|
*/
|
|
GO_WINDOW(2);
|
|
for (i = 0; i < 6; i++) {
|
|
CSR_WRITE_1(ELINK_W2_ADDR_0 + i,
|
|
myethaddr[i]);
|
|
CSR_WRITE_1(ELINK_W2_RECVMASK_0 + i, 0);
|
|
}
|
|
|
|
GO_WINDOW(3);
|
|
|
|
CSR_WRITE_2(ELINK_COMMAND, RX_RESET);
|
|
ex_waitcmd();
|
|
CSR_WRITE_2(ELINK_COMMAND, TX_RESET);
|
|
ex_waitcmd();
|
|
|
|
CSR_WRITE_2(ELINK_COMMAND, SET_INTR_MASK | 0); /* disable */
|
|
CSR_WRITE_2(ELINK_COMMAND, ACK_INTR | 0xff);
|
|
|
|
ex_set_media();
|
|
|
|
CSR_WRITE_2(ELINK_COMMAND, SET_RX_FILTER | FIL_INDIVIDUAL | FIL_BRDCST);
|
|
|
|
CSR_WRITE_4(ELINK_DNLISTPTR, 0);
|
|
CSR_WRITE_2(ELINK_COMMAND, TX_ENABLE);
|
|
|
|
CSR_WRITE_4(ELINK_UPLISTPTR, RECVBUF_PHYS);
|
|
CSR_WRITE_2(ELINK_COMMAND, RX_ENABLE);
|
|
CSR_WRITE_2(ELINK_COMMAND, ELINK_UPUNSTALL);
|
|
|
|
GO_WINDOW(1);
|
|
}
|
|
|
|
void
|
|
ex_set_media(void)
|
|
{
|
|
int config0, config1;
|
|
|
|
CSR_WRITE_2(ELINK_W3_MAC_CONTROL, 0);
|
|
|
|
if (ether_medium == ETHERMEDIUM_MII)
|
|
goto setcfg;
|
|
|
|
GO_WINDOW(4);
|
|
CSR_WRITE_2(ELINK_W4_MEDIA_TYPE, 0);
|
|
CSR_WRITE_2(ELINK_COMMAND, STOP_TRANSCEIVER);
|
|
delay(800);
|
|
|
|
switch (ether_medium) {
|
|
case ETHERMEDIUM_UTP:
|
|
CSR_WRITE_2(ELINK_W4_MEDIA_TYPE,
|
|
JABBER_GUARD_ENABLE | LINKBEAT_ENABLE);
|
|
break;
|
|
case ETHERMEDIUM_BNC:
|
|
CSR_WRITE_2(ELINK_COMMAND, START_TRANSCEIVER);
|
|
delay(800);
|
|
break;
|
|
case ETHERMEDIUM_AUI:
|
|
CSR_WRITE_2(ELINK_W4_MEDIA_TYPE, SQE_ENABLE);
|
|
delay(800);
|
|
break;
|
|
case ETHERMEDIUM_100TX:
|
|
CSR_WRITE_2(ELINK_W4_MEDIA_TYPE, LINKBEAT_ENABLE);
|
|
break;
|
|
}
|
|
|
|
setcfg:
|
|
GO_WINDOW(3);
|
|
|
|
config0 = CSR_READ_2(ELINK_W3_INTERNAL_CONFIG);
|
|
config1 = CSR_READ_2(ELINK_W3_INTERNAL_CONFIG + 2);
|
|
|
|
config1 = config1 & ~CONFIG_MEDIAMASK;
|
|
config1 |= (mediatab[ether_medium].address_cfg
|
|
<< CONFIG_MEDIAMASK_SHIFT);
|
|
|
|
CSR_WRITE_2(ELINK_W3_INTERNAL_CONFIG, config0);
|
|
CSR_WRITE_2(ELINK_W3_INTERNAL_CONFIG + 2, config1);
|
|
}
|
|
|
|
static void
|
|
ex_probemedia(void)
|
|
{
|
|
int i, j;
|
|
struct mtabentry *m;
|
|
|
|
/* test for presence of connectors */
|
|
GO_WINDOW(3);
|
|
i = CSR_READ_1(ELINK_W3_RESET_OPTIONS);
|
|
j = (CSR_READ_2(ELINK_W3_INTERNAL_CONFIG + 2) & CONFIG_MEDIAMASK)
|
|
>> CONFIG_MEDIAMASK_SHIFT;
|
|
GO_WINDOW(0);
|
|
|
|
for (ether_medium = 0, m = mediatab;
|
|
ether_medium < sizeof(mediatab) / sizeof(mediatab[0]);
|
|
ether_medium++, m++) {
|
|
if (j == m->address_cfg) {
|
|
if (!(i & m->config_bit)) {
|
|
printf("%s not present\n", m->name);
|
|
goto bad;
|
|
}
|
|
printf("using %s\n", m->name);
|
|
return;
|
|
}
|
|
}
|
|
printf("unknown connector\n");
|
|
bad:
|
|
ether_medium = -1;
|
|
}
|
|
|
|
int
|
|
EtherInit(unsigned char *myadr)
|
|
{
|
|
uint32_t pcicsr;
|
|
uint16_t val;
|
|
volatile struct ex_upd *upd;
|
|
#ifndef _STANDALONE
|
|
uint32_t id;
|
|
#endif
|
|
|
|
if (pcicheck()) {
|
|
printf("pcicheck failed\n");
|
|
return 0;
|
|
}
|
|
#ifndef _STANDALONE
|
|
pcicfgread(&mytag, 0, &id);
|
|
#endif
|
|
for (excard = &excards[0]; excard->did != -1; excard++) {
|
|
#ifdef _STANDALONE
|
|
if (pcifinddev(0x10b7, excard->did, &mytag) == 0)
|
|
goto found;
|
|
#else
|
|
if (id == (0x10b7 | (excard->did << 16)))
|
|
goto found;
|
|
#endif
|
|
}
|
|
printf("no ex\n");
|
|
return 0;
|
|
|
|
found:
|
|
pcicfgread(&mytag, 0x10, &iobase);
|
|
iobase &= ~3;
|
|
|
|
#ifndef _STANDALONE
|
|
dmamem = mapmem(DMABASE, DMASIZE);
|
|
if (!dmamem)
|
|
return 0;
|
|
#endif
|
|
|
|
/* enable bus mastering in PCI command register */
|
|
if (pcicfgread(&mytag, 0x04, (int *)&pcicsr)
|
|
|| pcicfgwrite(&mytag, 0x04, pcicsr | 4)) {
|
|
printf("cannot enable DMA\n");
|
|
return 0;
|
|
}
|
|
|
|
ex_reset();
|
|
|
|
if (excard->mii)
|
|
ether_medium = ETHERMEDIUM_MII;
|
|
else {
|
|
ex_probemedia();
|
|
if (ether_medium < 0)
|
|
return 0;
|
|
}
|
|
|
|
val = ex_read_eeprom(EEPROM_OEM_ADDR0);
|
|
myethaddr[0] = val >> 8;
|
|
myethaddr[1] = val & 0xff;
|
|
val = ex_read_eeprom(EEPROM_OEM_ADDR1);
|
|
myethaddr[2] = val >> 8;
|
|
myethaddr[3] = val & 0xff;
|
|
val = ex_read_eeprom(EEPROM_OEM_ADDR2);
|
|
myethaddr[4] = val >> 8;
|
|
myethaddr[5] = val & 0xff;
|
|
memcpy(myadr, myethaddr, 6);
|
|
|
|
upd = RECVBUF_VIRT;
|
|
upd->upd_nextptr = RECVBUF_PHYS;
|
|
upd->upd_pktstatus = 1500;
|
|
upd->upd_frags[0].fr_addr = RECVBUF_PHYS + 100;
|
|
upd->upd_frags[0].fr_len = 1500 | EX_FR_LAST;
|
|
|
|
ex_init();
|
|
|
|
#if defined(_STANDALONE) && !defined(SUPPORT_NO_NETBSD)
|
|
strncpy(bi_netif.ifname, "ex", sizeof(bi_netif.ifname));
|
|
bi_netif.bus = BI_BUS_PCI;
|
|
bi_netif.addr.tag = mytag;
|
|
|
|
BI_ADD(&bi_netif, BTINFO_NETIF, sizeof(bi_netif));
|
|
#endif
|
|
|
|
return 1;
|
|
}
|
|
|
|
void
|
|
EtherStop(void)
|
|
{
|
|
/*
|
|
* Issue software reset
|
|
*/
|
|
CSR_WRITE_2(ELINK_COMMAND, RX_DISABLE);
|
|
CSR_WRITE_2(ELINK_COMMAND, TX_DISABLE);
|
|
CSR_WRITE_2(ELINK_COMMAND, STOP_TRANSCEIVER);
|
|
CSR_WRITE_2(ELINK_COMMAND, INTR_LATCH);
|
|
}
|
|
|
|
int
|
|
EtherSend(char *pkt, int len)
|
|
{
|
|
volatile struct ex_dpd *dpd;
|
|
int i;
|
|
|
|
dpd = SNDBUF_VIRT;
|
|
|
|
dpd->dpd_nextptr = 0;
|
|
dpd->dpd_fsh = len;
|
|
#ifdef _STANDALONE
|
|
dpd->dpd_frags[0].fr_addr = vtophys(pkt);
|
|
#else
|
|
memcpy(SNDBUF_VIRT + 100, pkt, len);
|
|
dpd->dpd_frags[0].fr_addr = SNDBUF_PHYS + 100;
|
|
#endif
|
|
dpd->dpd_frags[0].fr_len = len | EX_FR_LAST;
|
|
|
|
CSR_WRITE_4(ELINK_DNLISTPTR, SNDBUF_PHYS);
|
|
CSR_WRITE_2(ELINK_COMMAND, ELINK_DNUNSTALL);
|
|
|
|
i = 10000;
|
|
while (!(dpd->dpd_fsh & 0x00010000)) {
|
|
if (--i < 0) {
|
|
printf("3c90xb: send timeout\n");
|
|
return -1;
|
|
}
|
|
delay(1);
|
|
}
|
|
|
|
return len;
|
|
}
|
|
|
|
int
|
|
EtherReceive(char *pkt, int maxlen)
|
|
{
|
|
volatile struct ex_upd *upd;
|
|
int len;
|
|
|
|
upd = RECVBUF_VIRT;
|
|
|
|
if (!(upd->upd_pktstatus & ~EX_UPD_PKTLENMASK))
|
|
return 0;
|
|
|
|
len = upd->upd_pktstatus & EX_UPD_PKTLENMASK;
|
|
if (len > maxlen)
|
|
len = 0;
|
|
else
|
|
memcpy(pkt, RECVBUF_VIRT + 100, len);
|
|
|
|
upd->upd_pktstatus = 1500;
|
|
CSR_WRITE_2(ELINK_COMMAND, ELINK_UPUNSTALL);
|
|
|
|
return len;
|
|
}
|