/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ /* Qualcomm Technologies, Inc. EMAC Ethernet Controller Hardware support */ #include #include #include #include #include #include "emac_hw.h" #include "emac_ptp.h" #define RFD_PREF_LOW_TH 0x10 #define RFD_PREF_UP_TH 0x10 #define JUMBO_1KAH 0x4 #define RXF_DOF_TH 0x0be #define RXF_UOF_TH 0x1a0 #define RXD_TH 0x100 /* RGMII specific macros */ #define EMAC_RGMII_PLL_LOCK_TIMEOUT (HZ / 1000) /* 1ms */ #define EMAC_RGMII_CORE_IE_C 0x2001 #define EMAC_RGMII_PLL_L_VAL 0x14 #define EMAC_RGMII_PHY_MODE 0 /* REG */ u32 emac_reg_r32(struct emac_hw *hw, u8 base, u32 reg) { return readl_relaxed(hw->reg_addr[base] + reg); } void emac_reg_w32(struct emac_hw *hw, u8 base, u32 reg, u32 val) { writel_relaxed(val, hw->reg_addr[base] + reg); } void emac_reg_update32(struct emac_hw *hw, u8 base, u32 reg, u32 mask, u32 val) { u32 data; data = emac_reg_r32(hw, base, reg); emac_reg_w32(hw, base, reg, ((data & ~mask) | val)); } u32 emac_reg_field_r32(struct emac_hw *hw, u8 base, u32 reg, u32 mask, u32 shift) { u32 data; data = emac_reg_r32(hw, base, reg); return (data & mask) >> shift; } /* INTR */ void emac_hw_enable_intr(struct emac_hw *hw) { struct emac_adapter *adpt = emac_hw_get_adap(hw); int i; for (i = 0; i < EMAC_NUM_CORE_IRQ; i++) { struct emac_irq_per_dev *irq = &adpt->irq[i]; const struct emac_irq_common *irq_cmn = &emac_irq_cmn_tbl[i]; emac_reg_w32(hw, EMAC, irq_cmn->status_reg, ~DIS_INT); emac_reg_w32(hw, EMAC, irq_cmn->mask_reg, irq->mask); } if (adpt->tstamp_en) emac_reg_w32(hw, EMAC_1588, EMAC_P1588_PTP_EXPANDED_INT_MASK, hw->ptp_intr_mask); wmb(); /* ensure that irq and ptp setting are flushed to HW */ } void emac_hw_disable_intr(struct emac_hw *hw) { struct emac_adapter *adpt = emac_hw_get_adap(hw); int i; for (i = 0; i < EMAC_NUM_CORE_IRQ; i++) { const struct emac_irq_common *irq_cmn = &emac_irq_cmn_tbl[i]; emac_reg_w32(hw, EMAC, irq_cmn->status_reg, DIS_INT); emac_reg_w32(hw, EMAC, irq_cmn->mask_reg, 0); } if (adpt->tstamp_en) emac_reg_w32(hw, EMAC_1588, EMAC_P1588_PTP_EXPANDED_INT_MASK, 0); wmb(); /* ensure that irq and ptp setting are flushed to HW */ } /* MC */ void emac_hw_set_mc_addr(struct emac_hw *hw, u8 *addr) { u32 crc32, bit, reg, mta; /* Calculate the CRC of the MAC address */ crc32 = ether_crc(ETH_ALEN, addr); /* The HASH Table is an array of 2 32-bit registers. It is * treated like an array of 64 bits (BitArray[hash_value]). * Use the upper 6 bits of the above CRC as the hash value. */ reg = (crc32 >> 31) & 0x1; bit = (crc32 >> 26) & 0x1F; mta = emac_reg_r32(hw, EMAC, EMAC_HASH_TAB_REG0 + (reg << 2)); mta |= (0x1 << bit); emac_reg_w32(hw, EMAC, EMAC_HASH_TAB_REG0 + (reg << 2), mta); wmb(); /* ensure that the mac address is flushed to HW */ } void emac_hw_clear_mc_addr(struct emac_hw *hw) { emac_reg_w32(hw, EMAC, EMAC_HASH_TAB_REG0, 0); emac_reg_w32(hw, EMAC, EMAC_HASH_TAB_REG1, 0); wmb(); /* ensure that clearing the mac address is flushed to HW */ } /* definitions for RSS */ #define EMAC_RSS_KEY(_i, _type) \ (EMAC_RSS_KEY0 + ((_i) * sizeof(_type))) #define EMAC_RSS_TBL(_i, _type) \ (EMAC_IDT_TABLE0 + ((_i) * sizeof(_type))) /* RSS */ void emac_hw_config_rss(struct emac_hw *hw) { int key_len_by_u32 = sizeof(hw->rss_key) / sizeof(u32); int idt_len_by_u32 = sizeof(hw->rss_idt) / sizeof(u32); u32 rxq0; int i; /* Fill out hash function keys */ for (i = 0; i < key_len_by_u32; i++) { u32 key, idx_base; idx_base = (key_len_by_u32 - i) * 4; key = ((hw->rss_key[idx_base - 1]) | (hw->rss_key[idx_base - 2] << 8) | (hw->rss_key[idx_base - 3] << 16) | (hw->rss_key[idx_base - 4] << 24)); emac_reg_w32(hw, EMAC, EMAC_RSS_KEY(i, u32), key); } /* Fill out redirection table */ for (i = 0; i < idt_len_by_u32; i++) emac_reg_w32(hw, EMAC, EMAC_RSS_TBL(i, u32), hw->rss_idt[i]); emac_reg_w32(hw, EMAC, EMAC_BASE_CPU_NUMBER, hw->rss_base_cpu); rxq0 = emac_reg_r32(hw, EMAC, EMAC_RXQ_CTRL_0); if (hw->rss_hstype & EMAC_RSS_HSTYP_IPV4_EN) rxq0 |= RXQ0_RSS_HSTYP_IPV4_EN; else rxq0 &= ~RXQ0_RSS_HSTYP_IPV4_EN; if (hw->rss_hstype & EMAC_RSS_HSTYP_TCP4_EN) rxq0 |= RXQ0_RSS_HSTYP_IPV4_TCP_EN; else rxq0 &= ~RXQ0_RSS_HSTYP_IPV4_TCP_EN; if (hw->rss_hstype & EMAC_RSS_HSTYP_IPV6_EN) rxq0 |= RXQ0_RSS_HSTYP_IPV6_EN; else rxq0 &= ~RXQ0_RSS_HSTYP_IPV6_EN; if (hw->rss_hstype & EMAC_RSS_HSTYP_TCP6_EN) rxq0 |= RXQ0_RSS_HSTYP_IPV6_TCP_EN; else rxq0 &= ~RXQ0_RSS_HSTYP_IPV6_TCP_EN; rxq0 |= ((hw->rss_idt_size << IDT_TABLE_SIZE_SHFT) & IDT_TABLE_SIZE_BMSK); rxq0 |= RSS_HASH_EN; wmb(); /* ensure all parameters are written before we enable RSS */ emac_reg_w32(hw, EMAC, EMAC_RXQ_CTRL_0, rxq0); wmb(); /* ensure that enabling RSS is flushed to HW */ } /* Config MAC modes */ void emac_hw_config_mac_ctrl(struct emac_hw *hw) { u32 mac; mac = emac_reg_r32(hw, EMAC, EMAC_MAC_CTRL); if (TEST_FLAG(hw, HW_VLANSTRIP_EN)) mac |= VLAN_STRIP; else mac &= ~VLAN_STRIP; if (TEST_FLAG(hw, HW_PROMISC_EN)) mac |= PROM_MODE; else mac &= ~PROM_MODE; if (TEST_FLAG(hw, HW_MULTIALL_EN)) mac |= MULTI_ALL; else mac &= ~MULTI_ALL; if (TEST_FLAG(hw, HW_LOOPBACK_EN)) mac |= MAC_LP_EN; else mac &= ~MAC_LP_EN; emac_reg_w32(hw, EMAC, EMAC_MAC_CTRL, mac); wmb(); /* ensure MAC setting is flushed to HW */ } /* Wake On LAN (WOL) */ void emac_hw_config_wol(struct emac_hw *hw, u32 wufc) { u32 wol = 0; /* turn on magic packet event */ if (wufc & EMAC_WOL_MAGIC) wol |= MG_FRAME_EN | MG_FRAME_PME | WK_FRAME_EN; /* turn on link up event */ if (wufc & EMAC_WOL_PHY) wol |= LK_CHG_EN | LK_CHG_PME; emac_reg_w32(hw, EMAC, EMAC_WOL_CTRL0, wol); wmb(); /* ensure that WOL setting is flushed to HW */ } /* Power Management */ void emac_hw_config_pow_save(struct emac_hw *hw, u32 speed, bool wol_en, bool rx_en) { u32 dma_mas, mac; dma_mas = emac_reg_r32(hw, EMAC, EMAC_DMA_MAS_CTRL); dma_mas &= ~LPW_CLK_SEL; dma_mas |= LPW_STATE; mac = emac_reg_r32(hw, EMAC, EMAC_MAC_CTRL); mac &= ~(FULLD | RXEN | TXEN); mac = (mac & ~SPEED_BMSK) | (((u32)emac_mac_speed_10_100 << SPEED_SHFT) & SPEED_BMSK); if (wol_en) { if (rx_en) mac |= (RXEN | BROAD_EN); /* If WOL is enabled, set link speed/duplex for mac */ if (EMAC_LINK_SPEED_1GB_FULL == speed) mac = (mac & ~SPEED_BMSK) | (((u32)emac_mac_speed_1000 << SPEED_SHFT) & SPEED_BMSK); if (EMAC_LINK_SPEED_10_FULL == speed || EMAC_LINK_SPEED_100_FULL == speed || EMAC_LINK_SPEED_1GB_FULL == speed) mac |= FULLD; } else { /* select lower clock speed if WOL is disabled */ dma_mas |= LPW_CLK_SEL; } emac_reg_w32(hw, EMAC, EMAC_DMA_MAS_CTRL, dma_mas); emac_reg_w32(hw, EMAC, EMAC_MAC_CTRL, mac); wmb(); /* ensure that power setting is flushed to HW */ } /* Config descriptor rings */ static void emac_hw_config_ring_ctrl(struct emac_hw *hw) { struct emac_adapter *adpt = emac_hw_get_adap(hw); if (adpt->tstamp_en) { emac_reg_update32(hw, EMAC_CSR, EMAC_EMAC_WRAPPER_CSR1, 0, ENABLE_RRD_TIMESTAMP); } /* TPD */ emac_reg_w32(hw, EMAC, EMAC_DESC_CTRL_1, EMAC_DMA_ADDR_HI(adpt->tx_queue[0].tpd.tpdma)); switch (adpt->num_txques) { case 4: emac_reg_w32(hw, EMAC, EMAC_H3TPD_BASE_ADDR_LO, EMAC_DMA_ADDR_LO(adpt->tx_queue[3].tpd.tpdma)); /* fall through */ case 3: emac_reg_w32(hw, EMAC, EMAC_H2TPD_BASE_ADDR_LO, EMAC_DMA_ADDR_LO(adpt->tx_queue[2].tpd.tpdma)); /* fall through */ case 2: emac_reg_w32(hw, EMAC, EMAC_H1TPD_BASE_ADDR_LO, EMAC_DMA_ADDR_LO(adpt->tx_queue[1].tpd.tpdma)); /* fall through */ case 1: emac_reg_w32(hw, EMAC, EMAC_DESC_CTRL_8, EMAC_DMA_ADDR_LO(adpt->tx_queue[0].tpd.tpdma)); break; default: emac_err(adpt, "Invalid number of TX queues (%d)\n", adpt->num_txques); return; } emac_reg_w32(hw, EMAC, EMAC_DESC_CTRL_9, adpt->tx_queue[0].tpd.count & TPD_RING_SIZE_BMSK); /* RFD & RRD */ emac_reg_w32(hw, EMAC, EMAC_DESC_CTRL_0, EMAC_DMA_ADDR_HI(adpt->rx_queue[0].rfd.rfdma)); switch (adpt->num_rxques) { case 4: emac_reg_w32(hw, EMAC, EMAC_DESC_CTRL_13, EMAC_DMA_ADDR_LO(adpt->rx_queue[3].rfd.rfdma)); emac_reg_w32(hw, EMAC, EMAC_DESC_CTRL_16, EMAC_DMA_ADDR_LO(adpt->rx_queue[3].rrd.rrdma)); /* fall through */ case 3: emac_reg_w32(hw, EMAC, EMAC_DESC_CTRL_12, EMAC_DMA_ADDR_LO(adpt->rx_queue[2].rfd.rfdma)); emac_reg_w32(hw, EMAC, EMAC_DESC_CTRL_15, EMAC_DMA_ADDR_LO(adpt->rx_queue[2].rrd.rrdma)); /* fall through */ case 2: emac_reg_w32(hw, EMAC, EMAC_DESC_CTRL_10, EMAC_DMA_ADDR_LO(adpt->rx_queue[1].rfd.rfdma)); emac_reg_w32(hw, EMAC, EMAC_DESC_CTRL_14, EMAC_DMA_ADDR_LO(adpt->rx_queue[1].rrd.rrdma)); /* fall through */ case 1: emac_reg_w32(hw, EMAC, EMAC_DESC_CTRL_2, EMAC_DMA_ADDR_LO(adpt->rx_queue[0].rfd.rfdma)); emac_reg_w32(hw, EMAC, EMAC_DESC_CTRL_5, EMAC_DMA_ADDR_LO(adpt->rx_queue[0].rrd.rrdma)); break; default: emac_err(adpt, "Invalid number of RX queues (%d)\n", adpt->num_rxques); return; } emac_reg_w32(hw, EMAC, EMAC_DESC_CTRL_3, adpt->rx_queue[0].rfd.count & RFD_RING_SIZE_BMSK); emac_reg_w32(hw, EMAC, EMAC_DESC_CTRL_6, adpt->rx_queue[0].rrd.count & RRD_RING_SIZE_BMSK); emac_reg_w32(hw, EMAC, EMAC_DESC_CTRL_4, adpt->rxbuf_size & RX_BUFFER_SIZE_BMSK); emac_reg_w32(hw, EMAC, EMAC_DESC_CTRL_11, 0); wmb(); /* ensure all parameters are written before we enable them */ /* Load all of base address above */ emac_reg_w32(hw, EMAC, EMAC_INTER_SRAM_PART9, 1); wmb(); /* ensure triggering HW to read ring pointers is flushed */ } /* Config transmit parameters */ static void emac_hw_config_tx_ctrl(struct emac_hw *hw) { u16 tx_offload_thresh = EMAC_MAX_TX_OFFLOAD_THRESH; u32 val; emac_reg_w32(hw, EMAC, EMAC_TXQ_CTRL_1, (tx_offload_thresh >> 3) & JUMBO_TASK_OFFLOAD_THRESHOLD_BMSK); val = (hw->tpd_burst << NUM_TPD_BURST_PREF_SHFT) & NUM_TPD_BURST_PREF_BMSK; val |= (TXQ_MODE | LS_8023_SP); val |= (0x0100 << NUM_TXF_BURST_PREF_SHFT) & NUM_TXF_BURST_PREF_BMSK; emac_reg_w32(hw, EMAC, EMAC_TXQ_CTRL_0, val); emac_reg_update32(hw, EMAC, EMAC_TXQ_CTRL_2, (TXF_HWM_BMSK | TXF_LWM_BMSK), 0); wmb(); /* ensure that Tx control settings are flushed to HW */ } /* Config receive parameters */ static void emac_hw_config_rx_ctrl(struct emac_hw *hw) { u32 val; val = ((hw->rfd_burst << NUM_RFD_BURST_PREF_SHFT) & NUM_RFD_BURST_PREF_BMSK); val |= (SP_IPV6 | CUT_THRU_EN); emac_reg_w32(hw, EMAC, EMAC_RXQ_CTRL_0, val); val = emac_reg_r32(hw, EMAC, EMAC_RXQ_CTRL_1); val &= ~(JUMBO_1KAH_BMSK | RFD_PREF_LOW_THRESHOLD_BMSK | RFD_PREF_UP_THRESHOLD_BMSK); val |= (JUMBO_1KAH << JUMBO_1KAH_SHFT) | (RFD_PREF_LOW_TH << RFD_PREF_LOW_THRESHOLD_SHFT) | (RFD_PREF_UP_TH << RFD_PREF_UP_THRESHOLD_SHFT); emac_reg_w32(hw, EMAC, EMAC_RXQ_CTRL_1, val); val = emac_reg_r32(hw, EMAC, EMAC_RXQ_CTRL_2); val &= ~(RXF_DOF_THRESHOLD_BMSK | RXF_UOF_THRESHOLD_BMSK); val |= (RXF_DOF_TH << RXF_DOF_THRESHOLD_SHFT) | (RXF_UOF_TH << RXF_UOF_THRESHOLD_SHFT); emac_reg_w32(hw, EMAC, EMAC_RXQ_CTRL_2, val); val = emac_reg_r32(hw, EMAC, EMAC_RXQ_CTRL_3); val &= ~(RXD_TIMER_BMSK | RXD_THRESHOLD_BMSK); val |= RXD_TH << RXD_THRESHOLD_SHFT; emac_reg_w32(hw, EMAC, EMAC_RXQ_CTRL_3, val); wmb(); /* ensure that Rx control settings are flushed to HW */ } /* Config dma */ static void emac_hw_config_dma_ctrl(struct emac_hw *hw) { u32 dma_ctrl; dma_ctrl = DMAR_REQ_PRI; switch (hw->dma_order) { case emac_dma_ord_in: dma_ctrl |= IN_ORDER_MODE; break; case emac_dma_ord_enh: dma_ctrl |= ENH_ORDER_MODE; break; case emac_dma_ord_out: dma_ctrl |= OUT_ORDER_MODE; break; default: break; } dma_ctrl |= (((u32)hw->dmar_block) << REGRDBLEN_SHFT) & REGRDBLEN_BMSK; dma_ctrl |= (((u32)hw->dmaw_block) << REGWRBLEN_SHFT) & REGWRBLEN_BMSK; dma_ctrl |= (((u32)hw->dmar_dly_cnt) << DMAR_DLY_CNT_SHFT) & DMAR_DLY_CNT_BMSK; dma_ctrl |= (((u32)hw->dmaw_dly_cnt) << DMAW_DLY_CNT_SHFT) & DMAW_DLY_CNT_BMSK; emac_reg_w32(hw, EMAC, EMAC_DMA_CTRL, dma_ctrl); wmb(); /* ensure that the DMA configuration is flushed to HW */ } /* Configure MAC */ void emac_hw_config_mac(struct emac_hw *hw) { u32 val; emac_hw_set_mac_addr(hw, hw->mac_addr); emac_hw_config_ring_ctrl(hw); emac_reg_w32(hw, EMAC, EMAC_MAX_FRAM_LEN_CTRL, hw->mtu + ETH_HLEN + VLAN_HLEN + ETH_FCS_LEN); emac_hw_config_tx_ctrl(hw); emac_hw_config_rx_ctrl(hw); emac_hw_config_dma_ctrl(hw); if (TEST_FLAG(hw, HW_PTP_CAP)) emac_ptp_config(hw); val = emac_reg_r32(hw, EMAC, EMAC_AXI_MAST_CTRL); val &= ~(DATA_BYTE_SWAP | MAX_BOUND); val |= MAX_BTYPE; emac_reg_w32(hw, EMAC, EMAC_AXI_MAST_CTRL, val); emac_reg_w32(hw, EMAC, EMAC_CLK_GATE_CTRL, 0); emac_reg_w32(hw, EMAC, EMAC_MISC_CTRL, RX_UNCPL_INT_EN); wmb(); /* ensure that the MAC configuration is flushed to HW */ } /* Reset MAC */ void emac_hw_reset_mac(struct emac_hw *hw) { emac_reg_w32(hw, EMAC, EMAC_INT_MASK, 0); emac_reg_w32(hw, EMAC, EMAC_INT_STATUS, DIS_INT); emac_hw_stop_mac(hw); emac_reg_update32(hw, EMAC, EMAC_DMA_MAS_CTRL, 0, SOFT_RST); wmb(); /* ensure mac is fully reset */ usleep_range(100, 150); emac_reg_update32(hw, EMAC, EMAC_DMA_MAS_CTRL, 0, INT_RD_CLR_EN); wmb(); /* ensure the interrupt clear-on-read setting is flushed to HW */ } /* Start MAC */ void emac_hw_start_mac(struct emac_hw *hw) { struct emac_adapter *adpt = emac_hw_get_adap(hw); struct emac_phy *phy = &adpt->phy; u32 mac, csr1; /* enable tx queue */ if (adpt->num_txques && (adpt->num_txques <= EMAC_MAX_TX_QUEUES)) emac_reg_update32(hw, EMAC, EMAC_TXQ_CTRL_0, 0, TXQ_EN); /* enable rx queue */ if (adpt->num_rxques && (adpt->num_rxques <= EMAC_MAX_RX_QUEUES)) emac_reg_update32(hw, EMAC, EMAC_RXQ_CTRL_0, 0, RXQ_EN); /* enable mac control */ mac = emac_reg_r32(hw, EMAC, EMAC_MAC_CTRL); csr1 = emac_reg_r32(hw, EMAC_CSR, EMAC_EMAC_WRAPPER_CSR1); mac |= TXEN | RXEN; /* enable RX/TX */ /* enable RX/TX Flow Control */ switch (phy->cur_fc_mode) { case EMAC_FC_FULL: mac |= (TXFC | RXFC); break; case EMAC_FC_RX_PAUSE: mac |= RXFC; break; case EMAC_FC_TX_PAUSE: mac |= TXFC; break; default: break; } /* setup link speed */ mac &= ~SPEED_BMSK; switch (phy->link_speed) { case EMAC_LINK_SPEED_1GB_FULL: mac |= ((emac_mac_speed_1000 << SPEED_SHFT) & SPEED_BMSK); csr1 |= FREQ_MODE; break; default: mac |= ((emac_mac_speed_10_100 << SPEED_SHFT) & SPEED_BMSK); csr1 &= ~FREQ_MODE; break; } switch (phy->link_speed) { case EMAC_LINK_SPEED_1GB_FULL: case EMAC_LINK_SPEED_100_FULL: case EMAC_LINK_SPEED_10_FULL: mac |= FULLD; break; default: mac &= ~FULLD; } /* other parameters */ mac |= (CRCE | PCRCE); mac |= ((hw->preamble << PRLEN_SHFT) & PRLEN_BMSK); mac |= BROAD_EN; mac |= FLCHK; mac &= ~RX_CHKSUM_EN; mac &= ~(HUGEN | VLAN_STRIP | TPAUSE | SIMR | HUGE | MULTI_ALL | DEBUG_MODE | SINGLE_PAUSE_MODE); emac_reg_w32(hw, EMAC_CSR, EMAC_EMAC_WRAPPER_CSR1, csr1); emac_reg_w32(hw, EMAC, EMAC_MAC_CTRL, mac); /* enable interrupt read clear, low power sleep mode and * the irq moderators */ emac_reg_w32(hw, EMAC, EMAC_IRQ_MOD_TIM_INIT, hw->irq_mod); emac_reg_w32(hw, EMAC, EMAC_DMA_MAS_CTRL, (INT_RD_CLR_EN | LPW_MODE | IRQ_MODERATOR_EN | IRQ_MODERATOR2_EN)); if (TEST_FLAG(hw, HW_PTP_CAP)) emac_ptp_set_linkspeed(hw, phy->link_speed); emac_hw_config_mac_ctrl(hw); emac_reg_update32(hw, EMAC, EMAC_ATHR_HEADER_CTRL, (HEADER_ENABLE | HEADER_CNT_EN), 0); emac_reg_update32(hw, EMAC_CSR, EMAC_EMAC_WRAPPER_CSR2, 0, WOL_EN); wmb(); /* ensure that MAC setting are flushed to HW */ } /* Stop MAC */ void emac_hw_stop_mac(struct emac_hw *hw) { emac_reg_update32(hw, EMAC, EMAC_RXQ_CTRL_0, RXQ_EN, 0); emac_reg_update32(hw, EMAC, EMAC_TXQ_CTRL_0, TXQ_EN, 0); emac_reg_update32(hw, EMAC, EMAC_MAC_CTRL, (TXEN | RXEN), 0); wmb(); /* ensure mac is stopped before we proceed */ usleep_range(1000, 1050); } /* set MAC address */ void emac_hw_set_mac_addr(struct emac_hw *hw, u8 *addr) { u32 sta; /* for example: 00-A0-C6-11-22-33 * 0<-->C6112233, 1<-->00A0. */ /* low dword */ sta = (((u32)addr[2]) << 24) | (((u32)addr[3]) << 16) | (((u32)addr[4]) << 8) | (((u32)addr[5])); emac_reg_w32(hw, EMAC, EMAC_MAC_STA_ADDR0, sta); /* hight dword */ sta = (((u32)addr[0]) << 8) | (((u32)addr[1])); emac_reg_w32(hw, EMAC, EMAC_MAC_STA_ADDR1, sta); wmb(); /* ensure that the MAC address is flushed to HW */ } /* Read one entry from the HW tx timestamp FIFO */ bool emac_hw_read_tx_tstamp(struct emac_hw *hw, struct emac_hwtxtstamp *ts) { u32 ts_idx; ts_idx = emac_reg_r32(hw, EMAC_CSR, EMAC_EMAC_WRAPPER_TX_TS_INX); if (ts_idx & EMAC_WRAPPER_TX_TS_EMPTY) return false; ts->ns = emac_reg_r32(hw, EMAC_CSR, EMAC_EMAC_WRAPPER_TX_TS_LO); ts->sec = emac_reg_r32(hw, EMAC_CSR, EMAC_EMAC_WRAPPER_TX_TS_HI); ts->ts_idx = ts_idx & EMAC_WRAPPER_TX_TS_INX_BMSK; return true; }