| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * dwmac-stm32.c - DWMAC Specific Glue layer for STM32 MCU |
|---|
| 3 | 4 | * |
|---|
| 4 | 5 | * Copyright (C) STMicroelectronics SA 2017 |
|---|
| 5 | 6 | * Author: Alexandre Torgue <alexandre.torgue@st.com> for STMicroelectronics. |
|---|
| 6 | | - * License terms: GNU General Public License (GPL), version 2 |
|---|
| 7 | | - * |
|---|
| 8 | 7 | */ |
|---|
| 9 | 8 | |
|---|
| 10 | 9 | #include <linux/clk.h> |
|---|
| .. | .. |
|---|
| 25 | 24 | |
|---|
| 26 | 25 | #define SYSCFG_MCU_ETH_MASK BIT(23) |
|---|
| 27 | 26 | #define SYSCFG_MP1_ETH_MASK GENMASK(23, 16) |
|---|
| 27 | +#define SYSCFG_PMCCLRR_OFFSET 0x40 |
|---|
| 28 | 28 | |
|---|
| 29 | 29 | #define SYSCFG_PMCR_ETH_CLK_SEL BIT(16) |
|---|
| 30 | 30 | #define SYSCFG_PMCR_ETH_REF_CLK_SEL BIT(17) |
|---|
| 31 | + |
|---|
| 32 | +/* CLOCK feed to PHY*/ |
|---|
| 33 | +#define ETH_CK_F_25M 25000000 |
|---|
| 34 | +#define ETH_CK_F_50M 50000000 |
|---|
| 35 | +#define ETH_CK_F_125M 125000000 |
|---|
| 36 | + |
|---|
| 37 | +/* Ethernet PHY interface selection in register SYSCFG Configuration |
|---|
| 38 | + *------------------------------------------ |
|---|
| 39 | + * src |BIT(23)| BIT(22)| BIT(21)|BIT(20)| |
|---|
| 40 | + *------------------------------------------ |
|---|
| 41 | + * MII | 0 | 0 | 0 | 1 | |
|---|
| 42 | + *------------------------------------------ |
|---|
| 43 | + * GMII | 0 | 0 | 0 | 0 | |
|---|
| 44 | + *------------------------------------------ |
|---|
| 45 | + * RGMII | 0 | 0 | 1 | n/a | |
|---|
| 46 | + *------------------------------------------ |
|---|
| 47 | + * RMII | 1 | 0 | 0 | n/a | |
|---|
| 48 | + *------------------------------------------ |
|---|
| 49 | + */ |
|---|
| 31 | 50 | #define SYSCFG_PMCR_ETH_SEL_MII BIT(20) |
|---|
| 32 | 51 | #define SYSCFG_PMCR_ETH_SEL_RGMII BIT(21) |
|---|
| 33 | 52 | #define SYSCFG_PMCR_ETH_SEL_RMII BIT(23) |
|---|
| .. | .. |
|---|
| 35 | 54 | #define SYSCFG_MCU_ETH_SEL_MII 0 |
|---|
| 36 | 55 | #define SYSCFG_MCU_ETH_SEL_RMII 1 |
|---|
| 37 | 56 | |
|---|
| 57 | +/* STM32MP1 register definitions |
|---|
| 58 | + * |
|---|
| 59 | + * Below table summarizes the clock requirement and clock sources for |
|---|
| 60 | + * supported phy interface modes. |
|---|
| 61 | + * __________________________________________________________________________ |
|---|
| 62 | + *|PHY_MODE | Normal | PHY wo crystal| PHY wo crystal |No 125Mhz from PHY| |
|---|
| 63 | + *| | | 25MHz | 50MHz | | |
|---|
| 64 | + * --------------------------------------------------------------------------- |
|---|
| 65 | + *| MII | - | eth-ck | n/a | n/a | |
|---|
| 66 | + *| | | st,ext-phyclk | | | |
|---|
| 67 | + * --------------------------------------------------------------------------- |
|---|
| 68 | + *| GMII | - | eth-ck | n/a | n/a | |
|---|
| 69 | + *| | | st,ext-phyclk | | | |
|---|
| 70 | + * --------------------------------------------------------------------------- |
|---|
| 71 | + *| RGMII | - | eth-ck | n/a | eth-ck | |
|---|
| 72 | + *| | | st,ext-phyclk | | st,eth-clk-sel or| |
|---|
| 73 | + *| | | | | st,ext-phyclk | |
|---|
| 74 | + * --------------------------------------------------------------------------- |
|---|
| 75 | + *| RMII | - | eth-ck | eth-ck | n/a | |
|---|
| 76 | + *| | | st,ext-phyclk | st,eth-ref-clk-sel | | |
|---|
| 77 | + *| | | | or st,ext-phyclk | | |
|---|
| 78 | + * --------------------------------------------------------------------------- |
|---|
| 79 | + * |
|---|
| 80 | + */ |
|---|
| 81 | + |
|---|
| 38 | 82 | struct stm32_dwmac { |
|---|
| 39 | 83 | struct clk *clk_tx; |
|---|
| 40 | 84 | struct clk *clk_rx; |
|---|
| 41 | 85 | struct clk *clk_eth_ck; |
|---|
| 42 | 86 | struct clk *clk_ethstp; |
|---|
| 43 | 87 | struct clk *syscfg_clk; |
|---|
| 44 | | - bool int_phyclk; /* Clock from RCC to drive PHY */ |
|---|
| 45 | | - u32 mode_reg; /* MAC glue-logic mode register */ |
|---|
| 88 | + int ext_phyclk; |
|---|
| 89 | + int enable_eth_ck; |
|---|
| 90 | + int eth_clk_sel_reg; |
|---|
| 91 | + int eth_ref_clk_sel_reg; |
|---|
| 92 | + int irq_pwr_wakeup; |
|---|
| 93 | + u32 mode_reg; /* MAC glue-logic mode register */ |
|---|
| 46 | 94 | struct regmap *regmap; |
|---|
| 47 | 95 | u32 speed; |
|---|
| 48 | 96 | const struct stm32_ops *ops; |
|---|
| .. | .. |
|---|
| 57 | 105 | int (*parse_data)(struct stm32_dwmac *dwmac, |
|---|
| 58 | 106 | struct device *dev); |
|---|
| 59 | 107 | u32 syscfg_eth_mask; |
|---|
| 108 | + bool clk_rx_enable_in_suspend; |
|---|
| 60 | 109 | }; |
|---|
| 61 | 110 | |
|---|
| 62 | 111 | static int stm32_dwmac_init(struct plat_stmmacenet_data *plat_dat) |
|---|
| .. | .. |
|---|
| 74 | 123 | if (ret) |
|---|
| 75 | 124 | return ret; |
|---|
| 76 | 125 | |
|---|
| 77 | | - if (!dwmac->dev->power.is_suspended) { |
|---|
| 126 | + if (!dwmac->ops->clk_rx_enable_in_suspend || |
|---|
| 127 | + !dwmac->dev->power.is_suspended) { |
|---|
| 78 | 128 | ret = clk_prepare_enable(dwmac->clk_rx); |
|---|
| 79 | 129 | if (ret) { |
|---|
| 80 | 130 | clk_disable_unprepare(dwmac->clk_tx); |
|---|
| .. | .. |
|---|
| 101 | 151 | ret = clk_prepare_enable(dwmac->syscfg_clk); |
|---|
| 102 | 152 | if (ret) |
|---|
| 103 | 153 | return ret; |
|---|
| 104 | | - |
|---|
| 105 | | - if (dwmac->int_phyclk) { |
|---|
| 154 | + if (dwmac->enable_eth_ck) { |
|---|
| 106 | 155 | ret = clk_prepare_enable(dwmac->clk_eth_ck); |
|---|
| 107 | 156 | if (ret) { |
|---|
| 108 | 157 | clk_disable_unprepare(dwmac->syscfg_clk); |
|---|
| .. | .. |
|---|
| 111 | 160 | } |
|---|
| 112 | 161 | } else { |
|---|
| 113 | 162 | clk_disable_unprepare(dwmac->syscfg_clk); |
|---|
| 114 | | - if (dwmac->int_phyclk) |
|---|
| 163 | + if (dwmac->enable_eth_ck) |
|---|
| 115 | 164 | clk_disable_unprepare(dwmac->clk_eth_ck); |
|---|
| 116 | 165 | } |
|---|
| 117 | 166 | return ret; |
|---|
| .. | .. |
|---|
| 120 | 169 | static int stm32mp1_set_mode(struct plat_stmmacenet_data *plat_dat) |
|---|
| 121 | 170 | { |
|---|
| 122 | 171 | struct stm32_dwmac *dwmac = plat_dat->bsp_priv; |
|---|
| 123 | | - u32 reg = dwmac->mode_reg; |
|---|
| 172 | + u32 reg = dwmac->mode_reg, clk_rate; |
|---|
| 124 | 173 | int val; |
|---|
| 125 | 174 | |
|---|
| 175 | + clk_rate = clk_get_rate(dwmac->clk_eth_ck); |
|---|
| 176 | + dwmac->enable_eth_ck = false; |
|---|
| 126 | 177 | switch (plat_dat->interface) { |
|---|
| 127 | 178 | case PHY_INTERFACE_MODE_MII: |
|---|
| 179 | + if (clk_rate == ETH_CK_F_25M && dwmac->ext_phyclk) |
|---|
| 180 | + dwmac->enable_eth_ck = true; |
|---|
| 128 | 181 | val = SYSCFG_PMCR_ETH_SEL_MII; |
|---|
| 129 | 182 | pr_debug("SYSCFG init : PHY_INTERFACE_MODE_MII\n"); |
|---|
| 130 | 183 | break; |
|---|
| 131 | 184 | case PHY_INTERFACE_MODE_GMII: |
|---|
| 132 | 185 | val = SYSCFG_PMCR_ETH_SEL_GMII; |
|---|
| 133 | | - if (dwmac->int_phyclk) |
|---|
| 186 | + if (clk_rate == ETH_CK_F_25M && |
|---|
| 187 | + (dwmac->eth_clk_sel_reg || dwmac->ext_phyclk)) { |
|---|
| 188 | + dwmac->enable_eth_ck = true; |
|---|
| 134 | 189 | val |= SYSCFG_PMCR_ETH_CLK_SEL; |
|---|
| 190 | + } |
|---|
| 135 | 191 | pr_debug("SYSCFG init : PHY_INTERFACE_MODE_GMII\n"); |
|---|
| 136 | 192 | break; |
|---|
| 137 | 193 | case PHY_INTERFACE_MODE_RMII: |
|---|
| 138 | 194 | val = SYSCFG_PMCR_ETH_SEL_RMII; |
|---|
| 139 | | - if (dwmac->int_phyclk) |
|---|
| 195 | + if ((clk_rate == ETH_CK_F_25M || clk_rate == ETH_CK_F_50M) && |
|---|
| 196 | + (dwmac->eth_ref_clk_sel_reg || dwmac->ext_phyclk)) { |
|---|
| 197 | + dwmac->enable_eth_ck = true; |
|---|
| 140 | 198 | val |= SYSCFG_PMCR_ETH_REF_CLK_SEL; |
|---|
| 199 | + } |
|---|
| 141 | 200 | pr_debug("SYSCFG init : PHY_INTERFACE_MODE_RMII\n"); |
|---|
| 142 | 201 | break; |
|---|
| 143 | 202 | case PHY_INTERFACE_MODE_RGMII: |
|---|
| 203 | + case PHY_INTERFACE_MODE_RGMII_ID: |
|---|
| 204 | + case PHY_INTERFACE_MODE_RGMII_RXID: |
|---|
| 205 | + case PHY_INTERFACE_MODE_RGMII_TXID: |
|---|
| 144 | 206 | val = SYSCFG_PMCR_ETH_SEL_RGMII; |
|---|
| 145 | | - if (dwmac->int_phyclk) |
|---|
| 207 | + if ((clk_rate == ETH_CK_F_25M || clk_rate == ETH_CK_F_125M) && |
|---|
| 208 | + (dwmac->eth_clk_sel_reg || dwmac->ext_phyclk)) { |
|---|
| 209 | + dwmac->enable_eth_ck = true; |
|---|
| 146 | 210 | val |= SYSCFG_PMCR_ETH_CLK_SEL; |
|---|
| 211 | + } |
|---|
| 147 | 212 | pr_debug("SYSCFG init : PHY_INTERFACE_MODE_RGMII\n"); |
|---|
| 148 | 213 | break; |
|---|
| 149 | 214 | default: |
|---|
| .. | .. |
|---|
| 153 | 218 | return -EINVAL; |
|---|
| 154 | 219 | } |
|---|
| 155 | 220 | |
|---|
| 221 | + /* Need to update PMCCLRR (clear register) */ |
|---|
| 222 | + regmap_write(dwmac->regmap, reg + SYSCFG_PMCCLRR_OFFSET, |
|---|
| 223 | + dwmac->ops->syscfg_eth_mask); |
|---|
| 224 | + |
|---|
| 225 | + /* Update PMCSETR (set register) */ |
|---|
| 156 | 226 | return regmap_update_bits(dwmac->regmap, reg, |
|---|
| 157 | 227 | dwmac->ops->syscfg_eth_mask, val); |
|---|
| 158 | 228 | } |
|---|
| .. | .. |
|---|
| 180 | 250 | } |
|---|
| 181 | 251 | |
|---|
| 182 | 252 | return regmap_update_bits(dwmac->regmap, reg, |
|---|
| 183 | | - dwmac->ops->syscfg_eth_mask, val); |
|---|
| 253 | + dwmac->ops->syscfg_eth_mask, val << 23); |
|---|
| 184 | 254 | } |
|---|
| 185 | 255 | |
|---|
| 186 | 256 | static void stm32_dwmac_clk_disable(struct stm32_dwmac *dwmac) |
|---|
| .. | .. |
|---|
| 232 | 302 | static int stm32mp1_parse_data(struct stm32_dwmac *dwmac, |
|---|
| 233 | 303 | struct device *dev) |
|---|
| 234 | 304 | { |
|---|
| 305 | + struct platform_device *pdev = to_platform_device(dev); |
|---|
| 235 | 306 | struct device_node *np = dev->of_node; |
|---|
| 307 | + int err = 0; |
|---|
| 236 | 308 | |
|---|
| 237 | | - dwmac->int_phyclk = of_property_read_bool(np, "st,int-phyclk"); |
|---|
| 309 | + /* Ethernet PHY have no crystal */ |
|---|
| 310 | + dwmac->ext_phyclk = of_property_read_bool(np, "st,ext-phyclk"); |
|---|
| 238 | 311 | |
|---|
| 239 | | - /* Check if internal clk from RCC selected */ |
|---|
| 240 | | - if (dwmac->int_phyclk) { |
|---|
| 241 | | - /* Get ETH_CLK clocks */ |
|---|
| 242 | | - dwmac->clk_eth_ck = devm_clk_get(dev, "eth-ck"); |
|---|
| 243 | | - if (IS_ERR(dwmac->clk_eth_ck)) { |
|---|
| 244 | | - dev_err(dev, "No ETH CK clock provided...\n"); |
|---|
| 245 | | - return PTR_ERR(dwmac->clk_eth_ck); |
|---|
| 246 | | - } |
|---|
| 312 | + /* Gigabit Ethernet 125MHz clock selection. */ |
|---|
| 313 | + dwmac->eth_clk_sel_reg = of_property_read_bool(np, "st,eth-clk-sel"); |
|---|
| 314 | + |
|---|
| 315 | + /* Ethernet 50Mhz RMII clock selection */ |
|---|
| 316 | + dwmac->eth_ref_clk_sel_reg = |
|---|
| 317 | + of_property_read_bool(np, "st,eth-ref-clk-sel"); |
|---|
| 318 | + |
|---|
| 319 | + /* Get ETH_CLK clocks */ |
|---|
| 320 | + dwmac->clk_eth_ck = devm_clk_get(dev, "eth-ck"); |
|---|
| 321 | + if (IS_ERR(dwmac->clk_eth_ck)) { |
|---|
| 322 | + dev_info(dev, "No phy clock provided...\n"); |
|---|
| 323 | + dwmac->clk_eth_ck = NULL; |
|---|
| 247 | 324 | } |
|---|
| 248 | 325 | |
|---|
| 249 | 326 | /* Clock used for low power mode */ |
|---|
| 250 | 327 | dwmac->clk_ethstp = devm_clk_get(dev, "ethstp"); |
|---|
| 251 | 328 | if (IS_ERR(dwmac->clk_ethstp)) { |
|---|
| 252 | | - dev_err(dev, "No ETH peripheral clock provided for CStop mode ...\n"); |
|---|
| 329 | + dev_err(dev, |
|---|
| 330 | + "No ETH peripheral clock provided for CStop mode ...\n"); |
|---|
| 253 | 331 | return PTR_ERR(dwmac->clk_ethstp); |
|---|
| 254 | 332 | } |
|---|
| 255 | 333 | |
|---|
| 256 | | - /* Clock for sysconfig */ |
|---|
| 334 | + /* Optional Clock for sysconfig */ |
|---|
| 257 | 335 | dwmac->syscfg_clk = devm_clk_get(dev, "syscfg-clk"); |
|---|
| 258 | | - if (IS_ERR(dwmac->syscfg_clk)) { |
|---|
| 259 | | - dev_err(dev, "No syscfg clock provided...\n"); |
|---|
| 260 | | - return PTR_ERR(dwmac->syscfg_clk); |
|---|
| 261 | | - } |
|---|
| 336 | + if (IS_ERR(dwmac->syscfg_clk)) |
|---|
| 337 | + dwmac->syscfg_clk = NULL; |
|---|
| 262 | 338 | |
|---|
| 263 | | - return 0; |
|---|
| 339 | + /* Get IRQ information early to have an ability to ask for deferred |
|---|
| 340 | + * probe if needed before we went too far with resource allocation. |
|---|
| 341 | + */ |
|---|
| 342 | + dwmac->irq_pwr_wakeup = platform_get_irq_byname_optional(pdev, |
|---|
| 343 | + "stm32_pwr_wakeup"); |
|---|
| 344 | + if (dwmac->irq_pwr_wakeup == -EPROBE_DEFER) |
|---|
| 345 | + return -EPROBE_DEFER; |
|---|
| 346 | + |
|---|
| 347 | + if (!dwmac->clk_eth_ck && dwmac->irq_pwr_wakeup >= 0) { |
|---|
| 348 | + err = device_init_wakeup(&pdev->dev, true); |
|---|
| 349 | + if (err) { |
|---|
| 350 | + dev_err(&pdev->dev, "Failed to init wake up irq\n"); |
|---|
| 351 | + return err; |
|---|
| 352 | + } |
|---|
| 353 | + err = dev_pm_set_dedicated_wake_irq(&pdev->dev, |
|---|
| 354 | + dwmac->irq_pwr_wakeup); |
|---|
| 355 | + if (err) { |
|---|
| 356 | + dev_err(&pdev->dev, "Failed to set wake up irq\n"); |
|---|
| 357 | + device_init_wakeup(&pdev->dev, false); |
|---|
| 358 | + } |
|---|
| 359 | + device_set_wakeup_enable(&pdev->dev, false); |
|---|
| 360 | + } |
|---|
| 361 | + return err; |
|---|
| 264 | 362 | } |
|---|
| 265 | 363 | |
|---|
| 266 | 364 | static int stm32_dwmac_probe(struct platform_device *pdev) |
|---|
| .. | .. |
|---|
| 326 | 424 | struct net_device *ndev = platform_get_drvdata(pdev); |
|---|
| 327 | 425 | struct stmmac_priv *priv = netdev_priv(ndev); |
|---|
| 328 | 426 | int ret = stmmac_dvr_remove(&pdev->dev); |
|---|
| 427 | + struct stm32_dwmac *dwmac = priv->plat->bsp_priv; |
|---|
| 329 | 428 | |
|---|
| 330 | 429 | stm32_dwmac_clk_disable(priv->plat->bsp_priv); |
|---|
| 430 | + |
|---|
| 431 | + if (dwmac->irq_pwr_wakeup >= 0) { |
|---|
| 432 | + dev_pm_clear_wake_irq(&pdev->dev); |
|---|
| 433 | + device_init_wakeup(&pdev->dev, false); |
|---|
| 434 | + } |
|---|
| 331 | 435 | |
|---|
| 332 | 436 | return ret; |
|---|
| 333 | 437 | } |
|---|
| .. | .. |
|---|
| 342 | 446 | |
|---|
| 343 | 447 | clk_disable_unprepare(dwmac->clk_tx); |
|---|
| 344 | 448 | clk_disable_unprepare(dwmac->syscfg_clk); |
|---|
| 345 | | - if (dwmac->int_phyclk) |
|---|
| 449 | + if (dwmac->enable_eth_ck) |
|---|
| 346 | 450 | clk_disable_unprepare(dwmac->clk_eth_ck); |
|---|
| 347 | 451 | |
|---|
| 348 | 452 | return ret; |
|---|
| .. | .. |
|---|
| 413 | 517 | .suspend = stm32mp1_suspend, |
|---|
| 414 | 518 | .resume = stm32mp1_resume, |
|---|
| 415 | 519 | .parse_data = stm32mp1_parse_data, |
|---|
| 416 | | - .syscfg_eth_mask = SYSCFG_MP1_ETH_MASK |
|---|
| 520 | + .syscfg_eth_mask = SYSCFG_MP1_ETH_MASK, |
|---|
| 521 | + .clk_rx_enable_in_suspend = true |
|---|
| 417 | 522 | }; |
|---|
| 418 | 523 | |
|---|
| 419 | 524 | static const struct of_device_id stm32_dwmac_match[] = { |
|---|