diff options
author | LEROY Christophe <christophe.leroy@c-s.fr> | 2012-09-24 00:00:58 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2012-09-27 17:58:06 -0400 |
commit | 871d1d6b59802adfd42fd22c82ec419bc8fd2f10 (patch) | |
tree | 7d07604d1448af51a3fafcbf5227c188600383b8 /drivers/net/phy/lxt.c | |
parent | 26f7cbc0a5a42d8cc0c7725d10317089a8215f97 (diff) |
lxt PHY: Support for the buggy LXT973 rev A2
This patch adds proper handling of the buggy revision A2 of LXT973 phy, adding
precautions linked to ERRATA Item 4:
Revision A2 of LXT973 chip randomly returns the contents of the previous even
register when you read a odd register regularly
Signed-off-by: Christophe Leroy <christophe.leroy@c-s.fr>
Acked-by: Richard Cochran <richardcochran@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/phy/lxt.c')
-rw-r--r-- | drivers/net/phy/lxt.c | 127 |
1 files changed, 127 insertions, 0 deletions
diff --git a/drivers/net/phy/lxt.c b/drivers/net/phy/lxt.c index 6d1e3fcc43e2..ec40ba882f61 100644 --- a/drivers/net/phy/lxt.c +++ b/drivers/net/phy/lxt.c | |||
@@ -122,6 +122,123 @@ static int lxt971_config_intr(struct phy_device *phydev) | |||
122 | return err; | 122 | return err; |
123 | } | 123 | } |
124 | 124 | ||
125 | /* | ||
126 | * A2 version of LXT973 chip has an ERRATA: it randomly return the contents | ||
127 | * of the previous even register when you read a odd register regularly | ||
128 | */ | ||
129 | |||
130 | static int lxt973a2_update_link(struct phy_device *phydev) | ||
131 | { | ||
132 | int status; | ||
133 | int control; | ||
134 | int retry = 8; /* we try 8 times */ | ||
135 | |||
136 | /* Do a fake read */ | ||
137 | status = phy_read(phydev, MII_BMSR); | ||
138 | |||
139 | if (status < 0) | ||
140 | return status; | ||
141 | |||
142 | control = phy_read(phydev, MII_BMCR); | ||
143 | if (control < 0) | ||
144 | return control; | ||
145 | |||
146 | do { | ||
147 | /* Read link and autonegotiation status */ | ||
148 | status = phy_read(phydev, MII_BMSR); | ||
149 | } while (status >= 0 && retry-- && status == control); | ||
150 | |||
151 | if (status < 0) | ||
152 | return status; | ||
153 | |||
154 | if ((status & BMSR_LSTATUS) == 0) | ||
155 | phydev->link = 0; | ||
156 | else | ||
157 | phydev->link = 1; | ||
158 | |||
159 | return 0; | ||
160 | } | ||
161 | |||
162 | int lxt973a2_read_status(struct phy_device *phydev) | ||
163 | { | ||
164 | int adv; | ||
165 | int err; | ||
166 | int lpa; | ||
167 | int lpagb = 0; | ||
168 | |||
169 | /* Update the link, but return if there was an error */ | ||
170 | err = lxt973a2_update_link(phydev); | ||
171 | if (err) | ||
172 | return err; | ||
173 | |||
174 | if (AUTONEG_ENABLE == phydev->autoneg) { | ||
175 | int retry = 1; | ||
176 | |||
177 | adv = phy_read(phydev, MII_ADVERTISE); | ||
178 | |||
179 | if (adv < 0) | ||
180 | return adv; | ||
181 | |||
182 | do { | ||
183 | lpa = phy_read(phydev, MII_LPA); | ||
184 | |||
185 | if (lpa < 0) | ||
186 | return lpa; | ||
187 | |||
188 | /* If both registers are equal, it is suspect but not | ||
189 | * impossible, hence a new try | ||
190 | */ | ||
191 | } while (lpa == adv && retry--); | ||
192 | |||
193 | lpa &= adv; | ||
194 | |||
195 | phydev->speed = SPEED_10; | ||
196 | phydev->duplex = DUPLEX_HALF; | ||
197 | phydev->pause = phydev->asym_pause = 0; | ||
198 | |||
199 | if (lpagb & (LPA_1000FULL | LPA_1000HALF)) { | ||
200 | phydev->speed = SPEED_1000; | ||
201 | |||
202 | if (lpagb & LPA_1000FULL) | ||
203 | phydev->duplex = DUPLEX_FULL; | ||
204 | } else if (lpa & (LPA_100FULL | LPA_100HALF)) { | ||
205 | phydev->speed = SPEED_100; | ||
206 | |||
207 | if (lpa & LPA_100FULL) | ||
208 | phydev->duplex = DUPLEX_FULL; | ||
209 | } else { | ||
210 | if (lpa & LPA_10FULL) | ||
211 | phydev->duplex = DUPLEX_FULL; | ||
212 | } | ||
213 | |||
214 | if (phydev->duplex == DUPLEX_FULL) { | ||
215 | phydev->pause = lpa & LPA_PAUSE_CAP ? 1 : 0; | ||
216 | phydev->asym_pause = lpa & LPA_PAUSE_ASYM ? 1 : 0; | ||
217 | } | ||
218 | } else { | ||
219 | int bmcr = phy_read(phydev, MII_BMCR); | ||
220 | |||
221 | if (bmcr < 0) | ||
222 | return bmcr; | ||
223 | |||
224 | if (bmcr & BMCR_FULLDPLX) | ||
225 | phydev->duplex = DUPLEX_FULL; | ||
226 | else | ||
227 | phydev->duplex = DUPLEX_HALF; | ||
228 | |||
229 | if (bmcr & BMCR_SPEED1000) | ||
230 | phydev->speed = SPEED_1000; | ||
231 | else if (bmcr & BMCR_SPEED100) | ||
232 | phydev->speed = SPEED_100; | ||
233 | else | ||
234 | phydev->speed = SPEED_10; | ||
235 | |||
236 | phydev->pause = phydev->asym_pause = 0; | ||
237 | } | ||
238 | |||
239 | return 0; | ||
240 | } | ||
241 | |||
125 | static int lxt973_probe(struct phy_device *phydev) | 242 | static int lxt973_probe(struct phy_device *phydev) |
126 | { | 243 | { |
127 | int val = phy_read(phydev, MII_LXT973_PCR); | 244 | int val = phy_read(phydev, MII_LXT973_PCR); |
@@ -175,6 +292,16 @@ static struct phy_driver lxt97x_driver[] = { | |||
175 | .driver = { .owner = THIS_MODULE,}, | 292 | .driver = { .owner = THIS_MODULE,}, |
176 | }, { | 293 | }, { |
177 | .phy_id = 0x00137a10, | 294 | .phy_id = 0x00137a10, |
295 | .name = "LXT973-A2", | ||
296 | .phy_id_mask = 0xffffffff, | ||
297 | .features = PHY_BASIC_FEATURES, | ||
298 | .flags = 0, | ||
299 | .probe = lxt973_probe, | ||
300 | .config_aneg = lxt973_config_aneg, | ||
301 | .read_status = lxt973a2_read_status, | ||
302 | .driver = { .owner = THIS_MODULE,}, | ||
303 | }, { | ||
304 | .phy_id = 0x00137a10, | ||
178 | .name = "LXT973", | 305 | .name = "LXT973", |
179 | .phy_id_mask = 0xfffffff0, | 306 | .phy_id_mask = 0xfffffff0, |
180 | .features = PHY_BASIC_FEATURES, | 307 | .features = PHY_BASIC_FEATURES, |