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:
parent
92fd335690
commit
d8ee7d30df
12
src/add-ons/kernel/drivers/network/wb840/debug.h
Normal file
12
src/add-ons/kernel/drivers/network/wb840/debug.h
Normal 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
|
344
src/add-ons/kernel/drivers/network/wb840/device.c
Normal file
344
src/add-ons/kernel/drivers/network/wb840/device.c
Normal 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
|
||||
};
|
28
src/add-ons/kernel/drivers/network/wb840/device.h
Normal file
28
src/add-ons/kernel/drivers/network/wb840/device.h
Normal 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
|
134
src/add-ons/kernel/drivers/network/wb840/driver.c
Normal file
134
src/add-ons/kernel/drivers/network/wb840/driver.c
Normal 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
|
||||
}
|
17
src/add-ons/kernel/drivers/network/wb840/driver.h
Normal file
17
src/add-ons/kernel/drivers/network/wb840/driver.h
Normal 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
|
371
src/add-ons/kernel/drivers/network/wb840/interface.c
Normal file
371
src/add-ons/kernel/drivers/network/wb840/interface.c
Normal 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;
|
||||
}
|
||||
}
|
9
src/add-ons/kernel/drivers/network/wb840/interface.h
Normal file
9
src/add-ons/kernel/drivers/network/wb840/interface.h
Normal 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
|
446
src/add-ons/kernel/drivers/network/wb840/wb840.c
Normal file
446
src/add-ons/kernel/drivers/network/wb840/wb840.c
Normal 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);
|
||||
}
|
399
src/add-ons/kernel/drivers/network/wb840/wb840.h
Normal file
399
src/add-ons/kernel/drivers/network/wb840/wb840.h
Normal 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
|
Loading…
Reference in New Issue
Block a user