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