Change glue to be more generic and manage easily next stm32 products. Signed-off-by: Christophe Roullier <christophe.roullier@xxxxxxxxxxx> --- .../net/ethernet/stmicro/stmmac/dwmac-stm32.c | 205 ++++++++++++------ 1 file changed, 139 insertions(+), 66 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-stm32.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-stm32.c index ab4324ed46da..762c5256398f 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-stm32.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-stm32.c @@ -14,6 +14,7 @@ #include <linux/of_net.h> #include <linux/phy.h> #include <linux/platform_device.h> +#include <linux/regulator/consumer.h> #include <linux/pm_wakeirq.h> #include <linux/regmap.h> #include <linux/slab.h> @@ -23,10 +24,6 @@ #define SYSCFG_MCU_ETH_MASK BIT(23) #define SYSCFG_MP1_ETH_MASK GENMASK(23, 16) -#define SYSCFG_PMCCLRR_OFFSET 0x40 - -#define SYSCFG_PMCR_ETH_CLK_SEL BIT(16) -#define SYSCFG_PMCR_ETH_REF_CLK_SEL BIT(17) /* CLOCK feed to PHY*/ #define ETH_CK_F_25M 25000000 @@ -46,9 +43,6 @@ * RMII | 1 | 0 | 0 | n/a | *------------------------------------------ */ -#define SYSCFG_PMCR_ETH_SEL_MII BIT(20) -#define SYSCFG_PMCR_ETH_SEL_RGMII BIT(21) -#define SYSCFG_PMCR_ETH_SEL_RMII BIT(23) #define SYSCFG_PMCR_ETH_SEL_GMII 0 #define SYSCFG_MCU_ETH_SEL_MII 0 #define SYSCFG_MCU_ETH_SEL_RMII 1 @@ -62,17 +56,17 @@ *| | | 25MHz | 50MHz | | * --------------------------------------------------------------------------- *| MII | - | eth-ck | n/a | n/a | - *| | | st,ext-phyclk | | | + *| | | | | | * --------------------------------------------------------------------------- *| GMII | - | eth-ck | n/a | n/a | - *| | | st,ext-phyclk | | | + *| | | | | | * --------------------------------------------------------------------------- *| RGMII | - | eth-ck | n/a | eth-ck | - *| | | st,ext-phyclk | | st,eth-clk-sel or| + *| | | | | st,eth-clk-sel or| *| | | | | st,ext-phyclk | * --------------------------------------------------------------------------- *| RMII | - | eth-ck | eth-ck | n/a | - *| | | st,ext-phyclk | st,eth-ref-clk-sel | | + *| | | | st,eth-ref-clk-sel | | *| | | | or st,ext-phyclk | | * --------------------------------------------------------------------------- * @@ -88,14 +82,27 @@ struct stm32_dwmac { int enable_eth_ck; int eth_clk_sel_reg; int eth_ref_clk_sel_reg; - int irq_pwr_wakeup; u32 mode_reg; /* MAC glue-logic mode register */ + u32 mode_mask; struct regmap *regmap; + struct regulator *regulator; u32 speed; const struct stm32_ops *ops; struct device *dev; }; +struct stm32_syscfg_pmcsetr { + u32 eth1_clk_sel; + u32 eth1_ref_clk_sel; + u32 eth1_selmii; + u32 eth1_sel_rgmii; + u32 eth1_sel_rmii; + u32 eth2_clk_sel; + u32 eth2_ref_clk_sel; + u32 eth2_sel_rgmii; + u32 eth2_sel_rmii; +}; + struct stm32_ops { int (*set_mode)(struct plat_stmmacenet_data *plat_dat); int (*clk_prepare)(struct stm32_dwmac *dwmac, bool prepare); @@ -103,7 +110,8 @@ struct stm32_ops { void (*resume)(struct stm32_dwmac *dwmac); int (*parse_data)(struct stm32_dwmac *dwmac, struct device *dev); - u32 syscfg_eth_mask; + u32 syscfg_clr_off; + struct stm32_syscfg_pmcsetr pmcsetr; }; static int stm32_dwmac_init(struct plat_stmmacenet_data *plat_dat) @@ -173,26 +181,26 @@ static int stm32mp1_set_mode(struct plat_stmmacenet_data *plat_dat) dwmac->enable_eth_ck = false; switch (plat_dat->mac_interface) { case PHY_INTERFACE_MODE_MII: - if (clk_rate == ETH_CK_F_25M && dwmac->ext_phyclk) + if (clk_rate == ETH_CK_F_25M) dwmac->enable_eth_ck = true; - val = SYSCFG_PMCR_ETH_SEL_MII; + val = dwmac->ops->pmcsetr.eth1_selmii; pr_debug("SYSCFG init : PHY_INTERFACE_MODE_MII\n"); break; case PHY_INTERFACE_MODE_GMII: val = SYSCFG_PMCR_ETH_SEL_GMII; - if (clk_rate == ETH_CK_F_25M && - (dwmac->eth_clk_sel_reg || dwmac->ext_phyclk)) { + if (clk_rate == ETH_CK_F_25M) dwmac->enable_eth_ck = true; - val |= SYSCFG_PMCR_ETH_CLK_SEL; - } pr_debug("SYSCFG init : PHY_INTERFACE_MODE_GMII\n"); break; case PHY_INTERFACE_MODE_RMII: - val = SYSCFG_PMCR_ETH_SEL_RMII; - if ((clk_rate == ETH_CK_F_25M || clk_rate == ETH_CK_F_50M) && + val = dwmac->ops->pmcsetr.eth1_sel_rmii | dwmac->ops->pmcsetr.eth2_sel_rmii; + if (clk_rate == ETH_CK_F_25M) + dwmac->enable_eth_ck = true; + if ((clk_rate == ETH_CK_F_50M) && (dwmac->eth_ref_clk_sel_reg || dwmac->ext_phyclk)) { dwmac->enable_eth_ck = true; - val |= SYSCFG_PMCR_ETH_REF_CLK_SEL; + val |= dwmac->ops->pmcsetr.eth1_ref_clk_sel; + val |= dwmac->ops->pmcsetr.eth2_ref_clk_sel; } pr_debug("SYSCFG init : PHY_INTERFACE_MODE_RMII\n"); break; @@ -200,11 +208,14 @@ static int stm32mp1_set_mode(struct plat_stmmacenet_data *plat_dat) case PHY_INTERFACE_MODE_RGMII_ID: case PHY_INTERFACE_MODE_RGMII_RXID: case PHY_INTERFACE_MODE_RGMII_TXID: - val = SYSCFG_PMCR_ETH_SEL_RGMII; - if ((clk_rate == ETH_CK_F_25M || clk_rate == ETH_CK_F_125M) && + val = dwmac->ops->pmcsetr.eth1_sel_rgmii | dwmac->ops->pmcsetr.eth2_sel_rgmii; + if (clk_rate == ETH_CK_F_25M) + dwmac->enable_eth_ck = true; + if ((clk_rate == ETH_CK_F_125M) && (dwmac->eth_clk_sel_reg || dwmac->ext_phyclk)) { dwmac->enable_eth_ck = true; - val |= SYSCFG_PMCR_ETH_CLK_SEL; + val |= dwmac->ops->pmcsetr.eth1_clk_sel; + val |= dwmac->ops->pmcsetr.eth2_clk_sel; } pr_debug("SYSCFG init : PHY_INTERFACE_MODE_RGMII\n"); break; @@ -216,12 +227,12 @@ static int stm32mp1_set_mode(struct plat_stmmacenet_data *plat_dat) } /* Need to update PMCCLRR (clear register) */ - regmap_write(dwmac->regmap, reg + SYSCFG_PMCCLRR_OFFSET, - dwmac->ops->syscfg_eth_mask); + regmap_write(dwmac->regmap, dwmac->ops->syscfg_clr_off, + dwmac->mode_mask); /* Update PMCSETR (set register) */ return regmap_update_bits(dwmac->regmap, reg, - dwmac->ops->syscfg_eth_mask, val); + dwmac->mode_mask, val); } static int stm32mcu_set_mode(struct plat_stmmacenet_data *plat_dat) @@ -247,7 +258,7 @@ static int stm32mcu_set_mode(struct plat_stmmacenet_data *plat_dat) } return regmap_update_bits(dwmac->regmap, reg, - dwmac->ops->syscfg_eth_mask, val << 23); + SYSCFG_MCU_ETH_MASK, val << 23); } static void stm32_dwmac_clk_disable(struct stm32_dwmac *dwmac) @@ -290,18 +301,33 @@ static int stm32_dwmac_parse_data(struct stm32_dwmac *dwmac, return PTR_ERR(dwmac->regmap); err = of_property_read_u32_index(np, "st,syscon", 1, &dwmac->mode_reg); + if (err) { + dev_err(dev, "Can't get sysconfig register offset (%d)\n", err); + return err; + } + + dwmac->mode_mask = SYSCFG_MP1_ETH_MASK; + err = of_property_read_u32_index(np, "st,syscon", 2, &dwmac->mode_mask); if (err) - dev_err(dev, "Can't get sysconfig mode offset (%d)\n", err); + pr_debug("Warning sysconfig register mask not set\n"); - return err; + dwmac->regulator = devm_regulator_get_optional(dev, "phy"); + if (IS_ERR(dwmac->regulator)) { + if (PTR_ERR(dwmac->regulator) == -EPROBE_DEFER) { + dev_dbg(dev, "phy regulator is not available yet, deferred probing\n"); + return -EPROBE_DEFER; + } + dev_dbg(dev, "no regulator found\n"); + dwmac->regulator = NULL; + } + + return 0; } static int stm32mp1_parse_data(struct stm32_dwmac *dwmac, struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); struct device_node *np = dev->of_node; - int err = 0; /* Ethernet PHY have no crystal */ dwmac->ext_phyclk = of_property_read_bool(np, "st,ext-phyclk"); @@ -316,7 +342,7 @@ static int stm32mp1_parse_data(struct stm32_dwmac *dwmac, /* Get ETH_CLK clocks */ dwmac->clk_eth_ck = devm_clk_get(dev, "eth-ck"); if (IS_ERR(dwmac->clk_eth_ck)) { - dev_info(dev, "No phy clock provided...\n"); + dev_dbg(dev, "No phy clock provided...\n"); dwmac->clk_eth_ck = NULL; } @@ -333,29 +359,45 @@ static int stm32mp1_parse_data(struct stm32_dwmac *dwmac, if (IS_ERR(dwmac->syscfg_clk)) dwmac->syscfg_clk = NULL; - /* Get IRQ information early to have an ability to ask for deferred - * probe if needed before we went too far with resource allocation. - */ - dwmac->irq_pwr_wakeup = platform_get_irq_byname_optional(pdev, - "stm32_pwr_wakeup"); - if (dwmac->irq_pwr_wakeup == -EPROBE_DEFER) - return -EPROBE_DEFER; - - if (!dwmac->clk_eth_ck && dwmac->irq_pwr_wakeup >= 0) { - err = device_init_wakeup(&pdev->dev, true); - if (err) { - dev_err(&pdev->dev, "Failed to init wake up irq\n"); - return err; - } - err = dev_pm_set_dedicated_wake_irq(&pdev->dev, - dwmac->irq_pwr_wakeup); - if (err) { - dev_err(&pdev->dev, "Failed to set wake up irq\n"); - device_init_wakeup(&pdev->dev, false); - } - device_set_wakeup_enable(&pdev->dev, false); + return 0; +} + +static int stm32_dwmac_wake_init(struct device *dev, + struct stmmac_resources *stmmac_res) +{ + int err; + + device_set_wakeup_capable(dev, true); + + err = dev_pm_set_wake_irq(dev, stmmac_res->wol_irq); + if (err) { + dev_err(dev, "Failed to set wake up irq\n"); + device_set_wakeup_capable(dev, false); + return err; + } + + return 0; +} + +static int phy_power_on(struct stm32_dwmac *bsp_priv, bool enable) +{ + int ret; + struct device *dev = bsp_priv->dev; + + if (!bsp_priv->regulator) + return 0; + + if (enable) { + ret = regulator_enable(bsp_priv->regulator); + if (ret) + dev_err(dev, "fail to enable phy-supply\n"); + } else { + ret = regulator_disable(bsp_priv->regulator); + if (ret) + dev_err(dev, "fail to disable phy-supply\n"); } - return err; + + return 0; } static int stm32_dwmac_probe(struct platform_device *pdev) @@ -393,21 +435,37 @@ static int stm32_dwmac_probe(struct platform_device *pdev) return ret; } + if (stmmac_res.wol_irq && !dwmac->clk_eth_ck) { + ret = stm32_dwmac_wake_init(&pdev->dev, &stmmac_res); + if (ret) + goto err_wake_init_disable; + } + plat_dat->bsp_priv = dwmac; ret = stm32_dwmac_init(plat_dat); if (ret) - return ret; + goto err_wake_init_disable; - ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); + ret = phy_power_on(plat_dat->bsp_priv, true); if (ret) goto err_clk_disable; + ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); + if (ret) + goto err_gmac_powerdown; + return 0; +err_gmac_powerdown: + phy_power_on(plat_dat->bsp_priv, false); err_clk_disable: stm32_dwmac_clk_disable(dwmac); - +err_wake_init_disable: + if (stmmac_res.wol_irq && !dwmac->clk_eth_ck) { + dev_pm_clear_wake_irq(&pdev->dev); + device_set_wakeup_capable(&pdev->dev, false); + } return ret; } @@ -415,16 +473,13 @@ static void stm32_dwmac_remove(struct platform_device *pdev) { struct net_device *ndev = platform_get_drvdata(pdev); struct stmmac_priv *priv = netdev_priv(ndev); - struct stm32_dwmac *dwmac = priv->plat->bsp_priv; stmmac_dvr_remove(&pdev->dev); stm32_dwmac_clk_disable(priv->plat->bsp_priv); - if (dwmac->irq_pwr_wakeup >= 0) { - dev_pm_clear_wake_irq(&pdev->dev); - device_init_wakeup(&pdev->dev, false); - } + dev_pm_clear_wake_irq(&pdev->dev); + device_init_wakeup(&pdev->dev, false); } static int stm32mp1_suspend(struct stm32_dwmac *dwmac) @@ -440,12 +495,20 @@ static int stm32mp1_suspend(struct stm32_dwmac *dwmac) if (dwmac->enable_eth_ck) clk_disable_unprepare(dwmac->clk_eth_ck); + /* Keep the PHY up if we use Wake-on-Lan. */ + if (!device_may_wakeup(dwmac->dev)) + phy_power_on(dwmac, false); + return ret; } static void stm32mp1_resume(struct stm32_dwmac *dwmac) { clk_disable_unprepare(dwmac->clk_ethstp); + + /* The PHY was up for Wake-on-Lan. */ + if (!device_may_wakeup(dwmac->dev)) + phy_power_on(dwmac, true); } static int stm32mcu_suspend(struct stm32_dwmac *dwmac) @@ -499,7 +562,6 @@ static SIMPLE_DEV_PM_OPS(stm32_dwmac_pm_ops, static struct stm32_ops stm32mcu_dwmac_data = { .set_mode = stm32mcu_set_mode, .suspend = stm32mcu_suspend, - .syscfg_eth_mask = SYSCFG_MCU_ETH_MASK }; static struct stm32_ops stm32mp1_dwmac_data = { @@ -508,7 +570,18 @@ static struct stm32_ops stm32mp1_dwmac_data = { .suspend = stm32mp1_suspend, .resume = stm32mp1_resume, .parse_data = stm32mp1_parse_data, - .syscfg_eth_mask = SYSCFG_MP1_ETH_MASK + .syscfg_clr_off = 0x44, + .pmcsetr = { + .eth1_clk_sel = BIT(16), + .eth1_ref_clk_sel = BIT(17), + .eth1_selmii = BIT(20), + .eth1_sel_rgmii = BIT(21), + .eth1_sel_rmii = BIT(23), + .eth2_clk_sel = 0, + .eth2_ref_clk_sel = 0, + .eth2_sel_rgmii = 0, + .eth2_sel_rmii = 0 + } }; static const struct of_device_id stm32_dwmac_match[] = { -- 2.25.1