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 <[email protected]>
7 *
8 */
9
10 #include <linux/bitfield.h>
11 #include <linux/module.h>
12 #include <linux/of.h>
13 #include <linux/of_device.h>
14 #include <linux/of_net.h>
15 #include <linux/platform_device.h>
16
17 #include "stmmac_platform.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_GMII_RGMII_RATE 125000000
49 #define GMAC_MII_RATE 25000000
50
51 struct thead_dwmac {
52 struct plat_stmmacenet_data *plat;
53 void __iomem *apb_base;
54 struct device *dev;
55 };
56
thead_dwmac_set_phy_if(struct plat_stmmacenet_data * plat)57 static int thead_dwmac_set_phy_if(struct plat_stmmacenet_data *plat)
58 {
59 struct thead_dwmac *dwmac = plat->bsp_priv;
60 u32 phyif;
61
62 switch (plat->mac_interface) {
63 case PHY_INTERFACE_MODE_MII:
64 phyif = PHY_INTF_MII_GMII;
65 break;
66 case PHY_INTERFACE_MODE_RGMII:
67 case PHY_INTERFACE_MODE_RGMII_ID:
68 case PHY_INTERFACE_MODE_RGMII_TXID:
69 case PHY_INTERFACE_MODE_RGMII_RXID:
70 phyif = PHY_INTF_RGMII;
71 break;
72 default:
73 dev_err(dwmac->dev, "unsupported phy interface %d\n",
74 plat->mac_interface);
75 return -EINVAL;
76 }
77
78 writel(phyif, dwmac->apb_base + GMAC_INTF_CTRL);
79 return 0;
80 }
81
thead_dwmac_set_txclk_dir(struct plat_stmmacenet_data * plat)82 static int thead_dwmac_set_txclk_dir(struct plat_stmmacenet_data *plat)
83 {
84 struct thead_dwmac *dwmac = plat->bsp_priv;
85 u32 txclk_dir;
86
87 switch (plat->mac_interface) {
88 case PHY_INTERFACE_MODE_MII:
89 txclk_dir = TXCLK_DIR_INPUT;
90 break;
91 case PHY_INTERFACE_MODE_RGMII:
92 case PHY_INTERFACE_MODE_RGMII_ID:
93 case PHY_INTERFACE_MODE_RGMII_TXID:
94 case PHY_INTERFACE_MODE_RGMII_RXID:
95 txclk_dir = TXCLK_DIR_OUTPUT;
96 break;
97 default:
98 dev_err(dwmac->dev, "unsupported phy interface %d\n",
99 plat->mac_interface);
100 return -EINVAL;
101 }
102
103 writel(txclk_dir, dwmac->apb_base + GMAC_TXCLK_OEN);
104 return 0;
105 }
106
thead_dwmac_fix_speed(void * priv,unsigned int speed,unsigned int mode)107 static void thead_dwmac_fix_speed(void *priv, unsigned int speed, unsigned int mode)
108 {
109 struct plat_stmmacenet_data *plat;
110 struct thead_dwmac *dwmac = priv;
111 unsigned long rate;
112 u32 div, reg;
113
114 plat = dwmac->plat;
115
116 switch (plat->mac_interface) {
117 /* For MII, rxc/txc is provided by phy */
118 case PHY_INTERFACE_MODE_MII:
119 return;
120
121 case PHY_INTERFACE_MODE_RGMII:
122 case PHY_INTERFACE_MODE_RGMII_ID:
123 case PHY_INTERFACE_MODE_RGMII_RXID:
124 case PHY_INTERFACE_MODE_RGMII_TXID:
125 rate = clk_get_rate(plat->stmmac_clk);
126 if (!rate || rate % GMAC_GMII_RGMII_RATE != 0 ||
127 rate % GMAC_MII_RATE != 0) {
128 dev_err(dwmac->dev, "invalid gmac rate %ld\n", rate);
129 return;
130 }
131
132 writel(0, dwmac->apb_base + GMAC_PLLCLK_DIV);
133
134 switch (speed) {
135 case SPEED_1000:
136 div = rate / GMAC_GMII_RGMII_RATE;
137 break;
138 case SPEED_100:
139 div = rate / GMAC_MII_RATE;
140 break;
141 case SPEED_10:
142 div = rate * 10 / GMAC_MII_RATE;
143 break;
144 default:
145 dev_err(dwmac->dev, "invalid speed %u\n", speed);
146 return;
147 }
148
149 reg = FIELD_PREP(GMAC_PLLCLK_DIV_EN, 1) |
150 FIELD_PREP(GMAC_PLLCLK_DIV_NUM, div);
151 writel(reg, dwmac->apb_base + GMAC_PLLCLK_DIV);
152 break;
153 default:
154 dev_err(dwmac->dev, "unsupported phy interface %d\n",
155 plat->mac_interface);
156 return;
157 }
158 }
159
thead_dwmac_enable_clk(struct plat_stmmacenet_data * plat)160 static int thead_dwmac_enable_clk(struct plat_stmmacenet_data *plat)
161 {
162 struct thead_dwmac *dwmac = plat->bsp_priv;
163 u32 reg;
164
165 switch (plat->mac_interface) {
166 case PHY_INTERFACE_MODE_MII:
167 reg = GMAC_RX_CLK_EN | GMAC_TX_CLK_EN;
168 break;
169
170 case PHY_INTERFACE_MODE_RGMII:
171 case PHY_INTERFACE_MODE_RGMII_ID:
172 case PHY_INTERFACE_MODE_RGMII_RXID:
173 case PHY_INTERFACE_MODE_RGMII_TXID:
174 /* use pll */
175 writel(GMAC_GTXCLK_SEL_PLL, dwmac->apb_base + GMAC_GTXCLK_SEL);
176 reg = GMAC_TX_CLK_EN | GMAC_TX_CLK_N_EN | GMAC_TX_CLK_OUT_EN |
177 GMAC_RX_CLK_EN | GMAC_RX_CLK_N_EN;
178 break;
179
180 default:
181 dev_err(dwmac->dev, "unsupported phy interface %d\n",
182 plat->mac_interface);
183 return -EINVAL;
184 }
185
186 writel(reg, dwmac->apb_base + GMAC_CLK_EN);
187 return 0;
188 }
189
thead_dwmac_init(struct platform_device * pdev,void * priv)190 static int thead_dwmac_init(struct platform_device *pdev, void *priv)
191 {
192 struct thead_dwmac *dwmac = priv;
193 unsigned int reg;
194 int ret;
195
196 ret = thead_dwmac_set_phy_if(dwmac->plat);
197 if (ret)
198 return ret;
199
200 ret = thead_dwmac_set_txclk_dir(dwmac->plat);
201 if (ret)
202 return ret;
203
204 reg = readl(dwmac->apb_base + GMAC_RXCLK_DELAY_CTRL);
205 reg &= ~(GMAC_RXCLK_DELAY);
206 reg |= FIELD_PREP(GMAC_RXCLK_DELAY, 0);
207 writel(reg, dwmac->apb_base + GMAC_RXCLK_DELAY_CTRL);
208
209 reg = readl(dwmac->apb_base + GMAC_TXCLK_DELAY_CTRL);
210 reg &= ~(GMAC_TXCLK_DELAY);
211 reg |= FIELD_PREP(GMAC_TXCLK_DELAY, 0);
212 writel(reg, dwmac->apb_base + GMAC_TXCLK_DELAY_CTRL);
213
214 return thead_dwmac_enable_clk(dwmac->plat);
215 }
216
thead_dwmac_probe(struct platform_device * pdev)217 static int thead_dwmac_probe(struct platform_device *pdev)
218 {
219 struct stmmac_resources stmmac_res;
220 struct plat_stmmacenet_data *plat;
221 struct thead_dwmac *dwmac;
222 void __iomem *apb;
223 int ret;
224
225 ret = stmmac_get_platform_resources(pdev, &stmmac_res);
226 if (ret)
227 return dev_err_probe(&pdev->dev, ret,
228 "failed to get resources\n");
229
230 plat = devm_stmmac_probe_config_dt(pdev, stmmac_res.mac);
231 if (IS_ERR(plat))
232 return dev_err_probe(&pdev->dev, PTR_ERR(plat),
233 "dt configuration failed\n");
234
235 dwmac = devm_kzalloc(&pdev->dev, sizeof(*dwmac), GFP_KERNEL);
236 if (!dwmac)
237 return -ENOMEM;
238
239 apb = devm_platform_ioremap_resource(pdev, 1);
240 if (IS_ERR(apb))
241 return dev_err_probe(&pdev->dev, PTR_ERR(apb),
242 "failed to remap gmac apb registers\n");
243
244 dwmac->dev = &pdev->dev;
245 dwmac->plat = plat;
246 dwmac->apb_base = apb;
247 plat->bsp_priv = dwmac;
248 plat->fix_mac_speed = thead_dwmac_fix_speed;
249 plat->init = thead_dwmac_init;
250
251 return devm_stmmac_pltfr_probe(pdev, plat, &stmmac_res);
252 }
253
254 static const struct of_device_id thead_dwmac_match[] = {
255 { .compatible = "thead,th1520-gmac" },
256 { /* sentinel */ }
257 };
258 MODULE_DEVICE_TABLE(of, thead_dwmac_match);
259
260 static struct platform_driver thead_dwmac_driver = {
261 .probe = thead_dwmac_probe,
262 .driver = {
263 .name = "thead-dwmac",
264 .pm = &stmmac_pltfr_pm_ops,
265 .of_match_table = thead_dwmac_match,
266 },
267 };
268 module_platform_driver(thead_dwmac_driver);
269
270 MODULE_AUTHOR("Jisheng Zhang <[email protected]>");
271 MODULE_AUTHOR("Drew Fustini <[email protected]>");
272 MODULE_DESCRIPTION("T-HEAD DWMAC platform driver");
273 MODULE_LICENSE("GPL");
274