1*4bd3bb7bSSamin Guo // SPDX-License-Identifier: GPL-2.0+
2*4bd3bb7bSSamin Guo /*
3*4bd3bb7bSSamin Guo  * StarFive DWMAC platform driver
4*4bd3bb7bSSamin Guo  *
5*4bd3bb7bSSamin Guo  * Copyright (C) 2021 Emil Renner Berthing <kernel@esmil.dk>
6*4bd3bb7bSSamin Guo  * Copyright (C) 2022 StarFive Technology Co., Ltd.
7*4bd3bb7bSSamin Guo  *
8*4bd3bb7bSSamin Guo  */
9*4bd3bb7bSSamin Guo 
10*4bd3bb7bSSamin Guo #include <linux/mfd/syscon.h>
11*4bd3bb7bSSamin Guo #include <linux/of_device.h>
12*4bd3bb7bSSamin Guo #include <linux/regmap.h>
13*4bd3bb7bSSamin Guo 
14*4bd3bb7bSSamin Guo #include "stmmac_platform.h"
15*4bd3bb7bSSamin Guo 
16*4bd3bb7bSSamin Guo struct starfive_dwmac {
17*4bd3bb7bSSamin Guo 	struct device *dev;
18*4bd3bb7bSSamin Guo 	struct clk *clk_tx;
19*4bd3bb7bSSamin Guo };
20*4bd3bb7bSSamin Guo 
21*4bd3bb7bSSamin Guo static void starfive_dwmac_fix_mac_speed(void *priv, unsigned int speed)
22*4bd3bb7bSSamin Guo {
23*4bd3bb7bSSamin Guo 	struct starfive_dwmac *dwmac = priv;
24*4bd3bb7bSSamin Guo 	unsigned long rate;
25*4bd3bb7bSSamin Guo 	int err;
26*4bd3bb7bSSamin Guo 
27*4bd3bb7bSSamin Guo 	rate = clk_get_rate(dwmac->clk_tx);
28*4bd3bb7bSSamin Guo 
29*4bd3bb7bSSamin Guo 	switch (speed) {
30*4bd3bb7bSSamin Guo 	case SPEED_1000:
31*4bd3bb7bSSamin Guo 		rate = 125000000;
32*4bd3bb7bSSamin Guo 		break;
33*4bd3bb7bSSamin Guo 	case SPEED_100:
34*4bd3bb7bSSamin Guo 		rate = 25000000;
35*4bd3bb7bSSamin Guo 		break;
36*4bd3bb7bSSamin Guo 	case SPEED_10:
37*4bd3bb7bSSamin Guo 		rate = 2500000;
38*4bd3bb7bSSamin Guo 		break;
39*4bd3bb7bSSamin Guo 	default:
40*4bd3bb7bSSamin Guo 		dev_err(dwmac->dev, "invalid speed %u\n", speed);
41*4bd3bb7bSSamin Guo 		break;
42*4bd3bb7bSSamin Guo 	}
43*4bd3bb7bSSamin Guo 
44*4bd3bb7bSSamin Guo 	err = clk_set_rate(dwmac->clk_tx, rate);
45*4bd3bb7bSSamin Guo 	if (err)
46*4bd3bb7bSSamin Guo 		dev_err(dwmac->dev, "failed to set tx rate %lu\n", rate);
47*4bd3bb7bSSamin Guo }
48*4bd3bb7bSSamin Guo 
49*4bd3bb7bSSamin Guo static int starfive_dwmac_probe(struct platform_device *pdev)
50*4bd3bb7bSSamin Guo {
51*4bd3bb7bSSamin Guo 	struct plat_stmmacenet_data *plat_dat;
52*4bd3bb7bSSamin Guo 	struct stmmac_resources stmmac_res;
53*4bd3bb7bSSamin Guo 	struct starfive_dwmac *dwmac;
54*4bd3bb7bSSamin Guo 	struct clk *clk_gtx;
55*4bd3bb7bSSamin Guo 	int err;
56*4bd3bb7bSSamin Guo 
57*4bd3bb7bSSamin Guo 	err = stmmac_get_platform_resources(pdev, &stmmac_res);
58*4bd3bb7bSSamin Guo 	if (err)
59*4bd3bb7bSSamin Guo 		return dev_err_probe(&pdev->dev, err,
60*4bd3bb7bSSamin Guo 				     "failed to get resources\n");
61*4bd3bb7bSSamin Guo 
62*4bd3bb7bSSamin Guo 	plat_dat = stmmac_probe_config_dt(pdev, stmmac_res.mac);
63*4bd3bb7bSSamin Guo 	if (IS_ERR(plat_dat))
64*4bd3bb7bSSamin Guo 		return dev_err_probe(&pdev->dev, PTR_ERR(plat_dat),
65*4bd3bb7bSSamin Guo 				     "dt configuration failed\n");
66*4bd3bb7bSSamin Guo 
67*4bd3bb7bSSamin Guo 	dwmac = devm_kzalloc(&pdev->dev, sizeof(*dwmac), GFP_KERNEL);
68*4bd3bb7bSSamin Guo 	if (!dwmac)
69*4bd3bb7bSSamin Guo 		return -ENOMEM;
70*4bd3bb7bSSamin Guo 
71*4bd3bb7bSSamin Guo 	dwmac->clk_tx = devm_clk_get_enabled(&pdev->dev, "tx");
72*4bd3bb7bSSamin Guo 	if (IS_ERR(dwmac->clk_tx))
73*4bd3bb7bSSamin Guo 		return dev_err_probe(&pdev->dev, PTR_ERR(dwmac->clk_tx),
74*4bd3bb7bSSamin Guo 				     "error getting tx clock\n");
75*4bd3bb7bSSamin Guo 
76*4bd3bb7bSSamin Guo 	clk_gtx = devm_clk_get_enabled(&pdev->dev, "gtx");
77*4bd3bb7bSSamin Guo 	if (IS_ERR(clk_gtx))
78*4bd3bb7bSSamin Guo 		return dev_err_probe(&pdev->dev, PTR_ERR(clk_gtx),
79*4bd3bb7bSSamin Guo 				     "error getting gtx clock\n");
80*4bd3bb7bSSamin Guo 
81*4bd3bb7bSSamin Guo 	/* Generally, the rgmii_tx clock is provided by the internal clock,
82*4bd3bb7bSSamin Guo 	 * which needs to match the corresponding clock frequency according
83*4bd3bb7bSSamin Guo 	 * to different speeds. If the rgmii_tx clock is provided by the
84*4bd3bb7bSSamin Guo 	 * external rgmii_rxin, there is no need to configure the clock
85*4bd3bb7bSSamin Guo 	 * internally, because rgmii_rxin will be adaptively adjusted.
86*4bd3bb7bSSamin Guo 	 */
87*4bd3bb7bSSamin Guo 	if (!device_property_read_bool(&pdev->dev, "starfive,tx-use-rgmii-clk"))
88*4bd3bb7bSSamin Guo 		plat_dat->fix_mac_speed = starfive_dwmac_fix_mac_speed;
89*4bd3bb7bSSamin Guo 
90*4bd3bb7bSSamin Guo 	dwmac->dev = &pdev->dev;
91*4bd3bb7bSSamin Guo 	plat_dat->bsp_priv = dwmac;
92*4bd3bb7bSSamin Guo 	plat_dat->dma_cfg->dche = true;
93*4bd3bb7bSSamin Guo 
94*4bd3bb7bSSamin Guo 	err = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
95*4bd3bb7bSSamin Guo 	if (err) {
96*4bd3bb7bSSamin Guo 		stmmac_remove_config_dt(pdev, plat_dat);
97*4bd3bb7bSSamin Guo 		return err;
98*4bd3bb7bSSamin Guo 	}
99*4bd3bb7bSSamin Guo 
100*4bd3bb7bSSamin Guo 	return 0;
101*4bd3bb7bSSamin Guo }
102*4bd3bb7bSSamin Guo 
103*4bd3bb7bSSamin Guo static const struct of_device_id starfive_dwmac_match[] = {
104*4bd3bb7bSSamin Guo 	{ .compatible = "starfive,jh7110-dwmac"	},
105*4bd3bb7bSSamin Guo 	{ /* sentinel */ }
106*4bd3bb7bSSamin Guo };
107*4bd3bb7bSSamin Guo MODULE_DEVICE_TABLE(of, starfive_dwmac_match);
108*4bd3bb7bSSamin Guo 
109*4bd3bb7bSSamin Guo static struct platform_driver starfive_dwmac_driver = {
110*4bd3bb7bSSamin Guo 	.probe  = starfive_dwmac_probe,
111*4bd3bb7bSSamin Guo 	.remove = stmmac_pltfr_remove,
112*4bd3bb7bSSamin Guo 	.driver = {
113*4bd3bb7bSSamin Guo 		.name = "starfive-dwmac",
114*4bd3bb7bSSamin Guo 		.pm = &stmmac_pltfr_pm_ops,
115*4bd3bb7bSSamin Guo 		.of_match_table = starfive_dwmac_match,
116*4bd3bb7bSSamin Guo 	},
117*4bd3bb7bSSamin Guo };
118*4bd3bb7bSSamin Guo module_platform_driver(starfive_dwmac_driver);
119*4bd3bb7bSSamin Guo 
120*4bd3bb7bSSamin Guo MODULE_LICENSE("GPL");
121*4bd3bb7bSSamin Guo MODULE_DESCRIPTION("StarFive DWMAC platform driver");
122*4bd3bb7bSSamin Guo MODULE_AUTHOR("Emil Renner Berthing <kernel@esmil.dk>");
123*4bd3bb7bSSamin Guo MODULE_AUTHOR("Samin Guo <samin.guo@starfivetech.com>");
124