1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * dwmac-sti.c - STMicroelectronics DWMAC Specific Glue layer |
4 | * |
5 | * Copyright (C) 2003-2014 STMicroelectronics (R&D) Limited |
6 | * Author: Srinivas Kandagatla <srinivas.kandagatla@st.com> |
7 | * Contributors: Giuseppe Cavallaro <peppe.cavallaro@st.com> |
8 | */ |
9 | |
10 | #include <linux/kernel.h> |
11 | #include <linux/slab.h> |
12 | #include <linux/platform_device.h> |
13 | #include <linux/stmmac.h> |
14 | #include <linux/phy.h> |
15 | #include <linux/mfd/syscon.h> |
16 | #include <linux/module.h> |
17 | #include <linux/regmap.h> |
18 | #include <linux/clk.h> |
19 | #include <linux/of.h> |
20 | #include <linux/of_net.h> |
21 | |
22 | #include "stmmac_platform.h" |
23 | |
24 | #define DWMAC_125MHZ 125000000 |
25 | #define DWMAC_50MHZ 50000000 |
26 | #define DWMAC_25MHZ 25000000 |
27 | #define DWMAC_2_5MHZ 2500000 |
28 | |
29 | #define IS_PHY_IF_MODE_RGMII(iface) (iface == PHY_INTERFACE_MODE_RGMII || \ |
30 | iface == PHY_INTERFACE_MODE_RGMII_ID || \ |
31 | iface == PHY_INTERFACE_MODE_RGMII_RXID || \ |
32 | iface == PHY_INTERFACE_MODE_RGMII_TXID) |
33 | |
34 | #define IS_PHY_IF_MODE_GBIT(iface) (IS_PHY_IF_MODE_RGMII(iface) || \ |
35 | iface == PHY_INTERFACE_MODE_GMII) |
36 | |
37 | /* STiH4xx register definitions (STiH407/STiH410 families) |
38 | * |
39 | * Below table summarizes the clock requirement and clock sources for |
40 | * supported phy interface modes with link speeds. |
41 | * ________________________________________________ |
42 | *| PHY_MODE | 1000 Mbit Link | 100 Mbit Link | |
43 | * ------------------------------------------------ |
44 | *| MII | n/a | 25Mhz | |
45 | *| | | txclk | |
46 | * ------------------------------------------------ |
47 | *| GMII | 125Mhz | 25Mhz | |
48 | *| | clk-125/txclk | txclk | |
49 | * ------------------------------------------------ |
50 | *| RGMII | 125Mhz | 25Mhz | |
51 | *| | clk-125/txclk | clkgen | |
52 | *| | clkgen | | |
53 | * ------------------------------------------------ |
54 | *| RMII | n/a | 25Mhz | |
55 | *| | |clkgen/phyclk-in | |
56 | * ------------------------------------------------ |
57 | * |
58 | * Register Configuration |
59 | *------------------------------- |
60 | * src |BIT(8)| BIT(7)| BIT(6)| |
61 | *------------------------------- |
62 | * txclk | 0 | n/a | 1 | |
63 | *------------------------------- |
64 | * ck_125| 0 | n/a | 0 | |
65 | *------------------------------- |
66 | * phyclk| 1 | 0 | n/a | |
67 | *------------------------------- |
68 | * clkgen| 1 | 1 | n/a | |
69 | *------------------------------- |
70 | */ |
71 | |
72 | #define STIH4XX_RETIME_SRC_MASK GENMASK(8, 6) |
73 | #define STIH4XX_ETH_SEL_TX_RETIME_CLK BIT(8) |
74 | #define STIH4XX_ETH_SEL_INTERNAL_NOTEXT_PHYCLK BIT(7) |
75 | #define STIH4XX_ETH_SEL_TXCLK_NOT_CLK125 BIT(6) |
76 | |
77 | #define ENMII_MASK GENMASK(5, 5) |
78 | #define ENMII BIT(5) |
79 | #define EN_MASK GENMASK(1, 1) |
80 | #define EN BIT(1) |
81 | |
82 | /* |
83 | * 3 bits [4:2] |
84 | * 000-GMII/MII |
85 | * 001-RGMII |
86 | * 010-SGMII |
87 | * 100-RMII |
88 | */ |
89 | #define MII_PHY_SEL_MASK GENMASK(4, 2) |
90 | #define ETH_PHY_SEL_RMII BIT(4) |
91 | #define ETH_PHY_SEL_SGMII BIT(3) |
92 | #define ETH_PHY_SEL_RGMII BIT(2) |
93 | #define ETH_PHY_SEL_GMII 0x0 |
94 | #define ETH_PHY_SEL_MII 0x0 |
95 | |
96 | struct sti_dwmac { |
97 | phy_interface_t interface; /* MII interface */ |
98 | bool ext_phyclk; /* Clock from external PHY */ |
99 | u32 tx_retime_src; /* TXCLK Retiming*/ |
100 | struct clk *clk; /* PHY clock */ |
101 | u32 ctrl_reg; /* GMAC glue-logic control register */ |
102 | int clk_sel_reg; /* GMAC ext clk selection register */ |
103 | struct regmap *regmap; |
104 | bool gmac_en; |
105 | u32 speed; |
106 | void (*fix_retime_src)(void *priv, unsigned int speed, unsigned int mode); |
107 | }; |
108 | |
109 | struct sti_dwmac_of_data { |
110 | void (*fix_retime_src)(void *priv, unsigned int speed, unsigned int mode); |
111 | }; |
112 | |
113 | static u32 phy_intf_sels[] = { |
114 | [PHY_INTERFACE_MODE_MII] = ETH_PHY_SEL_MII, |
115 | [PHY_INTERFACE_MODE_GMII] = ETH_PHY_SEL_GMII, |
116 | [PHY_INTERFACE_MODE_RGMII] = ETH_PHY_SEL_RGMII, |
117 | [PHY_INTERFACE_MODE_RGMII_ID] = ETH_PHY_SEL_RGMII, |
118 | [PHY_INTERFACE_MODE_SGMII] = ETH_PHY_SEL_SGMII, |
119 | [PHY_INTERFACE_MODE_RMII] = ETH_PHY_SEL_RMII, |
120 | }; |
121 | |
122 | enum { |
123 | TX_RETIME_SRC_NA = 0, |
124 | TX_RETIME_SRC_TXCLK = 1, |
125 | TX_RETIME_SRC_CLK_125, |
126 | TX_RETIME_SRC_PHYCLK, |
127 | TX_RETIME_SRC_CLKGEN, |
128 | }; |
129 | |
130 | static u32 stih4xx_tx_retime_val[] = { |
131 | [TX_RETIME_SRC_TXCLK] = STIH4XX_ETH_SEL_TXCLK_NOT_CLK125, |
132 | [TX_RETIME_SRC_CLK_125] = 0x0, |
133 | [TX_RETIME_SRC_PHYCLK] = STIH4XX_ETH_SEL_TX_RETIME_CLK, |
134 | [TX_RETIME_SRC_CLKGEN] = STIH4XX_ETH_SEL_TX_RETIME_CLK |
135 | | STIH4XX_ETH_SEL_INTERNAL_NOTEXT_PHYCLK, |
136 | }; |
137 | |
138 | static void stih4xx_fix_retime_src(void *priv, u32 spd, unsigned int mode) |
139 | { |
140 | struct sti_dwmac *dwmac = priv; |
141 | u32 src = dwmac->tx_retime_src; |
142 | u32 reg = dwmac->ctrl_reg; |
143 | u32 freq = 0; |
144 | |
145 | if (dwmac->interface == PHY_INTERFACE_MODE_MII) { |
146 | src = TX_RETIME_SRC_TXCLK; |
147 | } else if (dwmac->interface == PHY_INTERFACE_MODE_RMII) { |
148 | if (dwmac->ext_phyclk) { |
149 | src = TX_RETIME_SRC_PHYCLK; |
150 | } else { |
151 | src = TX_RETIME_SRC_CLKGEN; |
152 | freq = DWMAC_50MHZ; |
153 | } |
154 | } else if (IS_PHY_IF_MODE_RGMII(dwmac->interface)) { |
155 | /* On GiGa clk source can be either ext or from clkgen */ |
156 | if (spd == SPEED_1000) { |
157 | freq = DWMAC_125MHZ; |
158 | } else { |
159 | /* Switch to clkgen for these speeds */ |
160 | src = TX_RETIME_SRC_CLKGEN; |
161 | if (spd == SPEED_100) |
162 | freq = DWMAC_25MHZ; |
163 | else if (spd == SPEED_10) |
164 | freq = DWMAC_2_5MHZ; |
165 | } |
166 | } |
167 | |
168 | if (src == TX_RETIME_SRC_CLKGEN && freq) |
169 | clk_set_rate(clk: dwmac->clk, rate: freq); |
170 | |
171 | regmap_update_bits(map: dwmac->regmap, reg, STIH4XX_RETIME_SRC_MASK, |
172 | val: stih4xx_tx_retime_val[src]); |
173 | } |
174 | |
175 | static int sti_dwmac_set_mode(struct sti_dwmac *dwmac) |
176 | { |
177 | struct regmap *regmap = dwmac->regmap; |
178 | int iface = dwmac->interface; |
179 | u32 reg = dwmac->ctrl_reg; |
180 | u32 val; |
181 | |
182 | if (dwmac->gmac_en) |
183 | regmap_update_bits(map: regmap, reg, EN_MASK, EN); |
184 | |
185 | regmap_update_bits(map: regmap, reg, MII_PHY_SEL_MASK, val: phy_intf_sels[iface]); |
186 | |
187 | val = (iface == PHY_INTERFACE_MODE_REVMII) ? 0 : ENMII; |
188 | regmap_update_bits(map: regmap, reg, ENMII_MASK, val); |
189 | |
190 | dwmac->fix_retime_src(dwmac, dwmac->speed, 0); |
191 | |
192 | return 0; |
193 | } |
194 | |
195 | static int sti_dwmac_parse_data(struct sti_dwmac *dwmac, |
196 | struct platform_device *pdev) |
197 | { |
198 | struct resource *res; |
199 | struct device *dev = &pdev->dev; |
200 | struct device_node *np = dev->of_node; |
201 | struct regmap *regmap; |
202 | int err; |
203 | |
204 | /* clk selection from extra syscfg register */ |
205 | dwmac->clk_sel_reg = -ENXIO; |
206 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sti-clkconf" ); |
207 | if (res) |
208 | dwmac->clk_sel_reg = res->start; |
209 | |
210 | regmap = syscon_regmap_lookup_by_phandle(np, property: "st,syscon" ); |
211 | if (IS_ERR(ptr: regmap)) |
212 | return PTR_ERR(ptr: regmap); |
213 | |
214 | err = of_property_read_u32_index(np, propname: "st,syscon" , index: 1, out_value: &dwmac->ctrl_reg); |
215 | if (err) { |
216 | dev_err(dev, "Can't get sysconfig ctrl offset (%d)\n" , err); |
217 | return err; |
218 | } |
219 | |
220 | err = of_get_phy_mode(np, interface: &dwmac->interface); |
221 | if (err && err != -ENODEV) { |
222 | dev_err(dev, "Can't get phy-mode\n" ); |
223 | return err; |
224 | } |
225 | |
226 | dwmac->regmap = regmap; |
227 | dwmac->gmac_en = of_property_read_bool(np, propname: "st,gmac_en" ); |
228 | dwmac->ext_phyclk = of_property_read_bool(np, propname: "st,ext-phyclk" ); |
229 | dwmac->tx_retime_src = TX_RETIME_SRC_NA; |
230 | dwmac->speed = SPEED_100; |
231 | |
232 | if (IS_PHY_IF_MODE_GBIT(dwmac->interface)) { |
233 | const char *rs; |
234 | |
235 | dwmac->tx_retime_src = TX_RETIME_SRC_CLKGEN; |
236 | |
237 | err = of_property_read_string(np, propname: "st,tx-retime-src" , out_string: &rs); |
238 | if (err < 0) { |
239 | dev_warn(dev, "Use internal clock source\n" ); |
240 | } else { |
241 | if (!strcasecmp(s1: rs, s2: "clk_125" )) |
242 | dwmac->tx_retime_src = TX_RETIME_SRC_CLK_125; |
243 | else if (!strcasecmp(s1: rs, s2: "txclk" )) |
244 | dwmac->tx_retime_src = TX_RETIME_SRC_TXCLK; |
245 | } |
246 | dwmac->speed = SPEED_1000; |
247 | } |
248 | |
249 | dwmac->clk = devm_clk_get(dev, id: "sti-ethclk" ); |
250 | if (IS_ERR(ptr: dwmac->clk)) { |
251 | dev_warn(dev, "No phy clock provided...\n" ); |
252 | dwmac->clk = NULL; |
253 | } |
254 | |
255 | return 0; |
256 | } |
257 | |
258 | static int sti_dwmac_probe(struct platform_device *pdev) |
259 | { |
260 | struct plat_stmmacenet_data *plat_dat; |
261 | const struct sti_dwmac_of_data *data; |
262 | struct stmmac_resources stmmac_res; |
263 | struct sti_dwmac *dwmac; |
264 | int ret; |
265 | |
266 | data = of_device_get_match_data(dev: &pdev->dev); |
267 | if (!data) { |
268 | dev_err(&pdev->dev, "No OF match data provided\n" ); |
269 | return -EINVAL; |
270 | } |
271 | |
272 | ret = stmmac_get_platform_resources(pdev, stmmac_res: &stmmac_res); |
273 | if (ret) |
274 | return ret; |
275 | |
276 | plat_dat = devm_stmmac_probe_config_dt(pdev, mac: stmmac_res.mac); |
277 | if (IS_ERR(ptr: plat_dat)) |
278 | return PTR_ERR(ptr: plat_dat); |
279 | |
280 | dwmac = devm_kzalloc(dev: &pdev->dev, size: sizeof(*dwmac), GFP_KERNEL); |
281 | if (!dwmac) |
282 | return -ENOMEM; |
283 | |
284 | ret = sti_dwmac_parse_data(dwmac, pdev); |
285 | if (ret) { |
286 | dev_err(&pdev->dev, "Unable to parse OF data\n" ); |
287 | return ret; |
288 | } |
289 | |
290 | dwmac->fix_retime_src = data->fix_retime_src; |
291 | |
292 | plat_dat->bsp_priv = dwmac; |
293 | plat_dat->fix_mac_speed = data->fix_retime_src; |
294 | |
295 | ret = clk_prepare_enable(clk: dwmac->clk); |
296 | if (ret) |
297 | return ret; |
298 | |
299 | ret = sti_dwmac_set_mode(dwmac); |
300 | if (ret) |
301 | goto disable_clk; |
302 | |
303 | ret = stmmac_dvr_probe(device: &pdev->dev, plat_dat, res: &stmmac_res); |
304 | if (ret) |
305 | goto disable_clk; |
306 | |
307 | return 0; |
308 | |
309 | disable_clk: |
310 | clk_disable_unprepare(clk: dwmac->clk); |
311 | |
312 | return ret; |
313 | } |
314 | |
315 | static void sti_dwmac_remove(struct platform_device *pdev) |
316 | { |
317 | struct sti_dwmac *dwmac = get_stmmac_bsp_priv(dev: &pdev->dev); |
318 | |
319 | stmmac_dvr_remove(dev: &pdev->dev); |
320 | |
321 | clk_disable_unprepare(clk: dwmac->clk); |
322 | } |
323 | |
324 | #ifdef CONFIG_PM_SLEEP |
325 | static int sti_dwmac_suspend(struct device *dev) |
326 | { |
327 | struct sti_dwmac *dwmac = get_stmmac_bsp_priv(dev); |
328 | int ret = stmmac_suspend(dev); |
329 | |
330 | clk_disable_unprepare(clk: dwmac->clk); |
331 | |
332 | return ret; |
333 | } |
334 | |
335 | static int sti_dwmac_resume(struct device *dev) |
336 | { |
337 | struct sti_dwmac *dwmac = get_stmmac_bsp_priv(dev); |
338 | |
339 | clk_prepare_enable(clk: dwmac->clk); |
340 | sti_dwmac_set_mode(dwmac); |
341 | |
342 | return stmmac_resume(dev); |
343 | } |
344 | #endif /* CONFIG_PM_SLEEP */ |
345 | |
346 | static SIMPLE_DEV_PM_OPS(sti_dwmac_pm_ops, sti_dwmac_suspend, |
347 | sti_dwmac_resume); |
348 | |
349 | static const struct sti_dwmac_of_data stih4xx_dwmac_data = { |
350 | .fix_retime_src = stih4xx_fix_retime_src, |
351 | }; |
352 | |
353 | static const struct of_device_id sti_dwmac_match[] = { |
354 | { .compatible = "st,stih407-dwmac" , .data = &stih4xx_dwmac_data}, |
355 | { } |
356 | }; |
357 | MODULE_DEVICE_TABLE(of, sti_dwmac_match); |
358 | |
359 | static struct platform_driver sti_dwmac_driver = { |
360 | .probe = sti_dwmac_probe, |
361 | .remove_new = sti_dwmac_remove, |
362 | .driver = { |
363 | .name = "sti-dwmac" , |
364 | .pm = &sti_dwmac_pm_ops, |
365 | .of_match_table = sti_dwmac_match, |
366 | }, |
367 | }; |
368 | module_platform_driver(sti_dwmac_driver); |
369 | |
370 | MODULE_AUTHOR("Srinivas Kandagatla <srinivas.kandagatla@st.com>" ); |
371 | MODULE_DESCRIPTION("STMicroelectronics DWMAC Specific Glue layer" ); |
372 | MODULE_LICENSE("GPL" ); |
373 | |