diff --git a/src/add-ons/kernel/drivers/network/sis900/device.c b/src/add-ons/kernel/drivers/network/sis900/device.c new file mode 100644 index 0000000000..faa19afb01 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/sis900/device.c @@ -0,0 +1,549 @@ +/* device.c - device hooks for SiS900 networking +** +** Copyright © 2001-2003 pinc Software. All Rights Reserved. +*/ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "ether_driver.h" +#include "driver.h" +#include "device.h" +#include "sis900.h" + + +/* device hooks prototypes */ + +status_t device_open(const char *, uint32, void **); +status_t device_close(void *); +status_t device_free(void *); +status_t device_ioctl(void *, uint32, void *, size_t); +status_t device_read(void *, off_t, void *, size_t *); +status_t device_write(void *, off_t, const void *, size_t *); + + +int32 gDeviceOpenMask = 0; + +device_hooks gDeviceHooks = { + device_open, + device_close, + device_free, + device_ioctl, + device_read, + device_write, + NULL, + NULL, + NULL, + NULL +}; + +#include + +//#define EXCESSIVE_DEBUG +//#define THE_BUSY_WAITING_WAY + +#ifdef EXCESSIVE_DEBUG +//# include + +sem_id gIOLock; + +int +bug(const char *format, ...) +{ + va_list vl; + char c[2048]; + int i; + int file; + + if ((file = open("/boot/home/sis900-driver.log", O_RDWR | O_APPEND | O_CREAT)) >= 0) { +// time_t timer = time(NULL); +// strftime(c, 2048, "%H:%M:S: ", localtime(&timer)); + + va_start(vl, format); + i = vsprintf(c, format, vl); + va_end(vl); + + write(file, c, strlen(c)); + close(file); + } + + return i; +} + +#define DUMPED_BLOCK_SIZE 16 + +void +dumpBlock(const char *buffer, int size, const char *prefix) +{ + int i; + + for (i = 0; i < size;) + { + int start = i; + + bug(prefix); + for (; i < start+DUMPED_BLOCK_SIZE; i++) + { + if (!(i % 4)) + bug(" "); + + if (i >= size) + bug(" "); + else + bug("%02x", *(unsigned char *)(buffer + i)); + } + bug(" "); + + for (i = start; i < start + DUMPED_BLOCK_SIZE; i++) + { + if (i < size) + { + char c = buffer[i]; + + if (c < 30) + bug("."); + else + bug("%c", c); + } + else + break; + } + bug("\n"); + } +} + +#endif /* EXCESSIVE_DEBUG */ +// #pragma mark - + + +status_t +checkDeviceInfo(struct sis_info *info) +{ + if (!info || info->cookieMagic != SiS_COOKIE_MAGIC) + return EINVAL; + + return B_OK; +} + + +void +deleteSemaphores(struct sis_info *info) +{ +} + + +status_t +createSemaphores(struct sis_info *info) +{ + if ((info->rxSem = create_sem(0, "sis900 receive")) < B_OK) + return info->rxSem; + set_sem_owner(info->rxSem, B_SYSTEM_TEAM); + + if ((info->txSem = create_sem(NUM_Tx_DESCR, "sis900 transmit")) < B_OK) + { + delete_sem(info->rxSem); + return info->txSem; + } + set_sem_owner(info->txSem, B_SYSTEM_TEAM); + +#ifdef EXCESSIVE_DEBUG + if ((gIOLock = create_sem(1, "sis900 debug i/o lock")) < B_OK) + return gIOLock; + set_sem_owner(gIOLock, B_SYSTEM_TEAM); +#endif + + info->rxLock = info->txLock = 0; + + return B_OK; +} + + +//-------------------------------------------------------------------------- +// #pragma mark - +// the device will be accessed through the following functions (a.k.a. device hooks) + + +status_t +device_open(const char *name, uint32 flags, void **cookie) +{ + struct sis_info *info; + area_id area; + int id; + + // verify device access + { + char *thisName; + int32 mask; + + // search for device name + for (id = 0; (thisName = gDeviceNames[id]) != NULL; id++) { + if (!strcmp(name, thisName)) + break; + } + if (!thisName) + return EINVAL; + + // check if device is already open + mask = 1L << id; + if (atomic_or(&gDeviceOpenMask, mask) & mask) + return B_BUSY; + } + + // allocate internal device structure + if ((area = create_area(DEVICE_NAME " private data", cookie, + B_ANY_KERNEL_ADDRESS, + ROUND_TO_PAGE_SIZE(sizeof(struct sis_info)), + B_FULL_LOCK, B_READ_AREA | B_WRITE_AREA)) < B_OK) { + gDeviceOpenMask &= ~(1L << id); + return B_NO_MEMORY; + } + info = (struct sis_info *)*cookie; + memset(info, 0, sizeof(struct sis_info)); + +#ifdef DEBUG + load_driver_symbols("sis900"); +#endif + + info->cookieMagic = SiS_COOKIE_MAGIC; + info->thisArea = area; + info->id = id; + info->pciInfo = pciInfo[id]; + info->registers = (char *)pciInfo[id]->u.h0.base_registers[0]; + + if (sis900_getMACAddress(info)) + dprintf(DEVICE_NAME ": MAC address %02x:%02x:%02x:%02x:%02x:%02x\n", + info->address.ebyte[0], info->address.ebyte[1], info->address.ebyte[2], + info->address.ebyte[3], info->address.ebyte[4], info->address.ebyte[5]); + else + dprintf(DEVICE_NAME ": could not get MAC address\n"); + + if (createSemaphores(info) == B_OK) { + status_t status = sis900_initPHYs(info); + if (status == B_OK) { + TRACE((DEVICE_NAME ": MII status = %d\n", mdio_read(info, MII_STATUS))); + + //sis900_configFIFOs(info); + sis900_reset(info); + + // install & enable interrupts + install_io_interrupt_handler(info->pciInfo->u.h0.interrupt_line, + sis900_interrupt, info, 0); + sis900_enableInterrupts(info); + + sis900_setRxFilter(info); + sis900_createRings(info); + + // enable receiver's state machine + write32((uint32)info->registers + SiS900_MAC_COMMAND, SiS900_MAC_CMD_Rx_ENABLE); + + // check link mode & add timer + sis900_checkMode(info); + add_timer(&info->timer, sis900_timer, 1000000LL, B_PERIODIC_TIMER); + + return B_OK; + } + dprintf(DEVICE_NAME ": could not initialize MII PHY: %s\n", strerror(status)); + deleteSemaphores(info); + } else + dprintf(DEVICE_NAME ": could not create semaphores.\n"); + + gDeviceOpenMask &= ~(1L << id); + delete_area(area); + + return B_ERROR; +} + + +status_t +device_close(void *data) +{ + struct sis_info *info; + + if (checkDeviceInfo(info = data) != B_OK) + return EINVAL; + + info->cookieMagic = SiS_FREE_COOKIE_MAGIC; + + // cancel timer + cancel_timer(&info->timer); + + // disable the transmitter's and receiver's state machine + write32((uint32)info->registers + SiS900_MAC_COMMAND, + SiS900_MAC_CMD_Rx_DISABLE | SiS900_MAC_CMD_Tx_DISABLE); + + // remove & disable interrupt + sis900_disableInterrupts(info); + remove_io_interrupt_handler(info->pciInfo->u.h0.interrupt_line, sis900_interrupt, info); + + delete_sem(info->rxSem); + delete_sem(info->txSem); + +#ifdef EXCESSIVE_DEBUG + delete_sem(gIOLock); +#endif + + return B_OK; +} + + +status_t +device_free(void *data) +{ + struct sis_info *info = data; + status_t retval = B_NO_ERROR; + + if (info == NULL || info->cookieMagic != SiS_FREE_COOKIE_MAGIC) + retval = EINVAL; + + gDeviceOpenMask &= ~(1L << info->id); + + sis900_deleteRings(info); + delete_area(info->thisArea); + + return retval; +} + + +status_t +device_ioctl(void *data, uint32 msg, void *buffer, size_t bufferLength) +{ + struct sis_info *info; + + if (checkDeviceInfo(info = data) != B_OK) + return EINVAL; + + switch (msg) { + case ETHER_GETADDR: + TRACE(("ioctl: get MAC address\n")); + memcpy(buffer, &info->address, 6); + return B_NO_ERROR; + + case ETHER_INIT: + TRACE(("ioctl: init\n")); + return B_NO_ERROR; + + case ETHER_GETFRAMESIZE: + TRACE(("ioctl: get frame size\n")); + *(uint32*)buffer = MAX_FRAME_SIZE; + return B_NO_ERROR; + + case ETHER_SETPROMISC: + TRACE(("ioctl: set promisc\n")); + sis900_setPromiscuous(info, *(uint32 *)buffer != 0); + return B_OK; + + case ETHER_NONBLOCK: + TRACE(("ioctl: non blocking ? %s\n", info->blockFlag ? "yes" : "no")); + info->blockFlag = *(int32 *)buffer ? B_TIMEOUT : 0; + return B_NO_ERROR; + + case ETHER_ADDMULTI: + TRACE(("ioctl: add multicast\n")); + /* not yet implemented */ + break; + + default: + TRACE(("ioctl: unknown message %d (length = %ld)\n", msg, bufferLength)); + break; + } + return B_ERROR; +} + + +#ifdef DEBUG +// sis900.c +extern int32 intrCounter; +extern int32 lastIntr[100]; +uint32 counter = 0; +#endif + + +status_t +device_read(void *data, off_t pos, void *buffer, size_t *_length) +{ + struct sis_info *info; + status_t status; + size_t size; + int32 blockFlag; + thread_id threadID = find_thread(NULL); + int32 rxp, rxd; + uint32 check; + int16 current; + + if (checkDeviceInfo(info = data) != B_OK) { + *_length = 0; + return EINVAL; + } + + //dprintf("read: rx: isr = %d, free = %d, current = %d\n", + // info->rxInterruptIndex, info->rxFree, info->rxCurrent); + + blockFlag = info->blockFlag; + + // read is not reentrant + if (atomic_or(&info->rxLock, 1)) { + *_length = 0; + return B_ERROR; + } + + //TRACE(("current rx descr: %08x (last = %ld)\n", rxp = read32((uint32)info->registers + SiS900_MAC_Rx_DESCR),(info->rxLast+1) % NUM_Rx_DESCR)); + + // block until data is available (if blocking is allowed) + if ((status = acquire_sem_etc(info->rxSem, 1, B_CAN_INTERRUPT | blockFlag, 0)) != B_NO_ERROR) { + TRACE(("cannot acquire read sem: %x, %s\n", status, strerror(status))); + atomic_and(&info->rxLock, 0); + *_length = 0; + return status; + } + + /* three cases, frame is good, bad, or we don't own the descriptor */ + current = info->rxCurrent; + check = info->rxDescriptor[current].status; + + if (!(check & SiS900_DESCR_OWN)) { // the buffer is still in use! + TRACE(("ERROR: read: buffer %d still in use: %x\n", current, status)); + atomic_and(&info->rxLock, 0); + *_length = 0; + return; + } + + if (check & (SiS900_DESCR_Rx_ABORT | SiS900_DESCR_Rx_OVERRUN | + SiS900_DESCR_Rx_LONG_PACKET | SiS900_DESCR_Rx_RUNT_PACKET | + SiS900_DESCR_Rx_INVALID_SYM | SiS900_DESCR_Rx_FRAME_ALIGN | + SiS900_DESCR_Rx_CRC_ERROR)) { + TRACE(("ERROR read: packet with errors: %ld\n", check)); + *_length = 0; + } else { + // copy buffer + size = (check & SiS900_DESCR_SIZE_MASK) - CRC_SIZE; + if (size > MAX_FRAME_SIZE || size > *_length) { + TRACE(("ERROR read: bad frame size %d\n", size)); + size = *_length; + } + memcpy(buffer, (void *)info->rxBuffer[current], size); + } + info->rxCurrent = (current + 1) & NUM_Rx_MASK; + + /* update indexes and buffer ownership */ + { + cpu_status former; + former = disable_interrupts(); + acquire_spinlock(&info->rxSpinlock); + + // release buffer to ring + info->rxDescriptor[current].status = MAX_FRAME_SIZE + CRC_SIZE; + info->rxFree++; + + release_spinlock(&info->rxSpinlock); + restore_interrupts(former); + } + + atomic_and(&info->rxLock, 0); + return B_OK; +} + + +status_t +device_write(void *data, off_t pos, const void *buffer, size_t *_length) +{ + struct sis_info *info; + status_t status; + uint16 frameSize; + int16 current; + thread_id threadID = find_thread(NULL); + uint32 check; + + if (checkDeviceInfo(info = data) != B_OK) + return EINVAL; + + //TRACE(("****\t%5ld: write... %lx, %ld (%d) thread: %ld, counter = %ld\n", counter++, buf, *len, info->txLock, threadID, intrCounter)); + atomic_add(&info->txLock, 1); + + if (*_length > MAX_FRAME_SIZE) + *_length = MAX_FRAME_SIZE; + + frameSize = *_length; + current = info->txCurrent; + + //dprintf("\t%5ld: \twrite: tx: isr = %d, sent = %d, current = %d\n",counter++, + // info->txInterruptIndex,info->txSent,info->txCurrent); + + // block until a free tx descriptor is available + if ((status = acquire_sem_etc(info->txSem, 1, B_TIMEOUT, ETHER_TRANSMIT_TIMEOUT)) < B_NO_ERROR) { + write32((uint32)info->registers + SiS900_MAC_COMMAND, SiS900_MAC_CMD_Tx_ENABLE); + TRACE(("write: acquiring sem failed: %x, %s\n", status, strerror(status))); + atomic_add(&info->txLock, -1); + *_length = 0; + return status; + } + + check = info->txDescriptor[current].status; + if (check & SiS900_DESCR_OWN) { + // descriptor is still in use + dprintf(DEVICE_NAME ": card owns buffer %d\n", current); + atomic_add(&info->txLock, -1); + *_length = 0; + return B_ERROR; + } + + /* Copy data to tx buffer */ + memcpy((void *)info->txBuffer[current], buffer, frameSize); + info->txCurrent = (current + 1) & NUM_Tx_MASK; + + { + cpu_status former; + former = disable_interrupts(); + acquire_spinlock(&info->txSpinlock); + + info->txDescriptor[current].status = SiS900_DESCR_OWN | frameSize; + info->txSent++; + +#ifdef DEBUG + { + struct buffer_desc *b = (void *)read32((uint32)info->registers + SiS900_MAC_Tx_DESCR); + int16 that; + + dprintf("\twrite: status %d = %lx, sent = %d\n", current, info->txDescriptor[current].status,info->txSent); + dprintf("write: %d: mem = %lx : hardware = %lx\n", current, physicalAddress(&info->txDescriptor[current],sizeof(struct buffer_desc)),read32((uint32)info->registers + SiS900_MAC_Tx_DESCR)); + + for (that = 0;that < NUM_Tx_DESCR && (void *)physicalAddress(&info->txDescriptor[that],sizeof(struct buffer_desc)) != b;that++); + + if (that == NUM_Tx_DESCR) + { + //dprintf("not in ring!\n"); + that = 0; + } + dprintf("(hardware status %d = %lx)!\n", that, info->txDescriptor[that].status); + } +#endif + release_spinlock(&info->txSpinlock); + restore_interrupts(former); + } + + // enable transmit state machine + write32((uint32)info->registers + SiS900_MAC_COMMAND, SiS900_MAC_CMD_Tx_ENABLE); + +#ifdef EXCESSIVE_DEBUG + acquire_sem(gIOLock); + bug("\t\twrite last interrupts:\n"); + { + int ii; + for (ii = (intrCounter-2) % 100; ii < intrCounter; ii = (ii + 1) % 100) + bug("\t\t\t%ld: %08lx\n", ii, lastIntr[ii % 100]); + } + bug("\t\twrite block (%ld bytes) thread = %ld:\n", frameSize, threadID); + dumpBlock(buf,frameSize, "\t\t\t"); + release_sem(gIOLock); +#endif + + atomic_add(&info->txLock, -1); + return B_OK; +} + diff --git a/src/add-ons/kernel/drivers/network/sis900/device.h b/src/add-ons/kernel/drivers/network/sis900/device.h new file mode 100644 index 0000000000..a025aa4c33 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/sis900/device.h @@ -0,0 +1,11 @@ +#ifndef DEVICE_H +#define DEVICE_H + +#include + +#define ETHER_TRANSMIT_TIMEOUT ((bigtime_t)5000000) /* five seconds */ + +/* The published PCI bus interface device hooks */ +extern device_hooks gDeviceHooks; + +#endif /* DEVICE_H */ diff --git a/src/add-ons/kernel/drivers/network/sis900/driver.c b/src/add-ons/kernel/drivers/network/sis900/driver.c new file mode 100644 index 0000000000..5ba6d2baa2 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/sis900/driver.c @@ -0,0 +1,152 @@ +/* driver - kernel driver for SiS900 networking +** +** Copyright © 2001-2003 pinc Software. All Rights Reserved. +*/ + + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "ether_driver.h" +#include "driver.h" +#include "device.h" +#include "sis900.h" + +#define MAX_CARDS 4 + +int32 api_version = B_CUR_DRIVER_API_VERSION; + +char *gDeviceNames[MAX_CARDS + 1]; +pci_info *pciInfo[MAX_CARDS]; +pci_module_info *pci; + +extern status_t init_hardware(void); +extern status_t init_driver(void); +extern void uninit_driver(void); +extern const char **publish_devices(void); +extern device_hooks *find_device(const char *name); + + +const char ** +publish_devices(void) +{ + TRACE((DEVICE_NAME ": publish_devices()\n")); + return (const char **)gDeviceNames; +} + + +status_t +init_hardware(void) +{ + TRACE((DEVICE_NAME ": init_hardware()\n")); + return B_NO_ERROR; +} + + +status_t +init_driver(void) +{ + status_t status; + pci_info *info; + int i, found; + + TRACE((DEVICE_NAME ": init_driver()\n")); + + if ((status = get_module(B_PCI_MODULE_NAME,(module_info **)&pci)) != B_OK) + { + TRACE((DEVICE_NAME ": pci module unavailable\n")); + return status; + } + + // find devices + info = malloc(sizeof(pci_info)); + for (i = found = 0; (status = pci->get_nth_pci_info(i, info)) == B_OK; i++) { + if (info->vendor_id == VENDOR_ID_SiS && info->device_id == DEVICE_ID_SiS900) { + // enable bus mastering for device + pci->write_pci_config(info->bus, info->device, info->function, + PCI_command, 2, + PCI_command_master | pci->read_pci_config( + info->bus, info->device, info->function, + PCI_command, 2)); + + pciInfo[found++] = info; + dprintf(DEVICE_NAME ": revision = %lx\n", info->revision); + + info = malloc(sizeof(pci_info)); + } + } + free(info); + + if (found == 0) { + put_module(B_PCI_MODULE_NAME); + return ENODEV; + } + + // create device name list + { + char name[32]; + + for (i = 0; i < found; i++) { + sprintf(name, DEVICE_DRIVERNAME "/%d", i); + gDeviceNames[i] = strdup(name); + } + gDeviceNames[i] = NULL; + } + return B_OK; +} + + +void +uninit_driver(void) +{ + void *item; + int index; + + TRACE((DEVICE_NAME ": uninit_driver()\n")); + + // free device names & pci info + for (index = 0; (item = gDeviceNames[index]) != NULL; index++) { + free(item); + free(pciInfo[index]); + } + + put_module(B_PCI_MODULE_NAME); +} + + +device_hooks * +find_device(const char *name) +{ + int index; + + TRACE((DEVICE_NAME ": find_device()\n")); + + for (index = 0; gDeviceNames[index] != NULL; index++) { + if (!strcmp(name, gDeviceNames[index])) + return &gDeviceHooks; + } + + return NULL; +} + +/* +void +wake_driver(void) +{ + // for compatibility with Dano, only +} + + +void +suspend_driver(void) +{ + // for compatibility with Dano, only +} +*/ diff --git a/src/add-ons/kernel/drivers/network/sis900/driver.h b/src/add-ons/kernel/drivers/network/sis900/driver.h new file mode 100644 index 0000000000..feb0945eec --- /dev/null +++ b/src/add-ons/kernel/drivers/network/sis900/driver.h @@ -0,0 +1,34 @@ +#ifndef DRIVER_H +#define DRIVER_H +/* driver - kernel driver for SiS900 networking +** +** Copyright © 2001-2003 pinc Software. All Rights Reserved. +*/ + + +// PCI Communications + +#define IO_PORT_PCI_ACCESS true +//#define MEMORY_MAPPED_PCI_ACCESS true + +#if IO_PORT_PCI_ACCESS +# define write8(address,value) (*pci->write_io_8)((address),(value)) +# define write16(address,value) (*pci->write_io_16)((address),(value)) +# define write32(address,value) (*pci->write_io_32)((address),(value)) +# define read8(address) ((*pci->read_io_8)(address)) +# define read16(address) ((*pci->read_io_16)(address)) +# define read32(address) ((*pci->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 char *gDeviceNames[]; +extern pci_info *pciInfo[]; +extern pci_module_info *pci; + +#endif /* DRIVER_H */ diff --git a/src/add-ons/kernel/drivers/network/sis900/ether_driver.h b/src/add-ons/kernel/drivers/network/sis900/ether_driver.h new file mode 100644 index 0000000000..9572f792e8 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/sis900/ether_driver.h @@ -0,0 +1,57 @@ +/* + * ether_driver.h + * + * Ethernet driver: handles NE2000 and 3C503 cards + */ +/* + Copyright 1999, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. +*/ +#ifndef _ETHER_DRIVER_H +#define _ETHER_DRIVER_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/* + * ioctls: belongs in a public header file + * somewhere, so that the net_server and other ethernet drivers can use. + */ +enum { + ETHER_GETADDR = B_DEVICE_OP_CODES_END, /* get ethernet address */ + ETHER_INIT, /* set irq and port */ + ETHER_NONBLOCK, /* set/unset nonblocking mode */ + ETHER_ADDMULTI, /* add multicast addr */ + ETHER_REMMULTI, /* rem multicast addr */ + ETHER_SETPROMISC, /* set promiscuous */ + ETHER_GETFRAMESIZE /* get frame size */ +}; + + +/* + * 48-bit ethernet address, passed back from ETHER_GETADDR + */ +typedef struct { + unsigned char ebyte[6]; +} ether_address_t; + + +/* + * info passed to ETHER_INIT + */ + +typedef struct ether_init_params { + short port; + short irq; + unsigned long mem; +} ether_init_params_t; + +#ifdef __cplusplus +} +#endif + + +#endif /* _ETHER_DRIVER_H */ diff --git a/src/add-ons/kernel/drivers/network/sis900/interface.c b/src/add-ons/kernel/drivers/network/sis900/interface.c new file mode 100644 index 0000000000..ec5bfef5b8 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/sis900/interface.c @@ -0,0 +1,221 @@ +/* interface.c - MII, MDIO and EEPROM interface of SiS900 +** +** Copyright 2001 pinc Software. All Rights Reserved. +*/ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "ether_driver.h" +#include "driver.h" +#include "device.h" +#include "interface.h" +#include "sis900.h" + + +// EEPROM access definitions +#define EEPROM_DELAY() read32(eepromAccess) +#define EEPROM_READ32() read32(eepromAccess) +#define EEPROM_WRITE8(value) write8(eepromAccess,value) +#define EEPROM_WRITE32(value) write32(eepromAccess,value) + + +uint16 eeprom_read(struct sis_info *info,int address) +{ + long eepromAccess = (long)info->registers + SiS900_MAC_EEPROM_ACCESS; + uint32 readCmd = SiS900_EEPROM_CMD_READ | address; + uint16 retval = 0; + int i; + + EEPROM_WRITE32(0); + EEPROM_DELAY(); + EEPROM_WRITE32(SiS900_EEPROM_CLOCK); + EEPROM_DELAY(); + + // Shift the read command (9) bits out. + for (i = 8;i >= 0;i--) + { + uint32 value = (readCmd & (1 << i)) ? SiS900_EEPROM_DATA_IN | SiS900_EEPROM_SELECT + : SiS900_EEPROM_SELECT; + + EEPROM_WRITE32(value); + EEPROM_DELAY(); + EEPROM_WRITE32(value | SiS900_EEPROM_CLOCK); + EEPROM_DELAY(); + } + EEPROM_WRITE8(SiS900_EEPROM_SELECT); + EEPROM_DELAY(); + + // read in the 16 bit data + for (i = 16;i > 0;i--) + { + EEPROM_WRITE32(SiS900_EEPROM_SELECT); + EEPROM_DELAY(); + EEPROM_WRITE32(SiS900_EEPROM_SELECT | SiS900_EEPROM_CLOCK); + EEPROM_DELAY(); + retval = (retval << 1) | ((EEPROM_READ32() & SiS900_EEPROM_DATA_OUT) ? 1 : 0); + EEPROM_DELAY(); + } + + EEPROM_WRITE32(0); + EEPROM_DELAY(); + EEPROM_WRITE32(SiS900_EEPROM_CLOCK); + + return retval; +} + + +/**************************** MII/MDIO ****************************/ +// #pragma mark - + + +#define mdio_delay(address) read32(address) + + +static void +mdio_idle(uint32 address) +{ + write32(address,SiS900_MII_MDIO | SiS900_MII_MDDIR); + mdio_delay(address); + write32(address,SiS900_MII_MDIO | SiS900_MII_MDDIR | SiS900_MII_MDC); +} + + +static void +mdio_reset(uint32 address) +{ + int32 i; + + for (i = 32; i-- > 0;) + { + write32(address,SiS900_MII_MDIO | SiS900_MII_MDDIR); + mdio_delay(address); + write32(address,SiS900_MII_MDIO | SiS900_MII_MDDIR | SiS900_MII_MDC); + mdio_delay(address); + } +} + + +void +mdio_writeToPHY(struct sis_info *info,uint16 phy,uint16 reg,uint16 value) +{ + uint32 address = (uint32)info->registers + SiS900_MAC_EEPROM_ACCESS; + int32 cmd = MII_CMD_WRITE | (phy << MII_PHY_SHIFT) | (reg << MII_REG_SHIFT); + int i; + + mdio_reset(address); + mdio_idle(address); + + // issue the command + for (i = 16;i-- > 0;) + { + int32 data = SiS900_MII_MDDIR | (cmd & (1 << i) ? SiS900_MII_MDIO : 0); + + write8(address,data); + mdio_delay(address); + write8(address,data | SiS900_MII_MDC); + mdio_delay(address); + } + mdio_delay(address); + + // write the value + for (i = 16;i-- > 0;) + { + int32 data = SiS900_MII_MDDIR | (value & (1 << i) ? SiS900_MII_MDIO : 0); + + write32(address,data); + mdio_delay(address); + write32(address,data | SiS900_MII_MDC); + mdio_delay(address); + } + mdio_delay(address); + + // clear extra bits + for (i = 2;i-- > 0;) + { + write8(address,0); + mdio_delay(address); + write8(address,SiS900_MII_MDC); + mdio_delay(address); + } + write32(address,0); +} + + +uint16 +mdio_readFromPHY(struct sis_info *info,uint16 phy,uint16 reg) +{ + uint32 address = (uint32)info->registers + SiS900_MAC_EEPROM_ACCESS; + int32 cmd = MII_CMD_READ | (phy << MII_PHY_SHIFT) | (reg << MII_REG_SHIFT); + uint16 value = 0; + int i; + + mdio_reset(address); + mdio_idle(address); + + for (i = 16; i-- > 0;) + { + int32 data = SiS900_MII_MDDIR | (cmd & (1 << i) ? SiS900_MII_MDIO : 0); + + write32(address,data); + mdio_delay(address); + write32(address,data | SiS900_MII_MDC); + mdio_delay(address); + } + + // read the value + for (i = 16;i-- > 0;) + { + write32(address,0); + mdio_delay(address); + value = (value << 1) | (read32(address) & SiS900_MII_MDIO ? 1 : 0); + write32(address,SiS900_MII_MDC); + mdio_delay(address); + } + write32(address,0); + + return value; +} + + +uint16 +mdio_read(struct sis_info *info, uint16 reg) +{ + return mdio_readFromPHY(info,info->phy,reg); +} + + +void +mdio_write(struct sis_info *info, uint16 reg, uint16 value) +{ + mdio_writeToPHY(info,info->phy,reg,value); +} + + +uint16 +mdio_statusFromPHY(struct sis_info *info, uint16 phy) +{ + uint16 status; + int i = 0; + + // the status must be retrieved two times, because the first + // one may not work on some PHYs (notably ICS 1893) + while (i++ < 2) + status = mdio_readFromPHY(info,phy,MII_STATUS); + return status; +} + + +uint16 +mdio_status(struct sis_info *info) +{ + return mdio_statusFromPHY(info,info->phy); +} + diff --git a/src/add-ons/kernel/drivers/network/sis900/interface.h b/src/add-ons/kernel/drivers/network/sis900/interface.h new file mode 100644 index 0000000000..30d5d8d2a0 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/sis900/interface.h @@ -0,0 +1,17 @@ +#ifndef INTERFACE_H +#define INTERFACE_H + +#include "sis900.h" + +extern uint16 eeprom_read(struct sis_info *info,int address); + +extern uint16 mdio_read(struct sis_info *info,uint16 reg); +extern uint16 mdio_readFromPHY(struct sis_info *info,uint16 phy,uint16 reg); +extern void mdio_write(struct sis_info *info,uint16 reg,uint16 value); +extern void mdio_writeToPHY(struct sis_info *info,uint16 phy,uint16 reg,uint16 value); + +extern uint16 mdio_statusFromPHY(struct sis_info *info, uint16 phy); +extern uint16 mdio_status(struct sis_info *info); + + +#endif /* INTERFACE_H */ diff --git a/src/add-ons/kernel/drivers/network/sis900/sis900.c b/src/add-ons/kernel/drivers/network/sis900/sis900.c new file mode 100644 index 0000000000..2ff0061558 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/sis900/sis900.c @@ -0,0 +1,774 @@ +/* sis900.c - SiS900 chip specific functions +** +** Copyright © 2001-2003 pinc Software. All Rights Reserved. +*/ + + +#include +#include +#include +#include +#include + +#include +#include + +#include "ether_driver.h" +#include "driver.h" +#include "device.h" +#include "interface.h" +#include "sis900.h" + + +// prototypes + +uint16 sis900_resetPHY(struct sis_info *info); +void sis900_selectPHY(struct sis_info *info); +void sis900_setMode(struct sis_info *info, int32 mode); +int32 sis900_readMode(struct sis_info *info); + + +// MII chip info table + +#define PHY_ID0_SiS900_INTERNAL 0x001d +#define PHY_ID1_SiS900_INTERNAL 0x8000 +#define PHY_ID0_ICS_1893 0x0015 +#define PHY_ID1_ICS_1893 0xff40 +#define PHY_ID0_REALTEK_8201 0x0000 +#define PHY_ID1_REALTEK_8201 0x8200 +#define MII_HOME 0x0001 +#define MII_LAN 0x0002 + +const static struct mii_chip_info +{ + const char *name; + uint16 id0,id1; + uint8 types; +} +gMIIChips[] = { + {"SiS 900 Internal MII PHY", PHY_ID0_SiS900_INTERNAL,PHY_ID1_SiS900_INTERNAL,MII_LAN}, + {"SiS 7014 Physical Layer Solution",0x0016, 0xf830, MII_LAN}, + {"AMD 79C901 10BASE-T PHY", 0x0000, 0x6B70, MII_LAN}, + {"AMD 79C901 HomePNA PHY", 0x0000, 0x6B90, MII_HOME}, + {"ICS 1893 LAN PHY", PHY_ID0_ICS_1893,PHY_ID1_ICS_1893,MII_LAN}, + {"NS 83851 PHY", 0x2000, 0x5C20, MII_LAN | MII_HOME}, + {"Realtek RTL8201 PHY", PHY_ID0_REALTEK_8201,PHY_ID1_REALTEK_8201,MII_LAN}, + {NULL,0,0,0} +}; + + +/***************************** helper functions *****************************/ + +uint32 +physicalAddress(volatile void *address,uint32 length) +{ + physical_entry table; + + get_memory_map((void *)address,length,&table,1); + return (uint32)table.address; +} + + +/***************************** interrupt handling *****************************/ +// #pragma mark - +int32 intrCounter = 0; +int32 lastIntr[100]; + + +int32 +sis900_rxInterrupt(struct sis_info *info) +{ + int32 handled = B_UNHANDLED_INTERRUPT; + int16 releaseRxSem = 0; + int16 limit; + + acquire_spinlock(&info->rxSpinlock); + + HACK(spin(10000)); + + // check for packet ownership + for (limit = info->rxFree; limit > 0; limit--) + { + if (!(info->rxDescriptor[info->rxInterruptIndex].status & SiS900_DESCR_OWN)) + { +// if (limit == info->rxFree) +// { + //dprintf("here!\n"); +// limit++; +// continue; +// } + break; + } + //dprintf("received frame %d!\n",info->rxInterruptIndex); + releaseRxSem++; + info->rxInterruptIndex = (info->rxInterruptIndex + 1) & NUM_Rx_MASK; + info->rxFree--; + } + release_spinlock(&info->rxSpinlock); + + // reenable rx queue + write32((uint32)info->registers + SiS900_MAC_COMMAND,SiS900_MAC_CMD_Rx_ENABLE); + + if (releaseRxSem) + { + release_sem_etc(info->rxSem,releaseRxSem,B_DO_NOT_RESCHEDULE); + return B_INVOKE_SCHEDULER; + } + + return handled; +} + + +int32 +sis900_txInterrupt(struct sis_info *info) +{ + int16 releaseTxSem = 0; + uint32 status; + int16 limit; + + acquire_spinlock(&info->txSpinlock); + + HACK(spin(10000)); + +//dprintf("here we are: sent = %d\n",info->txSent); + for (limit = info->txSent; limit > 0; limit--) + { + status = info->txDescriptor[info->txInterruptIndex].status; + +//dprintf("txIntr: %d: mem = %lx : hardware = %lx\n",info->txInterruptIndex, +// physicalAddress(&info->txDescriptor[info->txInterruptIndex],sizeof(struct buffer_desc)), +// read32((uint32)info->registers + SiS900_MAC_Tx_DESCR)); + + /* Does the device generate extra interrupts? */ + if (status & SiS900_DESCR_OWN) + { + struct buffer_desc *b = (void *)read32((uint32)info->registers + SiS900_MAC_Tx_DESCR); + int16 that; + for (that = 0;that < NUM_Tx_DESCR && (void *)physicalAddress(&info->txDescriptor[that],sizeof(struct buffer_desc)) != b;that++); + if (that == NUM_Tx_DESCR) + { + //dprintf("not in ring!\n"); + that = 0; + } +//dprintf("tx busy %d: %lx (hardware status %d = %lx)!\n",info->txInterruptIndex,status,that,info->txDescriptor[that].status); +// if (limit == info->txSent) +// { +//dprintf("oh no!\n"); +// limit++; +// continue; +// } + break; + } + +//dprintf("tx %d!\n",info->txInterruptIndex); + if (status & (SiS900_DESCR_Tx_ABORT | SiS900_DESCR_Tx_UNDERRUN | + SiS900_DESCR_Tx_OOW_COLLISION)) + dprintf("tx error: %x\n", status); + else + info->txDescriptor[info->txInterruptIndex].status = 0; + + releaseTxSem++; /* this many buffers are free */ + info->txInterruptIndex = (info->txInterruptIndex + 1) & NUM_Tx_MASK; + info->txSent--; + + if (info->txSent < 0 || info->txSent > NUM_Tx_DESCR) + dprintf("ERROR interrupt: txSent = %d\n", info->txSent); + } + release_spinlock(&info->txSpinlock); + + if (releaseTxSem) + { +// sem_info semInfo; + +// get_sem_info(info->txSem,&semInfo); + release_sem_etc(info->txSem,releaseTxSem,B_DO_NOT_RESCHEDULE); + return B_INVOKE_SCHEDULER; + } + + return B_HANDLED_INTERRUPT; +} + + +int32 +sis900_interrupt(void *data) +{ + struct sis_info *info = data; + int32 handled = B_UNHANDLED_INTERRUPT; + int16 worklimit = 20; + uint32 intr; + uint32 i, j; + cpu_status former; + + former = disable_interrupts(); + acquire_spinlock(&info->lock); + + while (worklimit-- > 0) { + // reading the interrupt status register clears all interrupts + intr = read32((uint32)info->registers + SiS900_MAC_INTR_STATUS); + if (!intr) + break; + + TRACE(("************ interrupt received: %08lx **************\n", intr)); + intrCounter = (intrCounter + 1) % 100; + lastIntr[intrCounter] = intr; + + // wake-up event interrupt + if (intr & SiS900_INTR_WAKEUP_EVENT) + { + TRACE(("wake-up event received: %ld\n", read32((uint32)info->registers + SiS900_MAC_WAKEUP_EVENT))); + + // clear PM event register + write32((uint32)info->registers + SiS900_MAC_WAKEUP_EVENT, + SiS900_WAKEUP_LINK_ON | SiS900_WAKEUP_LINK_LOSS); + handled = B_HANDLED_INTERRUPT; + } + + // receive packet interrupts + if (intr & (/*SiS900_INTR_Rx_STATUS_OVERRUN |*/ SiS900_INTR_Rx_OVERRUN | + SiS900_INTR_Rx_ERROR | SiS900_INTR_Rx_OK)) + handled = sis900_rxInterrupt(info); + + // transmit packet interrupts + if (intr & (SiS900_INTR_Tx_UNDERRUN | SiS900_INTR_Tx_ERROR | + SiS900_INTR_Tx_IDLE | SiS900_INTR_Tx_OK)) + handled = sis900_txInterrupt(info); + } + + release_spinlock(&info->lock); + restore_interrupts(former); + + return handled; +} + + +void +sis900_disableInterrupts(struct sis_info *info) +{ + write32((uint32)info->registers + SiS900_MAC_INTR_MASK,0); + write32((uint32)info->registers + SiS900_MAC_INTR_ENABLE,0); +} + + +void +sis900_enableInterrupts(struct sis_info *info) +{ + write32((uint32)info->registers + SiS900_MAC_INTR_ENABLE,0); + + // enable link detection + write32((uint32)info->registers + SiS900_MAC_WAKEUP_CONTROL, + SiS900_WAKEUP_LINK_ON | SiS900_WAKEUP_LINK_LOSS); + + // set interrupt mask + write32((uint32)info->registers + SiS900_MAC_INTR_MASK, + //SiS900_INTR_WAKEUP_EVENT | + SiS900_INTR_Tx_UNDERRUN | SiS900_INTR_Tx_ERROR | SiS900_INTR_Tx_IDLE | SiS900_INTR_Tx_OK | + SiS900_INTR_Rx_STATUS_OVERRUN | SiS900_INTR_Rx_OVERRUN | + SiS900_INTR_Rx_ERROR | SiS900_INTR_Rx_OK); + + write32((uint32)info->registers + SiS900_MAC_INTR_ENABLE,1); +} + + +/***************************** PHY and link mode *****************************/ +// #pragma mark - + + +int32 +sis900_timer(timer *t) +{ + struct sis_info *info = (struct sis_info *)t; + + if (!info->autoNegotiationComplete) { + int32 mode = sis900_readMode(info); + if (mode) + sis900_setMode(info, mode); + + return 0; + } + + if (info->link) { // link lost + uint16 status = mdio_status(info); + + if ((status & MII_STATUS_LINK) == 0) { + info->link = false; + dprintf(DEVICE_NAME ": link lost\n"); + + // if it's the internal SiS900 MII PHY, reset it + if (info->currentPHY->id0 == PHY_ID0_SiS900_INTERNAL + && (info->currentPHY->id1 & 0xfff0) == PHY_ID1_SiS900_INTERNAL) + sis900_resetPHY(info); + } + } + if (!info->link) { // new link established + uint16 status; + + sis900_selectPHY(info); + + status = mdio_status(info); + if (status & MII_STATUS_LINK) { + sis900_checkMode(info); + info->link = true; + } + } + +// revision = info->pciInfo->revision; +// if (!(revision == SiS900_REVISION_SiS630E || revision == SiS900_REVISION_SiS630EA1 +// || revision == SiS900_REVISION_SiS630A || revision == SiS900_REVISION_SiS630ET)) +// bug(DEVICE_NAME ": set_eq() not needed!\n"); +// else +// bug("********* set_eq() would be needed! ********\n"); + + return 0; +} + + +uint16 +sis900_resetPHY(struct sis_info *info) +{ + uint16 status = mdio_status(info); + + mdio_write(info, MII_CONTROL, MII_CONTROL_RESET); + return status; +} + + +status_t +sis900_initPHYs(struct sis_info *info) +{ + uint16 phy; + uint8 revision; + + // search for total of 32 possible MII PHY addresses + for (phy = 0; phy < 32; phy++) { + struct mii_phy *mii; + uint16 status; + int i; + + status = mdio_statusFromPHY(info,phy); + if (status == 0xffff || status == 0x0000) + // this MII is not accessable + continue; + + mii = (struct mii_phy *)malloc(sizeof(struct mii_phy)); + if (mii == NULL) + return B_NO_MEMORY; + + mii->address = phy; + mii->id0 = mdio_readFromPHY(info,phy,MII_PHY_ID0); + mii->id1 = mdio_readFromPHY(info,phy,MII_PHY_ID1); + mii->types = MII_HOME; + mii->next = info->firstPHY; + info->firstPHY = mii; + + for (i = 0; gMIIChips[i].name; i++) { + if (gMIIChips[i].id0 != mii->id0 || gMIIChips[i].id1 != (mii->id1 & 0xfff0)) + continue; + + dprintf("Found MII PHY: %s\n", gMIIChips[i].name); + + mii->types = gMIIChips[i].types; + break; + } + if (gMIIChips[i].name == NULL) + dprintf("Unknown MII PHY transceiver: id = (%x, %x).\n",mii->id0,mii->id1); + } + + if (info->firstPHY == NULL) { + dprintf("No MII PHY transceiver found!\n"); + return B_ENTRY_NOT_FOUND; + } + + sis900_selectPHY(info); + + // if the internal PHY is selected, reset it + if (info->currentPHY->id0 == PHY_ID0_SiS900_INTERNAL + && (info->currentPHY->id1 & 0xfff0) == PHY_ID1_SiS900_INTERNAL) { + if (sis900_resetPHY(info) & MII_STATUS_LINK) { + uint16 poll = MII_STATUS_LINK; + while (poll) { + poll ^= mdio_read(info,MII_STATUS) & poll; + } + } + } + + // workaround for ICS1893 PHY + if (info->currentPHY->id0 == PHY_ID0_ICS_1893 + && (info->currentPHY->id1 & 0xfff0) == PHY_ID1_ICS_1893) + mdio_write(info, 0x0018, 0xD200); + + // SiS 630E has some bugs on default value of PHY registers + if (info->pciInfo->revision == SiS900_REVISION_SiS630E) { + mdio_write(info, MII_AUTONEG_ADV, 0x05e1); + mdio_write(info, MII_CONFIG1, 0x22); + mdio_write(info, MII_CONFIG2, 0xff00); + mdio_write(info, MII_MASK, 0xffc0); + } + + info->link = mdio_status(info) & MII_STATUS_LINK; + + return B_OK; +} + + +void +sis900_selectPHY(struct sis_info *info) +{ + uint16 status; + + // ToDo: need to be changed, select PHY in relation to the link mode + info->currentPHY = info->firstPHY; + info->phy = info->currentPHY->address; + + status = mdio_read(info, MII_CONTROL); + status &= ~MII_CONTROL_ISOLATE; + + mdio_write(info, MII_CONTROL, status); +} + + +void +sis900_setMode(struct sis_info *info,int32 mode) +{ + uint32 address = (uint32)info->registers + SiS900_MAC_CONFIG; + uint32 txFlags = SiS900_Tx_AUTO_PADDING | SiS900_Tx_FILL_THRES; + uint32 rxFlags = 0; + int32 speed = mode & LINK_SPEED_MASK; + + if (read32(address) & SiS900_MAC_CONFIG_EDB_MASTER) { + TRACE((DEVICE_NAME ": EDB master is set!\n")); + txFlags |= 5 << SiS900_DMA_SHIFT; + rxFlags = 5 << SiS900_DMA_SHIFT; + } + + // link speed FIFO thresholds + + if (speed == LINK_SPEED_HOME || speed == LINK_SPEED_10_MBIT) { + rxFlags |= SiS900_Rx_10_MBIT_DRAIN_THRES; + txFlags |= SiS900_Tx_10_MBIT_DRAIN_THRES; + } else { + rxFlags |= SiS900_Rx_100_MBIT_DRAIN_THRES; + txFlags |= SiS900_Tx_100_MBIT_DRAIN_THRES; + } + + // duplex mode + + if ((mode & LINK_DUPLEX_MASK) == LINK_FULL_DUPLEX) { + txFlags |= SiS900_Tx_CS_IGNORE | SiS900_Tx_HB_IGNORE; + rxFlags |= SiS900_Rx_ACCEPT_Tx_PACKETS; + } + + write32((uint32)info->registers + SiS900_MAC_Tx_CONFIG,txFlags); + write32((uint32)info->registers + SiS900_MAC_Rx_CONFIG,rxFlags); +} + + +int32 +sis900_readMode(struct sis_info *info) +{ + struct mii_phy *phy = info->currentPHY; + uint16 autoAdv, autoLinkPartner; + int32 speed, duplex; + + uint16 status = mdio_status(info); + if (!(status & MII_STATUS_LINK)) { + dprintf(DEVICE_NAME ": no link detected (status = %lx)\n", status); + return 0; + } + + // auto negotiation completed + autoAdv = mdio_read(info, MII_AUTONEG_ADV); + autoLinkPartner = mdio_read(info, MII_AUTONEG_LINK_PARTNER); + status = autoAdv & autoLinkPartner; + + speed = status & (MII_NWAY_TX | MII_NWAY_TX_FDX) ? LINK_SPEED_100_MBIT : LINK_SPEED_10_MBIT; + duplex = status & (MII_NWAY_TX_FDX | MII_NWAY_T_FDX) ? LINK_FULL_DUPLEX : LINK_HALF_DUPLEX; + + info->autoNegotiationComplete = true; + + // workaround for Realtek RTL8201 PHY issue + if (phy->id0 == PHY_ID0_REALTEK_8201 && (phy->id1 & 0xFFF0) == PHY_ID1_REALTEK_8201) { + if (mdio_read(info, MII_CONTROL) & MII_CONTROL_FULL_DUPLEX) + duplex = LINK_FULL_DUPLEX; + if (mdio_read(info, 0x0019) & 0x01) + speed = LINK_SPEED_100_MBIT; + } + + dprintf(DEVICE_NAME ": linked, 10%s MBit, %s duplex\n", + speed == LINK_SPEED_100_MBIT ? "0" : "", + duplex == LINK_FULL_DUPLEX ? "full" : "half"); + + return speed | duplex; +} + + +void +sis900_setAutoNegotiationCapabilities(struct sis_info *info) +{ + uint16 status = mdio_status(info); + uint16 capabilities = MII_NWAY_CSMA_CD + | (status & MII_STATUS_CAN_TX_FDX ? MII_NWAY_TX_FDX : 0) + | (status & MII_STATUS_CAN_TX ? MII_NWAY_TX : 0) + | (status & MII_STATUS_CAN_T_FDX ? MII_NWAY_T_FDX : 0) + | (status & MII_STATUS_CAN_T ? MII_NWAY_T : 0); + + TRACE((DEVICE_NAME ": write capabilities %ld\n", capabilities)); + mdio_write(info, MII_AUTONEG_ADV, capabilities); +} + + +void +sis900_autoNegotiate(struct sis_info *info) +{ + uint16 status = mdio_status(info); + int32 revision; + int32 mode; + + if ((status & MII_STATUS_LINK) == 0) + { + TRACE((DEVICE_NAME ": media link off\n")); + info->autoNegotiationComplete = true; + return; + } + TRACE((DEVICE_NAME ": auto negotiation started...\n")); + + // reset auto negotiation + mdio_write(info, MII_CONTROL, MII_CONTROL_AUTO | MII_CONTROL_RESET_AUTONEG); + info->autoNegotiationComplete = false; +} + + +void +sis900_checkMode(struct sis_info *info) +{ + uint32 address = (uint32)info->registers + SiS900_MAC_CONFIG; + + if (info->currentPHY->types == MII_LAN) { + TRACE((DEVICE_NAME ": PHY type is LAN\n")); + + // enable excessive defferal timer + write32(address, ~SiS900_MAC_CONFIG_EXCESSIVE_DEFERRAL & read32(address)); + + sis900_setAutoNegotiationCapabilities(info); + sis900_autoNegotiate(info); + } else { + TRACE((DEVICE_NAME ": PHY type is not LAN\n")); + + // disable excessive defferal timer + write32(address, SiS900_MAC_CONFIG_EXCESSIVE_DEFERRAL | read32(address)); + + sis900_setMode(info, LINK_SPEED_HOME | LINK_HALF_DUPLEX); + info->autoNegotiationComplete = true; + } +} + + +/***************************** MACs, rings & config *****************************/ +// #pragma mark - + + +bool +sis900_getMACAddress(struct sis_info *info) +{ + if (info->pciInfo->revision >= SiS900_REVISION_SiS96x) { + // SiS 962 & 963 are using a different method to access the EEPROM + // than the standard SiS 630 + long eepromAccess = (long)info->registers + SiS900_MAC_EEPROM_ACCESS; + uint32 tries = 0; + + write32(eepromAccess, SiS96x_EEPROM_CMD_REQ); + + while (tries < 1000) { + if (read32(eepromAccess) & SiS96x_EEPROM_CMD_GRANT) { + int i; + + /* get MAC address from EEPROM */ + for (i = 0; i < 3; i++) { + uint16 id; + + id = eeprom_read(info, SiS900_EEPROM_MAC_ADDRESS + i); + info->address.ebyte[i*2 + 1] = id >> 8; + info->address.ebyte[i*2] = id & 0xff; + } + + write32(eepromAccess, SiS96x_EEPROM_CMD_DONE); + return true; + } else { + spin(2); + tries++; + } + } + write32(eepromAccess, SiS96x_EEPROM_CMD_DONE); + return false; + } else if (info->pciInfo->revision >= SiS900_REVISION_SiS630E) { + /* SiS630E and above are using CMOS RAM to store MAC address */ + pci_info isa; + int32 index; + + for (index = 0; pci->get_nth_pci_info(index, &isa) == B_OK; index++) { + if (isa.vendor_id == VENDOR_ID_SiS && isa.device_id == DEVICE_ID_SiS_ISA_BRIDGE) + { + uint8 reg,i; + uint32 registers = isa.u.h0.base_registers[0]; + + reg = pci->read_pci_config(isa.bus,isa.device,isa.function,0x48,1); + pci->write_pci_config(isa.bus,isa.device,isa.function,0x48,1,reg | 0x40); + + for (i = 0; i < 6; i++) { + write8(registers + 0x70,0x09 + i); + info->address.ebyte[i] = read8(registers + 0x71); + } + + pci->write_pci_config(isa.bus,isa.device,isa.function,0x48,1,reg & ~0x40); + return true; + } + } + return false; + } else { + /* SiS630 stores the MAC in an eeprom */ + uint16 signature; + int i; + + /* check to see if we have sane EEPROM */ + signature = eeprom_read(info,SiS900_EEPROM_SIGNATURE); + if (signature == 0xffff || signature == 0x0000) { + dprintf(DEVICE_NAME ": cannot read EEPROM signature\n"); + return false; + } + + /* get MAC address from EEPROM */ + for (i = 0; i < 3; i++) { + uint16 id; + + id = eeprom_read(info,SiS900_EEPROM_MAC_ADDRESS + i); + info->address.ebyte[i*2 + 1] = id >> 8; + info->address.ebyte[i*2] = id & 0xff; + } + + return true; + } + return false; +} + + +status_t +sis900_reset(struct sis_info *info) +{ + uint32 address = (uint32)info->registers + SiS900_MAC_COMMAND; + int16 tries = 1000; + status_t status; + + TRACE(("sis900 reset\n")); + + //write32((uint32)info->registers + SiS900_MAC_INTR_MASK,0); + //write32((uint32)info->registers + SiS900_MAC_INTR_ENABLE,0); + + write32(address, SiS900_MAC_CMD_RESET); + + write32((uint32)info->registers + SiS900_MAC_Rx_FILTER_CONTROL, SiS900_RxF_ENABLE | + SiS900_RxF_ACCEPT_ALL_BROADCAST | SiS900_RxF_ACCEPT_ALL_MULTICAST); + + // wait until the chip leaves reset state + while ((read32(address) & SiS900_MAC_CMD_RESET) && tries-- > 0) + snooze(2); + + write32((uint32)info->registers + SiS900_MAC_COMMAND, SiS900_MAC_CMD_Tx_ENABLE); + + return B_OK; +} + + +void +sis900_setPromiscuous(struct sis_info *info, bool on) +{ + long filterControl = (long)info->registers + SiS900_MAC_Rx_FILTER_CONTROL; + int32 filter = read32(filterControl); + + // accept all incoming packets (or not) + if (on) { + write32(filterControl, SiS900_RxF_ENABLE | + SiS900_RxF_ACCEPT_ALL_BROADCAST | SiS900_RxF_ACCEPT_ALL_MULTICAST); + } else + write32(filterControl, filter | ~SiS900_RxF_ACCEPT_ALL_ADDRESSES); +} + + +void +sis900_setRxFilter(struct sis_info *info) +{ + long filterControl = (long)info->registers + SiS900_MAC_Rx_FILTER_CONTROL; + long filterData = (long)info->registers + SiS900_MAC_Rx_FILTER_DATA; + int i; + + // set MAC address as receive filter + for (i = 0; i < 3; i++) { + write32(filterControl, i << SiS900_Rx_FILTER_ADDRESS_SHIFT); + write32(filterData, info->address.ebyte[i*2] | (info->address.ebyte[i*2 + 1] << 8)); + } + write32(filterControl, SiS900_RxF_ENABLE | + SiS900_RxF_ACCEPT_ALL_BROADCAST | SiS900_RxF_ACCEPT_ALL_MULTICAST /*| SiS900_RxF_ACCEPT_ALL_ADDRESSES*/); +} + + +void +sis900_deleteRings(struct sis_info *info) +{ + delete_area(info->txArea); + delete_area(info->rxArea); +} + + +status_t +sis900_createRings(struct sis_info *info) +{ + uint16 i; + + // create transmit buffer area + if ((info->txArea = create_area("sis900 tx buffer", (void **)&info->txBuffer[0], + B_ANY_KERNEL_ADDRESS, + ROUND_TO_PAGE_SIZE(BUFFER_SIZE * NUM_Tx_DESCR), + B_FULL_LOCK, B_READ_AREA | B_WRITE_AREA)) < B_OK) + return info->txArea; + + // initialize transmit buffer descriptors + for (i = 1; i < NUM_Tx_DESCR; i++) + info->txBuffer[i] = (void *)(((uint32)info->txBuffer[0]) + (i * BUFFER_SIZE)); + + for (i = 0; i < NUM_Tx_DESCR; i++) { + info->txDescriptor[i].status = 0; + info->txDescriptor[i].buffer = physicalAddress(info->txBuffer[i], BUFFER_SIZE); + info->txDescriptor[i].link = +#if (NUM_Tx_DESCR == 1) + 0; +#else + physicalAddress(&info->txDescriptor[(i + 1) & NUM_Tx_MASK], sizeof(struct buffer_desc)); +#endif + } + + // create receive buffer area + if ((info->rxArea = create_area("sis900 rx buffer", (void **)&info->rxBuffer[0], + B_ANY_KERNEL_ADDRESS, + ROUND_TO_PAGE_SIZE(BUFFER_SIZE * NUM_Rx_DESCR), + B_FULL_LOCK, B_READ_AREA | B_WRITE_AREA)) < B_OK) + { + delete_area(info->txArea); + return info->rxArea; + } + + // initialize receive buffer descriptors + for (i = 1; i < NUM_Rx_DESCR; i++) + info->rxBuffer[i] = (void *)(((uint32)info->rxBuffer[0]) + (i * BUFFER_SIZE)); + + for (i = 0; i < NUM_Rx_DESCR; i++) { + info->rxDescriptor[i].status = MAX_FRAME_SIZE; + info->rxDescriptor[i].buffer = physicalAddress(info->rxBuffer[i],BUFFER_SIZE); + info->rxDescriptor[i].link = physicalAddress(&info->rxDescriptor[(i + 1) & NUM_Rx_MASK],sizeof(struct buffer_desc)); + } + info->rxFree = NUM_Rx_DESCR; + + // set descriptor pointer registers + write32((uint32)info->registers + SiS900_MAC_Tx_DESCR,physicalAddress(&info->txDescriptor[0],sizeof(struct buffer_desc))); + write32((uint32)info->registers + SiS900_MAC_Rx_DESCR,physicalAddress(&info->rxDescriptor[0],sizeof(struct buffer_desc))); + + return B_OK; +} + diff --git a/src/add-ons/kernel/drivers/network/sis900/sis900.h b/src/add-ons/kernel/drivers/network/sis900/sis900.h new file mode 100644 index 0000000000..9bfe609b64 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/sis900/sis900.h @@ -0,0 +1,387 @@ +#ifndef SIS900_H +#define SIS900_H +/* sis900.h - SiS 900 driver/chip specific definitions +** +** Copyright © 2001-2003 pinc Software. All Rights Reserved. +*/ + + +#include "ether_driver.h" + +#define VENDOR_ID_SiS 0x1039 +#define DEVICE_ID_SiS900 0x900 + +#define DEVICE_ID_SiS_ISA_BRIDGE 0x0008 + +enum SiS_revisions { + SiS900_REVISION_B = 0x03, + SiS900_REVISION_SiS630A = 0x80, + SiS900_REVISION_SiS630E = 0x81, + SiS900_REVISION_SiS630S = 0x82, + SiS900_REVISION_SiS630EA1 = 0x83, + SiS900_REVISION_SiS630ET = 0x84, + SiS900_REVISION_SiS635A = 0x90, + SiS900_REVISION_SiS96x = 0x91, +}; + +#define DEVICE_NAME "sis900" +#define DEVICE_DRIVERNAME "net/" DEVICE_NAME + + +/***************************** Buffer & Buffer Descriptors *****************************/ + +struct buffer_desc { + uint32 link; + uint32 status; + uint32 buffer; +}; + +enum SiS900_buffer_desc_bits { + SiS900_DESCR_OWN = 0x80000000, + SiS900_DESCR_MORE = 0x40000000, + SiS900_DESCR_INTR = 0x20000000, + SiS900_DESCR_SUPPRESS_CRC = 0x10000000, + SiS900_DESCR_OK = 0x08000000, + + SiS900_DESCR_Tx_ABORT = 0x04000000, + SiS900_DESCR_Tx_UNDERRUN = 0x02000000, + SiS900_DESCR_Tx_CRS = 0x01000000, + SiS900_DESCR_Tx_DEFERRED = 0x00800000, + SiS900_DESCR_Tx_EXC_DEFERRED = 0x00400000, + SiS900_DESCR_Tx_OOW_COLLISION = 0x00200000, + SiS900_DESCR_Tx_EXC_COLLISIONS = 0x00100000, + + SiS900_DESCR_Rx_ABORT = 0x04000000, + SiS900_DESCR_Rx_OVERRUN = 0x02000000, + SiS900_DESCR_Rx_DESTCLASS0 = 0x01000000, + SiS900_DESCR_Rx_DESTCLASS1 = 0x00800000, + SiS900_DESCR_Rx_LONG_PACKET = 0x00400000, + SiS900_DESCR_Rx_RUNT_PACKET = 0x00200000, + SiS900_DESCR_Rx_INVALID_SYM = 0x00100000, + SiS900_DESCR_Rx_CRC_ERROR = 0x00080000, + SiS900_DESCR_Rx_FRAME_ALIGN = 0x00040000, + SiS900_DESCR_Rx_LOOPBACK = 0x00020000, + SiS900_DESCR_Rx_COLLISION = 0x00010000, +}; + +#define SiS900_DESCR_SIZE_MASK 0x0fff + +#define NUM_Rx_DESCR 32 +#define NUM_Tx_DESCR 16 + +#define NUM_Rx_MASK (NUM_Rx_DESCR - 1) +#define NUM_Tx_MASK (NUM_Tx_DESCR - 1) + + +#define MAX_FRAME_SIZE 1514 +#define BUFFER_SIZE 2048 +#define CRC_SIZE 4 + + +/***************************** Private Device Data *****************************/ + +struct sis_info { + timer timer; + uint32 cookieMagic; + area_id thisArea; + uint32 id; + spinlock lock; + pci_info *pciInfo; + volatile unsigned char *registers; + ether_address_t address; + volatile int32 blockFlag; + + struct mii_phy *firstPHY; + struct mii_phy *currentPHY; + uint16 phy; + bool autoNegotiationComplete; + bool link; + + // receive data + volatile struct buffer_desc rxDescriptor[NUM_Rx_DESCR]; + volatile char *rxBuffer[NUM_Rx_DESCR]; + area_id rxArea; + sem_id rxSem; + spinlock rxSpinlock; + int32 rxLock; + int16 rxCurrent; + int16 rxInterruptIndex; + int16 rxFree; + + // transmit data + volatile struct buffer_desc txDescriptor[NUM_Tx_DESCR]; + volatile char *txBuffer[NUM_Tx_DESCR]; + area_id txArea; + sem_id txSem; + spinlock txSpinlock; + int32 txLock; + int16 txCurrent; + int16 txInterruptIndex; + int16 txSent; +}; + +#define SiS_COOKIE_MAGIC 's900' +#define SiS_FREE_COOKIE_MAGIC 's9fr' + +enum link_modes { + LINK_HALF_DUPLEX = 0x0100, + LINK_FULL_DUPLEX = 0x0200, + LINK_DUPLEX_MASK = 0xff00, + + LINK_SPEED_HOME = 1, + LINK_SPEED_10_MBIT = 10, + LINK_SPEED_100_MBIT = 100, + LINK_SPEED_DEFAULT = LINK_SPEED_100_MBIT, + LINK_SPEED_MASK = 0x00ff +}; + +/***************************** Media Access Control *****************************/ + +enum SiS900_MAC_registers { + SiS900_MAC_COMMAND = 0x00, + SiS900_MAC_CONFIG = 0x04, + SiS900_MAC_EEPROM_ACCESS = 0x08, + SiS900_MAC_PCI_TEST = 0x0c, + SiS900_MAC_INTR_STATUS = 0x10, + SiS900_MAC_INTR_MASK = 0x14, + SiS900_MAC_INTR_ENABLE = 0x18, + SiS900_MAC_PHY_ACCESS = 0x1c, + SiS900_MAC_Tx_DESCR = 0x20, + SiS900_MAC_Tx_CONFIG = 0x24, + /* reserved 0x28 - 0x2c */ + SiS900_MAC_Rx_DESCR = 0x30, + SiS900_MAC_Rx_CONFIG = 0x34, + SiS900_MAC_FLOW_CONTROL = 0x38, + /* reserved 0x3c - 0x44 */ + SiS900_MAC_Rx_FILTER_CONTROL = 0x48, + SiS900_MAC_Rx_FILTER_DATA = 0x4c, + /* reserved 0x50 - 0xac */ + SiS900_MAC_WAKEUP_CONTROL = 0xb0, + SiS900_MAC_WAKEUP_EVENT = 0xb4 + /* reserved, wake-up registers */ +}; + +enum SiS900_MAC_commands { + SiS900_MAC_CMD_RESET = 0x0100, + SiS900_MAC_CMD_Rx_RESET = 0x0020, + SiS900_MAC_CMD_Tx_RESET = 0x0010, + SiS900_MAC_CMD_Rx_DISABLE = 0x0008, + SiS900_MAC_CMD_Rx_ENABLE = 0x0004, + SiS900_MAC_CMD_Tx_DISABLE = 0x0002, + SiS900_MAC_CMD_Tx_ENABLE = 0x0001 +}; + +enum SiS900_MAC_configuration_bits { + SiS900_MAC_CONFIG_BIG_ENDIAN = 0x00000001, + SiS900_MAC_CONFIG_EXCESSIVE_DEFERRAL = 0x00000010, + SiS900_MAC_CONFIG_EDB_MASTER = 0x00002000, +}; + +enum SiS900_MAC_rxfilter { + SiS900_RxF_ENABLE = 0x80000000, + SiS900_RxF_ACCEPT_ALL_BROADCAST = 0x40000000, + SiS900_RxF_ACCEPT_ALL_MULTICAST = 0x20000000, + SiS900_RxF_ACCEPT_ALL_ADDRESSES = 0x10000000 +}; + +#define SiS900_Rx_FILTER_ADDRESS_SHIFT 16 + +enum SiS900_MAC_txconfig { + SiS900_Tx_CS_IGNORE = 0x80000000, + SiS900_Tx_HB_IGNORE = 0x40000000, + SiS900_Tx_MAC_LOOPBACK = 0x20000000, + SiS900_Tx_AUTO_PADDING = 0x10000000 +}; + +#define SiS900_DMA_SHIFT 20 +#define SiS900_Tx_FILL_THRES (16 << 8) /* 1/4 of FIFO */ +#define SiS900_Tx_100_MBIT_DRAIN_THRES 48 /* 3/4 */ +#define SiS900_Tx_10_MBIT_DRAIN_THRES 16 // 32 /* 1/2 */ + +enum SiS900_MAC_rxconfig { + SiS900_Rx_ACCEPT_ERROR_PACKETS = 0x80000000, + SiS900_Rx_ACCEPT_RUNT_PACKETS = 0x40000000, + SiS900_Rx_ACCEPT_Tx_PACKETS = 0x10000000, + SiS900_Rx_ACCEPT_JABBER_PACKETS = 0x08000000 +}; + +#define SiS900_Rx_100_MBIT_DRAIN_THRES 32 /* 1/2 of FIFO */ +#define SiS900_Rx_10_MBIT_DRAIN_THRES 48 /* 3/4 */ + +enum SiS900_MAC_wakeup { + SiS900_WAKEUP_LINK_ON = 0x02, + SiS900_WAKEUP_LINK_LOSS = 0x01 +}; + +enum SiS900_MAC_interrupts { + SiS900_INTR_WAKEUP_EVENT = 0x10000000, + SiS900_INTR_Tx_RESET_COMPLETE = 0x02000000, + SiS900_INTR_Rx_RESET_COMPLETE = 0x01000000, + SiS900_INTR_Rx_STATUS_OVERRUN = 0x00010000, + SiS900_INTR_Tx_UNDERRUN = 0x00000400, + SiS900_INTR_Tx_IDLE = 0x00000200, + SiS900_INTR_Tx_ERROR = 0x00000100, + SiS900_INTR_Tx_DESCRIPTION = 0x00000080, + SiS900_INTR_Tx_OK = 0x00000040, + SiS900_INTR_Rx_OVERRUN = 0x00000020, + SiS900_INTR_Rx_IDLE = 0x00000010, + SiS900_INTR_Rx_EARLY_THRESHOLD = 0x00000008, + SiS900_INTR_Rx_ERROR = 0x00000004, + SiS900_INTR_Rx_DESCRIPTION = 0x00000002, + SiS900_INTR_Rx_OK = 0x00000001 +}; + +/***************************** EEPROM *****************************/ + +enum SiS900_EEPROM_bits { + SiS900_EEPROM_SELECT = 0x08, + SiS900_EEPROM_CLOCK = 0x04, + SiS900_EEPROM_DATA_OUT = 0x02, + SiS900_EEPROM_DATA_IN = 0x01 +}; + +// the EEPROM definitions are taken from linux' sis900 driver header +enum SiS900_EEPROM_address { + SiS900_EEPROM_SIGNATURE = 0x00, + SiS900_EEPROM_VENDOR_ID = 0x02, + SiS900_EEPROM_DEVICE_ID = 0x03, + SiS900_EEPROM_MAC_ADDRESS = 0x08, + SiS900_EEPROM_CHECKSUM = 0x0b +}; + +enum SiS900_EEPROM_commands { + SiS900_EEPROM_CMD_READ = 0x0180 +}; + +enum SiS96x_EEPROM_command { + SiS96x_EEPROM_CMD_REQ = 0x00000400, + SiS96x_EEPROM_CMD_DONE = 0x00000200, + SiS96x_EEPROM_CMD_GRANT = 0x00000100 +}; + +/***************************** Media Independent Interface *****************************/ + +struct mii_phy { + struct mii_phy *next; + uint16 id0,id1; + uint16 address; + uint8 types; +}; + +enum SiS900_MII_bits { +// SiS900_MII_WRITE = 0x00, +// SiS900_MII_READ = 0x20, +// SiS900_MII_ACCESS = 0x10 + SiS900_MII_MDC = 0x40, + SiS900_MII_MDDIR = 0x20, + SiS900_MII_MDIO = 0x10, +}; + +enum SiS900_MII_address { + // standard registers + MII_CONTROL = 0x00, + MII_STATUS = 0x01, + MII_PHY_ID0 = 0x02, + MII_PHY_ID1 = 0x03, + MII_AUTONEG_ADV = 0x04, + MII_AUTONEG_LINK_PARTNER = 0x05, + MII_AUTONEG_EXT = 0x06, + + // SiS900 specific registers + MII_CONFIG1 = 0x10, + MII_CONFIG2 = 0x11, + MII_LINK_STATUS = 0x12, + MII_MASK = 0x13, + MII_RESERVED = 0x14 +}; + +enum MII_control { + MII_CONTROL_RESET = 0x8000, + MII_CONTROL_RESET_AUTONEG = 0x0200, + MII_CONTROL_AUTO = 0x1000, + MII_CONTROL_FULL_DUPLEX = 0x0100, + MII_CONTROL_ISOLATE = 0x0400 +}; + +enum SiS900_MII_commands { + MII_CMD_READ = 0x6000, + MII_CMD_WRITE = 0x5002, + + MII_PHY_SHIFT = 7, + MII_REG_SHIFT = 2, +}; + +enum MII_status_bits { + MII_STATUS_EXT = 0x0001, + MII_STATUS_JAB = 0x0002, + MII_STATUS_LINK = 0x0004, + MII_STATUS_CAN_AUTO = 0x0008, + MII_STATUS_FAULT = 0x0010, + MII_STATUS_AUTO_DONE = 0x0020, + MII_STATUS_CAN_T = 0x0800, + MII_STATUS_CAN_T_FDX = 0x1000, + MII_STATUS_CAN_TX = 0x2000, + MII_STATUS_CAN_TX_FDX = 0x4000, + MII_STATUS_CAN_T4 = 0x8000 +}; + +enum MII_auto_negotiation { + // taken from the linux sis900.h header file + MII_NWAY_NODE_SEL = 0x001f, + MII_NWAY_CSMA_CD = 0x0001, + MII_NWAY_T = 0x0020, + MII_NWAY_T_FDX = 0x0040, + MII_NWAY_TX = 0x0080, + MII_NWAY_TX_FDX = 0x0100, + MII_NWAY_T4 = 0x0200, + MII_NWAY_PAUSE = 0x0400, + MII_NWAY_RF = 0x2000, + MII_NWAY_ACK = 0x4000, + MII_NWAY_NP = 0x8000 +}; + + +enum SiS900_MII_link_status { + MII_LINK_FAIL = 0x4000, + MII_LINK_100_MBIT = 0x0080, + MII_LINK_FULL_DUPLEX = 0x0040 +}; + +#define SiS900_MII_REGISTER_SHIFT 6 +#define SiS900_MII_DATA_SHIFT 16 + + +/***************************** Misc. & Prototypes *****************************/ + +#define HACK(x) ; + +#ifdef EXCESSIVE_DEBUG +# define TRACE(x) dprintf x + extern int bug(const char *, ...); +#elif defined(DEBUG) +# define TRACE(x) dprintf x +#else +# define TRACE(x) ; +#endif + +#define ROUND_TO_PAGE_SIZE(x) (((x) + (B_PAGE_SIZE) - 1) & ~((B_PAGE_SIZE) - 1)) + +// chip specific network functions +extern int32 sis900_interrupt(void *data); +extern void sis900_disableInterrupts(struct sis_info *info); +extern void sis900_enableInterrupts(struct sis_info *info); + +extern int32 sis900_timer(timer *t); +extern status_t sis900_initPHYs(struct sis_info *info); +extern void sis900_checkMode(struct sis_info *info); +extern bool sis900_getMACAddress(struct sis_info *info); +extern status_t sis900_reset(struct sis_info *info); + +extern void sis900_setPromiscuous(struct sis_info *info, bool on); +extern void sis900_setRxFilter(struct sis_info *info); + +extern void sis900_deleteRings(struct sis_info *info); +extern status_t sis900_createRings(struct sis_info *info); + + +#endif /* SIS900_H */