/** ****************************************************************************** * @file bflb_emac.c * @version V1.0 * @date 2022-09-27 * @brief This file is the low hardware abstraction layer file ****************************************************************************** * @attention * *

© COPYRIGHT(c) 2022 Bouffalo Lab

* * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * 3. Neither the name of Bouffalo Lab nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * ****************************************************************************** */ #include "bflb_emac.h" #include "bflb_clock.h" #include "bflb_l1c.h" #include "hardware/emac_reg.h" /* private definition */ // #define TAG "EMAC_BD: " #define EMAC_TX_COMMON_FLAGS (EMAC_BD_TX_RD_MASK | EMAC_BD_TX_IRQ_MASK | EMAC_BD_TX_PAD_MASK | EMAC_BD_TX_CRC_MASK | EMAC_BD_TX_EOF_MASK) #define EMAC_RX_COMMON_FLAGS ((ETH_MAX_PACKET_SIZE << 16) | EMAC_BD_RX_IRQ_MASK) /** * @brief Note: Always write DWORD1 (buffer addr) first then DWORD0 for racing concern. */ struct bflb_emac_bd_desc_s { uint32_t C_S_L; /*!< Buffer Descriptors(BD) control,status,length */ uint32_t Buffer; /*!< BD buffer address */ }; /** * @brief emac handle type definition * @param bd Tx descriptor header pointer * @param tx_index_emac TX index: EMAC * @param tx_index_cpu TX index: CPU/SW * @param tx_buff_limit TX index max * @param rsv0 rsv0 * @param rx_index_emac RX index: EMAC * @param rx_index_cpu RX index: CPU/SW * @param rx_buff_limit RX index max * @param rsv1 rsv1 * */ struct bflb_emac_handle_s { struct bflb_emac_bd_desc_s *bd; uint8_t tx_index_emac; uint8_t tx_index_cpu; uint8_t tx_buff_limit; uint8_t rsv0; uint8_t rx_index_emac; uint8_t rx_index_cpu; uint8_t rx_buff_limit; uint8_t rsv1; }; static struct bflb_emac_handle_s eth_handle; static struct bflb_emac_handle_s *thiz = NULL; /** * * @brief get emac current active buffer describe index * @param dev * @param bdt @ref emac buffer descriptors type define * @return uint32_t * */ uint32_t bflb_emac_bd_get_cur_active(struct bflb_device_s *dev, uint8_t bdt) { uint32_t bd = 0; uint32_t reg_base; reg_base = dev->reg_base; bd = getreg32(reg_base + EMAC_TX_BD_NUM_OFFSET); if (bdt == EMAC_BD_TYPE_TX) { bd &= EMAC_TXBDPTR_MASK; bd >>= EMAC_TXBDPTR_SHIFT; } if (bdt == EMAC_BD_TYPE_RX) { bd &= EMAC_RXBDPTR_MASK; bd >>= EMAC_RXBDPTR_SHIFT; } return bd; } /** * @brief * * @param index * */ void bflb_emac_bd_rx_enqueue(uint32_t index) { thiz->rx_index_emac = index; } /** * @brief * * @param index * */ void bflb_emac_bd_rx_on_err(uint32_t index) { /* handle error */ if (thiz->bd[index].C_S_L & EMAC_BD_RX_OR_MASK) { printf("EMAC RX OR Error at %s:%d\r\n", __func__, __LINE__); } if (thiz->bd[index].C_S_L & EMAC_BD_RX_RE_MASK) { printf("MAC RX RE Error at %s:%d\r\n", __func__, __LINE__); } if (thiz->bd[index].C_S_L & EMAC_BD_RX_DN_MASK) { printf("MAC RX DN Error at %s:%d\r\n", __func__, __LINE__); } if (thiz->bd[index].C_S_L & EMAC_BD_RX_TL_MASK) { printf("MAC RX TL Error at %s:%d\r\n", __func__, __LINE__); } if (thiz->bd[index].C_S_L & EMAC_BD_RX_CRC_MASK) { printf("MAC RX CRC Error at %s:%d\r\n", __func__, __LINE__); } if (thiz->bd[index].C_S_L & EMAC_BD_RX_LC_MASK) { printf("MAC RX LC Error at %s:%d\r\n", __func__, __LINE__); } thiz->bd[index].C_S_L &= ~0xff; /* RX BD is ready for RX */ thiz->bd[index].C_S_L |= EMAC_BD_RX_E_MASK; } /** * @brief this func will be called in ISR * * @param index * */ void bflb_emac_bd_tx_dequeue(uint32_t index) { struct bflb_emac_bd_desc_s *DMADesc; thiz->tx_index_emac = index; DMADesc = &thiz->bd[thiz->tx_index_emac]; /* release this tx BD to SW (HW will do this) */ DMADesc->C_S_L &= ~EMAC_BD_TX_RD_MASK; } /** * @brief * * @param index * @return int */ void bflb_emac_bd_tx_on_err(uint32_t index) { /* handle error */ if (thiz->bd[index].C_S_L & EMAC_BD_TX_UR_MASK) { printf("%s:%d\r\n", __func__, __LINE__); } if (thiz->bd[index].C_S_L & EMAC_BD_TX_RTRY_MASK) { printf("%s:%d\r\n", __func__, __LINE__); } if (thiz->bd[index].C_S_L & EMAC_BD_TX_RL_MASK) { printf("%s:%d\r\n", __func__, __LINE__); } if (thiz->bd[index].C_S_L & EMAC_BD_TX_LC_MASK) { printf("%s:%d\r\n", __func__, __LINE__); } if (thiz->bd[index].C_S_L & EMAC_BD_TX_DF_MASK) { printf("%s:%d\r\n", __func__, __LINE__); } if (thiz->bd[index].C_S_L & EMAC_BD_TX_CS_MASK) { printf("%s:%d\r\n", __func__, __LINE__); } thiz->bd[index].C_S_L &= ~0xff; } /** * @brief * * @param none * @return int */ int emac_bd_fragment_support(void) { #if defined(BL616) || defined(BL808) return 1; #elif defined(BL702) return 0; #elif defined(BL628) return 1; #endif } /** * @brief * * @param flags * @param len * @param data_in * @return int */ int bflb_emac_bd_tx_enqueue(uint32_t flags, uint32_t len, const uint8_t *data_in) { uint32_t err = 0; struct bflb_emac_bd_desc_s *DMADesc; uint32_t tx_flags = EMAC_TX_COMMON_FLAGS; DMADesc = &thiz->bd[thiz->tx_index_cpu]; if (flags & EMAC_FRAGMENT_PACKET) { /* Fragment packet, clear EOF */ tx_flags &= ~EMAC_BD_TX_EOF_MASK; } if (DMADesc->C_S_L & EMAC_BD_TX_RD_MASK) { /* no free BD, lost sync with DMA TX? */ err = 4; //printf(TAG"%s:%d\n", __func__, __LINE__); } else { #if defined(BL616) __ASM volatile("fence"); #endif // printf("tx q flags:%d,len:%d,data:0x%x\r\n", flags, len, data_in); if (flags & EMAC_NOCOPY_PACKET) { DMADesc->Buffer = (uint32_t)(uintptr_t)data_in; } else { // memcpy((void *)(uintptr_t)(DMADesc->Buffer), data_in, len); arch_memcpy_fast((void *)(uintptr_t)(DMADesc->Buffer), data_in, len); } #ifdef EMAC_DO_FLUSH_DATA #if defined(BL616) bflb_l1c_dcache_invalidate_range((void *)DMADesc->Buffer, len); #endif #endif DMADesc->C_S_L = tx_flags | (len << EMAC_BD_TX_LEN_SHIFT); /* move to next TX BD */ if ((++thiz->tx_index_cpu) > thiz->tx_buff_limit) { /* the last BD */ DMADesc->C_S_L |= EMAC_BD_TX_WR_MASK; /* wrap back */ thiz->tx_index_cpu = 0; } } return err; } /** * @brief * * @param flags * @param len * @param data_out * @return int */ int bflb_emac_bd_rx_dequeue(uint32_t flags, uint32_t *len, uint8_t *data_out) { uint32_t err = 0; struct bflb_emac_bd_desc_s *DMADesc; DMADesc = &thiz->bd[thiz->rx_index_cpu]; if (DMADesc->C_S_L & EMAC_BD_RX_E_MASK) { /* current RX BD is empty */ err = 4; *len = 0; } else { *len = (thiz->bd[thiz->rx_index_cpu].C_S_L & EMAC_BD_RX_LEN_MASK) >> EMAC_BD_RX_LEN_SHIFT; #ifdef EMAC_DO_FLUSH_DATA #if defined(BL616) bflb_l1c_dcache_invalidate_range((void *)DMADesc->Buffer, *len); #endif #endif if (data_out) { // memcpy(data_out, (const void *)(uintptr_t)DMADesc->Buffer, *len); arch_memcpy_fast(data_out, (const void *)(uintptr_t)DMADesc->Buffer, *len); } /* RX BD can be used for another receive */ DMADesc->C_S_L |= EMAC_BD_RX_E_MASK; /* move to next RX BD */ if ((++thiz->rx_index_cpu) > thiz->rx_buff_limit) { /* the last BD */ DMADesc->C_S_L |= EMAC_BD_RX_WR_MASK; /* wrap back */ thiz->rx_index_cpu = thiz->tx_buff_limit + 1; } } return err; } /** * @brief bflb emac init * * @param dev * @param config * */ void bflb_emac_init(struct bflb_device_s *dev, const struct bflb_emac_config_s *config) { uint32_t reg_base; uint32_t reg_val; #if defined(BL616) || defined(BL808) #define GLB_EMAC_CLK_OUT_ADDRESS (0x20000390) #define GLB_UNGATE_CFG2_ADDRESS (0x20000588) /* GLB select inside clock or Not */ reg_val = getreg32(GLB_EMAC_CLK_OUT_ADDRESS); if (config->inside_clk == EMAC_CLK_USE_INTERNAL) { reg_val |= (1 << 5); reg_val |= (1 << 6); reg_val &= ~(1 << 7); reg_val |= (1 << 10); } else { reg_val &= ~(1 << 5); reg_val &= ~(1 << 6); reg_val &= ~(1 << 7); } putreg32(reg_val, GLB_EMAC_CLK_OUT_ADDRESS); /* ungate emac clock */ reg_val = getreg32(GLB_UNGATE_CFG2_ADDRESS); reg_val |= (1 << 23); putreg32(reg_val, GLB_UNGATE_CFG2_ADDRESS); #elif defined(BL702) /* enable audio clock and GLB select inside clock or Not */ // #define PDS_AUDIO_PLL_EN_ADDRESS (0x4000E41C) #define GLB_UNGATE_CFG1_ADDRESS (0x40000024) #define GLB_CLOCK_CFG3_ADDRESS (0x4000000C) // reg_val = getreg32(PDS_AUDIO_PLL_EN_ADDRESS); // reg_val |= (1 << 7); // putreg32(reg_val, PDS_AUDIO_PLL_EN_ADDRESS); reg_val = getreg32(GLB_CLOCK_CFG3_ADDRESS); if (config->inside_clk == EMAC_CLK_USE_INTERNAL) { reg_val |= (1 << 5); } else { reg_val &= ~(1 << 5); } putreg32(reg_val, GLB_CLOCK_CFG3_ADDRESS); /* ungate emac clock */ reg_val = getreg32(GLB_UNGATE_CFG1_ADDRESS); reg_val |= (1 << 0x0d); putreg32(reg_val, GLB_UNGATE_CFG1_ADDRESS); #endif reg_base = dev->reg_base; /* set mac defualt config , enable rmii and other*/ reg_val = getreg32(reg_base + EMAC_MODE_OFFSET); reg_val |= (EMAC_RMII_EN); reg_val |= (EMAC_PRO); reg_val |= (EMAC_BRO); reg_val &= ~(EMAC_NOPRE); reg_val |= (EMAC_PAD); reg_val |= (EMAC_CRCEN); reg_val &= ~(EMAC_HUGEN); reg_val |= (EMAC_RECSMALL); reg_val |= (EMAC_IFG); putreg32(reg_val, reg_base + EMAC_MODE_OFFSET); /* set inter frame gap defualt value */ reg_val = getreg32(reg_base + EMAC_IPGT_OFFSET); reg_val &= ~(EMAC_IPGT_MASK); reg_val |= (0x18) << EMAC_IPGT_SHIFT; putreg32(reg_val, reg_base + EMAC_IPGT_OFFSET); /* set MII interface */ reg_val = getreg32(reg_base + EMAC_MIIMODE_OFFSET); reg_val |= EMAC_MIINOPRE; reg_val &= ~(EMAC_CLKDIV_MASK); reg_val |= (config->mii_clk_div) << EMAC_CLKDIV_SHIFT; putreg32(reg_val, reg_base + EMAC_MIIMODE_OFFSET); /* set collision */ reg_val = getreg32(reg_base + EMAC_COLLCONFIG_OFFSET); reg_val &= ~(EMAC_MAXFL_MASK | EMAC_COLLVALID_MASK); reg_val |= (0xf) << EMAC_MAXFL_SHIFT; reg_val |= (0x10) << EMAC_COLLVALID_SHIFT; putreg32(reg_val, reg_base + EMAC_COLLCONFIG_OFFSET); /* set frame length */ reg_val = getreg32(reg_base + EMAC_PACKETLEN_OFFSET); reg_val &= ~(EMAC_MINFL_MASK | EMAC_MAXFL_MASK); reg_val |= (config->min_frame_len) << EMAC_MINFL_SHIFT; reg_val |= (config->max_frame_len) << EMAC_MAXFL_SHIFT; putreg32(reg_val, reg_base + EMAC_PACKETLEN_OFFSET); /* set emac address */ reg_val = getreg32(reg_base + EMAC_MAC_ADDR0_OFFSET); reg_val &= ~(EMAC_MAC_B2_MASK | EMAC_MAC_B3_MASK | EMAC_MAC_B4_MASK | EMAC_MAC_B5_MASK); reg_val |= (config->mac_addr[5]) << EMAC_MAC_B5_SHIFT; reg_val |= (config->mac_addr[4]) << EMAC_MAC_B4_SHIFT; reg_val |= (config->mac_addr[3]) << EMAC_MAC_B3_SHIFT; reg_val |= (config->mac_addr[2]) << EMAC_MAC_B2_SHIFT; putreg32(reg_val, reg_base + EMAC_MAC_ADDR0_OFFSET); reg_val = getreg32(reg_base + EMAC_MAC_ADDR1_OFFSET); reg_val &= ~(EMAC_MAC_B0_MASK | EMAC_MAC_B1_MASK); reg_val |= (config->mac_addr[1]) << EMAC_MAC_B1_SHIFT; reg_val |= (config->mac_addr[0]) << EMAC_MAC_B0_SHIFT; putreg32(reg_val, reg_base + EMAC_MAC_ADDR1_OFFSET); } /** * @brief bflb emac deinit * * @param dev * */ void bflb_emac_deinit(struct bflb_device_s *dev) { uint32_t reg_base; uint32_t reg_val; reg_base = dev->reg_base; reg_val = getreg32(reg_base + EMAC_MODE_OFFSET); reg_val &= ~(EMAC_TX_EN | EMAC_RX_EN); putreg32(reg_val, reg_base + EMAC_MODE_OFFSET); } /** * @brief bflb emac interrupt enable * * @param dev * @param flag * @param enable * */ void bflb_emac_int_enable(struct bflb_device_s *dev, uint32_t flag, bool enable) { uint32_t reg_base; uint32_t reg_val_mask; // reg_val_en; reg_base = dev->reg_base; reg_val_mask = getreg32(reg_base + EMAC_INT_MASK_OFFSET); if (enable) { reg_val_mask &= ~(flag); } else { reg_val_mask |= (flag); } putreg32(reg_val_mask, reg_base + EMAC_INT_MASK_OFFSET); }; /** * @brief bflb emac interrupt clear * * @param dev * @param flag * */ void bflb_emac_int_clear(struct bflb_device_s *dev, uint32_t flag) { putreg32(flag, dev->reg_base + EMAC_INT_SOURCE_OFFSET); } /** * @brief bflb emac get interrupt status * * @param dev * @return uint32_t */ uint32_t bflb_emac_get_int_status(struct bflb_device_s *dev) { uint32_t reg_base; uint32_t reg_sts_val, reg_mask_val; reg_base = dev->reg_base; reg_sts_val = getreg32(reg_base + EMAC_INT_SOURCE_OFFSET); reg_mask_val = getreg32(reg_base + EMAC_INT_MASK_OFFSET); return (reg_sts_val & (~reg_mask_val)); } /** * @brief emac dma description list init * * @param reg_base * @param handle * @param tx_buff * @param tx_buff_cnt * @param rx_buff * @param rx_buff_cnt * */ static void emac_dma_desc_list_init(uint32_t reg_base, struct bflb_emac_handle_s *handle, uint8_t *tx_buff, uint32_t tx_buff_cnt, uint8_t *rx_buff, uint32_t rx_buff_cnt) { uint32_t i = 0; /* Set the Ethernet handler env */ handle->bd = (struct bflb_emac_bd_desc_s *)(uintptr_t)(reg_base + EMAC_DMA_DESC_OFFSET); handle->tx_index_emac = 0; handle->tx_index_cpu = 0; handle->tx_buff_limit = tx_buff_cnt - 1; /* The receive descriptors' address starts right after the last transmit BD. */ handle->rx_index_emac = tx_buff_cnt; handle->rx_index_cpu = tx_buff_cnt; handle->rx_buff_limit = tx_buff_cnt + rx_buff_cnt - 1; /* Fill each DMARxDesc descriptor with the right values */ for (i = 0; i < tx_buff_cnt; i++) { /* Get the pointer on the ith member of the Tx Desc list */ handle->bd[i].Buffer = (NULL == tx_buff) ? 0 : (uint32_t)(uintptr_t)(tx_buff + (ETH_MAX_PACKET_SIZE * i)); handle->bd[i].C_S_L = 0; } /* For the last TX DMA Descriptor, it should be wrap back */ handle->bd[handle->tx_buff_limit].C_S_L |= EMAC_BD_TX_WR_MASK; for (i = tx_buff_cnt; i < (tx_buff_cnt + rx_buff_cnt); i++) { /* Get the pointer on the ith member of the Rx Desc list */ handle->bd[i].Buffer = (NULL == rx_buff) ? 0 : (uint32_t)(uintptr_t)(rx_buff + (ETH_MAX_PACKET_SIZE * (i - tx_buff_cnt))); handle->bd[i].C_S_L = (ETH_MAX_PACKET_SIZE << 16) | EMAC_BD_RX_IRQ_MASK | EMAC_BD_RX_E_MASK; } /* For the last RX DMA Descriptor, it should be wrap back */ handle->bd[handle->rx_buff_limit].C_S_L |= EMAC_BD_RX_WR_MASK; /* For the TX DMA Descriptor, it will wrap to 0 according to EMAC_TX_BD_NUM*/ putreg32(tx_buff_cnt, reg_base + EMAC_TX_BD_NUM_OFFSET); } /** * @brief emac buffer description init * * @param eth_tx_buff * @param tx_buf_count * @param eth_rx_buff * @param rx_buf_count * */ void bflb_emac_bd_init(struct bflb_device_s *dev, uint8_t *eth_tx_buff, uint8_t tx_buf_count, uint8_t *eth_rx_buff, uint8_t rx_buf_count) { thiz = ð_handle; uint32_t reg_base; reg_base = dev->reg_base; /* init the BDs in emac with buffer address */ emac_dma_desc_list_init(reg_base, thiz, (uint8_t *)eth_tx_buff, tx_buf_count, (uint8_t *)eth_rx_buff, rx_buf_count); } /** * @brief bflb emac phy register read * * @param dev * @param phy_reg * @param phy_reg_val * @return int * */ int bflb_emac_phy_reg_read(struct bflb_device_s *dev, uint16_t phy_reg, uint16_t *phy_reg_val) { uint32_t reg_val; /* Set Register Address */ reg_val = getreg32(dev->reg_base + EMAC_MIIADDRESS_OFFSET); reg_val &= ~(EMAC_RGAD_MASK); reg_val |= ((uint32_t)phy_reg << EMAC_RGAD_SHIFT); putreg32(reg_val, dev->reg_base + EMAC_MIIADDRESS_OFFSET); /* Trigger read */ reg_val = getreg32(dev->reg_base + EMAC_MIICOMMAND_OFFSET); reg_val |= (EMAC_RSTAT); putreg32(reg_val, dev->reg_base + EMAC_MIICOMMAND_OFFSET); __ASM volatile("nop"); __ASM volatile("nop"); __ASM volatile("nop"); __ASM volatile("nop"); do { reg_val = getreg32(dev->reg_base + EMAC_MIISTATUS_OFFSET); bflb_mtimer_delay_us(16); } while ((reg_val & (EMAC_MIIM_BUSY)) != 0); *phy_reg_val = getreg32(dev->reg_base + EMAC_MIIRX_DATA_OFFSET); return 0; } /** * @brief bflb emac phy register write * * @param dev * @param phy_reg * @param phy_reg_val * @return int * */ int bflb_emac_phy_reg_write(struct bflb_device_s *dev, uint16_t phy_reg, uint16_t phy_reg_val) { uint32_t reg_val; /* Set Register Address */ reg_val = getreg32(dev->reg_base + EMAC_MIIADDRESS_OFFSET); reg_val &= ~(EMAC_RGAD_MASK); reg_val |= ((uint32_t)phy_reg << EMAC_RGAD_SHIFT); putreg32(reg_val, dev->reg_base + EMAC_MIIADDRESS_OFFSET); /* Set Write data */ putreg32(phy_reg_val, dev->reg_base + EMAC_MIITX_DATA_OFFSET); /* Trigger write */ reg_val = getreg32(dev->reg_base + EMAC_MIICOMMAND_OFFSET); reg_val |= (EMAC_WCTRLDATA); putreg32(reg_val, dev->reg_base + EMAC_MIICOMMAND_OFFSET); __ASM volatile("nop"); __ASM volatile("nop"); __ASM volatile("nop"); __ASM volatile("nop"); do { reg_val = getreg32(dev->reg_base + EMAC_MIISTATUS_OFFSET); bflb_mtimer_delay_us(16); } while ((reg_val & (EMAC_MIIM_BUSY)) != 0); return 0; } /** * @brief bflb emac feature control * * @param dev * @param cmd * @param arg * @return int * */ int bflb_emac_feature_control(struct bflb_device_s *dev, int cmd, size_t arg) { int ret = 0; uint32_t reg_val; uint32_t reg_base; reg_base = dev->reg_base; switch (cmd) { case EMAC_CMD_NO_PREAMBLE_MODE: reg_val = getreg32(reg_base + EMAC_MODE_OFFSET); if (arg) { reg_val |= EMAC_NOPRE; } else { reg_val &= ~(EMAC_NOPRE); } putreg32(reg_val, reg_base + EMAC_MODE_OFFSET); break; case EMAC_CMD_EN_PROMISCUOUS: reg_val = getreg32(reg_base + EMAC_MODE_OFFSET); if (arg) { reg_val |= EMAC_PRO; } else { reg_val &= ~(EMAC_PRO); } putreg32(reg_val, reg_base + EMAC_MODE_OFFSET); break; case EMAC_CMD_FRAME_GAP_CHECK: reg_val = getreg32(reg_base + EMAC_MODE_OFFSET); if (arg) { reg_val |= EMAC_IFG; } else { reg_val &= ~(EMAC_IFG); } putreg32(reg_val, reg_base + EMAC_MODE_OFFSET); break; case EMAC_CMD_FULL_DUPLEX: reg_val = getreg32(reg_base + EMAC_MODE_OFFSET); if (arg) { reg_val |= EMAC_FULLD; } else { reg_val &= ~(EMAC_FULLD); } putreg32(reg_val, reg_base + EMAC_MODE_OFFSET); break; case EMAC_CMD_EN_TX_CRC_FIELD: reg_val = getreg32(reg_base + EMAC_MODE_OFFSET); if (arg) { reg_val |= EMAC_CRCEN; } else { reg_val &= ~(EMAC_CRCEN); } putreg32(reg_val, reg_base + EMAC_MODE_OFFSET); break; case EMAC_CMD_RECV_HUGE_FRAMES: reg_val = getreg32(reg_base + EMAC_MODE_OFFSET); if (arg) { reg_val |= EMAC_HUGEN; } else { reg_val &= ~(EMAC_HUGEN); } putreg32(reg_val, reg_base + EMAC_MODE_OFFSET); break; case EMAC_CMD_EN_AUTO_PADDING: reg_val = getreg32(reg_base + EMAC_MODE_OFFSET); if (arg) { reg_val |= EMAC_PAD; } else { reg_val &= ~(EMAC_PAD); } putreg32(reg_val, reg_base + EMAC_MODE_OFFSET); break; case EMAC_CMD_RECV_SMALL_FRAME: reg_val = getreg32(reg_base + EMAC_MODE_OFFSET); if (arg) { reg_val |= EMAC_RECSMALL; } else { reg_val &= ~(EMAC_RECSMALL); } putreg32(reg_val, reg_base + EMAC_MODE_OFFSET); break; case EMAC_CMD_SET_PHY_ADDRESS: reg_val = getreg32(reg_base + EMAC_MIIADDRESS_OFFSET); reg_val &= ~(EMAC_FIAD_MASK); reg_val |= (uint32_t)(arg << EMAC_FIAD_SHIFT); putreg32(reg_val, reg_base + EMAC_MIIADDRESS_OFFSET); break; case EMAC_CMD_SET_MAXRET: reg_val = getreg32(reg_base + EMAC_COLLCONFIG_OFFSET); reg_val &= ~(EMAC_MAXFL_MASK); reg_val |= (arg) << EMAC_MAXFL_SHIFT; putreg32(reg_val, reg_base + EMAC_COLLCONFIG_OFFSET); break; case EMAC_CMD_SET_COLLVALID: reg_val = getreg32(reg_base + EMAC_COLLCONFIG_OFFSET); reg_val &= ~(EMAC_COLLVALID_MASK); reg_val |= (arg) << EMAC_COLLVALID_SHIFT; putreg32(reg_val, reg_base + EMAC_COLLCONFIG_OFFSET); break; case EMAC_CMD_SET_PACKET_GAP: reg_val = getreg32(reg_base + EMAC_IPGT_OFFSET); reg_val &= ~(EMAC_IPGT_MASK); reg_val |= (arg) << EMAC_IPGT_SHIFT; putreg32(reg_val, reg_base + EMAC_IPGT_OFFSET); break; default: ret = -EPERM; break; } return ret; } /** * @brief bflb emac stop * * @param dev * */ void bflb_emac_stop(struct bflb_device_s *dev) { /* disable emac */ uint32_t reg_val; reg_val = getreg32(dev->reg_base + EMAC_MODE_OFFSET); reg_val &= ~(EMAC_TX_EN | EMAC_RX_EN); putreg32(reg_val, dev->reg_base + EMAC_MODE_OFFSET); } /** * @brief bflb emac start * * @param dev * */ void bflb_emac_start(struct bflb_device_s *dev) { /* enable emac */ uint32_t reg_val; reg_val = getreg32(dev->reg_base + EMAC_MODE_OFFSET); reg_val |= (EMAC_TX_EN | EMAC_RX_EN); putreg32(reg_val, dev->reg_base + EMAC_MODE_OFFSET); } /** * @brief bflb emac start tx * * @param dev * */ void bflb_emac_start_tx(struct bflb_device_s *dev) { uint32_t reg_val; reg_val = getreg32(dev->reg_base + EMAC_MODE_OFFSET); reg_val |= (EMAC_TX_EN); putreg32(reg_val, dev->reg_base + EMAC_MODE_OFFSET); } /** * @brief bflb emac stop tx * * @param dev * */ void bflb_emac_stop_tx(struct bflb_device_s *dev) { uint32_t reg_val; reg_val = getreg32(dev->reg_base + EMAC_MODE_OFFSET); reg_val &= ~(EMAC_TX_EN); putreg32(reg_val, dev->reg_base + EMAC_MODE_OFFSET); } /** * @brief bflb emac start rx * * @param dev * */ void bflb_emac_start_rx(struct bflb_device_s *dev) { uint32_t reg_val; reg_val = getreg32(dev->reg_base + EMAC_MODE_OFFSET); reg_val |= (EMAC_RX_EN); putreg32(reg_val, dev->reg_base + EMAC_MODE_OFFSET); } /** * @brief bflb emac stop rx * * @param dev * */ void bflb_emac_stop_rx(struct bflb_device_s *dev) { uint32_t reg_val; reg_val = getreg32(dev->reg_base + EMAC_MODE_OFFSET); reg_val &= ~(EMAC_RX_EN); putreg32(reg_val, dev->reg_base + EMAC_MODE_OFFSET); }