diff options
Diffstat (limited to 'drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c')
| -rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c | 335 |
1 files changed, 177 insertions, 158 deletions
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c index 58e0511badba..8aa496ac85cc 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c @@ -11,11 +11,13 @@ #include <linux/mdio-mux.h> #include <linux/mfd/syscon.h> #include <linux/module.h> -#include <linux/of_device.h> +#include <linux/of.h> #include <linux/of_mdio.h> #include <linux/of_net.h> +#include <linux/of_platform.h> #include <linux/phy.h> #include <linux/platform_device.h> +#include <linux/pm_runtime.h> #include <linux/regulator/consumer.h> #include <linux/regmap.h> #include <linux/stmmac.h> @@ -29,10 +31,6 @@ */ /* struct emac_variant - Describe dwmac-sun8i hardware variant - * @default_syscon_value: The default value of the EMAC register in syscon - * This value is used for disabling properly EMAC - * and used as a good starting value in case of the - * boot process(uboot) leave some stuff. * @syscon_field reg_field for the syscon's gmac register * @soc_has_internal_phy: Does the MAC embed an internal PHY * @support_mii: Does the MAC handle MII @@ -46,7 +44,6 @@ * value of zero indicates this is not supported. */ struct emac_variant { - u32 default_syscon_value; const struct reg_field *syscon_field; bool soc_has_internal_phy; bool support_mii; @@ -57,23 +54,23 @@ struct emac_variant { }; /* struct sunxi_priv_data - hold all sunxi private data - * @tx_clk: reference to MAC TX clock * @ephy_clk: reference to the optional EPHY clock for the internal PHY * @regulator: reference to the optional regulator * @rst_ephy: reference to the optional EPHY reset for the internal PHY * @variant: reference to the current board variant * @regmap: regmap for using the syscon * @internal_phy_powered: Does the internal PHY is enabled + * @use_internal_phy: Is the internal PHY selected for use * @mux_handle: Internal pointer used by mdio-mux lib */ struct sunxi_priv_data { - struct clk *tx_clk; struct clk *ephy_clk; struct regulator *regulator; struct reset_control *rst_ephy; const struct emac_variant *variant; struct regmap_field *regmap_field; bool internal_phy_powered; + bool use_internal_phy; void *mux_handle; }; @@ -92,7 +89,6 @@ static const struct reg_field sun8i_ccu_reg_field = { }; static const struct emac_variant emac_variant_h3 = { - .default_syscon_value = 0x58000, .syscon_field = &sun8i_syscon_reg_field, .soc_has_internal_phy = true, .support_mii = true, @@ -103,14 +99,12 @@ static const struct emac_variant emac_variant_h3 = { }; static const struct emac_variant emac_variant_v3s = { - .default_syscon_value = 0x38000, .syscon_field = &sun8i_syscon_reg_field, .soc_has_internal_phy = true, .support_mii = true }; static const struct emac_variant emac_variant_a83t = { - .default_syscon_value = 0, .syscon_field = &sun8i_syscon_reg_field, .soc_has_internal_phy = false, .support_mii = true, @@ -120,7 +114,6 @@ static const struct emac_variant emac_variant_a83t = { }; static const struct emac_variant emac_variant_r40 = { - .default_syscon_value = 0, .syscon_field = &sun8i_ccu_reg_field, .support_mii = true, .support_rgmii = true, @@ -128,7 +121,6 @@ static const struct emac_variant emac_variant_r40 = { }; static const struct emac_variant emac_variant_a64 = { - .default_syscon_value = 0, .syscon_field = &sun8i_syscon_reg_field, .soc_has_internal_phy = false, .support_mii = true, @@ -139,7 +131,6 @@ static const struct emac_variant emac_variant_a64 = { }; static const struct emac_variant emac_variant_h6 = { - .default_syscon_value = 0x50000, .syscon_field = &sun8i_syscon_reg_field, /* The "Internal PHY" of H6 is not on the die. It's on the * co-packaged AC200 chip instead. @@ -237,6 +228,22 @@ static const struct emac_variant emac_variant_h6 = { #define EMAC_RX_EARLY_INT BIT(13) #define EMAC_RGMII_STA_INT BIT(16) +#define EMAC_INT_MSK_COMMON EMAC_RGMII_STA_INT +#define EMAC_INT_MSK_TX (EMAC_TX_INT | \ + EMAC_TX_DMA_STOP_INT | \ + EMAC_TX_BUF_UA_INT | \ + EMAC_TX_TIMEOUT_INT | \ + EMAC_TX_UNDERFLOW_INT | \ + EMAC_TX_EARLY_INT |\ + EMAC_INT_MSK_COMMON) +#define EMAC_INT_MSK_RX (EMAC_RX_INT | \ + EMAC_RX_BUF_UA_INT | \ + EMAC_RX_DMA_STOP_INT | \ + EMAC_RX_TIMEOUT_INT | \ + EMAC_RX_OVERFLOW_INT | \ + EMAC_RX_EARLY_INT | \ + EMAC_INT_MSK_COMMON) + #define MAC_ADDR_TYPE_DST BIT(31) /* H3 specific bits for EPHY */ @@ -281,13 +288,14 @@ static int sun8i_dwmac_dma_reset(void __iomem *ioaddr) * Called from stmmac via stmmac_dma_ops->init */ static void sun8i_dwmac_dma_init(void __iomem *ioaddr, - struct stmmac_dma_cfg *dma_cfg, int atds) + struct stmmac_dma_cfg *dma_cfg) { writel(EMAC_RX_INT | EMAC_TX_INT, ioaddr + EMAC_INT_EN); writel(0x1FFFFFF, ioaddr + EMAC_INT_STA); } -static void sun8i_dwmac_dma_init_rx(void __iomem *ioaddr, +static void sun8i_dwmac_dma_init_rx(struct stmmac_priv *priv, + void __iomem *ioaddr, struct stmmac_dma_cfg *dma_cfg, dma_addr_t dma_rx_phy, u32 chan) { @@ -295,7 +303,8 @@ static void sun8i_dwmac_dma_init_rx(void __iomem *ioaddr, writel(lower_32_bits(dma_rx_phy), ioaddr + EMAC_RX_DESC_LIST); } -static void sun8i_dwmac_dma_init_tx(void __iomem *ioaddr, +static void sun8i_dwmac_dma_init_tx(struct stmmac_priv *priv, + void __iomem *ioaddr, struct stmmac_dma_cfg *dma_cfg, dma_addr_t dma_tx_phy, u32 chan) { @@ -307,7 +316,8 @@ static void sun8i_dwmac_dma_init_tx(void __iomem *ioaddr, * Called from stmmac_dma_ops->dump_regs * Used for ethtool */ -static void sun8i_dwmac_dump_regs(void __iomem *ioaddr, u32 *reg_space) +static void sun8i_dwmac_dump_regs(struct stmmac_priv *priv, + void __iomem *ioaddr, u32 *reg_space) { int i; @@ -335,7 +345,8 @@ static void sun8i_dwmac_dump_mac_regs(struct mac_device_info *hw, } } -static void sun8i_dwmac_enable_dma_irq(void __iomem *ioaddr, u32 chan, +static void sun8i_dwmac_enable_dma_irq(struct stmmac_priv *priv, + void __iomem *ioaddr, u32 chan, bool rx, bool tx) { u32 value = readl(ioaddr + EMAC_INT_EN); @@ -348,7 +359,8 @@ static void sun8i_dwmac_enable_dma_irq(void __iomem *ioaddr, u32 chan, writel(value, ioaddr + EMAC_INT_EN); } -static void sun8i_dwmac_disable_dma_irq(void __iomem *ioaddr, u32 chan, +static void sun8i_dwmac_disable_dma_irq(struct stmmac_priv *priv, + void __iomem *ioaddr, u32 chan, bool rx, bool tx) { u32 value = readl(ioaddr + EMAC_INT_EN); @@ -361,7 +373,8 @@ static void sun8i_dwmac_disable_dma_irq(void __iomem *ioaddr, u32 chan, writel(value, ioaddr + EMAC_INT_EN); } -static void sun8i_dwmac_dma_start_tx(void __iomem *ioaddr, u32 chan) +static void sun8i_dwmac_dma_start_tx(struct stmmac_priv *priv, + void __iomem *ioaddr, u32 chan) { u32 v; @@ -371,7 +384,7 @@ static void sun8i_dwmac_dma_start_tx(void __iomem *ioaddr, u32 chan) writel(v, ioaddr + EMAC_TX_CTL1); } -static void sun8i_dwmac_enable_dma_transmission(void __iomem *ioaddr) +static void sun8i_dwmac_enable_dma_transmission(void __iomem *ioaddr, u32 chan) { u32 v; @@ -381,7 +394,8 @@ static void sun8i_dwmac_enable_dma_transmission(void __iomem *ioaddr) writel(v, ioaddr + EMAC_TX_CTL1); } -static void sun8i_dwmac_dma_stop_tx(void __iomem *ioaddr, u32 chan) +static void sun8i_dwmac_dma_stop_tx(struct stmmac_priv *priv, + void __iomem *ioaddr, u32 chan) { u32 v; @@ -390,7 +404,8 @@ static void sun8i_dwmac_dma_stop_tx(void __iomem *ioaddr, u32 chan) writel(v, ioaddr + EMAC_TX_CTL1); } -static void sun8i_dwmac_dma_start_rx(void __iomem *ioaddr, u32 chan) +static void sun8i_dwmac_dma_start_rx(struct stmmac_priv *priv, + void __iomem *ioaddr, u32 chan) { u32 v; @@ -400,7 +415,8 @@ static void sun8i_dwmac_dma_start_rx(void __iomem *ioaddr, u32 chan) writel(v, ioaddr + EMAC_RX_CTL1); } -static void sun8i_dwmac_dma_stop_rx(void __iomem *ioaddr, u32 chan) +static void sun8i_dwmac_dma_stop_rx(struct stmmac_priv *priv, + void __iomem *ioaddr, u32 chan) { u32 v; @@ -409,17 +425,27 @@ static void sun8i_dwmac_dma_stop_rx(void __iomem *ioaddr, u32 chan) writel(v, ioaddr + EMAC_RX_CTL1); } -static int sun8i_dwmac_dma_interrupt(void __iomem *ioaddr, - struct stmmac_extra_stats *x, u32 chan) +static int sun8i_dwmac_dma_interrupt(struct stmmac_priv *priv, + void __iomem *ioaddr, + struct stmmac_extra_stats *x, u32 chan, + u32 dir) { - u32 v; + struct stmmac_pcpu_stats *stats = this_cpu_ptr(priv->xstats.pcpu_stats); int ret = 0; + u32 v; v = readl(ioaddr + EMAC_INT_STA); + if (dir == DMA_DIR_RX) + v &= EMAC_INT_MSK_RX; + else if (dir == DMA_DIR_TX) + v &= EMAC_INT_MSK_TX; + if (v & EMAC_TX_INT) { ret |= handle_tx; - x->tx_normal_irq_n++; + u64_stats_update_begin(&stats->syncp); + u64_stats_inc(&stats->tx_normal_irq_n[chan]); + u64_stats_update_end(&stats->syncp); } if (v & EMAC_TX_DMA_STOP_INT) @@ -441,7 +467,9 @@ static int sun8i_dwmac_dma_interrupt(void __iomem *ioaddr, if (v & EMAC_RX_INT) { ret |= handle_rx; - x->rx_normal_irq_n++; + u64_stats_update_begin(&stats->syncp); + u64_stats_inc(&stats->rx_normal_irq_n[chan]); + u64_stats_update_end(&stats->syncp); } if (v & EMAC_RX_BUF_UA_INT) @@ -469,7 +497,8 @@ static int sun8i_dwmac_dma_interrupt(void __iomem *ioaddr, return ret; } -static void sun8i_dwmac_dma_operation_mode_rx(void __iomem *ioaddr, int mode, +static void sun8i_dwmac_dma_operation_mode_rx(struct stmmac_priv *priv, + void __iomem *ioaddr, int mode, u32 channel, int fifosz, u8 qmode) { u32 v; @@ -492,7 +521,8 @@ static void sun8i_dwmac_dma_operation_mode_rx(void __iomem *ioaddr, int mode, writel(v, ioaddr + EMAC_RX_CTL1); } -static void sun8i_dwmac_dma_operation_mode_tx(void __iomem *ioaddr, int mode, +static void sun8i_dwmac_dma_operation_mode_tx(struct stmmac_priv *priv, + void __iomem *ioaddr, int mode, u32 channel, int fifosz, u8 qmode) { u32 v; @@ -539,28 +569,35 @@ static const struct stmmac_dma_ops sun8i_dwmac_dma_ops = { .dma_interrupt = sun8i_dwmac_dma_interrupt, }; -static int sun8i_dwmac_init(struct platform_device *pdev, void *priv) +static int sun8i_dwmac_power_internal_phy(struct stmmac_priv *priv); + +static int sun8i_dwmac_init(struct device *dev, void *priv) { + struct net_device *ndev = dev_get_drvdata(dev); struct sunxi_priv_data *gmac = priv; int ret; if (gmac->regulator) { ret = regulator_enable(gmac->regulator); if (ret) { - dev_err(&pdev->dev, "Fail to enable regulator\n"); + dev_err(dev, "Fail to enable regulator\n"); return ret; } } - ret = clk_prepare_enable(gmac->tx_clk); - if (ret) { - if (gmac->regulator) - regulator_disable(gmac->regulator); - dev_err(&pdev->dev, "Could not enable AHB clock\n"); - return ret; + if (gmac->use_internal_phy) { + ret = sun8i_dwmac_power_internal_phy(netdev_priv(ndev)); + if (ret) + goto err_disable_regulator; } return 0; + +err_disable_regulator: + if (gmac->regulator) + regulator_disable(gmac->regulator); + + return ret; } static void sun8i_dwmac_core_init(struct mac_device_info *hw, @@ -595,7 +632,7 @@ static void sun8i_dwmac_set_mac(void __iomem *ioaddr, bool enable) * If addr is NULL, clear the slot */ static void sun8i_dwmac_set_umac_addr(struct mac_device_info *hw, - unsigned char *addr, + const unsigned char *addr, unsigned int reg_n) { void __iomem *ioaddr = hw->pcsr; @@ -717,7 +754,7 @@ static int sun8i_dwmac_reset(struct stmmac_priv *priv) if (err) { dev_err(priv->device, "EMAC reset timeout\n"); - return -EFAULT; + return err; } return 0; } @@ -726,8 +763,8 @@ static int sun8i_dwmac_reset(struct stmmac_priv *priv) static int get_ephy_nodes(struct stmmac_priv *priv) { struct sunxi_priv_data *gmac = priv->plat->bsp_priv; - struct device_node *mdio_mux, *iphynode; struct device_node *mdio_internal; + struct device_node *mdio_mux; int ret; mdio_mux = of_get_child_by_name(priv->device->of_node, "mdio-mux"); @@ -745,7 +782,7 @@ static int get_ephy_nodes(struct stmmac_priv *priv) } /* Seek for internal PHY */ - for_each_child_of_node(mdio_internal, iphynode) { + for_each_child_of_node_scoped(mdio_internal, iphynode) { gmac->ephy_clk = of_clk_get(iphynode, 0); if (IS_ERR(gmac->ephy_clk)) continue; @@ -753,14 +790,12 @@ static int get_ephy_nodes(struct stmmac_priv *priv) if (IS_ERR(gmac->rst_ephy)) { ret = PTR_ERR(gmac->rst_ephy); if (ret == -EPROBE_DEFER) { - of_node_put(iphynode); of_node_put(mdio_internal); return ret; } continue; } dev_info(priv->device, "Found internal PHY node\n"); - of_node_put(iphynode); of_node_put(mdio_internal); return 0; } @@ -788,12 +823,12 @@ static int sun8i_dwmac_power_internal_phy(struct stmmac_priv *priv) /* Make sure the EPHY is properly reseted, as U-Boot may leave * it at deasserted state, and thus it may fail to reset EMAC. + * + * This assumes the driver has exclusive access to the EPHY reset. */ - reset_control_assert(gmac->rst_ephy); - - ret = reset_control_deassert(gmac->rst_ephy); + ret = reset_control_reset(gmac->rst_ephy); if (ret) { - dev_err(priv->device, "Cannot deassert internal phy\n"); + dev_err(priv->device, "Cannot reset internal PHY\n"); clk_disable_unprepare(gmac->ephy_clk); return ret; } @@ -803,15 +838,14 @@ static int sun8i_dwmac_power_internal_phy(struct stmmac_priv *priv) return 0; } -static int sun8i_dwmac_unpower_internal_phy(struct sunxi_priv_data *gmac) +static void sun8i_dwmac_unpower_internal_phy(struct sunxi_priv_data *gmac) { if (!gmac->internal_phy_powered) - return 0; + return; clk_disable_unprepare(gmac->ephy_clk); reset_control_assert(gmac->rst_ephy); gmac->internal_phy_powered = false; - return 0; } /* MDIO multiplexing switch function @@ -831,7 +865,6 @@ static int mdio_mux_syscon_switch_fn(int current_child, int desired_child, struct sunxi_priv_data *gmac = priv->plat->bsp_priv; u32 reg, val; int ret = 0; - bool need_power_ephy = false; if (current_child ^ desired_child) { regmap_field_read(gmac->regmap_field, ®); @@ -839,13 +872,12 @@ static int mdio_mux_syscon_switch_fn(int current_child, int desired_child, case DWMAC_SUN8I_MDIO_MUX_INTERNAL_ID: dev_info(priv->device, "Switch mux to internal PHY"); val = (reg & ~H3_EPHY_MUX_MASK) | H3_EPHY_SELECT; - - need_power_ephy = true; + gmac->use_internal_phy = true; break; case DWMAC_SUN8I_MDIO_MUX_EXTERNAL_ID: dev_info(priv->device, "Switch mux to external PHY"); val = (reg & ~H3_EPHY_MUX_MASK) | H3_EPHY_SHUTDOWN; - need_power_ephy = false; + gmac->use_internal_phy = false; break; default: dev_err(priv->device, "Invalid child ID %x\n", @@ -853,7 +885,7 @@ static int mdio_mux_syscon_switch_fn(int current_child, int desired_child, return -EINVAL; } regmap_field_write(gmac->regmap_field, val); - if (need_power_ephy) { + if (gmac->use_internal_phy) { ret = sun8i_dwmac_power_internal_phy(priv); if (ret) return ret; @@ -880,66 +912,47 @@ static int sun8i_dwmac_register_mdio_mux(struct stmmac_priv *priv) ret = mdio_mux_init(priv->device, mdio_mux, mdio_mux_syscon_switch_fn, &gmac->mux_handle, priv, priv->mii); + of_node_put(mdio_mux); return ret; } -static int sun8i_dwmac_set_syscon(struct stmmac_priv *priv) +static int sun8i_dwmac_set_syscon(struct device *dev, + struct plat_stmmacenet_data *plat) { - struct sunxi_priv_data *gmac = priv->plat->bsp_priv; - struct device_node *node = priv->device->of_node; + struct sunxi_priv_data *gmac = plat->bsp_priv; + struct device_node *node = dev->of_node; int ret; - u32 reg, val; - - ret = regmap_field_read(gmac->regmap_field, &val); - if (ret) { - dev_err(priv->device, "Fail to read from regmap field.\n"); - return ret; - } - - reg = gmac->variant->default_syscon_value; - if (reg != val) - dev_warn(priv->device, - "Current syscon value is not the default %x (expect %x)\n", - val, reg); + u32 reg = 0, val; if (gmac->variant->soc_has_internal_phy) { if (of_property_read_bool(node, "allwinner,leds-active-low")) reg |= H3_EPHY_LED_POL; - else - reg &= ~H3_EPHY_LED_POL; /* Force EPHY xtal frequency to 24MHz. */ reg |= H3_EPHY_CLK_SEL; - ret = of_mdio_parse_addr(priv->device, priv->plat->phy_node); + ret = of_mdio_parse_addr(dev, plat->phy_node); if (ret < 0) { - dev_err(priv->device, "Could not parse MDIO addr\n"); + dev_err(dev, "Could not parse MDIO addr\n"); return ret; } /* of_mdio_parse_addr returns a valid (0 ~ 31) PHY * address. No need to mask it again. */ - reg |= 1 << H3_EPHY_ADDR_SHIFT; - } else { - /* For SoCs without internal PHY the PHY selection bit should be - * set to 0 (external PHY). - */ - reg &= ~H3_EPHY_SELECT; + reg |= ret << H3_EPHY_ADDR_SHIFT; } if (!of_property_read_u32(node, "allwinner,tx-delay-ps", &val)) { if (val % 100) { - dev_err(priv->device, "tx-delay must be a multiple of 100\n"); + dev_err(dev, "tx-delay must be a multiple of 100\n"); return -EINVAL; } val /= 100; - dev_dbg(priv->device, "set tx-delay to %x\n", val); + dev_dbg(dev, "set tx-delay to %x\n", val); if (val <= gmac->variant->tx_delay_max) { - reg &= ~(gmac->variant->tx_delay_max << - SYSCON_ETXDC_SHIFT); reg |= (val << SYSCON_ETXDC_SHIFT); } else { - dev_err(priv->device, "Invalid TX clock delay: %d\n", + dev_err(dev, "Invalid TX clock delay: %d\n", val); return -EINVAL; } @@ -947,28 +960,21 @@ static int sun8i_dwmac_set_syscon(struct stmmac_priv *priv) if (!of_property_read_u32(node, "allwinner,rx-delay-ps", &val)) { if (val % 100) { - dev_err(priv->device, "rx-delay must be a multiple of 100\n"); + dev_err(dev, "rx-delay must be a multiple of 100\n"); return -EINVAL; } val /= 100; - dev_dbg(priv->device, "set rx-delay to %x\n", val); + dev_dbg(dev, "set rx-delay to %x\n", val); if (val <= gmac->variant->rx_delay_max) { - reg &= ~(gmac->variant->rx_delay_max << - SYSCON_ERXDC_SHIFT); reg |= (val << SYSCON_ERXDC_SHIFT); } else { - dev_err(priv->device, "Invalid RX clock delay: %d\n", + dev_err(dev, "Invalid RX clock delay: %d\n", val); return -EINVAL; } } - /* Clear interface mode bits */ - reg &= ~(SYSCON_ETCS_MASK | SYSCON_EPIT); - if (gmac->variant->support_rmii) - reg &= ~SYSCON_RMII_EN; - - switch (priv->plat->interface) { + switch (plat->phy_interface) { case PHY_INTERFACE_MODE_MII: /* default */ break; @@ -982,8 +988,8 @@ static int sun8i_dwmac_set_syscon(struct stmmac_priv *priv) reg |= SYSCON_RMII_EN | SYSCON_ETCS_EXT_GMII; break; default: - dev_err(priv->device, "Unsupported interface mode: %s", - phy_modes(priv->plat->interface)); + dev_err(dev, "Unsupported interface mode: %s", + phy_modes(plat->phy_interface)); return -EINVAL; } @@ -994,28 +1000,17 @@ static int sun8i_dwmac_set_syscon(struct stmmac_priv *priv) static void sun8i_dwmac_unset_syscon(struct sunxi_priv_data *gmac) { - u32 reg = gmac->variant->default_syscon_value; - - regmap_field_write(gmac->regmap_field, reg); + if (gmac->variant->soc_has_internal_phy) + regmap_field_write(gmac->regmap_field, + (H3_EPHY_SHUTDOWN | H3_EPHY_SELECT)); } -static void sun8i_dwmac_exit(struct platform_device *pdev, void *priv) +static void sun8i_dwmac_exit(struct device *dev, void *priv) { struct sunxi_priv_data *gmac = priv; - if (gmac->variant->soc_has_internal_phy) { - /* sun8i_dwmac_exit could be called with mdiomux uninit */ - if (gmac->mux_handle) - mdio_mux_uninit(gmac->mux_handle); - if (gmac->internal_phy_powered) - sun8i_dwmac_unpower_internal_phy(gmac); - } - - sun8i_dwmac_unset_syscon(gmac); - - reset_control_put(gmac->rst_ephy); - - clk_disable_unprepare(gmac->tx_clk); + if (gmac->variant->soc_has_internal_phy) + sun8i_dwmac_unpower_internal_phy(gmac); if (gmac->regulator) regulator_disable(gmac->regulator); @@ -1045,19 +1040,9 @@ static const struct stmmac_ops sun8i_dwmac_ops = { .set_mac_loopback = sun8i_dwmac_set_mac_loopback, }; -static struct mac_device_info *sun8i_dwmac_setup(void *ppriv) +static int sun8i_dwmac_setup(void *ppriv, struct mac_device_info *mac) { - struct mac_device_info *mac; struct stmmac_priv *priv = ppriv; - int ret; - - mac = devm_kzalloc(priv->device, sizeof(*mac), GFP_KERNEL); - if (!mac) - return NULL; - - ret = sun8i_dwmac_set_syscon(priv); - if (ret) - return NULL; mac->pcsr = priv->ioaddr; mac->mac = &sun8i_dwmac_ops; @@ -1065,6 +1050,8 @@ static struct mac_device_info *sun8i_dwmac_setup(void *ppriv) priv->dev->priv_flags |= IFF_UNICAST_FLT; + mac->link.caps = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | + MAC_10 | MAC_100 | MAC_1000; /* The loopback bit seems to be re-set when link change * Simply mask it each time * Speed 10/100/1000 are set in BIT(2)/BIT(3) @@ -1087,7 +1074,7 @@ static struct mac_device_info *sun8i_dwmac_setup(void *ppriv) /* Synopsys Id is not available */ priv->synopsys_id = 0; - return mac; + return 0; } static struct regmap *sun8i_dwmac_get_syscon_from_dev(struct device_node *node) @@ -1124,20 +1111,15 @@ static int sun8i_dwmac_probe(struct platform_device *pdev) struct stmmac_resources stmmac_res; struct sunxi_priv_data *gmac; struct device *dev = &pdev->dev; - phy_interface_t interface; - int ret; struct stmmac_priv *priv; struct net_device *ndev; struct regmap *regmap; + int ret; ret = stmmac_get_platform_resources(pdev, &stmmac_res); if (ret) return ret; - plat_dat = stmmac_probe_config_dt(pdev, &stmmac_res.mac); - if (IS_ERR(plat_dat)) - return PTR_ERR(plat_dat); - gmac = devm_kzalloc(dev, sizeof(*gmac), GFP_KERNEL); if (!gmac) return -ENOMEM; @@ -1148,12 +1130,6 @@ static int sun8i_dwmac_probe(struct platform_device *pdev) return -EINVAL; } - gmac->tx_clk = devm_clk_get(dev, "stmmaceth"); - if (IS_ERR(gmac->tx_clk)) { - dev_err(dev, "Could not get TX clock\n"); - return PTR_ERR(gmac->tx_clk); - } - /* Optional regulator for PHY */ gmac->regulator = devm_regulator_get_optional(dev, "phy"); if (IS_ERR(gmac->regulator)) { @@ -1198,39 +1174,47 @@ static int sun8i_dwmac_probe(struct platform_device *pdev) return ret; } - ret = of_get_phy_mode(dev->of_node, &interface); - if (ret) - return -EINVAL; - plat_dat->interface = interface; + plat_dat = devm_stmmac_probe_config_dt(pdev, stmmac_res.mac); + if (IS_ERR(plat_dat)) + return PTR_ERR(plat_dat); /* platform data specifying hardware features and callbacks. * hardware features were copied from Allwinner drivers. */ plat_dat->rx_coe = STMMAC_RX_COE_TYPE2; plat_dat->tx_coe = 1; - plat_dat->has_sun8i = true; + plat_dat->flags |= STMMAC_FLAG_HAS_SUN8I; plat_dat->bsp_priv = gmac; plat_dat->init = sun8i_dwmac_init; plat_dat->exit = sun8i_dwmac_exit; - plat_dat->setup = sun8i_dwmac_setup; + plat_dat->mac_setup = sun8i_dwmac_setup; + plat_dat->tx_fifo_size = 4096; + plat_dat->rx_fifo_size = 16384; - ret = sun8i_dwmac_init(pdev, plat_dat->bsp_priv); + ret = sun8i_dwmac_set_syscon(&pdev->dev, plat_dat); if (ret) return ret; - ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); + ret = stmmac_pltfr_probe(pdev, plat_dat, &stmmac_res); if (ret) - goto dwmac_exit; + goto dwmac_syscon; ndev = dev_get_drvdata(&pdev->dev); priv = netdev_priv(ndev); + + /* the MAC is runtime suspended after stmmac_dvr_probe(), so we + * need to ensure the MAC resume back before other operations such + * as reset. + */ + pm_runtime_get_sync(&pdev->dev); + /* The mux must be registered after parent MDIO * so after stmmac_dvr_probe() */ if (gmac->variant->soc_has_internal_phy) { ret = get_ephy_nodes(priv); if (ret) - goto dwmac_exit; + goto dwmac_remove; ret = sun8i_dwmac_register_mdio_mux(priv); if (ret) { dev_err(&pdev->dev, "Failed to register mux\n"); @@ -1239,15 +1223,49 @@ static int sun8i_dwmac_probe(struct platform_device *pdev) } else { ret = sun8i_dwmac_reset(priv); if (ret) - goto dwmac_exit; + goto dwmac_remove; } - return ret; + pm_runtime_put(&pdev->dev); + + return 0; + dwmac_mux: + reset_control_put(gmac->rst_ephy); + clk_put(gmac->ephy_clk); +dwmac_remove: + pm_runtime_put_noidle(&pdev->dev); + stmmac_pltfr_remove(pdev); +dwmac_syscon: sun8i_dwmac_unset_syscon(gmac); -dwmac_exit: + + return ret; +} + +static void sun8i_dwmac_remove(struct platform_device *pdev) +{ + struct net_device *ndev = platform_get_drvdata(pdev); + struct stmmac_priv *priv = netdev_priv(ndev); + struct sunxi_priv_data *gmac = priv->plat->bsp_priv; + + if (gmac->variant->soc_has_internal_phy) { + mdio_mux_uninit(gmac->mux_handle); + sun8i_dwmac_unpower_internal_phy(gmac); + reset_control_put(gmac->rst_ephy); + clk_put(gmac->ephy_clk); + } + stmmac_pltfr_remove(pdev); -return ret; + sun8i_dwmac_unset_syscon(gmac); +} + +static void sun8i_dwmac_shutdown(struct platform_device *pdev) +{ + struct net_device *ndev = platform_get_drvdata(pdev); + struct stmmac_priv *priv = netdev_priv(ndev); + struct sunxi_priv_data *gmac = priv->plat->bsp_priv; + + sun8i_dwmac_exit(&pdev->dev, gmac); } static const struct of_device_id sun8i_dwmac_match[] = { @@ -1269,7 +1287,8 @@ MODULE_DEVICE_TABLE(of, sun8i_dwmac_match); static struct platform_driver sun8i_dwmac_driver = { .probe = sun8i_dwmac_probe, - .remove = stmmac_pltfr_remove, + .remove = sun8i_dwmac_remove, + .shutdown = sun8i_dwmac_shutdown, .driver = { .name = "dwmac-sun8i", .pm = &stmmac_pltfr_pm_ops, |
