xref: /linux/drivers/net/ethernet/stmicro/stmmac/dwmac-thead.c (revision 1a9239bb4253f9076b5b4b2a1a4e8d7defd77a95)
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;
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 		writel(GMAC_GTXCLK_SEL_PLL, dwmac->apb_base + GMAC_GTXCLK_SEL);
168 		reg = GMAC_TX_CLK_EN | GMAC_TX_CLK_N_EN | GMAC_TX_CLK_OUT_EN |
169 		      GMAC_RX_CLK_EN | GMAC_RX_CLK_N_EN;
170 		break;
171 
172 	default:
173 		dev_err(dwmac->dev, "unsupported phy interface %d\n",
174 			plat->mac_interface);
175 		return -EINVAL;
176 	}
177 
178 	writel(reg, dwmac->apb_base + GMAC_CLK_EN);
179 	return 0;
180 }
181 
thead_dwmac_init(struct platform_device * pdev,void * priv)182 static int thead_dwmac_init(struct platform_device *pdev, void *priv)
183 {
184 	struct thead_dwmac *dwmac = priv;
185 	unsigned int reg;
186 	int ret;
187 
188 	ret = thead_dwmac_set_phy_if(dwmac->plat);
189 	if (ret)
190 		return ret;
191 
192 	ret = thead_dwmac_set_txclk_dir(dwmac->plat);
193 	if (ret)
194 		return ret;
195 
196 	reg = readl(dwmac->apb_base + GMAC_RXCLK_DELAY_CTRL);
197 	reg &= ~(GMAC_RXCLK_DELAY);
198 	reg |= FIELD_PREP(GMAC_RXCLK_DELAY, 0);
199 	writel(reg, dwmac->apb_base + GMAC_RXCLK_DELAY_CTRL);
200 
201 	reg = readl(dwmac->apb_base + GMAC_TXCLK_DELAY_CTRL);
202 	reg &= ~(GMAC_TXCLK_DELAY);
203 	reg |= FIELD_PREP(GMAC_TXCLK_DELAY, 0);
204 	writel(reg, dwmac->apb_base + GMAC_TXCLK_DELAY_CTRL);
205 
206 	return thead_dwmac_enable_clk(dwmac->plat);
207 }
208 
thead_dwmac_probe(struct platform_device * pdev)209 static int thead_dwmac_probe(struct platform_device *pdev)
210 {
211 	struct stmmac_resources stmmac_res;
212 	struct plat_stmmacenet_data *plat;
213 	struct thead_dwmac *dwmac;
214 	void __iomem *apb;
215 	int ret;
216 
217 	ret = stmmac_get_platform_resources(pdev, &stmmac_res);
218 	if (ret)
219 		return dev_err_probe(&pdev->dev, ret,
220 				     "failed to get resources\n");
221 
222 	plat = devm_stmmac_probe_config_dt(pdev, stmmac_res.mac);
223 	if (IS_ERR(plat))
224 		return dev_err_probe(&pdev->dev, PTR_ERR(plat),
225 				     "dt configuration failed\n");
226 
227 	dwmac = devm_kzalloc(&pdev->dev, sizeof(*dwmac), GFP_KERNEL);
228 	if (!dwmac)
229 		return -ENOMEM;
230 
231 	apb = devm_platform_ioremap_resource(pdev, 1);
232 	if (IS_ERR(apb))
233 		return dev_err_probe(&pdev->dev, PTR_ERR(apb),
234 				     "failed to remap gmac apb registers\n");
235 
236 	dwmac->dev = &pdev->dev;
237 	dwmac->plat = plat;
238 	dwmac->apb_base = apb;
239 	plat->bsp_priv = dwmac;
240 	plat->set_clk_tx_rate = thead_set_clk_tx_rate;
241 	plat->init = thead_dwmac_init;
242 
243 	return devm_stmmac_pltfr_probe(pdev, plat, &stmmac_res);
244 }
245 
246 static const struct of_device_id thead_dwmac_match[] = {
247 	{ .compatible = "thead,th1520-gmac" },
248 	{ /* sentinel */ }
249 };
250 MODULE_DEVICE_TABLE(of, thead_dwmac_match);
251 
252 static struct platform_driver thead_dwmac_driver = {
253 	.probe = thead_dwmac_probe,
254 	.driver = {
255 		.name = "thead-dwmac",
256 		.pm = &stmmac_pltfr_pm_ops,
257 		.of_match_table = thead_dwmac_match,
258 	},
259 };
260 module_platform_driver(thead_dwmac_driver);
261 
262 MODULE_AUTHOR("Jisheng Zhang <jszhang@kernel.org>");
263 MODULE_AUTHOR("Drew Fustini <drew@pdp7.com>");
264 MODULE_DESCRIPTION("T-HEAD DWMAC platform driver");
265 MODULE_LICENSE("GPL");
266