]>
Commit | Line | Data |
---|---|---|
caab277b | 1 | // SPDX-License-Identifier: GPL-2.0-only |
fb3bbdb8 | 2 | /* Copyright Altera Corporation (C) 2016. All rights reserved. |
fb3bbdb8 THL |
3 | * |
4 | * Author: Tien Hock Loh <thloh@altera.com> | |
5 | */ | |
6 | ||
7 | #include <linux/mfd/syscon.h> | |
8 | #include <linux/of.h> | |
9 | #include <linux/of_address.h> | |
10 | #include <linux/of_net.h> | |
11 | #include <linux/phy.h> | |
12 | #include <linux/regmap.h> | |
13 | #include <linux/reset.h> | |
14 | #include <linux/stmmac.h> | |
15 | ||
16 | #include "stmmac.h" | |
17 | #include "stmmac_platform.h" | |
18 | #include "altr_tse_pcs.h" | |
19 | ||
20 | #define SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_GMII_MII 0 | |
21 | #define SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_RGMII BIT(1) | |
22 | #define SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_RMII BIT(2) | |
23 | #define SYSMGR_EMACGRP_CTRL_PHYSEL_WIDTH 2 | |
24 | #define SYSMGR_EMACGRP_CTRL_PHYSEL_MASK GENMASK(1, 0) | |
25 | ||
26 | #define TSE_PCS_CONTROL_AN_EN_MASK BIT(12) | |
27 | #define TSE_PCS_CONTROL_REG 0x00 | |
28 | #define TSE_PCS_CONTROL_RESTART_AN_MASK BIT(9) | |
77032732 | 29 | #define TSE_PCS_CTRL_AUTONEG_SGMII 0x1140 |
fb3bbdb8 THL |
30 | #define TSE_PCS_IF_MODE_REG 0x28 |
31 | #define TSE_PCS_LINK_TIMER_0_REG 0x24 | |
32 | #define TSE_PCS_LINK_TIMER_1_REG 0x26 | |
33 | #define TSE_PCS_SIZE 0x40 | |
34 | #define TSE_PCS_STATUS_AN_COMPLETED_MASK BIT(5) | |
35 | #define TSE_PCS_STATUS_LINK_MASK 0x0004 | |
36 | #define TSE_PCS_STATUS_REG 0x02 | |
37 | #define TSE_PCS_SGMII_SPEED_1000 BIT(3) | |
38 | #define TSE_PCS_SGMII_SPEED_100 BIT(2) | |
39 | #define TSE_PCS_SGMII_SPEED_10 0x0 | |
40 | #define TSE_PCS_SW_RST_MASK 0x8000 | |
41 | #define TSE_PCS_PARTNER_ABILITY_REG 0x0A | |
42 | #define TSE_PCS_PARTNER_DUPLEX_FULL 0x1000 | |
43 | #define TSE_PCS_PARTNER_DUPLEX_HALF 0x0000 | |
44 | #define TSE_PCS_PARTNER_DUPLEX_MASK 0x1000 | |
45 | #define TSE_PCS_PARTNER_SPEED_MASK GENMASK(11, 10) | |
46 | #define TSE_PCS_PARTNER_SPEED_1000 BIT(11) | |
47 | #define TSE_PCS_PARTNER_SPEED_100 BIT(10) | |
48 | #define TSE_PCS_PARTNER_SPEED_10 0x0000 | |
49 | #define TSE_PCS_PARTNER_SPEED_1000 BIT(11) | |
50 | #define TSE_PCS_PARTNER_SPEED_100 BIT(10) | |
51 | #define TSE_PCS_PARTNER_SPEED_10 0x0000 | |
52 | #define TSE_PCS_SGMII_SPEED_MASK GENMASK(3, 2) | |
53 | #define TSE_PCS_SGMII_LINK_TIMER_0 0x0D40 | |
54 | #define TSE_PCS_SGMII_LINK_TIMER_1 0x0003 | |
55 | #define TSE_PCS_SW_RESET_TIMEOUT 100 | |
f6c365fa JJH |
56 | #define TSE_PCS_USE_SGMII_AN_MASK BIT(1) |
57 | #define TSE_PCS_USE_SGMII_ENA BIT(0) | |
77032732 | 58 | #define TSE_PCS_IF_USE_SGMII 0x03 |
fb3bbdb8 | 59 | |
fb3bbdb8 THL |
60 | #define AUTONEGO_LINK_TIMER 20 |
61 | ||
62 | static int tse_pcs_reset(void __iomem *base, struct tse_pcs *pcs) | |
63 | { | |
64 | int counter = 0; | |
65 | u16 val; | |
66 | ||
67 | val = readw(base + TSE_PCS_CONTROL_REG); | |
68 | val |= TSE_PCS_SW_RST_MASK; | |
69 | writew(val, base + TSE_PCS_CONTROL_REG); | |
70 | ||
71 | while (counter < TSE_PCS_SW_RESET_TIMEOUT) { | |
72 | val = readw(base + TSE_PCS_CONTROL_REG); | |
73 | val &= TSE_PCS_SW_RST_MASK; | |
74 | if (val == 0) | |
75 | break; | |
76 | counter++; | |
77 | udelay(1); | |
78 | } | |
79 | if (counter >= TSE_PCS_SW_RESET_TIMEOUT) { | |
80 | dev_err(pcs->dev, "PCS could not get out of sw reset\n"); | |
81 | return -ETIMEDOUT; | |
82 | } | |
83 | ||
84 | return 0; | |
85 | } | |
86 | ||
87 | int tse_pcs_init(void __iomem *base, struct tse_pcs *pcs) | |
88 | { | |
89 | int ret = 0; | |
90 | ||
77032732 TT |
91 | writew(TSE_PCS_IF_USE_SGMII, base + TSE_PCS_IF_MODE_REG); |
92 | ||
93 | writew(TSE_PCS_CTRL_AUTONEG_SGMII, base + TSE_PCS_CONTROL_REG); | |
fb3bbdb8 THL |
94 | |
95 | writew(TSE_PCS_SGMII_LINK_TIMER_0, base + TSE_PCS_LINK_TIMER_0_REG); | |
96 | writew(TSE_PCS_SGMII_LINK_TIMER_1, base + TSE_PCS_LINK_TIMER_1_REG); | |
97 | ||
98 | ret = tse_pcs_reset(base, pcs); | |
99 | if (ret == 0) | |
100 | writew(SGMII_ADAPTER_ENABLE, | |
101 | pcs->sgmii_adapter_base + SGMII_ADAPTER_CTRL_REG); | |
102 | ||
103 | return ret; | |
104 | } | |
105 | ||
abec4be3 | 106 | static void pcs_link_timer_callback(struct tse_pcs *pcs) |
fb3bbdb8 THL |
107 | { |
108 | u16 val = 0; | |
fb3bbdb8 THL |
109 | void __iomem *tse_pcs_base = pcs->tse_pcs_base; |
110 | void __iomem *sgmii_adapter_base = pcs->sgmii_adapter_base; | |
111 | ||
112 | val = readw(tse_pcs_base + TSE_PCS_STATUS_REG); | |
113 | val &= TSE_PCS_STATUS_LINK_MASK; | |
114 | ||
115 | if (val != 0) { | |
116 | dev_dbg(pcs->dev, "Adapter: Link is established\n"); | |
117 | writew(SGMII_ADAPTER_ENABLE, | |
118 | sgmii_adapter_base + SGMII_ADAPTER_CTRL_REG); | |
119 | } else { | |
120 | mod_timer(&pcs->aneg_link_timer, jiffies + | |
121 | msecs_to_jiffies(AUTONEGO_LINK_TIMER)); | |
122 | } | |
123 | } | |
124 | ||
abec4be3 | 125 | static void auto_nego_timer_callback(struct tse_pcs *pcs) |
fb3bbdb8 THL |
126 | { |
127 | u16 val = 0; | |
128 | u16 speed = 0; | |
129 | u16 duplex = 0; | |
fb3bbdb8 THL |
130 | void __iomem *tse_pcs_base = pcs->tse_pcs_base; |
131 | void __iomem *sgmii_adapter_base = pcs->sgmii_adapter_base; | |
132 | ||
133 | val = readw(tse_pcs_base + TSE_PCS_STATUS_REG); | |
134 | val &= TSE_PCS_STATUS_AN_COMPLETED_MASK; | |
135 | ||
136 | if (val != 0) { | |
137 | dev_dbg(pcs->dev, "Adapter: Auto Negotiation is completed\n"); | |
138 | val = readw(tse_pcs_base + TSE_PCS_PARTNER_ABILITY_REG); | |
139 | speed = val & TSE_PCS_PARTNER_SPEED_MASK; | |
140 | duplex = val & TSE_PCS_PARTNER_DUPLEX_MASK; | |
141 | ||
142 | if (speed == TSE_PCS_PARTNER_SPEED_10 && | |
143 | duplex == TSE_PCS_PARTNER_DUPLEX_FULL) | |
144 | dev_dbg(pcs->dev, | |
145 | "Adapter: Link Partner is Up - 10/Full\n"); | |
146 | else if (speed == TSE_PCS_PARTNER_SPEED_100 && | |
147 | duplex == TSE_PCS_PARTNER_DUPLEX_FULL) | |
148 | dev_dbg(pcs->dev, | |
149 | "Adapter: Link Partner is Up - 100/Full\n"); | |
150 | else if (speed == TSE_PCS_PARTNER_SPEED_1000 && | |
151 | duplex == TSE_PCS_PARTNER_DUPLEX_FULL) | |
152 | dev_dbg(pcs->dev, | |
153 | "Adapter: Link Partner is Up - 1000/Full\n"); | |
154 | else if (speed == TSE_PCS_PARTNER_SPEED_10 && | |
155 | duplex == TSE_PCS_PARTNER_DUPLEX_HALF) | |
156 | dev_err(pcs->dev, | |
157 | "Adapter does not support Half Duplex\n"); | |
158 | else if (speed == TSE_PCS_PARTNER_SPEED_100 && | |
159 | duplex == TSE_PCS_PARTNER_DUPLEX_HALF) | |
160 | dev_err(pcs->dev, | |
161 | "Adapter does not support Half Duplex\n"); | |
162 | else if (speed == TSE_PCS_PARTNER_SPEED_1000 && | |
163 | duplex == TSE_PCS_PARTNER_DUPLEX_HALF) | |
164 | dev_err(pcs->dev, | |
165 | "Adapter does not support Half Duplex\n"); | |
166 | else | |
167 | dev_err(pcs->dev, | |
168 | "Adapter: Invalid Partner Speed and Duplex\n"); | |
169 | ||
170 | if (duplex == TSE_PCS_PARTNER_DUPLEX_FULL && | |
171 | (speed == TSE_PCS_PARTNER_SPEED_10 || | |
172 | speed == TSE_PCS_PARTNER_SPEED_100 || | |
173 | speed == TSE_PCS_PARTNER_SPEED_1000)) | |
174 | writew(SGMII_ADAPTER_ENABLE, | |
175 | sgmii_adapter_base + SGMII_ADAPTER_CTRL_REG); | |
176 | } else { | |
177 | val = readw(tse_pcs_base + TSE_PCS_CONTROL_REG); | |
178 | val |= TSE_PCS_CONTROL_RESTART_AN_MASK; | |
179 | writew(val, tse_pcs_base + TSE_PCS_CONTROL_REG); | |
180 | ||
181 | tse_pcs_reset(tse_pcs_base, pcs); | |
182 | mod_timer(&pcs->aneg_link_timer, jiffies + | |
183 | msecs_to_jiffies(AUTONEGO_LINK_TIMER)); | |
184 | } | |
185 | } | |
186 | ||
abec4be3 | 187 | static void aneg_link_timer_callback(struct timer_list *t) |
fb3bbdb8 | 188 | { |
abec4be3 | 189 | struct tse_pcs *pcs = from_timer(pcs, t, aneg_link_timer); |
fb3bbdb8 THL |
190 | |
191 | if (pcs->autoneg == AUTONEG_ENABLE) | |
abec4be3 | 192 | auto_nego_timer_callback(pcs); |
fb3bbdb8 | 193 | else if (pcs->autoneg == AUTONEG_DISABLE) |
abec4be3 | 194 | pcs_link_timer_callback(pcs); |
fb3bbdb8 THL |
195 | } |
196 | ||
197 | void tse_pcs_fix_mac_speed(struct tse_pcs *pcs, struct phy_device *phy_dev, | |
198 | unsigned int speed) | |
199 | { | |
200 | void __iomem *tse_pcs_base = pcs->tse_pcs_base; | |
fb3bbdb8 THL |
201 | u32 val; |
202 | ||
fb3bbdb8 THL |
203 | pcs->autoneg = phy_dev->autoneg; |
204 | ||
205 | if (phy_dev->autoneg == AUTONEG_ENABLE) { | |
206 | val = readw(tse_pcs_base + TSE_PCS_CONTROL_REG); | |
207 | val |= TSE_PCS_CONTROL_AN_EN_MASK; | |
208 | writew(val, tse_pcs_base + TSE_PCS_CONTROL_REG); | |
209 | ||
210 | val = readw(tse_pcs_base + TSE_PCS_IF_MODE_REG); | |
211 | val |= TSE_PCS_USE_SGMII_AN_MASK; | |
212 | writew(val, tse_pcs_base + TSE_PCS_IF_MODE_REG); | |
213 | ||
214 | val = readw(tse_pcs_base + TSE_PCS_CONTROL_REG); | |
215 | val |= TSE_PCS_CONTROL_RESTART_AN_MASK; | |
216 | ||
217 | tse_pcs_reset(tse_pcs_base, pcs); | |
218 | ||
abec4be3 KC |
219 | timer_setup(&pcs->aneg_link_timer, aneg_link_timer_callback, |
220 | 0); | |
fb3bbdb8 THL |
221 | mod_timer(&pcs->aneg_link_timer, jiffies + |
222 | msecs_to_jiffies(AUTONEGO_LINK_TIMER)); | |
223 | } else if (phy_dev->autoneg == AUTONEG_DISABLE) { | |
224 | val = readw(tse_pcs_base + TSE_PCS_CONTROL_REG); | |
225 | val &= ~TSE_PCS_CONTROL_AN_EN_MASK; | |
226 | writew(val, tse_pcs_base + TSE_PCS_CONTROL_REG); | |
227 | ||
228 | val = readw(tse_pcs_base + TSE_PCS_IF_MODE_REG); | |
229 | val &= ~TSE_PCS_USE_SGMII_AN_MASK; | |
230 | writew(val, tse_pcs_base + TSE_PCS_IF_MODE_REG); | |
231 | ||
232 | val = readw(tse_pcs_base + TSE_PCS_IF_MODE_REG); | |
233 | val &= ~TSE_PCS_SGMII_SPEED_MASK; | |
234 | ||
235 | switch (speed) { | |
236 | case 1000: | |
237 | val |= TSE_PCS_SGMII_SPEED_1000; | |
238 | break; | |
239 | case 100: | |
240 | val |= TSE_PCS_SGMII_SPEED_100; | |
241 | break; | |
242 | case 10: | |
243 | val |= TSE_PCS_SGMII_SPEED_10; | |
244 | break; | |
245 | default: | |
246 | return; | |
247 | } | |
248 | writew(val, tse_pcs_base + TSE_PCS_IF_MODE_REG); | |
249 | ||
250 | tse_pcs_reset(tse_pcs_base, pcs); | |
251 | ||
abec4be3 KC |
252 | timer_setup(&pcs->aneg_link_timer, aneg_link_timer_callback, |
253 | 0); | |
fb3bbdb8 THL |
254 | mod_timer(&pcs->aneg_link_timer, jiffies + |
255 | msecs_to_jiffies(AUTONEGO_LINK_TIMER)); | |
256 | } | |
257 | } |