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 *
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 struct thead_dwmac {
49 struct plat_stmmacenet_data *plat;
50 void __iomem *apb_base;
51 struct device *dev;
52 };
53
thead_dwmac_set_phy_if(struct plat_stmmacenet_data * plat)54 static int thead_dwmac_set_phy_if(struct plat_stmmacenet_data *plat)
55 {
56 struct thead_dwmac *dwmac = plat->bsp_priv;
57 u32 phyif;
58
59 switch (plat->mac_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 dev_err(dwmac->dev, "unsupported phy interface %d\n",
71 plat->mac_interface);
72 return -EINVAL;
73 }
74
75 writel(phyif, dwmac->apb_base + GMAC_INTF_CTRL);
76 return 0;
77 }
78
thead_dwmac_set_txclk_dir(struct plat_stmmacenet_data * plat)79 static int thead_dwmac_set_txclk_dir(struct plat_stmmacenet_data *plat)
80 {
81 struct thead_dwmac *dwmac = plat->bsp_priv;
82 u32 txclk_dir;
83
84 switch (plat->mac_interface) {
85 case PHY_INTERFACE_MODE_MII:
86 txclk_dir = TXCLK_DIR_INPUT;
87 break;
88 case PHY_INTERFACE_MODE_RGMII:
89 case PHY_INTERFACE_MODE_RGMII_ID:
90 case PHY_INTERFACE_MODE_RGMII_TXID:
91 case PHY_INTERFACE_MODE_RGMII_RXID:
92 txclk_dir = TXCLK_DIR_OUTPUT;
93 break;
94 default:
95 dev_err(dwmac->dev, "unsupported phy interface %d\n",
96 plat->mac_interface);
97 return -EINVAL;
98 }
99
100 writel(txclk_dir, dwmac->apb_base + GMAC_TXCLK_OEN);
101 return 0;
102 }
103
thead_set_clk_tx_rate(void * bsp_priv,struct clk * clk_tx_i,phy_interface_t interface,int speed)104 static int thead_set_clk_tx_rate(void *bsp_priv, struct clk *clk_tx_i,
105 phy_interface_t interface, int speed)
106 {
107 struct thead_dwmac *dwmac = bsp_priv;
108 struct plat_stmmacenet_data *plat;
109 unsigned long rate;
110 long tx_rate;
111 u32 div, reg;
112
113 plat = dwmac->plat;
114
115 switch (plat->mac_interface) {
116 /* For MII, rxc/txc is provided by phy */
117 case PHY_INTERFACE_MODE_MII:
118 return 0;
119
120 case PHY_INTERFACE_MODE_RGMII:
121 case PHY_INTERFACE_MODE_RGMII_ID:
122 case PHY_INTERFACE_MODE_RGMII_RXID:
123 case PHY_INTERFACE_MODE_RGMII_TXID:
124 rate = clk_get_rate(plat->stmmac_clk);
125
126 writel(0, dwmac->apb_base + GMAC_PLLCLK_DIV);
127
128 tx_rate = rgmii_clock(speed);
129 if (tx_rate < 0) {
130 dev_err(dwmac->dev, "invalid speed %d\n", speed);
131 return tx_rate;
132 }
133
134 div = rate / tx_rate;
135 if (rate != tx_rate * div) {
136 dev_err(dwmac->dev, "invalid gmac rate %lu\n", rate);
137 return -EINVAL;
138 }
139
140 reg = FIELD_PREP(GMAC_PLLCLK_DIV_EN, 1) |
141 FIELD_PREP(GMAC_PLLCLK_DIV_NUM, div);
142 writel(reg, dwmac->apb_base + GMAC_PLLCLK_DIV);
143 return 0;
144
145 default:
146 dev_err(dwmac->dev, "unsupported phy interface %d\n",
147 plat->mac_interface);
148 return -EINVAL;
149 }
150 }
151
thead_dwmac_enable_clk(struct plat_stmmacenet_data * plat)152 static int thead_dwmac_enable_clk(struct plat_stmmacenet_data *plat)
153 {
154 struct thead_dwmac *dwmac = plat->bsp_priv;
155 u32 reg, div;
156
157 switch (plat->mac_interface) {
158 case PHY_INTERFACE_MODE_MII:
159 reg = GMAC_RX_CLK_EN | GMAC_TX_CLK_EN;
160 break;
161
162 case PHY_INTERFACE_MODE_RGMII:
163 case PHY_INTERFACE_MODE_RGMII_ID:
164 case PHY_INTERFACE_MODE_RGMII_RXID:
165 case PHY_INTERFACE_MODE_RGMII_TXID:
166 /* use pll */
167 div = clk_get_rate(plat->stmmac_clk) / rgmii_clock(SPEED_1000);
168 reg = FIELD_PREP(GMAC_PLLCLK_DIV_EN, 1) |
169 FIELD_PREP(GMAC_PLLCLK_DIV_NUM, div);
170
171 writel(0, dwmac->apb_base + GMAC_PLLCLK_DIV);
172 writel(reg, dwmac->apb_base + GMAC_PLLCLK_DIV);
173
174 writel(GMAC_GTXCLK_SEL_PLL, dwmac->apb_base + GMAC_GTXCLK_SEL);
175 reg = GMAC_TX_CLK_EN | GMAC_TX_CLK_N_EN | GMAC_TX_CLK_OUT_EN |
176 GMAC_RX_CLK_EN | GMAC_RX_CLK_N_EN;
177 break;
178
179 default:
180 dev_err(dwmac->dev, "unsupported phy interface %d\n",
181 plat->mac_interface);
182 return -EINVAL;
183 }
184
185 writel(reg, dwmac->apb_base + GMAC_CLK_EN);
186 return 0;
187 }
188
thead_dwmac_init(struct platform_device * pdev,void * priv)189 static int thead_dwmac_init(struct platform_device *pdev, void *priv)
190 {
191 struct thead_dwmac *dwmac = priv;
192 unsigned int reg;
193 int ret;
194
195 ret = thead_dwmac_set_phy_if(dwmac->plat);
196 if (ret)
197 return ret;
198
199 ret = thead_dwmac_set_txclk_dir(dwmac->plat);
200 if (ret)
201 return ret;
202
203 reg = readl(dwmac->apb_base + GMAC_RXCLK_DELAY_CTRL);
204 reg &= ~(GMAC_RXCLK_DELAY);
205 reg |= FIELD_PREP(GMAC_RXCLK_DELAY, 0);
206 writel(reg, dwmac->apb_base + GMAC_RXCLK_DELAY_CTRL);
207
208 reg = readl(dwmac->apb_base + GMAC_TXCLK_DELAY_CTRL);
209 reg &= ~(GMAC_TXCLK_DELAY);
210 reg |= FIELD_PREP(GMAC_TXCLK_DELAY, 0);
211 writel(reg, dwmac->apb_base + GMAC_TXCLK_DELAY_CTRL);
212
213 return thead_dwmac_enable_clk(dwmac->plat);
214 }
215
thead_dwmac_probe(struct platform_device * pdev)216 static int thead_dwmac_probe(struct platform_device *pdev)
217 {
218 struct stmmac_resources stmmac_res;
219 struct plat_stmmacenet_data *plat;
220 struct thead_dwmac *dwmac;
221 struct clk *apb_clk;
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 /*
236 * The APB clock is essential for accessing glue registers. However,
237 * old devicetrees don't describe it correctly. We continue to probe
238 * and emit a warning if it isn't present.
239 */
240 apb_clk = devm_clk_get_enabled(&pdev->dev, "apb");
241 if (PTR_ERR(apb_clk) == -ENOENT)
242 dev_warn(&pdev->dev,
243 "cannot get apb clock, link may break after speed changes\n");
244 else if (IS_ERR(apb_clk))
245 return dev_err_probe(&pdev->dev, PTR_ERR(apb_clk),
246 "failed to get apb clock\n");
247
248 dwmac = devm_kzalloc(&pdev->dev, sizeof(*dwmac), GFP_KERNEL);
249 if (!dwmac)
250 return -ENOMEM;
251
252 apb = devm_platform_ioremap_resource(pdev, 1);
253 if (IS_ERR(apb))
254 return dev_err_probe(&pdev->dev, PTR_ERR(apb),
255 "failed to remap gmac apb registers\n");
256
257 dwmac->dev = &pdev->dev;
258 dwmac->plat = plat;
259 dwmac->apb_base = apb;
260 plat->bsp_priv = dwmac;
261 plat->set_clk_tx_rate = thead_set_clk_tx_rate;
262 plat->init = thead_dwmac_init;
263
264 return devm_stmmac_pltfr_probe(pdev, plat, &stmmac_res);
265 }
266
267 static const struct of_device_id thead_dwmac_match[] = {
268 { .compatible = "thead,th1520-gmac" },
269 { /* sentinel */ }
270 };
271 MODULE_DEVICE_TABLE(of, thead_dwmac_match);
272
273 static struct platform_driver thead_dwmac_driver = {
274 .probe = thead_dwmac_probe,
275 .driver = {
276 .name = "thead-dwmac",
277 .pm = &stmmac_pltfr_pm_ops,
278 .of_match_table = thead_dwmac_match,
279 },
280 };
281 module_platform_driver(thead_dwmac_driver);
282
283 MODULE_AUTHOR("Jisheng Zhang <jszhang@kernel.org>");
284 MODULE_AUTHOR("Drew Fustini <drew@pdp7.com>");
285 MODULE_DESCRIPTION("T-HEAD DWMAC platform driver");
286 MODULE_LICENSE("GPL");
287