108f787a3SHao Wu /* 208f787a3SHao Wu * Nuvoton NPCM7xx/8xx GMAC Module 308f787a3SHao Wu * 408f787a3SHao Wu * Copyright 2024 Google LLC 508f787a3SHao Wu * Authors: 608f787a3SHao Wu * Hao Wu <wuhaotsh@google.com> 708f787a3SHao Wu * Nabih Estefan <nabihestefan@google.com> 808f787a3SHao Wu * 908f787a3SHao Wu * This program is free software; you can redistribute it and/or modify it 1008f787a3SHao Wu * under the terms of the GNU General Public License as published by the 1108f787a3SHao Wu * Free Software Foundation; either version 2 of the License, or 1208f787a3SHao Wu * (at your option) any later version. 1308f787a3SHao Wu * 1408f787a3SHao Wu * This program is distributed in the hope that it will be useful, but WITHOUT 1508f787a3SHao Wu * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 1608f787a3SHao Wu * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 1708f787a3SHao Wu * for more details. 1808f787a3SHao Wu * 1908f787a3SHao Wu * Unsupported/unimplemented features: 2008f787a3SHao Wu * - MII is not implemented, MII_ADDR.BUSY and MII_DATA always return zero 2108f787a3SHao Wu * - Precision timestamp (PTP) is not implemented. 2208f787a3SHao Wu */ 2308f787a3SHao Wu 2408f787a3SHao Wu #include "qemu/osdep.h" 2508f787a3SHao Wu 2608f787a3SHao Wu #include "hw/registerfields.h" 2708f787a3SHao Wu #include "hw/net/mii.h" 2808f787a3SHao Wu #include "hw/net/npcm_gmac.h" 2908f787a3SHao Wu #include "migration/vmstate.h" 30a4dd7a1dSNabih Estefan Diaz #include "net/checksum.h" 31a4dd7a1dSNabih Estefan Diaz #include "net/eth.h" 32a4dd7a1dSNabih Estefan Diaz #include "net/net.h" 33a4dd7a1dSNabih Estefan Diaz #include "qemu/cutils.h" 3408f787a3SHao Wu #include "qemu/log.h" 3508f787a3SHao Wu #include "qemu/units.h" 3608f787a3SHao Wu #include "sysemu/dma.h" 3708f787a3SHao Wu #include "trace.h" 3808f787a3SHao Wu 3908f787a3SHao Wu REG32(NPCM_DMA_BUS_MODE, 0x1000) 4008f787a3SHao Wu REG32(NPCM_DMA_XMT_POLL_DEMAND, 0x1004) 4108f787a3SHao Wu REG32(NPCM_DMA_RCV_POLL_DEMAND, 0x1008) 4208f787a3SHao Wu REG32(NPCM_DMA_RX_BASE_ADDR, 0x100c) 4308f787a3SHao Wu REG32(NPCM_DMA_TX_BASE_ADDR, 0x1010) 4408f787a3SHao Wu REG32(NPCM_DMA_STATUS, 0x1014) 4508f787a3SHao Wu REG32(NPCM_DMA_CONTROL, 0x1018) 4608f787a3SHao Wu REG32(NPCM_DMA_INTR_ENA, 0x101c) 4708f787a3SHao Wu REG32(NPCM_DMA_MISSED_FRAME_CTR, 0x1020) 4808f787a3SHao Wu REG32(NPCM_DMA_HOST_TX_DESC, 0x1048) 4908f787a3SHao Wu REG32(NPCM_DMA_HOST_RX_DESC, 0x104c) 5008f787a3SHao Wu REG32(NPCM_DMA_CUR_TX_BUF_ADDR, 0x1050) 5108f787a3SHao Wu REG32(NPCM_DMA_CUR_RX_BUF_ADDR, 0x1054) 5208f787a3SHao Wu REG32(NPCM_DMA_HW_FEATURE, 0x1058) 5308f787a3SHao Wu 5408f787a3SHao Wu REG32(NPCM_GMAC_MAC_CONFIG, 0x0) 5508f787a3SHao Wu REG32(NPCM_GMAC_FRAME_FILTER, 0x4) 5608f787a3SHao Wu REG32(NPCM_GMAC_HASH_HIGH, 0x8) 5708f787a3SHao Wu REG32(NPCM_GMAC_HASH_LOW, 0xc) 5808f787a3SHao Wu REG32(NPCM_GMAC_MII_ADDR, 0x10) 5908f787a3SHao Wu REG32(NPCM_GMAC_MII_DATA, 0x14) 6008f787a3SHao Wu REG32(NPCM_GMAC_FLOW_CTRL, 0x18) 6108f787a3SHao Wu REG32(NPCM_GMAC_VLAN_FLAG, 0x1c) 6208f787a3SHao Wu REG32(NPCM_GMAC_VERSION, 0x20) 6308f787a3SHao Wu REG32(NPCM_GMAC_WAKEUP_FILTER, 0x28) 6408f787a3SHao Wu REG32(NPCM_GMAC_PMT, 0x2c) 6508f787a3SHao Wu REG32(NPCM_GMAC_LPI_CTRL, 0x30) 6608f787a3SHao Wu REG32(NPCM_GMAC_TIMER_CTRL, 0x34) 6708f787a3SHao Wu REG32(NPCM_GMAC_INT_STATUS, 0x38) 6808f787a3SHao Wu REG32(NPCM_GMAC_INT_MASK, 0x3c) 6908f787a3SHao Wu REG32(NPCM_GMAC_MAC0_ADDR_HI, 0x40) 7008f787a3SHao Wu REG32(NPCM_GMAC_MAC0_ADDR_LO, 0x44) 7108f787a3SHao Wu REG32(NPCM_GMAC_MAC1_ADDR_HI, 0x48) 7208f787a3SHao Wu REG32(NPCM_GMAC_MAC1_ADDR_LO, 0x4c) 7308f787a3SHao Wu REG32(NPCM_GMAC_MAC2_ADDR_HI, 0x50) 7408f787a3SHao Wu REG32(NPCM_GMAC_MAC2_ADDR_LO, 0x54) 7508f787a3SHao Wu REG32(NPCM_GMAC_MAC3_ADDR_HI, 0x58) 7608f787a3SHao Wu REG32(NPCM_GMAC_MAC3_ADDR_LO, 0x5c) 7708f787a3SHao Wu REG32(NPCM_GMAC_RGMII_STATUS, 0xd8) 7808f787a3SHao Wu REG32(NPCM_GMAC_WATCHDOG, 0xdc) 7908f787a3SHao Wu REG32(NPCM_GMAC_PTP_TCR, 0x700) 8008f787a3SHao Wu REG32(NPCM_GMAC_PTP_SSIR, 0x704) 8108f787a3SHao Wu REG32(NPCM_GMAC_PTP_STSR, 0x708) 8208f787a3SHao Wu REG32(NPCM_GMAC_PTP_STNSR, 0x70c) 8308f787a3SHao Wu REG32(NPCM_GMAC_PTP_STSUR, 0x710) 8408f787a3SHao Wu REG32(NPCM_GMAC_PTP_STNSUR, 0x714) 8508f787a3SHao Wu REG32(NPCM_GMAC_PTP_TAR, 0x718) 8608f787a3SHao Wu REG32(NPCM_GMAC_PTP_TTSR, 0x71c) 8708f787a3SHao Wu 8808f787a3SHao Wu /* Register Fields */ 8908f787a3SHao Wu #define NPCM_GMAC_MII_ADDR_BUSY BIT(0) 9008f787a3SHao Wu #define NPCM_GMAC_MII_ADDR_WRITE BIT(1) 9108f787a3SHao Wu #define NPCM_GMAC_MII_ADDR_GR(rv) extract16((rv), 6, 5) 9208f787a3SHao Wu #define NPCM_GMAC_MII_ADDR_PA(rv) extract16((rv), 11, 5) 9308f787a3SHao Wu 9408f787a3SHao Wu #define NPCM_GMAC_INT_MASK_LPIIM BIT(10) 9508f787a3SHao Wu #define NPCM_GMAC_INT_MASK_PMTM BIT(3) 9608f787a3SHao Wu #define NPCM_GMAC_INT_MASK_RGIM BIT(0) 9708f787a3SHao Wu 9808f787a3SHao Wu #define NPCM_DMA_BUS_MODE_SWR BIT(0) 9908f787a3SHao Wu 10008f787a3SHao Wu static const uint32_t npcm_gmac_cold_reset_values[NPCM_GMAC_NR_REGS] = { 10108f787a3SHao Wu /* Reduce version to 3.2 so that the kernel can enable interrupt. */ 10208f787a3SHao Wu [R_NPCM_GMAC_VERSION] = 0x00001032, 10308f787a3SHao Wu [R_NPCM_GMAC_TIMER_CTRL] = 0x03e80000, 10408f787a3SHao Wu [R_NPCM_GMAC_MAC0_ADDR_HI] = 0x8000ffff, 10508f787a3SHao Wu [R_NPCM_GMAC_MAC0_ADDR_LO] = 0xffffffff, 10608f787a3SHao Wu [R_NPCM_GMAC_MAC1_ADDR_HI] = 0x0000ffff, 10708f787a3SHao Wu [R_NPCM_GMAC_MAC1_ADDR_LO] = 0xffffffff, 10808f787a3SHao Wu [R_NPCM_GMAC_MAC2_ADDR_HI] = 0x0000ffff, 10908f787a3SHao Wu [R_NPCM_GMAC_MAC2_ADDR_LO] = 0xffffffff, 11008f787a3SHao Wu [R_NPCM_GMAC_MAC3_ADDR_HI] = 0x0000ffff, 11108f787a3SHao Wu [R_NPCM_GMAC_MAC3_ADDR_LO] = 0xffffffff, 11208f787a3SHao Wu [R_NPCM_GMAC_PTP_TCR] = 0x00002000, 11308f787a3SHao Wu [R_NPCM_DMA_BUS_MODE] = 0x00020101, 11408f787a3SHao Wu [R_NPCM_DMA_HW_FEATURE] = 0x100d4f37, 11508f787a3SHao Wu }; 11608f787a3SHao Wu 11708f787a3SHao Wu static const uint16_t phy_reg_init[] = { 11808f787a3SHao Wu [MII_BMCR] = MII_BMCR_AUTOEN | MII_BMCR_FD | MII_BMCR_SPEED1000, 11908f787a3SHao Wu [MII_BMSR] = MII_BMSR_100TX_FD | MII_BMSR_100TX_HD | MII_BMSR_10T_FD | 12008f787a3SHao Wu MII_BMSR_10T_HD | MII_BMSR_EXTSTAT | MII_BMSR_AUTONEG | 12108f787a3SHao Wu MII_BMSR_LINK_ST | MII_BMSR_EXTCAP, 12208f787a3SHao Wu [MII_PHYID1] = 0x0362, 12308f787a3SHao Wu [MII_PHYID2] = 0x5e6a, 12408f787a3SHao Wu [MII_ANAR] = MII_ANAR_TXFD | MII_ANAR_TX | MII_ANAR_10FD | 12508f787a3SHao Wu MII_ANAR_10 | MII_ANAR_CSMACD, 12608f787a3SHao Wu [MII_ANLPAR] = MII_ANLPAR_ACK | MII_ANLPAR_PAUSE | 12708f787a3SHao Wu MII_ANLPAR_TXFD | MII_ANLPAR_TX | MII_ANLPAR_10FD | 12808f787a3SHao Wu MII_ANLPAR_10 | MII_ANLPAR_CSMACD, 12908f787a3SHao Wu [MII_ANER] = 0x64 | MII_ANER_NWAY, 13008f787a3SHao Wu [MII_ANNP] = 0x2001, 13108f787a3SHao Wu [MII_CTRL1000] = MII_CTRL1000_FULL, 13208f787a3SHao Wu [MII_STAT1000] = MII_STAT1000_FULL, 13308f787a3SHao Wu [MII_EXTSTAT] = 0x3000, /* 1000BASTE_T full-duplex capable */ 13408f787a3SHao Wu }; 13508f787a3SHao Wu 13608f787a3SHao Wu static void npcm_gmac_soft_reset(NPCMGMACState *gmac) 13708f787a3SHao Wu { 13808f787a3SHao Wu memcpy(gmac->regs, npcm_gmac_cold_reset_values, 13908f787a3SHao Wu NPCM_GMAC_NR_REGS * sizeof(uint32_t)); 14008f787a3SHao Wu /* Clear reset bits */ 14108f787a3SHao Wu gmac->regs[R_NPCM_DMA_BUS_MODE] &= ~NPCM_DMA_BUS_MODE_SWR; 14208f787a3SHao Wu } 14308f787a3SHao Wu 14408f787a3SHao Wu static void gmac_phy_set_link(NPCMGMACState *gmac, bool active) 14508f787a3SHao Wu { 14608f787a3SHao Wu /* Autonegotiation status mirrors link status. */ 14708f787a3SHao Wu if (active) { 14808f787a3SHao Wu gmac->phy_regs[0][MII_BMSR] |= (MII_BMSR_LINK_ST | MII_BMSR_AN_COMP); 14908f787a3SHao Wu } else { 15008f787a3SHao Wu gmac->phy_regs[0][MII_BMSR] &= ~(MII_BMSR_LINK_ST | MII_BMSR_AN_COMP); 15108f787a3SHao Wu } 15208f787a3SHao Wu } 15308f787a3SHao Wu 15408f787a3SHao Wu static bool gmac_can_receive(NetClientState *nc) 15508f787a3SHao Wu { 156a4dd7a1dSNabih Estefan Diaz NPCMGMACState *gmac = NPCM_GMAC(qemu_get_nic_opaque(nc)); 157a4dd7a1dSNabih Estefan Diaz 158a4dd7a1dSNabih Estefan Diaz /* If GMAC receive is disabled. */ 159a4dd7a1dSNabih Estefan Diaz if (!(gmac->regs[R_NPCM_GMAC_MAC_CONFIG] & NPCM_GMAC_MAC_CONFIG_RX_EN)) { 160a4dd7a1dSNabih Estefan Diaz return false; 161a4dd7a1dSNabih Estefan Diaz } 162a4dd7a1dSNabih Estefan Diaz 163a4dd7a1dSNabih Estefan Diaz /* If GMAC DMA RX is stopped. */ 164a4dd7a1dSNabih Estefan Diaz if (!(gmac->regs[R_NPCM_DMA_CONTROL] & NPCM_DMA_CONTROL_START_STOP_RX)) { 165a4dd7a1dSNabih Estefan Diaz return false; 166a4dd7a1dSNabih Estefan Diaz } 16708f787a3SHao Wu return true; 16808f787a3SHao Wu } 16908f787a3SHao Wu 17008f787a3SHao Wu /* 17108f787a3SHao Wu * Function that updates the GMAC IRQ 17208f787a3SHao Wu * It find the logical OR of the enabled bits for NIS (if enabled) 17308f787a3SHao Wu * It find the logical OR of the enabled bits for AIS (if enabled) 17408f787a3SHao Wu */ 17508f787a3SHao Wu static void gmac_update_irq(NPCMGMACState *gmac) 17608f787a3SHao Wu { 17708f787a3SHao Wu /* 17808f787a3SHao Wu * Check if the normal interrupts summary is enabled 17908f787a3SHao Wu * if so, add the bits for the summary that are enabled 18008f787a3SHao Wu */ 18108f787a3SHao Wu if (gmac->regs[R_NPCM_DMA_INTR_ENA] & gmac->regs[R_NPCM_DMA_STATUS] & 18208f787a3SHao Wu (NPCM_DMA_INTR_ENAB_NIE_BITS)) { 18308f787a3SHao Wu gmac->regs[R_NPCM_DMA_STATUS] |= NPCM_DMA_STATUS_NIS; 18408f787a3SHao Wu } 18508f787a3SHao Wu /* 18608f787a3SHao Wu * Check if the abnormal interrupts summary is enabled 18708f787a3SHao Wu * if so, add the bits for the summary that are enabled 18808f787a3SHao Wu */ 18908f787a3SHao Wu if (gmac->regs[R_NPCM_DMA_INTR_ENA] & gmac->regs[R_NPCM_DMA_STATUS] & 19008f787a3SHao Wu (NPCM_DMA_INTR_ENAB_AIE_BITS)) { 19108f787a3SHao Wu gmac->regs[R_NPCM_DMA_STATUS] |= NPCM_DMA_STATUS_AIS; 19208f787a3SHao Wu } 19308f787a3SHao Wu 19408f787a3SHao Wu /* Get the logical OR of both normal and abnormal interrupts */ 19508f787a3SHao Wu int level = !!((gmac->regs[R_NPCM_DMA_STATUS] & 19608f787a3SHao Wu gmac->regs[R_NPCM_DMA_INTR_ENA] & 19708f787a3SHao Wu NPCM_DMA_STATUS_NIS) | 19808f787a3SHao Wu (gmac->regs[R_NPCM_DMA_STATUS] & 19908f787a3SHao Wu gmac->regs[R_NPCM_DMA_INTR_ENA] & 20008f787a3SHao Wu NPCM_DMA_STATUS_AIS)); 20108f787a3SHao Wu 20208f787a3SHao Wu /* Set the IRQ */ 20308f787a3SHao Wu trace_npcm_gmac_update_irq(DEVICE(gmac)->canonical_path, 20408f787a3SHao Wu gmac->regs[R_NPCM_DMA_STATUS], 20508f787a3SHao Wu gmac->regs[R_NPCM_DMA_INTR_ENA], 20608f787a3SHao Wu level); 20708f787a3SHao Wu qemu_set_irq(gmac->irq, level); 20808f787a3SHao Wu } 20908f787a3SHao Wu 210a4dd7a1dSNabih Estefan Diaz static int gmac_read_rx_desc(dma_addr_t addr, struct NPCMGMACRxDesc *desc) 211a4dd7a1dSNabih Estefan Diaz { 212a4dd7a1dSNabih Estefan Diaz if (dma_memory_read(&address_space_memory, addr, desc, 213a4dd7a1dSNabih Estefan Diaz sizeof(*desc), MEMTXATTRS_UNSPECIFIED)) { 214a4dd7a1dSNabih Estefan Diaz qemu_log_mask(LOG_GUEST_ERROR, "%s: Failed to read descriptor @ 0x%" 215a4dd7a1dSNabih Estefan Diaz HWADDR_PRIx "\n", __func__, addr); 216a4dd7a1dSNabih Estefan Diaz return -1; 217a4dd7a1dSNabih Estefan Diaz } 218a4dd7a1dSNabih Estefan Diaz desc->rdes0 = le32_to_cpu(desc->rdes0); 219a4dd7a1dSNabih Estefan Diaz desc->rdes1 = le32_to_cpu(desc->rdes1); 220a4dd7a1dSNabih Estefan Diaz desc->rdes2 = le32_to_cpu(desc->rdes2); 221a4dd7a1dSNabih Estefan Diaz desc->rdes3 = le32_to_cpu(desc->rdes3); 222a4dd7a1dSNabih Estefan Diaz return 0; 223a4dd7a1dSNabih Estefan Diaz } 224a4dd7a1dSNabih Estefan Diaz 225a4dd7a1dSNabih Estefan Diaz static int gmac_write_rx_desc(dma_addr_t addr, struct NPCMGMACRxDesc *desc) 226a4dd7a1dSNabih Estefan Diaz { 227a4dd7a1dSNabih Estefan Diaz struct NPCMGMACRxDesc le_desc; 228a4dd7a1dSNabih Estefan Diaz le_desc.rdes0 = cpu_to_le32(desc->rdes0); 229a4dd7a1dSNabih Estefan Diaz le_desc.rdes1 = cpu_to_le32(desc->rdes1); 230a4dd7a1dSNabih Estefan Diaz le_desc.rdes2 = cpu_to_le32(desc->rdes2); 231a4dd7a1dSNabih Estefan Diaz le_desc.rdes3 = cpu_to_le32(desc->rdes3); 232a4dd7a1dSNabih Estefan Diaz if (dma_memory_write(&address_space_memory, addr, &le_desc, 233a4dd7a1dSNabih Estefan Diaz sizeof(le_desc), MEMTXATTRS_UNSPECIFIED)) { 234a4dd7a1dSNabih Estefan Diaz qemu_log_mask(LOG_GUEST_ERROR, "%s: Failed to write descriptor @ 0x%" 235a4dd7a1dSNabih Estefan Diaz HWADDR_PRIx "\n", __func__, addr); 236a4dd7a1dSNabih Estefan Diaz return -1; 237a4dd7a1dSNabih Estefan Diaz } 238a4dd7a1dSNabih Estefan Diaz return 0; 239a4dd7a1dSNabih Estefan Diaz } 240a4dd7a1dSNabih Estefan Diaz 2411c51c571SNabih Estefan Diaz static int gmac_read_tx_desc(dma_addr_t addr, struct NPCMGMACTxDesc *desc) 2421c51c571SNabih Estefan Diaz { 2431c51c571SNabih Estefan Diaz if (dma_memory_read(&address_space_memory, addr, desc, 2441c51c571SNabih Estefan Diaz sizeof(*desc), MEMTXATTRS_UNSPECIFIED)) { 2451c51c571SNabih Estefan Diaz qemu_log_mask(LOG_GUEST_ERROR, "%s: Failed to read descriptor @ 0x%" 2461c51c571SNabih Estefan Diaz HWADDR_PRIx "\n", __func__, addr); 2471c51c571SNabih Estefan Diaz return -1; 2481c51c571SNabih Estefan Diaz } 2491c51c571SNabih Estefan Diaz desc->tdes0 = le32_to_cpu(desc->tdes0); 2501c51c571SNabih Estefan Diaz desc->tdes1 = le32_to_cpu(desc->tdes1); 2511c51c571SNabih Estefan Diaz desc->tdes2 = le32_to_cpu(desc->tdes2); 2521c51c571SNabih Estefan Diaz desc->tdes3 = le32_to_cpu(desc->tdes3); 2531c51c571SNabih Estefan Diaz return 0; 2541c51c571SNabih Estefan Diaz } 2551c51c571SNabih Estefan Diaz 2561c51c571SNabih Estefan Diaz static int gmac_write_tx_desc(dma_addr_t addr, struct NPCMGMACTxDesc *desc) 2571c51c571SNabih Estefan Diaz { 2581c51c571SNabih Estefan Diaz struct NPCMGMACTxDesc le_desc; 2591c51c571SNabih Estefan Diaz le_desc.tdes0 = cpu_to_le32(desc->tdes0); 2601c51c571SNabih Estefan Diaz le_desc.tdes1 = cpu_to_le32(desc->tdes1); 2611c51c571SNabih Estefan Diaz le_desc.tdes2 = cpu_to_le32(desc->tdes2); 2621c51c571SNabih Estefan Diaz le_desc.tdes3 = cpu_to_le32(desc->tdes3); 2631c51c571SNabih Estefan Diaz if (dma_memory_write(&address_space_memory, addr, &le_desc, 2641c51c571SNabih Estefan Diaz sizeof(le_desc), MEMTXATTRS_UNSPECIFIED)) { 2651c51c571SNabih Estefan Diaz qemu_log_mask(LOG_GUEST_ERROR, "%s: Failed to write descriptor @ 0x%" 2661c51c571SNabih Estefan Diaz HWADDR_PRIx "\n", __func__, addr); 2671c51c571SNabih Estefan Diaz return -1; 2681c51c571SNabih Estefan Diaz } 2691c51c571SNabih Estefan Diaz return 0; 2701c51c571SNabih Estefan Diaz } 2711c51c571SNabih Estefan Diaz 272a4dd7a1dSNabih Estefan Diaz static int gmac_rx_transfer_frame_to_buffer(uint32_t rx_buf_len, 273a4dd7a1dSNabih Estefan Diaz uint32_t *left_frame, 274a4dd7a1dSNabih Estefan Diaz uint32_t rx_buf_addr, 275a4dd7a1dSNabih Estefan Diaz bool *eof_transferred, 276a4dd7a1dSNabih Estefan Diaz const uint8_t **frame_ptr, 277a4dd7a1dSNabih Estefan Diaz uint16_t *transferred) 278a4dd7a1dSNabih Estefan Diaz { 279a4dd7a1dSNabih Estefan Diaz uint32_t to_transfer; 280a4dd7a1dSNabih Estefan Diaz /* 281a4dd7a1dSNabih Estefan Diaz * Check that buffer is bigger than the frame being transfered 282a4dd7a1dSNabih Estefan Diaz * If bigger then transfer only whats left of frame 283a4dd7a1dSNabih Estefan Diaz * Else, fill frame with all the content possible 284a4dd7a1dSNabih Estefan Diaz */ 285a4dd7a1dSNabih Estefan Diaz if (rx_buf_len >= *left_frame) { 286a4dd7a1dSNabih Estefan Diaz to_transfer = *left_frame; 287a4dd7a1dSNabih Estefan Diaz *eof_transferred = true; 288a4dd7a1dSNabih Estefan Diaz } else { 289a4dd7a1dSNabih Estefan Diaz to_transfer = rx_buf_len; 290a4dd7a1dSNabih Estefan Diaz } 291a4dd7a1dSNabih Estefan Diaz 292a4dd7a1dSNabih Estefan Diaz /* write frame part to memory */ 293a4dd7a1dSNabih Estefan Diaz if (dma_memory_write(&address_space_memory, (uint64_t) rx_buf_addr, 294a4dd7a1dSNabih Estefan Diaz *frame_ptr, to_transfer, MEMTXATTRS_UNSPECIFIED)) { 295a4dd7a1dSNabih Estefan Diaz return -1; 296a4dd7a1dSNabih Estefan Diaz } 297a4dd7a1dSNabih Estefan Diaz 298a4dd7a1dSNabih Estefan Diaz /* update frame pointer and size of whats left of frame */ 299a4dd7a1dSNabih Estefan Diaz *frame_ptr += to_transfer; 300a4dd7a1dSNabih Estefan Diaz *left_frame -= to_transfer; 301a4dd7a1dSNabih Estefan Diaz *transferred += to_transfer; 302a4dd7a1dSNabih Estefan Diaz 303a4dd7a1dSNabih Estefan Diaz return 0; 304a4dd7a1dSNabih Estefan Diaz } 305a4dd7a1dSNabih Estefan Diaz 306a4dd7a1dSNabih Estefan Diaz static void gmac_dma_set_state(NPCMGMACState *gmac, int shift, uint32_t state) 307a4dd7a1dSNabih Estefan Diaz { 308a4dd7a1dSNabih Estefan Diaz gmac->regs[R_NPCM_DMA_STATUS] = deposit32(gmac->regs[R_NPCM_DMA_STATUS], 309a4dd7a1dSNabih Estefan Diaz shift, 3, state); 310a4dd7a1dSNabih Estefan Diaz } 311a4dd7a1dSNabih Estefan Diaz 31208f787a3SHao Wu static ssize_t gmac_receive(NetClientState *nc, const uint8_t *buf, size_t len) 31308f787a3SHao Wu { 314a4dd7a1dSNabih Estefan Diaz /* 315a4dd7a1dSNabih Estefan Diaz * Comments have steps that relate to the 316a4dd7a1dSNabih Estefan Diaz * receiving process steps in pg 386 317a4dd7a1dSNabih Estefan Diaz */ 318a4dd7a1dSNabih Estefan Diaz NPCMGMACState *gmac = NPCM_GMAC(qemu_get_nic_opaque(nc)); 319a4dd7a1dSNabih Estefan Diaz uint32_t left_frame = len; 320a4dd7a1dSNabih Estefan Diaz const uint8_t *frame_ptr = buf; 321a4dd7a1dSNabih Estefan Diaz uint32_t desc_addr; 322a4dd7a1dSNabih Estefan Diaz uint32_t rx_buf_len, rx_buf_addr; 323a4dd7a1dSNabih Estefan Diaz struct NPCMGMACRxDesc rx_desc; 324a4dd7a1dSNabih Estefan Diaz uint16_t transferred = 0; 325a4dd7a1dSNabih Estefan Diaz bool eof_transferred = false; 326a4dd7a1dSNabih Estefan Diaz 327a4dd7a1dSNabih Estefan Diaz trace_npcm_gmac_packet_receive(DEVICE(gmac)->canonical_path, len); 328a4dd7a1dSNabih Estefan Diaz if (!gmac_can_receive(nc)) { 329a4dd7a1dSNabih Estefan Diaz qemu_log_mask(LOG_GUEST_ERROR, "GMAC Currently is not able for Rx"); 330a4dd7a1dSNabih Estefan Diaz return -1; 331a4dd7a1dSNabih Estefan Diaz } 332a4dd7a1dSNabih Estefan Diaz if (!gmac->regs[R_NPCM_DMA_HOST_RX_DESC]) { 333a4dd7a1dSNabih Estefan Diaz gmac->regs[R_NPCM_DMA_HOST_RX_DESC] = 334a4dd7a1dSNabih Estefan Diaz NPCM_DMA_HOST_RX_DESC_MASK(gmac->regs[R_NPCM_DMA_RX_BASE_ADDR]); 335a4dd7a1dSNabih Estefan Diaz } 336a4dd7a1dSNabih Estefan Diaz desc_addr = NPCM_DMA_HOST_RX_DESC_MASK(gmac->regs[R_NPCM_DMA_HOST_RX_DESC]); 337a4dd7a1dSNabih Estefan Diaz 338a4dd7a1dSNabih Estefan Diaz /* step 1 */ 339a4dd7a1dSNabih Estefan Diaz gmac_dma_set_state(gmac, NPCM_DMA_STATUS_RX_PROCESS_STATE_SHIFT, 340a4dd7a1dSNabih Estefan Diaz NPCM_DMA_STATUS_RX_RUNNING_FETCHING_STATE); 341a4dd7a1dSNabih Estefan Diaz trace_npcm_gmac_packet_desc_read(DEVICE(gmac)->canonical_path, desc_addr); 342a4dd7a1dSNabih Estefan Diaz if (gmac_read_rx_desc(desc_addr, &rx_desc)) { 343a4dd7a1dSNabih Estefan Diaz qemu_log_mask(LOG_GUEST_ERROR, "RX Descriptor @ 0x%x cant be read\n", 344a4dd7a1dSNabih Estefan Diaz desc_addr); 345a4dd7a1dSNabih Estefan Diaz gmac_dma_set_state(gmac, NPCM_DMA_STATUS_RX_PROCESS_STATE_SHIFT, 346a4dd7a1dSNabih Estefan Diaz NPCM_DMA_STATUS_RX_SUSPENDED_STATE); 347a4dd7a1dSNabih Estefan Diaz return -1; 348a4dd7a1dSNabih Estefan Diaz } 349a4dd7a1dSNabih Estefan Diaz 350a4dd7a1dSNabih Estefan Diaz /* step 2 */ 351a4dd7a1dSNabih Estefan Diaz if (!(rx_desc.rdes0 & RX_DESC_RDES0_OWN)) { 352a4dd7a1dSNabih Estefan Diaz qemu_log_mask(LOG_GUEST_ERROR, 353a4dd7a1dSNabih Estefan Diaz "RX Descriptor @ 0x%x is owned by software\n", 354a4dd7a1dSNabih Estefan Diaz desc_addr); 355a4dd7a1dSNabih Estefan Diaz gmac->regs[R_NPCM_DMA_STATUS] |= NPCM_DMA_STATUS_RU; 356a4dd7a1dSNabih Estefan Diaz gmac->regs[R_NPCM_DMA_STATUS] |= NPCM_DMA_STATUS_RI; 357a4dd7a1dSNabih Estefan Diaz gmac_dma_set_state(gmac, NPCM_DMA_STATUS_RX_PROCESS_STATE_SHIFT, 358a4dd7a1dSNabih Estefan Diaz NPCM_DMA_STATUS_RX_SUSPENDED_STATE); 359a4dd7a1dSNabih Estefan Diaz gmac_update_irq(gmac); 360a4dd7a1dSNabih Estefan Diaz return len; 361a4dd7a1dSNabih Estefan Diaz } 362a4dd7a1dSNabih Estefan Diaz /* step 3 */ 363a4dd7a1dSNabih Estefan Diaz /* 364a4dd7a1dSNabih Estefan Diaz * TODO -- 365a4dd7a1dSNabih Estefan Diaz * Implement all frame filtering and processing (with its own interrupts) 366a4dd7a1dSNabih Estefan Diaz */ 367a4dd7a1dSNabih Estefan Diaz trace_npcm_gmac_debug_desc_data(DEVICE(gmac)->canonical_path, &rx_desc, 368a4dd7a1dSNabih Estefan Diaz rx_desc.rdes0, rx_desc.rdes1, rx_desc.rdes2, 369a4dd7a1dSNabih Estefan Diaz rx_desc.rdes3); 370a4dd7a1dSNabih Estefan Diaz /* Clear rdes0 for the incoming descriptor and set FS in first descriptor.*/ 371a4dd7a1dSNabih Estefan Diaz rx_desc.rdes0 = RX_DESC_RDES0_FIRST_DESC_MASK; 372a4dd7a1dSNabih Estefan Diaz 373a4dd7a1dSNabih Estefan Diaz gmac_dma_set_state(gmac, NPCM_DMA_STATUS_RX_PROCESS_STATE_SHIFT, 374a4dd7a1dSNabih Estefan Diaz NPCM_DMA_STATUS_RX_RUNNING_TRANSFERRING_STATE); 375a4dd7a1dSNabih Estefan Diaz 376a4dd7a1dSNabih Estefan Diaz /* Pad the frame with FCS as the kernel driver will strip it away. */ 377a4dd7a1dSNabih Estefan Diaz left_frame += ETH_FCS_LEN; 378a4dd7a1dSNabih Estefan Diaz 379a4dd7a1dSNabih Estefan Diaz /* repeat while we still have frame to transfer to memory */ 380a4dd7a1dSNabih Estefan Diaz while (!eof_transferred) { 381a4dd7a1dSNabih Estefan Diaz /* Return descriptor no matter what happens */ 382a4dd7a1dSNabih Estefan Diaz rx_desc.rdes0 &= ~RX_DESC_RDES0_OWN; 383a4dd7a1dSNabih Estefan Diaz /* Set the frame to be an IPv4/IPv6 frame. */ 384a4dd7a1dSNabih Estefan Diaz rx_desc.rdes0 |= RX_DESC_RDES0_FRM_TYPE_MASK; 385a4dd7a1dSNabih Estefan Diaz 386a4dd7a1dSNabih Estefan Diaz /* step 4 */ 387a4dd7a1dSNabih Estefan Diaz rx_buf_len = RX_DESC_RDES1_BFFR1_SZ_MASK(rx_desc.rdes1); 388a4dd7a1dSNabih Estefan Diaz rx_buf_addr = rx_desc.rdes2; 389a4dd7a1dSNabih Estefan Diaz gmac->regs[R_NPCM_DMA_CUR_RX_BUF_ADDR] = rx_buf_addr; 390a4dd7a1dSNabih Estefan Diaz gmac_rx_transfer_frame_to_buffer(rx_buf_len, &left_frame, rx_buf_addr, 391a4dd7a1dSNabih Estefan Diaz &eof_transferred, &frame_ptr, 392a4dd7a1dSNabih Estefan Diaz &transferred); 393a4dd7a1dSNabih Estefan Diaz 394a4dd7a1dSNabih Estefan Diaz trace_npcm_gmac_packet_receiving_buffer(DEVICE(gmac)->canonical_path, 395a4dd7a1dSNabih Estefan Diaz rx_buf_len, rx_buf_addr); 396a4dd7a1dSNabih Estefan Diaz /* if we still have frame left and the second buffer is not chained */ 397a4dd7a1dSNabih Estefan Diaz if (!(rx_desc.rdes1 & RX_DESC_RDES1_SEC_ADDR_CHND_MASK) && \ 398a4dd7a1dSNabih Estefan Diaz !eof_transferred) { 399a4dd7a1dSNabih Estefan Diaz /* repeat process from above on buffer 2 */ 400a4dd7a1dSNabih Estefan Diaz rx_buf_len = RX_DESC_RDES1_BFFR2_SZ_MASK(rx_desc.rdes1); 401a4dd7a1dSNabih Estefan Diaz rx_buf_addr = rx_desc.rdes3; 402a4dd7a1dSNabih Estefan Diaz gmac->regs[R_NPCM_DMA_CUR_RX_BUF_ADDR] = rx_buf_addr; 403a4dd7a1dSNabih Estefan Diaz gmac_rx_transfer_frame_to_buffer(rx_buf_len, &left_frame, 404a4dd7a1dSNabih Estefan Diaz rx_buf_addr, &eof_transferred, 405a4dd7a1dSNabih Estefan Diaz &frame_ptr, &transferred); 406a4dd7a1dSNabih Estefan Diaz trace_npcm_gmac_packet_receiving_buffer( \ 407a4dd7a1dSNabih Estefan Diaz DEVICE(gmac)->canonical_path, 408a4dd7a1dSNabih Estefan Diaz rx_buf_len, rx_buf_addr); 409a4dd7a1dSNabih Estefan Diaz } 410a4dd7a1dSNabih Estefan Diaz /* update address for descriptor */ 411a4dd7a1dSNabih Estefan Diaz gmac->regs[R_NPCM_DMA_HOST_RX_DESC] = rx_buf_addr; 412a4dd7a1dSNabih Estefan Diaz /* Return descriptor */ 413a4dd7a1dSNabih Estefan Diaz rx_desc.rdes0 &= ~RX_DESC_RDES0_OWN; 414a4dd7a1dSNabih Estefan Diaz /* Update frame length transferred */ 415a4dd7a1dSNabih Estefan Diaz rx_desc.rdes0 |= ((uint32_t)transferred) 416a4dd7a1dSNabih Estefan Diaz << RX_DESC_RDES0_FRAME_LEN_SHIFT; 417a4dd7a1dSNabih Estefan Diaz trace_npcm_gmac_debug_desc_data(DEVICE(gmac)->canonical_path, &rx_desc, 418a4dd7a1dSNabih Estefan Diaz rx_desc.rdes0, rx_desc.rdes1, 419a4dd7a1dSNabih Estefan Diaz rx_desc.rdes2, rx_desc.rdes3); 420a4dd7a1dSNabih Estefan Diaz 421a4dd7a1dSNabih Estefan Diaz /* step 5 */ 422a4dd7a1dSNabih Estefan Diaz gmac_write_rx_desc(desc_addr, &rx_desc); 423a4dd7a1dSNabih Estefan Diaz trace_npcm_gmac_debug_desc_data(DEVICE(gmac)->canonical_path, 424a4dd7a1dSNabih Estefan Diaz &rx_desc, rx_desc.rdes0, 425a4dd7a1dSNabih Estefan Diaz rx_desc.rdes1, rx_desc.rdes2, 426a4dd7a1dSNabih Estefan Diaz rx_desc.rdes3); 427a4dd7a1dSNabih Estefan Diaz /* read new descriptor into rx_desc if needed*/ 428a4dd7a1dSNabih Estefan Diaz if (!eof_transferred) { 429a4dd7a1dSNabih Estefan Diaz /* Get next descriptor address (chained or sequential) */ 430a4dd7a1dSNabih Estefan Diaz if (rx_desc.rdes1 & RX_DESC_RDES1_RC_END_RING_MASK) { 431a4dd7a1dSNabih Estefan Diaz desc_addr = gmac->regs[R_NPCM_DMA_RX_BASE_ADDR]; 432a4dd7a1dSNabih Estefan Diaz } else if (rx_desc.rdes1 & RX_DESC_RDES1_SEC_ADDR_CHND_MASK) { 433a4dd7a1dSNabih Estefan Diaz desc_addr = rx_desc.rdes3; 434a4dd7a1dSNabih Estefan Diaz } else { 435a4dd7a1dSNabih Estefan Diaz desc_addr += sizeof(rx_desc); 436a4dd7a1dSNabih Estefan Diaz } 437a4dd7a1dSNabih Estefan Diaz trace_npcm_gmac_packet_desc_read(DEVICE(gmac)->canonical_path, 438a4dd7a1dSNabih Estefan Diaz desc_addr); 439a4dd7a1dSNabih Estefan Diaz if (gmac_read_rx_desc(desc_addr, &rx_desc)) { 440a4dd7a1dSNabih Estefan Diaz qemu_log_mask(LOG_GUEST_ERROR, 441a4dd7a1dSNabih Estefan Diaz "RX Descriptor @ 0x%x cant be read\n", 442a4dd7a1dSNabih Estefan Diaz desc_addr); 443a4dd7a1dSNabih Estefan Diaz gmac->regs[R_NPCM_DMA_STATUS] |= NPCM_DMA_STATUS_RU; 444a4dd7a1dSNabih Estefan Diaz gmac_update_irq(gmac); 445a4dd7a1dSNabih Estefan Diaz return len; 446a4dd7a1dSNabih Estefan Diaz } 447a4dd7a1dSNabih Estefan Diaz 448a4dd7a1dSNabih Estefan Diaz /* step 6 */ 449a4dd7a1dSNabih Estefan Diaz if (!(rx_desc.rdes0 & RX_DESC_RDES0_OWN)) { 450a4dd7a1dSNabih Estefan Diaz if (!(gmac->regs[R_NPCM_DMA_CONTROL] & \ 451a4dd7a1dSNabih Estefan Diaz NPCM_DMA_CONTROL_FLUSH_MASK)) { 452a4dd7a1dSNabih Estefan Diaz rx_desc.rdes0 |= RX_DESC_RDES0_DESC_ERR_MASK; 453a4dd7a1dSNabih Estefan Diaz } 454a4dd7a1dSNabih Estefan Diaz eof_transferred = true; 455a4dd7a1dSNabih Estefan Diaz } 456a4dd7a1dSNabih Estefan Diaz /* Clear rdes0 for the incoming descriptor */ 457a4dd7a1dSNabih Estefan Diaz rx_desc.rdes0 = 0; 458a4dd7a1dSNabih Estefan Diaz } 459a4dd7a1dSNabih Estefan Diaz } 460a4dd7a1dSNabih Estefan Diaz gmac_dma_set_state(gmac, NPCM_DMA_STATUS_RX_PROCESS_STATE_SHIFT, 461a4dd7a1dSNabih Estefan Diaz NPCM_DMA_STATUS_RX_RUNNING_CLOSING_STATE); 462a4dd7a1dSNabih Estefan Diaz 463a4dd7a1dSNabih Estefan Diaz rx_desc.rdes0 |= RX_DESC_RDES0_LAST_DESC_MASK; 464a4dd7a1dSNabih Estefan Diaz if (!(rx_desc.rdes1 & RX_DESC_RDES1_DIS_INTR_COMP_MASK)) { 465a4dd7a1dSNabih Estefan Diaz gmac->regs[R_NPCM_DMA_STATUS] |= NPCM_DMA_STATUS_RI; 466a4dd7a1dSNabih Estefan Diaz gmac_update_irq(gmac); 467a4dd7a1dSNabih Estefan Diaz } 468a4dd7a1dSNabih Estefan Diaz trace_npcm_gmac_debug_desc_data(DEVICE(gmac)->canonical_path, &rx_desc, 469a4dd7a1dSNabih Estefan Diaz rx_desc.rdes0, rx_desc.rdes1, rx_desc.rdes2, 470a4dd7a1dSNabih Estefan Diaz rx_desc.rdes3); 471a4dd7a1dSNabih Estefan Diaz 472a4dd7a1dSNabih Estefan Diaz /* step 8 */ 473a4dd7a1dSNabih Estefan Diaz gmac->regs[R_NPCM_DMA_CONTROL] |= NPCM_DMA_CONTROL_FLUSH_MASK; 474a4dd7a1dSNabih Estefan Diaz 475a4dd7a1dSNabih Estefan Diaz /* step 9 */ 476a4dd7a1dSNabih Estefan Diaz trace_npcm_gmac_packet_received(DEVICE(gmac)->canonical_path, left_frame); 477a4dd7a1dSNabih Estefan Diaz gmac_dma_set_state(gmac, NPCM_DMA_STATUS_RX_PROCESS_STATE_SHIFT, 478a4dd7a1dSNabih Estefan Diaz NPCM_DMA_STATUS_RX_RUNNING_WAITING_STATE); 479a4dd7a1dSNabih Estefan Diaz gmac_write_rx_desc(desc_addr, &rx_desc); 480a4dd7a1dSNabih Estefan Diaz 481a4dd7a1dSNabih Estefan Diaz /* Get next descriptor address (chained or sequential) */ 482a4dd7a1dSNabih Estefan Diaz if (rx_desc.rdes1 & RX_DESC_RDES1_RC_END_RING_MASK) { 483a4dd7a1dSNabih Estefan Diaz desc_addr = gmac->regs[R_NPCM_DMA_RX_BASE_ADDR]; 484a4dd7a1dSNabih Estefan Diaz } else if (rx_desc.rdes1 & RX_DESC_RDES1_SEC_ADDR_CHND_MASK) { 485a4dd7a1dSNabih Estefan Diaz desc_addr = rx_desc.rdes3; 486a4dd7a1dSNabih Estefan Diaz } else { 487a4dd7a1dSNabih Estefan Diaz desc_addr += sizeof(rx_desc); 488a4dd7a1dSNabih Estefan Diaz } 489a4dd7a1dSNabih Estefan Diaz gmac->regs[R_NPCM_DMA_HOST_RX_DESC] = desc_addr; 490a4dd7a1dSNabih Estefan Diaz return len; 49108f787a3SHao Wu } 49208f787a3SHao Wu 4931c51c571SNabih Estefan Diaz static int gmac_tx_get_csum(uint32_t tdes1) 4941c51c571SNabih Estefan Diaz { 4951c51c571SNabih Estefan Diaz uint32_t mask = TX_DESC_TDES1_CHKSM_INS_CTRL_MASK(tdes1); 4961c51c571SNabih Estefan Diaz int csum = 0; 4971c51c571SNabih Estefan Diaz 4981c51c571SNabih Estefan Diaz if (likely(mask > 0)) { 4991c51c571SNabih Estefan Diaz csum |= CSUM_IP; 5001c51c571SNabih Estefan Diaz } 5011c51c571SNabih Estefan Diaz if (likely(mask > 1)) { 5021c51c571SNabih Estefan Diaz csum |= CSUM_TCP | CSUM_UDP; 5031c51c571SNabih Estefan Diaz } 5041c51c571SNabih Estefan Diaz 5051c51c571SNabih Estefan Diaz return csum; 5061c51c571SNabih Estefan Diaz } 5071c51c571SNabih Estefan Diaz 5081c51c571SNabih Estefan Diaz static void gmac_try_send_next_packet(NPCMGMACState *gmac) 5091c51c571SNabih Estefan Diaz { 5101c51c571SNabih Estefan Diaz /* 5111c51c571SNabih Estefan Diaz * Comments about steps refer to steps for 5121c51c571SNabih Estefan Diaz * transmitting in page 384 of datasheet 5131c51c571SNabih Estefan Diaz */ 5141c51c571SNabih Estefan Diaz uint16_t tx_buffer_size = 2048; 5151c51c571SNabih Estefan Diaz g_autofree uint8_t *tx_send_buffer = g_malloc(tx_buffer_size); 5161c51c571SNabih Estefan Diaz uint32_t desc_addr; 5171c51c571SNabih Estefan Diaz struct NPCMGMACTxDesc tx_desc; 5181c51c571SNabih Estefan Diaz uint32_t tx_buf_addr, tx_buf_len; 5191c51c571SNabih Estefan Diaz uint16_t length = 0; 5201c51c571SNabih Estefan Diaz uint8_t *buf = tx_send_buffer; 5211c51c571SNabih Estefan Diaz uint32_t prev_buf_size = 0; 5221c51c571SNabih Estefan Diaz int csum = 0; 5231c51c571SNabih Estefan Diaz 5241c51c571SNabih Estefan Diaz /* steps 1&2 */ 5251c51c571SNabih Estefan Diaz if (!gmac->regs[R_NPCM_DMA_HOST_TX_DESC]) { 5261c51c571SNabih Estefan Diaz gmac->regs[R_NPCM_DMA_HOST_TX_DESC] = 5271c51c571SNabih Estefan Diaz NPCM_DMA_HOST_TX_DESC_MASK(gmac->regs[R_NPCM_DMA_TX_BASE_ADDR]); 5281c51c571SNabih Estefan Diaz } 5291c51c571SNabih Estefan Diaz desc_addr = gmac->regs[R_NPCM_DMA_HOST_TX_DESC]; 5301c51c571SNabih Estefan Diaz 5311c51c571SNabih Estefan Diaz while (true) { 5321c51c571SNabih Estefan Diaz gmac_dma_set_state(gmac, NPCM_DMA_STATUS_TX_PROCESS_STATE_SHIFT, 5331c51c571SNabih Estefan Diaz NPCM_DMA_STATUS_TX_RUNNING_FETCHING_STATE); 5341c51c571SNabih Estefan Diaz if (gmac_read_tx_desc(desc_addr, &tx_desc)) { 5351c51c571SNabih Estefan Diaz qemu_log_mask(LOG_GUEST_ERROR, 5361c51c571SNabih Estefan Diaz "TX Descriptor @ 0x%x can't be read\n", 5371c51c571SNabih Estefan Diaz desc_addr); 5381c51c571SNabih Estefan Diaz return; 5391c51c571SNabih Estefan Diaz } 5401c51c571SNabih Estefan Diaz /* step 3 */ 5411c51c571SNabih Estefan Diaz 5421c51c571SNabih Estefan Diaz trace_npcm_gmac_packet_desc_read(DEVICE(gmac)->canonical_path, 5431c51c571SNabih Estefan Diaz desc_addr); 5441c51c571SNabih Estefan Diaz trace_npcm_gmac_debug_desc_data(DEVICE(gmac)->canonical_path, &tx_desc, 5451c51c571SNabih Estefan Diaz tx_desc.tdes0, tx_desc.tdes1, tx_desc.tdes2, tx_desc.tdes3); 5461c51c571SNabih Estefan Diaz 5471c51c571SNabih Estefan Diaz /* 1 = DMA Owned, 0 = Software Owned */ 5481c51c571SNabih Estefan Diaz if (!(tx_desc.tdes0 & TX_DESC_TDES0_OWN)) { 549ab4b56d9SNabih Estefan trace_npcm_gmac_tx_desc_owner(DEVICE(gmac)->canonical_path, 5501c51c571SNabih Estefan Diaz desc_addr); 5511c51c571SNabih Estefan Diaz gmac->regs[R_NPCM_DMA_STATUS] |= NPCM_DMA_STATUS_TU; 5521c51c571SNabih Estefan Diaz gmac_dma_set_state(gmac, NPCM_DMA_STATUS_TX_PROCESS_STATE_SHIFT, 5531c51c571SNabih Estefan Diaz NPCM_DMA_STATUS_TX_SUSPENDED_STATE); 5541c51c571SNabih Estefan Diaz gmac_update_irq(gmac); 5551c51c571SNabih Estefan Diaz return; 5561c51c571SNabih Estefan Diaz } 5571c51c571SNabih Estefan Diaz 5581c51c571SNabih Estefan Diaz gmac_dma_set_state(gmac, NPCM_DMA_STATUS_TX_PROCESS_STATE_SHIFT, 5591c51c571SNabih Estefan Diaz NPCM_DMA_STATUS_TX_RUNNING_READ_STATE); 5601c51c571SNabih Estefan Diaz /* Give the descriptor back regardless of what happens. */ 5611c51c571SNabih Estefan Diaz tx_desc.tdes0 &= ~TX_DESC_TDES0_OWN; 5621c51c571SNabih Estefan Diaz 5631c51c571SNabih Estefan Diaz if (tx_desc.tdes1 & TX_DESC_TDES1_FIRST_SEG_MASK) { 5641c51c571SNabih Estefan Diaz csum = gmac_tx_get_csum(tx_desc.tdes1); 5651c51c571SNabih Estefan Diaz } 5661c51c571SNabih Estefan Diaz 5671c51c571SNabih Estefan Diaz /* step 4 */ 5681c51c571SNabih Estefan Diaz tx_buf_addr = tx_desc.tdes2; 5691c51c571SNabih Estefan Diaz gmac->regs[R_NPCM_DMA_CUR_TX_BUF_ADDR] = tx_buf_addr; 5701c51c571SNabih Estefan Diaz tx_buf_len = TX_DESC_TDES1_BFFR1_SZ_MASK(tx_desc.tdes1); 5711c51c571SNabih Estefan Diaz buf = &tx_send_buffer[prev_buf_size]; 5721c51c571SNabih Estefan Diaz 5731c51c571SNabih Estefan Diaz if ((prev_buf_size + tx_buf_len) > sizeof(buf)) { 5741c51c571SNabih Estefan Diaz tx_buffer_size = prev_buf_size + tx_buf_len; 5751c51c571SNabih Estefan Diaz tx_send_buffer = g_realloc(tx_send_buffer, tx_buffer_size); 5761c51c571SNabih Estefan Diaz buf = &tx_send_buffer[prev_buf_size]; 5771c51c571SNabih Estefan Diaz } 5781c51c571SNabih Estefan Diaz 5791c51c571SNabih Estefan Diaz /* step 5 */ 5801c51c571SNabih Estefan Diaz if (dma_memory_read(&address_space_memory, tx_buf_addr, buf, 5811c51c571SNabih Estefan Diaz tx_buf_len, MEMTXATTRS_UNSPECIFIED)) { 5821c51c571SNabih Estefan Diaz qemu_log_mask(LOG_GUEST_ERROR, "%s: Failed to read packet @ 0x%x\n", 5831c51c571SNabih Estefan Diaz __func__, tx_buf_addr); 5841c51c571SNabih Estefan Diaz return; 5851c51c571SNabih Estefan Diaz } 5861c51c571SNabih Estefan Diaz length += tx_buf_len; 5871c51c571SNabih Estefan Diaz prev_buf_size += tx_buf_len; 5881c51c571SNabih Estefan Diaz 5891c51c571SNabih Estefan Diaz /* If not chained we'll have a second buffer. */ 5901c51c571SNabih Estefan Diaz if (!(tx_desc.tdes1 & TX_DESC_TDES1_SEC_ADDR_CHND_MASK)) { 5911c51c571SNabih Estefan Diaz tx_buf_addr = tx_desc.tdes3; 5921c51c571SNabih Estefan Diaz gmac->regs[R_NPCM_DMA_CUR_TX_BUF_ADDR] = tx_buf_addr; 5931c51c571SNabih Estefan Diaz tx_buf_len = TX_DESC_TDES1_BFFR2_SZ_MASK(tx_desc.tdes1); 5941c51c571SNabih Estefan Diaz buf = &tx_send_buffer[prev_buf_size]; 5951c51c571SNabih Estefan Diaz 5961c51c571SNabih Estefan Diaz if ((prev_buf_size + tx_buf_len) > sizeof(buf)) { 5971c51c571SNabih Estefan Diaz tx_buffer_size = prev_buf_size + tx_buf_len; 5981c51c571SNabih Estefan Diaz tx_send_buffer = g_realloc(tx_send_buffer, tx_buffer_size); 5991c51c571SNabih Estefan Diaz buf = &tx_send_buffer[prev_buf_size]; 6001c51c571SNabih Estefan Diaz } 6011c51c571SNabih Estefan Diaz 6021c51c571SNabih Estefan Diaz if (dma_memory_read(&address_space_memory, tx_buf_addr, buf, 6031c51c571SNabih Estefan Diaz tx_buf_len, MEMTXATTRS_UNSPECIFIED)) { 6041c51c571SNabih Estefan Diaz qemu_log_mask(LOG_GUEST_ERROR, 6051c51c571SNabih Estefan Diaz "%s: Failed to read packet @ 0x%x\n", 6061c51c571SNabih Estefan Diaz __func__, tx_buf_addr); 6071c51c571SNabih Estefan Diaz return; 6081c51c571SNabih Estefan Diaz } 6091c51c571SNabih Estefan Diaz length += tx_buf_len; 6101c51c571SNabih Estefan Diaz prev_buf_size += tx_buf_len; 6111c51c571SNabih Estefan Diaz } 6121c51c571SNabih Estefan Diaz if (tx_desc.tdes1 & TX_DESC_TDES1_LAST_SEG_MASK) { 6131c51c571SNabih Estefan Diaz net_checksum_calculate(tx_send_buffer, length, csum); 6141c51c571SNabih Estefan Diaz qemu_send_packet(qemu_get_queue(gmac->nic), tx_send_buffer, length); 6151c51c571SNabih Estefan Diaz trace_npcm_gmac_packet_sent(DEVICE(gmac)->canonical_path, length); 6161c51c571SNabih Estefan Diaz buf = tx_send_buffer; 6171c51c571SNabih Estefan Diaz length = 0; 6181c51c571SNabih Estefan Diaz } 6191c51c571SNabih Estefan Diaz 6201c51c571SNabih Estefan Diaz /* step 6 */ 6211c51c571SNabih Estefan Diaz gmac_dma_set_state(gmac, NPCM_DMA_STATUS_TX_PROCESS_STATE_SHIFT, 6221c51c571SNabih Estefan Diaz NPCM_DMA_STATUS_TX_RUNNING_CLOSING_STATE); 6231c51c571SNabih Estefan Diaz gmac_write_tx_desc(desc_addr, &tx_desc); 6241c51c571SNabih Estefan Diaz if (tx_desc.tdes1 & TX_DESC_TDES1_TX_END_RING_MASK) { 6251c51c571SNabih Estefan Diaz desc_addr = gmac->regs[R_NPCM_DMA_TX_BASE_ADDR]; 6261c51c571SNabih Estefan Diaz } else if (tx_desc.tdes1 & TX_DESC_TDES1_SEC_ADDR_CHND_MASK) { 6271c51c571SNabih Estefan Diaz desc_addr = tx_desc.tdes3; 6281c51c571SNabih Estefan Diaz } else { 6291c51c571SNabih Estefan Diaz desc_addr += sizeof(tx_desc); 6301c51c571SNabih Estefan Diaz } 6311c51c571SNabih Estefan Diaz gmac->regs[R_NPCM_DMA_HOST_TX_DESC] = desc_addr; 6321c51c571SNabih Estefan Diaz 6331c51c571SNabih Estefan Diaz /* step 7 */ 6341c51c571SNabih Estefan Diaz if (tx_desc.tdes1 & TX_DESC_TDES1_INTERR_COMP_MASK) { 6351c51c571SNabih Estefan Diaz gmac->regs[R_NPCM_DMA_STATUS] |= NPCM_DMA_STATUS_TI; 6361c51c571SNabih Estefan Diaz gmac_update_irq(gmac); 6371c51c571SNabih Estefan Diaz } 6381c51c571SNabih Estefan Diaz } 6391c51c571SNabih Estefan Diaz } 6401c51c571SNabih Estefan Diaz 64108f787a3SHao Wu static void gmac_cleanup(NetClientState *nc) 64208f787a3SHao Wu { 64308f787a3SHao Wu /* Nothing to do yet. */ 64408f787a3SHao Wu } 64508f787a3SHao Wu 64608f787a3SHao Wu static void gmac_set_link(NetClientState *nc) 64708f787a3SHao Wu { 64808f787a3SHao Wu NPCMGMACState *gmac = qemu_get_nic_opaque(nc); 64908f787a3SHao Wu 65008f787a3SHao Wu trace_npcm_gmac_set_link(!nc->link_down); 65108f787a3SHao Wu gmac_phy_set_link(gmac, !nc->link_down); 65208f787a3SHao Wu } 65308f787a3SHao Wu 65408f787a3SHao Wu static void npcm_gmac_mdio_access(NPCMGMACState *gmac, uint16_t v) 65508f787a3SHao Wu { 65608f787a3SHao Wu bool busy = v & NPCM_GMAC_MII_ADDR_BUSY; 65708f787a3SHao Wu uint8_t is_write; 65808f787a3SHao Wu uint8_t pa, gr; 65908f787a3SHao Wu uint16_t data; 66008f787a3SHao Wu 66108f787a3SHao Wu if (busy) { 66208f787a3SHao Wu is_write = v & NPCM_GMAC_MII_ADDR_WRITE; 66308f787a3SHao Wu pa = NPCM_GMAC_MII_ADDR_PA(v); 66408f787a3SHao Wu gr = NPCM_GMAC_MII_ADDR_GR(v); 66508f787a3SHao Wu /* Both pa and gr are 5 bits, so they are less than 32. */ 66608f787a3SHao Wu g_assert(pa < NPCM_GMAC_MAX_PHYS); 66708f787a3SHao Wu g_assert(gr < NPCM_GMAC_MAX_PHY_REGS); 66808f787a3SHao Wu 66908f787a3SHao Wu 67008f787a3SHao Wu if (v & NPCM_GMAC_MII_ADDR_WRITE) { 67108f787a3SHao Wu data = gmac->regs[R_NPCM_GMAC_MII_DATA]; 67208f787a3SHao Wu /* Clear reset bit for BMCR register */ 67308f787a3SHao Wu switch (gr) { 67408f787a3SHao Wu case MII_BMCR: 67508f787a3SHao Wu data &= ~MII_BMCR_RESET; 67608f787a3SHao Wu /* Autonegotiation is a W1C bit*/ 67708f787a3SHao Wu if (data & MII_BMCR_ANRESTART) { 67808f787a3SHao Wu /* Tells autonegotiation to not restart again */ 67908f787a3SHao Wu data &= ~MII_BMCR_ANRESTART; 68008f787a3SHao Wu } 68108f787a3SHao Wu if ((data & MII_BMCR_AUTOEN) && 68208f787a3SHao Wu !(gmac->phy_regs[pa][MII_BMSR] & MII_BMSR_AN_COMP)) { 68308f787a3SHao Wu /* sets autonegotiation as complete */ 68408f787a3SHao Wu gmac->phy_regs[pa][MII_BMSR] |= MII_BMSR_AN_COMP; 68508f787a3SHao Wu /* Resolve AN automatically->need to set this */ 68608f787a3SHao Wu gmac->phy_regs[0][MII_ANLPAR] = 0x0000; 68708f787a3SHao Wu } 68808f787a3SHao Wu } 68908f787a3SHao Wu gmac->phy_regs[pa][gr] = data; 69008f787a3SHao Wu } else { 69108f787a3SHao Wu data = gmac->phy_regs[pa][gr]; 69208f787a3SHao Wu gmac->regs[R_NPCM_GMAC_MII_DATA] = data; 69308f787a3SHao Wu } 69408f787a3SHao Wu trace_npcm_gmac_mdio_access(DEVICE(gmac)->canonical_path, is_write, pa, 69508f787a3SHao Wu gr, data); 69608f787a3SHao Wu } 69708f787a3SHao Wu gmac->regs[R_NPCM_GMAC_MII_ADDR] = v & ~NPCM_GMAC_MII_ADDR_BUSY; 69808f787a3SHao Wu } 69908f787a3SHao Wu 70008f787a3SHao Wu static uint64_t npcm_gmac_read(void *opaque, hwaddr offset, unsigned size) 70108f787a3SHao Wu { 70208f787a3SHao Wu NPCMGMACState *gmac = opaque; 70308f787a3SHao Wu uint32_t v = 0; 70408f787a3SHao Wu 70508f787a3SHao Wu switch (offset) { 70608f787a3SHao Wu /* Write only registers */ 70708f787a3SHao Wu case A_NPCM_DMA_XMT_POLL_DEMAND: 70808f787a3SHao Wu case A_NPCM_DMA_RCV_POLL_DEMAND: 70908f787a3SHao Wu qemu_log_mask(LOG_GUEST_ERROR, 71008f787a3SHao Wu "%s: Read of write-only reg: offset: 0x%04" HWADDR_PRIx 71108f787a3SHao Wu "\n", DEVICE(gmac)->canonical_path, offset); 71208f787a3SHao Wu break; 71308f787a3SHao Wu 71408f787a3SHao Wu default: 71508f787a3SHao Wu v = gmac->regs[offset / sizeof(uint32_t)]; 71608f787a3SHao Wu } 71708f787a3SHao Wu 71808f787a3SHao Wu trace_npcm_gmac_reg_read(DEVICE(gmac)->canonical_path, offset, v); 71908f787a3SHao Wu return v; 72008f787a3SHao Wu } 72108f787a3SHao Wu 72208f787a3SHao Wu static void npcm_gmac_write(void *opaque, hwaddr offset, 72308f787a3SHao Wu uint64_t v, unsigned size) 72408f787a3SHao Wu { 72508f787a3SHao Wu NPCMGMACState *gmac = opaque; 72608f787a3SHao Wu 72708f787a3SHao Wu trace_npcm_gmac_reg_write(DEVICE(gmac)->canonical_path, offset, v); 72808f787a3SHao Wu 72908f787a3SHao Wu switch (offset) { 73008f787a3SHao Wu /* Read only registers */ 73108f787a3SHao Wu case A_NPCM_GMAC_VERSION: 73208f787a3SHao Wu case A_NPCM_GMAC_INT_STATUS: 73308f787a3SHao Wu case A_NPCM_GMAC_RGMII_STATUS: 73408f787a3SHao Wu case A_NPCM_GMAC_PTP_STSR: 73508f787a3SHao Wu case A_NPCM_GMAC_PTP_STNSR: 73608f787a3SHao Wu case A_NPCM_DMA_MISSED_FRAME_CTR: 73708f787a3SHao Wu case A_NPCM_DMA_HOST_TX_DESC: 73808f787a3SHao Wu case A_NPCM_DMA_HOST_RX_DESC: 73908f787a3SHao Wu case A_NPCM_DMA_CUR_TX_BUF_ADDR: 74008f787a3SHao Wu case A_NPCM_DMA_CUR_RX_BUF_ADDR: 74108f787a3SHao Wu case A_NPCM_DMA_HW_FEATURE: 74208f787a3SHao Wu qemu_log_mask(LOG_GUEST_ERROR, 74308f787a3SHao Wu "%s: Write of read-only reg: offset: 0x%04" HWADDR_PRIx 74408f787a3SHao Wu ", value: 0x%04" PRIx64 "\n", 74508f787a3SHao Wu DEVICE(gmac)->canonical_path, offset, v); 74608f787a3SHao Wu break; 74708f787a3SHao Wu 74808f787a3SHao Wu case A_NPCM_GMAC_MAC_CONFIG: 749a4dd7a1dSNabih Estefan Diaz gmac->regs[offset / sizeof(uint32_t)] = v; 75008f787a3SHao Wu break; 75108f787a3SHao Wu 75208f787a3SHao Wu case A_NPCM_GMAC_MII_ADDR: 75308f787a3SHao Wu npcm_gmac_mdio_access(gmac, v); 75408f787a3SHao Wu break; 75508f787a3SHao Wu 75608f787a3SHao Wu case A_NPCM_GMAC_MAC0_ADDR_HI: 75708f787a3SHao Wu gmac->regs[offset / sizeof(uint32_t)] = v; 75808f787a3SHao Wu gmac->conf.macaddr.a[0] = v >> 8; 75908f787a3SHao Wu gmac->conf.macaddr.a[1] = v >> 0; 76008f787a3SHao Wu break; 76108f787a3SHao Wu 76208f787a3SHao Wu case A_NPCM_GMAC_MAC0_ADDR_LO: 76308f787a3SHao Wu gmac->regs[offset / sizeof(uint32_t)] = v; 76408f787a3SHao Wu gmac->conf.macaddr.a[2] = v >> 24; 76508f787a3SHao Wu gmac->conf.macaddr.a[3] = v >> 16; 76608f787a3SHao Wu gmac->conf.macaddr.a[4] = v >> 8; 76708f787a3SHao Wu gmac->conf.macaddr.a[5] = v >> 0; 76808f787a3SHao Wu break; 76908f787a3SHao Wu 77008f787a3SHao Wu case A_NPCM_GMAC_MAC1_ADDR_HI: 77108f787a3SHao Wu case A_NPCM_GMAC_MAC1_ADDR_LO: 77208f787a3SHao Wu case A_NPCM_GMAC_MAC2_ADDR_HI: 77308f787a3SHao Wu case A_NPCM_GMAC_MAC2_ADDR_LO: 77408f787a3SHao Wu case A_NPCM_GMAC_MAC3_ADDR_HI: 77508f787a3SHao Wu case A_NPCM_GMAC_MAC3_ADDR_LO: 77608f787a3SHao Wu gmac->regs[offset / sizeof(uint32_t)] = v; 77708f787a3SHao Wu qemu_log_mask(LOG_UNIMP, 77808f787a3SHao Wu "%s: Only MAC Address 0 is supported. This request " 77908f787a3SHao Wu "is ignored.\n", DEVICE(gmac)->canonical_path); 78008f787a3SHao Wu break; 78108f787a3SHao Wu 78208f787a3SHao Wu case A_NPCM_DMA_BUS_MODE: 78308f787a3SHao Wu gmac->regs[offset / sizeof(uint32_t)] = v; 78408f787a3SHao Wu if (v & NPCM_DMA_BUS_MODE_SWR) { 78508f787a3SHao Wu npcm_gmac_soft_reset(gmac); 78608f787a3SHao Wu } 78708f787a3SHao Wu break; 78808f787a3SHao Wu 78908f787a3SHao Wu case A_NPCM_DMA_RCV_POLL_DEMAND: 79008f787a3SHao Wu /* We dont actually care about the value */ 791a4dd7a1dSNabih Estefan Diaz gmac_dma_set_state(gmac, NPCM_DMA_STATUS_RX_PROCESS_STATE_SHIFT, 792a4dd7a1dSNabih Estefan Diaz NPCM_DMA_STATUS_RX_RUNNING_WAITING_STATE); 79308f787a3SHao Wu break; 79408f787a3SHao Wu 7951c51c571SNabih Estefan Diaz case A_NPCM_DMA_XMT_POLL_DEMAND: 7961c51c571SNabih Estefan Diaz /* We dont actually care about the value */ 7971c51c571SNabih Estefan Diaz gmac_try_send_next_packet(gmac); 7981c51c571SNabih Estefan Diaz break; 7991c51c571SNabih Estefan Diaz 8001c51c571SNabih Estefan Diaz case A_NPCM_DMA_CONTROL: 8011c51c571SNabih Estefan Diaz gmac->regs[offset / sizeof(uint32_t)] = v; 8021c51c571SNabih Estefan Diaz if (v & NPCM_DMA_CONTROL_START_STOP_TX) { 8031c51c571SNabih Estefan Diaz gmac_try_send_next_packet(gmac); 8041c51c571SNabih Estefan Diaz } else { 8051c51c571SNabih Estefan Diaz gmac_dma_set_state(gmac, NPCM_DMA_STATUS_TX_PROCESS_STATE_SHIFT, 8061c51c571SNabih Estefan Diaz NPCM_DMA_STATUS_TX_STOPPED_STATE); 8071c51c571SNabih Estefan Diaz } 8081c51c571SNabih Estefan Diaz if (v & NPCM_DMA_CONTROL_START_STOP_RX) { 8091c51c571SNabih Estefan Diaz gmac_dma_set_state(gmac, NPCM_DMA_STATUS_RX_PROCESS_STATE_SHIFT, 8101c51c571SNabih Estefan Diaz NPCM_DMA_STATUS_RX_RUNNING_WAITING_STATE); 8111c51c571SNabih Estefan Diaz qemu_flush_queued_packets(qemu_get_queue(gmac->nic)); 8121c51c571SNabih Estefan Diaz } else { 8131c51c571SNabih Estefan Diaz gmac_dma_set_state(gmac, NPCM_DMA_STATUS_RX_PROCESS_STATE_SHIFT, 8141c51c571SNabih Estefan Diaz NPCM_DMA_STATUS_RX_STOPPED_STATE); 8151c51c571SNabih Estefan Diaz } 8161c51c571SNabih Estefan Diaz break; 8171c51c571SNabih Estefan Diaz 81808f787a3SHao Wu case A_NPCM_DMA_STATUS: 81908f787a3SHao Wu /* Check that RO bits are not written to */ 82008f787a3SHao Wu if (NPCM_DMA_STATUS_RO_MASK(v)) { 82108f787a3SHao Wu qemu_log_mask(LOG_GUEST_ERROR, 82208f787a3SHao Wu "%s: Write of read-only bits of reg: offset: 0x%04" 82308f787a3SHao Wu HWADDR_PRIx ", value: 0x%04" PRIx64 "\n", 82408f787a3SHao Wu DEVICE(gmac)->canonical_path, offset, v); 82508f787a3SHao Wu } 826a4dd7a1dSNabih Estefan Diaz /* for W1C bits, implement W1C */ 827a4dd7a1dSNabih Estefan Diaz gmac->regs[offset / sizeof(uint32_t)] &= ~NPCM_DMA_STATUS_W1C_MASK(v); 828a4dd7a1dSNabih Estefan Diaz if (v & NPCM_DMA_STATUS_RU) { 829a4dd7a1dSNabih Estefan Diaz /* Clearing RU bit indicates descriptor is owned by DMA again. */ 830a4dd7a1dSNabih Estefan Diaz gmac_dma_set_state(gmac, NPCM_DMA_STATUS_RX_PROCESS_STATE_SHIFT, 831a4dd7a1dSNabih Estefan Diaz NPCM_DMA_STATUS_RX_RUNNING_WAITING_STATE); 832a4dd7a1dSNabih Estefan Diaz qemu_flush_queued_packets(qemu_get_queue(gmac->nic)); 833a4dd7a1dSNabih Estefan Diaz } 83408f787a3SHao Wu break; 83508f787a3SHao Wu 83608f787a3SHao Wu default: 83708f787a3SHao Wu gmac->regs[offset / sizeof(uint32_t)] = v; 83808f787a3SHao Wu break; 83908f787a3SHao Wu } 84008f787a3SHao Wu 84108f787a3SHao Wu gmac_update_irq(gmac); 84208f787a3SHao Wu } 84308f787a3SHao Wu 84408f787a3SHao Wu static void npcm_gmac_reset(DeviceState *dev) 84508f787a3SHao Wu { 84608f787a3SHao Wu NPCMGMACState *gmac = NPCM_GMAC(dev); 84708f787a3SHao Wu 84808f787a3SHao Wu npcm_gmac_soft_reset(gmac); 84908f787a3SHao Wu memcpy(gmac->phy_regs[0], phy_reg_init, sizeof(phy_reg_init)); 85008f787a3SHao Wu 85108f787a3SHao Wu trace_npcm_gmac_reset(DEVICE(gmac)->canonical_path, 85208f787a3SHao Wu gmac->phy_regs[0][MII_BMSR]); 85308f787a3SHao Wu } 85408f787a3SHao Wu 85508f787a3SHao Wu static NetClientInfo net_npcm_gmac_info = { 85608f787a3SHao Wu .type = NET_CLIENT_DRIVER_NIC, 85708f787a3SHao Wu .size = sizeof(NICState), 85808f787a3SHao Wu .can_receive = gmac_can_receive, 85908f787a3SHao Wu .receive = gmac_receive, 86008f787a3SHao Wu .cleanup = gmac_cleanup, 86108f787a3SHao Wu .link_status_changed = gmac_set_link, 86208f787a3SHao Wu }; 86308f787a3SHao Wu 86408f787a3SHao Wu static const struct MemoryRegionOps npcm_gmac_ops = { 86508f787a3SHao Wu .read = npcm_gmac_read, 86608f787a3SHao Wu .write = npcm_gmac_write, 86708f787a3SHao Wu .endianness = DEVICE_LITTLE_ENDIAN, 86808f787a3SHao Wu .valid = { 86908f787a3SHao Wu .min_access_size = 4, 87008f787a3SHao Wu .max_access_size = 4, 87108f787a3SHao Wu .unaligned = false, 87208f787a3SHao Wu }, 87308f787a3SHao Wu }; 87408f787a3SHao Wu 87508f787a3SHao Wu static void npcm_gmac_realize(DeviceState *dev, Error **errp) 87608f787a3SHao Wu { 87708f787a3SHao Wu NPCMGMACState *gmac = NPCM_GMAC(dev); 87808f787a3SHao Wu SysBusDevice *sbd = SYS_BUS_DEVICE(dev); 87908f787a3SHao Wu 88008f787a3SHao Wu memory_region_init_io(&gmac->iomem, OBJECT(gmac), &npcm_gmac_ops, gmac, 88108f787a3SHao Wu TYPE_NPCM_GMAC, 8 * KiB); 88208f787a3SHao Wu sysbus_init_mmio(sbd, &gmac->iomem); 88308f787a3SHao Wu sysbus_init_irq(sbd, &gmac->irq); 88408f787a3SHao Wu 88508f787a3SHao Wu qemu_macaddr_default_if_unset(&gmac->conf.macaddr); 88608f787a3SHao Wu 88708f787a3SHao Wu gmac->nic = qemu_new_nic(&net_npcm_gmac_info, &gmac->conf, TYPE_NPCM_GMAC, 88808f787a3SHao Wu dev->id, &dev->mem_reentrancy_guard, gmac); 88908f787a3SHao Wu qemu_format_nic_info_str(qemu_get_queue(gmac->nic), gmac->conf.macaddr.a); 89008f787a3SHao Wu gmac->regs[R_NPCM_GMAC_MAC0_ADDR_HI] = (gmac->conf.macaddr.a[0] << 8) + \ 89108f787a3SHao Wu gmac->conf.macaddr.a[1]; 89208f787a3SHao Wu gmac->regs[R_NPCM_GMAC_MAC0_ADDR_LO] = (gmac->conf.macaddr.a[2] << 24) + \ 89308f787a3SHao Wu (gmac->conf.macaddr.a[3] << 16) + \ 89408f787a3SHao Wu (gmac->conf.macaddr.a[4] << 8) + \ 89508f787a3SHao Wu gmac->conf.macaddr.a[5]; 89608f787a3SHao Wu } 89708f787a3SHao Wu 89808f787a3SHao Wu static void npcm_gmac_unrealize(DeviceState *dev) 89908f787a3SHao Wu { 90008f787a3SHao Wu NPCMGMACState *gmac = NPCM_GMAC(dev); 90108f787a3SHao Wu 90208f787a3SHao Wu qemu_del_nic(gmac->nic); 90308f787a3SHao Wu } 90408f787a3SHao Wu 90508f787a3SHao Wu static const VMStateDescription vmstate_npcm_gmac = { 90608f787a3SHao Wu .name = TYPE_NPCM_GMAC, 90708f787a3SHao Wu .version_id = 0, 90808f787a3SHao Wu .minimum_version_id = 0, 90908f787a3SHao Wu .fields = (VMStateField[]) { 91008f787a3SHao Wu VMSTATE_UINT32_ARRAY(regs, NPCMGMACState, NPCM_GMAC_NR_REGS), 91108f787a3SHao Wu VMSTATE_END_OF_LIST(), 91208f787a3SHao Wu }, 91308f787a3SHao Wu }; 91408f787a3SHao Wu 915*e732f00fSRichard Henderson static const Property npcm_gmac_properties[] = { 91608f787a3SHao Wu DEFINE_NIC_PROPERTIES(NPCMGMACState, conf), 91708f787a3SHao Wu DEFINE_PROP_END_OF_LIST(), 91808f787a3SHao Wu }; 91908f787a3SHao Wu 92008f787a3SHao Wu static void npcm_gmac_class_init(ObjectClass *klass, void *data) 92108f787a3SHao Wu { 92208f787a3SHao Wu DeviceClass *dc = DEVICE_CLASS(klass); 92308f787a3SHao Wu 92408f787a3SHao Wu set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); 92508f787a3SHao Wu dc->desc = "NPCM GMAC Controller"; 92608f787a3SHao Wu dc->realize = npcm_gmac_realize; 92708f787a3SHao Wu dc->unrealize = npcm_gmac_unrealize; 928e3d08143SPeter Maydell device_class_set_legacy_reset(dc, npcm_gmac_reset); 92908f787a3SHao Wu dc->vmsd = &vmstate_npcm_gmac; 93008f787a3SHao Wu device_class_set_props(dc, npcm_gmac_properties); 93108f787a3SHao Wu } 93208f787a3SHao Wu 93308f787a3SHao Wu static const TypeInfo npcm_gmac_types[] = { 93408f787a3SHao Wu { 93508f787a3SHao Wu .name = TYPE_NPCM_GMAC, 93608f787a3SHao Wu .parent = TYPE_SYS_BUS_DEVICE, 93708f787a3SHao Wu .instance_size = sizeof(NPCMGMACState), 93808f787a3SHao Wu .class_init = npcm_gmac_class_init, 93908f787a3SHao Wu }, 94008f787a3SHao Wu }; 94108f787a3SHao Wu DEFINE_TYPES(npcm_gmac_types) 942