xref: /linux/drivers/net/ethernet/stmicro/stmmac/dwmac-sun55i.c (revision 55a42f78ffd386e01a5404419f8c5ded7db70a21)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * dwmac-sun55i.c - Allwinner sun55i GMAC200 specific glue layer
4  *
5  * Copyright (C) 2025 Chen-Yu Tsai <wens@csie.org>
6  *
7  * syscon parts taken from dwmac-sun8i.c, which is
8  *
9  * Copyright (C) 2017 Corentin Labbe <clabbe.montjoie@gmail.com>
10  */
11 
12 #include <linux/bitfield.h>
13 #include <linux/bits.h>
14 #include <linux/mfd/syscon.h>
15 #include <linux/module.h>
16 #include <linux/of.h>
17 #include <linux/phy.h>
18 #include <linux/platform_device.h>
19 #include <linux/regmap.h>
20 #include <linux/regulator/consumer.h>
21 #include <linux/stmmac.h>
22 
23 #include "stmmac.h"
24 #include "stmmac_platform.h"
25 
26 #define SYSCON_REG		0x34
27 
28 /* RMII specific bits */
29 #define SYSCON_RMII_EN		BIT(13) /* 1: enable RMII (overrides EPIT) */
30 /* Generic system control EMAC_CLK bits */
31 #define SYSCON_ETXDC_MASK		GENMASK(12, 10)
32 #define SYSCON_ERXDC_MASK		GENMASK(9, 5)
33 /* EMAC PHY Interface Type */
34 #define SYSCON_EPIT			BIT(2) /* 1: RGMII, 0: MII */
35 #define SYSCON_ETCS_MASK		GENMASK(1, 0)
36 #define SYSCON_ETCS_MII		0x0
37 #define SYSCON_ETCS_EXT_GMII	0x1
38 #define SYSCON_ETCS_INT_GMII	0x2
39 
40 static int sun55i_gmac200_set_syscon(struct device *dev,
41 				     struct plat_stmmacenet_data *plat)
42 {
43 	struct device_node *node = dev->of_node;
44 	struct regmap *regmap;
45 	u32 val, reg = 0;
46 	int ret;
47 
48 	regmap = syscon_regmap_lookup_by_phandle(node, "syscon");
49 	if (IS_ERR(regmap))
50 		return dev_err_probe(dev, PTR_ERR(regmap), "Unable to map syscon\n");
51 
52 	if (!of_property_read_u32(node, "tx-internal-delay-ps", &val)) {
53 		if (val % 100)
54 			return dev_err_probe(dev, -EINVAL,
55 					     "tx-delay must be a multiple of 100ps\n");
56 		val /= 100;
57 		dev_dbg(dev, "set tx-delay to %x\n", val);
58 		if (!FIELD_FIT(SYSCON_ETXDC_MASK, val))
59 			return dev_err_probe(dev, -EINVAL,
60 					     "TX clock delay exceeds maximum (%u00ps > %lu00ps)\n",
61 					     val, FIELD_MAX(SYSCON_ETXDC_MASK));
62 
63 		reg |= FIELD_PREP(SYSCON_ETXDC_MASK, val);
64 	}
65 
66 	if (!of_property_read_u32(node, "rx-internal-delay-ps", &val)) {
67 		if (val % 100)
68 			return dev_err_probe(dev, -EINVAL,
69 					     "rx-delay must be a multiple of 100ps\n");
70 		val /= 100;
71 		dev_dbg(dev, "set rx-delay to %x\n", val);
72 		if (!FIELD_FIT(SYSCON_ERXDC_MASK, val))
73 			return dev_err_probe(dev, -EINVAL,
74 					     "RX clock delay exceeds maximum (%u00ps > %lu00ps)\n",
75 					     val, FIELD_MAX(SYSCON_ERXDC_MASK));
76 
77 		reg |= FIELD_PREP(SYSCON_ERXDC_MASK, val);
78 	}
79 
80 	switch (plat->phy_interface) {
81 	case PHY_INTERFACE_MODE_MII:
82 		/* default */
83 		break;
84 	case PHY_INTERFACE_MODE_RGMII:
85 	case PHY_INTERFACE_MODE_RGMII_ID:
86 	case PHY_INTERFACE_MODE_RGMII_RXID:
87 	case PHY_INTERFACE_MODE_RGMII_TXID:
88 		reg |= SYSCON_EPIT | SYSCON_ETCS_INT_GMII;
89 		break;
90 	case PHY_INTERFACE_MODE_RMII:
91 		reg |= SYSCON_RMII_EN;
92 		break;
93 	default:
94 		return dev_err_probe(dev, -EINVAL, "Unsupported interface mode: %s",
95 				     phy_modes(plat->phy_interface));
96 	}
97 
98 	ret = regmap_write(regmap, SYSCON_REG, reg);
99 	if (ret < 0)
100 		return dev_err_probe(dev, ret, "Failed to write to syscon\n");
101 
102 	return 0;
103 }
104 
105 static int sun55i_gmac200_probe(struct platform_device *pdev)
106 {
107 	struct plat_stmmacenet_data *plat_dat;
108 	struct stmmac_resources stmmac_res;
109 	struct device *dev = &pdev->dev;
110 	struct clk *clk;
111 	int ret;
112 
113 	ret = stmmac_get_platform_resources(pdev, &stmmac_res);
114 	if (ret)
115 		return ret;
116 
117 	plat_dat = devm_stmmac_probe_config_dt(pdev, stmmac_res.mac);
118 	if (IS_ERR(plat_dat))
119 		return PTR_ERR(plat_dat);
120 
121 	/* BSP disables it */
122 	plat_dat->flags |= STMMAC_FLAG_SPH_DISABLE;
123 	plat_dat->host_dma_width = 32;
124 
125 	ret = sun55i_gmac200_set_syscon(dev, plat_dat);
126 	if (ret)
127 		return ret;
128 
129 	clk = devm_clk_get_enabled(dev, "mbus");
130 	if (IS_ERR(clk))
131 		return dev_err_probe(dev, PTR_ERR(clk),
132 				     "Failed to get or enable MBUS clock\n");
133 
134 	ret = devm_regulator_get_enable_optional(dev, "phy");
135 	if (ret)
136 		return dev_err_probe(dev, ret, "Failed to get or enable PHY supply\n");
137 
138 	return devm_stmmac_pltfr_probe(pdev, plat_dat, &stmmac_res);
139 }
140 
141 static const struct of_device_id sun55i_gmac200_match[] = {
142 	{ .compatible = "allwinner,sun55i-a523-gmac200" },
143 	{ }
144 };
145 MODULE_DEVICE_TABLE(of, sun55i_gmac200_match);
146 
147 static struct platform_driver sun55i_gmac200_driver = {
148 	.probe  = sun55i_gmac200_probe,
149 	.driver = {
150 		.name           = "dwmac-sun55i",
151 		.pm		= &stmmac_pltfr_pm_ops,
152 		.of_match_table = sun55i_gmac200_match,
153 	},
154 };
155 module_platform_driver(sun55i_gmac200_driver);
156 
157 MODULE_AUTHOR("Chen-Yu Tsai <wens@csie.org>");
158 MODULE_DESCRIPTION("Allwinner sun55i GMAC200 specific glue layer");
159 MODULE_LICENSE("GPL");
160