#include <sys/param.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <lib/libsa/stand.h>
#include <lib/libsa/net.h>
#include <dev/ic/i82557reg.h>
#include "globals.h"
#define FRAMESIZE 1536
#define CSR_WRITE_1(l, r, v) out8((l)->iobase+(r), (v))
#define CSR_READ_1(l, r) in8((l)->iobase+(r))
#define CSR_WRITE_2(l, r, v) out16rb((l)->iobase+(r), (v))
#define CSR_READ_2(l, r) in16rb((l)->iobase+(r))
#define CSR_WRITE_4(l, r, v) out32rb((l)->iobase+(r), (v))
#define CSR_READ_4(l, r) in32rb((l)->iobase+(r))
#define VTOPHYS(va) (uint32_t)(va)
#define DEVTOV(pa) (uint32_t)(pa)
#define wbinv(adr, siz) _wbinv(VTOPHYS(adr), (uint32_t)(siz))
#define inv(adr, siz) _inv(VTOPHYS(adr), (uint32_t)(siz))
#define DELAY(n) delay(n)
#define ALLOC(T,A) (T *)allocaligned(sizeof(T),(A))
struct txdesc {
volatile uint16_t cb_status;
volatile uint16_t cb_command;
volatile uint32_t link_addr;
volatile uint32_t tbd_array_addr;
volatile uint16_t byte_count;
volatile uint8_t tx_threshold;
volatile uint8_t tbd_number;
volatile uint32_t tx_buf_addr0;
volatile uint32_t tx_buf_size0;
volatile uint32_t tx_buf_addr1;
volatile uint32_t tx_buf_size1;
};
struct rxdesc {
volatile uint16_t rfa_status;
volatile uint16_t rfa_control;
volatile uint32_t link_addr;
volatile uint32_t rbd_addr;
volatile uint16_t actual_size;
volatile uint16_t size;
};
struct local {
struct txdesc txd;
uint8_t store[sizeof(struct rxdesc) + FRAMESIZE];
unsigned iobase;
unsigned eeprom_addr;
};
static void autosize_eeprom(struct local *);
static int read_eeprom(struct local *, int);
static void fxp_scb_wait(struct local *);
#ifdef DEBUG
static int fxp_mdi_read(struct local *, int, int);
#endif
static uint8_t fxp_cb_config_template[] = {
0x0, 0x0,
0x80, 0x2,
0xff, 0xff, 0xff, 0xff,
0x16,
0x8,
0x0,
0x0,
0x0,
0x80,
0xb2,
0x3,
0x1,
0x0,
0x26,
0x0,
0x60,
0x0,
0xf2,
0x48,
0x0,
0x40,
0xf3,
0x0,
0x3f,
0x5
};
static struct fxp_cb_config store_cbc;
static struct fxp_cb_ias store_cbi;
int
fxp_match(unsigned tag, void *data)
{
unsigned v;
v = pcicfgread(tag, PCI_ID_REG);
switch (v) {
case PCI_DEVICE(0x8086, 0x1209):
case PCI_DEVICE(0x8086, 0x1229):
return 1;
}
return 0;
}
void *
fxp_init(unsigned tag, void *data)
{
struct local *sc;
uint8_t *en = data;
struct fxp_cb_config *cbp = &store_cbc;
struct fxp_cb_ias *cb_ias = &store_cbi;
struct rxdesc *rfa;
unsigned v, i;
sc = ALLOC(struct local, sizeof(struct txdesc));
memset(sc, 0, sizeof(struct local));
sc->iobase = DEVTOV(pcicfgread(tag, 0x10));
CSR_WRITE_4(sc, FXP_CSR_PORT, FXP_PORT_SELECTIVE_RESET);
DELAY(100);
autosize_eeprom(sc);
v = read_eeprom(sc, 0); en[0] = v; en[1] = v >> 8;
v = read_eeprom(sc, 1); en[2] = v; en[3] = v >> 8;
v = read_eeprom(sc, 2); en[4] = v; en[5] = v >> 8;
printf("MAC address %02x:%02x:%02x:%02x:%02x:%02x, ",
en[0], en[1], en[2], en[3], en[4], en[5]);
DPRINTF(("PHY %d (%04x.%04x)\n", fxp_mdi_read(sc, 1, 18),
fxp_mdi_read(sc, 1, 2), fxp_mdi_read(sc, 1, 3)));
CSR_WRITE_4(sc, FXP_CSR_SCB_GENERAL, 0);
CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_CU_BASE);
fxp_scb_wait(sc);
CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_RU_BASE);
memcpy(cbp, fxp_cb_config_template, sizeof(fxp_cb_config_template));
#define prm 0
#define phy_10Mbps_only 0
#define all_mcasts 0
cbp->cb_status = 0;
cbp->cb_command = htole16(FXP_CB_COMMAND_CONFIG |
FXP_CB_COMMAND_EL);
cbp->link_addr = -1;
cbp->byte_count = 22;
cbp->rx_fifo_limit = 8;
cbp->tx_fifo_limit = 0;
cbp->adaptive_ifs = 0;
cbp->rx_dma_bytecount = 0;
cbp->tx_dma_bytecount = 0;
cbp->dma_mbce = 0;
cbp->late_scb = 0;
cbp->tno_int_or_tco_en = 0;
cbp->ci_int = 0;
cbp->save_bf = prm;
cbp->disc_short_rx = !prm;
cbp->underrun_retry = 1;
cbp->mediatype = !phy_10Mbps_only;
cbp->nsai = 1;
cbp->preamble_length = 2;
cbp->loopback = 0;
cbp->linear_priority = 0;
cbp->linear_pri_mode = 0;
cbp->interfrm_spacing = 6;
cbp->promiscuous = prm;
cbp->bcast_disable = 0;
cbp->crscdt = 0;
cbp->stripping = !prm;
cbp->padding = 1;
cbp->rcv_crc_xfer = 0;
cbp->force_fdx = 0;
cbp->fdx_pin_en = 1;
cbp->multi_ia = 0;
cbp->mc_all = all_mcasts;
#undef prm
#undef phy_10Mbps_only
#undef all_mcasts
wbinv(cbp, sizeof(*cbp));
fxp_scb_wait(sc);
CSR_WRITE_4(sc, FXP_CSR_SCB_GENERAL, VTOPHYS(cbp));
CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_CU_START);
i = 1000;
while (!(le16toh(cbp->cb_status) & FXP_CB_STATUS_C) && --i > 0) {
DELAY(1);
inv(&cbp->cb_status, sizeof(cbp->cb_status));
}
if (i == 0)
printf("cbp config timeout\n");
cb_ias->cb_status = 0;
cb_ias->cb_command = htole16(FXP_CB_COMMAND_IAS | FXP_CB_COMMAND_EL);
cb_ias->link_addr = -1;
memcpy(cb_ias->macaddr, en, 6);
wbinv(cb_ias, sizeof(*cb_ias));
fxp_scb_wait(sc);
CSR_WRITE_4(sc, FXP_CSR_SCB_GENERAL, VTOPHYS(cb_ias));
CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_CU_START);
i = 1000;
while (!(le16toh(cb_ias->cb_status) & FXP_CB_STATUS_C) && --i > 0) {
DELAY(1);
inv(&cb_ias->cb_status, sizeof(cb_ias->cb_status));
}
if (i == 0)
printf("ias config timeout\n");
rfa = (struct rxdesc *)sc->store;
rfa->rfa_status = 0;
rfa->rfa_control = htole16(FXP_RFA_CONTROL_S);
rfa->link_addr = htole32(VTOPHYS(rfa));
rfa->rbd_addr = -1;
rfa->actual_size = 0;
rfa->size = htole16(sizeof(sc->store) - sizeof(struct rxdesc));
wbinv(rfa, sizeof(sc->store));
fxp_scb_wait(sc);
CSR_WRITE_4(sc, FXP_CSR_SCB_GENERAL, VTOPHYS(rfa));
CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_RU_START);
return sc;
}
int
fxp_send(void *dev, char *buf, unsigned len)
{
struct local *l = dev;
struct txdesc *txd;
int loop;
if (len > 1520)
printf("fxp_send: len > 1520 (%u)\n", len);
txd = &l->txd;
txd->cb_status = 0;
txd->cb_command =
htole16(FXP_CB_COMMAND_XMIT|FXP_CB_COMMAND_SF|FXP_CB_COMMAND_EL);
txd->link_addr = -1;
txd->tbd_array_addr = htole32(VTOPHYS(&txd->tx_buf_addr0));
txd->tx_buf_addr0 = htole32(VTOPHYS(buf));
txd->tx_buf_size0 = htole32(len);
txd->byte_count = htole16(0x8000);
txd->tx_threshold = 0x20;
txd->tbd_number = 1;
wbinv(buf, len);
wbinv(txd, sizeof(*txd));
fxp_scb_wait(l);
CSR_WRITE_4(l, FXP_CSR_SCB_GENERAL, VTOPHYS(txd));
CSR_WRITE_1(l, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_CU_START);
loop = 10000;
while (!(le16toh(txd->cb_status) & FXP_CB_STATUS_C) && --loop > 0) {
DELAY(1);
inv(txd, sizeof(struct txdesc));
}
if (loop == 0)
printf("send timeout\n");
return len;
}
int
fxp_recv(void *dev, char *buf, unsigned maxlen, unsigned timo)
{
struct local *l = dev;
struct rxdesc *rfa;
unsigned bound, ruscus, len;
fxp_scb_wait(l);
CSR_WRITE_1(l, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_RU_RESUME);
bound = 1000 * timo;
do {
ruscus = CSR_READ_1(l, FXP_CSR_SCB_RUSCUS);
if (((ruscus >> 2) & 0x0f) != FXP_SCB_RUS_READY
&& (((ruscus >> 2) & 0x0f) == FXP_SCB_RUS_SUSPENDED))
goto gotone;
DELAY(1000);
} while (--bound > 0);
errno = 0;
return -1;
gotone:
rfa = (struct rxdesc *)l->store;
inv(rfa, sizeof(l->store));
if ((le16toh(rfa->rfa_status) & FXP_RFA_STATUS_C) == 0)
return 0;
len = le16toh(rfa->actual_size) & 0x7ff;
if (len > maxlen)
len = maxlen;
memcpy(buf, &l->store[sizeof(struct rxdesc)], len);
rfa->rfa_status = 0;
rfa->rfa_control = htole16(FXP_RFA_CONTROL_S);
rfa->actual_size = 0;
wbinv(rfa, sizeof(struct rxdesc));
#if 0
fxp_scb_wait(l);
CSR_WRITE_1(l, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_RU_RESUME);
#endif
return len;
}
static void
eeprom_shiftin(struct local *sc, int data, int len)
{
uint16_t reg;
int x;
for (x = 1 << (len - 1); x != 0; x >>= 1) {
DELAY(40);
if (data & x)
reg = FXP_EEPROM_EECS | FXP_EEPROM_EEDI;
else
reg = FXP_EEPROM_EECS;
CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg);
DELAY(40);
CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL,
reg | FXP_EEPROM_EESK);
DELAY(40);
CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg);
}
DELAY(40);
}
void
autosize_eeprom(struct local *sc)
{
int x;
CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, FXP_EEPROM_EECS);
DELAY(40);
eeprom_shiftin(sc, FXP_EEPROM_OPC_READ, 3);
for (x = 1; x <= 8; x++) {
CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, FXP_EEPROM_EECS);
DELAY(40);
CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL,
FXP_EEPROM_EECS | FXP_EEPROM_EESK);
DELAY(40);
if ((CSR_READ_2(sc, FXP_CSR_EEPROMCONTROL) &
FXP_EEPROM_EEDO) == 0)
break;
DELAY(40);
CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, FXP_EEPROM_EECS);
DELAY(40);
}
CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, 0);
DELAY(40);
if (x != 6 && x != 8)
printf("fxp: strange EEPROM address size (%d)\n", x);
else
sc->eeprom_addr = x;
}
static int
read_eeprom(struct local *sc, int offset)
{
uint16_t reg;
int x, val;
CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, FXP_EEPROM_EECS);
eeprom_shiftin(sc, FXP_EEPROM_OPC_READ, 3);
eeprom_shiftin(sc, offset, sc->eeprom_addr);
reg = FXP_EEPROM_EECS;
val = 0;
for (x = 16; x > 0; x--) {
CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL,
reg | FXP_EEPROM_EESK);
DELAY(1);
if (CSR_READ_2(sc, FXP_CSR_EEPROMCONTROL) &
FXP_EEPROM_EEDO)
val |= (1 << (x - 1));
CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg);
DELAY(1);
}
CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, 0);
DELAY(1);
return val;
}
static void
fxp_scb_wait(struct local *sc)
{
int loop = 5000;
while (CSR_READ_1(sc, FXP_CSR_SCB_COMMAND) && --loop > 0)
DELAY(2);
if (loop == 0)
printf("SCB timeout\n");
}
#ifdef DEBUG
static int
fxp_mdi_read(struct local *sc, int phy, int reg)
{
int count = 10000;
int value;
CSR_WRITE_4(sc, FXP_CSR_MDICONTROL,
(FXP_MDI_READ << 26) | (reg << 16) | (phy << 21));
while (((value = CSR_READ_4(sc, FXP_CSR_MDICONTROL)) &
0x10000000) == 0 && count--)
DELAY(10);
if (count <= 0)
printf("fxp_mdi_read: timed out\n");
return (value & 0xffff);
}
#endif