diff --git a/src/add-ons/kernel/drivers/network/wb840/debug.h b/src/add-ons/kernel/drivers/network/wb840/debug.h new file mode 100644 index 0000000000..ce66d80ff3 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/wb840/debug.h @@ -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 diff --git a/src/add-ons/kernel/drivers/network/wb840/device.c b/src/add-ons/kernel/drivers/network/wb840/device.c new file mode 100644 index 0000000000..8e0b8f62e1 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/wb840/device.c @@ -0,0 +1,344 @@ +#include +#include +#include +#include +#include + +#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 +}; diff --git a/src/add-ons/kernel/drivers/network/wb840/device.h b/src/add-ons/kernel/drivers/network/wb840/device.h new file mode 100644 index 0000000000..83ee786002 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/wb840/device.h @@ -0,0 +1,28 @@ +#ifndef __WB_DRIVER_H +#define __WB_DRIVER_H + +// PCI Communications +#include + +#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 diff --git a/src/add-ons/kernel/drivers/network/wb840/driver.c b/src/add-ons/kernel/drivers/network/wb840/driver.c new file mode 100644 index 0000000000..d552c4e55d --- /dev/null +++ b/src/add-ons/kernel/drivers/network/wb840/driver.c @@ -0,0 +1,134 @@ +/* Copyright (c) 2003-2004 + * Stefano Ceccherini . All rights reserved. + */ +#include "debug.h" +#include + +#include +#include +#include +#include +#include + +#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 +} diff --git a/src/add-ons/kernel/drivers/network/wb840/driver.h b/src/add-ons/kernel/drivers/network/wb840/driver.h new file mode 100644 index 0000000000..88bb23b397 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/wb840/driver.h @@ -0,0 +1,17 @@ +#ifndef __DRIVER_H +#define __DRIVER_H + +#include +#include +#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 diff --git a/src/add-ons/kernel/drivers/network/wb840/interface.c b/src/add-ons/kernel/drivers/network/wb840/interface.c new file mode 100644 index 0000000000..14d26d893b --- /dev/null +++ b/src/add-ons/kernel/drivers/network/wb840/interface.c @@ -0,0 +1,371 @@ +/* + * Copyright (c) 1997, 1998 + * Bill Paul . 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 */ + + +#include "wb840.h" +#include "device.h" +#include "interface.h" + +#include +#include + +#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; + } +} diff --git a/src/add-ons/kernel/drivers/network/wb840/interface.h b/src/add-ons/kernel/drivers/network/wb840/interface.h new file mode 100644 index 0000000000..e8c93298f0 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/wb840/interface.h @@ -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 diff --git a/src/add-ons/kernel/drivers/network/wb840/wb840.c b/src/add-ons/kernel/drivers/network/wb840/wb840.c new file mode 100644 index 0000000000..2e2b76cc8d --- /dev/null +++ b/src/add-ons/kernel/drivers/network/wb840/wb840.c @@ -0,0 +1,446 @@ +/* Copyright (c) 2003-2004 + * Stefano Ceccherini . All rights reserved. + */ + +#include "device.h" +#include "driver.h" +#include "debug.h" +#include "ether_driver.h" +#include "interface.h" +#include "wb840.h" + +#include +#include + +#include +#include +#include + +#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); +} diff --git a/src/add-ons/kernel/drivers/network/wb840/wb840.h b/src/add-ons/kernel/drivers/network/wb840/wb840.h new file mode 100644 index 0000000000..8dc0c32067 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/wb840/wb840.h @@ -0,0 +1,399 @@ +/* + * Copyright (c) 1997, 1998 + * Bill Paul . 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 +#include +#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