2 * dwmac-sunxi.c - Allwinner sunxi DWMAC specific glue layer
4 * Copyright (C) 2013 Chen-Yu Tsai
6 * Chen-Yu Tsai <wens@csie.org>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
19 #include <linux/stmmac.h>
20 #include <linux/clk.h>
21 #include <linux/module.h>
22 #include <linux/phy.h>
23 #include <linux/platform_device.h>
24 #include <linux/of_net.h>
25 #include <linux/regulator/consumer.h>
27 #include "stmmac_platform.h"
29 struct sunxi_priv_data
{
33 struct regulator
*regulator
;
36 #define SUN7I_GMAC_GMII_RGMII_RATE 125000000
37 #define SUN7I_GMAC_MII_RATE 25000000
39 static int sun7i_gmac_init(struct platform_device
*pdev
, void *priv
)
41 struct sunxi_priv_data
*gmac
= priv
;
44 if (gmac
->regulator
) {
45 ret
= regulator_enable(gmac
->regulator
);
50 /* Set GMAC interface port mode
52 * The GMAC TX clock lines are configured by setting the clock
53 * rate, which then uses the auto-reparenting feature of the
54 * clock driver, and enabling/disabling the clock.
56 if (gmac
->interface
== PHY_INTERFACE_MODE_RGMII
) {
57 clk_set_rate(gmac
->tx_clk
, SUN7I_GMAC_GMII_RGMII_RATE
);
58 clk_prepare_enable(gmac
->tx_clk
);
59 gmac
->clk_enabled
= 1;
61 clk_set_rate(gmac
->tx_clk
, SUN7I_GMAC_MII_RATE
);
62 clk_prepare(gmac
->tx_clk
);
68 static void sun7i_gmac_exit(struct platform_device
*pdev
, void *priv
)
70 struct sunxi_priv_data
*gmac
= priv
;
72 if (gmac
->clk_enabled
) {
73 clk_disable(gmac
->tx_clk
);
74 gmac
->clk_enabled
= 0;
76 clk_unprepare(gmac
->tx_clk
);
79 regulator_disable(gmac
->regulator
);
82 static void sun7i_fix_speed(void *priv
, unsigned int speed
)
84 struct sunxi_priv_data
*gmac
= priv
;
86 /* only GMII mode requires us to reconfigure the clock lines */
87 if (gmac
->interface
!= PHY_INTERFACE_MODE_GMII
)
90 if (gmac
->clk_enabled
) {
91 clk_disable(gmac
->tx_clk
);
92 gmac
->clk_enabled
= 0;
94 clk_unprepare(gmac
->tx_clk
);
97 clk_set_rate(gmac
->tx_clk
, SUN7I_GMAC_GMII_RGMII_RATE
);
98 clk_prepare_enable(gmac
->tx_clk
);
99 gmac
->clk_enabled
= 1;
101 clk_set_rate(gmac
->tx_clk
, SUN7I_GMAC_MII_RATE
);
102 clk_prepare(gmac
->tx_clk
);
106 static int sun7i_gmac_probe(struct platform_device
*pdev
)
108 struct plat_stmmacenet_data
*plat_dat
;
109 struct stmmac_resources stmmac_res
;
110 struct sunxi_priv_data
*gmac
;
111 struct device
*dev
= &pdev
->dev
;
114 ret
= stmmac_get_platform_resources(pdev
, &stmmac_res
);
118 plat_dat
= stmmac_probe_config_dt(pdev
, &stmmac_res
.mac
);
119 if (IS_ERR(plat_dat
))
120 return PTR_ERR(plat_dat
);
122 gmac
= devm_kzalloc(dev
, sizeof(*gmac
), GFP_KERNEL
);
125 goto err_remove_config_dt
;
128 gmac
->interface
= of_get_phy_mode(dev
->of_node
);
130 gmac
->tx_clk
= devm_clk_get(dev
, "allwinner_gmac_tx");
131 if (IS_ERR(gmac
->tx_clk
)) {
132 dev_err(dev
, "could not get tx clock\n");
133 ret
= PTR_ERR(gmac
->tx_clk
);
134 goto err_remove_config_dt
;
137 /* Optional regulator for PHY */
138 gmac
->regulator
= devm_regulator_get_optional(dev
, "phy");
139 if (IS_ERR(gmac
->regulator
)) {
140 if (PTR_ERR(gmac
->regulator
) == -EPROBE_DEFER
) {
142 goto err_remove_config_dt
;
144 dev_info(dev
, "no regulator found\n");
145 gmac
->regulator
= NULL
;
148 /* platform data specifying hardware features and callbacks.
149 * hardware features were copied from Allwinner drivers. */
150 plat_dat
->tx_coe
= 1;
151 plat_dat
->has_gmac
= true;
152 plat_dat
->bsp_priv
= gmac
;
153 plat_dat
->init
= sun7i_gmac_init
;
154 plat_dat
->exit
= sun7i_gmac_exit
;
155 plat_dat
->fix_mac_speed
= sun7i_fix_speed
;
157 ret
= sun7i_gmac_init(pdev
, plat_dat
->bsp_priv
);
159 goto err_remove_config_dt
;
161 ret
= stmmac_dvr_probe(&pdev
->dev
, plat_dat
, &stmmac_res
);
168 sun7i_gmac_exit(pdev
, plat_dat
->bsp_priv
);
169 err_remove_config_dt
:
170 stmmac_remove_config_dt(pdev
, plat_dat
);
175 static const struct of_device_id sun7i_dwmac_match
[] = {
176 { .compatible
= "allwinner,sun7i-a20-gmac" },
179 MODULE_DEVICE_TABLE(of
, sun7i_dwmac_match
);
181 static struct platform_driver sun7i_dwmac_driver
= {
182 .probe
= sun7i_gmac_probe
,
183 .remove
= stmmac_pltfr_remove
,
185 .name
= "sun7i-dwmac",
186 .pm
= &stmmac_pltfr_pm_ops
,
187 .of_match_table
= sun7i_dwmac_match
,
190 module_platform_driver(sun7i_dwmac_driver
);
192 MODULE_AUTHOR("Chen-Yu Tsai <wens@csie.org>");
193 MODULE_DESCRIPTION("Allwinner sunxi DWMAC specific glue layer");
194 MODULE_LICENSE("GPL");