xref: /openbmc/qemu/hw/net/lan9118.c (revision a53b931645183bd0c15dd19ae0708fc3c81ecf1d)
149ab747fSPaolo Bonzini /*
249ab747fSPaolo Bonzini  * SMSC LAN9118 Ethernet interface emulation
349ab747fSPaolo Bonzini  *
449ab747fSPaolo Bonzini  * Copyright (c) 2009 CodeSourcery, LLC.
549ab747fSPaolo Bonzini  * Written by Paul Brook
649ab747fSPaolo Bonzini  *
749ab747fSPaolo Bonzini  * This code is licensed under the GNU GPL v2
849ab747fSPaolo Bonzini  *
949ab747fSPaolo Bonzini  * Contributions after 2012-01-13 are licensed under the terms of the
1049ab747fSPaolo Bonzini  * GNU GPL, version 2 or (at your option) any later version.
1149ab747fSPaolo Bonzini  */
1249ab747fSPaolo Bonzini 
13e8d40465SPeter Maydell #include "qemu/osdep.h"
1449ab747fSPaolo Bonzini #include "hw/sysbus.h"
15d6454270SMarkus Armbruster #include "migration/vmstate.h"
1649ab747fSPaolo Bonzini #include "net/net.h"
17eedeaee7SMark Cave-Ayland #include "net/eth.h"
1864552b6bSMarkus Armbruster #include "hw/irq.h"
1966b03dceSPhilippe Mathieu-Daudé #include "hw/net/lan9118.h"
2049ab747fSPaolo Bonzini #include "hw/ptimer.h"
21a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h"
223e80f690SMarkus Armbruster #include "qapi/error.h"
2303dd024fSPaolo Bonzini #include "qemu/log.h"
240b8fa32fSMarkus Armbruster #include "qemu/module.h"
25*5691f477SMichael Tokarev #include <zlib.h> /* for crc32 */
26db1015e9SEduardo Habkost #include "qom/object.h"
2749ab747fSPaolo Bonzini 
2849ab747fSPaolo Bonzini //#define DEBUG_LAN9118
2949ab747fSPaolo Bonzini 
3049ab747fSPaolo Bonzini #ifdef DEBUG_LAN9118
3149ab747fSPaolo Bonzini #define DPRINTF(fmt, ...) \
3249ab747fSPaolo Bonzini do { printf("lan9118: " fmt , ## __VA_ARGS__); } while (0)
3349ab747fSPaolo Bonzini #else
3449ab747fSPaolo Bonzini #define DPRINTF(fmt, ...) do {} while(0)
3549ab747fSPaolo Bonzini #endif
3649ab747fSPaolo Bonzini 
375cab6d5aSPeter Maydell /* The tx and rx fifo ports are a range of aliased 32-bit registers */
385cab6d5aSPeter Maydell #define RX_DATA_FIFO_PORT_FIRST 0x00
395cab6d5aSPeter Maydell #define RX_DATA_FIFO_PORT_LAST 0x1f
405cab6d5aSPeter Maydell #define TX_DATA_FIFO_PORT_FIRST 0x20
415cab6d5aSPeter Maydell #define TX_DATA_FIFO_PORT_LAST 0x3f
425cab6d5aSPeter Maydell 
435cab6d5aSPeter Maydell #define RX_STATUS_FIFO_PORT 0x40
445cab6d5aSPeter Maydell #define RX_STATUS_FIFO_PEEK 0x44
455cab6d5aSPeter Maydell #define TX_STATUS_FIFO_PORT 0x48
465cab6d5aSPeter Maydell #define TX_STATUS_FIFO_PEEK 0x4c
475cab6d5aSPeter Maydell 
4849ab747fSPaolo Bonzini #define CSR_ID_REV      0x50
4949ab747fSPaolo Bonzini #define CSR_IRQ_CFG     0x54
5049ab747fSPaolo Bonzini #define CSR_INT_STS     0x58
5149ab747fSPaolo Bonzini #define CSR_INT_EN      0x5c
5249ab747fSPaolo Bonzini #define CSR_BYTE_TEST   0x64
5349ab747fSPaolo Bonzini #define CSR_FIFO_INT    0x68
5449ab747fSPaolo Bonzini #define CSR_RX_CFG      0x6c
5549ab747fSPaolo Bonzini #define CSR_TX_CFG      0x70
5649ab747fSPaolo Bonzini #define CSR_HW_CFG      0x74
5749ab747fSPaolo Bonzini #define CSR_RX_DP_CTRL  0x78
5849ab747fSPaolo Bonzini #define CSR_RX_FIFO_INF 0x7c
5949ab747fSPaolo Bonzini #define CSR_TX_FIFO_INF 0x80
6049ab747fSPaolo Bonzini #define CSR_PMT_CTRL    0x84
6149ab747fSPaolo Bonzini #define CSR_GPIO_CFG    0x88
6249ab747fSPaolo Bonzini #define CSR_GPT_CFG     0x8c
6349ab747fSPaolo Bonzini #define CSR_GPT_CNT     0x90
6449ab747fSPaolo Bonzini #define CSR_WORD_SWAP   0x98
6549ab747fSPaolo Bonzini #define CSR_FREE_RUN    0x9c
6649ab747fSPaolo Bonzini #define CSR_RX_DROP     0xa0
6749ab747fSPaolo Bonzini #define CSR_MAC_CSR_CMD 0xa4
6849ab747fSPaolo Bonzini #define CSR_MAC_CSR_DATA 0xa8
6949ab747fSPaolo Bonzini #define CSR_AFC_CFG     0xac
7049ab747fSPaolo Bonzini #define CSR_E2P_CMD     0xb0
7149ab747fSPaolo Bonzini #define CSR_E2P_DATA    0xb4
7249ab747fSPaolo Bonzini 
7312fdd928SAndrew Baumann #define E2P_CMD_MAC_ADDR_LOADED 0x100
7412fdd928SAndrew Baumann 
7549ab747fSPaolo Bonzini /* IRQ_CFG */
7649ab747fSPaolo Bonzini #define IRQ_INT         0x00001000
7749ab747fSPaolo Bonzini #define IRQ_EN          0x00000100
7849ab747fSPaolo Bonzini #define IRQ_POL         0x00000010
7949ab747fSPaolo Bonzini #define IRQ_TYPE        0x00000001
8049ab747fSPaolo Bonzini 
8149ab747fSPaolo Bonzini /* INT_STS/INT_EN */
8249ab747fSPaolo Bonzini #define SW_INT          0x80000000
8349ab747fSPaolo Bonzini #define TXSTOP_INT      0x02000000
8449ab747fSPaolo Bonzini #define RXSTOP_INT      0x01000000
8549ab747fSPaolo Bonzini #define RXDFH_INT       0x00800000
8649ab747fSPaolo Bonzini #define TX_IOC_INT      0x00200000
8749ab747fSPaolo Bonzini #define RXD_INT         0x00100000
8849ab747fSPaolo Bonzini #define GPT_INT         0x00080000
8949ab747fSPaolo Bonzini #define PHY_INT         0x00040000
9049ab747fSPaolo Bonzini #define PME_INT         0x00020000
9149ab747fSPaolo Bonzini #define TXSO_INT        0x00010000
9249ab747fSPaolo Bonzini #define RWT_INT         0x00008000
9349ab747fSPaolo Bonzini #define RXE_INT         0x00004000
9449ab747fSPaolo Bonzini #define TXE_INT         0x00002000
9549ab747fSPaolo Bonzini #define TDFU_INT        0x00000800
9649ab747fSPaolo Bonzini #define TDFO_INT        0x00000400
9749ab747fSPaolo Bonzini #define TDFA_INT        0x00000200
9849ab747fSPaolo Bonzini #define TSFF_INT        0x00000100
9949ab747fSPaolo Bonzini #define TSFL_INT        0x00000080
10049ab747fSPaolo Bonzini #define RXDF_INT        0x00000040
10149ab747fSPaolo Bonzini #define RDFL_INT        0x00000020
10249ab747fSPaolo Bonzini #define RSFF_INT        0x00000010
10349ab747fSPaolo Bonzini #define RSFL_INT        0x00000008
10449ab747fSPaolo Bonzini #define GPIO2_INT       0x00000004
10549ab747fSPaolo Bonzini #define GPIO1_INT       0x00000002
10649ab747fSPaolo Bonzini #define GPIO0_INT       0x00000001
10749ab747fSPaolo Bonzini #define RESERVED_INT    0x7c001000
10849ab747fSPaolo Bonzini 
10949ab747fSPaolo Bonzini #define MAC_CR          1
11049ab747fSPaolo Bonzini #define MAC_ADDRH       2
11149ab747fSPaolo Bonzini #define MAC_ADDRL       3
11249ab747fSPaolo Bonzini #define MAC_HASHH       4
11349ab747fSPaolo Bonzini #define MAC_HASHL       5
11449ab747fSPaolo Bonzini #define MAC_MII_ACC     6
11549ab747fSPaolo Bonzini #define MAC_MII_DATA    7
11649ab747fSPaolo Bonzini #define MAC_FLOW        8
11749ab747fSPaolo Bonzini #define MAC_VLAN1       9 /* TODO */
11849ab747fSPaolo Bonzini #define MAC_VLAN2       10 /* TODO */
11949ab747fSPaolo Bonzini #define MAC_WUFF        11 /* TODO */
12049ab747fSPaolo Bonzini #define MAC_WUCSR       12 /* TODO */
12149ab747fSPaolo Bonzini 
12249ab747fSPaolo Bonzini #define MAC_CR_RXALL    0x80000000
12349ab747fSPaolo Bonzini #define MAC_CR_RCVOWN   0x00800000
12449ab747fSPaolo Bonzini #define MAC_CR_LOOPBK   0x00200000
12549ab747fSPaolo Bonzini #define MAC_CR_FDPX     0x00100000
12649ab747fSPaolo Bonzini #define MAC_CR_MCPAS    0x00080000
12749ab747fSPaolo Bonzini #define MAC_CR_PRMS     0x00040000
12849ab747fSPaolo Bonzini #define MAC_CR_INVFILT  0x00020000
12949ab747fSPaolo Bonzini #define MAC_CR_PASSBAD  0x00010000
13049ab747fSPaolo Bonzini #define MAC_CR_HO       0x00008000
13149ab747fSPaolo Bonzini #define MAC_CR_HPFILT   0x00002000
13249ab747fSPaolo Bonzini #define MAC_CR_LCOLL    0x00001000
13349ab747fSPaolo Bonzini #define MAC_CR_BCAST    0x00000800
13449ab747fSPaolo Bonzini #define MAC_CR_DISRTY   0x00000400
13549ab747fSPaolo Bonzini #define MAC_CR_PADSTR   0x00000100
13649ab747fSPaolo Bonzini #define MAC_CR_BOLMT    0x000000c0
13749ab747fSPaolo Bonzini #define MAC_CR_DFCHK    0x00000020
13849ab747fSPaolo Bonzini #define MAC_CR_TXEN     0x00000008
13949ab747fSPaolo Bonzini #define MAC_CR_RXEN     0x00000004
14049ab747fSPaolo Bonzini #define MAC_CR_RESERVED 0x7f404213
14149ab747fSPaolo Bonzini 
14249ab747fSPaolo Bonzini #define PHY_INT_ENERGYON            0x80
14349ab747fSPaolo Bonzini #define PHY_INT_AUTONEG_COMPLETE    0x40
14449ab747fSPaolo Bonzini #define PHY_INT_FAULT               0x20
14549ab747fSPaolo Bonzini #define PHY_INT_DOWN                0x10
14649ab747fSPaolo Bonzini #define PHY_INT_AUTONEG_LP          0x08
14749ab747fSPaolo Bonzini #define PHY_INT_PARFAULT            0x04
14849ab747fSPaolo Bonzini #define PHY_INT_AUTONEG_PAGE        0x02
14949ab747fSPaolo Bonzini 
15049ab747fSPaolo Bonzini #define GPT_TIMER_EN    0x20000000
15149ab747fSPaolo Bonzini 
152a4522346SPhilippe Mathieu-Daudé /*
153a4522346SPhilippe Mathieu-Daudé  * The MAC Interface Layer (MIL), within the MAC, contains a 2K Byte transmit
154a4522346SPhilippe Mathieu-Daudé  * and a 128 Byte receive FIFO which is separate from the TX and RX FIFOs.
155a4522346SPhilippe Mathieu-Daudé  */
156a4522346SPhilippe Mathieu-Daudé #define MIL_TXFIFO_SIZE         2048
157a4522346SPhilippe Mathieu-Daudé 
15849ab747fSPaolo Bonzini enum tx_state {
15949ab747fSPaolo Bonzini     TX_IDLE,
16049ab747fSPaolo Bonzini     TX_B,
16149ab747fSPaolo Bonzini     TX_DATA
16249ab747fSPaolo Bonzini };
16349ab747fSPaolo Bonzini 
16449ab747fSPaolo Bonzini typedef struct {
16549ab747fSPaolo Bonzini     /* state is a tx_state but we can't put enums in VMStateDescriptions. */
16649ab747fSPaolo Bonzini     uint32_t state;
16749ab747fSPaolo Bonzini     uint32_t cmd_a;
16849ab747fSPaolo Bonzini     uint32_t cmd_b;
16949ab747fSPaolo Bonzini     int32_t buffer_size;
17049ab747fSPaolo Bonzini     int32_t offset;
17149ab747fSPaolo Bonzini     int32_t pad;
17249ab747fSPaolo Bonzini     int32_t fifo_used;
17349ab747fSPaolo Bonzini     int32_t len;
174a4522346SPhilippe Mathieu-Daudé     uint8_t data[MIL_TXFIFO_SIZE];
17549ab747fSPaolo Bonzini } LAN9118Packet;
17649ab747fSPaolo Bonzini 
17749ab747fSPaolo Bonzini static const VMStateDescription vmstate_lan9118_packet = {
17849ab747fSPaolo Bonzini     .name = "lan9118_packet",
17949ab747fSPaolo Bonzini     .version_id = 1,
18049ab747fSPaolo Bonzini     .minimum_version_id = 1,
1811de81b42SRichard Henderson     .fields = (const VMStateField[]) {
18249ab747fSPaolo Bonzini         VMSTATE_UINT32(state, LAN9118Packet),
18349ab747fSPaolo Bonzini         VMSTATE_UINT32(cmd_a, LAN9118Packet),
18449ab747fSPaolo Bonzini         VMSTATE_UINT32(cmd_b, LAN9118Packet),
18549ab747fSPaolo Bonzini         VMSTATE_INT32(buffer_size, LAN9118Packet),
18649ab747fSPaolo Bonzini         VMSTATE_INT32(offset, LAN9118Packet),
18749ab747fSPaolo Bonzini         VMSTATE_INT32(pad, LAN9118Packet),
18849ab747fSPaolo Bonzini         VMSTATE_INT32(fifo_used, LAN9118Packet),
18949ab747fSPaolo Bonzini         VMSTATE_INT32(len, LAN9118Packet),
190a4522346SPhilippe Mathieu-Daudé         VMSTATE_UINT8_ARRAY(data, LAN9118Packet, MIL_TXFIFO_SIZE),
19149ab747fSPaolo Bonzini         VMSTATE_END_OF_LIST()
19249ab747fSPaolo Bonzini     }
19349ab747fSPaolo Bonzini };
19449ab747fSPaolo Bonzini 
1958063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(lan9118_state, LAN9118)
1963ff66d10SAndreas Färber 
197db1015e9SEduardo Habkost struct lan9118_state {
1983ff66d10SAndreas Färber     SysBusDevice parent_obj;
1993ff66d10SAndreas Färber 
20049ab747fSPaolo Bonzini     NICState *nic;
20149ab747fSPaolo Bonzini     NICConf conf;
20249ab747fSPaolo Bonzini     qemu_irq irq;
20349ab747fSPaolo Bonzini     MemoryRegion mmio;
20449ab747fSPaolo Bonzini     ptimer_state *timer;
20549ab747fSPaolo Bonzini 
20649ab747fSPaolo Bonzini     uint32_t irq_cfg;
20749ab747fSPaolo Bonzini     uint32_t int_sts;
20849ab747fSPaolo Bonzini     uint32_t int_en;
20949ab747fSPaolo Bonzini     uint32_t fifo_int;
21049ab747fSPaolo Bonzini     uint32_t rx_cfg;
21149ab747fSPaolo Bonzini     uint32_t tx_cfg;
21249ab747fSPaolo Bonzini     uint32_t hw_cfg;
21349ab747fSPaolo Bonzini     uint32_t pmt_ctrl;
21449ab747fSPaolo Bonzini     uint32_t gpio_cfg;
21549ab747fSPaolo Bonzini     uint32_t gpt_cfg;
21649ab747fSPaolo Bonzini     uint32_t word_swap;
21749ab747fSPaolo Bonzini     uint32_t free_timer_start;
21849ab747fSPaolo Bonzini     uint32_t mac_cmd;
21949ab747fSPaolo Bonzini     uint32_t mac_data;
22049ab747fSPaolo Bonzini     uint32_t afc_cfg;
22149ab747fSPaolo Bonzini     uint32_t e2p_cmd;
22249ab747fSPaolo Bonzini     uint32_t e2p_data;
22349ab747fSPaolo Bonzini 
22449ab747fSPaolo Bonzini     uint32_t mac_cr;
22549ab747fSPaolo Bonzini     uint32_t mac_hashh;
22649ab747fSPaolo Bonzini     uint32_t mac_hashl;
22749ab747fSPaolo Bonzini     uint32_t mac_mii_acc;
22849ab747fSPaolo Bonzini     uint32_t mac_mii_data;
22949ab747fSPaolo Bonzini     uint32_t mac_flow;
23049ab747fSPaolo Bonzini 
23149ab747fSPaolo Bonzini     uint32_t phy_status;
23249ab747fSPaolo Bonzini     uint32_t phy_control;
23349ab747fSPaolo Bonzini     uint32_t phy_advertise;
23449ab747fSPaolo Bonzini     uint32_t phy_int;
23549ab747fSPaolo Bonzini     uint32_t phy_int_mask;
23649ab747fSPaolo Bonzini 
23749ab747fSPaolo Bonzini     int32_t eeprom_writable;
23849ab747fSPaolo Bonzini     uint8_t eeprom[128];
23949ab747fSPaolo Bonzini 
24049ab747fSPaolo Bonzini     int32_t tx_fifo_size;
24149ab747fSPaolo Bonzini     LAN9118Packet *txp;
24249ab747fSPaolo Bonzini     LAN9118Packet tx_packet;
24349ab747fSPaolo Bonzini 
24449ab747fSPaolo Bonzini     int32_t tx_status_fifo_used;
24549ab747fSPaolo Bonzini     int32_t tx_status_fifo_head;
24649ab747fSPaolo Bonzini     uint32_t tx_status_fifo[512];
24749ab747fSPaolo Bonzini 
24849ab747fSPaolo Bonzini     int32_t rx_status_fifo_size;
24949ab747fSPaolo Bonzini     int32_t rx_status_fifo_used;
25049ab747fSPaolo Bonzini     int32_t rx_status_fifo_head;
25149ab747fSPaolo Bonzini     uint32_t rx_status_fifo[896];
25249ab747fSPaolo Bonzini     int32_t rx_fifo_size;
25349ab747fSPaolo Bonzini     int32_t rx_fifo_used;
25449ab747fSPaolo Bonzini     int32_t rx_fifo_head;
25549ab747fSPaolo Bonzini     uint32_t rx_fifo[3360];
25649ab747fSPaolo Bonzini     int32_t rx_packet_size_head;
25749ab747fSPaolo Bonzini     int32_t rx_packet_size_tail;
25849ab747fSPaolo Bonzini     int32_t rx_packet_size[1024];
25949ab747fSPaolo Bonzini 
26049ab747fSPaolo Bonzini     int32_t rxp_offset;
26149ab747fSPaolo Bonzini     int32_t rxp_size;
26249ab747fSPaolo Bonzini     int32_t rxp_pad;
26349ab747fSPaolo Bonzini 
26449ab747fSPaolo Bonzini     uint32_t write_word_prev_offset;
26549ab747fSPaolo Bonzini     uint32_t write_word_n;
26649ab747fSPaolo Bonzini     uint16_t write_word_l;
26749ab747fSPaolo Bonzini     uint16_t write_word_h;
26849ab747fSPaolo Bonzini     uint32_t read_word_prev_offset;
26949ab747fSPaolo Bonzini     uint32_t read_word_n;
27049ab747fSPaolo Bonzini     uint32_t read_long;
27149ab747fSPaolo Bonzini 
27249ab747fSPaolo Bonzini     uint32_t mode_16bit;
273db1015e9SEduardo Habkost };
27449ab747fSPaolo Bonzini 
27549ab747fSPaolo Bonzini static const VMStateDescription vmstate_lan9118 = {
27649ab747fSPaolo Bonzini     .name = "lan9118",
27749ab747fSPaolo Bonzini     .version_id = 2,
27849ab747fSPaolo Bonzini     .minimum_version_id = 1,
2791de81b42SRichard Henderson     .fields = (const VMStateField[]) {
28049ab747fSPaolo Bonzini         VMSTATE_PTIMER(timer, lan9118_state),
28149ab747fSPaolo Bonzini         VMSTATE_UINT32(irq_cfg, lan9118_state),
28249ab747fSPaolo Bonzini         VMSTATE_UINT32(int_sts, lan9118_state),
28349ab747fSPaolo Bonzini         VMSTATE_UINT32(int_en, lan9118_state),
28449ab747fSPaolo Bonzini         VMSTATE_UINT32(fifo_int, lan9118_state),
28549ab747fSPaolo Bonzini         VMSTATE_UINT32(rx_cfg, lan9118_state),
28649ab747fSPaolo Bonzini         VMSTATE_UINT32(tx_cfg, lan9118_state),
28749ab747fSPaolo Bonzini         VMSTATE_UINT32(hw_cfg, lan9118_state),
28849ab747fSPaolo Bonzini         VMSTATE_UINT32(pmt_ctrl, lan9118_state),
28949ab747fSPaolo Bonzini         VMSTATE_UINT32(gpio_cfg, lan9118_state),
29049ab747fSPaolo Bonzini         VMSTATE_UINT32(gpt_cfg, lan9118_state),
29149ab747fSPaolo Bonzini         VMSTATE_UINT32(word_swap, lan9118_state),
29249ab747fSPaolo Bonzini         VMSTATE_UINT32(free_timer_start, lan9118_state),
29349ab747fSPaolo Bonzini         VMSTATE_UINT32(mac_cmd, lan9118_state),
29449ab747fSPaolo Bonzini         VMSTATE_UINT32(mac_data, lan9118_state),
29549ab747fSPaolo Bonzini         VMSTATE_UINT32(afc_cfg, lan9118_state),
29649ab747fSPaolo Bonzini         VMSTATE_UINT32(e2p_cmd, lan9118_state),
29749ab747fSPaolo Bonzini         VMSTATE_UINT32(e2p_data, lan9118_state),
29849ab747fSPaolo Bonzini         VMSTATE_UINT32(mac_cr, lan9118_state),
29949ab747fSPaolo Bonzini         VMSTATE_UINT32(mac_hashh, lan9118_state),
30049ab747fSPaolo Bonzini         VMSTATE_UINT32(mac_hashl, lan9118_state),
30149ab747fSPaolo Bonzini         VMSTATE_UINT32(mac_mii_acc, lan9118_state),
30249ab747fSPaolo Bonzini         VMSTATE_UINT32(mac_mii_data, lan9118_state),
30349ab747fSPaolo Bonzini         VMSTATE_UINT32(mac_flow, lan9118_state),
30449ab747fSPaolo Bonzini         VMSTATE_UINT32(phy_status, lan9118_state),
30549ab747fSPaolo Bonzini         VMSTATE_UINT32(phy_control, lan9118_state),
30649ab747fSPaolo Bonzini         VMSTATE_UINT32(phy_advertise, lan9118_state),
30749ab747fSPaolo Bonzini         VMSTATE_UINT32(phy_int, lan9118_state),
30849ab747fSPaolo Bonzini         VMSTATE_UINT32(phy_int_mask, lan9118_state),
30949ab747fSPaolo Bonzini         VMSTATE_INT32(eeprom_writable, lan9118_state),
31049ab747fSPaolo Bonzini         VMSTATE_UINT8_ARRAY(eeprom, lan9118_state, 128),
31149ab747fSPaolo Bonzini         VMSTATE_INT32(tx_fifo_size, lan9118_state),
31249ab747fSPaolo Bonzini         /* txp always points at tx_packet so need not be saved */
31349ab747fSPaolo Bonzini         VMSTATE_STRUCT(tx_packet, lan9118_state, 0,
31449ab747fSPaolo Bonzini                        vmstate_lan9118_packet, LAN9118Packet),
31549ab747fSPaolo Bonzini         VMSTATE_INT32(tx_status_fifo_used, lan9118_state),
31649ab747fSPaolo Bonzini         VMSTATE_INT32(tx_status_fifo_head, lan9118_state),
31749ab747fSPaolo Bonzini         VMSTATE_UINT32_ARRAY(tx_status_fifo, lan9118_state, 512),
31849ab747fSPaolo Bonzini         VMSTATE_INT32(rx_status_fifo_size, lan9118_state),
31949ab747fSPaolo Bonzini         VMSTATE_INT32(rx_status_fifo_used, lan9118_state),
32049ab747fSPaolo Bonzini         VMSTATE_INT32(rx_status_fifo_head, lan9118_state),
32149ab747fSPaolo Bonzini         VMSTATE_UINT32_ARRAY(rx_status_fifo, lan9118_state, 896),
32249ab747fSPaolo Bonzini         VMSTATE_INT32(rx_fifo_size, lan9118_state),
32349ab747fSPaolo Bonzini         VMSTATE_INT32(rx_fifo_used, lan9118_state),
32449ab747fSPaolo Bonzini         VMSTATE_INT32(rx_fifo_head, lan9118_state),
32549ab747fSPaolo Bonzini         VMSTATE_UINT32_ARRAY(rx_fifo, lan9118_state, 3360),
32649ab747fSPaolo Bonzini         VMSTATE_INT32(rx_packet_size_head, lan9118_state),
32749ab747fSPaolo Bonzini         VMSTATE_INT32(rx_packet_size_tail, lan9118_state),
32849ab747fSPaolo Bonzini         VMSTATE_INT32_ARRAY(rx_packet_size, lan9118_state, 1024),
32949ab747fSPaolo Bonzini         VMSTATE_INT32(rxp_offset, lan9118_state),
33049ab747fSPaolo Bonzini         VMSTATE_INT32(rxp_size, lan9118_state),
33149ab747fSPaolo Bonzini         VMSTATE_INT32(rxp_pad, lan9118_state),
33249ab747fSPaolo Bonzini         VMSTATE_UINT32_V(write_word_prev_offset, lan9118_state, 2),
33349ab747fSPaolo Bonzini         VMSTATE_UINT32_V(write_word_n, lan9118_state, 2),
33449ab747fSPaolo Bonzini         VMSTATE_UINT16_V(write_word_l, lan9118_state, 2),
33549ab747fSPaolo Bonzini         VMSTATE_UINT16_V(write_word_h, lan9118_state, 2),
33649ab747fSPaolo Bonzini         VMSTATE_UINT32_V(read_word_prev_offset, lan9118_state, 2),
33749ab747fSPaolo Bonzini         VMSTATE_UINT32_V(read_word_n, lan9118_state, 2),
33849ab747fSPaolo Bonzini         VMSTATE_UINT32_V(read_long, lan9118_state, 2),
33949ab747fSPaolo Bonzini         VMSTATE_UINT32_V(mode_16bit, lan9118_state, 2),
34049ab747fSPaolo Bonzini         VMSTATE_END_OF_LIST()
34149ab747fSPaolo Bonzini     }
34249ab747fSPaolo Bonzini };
34349ab747fSPaolo Bonzini 
lan9118_update(lan9118_state * s)34449ab747fSPaolo Bonzini static void lan9118_update(lan9118_state *s)
34549ab747fSPaolo Bonzini {
34649ab747fSPaolo Bonzini     int level;
34749ab747fSPaolo Bonzini 
34849ab747fSPaolo Bonzini     /* TODO: Implement FIFO level IRQs.  */
34949ab747fSPaolo Bonzini     level = (s->int_sts & s->int_en) != 0;
35049ab747fSPaolo Bonzini     if (level) {
35149ab747fSPaolo Bonzini         s->irq_cfg |= IRQ_INT;
35249ab747fSPaolo Bonzini     } else {
35349ab747fSPaolo Bonzini         s->irq_cfg &= ~IRQ_INT;
35449ab747fSPaolo Bonzini     }
35549ab747fSPaolo Bonzini     if ((s->irq_cfg & IRQ_EN) == 0) {
35649ab747fSPaolo Bonzini         level = 0;
35749ab747fSPaolo Bonzini     }
35849ab747fSPaolo Bonzini     if ((s->irq_cfg & (IRQ_TYPE | IRQ_POL)) != (IRQ_TYPE | IRQ_POL)) {
35949ab747fSPaolo Bonzini         /* Interrupt is active low unless we're configured as
36049ab747fSPaolo Bonzini          * active-high polarity, push-pull type.
36149ab747fSPaolo Bonzini          */
36249ab747fSPaolo Bonzini         level = !level;
36349ab747fSPaolo Bonzini     }
36449ab747fSPaolo Bonzini     qemu_set_irq(s->irq, level);
36549ab747fSPaolo Bonzini }
36649ab747fSPaolo Bonzini 
lan9118_mac_changed(lan9118_state * s)36749ab747fSPaolo Bonzini static void lan9118_mac_changed(lan9118_state *s)
36849ab747fSPaolo Bonzini {
36949ab747fSPaolo Bonzini     qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
37049ab747fSPaolo Bonzini }
37149ab747fSPaolo Bonzini 
lan9118_reload_eeprom(lan9118_state * s)37249ab747fSPaolo Bonzini static void lan9118_reload_eeprom(lan9118_state *s)
37349ab747fSPaolo Bonzini {
37449ab747fSPaolo Bonzini     int i;
37549ab747fSPaolo Bonzini     if (s->eeprom[0] != 0xa5) {
37612fdd928SAndrew Baumann         s->e2p_cmd &= ~E2P_CMD_MAC_ADDR_LOADED;
37749ab747fSPaolo Bonzini         DPRINTF("MACADDR load failed\n");
37849ab747fSPaolo Bonzini         return;
37949ab747fSPaolo Bonzini     }
38049ab747fSPaolo Bonzini     for (i = 0; i < 6; i++) {
38149ab747fSPaolo Bonzini         s->conf.macaddr.a[i] = s->eeprom[i + 1];
38249ab747fSPaolo Bonzini     }
38312fdd928SAndrew Baumann     s->e2p_cmd |= E2P_CMD_MAC_ADDR_LOADED;
38449ab747fSPaolo Bonzini     DPRINTF("MACADDR loaded from eeprom\n");
38549ab747fSPaolo Bonzini     lan9118_mac_changed(s);
38649ab747fSPaolo Bonzini }
38749ab747fSPaolo Bonzini 
phy_update_irq(lan9118_state * s)38849ab747fSPaolo Bonzini static void phy_update_irq(lan9118_state *s)
38949ab747fSPaolo Bonzini {
39049ab747fSPaolo Bonzini     if (s->phy_int & s->phy_int_mask) {
39149ab747fSPaolo Bonzini         s->int_sts |= PHY_INT;
39249ab747fSPaolo Bonzini     } else {
39349ab747fSPaolo Bonzini         s->int_sts &= ~PHY_INT;
39449ab747fSPaolo Bonzini     }
39549ab747fSPaolo Bonzini     lan9118_update(s);
39649ab747fSPaolo Bonzini }
39749ab747fSPaolo Bonzini 
phy_update_link(lan9118_state * s)39849ab747fSPaolo Bonzini static void phy_update_link(lan9118_state *s)
39949ab747fSPaolo Bonzini {
40049ab747fSPaolo Bonzini     /* Autonegotiation status mirrors link status.  */
40149ab747fSPaolo Bonzini     if (qemu_get_queue(s->nic)->link_down) {
40249ab747fSPaolo Bonzini         s->phy_status &= ~0x0024;
40349ab747fSPaolo Bonzini         s->phy_int |= PHY_INT_DOWN;
40449ab747fSPaolo Bonzini     } else {
40549ab747fSPaolo Bonzini         s->phy_status |= 0x0024;
40649ab747fSPaolo Bonzini         s->phy_int |= PHY_INT_ENERGYON;
40749ab747fSPaolo Bonzini         s->phy_int |= PHY_INT_AUTONEG_COMPLETE;
40849ab747fSPaolo Bonzini     }
40949ab747fSPaolo Bonzini     phy_update_irq(s);
41049ab747fSPaolo Bonzini }
41149ab747fSPaolo Bonzini 
lan9118_set_link(NetClientState * nc)41249ab747fSPaolo Bonzini static void lan9118_set_link(NetClientState *nc)
41349ab747fSPaolo Bonzini {
41449ab747fSPaolo Bonzini     phy_update_link(qemu_get_nic_opaque(nc));
41549ab747fSPaolo Bonzini }
41649ab747fSPaolo Bonzini 
phy_reset(lan9118_state * s)41749ab747fSPaolo Bonzini static void phy_reset(lan9118_state *s)
41849ab747fSPaolo Bonzini {
41949ab747fSPaolo Bonzini     s->phy_status = 0x7809;
42049ab747fSPaolo Bonzini     s->phy_control = 0x3000;
42149ab747fSPaolo Bonzini     s->phy_advertise = 0x01e1;
42249ab747fSPaolo Bonzini     s->phy_int_mask = 0;
42349ab747fSPaolo Bonzini     s->phy_int = 0;
42449ab747fSPaolo Bonzini     phy_update_link(s);
42549ab747fSPaolo Bonzini }
42649ab747fSPaolo Bonzini 
lan9118_reset(DeviceState * d)42749ab747fSPaolo Bonzini static void lan9118_reset(DeviceState *d)
42849ab747fSPaolo Bonzini {
4293ff66d10SAndreas Färber     lan9118_state *s = LAN9118(d);
4303ff66d10SAndreas Färber 
43149ab747fSPaolo Bonzini     s->irq_cfg &= (IRQ_TYPE | IRQ_POL);
43249ab747fSPaolo Bonzini     s->int_sts = 0;
43349ab747fSPaolo Bonzini     s->int_en = 0;
43449ab747fSPaolo Bonzini     s->fifo_int = 0x48000000;
43549ab747fSPaolo Bonzini     s->rx_cfg = 0;
43649ab747fSPaolo Bonzini     s->tx_cfg = 0;
43749ab747fSPaolo Bonzini     s->hw_cfg = s->mode_16bit ? 0x00050000 : 0x00050004;
43849ab747fSPaolo Bonzini     s->pmt_ctrl &= 0x45;
43949ab747fSPaolo Bonzini     s->gpio_cfg = 0;
44049ab747fSPaolo Bonzini     s->txp->fifo_used = 0;
44149ab747fSPaolo Bonzini     s->txp->state = TX_IDLE;
44249ab747fSPaolo Bonzini     s->txp->cmd_a = 0xffffffffu;
44349ab747fSPaolo Bonzini     s->txp->cmd_b = 0xffffffffu;
44449ab747fSPaolo Bonzini     s->txp->len = 0;
44549ab747fSPaolo Bonzini     s->txp->fifo_used = 0;
44649ab747fSPaolo Bonzini     s->tx_fifo_size = 4608;
44749ab747fSPaolo Bonzini     s->tx_status_fifo_used = 0;
44849ab747fSPaolo Bonzini     s->rx_status_fifo_size = 704;
44949ab747fSPaolo Bonzini     s->rx_fifo_size = 2640;
45049ab747fSPaolo Bonzini     s->rx_fifo_used = 0;
45149ab747fSPaolo Bonzini     s->rx_status_fifo_size = 176;
45249ab747fSPaolo Bonzini     s->rx_status_fifo_used = 0;
45349ab747fSPaolo Bonzini     s->rxp_offset = 0;
45449ab747fSPaolo Bonzini     s->rxp_size = 0;
45549ab747fSPaolo Bonzini     s->rxp_pad = 0;
45649ab747fSPaolo Bonzini     s->rx_packet_size_tail = s->rx_packet_size_head;
45749ab747fSPaolo Bonzini     s->rx_packet_size[s->rx_packet_size_head] = 0;
45849ab747fSPaolo Bonzini     s->mac_cmd = 0;
45949ab747fSPaolo Bonzini     s->mac_data = 0;
46049ab747fSPaolo Bonzini     s->afc_cfg = 0;
46149ab747fSPaolo Bonzini     s->e2p_cmd = 0;
46249ab747fSPaolo Bonzini     s->e2p_data = 0;
463bc72ad67SAlex Bligh     s->free_timer_start = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / 40;
46449ab747fSPaolo Bonzini 
46588e4bd67SPeter Maydell     ptimer_transaction_begin(s->timer);
46649ab747fSPaolo Bonzini     ptimer_stop(s->timer);
46749ab747fSPaolo Bonzini     ptimer_set_count(s->timer, 0xffff);
46888e4bd67SPeter Maydell     ptimer_transaction_commit(s->timer);
46949ab747fSPaolo Bonzini     s->gpt_cfg = 0xffff;
47049ab747fSPaolo Bonzini 
47149ab747fSPaolo Bonzini     s->mac_cr = MAC_CR_PRMS;
47249ab747fSPaolo Bonzini     s->mac_hashh = 0;
47349ab747fSPaolo Bonzini     s->mac_hashl = 0;
47449ab747fSPaolo Bonzini     s->mac_mii_acc = 0;
47549ab747fSPaolo Bonzini     s->mac_mii_data = 0;
47649ab747fSPaolo Bonzini     s->mac_flow = 0;
47749ab747fSPaolo Bonzini 
47849ab747fSPaolo Bonzini     s->read_word_n = 0;
47949ab747fSPaolo Bonzini     s->write_word_n = 0;
48049ab747fSPaolo Bonzini 
48149ab747fSPaolo Bonzini     phy_reset(s);
48249ab747fSPaolo Bonzini 
48349ab747fSPaolo Bonzini     s->eeprom_writable = 0;
48449ab747fSPaolo Bonzini     lan9118_reload_eeprom(s);
48549ab747fSPaolo Bonzini }
48649ab747fSPaolo Bonzini 
rx_fifo_push(lan9118_state * s,uint32_t val)48749ab747fSPaolo Bonzini static void rx_fifo_push(lan9118_state *s, uint32_t val)
48849ab747fSPaolo Bonzini {
48949ab747fSPaolo Bonzini     int fifo_pos;
49049ab747fSPaolo Bonzini     fifo_pos = s->rx_fifo_head + s->rx_fifo_used;
49149ab747fSPaolo Bonzini     if (fifo_pos >= s->rx_fifo_size)
49249ab747fSPaolo Bonzini       fifo_pos -= s->rx_fifo_size;
49349ab747fSPaolo Bonzini     s->rx_fifo[fifo_pos] = val;
49449ab747fSPaolo Bonzini     s->rx_fifo_used++;
49549ab747fSPaolo Bonzini }
49649ab747fSPaolo Bonzini 
49749ab747fSPaolo Bonzini /* Return nonzero if the packet is accepted by the filter.  */
lan9118_filter(lan9118_state * s,const uint8_t * addr)49849ab747fSPaolo Bonzini static int lan9118_filter(lan9118_state *s, const uint8_t *addr)
49949ab747fSPaolo Bonzini {
50049ab747fSPaolo Bonzini     int multicast;
50149ab747fSPaolo Bonzini     uint32_t hash;
50249ab747fSPaolo Bonzini 
50349ab747fSPaolo Bonzini     if (s->mac_cr & MAC_CR_PRMS) {
50449ab747fSPaolo Bonzini         return 1;
50549ab747fSPaolo Bonzini     }
50649ab747fSPaolo Bonzini     if (addr[0] == 0xff && addr[1] == 0xff && addr[2] == 0xff &&
50749ab747fSPaolo Bonzini         addr[3] == 0xff && addr[4] == 0xff && addr[5] == 0xff) {
50849ab747fSPaolo Bonzini         return (s->mac_cr & MAC_CR_BCAST) == 0;
50949ab747fSPaolo Bonzini     }
51049ab747fSPaolo Bonzini 
51149ab747fSPaolo Bonzini     multicast = addr[0] & 1;
51249ab747fSPaolo Bonzini     if (multicast &&s->mac_cr & MAC_CR_MCPAS) {
51349ab747fSPaolo Bonzini         return 1;
51449ab747fSPaolo Bonzini     }
51549ab747fSPaolo Bonzini     if (multicast ? (s->mac_cr & MAC_CR_HPFILT) == 0
51649ab747fSPaolo Bonzini                   : (s->mac_cr & MAC_CR_HO) == 0) {
51749ab747fSPaolo Bonzini         /* Exact matching.  */
51849ab747fSPaolo Bonzini         hash = memcmp(addr, s->conf.macaddr.a, 6);
51949ab747fSPaolo Bonzini         if (s->mac_cr & MAC_CR_INVFILT) {
52049ab747fSPaolo Bonzini             return hash != 0;
52149ab747fSPaolo Bonzini         } else {
52249ab747fSPaolo Bonzini             return hash == 0;
52349ab747fSPaolo Bonzini         }
52449ab747fSPaolo Bonzini     } else {
52549ab747fSPaolo Bonzini         /* Hash matching  */
526eedeaee7SMark Cave-Ayland         hash = net_crc32(addr, ETH_ALEN) >> 26;
52749ab747fSPaolo Bonzini         if (hash & 0x20) {
52849ab747fSPaolo Bonzini             return (s->mac_hashh >> (hash & 0x1f)) & 1;
52949ab747fSPaolo Bonzini         } else {
53049ab747fSPaolo Bonzini             return (s->mac_hashl >> (hash & 0x1f)) & 1;
53149ab747fSPaolo Bonzini         }
53249ab747fSPaolo Bonzini     }
53349ab747fSPaolo Bonzini }
53449ab747fSPaolo Bonzini 
lan9118_receive(NetClientState * nc,const uint8_t * buf,size_t size)53549ab747fSPaolo Bonzini static ssize_t lan9118_receive(NetClientState *nc, const uint8_t *buf,
53649ab747fSPaolo Bonzini                                size_t size)
53749ab747fSPaolo Bonzini {
53849ab747fSPaolo Bonzini     lan9118_state *s = qemu_get_nic_opaque(nc);
53949ab747fSPaolo Bonzini     int fifo_len;
54049ab747fSPaolo Bonzini     int offset;
54149ab747fSPaolo Bonzini     int src_pos;
54249ab747fSPaolo Bonzini     int n;
54349ab747fSPaolo Bonzini     int filter;
54449ab747fSPaolo Bonzini     uint32_t val;
54549ab747fSPaolo Bonzini     uint32_t crc;
54649ab747fSPaolo Bonzini     uint32_t status;
54749ab747fSPaolo Bonzini 
54849ab747fSPaolo Bonzini     if ((s->mac_cr & MAC_CR_RXEN) == 0) {
54949ab747fSPaolo Bonzini         return -1;
55049ab747fSPaolo Bonzini     }
55149ab747fSPaolo Bonzini 
552a4522346SPhilippe Mathieu-Daudé     if (size >= MIL_TXFIFO_SIZE || size < 14) {
55349ab747fSPaolo Bonzini         return -1;
55449ab747fSPaolo Bonzini     }
55549ab747fSPaolo Bonzini 
55649ab747fSPaolo Bonzini     /* TODO: Implement FIFO overflow notification.  */
55749ab747fSPaolo Bonzini     if (s->rx_status_fifo_used == s->rx_status_fifo_size) {
55849ab747fSPaolo Bonzini         return -1;
55949ab747fSPaolo Bonzini     }
56049ab747fSPaolo Bonzini 
56149ab747fSPaolo Bonzini     filter = lan9118_filter(s, buf);
56249ab747fSPaolo Bonzini     if (!filter && (s->mac_cr & MAC_CR_RXALL) == 0) {
56349ab747fSPaolo Bonzini         return size;
56449ab747fSPaolo Bonzini     }
56549ab747fSPaolo Bonzini 
56649ab747fSPaolo Bonzini     offset = (s->rx_cfg >> 8) & 0x1f;
56749ab747fSPaolo Bonzini     n = offset & 3;
56849ab747fSPaolo Bonzini     fifo_len = (size + n + 3) >> 2;
56949ab747fSPaolo Bonzini     /* Add a word for the CRC.  */
57049ab747fSPaolo Bonzini     fifo_len++;
57149ab747fSPaolo Bonzini     if (s->rx_fifo_size - s->rx_fifo_used < fifo_len) {
57249ab747fSPaolo Bonzini         return -1;
57349ab747fSPaolo Bonzini     }
57449ab747fSPaolo Bonzini 
57549ab747fSPaolo Bonzini     DPRINTF("Got packet len:%d fifo:%d filter:%s\n",
57649ab747fSPaolo Bonzini             (int)size, fifo_len, filter ? "pass" : "fail");
57749ab747fSPaolo Bonzini     val = 0;
57849ab747fSPaolo Bonzini     crc = bswap32(crc32(~0, buf, size));
57949ab747fSPaolo Bonzini     for (src_pos = 0; src_pos < size; src_pos++) {
58049ab747fSPaolo Bonzini         val = (val >> 8) | ((uint32_t)buf[src_pos] << 24);
58149ab747fSPaolo Bonzini         n++;
58249ab747fSPaolo Bonzini         if (n == 4) {
58349ab747fSPaolo Bonzini             n = 0;
58449ab747fSPaolo Bonzini             rx_fifo_push(s, val);
58549ab747fSPaolo Bonzini             val = 0;
58649ab747fSPaolo Bonzini         }
58749ab747fSPaolo Bonzini     }
58849ab747fSPaolo Bonzini     if (n) {
58949ab747fSPaolo Bonzini         val >>= ((4 - n) * 8);
59049ab747fSPaolo Bonzini         val |= crc << (n * 8);
59149ab747fSPaolo Bonzini         rx_fifo_push(s, val);
59249ab747fSPaolo Bonzini         val = crc >> ((4 - n) * 8);
59349ab747fSPaolo Bonzini         rx_fifo_push(s, val);
59449ab747fSPaolo Bonzini     } else {
59549ab747fSPaolo Bonzini         rx_fifo_push(s, crc);
59649ab747fSPaolo Bonzini     }
59749ab747fSPaolo Bonzini     n = s->rx_status_fifo_head + s->rx_status_fifo_used;
59849ab747fSPaolo Bonzini     if (n >= s->rx_status_fifo_size) {
59949ab747fSPaolo Bonzini         n -= s->rx_status_fifo_size;
60049ab747fSPaolo Bonzini     }
60149ab747fSPaolo Bonzini     s->rx_packet_size[s->rx_packet_size_tail] = fifo_len;
60249ab747fSPaolo Bonzini     s->rx_packet_size_tail = (s->rx_packet_size_tail + 1023) & 1023;
60349ab747fSPaolo Bonzini     s->rx_status_fifo_used++;
60449ab747fSPaolo Bonzini 
60549ab747fSPaolo Bonzini     status = (size + 4) << 16;
60649ab747fSPaolo Bonzini     if (buf[0] == 0xff && buf[1] == 0xff && buf[2] == 0xff &&
60749ab747fSPaolo Bonzini         buf[3] == 0xff && buf[4] == 0xff && buf[5] == 0xff) {
60849ab747fSPaolo Bonzini         status |= 0x00002000;
60949ab747fSPaolo Bonzini     } else if (buf[0] & 1) {
61049ab747fSPaolo Bonzini         status |= 0x00000400;
61149ab747fSPaolo Bonzini     }
61249ab747fSPaolo Bonzini     if (!filter) {
61349ab747fSPaolo Bonzini         status |= 0x40000000;
61449ab747fSPaolo Bonzini     }
61549ab747fSPaolo Bonzini     s->rx_status_fifo[n] = status;
61649ab747fSPaolo Bonzini 
61749ab747fSPaolo Bonzini     if (s->rx_status_fifo_used > (s->fifo_int & 0xff)) {
61849ab747fSPaolo Bonzini         s->int_sts |= RSFL_INT;
61949ab747fSPaolo Bonzini     }
62049ab747fSPaolo Bonzini     lan9118_update(s);
62149ab747fSPaolo Bonzini 
62249ab747fSPaolo Bonzini     return size;
62349ab747fSPaolo Bonzini }
62449ab747fSPaolo Bonzini 
rx_fifo_pop(lan9118_state * s)62549ab747fSPaolo Bonzini static uint32_t rx_fifo_pop(lan9118_state *s)
62649ab747fSPaolo Bonzini {
62749ab747fSPaolo Bonzini     int n;
62849ab747fSPaolo Bonzini     uint32_t val;
62949ab747fSPaolo Bonzini 
63049ab747fSPaolo Bonzini     if (s->rxp_size == 0 && s->rxp_pad == 0) {
63149ab747fSPaolo Bonzini         s->rxp_size = s->rx_packet_size[s->rx_packet_size_head];
63249ab747fSPaolo Bonzini         s->rx_packet_size[s->rx_packet_size_head] = 0;
63349ab747fSPaolo Bonzini         if (s->rxp_size != 0) {
63449ab747fSPaolo Bonzini             s->rx_packet_size_head = (s->rx_packet_size_head + 1023) & 1023;
63549ab747fSPaolo Bonzini             s->rxp_offset = (s->rx_cfg >> 10) & 7;
63649ab747fSPaolo Bonzini             n = s->rxp_offset + s->rxp_size;
63749ab747fSPaolo Bonzini             switch (s->rx_cfg >> 30) {
63849ab747fSPaolo Bonzini             case 1:
63949ab747fSPaolo Bonzini                 n = (-n) & 3;
64049ab747fSPaolo Bonzini                 break;
64149ab747fSPaolo Bonzini             case 2:
64249ab747fSPaolo Bonzini                 n = (-n) & 7;
64349ab747fSPaolo Bonzini                 break;
64449ab747fSPaolo Bonzini             default:
64549ab747fSPaolo Bonzini                 n = 0;
64649ab747fSPaolo Bonzini                 break;
64749ab747fSPaolo Bonzini             }
64849ab747fSPaolo Bonzini             s->rxp_pad = n;
64949ab747fSPaolo Bonzini             DPRINTF("Pop packet size:%d offset:%d pad: %d\n",
65049ab747fSPaolo Bonzini                     s->rxp_size, s->rxp_offset, s->rxp_pad);
65149ab747fSPaolo Bonzini         }
65249ab747fSPaolo Bonzini     }
65349ab747fSPaolo Bonzini     if (s->rxp_offset > 0) {
65449ab747fSPaolo Bonzini         s->rxp_offset--;
65549ab747fSPaolo Bonzini         val = 0;
65649ab747fSPaolo Bonzini     } else if (s->rxp_size > 0) {
65749ab747fSPaolo Bonzini         s->rxp_size--;
65849ab747fSPaolo Bonzini         val = s->rx_fifo[s->rx_fifo_head++];
65949ab747fSPaolo Bonzini         if (s->rx_fifo_head >= s->rx_fifo_size) {
66049ab747fSPaolo Bonzini             s->rx_fifo_head -= s->rx_fifo_size;
66149ab747fSPaolo Bonzini         }
66249ab747fSPaolo Bonzini         s->rx_fifo_used--;
66349ab747fSPaolo Bonzini     } else if (s->rxp_pad > 0) {
66449ab747fSPaolo Bonzini         s->rxp_pad--;
66549ab747fSPaolo Bonzini         val =  0;
66649ab747fSPaolo Bonzini     } else {
66749ab747fSPaolo Bonzini         DPRINTF("RX underflow\n");
66849ab747fSPaolo Bonzini         s->int_sts |= RXE_INT;
66949ab747fSPaolo Bonzini         val =  0;
67049ab747fSPaolo Bonzini     }
67149ab747fSPaolo Bonzini     lan9118_update(s);
67249ab747fSPaolo Bonzini     return val;
67349ab747fSPaolo Bonzini }
67449ab747fSPaolo Bonzini 
do_tx_packet(lan9118_state * s)67549ab747fSPaolo Bonzini static void do_tx_packet(lan9118_state *s)
67649ab747fSPaolo Bonzini {
67749ab747fSPaolo Bonzini     int n;
67849ab747fSPaolo Bonzini     uint32_t status;
67949ab747fSPaolo Bonzini 
68049ab747fSPaolo Bonzini     /* FIXME: Honor TX disable, and allow queueing of packets.  */
68149ab747fSPaolo Bonzini     if (s->phy_control & 0x4000)  {
68249ab747fSPaolo Bonzini         /* This assumes the receive routine doesn't touch the VLANClient.  */
68337cee017SAlexander Bulekov         qemu_receive_packet(qemu_get_queue(s->nic), s->txp->data, s->txp->len);
68449ab747fSPaolo Bonzini     } else {
68549ab747fSPaolo Bonzini         qemu_send_packet(qemu_get_queue(s->nic), s->txp->data, s->txp->len);
68649ab747fSPaolo Bonzini     }
68749ab747fSPaolo Bonzini     s->txp->fifo_used = 0;
68849ab747fSPaolo Bonzini 
68949ab747fSPaolo Bonzini     if (s->tx_status_fifo_used == 512) {
69049ab747fSPaolo Bonzini         /* Status FIFO full */
69149ab747fSPaolo Bonzini         return;
69249ab747fSPaolo Bonzini     }
69349ab747fSPaolo Bonzini     /* Add entry to status FIFO.  */
69449ab747fSPaolo Bonzini     status = s->txp->cmd_b & 0xffff0000u;
69549ab747fSPaolo Bonzini     DPRINTF("Sent packet tag:%04x len %d\n", status >> 16, s->txp->len);
69649ab747fSPaolo Bonzini     n = (s->tx_status_fifo_head + s->tx_status_fifo_used) & 511;
69749ab747fSPaolo Bonzini     s->tx_status_fifo[n] = status;
69849ab747fSPaolo Bonzini     s->tx_status_fifo_used++;
699895a803cSLucas Dietrich 
700895a803cSLucas Dietrich     /*
701895a803cSLucas Dietrich      * Generate TSFL interrupt if TX FIFO level exceeds the level
702895a803cSLucas Dietrich      * specified in the FIFO_INT TX Status Level field.
703895a803cSLucas Dietrich      */
704895a803cSLucas Dietrich     if (s->tx_status_fifo_used > ((s->fifo_int >> 16) & 0xff)) {
705895a803cSLucas Dietrich         s->int_sts |= TSFL_INT;
706895a803cSLucas Dietrich     }
70749ab747fSPaolo Bonzini     if (s->tx_status_fifo_used == 512) {
70849ab747fSPaolo Bonzini         s->int_sts |= TSFF_INT;
70949ab747fSPaolo Bonzini         /* TODO: Stop transmission.  */
71049ab747fSPaolo Bonzini     }
71149ab747fSPaolo Bonzini }
71249ab747fSPaolo Bonzini 
rx_status_fifo_pop(lan9118_state * s)71349ab747fSPaolo Bonzini static uint32_t rx_status_fifo_pop(lan9118_state *s)
71449ab747fSPaolo Bonzini {
71549ab747fSPaolo Bonzini     uint32_t val;
71649ab747fSPaolo Bonzini 
71749ab747fSPaolo Bonzini     val = s->rx_status_fifo[s->rx_status_fifo_head];
71849ab747fSPaolo Bonzini     if (s->rx_status_fifo_used != 0) {
71949ab747fSPaolo Bonzini         s->rx_status_fifo_used--;
72049ab747fSPaolo Bonzini         s->rx_status_fifo_head++;
72149ab747fSPaolo Bonzini         if (s->rx_status_fifo_head >= s->rx_status_fifo_size) {
72249ab747fSPaolo Bonzini             s->rx_status_fifo_head -= s->rx_status_fifo_size;
72349ab747fSPaolo Bonzini         }
72449ab747fSPaolo Bonzini         /* ??? What value should be returned when the FIFO is empty?  */
72549ab747fSPaolo Bonzini         DPRINTF("RX status pop 0x%08x\n", val);
72649ab747fSPaolo Bonzini     }
72749ab747fSPaolo Bonzini     return val;
72849ab747fSPaolo Bonzini }
72949ab747fSPaolo Bonzini 
tx_status_fifo_pop(lan9118_state * s)73049ab747fSPaolo Bonzini static uint32_t tx_status_fifo_pop(lan9118_state *s)
73149ab747fSPaolo Bonzini {
73249ab747fSPaolo Bonzini     uint32_t val;
73349ab747fSPaolo Bonzini 
73449ab747fSPaolo Bonzini     val = s->tx_status_fifo[s->tx_status_fifo_head];
73549ab747fSPaolo Bonzini     if (s->tx_status_fifo_used != 0) {
73649ab747fSPaolo Bonzini         s->tx_status_fifo_used--;
73749ab747fSPaolo Bonzini         s->tx_status_fifo_head = (s->tx_status_fifo_head + 1) & 511;
73849ab747fSPaolo Bonzini         /* ??? What value should be returned when the FIFO is empty?  */
73949ab747fSPaolo Bonzini     }
74049ab747fSPaolo Bonzini     return val;
74149ab747fSPaolo Bonzini }
74249ab747fSPaolo Bonzini 
tx_fifo_push(lan9118_state * s,uint32_t val)74349ab747fSPaolo Bonzini static void tx_fifo_push(lan9118_state *s, uint32_t val)
74449ab747fSPaolo Bonzini {
74549ab747fSPaolo Bonzini     int n;
74649ab747fSPaolo Bonzini 
74749ab747fSPaolo Bonzini     if (s->txp->fifo_used == s->tx_fifo_size) {
74849ab747fSPaolo Bonzini         s->int_sts |= TDFO_INT;
74949ab747fSPaolo Bonzini         return;
75049ab747fSPaolo Bonzini     }
75149ab747fSPaolo Bonzini     switch (s->txp->state) {
75249ab747fSPaolo Bonzini     case TX_IDLE:
75349ab747fSPaolo Bonzini         s->txp->cmd_a = val & 0x831f37ff;
75449ab747fSPaolo Bonzini         s->txp->fifo_used++;
75549ab747fSPaolo Bonzini         s->txp->state = TX_B;
7562ad657e3SRoy Franz         s->txp->buffer_size = extract32(s->txp->cmd_a, 0, 11);
7572ad657e3SRoy Franz         s->txp->offset = extract32(s->txp->cmd_a, 16, 5);
75849ab747fSPaolo Bonzini         break;
75949ab747fSPaolo Bonzini     case TX_B:
76049ab747fSPaolo Bonzini         if (s->txp->cmd_a & 0x2000) {
76149ab747fSPaolo Bonzini             /* First segment */
76249ab747fSPaolo Bonzini             s->txp->cmd_b = val;
76349ab747fSPaolo Bonzini             s->txp->fifo_used++;
76449ab747fSPaolo Bonzini             /* End alignment does not include command words.  */
76549ab747fSPaolo Bonzini             n = (s->txp->buffer_size + s->txp->offset + 3) >> 2;
76649ab747fSPaolo Bonzini             switch ((n >> 24) & 3) {
76749ab747fSPaolo Bonzini             case 1:
76849ab747fSPaolo Bonzini                 n = (-n) & 3;
76949ab747fSPaolo Bonzini                 break;
77049ab747fSPaolo Bonzini             case 2:
77149ab747fSPaolo Bonzini                 n = (-n) & 7;
77249ab747fSPaolo Bonzini                 break;
77349ab747fSPaolo Bonzini             default:
77449ab747fSPaolo Bonzini                 n = 0;
77549ab747fSPaolo Bonzini             }
77649ab747fSPaolo Bonzini             s->txp->pad = n;
77749ab747fSPaolo Bonzini             s->txp->len = 0;
77849ab747fSPaolo Bonzini         }
77949ab747fSPaolo Bonzini         DPRINTF("Block len:%d offset:%d pad:%d cmd %08x\n",
78049ab747fSPaolo Bonzini                 s->txp->buffer_size, s->txp->offset, s->txp->pad,
78149ab747fSPaolo Bonzini                 s->txp->cmd_a);
78249ab747fSPaolo Bonzini         s->txp->state = TX_DATA;
78349ab747fSPaolo Bonzini         break;
78449ab747fSPaolo Bonzini     case TX_DATA:
78549ab747fSPaolo Bonzini         if (s->txp->offset >= 4) {
78649ab747fSPaolo Bonzini             s->txp->offset -= 4;
78749ab747fSPaolo Bonzini             break;
78849ab747fSPaolo Bonzini         }
78949ab747fSPaolo Bonzini         if (s->txp->buffer_size <= 0 && s->txp->pad != 0) {
79049ab747fSPaolo Bonzini             s->txp->pad--;
79149ab747fSPaolo Bonzini         } else {
792c444dfabSRoy Franz             n = MIN(4, s->txp->buffer_size + s->txp->offset);
79349ab747fSPaolo Bonzini             while (s->txp->offset) {
79449ab747fSPaolo Bonzini                 val >>= 8;
79549ab747fSPaolo Bonzini                 n--;
79649ab747fSPaolo Bonzini                 s->txp->offset--;
79749ab747fSPaolo Bonzini             }
79849ab747fSPaolo Bonzini             /* Documentation is somewhat unclear on the ordering of bytes
79949ab747fSPaolo Bonzini                in FIFO words.  Empirical results show it to be little-endian.
80049ab747fSPaolo Bonzini                */
80149ab747fSPaolo Bonzini             while (n--) {
802ad766d60SPhilippe Mathieu-Daudé                 if (s->txp->len == MIL_TXFIFO_SIZE) {
803ad766d60SPhilippe Mathieu-Daudé                     /*
804ad766d60SPhilippe Mathieu-Daudé                      * No more space in the FIFO. The datasheet is not
805ad766d60SPhilippe Mathieu-Daudé                      * precise about this case. We choose what is easiest
806ad766d60SPhilippe Mathieu-Daudé                      * to model: the packet is truncated, and TXE is raised.
807ad766d60SPhilippe Mathieu-Daudé                      *
808ad766d60SPhilippe Mathieu-Daudé                      * Note, it could be a fragmented packet, but we currently
809ad766d60SPhilippe Mathieu-Daudé                      * do not handle that (see earlier TX_B case).
810ad766d60SPhilippe Mathieu-Daudé                      */
811ad766d60SPhilippe Mathieu-Daudé                     qemu_log_mask(LOG_GUEST_ERROR,
812ad766d60SPhilippe Mathieu-Daudé                                   "MIL TX FIFO overrun, discarding %u byte%s\n",
813ad766d60SPhilippe Mathieu-Daudé                                   n, n > 1 ? "s" : "");
814ad766d60SPhilippe Mathieu-Daudé                     s->int_sts |= TXE_INT;
815ad766d60SPhilippe Mathieu-Daudé                     break;
816ad766d60SPhilippe Mathieu-Daudé                 }
81749ab747fSPaolo Bonzini                 s->txp->data[s->txp->len] = val & 0xff;
81849ab747fSPaolo Bonzini                 s->txp->len++;
81949ab747fSPaolo Bonzini                 val >>= 8;
82049ab747fSPaolo Bonzini                 s->txp->buffer_size--;
82149ab747fSPaolo Bonzini             }
82249ab747fSPaolo Bonzini             s->txp->fifo_used++;
82349ab747fSPaolo Bonzini         }
82449ab747fSPaolo Bonzini         if (s->txp->buffer_size <= 0 && s->txp->pad == 0) {
82549ab747fSPaolo Bonzini             if (s->txp->cmd_a & 0x1000) {
82649ab747fSPaolo Bonzini                 do_tx_packet(s);
82749ab747fSPaolo Bonzini             }
82849ab747fSPaolo Bonzini             if (s->txp->cmd_a & 0x80000000) {
82949ab747fSPaolo Bonzini                 s->int_sts |= TX_IOC_INT;
83049ab747fSPaolo Bonzini             }
83149ab747fSPaolo Bonzini             s->txp->state = TX_IDLE;
83249ab747fSPaolo Bonzini         }
83349ab747fSPaolo Bonzini         break;
83449ab747fSPaolo Bonzini     }
83549ab747fSPaolo Bonzini }
83649ab747fSPaolo Bonzini 
do_phy_read(lan9118_state * s,int reg)83749ab747fSPaolo Bonzini static uint32_t do_phy_read(lan9118_state *s, int reg)
83849ab747fSPaolo Bonzini {
83949ab747fSPaolo Bonzini     uint32_t val;
84049ab747fSPaolo Bonzini 
84149ab747fSPaolo Bonzini     switch (reg) {
84249ab747fSPaolo Bonzini     case 0: /* Basic Control */
84349ab747fSPaolo Bonzini         return s->phy_control;
84449ab747fSPaolo Bonzini     case 1: /* Basic Status */
84549ab747fSPaolo Bonzini         return s->phy_status;
84649ab747fSPaolo Bonzini     case 2: /* ID1 */
84749ab747fSPaolo Bonzini         return 0x0007;
84849ab747fSPaolo Bonzini     case 3: /* ID2 */
84949ab747fSPaolo Bonzini         return 0xc0d1;
85049ab747fSPaolo Bonzini     case 4: /* Auto-neg advertisement */
85149ab747fSPaolo Bonzini         return s->phy_advertise;
85249ab747fSPaolo Bonzini     case 5: /* Auto-neg Link Partner Ability */
85349ab747fSPaolo Bonzini         return 0x0f71;
85449ab747fSPaolo Bonzini     case 6: /* Auto-neg Expansion */
85549ab747fSPaolo Bonzini         return 1;
85649ab747fSPaolo Bonzini         /* TODO 17, 18, 27, 29, 30, 31 */
85749ab747fSPaolo Bonzini     case 29: /* Interrupt source.  */
85849ab747fSPaolo Bonzini         val = s->phy_int;
85949ab747fSPaolo Bonzini         s->phy_int = 0;
86049ab747fSPaolo Bonzini         phy_update_irq(s);
86149ab747fSPaolo Bonzini         return val;
86249ab747fSPaolo Bonzini     case 30: /* Interrupt mask */
86349ab747fSPaolo Bonzini         return s->phy_int_mask;
86449ab747fSPaolo Bonzini     default:
86544c94cdbSQiang Liu         qemu_log_mask(LOG_GUEST_ERROR,
86644c94cdbSQiang Liu                       "do_phy_read: PHY read reg %d\n", reg);
86749ab747fSPaolo Bonzini         return 0;
86849ab747fSPaolo Bonzini     }
86949ab747fSPaolo Bonzini }
87049ab747fSPaolo Bonzini 
do_phy_write(lan9118_state * s,int reg,uint32_t val)87149ab747fSPaolo Bonzini static void do_phy_write(lan9118_state *s, int reg, uint32_t val)
87249ab747fSPaolo Bonzini {
87349ab747fSPaolo Bonzini     switch (reg) {
87449ab747fSPaolo Bonzini     case 0: /* Basic Control */
87549ab747fSPaolo Bonzini         if (val & 0x8000) {
87649ab747fSPaolo Bonzini             phy_reset(s);
87749ab747fSPaolo Bonzini             break;
87849ab747fSPaolo Bonzini         }
87949ab747fSPaolo Bonzini         s->phy_control = val & 0x7980;
88049ab747fSPaolo Bonzini         /* Complete autonegotiation immediately.  */
88149ab747fSPaolo Bonzini         if (val & 0x1000) {
88249ab747fSPaolo Bonzini             s->phy_status |= 0x0020;
88349ab747fSPaolo Bonzini         }
88449ab747fSPaolo Bonzini         break;
88549ab747fSPaolo Bonzini     case 4: /* Auto-neg advertisement */
88649ab747fSPaolo Bonzini         s->phy_advertise = (val & 0x2d7f) | 0x80;
88749ab747fSPaolo Bonzini         break;
88849ab747fSPaolo Bonzini         /* TODO 17, 18, 27, 31 */
88949ab747fSPaolo Bonzini     case 30: /* Interrupt mask */
89049ab747fSPaolo Bonzini         s->phy_int_mask = val & 0xff;
89149ab747fSPaolo Bonzini         phy_update_irq(s);
89249ab747fSPaolo Bonzini         break;
89349ab747fSPaolo Bonzini     default:
89444c94cdbSQiang Liu         qemu_log_mask(LOG_GUEST_ERROR,
89544c94cdbSQiang Liu                       "do_phy_write: PHY write reg %d = 0x%04x\n", reg, val);
89649ab747fSPaolo Bonzini     }
89749ab747fSPaolo Bonzini }
89849ab747fSPaolo Bonzini 
do_mac_write(lan9118_state * s,int reg,uint32_t val)89949ab747fSPaolo Bonzini static void do_mac_write(lan9118_state *s, int reg, uint32_t val)
90049ab747fSPaolo Bonzini {
90149ab747fSPaolo Bonzini     switch (reg) {
90249ab747fSPaolo Bonzini     case MAC_CR:
90349ab747fSPaolo Bonzini         if ((s->mac_cr & MAC_CR_RXEN) != 0 && (val & MAC_CR_RXEN) == 0) {
90449ab747fSPaolo Bonzini             s->int_sts |= RXSTOP_INT;
90549ab747fSPaolo Bonzini         }
90649ab747fSPaolo Bonzini         s->mac_cr = val & ~MAC_CR_RESERVED;
90749ab747fSPaolo Bonzini         DPRINTF("MAC_CR: %08x\n", val);
90849ab747fSPaolo Bonzini         break;
90949ab747fSPaolo Bonzini     case MAC_ADDRH:
91049ab747fSPaolo Bonzini         s->conf.macaddr.a[4] = val & 0xff;
91149ab747fSPaolo Bonzini         s->conf.macaddr.a[5] = (val >> 8) & 0xff;
91249ab747fSPaolo Bonzini         lan9118_mac_changed(s);
91349ab747fSPaolo Bonzini         break;
91449ab747fSPaolo Bonzini     case MAC_ADDRL:
91549ab747fSPaolo Bonzini         s->conf.macaddr.a[0] = val & 0xff;
91649ab747fSPaolo Bonzini         s->conf.macaddr.a[1] = (val >> 8) & 0xff;
91749ab747fSPaolo Bonzini         s->conf.macaddr.a[2] = (val >> 16) & 0xff;
91849ab747fSPaolo Bonzini         s->conf.macaddr.a[3] = (val >> 24) & 0xff;
91949ab747fSPaolo Bonzini         lan9118_mac_changed(s);
92049ab747fSPaolo Bonzini         break;
92149ab747fSPaolo Bonzini     case MAC_HASHH:
92249ab747fSPaolo Bonzini         s->mac_hashh = val;
92349ab747fSPaolo Bonzini         break;
92449ab747fSPaolo Bonzini     case MAC_HASHL:
92549ab747fSPaolo Bonzini         s->mac_hashl = val;
92649ab747fSPaolo Bonzini         break;
92749ab747fSPaolo Bonzini     case MAC_MII_ACC:
92849ab747fSPaolo Bonzini         s->mac_mii_acc = val & 0xffc2;
92949ab747fSPaolo Bonzini         if (val & 2) {
93049ab747fSPaolo Bonzini             DPRINTF("PHY write %d = 0x%04x\n",
93149ab747fSPaolo Bonzini                     (val >> 6) & 0x1f, s->mac_mii_data);
93249ab747fSPaolo Bonzini             do_phy_write(s, (val >> 6) & 0x1f, s->mac_mii_data);
93349ab747fSPaolo Bonzini         } else {
93449ab747fSPaolo Bonzini             s->mac_mii_data = do_phy_read(s, (val >> 6) & 0x1f);
93549ab747fSPaolo Bonzini             DPRINTF("PHY read %d = 0x%04x\n",
93649ab747fSPaolo Bonzini                     (val >> 6) & 0x1f, s->mac_mii_data);
93749ab747fSPaolo Bonzini         }
93849ab747fSPaolo Bonzini         break;
93949ab747fSPaolo Bonzini     case MAC_MII_DATA:
94049ab747fSPaolo Bonzini         s->mac_mii_data = val & 0xffff;
94149ab747fSPaolo Bonzini         break;
94249ab747fSPaolo Bonzini     case MAC_FLOW:
94349ab747fSPaolo Bonzini         s->mac_flow = val & 0xffff0000;
94449ab747fSPaolo Bonzini         break;
94549ab747fSPaolo Bonzini     case MAC_VLAN1:
94649ab747fSPaolo Bonzini         /* Writing to this register changes a condition for
94749ab747fSPaolo Bonzini          * FrameTooLong bit in rx_status.  Since we do not set
94849ab747fSPaolo Bonzini          * FrameTooLong anyway, just ignore write to this.
94949ab747fSPaolo Bonzini          */
95049ab747fSPaolo Bonzini         break;
95149ab747fSPaolo Bonzini     default:
95252b4bb73SAndrew Baumann         qemu_log_mask(LOG_GUEST_ERROR,
95352b4bb73SAndrew Baumann                       "lan9118: Unimplemented MAC register write: %d = 0x%x\n",
95449ab747fSPaolo Bonzini                  s->mac_cmd & 0xf, val);
95549ab747fSPaolo Bonzini     }
95649ab747fSPaolo Bonzini }
95749ab747fSPaolo Bonzini 
do_mac_read(lan9118_state * s,int reg)95849ab747fSPaolo Bonzini static uint32_t do_mac_read(lan9118_state *s, int reg)
95949ab747fSPaolo Bonzini {
96049ab747fSPaolo Bonzini     switch (reg) {
96149ab747fSPaolo Bonzini     case MAC_CR:
96249ab747fSPaolo Bonzini         return s->mac_cr;
96349ab747fSPaolo Bonzini     case MAC_ADDRH:
96449ab747fSPaolo Bonzini         return s->conf.macaddr.a[4] | (s->conf.macaddr.a[5] << 8);
96549ab747fSPaolo Bonzini     case MAC_ADDRL:
96649ab747fSPaolo Bonzini         return s->conf.macaddr.a[0] | (s->conf.macaddr.a[1] << 8)
96749ab747fSPaolo Bonzini                | (s->conf.macaddr.a[2] << 16) | (s->conf.macaddr.a[3] << 24);
96849ab747fSPaolo Bonzini     case MAC_HASHH:
96949ab747fSPaolo Bonzini         return s->mac_hashh;
97049ab747fSPaolo Bonzini     case MAC_HASHL:
97149ab747fSPaolo Bonzini         return s->mac_hashl;
97249ab747fSPaolo Bonzini     case MAC_MII_ACC:
97349ab747fSPaolo Bonzini         return s->mac_mii_acc;
97449ab747fSPaolo Bonzini     case MAC_MII_DATA:
97549ab747fSPaolo Bonzini         return s->mac_mii_data;
97649ab747fSPaolo Bonzini     case MAC_FLOW:
97749ab747fSPaolo Bonzini         return s->mac_flow;
97849ab747fSPaolo Bonzini     default:
97952b4bb73SAndrew Baumann         qemu_log_mask(LOG_GUEST_ERROR,
98052b4bb73SAndrew Baumann                       "lan9118: Unimplemented MAC register read: %d\n",
98149ab747fSPaolo Bonzini                  s->mac_cmd & 0xf);
98252b4bb73SAndrew Baumann         return 0;
98349ab747fSPaolo Bonzini     }
98449ab747fSPaolo Bonzini }
98549ab747fSPaolo Bonzini 
lan9118_eeprom_cmd(lan9118_state * s,int cmd,int addr)98649ab747fSPaolo Bonzini static void lan9118_eeprom_cmd(lan9118_state *s, int cmd, int addr)
98749ab747fSPaolo Bonzini {
98812fdd928SAndrew Baumann     s->e2p_cmd = (s->e2p_cmd & E2P_CMD_MAC_ADDR_LOADED) | (cmd << 28) | addr;
98949ab747fSPaolo Bonzini     switch (cmd) {
99049ab747fSPaolo Bonzini     case 0:
99149ab747fSPaolo Bonzini         s->e2p_data = s->eeprom[addr];
99249ab747fSPaolo Bonzini         DPRINTF("EEPROM Read %d = 0x%02x\n", addr, s->e2p_data);
99349ab747fSPaolo Bonzini         break;
99449ab747fSPaolo Bonzini     case 1:
99549ab747fSPaolo Bonzini         s->eeprom_writable = 0;
99649ab747fSPaolo Bonzini         DPRINTF("EEPROM Write Disable\n");
99749ab747fSPaolo Bonzini         break;
99849ab747fSPaolo Bonzini     case 2: /* EWEN */
99949ab747fSPaolo Bonzini         s->eeprom_writable = 1;
100049ab747fSPaolo Bonzini         DPRINTF("EEPROM Write Enable\n");
100149ab747fSPaolo Bonzini         break;
100249ab747fSPaolo Bonzini     case 3: /* WRITE */
100349ab747fSPaolo Bonzini         if (s->eeprom_writable) {
100449ab747fSPaolo Bonzini             s->eeprom[addr] &= s->e2p_data;
100549ab747fSPaolo Bonzini             DPRINTF("EEPROM Write %d = 0x%02x\n", addr, s->e2p_data);
100649ab747fSPaolo Bonzini         } else {
100749ab747fSPaolo Bonzini             DPRINTF("EEPROM Write %d (ignored)\n", addr);
100849ab747fSPaolo Bonzini         }
100949ab747fSPaolo Bonzini         break;
101049ab747fSPaolo Bonzini     case 4: /* WRAL */
101149ab747fSPaolo Bonzini         if (s->eeprom_writable) {
101249ab747fSPaolo Bonzini             for (addr = 0; addr < 128; addr++) {
101349ab747fSPaolo Bonzini                 s->eeprom[addr] &= s->e2p_data;
101449ab747fSPaolo Bonzini             }
101549ab747fSPaolo Bonzini             DPRINTF("EEPROM Write All 0x%02x\n", s->e2p_data);
101649ab747fSPaolo Bonzini         } else {
101749ab747fSPaolo Bonzini             DPRINTF("EEPROM Write All (ignored)\n");
101849ab747fSPaolo Bonzini         }
101949ab747fSPaolo Bonzini         break;
102049ab747fSPaolo Bonzini     case 5: /* ERASE */
102149ab747fSPaolo Bonzini         if (s->eeprom_writable) {
102249ab747fSPaolo Bonzini             s->eeprom[addr] = 0xff;
102349ab747fSPaolo Bonzini             DPRINTF("EEPROM Erase %d\n", addr);
102449ab747fSPaolo Bonzini         } else {
102549ab747fSPaolo Bonzini             DPRINTF("EEPROM Erase %d (ignored)\n", addr);
102649ab747fSPaolo Bonzini         }
102749ab747fSPaolo Bonzini         break;
102849ab747fSPaolo Bonzini     case 6: /* ERAL */
102949ab747fSPaolo Bonzini         if (s->eeprom_writable) {
103049ab747fSPaolo Bonzini             memset(s->eeprom, 0xff, 128);
103149ab747fSPaolo Bonzini             DPRINTF("EEPROM Erase All\n");
103249ab747fSPaolo Bonzini         } else {
103349ab747fSPaolo Bonzini             DPRINTF("EEPROM Erase All (ignored)\n");
103449ab747fSPaolo Bonzini         }
103549ab747fSPaolo Bonzini         break;
103649ab747fSPaolo Bonzini     case 7: /* RELOAD */
103749ab747fSPaolo Bonzini         lan9118_reload_eeprom(s);
103849ab747fSPaolo Bonzini         break;
103949ab747fSPaolo Bonzini     }
104049ab747fSPaolo Bonzini }
104149ab747fSPaolo Bonzini 
lan9118_tick(void * opaque)104249ab747fSPaolo Bonzini static void lan9118_tick(void *opaque)
104349ab747fSPaolo Bonzini {
104449ab747fSPaolo Bonzini     lan9118_state *s = (lan9118_state *)opaque;
104549ab747fSPaolo Bonzini     if (s->int_en & GPT_INT) {
104649ab747fSPaolo Bonzini         s->int_sts |= GPT_INT;
104749ab747fSPaolo Bonzini     }
104849ab747fSPaolo Bonzini     lan9118_update(s);
104949ab747fSPaolo Bonzini }
105049ab747fSPaolo Bonzini 
lan9118_writel(void * opaque,hwaddr offset,uint64_t val,unsigned size)105149ab747fSPaolo Bonzini static void lan9118_writel(void *opaque, hwaddr offset,
105249ab747fSPaolo Bonzini                            uint64_t val, unsigned size)
105349ab747fSPaolo Bonzini {
105449ab747fSPaolo Bonzini     lan9118_state *s = (lan9118_state *)opaque;
105549ab747fSPaolo Bonzini     offset &= 0xff;
105649ab747fSPaolo Bonzini 
105749ab747fSPaolo Bonzini     //DPRINTF("Write reg 0x%02x = 0x%08x\n", (int)offset, val);
10585cab6d5aSPeter Maydell     if (offset >= TX_DATA_FIFO_PORT_FIRST &&
10595cab6d5aSPeter Maydell         offset <= TX_DATA_FIFO_PORT_LAST) {
106049ab747fSPaolo Bonzini         /* TX FIFO */
106149ab747fSPaolo Bonzini         tx_fifo_push(s, val);
106249ab747fSPaolo Bonzini         return;
106349ab747fSPaolo Bonzini     }
106449ab747fSPaolo Bonzini     switch (offset) {
106549ab747fSPaolo Bonzini     case CSR_IRQ_CFG:
106649ab747fSPaolo Bonzini         /* TODO: Implement interrupt deassertion intervals.  */
106749ab747fSPaolo Bonzini         val &= (IRQ_EN | IRQ_POL | IRQ_TYPE);
106849ab747fSPaolo Bonzini         s->irq_cfg = (s->irq_cfg & IRQ_INT) | val;
106949ab747fSPaolo Bonzini         break;
107049ab747fSPaolo Bonzini     case CSR_INT_STS:
107149ab747fSPaolo Bonzini         s->int_sts &= ~val;
107249ab747fSPaolo Bonzini         break;
107349ab747fSPaolo Bonzini     case CSR_INT_EN:
107449ab747fSPaolo Bonzini         s->int_en = val & ~RESERVED_INT;
107549ab747fSPaolo Bonzini         s->int_sts |= val & SW_INT;
107649ab747fSPaolo Bonzini         break;
107749ab747fSPaolo Bonzini     case CSR_FIFO_INT:
107849ab747fSPaolo Bonzini         DPRINTF("FIFO INT levels %08x\n", val);
107949ab747fSPaolo Bonzini         s->fifo_int = val;
108049ab747fSPaolo Bonzini         break;
108149ab747fSPaolo Bonzini     case CSR_RX_CFG:
108249ab747fSPaolo Bonzini         if (val & 0x8000) {
108349ab747fSPaolo Bonzini             /* RX_DUMP */
108449ab747fSPaolo Bonzini             s->rx_fifo_used = 0;
108549ab747fSPaolo Bonzini             s->rx_status_fifo_used = 0;
108649ab747fSPaolo Bonzini             s->rx_packet_size_tail = s->rx_packet_size_head;
108749ab747fSPaolo Bonzini             s->rx_packet_size[s->rx_packet_size_head] = 0;
108849ab747fSPaolo Bonzini         }
108949ab747fSPaolo Bonzini         s->rx_cfg = val & 0xcfff1ff0;
109049ab747fSPaolo Bonzini         break;
109149ab747fSPaolo Bonzini     case CSR_TX_CFG:
109249ab747fSPaolo Bonzini         if (val & 0x8000) {
109349ab747fSPaolo Bonzini             s->tx_status_fifo_used = 0;
109449ab747fSPaolo Bonzini         }
109549ab747fSPaolo Bonzini         if (val & 0x4000) {
109649ab747fSPaolo Bonzini             s->txp->state = TX_IDLE;
109749ab747fSPaolo Bonzini             s->txp->fifo_used = 0;
109849ab747fSPaolo Bonzini             s->txp->cmd_a = 0xffffffff;
109949ab747fSPaolo Bonzini         }
110049ab747fSPaolo Bonzini         s->tx_cfg = val & 6;
110149ab747fSPaolo Bonzini         break;
110249ab747fSPaolo Bonzini     case CSR_HW_CFG:
110349ab747fSPaolo Bonzini         if (val & 1) {
110449ab747fSPaolo Bonzini             /* SRST */
11053ff66d10SAndreas Färber             lan9118_reset(DEVICE(s));
110649ab747fSPaolo Bonzini         } else {
110749ab747fSPaolo Bonzini             s->hw_cfg = (val & 0x003f300) | (s->hw_cfg & 0x4);
110849ab747fSPaolo Bonzini         }
110949ab747fSPaolo Bonzini         break;
111049ab747fSPaolo Bonzini     case CSR_RX_DP_CTRL:
111149ab747fSPaolo Bonzini         if (val & 0x80000000) {
111249ab747fSPaolo Bonzini             /* Skip forward to next packet.  */
111349ab747fSPaolo Bonzini             s->rxp_pad = 0;
111449ab747fSPaolo Bonzini             s->rxp_offset = 0;
111549ab747fSPaolo Bonzini             if (s->rxp_size == 0) {
111649ab747fSPaolo Bonzini                 /* Pop a word to start the next packet.  */
111749ab747fSPaolo Bonzini                 rx_fifo_pop(s);
111849ab747fSPaolo Bonzini                 s->rxp_pad = 0;
111949ab747fSPaolo Bonzini                 s->rxp_offset = 0;
112049ab747fSPaolo Bonzini             }
112149ab747fSPaolo Bonzini             s->rx_fifo_head += s->rxp_size;
112249ab747fSPaolo Bonzini             if (s->rx_fifo_head >= s->rx_fifo_size) {
112349ab747fSPaolo Bonzini                 s->rx_fifo_head -= s->rx_fifo_size;
112449ab747fSPaolo Bonzini             }
112549ab747fSPaolo Bonzini         }
112649ab747fSPaolo Bonzini         break;
112749ab747fSPaolo Bonzini     case CSR_PMT_CTRL:
112849ab747fSPaolo Bonzini         if (val & 0x400) {
112949ab747fSPaolo Bonzini             phy_reset(s);
113049ab747fSPaolo Bonzini         }
113149ab747fSPaolo Bonzini         s->pmt_ctrl &= ~0x34e;
113249ab747fSPaolo Bonzini         s->pmt_ctrl |= (val & 0x34e);
113349ab747fSPaolo Bonzini         break;
113449ab747fSPaolo Bonzini     case CSR_GPIO_CFG:
113549ab747fSPaolo Bonzini         /* Probably just enabling LEDs.  */
113649ab747fSPaolo Bonzini         s->gpio_cfg = val & 0x7777071f;
113749ab747fSPaolo Bonzini         break;
113849ab747fSPaolo Bonzini     case CSR_GPT_CFG:
113949ab747fSPaolo Bonzini         if ((s->gpt_cfg ^ val) & GPT_TIMER_EN) {
114088e4bd67SPeter Maydell             ptimer_transaction_begin(s->timer);
114149ab747fSPaolo Bonzini             if (val & GPT_TIMER_EN) {
114249ab747fSPaolo Bonzini                 ptimer_set_count(s->timer, val & 0xffff);
114349ab747fSPaolo Bonzini                 ptimer_run(s->timer, 0);
114449ab747fSPaolo Bonzini             } else {
114549ab747fSPaolo Bonzini                 ptimer_stop(s->timer);
114649ab747fSPaolo Bonzini                 ptimer_set_count(s->timer, 0xffff);
114749ab747fSPaolo Bonzini             }
114888e4bd67SPeter Maydell             ptimer_transaction_commit(s->timer);
114949ab747fSPaolo Bonzini         }
115049ab747fSPaolo Bonzini         s->gpt_cfg = val & (GPT_TIMER_EN | 0xffff);
115149ab747fSPaolo Bonzini         break;
115249ab747fSPaolo Bonzini     case CSR_WORD_SWAP:
115349ab747fSPaolo Bonzini         /* Ignored because we're in 32-bit mode.  */
115449ab747fSPaolo Bonzini         s->word_swap = val;
115549ab747fSPaolo Bonzini         break;
115649ab747fSPaolo Bonzini     case CSR_MAC_CSR_CMD:
115749ab747fSPaolo Bonzini         s->mac_cmd = val & 0x4000000f;
115849ab747fSPaolo Bonzini         if (val & 0x80000000) {
115949ab747fSPaolo Bonzini             if (val & 0x40000000) {
116049ab747fSPaolo Bonzini                 s->mac_data = do_mac_read(s, val & 0xf);
116149ab747fSPaolo Bonzini                 DPRINTF("MAC read %d = 0x%08x\n", val & 0xf, s->mac_data);
116249ab747fSPaolo Bonzini             } else {
116349ab747fSPaolo Bonzini                 DPRINTF("MAC write %d = 0x%08x\n", val & 0xf, s->mac_data);
116449ab747fSPaolo Bonzini                 do_mac_write(s, val & 0xf, s->mac_data);
116549ab747fSPaolo Bonzini             }
116649ab747fSPaolo Bonzini         }
116749ab747fSPaolo Bonzini         break;
116849ab747fSPaolo Bonzini     case CSR_MAC_CSR_DATA:
116949ab747fSPaolo Bonzini         s->mac_data = val;
117049ab747fSPaolo Bonzini         break;
117149ab747fSPaolo Bonzini     case CSR_AFC_CFG:
117249ab747fSPaolo Bonzini         s->afc_cfg = val & 0x00ffffff;
117349ab747fSPaolo Bonzini         break;
117449ab747fSPaolo Bonzini     case CSR_E2P_CMD:
117549ab747fSPaolo Bonzini         lan9118_eeprom_cmd(s, (val >> 28) & 7, val & 0x7f);
117649ab747fSPaolo Bonzini         break;
117749ab747fSPaolo Bonzini     case CSR_E2P_DATA:
117849ab747fSPaolo Bonzini         s->e2p_data = val & 0xff;
117949ab747fSPaolo Bonzini         break;
118049ab747fSPaolo Bonzini 
118149ab747fSPaolo Bonzini     default:
118252b4bb73SAndrew Baumann         qemu_log_mask(LOG_GUEST_ERROR, "lan9118_write: Bad reg 0x%x = %x\n",
118352b4bb73SAndrew Baumann                       (int)offset, (int)val);
118449ab747fSPaolo Bonzini         break;
118549ab747fSPaolo Bonzini     }
118649ab747fSPaolo Bonzini     lan9118_update(s);
118749ab747fSPaolo Bonzini }
118849ab747fSPaolo Bonzini 
lan9118_writew(void * opaque,hwaddr offset,uint32_t val)118949ab747fSPaolo Bonzini static void lan9118_writew(void *opaque, hwaddr offset,
119049ab747fSPaolo Bonzini                            uint32_t val)
119149ab747fSPaolo Bonzini {
119249ab747fSPaolo Bonzini     lan9118_state *s = (lan9118_state *)opaque;
119349ab747fSPaolo Bonzini     offset &= 0xff;
119449ab747fSPaolo Bonzini 
119549ab747fSPaolo Bonzini     if (s->write_word_prev_offset != (offset & ~0x3)) {
119649ab747fSPaolo Bonzini         /* New offset, reset word counter */
119749ab747fSPaolo Bonzini         s->write_word_n = 0;
119849ab747fSPaolo Bonzini         s->write_word_prev_offset = offset & ~0x3;
119949ab747fSPaolo Bonzini     }
120049ab747fSPaolo Bonzini 
120149ab747fSPaolo Bonzini     if (offset & 0x2) {
120249ab747fSPaolo Bonzini         s->write_word_h = val;
120349ab747fSPaolo Bonzini     } else {
120449ab747fSPaolo Bonzini         s->write_word_l = val;
120549ab747fSPaolo Bonzini     }
120649ab747fSPaolo Bonzini 
120749ab747fSPaolo Bonzini     //DPRINTF("Writew reg 0x%02x = 0x%08x\n", (int)offset, val);
120849ab747fSPaolo Bonzini     s->write_word_n++;
120949ab747fSPaolo Bonzini     if (s->write_word_n == 2) {
121049ab747fSPaolo Bonzini         s->write_word_n = 0;
121149ab747fSPaolo Bonzini         lan9118_writel(s, offset & ~3, s->write_word_l +
121249ab747fSPaolo Bonzini                 (s->write_word_h << 16), 4);
121349ab747fSPaolo Bonzini     }
121449ab747fSPaolo Bonzini }
121549ab747fSPaolo Bonzini 
lan9118_16bit_mode_write(void * opaque,hwaddr offset,uint64_t val,unsigned size)121649ab747fSPaolo Bonzini static void lan9118_16bit_mode_write(void *opaque, hwaddr offset,
121749ab747fSPaolo Bonzini                                      uint64_t val, unsigned size)
121849ab747fSPaolo Bonzini {
121949ab747fSPaolo Bonzini     switch (size) {
122049ab747fSPaolo Bonzini     case 2:
122149ab747fSPaolo Bonzini         lan9118_writew(opaque, offset, (uint32_t)val);
122249ab747fSPaolo Bonzini         return;
122349ab747fSPaolo Bonzini     case 4:
122449ab747fSPaolo Bonzini         lan9118_writel(opaque, offset, val, size);
122549ab747fSPaolo Bonzini         return;
122649ab747fSPaolo Bonzini     }
122749ab747fSPaolo Bonzini 
122844c94cdbSQiang Liu     qemu_log_mask(LOG_GUEST_ERROR,
122944c94cdbSQiang Liu                   "lan9118_16bit_mode_write: Bad size 0x%x\n", size);
123049ab747fSPaolo Bonzini }
123149ab747fSPaolo Bonzini 
lan9118_readl(void * opaque,hwaddr offset,unsigned size)123249ab747fSPaolo Bonzini static uint64_t lan9118_readl(void *opaque, hwaddr offset,
123349ab747fSPaolo Bonzini                               unsigned size)
123449ab747fSPaolo Bonzini {
123549ab747fSPaolo Bonzini     lan9118_state *s = (lan9118_state *)opaque;
123649ab747fSPaolo Bonzini 
123749ab747fSPaolo Bonzini     //DPRINTF("Read reg 0x%02x\n", (int)offset);
12385cab6d5aSPeter Maydell     if (offset <= RX_DATA_FIFO_PORT_LAST) {
123949ab747fSPaolo Bonzini         /* RX FIFO */
124049ab747fSPaolo Bonzini         return rx_fifo_pop(s);
124149ab747fSPaolo Bonzini     }
124249ab747fSPaolo Bonzini     switch (offset) {
12435cab6d5aSPeter Maydell     case RX_STATUS_FIFO_PORT:
124449ab747fSPaolo Bonzini         return rx_status_fifo_pop(s);
12455cab6d5aSPeter Maydell     case RX_STATUS_FIFO_PEEK:
1246e7e29fdbSPeter Maydell         return s->rx_status_fifo[s->rx_status_fifo_head];
12475cab6d5aSPeter Maydell     case TX_STATUS_FIFO_PORT:
124849ab747fSPaolo Bonzini         return tx_status_fifo_pop(s);
12495cab6d5aSPeter Maydell     case TX_STATUS_FIFO_PEEK:
125049ab747fSPaolo Bonzini         return s->tx_status_fifo[s->tx_status_fifo_head];
125149ab747fSPaolo Bonzini     case CSR_ID_REV:
125249ab747fSPaolo Bonzini         return 0x01180001;
125349ab747fSPaolo Bonzini     case CSR_IRQ_CFG:
125449ab747fSPaolo Bonzini         return s->irq_cfg;
125549ab747fSPaolo Bonzini     case CSR_INT_STS:
125649ab747fSPaolo Bonzini         return s->int_sts;
125749ab747fSPaolo Bonzini     case CSR_INT_EN:
125849ab747fSPaolo Bonzini         return s->int_en;
125949ab747fSPaolo Bonzini     case CSR_BYTE_TEST:
126049ab747fSPaolo Bonzini         return 0x87654321;
126149ab747fSPaolo Bonzini     case CSR_FIFO_INT:
126249ab747fSPaolo Bonzini         return s->fifo_int;
126349ab747fSPaolo Bonzini     case CSR_RX_CFG:
126449ab747fSPaolo Bonzini         return s->rx_cfg;
126549ab747fSPaolo Bonzini     case CSR_TX_CFG:
126649ab747fSPaolo Bonzini         return s->tx_cfg;
126749ab747fSPaolo Bonzini     case CSR_HW_CFG:
126849ab747fSPaolo Bonzini         return s->hw_cfg;
126949ab747fSPaolo Bonzini     case CSR_RX_DP_CTRL:
127049ab747fSPaolo Bonzini         return 0;
127149ab747fSPaolo Bonzini     case CSR_RX_FIFO_INF:
127249ab747fSPaolo Bonzini         return (s->rx_status_fifo_used << 16) | (s->rx_fifo_used << 2);
127349ab747fSPaolo Bonzini     case CSR_TX_FIFO_INF:
127449ab747fSPaolo Bonzini         return (s->tx_status_fifo_used << 16)
127549ab747fSPaolo Bonzini                | (s->tx_fifo_size - s->txp->fifo_used);
127649ab747fSPaolo Bonzini     case CSR_PMT_CTRL:
127749ab747fSPaolo Bonzini         return s->pmt_ctrl;
127849ab747fSPaolo Bonzini     case CSR_GPIO_CFG:
127949ab747fSPaolo Bonzini         return s->gpio_cfg;
128049ab747fSPaolo Bonzini     case CSR_GPT_CFG:
128149ab747fSPaolo Bonzini         return s->gpt_cfg;
128249ab747fSPaolo Bonzini     case CSR_GPT_CNT:
128349ab747fSPaolo Bonzini         return ptimer_get_count(s->timer);
128449ab747fSPaolo Bonzini     case CSR_WORD_SWAP:
128549ab747fSPaolo Bonzini         return s->word_swap;
128649ab747fSPaolo Bonzini     case CSR_FREE_RUN:
1287bc72ad67SAlex Bligh         return (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / 40) - s->free_timer_start;
128849ab747fSPaolo Bonzini     case CSR_RX_DROP:
128949ab747fSPaolo Bonzini         /* TODO: Implement dropped frames counter.  */
129049ab747fSPaolo Bonzini         return 0;
129149ab747fSPaolo Bonzini     case CSR_MAC_CSR_CMD:
129249ab747fSPaolo Bonzini         return s->mac_cmd;
129349ab747fSPaolo Bonzini     case CSR_MAC_CSR_DATA:
129449ab747fSPaolo Bonzini         return s->mac_data;
129549ab747fSPaolo Bonzini     case CSR_AFC_CFG:
129649ab747fSPaolo Bonzini         return s->afc_cfg;
129749ab747fSPaolo Bonzini     case CSR_E2P_CMD:
129849ab747fSPaolo Bonzini         return s->e2p_cmd;
129949ab747fSPaolo Bonzini     case CSR_E2P_DATA:
130049ab747fSPaolo Bonzini         return s->e2p_data;
130149ab747fSPaolo Bonzini     }
130252b4bb73SAndrew Baumann     qemu_log_mask(LOG_GUEST_ERROR, "lan9118_read: Bad reg 0x%x\n", (int)offset);
130349ab747fSPaolo Bonzini     return 0;
130449ab747fSPaolo Bonzini }
130549ab747fSPaolo Bonzini 
lan9118_readw(void * opaque,hwaddr offset)130649ab747fSPaolo Bonzini static uint32_t lan9118_readw(void *opaque, hwaddr offset)
130749ab747fSPaolo Bonzini {
130849ab747fSPaolo Bonzini     lan9118_state *s = (lan9118_state *)opaque;
130949ab747fSPaolo Bonzini     uint32_t val;
131049ab747fSPaolo Bonzini 
131149ab747fSPaolo Bonzini     if (s->read_word_prev_offset != (offset & ~0x3)) {
131249ab747fSPaolo Bonzini         /* New offset, reset word counter */
131349ab747fSPaolo Bonzini         s->read_word_n = 0;
131449ab747fSPaolo Bonzini         s->read_word_prev_offset = offset & ~0x3;
131549ab747fSPaolo Bonzini     }
131649ab747fSPaolo Bonzini 
131749ab747fSPaolo Bonzini     s->read_word_n++;
131849ab747fSPaolo Bonzini     if (s->read_word_n == 1) {
131949ab747fSPaolo Bonzini         s->read_long = lan9118_readl(s, offset & ~3, 4);
132049ab747fSPaolo Bonzini     } else {
132149ab747fSPaolo Bonzini         s->read_word_n = 0;
132249ab747fSPaolo Bonzini     }
132349ab747fSPaolo Bonzini 
132449ab747fSPaolo Bonzini     if (offset & 2) {
132549ab747fSPaolo Bonzini         val = s->read_long >> 16;
132649ab747fSPaolo Bonzini     } else {
132749ab747fSPaolo Bonzini         val = s->read_long & 0xFFFF;
132849ab747fSPaolo Bonzini     }
132949ab747fSPaolo Bonzini 
133049ab747fSPaolo Bonzini     //DPRINTF("Readw reg 0x%02x, val 0x%x\n", (int)offset, val);
133149ab747fSPaolo Bonzini     return val;
133249ab747fSPaolo Bonzini }
133349ab747fSPaolo Bonzini 
lan9118_16bit_mode_read(void * opaque,hwaddr offset,unsigned size)133449ab747fSPaolo Bonzini static uint64_t lan9118_16bit_mode_read(void *opaque, hwaddr offset,
133549ab747fSPaolo Bonzini                                         unsigned size)
133649ab747fSPaolo Bonzini {
133749ab747fSPaolo Bonzini     switch (size) {
133849ab747fSPaolo Bonzini     case 2:
133949ab747fSPaolo Bonzini         return lan9118_readw(opaque, offset);
134049ab747fSPaolo Bonzini     case 4:
134149ab747fSPaolo Bonzini         return lan9118_readl(opaque, offset, size);
134249ab747fSPaolo Bonzini     }
134349ab747fSPaolo Bonzini 
134444c94cdbSQiang Liu     qemu_log_mask(LOG_GUEST_ERROR,
134544c94cdbSQiang Liu                   "lan9118_16bit_mode_read: Bad size 0x%x\n", size);
134649ab747fSPaolo Bonzini     return 0;
134749ab747fSPaolo Bonzini }
134849ab747fSPaolo Bonzini 
134949ab747fSPaolo Bonzini static const MemoryRegionOps lan9118_mem_ops = {
135049ab747fSPaolo Bonzini     .read = lan9118_readl,
135149ab747fSPaolo Bonzini     .write = lan9118_writel,
135249ab747fSPaolo Bonzini     .endianness = DEVICE_NATIVE_ENDIAN,
135349ab747fSPaolo Bonzini };
135449ab747fSPaolo Bonzini 
135549ab747fSPaolo Bonzini static const MemoryRegionOps lan9118_16bit_mem_ops = {
135649ab747fSPaolo Bonzini     .read = lan9118_16bit_mode_read,
135749ab747fSPaolo Bonzini     .write = lan9118_16bit_mode_write,
135849ab747fSPaolo Bonzini     .endianness = DEVICE_NATIVE_ENDIAN,
135949ab747fSPaolo Bonzini };
136049ab747fSPaolo Bonzini 
136149ab747fSPaolo Bonzini static NetClientInfo net_lan9118_info = {
1362f394b2e2SEric Blake     .type = NET_CLIENT_DRIVER_NIC,
136349ab747fSPaolo Bonzini     .size = sizeof(NICState),
136449ab747fSPaolo Bonzini     .receive = lan9118_receive,
136549ab747fSPaolo Bonzini     .link_status_changed = lan9118_set_link,
136649ab747fSPaolo Bonzini };
136749ab747fSPaolo Bonzini 
lan9118_realize(DeviceState * dev,Error ** errp)1368f71b3367SCédric Le Goater static void lan9118_realize(DeviceState *dev, Error **errp)
136949ab747fSPaolo Bonzini {
1370f71b3367SCédric Le Goater     SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
13713ff66d10SAndreas Färber     lan9118_state *s = LAN9118(dev);
137249ab747fSPaolo Bonzini     int i;
137349ab747fSPaolo Bonzini     const MemoryRegionOps *mem_ops =
137449ab747fSPaolo Bonzini             s->mode_16bit ? &lan9118_16bit_mem_ops : &lan9118_mem_ops;
137549ab747fSPaolo Bonzini 
1376eedfac6fSPaolo Bonzini     memory_region_init_io(&s->mmio, OBJECT(dev), mem_ops, s,
1377eedfac6fSPaolo Bonzini                           "lan9118-mmio", 0x100);
13783ff66d10SAndreas Färber     sysbus_init_mmio(sbd, &s->mmio);
13793ff66d10SAndreas Färber     sysbus_init_irq(sbd, &s->irq);
138049ab747fSPaolo Bonzini     qemu_macaddr_default_if_unset(&s->conf.macaddr);
138149ab747fSPaolo Bonzini 
138249ab747fSPaolo Bonzini     s->nic = qemu_new_nic(&net_lan9118_info, &s->conf,
13837d0fefdfSAkihiko Odaki                           object_get_typename(OBJECT(dev)), dev->id,
13847d0fefdfSAkihiko Odaki                           &dev->mem_reentrancy_guard, s);
138549ab747fSPaolo Bonzini     qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
138649ab747fSPaolo Bonzini     s->eeprom[0] = 0xa5;
138749ab747fSPaolo Bonzini     for (i = 0; i < 6; i++) {
138849ab747fSPaolo Bonzini         s->eeprom[i + 1] = s->conf.macaddr.a[i];
138949ab747fSPaolo Bonzini     }
139049ab747fSPaolo Bonzini     s->pmt_ctrl = 1;
139149ab747fSPaolo Bonzini     s->txp = &s->tx_packet;
139249ab747fSPaolo Bonzini 
13939598c1bbSPeter Maydell     s->timer = ptimer_init(lan9118_tick, s, PTIMER_POLICY_LEGACY);
139488e4bd67SPeter Maydell     ptimer_transaction_begin(s->timer);
139549ab747fSPaolo Bonzini     ptimer_set_freq(s->timer, 10000);
139649ab747fSPaolo Bonzini     ptimer_set_limit(s->timer, 0xffff, 1);
139788e4bd67SPeter Maydell     ptimer_transaction_commit(s->timer);
139849ab747fSPaolo Bonzini }
139949ab747fSPaolo Bonzini 
140049ab747fSPaolo Bonzini static Property lan9118_properties[] = {
140149ab747fSPaolo Bonzini     DEFINE_NIC_PROPERTIES(lan9118_state, conf),
140249ab747fSPaolo Bonzini     DEFINE_PROP_UINT32("mode_16bit", lan9118_state, mode_16bit, 0),
140349ab747fSPaolo Bonzini     DEFINE_PROP_END_OF_LIST(),
140449ab747fSPaolo Bonzini };
140549ab747fSPaolo Bonzini 
lan9118_class_init(ObjectClass * klass,void * data)140649ab747fSPaolo Bonzini static void lan9118_class_init(ObjectClass *klass, void *data)
140749ab747fSPaolo Bonzini {
140849ab747fSPaolo Bonzini     DeviceClass *dc = DEVICE_CLASS(klass);
140949ab747fSPaolo Bonzini 
1410e3d08143SPeter Maydell     device_class_set_legacy_reset(dc, lan9118_reset);
14114f67d30bSMarc-André Lureau     device_class_set_props(dc, lan9118_properties);
141249ab747fSPaolo Bonzini     dc->vmsd = &vmstate_lan9118;
1413f71b3367SCédric Le Goater     dc->realize = lan9118_realize;
141449ab747fSPaolo Bonzini }
141549ab747fSPaolo Bonzini 
141649ab747fSPaolo Bonzini static const TypeInfo lan9118_info = {
14173ff66d10SAndreas Färber     .name          = TYPE_LAN9118,
141849ab747fSPaolo Bonzini     .parent        = TYPE_SYS_BUS_DEVICE,
141949ab747fSPaolo Bonzini     .instance_size = sizeof(lan9118_state),
142049ab747fSPaolo Bonzini     .class_init    = lan9118_class_init,
142149ab747fSPaolo Bonzini };
142249ab747fSPaolo Bonzini 
lan9118_register_types(void)142349ab747fSPaolo Bonzini static void lan9118_register_types(void)
142449ab747fSPaolo Bonzini {
142549ab747fSPaolo Bonzini     type_register_static(&lan9118_info);
142649ab747fSPaolo Bonzini }
142749ab747fSPaolo Bonzini 
142849ab747fSPaolo Bonzini /* Legacy helper function.  Should go away when machine config files are
142949ab747fSPaolo Bonzini    implemented.  */
lan9118_init(uint32_t base,qemu_irq irq)1430f138ed5eSDavid Woodhouse void lan9118_init(uint32_t base, qemu_irq irq)
143149ab747fSPaolo Bonzini {
143249ab747fSPaolo Bonzini     DeviceState *dev;
143349ab747fSPaolo Bonzini     SysBusDevice *s;
143449ab747fSPaolo Bonzini 
14353e80f690SMarkus Armbruster     dev = qdev_new(TYPE_LAN9118);
1436f138ed5eSDavid Woodhouse     qemu_configure_nic_device(dev, true, NULL);
143749ab747fSPaolo Bonzini     s = SYS_BUS_DEVICE(dev);
14383c6ef471SMarkus Armbruster     sysbus_realize_and_unref(s, &error_fatal);
143949ab747fSPaolo Bonzini     sysbus_mmio_map(s, 0, base);
144049ab747fSPaolo Bonzini     sysbus_connect_irq(s, 0, irq);
144149ab747fSPaolo Bonzini }
144249ab747fSPaolo Bonzini 
144349ab747fSPaolo Bonzini type_init(lan9118_register_types)
1444