Merge tag 'linux-kselftest-4.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel...
[firefly-linux-kernel-4.4.55.git] / drivers / net / ethernet / stmicro / stmmac / dwmac-sunxi.c
1 /*
2  * dwmac-sunxi.c - Allwinner sunxi DWMAC specific glue layer
3  *
4  * Copyright (C) 2013 Chen-Yu Tsai
5  *
6  * Chen-Yu Tsai  <wens@csie.org>
7  *
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.
12  *
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.
17  */
18
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>
26
27 #include "stmmac_platform.h"
28
29 struct sunxi_priv_data {
30         int interface;
31         int clk_enabled;
32         struct clk *tx_clk;
33         struct regulator *regulator;
34 };
35
36 static void *sun7i_gmac_setup(struct platform_device *pdev)
37 {
38         struct sunxi_priv_data *gmac;
39         struct device *dev = &pdev->dev;
40
41         gmac = devm_kzalloc(dev, sizeof(*gmac), GFP_KERNEL);
42         if (!gmac)
43                 return ERR_PTR(-ENOMEM);
44
45         gmac->interface = of_get_phy_mode(dev->of_node);
46
47         gmac->tx_clk = devm_clk_get(dev, "allwinner_gmac_tx");
48         if (IS_ERR(gmac->tx_clk)) {
49                 dev_err(dev, "could not get tx clock\n");
50                 return gmac->tx_clk;
51         }
52
53         /* Optional regulator for PHY */
54         gmac->regulator = devm_regulator_get_optional(dev, "phy");
55         if (IS_ERR(gmac->regulator)) {
56                 if (PTR_ERR(gmac->regulator) == -EPROBE_DEFER)
57                         return ERR_PTR(-EPROBE_DEFER);
58                 dev_info(dev, "no regulator found\n");
59                 gmac->regulator = NULL;
60         }
61
62         return gmac;
63 }
64
65 #define SUN7I_GMAC_GMII_RGMII_RATE      125000000
66 #define SUN7I_GMAC_MII_RATE             25000000
67
68 static int sun7i_gmac_init(struct platform_device *pdev, void *priv)
69 {
70         struct sunxi_priv_data *gmac = priv;
71         int ret;
72
73         if (gmac->regulator) {
74                 ret = regulator_enable(gmac->regulator);
75                 if (ret)
76                         return ret;
77         }
78
79         /* Set GMAC interface port mode
80          *
81          * The GMAC TX clock lines are configured by setting the clock
82          * rate, which then uses the auto-reparenting feature of the
83          * clock driver, and enabling/disabling the clock.
84          */
85         if (gmac->interface == PHY_INTERFACE_MODE_RGMII) {
86                 clk_set_rate(gmac->tx_clk, SUN7I_GMAC_GMII_RGMII_RATE);
87                 clk_prepare_enable(gmac->tx_clk);
88                 gmac->clk_enabled = 1;
89         } else {
90                 clk_set_rate(gmac->tx_clk, SUN7I_GMAC_MII_RATE);
91                 clk_prepare(gmac->tx_clk);
92         }
93
94         return 0;
95 }
96
97 static void sun7i_gmac_exit(struct platform_device *pdev, void *priv)
98 {
99         struct sunxi_priv_data *gmac = priv;
100
101         if (gmac->clk_enabled) {
102                 clk_disable(gmac->tx_clk);
103                 gmac->clk_enabled = 0;
104         }
105         clk_unprepare(gmac->tx_clk);
106
107         if (gmac->regulator)
108                 regulator_disable(gmac->regulator);
109 }
110
111 static void sun7i_fix_speed(void *priv, unsigned int speed)
112 {
113         struct sunxi_priv_data *gmac = priv;
114
115         /* only GMII mode requires us to reconfigure the clock lines */
116         if (gmac->interface != PHY_INTERFACE_MODE_GMII)
117                 return;
118
119         if (gmac->clk_enabled) {
120                 clk_disable(gmac->tx_clk);
121                 gmac->clk_enabled = 0;
122         }
123         clk_unprepare(gmac->tx_clk);
124
125         if (speed == 1000) {
126                 clk_set_rate(gmac->tx_clk, SUN7I_GMAC_GMII_RGMII_RATE);
127                 clk_prepare_enable(gmac->tx_clk);
128                 gmac->clk_enabled = 1;
129         } else {
130                 clk_set_rate(gmac->tx_clk, SUN7I_GMAC_MII_RATE);
131                 clk_prepare(gmac->tx_clk);
132         }
133 }
134
135 /* of_data specifying hardware features and callbacks.
136  * hardware features were copied from Allwinner drivers. */
137 static const struct stmmac_of_data sun7i_gmac_data = {
138         .has_gmac = 1,
139         .tx_coe = 1,
140         .fix_mac_speed = sun7i_fix_speed,
141         .setup = sun7i_gmac_setup,
142         .init = sun7i_gmac_init,
143         .exit = sun7i_gmac_exit,
144 };
145
146 static const struct of_device_id sun7i_dwmac_match[] = {
147         { .compatible = "allwinner,sun7i-a20-gmac", .data = &sun7i_gmac_data},
148         { }
149 };
150 MODULE_DEVICE_TABLE(of, sun7i_dwmac_match);
151
152 static struct platform_driver sun7i_dwmac_driver = {
153         .probe  = stmmac_pltfr_probe,
154         .remove = stmmac_pltfr_remove,
155         .driver = {
156                 .name           = "sun7i-dwmac",
157                 .pm             = &stmmac_pltfr_pm_ops,
158                 .of_match_table = sun7i_dwmac_match,
159         },
160 };
161 module_platform_driver(sun7i_dwmac_driver);
162
163 MODULE_AUTHOR("Chen-Yu Tsai <wens@csie.org>");
164 MODULE_DESCRIPTION("Allwinner sunxi DWMAC specific glue layer");
165 MODULE_LICENSE("GPL");