diff options
Diffstat (limited to 'target/linux/bmips')
-rw-r--r-- | target/linux/bmips/dts/bcm6358-huawei-hg556a-b.dts | 7 | ||||
-rw-r--r-- | target/linux/bmips/files/drivers/net/ethernet/broadcom/bcm6348-enet.c | 112 |
2 files changed, 119 insertions, 0 deletions
diff --git a/target/linux/bmips/dts/bcm6358-huawei-hg556a-b.dts b/target/linux/bmips/dts/bcm6358-huawei-hg556a-b.dts index 2ad9460a9c..273470b1bd 100644 --- a/target/linux/bmips/dts/bcm6358-huawei-hg556a-b.dts +++ b/target/linux/bmips/dts/bcm6358-huawei-hg556a-b.dts @@ -145,6 +145,13 @@ nvmem-cells = <&macaddr_cfe_6a0>; nvmem-cell-names = "mac-address"; + + phy-mode = "mii"; + + fixed-link { + speed = <100>; + full-duplex; + }; }; &iudma { diff --git a/target/linux/bmips/files/drivers/net/ethernet/broadcom/bcm6348-enet.c b/target/linux/bmips/files/drivers/net/ethernet/broadcom/bcm6348-enet.c index 06a9df1c0a..75e85aa0f3 100644 --- a/target/linux/bmips/files/drivers/net/ethernet/broadcom/bcm6348-enet.c +++ b/target/linux/bmips/files/drivers/net/ethernet/broadcom/bcm6348-enet.c @@ -489,6 +489,11 @@ struct bcm6348_emac { /* external mii bus */ bool ext_mii; + + /* phy */ + int old_link; + int old_duplex; + int old_pause; }; static inline void emac_writel(struct bcm6348_emac *emac, u32 val, u32 off) @@ -968,6 +973,96 @@ static void bcm6348_emac_disable_mac(struct bcm6348_emac *emac) } while (limit--); } +/* + * set emac duplex parameters + */ +static void bcm6348_emac_set_duplex(struct bcm6348_emac *emac, int fullduplex) +{ + u32 val; + + val = emac_readl(emac, ENET_TXCTL_REG); + if (fullduplex) + val |= ENET_TXCTL_FD_MASK; + else + val &= ~ENET_TXCTL_FD_MASK; + emac_writel(emac, val, ENET_TXCTL_REG); +} + +/* + * set emac flow control parameters + */ +static void bcm6348_emac_set_flow(struct bcm6348_emac *emac, bool rx_en, bool tx_en) +{ + struct bcm6348_iudma *iudma = emac->iudma; + u32 val; + + val = emac_readl(emac, ENET_RXCFG_REG); + if (rx_en) + val |= ENET_RXCFG_ENFLOW_MASK; + else + val &= ~ENET_RXCFG_ENFLOW_MASK; + emac_writel(emac, val, ENET_RXCFG_REG); + + dmas_writel(iudma, emac->rx_desc_dma, DMAS_RSTART_REG, emac->rx_chan); + dmas_writel(iudma, emac->tx_desc_dma, DMAS_RSTART_REG, emac->tx_chan); + + val = dma_readl(iudma, DMA_CFG_REG); + if (tx_en) + val |= DMA_CFG_FLOWCH_MASK(emac->rx_chan); + else + val &= ~DMA_CFG_FLOWCH_MASK(emac->rx_chan); + dma_writel(iudma, val, DMA_CFG_REG); +} + +/* + * adjust emac phy + */ +static void bcm6348_emac_adjust_phy(struct net_device *ndev) +{ + struct phy_device *phydev = ndev->phydev; + struct bcm6348_emac *emac = netdev_priv(ndev); + struct platform_device *pdev = emac->pdev; + struct device *dev = &pdev->dev; + bool status_changed = false; + + if (emac->old_link != phydev->link) { + status_changed = true; + emac->old_link = phydev->link; + } + + if (phydev->link && phydev->duplex != emac->old_duplex) { + bcm6348_emac_set_duplex(emac, phydev->duplex == DUPLEX_FULL); + status_changed = true; + emac->old_duplex = phydev->duplex; + } + + if (phydev->link && phydev->pause != emac->old_pause) { + bool rx_pause_en, tx_pause_en; + + if (phydev->pause) { + rx_pause_en = true; + tx_pause_en = true; + } else { + rx_pause_en = false; + tx_pause_en = false; + } + + bcm6348_emac_set_flow(emac, rx_pause_en, tx_pause_en); + status_changed = true; + emac->old_pause = phydev->pause; + } + + if (status_changed) + dev_info(dev, "%s: phy link %s %s/%s/%s/%s\n", + ndev->name, + phydev->link ? "UP" : "DOWN", + phy_modes(phydev->interface), + phy_speed_to_str(phydev->speed), + phy_duplex_to_str(phydev->duplex), + phydev->pause ? "rx/tx" : "off"); +} + + static int bcm6348_emac_open(struct net_device *ndev) { struct bcm6348_emac *emac = netdev_priv(ndev); @@ -1133,6 +1228,9 @@ static int bcm6348_emac_open(struct net_device *ndev) dmac_writel(iudma, DMAC_IR_PKTDONE_MASK, DMAC_IRMASK_REG, emac->tx_chan); + if (ndev->phydev) + phy_start(ndev->phydev); + netif_carrier_on(ndev); netif_start_queue(ndev); @@ -1171,6 +1269,9 @@ out_freeirq_rx: free_irq(emac->irq_rx, ndev); out_freeirq: + if (ndev->phydev) + phy_disconnect(ndev->phydev); + return ret; } @@ -1183,6 +1284,8 @@ static int bcm6348_emac_stop(struct net_device *ndev) netif_stop_queue(ndev); napi_disable(&emac->napi); + if (ndev->phydev) + phy_stop(ndev->phydev); del_timer_sync(&emac->rx_timeout); /* mask all interrupts */ @@ -1454,6 +1557,10 @@ static int bcm6348_emac_probe(struct platform_device *pdev) emac->tx_ring_size = ENET_DEF_TX_DESC; emac->copybreak = ENET_DEF_CPY_BREAK; + emac->old_link = 0; + emac->old_duplex = -1; + emac->old_pause = -1; + of_get_mac_address(node, ndev->dev_addr); if (is_valid_ether_addr(ndev->dev_addr)) { dev_info(dev, "mtd mac %pM\n", ndev->dev_addr); @@ -1543,6 +1650,11 @@ static int bcm6348_emac_probe(struct platform_device *pdev) netif_carrier_off(ndev); + ndev->phydev = of_phy_get_and_connect(ndev, node, + bcm6348_emac_adjust_phy); + if (IS_ERR_OR_NULL(ndev->phydev)) + dev_warn(dev, "PHY not found!\n"); + dev_info(dev, "%s at 0x%px, IRQ %d\n", ndev->name, emac->base, ndev->irq); |