xref: /linux/drivers/net/ethernet/stmicro/stmmac/dwmac-thead.c (revision 7f71507851fc7764b36a3221839607d3a45c2025)
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 #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 
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 
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 
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 
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 
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 
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 <jszhang@kernel.org>");
271 MODULE_AUTHOR("Drew Fustini <drew@pdp7.com>");
272 MODULE_DESCRIPTION("T-HEAD DWMAC platform driver");
273 MODULE_LICENSE("GPL");
274