918 lines
32 KiB
C
918 lines
32 KiB
C
/*
|
|
* Copyright (C) yajin 2008 <yajinzhou@gmail.com >
|
|
*
|
|
* This file is part of the virtualmips distribution.
|
|
* See LICENSE file for terms of the license.
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* cs8900 net card emulation.
|
|
* (jz4740 driver).
|
|
* Only works in linux 2.6.24/2.6.22/2.4.20
|
|
* uboot can not use it.
|
|
*
|
|
* Please use TCP instead of UDP when using NFS.
|
|
* Throughput is about 50k-100k bytes per second when downloading a file from host using http.
|
|
* Maybe improved when JIT is implemented in the future.
|
|
*
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdarg.h>
|
|
#include <unistd.h>
|
|
#include <time.h>
|
|
#include <errno.h>
|
|
#include <assert.h>
|
|
|
|
#include "crc.h"
|
|
#include "utils.h"
|
|
#include "cpu.h"
|
|
#include "vm.h"
|
|
#include "mips_memory.h"
|
|
#include "device.h"
|
|
#include "net.h"
|
|
#include "net_io.h"
|
|
#include "dev_cs8900.h"
|
|
|
|
/*#define QUEUE_SIZE 128
|
|
#define PACKET_LEN 1600
|
|
m_uint8_t recv_buffer[QUEUE_SIZE][PACKET_LEN];
|
|
m_uint8_t packet_len[QUEUE_SIZE];
|
|
|
|
m_uint8_t read_index=0;
|
|
m_uint8_t write_index=0;
|
|
*/
|
|
|
|
/*00:62:9c:61:cf:16*/
|
|
static uint8_t cs8900a_default_mac[6] =
|
|
{ 0x00, 0x62, 0x9c, 0x61, 0xcf, 0x16 };
|
|
|
|
#define CS8900_DEFAULT_RX_TIMEOUT 40
|
|
#define CS8900_MIN_RX_TIMEOUT 20
|
|
#define CS8900_MAX_RX_TIMEOUT 100
|
|
#define CS8900_RX_TIMEOUT_STEP 5
|
|
|
|
static m_uint32_t cs8900a_rx_timeout = CS8900_DEFAULT_RX_TIMEOUT;
|
|
|
|
/* Maximum packet size */
|
|
#define CS8900_MAX_PKT_SIZE 1518
|
|
#define CS8900_MIN_PKT_SIZE 8
|
|
#define CS8900_RUN_PKT_SIZE 64
|
|
|
|
#define CS8900A_PRODUCT_ID 0x630e /*little endian */
|
|
|
|
#define PP_RT_DATA0 0x00
|
|
#define PP_RT_DATA1 0x02
|
|
#define PP_TX_CMD 0X04
|
|
#define PP_TX_LEN 0X06
|
|
#define PP_IO_ISQ 0X08
|
|
#define PP_ADDRESS 0x0a /* PacketPage Pointer Port (Section 4.10.10) */
|
|
#define PP_DATA0 0x0c /* PacketPage Data Port (Section 4.10.10) */
|
|
#define PP_DATA1 0X0e
|
|
|
|
#define PP_ProductID 0x0000 /* Section 4.3.1 Product Identification Code */
|
|
#define PP_ISAIOB 0x0020 /* IO base address */
|
|
#define PP_IntNum 0x0022 /* Section 3.2.3 Interrupt Number */
|
|
#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_MemBase 0x002c /* Section 4.9.2 Memory Base Address Register */
|
|
#define PP_EEPROMCommand 0x0040 /* Section 4.3.11 EEPROM Command */
|
|
#define PP_EEPROMData 0x0042 /* Section 4.3.12 EEPROM Data */
|
|
|
|
#define PP_RxCFG 0x0102 /* Section 4.4.6 Receiver Configuration */
|
|
#define PP_RxCTL 0x0104 /* Section 4.4.8 Receiver Control */
|
|
#define PP_TxCFG 0x0106 /* Section 4.4.9 Transmit Configuration */
|
|
#define PP_BufCFG 0x010a /* Section 4.4.12 Buffer Configuration */
|
|
#define PP_LineCTL 0x0112 /* Section 4.4.16 Line Control */
|
|
#define PP_SelfCTL 0x0114 /* Section 4.4.18 Self Control */
|
|
#define PP_BusCTL 0x0116 /* Section 4.4.20 Bus Control */
|
|
#define PP_TestCTL 0x0118 /* Section 4.4.22 Test Control */
|
|
#define PP_AutoNegCTL 0x011C /* Auto Negotiation Ctrl */
|
|
#define PP_ISQ 0x0120 /* Section 4.4.5 Interrupt Status Queue */
|
|
#define PP_RxEvent 0x0124 /* Rx Event Register */
|
|
#define PP_TxEvent 0x0128 /* Section 4.4.10 Transmitter Event */
|
|
#define PP_BufEvent 0x012c /* Section 4.4.13 Buffer Event */
|
|
#define PP_RxMISS 0x0130 /* Section 4.4.14 Receiver Miss Counter */
|
|
#define PP_TxCOL 0x0132 /* Section 4.4.15 Transmit Collision Counter */
|
|
#define PP_LineST 0x0134 /* Line State Register */
|
|
#define PP_SelfST 0x0136 /* Section 4.4.19 Self Status */
|
|
#define PP_BusST 0x0138 /* Section 4.4.21 Bus Status */
|
|
#define PP_TDR 0x013C /* Time Domain Reflectometry */
|
|
#define PP_AutoNegST 0x013E /* Auto Neg Status */
|
|
#define PP_TxCMD 0x0144 /* Section 4.4.11 Transmit Command */
|
|
#define PP_TxLength 0x0146 /* Section 4.5.2 Transmit Length */
|
|
#define PP_LAF 0x0150 /* Hash Table */
|
|
#define PP_IA 0x0158 /* Section 4.6.2 Individual Address (IEEE Address) */
|
|
|
|
#define PP_RxStatus 0x0400 /* Section 4.7.1 Receive Status */
|
|
#define PP_RxLength 0x0402 /* Section 4.7.1 Receive Length (in bytes) */
|
|
#define PP_RxFrame 0x0404 /* Section 4.7.2 Receive Frame Location */
|
|
#define PP_TxFrame 0x0a00 /* Section 4.7.2 Transmit Frame Location */
|
|
|
|
/* PP_RxCFG */
|
|
#define Skip_1 0x0040
|
|
#define StreamE 0x0080
|
|
#define RxOKiE 0x0100
|
|
#define RxDMAonly 0x0200
|
|
#define AutoRxDMAE 0x0400
|
|
#define BufferCRC 0x0800
|
|
#define CRCerroriE 0x1000
|
|
#define RuntiE 0x2000
|
|
#define ExtradataiE 0x4000
|
|
|
|
/* PP_TxCFG */
|
|
#define Loss_of_CRSiE 0x0040
|
|
#define SQErroriE 0x0080
|
|
#define TxOKiE 0x0100
|
|
#define Out_of_windowiE 0x0200
|
|
#define JabberiE 0x0400
|
|
#define AnycolliE 0x0800
|
|
#define T16colliE 0x8000
|
|
|
|
/* PP_BufCFG */
|
|
#define SWint_X 0x0040
|
|
#define RxDMAiE 0x0080
|
|
#define Rdy4TxiE 0x0100
|
|
#define TxUnderruniE 0x0200
|
|
#define RxMissiE 0x0400
|
|
#define Rx128iE 0x0800
|
|
#define TxColOvfiE 0x1000
|
|
#define MissOvfloiE 0x2000
|
|
#define RxDestiE 0x8000
|
|
|
|
/* PP_RxCTL */
|
|
#define IAHashA 0x0040
|
|
#define PromiscuousA 0x0080
|
|
#define RxOKA 0x0100
|
|
#define MulticastA 0x0200
|
|
#define IndividualA 0x0400
|
|
#define BroadcastA 0x0800
|
|
#define CRCerrorA 0x1000
|
|
#define RuntA 0x2000
|
|
#define ExtradataA 0x4000
|
|
|
|
/* PP_SelfCTL */
|
|
#define RESET 0x0040
|
|
#define SWSuspend 0x0100
|
|
#define HWSleepE 0x0200
|
|
#define HWStandbyE 0x0400
|
|
#define HC0E 0x1000
|
|
#define HC1E 0x2000
|
|
#define HCB0 0x4000
|
|
#define HCB1 0x8000
|
|
|
|
/* PP_LineCTL */
|
|
#define SerRxON 0x0040
|
|
#define SerTxON 0x0080
|
|
#define AUIonly 0x0100
|
|
#define AutoAUI_10BT 0x0200
|
|
#define ModBackoffE 0x0800
|
|
#define PolarityDis 0x1000
|
|
#define L2_partDefDis 0x2000
|
|
#define LoRxSquelch 0x4000
|
|
|
|
/* PP_TxEvent */
|
|
#define Loss_of_CRS 0x0040
|
|
#define SQEerror 0x0080
|
|
#define TxOK 0x0100
|
|
#define Out_of_window 0x0200
|
|
#define Jabber 0x0400
|
|
#define T16coll 0x8000
|
|
|
|
#define RxEvent 0x0004
|
|
#define TxEvent 0x0008
|
|
#define BufEvent 0x000c
|
|
#define RxMISS 0x0010
|
|
#define TxCOL 0x0012
|
|
|
|
/* PP_BufEvent */
|
|
#define SWint 0x0040
|
|
#define RxDMAFrame 0x0080
|
|
#define Rdy4Tx 0x0100
|
|
#define TxUnderrun 0x0200
|
|
#define RxMiss 0x0400
|
|
#define Rx128 0x0800
|
|
#define RxDest 0x8000
|
|
|
|
/* PP_TxCMD */
|
|
#define After5 0
|
|
#define After381 1
|
|
#define After1021 2
|
|
#define AfterAll 3
|
|
#define TxStart(x) ((x) << 6)
|
|
|
|
#define Force 0x0100
|
|
#define Onecoll 0x0200
|
|
#define InhibitCRC 0x1000
|
|
#define TxPadDis 0x2000
|
|
|
|
/* PP_BusST */
|
|
#define TxBidErr 0x0080
|
|
#define Rdy4TxNOW 0x0100
|
|
|
|
extern cpu_mips_t *current_cpu;
|
|
static void dev_cs8900_gen_interrupt (struct cs8900_data *d)
|
|
{
|
|
vm_instance_t *vm;
|
|
vm = d->vm;
|
|
|
|
/*must check RQ bit in 0x116 */
|
|
m_uint8_t *ram_base;
|
|
ram_base = (m_uint8_t *) (&(d->internal_ram[0]));
|
|
if ((*(m_uint16_t *) (ram_base + PP_BusCTL)) & (0x8000)) {
|
|
/*generate IRQ */
|
|
vm->set_irq (vm, d->irq_no);
|
|
}
|
|
}
|
|
|
|
static void dev_cs8900_init_defaultvalue (struct cs8900_data *d)
|
|
{
|
|
m_uint8_t *ram_base;
|
|
|
|
ram_base = (m_uint8_t *) (&(d->internal_ram[0]));
|
|
|
|
*(m_uint32_t *) (ram_base + PP_ProductID) = CS8900A_PRODUCT_ID;
|
|
*(m_uint16_t *) (ram_base + PP_ISAIOB) = 0x300;
|
|
*(m_uint16_t *) (ram_base + PP_IntNum) = 0x4;
|
|
*(m_uint16_t *) (ram_base + PP_IntNum) = 0x4;
|
|
|
|
*(m_uint16_t *) (ram_base + PP_RxCFG) = 0x3;
|
|
*(m_uint16_t *) (ram_base + PP_RxEvent) = 0x4;
|
|
|
|
*(m_uint16_t *) (ram_base + PP_RxCTL) = 0x5;
|
|
*(m_uint16_t *) (ram_base + PP_TxCFG) = 0x7;
|
|
*(m_uint16_t *) (ram_base + PP_TxEvent) = 0x8;
|
|
*(m_uint16_t *) (ram_base + 0x108) = 0x9;
|
|
|
|
*(m_uint16_t *) (ram_base + PP_BufCFG) = 0xb;
|
|
*(m_uint16_t *) (ram_base + PP_BufEvent) = 0xc;
|
|
|
|
*(m_uint16_t *) (ram_base + PP_RxMISS) = 0x10;
|
|
|
|
*(m_uint16_t *) (ram_base + PP_TxCOL) = 0x12;
|
|
*(m_uint16_t *) (ram_base + PP_LineCTL) = 0x13;
|
|
*(m_uint16_t *) (ram_base + PP_LineST) = 0x14;
|
|
*(m_uint16_t *) (ram_base + PP_SelfCTL) = 0x15;
|
|
|
|
*(m_uint16_t *) (ram_base + PP_SelfST) = 0x16;
|
|
*(m_uint16_t *) (ram_base + PP_BusCTL) = 0x17;
|
|
|
|
*(m_uint16_t *) (ram_base + PP_BusST) = 0x18;
|
|
*(m_uint16_t *) (ram_base + PP_TestCTL) = 0x19;
|
|
|
|
*(m_uint16_t *) (ram_base + PP_TDR) = 0x1c;
|
|
|
|
*(m_uint16_t *) (ram_base + PP_TxCMD) = 0x9;
|
|
|
|
*(ram_base + PP_IA) = cs8900a_default_mac[0];
|
|
*(ram_base + PP_IA + 1) = cs8900a_default_mac[1];
|
|
*(ram_base + PP_IA + 2) = cs8900a_default_mac[2];
|
|
*(ram_base + PP_IA + 3) = cs8900a_default_mac[3];
|
|
*(ram_base + PP_IA + 4) = cs8900a_default_mac[4];
|
|
*(ram_base + PP_IA + 5) = cs8900a_default_mac[5];
|
|
|
|
}
|
|
|
|
static void dev_cs8900_reset (cpu_mips_t * cpu, struct vdevice *dev)
|
|
{
|
|
struct cs8900_data *d = dev->priv_data;
|
|
memset (d->internal_ram, 0, sizeof (d->internal_ram));
|
|
dev_cs8900_init_defaultvalue (d);
|
|
}
|
|
|
|
/* Check if a packet must be delivered to the emulated chip based on length*/
|
|
static inline int cs8900_handle_len (struct cs8900_data *d, m_uint8_t * pkt,
|
|
ssize_t pkt_len)
|
|
{
|
|
/*we do not check CRC !!!! */
|
|
if (pkt_len < CS8900_MIN_PKT_SIZE)
|
|
return FALSE;
|
|
|
|
ASSERT ((pkt_len >= CS8900_RUN_PKT_SIZE)
|
|
&& (pkt_len <= CS8900_MAX_PKT_SIZE), "not valid pktlen 0x%x\n",
|
|
(unsigned) pkt_len);
|
|
/*64<LEN<1518 */
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* Check if a packet must be delivered to the emulated chip */
|
|
static inline int cs8900_handle_mac_addr (struct cs8900_data *d,
|
|
m_uint8_t * pkt)
|
|
{
|
|
n_eth_hdr_t *hdr = (n_eth_hdr_t *) pkt;
|
|
|
|
m_uint8_t *ram_base;
|
|
ram_base = (m_uint8_t *) (&(d->internal_ram[0]));
|
|
|
|
if ((*(m_uint16_t *) (ram_base + PP_RxCTL)) & PromiscuousA) {
|
|
goto rx_dest_int;
|
|
}
|
|
if (eth_addr_is_bcast (&hdr->daddr)) {
|
|
if ((*(m_uint16_t *) (ram_base + PP_RxCTL)) & BroadcastA) {
|
|
*(m_uint16_t *) (ram_base + PP_RxEvent) |= BroadcastA;
|
|
*(m_uint16_t *) (ram_base + PP_RxStatus) |= BroadcastA;
|
|
goto rx_dest_int;
|
|
} else
|
|
return FALSE;
|
|
}
|
|
if (eth_addr_is_mcast (&hdr->daddr)) {
|
|
if ((*(m_uint16_t *) (ram_base + PP_RxCTL)) & MulticastA) {
|
|
*(m_uint16_t *) (ram_base + PP_RxEvent) |= MulticastA;
|
|
*(m_uint16_t *) (ram_base + PP_RxStatus) |= MulticastA;
|
|
goto rx_dest_int;
|
|
} else
|
|
return FALSE;
|
|
}
|
|
|
|
if ((*(m_uint16_t *) (ram_base + PP_RxCTL)) & IndividualA) {
|
|
/* Accept frames directly for us, discard others */
|
|
if (!memcmp ((ram_base + PP_IA), &hdr->daddr, N_ETH_ALEN)) {
|
|
*(m_uint16_t *) (ram_base + PP_RxEvent) |= IndividualA;
|
|
*(m_uint16_t *) (ram_base + PP_RxStatus) |= IndividualA;
|
|
goto rx_dest_int;
|
|
} else
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
rx_dest_int:
|
|
return (TRUE);
|
|
}
|
|
|
|
static int dev_cs8900_receive_pkt (struct cs8900_data *d, u_char * pkt,
|
|
ssize_t pkt_len)
|
|
{
|
|
m_uint8_t *ram_base;
|
|
ram_base = (m_uint8_t *) (&(d->internal_ram[0]));
|
|
|
|
/* Truncate the packet if it is too big */
|
|
pkt_len = m_min (pkt_len, CS8900_MAX_PKT_SIZE);
|
|
/*set RX len */
|
|
*(m_uint16_t *) (ram_base + PP_RxLength) = pkt_len;
|
|
/*Rx status has been set */
|
|
/*just copy frame to internal ram */
|
|
memcpy (ram_base + PP_RxFrame, pkt, pkt_len);
|
|
/*generate interrupt */
|
|
|
|
*(m_uint16_t *) (ram_base + PP_RxEvent) |= RxOKA;
|
|
*(m_uint16_t *) (ram_base + PP_RxStatus) |= RxOKA;
|
|
if ((*(m_uint16_t *) (ram_base + PP_RxCFG)) & RxOKiE) {
|
|
//*(m_uint16_t*)(ram_base+PP_ISQ) &= ~0x3f;
|
|
*(m_uint16_t *) (ram_base + PP_ISQ) |= RxEvent;
|
|
dev_cs8900_gen_interrupt (d);
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
static int dev_cs8900_rx (netio_desc_t * nio, u_char * pkt, ssize_t pkt_len,
|
|
struct cs8900_data *d)
|
|
{
|
|
m_uint8_t *ram_base;
|
|
m_uint16_t real_len;
|
|
int i;
|
|
m_uint32_t ifcs;
|
|
|
|
ram_base = (m_uint8_t *) (&(d->internal_ram[0]));
|
|
|
|
if (!((*(m_uint16_t *) (ram_base + PP_LineCTL)) & SerRxON))
|
|
return FALSE;
|
|
real_len = pkt_len;
|
|
|
|
/*FIXME: yajin
|
|
* jzdriver discard <64 bytes packet. But arp packet has 40 bytes. Pad it to 64 bytes to meet jz driver's requirement
|
|
*/
|
|
if (unlikely (pkt_len < 64)) {
|
|
/*pad to 60 bytes */
|
|
for (i = pkt_len; i < 60; i++) {
|
|
*(pkt + i) = 0x0;
|
|
}
|
|
/*add crc */
|
|
ifcs = crc32_compute (0xFFFFFFFF, pkt, 60);
|
|
*(pkt + 60) = ifcs & 0xff;
|
|
*(pkt + 61) = (ifcs >> 8) & 0xff;
|
|
*(pkt + 62) = (ifcs >> 16) & 0xff;
|
|
*(pkt + 63) = ifcs >> 24;
|
|
real_len = 64;
|
|
}
|
|
|
|
/*check MAC address */
|
|
if (!(cs8900_handle_mac_addr (d, pkt)))
|
|
return FALSE;
|
|
|
|
/*check frame len */
|
|
if (!(cs8900_handle_len (d, pkt, real_len)))
|
|
return FALSE;
|
|
|
|
return (dev_cs8900_receive_pkt (d, pkt, real_len));
|
|
|
|
}
|
|
|
|
static int dev_cs8900_tx (struct cs8900_data *d)
|
|
{
|
|
|
|
m_uint8_t *ram_base;
|
|
ram_base = (m_uint8_t *) (&(d->internal_ram[0]));
|
|
m_uint16_t send_len;
|
|
int i;
|
|
m_uint32_t ifcs;
|
|
|
|
send_len = *(m_uint16_t *) (ram_base + PP_TxLength);
|
|
/*check if tx is enabled */
|
|
if ((*(m_uint16_t *) (ram_base + PP_LineCTL)) & SerTxON) {
|
|
/*pad if len<60 */
|
|
if (send_len <= (CS8900_RUN_PKT_SIZE - 4)) {
|
|
if (!((*(m_uint16_t *) (ram_base + PP_TxCMD)) & TxPadDis)) {
|
|
/*pad to 60 bytes */
|
|
for (i = send_len; i < 60; i++) {
|
|
*(ram_base + PP_TxFrame + i) = 0x0;
|
|
}
|
|
send_len = 60;
|
|
if (!((*(m_uint16_t *) (ram_base + PP_TxCMD)) & InhibitCRC)) {
|
|
/*append crc */
|
|
ifcs =
|
|
crc32_compute (0xFFFFFFFF, ram_base + PP_TxFrame,
|
|
send_len);
|
|
*(ram_base + PP_TxFrame + send_len) = ifcs & 0xff;
|
|
*(ram_base + PP_TxFrame + send_len + 1) =
|
|
(ifcs >> 8) & 0xff;
|
|
*(ram_base + PP_TxFrame + send_len + 2) =
|
|
(ifcs >> 16) & 0xff;
|
|
*(ram_base + PP_TxFrame + send_len + 3) = ifcs >> 24;
|
|
send_len += 4;
|
|
|
|
}
|
|
}
|
|
}
|
|
*(m_uint16_t *) (ram_base + PP_TxLength) = send_len;
|
|
netio_send (d->nio, ram_base + PP_TxFrame, send_len);
|
|
*(m_uint16_t *) (ram_base + PP_TxEvent) = TxOK | 0x8; /*is = not |. all other bits must be cleared */
|
|
if ((*(m_uint16_t *) (ram_base + PP_TxCFG)) & TxOKiE) {
|
|
/*if TXOKIE, generate an interrupt */
|
|
/*set ISQ (regno=TX Event) */
|
|
//*(m_uint16_t*)(ram_base+PP_ISQ) &= ~0x3f;
|
|
*(m_uint16_t *) (ram_base + PP_ISQ) |= TxEvent;
|
|
dev_cs8900_gen_interrupt (d);
|
|
}
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
how to determinte the timeout value???
|
|
*/
|
|
void dev_cs8900_active_timer (struct cs8900_data *d)
|
|
{
|
|
vp_mod_timer (d->cs8900_timer,
|
|
vp_get_clock (rt_clock) + cs8900a_rx_timeout);
|
|
}
|
|
|
|
void dev_cs8900_unactive_timer (struct cs8900_data *d)
|
|
{
|
|
vp_del_timer (d->cs8900_timer);
|
|
}
|
|
|
|
void dev_cs8900_cb (void *opaque)
|
|
{
|
|
struct cs8900_data *d = opaque;
|
|
|
|
int fd;
|
|
ssize_t pkt_len;
|
|
|
|
static m_uint8_t status = 0;
|
|
|
|
ASSERT (d->nio != NULL, "set nio first\n");
|
|
|
|
if ((fd = netio_get_fd (d->nio)) == -1) {
|
|
ASSERT (0, "can not get nio fd. init cs8900 nio first.\n");
|
|
}
|
|
|
|
pkt_len = netio_recv (d->nio, d->nio->rx_pkt, sizeof (d->nio->rx_pkt));
|
|
|
|
if (pkt_len > 0) {
|
|
/*rx packet */
|
|
dev_cs8900_rx (d->nio, d->nio->rx_pkt, pkt_len, d);
|
|
|
|
/*Why we need to adjust CS8900_MAX_RX_TIMEOUT? yajin
|
|
* If CS8900_MAX_RX_TIMEOUT is small, that means rx packets quickly. Tx can not get enough time to tell cpu
|
|
* that tx ok.
|
|
* If CS8900_MAX_RX_TIMEOUT is big, that means rx packets slow. This will decrease network throughtput and
|
|
* some applications will complain about rx timeout.
|
|
* So I adjut the CS8900_MAX_RX_TIMEOUT dynamicly when receiving a packet .
|
|
*
|
|
* Please use TCP protocol instead of UDP when mounting directory using nfs.
|
|
*
|
|
*/
|
|
if (cs8900a_rx_timeout >= CS8900_MAX_RX_TIMEOUT)
|
|
status = 1;
|
|
else if (cs8900a_rx_timeout <= CS8900_MIN_RX_TIMEOUT)
|
|
status = 2;
|
|
|
|
if (status == 0)
|
|
cs8900a_rx_timeout -= CS8900_RX_TIMEOUT_STEP;
|
|
if (status == 1)
|
|
cs8900a_rx_timeout -= CS8900_RX_TIMEOUT_STEP;
|
|
else if (status == 2)
|
|
cs8900a_rx_timeout += CS8900_RX_TIMEOUT_STEP;
|
|
|
|
}
|
|
|
|
cs8900a_rx_timeout = CS8900_DEFAULT_RX_TIMEOUT;
|
|
dev_cs8900_active_timer (d);
|
|
|
|
}
|
|
|
|
static void *dev_cs8900_access (cpu_mips_t * cpu, struct vdevice *dev,
|
|
m_uint32_t offset, u_int op_size, u_int op_type,
|
|
m_uint32_t * data, m_uint8_t * has_set_value)
|
|
{
|
|
struct cs8900_data *d = dev->priv_data;
|
|
void *ret;
|
|
m_uint8_t *ram_base;
|
|
m_uint16_t io_address;
|
|
m_uint16_t isq;
|
|
|
|
ram_base = (m_uint8_t *) (&(d->internal_ram[0]));
|
|
|
|
if (offset >= d->cs8900_size) {
|
|
*data = 0;
|
|
return NULL;
|
|
}
|
|
#if VALIDE_CS8900_OPERATION
|
|
if (op_type == MTS_WRITE) {
|
|
ASSERT (offset != PP_IO_ISQ,
|
|
"Write to read only register in CS8900. offset %x\n", offset);
|
|
} else if (op_type == MTS_READ) {
|
|
ASSERT (offset != PP_TX_CMD,
|
|
"Read write only register in CS8900. offset %x\n", offset);
|
|
ASSERT (offset != PP_TX_LEN,
|
|
"Read write only register in CS8900. offset %x\n", offset);
|
|
}
|
|
#endif
|
|
|
|
switch (offset) {
|
|
case PP_RT_DATA0:
|
|
case PP_RT_DATA0 + 1:
|
|
case PP_RT_DATA1:
|
|
case PP_RT_DATA1 + 1:
|
|
if (op_type == MTS_READ) {
|
|
ASSERT (d->rx_read_index <
|
|
(*(m_uint16_t *) (ram_base + PP_RxLength)),
|
|
"read out of data rx_read_index %x data len %x \n",
|
|
d->rx_read_index, (*(m_uint16_t *) (ram_base + PP_RxLength)));
|
|
ret = (void *) (ram_base + PP_RxFrame + d->rx_read_index);
|
|
d->rx_read_index += op_size;
|
|
/****if read all data,set d->rx_read_index=0*/
|
|
if (d->rx_read_index >= *(m_uint16_t *) (ram_base + PP_RxLength))
|
|
d->rx_read_index = 0;
|
|
return ret;
|
|
} else if (op_type == MTS_WRITE) {
|
|
ret = (void *) (ram_base + PP_TxFrame + d->tx_send_index);
|
|
if (op_size == MTS_BYTE)
|
|
*(m_uint8_t *) ret = *data;
|
|
if (op_size == MTS_HALF_WORD)
|
|
*(m_uint16_t *) ret = *data;
|
|
else
|
|
*(m_uint32_t *) ret = *data;
|
|
*has_set_value = TRUE;
|
|
d->tx_send_index += op_size;
|
|
/*if write all data into tx buffer, set d->tx_send_index=0 */
|
|
if (d->tx_send_index >= *(m_uint16_t *) (ram_base + PP_TxLength)) {
|
|
d->tx_send_index = 0;
|
|
/*start tx a frame */
|
|
dev_cs8900_tx (d);
|
|
}
|
|
return NULL;
|
|
}
|
|
break;
|
|
case PP_TX_CMD:
|
|
|
|
ret = (void *) (ram_base + PP_TxCMD);
|
|
return ret;
|
|
case PP_TX_LEN:
|
|
ret = (void *) (ram_base + PP_TxLength);
|
|
return ret;
|
|
case PP_IO_ISQ:
|
|
ASSERT (0, "not support PP_IO_ISQ \n");
|
|
case PP_ADDRESS:
|
|
return (void *) (ram_base + PP_ADDRESS);
|
|
case PP_DATA0:
|
|
case PP_DATA1:
|
|
if (offset == PP_DATA0)
|
|
ASSERT (op_size == MTS_HALF_WORD,
|
|
"op_size must be 2. op_size %x\n", op_size);
|
|
else if (offset == PP_DATA1)
|
|
ASSERT (0, "cs8900 only support 16 bit IO operation");
|
|
io_address = *(m_uint16_t *) (ram_base + PP_ADDRESS);
|
|
switch (io_address) {
|
|
case PP_ProductID:
|
|
ASSERT (op_type == MTS_READ, "write to read only register %x\n",
|
|
*(m_uint16_t *) (ram_base + PP_ADDRESS));
|
|
*data = CS8900A_PRODUCT_ID;
|
|
*has_set_value = TRUE;
|
|
return NULL;
|
|
case PP_ProductID + 2: /*16 bit */
|
|
*data = 0;
|
|
*has_set_value = TRUE;
|
|
return NULL;
|
|
case PP_ISAIOB:
|
|
case PP_IntNum:
|
|
return (void *) (ram_base + io_address);
|
|
case PP_ISASOF:
|
|
case PP_DmaFrameCnt:
|
|
case PP_DmaByteCnt:
|
|
case PP_MemBase:
|
|
case PP_EEPROMCommand:
|
|
case PP_EEPROMData:
|
|
ASSERT (0, "Not support yet offset %x \n", io_address);
|
|
break;
|
|
case PP_RxCFG:
|
|
if (op_type == MTS_WRITE) {
|
|
if (*data & Skip_1) {
|
|
memset (ram_base + PP_RxFrame, 0x0,
|
|
PP_TxFrame - 1 - PP_RxFrame);
|
|
}
|
|
*(m_uint16_t *) (ram_base + PP_RxCFG) = *data | 0x3;
|
|
*has_set_value = TRUE;
|
|
return NULL;
|
|
} else /*read */
|
|
return (void *) (ram_base + io_address);
|
|
case PP_RxCTL:
|
|
if (op_type == MTS_WRITE) {
|
|
*(m_uint16_t *) (ram_base + PP_RxCTL) = *data | 0x5;
|
|
*has_set_value = TRUE;
|
|
if (*data & IAHashA)
|
|
ASSERT (0, "Hash dest address is not support yet \n");
|
|
return NULL;
|
|
} else
|
|
return (void *) (ram_base + io_address);
|
|
|
|
case PP_TxCFG:
|
|
if (op_type == MTS_WRITE) {
|
|
*(m_uint16_t *) (ram_base + PP_TxCFG) = *data | 0x7;
|
|
*has_set_value = TRUE;
|
|
return NULL;
|
|
} else
|
|
return (void *) (ram_base + io_address);
|
|
case 0x108:
|
|
/*read 0x108 actually read 0x144 TXcmd */
|
|
ASSERT (op_type == MTS_READ,
|
|
"CS8900 write to read only register. IO address 0x108 \n");
|
|
return (void *) (ram_base + 0x144);
|
|
case PP_BufCFG:
|
|
if (op_type == MTS_WRITE) {
|
|
if (*data & SWint_X) {
|
|
*(m_uint16_t *) (ram_base + PP_BufEvent) |= SWint;
|
|
//*(m_uint16_t*)(ram_base+PP_ISQ) &= ~0x3f;
|
|
*(m_uint16_t *) (ram_base + PP_ISQ) |= BufEvent;
|
|
dev_cs8900_gen_interrupt (d);
|
|
}
|
|
if (*data & Rdy4TxiE) {
|
|
/*if host set rdy4tx, we are always ready for tx */
|
|
*(m_uint16_t *) (ram_base + PP_BufEvent) |= Rdy4Tx;
|
|
//*(m_uint16_t*)(ram_base+PP_ISQ) &= ~0x3f;
|
|
*(m_uint16_t *) (ram_base + PP_ISQ) |= BufEvent;
|
|
dev_cs8900_gen_interrupt (d);
|
|
}
|
|
*(m_uint16_t *) (ram_base + PP_BufCFG) = *data | 0xb;
|
|
*has_set_value = TRUE;
|
|
return NULL;
|
|
} else
|
|
return (void *) (ram_base + io_address);
|
|
|
|
case PP_LineCTL:
|
|
if (op_type == MTS_WRITE) {
|
|
*(m_uint16_t *) (ram_base + PP_LineCTL) = *data | 0x13;
|
|
if ((*data & SerRxON) || (*data & SerTxON))
|
|
dev_cs8900_active_timer (d);
|
|
*has_set_value = TRUE;
|
|
return NULL;
|
|
} else
|
|
return (void *) (ram_base + io_address);
|
|
case PP_SelfCTL:
|
|
if (op_type == MTS_WRITE) {
|
|
if (*data & RESET) {
|
|
dev_cs8900_reset (cpu, dev);
|
|
}
|
|
*(m_uint16_t *) (ram_base + PP_SelfCTL) = *data | 0x15;
|
|
*has_set_value = TRUE;
|
|
return NULL;
|
|
} else
|
|
return (void *) (ram_base + io_address);
|
|
case PP_BusCTL:
|
|
if (op_type == MTS_WRITE) {
|
|
*(m_uint16_t *) (ram_base + PP_BusCTL) = *data | 0x17;
|
|
*has_set_value = TRUE;
|
|
return NULL;
|
|
} else
|
|
return (void *) (ram_base + io_address);
|
|
|
|
case PP_TestCTL:
|
|
if (op_type == MTS_WRITE) {
|
|
*(m_uint16_t *) (ram_base + PP_TestCTL) = *data | 0x19;
|
|
*has_set_value = TRUE;
|
|
return NULL;
|
|
} else
|
|
return (void *) (ram_base + io_address);
|
|
|
|
case PP_ISQ:
|
|
isq = *(m_uint16_t *) (ram_base + PP_ISQ);
|
|
if (op_type == MTS_WRITE) {
|
|
*(m_uint16_t *) (ram_base + PP_ISQ) = 0;
|
|
*has_set_value = TRUE;
|
|
return NULL;
|
|
}
|
|
/*Readonly? But sometimes, kernel will write to this register. */
|
|
//ASSERT(op_type == MTS_READ, "wirte to read only register io_address %x.", io_address);
|
|
/*SHOULD be read */
|
|
if (isq & TxEvent) {
|
|
*(m_uint16_t *) (ram_base + PP_ISQ) &= ~TxEvent;
|
|
*(m_uint16_t *) data =
|
|
*(m_uint16_t *) (ram_base + PP_TxEvent);
|
|
*(m_uint16_t *) (ram_base + PP_TxEvent) = 0X8;
|
|
//return (void*)(ram_base+PP_TxEvent);
|
|
} else if (isq & RxEvent) {
|
|
*(m_uint16_t *) (ram_base + PP_ISQ) &= ~RxEvent;
|
|
*(m_uint16_t *) data =
|
|
*(m_uint16_t *) (ram_base + PP_RxEvent);
|
|
*(m_uint16_t *) (ram_base + PP_RxEvent) = 0X4;
|
|
|
|
//return (void*)(ram_base+PP_RxEvent);
|
|
} else if (isq & BufEvent) {
|
|
*(m_uint16_t *) (ram_base + PP_ISQ) &= ~BufEvent;
|
|
*(m_uint16_t *) data =
|
|
*(m_uint16_t *) (ram_base + PP_BufEvent);
|
|
*(m_uint16_t *) (ram_base + PP_BufEvent) = 0Xc;
|
|
|
|
//return (void*)(ram_base+PP_BufEvent);
|
|
}
|
|
|
|
else if (isq & RxMISS) {
|
|
*(m_uint16_t *) (ram_base + PP_ISQ) &= ~RxMISS;
|
|
*(m_uint16_t *) data = *(m_uint16_t *) (ram_base + PP_RxMISS);
|
|
*(m_uint16_t *) (ram_base + PP_RxMISS) = 0x10;
|
|
//return (void*)(ram_base+PP_RxMISS);
|
|
} else if (isq & TxCOL) {
|
|
*(m_uint16_t *) (ram_base + PP_ISQ) &= ~TxCOL;
|
|
*(m_uint16_t *) data = *(m_uint16_t *) (ram_base + PP_TxCOL);
|
|
*(m_uint16_t *) (ram_base + PP_TxCOL) = 0x12;
|
|
//return (void*)(ram_base+PP_TxCOL);
|
|
} else {
|
|
return (void *) (ram_base + PP_ISQ);
|
|
}
|
|
*has_set_value = TRUE;
|
|
return NULL;
|
|
break;
|
|
case PP_RxEvent:
|
|
/*read rx event will clear it */
|
|
ASSERT (op_type == MTS_READ,
|
|
"CS8900 write to read only register. IO address %x \n",
|
|
io_address);
|
|
*(m_uint16_t *) data = *(m_uint16_t *) (ram_base + PP_RxEvent);
|
|
*has_set_value = TRUE;
|
|
*(m_uint16_t *) (ram_base + PP_RxEvent) = 0X4;
|
|
return NULL;
|
|
case PP_TxEvent:
|
|
/*read tx event will clear it */
|
|
ASSERT (op_type == MTS_READ,
|
|
"CS8900 write to read only register. IO address %x \n",
|
|
io_address);
|
|
*(m_uint16_t *) data = *(m_uint16_t *) (ram_base + PP_TxEvent);
|
|
*has_set_value = TRUE;
|
|
*(m_uint16_t *) (ram_base + PP_TxEvent) = 0X8;
|
|
return NULL;
|
|
case PP_BufEvent:
|
|
/*read BufEvent event will clear it */
|
|
ASSERT (op_type == MTS_READ,
|
|
"CS8900 write to read only register. IO address %x \n",
|
|
io_address);
|
|
*(m_uint16_t *) data = *(m_uint16_t *) (ram_base + PP_BufEvent);
|
|
*has_set_value = TRUE;
|
|
*(m_uint16_t *) (ram_base + PP_BufEvent) = 0Xc;
|
|
return NULL;
|
|
|
|
case PP_RxMISS:
|
|
case PP_TxCOL:
|
|
case PP_LineST:
|
|
case PP_SelfST:
|
|
case PP_TDR:
|
|
ASSERT (op_type == MTS_READ,
|
|
"CS8900 write to read only register. IO address %x \n",
|
|
io_address);
|
|
return (void *) (ram_base + io_address);
|
|
case PP_BusST:
|
|
*(m_uint16_t *) (ram_base + PP_BusST) |= Rdy4TxNOW;
|
|
//else
|
|
//{
|
|
// *(m_uint16_t*)(ram_base+PP_BusST) &= ~Rdy4TxNOW;
|
|
//}
|
|
return (void *) (ram_base + io_address);
|
|
case PP_TxCMD:
|
|
ASSERT (op_type == MTS_WRITE,
|
|
"CS8900 read write only register. IO address %x \n",
|
|
PP_TxCMD);
|
|
*(m_uint16_t *) (ram_base + PP_TxCMD) = *data | 0x9;
|
|
*has_set_value = TRUE;
|
|
return NULL;
|
|
|
|
case PP_TxLength:
|
|
ASSERT (op_type == MTS_WRITE,
|
|
"CS8900 read write only register. IO address %x \n",
|
|
PP_TxLength);
|
|
*(m_uint16_t *) (ram_base + PP_TxLength) = *data;
|
|
*has_set_value = TRUE;
|
|
if (*(m_uint16_t *) (ram_base + PP_TxLength) > 1518) {
|
|
*(m_uint16_t *) (ram_base + PP_BusST) |= TxBidErr;
|
|
} else if (*(m_uint16_t *) (ram_base + PP_TxLength) > 1514) {
|
|
if (!((*(m_uint16_t *) (ram_base + PP_TxCMD)) & InhibitCRC))
|
|
*(m_uint16_t *) (ram_base + PP_BusST) |= TxBidErr;
|
|
else
|
|
*(m_uint16_t *) (ram_base + PP_BusST) &= ~TxBidErr;
|
|
} else
|
|
*(m_uint16_t *) (ram_base + PP_BusST) &= ~TxBidErr;
|
|
return NULL;
|
|
case PP_LAF:
|
|
case PP_LAF + 1:
|
|
case PP_LAF + 2:
|
|
case PP_LAF + 3:
|
|
case PP_LAF + 4:
|
|
case PP_LAF + 5:
|
|
case PP_LAF + 6:
|
|
case PP_LAF + 7:
|
|
case PP_IA:
|
|
case PP_IA + 1:
|
|
case PP_IA + 2:
|
|
case PP_IA + 3:
|
|
case PP_IA + 4:
|
|
case PP_IA + 5:
|
|
case PP_RxStatus:
|
|
case PP_RxLength:
|
|
return (void *) (ram_base + io_address);
|
|
|
|
default:
|
|
ASSERT (0, "error io address %x\n", io_address);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
struct cs8900_data *dev_cs8900_init (vm_instance_t * vm, char *name,
|
|
m_pa_t phys_addr, m_uint32_t phys_len, int irq)
|
|
{
|
|
struct cs8900_data *d;
|
|
|
|
/* Allocate the private data structure for DEC21140 */
|
|
if (!(d = malloc (sizeof (*d)))) {
|
|
fprintf (stderr, "%s (cs8900_data): out of memory\n", name);
|
|
return NULL;
|
|
}
|
|
memset (d, 0, sizeof (*d));
|
|
|
|
/* Create the device itself */
|
|
if (!(d->dev = dev_create (name))) {
|
|
fprintf (stderr, "%s (DEC21140): unable to create device.\n", name);
|
|
goto err_dev;
|
|
}
|
|
d->irq_no = irq;
|
|
d->vm = vm;
|
|
d->dev->priv_data = d;
|
|
d->dev->phys_addr = phys_addr;
|
|
d->dev->phys_len = phys_len;
|
|
d->cs8900_size = phys_len;
|
|
|
|
d->dev->handler = dev_cs8900_access;
|
|
d->dev->reset_handler = dev_cs8900_reset;
|
|
|
|
d->dev->flags = VDEVICE_FLAG_NO_MTS_MMAP;
|
|
d->cs8900_timer = vp_new_timer (rt_clock, dev_cs8900_cb, d);
|
|
vm_bind_device (vm, d->dev);
|
|
return (d);
|
|
|
|
err_dev:
|
|
free (d);
|
|
return NULL;
|
|
}
|
|
|
|
int dev_cs8900_set_nio (struct cs8900_data *d, netio_desc_t * nio)
|
|
{
|
|
|
|
/* check that a NIO is not already bound */
|
|
if (d->nio != NULL)
|
|
return (-1);
|
|
|
|
d->nio = nio;
|
|
|
|
return (0);
|
|
}
|