diff options
Diffstat (limited to 'net/dsa/mv88e6xxx.c')
-rw-r--r-- | net/dsa/mv88e6xxx.c | 145 |
1 files changed, 145 insertions, 0 deletions
diff --git a/net/dsa/mv88e6xxx.c b/net/dsa/mv88e6xxx.c index 13d2328a2406..aa6c609c59f2 100644 --- a/net/dsa/mv88e6xxx.c +++ b/net/dsa/mv88e6xxx.c | |||
@@ -165,6 +165,15 @@ int mv88e6xxx_config_prio(struct dsa_switch *ds) | |||
165 | return 0; | 165 | return 0; |
166 | } | 166 | } |
167 | 167 | ||
168 | int mv88e6xxx_set_addr_direct(struct dsa_switch *ds, u8 *addr) | ||
169 | { | ||
170 | REG_WRITE(REG_GLOBAL, 0x01, (addr[0] << 8) | addr[1]); | ||
171 | REG_WRITE(REG_GLOBAL, 0x02, (addr[2] << 8) | addr[3]); | ||
172 | REG_WRITE(REG_GLOBAL, 0x03, (addr[4] << 8) | addr[5]); | ||
173 | |||
174 | return 0; | ||
175 | } | ||
176 | |||
168 | int mv88e6xxx_set_addr_indirect(struct dsa_switch *ds, u8 *addr) | 177 | int mv88e6xxx_set_addr_indirect(struct dsa_switch *ds, u8 *addr) |
169 | { | 178 | { |
170 | int i; | 179 | int i; |
@@ -207,6 +216,142 @@ int mv88e6xxx_phy_write(struct dsa_switch *ds, int addr, int regnum, u16 val) | |||
207 | return 0; | 216 | return 0; |
208 | } | 217 | } |
209 | 218 | ||
219 | #ifdef CONFIG_NET_DSA_MV88E6XXX_NEED_PPU | ||
220 | static int mv88e6xxx_ppu_disable(struct dsa_switch *ds) | ||
221 | { | ||
222 | int ret; | ||
223 | int i; | ||
224 | |||
225 | ret = REG_READ(REG_GLOBAL, 0x04); | ||
226 | REG_WRITE(REG_GLOBAL, 0x04, ret & ~0x4000); | ||
227 | |||
228 | for (i = 0; i < 1000; i++) { | ||
229 | ret = REG_READ(REG_GLOBAL, 0x00); | ||
230 | msleep(1); | ||
231 | if ((ret & 0xc000) != 0xc000) | ||
232 | return 0; | ||
233 | } | ||
234 | |||
235 | return -ETIMEDOUT; | ||
236 | } | ||
237 | |||
238 | static int mv88e6xxx_ppu_enable(struct dsa_switch *ds) | ||
239 | { | ||
240 | int ret; | ||
241 | int i; | ||
242 | |||
243 | ret = REG_READ(REG_GLOBAL, 0x04); | ||
244 | REG_WRITE(REG_GLOBAL, 0x04, ret | 0x4000); | ||
245 | |||
246 | for (i = 0; i < 1000; i++) { | ||
247 | ret = REG_READ(REG_GLOBAL, 0x00); | ||
248 | msleep(1); | ||
249 | if ((ret & 0xc000) == 0xc000) | ||
250 | return 0; | ||
251 | } | ||
252 | |||
253 | return -ETIMEDOUT; | ||
254 | } | ||
255 | |||
256 | static void mv88e6xxx_ppu_reenable_work(struct work_struct *ugly) | ||
257 | { | ||
258 | struct mv88e6xxx_priv_state *ps; | ||
259 | |||
260 | ps = container_of(ugly, struct mv88e6xxx_priv_state, ppu_work); | ||
261 | if (mutex_trylock(&ps->ppu_mutex)) { | ||
262 | struct dsa_switch *ds = ((struct dsa_switch *)ps) - 1; | ||
263 | |||
264 | if (mv88e6xxx_ppu_enable(ds) == 0) | ||
265 | ps->ppu_disabled = 0; | ||
266 | mutex_unlock(&ps->ppu_mutex); | ||
267 | } | ||
268 | } | ||
269 | |||
270 | static void mv88e6xxx_ppu_reenable_timer(unsigned long _ps) | ||
271 | { | ||
272 | struct mv88e6xxx_priv_state *ps = (void *)_ps; | ||
273 | |||
274 | schedule_work(&ps->ppu_work); | ||
275 | } | ||
276 | |||
277 | static int mv88e6xxx_ppu_access_get(struct dsa_switch *ds) | ||
278 | { | ||
279 | struct mv88e6xxx_priv_state *ps = (void *)(ds + 1); | ||
280 | int ret; | ||
281 | |||
282 | mutex_lock(&ps->ppu_mutex); | ||
283 | |||
284 | /* | ||
285 | * If the PHY polling unit is enabled, disable it so that | ||
286 | * we can access the PHY registers. If it was already | ||
287 | * disabled, cancel the timer that is going to re-enable | ||
288 | * it. | ||
289 | */ | ||
290 | if (!ps->ppu_disabled) { | ||
291 | ret = mv88e6xxx_ppu_disable(ds); | ||
292 | if (ret < 0) { | ||
293 | mutex_unlock(&ps->ppu_mutex); | ||
294 | return ret; | ||
295 | } | ||
296 | ps->ppu_disabled = 1; | ||
297 | } else { | ||
298 | del_timer(&ps->ppu_timer); | ||
299 | ret = 0; | ||
300 | } | ||
301 | |||
302 | return ret; | ||
303 | } | ||
304 | |||
305 | static void mv88e6xxx_ppu_access_put(struct dsa_switch *ds) | ||
306 | { | ||
307 | struct mv88e6xxx_priv_state *ps = (void *)(ds + 1); | ||
308 | |||
309 | /* | ||
310 | * Schedule a timer to re-enable the PHY polling unit. | ||
311 | */ | ||
312 | mod_timer(&ps->ppu_timer, jiffies + msecs_to_jiffies(10)); | ||
313 | mutex_unlock(&ps->ppu_mutex); | ||
314 | } | ||
315 | |||
316 | void mv88e6xxx_ppu_state_init(struct dsa_switch *ds) | ||
317 | { | ||
318 | struct mv88e6xxx_priv_state *ps = (void *)(ds + 1); | ||
319 | |||
320 | mutex_init(&ps->ppu_mutex); | ||
321 | INIT_WORK(&ps->ppu_work, mv88e6xxx_ppu_reenable_work); | ||
322 | init_timer(&ps->ppu_timer); | ||
323 | ps->ppu_timer.data = (unsigned long)ps; | ||
324 | ps->ppu_timer.function = mv88e6xxx_ppu_reenable_timer; | ||
325 | } | ||
326 | |||
327 | int mv88e6xxx_phy_read_ppu(struct dsa_switch *ds, int addr, int regnum) | ||
328 | { | ||
329 | int ret; | ||
330 | |||
331 | ret = mv88e6xxx_ppu_access_get(ds); | ||
332 | if (ret >= 0) { | ||
333 | ret = mv88e6xxx_reg_read(ds, addr, regnum); | ||
334 | mv88e6xxx_ppu_access_put(ds); | ||
335 | } | ||
336 | |||
337 | return ret; | ||
338 | } | ||
339 | |||
340 | int mv88e6xxx_phy_write_ppu(struct dsa_switch *ds, int addr, | ||
341 | int regnum, u16 val) | ||
342 | { | ||
343 | int ret; | ||
344 | |||
345 | ret = mv88e6xxx_ppu_access_get(ds); | ||
346 | if (ret >= 0) { | ||
347 | ret = mv88e6xxx_reg_write(ds, addr, regnum, val); | ||
348 | mv88e6xxx_ppu_access_put(ds); | ||
349 | } | ||
350 | |||
351 | return ret; | ||
352 | } | ||
353 | #endif | ||
354 | |||
210 | void mv88e6xxx_poll_link(struct dsa_switch *ds) | 355 | void mv88e6xxx_poll_link(struct dsa_switch *ds) |
211 | { | 356 | { |
212 | int i; | 357 | int i; |