xref: /linux/drivers/net/ethernet/stmicro/stmmac/dwmac-thead.c (revision 6439a0e64c355d2e375bd094f365d56ce81faba3)
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