Driver for the Winbond 840 NIC, written by myself. It's working. It lacks speed autonegotiation, and some other cool things.

git-svn-id: file:///srv/svn/repos/haiku/trunk/current@6950 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Stefano Ceccherini 2004-03-11 11:09:38 +00:00
parent 92fd335690
commit d8ee7d30df
9 changed files with 1760 additions and 0 deletions

View File

@ -0,0 +1,12 @@
#ifndef __MYDEBUG_H
#define __MYDEBUG_H
#define ARGS (const char *, ...)
#ifdef DEBUG
#define LOG(ARGS) dprintf ARGS
#else
#define LOG(ARGS) ;
#endif // DEBUG
#endif //__MYDEBUG_H

View File

@ -0,0 +1,344 @@
#include <KernelExport.h>
#include <Errors.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "debug.h"
#include "device.h"
#include "driver.h"
#include "interface.h"
#include "wb840.h"
#define MAX_CARDS 4
extern char * gDevNameList[];
extern pci_info *gDevList[];
static int32 gOpenMask = 0;
static status_t
wb840_open(const char *name, uint32 flags, void** cookie)
{
char *deviceName = NULL;
int32 i;
int32 mask;
struct wb_device *data;
LOG((DEVICE_NAME ": open()\n"));
for (i = 0; (deviceName = gDevNameList[i]) != NULL; i++) {
if (!strcmp(name, deviceName))
break;
}
if (deviceName == NULL) {
LOG(("invalid device name"));
return EINVAL;
}
/* There can be only one access at time */
mask = 1L << i;
if (atomic_or(&gOpenMask, mask) & mask) {
return B_BUSY;
}
/* Allocate a wb_device structure */
if (!(data = (wb_device *)malloc(sizeof(wb_device)))) {
gOpenMask &= ~(1L << i);
return B_NO_MEMORY;
}
memset(data, 0, sizeof(wb_device));
*cookie = data;
#ifdef DEBUG
load_driver_symbols("wb840");
#endif
data->devId = i;
data->pciInfo = gDevList[i];
data->deviceName = gDevNameList[i];
data->blockFlag = 0;
/* Let's map card registers to host's memory */
data->reg_base = data->pciInfo->u.h0.base_registers[0];
LOG(("wb840: reg_base=%x\n", (int)data->reg_base));
wb_read_eeprom(data, &data->myaddr, 0, 3, 0);
print_address((void*)&data->myaddr);
data->wb_cachesize = gPci->read_pci_config(data->pciInfo->bus, data->pciInfo->device,
data->pciInfo->function, PCI_line_size, sizeof (PCI_line_size)) & 0xff;
if (wb_create_semaphores(data) == B_OK) {
/* reset the card */
wb_reset(data);
/* Setup interrupts */
data->irq = data->pciInfo->u.h0.interrupt_line;
install_io_interrupt_handler(data->irq, wb_interrupt, data, 0);
LOG(("Interrupts installed at irq line %x\n", data->irq));
wb_enable_interrupts(data);
wb_create_rings(data);
WB_SETBIT(data->reg_base + WB_NETCFG, WB_NETCFG_RX_ON);
write32(data->reg_base + WB_RXSTART, 0xFFFFFFFF);
WB_SETBIT(data->reg_base + WB_NETCFG, WB_NETCFG_TX_ON);
//write32(data->reg_base + WB_TXSTART, 0xFFFFFFFF);
//dump_registers(data);
add_timer(&data->timer, wb_tick, 1000000LL, B_PERIODIC_TIMER);
#if DEBUG
dump_registers(data);
#endif
return B_OK; // Everything after this line is an error
} else
LOG((DEVICE_NAME ": Couldn't create semaphores\n"));
gOpenMask &= ~(1L << i);
free(data);
LOG(("wb840: Open Failed\n"));
return B_ERROR;
}
static status_t
wb840_read(void* cookie, off_t position, void *buf, size_t* num_bytes)
{
wb_device *device = (wb_device*)cookie;
int16 current;
status_t status;
size_t size;
int32 blockFlag;
uint32 check;
LOG((DEVICE_NAME ": read()\n"));
blockFlag = device->blockFlag;
if (atomic_or(&device->rxLock, 1)) {
*num_bytes = 0;
return B_ERROR;
}
if ((status = acquire_sem_etc(device->rxSem, 1, B_CAN_INTERRUPT | blockFlag, 0)) != B_OK) {
atomic_and(&device->rxLock, 0);
*num_bytes = 0;
return status;
}
current = device->rxCurrent;
check = device->rxDescriptor[current].wb_status;
if (check & WB_RXSTAT_OWN) {
LOG(("ERROR: read: buffer %d still in use: %x\n", (int)current, (int)status));
atomic_and(&device->rxLock, 0);
*num_bytes = 0;
return B_BUSY;
}
if (check & (WB_RXSTAT_RXERR | WB_RXSTAT_CRCERR | WB_RXSTAT_RUNT)) {
LOG(("Error read: packet with errors."));
*num_bytes = 0;
} else {
size = WB_RXBYTES(check);
LOG((DEVICE_NAME": received %d bytes\n", (int)size));
if (size > WB_MAX_FRAMELEN || size > *num_bytes) {
LOG(("ERROR: Bad frame size: %d", (int)size));
size = *num_bytes;
}
memcpy(buf, (void *)device->rxBuffer[current], size);
}
device->rxCurrent = (current + 1) & WB_RX_CNT_MASK;
{
cpu_status former;
former = disable_interrupts();
acquire_spinlock(&device->rxSpinlock);
// release buffer to ring
wb_free_rx_descriptor(&device->rxDescriptor[current]);
device->rxFree++;
release_spinlock(&device->rxSpinlock);
restore_interrupts(former);
}
atomic_and(&device->rxLock, 0);
return B_OK;
}
static status_t
wb840_write(void* cookie, off_t position, const void* buffer, size_t* num_bytes)
{
wb_device *device = (wb_device*)cookie;
status_t status = B_OK;
uint16 frameSize;
int16 current;
uint32 check;
LOG((DEVICE_NAME ": write()\n"));
atomic_add(&device->txLock, 1);
if (*num_bytes > WB_MAX_FRAMELEN)
*num_bytes = WB_MAX_FRAMELEN;
frameSize = *num_bytes;
current = device->txCurrent;
// block until a free tx descriptor is available
if ((status = acquire_sem_etc(device->txSem, 1, B_TIMEOUT, ETHER_TRANSMIT_TIMEOUT)) < B_OK) {
write32(device->reg_base + WB_TXSTART, 0xFFFFFFFF);
LOG(("write: acquiring sem failed: %d, %s\n", (int)status, strerror(status)));
atomic_add(&device->txLock, -1);
*num_bytes = 0;
return status;
}
check = device->txDescriptor[current].wb_status;
if (check & WB_TXSTAT_OWN) {
// descriptor is still in use
dprintf(DEVICE_NAME ": card owns buffer %d\n", (int)current);
atomic_add(&device->txLock, -1);
*num_bytes = 0;
return B_ERROR;
}
/* Copy data to tx buffer */
memcpy((void *)device->txBuffer[current], buffer, frameSize);
device->txCurrent = (current + 1) & WB_TX_CNT_MASK;
LOG((DEVICE_NAME ": %d bytes written\n", frameSize));
{
cpu_status former;
former = disable_interrupts();
acquire_spinlock(&device->txSpinlock);
device->txDescriptor[current].wb_ctl |= frameSize | WB_TXCTL_FIRSTFRAG | WB_TXCTL_LASTFRAG;
device->txDescriptor[current].wb_status = WB_TXSTAT_OWN;
device->txSent++;
release_spinlock(&device->txSpinlock);
restore_interrupts(former);
}
// re-enable transmit state machine
write32(device->reg_base + WB_TXSTART, 0xFFFFFFFF);
atomic_add(&device->txLock, -1);
return B_OK;
}
static status_t
wb840_control (void *cookie, uint32 op, void *arg, size_t len)
{
wb_device *data = (wb_device *)cookie;
LOG((DEVICE_NAME ": control()\n"));
switch (op) {
case ETHER_INIT:
LOG(("%s: ETHER_INIT\n", data->deviceName));
return B_OK;
case ETHER_GETADDR:
LOG(("%s: ETHER_GETADDR\n", data->deviceName));
memcpy(arg, &data->myaddr, sizeof(data->myaddr));
print_address(arg);
return B_OK;
case ETHER_NONBLOCK:
LOG(("ETHER_NON_BLOCK\n"));
data->blockFlag = *(int32 *)arg ? B_TIMEOUT : 0;
return B_OK;
case ETHER_ADDMULTI:
LOG(("ETHER_ADDMULTI\n"));
break;
case ETHER_REMMULTI:
LOG(("ETHER_REMMULTI\n"));
return B_OK;
case ETHER_SETPROMISC:
LOG(("ETHER_SETPROMISC\n"));
return B_OK;
case ETHER_GETFRAMESIZE:
LOG(("ETHER_GETFRAMESIZE\n"));
*(uint32*)arg = WB_MAX_FRAMELEN;
return B_OK;
default:
LOG(("Invalid command\n"));
break;
}
return B_ERROR;
}
static status_t
wb840_close(void* cookie)
{
wb_device *device = (wb_device *)cookie;
LOG((DEVICE_NAME ": close()\n"));
cancel_timer(&device->timer);
// disable the transmitter's and receiver's state machine
WB_CLRBIT(device->reg_base + WB_NETCFG, (WB_NETCFG_RX_ON|WB_NETCFG_TX_ON));
write32(device->reg_base + WB_TXADDR, 0x00000000);
write32(device->reg_base + WB_RXADDR, 0x00000000);
wb_disable_interrupts(device);
remove_io_interrupt_handler(device->irq, wb_interrupt, device);
delete_sem(device->rxSem);
delete_sem(device->txSem);
return B_OK;
}
static status_t
wb840_free(void* cookie)
{
wb_device *device = (wb_device*)cookie;
LOG((DEVICE_NAME ": free()\n"));
gOpenMask &= ~(1L << device->devId);
wb_delete_rings(device);
free(device);
return B_OK;
}
device_hooks gDeviceHooks = {
wb840_open, /* -> open entry point */
wb840_close, /* -> close entry point */
wb840_free, /* -> free cookie */
wb840_control, /* -> control entry point */
wb840_read, /* -> read entry point */
wb840_write, /* -> write entry point */
NULL,
NULL,
NULL,
NULL
};

View File

@ -0,0 +1,28 @@
#ifndef __WB_DRIVER_H
#define __WB_DRIVER_H
// PCI Communications
#include <PCI.h>
#define IO_PORT_PCI_ACCESS true
//#define MEMORY_MAPPED_PCI_ACCESS true
#if IO_PORT_PCI_ACCESS
# define write8(address,value) (*gPci->write_io_8)((address),(value))
# define write16(address,value) (*gPci->write_io_16)((address),(value))
# define write32(address,value) (*gPci->write_io_32)((address),(value))
# define read8(address) ((*gPci->read_io_8)(address))
# define read16(address) ((*gPci->read_io_16)(address))
# define read32(address) ((*gPci->read_io_32)(address))
#else /* MEMORY_MAPPED_PCI_ACCESS */
# define read8(address) (*((volatile uint8*)(address)))
# define read16(address) (*((volatile uint16*)(address)))
# define read32(address) (*((volatile uint32*)(address)))
# define write8(address,data) (*((volatile uint8 *)(address)) = data)
# define write16(address,data) (*((volatile uint16 *)(address)) = (data))
# define write32(address,data) (*((volatile uint32 *)(address)) = (data))
#endif
extern pci_module_info *gPci;
#endif

View File

@ -0,0 +1,134 @@
/* Copyright (c) 2003-2004
* Stefano Ceccherini <burton666@libero.it>. All rights reserved.
*/
#include "debug.h"
#include <Debug.h>
#include <KernelExport.h>
#include <Errors.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "wb840.h"
#include "device.h"
#include "driver.h"
#define MAX_CARDS 4
int32 api_version = B_CUR_DRIVER_API_VERSION;
pci_module_info *gPci;
char* gDevNameList[MAX_CARDS + 1];
pci_info *gDevList[MAX_CARDS];
static bool
probe(pci_info *item)
{
if ((item->vendor_id == WB_VENDORID && item->device_id == WB_DEVICEID_840F)
|| (item->vendor_id == CP_VENDORID && item->device_id == CP_DEVICEID_RL100))
return true;
return false;
}
status_t
init_hardware (void)
{
LOG((DEVICE_NAME ": init_hardware\n"));
return B_OK;
}
status_t
init_driver (void)
{
struct pci_info *item = NULL;
int index = 0;
int card_found = 0;
char devName[64];
LOG((DEVICE_NAME ": init_driver\n"));
#ifdef DEBUG
set_dprintf_enabled(true);
#endif
if (get_module(B_PCI_MODULE_NAME, (module_info **)&gPci) < B_OK)
return B_ERROR;
item = (pci_info *)malloc(sizeof(pci_info));
while (gPci->get_nth_pci_info(index, item) == B_OK) {
if (probe(item)) {
gPci->write_pci_config(item->bus, item->device, item->function,
PCI_command, 2, PCI_command_master | gPci->read_pci_config(
item->bus, item->device, item->function,
PCI_command, 2));
gDevList[card_found++] = item;
dprintf(DEVICE_NAME ": revision = %x\n", item->revision);
item = (pci_info *)malloc(sizeof(pci_info));
}
index++;
}
free(item);
gDevList[card_found] = NULL;
if (card_found == 0) {
put_module(B_PCI_MODULE_NAME);
return ENODEV;
}
for (index = 0; index < card_found; index++) {
sprintf(devName, DEVICE_NAME "/%d", index);
LOG((DEVICE_NAME ":enabled %s\n", devName));
gDevNameList[index] = strdup(devName);
}
gDevNameList[index] = NULL;
return B_OK;
}
void
uninit_driver(void)
{
int32 i = 0;
LOG((DEVICE_NAME ": uninit_driver()\n"));
while(gDevNameList[i] != NULL) {
free(gDevList[i]);
free(gDevNameList[i]);
i++;
}
put_module(B_PCI_MODULE_NAME);
}
const char**
publish_devices()
{
return (const char**)gDevNameList;
}
device_hooks*
find_device(const char* name)
{
int32 i;
char *item;
LOG((DEVICE_NAME ": find_device()\n"));
// Find device name
for (i = 0; (item = gDevNameList[i]); i++) {
if (!strcmp(name, item)) {
return &gDeviceHooks;
}
}
return NULL; // Device not found
}

View File

@ -0,0 +1,17 @@
#ifndef __DRIVER_H
#define __DRIVER_H
#include <Drivers.h>
#include <PCI.h>
#include "ether_driver.h"
#define DEVICE_NAME "net/wb840"
extern pci_module_info *gPci;
extern char* gDevNameList[];
extern pci_info *gDevList[];
extern device_hooks gDeviceHooks;
#endif // __WB840_H

View File

@ -0,0 +1,371 @@
/*
* Copyright (c) 1997, 1998
* Bill Paul <wpaul@ctr.columbia.edu>. 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.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Bill Paul.
* 4. Neither the name of the author nor the names of any co-contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD
* 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.
*
*/
/* Adapted for BeOS by Stefano Ceccherini <burton666@libero.it> */
#include "wb840.h"
#include "device.h"
#include "interface.h"
#include <ByteOrder.h>
#include <KernelExport.h>
#define SIO_SET(x) \
write32(device->reg_base + WB_SIO, \
read32(device->reg_base + WB_SIO) | x)
#define SIO_CLR(x) \
write32(device->reg_base + WB_SIO, \
read32(device->reg_base + WB_SIO) & ~x)
#define MII_DELAY(x) read32(x->reg_base + WB_SIO)
void
wb_eeprom_putbyte(wb_device *device, int addr)
{
int d, i;
int delay;
d = addr | WB_EECMD_READ;
/*
* Feed in each bit and strobe the clock.
*/
for (i = 0x400; i; i >>= 1) {
if (d & i) {
SIO_SET(WB_SIO_EE_DATAIN);
} else {
SIO_CLR(WB_SIO_EE_DATAIN);
}
for (delay = 0; delay < 100; delay++)
MII_DELAY(device);
SIO_SET(WB_SIO_EE_CLK);
for (delay = 0; delay < 150; delay++)
MII_DELAY(device);
SIO_CLR(WB_SIO_EE_CLK);
for (delay = 0; delay < 100; delay++)
MII_DELAY(device);
}
return;
}
static void
mii_sync(struct wb_device *device)
{
// Set data bit and strobe the clock 32 times
int bits = 32;
SIO_SET(WB_SIO_MII_DIR|WB_SIO_MII_DATAIN);
while (--bits >= 0) {
SIO_SET(WB_SIO_MII_CLK);
MII_DELAY(device);
SIO_CLR(WB_SIO_MII_CLK);
MII_DELAY(device);
}
}
static void
mii_send(wb_device *device, uint32 bits, int count)
{
int i;
SIO_CLR(WB_SIO_MII_CLK);
for (i = (0x1 << (count - 1)); i; i >>= 1) {
if (bits & i)
SIO_SET(WB_SIO_MII_DATAIN);
else
SIO_CLR(WB_SIO_MII_DATAIN);
MII_DELAY(device);
SIO_CLR(WB_SIO_MII_CLK);
MII_DELAY(device);
SIO_SET(WB_SIO_MII_CLK);
}
}
/*
* Read an PHY register through the MII.
*/
int
wb_mii_readreg(wb_device *device, wb_mii_frame *frame)
{
int i, ack;
/*
* Set up frame for RX.
*/
frame->mii_stdelim = WB_MII_STARTDELIM;
frame->mii_opcode = WB_MII_READOP;
frame->mii_turnaround = 0;
frame->mii_data = 0;
write32(device->reg_base + WB_SIO, 0);
/*
* Turn on data xmit.
*/
SIO_SET(WB_SIO_MII_DIR);
mii_sync(device);
/*
* Send command/address info.
*/
mii_send(device, frame->mii_stdelim, 2);
mii_send(device, frame->mii_opcode, 2);
mii_send(device, frame->mii_phyaddr, 5);
mii_send(device, frame->mii_regaddr, 5);
/* Idle bit */
SIO_CLR((WB_SIO_MII_CLK|WB_SIO_MII_DATAIN));
MII_DELAY(device);
SIO_SET(WB_SIO_MII_CLK);
MII_DELAY(device);
/* Turn off xmit. */
SIO_CLR(WB_SIO_MII_DIR);
/* Check for ack */
SIO_CLR(WB_SIO_MII_CLK);
MII_DELAY(device);
SIO_SET(WB_SIO_MII_CLK);
MII_DELAY(device);
ack = read32(device->reg_base + WB_SIO) & WB_SIO_MII_DATAOUT;
SIO_CLR(WB_SIO_MII_CLK);
MII_DELAY(device);
SIO_SET(WB_SIO_MII_CLK);
MII_DELAY(device);
/*
* Now try reading data bits. If the ack failed, we still
* need to clock through 16 cycles to keep the PHY(s) in sync.
*/
if (ack) {
for(i = 0; i < 16; i++) {
SIO_CLR(WB_SIO_MII_CLK);
MII_DELAY(device);
SIO_SET(WB_SIO_MII_CLK);
MII_DELAY(device);
}
goto fail;
}
for (i = 0x8000; i; i >>= 1) {
SIO_CLR(WB_SIO_MII_CLK);
MII_DELAY(device);
if (!ack) {
if (read32(device->reg_base + WB_SIO) & WB_SIO_MII_DATAOUT)
frame->mii_data |= i;
MII_DELAY(device);
}
SIO_SET(WB_SIO_MII_CLK);
MII_DELAY(device);
}
fail:
SIO_CLR(WB_SIO_MII_CLK);
MII_DELAY(device);
SIO_SET(WB_SIO_MII_CLK);
MII_DELAY(device);
if (ack)
return 1;
return 0;
}
/*
* Write to a PHY register through the MII.
*/
int
wb_mii_writereg(wb_device *device, wb_mii_frame *frame)
{
/*
* Set up frame for TX.
*/
frame->mii_stdelim = WB_MII_STARTDELIM;
frame->mii_opcode = WB_MII_WRITEOP;
frame->mii_turnaround = WB_MII_TURNAROUND;
/*
* Turn on data output.
*/
SIO_SET(WB_SIO_MII_DIR);
mii_sync(device);
mii_send(device, frame->mii_stdelim, 2);
mii_send(device, frame->mii_opcode, 2);
mii_send(device, frame->mii_phyaddr, 5);
mii_send(device, frame->mii_regaddr, 5);
mii_send(device, frame->mii_turnaround, 2);
mii_send(device, frame->mii_data, 16);
/* Idle bit. */
SIO_SET(WB_SIO_MII_CLK);
MII_DELAY(device);
SIO_CLR(WB_SIO_MII_CLK);
MII_DELAY(device);
/*
* Turn off xmit.
*/
SIO_CLR(WB_SIO_MII_DIR);
return 0;
}
int
wb_miibus_readreg(wb_device *device, int phy, int reg)
{
struct wb_mii_frame frame;
memset(&frame, 0, sizeof(frame));
frame.mii_phyaddr = phy;
frame.mii_regaddr = reg;
wb_mii_readreg(device, &frame);
return(frame.mii_data);
}
void
wb_miibus_writereg(wb_device *device, int phy, int reg, int data)
{
struct wb_mii_frame frame;
memset(&frame, 0, sizeof(frame));
frame.mii_phyaddr = phy;
frame.mii_regaddr = reg;
frame.mii_data = data;
wb_mii_writereg(device, &frame);
return;
}
void
wb_miibus_statchg(wb_device *device)
{
//wb_setcfg(device, device->sc_mii.mii_media_active);
}
#define EEPROM_DELAY(x) read32(x->reg_base + WB_SIO)
static void
wb_eeprom_askdata(wb_device *device, int addr)
{
int command, i;
int delay;
command = addr | WB_EECMD_READ;
/* Feed in each bit and strobe the clock. */
for(i = 0x400; i; i >>= 1) {
if (command & i)
SIO_SET(WB_SIO_EE_DATAIN);
else
SIO_CLR(WB_SIO_EE_DATAIN);
SIO_SET(WB_SIO_EE_CLK);
SIO_CLR(WB_SIO_EE_CLK);
for (delay = 0; delay < 100; delay++)
EEPROM_DELAY(device);
}
}
/* Read a word of data stored in the EEPROM at address "addr". */
static void
wb_eeprom_getword(wb_device *device, int addr, uint16 *dest)
{
int i;
uint16 word = 0;
/* Enter EEPROM access mode */
write32(device->reg_base + WB_SIO, WB_SIO_EESEL|WB_SIO_EE_CS);
/* Send address of word we want to read. */
wb_eeprom_askdata(device, addr);
write32(device->reg_base + WB_SIO, WB_SIO_EESEL|WB_SIO_EE_CS);
/* Start reading bits from EEPROM */
for (i = 0x8000; i > 0; i >>= 1) {
SIO_SET(WB_SIO_EE_CLK);
if (read32(device->reg_base + WB_SIO) & WB_SIO_EE_DATAOUT)
word |= i;
SIO_CLR(WB_SIO_EE_CLK);
}
/* Turn off EEPROM access mode */
write32(device->reg_base + WB_SIO, 0);
*dest = word;
}
void
wb_read_eeprom(wb_device *device, void* dest,
int offset, int count, int swap)
{
int i;
uint16 word = 0, *ptr;
for (i = 0; i < count; i++) {
wb_eeprom_getword(device, offset + i, &word);
ptr = (uint16*)(dest + (i * 2));
if (swap)
*ptr = ntohs(word);
else
*ptr = word;
}
}

View File

@ -0,0 +1,9 @@
#ifndef __WB_INTERFACE_H
#define __WB_INTERFACE_H
#include "wb840.h"
extern void wb_read_eeprom(struct wb_device *, void* dest, int offset, int count, int swap);
extern uint16 mii_read(struct wb_device *, int phy_id, int location);
#endif

View File

@ -0,0 +1,446 @@
/* Copyright (c) 2003-2004
* Stefano Ceccherini <burton666@libero.it>. All rights reserved.
*/
#include "device.h"
#include "driver.h"
#include "debug.h"
#include "ether_driver.h"
#include "interface.h"
#include "wb840.h"
#include <ByteOrder.h>
#include <KernelExport.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define ROUND_TO_PAGE_SIZE(x) (((x) + (B_PAGE_SIZE) - 1) & ~((B_PAGE_SIZE) - 1))
static uint32
physicalAddress(volatile void *addr, uint32 length)
{
physical_entry table;
get_memory_map((void*)addr, length, &table, 1);
return (uint32)table.address;
}
#if DEBUG
static void
dump_registers(wb_device *device)
{
LOG(("Registers dump:\n"));
LOG(("WB_BUSCTL: %x\n", (int)read32(device->reg_base + WB_BUSCTL)));
LOG(("WB_TXSTART: %x\n", (int)read32(device->reg_base + WB_TXSTART)));
LOG(("WB_RXSTART: %x\n", (int)read32(device->reg_base + WB_RXSTART)));
LOG(("WB_RXADDR: %x\n", (int)read32(device->reg_base + WB_RXADDR)));
LOG(("WB_TXADDR: %x\n", (int)read32(device->reg_base + WB_TXADDR)));
LOG(("WB_ISR: %x\n", (int)read32(device->reg_base + WB_ISR)));
LOG(("WB_NETCFG: %x\n", (int)read32(device->reg_base + WB_NETCFG)));
LOG(("WB_IMR: %x\n", (int)read32(device->reg_base + WB_IMR)));
LOG(("WB_FRAMESDISCARDED: %x\n", (int)read32(device->reg_base + WB_FRAMESDISCARDED)));
LOG(("WB_SIO: %x\n", (int)read32(device->reg_base + WB_SIO)));
LOG(("WB_BOOTROMADDR: %x\n", (int)read32(device->reg_base + WB_BOOTROMADDR)));
LOG(("WB_TIMER: %x\n", (int)read32(device->reg_base + WB_TIMER)));
LOG(("WB_CURRXCTL: %x\n", (int)read32(device->reg_base + WB_CURRXCTL)));
LOG(("WB_CURRXBUF: %x\n", (int)read32(device->reg_base + WB_CURRXBUF)));
LOG(("WB_MAR0: %x\n", (int)read32(device->reg_base + WB_MAR0)));
LOG(("WB_MAR1: %x\n", (int)read32(device->reg_base + WB_MAR1)));
LOG(("WB_NODE0: %x\n", (int)read32(device->reg_base + WB_NODE0)));
LOG(("WB_NODE1: %x\n", (int)read32(device->reg_base + WB_NODE1)));
LOG(("WB_BOOTROMSIZE: %x\n", (int)read32(device->reg_base + WB_BOOTROMSIZE)));
LOG(("WB_CURTXCTL: %x\n", (int)read32(device->reg_base + WB_CURTXCTL)));
LOG(("WB_CURTXBUF: %x\n", (int)read32(device->reg_base + WB_CURTXBUF)));
}
#endif
// Prepares a RX descriptor to be used by the chip
void
wb_free_rx_descriptor(wb_desc *descriptor)
{
descriptor->wb_status = WB_RXSTAT_OWN;
descriptor->wb_ctl = WB_MAX_FRAMELEN | WB_RXCTL_RLINK;
}
void
wb_enable_interrupts(struct wb_device *device)
{
write32(device->reg_base + WB_IMR, WB_INTRS);
write32(device->reg_base + WB_ISR, 0xFFFFFFFF);
}
void
wb_disable_interrupts(struct wb_device *device)
{
write32(device->reg_base + WB_IMR, 0L);
write32(device->reg_base + WB_ISR, 0L);
}
void
wb_reset(wb_device *device)
{
int i = 0;
cpu_status status;
LOG((DEVICE_NAME": reset()\n"));
status = disable_interrupts();
device->wb_txthresh = WB_TXTHRESH_INIT;
write32(device->reg_base + WB_NETCFG, 0L);
write32(device->reg_base + WB_BUSCTL, 0L);
write32(device->reg_base + WB_TXADDR, 0L);
write32(device->reg_base + WB_RXADDR, 0L);
WB_SETBIT(device->reg_base + WB_BUSCTL, WB_BUSCTL_RESET);
WB_SETBIT(device->reg_base + WB_BUSCTL, WB_BUSCTL_RESET);
for (i = 0; i < WB_TIMEOUT; i++) {
if (!(read32(device->reg_base + WB_BUSCTL) & WB_BUSCTL_RESET))
break;
}
if (i == WB_TIMEOUT)
LOG(("reset hasn't completed!!!"));
write32(device->reg_base + WB_BUSCTL, WB_BUSCTL_MUSTBEONE|WB_BUSCTL_ARBITRATION);
WB_SETBIT(device->reg_base + WB_BUSCTL, WB_BURSTLEN_16LONG);
switch(device->wb_cachesize) {
case 32:
WB_SETBIT(device->reg_base + WB_BUSCTL, WB_CACHEALIGN_32LONG);
break;
case 16:
WB_SETBIT(device->reg_base + WB_BUSCTL, WB_CACHEALIGN_16LONG);
break;
case 8:
WB_SETBIT(device->reg_base + WB_BUSCTL, WB_CACHEALIGN_8LONG);
break;
case 0:
default:
WB_SETBIT(device->reg_base + WB_BUSCTL, WB_CACHEALIGN_NONE);
break;
}
write32(device->reg_base + WB_BUSCTL_SKIPLEN, WB_SKIPLEN_4LONG);
/* Early TX interrupt; doesn't tend to work too well at 100Mbps */
WB_SETBIT(device->reg_base + WB_NETCFG, (WB_NETCFG_TX_EARLY_ON|WB_NETCFG_RX_EARLY_ON));
// fullduplex
WB_SETBIT(device->reg_base + WB_NETCFG, WB_NETCFG_FULLDUPLEX);
//100 MBits
WB_SETBIT(device->reg_base + WB_NETCFG, WB_NETCFG_100MBPS);
/* Program the multicast filter, if necessary */
//wb_setmulti(device);
//Disable promiscuos mode
WB_CLRBIT(device->reg_base + WB_NETCFG, WB_NETCFG_RX_ALLPHYS);
//Disable capture broadcast
WB_CLRBIT(device->reg_base + WB_NETCFG, WB_NETCFG_RX_BROAD);
restore_interrupts(status);
/* Wait a bit while the chip reorders his toughts */
snooze(1000);
//XXX Initialize MII interface
}
void
wb_updateLink(struct wb_device *device)
{
}
int32
wb_tick(timer *arg)
{
struct wb_device *device = (wb_device*)arg;
wb_updateLink(device);
return B_OK;
}
/***************** Interrupt handling ******************************/
status_t
wb_rxok(struct wb_device *device)
{
uint32 releaseRxSem = 0;
int16 limit;
LOG(("Rx interrupt\n"));
acquire_spinlock(&device->rxSpinlock);
for (limit = device->rxFree; limit > 0; limit--) {
if (device->rxDescriptor[device->rxInterruptIndex].wb_status & WB_RXSTAT_OWN) {
//LOG((DEVICE_NAME ": RX_STAT_OWN\n"));
break;
}
releaseRxSem++;
device->rxInterruptIndex = (device->rxInterruptIndex + 1) & WB_RX_CNT_MASK;
device->rxFree--;
}
//Re-enable receive queue
write32(device->reg_base + WB_RXSTART, 0xFFFFFFFF);
release_spinlock(&device->rxSpinlock);
//LOG((DEVICE_NAME": RxCurrent = %d\n", device->rxInterruptIndex));
if (releaseRxSem > 0) {
//dprintf("releasing read sem %d times\n", releaseRxSem);
release_sem_etc(device->rxSem, releaseRxSem, B_DO_NOT_RESCHEDULE);
return B_INVOKE_SCHEDULER;
}
return B_HANDLED_INTERRUPT;
}
status_t
wb_tx_nobuf(struct wb_device *info)
{
int16 releaseTxSem = 0;
int16 limit;
status_t status;
acquire_spinlock(&info->txSpinlock);
for (limit = info->txSent; limit > 0; limit--) {
status =info->txDescriptor[info->txInterruptIndex].wb_status;
if (status & WB_TXSTAT_TXERR) {
LOG(("TX_STAT_ERR\n"));
break;
} else if (status & WB_UNSENT) {
LOG(("TX_STAT_UNSENT\n"));
break;
} else {
//dprintf("releasing TX descriptor to the ring\n");
info->txDescriptor[info->txInterruptIndex].wb_status = 0;
}
releaseTxSem++; // this many buffers are free
info->txInterruptIndex = (info->txInterruptIndex + 1) & WB_TX_CNT_MASK;
info->txSent--;
if (info->txSent < 0 || info->txSent > WB_TX_LIST_CNT)
dprintf("ERROR interrupt: txSent = %d\n", info->txSent);
}
release_spinlock(&info->txSpinlock);
if (releaseTxSem) {
release_sem_etc(info->txSem, releaseTxSem, B_DO_NOT_RESCHEDULE);
return B_INVOKE_SCHEDULER;
}
return B_HANDLED_INTERRUPT;
}
int32
wb_interrupt(void *arg)
{
struct wb_device *device = (wb_device*)arg;
int32 retval = B_UNHANDLED_INTERRUPT;
int16 worklimit = 20;
uint32 status;
acquire_spinlock(&device->intLock);
wb_disable_interrupts(device);
while (worklimit-- > 0) {
status = read32(device->reg_base + WB_ISR);
/* Is our card the one which requested the interrupt ? */
if (!(status & WB_INTRS)) {
/* No, bail out. */
break;
}
// Clean all the interrupts
if (status)
write32(device->reg_base + WB_ISR, status);
LOG((DEVICE_NAME":*** Interrupt received ***\n"));
if (status & WB_ISR_RX_NOBUF) {
LOG((DEVICE_NAME ": WB_ISR_RX_NOBUF\n"));
//retval = wb_rx_nobuf(device);
continue;
}
if (status & WB_ISR_RX_ERR) {
LOG((DEVICE_NAME": WB_ISR_RX_ERR\n"));
continue;
}
if (status & WB_ISR_RX_OK) {
LOG((DEVICE_NAME": WB_ISR_RX_OK\n"));
retval = wb_rxok(device);
continue;
}
if (status & WB_ISR_RX_IDLE) {
LOG((DEVICE_NAME": WB_ISR_RX_IDLE\n"));
continue;
}
if (status & WB_ISR_TX_NOBUF) {
LOG((DEVICE_NAME ": WB_ISR_TX_NOBUF\n"));
retval = wb_tx_nobuf(device);
continue;
}
if (status & WB_ISR_TX_UNDERRUN) {
LOG((DEVICE_NAME ": WB_ISR_TX_UNDERRUN\n"));
// TODO: Jack up TX Threshold
continue;
}
if (status & WB_ISR_TX_IDLE) {
LOG((DEVICE_NAME ": WB_ISR_TX_IDLE\n"));
continue;
}
if (status & WB_ISR_TX_OK) {
LOG((DEVICE_NAME": WB_ISR_TX_OK\n"));
continue;
}
if (status & WB_ISR_BUS_ERR) {
LOG((DEVICE_NAME": BUS_ERROR: %x\n", (int)(status & WB_ISR_BUSERRTYPE) >> 4));
//wb_reset(device);
continue;
}
LOG((DEVICE_NAME ": unknown interrupt received\n"));
}
wb_enable_interrupts(device);
release_spinlock(&device->intLock);
return retval;
}
/*
* Print an ethernet address
*/
void
print_address(ether_address_t *addr)
{
int i;
char buf[3 * 6 + 1];
for (i = 0; i < 5; i++) {
sprintf(&buf[3*i], "%02x:", addr->ebyte[i]);
}
sprintf(&buf[3*5], "%02x", addr->ebyte[5]);
dprintf("%s\n", buf);
}
status_t
wb_create_semaphores(struct wb_device *device)
{
device->rxSem = create_sem(0, "wb840 receive");
if (device->rxSem < B_OK) {
LOG(("Couldn't create sem, sem_id %d\n", (int)device->rxSem));
return device->rxSem;
}
set_sem_owner(device->rxSem, B_SYSTEM_TEAM);
device->txSem = create_sem(WB_TX_LIST_CNT, "wb840 transmit");
if (device->txSem < B_OK) {
LOG(("Couldn't create sem, sem_id %d\n", (int)device->txSem));
delete_sem(device->rxSem);
return device->txSem;
}
set_sem_owner(device->txSem, B_SYSTEM_TEAM);
device->rxLock = 0;
device->txLock = 0;
return B_OK;
}
status_t
wb_create_rings(struct wb_device *device)
{
int i;
if ((device->rxArea = create_area("wb840 rx buffer", (void **)&device->rxBuffer[0],
B_ANY_KERNEL_ADDRESS, ROUND_TO_PAGE_SIZE(WB_BUFBYTES * WB_RX_LIST_CNT),
B_FULL_LOCK, B_READ_AREA | B_WRITE_AREA)) < B_OK)
return device->rxArea;
for (i = 1; i < WB_RX_LIST_CNT; i++)
device->rxBuffer[i] = (void *)(((uint32)device->rxBuffer[0]) + (i * WB_BUFBYTES));
for (i = 0; i < WB_RX_LIST_CNT; i++) {
wb_free_rx_descriptor(&device->rxDescriptor[i]);
device->rxDescriptor[i].wb_data = physicalAddress(device->rxBuffer[i], WB_BUFBYTES);
device->rxDescriptor[i].wb_next = physicalAddress(&device->rxDescriptor[(i + 1)
& WB_RX_CNT_MASK], sizeof(struct wb_desc));
}
device->rxFree = WB_RX_LIST_CNT;
device->rxDescriptor[WB_RX_LIST_CNT - 1].wb_ctl |= WB_RXCTL_RLAST;
if ((device->txArea = create_area("wb840 tx buffer", (void **)&device->txBuffer[0],
B_ANY_KERNEL_ADDRESS, ROUND_TO_PAGE_SIZE(WB_BUFBYTES * WB_TX_LIST_CNT),
B_FULL_LOCK, B_READ_AREA | B_WRITE_AREA)) < B_OK) {
delete_area(device->rxArea);
return device->txArea;
}
for (i = 1; i < WB_TX_LIST_CNT; i++)
device->txBuffer[i] = (void *)(((uint32)device->txBuffer[0]) + (i * WB_BUFBYTES));
for (i = 0; i < WB_TX_LIST_CNT; i++) {
device->txDescriptor[i].wb_status = 0;
device->txDescriptor[i].wb_ctl = WB_TXCTL_TLINK;
device->txDescriptor[i].wb_data = physicalAddress(device->txBuffer[i], WB_BUFBYTES);
device->txDescriptor[i].wb_next = physicalAddress(&device->txDescriptor[(i + 1)
& WB_TX_CNT_MASK], sizeof(struct wb_desc));
}
device->txDescriptor[WB_TX_LIST_CNT - 1].wb_ctl |= WB_TXCTL_TLAST;
/* Load the address of the RX list */
WB_CLRBIT(device->reg_base + WB_NETCFG, WB_NETCFG_RX_ON);
write32(device->reg_base + WB_RXADDR, physicalAddress(&device->rxDescriptor[0],
sizeof(struct wb_desc)));
/* Load the address of the TX list */
WB_CLRBIT(device->reg_base + WB_NETCFG, WB_NETCFG_TX_ON);
write32(device->reg_base + WB_TXADDR, physicalAddress(&device->txDescriptor[0],
sizeof(struct wb_desc)));
return B_OK;
}
void
wb_delete_rings(struct wb_device *device)
{
delete_area(device->rxArea);
delete_area(device->txArea);
}

View File

@ -0,0 +1,399 @@
/*
* Copyright (c) 1997, 1998
* Bill Paul <wpaul@ctr.columbia.edu>. 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.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Bill Paul.
* 4. Neither the name of the author nor the names of any co-contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD
* 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.
*
* This file has been adapted to BeOS by Stefano Ceccherini
*/
#ifndef __WB840_H
#define __WB840_H
#include <PCI.h>
#include <KernelExport.h>
#include "ether_driver.h"
/*
* Winbond register definitions.
*/
enum registers {
WB_BUSCTL = 0x00, /* bus control */
WB_TXSTART = 0x04, /* tx start demand */
WB_RXSTART = 0x08, /* rx start demand */
WB_RXADDR = 0x0C, /* rx descriptor list start addr */
WB_TXADDR = 0x10, /* tx descriptor list start addr */
WB_ISR = 0x14, /* interrupt status register */
WB_NETCFG = 0x18, /* network config register */
WB_IMR = 0x1C, /* interrupt mask */
WB_FRAMESDISCARDED = 0x20, /* # of discarded frames */
WB_SIO = 0x24, /* MII and ROM/EEPROM access */
WB_BOOTROMADDR = 0x28,
WB_TIMER = 0x2C, /* general timer */
WB_CURRXCTL = 0x30, /* current RX descriptor */
WB_CURRXBUF = 0x34, /* current RX buffer */
WB_MAR0 = 0x38, /* multicast filter 0 */
WB_MAR1 = 0x3C, /* multicast filter 1 */
WB_NODE0 = 0x40, /* physical address 0 */
WB_NODE1 = 0x44, /* physical address 1 */
WB_BOOTROMSIZE = 0x48, /* boot ROM size */
WB_CURTXCTL = 0x4C, /* current TX descriptor */
WB_CURTXBUF = 0x50, /* current TX buffer */
};
/*
* Bus control bits.
*/
enum busControlBits {
WB_BUSCTL_RESET = 0x00000001,
WB_BUSCTL_ARBITRATION = 0x00000002,
WB_BUSCTL_SKIPLEN = 0x0000007C,
WB_BUSCTL_BUF_BIGENDIAN = 0x00000080,
WB_BUSCTL_BURSTLEN = 0x00003F00,
WB_BUSCTL_CACHEALIGN = 0x0000C000,
WB_BUSCTL_DES_BIGENDIAN = 0x00100000,
WB_BUSCTL_WAIT = 0x00200000,
WB_BUSCTL_MUSTBEONE = 0x00400000,
WB_SKIPLEN_1LONG = 0x00000004,
WB_SKIPLEN_2LONG = 0x00000008,
WB_SKIPLEN_3LONG = 0x00000010,
WB_SKIPLEN_4LONG = 0x00000020,
WB_SKIPLEN_5LONG = 0x00000040,
WB_CACHEALIGN_NONE = 0x00000000,
WB_CACHEALIGN_8LONG = 0x00004000,
WB_CACHEALIGN_16LONG = 0x00008000,
WB_CACHEALIGN_32LONG = 0x0000C000,
WB_BURSTLEN_USECA = 0x00000000,
WB_BURSTLEN_1LONG = 0x00000100,
WB_BURSTLEN_2LONG = 0x00000200,
WB_BURSTLEN_4LONG = 0x00000400,
WB_BURSTLEN_8LONG = 0x00000800,
WB_BURSTLEN_16LONG = 0x00001000,
WB_BURSTLEN_32LONG = 0x00002000,
};
#define WB_BUSCTL_CONFIG (WB_CACHEALIGN_8LONG|WB_SKIPLEN_4LONG|WB_BURSTLEN_8LONG)
/*
* Interrupt status bits.
*/
enum InterruptStatusBits {
WB_ISR_TX_OK = 0x00000001,
WB_ISR_TX_IDLE = 0x00000002,
WB_ISR_TX_NOBUF = 0x00000004,
WB_ISR_RX_EARLY = 0x00000008,
WB_ISR_RX_ERR = 0x00000010,
WB_ISR_TX_UNDERRUN = 0x00000020,
WB_ISR_RX_OK = 0x00000040,
WB_ISR_RX_NOBUF = 0x00000080,
WB_ISR_RX_IDLE = 0x00000100,
WB_ISR_TX_EARLY = 0x00000400,
WB_ISR_TIMER_EXPIRED = 0x00000800,
WB_ISR_BUS_ERR = 0x00002000,
WB_ISR_ABNORMAL = 0x00008000,
WB_ISR_NORMAL = 0x00010000,
WB_ISR_RX_STATE = 0x000E0000,
WB_ISR_TX_STATE = 0x00700000,
WB_ISR_BUSERRTYPE = 0x03800000,
};
/*
* The RX_STATE and TX_STATE fields are not described anywhere in the
* Winbond datasheet, however it appears that the Winbond chip is an
* attempt at a DEC 'tulip' clone, hence the ISR register is identical
* to that of the tulip chip and we can steal the bit definitions from
* the tulip documentation.
*/
enum rxState {
WB_RXSTATE_STOPPED = 0x00000000, /* 000 - Stopped */
WB_RXSTATE_FETCH = 0x00020000, /* 001 - Fetching descriptor */
WB_RXSTATE_ENDCHECK = 0x00040000, /* 010 - check for rx end */
WB_RXSTATE_WAIT = 0x00060000, /* 011 - waiting for packet */
WB_RXSTATE_SUSPEND = 0x00080000, /* 100 - suspend rx */
WB_RXSTATE_CLOSE = 0x000A0000, /* 101 - close tx desc */
WB_RXSTATE_FLUSH = 0x000C0000, /* 110 - flush from FIFO */
WB_RXSTATE_DEQUEUE = 0x000E0000, /* 111 - dequeue from FIFO */
};
enum txState {
WB_TXSTATE_RESET = 0x00000000, /* 000 - reset */
WB_TXSTATE_FETCH = 0x00100000, /* 001 - fetching descriptor */
WB_TXSTATE_WAITEND = 0x00200000, /* 010 - wait for tx end */
WB_TXSTATE_READING = 0x00300000, /* 011 - read and enqueue */
WB_TXSTATE_RSVD = 0x00400000, /* 100 - reserved */
WB_TXSTATE_SETUP = 0x00500000, /* 101 - setup packet */
WB_TXSTATE_SUSPEND = 0x00600000, /* 110 - suspend tx */
WB_TXSTATE_CLOSE = 0x00700000, /* 111 - close tx desc */
};
/*
* Network config bits.
*/
enum networkConfigBits {
WB_NETCFG_RX_ON = 0x00000002,
WB_NETCFG_RX_ALLPHYS = 0x00000008,
WB_NETCFG_RX_MULTI = 0x00000010,
WB_NETCFG_RX_BROAD = 0x00000020,
WB_NETCFG_RX_RUNT = 0x00000040,
WB_NETCFG_RX_ERR = 0x00000080,
WB_NETCFG_FULLDUPLEX = 0x00000200,
WB_NETCFG_LOOPBACK = 0x00000C00,
WB_NETCFG_TX_ON = 0x00002000,
WB_NETCFG_TX_THRESH = 0x001FC000,
WB_NETCFG_RX_EARLYTHRSH = 0x1FE00000,
WB_NETCFG_100MBPS = 0x20000000,
WB_NETCFG_TX_EARLY_ON = 0x40000000,
WB_NETCFG_RX_EARLY_ON = 0x80000000,
};
/*
* The tx threshold can be adjusted in increments of 32 bytes.
*/
#define WB_TXTHRESH(x) ((x >> 5) << 14)
#define WB_TXTHRESH_CHUNK 32
#define WB_TXTHRESH_INIT 0 /*72*/
/*
* Interrupt mask bits.
*/
enum interruptMaskBits {
WB_IMR_TX_OK = 0x00000001,
WB_IMR_TX_IDLE = 0x00000002,
WB_IMR_TX_NOBUF = 0x00000004,
WB_IMR_TX_UNDERRUN = 0x00000020,
WB_IMR_TX_EARLY = 0x00000400,
WB_IMR_RX_EARLY = 0x00000008,
WB_IMR_RX_ERR = 0x00000010,
WB_IMR_RX_OK = 0x00000040,
WB_IMR_RX_NOBUF = 0x00000080,
WB_IMR_RX_IDLE = 0x00000100,
WB_IMR_TIMER_EXPIRED = 0x00000800,
WB_IMR_BUS_ERR = 0x00002000,
WB_IMR_ABNORMAL = 0x00008000,
WB_IMR_NORMAL = 0x00010000,
};
#define WB_INTRS \
(WB_IMR_RX_OK|WB_IMR_RX_IDLE|WB_IMR_RX_ERR|WB_IMR_TX_OK| \
WB_IMR_RX_NOBUF|WB_IMR_RX_ERR|WB_IMR_RX_EARLY| \
WB_IMR_TX_NOBUF|WB_IMR_TX_UNDERRUN|WB_IMR_BUS_ERR| \
WB_IMR_ABNORMAL|WB_IMR_NORMAL|WB_IMR_TX_EARLY)
/*#define WB_INTRS \
(WB_IMR_RX_OK|WB_IMR_TX_OK|WB_IMR_RX_ERR| \
WB_IMR_TX_UNDERRUN|WB_IMR_BUS_ERR| \
WB_IMR_ABNORMAL|WB_IMR_NORMAL|WB_IMR_TX_EARLY)
*/
/*
* Serial I/O (EEPROM/ROM) bits.
*/
enum EEpromBits {
WB_SIO_EE_CS = 0x00000001, /* EEPROM chip select */
WB_SIO_EE_CLK = 0x00000002, /* EEPROM clock */
WB_SIO_EE_DATAIN = 0x00000004, /* EEPROM data output */
WB_SIO_EE_DATAOUT = 0x00000008, /* EEPROM data input */
WB_SIO_ROMDATA4 = 0x00000010,
WB_SIO_ROMDATA5 = 0x00000020,
WB_SIO_ROMDATA6 = 0x00000040,
WB_SIO_ROMDATA7 = 0x00000080,
WB_SIO_ROMCTL_WRITE = 0x00000200,
WB_SIO_ROMCTL_READ = 0x00000400,
WB_SIO_EESEL = 0x00000800,
WB_SIO_MII_CLK = 0x00010000, /* MDIO clock */
WB_SIO_MII_DATAIN = 0x00020000, /* MDIO data out */
WB_SIO_MII_DIR = 0x00040000, /* MDIO dir */
WB_SIO_MII_DATAOUT = 0x00080000, /* MDIO data in */
};
enum EEpromCmd {
WB_EECMD_WRITE = 0x140,
WB_EECMD_READ = 0x180,
WB_EECMD_ERASE = 0x1c0
};
/*
* Winbond TX/RX descriptor structure.
*/
typedef struct wb_desc wb_desc;
struct wb_desc {
uint32 wb_status;
uint32 wb_ctl;
uint32 wb_data;
uint32 wb_next;
};
#define WB_RXSTAT_CRCERR 0x00000002
#define WB_RXSTAT_DRIBBLE 0x00000004
#define WB_RXSTAT_MIIERR 0x00000008
#define WB_RXSTAT_LATEEVENT 0x00000040
#define WB_RXSTAT_GIANT 0x00000080
#define WB_RXSTAT_LASTFRAG 0x00000100
#define WB_RXSTAT_FIRSTFRAG 0x00000200
#define WB_RXSTAT_MULTICAST 0x00000400
#define WB_RXSTAT_RUNT 0x00000800
#define WB_RXSTAT_RXTYPE 0x00003000
#define WB_RXSTAT_RXERR 0x00008000
#define WB_RXSTAT_RXLEN 0x3FFF0000
#define WB_RXSTAT_RXCMP 0x40000000
#define WB_RXSTAT_OWN 0x80000000
#define WB_RXBYTES(x) ((x & WB_RXSTAT_RXLEN) >> 16)
#define WB_RXSTAT (WB_RXSTAT_FIRSTFRAG|WB_RXSTAT_LASTFRAG|WB_RXSTAT_OWN)
#define WB_RXCTL_BUFLEN1 0x00000FFF
#define WB_RXCTL_BUFLEN2 0x00FFF000
#define WB_RXCTL_RLINK 0x01000000
#define WB_RXCTL_RLAST 0x02000000
#define WB_TXSTAT_DEFER 0x00000001
#define WB_TXSTAT_UNDERRUN 0x00000002
#define WB_TXSTAT_COLLCNT 0x00000078
#define WB_TXSTAT_SQE 0x00000080
#define WB_TXSTAT_ABORT 0x00000100
#define WB_TXSTAT_LATECOLL 0x00000200
#define WB_TXSTAT_NOCARRIER 0x00000400
#define WB_TXSTAT_CARRLOST 0x00000800
#define WB_TXSTAT_TXERR 0x00001000
#define WB_TXSTAT_OWN 0x80000000
#define WB_TXCTL_BUFLEN1 0x000007FF
#define WB_TXCTL_BUFLEN2 0x003FF800
#define WB_TXCTL_PAD 0x00800000
#define WB_TXCTL_TLINK 0x01000000
#define WB_TXCTL_TLAST 0x02000000
#define WB_TXCTL_NOCRC 0x08000000
#define WB_TXCTL_FIRSTFRAG 0x20000000
#define WB_TXCTL_LASTFRAG 0x40000000
#define WB_TXCTL_FINT 0x80000000
#define WB_MAXFRAGS 16
#define WB_RX_LIST_CNT 64
#define WB_TX_LIST_CNT 64
#define WB_RX_CNT_MASK (WB_RX_LIST_CNT - 1)
#define WB_TX_CNT_MASK (WB_TX_LIST_CNT - 1)
#define WB_MIN_FRAMELEN 60
#define WB_MAX_FRAMELEN 1536
#define WB_UNSENT 0x1234
#define WB_BUFBYTES (1024 * sizeof(uint32))
/* Ethernet defines */
#define CRC_SIZE 4
#define ETHER_TRANSMIT_TIMEOUT ((bigtime_t)5000000) /* five seconds */
typedef struct wb_mii_frame wb_mii_frame;
struct wb_mii_frame {
uint8 mii_stdelim;
uint8 mii_opcode;
uint8 mii_phyaddr;
uint8 mii_regaddr;
uint8 mii_turnaround;
uint16 mii_data;
};
/*
* MII constants
*/
#define WB_MII_STARTDELIM 0x01
#define WB_MII_READOP 0x02
#define WB_MII_WRITEOP 0x01
#define WB_MII_TURNAROUND 0x02
typedef struct wb_device wb_device;
struct wb_device {
int32 devId;
pci_info* pciInfo;
uint16 irq; /* IRQ line */
volatile uint32 reg_base; /* hardware register base address */
timer timer;
// rx data
volatile wb_desc rxDescriptor[WB_RX_LIST_CNT];
volatile void *rxBuffer[WB_RX_LIST_CNT];
int32 rxLock;
sem_id rxSem;
spinlock rxSpinlock;
area_id rxArea;
int16 rxCurrent;
int16 rxInterruptIndex;
int16 rxFree;
//tx data
volatile wb_desc txDescriptor[WB_TX_LIST_CNT];
volatile char *txBuffer[WB_TX_LIST_CNT];
int32 txLock;
sem_id txSem;
spinlock txSpinlock;
area_id txArea;
int16 txCurrent;
int16 txInterruptIndex;
int16 txSent;
volatile int32 blockFlag;
ether_address_t myaddr; /* my ethernet address */
//volatile int interrupted; /* interrupted system call */
spinlock intLock;
const char* deviceName;
uint8 wb_type;
uint16 wb_txthresh;
int wb_cachesize;
};
#define WB_TIMEOUT 1000
/*
* Vendor and Card IDs
*
* Winbond
*/
#define WB_VENDORID 0x1050
#define WB_DEVICEID_840F 0x0840
/*
* Compex
*/
#define CP_VENDORID 0x11F6
#define CP_DEVICEID_RL100 0x2011
#define WB_SETBIT(reg, x) write32(reg, read32(reg) | x)
#define WB_CLRBIT(reg, x) write32(reg, read32(reg) & ~x)
// Prototypes
extern int32 wb_interrupt(void *arg);
extern status_t wb_create_semaphores(struct wb_device *device);
extern status_t wb_create_rings(struct wb_device *device);
extern void wb_delete_rings(wb_device *device);
extern void wb_reset(wb_device *device);
extern void wb_disable_interrupts(wb_device *device);
extern void wb_enable_interrupts(wb_device *device);
extern int32 wb_tick(timer *arg);
extern void wb_free_rx_descriptor(wb_desc *desc);
#endif //__WB840_H