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