/* 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)
} 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);
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);
free_irq(emac->irq_rx, ndev);
out_freeirq:
+ if (ndev->phydev)
+ phy_disconnect(ndev->phydev);
+
return ret;
}
netif_stop_queue(ndev);
napi_disable(&emac->napi);
+ if (ndev->phydev)
+ phy_stop(ndev->phydev);
del_timer_sync(&emac->rx_timeout);
/* mask all interrupts */
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);
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);