]>
Commit | Line | Data |
---|---|---|
732fdf0e | 1 | /* |
af0bd4e9 CYT |
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/phy.h> | |
22 | #include <linux/of_net.h> | |
23 | #include <linux/regulator/consumer.h> | |
24 | ||
f10f9fb2 AS |
25 | #include "stmmac_platform.h" |
26 | ||
af0bd4e9 CYT |
27 | struct sunxi_priv_data { |
28 | int interface; | |
29 | int clk_enabled; | |
30 | struct clk *tx_clk; | |
31 | struct regulator *regulator; | |
32 | }; | |
33 | ||
34 | static void *sun7i_gmac_setup(struct platform_device *pdev) | |
35 | { | |
36 | struct sunxi_priv_data *gmac; | |
37 | struct device *dev = &pdev->dev; | |
38 | ||
39 | gmac = devm_kzalloc(dev, sizeof(*gmac), GFP_KERNEL); | |
40 | if (!gmac) | |
41 | return ERR_PTR(-ENOMEM); | |
42 | ||
43 | gmac->interface = of_get_phy_mode(dev->of_node); | |
44 | ||
45 | gmac->tx_clk = devm_clk_get(dev, "allwinner_gmac_tx"); | |
46 | if (IS_ERR(gmac->tx_clk)) { | |
47 | dev_err(dev, "could not get tx clock\n"); | |
48 | return gmac->tx_clk; | |
49 | } | |
50 | ||
51 | /* Optional regulator for PHY */ | |
52 | gmac->regulator = devm_regulator_get_optional(dev, "phy"); | |
53 | if (IS_ERR(gmac->regulator)) { | |
54 | if (PTR_ERR(gmac->regulator) == -EPROBE_DEFER) | |
55 | return ERR_PTR(-EPROBE_DEFER); | |
56 | dev_info(dev, "no regulator found\n"); | |
57 | gmac->regulator = NULL; | |
58 | } | |
59 | ||
60 | return gmac; | |
61 | } | |
62 | ||
63 | #define SUN7I_GMAC_GMII_RGMII_RATE 125000000 | |
64 | #define SUN7I_GMAC_MII_RATE 25000000 | |
65 | ||
66 | static int sun7i_gmac_init(struct platform_device *pdev, void *priv) | |
67 | { | |
68 | struct sunxi_priv_data *gmac = priv; | |
69 | int ret; | |
70 | ||
71 | if (gmac->regulator) { | |
72 | ret = regulator_enable(gmac->regulator); | |
73 | if (ret) | |
74 | return ret; | |
75 | } | |
76 | ||
77 | /* Set GMAC interface port mode | |
78 | * | |
79 | * The GMAC TX clock lines are configured by setting the clock | |
80 | * rate, which then uses the auto-reparenting feature of the | |
81 | * clock driver, and enabling/disabling the clock. | |
82 | */ | |
83 | if (gmac->interface == PHY_INTERFACE_MODE_RGMII) { | |
84 | clk_set_rate(gmac->tx_clk, SUN7I_GMAC_GMII_RGMII_RATE); | |
85 | clk_prepare_enable(gmac->tx_clk); | |
86 | gmac->clk_enabled = 1; | |
87 | } else { | |
88 | clk_set_rate(gmac->tx_clk, SUN7I_GMAC_MII_RATE); | |
89 | clk_prepare(gmac->tx_clk); | |
90 | } | |
91 | ||
92 | return 0; | |
93 | } | |
94 | ||
95 | static void sun7i_gmac_exit(struct platform_device *pdev, void *priv) | |
96 | { | |
97 | struct sunxi_priv_data *gmac = priv; | |
98 | ||
99 | if (gmac->clk_enabled) { | |
100 | clk_disable(gmac->tx_clk); | |
101 | gmac->clk_enabled = 0; | |
102 | } | |
103 | clk_unprepare(gmac->tx_clk); | |
104 | ||
105 | if (gmac->regulator) | |
106 | regulator_disable(gmac->regulator); | |
107 | } | |
108 | ||
109 | static void sun7i_fix_speed(void *priv, unsigned int speed) | |
110 | { | |
111 | struct sunxi_priv_data *gmac = priv; | |
112 | ||
113 | /* only GMII mode requires us to reconfigure the clock lines */ | |
114 | if (gmac->interface != PHY_INTERFACE_MODE_GMII) | |
115 | return; | |
116 | ||
117 | if (gmac->clk_enabled) { | |
118 | clk_disable(gmac->tx_clk); | |
119 | gmac->clk_enabled = 0; | |
120 | } | |
121 | clk_unprepare(gmac->tx_clk); | |
122 | ||
123 | if (speed == 1000) { | |
124 | clk_set_rate(gmac->tx_clk, SUN7I_GMAC_GMII_RGMII_RATE); | |
125 | clk_prepare_enable(gmac->tx_clk); | |
126 | gmac->clk_enabled = 1; | |
127 | } else { | |
128 | clk_set_rate(gmac->tx_clk, SUN7I_GMAC_MII_RATE); | |
129 | clk_prepare(gmac->tx_clk); | |
130 | } | |
131 | } | |
132 | ||
133 | /* of_data specifying hardware features and callbacks. | |
134 | * hardware features were copied from Allwinner drivers. */ | |
135 | const struct stmmac_of_data sun7i_gmac_data = { | |
136 | .has_gmac = 1, | |
137 | .tx_coe = 1, | |
138 | .fix_mac_speed = sun7i_fix_speed, | |
139 | .setup = sun7i_gmac_setup, | |
140 | .init = sun7i_gmac_init, | |
141 | .exit = sun7i_gmac_exit, | |
142 | }; |