1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (C) 2018 Marek Vasut <marex@denx.de>
4  *
5  * Altera SoCFPGA EMAC extras
6  */
7 
8 #include <common.h>
9 #include <asm/arch/secure_reg_helper.h>
10 #include <asm/arch/system_manager.h>
11 #include <asm/io.h>
12 #include <dm.h>
13 #include <clk.h>
14 #include <phy.h>
15 #include <regmap.h>
16 #include <reset.h>
17 #include <syscon.h>
18 #include "designware.h"
19 #include <dm/device_compat.h>
20 #include <linux/err.h>
21 
22 struct dwmac_socfpga_plat {
23 	struct dw_eth_pdata	dw_eth_pdata;
24 	void			*phy_intf;
25 	u32			reg_shift;
26 };
27 
dwmac_socfpga_of_to_plat(struct udevice * dev)28 static int dwmac_socfpga_of_to_plat(struct udevice *dev)
29 {
30 	struct dwmac_socfpga_plat *pdata = dev_get_plat(dev);
31 	struct regmap *regmap;
32 	struct ofnode_phandle_args args;
33 	void *range;
34 	int ret;
35 
36 	ret = dev_read_phandle_with_args(dev, "altr,sysmgr-syscon", NULL,
37 					 2, 0, &args);
38 	if (ret) {
39 		dev_err(dev, "Failed to get syscon: %d\n", ret);
40 		return ret;
41 	}
42 
43 	if (args.args_count != 2) {
44 		dev_err(dev, "Invalid number of syscon args\n");
45 		return -EINVAL;
46 	}
47 
48 	regmap = syscon_node_to_regmap(args.node);
49 	if (IS_ERR(regmap)) {
50 		ret = PTR_ERR(regmap);
51 		dev_err(dev, "Failed to get regmap: %d\n", ret);
52 		return ret;
53 	}
54 
55 	range = regmap_get_range(regmap, 0);
56 	if (!range) {
57 		dev_err(dev, "Failed to get regmap range\n");
58 		return -ENOMEM;
59 	}
60 
61 	pdata->phy_intf = range + args.args[0];
62 	pdata->reg_shift = args.args[1];
63 
64 	return designware_eth_of_to_plat(dev);
65 }
66 
dwmac_socfpga_do_setphy(struct udevice * dev,u32 modereg)67 static int dwmac_socfpga_do_setphy(struct udevice *dev, u32 modereg)
68 {
69 	struct dwmac_socfpga_plat *pdata = dev_get_plat(dev);
70 	u32 modemask = SYSMGR_EMACGRP_CTRL_PHYSEL_MASK << pdata->reg_shift;
71 
72 #if !defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_ATF)
73 	u32 index = ((u64)pdata->phy_intf - socfpga_get_sysmgr_addr() -
74 		     SYSMGR_SOC64_EMAC0) >> 2;
75 
76 	u32 id = SOCFPGA_SECURE_REG_SYSMGR_SOC64_EMAC0 + index;
77 
78 	int ret = socfpga_secure_reg_update32(id,
79 					     modemask,
80 					     modereg << pdata->reg_shift);
81 	if (ret) {
82 		dev_err(dev, "Failed to set PHY register via SMC call\n");
83 		return ret;
84 	}
85 #else
86 	clrsetbits_le32(pdata->phy_intf, modemask,
87 			modereg << pdata->reg_shift);
88 #endif
89 
90 	return 0;
91 }
92 
dwmac_socfpga_probe(struct udevice * dev)93 static int dwmac_socfpga_probe(struct udevice *dev)
94 {
95 	struct dwmac_socfpga_plat *pdata = dev_get_plat(dev);
96 	struct eth_pdata *edata = &pdata->dw_eth_pdata.eth_pdata;
97 	struct reset_ctl_bulk reset_bulk;
98 	int ret;
99 	u32 modereg;
100 
101 	switch (edata->phy_interface) {
102 	case PHY_INTERFACE_MODE_MII:
103 	case PHY_INTERFACE_MODE_GMII:
104 		modereg = SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_GMII_MII;
105 		break;
106 	case PHY_INTERFACE_MODE_RMII:
107 		modereg = SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_RMII;
108 		break;
109 	case PHY_INTERFACE_MODE_RGMII:
110 		modereg = SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_RGMII;
111 		break;
112 	default:
113 		dev_err(dev, "Unsupported PHY mode\n");
114 		return -EINVAL;
115 	}
116 
117 	ret = reset_get_bulk(dev, &reset_bulk);
118 	if (ret) {
119 		dev_err(dev, "Failed to get reset: %d\n", ret);
120 		return ret;
121 	}
122 
123 	reset_assert_bulk(&reset_bulk);
124 
125 	ret = dwmac_socfpga_do_setphy(dev, modereg);
126 	if (ret)
127 		return ret;
128 
129 	reset_release_bulk(&reset_bulk);
130 
131 	return designware_eth_probe(dev);
132 }
133 
134 static const struct udevice_id dwmac_socfpga_ids[] = {
135 	{ .compatible = "altr,socfpga-stmmac" },
136 	{ }
137 };
138 
139 U_BOOT_DRIVER(dwmac_socfpga) = {
140 	.name		= "dwmac_socfpga",
141 	.id		= UCLASS_ETH,
142 	.of_match	= dwmac_socfpga_ids,
143 	.of_to_plat = dwmac_socfpga_of_to_plat,
144 	.probe		= dwmac_socfpga_probe,
145 	.ops		= &designware_eth_ops,
146 	.priv_auto	= sizeof(struct dw_eth_dev),
147 	.plat_auto	= sizeof(struct dwmac_socfpga_plat),
148 	.flags		= DM_FLAG_ALLOC_PRIV_DMA,
149 };
150