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