| .. | .. |
|---|
| 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; |
|---|
| .. | .. |
|---|
| 101 | 149 | ret = clk_prepare_enable(dwmac->syscfg_clk); |
|---|
| 102 | 150 | if (ret) |
|---|
| 103 | 151 | return ret; |
|---|
| 104 | | - |
|---|
| 105 | | - if (dwmac->int_phyclk) { |
|---|
| 152 | + if (dwmac->enable_eth_ck) { |
|---|
| 106 | 153 | ret = clk_prepare_enable(dwmac->clk_eth_ck); |
|---|
| 107 | 154 | if (ret) { |
|---|
| 108 | 155 | clk_disable_unprepare(dwmac->syscfg_clk); |
|---|
| .. | .. |
|---|
| 111 | 158 | } |
|---|
| 112 | 159 | } else { |
|---|
| 113 | 160 | clk_disable_unprepare(dwmac->syscfg_clk); |
|---|
| 114 | | - if (dwmac->int_phyclk) |
|---|
| 161 | + if (dwmac->enable_eth_ck) |
|---|
| 115 | 162 | clk_disable_unprepare(dwmac->clk_eth_ck); |
|---|
| 116 | 163 | } |
|---|
| 117 | 164 | return ret; |
|---|
| .. | .. |
|---|
| 120 | 167 | static int stm32mp1_set_mode(struct plat_stmmacenet_data *plat_dat) |
|---|
| 121 | 168 | { |
|---|
| 122 | 169 | struct stm32_dwmac *dwmac = plat_dat->bsp_priv; |
|---|
| 123 | | - u32 reg = dwmac->mode_reg; |
|---|
| 170 | + u32 reg = dwmac->mode_reg, clk_rate; |
|---|
| 124 | 171 | int val; |
|---|
| 125 | 172 | |
|---|
| 173 | + clk_rate = clk_get_rate(dwmac->clk_eth_ck); |
|---|
| 174 | + dwmac->enable_eth_ck = false; |
|---|
| 126 | 175 | switch (plat_dat->interface) { |
|---|
| 127 | 176 | case PHY_INTERFACE_MODE_MII: |
|---|
| 177 | + if (clk_rate == ETH_CK_F_25M && dwmac->ext_phyclk) |
|---|
| 178 | + dwmac->enable_eth_ck = true; |
|---|
| 128 | 179 | val = SYSCFG_PMCR_ETH_SEL_MII; |
|---|
| 129 | 180 | pr_debug("SYSCFG init : PHY_INTERFACE_MODE_MII\n"); |
|---|
| 130 | 181 | break; |
|---|
| 131 | 182 | case PHY_INTERFACE_MODE_GMII: |
|---|
| 132 | 183 | val = SYSCFG_PMCR_ETH_SEL_GMII; |
|---|
| 133 | | - if (dwmac->int_phyclk) |
|---|
| 184 | + if (clk_rate == ETH_CK_F_25M && |
|---|
| 185 | + (dwmac->eth_clk_sel_reg || dwmac->ext_phyclk)) { |
|---|
| 186 | + dwmac->enable_eth_ck = true; |
|---|
| 134 | 187 | val |= SYSCFG_PMCR_ETH_CLK_SEL; |
|---|
| 188 | + } |
|---|
| 135 | 189 | pr_debug("SYSCFG init : PHY_INTERFACE_MODE_GMII\n"); |
|---|
| 136 | 190 | break; |
|---|
| 137 | 191 | case PHY_INTERFACE_MODE_RMII: |
|---|
| 138 | 192 | val = SYSCFG_PMCR_ETH_SEL_RMII; |
|---|
| 139 | | - if (dwmac->int_phyclk) |
|---|
| 193 | + if ((clk_rate == ETH_CK_F_25M || clk_rate == ETH_CK_F_50M) && |
|---|
| 194 | + (dwmac->eth_ref_clk_sel_reg || dwmac->ext_phyclk)) { |
|---|
| 195 | + dwmac->enable_eth_ck = true; |
|---|
| 140 | 196 | val |= SYSCFG_PMCR_ETH_REF_CLK_SEL; |
|---|
| 197 | + } |
|---|
| 141 | 198 | pr_debug("SYSCFG init : PHY_INTERFACE_MODE_RMII\n"); |
|---|
| 142 | 199 | break; |
|---|
| 143 | 200 | case PHY_INTERFACE_MODE_RGMII: |
|---|
| 201 | + case PHY_INTERFACE_MODE_RGMII_ID: |
|---|
| 202 | + case PHY_INTERFACE_MODE_RGMII_RXID: |
|---|
| 203 | + case PHY_INTERFACE_MODE_RGMII_TXID: |
|---|
| 144 | 204 | val = SYSCFG_PMCR_ETH_SEL_RGMII; |
|---|
| 145 | | - if (dwmac->int_phyclk) |
|---|
| 205 | + if ((clk_rate == ETH_CK_F_25M || clk_rate == ETH_CK_F_125M) && |
|---|
| 206 | + (dwmac->eth_clk_sel_reg || dwmac->ext_phyclk)) { |
|---|
| 207 | + dwmac->enable_eth_ck = true; |
|---|
| 146 | 208 | val |= SYSCFG_PMCR_ETH_CLK_SEL; |
|---|
| 209 | + } |
|---|
| 147 | 210 | pr_debug("SYSCFG init : PHY_INTERFACE_MODE_RGMII\n"); |
|---|
| 148 | 211 | break; |
|---|
| 149 | 212 | default: |
|---|
| .. | .. |
|---|
| 153 | 216 | return -EINVAL; |
|---|
| 154 | 217 | } |
|---|
| 155 | 218 | |
|---|
| 219 | + /* Need to update PMCCLRR (clear register) */ |
|---|
| 220 | + regmap_write(dwmac->regmap, reg + SYSCFG_PMCCLRR_OFFSET, |
|---|
| 221 | + dwmac->ops->syscfg_eth_mask); |
|---|
| 222 | + |
|---|
| 223 | + /* Update PMCSETR (set register) */ |
|---|
| 156 | 224 | return regmap_update_bits(dwmac->regmap, reg, |
|---|
| 157 | 225 | dwmac->ops->syscfg_eth_mask, val); |
|---|
| 158 | 226 | } |
|---|
| .. | .. |
|---|
| 180 | 248 | } |
|---|
| 181 | 249 | |
|---|
| 182 | 250 | return regmap_update_bits(dwmac->regmap, reg, |
|---|
| 183 | | - dwmac->ops->syscfg_eth_mask, val); |
|---|
| 251 | + dwmac->ops->syscfg_eth_mask, val << 23); |
|---|
| 184 | 252 | } |
|---|
| 185 | 253 | |
|---|
| 186 | 254 | static void stm32_dwmac_clk_disable(struct stm32_dwmac *dwmac) |
|---|
| .. | .. |
|---|
| 232 | 300 | static int stm32mp1_parse_data(struct stm32_dwmac *dwmac, |
|---|
| 233 | 301 | struct device *dev) |
|---|
| 234 | 302 | { |
|---|
| 303 | + struct platform_device *pdev = to_platform_device(dev); |
|---|
| 235 | 304 | struct device_node *np = dev->of_node; |
|---|
| 305 | + int err = 0; |
|---|
| 236 | 306 | |
|---|
| 237 | | - dwmac->int_phyclk = of_property_read_bool(np, "st,int-phyclk"); |
|---|
| 307 | + /* Ethernet PHY have no crystal */ |
|---|
| 308 | + dwmac->ext_phyclk = of_property_read_bool(np, "st,ext-phyclk"); |
|---|
| 238 | 309 | |
|---|
| 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 | | - } |
|---|
| 310 | + /* Gigabit Ethernet 125MHz clock selection. */ |
|---|
| 311 | + dwmac->eth_clk_sel_reg = of_property_read_bool(np, "st,eth-clk-sel"); |
|---|
| 312 | + |
|---|
| 313 | + /* Ethernet 50Mhz RMII clock selection */ |
|---|
| 314 | + dwmac->eth_ref_clk_sel_reg = |
|---|
| 315 | + of_property_read_bool(np, "st,eth-ref-clk-sel"); |
|---|
| 316 | + |
|---|
| 317 | + /* Get ETH_CLK clocks */ |
|---|
| 318 | + dwmac->clk_eth_ck = devm_clk_get(dev, "eth-ck"); |
|---|
| 319 | + if (IS_ERR(dwmac->clk_eth_ck)) { |
|---|
| 320 | + dev_info(dev, "No phy clock provided...\n"); |
|---|
| 321 | + dwmac->clk_eth_ck = NULL; |
|---|
| 247 | 322 | } |
|---|
| 248 | 323 | |
|---|
| 249 | 324 | /* Clock used for low power mode */ |
|---|
| 250 | 325 | dwmac->clk_ethstp = devm_clk_get(dev, "ethstp"); |
|---|
| 251 | 326 | if (IS_ERR(dwmac->clk_ethstp)) { |
|---|
| 252 | | - dev_err(dev, "No ETH peripheral clock provided for CStop mode ...\n"); |
|---|
| 327 | + dev_err(dev, |
|---|
| 328 | + "No ETH peripheral clock provided for CStop mode ...\n"); |
|---|
| 253 | 329 | return PTR_ERR(dwmac->clk_ethstp); |
|---|
| 254 | 330 | } |
|---|
| 255 | 331 | |
|---|
| 256 | | - /* Clock for sysconfig */ |
|---|
| 332 | + /* Optional Clock for sysconfig */ |
|---|
| 257 | 333 | 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 | | - } |
|---|
| 334 | + if (IS_ERR(dwmac->syscfg_clk)) |
|---|
| 335 | + dwmac->syscfg_clk = NULL; |
|---|
| 262 | 336 | |
|---|
| 263 | | - return 0; |
|---|
| 337 | + /* Get IRQ information early to have an ability to ask for deferred |
|---|
| 338 | + * probe if needed before we went too far with resource allocation. |
|---|
| 339 | + */ |
|---|
| 340 | + dwmac->irq_pwr_wakeup = platform_get_irq_byname_optional(pdev, |
|---|
| 341 | + "stm32_pwr_wakeup"); |
|---|
| 342 | + if (dwmac->irq_pwr_wakeup == -EPROBE_DEFER) |
|---|
| 343 | + return -EPROBE_DEFER; |
|---|
| 344 | + |
|---|
| 345 | + if (!dwmac->clk_eth_ck && dwmac->irq_pwr_wakeup >= 0) { |
|---|
| 346 | + err = device_init_wakeup(&pdev->dev, true); |
|---|
| 347 | + if (err) { |
|---|
| 348 | + dev_err(&pdev->dev, "Failed to init wake up irq\n"); |
|---|
| 349 | + return err; |
|---|
| 350 | + } |
|---|
| 351 | + err = dev_pm_set_dedicated_wake_irq(&pdev->dev, |
|---|
| 352 | + dwmac->irq_pwr_wakeup); |
|---|
| 353 | + if (err) { |
|---|
| 354 | + dev_err(&pdev->dev, "Failed to set wake up irq\n"); |
|---|
| 355 | + device_init_wakeup(&pdev->dev, false); |
|---|
| 356 | + } |
|---|
| 357 | + device_set_wakeup_enable(&pdev->dev, false); |
|---|
| 358 | + } |
|---|
| 359 | + return err; |
|---|
| 264 | 360 | } |
|---|
| 265 | 361 | |
|---|
| 266 | 362 | static int stm32_dwmac_probe(struct platform_device *pdev) |
|---|
| .. | .. |
|---|
| 326 | 422 | struct net_device *ndev = platform_get_drvdata(pdev); |
|---|
| 327 | 423 | struct stmmac_priv *priv = netdev_priv(ndev); |
|---|
| 328 | 424 | int ret = stmmac_dvr_remove(&pdev->dev); |
|---|
| 425 | + struct stm32_dwmac *dwmac = priv->plat->bsp_priv; |
|---|
| 329 | 426 | |
|---|
| 330 | 427 | stm32_dwmac_clk_disable(priv->plat->bsp_priv); |
|---|
| 428 | + |
|---|
| 429 | + if (dwmac->irq_pwr_wakeup >= 0) { |
|---|
| 430 | + dev_pm_clear_wake_irq(&pdev->dev); |
|---|
| 431 | + device_init_wakeup(&pdev->dev, false); |
|---|
| 432 | + } |
|---|
| 331 | 433 | |
|---|
| 332 | 434 | return ret; |
|---|
| 333 | 435 | } |
|---|
| .. | .. |
|---|
| 342 | 444 | |
|---|
| 343 | 445 | clk_disable_unprepare(dwmac->clk_tx); |
|---|
| 344 | 446 | clk_disable_unprepare(dwmac->syscfg_clk); |
|---|
| 345 | | - if (dwmac->int_phyclk) |
|---|
| 447 | + if (dwmac->enable_eth_ck) |
|---|
| 346 | 448 | clk_disable_unprepare(dwmac->clk_eth_ck); |
|---|
| 347 | 449 | |
|---|
| 348 | 450 | return ret; |
|---|