]>
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> | |
4198b7db | 21 | #include <linux/module.h> |
af0bd4e9 | 22 | #include <linux/phy.h> |
4198b7db | 23 | #include <linux/platform_device.h> |
af0bd4e9 CYT |
24 | #include <linux/of_net.h> |
25 | #include <linux/regulator/consumer.h> | |
26 | ||
f10f9fb2 AS |
27 | #include "stmmac_platform.h" |
28 | ||
af0bd4e9 CYT |
29 | struct sunxi_priv_data { |
30 | int interface; | |
31 | int clk_enabled; | |
32 | struct clk *tx_clk; | |
33 | struct regulator *regulator; | |
34 | }; | |
35 | ||
af0bd4e9 CYT |
36 | #define SUN7I_GMAC_GMII_RGMII_RATE 125000000 |
37 | #define SUN7I_GMAC_MII_RATE 25000000 | |
38 | ||
39 | static int sun7i_gmac_init(struct platform_device *pdev, void *priv) | |
40 | { | |
41 | struct sunxi_priv_data *gmac = priv; | |
42 | int ret; | |
43 | ||
44 | if (gmac->regulator) { | |
45 | ret = regulator_enable(gmac->regulator); | |
46 | if (ret) | |
47 | return ret; | |
48 | } | |
49 | ||
50 | /* Set GMAC interface port mode | |
51 | * | |
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. | |
55 | */ | |
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; | |
60 | } else { | |
61 | clk_set_rate(gmac->tx_clk, SUN7I_GMAC_MII_RATE); | |
62 | clk_prepare(gmac->tx_clk); | |
63 | } | |
64 | ||
65 | return 0; | |
66 | } | |
67 | ||
68 | static void sun7i_gmac_exit(struct platform_device *pdev, void *priv) | |
69 | { | |
70 | struct sunxi_priv_data *gmac = priv; | |
71 | ||
72 | if (gmac->clk_enabled) { | |
73 | clk_disable(gmac->tx_clk); | |
74 | gmac->clk_enabled = 0; | |
75 | } | |
76 | clk_unprepare(gmac->tx_clk); | |
77 | ||
78 | if (gmac->regulator) | |
79 | regulator_disable(gmac->regulator); | |
80 | } | |
81 | ||
82 | static void sun7i_fix_speed(void *priv, unsigned int speed) | |
83 | { | |
84 | struct sunxi_priv_data *gmac = priv; | |
85 | ||
86 | /* only GMII mode requires us to reconfigure the clock lines */ | |
87 | if (gmac->interface != PHY_INTERFACE_MODE_GMII) | |
88 | return; | |
89 | ||
90 | if (gmac->clk_enabled) { | |
91 | clk_disable(gmac->tx_clk); | |
92 | gmac->clk_enabled = 0; | |
93 | } | |
94 | clk_unprepare(gmac->tx_clk); | |
95 | ||
96 | if (speed == 1000) { | |
97 | clk_set_rate(gmac->tx_clk, SUN7I_GMAC_GMII_RGMII_RATE); | |
98 | clk_prepare_enable(gmac->tx_clk); | |
99 | gmac->clk_enabled = 1; | |
100 | } else { | |
101 | clk_set_rate(gmac->tx_clk, SUN7I_GMAC_MII_RATE); | |
102 | clk_prepare(gmac->tx_clk); | |
103 | } | |
104 | } | |
105 | ||
9a9e9a1e | 106 | static int sun7i_gmac_probe(struct platform_device *pdev) |
22caae03 | 107 | { |
9a9e9a1e JE |
108 | struct plat_stmmacenet_data *plat_dat; |
109 | struct stmmac_resources stmmac_res; | |
22caae03 JE |
110 | struct sunxi_priv_data *gmac; |
111 | struct device *dev = &pdev->dev; | |
9a9e9a1e JE |
112 | int ret; |
113 | ||
114 | ret = stmmac_get_platform_resources(pdev, &stmmac_res); | |
115 | if (ret) | |
116 | return ret; | |
117 | ||
118 | plat_dat = stmmac_probe_config_dt(pdev, &stmmac_res.mac); | |
119 | if (IS_ERR(plat_dat)) | |
120 | return PTR_ERR(plat_dat); | |
22caae03 JE |
121 | |
122 | gmac = devm_kzalloc(dev, sizeof(*gmac), GFP_KERNEL); | |
123 | if (!gmac) | |
9a9e9a1e | 124 | return -ENOMEM; |
22caae03 JE |
125 | |
126 | gmac->interface = of_get_phy_mode(dev->of_node); | |
127 | ||
128 | gmac->tx_clk = devm_clk_get(dev, "allwinner_gmac_tx"); | |
129 | if (IS_ERR(gmac->tx_clk)) { | |
130 | dev_err(dev, "could not get tx clock\n"); | |
9a9e9a1e | 131 | return PTR_ERR(gmac->tx_clk); |
22caae03 JE |
132 | } |
133 | ||
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) | |
9a9e9a1e | 138 | return -EPROBE_DEFER; |
22caae03 JE |
139 | dev_info(dev, "no regulator found\n"); |
140 | gmac->regulator = NULL; | |
141 | } | |
142 | ||
9a9e9a1e JE |
143 | /* platform data specifying hardware features and callbacks. |
144 | * hardware features were copied from Allwinner drivers. */ | |
145 | plat_dat->tx_coe = 1; | |
146 | plat_dat->has_gmac = true; | |
147 | plat_dat->bsp_priv = gmac; | |
148 | plat_dat->init = sun7i_gmac_init; | |
149 | plat_dat->exit = sun7i_gmac_exit; | |
150 | plat_dat->fix_mac_speed = sun7i_fix_speed; | |
22caae03 | 151 | |
9a9e9a1e JE |
152 | ret = sun7i_gmac_init(pdev, plat_dat->bsp_priv); |
153 | if (ret) | |
154 | return ret; | |
155 | ||
d856c16d CYT |
156 | ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); |
157 | if (ret) | |
158 | sun7i_gmac_exit(pdev, plat_dat->bsp_priv); | |
159 | ||
160 | return ret; | |
9a9e9a1e | 161 | } |
4198b7db JE |
162 | |
163 | static const struct of_device_id sun7i_dwmac_match[] = { | |
9a9e9a1e | 164 | { .compatible = "allwinner,sun7i-a20-gmac" }, |
4198b7db JE |
165 | { } |
166 | }; | |
167 | MODULE_DEVICE_TABLE(of, sun7i_dwmac_match); | |
168 | ||
169 | static struct platform_driver sun7i_dwmac_driver = { | |
9a9e9a1e | 170 | .probe = sun7i_gmac_probe, |
4198b7db JE |
171 | .remove = stmmac_pltfr_remove, |
172 | .driver = { | |
173 | .name = "sun7i-dwmac", | |
174 | .pm = &stmmac_pltfr_pm_ops, | |
175 | .of_match_table = sun7i_dwmac_match, | |
176 | }, | |
177 | }; | |
178 | module_platform_driver(sun7i_dwmac_driver); | |
179 | ||
180 | MODULE_AUTHOR("Chen-Yu Tsai <wens@csie.org>"); | |
181 | MODULE_DESCRIPTION("Allwinner sunxi DWMAC specific glue layer"); | |
182 | MODULE_LICENSE("GPL"); |