diff options
Diffstat (limited to 'drivers/net/ethernet/broadcom/genet')
-rw-r--r-- | drivers/net/ethernet/broadcom/genet/bcmgenet.c | 115 | ||||
-rw-r--r-- | drivers/net/ethernet/broadcom/genet/bcmgenet.h | 2 | ||||
-rw-r--r-- | drivers/net/ethernet/broadcom/genet/bcmmii.c | 91 |
3 files changed, 154 insertions, 54 deletions
diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c index 12cb77ef1081..120fa05a39ff 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c @@ -1996,8 +1996,6 @@ static void reset_umac(struct bcmgenet_priv *priv) /* issue soft reset with (rg)mii loopback to ensure a stable rxclk */ bcmgenet_umac_writel(priv, CMD_SW_RESET | CMD_LCL_LOOP_EN, UMAC_CMD); - udelay(2); - bcmgenet_umac_writel(priv, 0, UMAC_CMD); } static void bcmgenet_intr_disable(struct bcmgenet_priv *priv) @@ -2018,6 +2016,8 @@ static void bcmgenet_link_intr_enable(struct bcmgenet_priv *priv) */ if (priv->internal_phy) { int0_enable |= UMAC_IRQ_LINK_EVENT; + if (GENET_IS_V1(priv) || GENET_IS_V2(priv) || GENET_IS_V3(priv)) + int0_enable |= UMAC_IRQ_PHY_DET_R; } else if (priv->ext_phy) { int0_enable |= UMAC_IRQ_LINK_EVENT; } else if (priv->phy_interface == PHY_INTERFACE_MODE_MOCA) { @@ -2576,7 +2576,8 @@ static int bcmgenet_init_dma(struct bcmgenet_priv *priv) } /* Init rDma */ - bcmgenet_rdma_writel(priv, DMA_MAX_BURST_LENGTH, DMA_SCB_BURST_SIZE); + bcmgenet_rdma_writel(priv, priv->dma_max_burst_length, + DMA_SCB_BURST_SIZE); /* Initialize Rx queues */ ret = bcmgenet_init_rx_queues(priv->dev); @@ -2589,7 +2590,8 @@ static int bcmgenet_init_dma(struct bcmgenet_priv *priv) } /* Init tDma */ - bcmgenet_tdma_writel(priv, DMA_MAX_BURST_LENGTH, DMA_SCB_BURST_SIZE); + bcmgenet_tdma_writel(priv, priv->dma_max_burst_length, + DMA_SCB_BURST_SIZE); /* Initialize Tx queues */ bcmgenet_init_tx_queues(priv->dev); @@ -2611,11 +2613,16 @@ static void bcmgenet_irq_task(struct work_struct *work) priv->irq0_stat = 0; spin_unlock_irq(&priv->lock); + if (status & UMAC_IRQ_PHY_DET_R && + priv->dev->phydev->autoneg != AUTONEG_ENABLE) { + phy_init_hw(priv->dev->phydev); + genphy_config_aneg(priv->dev->phydev); + } + /* Link UP/DOWN event */ - if (status & UMAC_IRQ_LINK_EVENT) { - priv->dev->phydev->link = !!(status & UMAC_IRQ_LINK_UP); + if (status & UMAC_IRQ_LINK_EVENT) phy_mac_interrupt(priv->dev->phydev); - } + } /* bcmgenet_isr1: handle Rx and Tx priority queues */ @@ -2710,7 +2717,7 @@ static irqreturn_t bcmgenet_isr0(int irq, void *dev_id) } /* all other interested interrupts handled in bottom half */ - status &= UMAC_IRQ_LINK_EVENT; + status &= (UMAC_IRQ_LINK_EVENT | UMAC_IRQ_PHY_DET_R); if (status) { /* Save irq status for bottom-half processing. */ spin_lock_irqsave(&priv->lock, flags); @@ -3415,12 +3422,48 @@ static void bcmgenet_set_hw_params(struct bcmgenet_priv *priv) params->words_per_bd); } +struct bcmgenet_plat_data { + enum bcmgenet_version version; + u32 dma_max_burst_length; +}; + +static const struct bcmgenet_plat_data v1_plat_data = { + .version = GENET_V1, + .dma_max_burst_length = DMA_MAX_BURST_LENGTH, +}; + +static const struct bcmgenet_plat_data v2_plat_data = { + .version = GENET_V2, + .dma_max_burst_length = DMA_MAX_BURST_LENGTH, +}; + +static const struct bcmgenet_plat_data v3_plat_data = { + .version = GENET_V3, + .dma_max_burst_length = DMA_MAX_BURST_LENGTH, +}; + +static const struct bcmgenet_plat_data v4_plat_data = { + .version = GENET_V4, + .dma_max_burst_length = DMA_MAX_BURST_LENGTH, +}; + +static const struct bcmgenet_plat_data v5_plat_data = { + .version = GENET_V5, + .dma_max_burst_length = DMA_MAX_BURST_LENGTH, +}; + +static const struct bcmgenet_plat_data bcm2711_plat_data = { + .version = GENET_V5, + .dma_max_burst_length = 0x08, +}; + static const struct of_device_id bcmgenet_match[] = { - { .compatible = "brcm,genet-v1", .data = (void *)GENET_V1 }, - { .compatible = "brcm,genet-v2", .data = (void *)GENET_V2 }, - { .compatible = "brcm,genet-v3", .data = (void *)GENET_V3 }, - { .compatible = "brcm,genet-v4", .data = (void *)GENET_V4 }, - { .compatible = "brcm,genet-v5", .data = (void *)GENET_V5 }, + { .compatible = "brcm,genet-v1", .data = &v1_plat_data }, + { .compatible = "brcm,genet-v2", .data = &v2_plat_data }, + { .compatible = "brcm,genet-v3", .data = &v3_plat_data }, + { .compatible = "brcm,genet-v4", .data = &v4_plat_data }, + { .compatible = "brcm,genet-v5", .data = &v5_plat_data }, + { .compatible = "brcm,bcm2711-genet-v5", .data = &bcm2711_plat_data }, { }, }; MODULE_DEVICE_TABLE(of, bcmgenet_match); @@ -3430,6 +3473,7 @@ static int bcmgenet_probe(struct platform_device *pdev) struct bcmgenet_platform_data *pd = pdev->dev.platform_data; struct device_node *dn = pdev->dev.of_node; const struct of_device_id *of_id = NULL; + const struct bcmgenet_plat_data *pdata; struct bcmgenet_priv *priv; struct net_device *dev; const void *macaddr; @@ -3453,24 +3497,21 @@ static int bcmgenet_probe(struct platform_device *pdev) priv = netdev_priv(dev); priv->irq0 = platform_get_irq(pdev, 0); + if (priv->irq0 < 0) { + err = priv->irq0; + goto err; + } priv->irq1 = platform_get_irq(pdev, 1); - priv->wol_irq = platform_get_irq(pdev, 2); - if (!priv->irq0 || !priv->irq1) { - dev_err(&pdev->dev, "can't find IRQs\n"); - err = -EINVAL; + if (priv->irq1 < 0) { + err = priv->irq1; goto err; } + priv->wol_irq = platform_get_irq_optional(pdev, 2); - if (dn) { + if (dn) macaddr = of_get_mac_address(dn); - if (IS_ERR(macaddr)) { - dev_err(&pdev->dev, "can't find MAC address\n"); - err = -EINVAL; - goto err; - } - } else { + else macaddr = pd->mac_address; - } priv->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(priv->base)) { @@ -3482,7 +3523,12 @@ static int bcmgenet_probe(struct platform_device *pdev) SET_NETDEV_DEV(dev, &pdev->dev); dev_set_drvdata(&pdev->dev, dev); - ether_addr_copy(dev->dev_addr, macaddr); + if (IS_ERR_OR_NULL(macaddr) || !is_valid_ether_addr(macaddr)) { + dev_warn(&pdev->dev, "using random Ethernet MAC\n"); + eth_hw_addr_random(dev); + } else { + ether_addr_copy(dev->dev_addr, macaddr); + } dev->watchdog_timeo = 2 * HZ; dev->ethtool_ops = &bcmgenet_ethtool_ops; dev->netdev_ops = &bcmgenet_netdev_ops; @@ -3509,10 +3555,14 @@ static int bcmgenet_probe(struct platform_device *pdev) priv->dev = dev; priv->pdev = pdev; - if (of_id) - priv->version = (enum bcmgenet_version)of_id->data; - else + if (of_id) { + pdata = of_id->data; + priv->version = pdata->version; + priv->dma_max_burst_length = pdata->dma_max_burst_length; + } else { priv->version = pd->genet_version; + priv->dma_max_burst_length = DMA_MAX_BURST_LENGTH; + } priv->clk = devm_clk_get(&priv->pdev->dev, "enet"); if (IS_ERR(priv->clk)) { @@ -3597,6 +3647,11 @@ static int bcmgenet_remove(struct platform_device *pdev) return 0; } +static void bcmgenet_shutdown(struct platform_device *pdev) +{ + bcmgenet_remove(pdev); +} + #ifdef CONFIG_PM_SLEEP static int bcmgenet_resume(struct device *d) { @@ -3631,6 +3686,7 @@ static int bcmgenet_resume(struct device *d) phy_init_hw(dev->phydev); /* Speed settings must be restored */ + genphy_config_aneg(dev->phydev); bcmgenet_mii_config(priv->dev, false); bcmgenet_set_hw_addr(priv, dev->dev_addr); @@ -3715,6 +3771,7 @@ static SIMPLE_DEV_PM_OPS(bcmgenet_pm_ops, bcmgenet_suspend, bcmgenet_resume); static struct platform_driver bcmgenet_driver = { .probe = bcmgenet_probe, .remove = bcmgenet_remove, + .shutdown = bcmgenet_shutdown, .driver = { .name = "bcmgenet", .of_match_table = bcmgenet_match, diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.h b/drivers/net/ethernet/broadcom/genet/bcmgenet.h index 4a8fc03d82fd..a5659197598f 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.h +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.h @@ -366,6 +366,7 @@ struct bcmgenet_mib_counters { #define EXT_PWR_DOWN_PHY_EN (1 << 20) #define EXT_RGMII_OOB_CTRL 0x0C +#define RGMII_MODE_EN_V123 (1 << 0) #define RGMII_LINK (1 << 4) #define OOB_DISABLE (1 << 5) #define RGMII_MODE_EN (1 << 6) @@ -663,6 +664,7 @@ struct bcmgenet_priv { bool crc_fwd_en; unsigned int dma_rx_chk_bit; + u32 dma_max_burst_length; u32 msg_enable; diff --git a/drivers/net/ethernet/broadcom/genet/bcmmii.c b/drivers/net/ethernet/broadcom/genet/bcmmii.c index 970e478a9017..6392a2530183 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmmii.c +++ b/drivers/net/ethernet/broadcom/genet/bcmmii.c @@ -181,13 +181,42 @@ int bcmgenet_mii_config(struct net_device *dev, bool init) const char *phy_name = NULL; u32 id_mode_dis = 0; u32 port_ctrl; + int bmcr = -1; + int ret; u32 reg; - priv->ext_phy = !priv->internal_phy && - (priv->phy_interface != PHY_INTERFACE_MODE_MOCA); + /* MAC clocking workaround during reset of umac state machines */ + reg = bcmgenet_umac_readl(priv, UMAC_CMD); + if (reg & CMD_SW_RESET) { + /* An MII PHY must be isolated to prevent TXC contention */ + if (priv->phy_interface == PHY_INTERFACE_MODE_MII) { + ret = phy_read(phydev, MII_BMCR); + if (ret >= 0) { + bmcr = ret; + ret = phy_write(phydev, MII_BMCR, + bmcr | BMCR_ISOLATE); + } + if (ret) { + netdev_err(dev, "failed to isolate PHY\n"); + return ret; + } + } + /* Switch MAC clocking to RGMII generated clock */ + bcmgenet_sys_writel(priv, PORT_MODE_EXT_GPHY, SYS_PORT_CTRL); + /* Ensure 5 clks with Rx disabled + * followed by 5 clks with Reset asserted + */ + udelay(4); + reg &= ~(CMD_SW_RESET | CMD_LCL_LOOP_EN); + bcmgenet_umac_writel(priv, reg, UMAC_CMD); + /* Ensure 5 more clocks before Rx is enabled */ + udelay(2); + } switch (priv->phy_interface) { case PHY_INTERFACE_MODE_INTERNAL: + phy_name = "internal PHY"; + /* fall through */ case PHY_INTERFACE_MODE_MOCA: /* Irrespective of the actually configured PHY speed (100 or * 1000) GENETv4 only has an internal GPHY so we will just end @@ -199,11 +228,7 @@ int bcmgenet_mii_config(struct net_device *dev, bool init) else port_ctrl = PORT_MODE_INT_EPHY; - bcmgenet_sys_writel(priv, port_ctrl, SYS_PORT_CTRL); - - if (priv->internal_phy) { - phy_name = "internal PHY"; - } else if (priv->phy_interface == PHY_INTERFACE_MODE_MOCA) { + if (!phy_name) { phy_name = "MoCA"; bcmgenet_moca_phy_setup(priv); } @@ -212,8 +237,7 @@ int bcmgenet_mii_config(struct net_device *dev, bool init) case PHY_INTERFACE_MODE_MII: phy_name = "external MII"; phy_set_max_speed(phydev, SPEED_100); - bcmgenet_sys_writel(priv, - PORT_MODE_EXT_EPHY, SYS_PORT_CTRL); + port_ctrl = PORT_MODE_EXT_EPHY; break; case PHY_INTERFACE_MODE_REVMII: @@ -228,37 +252,53 @@ int bcmgenet_mii_config(struct net_device *dev, bool init) port_ctrl = PORT_MODE_EXT_RVMII_50; else port_ctrl = PORT_MODE_EXT_RVMII_25; - bcmgenet_sys_writel(priv, port_ctrl, SYS_PORT_CTRL); break; case PHY_INTERFACE_MODE_RGMII: /* RGMII_NO_ID: TXC transitions at the same time as TXD * (requires PCB or receiver-side delay) - * RGMII: Add 2ns delay on TXC (90 degree shift) * * ID is implicitly disabled for 100Mbps (RG)MII operation. */ + phy_name = "external RGMII (no delay)"; id_mode_dis = BIT(16); - /* fall through */ + port_ctrl = PORT_MODE_EXT_GPHY; + break; + case PHY_INTERFACE_MODE_RGMII_TXID: - if (id_mode_dis) - phy_name = "external RGMII (no delay)"; - else - phy_name = "external RGMII (TX delay)"; - bcmgenet_sys_writel(priv, - PORT_MODE_EXT_GPHY, SYS_PORT_CTRL); + /* RGMII_TXID: Add 2ns delay on TXC (90 degree shift) */ + phy_name = "external RGMII (TX delay)"; + port_ctrl = PORT_MODE_EXT_GPHY; + break; + + case PHY_INTERFACE_MODE_RGMII_RXID: + phy_name = "external RGMII (RX delay)"; + port_ctrl = PORT_MODE_EXT_GPHY; break; default: dev_err(kdev, "unknown phy mode: %d\n", priv->phy_interface); return -EINVAL; } + bcmgenet_sys_writel(priv, port_ctrl, SYS_PORT_CTRL); + + /* Restore the MII PHY after isolation */ + if (bmcr >= 0) + phy_write(phydev, MII_BMCR, bmcr); + + priv->ext_phy = !priv->internal_phy && + (priv->phy_interface != PHY_INTERFACE_MODE_MOCA); + /* This is an external PHY (xMII), so we need to enable the RGMII * block for the interface to work */ if (priv->ext_phy) { reg = bcmgenet_ext_readl(priv, EXT_RGMII_OOB_CTRL); - reg |= RGMII_MODE_EN | id_mode_dis; + reg |= id_mode_dis; + if (GENET_IS_V1(priv) || GENET_IS_V2(priv) || GENET_IS_V3(priv)) + reg |= RGMII_MODE_EN_V123; + else + reg |= RGMII_MODE_EN; bcmgenet_ext_writel(priv, reg, EXT_RGMII_OOB_CTRL); } @@ -273,11 +313,12 @@ int bcmgenet_mii_probe(struct net_device *dev) struct bcmgenet_priv *priv = netdev_priv(dev); struct device_node *dn = priv->pdev->dev.of_node; struct phy_device *phydev; - u32 phy_flags; + u32 phy_flags = 0; int ret; /* Communicate the integrated PHY revision */ - phy_flags = priv->gphy_rev; + if (priv->internal_phy) + phy_flags = priv->gphy_rev; /* Initialize link state variables that bcmgenet_mii_setup() uses */ priv->old_link = -1; @@ -441,7 +482,7 @@ static int bcmgenet_mii_of_init(struct bcmgenet_priv *priv) struct device_node *dn = priv->pdev->dev.of_node; struct device *kdev = &priv->pdev->dev; struct phy_device *phydev; - int phy_mode; + phy_interface_t phy_mode; int ret; /* Fetch the PHY phandle */ @@ -459,10 +500,10 @@ static int bcmgenet_mii_of_init(struct bcmgenet_priv *priv) } /* Get the link mode */ - phy_mode = of_get_phy_mode(dn); - if (phy_mode < 0) { + ret = of_get_phy_mode(dn, &phy_mode); + if (ret) { dev_err(kdev, "invalid PHY mode property\n"); - return phy_mode; + return ret; } priv->phy_interface = phy_mode; |