1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * T-HEAD DWMAC platform driver
4  *
5  * Copyright (C) 2021 Alibaba Group Holding Limited.
6  * Copyright (C) 2023 Jisheng Zhang <jszhang@kernel.org>
7  * Copyright (C) 2025 Yao Zi <ziyao@disroot.org>
8  *
9  */
10 
11 #include <asm/io.h>
12 #include <clk.h>
13 #include <dm.h>
14 #include <linux/bitfield.h>
15 #include <phy.h>
16 
17 #include "designware.h"
18 
19 #define GMAC_CLK_EN			0x00
20 #define  GMAC_TX_CLK_EN			BIT(1)
21 #define  GMAC_TX_CLK_N_EN		BIT(2)
22 #define  GMAC_TX_CLK_OUT_EN		BIT(3)
23 #define  GMAC_RX_CLK_EN			BIT(4)
24 #define  GMAC_RX_CLK_N_EN		BIT(5)
25 #define  GMAC_EPHY_REF_CLK_EN		BIT(6)
26 #define GMAC_RXCLK_DELAY_CTRL		0x04
27 #define  GMAC_RXCLK_BYPASS		BIT(15)
28 #define  GMAC_RXCLK_INVERT		BIT(14)
29 #define  GMAC_RXCLK_DELAY		GENMASK(4, 0)
30 #define GMAC_TXCLK_DELAY_CTRL		0x08
31 #define  GMAC_TXCLK_BYPASS		BIT(15)
32 #define  GMAC_TXCLK_INVERT		BIT(14)
33 #define  GMAC_TXCLK_DELAY		GENMASK(4, 0)
34 #define GMAC_PLLCLK_DIV			0x0c
35 #define  GMAC_PLLCLK_DIV_EN		BIT(31)
36 #define  GMAC_PLLCLK_DIV_NUM		GENMASK(7, 0)
37 #define GMAC_GTXCLK_SEL			0x18
38 #define  GMAC_GTXCLK_SEL_PLL		BIT(0)
39 #define GMAC_INTF_CTRL			0x1c
40 #define  PHY_INTF_MASK			BIT(0)
41 #define  PHY_INTF_RGMII			FIELD_PREP(PHY_INTF_MASK, 1)
42 #define  PHY_INTF_MII_GMII		FIELD_PREP(PHY_INTF_MASK, 0)
43 #define GMAC_TXCLK_OEN			0x20
44 #define  TXCLK_DIR_MASK			BIT(0)
45 #define  TXCLK_DIR_OUTPUT		FIELD_PREP(TXCLK_DIR_MASK, 0)
46 #define  TXCLK_DIR_INPUT		FIELD_PREP(TXCLK_DIR_MASK, 1)
47 
48 #define GMAC_RGMII_CLK_RATE		125000000
49 
50 struct dwmac_thead_plat {
51 	struct dw_eth_pdata dw_eth_pdata;
52 	void __iomem *apb_base;
53 };
54 
dwmac_thead_set_phy_if(struct dwmac_thead_plat * plat)55 static int dwmac_thead_set_phy_if(struct dwmac_thead_plat *plat)
56 {
57 	u32 phyif;
58 
59 	switch (plat->dw_eth_pdata.eth_pdata.phy_interface) {
60 	case PHY_INTERFACE_MODE_MII:
61 		phyif = PHY_INTF_MII_GMII;
62 		break;
63 	case PHY_INTERFACE_MODE_RGMII:
64 	case PHY_INTERFACE_MODE_RGMII_ID:
65 	case PHY_INTERFACE_MODE_RGMII_TXID:
66 	case PHY_INTERFACE_MODE_RGMII_RXID:
67 		phyif = PHY_INTF_RGMII;
68 		break;
69 	default:
70 		return -EINVAL;
71 	}
72 
73 	writel(phyif, plat->apb_base + GMAC_INTF_CTRL);
74 	return 0;
75 }
76 
dwmac_thead_set_txclk_dir(struct dwmac_thead_plat * plat)77 static int dwmac_thead_set_txclk_dir(struct dwmac_thead_plat *plat)
78 {
79 	u32 txclk_dir;
80 
81 	switch (plat->dw_eth_pdata.eth_pdata.phy_interface) {
82 	case PHY_INTERFACE_MODE_MII:
83 		txclk_dir = TXCLK_DIR_INPUT;
84 		break;
85 	case PHY_INTERFACE_MODE_RGMII:
86 	case PHY_INTERFACE_MODE_RGMII_ID:
87 	case PHY_INTERFACE_MODE_RGMII_TXID:
88 	case PHY_INTERFACE_MODE_RGMII_RXID:
89 		txclk_dir = TXCLK_DIR_OUTPUT;
90 		break;
91 	default:
92 		return -EINVAL;
93 	}
94 
95 	writel(txclk_dir, plat->apb_base + GMAC_TXCLK_OEN);
96 	return 0;
97 }
98 
dwmac_thead_rgmii_tx_rate(int speed)99 static unsigned long dwmac_thead_rgmii_tx_rate(int speed)
100 {
101 	switch (speed) {
102 	case 10:
103 		return 2500000;
104 	case 100:
105 		return 25000000;
106 	case 1000:
107 		return 125000000;
108 	}
109 
110 	return -EINVAL;
111 }
112 
dwmac_thead_set_clk_tx_rate(struct dwmac_thead_plat * plat,struct dw_eth_dev * edev,unsigned long tx_rate)113 static int dwmac_thead_set_clk_tx_rate(struct dwmac_thead_plat *plat,
114 				       struct dw_eth_dev *edev,
115 				       unsigned long tx_rate)
116 {
117 	unsigned long rate;
118 	u32 div, reg;
119 
120 	rate = clk_get_rate(&edev->clocks[0]);
121 
122 	writel(0, plat->apb_base + GMAC_PLLCLK_DIV);
123 
124 	div = rate / tx_rate;
125 	if (rate != tx_rate * div) {
126 		pr_err("invalid gmac rate %lu\n", rate);
127 		return -EINVAL;
128 	}
129 
130 	reg = FIELD_PREP(GMAC_PLLCLK_DIV_EN, 1) |
131 	      FIELD_PREP(GMAC_PLLCLK_DIV_NUM, div);
132 		writel(reg, plat->apb_base + GMAC_PLLCLK_DIV);
133 
134 	return 0;
135 }
136 
dwmac_thead_enable_clk(struct dwmac_thead_plat * plat)137 static int dwmac_thead_enable_clk(struct dwmac_thead_plat *plat)
138 {
139 	u32 reg;
140 
141 	switch (plat->dw_eth_pdata.eth_pdata.phy_interface) {
142 	case PHY_INTERFACE_MODE_MII:
143 		reg = GMAC_RX_CLK_EN | GMAC_TX_CLK_EN;
144 		break;
145 
146 	case PHY_INTERFACE_MODE_RGMII:
147 	case PHY_INTERFACE_MODE_RGMII_ID:
148 	case PHY_INTERFACE_MODE_RGMII_RXID:
149 	case PHY_INTERFACE_MODE_RGMII_TXID:
150 		/* use pll */
151 		writel(GMAC_GTXCLK_SEL_PLL, plat->apb_base + GMAC_GTXCLK_SEL);
152 		reg = GMAC_TX_CLK_EN | GMAC_TX_CLK_N_EN | GMAC_TX_CLK_OUT_EN |
153 		      GMAC_RX_CLK_EN | GMAC_RX_CLK_N_EN;
154 		break;
155 
156 	default:
157 		return -EINVAL;
158 	}
159 
160 	writel(reg, plat->apb_base + GMAC_CLK_EN);
161 	return 0;
162 }
163 
dwmac_thead_eth_start(struct udevice * dev)164 static int dwmac_thead_eth_start(struct udevice *dev)
165 {
166 	struct dwmac_thead_plat *plat = dev_get_plat(dev);
167 	struct dw_eth_dev *edev = dev_get_priv(dev);
168 	phy_interface_t interface;
169 	bool is_rgmii;
170 	long tx_rate;
171 	int ret;
172 
173 	interface = plat->dw_eth_pdata.eth_pdata.phy_interface;
174 	is_rgmii = (interface == PHY_INTERFACE_MODE_RGMII)	|
175 		   (interface == PHY_INTERFACE_MODE_RGMII_ID)	|
176 		   (interface == PHY_INTERFACE_MODE_RGMII_RXID)	|
177 		   (interface == PHY_INTERFACE_MODE_RGMII_TXID);
178 
179 	/*
180 	 * When operating in RGMII mode, the TX clock is generated by an
181 	 * internal divider and fed to the MAC. Configure and enable it before
182 	 * initializing the MAC.
183 	 */
184 	if (is_rgmii) {
185 		ret = dwmac_thead_set_clk_tx_rate(plat, edev,
186 						  GMAC_RGMII_CLK_RATE);
187 		if (ret)
188 			return ret;
189 	}
190 
191 	ret = designware_eth_init(edev, plat->dw_eth_pdata.eth_pdata.enetaddr);
192 	if (ret)
193 		return ret;
194 
195 	if (is_rgmii) {
196 		tx_rate = dwmac_thead_rgmii_tx_rate(edev->phydev->speed);
197 		if (tx_rate < 0)
198 			return tx_rate;
199 
200 		ret = dwmac_thead_set_clk_tx_rate(plat, edev, tx_rate);
201 		if (ret)
202 			return ret;
203 	}
204 
205 	ret = designware_eth_enable(edev);
206 	if (ret)
207 		return ret;
208 
209 	return 0;
210 }
211 
dwmac_thead_probe(struct udevice * dev)212 static int dwmac_thead_probe(struct udevice *dev)
213 {
214 	struct dwmac_thead_plat *plat = dev_get_plat(dev);
215 	unsigned int reg;
216 	int ret;
217 
218 	ret = designware_eth_probe(dev);
219 	if (ret)
220 		return ret;
221 
222 	ret = dwmac_thead_set_phy_if(plat);
223 	if (ret) {
224 		pr_err("failed to set phy interface: %d\n", ret);
225 		return ret;
226 	}
227 
228 	ret = dwmac_thead_set_txclk_dir(plat);
229 	if (ret) {
230 		pr_err("failed to set TX clock direction: %d\n", ret);
231 		return ret;
232 	}
233 
234 	reg = readl(plat->apb_base + GMAC_RXCLK_DELAY_CTRL);
235 	reg &= ~(GMAC_RXCLK_DELAY);
236 	reg |= FIELD_PREP(GMAC_RXCLK_DELAY, 0);
237 	writel(reg, plat->apb_base + GMAC_RXCLK_DELAY_CTRL);
238 
239 	reg = readl(plat->apb_base + GMAC_TXCLK_DELAY_CTRL);
240 	reg &= ~(GMAC_TXCLK_DELAY);
241 	reg |= FIELD_PREP(GMAC_TXCLK_DELAY, 0);
242 	writel(reg, plat->apb_base + GMAC_TXCLK_DELAY_CTRL);
243 
244 	ret = dwmac_thead_enable_clk(plat);
245 	if (ret)
246 		pr_err("failed to enable clock: %d\n", ret);
247 
248 	return ret;
249 }
250 
dwmac_thead_of_to_plat(struct udevice * dev)251 static int dwmac_thead_of_to_plat(struct udevice *dev)
252 {
253 	struct dwmac_thead_plat *pdata = dev_get_plat(dev);
254 
255 	pdata->apb_base = dev_read_addr_index_ptr(dev, 1);
256 	if (!pdata->apb_base) {
257 		pr_err("failed to get apb registers\n");
258 		return -ENOENT;
259 	}
260 
261 	return designware_eth_of_to_plat(dev);
262 }
263 
264 static const struct eth_ops dwmac_thead_eth_ops = {
265 	.start                  = dwmac_thead_eth_start,
266 	.send                   = designware_eth_send,
267 	.recv                   = designware_eth_recv,
268 	.free_pkt               = designware_eth_free_pkt,
269 	.stop                   = designware_eth_stop,
270 	.write_hwaddr           = designware_eth_write_hwaddr,
271 };
272 
273 static const struct udevice_id dwmac_thead_match[] = {
274 	{ .compatible = "thead,th1520-gmac" },
275 	{ /* sentinel */ }
276 };
277 
278 U_BOOT_DRIVER(dwmac_thead) = {
279 	.name		= "dwmac_thead",
280 	.id		= UCLASS_ETH,
281 	.of_match	= dwmac_thead_match,
282 	.of_to_plat	= dwmac_thead_of_to_plat,
283 	.probe		= dwmac_thead_probe,
284 	.ops		= &dwmac_thead_eth_ops,
285 	.priv_auto	= sizeof(struct dw_eth_dev),
286 	.plat_auto	= sizeof(struct dwmac_thead_plat),
287 	.flags		= DM_FLAG_ALLOC_PRIV_DMA,
288 };
289