/* * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ /* * 3C589 and 3C562. * To do: * check xcvr10Base2 still works (is GlobalReset necessary?). */#include "u.h"#include "../port/lib.h"#include "mem.h"#include "dat.h"#include "fns.h"#include "io.h"#include "../port/error.h"#include "../port/netif.h" #include "etherif.h" enum { /* all windows */ CommandR = 0x000E, IntStatusR = 0x000E,}; enum { /* Commands */ GlobalReset = 0x0000, SelectRegisterWindow = 0x0001, RxReset = 0x0005, TxReset = 0x000B, AcknowledgeInterrupt = 0x000D,}; enum { /* IntStatus bits */ commandInProgress = 0x1000,}; #define COMMAND(port, cmd, a) outs((port)+CommandR, ((cmd)<<11)|(a))#define STATUS(port) ins((port)+IntStatusR) enum { /* Window 0 - setup */ Wsetup = 0x0000, /* registers */ ManufacturerID = 0x0000, /* 3C5[08]*, 3C59[27] */ ProductID = 0x0002, /* 3C5[08]*, 3C59[27] */ ConfigControl = 0x0004, /* 3C5[08]*, 3C59[27] */ AddressConfig = 0x0006, /* 3C5[08]*, 3C59[27] */ ResourceConfig = 0x0008, /* 3C5[08]*, 3C59[27] */ EepromCommand = 0x000A, EepromData = 0x000C, /* AddressConfig Bits */ autoSelect9 = 0x0080, xcvrMask9 = 0xC000, /* ConfigControl bits */ Ena = 0x0001, base10TAvailable9 = 0x0200, coaxAvailable9 = 0x1000, auiAvailable9 = 0x2000, /* EepromCommand bits */ EepromReadRegister = 0x0080, EepromBusy = 0x8000,}; enum { /* Window 1 - operating set */ Wop = 0x0001,}; enum { /* Window 3 - FIFO management */ Wfifo = 0x0003, /* registers */ InternalConfig = 0x0000, /* 3C509B, 3C589, 3C59[0257] */ /* InternalConfig bits */ xcvr10BaseT = 0x00000000, xcvr10Base2 = 0x00300000,}; enum { /* Window 4 - diagnostic */ Wdiagnostic = 0x0004, /* registers */ MediaStatus = 0x000A, /* MediaStatus bits */ linkBeatDetect = 0x0800,}; extern int etherelnk3reset(Ether*); static char *tcmpcmcia[] = { "3C589", /* 3COM 589[ABCD] */ "3C562", /* 3COM 562 */ "589E", /* 3COM Megahertz 589E */ nil,}; static intconfigASIC(Ether* ether, int port, int xcvr){ int x; /* set Window 0 configuration registers */ COMMAND(port, SelectRegisterWindow, Wsetup); outs(port+ConfigControl, Ena); /* IRQ must be 3 on 3C589/3C562 */ outs(port + ResourceConfig, 0x3F00); x = ins(port+AddressConfig) & ~xcvrMask9; x |= (xcvr>>20)<<14; outs(port+AddressConfig, x); COMMAND(port, TxReset, 0); while(STATUS(port) & commandInProgress) ; COMMAND(port, RxReset, 0); while(STATUS(port) & commandInProgress) ; return etherelnk3reset(ether);} static intreset(Ether* ether){ int i, t, slot; char *type; int port; enum { WantAny, Want10BT, Want10B2 }; int want; uchar ea[6]; char *p; if(ether->irq == 0) ether->irq = 10; if(ether->port == 0) ether->port = 0x240; port = ether->port; if(ioalloc(port, 0x10, 0, "3C589") < 0) return -1; type = nil; slot = -1; for(i = 0; tcmpcmcia[i] != nil; i++){ type = tcmpcmcia[i]; if((slot = pcmspecial(type, ether)) >= 0) break; } ether->type = type; /* must be set before calling configASIC */ if(slot < 0){ iofree(port); return -1; } /* * Read Ethernet address from card memory * on 3C562, but only if the user has not * overridden it. */ memset(ea, 0, sizeof ea); if(memcmp(ea, ether->ea, 6) == 0 && strcmp(type, "3C562") == 0) { if(pcmcistuple(slot, 0x88, -1, ea, 6) == 6) { for(i = 0; i < 6; i += 2){ t = ea[i]; ea[i] = ea[i+1]; ea[i+1] = t; } memmove(ether->ea, ea, 6); } } /* * Allow user to specify desired media in plan9.ini */ want = WantAny; for(i = 0; i < ether->nopt; i++){ if(cistrncmp(ether->opt[i], "media=", 6) != 0) continue; p = ether->opt[i]+6; if(cistrcmp(p, "10base2") == 0) want = Want10B2; else if(cistrcmp(p, "10baseT") == 0) want = Want10BT; } /* try configuring as a 10BaseT */ if(want==WantAny || want==Want10BT){ if(configASIC(ether, port, xcvr10BaseT) < 0){ pcmspecialclose(slot); iofree(port); return -1; } delay(100); COMMAND(port, SelectRegisterWindow, Wdiagnostic); if((ins(port+MediaStatus)&linkBeatDetect) || want==Want10BT){ COMMAND(port, SelectRegisterWindow, Wop); print("#l%d: xcvr10BaseT %s\n", ether->ctlrno, type); return 0; } } /* try configuring as a 10base2 */ if(want==WantAny || want==Want10B2){ COMMAND(port, GlobalReset, 0); if(configASIC(ether, port, xcvr10Base2) < 0){ pcmspecialclose(slot); iofree(port); return -1; } print("#l%d: xcvr10Base2 %s\n", ether->ctlrno, type); return 0; } return -1; /* not reached */} voidether589link(void){ addethercard("3C589", reset); addethercard("3C562", reset); addethercard("589E", reset);}
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
/*
* 3C589 and 3C562.
* To do:
* check xcvr10Base2 still works (is GlobalReset necessary?).
*/
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"
#include "../port/error.h"
#include "../port/netif.h"
#include "etherif.h"
enum { /* all windows */
CommandR = 0x000E,
IntStatusR = 0x000E,
};
enum { /* Commands */
GlobalReset = 0x0000,
SelectRegisterWindow = 0x0001,
RxReset = 0x0005,
TxReset = 0x000B,
AcknowledgeInterrupt = 0x000D,
};
enum { /* IntStatus bits */
commandInProgress = 0x1000,
};
#define COMMAND(port, cmd, a) outs((port)+CommandR, ((cmd)<<11)|(a))
#define STATUS(port) ins((port)+IntStatusR)
enum { /* Window 0 - setup */
Wsetup = 0x0000,
/* registers */
ManufacturerID = 0x0000, /* 3C5[08]*, 3C59[27] */
ProductID = 0x0002, /* 3C5[08]*, 3C59[27] */
ConfigControl = 0x0004, /* 3C5[08]*, 3C59[27] */
AddressConfig = 0x0006, /* 3C5[08]*, 3C59[27] */
ResourceConfig = 0x0008, /* 3C5[08]*, 3C59[27] */
EepromCommand = 0x000A,
EepromData = 0x000C,
/* AddressConfig Bits */
autoSelect9 = 0x0080,
xcvrMask9 = 0xC000,
/* ConfigControl bits */
Ena = 0x0001,
base10TAvailable9 = 0x0200,
coaxAvailable9 = 0x1000,
auiAvailable9 = 0x2000,
/* EepromCommand bits */
EepromReadRegister = 0x0080,
EepromBusy = 0x8000,
};
enum { /* Window 1 - operating set */
Wop = 0x0001,
};
enum { /* Window 3 - FIFO management */
Wfifo = 0x0003,
/* registers */
InternalConfig = 0x0000, /* 3C509B, 3C589, 3C59[0257] */
/* InternalConfig bits */
xcvr10BaseT = 0x00000000,
xcvr10Base2 = 0x00300000,
};
enum { /* Window 4 - diagnostic */
Wdiagnostic = 0x0004,
/* registers */
MediaStatus = 0x000A,
/* MediaStatus bits */
linkBeatDetect = 0x0800,
};
extern int etherelnk3reset(Ether*);
static char *tcmpcmcia[] = {
"3C589", /* 3COM 589[ABCD] */
"3C562", /* 3COM 562 */
"589E", /* 3COM Megahertz 589E */
nil,
};
static int
configASIC(Ether* ether, int port, int xcvr)
{
int x;
/* set Window 0 configuration registers */
COMMAND(port, SelectRegisterWindow, Wsetup);
outs(port+ConfigControl, Ena);
/* IRQ must be 3 on 3C589/3C562 */
outs(port + ResourceConfig, 0x3F00);
x = ins(port+AddressConfig) & ~xcvrMask9;
x |= (xcvr>>20)<<14;
outs(port+AddressConfig, x);
COMMAND(port, TxReset, 0);
while(STATUS(port) & commandInProgress)
;
COMMAND(port, RxReset, 0);
while(STATUS(port) & commandInProgress)
;
return etherelnk3reset(ether);
}
static int
reset(Ether* ether)
{
int i, t, slot;
char *type;
int port;
enum { WantAny, Want10BT, Want10B2 };
int want;
uchar ea[6];
char *p;
if(ether->irq == 0)
ether->irq = 10;
if(ether->port == 0)
ether->port = 0x240;
port = ether->port;
if(ioalloc(port, 0x10, 0, "3C589") < 0)
return -1;
type = nil;
slot = -1;
for(i = 0; tcmpcmcia[i] != nil; i++){
type = tcmpcmcia[i];
if((slot = pcmspecial(type, ether)) >= 0)
break;
}
ether->type = type; /* must be set before calling configASIC */
if(slot < 0){
iofree(port);
return -1;
}
/*
* Read Ethernet address from card memory
* on 3C562, but only if the user has not
* overridden it.
*/
memset(ea, 0, sizeof ea);
if(memcmp(ea, ether->ea, 6) == 0 && strcmp(type, "3C562") == 0) {
if(pcmcistuple(slot, 0x88, -1, ea, 6) == 6) {
for(i = 0; i < 6; i += 2){
t = ea[i];
ea[i] = ea[i+1];
ea[i+1] = t;
}
memmove(ether->ea, ea, 6);
}
}
/*
* Allow user to specify desired media in plan9.ini
*/
want = WantAny;
for(i = 0; i < ether->nopt; i++){
if(cistrncmp(ether->opt[i], "media=", 6) != 0)
continue;
p = ether->opt[i]+6;
if(cistrcmp(p, "10base2") == 0)
want = Want10B2;
else if(cistrcmp(p, "10baseT") == 0)
want = Want10BT;
}
/* try configuring as a 10BaseT */
if(want==WantAny || want==Want10BT){
if(configASIC(ether, port, xcvr10BaseT) < 0){
pcmspecialclose(slot);
iofree(port);
return -1;
}
delay(100);
COMMAND(port, SelectRegisterWindow, Wdiagnostic);
if((ins(port+MediaStatus)&linkBeatDetect) || want==Want10BT){
COMMAND(port, SelectRegisterWindow, Wop);
print("#l%d: xcvr10BaseT %s\n", ether->ctlrno, type);
return 0;
}
}
/* try configuring as a 10base2 */
if(want==WantAny || want==Want10B2){
COMMAND(port, GlobalReset, 0);
if(configASIC(ether, port, xcvr10Base2) < 0){
pcmspecialclose(slot);
iofree(port);
return -1;
}
print("#l%d: xcvr10Base2 %s\n", ether->ctlrno, type);
return 0;
}
return -1; /* not reached */
}
void
ether589link(void)
{
addethercard("3C589", reset);
addethercard("3C562", reset);
addethercard("589E", reset);
}