aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/base/power/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/base/power/main.c')
-rw-r--r--drivers/base/power/main.c675
1 files changed, 523 insertions, 152 deletions
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
index 45cc3d9eacb8..3250c5257b74 100644
--- a/drivers/base/power/main.c
+++ b/drivers/base/power/main.c
@@ -12,11 +12,9 @@
12 * and add it to the list of power-controlled devices. sysfs entries for 12 * and add it to the list of power-controlled devices. sysfs entries for
13 * controlling device power management will also be added. 13 * controlling device power management will also be added.
14 * 14 *
15 * A different set of lists than the global subsystem list are used to 15 * A separate list is used for keeping track of power info, because the power
16 * keep track of power info because we use different lists to hold 16 * domain dependencies may differ from the ancestral dependencies that the
17 * devices based on what stage of the power management process they 17 * subsystem list maintains.
18 * are in. The power domain dependencies may also differ from the
19 * ancestral dependencies that the subsystem list maintains.
20 */ 18 */
21 19
22#include <linux/device.h> 20#include <linux/device.h>
@@ -30,31 +28,40 @@
30#include "power.h" 28#include "power.h"
31 29
32/* 30/*
33 * The entries in the dpm_active list are in a depth first order, simply 31 * The entries in the dpm_list list are in a depth first order, simply
34 * because children are guaranteed to be discovered after parents, and 32 * because children are guaranteed to be discovered after parents, and
35 * are inserted at the back of the list on discovery. 33 * are inserted at the back of the list on discovery.
36 * 34 *
37 * All the other lists are kept in the same order, for consistency.
38 * However the lists aren't always traversed in the same order.
39 * Semaphores must be acquired from the top (i.e., front) down
40 * and released in the opposite order. Devices must be suspended
41 * from the bottom (i.e., end) up and resumed in the opposite order.
42 * That way no parent will be suspended while it still has an active
43 * child.
44 *
45 * Since device_pm_add() may be called with a device semaphore held, 35 * Since device_pm_add() may be called with a device semaphore held,
46 * we must never try to acquire a device semaphore while holding 36 * we must never try to acquire a device semaphore while holding
47 * dpm_list_mutex. 37 * dpm_list_mutex.
48 */ 38 */
49 39
50LIST_HEAD(dpm_active); 40LIST_HEAD(dpm_list);
51static LIST_HEAD(dpm_off);
52static LIST_HEAD(dpm_off_irq);
53 41
54static DEFINE_MUTEX(dpm_list_mtx); 42static DEFINE_MUTEX(dpm_list_mtx);
55 43
56/* 'true' if all devices have been suspended, protected by dpm_list_mtx */ 44/*
57static bool all_sleeping; 45 * Set once the preparation of devices for a PM transition has started, reset
46 * before starting to resume devices. Protected by dpm_list_mtx.
47 */
48static bool transition_started;
49
50/**
51 * device_pm_lock - lock the list of active devices used by the PM core
52 */
53void device_pm_lock(void)
54{
55 mutex_lock(&dpm_list_mtx);
56}
57
58/**
59 * device_pm_unlock - unlock the list of active devices used by the PM core
60 */
61void device_pm_unlock(void)
62{
63 mutex_unlock(&dpm_list_mtx);
64}
58 65
59/** 66/**
60 * device_pm_add - add a device to the list of active devices 67 * device_pm_add - add a device to the list of active devices
@@ -68,17 +75,25 @@ int device_pm_add(struct device *dev)
68 dev->bus ? dev->bus->name : "No Bus", 75 dev->bus ? dev->bus->name : "No Bus",
69 kobject_name(&dev->kobj)); 76 kobject_name(&dev->kobj));
70 mutex_lock(&dpm_list_mtx); 77 mutex_lock(&dpm_list_mtx);
71 if ((dev->parent && dev->parent->power.sleeping) || all_sleeping) { 78 if (dev->parent) {
72 if (dev->parent->power.sleeping) 79 if (dev->parent->power.status >= DPM_SUSPENDING) {
73 dev_warn(dev, "parent %s is sleeping\n", 80 dev_warn(dev, "parent %s is sleeping, will not add\n",
74 dev->parent->bus_id); 81 dev->parent->bus_id);
75 else 82 WARN_ON(true);
76 dev_warn(dev, "all devices are sleeping\n"); 83 }
84 } else if (transition_started) {
85 /*
86 * We refuse to register parentless devices while a PM
87 * transition is in progress in order to avoid leaving them
88 * unhandled down the road
89 */
77 WARN_ON(true); 90 WARN_ON(true);
78 } 91 }
79 error = dpm_sysfs_add(dev); 92 error = dpm_sysfs_add(dev);
80 if (!error) 93 if (!error) {
81 list_add_tail(&dev->power.entry, &dpm_active); 94 dev->power.status = DPM_ON;
95 list_add_tail(&dev->power.entry, &dpm_list);
96 }
82 mutex_unlock(&dpm_list_mtx); 97 mutex_unlock(&dpm_list_mtx);
83 return error; 98 return error;
84} 99}
@@ -100,73 +115,243 @@ void device_pm_remove(struct device *dev)
100 mutex_unlock(&dpm_list_mtx); 115 mutex_unlock(&dpm_list_mtx);
101} 116}
102 117
118/**
119 * pm_op - execute the PM operation appropiate for given PM event
120 * @dev: Device.
121 * @ops: PM operations to choose from.
122 * @state: PM transition of the system being carried out.
123 */
124static int pm_op(struct device *dev, struct pm_ops *ops, pm_message_t state)
125{
126 int error = 0;
127
128 switch (state.event) {
129#ifdef CONFIG_SUSPEND
130 case PM_EVENT_SUSPEND:
131 if (ops->suspend) {
132 error = ops->suspend(dev);
133 suspend_report_result(ops->suspend, error);
134 }
135 break;
136 case PM_EVENT_RESUME:
137 if (ops->resume) {
138 error = ops->resume(dev);
139 suspend_report_result(ops->resume, error);
140 }
141 break;
142#endif /* CONFIG_SUSPEND */
143#ifdef CONFIG_HIBERNATION
144 case PM_EVENT_FREEZE:
145 case PM_EVENT_QUIESCE:
146 if (ops->freeze) {
147 error = ops->freeze(dev);
148 suspend_report_result(ops->freeze, error);
149 }
150 break;
151 case PM_EVENT_HIBERNATE:
152 if (ops->poweroff) {
153 error = ops->poweroff(dev);
154 suspend_report_result(ops->poweroff, error);
155 }
156 break;
157 case PM_EVENT_THAW:
158 case PM_EVENT_RECOVER:
159 if (ops->thaw) {
160 error = ops->thaw(dev);
161 suspend_report_result(ops->thaw, error);
162 }
163 break;
164 case PM_EVENT_RESTORE:
165 if (ops->restore) {
166 error = ops->restore(dev);
167 suspend_report_result(ops->restore, error);
168 }
169 break;
170#endif /* CONFIG_HIBERNATION */
171 default:
172 error = -EINVAL;
173 }
174 return error;
175}
176
177/**
178 * pm_noirq_op - execute the PM operation appropiate for given PM event
179 * @dev: Device.
180 * @ops: PM operations to choose from.
181 * @state: PM transition of the system being carried out.
182 *
183 * The operation is executed with interrupts disabled by the only remaining
184 * functional CPU in the system.
185 */
186static int pm_noirq_op(struct device *dev, struct pm_ext_ops *ops,
187 pm_message_t state)
188{
189 int error = 0;
190
191 switch (state.event) {
192#ifdef CONFIG_SUSPEND
193 case PM_EVENT_SUSPEND:
194 if (ops->suspend_noirq) {
195 error = ops->suspend_noirq(dev);
196 suspend_report_result(ops->suspend_noirq, error);
197 }
198 break;
199 case PM_EVENT_RESUME:
200 if (ops->resume_noirq) {
201 error = ops->resume_noirq(dev);
202 suspend_report_result(ops->resume_noirq, error);
203 }
204 break;
205#endif /* CONFIG_SUSPEND */
206#ifdef CONFIG_HIBERNATION
207 case PM_EVENT_FREEZE:
208 case PM_EVENT_QUIESCE:
209 if (ops->freeze_noirq) {
210 error = ops->freeze_noirq(dev);
211 suspend_report_result(ops->freeze_noirq, error);
212 }
213 break;
214 case PM_EVENT_HIBERNATE:
215 if (ops->poweroff_noirq) {
216 error = ops->poweroff_noirq(dev);
217 suspend_report_result(ops->poweroff_noirq, error);
218 }
219 break;
220 case PM_EVENT_THAW:
221 case PM_EVENT_RECOVER:
222 if (ops->thaw_noirq) {
223 error = ops->thaw_noirq(dev);
224 suspend_report_result(ops->thaw_noirq, error);
225 }
226 break;
227 case PM_EVENT_RESTORE:
228 if (ops->restore_noirq) {
229 error = ops->restore_noirq(dev);
230 suspend_report_result(ops->restore_noirq, error);
231 }
232 break;
233#endif /* CONFIG_HIBERNATION */
234 default:
235 error = -EINVAL;
236 }
237 return error;
238}
239
240static char *pm_verb(int event)
241{
242 switch (event) {
243 case PM_EVENT_SUSPEND:
244 return "suspend";
245 case PM_EVENT_RESUME:
246 return "resume";
247 case PM_EVENT_FREEZE:
248 return "freeze";
249 case PM_EVENT_QUIESCE:
250 return "quiesce";
251 case PM_EVENT_HIBERNATE:
252 return "hibernate";
253 case PM_EVENT_THAW:
254 return "thaw";
255 case PM_EVENT_RESTORE:
256 return "restore";
257 case PM_EVENT_RECOVER:
258 return "recover";
259 default:
260 return "(unknown PM event)";
261 }
262}
263
264static void pm_dev_dbg(struct device *dev, pm_message_t state, char *info)
265{
266 dev_dbg(dev, "%s%s%s\n", info, pm_verb(state.event),
267 ((state.event & PM_EVENT_SLEEP) && device_may_wakeup(dev)) ?
268 ", may wakeup" : "");
269}
270
271static void pm_dev_err(struct device *dev, pm_message_t state, char *info,
272 int error)
273{
274 printk(KERN_ERR "PM: Device %s failed to %s%s: error %d\n",
275 kobject_name(&dev->kobj), pm_verb(state.event), info, error);
276}
277
103/*------------------------- Resume routines -------------------------*/ 278/*------------------------- Resume routines -------------------------*/
104 279
105/** 280/**
106 * resume_device_early - Power on one device (early resume). 281 * resume_device_noirq - Power on one device (early resume).
107 * @dev: Device. 282 * @dev: Device.
283 * @state: PM transition of the system being carried out.
108 * 284 *
109 * Must be called with interrupts disabled. 285 * Must be called with interrupts disabled.
110 */ 286 */
111static int resume_device_early(struct device *dev) 287static int resume_device_noirq(struct device *dev, pm_message_t state)
112{ 288{
113 int error = 0; 289 int error = 0;
114 290
115 TRACE_DEVICE(dev); 291 TRACE_DEVICE(dev);
116 TRACE_RESUME(0); 292 TRACE_RESUME(0);
117 293
118 if (dev->bus && dev->bus->resume_early) { 294 if (!dev->bus)
119 dev_dbg(dev, "EARLY resume\n"); 295 goto End;
296
297 if (dev->bus->pm) {
298 pm_dev_dbg(dev, state, "EARLY ");
299 error = pm_noirq_op(dev, dev->bus->pm, state);
300 } else if (dev->bus->resume_early) {
301 pm_dev_dbg(dev, state, "legacy EARLY ");
120 error = dev->bus->resume_early(dev); 302 error = dev->bus->resume_early(dev);
121 } 303 }
122 304 End:
123 TRACE_RESUME(error); 305 TRACE_RESUME(error);
124 return error; 306 return error;
125} 307}
126 308
127/** 309/**
128 * dpm_power_up - Power on all regular (non-sysdev) devices. 310 * dpm_power_up - Power on all regular (non-sysdev) devices.
311 * @state: PM transition of the system being carried out.
129 * 312 *
130 * Walk the dpm_off_irq list and power each device up. This 313 * Execute the appropriate "noirq resume" callback for all devices marked
131 * is used for devices that required they be powered down with 314 * as DPM_OFF_IRQ.
132 * interrupts disabled. As devices are powered on, they are moved
133 * to the dpm_off list.
134 * 315 *
135 * Must be called with interrupts disabled and only one CPU running. 316 * Must be called with interrupts disabled and only one CPU running.
136 */ 317 */
137static void dpm_power_up(void) 318static void dpm_power_up(pm_message_t state)
138{ 319{
320 struct device *dev;
139 321
140 while (!list_empty(&dpm_off_irq)) { 322 list_for_each_entry(dev, &dpm_list, power.entry)
141 struct list_head *entry = dpm_off_irq.next; 323 if (dev->power.status > DPM_OFF) {
142 struct device *dev = to_device(entry); 324 int error;
143 325
144 list_move_tail(entry, &dpm_off); 326 dev->power.status = DPM_OFF;
145 resume_device_early(dev); 327 error = resume_device_noirq(dev, state);
146 } 328 if (error)
329 pm_dev_err(dev, state, " early", error);
330 }
147} 331}
148 332
149/** 333/**
150 * device_power_up - Turn on all devices that need special attention. 334 * device_power_up - Turn on all devices that need special attention.
335 * @state: PM transition of the system being carried out.
151 * 336 *
152 * Power on system devices, then devices that required we shut them down 337 * Power on system devices, then devices that required we shut them down
153 * with interrupts disabled. 338 * with interrupts disabled.
154 * 339 *
155 * Must be called with interrupts disabled. 340 * Must be called with interrupts disabled.
156 */ 341 */
157void device_power_up(void) 342void device_power_up(pm_message_t state)
158{ 343{
159 sysdev_resume(); 344 sysdev_resume();
160 dpm_power_up(); 345 dpm_power_up(state);
161} 346}
162EXPORT_SYMBOL_GPL(device_power_up); 347EXPORT_SYMBOL_GPL(device_power_up);
163 348
164/** 349/**
165 * resume_device - Restore state for one device. 350 * resume_device - Restore state for one device.
166 * @dev: Device. 351 * @dev: Device.
167 * 352 * @state: PM transition of the system being carried out.
168 */ 353 */
169static int resume_device(struct device *dev) 354static int resume_device(struct device *dev, pm_message_t state)
170{ 355{
171 int error = 0; 356 int error = 0;
172 357
@@ -175,21 +360,40 @@ static int resume_device(struct device *dev)
175 360
176 down(&dev->sem); 361 down(&dev->sem);
177 362
178 if (dev->bus && dev->bus->resume) { 363 if (dev->bus) {
179 dev_dbg(dev,"resuming\n"); 364 if (dev->bus->pm) {
180 error = dev->bus->resume(dev); 365 pm_dev_dbg(dev, state, "");
366 error = pm_op(dev, &dev->bus->pm->base, state);
367 } else if (dev->bus->resume) {
368 pm_dev_dbg(dev, state, "legacy ");
369 error = dev->bus->resume(dev);
370 }
371 if (error)
372 goto End;
181 } 373 }
182 374
183 if (!error && dev->type && dev->type->resume) { 375 if (dev->type) {
184 dev_dbg(dev,"resuming\n"); 376 if (dev->type->pm) {
185 error = dev->type->resume(dev); 377 pm_dev_dbg(dev, state, "type ");
378 error = pm_op(dev, dev->type->pm, state);
379 } else if (dev->type->resume) {
380 pm_dev_dbg(dev, state, "legacy type ");
381 error = dev->type->resume(dev);
382 }
383 if (error)
384 goto End;
186 } 385 }
187 386
188 if (!error && dev->class && dev->class->resume) { 387 if (dev->class) {
189 dev_dbg(dev,"class resume\n"); 388 if (dev->class->pm) {
190 error = dev->class->resume(dev); 389 pm_dev_dbg(dev, state, "class ");
390 error = pm_op(dev, dev->class->pm, state);
391 } else if (dev->class->resume) {
392 pm_dev_dbg(dev, state, "legacy class ");
393 error = dev->class->resume(dev);
394 }
191 } 395 }
192 396 End:
193 up(&dev->sem); 397 up(&dev->sem);
194 398
195 TRACE_RESUME(error); 399 TRACE_RESUME(error);
@@ -198,78 +402,161 @@ static int resume_device(struct device *dev)
198 402
199/** 403/**
200 * dpm_resume - Resume every device. 404 * dpm_resume - Resume every device.
405 * @state: PM transition of the system being carried out.
201 * 406 *
202 * Resume the devices that have either not gone through 407 * Execute the appropriate "resume" callback for all devices the status of
203 * the late suspend, or that did go through it but also 408 * which indicates that they are inactive.
204 * went through the early resume. 409 */
410static void dpm_resume(pm_message_t state)
411{
412 struct list_head list;
413
414 INIT_LIST_HEAD(&list);
415 mutex_lock(&dpm_list_mtx);
416 transition_started = false;
417 while (!list_empty(&dpm_list)) {
418 struct device *dev = to_device(dpm_list.next);
419
420 get_device(dev);
421 if (dev->power.status >= DPM_OFF) {
422 int error;
423
424 dev->power.status = DPM_RESUMING;
425 mutex_unlock(&dpm_list_mtx);
426
427 error = resume_device(dev, state);
428
429 mutex_lock(&dpm_list_mtx);
430 if (error)
431 pm_dev_err(dev, state, "", error);
432 } else if (dev->power.status == DPM_SUSPENDING) {
433 /* Allow new children of the device to be registered */
434 dev->power.status = DPM_RESUMING;
435 }
436 if (!list_empty(&dev->power.entry))
437 list_move_tail(&dev->power.entry, &list);
438 put_device(dev);
439 }
440 list_splice(&list, &dpm_list);
441 mutex_unlock(&dpm_list_mtx);
442}
443
444/**
445 * complete_device - Complete a PM transition for given device
446 * @dev: Device.
447 * @state: PM transition of the system being carried out.
448 */
449static void complete_device(struct device *dev, pm_message_t state)
450{
451 down(&dev->sem);
452
453 if (dev->class && dev->class->pm && dev->class->pm->complete) {
454 pm_dev_dbg(dev, state, "completing class ");
455 dev->class->pm->complete(dev);
456 }
457
458 if (dev->type && dev->type->pm && dev->type->pm->complete) {
459 pm_dev_dbg(dev, state, "completing type ");
460 dev->type->pm->complete(dev);
461 }
462
463 if (dev->bus && dev->bus->pm && dev->bus->pm->base.complete) {
464 pm_dev_dbg(dev, state, "completing ");
465 dev->bus->pm->base.complete(dev);
466 }
467
468 up(&dev->sem);
469}
470
471/**
472 * dpm_complete - Complete a PM transition for all devices.
473 * @state: PM transition of the system being carried out.
205 * 474 *
206 * Take devices from the dpm_off_list, resume them, 475 * Execute the ->complete() callbacks for all devices that are not marked
207 * and put them on the dpm_locked list. 476 * as DPM_ON.
208 */ 477 */
209static void dpm_resume(void) 478static void dpm_complete(pm_message_t state)
210{ 479{
480 struct list_head list;
481
482 INIT_LIST_HEAD(&list);
211 mutex_lock(&dpm_list_mtx); 483 mutex_lock(&dpm_list_mtx);
212 all_sleeping = false; 484 while (!list_empty(&dpm_list)) {
213 while(!list_empty(&dpm_off)) { 485 struct device *dev = to_device(dpm_list.prev);
214 struct list_head *entry = dpm_off.next;
215 struct device *dev = to_device(entry);
216 486
217 list_move_tail(entry, &dpm_active); 487 get_device(dev);
218 dev->power.sleeping = false; 488 if (dev->power.status > DPM_ON) {
219 mutex_unlock(&dpm_list_mtx); 489 dev->power.status = DPM_ON;
220 resume_device(dev); 490 mutex_unlock(&dpm_list_mtx);
221 mutex_lock(&dpm_list_mtx); 491
492 complete_device(dev, state);
493
494 mutex_lock(&dpm_list_mtx);
495 }
496 if (!list_empty(&dev->power.entry))
497 list_move(&dev->power.entry, &list);
498 put_device(dev);
222 } 499 }
500 list_splice(&list, &dpm_list);
223 mutex_unlock(&dpm_list_mtx); 501 mutex_unlock(&dpm_list_mtx);
224} 502}
225 503
226/** 504/**
227 * device_resume - Restore state of each device in system. 505 * device_resume - Restore state of each device in system.
506 * @state: PM transition of the system being carried out.
228 * 507 *
229 * Resume all the devices, unlock them all, and allow new 508 * Resume all the devices, unlock them all, and allow new
230 * devices to be registered once again. 509 * devices to be registered once again.
231 */ 510 */
232void device_resume(void) 511void device_resume(pm_message_t state)
233{ 512{
234 might_sleep(); 513 might_sleep();
235 dpm_resume(); 514 dpm_resume(state);
515 dpm_complete(state);
236} 516}
237EXPORT_SYMBOL_GPL(device_resume); 517EXPORT_SYMBOL_GPL(device_resume);
238 518
239 519
240/*------------------------- Suspend routines -------------------------*/ 520/*------------------------- Suspend routines -------------------------*/
241 521
242static inline char *suspend_verb(u32 event) 522/**
523 * resume_event - return a PM message representing the resume event
524 * corresponding to given sleep state.
525 * @sleep_state: PM message representing a sleep state.
526 */
527static pm_message_t resume_event(pm_message_t sleep_state)
243{ 528{
244 switch (event) { 529 switch (sleep_state.event) {
245 case PM_EVENT_SUSPEND: return "suspend"; 530 case PM_EVENT_SUSPEND:
246 case PM_EVENT_FREEZE: return "freeze"; 531 return PMSG_RESUME;
247 case PM_EVENT_PRETHAW: return "prethaw"; 532 case PM_EVENT_FREEZE:
248 default: return "(unknown suspend event)"; 533 case PM_EVENT_QUIESCE:
534 return PMSG_RECOVER;
535 case PM_EVENT_HIBERNATE:
536 return PMSG_RESTORE;
249 } 537 }
250} 538 return PMSG_ON;
251
252static void
253suspend_device_dbg(struct device *dev, pm_message_t state, char *info)
254{
255 dev_dbg(dev, "%s%s%s\n", info, suspend_verb(state.event),
256 ((state.event == PM_EVENT_SUSPEND) && device_may_wakeup(dev)) ?
257 ", may wakeup" : "");
258} 539}
259 540
260/** 541/**
261 * suspend_device_late - Shut down one device (late suspend). 542 * suspend_device_noirq - Shut down one device (late suspend).
262 * @dev: Device. 543 * @dev: Device.
263 * @state: Power state device is entering. 544 * @state: PM transition of the system being carried out.
264 * 545 *
265 * This is called with interrupts off and only a single CPU running. 546 * This is called with interrupts off and only a single CPU running.
266 */ 547 */
267static int suspend_device_late(struct device *dev, pm_message_t state) 548static int suspend_device_noirq(struct device *dev, pm_message_t state)
268{ 549{
269 int error = 0; 550 int error = 0;
270 551
271 if (dev->bus && dev->bus->suspend_late) { 552 if (!dev->bus)
272 suspend_device_dbg(dev, state, "LATE "); 553 return 0;
554
555 if (dev->bus->pm) {
556 pm_dev_dbg(dev, state, "LATE ");
557 error = pm_noirq_op(dev, dev->bus->pm, state);
558 } else if (dev->bus->suspend_late) {
559 pm_dev_dbg(dev, state, "legacy LATE ");
273 error = dev->bus->suspend_late(dev, state); 560 error = dev->bus->suspend_late(dev, state);
274 suspend_report_result(dev->bus->suspend_late, error); 561 suspend_report_result(dev->bus->suspend_late, error);
275 } 562 }
@@ -278,37 +565,30 @@ static int suspend_device_late(struct device *dev, pm_message_t state)
278 565
279/** 566/**
280 * device_power_down - Shut down special devices. 567 * device_power_down - Shut down special devices.
281 * @state: Power state to enter. 568 * @state: PM transition of the system being carried out.
282 * 569 *
283 * Power down devices that require interrupts to be disabled 570 * Power down devices that require interrupts to be disabled.
284 * and move them from the dpm_off list to the dpm_off_irq list.
285 * Then power down system devices. 571 * Then power down system devices.
286 * 572 *
287 * Must be called with interrupts disabled and only one CPU running. 573 * Must be called with interrupts disabled and only one CPU running.
288 */ 574 */
289int device_power_down(pm_message_t state) 575int device_power_down(pm_message_t state)
290{ 576{
577 struct device *dev;
291 int error = 0; 578 int error = 0;
292 579
293 while (!list_empty(&dpm_off)) { 580 list_for_each_entry_reverse(dev, &dpm_list, power.entry) {
294 struct list_head *entry = dpm_off.prev; 581 error = suspend_device_noirq(dev, state);
295 struct device *dev = to_device(entry);
296
297 error = suspend_device_late(dev, state);
298 if (error) { 582 if (error) {
299 printk(KERN_ERR "Could not power down device %s: " 583 pm_dev_err(dev, state, " late", error);
300 "error %d\n",
301 kobject_name(&dev->kobj), error);
302 break; 584 break;
303 } 585 }
304 if (!list_empty(&dev->power.entry)) 586 dev->power.status = DPM_OFF_IRQ;
305 list_move(&dev->power.entry, &dpm_off_irq);
306 } 587 }
307
308 if (!error) 588 if (!error)
309 error = sysdev_suspend(state); 589 error = sysdev_suspend(state);
310 if (error) 590 if (error)
311 dpm_power_up(); 591 dpm_power_up(resume_event(state));
312 return error; 592 return error;
313} 593}
314EXPORT_SYMBOL_GPL(device_power_down); 594EXPORT_SYMBOL_GPL(device_power_down);
@@ -316,7 +596,7 @@ EXPORT_SYMBOL_GPL(device_power_down);
316/** 596/**
317 * suspend_device - Save state of one device. 597 * suspend_device - Save state of one device.
318 * @dev: Device. 598 * @dev: Device.
319 * @state: Power state device is entering. 599 * @state: PM transition of the system being carried out.
320 */ 600 */
321static int suspend_device(struct device *dev, pm_message_t state) 601static int suspend_device(struct device *dev, pm_message_t state)
322{ 602{
@@ -324,24 +604,43 @@ static int suspend_device(struct device *dev, pm_message_t state)
324 604
325 down(&dev->sem); 605 down(&dev->sem);
326 606
327 if (dev->class && dev->class->suspend) { 607 if (dev->class) {
328 suspend_device_dbg(dev, state, "class "); 608 if (dev->class->pm) {
329 error = dev->class->suspend(dev, state); 609 pm_dev_dbg(dev, state, "class ");
330 suspend_report_result(dev->class->suspend, error); 610 error = pm_op(dev, dev->class->pm, state);
611 } else if (dev->class->suspend) {
612 pm_dev_dbg(dev, state, "legacy class ");
613 error = dev->class->suspend(dev, state);
614 suspend_report_result(dev->class->suspend, error);
615 }
616 if (error)
617 goto End;
331 } 618 }
332 619
333 if (!error && dev->type && dev->type->suspend) { 620 if (dev->type) {
334 suspend_device_dbg(dev, state, "type "); 621 if (dev->type->pm) {
335 error = dev->type->suspend(dev, state); 622 pm_dev_dbg(dev, state, "type ");
336 suspend_report_result(dev->type->suspend, error); 623 error = pm_op(dev, dev->type->pm, state);
624 } else if (dev->type->suspend) {
625 pm_dev_dbg(dev, state, "legacy type ");
626 error = dev->type->suspend(dev, state);
627 suspend_report_result(dev->type->suspend, error);
628 }
629 if (error)
630 goto End;
337 } 631 }
338 632
339 if (!error && dev->bus && dev->bus->suspend) { 633 if (dev->bus) {
340 suspend_device_dbg(dev, state, ""); 634 if (dev->bus->pm) {
341 error = dev->bus->suspend(dev, state); 635 pm_dev_dbg(dev, state, "");
342 suspend_report_result(dev->bus->suspend, error); 636 error = pm_op(dev, &dev->bus->pm->base, state);
637 } else if (dev->bus->suspend) {
638 pm_dev_dbg(dev, state, "legacy ");
639 error = dev->bus->suspend(dev, state);
640 suspend_report_result(dev->bus->suspend, error);
641 }
343 } 642 }
344 643 End:
345 up(&dev->sem); 644 up(&dev->sem);
346 645
347 return error; 646 return error;
@@ -349,67 +648,139 @@ static int suspend_device(struct device *dev, pm_message_t state)
349 648
350/** 649/**
351 * dpm_suspend - Suspend every device. 650 * dpm_suspend - Suspend every device.
352 * @state: Power state to put each device in. 651 * @state: PM transition of the system being carried out.
353 *
354 * Walk the dpm_locked list. Suspend each device and move it
355 * to the dpm_off list.
356 * 652 *
357 * (For historical reasons, if it returns -EAGAIN, that used to mean 653 * Execute the appropriate "suspend" callbacks for all devices.
358 * that the device would be called again with interrupts disabled.
359 * These days, we use the "suspend_late()" callback for that, so we
360 * print a warning and consider it an error).
361 */ 654 */
362static int dpm_suspend(pm_message_t state) 655static int dpm_suspend(pm_message_t state)
363{ 656{
657 struct list_head list;
364 int error = 0; 658 int error = 0;
365 659
660 INIT_LIST_HEAD(&list);
366 mutex_lock(&dpm_list_mtx); 661 mutex_lock(&dpm_list_mtx);
367 while (!list_empty(&dpm_active)) { 662 while (!list_empty(&dpm_list)) {
368 struct list_head *entry = dpm_active.prev; 663 struct device *dev = to_device(dpm_list.prev);
369 struct device *dev = to_device(entry);
370 664
371 WARN_ON(dev->parent && dev->parent->power.sleeping); 665 get_device(dev);
372
373 dev->power.sleeping = true;
374 mutex_unlock(&dpm_list_mtx); 666 mutex_unlock(&dpm_list_mtx);
667
375 error = suspend_device(dev, state); 668 error = suspend_device(dev, state);
669
376 mutex_lock(&dpm_list_mtx); 670 mutex_lock(&dpm_list_mtx);
377 if (error) { 671 if (error) {
378 printk(KERN_ERR "Could not suspend device %s: " 672 pm_dev_err(dev, state, "", error);
379 "error %d%s\n", 673 put_device(dev);
380 kobject_name(&dev->kobj),
381 error,
382 (error == -EAGAIN ?
383 " (please convert to suspend_late)" :
384 ""));
385 dev->power.sleeping = false;
386 break; 674 break;
387 } 675 }
676 dev->power.status = DPM_OFF;
388 if (!list_empty(&dev->power.entry)) 677 if (!list_empty(&dev->power.entry))
389 list_move(&dev->power.entry, &dpm_off); 678 list_move(&dev->power.entry, &list);
679 put_device(dev);
390 } 680 }
391 if (!error) 681 list_splice(&list, dpm_list.prev);
392 all_sleeping = true;
393 mutex_unlock(&dpm_list_mtx); 682 mutex_unlock(&dpm_list_mtx);
683 return error;
684}
685
686/**
687 * prepare_device - Execute the ->prepare() callback(s) for given device.
688 * @dev: Device.
689 * @state: PM transition of the system being carried out.
690 */
691static int prepare_device(struct device *dev, pm_message_t state)
692{
693 int error = 0;
694
695 down(&dev->sem);
696
697 if (dev->bus && dev->bus->pm && dev->bus->pm->base.prepare) {
698 pm_dev_dbg(dev, state, "preparing ");
699 error = dev->bus->pm->base.prepare(dev);
700 suspend_report_result(dev->bus->pm->base.prepare, error);
701 if (error)
702 goto End;
703 }
704
705 if (dev->type && dev->type->pm && dev->type->pm->prepare) {
706 pm_dev_dbg(dev, state, "preparing type ");
707 error = dev->type->pm->prepare(dev);
708 suspend_report_result(dev->type->pm->prepare, error);
709 if (error)
710 goto End;
711 }
712
713 if (dev->class && dev->class->pm && dev->class->pm->prepare) {
714 pm_dev_dbg(dev, state, "preparing class ");
715 error = dev->class->pm->prepare(dev);
716 suspend_report_result(dev->class->pm->prepare, error);
717 }
718 End:
719 up(&dev->sem);
720
721 return error;
722}
723
724/**
725 * dpm_prepare - Prepare all devices for a PM transition.
726 * @state: PM transition of the system being carried out.
727 *
728 * Execute the ->prepare() callback for all devices.
729 */
730static int dpm_prepare(pm_message_t state)
731{
732 struct list_head list;
733 int error = 0;
734
735 INIT_LIST_HEAD(&list);
736 mutex_lock(&dpm_list_mtx);
737 transition_started = true;
738 while (!list_empty(&dpm_list)) {
739 struct device *dev = to_device(dpm_list.next);
740
741 get_device(dev);
742 dev->power.status = DPM_PREPARING;
743 mutex_unlock(&dpm_list_mtx);
394 744
745 error = prepare_device(dev, state);
746
747 mutex_lock(&dpm_list_mtx);
748 if (error) {
749 dev->power.status = DPM_ON;
750 if (error == -EAGAIN) {
751 put_device(dev);
752 continue;
753 }
754 printk(KERN_ERR "PM: Failed to prepare device %s "
755 "for power transition: error %d\n",
756 kobject_name(&dev->kobj), error);
757 put_device(dev);
758 break;
759 }
760 dev->power.status = DPM_SUSPENDING;
761 if (!list_empty(&dev->power.entry))
762 list_move_tail(&dev->power.entry, &list);
763 put_device(dev);
764 }
765 list_splice(&list, &dpm_list);
766 mutex_unlock(&dpm_list_mtx);
395 return error; 767 return error;
396} 768}
397 769
398/** 770/**
399 * device_suspend - Save state and stop all devices in system. 771 * device_suspend - Save state and stop all devices in system.
400 * @state: new power management state 772 * @state: PM transition of the system being carried out.
401 * 773 *
402 * Prevent new devices from being registered, then lock all devices 774 * Prepare and suspend all devices.
403 * and suspend them.
404 */ 775 */
405int device_suspend(pm_message_t state) 776int device_suspend(pm_message_t state)
406{ 777{
407 int error; 778 int error;
408 779
409 might_sleep(); 780 might_sleep();
410 error = dpm_suspend(state); 781 error = dpm_prepare(state);
411 if (error) 782 if (!error)
412 device_resume(); 783 error = dpm_suspend(state);
413 return error; 784 return error;
414} 785}
415EXPORT_SYMBOL_GPL(device_suspend); 786EXPORT_SYMBOL_GPL(device_suspend);
/span> port*4, 1); /* for paladium */ if (CHIP_REV_IS_EMUL(bp)) { /* Use lane 1 (of lanes 0-3) */ REG_WR(bp, NIG_REG_XGXS_LANE_SEL_P0 + port*4, 1); REG_WR(bp, NIG_REG_XGXS_SERDES0_MODE_SEL + port*4, 1); } /* for fpga */ else if (CHIP_REV_IS_FPGA(bp)) { /* Use lane 1 (of lanes 0-3) */ DP(NETIF_MSG_LINK, "bnx2x_emac_enable: Setting FPGA\n"); REG_WR(bp, NIG_REG_XGXS_LANE_SEL_P0 + port*4, 1); REG_WR(bp, NIG_REG_XGXS_SERDES0_MODE_SEL + port*4, 0); } else /* ASIC */ if (vars->phy_flags & PHY_XGXS_FLAG) { u32 ser_lane = ((params->lane_config & PORT_HW_CFG_LANE_SWAP_CFG_MASTER_MASK) >> PORT_HW_CFG_LANE_SWAP_CFG_MASTER_SHIFT); DP(NETIF_MSG_LINK, "XGXS\n"); /* select the master lanes (out of 0-3) */ REG_WR(bp, NIG_REG_XGXS_LANE_SEL_P0 + port*4, ser_lane); /* select XGXS */ REG_WR(bp, NIG_REG_XGXS_SERDES0_MODE_SEL + port*4, 1); } else { /* SerDes */ DP(NETIF_MSG_LINK, "SerDes\n"); /* select SerDes */ REG_WR(bp, NIG_REG_XGXS_SERDES0_MODE_SEL + port*4, 0); } bnx2x_bits_en(bp, emac_base + EMAC_REG_EMAC_RX_MODE, EMAC_RX_MODE_RESET); bnx2x_bits_en(bp, emac_base + EMAC_REG_EMAC_TX_MODE, EMAC_TX_MODE_RESET); if (CHIP_REV_IS_SLOW(bp)) { /* config GMII mode */ val = REG_RD(bp, emac_base + EMAC_REG_EMAC_MODE); EMAC_WR(bp, EMAC_REG_EMAC_MODE, (val | EMAC_MODE_PORT_GMII)); } else { /* ASIC */ /* pause enable/disable */ bnx2x_bits_dis(bp, emac_base + EMAC_REG_EMAC_RX_MODE, EMAC_RX_MODE_FLOW_EN); if (vars->flow_ctrl & BNX2X_FLOW_CTRL_RX) bnx2x_bits_en(bp, emac_base + EMAC_REG_EMAC_RX_MODE, EMAC_RX_MODE_FLOW_EN); bnx2x_bits_dis(bp, emac_base + EMAC_REG_EMAC_TX_MODE, (EMAC_TX_MODE_EXT_PAUSE_EN | EMAC_TX_MODE_FLOW_EN)); if (vars->flow_ctrl & BNX2X_FLOW_CTRL_TX) bnx2x_bits_en(bp, emac_base + EMAC_REG_EMAC_TX_MODE, (EMAC_TX_MODE_EXT_PAUSE_EN | EMAC_TX_MODE_FLOW_EN)); } /* KEEP_VLAN_TAG, promiscuous */ val = REG_RD(bp, emac_base + EMAC_REG_EMAC_RX_MODE); val |= EMAC_RX_MODE_KEEP_VLAN_TAG | EMAC_RX_MODE_PROMISCUOUS; EMAC_WR(bp, EMAC_REG_EMAC_RX_MODE, val); /* Set Loopback */ val = REG_RD(bp, emac_base + EMAC_REG_EMAC_MODE); if (lb) val |= 0x810; else val &= ~0x810; EMAC_WR(bp, EMAC_REG_EMAC_MODE, val); /* enable emac */ REG_WR(bp, NIG_REG_NIG_EMAC0_EN + port*4, 1); /* enable emac for jumbo packets */ EMAC_WR(bp, EMAC_REG_EMAC_RX_MTU_SIZE, (EMAC_RX_MTU_SIZE_JUMBO_ENA | (ETH_MAX_JUMBO_PACKET_SIZE + ETH_OVREHEAD))); /* strip CRC */ REG_WR(bp, NIG_REG_NIG_INGRESS_EMAC0_NO_CRC + port*4, 0x1); /* disable the NIG in/out to the bmac */ REG_WR(bp, NIG_REG_BMAC0_IN_EN + port*4, 0x0); REG_WR(bp, NIG_REG_BMAC0_PAUSE_OUT_EN + port*4, 0x0); REG_WR(bp, NIG_REG_BMAC0_OUT_EN + port*4, 0x0); /* enable the NIG in/out to the emac */ REG_WR(bp, NIG_REG_EMAC0_IN_EN + port*4, 0x1); val = 0; if (vars->flow_ctrl & BNX2X_FLOW_CTRL_TX) val = 1; REG_WR(bp, NIG_REG_EMAC0_PAUSE_OUT_EN + port*4, val); REG_WR(bp, NIG_REG_EGRESS_EMAC0_OUT_EN + port*4, 0x1); if (CHIP_REV_IS_EMUL(bp)) { /* take the BigMac out of reset */ REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_SET, (MISC_REGISTERS_RESET_REG_2_RST_BMAC0 << port)); /* enable access for bmac registers */ REG_WR(bp, NIG_REG_BMAC0_REGS_OUT_EN + port*4, 0x1); } else REG_WR(bp, NIG_REG_BMAC0_REGS_OUT_EN + port*4, 0x0); vars->mac_type = MAC_TYPE_EMAC; return 0; } static u8 bnx2x_bmac_enable(struct link_params *params, struct link_vars *vars, u8 is_lb) { struct bnx2x *bp = params->bp; u8 port = params->port; u32 bmac_addr = port ? NIG_REG_INGRESS_BMAC1_MEM : NIG_REG_INGRESS_BMAC0_MEM; u32 wb_data[2]; u32 val; DP(NETIF_MSG_LINK, "Enabling BigMAC\n"); /* reset and unreset the BigMac */ REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_CLEAR, (MISC_REGISTERS_RESET_REG_2_RST_BMAC0 << port)); msleep(1); REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_SET, (MISC_REGISTERS_RESET_REG_2_RST_BMAC0 << port)); /* enable access for bmac registers */ REG_WR(bp, NIG_REG_BMAC0_REGS_OUT_EN + port*4, 0x1); /* XGXS control */ wb_data[0] = 0x3c; wb_data[1] = 0; REG_WR_DMAE(bp, bmac_addr + BIGMAC_REGISTER_BMAC_XGXS_CONTROL, wb_data, 2); /* tx MAC SA */ wb_data[0] = ((params->mac_addr[2] << 24) | (params->mac_addr[3] << 16) | (params->mac_addr[4] << 8) | params->mac_addr[5]); wb_data[1] = ((params->mac_addr[0] << 8) | params->mac_addr[1]); REG_WR_DMAE(bp, bmac_addr + BIGMAC_REGISTER_TX_SOURCE_ADDR, wb_data, 2); /* tx control */ val = 0xc0; if (vars->flow_ctrl & BNX2X_FLOW_CTRL_TX) val |= 0x800000; wb_data[0] = val; wb_data[1] = 0; REG_WR_DMAE(bp, bmac_addr + BIGMAC_REGISTER_TX_CONTROL, wb_data, 2); /* mac control */ val = 0x3; if (is_lb) { val |= 0x4; DP(NETIF_MSG_LINK, "enable bmac loopback\n"); } wb_data[0] = val; wb_data[1] = 0; REG_WR_DMAE(bp, bmac_addr + BIGMAC_REGISTER_BMAC_CONTROL, wb_data, 2); /* set rx mtu */ wb_data[0] = ETH_MAX_JUMBO_PACKET_SIZE + ETH_OVREHEAD; wb_data[1] = 0; REG_WR_DMAE(bp, bmac_addr + BIGMAC_REGISTER_RX_MAX_SIZE, wb_data, 2); /* rx control set to don't strip crc */ val = 0x14; if (vars->flow_ctrl & BNX2X_FLOW_CTRL_RX) val |= 0x20; wb_data[0] = val; wb_data[1] = 0; REG_WR_DMAE(bp, bmac_addr + BIGMAC_REGISTER_RX_CONTROL, wb_data, 2); /* set tx mtu */ wb_data[0] = ETH_MAX_JUMBO_PACKET_SIZE + ETH_OVREHEAD; wb_data[1] = 0; REG_WR_DMAE(bp, bmac_addr + BIGMAC_REGISTER_TX_MAX_SIZE, wb_data, 2); /* set cnt max size */ wb_data[0] = ETH_MAX_JUMBO_PACKET_SIZE + ETH_OVREHEAD; wb_data[1] = 0; REG_WR_DMAE(bp, bmac_addr + BIGMAC_REGISTER_CNT_MAX_SIZE, wb_data, 2); /* configure safc */ wb_data[0] = 0x1000200; wb_data[1] = 0; REG_WR_DMAE(bp, bmac_addr + BIGMAC_REGISTER_RX_LLFC_MSG_FLDS, wb_data, 2); /* fix for emulation */ if (CHIP_REV_IS_EMUL(bp)) { wb_data[0] = 0xf000; wb_data[1] = 0; REG_WR_DMAE(bp, bmac_addr + BIGMAC_REGISTER_TX_PAUSE_THRESHOLD, wb_data, 2); } REG_WR(bp, NIG_REG_XGXS_SERDES0_MODE_SEL + port*4, 0x1); REG_WR(bp, NIG_REG_XGXS_LANE_SEL_P0 + port*4, 0x0); REG_WR(bp, NIG_REG_EGRESS_EMAC0_PORT + port*4, 0x0); val = 0; if (vars->flow_ctrl & BNX2X_FLOW_CTRL_TX) val = 1; REG_WR(bp, NIG_REG_BMAC0_PAUSE_OUT_EN + port*4, val); REG_WR(bp, NIG_REG_EGRESS_EMAC0_OUT_EN + port*4, 0x0); REG_WR(bp, NIG_REG_EMAC0_IN_EN + port*4, 0x0); REG_WR(bp, NIG_REG_EMAC0_PAUSE_OUT_EN + port*4, 0x0); REG_WR(bp, NIG_REG_BMAC0_IN_EN + port*4, 0x1); REG_WR(bp, NIG_REG_BMAC0_OUT_EN + port*4, 0x1); vars->mac_type = MAC_TYPE_BMAC; return 0; } static void bnx2x_phy_deassert(struct link_params *params, u8 phy_flags) { struct bnx2x *bp = params->bp; u32 val; if (phy_flags & PHY_XGXS_FLAG) { DP(NETIF_MSG_LINK, "bnx2x_phy_deassert:XGXS\n"); val = XGXS_RESET_BITS; } else { /* SerDes */ DP(NETIF_MSG_LINK, "bnx2x_phy_deassert:SerDes\n"); val = SERDES_RESET_BITS; } val = val << (params->port*16); /* reset and unreset the SerDes/XGXS */ REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_3_CLEAR, val); udelay(500); REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_3_SET, val); bnx2x_set_phy_mdio(params, phy_flags); } void bnx2x_link_status_update(struct link_params *params, struct link_vars *vars) { struct bnx2x *bp = params->bp; u8 link_10g; u8 port = params->port; if (params->switch_cfg == SWITCH_CFG_1G) vars->phy_flags = PHY_SERDES_FLAG; else vars->phy_flags = PHY_XGXS_FLAG; vars->link_status = REG_RD(bp, params->shmem_base + offsetof(struct shmem_region, port_mb[port].link_status)); vars->link_up = (vars->link_status & LINK_STATUS_LINK_UP); if (vars->link_up) { DP(NETIF_MSG_LINK, "phy link up\n"); vars->phy_link_up = 1; vars->duplex = DUPLEX_FULL; switch (vars->link_status & LINK_STATUS_SPEED_AND_DUPLEX_MASK) { case LINK_10THD: vars->duplex = DUPLEX_HALF; /* fall thru */ case LINK_10TFD: vars->line_speed = SPEED_10; break; case LINK_100TXHD: vars->duplex = DUPLEX_HALF; /* fall thru */ case LINK_100T4: case LINK_100TXFD: vars->line_speed = SPEED_100; break; case LINK_1000THD: vars->duplex = DUPLEX_HALF; /* fall thru */ case LINK_1000TFD: vars->line_speed = SPEED_1000; break; case LINK_2500THD: vars->duplex = DUPLEX_HALF; /* fall thru */ case LINK_2500TFD: vars->line_speed = SPEED_2500; break; case LINK_10GTFD: vars->line_speed = SPEED_10000; break; case LINK_12GTFD: vars->line_speed = SPEED_12000; break; case LINK_12_5GTFD: vars->line_speed = SPEED_12500; break; case LINK_13GTFD: vars->line_speed = SPEED_13000; break; case LINK_15GTFD: vars->line_speed = SPEED_15000; break; case LINK_16GTFD: vars->line_speed = SPEED_16000; break; default: break; } if (vars->link_status & LINK_STATUS_TX_FLOW_CONTROL_ENABLED) vars->flow_ctrl |= BNX2X_FLOW_CTRL_TX; else vars->flow_ctrl &= ~BNX2X_FLOW_CTRL_TX; if (vars->link_status & LINK_STATUS_RX_FLOW_CONTROL_ENABLED) vars->flow_ctrl |= BNX2X_FLOW_CTRL_RX; else vars->flow_ctrl &= ~BNX2X_FLOW_CTRL_RX; if (vars->phy_flags & PHY_XGXS_FLAG) { if (vars->line_speed && ((vars->line_speed == SPEED_10) || (vars->line_speed == SPEED_100))) { vars->phy_flags |= PHY_SGMII_FLAG; } else { vars->phy_flags &= ~PHY_SGMII_FLAG; } } /* anything 10 and over uses the bmac */ link_10g = ((vars->line_speed == SPEED_10000) || (vars->line_speed == SPEED_12000) || (vars->line_speed == SPEED_12500) || (vars->line_speed == SPEED_13000) || (vars->line_speed == SPEED_15000) || (vars->line_speed == SPEED_16000)); if (link_10g) vars->mac_type = MAC_TYPE_BMAC; else vars->mac_type = MAC_TYPE_EMAC; } else { /* link down */ DP(NETIF_MSG_LINK, "phy link down\n"); vars->phy_link_up = 0; vars->line_speed = 0; vars->duplex = DUPLEX_FULL; vars->flow_ctrl = BNX2X_FLOW_CTRL_NONE; /* indicate no mac active */ vars->mac_type = MAC_TYPE_NONE; } DP(NETIF_MSG_LINK, "link_status 0x%x phy_link_up %x\n", vars->link_status, vars->phy_link_up); DP(NETIF_MSG_LINK, "line_speed %x duplex %x flow_ctrl 0x%x\n", vars->line_speed, vars->duplex, vars->flow_ctrl); } static void bnx2x_update_mng(struct link_params *params, u32 link_status) { struct bnx2x *bp = params->bp; REG_WR(bp, params->shmem_base + offsetof(struct shmem_region, port_mb[params->port].link_status), link_status); } static void bnx2x_bmac_rx_disable(struct bnx2x *bp, u8 port) { u32 bmac_addr = port ? NIG_REG_INGRESS_BMAC1_MEM : NIG_REG_INGRESS_BMAC0_MEM; u32 wb_data[2]; u32 nig_bmac_enable = REG_RD(bp, NIG_REG_BMAC0_REGS_OUT_EN + port*4); /* Only if the bmac is out of reset */ if (REG_RD(bp, MISC_REG_RESET_REG_2) & (MISC_REGISTERS_RESET_REG_2_RST_BMAC0 << port) && nig_bmac_enable) { /* Clear Rx Enable bit in BMAC_CONTROL register */ REG_RD_DMAE(bp, bmac_addr + BIGMAC_REGISTER_BMAC_CONTROL, wb_data, 2); wb_data[0] &= ~BMAC_CONTROL_RX_ENABLE; REG_WR_DMAE(bp, bmac_addr + BIGMAC_REGISTER_BMAC_CONTROL, wb_data, 2); msleep(1); } } static u8 bnx2x_pbf_update(struct link_params *params, u32 flow_ctrl, u32 line_speed) { struct bnx2x *bp = params->bp; u8 port = params->port; u32 init_crd, crd; u32 count = 1000; /* disable port */ REG_WR(bp, PBF_REG_DISABLE_NEW_TASK_PROC_P0 + port*4, 0x1); /* wait for init credit */ init_crd = REG_RD(bp, PBF_REG_P0_INIT_CRD + port*4); crd = REG_RD(bp, PBF_REG_P0_CREDIT + port*8); DP(NETIF_MSG_LINK, "init_crd 0x%x crd 0x%x\n", init_crd, crd); while ((init_crd != crd) && count) { msleep(5); crd = REG_RD(bp, PBF_REG_P0_CREDIT + port*8); count--; } crd = REG_RD(bp, PBF_REG_P0_CREDIT + port*8); if (init_crd != crd) { DP(NETIF_MSG_LINK, "BUG! init_crd 0x%x != crd 0x%x\n", init_crd, crd); return -EINVAL; } if (flow_ctrl & BNX2X_FLOW_CTRL_RX || line_speed == SPEED_10 || line_speed == SPEED_100 || line_speed == SPEED_1000 || line_speed == SPEED_2500) { REG_WR(bp, PBF_REG_P0_PAUSE_ENABLE + port*4, 1); /* update threshold */ REG_WR(bp, PBF_REG_P0_ARB_THRSH + port*4, 0); /* update init credit */ init_crd = 778; /* (800-18-4) */ } else { u32 thresh = (ETH_MAX_JUMBO_PACKET_SIZE + ETH_OVREHEAD)/16; REG_WR(bp, PBF_REG_P0_PAUSE_ENABLE + port*4, 0); /* update threshold */ REG_WR(bp, PBF_REG_P0_ARB_THRSH + port*4, thresh); /* update init credit */ switch (line_speed) { case SPEED_10000: init_crd = thresh + 553 - 22; break; case SPEED_12000: init_crd = thresh + 664 - 22; break; case SPEED_13000: init_crd = thresh + 742 - 22; break; case SPEED_16000: init_crd = thresh + 778 - 22; break; default: DP(NETIF_MSG_LINK, "Invalid line_speed 0x%x\n", line_speed); return -EINVAL; } } REG_WR(bp, PBF_REG_P0_INIT_CRD + port*4, init_crd); DP(NETIF_MSG_LINK, "PBF updated to speed %d credit %d\n", line_speed, init_crd); /* probe the credit changes */ REG_WR(bp, PBF_REG_INIT_P0 + port*4, 0x1); msleep(5); REG_WR(bp, PBF_REG_INIT_P0 + port*4, 0x0); /* enable port */ REG_WR(bp, PBF_REG_DISABLE_NEW_TASK_PROC_P0 + port*4, 0x0); return 0; } static u32 bnx2x_get_emac_base(struct bnx2x *bp, u32 ext_phy_type, u8 port) { u32 emac_base; switch (ext_phy_type) { case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8072: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8726: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8727: /* All MDC/MDIO is directed through single EMAC */ if (REG_RD(bp, NIG_REG_PORT_SWAP)) emac_base = GRCBASE_EMAC0; else emac_base = GRCBASE_EMAC1; break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073: emac_base = (port) ? GRCBASE_EMAC0 : GRCBASE_EMAC1; break; default: emac_base = (port) ? GRCBASE_EMAC1 : GRCBASE_EMAC0; break; } return emac_base; } u8 bnx2x_cl45_write(struct bnx2x *bp, u8 port, u32 ext_phy_type, u8 phy_addr, u8 devad, u16 reg, u16 val) { u32 tmp, saved_mode; u8 i, rc = 0; u32 mdio_ctrl = bnx2x_get_emac_base(bp, ext_phy_type, port); /* set clause 45 mode, slow down the MDIO clock to 2.5MHz * (a value of 49==0x31) and make sure that the AUTO poll is off */ saved_mode = REG_RD(bp, mdio_ctrl + EMAC_REG_EMAC_MDIO_MODE); tmp = saved_mode & ~(EMAC_MDIO_MODE_AUTO_POLL | EMAC_MDIO_MODE_CLOCK_CNT); tmp |= (EMAC_MDIO_MODE_CLAUSE_45 | (49 << EMAC_MDIO_MODE_CLOCK_CNT_BITSHIFT)); REG_WR(bp, mdio_ctrl + EMAC_REG_EMAC_MDIO_MODE, tmp); REG_RD(bp, mdio_ctrl + EMAC_REG_EMAC_MDIO_MODE); udelay(40); /* address */ tmp = ((phy_addr << 21) | (devad << 16) | reg | EMAC_MDIO_COMM_COMMAND_ADDRESS | EMAC_MDIO_COMM_START_BUSY); REG_WR(bp, mdio_ctrl + EMAC_REG_EMAC_MDIO_COMM, tmp); for (i = 0; i < 50; i++) { udelay(10); tmp = REG_RD(bp, mdio_ctrl + EMAC_REG_EMAC_MDIO_COMM); if (!(tmp & EMAC_MDIO_COMM_START_BUSY)) { udelay(5); break; } } if (tmp & EMAC_MDIO_COMM_START_BUSY) { DP(NETIF_MSG_LINK, "write phy register failed\n"); rc = -EFAULT; } else { /* data */ tmp = ((phy_addr << 21) | (devad << 16) | val | EMAC_MDIO_COMM_COMMAND_WRITE_45 | EMAC_MDIO_COMM_START_BUSY); REG_WR(bp, mdio_ctrl + EMAC_REG_EMAC_MDIO_COMM, tmp); for (i = 0; i < 50; i++) { udelay(10); tmp = REG_RD(bp, mdio_ctrl + EMAC_REG_EMAC_MDIO_COMM); if (!(tmp & EMAC_MDIO_COMM_START_BUSY)) { udelay(5); break; } } if (tmp & EMAC_MDIO_COMM_START_BUSY) { DP(NETIF_MSG_LINK, "write phy register failed\n"); rc = -EFAULT; } } /* Restore the saved mode */ REG_WR(bp, mdio_ctrl + EMAC_REG_EMAC_MDIO_MODE, saved_mode); return rc; } u8 bnx2x_cl45_read(struct bnx2x *bp, u8 port, u32 ext_phy_type, u8 phy_addr, u8 devad, u16 reg, u16 *ret_val) { u32 val, saved_mode; u16 i; u8 rc = 0; u32 mdio_ctrl = bnx2x_get_emac_base(bp, ext_phy_type, port); /* set clause 45 mode, slow down the MDIO clock to 2.5MHz * (a value of 49==0x31) and make sure that the AUTO poll is off */ saved_mode = REG_RD(bp, mdio_ctrl + EMAC_REG_EMAC_MDIO_MODE); val = saved_mode & ((EMAC_MDIO_MODE_AUTO_POLL | EMAC_MDIO_MODE_CLOCK_CNT)); val |= (EMAC_MDIO_MODE_CLAUSE_45 | (49L << EMAC_MDIO_MODE_CLOCK_CNT_BITSHIFT)); REG_WR(bp, mdio_ctrl + EMAC_REG_EMAC_MDIO_MODE, val); REG_RD(bp, mdio_ctrl + EMAC_REG_EMAC_MDIO_MODE); udelay(40); /* address */ val = ((phy_addr << 21) | (devad << 16) | reg | EMAC_MDIO_COMM_COMMAND_ADDRESS | EMAC_MDIO_COMM_START_BUSY); REG_WR(bp, mdio_ctrl + EMAC_REG_EMAC_MDIO_COMM, val); for (i = 0; i < 50; i++) { udelay(10); val = REG_RD(bp, mdio_ctrl + EMAC_REG_EMAC_MDIO_COMM); if (!(val & EMAC_MDIO_COMM_START_BUSY)) { udelay(5); break; } } if (val & EMAC_MDIO_COMM_START_BUSY) { DP(NETIF_MSG_LINK, "read phy register failed\n"); *ret_val = 0; rc = -EFAULT; } else { /* data */ val = ((phy_addr << 21) | (devad << 16) | EMAC_MDIO_COMM_COMMAND_READ_45 | EMAC_MDIO_COMM_START_BUSY); REG_WR(bp, mdio_ctrl + EMAC_REG_EMAC_MDIO_COMM, val); for (i = 0; i < 50; i++) { udelay(10); val = REG_RD(bp, mdio_ctrl + EMAC_REG_EMAC_MDIO_COMM); if (!(val & EMAC_MDIO_COMM_START_BUSY)) { *ret_val = (u16)(val & EMAC_MDIO_COMM_DATA); break; } } if (val & EMAC_MDIO_COMM_START_BUSY) { DP(NETIF_MSG_LINK, "read phy register failed\n"); *ret_val = 0; rc = -EFAULT; } } /* Restore the saved mode */ REG_WR(bp, mdio_ctrl + EMAC_REG_EMAC_MDIO_MODE, saved_mode); return rc; } static void bnx2x_set_aer_mmd(struct link_params *params, struct link_vars *vars) { struct bnx2x *bp = params->bp; u32 ser_lane; u16 offset; ser_lane = ((params->lane_config & PORT_HW_CFG_LANE_SWAP_CFG_MASTER_MASK) >> PORT_HW_CFG_LANE_SWAP_CFG_MASTER_SHIFT); offset = (vars->phy_flags & PHY_XGXS_FLAG) ? (params->phy_addr + ser_lane) : 0; CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_AER_BLOCK, MDIO_AER_BLOCK_AER_REG, 0x3800 + offset); } static void bnx2x_set_master_ln(struct link_params *params) { struct bnx2x *bp = params->bp; u16 new_master_ln, ser_lane; ser_lane = ((params->lane_config & PORT_HW_CFG_LANE_SWAP_CFG_MASTER_MASK) >> PORT_HW_CFG_LANE_SWAP_CFG_MASTER_SHIFT); /* set the master_ln for AN */ CL45_RD_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_XGXS_BLOCK2, MDIO_XGXS_BLOCK2_TEST_MODE_LANE, &new_master_ln); CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_XGXS_BLOCK2 , MDIO_XGXS_BLOCK2_TEST_MODE_LANE, (new_master_ln | ser_lane)); } static u8 bnx2x_reset_unicore(struct link_params *params) { struct bnx2x *bp = params->bp; u16 mii_control; u16 i; CL45_RD_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_COMBO_IEEE0, MDIO_COMBO_IEEE0_MII_CONTROL, &mii_control); /* reset the unicore */ CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_COMBO_IEEE0, MDIO_COMBO_IEEE0_MII_CONTROL, (mii_control | MDIO_COMBO_IEEO_MII_CONTROL_RESET)); if (params->switch_cfg == SWITCH_CFG_1G) bnx2x_set_serdes_access(params); /* wait for the reset to self clear */ for (i = 0; i < MDIO_ACCESS_TIMEOUT; i++) { udelay(5); /* the reset erased the previous bank value */ CL45_RD_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_COMBO_IEEE0, MDIO_COMBO_IEEE0_MII_CONTROL, &mii_control); if (!(mii_control & MDIO_COMBO_IEEO_MII_CONTROL_RESET)) { udelay(5); return 0; } } DP(NETIF_MSG_LINK, "BUG! XGXS is still in reset!\n"); return -EINVAL; } static void bnx2x_set_swap_lanes(struct link_params *params) { struct bnx2x *bp = params->bp; /* Each two bits represents a lane number: No swap is 0123 => 0x1b no need to enable the swap */ u16 ser_lane, rx_lane_swap, tx_lane_swap; ser_lane = ((params->lane_config & PORT_HW_CFG_LANE_SWAP_CFG_MASTER_MASK) >> PORT_HW_CFG_LANE_SWAP_CFG_MASTER_SHIFT); rx_lane_swap = ((params->lane_config & PORT_HW_CFG_LANE_SWAP_CFG_RX_MASK) >> PORT_HW_CFG_LANE_SWAP_CFG_RX_SHIFT); tx_lane_swap = ((params->lane_config & PORT_HW_CFG_LANE_SWAP_CFG_TX_MASK) >> PORT_HW_CFG_LANE_SWAP_CFG_TX_SHIFT); if (rx_lane_swap != 0x1b) { CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_XGXS_BLOCK2, MDIO_XGXS_BLOCK2_RX_LN_SWAP, (rx_lane_swap | MDIO_XGXS_BLOCK2_RX_LN_SWAP_ENABLE | MDIO_XGXS_BLOCK2_RX_LN_SWAP_FORCE_ENABLE)); } else { CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_XGXS_BLOCK2, MDIO_XGXS_BLOCK2_RX_LN_SWAP, 0); } if (tx_lane_swap != 0x1b) { CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_XGXS_BLOCK2, MDIO_XGXS_BLOCK2_TX_LN_SWAP, (tx_lane_swap | MDIO_XGXS_BLOCK2_TX_LN_SWAP_ENABLE)); } else { CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_XGXS_BLOCK2, MDIO_XGXS_BLOCK2_TX_LN_SWAP, 0); } } static void bnx2x_set_parallel_detection(struct link_params *params, u8 phy_flags) { struct bnx2x *bp = params->bp; u16 control2; CL45_RD_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_SERDES_DIGITAL, MDIO_SERDES_DIGITAL_A_1000X_CONTROL2, &control2); control2 |= MDIO_SERDES_DIGITAL_A_1000X_CONTROL2_PRL_DT_EN; CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_SERDES_DIGITAL, MDIO_SERDES_DIGITAL_A_1000X_CONTROL2, control2); if (phy_flags & PHY_XGXS_FLAG) { DP(NETIF_MSG_LINK, "XGXS\n"); CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_10G_PARALLEL_DETECT, MDIO_10G_PARALLEL_DETECT_PAR_DET_10G_LINK, MDIO_10G_PARALLEL_DETECT_PAR_DET_10G_LINK_CNT); CL45_RD_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_10G_PARALLEL_DETECT, MDIO_10G_PARALLEL_DETECT_PAR_DET_10G_CONTROL, &control2); control2 |= MDIO_10G_PARALLEL_DETECT_PAR_DET_10G_CONTROL_PARDET10G_EN; CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_10G_PARALLEL_DETECT, MDIO_10G_PARALLEL_DETECT_PAR_DET_10G_CONTROL, control2); /* Disable parallel detection of HiG */ CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_XGXS_BLOCK2, MDIO_XGXS_BLOCK2_UNICORE_MODE_10G, MDIO_XGXS_BLOCK2_UNICORE_MODE_10G_CX4_XGXS | MDIO_XGXS_BLOCK2_UNICORE_MODE_10G_HIGIG_XGXS); } } static void bnx2x_set_autoneg(struct link_params *params, struct link_vars *vars, u8 enable_cl73) { struct bnx2x *bp = params->bp; u16 reg_val; /* CL37 Autoneg */ CL45_RD_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_COMBO_IEEE0, MDIO_COMBO_IEEE0_MII_CONTROL, &reg_val); /* CL37 Autoneg Enabled */ if (vars->line_speed == SPEED_AUTO_NEG) reg_val |= MDIO_COMBO_IEEO_MII_CONTROL_AN_EN; else /* CL37 Autoneg Disabled */ reg_val &= ~(MDIO_COMBO_IEEO_MII_CONTROL_AN_EN | MDIO_COMBO_IEEO_MII_CONTROL_RESTART_AN); CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_COMBO_IEEE0, MDIO_COMBO_IEEE0_MII_CONTROL, reg_val); /* Enable/Disable Autodetection */ CL45_RD_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_SERDES_DIGITAL, MDIO_SERDES_DIGITAL_A_1000X_CONTROL1, &reg_val); reg_val &= ~(MDIO_SERDES_DIGITAL_A_1000X_CONTROL1_SIGNAL_DETECT_EN | MDIO_SERDES_DIGITAL_A_1000X_CONTROL1_INVERT_SIGNAL_DETECT); reg_val |= MDIO_SERDES_DIGITAL_A_1000X_CONTROL1_FIBER_MODE; if (vars->line_speed == SPEED_AUTO_NEG) reg_val |= MDIO_SERDES_DIGITAL_A_1000X_CONTROL1_AUTODET; else reg_val &= ~MDIO_SERDES_DIGITAL_A_1000X_CONTROL1_AUTODET; CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_SERDES_DIGITAL, MDIO_SERDES_DIGITAL_A_1000X_CONTROL1, reg_val); /* Enable TetonII and BAM autoneg */ CL45_RD_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_BAM_NEXT_PAGE, MDIO_BAM_NEXT_PAGE_MP5_NEXT_PAGE_CTRL, &reg_val); if (vars->line_speed == SPEED_AUTO_NEG) { /* Enable BAM aneg Mode and TetonII aneg Mode */ reg_val |= (MDIO_BAM_NEXT_PAGE_MP5_NEXT_PAGE_CTRL_BAM_MODE | MDIO_BAM_NEXT_PAGE_MP5_NEXT_PAGE_CTRL_TETON_AN); } else { /* TetonII and BAM Autoneg Disabled */ reg_val &= ~(MDIO_BAM_NEXT_PAGE_MP5_NEXT_PAGE_CTRL_BAM_MODE | MDIO_BAM_NEXT_PAGE_MP5_NEXT_PAGE_CTRL_TETON_AN); } CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_BAM_NEXT_PAGE, MDIO_BAM_NEXT_PAGE_MP5_NEXT_PAGE_CTRL, reg_val); if (enable_cl73) { /* Enable Cl73 FSM status bits */ CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_CL73_USERB0, MDIO_CL73_USERB0_CL73_UCTRL, MDIO_CL73_USERB0_CL73_UCTRL_USTAT1_MUXSEL); /* Enable BAM Station Manager*/ CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_CL73_USERB0, MDIO_CL73_USERB0_CL73_BAM_CTRL1, MDIO_CL73_USERB0_CL73_BAM_CTRL1_BAM_EN | MDIO_CL73_USERB0_CL73_BAM_CTRL1_BAM_STATION_MNGR_EN | MDIO_CL73_USERB0_CL73_BAM_CTRL1_BAM_NP_AFTER_BP_EN); /* Merge CL73 and CL37 aneg resolution */ CL45_RD_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_CL73_USERB0, MDIO_CL73_USERB0_CL73_BAM_CTRL3, &reg_val); if (params->speed_cap_mask & PORT_HW_CFG_SPEED_CAPABILITY_D0_10G) { /* Set the CL73 AN speed */ CL45_RD_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_CL73_IEEEB1, MDIO_CL73_IEEEB1_AN_ADV2, &reg_val); CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_CL73_IEEEB1, MDIO_CL73_IEEEB1_AN_ADV2, reg_val | MDIO_CL73_IEEEB1_AN_ADV2_ADVR_10G_KX4); } /* CL73 Autoneg Enabled */ reg_val = MDIO_CL73_IEEEB0_CL73_AN_CONTROL_AN_EN; } else /* CL73 Autoneg Disabled */ reg_val = 0; CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_CL73_IEEEB0, MDIO_CL73_IEEEB0_CL73_AN_CONTROL, reg_val); } /* program SerDes, forced speed */ static void bnx2x_program_serdes(struct link_params *params, struct link_vars *vars) { struct bnx2x *bp = params->bp; u16 reg_val; /* program duplex, disable autoneg and sgmii*/ CL45_RD_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_COMBO_IEEE0, MDIO_COMBO_IEEE0_MII_CONTROL, &reg_val); reg_val &= ~(MDIO_COMBO_IEEO_MII_CONTROL_FULL_DUPLEX | MDIO_COMBO_IEEO_MII_CONTROL_AN_EN | MDIO_COMBO_IEEO_MII_CONTROL_MAN_SGMII_SP_MASK); if (params->req_duplex == DUPLEX_FULL) reg_val |= MDIO_COMBO_IEEO_MII_CONTROL_FULL_DUPLEX; CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_COMBO_IEEE0, MDIO_COMBO_IEEE0_MII_CONTROL, reg_val); /* program speed - needed only if the speed is greater than 1G (2.5G or 10G) */ CL45_RD_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_SERDES_DIGITAL, MDIO_SERDES_DIGITAL_MISC1, &reg_val); /* clearing the speed value before setting the right speed */ DP(NETIF_MSG_LINK, "MDIO_REG_BANK_SERDES_DIGITAL = 0x%x\n", reg_val); reg_val &= ~(MDIO_SERDES_DIGITAL_MISC1_FORCE_SPEED_MASK | MDIO_SERDES_DIGITAL_MISC1_FORCE_SPEED_SEL); if (!((vars->line_speed == SPEED_1000) || (vars->line_speed == SPEED_100) || (vars->line_speed == SPEED_10))) { reg_val |= (MDIO_SERDES_DIGITAL_MISC1_REFCLK_SEL_156_25M | MDIO_SERDES_DIGITAL_MISC1_FORCE_SPEED_SEL); if (vars->line_speed == SPEED_10000) reg_val |= MDIO_SERDES_DIGITAL_MISC1_FORCE_SPEED_10G_CX4; if (vars->line_speed == SPEED_13000) reg_val |= MDIO_SERDES_DIGITAL_MISC1_FORCE_SPEED_13G; } CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_SERDES_DIGITAL, MDIO_SERDES_DIGITAL_MISC1, reg_val); } static void bnx2x_set_brcm_cl37_advertisment(struct link_params *params) { struct bnx2x *bp = params->bp; u16 val = 0; /* configure the 48 bits for BAM AN */ /* set extended capabilities */ if (params->speed_cap_mask & PORT_HW_CFG_SPEED_CAPABILITY_D0_2_5G) val |= MDIO_OVER_1G_UP1_2_5G; if (params->speed_cap_mask & PORT_HW_CFG_SPEED_CAPABILITY_D0_10G) val |= MDIO_OVER_1G_UP1_10G; CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_OVER_1G, MDIO_OVER_1G_UP1, val); CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_OVER_1G, MDIO_OVER_1G_UP3, 0x400); } static void bnx2x_calc_ieee_aneg_adv(struct link_params *params, u16 *ieee_fc) { *ieee_fc = MDIO_COMBO_IEEE0_AUTO_NEG_ADV_FULL_DUPLEX; /* resolve pause mode and advertisement * Please refer to Table 28B-3 of the 802.3ab-1999 spec */ switch (params->req_flow_ctrl) { case BNX2X_FLOW_CTRL_AUTO: if (params->req_fc_auto_adv == BNX2X_FLOW_CTRL_BOTH) { *ieee_fc |= MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_BOTH; } else { *ieee_fc |= MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_ASYMMETRIC; } break; case BNX2X_FLOW_CTRL_TX: *ieee_fc |= MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_ASYMMETRIC; break; case BNX2X_FLOW_CTRL_RX: case BNX2X_FLOW_CTRL_BOTH: *ieee_fc |= MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_BOTH; break; case BNX2X_FLOW_CTRL_NONE: default: *ieee_fc |= MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_NONE; break; } } static void bnx2x_set_ieee_aneg_advertisment(struct link_params *params, u16 ieee_fc) { struct bnx2x *bp = params->bp; /* for AN, we are always publishing full duplex */ CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_COMBO_IEEE0, MDIO_COMBO_IEEE0_AUTO_NEG_ADV, ieee_fc); } static void bnx2x_restart_autoneg(struct link_params *params, u8 enable_cl73) { struct bnx2x *bp = params->bp; u16 mii_control; DP(NETIF_MSG_LINK, "bnx2x_restart_autoneg\n"); /* Enable and restart BAM/CL37 aneg */ if (enable_cl73) { CL45_RD_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_CL73_IEEEB0, MDIO_CL73_IEEEB0_CL73_AN_CONTROL, &mii_control); CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_CL73_IEEEB0, MDIO_CL73_IEEEB0_CL73_AN_CONTROL, (mii_control | MDIO_CL73_IEEEB0_CL73_AN_CONTROL_AN_EN | MDIO_CL73_IEEEB0_CL73_AN_CONTROL_RESTART_AN)); } else { CL45_RD_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_COMBO_IEEE0, MDIO_COMBO_IEEE0_MII_CONTROL, &mii_control); DP(NETIF_MSG_LINK, "bnx2x_restart_autoneg mii_control before = 0x%x\n", mii_control); CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_COMBO_IEEE0, MDIO_COMBO_IEEE0_MII_CONTROL, (mii_control | MDIO_COMBO_IEEO_MII_CONTROL_AN_EN | MDIO_COMBO_IEEO_MII_CONTROL_RESTART_AN)); } } static void bnx2x_initialize_sgmii_process(struct link_params *params, struct link_vars *vars) { struct bnx2x *bp = params->bp; u16 control1; /* in SGMII mode, the unicore is always slave */ CL45_RD_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_SERDES_DIGITAL, MDIO_SERDES_DIGITAL_A_1000X_CONTROL1, &control1); control1 |= MDIO_SERDES_DIGITAL_A_1000X_CONTROL1_INVERT_SIGNAL_DETECT; /* set sgmii mode (and not fiber) */ control1 &= ~(MDIO_SERDES_DIGITAL_A_1000X_CONTROL1_FIBER_MODE | MDIO_SERDES_DIGITAL_A_1000X_CONTROL1_AUTODET | MDIO_SERDES_DIGITAL_A_1000X_CONTROL1_MSTR_MODE); CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_SERDES_DIGITAL, MDIO_SERDES_DIGITAL_A_1000X_CONTROL1, control1); /* if forced speed */ if (!(vars->line_speed == SPEED_AUTO_NEG)) { /* set speed, disable autoneg */ u16 mii_control; CL45_RD_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_COMBO_IEEE0, MDIO_COMBO_IEEE0_MII_CONTROL, &mii_control); mii_control &= ~(MDIO_COMBO_IEEO_MII_CONTROL_AN_EN | MDIO_COMBO_IEEO_MII_CONTROL_MAN_SGMII_SP_MASK| MDIO_COMBO_IEEO_MII_CONTROL_FULL_DUPLEX); switch (vars->line_speed) { case SPEED_100: mii_control |= MDIO_COMBO_IEEO_MII_CONTROL_MAN_SGMII_SP_100; break; case SPEED_1000: mii_control |= MDIO_COMBO_IEEO_MII_CONTROL_MAN_SGMII_SP_1000; break; case SPEED_10: /* there is nothing to set for 10M */ break; default: /* invalid speed for SGMII */ DP(NETIF_MSG_LINK, "Invalid line_speed 0x%x\n", vars->line_speed); break; } /* setting the full duplex */ if (params->req_duplex == DUPLEX_FULL) mii_control |= MDIO_COMBO_IEEO_MII_CONTROL_FULL_DUPLEX; CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_COMBO_IEEE0, MDIO_COMBO_IEEE0_MII_CONTROL, mii_control); } else { /* AN mode */ /* enable and restart AN */ bnx2x_restart_autoneg(params, 0); } } /* * link management */ static void bnx2x_pause_resolve(struct link_vars *vars, u32 pause_result) { /* LD LP */ switch (pause_result) { /* ASYM P ASYM P */ case 0xb: /* 1 0 1 1 */ vars->flow_ctrl = BNX2X_FLOW_CTRL_TX; break; case 0xe: /* 1 1 1 0 */ vars->flow_ctrl = BNX2X_FLOW_CTRL_RX; break; case 0x5: /* 0 1 0 1 */ case 0x7: /* 0 1 1 1 */ case 0xd: /* 1 1 0 1 */ case 0xf: /* 1 1 1 1 */ vars->flow_ctrl = BNX2X_FLOW_CTRL_BOTH; break; default: break; } } static u8 bnx2x_ext_phy_resolve_fc(struct link_params *params, struct link_vars *vars) { struct bnx2x *bp = params->bp; u8 ext_phy_addr; u16 ld_pause; /* local */ u16 lp_pause; /* link partner */ u16 an_complete; /* AN complete */ u16 pause_result; u8 ret = 0; u32 ext_phy_type; u8 port = params->port; ext_phy_addr = XGXS_EXT_PHY_ADDR(params->ext_phy_config); ext_phy_type = XGXS_EXT_PHY_TYPE(params->ext_phy_config); /* read twice */ bnx2x_cl45_read(bp, port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_STATUS, &an_complete); bnx2x_cl45_read(bp, port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_STATUS, &an_complete); if (an_complete & MDIO_AN_REG_STATUS_AN_COMPLETE) { ret = 1; bnx2x_cl45_read(bp, port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_ADV_PAUSE, &ld_pause); bnx2x_cl45_read(bp, port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_LP_AUTO_NEG, &lp_pause); pause_result = (ld_pause & MDIO_AN_REG_ADV_PAUSE_MASK) >> 8; pause_result |= (lp_pause & MDIO_AN_REG_ADV_PAUSE_MASK) >> 10; DP(NETIF_MSG_LINK, "Ext PHY pause result 0x%x \n", pause_result); bnx2x_pause_resolve(vars, pause_result); if (vars->flow_ctrl == BNX2X_FLOW_CTRL_NONE && ext_phy_type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073) { bnx2x_cl45_read(bp, port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_CL37_FC_LD, &ld_pause); bnx2x_cl45_read(bp, port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_CL37_FC_LP, &lp_pause); pause_result = (ld_pause & MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_BOTH) >> 5; pause_result |= (lp_pause & MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_BOTH) >> 7; bnx2x_pause_resolve(vars, pause_result); DP(NETIF_MSG_LINK, "Ext PHY CL37 pause result 0x%x \n", pause_result); } } return ret; } static void bnx2x_flow_ctrl_resolve(struct link_params *params, struct link_vars *vars, u32 gp_status) { struct bnx2x *bp = params->bp; u16 ld_pause; /* local driver */ u16 lp_pause; /* link partner */ u16 pause_result; vars->flow_ctrl = BNX2X_FLOW_CTRL_NONE; /* resolve from gp_status in case of AN complete and not sgmii */ if ((params->req_flow_ctrl == BNX2X_FLOW_CTRL_AUTO) && (gp_status & MDIO_AN_CL73_OR_37_COMPLETE) && (!(vars->phy_flags & PHY_SGMII_FLAG)) && (XGXS_EXT_PHY_TYPE(params->ext_phy_config) == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT)) { CL45_RD_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_COMBO_IEEE0, MDIO_COMBO_IEEE0_AUTO_NEG_ADV, &ld_pause); CL45_RD_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_COMBO_IEEE0, MDIO_COMBO_IEEE0_AUTO_NEG_LINK_PARTNER_ABILITY1, &lp_pause); pause_result = (ld_pause & MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_MASK)>>5; pause_result |= (lp_pause & MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_MASK)>>7; DP(NETIF_MSG_LINK, "pause_result 0x%x\n", pause_result); bnx2x_pause_resolve(vars, pause_result); } else if ((params->req_flow_ctrl == BNX2X_FLOW_CTRL_AUTO) && (bnx2x_ext_phy_resolve_fc(params, vars))) { return; } else { if (params->req_flow_ctrl == BNX2X_FLOW_CTRL_AUTO) vars->flow_ctrl = params->req_fc_auto_adv; else vars->flow_ctrl = params->req_flow_ctrl; } DP(NETIF_MSG_LINK, "flow_ctrl 0x%x\n", vars->flow_ctrl); } static void bnx2x_check_fallback_to_cl37(struct link_params *params) { struct bnx2x *bp = params->bp; u16 rx_status, ustat_val, cl37_fsm_recieved; DP(NETIF_MSG_LINK, "bnx2x_check_fallback_to_cl37\n"); /* Step 1: Make sure signal is detected */ CL45_RD_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_RX0, MDIO_RX0_RX_STATUS, &rx_status); if ((rx_status & MDIO_RX0_RX_STATUS_SIGDET) != (MDIO_RX0_RX_STATUS_SIGDET)) { DP(NETIF_MSG_LINK, "Signal is not detected. Restoring CL73." "rx_status(0x80b0) = 0x%x\n", rx_status); CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_CL73_IEEEB0, MDIO_CL73_IEEEB0_CL73_AN_CONTROL, MDIO_CL73_IEEEB0_CL73_AN_CONTROL_AN_EN); return; } /* Step 2: Check CL73 state machine */ CL45_RD_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_CL73_USERB0, MDIO_CL73_USERB0_CL73_USTAT1, &ustat_val); if ((ustat_val & (MDIO_CL73_USERB0_CL73_USTAT1_LINK_STATUS_CHECK | MDIO_CL73_USERB0_CL73_USTAT1_AN_GOOD_CHECK_BAM37)) != (MDIO_CL73_USERB0_CL73_USTAT1_LINK_STATUS_CHECK | MDIO_CL73_USERB0_CL73_USTAT1_AN_GOOD_CHECK_BAM37)) { DP(NETIF_MSG_LINK, "CL73 state-machine is not stable. " "ustat_val(0x8371) = 0x%x\n", ustat_val); return; } /* Step 3: Check CL37 Message Pages received to indicate LP supports only CL37 */ CL45_RD_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_REMOTE_PHY, MDIO_REMOTE_PHY_MISC_RX_STATUS, &cl37_fsm_recieved); if ((cl37_fsm_recieved & (MDIO_REMOTE_PHY_MISC_RX_STATUS_CL37_FSM_RECEIVED_OVER1G_MSG | MDIO_REMOTE_PHY_MISC_RX_STATUS_CL37_FSM_RECEIVED_BRCM_OUI_MSG)) != (MDIO_REMOTE_PHY_MISC_RX_STATUS_CL37_FSM_RECEIVED_OVER1G_MSG | MDIO_REMOTE_PHY_MISC_RX_STATUS_CL37_FSM_RECEIVED_BRCM_OUI_MSG)) { DP(NETIF_MSG_LINK, "No CL37 FSM were received. " "misc_rx_status(0x8330) = 0x%x\n", cl37_fsm_recieved); return; } /* The combined cl37/cl73 fsm state information indicating that we are connected to a device which does not support cl73, but does support cl37 BAM. In this case we disable cl73 and restart cl37 auto-neg */ /* Disable CL73 */ CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_CL73_IEEEB0, MDIO_CL73_IEEEB0_CL73_AN_CONTROL, 0); /* Restart CL37 autoneg */ bnx2x_restart_autoneg(params, 0); DP(NETIF_MSG_LINK, "Disabling CL73, and restarting CL37 autoneg\n"); } static u8 bnx2x_link_settings_status(struct link_params *params, struct link_vars *vars, u32 gp_status, u8 ext_phy_link_up) { struct bnx2x *bp = params->bp; u16 new_line_speed; u8 rc = 0; vars->link_status = 0; if (gp_status & MDIO_GP_STATUS_TOP_AN_STATUS1_LINK_STATUS) { DP(NETIF_MSG_LINK, "phy link up gp_status=0x%x\n", gp_status); vars->phy_link_up = 1; vars->link_status |= LINK_STATUS_LINK_UP; if (gp_status & MDIO_GP_STATUS_TOP_AN_STATUS1_DUPLEX_STATUS) vars->duplex = DUPLEX_FULL; else vars->duplex = DUPLEX_HALF; bnx2x_flow_ctrl_resolve(params, vars, gp_status); switch (gp_status & GP_STATUS_SPEED_MASK) { case GP_STATUS_10M: new_line_speed = SPEED_10; if (vars->duplex == DUPLEX_FULL) vars->link_status |= LINK_10TFD; else vars->link_status |= LINK_10THD; break; case GP_STATUS_100M: new_line_speed = SPEED_100; if (vars->duplex == DUPLEX_FULL) vars->link_status |= LINK_100TXFD; else vars->link_status |= LINK_100TXHD; break; case GP_STATUS_1G: case GP_STATUS_1G_KX: new_line_speed = SPEED_1000; if (vars->duplex == DUPLEX_FULL) vars->link_status |= LINK_1000TFD; else vars->link_status |= LINK_1000THD; break; case GP_STATUS_2_5G: new_line_speed = SPEED_2500; if (vars->duplex == DUPLEX_FULL) vars->link_status |= LINK_2500TFD; else vars->link_status |= LINK_2500THD; break; case GP_STATUS_5G: case GP_STATUS_6G: DP(NETIF_MSG_LINK, "link speed unsupported gp_status 0x%x\n", gp_status); return -EINVAL; case GP_STATUS_10G_KX4: case GP_STATUS_10G_HIG: case GP_STATUS_10G_CX4: new_line_speed = SPEED_10000; vars->link_status |= LINK_10GTFD; break; case GP_STATUS_12G_HIG: new_line_speed = SPEED_12000; vars->link_status |= LINK_12GTFD; break; case GP_STATUS_12_5G: new_line_speed = SPEED_12500; vars->link_status |= LINK_12_5GTFD; break; case GP_STATUS_13G: new_line_speed = SPEED_13000; vars->link_status |= LINK_13GTFD; break; case GP_STATUS_15G: new_line_speed = SPEED_15000; vars->link_status |= LINK_15GTFD; break; case GP_STATUS_16G: new_line_speed = SPEED_16000; vars->link_status |= LINK_16GTFD; break; default: DP(NETIF_MSG_LINK, "link speed unsupported gp_status 0x%x\n", gp_status); return -EINVAL; } /* Upon link speed change set the NIG into drain mode. Comes to deals with possible FIFO glitch due to clk change when speed is decreased without link down indicator */ if (new_line_speed != vars->line_speed) { if (XGXS_EXT_PHY_TYPE(params->ext_phy_config) != PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT && ext_phy_link_up) { DP(NETIF_MSG_LINK, "Internal link speed %d is" " different than the external" " link speed %d\n", new_line_speed, vars->line_speed); vars->phy_link_up = 0; return 0; } REG_WR(bp, NIG_REG_EGRESS_DRAIN0_MODE + params->port*4, 0); msleep(1); } vars->line_speed = new_line_speed; vars->link_status |= LINK_STATUS_SERDES_LINK; if ((params->req_line_speed == SPEED_AUTO_NEG) && ((XGXS_EXT_PHY_TYPE(params->ext_phy_config) == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT) || (XGXS_EXT_PHY_TYPE(params->ext_phy_config) == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8705) || (XGXS_EXT_PHY_TYPE(params->ext_phy_config) == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8726))) { vars->autoneg = AUTO_NEG_ENABLED; if (gp_status & MDIO_AN_CL73_OR_37_COMPLETE) { vars->autoneg |= AUTO_NEG_COMPLETE; vars->link_status |= LINK_STATUS_AUTO_NEGOTIATE_COMPLETE; } vars->autoneg |= AUTO_NEG_PARALLEL_DETECTION_USED; vars->link_status |= LINK_STATUS_PARALLEL_DETECTION_USED; } if (vars->flow_ctrl & BNX2X_FLOW_CTRL_TX) vars->link_status |= LINK_STATUS_TX_FLOW_CONTROL_ENABLED; if (vars->flow_ctrl & BNX2X_FLOW_CTRL_RX) vars->link_status |= LINK_STATUS_RX_FLOW_CONTROL_ENABLED; } else { /* link_down */ DP(NETIF_MSG_LINK, "phy link down\n"); vars->phy_link_up = 0; vars->duplex = DUPLEX_FULL; vars->flow_ctrl = BNX2X_FLOW_CTRL_NONE; vars->autoneg = AUTO_NEG_DISABLED; vars->mac_type = MAC_TYPE_NONE; if ((params->req_line_speed == SPEED_AUTO_NEG) && ((XGXS_EXT_PHY_TYPE(params->ext_phy_config) == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT))) { /* Check signal is detected */ bnx2x_check_fallback_to_cl37(params); } } DP(NETIF_MSG_LINK, "gp_status 0x%x phy_link_up %x line_speed %x \n", gp_status, vars->phy_link_up, vars->line_speed); DP(NETIF_MSG_LINK, "duplex %x flow_ctrl 0x%x" " autoneg 0x%x\n", vars->duplex, vars->flow_ctrl, vars->autoneg); DP(NETIF_MSG_LINK, "link_status 0x%x\n", vars->link_status); return rc; } static void bnx2x_set_gmii_tx_driver(struct link_params *params) { struct bnx2x *bp = params->bp; u16 lp_up2; u16 tx_driver; u16 bank; /* read precomp */ CL45_RD_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_OVER_1G, MDIO_OVER_1G_LP_UP2, &lp_up2); /* bits [10:7] at lp_up2, positioned at [15:12] */ lp_up2 = (((lp_up2 & MDIO_OVER_1G_LP_UP2_PREEMPHASIS_MASK) >> MDIO_OVER_1G_LP_UP2_PREEMPHASIS_SHIFT) << MDIO_TX0_TX_DRIVER_PREEMPHASIS_SHIFT); if (lp_up2 == 0) return; for (bank = MDIO_REG_BANK_TX0; bank <= MDIO_REG_BANK_TX3; bank += (MDIO_REG_BANK_TX1 - MDIO_REG_BANK_TX0)) { CL45_RD_OVER_CL22(bp, params->port, params->phy_addr, bank, MDIO_TX0_TX_DRIVER, &tx_driver); /* replace tx_driver bits [15:12] */ if (lp_up2 != (tx_driver & MDIO_TX0_TX_DRIVER_PREEMPHASIS_MASK)) { tx_driver &= ~MDIO_TX0_TX_DRIVER_PREEMPHASIS_MASK; tx_driver |= lp_up2; CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, bank, MDIO_TX0_TX_DRIVER, tx_driver); } } } static u8 bnx2x_emac_program(struct link_params *params, u32 line_speed, u32 duplex) { struct bnx2x *bp = params->bp; u8 port = params->port; u16 mode = 0; DP(NETIF_MSG_LINK, "setting link speed & duplex\n"); bnx2x_bits_dis(bp, GRCBASE_EMAC0 + port*0x400 + EMAC_REG_EMAC_MODE, (EMAC_MODE_25G_MODE | EMAC_MODE_PORT_MII_10M | EMAC_MODE_HALF_DUPLEX)); switch (line_speed) { case SPEED_10: mode |= EMAC_MODE_PORT_MII_10M; break; case SPEED_100: mode |= EMAC_MODE_PORT_MII; break; case SPEED_1000: mode |= EMAC_MODE_PORT_GMII; break; case SPEED_2500: mode |= (EMAC_MODE_25G_MODE | EMAC_MODE_PORT_GMII); break; default: /* 10G not valid for EMAC */ DP(NETIF_MSG_LINK, "Invalid line_speed 0x%x\n", line_speed); return -EINVAL; } if (duplex == DUPLEX_HALF) mode |= EMAC_MODE_HALF_DUPLEX; bnx2x_bits_en(bp, GRCBASE_EMAC0 + port*0x400 + EMAC_REG_EMAC_MODE, mode); bnx2x_set_led(bp, params->port, LED_MODE_OPER, line_speed, params->hw_led_mode, params->chip_id); return 0; } /*****************************************************************************/ /* External Phy section */ /*****************************************************************************/ void bnx2x_ext_phy_hw_reset(struct bnx2x *bp, u8 port) { bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_1, MISC_REGISTERS_GPIO_OUTPUT_LOW, port); msleep(1); bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_1, MISC_REGISTERS_GPIO_OUTPUT_HIGH, port); } static void bnx2x_ext_phy_reset(struct link_params *params, struct link_vars *vars) { struct bnx2x *bp = params->bp; u32 ext_phy_type; u8 ext_phy_addr = XGXS_EXT_PHY_ADDR(params->ext_phy_config); DP(NETIF_MSG_LINK, "Port %x: bnx2x_ext_phy_reset\n", params->port); ext_phy_type = XGXS_EXT_PHY_TYPE(params->ext_phy_config); /* The PHY reset is controled by GPIO 1 * Give it 1ms of reset pulse */ if (vars->phy_flags & PHY_XGXS_FLAG) { switch (ext_phy_type) { case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT: DP(NETIF_MSG_LINK, "XGXS Direct\n"); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8705: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8706: DP(NETIF_MSG_LINK, "XGXS 8705/8706\n"); /* Restore normal power mode*/ bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_2, MISC_REGISTERS_GPIO_OUTPUT_HIGH, params->port); /* HW reset */ bnx2x_ext_phy_hw_reset(bp, params->port); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_CTRL, 0xa040); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8727: break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8726: /* Restore normal power mode*/ bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_2, MISC_REGISTERS_GPIO_OUTPUT_HIGH, params->port); bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_1, MISC_REGISTERS_GPIO_OUTPUT_HIGH, params->port); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_CTRL, 1<<15); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8072: DP(NETIF_MSG_LINK, "XGXS 8072\n"); /* Unset Low Power Mode and SW reset */ /* Restore normal power mode*/ bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_2, MISC_REGISTERS_GPIO_OUTPUT_HIGH, params->port); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_CTRL, 1<<15); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073: DP(NETIF_MSG_LINK, "XGXS 8073\n"); /* Restore normal power mode*/ bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_2, MISC_REGISTERS_GPIO_OUTPUT_HIGH, params->port); bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_1, MISC_REGISTERS_GPIO_OUTPUT_HIGH, params->port); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101: DP(NETIF_MSG_LINK, "XGXS SFX7101\n"); /* Restore normal power mode*/ bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_2, MISC_REGISTERS_GPIO_OUTPUT_HIGH, params->port); /* HW reset */ bnx2x_ext_phy_hw_reset(bp, params->port); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8481: /* Restore normal power mode*/ bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_2, MISC_REGISTERS_GPIO_OUTPUT_HIGH, params->port); /* HW reset */ bnx2x_ext_phy_hw_reset(bp, params->port); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_CTRL, 1<<15); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_FAILURE: DP(NETIF_MSG_LINK, "XGXS PHY Failure detected\n"); break; default: DP(NETIF_MSG_LINK, "BAD XGXS ext_phy_config 0x%x\n", params->ext_phy_config); break; } } else { /* SerDes */ ext_phy_type = SERDES_EXT_PHY_TYPE(params->ext_phy_config); switch (ext_phy_type) { case PORT_HW_CFG_SERDES_EXT_PHY_TYPE_DIRECT: DP(NETIF_MSG_LINK, "SerDes Direct\n"); break; case PORT_HW_CFG_SERDES_EXT_PHY_TYPE_BCM5482: DP(NETIF_MSG_LINK, "SerDes 5482\n"); bnx2x_ext_phy_hw_reset(bp, params->port); break; default: DP(NETIF_MSG_LINK, "BAD SerDes ext_phy_config 0x%x\n", params->ext_phy_config); break; } } } static void bnx2x_save_spirom_version(struct bnx2x *bp, u8 port, u32 shmem_base, u32 spirom_ver) { DP(NETIF_MSG_LINK, "FW version 0x%x:0x%x for port %d\n", (u16)(spirom_ver>>16), (u16)spirom_ver, port); REG_WR(bp, shmem_base + offsetof(struct shmem_region, port_mb[port].ext_phy_fw_version), spirom_ver); } static void bnx2x_save_bcm_spirom_ver(struct bnx2x *bp, u8 port, u32 ext_phy_type, u8 ext_phy_addr, u32 shmem_base) { u16 fw_ver1, fw_ver2; bnx2x_cl45_read(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_ROM_VER1, &fw_ver1); bnx2x_cl45_read(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_ROM_VER2, &fw_ver2); bnx2x_save_spirom_version(bp, port, shmem_base, (u32)(fw_ver1<<16 | fw_ver2)); } static void bnx2x_save_8481_spirom_version(struct bnx2x *bp, u8 port, u8 ext_phy_addr, u32 shmem_base) { u16 val, fw_ver1, fw_ver2, cnt; /* For the 32 bits registers in 8481, access via MDIO2ARM interface.*/ /* (1) set register 0xc200_0014(SPI_BRIDGE_CTRL_2) to 0x03000000 */ bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8481, ext_phy_addr, MDIO_PMA_DEVAD, 0xA819, 0x0014); bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8481, ext_phy_addr, MDIO_PMA_DEVAD, 0xA81A, 0xc200); bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8481, ext_phy_addr, MDIO_PMA_DEVAD, 0xA81B, 0x0000); bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8481, ext_phy_addr, MDIO_PMA_DEVAD, 0xA81C, 0x0300); bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8481, ext_phy_addr, MDIO_PMA_DEVAD, 0xA817, 0x0009); for (cnt = 0; cnt < 100; cnt++) { bnx2x_cl45_read(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8481, ext_phy_addr, MDIO_PMA_DEVAD, 0xA818, &val); if (val & 1) break; udelay(5); } if (cnt == 100) { DP(NETIF_MSG_LINK, "Unable to read 8481 phy fw version(1)\n"); bnx2x_save_spirom_version(bp, port, shmem_base, 0); return; } /* 2) read register 0xc200_0000 (SPI_FW_STATUS) */ bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8481, ext_phy_addr, MDIO_PMA_DEVAD, 0xA819, 0x0000); bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8481, ext_phy_addr, MDIO_PMA_DEVAD, 0xA81A, 0xc200); bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8481, ext_phy_addr, MDIO_PMA_DEVAD, 0xA817, 0x000A); for (cnt = 0; cnt < 100; cnt++) { bnx2x_cl45_read(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8481, ext_phy_addr, MDIO_PMA_DEVAD, 0xA818, &val); if (val & 1) break; udelay(5); } if (cnt == 100) { DP(NETIF_MSG_LINK, "Unable to read 8481 phy fw version(2)\n"); bnx2x_save_spirom_version(bp, port, shmem_base, 0); return; } /* lower 16 bits of the register SPI_FW_STATUS */ bnx2x_cl45_read(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8481, ext_phy_addr, MDIO_PMA_DEVAD, 0xA81B, &fw_ver1); /* upper 16 bits of register SPI_FW_STATUS */ bnx2x_cl45_read(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8481, ext_phy_addr, MDIO_PMA_DEVAD, 0xA81C, &fw_ver2); bnx2x_save_spirom_version(bp, port, shmem_base, (fw_ver2<<16) | fw_ver1); } static void bnx2x_bcm8072_external_rom_boot(struct link_params *params) { struct bnx2x *bp = params->bp; u8 port = params->port; u8 ext_phy_addr = XGXS_EXT_PHY_ADDR(params->ext_phy_config); u32 ext_phy_type = XGXS_EXT_PHY_TYPE(params->ext_phy_config); /* Need to wait 200ms after reset */ msleep(200); /* Boot port from external ROM * Set ser_boot_ctl bit in the MISC_CTRL1 register */ bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_MISC_CTRL1, 0x0001); /* Reset internal microprocessor */ bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_GEN_CTRL, MDIO_PMA_REG_GEN_CTRL_ROM_RESET_INTERNAL_MP); /* set micro reset = 0 */ bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_GEN_CTRL, MDIO_PMA_REG_GEN_CTRL_ROM_MICRO_RESET); /* Reset internal microprocessor */ bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_GEN_CTRL, MDIO_PMA_REG_GEN_CTRL_ROM_RESET_INTERNAL_MP); /* wait for 100ms for code download via SPI port */ msleep(100); /* Clear ser_boot_ctl bit */ bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_MISC_CTRL1, 0x0000); /* Wait 100ms */ msleep(100); bnx2x_save_bcm_spirom_ver(bp, port, ext_phy_type, ext_phy_addr, params->shmem_base); } static u8 bnx2x_8073_is_snr_needed(struct link_params *params) { /* This is only required for 8073A1, version 102 only */ struct bnx2x *bp = params->bp; u8 ext_phy_addr = XGXS_EXT_PHY_ADDR(params->ext_phy_config); u16 val; /* Read 8073 HW revision*/ bnx2x_cl45_read(bp, params->port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_8073_CHIP_REV, &val); if (val != 1) { /* No need to workaround in 8073 A1 */ return 0; } bnx2x_cl45_read(bp, params->port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_ROM_VER2, &val); /* SNR should be applied only for version 0x102 */ if (val != 0x102) return 0; return 1; } static u8 bnx2x_bcm8073_xaui_wa(struct link_params *params) { struct bnx2x *bp = params->bp; u8 ext_phy_addr = XGXS_EXT_PHY_ADDR(params->ext_phy_config); u16 val, cnt, cnt1 ; bnx2x_cl45_read(bp, params->port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_8073_CHIP_REV, &val); if (val > 0) { /* No need to workaround in 8073 A1 */ return 0; } /* XAUI workaround in 8073 A0: */ /* After loading the boot ROM and restarting Autoneg, poll Dev1, Reg $C820: */ for (cnt = 0; cnt < 1000; cnt++) { bnx2x_cl45_read(bp, params->port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_8073_SPEED_LINK_STATUS, &val); /* If bit [14] = 0 or bit [13] = 0, continue on with system initialization (XAUI work-around not required, as these bits indicate 2.5G or 1G link up). */ if (!(val & (1<<14)) || !(val & (1<<13))) { DP(NETIF_MSG_LINK, "XAUI work-around not required\n"); return 0; } else if (!(val & (1<<15))) { DP(NETIF_MSG_LINK, "clc bit 15 went off\n"); /* If bit 15 is 0, then poll Dev1, Reg $C841 until it's MSB (bit 15) goes to 1 (indicating that the XAUI workaround has completed), then continue on with system initialization.*/ for (cnt1 = 0; cnt1 < 1000; cnt1++) { bnx2x_cl45_read(bp, params->port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_8073_XAUI_WA, &val); if (val & (1<<15)) { DP(NETIF_MSG_LINK, "XAUI workaround has completed\n"); return 0; } msleep(3); } break; } msleep(3); } DP(NETIF_MSG_LINK, "Warning: XAUI work-around timeout !!!\n"); return -EINVAL; } static void bnx2x_bcm8073_bcm8727_external_rom_boot(struct bnx2x *bp, u8 port, u8 ext_phy_addr, u32 ext_phy_type, u32 shmem_base) { /* Boot port from external ROM */ /* EDC grst */ bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_GEN_CTRL, 0x0001); /* ucode reboot and rst */ bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_GEN_CTRL, 0x008c); bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_MISC_CTRL1, 0x0001); /* Reset internal microprocessor */ bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_GEN_CTRL, MDIO_PMA_REG_GEN_CTRL_ROM_MICRO_RESET); /* Release srst bit */ bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_GEN_CTRL, MDIO_PMA_REG_GEN_CTRL_ROM_RESET_INTERNAL_MP); /* wait for 100ms for code download via SPI port */ msleep(100); /* Clear ser_boot_ctl bit */ bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_MISC_CTRL1, 0x0000); bnx2x_save_bcm_spirom_ver(bp, port, ext_phy_type, ext_phy_addr, shmem_base); } static void bnx2x_bcm8073_external_rom_boot(struct bnx2x *bp, u8 port, u8 ext_phy_addr, u32 shmem_base) { bnx2x_bcm8073_bcm8727_external_rom_boot(bp, port, ext_phy_addr, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073, shmem_base); } static void bnx2x_bcm8727_external_rom_boot(struct bnx2x *bp, u8 port, u8 ext_phy_addr, u32 shmem_base) { bnx2x_bcm8073_bcm8727_external_rom_boot(bp, port, ext_phy_addr, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8727, shmem_base); } static void bnx2x_bcm8726_external_rom_boot(struct link_params *params) { struct bnx2x *bp = params->bp; u8 port = params->port; u8 ext_phy_addr = XGXS_EXT_PHY_ADDR(params->ext_phy_config); u32 ext_phy_type = XGXS_EXT_PHY_TYPE(params->ext_phy_config); /* Need to wait 100ms after reset */ msleep(100); /* Set serial boot control for external load */ bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_MISC_CTRL1, 0x0001); /* Micro controller re-boot */ bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_GEN_CTRL, MDIO_PMA_REG_GEN_CTRL_ROM_RESET_INTERNAL_MP); /* Set soft reset */ bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_GEN_CTRL, MDIO_PMA_REG_GEN_CTRL_ROM_MICRO_RESET); /* Set PLL register value to be same like in P13 ver */ bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_PLL_CTRL, 0x73A0); /* Clear soft reset. Will automatically reset micro-controller re-boot */ bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_GEN_CTRL, MDIO_PMA_REG_GEN_CTRL_ROM_RESET_INTERNAL_MP); /* wait for 150ms for microcode load */ msleep(150); /* Disable serial boot control, tristates pins SS_N, SCK, MOSI, MISO */ bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_MISC_CTRL1, 0x0000); msleep(200); bnx2x_save_bcm_spirom_ver(bp, port, ext_phy_type, ext_phy_addr, params->shmem_base); } static void bnx2x_sfp_set_transmitter(struct bnx2x *bp, u8 port, u32 ext_phy_type, u8 ext_phy_addr, u8 tx_en) { u16 val; DP(NETIF_MSG_LINK, "Setting transmitter tx_en=%x for port %x\n", tx_en, port); /* Disable/Enable transmitter ( TX laser of the SFP+ module.)*/ bnx2x_cl45_read(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_PHY_IDENTIFIER, &val); if (tx_en) val &= ~(1<<15); else val |= (1<<15); bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_PHY_IDENTIFIER, val); } static u8 bnx2x_8726_read_sfp_module_eeprom(struct link_params *params, u16 addr, u8 byte_cnt, u8 *o_buf) { struct bnx2x *bp = params->bp; u16 val = 0; u16 i; u8 port = params->port; u8 ext_phy_addr = XGXS_EXT_PHY_ADDR(params->ext_phy_config); u32 ext_phy_type = XGXS_EXT_PHY_TYPE(params->ext_phy_config); if (byte_cnt > 16) { DP(NETIF_MSG_LINK, "Reading from eeprom is" " is limited to 0xf\n"); return -EINVAL; } /* Set the read command byte count */ bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_SFP_TWO_WIRE_BYTE_CNT, (byte_cnt | 0xa000)); /* Set the read command address */ bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_SFP_TWO_WIRE_MEM_ADDR, addr); /* Activate read command */ bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_SFP_TWO_WIRE_CTRL, 0x2c0f); /* Wait up to 500us for command complete status */ for (i = 0; i < 100; i++) { bnx2x_cl45_read(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_SFP_TWO_WIRE_CTRL, &val); if ((val & MDIO_PMA_REG_SFP_TWO_WIRE_CTRL_STATUS_MASK) == MDIO_PMA_REG_SFP_TWO_WIRE_STATUS_COMPLETE) break; udelay(5); } if ((val & MDIO_PMA_REG_SFP_TWO_WIRE_CTRL_STATUS_MASK) != MDIO_PMA_REG_SFP_TWO_WIRE_STATUS_COMPLETE) { DP(NETIF_MSG_LINK, "Got bad status 0x%x when reading from SFP+ EEPROM\n", (val & MDIO_PMA_REG_SFP_TWO_WIRE_CTRL_STATUS_MASK)); return -EINVAL; } /* Read the buffer */ for (i = 0; i < byte_cnt; i++) { bnx2x_cl45_read(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_8726_TWO_WIRE_DATA_BUF + i, &val); o_buf[i] = (u8)(val & MDIO_PMA_REG_8726_TWO_WIRE_DATA_MASK); } for (i = 0; i < 100; i++) { bnx2x_cl45_read(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_SFP_TWO_WIRE_CTRL, &val); if ((val & MDIO_PMA_REG_SFP_TWO_WIRE_CTRL_STATUS_MASK) == MDIO_PMA_REG_SFP_TWO_WIRE_STATUS_IDLE) return 0;; msleep(1); } return -EINVAL; } static u8 bnx2x_8727_read_sfp_module_eeprom(struct link_params *params, u16 addr, u8 byte_cnt, u8 *o_buf) { struct bnx2x *bp = params->bp; u16 val, i; u8 port = params->port; u8 ext_phy_addr = XGXS_EXT_PHY_ADDR(params->ext_phy_config); u32 ext_phy_type = XGXS_EXT_PHY_TYPE(params->ext_phy_config); if (byte_cnt > 16) { DP(NETIF_MSG_LINK, "Reading from eeprom is" " is limited to 0xf\n"); return -EINVAL; } /* Need to read from 1.8000 to clear it */ bnx2x_cl45_read(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8727, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_SFP_TWO_WIRE_CTRL, &val); /* Set the read command byte count */ bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_SFP_TWO_WIRE_BYTE_CNT, ((byte_cnt < 2) ? 2 : byte_cnt)); /* Set the read command address */ bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_SFP_TWO_WIRE_MEM_ADDR, addr); /* Set the destination address */ bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, 0x8004, MDIO_PMA_REG_8727_TWO_WIRE_DATA_BUF); /* Activate read command */ bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_SFP_TWO_WIRE_CTRL, 0x8002); /* Wait appropriate time for two-wire command to finish before polling the status register */ msleep(1); /* Wait up to 500us for command complete status */ for (i = 0; i < 100; i++) { bnx2x_cl45_read(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_SFP_TWO_WIRE_CTRL, &val); if ((val & MDIO_PMA_REG_SFP_TWO_WIRE_CTRL_STATUS_MASK) == MDIO_PMA_REG_SFP_TWO_WIRE_STATUS_COMPLETE) break; udelay(5); } if ((val & MDIO_PMA_REG_SFP_TWO_WIRE_CTRL_STATUS_MASK) != MDIO_PMA_REG_SFP_TWO_WIRE_STATUS_COMPLETE) { DP(NETIF_MSG_LINK, "Got bad status 0x%x when reading from SFP+ EEPROM\n", (val & MDIO_PMA_REG_SFP_TWO_WIRE_CTRL_STATUS_MASK)); return -EINVAL; } /* Read the buffer */ for (i = 0; i < byte_cnt; i++) { bnx2x_cl45_read(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_8727_TWO_WIRE_DATA_BUF + i, &val); o_buf[i] = (u8)(val & MDIO_PMA_REG_8727_TWO_WIRE_DATA_MASK); } for (i = 0; i < 100; i++) { bnx2x_cl45_read(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_SFP_TWO_WIRE_CTRL, &val); if ((val & MDIO_PMA_REG_SFP_TWO_WIRE_CTRL_STATUS_MASK) == MDIO_PMA_REG_SFP_TWO_WIRE_STATUS_IDLE) return 0;; msleep(1); } return -EINVAL; } u8 bnx2x_read_sfp_module_eeprom(struct link_params *params, u16 addr, u8 byte_cnt, u8 *o_buf) { u32 ext_phy_type = XGXS_EXT_PHY_TYPE(params->ext_phy_config); if (ext_phy_type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8726) return bnx2x_8726_read_sfp_module_eeprom(params, addr, byte_cnt, o_buf); else if (ext_phy_type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8727) return bnx2x_8727_read_sfp_module_eeprom(params, addr, byte_cnt, o_buf); return -EINVAL; } static u8 bnx2x_get_edc_mode(struct link_params *params, u16 *edc_mode) { struct bnx2x *bp = params->bp; u8 val, check_limiting_mode = 0; *edc_mode = EDC_MODE_LIMITING; /* First check for copper cable */ if (bnx2x_read_sfp_module_eeprom(params, SFP_EEPROM_CON_TYPE_ADDR, 1, &val) != 0) { DP(NETIF_MSG_LINK, "Failed to read from SFP+ module EEPROM\n"); return -EINVAL; } switch (val) { case SFP_EEPROM_CON_TYPE_VAL_COPPER: { u8 copper_module_type; /* Check if its active cable( includes SFP+ module) of passive cable*/ if (bnx2x_read_sfp_module_eeprom(params, SFP_EEPROM_FC_TX_TECH_ADDR, 1, &copper_module_type) != 0) { DP(NETIF_MSG_LINK, "Failed to read copper-cable-type" " from SFP+ EEPROM\n"); return -EINVAL; } if (copper_module_type & SFP_EEPROM_FC_TX_TECH_BITMASK_COPPER_ACTIVE) { DP(NETIF_MSG_LINK, "Active Copper cable detected\n"); check_limiting_mode = 1; } else if (copper_module_type & SFP_EEPROM_FC_TX_TECH_BITMASK_COPPER_PASSIVE) { DP(NETIF_MSG_LINK, "Passive Copper" " cable detected\n"); *edc_mode = EDC_MODE_PASSIVE_DAC; } else { DP(NETIF_MSG_LINK, "Unknown copper-cable-" "type 0x%x !!!\n", copper_module_type); return -EINVAL; } break; } case SFP_EEPROM_CON_TYPE_VAL_LC: DP(NETIF_MSG_LINK, "Optic module detected\n"); check_limiting_mode = 1; break; default: DP(NETIF_MSG_LINK, "Unable to determine module type 0x%x !!!\n", val); return -EINVAL; } if (check_limiting_mode) { u8 options[SFP_EEPROM_OPTIONS_SIZE]; if (bnx2x_read_sfp_module_eeprom(params, SFP_EEPROM_OPTIONS_ADDR, SFP_EEPROM_OPTIONS_SIZE, options) != 0) { DP(NETIF_MSG_LINK, "Failed to read Option" " field from module EEPROM\n"); return -EINVAL; } if ((options[0] & SFP_EEPROM_OPTIONS_LINEAR_RX_OUT_MASK)) *edc_mode = EDC_MODE_LINEAR; else *edc_mode = EDC_MODE_LIMITING; } DP(NETIF_MSG_LINK, "EDC mode is set to 0x%x\n", *edc_mode); return 0; } /* This function read the relevant field from the module ( SFP+ ), and verify it is compliant with this board */ static u8 bnx2x_verify_sfp_module(struct link_params *params) { struct bnx2x *bp = params->bp; u32 val; u32 fw_resp; char vendor_name[SFP_EEPROM_VENDOR_NAME_SIZE+1]; char vendor_pn[SFP_EEPROM_PART_NO_SIZE+1]; val = REG_RD(bp, params->shmem_base + offsetof(struct shmem_region, dev_info. port_feature_config[params->port].config)); if ((val & PORT_FEAT_CFG_OPT_MDL_ENFRCMNT_MASK) == PORT_FEAT_CFG_OPT_MDL_ENFRCMNT_NO_ENFORCEMENT) { DP(NETIF_MSG_LINK, "NOT enforcing module verification\n"); return 0; } /* Ask the FW to validate the module */ if (!(params->feature_config_flags & FEATURE_CONFIG_BC_SUPPORTS_OPT_MDL_VRFY)) { DP(NETIF_MSG_LINK, "FW does not support OPT MDL " "verification\n"); return -EINVAL; } fw_resp = bnx2x_fw_command(bp, DRV_MSG_CODE_VRFY_OPT_MDL); if (fw_resp == FW_MSG_CODE_VRFY_OPT_MDL_SUCCESS) { DP(NETIF_MSG_LINK, "Approved module\n"); return 0; } /* format the warning message */ if (bnx2x_read_sfp_module_eeprom(params, SFP_EEPROM_VENDOR_NAME_ADDR, SFP_EEPROM_VENDOR_NAME_SIZE, (u8 *)vendor_name)) vendor_name[0] = '\0'; else vendor_name[SFP_EEPROM_VENDOR_NAME_SIZE] = '\0'; if (bnx2x_read_sfp_module_eeprom(params, SFP_EEPROM_PART_NO_ADDR, SFP_EEPROM_PART_NO_SIZE, (u8 *)vendor_pn)) vendor_pn[0] = '\0'; else vendor_pn[SFP_EEPROM_PART_NO_SIZE] = '\0'; printk(KERN_INFO PFX "Warning: " "Unqualified SFP+ module " "detected on %s, Port %d from %s part number %s\n" , bp->dev->name, params->port, vendor_name, vendor_pn); return -EINVAL; } static u8 bnx2x_bcm8726_set_limiting_mode(struct link_params *params, u16 edc_mode) { struct bnx2x *bp = params->bp; u8 port = params->port; u8 ext_phy_addr = XGXS_EXT_PHY_ADDR(params->ext_phy_config); u16 cur_limiting_mode; bnx2x_cl45_read(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8726, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_ROM_VER2, &cur_limiting_mode); DP(NETIF_MSG_LINK, "Current Limiting mode is 0x%x\n", cur_limiting_mode); if (edc_mode == EDC_MODE_LIMITING) { DP(NETIF_MSG_LINK, "Setting LIMITING MODE\n"); bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8726, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_ROM_VER2, EDC_MODE_LIMITING); } else { /* LRM mode ( default )*/ DP(NETIF_MSG_LINK, "Setting LRM MODE\n"); /* Changing to LRM mode takes quite few seconds. So do it only if current mode is limiting ( default is LRM )*/ if (cur_limiting_mode != EDC_MODE_LIMITING) return 0; bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8726, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_LRM_MODE, 0); bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8726, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_ROM_VER2, 0x128); bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8726, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_MISC_CTRL0, 0x4008); bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8726, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_LRM_MODE, 0xaaaa); } return 0; } static u8 bnx2x_bcm8727_set_limiting_mode(struct link_params *params, u16 edc_mode) { struct bnx2x *bp = params->bp; u8 port = params->port; u16 phy_identifier; u16 rom_ver2_val; u8 ext_phy_addr = XGXS_EXT_PHY_ADDR(params->ext_phy_config); bnx2x_cl45_read(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8727, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_PHY_IDENTIFIER, &phy_identifier); bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8727, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_PHY_IDENTIFIER, (phy_identifier & ~(1<<9))); bnx2x_cl45_read(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8727, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_ROM_VER2, &rom_ver2_val); /* Keep the MSB 8-bits, and set the LSB 8-bits with the edc_mode */ bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8727, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_ROM_VER2, (rom_ver2_val & 0xff00) | (edc_mode & 0x00ff)); bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8727, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_PHY_IDENTIFIER, (phy_identifier | (1<<9))); return 0; } static u8 bnx2x_wait_for_sfp_module_initialized(struct link_params *params) { u8 val; struct bnx2x *bp = params->bp; u16 timeout; /* Initialization time after hot-plug may take up to 300ms for some phys type ( e.g. JDSU ) */ for (timeout = 0; timeout < 60; timeout++) { if (bnx2x_read_sfp_module_eeprom(params, 1, 1, &val) == 0) { DP(NETIF_MSG_LINK, "SFP+ module initialization " "took %d ms\n", timeout * 5); return 0; } msleep(5); } return -EINVAL; } static void bnx2x_8727_power_module(struct bnx2x *bp, struct link_params *params, u8 ext_phy_addr, u8 is_power_up) { /* Make sure GPIOs are not using for LED mode */ u16 val; u8 port = params->port; /* * In the GPIO register, bit 4 is use to detemine if the GPIOs are * operating as INPUT or as OUTPUT. Bit 1 is for input, and 0 for * output * Bits 0-1 determine the gpios value for OUTPUT in case bit 4 val is 0 * Bits 8-9 determine the gpios value for INPUT in case bit 4 val is 1 * where the 1st bit is the over-current(only input), and 2nd bit is * for power( only output ) */ /* * In case of NOC feature is disabled and power is up, set GPIO control * as input to enable listening of over-current indication */ if (!(params->feature_config_flags & FEATURE_CONFIG_BCM8727_NOC) && is_power_up) val = (1<<4); else /* * Set GPIO control to OUTPUT, and set the power bit * to according to the is_power_up */ val = ((!(is_power_up)) << 1); bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8727, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_8727_GPIO_CTRL, val); } static u8 bnx2x_sfp_module_detection(struct link_params *params) { struct bnx2x *bp = params->bp; u16 edc_mode; u8 rc = 0; u8 ext_phy_addr = XGXS_EXT_PHY_ADDR(params->ext_phy_config); u32 ext_phy_type = XGXS_EXT_PHY_TYPE(params->ext_phy_config); u32 val = REG_RD(bp, params->shmem_base + offsetof(struct shmem_region, dev_info. port_feature_config[params->port].config)); DP(NETIF_MSG_LINK, "SFP+ module plugged in/out detected on port %d\n", params->port); if (bnx2x_get_edc_mode(params, &edc_mode) != 0) { DP(NETIF_MSG_LINK, "Failed to get valid module type\n"); return -EINVAL; } else if (bnx2x_verify_sfp_module(params) != 0) { /* check SFP+ module compatibility */ DP(NETIF_MSG_LINK, "Module verification failed!!\n"); rc = -EINVAL; /* Turn on fault module-detected led */ bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_0, MISC_REGISTERS_GPIO_HIGH, params->port); if ((ext_phy_type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8727) && ((val & PORT_FEAT_CFG_OPT_MDL_ENFRCMNT_MASK) == PORT_FEAT_CFG_OPT_MDL_ENFRCMNT_POWER_DOWN)) { /* Shutdown SFP+ module */ DP(NETIF_MSG_LINK, "Shutdown SFP+ module!!\n"); bnx2x_8727_power_module(bp, params, ext_phy_addr, 0); return rc; } } else { /* Turn off fault module-detected led */ DP(NETIF_MSG_LINK, "Turn off fault module-detected led\n"); bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_0, MISC_REGISTERS_GPIO_LOW, params->port); } /* power up the SFP module */ if (ext_phy_type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8727) bnx2x_8727_power_module(bp, params, ext_phy_addr, 1); /* Check and set limiting mode / LRM mode on 8726. On 8727 it is done automatically */ if (ext_phy_type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8726) bnx2x_bcm8726_set_limiting_mode(params, edc_mode); else bnx2x_bcm8727_set_limiting_mode(params, edc_mode); /* * Enable transmit for this module if the module is approved, or * if unapproved modules should also enable the Tx laser */ if (rc == 0 || (val & PORT_FEAT_CFG_OPT_MDL_ENFRCMNT_MASK) != PORT_FEAT_CFG_OPT_MDL_ENFRCMNT_DISABLE_TX_LASER) bnx2x_sfp_set_transmitter(bp, params->port, ext_phy_type, ext_phy_addr, 1); else bnx2x_sfp_set_transmitter(bp, params->port, ext_phy_type, ext_phy_addr, 0); return rc; } void bnx2x_handle_module_detect_int(struct link_params *params) { struct bnx2x *bp = params->bp; u32 gpio_val; u8 port = params->port; /* Set valid module led off */ bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_0, MISC_REGISTERS_GPIO_HIGH, params->port); /* Get current gpio val refelecting module plugged in / out*/ gpio_val = bnx2x_get_gpio(bp, MISC_REGISTERS_GPIO_3, port); /* Call the handling function in case module is detected */ if (gpio_val == 0) { bnx2x_set_gpio_int(bp, MISC_REGISTERS_GPIO_3, MISC_REGISTERS_GPIO_INT_OUTPUT_CLR, port); if (bnx2x_wait_for_sfp_module_initialized(params) == 0) bnx2x_sfp_module_detection(params); else DP(NETIF_MSG_LINK, "SFP+ module is not initialized\n"); } else { u8 ext_phy_addr = XGXS_EXT_PHY_ADDR(params->ext_phy_config); u32 ext_phy_type = XGXS_EXT_PHY_TYPE(params->ext_phy_config); u32 val = REG_RD(bp, params->shmem_base + offsetof(struct shmem_region, dev_info. port_feature_config[params->port]. config)); bnx2x_set_gpio_int(bp, MISC_REGISTERS_GPIO_3, MISC_REGISTERS_GPIO_INT_OUTPUT_SET, port); /* Module was plugged out. */ /* Disable transmit for this module */ if ((val & PORT_FEAT_CFG_OPT_MDL_ENFRCMNT_MASK) == PORT_FEAT_CFG_OPT_MDL_ENFRCMNT_DISABLE_TX_LASER) bnx2x_sfp_set_transmitter(bp, params->port, ext_phy_type, ext_phy_addr, 0); } } static void bnx2x_bcm807x_force_10G(struct link_params *params) { struct bnx2x *bp = params->bp; u8 port = params->port; u8 ext_phy_addr = XGXS_EXT_PHY_ADDR(params->ext_phy_config); u32 ext_phy_type = XGXS_EXT_PHY_TYPE(params->ext_phy_config); /* Force KR or KX */ bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_CTRL, 0x2040); bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_10G_CTRL2, 0x000b); bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_BCM_CTRL, 0x0000); bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_CTRL, 0x0000); } static void bnx2x_bcm8073_set_xaui_low_power_mode(struct link_params *params) { struct bnx2x *bp = params->bp; u8 port = params->port; u16 val; u8 ext_phy_addr = XGXS_EXT_PHY_ADDR(params->ext_phy_config); u32 ext_phy_type = XGXS_EXT_PHY_TYPE(params->ext_phy_config); bnx2x_cl45_read(bp, params->port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_8073_CHIP_REV, &val); if (val == 0) { /* Mustn't set low power mode in 8073 A0 */ return; } /* Disable PLL sequencer (use read-modify-write to clear bit 13) */ bnx2x_cl45_read(bp, port, ext_phy_type, ext_phy_addr, MDIO_XS_DEVAD, MDIO_XS_PLL_SEQUENCER, &val); val &= ~(1<<13); bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_XS_DEVAD, MDIO_XS_PLL_SEQUENCER, val); /* PLL controls */ bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_XS_DEVAD, 0x805E, 0x1077); bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_XS_DEVAD, 0x805D, 0x0000); bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_XS_DEVAD, 0x805C, 0x030B); bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_XS_DEVAD, 0x805B, 0x1240); bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_XS_DEVAD, 0x805A, 0x2490); /* Tx Controls */ bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_XS_DEVAD, 0x80A7, 0x0C74); bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_XS_DEVAD, 0x80A6, 0x9041); bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_XS_DEVAD, 0x80A5, 0x4640); /* Rx Controls */ bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_XS_DEVAD, 0x80FE, 0x01C4); bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_XS_DEVAD, 0x80FD, 0x9249); bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_XS_DEVAD, 0x80FC, 0x2015); /* Enable PLL sequencer (use read-modify-write to set bit 13) */ bnx2x_cl45_read(bp, port, ext_phy_type, ext_phy_addr, MDIO_XS_DEVAD, MDIO_XS_PLL_SEQUENCER, &val); val |= (1<<13); bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_XS_DEVAD, MDIO_XS_PLL_SEQUENCER, val); } static void bnx2x_8073_set_pause_cl37(struct link_params *params, struct link_vars *vars) { struct bnx2x *bp = params->bp; u16 cl37_val; u8 ext_phy_addr = XGXS_EXT_PHY_ADDR(params->ext_phy_config); u32 ext_phy_type = XGXS_EXT_PHY_TYPE(params->ext_phy_config); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_CL37_FC_LD, &cl37_val); cl37_val &= ~MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_BOTH; /* Please refer to Table 28B-3 of 802.3ab-1999 spec. */ if ((vars->ieee_fc & MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_SYMMETRIC) == MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_SYMMETRIC) { cl37_val |= MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_SYMMETRIC; } if ((vars->ieee_fc & MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_ASYMMETRIC) == MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_ASYMMETRIC) { cl37_val |= MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_ASYMMETRIC; } if ((vars->ieee_fc & MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_BOTH) == MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_BOTH) { cl37_val |= MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_BOTH; } DP(NETIF_MSG_LINK, "Ext phy AN advertize cl37 0x%x\n", cl37_val); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_CL37_FC_LD, cl37_val); msleep(500); } static void bnx2x_ext_phy_set_pause(struct link_params *params, struct link_vars *vars) { struct bnx2x *bp = params->bp; u16 val; u8 ext_phy_addr = XGXS_EXT_PHY_ADDR(params->ext_phy_config); u32 ext_phy_type = XGXS_EXT_PHY_TYPE(params->ext_phy_config); /* read modify write pause advertizing */ bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_ADV_PAUSE, &val); val &= ~MDIO_AN_REG_ADV_PAUSE_BOTH; /* Please refer to Table 28B-3 of 802.3ab-1999 spec. */ if ((vars->ieee_fc & MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_ASYMMETRIC) == MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_ASYMMETRIC) { val |= MDIO_AN_REG_ADV_PAUSE_ASYMMETRIC; } if ((vars->ieee_fc & MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_BOTH) == MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_BOTH) { val |= MDIO_AN_REG_ADV_PAUSE_PAUSE; } DP(NETIF_MSG_LINK, "Ext phy AN advertize 0x%x\n", val); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_ADV_PAUSE, val); } static void bnx2x_set_preemphasis(struct link_params *params) { u16 bank, i = 0; struct bnx2x *bp = params->bp; for (bank = MDIO_REG_BANK_RX0, i = 0; bank <= MDIO_REG_BANK_RX3; bank += (MDIO_REG_BANK_RX1-MDIO_REG_BANK_RX0), i++) { CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, bank, MDIO_RX0_RX_EQ_BOOST, params->xgxs_config_rx[i]); } for (bank = MDIO_REG_BANK_TX0, i = 0; bank <= MDIO_REG_BANK_TX3; bank += (MDIO_REG_BANK_TX1 - MDIO_REG_BANK_TX0), i++) { CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, bank, MDIO_TX0_TX_DRIVER, params->xgxs_config_tx[i]); } } static void bnx2x_8481_set_led4(struct link_params *params, u32 ext_phy_type, u8 ext_phy_addr) { struct bnx2x *bp = params->bp; /* PHYC_CTL_LED_CTL */ bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_8481_LINK_SIGNAL, 0xa482); /* Unmask LED4 for 10G link */ bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_8481_SIGNAL_MASK, (1<<6)); /* 'Interrupt Mask' */ bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, 0xFFFB, 0xFFFD); } static void bnx2x_8481_set_legacy_led_mode(struct link_params *params, u32 ext_phy_type, u8 ext_phy_addr) { struct bnx2x *bp = params->bp; /* LED1 (10G Link): Disable LED1 when 10/100/1000 link */ /* LED2 (1G/100/10 Link): Enable LED2 when 10/100/1000 link) */ bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_8481_LEGACY_SHADOW, (1<<15) | (0xd << 10) | (0xc<<4) | 0xe); } static void bnx2x_8481_set_10G_led_mode(struct link_params *params, u32 ext_phy_type, u8 ext_phy_addr) { struct bnx2x *bp = params->bp; u16 val1; /* LED1 (10G Link) */ /* Enable continuse based on source 7(10G-link) */ bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_8481_LINK_SIGNAL, &val1); /* Set bit 2 to 0, and bits [1:0] to 10 */ val1 &= ~((1<<0) | (1<<2)); /* Clear bits 0,2*/ val1 |= (1<<1); /* Set bit 1 */ bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_8481_LINK_SIGNAL, val1); /* Unmask LED1 for 10G link */ bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_8481_LED1_MASK, &val1); /* Set bit 2 to 0, and bits [1:0] to 10 */ val1 |= (1<<7); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_8481_LED1_MASK, val1); /* LED2 (1G/100/10G Link) */ /* Mask LED2 for 10G link */ bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_8481_LED2_MASK, 0); /* LED3 (10G/1G/100/10G Activity) */ bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_8481_LINK_SIGNAL, &val1); /* Enable blink based on source 4(Activity) */ val1 &= ~((1<<7) | (1<<8)); /* Clear bits 7,8 */ val1 |= (1<<6); /* Set only bit 6 */ bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_8481_LINK_SIGNAL, val1); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_8481_LED3_MASK, &val1); val1 |= (1<<4); /* Unmask LED3 for 10G link */ bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_8481_LED3_MASK, val1); } static void bnx2x_init_internal_phy(struct link_params *params, struct link_vars *vars, u8 enable_cl73) { struct bnx2x *bp = params->bp; if (!(vars->phy_flags & PHY_SGMII_FLAG)) { if ((XGXS_EXT_PHY_TYPE(params->ext_phy_config) == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT) && (params->feature_config_flags & FEATURE_CONFIG_OVERRIDE_PREEMPHASIS_ENABLED)) bnx2x_set_preemphasis(params); /* forced speed requested? */ if (vars->line_speed != SPEED_AUTO_NEG) { DP(NETIF_MSG_LINK, "not SGMII, no AN\n"); /* disable autoneg */ bnx2x_set_autoneg(params, vars, 0); /* program speed and duplex */ bnx2x_program_serdes(params, vars); } else { /* AN_mode */ DP(NETIF_MSG_LINK, "not SGMII, AN\n"); /* AN enabled */ bnx2x_set_brcm_cl37_advertisment(params); /* program duplex & pause advertisement (for aneg) */ bnx2x_set_ieee_aneg_advertisment(params, vars->ieee_fc); /* enable autoneg */ bnx2x_set_autoneg(params, vars, enable_cl73); /* enable and restart AN */ bnx2x_restart_autoneg(params, enable_cl73); } } else { /* SGMII mode */ DP(NETIF_MSG_LINK, "SGMII\n"); bnx2x_initialize_sgmii_process(params, vars); } } static u8 bnx2x_ext_phy_init(struct link_params *params, struct link_vars *vars) { struct bnx2x *bp = params->bp; u32 ext_phy_type; u8 ext_phy_addr; u16 cnt; u16 ctrl = 0; u16 val = 0; u8 rc = 0; if (vars->phy_flags & PHY_XGXS_FLAG) { ext_phy_addr = XGXS_EXT_PHY_ADDR(params->ext_phy_config); ext_phy_type = XGXS_EXT_PHY_TYPE(params->ext_phy_config); /* Make sure that the soft reset is off (expect for the 8072: * due to the lock, it will be done inside the specific * handling) */ if ((ext_phy_type != PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT) && (ext_phy_type != PORT_HW_CFG_XGXS_EXT_PHY_TYPE_FAILURE) && (ext_phy_type != PORT_HW_CFG_XGXS_EXT_PHY_TYPE_NOT_CONN) && (ext_phy_type != PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8072) && (ext_phy_type != PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073)) { /* Wait for soft reset to get cleared upto 1 sec */ for (cnt = 0; cnt < 1000; cnt++) { bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_CTRL, &ctrl); if (!(ctrl & (1<<15))) break; msleep(1); } DP(NETIF_MSG_LINK, "control reg 0x%x (after %d ms)\n", ctrl, cnt); } switch (ext_phy_type) { case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT: break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8705: DP(NETIF_MSG_LINK, "XGXS 8705\n"); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_MISC_CTRL, 0x8288); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_PHY_IDENTIFIER, 0x7fbf); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_CMU_PLL_BYPASS, 0x0100); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_WIS_DEVAD, MDIO_WIS_REG_LASI_CNTL, 0x1); /* BCM8705 doesn't have microcode, hence the 0 */ bnx2x_save_spirom_version(bp, params->port, params->shmem_base, 0); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8706: /* Wait until fw is loaded */ for (cnt = 0; cnt < 100; cnt++) { bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_ROM_VER1, &val); if (val) break; msleep(10); } DP(NETIF_MSG_LINK, "XGXS 8706 is initialized " "after %d ms\n", cnt); if ((params->feature_config_flags & FEATURE_CONFIG_OVERRIDE_PREEMPHASIS_ENABLED)) { u8 i; u16 reg; for (i = 0; i < 4; i++) { reg = MDIO_XS_8706_REG_BANK_RX0 + i*(MDIO_XS_8706_REG_BANK_RX1 - MDIO_XS_8706_REG_BANK_RX0); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_XS_DEVAD, reg, &val); /* Clear first 3 bits of the control */ val &= ~0x7; /* Set control bits according to configuation */ val |= (params->xgxs_config_rx[i] & 0x7); DP(NETIF_MSG_LINK, "Setting RX" "Equalizer to BCM8706 reg 0x%x" " <-- val 0x%x\n", reg, val); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_XS_DEVAD, reg, val); } } /* Force speed */ /* First enable LASI */ bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_RX_ALARM_CTRL, 0x0400); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_LASI_CTRL, 0x0004); if (params->req_line_speed == SPEED_10000) { DP(NETIF_MSG_LINK, "XGXS 8706 force 10Gbps\n"); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_DIGITAL_CTRL, 0x400); } else { /* Force 1Gbps using autoneg with 1G advertisment */ /* Allow CL37 through CL73 */ DP(NETIF_MSG_LINK, "XGXS 8706 AutoNeg\n"); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_CL37_CL73, 0x040c); /* Enable Full-Duplex advertisment on CL37 */ bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_CL37_FC_LP, 0x0020); /* Enable CL37 AN */ bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_CL37_AN, 0x1000); /* 1G support */ bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_ADV, (1<<5)); /* Enable clause 73 AN */ bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_CTRL, 0x1200); } bnx2x_save_bcm_spirom_ver(bp, params->port, ext_phy_type, ext_phy_addr, params->shmem_base); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8726: DP(NETIF_MSG_LINK, "Initializing BCM8726\n"); bnx2x_bcm8726_external_rom_boot(params); /* Need to call module detected on initialization since the module detection triggered by actual module insertion might occur before driver is loaded, and when driver is loaded, it reset all registers, including the transmitter */ bnx2x_sfp_module_detection(params); /* Set Flow control */ bnx2x_ext_phy_set_pause(params, vars); if (params->req_line_speed == SPEED_1000) { DP(NETIF_MSG_LINK, "Setting 1G force\n"); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_CTRL, 0x40); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_10G_CTRL2, 0xD); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_LASI_CTRL, 0x5); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_RX_ALARM_CTRL, 0x400); } else if ((params->req_line_speed == SPEED_AUTO_NEG) && ((params->speed_cap_mask & PORT_HW_CFG_SPEED_CAPABILITY_D0_1G))) { DP(NETIF_MSG_LINK, "Setting 1G clause37 \n"); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_ADV, 0x20); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_CL37_CL73, 0x040c); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_CL37_FC_LD, 0x0020); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_CL37_AN, 0x1000); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_CTRL, 0x1200); /* Enable RX-ALARM control to receive interrupt for 1G speed change */ bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_LASI_CTRL, 0x4); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_RX_ALARM_CTRL, 0x400); } else { /* Default 10G. Set only LASI control */ bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_LASI_CTRL, 1); } /* Set TX PreEmphasis if needed */ if ((params->feature_config_flags & FEATURE_CONFIG_OVERRIDE_PREEMPHASIS_ENABLED)) { DP(NETIF_MSG_LINK, "Setting TX_CTRL1 0x%x," "TX_CTRL2 0x%x\n", params->xgxs_config_tx[0], params->xgxs_config_tx[1]); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_8726_TX_CTRL1, params->xgxs_config_tx[0]); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_8726_TX_CTRL2, params->xgxs_config_tx[1]); } break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8072: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073: { u16 tmp1; u16 rx_alarm_ctrl_val; u16 lasi_ctrl_val; if (ext_phy_type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8072) { rx_alarm_ctrl_val = 0x400; lasi_ctrl_val = 0x0004; } else { rx_alarm_ctrl_val = (1<<2); lasi_ctrl_val = 0x0004; } /* enable LASI */ bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_RX_ALARM_CTRL, rx_alarm_ctrl_val); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_LASI_CTRL, lasi_ctrl_val); bnx2x_8073_set_pause_cl37(params, vars); if (ext_phy_type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8072) bnx2x_bcm8072_external_rom_boot(params); else /* In case of 8073 with long xaui lines, don't set the 8073 xaui low power*/ bnx2x_bcm8073_set_xaui_low_power_mode(params); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_M8051_MSGOUT_REG, &tmp1); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_RX_ALARM, &tmp1); DP(NETIF_MSG_LINK, "Before rom RX_ALARM(port1):" "0x%x\n", tmp1); /* If this is forced speed, set to KR or KX * (all other are not supported) */ if (params->loopback_mode == LOOPBACK_EXT) { bnx2x_bcm807x_force_10G(params); DP(NETIF_MSG_LINK, "Forced speed 10G on 807X\n"); break; } else { bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_BCM_CTRL, 0x0002); } if (params->req_line_speed != SPEED_AUTO_NEG) { if (params->req_line_speed == SPEED_10000) { val = (1<<7); } else if (params->req_line_speed == SPEED_2500) { val = (1<<5); /* Note that 2.5G works only when used with 1G advertisment */ } else val = (1<<5); } else { val = 0; if (params->speed_cap_mask & PORT_HW_CFG_SPEED_CAPABILITY_D0_10G) val |= (1<<7); /* Note that 2.5G works only when used with 1G advertisment */ if (params->speed_cap_mask & (PORT_HW_CFG_SPEED_CAPABILITY_D0_1G | PORT_HW_CFG_SPEED_CAPABILITY_D0_2_5G)) val |= (1<<5); DP(NETIF_MSG_LINK, "807x autoneg val = 0x%x\n", val); } bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_ADV, val); if (ext_phy_type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073) { bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_8073_2_5G, &tmp1); if (((params->speed_cap_mask & PORT_HW_CFG_SPEED_CAPABILITY_D0_2_5G) && (params->req_line_speed == SPEED_AUTO_NEG)) || (params->req_line_speed == SPEED_2500)) { u16 phy_ver; /* Allow 2.5G for A1 and above */ bnx2x_cl45_read(bp, params->port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_8073_CHIP_REV, &phy_ver); DP(NETIF_MSG_LINK, "Add 2.5G\n"); if (phy_ver > 0) tmp1 |= 1; else tmp1 &= 0xfffe; } else { DP(NETIF_MSG_LINK, "Disable 2.5G\n"); tmp1 &= 0xfffe; } bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_8073_2_5G, tmp1); } /* Add support for CL37 (passive mode) II */ bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_CL37_FC_LD, &tmp1); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_CL37_FC_LD, (tmp1 | ((params->req_duplex == DUPLEX_FULL) ? 0x20 : 0x40))); /* Add support for CL37 (passive mode) III */ bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_CL37_AN, 0x1000); if (ext_phy_type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073) { /* The SNR will improve about 2db by changing BW and FEE main tap. Rest commands are executed after link is up*/ /*Change FFE main cursor to 5 in EDC register*/ if (bnx2x_8073_is_snr_needed(params)) bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_EDC_FFE_MAIN, 0xFB0C); /* Enable FEC (Forware Error Correction) Request in the AN */ bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_ADV2, &tmp1); tmp1 |= (1<<15); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_ADV2, tmp1); } bnx2x_ext_phy_set_pause(params, vars); /* Restart autoneg */ msleep(500); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_CTRL, 0x1200); DP(NETIF_MSG_LINK, "807x Autoneg Restart: " "Advertise 1G=%x, 10G=%x\n", ((val & (1<<5)) > 0), ((val & (1<<7)) > 0)); break; } case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8727: { u16 tmp1; u16 rx_alarm_ctrl_val; u16 lasi_ctrl_val; /* Enable PMD link, MOD_ABS_FLT, and 1G link alarm */ u16 mod_abs; rx_alarm_ctrl_val = (1<<2) | (1<<5) ; lasi_ctrl_val = 0x0004; DP(NETIF_MSG_LINK, "Initializing BCM8727\n"); /* enable LASI */ bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_RX_ALARM_CTRL, rx_alarm_ctrl_val); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_LASI_CTRL, lasi_ctrl_val); /* Initially configure MOD_ABS to interrupt when module is presence( bit 8) */ bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_PHY_IDENTIFIER, &mod_abs); /* Set EDC off by setting OPTXLOS signal input to low (bit 9). When the EDC is off it locks onto a reference clock and avoids becoming 'lost'.*/ mod_abs &= ~((1<<8) | (1<<9)); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_PHY_IDENTIFIER, mod_abs); /* Make MOD_ABS give interrupt on change */ bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_8727_PCS_OPT_CTRL, &val); val |= (1<<12); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_8727_PCS_OPT_CTRL, val); /* Set 8727 GPIOs to input to allow reading from the 8727 GPIO0 status which reflect SFP+ module over-current */ bnx2x_cl45_read(bp, params->port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8727, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_8727_PCS_OPT_CTRL, &val); val &= 0xff8f; /* Reset bits 4-6 */ bnx2x_cl45_write(bp, params->port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8727, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_8727_PCS_OPT_CTRL, val); bnx2x_8727_power_module(bp, params, ext_phy_addr, 1); bnx2x_bcm8073_set_xaui_low_power_mode(params); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_M8051_MSGOUT_REG, &tmp1); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_RX_ALARM, &tmp1); /* Set option 1G speed */ if (params->req_line_speed == SPEED_1000) { DP(NETIF_MSG_LINK, "Setting 1G force\n"); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_CTRL, 0x40); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_10G_CTRL2, 0xD); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_10G_CTRL2, &tmp1); DP(NETIF_MSG_LINK, "1.7 = 0x%x \n", tmp1); } else if ((params->req_line_speed == SPEED_AUTO_NEG) && ((params->speed_cap_mask & PORT_HW_CFG_SPEED_CAPABILITY_D0_1G))) { DP(NETIF_MSG_LINK, "Setting 1G clause37 \n"); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_PMA_REG_8727_MISC_CTRL, 0); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_CL37_AN, 0x1300); } else { /* Since the 8727 has only single reset pin, need to set the 10G registers although it is default */ bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_CTRL, 0x0020); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, 0x7, 0x0100); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_CTRL, 0x2040); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_10G_CTRL2, 0x0008); } /* Set 2-wire transfer rate to 400Khz since 100Khz is not operational */ bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_8727_TWO_WIRE_SLAVE_ADDR, 0xa101); /* Set TX PreEmphasis if needed */ if ((params->feature_config_flags & FEATURE_CONFIG_OVERRIDE_PREEMPHASIS_ENABLED)) { DP(NETIF_MSG_LINK, "Setting TX_CTRL1 0x%x," "TX_CTRL2 0x%x\n", params->xgxs_config_tx[0], params->xgxs_config_tx[1]); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_8727_TX_CTRL1, params->xgxs_config_tx[0]); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_8727_TX_CTRL2, params->xgxs_config_tx[1]); } break; } case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101: { u16 fw_ver1, fw_ver2; DP(NETIF_MSG_LINK, "Setting the SFX7101 LASI indication\n"); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_LASI_CTRL, 0x1); DP(NETIF_MSG_LINK, "Setting the SFX7101 LED to blink on traffic\n"); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_7107_LED_CNTL, (1<<3)); bnx2x_ext_phy_set_pause(params, vars); /* Restart autoneg */ bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_CTRL, &val); val |= 0x200; bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_CTRL, val); /* Save spirom version */ bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_7101_VER1, &fw_ver1); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_7101_VER2, &fw_ver2); bnx2x_save_spirom_version(params->bp, params->port, params->shmem_base, (u32)(fw_ver1<<16 | fw_ver2)); break; } case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8481: /* This phy uses the NIG latch mechanism since link indication arrives through its LED4 and not via its LASI signal, so we get steady signal instead of clear on read */ bnx2x_bits_en(bp, NIG_REG_LATCH_BC_0 + params->port*4, 1 << NIG_LATCH_BC_ENABLE_MI_INT); bnx2x_8481_set_led4(params, ext_phy_type, ext_phy_addr); if (params->req_line_speed == SPEED_AUTO_NEG) { u16 autoneg_val, an_1000_val, an_10_100_val; /* set 1000 speed advertisement */ bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_8481_1000T_CTRL, &an_1000_val); if (params->speed_cap_mask & PORT_HW_CFG_SPEED_CAPABILITY_D0_1G) { an_1000_val |= (1<<8); if (params->req_duplex == DUPLEX_FULL) an_1000_val |= (1<<9); DP(NETIF_MSG_LINK, "Advertising 1G\n"); } else an_1000_val &= ~((1<<8) | (1<<9)); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_8481_1000T_CTRL, an_1000_val); /* set 100 speed advertisement */ bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_8481_LEGACY_AN_ADV, &an_10_100_val); if (params->speed_cap_mask & (PORT_HW_CFG_SPEED_CAPABILITY_D0_100M_FULL | PORT_HW_CFG_SPEED_CAPABILITY_D0_100M_HALF)) { an_10_100_val |= (1<<7); if (params->req_duplex == DUPLEX_FULL) an_10_100_val |= (1<<8); DP(NETIF_MSG_LINK, "Advertising 100M\n"); } else an_10_100_val &= ~((1<<7) | (1<<8)); /* set 10 speed advertisement */ if (params->speed_cap_mask & (PORT_HW_CFG_SPEED_CAPABILITY_D0_10M_FULL | PORT_HW_CFG_SPEED_CAPABILITY_D0_10M_HALF)) { an_10_100_val |= (1<<5); if (params->req_duplex == DUPLEX_FULL) an_10_100_val |= (1<<6); DP(NETIF_MSG_LINK, "Advertising 10M\n"); } else an_10_100_val &= ~((1<<5) | (1<<6)); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_8481_LEGACY_AN_ADV, an_10_100_val); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_8481_LEGACY_MII_CTRL, &autoneg_val); /* Disable forced speed */ autoneg_val &= ~(1<<6|1<<13); /* Enable autoneg and restart autoneg for legacy speeds */ autoneg_val |= (1<<9|1<<12); if (params->req_duplex == DUPLEX_FULL) autoneg_val |= (1<<8); else autoneg_val &= ~(1<<8); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_8481_LEGACY_MII_CTRL, autoneg_val); if (params->speed_cap_mask & PORT_HW_CFG_SPEED_CAPABILITY_D0_10G) { DP(NETIF_MSG_LINK, "Advertising 10G\n"); /* Restart autoneg for 10G*/ bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_CTRL, &val); val |= 0x200; bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_CTRL, val); } } else { /* Force speed */ u16 autoneg_ctrl, pma_ctrl; bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_8481_LEGACY_MII_CTRL, &autoneg_ctrl); /* Disable autoneg */ autoneg_ctrl &= ~(1<<12); /* Set 1000 force */ switch (params->req_line_speed) { case SPEED_10000: DP(NETIF_MSG_LINK, "Unable to set 10G force !\n"); break; case SPEED_1000: bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_CTRL, &pma_ctrl); autoneg_ctrl &= ~(1<<13); autoneg_ctrl |= (1<<6); pma_ctrl &= ~(1<<13); pma_ctrl |= (1<<6); DP(NETIF_MSG_LINK, "Setting 1000M force\n"); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_CTRL, pma_ctrl); break; case SPEED_100: autoneg_ctrl |= (1<<13); autoneg_ctrl &= ~(1<<6); DP(NETIF_MSG_LINK, "Setting 100M force\n"); break; case SPEED_10: autoneg_ctrl &= ~(1<<13); autoneg_ctrl &= ~(1<<6); DP(NETIF_MSG_LINK, "Setting 10M force\n"); break; } /* Duplex mode */ if (params->req_duplex == DUPLEX_FULL) { autoneg_ctrl |= (1<<8); DP(NETIF_MSG_LINK, "Setting full duplex\n"); } else autoneg_ctrl &= ~(1<<8); /* Update autoneg ctrl and pma ctrl */ bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_8481_LEGACY_MII_CTRL, autoneg_ctrl); } /* Save spirom version */ bnx2x_save_8481_spirom_version(bp, params->port, ext_phy_addr, params->shmem_base); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_FAILURE: DP(NETIF_MSG_LINK, "XGXS PHY Failure detected 0x%x\n", params->ext_phy_config); rc = -EINVAL; break; default: DP(NETIF_MSG_LINK, "BAD XGXS ext_phy_config 0x%x\n", params->ext_phy_config); rc = -EINVAL; break; } } else { /* SerDes */ ext_phy_type = SERDES_EXT_PHY_TYPE(params->ext_phy_config); switch (ext_phy_type) { case PORT_HW_CFG_SERDES_EXT_PHY_TYPE_DIRECT: DP(NETIF_MSG_LINK, "SerDes Direct\n"); break; case PORT_HW_CFG_SERDES_EXT_PHY_TYPE_BCM5482: DP(NETIF_MSG_LINK, "SerDes 5482\n"); break; default: DP(NETIF_MSG_LINK, "BAD SerDes ext_phy_config 0x%x\n", params->ext_phy_config); break; } } return rc; } static void bnx2x_8727_handle_mod_abs(struct link_params *params) { struct bnx2x *bp = params->bp; u16 mod_abs, rx_alarm_status; u8 ext_phy_addr = XGXS_EXT_PHY_ADDR(params->ext_phy_config); u32 val = REG_RD(bp, params->shmem_base + offsetof(struct shmem_region, dev_info. port_feature_config[params->port]. config)); bnx2x_cl45_read(bp, params->port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8727, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_PHY_IDENTIFIER, &mod_abs); if (mod_abs & (1<<8)) { /* Module is absent */ DP(NETIF_MSG_LINK, "MOD_ABS indication " "show module is absent\n"); /* 1. Set mod_abs to detect next module presence event 2. Set EDC off by setting OPTXLOS signal input to low (bit 9). When the EDC is off it locks onto a reference clock and avoids becoming 'lost'.*/ mod_abs &= ~((1<<8)|(1<<9)); bnx2x_cl45_write(bp, params->port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8727, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_PHY_IDENTIFIER, mod_abs); /* Clear RX alarm since it stays up as long as the mod_abs wasn't changed */ bnx2x_cl45_read(bp, params->port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8727, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_RX_ALARM, &rx_alarm_status); } else { /* Module is present */ DP(NETIF_MSG_LINK, "MOD_ABS indication " "show module is present\n"); /* First thing, disable transmitter, and if the module is ok, the module_detection will enable it*/ /* 1. Set mod_abs to detect next module absent event ( bit 8) 2. Restore the default polarity of the OPRXLOS signal and this signal will then correctly indicate the presence or absence of the Rx signal. (bit 9) */ mod_abs |= ((1<<8)|(1<<9)); bnx2x_cl45_write(bp, params->port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8727, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_PHY_IDENTIFIER, mod_abs); /* Clear RX alarm since it stays up as long as the mod_abs wasn't changed. This is need to be done before calling the module detection, otherwise it will clear the link update alarm */ bnx2x_cl45_read(bp, params->port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8727, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_RX_ALARM, &rx_alarm_status); if ((val & PORT_FEAT_CFG_OPT_MDL_ENFRCMNT_MASK) == PORT_FEAT_CFG_OPT_MDL_ENFRCMNT_DISABLE_TX_LASER) bnx2x_sfp_set_transmitter(bp, params->port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8727, ext_phy_addr, 0); if (bnx2x_wait_for_sfp_module_initialized(params) == 0) bnx2x_sfp_module_detection(params); else DP(NETIF_MSG_LINK, "SFP+ module is not initialized\n"); } DP(NETIF_MSG_LINK, "8727 RX_ALARM_STATUS 0x%x\n", rx_alarm_status); /* No need to check link status in case of module plugged in/out */ } static u8 bnx2x_ext_phy_is_link_up(struct link_params *params, struct link_vars *vars, u8 is_mi_int) { struct bnx2x *bp = params->bp; u32 ext_phy_type; u8 ext_phy_addr; u16 val1 = 0, val2; u16 rx_sd, pcs_status; u8 ext_phy_link_up = 0; u8 port = params->port; if (vars->phy_flags & PHY_XGXS_FLAG) { ext_phy_addr = XGXS_EXT_PHY_ADDR(params->ext_phy_config); ext_phy_type = XGXS_EXT_PHY_TYPE(params->ext_phy_config); switch (ext_phy_type) { case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT: DP(NETIF_MSG_LINK, "XGXS Direct\n"); ext_phy_link_up = 1; break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8705: DP(NETIF_MSG_LINK, "XGXS 8705\n"); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_WIS_DEVAD, MDIO_WIS_REG_LASI_STATUS, &val1); DP(NETIF_MSG_LINK, "8705 LASI status 0x%x\n", val1); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_WIS_DEVAD, MDIO_WIS_REG_LASI_STATUS, &val1); DP(NETIF_MSG_LINK, "8705 LASI status 0x%x\n", val1); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_RX_SD, &rx_sd); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, 1, 0xc809, &val1); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, 1, 0xc809, &val1); DP(NETIF_MSG_LINK, "8705 1.c809 val=0x%x\n", val1); ext_phy_link_up = ((rx_sd & 0x1) && (val1 & (1<<9)) && ((val1 & (1<<8)) == 0)); if (ext_phy_link_up) vars->line_speed = SPEED_10000; break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8706: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8726: DP(NETIF_MSG_LINK, "XGXS 8706/8726\n"); /* Clear RX Alarm*/ bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_RX_ALARM, &val2); /* clear LASI indication*/ bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_LASI_STATUS, &val1); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_LASI_STATUS, &val2); DP(NETIF_MSG_LINK, "8706/8726 LASI status 0x%x-->" "0x%x\n", val1, val2); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_RX_SD, &rx_sd); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PCS_DEVAD, MDIO_PCS_REG_STATUS, &pcs_status); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_LINK_STATUS, &val2); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_LINK_STATUS, &val2); DP(NETIF_MSG_LINK, "8706/8726 rx_sd 0x%x" " pcs_status 0x%x 1Gbps link_status 0x%x\n", rx_sd, pcs_status, val2); /* link is up if both bit 0 of pmd_rx_sd and * bit 0 of pcs_status are set, or if the autoneg bit 1 is set */ ext_phy_link_up = ((rx_sd & pcs_status & 0x1) || (val2 & (1<<1))); if (ext_phy_link_up) { if (ext_phy_type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8726) { /* If transmitter is disabled, ignore false link up indication */ bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_PHY_IDENTIFIER, &val1); if (val1 & (1<<15)) { DP(NETIF_MSG_LINK, "Tx is " "disabled\n"); ext_phy_link_up = 0; break; } } if (val2 & (1<<1)) vars->line_speed = SPEED_1000; else vars->line_speed = SPEED_10000; } break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8727: { u16 link_status = 0; u16 rx_alarm_status; /* Check the LASI */ bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_RX_ALARM, &rx_alarm_status); DP(NETIF_MSG_LINK, "8727 RX_ALARM_STATUS 0x%x\n", rx_alarm_status); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_LASI_STATUS, &val1); DP(NETIF_MSG_LINK, "8727 LASI status 0x%x\n", val1); /* Clear MSG-OUT */ bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_M8051_MSGOUT_REG, &val1); /* * If a module is present and there is need to check * for over current */ if (!(params->feature_config_flags & FEATURE_CONFIG_BCM8727_NOC) && !(rx_alarm_status & (1<<5))) { /* Check over-current using 8727 GPIO0 input*/ bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_8727_GPIO_CTRL, &val1); if ((val1 & (1<<8)) == 0) { DP(NETIF_MSG_LINK, "8727 Power fault" " has been detected on " "port %d\n", params->port); printk(KERN_ERR PFX "Error: Power" " fault on %s Port %d has" " been detected and the" " power to that SFP+ module" " has been removed to prevent" " failure of the card. Please" " remove the SFP+ module and" " restart the system to clear" " this error.\n" , bp->dev->name, params->port); /* * Disable all RX_ALARMs except for * mod_abs */ bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_RX_ALARM_CTRL, (1<<5)); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_PHY_IDENTIFIER, &val1); /* Wait for module_absent_event */ val1 |= (1<<8); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_PHY_IDENTIFIER, val1); /* Clear RX alarm */ bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_RX_ALARM, &rx_alarm_status); break; } } /* Over current check */ /* When module absent bit is set, check module */ if (rx_alarm_status & (1<<5)) { bnx2x_8727_handle_mod_abs(params); /* Enable all mod_abs and link detection bits */ bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_RX_ALARM_CTRL, ((1<<5) | (1<<2))); } /* If transmitter is disabled, ignore false link up indication */ bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_PHY_IDENTIFIER, &val1); if (val1 & (1<<15)) { DP(NETIF_MSG_LINK, "Tx is disabled\n"); ext_phy_link_up = 0; break; } bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_8073_SPEED_LINK_STATUS, &link_status); /* Bits 0..2 --> speed detected, bits 13..15--> link is down */ if ((link_status & (1<<2)) && (!(link_status & (1<<15)))) { ext_phy_link_up = 1; vars->line_speed = SPEED_10000; } else if ((link_status & (1<<0)) && (!(link_status & (1<<13)))) { ext_phy_link_up = 1; vars->line_speed = SPEED_1000; DP(NETIF_MSG_LINK, "port %x: External link" " up in 1G\n", params->port); } else { ext_phy_link_up = 0; DP(NETIF_MSG_LINK, "port %x: External link" " is down\n", params->port); } break; } case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8072: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073: { u16 link_status = 0; u16 an1000_status = 0; if (ext_phy_type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8072) { bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PCS_DEVAD, MDIO_PCS_REG_LASI_STATUS, &val1); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PCS_DEVAD, MDIO_PCS_REG_LASI_STATUS, &val2); DP(NETIF_MSG_LINK, "870x LASI status 0x%x->0x%x\n", val1, val2); } else { /* In 8073, port1 is directed through emac0 and * port0 is directed through emac1 */ bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_LASI_STATUS, &val1); DP(NETIF_MSG_LINK, "8703 LASI status 0x%x\n", val1); } /* clear the interrupt LASI status register */ bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PCS_DEVAD, MDIO_PCS_REG_STATUS, &val2); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PCS_DEVAD, MDIO_PCS_REG_STATUS, &val1); DP(NETIF_MSG_LINK, "807x PCS status 0x%x->0x%x\n", val2, val1); /* Clear MSG-OUT */ bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_M8051_MSGOUT_REG, &val1); /* Check the LASI */ bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_RX_ALARM, &val2); DP(NETIF_MSG_LINK, "KR 0x9003 0x%x\n", val2); /* Check the link status */ bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PCS_DEVAD, MDIO_PCS_REG_STATUS, &val2); DP(NETIF_MSG_LINK, "KR PCS status 0x%x\n", val2); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_STATUS, &val2); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_STATUS, &val1); ext_phy_link_up = ((val1 & 4) == 4); DP(NETIF_MSG_LINK, "PMA_REG_STATUS=0x%x\n", val1); if (ext_phy_type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073) { if (ext_phy_link_up && ((params->req_line_speed != SPEED_10000))) { if (bnx2x_bcm8073_xaui_wa(params) != 0) { ext_phy_link_up = 0; break; } } bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_LINK_STATUS, &an1000_status); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_LINK_STATUS, &an1000_status); /* Check the link status on 1.1.2 */ bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_STATUS, &val2); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_STATUS, &val1); DP(NETIF_MSG_LINK, "KR PMA status 0x%x->0x%x," "an_link_status=0x%x\n", val2, val1, an1000_status); ext_phy_link_up = (((val1 & 4) == 4) || (an1000_status & (1<<1))); if (ext_phy_link_up && bnx2x_8073_is_snr_needed(params)) { /* The SNR will improve about 2dbby changing the BW and FEE main tap.*/ /* The 1st write to change FFE main tap is set before restart AN */ /* Change PLL Bandwidth in EDC register */ bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_PLL_BANDWIDTH, 0x26BC); /* Change CDR Bandwidth in EDC register */ bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_CDR_BANDWIDTH, 0x0333); } bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_8073_SPEED_LINK_STATUS, &link_status); /* Bits 0..2 --> speed detected, bits 13..15--> link is down */ if ((link_status & (1<<2)) && (!(link_status & (1<<15)))) { ext_phy_link_up = 1; vars->line_speed = SPEED_10000; DP(NETIF_MSG_LINK, "port %x: External link" " up in 10G\n", params->port); } else if ((link_status & (1<<1)) && (!(link_status & (1<<14)))) { ext_phy_link_up = 1; vars->line_speed = SPEED_2500; DP(NETIF_MSG_LINK, "port %x: External link" " up in 2.5G\n", params->port); } else if ((link_status & (1<<0)) && (!(link_status & (1<<13)))) { ext_phy_link_up = 1; vars->line_speed = SPEED_1000; DP(NETIF_MSG_LINK, "port %x: External link" " up in 1G\n", params->port); } else { ext_phy_link_up = 0; DP(NETIF_MSG_LINK, "port %x: External link" " is down\n", params->port); } } else { /* See if 1G link is up for the 8072 */ bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_LINK_STATUS, &an1000_status); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_LINK_STATUS, &an1000_status); if (an1000_status & (1<<1)) { ext_phy_link_up = 1; vars->line_speed = SPEED_1000; DP(NETIF_MSG_LINK, "port %x: External link" " up in 1G\n", params->port); } else if (ext_phy_link_up) { ext_phy_link_up = 1; vars->line_speed = SPEED_10000; DP(NETIF_MSG_LINK, "port %x: External link" " up in 10G\n", params->port); } } break; } case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101: bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_LASI_STATUS, &val2); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_LASI_STATUS, &val1); DP(NETIF_MSG_LINK, "10G-base-T LASI status 0x%x->0x%x\n", val2, val1); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_STATUS, &val2); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_STATUS, &val1); DP(NETIF_MSG_LINK, "10G-base-T PMA status 0x%x->0x%x\n", val2, val1); ext_phy_link_up = ((val1 & 4) == 4); /* if link is up * print the AN outcome of the SFX7101 PHY */ if (ext_phy_link_up) { bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_MASTER_STATUS, &val2); vars->line_speed = SPEED_10000; DP(NETIF_MSG_LINK, "SFX7101 AN status 0x%x->Master=%x\n", val2, (val2 & (1<<14))); } break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8481: /* Check 10G-BaseT link status */ /* Check PMD signal ok */ bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, 0xFFFA, &val1); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_8481_PMD_SIGNAL, &val2); DP(NETIF_MSG_LINK, "PMD_SIGNAL 1.a811 = 0x%x\n", val2); /* Check link 10G */ if (val2 & (1<<11)) { vars->line_speed = SPEED_10000; ext_phy_link_up = 1; bnx2x_8481_set_10G_led_mode(params, ext_phy_type, ext_phy_addr); } else { /* Check Legacy speed link */ u16 legacy_status, legacy_speed; /* Enable expansion register 0x42 (Operation mode status) */ bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_8481_EXPANSION_REG_ACCESS, 0xf42); /* Get legacy speed operation status */ bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_8481_EXPANSION_REG_RD_RW, &legacy_status); DP(NETIF_MSG_LINK, "Legacy speed status" " = 0x%x\n", legacy_status); ext_phy_link_up = ((legacy_status & (1<<11)) == (1<<11)); if (ext_phy_link_up) { legacy_speed = (legacy_status & (3<<9)); if (legacy_speed == (0<<9)) vars->line_speed = SPEED_10; else if (legacy_speed == (1<<9)) vars->line_speed = SPEED_100; else if (legacy_speed == (2<<9)) vars->line_speed = SPEED_1000; else /* Should not happen */ vars->line_speed = 0; if (legacy_status & (1<<8)) vars->duplex = DUPLEX_FULL; else vars->duplex = DUPLEX_HALF; DP(NETIF_MSG_LINK, "Link is up " "in %dMbps, is_duplex_full" "= %d\n", vars->line_speed, (vars->duplex == DUPLEX_FULL)); bnx2x_8481_set_legacy_led_mode(params, ext_phy_type, ext_phy_addr); } } break; default: DP(NETIF_MSG_LINK, "BAD XGXS ext_phy_config 0x%x\n", params->ext_phy_config); ext_phy_link_up = 0; break; } /* Set SGMII mode for external phy */ if (ext_phy_type != PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT) { if (vars->line_speed < SPEED_1000) vars->phy_flags |= PHY_SGMII_FLAG; else vars->phy_flags &= ~PHY_SGMII_FLAG; } } else { /* SerDes */ ext_phy_type = SERDES_EXT_PHY_TYPE(params->ext_phy_config); switch (ext_phy_type) { case PORT_HW_CFG_SERDES_EXT_PHY_TYPE_DIRECT: DP(NETIF_MSG_LINK, "SerDes Direct\n"); ext_phy_link_up = 1; break; case PORT_HW_CFG_SERDES_EXT_PHY_TYPE_BCM5482: DP(NETIF_MSG_LINK, "SerDes 5482\n"); ext_phy_link_up = 1; break; default: DP(NETIF_MSG_LINK, "BAD SerDes ext_phy_config 0x%x\n", params->ext_phy_config); ext_phy_link_up = 0; break; } } return ext_phy_link_up; } static void bnx2x_link_int_enable(struct link_params *params) { u8 port = params->port; u32 ext_phy_type; u32 mask; struct bnx2x *bp = params->bp; /* setting the status to report on link up for either XGXS or SerDes */ if (params->switch_cfg == SWITCH_CFG_10G) { mask = (NIG_MASK_XGXS0_LINK10G | NIG_MASK_XGXS0_LINK_STATUS); DP(NETIF_MSG_LINK, "enabled XGXS interrupt\n"); ext_phy_type = XGXS_EXT_PHY_TYPE(params->ext_phy_config); if ((ext_phy_type != PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT) && (ext_phy_type != PORT_HW_CFG_XGXS_EXT_PHY_TYPE_FAILURE) && (ext_phy_type != PORT_HW_CFG_XGXS_EXT_PHY_TYPE_NOT_CONN)) { mask |= NIG_MASK_MI_INT; DP(NETIF_MSG_LINK, "enabled external phy int\n"); } } else { /* SerDes */ mask = NIG_MASK_SERDES0_LINK_STATUS; DP(NETIF_MSG_LINK, "enabled SerDes interrupt\n"); ext_phy_type = SERDES_EXT_PHY_TYPE(params->ext_phy_config); if ((ext_phy_type != PORT_HW_CFG_SERDES_EXT_PHY_TYPE_DIRECT) && (ext_phy_type != PORT_HW_CFG_SERDES_EXT_PHY_TYPE_NOT_CONN)) { mask |= NIG_MASK_MI_INT; DP(NETIF_MSG_LINK, "enabled external phy int\n"); } } bnx2x_bits_en(bp, NIG_REG_MASK_INTERRUPT_PORT0 + port*4, mask); DP(NETIF_MSG_LINK, "port %x, is_xgxs %x, int_status 0x%x\n", port, (params->switch_cfg == SWITCH_CFG_10G), REG_RD(bp, NIG_REG_STATUS_INTERRUPT_PORT0 + port*4)); DP(NETIF_MSG_LINK, " int_mask 0x%x, MI_INT %x, SERDES_LINK %x\n", REG_RD(bp, NIG_REG_MASK_INTERRUPT_PORT0 + port*4), REG_RD(bp, NIG_REG_EMAC0_STATUS_MISC_MI_INT + port*0x18), REG_RD(bp, NIG_REG_SERDES0_STATUS_LINK_STATUS+port*0x3c)); DP(NETIF_MSG_LINK, " 10G %x, XGXS_LINK %x\n", REG_RD(bp, NIG_REG_XGXS0_STATUS_LINK10G + port*0x68), REG_RD(bp, NIG_REG_XGXS0_STATUS_LINK_STATUS + port*0x68)); } static void bnx2x_8481_rearm_latch_signal(struct bnx2x *bp, u8 port, u8 is_mi_int) { u32 latch_status = 0, is_mi_int_status; /* Disable the MI INT ( external phy int ) * by writing 1 to the status register. Link down indication * is high-active-signal, so in this case we need to write the * status to clear the XOR */ /* Read Latched signals */ latch_status = REG_RD(bp, NIG_REG_LATCH_STATUS_0 + port*8); is_mi_int_status = REG_RD(bp, NIG_REG_STATUS_INTERRUPT_PORT0 + port*4); DP(NETIF_MSG_LINK, "original_signal = 0x%x, nig_status = 0x%x," "latch_status = 0x%x\n", is_mi_int, is_mi_int_status, latch_status); /* Handle only those with latched-signal=up.*/ if (latch_status & 1) { /* For all latched-signal=up,Write original_signal to status */ if (is_mi_int) bnx2x_bits_en(bp, NIG_REG_STATUS_INTERRUPT_PORT0 + port*4, NIG_STATUS_EMAC0_MI_INT); else bnx2x_bits_dis(bp, NIG_REG_STATUS_INTERRUPT_PORT0 + port*4, NIG_STATUS_EMAC0_MI_INT); /* For all latched-signal=up : Re-Arm Latch signals */ REG_WR(bp, NIG_REG_LATCH_STATUS_0 + port*8, (latch_status & 0xfffe) | (latch_status & 1)); } } /* * link management */ static void bnx2x_link_int_ack(struct link_params *params, struct link_vars *vars, u8 is_10g, u8 is_mi_int) { struct bnx2x *bp = params->bp; u8 port = params->port; /* first reset all status * we assume only one line will be change at a time */ bnx2x_bits_dis(bp, NIG_REG_STATUS_INTERRUPT_PORT0 + port*4, (NIG_STATUS_XGXS0_LINK10G | NIG_STATUS_XGXS0_LINK_STATUS | NIG_STATUS_SERDES0_LINK_STATUS)); if (XGXS_EXT_PHY_TYPE(params->ext_phy_config) == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8481) { bnx2x_8481_rearm_latch_signal(bp, port, is_mi_int); } if (vars->phy_link_up) { if (is_10g) { /* Disable the 10G link interrupt * by writing 1 to the status register */ DP(NETIF_MSG_LINK, "10G XGXS phy link up\n"); bnx2x_bits_en(bp, NIG_REG_STATUS_INTERRUPT_PORT0 + port*4, NIG_STATUS_XGXS0_LINK10G); } else if (params->switch_cfg == SWITCH_CFG_10G) { /* Disable the link interrupt * by writing 1 to the relevant lane * in the status register */ u32 ser_lane = ((params->lane_config & PORT_HW_CFG_LANE_SWAP_CFG_MASTER_MASK) >> PORT_HW_CFG_LANE_SWAP_CFG_MASTER_SHIFT); DP(NETIF_MSG_LINK, "%d speed XGXS phy link up\n", vars->line_speed); bnx2x_bits_en(bp, NIG_REG_STATUS_INTERRUPT_PORT0 + port*4, ((1 << ser_lane) << NIG_STATUS_XGXS0_LINK_STATUS_SIZE)); } else { /* SerDes */ DP(NETIF_MSG_LINK, "SerDes phy link up\n"); /* Disable the link interrupt * by writing 1 to the status register */ bnx2x_bits_en(bp, NIG_REG_STATUS_INTERRUPT_PORT0 + port*4, NIG_STATUS_SERDES0_LINK_STATUS); } } else { /* link_down */ } } static u8 bnx2x_format_ver(u32 num, u8 *str, u16 len) { u8 *str_ptr = str; u32 mask = 0xf0000000; u8 shift = 8*4; u8 digit; if (len < 10) { /* Need more than 10chars for this format */ *str_ptr = '\0'; return -EINVAL; } while (shift > 0) { shift -= 4; digit = ((num & mask) >> shift); if (digit < 0xa) *str_ptr = digit + '0'; else *str_ptr = digit - 0xa + 'a'; str_ptr++; mask = mask >> 4; if (shift == 4*4) { *str_ptr = ':'; str_ptr++; } } *str_ptr = '\0'; return 0; } u8 bnx2x_get_ext_phy_fw_version(struct link_params *params, u8 driver_loaded, u8 *version, u16 len) { struct bnx2x *bp; u32 ext_phy_type = 0; u32 spirom_ver = 0; u8 status; if (version == NULL || params == NULL) return -EINVAL; bp = params->bp; spirom_ver = REG_RD(bp, params->shmem_base + offsetof(struct shmem_region, port_mb[params->port].ext_phy_fw_version)); status = 0; /* reset the returned value to zero */ ext_phy_type = XGXS_EXT_PHY_TYPE(params->ext_phy_config); switch (ext_phy_type) { case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101: if (len < 5) return -EINVAL; version[0] = (spirom_ver & 0xFF); version[1] = (spirom_ver & 0xFF00) >> 8; version[2] = (spirom_ver & 0xFF0000) >> 16; version[3] = (spirom_ver & 0xFF000000) >> 24; version[4] = '\0'; break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8072: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8727: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8706: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8726: status = bnx2x_format_ver(spirom_ver, version, len); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8481: spirom_ver = ((spirom_ver & 0xF80) >> 7) << 16 | (spirom_ver & 0x7F); status = bnx2x_format_ver(spirom_ver, version, len); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8705: version[0] = '\0'; break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_FAILURE: DP(NETIF_MSG_LINK, "bnx2x_get_ext_phy_fw_version:" " type is FAILURE!\n"); status = -EINVAL; break; default: break; } return status; } static void bnx2x_set_xgxs_loopback(struct link_params *params, struct link_vars *vars, u8 is_10g) { u8 port = params->port; struct bnx2x *bp = params->bp; if (is_10g) { u32 md_devad; DP(NETIF_MSG_LINK, "XGXS 10G loopback enable\n"); /* change the uni_phy_addr in the nig */ md_devad = REG_RD(bp, (NIG_REG_XGXS0_CTRL_MD_DEVAD + port*0x18)); REG_WR(bp, NIG_REG_XGXS0_CTRL_MD_DEVAD + port*0x18, 0x5); bnx2x_cl45_write(bp, port, 0, params->phy_addr, 5, (MDIO_REG_BANK_AER_BLOCK + (MDIO_AER_BLOCK_AER_REG & 0xf)), 0x2800); bnx2x_cl45_write(bp, port, 0, params->phy_addr, 5, (MDIO_REG_BANK_CL73_IEEEB0 + (MDIO_CL73_IEEEB0_CL73_AN_CONTROL & 0xf)), 0x6041); msleep(200); /* set aer mmd back */ bnx2x_set_aer_mmd(params, vars); /* and md_devad */ REG_WR(bp, NIG_REG_XGXS0_CTRL_MD_DEVAD + port*0x18, md_devad); } else { u16 mii_control; DP(NETIF_MSG_LINK, "XGXS 1G loopback enable\n"); CL45_RD_OVER_CL22(bp, port, params->phy_addr, MDIO_REG_BANK_COMBO_IEEE0, MDIO_COMBO_IEEE0_MII_CONTROL, &mii_control); CL45_WR_OVER_CL22(bp, port, params->phy_addr, MDIO_REG_BANK_COMBO_IEEE0, MDIO_COMBO_IEEE0_MII_CONTROL, (mii_control | MDIO_COMBO_IEEO_MII_CONTROL_LOOPBACK)); } } static void bnx2x_ext_phy_loopback(struct link_params *params) { struct bnx2x *bp = params->bp; u8 ext_phy_addr; u32 ext_phy_type; if (params->switch_cfg == SWITCH_CFG_10G) { ext_phy_type = XGXS_EXT_PHY_TYPE(params->ext_phy_config); ext_phy_addr = XGXS_EXT_PHY_ADDR(params->ext_phy_config); /* CL37 Autoneg Enabled */ switch (ext_phy_type) { case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_NOT_CONN: DP(NETIF_MSG_LINK, "ext_phy_loopback: We should not get here\n"); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8705: DP(NETIF_MSG_LINK, "ext_phy_loopback: 8705\n"); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8706: DP(NETIF_MSG_LINK, "ext_phy_loopback: 8706\n"); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8726: DP(NETIF_MSG_LINK, "PMA/PMD ext_phy_loopback: 8726\n"); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_CTRL, 0x0001); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101: /* SFX7101_XGXS_TEST1 */ bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_XS_DEVAD, MDIO_XS_SFX7101_XGXS_TEST1, 0x100); DP(NETIF_MSG_LINK, "ext_phy_loopback: set ext phy loopback\n"); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8072: break; } /* switch external PHY type */ } else { /* serdes */ ext_phy_type = SERDES_EXT_PHY_TYPE(params->ext_phy_config); ext_phy_addr = (params->ext_phy_config & PORT_HW_CFG_SERDES_EXT_PHY_ADDR_MASK) >> PORT_HW_CFG_SERDES_EXT_PHY_ADDR_SHIFT; } } /* *------------------------------------------------------------------------ * bnx2x_override_led_value - * * Override the led value of the requsted led * *------------------------------------------------------------------------ */ u8 bnx2x_override_led_value(struct bnx2x *bp, u8 port, u32 led_idx, u32 value) { u32 reg_val; /* If port 0 then use EMAC0, else use EMAC1*/ u32 emac_base = (port) ? GRCBASE_EMAC1 : GRCBASE_EMAC0; DP(NETIF_MSG_LINK, "bnx2x_override_led_value() port %x led_idx %d value %d\n", port, led_idx, value); switch (led_idx) { case 0: /* 10MB led */ /* Read the current value of the LED register in the EMAC block */ reg_val = REG_RD(bp, emac_base + EMAC_REG_EMAC_LED); /* Set the OVERRIDE bit to 1 */ reg_val |= EMAC_LED_OVERRIDE; /* If value is 1, set the 10M_OVERRIDE bit, otherwise reset it.*/ reg_val = (value == 1) ? (reg_val | EMAC_LED_10MB_OVERRIDE) : (reg_val & ~EMAC_LED_10MB_OVERRIDE); REG_WR(bp, emac_base + EMAC_REG_EMAC_LED, reg_val); break; case 1: /*100MB led */ /*Read the current value of the LED register in the EMAC block */ reg_val = REG_RD(bp, emac_base + EMAC_REG_EMAC_LED); /* Set the OVERRIDE bit to 1 */ reg_val |= EMAC_LED_OVERRIDE; /* If value is 1, set the 100M_OVERRIDE bit, otherwise reset it.*/ reg_val = (value == 1) ? (reg_val | EMAC_LED_100MB_OVERRIDE) : (reg_val & ~EMAC_LED_100MB_OVERRIDE); REG_WR(bp, emac_base + EMAC_REG_EMAC_LED, reg_val); break; case 2: /* 1000MB led */ /* Read the current value of the LED register in the EMAC block */ reg_val = REG_RD(bp, emac_base + EMAC_REG_EMAC_LED); /* Set the OVERRIDE bit to 1 */ reg_val |= EMAC_LED_OVERRIDE; /* If value is 1, set the 1000M_OVERRIDE bit, otherwise reset it. */ reg_val = (value == 1) ? (reg_val | EMAC_LED_1000MB_OVERRIDE) : (reg_val & ~EMAC_LED_1000MB_OVERRIDE); REG_WR(bp, emac_base + EMAC_REG_EMAC_LED, reg_val); break; case 3: /* 2500MB led */ /* Read the current value of the LED register in the EMAC block*/ reg_val = REG_RD(bp, emac_base + EMAC_REG_EMAC_LED); /* Set the OVERRIDE bit to 1 */ reg_val |= EMAC_LED_OVERRIDE; /* If value is 1, set the 2500M_OVERRIDE bit, otherwise reset it.*/ reg_val = (value == 1) ? (reg_val | EMAC_LED_2500MB_OVERRIDE) : (reg_val & ~EMAC_LED_2500MB_OVERRIDE); REG_WR(bp, emac_base + EMAC_REG_EMAC_LED, reg_val); break; case 4: /*10G led */ if (port == 0) { REG_WR(bp, NIG_REG_LED_10G_P0, value); } else { REG_WR(bp, NIG_REG_LED_10G_P1, value); } break; case 5: /* TRAFFIC led */ /* Find if the traffic control is via BMAC or EMAC */ if (port == 0) reg_val = REG_RD(bp, NIG_REG_NIG_EMAC0_EN); else reg_val = REG_RD(bp, NIG_REG_NIG_EMAC1_EN); /* Override the traffic led in the EMAC:*/ if (reg_val == 1) { /* Read the current value of the LED register in the EMAC block */ reg_val = REG_RD(bp, emac_base + EMAC_REG_EMAC_LED); /* Set the TRAFFIC_OVERRIDE bit to 1 */ reg_val |= EMAC_LED_OVERRIDE; /* If value is 1, set the TRAFFIC bit, otherwise reset it.*/ reg_val = (value == 1) ? (reg_val | EMAC_LED_TRAFFIC) : (reg_val & ~EMAC_LED_TRAFFIC); REG_WR(bp, emac_base + EMAC_REG_EMAC_LED, reg_val); } else { /* Override the traffic led in the BMAC: */ REG_WR(bp, NIG_REG_LED_CONTROL_OVERRIDE_TRAFFIC_P0 + port*4, 1); REG_WR(bp, NIG_REG_LED_CONTROL_TRAFFIC_P0 + port*4, value); } break; default: DP(NETIF_MSG_LINK, "bnx2x_override_led_value() unknown led index %d " "(should be 0-5)\n", led_idx); return -EINVAL; } return 0; } u8 bnx2x_set_led(struct bnx2x *bp, u8 port, u8 mode, u32 speed, u16 hw_led_mode, u32 chip_id) { u8 rc = 0; u32 tmp; u32 emac_base = port ? GRCBASE_EMAC1 : GRCBASE_EMAC0; DP(NETIF_MSG_LINK, "bnx2x_set_led: port %x, mode %d\n", port, mode); DP(NETIF_MSG_LINK, "speed 0x%x, hw_led_mode 0x%x\n", speed, hw_led_mode); switch (mode) { case LED_MODE_OFF: REG_WR(bp, NIG_REG_LED_10G_P0 + port*4, 0); REG_WR(bp, NIG_REG_LED_MODE_P0 + port*4, SHARED_HW_CFG_LED_MAC1); tmp = EMAC_RD(bp, EMAC_REG_EMAC_LED); EMAC_WR(bp, EMAC_REG_EMAC_LED, (tmp | EMAC_LED_OVERRIDE)); break; case LED_MODE_OPER: REG_WR(bp, NIG_REG_LED_MODE_P0 + port*4, hw_led_mode); REG_WR(bp, NIG_REG_LED_CONTROL_OVERRIDE_TRAFFIC_P0 + port*4, 0); /* Set blinking rate to ~15.9Hz */ REG_WR(bp, NIG_REG_LED_CONTROL_BLINK_RATE_P0 + port*4, LED_BLINK_RATE_VAL); REG_WR(bp, NIG_REG_LED_CONTROL_BLINK_RATE_ENA_P0 + port*4, 1); tmp = EMAC_RD(bp, EMAC_REG_EMAC_LED); EMAC_WR(bp, EMAC_REG_EMAC_LED, (tmp & (~EMAC_LED_OVERRIDE))); if (!CHIP_IS_E1H(bp) && ((speed == SPEED_2500) || (speed == SPEED_1000) || (speed == SPEED_100) || (speed == SPEED_10))) { /* On Everest 1 Ax chip versions for speeds less than 10G LED scheme is different */ REG_WR(bp, NIG_REG_LED_CONTROL_OVERRIDE_TRAFFIC_P0 + port*4, 1); REG_WR(bp, NIG_REG_LED_CONTROL_TRAFFIC_P0 + port*4, 0); REG_WR(bp, NIG_REG_LED_CONTROL_BLINK_TRAFFIC_P0 + port*4, 1); } break; default: rc = -EINVAL; DP(NETIF_MSG_LINK, "bnx2x_set_led: Invalid led mode %d\n", mode); break; } return rc; } u8 bnx2x_test_link(struct link_params *params, struct link_vars *vars) { struct bnx2x *bp = params->bp; u16 gp_status = 0; CL45_RD_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_GP_STATUS, MDIO_GP_STATUS_TOP_AN_STATUS1, &gp_status); /* link is up only if both local phy and external phy are up */ if ((gp_status & MDIO_GP_STATUS_TOP_AN_STATUS1_LINK_STATUS) && bnx2x_ext_phy_is_link_up(params, vars, 1)) return 0; return -ESRCH; } static u8 bnx2x_link_initialize(struct link_params *params, struct link_vars *vars) { struct bnx2x *bp = params->bp; u8 port = params->port; u8 rc = 0; u8 non_ext_phy; u32 ext_phy_type = XGXS_EXT_PHY_TYPE(params->ext_phy_config); /* Activate the external PHY */ bnx2x_ext_phy_reset(params, vars); bnx2x_set_aer_mmd(params, vars); if (vars->phy_flags & PHY_XGXS_FLAG) bnx2x_set_master_ln(params); rc = bnx2x_reset_unicore(params); /* reset the SerDes and wait for reset bit return low */ if (rc != 0) return rc; bnx2x_set_aer_mmd(params, vars); /* setting the masterLn_def again after the reset */ if (vars->phy_flags & PHY_XGXS_FLAG) { bnx2x_set_master_ln(params); bnx2x_set_swap_lanes(params); } if (vars->phy_flags & PHY_XGXS_FLAG) { if ((params->req_line_speed && ((params->req_line_speed == SPEED_100) || (params->req_line_speed == SPEED_10))) || (!params->req_line_speed && (params->speed_cap_mask >= PORT_HW_CFG_SPEED_CAPABILITY_D0_10M_FULL) && (params->speed_cap_mask < PORT_HW_CFG_SPEED_CAPABILITY_D0_1G) )) { vars->phy_flags |= PHY_SGMII_FLAG; } else { vars->phy_flags &= ~PHY_SGMII_FLAG; } } /* In case of external phy existance, the line speed would be the line speed linked up by the external phy. In case it is direct only, then the line_speed during initialization will be equal to the req_line_speed*/ vars->line_speed = params->req_line_speed; bnx2x_calc_ieee_aneg_adv(params, &vars->ieee_fc); /* init ext phy and enable link state int */ non_ext_phy = ((ext_phy_type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT) || (params->loopback_mode == LOOPBACK_XGXS_10)); if (non_ext_phy || (ext_phy_type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8705) || (ext_phy_type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8726) || (params->loopback_mode == LOOPBACK_EXT_PHY)) { if (params->req_line_speed == SPEED_AUTO_NEG) bnx2x_set_parallel_detection(params, vars->phy_flags); bnx2x_init_internal_phy(params, vars, non_ext_phy); } if (!non_ext_phy) rc |= bnx2x_ext_phy_init(params, vars); bnx2x_bits_dis(bp, NIG_REG_STATUS_INTERRUPT_PORT0 + port*4, (NIG_STATUS_XGXS0_LINK10G | NIG_STATUS_XGXS0_LINK_STATUS | NIG_STATUS_SERDES0_LINK_STATUS)); return rc; } u8 bnx2x_phy_init(struct link_params *params, struct link_vars *vars) { struct bnx2x *bp = params->bp; u32 val; DP(NETIF_MSG_LINK, "Phy Initialization started\n"); DP(NETIF_MSG_LINK, "req_speed %d, req_flowctrl %d\n", params->req_line_speed, params->req_flow_ctrl); vars->link_status = 0; vars->phy_link_up = 0; vars->link_up = 0; vars->line_speed = 0; vars->duplex = DUPLEX_FULL; vars->flow_ctrl = BNX2X_FLOW_CTRL_NONE; vars->mac_type = MAC_TYPE_NONE; if (params->switch_cfg == SWITCH_CFG_1G) vars->phy_flags = PHY_SERDES_FLAG; else vars->phy_flags = PHY_XGXS_FLAG; /* disable attentions */ bnx2x_bits_dis(bp, NIG_REG_MASK_INTERRUPT_PORT0 + params->port*4, (NIG_MASK_XGXS0_LINK_STATUS | NIG_MASK_XGXS0_LINK10G | NIG_MASK_SERDES0_LINK_STATUS | NIG_MASK_MI_INT)); bnx2x_emac_init(params, vars); if (CHIP_REV_IS_FPGA(bp)) { vars->link_up = 1; vars->line_speed = SPEED_10000; vars->duplex = DUPLEX_FULL; vars->flow_ctrl = BNX2X_FLOW_CTRL_NONE; vars->link_status = (LINK_STATUS_LINK_UP | LINK_10GTFD); /* enable on E1.5 FPGA */ if (CHIP_IS_E1H(bp)) { vars->flow_ctrl |= (BNX2X_FLOW_CTRL_TX | BNX2X_FLOW_CTRL_RX); vars->link_status |= (LINK_STATUS_TX_FLOW_CONTROL_ENABLED | LINK_STATUS_RX_FLOW_CONTROL_ENABLED); } bnx2x_emac_enable(params, vars, 0); bnx2x_pbf_update(params, vars->flow_ctrl, vars->line_speed); /* disable drain */ REG_WR(bp, NIG_REG_EGRESS_DRAIN0_MODE + params->port*4, 0); /* update shared memory */ bnx2x_update_mng(params, vars->link_status); return 0; } else if (CHIP_REV_IS_EMUL(bp)) { vars->link_up = 1; vars->line_speed = SPEED_10000; vars->duplex = DUPLEX_FULL; vars->flow_ctrl = BNX2X_FLOW_CTRL_NONE; vars->link_status = (LINK_STATUS_LINK_UP | LINK_10GTFD); bnx2x_bmac_enable(params, vars, 0); bnx2x_pbf_update(params, vars->flow_ctrl, vars->line_speed); /* Disable drain */ REG_WR(bp, NIG_REG_EGRESS_DRAIN0_MODE + params->port*4, 0); /* update shared memory */ bnx2x_update_mng(params, vars->link_status); return 0; } else if (params->loopback_mode == LOOPBACK_BMAC) { vars->link_up = 1; vars->line_speed = SPEED_10000; vars->duplex = DUPLEX_FULL; vars->flow_ctrl = BNX2X_FLOW_CTRL_NONE; vars->mac_type = MAC_TYPE_BMAC; vars->phy_flags = PHY_XGXS_FLAG; bnx2x_phy_deassert(params, vars->phy_flags); /* set bmac loopback */