1caab277bSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2801d233bSDinh Nguyen /* Copyright Altera Corporation (C) 2014. All rights reserved.
3801d233bSDinh Nguyen *
4801d233bSDinh Nguyen * Adopted from dwmac-sti.c
5801d233bSDinh Nguyen */
6801d233bSDinh Nguyen
754a5afb4SThor Thayer #include <linux/mfd/altera-sysmgr.h>
8801d233bSDinh Nguyen #include <linux/of.h>
9b4834c86SLey Foon Tan #include <linux/of_address.h>
10801d233bSDinh Nguyen #include <linux/of_net.h>
11801d233bSDinh Nguyen #include <linux/phy.h>
12801d233bSDinh Nguyen #include <linux/regmap.h>
135d1f3fe7SMaxime Chevallier #include <linux/mdio/mdio-regmap.h>
14a8dd7404SMaxime Chevallier #include <linux/pcs-lynx.h>
152d871aa0SVince Bridgers #include <linux/reset.h>
16801d233bSDinh Nguyen #include <linux/stmmac.h>
17f10f9fb2SAndy Shevchenko
182d871aa0SVince Bridgers #include "stmmac.h"
19f10f9fb2SAndy Shevchenko #include "stmmac_platform.h"
20801d233bSDinh Nguyen
21801d233bSDinh Nguyen #define SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_GMII_MII 0x0
22801d233bSDinh Nguyen #define SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_RGMII 0x1
23801d233bSDinh Nguyen #define SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_RMII 0x2
24801d233bSDinh Nguyen #define SYSMGR_EMACGRP_CTRL_PHYSEL_WIDTH 2
25801d233bSDinh Nguyen #define SYSMGR_EMACGRP_CTRL_PHYSEL_MASK 0x00000003
2643569814SPhil Reid #define SYSMGR_EMACGRP_CTRL_PTP_REF_CLK_MASK 0x00000010
2740ae2550SDinh Nguyen #define SYSMGR_GEN10_EMACGRP_CTRL_PTP_REF_CLK_MASK 0x00000100
28801d233bSDinh Nguyen
29734e00faSPhil Reid #define SYSMGR_FPGAGRP_MODULE_REG 0x00000028
30734e00faSPhil Reid #define SYSMGR_FPGAGRP_MODULE_EMAC 0x00000004
3140ae2550SDinh Nguyen #define SYSMGR_FPGAINTF_EMAC_REG 0x00000070
3240ae2550SDinh Nguyen #define SYSMGR_FPGAINTF_EMAC_BIT 0x1
33734e00faSPhil Reid
34b4834c86SLey Foon Tan #define EMAC_SPLITTER_CTRL_REG 0x0
35b4834c86SLey Foon Tan #define EMAC_SPLITTER_CTRL_SPEED_MASK 0x3
36b4834c86SLey Foon Tan #define EMAC_SPLITTER_CTRL_SPEED_10 0x2
37b4834c86SLey Foon Tan #define EMAC_SPLITTER_CTRL_SPEED_100 0x3
38b4834c86SLey Foon Tan #define EMAC_SPLITTER_CTRL_SPEED_1000 0x0
39b4834c86SLey Foon Tan
405d1f3fe7SMaxime Chevallier #define SGMII_ADAPTER_CTRL_REG 0x00
415d1f3fe7SMaxime Chevallier #define SGMII_ADAPTER_ENABLE 0x0000
425d1f3fe7SMaxime Chevallier #define SGMII_ADAPTER_DISABLE 0x0001
435d1f3fe7SMaxime Chevallier
4440ae2550SDinh Nguyen struct socfpga_dwmac;
4540ae2550SDinh Nguyen struct socfpga_dwmac_ops {
4640ae2550SDinh Nguyen int (*set_phy_mode)(struct socfpga_dwmac *dwmac_priv);
4740ae2550SDinh Nguyen };
4840ae2550SDinh Nguyen
49801d233bSDinh Nguyen struct socfpga_dwmac {
50801d233bSDinh Nguyen u32 reg_offset;
51801d233bSDinh Nguyen u32 reg_shift;
52801d233bSDinh Nguyen struct device *dev;
53801d233bSDinh Nguyen struct regmap *sys_mgr_base_addr;
5470cb136fSJoachim Eastwood struct reset_control *stmmac_rst;
55bc8a2d9bSDinh Nguyen struct reset_control *stmmac_ocp_rst;
56b4834c86SLey Foon Tan void __iomem *splitter_base;
575d1f3fe7SMaxime Chevallier void __iomem *tse_pcs_base;
585d1f3fe7SMaxime Chevallier void __iomem *sgmii_adapter_base;
5943569814SPhil Reid bool f2h_ptp_ref_clk;
6040ae2550SDinh Nguyen const struct socfpga_dwmac_ops *ops;
615d1f3fe7SMaxime Chevallier struct mdio_device *pcs_mdiodev;
62801d233bSDinh Nguyen };
63801d233bSDinh Nguyen
socfpga_dwmac_fix_mac_speed(void * priv,unsigned int speed,unsigned int mode)641fc04a0bSShenwei Wang static void socfpga_dwmac_fix_mac_speed(void *priv, unsigned int speed, unsigned int mode)
65b4834c86SLey Foon Tan {
66b4834c86SLey Foon Tan struct socfpga_dwmac *dwmac = (struct socfpga_dwmac *)priv;
67b4834c86SLey Foon Tan void __iomem *splitter_base = dwmac->splitter_base;
685d1f3fe7SMaxime Chevallier void __iomem *sgmii_adapter_base = dwmac->sgmii_adapter_base;
69fb3bbdb8STien Hock Loh struct device *dev = dwmac->dev;
70fb3bbdb8STien Hock Loh struct net_device *ndev = dev_get_drvdata(dev);
71fb3bbdb8STien Hock Loh struct phy_device *phy_dev = ndev->phydev;
72b4834c86SLey Foon Tan u32 val;
73b4834c86SLey Foon Tan
745fd1fe48SDinh Nguyen if (sgmii_adapter_base)
75fb3bbdb8STien Hock Loh writew(SGMII_ADAPTER_DISABLE,
76fb3bbdb8STien Hock Loh sgmii_adapter_base + SGMII_ADAPTER_CTRL_REG);
77b4834c86SLey Foon Tan
78fb3bbdb8STien Hock Loh if (splitter_base) {
79b4834c86SLey Foon Tan val = readl(splitter_base + EMAC_SPLITTER_CTRL_REG);
80b4834c86SLey Foon Tan val &= ~EMAC_SPLITTER_CTRL_SPEED_MASK;
81b4834c86SLey Foon Tan
82b4834c86SLey Foon Tan switch (speed) {
83b4834c86SLey Foon Tan case 1000:
84b4834c86SLey Foon Tan val |= EMAC_SPLITTER_CTRL_SPEED_1000;
85b4834c86SLey Foon Tan break;
86b4834c86SLey Foon Tan case 100:
87b4834c86SLey Foon Tan val |= EMAC_SPLITTER_CTRL_SPEED_100;
88b4834c86SLey Foon Tan break;
89b4834c86SLey Foon Tan case 10:
90b4834c86SLey Foon Tan val |= EMAC_SPLITTER_CTRL_SPEED_10;
91b4834c86SLey Foon Tan break;
92b4834c86SLey Foon Tan default:
93b4834c86SLey Foon Tan return;
94b4834c86SLey Foon Tan }
95b4834c86SLey Foon Tan writel(val, splitter_base + EMAC_SPLITTER_CTRL_REG);
96b4834c86SLey Foon Tan }
97b4834c86SLey Foon Tan
985d1f3fe7SMaxime Chevallier if (phy_dev && sgmii_adapter_base)
99a6aaa003SDinh Nguyen writew(SGMII_ADAPTER_ENABLE,
100a6aaa003SDinh Nguyen sgmii_adapter_base + SGMII_ADAPTER_CTRL_REG);
1015fd1fe48SDinh Nguyen }
102fb3bbdb8STien Hock Loh
socfpga_dwmac_parse_data(struct socfpga_dwmac * dwmac,struct device * dev)103801d233bSDinh Nguyen static int socfpga_dwmac_parse_data(struct socfpga_dwmac *dwmac, struct device *dev)
104801d233bSDinh Nguyen {
105801d233bSDinh Nguyen struct device_node *np = dev->of_node;
106801d233bSDinh Nguyen struct regmap *sys_mgr_base_addr;
107801d233bSDinh Nguyen u32 reg_offset, reg_shift;
108fb3bbdb8STien Hock Loh int ret, index;
109fb3bbdb8STien Hock Loh struct device_node *np_splitter = NULL;
110fb3bbdb8STien Hock Loh struct device_node *np_sgmii_adapter = NULL;
111b4834c86SLey Foon Tan struct resource res_splitter;
112fb3bbdb8STien Hock Loh struct resource res_tse_pcs;
113fb3bbdb8STien Hock Loh struct resource res_sgmii_adapter;
114801d233bSDinh Nguyen
11554a5afb4SThor Thayer sys_mgr_base_addr =
11654a5afb4SThor Thayer altr_sysmgr_regmap_lookup_by_phandle(np, "altr,sysmgr-syscon");
117801d233bSDinh Nguyen if (IS_ERR(sys_mgr_base_addr)) {
118801d233bSDinh Nguyen dev_info(dev, "No sysmgr-syscon node found\n");
119801d233bSDinh Nguyen return PTR_ERR(sys_mgr_base_addr);
120801d233bSDinh Nguyen }
121801d233bSDinh Nguyen
122801d233bSDinh Nguyen ret = of_property_read_u32_index(np, "altr,sysmgr-syscon", 1, ®_offset);
123801d233bSDinh Nguyen if (ret) {
124801d233bSDinh Nguyen dev_info(dev, "Could not read reg_offset from sysmgr-syscon!\n");
125801d233bSDinh Nguyen return -EINVAL;
126801d233bSDinh Nguyen }
127801d233bSDinh Nguyen
128801d233bSDinh Nguyen ret = of_property_read_u32_index(np, "altr,sysmgr-syscon", 2, ®_shift);
129801d233bSDinh Nguyen if (ret) {
130801d233bSDinh Nguyen dev_info(dev, "Could not read reg_shift from sysmgr-syscon!\n");
131801d233bSDinh Nguyen return -EINVAL;
132801d233bSDinh Nguyen }
133801d233bSDinh Nguyen
13443569814SPhil Reid dwmac->f2h_ptp_ref_clk = of_property_read_bool(np, "altr,f2h_ptp_ref_clk");
13543569814SPhil Reid
136b4834c86SLey Foon Tan np_splitter = of_parse_phandle(np, "altr,emac-splitter", 0);
137b4834c86SLey Foon Tan if (np_splitter) {
138f7113b3aSPeter Chen ret = of_address_to_resource(np_splitter, 0, &res_splitter);
139f7113b3aSPeter Chen of_node_put(np_splitter);
140f7113b3aSPeter Chen if (ret) {
141b4834c86SLey Foon Tan dev_info(dev, "Missing emac splitter address\n");
142b4834c86SLey Foon Tan return -EINVAL;
143b4834c86SLey Foon Tan }
144b4834c86SLey Foon Tan
145dace1b54SLey Foon Tan dwmac->splitter_base = devm_ioremap_resource(dev, &res_splitter);
146f19f916dSWei Yongjun if (IS_ERR(dwmac->splitter_base)) {
147b4834c86SLey Foon Tan dev_info(dev, "Failed to mapping emac splitter\n");
148f19f916dSWei Yongjun return PTR_ERR(dwmac->splitter_base);
149b4834c86SLey Foon Tan }
150b4834c86SLey Foon Tan }
151b4834c86SLey Foon Tan
152fb3bbdb8STien Hock Loh np_sgmii_adapter = of_parse_phandle(np,
153fb3bbdb8STien Hock Loh "altr,gmii-to-sgmii-converter", 0);
154fb3bbdb8STien Hock Loh if (np_sgmii_adapter) {
155fb3bbdb8STien Hock Loh index = of_property_match_string(np_sgmii_adapter, "reg-names",
156fb3bbdb8STien Hock Loh "hps_emac_interface_splitter_avalon_slave");
157fb3bbdb8STien Hock Loh
158fb3bbdb8STien Hock Loh if (index >= 0) {
159fb3bbdb8STien Hock Loh if (of_address_to_resource(np_sgmii_adapter, index,
160fb3bbdb8STien Hock Loh &res_splitter)) {
161fb3bbdb8STien Hock Loh dev_err(dev,
162fb3bbdb8STien Hock Loh "%s: ERROR: missing emac splitter address\n",
163fb3bbdb8STien Hock Loh __func__);
164f7113b3aSPeter Chen ret = -EINVAL;
165f7113b3aSPeter Chen goto err_node_put;
166fb3bbdb8STien Hock Loh }
167fb3bbdb8STien Hock Loh
168fb3bbdb8STien Hock Loh dwmac->splitter_base =
169fb3bbdb8STien Hock Loh devm_ioremap_resource(dev, &res_splitter);
170fb3bbdb8STien Hock Loh
171f7113b3aSPeter Chen if (IS_ERR(dwmac->splitter_base)) {
172f7113b3aSPeter Chen ret = PTR_ERR(dwmac->splitter_base);
173f7113b3aSPeter Chen goto err_node_put;
174f7113b3aSPeter Chen }
175fb3bbdb8STien Hock Loh }
176fb3bbdb8STien Hock Loh
177fb3bbdb8STien Hock Loh index = of_property_match_string(np_sgmii_adapter, "reg-names",
178fb3bbdb8STien Hock Loh "gmii_to_sgmii_adapter_avalon_slave");
179fb3bbdb8STien Hock Loh
180fb3bbdb8STien Hock Loh if (index >= 0) {
181fb3bbdb8STien Hock Loh if (of_address_to_resource(np_sgmii_adapter, index,
182fb3bbdb8STien Hock Loh &res_sgmii_adapter)) {
183fb3bbdb8STien Hock Loh dev_err(dev,
184fb3bbdb8STien Hock Loh "%s: ERROR: failed mapping adapter\n",
185fb3bbdb8STien Hock Loh __func__);
186f7113b3aSPeter Chen ret = -EINVAL;
187f7113b3aSPeter Chen goto err_node_put;
188fb3bbdb8STien Hock Loh }
189fb3bbdb8STien Hock Loh
1905d1f3fe7SMaxime Chevallier dwmac->sgmii_adapter_base =
191fb3bbdb8STien Hock Loh devm_ioremap_resource(dev, &res_sgmii_adapter);
192fb3bbdb8STien Hock Loh
1935d1f3fe7SMaxime Chevallier if (IS_ERR(dwmac->sgmii_adapter_base)) {
1945d1f3fe7SMaxime Chevallier ret = PTR_ERR(dwmac->sgmii_adapter_base);
195f7113b3aSPeter Chen goto err_node_put;
196f7113b3aSPeter Chen }
197fb3bbdb8STien Hock Loh }
198fb3bbdb8STien Hock Loh
199fb3bbdb8STien Hock Loh index = of_property_match_string(np_sgmii_adapter, "reg-names",
200fb3bbdb8STien Hock Loh "eth_tse_control_port");
201fb3bbdb8STien Hock Loh
202fb3bbdb8STien Hock Loh if (index >= 0) {
203fb3bbdb8STien Hock Loh if (of_address_to_resource(np_sgmii_adapter, index,
204fb3bbdb8STien Hock Loh &res_tse_pcs)) {
205fb3bbdb8STien Hock Loh dev_err(dev,
206fb3bbdb8STien Hock Loh "%s: ERROR: failed mapping tse control port\n",
207fb3bbdb8STien Hock Loh __func__);
208f7113b3aSPeter Chen ret = -EINVAL;
209f7113b3aSPeter Chen goto err_node_put;
210fb3bbdb8STien Hock Loh }
211fb3bbdb8STien Hock Loh
2125d1f3fe7SMaxime Chevallier dwmac->tse_pcs_base =
213fb3bbdb8STien Hock Loh devm_ioremap_resource(dev, &res_tse_pcs);
214fb3bbdb8STien Hock Loh
2155d1f3fe7SMaxime Chevallier if (IS_ERR(dwmac->tse_pcs_base)) {
2165d1f3fe7SMaxime Chevallier ret = PTR_ERR(dwmac->tse_pcs_base);
217f7113b3aSPeter Chen goto err_node_put;
218f7113b3aSPeter Chen }
219fb3bbdb8STien Hock Loh }
220fb3bbdb8STien Hock Loh }
221801d233bSDinh Nguyen dwmac->reg_offset = reg_offset;
222801d233bSDinh Nguyen dwmac->reg_shift = reg_shift;
223801d233bSDinh Nguyen dwmac->sys_mgr_base_addr = sys_mgr_base_addr;
224801d233bSDinh Nguyen dwmac->dev = dev;
225f7113b3aSPeter Chen of_node_put(np_sgmii_adapter);
226801d233bSDinh Nguyen
227801d233bSDinh Nguyen return 0;
228f7113b3aSPeter Chen
229f7113b3aSPeter Chen err_node_put:
230f7113b3aSPeter Chen of_node_put(np_sgmii_adapter);
231f7113b3aSPeter Chen return ret;
232801d233bSDinh Nguyen }
233801d233bSDinh Nguyen
socfpga_get_plat_phymode(struct socfpga_dwmac * dwmac)2345f109d45SAlexandru Ardelean static int socfpga_get_plat_phymode(struct socfpga_dwmac *dwmac)
2355f109d45SAlexandru Ardelean {
2365f109d45SAlexandru Ardelean struct net_device *ndev = dev_get_drvdata(dwmac->dev);
2375f109d45SAlexandru Ardelean struct stmmac_priv *priv = netdev_priv(ndev);
2385f109d45SAlexandru Ardelean
239a014c355SRussell King (Oracle) return priv->plat->mac_interface;
2405f109d45SAlexandru Ardelean }
2415f109d45SAlexandru Ardelean
socfpga_sgmii_config(struct socfpga_dwmac * dwmac,bool enable)2425d1f3fe7SMaxime Chevallier static void socfpga_sgmii_config(struct socfpga_dwmac *dwmac, bool enable)
2435d1f3fe7SMaxime Chevallier {
2445d1f3fe7SMaxime Chevallier u16 val = enable ? SGMII_ADAPTER_ENABLE : SGMII_ADAPTER_DISABLE;
2455d1f3fe7SMaxime Chevallier
2465d1f3fe7SMaxime Chevallier writew(val, dwmac->sgmii_adapter_base + SGMII_ADAPTER_CTRL_REG);
2475d1f3fe7SMaxime Chevallier }
2485d1f3fe7SMaxime Chevallier
socfpga_set_phy_mode_common(int phymode,u32 * val)24940ae2550SDinh Nguyen static int socfpga_set_phy_mode_common(int phymode, u32 *val)
25040ae2550SDinh Nguyen {
25140ae2550SDinh Nguyen switch (phymode) {
25240ae2550SDinh Nguyen case PHY_INTERFACE_MODE_RGMII:
25340ae2550SDinh Nguyen case PHY_INTERFACE_MODE_RGMII_ID:
254a7a0d626SAtsushi Nemoto case PHY_INTERFACE_MODE_RGMII_RXID:
255a7a0d626SAtsushi Nemoto case PHY_INTERFACE_MODE_RGMII_TXID:
25640ae2550SDinh Nguyen *val = SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_RGMII;
25740ae2550SDinh Nguyen break;
25840ae2550SDinh Nguyen case PHY_INTERFACE_MODE_MII:
25940ae2550SDinh Nguyen case PHY_INTERFACE_MODE_GMII:
26040ae2550SDinh Nguyen case PHY_INTERFACE_MODE_SGMII:
26140ae2550SDinh Nguyen *val = SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_GMII_MII;
26240ae2550SDinh Nguyen break;
26340ae2550SDinh Nguyen case PHY_INTERFACE_MODE_RMII:
26440ae2550SDinh Nguyen *val = SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_RMII;
26540ae2550SDinh Nguyen break;
26640ae2550SDinh Nguyen default:
26740ae2550SDinh Nguyen return -EINVAL;
26840ae2550SDinh Nguyen }
26940ae2550SDinh Nguyen return 0;
27040ae2550SDinh Nguyen }
27140ae2550SDinh Nguyen
socfpga_gen5_set_phy_mode(struct socfpga_dwmac * dwmac)27240ae2550SDinh Nguyen static int socfpga_gen5_set_phy_mode(struct socfpga_dwmac *dwmac)
273801d233bSDinh Nguyen {
274801d233bSDinh Nguyen struct regmap *sys_mgr_base_addr = dwmac->sys_mgr_base_addr;
2755f109d45SAlexandru Ardelean int phymode = socfpga_get_plat_phymode(dwmac);
276801d233bSDinh Nguyen u32 reg_offset = dwmac->reg_offset;
277801d233bSDinh Nguyen u32 reg_shift = dwmac->reg_shift;
278734e00faSPhil Reid u32 ctrl, val, module;
279801d233bSDinh Nguyen
28040ae2550SDinh Nguyen if (socfpga_set_phy_mode_common(phymode, &val)) {
281801d233bSDinh Nguyen dev_err(dwmac->dev, "bad phy mode %d\n", phymode);
282801d233bSDinh Nguyen return -EINVAL;
283801d233bSDinh Nguyen }
284801d233bSDinh Nguyen
285b4834c86SLey Foon Tan /* Overwrite val to GMII if splitter core is enabled. The phymode here
286b4834c86SLey Foon Tan * is the actual phy mode on phy hardware, but phy interface from
287b4834c86SLey Foon Tan * EMAC core is GMII.
288b4834c86SLey Foon Tan */
289b4834c86SLey Foon Tan if (dwmac->splitter_base)
290b4834c86SLey Foon Tan val = SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_GMII_MII;
291b4834c86SLey Foon Tan
29270cb136fSJoachim Eastwood /* Assert reset to the enet controller before changing the phy mode */
293bc8a2d9bSDinh Nguyen reset_control_assert(dwmac->stmmac_ocp_rst);
29470cb136fSJoachim Eastwood reset_control_assert(dwmac->stmmac_rst);
29570cb136fSJoachim Eastwood
296801d233bSDinh Nguyen regmap_read(sys_mgr_base_addr, reg_offset, &ctrl);
297801d233bSDinh Nguyen ctrl &= ~(SYSMGR_EMACGRP_CTRL_PHYSEL_MASK << reg_shift);
298801d233bSDinh Nguyen ctrl |= val << reg_shift;
299801d233bSDinh Nguyen
300013dae5dSStephan Gatzka if (dwmac->f2h_ptp_ref_clk ||
301013dae5dSStephan Gatzka phymode == PHY_INTERFACE_MODE_MII ||
302013dae5dSStephan Gatzka phymode == PHY_INTERFACE_MODE_GMII ||
303013dae5dSStephan Gatzka phymode == PHY_INTERFACE_MODE_SGMII) {
304734e00faSPhil Reid regmap_read(sys_mgr_base_addr, SYSMGR_FPGAGRP_MODULE_REG,
305734e00faSPhil Reid &module);
306734e00faSPhil Reid module |= (SYSMGR_FPGAGRP_MODULE_EMAC << (reg_shift / 2));
307734e00faSPhil Reid regmap_write(sys_mgr_base_addr, SYSMGR_FPGAGRP_MODULE_REG,
308734e00faSPhil Reid module);
309734e00faSPhil Reid }
31043569814SPhil Reid
31115ce3060SJulien Beraud if (dwmac->f2h_ptp_ref_clk)
31215ce3060SJulien Beraud ctrl |= SYSMGR_EMACGRP_CTRL_PTP_REF_CLK_MASK << (reg_shift / 2);
31315ce3060SJulien Beraud else
31415ce3060SJulien Beraud ctrl &= ~(SYSMGR_EMACGRP_CTRL_PTP_REF_CLK_MASK <<
31515ce3060SJulien Beraud (reg_shift / 2));
31615ce3060SJulien Beraud
317801d233bSDinh Nguyen regmap_write(sys_mgr_base_addr, reg_offset, ctrl);
318734e00faSPhil Reid
31970cb136fSJoachim Eastwood /* Deassert reset for the phy configuration to be sampled by
32070cb136fSJoachim Eastwood * the enet controller, and operation to start in requested mode
32170cb136fSJoachim Eastwood */
322bc8a2d9bSDinh Nguyen reset_control_deassert(dwmac->stmmac_ocp_rst);
32370cb136fSJoachim Eastwood reset_control_deassert(dwmac->stmmac_rst);
3245d1f3fe7SMaxime Chevallier if (phymode == PHY_INTERFACE_MODE_SGMII)
3255d1f3fe7SMaxime Chevallier socfpga_sgmii_config(dwmac, true);
32670cb136fSJoachim Eastwood
327801d233bSDinh Nguyen return 0;
328801d233bSDinh Nguyen }
329801d233bSDinh Nguyen
socfpga_gen10_set_phy_mode(struct socfpga_dwmac * dwmac)33040ae2550SDinh Nguyen static int socfpga_gen10_set_phy_mode(struct socfpga_dwmac *dwmac)
33140ae2550SDinh Nguyen {
33240ae2550SDinh Nguyen struct regmap *sys_mgr_base_addr = dwmac->sys_mgr_base_addr;
3335f109d45SAlexandru Ardelean int phymode = socfpga_get_plat_phymode(dwmac);
33440ae2550SDinh Nguyen u32 reg_offset = dwmac->reg_offset;
33540ae2550SDinh Nguyen u32 reg_shift = dwmac->reg_shift;
33640ae2550SDinh Nguyen u32 ctrl, val, module;
33740ae2550SDinh Nguyen
33840ae2550SDinh Nguyen if (socfpga_set_phy_mode_common(phymode, &val))
33940ae2550SDinh Nguyen return -EINVAL;
34040ae2550SDinh Nguyen
34140ae2550SDinh Nguyen /* Overwrite val to GMII if splitter core is enabled. The phymode here
34240ae2550SDinh Nguyen * is the actual phy mode on phy hardware, but phy interface from
34340ae2550SDinh Nguyen * EMAC core is GMII.
34440ae2550SDinh Nguyen */
34540ae2550SDinh Nguyen if (dwmac->splitter_base)
34640ae2550SDinh Nguyen val = SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_GMII_MII;
34740ae2550SDinh Nguyen
34840ae2550SDinh Nguyen /* Assert reset to the enet controller before changing the phy mode */
34940ae2550SDinh Nguyen reset_control_assert(dwmac->stmmac_ocp_rst);
35040ae2550SDinh Nguyen reset_control_assert(dwmac->stmmac_rst);
35140ae2550SDinh Nguyen
35240ae2550SDinh Nguyen regmap_read(sys_mgr_base_addr, reg_offset, &ctrl);
35340ae2550SDinh Nguyen ctrl &= ~(SYSMGR_EMACGRP_CTRL_PHYSEL_MASK);
35440ae2550SDinh Nguyen ctrl |= val;
35540ae2550SDinh Nguyen
35640ae2550SDinh Nguyen if (dwmac->f2h_ptp_ref_clk ||
35740ae2550SDinh Nguyen phymode == PHY_INTERFACE_MODE_MII ||
35840ae2550SDinh Nguyen phymode == PHY_INTERFACE_MODE_GMII ||
35940ae2550SDinh Nguyen phymode == PHY_INTERFACE_MODE_SGMII) {
36040ae2550SDinh Nguyen ctrl |= SYSMGR_GEN10_EMACGRP_CTRL_PTP_REF_CLK_MASK;
36140ae2550SDinh Nguyen regmap_read(sys_mgr_base_addr, SYSMGR_FPGAINTF_EMAC_REG,
36240ae2550SDinh Nguyen &module);
36340ae2550SDinh Nguyen module |= (SYSMGR_FPGAINTF_EMAC_BIT << reg_shift);
36440ae2550SDinh Nguyen regmap_write(sys_mgr_base_addr, SYSMGR_FPGAINTF_EMAC_REG,
36540ae2550SDinh Nguyen module);
36640ae2550SDinh Nguyen } else {
36740ae2550SDinh Nguyen ctrl &= ~SYSMGR_GEN10_EMACGRP_CTRL_PTP_REF_CLK_MASK;
36840ae2550SDinh Nguyen }
36940ae2550SDinh Nguyen
37040ae2550SDinh Nguyen regmap_write(sys_mgr_base_addr, reg_offset, ctrl);
37140ae2550SDinh Nguyen
37240ae2550SDinh Nguyen /* Deassert reset for the phy configuration to be sampled by
37340ae2550SDinh Nguyen * the enet controller, and operation to start in requested mode
37440ae2550SDinh Nguyen */
37540ae2550SDinh Nguyen reset_control_deassert(dwmac->stmmac_ocp_rst);
37640ae2550SDinh Nguyen reset_control_deassert(dwmac->stmmac_rst);
3775d1f3fe7SMaxime Chevallier if (phymode == PHY_INTERFACE_MODE_SGMII)
3785d1f3fe7SMaxime Chevallier socfpga_sgmii_config(dwmac, true);
37940ae2550SDinh Nguyen return 0;
38040ae2550SDinh Nguyen }
38140ae2550SDinh Nguyen
socfpga_dwmac_probe(struct platform_device * pdev)3828880b6c8SJoachim Eastwood static int socfpga_dwmac_probe(struct platform_device *pdev)
38382732789SJoachim Eastwood {
3848880b6c8SJoachim Eastwood struct plat_stmmacenet_data *plat_dat;
3858880b6c8SJoachim Eastwood struct stmmac_resources stmmac_res;
38682732789SJoachim Eastwood struct device *dev = &pdev->dev;
38782732789SJoachim Eastwood int ret;
38882732789SJoachim Eastwood struct socfpga_dwmac *dwmac;
38950ac64cfSJohan Hovold struct net_device *ndev;
39050ac64cfSJohan Hovold struct stmmac_priv *stpriv;
39140ae2550SDinh Nguyen const struct socfpga_dwmac_ops *ops;
39240ae2550SDinh Nguyen
39340ae2550SDinh Nguyen ops = device_get_match_data(&pdev->dev);
39440ae2550SDinh Nguyen if (!ops) {
39540ae2550SDinh Nguyen dev_err(&pdev->dev, "no of match data provided\n");
39640ae2550SDinh Nguyen return -EINVAL;
39740ae2550SDinh Nguyen }
39882732789SJoachim Eastwood
3998880b6c8SJoachim Eastwood ret = stmmac_get_platform_resources(pdev, &stmmac_res);
4008880b6c8SJoachim Eastwood if (ret)
4018880b6c8SJoachim Eastwood return ret;
4028880b6c8SJoachim Eastwood
40383216e39SMichael Walle plat_dat = stmmac_probe_config_dt(pdev, stmmac_res.mac);
4048880b6c8SJoachim Eastwood if (IS_ERR(plat_dat))
4058880b6c8SJoachim Eastwood return PTR_ERR(plat_dat);
4068880b6c8SJoachim Eastwood
40782732789SJoachim Eastwood dwmac = devm_kzalloc(dev, sizeof(*dwmac), GFP_KERNEL);
408d2ed0a77SJohan Hovold if (!dwmac) {
409d2ed0a77SJohan Hovold ret = -ENOMEM;
410d2ed0a77SJohan Hovold goto err_remove_config_dt;
411d2ed0a77SJohan Hovold }
41282732789SJoachim Eastwood
413bc8a2d9bSDinh Nguyen dwmac->stmmac_ocp_rst = devm_reset_control_get_optional(dev, "stmmaceth-ocp");
414bc8a2d9bSDinh Nguyen if (IS_ERR(dwmac->stmmac_ocp_rst)) {
415bc8a2d9bSDinh Nguyen ret = PTR_ERR(dwmac->stmmac_ocp_rst);
416bc8a2d9bSDinh Nguyen dev_err(dev, "error getting reset control of ocp %d\n", ret);
417bc8a2d9bSDinh Nguyen goto err_remove_config_dt;
418bc8a2d9bSDinh Nguyen }
419bc8a2d9bSDinh Nguyen
420bc8a2d9bSDinh Nguyen reset_control_deassert(dwmac->stmmac_ocp_rst);
421bc8a2d9bSDinh Nguyen
42282732789SJoachim Eastwood ret = socfpga_dwmac_parse_data(dwmac, dev);
42382732789SJoachim Eastwood if (ret) {
42482732789SJoachim Eastwood dev_err(dev, "Unable to parse OF data\n");
425d2ed0a77SJohan Hovold goto err_remove_config_dt;
42682732789SJoachim Eastwood }
42782732789SJoachim Eastwood
42840ae2550SDinh Nguyen dwmac->ops = ops;
4298880b6c8SJoachim Eastwood plat_dat->bsp_priv = dwmac;
4308880b6c8SJoachim Eastwood plat_dat->fix_mac_speed = socfpga_dwmac_fix_mac_speed;
43182732789SJoachim Eastwood
432*7501912eSMaxime Chevallier plat_dat->riwt_off = 1;
433*7501912eSMaxime Chevallier
4343c201b5aSMarek Vasut ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
43550ac64cfSJohan Hovold if (ret)
436d2ed0a77SJohan Hovold goto err_remove_config_dt;
437fb3bbdb8STien Hock Loh
43850ac64cfSJohan Hovold ndev = platform_get_drvdata(pdev);
43950ac64cfSJohan Hovold stpriv = netdev_priv(ndev);
44070cb136fSJoachim Eastwood
44150ac64cfSJohan Hovold /* The socfpga driver needs to control the stmmac reset to set the phy
44250ac64cfSJohan Hovold * mode. Create a copy of the core reset handle so it can be used by
44350ac64cfSJohan Hovold * the driver later.
44470cb136fSJoachim Eastwood */
445f573c0b9Sjpinto dwmac->stmmac_rst = stpriv->plat->stmmac_rst;
44670cb136fSJoachim Eastwood
44740ae2550SDinh Nguyen ret = ops->set_phy_mode(dwmac);
44850ac64cfSJohan Hovold if (ret)
44950ac64cfSJohan Hovold goto err_dvr_remove;
45050ac64cfSJohan Hovold
4515d1f3fe7SMaxime Chevallier /* Create a regmap for the PCS so that it can be used by the PCS driver,
4525d1f3fe7SMaxime Chevallier * if we have such a PCS
4535d1f3fe7SMaxime Chevallier */
4545d1f3fe7SMaxime Chevallier if (dwmac->tse_pcs_base) {
45506b9dedeSMaxime Chevallier struct regmap_config pcs_regmap_cfg;
4565d1f3fe7SMaxime Chevallier struct mdio_regmap_config mrc;
4575d1f3fe7SMaxime Chevallier struct regmap *pcs_regmap;
4585d1f3fe7SMaxime Chevallier struct mii_bus *pcs_bus;
4595d1f3fe7SMaxime Chevallier
46006b9dedeSMaxime Chevallier memset(&pcs_regmap_cfg, 0, sizeof(pcs_regmap_cfg));
46106b9dedeSMaxime Chevallier memset(&mrc, 0, sizeof(mrc));
46206b9dedeSMaxime Chevallier
46306b9dedeSMaxime Chevallier pcs_regmap_cfg.reg_bits = 16;
46406b9dedeSMaxime Chevallier pcs_regmap_cfg.val_bits = 16;
46506b9dedeSMaxime Chevallier pcs_regmap_cfg.reg_shift = REGMAP_UPSHIFT(1);
46606b9dedeSMaxime Chevallier
4675d1f3fe7SMaxime Chevallier pcs_regmap = devm_regmap_init_mmio(&pdev->dev, dwmac->tse_pcs_base,
4685d1f3fe7SMaxime Chevallier &pcs_regmap_cfg);
4695d1f3fe7SMaxime Chevallier if (IS_ERR(pcs_regmap)) {
4705d1f3fe7SMaxime Chevallier ret = PTR_ERR(pcs_regmap);
4715d1f3fe7SMaxime Chevallier goto err_dvr_remove;
4725d1f3fe7SMaxime Chevallier }
4735d1f3fe7SMaxime Chevallier
4745d1f3fe7SMaxime Chevallier mrc.regmap = pcs_regmap;
4755d1f3fe7SMaxime Chevallier mrc.parent = &pdev->dev;
4765d1f3fe7SMaxime Chevallier mrc.valid_addr = 0x0;
47706b9dedeSMaxime Chevallier mrc.autoscan = false;
4785d1f3fe7SMaxime Chevallier
4795d1f3fe7SMaxime Chevallier snprintf(mrc.name, MII_BUS_ID_SIZE, "%s-pcs-mii", ndev->name);
4805d1f3fe7SMaxime Chevallier pcs_bus = devm_mdio_regmap_register(&pdev->dev, &mrc);
4815d1f3fe7SMaxime Chevallier if (IS_ERR(pcs_bus)) {
4825d1f3fe7SMaxime Chevallier ret = PTR_ERR(pcs_bus);
4835d1f3fe7SMaxime Chevallier goto err_dvr_remove;
4845d1f3fe7SMaxime Chevallier }
4855d1f3fe7SMaxime Chevallier
4865d1f3fe7SMaxime Chevallier stpriv->hw->lynx_pcs = lynx_pcs_create_mdiodev(pcs_bus, 0);
4875d1f3fe7SMaxime Chevallier if (IS_ERR(stpriv->hw->lynx_pcs)) {
4885d1f3fe7SMaxime Chevallier ret = PTR_ERR(stpriv->hw->lynx_pcs);
4895d1f3fe7SMaxime Chevallier goto err_dvr_remove;
4905d1f3fe7SMaxime Chevallier }
4915d1f3fe7SMaxime Chevallier }
4925d1f3fe7SMaxime Chevallier
49350ac64cfSJohan Hovold return 0;
49450ac64cfSJohan Hovold
49550ac64cfSJohan Hovold err_dvr_remove:
49650ac64cfSJohan Hovold stmmac_dvr_remove(&pdev->dev);
497d2ed0a77SJohan Hovold err_remove_config_dt:
498d2ed0a77SJohan Hovold stmmac_remove_config_dt(pdev, plat_dat);
4998880b6c8SJoachim Eastwood
5003c201b5aSMarek Vasut return ret;
5018880b6c8SJoachim Eastwood }
502c7c52ae7SJoachim Eastwood
socfpga_dwmac_remove(struct platform_device * pdev)503a8dd7404SMaxime Chevallier static void socfpga_dwmac_remove(struct platform_device *pdev)
504a8dd7404SMaxime Chevallier {
505a8dd7404SMaxime Chevallier struct net_device *ndev = platform_get_drvdata(pdev);
506a8dd7404SMaxime Chevallier struct stmmac_priv *priv = netdev_priv(ndev);
507a8dd7404SMaxime Chevallier struct phylink_pcs *pcs = priv->hw->lynx_pcs;
508a8dd7404SMaxime Chevallier
509a8dd7404SMaxime Chevallier stmmac_pltfr_remove(pdev);
510a8dd7404SMaxime Chevallier
511a8dd7404SMaxime Chevallier lynx_pcs_destroy(pcs);
512a8dd7404SMaxime Chevallier }
513a8dd7404SMaxime Chevallier
51456868deeSJoachim Eastwood #ifdef CONFIG_PM_SLEEP
socfpga_dwmac_resume(struct device * dev)51556868deeSJoachim Eastwood static int socfpga_dwmac_resume(struct device *dev)
51656868deeSJoachim Eastwood {
51756868deeSJoachim Eastwood struct net_device *ndev = dev_get_drvdata(dev);
51856868deeSJoachim Eastwood struct stmmac_priv *priv = netdev_priv(ndev);
51940ae2550SDinh Nguyen struct socfpga_dwmac *dwmac_priv = get_stmmac_bsp_priv(dev);
52056868deeSJoachim Eastwood
52140ae2550SDinh Nguyen dwmac_priv->ops->set_phy_mode(priv->plat->bsp_priv);
52256868deeSJoachim Eastwood
52353737247SJoachim Eastwood /* Before the enet controller is suspended, the phy is suspended.
52453737247SJoachim Eastwood * This causes the phy clock to be gated. The enet controller is
52553737247SJoachim Eastwood * resumed before the phy, so the clock is still gated "off" when
52653737247SJoachim Eastwood * the enet controller is resumed. This code makes sure the phy
52753737247SJoachim Eastwood * is "resumed" before reinitializing the enet controller since
52853737247SJoachim Eastwood * the enet controller depends on an active phy clock to complete
52953737247SJoachim Eastwood * a DMA reset. A DMA reset will "time out" if executed
53053737247SJoachim Eastwood * with no phy clock input on the Synopsys enet controller.
53153737247SJoachim Eastwood * Verified through Synopsys Case #8000711656.
53253737247SJoachim Eastwood *
53353737247SJoachim Eastwood * Note that the phy clock is also gated when the phy is isolated.
53453737247SJoachim Eastwood * Phy "suspend" and "isolate" controls are located in phy basic
53553737247SJoachim Eastwood * control register 0, and can be modified by the phy driver
53653737247SJoachim Eastwood * framework.
53753737247SJoachim Eastwood */
538d6d50c7eSPhilippe Reynes if (ndev->phydev)
539d6d50c7eSPhilippe Reynes phy_resume(ndev->phydev);
54053737247SJoachim Eastwood
54156868deeSJoachim Eastwood return stmmac_resume(dev);
54256868deeSJoachim Eastwood }
54356868deeSJoachim Eastwood #endif /* CONFIG_PM_SLEEP */
54456868deeSJoachim Eastwood
socfpga_dwmac_runtime_suspend(struct device * dev)54591195700SMeng Li static int __maybe_unused socfpga_dwmac_runtime_suspend(struct device *dev)
54691195700SMeng Li {
54791195700SMeng Li struct net_device *ndev = dev_get_drvdata(dev);
54891195700SMeng Li struct stmmac_priv *priv = netdev_priv(ndev);
54991195700SMeng Li
55091195700SMeng Li stmmac_bus_clks_config(priv, false);
55191195700SMeng Li
55291195700SMeng Li return 0;
55391195700SMeng Li }
55491195700SMeng Li
socfpga_dwmac_runtime_resume(struct device * dev)55591195700SMeng Li static int __maybe_unused socfpga_dwmac_runtime_resume(struct device *dev)
55691195700SMeng Li {
55791195700SMeng Li struct net_device *ndev = dev_get_drvdata(dev);
55891195700SMeng Li struct stmmac_priv *priv = netdev_priv(ndev);
55991195700SMeng Li
56091195700SMeng Li return stmmac_bus_clks_config(priv, true);
56191195700SMeng Li }
56291195700SMeng Li
56391195700SMeng Li static const struct dev_pm_ops socfpga_dwmac_pm_ops = {
56491195700SMeng Li SET_SYSTEM_SLEEP_PM_OPS(stmmac_suspend, socfpga_dwmac_resume)
56591195700SMeng Li SET_RUNTIME_PM_OPS(socfpga_dwmac_runtime_suspend, socfpga_dwmac_runtime_resume, NULL)
56691195700SMeng Li };
56756868deeSJoachim Eastwood
56840ae2550SDinh Nguyen static const struct socfpga_dwmac_ops socfpga_gen5_ops = {
56940ae2550SDinh Nguyen .set_phy_mode = socfpga_gen5_set_phy_mode,
57040ae2550SDinh Nguyen };
57140ae2550SDinh Nguyen
57240ae2550SDinh Nguyen static const struct socfpga_dwmac_ops socfpga_gen10_ops = {
57340ae2550SDinh Nguyen .set_phy_mode = socfpga_gen10_set_phy_mode,
57440ae2550SDinh Nguyen };
57540ae2550SDinh Nguyen
576c7c52ae7SJoachim Eastwood static const struct of_device_id socfpga_dwmac_match[] = {
57740ae2550SDinh Nguyen { .compatible = "altr,socfpga-stmmac", .data = &socfpga_gen5_ops },
57840ae2550SDinh Nguyen { .compatible = "altr,socfpga-stmmac-a10-s10", .data = &socfpga_gen10_ops },
579c7c52ae7SJoachim Eastwood { }
580c7c52ae7SJoachim Eastwood };
581c7c52ae7SJoachim Eastwood MODULE_DEVICE_TABLE(of, socfpga_dwmac_match);
582c7c52ae7SJoachim Eastwood
583c7c52ae7SJoachim Eastwood static struct platform_driver socfpga_dwmac_driver = {
5848880b6c8SJoachim Eastwood .probe = socfpga_dwmac_probe,
585a8dd7404SMaxime Chevallier .remove_new = socfpga_dwmac_remove,
586c7c52ae7SJoachim Eastwood .driver = {
587c7c52ae7SJoachim Eastwood .name = "socfpga-dwmac",
58856868deeSJoachim Eastwood .pm = &socfpga_dwmac_pm_ops,
589c7c52ae7SJoachim Eastwood .of_match_table = socfpga_dwmac_match,
590c7c52ae7SJoachim Eastwood },
591c7c52ae7SJoachim Eastwood };
592c7c52ae7SJoachim Eastwood module_platform_driver(socfpga_dwmac_driver);
593c7c52ae7SJoachim Eastwood
594c7c52ae7SJoachim Eastwood MODULE_LICENSE("GPL v2");
595