/*
 * Copyright (c) 2007, 2008, ETH Zurich. All rights reserved.
 *
 * This file is distributed under the terms in the attached LICENSE file.
 * If you do not find this file, copies can be found by writing to:
 * ETH Zurich D-INFK, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group.
 */

/*
 * rtl8029as.dev
 *
 * DESCRIPTION: RTL8029AS Realtek PCI Full-Duplex Ethernet Controller
 * 		with built-in SRAM
 *
 * This is derived from the IC specification white paper from RealTek
 * (RTL8029AS Preliminary LS009.0 1997.01.16).
 */

device rtl8029as lsbfirst ( io base ) "RTL8029AS Ethernet Controller"
{
  /*
   * 5. REGISTER DESCRIPTIONS
   *
   * The registers in RTL8029AS controller can be roughly divided into
   * two groups by their address and functions -- one for NE2000, the
   * other for PCI Configuration Space.
   */

  /*
   * 5.1. Group 1: NE2000 Registers
   *
   * This group includes 4 pages of registers which are selected by
   * bit PS0 & PS1 in the CR register. Each page contains 16
   * registers. Besides those registers compatible with NE2000, the
   * RTL8029AS controller defines some registers for software
   * configuration and feature enhancement.
   */

  constants rd "Remote read/write" {
    notallowed = 0b000 "Not allowed";
    rrd	       = 0b001 "Remote read";
    rwr	       = 0b010 "Remote write";
    sp	       = 0b011 "Send packet";
    acrdma     = 0b100 "Abort/Complete remote DMA";
  };

  constants ps "Page identification" {
    ne2000p0   = 0b00  "NE2000 compatible page 0";
    ne2000p1   = 0b01  "NE2000 compatible page 1";
    ne2000p2   = 0b10  "NE2000 compatible page 2";
    rtl8029as  = 0b11  "RTL8029AS configuration page 3";
  };
  space page0(addr) bytewise "NE2000 compatible page 0";
  space page1(addr) bytewise "NE2000 compatible page 1";
  space page2(addr) bytewise "NE2000 compatible page 2";
  space p2p0(addr) bytewise "Write page 0, read page 2";
  space rtlp(addr) bytewise "RTL8029AS configuration page 3";

  constants lpconf "Loopback configuration" {
    normal  	   = 0b00    "Normal operation";
    internal	   = 0b01    "Internal loopback";
    external	   = 0b10    "External loopback";
    external1	   = 0b11    "External loopback";
  };

  /* Supported interrupts */
  regtype irq "Interrupts" {
    prx	   1   "Packet received correctly";
    ptx	   1   "Packet transmitted correctly";
    rxe	   1   "Packet receive error";
    txe	   1   "Packet sent aborted (excessive collisions)";
    ovw	   1   "Receive buffer exhausted";
    cnt	   1   "MSB of one or more network tally counters is set";
    rdc	   1   "Remote DMA operation completed";
    rst	   1   "Reset state / Receive buffer overflow";
  };

  /*
   * This register is used to select register pages, enable or disable
   * remote DMA operation and issue commands.
   */
  register cr rw io ( base, 0x0 ) "Command" {
    stp	      1  "Stop command";
    sta	      1	 "Start command";
    txp	      1	 "Transmit packet";
    rd	      3	 type(rd)  "Remote read/write";
    ps	      2	 type(ps)  "Page identification";
  };

  /*
   * This register reflects the NIC status. The host reads it to
   * determine the cause of an interrupt. Individual bits are cleared
   * by writing a "1" into the corresponding bit. It must be cleared
   * after power up.
   *
   * Type=R/W in Page0.
   */
  register isr rw page0( 0x7 ) "Interrupt Status" type(irq);

  /*
   * All bits correspond to the bits in the ISR register. POWER UP=all
   * 0s. Setting individual bits will enable the corresponding
   * interrupts.
   *
   * Type=W in Page0, Type=R in Page2.
   */
  register imr rw p2p0( 0xf ) "Interrupt Mask" type(irq);

  /* Type=W in Page0, Type=R in Page2. */
  register dcr rw p2p0( 0xe ) "Data Configuration" {
    wts	   1   "Word transfer select";
    bos	   1   "Byte order select";
    _	   1   mbz;
    ls	   1   "Loopback select";
    arm	   1   "Auto-Initialize remote";
    ft	   2   "FIFO threshold select";
    _	   1   mb1;
  };

  /* Type=W in Page0, Type=R in Page2 */
  register tcr rw p2p0 ( 0xd ) "Transmit Configuration" {
    crc	   1   "Inhibit CRC generation";
    lb	   2   type(lpconf) "Loopback configuration";
    atd	   1   "Auto transmit disable";
    ofst   1   "Collision offset enable";
    _	   3   mb1;
  };

  /*
   * This register indicates the status of a packet transmission.
   *
   * Type=R in Page0.
   */
  register tsr ro page0( 0x4 ) "Transmit Status" {
    ptx	   1   "Transmission completed";
    _	   1   mb1;
    col	   1   "Collision";
    abt	   1   "Aborted (excessive collisions)";
    crs	   1   "Carrier lost";
    _	   1   mbz;
    cdh	   1   "CD Heartbeat failed";
    owc	   1   "Out of Window collision";
  };

  /* Type=W in Page0, Type=R in Page2. */
  register rcr rw p2p0( 0xc ) "Receive Configuration" {
    sep	   1   "Accept packets with receive errors";
    ar	   1   "Accept length < 64 bytes";
    ab	   1   "Accept broadcast destination address";
    am	   1   "Accept multicast destination address";
    pro	   1   "Promiscuous mode";
    mon	   1   "Monitor mode";
    _	   2   mb1;
  };

  /* Type=R in Page0. */
  register rsr ro page0( 0xc ) "Receive Status" {
    prx	   1   "Packet received correctly";
    crc	   1   "CRC mismatch";
    fae	   1   "Frame alignment error";
    _	   1   mbz;
    mpa	   1   "Missed packet";
    phy	   1   "Physical destination error";
    dis	   1   "Receiver disabled";
    dfr	   1   "Deferring";
  };

  /*
   * These two registers can be read to get the current local DMA
   * address.
   *
   * Type=R in Page0.
   */
  register clda ro page0 ( 0x1 ) "Current Local DMA" type(uint16);

  /*
   * The Page Start register sets the start page address of the
   * receive buffer ring.
   *
   * Type=W in Page0, Type=R in Page 2.
   */
  register pstart rw p2p0( 0x1 ) "Page Start" type(uint8);

  /*
   * PSTOP: Page Stop Register (02H; Type=W in Page0, Type=R in Page2)
   *
   * The Page Stop register sets the stop page address of the receive
   * buffer ring.
   */
  register pstop rw p2p0( 0x2 ) "Page Stop" type(uint8);

  /*
   * BNRY: Boundary Register (03H; Type=R/W in Page0)
   *
   * This register is used to prevent overwrite of the receive buffer
   * ring. It is typically used as a pointer indicating the last receive
   * buffer page the host has read.
   */
   register bnry rw page0( 0x3 ) "Boundary" type(uint8);

  /*
   * TPSR: Transmit Page Start Register (04H; Type=W in Page0)
   *
   * This register sets the start page address of the packet to the
   * transmitted.
   */
   register tpsr wo also page0( 0x4 ) "Transmit Page Start" type(uint8);

  /*
   * TBCR0,1: Transmit Byte Count Registers (05H & 06H; Type=W in
   * Page0)
   *
   * These two registers set the byte counts of the packet to be
   * transmitted.
   */
   register tbcr wo also page0( 0x5 ) "Transmit Byte Count" type(uint16);

  /*
   * NCR: Number of Collisions Register (05H; Type=R in Page0)
   *
   * The register records the number of collisions a node experiences
   * during a packet transmission.
   */
  register ncr ro page0( 0x5 ) "Number of Collisions" type(uint8);

  /*
   * FIFO: First In First Out Register (06H; Type=R in Page0)
   *
   * This register allows the host to examine the contents of the FIFO
   * after loopback.
   */
  register fifo ro page0( 0x6 ) "FIFO" type(uint8);

  /*
   * CRDA0,1: Current Remote DMA Address registers (08H & 09H;
   * Type=R in Page0)
   *
   * These two registers contain the current address of remote
   * DMA.
   */
  register crda ro page0( 0x8 ) "Current Remote DMA Address" type(uint16);

  /*
   * RSAR0,1: Remote Start Address Registers (08H & 09H; Type=W in Page0)
   *
   * These two registers set the start address of remote DMA.
   */
  register rsar wo also page0( 0x8 ) "Remote Start Address" type(uint16);

  /*
   * RBCR0,1: Remote Byte Count Registers (0AH & 0BH; Type=W in Page0)
   *
   * These two registers set the data byte counts of remote DMA.
   */
  register rbcr wo also page0( 0xa ) "Remote Byte Count" type(uint16);

  /*
   * CNTR0: Frame Alignment Error Tally Counter Register (0DH;
   * Type=R in Page0)
   */
  register cntr0 ro page0( 0xd ) "Frame Alignment Error Tally Counter" type(uint8);

  /*
   * CNTR1: CRC Error Tally Counter Register (0EH; Type=R in Page0)
   */
  register cntr1 ro page0( 0xe ) "CRC Error Tally Counter" type(uint8);

  /*
   * CNTR2: Missed Packet Tally Counter Register (0FH; Type=R in
   * Page0)
   */
  register cntr2 ro page0( 0xf ) "Missed Packet Tally Counter" type(uint8);

  /*
   * PAR0-5: Physical Address Registers (01H - 06H; Type=R/W in Page1)
   *
   * These registers contain my Ethernet node address and are used
   * to compare the destination address of incoming packets for
   * acceptation or rejection.
   */
  regarray par rw page1( 0x1 )[6] "Physical Address" type(uint8);

  /*
   * CURR: Current Page Register (07H; Type=R/W in Page1)
   *
   * This register points to the page address of the first receive
   * buffer page to be used for a packet reception.
   */
  register curr rw page1 ( 0x7 ) "Current Page" type(uint8);

  /*
   * MAR0-7: Multicast Address Register (08H - 0FH; Type=R/W in
   * Page1)
   *
   * These registers provide filtering bits of multicast addresses
   * hashed by the CRC logic.
   */
  regarray mar rw page1( 0x8 )[8] "Multicast Address" type(uint8);

  /*
   * 5.1.2.2. RTL8029AS Defined Registers
   */

  /*
   * Page 0 (PS1=0, PS0=0)
   *
   * Two registers are defined to contain the RTL8029AS chip ID and
   * Read Sequence Command is NO LONGER supported in RTL8029AS.
   *
   * Address 0x0a reads ASCII "P", address 0x0b reads ASCII "C".
   */
  regarray rtl8029id ro page0( 0xa )[2] "RTL8029AS Chip ID" type(uint8);

  /*
   * Page 3 (PS1=1, PS0=1).
   */

  constants mode "RTL8029AS operating mode" {
    opnormal  	   = 0b00     "Normal (DP8390 compatible)";
    autoload	   = 0b01     "Auto-load 9346";
    programming	   = 0b10     "9346 programming";
    configwrite    = 0b11     "Config register write enable";
  };

  /* 9346CR: 9346 Command Register (01H; Type=R/W except Bit0=R) */
  register cr9346 rw also rtlp( 0x1 ) "9346 Command" {
    eedo   1;
    eedi   1;
    eesk   1;
    eecs   1;
    _	   2;
    eem    2	type(mode)	"RTL8029AS operating mode";
  };

  /* CONFIG0: RTL8029AS Configuration Register 0 (03H; Type=R) */
  register config0 ro rtlp( 0x3 ) "RTL8029AS Configuration 0" {
    _	   2	     mbz;
    bnc    1	     "10Base2 thin cable connected";
    _	   5;
  };

  constants bromsize "Boot ROM Size" {
    nobrom  = 0b00   "No Boot ROM";
    size8k  = 0b01   "8K Boot ROM";
    size16k = 0b10   "16K Boot ROM";
    size32k = 0b11   "32K Boot ROM";
  };

  constants medium "Network Medium" {
    auto     = 0b00 "TP/CX auto-detect";
    tenbaset = 0b01 "10BaseT";
    tenbase5 = 0b10 "10Base5";
    tenbase2 = 0b11 "10Base2";
  };

  /*
   * CONFIG2: RTL8029AS Configuration Register 2 (05H; Type=R except
   * Bit[7:5]=R/W)
   */
  register config2 rtlp( 0x5 ) "RTL8029AS Configuration 2" {
    bs	   2	ro type(bromsize)  "Boot ROM size";
    _	   2;
    pf     1	ro "Pause flag";
    fce    1	rw "Flow control enable";
    pl     2	rw type(medium)	"Network medium select";
  };

  constants leds "LED Control" {
    rxcol   = 0b00	"RX and COL";
    rxlink  = 0b01	"RX and LINK";
    crscol  = 0b10	"CRS and COL";
    crslink = 0b11	"CRS and LINK";
  };

  /*
   * CONFIG3: RTL8029AS Configuration Register 3 (06H; Type=R except
   * Bit[6,2:1]=R/W)
   */
  register config3 rw rtlp( 0x6 ) "RTL8029AS Configuration 3" {
    _	   1;
    pwrdn  1	"Power down mode";
    sleep  1	"Sleep mode";
    _	   1	mbz;
    leds   2	type(leds)	"LED control";
    fudup  1	"Full-duplex mode";
    _	   1;
  };

  constants clock "Halt Clock Mode" {
    running = 0x52	  "Running";  /* ASCII code of 'R' */
    halted  = 0x48	  "Halted";   /* ASCII code of 'H' */
  };

  /*
   * HLTCLK: Halt Clock Register (09H; Type=W)
   *
   * This is the only active one of Group1 registers when RTL8029AS
   * is inactivated. Writing to this register is invalid if
   * RTL8029AS is not in power down mode. (i.e., If PWRDN bit in
   * CONFIG3 register is zero.) The data written to this register
   * determines the RTL8029AS's power down mode.
   */
  register hltclk wo rtlp( 0x9 ) "Halt Clock" type(uint8);

  /* 8029ASID0,1: RTL8029AS ID = 8029H (0E,0FH; Type=R) */
  register rtl8029asid ro rtlp( 0xe ) "8029AS ID" type(uint16);

  /*
   * 6.2.2 ID PROM Contents
   *
   * The RTL8029AS emulates the ID PROM of NE2000 internally. After
   * 9346 is loaded, the contents of ID PROM are as follows.
   *
   * offset	Byte contents
   * ------	-------------
   * 00H	Ethernet ID0
   * 01H	Ethernet ID1
   * 02H	Ethernet ID2
   * 03H	Ethernet ID3
   * 04H	Ethernet ID4
   * 05H	Ethernet ID5
   * 06H	PID0
   * 07H	PID1
   * 08H	PID2
   * 09H	PID3
   * 0AH	PID4
   * 0BH	PID5
   * 0CH	PID6
   * 0DH	PID7
   * 0EH	57 (ASCII Code of "W")
   * 0FH	57 (ASCII Code of "W")
   * 10H	Ethernet ID0
   * 11H	Ethernet ID1
   * 12H	Ethernet ID2
   * 13H	Ethernet ID3
   * 14H	Ethernet ID4
   * 15H	Ethernet ID5
   * 16H	PID0
   * 17H	PID1
   * 18H	PID2
   * 19H	PID3
   * 1AH	PID4
   * 1BH	PID5
   * 1CH	PID6
   * 1DH	PID7
   * 1EH	42 (ASCII Code of "B")
   * 1FH	42 (ASCII Code of "B")
   */

  /*
   * Remote DMA Port (10H - 17H)
   *
   *
   * The ID PROM and Ethernet packet RAM contents can be read using
   * these registers. Set the start address in RSAR and amount of
   * bytes to transfer in RBCR.
   *
   * RAM start is at offset 0x4000.
   */
   /*
  regarray rdma rw rtlp( 0x10 )[8] "Remote DMA 0" type(uint8);
  */
  register rdma32 rw rtlp( 0x10 ) "Remote DMA 32-bit" type(uint32);
  register rdma16 rw also rtlp( 0x10 ) "Remote DMA 16-bit" type(uint16);
  register rdma8 rw also rtlp( 0x10 ) "Remote DMA 8-bit" type(uint8);

  /*
   * Reset Port
   *
   * Ports in the range 0x18 - 0x1f are reset ports. We only index the
   * last one here as the others are not used on QEMU.
   *
   * Reading from the reset port causes the card to be reset.
   */
  register reset rc rtlp( 0x1f ) "Reset" type(uint8);
};
