/* * Copyright (C) yajin 2008 * * 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 #include #include #include #include #include #include #include #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); /*64internal_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); }