| /* Mediatek STAR MAC network driver. |
| * |
| * Copyright (c) 2016-2017 Mediatek Corporation |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation. |
| */ |
| |
| #include "star.h" |
| |
| #ifdef CONFIG_ARM64 |
| uintptr_t tx_skb_reserve[TX_DESC_NUM]; |
| uintptr_t rx_skb_reserve[RX_DESC_NUM]; |
| #endif |
| |
| u16 star_mdc_mdio_read(star_dev *dev, u32 phy_addr, u32 phy_reg) |
| { |
| u16 data; |
| u32 phy_ctl; |
| void __iomem *base = dev->base; |
| |
| /* Clear previous read/write OK status (write 1 clear) */ |
| star_set_reg(STAR_PHY_CTRL0(base), STAR_PHY_CTRL0_RWOK); |
| phy_ctl = (phy_addr & STAR_PHY_CTRL0_PA_MASK) |
| << STAR_PHY_CTRL0_PA_OFFSET | |
| (phy_reg & STAR_PHY_CTRL0_PREG_MASK) |
| << STAR_PHY_CTRL0_PREG_OFFSET | |
| STAR_PHY_CTRL0_RDCMD; |
| star_mb(); |
| star_set_reg(STAR_PHY_CTRL0(base), phy_ctl); |
| star_mb(); |
| |
| STAR_POLLING_TIMEOUT(star_is_set_bit(STAR_PHY_CTRL0(base), |
| STAR_PHY_CTRL0_RWOK)); |
| star_mb(); |
| data = (u16)star_get_bit_mask(STAR_PHY_CTRL0(base), |
| STAR_PHY_CTRL0_RWDATA_MASK, |
| STAR_PHY_CTRL0_RWDATA_OFFSET); |
| |
| return data; |
| } |
| |
| void star_mdc_mdio_write(star_dev *dev, u32 phy_addr, u32 phy_reg, u16 value) |
| { |
| u32 phy_ctl; |
| void __iomem *base = dev->base; |
| |
| /* Clear previous read/write OK status (write 1 clear) */ |
| star_set_reg(STAR_PHY_CTRL0(base), STAR_PHY_CTRL0_RWOK); |
| phy_ctl = ((value & STAR_PHY_CTRL0_RWDATA_MASK) |
| << STAR_PHY_CTRL0_RWDATA_OFFSET) | |
| ((phy_addr & STAR_PHY_CTRL0_PA_MASK) |
| << STAR_PHY_CTRL0_PA_OFFSET) | |
| ((phy_reg & STAR_PHY_CTRL0_PREG_MASK) |
| << STAR_PHY_CTRL0_PREG_OFFSET) | |
| STAR_PHY_CTRL0_WTCMD; |
| star_mb(); |
| star_set_reg(STAR_PHY_CTRL0(base), phy_ctl); |
| star_mb(); |
| STAR_POLLING_TIMEOUT(star_is_set_bit(STAR_PHY_CTRL0(base), |
| STAR_PHY_CTRL0_RWOK)); |
| } |
| |
| static void desc_tx_init(tx_desc *tx_desc, u32 is_eor) |
| { |
| tx_desc->buffer = 0; |
| tx_desc->ctrl_len = TX_COWN | (is_eor ? TX_EOR : 0); |
| tx_desc->vtag = 0; |
| tx_desc->reserve = 0; |
| } |
| |
| static void desc_rx_init(rx_desc *rx_desc, u32 is_eor) |
| { |
| rx_desc->buffer = 0; |
| rx_desc->ctrl_len = RX_COWN | (is_eor ? RX_EOR : 0); |
| rx_desc->vtag = 0; |
| rx_desc->reserve = 0; |
| } |
| |
| static void desc_tx_take(tx_desc *tx_desc) |
| { |
| if (desc_tx_dma(tx_desc)) |
| tx_desc->ctrl_len |= TX_COWN; |
| } |
| |
| static void desc_rx_take(rx_desc *rx_desc) |
| { |
| if (desc_rx_dma(rx_desc)) |
| rx_desc->ctrl_len |= RX_COWN; |
| } |
| |
| int star_dma_init(star_dev *dev, uintptr_t desc_viraddr, |
| dma_addr_t desc_dmaaddrdr) |
| { |
| int i; |
| void __iomem *base = dev->base; |
| |
| STAR_MSG(STAR_VERB, "%s virAddr=0x%lx\n", __func__, desc_viraddr); |
| dev->tx_ring_size = TX_DESC_NUM; |
| dev->rx_ring_size = RX_DESC_NUM; |
| |
| dev->tx_desc = (tx_desc *)desc_viraddr; |
| dev->rx_desc = (rx_desc *)dev->tx_desc + dev->tx_ring_size; |
| |
| for (i = 0; i < dev->tx_ring_size; i++) |
| desc_tx_init(dev->tx_desc + i, i == dev->tx_ring_size - 1); |
| for (i = 0; i < dev->rx_ring_size; i++) |
| desc_rx_init(dev->rx_desc + i, i == dev->rx_ring_size - 1); |
| |
| dev->tx_head = 0; |
| dev->tx_tail = 0; |
| dev->rx_head = 0; |
| dev->rx_tail = 0; |
| dev->tx_num = 0; |
| dev->rx_num = 0; |
| |
| /* Set Tx/Rx descriptor address */ |
| star_set_reg(STAR_TX_BASE_ADDR(base), (u32)desc_dmaaddrdr); |
| star_set_reg(STAR_TX_DPTR(base), (u32)desc_dmaaddrdr); |
| star_set_reg(STAR_RX_BASE_ADDR(base), |
| (u32)desc_dmaaddrdr + sizeof(tx_desc) * dev->tx_ring_size); |
| star_set_reg(STAR_RX_DPTR(base), |
| (u32)desc_dmaaddrdr + sizeof(tx_desc) * dev->tx_ring_size); |
| |
| star_intr_disable(dev); |
| |
| return 0; |
| } |
| |
| int star_dma_tx_set(star_dev *dev, u32 buffer, u32 length, uintptr_t ext_buf) |
| { |
| int is_tx_last; |
| int desc_idx = dev->tx_head; |
| tx_desc *tx_desc = dev->tx_desc + desc_idx; |
| u32 len = (((length < 60) ? 60 : length) & TX_LEN_MASK) |
| << TX_LEN_OFFSET; |
| |
| /* Error checking */ |
| if (dev->tx_num == dev->tx_ring_size) |
| goto err; |
| /* descriptor is not empty - cannot set */ |
| if (!desc_tx_empty(tx_desc)) |
| goto err; |
| |
| tx_desc->buffer = buffer; |
| tx_desc->ctrl_len |= len | TX_FS | TX_LS | TX_INT; |
| #ifdef CONFIG_ARM64 |
| tx_skb_reserve[desc_idx] = ext_buf; |
| #else |
| tx_desc->reserve = ext_buf; |
| #endif |
| /* star memory barrier */ |
| wmb(); |
| /* Set HW own */ |
| tx_desc->ctrl_len &= ~TX_COWN; |
| |
| dev->tx_num++; |
| is_tx_last = desc_tx_last(tx_desc); |
| dev->tx_head = is_tx_last ? 0 : desc_idx + 1; |
| |
| return desc_idx; |
| err: |
| return -1; |
| } |
| |
| int star_dma_tx_get(star_dev *dev, u32 *buffer, |
| u32 *ctrl_len, uintptr_t *ext_buf) |
| { |
| int is_tx_last; |
| int desc_idx = dev->tx_tail; |
| tx_desc *tx_desc = dev->tx_desc + desc_idx; |
| |
| if (dev->tx_num == 0) |
| goto err; |
| if (desc_tx_dma(tx_desc)) |
| goto err; |
| if (desc_tx_empty(tx_desc)) |
| goto err; |
| |
| if (buffer != 0) |
| *buffer = tx_desc->buffer; |
| if (ctrl_len != 0) |
| *ctrl_len = tx_desc->ctrl_len; |
| |
| #ifdef CONFIG_ARM64 |
| if (ext_buf != 0) |
| *ext_buf = tx_skb_reserve[desc_idx]; |
| #else |
| if (ext_buf != 0) |
| *ext_buf = tx_desc->reserve; |
| #endif |
| /* add star memory barrier */ |
| rmb(); |
| |
| desc_tx_init(tx_desc, desc_tx_last(tx_desc)); |
| dev->tx_num--; |
| is_tx_last = desc_tx_last(tx_desc); |
| dev->tx_tail = is_tx_last ? 0 : desc_idx + 1; |
| |
| return desc_idx; |
| err: |
| return -1; |
| } |
| |
| int star_dma_rx_set(star_dev *dev, u32 buffer, u32 length, uintptr_t ext_buf) |
| { |
| int desc_idx = dev->rx_head; |
| rx_desc *rx_desc = dev->rx_desc + desc_idx; |
| int is_rx_last; |
| |
| /* Error checking */ |
| if (dev->rx_num == dev->rx_ring_size) |
| goto err; |
| /* descriptor is not empty - cannot set */ |
| if (!desc_rx_empty(rx_desc)) |
| goto err; |
| |
| rx_desc->buffer = buffer; |
| rx_desc->ctrl_len |= ((length & RX_LEN_MASK) << RX_LEN_OFFSET); |
| #ifdef CONFIG_ARM64 |
| rx_skb_reserve[desc_idx] = ext_buf; |
| #else |
| rx_desc->reserve = ext_buf; |
| #endif |
| /* star memory barrier */ |
| wmb(); |
| /* Set HW own */ |
| rx_desc->ctrl_len &= ~RX_COWN; |
| |
| dev->rx_num++; |
| is_rx_last = desc_rx_last(rx_desc); |
| dev->rx_head = is_rx_last ? 0 : desc_idx + 1; |
| |
| return desc_idx; |
| err: |
| return -1; |
| } |
| |
| int star_dma_rx_get(star_dev *dev, u32 *buffer, |
| u32 *ctrl_len, uintptr_t *ext_buf) |
| { |
| int is_rx_last; |
| int desc_idx = dev->rx_tail; |
| rx_desc *rx_desc = dev->rx_desc + desc_idx; |
| |
| /* Error checking */ |
| /* No buffer can be got */ |
| if (dev->rx_num == 0) |
| goto err; |
| /* descriptor is owned by DMA - cannot get */ |
| if (desc_rx_dma(rx_desc)) |
| goto err; |
| /* descriptor is empty - cannot get */ |
| if (desc_rx_empty(rx_desc)) |
| goto err; |
| |
| if (buffer != 0) |
| *buffer = rx_desc->buffer; |
| if (ctrl_len != 0) |
| *ctrl_len = rx_desc->ctrl_len; |
| #ifdef CONFIG_ARM64 |
| if (ext_buf != 0) |
| *ext_buf = rx_skb_reserve[desc_idx]; |
| #else |
| if (ext_buf != 0) |
| *ext_buf = rx_desc->reserve; |
| #endif |
| /* star memory barrier */ |
| rmb(); |
| |
| desc_rx_init(rx_desc, desc_rx_last(rx_desc)); |
| dev->rx_num--; |
| is_rx_last = desc_rx_last(rx_desc); |
| dev->rx_tail = is_rx_last ? 0 : desc_idx + 1; |
| |
| return desc_idx; |
| err: |
| return -1; |
| } |
| |
| void star_dma_tx_stop(star_dev *dev) |
| { |
| int i; |
| |
| star_dma_tx_disable(dev); |
| for (i = 0; i < dev->tx_ring_size; i++) |
| desc_tx_take(dev->tx_desc + i); |
| } |
| |
| void star_dma_rx_stop(star_dev *dev) |
| { |
| int i; |
| |
| star_dma_rx_disable(dev); |
| for (i = 0; i < dev->rx_ring_size; i++) |
| desc_rx_take(dev->rx_desc + i); |
| } |
| |
| int star_mac_init(star_dev *dev, u8 mac_addr[6]) |
| { |
| void __iomem *base = dev->base; |
| |
| STAR_MSG(STAR_VERB, "MAC Initialization\n"); |
| |
| /* Set Mac Address */ |
| star_set_reg(star_my_mac_h(base), |
| mac_addr[0] << 8 | mac_addr[1] << 0); |
| star_set_reg(star_my_mac_l(base), |
| mac_addr[2] << 24 | mac_addr[3] << 16 | |
| mac_addr[4] << 8 | mac_addr[5] << 0); |
| |
| /* Set Mac Configuration */ |
| star_set_reg(STAR_MAC_CFG(base), |
| STAR_MAC_CFG_CRCSTRIP | |
| STAR_MAC_CFG_MAXLEN_1522 | |
| /* 12 byte IPG */ |
| (0x1f & STAR_MAC_CFG_IPG_MASK) << STAR_MAC_CFG_IPG_OFFSET); |
| |
| /* Init Flow Control register */ |
| star_set_reg(STAR_FC_CFG(base), |
| STAR_FC_CFG_SEND_PAUSE_TH_DEF | |
| STAR_FC_CFG_UCPAUSEDIS | |
| STAR_FC_CFG_BPEN); |
| |
| /* Init SEND_PAUSE_RLS */ |
| star_set_reg(star_extend_cfg(base), STAR_EXTEND_CFG_SEND_PAUSE_RLS_DEF); |
| |
| /* Init MIB counter (reset to 0) */ |
| star_mib_init(dev); |
| |
| /* Enable Hash Table BIST */ |
| star_set_bit(star_hash_ctrl(base), STAR_HASH_CTRL_HASHEN); |
| |
| /* Reset Hash Table (All reset to 0) */ |
| star_reset_hash_table(dev); |
| star_clear_bit(STAR_ARL_CFG(base), STAR_ARL_CFG_MISCMODE); |
| star_clear_bit(STAR_ARL_CFG(base), STAR_ARL_CFG_HASHALG_CRCDA); |
| |
| /*Recv VLAN tag in RX packet */ |
| star_clear_bit(STAR_MAC_CFG(base), STAR_MAC_CFG_VLANSTRIP); |
| |
| return 0; |
| } |
| |
| static void star_mib_reset(star_dev *dev) |
| { |
| void __iomem *base = dev->base; |
| |
| star_get_reg(STAR_MIB_RXOKPKT(base)); |
| star_get_reg(STAR_MIB_RXOKBYTE(base)); |
| star_get_reg(STAR_MIB_RXRUNT(base)); |
| star_get_reg(STAR_MIB_RXOVERSIZE(base)); |
| star_get_reg(STAR_MIB_RXNOBUFDROP(base)); |
| star_get_reg(STAR_MIB_RXCRCERR(base)); |
| star_get_reg(STAR_MIB_RXARLDROP(base)); |
| star_get_reg(STAR_MIB_RXVLANDROP(base)); |
| star_get_reg(STAR_MIB_RXCKSERR(base)); |
| star_get_reg(STAR_MIB_RXPAUSE(base)); |
| star_get_reg(STAR_MIB_TXOKPKT(base)); |
| star_get_reg(STAR_MIB_TXOKBYTE(base)); |
| star_get_reg(STAR_MIB_TXPAUSECOL(base)); |
| } |
| |
| int star_mib_init(star_dev *dev) |
| { |
| star_mib_reset(dev); |
| |
| return 0; |
| } |
| |
| int star_phyctrl_init(star_dev *dev, u32 enable, u32 phy_addr) |
| { |
| u32 data; |
| void __iomem *base = dev->base; |
| |
| data = STAR_PHY_CTRL1_FORCETXFC | |
| STAR_PHY_CTRL1_FORCERXFC | |
| STAR_PHY_CTRL1_FORCEFULL | |
| STAR_PHY_CTRL1_FORCESPD_100M | |
| STAR_PHY_CTRL1_ANEN; |
| |
| STAR_MSG(STAR_VERB, "PHY Control Initialization\n"); |
| /* Enable/Disable PHY auto-polling */ |
| if (enable) |
| star_set_reg(STAR_PHY_CTRL1(base), |
| data | STAR_PHY_CTRL1_APEN | |
| (phy_addr & |
| STAR_PHY_CTRL1_phy_addr_MASK) |
| << STAR_PHY_CTRL1_phy_addr_OFFSET); |
| else |
| star_set_reg(STAR_PHY_CTRL1(base), data | STAR_PHY_CTRL1_APDIS); |
| |
| return 0; |
| } |
| |
| void star_set_hashbit(star_dev *dev, u32 addr, u32 value) |
| { |
| u32 data; |
| void __iomem *base = dev->base; |
| |
| STAR_POLLING_TIMEOUT(star_is_set_bit(star_hash_ctrl(base), |
| STAR_HASH_CTRL_HTBISTDONE)); |
| STAR_POLLING_TIMEOUT(star_is_set_bit(star_hash_ctrl(base), |
| STAR_HASH_CTRL_HTBISTOK)); |
| STAR_POLLING_TIMEOUT(!star_is_set_bit(star_hash_ctrl(base), |
| STAR_HASH_CTRL_START)); |
| |
| data = (STAR_HASH_CTRL_HASHEN | |
| STAR_HASH_CTRL_ACCESSWT | STAR_HASH_CTRL_START | |
| (value ? STAR_HASH_CTRL_HBITDATA : 0) | |
| (addr & STAR_HASH_CTRL_HBITADDR_MASK) |
| << STAR_HASH_CTRL_HBITADDR_OFFSET); |
| star_set_reg(star_hash_ctrl(base), data); |
| STAR_POLLING_TIMEOUT(!star_is_set_bit(star_hash_ctrl(base), |
| STAR_HASH_CTRL_START)); |
| } |
| |
| int star_hw_init(star_dev *dev) |
| { |
| star_set_reg(ETHSYS_CONFIG(dev->base), |
| SWC_MII_MODE | EXT_MDC_MODE | MII_PAD_OE); |
| star_set_reg(MAC_CLOCK_CONFIG(dev->base), |
| (star_get_reg(MAC_CLOCK_CONFIG(dev->base)) & |
| (~(0xff << 0))) | MDC_CLK_DIV_10); |
| |
| return 0; |
| } |
| |
| void star_link_status_change(star_dev *dev) |
| { |
| u32 val, speed; |
| |
| val = star_get_reg(STAR_PHY_CTRL1(dev->base)); |
| if (dev->link_up != ((val & STAR_PHY_CTRL1_STA_LINK) ? 1UL : 0UL)) { |
| dev->link_up = (val & STAR_PHY_CTRL1_STA_LINK) ? 1UL : 0UL; |
| STAR_MSG(STAR_WARN, "Link status: %s\n", |
| dev->link_up ? "Up" : "Down"); |
| if (dev->link_up) { |
| speed = ((val >> STAR_PHY_CTRL1_STA_SPD_OFFSET) & |
| STAR_PHY_CTRL1_STA_SPD_MASK); |
| STAR_MSG(STAR_WARN, "%s Duplex - %s Mbps mode\n", |
| (val & STAR_PHY_CTRL1_STA_FULL) ? |
| "Full" : "Half", |
| !speed ? "10" : (speed == 1 ? "100" : |
| (speed == 2 ? "1000" : "unknown"))); |
| STAR_MSG(STAR_WARN, |
| "TX flow control:%s, RX flow control:%s\n", |
| (val & STAR_PHY_CTRL1_STA_TXFC) ? "On" : "Off", |
| (val & STAR_PHY_CTRL1_STA_RXFC) ? "On" : "Off"); |
| } else { |
| netif_carrier_off(((star_private *)dev->star_prv)->dev); |
| } |
| } |
| |
| if (dev->link_up) |
| netif_carrier_on(((star_private *)dev->star_prv)->dev); |
| } |
| |
| void star_nic_pdset(star_dev *dev, bool flag) |
| { |
| #define MAX_NICPDRDY_RETRY 10000 |
| u32 data, retry = 0; |
| |
| data = star_get_reg(STAR_MAC_CFG(dev->base)); |
| if (flag) { |
| data |= STAR_MAC_CFG_NICPD; |
| star_set_reg(STAR_MAC_CFG(dev->base), data); |
| /* wait until NIC_PD_READY and clear it */ |
| do { |
| data = star_get_reg(STAR_MAC_CFG(dev->base)); |
| if (data & STAR_MAC_CFG_NICPDRDY) { |
| /* clear NIC_PD_READY */ |
| data |= STAR_MAC_CFG_NICPDRDY; |
| star_set_reg(STAR_MAC_CFG(dev->base), data); |
| break; |
| } |
| } while (retry++ < MAX_NICPDRDY_RETRY); |
| if (retry >= MAX_NICPDRDY_RETRY) |
| STAR_MSG(STAR_ERR, "timeout MAX_NICPDRDY_RETRY(%d)\n", |
| MAX_NICPDRDY_RETRY); |
| } else { |
| data &= ~STAR_MAC_CFG_NICPD; |
| star_set_reg(STAR_MAC_CFG(dev->base), data); |
| } |
| } |
| |
| void star_config_wol(star_dev *star_dev, bool enable) |
| { |
| STAR_MSG(STAR_DBG, "[%s]%s wol\n", __func__, |
| enable ? "enable" : "disable"); |
| if (enable) { |
| star_set_reg(star_int_sta(star_dev->base), |
| star_get_reg(star_int_sta(star_dev->base))); |
| star_set_bit(STAR_MAC_CFG(star_dev->base), STAR_MAC_CFG_WOLEN); |
| star_mb(); |
| star_clear_bit(star_int_mask(star_dev->base), |
| STAR_INT_STA_MAGICPKT); |
| } else { |
| star_clear_bit(STAR_MAC_CFG(star_dev->base), |
| STAR_MAC_CFG_WOLEN); |
| star_mb(); |
| star_set_bit(star_int_mask(star_dev->base), |
| STAR_INT_STA_MAGICPKT); |
| } |
| } |
| |
| void star_switch_to_rmii_mode(star_dev *star_dev) |
| { |
| u32 reg_val; |
| |
| reg_val = star_get_reg(star_dev->pericfg_base + 0x14); |
| reg_val &= ~(0xf << 0); |
| /* select RMII mode */ |
| reg_val |= (0x1 << 0); |
| star_set_reg(star_dev->pericfg_base + 0x14, reg_val); |
| |
| #ifdef STAR_USE_TX_CLOCK |
| reg_val = star_get_reg(star_dev->pericfg_base + 0x18); |
| reg_val &= ~(0x1 << 0); |
| /* select tx clock */ |
| reg_val |= (0x1 << 0); |
| star_set_reg(star_dev->pericfg_base + 0x18, reg_val); |
| #endif |
| } |
| |