.. | .. |
---|
| 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[] = { |
---|