/* Naive RealTek 8139C driver for the DC Broadband Adapter. Some assistance on names of things from the NetBSD-DC sources. (c)2001 Dan Potter License: X11 This is just a little test version that runs under Libdream 0.95. The real release will be part of KOS 0.8x (not 0.80) and will actually support Rx/Tx/interrupts/etc. TODO: Tx, better queueing, interrupt support */ /* NOTES If you want to analyze the output to see that it works, setup your DC, add an ARP entry on your host PC, and then ping the DC. The packet should follow this format. ICMP ping packet (from ethernet level) Ethernet header - 6 bytes dest MAC address - 6 bytes source MAC address - 2 bytes packet type (08,00) IP header - 1 byte version / ihl - 1 byte tos - 2 bytes total length (including this header) - 2 bytes packet id - 2 bytes fragment offset - 1 byte ttl - 1 byte protocol (ICMP == 0x40) - 2 bytes checksum - 4 bytes source address - 4 bytes destination address ICMP Echo - 1 byte Type (8 for echo, 0 for response) - 1 byte Code (0) - 2 bytes checksum (weird xor thing?) - 2 bytes identifier (?) - 2 bytes sequence number - data */ #include "dream.h" /* RTL8139C register definitions */ #define RT_IDR0 0x00 /* Mac address */ #define RT_MAR0 0x08 /* Multicast filter */ #define RT_TXSTATUS0 0x10 /* Transmit status (4 32bit regs) */ #define RT_TXADDR0 0x20 /* Tx descriptors (also 4 32bit) */ #define RT_RXBUF 0x30 /* Receive buffer start address */ #define RT_RXEARLYCNT 0x34 /* Early Rx byte count */ #define RT_RXEARLYSTATUS 0x36 /* Early Rx status */ #define RT_CHIPCMD 0x37 /* Command register */ #define RT_RXBUFTAIL 0x38 /* Current address of packet read (queue tail) */ #define RT_RXBUFHEAD 0x3A /* Current buffer address (queue head) */ #define RT_INTRMASK 0x3C /* Interrupt mask */ #define RT_INTRSTATUS 0x3E /* Interrupt status */ #define RT_TXCONFIG 0x40 /* Tx config */ #define RT_RXCONFIG 0x44 /* Rx config */ #define RT_TIMER 0x48 /* A general purpose counter */ #define RT_RXMISSED 0x4C /* 24 bits valid, write clears */ #define RT_CFG9346 0x50 /* 93C46 command register */ #define RT_CONFIG0 0x51 /* Configuration reg 0 */ #define RT_CONFIG1 0x52 /* Configuration reg 1 */ #define RT_TIMERINT 0x54 /* Timer interrupt register (32 bits) */ #define RT_MEDIASTATUS 0x58 /* Media status register */ #define RT_CONFIG3 0x59 /* Config register 3 */ #define RT_CONFIG4 0x5A /* Config register 4 */ #define RT_MULTIINTR 0x5C /* Multiple interrupt select */ #define RT_MII_TSAD 0x60 /* Transmit status of all descriptors (16 bits) */ #define RT_MII_BMCR 0x62 /* Basic Mode Control Register (16 bits) */ #define RT_MII_BMSR 0x64 /* Basic Mode Status Register (16 bits) */ #define RT_AS_ADVERT 0x66 /* Auto-negotiation advertisement reg (16 bits) */ #define RT_AS_LPAR 0x68 /* Auto-negotiation link partner reg (16 bits) */ #define RT_AS_EXPANSION 0x6A /* Auto-negotiation expansion reg (16 bits) */ /* RTL8193C command bits; or these together and write teh resulting value into CHIPCMD to execute it. */ #define RT_CMD_RESET 0x10 #define RT_CMD_RX_ENABLE 0x08 #define RT_CMD_TX_ENABLE 0x04 #define RT_CMD_RX_BUF_EMPTY 0x01 /* RTL8139C interrupt status bits */ #define RT_INT_PCIERR 0x8000 /* PCI Bus error */ #define RT_INT_TIMEOUT 0x4000 /* Set when TCTR reaches TimerInt value */ #define RT_INT_RXFIFO_OVERFLOW 0x0040 /* Rx FIFO overflow */ #define RT_INT_RXFIFO_UNDERRUN 0x0020 /* Packet underrun / link change */ #define RT_INT_RXBUF_OVERFLOW 0x0010 /* Rx BUFFER overflow */ #define RT_INT_TX_ERR 0x0008 #define RT_INT_TX_OK 0x0004 #define RT_INT_RX_ERR 0x0002 #define RT_INT_RX_OK 0x0001 /* RTL8139C transmit status bits */ #define RT_TX_CARRIER_LOST 0x80000000 /* Carrier sense lost */ #define RT_TX_ABORTED 0x40000000 /* Transmission aborted */ #define RT_TX_OUT_OF_WINDOW 0x20000000 /* Out of window collision */ #define RT_TX_STATUS_OK 0x00008000 /* Status ok: a good packet was transmitted */ #define RT_TX_UNDERRUN 0x00004000 /* Transmit FIFO underrun */ #define RT_TX_HOST_OWNS 0x00002000 /* Set to 1 when DMA operation is completed */ #define RT_TX_SIZE_MASK 0x00001fff /* Descriptor size mask */ /* RTL8139C receive status bits */ #define RT_RX_MULTICAST 0x00008000 /* Multicast packet */ #define RT_RX_PAM 0x00004000 /* Physical address matched */ #define RT_RX_BROADCAST 0x00002000 /* Broadcast address matched */ #define RT_RX_BAD_SYMBOL 0x00000020 /* Invalid symbol in 100TX packet */ #define RT_RX_RUNT 0x00000010 /* Packet size is <64 bytes */ #define RT_RX_TOO_LONG 0x00000008 /* Packet size is >4K bytes */ #define RT_RX_CRC_ERR 0x00000004 /* CRC error */ #define RT_RX_FRAME_ALIGN 0x00000002 /* Frame alignment error */ #define RT_RX_STATUS_OK 0x00000001 /* Status ok: a good packet was received */ /* Convienence macros */ #define REGL(a) (volatile unsigned long *)(a) #define vul volatile unsigned long #define REGS(a) (volatile unsigned short *)(a) #define vus volatile unsigned short #define REGC(a) (volatile unsigned char *)(a) #define vuc volatile unsigned char /* Configuration definitions */ #define RX_BUFFER_LEN 8192 /* RTL8139C Config/Status info */ typedef struct { uint16 cur_rx; /* Current Rx read ptr */ uint16 cur_tx; /* Current available Tx slot */ uint8 mac[6]; /* Mac address */ } rtl_status_t; rtl_status_t rtl; /* Mac address of card */ static uint8 rtl_mac[6]; /* 8, 16, and 32 bit access to the PCI I/O space (configured by GAPS) */ static vuc * const nic8 = REGC(0xa1001700); static vus * const nic16 = REGS(0xa1001700); static vul * const nic32 = REGL(0xa1001700); /* 8 and 32 bit access to the PCI MEMMAP space (configured by GAPS) */ static vuc * const mem8 = REGC(0xa1840000); static vul * const mem32 = REGL(0xa1840000); /* Detect the presence of a Broadband Adapter This really detects a "GAPS PCI" bridge chip, but the only device for the Dreamcast that uses it is the net card. Returns 0 if one is present, -1 if one is not. */ int bb_detect() { const char *str = (char*)REGC(0xa1001400); if (!strncmp(str, "GAPSPCI_BRIDGE_2", 16)) return 0; else return -1; } /* Initializes the BBA Returns 0 for success or -1 for failure. */ int bb_init() { vuc * const g28 = REGC(0xa1000000); vus * const g216 = REGS(0xa1000000); vul * const g232 = REGL(0xa1000000); int i; uint32 tmp; /* Initialize the "GAPS" PCI glue controller. It ain't pretty but it works. */ g232[0x1418/4] = 0x5a14a501; /* M */ i = 10000; while (!(g232[0x1418/4] & 1) && i > 0) i--; if (!(g232[0x1418/4] & 1)) { printf("GAPS PCI controller not responding; giving up!\r\n"); return -1; } g232[0x1420/4] = 0x01000000; g232[0x1424/4] = 0x01000000; g232[0x1428/4] = 0x01840000; /* DMA Base */ g232[0x142c/4] = 0x01840000 + 32*1024; /* DMA End */ g232[0x1414/4] = 0x00000001; g232[0x1434/4] = 0x00000001; /* Configure PCI bridge (very very hacky). If we wanted to be proper, we ought to implement a full PCI subsystem. In this case that is ridiculous for accessing a single card that will probably never change. Considering that the DC is now out of production officially, there is a VERY good chance it will never change. */ g216[0x1606/2] = 0xf900; g232[0x1630/4] = 0x00000000; g28[0x163c] = 0x00; g28[0x160d] = 0xf0; (void)g216[0x04/2]; g216[0x1604/2] = 0x0006; g232[0x1614/4] = 0x01000000; (void)g28[0x1650]; /* Soft-reset the chip */ nic8[RT_CHIPCMD] = RT_CMD_RESET; /* Wait for it to come back */ i = 1000; while ((nic8[RT_CHIPCMD] & RT_CMD_RESET) && i > 0) i--; /* Reset CONFIG1 */ nic8[RT_CONFIG1] = 0; /* Enable auto-negotiation and restart that process */ nic16[RT_MII_BMCR/2] = 0x1200; /* Do another reset */ nic8[RT_CHIPCMD] = RT_CMD_RESET; /* Wait for it to come back */ i = 1000; while ((nic8[RT_CHIPCMD] & RT_CMD_RESET) && i > 0) i--; /* Enable writing to the config registers */ nic8[RT_CFG9346] = 0xc0; /* Read MAC address */ tmp = nic32[RT_IDR0]; rtl.mac[0] = tmp & 0xff; rtl.mac[1] = (tmp >> 8) & 0xff; rtl.mac[2] = (tmp >> 16) & 0xff; rtl.mac[3] = (tmp >> 24) & 0xff; tmp = nic32[RT_IDR0+1]; rtl.mac[4] = tmp & 0xff; rtl.mac[5] = (tmp >> 8) & 0xff; printf("MAC Address is %02x:%02x:%02x:%02x:%02x:%02x\r\n", rtl.mac[0], rtl.mac[1], rtl.mac[2], rtl.mac[3], rtl.mac[4], rtl.mac[5]); /* Enable receive and transmit functions */ nic8[RT_CHIPCMD] = RT_CMD_RX_ENABLE | RT_CMD_TX_ENABLE; /* Set Rx FIFO threashold to 1K, Rx size to 8k+16, 1024 byte DMA burst */ nic32[RT_RXCONFIG/4] = 0x0000c600; /* Set Tx 1024 byte DMA burst */ nic32[RT_TXCONFIG/4] = 00000600; /* Turn off lan-wake and set the driver-loaded bit */ nic8[RT_CONFIG1] = (nic8[RT_CONFIG1] & ~0x30) | 0x20; /* Enable FIFO auto-clear */ nic8[RT_CONFIG4] |= 0x80; /* Switch back to normal operation mode */ nic8[RT_CFG9346] = 0; /* Setup Rx buffers */ nic32[RT_RXBUF/4] = 0x01844000; nic32[RT_TXADDR0/4 + 0] = 0x01846000; nic32[RT_TXADDR0/4 + 1] = 0x01846800; nic32[RT_TXADDR0/4 + 2] = 0x01847000; nic32[RT_TXADDR0/4 + 3] = 0x01847800; /* Reset RXMISSED counter */ nic32[RT_RXMISSED/4] = 0; /* Enable receiving broadcast and physical match packets */ nic32[RT_RXCONFIG/4] |= 0x0000000a; /* Filter out all multicast packets */ nic32[RT_MAR0/4 + 0] = 0; nic32[RT_MAR0/4 + 1] = 0; /* Disable all multi-interrupts */ nic16[RT_MULTIINTR/2] = 0; /* Enable RX/TX once more */ nic8[RT_CHIPCMD] = RT_CMD_RX_ENABLE | RT_CMD_TX_ENABLE; /* Initialize status vars */ rtl.cur_tx = 0; rtl.cur_rx = 0; printf("Init complete\r\n"); return 0; } /* Debug register dump */ void bb_dump() { int i; /* Probe the RTL8139C (should be on the bus now) */ for (i=0; i<256; i++) { printf("%08lx ", nic32[i]); if (i && !(i % 8)) printf("\r\n"); } printf("\r\n"); } /* "Receive" the next available packet (if any) */ int bb_rx() { int avail; int processed; uint32 rx_status; int rx_size, pkt_size, ring_offset; int i; uint8 *pkt; processed = 0; /* While we have frames left to process... */ while (!(nic8[RT_CHIPCMD] & 1)) { /* Get frame size and status */ rx_status = mem32[0x4000/4]; rx_size = (rx_status >> 16) & 0xffff; pkt_size = rx_size - 4; if (!(rx_status & 1)) { printf("Frame receive error, status is %08x; skipping\r\n", rx_status); } else { ring_offset = rtl.cur_rx % RX_BUFFER_LEN + 4; /* Print the packet */ pkt = (uint8*)(mem8 + 0x4000); printf("Received packet of size %d (from %08x):\r\n", pkt_size, pkt + ring_offset % RX_BUFFER_LEN); for (i=0; i