xref: /OK3568_Linux_fs/kernel/drivers/net/ethernet/stmicro/stmmac/dwmac-sunxi.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * dwmac-sunxi.c - Allwinner sunxi DWMAC specific glue layer
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Copyright (C) 2013 Chen-Yu Tsai
6*4882a593Smuzhiyun  *
7*4882a593Smuzhiyun  * Chen-Yu Tsai  <wens@csie.org>
8*4882a593Smuzhiyun  */
9*4882a593Smuzhiyun 
10*4882a593Smuzhiyun #include <linux/stmmac.h>
11*4882a593Smuzhiyun #include <linux/clk.h>
12*4882a593Smuzhiyun #include <linux/module.h>
13*4882a593Smuzhiyun #include <linux/phy.h>
14*4882a593Smuzhiyun #include <linux/platform_device.h>
15*4882a593Smuzhiyun #include <linux/of_net.h>
16*4882a593Smuzhiyun #include <linux/regulator/consumer.h>
17*4882a593Smuzhiyun 
18*4882a593Smuzhiyun #include "stmmac_platform.h"
19*4882a593Smuzhiyun 
20*4882a593Smuzhiyun struct sunxi_priv_data {
21*4882a593Smuzhiyun 	phy_interface_t interface;
22*4882a593Smuzhiyun 	int clk_enabled;
23*4882a593Smuzhiyun 	struct clk *tx_clk;
24*4882a593Smuzhiyun 	struct regulator *regulator;
25*4882a593Smuzhiyun };
26*4882a593Smuzhiyun 
27*4882a593Smuzhiyun #define SUN7I_GMAC_GMII_RGMII_RATE	125000000
28*4882a593Smuzhiyun #define SUN7I_GMAC_MII_RATE		25000000
29*4882a593Smuzhiyun 
sun7i_gmac_init(struct platform_device * pdev,void * priv)30*4882a593Smuzhiyun static int sun7i_gmac_init(struct platform_device *pdev, void *priv)
31*4882a593Smuzhiyun {
32*4882a593Smuzhiyun 	struct sunxi_priv_data *gmac = priv;
33*4882a593Smuzhiyun 	int ret = 0;
34*4882a593Smuzhiyun 
35*4882a593Smuzhiyun 	if (gmac->regulator) {
36*4882a593Smuzhiyun 		ret = regulator_enable(gmac->regulator);
37*4882a593Smuzhiyun 		if (ret)
38*4882a593Smuzhiyun 			return ret;
39*4882a593Smuzhiyun 	}
40*4882a593Smuzhiyun 
41*4882a593Smuzhiyun 	/* Set GMAC interface port mode
42*4882a593Smuzhiyun 	 *
43*4882a593Smuzhiyun 	 * The GMAC TX clock lines are configured by setting the clock
44*4882a593Smuzhiyun 	 * rate, which then uses the auto-reparenting feature of the
45*4882a593Smuzhiyun 	 * clock driver, and enabling/disabling the clock.
46*4882a593Smuzhiyun 	 */
47*4882a593Smuzhiyun 	if (phy_interface_mode_is_rgmii(gmac->interface)) {
48*4882a593Smuzhiyun 		clk_set_rate(gmac->tx_clk, SUN7I_GMAC_GMII_RGMII_RATE);
49*4882a593Smuzhiyun 		clk_prepare_enable(gmac->tx_clk);
50*4882a593Smuzhiyun 		gmac->clk_enabled = 1;
51*4882a593Smuzhiyun 	} else {
52*4882a593Smuzhiyun 		clk_set_rate(gmac->tx_clk, SUN7I_GMAC_MII_RATE);
53*4882a593Smuzhiyun 		ret = clk_prepare(gmac->tx_clk);
54*4882a593Smuzhiyun 		if (ret && gmac->regulator)
55*4882a593Smuzhiyun 			regulator_disable(gmac->regulator);
56*4882a593Smuzhiyun 	}
57*4882a593Smuzhiyun 
58*4882a593Smuzhiyun 	return ret;
59*4882a593Smuzhiyun }
60*4882a593Smuzhiyun 
sun7i_gmac_exit(struct platform_device * pdev,void * priv)61*4882a593Smuzhiyun static void sun7i_gmac_exit(struct platform_device *pdev, void *priv)
62*4882a593Smuzhiyun {
63*4882a593Smuzhiyun 	struct sunxi_priv_data *gmac = priv;
64*4882a593Smuzhiyun 
65*4882a593Smuzhiyun 	if (gmac->clk_enabled) {
66*4882a593Smuzhiyun 		clk_disable(gmac->tx_clk);
67*4882a593Smuzhiyun 		gmac->clk_enabled = 0;
68*4882a593Smuzhiyun 	}
69*4882a593Smuzhiyun 	clk_unprepare(gmac->tx_clk);
70*4882a593Smuzhiyun 
71*4882a593Smuzhiyun 	if (gmac->regulator)
72*4882a593Smuzhiyun 		regulator_disable(gmac->regulator);
73*4882a593Smuzhiyun }
74*4882a593Smuzhiyun 
sun7i_fix_speed(void * priv,unsigned int speed)75*4882a593Smuzhiyun static void sun7i_fix_speed(void *priv, unsigned int speed)
76*4882a593Smuzhiyun {
77*4882a593Smuzhiyun 	struct sunxi_priv_data *gmac = priv;
78*4882a593Smuzhiyun 
79*4882a593Smuzhiyun 	/* only GMII mode requires us to reconfigure the clock lines */
80*4882a593Smuzhiyun 	if (gmac->interface != PHY_INTERFACE_MODE_GMII)
81*4882a593Smuzhiyun 		return;
82*4882a593Smuzhiyun 
83*4882a593Smuzhiyun 	if (gmac->clk_enabled) {
84*4882a593Smuzhiyun 		clk_disable(gmac->tx_clk);
85*4882a593Smuzhiyun 		gmac->clk_enabled = 0;
86*4882a593Smuzhiyun 	}
87*4882a593Smuzhiyun 	clk_unprepare(gmac->tx_clk);
88*4882a593Smuzhiyun 
89*4882a593Smuzhiyun 	if (speed == 1000) {
90*4882a593Smuzhiyun 		clk_set_rate(gmac->tx_clk, SUN7I_GMAC_GMII_RGMII_RATE);
91*4882a593Smuzhiyun 		clk_prepare_enable(gmac->tx_clk);
92*4882a593Smuzhiyun 		gmac->clk_enabled = 1;
93*4882a593Smuzhiyun 	} else {
94*4882a593Smuzhiyun 		clk_set_rate(gmac->tx_clk, SUN7I_GMAC_MII_RATE);
95*4882a593Smuzhiyun 		clk_prepare(gmac->tx_clk);
96*4882a593Smuzhiyun 	}
97*4882a593Smuzhiyun }
98*4882a593Smuzhiyun 
sun7i_gmac_probe(struct platform_device * pdev)99*4882a593Smuzhiyun static int sun7i_gmac_probe(struct platform_device *pdev)
100*4882a593Smuzhiyun {
101*4882a593Smuzhiyun 	struct plat_stmmacenet_data *plat_dat;
102*4882a593Smuzhiyun 	struct stmmac_resources stmmac_res;
103*4882a593Smuzhiyun 	struct sunxi_priv_data *gmac;
104*4882a593Smuzhiyun 	struct device *dev = &pdev->dev;
105*4882a593Smuzhiyun 	int ret;
106*4882a593Smuzhiyun 
107*4882a593Smuzhiyun 	ret = stmmac_get_platform_resources(pdev, &stmmac_res);
108*4882a593Smuzhiyun 	if (ret)
109*4882a593Smuzhiyun 		return ret;
110*4882a593Smuzhiyun 
111*4882a593Smuzhiyun 	plat_dat = stmmac_probe_config_dt(pdev, &stmmac_res.mac);
112*4882a593Smuzhiyun 	if (IS_ERR(plat_dat))
113*4882a593Smuzhiyun 		return PTR_ERR(plat_dat);
114*4882a593Smuzhiyun 
115*4882a593Smuzhiyun 	gmac = devm_kzalloc(dev, sizeof(*gmac), GFP_KERNEL);
116*4882a593Smuzhiyun 	if (!gmac) {
117*4882a593Smuzhiyun 		ret = -ENOMEM;
118*4882a593Smuzhiyun 		goto err_remove_config_dt;
119*4882a593Smuzhiyun 	}
120*4882a593Smuzhiyun 
121*4882a593Smuzhiyun 	ret = of_get_phy_mode(dev->of_node, &gmac->interface);
122*4882a593Smuzhiyun 	if (ret && ret != -ENODEV) {
123*4882a593Smuzhiyun 		dev_err(dev, "Can't get phy-mode\n");
124*4882a593Smuzhiyun 		goto err_remove_config_dt;
125*4882a593Smuzhiyun 	}
126*4882a593Smuzhiyun 
127*4882a593Smuzhiyun 	gmac->tx_clk = devm_clk_get(dev, "allwinner_gmac_tx");
128*4882a593Smuzhiyun 	if (IS_ERR(gmac->tx_clk)) {
129*4882a593Smuzhiyun 		dev_err(dev, "could not get tx clock\n");
130*4882a593Smuzhiyun 		ret = PTR_ERR(gmac->tx_clk);
131*4882a593Smuzhiyun 		goto err_remove_config_dt;
132*4882a593Smuzhiyun 	}
133*4882a593Smuzhiyun 
134*4882a593Smuzhiyun 	/* Optional regulator for PHY */
135*4882a593Smuzhiyun 	gmac->regulator = devm_regulator_get_optional(dev, "phy");
136*4882a593Smuzhiyun 	if (IS_ERR(gmac->regulator)) {
137*4882a593Smuzhiyun 		if (PTR_ERR(gmac->regulator) == -EPROBE_DEFER) {
138*4882a593Smuzhiyun 			ret = -EPROBE_DEFER;
139*4882a593Smuzhiyun 			goto err_remove_config_dt;
140*4882a593Smuzhiyun 		}
141*4882a593Smuzhiyun 		dev_info(dev, "no regulator found\n");
142*4882a593Smuzhiyun 		gmac->regulator = NULL;
143*4882a593Smuzhiyun 	}
144*4882a593Smuzhiyun 
145*4882a593Smuzhiyun 	/* platform data specifying hardware features and callbacks.
146*4882a593Smuzhiyun 	 * hardware features were copied from Allwinner drivers. */
147*4882a593Smuzhiyun 	plat_dat->tx_coe = 1;
148*4882a593Smuzhiyun 	plat_dat->has_gmac = true;
149*4882a593Smuzhiyun 	plat_dat->bsp_priv = gmac;
150*4882a593Smuzhiyun 	plat_dat->init = sun7i_gmac_init;
151*4882a593Smuzhiyun 	plat_dat->exit = sun7i_gmac_exit;
152*4882a593Smuzhiyun 	plat_dat->fix_mac_speed = sun7i_fix_speed;
153*4882a593Smuzhiyun 	plat_dat->tx_fifo_size = 4096;
154*4882a593Smuzhiyun 	plat_dat->rx_fifo_size = 16384;
155*4882a593Smuzhiyun 
156*4882a593Smuzhiyun 	ret = sun7i_gmac_init(pdev, plat_dat->bsp_priv);
157*4882a593Smuzhiyun 	if (ret)
158*4882a593Smuzhiyun 		goto err_remove_config_dt;
159*4882a593Smuzhiyun 
160*4882a593Smuzhiyun 	ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
161*4882a593Smuzhiyun 	if (ret)
162*4882a593Smuzhiyun 		goto err_gmac_exit;
163*4882a593Smuzhiyun 
164*4882a593Smuzhiyun 	return 0;
165*4882a593Smuzhiyun 
166*4882a593Smuzhiyun err_gmac_exit:
167*4882a593Smuzhiyun 	sun7i_gmac_exit(pdev, plat_dat->bsp_priv);
168*4882a593Smuzhiyun err_remove_config_dt:
169*4882a593Smuzhiyun 	stmmac_remove_config_dt(pdev, plat_dat);
170*4882a593Smuzhiyun 
171*4882a593Smuzhiyun 	return ret;
172*4882a593Smuzhiyun }
173*4882a593Smuzhiyun 
174*4882a593Smuzhiyun static const struct of_device_id sun7i_dwmac_match[] = {
175*4882a593Smuzhiyun 	{ .compatible = "allwinner,sun7i-a20-gmac" },
176*4882a593Smuzhiyun 	{ }
177*4882a593Smuzhiyun };
178*4882a593Smuzhiyun MODULE_DEVICE_TABLE(of, sun7i_dwmac_match);
179*4882a593Smuzhiyun 
180*4882a593Smuzhiyun static struct platform_driver sun7i_dwmac_driver = {
181*4882a593Smuzhiyun 	.probe  = sun7i_gmac_probe,
182*4882a593Smuzhiyun 	.remove = stmmac_pltfr_remove,
183*4882a593Smuzhiyun 	.driver = {
184*4882a593Smuzhiyun 		.name           = "sun7i-dwmac",
185*4882a593Smuzhiyun 		.pm		= &stmmac_pltfr_pm_ops,
186*4882a593Smuzhiyun 		.of_match_table = sun7i_dwmac_match,
187*4882a593Smuzhiyun 	},
188*4882a593Smuzhiyun };
189*4882a593Smuzhiyun module_platform_driver(sun7i_dwmac_driver);
190*4882a593Smuzhiyun 
191*4882a593Smuzhiyun MODULE_AUTHOR("Chen-Yu Tsai <wens@csie.org>");
192*4882a593Smuzhiyun MODULE_DESCRIPTION("Allwinner sunxi DWMAC specific glue layer");
193*4882a593Smuzhiyun MODULE_LICENSE("GPL");
194