1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * dwmac-sunxi.c - Allwinner sunxi DWMAC specific glue layer
5 * Copyright (C) 2013 Chen-Yu Tsai
7 * Chen-Yu Tsai <wens@csie.org>
10 #include <linux/stmmac.h>
11 #include <linux/clk.h>
12 #include <linux/module.h>
13 #include <linux/phy.h>
14 #include <linux/platform_device.h>
15 #include <linux/of_net.h>
16 #include <linux/regulator/consumer.h>
18 #include "stmmac_platform.h"
20 struct sunxi_priv_data
{
21 phy_interface_t interface
;
24 struct regulator
*regulator
;
27 #define SUN7I_GMAC_GMII_RGMII_RATE 125000000
28 #define SUN7I_GMAC_MII_RATE 25000000
30 static int sun7i_gmac_init(struct platform_device
*pdev
, void *priv
)
32 struct sunxi_priv_data
*gmac
= priv
;
35 if (gmac
->regulator
) {
36 ret
= regulator_enable(gmac
->regulator
);
41 /* Set GMAC interface port mode
43 * The GMAC TX clock lines are configured by setting the clock
44 * rate, which then uses the auto-reparenting feature of the
45 * clock driver, and enabling/disabling the clock.
47 if (phy_interface_mode_is_rgmii(gmac
->interface
)) {
48 clk_set_rate(gmac
->tx_clk
, SUN7I_GMAC_GMII_RGMII_RATE
);
49 clk_prepare_enable(gmac
->tx_clk
);
50 gmac
->clk_enabled
= 1;
52 clk_set_rate(gmac
->tx_clk
, SUN7I_GMAC_MII_RATE
);
53 ret
= clk_prepare(gmac
->tx_clk
);
61 static void sun7i_gmac_exit(struct platform_device
*pdev
, void *priv
)
63 struct sunxi_priv_data
*gmac
= priv
;
65 if (gmac
->clk_enabled
) {
66 clk_disable(gmac
->tx_clk
);
67 gmac
->clk_enabled
= 0;
69 clk_unprepare(gmac
->tx_clk
);
72 regulator_disable(gmac
->regulator
);
75 static void sun7i_fix_speed(void *priv
, unsigned int speed
)
77 struct sunxi_priv_data
*gmac
= priv
;
79 /* only GMII mode requires us to reconfigure the clock lines */
80 if (gmac
->interface
!= PHY_INTERFACE_MODE_GMII
)
83 if (gmac
->clk_enabled
) {
84 clk_disable(gmac
->tx_clk
);
85 gmac
->clk_enabled
= 0;
87 clk_unprepare(gmac
->tx_clk
);
90 clk_set_rate(gmac
->tx_clk
, SUN7I_GMAC_GMII_RGMII_RATE
);
91 clk_prepare_enable(gmac
->tx_clk
);
92 gmac
->clk_enabled
= 1;
94 clk_set_rate(gmac
->tx_clk
, SUN7I_GMAC_MII_RATE
);
95 clk_prepare(gmac
->tx_clk
);
99 static int sun7i_gmac_probe(struct platform_device
*pdev
)
101 struct plat_stmmacenet_data
*plat_dat
;
102 struct stmmac_resources stmmac_res
;
103 struct sunxi_priv_data
*gmac
;
104 struct device
*dev
= &pdev
->dev
;
107 ret
= stmmac_get_platform_resources(pdev
, &stmmac_res
);
111 plat_dat
= stmmac_probe_config_dt(pdev
, &stmmac_res
.mac
);
112 if (IS_ERR(plat_dat
))
113 return PTR_ERR(plat_dat
);
115 gmac
= devm_kzalloc(dev
, sizeof(*gmac
), GFP_KERNEL
);
118 goto err_remove_config_dt
;
121 ret
= of_get_phy_mode(dev
->of_node
, &gmac
->interface
);
122 if (ret
&& ret
!= -ENODEV
) {
123 dev_err(dev
, "Can't get phy-mode\n");
124 goto err_remove_config_dt
;
127 gmac
->tx_clk
= devm_clk_get(dev
, "allwinner_gmac_tx");
128 if (IS_ERR(gmac
->tx_clk
)) {
129 dev_err(dev
, "could not get tx clock\n");
130 ret
= PTR_ERR(gmac
->tx_clk
);
131 goto err_remove_config_dt
;
134 /* Optional regulator for PHY */
135 gmac
->regulator
= devm_regulator_get_optional(dev
, "phy");
136 if (IS_ERR(gmac
->regulator
)) {
137 if (PTR_ERR(gmac
->regulator
) == -EPROBE_DEFER
) {
139 goto err_remove_config_dt
;
141 dev_info(dev
, "no regulator found\n");
142 gmac
->regulator
= NULL
;
145 /* platform data specifying hardware features and callbacks.
146 * hardware features were copied from Allwinner drivers. */
147 plat_dat
->tx_coe
= 1;
148 plat_dat
->has_gmac
= true;
149 plat_dat
->bsp_priv
= gmac
;
150 plat_dat
->init
= sun7i_gmac_init
;
151 plat_dat
->exit
= sun7i_gmac_exit
;
152 plat_dat
->fix_mac_speed
= sun7i_fix_speed
;
153 plat_dat
->tx_fifo_size
= 4096;
154 plat_dat
->rx_fifo_size
= 16384;
156 ret
= sun7i_gmac_init(pdev
, plat_dat
->bsp_priv
);
158 goto err_remove_config_dt
;
160 ret
= stmmac_dvr_probe(&pdev
->dev
, plat_dat
, &stmmac_res
);
167 sun7i_gmac_exit(pdev
, plat_dat
->bsp_priv
);
168 err_remove_config_dt
:
169 stmmac_remove_config_dt(pdev
, plat_dat
);
174 static const struct of_device_id sun7i_dwmac_match
[] = {
175 { .compatible
= "allwinner,sun7i-a20-gmac" },
178 MODULE_DEVICE_TABLE(of
, sun7i_dwmac_match
);
180 static struct platform_driver sun7i_dwmac_driver
= {
181 .probe
= sun7i_gmac_probe
,
182 .remove
= stmmac_pltfr_remove
,
184 .name
= "sun7i-dwmac",
185 .pm
= &stmmac_pltfr_pm_ops
,
186 .of_match_table
= sun7i_dwmac_match
,
189 module_platform_driver(sun7i_dwmac_driver
);
191 MODULE_AUTHOR("Chen-Yu Tsai <wens@csie.org>");
192 MODULE_DESCRIPTION("Allwinner sunxi DWMAC specific glue layer");
193 MODULE_LICENSE("GPL");