diff options
author | Tien Hock Loh <thloh@altera.com> | 2016-07-07 23:23:30 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2016-07-11 01:07:50 -0400 |
commit | fb3bbdb859891e6bc27fd1afb3a07319f82c2ee4 (patch) | |
tree | 199877544d3ac266c473bcf49a8495d19cf46c66 /drivers/net/ethernet/stmicro | |
parent | 927265bc6cd6374c9bafc43408ece4e92311b149 (diff) |
net: ethernet: Add TSE PCS support to dwmac-socfpga
This adds support for TSE PCS that uses SGMII adapter when the phy-mode of
the dwmac is set to sgmii.
Signed-off-by: Tien Hock Loh <thloh@altera.com>
Acked-by: Rob Herring <robh@kernel.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/ethernet/stmicro')
-rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/Makefile | 3 | ||||
-rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/altr_tse_pcs.c | 274 | ||||
-rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/altr_tse_pcs.h | 36 | ||||
-rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c | 140 |
4 files changed, 431 insertions, 22 deletions
diff --git a/drivers/net/ethernet/stmicro/stmmac/Makefile b/drivers/net/ethernet/stmicro/stmmac/Makefile index 0fb362d5a722..44b630cd1755 100644 --- a/drivers/net/ethernet/stmicro/stmmac/Makefile +++ b/drivers/net/ethernet/stmicro/stmmac/Makefile | |||
@@ -11,11 +11,12 @@ obj-$(CONFIG_DWMAC_IPQ806X) += dwmac-ipq806x.o | |||
11 | obj-$(CONFIG_DWMAC_LPC18XX) += dwmac-lpc18xx.o | 11 | obj-$(CONFIG_DWMAC_LPC18XX) += dwmac-lpc18xx.o |
12 | obj-$(CONFIG_DWMAC_MESON) += dwmac-meson.o | 12 | obj-$(CONFIG_DWMAC_MESON) += dwmac-meson.o |
13 | obj-$(CONFIG_DWMAC_ROCKCHIP) += dwmac-rk.o | 13 | obj-$(CONFIG_DWMAC_ROCKCHIP) += dwmac-rk.o |
14 | obj-$(CONFIG_DWMAC_SOCFPGA) += dwmac-socfpga.o | 14 | obj-$(CONFIG_DWMAC_SOCFPGA) += dwmac-altr-socfpga.o |
15 | obj-$(CONFIG_DWMAC_STI) += dwmac-sti.o | 15 | obj-$(CONFIG_DWMAC_STI) += dwmac-sti.o |
16 | obj-$(CONFIG_DWMAC_SUNXI) += dwmac-sunxi.o | 16 | obj-$(CONFIG_DWMAC_SUNXI) += dwmac-sunxi.o |
17 | obj-$(CONFIG_DWMAC_GENERIC) += dwmac-generic.o | 17 | obj-$(CONFIG_DWMAC_GENERIC) += dwmac-generic.o |
18 | stmmac-platform-objs:= stmmac_platform.o | 18 | stmmac-platform-objs:= stmmac_platform.o |
19 | dwmac-altr-socfpga-objs := altr_tse_pcs.o dwmac-socfpga.o | ||
19 | 20 | ||
20 | obj-$(CONFIG_STMMAC_PCI) += stmmac-pci.o | 21 | obj-$(CONFIG_STMMAC_PCI) += stmmac-pci.o |
21 | stmmac-pci-objs:= stmmac_pci.o | 22 | stmmac-pci-objs:= stmmac_pci.o |
diff --git a/drivers/net/ethernet/stmicro/stmmac/altr_tse_pcs.c b/drivers/net/ethernet/stmicro/stmmac/altr_tse_pcs.c new file mode 100644 index 000000000000..2920e2ee3864 --- /dev/null +++ b/drivers/net/ethernet/stmicro/stmmac/altr_tse_pcs.c | |||
@@ -0,0 +1,274 @@ | |||
1 | /* Copyright Altera Corporation (C) 2016. All rights reserved. | ||
2 | * | ||
3 | * This program is free software; you can redistribute it and/or modify | ||
4 | * it under the terms of the GNU General Public License, version 2, | ||
5 | * as published by the Free Software Foundation. | ||
6 | * | ||
7 | * This program is distributed in the hope that it will be useful, | ||
8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
10 | * GNU General Public License for more details. | ||
11 | * | ||
12 | * You should have received a copy of the GNU General Public License | ||
13 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
14 | * | ||
15 | * Author: Tien Hock Loh <thloh@altera.com> | ||
16 | */ | ||
17 | |||
18 | #include <linux/mfd/syscon.h> | ||
19 | #include <linux/of.h> | ||
20 | #include <linux/of_address.h> | ||
21 | #include <linux/of_net.h> | ||
22 | #include <linux/phy.h> | ||
23 | #include <linux/regmap.h> | ||
24 | #include <linux/reset.h> | ||
25 | #include <linux/stmmac.h> | ||
26 | |||
27 | #include "stmmac.h" | ||
28 | #include "stmmac_platform.h" | ||
29 | #include "altr_tse_pcs.h" | ||
30 | |||
31 | #define SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_GMII_MII 0 | ||
32 | #define SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_RGMII BIT(1) | ||
33 | #define SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_RMII BIT(2) | ||
34 | #define SYSMGR_EMACGRP_CTRL_PHYSEL_WIDTH 2 | ||
35 | #define SYSMGR_EMACGRP_CTRL_PHYSEL_MASK GENMASK(1, 0) | ||
36 | |||
37 | #define TSE_PCS_CONTROL_AN_EN_MASK BIT(12) | ||
38 | #define TSE_PCS_CONTROL_REG 0x00 | ||
39 | #define TSE_PCS_CONTROL_RESTART_AN_MASK BIT(9) | ||
40 | #define TSE_PCS_IF_MODE_REG 0x28 | ||
41 | #define TSE_PCS_LINK_TIMER_0_REG 0x24 | ||
42 | #define TSE_PCS_LINK_TIMER_1_REG 0x26 | ||
43 | #define TSE_PCS_SIZE 0x40 | ||
44 | #define TSE_PCS_STATUS_AN_COMPLETED_MASK BIT(5) | ||
45 | #define TSE_PCS_STATUS_LINK_MASK 0x0004 | ||
46 | #define TSE_PCS_STATUS_REG 0x02 | ||
47 | #define TSE_PCS_SGMII_SPEED_1000 BIT(3) | ||
48 | #define TSE_PCS_SGMII_SPEED_100 BIT(2) | ||
49 | #define TSE_PCS_SGMII_SPEED_10 0x0 | ||
50 | #define TSE_PCS_SW_RST_MASK 0x8000 | ||
51 | #define TSE_PCS_PARTNER_ABILITY_REG 0x0A | ||
52 | #define TSE_PCS_PARTNER_DUPLEX_FULL 0x1000 | ||
53 | #define TSE_PCS_PARTNER_DUPLEX_HALF 0x0000 | ||
54 | #define TSE_PCS_PARTNER_DUPLEX_MASK 0x1000 | ||
55 | #define TSE_PCS_PARTNER_SPEED_MASK GENMASK(11, 10) | ||
56 | #define TSE_PCS_PARTNER_SPEED_1000 BIT(11) | ||
57 | #define TSE_PCS_PARTNER_SPEED_100 BIT(10) | ||
58 | #define TSE_PCS_PARTNER_SPEED_10 0x0000 | ||
59 | #define TSE_PCS_PARTNER_SPEED_1000 BIT(11) | ||
60 | #define TSE_PCS_PARTNER_SPEED_100 BIT(10) | ||
61 | #define TSE_PCS_PARTNER_SPEED_10 0x0000 | ||
62 | #define TSE_PCS_SGMII_SPEED_MASK GENMASK(3, 2) | ||
63 | #define TSE_PCS_SGMII_LINK_TIMER_0 0x0D40 | ||
64 | #define TSE_PCS_SGMII_LINK_TIMER_1 0x0003 | ||
65 | #define TSE_PCS_SW_RESET_TIMEOUT 100 | ||
66 | #define TSE_PCS_USE_SGMII_AN_MASK BIT(2) | ||
67 | #define TSE_PCS_USE_SGMII_ENA BIT(1) | ||
68 | |||
69 | #define SGMII_ADAPTER_CTRL_REG 0x00 | ||
70 | #define SGMII_ADAPTER_DISABLE 0x0001 | ||
71 | #define SGMII_ADAPTER_ENABLE 0x0000 | ||
72 | |||
73 | #define AUTONEGO_LINK_TIMER 20 | ||
74 | |||
75 | static int tse_pcs_reset(void __iomem *base, struct tse_pcs *pcs) | ||
76 | { | ||
77 | int counter = 0; | ||
78 | u16 val; | ||
79 | |||
80 | val = readw(base + TSE_PCS_CONTROL_REG); | ||
81 | val |= TSE_PCS_SW_RST_MASK; | ||
82 | writew(val, base + TSE_PCS_CONTROL_REG); | ||
83 | |||
84 | while (counter < TSE_PCS_SW_RESET_TIMEOUT) { | ||
85 | val = readw(base + TSE_PCS_CONTROL_REG); | ||
86 | val &= TSE_PCS_SW_RST_MASK; | ||
87 | if (val == 0) | ||
88 | break; | ||
89 | counter++; | ||
90 | udelay(1); | ||
91 | } | ||
92 | if (counter >= TSE_PCS_SW_RESET_TIMEOUT) { | ||
93 | dev_err(pcs->dev, "PCS could not get out of sw reset\n"); | ||
94 | return -ETIMEDOUT; | ||
95 | } | ||
96 | |||
97 | return 0; | ||
98 | } | ||
99 | |||
100 | int tse_pcs_init(void __iomem *base, struct tse_pcs *pcs) | ||
101 | { | ||
102 | int ret = 0; | ||
103 | |||
104 | writew(TSE_PCS_USE_SGMII_ENA, base + TSE_PCS_IF_MODE_REG); | ||
105 | |||
106 | writew(TSE_PCS_SGMII_LINK_TIMER_0, base + TSE_PCS_LINK_TIMER_0_REG); | ||
107 | writew(TSE_PCS_SGMII_LINK_TIMER_1, base + TSE_PCS_LINK_TIMER_1_REG); | ||
108 | |||
109 | ret = tse_pcs_reset(base, pcs); | ||
110 | if (ret == 0) | ||
111 | writew(SGMII_ADAPTER_ENABLE, | ||
112 | pcs->sgmii_adapter_base + SGMII_ADAPTER_CTRL_REG); | ||
113 | |||
114 | return ret; | ||
115 | } | ||
116 | |||
117 | static void pcs_link_timer_callback(unsigned long data) | ||
118 | { | ||
119 | u16 val = 0; | ||
120 | struct tse_pcs *pcs = (struct tse_pcs *)data; | ||
121 | void __iomem *tse_pcs_base = pcs->tse_pcs_base; | ||
122 | void __iomem *sgmii_adapter_base = pcs->sgmii_adapter_base; | ||
123 | |||
124 | val = readw(tse_pcs_base + TSE_PCS_STATUS_REG); | ||
125 | val &= TSE_PCS_STATUS_LINK_MASK; | ||
126 | |||
127 | if (val != 0) { | ||
128 | dev_dbg(pcs->dev, "Adapter: Link is established\n"); | ||
129 | writew(SGMII_ADAPTER_ENABLE, | ||
130 | sgmii_adapter_base + SGMII_ADAPTER_CTRL_REG); | ||
131 | } else { | ||
132 | mod_timer(&pcs->aneg_link_timer, jiffies + | ||
133 | msecs_to_jiffies(AUTONEGO_LINK_TIMER)); | ||
134 | } | ||
135 | } | ||
136 | |||
137 | static void auto_nego_timer_callback(unsigned long data) | ||
138 | { | ||
139 | u16 val = 0; | ||
140 | u16 speed = 0; | ||
141 | u16 duplex = 0; | ||
142 | struct tse_pcs *pcs = (struct tse_pcs *)data; | ||
143 | void __iomem *tse_pcs_base = pcs->tse_pcs_base; | ||
144 | void __iomem *sgmii_adapter_base = pcs->sgmii_adapter_base; | ||
145 | |||
146 | val = readw(tse_pcs_base + TSE_PCS_STATUS_REG); | ||
147 | val &= TSE_PCS_STATUS_AN_COMPLETED_MASK; | ||
148 | |||
149 | if (val != 0) { | ||
150 | dev_dbg(pcs->dev, "Adapter: Auto Negotiation is completed\n"); | ||
151 | val = readw(tse_pcs_base + TSE_PCS_PARTNER_ABILITY_REG); | ||
152 | speed = val & TSE_PCS_PARTNER_SPEED_MASK; | ||
153 | duplex = val & TSE_PCS_PARTNER_DUPLEX_MASK; | ||
154 | |||
155 | if (speed == TSE_PCS_PARTNER_SPEED_10 && | ||
156 | duplex == TSE_PCS_PARTNER_DUPLEX_FULL) | ||
157 | dev_dbg(pcs->dev, | ||
158 | "Adapter: Link Partner is Up - 10/Full\n"); | ||
159 | else if (speed == TSE_PCS_PARTNER_SPEED_100 && | ||
160 | duplex == TSE_PCS_PARTNER_DUPLEX_FULL) | ||
161 | dev_dbg(pcs->dev, | ||
162 | "Adapter: Link Partner is Up - 100/Full\n"); | ||
163 | else if (speed == TSE_PCS_PARTNER_SPEED_1000 && | ||
164 | duplex == TSE_PCS_PARTNER_DUPLEX_FULL) | ||
165 | dev_dbg(pcs->dev, | ||
166 | "Adapter: Link Partner is Up - 1000/Full\n"); | ||
167 | else if (speed == TSE_PCS_PARTNER_SPEED_10 && | ||
168 | duplex == TSE_PCS_PARTNER_DUPLEX_HALF) | ||
169 | dev_err(pcs->dev, | ||
170 | "Adapter does not support Half Duplex\n"); | ||
171 | else if (speed == TSE_PCS_PARTNER_SPEED_100 && | ||
172 | duplex == TSE_PCS_PARTNER_DUPLEX_HALF) | ||
173 | dev_err(pcs->dev, | ||
174 | "Adapter does not support Half Duplex\n"); | ||
175 | else if (speed == TSE_PCS_PARTNER_SPEED_1000 && | ||
176 | duplex == TSE_PCS_PARTNER_DUPLEX_HALF) | ||
177 | dev_err(pcs->dev, | ||
178 | "Adapter does not support Half Duplex\n"); | ||
179 | else | ||
180 | dev_err(pcs->dev, | ||
181 | "Adapter: Invalid Partner Speed and Duplex\n"); | ||
182 | |||
183 | if (duplex == TSE_PCS_PARTNER_DUPLEX_FULL && | ||
184 | (speed == TSE_PCS_PARTNER_SPEED_10 || | ||
185 | speed == TSE_PCS_PARTNER_SPEED_100 || | ||
186 | speed == TSE_PCS_PARTNER_SPEED_1000)) | ||
187 | writew(SGMII_ADAPTER_ENABLE, | ||
188 | sgmii_adapter_base + SGMII_ADAPTER_CTRL_REG); | ||
189 | } else { | ||
190 | val = readw(tse_pcs_base + TSE_PCS_CONTROL_REG); | ||
191 | val |= TSE_PCS_CONTROL_RESTART_AN_MASK; | ||
192 | writew(val, tse_pcs_base + TSE_PCS_CONTROL_REG); | ||
193 | |||
194 | tse_pcs_reset(tse_pcs_base, pcs); | ||
195 | mod_timer(&pcs->aneg_link_timer, jiffies + | ||
196 | msecs_to_jiffies(AUTONEGO_LINK_TIMER)); | ||
197 | } | ||
198 | } | ||
199 | |||
200 | static void aneg_link_timer_callback(unsigned long data) | ||
201 | { | ||
202 | struct tse_pcs *pcs = (struct tse_pcs *)data; | ||
203 | |||
204 | if (pcs->autoneg == AUTONEG_ENABLE) | ||
205 | auto_nego_timer_callback(data); | ||
206 | else if (pcs->autoneg == AUTONEG_DISABLE) | ||
207 | pcs_link_timer_callback(data); | ||
208 | } | ||
209 | |||
210 | void tse_pcs_fix_mac_speed(struct tse_pcs *pcs, struct phy_device *phy_dev, | ||
211 | unsigned int speed) | ||
212 | { | ||
213 | void __iomem *tse_pcs_base = pcs->tse_pcs_base; | ||
214 | void __iomem *sgmii_adapter_base = pcs->sgmii_adapter_base; | ||
215 | u32 val; | ||
216 | |||
217 | writew(SGMII_ADAPTER_ENABLE, | ||
218 | sgmii_adapter_base + SGMII_ADAPTER_CTRL_REG); | ||
219 | |||
220 | pcs->autoneg = phy_dev->autoneg; | ||
221 | |||
222 | if (phy_dev->autoneg == AUTONEG_ENABLE) { | ||
223 | val = readw(tse_pcs_base + TSE_PCS_CONTROL_REG); | ||
224 | val |= TSE_PCS_CONTROL_AN_EN_MASK; | ||
225 | writew(val, tse_pcs_base + TSE_PCS_CONTROL_REG); | ||
226 | |||
227 | val = readw(tse_pcs_base + TSE_PCS_IF_MODE_REG); | ||
228 | val |= TSE_PCS_USE_SGMII_AN_MASK; | ||
229 | writew(val, tse_pcs_base + TSE_PCS_IF_MODE_REG); | ||
230 | |||
231 | val = readw(tse_pcs_base + TSE_PCS_CONTROL_REG); | ||
232 | val |= TSE_PCS_CONTROL_RESTART_AN_MASK; | ||
233 | |||
234 | tse_pcs_reset(tse_pcs_base, pcs); | ||
235 | |||
236 | setup_timer(&pcs->aneg_link_timer, | ||
237 | aneg_link_timer_callback, (unsigned long)pcs); | ||
238 | mod_timer(&pcs->aneg_link_timer, jiffies + | ||
239 | msecs_to_jiffies(AUTONEGO_LINK_TIMER)); | ||
240 | } else if (phy_dev->autoneg == AUTONEG_DISABLE) { | ||
241 | val = readw(tse_pcs_base + TSE_PCS_CONTROL_REG); | ||
242 | val &= ~TSE_PCS_CONTROL_AN_EN_MASK; | ||
243 | writew(val, tse_pcs_base + TSE_PCS_CONTROL_REG); | ||
244 | |||
245 | val = readw(tse_pcs_base + TSE_PCS_IF_MODE_REG); | ||
246 | val &= ~TSE_PCS_USE_SGMII_AN_MASK; | ||
247 | writew(val, tse_pcs_base + TSE_PCS_IF_MODE_REG); | ||
248 | |||
249 | val = readw(tse_pcs_base + TSE_PCS_IF_MODE_REG); | ||
250 | val &= ~TSE_PCS_SGMII_SPEED_MASK; | ||
251 | |||
252 | switch (speed) { | ||
253 | case 1000: | ||
254 | val |= TSE_PCS_SGMII_SPEED_1000; | ||
255 | break; | ||
256 | case 100: | ||
257 | val |= TSE_PCS_SGMII_SPEED_100; | ||
258 | break; | ||
259 | case 10: | ||
260 | val |= TSE_PCS_SGMII_SPEED_10; | ||
261 | break; | ||
262 | default: | ||
263 | return; | ||
264 | } | ||
265 | writew(val, tse_pcs_base + TSE_PCS_IF_MODE_REG); | ||
266 | |||
267 | tse_pcs_reset(tse_pcs_base, pcs); | ||
268 | |||
269 | setup_timer(&pcs->aneg_link_timer, | ||
270 | aneg_link_timer_callback, (unsigned long)pcs); | ||
271 | mod_timer(&pcs->aneg_link_timer, jiffies + | ||
272 | msecs_to_jiffies(AUTONEGO_LINK_TIMER)); | ||
273 | } | ||
274 | } | ||
diff --git a/drivers/net/ethernet/stmicro/stmmac/altr_tse_pcs.h b/drivers/net/ethernet/stmicro/stmmac/altr_tse_pcs.h new file mode 100644 index 000000000000..2f5882450b06 --- /dev/null +++ b/drivers/net/ethernet/stmicro/stmmac/altr_tse_pcs.h | |||
@@ -0,0 +1,36 @@ | |||
1 | /* Copyright Altera Corporation (C) 2016. All rights reserved. | ||
2 | * | ||
3 | * This program is free software; you can redistribute it and/or modify | ||
4 | * it under the terms of the GNU General Public License, version 2, | ||
5 | * as published by the Free Software Foundation. | ||
6 | * | ||
7 | * This program is distributed in the hope that it will be useful, | ||
8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
10 | * GNU General Public License for more details. | ||
11 | * | ||
12 | * You should have received a copy of the GNU General Public License | ||
13 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
14 | * | ||
15 | * Author: Tien Hock Loh <thloh@altera.com> | ||
16 | */ | ||
17 | |||
18 | #ifndef __TSE_PCS_H__ | ||
19 | #define __TSE_PCS_H__ | ||
20 | |||
21 | #include <linux/phy.h> | ||
22 | #include <linux/timer.h> | ||
23 | |||
24 | struct tse_pcs { | ||
25 | struct device *dev; | ||
26 | void __iomem *tse_pcs_base; | ||
27 | void __iomem *sgmii_adapter_base; | ||
28 | struct timer_list aneg_link_timer; | ||
29 | int autoneg; | ||
30 | }; | ||
31 | |||
32 | int tse_pcs_init(void __iomem *base, struct tse_pcs *pcs); | ||
33 | void tse_pcs_fix_mac_speed(struct tse_pcs *pcs, struct phy_device *phy_dev, | ||
34 | unsigned int speed); | ||
35 | |||
36 | #endif /* __TSE_PCS_H__ */ | ||
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c index f13499fa1f58..4bee2f934a73 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c | |||
@@ -27,6 +27,11 @@ | |||
27 | #include "stmmac.h" | 27 | #include "stmmac.h" |
28 | #include "stmmac_platform.h" | 28 | #include "stmmac_platform.h" |
29 | 29 | ||
30 | #include "altr_tse_pcs.h" | ||
31 | |||
32 | #define SGMII_ADAPTER_CTRL_REG 0x00 | ||
33 | #define SGMII_ADAPTER_DISABLE 0x0001 | ||
34 | |||
30 | #define SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_GMII_MII 0x0 | 35 | #define SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_GMII_MII 0x0 |
31 | #define SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_RGMII 0x1 | 36 | #define SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_RGMII 0x1 |
32 | #define SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_RMII 0x2 | 37 | #define SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_RMII 0x2 |
@@ -52,35 +57,46 @@ struct socfpga_dwmac { | |||
52 | struct reset_control *stmmac_rst; | 57 | struct reset_control *stmmac_rst; |
53 | void __iomem *splitter_base; | 58 | void __iomem *splitter_base; |
54 | bool f2h_ptp_ref_clk; | 59 | bool f2h_ptp_ref_clk; |
60 | struct tse_pcs pcs; | ||
55 | }; | 61 | }; |
56 | 62 | ||
57 | static void socfpga_dwmac_fix_mac_speed(void *priv, unsigned int speed) | 63 | static void socfpga_dwmac_fix_mac_speed(void *priv, unsigned int speed) |
58 | { | 64 | { |
59 | struct socfpga_dwmac *dwmac = (struct socfpga_dwmac *)priv; | 65 | struct socfpga_dwmac *dwmac = (struct socfpga_dwmac *)priv; |
60 | void __iomem *splitter_base = dwmac->splitter_base; | 66 | void __iomem *splitter_base = dwmac->splitter_base; |
67 | void __iomem *tse_pcs_base = dwmac->pcs.tse_pcs_base; | ||
68 | void __iomem *sgmii_adapter_base = dwmac->pcs.sgmii_adapter_base; | ||
69 | struct device *dev = dwmac->dev; | ||
70 | struct net_device *ndev = dev_get_drvdata(dev); | ||
71 | struct phy_device *phy_dev = ndev->phydev; | ||
61 | u32 val; | 72 | u32 val; |
62 | 73 | ||
63 | if (!splitter_base) | 74 | if ((tse_pcs_base) && (sgmii_adapter_base)) |
64 | return; | 75 | writew(SGMII_ADAPTER_DISABLE, |
65 | 76 | sgmii_adapter_base + SGMII_ADAPTER_CTRL_REG); | |
66 | val = readl(splitter_base + EMAC_SPLITTER_CTRL_REG); | 77 | |
67 | val &= ~EMAC_SPLITTER_CTRL_SPEED_MASK; | 78 | if (splitter_base) { |
68 | 79 | val = readl(splitter_base + EMAC_SPLITTER_CTRL_REG); | |
69 | switch (speed) { | 80 | val &= ~EMAC_SPLITTER_CTRL_SPEED_MASK; |
70 | case 1000: | 81 | |
71 | val |= EMAC_SPLITTER_CTRL_SPEED_1000; | 82 | switch (speed) { |
72 | break; | 83 | case 1000: |
73 | case 100: | 84 | val |= EMAC_SPLITTER_CTRL_SPEED_1000; |
74 | val |= EMAC_SPLITTER_CTRL_SPEED_100; | 85 | break; |
75 | break; | 86 | case 100: |
76 | case 10: | 87 | val |= EMAC_SPLITTER_CTRL_SPEED_100; |
77 | val |= EMAC_SPLITTER_CTRL_SPEED_10; | 88 | break; |
78 | break; | 89 | case 10: |
79 | default: | 90 | val |= EMAC_SPLITTER_CTRL_SPEED_10; |
80 | return; | 91 | break; |
92 | default: | ||
93 | return; | ||
94 | } | ||
95 | writel(val, splitter_base + EMAC_SPLITTER_CTRL_REG); | ||
81 | } | 96 | } |
82 | 97 | ||
83 | writel(val, splitter_base + EMAC_SPLITTER_CTRL_REG); | 98 | if (tse_pcs_base && sgmii_adapter_base) |
99 | tse_pcs_fix_mac_speed(&dwmac->pcs, phy_dev, speed); | ||
84 | } | 100 | } |
85 | 101 | ||
86 | static int socfpga_dwmac_parse_data(struct socfpga_dwmac *dwmac, struct device *dev) | 102 | static int socfpga_dwmac_parse_data(struct socfpga_dwmac *dwmac, struct device *dev) |
@@ -88,9 +104,12 @@ static int socfpga_dwmac_parse_data(struct socfpga_dwmac *dwmac, struct device * | |||
88 | struct device_node *np = dev->of_node; | 104 | struct device_node *np = dev->of_node; |
89 | struct regmap *sys_mgr_base_addr; | 105 | struct regmap *sys_mgr_base_addr; |
90 | u32 reg_offset, reg_shift; | 106 | u32 reg_offset, reg_shift; |
91 | int ret; | 107 | int ret, index; |
92 | struct device_node *np_splitter; | 108 | struct device_node *np_splitter = NULL; |
109 | struct device_node *np_sgmii_adapter = NULL; | ||
93 | struct resource res_splitter; | 110 | struct resource res_splitter; |
111 | struct resource res_tse_pcs; | ||
112 | struct resource res_sgmii_adapter; | ||
94 | 113 | ||
95 | dwmac->interface = of_get_phy_mode(np); | 114 | dwmac->interface = of_get_phy_mode(np); |
96 | 115 | ||
@@ -128,6 +147,77 @@ static int socfpga_dwmac_parse_data(struct socfpga_dwmac *dwmac, struct device * | |||
128 | } | 147 | } |
129 | } | 148 | } |
130 | 149 | ||
150 | np_sgmii_adapter = of_parse_phandle(np, | ||
151 | "altr,gmii-to-sgmii-converter", 0); | ||
152 | if (np_sgmii_adapter) { | ||
153 | index = of_property_match_string(np_sgmii_adapter, "reg-names", | ||
154 | "hps_emac_interface_splitter_avalon_slave"); | ||
155 | |||
156 | if (index >= 0) { | ||
157 | if (of_address_to_resource(np_sgmii_adapter, index, | ||
158 | &res_splitter)) { | ||
159 | dev_err(dev, | ||
160 | "%s: ERROR: missing emac splitter address\n", | ||
161 | __func__); | ||
162 | return -EINVAL; | ||
163 | } | ||
164 | |||
165 | dwmac->splitter_base = | ||
166 | devm_ioremap_resource(dev, &res_splitter); | ||
167 | |||
168 | if (IS_ERR(dwmac->splitter_base)) { | ||
169 | dev_err(dev, | ||
170 | "%s: ERROR: failed mapping emac splitter\n", | ||
171 | __func__); | ||
172 | return PTR_ERR(dwmac->splitter_base); | ||
173 | } | ||
174 | } | ||
175 | |||
176 | index = of_property_match_string(np_sgmii_adapter, "reg-names", | ||
177 | "gmii_to_sgmii_adapter_avalon_slave"); | ||
178 | |||
179 | if (index >= 0) { | ||
180 | if (of_address_to_resource(np_sgmii_adapter, index, | ||
181 | &res_sgmii_adapter)) { | ||
182 | dev_err(dev, | ||
183 | "%s: ERROR: failed mapping adapter\n", | ||
184 | __func__); | ||
185 | return -EINVAL; | ||
186 | } | ||
187 | |||
188 | dwmac->pcs.sgmii_adapter_base = | ||
189 | devm_ioremap_resource(dev, &res_sgmii_adapter); | ||
190 | |||
191 | if (IS_ERR(dwmac->pcs.sgmii_adapter_base)) { | ||
192 | dev_err(dev, "%s: failed to mapping adapter\n", | ||
193 | __func__); | ||
194 | return PTR_ERR(dwmac->pcs.sgmii_adapter_base); | ||
195 | } | ||
196 | } | ||
197 | |||
198 | index = of_property_match_string(np_sgmii_adapter, "reg-names", | ||
199 | "eth_tse_control_port"); | ||
200 | |||
201 | if (index >= 0) { | ||
202 | if (of_address_to_resource(np_sgmii_adapter, index, | ||
203 | &res_tse_pcs)) { | ||
204 | dev_err(dev, | ||
205 | "%s: ERROR: failed mapping tse control port\n", | ||
206 | __func__); | ||
207 | return -EINVAL; | ||
208 | } | ||
209 | |||
210 | dwmac->pcs.tse_pcs_base = | ||
211 | devm_ioremap_resource(dev, &res_tse_pcs); | ||
212 | |||
213 | if (IS_ERR(dwmac->pcs.tse_pcs_base)) { | ||
214 | dev_err(dev, | ||
215 | "%s: ERROR: failed mapping tse control port\n", | ||
216 | __func__); | ||
217 | return PTR_ERR(dwmac->pcs.sgmii_adapter_base); | ||
218 | } | ||
219 | } | ||
220 | } | ||
131 | dwmac->reg_offset = reg_offset; | 221 | dwmac->reg_offset = reg_offset; |
132 | dwmac->reg_shift = reg_shift; | 222 | dwmac->reg_shift = reg_shift; |
133 | dwmac->sys_mgr_base_addr = sys_mgr_base_addr; | 223 | dwmac->sys_mgr_base_addr = sys_mgr_base_addr; |
@@ -151,6 +241,7 @@ static int socfpga_dwmac_set_phy_mode(struct socfpga_dwmac *dwmac) | |||
151 | break; | 241 | break; |
152 | case PHY_INTERFACE_MODE_MII: | 242 | case PHY_INTERFACE_MODE_MII: |
153 | case PHY_INTERFACE_MODE_GMII: | 243 | case PHY_INTERFACE_MODE_GMII: |
244 | case PHY_INTERFACE_MODE_SGMII: | ||
154 | val = SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_GMII_MII; | 245 | val = SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_GMII_MII; |
155 | break; | 246 | break; |
156 | default: | 247 | default: |
@@ -191,6 +282,12 @@ static int socfpga_dwmac_set_phy_mode(struct socfpga_dwmac *dwmac) | |||
191 | */ | 282 | */ |
192 | if (dwmac->stmmac_rst) | 283 | if (dwmac->stmmac_rst) |
193 | reset_control_deassert(dwmac->stmmac_rst); | 284 | reset_control_deassert(dwmac->stmmac_rst); |
285 | if (phymode == PHY_INTERFACE_MODE_SGMII) { | ||
286 | if (tse_pcs_init(dwmac->pcs.tse_pcs_base, &dwmac->pcs) != 0) { | ||
287 | dev_err(dwmac->dev, "Unable to initialize TSE PCS"); | ||
288 | return -EINVAL; | ||
289 | } | ||
290 | } | ||
194 | 291 | ||
195 | return 0; | 292 | return 0; |
196 | } | 293 | } |
@@ -225,6 +322,7 @@ static int socfpga_dwmac_probe(struct platform_device *pdev) | |||
225 | plat_dat->fix_mac_speed = socfpga_dwmac_fix_mac_speed; | 322 | plat_dat->fix_mac_speed = socfpga_dwmac_fix_mac_speed; |
226 | 323 | ||
227 | ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); | 324 | ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); |
325 | |||
228 | if (!ret) { | 326 | if (!ret) { |
229 | struct net_device *ndev = platform_get_drvdata(pdev); | 327 | struct net_device *ndev = platform_get_drvdata(pdev); |
230 | struct stmmac_priv *stpriv = netdev_priv(ndev); | 328 | struct stmmac_priv *stpriv = netdev_priv(ndev); |