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