#include #include #include #if __has_include() #include #else #include #endif #include #include #if LINUX_VERSION_CODE < KERNEL_VERSION(4,4,46) #include #else #include #endif #include "ag934x.h" #include "atheros_mac.h" #define MAX_PACKET_SIZE 1500 // Ring sizes should be power of two #define RX_RING_SIZE 2 #define TX_RING_SIZE 4 // #define ENABLE_RX 1 #if defined(CONFIG_SOC_QCA956X) || defined(CONFIG_SOC_AR934X) #define USE_SWITCH 1 #else #define USE_SWITCH 0 #endif enum { FLAG_SGMII = (1 << 0), FLAG_RX_TX_DELAY = (1 << 1), FLAG_SWITCH = (1 << 2), }; struct gmac_desc { uint32_t pkt_start_addr; athr_mac_packet packet; uint32_t next_desc; void* data; }; struct gmac_ring { struct gmac_desc* descs; dma_addr_t descs_dma; void* packet_data; dma_addr_t packet_data_dma; unsigned int head; }; struct hw { unsigned int hw_id; unsigned int flags; unsigned int ports; }; struct avm_coredump_net_context { struct hw hw; unsigned int BaseAddress; unsigned int BaseAddressMdio; struct gmac_ring mac_txring; #if defined(ENABLE_RX) struct gmac_ring mac_rxring; #endif }; static int ring_allocate(struct gmac_ring* r, unsigned int nelem) { unsigned int i; // Allocate ring descriptors r->descs = dma_alloc_coherent(NULL, sizeof(struct gmac_desc) * nelem, &r->descs_dma, GFP_DMA); if (!r->descs) { printk(KERN_ERR "Could not allocated coherent descs\n"); return -ENOMEM; } memset(r->descs, 0, sizeof(struct gmac_desc) * nelem); // Allocate packet buffers r->packet_data = dma_alloc_coherent(NULL, MAX_PACKET_SIZE * nelem, &r->packet_data_dma, GFP_DMA); if (!r->packet_data) { printk(KERN_ERR "Could not allocated coherent descs\n"); return -ENOMEM; } for (i = 0; i < nelem; i++) { struct gmac_desc *d = r->descs + i; if (i == nelem - 1) { d->next_desc = virt_to_phys(r->descs + 0); } else { d->next_desc = virt_to_phys(r->descs + i + 1); } d->data = (char*)r->packet_data + i * MAX_PACKET_SIZE; d->pkt_start_addr = virt_to_phys(d->data); } return 0; } #if defined(CONFIG_MACH_QCA955x) || defined(CONFIG_SOC_QCA955X) static int special_setup(struct avm_coredump_net_context *this) { athr_gmac_reg_wr(this->BaseAddress, ATHR_GMAC_FIFO_CFG_0, 0x1f00); athr_gmac_reg_wr(this->BaseAddress, ATHR_GMAC_FIFO_CFG_1, 0x10ffff); athr_gmac_reg_wr(this->BaseAddress, ATHR_GMAC_FIFO_CFG_2, 0x027001aa); athr_gmac_reg_wr(this->BaseAddress, ATHR_GMAC_FIFO_CFG_3, 0x1f00140); athr_gmac_reg_wr(this->BaseAddress, ATHR_GMAC_FIFO_CFG_4, ATHR_ALL_FRAMES); athr_gmac_reg_wr(this->BaseAddress, ATHR_GMAC_FIFO_CFG_5, ATHR_DEFAULT_FRAMES); athr_gmac_reg_wr(this->BaseAddress, ATHR_GMAC_DMA_TXFIFO_THRESH ,0x01d80160); athr_gmac_reg_rmw_clear(this->BaseAddress, ATHR_GMAC_DMA_TX_ARB_CFG, ATHR_GMAC_TX_QOS_MODE_WEIGHTED); athr_gmac_reg_rmw_clear(this->BaseAddress, ATHR_GMAC_CFG2, (ATHR_GMAC_CFG2_IF_1000| ATHR_GMAC_CFG2_IF_10_100)); switch (this->BaseAddress) { case ATH_GE0_BASE: if (this->hw.flags & FLAG_SGMII) { ath_reg_wr(ETH_CFG_ADDRESS, ETH_CFG_GE0_SGMII_SET(1)); athr_gmac_reg_rmw_set(this->BaseAddress, ATHR_GMAC_CFG2, (ATHR_GMAC_CFG2_PAD_CRC_EN | ATHR_GMAC_CFG2_LEN_CHECK)); athr_gmac_reg_rmw_set(this->BaseAddress, ATHR_GMAC_FIFO_CFG_5, ATHR_GMAC_BYTE_PER_CLK_EN); } else { /*--- External RMII Mode on GE0,Master and High speed ---*/ if (this->hw.flags & FLAG_RX_TX_DELAY) { ath_reg_wr(ETH_CFG_ADDRESS, ETH_CFG_ETH_RXDV_DELAY_SET(0x3) | ETH_CFG_ETH_RXD_DELAY_SET(0x3) | ETH_CFG_RGMII_GE0_SET(0x1)); } else { ath_reg_wr(ETH_CFG_ADDRESS, ETH_CFG_RGMII_GE0_SET(1)); } athr_gmac_reg_rmw_set(this->BaseAddress, ATHR_GMAC_CFG2, (ATHR_GMAC_CFG2_PAD_CRC_EN | ATHR_GMAC_CFG2_LEN_CHECK | ATHR_GMAC_CFG2_IF_10_100)); athr_gmac_reg_rmw_clear(this->BaseAddress, ATHR_GMAC_FIFO_CFG_5, ATHR_GMAC_BYTE_PER_CLK_EN); } break; case ATH_GE1_BASE: if (is_qca9558()) { ath_reg_wr(ETH_CFG_ADDRESS, ETH_CFG_RGMII_GE0_SET(1)); } else { ath_reg_wr(ETH_CFG_ADDRESS, ETH_CFG_GE0_SGMII_SET(0)); } athr_gmac_reg_rmw_set(this->BaseAddress, ATHR_GMAC_CFG2, (ATHR_GMAC_CFG2_FDX | ATHR_GMAC_CFG2_PAD_CRC_EN | ATHR_GMAC_CFG2_IF_1000)); athr_gmac_reg_rmw_set(this->BaseAddress, ATHR_GMAC_FIFO_CFG_5, ATHR_GMAC_BYTE_PER_CLK_EN); break; } athr_gmac_reg_wr(this->BaseAddress, ATHR_GMAC_DMA_TX_DESC_Q0, virt_to_phys(&this->mac_txring.descs[0])); #if defined(ENABLE_RX) athr_gmac_reg_wr(this->BaseAddress, ATHR_GMAC_DMA_RX_DESC, virt_to_phys(&this->mac_rxring.descs[0])); #endif return 0; } #elif defined(CONFIG_MACH_QCA956X) || defined(CONFIG_SOC_QCA956X) static int special_setup(struct avm_coredump_net_context *this) { athr_gmac_reg_wr (this->BaseAddress, ATHR_GMAC_FIFO_CFG_0, 0x1f00); athr_gmac_reg_wr (this->BaseAddress, ATHR_GMAC_FIFO_CFG_1, 0x10ffff); athr_gmac_reg_wr (this->BaseAddress, ATHR_GMAC_FIFO_CFG_2, 0x027001aa); athr_gmac_reg_wr (this->BaseAddress, ATHR_GMAC_FIFO_CFG_3, 0x1f00140); athr_gmac_reg_wr (this->BaseAddress, ATHR_GMAC_FIFO_CFG_4, ATHR_ALL_FRAMES); athr_gmac_reg_wr (this->BaseAddress, ATHR_GMAC_FIFO_CFG_5, ATHR_DEFAULT_FRAMES); athr_gmac_reg_wr (this->BaseAddress, ATHR_GMAC_DMA_TXFIFO_THRESH ,0x01d80160); athr_gmac_reg_rmw_clear (this->BaseAddress, ATHR_GMAC_DMA_TX_ARB_CFG, ATHR_GMAC_TX_QOS_MODE_WEIGHTED); athr_gmac_reg_rmw_clear(this->BaseAddress, ATHR_GMAC_CFG2, (ATHR_GMAC_CFG2_IF_1000| ATHR_GMAC_CFG2_IF_10_100)); switch (this->BaseAddress) { case ATH_GE0_BASE: if (this->hw.flags & FLAG_SGMII) { ath_reg_wr ( ATHR_GMAC_ETH_CFG, ATH_GMAC_ETH_CFG_GE0_SGMII_SET (1)); ath_reg_wr ( ATH_PLL_ETH_SGMII, ATH_PLL_ETH_SGMII_GIGE_SET (1) | ATH_PLL_ETH_SGMII_RX_DELAY_SET (1) | ATH_PLL_ETH_SGMII_TX_DELAY_SET (1) | ATH_PLL_ETH_SGMII_CLK_SEL_SET (1) | ATH_PLL_ETH_SGMII_PHASE1_COUNT_SET (1) | ATH_PLL_ETH_SGMII_PHASE0_COUNT_SET (1) ); athr_gmac_reg_rmw_set ( this->BaseAddress, ATHR_GMAC_CFG2, (ATHR_GMAC_CFG2_PAD_CRC_EN | ATHR_GMAC_CFG2_LEN_CHECK | ATHR_GMAC_CFG2_IF_1000)); athr_gmac_reg_rmw_set ( this->BaseAddress, ATHR_GMAC_FIFO_CFG_5, ATHR_GMAC_BYTE_PER_CLK_EN); break; } if( ! false /* (this->this_module->initdata.mac.flags & AVMNET_CONFIG_FLAG_NO_WANPORT)*/) { ath_reg_wr ( ATH_GMAC_ETH_CFG, ATH_GMAC_ETH_CFG_SW_PHY_SWAP_SET (1)); } athr_gmac_reg_rmw_set ( this->BaseAddress, ATHR_GMAC_CFG2, (ATHR_GMAC_CFG2_PAD_CRC_EN | ATHR_GMAC_CFG2_LEN_CHECK )); athr_gmac_reg_rmw_clear ( this->BaseAddress, ATHR_GMAC_FIFO_CFG_5, ATHR_GMAC_BYTE_PER_CLK_EN); break; case ATH_GE1_BASE: if(false /* this->this_module->initdata.mac.flags & AVMNET_CONFIG_FLAG_NO_WANPORT */){ ath_reg_rmw_set ( ATH_GMAC_ETH_CFG, ATH_GMAC_ETH_CFG_SW_ONLY_MODE_SET (1)); } athr_gmac_reg_rmw_set ( this->BaseAddress, ATHR_GMAC_CFG2, (ATHR_GMAC_CFG2_FDX | ATHR_GMAC_CFG2_PAD_CRC_EN | ATHR_GMAC_CFG2_IF_1000)); athr_gmac_reg_rmw_set ( this->BaseAddress, ATHR_GMAC_FIFO_CFG_5, ATHR_GMAC_BYTE_PER_CLK_EN); break; } athr_gmac_reg_wr(this->BaseAddress, ATHR_GMAC_DMA_TX_DESC_Q0, virt_to_phys(&this->mac_txring.descs[0])); #if defined(ENABLE_RX) athr_gmac_reg_wr(this->BaseAddress, ATHR_GMAC_DMA_RX_DESC, virt_to_phys(&this->mac_rxring.descs[0])); #endif return 0; } #elif defined(CONFIG_SOC_AR724X) || defined(CONFIG_SOC_AR934X) static int special_setup(struct avm_coredump_net_context *this) { unsigned int sw_only; athr_gmac_reg_wr(this->BaseAddress, ATHR_GMAC_FIFO_CFG_0, 0x1f00); athr_gmac_reg_wr(this->BaseAddress, ATHR_GMAC_FIFO_CFG_1, 0x10ffff); athr_gmac_reg_wr(this->BaseAddress, ATHR_GMAC_FIFO_CFG_2, 0x015500aa); athr_gmac_reg_wr(this->BaseAddress, ATHR_GMAC_FIFO_CFG_3, 0x1f00140); athr_gmac_reg_wr(this->BaseAddress, ATHR_GMAC_FIFO_CFG_4, ATHR_ALL_FRAMES); athr_gmac_reg_wr(this->BaseAddress, ATHR_GMAC_FIFO_CFG_5, ATHR_DEFAULT_FRAMES); athr_gmac_reg_wr(this->BaseAddress, ATHR_GMAC_DMA_TXFIFO_THRESH ,0x01d80160); athr_gmac_reg_rmw_clear(this->BaseAddress, ATHR_GMAC_DMA_TX_ARB_CFG, ATHR_GMAC_TX_QOS_MODE_WEIGHTED); athr_gmac_reg_rmw_clear(this->BaseAddress, ATHR_GMAC_CFG2, (ATHR_GMAC_CFG2_IF_1000| ATHR_GMAC_CFG2_IF_10_100)); switch (this->BaseAddress) { case ATH_GE0_BASE: /*--- External RMII Mode on GE0,Master and High speed ---*/ // make sure we keep WAN PHY attachment to switch, if set sw_only = ath_reg_rd(ATHR_GMAC_ETH_CFG) & ATHR_GMAC_ETH_CFG_SW_ONLY_MODE; ath_reg_wr(ATHR_GMAC_ETH_CFG, ATHR_GMAC_ETH_CFG_RGMII_EN | sw_only); athr_gmac_reg_rmw_set(this->BaseAddress, ATHR_GMAC_CFG2, (ATHR_GMAC_CFG2_PAD_CRC_EN | ATHR_GMAC_CFG2_LEN_CHECK | ATHR_GMAC_CFG2_IF_10_100)); athr_gmac_reg_rmw_clear(this->BaseAddress, ATHR_GMAC_FIFO_CFG_5, ATHR_GMAC_BYTE_PER_CLK_EN); break; case ATH_GE1_BASE: // if configured, connect WAN port PHY to switch MAC 5 if(true /* this->this_module->initdata.mac.flags & AVMNET_CONFIG_FLAG_NO_WANPORT */){ ath_reg_rmw_set(ATHR_GMAC_ETH_CFG, ATHR_GMAC_ETH_CFG_SW_ONLY_MODE); } athr_gmac_reg_rmw_set(this->BaseAddress, ATHR_GMAC_CFG2, (ATHR_GMAC_CFG2_FDX | ATHR_GMAC_CFG2_PAD_CRC_EN | ATHR_GMAC_CFG2_IF_1000)); athr_gmac_reg_rmw_set(this->BaseAddress, ATHR_GMAC_FIFO_CFG_5, ATHR_GMAC_BYTE_PER_CLK_EN); break; } athr_gmac_reg_wr(this->BaseAddress, ATHR_GMAC_DMA_TX_DESC_Q0, virt_to_phys(&this->mac_txring.descs[0])); #if defined(ENABLE_RX) athr_gmac_reg_wr(this->BaseAddress, ATHR_GMAC_DMA_RX_DESC, virt_to_phys(&this->mac_rxring.descs[0])); #endif return 0; } #elif defined(CONFIG_SOC_QCA953X) static int special_setup(struct avm_coredump_net_context *this) { athr_gmac_reg_wr(this->BaseAddress, ATHR_GMAC_FIFO_CFG_0, 0x1f00); athr_gmac_reg_wr(this->BaseAddress, ATHR_GMAC_FIFO_CFG_1, 0x10ffff); athr_gmac_reg_wr(this->BaseAddress, ATHR_GMAC_FIFO_CFG_2, 0x015500aa); athr_gmac_reg_wr(this->BaseAddress, ATHR_GMAC_FIFO_CFG_4, ATHR_ALL_FRAMES); athr_gmac_reg_wr(this->BaseAddress, ATHR_GMAC_FIFO_CFG_5, ATHR_DEFAULT_FRAMES); athr_gmac_reg_rmw_clear(this->BaseAddress, ATHR_GMAC_DMA_TX_ARB_CFG, ATHR_GMAC_TX_QOS_MODE_WEIGHTED); athr_gmac_reg_rmw_clear(this->BaseAddress, ATHR_GMAC_CFG2, (ATHR_GMAC_CFG2_IF_1000| ATHR_GMAC_CFG2_IF_10_100)); switch (this->BaseAddress) { case ATH_GE0_BASE: /*--- beim Honeybee ist dieses Interface max. 100MBit ---*/ if( ! false /* (this->this_module->initdata.mac.flags & AVMNET_CONFIG_FLAG_NO_WANPORT) */) { ath_reg_wr(ATH_GMAC_ETH_CFG, ATH_GMAC_ETH_CFG_SW_PHY_SWAP_SET (1)); } else { ath_reg_wr(ATHR_GMAC_ETH_CFG, 0); } athr_gmac_reg_rmw_set(this->BaseAddress, ATHR_GMAC_CFG2, (ATHR_GMAC_CFG2_FDX | ATHR_GMAC_CFG2_PAD_CRC_EN | ATHR_GMAC_CFG2_LEN_CHECK | ATHR_GMAC_CFG2_IF_10_100)); athr_gmac_reg_wr(this->BaseAddress, ATHR_GMAC_DMA_TXFIFO_THRESH ,0x00880060); athr_gmac_reg_wr(this->BaseAddress, ATHR_GMAC_FIFO_CFG_3, 0x00f00040); athr_gmac_reg_rmw_clear(this->BaseAddress, ATHR_GMAC_FIFO_CFG_5, ATHR_GMAC_BYTE_PER_CLK_EN); break; case ATH_GE1_BASE: /*--- beim Honeybee ist dieses Interface immer 1000MBit ---*/ // if configured, connect WAN port PHY to switch MAC 5 if(false /* this->this_module->initdata.mac.flags & AVMNET_CONFIG_FLAG_NO_WANPORT */){ ath_reg_rmw_set(ATHR_GMAC_ETH_CFG, ATHR_GMAC_ETH_CFG_SW_ONLY_MODE); } athr_gmac_reg_rmw_set(this->BaseAddress, ATHR_GMAC_CFG2, (ATHR_GMAC_CFG2_FDX | ATHR_GMAC_CFG2_PAD_CRC_EN | ATHR_GMAC_CFG2_IF_1000)); athr_gmac_reg_wr(this->BaseAddress, ATHR_GMAC_FIFO_CFG_3, 0x1f00140); athr_gmac_reg_wr(this->BaseAddress, ATHR_GMAC_DMA_TXFIFO_THRESH ,0x01d80160); athr_gmac_reg_rmw_set(this->BaseAddress, ATHR_GMAC_FIFO_CFG_5, ATHR_GMAC_BYTE_PER_CLK_EN); break; } athr_gmac_reg_wr(this->BaseAddress, ATHR_GMAC_DMA_TX_DESC_Q0, virt_to_phys(&this->mac_txring.descs[0])); #if defined(ENABLE_RX) athr_gmac_reg_wr(this->BaseAddress, ATHR_GMAC_DMA_RX_DESC, virt_to_phys(&this->mac_rxring.descs[0])); #endif return 0; } #endif static void athmac_gmac_setup_mii(struct avm_coredump_net_context *mac) { unsigned int mgmt_cfg_val = 0; #if ! defined(CONFIG_MACH_AR724x) && ! defined(CONFIG_SOC_AR724X) // [GJu] der QCA9563 hat keinen Switch, wird aber identisch behandelt, weil er anhand der // Revisionsnummer nicht vom QCA9561 zu unterscheiden ist if (soc_is_ar934x() || soc_is_qca953x() || soc_is_qca955x() || soc_is_qca956x()) { ath_reg_rmw_set (ATH_PLL_SWITCH_CLOCK_CONTROL, (1 << 6)); /*--- MDIO_CLK = 100MHz ---*/ } #endif if (soc_is_qca955x() || soc_is_qca956x()) { mgmt_cfg_val = 0x7; } else { mgmt_cfg_val = 0x6; } athr_gmac_reg_wr(mac->BaseAddressMdio, ATHR_GMAC_MII_MGMT_CFG, mgmt_cfg_val | (1 << 31)); athr_gmac_reg_wr(mac->BaseAddressMdio, ATHR_GMAC_MII_MGMT_CFG, mgmt_cfg_val); } static void athmac_gmac_enable_hw(struct avm_coredump_net_context *mac) { /*--- enable FLOW-Control ---*/ #if defined(CONFIG_MACH_QCA955x) || defined(CONFIG_SOC_QCA955X) athr_gmac_reg_wr(mac->BaseAddress, ATHR_GMAC_CFG1, (ATHR_GMAC_CFG1_RX_EN | ATHR_GMAC_CFG1_TX_EN | ATHR_GMAC_CFG1_RX_FCTL)); #else #if defined(CONFIG_MACH_QCA956x) || defined(CONFIG_SOC_QCA956X) ath_reg_wr(ATH_PLL_ETH_SGMII, ATH_PLL_ETH_SGMII_GIGE_SET(1) | ATH_PLL_ETH_SGMII_RX_DELAY_SET(1) | ATH_PLL_ETH_SGMII_TX_DELAY_SET(1) | ATH_PLL_ETH_SGMII_CLK_SEL_SET(1) | ATH_PLL_ETH_SGMII_PHASE1_COUNT_SET(1) | ATH_PLL_ETH_SGMII_PHASE0_COUNT_SET(1)); #endif athr_gmac_reg_wr(mac->BaseAddress, ATHR_GMAC_CFG1, (ATHR_GMAC_CFG1_RX_EN | ATHR_GMAC_CFG1_TX_EN | ATHR_GMAC_CFG1_RX_FCTL | ATHR_GMAC_CFG1_TX_FCTL)); #endif } static const struct hw hws[] = { /* 546E */ { .hw_id = 190, .flags = FLAG_SWITCH, .ports = 0b111110 }, /* 310v1 */ { .hw_id = 194, .flags = FLAG_SWITCH, .ports = 0b111110 }, /* 546E */ { .hw_id = 201, .flags = FLAG_SWITCH, .ports = 0b111110 }, /* 310v2 */ { .hw_id = 215, .flags = FLAG_SWITCH, .ports = 0b111110 }, /* 1160 */ { .hw_id = 216, .flags = FLAG_SWITCH, .ports = 0b111110 }, /* 1240E */ { .hw_id = 222, .flags = FLAG_SWITCH, .ports = 0b111110 }, /* 1750E */ { .hw_id = 206, .flags = FLAG_SGMII}, /* 4020 */ { .hw_id = 219, .flags = FLAG_SWITCH, .ports = 0b111110 }, /* 7490 Offload Scorpion */ { .hw_id = 238, .flags = FLAG_RX_TX_DELAY}, }; static struct avm_coredump_net_context crash_ctx = { .BaseAddressMdio = ATH_GE1_BASE, }; static unsigned int avm_get_hw_id(void) { char *s; s = prom_getenv("HWRevision"); if (!s) { return 0; } return simple_strtoul(s, NULL, 10); } int avm_coredump_net_prepare_setup(void) { int ret, i; unsigned int hw_id; struct avm_coredump_net_context *this = &crash_ctx; hw_id = avm_get_hw_id(); for (i = 0; i < ARRAY_SIZE(hws); i++) { if (hws[i].hw_id == hw_id) { this->hw = hws[i]; } } if (this->hw.hw_id == 0) { pr_err("[avm_coredump] No conf for hw_id %d found.\n", hw_id); return -EINVAL; } if (this->hw.flags & FLAG_SWITCH) { this->BaseAddress = ATH_GE1_BASE; } else { this->BaseAddress = ATH_GE0_BASE; } ret = ring_allocate(&this->mac_txring, TX_RING_SIZE); if (ret != 0) { return ret; } #if defined(ENABLE_RX) ret = ring_allocate(&this->mac_rxring, RX_RING_SIZE); if (ret != 0) { return ret; } #endif return ret; } int avm_coredump_net_force_setup(u8* mac_addr) { unsigned int w1 = 0, w2 = 0; unsigned int i = 0; bool use1000 = false; struct avm_coredump_net_context *this = &crash_ctx; // Before we reinitialize we need to look up the used interface speed as // we don't reset the phy use1000 = athr_gmac_reg_rd(this->BaseAddress, ATHR_GMAC_CFG2) & ATHR_GMAC_CFG2_IF_1000; athr_gmac_reg_rmw_set(this->BaseAddress, ATHR_GMAC_CFG1, ATHR_GMAC_CFG1_SOFT_RST | ATHR_GMAC_CFG1_RX_RST | ATHR_GMAC_CFG1_TX_RST); /*--- athr_init_hwaccels(mac); ---*/ /*--- athmac_gmac_setup_hw(mac); ---*/ athmac_gmac_setup_mii(this); special_setup(this); athmac_gmac_enable_hw(this); /*--- restore RX/TX- Descriptors ---*/ //athr_gmac_reg_wr(this->BaseAddress, ATHR_GMAC_DMA_TX_DESC, tx_ds); //athr_gmac_reg_wr(this->BaseAddress, ATHR_GMAC_DMA_RX_DESC, rx_ds); /*--- set the mac addr ---*/ addr_to_words(mac_addr, w1, w2); athr_gmac_reg_wr(this->BaseAddress, ATHR_GMAC_GE_MAC_ADDR1, w1); athr_gmac_reg_wr(this->BaseAddress, ATHR_GMAC_GE_MAC_ADDR2, w2); // Set GMII interface speed if (use1000) { athr_gmac_reg_rmw_set(this->BaseAddress, ATHR_GMAC_CFG2, ATHR_GMAC_CFG2_IF_1000); athr_gmac_reg_rmw_set(this->BaseAddress, ATHR_GMAC_FIFO_CFG_5, ATHR_GMAC_BYTE_PER_CLK_EN); } else { athr_gmac_reg_rmw_set(this->BaseAddress, ATHR_GMAC_CFG2, ATHR_GMAC_CFG2_IF_10_100); athr_gmac_reg_rmw_clear(this->BaseAddress,ATHR_GMAC_FIFO_CFG_5, ATHR_GMAC_BYTE_PER_CLK_EN); } #if defined(ENABLE_RX) // Mark rx packages empty and usuable for (i = 0; i < RX_RING_SIZE; i++) { this->mac_rxring.descs[i].packet.Register = 0; this->mac_rxring.descs[i].packet.Bits.pkt_size = MAX_PACKET_SIZE; athr_gmac_rx_give_to_dma(&this->mac_rxring.descs[i]); } this->mac_rxring.head = 0; #endif // Mark tx packages as empty for (i = 0; i < TX_RING_SIZE; i++) { this->mac_txring.descs[i].packet.Register = ATHR_TX_EMPTY; } this->mac_txring.head = 0; #if defined(ENABLE_RX) athr_gmac_rx_start(this->BaseAddress); #endif wmb(); printk(KERN_ERR "Forcefully reset network..."); return 0; } int avm_coredump_net_send_eth(const void* data, unsigned int len) { // TODO check data != NULL // TODO check if len fits in packet buffer struct avm_coredump_net_context *this = &crash_ctx; struct gmac_desc *d = &this->mac_txring.descs[this->mac_txring.head]; u8 *buf = d->data; unsigned int full_len = len; athr_mac_packet_header_t *hdr; // The queue is full if (athr_gmac_tx_owned_by_dma(d)) { return -EAGAIN; } if (this->hw.flags & FLAG_SWITCH) { hdr = (athr_mac_packet_header_t*)buf; // Copy over src/dest mac address memcpy(hdr, data, 2 * ETH_ALEN); hdr->s.header.version = 2; hdr->s.header.priority = 0; hdr->s.header.type = 0; hdr->s.header.from_cpu = 1; hdr->s.header.portnum = this->hw.ports; hdr->s.data = *(unsigned short *)(data + 2 * ETH_ALEN); buf += sizeof(athr_mac_packet_header_t); full_len += sizeof(athr_mac_packet_header_t) - 2 * ETH_ALEN - 2; memcpy(buf, data + 2 * ETH_ALEN + 2, len - 2 * ETH_ALEN -2); } else { // Got space, copy over data memcpy(buf, data, len); } d->packet.Register = ATHR_TX_EMPTY; d->packet.Bits.pkt_size = full_len; // Advance head this->mac_txring.head = (this->mac_txring.head + 1) % TX_RING_SIZE; // update cache dma_cache_wback((unsigned long)d->data, MAX_PACKET_SIZE); wmb(); // tell gmac that a new package should be send athr_gmac_tx_give_to_dma(d); athr_gmac_tx_start(this->BaseAddress); return 0; } int avm_coredump_net_recv_eth(void* data, unsigned int *len) { #if defined(ENABLE_RX) struct avm_coredump_net_context *this = &crash_ctx; struct gmac_desc *d = &this->mac_rxring.descs[this->mac_rxring.head]; // The queue is empty if (athr_gmac_rx_owned_by_dma(d)) { return -EAGAIN; } // If received package is shorter than available space, update len // NOTE: this turncats packages that do not fit into the provided buffer if (d->packet.Bits.pkt_size < *len) { *len = d->packet.Bits.pkt_size; } // Invalidate cache and read received data dma_cache_inv((unsigned long)d->data, MAX_PACKET_SIZE); memcpy(data, d->data, *len); // Advance head this->mac_rxring.head = (this->mac_rxring.head + 1) % RX_RING_SIZE; // Mark read package as empty and give it back to the dma d->packet.Register = 0; d->packet.Bits.pkt_size = MAX_PACKET_SIZE; wmb(); athr_gmac_rx_give_to_dma(d); return 0; #else return -ENOSYS; #endif } int avm_coredump_net_poll_eth(void) { AVM_WATCHDOG_emergency_retrigger(); // No polling needed here return 0; } int avm_coredump_net_shutdown(void) { return 0; }