Initial revision

This commit is contained in:
Michael Brown
2005-03-08 18:53:11 +00:00
commit 3d6123e69a
373 changed files with 114041 additions and 0 deletions

58
src/drivers/disk/filo.c Normal file
View File

@@ -0,0 +1,58 @@
#include "etherboot.h"
#include "pci.h"
#include "disk.h"
/*
* UBL, The Universal Talkware Boot Loader
* Copyright (C) 2000 Universal Talkware Inc.
* Copyright (C) 2002 Eric Biederman
* Add to load filo
* By LYH yhlu@tyan.com
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*
*/
#ifdef CONFIG_PCI
extern int filo(void);
static int filo_pci_probe(struct dev *dev, struct pci_device *pci)
{
struct disk *disk = (struct disk *)dev;
filo();
/* past all of the drives */
dev->index = 0;
return 0;
}
#define PCI_DEVICE_ID_INTEL_82801CA_11 0x248b
static struct pci_id ide_controllers[] = {
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_11, "PIIX4" },
};
static struct pci_driver ide_driver __pci_driver = {
.type = DISK_DRIVER,
.name = "FILO",
.probe = filo_pci_probe,
.ids = ide_controllers,
.id_count = sizeof(ide_controllers)/sizeof(ide_controllers),
.class = PCI_CLASS_STORAGE_IDE,
};
#endif

88
src/drivers/disk/floppy.c Normal file
View File

@@ -0,0 +1,88 @@
#if (TRY_FLOPPY_FIRST > 0)
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2, or (at
* your option) any later version.
*/
#include "etherboot.h"
#define BOOTSECT ((char *)0x7C00)
#define BOOTSIG ((unsigned short *)BOOTSECT)[0xFF]
#define PARTTAB ((partentry *)(BOOTSECT+0x1BE))
typedef struct partentry {
unsigned char flags;
unsigned char start_head;
unsigned char start_sector;
unsigned char start_cylinder;
unsigned char type;
unsigned char end_head;
unsigned char end_sector;
unsigned char end_cylinder;
unsigned long offset;
unsigned long length;
} partentry;
static unsigned int disk_read_retry(int dev,int c,int h,int s)
{
int retry = 3,rc;
while (retry-- && (rc = pcbios_disk_read(dev,c,h,s,BOOTSECT)) != 0);
if (BOOTSIG != 0xAA55) {
printf("not a boot sector");
return(0xFFFF); }
return(rc);
}
int bootdisk(int dev,int part)
{
int rc;
disk_init();
if ((rc = disk_read_retry(dev,0,0,1)) != 0) {
readerr:
if (rc != 0xFFFF)
printf("read error (%#hhX)",rc/256);
return(0); }
if (part) {
partentry *ptr;
if (part >= 5) {
int i;
for (i = 0, ptr = PARTTAB; ptr->type != 5; ptr++)
if (++i == 4) {
printf("partition not found");
return(0); }
if ((rc = disk_read_retry(dev,
ptr->start_cylinder,
ptr->start_head,
ptr->start_sector)) != 0)
goto readerr;
part -= 4; }
if (!(ptr = PARTTAB-1+part)->type) {
printf("empty partition");
return(0); }
if ((rc = disk_read_retry(dev,
ptr->start_cylinder,
ptr->start_head,
ptr->start_sector)) != 0)
goto readerr; }
cleanup();
gateA20_unset();
/* Set %edx to device number to emulate BIOS
Fortunately %edx is not used after this */
__asm__("movl %0,%%edx" : : "g" (dev));
xstart((unsigned long)BOOTSECT, 0, 0);
return(0);
}
#endif
/*
* Local variables:
* c-basic-offset: 8
* End:
*/

893
src/drivers/disk/ide_disk.c Normal file
View File

@@ -0,0 +1,893 @@
#include "etherboot.h"
#include "timer.h"
#include "pci.h"
#include "isa.h"
#include "disk.h"
#define BSY_SET_DURING_SPINUP 1
/*
* UBL, The Universal Talkware Boot Loader
* Copyright (C) 2000 Universal Talkware Inc.
* Copyright (C) 2002 Eric Biederman
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*
*/
struct controller {
uint16_t cmd_base;
uint16_t ctrl_base;
};
struct harddisk_info {
struct controller *ctrl;
uint16_t heads;
uint16_t cylinders;
uint16_t sectors_per_track;
uint8_t model_number[41];
uint8_t slave;
sector_t sectors;
int address_mode; /* am i lba (0x40) or chs (0x00) */
#define ADDRESS_MODE_CHS 0
#define ADDRESS_MODE_LBA 1
#define ADDRESS_MODE_LBA48 2
int drive_exists;
int slave_absent;
int basedrive;
};
#define IDE_SECTOR_SIZE 0x200
#define IDE_BASE0 (0x1F0u) /* primary controller */
#define IDE_BASE1 (0x170u) /* secondary */
#define IDE_BASE2 (0x0F0u) /* third */
#define IDE_BASE3 (0x070u) /* fourth */
#define IDE_REG_EXTENDED_OFFSET (0x204u)
#define IDE_REG_DATA(base) ((ctrl)->cmd_base + 0u) /* word register */
#define IDE_REG_ERROR(base) ((ctrl)->cmd_base + 1u)
#define IDE_REG_PRECOMP(base) ((ctrl)->cmd_base + 1u)
#define IDE_REG_FEATURE(base) ((ctrl)->cmd_base + 1u)
#define IDE_REG_SECTOR_COUNT(base) ((ctrl)->cmd_base + 2u)
#define IDE_REG_SECTOR_NUMBER(base) ((ctrl)->cmd_base + 3u)
#define IDE_REG_LBA_LOW(base) ((ctrl)->cmd_base + 3u)
#define IDE_REG_CYLINDER_LSB(base) ((ctrl)->cmd_base + 4u)
#define IDE_REG_LBA_MID(base) ((ctrl)->cmd_base + 4u)
#define IDE_REG_CYLINDER_MSB(base) ((ctrl)->cmd_base + 5u)
#define IDE_REG_LBA_HIGH(base) ((ctrl)->cmd_base + 5u)
#define IDE_REG_DRIVEHEAD(base) ((ctrl)->cmd_base + 6u)
#define IDE_REG_DEVICE(base) ((ctrl)->cmd_base + 6u)
#define IDE_REG_STATUS(base) ((ctrl)->cmd_base + 7u)
#define IDE_REG_COMMAND(base) ((ctrl)->cmd_base + 7u)
#define IDE_REG_ALTSTATUS(base) ((ctrl)->ctrl_base + 2u)
#define IDE_REG_DEVICE_CONTROL(base) ((ctrl)->ctrl_base + 2u)
struct ide_pio_command
{
uint8_t feature;
uint8_t sector_count;
uint8_t lba_low;
uint8_t lba_mid;
uint8_t lba_high;
uint8_t device;
# define IDE_DH_DEFAULT (0xA0)
# define IDE_DH_HEAD(x) ((x) & 0x0F)
# define IDE_DH_MASTER (0x00)
# define IDE_DH_SLAVE (0x10)
# define IDE_DH_LBA (0x40)
# define IDE_DH_CHS (0x00)
uint8_t command;
uint8_t sector_count2;
uint8_t lba_low2;
uint8_t lba_mid2;
uint8_t lba_high2;
};
#define IDE_DEFAULT_COMMAND { 0xFFu, 0x01, 0x00, 0x0000, IDE_DH_DEFAULT }
#define IDE_ERR_ICRC 0x80 /* ATA Ultra DMA bad CRC */
#define IDE_ERR_BBK 0x80 /* ATA bad block */
#define IDE_ERR_UNC 0x40 /* ATA uncorrected error */
#define IDE_ERR_MC 0x20 /* ATA media change */
#define IDE_ERR_IDNF 0x10 /* ATA id not found */
#define IDE_ERR_MCR 0x08 /* ATA media change request */
#define IDE_ERR_ABRT 0x04 /* ATA command aborted */
#define IDE_ERR_NTK0 0x02 /* ATA track 0 not found */
#define IDE_ERR_NDAM 0x01 /* ATA address mark not found */
#define IDE_STATUS_BSY 0x80 /* busy */
#define IDE_STATUS_RDY 0x40 /* ready */
#define IDE_STATUS_DF 0x20 /* device fault */
#define IDE_STATUS_WFT 0x20 /* write fault (old name) */
#define IDE_STATUS_SKC 0x10 /* seek complete */
#define IDE_STATUS_DRQ 0x08 /* data request */
#define IDE_STATUS_CORR 0x04 /* corrected */
#define IDE_STATUS_IDX 0x02 /* index */
#define IDE_STATUS_ERR 0x01 /* error (ATA) */
#define IDE_STATUS_CHK 0x01 /* check (ATAPI) */
#define IDE_CTRL_HD15 0x08 /* bit should always be set to one */
#define IDE_CTRL_SRST 0x04 /* soft reset */
#define IDE_CTRL_NIEN 0x02 /* disable interrupts */
/* Most mandtory and optional ATA commands (from ATA-3), */
#define IDE_CMD_CFA_ERASE_SECTORS 0xC0
#define IDE_CMD_CFA_REQUEST_EXT_ERR_CODE 0x03
#define IDE_CMD_CFA_TRANSLATE_SECTOR 0x87
#define IDE_CMD_CFA_WRITE_MULTIPLE_WO_ERASE 0xCD
#define IDE_CMD_CFA_WRITE_SECTORS_WO_ERASE 0x38
#define IDE_CMD_CHECK_POWER_MODE1 0xE5
#define IDE_CMD_CHECK_POWER_MODE2 0x98
#define IDE_CMD_DEVICE_RESET 0x08
#define IDE_CMD_EXECUTE_DEVICE_DIAGNOSTIC 0x90
#define IDE_CMD_FLUSH_CACHE 0xE7
#define IDE_CMD_FORMAT_TRACK 0x50
#define IDE_CMD_IDENTIFY_DEVICE 0xEC
#define IDE_CMD_IDENTIFY_DEVICE_PACKET 0xA1
#define IDE_CMD_IDENTIFY_PACKET_DEVICE 0xA1
#define IDE_CMD_IDLE1 0xE3
#define IDE_CMD_IDLE2 0x97
#define IDE_CMD_IDLE_IMMEDIATE1 0xE1
#define IDE_CMD_IDLE_IMMEDIATE2 0x95
#define IDE_CMD_INITIALIZE_DRIVE_PARAMETERS 0x91
#define IDE_CMD_INITIALIZE_DEVICE_PARAMETERS 0x91
#define IDE_CMD_NOP 0x00
#define IDE_CMD_PACKET 0xA0
#define IDE_CMD_READ_BUFFER 0xE4
#define IDE_CMD_READ_DMA 0xC8
#define IDE_CMD_READ_DMA_QUEUED 0xC7
#define IDE_CMD_READ_MULTIPLE 0xC4
#define IDE_CMD_READ_SECTORS 0x20
#define IDE_CMD_READ_SECTORS_EXT 0x24
#define IDE_CMD_READ_VERIFY_SECTORS 0x40
#define IDE_CMD_RECALIBRATE 0x10
#define IDE_CMD_SEEK 0x70
#define IDE_CMD_SET_FEATURES 0xEF
#define IDE_CMD_SET_MAX_ADDR_EXT 0x24
#define IDE_CMD_SET_MULTIPLE_MODE 0xC6
#define IDE_CMD_SLEEP1 0xE6
#define IDE_CMD_SLEEP2 0x99
#define IDE_CMD_STANDBY1 0xE2
#define IDE_CMD_STANDBY2 0x96
#define IDE_CMD_STANDBY_IMMEDIATE1 0xE0
#define IDE_CMD_STANDBY_IMMEDIATE2 0x94
#define IDE_CMD_WRITE_BUFFER 0xE8
#define IDE_CMD_WRITE_DMA 0xCA
#define IDE_CMD_WRITE_DMA_QUEUED 0xCC
#define IDE_CMD_WRITE_MULTIPLE 0xC5
#define IDE_CMD_WRITE_SECTORS 0x30
#define IDE_CMD_WRITE_VERIFY 0x3C
/* IDE_CMD_SET_FEATURE sub commands */
#define IDE_FEATURE_CFA_ENABLE_8BIT_PIO 0x01
#define IDE_FEATURE_ENABLE_WRITE_CACHE 0x02
#define IDE_FEATURE_SET_TRANSFER_MODE 0x03
#define IDE_FEATURE_ENABLE_POWER_MANAGEMENT 0x05
#define IDE_FEATURE_ENABLE_POWERUP_IN_STANDBY 0x06
#define IDE_FEATURE_STANDBY_SPINUP_DRIVE 0x07
#define IDE_FEATURE_CFA_ENABLE_POWER_MODE1 0x0A
#define IDE_FEATURE_DISABLE_MEDIA_STATUS_NOTIFICATION 0x31
#define IDE_FEATURE_ENABLE_AUTOMATIC_ACOUSTIC_MANAGEMENT 0x42
#define IDE_FEATURE_SET_MAXIMUM_HOST_INTERFACE_SECTOR_TIMES 0x43
#define IDE_FEATURE_DISABLE_READ_LOOKAHEAD 0x55
#define IDE_FEATURE_ENABLE_RELEASE_INTERRUPT 0x5D
#define IDE_FEATURE_ENABLE_SERVICE_INTERRUPT 0x5E
#define IDE_FEATURE_DISABLE_REVERTING_TO_POWERON_DEFAULTS 0x66
#define IDE_FEATURE_CFA_DISABLE_8BIT_PIO 0x81
#define IDE_FEATURE_DISABLE_WRITE_CACHE 0x82
#define IDE_FEATURE_DISABLE_POWER_MANAGEMENT 0x85
#define IDE_FEATURE_DISABLE_POWERUP_IN_STANDBY 0x86
#define IDE_FEATURE_CFA_DISABLE_POWER_MODE1 0x8A
#define IDE_FEATURE_ENABLE_MEDIA_STATUS_NOTIFICATION 0x95
#define IDE_FEATURE_ENABLE_READ_LOOKAHEAD 0xAA
#define IDE_FEATURE_DISABLE_AUTOMATIC_ACOUSTIC_MANAGEMENT 0xC2
#define IDE_FEATURE_ENABLE_REVERTING_TO_POWERON_DEFAULTS 0xCC
#define IDE_FEATURE_DISABLE_SERVICE_INTERRUPT 0xDE
struct controller controller;
struct harddisk_info harddisk_info[2];
static int await_ide(int (*done)(struct controller *ctrl),
struct controller *ctrl, unsigned long timeout)
{
int result;
for(;;) {
result = done(ctrl);
if (result) {
return 0;
}
poll_interruptions();
if ((timeout == 0) || (currticks() > timeout)) {
break;
}
}
return -1;
}
/* The maximum time any IDE command can last 31 seconds,
* So if any IDE commands takes this long we know we have problems.
*/
#define IDE_TIMEOUT (32*TICKS_PER_SEC)
static int not_bsy(struct controller *ctrl)
{
return !(inb(IDE_REG_STATUS(ctrl)) & IDE_STATUS_BSY);
}
#if !BSY_SET_DURING_SPINUP
static int timeout(struct controller *ctrl)
{
return 0;
}
#endif
static int ide_software_reset(struct controller *ctrl)
{
/* Wait a little bit in case this is immediately after
* hardware reset.
*/
mdelay(2);
/* A software reset should not be delivered while the bsy bit
* is set. If the bsy bit does not clear in a reasonable
* amount of time give up.
*/
if (await_ide(not_bsy, ctrl, currticks() + IDE_TIMEOUT) < 0) {
return -1;
}
/* Disable Interrupts and reset the ide bus */
outb(IDE_CTRL_HD15 | IDE_CTRL_SRST | IDE_CTRL_NIEN,
IDE_REG_DEVICE_CONTROL(ctrl));
udelay(5);
outb(IDE_CTRL_HD15 | IDE_CTRL_NIEN, IDE_REG_DEVICE_CONTROL(ctrl));
mdelay(2);
if (await_ide(not_bsy, ctrl, currticks() + IDE_TIMEOUT) < 0) {
return -1;
}
return 0;
}
static void pio_set_registers(
struct controller *ctrl, const struct ide_pio_command *cmd)
{
uint8_t device;
/* Disable Interrupts */
outb(IDE_CTRL_HD15 | IDE_CTRL_NIEN, IDE_REG_DEVICE_CONTROL(ctrl));
/* Possibly switch selected device */
device = inb(IDE_REG_DEVICE(ctrl));
outb(cmd->device, IDE_REG_DEVICE(ctrl));
if ((device & (1UL << 4)) != (cmd->device & (1UL << 4))) {
/* Allow time for the selected drive to switch,
* The linux ide code suggests 50ms is the right
* amount of time to use here.
*/
mdelay(50);
}
outb(cmd->feature, IDE_REG_FEATURE(ctrl));
outb(cmd->sector_count2, IDE_REG_SECTOR_COUNT(ctrl));
outb(cmd->sector_count, IDE_REG_SECTOR_COUNT(ctrl));
outb(cmd->lba_low2, IDE_REG_LBA_LOW(ctrl));
outb(cmd->lba_low, IDE_REG_LBA_LOW(ctrl));
outb(cmd->lba_mid2, IDE_REG_LBA_MID(ctrl));
outb(cmd->lba_mid, IDE_REG_LBA_MID(ctrl));
outb(cmd->lba_high2, IDE_REG_LBA_HIGH(ctrl));
outb(cmd->lba_high, IDE_REG_LBA_HIGH(ctrl));
outb(cmd->command, IDE_REG_COMMAND(ctrl));
}
static int pio_non_data(struct controller *ctrl, const struct ide_pio_command *cmd)
{
/* Wait until the busy bit is clear */
if (await_ide(not_bsy, ctrl, currticks() + IDE_TIMEOUT) < 0) {
return -1;
}
pio_set_registers(ctrl, cmd);
if (await_ide(not_bsy, ctrl, currticks() + IDE_TIMEOUT) < 0) {
return -1;
}
/* FIXME is there more error checking I could do here? */
return 0;
}
static int pio_data_in(struct controller *ctrl, const struct ide_pio_command *cmd,
void *buffer, size_t bytes)
{
unsigned int status;
/* FIXME handle commands with multiple blocks */
/* Wait until the busy bit is clear */
if (await_ide(not_bsy, ctrl, currticks() + IDE_TIMEOUT) < 0) {
return -1;
}
/* How do I tell if INTRQ is asserted? */
pio_set_registers(ctrl, cmd);
if (await_ide(not_bsy, ctrl, currticks() + IDE_TIMEOUT) < 0) {
return -1;
}
status = inb(IDE_REG_STATUS(ctrl));
if (!(status & IDE_STATUS_DRQ)) {
return -1;
}
insw(IDE_REG_DATA(ctrl), buffer, bytes/2);
status = inb(IDE_REG_STATUS(ctrl));
if (status & IDE_STATUS_DRQ) {
return -1;
}
return 0;
}
#if 0
static int pio_packet(struct controller *ctrl, int in,
const void *packet, int packet_len,
void *buffer, int buffer_len)
{
const uint8_t *pbuf;
unsigned int status;
struct ide_pio_command cmd;
memset(&cmd, 0, sizeof(cmd));
/* Wait until the busy bit is clear */
if (await_ide(not_bsy, ctrl, currticks() + IDE_TIMEOUT) < 0) {
return -1;
}
pio_set_registers(ctrl, cmd);
ndelay(400);
if (await_ide(not_bsy, ctrl, currticks() + IDE_TIMEOUT) < 0) {
return -1;
}
status = inb(IDE_REG_STATUS(ctrl));
if (!(status & IDE_STATUS_DRQ)) {
return -1;
}
while(packet_len > 1) {
outb(*pbuf, IDE_REG_DATA(ctrl));
pbuf++;
packet_len -= 1;
}
inb(IDE_REG_ALTSTATUS(ctrl));
if (await_ide){}
/*FIXME finish this function */
}
#endif
static inline int ide_read_sector_chs(
struct harddisk_info *info, void *buffer, unsigned long sector)
{
struct ide_pio_command cmd;
unsigned int track;
unsigned int offset;
unsigned int cylinder;
memset(&cmd, 0, sizeof(cmd));
cmd.sector_count = 1;
track = sector / info->sectors_per_track;
/* Sector number */
offset = 1 + (sector % info->sectors_per_track);
cylinder = track / info->heads;
cmd.lba_low = offset;
cmd.lba_mid = cylinder & 0xff;
cmd.lba_high = (cylinder >> 8) & 0xff;
cmd.device = IDE_DH_DEFAULT |
IDE_DH_HEAD(track % info->heads) |
info->slave |
IDE_DH_CHS;
cmd.command = IDE_CMD_READ_SECTORS;
return pio_data_in(info->ctrl, &cmd, buffer, IDE_SECTOR_SIZE);
}
static inline int ide_read_sector_lba(
struct harddisk_info *info, void *buffer, unsigned long sector)
{
struct ide_pio_command cmd;
memset(&cmd, 0, sizeof(cmd));
cmd.sector_count = 1;
cmd.lba_low = sector & 0xff;
cmd.lba_mid = (sector >> 8) & 0xff;
cmd.lba_high = (sector >> 16) & 0xff;
cmd.device = IDE_DH_DEFAULT |
((sector >> 24) & 0x0f) |
info->slave |
IDE_DH_LBA;
cmd.command = IDE_CMD_READ_SECTORS;
return pio_data_in(info->ctrl, &cmd, buffer, IDE_SECTOR_SIZE);
}
static inline int ide_read_sector_lba48(
struct harddisk_info *info, void *buffer, sector_t sector)
{
struct ide_pio_command cmd;
memset(&cmd, 0, sizeof(cmd));
cmd.sector_count = 1;
cmd.lba_low = sector & 0xff;
cmd.lba_mid = (sector >> 8) & 0xff;
cmd.lba_high = (sector >> 16) & 0xff;
cmd.lba_low2 = (sector >> 24) & 0xff;
cmd.lba_mid2 = (sector >> 32) & 0xff;
cmd.lba_high2 = (sector >> 40) & 0xff;
cmd.device = info->slave | IDE_DH_LBA;
cmd.command = IDE_CMD_READ_SECTORS_EXT;
return pio_data_in(info->ctrl, &cmd, buffer, IDE_SECTOR_SIZE);
}
static int ide_read(struct disk *disk, sector_t sector)
{
struct harddisk_info *info = disk->priv;
int result;
/* Report the buffer is empty */
disk->sector = 0;
disk->bytes = 0;
if (sector > info->sectors) {
return -1;
}
if (info->address_mode == ADDRESS_MODE_CHS) {
result = ide_read_sector_chs(info, disk->buffer, sector);
}
else if (info->address_mode == ADDRESS_MODE_LBA) {
result = ide_read_sector_lba(info, disk->buffer, sector);
}
else if (info->address_mode == ADDRESS_MODE_LBA48) {
result = ide_read_sector_lba48(info, disk->buffer, sector);
}
else {
result = -1;
}
/* On success report the buffer has data */
if (result != -1) {
disk->bytes = IDE_SECTOR_SIZE;
disk->sector = sector;
}
return result;
}
static int init_drive(struct harddisk_info *info, struct controller *ctrl, int slave,
int basedrive, unsigned char *buffer)
{
uint16_t* drive_info;
struct ide_pio_command cmd;
int i;
info->ctrl = ctrl;
info->heads = 0u;
info->cylinders = 0u;
info->sectors_per_track = 0u;
info->address_mode = IDE_DH_CHS;
info->sectors = 0ul;
info->drive_exists = 0;
info->slave_absent = 0;
info->slave = slave?IDE_DH_SLAVE: IDE_DH_MASTER;
info->basedrive = basedrive;
#if 0
printf("Testing for disk %d\n", info->basedrive);
#endif
/* Select the drive that we are testing */
outb(IDE_DH_DEFAULT | IDE_DH_HEAD(0) | IDE_DH_CHS | info->slave,
IDE_REG_DEVICE(ctrl));
mdelay(50);
/* Test to see if the drive registers exist,
* In many cases this quickly rules out a missing drive.
*/
for(i = 0; i < 4; i++) {
outb(0xaa + i, (ctrl->cmd_base) + 2 + i);
}
for(i = 0; i < 4; i++) {
if (inb((ctrl->cmd_base) + 2 + i) != 0xaa + i) {
return 1;
}
}
for(i = 0; i < 4; i++) {
outb(0x55 + i, (ctrl->cmd_base) + 2 + i);
}
for(i = 0; i < 4; i++) {
if (inb((ctrl->cmd_base) + 2 + i) != 0x55 + i) {
return 1;
}
}
#if 0
printf("Probing for disk %d\n", info->basedrive);
#endif
memset(&cmd, 0, sizeof(cmd));
cmd.device = IDE_DH_DEFAULT | IDE_DH_HEAD(0) | IDE_DH_CHS | info->slave;
cmd.command = IDE_CMD_IDENTIFY_DEVICE;
if (pio_data_in(ctrl, &cmd, buffer, IDE_SECTOR_SIZE) < 0) {
/* Well, if that command didn't work, we probably don't have drive. */
return 1;
}
/* Now suck the data out */
drive_info = (uint16_t *)buffer;
if (drive_info[2] == 0x37C8) {
/* If the response is incomplete spin up the drive... */
memset(&cmd, 0, sizeof(cmd));
cmd.device = IDE_DH_DEFAULT | IDE_DH_HEAD(0) | IDE_DH_CHS |
info->slave;
cmd.feature = IDE_FEATURE_STANDBY_SPINUP_DRIVE;
if (pio_non_data(ctrl, &cmd) < 0) {
/* If the command doesn't work give up on the drive */
return 1;
}
}
if ((drive_info[2] == 0x37C8) || (drive_info[2] == 0x8C73)) {
/* The response is incomplete retry the drive info command */
memset(&cmd, 0, sizeof(cmd));
cmd.device = IDE_DH_DEFAULT | IDE_DH_HEAD(0) | IDE_DH_CHS |
info->slave;
cmd.command = IDE_CMD_IDENTIFY_DEVICE;
if(pio_data_in(ctrl, &cmd, buffer, IDE_SECTOR_SIZE) < 0) {
/* If the command didn't work give up on the drive. */
return 1;
}
}
if ((drive_info[2] != 0x37C8) &&
(drive_info[2] != 0x738C) &&
(drive_info[2] != 0x8C73) &&
(drive_info[2] != 0xC837) &&
(drive_info[2] != 0x0000)) {
printf("Invalid IDE Configuration: %hx\n", drive_info[2]);
return 1;
}
for(i = 27; i < 47; i++) {
info->model_number[((i-27)<< 1)] = (drive_info[i] >> 8) & 0xff;
info->model_number[((i-27)<< 1)+1] = drive_info[i] & 0xff;
}
info->model_number[40] = '\0';
info->drive_exists = 1;
/* See if LBA is supported */
if (drive_info[49] & (1 << 9)) {
info->address_mode = ADDRESS_MODE_LBA;
info->sectors = (drive_info[61] << 16) | (drive_info[60]);
/* Enable LBA48 mode if it is present */
if (drive_info[83] & (1 <<10)) {
/* Should LBA48 depend on LBA? */
printf("LBA48 mode\n");
info->address_mode = ADDRESS_MODE_LBA48;
info->sectors =
(((sector_t)drive_info[103]) << 48) |
(((sector_t)drive_info[102]) << 32) |
(((sector_t)drive_info[101]) << 16) |
(((sector_t)drive_info[100]) << 0);
}
} else {
info->address_mode = ADDRESS_MODE_CHS;
info->heads = drive_info[3];
info->cylinders = drive_info[1];
info->sectors_per_track = drive_info[6];
info->sectors =
info->sectors_per_track *
info->heads *
info->cylinders;
printf( "%s sectors_per_track=[%d], heads=[%d], cylinders=[%d]\n",
__FUNCTION__,
info->sectors_per_track,
info->heads,
info->cylinders);
}
/* See if we have a slave */
if (!info->slave && (((drive_info[93] >> 14) & 3) == 1)) {
info->slave_absent = !(drive_info[93] & (1 << 5));
}
/* See if we need to put the device in CFA power mode 1 */
if ((drive_info[160] & ((1 << 15) | (1 << 13)| (1 << 12))) ==
((1 << 15) | (1 << 13)| (1 << 12))) {
memset(&cmd, 0, sizeof(cmd));
cmd.device = IDE_DH_DEFAULT | IDE_DH_HEAD(0) | IDE_DH_CHS | info->slave;
cmd.feature = IDE_FEATURE_CFA_ENABLE_POWER_MODE1;
if (pio_non_data(ctrl, &cmd) < 0) {
/* If I need to power up the drive, and I can't
* give up.
*/
printf("Cannot power up CFA device\n");
return 1;
}
}
printf("disk%d %dk cap: %hx\n",
info->basedrive,
(unsigned long)(info->sectors >> 1),
drive_info[49]);
return 0;
}
static int init_controller(struct controller *ctrl, int basedrive, unsigned char *buffer)
{
struct harddisk_info *info;
/* Intialize the harddisk_info structures */
memset(harddisk_info, 0, sizeof(harddisk_info));
/* Put the drives ide channel in a know state and wait
* for the drives to spinup.
*
* In practice IDE disks tend not to respond to commands until
* they have spun up. This makes IDE hard to deal with
* immediately after power up, as the delays can be quite
* long, so we must be very careful here.
*
* There are two pathological cases that must be dealt with:
*
* - The BSY bit not being set while the IDE drives spin up.
* In this cases only a hard coded delay will work. As
* I have not reproduced it, and this is out of spec for
* IDE drives the work around can be enabled by setting
* BSY_SET_DURING_SPINUP to 0.
*
* - The BSY bit floats high when no drives are plugged in.
* This case will not be detected except by timing out but
* we avoid the problems by only probing devices we are
* supposed to boot from. If we don't do the probe we
* will not experience the problem.
*
* So speed wise I am only slow if the BSY bit is not set
* or not reported by the IDE controller during spinup, which
* is quite rare.
*
*/
#if !BSY_SET_DURING_SPINUP
if (await_ide(timeout, ctrl, IDE_TIMEOUT) < 0) {
return -1;
}
#endif
if (ide_software_reset(ctrl) < 0) {
return -1;
}
/* Note: I have just done a software reset. It may be
* reasonable to just read the boot time signatures
* off of the drives to see if they are present.
*
* For now I will go with just sending commands to the drives
* and assuming filtering out missing drives by detecting registers
* that won't set and commands that fail to execute properly.
*/
/* Now initialize the individual drives */
info = &harddisk_info[0];
init_drive(info, ctrl, 0, basedrive, buffer);
if (info->drive_exists && !info->slave_absent) {
basedrive++;
info++;
init_drive(info, ctrl, 1, basedrive, buffer);
}
return 0;
}
static void ide_disable(struct dev *dev)
{
struct disk *disk = (struct disk *)dev;
struct harddisk_info *info = disk->priv;
ide_software_reset(info->ctrl);
}
#ifdef CONFIG_PCI
static int ide_pci_probe(struct dev *dev, struct pci_device *pci)
{
struct disk *disk = (struct disk *)dev;
struct harddisk_info *info;
int index;
adjust_pci_device(pci);
index = dev->index + 1;
if (dev->how_probe == PROBE_NEXT) {
index++;
}
for(; index < 4; index++) {
unsigned mask;
mask = (index < 2)? (1 << 0) : (1 << 2);
if ((pci->class & mask) == 0) {
/* IDE special pci mode */
uint16_t base;
base = (index < 2)?IDE_BASE0:IDE_BASE1;
controller.cmd_base = base;
controller.ctrl_base = base + IDE_REG_EXTENDED_OFFSET;
} else {
/* IDE normal pci mode */
unsigned cmd_reg, ctrl_reg;
uint32_t cmd_base, ctrl_base;
if (index < 2) {
cmd_reg = PCI_BASE_ADDRESS_0;
ctrl_reg = PCI_BASE_ADDRESS_1;
} else {
cmd_reg = PCI_BASE_ADDRESS_2;
ctrl_reg = PCI_BASE_ADDRESS_3;
}
pcibios_read_config_dword(pci->bus, pci->devfn, cmd_reg, &cmd_base);
pcibios_read_config_dword(pci->bus, pci->devfn, ctrl_reg, &ctrl_base);
controller.cmd_base = cmd_base & ~3;
controller.ctrl_base = ctrl_base & ~3;
}
if (((index & 1) == 0) || (dev->how_probe == PROBE_AWAKE)) {
if (init_controller(&controller, disk->drive, disk->buffer) < 0) {
/* nothing behind the controller */
continue;
}
}
info = &harddisk_info[index & 1];
if (!info->drive_exists) {
/* unknown drive */
continue;
}
disk->hw_sector_size = IDE_SECTOR_SIZE;
disk->sectors_per_read = 1;
disk->sectors = info->sectors;
dev->index = index;
dev->disable = ide_disable;
disk->read = ide_read;
disk->priv = info;
return 1;
}
/* past all of the drives */
dev->index = 0;
return 0;
}
#define PCI_DEVICE_ID_INTEL_82801CA_11 0x248b
static struct pci_id ide_controllers[] = {
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_11, "PIIX4" },
#if 0 /* Currently I don't need any entries in this table so ignore it */
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371FB_0, "PIIX" },
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371FB_1, "PIIX" },
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371MX, "MPIIX" },
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371SB_1, "PIIX3" },
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB, "PIIX4" },
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AB_1, "PIIX4" },
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443MX_1, "PIIX4" },
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AA_1, "PIIX4" },
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82372FB_1, "PIIX4" },
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82451NX, "PIIX4" },
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_9, "PIIX4" },
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_8, "PIIX4" },
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_10, "PIIX4" },
{ PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C561, "VIA_IDE" },
{ PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C576_1, "VP_IDE" },
{ PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_1, "VP_IDE" },
{ PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20246, "PDC20246" },
{ PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20262, "PDC20262" },
{ PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20265, "PDC20265" },
{ PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20267, "PDC20267" },
{ PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20268, "PDC20268" },
{ PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20268R, "PDC20268" },
{ PCI_VENDOR_ID_PCTECH, PCI_DEVICE_ID_PCTECH_RZ1000, "RZ1000" },
{ PCI_VENDOR_ID_PCTECH, PCI_DEVICE_ID_PCTECH_RZ1001, "RZ1001" },
{ PCI_VENDOR_ID_PCTECH, PCI_DEVICE_ID_PCTECH_SAMURAI_IDE, "SAMURAI" },
{ PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_CMD_640, "CMD640" },
{ PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_CMD_643, "CMD646" },
{ PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_CMD_646, "CMD648" },
{ PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_CMD_648, "CMD643" },
{ PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_CMD_649, "CMD649" },
{ PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_5513, "SIS5513" },
{ PCI_VENDOR_ID_OPTI, PCI_DEVICE_ID_OPTI_82C621, "OPTI621" },
{ PCI_VENDOR_ID_OPTI, PCI_DEVICE_ID_OPTI_82C558, "OPTI621V" },
{ PCI_VENDOR_ID_OPTI, PCI_DEVICE_ID_OPTI_82C825, "OPTI621X" },
{ PCI_VENDOR_ID_TEKRAM, PCI_DEVICE_ID_TEKRAM_DC290, "TRM290" },
{ PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_87410, "NS87410" },
{ PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_87415, "NS87415" },
{ PCI_VENDOR_ID_HOLTEK2, PCI_DEVICE_ID_HOLTEK2_6565, "HT6565" },
{ PCI_VENDOR_ID_ARTOP, PCI_DEVICE_ID_ARTOP_ATP850UF, "AEC6210" },
{ PCI_VENDOR_ID_ARTOP, PCI_DEVICE_ID_ARTOP_ATP860, "AEC6260" },
{ PCI_VENDOR_ID_ARTOP, PCI_DEVICE_ID_ARTOP_ATP860R, "AEC6260R" },
{ PCI_VENDOR_ID_WINBOND, PCI_DEVICE_ID_WINBOND_82C105, "W82C105" },
{ PCI_VENDOR_ID_UMC, PCI_DEVICE_ID_UMC_UM8673F, "UM8673F" },
{ PCI_VENDOR_ID_UMC, PCI_DEVICE_ID_UMC_UM8886A, "UM8886A" },
{ PCI_VENDOR_ID_UMC, PCI_DEVICE_ID_UMC_UM8886BF, "UM8886BF" },
{ PCI_VENDOR_ID_TTI, PCI_DEVICE_ID_TTI_HPT343, "HPT34X" },
{ PCI_VENDOR_ID_TTI, PCI_DEVICE_ID_TTI_HPT366, "HPT366" },
{ PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M5229, "ALI15X3" },
{ PCI_VENDOR_ID_CONTAQ, PCI_DEVICE_ID_CONTAQ_82C693, "CY82C693" },
{ 0x3388, 0x8013, "HINT_IDE" },
{ PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5530_IDE, "CS5530" },
{ PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_COBRA_7401, "AMD7401" },
{ PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7409, "AMD7409" },
{ PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7411, "AMD7411" },
{ PCI_VENDOR_ID_PDC, PCI_DEVICE_ID_PDC_1841, "PDCADMA" },
{ PCI_VENDOR_ID_EFAR, PCI_DEVICE_ID_EFAR_SLC90E66_1, "SLC90E66" },
{ PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_OSB4IDE, "OSB4" },
{ PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB5IDE, "OSB5" },
{ PCI_VENDOR_ID_ITE, PCI_DEVICE_ID_ITE_IT8172G, "ITE8172G" },
#endif
};
static struct pci_driver ide_driver __pci_driver = {
.type = DISK_DRIVER,
.name = "IDE",
.probe = ide_pci_probe,
.ids = ide_controllers,
.id_count = sizeof(ide_controllers)/sizeof(ide_controllers),
.class = PCI_CLASS_STORAGE_IDE,
};
#endif
/* The isa driver works but it causes disks to show up twice.
* comment it out for now.
*/
#if 0 && defined(CONFIG_ISA)
static int ide_isa_probe(struct dev * dev, unsigned short *probe_addrs)
{
struct disk *disk = (struct disk *)dev;
int index;
unsigned short addr;
struct harddisk_info *info;
index = dev->index +1;
if (dev->how_probe == PROBE_AWAKE) {
index--;
}
for(; (index >= 0) && (addr = probe_addrs[index >> 1]); index += 2) {
if ((index & 1) == 0) {
controller.cmd_base = addr;
controller.ctrl_base = addr + IDE_REG_EXTENDED_OFFSET;
if (init_controller(&controller, disk->drive, disk->buffer) < 0) {
/* nothing behind the controller */
continue;
}
}
info = &harddisk_info[index & 1];
if (!info->drive_exists) {
/* unknown drive */
return 0;
}
disk->sectors_per_read = 1;
disk->sectors = info->sectors;
dev->index = index;
dev->disable = ide_disable;
disk->read = ide_read;
disk->priv = info;
return 1;
}
/* past all of the drives */
dev->index = -1;
return 0;
}
static unsigned short ide_base[] = {
IDE_BASE0,
IDE_BASE1,
IDE_BASE2,
IDE_BASE3,
0
};
static struct isa_driver ide_isa_driver __isa_driver = {
.type = DISK_DRIVER,
.name = "IDE/ISA",
.probe = ide_isa_probe,
.ioaddrs = ide_base,
};
#endif

1151
src/drivers/disk/pc_floppy.c Normal file

File diff suppressed because it is too large Load Diff

671
src/drivers/net/3c509.c Normal file
View File

@@ -0,0 +1,671 @@
/**************************************************************************
ETHERBOOT - BOOTP/TFTP Bootstrap Program
Author: Martin Renters.
Date: Mar 22 1995
This code is based heavily on David Greenman's if_ed.c driver and
Andres Vega Garcia's if_ep.c driver.
Copyright (C) 1993-1994, David Greenman, Martin Renters.
Copyright (C) 1993-1995, Andres Vega Garcia.
Copyright (C) 1995, Serge Babkin.
This software may be used, modified, copied, distributed, and sold, in
both source and binary form provided that the above copyright and these
terms are retained. Under no circumstances are the authors responsible for
the proper functioning of this software, nor do the authors assume any
responsibility for damages incurred with its use.
3c509 support added by Serge Babkin (babkin@hq.icb.chel.su)
$Id$
***************************************************************************/
/* #define EDEBUG */
#include "etherboot.h"
#include "nic.h"
#include "isa.h"
#include "timer.h"
#include "3c509.h"
static unsigned short eth_nic_base;
static enum { none, bnc, utp } connector = none; /* for 3C509 */
#ifdef INCLUDE_3C529
/*
* This table and several other pieces of the MCA support
* code were shamelessly borrowed from the Linux kernel source.
*
* MCA support added by Adam Fritzler (mid@auk.cx)
*
*/
struct el3_mca_adapters_struct {
const char *name;
int id;
};
static struct el3_mca_adapters_struct el3_mca_adapters[] = {
{ "3Com 3c529 EtherLink III (10base2)", 0x627c },
{ "3Com 3c529 EtherLink III (10baseT)", 0x627d },
{ "3Com 3c529 EtherLink III (test mode)", 0x62db },
{ "3Com 3c529 EtherLink III (TP or coax)", 0x62f6 },
{ "3Com 3c529 EtherLink III (TP)", 0x62f7 },
{ NULL, 0 },
};
#endif
/**************************************************************************
ETH_RESET - Reset adapter
***************************************************************************/
static void t509_reset(struct nic *nic)
{
int i;
/***********************************************************
Reset 3Com 509 card
*************************************************************/
/* stop card */
outw(RX_DISABLE, BASE + EP_COMMAND);
outw(RX_DISCARD_TOP_PACK, BASE + EP_COMMAND);
while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS)
;
outw(TX_DISABLE, BASE + EP_COMMAND);
outw(STOP_TRANSCEIVER, BASE + EP_COMMAND);
udelay(1000);
outw(RX_RESET, BASE + EP_COMMAND);
outw(TX_RESET, BASE + EP_COMMAND);
outw(C_INTR_LATCH, BASE + EP_COMMAND);
outw(SET_RD_0_MASK, BASE + EP_COMMAND);
outw(SET_INTR_MASK, BASE + EP_COMMAND);
outw(SET_RX_FILTER, BASE + EP_COMMAND);
/*
* initialize card
*/
while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS)
;
GO_WINDOW(0);
/* Disable the card */
outw(0, BASE + EP_W0_CONFIG_CTRL);
/* Configure IRQ to none */
outw(SET_IRQ(0), BASE + EP_W0_RESOURCE_CFG);
/* Enable the card */
outw(ENABLE_DRQ_IRQ, BASE + EP_W0_CONFIG_CTRL);
GO_WINDOW(2);
/* Reload the ether_addr. */
for (i = 0; i < ETH_ALEN; i++)
outb(nic->node_addr[i], BASE + EP_W2_ADDR_0 + i);
outw(RX_RESET, BASE + EP_COMMAND);
outw(TX_RESET, BASE + EP_COMMAND);
/* Window 1 is operating window */
GO_WINDOW(1);
for (i = 0; i < 31; i++)
inb(BASE + EP_W1_TX_STATUS);
/* get rid of stray intr's */
outw(ACK_INTR | 0xff, BASE + EP_COMMAND);
outw(SET_RD_0_MASK | S_5_INTS, BASE + EP_COMMAND);
outw(SET_INTR_MASK, BASE + EP_COMMAND);
outw(SET_RX_FILTER | FIL_GROUP | FIL_INDIVIDUAL | FIL_BRDCST, BASE + EP_COMMAND);
/* configure BNC */
if (connector == bnc) {
outw(START_TRANSCEIVER, BASE + EP_COMMAND);
udelay(1000);
}
/* configure UTP */
else if (connector == utp) {
GO_WINDOW(4);
outw(ENABLE_UTP, BASE + EP_W4_MEDIA_TYPE);
sleep(2); /* Give time for media to negotiate */
GO_WINDOW(1);
}
/* start transceiver and receiver */
outw(RX_ENABLE, BASE + EP_COMMAND);
outw(TX_ENABLE, BASE + EP_COMMAND);
/* set early threshold for minimal packet length */
outw(SET_RX_EARLY_THRESH | ETH_ZLEN, BASE + EP_COMMAND);
outw(SET_TX_START_THRESH | 16, BASE + EP_COMMAND);
}
/**************************************************************************
ETH_TRANSMIT - Transmit a frame
***************************************************************************/
static char padmap[] = {
0, 3, 2, 1};
static void t509_transmit(
struct nic *nic,
const char *d, /* Destination */
unsigned int t, /* Type */
unsigned int s, /* size */
const char *p) /* Packet */
{
register unsigned int len;
int pad;
int status;
#ifdef EDEBUG
printf("{l=%d,t=%hX}",s+ETH_HLEN,t);
#endif
/* swap bytes of type */
t= htons(t);
len=s+ETH_HLEN; /* actual length of packet */
pad = padmap[len & 3];
/*
* The 3c509 automatically pads short packets to minimum ethernet length,
* but we drop packets that are too large. Perhaps we should truncate
* them instead?
*/
if (len + pad > ETH_FRAME_LEN) {
return;
}
/* drop acknowledgements */
while ((status=inb(BASE + EP_W1_TX_STATUS)) & TXS_COMPLETE ) {
if (status & (TXS_UNDERRUN|TXS_MAX_COLLISION|TXS_STATUS_OVERFLOW)) {
outw(TX_RESET, BASE + EP_COMMAND);
outw(TX_ENABLE, BASE + EP_COMMAND);
}
outb(0x0, BASE + EP_W1_TX_STATUS);
}
while (inw(BASE + EP_W1_FREE_TX) < (unsigned short)len + pad + 4)
; /* no room in FIFO */
outw(len, BASE + EP_W1_TX_PIO_WR_1);
outw(0x0, BASE + EP_W1_TX_PIO_WR_1); /* Second dword meaningless */
/* write packet */
outsw(BASE + EP_W1_TX_PIO_WR_1, d, ETH_ALEN/2);
outsw(BASE + EP_W1_TX_PIO_WR_1, nic->node_addr, ETH_ALEN/2);
outw(t, BASE + EP_W1_TX_PIO_WR_1);
outsw(BASE + EP_W1_TX_PIO_WR_1, p, s / 2);
if (s & 1)
outb(*(p+s - 1), BASE + EP_W1_TX_PIO_WR_1);
while (pad--)
outb(0, BASE + EP_W1_TX_PIO_WR_1); /* Padding */
/* wait for Tx complete */
while((inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS) != 0)
;
}
/**************************************************************************
ETH_POLL - Wait for a frame
***************************************************************************/
static int t509_poll(struct nic *nic, int retrieve)
{
/* common variables */
/* variables for 3C509 */
short status, cst;
register short rx_fifo;
cst=inw(BASE + EP_STATUS);
#ifdef EDEBUG
if(cst & 0x1FFF)
printf("-%hX-",cst);
#endif
if( (cst & S_RX_COMPLETE)==0 ) {
/* acknowledge everything */
outw(ACK_INTR| (cst & S_5_INTS), BASE + EP_COMMAND);
outw(C_INTR_LATCH, BASE + EP_COMMAND);
return 0;
}
status = inw(BASE + EP_W1_RX_STATUS);
#ifdef EDEBUG
printf("*%hX*",status);
#endif
if (status & ERR_RX) {
outw(RX_DISCARD_TOP_PACK, BASE + EP_COMMAND);
return 0;
}
rx_fifo = status & RX_BYTES_MASK;
if (rx_fifo==0)
return 0;
if ( ! retrieve ) return 1;
/* read packet */
#ifdef EDEBUG
printf("[l=%d",rx_fifo);
#endif
insw(BASE + EP_W1_RX_PIO_RD_1, nic->packet, rx_fifo / 2);
if(rx_fifo & 1)
nic->packet[rx_fifo-1]=inb(BASE + EP_W1_RX_PIO_RD_1);
nic->packetlen=rx_fifo;
while(1) {
status = inw(BASE + EP_W1_RX_STATUS);
#ifdef EDEBUG
printf("*%hX*",status);
#endif
rx_fifo = status & RX_BYTES_MASK;
if(rx_fifo>0) {
insw(BASE + EP_W1_RX_PIO_RD_1, nic->packet+nic->packetlen, rx_fifo / 2);
if(rx_fifo & 1)
nic->packet[nic->packetlen+rx_fifo-1]=inb(BASE + EP_W1_RX_PIO_RD_1);
nic->packetlen+=rx_fifo;
#ifdef EDEBUG
printf("+%d",rx_fifo);
#endif
}
if(( status & RX_INCOMPLETE )==0) {
#ifdef EDEBUG
printf("=%d",nic->packetlen);
#endif
break;
}
udelay(1000); /* if incomplete wait 1 ms */
}
/* acknowledge reception of packet */
outw(RX_DISCARD_TOP_PACK, BASE + EP_COMMAND);
while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS)
;
#ifdef EDEBUG
{
unsigned short type = 0; /* used by EDEBUG */
type = (nic->packet[12]<<8) | nic->packet[13];
if(nic->packet[0]+nic->packet[1]+nic->packet[2]+nic->packet[3]+nic->packet[4]+
nic->packet[5] == 0xFF*ETH_ALEN)
printf(",t=%hX,b]",type);
else
printf(",t=%hX]",type);
}
#endif
return (1);
}
/*************************************************************************
3Com 509 - specific routines
**************************************************************************/
static int
eeprom_rdy(void)
{
int i;
for (i = 0; is_eeprom_busy(IS_BASE) && i < MAX_EEPROMBUSY; i++);
if (i >= MAX_EEPROMBUSY) {
/* printf("3c509: eeprom failed to come ready.\n"); */
/* memory in EPROM is tight */
/* printf("3c509: eeprom busy.\n"); */
return (0);
}
return (1);
}
/*
* get_e: gets a 16 bits word from the EEPROM. we must have set the window
* before
*/
static int
get_e(int offset)
{
if (!eeprom_rdy())
return (0xffff);
outw(EEPROM_CMD_RD | offset, IS_BASE + EP_W0_EEPROM_COMMAND);
if (!eeprom_rdy())
return (0xffff);
return (inw(IS_BASE + EP_W0_EEPROM_DATA));
}
static int
send_ID_sequence(int port)
{
int cx, al;
for (al = 0xff, cx = 0; cx < 255; cx++) {
outb(al, port);
al <<= 1;
if (al & 0x100)
al ^= 0xcf;
}
return (1);
}
/*
* We get eeprom data from the id_port given an offset into the eeprom.
* Basically; after the ID_sequence is sent to all of the cards; they enter
* the ID_CMD state where they will accept command requests. 0x80-0xbf loads
* the eeprom data. We then read the port 16 times and with every read; the
* cards check for contention (ie: if one card writes a 0 bit and another
* writes a 1 bit then the host sees a 0. At the end of the cycle; each card
* compares the data on the bus; if there is a difference then that card goes
* into ID_WAIT state again). In the meantime; one bit of data is returned in
* the AX register which is conveniently returned to us by inb(). Hence; we
* read 16 times getting one bit of data with each read.
*/
static int
get_eeprom_data(int id_port, int offset)
{
int i, data = 0;
outb(0x80 + offset, id_port);
/* Do we really need this wait? Won't be noticeable anyway */
udelay(10000);
for (i = 0; i < 16; i++)
data = (data << 1) | (inw(id_port) & 1);
return (data);
}
static void __t509_disable(void)
{
outb(0xc0, EP_ID_PORT);
}
static void t509_disable(struct dev *dev)
{
struct nic *nic = (struct nic *)dev;
/* reset and disable merge */
t509_reset(nic);
__t509_disable();
}
static void t509_irq(struct nic *nic __unused, irq_action_t action __unused)
{
switch ( action ) {
case DISABLE :
break;
case ENABLE :
break;
case FORCE :
break;
}
}
/**************************************************************************
ETH_PROBE - Look for an adapter
***************************************************************************/
#ifdef INCLUDE_3C529
static int t529_probe(struct dev *dev, unsigned short *probe_addrs __unused)
#else
static int t509_probe(struct dev *dev, unsigned short *probe_addrs __unused)
#endif
{
struct nic *nic = (struct nic *)dev;
/* common variables */
int i;
int failcount;
#ifdef INCLUDE_3C529
struct el3_mca_adapters_struct *mcafound = NULL;
int mca_pos4 = 0, mca_pos5 = 0, mca_irq = 0;
#endif
__t509_disable(); /* in case board was active */
for (failcount = 0; failcount < 100; failcount++) {
int data, j, io_base, id_port;
unsigned short k;
int ep_current_tag;
short *p;
#ifdef INCLUDE_3C529
int curboard;
#endif
id_port = EP_ID_PORT;
ep_current_tag = EP_LAST_TAG + 1;
/*********************************************************
Search for 3Com 509 card
***********************************************************/
#ifdef INCLUDE_3C529
/*
* XXX: We should really check to make sure we have an MCA
* bus controller before going ahead with this...
*
* For now, we avoid any hassle by making it a compile
* time option.
*
*/
/* printf("\nWarning: Assuming presence of MCA bus\n"); */
/* Make sure motherboard setup is off */
outb_p(0xff, MCA_MOTHERBOARD_SETUP_REG);
/* Cycle through slots */
for(curboard=0; curboard<MCA_MAX_SLOT_NR; curboard++) {
int boardid;
int curcard;
outb_p(0x8|(curboard&0xf), MCA_ADAPTER_SETUP_REG);
boardid = inb_p(MCA_POS_REG(0));
boardid += inb_p(MCA_POS_REG(1)) << 8;
curcard = 0;
while (el3_mca_adapters[curcard].name) {
if (el3_mca_adapters[curcard].id == boardid) {
mcafound = &el3_mca_adapters[curcard];
mca_pos4 = inb_p(MCA_POS_REG(4));
mca_pos5 = inb_p(MCA_POS_REG(5));
goto donewithdetect;
}
else
curcard++;
}
}
donewithdetect:
/* Kill all setup modes */
outb_p(0, MCA_ADAPTER_SETUP_REG);
if (mcafound) {
eth_nic_base = ((short)((mca_pos4&0xfc)|0x02)) << 8;
mca_irq = mca_pos5 & 0x0f;
ep_current_tag--;
}
else
/*printf("MCA Card not found\n")*/;
#endif
/* Look for the EISA boards, leave them activated */
/* search for the first card, ignore all others */
for(j = 1; j < 16; j++) {
io_base = (j * EP_EISA_START) | EP_EISA_W0;
if (inw(io_base + EP_W0_MFG_ID) != MFG_ID)
continue;
/* we must have found 0x1f if the board is EISA configurated */
if ((inw(io_base + EP_W0_ADDRESS_CFG) & 0x1f) != 0x1f)
continue;
/* Reset and Enable the card */
outb(W0_P4_CMD_RESET_ADAPTER, io_base + EP_W0_CONFIG_CTRL);
udelay(1000); /* Must wait 800 µs, be conservative */
outb(W0_P4_CMD_ENABLE_ADAPTER, io_base + EP_W0_CONFIG_CTRL);
/*
* Once activated, all the registers are mapped in the range
* x000 - x00F, where x is the slot number.
*/
eth_nic_base = j * EP_EISA_START;
break;
}
ep_current_tag--;
/* Look for the ISA boards. Init and leave them actived */
/* search for the first card, ignore all others */
outb(0xc0, id_port); /* Global reset */
udelay(1000); /* wait 1 ms */
for (i = 0; i < EP_MAX_BOARDS; i++) {
outb(0, id_port);
outb(0, id_port);
send_ID_sequence(id_port);
data = get_eeprom_data(id_port, EEPROM_MFG_ID);
if (data != MFG_ID)
break;
/* resolve contention using the Ethernet address */
for (j = 0; j < 3; j++)
data = get_eeprom_data(id_port, j);
eth_nic_base =
(get_eeprom_data(id_port, EEPROM_ADDR_CFG) & 0x1f) * 0x10 + 0x200;
outb(ep_current_tag, id_port); /* tags board */
outb(ACTIVATE_ADAPTER_TO_CONFIG, id_port);
ep_current_tag--;
break;
}
if (i >= EP_MAX_BOARDS)
goto no3c509;
/*
* The iobase was found and MFG_ID was 0x6d50. PROD_ID should be
* 0x9[0-f]50
*/
GO_WINDOW(0);
k = get_e(EEPROM_PROD_ID);
#ifdef INCLUDE_3C529
/*
* On MCA, the PROD_ID matches the MCA card ID (POS0+POS1)
*/
if (mcafound) {
if (mcafound->id != k) {
printf("MCA: PROD_ID in EEPROM does not match MCA card ID! (%hX != %hX)\n", k, mcafound->id);
goto no3c509;
}
} else { /* for ISA/EISA */
if ((k & 0xf0ff) != (PROD_ID & 0xf0ff))
goto no3c509;
}
#else
if ((k & 0xf0ff) != (PROD_ID & 0xf0ff))
goto no3c509;
#endif
#ifdef INCLUDE_3C529
if (mcafound) {
printf("%s board found on MCA at %#hx IRQ %d -",
mcafound->name, eth_nic_base, mca_irq);
} else {
#endif
if(eth_nic_base >= EP_EISA_START)
printf("3C5x9 board on EISA at %#hx - ",eth_nic_base);
else
printf("3C5x9 board on ISA at %#hx - ",eth_nic_base);
#ifdef INCLUDE_3C529
}
#endif
/* test for presence of connectors */
i = inw(IS_BASE + EP_W0_CONFIG_CTRL);
j = (inw(IS_BASE + EP_W0_ADDRESS_CFG) >> 14) & 0x3;
switch(j) {
case 0:
if (i & IS_UTP) {
printf("10baseT\n");
connector = utp;
}
else {
printf("10baseT not present\n");
goto no3c509;
}
break;
case 1:
if (i & IS_AUI)
printf("10base5\n");
else {
printf("10base5 not present\n");
goto no3c509;
}
break;
case 3:
if (i & IS_BNC) {
printf("10base2\n");
connector = bnc;
}
else {
printf("10base2 not present\n");
goto no3c509;
}
break;
default:
printf("unknown connector\n");
goto no3c509;
}
/*
* Read the station address from the eeprom
*/
p = (unsigned short *) nic->node_addr;
for (i = 0; i < ETH_ALEN / 2; i++) {
GO_WINDOW(0);
p[i] = htons(get_e(i));
GO_WINDOW(2);
outw(ntohs(p[i]), BASE + EP_W2_ADDR_0 + (i * 2));
}
printf("Ethernet address: %!\n", nic->node_addr);
t509_reset(nic);
nic->irqno = 0;
nic->ioaddr = eth_nic_base;
dev->disable = t509_disable;
nic->poll = t509_poll;
nic->transmit = t509_transmit;
nic->irq = t509_irq;
/* Based on PnP ISA map */
dev->devid.vendor_id = htons(GENERIC_ISAPNP_VENDOR);
dev->devid.device_id = htons(0x80f7);
return 1;
no3c509:
continue;
/* printf("(probe fail)"); */
}
return 0;
}
#ifdef INCLUDE_3C509
static struct isa_driver t509_driver __isa_driver = {
.type = NIC_DRIVER,
.name = "3C509",
.probe = t509_probe,
.ioaddrs = 0,
};
#endif
#ifdef INCLUDE_3C529
static struct isa_driver t529_driver __isa_driver = {
.type = NIC_DRIVER,
.name = "3C529",
.probe = t529_probe,
.ioaddrs = 0,
};
#endif
/*
* Local variables:
* c-basic-offset: 8
* End:
*/

399
src/drivers/net/3c509.h Normal file
View File

@@ -0,0 +1,399 @@
/*
* Copyright (c) 1993 Herb Peyerl (hpeyerl@novatel.ca) 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. The name
* of the author may not be used to endorse or promote products derived from
* this software withough specific prior written permission
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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.
*
* if_epreg.h,v 1.4 1994/11/13 10:12:37 gibbs Exp Modified by:
*
October 2, 1994
Modified by: Andres Vega Garcia
INRIA - Sophia Antipolis, France
e-mail: avega@sophia.inria.fr
finger: avega@pax.inria.fr
*/
/*
* Ethernet software status per interface.
*/
/*
* Some global constants
*/
#define TX_INIT_RATE 16
#define TX_INIT_MAX_RATE 64
#define RX_INIT_LATENCY 64
#define RX_INIT_EARLY_THRESH 64
#define MIN_RX_EARLY_THRESHF 16 /* not less than ether_header */
#define MIN_RX_EARLY_THRESHL 4
#define EEPROMSIZE 0x40
#define MAX_EEPROMBUSY 1000
#define EP_LAST_TAG 0xd7
#define EP_MAX_BOARDS 16
#ifndef EP_ID_PORT
#define EP_ID_PORT 0x100
#endif
/*
* some macros to acces long named fields
*/
#define IS_BASE (eth_nic_base)
#define BASE (eth_nic_base)
/*
* Commands to read/write EEPROM trough EEPROM command register (Window 0,
* Offset 0xa)
*/
#define EEPROM_CMD_RD 0x0080 /* Read: Address required (5 bits) */
#define EEPROM_CMD_WR 0x0040 /* Write: Address required (5 bits) */
#define EEPROM_CMD_ERASE 0x00c0 /* Erase: Address required (5 bits) */
#define EEPROM_CMD_EWEN 0x0030 /* Erase/Write Enable: No data required */
#define EEPROM_BUSY (1<<15)
#define EEPROM_TST_MODE (1<<14)
/*
* Some short functions, worth to let them be a macro
*/
#define is_eeprom_busy(b) (inw((b)+EP_W0_EEPROM_COMMAND)&EEPROM_BUSY)
#define GO_WINDOW(x) outw(WINDOW_SELECT|(x), BASE+EP_COMMAND)
/**************************************************************************
*
* These define the EEPROM data structure. They are used in the probe
* function to verify the existance of the adapter after having sent
* the ID_Sequence.
*
* There are others but only the ones we use are defined here.
*
**************************************************************************/
#define EEPROM_NODE_ADDR_0 0x0 /* Word */
#define EEPROM_NODE_ADDR_1 0x1 /* Word */
#define EEPROM_NODE_ADDR_2 0x2 /* Word */
#define EEPROM_PROD_ID 0x3 /* 0x9[0-f]50 */
#define EEPROM_MFG_ID 0x7 /* 0x6d50 */
#define EEPROM_ADDR_CFG 0x8 /* Base addr */
#define EEPROM_RESOURCE_CFG 0x9 /* IRQ. Bits 12-15 */
/**************************************************************************
*
* These are the registers for the 3Com 3c509 and their bit patterns when
* applicable. They have been taken out the the "EtherLink III Parallel
* Tasking EISA and ISA Technical Reference" "Beta Draft 10/30/92" manual
* from 3com.
*
**************************************************************************/
#define EP_COMMAND 0x0e /* Write. BASE+0x0e is always a
* command reg. */
#define EP_STATUS 0x0e /* Read. BASE+0x0e is always status
* reg. */
#define EP_WINDOW 0x0f /* Read. BASE+0x0f is always window
* reg. */
/*
* Window 0 registers. Setup.
*/
/* Write */
#define EP_W0_EEPROM_DATA 0x0c
#define EP_W0_EEPROM_COMMAND 0x0a
#define EP_W0_RESOURCE_CFG 0x08
#define EP_W0_ADDRESS_CFG 0x06
#define EP_W0_CONFIG_CTRL 0x04
/* Read */
#define EP_W0_PRODUCT_ID 0x02
#define EP_W0_MFG_ID 0x00
/*
* Window 1 registers. Operating Set.
*/
/* Write */
#define EP_W1_TX_PIO_WR_2 0x02
#define EP_W1_TX_PIO_WR_1 0x00
/* Read */
#define EP_W1_FREE_TX 0x0c
#define EP_W1_TX_STATUS 0x0b /* byte */
#define EP_W1_TIMER 0x0a /* byte */
#define EP_W1_RX_STATUS 0x08
#define EP_W1_RX_PIO_RD_2 0x02
#define EP_W1_RX_PIO_RD_1 0x00
/*
* Window 2 registers. Station Address Setup/Read
*/
/* Read/Write */
#define EP_W2_ADDR_5 0x05
#define EP_W2_ADDR_4 0x04
#define EP_W2_ADDR_3 0x03
#define EP_W2_ADDR_2 0x02
#define EP_W2_ADDR_1 0x01
#define EP_W2_ADDR_0 0x00
/*
* Window 3 registers. FIFO Management.
*/
/* Read */
#define EP_W3_FREE_TX 0x0c
#define EP_W3_FREE_RX 0x0a
/*
* Window 4 registers. Diagnostics.
*/
/* Read/Write */
#define EP_W4_MEDIA_TYPE 0x0a
#define EP_W4_CTRLR_STATUS 0x08
#define EP_W4_NET_DIAG 0x06
#define EP_W4_FIFO_DIAG 0x04
#define EP_W4_HOST_DIAG 0x02
#define EP_W4_TX_DIAG 0x00
/*
* Window 5 Registers. Results and Internal status.
*/
/* Read */
#define EP_W5_READ_0_MASK 0x0c
#define EP_W5_INTR_MASK 0x0a
#define EP_W5_RX_FILTER 0x08
#define EP_W5_RX_EARLY_THRESH 0x06
#define EP_W5_TX_AVAIL_THRESH 0x02
#define EP_W5_TX_START_THRESH 0x00
/*
* Window 6 registers. Statistics.
*/
/* Read/Write */
#define TX_TOTAL_OK 0x0c
#define RX_TOTAL_OK 0x0a
#define TX_DEFERRALS 0x08
#define RX_FRAMES_OK 0x07
#define TX_FRAMES_OK 0x06
#define RX_OVERRUNS 0x05
#define TX_COLLISIONS 0x04
#define TX_AFTER_1_COLLISION 0x03
#define TX_AFTER_X_COLLISIONS 0x02
#define TX_NO_SQE 0x01
#define TX_CD_LOST 0x00
/****************************************
*
* Register definitions.
*
****************************************/
/*
* Command register. All windows.
*
* 16 bit register.
* 15-11: 5-bit code for command to be executed.
* 10-0: 11-bit arg if any. For commands with no args;
* this can be set to anything.
*/
#define GLOBAL_RESET (unsigned short) 0x0000 /* Wait at least 1ms
* after issuing */
#define WINDOW_SELECT (unsigned short) (0x1<<11)
#define START_TRANSCEIVER (unsigned short) (0x2<<11) /* Read ADDR_CFG reg to
* determine whether
* this is needed. If
* so; wait 800 uSec
* before using trans-
* ceiver. */
#define RX_DISABLE (unsigned short) (0x3<<11) /* state disabled on
* power-up */
#define RX_ENABLE (unsigned short) (0x4<<11)
#define RX_RESET (unsigned short) (0x5<<11)
#define RX_DISCARD_TOP_PACK (unsigned short) (0x8<<11)
#define TX_ENABLE (unsigned short) (0x9<<11)
#define TX_DISABLE (unsigned short) (0xa<<11)
#define TX_RESET (unsigned short) (0xb<<11)
#define REQ_INTR (unsigned short) (0xc<<11)
#define SET_INTR_MASK (unsigned short) (0xe<<11)
#define SET_RD_0_MASK (unsigned short) (0xf<<11)
#define SET_RX_FILTER (unsigned short) (0x10<<11)
#define FIL_INDIVIDUAL (unsigned short) (0x1)
#define FIL_GROUP (unsigned short) (0x2)
#define FIL_BRDCST (unsigned short) (0x4)
#define FIL_ALL (unsigned short) (0x8)
#define SET_RX_EARLY_THRESH (unsigned short) (0x11<<11)
#define SET_TX_AVAIL_THRESH (unsigned short) (0x12<<11)
#define SET_TX_START_THRESH (unsigned short) (0x13<<11)
#define STATS_ENABLE (unsigned short) (0x15<<11)
#define STATS_DISABLE (unsigned short) (0x16<<11)
#define STOP_TRANSCEIVER (unsigned short) (0x17<<11)
/*
* The following C_* acknowledge the various interrupts. Some of them don't
* do anything. See the manual.
*/
#define ACK_INTR (unsigned short) (0x6800)
#define C_INTR_LATCH (unsigned short) (ACK_INTR|0x1)
#define C_CARD_FAILURE (unsigned short) (ACK_INTR|0x2)
#define C_TX_COMPLETE (unsigned short) (ACK_INTR|0x4)
#define C_TX_AVAIL (unsigned short) (ACK_INTR|0x8)
#define C_RX_COMPLETE (unsigned short) (ACK_INTR|0x10)
#define C_RX_EARLY (unsigned short) (ACK_INTR|0x20)
#define C_INT_RQD (unsigned short) (ACK_INTR|0x40)
#define C_UPD_STATS (unsigned short) (ACK_INTR|0x80)
/*
* Status register. All windows.
*
* 15-13: Window number(0-7).
* 12: Command_in_progress.
* 11: reserved.
* 10: reserved.
* 9: reserved.
* 8: reserved.
* 7: Update Statistics.
* 6: Interrupt Requested.
* 5: RX Early.
* 4: RX Complete.
* 3: TX Available.
* 2: TX Complete.
* 1: Adapter Failure.
* 0: Interrupt Latch.
*/
#define S_INTR_LATCH (unsigned short) (0x1)
#define S_CARD_FAILURE (unsigned short) (0x2)
#define S_TX_COMPLETE (unsigned short) (0x4)
#define S_TX_AVAIL (unsigned short) (0x8)
#define S_RX_COMPLETE (unsigned short) (0x10)
#define S_RX_EARLY (unsigned short) (0x20)
#define S_INT_RQD (unsigned short) (0x40)
#define S_UPD_STATS (unsigned short) (0x80)
#define S_5_INTS (S_CARD_FAILURE|S_TX_COMPLETE|\
S_TX_AVAIL|S_RX_COMPLETE|S_RX_EARLY)
#define S_COMMAND_IN_PROGRESS (unsigned short) (0x1000)
/*
* FIFO Registers.
* RX Status. Window 1/Port 08
*
* 15: Incomplete or FIFO empty.
* 14: 1: Error in RX Packet 0: Incomplete or no error.
* 13-11: Type of error.
* 1000 = Overrun.
* 1011 = Run Packet Error.
* 1100 = Alignment Error.
* 1101 = CRC Error.
* 1001 = Oversize Packet Error (>1514 bytes)
* 0010 = Dribble Bits.
* (all other error codes, no errors.)
*
* 10-0: RX Bytes (0-1514)
*/
#define ERR_RX_INCOMPLETE (unsigned short) (0x1<<15)
#define ERR_RX (unsigned short) (0x1<<14)
#define ERR_RX_OVERRUN (unsigned short) (0x8<<11)
#define ERR_RX_RUN_PKT (unsigned short) (0xb<<11)
#define ERR_RX_ALIGN (unsigned short) (0xc<<11)
#define ERR_RX_CRC (unsigned short) (0xd<<11)
#define ERR_RX_OVERSIZE (unsigned short) (0x9<<11)
#define ERR_RX_DRIBBLE (unsigned short) (0x2<<11)
/*
* FIFO Registers.
* TX Status. Window 1/Port 0B
*
* Reports the transmit status of a completed transmission. Writing this
* register pops the transmit completion stack.
*
* Window 1/Port 0x0b.
*
* 7: Complete
* 6: Interrupt on successful transmission requested.
* 5: Jabber Error (TP Only, TX Reset required. )
* 4: Underrun (TX Reset required. )
* 3: Maximum Collisions.
* 2: TX Status Overflow.
* 1-0: Undefined.
*
*/
#define TXS_COMPLETE 0x80
#define TXS_SUCCES_INTR_REQ 0x40
#define TXS_JABBER 0x20
#define TXS_UNDERRUN 0x10
#define TXS_MAX_COLLISION 0x8
#define TXS_STATUS_OVERFLOW 0x4
/*
* Configuration control register.
* Window 0/Port 04
*/
/* Read */
#define IS_AUI (1<<13)
#define IS_BNC (1<<12)
#define IS_UTP (1<<9)
/* Write */
#define ENABLE_DRQ_IRQ 0x0001
#define W0_P4_CMD_RESET_ADAPTER 0x4
#define W0_P4_CMD_ENABLE_ADAPTER 0x1
/*
* Media type and status.
* Window 4/Port 0A
*/
#define ENABLE_UTP 0xc0
#define DISABLE_UTP 0x0
/*
* Resource control register
*/
#define SET_IRQ(i) ( ((i)<<12) | 0xF00) /* set IRQ i */
/*
* Receive status register
*/
#define RX_BYTES_MASK (unsigned short) (0x07ff)
#define RX_ERROR 0x4000
#define RX_INCOMPLETE 0x8000
/*
* Misc defines for various things.
*/
#define ACTIVATE_ADAPTER_TO_CONFIG 0xff /* to the id_port */
#define MFG_ID 0x6d50 /* in EEPROM and W0 ADDR_CONFIG */
#define PROD_ID 0x9150
#define AUI 0x1
#define BNC 0x2
#define UTP 0x4
#define RX_BYTES_MASK (unsigned short) (0x07ff)
/* EISA support */
#define EP_EISA_START 0x1000
#define EP_EISA_W0 0x0c80
#ifdef INCLUDE_3C529
/* MCA support */
#define MCA_MOTHERBOARD_SETUP_REG 0x94
#define MCA_ADAPTER_SETUP_REG 0x96
#define MCA_MAX_SLOT_NR 8
#define MCA_POS_REG(n) (0x100+(n))
#endif
/*
* Local variables:
* c-basic-offset: 8
* End:
*/

814
src/drivers/net/3c515.c Normal file
View File

@@ -0,0 +1,814 @@
/*
* 3c515.c -- 3COM 3C515 Fast Etherlink ISA 10/100BASE-TX driver for etherboot
* Copyright (C) 2002 Timothy Legge <tlegge@rogers.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Portions of this code:
* Copyright (C) 1997-2002 Donald Becker 3c515.c: A 3Com ISA EtherLink XL "Corkscrew" ethernet driver for linux.
* Copyright (C) 2001 P.J.H.Fox (fox@roestock.demon.co.uk) ISAPNP Tools
* Copyright (c) 2002 Jaroslav Kysela <perex@suse.cz> ISA Plug & Play support Linux Kernel
* Copyright (C) 2000 Shusuke Nisiyama <shu@athena.qe.eng.hokudai.ac.jp> etherboot-5.0.5 3c595.c
* Coptright (C) 1995 Martin Renters etherboot-5.0.5 3c509.c
* Copyright (C) 1999 LightSys Technology Services, Inc. etherboot-5.0.5 3c90x.c
* Portions Copyright (C) 1999 Steve Smith etherboot-5.0.5 3c90x.c
*
* The probe and reset functions and defines are direct copies from the
* Becker code modified where necessary to make it work for etherboot
*
* The poll and transmit functions either contain code from or were written by referencing
* the above referenced etherboot drivers. This driver would not have been
* possible without this prior work
*
* REVISION HISTORY:
* ================
* v0.10 4-17-2002 TJL Initial implementation.
* v0.11 4-17-2002 TJL Cleanup of the code
* v0.12 4-26-2002 TJL Added ISA Plug and Play for Non-PNP Bioses
* v0.13 6-10-2002 TJL Fixed ISA_PNP MAC Address problem
* v0.14 9-23-2003 TJL Replaced delay with currticks
*
* Indent Options: indent -kr -i8
* *********************************************************/
#define ISA_PNP
/*#define EDEBUG1*/
/* to get some global routines like printf */
#include "etherboot.h"
/* to get the interface to the body of the program */
#include "nic.h"
#include "isa.h"
#include "timer.h"
#ifdef ISA_PNP
static void t3c515_wait(unsigned int nticks)
{
unsigned int to = currticks() + nticks;
while (currticks() < to)
/* wait */ ;
}
#endif
/* TJL definations */
#define HZ 100
#define u16 unsigned short
#define u32 unsigned long
#define s16 signed short
#define s32 signed long
static unsigned short eth_nic_base;
#define BASE (eth_nic_base)
static int if_port;
struct corkscrew_private *vp;
/* Brought directly from 3c515.c by Becker */
#define CORKSCREW 1
/* Maximum events (Rx packets, etc.) to handle at each interrupt.
static int max_interrupt_work = 20;
*/
/* Enable the automatic media selection code -- usually set. */
#define AUTOMEDIA 1
/* Allow the use of fragment bus master transfers instead of only
programmed-I/O for Vortex cards. Full-bus-master transfers are always
enabled by default on Boomerang cards. If VORTEX_BUS_MASTER is defined,
the feature may be turned on using 'options'. */
#define VORTEX_BUS_MASTER
/* A few values that may be tweaked. */
/* Keep the ring sizes a power of two for efficiency. */
#define TX_RING_SIZE 16
#define RX_RING_SIZE 16
#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer. */
/* "Knobs" for adjusting internal parameters. */
/* Put out somewhat more debugging messages. (0 - no msg, 1 minimal msgs). */
#define DRIVER_DEBUG 1
/* Some values here only for performance evaluation and path-coverage
debugging.
static int rx_nocopy, rx_copy, queued_packet;
*/
#ifdef DRIVER_DEBUG
static int corkscrew_debug = DRIVER_DEBUG;
#else
static int corkscrew_debug = 1;
#endif
#define CORKSCREW_ID 10
#define EL3WINDOW(win_num) outw(SelectWindow + (win_num), ioaddr + EL3_CMD)
#define EL3_CMD 0x0e
#define EL3_STATUS 0x0e
#define RX_BYTES_MASK (unsigned short) (0x07ff)
enum corkscrew_cmd {
TotalReset = 0 << 11, SelectWindow = 1 << 11, StartCoax = 2 << 11,
RxDisable = 3 << 11, RxEnable = 4 << 11, RxReset = 5 << 11,
UpStall = 6 << 11, UpUnstall = (6 << 11) + 1,
DownStall = (6 << 11) + 2, DownUnstall = (6 << 11) + 3,
RxDiscard = 8 << 11, TxEnable = 9 << 11, TxDisable =
10 << 11, TxReset = 11 << 11,
FakeIntr = 12 << 11, AckIntr = 13 << 11, SetIntrEnb = 14 << 11,
SetStatusEnb = 15 << 11, SetRxFilter = 16 << 11, SetRxThreshold =
17 << 11,
SetTxThreshold = 18 << 11, SetTxStart = 19 << 11,
StartDMAUp = 20 << 11, StartDMADown = (20 << 11) + 1, StatsEnable =
21 << 11,
StatsDisable = 22 << 11, StopCoax = 23 << 11,
};
/* The SetRxFilter command accepts the following classes: */
enum RxFilter {
RxStation = 1, RxMulticast = 2, RxBroadcast = 4, RxProm = 8
};
/* Bits in the general status register. */
enum corkscrew_status {
IntLatch = 0x0001, AdapterFailure = 0x0002, TxComplete = 0x0004,
TxAvailable = 0x0008, RxComplete = 0x0010, RxEarly = 0x0020,
IntReq = 0x0040, StatsFull = 0x0080,
DMADone = 1 << 8, DownComplete = 1 << 9, UpComplete = 1 << 10,
DMAInProgress = 1 << 11, /* DMA controller is still busy. */
CmdInProgress = 1 << 12, /* EL3_CMD is still busy. */
};
/* Register window 1 offsets, the window used in normal operation.
On the Corkscrew this window is always mapped at offsets 0x10-0x1f. */
enum Window1 {
TX_FIFO = 0x10, RX_FIFO = 0x10, RxErrors = 0x14,
RxStatus = 0x18, Timer = 0x1A, TxStatus = 0x1B,
TxFree = 0x1C, /* Remaining free bytes in Tx buffer. */
};
enum Window0 {
Wn0IRQ = 0x08,
#if defined(CORKSCREW)
Wn0EepromCmd = 0x200A, /* Corkscrew EEPROM command register. */
Wn0EepromData = 0x200C, /* Corkscrew EEPROM results register. */
#else
Wn0EepromCmd = 10, /* Window 0: EEPROM command register. */
Wn0EepromData = 12, /* Window 0: EEPROM results register. */
#endif
};
enum Win0_EEPROM_bits {
EEPROM_Read = 0x80, EEPROM_WRITE = 0x40, EEPROM_ERASE = 0xC0,
EEPROM_EWENB = 0x30, /* Enable erasing/writing for 10 msec. */
EEPROM_EWDIS = 0x00, /* Disable EWENB before 10 msec timeout. */
};
enum Window3 { /* Window 3: MAC/config bits. */
Wn3_Config = 0, Wn3_MAC_Ctrl = 6, Wn3_Options = 8,
};
union wn3_config {
int i;
struct w3_config_fields {
unsigned int ram_size:3, ram_width:1, ram_speed:2,
rom_size:2;
int pad8:8;
unsigned int ram_split:2, pad18:2, xcvr:3, pad21:1,
autoselect:1;
int pad24:7;
} u;
};
enum Window4 {
Wn4_NetDiag = 6, Wn4_Media = 10, /* Window 4: Xcvr/media bits. */
};
enum Win4_Media_bits {
Media_SQE = 0x0008, /* Enable SQE error counting for AUI. */
Media_10TP = 0x00C0, /* Enable link beat and jabber for 10baseT. */
Media_Lnk = 0x0080, /* Enable just link beat for 100TX/100FX. */
Media_LnkBeat = 0x0800,
};
enum Window7 { /* Window 7: Bus Master control. */
Wn7_MasterAddr = 0, Wn7_MasterLen = 6, Wn7_MasterStatus = 12,
};
/* Boomerang-style bus master control registers. Note ISA aliases! */
enum MasterCtrl {
PktStatus = 0x400, DownListPtr = 0x404, FragAddr = 0x408, FragLen =
0x40c,
TxFreeThreshold = 0x40f, UpPktStatus = 0x410, UpListPtr = 0x418,
};
/* The Rx and Tx descriptor lists.
Caution Alpha hackers: these types are 32 bits! Note also the 8 byte
alignment contraint on tx_ring[] and rx_ring[]. */
struct boom_rx_desc {
u32 next;
s32 status;
u32 addr;
s32 length;
};
/* Values for the Rx status entry. */
enum rx_desc_status {
RxDComplete = 0x00008000, RxDError = 0x4000,
/* See boomerang_rx() for actual error bits */
};
struct boom_tx_desc {
u32 next;
s32 status;
u32 addr;
s32 length;
};
struct corkscrew_private {
const char *product_name;
struct net_device *next_module;
/* The Rx and Tx rings are here to keep them quad-word-aligned. */
struct boom_rx_desc rx_ring[RX_RING_SIZE];
struct boom_tx_desc tx_ring[TX_RING_SIZE];
/* The addresses of transmit- and receive-in-place skbuffs. */
struct sk_buff *rx_skbuff[RX_RING_SIZE];
struct sk_buff *tx_skbuff[TX_RING_SIZE];
unsigned int cur_rx, cur_tx; /* The next free ring entry */
unsigned int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */
struct sk_buff *tx_skb; /* Packet being eaten by bus master ctrl. */
int capabilities; /* Adapter capabilities word. */
int options; /* User-settable misc. driver options. */
int last_rx_packets; /* For media autoselection. */
unsigned int available_media:8, /* From Wn3_Options */
media_override:3, /* Passed-in media type. */
default_media:3, /* Read from the EEPROM. */
full_duplex:1, autoselect:1, bus_master:1, /* Vortex can only do a fragment bus-m. */
full_bus_master_tx:1, full_bus_master_rx:1, /* Boomerang */
tx_full:1;
};
/* The action to take with a media selection timer tick.
Note that we deviate from the 3Com order by checking 10base2 before AUI.
*/
enum xcvr_types {
XCVR_10baseT =
0, XCVR_AUI, XCVR_10baseTOnly, XCVR_10base2, XCVR_100baseTx,
XCVR_100baseFx, XCVR_MII = 6, XCVR_Default = 8,
};
static struct media_table {
char *name;
unsigned int media_bits:16, /* Bits to set in Wn4_Media register. */
mask:8, /* The transceiver-present bit in Wn3_Config. */
next:8; /* The media type to try next. */
short wait; /* Time before we check media status. */
} media_tbl[] = {
{
"10baseT", Media_10TP, 0x08, XCVR_10base2, (14 * HZ) / 10}
, {
"10Mbs AUI", Media_SQE, 0x20, XCVR_Default, (1 * HZ) / 10}
, {
"undefined", 0, 0x80, XCVR_10baseT, 10000}
, {
"10base2", 0, 0x10, XCVR_AUI, (1 * HZ) / 10}
, {
"100baseTX", Media_Lnk, 0x02, XCVR_100baseFx,
(14 * HZ) / 10}
, {
"100baseFX", Media_Lnk, 0x04, XCVR_MII, (14 * HZ) / 10}
, {
"MII", 0, 0x40, XCVR_10baseT, 3 * HZ}
, {
"undefined", 0, 0x01, XCVR_10baseT, 10000}
, {
"Default", 0, 0xFF, XCVR_10baseT, 10000}
,};
/* TILEG Modified to remove reference to dev */
static int corkscrew_found_device(int ioaddr, int irq, int product_index,
int options, struct nic *nic);
static int corkscrew_probe1(int ioaddr, int irq, int product_index,
struct nic *nic);
/* This driver uses 'options' to pass the media type, full-duplex flag, etc. */
/* Note: this is the only limit on the number of cards supported!! */
static int options[8] = { -1, -1, -1, -1, -1, -1, -1, -1, };
/* End Brought directly from 3c515.c by Becker */
/**************************************************************************
RESET - Reset adapter
***************************************************************************/
static void t515_reset(struct nic *nic)
{
int ioaddr = BASE;
union wn3_config config;
int i;
/* Before initializing select the active media port. */
EL3WINDOW(3);
if (vp->full_duplex)
outb(0x20, ioaddr + Wn3_MAC_Ctrl); /* Set the full-duplex bit. */
config.i = inl(ioaddr + Wn3_Config);
if (vp->media_override != 7) {
if (corkscrew_debug > 1)
printf("Media override to transceiver %d (%s).\n",
vp->media_override,
media_tbl[vp->media_override].name);
if_port = vp->media_override;
} else if (vp->autoselect) {
/* Find first available media type, starting with 100baseTx. */
if_port = 4;
while (!(vp->available_media & media_tbl[if_port].mask))
if_port = media_tbl[if_port].next;
if (corkscrew_debug > 1)
printf("Initial media type %s.\n",
media_tbl[if_port].name);
} else
if_port = vp->default_media;
config.u.xcvr = if_port;
outl(config.i, ioaddr + Wn3_Config);
if (corkscrew_debug > 1) {
printf("corkscrew_open() InternalConfig 0x%hX.\n",
config.i);
}
outw(TxReset, ioaddr + EL3_CMD);
for (i = 20; i >= 0; i--)
if (!(inw(ioaddr + EL3_STATUS) & CmdInProgress))
break;
outw(RxReset, ioaddr + EL3_CMD);
/* Wait a few ticks for the RxReset command to complete. */
for (i = 20; i >= 0; i--)
if (!(inw(ioaddr + EL3_STATUS) & CmdInProgress))
break;
outw(SetStatusEnb | 0x00, ioaddr + EL3_CMD);
if (corkscrew_debug > 1) {
EL3WINDOW(4);
printf("FIXME: fix print for irq, not 9");
printf("corkscrew_open() irq %d media status 0x%hX.\n",
9, inw(ioaddr + Wn4_Media));
}
/* Set the station address and mask in window 2 each time opened. */
EL3WINDOW(2);
for (i = 0; i < 6; i++)
outb(nic->node_addr[i], ioaddr + i);
for (; i < 12; i += 2)
outw(0, ioaddr + i);
if (if_port == 3)
/* Start the thinnet transceiver. We should really wait 50ms... */
outw(StartCoax, ioaddr + EL3_CMD);
EL3WINDOW(4);
outw((inw(ioaddr + Wn4_Media) & ~(Media_10TP | Media_SQE)) |
media_tbl[if_port].media_bits, ioaddr + Wn4_Media);
/* Switch to the stats window, and clear all stats by reading. */
/* outw(StatsDisable, ioaddr + EL3_CMD);*/
EL3WINDOW(6);
for (i = 0; i < 10; i++)
inb(ioaddr + i);
inw(ioaddr + 10);
inw(ioaddr + 12);
/* New: On the Vortex we must also clear the BadSSD counter. */
EL3WINDOW(4);
inb(ioaddr + 12);
/* ..and on the Boomerang we enable the extra statistics bits. */
outw(0x0040, ioaddr + Wn4_NetDiag);
/* Switch to register set 7 for normal use. */
EL3WINDOW(7);
/* Temporarily left in place. If these FIXMEs are printed
it meand that special logic for that card may need to be added
see Becker's 3c515.c driver */
if (vp->full_bus_master_rx) { /* Boomerang bus master. */
printf("FIXME: Is this if necessary");
vp->cur_rx = vp->dirty_rx = 0;
if (corkscrew_debug > 2)
printf(" Filling in the Rx ring.\n");
for (i = 0; i < RX_RING_SIZE; i++) {
printf("FIXME: Is this if necessary");
}
}
if (vp->full_bus_master_tx) { /* Boomerang bus master Tx. */
vp->cur_tx = vp->dirty_tx = 0;
outb(PKT_BUF_SZ >> 8, ioaddr + TxFreeThreshold); /* Room for a packet. */
/* Clear the Tx ring. */
for (i = 0; i < TX_RING_SIZE; i++)
vp->tx_skbuff[i] = 0;
outl(0, ioaddr + DownListPtr);
}
/* Set receiver mode: presumably accept b-case and phys addr only. */
outw(SetRxFilter | RxStation | RxMulticast | RxBroadcast | RxProm,
ioaddr + EL3_CMD);
outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */
outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */
/* Allow status bits to be seen. */
outw(SetStatusEnb | AdapterFailure | IntReq | StatsFull |
(vp->full_bus_master_tx ? DownComplete : TxAvailable) |
(vp->full_bus_master_rx ? UpComplete : RxComplete) |
(vp->bus_master ? DMADone : 0), ioaddr + EL3_CMD);
/* Ack all pending events, and set active indicator mask. */
outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq,
ioaddr + EL3_CMD);
outw(SetIntrEnb | IntLatch | TxAvailable | RxComplete | StatsFull
| (vp->bus_master ? DMADone : 0) | UpComplete | DownComplete,
ioaddr + EL3_CMD);
}
/**************************************************************************
POLL - Wait for a frame
***************************************************************************/
static int t515_poll(struct nic *nic, int retrieve)
{
short status, cst;
register short rx_fifo;
cst = inw(BASE + EL3_STATUS);
if ((cst & RxComplete) == 0) {
/* Ack all pending events, and set active indicator mask. */
outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq,
BASE + EL3_CMD);
outw(SetIntrEnb | IntLatch | TxAvailable | RxComplete |
StatsFull | (vp->
bus_master ? DMADone : 0) | UpComplete |
DownComplete, BASE + EL3_CMD);
return 0;
}
status = inw(BASE + RxStatus);
if (status & RxDError) {
printf("RxDError\n");
outw(RxDiscard, BASE + EL3_CMD);
return 0;
}
rx_fifo = status & RX_BYTES_MASK;
if (rx_fifo == 0)
return 0;
if ( ! retrieve ) return 1;
#ifdef EDEBUG
printf("[l=%d", rx_fifo);
#endif
insw(BASE + RX_FIFO, nic->packet, rx_fifo / 2);
if (rx_fifo & 1)
nic->packet[rx_fifo - 1] = inb(BASE + RX_FIFO);
nic->packetlen = rx_fifo;
while (1) {
status = inw(BASE + RxStatus);
#ifdef EDEBUG
printf("0x%hX*", status);
#endif
rx_fifo = status & RX_BYTES_MASK;
if (rx_fifo > 0) {
insw(BASE + RX_FIFO, nic->packet + nic->packetlen,
rx_fifo / 2);
if (rx_fifo & 1)
nic->packet[nic->packetlen + rx_fifo - 1] =
inb(BASE + RX_FIFO);
nic->packetlen += rx_fifo;
#ifdef EDEBUG
printf("+%d", rx_fifo);
#endif
}
if ((status & RxComplete) == 0) {
#ifdef EDEBUG
printf("=%d", nic->packetlen);
#endif
break;
}
udelay(1000);
}
/* acknowledge reception of packet */
outw(RxDiscard, BASE + EL3_CMD);
while (inw(BASE + EL3_STATUS) & CmdInProgress);
#ifdef EDEBUG
{
unsigned short type = 0;
type = (nic->packet[12] << 8) | nic->packet[13];
if (nic->packet[0] + nic->packet[1] + nic->packet[2] +
nic->packet[3] + nic->packet[4] + nic->packet[5] ==
0xFF * ETH_ALEN)
printf(",t=0x%hX,b]", type);
else
printf(",t=0x%hX]", type);
}
#endif
return 1;
}
/*************************************************************************
3Com 515 - specific routines
**************************************************************************/
static char padmap[] = {
0, 3, 2, 1
};
/**************************************************************************
TRANSMIT - Transmit a frame
***************************************************************************/
static void t515_transmit(struct nic *nic, const char *d, /* Destination */
unsigned int t, /* Type */
unsigned int s, /* size */
const char *p)
{ /* Packet */
register int len;
int pad;
int status;
#ifdef EDEBUG
printf("{l=%d,t=0x%hX}", s + ETH_HLEN, t);
#endif
/* swap bytes of type */
t = htons(t);
len = s + ETH_HLEN; /* actual length of packet */
pad = padmap[len & 3];
/*
* The 3c515 automatically pads short packets to minimum ethernet length,
* but we drop packets that are too large. Perhaps we should truncate
* them instead?
Copied from 3c595. Is this true for the 3c515?
*/
if (len + pad > ETH_FRAME_LEN) {
return;
}
/* drop acknowledgements */
while ((status = inb(BASE + TxStatus)) & TxComplete) {
/*if(status & (TXS_UNDERRUN|0x88|TXS_STATUS_OVERFLOW)) { */
outw(TxReset, BASE + EL3_CMD);
outw(TxEnable, BASE + EL3_CMD);
/* } */
outb(0x0, BASE + TxStatus);
}
while (inw(BASE + TxFree) < len + pad + 4) {
/* no room in FIFO */
}
outw(len, BASE + TX_FIFO);
outw(0x0, BASE + TX_FIFO); /* Second dword meaningless */
/* write packet */
outsw(BASE + TX_FIFO, d, ETH_ALEN / 2);
outsw(BASE + TX_FIFO, nic->node_addr, ETH_ALEN / 2);
outw(t, BASE + TX_FIFO);
outsw(BASE + TX_FIFO, p, s / 2);
if (s & 1)
outb(*(p + s - 1), BASE + TX_FIFO);
while (pad--)
outb(0, BASE + TX_FIFO); /* Padding */
/* wait for Tx complete */
while ((inw(BASE + EL3_STATUS) & CmdInProgress) != 0);
}
/**************************************************************************
DISABLE - Turn off ethernet interface
***************************************************************************/
static void t515_disable(struct dev *dev)
{
struct nic *nic = (struct nic *) dev;
/* merge reset an disable */
t515_reset(nic);
/* This is a hack. Since ltsp worked on my
system without any disable functionality I
have no way to determine if this works */
/* Disable the receiver and transmitter. */
outw(RxDisable, BASE + EL3_CMD);
outw(TxDisable, BASE + EL3_CMD);
if (if_port == XCVR_10base2)
/* Turn off thinnet power. Green! */
outw(StopCoax, BASE + EL3_CMD);
outw(SetIntrEnb | 0x0000, BASE + EL3_CMD);
#ifdef ISA_PNP
/*Deactivate */
/* ACTIVATE;
WRITE_DATA(0);
*/
#endif
return;
}
static void t515_irq(struct nic *nic __unused, irq_action_t action __unused)
{
switch ( action ) {
case DISABLE :
break;
case ENABLE :
break;
case FORCE :
break;
}
}
/**************************************************************************
PROBE - Look for an adapter, this routine's visible to the outside
You should omit the last argument struct pci_device * for a non-PCI NIC
***************************************************************************/
void config_pnp_device(void);
static int t515_probe(struct dev *dev,
unsigned short *probe_addrs __unused)
{
struct nic *nic = (struct nic *) dev;
/* Direct copy from Beckers 3c515.c removing any ISAPNP sections */
int cards_found = 0;
static int ioaddr;
#ifdef ISA_PNP
config_pnp_device();
#endif
/* Check all locations on the ISA bus -- evil! */
for (ioaddr = 0x100; ioaddr < 0x400; ioaddr += 0x20) {
int irq;
/* Check the resource configuration for a matching ioaddr. */
if ((inw(ioaddr + 0x2002) & 0x1f0) != (ioaddr & 0x1f0))
continue;
/* Verify by reading the device ID from the EEPROM. */
{
int timer;
outw(EEPROM_Read + 7, ioaddr + Wn0EepromCmd);
/* Pause for at least 162 us. for the read to take place. */
for (timer = 4; timer >= 0; timer--) {
t3c515_wait(1);
if ((inw(ioaddr + Wn0EepromCmd) & 0x0200)
== 0)
break;
}
if (inw(ioaddr + Wn0EepromData) != 0x6d50)
continue;
}
printf
("3c515 Resource configuration register 0x%hX, DCR 0x%hX.\n",
inl(ioaddr + 0x2002), inw(ioaddr + 0x2000));
irq = inw(ioaddr + 0x2002) & 15;
BASE = ioaddr;
corkscrew_found_device(BASE, irq, CORKSCREW_ID,
options[cards_found], nic);
cards_found++;
}
if (corkscrew_debug)
printf("%d 3c515 cards found.\n", cards_found);
if (cards_found > 0) {
t515_reset(nic);
nic->irqno = 0;
nic->ioaddr = BASE;
dev->disable = t515_disable;
nic->poll = t515_poll;
nic->transmit = t515_transmit;
nic->irq = t515_irq;
/* Based on PnP ISA map */
dev->devid.vendor_id = htons(ISAPNP_VENDOR('T', 'C', 'M'));
dev->devid.device_id = htons(0x5051);
return 1;
} else
return 0;
}
static int
corkscrew_found_device(int ioaddr, int irq,
int product_index, int options, struct nic *nic)
{
/* Direct copy from Becker 3c515.c with unecessary parts removed */
vp->product_name = "3c515";
vp->options = options;
if (options >= 0) {
vp->media_override =
((options & 7) == 2) ? 0 : options & 7;
vp->full_duplex = (options & 8) ? 1 : 0;
vp->bus_master = (options & 16) ? 1 : 0;
} else {
vp->media_override = 7;
vp->full_duplex = 0;
vp->bus_master = 0;
}
corkscrew_probe1(ioaddr, irq, product_index, nic);
return 0;
}
static int
corkscrew_probe1(int ioaddr, int irq, int product_index __unused,
struct nic *nic)
{
unsigned int eeprom[0x40], checksum = 0; /* EEPROM contents */
int i;
ioaddr = BASE;
printf("3Com %s at 0x%hX, ", vp->product_name, ioaddr);
/* Read the station address from the EEPROM. */
EL3WINDOW(0);
for (i = 0; i < 0x18; i++) {
short *phys_addr = (short *) nic->node_addr;
int timer;
outw(EEPROM_Read + i, ioaddr + Wn0EepromCmd);
/* Pause for at least 162 us. for the read to take place. */
for (timer = 4; timer >= 0; timer--) {
t3c515_wait(1);
if ((inw(ioaddr + Wn0EepromCmd) & 0x0200) == 0)
break;
}
eeprom[i] = inw(ioaddr + Wn0EepromData);
#ifdef EDEBUG1
printf("Value %d: %hX ", i, eeprom[i]);
#endif
checksum ^= eeprom[i];
if (i < 3)
phys_addr[i] = htons(eeprom[i]);
}
checksum = (checksum ^ (checksum >> 8)) & 0xff;
if (checksum != 0x00)
printf(" ***INVALID CHECKSUM 0x%hX*** ", checksum);
printf("%!", nic->node_addr);
if (eeprom[16] == 0x11c7) { /* Corkscrew */
}
printf(", IRQ %d\n", irq);
/* Tell them about an invalid IRQ. */
if (corkscrew_debug && (irq <= 0 || irq > 15))
printf
(" *** Warning: this IRQ is unlikely to work! ***\n");
{
char *ram_split[] = { "5:3", "3:1", "1:1", "3:5" };
union wn3_config config;
EL3WINDOW(3);
vp->available_media = inw(ioaddr + Wn3_Options);
config.i = inl(ioaddr + Wn3_Config);
if (corkscrew_debug > 1)
printf
(" Internal config register is %4.4x, transceivers 0x%hX.\n",
config.i, inw(ioaddr + Wn3_Options));
printf
(" %dK %s-wide RAM %s Rx:Tx split, %s%s interface.\n",
8 << config.u.ram_size,
config.u.ram_width ? "word" : "byte",
ram_split[config.u.ram_split],
config.u.autoselect ? "autoselect/" : "",
media_tbl[config.u.xcvr].name);
if_port = config.u.xcvr;
vp->default_media = config.u.xcvr;
vp->autoselect = config.u.autoselect;
}
if (vp->media_override != 7) {
printf(" Media override to transceiver type %d (%s).\n",
vp->media_override,
media_tbl[vp->media_override].name);
if_port = vp->media_override;
}
vp->capabilities = eeprom[16];
vp->full_bus_master_tx = (vp->capabilities & 0x20) ? 1 : 0;
/* Rx is broken at 10mbps, so we always disable it. */
/* vp->full_bus_master_rx = 0; */
vp->full_bus_master_rx = (vp->capabilities & 0x20) ? 1 : 0;
return 0;
}
static struct isa_driver t515_driver __isa_driver = {
.type = NIC_DRIVER,
.name = "3C515",
.probe = t515_probe,
.ioaddrs = 0,
};

31
src/drivers/net/3c515.txt Normal file
View File

@@ -0,0 +1,31 @@
3c515.c -- 3COM 3C515 Fast Etherlink ISA 10/100BASE-TX driver for etherboot
Copyright (C) 2002 Timothy Legge <tlegge@rogers.com>
This driver is for the 3COM 3C515 Fast Etherlink ISA 10/100BASE-TX
REVISION HISTORY:
================
v0.10 4-17-2002 TJL Initial implementation.
v0.11 4-17-2002 TJL Cleanup of the code
v0.12 4-26-2002 TJL Added ISA Plug and Play for Non-PNP Bioses
v0.13 3-31-2003 TJL Fixed issue 1 and 2 below
The driver is heavily based on the work of others are referenced in the 3c515.c file.
ISA Plug and Play (ISAPNP) support has been added for Non-PNP Bioses. The ISAPNP code requires the defination of ISA_PNP as:
#define ISA_PNP
Issues:
=======
1) RESOLVED - When ISAPNP is defined, the etherboot probe is unable to find the card during the first probe. This is true even though the ISA PNP code actually found and activated the driver.
2) RESOLVED - When ISA_PNP is defined, the etherboot probe finds the incorrect MAC address for the card. However, when the linux kernel boots and loads the linux 3c515 driver the correct MAC address is found. This means that with ISA_PNP defined, you require both MAC addresses defined in the /etc/dhcpd.conf file. The first MAC address allows the driver to load the LTSP Linux kernel. The second allows the Linux dhclient to resolve its IP address.
3) Although the ISA PNP docs specify that the IRQ, DMA and IO Address needs to be assigned to the card before it is activated, Etherboot does not seem to care. Therefore the code does not assign the card with these values.
If you can help address any of thse issues, please feel free.
Timothy Legge
timlegge@users.sourceforge.net
April 9, 2003

550
src/drivers/net/3c595.c Normal file
View File

@@ -0,0 +1,550 @@
/*
* 3c595.c -- 3COM 3C595 Fast Etherlink III PCI driver for etherboot
*
* Copyright (C) 2000 Shusuke Nisiyama <shu@athena.qe.eng.hokudai.ac.jp>
* All rights reserved.
* Mar. 14, 2000
*
* This software may be used, modified, copied, distributed, and sold, in
* both source and binary form provided that the above copyright and these
* terms are retained. Under no circumstances are the authors responsible for
* the proper functioning of this software, nor do the authors assume any
* responsibility for damages incurred with its use.
*
* This code is based on Martin Renters' etherboot-4.4.3 3c509.c and
* Herb Peyerl's FreeBSD 3.4-RELEASE if_vx.c driver.
*
* Copyright (C) 1993-1994, David Greenman, Martin Renters.
* Copyright (C) 1993-1995, Andres Vega Garcia.
* Copyright (C) 1995, Serge Babkin.
*
* Copyright (c) 1994 Herb Peyerl <hpeyerl@novatel.ca>
*
* timlegge 08-24-2003 Add Multicast Support
*/
/* #define EDEBUG */
#include "etherboot.h"
#include "nic.h"
#include "pci.h"
#include "3c595.h"
#include "timer.h"
static unsigned short eth_nic_base;
static unsigned short vx_connector, vx_connectors;
static struct connector_entry {
int bit;
char *name;
} conn_tab[VX_CONNECTORS] = {
#define CONNECTOR_UTP 0
{ 0x08, "utp"},
#define CONNECTOR_AUI 1
{ 0x20, "aui"},
/* dummy */
{ 0, "???"},
#define CONNECTOR_BNC 3
{ 0x10, "bnc"},
#define CONNECTOR_TX 4
{ 0x02, "tx"},
#define CONNECTOR_FX 5
{ 0x04, "fx"},
#define CONNECTOR_MII 6
{ 0x40, "mii"},
{ 0, "???"}
};
static void vxgetlink(void);
static void vxsetlink(void);
/**************************************************************************
ETH_RESET - Reset adapter
***************************************************************************/
static void t595_reset(struct nic *nic)
{
int i;
/***********************************************************
Reset 3Com 595 card
*************************************************************/
/* stop card */
outw(RX_DISABLE, BASE + VX_COMMAND);
outw(RX_DISCARD_TOP_PACK, BASE + VX_COMMAND);
VX_BUSY_WAIT;
outw(TX_DISABLE, BASE + VX_COMMAND);
outw(STOP_TRANSCEIVER, BASE + VX_COMMAND);
udelay(8000);
outw(RX_RESET, BASE + VX_COMMAND);
VX_BUSY_WAIT;
outw(TX_RESET, BASE + VX_COMMAND);
VX_BUSY_WAIT;
outw(C_INTR_LATCH, BASE + VX_COMMAND);
outw(SET_RD_0_MASK, BASE + VX_COMMAND);
outw(SET_INTR_MASK, BASE + VX_COMMAND);
outw(SET_RX_FILTER, BASE + VX_COMMAND);
/*
* initialize card
*/
VX_BUSY_WAIT;
GO_WINDOW(0);
/* Disable the card */
/* outw(0, BASE + VX_W0_CONFIG_CTRL); */
/* Configure IRQ to none */
/* outw(SET_IRQ(0), BASE + VX_W0_RESOURCE_CFG); */
/* Enable the card */
/* outw(ENABLE_DRQ_IRQ, BASE + VX_W0_CONFIG_CTRL); */
GO_WINDOW(2);
/* Reload the ether_addr. */
for (i = 0; i < ETH_ALEN; i++)
outb(nic->node_addr[i], BASE + VX_W2_ADDR_0 + i);
outw(RX_RESET, BASE + VX_COMMAND);
VX_BUSY_WAIT;
outw(TX_RESET, BASE + VX_COMMAND);
VX_BUSY_WAIT;
/* Window 1 is operating window */
GO_WINDOW(1);
for (i = 0; i < 31; i++)
inb(BASE + VX_W1_TX_STATUS);
outw(SET_RD_0_MASK | S_CARD_FAILURE | S_RX_COMPLETE |
S_TX_COMPLETE | S_TX_AVAIL, BASE + VX_COMMAND);
outw(SET_INTR_MASK | S_CARD_FAILURE | S_RX_COMPLETE |
S_TX_COMPLETE | S_TX_AVAIL, BASE + VX_COMMAND);
/*
* Attempt to get rid of any stray interrupts that occured during
* configuration. On the i386 this isn't possible because one may
* already be queued. However, a single stray interrupt is
* unimportant.
*/
outw(ACK_INTR | 0xff, BASE + VX_COMMAND);
outw(SET_RX_FILTER | FIL_INDIVIDUAL |
FIL_BRDCST|FIL_MULTICAST, BASE + VX_COMMAND);
vxsetlink();
/*{
int i,j;
i = CONNECTOR_TX;
GO_WINDOW(3);
j = inl(BASE + VX_W3_INTERNAL_CFG) & ~INTERNAL_CONNECTOR_MASK;
outl(BASE + VX_W3_INTERNAL_CFG, j | (i <<INTERNAL_CONNECTOR_BITS));
GO_WINDOW(4);
outw(LINKBEAT_ENABLE, BASE + VX_W4_MEDIA_TYPE);
GO_WINDOW(1);
}*/
/* start tranciever and receiver */
outw(RX_ENABLE, BASE + VX_COMMAND);
outw(TX_ENABLE, BASE + VX_COMMAND);
}
/**************************************************************************
ETH_TRANSMIT - Transmit a frame
***************************************************************************/
static char padmap[] = {
0, 3, 2, 1};
static void t595_transmit(
struct nic *nic,
const char *d, /* Destination */
unsigned int t, /* Type */
unsigned int s, /* size */
const char *p) /* Packet */
{
register int len;
int pad;
int status;
#ifdef EDEBUG
printf("{l=%d,t=%hX}",s+ETH_HLEN,t);
#endif
/* swap bytes of type */
t= htons(t);
len=s+ETH_HLEN; /* actual length of packet */
pad = padmap[len & 3];
/*
* The 3c595 automatically pads short packets to minimum ethernet length,
* but we drop packets that are too large. Perhaps we should truncate
* them instead?
*/
if (len + pad > ETH_FRAME_LEN) {
return;
}
/* drop acknowledgements */
while(( status=inb(BASE + VX_W1_TX_STATUS) )& TXS_COMPLETE ) {
if(status & (TXS_UNDERRUN|TXS_MAX_COLLISION|TXS_STATUS_OVERFLOW)) {
outw(TX_RESET, BASE + VX_COMMAND);
outw(TX_ENABLE, BASE + VX_COMMAND);
}
outb(0x0, BASE + VX_W1_TX_STATUS);
}
while (inw(BASE + VX_W1_FREE_TX) < len + pad + 4) {
/* no room in FIFO */
}
outw(len, BASE + VX_W1_TX_PIO_WR_1);
outw(0x0, BASE + VX_W1_TX_PIO_WR_1); /* Second dword meaningless */
/* write packet */
outsw(BASE + VX_W1_TX_PIO_WR_1, d, ETH_ALEN/2);
outsw(BASE + VX_W1_TX_PIO_WR_1, nic->node_addr, ETH_ALEN/2);
outw(t, BASE + VX_W1_TX_PIO_WR_1);
outsw(BASE + VX_W1_TX_PIO_WR_1, p, s / 2);
if (s & 1)
outb(*(p+s - 1), BASE + VX_W1_TX_PIO_WR_1);
while (pad--)
outb(0, BASE + VX_W1_TX_PIO_WR_1); /* Padding */
/* wait for Tx complete */
while((inw(BASE + VX_STATUS) & S_COMMAND_IN_PROGRESS) != 0)
;
}
/**************************************************************************
ETH_POLL - Wait for a frame
***************************************************************************/
static int t595_poll(struct nic *nic, int retrieve)
{
/* common variables */
/* variables for 3C595 */
short status, cst;
register short rx_fifo;
cst=inw(BASE + VX_STATUS);
#ifdef EDEBUG
if(cst & 0x1FFF)
printf("-%hX-",cst);
#endif
if( (cst & S_RX_COMPLETE)==0 ) {
/* acknowledge everything */
outw(ACK_INTR | cst, BASE + VX_COMMAND);
outw(C_INTR_LATCH, BASE + VX_COMMAND);
return 0;
}
status = inw(BASE + VX_W1_RX_STATUS);
#ifdef EDEBUG
printf("*%hX*",status);
#endif
if (status & ERR_RX) {
outw(RX_DISCARD_TOP_PACK, BASE + VX_COMMAND);
return 0;
}
rx_fifo = status & RX_BYTES_MASK;
if (rx_fifo==0)
return 0;
if ( ! retrieve ) return 1;
/* read packet */
#ifdef EDEBUG
printf("[l=%d",rx_fifo);
#endif
insw(BASE + VX_W1_RX_PIO_RD_1, nic->packet, rx_fifo / 2);
if(rx_fifo & 1)
nic->packet[rx_fifo-1]=inb(BASE + VX_W1_RX_PIO_RD_1);
nic->packetlen=rx_fifo;
while(1) {
status = inw(BASE + VX_W1_RX_STATUS);
#ifdef EDEBUG
printf("*%hX*",status);
#endif
rx_fifo = status & RX_BYTES_MASK;
if(rx_fifo>0) {
insw(BASE + VX_W1_RX_PIO_RD_1, nic->packet+nic->packetlen, rx_fifo / 2);
if(rx_fifo & 1)
nic->packet[nic->packetlen+rx_fifo-1]=inb(BASE + VX_W1_RX_PIO_RD_1);
nic->packetlen+=rx_fifo;
#ifdef EDEBUG
printf("+%d",rx_fifo);
#endif
}
if(( status & RX_INCOMPLETE )==0) {
#ifdef EDEBUG
printf("=%d",nic->packetlen);
#endif
break;
}
udelay(1000);
}
/* acknowledge reception of packet */
outw(RX_DISCARD_TOP_PACK, BASE + VX_COMMAND);
while (inw(BASE + VX_STATUS) & S_COMMAND_IN_PROGRESS);
#ifdef EDEBUG
{
unsigned short type = 0; /* used by EDEBUG */
type = (nic->packet[12]<<8) | nic->packet[13];
if(nic->packet[0]+nic->packet[1]+nic->packet[2]+nic->packet[3]+nic->packet[4]+
nic->packet[5] == 0xFF*ETH_ALEN)
printf(",t=%hX,b]",type);
else
printf(",t=%hX]",type);
}
#endif
return 1;
}
/*************************************************************************
3Com 595 - specific routines
**************************************************************************/
static int
eeprom_rdy()
{
int i;
for (i = 0; is_eeprom_busy(BASE) && i < MAX_EEPROMBUSY; i++)
udelay(1000);
if (i >= MAX_EEPROMBUSY) {
/* printf("3c595: eeprom failed to come ready.\n"); */
printf("3c595: eeprom is busy.\n"); /* memory in EPROM is tight */
return (0);
}
return (1);
}
/*
* get_e: gets a 16 bits word from the EEPROM. we must have set the window
* before
*/
static int
get_e(offset)
int offset;
{
if (!eeprom_rdy())
return (0xffff);
outw(EEPROM_CMD_RD | offset, BASE + VX_W0_EEPROM_COMMAND);
if (!eeprom_rdy())
return (0xffff);
return (inw(BASE + VX_W0_EEPROM_DATA));
}
static void
vxgetlink(void)
{
int n, k;
GO_WINDOW(3);
vx_connectors = inw(BASE + VX_W3_RESET_OPT) & 0x7f;
for (n = 0, k = 0; k < VX_CONNECTORS; k++) {
if (vx_connectors & conn_tab[k].bit) {
if (n > 0) {
printf("/");
}
printf(conn_tab[k].name);
n++;
}
}
if (vx_connectors == 0) {
printf("no connectors!");
return;
}
GO_WINDOW(3);
vx_connector = (inl(BASE + VX_W3_INTERNAL_CFG)
& INTERNAL_CONNECTOR_MASK)
>> INTERNAL_CONNECTOR_BITS;
if (vx_connector & 0x10) {
vx_connector &= 0x0f;
printf("[*%s*]", conn_tab[vx_connector].name);
printf(": disable 'auto select' with DOS util!");
} else {
printf("[*%s*]", conn_tab[vx_connector].name);
}
}
static void
vxsetlink(void)
{
int i, j;
char *reason, *warning;
static char prev_conn = -1;
if (prev_conn == -1) {
prev_conn = vx_connector;
}
i = vx_connector; /* default in EEPROM */
reason = "default";
warning = 0;
if ((vx_connectors & conn_tab[vx_connector].bit) == 0) {
warning = "strange connector type in EEPROM.";
reason = "forced";
i = CONNECTOR_UTP;
}
if (warning != 0) {
printf("warning: %s\n", warning);
}
printf("selected %s. (%s)\n", conn_tab[i].name, reason);
/* Set the selected connector. */
GO_WINDOW(3);
j = inl(BASE + VX_W3_INTERNAL_CFG) & ~INTERNAL_CONNECTOR_MASK;
outl(j | (i <<INTERNAL_CONNECTOR_BITS), BASE + VX_W3_INTERNAL_CFG);
/* First, disable all. */
outw(STOP_TRANSCEIVER, BASE + VX_COMMAND);
udelay(8000);
GO_WINDOW(4);
outw(0, BASE + VX_W4_MEDIA_TYPE);
/* Second, enable the selected one. */
switch(i) {
case CONNECTOR_UTP:
GO_WINDOW(4);
outw(ENABLE_UTP, BASE + VX_W4_MEDIA_TYPE);
break;
case CONNECTOR_BNC:
outw(START_TRANSCEIVER,BASE + VX_COMMAND);
udelay(8000);
break;
case CONNECTOR_TX:
case CONNECTOR_FX:
GO_WINDOW(4);
outw(LINKBEAT_ENABLE, BASE + VX_W4_MEDIA_TYPE);
break;
default: /* AUI and MII fall here */
break;
}
GO_WINDOW(1);
}
static void t595_disable(struct dev *dev)
{
struct nic *nic = (struct nic *)dev;
t595_reset(nic);
outw(STOP_TRANSCEIVER, BASE + VX_COMMAND);
udelay(8000);
GO_WINDOW(4);
outw(0, BASE + VX_W4_MEDIA_TYPE);
GO_WINDOW(1);
}
static void t595_irq(struct nic *nic __unused, irq_action_t action __unused)
{
switch ( action ) {
case DISABLE :
break;
case ENABLE :
break;
case FORCE :
break;
}
}
/**************************************************************************
ETH_PROBE - Look for an adapter
***************************************************************************/
static int t595_probe(struct dev *dev, struct pci_device *pci)
{
struct nic *nic = (struct nic *)dev;
int i;
unsigned short *p;
if (pci->ioaddr == 0)
return 0;
/* eth_nic_base = probeaddrs[0] & ~3; */
eth_nic_base = pci->ioaddr;
nic->irqno = 0;
nic->ioaddr = pci->ioaddr & ~3;
GO_WINDOW(0);
outw(GLOBAL_RESET, BASE + VX_COMMAND);
VX_BUSY_WAIT;
vxgetlink();
/*
printf("\nEEPROM:");
for (i = 0; i < (EEPROMSIZE/2); i++) {
printf("%hX:", get_e(i));
}
printf("\n");
*/
/*
* Read the station address from the eeprom
*/
p = (unsigned short *) nic->node_addr;
for (i = 0; i < 3; i++) {
GO_WINDOW(0);
p[i] = htons(get_e(EEPROM_OEM_ADDR_0 + i));
GO_WINDOW(2);
outw(ntohs(p[i]), BASE + VX_W2_ADDR_0 + (i * 2));
}
printf("Ethernet address: %!\n", nic->node_addr);
t595_reset(nic);
dev->disable = t595_disable;
nic->poll = t595_poll;
nic->transmit = t595_transmit;
nic->irq = t595_irq;
return 1;
}
static struct pci_id t595_nics[] = {
PCI_ROM(0x10b7, 0x5900, "3c590", "3Com590"), /* Vortex 10Mbps */
PCI_ROM(0x10b7, 0x5950, "3c595", "3Com595"), /* Vortex 100baseTx */
PCI_ROM(0x10b7, 0x5951, "3c595-1", "3Com595"), /* Vortex 100baseT4 */
PCI_ROM(0x10b7, 0x5952, "3c595-2", "3Com595"), /* Vortex 100base-MII */
PCI_ROM(0x10b7, 0x9000, "3c900-tpo", "3Com900-TPO"), /* 10 Base TPO */
PCI_ROM(0x10b7, 0x9001, "3c900-t4", "3Com900-Combo"), /* 10/100 T4 */
PCI_ROM(0x10b7, 0x9004, "3c900b-tpo", "3Com900B-TPO"), /* 10 Base TPO */
PCI_ROM(0x10b7, 0x9005, "3c900b-combo", "3Com900B-Combo"), /* 10 Base Combo */
PCI_ROM(0x10b7, 0x9006, "3c900b-tpb2", "3Com900B-2/T"), /* 10 Base TP and Base2 */
PCI_ROM(0x10b7, 0x900a, "3c900b-fl", "3Com900B-FL"), /* 10 Base F */
PCI_ROM(0x10b7, 0x9800, "3c980-cyclone-1", "3Com980-Cyclone"), /* Cyclone */
PCI_ROM(0x10b7, 0x9805, "3c9805-1", "3Com9805"), /* Dual Port Server Cyclone */
PCI_ROM(0x10b7, 0x7646, "3csoho100-tx-1", "3CSOHO100-TX"), /* Hurricane */
PCI_ROM(0x10b7, 0x4500, "3c450-1", "3Com450 HomePNA Tornado"),
};
static struct pci_driver t595_driver __pci_driver = {
.type = NIC_DRIVER,
.name = "3C595",
.probe = t595_probe,
.ids = t595_nics,
.id_count = sizeof(t595_nics)/sizeof(t595_nics[0]),
.class = 0,
};
/*
* Local variables:
* c-basic-offset: 8
* End:
*/

435
src/drivers/net/3c595.h Normal file
View File

@@ -0,0 +1,435 @@
/*
* Copyright (c) 1993 Herb Peyerl (hpeyerl@novatel.ca) 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. The name
* of the author may not be used to endorse or promote products derived from
* this software without specific prior written permission
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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.
*
October 2, 1994
Modified by: Andres Vega Garcia
INRIA - Sophia Antipolis, France
e-mail: avega@sophia.inria.fr
finger: avega@pax.inria.fr
*/
/*
* Created from if_epreg.h by Fred Gray (fgray@rice.edu) to support the
* 3c590 family.
*/
/*
* Modified by Shusuke Nisiyama <shu@athena.qe.eng.hokudai.ac.jp>
* for etherboot
* Mar. 14, 2000
*/
/*
* Ethernet software status per interface.
*/
/*
* Some global constants
*/
#define TX_INIT_RATE 16
#define TX_INIT_MAX_RATE 64
#define RX_INIT_LATENCY 64
#define RX_INIT_EARLY_THRESH 64
#define MIN_RX_EARLY_THRESHF 16 /* not less than ether_header */
#define MIN_RX_EARLY_THRESHL 4
#define EEPROMSIZE 0x40
#define MAX_EEPROMBUSY 1000
#define VX_LAST_TAG 0xd7
#define VX_MAX_BOARDS 16
#define VX_ID_PORT 0x100
/*
* some macros to acces long named fields
*/
#define BASE (eth_nic_base)
/*
* Commands to read/write EEPROM trough EEPROM command register (Window 0,
* Offset 0xa)
*/
#define EEPROM_CMD_RD 0x0080 /* Read: Address required (5 bits) */
#define EEPROM_CMD_WR 0x0040 /* Write: Address required (5 bits) */
#define EEPROM_CMD_ERASE 0x00c0 /* Erase: Address required (5 bits) */
#define EEPROM_CMD_EWEN 0x0030 /* Erase/Write Enable: No data required */
#define EEPROM_BUSY (1<<15)
/*
* Some short functions, worth to let them be a macro
*/
/**************************************************************************
* *
* These define the EEPROM data structure. They are used in the probe
* function to verify the existence of the adapter after having sent
* the ID_Sequence.
*
* There are others but only the ones we use are defined here.
*
**************************************************************************/
#define EEPROM_NODE_ADDR_0 0x0 /* Word */
#define EEPROM_NODE_ADDR_1 0x1 /* Word */
#define EEPROM_NODE_ADDR_2 0x2 /* Word */
#define EEPROM_PROD_ID 0x3 /* 0x9[0-f]50 */
#define EEPROM_MFG_ID 0x7 /* 0x6d50 */
#define EEPROM_ADDR_CFG 0x8 /* Base addr */
#define EEPROM_RESOURCE_CFG 0x9 /* IRQ. Bits 12-15 */
#define EEPROM_OEM_ADDR_0 0xa /* Word */
#define EEPROM_OEM_ADDR_1 0xb /* Word */
#define EEPROM_OEM_ADDR_2 0xc /* Word */
#define EEPROM_SOFT_INFO_2 0xf /* Software information 2 */
#define NO_RX_OVN_ANOMALY (1<<5)
/**************************************************************************
* *
* These are the registers for the 3Com 3c509 and their bit patterns when *
* applicable. They have been taken out the the "EtherLink III Parallel *
* Tasking EISA and ISA Technical Reference" "Beta Draft 10/30/92" manual *
* from 3com. *
* *
**************************************************************************/
#define VX_COMMAND 0x0e /* Write. BASE+0x0e is always a
* command reg. */
#define VX_STATUS 0x0e /* Read. BASE+0x0e is always status
* reg. */
#define VX_WINDOW 0x0f /* Read. BASE+0x0f is always window
* reg. */
/*
* Window 0 registers. Setup.
*/
/* Write */
#define VX_W0_EEPROM_DATA 0x0c
#define VX_W0_EEPROM_COMMAND 0x0a
#define VX_W0_RESOURCE_CFG 0x08
#define VX_W0_ADDRESS_CFG 0x06
#define VX_W0_CONFIG_CTRL 0x04
/* Read */
#define VX_W0_PRODUCT_ID 0x02
#define VX_W0_MFG_ID 0x00
/*
* Window 1 registers. Operating Set.
*/
/* Write */
#define VX_W1_TX_PIO_WR_2 0x02
#define VX_W1_TX_PIO_WR_1 0x00
/* Read */
#define VX_W1_FREE_TX 0x0c
#define VX_W1_TX_STATUS 0x0b /* byte */
#define VX_W1_TIMER 0x0a /* byte */
#define VX_W1_RX_STATUS 0x08
#define VX_W1_RX_PIO_RD_2 0x02
#define VX_W1_RX_PIO_RD_1 0x00
/*
* Window 2 registers. Station Address Setup/Read
*/
/* Read/Write */
#define VX_W2_ADDR_5 0x05
#define VX_W2_ADDR_4 0x04
#define VX_W2_ADDR_3 0x03
#define VX_W2_ADDR_2 0x02
#define VX_W2_ADDR_1 0x01
#define VX_W2_ADDR_0 0x00
/*
* Window 3 registers. FIFO Management.
*/
/* Read */
#define VX_W3_INTERNAL_CFG 0x00
#define VX_W3_RESET_OPT 0x08
#define VX_W3_FREE_TX 0x0c
#define VX_W3_FREE_RX 0x0a
/*
* Window 4 registers. Diagnostics.
*/
/* Read/Write */
#define VX_W4_MEDIA_TYPE 0x0a
#define VX_W4_CTRLR_STATUS 0x08
#define VX_W4_NET_DIAG 0x06
#define VX_W4_FIFO_DIAG 0x04
#define VX_W4_HOST_DIAG 0x02
#define VX_W4_TX_DIAG 0x00
/*
* Window 5 Registers. Results and Internal status.
*/
/* Read */
#define VX_W5_READ_0_MASK 0x0c
#define VX_W5_INTR_MASK 0x0a
#define VX_W5_RX_FILTER 0x08
#define VX_W5_RX_EARLY_THRESH 0x06
#define VX_W5_TX_AVAIL_THRESH 0x02
#define VX_W5_TX_START_THRESH 0x00
/*
* Window 6 registers. Statistics.
*/
/* Read/Write */
#define TX_TOTAL_OK 0x0c
#define RX_TOTAL_OK 0x0a
#define TX_DEFERRALS 0x08
#define RX_FRAMES_OK 0x07
#define TX_FRAMES_OK 0x06
#define RX_OVERRUNS 0x05
#define TX_COLLISIONS 0x04
#define TX_AFTER_1_COLLISION 0x03
#define TX_AFTER_X_COLLISIONS 0x02
#define TX_NO_SQE 0x01
#define TX_CD_LOST 0x00
/****************************************
*
* Register definitions.
*
****************************************/
/*
* Command register. All windows.
*
* 16 bit register.
* 15-11: 5-bit code for command to be executed.
* 10-0: 11-bit arg if any. For commands with no args;
* this can be set to anything.
*/
#define GLOBAL_RESET (unsigned short) 0x0000 /* Wait at least 1ms
* after issuing */
#define WINDOW_SELECT (unsigned short) (0x1<<11)
#define START_TRANSCEIVER (unsigned short) (0x2<<11) /* Read ADDR_CFG reg to
* determine whether
* this is needed. If
* so; wait 800 uSec
* before using trans-
* ceiver. */
#define RX_DISABLE (unsigned short) (0x3<<11) /* state disabled on
* power-up */
#define RX_ENABLE (unsigned short) (0x4<<11)
#define RX_RESET (unsigned short) (0x5<<11)
#define RX_DISCARD_TOP_PACK (unsigned short) (0x8<<11)
#define TX_ENABLE (unsigned short) (0x9<<11)
#define TX_DISABLE (unsigned short) (0xa<<11)
#define TX_RESET (unsigned short) (0xb<<11)
#define REQ_INTR (unsigned short) (0xc<<11)
/*
* The following C_* acknowledge the various interrupts. Some of them don't
* do anything. See the manual.
*/
#define ACK_INTR (unsigned short) (0x6800)
# define C_INTR_LATCH (unsigned short) (ACK_INTR|0x1)
# define C_CARD_FAILURE (unsigned short) (ACK_INTR|0x2)
# define C_TX_COMPLETE (unsigned short) (ACK_INTR|0x4)
# define C_TX_AVAIL (unsigned short) (ACK_INTR|0x8)
# define C_RX_COMPLETE (unsigned short) (ACK_INTR|0x10)
# define C_RX_EARLY (unsigned short) (ACK_INTR|0x20)
# define C_INT_RQD (unsigned short) (ACK_INTR|0x40)
# define C_UPD_STATS (unsigned short) (ACK_INTR|0x80)
#define SET_INTR_MASK (unsigned short) (0xe<<11)
#define SET_RD_0_MASK (unsigned short) (0xf<<11)
#define SET_RX_FILTER (unsigned short) (0x10<<11)
# define FIL_INDIVIDUAL (unsigned short) (0x1)
# define FIL_MULTICAST (unsigned short) (0x02)
# define FIL_BRDCST (unsigned short) (0x04)
# define FIL_PROMISC (unsigned short) (0x08)
#define SET_RX_EARLY_THRESH (unsigned short) (0x11<<11)
#define SET_TX_AVAIL_THRESH (unsigned short) (0x12<<11)
#define SET_TX_START_THRESH (unsigned short) (0x13<<11)
#define STATS_ENABLE (unsigned short) (0x15<<11)
#define STATS_DISABLE (unsigned short) (0x16<<11)
#define STOP_TRANSCEIVER (unsigned short) (0x17<<11)
/*
* Status register. All windows.
*
* 15-13: Window number(0-7).
* 12: Command_in_progress.
* 11: reserved.
* 10: reserved.
* 9: reserved.
* 8: reserved.
* 7: Update Statistics.
* 6: Interrupt Requested.
* 5: RX Early.
* 4: RX Complete.
* 3: TX Available.
* 2: TX Complete.
* 1: Adapter Failure.
* 0: Interrupt Latch.
*/
#define S_INTR_LATCH (unsigned short) (0x1)
#define S_CARD_FAILURE (unsigned short) (0x2)
#define S_TX_COMPLETE (unsigned short) (0x4)
#define S_TX_AVAIL (unsigned short) (0x8)
#define S_RX_COMPLETE (unsigned short) (0x10)
#define S_RX_EARLY (unsigned short) (0x20)
#define S_INT_RQD (unsigned short) (0x40)
#define S_UPD_STATS (unsigned short) (0x80)
#define S_COMMAND_IN_PROGRESS (unsigned short) (0x1000)
#define VX_BUSY_WAIT while (inw(BASE + VX_STATUS) & S_COMMAND_IN_PROGRESS)
/* Address Config. Register.
* Window 0/Port 06
*/
#define ACF_CONNECTOR_BITS 14
#define ACF_CONNECTOR_UTP 0
#define ACF_CONNECTOR_AUI 1
#define ACF_CONNECTOR_BNC 3
#define INTERNAL_CONNECTOR_BITS 20
#define INTERNAL_CONNECTOR_MASK 0x01700000
/*
* FIFO Registers. RX Status.
*
* 15: Incomplete or FIFO empty.
* 14: 1: Error in RX Packet 0: Incomplete or no error.
* 13-11: Type of error.
* 1000 = Overrun.
* 1011 = Run Packet Error.
* 1100 = Alignment Error.
* 1101 = CRC Error.
* 1001 = Oversize Packet Error (>1514 bytes)
* 0010 = Dribble Bits.
* (all other error codes, no errors.)
*
* 10-0: RX Bytes (0-1514)
*/
#define ERR_INCOMPLETE (unsigned short) (0x8000)
#define ERR_RX (unsigned short) (0x4000)
#define ERR_MASK (unsigned short) (0x7800)
#define ERR_OVERRUN (unsigned short) (0x4000)
#define ERR_RUNT (unsigned short) (0x5800)
#define ERR_ALIGNMENT (unsigned short) (0x6000)
#define ERR_CRC (unsigned short) (0x6800)
#define ERR_OVERSIZE (unsigned short) (0x4800)
#define ERR_DRIBBLE (unsigned short) (0x1000)
/*
* TX Status.
*
* Reports the transmit status of a completed transmission. Writing this
* register pops the transmit completion stack.
*
* Window 1/Port 0x0b.
*
* 7: Complete
* 6: Interrupt on successful transmission requested.
* 5: Jabber Error (TP Only, TX Reset required. )
* 4: Underrun (TX Reset required. )
* 3: Maximum Collisions.
* 2: TX Status Overflow.
* 1-0: Undefined.
*
*/
#define TXS_COMPLETE 0x80
#define TXS_INTR_REQ 0x40
#define TXS_JABBER 0x20
#define TXS_UNDERRUN 0x10
#define TXS_MAX_COLLISION 0x8
#define TXS_STATUS_OVERFLOW 0x4
#define RS_AUI (1<<5)
#define RS_BNC (1<<4)
#define RS_UTP (1<<3)
#define RS_T4 (1<<0)
#define RS_TX (1<<1)
#define RS_FX (1<<2)
#define RS_MII (1<<6)
/*
* FIFO Status (Window 4)
*
* Supports FIFO diagnostics
*
* Window 4/Port 0x04.1
*
* 15: 1=RX receiving (RO). Set when a packet is being received
* into the RX FIFO.
* 14: Reserved
* 13: 1=RX underrun (RO). Generates Adapter Failure interrupt.
* Requires RX Reset or Global Reset command to recover.
* It is generated when you read past the end of a packet -
* reading past what has been received so far will give bad
* data.
* 12: 1=RX status overrun (RO). Set when there are already 8
* packets in the RX FIFO. While this bit is set, no additional
* packets are received. Requires no action on the part of
* the host. The condition is cleared once a packet has been
* read out of the RX FIFO.
* 11: 1=RX overrun (RO). Set when the RX FIFO is full (there
* may not be an overrun packet yet). While this bit is set,
* no additional packets will be received (some additional
* bytes can still be pending between the wire and the RX
* FIFO). Requires no action on the part of the host. The
* condition is cleared once a few bytes have been read out
* from the RX FIFO.
* 10: 1=TX overrun (RO). Generates adapter failure interrupt.
* Requires TX Reset or Global Reset command to recover.
* Disables Transmitter.
* 9-8: Unassigned.
* 7-0: Built in self test bits for the RX and TX FIFO's.
*/
#define FIFOS_RX_RECEIVING (unsigned short) 0x8000
#define FIFOS_RX_UNDERRUN (unsigned short) 0x2000
#define FIFOS_RX_STATUS_OVERRUN (unsigned short) 0x1000
#define FIFOS_RX_OVERRUN (unsigned short) 0x0800
#define FIFOS_TX_OVERRUN (unsigned short) 0x0400
/*
* Misc defines for various things.
*/
#define TAG_ADAPTER 0xd0
#define ACTIVATE_ADAPTER_TO_CONFIG 0xff
#define ENABLE_DRQ_IRQ 0x0001
#define MFG_ID 0x506d /* `TCM' */
#define PROD_ID 0x5090
#define GO_WINDOW(x) outw(WINDOW_SELECT|(x),BASE+VX_COMMAND)
#define JABBER_GUARD_ENABLE 0x40
#define LINKBEAT_ENABLE 0x80
#define ENABLE_UTP (JABBER_GUARD_ENABLE | LINKBEAT_ENABLE)
#define DISABLE_UTP 0x0
#define RX_BYTES_MASK (unsigned short) (0x07ff)
#define RX_ERROR 0x4000
#define RX_INCOMPLETE 0x8000
#define TX_INDICATE 1<<15
#define is_eeprom_busy(b) (inw((b)+VX_W0_EEPROM_COMMAND)&EEPROM_BUSY)
#define VX_IOSIZE 0x20
#define VX_CONNECTORS 8
/*
* Local variables:
* c-basic-offset: 8
* End:
*/

996
src/drivers/net/3c90x.c Normal file
View File

@@ -0,0 +1,996 @@
/*
* 3c90x.c -- This file implements the 3c90x driver for etherboot. Written
* by Greg Beeley, Greg.Beeley@LightSys.org. Modified by Steve Smith,
* Steve.Smith@Juno.Com. Alignment bug fix Neil Newell (nn@icenoir.net).
*
* This program Copyright (C) 1999 LightSys Technology Services, Inc.
* Portions Copyright (C) 1999 Steve Smith
*
* This program may be re-distributed in source or binary form, modified,
* sold, or copied for any purpose, provided that the above copyright message
* and this text are included with all source copies or derivative works, and
* provided that the above copyright message and this text are included in the
* documentation of any binary-only distributions. This program is distributed
* WITHOUT ANY WARRANTY, without even the warranty of FITNESS FOR A PARTICULAR
* PURPOSE or MERCHANTABILITY. Please read the associated documentation
* "3c90x.txt" before compiling and using this driver.
*
* --------
*
* Program written with the assistance of the 3com documentation for
* the 3c905B-TX card, as well as with some assistance from the 3c59x
* driver Donald Becker wrote for the Linux kernel, and with some assistance
* from the remainder of the Etherboot distribution.
*
* REVISION HISTORY:
*
* v0.10 1-26-1998 GRB Initial implementation.
* v0.90 1-27-1998 GRB System works.
* v1.00pre1 2-11-1998 GRB Got prom boot issue fixed.
* v2.0 9-24-1999 SCS Modified for 3c905 (from 3c905b code)
* Re-wrote poll and transmit for
* better error recovery and heavy
* network traffic operation
* v2.01 5-26-2003 NN Fixed driver alignment issue which
* caused system lockups if driver structures
* not 8-byte aligned.
*
*/
#include "etherboot.h"
#include "nic.h"
#include "pci.h"
#include "timer.h"
#define XCVR_MAGIC (0x5A00)
/** any single transmission fails after 16 collisions or other errors
** this is the number of times to retry the transmission -- this should
** be plenty
**/
#define XMIT_RETRIES 250
/*** Register definitions for the 3c905 ***/
enum Registers
{
regPowerMgmtCtrl_w = 0x7c, /** 905B Revision Only **/
regUpMaxBurst_w = 0x7a, /** 905B Revision Only **/
regDnMaxBurst_w = 0x78, /** 905B Revision Only **/
regDebugControl_w = 0x74, /** 905B Revision Only **/
regDebugData_l = 0x70, /** 905B Revision Only **/
regRealTimeCnt_l = 0x40, /** Universal **/
regUpBurstThresh_b = 0x3e, /** 905B Revision Only **/
regUpPoll_b = 0x3d, /** 905B Revision Only **/
regUpPriorityThresh_b = 0x3c, /** 905B Revision Only **/
regUpListPtr_l = 0x38, /** Universal **/
regCountdown_w = 0x36, /** Universal **/
regFreeTimer_w = 0x34, /** Universal **/
regUpPktStatus_l = 0x30, /** Universal with Exception, pg 130 **/
regTxFreeThresh_b = 0x2f, /** 90X Revision Only **/
regDnPoll_b = 0x2d, /** 905B Revision Only **/
regDnPriorityThresh_b = 0x2c, /** 905B Revision Only **/
regDnBurstThresh_b = 0x2a, /** 905B Revision Only **/
regDnListPtr_l = 0x24, /** Universal with Exception, pg 107 **/
regDmaCtrl_l = 0x20, /** Universal with Exception, pg 106 **/
/** **/
regIntStatusAuto_w = 0x1e, /** 905B Revision Only **/
regTxStatus_b = 0x1b, /** Universal with Exception, pg 113 **/
regTimer_b = 0x1a, /** Universal **/
regTxPktId_b = 0x18, /** 905B Revision Only **/
regCommandIntStatus_w = 0x0e, /** Universal (Command Variations) **/
};
/** following are windowed registers **/
enum Registers7
{
regPowerMgmtEvent_7_w = 0x0c, /** 905B Revision Only **/
regVlanEtherType_7_w = 0x04, /** 905B Revision Only **/
regVlanMask_7_w = 0x00, /** 905B Revision Only **/
};
enum Registers6
{
regBytesXmittedOk_6_w = 0x0c, /** Universal **/
regBytesRcvdOk_6_w = 0x0a, /** Universal **/
regUpperFramesOk_6_b = 0x09, /** Universal **/
regFramesDeferred_6_b = 0x08, /** Universal **/
regFramesRecdOk_6_b = 0x07, /** Universal with Exceptions, pg 142 **/
regFramesXmittedOk_6_b = 0x06, /** Universal **/
regRxOverruns_6_b = 0x05, /** Universal **/
regLateCollisions_6_b = 0x04, /** Universal **/
regSingleCollisions_6_b = 0x03, /** Universal **/
regMultipleCollisions_6_b = 0x02, /** Universal **/
regSqeErrors_6_b = 0x01, /** Universal **/
regCarrierLost_6_b = 0x00, /** Universal **/
};
enum Registers5
{
regIndicationEnable_5_w = 0x0c, /** Universal **/
regInterruptEnable_5_w = 0x0a, /** Universal **/
regTxReclaimThresh_5_b = 0x09, /** 905B Revision Only **/
regRxFilter_5_b = 0x08, /** Universal **/
regRxEarlyThresh_5_w = 0x06, /** Universal **/
regTxStartThresh_5_w = 0x00, /** Universal **/
};
enum Registers4
{
regUpperBytesOk_4_b = 0x0d, /** Universal **/
regBadSSD_4_b = 0x0c, /** Universal **/
regMediaStatus_4_w = 0x0a, /** Universal with Exceptions, pg 201 **/
regPhysicalMgmt_4_w = 0x08, /** Universal **/
regNetworkDiagnostic_4_w = 0x06, /** Universal with Exceptions, pg 203 **/
regFifoDiagnostic_4_w = 0x04, /** Universal with Exceptions, pg 196 **/
regVcoDiagnostic_4_w = 0x02, /** Undocumented? **/
};
enum Registers3
{
regTxFree_3_w = 0x0c, /** Universal **/
regRxFree_3_w = 0x0a, /** Universal with Exceptions, pg 125 **/
regResetMediaOptions_3_w = 0x08, /** Media Options on B Revision, **/
/** Reset Options on Non-B Revision **/
regMacControl_3_w = 0x06, /** Universal with Exceptions, pg 199 **/
regMaxPktSize_3_w = 0x04, /** 905B Revision Only **/
regInternalConfig_3_l = 0x00, /** Universal, different bit **/
/** definitions, pg 59 **/
};
enum Registers2
{
regResetOptions_2_w = 0x0c, /** 905B Revision Only **/
regStationMask_2_3w = 0x06, /** Universal with Exceptions, pg 127 **/
regStationAddress_2_3w = 0x00, /** Universal with Exceptions, pg 127 **/
};
enum Registers1
{
regRxStatus_1_w = 0x0a, /** 90X Revision Only, Pg 126 **/
};
enum Registers0
{
regEepromData_0_w = 0x0c, /** Universal **/
regEepromCommand_0_w = 0x0a, /** Universal **/
regBiosRomData_0_b = 0x08, /** 905B Revision Only **/
regBiosRomAddr_0_l = 0x04, /** 905B Revision Only **/
};
/*** The names for the eight register windows ***/
enum Windows
{
winPowerVlan7 = 0x07,
winStatistics6 = 0x06,
winTxRxControl5 = 0x05,
winDiagnostics4 = 0x04,
winTxRxOptions3 = 0x03,
winAddressing2 = 0x02,
winUnused1 = 0x01,
winEepromBios0 = 0x00,
};
/*** Command definitions for the 3c90X ***/
enum Commands
{
cmdGlobalReset = 0x00, /** Universal with Exceptions, pg 151 **/
cmdSelectRegisterWindow = 0x01, /** Universal **/
cmdEnableDcConverter = 0x02, /** **/
cmdRxDisable = 0x03, /** **/
cmdRxEnable = 0x04, /** Universal **/
cmdRxReset = 0x05, /** Universal **/
cmdStallCtl = 0x06, /** Universal **/
cmdTxEnable = 0x09, /** Universal **/
cmdTxDisable = 0x0A, /** **/
cmdTxReset = 0x0B, /** Universal **/
cmdRequestInterrupt = 0x0C, /** **/
cmdAcknowledgeInterrupt = 0x0D, /** Universal **/
cmdSetInterruptEnable = 0x0E, /** Universal **/
cmdSetIndicationEnable = 0x0F, /** Universal **/
cmdSetRxFilter = 0x10, /** Universal **/
cmdSetRxEarlyThresh = 0x11, /** **/
cmdSetTxStartThresh = 0x13, /** **/
cmdStatisticsEnable = 0x15, /** **/
cmdStatisticsDisable = 0x16, /** **/
cmdDisableDcConverter = 0x17, /** **/
cmdSetTxReclaimThresh = 0x18, /** **/
cmdSetHashFilterBit = 0x19, /** **/
};
/*** Values for int status register bitmask **/
#define INT_INTERRUPTLATCH (1<<0)
#define INT_HOSTERROR (1<<1)
#define INT_TXCOMPLETE (1<<2)
#define INT_RXCOMPLETE (1<<4)
#define INT_RXEARLY (1<<5)
#define INT_INTREQUESTED (1<<6)
#define INT_UPDATESTATS (1<<7)
#define INT_LINKEVENT (1<<8)
#define INT_DNCOMPLETE (1<<9)
#define INT_UPCOMPLETE (1<<10)
#define INT_CMDINPROGRESS (1<<12)
#define INT_WINDOWNUMBER (7<<13)
/*** TX descriptor ***/
typedef struct
{
unsigned int DnNextPtr;
unsigned int FrameStartHeader;
unsigned int HdrAddr;
unsigned int HdrLength;
unsigned int DataAddr;
unsigned int DataLength;
}
TXD __attribute__ ((aligned(8))); /* 64-bit aligned for bus mastering */
/*** RX descriptor ***/
typedef struct
{
unsigned int UpNextPtr;
unsigned int UpPktStatus;
unsigned int DataAddr;
unsigned int DataLength;
}
RXD __attribute__ ((aligned(8))); /* 64-bit aligned for bus mastering */
/*** Global variables ***/
static struct
{
unsigned char isBrev;
unsigned char CurrentWindow;
unsigned int IOAddr;
unsigned char HWAddr[ETH_ALEN];
TXD TransmitDPD;
RXD ReceiveUPD;
}
INF_3C90X;
/*** a3c90x_internal_IssueCommand: sends a command to the 3c90x card
***/
static int
a3c90x_internal_IssueCommand(int ioaddr, int cmd, int param)
{
unsigned int val;
/** Build the cmd. **/
val = cmd;
val <<= 11;
val |= param;
/** Send the cmd to the cmd register **/
outw(val, ioaddr + regCommandIntStatus_w);
/** Wait for the cmd to complete, if necessary **/
while (inw(ioaddr + regCommandIntStatus_w) & INT_CMDINPROGRESS);
return 0;
}
/*** a3c90x_internal_SetWindow: selects a register window set.
***/
static int
a3c90x_internal_SetWindow(int ioaddr, int window)
{
/** Window already as set? **/
if (INF_3C90X.CurrentWindow == window) return 0;
/** Issue the window command. **/
a3c90x_internal_IssueCommand(ioaddr, cmdSelectRegisterWindow, window);
INF_3C90X.CurrentWindow = window;
return 0;
}
/*** a3c90x_internal_ReadEeprom - read data from the serial eeprom.
***/
static unsigned short
a3c90x_internal_ReadEeprom(int ioaddr, int address)
{
unsigned short val;
/** Select correct window **/
a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winEepromBios0);
/** Make sure the eeprom isn't busy **/
while((1<<15) & inw(ioaddr + regEepromCommand_0_w));
/** Read the value. **/
outw(address + ((0x02)<<6), ioaddr + regEepromCommand_0_w);
while((1<<15) & inw(ioaddr + regEepromCommand_0_w));
val = inw(ioaddr + regEepromData_0_w);
return val;
}
#if 0
/*** a3c90x_internal_WriteEepromWord - write a physical word of
*** data to the onboard serial eeprom (not the BIOS prom, but the
*** nvram in the card that stores, among other things, the MAC
*** address).
***/
static int
a3c90x_internal_WriteEepromWord(int ioaddr, int address, unsigned short value)
{
/** Select register window **/
a3c90x_internal_SetWindow(ioaddr, winEepromBios0);
/** Verify Eeprom not busy **/
while((1<<15) & inw(ioaddr + regEepromCommand_0_w));
/** Issue WriteEnable, and wait for completion. **/
outw(0x30, ioaddr + regEepromCommand_0_w);
while((1<<15) & inw(ioaddr + regEepromCommand_0_w));
/** Issue EraseRegister, and wait for completion. **/
outw(address + ((0x03)<<6), ioaddr + regEepromCommand_0_w);
while((1<<15) & inw(ioaddr + regEepromCommand_0_w));
/** Send the new data to the eeprom, and wait for completion. **/
outw(value, ioaddr + regEepromData_0_w);
outw(0x30, ioaddr + regEepromCommand_0_w);
while((1<<15) & inw(ioaddr + regEepromCommand_0_w));
/** Burn the new data into the eeprom, and wait for completion. **/
outw(address + ((0x01)<<6), ioaddr + regEepromCommand_0_w);
while((1<<15) & inw(ioaddr + regEepromCommand_0_w));
return 0;
}
#endif
#if 0
/*** a3c90x_internal_WriteEeprom - write data to the serial eeprom,
*** and re-compute the eeprom checksum.
***/
static int
a3c90x_internal_WriteEeprom(int ioaddr, int address, unsigned short value)
{
int cksum = 0,v;
int i;
int maxAddress, cksumAddress;
if (INF_3C90X.isBrev)
{
maxAddress=0x1f;
cksumAddress=0x20;
}
else
{
maxAddress=0x16;
cksumAddress=0x17;
}
/** Write the value. **/
if (a3c90x_internal_WriteEepromWord(ioaddr, address, value) == -1)
return -1;
/** Recompute the checksum. **/
for(i=0;i<=maxAddress;i++)
{
v = a3c90x_internal_ReadEeprom(ioaddr, i);
cksum ^= (v & 0xFF);
cksum ^= ((v>>8) & 0xFF);
}
/** Write the checksum to the location in the eeprom **/
if (a3c90x_internal_WriteEepromWord(ioaddr, cksumAddress, cksum) == -1)
return -1;
return 0;
}
#endif
/*** a3c90x_reset: exported function that resets the card to its default
*** state. This is so the Linux driver can re-set the card up the way
*** it wants to. If CFG_3C90X_PRESERVE_XCVR is defined, then the reset will
*** not alter the selected transceiver that we used to download the boot
*** image.
***/
static void a3c90x_reset(void)
{
#ifdef CFG_3C90X_PRESERVE_XCVR
int cfg;
/** Read the current InternalConfig value. **/
a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winTxRxOptions3);
cfg = inl(INF_3C90X.IOAddr + regInternalConfig_3_l);
#endif
/** Send the reset command to the card **/
printf("Issuing RESET:\n");
a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdGlobalReset, 0);
/** wait for reset command to complete **/
while (inw(INF_3C90X.IOAddr + regCommandIntStatus_w) & INT_CMDINPROGRESS);
/** global reset command resets station mask, non-B revision cards
** require explicit reset of values
**/
a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winAddressing2);
outw(0, INF_3C90X.IOAddr + regStationMask_2_3w+0);
outw(0, INF_3C90X.IOAddr + regStationMask_2_3w+2);
outw(0, INF_3C90X.IOAddr + regStationMask_2_3w+4);
#ifdef CFG_3C90X_PRESERVE_XCVR
/** Re-set the original InternalConfig value from before reset **/
a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winTxRxOptions3);
outl(cfg, INF_3C90X.IOAddr + regInternalConfig_3_l);
/** enable DC converter for 10-Base-T **/
if ((cfg&0x0300) == 0x0300)
{
a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdEnableDcConverter, 0);
}
#endif
/** Issue transmit reset, wait for command completion **/
a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdTxReset, 0);
while (inw(INF_3C90X.IOAddr + regCommandIntStatus_w) & INT_CMDINPROGRESS)
;
if (! INF_3C90X.isBrev)
outb(0x01, INF_3C90X.IOAddr + regTxFreeThresh_b);
a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdTxEnable, 0);
/**
** reset of the receiver on B-revision cards re-negotiates the link
** takes several seconds (a computer eternity)
**/
if (INF_3C90X.isBrev)
a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdRxReset, 0x04);
else
a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdRxReset, 0x00);
while (inw(INF_3C90X.IOAddr + regCommandIntStatus_w) & INT_CMDINPROGRESS);
;
a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdRxEnable, 0);
a3c90x_internal_IssueCommand(INF_3C90X.IOAddr,
cmdSetInterruptEnable, 0);
/** enable rxComplete and txComplete **/
a3c90x_internal_IssueCommand(INF_3C90X.IOAddr,
cmdSetIndicationEnable, 0x0014);
/** acknowledge any pending status flags **/
a3c90x_internal_IssueCommand(INF_3C90X.IOAddr,
cmdAcknowledgeInterrupt, 0x661);
return;
}
/*** a3c90x_transmit: exported function that transmits a packet. Does not
*** return any particular status. Parameters are:
*** d[6] - destination address, ethernet;
*** t - protocol type (ARP, IP, etc);
*** s - size of the non-header part of the packet that needs transmitted;
*** p - the pointer to the packet data itself.
***/
static void
a3c90x_transmit(struct nic *nic __unused, const char *d, unsigned int t,
unsigned int s, const char *p)
{
struct eth_hdr
{
unsigned char dst_addr[ETH_ALEN];
unsigned char src_addr[ETH_ALEN];
unsigned short type;
} hdr;
unsigned char status;
unsigned i, retries;
for (retries=0; retries < XMIT_RETRIES ; retries++)
{
/** Stall the download engine **/
a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdStallCtl, 2);
/** Make sure the card is not waiting on us **/
inw(INF_3C90X.IOAddr + regCommandIntStatus_w);
inw(INF_3C90X.IOAddr + regCommandIntStatus_w);
while (inw(INF_3C90X.IOAddr+regCommandIntStatus_w) &
INT_CMDINPROGRESS)
;
/** Set the ethernet packet type **/
hdr.type = htons(t);
/** Copy the destination address **/
memcpy(hdr.dst_addr, d, ETH_ALEN);
/** Copy our MAC address **/
memcpy(hdr.src_addr, INF_3C90X.HWAddr, ETH_ALEN);
/** Setup the DPD (download descriptor) **/
INF_3C90X.TransmitDPD.DnNextPtr = 0;
/** set notification for transmission completion (bit 15) **/
INF_3C90X.TransmitDPD.FrameStartHeader = (s + sizeof(hdr)) | 0x8000;
INF_3C90X.TransmitDPD.HdrAddr = virt_to_bus(&hdr);
INF_3C90X.TransmitDPD.HdrLength = sizeof(hdr);
INF_3C90X.TransmitDPD.DataAddr = virt_to_bus(p);
INF_3C90X.TransmitDPD.DataLength = s + (1<<31);
/** Send the packet **/
outl(virt_to_bus(&(INF_3C90X.TransmitDPD)),
INF_3C90X.IOAddr + regDnListPtr_l);
/** End Stall and Wait for upload to complete. **/
a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdStallCtl, 3);
while(inl(INF_3C90X.IOAddr + regDnListPtr_l) != 0)
;
/** Wait for NIC Transmit to Complete **/
load_timer2(10*TICKS_PER_MS); /* Give it 10 ms */
while (!(inw(INF_3C90X.IOAddr + regCommandIntStatus_w)&0x0004) &&
timer2_running())
;
if (!(inw(INF_3C90X.IOAddr + regCommandIntStatus_w)&0x0004))
{
printf("3C90X: Tx Timeout\n");
continue;
}
status = inb(INF_3C90X.IOAddr + regTxStatus_b);
/** acknowledge transmit interrupt by writing status **/
outb(0x00, INF_3C90X.IOAddr + regTxStatus_b);
/** successful completion (sans "interrupt Requested" bit) **/
if ((status & 0xbf) == 0x80)
return;
printf("3C90X: Status (%hhX)\n", status);
/** check error codes **/
if (status & 0x02)
{
printf("3C90X: Tx Reclaim Error (%hhX)\n", status);
a3c90x_reset();
}
else if (status & 0x04)
{
printf("3C90X: Tx Status Overflow (%hhX)\n", status);
for (i=0; i<32; i++)
outb(0x00, INF_3C90X.IOAddr + regTxStatus_b);
/** must re-enable after max collisions before re-issuing tx **/
a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdTxEnable, 0);
}
else if (status & 0x08)
{
printf("3C90X: Tx Max Collisions (%hhX)\n", status);
/** must re-enable after max collisions before re-issuing tx **/
a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdTxEnable, 0);
}
else if (status & 0x10)
{
printf("3C90X: Tx Underrun (%hhX)\n", status);
a3c90x_reset();
}
else if (status & 0x20)
{
printf("3C90X: Tx Jabber (%hhX)\n", status);
a3c90x_reset();
}
else if ((status & 0x80) != 0x80)
{
printf("3C90X: Internal Error - Incomplete Transmission (%hhX)\n",
status);
a3c90x_reset();
}
}
/** failed after RETRY attempts **/
printf("Failed to send after %d retries\n", retries);
return;
}
/*** a3c90x_poll: exported routine that waits for a certain length of time
*** for a packet, and if it sees none, returns 0. This routine should
*** copy the packet to nic->packet if it gets a packet and set the size
*** in nic->packetlen. Return 1 if a packet was found.
***/
static int
a3c90x_poll(struct nic *nic, int retrieve)
{
int i, errcode;
if (!(inw(INF_3C90X.IOAddr + regCommandIntStatus_w)&0x0010))
{
return 0;
}
if ( ! retrieve ) return 1;
/** we don't need to acknowledge rxComplete -- the upload engine
** does it for us.
**/
/** Build the up-load descriptor **/
INF_3C90X.ReceiveUPD.UpNextPtr = 0;
INF_3C90X.ReceiveUPD.UpPktStatus = 0;
INF_3C90X.ReceiveUPD.DataAddr = virt_to_bus(nic->packet);
INF_3C90X.ReceiveUPD.DataLength = 1536 + (1<<31);
/** Submit the upload descriptor to the NIC **/
outl(virt_to_bus(&(INF_3C90X.ReceiveUPD)),
INF_3C90X.IOAddr + regUpListPtr_l);
/** Wait for upload completion (upComplete(15) or upError (14)) **/
for(i=0;i<40000;i++);
while((INF_3C90X.ReceiveUPD.UpPktStatus & ((1<<14) | (1<<15))) == 0)
for(i=0;i<40000;i++);
/** Check for Error (else we have good packet) **/
if (INF_3C90X.ReceiveUPD.UpPktStatus & (1<<14))
{
errcode = INF_3C90X.ReceiveUPD.UpPktStatus;
if (errcode & (1<<16))
printf("3C90X: Rx Overrun (%hX)\n",errcode>>16);
else if (errcode & (1<<17))
printf("3C90X: Runt Frame (%hX)\n",errcode>>16);
else if (errcode & (1<<18))
printf("3C90X: Alignment Error (%hX)\n",errcode>>16);
else if (errcode & (1<<19))
printf("3C90X: CRC Error (%hX)\n",errcode>>16);
else if (errcode & (1<<20))
printf("3C90X: Oversized Frame (%hX)\n",errcode>>16);
else
printf("3C90X: Packet error (%hX)\n",errcode>>16);
return 0;
}
/** Ok, got packet. Set length in nic->packetlen. **/
nic->packetlen = (INF_3C90X.ReceiveUPD.UpPktStatus & 0x1FFF);
return 1;
}
/*** a3c90x_disable: exported routine to disable the card. What's this for?
*** the eepro100.c driver didn't have one, so I just left this one empty too.
*** Ideas anyone?
*** Must turn off receiver at least so stray packets will not corrupt memory
*** [Ken]
***/
static void
a3c90x_disable(struct dev *dev __unused)
{
/* reset and disable merge */
a3c90x_reset();
/* Disable the receiver and transmitter. */
outw(cmdRxDisable, INF_3C90X.IOAddr + regCommandIntStatus_w);
outw(cmdTxDisable, INF_3C90X.IOAddr + regCommandIntStatus_w);
}
static void a3c90x_irq(struct nic *nic __unused, irq_action_t action __unused)
{
switch ( action ) {
case DISABLE :
break;
case ENABLE :
break;
case FORCE :
break;
}
}
/*** a3c90x_probe: exported routine to probe for the 3c905 card and perform
*** initialization. If this routine is called, the pci functions did find the
*** card. We just have to init it here.
***/
static int a3c90x_probe(struct dev *dev, struct pci_device *pci)
{
struct nic *nic = (struct nic *)dev;
int i, c;
unsigned short eeprom[0x21];
unsigned int cfg;
unsigned int mopt;
unsigned int mstat;
unsigned short linktype;
#define HWADDR_OFFSET 10
if (pci->ioaddr == 0)
return 0;
adjust_pci_device(pci);
nic->ioaddr = pci->ioaddr & ~3;
nic->irqno = 0;
INF_3C90X.IOAddr = pci->ioaddr & ~3;
INF_3C90X.CurrentWindow = 255;
switch (a3c90x_internal_ReadEeprom(INF_3C90X.IOAddr, 0x03))
{
case 0x9000: /** 10 Base TPO **/
case 0x9001: /** 10/100 T4 **/
case 0x9050: /** 10/100 TPO **/
case 0x9051: /** 10 Base Combo **/
INF_3C90X.isBrev = 0;
break;
case 0x9004: /** 10 Base TPO **/
case 0x9005: /** 10 Base Combo **/
case 0x9006: /** 10 Base TPO and Base2 **/
case 0x900A: /** 10 Base FL **/
case 0x9055: /** 10/100 TPO **/
case 0x9056: /** 10/100 T4 **/
case 0x905A: /** 10 Base FX **/
default:
INF_3C90X.isBrev = 1;
break;
}
/** Load the EEPROM contents **/
if (INF_3C90X.isBrev)
{
for(i=0;i<=0x20;i++)
{
eeprom[i] = a3c90x_internal_ReadEeprom(INF_3C90X.IOAddr, i);
}
#ifdef CFG_3C90X_BOOTROM_FIX
/** Set xcvrSelect in InternalConfig in eeprom. **/
/* only necessary for 3c905b revision cards with boot PROM bug!!! */
a3c90x_internal_WriteEeprom(INF_3C90X.IOAddr, 0x13, 0x0160);
#endif
#ifdef CFG_3C90X_XCVR
if (CFG_3C90X_XCVR == 255)
{
/** Clear the LanWorks register **/
a3c90x_internal_WriteEeprom(INF_3C90X.IOAddr, 0x16, 0);
}
else
{
/** Set the selected permanent-xcvrSelect in the
** LanWorks register
**/
a3c90x_internal_WriteEeprom(INF_3C90X.IOAddr, 0x16,
XCVR_MAGIC + ((CFG_3C90X_XCVR) & 0x000F));
}
#endif
}
else
{
for(i=0;i<=0x17;i++)
{
eeprom[i] = a3c90x_internal_ReadEeprom(INF_3C90X.IOAddr, i);
}
}
/** Print identification message **/
printf("\n\n3C90X Driver 2.00 "
"Copyright 1999 LightSys Technology Services, Inc.\n"
"Portions Copyright 1999 Steve Smith\n");
printf("Provided with ABSOLUTELY NO WARRANTY.\n");
#ifdef CFG_3C90X_BOOTROM_FIX
if (INF_3C90X.isBrev)
{
printf("NOTE: 3c905b bootrom fix enabled; has side "
"effects. See 3c90x.txt for info.\n");
}
#endif
printf("-------------------------------------------------------"
"------------------------\n");
/** Retrieve the Hardware address and print it on the screen. **/
INF_3C90X.HWAddr[0] = eeprom[HWADDR_OFFSET + 0]>>8;
INF_3C90X.HWAddr[1] = eeprom[HWADDR_OFFSET + 0]&0xFF;
INF_3C90X.HWAddr[2] = eeprom[HWADDR_OFFSET + 1]>>8;
INF_3C90X.HWAddr[3] = eeprom[HWADDR_OFFSET + 1]&0xFF;
INF_3C90X.HWAddr[4] = eeprom[HWADDR_OFFSET + 2]>>8;
INF_3C90X.HWAddr[5] = eeprom[HWADDR_OFFSET + 2]&0xFF;
printf("MAC Address = %!\n", INF_3C90X.HWAddr);
/* Test if the link is good, if not continue */
a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winDiagnostics4);
mstat = inw(INF_3C90X.IOAddr + regMediaStatus_4_w);
if((mstat & (1<<11)) == 0) {
printf("Valid link not established\n");
return 0;
}
/** Program the MAC address into the station address registers **/
a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winAddressing2);
outw(htons(eeprom[HWADDR_OFFSET + 0]), INF_3C90X.IOAddr + regStationAddress_2_3w);
outw(htons(eeprom[HWADDR_OFFSET + 1]), INF_3C90X.IOAddr + regStationAddress_2_3w+2);
outw(htons(eeprom[HWADDR_OFFSET + 2]), INF_3C90X.IOAddr + regStationAddress_2_3w+4);
outw(0, INF_3C90X.IOAddr + regStationMask_2_3w+0);
outw(0, INF_3C90X.IOAddr + regStationMask_2_3w+2);
outw(0, INF_3C90X.IOAddr + regStationMask_2_3w+4);
/** Fill in our entry in the etherboot arp table **/
for(i=0;i<ETH_ALEN;i++)
nic->node_addr[i] = (eeprom[HWADDR_OFFSET + i/2] >> (8*((i&1)^1))) & 0xff;
/** Read the media options register, print a message and set default
** xcvr.
**
** Uses Media Option command on B revision, Reset Option on non-B
** revision cards -- same register address
**/
a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winTxRxOptions3);
mopt = inw(INF_3C90X.IOAddr + regResetMediaOptions_3_w);
/** mask out VCO bit that is defined as 10baseFL bit on B-rev cards **/
if (! INF_3C90X.isBrev)
{
mopt &= 0x7F;
}
printf("Connectors present: ");
c = 0;
linktype = 0x0008;
if (mopt & 0x01)
{
printf("%s100Base-T4",(c++)?", ":"");
linktype = 0x0006;
}
if (mopt & 0x04)
{
printf("%s100Base-FX",(c++)?", ":"");
linktype = 0x0005;
}
if (mopt & 0x10)
{
printf("%s10Base-2",(c++)?", ":"");
linktype = 0x0003;
}
if (mopt & 0x20)
{
printf("%sAUI",(c++)?", ":"");
linktype = 0x0001;
}
if (mopt & 0x40)
{
printf("%sMII",(c++)?", ":"");
linktype = 0x0006;
}
if ((mopt & 0xA) == 0xA)
{
printf("%s10Base-T / 100Base-TX",(c++)?", ":"");
linktype = 0x0008;
}
else if ((mopt & 0xA) == 0x2)
{
printf("%s100Base-TX",(c++)?", ":"");
linktype = 0x0008;
}
else if ((mopt & 0xA) == 0x8)
{
printf("%s10Base-T",(c++)?", ":"");
linktype = 0x0008;
}
printf(".\n");
/** Determine transceiver type to use, depending on value stored in
** eeprom 0x16
**/
if (INF_3C90X.isBrev)
{
if ((eeprom[0x16] & 0xFF00) == XCVR_MAGIC)
{
/** User-defined **/
linktype = eeprom[0x16] & 0x000F;
}
}
else
{
#ifdef CFG_3C90X_XCVR
if (CFG_3C90X_XCVR != 255)
linktype = CFG_3C90X_XCVR;
#endif /* CFG_3C90X_XCVR */
/** I don't know what MII MAC only mode is!!! **/
if (linktype == 0x0009)
{
if (INF_3C90X.isBrev)
printf("WARNING: MII External MAC Mode only supported on B-revision "
"cards!!!!\nFalling Back to MII Mode\n");
linktype = 0x0006;
}
}
/** enable DC converter for 10-Base-T **/
if (linktype == 0x0003)
{
a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdEnableDcConverter, 0);
}
/** Set the link to the type we just determined. **/
a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winTxRxOptions3);
cfg = inl(INF_3C90X.IOAddr + regInternalConfig_3_l);
cfg &= ~(0xF<<20);
cfg |= (linktype<<20);
outl(cfg, INF_3C90X.IOAddr + regInternalConfig_3_l);
/** Now that we set the xcvr type, reset the Tx and Rx, re-enable. **/
a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdTxReset, 0x00);
while (inw(INF_3C90X.IOAddr + regCommandIntStatus_w) & INT_CMDINPROGRESS)
;
if (!INF_3C90X.isBrev)
outb(0x01, INF_3C90X.IOAddr + regTxFreeThresh_b);
a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdTxEnable, 0);
/**
** reset of the receiver on B-revision cards re-negotiates the link
** takes several seconds (a computer eternity)
**/
if (INF_3C90X.isBrev)
a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdRxReset, 0x04);
else
a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdRxReset, 0x00);
while (inw(INF_3C90X.IOAddr + regCommandIntStatus_w) & INT_CMDINPROGRESS)
;
/** Set the RX filter = receive only individual pkts & multicast & bcast. **/
a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdSetRxFilter, 0x01 + 0x02 + 0x04);
a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdRxEnable, 0);
/**
** set Indication and Interrupt flags , acknowledge any IRQ's
**/
a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdSetInterruptEnable, 0);
a3c90x_internal_IssueCommand(INF_3C90X.IOAddr,
cmdSetIndicationEnable, 0x0014);
a3c90x_internal_IssueCommand(INF_3C90X.IOAddr,
cmdAcknowledgeInterrupt, 0x661);
/** Set our exported functions **/
dev->disable = a3c90x_disable;
nic->poll = a3c90x_poll;
nic->transmit = a3c90x_transmit;
nic->irq = a3c90x_irq;
return 1;
}
static struct pci_id a3c90x_nics[] = {
/* Original 90x revisions: */
PCI_ROM(0x10b7, 0x9000, "3c905-tpo", "3Com900-TPO"), /* 10 Base TPO */
PCI_ROM(0x10b7, 0x9001, "3c905-t4", "3Com900-Combo"), /* 10/100 T4 */
PCI_ROM(0x10b7, 0x9050, "3c905-tpo100", "3Com905-TX"), /* 100 Base TX / 10/100 TPO */
PCI_ROM(0x10b7, 0x9051, "3c905-combo", "3Com905-T4"), /* 100 Base T4 / 10 Base Combo */
/* Newer 90xB revisions: */
PCI_ROM(0x10b7, 0x9004, "3c905b-tpo", "3Com900B-TPO"), /* 10 Base TPO */
PCI_ROM(0x10b7, 0x9005, "3c905b-combo", "3Com900B-Combo"), /* 10 Base Combo */
PCI_ROM(0x10b7, 0x9006, "3c905b-tpb2", "3Com900B-2/T"), /* 10 Base TP and Base2 */
PCI_ROM(0x10b7, 0x900a, "3c905b-fl", "3Com900B-FL"), /* 10 Base FL */
PCI_ROM(0x10b7, 0x9055, "3c905b-tpo100", "3Com905B-TX"), /* 10/100 TPO */
PCI_ROM(0x10b7, 0x9056, "3c905b-t4", "3Com905B-T4"), /* 10/100 T4 */
PCI_ROM(0x10b7, 0x9058, "3c905b-9058", "3Com905B-9058"), /* Cyclone 10/100/BNC */
PCI_ROM(0x10b7, 0x905a, "3c905b-fx", "3Com905B-FL"), /* 100 Base FX / 10 Base FX */
/* Newer 90xC revision: */
PCI_ROM(0x10b7, 0x9200, "3c905c-tpo", "3Com905C-TXM"), /* 10/100 TPO (3C905C-TXM) */
PCI_ROM(0x10b7, 0x9202, "3c920b-emb-ati", "3c920B-EMB-WNM (ATI Radeon 9100 IGP)"), /* 3c920B-EMB-WNM (ATI Radeon 9100 IGP) */
PCI_ROM(0x10b7, 0x9210, "3c920b-emb-wnm","3Com20B-EMB WNM"),
PCI_ROM(0x10b7, 0x9800, "3c980", "3Com980-Cyclone"), /* Cyclone */
PCI_ROM(0x10b7, 0x9805, "3c9805", "3Com9805"), /* Dual Port Server Cyclone */
PCI_ROM(0x10b7, 0x7646, "3csoho100-tx", "3CSOHO100-TX"), /* Hurricane */
PCI_ROM(0x10b7, 0x4500, "3c450", "3Com450 HomePNA Tornado"),
PCI_ROM(0x10b7, 0x1201, "3c982a", "3Com982A"),
PCI_ROM(0x10b7, 0x1202, "3c982b", "3Com982B"),
};
static struct pci_driver a3c90x_driver __pci_driver = {
.type = NIC_DRIVER,
.name = "3C90X",
.probe = a3c90x_probe,
.ids = a3c90x_nics,
.id_count = sizeof(a3c90x_nics)/sizeof(a3c90x_nics[0]),
.class = 0,
};

307
src/drivers/net/3c90x.txt Normal file
View File

@@ -0,0 +1,307 @@
Instructions for use of the 3C90X driver for EtherBoot
Original 3C905B support by:
Greg Beeley (Greg.Beeley@LightSys.org),
LightSys Technology Services, Inc.
February 11, 1999
Updates for 3C90X family by:
Steve Smith (steve.smith@juno.com)
October 1, 1999
Minor documentation updates by
Greg Beeley (Greg.Beeley@LightSys.org)
March 29, 2000
-------------------------------------------------------------------------------
I OVERVIEW
The 3c90X series ethernet cards are a group of high-performance busmaster
DMA cards from 3Com. This particular driver supports both the 3c90x and
the 3c90xB revision cards. 3C90xC family support has been tested to some
degree but not extensively.
Here's the licensing information:
This program Copyright (C) 1999 LightSys Technology Services, Inc.
Portions Copyright (C) 1999 Steve Smith.
This program may be re-distributed in source or binary form, modified,
sold, or copied for any purpose, provided that the above copyright message
and this text are included with all source copies or derivative works, and
provided that the above copyright message and this text are included in the
documentation of any binary-only distributions. This program is
distributed WITHOUT ANY WARRANTY, without even the warranty of FITNESS FOR
A PARTICULAR PURPOSE or MERCHANTABILITY. Please read the associated
documentation "3c90x.txt" before compiling and using this driver.
II FLASH PROMS
The 3c90xB cards, according to the 3Com documentation, only accept the
following flash memory chips:
Atmel AT29C512 (64 kilobyte)
Atmel AT29C010 (128 kilobyte)
The 3c90x cards, according to the 3Com documentation, accept the
following flash memory chips capacities:
64 kb (8 kB)
128 kb (16 kB)
256 kb (32 kB) and
512 kb (64 kB)
Atmel AT29C512 (64 kilobyte) chips are specifically listed for both
adapters, but flashing on the 3c905b cards would only be supported
through the Atmel parts. Any device, of the supported size, should
be supported when programmed by a dedicated PROM programmer (e.g.
not the card).
To use this driver in such a PROM, visit Atmel's web site and download
their .PDF file containing a list of their distributors. Contact the
distributors for pricing information. The prices are quite reasonable
(about $3 US each for the 64 kB part), and are comparable to what one would
expect for similarly sized standard EPROMs. And, the flash chips are much
easier to work with, as they don't need to be UV-erased to be reprogrammed.
The 3C905B card actually provides a method to program the flash memory
while it is resident on board the card itself; if someone would like to
write a small DOS program to do the programming, I can provide the
information about the registers and so forth.
A utility program, 3c90xutil, is provided with Etherboot in the 'contrib'
directory that allows for the on-board flashing of the ROM while Linux
is running. The program has been successfully used under Linux, but I
have heard problem reports of its use under FreeBSD. Anyone willing to
make it work under FreeBSD is more than welcome to do so!
You also have the option of using EPROM chips - the 3C905B-TX-NM has been
successfully tested with 27C256 (32kB) and 27C512 (64kB) chips with a
specified access time of 100ns and faster.
III GENERAL USE
Normally, the basic procedure for using this driver is as follows:
1. Run the 3c90xcfg program on the driver diskette to enable the
boot PROM and set it to 64k or 128k, as appropriate.
2. Build the appropriate 3c90x.fd0 or 3c90x.fd0 floppy image with
possibly the value CFG_3C90X_XCVR defined to the transceiver type that
you want to use (i.e., 10/100 rj45, AUI, coax, MII).
3. Run the floppy image on the PC to be network booted, to get
it configured, and to verify that it will boot properly.
4. Build the 3c90x.rom or 3c90x.lzrom PROM image and program
it into the flash or EPROM memory chip.
5. Put the PROM in the ethernet card, boot and enable 'boot from
network first' in the system BIOS, save and reboot.
Here are some issues to be aware of:
1. If you experience crashes or different behaviour when using the
boot PROM, add the setting CFG_3C90X_BOOTROM_FIX and go through the
steps 2-5 above. This works around a bug in some 3c905B cards (see
below), but has some side-effects which may not be desirable.
Please note that you have to boot off a floppy (not PROM!) once for
this fix to take effect.
2. The possible need to manually set the CFG_3C90X_XCVR value to
configure the transceiver type. Values are listed below.
3. The possible need to define CFG_3C90X_PRESERVE_XCVR for use in
operating systems that don't intelligently determine the
transceiver type.
Some things that are on the 'To-Do' list, perhaps for me, but perhaps
for any other volunteers out there:
1. Extend the driver to fully implement the auto-select
algorithm if the card has multiple media ports.
2. Fix any bugs in the code <grin>....
3. Extend the driver to support the 3c905c revision cards
"officially". Right now, the support has been primarily empirical
and not based on 3c905C documentation.
Now for the details....
This driver has been tested on roughly 300 systems. The main two
configuration issues to contend with are:
1. Ensure that PCI Busmastering is enabled for the adapter (configured
in the CMOS setup)
2. Some systems don't work properly with the adapter when plug and
play OS is enabled; I always set it to "No" or "Disabled" -- this makes
it easier and really doesn't adversely affect anything.
Roughly 95% of the systems worked when configured properly. A few
have issues with booting locally once the boot PROM has been installed
(this number has been less than 2%). Other configuration issues that
to check:
1. Newer BIOS's actually work correctly with the network boot order.
Set the network adapter first. Most older BIOS's automatically go to
the network boot PROM first.
2. For systems where the adapter was already installed and is just
having the PROM installed, try setting the "reset configuration data"
to yes in the CMOS setup if the BIOS isn't seen at first. If your BIOS
doesn't have this option, remove the card, start the system, shut down,
install the card and restart (or switch to a different PCI slot).
3. Make sure the CMOS security settings aren't preventing a boot.
The 3c905B cards have a significant 'bug' that relates to the flash prom:
unless the card is set internally to the MII transceiver, it will only
read the first 8k of the PROM image. Don't ask why -- it seems really
obscure, but it has to do with the way they mux'd the address lines
from the PCI bus to the ROM. Unfortunately, most of us are not using
MII transceivers, and even the .lzrom image ends up being just a little
bit larger than 8k. Note that the workaround for this is disabled by
default, because the Windows NT 4.0 driver does not like it (no packets
are transmitted).
So, the solution that I've used is to internally set the card's nvram
configuration to use MII when it boots. The 3c905b driver does this
automatically. This way, the 16k prom image can be loaded into memory,
and then the 3c905b driver can set the temporary configuration of the
card to an appropriate value, either configurable by the user or chosen
by the driver.
To enable the 3c905B bugfix, which is necessary for these cards when
booting from the Flash ROM, define -DCFG_3C90X_BOOTROM_FIX when building,
create a floppy image and boot it once.
Thereafter, the card should accept the larger prom image.
The driver should choose an appropriate transceiver on the card. However,
if it doesn't on your card or if you need to, for instance, set your
card to 10mbps when connected to an unmanaged 10/100 hub, you can specify
which transceiver you want to use. To do this, build the 3c905b.fd0
image with -DCFG_3C90X_XCVR=x, where 'x' is one of the following
values:
0 10Base-T
1 10mbps AUI
3 10Base-2 (thinnet/coax)
4 100Base-TX
5 100Base-FX
6 MII
8 Auto-negotiation 10Base-T / 100Base-TX (usually the default)
9 MII External MAC Mode
255 Allow driver to choose an 'appropriate' media port.
Then proceed from step 2 in the above 'general use' instructions. The
.rom image can be built with CFG_3C90X_XCVR set to a value, but you
normally don't want to do this, since it is easier to change the
transceiver type by rebuilding a new floppy, changing the BIOS to floppy
boot, booting, and then changing the BIOS back to network boot. If
CFG_3C90X_XCVR is not set in a particular build, it just uses the
current configuration (either its 'best guess' or whatever the stored
CFG_3C90X_XCVR value was from the last time it was set).
[[ Note for the more technically inclined: The CFG_3C90X_XCVR value is
programmed into a register in the card's NVRAM that was reserved for
LanWorks PROM images to use. When the driver boots, the card comes
up in MII mode, and the driver checks the LanWorks register to find
out if the user specified a transceiver type. If it finds that
information, it uses that, otherwise it picks a transceiver that the
card has based on the 3c905b's MediaOptions register. This driver isn't
quite smart enough to always determine which media port is actually
_connected_; maybe someone else would like to take on that task (it
actually involves sending a self-directed packet and seeing if it
comes back. IF it does, that port is connected). ]]
Another issue to keep in mind is that it is possible that some OS'es
might not be happy with the way I've handled the PROM-image hack with
setting MII mode on bootup. Linux 2.0.35 does not have this problem.
Behavior of other systems may vary. The 3com documentation specifically
says that, at least with the card that I have, the device driver in the
OS should auto-select the media port, so other drivers should work fine
with this 'hack'. However, if yours doesn't seem to, you can try defining
CFG_3C90X_PRESERVE_XCVR when building to cause Etherboot to keep the
working setting (that allowed the bootp/tftp process) across the eth_reset
operation.
IV FOR DEVELOPERS....
If you would like to fix/extend/etc. this driver, feel free to do so; just
be sure you can test the modified version on the 3c905B-TX cards that the
driver was originally designed for. This section of this document gives
some information that might be relevant to a programmer.
A. Main Entry Point
a3c90x_probe is the main entry point for this driver. It is referred
to in an array in 'config.c'.
B. Other Important Functions
The functions a3c90x_transmit, a3c90x_poll, a3c90x_reset, and
a3c90x_disable are static functions that EtherBoot finds out about
as a result of a3c90x_probe setting entries in the nic structure
for them. The EtherBoot framework does not use interrupts. It is
polled. All transmit and receive operations are initiated by the
etherboot framework, not by an interrupt or by the driver.
C. Internal Functions
The following functions are internal to the driver:
a3c90x_internal_IssueCommand - sends a command to the 3c905b card.
a3c90x_internal_SetWindow - shifts between one of eight register
windows onboard the 3c90x. The bottom 16 bytes of the card's
I/O space are multiplexed among 128 bytes, only 16 of which are
visible at any one time. This SetWindow function selects one of
the eight sets.
a3c90x_internal_ReadEeprom - reads a word (16 bits) from the
card's onboard nvram. This is NOT the BIOS boot rom. This is
where the card stores such things as its hardware address.
a3c90x_internal_WriteEeprom - writes a word (16 bits) to the
card's nvram, and recomputes the eeprom checksum.
a3c90x_internal_WriteEepromWord - writes a word (16 bits) to the
card's nvram. Used by the above routine.
a3c90x_internal_WriteEepromWord - writes a word (16 bits) to the
card's nvram. Used by the above routine.
D. Globals
All global variables are inside a global structure named INF_3C90X.
So, wherever you see that structure referenced, you know the variable
is a global. Just keeps things a little neater.
E. Enumerations
There are quite a few enumerated type definitions for registers and
so forth, many for registers that I didn't even touch in the driver.
Register types start with 'reg', window numbers (for SetWindow)
start with 'win', and commands (for IssueCommand) start with 'cmd'.
Register offsets also include an indication in the name as to the
size of the register (_b = byte, _w = word, _l = long), and which
window the register is in, if it is windowed (0-7).
F. Why the 'a3c90x' name?
I had to come up with a letter at the beginning of all of the
identifiers, since 3com so conveniently had their name start with a
number. Another driver used 't' (for 'three'?); I chose 'a' for
no reason at all.
Addendum by Jorge L. deLyra <delyra@latt.if.usp.br>, 22Nov2000 re
working around the 3C905 hardware bug mentioned above:
Use this floppy to fix any 3COM model 3C905B PCI 10/100 Ethernet cards
that fail to load and run the boot program the first time around. If
they have a "Lucent" rather than a "Broadcom" chipset these cards have
a configuration bug that causes a hang when trying to load the boot
program from the PROM, if you try to use them right out of the box.
The boot program in this floppy is the file named 3c905b-tpo100.rom
from Etherboot version 4.6.10, compiled with the bugfix parameter
CFG_3C90X_BOOTROM_FIX
You have to take the chip off the card and boot the system once using
this floppy. Once loaded from the floppy, the boot program will access
the card and change some setting in it, correcting the problem. After
that you may use either this boot program or the normal one, compiled
without this bugfix parameter, to boot the machine from the PROM chip.
[Any recent Etherboot version should do, not just 4.6.10 - Ed.]

720
src/drivers/net/cs89x0.c Normal file
View File

@@ -0,0 +1,720 @@
#ifdef ALLMULTI
#error multicast support is not yet implemented
#endif
/* cs89x0.c: A Crystal Semiconductor CS89[02]0 driver for etherboot. */
/*
Permission is granted to distribute the enclosed cs89x0.[ch] driver
only in conjunction with the Etherboot package. The code is
ordinarily distributed under the GPL.
Russ Nelson, January 2000
ChangeLog:
Thu Dec 6 22:40:00 1996 Markus Gutschke <gutschk@math.uni-muenster.de>
* disabled all "advanced" features; this should make the code more reliable
* reorganized the reset function
* always reset the address port, so that autoprobing will continue working
* some cosmetic changes
* 2.5
Thu Dec 5 21:00:00 1996 Markus Gutschke <gutschk@math.uni-muenster.de>
* tested the code against a CS8900 card
* lots of minor bug fixes and adjustments
* this is the first release, that actually works! it still requires some
changes in order to be more tolerant to different environments
* 4
Fri Nov 22 23:00:00 1996 Markus Gutschke <gutschk@math.uni-muenster.de>
* read the manuals for the CS89x0 chipsets and took note of all the
changes that will be neccessary in order to adapt Russel Nelson's code
to the requirements of a BOOT-Prom
* 6
Thu Nov 19 22:00:00 1996 Markus Gutschke <gutschk@math.uni-muenster.de>
* Synched with Russel Nelson's current code (v1.00)
* 2
Thu Nov 12 18:00:00 1996 Markus Gutschke <gutschk@math.uni-muenster.de>
* Cleaned up some of the code and tried to optimize the code size.
* 1.5
Sun Nov 10 16:30:00 1996 Markus Gutschke <gutschk@math.uni-muenster.de>
* First experimental release. This code compiles fine, but I
have no way of testing whether it actually works.
* I did not (yet) bother to make the code 16bit aware, so for
the time being, it will only work for Etherboot/32.
* 12
*/
#include "etherboot.h"
#include "nic.h"
#include "isa.h"
#include "cs89x0.h"
#ifndef EMBEDDED
static unsigned short eth_nic_base;
#else
static unsigned long eth_nic_base;
#endif
static unsigned long eth_mem_start;
static unsigned short eth_irqno;
static unsigned short eth_cs_type; /* one of: CS8900, CS8920, CS8920M */
static unsigned short eth_auto_neg_cnf;
static unsigned short eth_adapter_cnf;
static unsigned short eth_linectl;
/*************************************************************************
CS89x0 - specific routines
**************************************************************************/
static inline int readreg(int portno)
{
outw(portno, eth_nic_base + ADD_PORT);
return inw(eth_nic_base + DATA_PORT);
}
static inline void writereg(int portno, int value)
{
outw(portno, eth_nic_base + ADD_PORT);
outw(value, eth_nic_base + DATA_PORT);
return;
}
/*************************************************************************
EEPROM access
**************************************************************************/
static int wait_eeprom_ready(void)
{
unsigned long tmo = currticks() + 4*TICKS_PER_SEC;
/* check to see if the EEPROM is ready, a timeout is used -
just in case EEPROM is ready when SI_BUSY in the
PP_SelfST is clear */
while(readreg(PP_SelfST) & SI_BUSY) {
if (currticks() >= tmo)
return -1; }
return 0;
}
static int get_eeprom_data(int off, int len, unsigned short *buffer)
{
int i;
#ifdef EDEBUG
printf("\ncs: EEPROM data from %hX for %hX:",off,len);
#endif
for (i = 0; i < len; i++) {
if (wait_eeprom_ready() < 0)
return -1;
/* Now send the EEPROM read command and EEPROM location
to read */
writereg(PP_EECMD, (off + i) | EEPROM_READ_CMD);
if (wait_eeprom_ready() < 0)
return -1;
buffer[i] = readreg(PP_EEData);
#ifdef EDEBUG
if (!(i%10))
printf("\ncs: ");
printf("%hX ", buffer[i]);
#endif
}
#ifdef EDEBUG
putchar('\n');
#endif
return(0);
}
static int get_eeprom_chksum(int off __unused, int len, unsigned short *buffer)
{
int i, cksum;
cksum = 0;
for (i = 0; i < len; i++)
cksum += buffer[i];
cksum &= 0xffff;
if (cksum == 0)
return 0;
return -1;
}
/*************************************************************************
Activate all of the available media and probe for network
**************************************************************************/
static void clrline(void)
{
int i;
putchar('\r');
for (i = 79; i--; ) putchar(' ');
printf("\rcs: ");
return;
}
static void control_dc_dc(int on_not_off)
{
unsigned int selfcontrol;
unsigned long tmo = currticks() + TICKS_PER_SEC;
/* control the DC to DC convertor in the SelfControl register. */
selfcontrol = HCB1_ENBL; /* Enable the HCB1 bit as an output */
if (((eth_adapter_cnf & A_CNF_DC_DC_POLARITY) != 0) ^ on_not_off)
selfcontrol |= HCB1;
else
selfcontrol &= ~HCB1;
writereg(PP_SelfCTL, selfcontrol);
/* Wait for the DC/DC converter to power up - 1000ms */
while (currticks() < tmo);
return;
}
static int detect_tp(void)
{
unsigned long tmo;
/* Turn on the chip auto detection of 10BT/ AUI */
clrline(); printf("attempting %s:","TP");
/* If connected to another full duplex capable 10-Base-T card
the link pulses seem to be lost when the auto detect bit in
the LineCTL is set. To overcome this the auto detect bit
will be cleared whilst testing the 10-Base-T interface.
This would not be necessary for the sparrow chip but is
simpler to do it anyway. */
writereg(PP_LineCTL, eth_linectl &~ AUI_ONLY);
control_dc_dc(0);
/* Delay for the hardware to work out if the TP cable is
present - 150ms */
for (tmo = currticks() + 4; currticks() < tmo; );
if ((readreg(PP_LineST) & LINK_OK) == 0)
return 0;
if (eth_cs_type != CS8900) {
writereg(PP_AutoNegCTL, eth_auto_neg_cnf & AUTO_NEG_MASK);
if ((eth_auto_neg_cnf & AUTO_NEG_BITS) == AUTO_NEG_ENABLE) {
printf(" negotiating duplex... ");
while (readreg(PP_AutoNegST) & AUTO_NEG_BUSY) {
if (currticks() - tmo > 40*TICKS_PER_SEC) {
printf("time out ");
break;
}
}
}
if (readreg(PP_AutoNegST) & FDX_ACTIVE)
printf("using full duplex");
else
printf("using half duplex");
}
return A_CNF_MEDIA_10B_T;
}
/* send a test packet - return true if carrier bits are ok */
static int send_test_pkt(struct nic *nic)
{
static unsigned char testpacket[] = { 0,0,0,0,0,0, 0,0,0,0,0,0,
0, 46, /*A 46 in network order */
0, 0, /*DSAP=0 & SSAP=0 fields */
0xf3,0 /*Control (Test Req+P bit set)*/ };
unsigned long tmo;
writereg(PP_LineCTL, readreg(PP_LineCTL) | SERIAL_TX_ON);
memcpy(testpacket, nic->node_addr, ETH_ALEN);
memcpy(testpacket+ETH_ALEN, nic->node_addr, ETH_ALEN);
outw(TX_AFTER_ALL, eth_nic_base + TX_CMD_PORT);
outw(ETH_ZLEN, eth_nic_base + TX_LEN_PORT);
/* Test to see if the chip has allocated memory for the packet */
for (tmo = currticks() + 2;
(readreg(PP_BusST) & READY_FOR_TX_NOW) == 0; )
if (currticks() >= tmo)
return(0);
/* Write the contents of the packet */
outsw(eth_nic_base + TX_FRAME_PORT, testpacket,
(ETH_ZLEN+1)>>1);
printf(" sending test packet ");
/* wait a couple of timer ticks for packet to be received */
for (tmo = currticks() + 2; currticks() < tmo; );
if ((readreg(PP_TxEvent) & TX_SEND_OK_BITS) == TX_OK) {
printf("succeeded");
return 1;
}
printf("failed");
return 0;
}
static int detect_aui(struct nic *nic)
{
clrline(); printf("attempting %s:","AUI");
control_dc_dc(0);
writereg(PP_LineCTL, (eth_linectl & ~AUTO_AUI_10BASET) | AUI_ONLY);
if (send_test_pkt(nic)) {
return A_CNF_MEDIA_AUI; }
else
return 0;
}
static int detect_bnc(struct nic *nic)
{
clrline(); printf("attempting %s:","BNC");
control_dc_dc(1);
writereg(PP_LineCTL, (eth_linectl & ~AUTO_AUI_10BASET) | AUI_ONLY);
if (send_test_pkt(nic)) {
return A_CNF_MEDIA_10B_2; }
else
return 0;
}
/**************************************************************************
ETH_RESET - Reset adapter
***************************************************************************/
static void cs89x0_reset(struct nic *nic)
{
int i;
unsigned long reset_tmo;
writereg(PP_SelfCTL, readreg(PP_SelfCTL) | POWER_ON_RESET);
/* wait for two ticks; that is 2*55ms */
for (reset_tmo = currticks() + 2; currticks() < reset_tmo; );
if (eth_cs_type != CS8900) {
/* Hardware problem requires PNP registers to be reconfigured
after a reset */
if (eth_irqno != 0xFFFF) {
outw(PP_CS8920_ISAINT, eth_nic_base + ADD_PORT);
outb(eth_irqno, eth_nic_base + DATA_PORT);
outb(0, eth_nic_base + DATA_PORT + 1); }
if (eth_mem_start) {
outw(PP_CS8920_ISAMemB, eth_nic_base + ADD_PORT);
outb((eth_mem_start >> 8) & 0xff, eth_nic_base + DATA_PORT);
outb((eth_mem_start >> 24) & 0xff, eth_nic_base + DATA_PORT + 1); } }
/* Wait until the chip is reset */
for (reset_tmo = currticks() + 2;
(readreg(PP_SelfST) & INIT_DONE) == 0 &&
currticks() < reset_tmo; );
/* disable interrupts and memory accesses */
writereg(PP_BusCTL, 0);
/* set the ethernet address */
for (i=0; i < ETH_ALEN/2; i++)
writereg(PP_IA+i*2,
nic->node_addr[i*2] |
(nic->node_addr[i*2+1] << 8));
/* receive only error free packets addressed to this card */
writereg(PP_RxCTL, DEF_RX_ACCEPT);
/* do not generate any interrupts on receive operations */
writereg(PP_RxCFG, 0);
/* do not generate any interrupts on transmit operations */
writereg(PP_TxCFG, 0);
/* do not generate any interrupts on buffer operations */
writereg(PP_BufCFG, 0);
/* reset address port, so that autoprobing will keep working */
outw(PP_ChipID, eth_nic_base + ADD_PORT);
return;
}
/**************************************************************************
ETH_TRANSMIT - Transmit a frame
***************************************************************************/
static void cs89x0_transmit(
struct nic *nic,
const char *d, /* Destination */
unsigned int t, /* Type */
unsigned int s, /* size */
const char *p) /* Packet */
{
unsigned long tmo;
int sr;
/* does this size have to be rounded??? please,
somebody have a look in the specs */
if ((sr = ((s + ETH_HLEN + 1)&~1)) < ETH_ZLEN)
sr = ETH_ZLEN;
retry:
/* initiate a transmit sequence */
outw(TX_AFTER_ALL, eth_nic_base + TX_CMD_PORT);
outw(sr, eth_nic_base + TX_LEN_PORT);
/* Test to see if the chip has allocated memory for the packet */
if ((readreg(PP_BusST) & READY_FOR_TX_NOW) == 0) {
/* Oops... this should not happen! */
printf("cs: unable to send packet; retrying...\n");
for (tmo = currticks() + 5*TICKS_PER_SEC; currticks() < tmo; );
cs89x0_reset(nic);
goto retry; }
/* Write the contents of the packet */
outsw(eth_nic_base + TX_FRAME_PORT, d, ETH_ALEN/2);
outsw(eth_nic_base + TX_FRAME_PORT, nic->node_addr,
ETH_ALEN/2);
outw(((t >> 8)&0xFF)|(t << 8), eth_nic_base + TX_FRAME_PORT);
outsw(eth_nic_base + TX_FRAME_PORT, p, (s+1)/2);
for (sr = sr/2 - (s+1)/2 - ETH_ALEN - 1; sr-- > 0;
outw(0, eth_nic_base + TX_FRAME_PORT));
/* wait for transfer to succeed */
for (tmo = currticks()+5*TICKS_PER_SEC;
(s = readreg(PP_TxEvent)&~0x1F) == 0 && currticks() < tmo;)
/* nothing */ ;
if ((s & TX_SEND_OK_BITS) != TX_OK) {
printf("\ntransmission error %#hX\n", s);
}
return;
}
/**************************************************************************
ETH_POLL - Wait for a frame
***************************************************************************/
static int cs89x0_poll(struct nic *nic, int retrieve)
{
int status;
status = readreg(PP_RxEvent);
if ((status & RX_OK) == 0)
return(0);
if ( ! retrieve ) return 1;
status = inw(eth_nic_base + RX_FRAME_PORT);
nic->packetlen = inw(eth_nic_base + RX_FRAME_PORT);
insw(eth_nic_base + RX_FRAME_PORT, nic->packet, nic->packetlen >> 1);
if (nic->packetlen & 1)
nic->packet[nic->packetlen-1] = inw(eth_nic_base + RX_FRAME_PORT);
return 1;
}
static void cs89x0_disable(struct dev *dev)
{
struct nic *nic = (struct nic *)dev;
cs89x0_reset(nic);
}
static void cs89x0_irq(struct nic *nic __unused, irq_action_t action __unused)
{
switch ( action ) {
case DISABLE :
break;
case ENABLE :
break;
case FORCE :
break;
}
}
/**************************************************************************
ETH_PROBE - Look for an adapter
***************************************************************************/
static int cs89x0_probe(struct dev *dev, unsigned short *probe_addrs __unused)
{
struct nic *nic = (struct nic *)dev;
static const unsigned int netcard_portlist[] = {
#ifdef CS_SCAN
CS_SCAN,
#else /* use "conservative" default values for autoprobing */
#ifndef EMBEDDED
0x300,0x320,0x340,0x200,0x220,0x240,
0x260,0x280,0x2a0,0x2c0,0x2e0,
/* if that did not work, then be more aggressive */
0x301,0x321,0x341,0x201,0x221,0x241,
0x261,0x281,0x2a1,0x2c1,0x2e1,
#else
0x01000300,
#endif
#endif
0};
int i, result = -1;
unsigned rev_type = 0, ioaddr, ioidx, isa_cnf, cs_revision;
unsigned short eeprom_buff[CHKSUM_LEN];
for (ioidx = 0; (ioaddr=netcard_portlist[ioidx++]) != 0; ) {
/* if they give us an odd I/O address, then do ONE write to
the address port, to get it back to address zero, where we
expect to find the EISA signature word. */
if (ioaddr & 1) {
ioaddr &= ~1;
if ((inw(ioaddr + ADD_PORT) & ADD_MASK) != ADD_SIG)
continue;
outw(PP_ChipID, ioaddr + ADD_PORT);
}
if (inw(ioaddr + DATA_PORT) != CHIP_EISA_ID_SIG)
continue;
eth_nic_base = ioaddr;
/* get the chip type */
rev_type = readreg(PRODUCT_ID_ADD);
eth_cs_type = rev_type &~ REVISON_BITS;
cs_revision = ((rev_type & REVISON_BITS) >> 8) + 'A';
printf("\ncs: cs89%c0%s rev %c, base %#hX",
eth_cs_type==CS8900?'0':'2',
eth_cs_type==CS8920M?"M":"",
cs_revision,
eth_nic_base);
#ifndef EMBEDDED
/* First check to see if an EEPROM is attached*/
if ((readreg(PP_SelfST) & EEPROM_PRESENT) == 0) {
printf("\ncs: no EEPROM...\n");
outw(PP_ChipID, eth_nic_base + ADD_PORT);
continue; }
else if (get_eeprom_data(START_EEPROM_DATA,CHKSUM_LEN,
eeprom_buff) < 0) {
printf("\ncs: EEPROM read failed...\n");
outw(PP_ChipID, eth_nic_base + ADD_PORT);
continue; }
else if (get_eeprom_chksum(START_EEPROM_DATA,CHKSUM_LEN,
eeprom_buff) < 0) {
printf("\ncs: EEPROM checksum bad...\n");
outw(PP_ChipID, eth_nic_base + ADD_PORT);
continue; }
/* get transmission control word but keep the
autonegotiation bits */
eth_auto_neg_cnf = eeprom_buff[AUTO_NEG_CNF_OFFSET/2];
/* Store adapter configuration */
eth_adapter_cnf = eeprom_buff[ADAPTER_CNF_OFFSET/2];
/* Store ISA configuration */
isa_cnf = eeprom_buff[ISA_CNF_OFFSET/2];
/* store the initial memory base address */
eth_mem_start = eeprom_buff[PACKET_PAGE_OFFSET/2] << 8;
printf("%s%s%s, addr ",
(eth_adapter_cnf & A_CNF_10B_T)?", RJ-45":"",
(eth_adapter_cnf & A_CNF_AUI)?", AUI":"",
(eth_adapter_cnf & A_CNF_10B_2)?", BNC":"");
/* If this is a CS8900 then no pnp soft */
if (eth_cs_type != CS8900 &&
/* Check if the ISA IRQ has been set */
(i = readreg(PP_CS8920_ISAINT) & 0xff,
(i != 0 && i < CS8920_NO_INTS)))
eth_irqno = i;
else {
i = isa_cnf & INT_NO_MASK;
if (eth_cs_type == CS8900) {
/* the table that follows is dependent
upon how you wired up your cs8900
in your system. The table is the
same as the cs8900 engineering demo
board. irq_map also depends on the
contents of the table. Also see
write_irq, which is the reverse
mapping of the table below. */
if (i < 4) i = "\012\013\014\005"[i];
else printf("\ncs: BUG: isa_config is %d\n", i); }
eth_irqno = i; }
/* Retrieve and print the ethernet address. */
for (i=0; i<ETH_ALEN; i++) {
nic->node_addr[i] = ((unsigned char *)eeprom_buff)[i];
}
printf("%!\n", nic->node_addr);
#endif
#ifdef EMBEDDED
/* Retrieve and print the ethernet address. */
{
unsigned char MAC_HW_ADDR[6]={MAC_HW_ADDR_DRV};
memcpy(nic->node_addr, MAC_HW_ADDR, 6);
}
printf("\n%!\n", nic->node_addr);
eth_adapter_cnf = A_CNF_10B_T | A_CNF_MEDIA_10B_T;
eth_auto_neg_cnf = EE_AUTO_NEG_ENABLE | IMM_BIT;
#endif
#ifndef EMBEDDED
/* Set the LineCTL quintuplet based on adapter
configuration read from EEPROM */
if ((eth_adapter_cnf & A_CNF_EXTND_10B_2) &&
(eth_adapter_cnf & A_CNF_LOW_RX_SQUELCH))
eth_linectl = LOW_RX_SQUELCH;
else
eth_linectl = 0;
/* check to make sure that they have the "right"
hardware available */
switch(eth_adapter_cnf & A_CNF_MEDIA_TYPE) {
case A_CNF_MEDIA_10B_T: result = eth_adapter_cnf & A_CNF_10B_T;
break;
case A_CNF_MEDIA_AUI: result = eth_adapter_cnf & A_CNF_AUI;
break;
case A_CNF_MEDIA_10B_2: result = eth_adapter_cnf & A_CNF_10B_2;
break;
default: result = eth_adapter_cnf & (A_CNF_10B_T | A_CNF_AUI |
A_CNF_10B_2);
}
if (!result) {
printf("cs: EEPROM is configured for unavailable media\n");
error:
writereg(PP_LineCTL, readreg(PP_LineCTL) &
~(SERIAL_TX_ON | SERIAL_RX_ON));
outw(PP_ChipID, eth_nic_base + ADD_PORT);
continue;
}
#endif
/* Initialize the card for probing of the attached media */
cs89x0_reset(nic);
/* set the hardware to the configured choice */
switch(eth_adapter_cnf & A_CNF_MEDIA_TYPE) {
case A_CNF_MEDIA_10B_T:
result = detect_tp();
if (!result) {
clrline();
printf("10Base-T (RJ-45%s",
") has no cable\n"); }
/* check "ignore missing media" bit */
if (eth_auto_neg_cnf & IMM_BIT)
/* Yes! I don't care if I see a link pulse */
result = A_CNF_MEDIA_10B_T;
break;
case A_CNF_MEDIA_AUI:
result = detect_aui(nic);
if (!result) {
clrline();
printf("10Base-5 (AUI%s",
") has no cable\n"); }
/* check "ignore missing media" bit */
if (eth_auto_neg_cnf & IMM_BIT)
/* Yes! I don't care if I see a carrrier */
result = A_CNF_MEDIA_AUI;
break;
case A_CNF_MEDIA_10B_2:
result = detect_bnc(nic);
if (!result) {
clrline();
printf("10Base-2 (BNC%s",
") has no cable\n"); }
/* check "ignore missing media" bit */
if (eth_auto_neg_cnf & IMM_BIT)
/* Yes! I don't care if I can xmit a packet */
result = A_CNF_MEDIA_10B_2;
break;
case A_CNF_MEDIA_AUTO:
writereg(PP_LineCTL, eth_linectl | AUTO_AUI_10BASET);
if (eth_adapter_cnf & A_CNF_10B_T)
if ((result = detect_tp()) != 0)
break;
if (eth_adapter_cnf & A_CNF_AUI)
if ((result = detect_aui(nic)) != 0)
break;
if (eth_adapter_cnf & A_CNF_10B_2)
if ((result = detect_bnc(nic)) != 0)
break;
clrline(); printf("no media detected\n");
goto error;
}
clrline();
switch(result) {
case 0: printf("no network cable attached to configured media\n");
goto error;
case A_CNF_MEDIA_10B_T: printf("using 10Base-T (RJ-45)\n");
break;
case A_CNF_MEDIA_AUI: printf("using 10Base-5 (AUI)\n");
break;
case A_CNF_MEDIA_10B_2: printf("using 10Base-2 (BNC)\n");
break;
}
/* Turn on both receive and transmit operations */
writereg(PP_LineCTL, readreg(PP_LineCTL) | SERIAL_RX_ON |
SERIAL_TX_ON);
break;
#ifdef EMBEDDED
error:
writereg(PP_LineCTL, readreg(PP_LineCTL) &
~(SERIAL_TX_ON | SERIAL_RX_ON));
outw(PP_ChipID, eth_nic_base + ADD_PORT);
continue;
#endif
}
if (ioaddr == 0)
return (0);
nic->irqno = 0;
nic->ioaddr = ioaddr;
dev->disable = cs89x0_disable;
nic->poll = cs89x0_poll;
nic->transmit = cs89x0_transmit;
nic->irq = cs89x0_irq;
/* Based on PnP ISA map */
dev->devid.vendor_id = htons(ISAPNP_VENDOR('C','S','C'));
dev->devid.device_id = htons(0x0007);
return 1;
}
static struct isa_driver cs89x0_driver __isa_driver = {
.type = NIC_DRIVER,
.name = "CS89x0",
.probe = cs89x0_probe,
.ioaddrs = 0,
};
/*
* Local variables:
* c-basic-offset: 8
* End:
*/

461
src/drivers/net/cs89x0.h Normal file
View File

@@ -0,0 +1,461 @@
/* Copyright, 1988-1992, Russell Nelson, Crynwr Software
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, version 1.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#define PP_ChipID 0x0000 /* offset 0h -> Corp -ID */
/* offset 2h -> Model/Product Number */
/* offset 3h -> Chip Revision Number */
#define PP_ISAIOB 0x0020 /* IO base address */
#define PP_CS8900_ISAINT 0x0022 /* ISA interrupt select */
#define PP_CS8920_ISAINT 0x0370 /* ISA interrupt select */
#define PP_CS8900_ISADMA 0x0024 /* ISA Rec DMA channel */
#define PP_CS8920_ISADMA 0x0374 /* ISA Rec DMA channel */
#define PP_ISASOF 0x0026 /* ISA DMA offset */
#define PP_DmaFrameCnt 0x0028 /* ISA DMA Frame count */
#define PP_DmaByteCnt 0x002A /* ISA DMA Byte count */
#define PP_CS8900_ISAMemB 0x002C /* Memory base */
#define PP_CS8920_ISAMemB 0x0348 /* */
#define PP_ISABootBase 0x0030 /* Boot Prom base */
#define PP_ISABootMask 0x0034 /* Boot Prom Mask */
/* EEPROM data and command registers */
#define PP_EECMD 0x0040 /* NVR Interface Command register */
#define PP_EEData 0x0042 /* NVR Interface Data Register */
#define PP_DebugReg 0x0044 /* Debug Register */
#define PP_RxCFG 0x0102 /* Rx Bus config */
#define PP_RxCTL 0x0104 /* Receive Control Register */
#define PP_TxCFG 0x0106 /* Transmit Config Register */
#define PP_TxCMD 0x0108 /* Transmit Command Register */
#define PP_BufCFG 0x010A /* Bus configuration Register */
#define PP_LineCTL 0x0112 /* Line Config Register */
#define PP_SelfCTL 0x0114 /* Self Command Register */
#define PP_BusCTL 0x0116 /* ISA bus control Register */
#define PP_TestCTL 0x0118 /* Test Register */
#define PP_AutoNegCTL 0x011C /* Auto Negotiation Ctrl */
#define PP_ISQ 0x0120 /* Interrupt Status */
#define PP_RxEvent 0x0124 /* Rx Event Register */
#define PP_TxEvent 0x0128 /* Tx Event Register */
#define PP_BufEvent 0x012C /* Bus Event Register */
#define PP_RxMiss 0x0130 /* Receive Miss Count */
#define PP_TxCol 0x0132 /* Transmit Collision Count */
#define PP_LineST 0x0134 /* Line State Register */
#define PP_SelfST 0x0136 /* Self State register */
#define PP_BusST 0x0138 /* Bus Status */
#define PP_TDR 0x013C /* Time Domain Reflectometry */
#define PP_AutoNegST 0x013E /* Auto Neg Status */
#define PP_TxCommand 0x0144 /* Tx Command */
#define PP_TxLength 0x0146 /* Tx Length */
#define PP_LAF 0x0150 /* Hash Table */
#define PP_IA 0x0158 /* Physical Address Register */
#define PP_RxStatus 0x0400 /* Receive start of frame */
#define PP_RxLength 0x0402 /* Receive Length of frame */
#define PP_RxFrame 0x0404 /* Receive frame pointer */
#define PP_TxFrame 0x0A00 /* Transmit frame pointer */
/* Primary I/O Base Address. If no I/O base is supplied by the user, then this */
/* can be used as the default I/O base to access the PacketPage Area. */
#define DEFAULTIOBASE 0x0300
#define FIRST_IO 0x020C /* First I/O port to check */
#define LAST_IO 0x037C /* Last I/O port to check (+10h) */
#define ADD_MASK 0x3000 /* Mask it use of the ADD_PORT register */
#define ADD_SIG 0x3000 /* Expected ID signature */
#define CHIP_EISA_ID_SIG 0x630E /* Product ID Code for Crystal Chip (CS8900 spec 4.3) */
#ifdef IBMEIPKT
#define EISA_ID_SIG 0x4D24 /* IBM */
#define PART_NO_SIG 0x1010 /* IBM */
#define MONGOOSE_BIT 0x0000 /* IBM */
#else
#define EISA_ID_SIG 0x630E /* PnP Vendor ID (same as chip id for Crystal board) */
#define PART_NO_SIG 0x4000 /* ID code CS8920 board (PnP Vendor Product code) */
#define MONGOOSE_BIT 0x2000 /* PART_NO_SIG + MONGOOSE_BUT => ID of mongoose */
#endif
#define PRODUCT_ID_ADD 0x0002 /* Address of product ID */
/* Mask to find out the types of registers */
#define REG_TYPE_MASK 0x001F
/* Eeprom Commands */
#define ERSE_WR_ENBL 0x00F0
#define ERSE_WR_DISABLE 0x0000
/* Defines Control/Config register quintuplet numbers */
#define RX_BUF_CFG 0x0003
#define RX_CONTROL 0x0005
#define TX_CFG 0x0007
#define TX_COMMAND 0x0009
#define BUF_CFG 0x000B
#define LINE_CONTROL 0x0013
#define SELF_CONTROL 0x0015
#define BUS_CONTROL 0x0017
#define TEST_CONTROL 0x0019
/* Defines Status/Count registers quintuplet numbers */
#define RX_EVENT 0x0004
#define TX_EVENT 0x0008
#define BUF_EVENT 0x000C
#define RX_MISS_COUNT 0x0010
#define TX_COL_COUNT 0x0012
#define LINE_STATUS 0x0014
#define SELF_STATUS 0x0016
#define BUS_STATUS 0x0018
#define TDR 0x001C
/* PP_RxCFG - Receive Configuration and Interrupt Mask bit definition - Read/write */
#define SKIP_1 0x0040
#define RX_STREAM_ENBL 0x0080
#define RX_OK_ENBL 0x0100
#define RX_DMA_ONLY 0x0200
#define AUTO_RX_DMA 0x0400
#define BUFFER_CRC 0x0800
#define RX_CRC_ERROR_ENBL 0x1000
#define RX_RUNT_ENBL 0x2000
#define RX_EXTRA_DATA_ENBL 0x4000
/* PP_RxCTL - Receive Control bit definition - Read/write */
#define RX_IA_HASH_ACCEPT 0x0040
#define RX_PROM_ACCEPT 0x0080
#define RX_OK_ACCEPT 0x0100
#define RX_MULTCAST_ACCEPT 0x0200
#define RX_IA_ACCEPT 0x0400
#define RX_BROADCAST_ACCEPT 0x0800
#define RX_BAD_CRC_ACCEPT 0x1000
#define RX_RUNT_ACCEPT 0x2000
#define RX_EXTRA_DATA_ACCEPT 0x4000
#define RX_ALL_ACCEPT (RX_PROM_ACCEPT|RX_BAD_CRC_ACCEPT|RX_RUNT_ACCEPT|RX_EXTRA_DATA_ACCEPT)
/* Default receive mode - individually addressed, broadcast, and error free */
#define DEF_RX_ACCEPT (RX_IA_ACCEPT | RX_BROADCAST_ACCEPT | RX_OK_ACCEPT)
/* PP_TxCFG - Transmit Configuration Interrupt Mask bit definition - Read/write */
#define TX_LOST_CRS_ENBL 0x0040
#define TX_SQE_ERROR_ENBL 0x0080
#define TX_OK_ENBL 0x0100
#define TX_LATE_COL_ENBL 0x0200
#define TX_JBR_ENBL 0x0400
#define TX_ANY_COL_ENBL 0x0800
#define TX_16_COL_ENBL 0x8000
/* PP_TxCMD - Transmit Command bit definition - Read-only */
#define TX_START_4_BYTES 0x0000
#define TX_START_64_BYTES 0x0040
#define TX_START_128_BYTES 0x0080
#define TX_START_ALL_BYTES 0x00C0
#define TX_FORCE 0x0100
#define TX_ONE_COL 0x0200
#define TX_TWO_PART_DEFF_DISABLE 0x0400
#define TX_NO_CRC 0x1000
#define TX_RUNT 0x2000
/* PP_BufCFG - Buffer Configuration Interrupt Mask bit definition - Read/write */
#define GENERATE_SW_INTERRUPT 0x0040
#define RX_DMA_ENBL 0x0080
#define READY_FOR_TX_ENBL 0x0100
#define TX_UNDERRUN_ENBL 0x0200
#define RX_MISS_ENBL 0x0400
#define RX_128_BYTE_ENBL 0x0800
#define TX_COL_COUNT_OVRFLOW_ENBL 0x1000
#define RX_MISS_COUNT_OVRFLOW_ENBL 0x2000
#define RX_DEST_MATCH_ENBL 0x8000
/* PP_LineCTL - Line Control bit definition - Read/write */
#define SERIAL_RX_ON 0x0040
#define SERIAL_TX_ON 0x0080
#define AUI_ONLY 0x0100
#define AUTO_AUI_10BASET 0x0200
#define MODIFIED_BACKOFF 0x0800
#define NO_AUTO_POLARITY 0x1000
#define TWO_PART_DEFDIS 0x2000
#define LOW_RX_SQUELCH 0x4000
/* PP_SelfCTL - Software Self Control bit definition - Read/write */
#define POWER_ON_RESET 0x0040
#define SW_STOP 0x0100
#define SLEEP_ON 0x0200
#define AUTO_WAKEUP 0x0400
#define HCB0_ENBL 0x1000
#define HCB1_ENBL 0x2000
#define HCB0 0x4000
#define HCB1 0x8000
/* PP_BusCTL - ISA Bus Control bit definition - Read/write */
#define RESET_RX_DMA 0x0040
#define MEMORY_ON 0x0400
#define DMA_BURST_MODE 0x0800
#define IO_CHANNEL_READY_ON 0x1000
#define RX_DMA_SIZE_64K 0x2000
#define ENABLE_IRQ 0x8000
/* PP_TestCTL - Test Control bit definition - Read/write */
#define LINK_OFF 0x0080
#define ENDEC_LOOPBACK 0x0200
#define AUI_LOOPBACK 0x0400
#define BACKOFF_OFF 0x0800
#define FAST_TEST 0x8000
/* PP_RxEvent - Receive Event Bit definition - Read-only */
#define RX_IA_HASHED 0x0040
#define RX_DRIBBLE 0x0080
#define RX_OK 0x0100
#define RX_HASHED 0x0200
#define RX_IA 0x0400
#define RX_BROADCAST 0x0800
#define RX_CRC_ERROR 0x1000
#define RX_RUNT 0x2000
#define RX_EXTRA_DATA 0x4000
#define HASH_INDEX_MASK 0x0FC00
/* PP_TxEvent - Transmit Event Bit definition - Read-only */
#define TX_LOST_CRS 0x0040
#define TX_SQE_ERROR 0x0080
#define TX_OK 0x0100
#define TX_LATE_COL 0x0200
#define TX_JBR 0x0400
#define TX_16_COL 0x8000
#define TX_SEND_OK_BITS (TX_OK|TX_LOST_CRS)
#define TX_COL_COUNT_MASK 0x7800
/* PP_BufEvent - Buffer Event Bit definition - Read-only */
#define SW_INTERRUPT 0x0040
#define RX_DMA 0x0080
#define READY_FOR_TX 0x0100
#define TX_UNDERRUN 0x0200
#define RX_MISS 0x0400
#define RX_128_BYTE 0x0800
#define TX_COL_OVRFLW 0x1000
#define RX_MISS_OVRFLW 0x2000
#define RX_DEST_MATCH 0x8000
/* PP_LineST - Ethernet Line Status bit definition - Read-only */
#define LINK_OK 0x0080
#define AUI_ON 0x0100
#define TENBASET_ON 0x0200
#define POLARITY_OK 0x1000
#define CRS_OK 0x4000
/* PP_SelfST - Chip Software Status bit definition */
#define ACTIVE_33V 0x0040
#define INIT_DONE 0x0080
#define SI_BUSY 0x0100
#define EEPROM_PRESENT 0x0200
#define EEPROM_OK 0x0400
#define EL_PRESENT 0x0800
#define EE_SIZE_64 0x1000
/* PP_BusST - ISA Bus Status bit definition */
#define TX_BID_ERROR 0x0080
#define READY_FOR_TX_NOW 0x0100
/* PP_AutoNegCTL - Auto Negotiation Control bit definition */
#define RE_NEG_NOW 0x0040
#define ALLOW_FDX 0x0080
#define AUTO_NEG_ENABLE 0x0100
#define NLP_ENABLE 0x0200
#define FORCE_FDX 0x8000
#define AUTO_NEG_BITS (FORCE_FDX|NLP_ENABLE|AUTO_NEG_ENABLE)
#define AUTO_NEG_MASK (FORCE_FDX|NLP_ENABLE|AUTO_NEG_ENABLE|ALLOW_FDX|RE_NEG_NOW)
/* PP_AutoNegST - Auto Negotiation Status bit definition */
#define AUTO_NEG_BUSY 0x0080
#define FLP_LINK 0x0100
#define FLP_LINK_GOOD 0x0800
#define LINK_FAULT 0x1000
#define HDX_ACTIVE 0x4000
#define FDX_ACTIVE 0x8000
/* The following block defines the ISQ event types */
#define ISQ_RECEIVER_EVENT 0x04
#define ISQ_TRANSMITTER_EVENT 0x08
#define ISQ_BUFFER_EVENT 0x0c
#define ISQ_RX_MISS_EVENT 0x10
#define ISQ_TX_COL_EVENT 0x12
#define ISQ_EVENT_MASK 0x003F /* ISQ mask to find out type of event */
#define ISQ_HIST 16 /* small history buffer */
#define AUTOINCREMENT 0x8000 /* Bit mask to set bit-15 for autoincrement */
#define TXRXBUFSIZE 0x0600
#define RXDMABUFSIZE 0x8000
#define RXDMASIZE 0x4000
#define TXRX_LENGTH_MASK 0x07FF
/* rx options bits */
#define RCV_WITH_RXON 1 /* Set SerRx ON */
#define RCV_COUNTS 2 /* Use Framecnt1 */
#define RCV_PONG 4 /* Pong respondent */
#define RCV_DONG 8 /* Dong operation */
#define RCV_POLLING 0x10 /* Poll RxEvent */
#define RCV_ISQ 0x20 /* Use ISQ, int */
#define RCV_AUTO_DMA 0x100 /* Set AutoRxDMAE */
#define RCV_DMA 0x200 /* Set RxDMA only */
#define RCV_DMA_ALL 0x400 /* Copy all DMA'ed */
#define RCV_FIXED_DATA 0x800 /* Every frame same */
#define RCV_IO 0x1000 /* Use ISA IO only */
#define RCV_MEMORY 0x2000 /* Use ISA Memory */
#define RAM_SIZE 0x1000 /* The card has 4k bytes or RAM */
#define PKT_START PP_TxFrame /* Start of packet RAM */
#define RX_FRAME_PORT 0x0000
#define TX_FRAME_PORT RX_FRAME_PORT
#define TX_CMD_PORT 0x0004
#define TX_NOW 0x0000 /* Tx packet after 5 bytes copied */
#define TX_AFTER_381 0x0020 /* Tx packet after 381 bytes copied */
#define TX_AFTER_ALL 0x00C0 /* Tx packet after all bytes copied */
#define TX_LEN_PORT 0x0006
#define ISQ_PORT 0x0008
#define ADD_PORT 0x000A
#define DATA_PORT 0x000C
#define EEPROM_WRITE_EN 0x00F0
#define EEPROM_WRITE_DIS 0x0000
#define EEPROM_WRITE_CMD 0x0100
#define EEPROM_READ_CMD 0x0200
/* Receive Header */
/* Description of header of each packet in receive area of memory */
#define RBUF_EVENT_LOW 0 /* Low byte of RxEvent - status of received frame */
#define RBUF_EVENT_HIGH 1 /* High byte of RxEvent - status of received frame */
#define RBUF_LEN_LOW 2 /* Length of received data - low byte */
#define RBUF_LEN_HI 3 /* Length of received data - high byte */
#define RBUF_HEAD_LEN 4 /* Length of this header */
#define CHIP_READ 0x1 /* Used to mark state of the repins code (chip or dma) */
#define DMA_READ 0x2 /* Used to mark state of the repins code (chip or dma) */
/* for bios scan */
/* */
#ifdef CSDEBUG
/* use these values for debugging bios scan */
#define BIOS_START_SEG 0x00000
#define BIOS_OFFSET_INC 0x0010
#else
#define BIOS_START_SEG 0x0c000
#define BIOS_OFFSET_INC 0x0200
#endif
#define BIOS_LAST_OFFSET 0x0fc00
/* Byte offsets into the EEPROM configuration buffer */
#define ISA_CNF_OFFSET 0x6
#define TX_CTL_OFFSET (ISA_CNF_OFFSET + 8) /* 8900 eeprom */
#define AUTO_NEG_CNF_OFFSET (ISA_CNF_OFFSET + 8) /* 8920 eeprom */
/* the assumption here is that the bits in the eeprom are generally */
/* in the same position as those in the autonegctl register. */
/* Of course the IMM bit is not in that register so it must be */
/* masked out */
#define EE_FORCE_FDX 0x8000
#define EE_NLP_ENABLE 0x0200
#define EE_AUTO_NEG_ENABLE 0x0100
#define EE_ALLOW_FDX 0x0080
#define EE_AUTO_NEG_CNF_MASK (EE_FORCE_FDX|EE_NLP_ENABLE|EE_AUTO_NEG_ENABLE|EE_ALLOW_FDX)
#define IMM_BIT 0x0040 /* ignore missing media */
#define ADAPTER_CNF_OFFSET (AUTO_NEG_CNF_OFFSET + 2)
#define A_CNF_10B_T 0x0001
#define A_CNF_AUI 0x0002
#define A_CNF_10B_2 0x0004
#define A_CNF_MEDIA_TYPE 0x0060
#define A_CNF_MEDIA_AUTO 0x0000
#define A_CNF_MEDIA_10B_T 0x0020
#define A_CNF_MEDIA_AUI 0x0040
#define A_CNF_MEDIA_10B_2 0x0060
#define A_CNF_DC_DC_POLARITY 0x0080
#define A_CNF_NO_AUTO_POLARITY 0x2000
#define A_CNF_LOW_RX_SQUELCH 0x4000
#define A_CNF_EXTND_10B_2 0x8000
#define PACKET_PAGE_OFFSET 0x8
/* Bit definitions for the ISA configuration word from the EEPROM */
#define INT_NO_MASK 0x000F
#define DMA_NO_MASK 0x0070
#define ISA_DMA_SIZE 0x0200
#define ISA_AUTO_RxDMA 0x0400
#define ISA_RxDMA 0x0800
#define DMA_BURST 0x1000
#define STREAM_TRANSFER 0x2000
#define ANY_ISA_DMA (ISA_AUTO_RxDMA | ISA_RxDMA)
/* DMA controller registers */
#define DMA_BASE 0x00 /* DMA controller base */
#define DMA_BASE_2 0x0C0 /* DMA controller base */
#define DMA_STAT 0x0D0 /* DMA controller status register */
#define DMA_MASK 0x0D4 /* DMA controller mask register */
#define DMA_MODE 0x0D6 /* DMA controller mode register */
#define DMA_RESETFF 0x0D8 /* DMA controller first/last flip flop */
/* DMA data */
#define DMA_DISABLE 0x04 /* Disable channel n */
#define DMA_ENABLE 0x00 /* Enable channel n */
/* Demand transfers, incr. address, auto init, writes, ch. n */
#define DMA_RX_MODE 0x14
/* Demand transfers, incr. address, auto init, reads, ch. n */
#define DMA_TX_MODE 0x18
#define DMA_SIZE (16*1024) /* Size of dma buffer - 16k */
#define CS8900 0x0000
#define CS8920 0x4000
#define CS8920M 0x6000
#define REVISON_BITS 0x1F00
#define EEVER_NUMBER 0x12
#define CHKSUM_LEN 0x14
#define CHKSUM_VAL 0x0000
#define START_EEPROM_DATA 0x001c /* Offset into eeprom for start of data */
#define IRQ_MAP_EEPROM_DATA 0x0046 /* Offset into eeprom for the IRQ map */
#define IRQ_MAP_LEN 0x0004 /* No of bytes to read for the IRQ map */
#define PNP_IRQ_FRMT 0x0022 /* PNP small item IRQ format */
#define CS8900_IRQ_MAP 0x1c20 /* This IRQ map is fixed */
#define CS8920_NO_INTS 0x0F /* Max CS8920 interrupt select # */
#define PNP_ADD_PORT 0x0279
#define PNP_WRITE_PORT 0x0A79
#define GET_PNP_ISA_STRUCT 0x40
#define PNP_ISA_STRUCT_LEN 0x06
#define PNP_CSN_CNT_OFF 0x01
#define PNP_RD_PORT_OFF 0x02
#define PNP_FUNCTION_OK 0x00
#define PNP_WAKE 0x03
#define PNP_RSRC_DATA 0x04
#define PNP_RSRC_READY 0x01
#define PNP_STATUS 0x05
#define PNP_ACTIVATE 0x30
#define PNP_CNF_IO_H 0x60
#define PNP_CNF_IO_L 0x61
#define PNP_CNF_INT 0x70
#define PNP_CNF_DMA 0x74
#define PNP_CNF_MEM 0x48
#define BIT0 1
#define BIT15 0x8000
/*
* Local variables:
* c-basic-offset: 8
* End:
*/

View File

@@ -0,0 +1,26 @@
Permission is granted to distribute the enclosed cs89x0.[ch] driver
only in conjunction with the Etherboot package. The code is
ordinarily distributed under the GPL.
Russ Nelson, January 2000
CREDITS
I want to thank
Mike Cruse <mcruse@cti-ltd.com>
for providing an evaluation NIC and for sponsoring the
development of this driver.
Randall Sears <sears@crystal.cirrus.com>
Deva Bodas <bodas@crystal.cirrus.com>
Andreas Kraemer <akraemer@crystal.cirrus.com>
Wolfgang Krause <100303.2673@compuserve.com>
for excellent technical support and for providing the required
programming information. I appreciate Crystal Semiconductor's
commitment towards free software.
Russell Nelson <nelson@crynwr.com>
for writing the Linux device driver for the CS89x0
chipset. Russel's code is very well designed and simplified my
job a lot.

720
src/drivers/net/davicom.c Normal file
View File

@@ -0,0 +1,720 @@
#ifdef ALLMULTI
#error multicast support is not yet implemented
#endif
/*
DAVICOM DM9009/DM9102/DM9102A Etherboot Driver V1.00
This driver was ported from Marty Connor's Tulip Etherboot driver.
Thanks Marty Connor (mdc@etherboot.org)
This davicom etherboot driver supports DM9009/DM9102/DM9102A/
DM9102A+DM9801/DM9102A+DM9802 NICs.
This software may be used and distributed according to the terms
of the GNU Public License, incorporated herein by reference.
*/
/*********************************************************************/
/* Revision History */
/*********************************************************************/
/*
19 OCT 2000 Sten 1.00
Different half and full duplex mode
Do the different programming for DM9801/DM9802
12 OCT 2000 Sten 0.90
This driver was ported from tulip driver and it
has the following difference.
Changed symbol tulip/TULIP to davicom/DAVICOM
Deleted some code that did not use in this driver.
Used chain-strcture to replace ring structure
for both TX/RX descriptor.
Allocated two tx descriptor.
According current media mode to set operating
register(CR6)
*/
/*********************************************************************/
/* Declarations */
/*********************************************************************/
#include "etherboot.h"
#include "nic.h"
#include "pci.h"
#undef DAVICOM_DEBUG
#undef DAVICOM_DEBUG_WHERE
#define TX_TIME_OUT 2*TICKS_PER_SEC
typedef unsigned char u8;
typedef signed char s8;
typedef unsigned short u16;
typedef signed short s16;
typedef unsigned int u32;
typedef signed int s32;
/* Register offsets for davicom device */
enum davicom_offsets {
CSR0=0, CSR1=0x08, CSR2=0x10, CSR3=0x18, CSR4=0x20, CSR5=0x28,
CSR6=0x30, CSR7=0x38, CSR8=0x40, CSR9=0x48, CSR10=0x50, CSR11=0x58,
CSR12=0x60, CSR13=0x68, CSR14=0x70, CSR15=0x78, CSR16=0x80, CSR20=0xA0
};
/* EEPROM Address width definitions */
#define EEPROM_ADDRLEN 6
#define EEPROM_SIZE 32 /* 1 << EEPROM_ADDRLEN */
/* Used to be 128, but we only need to read enough to get the MAC
address at bytes 20..25 */
/* Data Read from the EEPROM */
static unsigned char ee_data[EEPROM_SIZE];
/* The EEPROM commands include the alway-set leading bit. */
#define EE_WRITE_CMD (5 << addr_len)
#define EE_READ_CMD (6 << addr_len)
#define EE_ERASE_CMD (7 << addr_len)
/* EEPROM_Ctrl bits. */
#define EE_SHIFT_CLK 0x02 /* EEPROM shift clock. */
#define EE_CS 0x01 /* EEPROM chip select. */
#define EE_DATA_WRITE 0x04 /* EEPROM chip data in. */
#define EE_WRITE_0 0x01
#define EE_WRITE_1 0x05
#define EE_DATA_READ 0x08 /* EEPROM chip data out. */
#define EE_ENB (0x4800 | EE_CS)
/* Sten 10/11 for phyxcer */
#define PHY_DATA_0 0x0
#define PHY_DATA_1 0x20000
#define MDCLKH 0x10000
/* Delay between EEPROM clock transitions. Even at 33Mhz current PCI
implementations don't overrun the EEPROM clock. We add a bus
turn-around to insure that this remains true. */
#define eeprom_delay() inl(ee_addr)
/* helpful macro if on a big_endian machine for changing byte order.
not strictly needed on Intel
Already defined in Etherboot includes
#define le16_to_cpu(val) (val)
*/
/* transmit and receive descriptor format */
struct txdesc {
volatile unsigned long status; /* owner, status */
unsigned long buf1sz:11, /* size of buffer 1 */
buf2sz:11, /* size of buffer 2 */
control:10; /* control bits */
const unsigned char *buf1addr; /* buffer 1 address */
const unsigned char *buf2addr; /* buffer 2 address */
};
struct rxdesc {
volatile unsigned long status; /* owner, status */
unsigned long buf1sz:11, /* size of buffer 1 */
buf2sz:11, /* size of buffer 2 */
control:10; /* control bits */
unsigned char *buf1addr; /* buffer 1 address */
unsigned char *buf2addr; /* buffer 2 address */
};
/* Size of transmit and receive buffers */
#define BUFLEN 1536
/*********************************************************************/
/* Global Storage */
/*********************************************************************/
/* PCI Bus parameters */
static unsigned short vendor, dev_id;
static unsigned long ioaddr;
/* Note: transmit and receive buffers must be longword aligned and
longword divisable */
/* transmit descriptor and buffer */
#define NTXD 2
static struct txdesc txd[NTXD] __attribute__ ((aligned(4)));
static unsigned char txb[BUFLEN] __attribute__ ((aligned(4)));
/* receive descriptor(s) and buffer(s) */
#define NRXD 4
static struct rxdesc rxd[NRXD] __attribute__ ((aligned(4)));
static unsigned char rxb[NRXD * BUFLEN] __attribute__ ((aligned(4)));
static int rxd_tail;
static int TxPtr;
/*********************************************************************/
/* Function Prototypes */
/*********************************************************************/
static void whereami(const char *str);
static int read_eeprom(unsigned long ioaddr, int location, int addr_len);
static int davicom_probe(struct dev *dev, struct pci_device *pci);
static void davicom_init_chain(struct nic *nic); /* Sten 10/9 */
static void davicom_reset(struct nic *nic);
static void davicom_transmit(struct nic *nic, const char *d, unsigned int t,
unsigned int s, const char *p);
static int davicom_poll(struct nic *nic, int retrieve);
static void davicom_disable(struct dev *dev);
#ifdef DAVICOM_DEBUG
static void davicom_more(void);
#endif /* DAVICOM_DEBUG */
static void davicom_wait(unsigned int nticks);
static int phy_read(int);
static void phy_write(int, u16);
static void phy_write_1bit(u32, u32);
static int phy_read_1bit(u32);
static void davicom_media_chk(struct nic *);
/*********************************************************************/
/* Utility Routines */
/*********************************************************************/
static inline void whereami(const char *str)
{
printf("%s\n", str);
/* sleep(2); */
}
#ifdef DAVICOM_DEBUG
static void davicom_more()
{
printf("\n\n-- more --");
while (!iskey())
/* wait */;
getchar();
printf("\n\n");
}
#endif /* DAVICOM_DEBUG */
static void davicom_wait(unsigned int nticks)
{
unsigned int to = currticks() + nticks;
while (currticks() < to)
/* wait */ ;
}
/*********************************************************************/
/* For DAVICOM phyxcer register by MII interface */
/*********************************************************************/
/*
Read a word data from phy register
*/
static int phy_read(int location)
{
int i, phy_addr=1;
u16 phy_data;
u32 io_dcr9;
whereami("phy_read\n");
io_dcr9 = ioaddr + CSR9;
/* Send 33 synchronization clock to Phy controller */
for (i=0; i<34; i++)
phy_write_1bit(io_dcr9, PHY_DATA_1);
/* Send start command(01) to Phy */
phy_write_1bit(io_dcr9, PHY_DATA_0);
phy_write_1bit(io_dcr9, PHY_DATA_1);
/* Send read command(10) to Phy */
phy_write_1bit(io_dcr9, PHY_DATA_1);
phy_write_1bit(io_dcr9, PHY_DATA_0);
/* Send Phy addres */
for (i=0x10; i>0; i=i>>1)
phy_write_1bit(io_dcr9, phy_addr&i ? PHY_DATA_1: PHY_DATA_0);
/* Send register addres */
for (i=0x10; i>0; i=i>>1)
phy_write_1bit(io_dcr9, location&i ? PHY_DATA_1: PHY_DATA_0);
/* Skip transition state */
phy_read_1bit(io_dcr9);
/* read 16bit data */
for (phy_data=0, i=0; i<16; i++) {
phy_data<<=1;
phy_data|=phy_read_1bit(io_dcr9);
}
return phy_data;
}
/*
Write a word to Phy register
*/
static void phy_write(int location, u16 phy_data)
{
u16 i, phy_addr=1;
u32 io_dcr9;
whereami("phy_write\n");
io_dcr9 = ioaddr + CSR9;
/* Send 33 synchronization clock to Phy controller */
for (i=0; i<34; i++)
phy_write_1bit(io_dcr9, PHY_DATA_1);
/* Send start command(01) to Phy */
phy_write_1bit(io_dcr9, PHY_DATA_0);
phy_write_1bit(io_dcr9, PHY_DATA_1);
/* Send write command(01) to Phy */
phy_write_1bit(io_dcr9, PHY_DATA_0);
phy_write_1bit(io_dcr9, PHY_DATA_1);
/* Send Phy addres */
for (i=0x10; i>0; i=i>>1)
phy_write_1bit(io_dcr9, phy_addr&i ? PHY_DATA_1: PHY_DATA_0);
/* Send register addres */
for (i=0x10; i>0; i=i>>1)
phy_write_1bit(io_dcr9, location&i ? PHY_DATA_1: PHY_DATA_0);
/* written trasnition */
phy_write_1bit(io_dcr9, PHY_DATA_1);
phy_write_1bit(io_dcr9, PHY_DATA_0);
/* Write a word data to PHY controller */
for (i=0x8000; i>0; i>>=1)
phy_write_1bit(io_dcr9, phy_data&i ? PHY_DATA_1: PHY_DATA_0);
}
/*
Write one bit data to Phy Controller
*/
static void phy_write_1bit(u32 ee_addr, u32 phy_data)
{
whereami("phy_write_1bit\n");
outl(phy_data, ee_addr); /* MII Clock Low */
eeprom_delay();
outl(phy_data|MDCLKH, ee_addr); /* MII Clock High */
eeprom_delay();
outl(phy_data, ee_addr); /* MII Clock Low */
eeprom_delay();
}
/*
Read one bit phy data from PHY controller
*/
static int phy_read_1bit(u32 ee_addr)
{
int phy_data;
whereami("phy_read_1bit\n");
outl(0x50000, ee_addr);
eeprom_delay();
phy_data=(inl(ee_addr)>>19) & 0x1;
outl(0x40000, ee_addr);
eeprom_delay();
return phy_data;
}
/*
DM9801/DM9802 present check and program
*/
static void HPNA_process(void)
{
if ( (phy_read(3) & 0xfff0) == 0xb900 ) {
if ( phy_read(31) == 0x4404 ) {
/* DM9801 present */
if (phy_read(3) == 0xb901)
phy_write(16, 0x5); /* DM9801 E4 */
else
phy_write(16, 0x1005); /* DM9801 E3 and others */
phy_write(25, ((phy_read(24) + 3) & 0xff) | 0xf000);
} else {
/* DM9802 present */
phy_write(16, 0x5);
phy_write(25, (phy_read(25) & 0xff00) + 2);
}
}
}
/*
Sense media mode and set CR6
*/
static void davicom_media_chk(struct nic * nic __unused)
{
unsigned long to, csr6;
csr6 = 0x00200000; /* SF */
outl(csr6, ioaddr + CSR6);
#define PCI_DEVICE_ID_DM9009 0x9009
if (vendor == PCI_VENDOR_ID_DAVICOM && dev_id == PCI_DEVICE_ID_DM9009) {
/* Set to 10BaseT mode for DM9009 */
phy_write(0, 0);
} else {
/* For DM9102/DM9102A */
to = currticks() + 2 * TICKS_PER_SEC;
while ( ((phy_read(1) & 0x24)!=0x24) && (currticks() < to))
/* wait */ ;
if ( (phy_read(1) & 0x24) == 0x24 ) {
if (phy_read(17) & 0xa000)
csr6 |= 0x00000200; /* Full Duplex mode */
} else
csr6 |= 0x00040000; /* Select DM9801/DM9802 when Ethernet link failed */
}
/* set the chip's operating mode */
outl(csr6, ioaddr + CSR6);
/* DM9801/DM9802 present check & program */
if (csr6 & 0x40000)
HPNA_process();
}
/*********************************************************************/
/* EEPROM Reading Code */
/*********************************************************************/
/* EEPROM routines adapted from the Linux Tulip Code */
/* Reading a serial EEPROM is a "bit" grungy, but we work our way
through:->.
*/
static int read_eeprom(unsigned long ioaddr, int location, int addr_len)
{
int i;
unsigned short retval = 0;
long ee_addr = ioaddr + CSR9;
int read_cmd = location | EE_READ_CMD;
whereami("read_eeprom\n");
outl(EE_ENB & ~EE_CS, ee_addr);
outl(EE_ENB, ee_addr);
/* Shift the read command bits out. */
for (i = 4 + addr_len; i >= 0; i--) {
short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0;
outl(EE_ENB | dataval, ee_addr);
eeprom_delay();
outl(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr);
eeprom_delay();
}
outl(EE_ENB, ee_addr);
for (i = 16; i > 0; i--) {
outl(EE_ENB | EE_SHIFT_CLK, ee_addr);
eeprom_delay();
retval = (retval << 1) | ((inl(ee_addr) & EE_DATA_READ) ? 1 : 0);
outl(EE_ENB, ee_addr);
eeprom_delay();
}
/* Terminate the EEPROM access. */
outl(EE_ENB & ~EE_CS, ee_addr);
return retval;
}
/*********************************************************************/
/* davicom_init_chain - setup the tx and rx descriptors */
/* Sten 10/9 */
/*********************************************************************/
static void davicom_init_chain(struct nic *nic)
{
int i;
/* setup the transmit descriptor */
/* Sten: Set 2 TX descriptor but use one TX buffer because
it transmit a packet and wait complete every time. */
for (i=0; i<NTXD; i++) {
txd[i].buf1addr = (void *)virt_to_bus(&txb[0]); /* Used same TX buffer */
txd[i].buf2addr = (void *)virt_to_bus(&txd[i+1]); /* Point to Next TX desc */
txd[i].buf1sz = 0;
txd[i].buf2sz = 0;
txd[i].control = 0x184; /* Begin/End/Chain */
txd[i].status = 0x00000000; /* give ownership to Host */
}
/* construct perfect filter frame with mac address as first match
and broadcast address for all others */
for (i=0; i<192; i++) txb[i] = 0xFF;
txb[0] = nic->node_addr[0];
txb[1] = nic->node_addr[1];
txb[4] = nic->node_addr[2];
txb[5] = nic->node_addr[3];
txb[8] = nic->node_addr[4];
txb[9] = nic->node_addr[5];
/* setup receive descriptor */
for (i=0; i<NRXD; i++) {
rxd[i].buf1addr = (void *)virt_to_bus(&rxb[i * BUFLEN]);
rxd[i].buf2addr = (void *)virt_to_bus(&rxd[i+1]); /* Point to Next RX desc */
rxd[i].buf1sz = BUFLEN;
rxd[i].buf2sz = 0; /* not used */
rxd[i].control = 0x4; /* Chain Structure */
rxd[i].status = 0x80000000; /* give ownership to device */
}
/* Chain the last descriptor to first */
txd[NTXD - 1].buf2addr = (void *)virt_to_bus(&txd[0]);
rxd[NRXD - 1].buf2addr = (void *)virt_to_bus(&rxd[0]);
TxPtr = 0;
rxd_tail = 0;
}
/*********************************************************************/
/* davicom_reset - Reset adapter */
/*********************************************************************/
static void davicom_reset(struct nic *nic)
{
unsigned long to;
whereami("davicom_reset\n");
/* Stop Tx and RX */
outl(inl(ioaddr + CSR6) & ~0x00002002, ioaddr + CSR6);
/* Reset the chip, holding bit 0 set at least 50 PCI cycles. */
outl(0x00000001, ioaddr + CSR0);
davicom_wait(TICKS_PER_SEC);
/* TX/RX descriptor burst */
outl(0x0C00000, ioaddr + CSR0); /* Sten 10/9 */
/* set up transmit and receive descriptors */
davicom_init_chain(nic); /* Sten 10/9 */
/* Point to receive descriptor */
outl(virt_to_bus(&rxd[0]), ioaddr + CSR3);
outl(virt_to_bus(&txd[0]), ioaddr + CSR4); /* Sten 10/9 */
/* According phyxcer media mode to set CR6,
DM9102/A phyxcer can auto-detect media mode */
davicom_media_chk(nic);
/* Prepare Setup Frame Sten 10/9 */
txd[TxPtr].buf1sz = 192;
txd[TxPtr].control = 0x024; /* SF/CE */
txd[TxPtr].status = 0x80000000; /* Give ownership to device */
/* Start Tx */
outl(inl(ioaddr + CSR6) | 0x00002000, ioaddr + CSR6);
/* immediate transmit demand */
outl(0, ioaddr + CSR1);
to = currticks() + TX_TIME_OUT;
while ((txd[TxPtr].status & 0x80000000) && (currticks() < to)) /* Sten 10/9 */
/* wait */ ;
if (currticks() >= to) {
printf ("TX Setup Timeout!\n");
}
/* Point to next TX descriptor */
TxPtr = (++TxPtr >= NTXD) ? 0:TxPtr; /* Sten 10/9 */
#ifdef DAVICOM_DEBUG
printf("txd.status = %X\n", txd.status);
printf("ticks = %d\n", currticks() - (to - TX_TIME_OUT));
davicom_more();
#endif
/* enable RX */
outl(inl(ioaddr + CSR6) | 0x00000002, ioaddr + CSR6);
/* immediate poll demand */
outl(0, ioaddr + CSR2);
}
/*********************************************************************/
/* eth_transmit - Transmit a frame */
/*********************************************************************/
static void davicom_transmit(struct nic *nic, const char *d, unsigned int t,
unsigned int s, const char *p)
{
unsigned long to;
whereami("davicom_transmit\n");
/* Stop Tx */
/* outl(inl(ioaddr + CSR6) & ~0x00002000, ioaddr + CSR6); */
/* setup ethernet header */
memcpy(&txb[0], d, ETH_ALEN); /* DA 6byte */
memcpy(&txb[ETH_ALEN], nic->node_addr, ETH_ALEN); /* SA 6byte*/
txb[ETH_ALEN*2] = (t >> 8) & 0xFF; /* Frame type: 2byte */
txb[ETH_ALEN*2+1] = t & 0xFF;
memcpy(&txb[ETH_HLEN], p, s); /* Frame data */
/* setup the transmit descriptor */
txd[TxPtr].buf1sz = ETH_HLEN+s;
txd[TxPtr].control = 0x00000184; /* LS+FS+CE */
txd[TxPtr].status = 0x80000000; /* give ownership to device */
/* immediate transmit demand */
outl(0, ioaddr + CSR1);
to = currticks() + TX_TIME_OUT;
while ((txd[TxPtr].status & 0x80000000) && (currticks() < to))
/* wait */ ;
if (currticks() >= to) {
printf ("TX Timeout!\n");
}
/* Point to next TX descriptor */
TxPtr = (++TxPtr >= NTXD) ? 0:TxPtr; /* Sten 10/9 */
}
/*********************************************************************/
/* eth_poll - Wait for a frame */
/*********************************************************************/
static int davicom_poll(struct nic *nic, int retrieve)
{
whereami("davicom_poll\n");
if (rxd[rxd_tail].status & 0x80000000)
return 0;
if ( ! retrieve ) return 1;
whereami("davicom_poll got one\n");
nic->packetlen = (rxd[rxd_tail].status & 0x3FFF0000) >> 16;
if( rxd[rxd_tail].status & 0x00008000){
rxd[rxd_tail].status = 0x80000000;
rxd_tail++;
if (rxd_tail == NRXD) rxd_tail = 0;
return 0;
}
/* copy packet to working buffer */
/* XXX - this copy could be avoided with a little more work
but for now we are content with it because the optimised
memcpy is quite fast */
memcpy(nic->packet, rxb + rxd_tail * BUFLEN, nic->packetlen);
/* return the descriptor and buffer to receive ring */
rxd[rxd_tail].status = 0x80000000;
rxd_tail++;
if (rxd_tail == NRXD) rxd_tail = 0;
return 1;
}
/*********************************************************************/
/* eth_disable - Disable the interface */
/*********************************************************************/
static void davicom_disable(struct dev *dev)
{
struct nic *nic = (struct nic *)dev;
whereami("davicom_disable\n");
davicom_reset(nic);
/* disable interrupts */
outl(0x00000000, ioaddr + CSR7);
/* Stop the chip's Tx and Rx processes. */
outl(inl(ioaddr + CSR6) & ~0x00002002, ioaddr + CSR6);
/* Clear the missed-packet counter. */
(volatile unsigned long)inl(ioaddr + CSR8);
}
/*********************************************************************/
/* eth_irq - enable, disable and force interrupts */
/*********************************************************************/
static void davicom_irq(struct nic *nic __unused, irq_action_t action __unused)
{
switch ( action ) {
case DISABLE :
break;
case ENABLE :
break;
case FORCE :
break;
}
}
/*********************************************************************/
/* eth_probe - Look for an adapter */
/*********************************************************************/
static int davicom_probe(struct dev *dev, struct pci_device *pci)
{
struct nic *nic = (struct nic *)dev;
unsigned int i;
whereami("davicom_probe\n");
if (pci->ioaddr == 0)
return 0;
vendor = pci->vendor;
dev_id = pci->dev_id;
ioaddr = pci->ioaddr & ~3;
nic->irqno = 0;
nic->ioaddr = pci->ioaddr & ~3;
/* wakeup chip */
pcibios_write_config_dword(pci->bus, pci->devfn, 0x40, 0x00000000);
/* Stop the chip's Tx and Rx processes. */
outl(inl(ioaddr + CSR6) & ~0x00002002, ioaddr + CSR6);
/* Clear the missed-packet counter. */
(volatile unsigned long)inl(ioaddr + CSR8);
/* Get MAC Address */
/* read EEPROM data */
for (i = 0; i < sizeof(ee_data)/2; i++)
((unsigned short *)ee_data)[i] =
le16_to_cpu(read_eeprom(ioaddr, i, EEPROM_ADDRLEN));
/* extract MAC address from EEPROM buffer */
for (i=0; i<ETH_ALEN; i++)
nic->node_addr[i] = ee_data[20+i];
printf("Davicom %! at ioaddr %#hX\n", nic->node_addr, ioaddr);
/* initialize device */
davicom_reset(nic);
dev->disable = davicom_disable;
nic->poll = davicom_poll;
nic->transmit = davicom_transmit;
nic->irq = davicom_irq;
return 1;
}
static struct pci_id davicom_nics[] = {
PCI_ROM(0x1282, 0x9100, "davicom9100", "Davicom 9100"),
PCI_ROM(0x1282, 0x9102, "davicom9102", "Davicom 9102"),
PCI_ROM(0x1282, 0x9009, "davicom9009", "Davicom 9009"),
PCI_ROM(0x1282, 0x9132, "davicom9132", "Davicom 9132"), /* Needs probably some fixing */
};
static struct pci_driver davicom_driver __pci_driver = {
.type = NIC_DRIVER,
.name = "DAVICOM",
.probe = davicom_probe,
.ids = davicom_nics,
.id_count = sizeof(davicom_nics)/sizeof(davicom_nics[0]),
.class = 0,
};

794
src/drivers/net/depca.c Normal file
View File

@@ -0,0 +1,794 @@
/* Not fixed for relocation yet. Probably won't work relocated above 16MB */
#ifdef ALLMULTI
#error multicast support is not yet implemented
#endif
/* Etherboot: depca.h merged, comments from Linux driver retained */
/* depca.c: A DIGITAL DEPCA & EtherWORKS ethernet driver for linux.
Written 1994, 1995 by David C. Davies.
Copyright 1994 David C. Davies
and
United States Government
(as represented by the Director, National Security Agency).
Copyright 1995 Digital Equipment Corporation.
This software may be used and distributed according to the terms of
the GNU Public License, incorporated herein by reference.
This driver is written for the Digital Equipment Corporation series
of DEPCA and EtherWORKS ethernet cards:
DEPCA (the original)
DE100
DE101
DE200 Turbo
DE201 Turbo
DE202 Turbo (TP BNC)
DE210
DE422 (EISA)
The driver has been tested on DE100, DE200 and DE202 cards in a
relatively busy network. The DE422 has been tested a little.
This driver will NOT work for the DE203, DE204 and DE205 series of
cards, since they have a new custom ASIC in place of the AMD LANCE
chip. See the 'ewrk3.c' driver in the Linux source tree for running
those cards.
I have benchmarked the driver with a DE100 at 595kB/s to (542kB/s from)
a DECstation 5000/200.
The author may be reached at davies@maniac.ultranet.com
=========================================================================
The driver was originally based on the 'lance.c' driver from Donald
Becker which is included with the standard driver distribution for
linux. V0.4 is a complete re-write with only the kernel interface
remaining from the original code.
1) Lance.c code in /linux/drivers/net/
2) "Ethernet/IEEE 802.3 Family. 1992 World Network Data Book/Handbook",
AMD, 1992 [(800) 222-9323].
3) "Am79C90 CMOS Local Area Network Controller for Ethernet (C-LANCE)",
AMD, Pub. #17881, May 1993.
4) "Am79C960 PCnet-ISA(tm), Single-Chip Ethernet Controller for ISA",
AMD, Pub. #16907, May 1992
5) "DEC EtherWORKS LC Ethernet Controller Owners Manual",
Digital Equipment corporation, 1990, Pub. #EK-DE100-OM.003
6) "DEC EtherWORKS Turbo Ethernet Controller Owners Manual",
Digital Equipment corporation, 1990, Pub. #EK-DE200-OM.003
7) "DEPCA Hardware Reference Manual", Pub. #EK-DEPCA-PR
Digital Equipment Corporation, 1989
8) "DEC EtherWORKS Turbo_(TP BNC) Ethernet Controller Owners Manual",
Digital Equipment corporation, 1991, Pub. #EK-DE202-OM.001
Peter Bauer's depca.c (V0.5) was referred to when debugging V0.1 of this
driver.
The original DEPCA card requires that the ethernet ROM address counter
be enabled to count and has an 8 bit NICSR. The ROM counter enabling is
only done when a 0x08 is read as the first address octet (to minimise
the chances of writing over some other hardware's I/O register). The
NICSR accesses have been changed to byte accesses for all the cards
supported by this driver, since there is only one useful bit in the MSB
(remote boot timeout) and it is not used. Also, there is a maximum of
only 48kB network RAM for this card. My thanks to Torbjorn Lindh for
help debugging all this (and holding my feet to the fire until I got it
right).
The DE200 series boards have on-board 64kB RAM for use as a shared
memory network buffer. Only the DE100 cards make use of a 2kB buffer
mode which has not been implemented in this driver (only the 32kB and
64kB modes are supported [16kB/48kB for the original DEPCA]).
At the most only 2 DEPCA cards can be supported on the ISA bus because
there is only provision for two I/O base addresses on each card (0x300
and 0x200). The I/O address is detected by searching for a byte sequence
in the Ethernet station address PROM at the expected I/O address for the
Ethernet PROM. The shared memory base address is 'autoprobed' by
looking for the self test PROM and detecting the card name. When a
second DEPCA is detected, information is placed in the base_addr
variable of the next device structure (which is created if necessary),
thus enabling ethif_probe initialization for the device. More than 2
EISA cards can be supported, but care will be needed assigning the
shared memory to ensure that each slot has the correct IRQ, I/O address
and shared memory address assigned.
************************************************************************
NOTE: If you are using two ISA DEPCAs, it is important that you assign
the base memory addresses correctly. The driver autoprobes I/O 0x300
then 0x200. The base memory address for the first device must be less
than that of the second so that the auto probe will correctly assign the
I/O and memory addresses on the same card. I can't think of a way to do
this unambiguously at the moment, since there is nothing on the cards to
tie I/O and memory information together.
I am unable to test 2 cards together for now, so this code is
unchecked. All reports, good or bad, are welcome.
************************************************************************
The board IRQ setting must be at an unused IRQ which is auto-probed
using Donald Becker's autoprobe routines. DEPCA and DE100 board IRQs are
{2,3,4,5,7}, whereas the DE200 is at {5,9,10,11,15}. Note that IRQ2 is
really IRQ9 in machines with 16 IRQ lines.
No 16MB memory limitation should exist with this driver as DMA is not
used and the common memory area is in low memory on the network card (my
current system has 20MB and I've not had problems yet).
The ability to load this driver as a loadable module has been added. To
utilise this ability, you have to do <8 things:
0) have a copy of the loadable modules code installed on your system.
1) copy depca.c from the /linux/drivers/net directory to your favourite
temporary directory.
2) if you wish, edit the source code near line 1530 to reflect the I/O
address and IRQ you're using (see also 5).
3) compile depca.c, but include -DMODULE in the command line to ensure
that the correct bits are compiled (see end of source code).
4) if you are wanting to add a new card, goto 5. Otherwise, recompile a
kernel with the depca configuration turned off and reboot.
5) insmod depca.o [irq=7] [io=0x200] [mem=0xd0000] [adapter_name=DE100]
[Alan Cox: Changed the code to allow command line irq/io assignments]
[Dave Davies: Changed the code to allow command line mem/name
assignments]
6) run the net startup bits for your eth?? interface manually
(usually /etc/rc.inet[12] at boot time).
7) enjoy!
Note that autoprobing is not allowed in loadable modules - the system is
already up and running and you're messing with interrupts.
To unload a module, turn off the associated interface
'ifconfig eth?? down' then 'rmmod depca'.
To assign a base memory address for the shared memory when running as a
loadable module, see 5 above. To include the adapter name (if you have
no PROM but know the card name) also see 5 above. Note that this last
option will not work with kernel built-in depca's.
The shared memory assignment for a loadable module makes sense to avoid
the 'memory autoprobe' picking the wrong shared memory (for the case of
2 depca's in a PC).
************************************************************************
Support for MCA EtherWORKS cards added 11-3-98.
Verified to work with up to 2 DE212 cards in a system (although not
fully stress-tested).
Currently known bugs/limitations:
Note: with the MCA stuff as a module, it trusts the MCA configuration,
not the command line for IRQ and memory address. You can
specify them if you want, but it will throw your values out.
You still have to pass the IO address it was configured as
though.
************************************************************************
TO DO:
------
Revision History
----------------
Version Date Description
0.1 25-jan-94 Initial writing.
0.2 27-jan-94 Added LANCE TX hardware buffer chaining.
0.3 1-feb-94 Added multiple DEPCA support.
0.31 4-feb-94 Added DE202 recognition.
0.32 19-feb-94 Tidy up. Improve multi-DEPCA support.
0.33 25-feb-94 Fix DEPCA ethernet ROM counter enable.
Add jabber packet fix from murf@perftech.com
and becker@super.org
0.34 7-mar-94 Fix DEPCA max network memory RAM & NICSR access.
0.35 8-mar-94 Added DE201 recognition. Tidied up.
0.351 30-apr-94 Added EISA support. Added DE422 recognition.
0.36 16-may-94 DE422 fix released.
0.37 22-jul-94 Added MODULE support
0.38 15-aug-94 Added DBR ROM switch in depca_close().
Multi DEPCA bug fix.
0.38axp 15-sep-94 Special version for Alpha AXP Linux V1.0.
0.381 12-dec-94 Added DE101 recognition, fix multicast bug.
0.382 9-feb-95 Fix recognition bug reported by <bkm@star.rl.ac.uk>.
0.383 22-feb-95 Fix for conflict with VESA SCSI reported by
<stromain@alf.dec.com>
0.384 17-mar-95 Fix a ring full bug reported by <bkm@star.rl.ac.uk>
0.385 3-apr-95 Fix a recognition bug reported by
<ryan.niemi@lastfrontier.com>
0.386 21-apr-95 Fix the last fix...sorry, must be galloping senility
0.40 25-May-95 Rewrite for portability & updated.
ALPHA support from <jestabro@amt.tay1.dec.com>
0.41 26-Jun-95 Added verify_area() calls in depca_ioctl() from
suggestion by <heiko@colossus.escape.de>
0.42 27-Dec-95 Add 'mem' shared memory assignment for loadable
modules.
Add 'adapter_name' for loadable modules when no PROM.
Both above from a suggestion by
<pchen@woodruffs121.residence.gatech.edu>.
Add new multicasting code.
0.421 22-Apr-96 Fix alloc_device() bug <jari@markkus2.fimr.fi>
0.422 29-Apr-96 Fix depca_hw_init() bug <jari@markkus2.fimr.fi>
0.423 7-Jun-96 Fix module load bug <kmg@barco.be>
0.43 16-Aug-96 Update alloc_device() to conform to de4x5.c
0.44 1-Sep-97 Fix *_probe() to test check_region() first - bug
reported by <mmogilvi@elbert.uccs.edu>
0.45 3-Nov-98 Added support for MCA EtherWORKS (DE210/DE212) cards
by <tymm@computer.org>
0.451 5-Nov-98 Fixed mca stuff cuz I'm a dummy. <tymm@computer.org>
0.5 14-Nov-98 Re-spin for 2.1.x kernels.
0.51 27-Jun-99 Correct received packet length for CRC from
report by <worm@dkik.dk>
=========================================================================
*/
#include "etherboot.h"
#include "nic.h"
#include "isa.h"
/*
** I/O addresses. Note that the 2k buffer option is not supported in
** this driver.
*/
#define DEPCA_NICSR ioaddr+0x00 /* Network interface CSR */
#define DEPCA_RBI ioaddr+0x02 /* RAM buffer index (2k buffer mode) */
#define DEPCA_DATA ioaddr+0x04 /* LANCE registers' data port */
#define DEPCA_ADDR ioaddr+0x06 /* LANCE registers' address port */
#define DEPCA_HBASE ioaddr+0x08 /* EISA high memory base address reg. */
#define DEPCA_PROM ioaddr+0x0c /* Ethernet address ROM data port */
#define DEPCA_CNFG ioaddr+0x0c /* EISA Configuration port */
#define DEPCA_RBSA ioaddr+0x0e /* RAM buffer starting address (2k buff.) */
/*
** These are LANCE registers addressable through DEPCA_ADDR
*/
#define CSR0 0
#define CSR1 1
#define CSR2 2
#define CSR3 3
/*
** NETWORK INTERFACE CSR (NI_CSR) bit definitions
*/
#define TO 0x0100 /* Time Out for remote boot */
#define SHE 0x0080 /* SHadow memory Enable */
#define BS 0x0040 /* Bank Select */
#define BUF 0x0020 /* BUFfer size (1->32k, 0->64k) */
#define RBE 0x0010 /* Remote Boot Enable (1->net boot) */
#define AAC 0x0008 /* Address ROM Address Counter (1->enable) */
#define _128KB 0x0008 /* 128kB Network RAM (1->enable) */
#define IM 0x0004 /* Interrupt Mask (1->mask) */
#define IEN 0x0002 /* Interrupt tristate ENable (1->enable) */
#define LED 0x0001 /* LED control */
/*
** Control and Status Register 0 (CSR0) bit definitions
*/
#define ERR 0x8000 /* Error summary */
#define BABL 0x4000 /* Babble transmitter timeout error */
#define CERR 0x2000 /* Collision Error */
#define MISS 0x1000 /* Missed packet */
#define MERR 0x0800 /* Memory Error */
#define RINT 0x0400 /* Receiver Interrupt */
#define TINT 0x0200 /* Transmit Interrupt */
#define IDON 0x0100 /* Initialization Done */
#define INTR 0x0080 /* Interrupt Flag */
#define INEA 0x0040 /* Interrupt Enable */
#define RXON 0x0020 /* Receiver on */
#define TXON 0x0010 /* Transmitter on */
#define TDMD 0x0008 /* Transmit Demand */
#define STOP 0x0004 /* Stop */
#define STRT 0x0002 /* Start */
#define INIT 0x0001 /* Initialize */
#define INTM 0xff00 /* Interrupt Mask */
#define INTE 0xfff0 /* Interrupt Enable */
/*
** CONTROL AND STATUS REGISTER 3 (CSR3)
*/
#define BSWP 0x0004 /* Byte SWaP */
#define ACON 0x0002 /* ALE control */
#define BCON 0x0001 /* Byte CONtrol */
/*
** Initialization Block Mode Register
*/
#define PROM 0x8000 /* Promiscuous Mode */
#define EMBA 0x0080 /* Enable Modified Back-off Algorithm */
#define INTL 0x0040 /* Internal Loopback */
#define DRTY 0x0020 /* Disable Retry */
#define COLL 0x0010 /* Force Collision */
#define DTCR 0x0008 /* Disable Transmit CRC */
#define LOOP 0x0004 /* Loopback */
#define DTX 0x0002 /* Disable the Transmitter */
#define DRX 0x0001 /* Disable the Receiver */
/*
** Receive Message Descriptor 1 (RMD1) bit definitions.
*/
#define R_OWN 0x80000000 /* Owner bit 0 = host, 1 = lance */
#define R_ERR 0x4000 /* Error Summary */
#define R_FRAM 0x2000 /* Framing Error */
#define R_OFLO 0x1000 /* Overflow Error */
#define R_CRC 0x0800 /* CRC Error */
#define R_BUFF 0x0400 /* Buffer Error */
#define R_STP 0x0200 /* Start of Packet */
#define R_ENP 0x0100 /* End of Packet */
/*
** Transmit Message Descriptor 1 (TMD1) bit definitions.
*/
#define T_OWN 0x80000000 /* Owner bit 0 = host, 1 = lance */
#define T_ERR 0x4000 /* Error Summary */
#define T_ADD_FCS 0x2000 /* More the 1 retry needed to Xmit */
#define T_MORE 0x1000 /* >1 retry to transmit packet */
#define T_ONE 0x0800 /* 1 try needed to transmit the packet */
#define T_DEF 0x0400 /* Deferred */
#define T_STP 0x02000000 /* Start of Packet */
#define T_ENP 0x01000000 /* End of Packet */
#define T_FLAGS 0xff000000 /* TX Flags Field */
/*
** Transmit Message Descriptor 3 (TMD3) bit definitions.
*/
#define TMD3_BUFF 0x8000 /* BUFFer error */
#define TMD3_UFLO 0x4000 /* UnderFLOw error */
#define TMD3_RES 0x2000 /* REServed */
#define TMD3_LCOL 0x1000 /* Late COLlision */
#define TMD3_LCAR 0x0800 /* Loss of CARrier */
#define TMD3_RTRY 0x0400 /* ReTRY error */
/*
** Ethernet PROM defines
*/
#define PROBE_LENGTH 32
/*
** Set the number of Tx and Rx buffers. Ensure that the memory requested
** here is <= to the amount of shared memory set up by the board switches.
** The number of descriptors MUST BE A POWER OF 2.
**
** total_memory = NUM_RX_DESC*(8+RX_BUFF_SZ) + NUM_TX_DESC*(8+TX_BUFF_SZ)
*/
#define NUM_RX_DESC 2 /* Number of RX descriptors */
#define NUM_TX_DESC 2 /* Number of TX descriptors */
#define RX_BUFF_SZ 1536 /* Buffer size for each Rx buffer */
#define TX_BUFF_SZ 1536 /* Buffer size for each Tx buffer */
/*
** ISA Bus defines
*/
#define DEPCA_IO_PORTS {0x300, 0x200, 0}
#ifndef DEPCA_MODEL
#define DEPCA_MODEL DEPCA
#endif
static enum {
DEPCA, DE100, DE101, DE200, DE201, DE202, DE210, DE212, DE422, unknown
} adapter = DEPCA_MODEL;
/*
** Name <-> Adapter mapping
*/
static char *adapter_name[] = {
"DEPCA",
"DE100","DE101",
"DE200","DE201","DE202",
"DE210","DE212",
"DE422",
""
};
#ifndef DEPCA_RAM_BASE
#define DEPCA_RAM_BASE 0xd0000
#endif
/*
** Memory Alignment. Each descriptor is 4 longwords long. To force a
** particular alignment on the TX descriptor, adjust DESC_SKIP_LEN and
** DESC_ALIGN. ALIGN aligns the start address of the private memory area
** and hence the RX descriptor ring's first entry.
*/
#define ALIGN4 ((u32)4 - 1) /* 1 longword align */
#define ALIGN8 ((u32)8 - 1) /* 2 longword (quadword) align */
#define ALIGN ALIGN8 /* Keep the LANCE happy... */
typedef long s32;
typedef unsigned long u32;
typedef short s16;
typedef unsigned short u16;
typedef char s8;
typedef unsigned char u8;
/*
** The DEPCA Rx and Tx ring descriptors.
*/
struct depca_rx_desc {
volatile s32 base;
s16 buf_length; /* This length is negative 2's complement! */
s16 msg_length; /* This length is "normal". */
};
struct depca_tx_desc {
volatile s32 base;
s16 length; /* This length is negative 2's complement! */
s16 misc; /* Errors and TDR info */
};
#define LA_MASK 0x0000ffff /* LANCE address mask for mapping network RAM
to LANCE memory address space */
/*
** The Lance initialization block, described in databook, in common memory.
*/
struct depca_init {
u16 mode; /* Mode register */
u8 phys_addr[ETH_ALEN]; /* Physical ethernet address */
u8 mcast_table[8]; /* Multicast Hash Table. */
u32 rx_ring; /* Rx ring base pointer & ring length */
u32 tx_ring; /* Tx ring base pointer & ring length */
};
struct depca_private {
struct depca_rx_desc *rx_ring;
struct depca_tx_desc *tx_ring;
struct depca_init init_block; /* Shadow init block */
char *rx_memcpy[NUM_RX_DESC];
char *tx_memcpy[NUM_TX_DESC];
u32 bus_offset; /* ISA bus address offset */
u32 sh_mem; /* address of shared mem */
u32 dma_buffs; /* Rx & Tx buffer start */
int rx_cur, tx_cur; /* Next free ring entry */
int txRingMask, rxRingMask;
s32 rx_rlen, tx_rlen;
/* log2([rt]xRingMask+1) for the descriptors */
};
static Address mem_start = DEPCA_RAM_BASE;
static Address mem_len, offset;
static unsigned short ioaddr = 0;
static struct depca_private lp;
/*
** Miscellaneous defines...
*/
#define STOP_DEPCA \
outw(CSR0, DEPCA_ADDR);\
outw(STOP, DEPCA_DATA)
/* Initialize the lance Rx and Tx descriptor rings. */
static void depca_init_ring(struct nic *nic)
{
int i;
u32 p;
lp.rx_cur = lp.tx_cur = 0;
/* Initialize the base addresses and length of each buffer in the ring */
for (i = 0; i <= lp.rxRingMask; i++) {
writel((p = lp.dma_buffs + i * RX_BUFF_SZ) | R_OWN, &lp.rx_ring[i].base);
writew(-RX_BUFF_SZ, &lp.rx_ring[i].buf_length);
lp.rx_memcpy[i] = (char *) (p + lp.bus_offset);
}
for (i = 0; i <= lp.txRingMask; i++) {
writel((p = lp.dma_buffs + (i + lp.txRingMask + 1) * TX_BUFF_SZ) & 0x00ffffff, &lp.tx_ring[i].base);
lp.tx_memcpy[i] = (char *) (p + lp.bus_offset);
}
/* Set up the initialization block */
lp.init_block.rx_ring = ((u32) ((u32) lp.rx_ring) & LA_MASK) | lp.rx_rlen;
lp.init_block.tx_ring = ((u32) ((u32) lp.tx_ring) & LA_MASK) | lp.tx_rlen;
for (i = 0; i < ETH_ALEN; i++)
lp.init_block.phys_addr[i] = nic->node_addr[i];
lp.init_block.mode = 0x0000; /* Enable the Tx and Rx */
memset(lp.init_block.mcast_table, 0, sizeof(lp.init_block.mcast_table));
}
static void LoadCSRs(void)
{
outw(CSR1, DEPCA_ADDR); /* initialisation block address LSW */
outw((u16) (lp.sh_mem & LA_MASK), DEPCA_DATA);
outw(CSR2, DEPCA_ADDR); /* initialisation block address MSW */
outw((u16) ((lp.sh_mem & LA_MASK) >> 16), DEPCA_DATA);
outw(CSR3, DEPCA_ADDR); /* ALE control */
outw(ACON, DEPCA_DATA);
outw(CSR0, DEPCA_ADDR); /* Point back to CSR0 */
}
static int InitRestartDepca(void)
{
int i;
/* Copy the shadow init_block to shared memory */
memcpy_toio((char *)lp.sh_mem, &lp.init_block, sizeof(struct depca_init));
outw(CSR0, DEPCA_ADDR); /* point back to CSR0 */
outw(INIT, DEPCA_DATA); /* initialise DEPCA */
for (i = 0; i < 100 && !(inw(DEPCA_DATA) & IDON); i++)
;
if (i < 100) {
/* clear IDON by writing a 1, and start LANCE */
outw(IDON | STRT, DEPCA_DATA);
} else {
printf("DEPCA not initialised\n");
return (1);
}
return (0);
}
/**************************************************************************
RESET - Reset adapter
***************************************************************************/
static void depca_reset(struct nic *nic)
{
s16 nicsr;
int i, j;
STOP_DEPCA;
nicsr = inb(DEPCA_NICSR);
nicsr = ((nicsr & ~SHE & ~RBE & ~IEN) | IM);
outb(nicsr, DEPCA_NICSR);
if (inw(DEPCA_DATA) != STOP)
{
printf("depca: Cannot stop NIC\n");
return;
}
/* Initialisation block */
lp.sh_mem = mem_start;
mem_start += sizeof(struct depca_init);
/* Tx & Rx descriptors (aligned to a quadword boundary) */
mem_start = (mem_start + ALIGN) & ~ALIGN;
lp.rx_ring = (struct depca_rx_desc *) mem_start;
mem_start += (sizeof(struct depca_rx_desc) * NUM_RX_DESC);
lp.tx_ring = (struct depca_tx_desc *) mem_start;
mem_start += (sizeof(struct depca_tx_desc) * NUM_TX_DESC);
lp.bus_offset = mem_start & 0x00ff0000;
/* LANCE re-mapped start address */
lp.dma_buffs = mem_start & LA_MASK;
/* Finish initialising the ring information. */
lp.rxRingMask = NUM_RX_DESC - 1;
lp.txRingMask = NUM_TX_DESC - 1;
/* Calculate Tx/Rx RLEN size for the descriptors. */
for (i = 0, j = lp.rxRingMask; j > 0; i++) {
j >>= 1;
}
lp.rx_rlen = (s32) (i << 29);
for (i = 0, j = lp.txRingMask; j > 0; i++) {
j >>= 1;
}
lp.tx_rlen = (s32) (i << 29);
/* Load the initialisation block */
depca_init_ring(nic);
LoadCSRs();
InitRestartDepca();
}
/**************************************************************************
POLL - Wait for a frame
***************************************************************************/
static int depca_poll(struct nic *nic, int retrieve)
{
int entry;
u32 status;
entry = lp.rx_cur;
if ((status = readl(&lp.rx_ring[entry].base) & R_OWN))
return (0);
if ( ! retrieve ) return 1;
memcpy(nic->packet, lp.rx_memcpy[entry], nic->packetlen = lp.rx_ring[entry].msg_length);
lp.rx_ring[entry].base |= R_OWN;
lp.rx_cur = (++lp.rx_cur) & lp.rxRingMask;
return (1);
}
/**************************************************************************
TRANSMIT - Transmit a frame
***************************************************************************/
static void depca_transmit(
struct nic *nic,
const char *d, /* Destination */
unsigned int t, /* Type */
unsigned int s, /* size */
const char *p) /* Packet */
{
int entry, len;
char *mem;
/* send the packet to destination */
/*
** Caution: the right order is important here... dont
** setup the ownership rights until all the other
** information is in place
*/
mem = lp.tx_memcpy[entry = lp.tx_cur];
memcpy_toio(mem, d, ETH_ALEN);
memcpy_toio(mem + ETH_ALEN, nic->node_addr, ETH_ALEN);
mem[ETH_ALEN * 2] = t >> 8;
mem[ETH_ALEN * 2 + 1] = t;
memcpy_toio(mem + ETH_HLEN, p, s);
s += ETH_HLEN;
len = (s < ETH_ZLEN ? ETH_ZLEN : s);
/* clean out flags */
writel(readl(&lp.tx_ring[entry].base) & ~T_FLAGS, &lp.tx_ring[entry].base);
/* clears other error flags */
writew(0x0000, &lp.tx_ring[entry].misc);
/* packet length in buffer */
writew(-len, &lp.tx_ring[entry].length);
/* start and end of packet, ownership */
writel(readl(&lp.tx_ring[entry].base) | (T_STP|T_ENP|T_OWN), &lp.tx_ring[entry].base);
/* update current pointers */
lp.tx_cur = (++lp.tx_cur) & lp.txRingMask;
}
/**************************************************************************
DISABLE - Turn off ethernet interface
***************************************************************************/
static void depca_disable(struct dev *dev)
{
struct nic *nic = (struct nic *)dev;
/* reset and disable merge */
depca_reset(nic);
STOP_DEPCA;
}
/**************************************************************************
IRQ - Interrupt Control
***************************************************************************/
static void depca_irq(struct nic *nic __unused, irq_action_t action __unused)
{
switch ( action ) {
case DISABLE :
break;
case ENABLE :
break;
case FORCE :
break;
}
}
/*
** Look for a special sequence in the Ethernet station address PROM that
** is common across all DEPCA products. Note that the original DEPCA needs
** its ROM address counter to be initialized and enabled. Only enable
** if the first address octet is a 0x08 - this minimises the chances of
** messing around with some other hardware, but it assumes that this DEPCA
** card initialized itself correctly.
**
** Search the Ethernet address ROM for the signature. Since the ROM address
** counter can start at an arbitrary point, the search must include the entire
** probe sequence length plus the (length_of_the_signature - 1).
** Stop the search IMMEDIATELY after the signature is found so that the
** PROM address counter is correctly positioned at the start of the
** ethernet address for later read out.
*/
static int depca_probe1(struct nic *nic)
{
u8 data, nicsr;
/* This is only correct for little endian machines, but then
Etherboot doesn't work on anything but a PC */
u8 sig[] = { 0xFF, 0x00, 0x55, 0xAA, 0xFF, 0x00, 0x55, 0xAA };
int i, j;
long sum, chksum;
data = inb(DEPCA_PROM); /* clear counter on DEPCA */
data = inb(DEPCA_PROM); /* read data */
if (data == 0x8) {
nicsr = inb(DEPCA_NICSR);
nicsr |= AAC;
outb(nicsr, DEPCA_NICSR);
}
for (i = 0, j = 0; j < (int)sizeof(sig) && i < PROBE_LENGTH+((int)sizeof(sig))-1; ++i) {
data = inb(DEPCA_PROM);
if (data == sig[j]) /* track signature */
++j;
else
j = (data == sig[0]) ? 1 : 0;
}
if (j != sizeof(sig))
return (0);
/* put the card in its initial state */
STOP_DEPCA;
nicsr = ((inb(DEPCA_NICSR) & ~SHE & ~RBE & ~IEN) | IM);
outb(nicsr, DEPCA_NICSR);
if (inw(DEPCA_DATA) != STOP)
return (0);
memcpy((char *)mem_start, sig, sizeof(sig));
if (memcmp((char *)mem_start, sig, sizeof(sig)) != 0)
return (0);
for (i = 0, j = 0, sum = 0; j < 3; j++) {
sum <<= 1;
if (sum > 0xFFFF)
sum -= 0xFFFF;
sum += (u8)(nic->node_addr[i++] = inb(DEPCA_PROM));
sum += (u16)((nic->node_addr[i++] = inb(DEPCA_PROM)) << 8);
if (sum > 0xFFFF)
sum -= 0xFFFF;
}
if (sum == 0xFFFF)
sum = 0;
chksum = (u8)inb(DEPCA_PROM);
chksum |= (u16)(inb(DEPCA_PROM) << 8);
mem_len = (adapter == DEPCA) ? (48 << 10) : (64 << 10);
offset = 0;
if (nicsr & BUF) {
offset = 0x8000;
nicsr &= ~BS;
mem_len -= (32 << 10);
}
if (adapter != DEPCA) /* enable shadow RAM */
outb(nicsr |= SHE, DEPCA_NICSR);
printf("%s base %#hX, memory [%#hX-%#hX], addr %!",
adapter_name[adapter], ioaddr, mem_start, mem_start + mem_len,
nic->node_addr);
if (sum != chksum)
printf(" (bad checksum)");
putchar('\n');
return (1);
}
/**************************************************************************
PROBE - Look for an adapter, this routine's visible to the outside
***************************************************************************/
static int depca_probe(struct dev *dev, unsigned short *probe_addrs)
{
struct nic *nic = (struct nic *)dev;
static unsigned short base[] = DEPCA_IO_PORTS;
int i;
if (probe_addrs == 0 || probe_addrs[0] == 0)
probe_addrs = base; /* Use defaults */
for (i = 0; (ioaddr = base[i]) != 0; ++i) {
if (depca_probe1(nic))
break;
}
if (ioaddr == 0)
return (0);
nic->irqno = 0;
nic->ioaddr = ioaddr & ~3;
depca_reset(nic);
/* point to NIC specific routines */
dev->disable = depca_disable;
nic->poll = depca_poll;
nic->transmit = depca_transmit;
nic->irq = depca_irq;
/* Based on PnP ISA map */
dev->devid.vendor_id = htons(GENERIC_ISAPNP_VENDOR);
dev->devid.device_id = htons(0x80f7);
return 1;
}
static struct isa_driver depca_driver __isa_driver = {
.type = NIC_DRIVER,
.name = "DEPCA",
.probe = depca_probe,
.ioaddrs = 0,
};

1230
src/drivers/net/dmfe.c Normal file

File diff suppressed because it is too large Load Diff

3713
src/drivers/net/e1000.c Normal file

File diff suppressed because it is too large Load Diff

2058
src/drivers/net/e1000_hw.h Normal file

File diff suppressed because it is too large Load Diff

625
src/drivers/net/eepro.c Normal file
View File

@@ -0,0 +1,625 @@
#ifdef ALLMULTI
#error multicast support is not yet implemented
#endif
/**************************************************************************
Etherboot - BOOTP/TFTP Bootstrap Program
Intel EEPRO/10 NIC driver for Etherboot
Adapted from Linux eepro.c from kernel 2.2.17
This board accepts a 32 pin EEPROM (29C256), however a test with a
27C010 shows that this EPROM also works in the socket, but it's not clear
how repeatably. The two top address pins appear to be held low, thus
the bottom 32kB of the 27C010 is visible in the CPU's address space.
To be sure you could put 4 copies of the code in the 27C010, then
it doesn't matter whether the extra lines are held low or high, just
hopefully not floating as CMOS chips don't like floating inputs.
Be careful with seating the EPROM as the socket on my board actually
has 34 pins, the top row of 2 are not used.
***************************************************************************/
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2, or (at
* your option) any later version.
*/
/* to get some global routines like printf */
#include "etherboot.h"
/* to get the interface to the body of the program */
#include "nic.h"
#include "isa.h"
/* we use timer2 for microsecond waits */
#include "timer.h"
#undef DEBUG /* only after include files */
/* Different 82595 chips */
#define LAN595 0
#define LAN595TX 1
#define LAN595FX 2
#define LAN595FX_10ISA 3
#define SLOW_DOWN inb(0x80);
/* The station (ethernet) address prefix, used for IDing the board. */
#define SA_ADDR0 0x00 /* Etherexpress Pro/10 */
#define SA_ADDR1 0xaa
#define SA_ADDR2 0x00
#define GetBit(x,y) ((x & (1<<y))>>y)
/* EEPROM Word 0: */
#define ee_PnP 0 /* Plug 'n Play enable bit */
#define ee_Word1 1 /* Word 1? */
#define ee_BusWidth 2 /* 8/16 bit */
#define ee_FlashAddr 3 /* Flash Address */
#define ee_FlashMask 0x7 /* Mask */
#define ee_AutoIO 6 /* */
#define ee_reserved0 7 /* =0! */
#define ee_Flash 8 /* Flash there? */
#define ee_AutoNeg 9 /* Auto Negotiation enabled? */
#define ee_IO0 10 /* IO Address LSB */
#define ee_IO0Mask 0x /*...*/
#define ee_IO1 15 /* IO MSB */
/* EEPROM Word 1: */
#define ee_IntSel 0 /* Interrupt */
#define ee_IntMask 0x7
#define ee_LI 3 /* Link Integrity 0= enabled */
#define ee_PC 4 /* Polarity Correction 0= enabled */
#define ee_TPE_AUI 5 /* PortSelection 1=TPE */
#define ee_Jabber 6 /* Jabber prevention 0= enabled */
#define ee_AutoPort 7 /* Auto Port Selection 1= Disabled */
#define ee_SMOUT 8 /* SMout Pin Control 0= Input */
#define ee_PROM 9 /* Flash EPROM / PROM 0=Flash */
#define ee_reserved1 10 /* .. 12 =0! */
#define ee_AltReady 13 /* Alternate Ready, 0=normal */
#define ee_reserved2 14 /* =0! */
#define ee_Duplex 15
/* Word2,3,4: */
#define ee_IA5 0 /*bit start for individual Addr Byte 5 */
#define ee_IA4 8 /*bit start for individual Addr Byte 5 */
#define ee_IA3 0 /*bit start for individual Addr Byte 5 */
#define ee_IA2 8 /*bit start for individual Addr Byte 5 */
#define ee_IA1 0 /*bit start for individual Addr Byte 5 */
#define ee_IA0 8 /*bit start for individual Addr Byte 5 */
/* Word 5: */
#define ee_BNC_TPE 0 /* 0=TPE */
#define ee_BootType 1 /* 00=None, 01=IPX, 10=ODI, 11=NDIS */
#define ee_BootTypeMask 0x3
#define ee_NumConn 3 /* Number of Connections 0= One or Two */
#define ee_FlashSock 4 /* Presence of Flash Socket 0= Present */
#define ee_PortTPE 5
#define ee_PortBNC 6
#define ee_PortAUI 7
#define ee_PowerMgt 10 /* 0= disabled */
#define ee_CP 13 /* Concurrent Processing */
#define ee_CPMask 0x7
/* Word 6: */
#define ee_Stepping 0 /* Stepping info */
#define ee_StepMask 0x0F
#define ee_BoardID 4 /* Manucaturer Board ID, reserved */
#define ee_BoardMask 0x0FFF
/* Word 7: */
#define ee_INT_TO_IRQ 0 /* int to IRQ Mapping = 0x1EB8 for Pro/10+ */
#define ee_FX_INT2IRQ 0x1EB8 /* the _only_ mapping allowed for FX chips */
/*..*/
#define ee_SIZE 0x40 /* total EEprom Size */
#define ee_Checksum 0xBABA /* initial and final value for adding checksum */
/* Card identification via EEprom: */
#define ee_addr_vendor 0x10 /* Word offset for EISA Vendor ID */
#define ee_addr_id 0x11 /* Word offset for Card ID */
#define ee_addr_SN 0x12 /* Serial Number */
#define ee_addr_CRC_8 0x14 /* CRC over last thee Bytes */
#define ee_vendor_intel0 0x25 /* Vendor ID Intel */
#define ee_vendor_intel1 0xD4
#define ee_id_eepro10p0 0x10 /* ID for eepro/10+ */
#define ee_id_eepro10p1 0x31
/* now this section could be used by both boards: the oldies and the ee10:
* ee10 uses tx buffer before of rx buffer and the oldies the inverse.
* (aris)
*/
#define RAM_SIZE 0x8000
#define RCV_HEADER 8
#define RCV_DEFAULT_RAM 0x6000
#define RCV_RAM rcv_ram
static unsigned rcv_ram = RCV_DEFAULT_RAM;
#define XMT_HEADER 8
#define XMT_RAM (RAM_SIZE - RCV_RAM)
#define XMT_START ((rcv_start + RCV_RAM) % RAM_SIZE)
#define RCV_LOWER_LIMIT (rcv_start >> 8)
#define RCV_UPPER_LIMIT (((rcv_start + RCV_RAM) - 2) >> 8)
#define XMT_LOWER_LIMIT (XMT_START >> 8)
#define XMT_UPPER_LIMIT (((XMT_START + XMT_RAM) - 2) >> 8)
#define RCV_START_PRO 0x00
#define RCV_START_10 XMT_RAM
/* by default the old driver */
static unsigned rcv_start = RCV_START_PRO;
#define RCV_DONE 0x0008
#define RX_OK 0x2000
#define RX_ERROR 0x0d81
#define TX_DONE_BIT 0x0080
#define CHAIN_BIT 0x8000
#define XMT_STATUS 0x02
#define XMT_CHAIN 0x04
#define XMT_COUNT 0x06
#define BANK0_SELECT 0x00
#define BANK1_SELECT 0x40
#define BANK2_SELECT 0x80
/* Bank 0 registers */
#define COMMAND_REG 0x00 /* Register 0 */
#define MC_SETUP 0x03
#define XMT_CMD 0x04
#define DIAGNOSE_CMD 0x07
#define RCV_ENABLE_CMD 0x08
#define RCV_DISABLE_CMD 0x0a
#define STOP_RCV_CMD 0x0b
#define RESET_CMD 0x0e
#define POWER_DOWN_CMD 0x18
#define RESUME_XMT_CMD 0x1c
#define SEL_RESET_CMD 0x1e
#define STATUS_REG 0x01 /* Register 1 */
#define RX_INT 0x02
#define TX_INT 0x04
#define EXEC_STATUS 0x30
#define ID_REG 0x02 /* Register 2 */
#define R_ROBIN_BITS 0xc0 /* round robin counter */
#define ID_REG_MASK 0x2c
#define ID_REG_SIG 0x24
#define AUTO_ENABLE 0x10
#define INT_MASK_REG 0x03 /* Register 3 */
#define RX_STOP_MASK 0x01
#define RX_MASK 0x02
#define TX_MASK 0x04
#define EXEC_MASK 0x08
#define ALL_MASK 0x0f
#define IO_32_BIT 0x10
#define RCV_BAR 0x04 /* The following are word (16-bit) registers */
#define RCV_STOP 0x06
#define XMT_BAR_PRO 0x0a
#define XMT_BAR_10 0x0b
static unsigned xmt_bar = XMT_BAR_PRO;
#define HOST_ADDRESS_REG 0x0c
#define IO_PORT 0x0e
#define IO_PORT_32_BIT 0x0c
/* Bank 1 registers */
#define REG1 0x01
#define WORD_WIDTH 0x02
#define INT_ENABLE 0x80
#define INT_NO_REG 0x02
#define RCV_LOWER_LIMIT_REG 0x08
#define RCV_UPPER_LIMIT_REG 0x09
#define XMT_LOWER_LIMIT_REG_PRO 0x0a
#define XMT_UPPER_LIMIT_REG_PRO 0x0b
#define XMT_LOWER_LIMIT_REG_10 0x0b
#define XMT_UPPER_LIMIT_REG_10 0x0a
static unsigned xmt_lower_limit_reg = XMT_LOWER_LIMIT_REG_PRO;
static unsigned xmt_upper_limit_reg = XMT_UPPER_LIMIT_REG_PRO;
/* Bank 2 registers */
#define XMT_Chain_Int 0x20 /* Interrupt at the end of the transmit chain */
#define XMT_Chain_ErrStop 0x40 /* Interrupt at the end of the chain even if there are errors */
#define RCV_Discard_BadFrame 0x80 /* Throw bad frames away, and continue to receive others */
#define REG2 0x02
#define PRMSC_Mode 0x01
#define Multi_IA 0x20
#define REG3 0x03
#define TPE_BIT 0x04
#define BNC_BIT 0x20
#define REG13 0x0d
#define FDX 0x00
#define A_N_ENABLE 0x02
#define I_ADD_REG0 0x04
#define I_ADD_REG1 0x05
#define I_ADD_REG2 0x06
#define I_ADD_REG3 0x07
#define I_ADD_REG4 0x08
#define I_ADD_REG5 0x09
#define EEPROM_REG_PRO 0x0a
#define EEPROM_REG_10 0x0b
static unsigned eeprom_reg = EEPROM_REG_PRO;
#define EESK 0x01
#define EECS 0x02
#define EEDI 0x04
#define EEDO 0x08
/* The horrible routine to read a word from the serial EEPROM. */
/* IMPORTANT - the 82595 will be set to Bank 0 after the eeprom is read */
/* The delay between EEPROM clock transitions. */
#define eeprom_delay() { udelay(40); }
#define EE_READ_CMD (6 << 6)
/* do a full reset */
#define eepro_full_reset(ioaddr) outb(RESET_CMD, ioaddr); udelay(40);
/* do a nice reset */
#define eepro_sel_reset(ioaddr) { \
outb(SEL_RESET_CMD, ioaddr); \
SLOW_DOWN; \
SLOW_DOWN; \
}
/* clear all interrupts */
#define eepro_clear_int(ioaddr) outb(ALL_MASK, ioaddr + STATUS_REG)
/* enable rx */
#define eepro_en_rx(ioaddr) outb(RCV_ENABLE_CMD, ioaddr)
/* disable rx */
#define eepro_dis_rx(ioaddr) outb(RCV_DISABLE_CMD, ioaddr)
/* switch bank */
#define eepro_sw2bank0(ioaddr) outb(BANK0_SELECT, ioaddr)
#define eepro_sw2bank1(ioaddr) outb(BANK1_SELECT, ioaddr)
#define eepro_sw2bank2(ioaddr) outb(BANK2_SELECT, ioaddr)
static unsigned int rx_start, tx_start;
static int tx_last;
static unsigned int tx_end;
static int eepro = 0;
static unsigned short ioaddr = 0;
static unsigned int mem_start, mem_end = RCV_DEFAULT_RAM / 1024;
/**************************************************************************
RESET - Reset adapter
***************************************************************************/
static void eepro_reset(struct nic *nic)
{
int temp_reg, i;
/* put the card in its initial state */
eepro_sw2bank2(ioaddr); /* be careful, bank2 now */
temp_reg = inb(ioaddr + eeprom_reg);
#ifdef DEBUG
printf("Stepping %d\n", temp_reg >> 5);
#endif
if (temp_reg & 0x10) /* check the TurnOff Enable bit */
outb(temp_reg & 0xEF, ioaddr + eeprom_reg);
for (i = 0; i < ETH_ALEN; i++) /* fill the MAC address */
outb(nic->node_addr[i], ioaddr + I_ADD_REG0 + i);
temp_reg = inb(ioaddr + REG1);
/* setup Transmit Chaining and discard bad RCV frames */
outb(temp_reg | XMT_Chain_Int | XMT_Chain_ErrStop
| RCV_Discard_BadFrame, ioaddr + REG1);
temp_reg = inb(ioaddr + REG2); /* match broadcast */
outb(temp_reg | 0x14, ioaddr + REG2);
temp_reg = inb(ioaddr + REG3);
outb(temp_reg & 0x3F, ioaddr + REG3); /* clear test mode */
/* set the receiving mode */
eepro_sw2bank1(ioaddr); /* be careful, bank1 now */
/* initialise the RCV and XMT upper and lower limits */
outb(RCV_LOWER_LIMIT, ioaddr + RCV_LOWER_LIMIT_REG);
outb(RCV_UPPER_LIMIT, ioaddr + RCV_UPPER_LIMIT_REG);
outb(XMT_LOWER_LIMIT, ioaddr + xmt_lower_limit_reg);
outb(XMT_UPPER_LIMIT, ioaddr + xmt_upper_limit_reg);
eepro_sw2bank0(ioaddr); /* Switch back to bank 0 */
eepro_clear_int(ioaddr);
/* Initialise RCV */
rx_start = (unsigned int)bus_to_virt(RCV_LOWER_LIMIT << 8);
outw(RCV_LOWER_LIMIT << 8, ioaddr + RCV_BAR);
outw(((RCV_UPPER_LIMIT << 8) | 0xFE), ioaddr + RCV_STOP);
/* Intialise XMT */
outw((XMT_LOWER_LIMIT << 8), ioaddr + xmt_bar);
eepro_sel_reset(ioaddr);
tx_start = tx_end = (unsigned int)bus_to_virt(XMT_LOWER_LIMIT << 8);
tx_last = 0;
eepro_en_rx(ioaddr);
}
/**************************************************************************
POLL - Wait for a frame
***************************************************************************/
static int eepro_poll(struct nic *nic, int retrieve)
{
unsigned int rcv_car = virt_to_bus((void *)rx_start);
unsigned int rcv_event, rcv_status, rcv_next_frame, rcv_size;
/* return true if there's an ethernet packet ready to read */
/* nic->packet should contain data on return */
/* nic->packetlen should contain length of data */
#if 0
if ((inb(ioaddr + STATUS_REG) & 0x40) == 0)
return (0);
outb(0x40, ioaddr + STATUS_REG);
#endif
outw(rcv_car, ioaddr + HOST_ADDRESS_REG);
rcv_event = inw(ioaddr + IO_PORT);
if (rcv_event != RCV_DONE)
return (0);
/* FIXME: I'm guessing this might not work with this card, since
it looks like once a rcv_event is started it must be completed.
maybe there's another way. */
if ( ! retrieve ) return 1;
rcv_status = inw(ioaddr + IO_PORT);
rcv_next_frame = inw(ioaddr + IO_PORT);
rcv_size = inw(ioaddr + IO_PORT);
#if 0
printf("%hX %hX %d %hhX\n", rcv_status, rcv_next_frame, rcv_size,
inb(ioaddr + STATUS_REG));
#endif
if ((rcv_status & (RX_OK|RX_ERROR)) != RX_OK) {
printf("Receive error %hX\n", rcv_status);
return (0);
}
rcv_size &= 0x3FFF;
insw(ioaddr + IO_PORT, nic->packet, ((rcv_size + 3) >> 1));
#if 0
{
int i;
for (i = 0; i < 48; i++) {
printf("%hhX", nic->packet[i]);
putchar(i % 16 == 15 ? '\n' : ' ');
}
}
#endif
nic->packetlen = rcv_size;
rcv_car = virt_to_bus((void *) (rx_start + RCV_HEADER + rcv_size));
rx_start = (unsigned int)bus_to_virt(rcv_next_frame << 8);
if (rcv_car == 0)
rcv_car = ((RCV_UPPER_LIMIT << 8) | 0xff);
outw(rcv_car - 1, ioaddr + RCV_STOP);
return (1);
}
/**************************************************************************
TRANSMIT - Transmit a frame
***************************************************************************/
static void eepro_transmit(
struct nic *nic,
const char *d, /* Destination */
unsigned int t, /* Type */
unsigned int s, /* size */
const char *p) /* Packet */
{
unsigned int status, tx_available, last, end, length;
unsigned short type;
int boguscount = 20;
length = s + ETH_HLEN;
if (tx_end > tx_start)
tx_available = XMT_RAM - (tx_end - tx_start);
else if (tx_end < tx_start)
tx_available = tx_start - tx_end;
else
tx_available = XMT_RAM;
last = tx_end;
end = last + (((length + 3) >> 1) << 1) + XMT_HEADER;
if (end >= (XMT_UPPER_LIMIT << 8)) {
last = (XMT_LOWER_LIMIT << 8);
end = last + (((length + 3) >> 1) << 1) + XMT_HEADER;
}
outw(last, ioaddr + HOST_ADDRESS_REG);
outw(XMT_CMD, ioaddr + IO_PORT);
outw(0, ioaddr + IO_PORT);
outw(end, ioaddr + IO_PORT);
outw(length, ioaddr + IO_PORT);
outsw(ioaddr + IO_PORT, d, ETH_ALEN / 2);
outsw(ioaddr + IO_PORT, nic->node_addr, ETH_ALEN / 2);
type = htons(t);
outsw(ioaddr + IO_PORT, &type, sizeof(type) / 2);
outsw(ioaddr + IO_PORT, p, (s + 3) >> 1);
/* A dummy read to flush the DRAM write pipeline */
status = inw(ioaddr + IO_PORT);
outw(last, ioaddr + xmt_bar);
outb(XMT_CMD, ioaddr);
tx_start = last;
tx_last = last;
tx_end = end;
#if 0
printf("%d %d\n", tx_start, tx_end);
#endif
while (boguscount > 0) {
if (((status = inw(ioaddr + IO_PORT)) & TX_DONE_BIT) == 0) {
udelay(40);
boguscount--;
continue;
}
#if DEBUG
if ((status & 0x2000) == 0)
printf("Transmit status %hX\n", status);
#endif
}
}
/**************************************************************************
DISABLE - Turn off ethernet interface
***************************************************************************/
static void eepro_disable(struct dev *dev __unused)
{
eepro_sw2bank0(ioaddr); /* Switch to bank 0 */
/* Flush the Tx and disable Rx */
outb(STOP_RCV_CMD, ioaddr);
tx_start = tx_end = (unsigned int) (bus_to_virt(XMT_LOWER_LIMIT << 8));
tx_last = 0;
/* Reset the 82595 */
eepro_full_reset(ioaddr);
}
/**************************************************************************
DISABLE - Enable, Disable, or Force interrupts
***************************************************************************/
static void eepro_irq(struct nic *nic __unused, irq_action_t action __unused)
{
switch ( action ) {
case DISABLE :
break;
case ENABLE :
break;
case FORCE :
break;
}
}
static int read_eeprom(int location)
{
int i;
unsigned short retval = 0;
int ee_addr = ioaddr + eeprom_reg;
int read_cmd = location | EE_READ_CMD;
int ctrl_val = EECS;
if (eepro == LAN595FX_10ISA) {
eepro_sw2bank1(ioaddr);
outb(0x00, ioaddr + STATUS_REG);
}
eepro_sw2bank2(ioaddr);
outb(ctrl_val, ee_addr);
/* shift the read command bits out */
for (i = 8; i >= 0; i--) {
short outval = (read_cmd & (1 << i)) ? ctrl_val | EEDI : ctrl_val;
outb(outval, ee_addr);
outb(outval | EESK, ee_addr); /* EEPROM clock tick */
eeprom_delay();
outb(outval, ee_addr); /* finish EEPROM clock tick */
eeprom_delay();
}
outb(ctrl_val, ee_addr);
for (i = 16; i > 0; i--) {
outb(ctrl_val | EESK, ee_addr);
eeprom_delay();
retval = (retval << 1) | ((inb(ee_addr) & EEDO) ? 1 : 0);
outb(ctrl_val, ee_addr);
eeprom_delay();
}
/* terminate the EEPROM access */
ctrl_val &= ~EECS;
outb(ctrl_val | EESK, ee_addr);
eeprom_delay();
outb(ctrl_val, ee_addr);
eeprom_delay();
eepro_sw2bank0(ioaddr);
return (retval);
}
static int eepro_probe1(struct nic *nic)
{
int i, id, counter, l_eepro = 0;
union {
unsigned char caddr[ETH_ALEN];
unsigned short saddr[ETH_ALEN/2];
} station_addr;
char *name;
id = inb(ioaddr + ID_REG);
if ((id & ID_REG_MASK) != ID_REG_SIG)
return (0);
counter = id & R_ROBIN_BITS;
if (((id = inb(ioaddr + ID_REG)) & R_ROBIN_BITS) != (counter + 0x40))
return (0);
/* yes the 82595 has been found */
station_addr.saddr[2] = read_eeprom(2);
if (station_addr.saddr[2] == 0x0000 || station_addr.saddr[2] == 0xFFFF) {
l_eepro = 3;
eepro = LAN595FX_10ISA;
eeprom_reg= EEPROM_REG_10;
rcv_start = RCV_START_10;
xmt_lower_limit_reg = XMT_LOWER_LIMIT_REG_10;
xmt_upper_limit_reg = XMT_UPPER_LIMIT_REG_10;
station_addr.saddr[2] = read_eeprom(2);
}
station_addr.saddr[1] = read_eeprom(3);
station_addr.saddr[0] = read_eeprom(4);
if (l_eepro)
name = "Intel EtherExpress 10 ISA";
else if (read_eeprom(7) == ee_FX_INT2IRQ) {
name = "Intel EtherExpress Pro/10+ ISA";
l_eepro = 2;
} else if (station_addr.saddr[0] == SA_ADDR1) {
name = "Intel EtherExpress Pro/10 ISA";
l_eepro = 1;
} else {
l_eepro = 0;
name = "Intel 82595-based LAN card";
}
station_addr.saddr[0] = swap16(station_addr.saddr[0]);
station_addr.saddr[1] = swap16(station_addr.saddr[1]);
station_addr.saddr[2] = swap16(station_addr.saddr[2]);
for (i = 0; i < ETH_ALEN; i++) {
nic->node_addr[i] = station_addr.caddr[i];
}
printf("\n%s ioaddr %#hX, addr %!", name, ioaddr, nic->node_addr);
mem_start = RCV_LOWER_LIMIT << 8;
if ((mem_end & 0x3F) < 3 || (mem_end & 0x3F) > 29)
mem_end = RCV_UPPER_LIMIT << 8;
else {
mem_end = mem_end * 1024 + (RCV_LOWER_LIMIT << 8);
rcv_ram = mem_end - (RCV_LOWER_LIMIT << 8);
}
printf(", Rx mem %dK, if %s\n", (mem_end - mem_start) >> 10,
GetBit(read_eeprom(5), ee_BNC_TPE) ? "BNC" : "TP");
return (1);
}
/**************************************************************************
PROBE - Look for an adapter, this routine's visible to the outside
***************************************************************************/
static int eepro_probe(struct dev *dev, unsigned short *probe_addrs)
{
struct nic *nic = (struct nic *)dev;
unsigned short *p;
/* same probe list as the Linux driver */
static unsigned short ioaddrs[] = {
0x300, 0x210, 0x240, 0x280, 0x2C0, 0x200, 0x320, 0x340, 0x360, 0};
if (probe_addrs == 0 || probe_addrs[0] == 0)
probe_addrs = ioaddrs;
for (p = probe_addrs; (ioaddr = *p) != 0; p++) {
if (eepro_probe1(nic))
break;
}
if (*p == 0)
return (0);
nic->irqno = 0;
nic->ioaddr = *p;
eepro_reset(nic);
/* point to NIC specific routines */
dev->disable = eepro_disable;
nic->poll = eepro_poll;
nic->transmit = eepro_transmit;
nic->irq = eepro_irq;
/* Based on PnP ISA map */
dev->devid.vendor_id = htons(GENERIC_ISAPNP_VENDOR);
dev->devid.device_id = htons(0x828a);
return 1;
}
static struct isa_driver eepro_driver __isa_driver = {
.type = NIC_DRIVER,
.name = "EEPRO",
.probe = eepro_probe,
.ioaddrs = 0,
};

841
src/drivers/net/eepro100.c Normal file
View File

@@ -0,0 +1,841 @@
/*
* eepro100.c -- This file implements the eepro100 driver for etherboot.
*
*
* Copyright (C) AW Computer Systems.
* written by R.E.Wolff -- R.E.Wolff@BitWizard.nl
*
*
* AW Computer Systems is contributing to the free software community
* by paying for this driver and then putting the result under GPL.
*
* If you need a Linux device driver, please contact BitWizard for a
* quote.
*
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*
* date version by what
* Written: May 29 1997 V0.10 REW Initial revision.
* changes: May 31 1997 V0.90 REW Works!
* Jun 1 1997 V0.91 REW Cleanup
* Jun 2 1997 V0.92 REW Add some code documentation
* Jul 25 1997 V1.00 REW Tested by AW to work in a PROM
* Cleanup for publication
* Dez 11 2004 V1.10 Kiszka Add RX ring buffer support
*
* This is the etherboot intel etherexpress Pro/100B driver.
*
* It was written from scratch, with Donald Beckers eepro100.c kernel
* driver as a guideline. Mostly the 82557 related definitions and the
* lower level routines have been cut-and-pasted into this source.
*
* The driver was finished before Intel got the NDA out of the closet.
* I still don't have the docs.
*
*
* Datasheet is now published and available from
* ftp://download.intel.com/design/network/manuals/8255X_OpenSDM.pdf
* - Michael Brown
* */
/* Philosophy of this driver.
*
* Probing:
*
* Using the pci.c functions of the Etherboot code, the 82557 chip is detected.
* It is verified that the BIOS initialized everything properly and if
* something is missing it is done now.
*
*
* Initialization:
*
*
* The chip is then initialized to "know" its ethernet address, and to
* start recieving packets. The Linux driver has a whole transmit and
* recieve ring of buffers. This is neat if you need high performance:
* you can write the buffers asynchronously to the chip reading the
* buffers and transmitting them over the network. Performance is NOT
* an issue here. We can boot a 400k kernel in about two
* seconds. (Theory: 0.4 seconds). Booting a system is going to take
* about half a minute anyway, so getting 10 times closer to the
* theoretical limit is going to make a difference of a few percent. */
/* Not totally true: busy networks can cause packet drops due to RX
* buffer overflows. Fixed in V1.10 of this driver. [Kiszka] */
/*
*
* Transmitting and recieving.
*
* We have only one transmit descriptor. It has two buffer descriptors:
* one for the header, and the other for the data.
* We have multiple receive buffers (currently: 4). The chip is told to
* receive packets and suspend itself once it ran on the last free buffer.
* The recieve (poll) routine simply looks at the current recieve buffer,
* picks the packet if any, and releases this buffer again (classic ring
* buffer concept). This helps to avoid packet drops on busy networks.
*
* Caveats:
*
* The Etherboot framework moves the code to the 48k segment from
* 0x94000 to 0xa0000. There is just a little room between the end of
* this driver and the 0xa0000 address. If you compile in too many
* features, this will overflow.
* The number under "hex" in the output of size that scrolls by while
* compiling should be less than 8000. Maybe even the stack is up there,
* so that you need even more headroom.
*/
/* The etherboot authors seem to dislike the argument ordering in
* outb macros that Linux uses. I disklike the confusion that this
* has caused even more.... This file uses the Linux argument ordering. */
/* Sorry not us. It's inherited code from FreeBSD. [The authors] */
#include "etherboot.h"
#include "nic.h"
#include "pci.h"
#include "timer.h"
static int ioaddr;
typedef unsigned char u8;
typedef signed char s8;
typedef unsigned short u16;
typedef signed short s16;
typedef unsigned int u32;
typedef signed int s32;
enum speedo_offsets {
SCBStatus = 0, SCBCmd = 2, /* Rx/Command Unit command and status. */
SCBPointer = 4, /* General purpose pointer. */
SCBPort = 8, /* Misc. commands and operands. */
SCBflash = 12, SCBeeprom = 14, /* EEPROM and flash memory control. */
SCBCtrlMDI = 16, /* MDI interface control. */
SCBEarlyRx = 20, /* Early receive byte count. */
};
enum SCBCmdBits {
SCBMaskCmdDone=0x8000, SCBMaskRxDone=0x4000, SCBMaskCmdIdle=0x2000,
SCBMaskRxSuspend=0x1000, SCBMaskEarlyRx=0x0800, SCBMaskFlowCtl=0x0400,
SCBTriggerIntr=0x0200, SCBMaskAll=0x0100,
/* The rest are Rx and Tx commands. */
CUStart=0x0010, CUResume=0x0020, CUStatsAddr=0x0040, CUShowStats=0x0050,
CUCmdBase=0x0060, /* CU Base address (set to zero) . */
CUDumpStats=0x0070, /* Dump then reset stats counters. */
RxStart=0x0001, RxResume=0x0002, RxAbort=0x0004, RxAddrLoad=0x0006,
RxResumeNoResources=0x0007,
};
static int do_eeprom_cmd(int cmd, int cmd_len);
void hd(void *where, int n);
/***********************************************************************/
/* I82557 related defines */
/***********************************************************************/
/* Serial EEPROM section.
A "bit" grungy, but we work our way through bit-by-bit :->. */
/* EEPROM_Ctrl bits. */
#define EE_SHIFT_CLK 0x01 /* EEPROM shift clock. */
#define EE_CS 0x02 /* EEPROM chip select. */
#define EE_DATA_WRITE 0x04 /* EEPROM chip data in. */
#define EE_DATA_READ 0x08 /* EEPROM chip data out. */
#define EE_WRITE_0 0x4802
#define EE_WRITE_1 0x4806
#define EE_ENB (0x4800 | EE_CS)
/* The EEPROM commands include the alway-set leading bit. */
#define EE_READ_CMD 6
/* The SCB accepts the following controls for the Tx and Rx units: */
#define CU_START 0x0010
#define CU_RESUME 0x0020
#define CU_STATSADDR 0x0040
#define CU_SHOWSTATS 0x0050 /* Dump statistics counters. */
#define CU_CMD_BASE 0x0060 /* Base address to add to add CU commands. */
#define CU_DUMPSTATS 0x0070 /* Dump then reset stats counters. */
#define RX_START 0x0001
#define RX_RESUME 0x0002
#define RX_ABORT 0x0004
#define RX_ADDR_LOAD 0x0006
#define RX_RESUMENR 0x0007
#define INT_MASK 0x0100
#define DRVR_INT 0x0200 /* Driver generated interrupt. */
enum phy_chips { NonSuchPhy=0, I82553AB, I82553C, I82503, DP83840, S80C240,
S80C24, PhyUndefined, DP83840A=10, };
/* Commands that can be put in a command list entry. */
enum commands {
CmdNOp = 0,
CmdIASetup = 1,
CmdConfigure = 2,
CmdMulticastList = 3,
CmdTx = 4,
CmdTDR = 5,
CmdDump = 6,
CmdDiagnose = 7,
/* And some extra flags: */
CmdSuspend = 0x4000, /* Suspend after completion. */
CmdIntr = 0x2000, /* Interrupt after completion. */
CmdTxFlex = 0x0008, /* Use "Flexible mode" for CmdTx command. */
};
/* How to wait for the command unit to accept a command.
Typically this takes 0 ticks. */
static inline void wait_for_cmd_done(int cmd_ioaddr)
{
int wait = 0;
int delayed_cmd;
do
if (inb(cmd_ioaddr) == 0) return;
while(++wait <= 100);
delayed_cmd = inb(cmd_ioaddr);
do
if (inb(cmd_ioaddr) == 0) break;
while(++wait <= 10000);
printf("Command %2.2x was not immediately accepted, %d ticks!\n",
delayed_cmd, wait);
}
/* Elements of the dump_statistics block. This block must be lword aligned. */
static struct speedo_stats {
u32 tx_good_frames;
u32 tx_coll16_errs;
u32 tx_late_colls;
u32 tx_underruns;
u32 tx_lost_carrier;
u32 tx_deferred;
u32 tx_one_colls;
u32 tx_multi_colls;
u32 tx_total_colls;
u32 rx_good_frames;
u32 rx_crc_errs;
u32 rx_align_errs;
u32 rx_resource_errs;
u32 rx_overrun_errs;
u32 rx_colls_errs;
u32 rx_runt_errs;
u32 done_marker;
} lstats;
/* A speedo3 TX buffer descriptor with two buffers... */
static struct TxFD {
volatile s16 status;
s16 command;
u32 link; /* void * */
u32 tx_desc_addr; /* (almost) Always points to the tx_buf_addr element. */
s32 count; /* # of TBD (=2), Tx start thresh., etc. */
/* This constitutes two "TBD" entries: hdr and data */
u32 tx_buf_addr0; /* void *, header of frame to be transmitted. */
s32 tx_buf_size0; /* Length of Tx hdr. */
u32 tx_buf_addr1; /* void *, data to be transmitted. */
s32 tx_buf_size1; /* Length of Tx data. */
} txfd;
struct RxFD { /* Receive frame descriptor. */
volatile s16 status;
s16 command;
u32 link; /* struct RxFD * */
u32 rx_buf_addr; /* void * */
u16 count;
u16 size;
char packet[1518];
};
#define RXFD_COUNT 4
static struct RxFD rxfds[RXFD_COUNT];
static unsigned int rxfd = 0;
static int congenb = 0; /* Enable congestion control in the DP83840. */
static int txfifo = 8; /* Tx FIFO threshold in 4 byte units, 0-15 */
static int rxfifo = 8; /* Rx FIFO threshold, default 32 bytes. */
static int txdmacount = 0; /* Tx DMA burst length, 0-127, default 0. */
static int rxdmacount = 0; /* Rx DMA length, 0 means no preemption. */
/* I don't understand a byte in this structure. It was copied from the
* Linux kernel initialization for the eepro100. -- REW */
static struct ConfCmd {
s16 status;
s16 command;
u32 link;
unsigned char data[22];
} confcmd = {
0, 0, 0, /* filled in later */
{22, 0x08, 0, 0, 0, 0x80, 0x32, 0x03, 1, /* 1=Use MII 0=Use AUI */
0, 0x2E, 0, 0x60, 0,
0xf2, 0x48, 0, 0x40, 0xf2, 0x80, /* 0x40=Force full-duplex */
0x3f, 0x05, }
};
/***********************************************************************/
/* Locally used functions */
/***********************************************************************/
/* Support function: mdio_write
*
* This probably writes to the "physical media interface chip".
* -- REW
*/
static int mdio_write(int phy_id, int location, int value)
{
int val, boguscnt = 64*4; /* <64 usec. to complete, typ 27 ticks */
outl(0x04000000 | (location<<16) | (phy_id<<21) | value,
ioaddr + SCBCtrlMDI);
do {
udelay(16);
val = inl(ioaddr + SCBCtrlMDI);
if (--boguscnt < 0) {
printf(" mdio_write() timed out with val = %X.\n", val);
break;
}
} while (! (val & 0x10000000));
return val & 0xffff;
}
/* Support function: mdio_read
*
* This probably reads a register in the "physical media interface chip".
* -- REW
*/
static int mdio_read(int phy_id, int location)
{
int val, boguscnt = 64*4; /* <64 usec. to complete, typ 27 ticks */
outl(0x08000000 | (location<<16) | (phy_id<<21), ioaddr + SCBCtrlMDI);
do {
udelay(16);
val = inl(ioaddr + SCBCtrlMDI);
if (--boguscnt < 0) {
printf( " mdio_read() timed out with val = %X.\n", val);
break;
}
} while (! (val & 0x10000000));
return val & 0xffff;
}
/* The fixes for the code were kindly provided by Dragan Stancevic
<visitor@valinux.com> to strictly follow Intel specifications of EEPROM
access timing.
The publicly available sheet 64486302 (sec. 3.1) specifies 1us access
interval for serial EEPROM. However, it looks like that there is an
additional requirement dictating larger udelay's in the code below.
2000/05/24 SAW */
static int do_eeprom_cmd(int cmd, int cmd_len)
{
unsigned retval = 0;
long ee_addr = ioaddr + SCBeeprom;
outw(EE_ENB, ee_addr); udelay(2);
outw(EE_ENB | EE_SHIFT_CLK, ee_addr); udelay(2);
/* Shift the command bits out. */
do {
short dataval = (cmd & (1 << cmd_len)) ? EE_WRITE_1 : EE_WRITE_0;
outw(dataval, ee_addr); udelay(2);
outw(dataval | EE_SHIFT_CLK, ee_addr); udelay(2);
retval = (retval << 1) | ((inw(ee_addr) & EE_DATA_READ) ? 1 : 0);
} while (--cmd_len >= 0);
outw(EE_ENB, ee_addr); udelay(2);
/* Terminate the EEPROM access. */
outw(EE_ENB & ~EE_CS, ee_addr);
return retval;
}
#if 0
static inline void whereami (const char *str)
{
printf ("%s\n", str);
sleep (2);
}
#else
#define whereami(s)
#endif
static void eepro100_irq(struct nic *nic __unused, irq_action_t action)
{
uint16_t enabled_mask = ( SCBMaskCmdDone | SCBMaskCmdIdle |
SCBMaskEarlyRx | SCBMaskFlowCtl );
switch ( action ) {
case DISABLE :
outw(SCBMaskAll, ioaddr + SCBCmd);
break;
case ENABLE :
outw(enabled_mask, ioaddr + SCBCmd);
break;
case FORCE :
outw(enabled_mask | SCBTriggerIntr, ioaddr + SCBCmd);
break;
}
}
/* function: eepro100_transmit
* This transmits a packet.
*
* Arguments: char d[6]: destination ethernet address.
* unsigned short t: ethernet protocol type.
* unsigned short s: size of the data-part of the packet.
* char *p: the data for the packet.
* returns: void.
*/
static void eepro100_transmit(struct nic *nic, const char *d, unsigned int t, unsigned int s, const char *p)
{
struct eth_hdr {
unsigned char dst_addr[ETH_ALEN];
unsigned char src_addr[ETH_ALEN];
unsigned short type;
} hdr;
unsigned short status;
int s1, s2;
status = inw(ioaddr + SCBStatus);
/* Acknowledge all of the current interrupt sources ASAP. */
outw(status & 0xfc00, ioaddr + SCBStatus);
#ifdef DEBUG
printf ("transmitting type %hX packet (%d bytes). status = %hX, cmd=%hX\n",
t, s, status, inw (ioaddr + SCBCmd));
#endif
memcpy (&hdr.dst_addr, d, ETH_ALEN);
memcpy (&hdr.src_addr, nic->node_addr, ETH_ALEN);
hdr.type = htons (t);
txfd.status = 0;
txfd.command = CmdSuspend | CmdTx | CmdTxFlex;
txfd.link = virt_to_bus (&txfd);
txfd.count = 0x02208000;
txfd.tx_desc_addr = virt_to_bus(&txfd.tx_buf_addr0);
txfd.tx_buf_addr0 = virt_to_bus (&hdr);
txfd.tx_buf_size0 = sizeof (hdr);
txfd.tx_buf_addr1 = virt_to_bus (p);
txfd.tx_buf_size1 = s;
#ifdef DEBUG
printf ("txfd: \n");
hd (&txfd, sizeof (txfd));
#endif
outl(virt_to_bus(&txfd), ioaddr + SCBPointer);
outb(CU_START, ioaddr + SCBCmd);
wait_for_cmd_done(ioaddr + SCBCmd);
s1 = inw (ioaddr + SCBStatus);
load_timer2(10*TICKS_PER_MS); /* timeout 10 ms for transmit */
while (!txfd.status && timer2_running())
/* Wait */;
s2 = inw (ioaddr + SCBStatus);
#ifdef DEBUG
printf ("s1 = %hX, s2 = %hX.\n", s1, s2);
#endif
}
/*
* Sometimes the receiver stops making progress. This routine knows how to
* get it going again, without losing packets or being otherwise nasty like
* a chip reset would be. Previously the driver had a whole sequence
* of if RxSuspended, if it's no buffers do one thing, if it's no resources,
* do another, etc. But those things don't really matter. Separate logic
* in the ISR provides for allocating buffers--the other half of operation
* is just making sure the receiver is active. speedo_rx_soft_reset does that.
* This problem with the old, more involved algorithm is shown up under
* ping floods on the order of 60K packets/second on a 100Mbps fdx network.
*/
static void
speedo_rx_soft_reset(void)
{
int i;
#ifdef DEBUG
printf("reset\n");
#endif
wait_for_cmd_done(ioaddr + SCBCmd);
/*
* Put the hardware into a known state.
*/
outb(RX_ABORT, ioaddr + SCBCmd);
for (i = 0; i < RXFD_COUNT; i++) {
rxfds[i].status = 0;
rxfds[i].rx_buf_addr = 0xffffffff;
rxfds[i].count = 0;
rxfds[i].size = 1528;
}
wait_for_cmd_done(ioaddr + SCBCmd);
outl(virt_to_bus(&rxfds[rxfd]), ioaddr + SCBPointer);
outb(RX_START, ioaddr + SCBCmd);
}
/* function: eepro100_poll / eth_poll
* This receives a packet from the network.
*
* Arguments: none
*
* returns: 1 if a packet was received.
* 0 if no packet was received.
* side effects:
* returns the packet in the array nic->packet.
* returns the length of the packet in nic->packetlen.
*/
static int eepro100_poll(struct nic *nic, int retrieve)
{
if (rxfds[rxfd].status) {
if (!retrieve)
return 1;
#ifdef DEBUG
printf("Got a packet: Len = %d, rxfd = %d.\n",
rxfds[rxfd].count & 0x3fff, rxfd);
#endif
/* First save the data from the rxfd */
nic->packetlen = rxfds[rxfd].count & 0x3fff;
memcpy(nic->packet, rxfds[rxfd].packet, nic->packetlen);
rxfds[rxfd].status = 0;
rxfds[rxfd].command = 0xc000;
rxfds[rxfd].rx_buf_addr = 0xFFFFFFFF;
rxfds[rxfd].count = 0;
rxfds[rxfd].size = 1528;
rxfds[(rxfd-1) % RXFD_COUNT].command = 0x0000;
rxfd = (rxfd+1) % RXFD_COUNT;
#ifdef DEBUG
hd (nic->packet, 0x30);
#endif
/* Acknowledge all conceivable interrupts */
outw(0xff00, ioaddr + SCBStatus);
return 1;
}
/*
* The chip may have suspended reception for various reasons.
* Check for that, and re-prime it should this be the case.
*/
switch ((inw(ioaddr + SCBStatus) >> 2) & 0xf) {
case 0: /* Idle */
break;
case 1: /* Suspended */
case 2: /* No resources (RxFDs) */
case 9: /* Suspended with no more RBDs */
case 10: /* No resources due to no RBDs */
case 12: /* Ready with no RBDs */
speedo_rx_soft_reset();
break;
default:
/* reserved values */
break;
}
return 0;
}
/* function: eepro100_disable
* resets the card. This is used to allow Etherboot or Linux
* to probe the card again from a "virginal" state....
* Arguments: none
*
* returns: void.
*/
static void eepro100_disable(struct dev *dev __unused)
{
/* from eepro100_reset */
outl(0, ioaddr + SCBPort);
/* from eepro100_disable */
/* See if this PartialReset solves the problem with interfering with
kernel operation after Etherboot hands over. - Ken 20001102 */
outl(2, ioaddr + SCBPort);
/* The following is from the Intel e100 driver.
* This hopefully solves the problem with hanging hard DOS images. */
/* wait for the reset to take effect */
udelay(20);
/* Mask off our interrupt line -- it is unmasked after reset */
{
u16 intr_status;
/* Disable interrupts on our PCI board by setting the mask bit */
outw(INT_MASK, ioaddr + SCBCmd);
intr_status = inw(ioaddr + SCBStatus);
/* ack and clear intrs */
outw(intr_status, ioaddr + SCBStatus);
inw(ioaddr + SCBStatus);
}
}
/* exported function: eepro100_probe / eth_probe
* initializes a card
*
* side effects:
* leaves the ioaddress of the 82557 chip in the variable ioaddr.
* leaves the 82557 initialized, and ready to recieve packets.
*/
static int eepro100_probe(struct dev *dev, struct pci_device *p)
{
struct nic *nic = (struct nic *)dev;
unsigned short sum = 0;
int i;
int read_cmd, ee_size;
int options;
int rx_mode;
/* we cache only the first few words of the EEPROM data
be careful not to access beyond this array */
unsigned short eeprom[16];
if (p->ioaddr == 0)
return 0;
ioaddr = p->ioaddr & ~3; /* Mask the bit that says "this is an io addr" */
nic->ioaddr = ioaddr;
adjust_pci_device(p);
/* Copy IRQ from PCI information */
nic->irqno = p->irq;
if ((do_eeprom_cmd(EE_READ_CMD << 24, 27) & 0xffe0000)
== 0xffe0000) {
ee_size = 0x100;
read_cmd = EE_READ_CMD << 24;
} else {
ee_size = 0x40;
read_cmd = EE_READ_CMD << 22;
}
for (i = 0, sum = 0; i < ee_size; i++) {
unsigned short value = do_eeprom_cmd(read_cmd | (i << 16), 27);
if (i < (int)(sizeof(eeprom)/sizeof(eeprom[0])))
eeprom[i] = value;
sum += value;
}
for (i=0;i<ETH_ALEN;i++) {
nic->node_addr[i] = (eeprom[i/2] >> (8*(i&1))) & 0xff;
}
printf ("Ethernet addr: %!\n", nic->node_addr);
if (sum != 0xBABA)
printf("eepro100: Invalid EEPROM checksum %#hX, "
"check settings before activating this device!\n", sum);
outl(0, ioaddr + SCBPort);
udelay (10000);
whereami ("Got eeprom.");
/* Base = 0, disable all interrupts */
outl(0, ioaddr + SCBPointer);
outw(INT_MASK | RX_ADDR_LOAD, ioaddr + SCBCmd);
wait_for_cmd_done(ioaddr + SCBCmd);
whereami ("set rx base addr.");
outl(virt_to_bus(&lstats), ioaddr + SCBPointer);
outb(CU_STATSADDR, ioaddr + SCBCmd);
wait_for_cmd_done(ioaddr + SCBCmd);
whereami ("set stats addr.");
/* INIT RX stuff. */
for (i = 0; i < RXFD_COUNT; i++) {
rxfds[i].status = 0x0000;
rxfds[i].command = 0x0000;
rxfds[i].rx_buf_addr = 0xFFFFFFFF;
rxfds[i].count = 0;
rxfds[i].size = 1528;
rxfds[i].link = virt_to_bus(&rxfds[i+1]);
}
rxfds[RXFD_COUNT-1].status = 0x0000;
rxfds[RXFD_COUNT-1].command = 0xC000;
rxfds[RXFD_COUNT-1].link = virt_to_bus(&rxfds[0]);
outl(virt_to_bus(&rxfds[0]), ioaddr + SCBPointer);
outb(RX_START, ioaddr + SCBCmd);
wait_for_cmd_done(ioaddr + SCBCmd);
whereami ("started RX process.");
/* INIT TX stuff. */
/* Base = 0 */
outl(0, ioaddr + SCBPointer);
outb(CU_CMD_BASE, ioaddr + SCBCmd);
wait_for_cmd_done(ioaddr + SCBCmd);
whereami ("set TX base addr.");
txfd.command = (CmdIASetup);
txfd.status = 0x0000;
txfd.link = virt_to_bus (&confcmd);
{
char *t = (char *)&txfd.tx_desc_addr;
for (i=0;i<ETH_ALEN;i++)
t[i] = nic->node_addr[i];
}
#ifdef DEBUG
printf ("Setup_eaddr:\n");
hd (&txfd, 0x20);
#endif
/* options = 0x40; */ /* 10mbps half duplex... */
options = 0x00; /* Autosense */
#ifdef PROMISC
rx_mode = 3;
#elif ALLMULTI
rx_mode = 1;
#else
rx_mode = 0;
#endif
if ( ((eeprom[6]>>8) & 0x3f) == DP83840
|| ((eeprom[6]>>8) & 0x3f) == DP83840A) {
int mdi_reg23 = mdio_read(eeprom[6] & 0x1f, 23) | 0x0422;
if (congenb)
mdi_reg23 |= 0x0100;
printf(" DP83840 specific setup, setting register 23 to %hX.\n",
mdi_reg23);
mdio_write(eeprom[6] & 0x1f, 23, mdi_reg23);
}
whereami ("Done DP8340 special setup.");
if (options != 0) {
mdio_write(eeprom[6] & 0x1f, 0,
((options & 0x20) ? 0x2000 : 0) | /* 100mbps? */
((options & 0x10) ? 0x0100 : 0)); /* Full duplex? */
whereami ("set mdio_register.");
}
confcmd.command = CmdSuspend | CmdConfigure;
confcmd.status = 0x0000;
confcmd.link = virt_to_bus (&txfd);
confcmd.data[1] = (txfifo << 4) | rxfifo;
confcmd.data[4] = rxdmacount;
confcmd.data[5] = txdmacount + 0x80;
confcmd.data[15] = (rx_mode & 2) ? 0x49: 0x48;
confcmd.data[19] = (options & 0x10) ? 0xC0 : 0x80;
confcmd.data[21] = (rx_mode & 1) ? 0x0D: 0x05;
outl(virt_to_bus(&txfd), ioaddr + SCBPointer);
outb(CU_START, ioaddr + SCBCmd);
wait_for_cmd_done(ioaddr + SCBCmd);
whereami ("started TX thingy (config, iasetup).");
load_timer2(10*TICKS_PER_MS);
while (!txfd.status && timer2_running())
/* Wait */;
/* Read the status register once to disgard stale data */
mdio_read(eeprom[6] & 0x1f, 1);
/* Check to see if the network cable is plugged in.
* This allows for faster failure if there is nothing
* we can do.
*/
if (!(mdio_read(eeprom[6] & 0x1f, 1) & (1 << 2))) {
printf("Valid link not established\n");
eepro100_disable(dev);
return 0;
}
dev->disable = eepro100_disable;
nic->poll = eepro100_poll;
nic->transmit = eepro100_transmit;
nic->irq = eepro100_irq;
return 1;
}
/*********************************************************************/
#ifdef DEBUG
/* Hexdump a number of bytes from memory... */
void hd (void *where, int n)
{
int i;
while (n > 0) {
printf ("%X ", where);
for (i=0;i < ( (n>16)?16:n);i++)
printf (" %hhX", ((char *)where)[i]);
printf ("\n");
n -= 16;
where += 16;
}
}
#endif
static struct pci_id eepro100_nics[] = {
PCI_ROM(0x8086, 0x1029, "id1029", "Intel EtherExpressPro100 ID1029"),
PCI_ROM(0x8086, 0x1030, "id1030", "Intel EtherExpressPro100 ID1030"),
PCI_ROM(0x8086, 0x1031, "82801cam", "Intel 82801CAM (ICH3) Chipset Ethernet Controller"),
PCI_ROM(0x8086, 0x1032, "eepro100-1032", "Intel PRO/100 VE Network Connection"),
PCI_ROM(0x8086, 0x1033, "eepro100-1033", "Intel PRO/100 VM Network Connection"),
PCI_ROM(0x8086, 0x1034, "eepro100-1034", "Intel PRO/100 VM Network Connection"),
PCI_ROM(0x8086, 0x1035, "eepro100-1035", "Intel 82801CAM (ICH3) Chipset Ethernet Controller"),
PCI_ROM(0x8086, 0x1036, "eepro100-1036", "Intel 82801CAM (ICH3) Chipset Ethernet Controller"),
PCI_ROM(0x8086, 0x1037, "eepro100-1037", "Intel 82801CAM (ICH3) Chipset Ethernet Controller"),
PCI_ROM(0x8086, 0x1038, "id1038", "Intel PRO/100 VM Network Connection"),
PCI_ROM(0x8086, 0x1039, "82562et", "Intel PRO100 VE 82562ET"),
PCI_ROM(0x8086, 0x103a, "id103a", "Intel Corporation 82559 InBusiness 10/100"),
PCI_ROM(0x8086, 0x103b, "82562etb", "Intel PRO100 VE 82562ETB"),
PCI_ROM(0x8086, 0x103c, "eepro100-103c", "Intel PRO/100 VM Network Connection"),
PCI_ROM(0x8086, 0x103d, "eepro100-103d", "Intel PRO/100 VE Network Connection"),
PCI_ROM(0x8086, 0x103e, "eepro100-103e", "Intel PRO/100 VM Network Connection"),
PCI_ROM(0x8086, 0x1051, "prove", "Intel PRO/100 VE Network Connection"),
PCI_ROM(0x8086, 0x1059, "82551qm", "Intel PRO/100 M Mobile Connection"),
PCI_ROM(0x8086, 0x1209, "82559er", "Intel EtherExpressPro100 82559ER"),
PCI_ROM(0x8086, 0x1227, "82865", "Intel 82865 EtherExpress PRO/100A"),
PCI_ROM(0x8086, 0x1228, "82556", "Intel 82556 EtherExpress PRO/100 Smart"),
PCI_ROM(0x8086, 0x1229, "eepro100", "Intel EtherExpressPro100"),
PCI_ROM(0x8086, 0x2449, "82562em", "Intel EtherExpressPro100 82562EM"),
PCI_ROM(0x8086, 0x2459, "82562-1", "Intel 82562 based Fast Ethernet Connection"),
PCI_ROM(0x8086, 0x245d, "82562-2", "Intel 82562 based Fast Ethernet Connection"),
PCI_ROM(0x8086, 0x1050, "82562ez", "Intel 82562EZ Network Connection"),
PCI_ROM(0x8086, 0x1051, "eepro100-1051", "Intel 82801EB/ER (ICH5/ICH5R) Chipset Ethernet Controller"),
PCI_ROM(0x8086, 0x5200, "eepro100-5200", "Intel EtherExpress PRO/100 Intelligent Server"),
PCI_ROM(0x8086, 0x5201, "eepro100-5201", "Intel EtherExpress PRO/100 Intelligent Server"),
};
/* Cards with device ids 0x1030 to 0x103F, 0x2449, 0x2459 or 0x245D might need
* a workaround for hardware bug on 10 mbit half duplex (see linux driver eepro100.c)
* 2003/03/17 gbaum */
static struct pci_driver eepro100_driver __pci_driver = {
.type = NIC_DRIVER,
.name = "EEPRO100",
.probe = eepro100_probe,
.ids = eepro100_nics,
.id_count = sizeof(eepro100_nics)/sizeof(eepro100_nics[0]),
.class = 0
};

520
src/drivers/net/epic100.c Normal file
View File

@@ -0,0 +1,520 @@
/* epic100.c: A SMC 83c170 EPIC/100 fast ethernet driver for Etherboot */
/* 05/06/2003 timlegge Fixed relocation and implemented Multicast */
#define LINUX_OUT_MACROS
#include "etherboot.h"
#include "pci.h"
#include "nic.h"
#include "timer.h"
#include "epic100.h"
/* Condensed operations for readability */
#define virt_to_le32desc(addr) cpu_to_le32(virt_to_bus(addr))
#define le32desc_to_virt(addr) bus_to_virt(le32_to_cpu(addr))
#define TX_RING_SIZE 2 /* use at least 2 buffers for TX */
#define RX_RING_SIZE 2
#define PKT_BUF_SZ 1536 /* Size of each temporary Tx/Rx buffer.*/
/*
#define DEBUG_RX
#define DEBUG_TX
#define DEBUG_EEPROM
*/
#define EPIC_DEBUG 0 /* debug level */
/* The EPIC100 Rx and Tx buffer descriptors. */
struct epic_rx_desc {
unsigned long status;
unsigned long bufaddr;
unsigned long buflength;
unsigned long next;
};
/* description of the tx descriptors control bits commonly used */
#define TD_STDFLAGS TD_LASTDESC
struct epic_tx_desc {
unsigned long status;
unsigned long bufaddr;
unsigned long buflength;
unsigned long next;
};
#define delay(nanosec) do { int _i = 3; while (--_i > 0) \
{ __SLOW_DOWN_IO; }} while (0)
static void epic100_open(void);
static void epic100_init_ring(void);
static void epic100_disable(struct dev *dev);
static int epic100_poll(struct nic *nic, int retrieve);
static void epic100_transmit(struct nic *nic, const char *destaddr,
unsigned int type, unsigned int len, const char *data);
#ifdef DEBUG_EEPROM
static int read_eeprom(int location);
#endif
static int mii_read(int phy_id, int location);
static void epic100_irq(struct nic *nic, irq_action_t action);
static int ioaddr;
static int command;
static int intstat;
static int intmask;
static int genctl ;
static int eectl ;
static int test ;
static int mmctl ;
static int mmdata ;
static int lan0 ;
static int mc0 ;
static int rxcon ;
static int txcon ;
static int prcdar ;
static int ptcdar ;
static int eththr ;
static unsigned int cur_rx, cur_tx; /* The next free ring entry */
#ifdef DEBUG_EEPROM
static unsigned short eeprom[64];
#endif
static signed char phys[4]; /* MII device addresses. */
static struct epic_rx_desc rx_ring[RX_RING_SIZE]
__attribute__ ((aligned(4)));
static struct epic_tx_desc tx_ring[TX_RING_SIZE]
__attribute__ ((aligned(4)));
static unsigned char rx_packet[PKT_BUF_SZ * RX_RING_SIZE];
static unsigned char tx_packet[PKT_BUF_SZ * TX_RING_SIZE];
/***********************************************************************/
/* Externally visible functions */
/***********************************************************************/
static int
epic100_probe(struct dev *dev, struct pci_device *pci)
{
struct nic *nic = (struct nic *)dev;
int i;
unsigned short* ap;
unsigned int phy, phy_idx;
if (pci->ioaddr == 0)
return 0;
/* Ideally we would detect all network cards in slot order. That would
be best done a central PCI probe dispatch, which wouldn't work
well with the current structure. So instead we detect just the
Epic cards in slot order. */
ioaddr = pci->ioaddr;
nic->irqno = 0;
nic->ioaddr = pci->ioaddr & ~3;
/* compute all used static epic100 registers address */
command = ioaddr + COMMAND; /* Control Register */
intstat = ioaddr + INTSTAT; /* Interrupt Status */
intmask = ioaddr + INTMASK; /* Interrupt Mask */
genctl = ioaddr + GENCTL; /* General Control */
eectl = ioaddr + EECTL; /* EEPROM Control */
test = ioaddr + TEST; /* Test register (clocks) */
mmctl = ioaddr + MMCTL; /* MII Management Interface Control */
mmdata = ioaddr + MMDATA; /* MII Management Interface Data */
lan0 = ioaddr + LAN0; /* MAC address. (0x40-0x48) */
mc0 = ioaddr + MC0; /* Multicast Control */
rxcon = ioaddr + RXCON; /* Receive Control */
txcon = ioaddr + TXCON; /* Transmit Control */
prcdar = ioaddr + PRCDAR; /* PCI Receive Current Descr Address */
ptcdar = ioaddr + PTCDAR; /* PCI Transmit Current Descr Address */
eththr = ioaddr + ETHTHR; /* Early Transmit Threshold */
/* Reset the chip & bring it out of low-power mode. */
outl(GC_SOFT_RESET, genctl);
/* Disable ALL interrupts by setting the interrupt mask. */
outl(INTR_DISABLE, intmask);
/*
* set the internal clocks:
* Application Note 7.15 says:
* In order to set the CLOCK TEST bit in the TEST register,
* perform the following:
*
* Write 0x0008 to the test register at least sixteen
* consecutive times.
*
* The CLOCK TEST bit is Write-Only. Writing it several times
* consecutively insures a successful write to the bit...
*/
for (i = 0; i < 16; i++) {
outl(0x00000008, test);
}
#ifdef DEBUG_EEPROM
{
unsigned short sum = 0;
unsigned short value;
for (i = 0; i < 64; i++) {
value = read_eeprom(i);
eeprom[i] = value;
sum += value;
}
}
#if (EPIC_DEBUG > 1)
printf("EEPROM contents\n");
for (i = 0; i < 64; i++) {
printf(" %hhX%s", eeprom[i], i % 16 == 15 ? "\n" : "");
}
#endif
#endif
/* This could also be read from the EEPROM. */
ap = (unsigned short*)nic->node_addr;
for (i = 0; i < 3; i++)
*ap++ = inw(lan0 + i*4);
printf(" I/O %#hX %! ", ioaddr, nic->node_addr);
/* Find the connected MII xcvrs. */
for (phy = 0, phy_idx = 0; phy < 32 && phy_idx < sizeof(phys); phy++) {
int mii_status = mii_read(phy, 0);
if (mii_status != 0xffff && mii_status != 0x0000) {
phys[phy_idx++] = phy;
#if (EPIC_DEBUG > 1)
printf("MII transceiver found at address %d.\n", phy);
#endif
}
}
if (phy_idx == 0) {
#if (EPIC_DEBUG > 1)
printf("***WARNING***: No MII transceiver found!\n");
#endif
/* Use the known PHY address of the EPII. */
phys[0] = 3;
}
epic100_open();
dev->disable = epic100_disable;
nic->poll = epic100_poll;
nic->transmit = epic100_transmit;
nic->irq = epic100_irq;
return 1;
}
static void set_rx_mode(void)
{
unsigned char mc_filter[8];
int i;
memset(mc_filter, 0xff, sizeof(mc_filter));
outl(0x0C, rxcon);
for(i = 0; i < 4; i++)
outw(((unsigned short *)mc_filter)[i], mc0 + i*4);
return;
}
static void
epic100_open(void)
{
int mii_reg5;
int full_duplex = 0;
unsigned long tmp;
epic100_init_ring();
/* Pull the chip out of low-power mode, and set for PCI read multiple. */
outl(GC_RX_FIFO_THR_64 | GC_MRC_READ_MULT | GC_ONE_COPY, genctl);
outl(TX_FIFO_THRESH, eththr);
tmp = TC_EARLY_TX_ENABLE | TX_SLOT_TIME;
mii_reg5 = mii_read(phys[0], 5);
if (mii_reg5 != 0xffff && (mii_reg5 & 0x0100)) {
full_duplex = 1;
printf(" full-duplex mode");
tmp |= TC_LM_FULL_DPX;
} else
tmp |= TC_LM_NORMAL;
outl(tmp, txcon);
/* Give adress of RX and TX ring to the chip */
outl(virt_to_le32desc(&rx_ring), prcdar);
outl(virt_to_le32desc(&tx_ring), ptcdar);
/* Start the chip's Rx process: receive unicast and broadcast */
set_rx_mode();
outl(CR_START_RX | CR_QUEUE_RX, command);
putchar('\n');
}
/* Initialize the Rx and Tx rings. */
static void
epic100_init_ring(void)
{
int i;
cur_rx = cur_tx = 0;
for (i = 0; i < RX_RING_SIZE; i++) {
rx_ring[i].status = cpu_to_le32(RRING_OWN); /* Owned by Epic chip */
rx_ring[i].buflength = cpu_to_le32(PKT_BUF_SZ);
rx_ring[i].bufaddr = virt_to_bus(&rx_packet[i * PKT_BUF_SZ]);
rx_ring[i].next = virt_to_le32desc(&rx_ring[i + 1]) ;
}
/* Mark the last entry as wrapping the ring. */
rx_ring[i-1].next = virt_to_le32desc(&rx_ring[0]);
/*
*The Tx buffer descriptor is filled in as needed,
* but we do need to clear the ownership bit.
*/
for (i = 0; i < TX_RING_SIZE; i++) {
tx_ring[i].status = 0x0000; /* Owned by CPU */
tx_ring[i].buflength = 0x0000 | cpu_to_le32(TD_STDFLAGS << 16);
tx_ring[i].bufaddr = virt_to_bus(&tx_packet[i * PKT_BUF_SZ]);
tx_ring[i].next = virt_to_le32desc(&tx_ring[i + 1]);
}
tx_ring[i-1].next = virt_to_le32desc(&tx_ring[0]);
}
/* function: epic100_transmit
* This transmits a packet.
*
* Arguments: char d[6]: destination ethernet address.
* unsigned short t: ethernet protocol type.
* unsigned short s: size of the data-part of the packet.
* char *p: the data for the packet.
* returns: void.
*/
static void
epic100_transmit(struct nic *nic, const char *destaddr, unsigned int type,
unsigned int len, const char *data)
{
unsigned short nstype;
unsigned char *txp;
int entry;
/* Calculate the next Tx descriptor entry. */
entry = cur_tx % TX_RING_SIZE;
if ((tx_ring[entry].status & TRING_OWN) == TRING_OWN) {
printf("eth_transmit: Unable to transmit. status=%hX. Resetting...\n",
tx_ring[entry].status);
epic100_open();
return;
}
txp = tx_packet + (entry * PKT_BUF_SZ);
memcpy(txp, destaddr, ETH_ALEN);
memcpy(txp + ETH_ALEN, nic->node_addr, ETH_ALEN);
nstype = htons(type);
memcpy(txp + 12, (char*)&nstype, 2);
memcpy(txp + ETH_HLEN, data, len);
len += ETH_HLEN;
len &= 0x0FFF;
while(len < ETH_ZLEN)
txp[len++] = '\0';
/*
* Caution: the write order is important here,
* set the base address with the "ownership"
* bits last.
*/
tx_ring[entry].buflength |= cpu_to_le32(len);
tx_ring[entry].status = cpu_to_le32(len << 16) |
cpu_to_le32(TRING_OWN); /* Pass ownership to the chip. */
cur_tx++;
/* Trigger an immediate transmit demand. */
outl(CR_QUEUE_TX, command);
load_timer2(10*TICKS_PER_MS); /* timeout 10 ms for transmit */
while ((le32_to_cpu(tx_ring[entry].status) & (TRING_OWN)) && timer2_running())
/* Wait */;
if ((le32_to_cpu(tx_ring[entry].status) & TRING_OWN) != 0)
printf("Oops, transmitter timeout, status=%hX\n",
tx_ring[entry].status);
}
/* function: epic100_poll / eth_poll
* This receives a packet from the network.
*
* Arguments: none
*
* returns: 1 if a packet was received.
* 0 if no pacet was received.
* side effects:
* returns the packet in the array nic->packet.
* returns the length of the packet in nic->packetlen.
*/
static int
epic100_poll(struct nic *nic, int retrieve)
{
int entry;
int retcode;
int status;
entry = cur_rx % RX_RING_SIZE;
if ((rx_ring[entry].status & cpu_to_le32(RRING_OWN)) == RRING_OWN)
return (0);
if ( ! retrieve ) return 1;
status = le32_to_cpu(rx_ring[entry].status);
/* We own the next entry, it's a new packet. Send it up. */
#if (EPIC_DEBUG > 4)
printf("epic_poll: entry %d status %hX\n", entry, status);
#endif
cur_rx++;
if (status & 0x2000) {
printf("epic_poll: Giant packet\n");
retcode = 0;
} else if (status & 0x0006) {
/* Rx Frame errors are counted in hardware. */
printf("epic_poll: Frame received with errors\n");
retcode = 0;
} else {
/* Omit the four octet CRC from the length. */
nic->packetlen = le32_to_cpu((rx_ring[entry].buflength))- 4;
memcpy(nic->packet, &rx_packet[entry * PKT_BUF_SZ], nic->packetlen);
retcode = 1;
}
/* Clear all error sources. */
outl(status & INTR_CLEARERRS, intstat);
/* Give the descriptor back to the chip */
rx_ring[entry].status = RRING_OWN;
/* Restart Receiver */
outl(CR_START_RX | CR_QUEUE_RX, command);
return retcode;
}
static void
epic100_disable(struct dev *dev __unused)
{
/* Soft reset the chip. */
outl(GC_SOFT_RESET, genctl);
}
static void epic100_irq(struct nic *nic __unused, irq_action_t action __unused)
{
switch ( action ) {
case DISABLE :
break;
case ENABLE :
break;
case FORCE :
break;
}
}
#ifdef DEBUG_EEPROM
/* Serial EEPROM section. */
/* EEPROM_Ctrl bits. */
#define EE_SHIFT_CLK 0x04 /* EEPROM shift clock. */
#define EE_CS 0x02 /* EEPROM chip select. */
#define EE_DATA_WRITE 0x08 /* EEPROM chip data in. */
#define EE_WRITE_0 0x01
#define EE_WRITE_1 0x09
#define EE_DATA_READ 0x10 /* EEPROM chip data out. */
#define EE_ENB (0x0001 | EE_CS)
/* The EEPROM commands include the alway-set leading bit. */
#define EE_WRITE_CMD (5 << 6)
#define EE_READ_CMD (6 << 6)
#define EE_ERASE_CMD (7 << 6)
#define eeprom_delay(n) delay(n)
static int
read_eeprom(int location)
{
int i;
int retval = 0;
int read_cmd = location | EE_READ_CMD;
outl(EE_ENB & ~EE_CS, eectl);
outl(EE_ENB, eectl);
/* Shift the read command bits out. */
for (i = 10; i >= 0; i--) {
short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0;
outl(EE_ENB | dataval, eectl);
eeprom_delay(100);
outl(EE_ENB | dataval | EE_SHIFT_CLK, eectl);
eeprom_delay(150);
outl(EE_ENB | dataval, eectl); /* Finish EEPROM a clock tick. */
eeprom_delay(250);
}
outl(EE_ENB, eectl);
for (i = 16; i > 0; i--) {
outl(EE_ENB | EE_SHIFT_CLK, eectl);
eeprom_delay(100);
retval = (retval << 1) | ((inl(eectl) & EE_DATA_READ) ? 1 : 0);
outl(EE_ENB, eectl);
eeprom_delay(100);
}
/* Terminate the EEPROM access. */
outl(EE_ENB & ~EE_CS, eectl);
return retval;
}
#endif
#define MII_READOP 1
#define MII_WRITEOP 2
static int
mii_read(int phy_id, int location)
{
int i;
outl((phy_id << 9) | (location << 4) | MII_READOP, mmctl);
/* Typical operation takes < 50 ticks. */
for (i = 4000; i > 0; i--)
if ((inl(mmctl) & MII_READOP) == 0)
break;
return inw(mmdata);
}
static struct pci_id epic100_nics[] = {
PCI_ROM(0x10b8, 0x0005, "epic100", "SMC EtherPowerII"), /* SMC 83c170 EPIC/100 */
PCI_ROM(0x10b8, 0x0006, "smc-83c175", "SMC EPIC/C 83c175"),
};
static struct pci_driver epic100_driver __pci_driver = {
.type = NIC_DRIVER,
.name = "EPIC100",
.probe = epic100_probe,
.ids = epic100_nics,
.id_count = sizeof(epic100_nics)/sizeof(epic100_nics[0]),
.class = 0,
};

188
src/drivers/net/epic100.h Normal file
View File

@@ -0,0 +1,188 @@
#ifndef _EPIC100_H_
# define _EPIC100_H_
#ifndef PCI_VENDOR_SMC
# define PCI_VENDOR_SMC 0x10B8
#endif
#ifndef PCI_DEVICE_SMC_EPIC100
# define PCI_DEVICE_SMC_EPIC100 0x0005
#endif
#define PCI_DEVICE_ID_NONE 0xFFFF
/* Offsets to registers (using SMC names). */
enum epic100_registers {
COMMAND= 0, /* Control Register */
INTSTAT= 4, /* Interrupt Status */
INTMASK= 8, /* Interrupt Mask */
GENCTL = 0x0C, /* General Control */
NVCTL = 0x10, /* Non Volatile Control */
EECTL = 0x14, /* EEPROM Control */
TEST = 0x1C, /* Test register: marked as reserved (see in source code) */
CRCCNT = 0x20, /* CRC Error Counter */
ALICNT = 0x24, /* Frame Alignment Error Counter */
MPCNT = 0x28, /* Missed Packet Counter */
MMCTL = 0x30, /* MII Management Interface Control */
MMDATA = 0x34, /* MII Management Interface Data */
MIICFG = 0x38, /* MII Configuration */
IPG = 0x3C, /* InterPacket Gap */
LAN0 = 0x40, /* MAC address. (0x40-0x48) */
IDCHK = 0x4C, /* BoardID/ Checksum */
MC0 = 0x50, /* Multicast filter table. (0x50-0x5c) */
RXCON = 0x60, /* Receive Control */
TXCON = 0x70, /* Transmit Control */
TXSTAT = 0x74, /* Transmit Status */
PRCDAR = 0x84, /* PCI Receive Current Descriptor Address */
PRSTAT = 0xA4, /* PCI Receive DMA Status */
PRCPTHR= 0xB0, /* PCI Receive Copy Threshold */
PTCDAR = 0xC4, /* PCI Transmit Current Descriptor Address */
ETHTHR = 0xDC /* Early Transmit Threshold */
};
/* Command register (CR_) bits */
#define CR_STOP_RX (0x00000001)
#define CR_START_RX (0x00000002)
#define CR_QUEUE_TX (0x00000004)
#define CR_QUEUE_RX (0x00000008)
#define CR_NEXTFRAME (0x00000010)
#define CR_STOP_TX_DMA (0x00000020)
#define CR_STOP_RX_DMA (0x00000040)
#define CR_TX_UGO (0x00000080)
/* Interrupt register bits. NI means No Interrupt generated */
#define INTR_RX_THR_STA (0x00400000) /* rx copy threshold status NI */
#define INTR_RX_BUFF_EMPTY (0x00200000) /* rx buffers empty. NI */
#define INTR_TX_IN_PROG (0x00100000) /* tx copy in progess. NI */
#define INTR_RX_IN_PROG (0x00080000) /* rx copy in progress. NI */
#define INTR_TXIDLE (0x00040000) /* tx idle. NI */
#define INTR_RXIDLE (0x00020000) /* rx idle. NI */
#define INTR_INTR_ACTIVE (0x00010000) /* Interrupt active. NI */
#define INTR_RX_STATUS_OK (0x00008000) /* rx status valid. NI */
#define INTR_PCI_TGT_ABT (0x00004000) /* PCI Target abort */
#define INTR_PCI_MASTER_ABT (0x00002000) /* PCI Master abort */
#define INTR_PCI_PARITY_ERR (0x00001000) /* PCI adress parity error */
#define INTR_PCI_DATA_ERR (0x00000800) /* PCI data parity error */
#define INTR_RX_THR_CROSSED (0x00000400) /* rx copy threshold crossed */
#define INTR_CNTFULL (0x00000200) /* Counter overflow */
#define INTR_TXUNDERRUN (0x00000100) /* tx underrun. */
#define INTR_TXEMPTY (0x00000080) /* tx queue empty */
#define INTR_TX_CH_COMPLETE (0x00000040) /* tx chain complete */
#define INTR_TXDONE (0x00000020) /* tx complete (w or w/o err) */
#define INTR_RXERROR (0x00000010) /* rx error (CRC) */
#define INTR_RXOVERFLOW (0x00000008) /* rx buffer overflow */
#define INTR_RX_QUEUE_EMPTY (0x00000004) /* rx queue empty. */
#define INTR_RXHEADER (0x00000002) /* header copy complete */
#define INTR_RXDONE (0x00000001) /* Receive copy complete */
#define INTR_CLEARINTR (0x00007FFF)
#define INTR_VALIDBITS (0x007FFFFF)
#define INTR_DISABLE (0x00000000)
#define INTR_CLEARERRS (0x00007F18)
#define INTR_ABNINTR (INTR_CNTFULL | INTR_TXUNDERRUN | INTR_RXOVERFLOW)
/* General Control (GC_) bits */
#define GC_SOFT_RESET (0x00000001)
#define GC_INTR_ENABLE (0x00000002)
#define GC_SOFT_INTR (0x00000004)
#define GC_POWER_DOWN (0x00000008)
#define GC_ONE_COPY (0x00000010)
#define GC_BIG_ENDIAN (0x00000020)
#define GC_RX_PREEMPT_TX (0x00000040)
#define GC_TX_PREEMPT_RX (0x00000080)
/*
* Receive FIFO Threshold values
* Control the level at which the PCI burst state machine
* begins to empty the receive FIFO. Possible values: 0-3
*
* 0 => 32, 1 => 64, 2 => 96 3 => 128 bytes.
*/
#define GC_RX_FIFO_THR_32 (0x00000000)
#define GC_RX_FIFO_THR_64 (0x00000100)
#define GC_RX_FIFO_THR_96 (0x00000200)
#define GC_RX_FIFO_THR_128 (0x00000300)
/* Memory Read Control (MRC_) values */
#define GC_MRC_MEM_READ (0x00000000)
#define GC_MRC_READ_MULT (0x00000400)
#define GC_MRC_READ_LINE (0x00000800)
#define GC_SOFTBIT0 (0x00001000)
#define GC_SOFTBIT1 (0x00002000)
#define GC_RESET_PHY (0x00004000)
/* Definitions of the Receive Control (RC_) register bits */
#define RC_SAVE_ERRORED_PKT (0x00000001)
#define RC_SAVE_RUNT_FRAMES (0x00000002)
#define RC_RCV_BROADCAST (0x00000004)
#define RC_RCV_MULTICAST (0x00000008)
#define RC_RCV_INVERSE_PKT (0x00000010)
#define RC_PROMISCUOUS_MODE (0x00000020)
#define RC_MONITOR_MODE (0x00000040)
#define RC_EARLY_RCV_ENABLE (0x00000080)
/* description of the rx descriptors control bits */
#define RD_FRAGLIST (0x0001) /* Desc points to a fragment list */
#define RD_LLFORM (0x0002) /* Frag list format */
#define RD_HDR_CPY (0x0004) /* Desc used for header copy */
/* Definition of the Transmit CONTROL (TC) register bits */
#define TC_EARLY_TX_ENABLE (0x00000001)
/* Loopback Mode (LM_) Select valuesbits */
#define TC_LM_NORMAL (0x00000000)
#define TC_LM_INTERNAL (0x00000002)
#define TC_LM_EXTERNAL (0x00000004)
#define TC_LM_FULL_DPX (0x00000006)
#define TX_SLOT_TIME (0x00000078)
/* Bytes transferred to chip before transmission starts. */
#define TX_FIFO_THRESH 128 /* Rounded down to 4 byte units. */
/* description of rx descriptors status bits */
#define RRING_PKT_INTACT (0x0001)
#define RRING_ALIGN_ERR (0x0002)
#define RRING_CRC_ERR (0x0004)
#define RRING_MISSED_PKT (0x0008)
#define RRING_MULTICAST (0x0010)
#define RRING_BROADCAST (0x0020)
#define RRING_RECEIVER_DISABLE (0x0040)
#define RRING_STATUS_VALID (0x1000)
#define RRING_FRAGLIST_ERR (0x2000)
#define RRING_HDR_COPIED (0x4000)
#define RRING_OWN (0x8000)
/* error summary */
#define RRING_ERROR (RRING_ALIGN_ERR|RRING_CRC_ERR)
/* description of tx descriptors status bits */
#define TRING_PKT_INTACT (0x0001) /* pkt transmitted. */
#define TRING_PKT_NONDEFER (0x0002) /* pkt xmitted w/o deferring */
#define TRING_COLL (0x0004) /* pkt xmitted w collisions */
#define TRING_CARR (0x0008) /* carrier sense lost */
#define TRING_UNDERRUN (0x0010) /* DMA underrun */
#define TRING_HB_COLL (0x0020) /* Collision detect Heartbeat */
#define TRING_WIN_COLL (0x0040) /* out of window collision */
#define TRING_DEFERRED (0x0080) /* Deferring */
#define TRING_COLL_COUNT (0x0F00) /* collision counter (mask) */
#define TRING_COLL_EXCESS (0x1000) /* tx aborted: excessive colls */
#define TRING_OWN (0x8000) /* desc ownership bit */
/* error summary */
#define TRING_ABORT (TRING_COLL_EXCESS|TRING_WIN_COLL|TRING_UNDERRUN)
#define TRING_ERROR (TRING_DEFERRED|TRING_WIN_COLL|TRING_UNDERRUN|TRING_CARR/*|TRING_COLL*/ )
/* description of the tx descriptors control bits */
#define TD_FRAGLIST (0x0001) /* Desc points to a fragment list */
#define TD_LLFORM (0x0002) /* Frag list format */
#define TD_IAF (0x0004) /* Generate Interrupt after tx */
#define TD_NOCRC (0x0008) /* No CRC generated */
#define TD_LASTDESC (0x0010) /* Last desc for this frame */
#endif /* _EPIC100_H_ */

1039
src/drivers/net/forcedeth.c Normal file

File diff suppressed because it is too large Load Diff

2744
src/drivers/net/hfa384x.h Normal file

File diff suppressed because it is too large Load Diff

1096
src/drivers/net/mtd80x.c Normal file

File diff suppressed because it is too large Load Diff

780
src/drivers/net/natsemi.c Normal file
View File

@@ -0,0 +1,780 @@
/* -*- Mode:C; c-basic-offset:4; -*- */
/*
natsemi.c: An Etherboot driver for the NatSemi DP8381x series.
Copyright (C) 2001 Entity Cyber, Inc.
This development of this Etherboot driver was funded by
Sicom Systems: http://www.sicompos.com/
Author: Marty Connor (mdc@thinguin.org)
Adapted from a Linux driver which was written by Donald Becker
This software may be used and distributed according to the terms
of the GNU Public License (GPL), incorporated herein by reference.
Original Copyright Notice:
Written/copyright 1999-2001 by Donald Becker.
This software may be used and distributed according to the terms of
the GNU General Public License (GPL), incorporated herein by reference.
Drivers based on or derived from this code fall under the GPL and must
retain the authorship, copyright and license notice. This file is not
a complete program and may only be used when the entire operating
system is licensed under the GPL. License for under other terms may be
available. Contact the original author for details.
The original author may be reached as becker@scyld.com, or at
Scyld Computing Corporation
410 Severn Ave., Suite 210
Annapolis MD 21403
Support information and updates available at
http://www.scyld.com/network/netsemi.html
References:
http://www.scyld.com/expert/100mbps.html
http://www.scyld.com/expert/NWay.html
Datasheet is available from:
http://www.national.com/pf/DP/DP83815.html
*/
/* Revision History */
/*
13 Dec 2003 timlegge 1.1 Enabled Multicast Support
29 May 2001 mdc 1.0
Initial Release. Tested with Netgear FA311 and FA312 boards
*/
/* Includes */
#include "etherboot.h"
#include "nic.h"
#include "pci.h"
/* defines */
#define OWN 0x80000000
#define DSIZE 0x00000FFF
#define CRC_SIZE 4
/* Time in ticks before concluding the transmitter is hung. */
#define TX_TIMEOUT (4*TICKS_PER_SEC)
#define TX_BUF_SIZE 1536
#define RX_BUF_SIZE 1536
#define NUM_RX_DESC 4 /* Number of Rx descriptor registers. */
typedef uint8_t u8;
typedef int8_t s8;
typedef uint16_t u16;
typedef int16_t s16;
typedef uint32_t u32;
typedef int32_t s32;
/* helpful macroes if on a big_endian machine for changing byte order.
not strictly needed on Intel */
#define get_unaligned(ptr) (*(ptr))
#define put_unaligned(val, ptr) ((void)( *(ptr) = (val) ))
#define get_u16(ptr) (*(u16 *)(ptr))
#define virt_to_le32desc(addr) virt_to_bus(addr)
enum pcistuff {
PCI_USES_IO = 0x01,
PCI_USES_MEM = 0x02,
PCI_USES_MASTER = 0x04,
PCI_ADDR0 = 0x08,
PCI_ADDR1 = 0x10,
};
/* MMIO operations required */
#define PCI_IOTYPE (PCI_USES_MASTER | PCI_USES_MEM | PCI_ADDR1)
/* Offsets to the device registers.
Unlike software-only systems, device drivers interact with complex hardware.
It's not useful to define symbolic names for every register bit in the
device.
*/
enum register_offsets {
ChipCmd = 0x00,
ChipConfig = 0x04,
EECtrl = 0x08,
PCIBusCfg = 0x0C,
IntrStatus = 0x10,
IntrMask = 0x14,
IntrEnable = 0x18,
TxRingPtr = 0x20,
TxConfig = 0x24,
RxRingPtr = 0x30,
RxConfig = 0x34,
ClkRun = 0x3C,
WOLCmd = 0x40,
PauseCmd = 0x44,
RxFilterAddr = 0x48,
RxFilterData = 0x4C,
BootRomAddr = 0x50,
BootRomData = 0x54,
SiliconRev = 0x58,
StatsCtrl = 0x5C,
StatsData = 0x60,
RxPktErrs = 0x60,
RxMissed = 0x68,
RxCRCErrs = 0x64,
PCIPM = 0x44,
PhyStatus = 0xC0,
MIntrCtrl = 0xC4,
MIntrStatus = 0xC8,
/* These are from the spec, around page 78... on a separate table. */
PGSEL = 0xCC,
PMDCSR = 0xE4,
TSTDAT = 0xFC,
DSPCFG = 0xF4,
SDCFG = 0x8C
};
/* Bit in ChipCmd. */
enum ChipCmdBits {
ChipReset = 0x100,
RxReset = 0x20,
TxReset = 0x10,
RxOff = 0x08,
RxOn = 0x04,
TxOff = 0x02,
TxOn = 0x01
};
/* Bits in the RxMode register. */
enum rx_mode_bits {
AcceptErr = 0x20,
AcceptRunt = 0x10,
AcceptBroadcast = 0xC0000000,
AcceptMulticast = 0x00200000,
AcceptAllMulticast = 0x20000000,
AcceptAllPhys = 0x10000000,
AcceptMyPhys = 0x08000000,
RxFilterEnable = 0x80000000
};
typedef struct _BufferDesc {
u32 link;
volatile u32 cmdsts;
u32 bufptr;
u32 software_use;
} BufferDesc;
/* Bits in network_desc.status */
enum desc_status_bits {
DescOwn = 0x80000000,
DescMore = 0x40000000,
DescIntr = 0x20000000,
DescNoCRC = 0x10000000,
DescPktOK = 0x08000000,
RxTooLong = 0x00400000
};
/* Globals */
static int natsemi_debug = 1; /* 1 normal messages, 0 quiet .. 7 verbose. */
const char *nic_name;
static u32 SavedClkRun;
static unsigned short vendor, dev_id;
static unsigned long ioaddr;
static unsigned int cur_rx;
static unsigned int advertising;
static unsigned int rx_config;
static unsigned int tx_config;
/* Note: transmit and receive buffers and descriptors must be
longword aligned
*/
static BufferDesc txd __attribute__ ((aligned(4)));
static BufferDesc rxd[NUM_RX_DESC] __attribute__ ((aligned(4)));
static unsigned char txb[TX_BUF_SIZE] __attribute__ ((aligned(4)));
static unsigned char rxb[NUM_RX_DESC * RX_BUF_SIZE] __attribute__ ((aligned(4)));
/* Function Prototypes */
static int natsemi_probe(struct dev *dev, struct pci_device *pci);
static int eeprom_read(long addr, int location);
static int mdio_read(int phy_id, int location);
static void natsemi_init(struct nic *nic);
static void natsemi_reset(struct nic *nic);
static void natsemi_init_rxfilter(struct nic *nic);
static void natsemi_init_txd(struct nic *nic);
static void natsemi_init_rxd(struct nic *nic);
static void natsemi_set_rx_mode(struct nic *nic);
static void natsemi_check_duplex(struct nic *nic);
static void natsemi_transmit(struct nic *nic, const char *d, unsigned int t, unsigned int s, const char *p);
static int natsemi_poll(struct nic *nic, int retrieve);
static void natsemi_disable(struct dev *dev);
static void natsemi_irq(struct nic *nic, irq_action_t action);
/*
* Function: natsemi_probe
*
* Description: Retrieves the MAC address of the card, and sets up some
* globals required by other routines, and initializes the NIC, making it
* ready to send and receive packets.
*
* Side effects:
* leaves the ioaddress of the natsemi chip in the variable ioaddr.
* leaves the natsemi initialized, and ready to recieve packets.
*
* Returns: struct nic *: pointer to NIC data structure
*/
static int
natsemi_probe(struct dev *dev, struct pci_device *pci)
{
struct nic *nic = (struct nic *)dev;
int i;
int prev_eedata;
u32 tmp;
if (pci->ioaddr == 0)
return 0;
adjust_pci_device(pci);
/* initialize some commonly used globals */
nic->irqno = 0;
nic->ioaddr = pci->ioaddr & ~3;
ioaddr = pci->ioaddr & ~3;
vendor = pci->vendor;
dev_id = pci->dev_id;
nic_name = pci->name;
/* natsemi has a non-standard PM control register
* in PCI config space. Some boards apparently need
* to be brought to D0 in this manner.
*/
pcibios_read_config_dword(pci->bus, pci->devfn, PCIPM, &tmp);
if (tmp & (0x03|0x100)) {
/* D0 state, disable PME assertion */
u32 newtmp = tmp & ~(0x03|0x100);
pcibios_write_config_dword(pci->bus, pci->devfn, PCIPM, newtmp);
}
/* get MAC address */
prev_eedata = eeprom_read(ioaddr, 6);
for (i = 0; i < 3; i++) {
int eedata = eeprom_read(ioaddr, i + 7);
nic->node_addr[i*2] = (eedata << 1) + (prev_eedata >> 15);
nic->node_addr[i*2+1] = eedata >> 7;
prev_eedata = eedata;
}
printf("\nnatsemi_probe: MAC addr %! at ioaddr %#hX\n",
nic->node_addr, ioaddr);
printf("natsemi_probe: Vendor:%#hX Device:%#hX\n", vendor, dev_id);
/* Reset the chip to erase any previous misconfiguration. */
outl(ChipReset, ioaddr + ChipCmd);
advertising = mdio_read(1, 4);
{
u32 chip_config = inl(ioaddr + ChipConfig);
printf("%s: Transceiver default autoneg. %s "
"10%s %s duplex.\n",
nic_name,
chip_config & 0x2000 ? "enabled, advertise" : "disabled, force",
chip_config & 0x4000 ? "0" : "",
chip_config & 0x8000 ? "full" : "half");
}
printf("%s: Transceiver status %hX advertising %hX\n",
nic_name, (int)inl(ioaddr + 0x84), advertising);
/* Disable PME:
* The PME bit is initialized from the EEPROM contents.
* PCI cards probably have PME disabled, but motherboard
* implementations may have PME set to enable WakeOnLan.
* With PME set the chip will scan incoming packets but
* nothing will be written to memory. */
SavedClkRun = inl(ioaddr + ClkRun);
outl(SavedClkRun & ~0x100, ioaddr + ClkRun);
/* initialize device */
natsemi_init(nic);
dev->disable = natsemi_disable;
nic->poll = natsemi_poll;
nic->transmit = natsemi_transmit;
nic->irq = natsemi_irq;
return 1;
}
/* Read the EEPROM and MII Management Data I/O (MDIO) interfaces.
The EEPROM code is for the common 93c06/46 EEPROMs with 6 bit addresses.
*/
/* Delay between EEPROM clock transitions.
No extra delay is needed with 33Mhz PCI, but future 66Mhz access may need
a delay. */
#define eeprom_delay(ee_addr) inl(ee_addr)
enum EEPROM_Ctrl_Bits {
EE_ShiftClk = 0x04,
EE_DataIn = 0x01,
EE_ChipSelect = 0x08,
EE_DataOut = 0x02
};
#define EE_Write0 (EE_ChipSelect)
#define EE_Write1 (EE_ChipSelect | EE_DataIn)
/* The EEPROM commands include the alway-set leading bit. */
enum EEPROM_Cmds {
EE_WriteCmd=(5 << 6), EE_ReadCmd=(6 << 6), EE_EraseCmd=(7 << 6),
};
static int eeprom_read(long addr, int location)
{
int i;
int retval = 0;
int ee_addr = addr + EECtrl;
int read_cmd = location | EE_ReadCmd;
outl(EE_Write0, ee_addr);
/* Shift the read command bits out. */
for (i = 10; i >= 0; i--) {
short dataval = (read_cmd & (1 << i)) ? EE_Write1 : EE_Write0;
outl(dataval, ee_addr);
eeprom_delay(ee_addr);
outl(dataval | EE_ShiftClk, ee_addr);
eeprom_delay(ee_addr);
}
outl(EE_ChipSelect, ee_addr);
eeprom_delay(ee_addr);
for (i = 0; i < 16; i++) {
outl(EE_ChipSelect | EE_ShiftClk, ee_addr);
eeprom_delay(ee_addr);
retval |= (inl(ee_addr) & EE_DataOut) ? 1 << i : 0;
outl(EE_ChipSelect, ee_addr);
eeprom_delay(ee_addr);
}
/* Terminate the EEPROM access. */
outl(EE_Write0, ee_addr);
outl(0, ee_addr);
return retval;
}
/* MII transceiver control section.
The 83815 series has an internal transceiver, and we present the
management registers as if they were MII connected. */
static int mdio_read(int phy_id, int location)
{
if (phy_id == 1 && location < 32)
return inl(ioaddr + 0x80 + (location<<2)) & 0xffff;
else
return 0xffff;
}
/* Function: natsemi_init
*
* Description: resets the ethernet controller chip and configures
* registers and data structures required for sending and receiving packets.
*
* Arguments: struct nic *nic: NIC data structure
*
* returns: void.
*/
static void
natsemi_init(struct nic *nic)
{
natsemi_reset(nic);
/* Disable PME:
* The PME bit is initialized from the EEPROM contents.
* PCI cards probably have PME disabled, but motherboard
* implementations may have PME set to enable WakeOnLan.
* With PME set the chip will scan incoming packets but
* nothing will be written to memory. */
outl(SavedClkRun & ~0x100, ioaddr + ClkRun);
natsemi_init_rxfilter(nic);
natsemi_init_txd(nic);
natsemi_init_rxd(nic);
/* Initialize other registers. */
/* Configure the PCI bus bursts and FIFO thresholds. */
/* Configure for standard, in-spec Ethernet. */
if (inl(ioaddr + ChipConfig) & 0x20000000) { /* Full duplex */
tx_config = 0xD0801002;
rx_config = 0x10000020;
} else {
tx_config = 0x10801002;
rx_config = 0x0020;
}
outl(tx_config, ioaddr + TxConfig);
outl(rx_config, ioaddr + RxConfig);
natsemi_check_duplex(nic);
natsemi_set_rx_mode(nic);
outl(RxOn, ioaddr + ChipCmd);
}
/*
* Function: natsemi_reset
*
* Description: soft resets the controller chip
*
* Arguments: struct nic *nic: NIC data structure
*
* Returns: void.
*/
static void
natsemi_reset(struct nic *nic __unused)
{
outl(ChipReset, ioaddr + ChipCmd);
/* On page 78 of the spec, they recommend some settings for "optimum
performance" to be done in sequence. These settings optimize some
of the 100Mbit autodetection circuitry. Also, we only want to do
this for rev C of the chip.
*/
if (inl(ioaddr + SiliconRev) == 0x302) {
outw(0x0001, ioaddr + PGSEL);
outw(0x189C, ioaddr + PMDCSR);
outw(0x0000, ioaddr + TSTDAT);
outw(0x5040, ioaddr + DSPCFG);
outw(0x008C, ioaddr + SDCFG);
}
/* Disable interrupts using the mask. */
outl(0, ioaddr + IntrMask);
outl(0, ioaddr + IntrEnable);
}
/* Function: natsemi_init_rxfilter
*
* Description: sets receive filter address to our MAC address
*
* Arguments: struct nic *nic: NIC data structure
*
* returns: void.
*/
static void
natsemi_init_rxfilter(struct nic *nic)
{
int i;
for (i = 0; i < ETH_ALEN; i += 2) {
outl(i, ioaddr + RxFilterAddr);
outw(nic->node_addr[i] + (nic->node_addr[i+1] << 8), ioaddr + RxFilterData);
}
}
/*
* Function: natsemi_init_txd
*
* Description: initializes the Tx descriptor
*
* Arguments: struct nic *nic: NIC data structure
*
* returns: void.
*/
static void
natsemi_init_txd(struct nic *nic __unused)
{
txd.link = (u32) 0;
txd.cmdsts = (u32) 0;
txd.bufptr = virt_to_bus(&txb[0]);
/* load Transmit Descriptor Register */
outl(virt_to_bus(&txd), ioaddr + TxRingPtr);
if (natsemi_debug > 1)
printf("natsemi_init_txd: TX descriptor register loaded with: %X\n",
inl(ioaddr + TxRingPtr));
}
/* Function: natsemi_init_rxd
*
* Description: initializes the Rx descriptor ring
*
* Arguments: struct nic *nic: NIC data structure
*
* Returns: void.
*/
static void
natsemi_init_rxd(struct nic *nic __unused)
{
int i;
cur_rx = 0;
/* init RX descriptor */
for (i = 0; i < NUM_RX_DESC; i++) {
rxd[i].link = virt_to_bus((i+1 < NUM_RX_DESC) ? &rxd[i+1] : &rxd[0]);
rxd[i].cmdsts = (u32) RX_BUF_SIZE;
rxd[i].bufptr = virt_to_bus(&rxb[i*RX_BUF_SIZE]);
if (natsemi_debug > 1)
printf("natsemi_init_rxd: rxd[%d]=%X link=%X cmdsts=%X bufptr=%X\n",
i, &rxd[i], rxd[i].link, rxd[i].cmdsts, rxd[i].bufptr);
}
/* load Receive Descriptor Register */
outl(virt_to_bus(&rxd[0]), ioaddr + RxRingPtr);
if (natsemi_debug > 1)
printf("natsemi_init_rxd: RX descriptor register loaded with: %X\n",
inl(ioaddr + RxRingPtr));
}
/* Function: natsemi_set_rx_mode
*
* Description:
* sets the receive mode to accept all broadcast packets and packets
* with our MAC address, and reject all multicast packets.
*
* Arguments: struct nic *nic: NIC data structure
*
* Returns: void.
*/
static void natsemi_set_rx_mode(struct nic *nic __unused)
{
u32 rx_mode = RxFilterEnable | AcceptBroadcast |
AcceptAllMulticast | AcceptMyPhys;
outl(rx_mode, ioaddr + RxFilterAddr);
}
static void natsemi_check_duplex(struct nic *nic __unused)
{
int duplex = inl(ioaddr + ChipConfig) & 0x20000000 ? 1 : 0;
if (natsemi_debug)
printf("%s: Setting %s-duplex based on negotiated link"
" capability.\n", nic_name,
duplex ? "full" : "half");
if (duplex) {
rx_config |= 0x10000000;
tx_config |= 0xC0000000;
} else {
rx_config &= ~0x10000000;
tx_config &= ~0xC0000000;
}
outl(tx_config, ioaddr + TxConfig);
outl(rx_config, ioaddr + RxConfig);
}
/* Function: natsemi_transmit
*
* Description: transmits a packet and waits for completion or timeout.
*
* Arguments: char d[6]: destination ethernet address.
* unsigned short t: ethernet protocol type.
* unsigned short s: size of the data-part of the packet.
* char *p: the data for the packet.
*
* Returns: void.
*/
static void
natsemi_transmit(struct nic *nic,
const char *d, /* Destination */
unsigned int t, /* Type */
unsigned int s, /* size */
const char *p) /* Packet */
{
u32 to, nstype;
u32 tx_status;
/* Stop the transmitter */
outl(TxOff, ioaddr + ChipCmd);
/* load Transmit Descriptor Register */
outl(virt_to_bus(&txd), ioaddr + TxRingPtr);
if (natsemi_debug > 1)
printf("natsemi_transmit: TX descriptor register loaded with: %X\n",
inl(ioaddr + TxRingPtr));
memcpy(txb, d, ETH_ALEN);
memcpy(txb + ETH_ALEN, nic->node_addr, ETH_ALEN);
nstype = htons(t);
memcpy(txb + 2 * ETH_ALEN, (char*)&nstype, 2);
memcpy(txb + ETH_HLEN, p, s);
s += ETH_HLEN;
s &= DSIZE;
if (natsemi_debug > 1)
printf("natsemi_transmit: sending %d bytes ethtype %hX\n", (int) s, t);
/* pad to minimum packet size */
while (s < ETH_ZLEN)
txb[s++] = '\0';
/* set the transmit buffer descriptor and enable Transmit State Machine */
txd.bufptr = virt_to_bus(&txb[0]);
txd.cmdsts = (u32) OWN | s;
/* restart the transmitter */
outl(TxOn, ioaddr + ChipCmd);
if (natsemi_debug > 1)
printf("natsemi_transmit: Queued Tx packet size %d.\n", (int) s);
to = currticks() + TX_TIMEOUT;
while ((((volatile u32) tx_status=txd.cmdsts) & OWN) && (currticks() < to))
/* wait */ ;
if (currticks() >= to) {
printf("natsemi_transmit: TX Timeout! Tx status %X.\n", tx_status);
}
if (!(tx_status & 0x08000000)) {
printf("natsemi_transmit: Transmit error, Tx status %X.\n", tx_status);
}
}
/* Function: natsemi_poll
*
* Description: checks for a received packet and returns it if found.
*
* Arguments: struct nic *nic: NIC data structure
*
* Returns: 1 if packet was received.
* 0 if no packet was received.
*
* Side effects:
* Returns (copies) the packet to the array nic->packet.
* Returns the length of the packet in nic->packetlen.
*/
static int
natsemi_poll(struct nic *nic, int retrieve)
{
u32 rx_status = rxd[cur_rx].cmdsts;
int retstat = 0;
if (natsemi_debug > 2)
printf("natsemi_poll: cur_rx:%d, status:%X\n", cur_rx, rx_status);
if (!(rx_status & OWN))
return retstat;
if ( ! retrieve ) return 1;
if (natsemi_debug > 1)
printf("natsemi_poll: got a packet: cur_rx:%d, status:%X\n",
cur_rx, rx_status);
nic->packetlen = (rx_status & DSIZE) - CRC_SIZE;
if ((rx_status & (DescMore|DescPktOK|RxTooLong)) != DescPktOK) {
/* corrupted packet received */
printf("natsemi_poll: Corrupted packet received, buffer status = %X\n",
rx_status);
retstat = 0;
} else {
/* give packet to higher level routine */
memcpy(nic->packet, (rxb + cur_rx*RX_BUF_SIZE), nic->packetlen);
retstat = 1;
}
/* return the descriptor and buffer to receive ring */
rxd[cur_rx].cmdsts = RX_BUF_SIZE;
rxd[cur_rx].bufptr = virt_to_bus(&rxb[cur_rx*RX_BUF_SIZE]);
if (++cur_rx == NUM_RX_DESC)
cur_rx = 0;
/* re-enable the potentially idle receive state machine */
outl(RxOn, ioaddr + ChipCmd);
return retstat;
}
/* Function: natsemi_disable
*
* Description: Turns off interrupts and stops Tx and Rx engines
*
* Arguments: struct nic *nic: NIC data structure
*
* Returns: void.
*/
static void
natsemi_disable(struct dev *dev)
{
struct nic *nic = (struct nic *)dev;
/* merge reset and disable */
natsemi_init(nic);
/* Disable interrupts using the mask. */
outl(0, ioaddr + IntrMask);
outl(0, ioaddr + IntrEnable);
/* Stop the chip's Tx and Rx processes. */
outl(RxOff | TxOff, ioaddr + ChipCmd);
/* Restore PME enable bit */
outl(SavedClkRun, ioaddr + ClkRun);
}
/* Function: natsemi_irq
*
* Description: Enable, Disable, or Force interrupts
*
* Arguments: struct nic *nic: NIC data structure
* irq_action_t action: requested action to perform
*
* Returns: void.
*/
static void
natsemi_irq(struct nic *nic __unused, irq_action_t action __unused)
{
switch ( action ) {
case DISABLE :
break;
case ENABLE :
break;
case FORCE :
break;
}
}
static struct pci_id natsemi_nics[] = {
PCI_ROM(0x100b, 0x0020, "dp83815", "DP83815"),
};
static struct pci_driver natsemi_driver __pci_driver = {
.type = NIC_DRIVER,
.name = "NATSEMI",
.probe = natsemi_probe,
.ids = natsemi_nics,
.id_count = sizeof(natsemi_nics)/sizeof(natsemi_nics[0]),
.class = 0,
};

1020
src/drivers/net/ns83820.c Executable file

File diff suppressed because it is too large Load Diff

1016
src/drivers/net/ns8390.c Normal file

File diff suppressed because it is too large Load Diff

238
src/drivers/net/ns8390.h Normal file
View File

@@ -0,0 +1,238 @@
/**************************************************************************
ETHERBOOT - BOOTP/TFTP Bootstrap Program
Author: Martin Renters
Date: Jun/94
**************************************************************************/
#define VENDOR_NONE 0
#define VENDOR_WD 1
#define VENDOR_NOVELL 2
#define VENDOR_3COM 3
#define FLAG_PIO 0x01
#define FLAG_16BIT 0x02
#define FLAG_790 0x04
#define MEM_8192 32
#define MEM_16384 64
#define MEM_32768 128
#define ISA_MAX_ADDR 0x400
/**************************************************************************
Western Digital/SMC Board Definitions
**************************************************************************/
#define WD_LOW_BASE 0x200
#define WD_HIGH_BASE 0x3e0
#ifndef WD_DEFAULT_MEM
#define WD_DEFAULT_MEM 0xD0000
#endif
#define WD_NIC_ADDR 0x10
/**************************************************************************
Western Digital/SMC ASIC Addresses
**************************************************************************/
#define WD_MSR 0x00
#define WD_ICR 0x01
#define WD_IAR 0x02
#define WD_BIO 0x03
#define WD_IRR 0x04
#define WD_LAAR 0x05
#define WD_IJR 0x06
#define WD_GP2 0x07
#define WD_LAR 0x08
#define WD_BID 0x0E
#define WD_ICR_16BIT 0x01
#define WD_MSR_MENB 0x40
#define WD_LAAR_L16EN 0x40
#define WD_LAAR_M16EN 0x80
#define WD_SOFTCONFIG 0x20
/**************************************************************************
Western Digital/SMC Board Types
**************************************************************************/
#define TYPE_WD8003S 0x02
#define TYPE_WD8003E 0x03
#define TYPE_WD8013EBT 0x05
#define TYPE_WD8003W 0x24
#define TYPE_WD8003EB 0x25
#define TYPE_WD8013W 0x26
#define TYPE_WD8013EP 0x27
#define TYPE_WD8013WC 0x28
#define TYPE_WD8013EPC 0x29
#define TYPE_SMC8216T 0x2a
#define TYPE_SMC8216C 0x2b
#define TYPE_SMC8416T 0x00 /* Bogus entries: the 8416 generates the */
#define TYPE_SMC8416C 0x00 /* the same codes as the 8216. */
#define TYPE_SMC8013EBP 0x2c
/**************************************************************************
3com 3c503 definitions
**************************************************************************/
#ifndef _3COM_BASE
#define _3COM_BASE 0x300
#endif
#define _3COM_TX_PAGE_OFFSET_8BIT 0x20
#define _3COM_TX_PAGE_OFFSET_16BIT 0x0
#define _3COM_RX_PAGE_OFFSET_16BIT 0x20
#define _3COM_ASIC_OFFSET 0x400
#define _3COM_NIC_OFFSET 0x0
#define _3COM_PSTR 0
#define _3COM_PSPR 1
#define _3COM_BCFR 3
#define _3COM_BCFR_2E0 0x01
#define _3COM_BCFR_2A0 0x02
#define _3COM_BCFR_280 0x04
#define _3COM_BCFR_250 0x08
#define _3COM_BCFR_350 0x10
#define _3COM_BCFR_330 0x20
#define _3COM_BCFR_310 0x40
#define _3COM_BCFR_300 0x80
#define _3COM_PCFR 4
#define _3COM_PCFR_PIO 0
#define _3COM_PCFR_C8000 0x10
#define _3COM_PCFR_CC000 0x20
#define _3COM_PCFR_D8000 0x40
#define _3COM_PCFR_DC000 0x80
#define _3COM_CR 6
#define _3COM_CR_RST 0x01 /* Reset GA and NIC */
#define _3COM_CR_XSEL 0x02 /* Transceiver select. BNC=1(def) AUI=0 */
#define _3COM_CR_EALO 0x04 /* window EA PROM 0-15 to I/O base */
#define _3COM_CR_EAHI 0x08 /* window EA PROM 16-31 to I/O base */
#define _3COM_CR_SHARE 0x10 /* select interrupt sharing option */
#define _3COM_CR_DBSEL 0x20 /* Double buffer select */
#define _3COM_CR_DDIR 0x40 /* DMA direction select */
#define _3COM_CR_START 0x80 /* Start DMA controller */
#define _3COM_GACFR 5
#define _3COM_GACFR_MBS0 0x01
#define _3COM_GACFR_MBS1 0x02
#define _3COM_GACFR_MBS2 0x04
#define _3COM_GACFR_RSEL 0x08 /* enable shared memory */
#define _3COM_GACFR_TEST 0x10 /* for GA testing */
#define _3COM_GACFR_OWS 0x20 /* select 0WS access to GA */
#define _3COM_GACFR_TCM 0x40 /* Mask DMA interrupts */
#define _3COM_GACFR_NIM 0x80 /* Mask NIC interrupts */
#define _3COM_STREG 7
#define _3COM_STREG_REV 0x07 /* GA revision */
#define _3COM_STREG_DIP 0x08 /* DMA in progress */
#define _3COM_STREG_DTC 0x10 /* DMA terminal count */
#define _3COM_STREG_OFLW 0x20 /* Overflow */
#define _3COM_STREG_UFLW 0x40 /* Underflow */
#define _3COM_STREG_DPRDY 0x80 /* Data port ready */
#define _3COM_IDCFR 8
#define _3COM_IDCFR_DRQ0 0x01 /* DMA request 1 select */
#define _3COM_IDCFR_DRQ1 0x02 /* DMA request 2 select */
#define _3COM_IDCFR_DRQ2 0x04 /* DMA request 3 select */
#define _3COM_IDCFR_UNUSED 0x08 /* not used */
#define _3COM_IDCFR_IRQ2 0x10 /* Interrupt request 2 select */
#define _3COM_IDCFR_IRQ3 0x20 /* Interrupt request 3 select */
#define _3COM_IDCFR_IRQ4 0x40 /* Interrupt request 4 select */
#define _3COM_IDCFR_IRQ5 0x80 /* Interrupt request 5 select */
#define _3COM_IRQ2 2
#define _3COM_IRQ3 3
#define _3COM_IRQ4 4
#define _3COM_IRQ5 5
#define _3COM_DAMSB 9
#define _3COM_DALSB 0x0a
#define _3COM_VPTR2 0x0b
#define _3COM_VPTR1 0x0c
#define _3COM_VPTR0 0x0d
#define _3COM_RFMSB 0x0e
#define _3COM_RFLSB 0x0f
/**************************************************************************
NE1000/2000 definitions
**************************************************************************/
#define NE_ASIC_OFFSET 0x10
#define NE_RESET 0x0F /* Used to reset card */
#define NE_DATA 0x00 /* Used to read/write NIC mem */
#define COMPEX_RL2000_TRIES 200
/**************************************************************************
8390 Register Definitions
**************************************************************************/
#define D8390_P0_COMMAND 0x00
#define D8390_P0_PSTART 0x01
#define D8390_P0_PSTOP 0x02
#define D8390_P0_BOUND 0x03
#define D8390_P0_TSR 0x04
#define D8390_P0_TPSR 0x04
#define D8390_P0_TBCR0 0x05
#define D8390_P0_TBCR1 0x06
#define D8390_P0_ISR 0x07
#define D8390_P0_RSAR0 0x08
#define D8390_P0_RSAR1 0x09
#define D8390_P0_RBCR0 0x0A
#define D8390_P0_RBCR1 0x0B
#define D8390_P0_RSR 0x0C
#define D8390_P0_RCR 0x0C
#define D8390_P0_TCR 0x0D
#define D8390_P0_DCR 0x0E
#define D8390_P0_IMR 0x0F
#define D8390_P1_COMMAND 0x00
#define D8390_P1_PAR0 0x01
#define D8390_P1_PAR1 0x02
#define D8390_P1_PAR2 0x03
#define D8390_P1_PAR3 0x04
#define D8390_P1_PAR4 0x05
#define D8390_P1_PAR5 0x06
#define D8390_P1_CURR 0x07
#define D8390_P1_MAR0 0x08
#define D8390_COMMAND_PS0 0x0 /* Page 0 select */
#define D8390_COMMAND_PS1 0x40 /* Page 1 select */
#define D8390_COMMAND_PS2 0x80 /* Page 2 select */
#define D8390_COMMAND_RD2 0x20 /* Remote DMA control */
#define D8390_COMMAND_RD1 0x10
#define D8390_COMMAND_RD0 0x08
#define D8390_COMMAND_TXP 0x04 /* transmit packet */
#define D8390_COMMAND_STA 0x02 /* start */
#define D8390_COMMAND_STP 0x01 /* stop */
#define D8390_RCR_MON 0x20 /* monitor mode */
#define D8390_DCR_FT1 0x40
#define D8390_DCR_LS 0x08 /* Loopback select */
#define D8390_DCR_WTS 0x01 /* Word transfer select */
#define D8390_ISR_PRX 0x01 /* successful recv */
#define D8390_ISR_PTX 0x02 /* successful xmit */
#define D8390_ISR_RXE 0x04 /* receive error */
#define D8390_ISR_TXE 0x08 /* transmit error */
#define D8390_ISR_OVW 0x10 /* Overflow */
#define D8390_ISR_CNT 0x20 /* Counter overflow */
#define D8390_ISR_RDC 0x40 /* Remote DMA complete */
#define D8390_ISR_RST 0x80 /* reset */
#define D8390_RSTAT_PRX 0x01 /* successful recv */
#define D8390_RSTAT_CRC 0x02 /* CRC error */
#define D8390_RSTAT_FAE 0x04 /* Frame alignment error */
#define D8390_RSTAT_OVER 0x08 /* FIFO overrun */
#define D8390_TXBUF_SIZE 6
#define D8390_RXBUF_END 32
#define D8390_PAGE_SIZE 256
struct ringbuffer {
unsigned char status;
unsigned char next;
unsigned short len;
};
/*
* Local variables:
* c-basic-offset: 8
* End:
*/

262
src/drivers/net/p80211hdr.h Normal file
View File

@@ -0,0 +1,262 @@
/* src/include/wlan/p80211hdr.h
*
* Macros, types, and functions for handling 802.11 MAC headers
*
* Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved.
* --------------------------------------------------------------------
*
* linux-wlan
*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU Public License version 2 (the "GPL"), in which
* case the provisions of the GPL are applicable instead of the
* above. If you wish to allow the use of your version of this file
* only under the terms of the GPL and not to allow others to use
* your version of this file under the MPL, indicate your decision
* by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL. If you do not delete
* the provisions above, a recipient may use your version of this
* file under either the MPL or the GPL.
*
* --------------------------------------------------------------------
*
* Inquiries regarding the linux-wlan Open Source project can be
* made directly to:
*
* AbsoluteValue Systems Inc.
* info@linux-wlan.com
* http://www.linux-wlan.com
*
* --------------------------------------------------------------------
*
* Portions of the development of this software were funded by
* Intersil Corporation as part of PRISM(R) chipset product development.
*
* --------------------------------------------------------------------
*
* This file declares the constants and types used in the interface
* between a wlan driver and the user mode utilities.
*
* Note:
* - Constant values are always in HOST byte order. To assign
* values to multi-byte fields they _must_ be converted to
* ieee byte order. To retrieve multi-byte values from incoming
* frames, they must be converted to host order.
*
* All functions declared here are implemented in p80211.c
* --------------------------------------------------------------------
*/
#ifndef _P80211HDR_H
#define _P80211HDR_H
/*================================================================*/
/* System Includes */
/*================================================================*/
/* Project Includes */
#ifndef _WLAN_COMPAT_H
#include <wlan/wlan_compat.h>
#endif
/*================================================================*/
/* Constants */
/*--- Sizes -----------------------------------------------*/
#define WLAN_ADDR_LEN 6
#define WLAN_CRC_LEN 4
#define WLAN_BSSID_LEN 6
#define WLAN_BSS_TS_LEN 8
#define WLAN_HDR_A3_LEN 24
#define WLAN_HDR_A4_LEN 30
#define WLAN_SSID_MAXLEN 32
#define WLAN_DATA_MAXLEN 2312
#define WLAN_A3FR_MAXLEN (WLAN_HDR_A3_LEN + WLAN_DATA_MAXLEN + WLAN_CRC_LEN)
#define WLAN_A4FR_MAXLEN (WLAN_HDR_A4_LEN + WLAN_DATA_MAXLEN + WLAN_CRC_LEN)
#define WLAN_BEACON_FR_MAXLEN (WLAN_HDR_A3_LEN + 334)
#define WLAN_ATIM_FR_MAXLEN (WLAN_HDR_A3_LEN + 0)
#define WLAN_DISASSOC_FR_MAXLEN (WLAN_HDR_A3_LEN + 2)
#define WLAN_ASSOCREQ_FR_MAXLEN (WLAN_HDR_A3_LEN + 48)
#define WLAN_ASSOCRESP_FR_MAXLEN (WLAN_HDR_A3_LEN + 16)
#define WLAN_REASSOCREQ_FR_MAXLEN (WLAN_HDR_A3_LEN + 54)
#define WLAN_REASSOCRESP_FR_MAXLEN (WLAN_HDR_A3_LEN + 16)
#define WLAN_PROBEREQ_FR_MAXLEN (WLAN_HDR_A3_LEN + 44)
#define WLAN_PROBERESP_FR_MAXLEN (WLAN_HDR_A3_LEN + 78)
#define WLAN_AUTHEN_FR_MAXLEN (WLAN_HDR_A3_LEN + 261)
#define WLAN_DEAUTHEN_FR_MAXLEN (WLAN_HDR_A3_LEN + 2)
#define WLAN_WEP_NKEYS 4
#define WLAN_WEP_MAXKEYLEN 13
#define WLAN_CHALLENGE_IE_LEN 130
#define WLAN_CHALLENGE_LEN 128
#define WLAN_WEP_IV_LEN 4
#define WLAN_WEP_ICV_LEN 4
/*--- Frame Control Field -------------------------------------*/
/* Frame Types */
#define WLAN_FTYPE_MGMT 0x00
#define WLAN_FTYPE_CTL 0x01
#define WLAN_FTYPE_DATA 0x02
/* Frame subtypes */
/* Management */
#define WLAN_FSTYPE_ASSOCREQ 0x00
#define WLAN_FSTYPE_ASSOCRESP 0x01
#define WLAN_FSTYPE_REASSOCREQ 0x02
#define WLAN_FSTYPE_REASSOCRESP 0x03
#define WLAN_FSTYPE_PROBEREQ 0x04
#define WLAN_FSTYPE_PROBERESP 0x05
#define WLAN_FSTYPE_BEACON 0x08
#define WLAN_FSTYPE_ATIM 0x09
#define WLAN_FSTYPE_DISASSOC 0x0a
#define WLAN_FSTYPE_AUTHEN 0x0b
#define WLAN_FSTYPE_DEAUTHEN 0x0c
/* Control */
#define WLAN_FSTYPE_PSPOLL 0x0a
#define WLAN_FSTYPE_RTS 0x0b
#define WLAN_FSTYPE_CTS 0x0c
#define WLAN_FSTYPE_ACK 0x0d
#define WLAN_FSTYPE_CFEND 0x0e
#define WLAN_FSTYPE_CFENDCFACK 0x0f
/* Data */
#define WLAN_FSTYPE_DATAONLY 0x00
#define WLAN_FSTYPE_DATA_CFACK 0x01
#define WLAN_FSTYPE_DATA_CFPOLL 0x02
#define WLAN_FSTYPE_DATA_CFACK_CFPOLL 0x03
#define WLAN_FSTYPE_NULL 0x04
#define WLAN_FSTYPE_CFACK 0x05
#define WLAN_FSTYPE_CFPOLL 0x06
#define WLAN_FSTYPE_CFACK_CFPOLL 0x07
/*================================================================*/
/* Macros */
/*--- FC Macros ----------------------------------------------*/
/* Macros to get/set the bitfields of the Frame Control Field */
/* GET_FC_??? - takes the host byte-order value of an FC */
/* and retrieves the value of one of the */
/* bitfields and moves that value so its lsb is */
/* in bit 0. */
/* SET_FC_??? - takes a host order value for one of the FC */
/* bitfields and moves it to the proper bit */
/* location for ORing into a host order FC. */
/* To send the FC produced from SET_FC_???, */
/* one must put the bytes in IEEE order. */
/* e.g. */
/* printf("the frame subtype is %x", */
/* GET_FC_FTYPE( ieee2host( rx.fc ))) */
/* */
/* tx.fc = host2ieee( SET_FC_FTYPE(WLAN_FTYP_CTL) | */
/* SET_FC_FSTYPE(WLAN_FSTYPE_RTS) ); */
/*------------------------------------------------------------*/
#define WLAN_GET_FC_PVER(n) (((UINT16)(n)) & (BIT0 | BIT1))
#define WLAN_GET_FC_FTYPE(n) ((((UINT16)(n)) & (BIT2 | BIT3)) >> 2)
#define WLAN_GET_FC_FSTYPE(n) ((((UINT16)(n)) & (BIT4|BIT5|BIT6|BIT7)) >> 4)
#define WLAN_GET_FC_TODS(n) ((((UINT16)(n)) & (BIT8)) >> 8)
#define WLAN_GET_FC_FROMDS(n) ((((UINT16)(n)) & (BIT9)) >> 9)
#define WLAN_GET_FC_MOREFRAG(n) ((((UINT16)(n)) & (BIT10)) >> 10)
#define WLAN_GET_FC_RETRY(n) ((((UINT16)(n)) & (BIT11)) >> 11)
#define WLAN_GET_FC_PWRMGT(n) ((((UINT16)(n)) & (BIT12)) >> 12)
#define WLAN_GET_FC_MOREDATA(n) ((((UINT16)(n)) & (BIT13)) >> 13)
#define WLAN_GET_FC_ISWEP(n) ((((UINT16)(n)) & (BIT14)) >> 14)
#define WLAN_GET_FC_ORDER(n) ((((UINT16)(n)) & (BIT15)) >> 15)
#define WLAN_SET_FC_PVER(n) ((UINT16)(n))
#define WLAN_SET_FC_FTYPE(n) (((UINT16)(n)) << 2)
#define WLAN_SET_FC_FSTYPE(n) (((UINT16)(n)) << 4)
#define WLAN_SET_FC_TODS(n) (((UINT16)(n)) << 8)
#define WLAN_SET_FC_FROMDS(n) (((UINT16)(n)) << 9)
#define WLAN_SET_FC_MOREFRAG(n) (((UINT16)(n)) << 10)
#define WLAN_SET_FC_RETRY(n) (((UINT16)(n)) << 11)
#define WLAN_SET_FC_PWRMGT(n) (((UINT16)(n)) << 12)
#define WLAN_SET_FC_MOREDATA(n) (((UINT16)(n)) << 13)
#define WLAN_SET_FC_ISWEP(n) (((UINT16)(n)) << 14)
#define WLAN_SET_FC_ORDER(n) (((UINT16)(n)) << 15)
/*--- Duration Macros ----------------------------------------*/
/* Macros to get/set the bitfields of the Duration Field */
/* - the duration value is only valid when bit15 is zero */
/* - the firmware handles these values, so I'm not going */
/* these macros right now. */
/*------------------------------------------------------------*/
/*--- Sequence Control Macros -------------------------------*/
/* Macros to get/set the bitfields of the Sequence Control */
/* Field. */
/*------------------------------------------------------------*/
#define WLAN_GET_SEQ_FRGNUM(n) (((UINT16)(n)) & (BIT0|BIT1|BIT2|BIT3))
#define WLAN_GET_SEQ_SEQNUM(n) ((((UINT16)(n)) & (~(BIT0|BIT1|BIT2|BIT3))) >> 4)
/*--- Data ptr macro -----------------------------------------*/
/* Creates a UINT8* to the data portion of a frame */
/* Assumes you're passing in a ptr to the beginning of the hdr*/
/*------------------------------------------------------------*/
#define WLAN_HDR_A3_DATAP(p) (((UINT8*)(p)) + WLAN_HDR_A3_LEN)
#define WLAN_HDR_A4_DATAP(p) (((UINT8*)(p)) + WLAN_HDR_A4_LEN)
#define DOT11_RATE5_ISBASIC_GET(r) (((UINT8)(r)) & BIT7)
/*================================================================*/
/* Types */
/* BSS Timestamp */
typedef UINT8 wlan_bss_ts_t[WLAN_BSS_TS_LEN];
/* Generic 802.11 Header types */
__WLAN_PRAGMA_PACK1__
typedef struct p80211_hdr_a3
{
UINT16 fc __WLAN_ATTRIB_PACK__;
UINT16 dur __WLAN_ATTRIB_PACK__;
UINT8 a1[WLAN_ADDR_LEN] __WLAN_ATTRIB_PACK__;
UINT8 a2[WLAN_ADDR_LEN] __WLAN_ATTRIB_PACK__;
UINT8 a3[WLAN_ADDR_LEN] __WLAN_ATTRIB_PACK__;
UINT16 seq __WLAN_ATTRIB_PACK__;
} __WLAN_ATTRIB_PACK__ p80211_hdr_a3_t;
__WLAN_PRAGMA_PACKDFLT__
__WLAN_PRAGMA_PACK1__
typedef struct p80211_hdr_a4
{
UINT16 fc __WLAN_ATTRIB_PACK__;
UINT16 dur __WLAN_ATTRIB_PACK__;
UINT8 a1[WLAN_ADDR_LEN] __WLAN_ATTRIB_PACK__;
UINT8 a2[WLAN_ADDR_LEN] __WLAN_ATTRIB_PACK__;
UINT8 a3[WLAN_ADDR_LEN] __WLAN_ATTRIB_PACK__;
UINT16 seq __WLAN_ATTRIB_PACK__;
UINT8 a4[WLAN_ADDR_LEN] __WLAN_ATTRIB_PACK__;
} __WLAN_ATTRIB_PACK__ p80211_hdr_a4_t;
__WLAN_PRAGMA_PACKDFLT__
typedef union p80211_hdr
{
p80211_hdr_a3_t a3 __WLAN_ATTRIB_PACK__;
p80211_hdr_a4_t a4 __WLAN_ATTRIB_PACK__;
} __WLAN_ATTRIB_PACK__ p80211_hdr_t;
/*================================================================*/
/* Extern Declarations */
/*================================================================*/
/* Function Declarations */
void p802addr_to_str( char *buf, UINT8 *addr);
#endif /* _P80211HDR_H */

1004
src/drivers/net/pcnet32.c Normal file

File diff suppressed because it is too large Load Diff

267
src/drivers/net/pnic.c Normal file
View File

@@ -0,0 +1,267 @@
/**************************************************************************
Etherboot - BOOTP/TFTP Bootstrap Program
Bochs Pseudo NIC driver for Etherboot
***************************************************************************/
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2, or (at
* your option) any later version.
*
* See pnic_api.h for an explanation of the Bochs Pseudo NIC.
*/
/* to get some global routines like printf */
#include "etherboot.h"
/* to get the interface to the body of the program */
#include "nic.h"
/* to get the PCI support functions, if this is a PCI NIC */
#include "pci.h"
/* PNIC API */
#include "pnic_api.h"
/* Private data structure */
typedef struct {
uint16_t api_version;
} pnic_priv_data_t;
/* Function prototypes */
static int pnic_api_check ( uint16_t api_version );
/* NIC specific static variables go here */
static uint8_t tx_buffer[ETH_FRAME_LEN];
/*
* Utility functions: issue a PNIC command, retrieve result. Use
* pnic_command_quiet if you don't want failure codes to be
* automatically printed. Returns the PNIC status code.
*
* Set output_length to NULL only if you expect to receive exactly
* output_max_length bytes, otherwise it'll complain that you didn't
* get enough data (on the assumption that if you not interested in
* discovering the output length then you're expecting a fixed amount
* of data).
*/
static uint16_t pnic_command_quiet ( struct nic *nic, uint16_t command,
void *input, uint16_t input_length,
void *output, uint16_t output_max_length,
uint16_t *output_length ) {
int i;
uint16_t status;
uint16_t _output_length;
if ( input != NULL ) {
/* Write input length */
outw ( input_length, nic->ioaddr + PNIC_REG_LEN );
/* Write input data */
for ( i = 0; i < input_length; i++ ) {
outb( ((char*)input)[i], nic->ioaddr + PNIC_REG_DATA );
}
}
/* Write command */
outw ( command, nic->ioaddr + PNIC_REG_CMD );
/* Retrieve status */
status = inw ( nic->ioaddr + PNIC_REG_STAT );
/* Retrieve output length */
_output_length = inw ( nic->ioaddr + PNIC_REG_LEN );
if ( output_length == NULL ) {
if ( _output_length != output_max_length ) {
printf ( "pnic_command %#hx: wrong data length "
"returned (expected %d, got %d)\n", command,
output_max_length, _output_length );
}
} else {
*output_length = _output_length;
}
if ( output != NULL ) {
if ( _output_length > output_max_length ) {
printf ( "pnic_command %#hx: output buffer too small "
"(have %d, need %d)\n", command,
output_max_length, _output_length );
_output_length = output_max_length;
}
/* Retrieve output data */
for ( i = 0; i < _output_length; i++ ) {
((char*)output)[i] =
inb ( nic->ioaddr + PNIC_REG_DATA );
}
}
return status;
}
static uint16_t pnic_command ( struct nic *nic, uint16_t command,
void *input, uint16_t input_length,
void *output, uint16_t output_max_length,
uint16_t *output_length ) {
pnic_priv_data_t *priv = (pnic_priv_data_t*)nic->priv_data;
uint16_t status = pnic_command_quiet ( nic, command,
input, input_length,
output, output_max_length,
output_length );
if ( status == PNIC_STATUS_OK ) return status;
printf ( "PNIC command %#hx (len %#hx) failed with status %#hx\n",
command, input_length, status );
if ( priv->api_version ) pnic_api_check(priv->api_version);
return status;
}
/* Check API version matches that of NIC */
static int pnic_api_check ( uint16_t api_version ) {
if ( api_version != PNIC_API_VERSION ) {
printf ( "Warning: API version mismatch! "
"(NIC's is %d.%d, ours is %d.%d)\n",
api_version >> 8, api_version & 0xff,
PNIC_API_VERSION >> 8, PNIC_API_VERSION & 0xff );
}
if ( api_version < PNIC_API_VERSION ) {
printf ( "*** You may need to update your copy of Bochs ***\n" );
}
return ( api_version == PNIC_API_VERSION );
}
/**************************************************************************
POLL - Wait for a frame
***************************************************************************/
static int pnic_poll(struct nic *nic, int retrieve)
{
uint16_t length;
uint16_t qlen;
/* Check receive queue length to see if there's anything to
* get. Necessary since once we've called PNIC_CMD_RECV we
* have to read out the packet, otherwise it's lost forever.
*/
if ( pnic_command ( nic, PNIC_CMD_RECV_QLEN, NULL, 0,
&qlen, sizeof(qlen), NULL )
!= PNIC_STATUS_OK ) return ( 0 );
if ( qlen == 0 ) return ( 0 );
/* There is a packet ready. Return 1 if we're only checking. */
if ( ! retrieve ) return ( 1 );
/* Retrieve the packet */
if ( pnic_command ( nic, PNIC_CMD_RECV, NULL, 0,
nic->packet, ETH_FRAME_LEN, &length )
!= PNIC_STATUS_OK ) return ( 0 );
nic->packetlen = length;
return ( 1 );
}
/**************************************************************************
TRANSMIT - Transmit a frame
***************************************************************************/
static void pnic_transmit(
struct nic *nic,
const char *dest, /* Destination */
unsigned int type, /* Type */
unsigned int size, /* size */
const char *data) /* Packet */
{
unsigned int nstype = htons ( type );
if ( ( ETH_HLEN + size ) >= ETH_FRAME_LEN ) {
printf ( "pnic_transmit: packet too large\n" );
return;
}
/* Assemble packet */
memcpy ( tx_buffer, dest, ETH_ALEN );
memcpy ( tx_buffer + ETH_ALEN, nic->node_addr, ETH_ALEN );
memcpy ( tx_buffer + 2 * ETH_ALEN, &nstype, 2 );
memcpy ( tx_buffer + ETH_HLEN, data, size );
pnic_command ( nic, PNIC_CMD_XMIT, tx_buffer, ETH_HLEN + size,
NULL, 0, NULL );
}
/**************************************************************************
DISABLE - Turn off ethernet interface
***************************************************************************/
static void pnic_disable(struct dev *dev)
{
struct nic *nic = (struct nic *)dev;
pnic_command ( nic, PNIC_CMD_RESET, NULL, 0, NULL, 0, NULL );
}
/**************************************************************************
IRQ - Handle card interrupt status
***************************************************************************/
static void pnic_irq ( struct nic *nic, irq_action_t action )
{
uint8_t enabled;
switch ( action ) {
case DISABLE :
case ENABLE :
enabled = ( action == ENABLE ? 1 : 0 );
pnic_command ( nic, PNIC_CMD_MASK_IRQ,
&enabled, sizeof(enabled), NULL, 0, NULL );
break;
case FORCE :
pnic_command ( nic, PNIC_CMD_FORCE_IRQ,
NULL, 0, NULL, 0, NULL );
break;
}
}
/**************************************************************************
PROBE - Look for an adapter, this routine's visible to the outside
***************************************************************************/
static int pnic_probe(struct dev *dev, struct pci_device *pci)
{
struct nic *nic = (struct nic *)dev;
static pnic_priv_data_t priv;
uint16_t status;
printf(" - ");
/* Clear private data structure and chain it in */
memset ( &priv, 0, sizeof(priv) );
nic->priv_data = &priv;
/* Mask the bit that says "this is an io addr" */
nic->ioaddr = pci->ioaddr & ~3;
nic->irqno = pci->irq;
/* Not sure what this does, but the rtl8139 driver does it */
adjust_pci_device(pci);
status = pnic_command_quiet( nic, PNIC_CMD_API_VER, NULL, 0,
&priv.api_version,
sizeof(priv.api_version), NULL );
if ( status != PNIC_STATUS_OK ) {
printf ( "PNIC failed installation check, code %#hx\n",
status );
return 0;
}
pnic_api_check(priv.api_version);
status = pnic_command ( nic, PNIC_CMD_READ_MAC, NULL, 0,
nic->node_addr, ETH_ALEN, NULL );
printf ( "Detected Bochs Pseudo NIC MAC %! (API v%d.%d) at %#hx\n",
nic->node_addr, priv.api_version>>8, priv.api_version&0xff,
nic->ioaddr );
/* point to NIC specific routines */
dev->disable = pnic_disable;
nic->poll = pnic_poll;
nic->transmit = pnic_transmit;
nic->irq = pnic_irq;
return 1;
}
static struct pci_id pnic_nics[] = {
/* genrules.pl doesn't let us use macros for PCI IDs...*/
PCI_ROM(0xfefe, 0xefef, "pnic", "Bochs Pseudo NIC Adaptor"),
};
static struct pci_driver pnic_driver __pci_driver = {
.type = NIC_DRIVER,
.name = "PNIC",
.probe = pnic_probe,
.ids = pnic_nics,
.id_count = sizeof(pnic_nics)/sizeof(pnic_nics[0]),
.class = 0,
};

View File

@@ -0,0 +1,59 @@
/*
* Constants etc. for the Bochs/Etherboot pseudo-NIC
*
* This header file must be valid C and C++.
*
* Operation of the pseudo-NIC (PNIC) is pretty simple. To write a
* command plus data, first write the length of the data to
* PNIC_REG_LEN, then write the data a byte at a type to
* PNIC_REG_DATA, then write the command code to PNIC_REG_CMD. The
* status will be available from PNIC_REG_STAT. The length of any
* data returned will be in PNIC_REG_LEN and can be read a byte at a
* time from PNIC_REG_DATA.
*/
/*
* PCI parameters
*/
#define PNIC_PCI_VENDOR 0xfefe /* Hopefully these won't clash with */
#define PNIC_PCI_DEVICE 0xefef /* any real PCI device IDs. */
/*
* 'Hardware' register addresses, offset from io_base
*/
#define PNIC_REG_CMD 0x00 /* Command register, 2 bytes, write only */
#define PNIC_REG_STAT 0x00 /* Status register, 2 bytes, read only */
#define PNIC_REG_LEN 0x02 /* Length register, 2 bytes, read-write */
#define PNIC_REG_DATA 0x04 /* Data port, 1 byte, read-write */
/*
* PNIC_MAX_REG used in Bochs to claim i/o space
*/
#define PNIC_MAX_REG 0x04
/*
* Command code definitions: write these into PNIC_REG_CMD
*/
#define PNIC_CMD_NOOP 0x0000
#define PNIC_CMD_API_VER 0x0001
#define PNIC_CMD_READ_MAC 0x0002
#define PNIC_CMD_RESET 0x0003
#define PNIC_CMD_XMIT 0x0004
#define PNIC_CMD_RECV 0x0005
#define PNIC_CMD_RECV_QLEN 0x0006
#define PNIC_CMD_MASK_IRQ 0x0007
#define PNIC_CMD_FORCE_IRQ 0x0008
/*
* Status code definitions: read these from PNIC_REG_STAT
*
* We avoid using status codes that might be confused with
* randomly-read data (e.g. 0x0000, 0xffff etc.)
*/
#define PNIC_STATUS_OK 0x4f4b /* 'OK' */
#define PNIC_STATUS_UNKNOWN_CMD 0x3f3f /* '??' */
/*
* Other miscellaneous information
*/
#define PNIC_API_VERSION 0x0101 /* 1.1 */

948
src/drivers/net/prism2.c Normal file
View File

@@ -0,0 +1,948 @@
/**************************************************************************
Etherboot - BOOTP/TFTP Bootstrap Program
Prism2 NIC driver for Etherboot
Written by Michael Brown of Fen Systems Ltd
$Id$
***************************************************************************/
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2, or (at
* your option) any later version.
*/
/* to get some global routines like printf */
#include "etherboot.h"
/* to get the interface to the body of the program */
#include "nic.h"
/* to get the PCI support functions, if this is a PCI NIC */
#include "pci.h"
/*
* Hard-coded SSID
* Leave blank in order to connect to any available SSID
*/
static const char hardcoded_ssid[] = "";
/*
* Maximum number of info packets to wait for on a join attempt.
* Some APs (including the Linksys WAP11) will send a "you are disconnected" packet
* before sending the "you are connected" packet, if the card has previously been
* attached to the AP.
*
* 2 is probably a sensible value, but YMMV.
*/
#define MAX_JOIN_INFO_COUNT 2
/*
* Type of Prism2 interface to support
* If not already defined, select PLX
*/
#ifndef WLAN_HOSTIF
#define WLAN_HOSTIF WLAN_PLX
#endif
/*
* Include wlan_compat, p80211 and hfa384x header files from Linux Prism2 driver
* We need to hack some defines in order to avoid compiling kernel-specific routines
*/
#define __LINUX_WLAN__
#undef __KERNEL__
#define __I386__
#include "wlan_compat.h"
#include "p80211hdr.h"
#include "hfa384x.h"
#define BAP_TIMEOUT ( 5000 )
/*
* A few hacks to make the coding environment more Linux-like. This makes it somewhat
* quicker to convert code from the Linux Prism2 driver.
*/
#include <errno.h>
#include "timer.h"
#define __le16_to_cpu(x) (x)
#define __le32_to_cpu(x) (x)
#define __cpu_to_le16(x) (x)
#define __cpu_to_le32(x) (x)
/*
* PLX9052 PCI register offsets
* Taken from PLX9052 datasheet available from http://www.plxtech.com/download/9052/databook/9052db-20.pdf
*/
#define PLX_LOCAL_CONFIG_REGISTER_BASE ( PCI_BASE_ADDRESS_1 )
#define PLX_LOCAL_ADDRESS_SPACE_0_BASE ( PCI_BASE_ADDRESS_2 )
#define PLX_LOCAL_ADDRESS_SPACE_1_BASE ( PCI_BASE_ADDRESS_3 )
#define PLX_LOCAL_ADDRESS_SPACE_2_BASE ( PCI_BASE_ADDRESS_4 )
#define PLX_LOCAL_ADDRESS_SPACE_3_BASE ( PCI_BASE_ADDRESS_5 )
#define PRISM2_PLX_ATTR_MEM_BASE ( PLX_LOCAL_ADDRESS_SPACE_0_BASE )
#define PRISM2_PLX_IO_BASE ( PLX_LOCAL_ADDRESS_SPACE_1_BASE )
#define PRISM2_PCI_MEM_BASE ( PCI_BASE_ADDRESS_0 )
/*
* PCMCIA CIS types
* Taken from cistpl.h in pcmcia-cs
*/
#define CISTPL_VERS_1 ( 0x15 )
#define CISTPL_END ( 0xff )
#define CIS_STEP ( 2 )
#define CISTPL_HEADER_LEN ( 2 * CIS_STEP )
#define CISTPL_LEN_OFF ( 1 * CIS_STEP )
#define CISTPL_VERS_1_STR_OFF ( 4 * CIS_STEP )
/*
* Prism2 constants
* Taken from prism2sta.c in linux-wlan-ng
*/
#define COR_OFFSET ( 0x3e0 ) /* COR attribute offset of Prism2 PC card */
#define COR_VALUE ( 0x41 ) /* Enable PC card with irq in level trigger (but interrupts disabled) */
/* NIC specific static variables */
/* The hfa384x_t structure is used extensively in the Linux driver but is ifdef'd out in our include since __KERNEL__ is not defined.
* This is a dummy version that contains only the fields we are interested in.
*/
typedef struct hfa384x
{
UINT32 iobase;
UINT32 membase;
UINT16 lastcmd;
UINT16 status; /* in host order */
UINT16 resp0; /* in host order */
UINT16 resp1; /* in host order */
UINT16 resp2; /* in host order */
UINT8 bssid[WLAN_BSSID_LEN];
} hfa384x_t;
/* The global instance of the hardware (i.e. where we store iobase and membase, in the absence of anywhere better to put them */
static hfa384x_t hw_global = {
0, 0, 0, 0, 0, 0, 0, {0,0,0,0,0,0}
};
/*
* 802.11 headers in addition to those in hfa384x_tx_frame_t (LLC and SNAP)
* Taken from p80211conv.h
*/
typedef struct wlan_llc
{
UINT8 dsap __WLAN_ATTRIB_PACK__;
UINT8 ssap __WLAN_ATTRIB_PACK__;
UINT8 ctl __WLAN_ATTRIB_PACK__;
} __WLAN_ATTRIB_PACK__ wlan_llc_t;
static const wlan_llc_t wlan_llc_snap = { 0xaa, 0xaa, 0x03 }; /* LLC header indicating SNAP (?) */
#define WLAN_IEEE_OUI_LEN 3
typedef struct wlan_snap
{
UINT8 oui[WLAN_IEEE_OUI_LEN] __WLAN_ATTRIB_PACK__;
UINT16 type __WLAN_ATTRIB_PACK__;
} __WLAN_ATTRIB_PACK__ wlan_snap_t;
typedef struct wlan_80211hdr
{
wlan_llc_t llc;
wlan_snap_t snap;
} wlan_80211hdr_t;
/*
* Function prototypes
*/
#if (WLAN_HOSTIF == WLAN_PLX)
static int prism2_find_plx ( hfa384x_t *hw, struct pci_device *p );
#elif (WLAN_HOSTIF == WLAN_PCI)
static int prism2_find_pci ( hfa384x_t *hw, struct pci_device *p );
#endif
/*
* Hardware-level hfa384x functions
* These are based on the ones in hfa384x.h (which are ifdef'd out since __KERNEL__ is not defined).
* Basically, these functions are the result of hand-evaluating all the ifdefs and defines in the hfa384x.h versions.
*/
/* Retrieve the value of one of the MAC registers. */
static inline UINT16 hfa384x_getreg( hfa384x_t *hw, UINT reg )
{
#if (WLAN_HOSTIF == WLAN_PLX)
return inw ( hw->iobase + reg );
#elif (WLAN_HOSTIF == WLAN_PCI)
return readw ( hw->membase + reg );
#endif
}
/* Set the value of one of the MAC registers. */
static inline void hfa384x_setreg( hfa384x_t *hw, UINT16 val, UINT reg )
{
#if (WLAN_HOSTIF == WLAN_PLX)
outw ( val, hw->iobase + reg );
#elif (WLAN_HOSTIF == WLAN_PCI)
writew ( val, hw->membase + reg );
#endif
return;
}
/*
* Noswap versions
* Etherboot is i386 only, so swap and noswap are the same...
*/
static inline UINT16 hfa384x_getreg_noswap( hfa384x_t *hw, UINT reg )
{
return hfa384x_getreg ( hw, reg );
}
static inline void hfa384x_setreg_noswap( hfa384x_t *hw, UINT16 val, UINT reg )
{
hfa384x_setreg ( hw, val, reg );
}
/*
* Low-level hfa384x functions
* These are based on the ones in hfa384x.c, modified to work in the Etherboot environment.
*/
/*
* hfa384x_docmd_wait
*
* Waits for availability of the Command register, then
* issues the given command. Then polls the Evstat register
* waiting for command completion.
* Arguments:
* hw device structure
* cmd Command in host order
* parm0 Parameter0 in host order
* parm1 Parameter1 in host order
* parm2 Parameter2 in host order
* Returns:
* 0 success
* >0 command indicated error, Status and Resp0-2 are
* in hw structure.
*/
static int hfa384x_docmd_wait( hfa384x_t *hw, UINT16 cmd, UINT16 parm0, UINT16 parm1, UINT16 parm2)
{
UINT16 reg = 0;
UINT16 counter = 0;
/* wait for the busy bit to clear */
counter = 0;
reg = hfa384x_getreg(hw, HFA384x_CMD);
while ( HFA384x_CMD_ISBUSY(reg) && (counter < 10) ) {
reg = hfa384x_getreg(hw, HFA384x_CMD);
counter++;
udelay(10);
}
if (HFA384x_CMD_ISBUSY(reg)) {
printf("hfa384x_cmd timeout(1), reg=0x%0hx.\n", reg);
return -ETIMEDOUT;
}
/* busy bit clear, write command */
hfa384x_setreg(hw, parm0, HFA384x_PARAM0);
hfa384x_setreg(hw, parm1, HFA384x_PARAM1);
hfa384x_setreg(hw, parm2, HFA384x_PARAM2);
hw->lastcmd = cmd;
hfa384x_setreg(hw, cmd, HFA384x_CMD);
/* Now wait for completion */
counter = 0;
reg = hfa384x_getreg(hw, HFA384x_EVSTAT);
/* Initialization is the problem. It takes about
100ms. "normal" commands are typically is about
200-400 us (I've never seen less than 200). Longer
is better so that we're not hammering the bus. */
while ( !HFA384x_EVSTAT_ISCMD(reg) && (counter < 5000)) {
reg = hfa384x_getreg(hw, HFA384x_EVSTAT);
counter++;
udelay(200);
}
if ( ! HFA384x_EVSTAT_ISCMD(reg) ) {
printf("hfa384x_cmd timeout(2), reg=0x%0hx.\n", reg);
return -ETIMEDOUT;
}
/* Read status and response */
hw->status = hfa384x_getreg(hw, HFA384x_STATUS);
hw->resp0 = hfa384x_getreg(hw, HFA384x_RESP0);
hw->resp1 = hfa384x_getreg(hw, HFA384x_RESP1);
hw->resp2 = hfa384x_getreg(hw, HFA384x_RESP2);
hfa384x_setreg(hw, HFA384x_EVACK_CMD, HFA384x_EVACK);
return HFA384x_STATUS_RESULT_GET(hw->status);
}
/*
* Prepare BAP for access. Assigns FID and RID, sets offset register
* and waits for BAP to become available.
*
* Arguments:
* hw device structure
* id FID or RID, destined for the select register (host order)
* offset An _even_ offset into the buffer for the given FID/RID.
* Returns:
* 0 success
*/
static int hfa384x_prepare_bap(hfa384x_t *hw, UINT16 id, UINT16 offset)
{
int result = 0;
UINT16 reg;
UINT16 i;
/* Validate offset, buf, and len */
if ( (offset > HFA384x_BAP_OFFSET_MAX) || (offset % 2) ) {
result = -EINVAL;
} else {
/* Write fid/rid and offset */
hfa384x_setreg(hw, id, HFA384x_SELECT0);
udelay(10);
hfa384x_setreg(hw, offset, HFA384x_OFFSET0);
/* Wait for offset[busy] to clear (see BAP_TIMEOUT) */
i = 0;
do {
reg = hfa384x_getreg(hw, HFA384x_OFFSET0);
if ( i > 0 ) udelay(2);
i++;
} while ( i < BAP_TIMEOUT && HFA384x_OFFSET_ISBUSY(reg));
if ( i >= BAP_TIMEOUT ) {
/* failure */
result = reg;
} else if ( HFA384x_OFFSET_ISERR(reg) ){
/* failure */
result = reg;
}
}
return result;
}
/*
* Copy data from BAP to memory.
*
* Arguments:
* hw device structure
* id FID or RID, destined for the select register (host order)
* offset An _even_ offset into the buffer for the given FID/RID.
* buf ptr to array of bytes
* len length of data to transfer in bytes
* Returns:
* 0 success
*/
static int hfa384x_copy_from_bap(hfa384x_t *hw, UINT16 id, UINT16 offset,
void *buf, UINT len)
{
int result = 0;
UINT8 *d = (UINT8*)buf;
UINT16 i;
UINT16 reg = 0;
/* Prepare BAP */
result = hfa384x_prepare_bap ( hw, id, offset );
if ( result == 0 ) {
/* Read even(len) buf contents from data reg */
for ( i = 0; i < (len & 0xfffe); i+=2 ) {
*(UINT16*)(&(d[i])) = hfa384x_getreg_noswap(hw, HFA384x_DATA0);
}
/* If len odd, handle last byte */
if ( len % 2 ){
reg = hfa384x_getreg_noswap(hw, HFA384x_DATA0);
d[len-1] = ((UINT8*)(&reg))[0];
}
}
if (result) {
printf ( "copy_from_bap(%#hx, %#hx, %d) failed, result=%#hx\n", id, offset, len, result);
}
return result;
}
/*
* Copy data from memory to BAP.
*
* Arguments:
* hw device structure
* id FID or RID, destined for the select register (host order)
* offset An _even_ offset into the buffer for the given FID/RID.
* buf ptr to array of bytes
* len length of data to transfer in bytes
* Returns:
* 0 success
*/
static int hfa384x_copy_to_bap(hfa384x_t *hw, UINT16 id, UINT16 offset,
void *buf, UINT len)
{
int result = 0;
UINT8 *d = (UINT8*)buf;
UINT16 i;
UINT16 savereg;
/* Prepare BAP */
result = hfa384x_prepare_bap ( hw, id, offset );
if ( result == 0 ) {
/* Write even(len) buf contents to data reg */
for ( i = 0; i < (len & 0xfffe); i+=2 ) {
hfa384x_setreg_noswap(hw, *(UINT16*)(&(d[i])), HFA384x_DATA0);
}
/* If len odd, handle last byte */
if ( len % 2 ){
savereg = hfa384x_getreg_noswap(hw, HFA384x_DATA0);
result = hfa384x_prepare_bap ( hw, id, offset + (len & 0xfffe) );
if ( result == 0 ) {
((UINT8*)(&savereg))[0] = d[len-1];
hfa384x_setreg_noswap(hw, savereg, HFA384x_DATA0);
}
}
}
if (result) {
printf ( "copy_to_bap(%#hx, %#hx, %d) failed, result=%#hx\n", id, offset, len, result);
}
return result;
}
/*
* Request a given record to be copied to/from the record buffer.
*
* Arguments:
* hw device structure
* write [0|1] copy the record buffer to the given
* configuration record. (host order)
* rid RID of the record to read/write. (host order)
*
* Returns:
* 0 success
*/
static inline int hfa384x_cmd_access(hfa384x_t *hw, UINT16 write, UINT16 rid)
{
return hfa384x_docmd_wait(hw, HFA384x_CMD_CMDCODE_SET(HFA384x_CMDCODE_ACCESS) | HFA384x_CMD_WRITE_SET(write), rid, 0, 0);
}
/*
* Performs the sequence necessary to read a config/info item.
*
* Arguments:
* hw device structure
* rid config/info record id (host order)
* buf host side record buffer. Upon return it will
* contain the body portion of the record (minus the
* RID and len).
* len buffer length (in bytes, should match record length)
*
* Returns:
* 0 success
*/
static int hfa384x_drvr_getconfig(hfa384x_t *hw, UINT16 rid, void *buf, UINT16 len)
{
int result = 0;
hfa384x_rec_t rec;
/* Request read of RID */
result = hfa384x_cmd_access( hw, 0, rid);
if ( result ) {
printf("Call to hfa384x_cmd_access failed\n");
return -1;
}
/* Copy out record length */
result = hfa384x_copy_from_bap( hw, rid, 0, &rec, sizeof(rec));
if ( result ) {
return -1;
}
/* Validate the record length */
if ( ((hfa384x2host_16(rec.reclen)-1)*2) != len ) { /* note body len calculation in bytes */
printf ( "RID len mismatch, rid=%#hx hlen=%d fwlen=%d\n", rid, len, (hfa384x2host_16(rec.reclen)-1)*2);
return -1;
}
/* Copy out record data */
result = hfa384x_copy_from_bap( hw, rid, sizeof(rec), buf, len);
return result;
}
/*
* Performs the sequence necessary to read a 16/32 bit config/info item
* and convert it to host order.
*
* Arguments:
* hw device structure
* rid config/info record id (in host order)
* val ptr to 16/32 bit buffer to receive value (in host order)
*
* Returns:
* 0 success
*/
#if 0 /* Not actually used anywhere */
static int hfa384x_drvr_getconfig16(hfa384x_t *hw, UINT16 rid, void *val)
{
int result = 0;
result = hfa384x_drvr_getconfig(hw, rid, val, sizeof(UINT16));
if ( result == 0 ) {
*((UINT16*)val) = hfa384x2host_16(*((UINT16*)val));
}
return result;
}
#endif
#if 0 /* Not actually used anywhere */
static int hfa384x_drvr_getconfig32(hfa384x_t *hw, UINT16 rid, void *val)
{
int result = 0;
result = hfa384x_drvr_getconfig(hw, rid, val, sizeof(UINT32));
if ( result == 0 ) {
*((UINT32*)val) = hfa384x2host_32(*((UINT32*)val));
}
return result;
}
#endif
/*
* Performs the sequence necessary to write a config/info item.
*
* Arguments:
* hw device structure
* rid config/info record id (in host order)
* buf host side record buffer
* len buffer length (in bytes)
*
* Returns:
* 0 success
*/
static int hfa384x_drvr_setconfig(hfa384x_t *hw, UINT16 rid, void *buf, UINT16 len)
{
int result = 0;
hfa384x_rec_t rec;
rec.rid = host2hfa384x_16(rid);
rec.reclen = host2hfa384x_16((len/2) + 1); /* note conversion to words, +1 for rid field */
/* write the record header */
result = hfa384x_copy_to_bap( hw, rid, 0, &rec, sizeof(rec));
if ( result ) {
printf("Failure writing record header\n");
return -1;
}
/* write the record data (if there is any) */
if ( len > 0 ) {
result = hfa384x_copy_to_bap( hw, rid, sizeof(rec), buf, len);
if ( result ) {
printf("Failure writing record data\n");
return -1;
}
}
/* Trigger setting of record */
result = hfa384x_cmd_access( hw, 1, rid);
return result;
}
/*
* Performs the sequence necessary to write a 16/32 bit config/info item.
*
* Arguments:
* hw device structure
* rid config/info record id (in host order)
* val 16/32 bit value to store (in host order)
*
* Returns:
* 0 success
*/
static int hfa384x_drvr_setconfig16(hfa384x_t *hw, UINT16 rid, UINT16 *val)
{
UINT16 value;
value = host2hfa384x_16(*val);
return hfa384x_drvr_setconfig(hw, rid, &value, sizeof(UINT16));
}
#if 0 /* Not actually used anywhere */
static int hfa384x_drvr_setconfig32(hfa384x_t *hw, UINT16 rid, UINT32 *val)
{
UINT32 value;
value = host2hfa384x_32(*val);
return hfa384x_drvr_setconfig(hw, rid, &value, sizeof(UINT32));
}
#endif
/*
* Wait for an event, with specified checking interval and timeout.
* Automatically acknolwedges events.
*
* Arguments:
* hw device structure
* event_mask EVSTAT register mask of events to wait for
* event_ack EVACK register set of events to be acknowledged if they happen (can be
* used to acknowledge "ignorable" events in addition to the "main" event)
* wait Time (in us) to wait between each poll of the register
* timeout Maximum number of polls before timing out
* descr Descriptive text string of what is being waited for
* (will be printed out if a timeout happens)
*
* Returns:
* value of EVSTAT register, or 0 on failure
*/
static int hfa384x_wait_for_event(hfa384x_t *hw, UINT16 event_mask, UINT16 event_ack, int wait, int timeout, const char *descr)
{
UINT16 reg;
int count = 0;
do {
reg = hfa384x_getreg(hw, HFA384x_EVSTAT);
if ( count > 0 ) udelay(wait);
count++;
} while ( !(reg & event_mask) && count < timeout);
if ( count >= timeout ) {
printf("hfa384x: Timed out waiting for %s\n", descr);
return 0; /* Return failure */
}
/* Acknowledge all events that we were waiting on */
hfa384x_setreg(hw, reg & ( event_mask | event_ack ), HFA384x_EVACK);
return reg;
}
/**************************************************************************
POLL - Wait for a frame
***************************************************************************/
static int prism2_poll(struct nic *nic, int retrieve)
{
UINT16 reg;
UINT16 rxfid;
UINT16 result;
hfa384x_rx_frame_t rxdesc;
hfa384x_t *hw = &hw_global;
/* Check for received packet */
reg = hfa384x_getreg(hw, HFA384x_EVSTAT);
if ( ! HFA384x_EVSTAT_ISRX(reg) ) {
/* No packet received - return 0 */
return 0;
}
if ( ! retrieve ) return 1;
/* Acknowledge RX event */
hfa384x_setreg(hw, HFA384x_EVACK_RX_SET(1), HFA384x_EVACK);
/* Get RX FID */
rxfid = hfa384x_getreg(hw, HFA384x_RXFID);
/* Get the descriptor (including headers) */
result = hfa384x_copy_from_bap(hw, rxfid, 0, &rxdesc, sizeof(rxdesc));
if ( result ) {
return 0; /* fail */
}
/* Byte order convert once up front. */
rxdesc.status = hfa384x2host_16(rxdesc.status);
rxdesc.time = hfa384x2host_32(rxdesc.time);
rxdesc.data_len = hfa384x2host_16(rxdesc.data_len);
/* Fill in nic->packetlen */
nic->packetlen = rxdesc.data_len;
if ( nic->packetlen > 0 ) {
/* Fill in nic->packet */
/*
* NOTE: Packets as received have an 8-byte header (LLC+SNAP(?)) terminating with the packet type.
* Etherboot expects a 14-byte header terminating with the packet type (it ignores the rest of the
* header), so we use a quick hack to achieve this.
*/
result = hfa384x_copy_from_bap(hw, rxfid, HFA384x_RX_DATA_OFF,
nic->packet + ETH_HLEN - sizeof(wlan_80211hdr_t), nic->packetlen);
if ( result ) {
return 0; /* fail */
}
}
return 1; /* Packet successfully received */
}
/**************************************************************************
TRANSMIT - Transmit a frame
***************************************************************************/
static void prism2_transmit(
struct nic *nic,
const char *d, /* Destination */
unsigned int t, /* Type */
unsigned int s, /* size */
const char *p) /* Packet */
{
hfa384x_t *hw = &hw_global;
hfa384x_tx_frame_t txdesc;
wlan_80211hdr_t p80211hdr = { wlan_llc_snap, {{0,0,0},0} };
UINT16 fid;
UINT16 status;
int result;
// Request FID allocation
result = hfa384x_docmd_wait(hw, HFA384x_CMD_CMDCODE_SET(HFA384x_CMDCODE_ALLOC), HFA384x_DRVR_TXBUF_MAX, 0, 0);
if (result != 0) {
printf("hfa384x: Tx FID allocate command failed: Aborting transmit..\n");
return;
}
if ( !hfa384x_wait_for_event(hw, HFA384x_EVSTAT_ALLOC, HFA384x_EVACK_INFO, 10, 50, "Tx FID to be allocated\n" ) ) return;
fid = hfa384x_getreg(hw, HFA384x_ALLOCFID);
/* Build Tx frame structure */
memset(&txdesc, 0, sizeof(txdesc));
txdesc.tx_control = host2hfa384x_16( HFA384x_TX_MACPORT_SET(0) | HFA384x_TX_STRUCTYPE_SET(1) |
HFA384x_TX_TXEX_SET(1) | HFA384x_TX_TXOK_SET(1) );
txdesc.frame_control = host2ieee16( WLAN_SET_FC_FTYPE(WLAN_FTYPE_DATA) |
WLAN_SET_FC_FSTYPE(WLAN_FSTYPE_DATAONLY) |
WLAN_SET_FC_TODS(1) );
memcpy(txdesc.address1, hw->bssid, WLAN_ADDR_LEN);
memcpy(txdesc.address2, nic->node_addr, WLAN_ADDR_LEN);
memcpy(txdesc.address3, d, WLAN_ADDR_LEN);
txdesc.data_len = host2hfa384x_16( sizeof(txdesc) + sizeof(p80211hdr) + s );
/* Set up SNAP header */
/* Let OUI default to RFC1042 (0x000000) */
p80211hdr.snap.type = htons(t);
/* Copy txdesc, p80211hdr and payload parts to FID */
result = hfa384x_copy_to_bap(hw, fid, 0, &txdesc, sizeof(txdesc));
if ( result ) return; /* fail */
result = hfa384x_copy_to_bap( hw, fid, sizeof(txdesc), &p80211hdr, sizeof(p80211hdr) );
if ( result ) return; /* fail */
result = hfa384x_copy_to_bap( hw, fid, sizeof(txdesc) + sizeof(p80211hdr), (UINT8*)p, s );
if ( result ) return; /* fail */
/* Issue Tx command */
result = hfa384x_docmd_wait(hw, HFA384x_CMD_CMDCODE_SET(HFA384x_CMDCODE_TX), fid, 0, 0);
if ( result != 0 ) {
printf("hfa384x: Transmit failed with result %#hx.\n", result);
return;
}
/* Wait for transmit completion (or exception) */
result = hfa384x_wait_for_event(hw, HFA384x_EVSTAT_TXEXC | HFA384x_EVSTAT_TX, HFA384x_EVACK_INFO,
200, 500, "Tx to complete\n" );
if ( !result ) return; /* timeout failure */
if ( HFA384x_EVSTAT_ISTXEXC(result) ) {
fid = hfa384x_getreg(hw, HFA384x_TXCOMPLFID);
printf ( "Tx exception occurred with fid %#hx\n", fid );
result = hfa384x_copy_from_bap(hw, fid, 0, &status, sizeof(status));
if ( result ) return; /* fail */
printf("hfa384x: Tx error occurred (status %#hx):\n", status);
if ( HFA384x_TXSTATUS_ISACKERR(status) ) { printf(" ...acknowledgement error\n"); }
if ( HFA384x_TXSTATUS_ISFORMERR(status) ) { printf(" ...format error\n"); }
if ( HFA384x_TXSTATUS_ISDISCON(status) ) { printf(" ...disconnected error\n"); }
if ( HFA384x_TXSTATUS_ISAGEDERR(status) ) { printf(" ...AGED error\n"); }
if ( HFA384x_TXSTATUS_ISRETRYERR(status) ) { printf(" ...retry error\n"); }
return; /* fail */
}
}
/**************************************************************************
DISABLE - Turn off ethernet interface
***************************************************************************/
static void prism2_disable(struct dev *dev __unused)
{
/* put the card in its initial state */
}
/**************************************************************************
IRQ - Enable, Disable, or Force interrupts
***************************************************************************/
static void prism2_irq(struct nic *nic __unused, irq_action_t action __unused)
{
switch ( action ) {
case DISABLE :
break;
case ENABLE :
break;
case FORCE :
break;
}
}
/**************************************************************************
PROBE - Look for an adapter, this routine's visible to the outside
You should omit the last argument struct pci_device * for a non-PCI NIC
***************************************************************************/
#if (WLAN_HOSTIF == WLAN_PLX)
static int prism2_plx_probe(struct dev *dev, struct pci_device *p)
#elif (WLAN_HOSTIF == WLAN_PCI)
static int prism2_pci_probe(struct dev *dev, struct pci_device *p)
#endif
{
struct nic *nic = (struct nic *)dev;
hfa384x_t *hw = &hw_global;
int result;
UINT16 tmp16 = 0;
UINT16 infofid;
hfa384x_InfFrame_t inf;
char ssid[HFA384x_RID_CNFDESIREDSSID_LEN];
int info_count = 0;
/* Find and intialise PLX Prism2 card */
#if (WLAN_HOSTIF == WLAN_PLX)
if ( ! prism2_find_plx ( hw, p ) ) return 0;
nic->ioaddr = hw->iobase;
#elif (WLAN_HOSTIF == WLAN_PCI)
if ( ! prism2_find_pci ( hw, p ) ) return 0;
nic->ioaddr = hw->membase;
#endif
nic->irqno = 0;
/* Initialize card */
result = hfa384x_docmd_wait(hw, HFA384x_CMDCODE_INIT, 0,0,0); /* Send initialize command */
if ( result ) printf ( "Initialize command returned %#hx\n", result );
hfa384x_setreg(hw, 0, HFA384x_INTEN); /* Disable interrupts */
hfa384x_setreg(hw, 0xffff, HFA384x_EVACK); /* Acknowledge any spurious events */
/* Retrieve MAC address (and fill out nic->node_addr) */
hfa384x_drvr_getconfig ( hw, HFA384x_RID_CNFOWNMACADDR, nic->node_addr, HFA384x_RID_CNFOWNMACADDR_LEN );
printf ( "MAC address %!\n", nic->node_addr );
/* Prepare card for autojoin */
/* This procedure is reverse-engineered from a register-level trace of the Linux driver's join process */
tmp16 = WLAN_DATA_MAXLEN; /* Set maximum data length */
result = hfa384x_drvr_setconfig16(hw, HFA384x_RID_CNFMAXDATALEN, &tmp16);
if ( result ) printf ( "Set Max Data Length command returned %#hx\n", result );
tmp16 = 0x000f; /* Set transmit rate(?) */
result = hfa384x_drvr_setconfig16(hw, HFA384x_RID_TXRATECNTL, &tmp16);
if ( result ) printf ( "Set Transmit Rate command returned %#hx\n", result );
tmp16 = HFA384x_CNFAUTHENTICATION_OPENSYSTEM; /* Set authentication type to OpenSystem */
result = hfa384x_drvr_setconfig16(hw, HFA384x_RID_CNFAUTHENTICATION, &tmp16);
if ( result ) printf ( "Set Authentication Type command returned %#hx\n", result );
/* Set SSID */
memset(ssid, 0, HFA384x_RID_CNFDESIREDSSID_LEN);
for ( tmp16=0; tmp16<sizeof(hardcoded_ssid); tmp16++ ) { ssid[2+tmp16] = hardcoded_ssid[tmp16]; }
ssid[0] = sizeof(hardcoded_ssid) - 1; /* Ignore terminating zero */
result = hfa384x_drvr_setconfig(hw, HFA384x_RID_CNFDESIREDSSID, ssid, HFA384x_RID_CNFDESIREDSSID_LEN); /* Set the SSID */
if ( result ) printf ( "Set SSID command returned %#hx\n", result );
tmp16 = 1; /* Set port type to ESS port */
result = hfa384x_drvr_setconfig16(hw, HFA384x_RID_CNFPORTTYPE, &tmp16);
if ( result ) printf ( "Set port type command returned %#hx\n", result );
/* Enable card */
result = hfa384x_docmd_wait(hw, HFA384x_CMD_CMDCODE_SET(HFA384x_CMDCODE_ENABLE) | HFA384x_CMD_MACPORT_SET(0), 0,0,0);
if ( result ) printf ( "Enable command returned %#hx\n", result );
do {
/* Increment info_count, abort if too many attempts.
* See comment next to definition of MAX_JOIN_INFO_COUNT for explanation.
*/
info_count++;
if ( info_count > MAX_JOIN_INFO_COUNT ) {
printf ( "Too many failed attempts - aborting\n" );
return 0;
}
/* Wait for info frame to indicate link status */
if ( sizeof(hardcoded_ssid) == 1 ) {
/* Empty SSID => join to any SSID */
printf ( "Attempting to autojoin to any available access point (attempt %d)...", info_count );
} else {
printf ( "Attempting to autojoin to SSID %s (attempt %d)...", &ssid[2], info_count );
}
if ( !hfa384x_wait_for_event(hw, HFA384x_EVSTAT_INFO, 0, 1000, 2000, "Info event" ) ) return 0;
printf("done\n");
infofid = hfa384x_getreg(hw, HFA384x_INFOFID);
/* Retrieve the length */
result = hfa384x_copy_from_bap( hw, infofid, 0, &inf.framelen, sizeof(UINT16));
if ( result ) return 0; /* fail */
inf.framelen = hfa384x2host_16(inf.framelen);
/* Retrieve the rest */
result = hfa384x_copy_from_bap( hw, infofid, sizeof(UINT16),
&(inf.infotype), inf.framelen * sizeof(UINT16));
if ( result ) return 0; /* fail */
if ( inf.infotype != HFA384x_IT_LINKSTATUS ) {
/* Not a Link Status info frame: die */
printf ( "Unexpected info frame type %#hx (not LinkStatus type)\n", inf.infotype );
return 0;
}
inf.info.linkstatus.linkstatus = hfa384x2host_16(inf.info.linkstatus.linkstatus);
if ( inf.info.linkstatus.linkstatus != HFA384x_LINK_CONNECTED ) {
/* Link not connected - retry */
printf ( "Link not connected (status %#hx)\n", inf.info.linkstatus.linkstatus );
}
} while ( inf.info.linkstatus.linkstatus != HFA384x_LINK_CONNECTED );
/* Retrieve BSSID and print Connected message */
result = hfa384x_drvr_getconfig(hw, HFA384x_RID_CURRENTBSSID, hw->bssid, WLAN_BSSID_LEN);
printf ( "Link connected (BSSID %! - MAC address %!)\n", hw->bssid, nic->node_addr );
/* point to NIC specific routines */
dev->disable = prism2_disable;
nic->poll = prism2_poll;
nic->transmit = prism2_transmit;
nic->irq = prism2_irq;
return 1;
}
#if (WLAN_HOSTIF == WLAN_PLX)
/*
* Find PLX card. Prints out information strings from PCMCIA CIS as visual
* confirmation of presence of card.
*
* Arguments:
* hw device structure to be filled in
* p PCI device structure
*
* Returns:
* 1 Success
*/
static int prism2_find_plx ( hfa384x_t *hw, struct pci_device *p )
{
int found = 0;
uint32_t plx_lcr = 0; /* PLX9052 Local Configuration Register Base (I/O) */
uint32_t attr_mem = 0; /* Prism2 Attribute Memory Base */
uint32_t iobase = 0; /* Prism2 I/O Base */
unsigned char *cis_tpl = NULL;
unsigned char *cis_string;
/* Obtain all memory and IO base addresses */
pcibios_read_config_dword( p->bus, p->devfn, PLX_LOCAL_CONFIG_REGISTER_BASE, &plx_lcr);
plx_lcr &= PCI_BASE_ADDRESS_IO_MASK;
pcibios_read_config_dword( p->bus, p->devfn, PRISM2_PLX_ATTR_MEM_BASE, &attr_mem);
pcibios_read_config_dword( p->bus, p->devfn, PRISM2_PLX_IO_BASE, &iobase);
iobase &= PCI_BASE_ADDRESS_IO_MASK;
/* Fill out hw structure */
hw->membase = attr_mem;
hw->iobase = iobase;
printf ( "PLX9052 has local config registers at %#hx\n", plx_lcr );
printf ( "Prism2 has attribute memory at %#x and I/O base at %#hx\n", attr_mem, iobase );
/* Search for CIS strings */
printf ( "Searching for PCMCIA card...\n" );
cis_tpl = bus_to_virt(attr_mem);
while ( *cis_tpl != CISTPL_END ) {
if ( *cis_tpl == CISTPL_VERS_1 ) {
/* CISTPL_VERS_1 contains some nice text strings */
printf ( "...found " );
found = 1;
cis_string = cis_tpl + CISTPL_VERS_1_STR_OFF;
while ( ! ( ( *cis_string == 0 ) && ( *(cis_string+CIS_STEP) == 0 ) ) ) {
printf ( "%c", *cis_string == 0 ? ' ' : *cis_string );
cis_string += CIS_STEP;
}
printf ( "\n" );
}
/* printf ( "CIS tuple type %#hhx, length %#hhx\n", *cis_tpl, *(cis_tpl+CISTPL_LEN_OFF) ); */
cis_tpl += CISTPL_HEADER_LEN + CIS_STEP * ( *(cis_tpl+CISTPL_LEN_OFF) );
}
if ( found == 0 ) {
printf ( "...nothing found\n" );
}
((unsigned char *)bus_to_virt(attr_mem))[COR_OFFSET] = COR_VALUE; /* Write COR to enable PC card */
return found;
}
#endif /* WLAN_PLX */
#if (WLAN_HOSTIF == WLAN_PCI)
/*
* Find PCI card.
*
* Arguments:
* hw device structure to be filled in
* p PCI device structure
*
* Returns:
* 1 Success
*/
static int prism2_find_pci ( hfa384x_t *hw, struct pci_device *p )
{
uint32_t membase = 0; /* Prism2.5 Memory Base */
pcibios_read_config_dword( p->bus, p->devfn, PRISM2_PCI_MEM_BASE, &membase);
membase &= PCI_BASE_ADDRESS_MEM_MASK;
hw->membase = (uint32_t) phys_to_virt(membase);
printf ( "Prism2.5 has registers at %#x\n", hw->membase );
return 1;
}
#endif /* WLAN_PCI */

View File

@@ -0,0 +1,32 @@
/**************************************************************************
Etherboot - BOOTP/TFTP Bootstrap Program
Prism2 NIC driver for Etherboot
Wrapper for prism2_pci
Written by Michael Brown of Fen Systems Ltd
$Id$
***************************************************************************/
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2, or (at
* your option) any later version.
*/
#define WLAN_HOSTIF WLAN_PCI
#include "prism2.c"
static struct pci_id prism2_pci_nics[] = {
PCI_ROM(0x1260, 0x3873, "prism2_pci", "Harris Semiconductor Prism2.5 clone"), /* Generic Prism2.5 PCI device */
};
static struct pci_driver prism2_pci_driver __pci_driver = {
.type = NIC_DRIVER,
.name = "Prism2_PCI",
.probe = prism2_pci_probe,
.ids = prism2_pci_nics,
.id_count = sizeof(prism2_pci_nics)/sizeof(prism2_pci_nics[0]),
.class = 0,
};

View File

@@ -0,0 +1,42 @@
/**************************************************************************
Etherboot - BOOTP/TFTP Bootstrap Program
Prism2 NIC driver for Etherboot
Wrapper for prism2_plx
Written by Michael Brown of Fen Systems Ltd
$Id$
***************************************************************************/
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2, or (at
* your option) any later version.
*/
#define WLAN_HOSTIF WLAN_PLX
#include "prism2.c"
static struct pci_id prism2_plx_nics[] = {
PCI_ROM(0x1385, 0x4100, "ma301", "Netgear MA301"),
PCI_ROM(0x10b7, 0x7770, "3c-airconnect", "3Com AirConnect"),
PCI_ROM(0x111a, 0x1023, "ss1023", "Siemens SpeedStream SS1023"),
PCI_ROM(0x15e8, 0x0130, "correga", "Correga"),
PCI_ROM(0x1638, 0x1100, "smc2602w", "SMC EZConnect SMC2602W"), /* or Eumitcom PCI WL11000, Addtron AWA-100 */
PCI_ROM(0x16ab, 0x1100, "gl24110p", "Global Sun Tech GL24110P"),
PCI_ROM(0x16ab, 0x1101, "16ab-1101", "Unknown"),
PCI_ROM(0x16ab, 0x1102, "wdt11", "Linksys WDT11"),
PCI_ROM(0x16ec, 0x3685, "usr2415", "USR 2415"),
PCI_ROM(0xec80, 0xec00, "f5d6000", "Belkin F5D6000"),
PCI_ROM(0x126c, 0x8030, "emobility", "Nortel emobility"),
};
static struct pci_driver prism2_plx_driver __pci_driver = {
.type = NIC_DRIVER,
.name = "Prism2_PLX",
.probe = prism2_plx_probe,
.ids = prism2_plx_nics,
.id_count = sizeof(prism2_plx_nics)/sizeof(prism2_plx_nics[0]),
.class = 0,
};

854
src/drivers/net/r8169.c Normal file
View File

@@ -0,0 +1,854 @@
/**************************************************************************
* r8169.c: Etherboot device driver for the RealTek RTL-8169 Gigabit
* Written 2003 by Timothy Legge <tlegge@rogers.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Portions of this code based on:
* r8169.c: A RealTek RTL-8169 Gigabit Ethernet driver
* for Linux kernel 2.4.x.
*
* Written 2002 ShuChen <shuchen@realtek.com.tw>
* See Linux Driver for full information
*
* Linux Driver Version 1.27a, 10.02.2002
*
* Thanks to:
* Jean Chen of RealTek Semiconductor Corp. for
* providing the evaluation NIC used to develop
* this driver. RealTek's support for Etherboot
* is appreciated.
*
* REVISION HISTORY:
* ================
*
* v1.0 11-26-2003 timlegge Initial port of Linux driver
* v1.5 01-17-2004 timlegge Initial driver output cleanup
* v1.6 03-27-2004 timlegge Additional Cleanup
*
* Indent Options: indent -kr -i8
***************************************************************************/
/* to get some global routines like printf */
#include "etherboot.h"
/* to get the interface to the body of the program */
#include "nic.h"
/* to get the PCI support functions, if this is a PCI NIC */
#include "pci.h"
#include "timer.h"
#define drv_version "v1.6"
#define drv_date "03-27-2004"
typedef unsigned char u8;
typedef signed char s8;
typedef unsigned short u16;
typedef signed short s16;
typedef unsigned int u32;
typedef signed int s32;
#define HZ 1000
static u32 ioaddr;
#ifdef EDEBUG
#define dprintf(x) printf x
#else
#define dprintf(x)
#endif
/* Condensed operations for readability. */
#define virt_to_le32desc(addr) cpu_to_le32(virt_to_bus(addr))
#define le32desc_to_virt(addr) bus_to_virt(le32_to_cpu(addr))
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
/* media options
_10_Half = 0x01,
_10_Full = 0x02,
_100_Half = 0x04,
_100_Full = 0x08,
_1000_Full = 0x10,
*/
static int media = -1;
#if 0
/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
static int max_interrupt_work = 20;
#endif
#if 0
/* Maximum number of multicast addresses to filter (vs. Rx-all-multicast).
The RTL chips use a 64 element hash table based on the Ethernet CRC. */
static int multicast_filter_limit = 32;
#endif
/* MAC address length*/
#define MAC_ADDR_LEN 6
/* max supported gigabit ethernet frame size -- must be at least (dev->mtu+14+4).*/
#define MAX_ETH_FRAME_SIZE 1536
#define TX_FIFO_THRESH 256 /* In bytes */
#define RX_FIFO_THRESH 7 /* 7 means NO threshold, Rx buffer level before first PCI xfer. */
#define RX_DMA_BURST 6 /* Maximum PCI burst, '6' is 1024 */
#define TX_DMA_BURST 6 /* Maximum PCI burst, '6' is 1024 */
#define EarlyTxThld 0x3F /* 0x3F means NO early transmit */
#define RxPacketMaxSize 0x0800 /* Maximum size supported is 16K-1 */
#define InterFrameGap 0x03 /* 3 means InterFrameGap = the shortest one */
#define NUM_TX_DESC 1 /* Number of Tx descriptor registers */
#define NUM_RX_DESC 4 /* Number of Rx descriptor registers */
#define RX_BUF_SIZE 1536 /* Rx Buffer size */
#define RTL_MIN_IO_SIZE 0x80
#define TX_TIMEOUT (6*HZ)
/* write/read MMIO register */
#define RTL_W8(reg, val8) writeb ((val8), ioaddr + (reg))
#define RTL_W16(reg, val16) writew ((val16), ioaddr + (reg))
#define RTL_W32(reg, val32) writel ((val32), ioaddr + (reg))
#define RTL_R8(reg) readb (ioaddr + (reg))
#define RTL_R16(reg) readw (ioaddr + (reg))
#define RTL_R32(reg) ((unsigned long) readl (ioaddr + (reg)))
enum RTL8169_registers {
MAC0 = 0, /* Ethernet hardware address. */
MAR0 = 8, /* Multicast filter. */
TxDescStartAddr = 0x20,
TxHDescStartAddr = 0x28,
FLASH = 0x30,
ERSR = 0x36,
ChipCmd = 0x37,
TxPoll = 0x38,
IntrMask = 0x3C,
IntrStatus = 0x3E,
TxConfig = 0x40,
RxConfig = 0x44,
RxMissed = 0x4C,
Cfg9346 = 0x50,
Config0 = 0x51,
Config1 = 0x52,
Config2 = 0x53,
Config3 = 0x54,
Config4 = 0x55,
Config5 = 0x56,
MultiIntr = 0x5C,
PHYAR = 0x60,
TBICSR = 0x64,
TBI_ANAR = 0x68,
TBI_LPAR = 0x6A,
PHYstatus = 0x6C,
RxMaxSize = 0xDA,
CPlusCmd = 0xE0,
RxDescStartAddr = 0xE4,
EarlyTxThres = 0xEC,
FuncEvent = 0xF0,
FuncEventMask = 0xF4,
FuncPresetState = 0xF8,
FuncForceEvent = 0xFC,
};
enum RTL8169_register_content {
/*InterruptStatusBits */
SYSErr = 0x8000,
PCSTimeout = 0x4000,
SWInt = 0x0100,
TxDescUnavail = 0x80,
RxFIFOOver = 0x40,
RxUnderrun = 0x20,
RxOverflow = 0x10,
TxErr = 0x08,
TxOK = 0x04,
RxErr = 0x02,
RxOK = 0x01,
/*RxStatusDesc */
RxRES = 0x00200000,
RxCRC = 0x00080000,
RxRUNT = 0x00100000,
RxRWT = 0x00400000,
/*ChipCmdBits */
CmdReset = 0x10,
CmdRxEnb = 0x08,
CmdTxEnb = 0x04,
RxBufEmpty = 0x01,
/*Cfg9346Bits */
Cfg9346_Lock = 0x00,
Cfg9346_Unlock = 0xC0,
/*rx_mode_bits */
AcceptErr = 0x20,
AcceptRunt = 0x10,
AcceptBroadcast = 0x08,
AcceptMulticast = 0x04,
AcceptMyPhys = 0x02,
AcceptAllPhys = 0x01,
/*RxConfigBits */
RxCfgFIFOShift = 13,
RxCfgDMAShift = 8,
/*TxConfigBits */
TxInterFrameGapShift = 24,
TxDMAShift = 8, /* DMA burst value (0-7) is shift this many bits */
/*rtl8169_PHYstatus */
TBI_Enable = 0x80,
TxFlowCtrl = 0x40,
RxFlowCtrl = 0x20,
_1000bpsF = 0x10,
_100bps = 0x08,
_10bps = 0x04,
LinkStatus = 0x02,
FullDup = 0x01,
/*GIGABIT_PHY_registers */
PHY_CTRL_REG = 0,
PHY_STAT_REG = 1,
PHY_AUTO_NEGO_REG = 4,
PHY_1000_CTRL_REG = 9,
/*GIGABIT_PHY_REG_BIT */
PHY_Restart_Auto_Nego = 0x0200,
PHY_Enable_Auto_Nego = 0x1000,
/* PHY_STAT_REG = 1; */
PHY_Auto_Neco_Comp = 0x0020,
/* PHY_AUTO_NEGO_REG = 4; */
PHY_Cap_10_Half = 0x0020,
PHY_Cap_10_Full = 0x0040,
PHY_Cap_100_Half = 0x0080,
PHY_Cap_100_Full = 0x0100,
/* PHY_1000_CTRL_REG = 9; */
PHY_Cap_1000_Full = 0x0200,
PHY_Cap_Null = 0x0,
/*_MediaType*/
_10_Half = 0x01,
_10_Full = 0x02,
_100_Half = 0x04,
_100_Full = 0x08,
_1000_Full = 0x10,
/*_TBICSRBit*/
TBILinkOK = 0x02000000,
};
static struct {
const char *name;
u8 version; /* depend on RTL8169 docs */
u32 RxConfigMask; /* should clear the bits supported by this chip */
} rtl_chip_info[] = {
{
"RTL-8169", 0x00, 0xff7e1880,},};
enum _DescStatusBit {
OWNbit = 0x80000000,
EORbit = 0x40000000,
FSbit = 0x20000000,
LSbit = 0x10000000,
};
struct TxDesc {
u32 status;
u32 vlan_tag;
u32 buf_addr;
u32 buf_Haddr;
};
struct RxDesc {
u32 status;
u32 vlan_tag;
u32 buf_addr;
u32 buf_Haddr;
};
/* The descriptors for this card are required to be aligned on
256 byte boundaries. As the align attribute does not do more than
16 bytes of alignment it requires some extra steps. Add 256 to the
size of the array and the init_ring adjusts the alignment */
/* Define the TX Descriptor */
static u8 tx_ring[NUM_TX_DESC * sizeof(struct TxDesc) + 256];
/* Create a static buffer of size RX_BUF_SZ for each
TX Descriptor. All descriptors point to a
part of this buffer */
static unsigned char txb[NUM_TX_DESC * RX_BUF_SIZE];
/* Define the RX Descriptor */
static u8 rx_ring[NUM_RX_DESC * sizeof(struct TxDesc) + 256];
/* Create a static buffer of size RX_BUF_SZ for each
RX Descriptor All descriptors point to a
part of this buffer */
static unsigned char rxb[NUM_RX_DESC * RX_BUF_SIZE];
struct rtl8169_private {
void *mmio_addr; /* memory map physical address */
int chipset;
unsigned long cur_rx; /* Index into the Rx descriptor buffer of next Rx pkt. */
unsigned long cur_tx; /* Index into the Tx descriptor buffer of next Rx pkt. */
unsigned char *TxDescArrays; /* Index of Tx Descriptor buffer */
unsigned char *RxDescArrays; /* Index of Rx Descriptor buffer */
struct TxDesc *TxDescArray; /* Index of 256-alignment Tx Descriptor buffer */
struct RxDesc *RxDescArray; /* Index of 256-alignment Rx Descriptor buffer */
unsigned char *RxBufferRing[NUM_RX_DESC]; /* Index of Rx Buffer array */
unsigned char *Tx_skbuff[NUM_TX_DESC];
} tpx;
static struct rtl8169_private *tpc;
static const u16 rtl8169_intr_mask =
SYSErr | PCSTimeout | RxUnderrun | RxOverflow | RxFIFOOver | TxErr |
TxOK | RxErr | RxOK;
static const unsigned int rtl8169_rx_config =
(RX_FIFO_THRESH << RxCfgFIFOShift) | (RX_DMA_BURST << RxCfgDMAShift);
void mdio_write(int RegAddr, int value)
{
int i;
RTL_W32(PHYAR, 0x80000000 | (RegAddr & 0xFF) << 16 | value);
udelay(1000);
for (i = 2000; i > 0; i--) {
/* Check if the RTL8169 has completed writing to the specified MII register */
if (!(RTL_R32(PHYAR) & 0x80000000)) {
break;
} else {
udelay(100);
}
}
}
int mdio_read(int RegAddr)
{
int i, value = -1;
RTL_W32(PHYAR, 0x0 | (RegAddr & 0xFF) << 16);
udelay(1000);
for (i = 2000; i > 0; i--) {
/* Check if the RTL8169 has completed retrieving data from the specified MII register */
if (RTL_R32(PHYAR) & 0x80000000) {
value = (int) (RTL_R32(PHYAR) & 0xFFFF);
break;
} else {
udelay(100);
}
}
return value;
}
static int rtl8169_init_board(struct pci_device *pdev)
{
int i;
unsigned long rtreg_base, rtreg_len;
u32 tmp;
rtreg_base = pci_bar_start(pdev, PCI_BASE_ADDRESS_1);
rtreg_len = pci_bar_size(pdev, PCI_BASE_ADDRESS_1);
/* check for weird/broken PCI region reporting */
if (rtreg_len < RTL_MIN_IO_SIZE) {
printf("Invalid PCI region size(s), aborting\n");
}
adjust_pci_device(pdev);
/* pm_cap = pci_find_capability(pdev, PCI_CAP_ID_PM); */
/* ioremap MMIO region */
ioaddr = (unsigned long) ioremap(rtreg_base, rtreg_len);
if (ioaddr == 0)
return 0;
tpc->mmio_addr = &ioaddr;
/* Soft reset the chip. */
RTL_W8(ChipCmd, CmdReset);
/* Check that the chip has finished the reset. */
for (i = 1000; i > 0; i--)
if ((RTL_R8(ChipCmd) & CmdReset) == 0)
break;
else
udelay(10);
/* identify chip attached to board */
tmp = RTL_R32(TxConfig);
tmp = ((tmp & 0x7c000000) + ((tmp & 0x00800000) << 2)) >> 24;
for (i = ARRAY_SIZE(rtl_chip_info) - 1; i >= 0; i--)
if (tmp == rtl_chip_info[i].version) {
tpc->chipset = i;
goto match;
}
/* if unknown chip, assume array element #0, original RTL-8169 in this case */
dprintf(("PCI device: unknown chip version, assuming RTL-8169\n"));
dprintf(("PCI device: TxConfig = 0x%hX\n",
(unsigned long) RTL_R32(TxConfig)));
tpc->chipset = 0;
return 1;
match:
return 0;
}
/**************************************************************************
IRQ - Wait for a frame
***************************************************************************/
void r8169_irq ( struct nic *nic __unused, irq_action_t action ) {
int intr_status = 0;
int interested = RxUnderrun | RxOverflow | RxFIFOOver | RxErr | RxOK;
switch ( action ) {
case DISABLE:
case ENABLE:
intr_status = RTL_R16(IntrStatus);
/* h/w no longer present (hotplug?) or major error,
bail */
if (intr_status == 0xFFFF)
break;
intr_status = intr_status & ~interested;
if ( action == ENABLE )
intr_status = intr_status | interested;
RTL_W16(IntrMask, intr_status);
break;
case FORCE :
RTL_W8(TxPoll, (RTL_R8(TxPoll) | 0x01));
break;
}
}
/**************************************************************************
POLL - Wait for a frame
***************************************************************************/
static int r8169_poll(struct nic *nic, int retreive)
{
/* return true if there's an ethernet packet ready to read */
/* nic->packet should contain data on return */
/* nic->packetlen should contain length of data */
int cur_rx;
unsigned int intr_status = 0;
cur_rx = tpc->cur_rx;
if ((tpc->RxDescArray[cur_rx].status & OWNbit) == 0) {
/* There is a packet ready */
if(!retreive)
return 1;
intr_status = RTL_R16(IntrStatus);
/* h/w no longer present (hotplug?) or major error,
bail */
if (intr_status == 0xFFFF)
return 0;
RTL_W16(IntrStatus, intr_status &
~(RxFIFOOver | RxOverflow | RxOK));
if (!(tpc->RxDescArray[cur_rx].status & RxRES)) {
nic->packetlen = (int) (tpc->RxDescArray[cur_rx].
status & 0x00001FFF) - 4;
memcpy(nic->packet, tpc->RxBufferRing[cur_rx],
nic->packetlen);
if (cur_rx == NUM_RX_DESC - 1)
tpc->RxDescArray[cur_rx].status =
(OWNbit | EORbit) + RX_BUF_SIZE;
else
tpc->RxDescArray[cur_rx].status =
OWNbit + RX_BUF_SIZE;
tpc->RxDescArray[cur_rx].buf_addr =
virt_to_bus(tpc->RxBufferRing[cur_rx]);
} else
printf("Error Rx");
/* FIXME: shouldn't I reset the status on an error */
cur_rx = (cur_rx + 1) % NUM_RX_DESC;
tpc->cur_rx = cur_rx;
RTL_W16(IntrStatus, intr_status &
(RxFIFOOver | RxOverflow | RxOK));
return 1;
}
tpc->cur_rx = cur_rx;
/* FIXME: There is no reason to do this as cur_rx did not change */
return (0); /* initially as this is called to flush the input */
}
/**************************************************************************
TRANSMIT - Transmit a frame
***************************************************************************/
static void r8169_transmit(struct nic *nic, const char *d, /* Destination */
unsigned int t, /* Type */
unsigned int s, /* size */
const char *p)
{ /* Packet */
/* send the packet to destination */
u16 nstype;
u32 to;
u8 *ptxb;
int entry = tpc->cur_tx % NUM_TX_DESC;
/* point to the current txb incase multiple tx_rings are used */
ptxb = tpc->Tx_skbuff[entry * MAX_ETH_FRAME_SIZE];
memcpy(ptxb, d, ETH_ALEN);
memcpy(ptxb + ETH_ALEN, nic->node_addr, ETH_ALEN);
nstype = htons((u16) t);
memcpy(ptxb + 2 * ETH_ALEN, (u8 *) & nstype, 2);
memcpy(ptxb + ETH_HLEN, p, s);
s += ETH_HLEN;
s &= 0x0FFF;
while (s < ETH_ZLEN)
ptxb[s++] = '\0';
tpc->TxDescArray[entry].buf_addr = virt_to_bus(ptxb);
if (entry != (NUM_TX_DESC - 1))
tpc->TxDescArray[entry].status =
(OWNbit | FSbit | LSbit) | ((s > ETH_ZLEN) ? s :
ETH_ZLEN);
else
tpc->TxDescArray[entry].status =
(OWNbit | EORbit | FSbit | LSbit) | ((s > ETH_ZLEN) ? s
: ETH_ZLEN);
RTL_W8(TxPoll, 0x40); /* set polling bit */
tpc->cur_tx++;
to = currticks() + TX_TIMEOUT;
while ((tpc->TxDescArray[entry].status & OWNbit) && (currticks() < to)); /* wait */
if (currticks() >= to) {
printf("TX Time Out");
}
}
static void rtl8169_set_rx_mode(struct nic *nic __unused)
{
u32 mc_filter[2]; /* Multicast hash filter */
int rx_mode;
u32 tmp = 0;
/* IFF_ALLMULTI */
/* Too many to filter perfectly -- accept all multicasts. */
rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys;
mc_filter[1] = mc_filter[0] = 0xffffffff;
tmp =
rtl8169_rx_config | rx_mode | (RTL_R32(RxConfig) &
rtl_chip_info[tpc->chipset].
RxConfigMask);
RTL_W32(RxConfig, tmp);
RTL_W32(MAR0 + 0, mc_filter[0]);
RTL_W32(MAR0 + 4, mc_filter[1]);
}
static void rtl8169_hw_start(struct nic *nic)
{
u32 i;
/* Soft reset the chip. */
RTL_W8(ChipCmd, CmdReset);
/* Check that the chip has finished the reset. */
for (i = 1000; i > 0; i--) {
if ((RTL_R8(ChipCmd) & CmdReset) == 0)
break;
else
udelay(10);
}
RTL_W8(Cfg9346, Cfg9346_Unlock);
RTL_W8(ChipCmd, CmdTxEnb | CmdRxEnb);
RTL_W8(EarlyTxThres, EarlyTxThld);
/* For gigabit rtl8169 */
RTL_W16(RxMaxSize, RxPacketMaxSize);
/* Set Rx Config register */
i = rtl8169_rx_config | (RTL_R32(RxConfig) &
rtl_chip_info[tpc->chipset].RxConfigMask);
RTL_W32(RxConfig, i);
/* Set DMA burst size and Interframe Gap Time */
RTL_W32(TxConfig,
(TX_DMA_BURST << TxDMAShift) | (InterFrameGap <<
TxInterFrameGapShift));
tpc->cur_rx = 0;
RTL_W32(TxDescStartAddr, virt_to_le32desc(tpc->TxDescArray));
RTL_W32(RxDescStartAddr, virt_to_le32desc(tpc->RxDescArray));
RTL_W8(Cfg9346, Cfg9346_Lock);
udelay(10);
RTL_W32(RxMissed, 0);
rtl8169_set_rx_mode(nic);
/* no early-rx interrupts */
RTL_W16(MultiIntr, RTL_R16(MultiIntr) & 0xF000);
}
static void rtl8169_init_ring(struct nic *nic __unused)
{
int i;
tpc->cur_rx = 0;
tpc->cur_tx = 0;
memset(tpc->TxDescArray, 0x0, NUM_TX_DESC * sizeof(struct TxDesc));
memset(tpc->RxDescArray, 0x0, NUM_RX_DESC * sizeof(struct RxDesc));
for (i = 0; i < NUM_TX_DESC; i++) {
tpc->Tx_skbuff[i] = &txb[i];
}
for (i = 0; i < NUM_RX_DESC; i++) {
if (i == (NUM_RX_DESC - 1))
tpc->RxDescArray[i].status =
(OWNbit | EORbit) + RX_BUF_SIZE;
else
tpc->RxDescArray[i].status = OWNbit + RX_BUF_SIZE;
tpc->RxBufferRing[i] = &rxb[i * RX_BUF_SIZE];
tpc->RxDescArray[i].buf_addr =
virt_to_bus(tpc->RxBufferRing[i]);
}
}
/**************************************************************************
RESET - Finish setting up the ethernet interface
***************************************************************************/
static void r8169_reset(struct nic *nic)
{
int i;
u8 diff;
u32 TxPhyAddr, RxPhyAddr;
tpc->TxDescArrays = tx_ring;
if (tpc->TxDescArrays == 0)
printf("Allot Error");
/* Tx Desscriptor needs 256 bytes alignment; */
TxPhyAddr = virt_to_bus(tpc->TxDescArrays);
diff = 256 - (TxPhyAddr - ((TxPhyAddr >> 8) << 8));
TxPhyAddr += diff;
tpc->TxDescArray = (struct TxDesc *) (tpc->TxDescArrays + diff);
tpc->RxDescArrays = rx_ring;
/* Rx Desscriptor needs 256 bytes alignment; */
RxPhyAddr = virt_to_bus(tpc->RxDescArrays);
diff = 256 - (RxPhyAddr - ((RxPhyAddr >> 8) << 8));
RxPhyAddr += diff;
tpc->RxDescArray = (struct RxDesc *) (tpc->RxDescArrays + diff);
if (tpc->TxDescArrays == NULL || tpc->RxDescArrays == NULL) {
printf("Allocate RxDescArray or TxDescArray failed\n");
return;
}
rtl8169_init_ring(nic);
rtl8169_hw_start(nic);
/* Construct a perfect filter frame with the mac address as first match
* and broadcast for all others */
for (i = 0; i < 192; i++)
txb[i] = 0xFF;
txb[0] = nic->node_addr[0];
txb[1] = nic->node_addr[1];
txb[2] = nic->node_addr[2];
txb[3] = nic->node_addr[3];
txb[4] = nic->node_addr[4];
txb[5] = nic->node_addr[5];
}
/**************************************************************************
DISABLE - Turn off ethernet interface
***************************************************************************/
static void r8169_disable(struct dev *dev __unused)
{
int i;
/* Stop the chip's Tx and Rx DMA processes. */
RTL_W8(ChipCmd, 0x00);
/* Disable interrupts by clearing the interrupt mask. */
RTL_W16(IntrMask, 0x0000);
RTL_W32(RxMissed, 0);
tpc->TxDescArrays = NULL;
tpc->RxDescArrays = NULL;
tpc->TxDescArray = NULL;
tpc->RxDescArray = NULL;
for (i = 0; i < NUM_RX_DESC; i++) {
tpc->RxBufferRing[i] = NULL;
}
}
/**************************************************************************
PROBE - Look for an adapter, this routine's visible to the outside
***************************************************************************/
#define board_found 1
#define valid_link 0
static int r8169_probe(struct dev *dev, struct pci_device *pci)
{
struct nic *nic = (struct nic *) dev;
static int board_idx = -1;
static int printed_version = 0;
int i, rc;
int option = -1, Cap10_100 = 0, Cap1000 = 0;
printf("r8169.c: Found %s, Vendor=%hX Device=%hX\n",
pci->name, pci->vendor, pci->dev_id);
board_idx++;
printed_version = 1;
/* point to private storage */
tpc = &tpx;
rc = rtl8169_init_board(pci); /* Return code is meaningless */
/* Get MAC address. FIXME: read EEPROM */
for (i = 0; i < MAC_ADDR_LEN; i++)
nic->node_addr[i] = RTL_R8(MAC0 + i);
dprintf(("%s: Identified chip type is '%s'.\n", pci->name,
rtl_chip_info[tpc->chipset].name));
/* Print out some hardware info */
printf("%s: %! at ioaddr %hX, ", pci->name, nic->node_addr,
ioaddr);
/* if TBI is not endbled */
if (!(RTL_R8(PHYstatus) & TBI_Enable)) {
int val = mdio_read(PHY_AUTO_NEGO_REG);
option = media;
/* Force RTL8169 in 10/100/1000 Full/Half mode. */
if (option > 0) {
printf(" Force-mode Enabled.\n");
Cap10_100 = 0, Cap1000 = 0;
switch (option) {
case _10_Half:
Cap10_100 = PHY_Cap_10_Half;
Cap1000 = PHY_Cap_Null;
break;
case _10_Full:
Cap10_100 = PHY_Cap_10_Full;
Cap1000 = PHY_Cap_Null;
break;
case _100_Half:
Cap10_100 = PHY_Cap_100_Half;
Cap1000 = PHY_Cap_Null;
break;
case _100_Full:
Cap10_100 = PHY_Cap_100_Full;
Cap1000 = PHY_Cap_Null;
break;
case _1000_Full:
Cap10_100 = PHY_Cap_Null;
Cap1000 = PHY_Cap_1000_Full;
break;
default:
break;
}
/* leave PHY_AUTO_NEGO_REG bit4:0 unchanged */
mdio_write(PHY_AUTO_NEGO_REG,
Cap10_100 | (val & 0x1F));
mdio_write(PHY_1000_CTRL_REG, Cap1000);
} else {
dprintf(("Auto-negotiation Enabled.\n",
pci->name));
/* enable 10/100 Full/Half Mode, leave PHY_AUTO_NEGO_REG bit4:0 unchanged */
mdio_write(PHY_AUTO_NEGO_REG,
PHY_Cap_10_Half | PHY_Cap_10_Full |
PHY_Cap_100_Half | PHY_Cap_100_Full |
(val & 0x1F));
/* enable 1000 Full Mode */
mdio_write(PHY_1000_CTRL_REG, PHY_Cap_1000_Full);
}
/* Enable auto-negotiation and restart auto-nigotiation */
mdio_write(PHY_CTRL_REG,
PHY_Enable_Auto_Nego | PHY_Restart_Auto_Nego);
udelay(100);
/* wait for auto-negotiation process */
for (i = 10000; i > 0; i--) {
/* Check if auto-negotiation complete */
if (mdio_read(PHY_STAT_REG) & PHY_Auto_Neco_Comp) {
udelay(100);
option = RTL_R8(PHYstatus);
if (option & _1000bpsF) {
printf
("1000Mbps Full-duplex operation.\n");
} else {
printf
("%sMbps %s-duplex operation.\n",
(option & _100bps) ? "100" :
"10",
(option & FullDup) ? "Full" :
"Half");
}
break;
} else {
udelay(100);
}
} /* end for-loop to wait for auto-negotiation process */
} else {
udelay(100);
printf
("%s: 1000Mbps Full-duplex operation, TBI Link %s!\n",
pci->name,
(RTL_R32(TBICSR) & TBILinkOK) ? "OK" : "Failed");
}
r8169_reset(nic);
/* point to NIC specific routines */
dev->disable = r8169_disable;
nic->poll = r8169_poll;
nic->transmit = r8169_transmit;
nic->irqno = pci->irq;
nic->irq = r8169_irq;
nic->ioaddr = ioaddr;
return 1;
}
static struct pci_id r8169_nics[] = {
PCI_ROM(0x10ec, 0x8169, "r8169", "RealTek RTL8169 Gigabit Ethernet"),
};
static struct pci_driver r8169_driver __pci_driver = {
.type = NIC_DRIVER,
.name = "r8169/PCI",
.probe = r8169_probe,
.ids = r8169_nics,
.id_count = sizeof(r8169_nics) / sizeof(r8169_nics[0]),
.class = 0,
};

551
src/drivers/net/rtl8139.c Normal file
View File

@@ -0,0 +1,551 @@
/* rtl8139.c - etherboot driver for the Realtek 8139 chipset
ported from the linux driver written by Donald Becker
by Rainer Bawidamann (Rainer.Bawidamann@informatik.uni-ulm.de) 1999
This software may be used and distributed according to the terms
of the GNU Public License, incorporated herein by reference.
changes to the original driver:
- removed support for interrupts, switching to polling mode (yuck!)
- removed support for the 8129 chip (external MII)
*/
/*********************************************************************/
/* Revision History */
/*********************************************************************/
/*
28 Dec 2002 ken_yap@users.sourceforge.net (Ken Yap)
Put in virt_to_bus calls to allow Etherboot relocation.
06 Apr 2001 ken_yap@users.sourceforge.net (Ken Yap)
Following email from Hyun-Joon Cha, added a disable routine, otherwise
NIC remains live and can crash the kernel later.
4 Feb 2000 espenlaub@informatik.uni-ulm.de (Klaus Espenlaub)
Shuffled things around, removed the leftovers from the 8129 support
that was in the Linux driver and added a bit more 8139 definitions.
Moved the 8K receive buffer to a fixed, available address outside the
0x98000-0x9ffff range. This is a bit of a hack, but currently the only
way to make room for the Etherboot features that need substantial amounts
of code like the ANSI console support. Currently the buffer is just below
0x10000, so this even conforms to the tagged boot image specification,
which reserves the ranges 0x00000-0x10000 and 0x98000-0xA0000. My
interpretation of this "reserved" is that Etherboot may do whatever it
likes, as long as its environment is kept intact (like the BIOS
variables). Hopefully fixed rtl_poll() once and for all. The symptoms
were that if Etherboot was left at the boot menu for several minutes, the
first eth_poll failed. Seems like I am the only person who does this.
First of all I fixed the debugging code and then set out for a long bug
hunting session. It took me about a week full time work - poking around
various places in the driver, reading Don Becker's and Jeff Garzik's Linux
driver and even the FreeBSD driver (what a piece of crap!) - and
eventually spotted the nasty thing: the transmit routine was acknowledging
each and every interrupt pending, including the RxOverrun and RxFIFIOver
interrupts. This confused the RTL8139 thoroughly. It destroyed the
Rx ring contents by dumping the 2K FIFO contents right where we wanted to
get the next packet. Oh well, what fun.
18 Jan 2000 mdc@thinguin.org (Marty Connor)
Drastically simplified error handling. Basically, if any error
in transmission or reception occurs, the card is reset.
Also, pointed all transmit descriptors to the same buffer to
save buffer space. This should decrease driver size and avoid
corruption because of exceeding 32K during runtime.
28 Jul 1999 (Matthias Meixner - meixner@rbg.informatik.tu-darmstadt.de)
rtl_poll was quite broken: it used the RxOK interrupt flag instead
of the RxBufferEmpty flag which often resulted in very bad
transmission performace - below 1kBytes/s.
*/
#include "etherboot.h"
#include "nic.h"
#include "pci.h"
#include "timer.h"
#define RTL_TIMEOUT (1*TICKS_PER_SEC)
/* PCI Tuning Parameters
Threshold is bytes transferred to chip before transmission starts. */
#define TX_FIFO_THRESH 256 /* In bytes, rounded down to 32 byte units. */
#define RX_FIFO_THRESH 4 /* Rx buffer level before first PCI xfer. */
#define RX_DMA_BURST 4 /* Maximum PCI burst, '4' is 256 bytes */
#define TX_DMA_BURST 4 /* Calculate as 16<<val. */
#define NUM_TX_DESC 4 /* Number of Tx descriptor registers. */
#define TX_BUF_SIZE ETH_FRAME_LEN /* FCS is added by the chip */
#define RX_BUF_LEN_IDX 0 /* 0, 1, 2 is allowed - 8,16,32K rx buffer */
#define RX_BUF_LEN (8192 << RX_BUF_LEN_IDX)
#undef DEBUG_TX
#undef DEBUG_RX
/* Symbolic offsets to registers. */
enum RTL8139_registers {
MAC0=0, /* Ethernet hardware address. */
MAR0=8, /* Multicast filter. */
TxStatus0=0x10, /* Transmit status (four 32bit registers). */
TxAddr0=0x20, /* Tx descriptors (also four 32bit). */
RxBuf=0x30, RxEarlyCnt=0x34, RxEarlyStatus=0x36,
ChipCmd=0x37, RxBufPtr=0x38, RxBufAddr=0x3A,
IntrMask=0x3C, IntrStatus=0x3E,
TxConfig=0x40, RxConfig=0x44,
Timer=0x48, /* general-purpose counter. */
RxMissed=0x4C, /* 24 bits valid, write clears. */
Cfg9346=0x50, Config0=0x51, Config1=0x52,
TimerIntrReg=0x54, /* intr if gp counter reaches this value */
MediaStatus=0x58,
Config3=0x59,
MultiIntr=0x5C,
RevisionID=0x5E, /* revision of the RTL8139 chip */
TxSummary=0x60,
MII_BMCR=0x62, MII_BMSR=0x64, NWayAdvert=0x66, NWayLPAR=0x68,
NWayExpansion=0x6A,
DisconnectCnt=0x6C, FalseCarrierCnt=0x6E,
NWayTestReg=0x70,
RxCnt=0x72, /* packet received counter */
CSCR=0x74, /* chip status and configuration register */
PhyParm1=0x78,TwisterParm=0x7c,PhyParm2=0x80, /* undocumented */
/* from 0x84 onwards are a number of power management/wakeup frame
* definitions we will probably never need to know about. */
};
enum RxEarlyStatusBits {
ERGood=0x08, ERBad=0x04, EROVW=0x02, EROK=0x01
};
enum ChipCmdBits {
CmdReset=0x10, CmdRxEnb=0x08, CmdTxEnb=0x04, RxBufEmpty=0x01, };
enum IntrMaskBits {
SERR=0x8000, TimeOut=0x4000, LenChg=0x2000,
FOVW=0x40, PUN_LinkChg=0x20, RXOVW=0x10,
TER=0x08, TOK=0x04, RER=0x02, ROK=0x01
};
/* Interrupt register bits, using my own meaningful names. */
enum IntrStatusBits {
PCIErr=0x8000, PCSTimeout=0x4000, CableLenChange= 0x2000,
RxFIFOOver=0x40, RxUnderrun=0x20, RxOverflow=0x10,
TxErr=0x08, TxOK=0x04, RxErr=0x02, RxOK=0x01,
};
enum TxStatusBits {
TxHostOwns=0x2000, TxUnderrun=0x4000, TxStatOK=0x8000,
TxOutOfWindow=0x20000000, TxAborted=0x40000000,
TxCarrierLost=0x80000000,
};
enum RxStatusBits {
RxMulticast=0x8000, RxPhysical=0x4000, RxBroadcast=0x2000,
RxBadSymbol=0x0020, RxRunt=0x0010, RxTooLong=0x0008, RxCRCErr=0x0004,
RxBadAlign=0x0002, RxStatusOK=0x0001,
};
enum MediaStatusBits {
MSRTxFlowEnable=0x80, MSRRxFlowEnable=0x40, MSRSpeed10=0x08,
MSRLinkFail=0x04, MSRRxPauseFlag=0x02, MSRTxPauseFlag=0x01,
};
enum MIIBMCRBits {
BMCRReset=0x8000, BMCRSpeed100=0x2000, BMCRNWayEnable=0x1000,
BMCRRestartNWay=0x0200, BMCRDuplex=0x0100,
};
enum CSCRBits {
CSCR_LinkOKBit=0x0400, CSCR_LinkChangeBit=0x0800,
CSCR_LinkStatusBits=0x0f000, CSCR_LinkDownOffCmd=0x003c0,
CSCR_LinkDownCmd=0x0f3c0,
};
/* Bits in RxConfig. */
enum rx_mode_bits {
RxCfgWrap=0x80,
AcceptErr=0x20, AcceptRunt=0x10, AcceptBroadcast=0x08,
AcceptMulticast=0x04, AcceptMyPhys=0x02, AcceptAllPhys=0x01,
};
static unsigned int cur_rx,cur_tx;
/* The RTL8139 can only transmit from a contiguous, aligned memory block. */
static unsigned char tx_buffer[TX_BUF_SIZE] __attribute__((aligned(4)));
static unsigned char rx_ring[RX_BUF_LEN+16] __attribute__((aligned(4)));
static int rtl8139_probe(struct dev *dev, struct pci_device *pci);
static int read_eeprom(struct nic *nic, int location, int addr_len);
static void rtl_reset(struct nic *nic);
static void rtl_transmit(struct nic *nic, const char *destaddr,
unsigned int type, unsigned int len, const char *data);
static int rtl_poll(struct nic *nic, int retrieve);
static void rtl_disable(struct dev *);
static void rtl_irq(struct nic *nic, irq_action_t action);
static int rtl8139_probe(struct dev *dev, struct pci_device *pci)
{
struct nic *nic = (struct nic *)dev;
int i;
int speed10, fullduplex;
int addr_len;
unsigned short *ap = (unsigned short*)nic->node_addr;
/* There are enough "RTL8139" strings on the console already, so
* be brief and concentrate on the interesting pieces of info... */
printf(" - ");
/* Mask the bit that says "this is an io addr" */
nic->ioaddr = pci->ioaddr & ~3;
/* Copy IRQ from PCI information */
nic->irqno = pci->irq;
adjust_pci_device(pci);
/* Bring the chip out of low-power mode. */
outb(0x00, nic->ioaddr + Config1);
addr_len = read_eeprom(nic,0,8) == 0x8129 ? 8 : 6;
for (i = 0; i < 3; i++)
*ap++ = read_eeprom(nic,i + 7,addr_len);
speed10 = inb(nic->ioaddr + MediaStatus) & MSRSpeed10;
fullduplex = inw(nic->ioaddr + MII_BMCR) & BMCRDuplex;
printf("ioaddr %#hX, irq %d, addr %! %sMbps %s-duplex\n", nic->ioaddr,
nic->irqno, nic->node_addr, speed10 ? "10" : "100",
fullduplex ? "full" : "half");
rtl_reset(nic);
if (inb(nic->ioaddr + MediaStatus) & MSRLinkFail) {
printf("Cable not connected or other link failure\n");
return(0);
}
dev->disable = rtl_disable;
nic->poll = rtl_poll;
nic->transmit = rtl_transmit;
nic->irq = rtl_irq;
return 1;
}
/* Serial EEPROM section. */
/* EEPROM_Ctrl bits. */
#define EE_SHIFT_CLK 0x04 /* EEPROM shift clock. */
#define EE_CS 0x08 /* EEPROM chip select. */
#define EE_DATA_WRITE 0x02 /* EEPROM chip data in. */
#define EE_WRITE_0 0x00
#define EE_WRITE_1 0x02
#define EE_DATA_READ 0x01 /* EEPROM chip data out. */
#define EE_ENB (0x80 | EE_CS)
/*
Delay between EEPROM clock transitions.
No extra delay is needed with 33Mhz PCI, but 66Mhz may change this.
*/
#define eeprom_delay() inl(ee_addr)
/* The EEPROM commands include the alway-set leading bit. */
#define EE_WRITE_CMD (5)
#define EE_READ_CMD (6)
#define EE_ERASE_CMD (7)
static int read_eeprom(struct nic *nic, int location, int addr_len)
{
int i;
unsigned int retval = 0;
long ee_addr = nic->ioaddr + Cfg9346;
int read_cmd = location | (EE_READ_CMD << addr_len);
outb(EE_ENB & ~EE_CS, ee_addr);
outb(EE_ENB, ee_addr);
eeprom_delay();
/* Shift the read command bits out. */
for (i = 4 + addr_len; i >= 0; i--) {
int dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0;
outb(EE_ENB | dataval, ee_addr);
eeprom_delay();
outb(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr);
eeprom_delay();
}
outb(EE_ENB, ee_addr);
eeprom_delay();
for (i = 16; i > 0; i--) {
outb(EE_ENB | EE_SHIFT_CLK, ee_addr);
eeprom_delay();
retval = (retval << 1) | ((inb(ee_addr) & EE_DATA_READ) ? 1 : 0);
outb(EE_ENB, ee_addr);
eeprom_delay();
}
/* Terminate the EEPROM access. */
outb(~EE_CS, ee_addr);
eeprom_delay();
return retval;
}
static const unsigned int rtl8139_rx_config =
(RX_BUF_LEN_IDX << 11) |
(RX_FIFO_THRESH << 13) |
(RX_DMA_BURST << 8);
static void set_rx_mode(struct nic *nic) {
unsigned int mc_filter[2];
int rx_mode;
/* !IFF_PROMISC */
rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys;
mc_filter[1] = mc_filter[0] = 0xffffffff;
outl(rtl8139_rx_config | rx_mode, nic->ioaddr + RxConfig);
outl(mc_filter[0], nic->ioaddr + MAR0 + 0);
outl(mc_filter[1], nic->ioaddr + MAR0 + 4);
}
static void rtl_reset(struct nic* nic)
{
int i;
outb(CmdReset, nic->ioaddr + ChipCmd);
cur_rx = 0;
cur_tx = 0;
/* Give the chip 10ms to finish the reset. */
load_timer2(10*TICKS_PER_MS);
while ((inb(nic->ioaddr + ChipCmd) & CmdReset) != 0 &&
timer2_running())
/* wait */;
for (i = 0; i < ETH_ALEN; i++)
outb(nic->node_addr[i], nic->ioaddr + MAC0 + i);
/* Must enable Tx/Rx before setting transfer thresholds! */
outb(CmdRxEnb | CmdTxEnb, nic->ioaddr + ChipCmd);
outl((RX_FIFO_THRESH<<13) | (RX_BUF_LEN_IDX<<11) | (RX_DMA_BURST<<8),
nic->ioaddr + RxConfig); /* accept no frames yet! */
outl((TX_DMA_BURST<<8)|0x03000000, nic->ioaddr + TxConfig);
/* The Linux driver changes Config1 here to use a different LED pattern
* for half duplex or full/autodetect duplex (for full/autodetect, the
* outputs are TX/RX, Link10/100, FULL, while for half duplex it uses
* TX/RX, Link100, Link10). This is messy, because it doesn't match
* the inscription on the mounting bracket. It should not be changed
* from the configuration EEPROM default, because the card manufacturer
* should have set that to match the card. */
#ifdef DEBUG_RX
printf("rx ring address is %X\n",(unsigned long)rx_ring);
#endif
outl((unsigned long)virt_to_bus(rx_ring), nic->ioaddr + RxBuf);
/* If we add multicast support, the MAR0 register would have to be
* initialized to 0xffffffffffffffff (two 32 bit accesses). Etherboot
* only needs broadcast (for ARP/RARP/BOOTP/DHCP) and unicast. */
outb(CmdRxEnb | CmdTxEnb, nic->ioaddr + ChipCmd);
outl(rtl8139_rx_config, nic->ioaddr + RxConfig);
/* Start the chip's Tx and Rx process. */
outl(0, nic->ioaddr + RxMissed);
/* set_rx_mode */
set_rx_mode(nic);
/* Disable all known interrupts by setting the interrupt mask. */
outw(0, nic->ioaddr + IntrMask);
}
static void rtl_transmit(struct nic *nic, const char *destaddr,
unsigned int type, unsigned int len, const char *data)
{
unsigned int status, to, nstype;
unsigned long txstatus;
/* nstype assignment moved up here to avoid gcc 3.0.3 compiler bug */
nstype = htons(type);
memcpy(tx_buffer, destaddr, ETH_ALEN);
memcpy(tx_buffer + ETH_ALEN, nic->node_addr, ETH_ALEN);
memcpy(tx_buffer + 2 * ETH_ALEN, &nstype, 2);
memcpy(tx_buffer + ETH_HLEN, data, len);
len += ETH_HLEN;
#ifdef DEBUG_TX
printf("sending %d bytes ethtype %hX\n", len, type);
#endif
/* Note: RTL8139 doesn't auto-pad, send minimum payload (another 4
* bytes are sent automatically for the FCS, totalling to 64 bytes). */
while (len < ETH_ZLEN) {
tx_buffer[len++] = '\0';
}
outl((unsigned long)virt_to_bus(tx_buffer), nic->ioaddr + TxAddr0 + cur_tx*4);
outl(((TX_FIFO_THRESH<<11) & 0x003f0000) | len,
nic->ioaddr + TxStatus0 + cur_tx*4);
to = currticks() + RTL_TIMEOUT;
do {
status = inw(nic->ioaddr + IntrStatus);
/* Only acknlowledge interrupt sources we can properly handle
* here - the RxOverflow/RxFIFOOver MUST be handled in the
* rtl_poll() function. */
outw(status & (TxOK | TxErr | PCIErr), nic->ioaddr + IntrStatus);
if ((status & (TxOK | TxErr | PCIErr)) != 0) break;
} while (currticks() < to);
txstatus = inl(nic->ioaddr+ TxStatus0 + cur_tx*4);
if (status & TxOK) {
cur_tx = (cur_tx + 1) % NUM_TX_DESC;
#ifdef DEBUG_TX
printf("tx done (%d ticks), status %hX txstatus %X\n",
to-currticks(), status, txstatus);
#endif
} else {
#ifdef DEBUG_TX
printf("tx timeout/error (%d ticks), status %hX txstatus %X\n",
currticks()-to, status, txstatus);
#endif
rtl_reset(nic);
}
}
static int rtl_poll(struct nic *nic, int retrieve)
{
unsigned int status;
unsigned int ring_offs;
unsigned int rx_size, rx_status;
if (inb(nic->ioaddr + ChipCmd) & RxBufEmpty) {
return 0;
}
/* There is a packet ready */
if ( ! retrieve ) return 1;
status = inw(nic->ioaddr + IntrStatus);
/* See below for the rest of the interrupt acknowledges. */
outw(status & ~(RxFIFOOver | RxOverflow | RxOK), nic->ioaddr + IntrStatus);
#ifdef DEBUG_RX
printf("rtl_poll: int %hX ", status);
#endif
ring_offs = cur_rx % RX_BUF_LEN;
rx_status = *(unsigned int*)(rx_ring + ring_offs);
rx_size = rx_status >> 16;
rx_status &= 0xffff;
if ((rx_status & (RxBadSymbol|RxRunt|RxTooLong|RxCRCErr|RxBadAlign)) ||
(rx_size < ETH_ZLEN) || (rx_size > ETH_FRAME_LEN + 4)) {
printf("rx error %hX\n", rx_status);
rtl_reset(nic); /* this clears all interrupts still pending */
return 0;
}
/* Received a good packet */
nic->packetlen = rx_size - 4; /* no one cares about the FCS */
if (ring_offs+4+rx_size-4 > RX_BUF_LEN) {
int semi_count = RX_BUF_LEN - ring_offs - 4;
memcpy(nic->packet, rx_ring + ring_offs + 4, semi_count);
memcpy(nic->packet+semi_count, rx_ring, rx_size-4-semi_count);
#ifdef DEBUG_RX
printf("rx packet %d+%d bytes", semi_count,rx_size-4-semi_count);
#endif
} else {
memcpy(nic->packet, rx_ring + ring_offs + 4, nic->packetlen);
#ifdef DEBUG_RX
printf("rx packet %d bytes", rx_size-4);
#endif
}
#ifdef DEBUG_RX
printf(" at %X type %hhX%hhX rxstatus %hX\n",
(unsigned long)(rx_ring+ring_offs+4),
nic->packet[12], nic->packet[13], rx_status);
#endif
cur_rx = (cur_rx + rx_size + 4 + 3) & ~3;
outw(cur_rx - 16, nic->ioaddr + RxBufPtr);
/* See RTL8139 Programming Guide V0.1 for the official handling of
* Rx overflow situations. The document itself contains basically no
* usable information, except for a few exception handling rules. */
outw(status & (RxFIFOOver | RxOverflow | RxOK), nic->ioaddr + IntrStatus);
return 1;
}
static void rtl_irq(struct nic *nic, irq_action_t action)
{
unsigned int mask;
/* Bit of a guess as to which interrupts we should allow */
unsigned int interested = ROK | RER | RXOVW | FOVW | SERR;
switch ( action ) {
case DISABLE :
case ENABLE :
mask = inw(nic->ioaddr + IntrMask);
mask = mask & ~interested;
if ( action == ENABLE ) mask = mask | interested;
outw(mask, nic->ioaddr + IntrMask);
break;
case FORCE :
/* Apparently writing a 1 to this read-only bit of a
* read-only and otherwise unrelated register will
* force an interrupt. If you ever want to see how
* not to write a datasheet, read the one for the
* RTL8139...
*/
outb(EROK, nic->ioaddr + RxEarlyStatus);
break;
}
}
static void rtl_disable(struct dev *dev)
{
struct nic *nic = (struct nic *)dev;
/* merge reset and disable */
rtl_reset(nic);
/* reset the chip */
outb(CmdReset, nic->ioaddr + ChipCmd);
/* 10 ms timeout */
load_timer2(10*TICKS_PER_MS);
while ((inb(nic->ioaddr + ChipCmd) & CmdReset) != 0 && timer2_running())
/* wait */;
}
static struct pci_id rtl8139_nics[] = {
PCI_ROM(0x10ec, 0x8129, "rtl8129", "Realtek 8129"),
PCI_ROM(0x10ec, 0x8139, "rtl8139", "Realtek 8139"),
PCI_ROM(0x10ec, 0x8138, "rtl8139b", "Realtek 8139B"),
PCI_ROM(0x1186, 0x1300, "dfe538", "DFE530TX+/DFE538TX"),
PCI_ROM(0x1113, 0x1211, "smc1211-1", "SMC EZ10/100"),
PCI_ROM(0x1112, 0x1211, "smc1211", "SMC EZ10/100"),
PCI_ROM(0x1500, 0x1360, "delta8139", "Delta Electronics 8139"),
PCI_ROM(0x4033, 0x1360, "addtron8139", "Addtron Technology 8139"),
PCI_ROM(0x1186, 0x1340, "dfe690txd", "D-Link DFE690TXD"),
PCI_ROM(0x13d1, 0xab06, "fe2000vx", "AboCom FE2000VX"),
PCI_ROM(0x1259, 0xa117, "allied8139", "Allied Telesyn 8139"),
PCI_ROM(0x14ea, 0xab06, "fnw3603tx", "Planex FNW-3603-TX"),
PCI_ROM(0x14ea, 0xab07, "fnw3800tx", "Planex FNW-3800-TX"),
PCI_ROM(0xffff, 0x8139, "clone-rtl8139", "Cloned 8139"),
};
static struct pci_driver rtl8139_driver __pci_driver = {
.type = NIC_DRIVER,
.name = "RTL8139",
.probe = rtl8139_probe,
.ids = rtl8139_nics,
.id_count = sizeof(rtl8139_nics)/sizeof(rtl8139_nics[0]),
.class = 0,
};

1271
src/drivers/net/sis900.c Normal file

File diff suppressed because it is too large Load Diff

380
src/drivers/net/sis900.h Normal file
View File

@@ -0,0 +1,380 @@
/* -*- Mode:C; c-basic-offset:4; -*- */
/* Definitions for SiS ethernet controllers including 7014/7016 and 900
* References:
* SiS 7016 Fast Ethernet PCI Bus 10/100 Mbps LAN Controller with OnNow Support,
* preliminary Rev. 1.0 Jan. 14, 1998
* SiS 900 Fast Ethernet PCI Bus 10/100 Mbps LAN Single Chip with OnNow Support,
* preliminary Rev. 1.0 Nov. 10, 1998
* SiS 7014 Single Chip 100BASE-TX/10BASE-T Physical Layer Solution,
* preliminary Rev. 1.0 Jan. 18, 1998
* http://www.sis.com.tw/support/databook.htm
*/
/* MAC operationl registers of SiS 7016 and SiS 900 ethernet controller */
/* The I/O extent, SiS 900 needs 256 bytes of io address */
#define SIS900_TOTAL_SIZE 0x100
/* Symbolic offsets to registers. */
enum sis900_registers {
cr=0x0, /* Command Register */
cfg=0x4, /* Configuration Register */
mear=0x8, /* EEPROM Access Register */
ptscr=0xc, /* PCI Test Control Register */
isr=0x10, /* Interrupt Status Register */
imr=0x14, /* Interrupt Mask Register */
ier=0x18, /* Interrupt Enable Register */
epar=0x18, /* Enhanced PHY Access Register */
txdp=0x20, /* Transmit Descriptor Pointer Register */
txcfg=0x24, /* Transmit Configuration Register */
rxdp=0x30, /* Receive Descriptor Pointer Register */
rxcfg=0x34, /* Receive Configuration Register */
flctrl=0x38, /* Flow Control Register */
rxlen=0x3c, /* Receive Packet Length Register */
rfcr=0x48, /* Receive Filter Control Register */
rfdr=0x4C, /* Receive Filter Data Register */
pmctrl=0xB0, /* Power Management Control Register */
pmer=0xB4 /* Power Management Wake-up Event Register */
};
/* Symbolic names for bits in various registers */
enum sis900_command_register_bits {
RELOAD = 0x00000400,
ACCESSMODE = 0x00000200,
RESET = 0x00000100,
SWI = 0x00000080,
RxRESET = 0x00000020,
TxRESET = 0x00000010,
RxDIS = 0x00000008,
RxENA = 0x00000004,
TxDIS = 0x00000002,
TxENA = 0x00000001
};
enum sis900_configuration_register_bits {
DESCRFMT = 0x00000100, /* 7016 specific */
REQALG = 0x00000080,
SB = 0x00000040,
POW = 0x00000020,
EXD = 0x00000010,
PESEL = 0x00000008,
LPM = 0x00000004,
BEM = 0x00000001,
RND_CNT = 0x00000400,
FAIR_BACKOFF = 0x00000200,
EDB_MASTER_EN = 0x00002000
};
enum sis900_eeprom_access_reigster_bits {
MDC = 0x00000040,
MDDIR = 0x00000020,
MDIO = 0x00000010, /* 7016 specific */
EECS = 0x00000008,
EECLK = 0x00000004,
EEDO = 0x00000002,
EEDI = 0x00000001
};
enum sis900_interrupt_register_bits {
WKEVT = 0x10000000,
TxPAUSEEND = 0x08000000,
TxPAUSE = 0x04000000,
TxRCMP = 0x02000000,
RxRCMP = 0x01000000,
DPERR = 0x00800000,
SSERR = 0x00400000,
RMABT = 0x00200000,
RTABT = 0x00100000,
RxSOVR = 0x00010000,
HIBERR = 0x00008000,
SWINT = 0x00001000,
MIBINT = 0x00000800,
TxURN = 0x00000400,
TxIDLE = 0x00000200,
TxERR = 0x00000100,
TxDESC = 0x00000080,
TxOK = 0x00000040,
RxORN = 0x00000020,
RxIDLE = 0x00000010,
RxEARLY = 0x00000008,
RxERR = 0x00000004,
RxDESC = 0x00000002,
RxOK = 0x00000001
};
enum sis900_interrupt_enable_reigster_bits {
IE = 0x00000001
};
/* maximum dma burst fro transmission and receive*/
#define MAX_DMA_RANGE 7 /* actually 0 means MAXIMUM !! */
#define TxMXDMA_shift 20
#define RxMXDMA_shift 20
#define TX_DMA_BURST 0
#define RX_DMA_BURST 0
enum sis900_tx_rx_dma{
DMA_BURST_512 = 0, DMA_BURST_64 = 5
};
/* transmit FIFO threshholds */
#define TX_FILL_THRESH 16 /* 1/4 FIFO size */
#define TxFILLT_shift 8
#define TxDRNT_shift 0
#define TxDRNT_100 48 /* 3/4 FIFO size */
#define TxDRNT_10 16 /* 1/2 FIFO size */
enum sis900_transmit_config_register_bits {
TxCSI = 0x80000000,
TxHBI = 0x40000000,
TxMLB = 0x20000000,
TxATP = 0x10000000,
TxIFG = 0x0C000000,
TxFILLT = 0x00003F00,
TxDRNT = 0x0000003F
};
/* recevie FIFO thresholds */
#define RxDRNT_shift 1
#define RxDRNT_100 16 /* 1/2 FIFO size */
#define RxDRNT_10 24 /* 3/4 FIFO size */
enum sis900_reveive_config_register_bits {
RxAEP = 0x80000000,
RxARP = 0x40000000,
RxATX = 0x10000000,
RxAJAB = 0x08000000,
RxDRNT = 0x0000007F
};
#define RFAA_shift 28
#define RFADDR_shift 16
enum sis900_receive_filter_control_register_bits {
RFEN = 0x80000000,
RFAAB = 0x40000000,
RFAAM = 0x20000000,
RFAAP = 0x10000000,
RFPromiscuous = (RFAAB|RFAAM|RFAAP)
};
enum sis900_reveive_filter_data_mask {
RFDAT = 0x0000FFFF
};
/* EEPROM Addresses */
enum sis900_eeprom_address {
EEPROMSignature = 0x00,
EEPROMVendorID = 0x02,
EEPROMDeviceID = 0x03,
EEPROMMACAddr = 0x08,
EEPROMChecksum = 0x0b
};
/* The EEPROM commands include the alway-set leading bit. Refer to NM93Cxx datasheet */
enum sis900_eeprom_command {
EEread = 0x0180,
EEwrite = 0x0140,
EEerase = 0x01C0,
EEwriteEnable = 0x0130,
EEwriteDisable = 0x0100,
EEeraseAll = 0x0120,
EEwriteAll = 0x0110,
EEaddrMask = 0x013F,
EEcmdShift = 16
};
/* For SiS962 or SiS963, request the eeprom software access */
enum sis96x_eeprom_command {
EEREQ = 0x00000400, EEDONE = 0x00000200, EEGNT = 0x00000100
};
/* Manamgement Data I/O (mdio) frame */
#define MIIread 0x6000
#define MIIwrite 0x5002
#define MIIpmdShift 7
#define MIIregShift 2
#define MIIcmdLen 16
#define MIIcmdShift 16
/* Buffer Descriptor Status*/
enum sis900_buffer_status {
OWN = 0x80000000,
MORE = 0x40000000,
INTR = 0x20000000,
SUPCRC = 0x10000000,
INCCRC = 0x10000000,
OK = 0x08000000,
DSIZE = 0x00000FFF
};
/* Status for TX Buffers */
enum sis900_tx_buffer_status {
ABORT = 0x04000000,
UNDERRUN = 0x02000000,
NOCARRIER = 0x01000000,
DEFERD = 0x00800000,
EXCDEFER = 0x00400000,
OWCOLL = 0x00200000,
EXCCOLL = 0x00100000,
COLCNT = 0x000F0000
};
enum sis900_rx_bufer_status {
OVERRUN = 0x02000000,
DEST = 0x00800000,
BCAST = 0x01800000,
MCAST = 0x01000000,
UNIMATCH = 0x00800000,
TOOLONG = 0x00400000,
RUNT = 0x00200000,
RXISERR = 0x00100000,
CRCERR = 0x00080000,
FAERR = 0x00040000,
LOOPBK = 0x00020000,
RXCOL = 0x00010000
};
/* MII register offsets */
enum mii_registers {
MII_CONTROL = 0x0000,
MII_STATUS = 0x0001,
MII_PHY_ID0 = 0x0002,
MII_PHY_ID1 = 0x0003,
MII_ANADV = 0x0004,
MII_ANLPAR = 0x0005,
MII_ANEXT = 0x0006
};
/* mii registers specific to SiS 900 */
enum sis_mii_registers {
MII_CONFIG1 = 0x0010,
MII_CONFIG2 = 0x0011,
MII_STSOUT = 0x0012,
MII_MASK = 0x0013,
MII_RESV = 0x0014
};
/* mii registers specific to AMD 79C901 */
enum amd_mii_registers {
MII_STATUS_SUMMARY = 0x0018
};
/* mii registers specific to ICS 1893 */
enum ics_mii_registers {
MII_EXTCTRL = 0x0010, MII_QPDSTS = 0x0011, MII_10BTOP = 0x0012,
MII_EXTCTRL2 = 0x0013
};
/* MII Control register bit definitions. */
enum mii_control_register_bits {
MII_CNTL_FDX = 0x0100,
MII_CNTL_RST_AUTO = 0x0200,
MII_CNTL_ISOLATE = 0x0400,
MII_CNTL_PWRDWN = 0x0800,
MII_CNTL_AUTO = 0x1000,
MII_CNTL_SPEED = 0x2000,
MII_CNTL_LPBK = 0x4000,
MII_CNTL_RESET = 0x8000
};
/* MII Status register bit */
enum mii_status_register_bits {
MII_STAT_EXT = 0x0001,
MII_STAT_JAB = 0x0002,
MII_STAT_LINK = 0x0004,
MII_STAT_CAN_AUTO = 0x0008,
MII_STAT_FAULT = 0x0010,
MII_STAT_AUTO_DONE = 0x0020,
MII_STAT_CAN_T = 0x0800,
MII_STAT_CAN_T_FDX = 0x1000,
MII_STAT_CAN_TX = 0x2000,
MII_STAT_CAN_TX_FDX = 0x4000,
MII_STAT_CAN_T4 = 0x8000
};
#define MII_ID1_OUI_LO 0xFC00 /* low bits of OUI mask */
#define MII_ID1_MODEL 0x03F0 /* model number */
#define MII_ID1_REV 0x000F /* model number */
/* MII NWAY Register Bits ...
valid for the ANAR (Auto-Negotiation Advertisement) and
ANLPAR (Auto-Negotiation Link Partner) registers */
enum mii_nway_register_bits {
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 mii_stsout_register_bits {
MII_STSOUT_LINK_FAIL = 0x4000,
MII_STSOUT_SPD = 0x0080,
MII_STSOUT_DPLX = 0x0040
};
enum mii_stsics_register_bits {
MII_STSICS_SPD = 0x8000, MII_STSICS_DPLX = 0x4000,
MII_STSICS_LINKSTS = 0x0001
};
enum mii_stssum_register_bits {
MII_STSSUM_LINK = 0x0008,
MII_STSSUM_DPLX = 0x0004,
MII_STSSUM_AUTO = 0x0002,
MII_STSSUM_SPD = 0x0001
};
enum sis900_revision_id {
SIS630A_900_REV = 0x80, SIS630E_900_REV = 0x81,
SIS630S_900_REV = 0x82, SIS630EA1_900_REV = 0x83,
SIS630ET_900_REV = 0x84, SIS635A_900_REV = 0x90,
SIS96x_900_REV = 0X91, SIS900B_900_REV = 0x03
};
enum sis630_revision_id {
SIS630A0 = 0x00, SIS630A1 = 0x01,
SIS630B0 = 0x10, SIS630B1 = 0x11
};
#define FDX_CAPABLE_DUPLEX_UNKNOWN 0
#define FDX_CAPABLE_HALF_SELECTED 1
#define FDX_CAPABLE_FULL_SELECTED 2
#define HW_SPEED_UNCONFIG 0
#define HW_SPEED_HOME 1
#define HW_SPEED_10_MBPS 10
#define HW_SPEED_100_MBPS 100
#define HW_SPEED_DEFAULT (HW_SPEED_100_MBPS)
#define CRC_SIZE 4
#define MAC_HEADER_SIZE 14
#define TX_BUF_SIZE 1536
#define RX_BUF_SIZE 1536
#define NUM_RX_DESC 4 /* Number of Rx descriptor registers. */
typedef unsigned char u8;
typedef signed char s8;
typedef unsigned short u16;
typedef signed short s16;
typedef unsigned int u32;
typedef signed int s32;
/* Time in ticks before concluding the transmitter is hung. */
#define TX_TIMEOUT (4*TICKS_PER_SEC)
typedef struct _BufferDesc {
u32 link;
volatile u32 cmdsts;
u32 bufptr;
} BufferDesc;

View File

@@ -0,0 +1,91 @@
How I added the SIS900 card to Etherboot
Author: Marty Connor (mdc@thinguin.org)
Date: 25 Febrary 2001
Description:
This file is intended to help people who want to write an Etherboot
driver or port another driver to Etherboot. It is a starting point.
Perhaps someday I may write a more detailed description of writing an
Etherboot driver. This text should help get people started, and
studying sis900.[ch] should help show the basic structure and
techniques involved in writing and Etherboot driver.
***********************************************************************
0. Back up all the files I need to modify:
cd etherboot-4.7.20/src
cp Makefile Makefile.orig
cp config.c config.c.orig
cp pci.h pci.h.orig
cp NIC NIC.orig
cp cards.h cards.h.orig
1. Edit src/Makefile to add SIS900FLAGS to defines
SIS900FLAGS= -DINCLUDE_SIS900
2. edit src/pci.h to add PCI signatures for card
#define PCI_VENDOR_ID_SIS 0x1039
#define PCI_DEVICE_ID_SIS900 0x0900
#define PCI_DEVICE_ID_SIS7016 0x7016
3. Edit src/config.c to add the card to the card probe list
#if defined(INCLUDE_NS8390) || defined(INCLUDE_EEPRO100) ||
defined(INCLUDE_LANCE) || defined(INCLUDE_EPIC100) ||
defined(INCLUDE_TULIP) || defined(INCLUDE_OTULIP) ||
defined(INCLUDE_3C90X) || defined(INCLUDE_3C595) ||
defined(INCLUDE_RTL8139) || defined(INCLUDE_VIA_RHINE) ||
defined(INCLUDE_SIS900) || defined(INCLUDE_W89C840)
... and ...
#ifdef INCLUDE_SIS900
{ PCI_VENDOR_ID_SIS, PCI_DEVICE_ID_SIS900,
"SIS900", 0, 0, 0, 0},
{ PCI_VENDOR_ID_SIS, PCI_DEVICE_ID_SIS7016,
"SIS7016", 0, 0, 0, 0},
#endif
... and ...
#ifdef INCLUDE_SIS900
{ "SIS900", sis900_probe, pci_ioaddrs },
#endif
4. Edit NIC to add sis900 and sis7016 to NIC list
# SIS 900 and SIS 7016
sis900 sis900 0x1039,0x0900
sis7016 sis900 0x1039,0x7016
5. Edit cards.h to add sis900 probe routine declaration
#ifdef INCLUDE_SIS900
extern struct nic *sis900_probe(struct nic *, unsigned short *
PCI_ARG(struct pci_device *));
#endif
***********************************************************************
At this point, you can begin creating your driver source file. See
the "Writing an Etherboot Driver" section of the Etherboot
documentation for some hints. See the skel.c file for a starting
point. If there is a Linux driver for the card, you may be able to
use that. Copy and learn from existing Etherboot drivers (this is GPL
/ Open Source software!).
Join the etherboot-developers and etherboot-users mailing lists
(information is on http://etherboot.sourceforge.net) for information and
assistance. We invite more developers to help improve Etherboot.
Visit the http://etherboot.sourceforge.net, http://thinguin.org,
http://rom-o-matic.net, and http://ltsp.org sites for information and
assistance.
Enjoy.

1189
src/drivers/net/sk_g16.c Normal file

File diff suppressed because it is too large Load Diff

171
src/drivers/net/sk_g16.h Normal file
View File

@@ -0,0 +1,171 @@
/*-
*
* This software may be used and distributed according to the terms
* of the GNU Public License, incorporated herein by reference.
*
* Module : sk_g16.h
* Version : $Revision$
*
* Author : M.Hipp (mhipp@student.uni-tuebingen.de)
* changes by : Patrick J.D. Weichmann
*
* Date Created : 94/05/25
*
* Description : In here are all necessary definitions of
* the am7990 (LANCE) chip used for writing a
* network device driver which uses this chip
*
* $Log$
* Revision 1.1 2005/03/08 18:53:40 mcb30
* Initial revision
*
* Revision 1.1 2002/12/12 02:18:20 ebiederm
* Moved network drivers into drivers/net
*
-*/
#ifndef SK_G16_H
#define SK_G16_H
/*
* Control and Status Register 0 (CSR0) bit definitions
*
* (R=Readable) (W=Writeable) (S=Set on write) (C-Clear on write)
*
*/
#define CSR0_ERR 0x8000 /* Error summary (R) */
#define CSR0_BABL 0x4000 /* Babble transmitter timeout error (RC) */
#define CSR0_CERR 0x2000 /* Collision Error (RC) */
#define CSR0_MISS 0x1000 /* Missed packet (RC) */
#define CSR0_MERR 0x0800 /* Memory Error (RC) */
#define CSR0_RINT 0x0400 /* Receiver Interrupt (RC) */
#define CSR0_TINT 0x0200 /* Transmit Interrupt (RC) */
#define CSR0_IDON 0x0100 /* Initialization Done (RC) */
#define CSR0_INTR 0x0080 /* Interrupt Flag (R) */
#define CSR0_INEA 0x0040 /* Interrupt Enable (RW) */
#define CSR0_RXON 0x0020 /* Receiver on (R) */
#define CSR0_TXON 0x0010 /* Transmitter on (R) */
#define CSR0_TDMD 0x0008 /* Transmit Demand (RS) */
#define CSR0_STOP 0x0004 /* Stop (RS) */
#define CSR0_STRT 0x0002 /* Start (RS) */
#define CSR0_INIT 0x0001 /* Initialize (RS) */
#define CSR0_CLRALL 0x7f00 /* mask for all clearable bits */
/*
* Control and Status Register 3 (CSR3) bit definitions
*
*/
#define CSR3_BSWAP 0x0004 /* Byte Swap (RW) */
#define CSR3_ACON 0x0002 /* ALE Control (RW) */
#define CSR3_BCON 0x0001 /* Byte Control (RW) */
/*
* Initialization Block Mode operation Bit Definitions.
*/
#define MODE_PROM 0x8000 /* Promiscuous Mode */
#define MODE_INTL 0x0040 /* Internal Loopback */
#define MODE_DRTY 0x0020 /* Disable Retry */
#define MODE_COLL 0x0010 /* Force Collision */
#define MODE_DTCR 0x0008 /* Disable Transmit CRC) */
#define MODE_LOOP 0x0004 /* Loopback */
#define MODE_DTX 0x0002 /* Disable the Transmitter */
#define MODE_DRX 0x0001 /* Disable the Receiver */
#define MODE_NORMAL 0x0000 /* Normal operation mode */
/*
* Receive message descriptor status bit definitions.
*/
#define RX_OWN 0x80 /* Owner bit 0 = host, 1 = lance */
#define RX_ERR 0x40 /* Error Summary */
#define RX_FRAM 0x20 /* Framing Error */
#define RX_OFLO 0x10 /* Overflow Error */
#define RX_CRC 0x08 /* CRC Error */
#define RX_BUFF 0x04 /* Buffer Error */
#define RX_STP 0x02 /* Start of Packet */
#define RX_ENP 0x01 /* End of Packet */
/*
* Transmit message descriptor status bit definitions.
*/
#define TX_OWN 0x80 /* Owner bit 0 = host, 1 = lance */
#define TX_ERR 0x40 /* Error Summary */
#define TX_MORE 0x10 /* More the 1 retry needed to Xmit */
#define TX_ONE 0x08 /* One retry needed to Xmit */
#define TX_DEF 0x04 /* Deferred */
#define TX_STP 0x02 /* Start of Packet */
#define TX_ENP 0x01 /* End of Packet */
/*
* Transmit status (2) (valid if TX_ERR == 1)
*/
#define TX_BUFF 0x8000 /* Buffering error (no ENP) */
#define TX_UFLO 0x4000 /* Underflow (late memory) */
#define TX_LCOL 0x1000 /* Late collision */
#define TX_LCAR 0x0400 /* Loss of Carrier */
#define TX_RTRY 0x0200 /* Failed after 16 retransmissions */
#define TX_TDR 0x003f /* Time-domain-reflectometer-value */
/*
* Structures used for Communication with the LANCE
*/
/* LANCE Initialize Block */
struct init_block
{
unsigned short mode; /* Mode Register */
unsigned char paddr[6]; /* Physical Address (MAC) */
unsigned char laddr[8]; /* Logical Filter Address (not used) */
unsigned int rdrp; /* Receive Descriptor Ring pointer */
unsigned int tdrp; /* Transmit Descriptor Ring pointer */
};
/* Receive Message Descriptor Entry */
struct rmd
{
union rmd_u
{
unsigned long buffer; /* Address of buffer */
struct rmd_s
{
unsigned char unused[3];
unsigned volatile char status; /* Status Bits */
} s;
} u;
volatile short blen; /* Buffer Length (two's complement) */
unsigned short mlen; /* Message Byte Count */
};
/* Transmit Message Descriptor Entry */
struct tmd
{
union tmd_u
{
unsigned long buffer; /* Address of buffer */
struct tmd_s
{
unsigned char unused[3];
unsigned volatile char status; /* Status Bits */
} s;
} u;
unsigned short blen; /* Buffer Length (two's complement) */
unsigned volatile short status2; /* Error Status Bits */
};
#endif /* End of SK_G16_H */

200
src/drivers/net/skel.c Normal file
View File

@@ -0,0 +1,200 @@
/**************************************************************************
Etherboot - BOOTP/TFTP Bootstrap Program
Skeleton NIC driver for Etherboot
***************************************************************************/
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2, or (at
* your option) any later version.
*/
/* to get some global routines like printf */
#include "etherboot.h"
/* to get the interface to the body of the program */
#include "nic.h"
/* to get the PCI support functions, if this is a PCI NIC */
#include "pci.h"
/* to get the ISA support functions, if this is an ISA NIC */
#include "isa.h"
/* NIC specific static variables go here */
/**************************************************************************
POLL - Wait for a frame
***************************************************************************/
static int skel_poll(struct nic *nic, int retrieve)
{
/* Work out whether or not there's an ethernet packet ready to
* read. Return 0 if not.
*/
/*
if ( ! <packet_ready> ) return 0;
*/
/* retrieve==0 indicates that we are just checking for the
* presence of a packet but don't want to read it just yet.
*/
/*
if ( ! retrieve ) return 1;
*/
/* Copy data to nic->packet. Data should include the
* link-layer header (dest MAC, source MAC, type).
* Store length of data in nic->packetlen.
* Return true to indicate a packet has been read.
*/
/*
nic->packetlen = <packet_length>;
memcpy ( nic->packet, <packet_data>, <packet_length> );
return 1;
*/
return 0; /* Remove this line once this method is implemented */
}
/**************************************************************************
TRANSMIT - Transmit a frame
***************************************************************************/
static void skel_transmit(
struct nic *nic,
const char *dest, /* Destination */
unsigned int type, /* Type */
unsigned int size, /* size */
const char *packet) /* Packet */
{
/* Transmit packet to dest MAC address. You will need to
* construct the link-layer header (dest MAC, source MAC,
* type).
*/
/*
unsigned int nstype = htons ( type );
memcpy ( <tx_buffer>, dest, ETH_ALEN );
memcpy ( <tx_buffer> + ETH_ALEN, nic->node_addr, ETH_ALEN );
memcpy ( <tx_buffer> + 2 * ETH_ALEN, &nstype, 2 );
memcpy ( <tx_buffer> + ETH_HLEN, data, size );
<transmit_data> ( <tx_buffer>, size + ETH_HLEN );
*/
}
/**************************************************************************
DISABLE - Turn off ethernet interface
***************************************************************************/
static void skel_disable(struct dev *dev)
{
/* put the card in its initial state */
/* This function serves 3 purposes.
* This disables DMA and interrupts so we don't receive
* unexpected packets or interrupts from the card after
* etherboot has finished.
* This frees resources so etherboot may use
* this driver on another interface
* This allows etherboot to reinitialize the interface
* if something is something goes wrong.
*/
}
/**************************************************************************
IRQ - handle interrupts
***************************************************************************/
static void skel_irq(struct nic *nic, irq_action_t action)
{
/* This routine is somewhat optional. Etherboot itself
* doesn't use interrupts, but they are required under some
* circumstances when we're acting as a PXE stack.
*
* If you don't implement this routine, the only effect will
* be that your driver cannot be used via Etherboot's UNDI
* API. This won't affect programs that use only the UDP
* portion of the PXE API, such as pxelinux.
*/
switch ( action ) {
case DISABLE :
case ENABLE :
/* Set receive interrupt enabled/disabled state */
/*
outb ( action == ENABLE ? IntrMaskEnabled : IntrMaskDisabled,
nic->ioaddr + IntrMaskRegister );
*/
break;
case FORCE :
/* Force NIC to generate a receive interrupt */
/*
outb ( ForceInterrupt, nic->ioaddr + IntrForceRegister );
*/
break;
}
}
/**************************************************************************
PROBE - Look for an adapter, this routine's visible to the outside
***************************************************************************/
#define board_found 1
#define valid_link 0
static int skel_probe(struct dev *dev, struct pci_device *pci)
{
struct nic *nic = (struct nic *)dev;
if (board_found && valid_link)
{
/* store NIC parameters */
nic->ioaddr = pci->ioaddr & ~3;
nic->irqno = pci->irq;
/* point to NIC specific routines */
dev->disable = skel_disable;
nic->poll = skel_poll;
nic->transmit = skel_transmit;
nic->irq = skel_irq;
return 1;
}
/* else */
return 0;
}
static struct pci_id skel_nics[] = {
PCI_ROM(0x0000, 0x0000, "skel-pci", "Skeleton PCI Adaptor"),
};
static struct pci_driver skel_driver __pci_driver = {
.type = NIC_DRIVER,
.name = "SKELETON/PCI",
.probe = skel_probe,
.ids = skel_nics,
.id_count = sizeof(skel_nics)/sizeof(skel_nics[0]),
.class = 0,
};
/**************************************************************************
PROBE - Look for an adapter, this routine's visible to the outside
***************************************************************************/
static int skel_isa_probe(struct dev *dev, unsigned short *probe_addrs)
{
struct nic *nic = (struct nic *)dev;
/* if probe_addrs is 0, then routine can use a hardwired default */
if (board_found && valid_link)
{
/* point to NIC specific routines */
dev->disable = skel_disable;
nic->poll = skel_poll;
nic->transmit = skel_transmit;
/* Report the ISA pnp id of the board */
dev->devid.vendor_id = htons(GENERIC_ISAPNP_VENDOR);
dev->devid.vendor_id = htons(0x1234);
return 1;
}
/* else */
return 0;
}
ISA_ROM("skel-isa", "Skeleton ISA driver")
static struct isa_driver skel_isa_driver __isa_driver = {
.type = NIC_DRIVER,
.name = "SKELETON/ISA",
.probe = skel_isa_probe,
.ioaddrs = 0,
};

544
src/drivers/net/smc9000.c Normal file
View File

@@ -0,0 +1,544 @@
#ifdef ALLMULTI
#error multicast support is not yet implemented
#endif
/*------------------------------------------------------------------------
* smc9000.c
* This is a Etherboot driver for SMC's 9000 series of Ethernet cards.
*
* Copyright (C) 1998 Daniel Engström <daniel.engstrom@riksnett.no>
* Based on the Linux SMC9000 driver, smc9194.c by Eric Stahlman
* Copyright (C) 1996 by Erik Stahlman <eric@vt.edu>
*
* This software may be used and distributed according to the terms
* of the GNU Public License, incorporated herein by reference.
*
* "Features" of the SMC chip:
* 4608 byte packet memory. ( for the 91C92/4. Others have more )
* EEPROM for configuration
* AUI/TP selection
*
* Authors
* Erik Stahlman <erik@vt.edu>
* Daniel Engström <daniel.engstrom@riksnett.no>
*
* History
* 98-09-25 Daniel Engström Etherboot driver crated from Eric's
* Linux driver.
*
*---------------------------------------------------------------------------*/
#define LINUX_OUT_MACROS 1
#define SMC9000_VERBOSE 1
#define SMC9000_DEBUG 0
#include "etherboot.h"
#include "nic.h"
#include "isa.h"
#include "smc9000.h"
# define _outb outb
# define _outw outw
static const char smc9000_version[] = "Version 0.99 98-09-30";
static unsigned int smc9000_base=0;
static const char *interfaces[ 2 ] = { "TP", "AUI" };
static const char *chip_ids[ 15 ] = {
NULL, NULL, NULL,
/* 3 */ "SMC91C90/91C92",
/* 4 */ "SMC91C94",
/* 5 */ "SMC91C95",
NULL,
/* 7 */ "SMC91C100",
/* 8 */ "SMC91C100FD",
NULL, NULL, NULL,
NULL, NULL, NULL
};
static const char smc91c96_id[] = "SMC91C96";
/*
* Function: smc_reset( int ioaddr )
* Purpose:
* This sets the SMC91xx chip to its normal state, hopefully from whatever
* mess that any other DOS driver has put it in.
*
* Maybe I should reset more registers to defaults in here? SOFTRESET should
* do that for me.
*
* Method:
* 1. send a SOFT RESET
* 2. wait for it to finish
* 3. reset the memory management unit
* 4. clear all interrupts
*
*/
static void smc_reset(int ioaddr)
{
/* This resets the registers mostly to defaults, but doesn't
* affect EEPROM. That seems unnecessary */
SMC_SELECT_BANK(ioaddr, 0);
_outw( RCR_SOFTRESET, ioaddr + RCR );
/* this should pause enough for the chip to be happy */
SMC_DELAY(ioaddr);
/* Set the transmit and receive configuration registers to
* default values */
_outw(RCR_CLEAR, ioaddr + RCR);
_outw(TCR_CLEAR, ioaddr + TCR);
/* Reset the MMU */
SMC_SELECT_BANK(ioaddr, 2);
_outw( MC_RESET, ioaddr + MMU_CMD );
/* Note: It doesn't seem that waiting for the MMU busy is needed here,
* but this is a place where future chipsets _COULD_ break. Be wary
* of issuing another MMU command right after this */
_outb(0, ioaddr + INT_MASK);
}
/*----------------------------------------------------------------------
* Function: smc_probe( int ioaddr )
*
* Purpose:
* Tests to see if a given ioaddr points to an SMC9xxx chip.
* Returns a 0 on success
*
* Algorithm:
* (1) see if the high byte of BANK_SELECT is 0x33
* (2) compare the ioaddr with the base register's address
* (3) see if I recognize the chip ID in the appropriate register
*
* ---------------------------------------------------------------------
*/
static int smc_probe( int ioaddr )
{
word bank;
word revision_register;
word base_address_register;
/* First, see if the high byte is 0x33 */
bank = inw(ioaddr + BANK_SELECT);
if ((bank & 0xFF00) != 0x3300) {
return -1;
}
/* The above MIGHT indicate a device, but I need to write to further
* test this. */
_outw(0x0, ioaddr + BANK_SELECT);
bank = inw(ioaddr + BANK_SELECT);
if ((bank & 0xFF00) != 0x3300) {
return -1;
}
/* well, we've already written once, so hopefully another time won't
* hurt. This time, I need to switch the bank register to bank 1,
* so I can access the base address register */
SMC_SELECT_BANK(ioaddr, 1);
base_address_register = inw(ioaddr + BASE);
if (ioaddr != (base_address_register >> 3 & 0x3E0)) {
#ifdef SMC9000_VERBOSE
printf("SMC9000: IOADDR %hX doesn't match configuration (%hX)."
"Probably not a SMC chip\n",
ioaddr, base_address_register >> 3 & 0x3E0);
#endif
/* well, the base address register didn't match. Must not have
* been a SMC chip after all. */
return -1;
}
/* check if the revision register is something that I recognize.
* These might need to be added to later, as future revisions
* could be added. */
SMC_SELECT_BANK(ioaddr, 3);
revision_register = inw(ioaddr + REVISION);
if (!chip_ids[(revision_register >> 4) & 0xF]) {
/* I don't recognize this chip, so... */
#ifdef SMC9000_VERBOSE
printf("SMC9000: IO %hX: Unrecognized revision register:"
" %hX, Contact author.\n", ioaddr, revision_register);
#endif
return -1;
}
/* at this point I'll assume that the chip is an SMC9xxx.
* It might be prudent to check a listing of MAC addresses
* against the hardware address, or do some other tests. */
return 0;
}
/**************************************************************************
* ETH_TRANSMIT - Transmit a frame
***************************************************************************/
static void smc9000_transmit(
struct nic *nic,
const char *d, /* Destination */
unsigned int t, /* Type */
unsigned int s, /* size */
const char *p) /* Packet */
{
word length; /* real, length incl. header */
word numPages;
unsigned long time_out;
byte packet_no;
word status;
int i;
/* We dont pad here since we can have the hardware doing it for us */
length = (s + ETH_HLEN + 1)&~1;
/* convert to MMU pages */
numPages = length / 256;
if (numPages > 7 ) {
#ifdef SMC9000_VERBOSE
printf("SMC9000: Far too big packet error. \n");
#endif
return;
}
/* dont try more than, say 30 times */
for (i=0;i<30;i++) {
/* now, try to allocate the memory */
SMC_SELECT_BANK(smc9000_base, 2);
_outw(MC_ALLOC | numPages, smc9000_base + MMU_CMD);
status = 0;
/* wait for the memory allocation to finnish */
for (time_out = currticks() + 5*TICKS_PER_SEC; currticks() < time_out; ) {
status = inb(smc9000_base + INTERRUPT);
if ( status & IM_ALLOC_INT ) {
/* acknowledge the interrupt */
_outb(IM_ALLOC_INT, smc9000_base + INTERRUPT);
break;
}
}
if ((status & IM_ALLOC_INT) != 0 ) {
/* We've got the memory */
break;
} else {
printf("SMC9000: Memory allocation timed out, resetting MMU.\n");
_outw(MC_RESET, smc9000_base + MMU_CMD);
}
}
/* If I get here, I _know_ there is a packet slot waiting for me */
packet_no = inb(smc9000_base + PNR_ARR + 1);
if (packet_no & 0x80) {
/* or isn't there? BAD CHIP! */
printf("SMC9000: Memory allocation failed. \n");
return;
}
/* we have a packet address, so tell the card to use it */
_outb(packet_no, smc9000_base + PNR_ARR);
/* point to the beginning of the packet */
_outw(PTR_AUTOINC, smc9000_base + POINTER);
#if SMC9000_DEBUG > 2
printf("Trying to xmit packet of length %hX\n", length );
#endif
/* send the packet length ( +6 for status, length and ctl byte )
* and the status word ( set to zeros ) */
_outw(0, smc9000_base + DATA_1 );
/* send the packet length ( +6 for status words, length, and ctl) */
_outb((length+6) & 0xFF, smc9000_base + DATA_1);
_outb((length+6) >> 8 , smc9000_base + DATA_1);
/* Write the contents of the packet */
/* The ethernet header first... */
outsw(smc9000_base + DATA_1, d, ETH_ALEN >> 1);
outsw(smc9000_base + DATA_1, nic->node_addr, ETH_ALEN >> 1);
_outw(htons(t), smc9000_base + DATA_1);
/* ... the data ... */
outsw(smc9000_base + DATA_1 , p, s >> 1);
/* ... and the last byte, if there is one. */
if ((s & 1) == 0) {
_outw(0, smc9000_base + DATA_1);
} else {
_outb(p[s-1], smc9000_base + DATA_1);
_outb(0x20, smc9000_base + DATA_1);
}
/* and let the chipset deal with it */
_outw(MC_ENQUEUE , smc9000_base + MMU_CMD);
status = 0; time_out = currticks() + 5*TICKS_PER_SEC;
do {
status = inb(smc9000_base + INTERRUPT);
if ((status & IM_TX_INT ) != 0) {
word tx_status;
/* ack interrupt */
_outb(IM_TX_INT, smc9000_base + INTERRUPT);
packet_no = inw(smc9000_base + FIFO_PORTS);
packet_no &= 0x7F;
/* select this as the packet to read from */
_outb( packet_no, smc9000_base + PNR_ARR );
/* read the first word from this packet */
_outw( PTR_AUTOINC | PTR_READ, smc9000_base + POINTER );
tx_status = inw( smc9000_base + DATA_1 );
if (0 == (tx_status & TS_SUCCESS)) {
#ifdef SMC9000_VERBOSE
printf("SMC9000: TX FAIL STATUS: %hX \n", tx_status);
#endif
/* re-enable transmit */
SMC_SELECT_BANK(smc9000_base, 0);
_outw(inw(smc9000_base + TCR ) | TCR_ENABLE, smc9000_base + TCR );
}
/* kill the packet */
SMC_SELECT_BANK(smc9000_base, 2);
_outw(MC_FREEPKT, smc9000_base + MMU_CMD);
return;
}
}while(currticks() < time_out);
printf("SMC9000: Waring TX timed out, resetting board\n");
smc_reset(smc9000_base);
return;
}
/**************************************************************************
* ETH_POLL - Wait for a frame
***************************************************************************/
static int smc9000_poll(struct nic *nic, int retrieve)
{
if(!smc9000_base)
return 0;
SMC_SELECT_BANK(smc9000_base, 2);
if (inw(smc9000_base + FIFO_PORTS) & FP_RXEMPTY)
return 0;
if ( ! retrieve ) return 1;
/* start reading from the start of the packet */
_outw(PTR_READ | PTR_RCV | PTR_AUTOINC, smc9000_base + POINTER);
/* First read the status and check that we're ok */
if (!(inw(smc9000_base + DATA_1) & RS_ERRORS)) {
/* Next: read the packet length and mask off the top bits */
nic->packetlen = (inw(smc9000_base + DATA_1) & 0x07ff);
/* the packet length includes the 3 extra words */
nic->packetlen -= 6;
#if SMC9000_DEBUG > 2
printf(" Reading %d words (and %d byte(s))\n",
(nic->packetlen >> 1), nic->packetlen & 1);
#endif
/* read the packet (and the last "extra" word) */
insw(smc9000_base + DATA_1, nic->packet, (nic->packetlen+2) >> 1);
/* is there an odd last byte ? */
if (nic->packet[nic->packetlen+1] & 0x20)
nic->packetlen++;
/* error or good, tell the card to get rid of this packet */
_outw(MC_RELEASE, smc9000_base + MMU_CMD);
return 1;
}
printf("SMC9000: RX error\n");
/* error or good, tell the card to get rid of this packet */
_outw(MC_RELEASE, smc9000_base + MMU_CMD);
return 0;
}
static void smc9000_disable(struct dev *dev __unused)
{
if(!smc9000_base)
return;
smc_reset(smc9000_base);
/* no more interrupts for me */
SMC_SELECT_BANK(smc9000_base, 2);
_outb( 0, smc9000_base + INT_MASK);
/* and tell the card to stay away from that nasty outside world */
SMC_SELECT_BANK(smc9000_base, 0);
_outb( RCR_CLEAR, smc9000_base + RCR );
_outb( TCR_CLEAR, smc9000_base + TCR );
}
static void smc9000_irq(struct nic *nic __unused, irq_action_t action __unused)
{
switch ( action ) {
case DISABLE :
break;
case ENABLE :
break;
case FORCE :
break;
}
}
/**************************************************************************
* ETH_PROBE - Look for an adapter
***************************************************************************/
static int smc9000_probe(struct dev *dev, unsigned short *probe_addrs)
{
struct nic *nic = (struct nic *)dev;
unsigned short revision;
int memory;
int media;
const char * version_string;
const char * if_string;
int i;
/*
* the SMC9000 can be at any of the following port addresses. To change,
* for a slightly different card, you can add it to the array. Keep in
* mind that the array must end in zero.
*/
static unsigned short portlist[] = {
#ifdef SMC9000_SCAN
SMC9000_SCAN,
#else
0x200, 0x220, 0x240, 0x260, 0x280, 0x2A0, 0x2C0, 0x2E0,
0x300, 0x320, 0x340, 0x360, 0x380, 0x3A0, 0x3C0, 0x3E0,
#endif
0 };
/* if no addresses supplied, fall back on defaults */
if (probe_addrs == 0 || probe_addrs[0] == 0)
probe_addrs = portlist;
/* check every ethernet address */
for (i = 0; probe_addrs[i]; i++) {
/* check this specific address */
if (smc_probe(probe_addrs[i]) == 0)
smc9000_base = probe_addrs[i];
}
/* couldn't find anything */
if(0 == smc9000_base)
goto out;
nic->irqno = 0;
nic->ioaddr = smc9000_base;
/*
* Get the MAC address ( bank 1, regs 4 - 9 )
*/
SMC_SELECT_BANK(smc9000_base, 1);
for ( i = 0; i < 6; i += 2 ) {
word address;
address = inw(smc9000_base + ADDR0 + i);
nic->node_addr[i+1] = address >> 8;
nic->node_addr[i] = address & 0xFF;
}
/* get the memory information */
SMC_SELECT_BANK(smc9000_base, 0);
memory = ( inw(smc9000_base + MCR) >> 9 ) & 0x7; /* multiplier */
memory *= 256 * (inw(smc9000_base + MIR) & 0xFF);
/*
* Now, I want to find out more about the chip. This is sort of
* redundant, but it's cleaner to have it in both, rather than having
* one VERY long probe procedure.
*/
SMC_SELECT_BANK(smc9000_base, 3);
revision = inw(smc9000_base + REVISION);
version_string = chip_ids[(revision >> 4) & 0xF];
if (((revision & 0xF0) >> 4 == CHIP_9196) &&
((revision & 0x0F) >= REV_9196)) {
/* This is a 91c96. 'c96 has the same chip id as 'c94 (4) but
* a revision starting at 6 */
version_string = smc91c96_id;
}
if ( !version_string ) {
/* I shouldn't get here because this call was done before.... */
goto out;
}
/* is it using AUI or 10BaseT ? */
SMC_SELECT_BANK(smc9000_base, 1);
if (inw(smc9000_base + CONFIG) & CFG_AUI_SELECT)
media = 2;
else
media = 1;
if_string = interfaces[media - 1];
/* now, reset the chip, and put it into a known state */
smc_reset(smc9000_base);
printf("SMC9000 %s\n", smc9000_version);
#ifdef SMC9000_VERBOSE
printf("Copyright (C) 1998 Daniel Engstr\x94m\n");
printf("Copyright (C) 1996 Eric Stahlman\n");
#endif
printf("%s rev:%d I/O port:%hX Interface:%s RAM:%d bytes \n",
version_string, revision & 0xF,
smc9000_base, if_string, memory );
/*
* Print the Ethernet address
*/
printf("Ethernet MAC address: %!\n", nic->node_addr);
SMC_SELECT_BANK(smc9000_base, 0);
/* see the header file for options in TCR/RCR NORMAL*/
_outw(TCR_NORMAL, smc9000_base + TCR);
_outw(RCR_NORMAL, smc9000_base + RCR);
/* Select which interface to use */
SMC_SELECT_BANK(smc9000_base, 1);
if ( media == 1 ) {
_outw( inw( smc9000_base + CONFIG ) & ~CFG_AUI_SELECT,
smc9000_base + CONFIG );
}
else if ( media == 2 ) {
_outw( inw( smc9000_base + CONFIG ) | CFG_AUI_SELECT,
smc9000_base + CONFIG );
}
dev->disable = smc9000_disable;
nic->poll = smc9000_poll;
nic->transmit = smc9000_transmit;
nic->irq = smc9000_irq;
/* Based on PnP ISA map */
dev->devid.vendor_id = htons(GENERIC_ISAPNP_VENDOR);
dev->devid.device_id = htons(0x8228);
return 1;
out:
#ifdef SMC9000_VERBOSE
/* printf("No SMC9000 adapters found\n"); */
#endif
smc9000_base = 0;
return (0);
}
static struct isa_driver smc9000_driver __isa_driver = {
.type = NIC_DRIVER,
.name = "SMC9000",
.probe = smc9000_probe,
.ioaddrs = 0,
};

205
src/drivers/net/smc9000.h Normal file
View File

@@ -0,0 +1,205 @@
/*------------------------------------------------------------------------
* smc9000.h
*
* Copyright (C) 1998 by Daniel Engström
* Copyright (C) 1996 by Erik Stahlman
*
* This software may be used and distributed according to the terms
* of the GNU Public License, incorporated herein by reference.
*
* This file contains register information and access macros for
* the SMC91xxx chipset.
*
* Information contained in this file was obtained from the SMC91C94
* manual from SMC. To get a copy, if you really want one, you can find
* information under www.smsc.com in the components division.
* ( this thanks to advice from Donald Becker ).
*
* Authors
* Daniel Engström <daniel.engstrom@riksnett.no>
* Erik Stahlman <erik@vt.edu>
*
* History
* 96-01-06 Erik Stahlman moved definitions here from main .c
* file
* 96-01-19 Erik Stahlman polished this up some, and added
* better error handling
* 98-09-25 Daniel Engström adjusted for Etherboot
* 98-09-27 Daniel Engström moved some static strings back to the
* main .c file
* --------------------------------------------------------------------------*/
#ifndef _SMC9000_H_
# define _SMC9000_H_
/* I want some simple types */
typedef unsigned char byte;
typedef unsigned short word;
typedef unsigned long int dword;
/*---------------------------------------------------------------
*
* A description of the SMC registers is probably in order here,
* although for details, the SMC datasheet is invaluable.
*
* Basically, the chip has 4 banks of registers ( 0 to 3 ), which
* are accessed by writing a number into the BANK_SELECT register
* ( I also use a SMC_SELECT_BANK macro for this ).
*
* The banks are configured so that for most purposes, bank 2 is all
* that is needed for simple run time tasks.
* ----------------------------------------------------------------------*/
/*
* Bank Select Register:
*
* yyyy yyyy 0000 00xx
* xx = bank number
* yyyy yyyy = 0x33, for identification purposes.
*/
#define BANK_SELECT 14
/* BANK 0 */
#define TCR 0 /* transmit control register */
#define TCR_ENABLE 0x0001 /* if this is 1, we can transmit */
#define TCR_FDUPLX 0x0800 /* receive packets sent out */
#define TCR_STP_SQET 0x1000 /* stop transmitting if Signal quality error */
#define TCR_MON_CNS 0x0400 /* monitors the carrier status */
#define TCR_PAD_ENABLE 0x0080 /* pads short packets to 64 bytes */
#define TCR_CLEAR 0 /* do NOTHING */
/* the normal settings for the TCR register : */
#define TCR_NORMAL (TCR_ENABLE | TCR_PAD_ENABLE)
#define EPH_STATUS 2
#define ES_LINK_OK 0x4000 /* is the link integrity ok ? */
#define RCR 4
#define RCR_SOFTRESET 0x8000 /* resets the chip */
#define RCR_STRIP_CRC 0x200 /* strips CRC */
#define RCR_ENABLE 0x100 /* IFF this is set, we can receive packets */
#define RCR_ALMUL 0x4 /* receive all multicast packets */
#define RCR_PROMISC 0x2 /* enable promiscuous mode */
/* the normal settings for the RCR register : */
#define RCR_NORMAL (RCR_STRIP_CRC | RCR_ENABLE)
#define RCR_CLEAR 0x0 /* set it to a base state */
#define COUNTER 6
#define MIR 8
#define MCR 10
/* 12 is reserved */
/* BANK 1 */
#define CONFIG 0
#define CFG_AUI_SELECT 0x100
#define BASE 2
#define ADDR0 4
#define ADDR1 6
#define ADDR2 8
#define GENERAL 10
#define CONTROL 12
#define CTL_POWERDOWN 0x2000
#define CTL_LE_ENABLE 0x80
#define CTL_CR_ENABLE 0x40
#define CTL_TE_ENABLE 0x0020
#define CTL_AUTO_RELEASE 0x0800
#define CTL_EPROM_ACCESS 0x0003 /* high if Eprom is being read */
/* BANK 2 */
#define MMU_CMD 0
#define MC_BUSY 1 /* only readable bit in the register */
#define MC_NOP 0
#define MC_ALLOC 0x20 /* or with number of 256 byte packets */
#define MC_RESET 0x40
#define MC_REMOVE 0x60 /* remove the current rx packet */
#define MC_RELEASE 0x80 /* remove and release the current rx packet */
#define MC_FREEPKT 0xA0 /* Release packet in PNR register */
#define MC_ENQUEUE 0xC0 /* Enqueue the packet for transmit */
#define PNR_ARR 2
#define FIFO_PORTS 4
#define FP_RXEMPTY 0x8000
#define FP_TXEMPTY 0x80
#define POINTER 6
#define PTR_READ 0x2000
#define PTR_RCV 0x8000
#define PTR_AUTOINC 0x4000
#define PTR_AUTO_INC 0x0040
#define DATA_1 8
#define DATA_2 10
#define INTERRUPT 12
#define INT_MASK 13
#define IM_RCV_INT 0x1
#define IM_TX_INT 0x2
#define IM_TX_EMPTY_INT 0x4
#define IM_ALLOC_INT 0x8
#define IM_RX_OVRN_INT 0x10
#define IM_EPH_INT 0x20
#define IM_ERCV_INT 0x40 /* not on SMC9192 */
/* BANK 3 */
#define MULTICAST1 0
#define MULTICAST2 2
#define MULTICAST3 4
#define MULTICAST4 6
#define MGMT 8
#define REVISION 10 /* ( hi: chip id low: rev # ) */
/* this is NOT on SMC9192 */
#define ERCV 12
/* Note that 9194 and 9196 have the smame chip id,
* the 9196 will have revisions starting at 6 */
#define CHIP_9190 3
#define CHIP_9194 4
#define CHIP_9195 5
#define CHIP_9196 4
#define CHIP_91100 7
#define CHIP_91100FD 8
#define REV_9196 6
/*
* Transmit status bits
*/
#define TS_SUCCESS 0x0001
#define TS_LOSTCAR 0x0400
#define TS_LATCOL 0x0200
#define TS_16COL 0x0010
/*
* Receive status bits
*/
#define RS_ALGNERR 0x8000
#define RS_BADCRC 0x2000
#define RS_ODDFRAME 0x1000
#define RS_TOOLONG 0x0800
#define RS_TOOSHORT 0x0400
#define RS_MULTICAST 0x0001
#define RS_ERRORS (RS_ALGNERR | RS_BADCRC | RS_TOOLONG | RS_TOOSHORT)
/*-------------------------------------------------------------------------
* I define some macros to make it easier to do somewhat common
* or slightly complicated, repeated tasks.
--------------------------------------------------------------------------*/
/* select a register bank, 0 to 3 */
#define SMC_SELECT_BANK(x, y) { _outw( y, x + BANK_SELECT ); }
/* define a small delay for the reset */
#define SMC_DELAY(x) { inw( x + RCR );\
inw( x + RCR );\
inw( x + RCR ); }
#endif /* _SMC_9000_H_ */

891
src/drivers/net/sundance.c Normal file
View File

@@ -0,0 +1,891 @@
/**************************************************************************
*
* sundance.c -- Etherboot device driver for the Sundance ST201 "Alta".
* Written 2002-2002 by Timothy Legge <tlegge@rogers.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Portions of this code based on:
* sundance.c: A Linux device driver for the Sundance ST201 "Alta"
* Written 1999-2002 by Donald Becker
*
* tulip.c: Tulip and Clone Etherboot Driver
* By Marty Conner
* Copyright (C) 2001 Entity Cyber, Inc.
*
* Linux Driver Version LK1.09a, 10-Jul-2003 (2.4.25)
*
* REVISION HISTORY:
* ================
* v1.1 01-01-2003 timlegge Initial implementation
* v1.7 04-10-2003 timlegge Transfers Linux Kernel (30 sec)
* v1.8 04-13-2003 timlegge Fix multiple transmission bug
* v1.9 08-19-2003 timlegge Support Multicast
* v1.10 01-17-2004 timlegge Initial driver output cleanup
* v1.11 03-21-2004 timlegge Remove unused variables
* v1.12 03-21-2004 timlegge Remove excess MII defines
* v1.13 03-24-2004 timlegge Update to Linux 2.4.25 driver
*
****************************************************************************/
/* to get some global routines like printf */
#include "etherboot.h"
/* to get the interface to the body of the program */
#include "nic.h"
/* to get the PCI support functions, if this is a PCI NIC */
#include "pci.h"
#include "timer.h"
#include "mii.h"
#define drv_version "v1.12"
#define drv_date "2004-03-21"
typedef unsigned char u8;
typedef signed char s8;
typedef unsigned short u16;
typedef signed short s16;
typedef unsigned int u32;
typedef signed int s32;
#define HZ 100
/* Condensed operations for readability. */
#define virt_to_le32desc(addr) cpu_to_le32(virt_to_bus(addr))
#define le32desc_to_virt(addr) bus_to_virt(le32_to_cpu(addr))
/* May need to be moved to mii.h */
struct mii_if_info {
int phy_id;
int advertising;
unsigned int full_duplex:1; /* is full duplex? */
};
//#define EDEBUG
#ifdef EDEBUG
#define dprintf(x) printf x
#else
#define dprintf(x)
#endif
/* Set the mtu */
static int mtu = 1514;
/* Maximum number of multicast addresses to filter (vs. rx-all-multicast).
The sundance uses a 64 element hash table based on the Ethernet CRC. */
// static int multicast_filter_limit = 32;
/* Set the copy breakpoint for the copy-only-tiny-frames scheme.
Setting to > 1518 effectively disables this feature.
This chip can receive into any byte alignment buffers, so word-oriented
archs do not need a copy-align of the IP header. */
static int rx_copybreak = 0;
static int flowctrl = 1;
/* Allow forcing the media type */
/* media[] specifies the media type the NIC operates at.
autosense Autosensing active media.
10mbps_hd 10Mbps half duplex.
10mbps_fd 10Mbps full duplex.
100mbps_hd 100Mbps half duplex.
100mbps_fd 100Mbps full duplex.
*/
static char media[] = "autosense";
/* Operational parameters that are set at compile time. */
/* As Etherboot uses a Polling driver we can keep the number of rings
to the minimum number required. In general that is 1 transmit and 4 receive receive rings. However some cards require that
there be a minimum of 2 rings */
#define TX_RING_SIZE 2
#define TX_QUEUE_LEN 10 /* Limit ring entries actually used. */
#define RX_RING_SIZE 4
/* Operational parameters that usually are not changed. */
/* Time in jiffies before concluding the transmitter is hung. */
#define TX_TIME_OUT (4*HZ)
#define PKT_BUF_SZ 1536
/* Offsets to the device registers.
Unlike software-only systems, device drivers interact with complex hardware.
It's not useful to define symbolic names for every register bit in the
device. The name can only partially document the semantics and make
the driver longer and more difficult to read.
In general, only the important configuration values or bits changed
multiple times should be defined symbolically.
*/
enum alta_offsets {
DMACtrl = 0x00,
TxListPtr = 0x04,
TxDMABurstThresh = 0x08,
TxDMAUrgentThresh = 0x09,
TxDMAPollPeriod = 0x0a,
RxDMAStatus = 0x0c,
RxListPtr = 0x10,
DebugCtrl0 = 0x1a,
DebugCtrl1 = 0x1c,
RxDMABurstThresh = 0x14,
RxDMAUrgentThresh = 0x15,
RxDMAPollPeriod = 0x16,
LEDCtrl = 0x1a,
ASICCtrl = 0x30,
EEData = 0x34,
EECtrl = 0x36,
TxStartThresh = 0x3c,
RxEarlyThresh = 0x3e,
FlashAddr = 0x40,
FlashData = 0x44,
TxStatus = 0x46,
TxFrameId = 0x47,
DownCounter = 0x18,
IntrClear = 0x4a,
IntrEnable = 0x4c,
IntrStatus = 0x4e,
MACCtrl0 = 0x50,
MACCtrl1 = 0x52,
StationAddr = 0x54,
MaxFrameSize = 0x5A,
RxMode = 0x5c,
MIICtrl = 0x5e,
MulticastFilter0 = 0x60,
MulticastFilter1 = 0x64,
RxOctetsLow = 0x68,
RxOctetsHigh = 0x6a,
TxOctetsLow = 0x6c,
TxOctetsHigh = 0x6e,
TxFramesOK = 0x70,
RxFramesOK = 0x72,
StatsCarrierError = 0x74,
StatsLateColl = 0x75,
StatsMultiColl = 0x76,
StatsOneColl = 0x77,
StatsTxDefer = 0x78,
RxMissed = 0x79,
StatsTxXSDefer = 0x7a,
StatsTxAbort = 0x7b,
StatsBcastTx = 0x7c,
StatsBcastRx = 0x7d,
StatsMcastTx = 0x7e,
StatsMcastRx = 0x7f,
/* Aliased and bogus values! */
RxStatus = 0x0c,
};
enum ASICCtrl_HiWord_bit {
GlobalReset = 0x0001,
RxReset = 0x0002,
TxReset = 0x0004,
DMAReset = 0x0008,
FIFOReset = 0x0010,
NetworkReset = 0x0020,
HostReset = 0x0040,
ResetBusy = 0x0400,
};
/* Bits in the interrupt status/mask registers. */
enum intr_status_bits {
IntrSummary = 0x0001, IntrPCIErr = 0x0002, IntrMACCtrl = 0x0008,
IntrTxDone = 0x0004, IntrRxDone = 0x0010, IntrRxStart = 0x0020,
IntrDrvRqst = 0x0040,
StatsMax = 0x0080, LinkChange = 0x0100,
IntrTxDMADone = 0x0200, IntrRxDMADone = 0x0400,
};
/* Bits in the RxMode register. */
enum rx_mode_bits {
AcceptAllIPMulti = 0x20, AcceptMultiHash = 0x10, AcceptAll = 0x08,
AcceptBroadcast = 0x04, AcceptMulticast = 0x02, AcceptMyPhys =
0x01,
};
/* Bits in MACCtrl. */
enum mac_ctrl0_bits {
EnbFullDuplex = 0x20, EnbRcvLargeFrame = 0x40,
EnbFlowCtrl = 0x100, EnbPassRxCRC = 0x200,
};
enum mac_ctrl1_bits {
StatsEnable = 0x0020, StatsDisable = 0x0040, StatsEnabled = 0x0080,
TxEnable = 0x0100, TxDisable = 0x0200, TxEnabled = 0x0400,
RxEnable = 0x0800, RxDisable = 0x1000, RxEnabled = 0x2000,
};
/* The Rx and Tx buffer descriptors.
Using only 32 bit fields simplifies software endian correction.
This structure must be aligned, and should avoid spanning cache lines.
*/
struct netdev_desc {
u32 next_desc;
u32 status;
u32 addr;
u32 length;
};
/* Bits in netdev_desc.status */
enum desc_status_bits {
DescOwn = 0x8000,
DescEndPacket = 0x4000,
DescEndRing = 0x2000,
LastFrag = 0x80000000,
DescIntrOnTx = 0x8000,
DescIntrOnDMADone = 0x80000000,
DisableAlign = 0x00000001,
};
/**********************************************
* Descriptor Ring and Buffer defination
***********************************************/
/* Define the TX Descriptor */
static struct netdev_desc tx_ring[TX_RING_SIZE];
/* Create a static buffer of size PKT_BUF_SZ for each TX Descriptor.
All descriptors point to a part of this buffer */
static unsigned char txb[PKT_BUF_SZ * TX_RING_SIZE];
/* Define the RX Descriptor */
static struct netdev_desc rx_ring[RX_RING_SIZE];
/* Create a static buffer of size PKT_BUF_SZ for each RX Descriptor.
All descriptors point to a part of this buffer */
static unsigned char rxb[RX_RING_SIZE * PKT_BUF_SZ];
/* FIXME: Move BASE to the private structure */
static u32 BASE;
#define EEPROM_SIZE 128
enum pci_id_flags_bits {
PCI_USES_IO = 1, PCI_USES_MEM = 2, PCI_USES_MASTER = 4,
PCI_ADDR0 = 0 << 4, PCI_ADDR1 = 1 << 4, PCI_ADDR2 =
2 << 4, PCI_ADDR3 = 3 << 4,
};
enum chip_capability_flags { CanHaveMII = 1, KendinPktDropBug = 2, };
#define PCI_IOTYPE (PCI_USES_MASTER | PCI_USES_IO | PCI_ADDR0)
#define MII_CNT 4
struct sundance_private {
const char *nic_name;
/* Frequently used values */
unsigned int cur_rx; /* Producer/consumer ring indicies */
unsigned int mtu;
/* These values keep track of the tranceiver/media in use */
unsigned int flowctrl:1;
unsigned int an_enable:1;
unsigned int speed;
/* MII tranceiver section */
struct mii_if_info mii_if;
int mii_preamble_required;
unsigned char phys[MII_CNT];
unsigned char pci_rev_id;
} sdx;
static struct sundance_private *sdc;
/* Station Address location within the EEPROM */
#define EEPROM_SA_OFFSET 0x10
#define DEFAULT_INTR (IntrRxDMADone | IntrPCIErr | \
IntrDrvRqst | IntrTxDone | StatsMax | \
LinkChange)
static int eeprom_read(long ioaddr, int location);
static int mdio_read(struct nic *nic, int phy_id, unsigned int location);
static void mdio_write(struct nic *nic, int phy_id, unsigned int location,
int value);
static void set_rx_mode(struct nic *nic);
static void check_duplex(struct nic *nic)
{
int mii_lpa = mdio_read(nic, sdc->phys[0], MII_LPA);
int negotiated = mii_lpa & sdc->mii_if.advertising;
int duplex;
/* Force media */
if (!sdc->an_enable || mii_lpa == 0xffff) {
if (sdc->mii_if.full_duplex)
outw(inw(BASE + MACCtrl0) | EnbFullDuplex,
BASE + MACCtrl0);
return;
}
/* Autonegotiation */
duplex = (negotiated & 0x0100) || (negotiated & 0x01C0) == 0x0040;
if (sdc->mii_if.full_duplex != duplex) {
sdc->mii_if.full_duplex = duplex;
dprintf(("%s: Setting %s-duplex based on MII #%d "
"negotiated capability %4.4x.\n", sdc->nic_name,
duplex ? "full" : "half", sdc->phys[0],
negotiated));
outw(inw(BASE + MACCtrl0) | duplex ? 0x20 : 0,
BASE + MACCtrl0);
}
}
/**************************************************************************
* init_ring - setup the tx and rx descriptors
*************************************************************************/
static void init_ring(struct nic *nic __unused)
{
int i;
sdc->cur_rx = 0;
/* Initialize all the Rx descriptors */
for (i = 0; i < RX_RING_SIZE; i++) {
rx_ring[i].next_desc = virt_to_le32desc(&rx_ring[i + 1]);
rx_ring[i].status = 0;
rx_ring[i].length = 0;
rx_ring[i].addr = 0;
}
/* Mark the last entry as wrapping the ring */
rx_ring[i - 1].next_desc = virt_to_le32desc(&rx_ring[0]);
for (i = 0; i < RX_RING_SIZE; i++) {
rx_ring[i].addr = virt_to_le32desc(&rxb[i * PKT_BUF_SZ]);
rx_ring[i].length = cpu_to_le32(PKT_BUF_SZ | LastFrag);
}
/* We only use one transmit buffer, but two
* descriptors so transmit engines have somewhere
* to point should they feel the need */
tx_ring[0].status = 0x00000000;
tx_ring[0].addr = virt_to_bus(&txb[0]);
tx_ring[0].next_desc = 0; /* virt_to_bus(&tx_ring[1]); */
/* This descriptor is never used */
tx_ring[1].status = 0x00000000;
tx_ring[1].addr = 0; /*virt_to_bus(&txb[0]); */
tx_ring[1].next_desc = 0;
/* Mark the last entry as wrapping the ring,
* though this should never happen */
tx_ring[1].length = cpu_to_le32(LastFrag | PKT_BUF_SZ);
}
/**************************************************************************
* RESET - Reset Adapter
* ***********************************************************************/
static void sundance_reset(struct nic *nic)
{
int i;
init_ring(nic);
outl(virt_to_le32desc(&rx_ring[0]), BASE + RxListPtr);
/* The Tx List Pointer is written as packets are queued */
/* Initialize other registers. */
/* __set_mac_addr(dev); */
{
u16 addr16;
addr16 = (nic->node_addr[0] | (nic->node_addr[1] << 8));
outw(addr16, BASE + StationAddr);
addr16 = (nic->node_addr[2] | (nic->node_addr[3] << 8));
outw(addr16, BASE + StationAddr + 2);
addr16 = (nic->node_addr[4] | (nic->node_addr[5] << 8));
outw(addr16, BASE + StationAddr + 4);
}
outw(sdc->mtu + 14, BASE + MaxFrameSize);
if (sdc->mtu > 2047) /* this will never happen with default options */
outl(inl(BASE + ASICCtrl) | 0x0c, BASE + ASICCtrl);
set_rx_mode(nic);
outw(0, BASE + DownCounter);
/* Set the chip to poll every N*30nsec */
outb(100, BASE + RxDMAPollPeriod);
/* Fix DFE-580TX packet drop issue */
if (sdc->pci_rev_id >= 0x14)
writeb(0x01, BASE + DebugCtrl1);
outw(RxEnable | TxEnable, BASE + MACCtrl1);
/* Construct a perfect filter frame with the mac address as first match
* and broadcast for all others */
for (i = 0; i < 192; i++)
txb[i] = 0xFF;
txb[0] = nic->node_addr[0];
txb[1] = nic->node_addr[1];
txb[2] = nic->node_addr[2];
txb[3] = nic->node_addr[3];
txb[4] = nic->node_addr[4];
txb[5] = nic->node_addr[5];
dprintf(("%s: Done sundance_reset, status: Rx %hX Tx %hX "
"MAC Control %hX, %hX %hX\n",
sdc->nic_name, (int) inl(BASE + RxStatus),
(int) inw(BASE + TxStatus), (int) inl(BASE + MACCtrl0),
(int) inw(BASE + MACCtrl1), (int) inw(BASE + MACCtrl0)));
}
/**************************************************************************
IRQ - Wait for a frame
***************************************************************************/
void sundance_irq ( struct nic *nic, irq_action_t action ) {
unsigned int intr_status;
switch ( action ) {
case DISABLE :
case ENABLE :
intr_status = inw(nic->ioaddr + IntrStatus);
intr_status = intr_status & ~DEFAULT_INTR;
if ( action == ENABLE )
intr_status = intr_status | DEFAULT_INTR;
outw(intr_status, nic->ioaddr + IntrEnable);
break;
case FORCE :
outw(0x0200, BASE + ASICCtrl);
break;
}
}
/**************************************************************************
POLL - Wait for a frame
***************************************************************************/
static int sundance_poll(struct nic *nic, int retreive)
{
/* return true if there's an ethernet packet ready to read */
/* nic->packet should contain data on return */
/* nic->packetlen should contain length of data */
int entry = sdc->cur_rx % RX_RING_SIZE;
u32 frame_status = le32_to_cpu(rx_ring[entry].status);
int intr_status;
int pkt_len = 0;
if (!(frame_status & DescOwn))
return 0;
/* There is a packet ready */
if(!retreive)
return 1;
intr_status = inw(nic->ioaddr + IntrStatus);
outw(intr_status, nic->ioaddr + IntrStatus);
pkt_len = frame_status & 0x1fff;
if (frame_status & 0x001f4000) {
dprintf(("Polling frame_status error\n")); /* Do we really care about this */
} else {
if (pkt_len < rx_copybreak) {
/* FIXME: What should happen Will this ever occur */
printf("Poll Error: pkt_len < rx_copybreak");
} else {
nic->packetlen = pkt_len;
memcpy(nic->packet, rxb +
(sdc->cur_rx * PKT_BUF_SZ), nic->packetlen);
}
}
rx_ring[entry].length = cpu_to_le32(PKT_BUF_SZ | LastFrag);
rx_ring[entry].status = 0;
entry++;
sdc->cur_rx = entry % RX_RING_SIZE;
outw(DEFAULT_INTR & ~(IntrRxDone|IntrRxDMADone),
nic->ioaddr + IntrStatus);
return 1;
}
/**************************************************************************
TRANSMIT - Transmit a frame
***************************************************************************/
static void sundance_transmit(struct nic *nic, const char *d, /* Destination */
unsigned int t, /* Type */
unsigned int s, /* size */
const char *p)
{ /* Packet */
u16 nstype;
u32 to;
/* Disable the Tx */
outw(TxDisable, BASE + MACCtrl1);
memcpy(txb, d, ETH_ALEN);
memcpy(txb + ETH_ALEN, nic->node_addr, ETH_ALEN);
nstype = htons((u16) t);
memcpy(txb + 2 * ETH_ALEN, (u8 *) & nstype, 2);
memcpy(txb + ETH_HLEN, p, s);
s += ETH_HLEN;
s &= 0x0FFF;
while (s < ETH_ZLEN)
txb[s++] = '\0';
/* Setup the transmit descriptor */
tx_ring[0].length = cpu_to_le32(s | LastFrag);
tx_ring[0].status = cpu_to_le32(0x00000001);
/* Point to transmit descriptor */
outl(virt_to_le32desc(&tx_ring[0]), BASE + TxListPtr);
/* Enable Tx */
outw(TxEnable, BASE + MACCtrl1);
/* Trigger an immediate send */
outw(0, BASE + TxStatus);
to = currticks() + TX_TIME_OUT;
while (!(tx_ring[0].status & 0x00010000) && (currticks() < to)); /* wait */
if (currticks() >= to) {
printf("TX Time Out");
}
/* Disable Tx */
outw(TxDisable, BASE + MACCtrl1);
}
/**************************************************************************
DISABLE - Turn off ethernet interface
***************************************************************************/
static void sundance_disable(struct dev *dev __unused)
{
/* put the card in its initial state */
/* This function serves 3 purposes.
* This disables DMA and interrupts so we don't receive
* unexpected packets or interrupts from the card after
* etherboot has finished.
* This frees resources so etherboot may use
* this driver on another interface
* This allows etherboot to reinitialize the interface
* if something is something goes wrong.
*/
outw(0x0000, BASE + IntrEnable);
/* Stop the Chipchips Tx and Rx Status */
outw(TxDisable | RxDisable | StatsDisable, BASE + MACCtrl1);
}
/**************************************************************************
PROBE - Look for an adapter, this routine's visible to the outside
***************************************************************************/
static int sundance_probe(struct dev *dev, struct pci_device *pci)
{
struct nic *nic = (struct nic *) dev;
u8 ee_data[EEPROM_SIZE];
u16 mii_ctl;
int i;
int speed;
if (pci->ioaddr == 0)
return 0;
/* BASE is used throughout to address the card */
BASE = pci->ioaddr;
printf(" sundance.c: Found %s Vendor=0x%hX Device=0x%hX\n",
pci->name, pci->vendor, pci->dev_id);
/* Get the MAC Address by reading the EEPROM */
for (i = 0; i < 3; i++) {
((u16 *) ee_data)[i] =
le16_to_cpu(eeprom_read(BASE, i + EEPROM_SA_OFFSET));
}
/* Update the nic structure with the MAC Address */
for (i = 0; i < ETH_ALEN; i++) {
nic->node_addr[i] = ee_data[i];
}
/* Set the card as PCI Bus Master */
adjust_pci_device(pci);
// sdc->mii_if.dev = pci;
// sdc->mii_if.phy_id_mask = 0x1f;
// sdc->mii_if.reg_num_mask = 0x1f;
/* point to private storage */
sdc = &sdx;
sdc->nic_name = pci->name;
sdc->mtu = mtu;
pci_read_config_byte(pci, PCI_REVISION_ID, &sdc->pci_rev_id);
dprintf(("Device revision id: %hx\n", sdc->pci_rev_id));
/* Print out some hardware info */
printf("%s: %! at ioaddr %hX, ", pci->name, nic->node_addr, BASE);
sdc->mii_preamble_required = 0;
if (1) {
int phy, phy_idx = 0;
sdc->phys[0] = 1; /* Default Setting */
sdc->mii_preamble_required++;
for (phy = 1; phy < 32 && phy_idx < MII_CNT; phy++) {
int mii_status = mdio_read(nic, phy, MII_BMSR);
if (mii_status != 0xffff && mii_status != 0x0000) {
sdc->phys[phy_idx++] = phy;
sdc->mii_if.advertising =
mdio_read(nic, phy, MII_ADVERTISE);
if ((mii_status & 0x0040) == 0)
sdc->mii_preamble_required++;
dprintf
(("%s: MII PHY found at address %d, status " "%hX advertising %hX\n", sdc->nic_name, phy, mii_status, sdc->mii_if.advertising));
}
}
sdc->mii_preamble_required--;
if (phy_idx == 0)
printf("%s: No MII transceiver found!\n",
sdc->nic_name);
sdc->mii_if.phy_id = sdc->phys[0];
}
/* Parse override configuration */
sdc->an_enable = 1;
if (strcasecmp(media, "autosense") != 0) {
sdc->an_enable = 0;
if (strcasecmp(media, "100mbps_fd") == 0 ||
strcasecmp(media, "4") == 0) {
sdc->speed = 100;
sdc->mii_if.full_duplex = 1;
} else if (strcasecmp(media, "100mbps_hd") == 0
|| strcasecmp(media, "3") == 0) {
sdc->speed = 100;
sdc->mii_if.full_duplex = 0;
} else if (strcasecmp(media, "10mbps_fd") == 0 ||
strcasecmp(media, "2") == 0) {
sdc->speed = 10;
sdc->mii_if.full_duplex = 1;
} else if (strcasecmp(media, "10mbps_hd") == 0 ||
strcasecmp(media, "1") == 0) {
sdc->speed = 10;
sdc->mii_if.full_duplex = 0;
} else {
sdc->an_enable = 1;
}
}
if (flowctrl == 1)
sdc->flowctrl = 1;
/* Fibre PHY? */
if (inl(BASE + ASICCtrl) & 0x80) {
/* Default 100Mbps Full */
if (sdc->an_enable) {
sdc->speed = 100;
sdc->mii_if.full_duplex = 1;
sdc->an_enable = 0;
}
}
/* The Linux driver uses flow control and resets the link here. This means the
mii section from above would need to be re done I believe. Since it serves
no real purpose leave it out. */
/* Force media type */
if (!sdc->an_enable) {
mii_ctl = 0;
mii_ctl |= (sdc->speed == 100) ? BMCR_SPEED100 : 0;
mii_ctl |= (sdc->mii_if.full_duplex) ? BMCR_FULLDPLX : 0;
mdio_write(nic, sdc->phys[0], MII_BMCR, mii_ctl);
printf("Override speed=%d, %s duplex\n",
sdc->speed,
sdc->mii_if.full_duplex ? "Full" : "Half");
}
/* Reset the chip to erase previous misconfiguration */
dprintf(("ASIC Control is %x.\n", inl(BASE + ASICCtrl)));
outw(0x007f, BASE + ASICCtrl + 2);
dprintf(("ASIC Control is now %x.\n", inl(BASE + ASICCtrl)));
sundance_reset(nic);
if (sdc->an_enable) {
u16 mii_advertise, mii_lpa;
mii_advertise =
mdio_read(nic, sdc->phys[0], MII_ADVERTISE);
mii_lpa = mdio_read(nic, sdc->phys[0], MII_LPA);
mii_advertise &= mii_lpa;
if (mii_advertise & ADVERTISE_100FULL)
sdc->speed = 100;
else if (mii_advertise & ADVERTISE_100HALF)
sdc->speed = 100;
else if (mii_advertise & ADVERTISE_10FULL)
sdc->speed = 10;
else if (mii_advertise & ADVERTISE_10HALF)
sdc->speed = 10;
} else {
mii_ctl = mdio_read(nic, sdc->phys[0], MII_BMCR);
speed = (mii_ctl & BMCR_SPEED100) ? 100 : 10;
sdc->speed = speed;
printf("%s: Link changed: %dMbps ,", sdc->nic_name, speed);
printf("%s duplex.\n", (mii_ctl & BMCR_FULLDPLX) ?
"full" : "half");
}
check_duplex(nic);
if (sdc->flowctrl && sdc->mii_if.full_duplex) {
outw(inw(BASE + MulticastFilter1 + 2) | 0x0200,
BASE + MulticastFilter1 + 2);
outw(inw(BASE + MACCtrl0) | EnbFlowCtrl, BASE + MACCtrl0);
}
printf("%dMbps, %s-Duplex\n", sdc->speed,
sdc->mii_if.full_duplex ? "Full" : "Half");
/* point to NIC specific routines */
dev->disable = sundance_disable;
nic->poll = sundance_poll;
nic->transmit = sundance_transmit;
nic->irqno = pci->irq;
nic->irq = sundance_irq;
nic->ioaddr = BASE;
return 1;
}
/* Read the EEPROM and MII Management Data I/O (MDIO) interfaces. */
static int eeprom_read(long ioaddr, int location)
{
int boguscnt = 10000; /* Typical 1900 ticks */
outw(0x0200 | (location & 0xff), ioaddr + EECtrl);
do {
if (!(inw(ioaddr + EECtrl) & 0x8000)) {
return inw(ioaddr + EEData);
}
}
while (--boguscnt > 0);
return 0;
}
/* MII transceiver control section.
Read and write the MII registers using software-generated serial
MDIO protocol. See the MII specifications or DP83840A data sheet
for details.
The maximum data clock rate is 2.5 Mhz.
The timing is decoupled from the processor clock by flushing the write
from the CPU write buffer with a following read, and using PCI
transaction time. */
#define mdio_in(mdio_addr) inb(mdio_addr)
#define mdio_out(value, mdio_addr) outb(value, mdio_addr)
#define mdio_delay(mdio_addr) inb(mdio_addr)
enum mii_reg_bits {
MDIO_ShiftClk = 0x0001, MDIO_Data = 0x0002, MDIO_EnbOutput =
0x0004,
};
#define MDIO_EnbIn (0)
#define MDIO_WRITE0 (MDIO_EnbOutput)
#define MDIO_WRITE1 (MDIO_Data | MDIO_EnbOutput)
/* Generate the preamble required for initial synchronization and
a few older transceivers. */
static void mdio_sync(long mdio_addr)
{
int bits = 32;
/* Establish sync by sending at least 32 logic ones. */
while (--bits >= 0) {
mdio_out(MDIO_WRITE1, mdio_addr);
mdio_delay(mdio_addr);
mdio_out(MDIO_WRITE1 | MDIO_ShiftClk, mdio_addr);
mdio_delay(mdio_addr);
}
}
static int
mdio_read(struct nic *nic __unused, int phy_id, unsigned int location)
{
long mdio_addr = BASE + MIICtrl;
int mii_cmd = (0xf6 << 10) | (phy_id << 5) | location;
int i, retval = 0;
if (sdc->mii_preamble_required)
mdio_sync(mdio_addr);
/* Shift the read command bits out. */
for (i = 15; i >= 0; i--) {
int dataval =
(mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0;
mdio_out(dataval, mdio_addr);
mdio_delay(mdio_addr);
mdio_out(dataval | MDIO_ShiftClk, mdio_addr);
mdio_delay(mdio_addr);
}
/* Read the two transition, 16 data, and wire-idle bits. */
for (i = 19; i > 0; i--) {
mdio_out(MDIO_EnbIn, mdio_addr);
mdio_delay(mdio_addr);
retval = (retval << 1) | ((mdio_in(mdio_addr) & MDIO_Data)
? 1 : 0);
mdio_out(MDIO_EnbIn | MDIO_ShiftClk, mdio_addr);
mdio_delay(mdio_addr);
}
return (retval >> 1) & 0xffff;
}
static void
mdio_write(struct nic *nic __unused, int phy_id,
unsigned int location, int value)
{
long mdio_addr = BASE + MIICtrl;
int mii_cmd =
(0x5002 << 16) | (phy_id << 23) | (location << 18) | value;
int i;
if (sdc->mii_preamble_required)
mdio_sync(mdio_addr);
/* Shift the command bits out. */
for (i = 31; i >= 0; i--) {
int dataval =
(mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0;
mdio_out(dataval, mdio_addr);
mdio_delay(mdio_addr);
mdio_out(dataval | MDIO_ShiftClk, mdio_addr);
mdio_delay(mdio_addr);
}
/* Clear out extra bits. */
for (i = 2; i > 0; i--) {
mdio_out(MDIO_EnbIn, mdio_addr);
mdio_delay(mdio_addr);
mdio_out(MDIO_EnbIn | MDIO_ShiftClk, mdio_addr);
mdio_delay(mdio_addr);
}
return;
}
static void set_rx_mode(struct nic *nic __unused)
{
int i;
u16 mc_filter[4]; /* Multicast hash filter */
u32 rx_mode;
memset(mc_filter, 0xff, sizeof(mc_filter));
rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys;
if (sdc->mii_if.full_duplex && sdc->flowctrl)
mc_filter[3] |= 0x0200;
for (i = 0; i < 4; i++)
outw(mc_filter[i], BASE + MulticastFilter0 + i * 2);
outb(rx_mode, BASE + RxMode);
return;
}
static struct pci_id sundance_nics[] = {
PCI_ROM(0x13f0, 0x0201, "sundance", "ST201 Sundance 'Alta' based Adaptor"),
PCI_ROM(0x1186, 0x1002, "dfe530txs", "D-Link DFE530TXS (Sundance ST201 Alta)"),
};
static struct pci_driver sundance_driver __pci_driver = {
.type = NIC_DRIVER,
.name = "SUNDANCE/PCI",
.probe = sundance_probe,
.ids = sundance_nics,
.id_count = sizeof(sundance_nics) / sizeof(sundance_nics[0]),
.class = 0,
};

3394
src/drivers/net/tg3.c Normal file

File diff suppressed because it is too large Load Diff

2211
src/drivers/net/tg3.h Normal file

File diff suppressed because it is too large Load Diff

1722
src/drivers/net/tlan.c Normal file

File diff suppressed because it is too large Load Diff

536
src/drivers/net/tlan.h Normal file
View File

@@ -0,0 +1,536 @@
/**************************************************************************
*
* tlan.c -- Etherboot device driver for the Texas Instruments ThunderLAN
* Written 2003-2003 by Timothy Legge <tlegge@rogers.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Portions of this code (almost all) based on:
* tlan.c: Linux ThunderLan Driver:
*
* by James Banks
*
* (C) 1997-1998 Caldera, Inc.
* (C) 1998 James Banks
* (C) 1999-2001 Torben Mathiasen
* (C) 2002 Samuel Chessman
*
* REVISION HISTORY:
* ================
* v1.0 07-08-2003 timlegge Initial not quite working version
*
* Indent Style: indent -kr -i8
***************************************************************************/
/*
#include <asm/io.h>
#include <asm/types.h>
#include <linux/netdevice.h>
*/
typedef unsigned char u8;
typedef signed char s8;
typedef unsigned short u16;
typedef signed short s16;
typedef unsigned int u32;
typedef signed int s32;
/*****************************************************************
* TLan Definitions
*
****************************************************************/
#define FALSE 0
#define TRUE 1
#define TLAN_MIN_FRAME_SIZE 64
#define TLAN_MAX_FRAME_SIZE 1600
#define TLAN_NUM_RX_LISTS 4
#define TLAN_NUM_TX_LISTS 2
#define TLAN_IGNORE 0
#define TLAN_RECORD 1
/*
#define TLAN_DBG(lvl, format, args...) if (debug&lvl) printf("TLAN: " format, ##args );
*/
#define TLAN_DEBUG_GNRL 0x0001
#define TLAN_DEBUG_TX 0x0002
#define TLAN_DEBUG_RX 0x0004
#define TLAN_DEBUG_LIST 0x0008
#define TLAN_DEBUG_PROBE 0x0010
#define TX_TIMEOUT (10*HZ) /* We need time for auto-neg */
#define MAX_TLAN_BOARDS 8 /* Max number of boards installed at a time */
/*****************************************************************
* Device Identification Definitions
*
****************************************************************/
#define PCI_DEVICE_ID_NETELLIGENT_10_T2 0xB012
#define PCI_DEVICE_ID_NETELLIGENT_10_100_WS_5100 0xB030
#ifndef PCI_DEVICE_ID_OLICOM_OC2183
#define PCI_DEVICE_ID_OLICOM_OC2183 0x0013
#endif
#ifndef PCI_DEVICE_ID_OLICOM_OC2325
#define PCI_DEVICE_ID_OLICOM_OC2325 0x0012
#endif
#ifndef PCI_DEVICE_ID_OLICOM_OC2326
#define PCI_DEVICE_ID_OLICOM_OC2326 0x0014
#endif
typedef struct tlan_adapter_entry {
u16 vendorId;
u16 deviceId;
char *deviceLabel;
u32 flags;
u16 addrOfs;
} TLanAdapterEntry;
#define TLAN_ADAPTER_NONE 0x00000000
#define TLAN_ADAPTER_UNMANAGED_PHY 0x00000001
#define TLAN_ADAPTER_BIT_RATE_PHY 0x00000002
#define TLAN_ADAPTER_USE_INTERN_10 0x00000004
#define TLAN_ADAPTER_ACTIVITY_LED 0x00000008
#define TLAN_SPEED_DEFAULT 0
#define TLAN_SPEED_10 10
#define TLAN_SPEED_100 100
#define TLAN_DUPLEX_DEFAULT 0
#define TLAN_DUPLEX_HALF 1
#define TLAN_DUPLEX_FULL 2
/*****************************************************************
* EISA Definitions
*
****************************************************************/
#define EISA_ID 0xc80 /* EISA ID Registers */
#define EISA_ID0 0xc80 /* EISA ID Register 0 */
#define EISA_ID1 0xc81 /* EISA ID Register 1 */
#define EISA_ID2 0xc82 /* EISA ID Register 2 */
#define EISA_ID3 0xc83 /* EISA ID Register 3 */
#define EISA_CR 0xc84 /* EISA Control Register */
#define EISA_REG0 0xc88 /* EISA Configuration Register 0 */
#define EISA_REG1 0xc89 /* EISA Configuration Register 1 */
#define EISA_REG2 0xc8a /* EISA Configuration Register 2 */
#define EISA_REG3 0xc8f /* EISA Configuration Register 3 */
#define EISA_APROM 0xc90 /* Ethernet Address PROM */
/*****************************************************************
* Rx/Tx List Definitions
*
****************************************************************/
#define TLAN_BUFFERS_PER_LIST 10
#define TLAN_LAST_BUFFER 0x80000000
#define TLAN_CSTAT_UNUSED 0x8000
#define TLAN_CSTAT_FRM_CMP 0x4000
#define TLAN_CSTAT_READY 0x3000
#define TLAN_CSTAT_EOC 0x0800
#define TLAN_CSTAT_RX_ERROR 0x0400
#define TLAN_CSTAT_PASS_CRC 0x0200
#define TLAN_CSTAT_DP_PR 0x0100
/*****************************************************************
* PHY definitions
*
****************************************************************/
#define TLAN_PHY_MAX_ADDR 0x1F
#define TLAN_PHY_NONE 0x20
/*****************************************************************
* TLan Driver Timer Definitions
*
****************************************************************/
#define TLAN_TIMER_LINK_BEAT 1
#define TLAN_TIMER_ACTIVITY 2
#define TLAN_TIMER_PHY_PDOWN 3
#define TLAN_TIMER_PHY_PUP 4
#define TLAN_TIMER_PHY_RESET 5
#define TLAN_TIMER_PHY_START_LINK 6
#define TLAN_TIMER_PHY_FINISH_AN 7
#define TLAN_TIMER_FINISH_RESET 8
#define TLAN_TIMER_ACT_DELAY (HZ/10)
/*****************************************************************
* TLan Driver Eeprom Definitions
*
****************************************************************/
#define TLAN_EEPROM_ACK 0
#define TLAN_EEPROM_STOP 1
/*****************************************************************
* Host Register Offsets and Contents
*
****************************************************************/
#define TLAN_HOST_CMD 0x00
#define TLAN_HC_GO 0x80000000
#define TLAN_HC_STOP 0x40000000
#define TLAN_HC_ACK 0x20000000
#define TLAN_HC_CS_MASK 0x1FE00000
#define TLAN_HC_EOC 0x00100000
#define TLAN_HC_RT 0x00080000
#define TLAN_HC_NES 0x00040000
#define TLAN_HC_AD_RST 0x00008000
#define TLAN_HC_LD_TMR 0x00004000
#define TLAN_HC_LD_THR 0x00002000
#define TLAN_HC_REQ_INT 0x00001000
#define TLAN_HC_INT_OFF 0x00000800
#define TLAN_HC_INT_ON 0x00000400
#define TLAN_HC_AC_MASK 0x000000FF
#define TLAN_CH_PARM 0x04
#define TLAN_DIO_ADR 0x08
#define TLAN_DA_ADR_INC 0x8000
#define TLAN_DA_RAM_ADR 0x4000
#define TLAN_HOST_INT 0x0A
#define TLAN_HI_IV_MASK 0x1FE0
#define TLAN_HI_IT_MASK 0x001C
#define TLAN_DIO_DATA 0x0C
/* ThunderLAN Internal Register DIO Offsets */
#define TLAN_NET_CMD 0x00
#define TLAN_NET_CMD_NRESET 0x80
#define TLAN_NET_CMD_NWRAP 0x40
#define TLAN_NET_CMD_CSF 0x20
#define TLAN_NET_CMD_CAF 0x10
#define TLAN_NET_CMD_NOBRX 0x08
#define TLAN_NET_CMD_DUPLEX 0x04
#define TLAN_NET_CMD_TRFRAM 0x02
#define TLAN_NET_CMD_TXPACE 0x01
#define TLAN_NET_SIO 0x01
#define TLAN_NET_SIO_MINTEN 0x80
#define TLAN_NET_SIO_ECLOK 0x40
#define TLAN_NET_SIO_ETXEN 0x20
#define TLAN_NET_SIO_EDATA 0x10
#define TLAN_NET_SIO_NMRST 0x08
#define TLAN_NET_SIO_MCLK 0x04
#define TLAN_NET_SIO_MTXEN 0x02
#define TLAN_NET_SIO_MDATA 0x01
#define TLAN_NET_STS 0x02
#define TLAN_NET_STS_MIRQ 0x80
#define TLAN_NET_STS_HBEAT 0x40
#define TLAN_NET_STS_TXSTOP 0x20
#define TLAN_NET_STS_RXSTOP 0x10
#define TLAN_NET_STS_RSRVD 0x0F
#define TLAN_NET_MASK 0x03
#define TLAN_NET_MASK_MASK7 0x80
#define TLAN_NET_MASK_MASK6 0x40
#define TLAN_NET_MASK_MASK5 0x20
#define TLAN_NET_MASK_MASK4 0x10
#define TLAN_NET_MASK_RSRVD 0x0F
#define TLAN_NET_CONFIG 0x04
#define TLAN_NET_CFG_RCLK 0x8000
#define TLAN_NET_CFG_TCLK 0x4000
#define TLAN_NET_CFG_BIT 0x2000
#define TLAN_NET_CFG_RXCRC 0x1000
#define TLAN_NET_CFG_PEF 0x0800
#define TLAN_NET_CFG_1FRAG 0x0400
#define TLAN_NET_CFG_1CHAN 0x0200
#define TLAN_NET_CFG_MTEST 0x0100
#define TLAN_NET_CFG_PHY_EN 0x0080
#define TLAN_NET_CFG_MSMASK 0x007F
#define TLAN_MAN_TEST 0x06
#define TLAN_DEF_VENDOR_ID 0x08
#define TLAN_DEF_DEVICE_ID 0x0A
#define TLAN_DEF_REVISION 0x0C
#define TLAN_DEF_SUBCLASS 0x0D
#define TLAN_DEF_MIN_LAT 0x0E
#define TLAN_DEF_MAX_LAT 0x0F
#define TLAN_AREG_0 0x10
#define TLAN_AREG_1 0x16
#define TLAN_AREG_2 0x1C
#define TLAN_AREG_3 0x22
#define TLAN_HASH_1 0x28
#define TLAN_HASH_2 0x2C
#define TLAN_GOOD_TX_FRMS 0x30
#define TLAN_TX_UNDERUNS 0x33
#define TLAN_GOOD_RX_FRMS 0x34
#define TLAN_RX_OVERRUNS 0x37
#define TLAN_DEFERRED_TX 0x38
#define TLAN_CRC_ERRORS 0x3A
#define TLAN_CODE_ERRORS 0x3B
#define TLAN_MULTICOL_FRMS 0x3C
#define TLAN_SINGLECOL_FRMS 0x3E
#define TLAN_EXCESSCOL_FRMS 0x40
#define TLAN_LATE_COLS 0x41
#define TLAN_CARRIER_LOSS 0x42
#define TLAN_ACOMMIT 0x43
#define TLAN_LED_REG 0x44
#define TLAN_LED_ACT 0x10
#define TLAN_LED_LINK 0x01
#define TLAN_BSIZE_REG 0x45
#define TLAN_MAX_RX 0x46
#define TLAN_INT_DIS 0x48
#define TLAN_ID_TX_EOC 0x04
#define TLAN_ID_RX_EOF 0x02
#define TLAN_ID_RX_EOC 0x01
/* ThunderLAN Interrupt Codes */
#define TLAN_INT_NUMBER_OF_INTS 8
#define TLAN_INT_NONE 0x0000
#define TLAN_INT_TX_EOF 0x0001
#define TLAN_INT_STAT_OVERFLOW 0x0002
#define TLAN_INT_RX_EOF 0x0003
#define TLAN_INT_DUMMY 0x0004
#define TLAN_INT_TX_EOC 0x0005
#define TLAN_INT_STATUS_CHECK 0x0006
#define TLAN_INT_RX_EOC 0x0007
/* ThunderLAN MII Registers */
/* Generic MII/PHY Registers */
#define MII_GEN_CTL 0x00
#define MII_GC_RESET 0x8000
#define MII_GC_LOOPBK 0x4000
#define MII_GC_SPEEDSEL 0x2000
#define MII_GC_AUTOENB 0x1000
#define MII_GC_PDOWN 0x0800
#define MII_GC_ISOLATE 0x0400
#define MII_GC_AUTORSRT 0x0200
#define MII_GC_DUPLEX 0x0100
#define MII_GC_COLTEST 0x0080
#define MII_GC_RESERVED 0x007F
#define MII_GEN_STS 0x01
#define MII_GS_100BT4 0x8000
#define MII_GS_100BTXFD 0x4000
#define MII_GS_100BTXHD 0x2000
#define MII_GS_10BTFD 0x1000
#define MII_GS_10BTHD 0x0800
#define MII_GS_RESERVED 0x07C0
#define MII_GS_AUTOCMPLT 0x0020
#define MII_GS_RFLT 0x0010
#define MII_GS_AUTONEG 0x0008
#define MII_GS_LINK 0x0004
#define MII_GS_JABBER 0x0002
#define MII_GS_EXTCAP 0x0001
#define MII_GEN_ID_HI 0x02
#define MII_GEN_ID_LO 0x03
#define MII_GIL_OUI 0xFC00
#define MII_GIL_MODEL 0x03F0
#define MII_GIL_REVISION 0x000F
#define MII_AN_ADV 0x04
#define MII_AN_LPA 0x05
#define MII_AN_EXP 0x06
/* ThunderLAN Specific MII/PHY Registers */
#define TLAN_TLPHY_ID 0x10
#define TLAN_TLPHY_CTL 0x11
#define TLAN_TC_IGLINK 0x8000
#define TLAN_TC_SWAPOL 0x4000
#define TLAN_TC_AUISEL 0x2000
#define TLAN_TC_SQEEN 0x1000
#define TLAN_TC_MTEST 0x0800
#define TLAN_TC_RESERVED 0x07F8
#define TLAN_TC_NFEW 0x0004
#define TLAN_TC_INTEN 0x0002
#define TLAN_TC_TINT 0x0001
#define TLAN_TLPHY_STS 0x12
#define TLAN_TS_MINT 0x8000
#define TLAN_TS_PHOK 0x4000
#define TLAN_TS_POLOK 0x2000
#define TLAN_TS_TPENERGY 0x1000
#define TLAN_TS_RESERVED 0x0FFF
#define TLAN_TLPHY_PAR 0x19
#define TLAN_PHY_CIM_STAT 0x0020
#define TLAN_PHY_SPEED_100 0x0040
#define TLAN_PHY_DUPLEX_FULL 0x0080
#define TLAN_PHY_AN_EN_STAT 0x0400
/* National Sem. & Level1 PHY id's */
#define NAT_SEM_ID1 0x2000
#define NAT_SEM_ID2 0x5C01
#define LEVEL1_ID1 0x7810
#define LEVEL1_ID2 0x0000
#define CIRC_INC( a, b ) if ( ++a >= b ) a = 0
/* Routines to access internal registers. */
inline u8 TLan_DioRead8(u16 base_addr, u16 internal_addr)
{
outw(internal_addr, base_addr + TLAN_DIO_ADR);
return (inb((base_addr + TLAN_DIO_DATA) + (internal_addr & 0x3)));
} /* TLan_DioRead8 */
inline u16 TLan_DioRead16(u16 base_addr, u16 internal_addr)
{
outw(internal_addr, base_addr + TLAN_DIO_ADR);
return (inw((base_addr + TLAN_DIO_DATA) + (internal_addr & 0x2)));
} /* TLan_DioRead16 */
inline u32 TLan_DioRead32(u16 base_addr, u16 internal_addr)
{
outw(internal_addr, base_addr + TLAN_DIO_ADR);
return (inl(base_addr + TLAN_DIO_DATA));
} /* TLan_DioRead32 */
inline void TLan_DioWrite8(u16 base_addr, u16 internal_addr, u8 data)
{
outw(internal_addr, base_addr + TLAN_DIO_ADR);
outb(data, base_addr + TLAN_DIO_DATA + (internal_addr & 0x3));
}
inline void TLan_DioWrite16(u16 base_addr, u16 internal_addr, u16 data)
{
outw(internal_addr, base_addr + TLAN_DIO_ADR);
outw(data, base_addr + TLAN_DIO_DATA + (internal_addr & 0x2));
}
inline void TLan_DioWrite32(u16 base_addr, u16 internal_addr, u32 data)
{
outw(internal_addr, base_addr + TLAN_DIO_ADR);
outl(data, base_addr + TLAN_DIO_DATA + (internal_addr & 0x2));
}
#if 0
inline void TLan_ClearBit(u8 bit, u16 port)
{
outb_p(inb_p(port) & ~bit, port);
}
inline int TLan_GetBit(u8 bit, u16 port)
{
return ((int) (inb_p(port) & bit));
}
inline void TLan_SetBit(u8 bit, u16 port)
{
outb_p(inb_p(port) | bit, port);
}
#endif
#define TLan_ClearBit( bit, port ) outb_p(inb_p(port) & ~bit, port)
#define TLan_GetBit( bit, port ) ((int) (inb_p(port) & bit))
#define TLan_SetBit( bit, port ) outb_p(inb_p(port) | bit, port)
#ifdef I_LIKE_A_FAST_HASH_FUNCTION
/* given 6 bytes, view them as 8 6-bit numbers and return the XOR of those */
/* the code below is about seven times as fast as the original code */
inline u32 TLan_HashFunc(u8 * a)
{
u8 hash;
hash = (a[0] ^ a[3]); /* & 077 */
hash ^= ((a[0] ^ a[3]) >> 6); /* & 003 */
hash ^= ((a[1] ^ a[4]) << 2); /* & 074 */
hash ^= ((a[1] ^ a[4]) >> 4); /* & 017 */
hash ^= ((a[2] ^ a[5]) << 4); /* & 060 */
hash ^= ((a[2] ^ a[5]) >> 2); /* & 077 */
return (hash & 077);
}
#else /* original code */
inline u32 xor(u32 a, u32 b)
{
return ((a && !b) || (!a && b));
}
#define XOR8( a, b, c, d, e, f, g, h ) xor( a, xor( b, xor( c, xor( d, xor( e, xor( f, xor( g, h ) ) ) ) ) ) )
#define DA( a, bit ) ( ( (u8) a[bit/8] ) & ( (u8) ( 1 << bit%8 ) ) )
inline u32 TLan_HashFunc(u8 * a)
{
u32 hash;
hash =
XOR8(DA(a, 0), DA(a, 6), DA(a, 12), DA(a, 18), DA(a, 24),
DA(a, 30), DA(a, 36), DA(a, 42));
hash |=
XOR8(DA(a, 1), DA(a, 7), DA(a, 13), DA(a, 19), DA(a, 25),
DA(a, 31), DA(a, 37), DA(a, 43)) << 1;
hash |=
XOR8(DA(a, 2), DA(a, 8), DA(a, 14), DA(a, 20), DA(a, 26),
DA(a, 32), DA(a, 38), DA(a, 44)) << 2;
hash |=
XOR8(DA(a, 3), DA(a, 9), DA(a, 15), DA(a, 21), DA(a, 27),
DA(a, 33), DA(a, 39), DA(a, 45)) << 3;
hash |=
XOR8(DA(a, 4), DA(a, 10), DA(a, 16), DA(a, 22), DA(a, 28),
DA(a, 34), DA(a, 40), DA(a, 46)) << 4;
hash |=
XOR8(DA(a, 5), DA(a, 11), DA(a, 17), DA(a, 23), DA(a, 29),
DA(a, 35), DA(a, 41), DA(a, 47)) << 5;
return hash;
}
#endif /* I_LIKE_A_FAST_HASH_FUNCTION */

2082
src/drivers/net/tulip.c Normal file

File diff suppressed because it is too large Load Diff

53
src/drivers/net/tulip.txt Normal file
View File

@@ -0,0 +1,53 @@
This software may be used and distributed according to the terms of
the GNU Public License, incorporated herein by reference.
This is a tulip and clone driver for Etherboot. See the revision
history in the tulip.c file for information on changes. This version
of the driver incorporates changes from Bob Edwards and Paul Mackerras
who cantributed changes to support the TRENDnet TE100-PCIA NIC which
uses a genuine Intel 21143-PD chipset. There are also various code
cleanups to make time-based activities more reliable.
Of course you have to have all the usual Etherboot environment
(bootp/dhcp/NFS) set up, and you need a Linux kernel with v0.91g
(7.16.99) or later of the tulip.c driver compiled in to support some
MX98715 based cards. That file is available at:
http://cesdis.gsfc.nasa.gov/linux/drivers/test/tulip.c
NOTES
I've tested this driver with a SOHOware Fast 10/100 Model SDA110A,
a Linksys LNE100TX v2.0, and a Netgear FA310TX card, and it worked at
both 10 and 100 mbits. Other cards based on the tulip family may work as
well.
These cards are about 20$US, are supported by Linux and now Etherboot,
and being PCI, they auto-configure IRQ and IOADDR and auto-negotiate
10/100 half/full duplex. It seems like a pretty good value compared to
some of the pricier cards, and can lower the cost of building/adapting
thin client workstations substantially while giving a considerable
performance increase.
On some PCI tulip clone chipsets (MX987x5, LC82C115, LC82C168) this driver
lets the card choose the fastest speed it can negotiate with the peer
device. On other cards, it chooses 10mbit half-duplex.
I burned an AM27C256 (32KByte) EPROM with mx987x5.lzrom and it worked.
According to the data sheet the MX98715A supports up to 64K (27C512)
EPROMs,
I've liberally commented the code and header files in the hope that it
will help the next person who hacks the code or needs to support some
tulip clone card, or wishes to add functionality.
Anyway, please test this if you can on your tulip based card, and let
me (mdc@thinguin.org) and the netboot list (netboot@baghira.han.de)
know how things go. I also would appreciate code review by people who
program. I'm a strong believer in "another set of eyes".
Regards,
Marty Connor
mdc@thinguin.org
http://www.thinguin.org/

1426
src/drivers/net/via-rhine.c Normal file

File diff suppressed because it is too large Load Diff

958
src/drivers/net/w89c840.c Normal file
View File

@@ -0,0 +1,958 @@
/*
* Etherboot - BOOTP/TFTP Bootstrap Program
*
* w89c840.c -- This file implements the winbond-840 driver for etherboot.
*
*/
/*
* Adapted by Igor V. Kovalenko
* -- <garrison@mail.ru>
* OR
* -- <iko@crec.mipt.ru>
* Initial adaptaion stage, including testing, completed 23 August 2000.
*/
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
* date version by what
* Written: Aug 20 2000 V0.10 iko Initial revision.
* changes: Aug 22 2000 V0.90 iko Works!
* Aug 23 2000 V0.91 iko Cleanup, posted to etherboot
* maintainer.
* Aug 26 2000 V0.92 iko Fixed Rx ring handling.
* First Linux Kernel (TM)
* successfully loaded using
* this driver.
* Jan 07 2001 V0.93 iko Transmitter timeouts are handled
* using timer2 routines. Proposed
* by Ken Yap to eliminate CPU speed
* dependency.
* Dec 12 2003 V0.94 timlegge Fixed issues in 5.2, removed
* interrupt usage, enabled
* multicast support
*
* This is the etherboot driver for cards based on Winbond W89c840F chip.
*
* It was written from skeleton source, with Donald Becker's winbond-840.c
* kernel driver as a guideline. Mostly the w89c840 related definitions
* and the lower level routines have been cut-and-pasted into this source.
*
* Frankly speaking, about 90% of the code was obtained using cut'n'paste
* sequence :) while the remainder appeared while brainstorming
* Linux Kernel 2.4.0-testX source code. Thanks, Donald and Linus!
*
* There was a demand for using this card in a rather large
* remote boot environment at MSKP OVTI Lab of
* Moscow Institute for Physics and Technology (MIPT) -- http://www.mipt.ru/
* so you may count that for motivation.
*
*/
/*
* If you want to see debugging output then define W89C840_DEBUG
*/
/*
#define W89C840_DEBUG
*/
/*
* Keep using IO_OPS for Etherboot driver!
*/
#define USE_IO_OPS
#include "etherboot.h"
#include "nic.h"
#include "pci.h"
#include "timer.h"
static const char *w89c840_version = "driver Version 0.94 - December 12, 2003";
typedef unsigned char u8;
typedef signed char s8;
typedef unsigned short u16;
typedef signed short s16;
typedef unsigned int u32;
typedef signed int s32;
/* Linux support functions */
#define virt_to_le32desc(addr) virt_to_bus(addr)
#define le32desc_to_virt(addr) bus_to_virt(addr)
/*
#define cpu_to_le32(val) (val)
#define le32_to_cpu(val) (val)
*/
/* Operational parameters that are set at compile time. */
/* Keep the ring sizes a power of two for compile efficiency.
The compiler will convert <unsigned>'%'<2^N> into a bit mask.
Making the Tx ring too large decreases the effectiveness of channel
bonding and packet priority.
There are no ill effects from too-large receive rings. */
#define TX_RING_SIZE 2
#define RX_RING_SIZE 2
/* The presumed FIFO size for working around the Tx-FIFO-overflow bug.
To avoid overflowing we don't queue again until we have room for a
full-size packet.
*/
#define TX_FIFO_SIZE (2048)
#define TX_BUG_FIFO_LIMIT (TX_FIFO_SIZE-1514-16)
/* Operational parameters that usually are not changed. */
/* Time in jiffies before concluding the transmitter is hung. */
#define TX_TIMEOUT (10*TICKS_PER_MS)
#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer.*/
/*
* Used to be this much CPU loops on Celeron@400 (?),
* now using real timer and TX_TIMEOUT!
* #define TX_LOOP_COUNT 10000000
*/
#if !defined(__OPTIMIZE__)
#warning You must compile this file with the correct options!
#warning See the last lines of the source file.
#error You must compile this driver with "-O".
#endif
enum chip_capability_flags {CanHaveMII=1, HasBrokenTx=2};
#ifdef USE_IO_OPS
#define W840_FLAGS (PCI_USES_IO | PCI_ADDR0 | PCI_USES_MASTER)
#else
#define W840_FLAGS (PCI_USES_MEM | PCI_ADDR1 | PCI_USES_MASTER)
#endif
static u32 driver_flags = CanHaveMII | HasBrokenTx;
/* This driver was written to use PCI memory space, however some x86 systems
work only with I/O space accesses. Pass -DUSE_IO_OPS to use PCI I/O space
accesses instead of memory space. */
#ifdef USE_IO_OPS
#undef readb
#undef readw
#undef readl
#undef writeb
#undef writew
#undef writel
#define readb inb
#define readw inw
#define readl inl
#define writeb outb
#define writew outw
#define writel outl
#endif
/* Offsets to the Command and Status Registers, "CSRs".
While similar to the Tulip, these registers are longword aligned.
Note: It's not useful to define symbolic names for every register bit in
the device. The name can only partially document the semantics and make
the driver longer and more difficult to read.
*/
enum w840_offsets {
PCIBusCfg=0x00, TxStartDemand=0x04, RxStartDemand=0x08,
RxRingPtr=0x0C, TxRingPtr=0x10,
IntrStatus=0x14, NetworkConfig=0x18, IntrEnable=0x1C,
RxMissed=0x20, EECtrl=0x24, MIICtrl=0x24, BootRom=0x28, GPTimer=0x2C,
CurRxDescAddr=0x30, CurRxBufAddr=0x34, /* Debug use */
MulticastFilter0=0x38, MulticastFilter1=0x3C, StationAddr=0x40,
CurTxDescAddr=0x4C, CurTxBufAddr=0x50,
};
/* Bits in the interrupt status/enable registers. */
/* The bits in the Intr Status/Enable registers, mostly interrupt sources. */
enum intr_status_bits {
NormalIntr=0x10000, AbnormalIntr=0x8000,
IntrPCIErr=0x2000, TimerInt=0x800,
IntrRxDied=0x100, RxNoBuf=0x80, IntrRxDone=0x40,
TxFIFOUnderflow=0x20, RxErrIntr=0x10,
TxIdle=0x04, IntrTxStopped=0x02, IntrTxDone=0x01,
};
/* Bits in the NetworkConfig register. */
enum rx_mode_bits {
AcceptErr=0x80, AcceptRunt=0x40,
AcceptBroadcast=0x20, AcceptMulticast=0x10,
AcceptAllPhys=0x08, AcceptMyPhys=0x02,
};
enum mii_reg_bits {
MDIO_ShiftClk=0x10000, MDIO_DataIn=0x80000, MDIO_DataOut=0x20000,
MDIO_EnbOutput=0x40000, MDIO_EnbIn = 0x00000,
};
/* The Tulip Rx and Tx buffer descriptors. */
struct w840_rx_desc {
s32 status;
s32 length;
u32 buffer1;
u32 next_desc;
};
struct w840_tx_desc {
s32 status;
s32 length;
u32 buffer1, buffer2; /* We use only buffer 1. */
};
/* Bits in network_desc.status */
enum desc_status_bits {
DescOwn=0x80000000, DescEndRing=0x02000000, DescUseLink=0x01000000,
DescWholePkt=0x60000000, DescStartPkt=0x20000000, DescEndPkt=0x40000000,
DescIntr=0x80000000,
};
#define PRIV_ALIGN 15 /* Required alignment mask */
#define PRIV_ALIGN_BYTES 32
static struct winbond_private
{
/* Descriptor rings first for alignment. */
struct w840_rx_desc rx_ring[RX_RING_SIZE];
struct w840_tx_desc tx_ring[TX_RING_SIZE];
struct net_device *next_module; /* Link for devices of this type. */
void *priv_addr; /* Unaligned address for kfree */
const char *product_name;
/* Frequently used values: keep some adjacent for cache effect. */
int chip_id, drv_flags;
struct pci_dev *pci_dev;
int csr6;
struct w840_rx_desc *rx_head_desc;
unsigned int cur_rx, dirty_rx; /* Producer/consumer ring indices */
unsigned int rx_buf_sz; /* Based on MTU+slack. */
unsigned int cur_tx, dirty_tx;
int tx_q_bytes;
unsigned int tx_full:1; /* The Tx queue is full. */
/* These values are keep track of the transceiver/media in use. */
unsigned int full_duplex:1; /* Full-duplex operation requested. */
unsigned int duplex_lock:1;
unsigned int medialock:1; /* Do not sense media. */
unsigned int default_port:4; /* Last dev->if_port value. */
/* MII transceiver section. */
int mii_cnt; /* MII device addresses. */
u16 advertising; /* NWay media advertisement */
unsigned char phys[2]; /* MII device addresses. */
} w840private __attribute__ ((aligned (PRIV_ALIGN_BYTES)));
/* NIC specific static variables go here */
static int ioaddr;
static unsigned short eeprom [0x40];
static char rx_packet[PKT_BUF_SZ * RX_RING_SIZE];
static char tx_packet[PKT_BUF_SZ * TX_RING_SIZE];
static int eeprom_read(long ioaddr, int location);
static int mdio_read(int base_address, int phy_id, int location);
#if 0
static void mdio_write(int base_address, int phy_id, int location, int value);
#endif
static void check_duplex(void);
static void set_rx_mode(void);
static void init_ring(void);
#if defined(W89C840_DEBUG)
static void decode_interrupt(u32 intr_status)
{
printf("Interrupt status: ");
#define TRACE_INTR(_intr_) \
if (intr_status & (_intr_)) { printf (" " #_intr_); }
TRACE_INTR(NormalIntr);
TRACE_INTR(AbnormalIntr);
TRACE_INTR(IntrPCIErr);
TRACE_INTR(TimerInt);
TRACE_INTR(IntrRxDied);
TRACE_INTR(RxNoBuf);
TRACE_INTR(IntrRxDone);
TRACE_INTR(TxFIFOUnderflow);
TRACE_INTR(RxErrIntr);
TRACE_INTR(TxIdle);
TRACE_INTR(IntrTxStopped);
TRACE_INTR(IntrTxDone);
printf("\n");
/*sleep(1);*/
}
#endif
/**************************************************************************
w89c840_reset - Reset adapter
***************************************************************************/
static void w89c840_reset(struct nic *nic)
{
int i;
/* Reset the chip to erase previous misconfiguration.
No hold time required! */
writel(0x00000001, ioaddr + PCIBusCfg);
init_ring();
writel(virt_to_bus(w840private.rx_ring), ioaddr + RxRingPtr);
writel(virt_to_bus(w840private.tx_ring), ioaddr + TxRingPtr);
for (i = 0; i < ETH_ALEN; i++)
writeb(nic->node_addr[i], ioaddr + StationAddr + i);
/* Initialize other registers. */
/* Configure the PCI bus bursts and FIFO thresholds.
486: Set 8 longword cache alignment, 8 longword burst.
586: Set 16 longword cache alignment, no burst limit.
Cache alignment bits 15:14 Burst length 13:8
0000 <not allowed> 0000 align to cache 0800 8 longwords
4000 8 longwords 0100 1 longword 1000 16 longwords
8000 16 longwords 0200 2 longwords 2000 32 longwords
C000 32 longwords 0400 4 longwords
Wait the specified 50 PCI cycles after a reset by initializing
Tx and Rx queues and the address filter list. */
writel(0xE010, ioaddr + PCIBusCfg);
writel(0, ioaddr + RxStartDemand);
w840private.csr6 = 0x20022002;
check_duplex();
set_rx_mode();
/* Do not enable the interrupts Etherboot doesn't need them */
/*
writel(0x1A0F5, ioaddr + IntrStatus);
writel(0x1A0F5, ioaddr + IntrEnable);
*/
#if defined(W89C840_DEBUG)
printf("winbond-840 : Done reset.\n");
#endif
}
#if 0
static void handle_intr(u32 intr_stat)
{
if ((intr_stat & (NormalIntr|AbnormalIntr)) == 0) {
/* we are polling, do not return now */
/*return 0;*/
} else {
/* Acknowledge all of the current interrupt sources ASAP. */
writel(intr_stat & 0x001ffff, ioaddr + IntrStatus);
}
if (intr_stat & AbnormalIntr) {
/* There was an abnormal interrupt */
printf("\n-=- Abnormal interrupt.\n");
#if defined(W89C840_DEBUG)
decode_interrupt(intr_stat);
#endif
if (intr_stat & RxNoBuf) {
/* There was an interrupt */
printf("-=- <=> No receive buffers available.\n");
writel(0, ioaddr + RxStartDemand);
}
}
}
#endif
/**************************************************************************
w89c840_poll - Wait for a frame
***************************************************************************/
static int w89c840_poll(struct nic *nic, int retrieve)
{
/* return true if there's an ethernet packet ready to read */
/* nic->packet should contain data on return */
/* nic->packetlen should contain length of data */
int packet_received = 0;
#if defined(W89C840_DEBUG)
u32 intr_status = readl(ioaddr + IntrStatus);
#endif
do {
/* Code from netdev_rx(dev) */
int entry = w840private.cur_rx % RX_RING_SIZE;
struct w840_rx_desc *desc = w840private.rx_head_desc;
s32 status = desc->status;
if (status & DescOwn) {
/* DescOwn bit is still set, we should wait for RX to complete */
packet_received = 0;
break;
}
if ( !retrieve ) {
packet_received = 1;
break;
}
if ((status & 0x38008300) != 0x0300) {
if ((status & 0x38000300) != 0x0300) {
/* Ingore earlier buffers. */
if ((status & 0xffff) != 0x7fff) {
printf("winbond-840 : Oversized Ethernet frame spanned "
"multiple buffers, entry %d status %X !\n",
w840private.cur_rx, status);
}
} else if (status & 0x8000) {
/* There was a fatal error. */
#if defined(W89C840_DEBUG)
printf("winbond-840 : Receive error, Rx status %X :", status);
if (status & 0x0890) {
printf(" RXLEN_ERROR");
}
if (status & 0x004C) {
printf(", FRAME_ERROR");
}
if (status & 0x0002) {
printf(", CRC_ERROR");
}
printf("\n");
#endif
/* Simpy do a reset now... */
w89c840_reset(nic);
packet_received = 0;
break;
}
} else {
/* Omit the four octet CRC from the length. */
int pkt_len = ((status >> 16) & 0x7ff) - 4;
#if defined(W89C840_DEBUG)
printf(" netdev_rx() normal Rx pkt ring %d length %d status %X\n", entry, pkt_len, status);
#endif
nic->packetlen = pkt_len;
/* Check if the packet is long enough to accept without copying
to a minimally-sized skbuff. */
memcpy(nic->packet, le32desc_to_virt(w840private.rx_ring[entry].buffer1), pkt_len);
packet_received = 1;
/* Release buffer to NIC */
w840private.rx_ring[entry].status = DescOwn;
#if defined(W89C840_DEBUG)
/* You will want this info for the initial debug. */
printf(" Rx data %hhX:%hhX:%hhX:%hhX:%hhX:"
"%hhX %hhX:%hhX:%hhX:%hhX:%hhX:%hhX %hhX%hhX "
"%hhX.%hhX.%hhX.%hhX.\n",
nic->packet[0], nic->packet[1], nic->packet[2], nic->packet[3],
nic->packet[4], nic->packet[5], nic->packet[6], nic->packet[7],
nic->packet[8], nic->packet[9], nic->packet[10],
nic->packet[11], nic->packet[12], nic->packet[13],
nic->packet[14], nic->packet[15], nic->packet[16],
nic->packet[17]);
#endif
}
entry = (++w840private.cur_rx) % RX_RING_SIZE;
w840private.rx_head_desc = &w840private.rx_ring[entry];
} while (0);
return packet_received;
}
/**************************************************************************
w89c840_transmit - Transmit a frame
***************************************************************************/
static void w89c840_transmit(
struct nic *nic,
const char *d, /* Destination */
unsigned int t, /* Type */
unsigned int s, /* size */
const char *p) /* Packet */
{
/* send the packet to destination */
unsigned entry;
int transmit_status;
/* Caution: the write order is important here, set the field
with the "ownership" bits last. */
/* Fill in our transmit buffer */
entry = w840private.cur_tx % TX_RING_SIZE;
memcpy (tx_packet, d, ETH_ALEN); /* dst */
memcpy (tx_packet + ETH_ALEN, nic->node_addr, ETH_ALEN);/* src */
*((char *) tx_packet + 12) = t >> 8; /* type */
*((char *) tx_packet + 13) = t;
memcpy (tx_packet + ETH_HLEN, p, s);
s += ETH_HLEN;
while (s < ETH_ZLEN)
*((char *) tx_packet + ETH_HLEN + (s++)) = 0;
w840private.tx_ring[entry].buffer1 = virt_to_le32desc(tx_packet);
w840private.tx_ring[entry].length = (DescWholePkt | (u32) s);
if (entry >= TX_RING_SIZE-1) /* Wrap ring */
w840private.tx_ring[entry].length |= (DescIntr | DescEndRing);
w840private.tx_ring[entry].status = (DescOwn);
w840private.cur_tx++;
w840private.tx_q_bytes = (u16) s;
writel(0, ioaddr + TxStartDemand);
/* Work around horrible bug in the chip by marking the queue as full
when we do not have FIFO room for a maximum sized packet. */
if ((w840private.drv_flags & HasBrokenTx) && w840private.tx_q_bytes > TX_BUG_FIFO_LIMIT) {
/* Actually this is left to help finding error tails later in debugging...
* See Linux kernel driver in winbond-840.c for details.
*/
w840private.tx_full = 1;
}
#if defined(W89C840_DEBUG)
printf("winbond-840 : Transmit frame # %d size %d queued in slot %d.\n", w840private.cur_tx, s, entry);
#endif
/* Now wait for TX to complete. */
transmit_status = w840private.tx_ring[entry].status;
load_timer2(TX_TIMEOUT);
{
#if defined W89C840_DEBUG
u32 intr_stat = 0;
#endif
while (1) {
#if defined(W89C840_DEBUG)
decode_interrupt(intr_stat);
#endif
while ( (transmit_status & DescOwn) && timer2_running()) {
transmit_status = w840private.tx_ring[entry].status;
}
break;
}
}
if ((transmit_status & DescOwn) == 0) {
#if defined(W89C840_DEBUG)
printf("winbond-840 : transmission complete after wait loop iterations, status %X\n",
w840private.tx_ring[entry].status);
#endif
return;
}
/* Transmit timed out... */
printf("winbond-840 : transmission TIMEOUT : status %X\n", w840private.tx_ring[entry].status);
return;
}
/**************************************************************************
w89c840_disable - Turn off ethernet interface
***************************************************************************/
static void w89c840_disable(struct dev *dev)
{
struct nic *nic = (struct nic *)dev;
/* merge reset and disable */
w89c840_reset(nic);
/* Don't know what to do to disable the board. Is this needed at all? */
/* Yes, a live NIC can corrupt the loaded memory later [Ken] */
/* Stop the chip's Tx and Rx processes. */
writel(w840private.csr6 &= ~0x20FA, ioaddr + NetworkConfig);
}
/**************************************************************************
w89c840_irq - Enable, Disable, or Force interrupts
***************************************************************************/
static void w89c840_irq(struct nic *nic __unused, irq_action_t action __unused)
{
switch ( action ) {
case DISABLE :
break;
case ENABLE :
break;
case FORCE :
break;
}
}
/**************************************************************************
w89c840_probe - Look for an adapter, this routine's visible to the outside
***************************************************************************/
static int w89c840_probe(struct dev *dev, struct pci_device *p)
{
struct nic *nic = (struct nic *)dev;
u16 sum = 0;
int i, j;
unsigned short value;
if (p->ioaddr == 0)
return 0;
ioaddr = p->ioaddr;
nic->ioaddr = p->ioaddr & ~3;
nic->irqno = 0;
#if defined(W89C840_DEBUG)
printf("winbond-840: PCI bus %hhX device function %hhX: I/O address: %hX\n", p->bus, p->devfn, ioaddr);
#endif
ioaddr = ioaddr & ~3; /* Mask the bit that says "this is an io addr" */
#define PCI_DEVICE_ID_WINBOND2_89C840 0x0840
#define PCI_DEVICE_ID_COMPEX_RL100ATX 0x2011
/* From Matt Hortman <mbhortman@acpthinclient.com> */
if (p->vendor == PCI_VENDOR_ID_WINBOND2
&& p->dev_id == PCI_DEVICE_ID_WINBOND2_89C840) {
/* detected "Winbond W89c840 Fast Ethernet PCI NIC" */
} else if ( p->vendor == PCI_VENDOR_ID_COMPEX
&& p->dev_id == PCI_DEVICE_ID_COMPEX_RL100ATX) {
/* detected "Compex RL100ATX Fast Ethernet PCI NIC" */
} else {
/* Gee, guess what? They missed again. */
printf("device ID : %X - is not a Compex RL100ATX NIC.\n", p->dev_id);
return 0;
}
printf(" %s\n", w89c840_version);
adjust_pci_device(p);
/* Ok. Got one. Read the eeprom. */
for (j = 0, i = 0; i < 0x40; i++) {
value = eeprom_read(ioaddr, i);
eeprom[i] = value;
sum += value;
}
for (i=0;i<ETH_ALEN;i++) {
nic->node_addr[i] = (eeprom[i/2] >> (8*(i&1))) & 0xff;
}
printf ("Ethernet addr: %!\n", nic->node_addr);
#if defined(W89C840_DEBUG)
printf("winbond-840: EEPROM checksum %hX, got eeprom", sum);
#endif
/* Reset the chip to erase previous misconfiguration.
No hold time required! */
writel(0x00000001, ioaddr + PCIBusCfg);
if (driver_flags & CanHaveMII) {
int phy, phy_idx = 0;
for (phy = 1; phy < 32 && phy_idx < 4; phy++) {
int mii_status = mdio_read(ioaddr, phy, 1);
if (mii_status != 0xffff && mii_status != 0x0000) {
w840private.phys[phy_idx++] = phy;
w840private.advertising = mdio_read(ioaddr, phy, 4);
#if defined(W89C840_DEBUG)
printf("winbond-840 : MII PHY found at address %d, status "
"%X advertising %hX.\n", phy, mii_status, w840private.advertising);
#endif
}
}
w840private.mii_cnt = phy_idx;
if (phy_idx == 0) {
printf("winbond-840 : MII PHY not found -- this device may not operate correctly.\n");
}
}
/* point to NIC specific routines */
dev->disable = w89c840_disable;
nic->poll = w89c840_poll;
nic->transmit = w89c840_transmit;
nic->irq = w89c840_irq;
w89c840_reset(nic);
return 1;
}
/* Read the EEPROM and MII Management Data I/O (MDIO) interfaces. These are
often serial bit streams generated by the host processor.
The example below is for the common 93c46 EEPROM, 64 16 bit words. */
/* Delay between EEPROM clock transitions.
No extra delay is needed with 33Mhz PCI, but future 66Mhz access may need
a delay. Note that pre-2.0.34 kernels had a cache-alignment bug that
made udelay() unreliable.
The old method of using an ISA access as a delay, __SLOW_DOWN_IO__, is
depricated.
*/
#define eeprom_delay(ee_addr) readl(ee_addr)
enum EEPROM_Ctrl_Bits {
EE_ShiftClk=0x02, EE_Write0=0x801, EE_Write1=0x805,
EE_ChipSelect=0x801, EE_DataIn=0x08,
};
/* The EEPROM commands include the alway-set leading bit. */
enum EEPROM_Cmds {
EE_WriteCmd=(5 << 6), EE_ReadCmd=(6 << 6), EE_EraseCmd=(7 << 6),
};
static int eeprom_read(long addr, int location)
{
int i;
int retval = 0;
int ee_addr = addr + EECtrl;
int read_cmd = location | EE_ReadCmd;
writel(EE_ChipSelect, ee_addr);
/* Shift the read command bits out. */
for (i = 10; i >= 0; i--) {
short dataval = (read_cmd & (1 << i)) ? EE_Write1 : EE_Write0;
writel(dataval, ee_addr);
eeprom_delay(ee_addr);
writel(dataval | EE_ShiftClk, ee_addr);
eeprom_delay(ee_addr);
}
writel(EE_ChipSelect, ee_addr);
for (i = 16; i > 0; i--) {
writel(EE_ChipSelect | EE_ShiftClk, ee_addr);
eeprom_delay(ee_addr);
retval = (retval << 1) | ((readl(ee_addr) & EE_DataIn) ? 1 : 0);
writel(EE_ChipSelect, ee_addr);
eeprom_delay(ee_addr);
}
/* Terminate the EEPROM access. */
writel(0, ee_addr);
return retval;
}
/* MII transceiver control section.
Read and write the MII registers using software-generated serial
MDIO protocol. See the MII specifications or DP83840A data sheet
for details.
The maximum data clock rate is 2.5 Mhz. The minimum timing is usually
met by back-to-back 33Mhz PCI cycles. */
#define mdio_delay(mdio_addr) readl(mdio_addr)
/* Set iff a MII transceiver on any interface requires mdio preamble.
This only set with older tranceivers, so the extra
code size of a per-interface flag is not worthwhile. */
static char mii_preamble_required = 1;
#define MDIO_WRITE0 (MDIO_EnbOutput)
#define MDIO_WRITE1 (MDIO_DataOut | MDIO_EnbOutput)
/* Generate the preamble required for initial synchronization and
a few older transceivers. */
static void mdio_sync(long mdio_addr)
{
int bits = 32;
/* Establish sync by sending at least 32 logic ones. */
while (--bits >= 0) {
writel(MDIO_WRITE1, mdio_addr);
mdio_delay(mdio_addr);
writel(MDIO_WRITE1 | MDIO_ShiftClk, mdio_addr);
mdio_delay(mdio_addr);
}
}
static int mdio_read(int base_address, int phy_id, int location)
{
long mdio_addr = base_address + MIICtrl;
int mii_cmd = (0xf6 << 10) | (phy_id << 5) | location;
int i, retval = 0;
if (mii_preamble_required)
mdio_sync(mdio_addr);
/* Shift the read command bits out. */
for (i = 15; i >= 0; i--) {
int dataval = (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0;
writel(dataval, mdio_addr);
mdio_delay(mdio_addr);
writel(dataval | MDIO_ShiftClk, mdio_addr);
mdio_delay(mdio_addr);
}
/* Read the two transition, 16 data, and wire-idle bits. */
for (i = 20; i > 0; i--) {
writel(MDIO_EnbIn, mdio_addr);
mdio_delay(mdio_addr);
retval = (retval << 1) | ((readl(mdio_addr) & MDIO_DataIn) ? 1 : 0);
writel(MDIO_EnbIn | MDIO_ShiftClk, mdio_addr);
mdio_delay(mdio_addr);
}
return (retval>>1) & 0xffff;
}
#if 0
static void mdio_write(int base_address, int phy_id, int location, int value)
{
long mdio_addr = base_address + MIICtrl;
int mii_cmd = (0x5002 << 16) | (phy_id << 23) | (location<<18) | value;
int i;
if (location == 4 && phy_id == w840private.phys[0])
w840private.advertising = value;
if (mii_preamble_required)
mdio_sync(mdio_addr);
/* Shift the command bits out. */
for (i = 31; i >= 0; i--) {
int dataval = (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0;
writel(dataval, mdio_addr);
mdio_delay(mdio_addr);
writel(dataval | MDIO_ShiftClk, mdio_addr);
mdio_delay(mdio_addr);
}
/* Clear out extra bits. */
for (i = 2; i > 0; i--) {
writel(MDIO_EnbIn, mdio_addr);
mdio_delay(mdio_addr);
writel(MDIO_EnbIn | MDIO_ShiftClk, mdio_addr);
mdio_delay(mdio_addr);
}
return;
}
#endif
static void check_duplex(void)
{
int mii_reg5 = mdio_read(ioaddr, w840private.phys[0], 5);
int negotiated = mii_reg5 & w840private.advertising;
int duplex;
if (w840private.duplex_lock || mii_reg5 == 0xffff)
return;
duplex = (negotiated & 0x0100) || (negotiated & 0x01C0) == 0x0040;
if (w840private.full_duplex != duplex) {
w840private.full_duplex = duplex;
#if defined(W89C840_DEBUG)
printf("winbond-840 : Setting %s-duplex based on MII # %d negotiated capability %X\n",
duplex ? "full" : "half", w840private.phys[0], negotiated);
#endif
w840private.csr6 &= ~0x200;
w840private.csr6 |= duplex ? 0x200 : 0;
}
}
static void set_rx_mode(void)
{
u32 mc_filter[2]; /* Multicast hash filter */
u32 rx_mode;
/* Accept all multicasts from now on. */
memset(mc_filter, 0xff, sizeof(mc_filter));
/*
* works OK with multicast enabled.
*/
rx_mode = AcceptBroadcast | AcceptMyPhys | AcceptMulticast;
writel(mc_filter[0], ioaddr + MulticastFilter0);
writel(mc_filter[1], ioaddr + MulticastFilter1);
w840private.csr6 &= ~0x00F8;
w840private.csr6 |= rx_mode;
writel(w840private.csr6, ioaddr + NetworkConfig);
#if defined(W89C840_DEBUG)
printf("winbond-840 : Done setting RX mode.\n");
#endif
}
/* Initialize the Rx and Tx rings, along with various 'dev' bits. */
static void init_ring(void)
{
int i;
char * p;
w840private.tx_full = 0;
w840private.tx_q_bytes = w840private.cur_rx = w840private.cur_tx = 0;
w840private.dirty_rx = w840private.dirty_tx = 0;
w840private.rx_buf_sz = PKT_BUF_SZ;
w840private.rx_head_desc = &w840private.rx_ring[0];
/* Initial all Rx descriptors. Fill in the Rx buffers. */
p = &rx_packet[0];
for (i = 0; i < RX_RING_SIZE; i++) {
w840private.rx_ring[i].length = w840private.rx_buf_sz;
w840private.rx_ring[i].status = 0;
w840private.rx_ring[i].next_desc = virt_to_le32desc(&w840private.rx_ring[i+1]);
w840private.rx_ring[i].buffer1 = virt_to_le32desc(p + (PKT_BUF_SZ * i));
w840private.rx_ring[i].status = DescOwn | DescIntr;
}
/* Mark the last entry as wrapping the ring. */
w840private.rx_ring[i-1].length |= DescEndRing;
w840private.rx_ring[i-1].next_desc = virt_to_le32desc(&w840private.rx_ring[0]);
w840private.dirty_rx = (unsigned int)(i - RX_RING_SIZE);
for (i = 0; i < TX_RING_SIZE; i++) {
w840private.tx_ring[i].status = 0;
}
return;
}
static struct pci_id w89c840_nics[] = {
PCI_ROM(0x1050, 0x0840, "winbond840", "Winbond W89C840F"),
PCI_ROM(0x11f6, 0x2011, "compexrl100atx", "Compex RL100ATX"),
};
static struct pci_driver w89c840_driver __pci_driver = {
.type = NIC_DRIVER,
.name = "W89C840F",
.probe = w89c840_probe,
.ids = w89c840_nics,
.id_count = sizeof(w89c840_nics)/sizeof(w89c840_nics[0]),
.class = 0,
};

View File

@@ -0,0 +1,575 @@
/* src/include/wlan/wlan_compat.h
*
* Types and macros to aid in portability
*
* Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved.
* --------------------------------------------------------------------
*
* linux-wlan
*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU Public License version 2 (the "GPL"), in which
* case the provisions of the GPL are applicable instead of the
* above. If you wish to allow the use of your version of this file
* only under the terms of the GPL and not to allow others to use
* your version of this file under the MPL, indicate your decision
* by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL. If you do not delete
* the provisions above, a recipient may use your version of this
* file under either the MPL or the GPL.
*
* --------------------------------------------------------------------
*
* Inquiries regarding the linux-wlan Open Source project can be
* made directly to:
*
* AbsoluteValue Systems Inc.
* info@linux-wlan.com
* http://www.linux-wlan.com
*
* --------------------------------------------------------------------
*
* Portions of the development of this software were funded by
* Intersil Corporation as part of PRISM(R) chipset product development.
*
* --------------------------------------------------------------------
*/
#ifndef _WLAN_COMPAT_H
#define _WLAN_COMPAT_H
/*=============================================================*/
/*------ Establish Platform Identity --------------------------*/
/*=============================================================*/
/* Key macros: */
/* WLAN_CPU_FAMILY */
#define WLAN_Ix86 1
#define WLAN_PPC 2
#define WLAN_Ix96 3
#define WLAN_ARM 4
#define WLAN_ALPHA 5
#define WLAN_MIPS 6
#define WLAN_HPPA 7
/* WLAN_CPU_CORE */
#define WLAN_I386CORE 1
#define WLAN_PPCCORE 2
#define WLAN_I296 3
#define WLAN_ARMCORE 4
#define WLAN_ALPHACORE 5
#define WLAN_MIPSCORE 6
#define WLAN_HPPACORE 7
/* WLAN_CPU_PART */
#define WLAN_I386PART 1
#define WLAN_MPC860 2
#define WLAN_MPC823 3
#define WLAN_I296SA 4
#define WLAN_PPCPART 5
#define WLAN_ARMPART 6
#define WLAN_ALPHAPART 7
#define WLAN_MIPSPART 8
#define WLAN_HPPAPART 9
/* WLAN_SYSARCH */
#define WLAN_PCAT 1
#define WLAN_MBX 2
#define WLAN_RPX 3
#define WLAN_LWARCH 4
#define WLAN_PMAC 5
#define WLAN_SKIFF 6
#define WLAN_BITSY 7
#define WLAN_ALPHAARCH 7
#define WLAN_MIPSARCH 9
#define WLAN_HPPAARCH 10
/* WLAN_OS */
#define WLAN_LINUX_KERNEL 1
#define WLAN_LINUX_USER 2
/* WLAN_HOSTIF (generally set on the command line, not detected) */
#define WLAN_PCMCIA 1
#define WLAN_ISA 2
#define WLAN_PCI 3
#define WLAN_USB 4
#define WLAN_PLX 5
/* Note: the PLX HOSTIF above refers to some vendors implementations for */
/* PCI. It's a PLX chip that is a PCI to PCMCIA adapter, but it */
/* isn't a real PCMCIA host interface adapter providing all the */
/* card&socket services. */
/* Lets try to figure out what we've got. Kernel mode or User mode? */
#if defined(__KERNEL__)
#define WLAN_OS WLAN_LINUX_KERNEL
#else
#define WLAN_OS WLAN_LINUX_USER
#endif
#ifdef __powerpc__
#ifndef __ppc__
#define __ppc__
#endif
#endif
#if (defined(CONFIG_PPC) || defined(CONFIG_8xx))
#ifndef __ppc__
#define __ppc__
#endif
#endif
#if defined(__KERNEL__)
#if defined(__i386__) || defined(__i486__) || defined(__i586__) || defined(__i686__)
#define WLAN_CPU_FAMILY WLAN_Ix86
#define WLAN_CPU_CORE WLAN_I386CORE
#define WLAN_CPU_PART WLAN_I386PART
#define WLAN_SYSARCH WLAN_PCAT
#elif defined(__ppc__)
#define WLAN_CPU_FAMILY WLAN_PPC
#define WLAN_CPU_CORE WLAN_PPCCORE
#if defined(CONFIG_MBX)
#define WLAN_CPU_PART WLAN_MPC860
#define WLAN_SYSARCH WLAN_MBX
#elif defined(CONFIG_RPXLITE)
#define WLAN_CPU_PART WLAN_MPC823
#define WLAN_SYSARCH WLAN_RPX
#elif defined(CONFIG_RPXCLASSIC)
#define WLAN_CPU_PART WLAN_MPC860
#define WLAN_SYSARCH WLAN_RPX
#else
#define WLAN_CPU_PART WLAN_PPCPART
#define WLAN_SYSARCH WLAN_PMAC
#endif
#elif defined(__arm__)
#define WLAN_CPU_FAMILY WLAN_ARM
#define WLAN_CPU_CORE WLAN_ARMCORE
#define WLAN_CPU_PART WLAN_ARM_PART
#define WLAN_SYSARCH WLAN_SKIFF
#elif defined(__alpha__)
#define WLAN_CPU_FAMILY WLAN_ALPHA
#define WLAN_CPU_CORE WLAN_ALPHACORE
#define WLAN_CPU_PART WLAN_ALPHAPART
#define WLAN_SYSARCH WLAN_ALPHAARCH
#elif defined(__mips__)
#define WLAN_CPU_FAMILY WLAN_MIPS
#define WLAN_CPU_CORE WLAN_MIPSCORE
#define WLAN_CPU_PART WLAN_MIPSPART
#define WLAN_SYSARCH WLAN_MIPSARCH
#elif defined(__hppa__)
#define WLAN_CPU_FAMILY WLAN_HPPA
#define WLAN_CPU_CORE WLAN_HPPACORE
#define WLAN_CPU_PART WLAN_HPPAPART
#define WLAN_SYSARCH WLAN_HPPAARCH
#else
#error "No CPU identified!"
#endif
#endif /* __KERNEL__ */
/*
Some big endian machines implicitly do all I/O in little endian mode.
In particular:
Linux/PPC on PowerMacs (PCI)
Arm/Intel Xscale (PCI)
This may also affect PLX boards and other BE &| PPC platforms;
as new ones are discovered, add them below.
*/
#if (WLAN_HOSTIF == WLAN_PCI)
#if ((WLAN_SYSARCH == WLAN_SKIFF) || (WLAN_SYSARCH == WLAN_PMAC))
#define REVERSE_ENDIAN
#endif
#endif
/*=============================================================*/
/*------ Bit settings -----------------------------------------*/
/*=============================================================*/
#define BIT0 0x00000001
#define BIT1 0x00000002
#define BIT2 0x00000004
#define BIT3 0x00000008
#define BIT4 0x00000010
#define BIT5 0x00000020
#define BIT6 0x00000040
#define BIT7 0x00000080
#define BIT8 0x00000100
#define BIT9 0x00000200
#define BIT10 0x00000400
#define BIT11 0x00000800
#define BIT12 0x00001000
#define BIT13 0x00002000
#define BIT14 0x00004000
#define BIT15 0x00008000
#define BIT16 0x00010000
#define BIT17 0x00020000
#define BIT18 0x00040000
#define BIT19 0x00080000
#define BIT20 0x00100000
#define BIT21 0x00200000
#define BIT22 0x00400000
#define BIT23 0x00800000
#define BIT24 0x01000000
#define BIT25 0x02000000
#define BIT26 0x04000000
#define BIT27 0x08000000
#define BIT28 0x10000000
#define BIT29 0x20000000
#define BIT30 0x40000000
#define BIT31 0x80000000
typedef unsigned char UINT8;
typedef unsigned short UINT16;
typedef unsigned long UINT32;
typedef signed char INT8;
typedef signed short INT16;
typedef signed long INT32;
typedef unsigned int UINT;
typedef signed int INT;
typedef unsigned long long UINT64;
typedef signed long long INT64;
#define UINT8_MAX (0xffUL)
#define UINT16_MAX (0xffffUL)
#define UINT32_MAX (0xffffffffUL)
#define INT8_MAX (0x7fL)
#define INT16_MAX (0x7fffL)
#define INT32_MAX (0x7fffffffL)
/*=============================================================*/
/*------ Compiler Portability Macros --------------------------*/
/*=============================================================*/
#define __WLAN_ATTRIB_PACK__ __attribute__ ((packed))
#define __WLAN_PRAGMA_PACK1__
#define __WLAN_PRAGMA_PACKDFLT__
#define __WLAN_INLINE__ inline
#define WLAN_MIN_ARRAY 0
/*=============================================================*/
/*------ OS Portability Macros --------------------------------*/
/*=============================================================*/
#ifndef WLAN_DBVAR
#define WLAN_DBVAR wlan_debug
#endif
#if (WLAN_OS == WLAN_LINUX_KERNEL)
#define WLAN_LOG_ERROR0(x) printk(KERN_ERR "%s: " x , __FUNCTION__ );
#define WLAN_LOG_ERROR1(x,n) printk(KERN_ERR "%s: " x , __FUNCTION__ , (n));
#define WLAN_LOG_ERROR2(x,n1,n2) printk(KERN_ERR "%s: " x , __FUNCTION__ , (n1), (n2));
#define WLAN_LOG_ERROR3(x,n1,n2,n3) printk(KERN_ERR "%s: " x , __FUNCTION__, (n1), (n2), (n3));
#define WLAN_LOG_ERROR4(x,n1,n2,n3,n4) printk(KERN_ERR "%s: " x , __FUNCTION__, (n1), (n2), (n3), (n4));
#define WLAN_LOG_WARNING0(x) printk(KERN_WARNING "%s: " x , __FUNCTION__);
#define WLAN_LOG_WARNING1(x,n) printk(KERN_WARNING "%s: " x , __FUNCTION__, (n));
#define WLAN_LOG_WARNING2(x,n1,n2) printk(KERN_WARNING "%s: " x , __FUNCTION__, (n1), (n2));
#define WLAN_LOG_WARNING3(x,n1,n2,n3) printk(KERN_WARNING "%s: " x , __FUNCTION__, (n1), (n2), (n3));
#define WLAN_LOG_WARNING4(x,n1,n2,n3,n4) printk(KERN_WARNING "%s: " x , __FUNCTION__ , (n1), (n2), (n3), (n4));
#define WLAN_LOG_NOTICE0(x) printk(KERN_NOTICE "%s: " x , __FUNCTION__);
#define WLAN_LOG_NOTICE1(x,n) printk(KERN_NOTICE "%s: " x , __FUNCTION__, (n));
#define WLAN_LOG_NOTICE2(x,n1,n2) printk(KERN_NOTICE "%s: " x , __FUNCTION__, (n1), (n2));
#define WLAN_LOG_NOTICE3(x,n1,n2,n3) printk(KERN_NOTICE "%s: " x , __FUNCTION__, (n1), (n2), (n3));
#define WLAN_LOG_NOTICE4(x,n1,n2,n3,n4) printk(KERN_NOTICE "%s: " x , __FUNCTION__, (n1), (n2), (n3), (n4));
#define WLAN_LOG_INFO0(x) printk(KERN_INFO x);
#define WLAN_LOG_INFO1(x,n) printk(KERN_INFO x, (n));
#define WLAN_LOG_INFO2(x,n1,n2) printk(KERN_INFO x, (n1), (n2));
#define WLAN_LOG_INFO3(x,n1,n2,n3) printk(KERN_INFO x, (n1), (n2), (n3));
#define WLAN_LOG_INFO4(x,n1,n2,n3,n4) printk(KERN_INFO x, (n1), (n2), (n3), (n4));
#define WLAN_LOG_INFO5(x,n1,n2,n3,n4,n5) printk(KERN_INFO x, (n1), (n2), (n3), (n4), (n5));
#if defined(WLAN_INCLUDE_DEBUG)
#define WLAN_ASSERT(c) if ((!(c)) && WLAN_DBVAR >= 1) { \
WLAN_LOG_DEBUG0(1, "Assertion failure!\n"); }
#define WLAN_HEX_DUMP( l, x, p, n) if( WLAN_DBVAR >= (l) ){ \
int __i__; \
printk(KERN_DEBUG x ":"); \
for( __i__=0; __i__ < (n); __i__++) \
printk( " %02x", ((UINT8*)(p))[__i__]); \
printk("\n"); }
#define DBFENTER { if ( WLAN_DBVAR >= 4 ){ WLAN_LOG_DEBUG0(3,"Enter\n"); } }
#define DBFEXIT { if ( WLAN_DBVAR >= 4 ){ WLAN_LOG_DEBUG0(3,"Exit\n"); } }
#define WLAN_LOG_DEBUG0(l,x) if ( WLAN_DBVAR >= (l)) printk(KERN_DEBUG "%s: " x , __FUNCTION__ );
#define WLAN_LOG_DEBUG1(l,x,n) if ( WLAN_DBVAR >= (l)) printk(KERN_DEBUG "%s: " x , __FUNCTION__ , (n));
#define WLAN_LOG_DEBUG2(l,x,n1,n2) if ( WLAN_DBVAR >= (l)) printk(KERN_DEBUG "%s: " x , __FUNCTION__ , (n1), (n2));
#define WLAN_LOG_DEBUG3(l,x,n1,n2,n3) if ( WLAN_DBVAR >= (l)) printk(KERN_DEBUG "%s: " x , __FUNCTION__ , (n1), (n2), (n3));
#define WLAN_LOG_DEBUG4(l,x,n1,n2,n3,n4) if ( WLAN_DBVAR >= (l)) printk(KERN_DEBUG "%s: " x , __FUNCTION__ , (n1), (n2), (n3), (n4));
#define WLAN_LOG_DEBUG5(l,x,n1,n2,n3,n4,n5) if ( WLAN_DBVAR >= (l)) printk(KERN_DEBUG "%s: " x , __FUNCTION__ , (n1), (n2), (n3), (n4), (n5));
#define WLAN_LOG_DEBUG6(l,x,n1,n2,n3,n4,n5,n6) if ( WLAN_DBVAR >= (l)) printk(KERN_DEBUG "%s: " x , __FUNCTION__ , (n1), (n2), (n3), (n4), (n5), (n6));
#else
#define WLAN_ASSERT(c)
#define WLAN_HEX_DUMP( l, s, p, n)
#define DBFENTER
#define DBFEXIT
#define WLAN_LOG_DEBUG0(l, s)
#define WLAN_LOG_DEBUG1(l, s,n)
#define WLAN_LOG_DEBUG2(l, s,n1,n2)
#define WLAN_LOG_DEBUG3(l, s,n1,n2,n3)
#define WLAN_LOG_DEBUG4(l, s,n1,n2,n3,n4)
#define WLAN_LOG_DEBUG5(l, s,n1,n2,n3,n4,n5)
#endif
#else
#define WLAN_LOG_ERROR0(s)
#define WLAN_LOG_ERROR1(s,n)
#define WLAN_LOG_ERROR2(s,n1,n2)
#define WLAN_LOG_ERROR3(s,n1,n2,n3)
#define WLAN_LOG_ERROR4(s,n1,n2,n3,n4)
#define WLAN_LOG_WARNING0(s)
#define WLAN_LOG_WARNING1(s,n)
#define WLAN_LOG_WARNING2(s,n1,n2)
#define WLAN_LOG_WARNING3(s,n1,n2,n3)
#define WLAN_LOG_WARNING4(s,n1,n2,n3,n4)
#define WLAN_LOG_NOTICE0(s)
#define WLAN_LOG_NOTICE1(s,n)
#define WLAN_LOG_NOTICE2(s,n1,n2)
#define WLAN_LOG_NOTICE3(s,n1,n2,n3)
#define WLAN_LOG_NOTICE4(s,n1,n2,n3,n4)
#define WLAN_ASSERT(c)
#define WLAN_HEX_DUMP( l, s, p, n)
#define DBFENTER
#define DBFEXIT
#define WLAN_LOG_INFO0(s)
#define WLAN_LOG_INFO1(s,n)
#define WLAN_LOG_INFO2(s,n1,n2)
#define WLAN_LOG_INFO3(s,n1,n2,n3)
#define WLAN_LOG_INFO4(s,n1,n2,n3,n4)
#define WLAN_LOG_INFO5(s,n1,n2,n3,n4,n5)
#define WLAN_LOG_DEBUG0(l, s)
#define WLAN_LOG_DEBUG1(l, s,n)
#define WLAN_LOG_DEBUG2(l, s,n1,n2)
#define WLAN_LOG_DEBUG3(l, s,n1,n2,n3)
#define WLAN_LOG_DEBUG4(l, s,n1,n2,n3,n4)
#define WLAN_LOG_DEBUG5(l, s,n1,n2,n3,n4,n5)
#endif
#define wlan_ms_per_tick (1000UL / (wlan_ticks_per_sec))
#define wlan_ms_to_ticks(n) ( (n) / (wlan_ms_per_tick))
#define wlan_tu2ticks(n) ( (n) / (wlan_ms_per_tick))
#define WLAN_INT_DISABLE(n) { save_flags((n)); cli(); }
#define WLAN_INT_ENABLE(n) { sti(); restore_flags((n)); }
#ifdef CONFIG_MODVERSIONS
#define MODVERSIONS 1
#include <linux/modversions.h>
#endif
#ifdef CONFIG_SMP
#define __SMP__ 1
#endif
#ifndef KERNEL_VERSION
#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
#endif
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,17))
#define CONFIG_NETLINK 1
#endif
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0))
#define kfree_s(a, b) kfree((a))
#endif
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18))
#ifndef init_waitqueue_head
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,0,16))
#define init_waitqueue_head(p) (*(p) = NULL)
#else
#define init_waitqueue_head(p) init_waitqueue(p)
#endif
typedef struct wait_queue *wait_queue_head_t;
typedef struct wait_queue wait_queue_t;
#define set_current_state(b) { current->state = (b); mb(); }
#define init_waitqueue_entry(a, b) { (a)->task = current; }
#endif
#endif
#ifndef wait_event_interruptible_timeout
// retval == 0; signal met; we're good.
// retval < 0; interrupted by signal.
// retval > 0; timed out.
#define __wait_event_interruptible_timeout(wq, condition, timeout, ret) \
do { \
int __ret = 0; \
if (!(condition)) { \
wait_queue_t __wait; \
unsigned long expire; \
init_waitqueue_entry(&__wait, current); \
\
expire = timeout + jiffies; \
add_wait_queue(&wq, &__wait); \
for (;;) { \
set_current_state(TASK_INTERRUPTIBLE); \
if (condition) \
break; \
if (jiffies > expire) { \
ret = jiffies - expire; \
break; \
} \
if (!signal_pending(current)) { \
schedule_timeout(timeout); \
continue; \
} \
ret = -ERESTARTSYS; \
break; \
} \
set_current_state(TASK_RUNNING); \
remove_wait_queue(&wq, &__wait); \
} \
} while (0)
#define wait_event_interruptible_timeout(wq, condition, timeout) \
({ \
int __ret = 0; \
if (!(condition)) \
__wait_event_interruptible_timeout(wq, condition, \
timeout, __ret); \
__ret; \
})
#endif
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,90))
#define spin_lock(l) do { } while (0)
#define spin_unlock(l) do { } while (0)
#define spin_lock_irqsave(l,f) do { save_flags(f); cli(); } while (0)
#define spin_unlock_irqrestore(l,f) do { restore_flags(f); } while (0)
#define spin_lock_init(s) do { } while (0)
#define spin_trylock(l) (1)
typedef int spinlock_t;
#endif
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0))
#ifdef CONFIG_SMP
#define spin_is_locked(x) (*(volatile char *)(&(x)->lock) <= 0)
#else
#define spin_is_locked(l) (0)
#endif
#endif
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,38))
typedef struct device netdevice_t;
#elif (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4))
typedef struct net_device netdevice_t;
#else
#undef netdevice_t
typedef struct net_device netdevice_t;
#endif
#ifdef WIRELESS_EXT
#if (WIRELESS_EXT < 13)
struct iw_request_info
{
__u16 cmd; /* Wireless Extension command */
__u16 flags; /* More to come ;-) */
};
#endif
#endif
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,18))
#define MODULE_PARM(a,b) extern int __bogus_decl
#define MODULE_AUTHOR(a) extern int __bogus_decl
#define MODULE_DESCRIPTION(a) extern int __bogus_decl
#define MODULE_SUPPORTED_DEVICE(a) extern int __bogus_decl
#undef GET_USE_COUNT
#define GET_USE_COUNT(m) mod_use_count_
#endif
#ifndef MODULE_LICENSE
#define MODULE_LICENSE(m) extern int __bogus_decl
#endif
/* TODO: Do we care about this? */
#ifndef MODULE_DEVICE_TABLE
#define MODULE_DEVICE_TABLE(foo,bar)
#endif
#define wlan_minutes2ticks(a) ((a)*(wlan_ticks_per_sec * 60))
#define wlan_seconds2ticks(a) ((a)*(wlan_ticks_per_sec))
/*=============================================================*/
/*------ Hardware Portability Macros --------------------------*/
/*=============================================================*/
#define ieee2host16(n) __le16_to_cpu(n)
#define ieee2host32(n) __le32_to_cpu(n)
#define host2ieee16(n) __cpu_to_le16(n)
#define host2ieee32(n) __cpu_to_le32(n)
#if (WLAN_CPU_FAMILY == WLAN_PPC)
#define wlan_inw(a) in_be16((unsigned short *)((a)+_IO_BASE))
#define wlan_inw_le16_to_cpu(a) inw((a))
#define wlan_outw(v,a) out_be16((unsigned short *)((a)+_IO_BASE), (v))
#define wlan_outw_cpu_to_le16(v,a) outw((v),(a))
#else
#define wlan_inw(a) inw((a))
#define wlan_inw_le16_to_cpu(a) __cpu_to_le16(inw((a)))
#define wlan_outw(v,a) outw((v),(a))
#define wlan_outw_cpu_to_le16(v,a) outw(__cpu_to_le16((v)),(a))
#endif
/*=============================================================*/
/*--- General Macros ------------------------------------------*/
/*=============================================================*/
#define wlan_max(a, b) (((a) > (b)) ? (a) : (b))
#define wlan_min(a, b) (((a) < (b)) ? (a) : (b))
#define wlan_isprint(c) (((c) > (0x19)) && ((c) < (0x7f)))
#define wlan_hexchar(x) (((x) < 0x0a) ? ('0' + (x)) : ('a' + ((x) - 0x0a)))
/* Create a string of printable chars from something that might not be */
/* It's recommended that the str be 4*len + 1 bytes long */
#define wlan_mkprintstr(buf, buflen, str, strlen) \
{ \
int i = 0; \
int j = 0; \
memset(str, 0, (strlen)); \
for (i = 0; i < (buflen); i++) { \
if ( wlan_isprint((buf)[i]) ) { \
(str)[j] = (buf)[i]; \
j++; \
} else { \
(str)[j] = '\\'; \
(str)[j+1] = 'x'; \
(str)[j+2] = wlan_hexchar(((buf)[i] & 0xf0) >> 4); \
(str)[j+3] = wlan_hexchar(((buf)[i] & 0x0f)); \
j += 4; \
} \
} \
}
/*=============================================================*/
/*--- Variables -----------------------------------------------*/
/*=============================================================*/
extern int wlan_debug;
extern int wlan_ethconv; /* What's the default ethconv? */
/*=============================================================*/
/*--- Functions -----------------------------------------------*/
/*=============================================================*/
#endif /* _WLAN_COMPAT_H */