1 // SPDX-License-Identifier: GPL-2.0
3 * dwmac-ingenic.c - Ingenic SoCs DWMAC specific glue layer
5 * Copyright (c) 2021 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>
8 #include <linux/bitfield.h>
10 #include <linux/kernel.h>
11 #include <linux/mfd/syscon.h>
12 #include <linux/module.h>
14 #include <linux/of_device.h>
15 #include <linux/of_net.h>
16 #include <linux/phy.h>
17 #include <linux/platform_device.h>
18 #include <linux/regmap.h>
19 #include <linux/slab.h>
20 #include <linux/stmmac.h>
22 #include "stmmac_platform.h"
24 #define MACPHYC_TXCLK_SEL_MASK GENMASK(31, 31)
25 #define MACPHYC_TXCLK_SEL_OUTPUT 0x1
26 #define MACPHYC_TXCLK_SEL_INPUT 0x0
27 #define MACPHYC_MODE_SEL_MASK GENMASK(31, 31)
28 #define MACPHYC_MODE_SEL_RMII 0x0
29 #define MACPHYC_TX_SEL_MASK GENMASK(19, 19)
30 #define MACPHYC_TX_SEL_ORIGIN 0x0
31 #define MACPHYC_TX_SEL_DELAY 0x1
32 #define MACPHYC_TX_DELAY_MASK GENMASK(18, 12)
33 #define MACPHYC_RX_SEL_MASK GENMASK(11, 11)
34 #define MACPHYC_RX_SEL_ORIGIN 0x0
35 #define MACPHYC_RX_SEL_DELAY 0x1
36 #define MACPHYC_RX_DELAY_MASK GENMASK(10, 4)
37 #define MACPHYC_SOFT_RST_MASK GENMASK(3, 3)
38 #define MACPHYC_PHY_INFT_MASK GENMASK(2, 0)
39 #define MACPHYC_PHY_INFT_RMII 0x4
40 #define MACPHYC_PHY_INFT_RGMII 0x1
41 #define MACPHYC_PHY_INFT_GMII 0x0
42 #define MACPHYC_PHY_INFT_MII 0x0
44 #define MACPHYC_TX_DELAY_PS_MAX 2496
45 #define MACPHYC_TX_DELAY_PS_MIN 20
47 #define MACPHYC_RX_DELAY_PS_MAX 2496
48 #define MACPHYC_RX_DELAY_PS_MIN 20
50 enum ingenic_mac_version {
59 const struct ingenic_soc_info *soc_info;
61 struct regmap *regmap;
67 struct ingenic_soc_info {
68 enum ingenic_mac_version version;
71 int (*set_mode)(struct plat_stmmacenet_data *plat_dat);
74 static int ingenic_mac_init(struct plat_stmmacenet_data *plat_dat)
76 struct ingenic_mac *mac = plat_dat->bsp_priv;
79 if (mac->soc_info->set_mode) {
80 ret = mac->soc_info->set_mode(plat_dat);
88 static int jz4775_mac_set_mode(struct plat_stmmacenet_data *plat_dat)
90 struct ingenic_mac *mac = plat_dat->bsp_priv;
93 switch (plat_dat->interface) {
94 case PHY_INTERFACE_MODE_MII:
95 val = FIELD_PREP(MACPHYC_TXCLK_SEL_MASK, MACPHYC_TXCLK_SEL_INPUT) |
96 FIELD_PREP(MACPHYC_PHY_INFT_MASK, MACPHYC_PHY_INFT_MII);
97 dev_dbg(mac->dev, "MAC PHY Control Register: PHY_INTERFACE_MODE_MII\n");
100 case PHY_INTERFACE_MODE_GMII:
101 val = FIELD_PREP(MACPHYC_TXCLK_SEL_MASK, MACPHYC_TXCLK_SEL_INPUT) |
102 FIELD_PREP(MACPHYC_PHY_INFT_MASK, MACPHYC_PHY_INFT_GMII);
103 dev_dbg(mac->dev, "MAC PHY Control Register: PHY_INTERFACE_MODE_GMII\n");
106 case PHY_INTERFACE_MODE_RMII:
107 val = FIELD_PREP(MACPHYC_TXCLK_SEL_MASK, MACPHYC_TXCLK_SEL_INPUT) |
108 FIELD_PREP(MACPHYC_PHY_INFT_MASK, MACPHYC_PHY_INFT_RMII);
109 dev_dbg(mac->dev, "MAC PHY Control Register: PHY_INTERFACE_MODE_RMII\n");
112 case PHY_INTERFACE_MODE_RGMII:
113 case PHY_INTERFACE_MODE_RGMII_ID:
114 case PHY_INTERFACE_MODE_RGMII_TXID:
115 case PHY_INTERFACE_MODE_RGMII_RXID:
116 val = FIELD_PREP(MACPHYC_TXCLK_SEL_MASK, MACPHYC_TXCLK_SEL_INPUT) |
117 FIELD_PREP(MACPHYC_PHY_INFT_MASK, MACPHYC_PHY_INFT_RGMII);
118 dev_dbg(mac->dev, "MAC PHY Control Register: PHY_INTERFACE_MODE_RGMII\n");
122 dev_err(mac->dev, "Unsupported interface %d", plat_dat->interface);
126 /* Update MAC PHY control register */
127 return regmap_update_bits(mac->regmap, 0, mac->soc_info->mask, val);
130 static int x1000_mac_set_mode(struct plat_stmmacenet_data *plat_dat)
132 struct ingenic_mac *mac = plat_dat->bsp_priv;
134 switch (plat_dat->interface) {
135 case PHY_INTERFACE_MODE_RMII:
136 dev_dbg(mac->dev, "MAC PHY Control Register: PHY_INTERFACE_MODE_RMII\n");
140 dev_err(mac->dev, "Unsupported interface %d", plat_dat->interface);
144 /* Update MAC PHY control register */
145 return regmap_update_bits(mac->regmap, 0, mac->soc_info->mask, 0);
148 static int x1600_mac_set_mode(struct plat_stmmacenet_data *plat_dat)
150 struct ingenic_mac *mac = plat_dat->bsp_priv;
153 switch (plat_dat->interface) {
154 case PHY_INTERFACE_MODE_RMII:
155 val = FIELD_PREP(MACPHYC_PHY_INFT_MASK, MACPHYC_PHY_INFT_RMII);
156 dev_dbg(mac->dev, "MAC PHY Control Register: PHY_INTERFACE_MODE_RMII\n");
160 dev_err(mac->dev, "Unsupported interface %d", plat_dat->interface);
164 /* Update MAC PHY control register */
165 return regmap_update_bits(mac->regmap, 0, mac->soc_info->mask, val);
168 static int x1830_mac_set_mode(struct plat_stmmacenet_data *plat_dat)
170 struct ingenic_mac *mac = plat_dat->bsp_priv;
173 switch (plat_dat->interface) {
174 case PHY_INTERFACE_MODE_RMII:
175 val = FIELD_PREP(MACPHYC_MODE_SEL_MASK, MACPHYC_MODE_SEL_RMII) |
176 FIELD_PREP(MACPHYC_PHY_INFT_MASK, MACPHYC_PHY_INFT_RMII);
177 dev_dbg(mac->dev, "MAC PHY Control Register: PHY_INTERFACE_MODE_RMII\n");
181 dev_err(mac->dev, "Unsupported interface %d", plat_dat->interface);
185 /* Update MAC PHY control register */
186 return regmap_update_bits(mac->regmap, 0, mac->soc_info->mask, val);
189 static int x2000_mac_set_mode(struct plat_stmmacenet_data *plat_dat)
191 struct ingenic_mac *mac = plat_dat->bsp_priv;
194 switch (plat_dat->interface) {
195 case PHY_INTERFACE_MODE_RMII:
196 val = FIELD_PREP(MACPHYC_TX_SEL_MASK, MACPHYC_TX_SEL_ORIGIN) |
197 FIELD_PREP(MACPHYC_RX_SEL_MASK, MACPHYC_RX_SEL_ORIGIN) |
198 FIELD_PREP(MACPHYC_PHY_INFT_MASK, MACPHYC_PHY_INFT_RMII);
199 dev_dbg(mac->dev, "MAC PHY Control Register: PHY_INTERFACE_MODE_RMII\n");
202 case PHY_INTERFACE_MODE_RGMII:
203 case PHY_INTERFACE_MODE_RGMII_ID:
204 case PHY_INTERFACE_MODE_RGMII_TXID:
205 case PHY_INTERFACE_MODE_RGMII_RXID:
206 val = FIELD_PREP(MACPHYC_PHY_INFT_MASK, MACPHYC_PHY_INFT_RGMII);
208 if (mac->tx_delay == 0)
209 val |= FIELD_PREP(MACPHYC_TX_SEL_MASK, MACPHYC_TX_SEL_ORIGIN);
211 val |= FIELD_PREP(MACPHYC_TX_SEL_MASK, MACPHYC_TX_SEL_DELAY) |
212 FIELD_PREP(MACPHYC_TX_DELAY_MASK, (mac->tx_delay + 9750) / 19500 - 1);
214 if (mac->rx_delay == 0)
215 val |= FIELD_PREP(MACPHYC_RX_SEL_MASK, MACPHYC_RX_SEL_ORIGIN);
217 val |= FIELD_PREP(MACPHYC_RX_SEL_MASK, MACPHYC_RX_SEL_DELAY) |
218 FIELD_PREP(MACPHYC_RX_DELAY_MASK, (mac->rx_delay + 9750) / 19500 - 1);
220 dev_dbg(mac->dev, "MAC PHY Control Register: PHY_INTERFACE_MODE_RGMII\n");
224 dev_err(mac->dev, "Unsupported interface %d", plat_dat->interface);
228 /* Update MAC PHY control register */
229 return regmap_update_bits(mac->regmap, 0, mac->soc_info->mask, val);
232 static int ingenic_mac_probe(struct platform_device *pdev)
234 struct plat_stmmacenet_data *plat_dat;
235 struct stmmac_resources stmmac_res;
236 struct ingenic_mac *mac;
237 const struct ingenic_soc_info *data;
238 u32 tx_delay_ps, rx_delay_ps;
241 ret = stmmac_get_platform_resources(pdev, &stmmac_res);
245 plat_dat = stmmac_probe_config_dt(pdev, stmmac_res.mac);
246 if (IS_ERR(plat_dat))
247 return PTR_ERR(plat_dat);
249 mac = devm_kzalloc(&pdev->dev, sizeof(*mac), GFP_KERNEL);
252 goto err_remove_config_dt;
255 data = of_device_get_match_data(&pdev->dev);
257 dev_err(&pdev->dev, "No of match data provided\n");
259 goto err_remove_config_dt;
262 /* Get MAC PHY control register */
263 mac->regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "mode-reg");
264 if (IS_ERR(mac->regmap)) {
265 dev_err(&pdev->dev, "%s: Failed to get syscon regmap\n", __func__);
266 ret = PTR_ERR(mac->regmap);
267 goto err_remove_config_dt;
270 if (!of_property_read_u32(pdev->dev.of_node, "tx-clk-delay-ps", &tx_delay_ps)) {
271 if (tx_delay_ps >= MACPHYC_TX_DELAY_PS_MIN &&
272 tx_delay_ps <= MACPHYC_TX_DELAY_PS_MAX) {
273 mac->tx_delay = tx_delay_ps * 1000;
275 dev_err(&pdev->dev, "Invalid TX clock delay: %dps\n", tx_delay_ps);
277 goto err_remove_config_dt;
281 if (!of_property_read_u32(pdev->dev.of_node, "rx-clk-delay-ps", &rx_delay_ps)) {
282 if (rx_delay_ps >= MACPHYC_RX_DELAY_PS_MIN &&
283 rx_delay_ps <= MACPHYC_RX_DELAY_PS_MAX) {
284 mac->rx_delay = rx_delay_ps * 1000;
286 dev_err(&pdev->dev, "Invalid RX clock delay: %dps\n", rx_delay_ps);
288 goto err_remove_config_dt;
292 mac->soc_info = data;
293 mac->dev = &pdev->dev;
295 plat_dat->bsp_priv = mac;
297 ret = ingenic_mac_init(plat_dat);
299 goto err_remove_config_dt;
301 ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
303 goto err_remove_config_dt;
307 err_remove_config_dt:
308 stmmac_remove_config_dt(pdev, plat_dat);
313 #ifdef CONFIG_PM_SLEEP
314 static int ingenic_mac_suspend(struct device *dev)
318 ret = stmmac_suspend(dev);
323 static int ingenic_mac_resume(struct device *dev)
325 struct net_device *ndev = dev_get_drvdata(dev);
326 struct stmmac_priv *priv = netdev_priv(ndev);
329 ret = ingenic_mac_init(priv->plat);
333 ret = stmmac_resume(dev);
337 #endif /* CONFIG_PM_SLEEP */
339 static SIMPLE_DEV_PM_OPS(ingenic_mac_pm_ops, ingenic_mac_suspend, ingenic_mac_resume);
341 static struct ingenic_soc_info jz4775_soc_info = {
342 .version = ID_JZ4775,
343 .mask = MACPHYC_TXCLK_SEL_MASK | MACPHYC_SOFT_RST_MASK | MACPHYC_PHY_INFT_MASK,
345 .set_mode = jz4775_mac_set_mode,
348 static struct ingenic_soc_info x1000_soc_info = {
350 .mask = MACPHYC_SOFT_RST_MASK,
352 .set_mode = x1000_mac_set_mode,
355 static struct ingenic_soc_info x1600_soc_info = {
357 .mask = MACPHYC_SOFT_RST_MASK | MACPHYC_PHY_INFT_MASK,
359 .set_mode = x1600_mac_set_mode,
362 static struct ingenic_soc_info x1830_soc_info = {
364 .mask = MACPHYC_MODE_SEL_MASK | MACPHYC_SOFT_RST_MASK | MACPHYC_PHY_INFT_MASK,
366 .set_mode = x1830_mac_set_mode,
369 static struct ingenic_soc_info x2000_soc_info = {
371 .mask = MACPHYC_TX_SEL_MASK | MACPHYC_TX_DELAY_MASK | MACPHYC_RX_SEL_MASK |
372 MACPHYC_RX_DELAY_MASK | MACPHYC_SOFT_RST_MASK | MACPHYC_PHY_INFT_MASK,
374 .set_mode = x2000_mac_set_mode,
377 static const struct of_device_id ingenic_mac_of_matches[] = {
378 { .compatible = "ingenic,jz4775-mac", .data = &jz4775_soc_info },
379 { .compatible = "ingenic,x1000-mac", .data = &x1000_soc_info },
380 { .compatible = "ingenic,x1600-mac", .data = &x1600_soc_info },
381 { .compatible = "ingenic,x1830-mac", .data = &x1830_soc_info },
382 { .compatible = "ingenic,x2000-mac", .data = &x2000_soc_info },
385 MODULE_DEVICE_TABLE(of, ingenic_mac_of_matches);
387 static struct platform_driver ingenic_mac_driver = {
388 .probe = ingenic_mac_probe,
389 .remove = stmmac_pltfr_remove,
391 .name = "ingenic-mac",
392 .pm = pm_ptr(&ingenic_mac_pm_ops),
393 .of_match_table = ingenic_mac_of_matches,
396 module_platform_driver(ingenic_mac_driver);
398 MODULE_AUTHOR("周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>");
399 MODULE_DESCRIPTION("Ingenic SoCs DWMAC specific glue layer");
400 MODULE_LICENSE("GPL v2");