aboutsummaryrefslogtreecommitdiffstats
path: root/package/kernel/ep80579-drivers/patches/150-ocracoke_island.patch
blob: ae74e0c905f3ddae3812270ca4fb55d2a0ccf5e3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
--- a/Embedded/src/GbE/iegbe_oem_phy.c
+++ b/Embedded/src/GbE/iegbe_oem_phy.c
@@ -65,6 +65,10 @@ static int32_t iegbe_oem_link_m88_setup(
 static int32_t iegbe_oem_set_phy_mode(struct iegbe_hw *hw);
 static int32_t iegbe_oem_detect_phy(struct iegbe_hw *hw);
 
+static int32_t iegbe_oem_link_bcm5481_setup(struct iegbe_hw *hw);
+static int32_t bcm5481_read_18sv (struct iegbe_hw *hw, int sv, uint16_t *data);
+static int32_t oi_phy_setup (struct iegbe_hw *hw);
+
 /**
  * iegbe_oem_setup_link
  * @hw: iegbe_hw struct containing device specific information
@@ -114,6 +118,10 @@ iegbe_oem_setup_link(struct iegbe_hw *hw
     }
 
     switch (hw->phy_id) {
+	case BCM5395S_PHY_ID:
+		return E1000_SUCCESS;
+		break;
+
         case M88E1000_I_PHY_ID:
         case M88E1141_E_PHY_ID:
             ret_val = iegbe_oem_link_m88_setup(hw);
@@ -121,6 +129,12 @@ iegbe_oem_setup_link(struct iegbe_hw *hw
                 return ret_val; 
             }
         break; 
+	case BCM5481_PHY_ID:
+		ret_val = iegbe_oem_link_bcm5481_setup(hw);
+		if(ret_val) { 
+			return ret_val; 
+		}
+		break; 
         default:
             DEBUGOUT("Invalid PHY ID\n");
             return -E1000_ERR_PHY_TYPE;
@@ -179,6 +193,51 @@ iegbe_oem_setup_link(struct iegbe_hw *hw
 #endif /* ifdef EXTERNAL_MDIO */
 }
 
+/**
+ * iegbe_oem_link_bcm5481_setup
+ * @hw: iegbe_hw struct containing device specific information
+ *
+ * Returns E1000_SUCCESS, negative E1000 error code on failure
+ *
+ * copied verbatim from iegbe_oem_link_m88_setup
+ **/
+static int32_t
+iegbe_oem_link_bcm5481_setup(struct iegbe_hw *hw)
+{
+	int32_t ret_val;
+	uint16_t phy_data;
+
+	//DEBUGFUNC(__func__);
+
+	if(!hw)
+		return -1;
+
+	/* phy_reset_disable is set in iegbe_oem_set_phy_mode */
+	if(hw->phy_reset_disable)
+		return E1000_SUCCESS;
+
+	// Enable MDIX in extended control reg.
+	ret_val = iegbe_oem_read_phy_reg_ex(hw, BCM5481_ECTRL, &phy_data);
+	if(ret_val)
+	{
+		DEBUGOUT("Unable to read BCM5481_ECTRL register\n");
+		return ret_val;
+	}
+
+	phy_data &= ~BCM5481_ECTRL_DISMDIX;
+	ret_val = iegbe_oem_write_phy_reg_ex(hw, BCM5481_ECTRL, phy_data);
+	if(ret_val)
+	{
+		DEBUGOUT("Unable to write BCM5481_ECTRL register\n");
+		return ret_val;
+	}
+
+	ret_val = oi_phy_setup (hw);
+	if (ret_val)
+		return ret_val;
+
+	return E1000_SUCCESS;
+}
 
 /**
  * iegbe_oem_link_m88_setup
@@ -340,6 +399,11 @@ iegbe_oem_force_mdi(struct iegbe_hw *hw,
      * see iegbe_phy_force_speed_duplex, which does the following for M88
      */
       switch (hw->phy_id) {
+		case BCM5395S_PHY_ID:
+		case BCM5481_PHY_ID:
+			DEBUGOUT("WARNING: An empty iegbe_oem_force_mdi() has been called!\n");
+			break;
+
           case M88E1000_I_PHY_ID:
           case M88E1141_E_PHY_ID:
               ret_val = iegbe_oem_read_phy_reg_ex(hw, 
@@ -415,6 +479,8 @@ iegbe_oem_phy_reset_dsp(struct iegbe_hw
      switch (hw->phy_id) {
          case M88E1000_I_PHY_ID:
          case M88E1141_E_PHY_ID:
+		case BCM5481_PHY_ID:
+		case BCM5395S_PHY_ID:
              DEBUGOUT("No DSP to reset on OEM PHY\n");
          break;
          default:
@@ -460,6 +526,11 @@ iegbe_oem_cleanup_after_phy_reset(struct
      * see iegbe_phy_force_speed_duplex, which does the following for M88
      */
     switch (hw->phy_id) {
+		case BCM5395S_PHY_ID:
+		case BCM5481_PHY_ID:
+            DEBUGOUT("WARNING: An empty iegbe_oem_cleanup_after_phy_reset() has been called!\n");
+        break;
+
         case M88E1000_I_PHY_ID:
         case M88E1141_E_PHY_ID:
             /*
@@ -573,6 +644,11 @@ iegbe_oem_set_phy_mode(struct iegbe_hw *
      * use iegbe_set_phy_mode as example
      */
     switch (hw->phy_id) {
+		case BCM5395S_PHY_ID:
+		case BCM5481_PHY_ID:
+             DEBUGOUT("WARNING: An empty iegbe_oem_set_phy_mode() has been called!\n");
+         break;
+
          case M88E1000_I_PHY_ID:
          case M88E1141_E_PHY_ID:
              ret_val = iegbe_read_eeprom(hw, 
@@ -641,6 +717,19 @@ iegbe_oem_detect_phy(struct iegbe_hw *hw
     }
     hw->phy_type = iegbe_phy_oem;
 
+{
+	// If MAC2 (BCM5395 switch), manually detect the phy
+	struct iegbe_adapter *adapter;
+	uint32_t device_number;
+	adapter = (struct iegbe_adapter *) hw->back;
+	device_number = PCI_SLOT(adapter->pdev->devfn);
+	if (device_number == ICP_XXXX_MAC_2) {
+		hw->phy_id = BCM5395S_PHY_ID;
+		hw->phy_revision = 0;
+		return E1000_SUCCESS;
+	}
+}
+
     ret_val = iegbe_oem_read_phy_reg_ex(hw, PHY_ID1, &phy_id_high);
     if(ret_val) {
         DEBUGOUT("Unable to read PHY register PHY_ID1\n");
@@ -690,6 +779,8 @@ iegbe_oem_get_tipg(struct iegbe_hw *hw)
     switch (hw->phy_id) {
          case M88E1000_I_PHY_ID:
          case M88E1141_E_PHY_ID:
+		case BCM5481_PHY_ID:
+		case BCM5395S_PHY_ID:
              phy_num = DEFAULT_ICP_XXXX_TIPG_IPGT;
          break;
          default:
@@ -738,6 +829,8 @@ iegbe_oem_phy_is_copper(struct iegbe_hw
     switch (hw->phy_id) {
         case M88E1000_I_PHY_ID:
         case M88E1141_E_PHY_ID:
+		case BCM5481_PHY_ID:
+		case BCM5395S_PHY_ID:
             isCopper = TRUE;
         break;
         default:
@@ -796,13 +889,13 @@ iegbe_oem_get_phy_dev_number(struct iegb
 	switch(device_number)
     {
       case ICP_XXXX_MAC_0: 
-	      hw->phy_addr = 0x00;
+	      hw->phy_addr = 0x01;
 	  break;
       case ICP_XXXX_MAC_1: 
-	      hw->phy_addr = 0x01;
+	      hw->phy_addr = 0x02;
 	  break;
       case ICP_XXXX_MAC_2: 
-	      hw->phy_addr = 0x02;
+	      hw->phy_addr = 0x00;
 	  break;
 	  default:  hw->phy_addr = 0x00;
     }
@@ -851,6 +944,12 @@ iegbe_oem_mii_ioctl(struct iegbe_adapter
     if(!adapter || !ifr) {
         return -1;
     }
+
+	// If MAC2 (BCM5395 switch) then leave now
+	if ((PCI_SLOT(adapter->pdev->devfn)) == ICP_XXXX_MAC_2) {
+			return -1;
+	}
+
     switch (data->reg_num) {
         case PHY_CTRL:
             if(mii_reg & MII_CR_POWER_DOWN) {
@@ -987,6 +1086,11 @@ void iegbe_oem_get_phy_regs(struct iegbe
      * [10] = mdix mode
      */
     switch (adapter->hw.phy_id) {
+	case BCM5395S_PHY_ID:
+	case BCM5481_PHY_ID:
+		DEBUGOUT("WARNING: An empty iegbe_oem_get_phy_regs() has been called!\n");
+	break;
+
         case M88E1000_I_PHY_ID:
         case M88E1141_E_PHY_ID:
             if(corrected_len > 0) {
@@ -1068,8 +1172,13 @@ iegbe_oem_phy_loopback(struct iegbe_adap
      * Loopback configuration is the same for each of the supported PHYs.
      */
     switch (adapter->hw.phy_id) {
+		case BCM5395S_PHY_ID:
+			DEBUGOUT("WARNING: An empty iegbe_oem_phy_loopback() has been called!\n");
+			break;
+
         case M88E1000_I_PHY_ID:
         case M88E1141_E_PHY_ID:
+		case BCM5481_PHY_ID:
 
           adapter->hw.autoneg = FALSE;
 
@@ -1182,8 +1291,14 @@ iegbe_oem_loopback_cleanup(struct iegbe_
     }
 
     switch (adapter->hw.phy_id) {
+		case BCM5395S_PHY_ID:
+		DEBUGOUT("WARNING: An empty iegbe_oem_loopback_cleanup() has been called!\n");
+		return;
+		break;
+
         case M88E1000_I_PHY_ID:
         case M88E1141_E_PHY_ID:
+		case BCM5481_PHY_ID:
         default:
             adapter->hw.autoneg = TRUE;
         
@@ -1243,6 +1358,11 @@ iegbe_oem_phy_speed_downgraded(struct ie
      */
 
     switch (hw->phy_id) {
+		case BCM5395S_PHY_ID:
+		case BCM5481_PHY_ID:
+			*isDowngraded = 0;
+			break;
+
         case M88E1000_I_PHY_ID:
         case M88E1141_E_PHY_ID:
             ret_val = iegbe_oem_read_phy_reg_ex(hw, M88E1000_PHY_SPEC_STATUS, 
@@ -1305,6 +1425,11 @@ iegbe_oem_check_polarity(struct iegbe_hw
      */
 
     switch (hw->phy_id) {
+		case BCM5395S_PHY_ID:
+		case BCM5481_PHY_ID:
+			*polarity = 0;
+			break;
+
         case M88E1000_I_PHY_ID:
         case M88E1141_E_PHY_ID:
             /* return the Polarity bit in the Status register. */
@@ -1367,6 +1492,25 @@ iegbe_oem_phy_is_full_duplex(struct iegb
      */
         
       switch (hw->phy_id) {
+		case BCM5395S_PHY_ID:
+			/* Always full duplex */
+			*isFD = 1;
+			break;
+
+		case BCM5481_PHY_ID:
+			ret_val = iegbe_read_phy_reg(hw, BCM5481_ASTAT, &phy_data);
+			if(ret_val) return ret_val;
+
+				switch (BCM5481_ASTAT_HCD(phy_data)) {
+					case BCM5481_ASTAT_1KBTFD:
+					case BCM5481_ASTAT_100BTXFD:
+						*isFD = 1;
+						break;
+					default:
+						*isFD = 0;
+				}
+			break;
+
           case M88E1000_I_PHY_ID:
           case M88E1141_E_PHY_ID:
              ret_val = iegbe_oem_read_phy_reg_ex(hw, M88E1000_PHY_SPEC_STATUS,
@@ -1423,6 +1567,25 @@ iegbe_oem_phy_is_speed_1000(struct iegbe
      */
 
     switch (hw->phy_id) {
+		case BCM5395S_PHY_ID:
+			/* Always 1000mb */
+			*is1000 = 1;
+			break;
+
+		case BCM5481_PHY_ID:
+			ret_val = iegbe_read_phy_reg(hw, BCM5481_ASTAT, &phy_data);
+			if(ret_val) return ret_val;
+
+				switch (BCM5481_ASTAT_HCD(phy_data)) {
+					case BCM5481_ASTAT_1KBTFD:
+					case BCM5481_ASTAT_1KBTHD:
+						*is1000 = 1;
+					break;
+				default:
+					*is1000 = 0;
+				}
+			break;
+
         case M88E1000_I_PHY_ID:
         case M88E1141_E_PHY_ID:
             ret_val = iegbe_oem_read_phy_reg_ex(hw, M88E1000_PHY_SPEC_STATUS, 
@@ -1478,6 +1641,25 @@ iegbe_oem_phy_is_speed_100(struct iegbe_
      * see iegbe_config_mac_to_phy
      */
     switch (hw->phy_id) {
+		case BCM5395S_PHY_ID:
+			/* Always 1000Mb, never 100mb */
+			*is100 = 0;
+			break;
+
+		case BCM5481_PHY_ID:
+			ret_val = iegbe_read_phy_reg(hw, BCM5481_ASTAT, &phy_data);
+			if(ret_val) return ret_val;
+
+			switch (BCM5481_ASTAT_HCD(phy_data)) {
+				case BCM5481_ASTAT_100BTXFD:
+				case BCM5481_ASTAT_100BTXHD:
+					*is100 = 1;
+					break;
+				default:
+					*is100 = 0;
+			}
+			break;
+
         case M88E1000_I_PHY_ID:
         case M88E1141_E_PHY_ID:
             ret_val = iegbe_oem_read_phy_reg_ex(hw, 
@@ -1535,6 +1717,11 @@ iegbe_oem_phy_get_info(struct iegbe_hw *
      * see iegbe_phy_m88_get_info
      */
     switch (hw->phy_id) {
+		case BCM5395S_PHY_ID:
+		case BCM5481_PHY_ID:
+			DEBUGOUT("WARNING: An empty iegbe_oem_phy_get_info() has been called!\n");
+			break;
+
         case M88E1000_I_PHY_ID:
         case M88E1141_E_PHY_ID:
   /* The downshift status is checked only once, after link is
@@ -1636,8 +1823,13 @@ iegbe_oem_phy_hw_reset(struct iegbe_hw *
      * the M88 used in truxton. 
      */
     switch (hw->phy_id) {
+		case BCM5395S_PHY_ID:
+			DEBUGOUT("WARNING: An empty iegbe_oem_phy_hw_reset() has been called!\n");
+			break;
+
         case M88E1000_I_PHY_ID:
         case M88E1141_E_PHY_ID:
+		case BCM5481_PHY_ID:
             ret_val = iegbe_oem_read_phy_reg_ex(hw, PHY_CTRL, &phy_data);
             if(ret_val) {
                 DEBUGOUT("Unable to read register PHY_CTRL\n");
@@ -1699,6 +1891,8 @@ iegbe_oem_phy_init_script(struct iegbe_h
     switch (hw->phy_id) {
         case M88E1000_I_PHY_ID:
         case M88E1141_E_PHY_ID:
+		case BCM5481_PHY_ID:
+		case BCM5395S_PHY_ID:
             DEBUGOUT("Nothing to do for OEM PHY Init");
         break;
         default:
@@ -1735,6 +1929,11 @@ iegbe_oem_read_phy_reg_ex(struct iegbe_h
         return -1;
     }
 
+	if (hw->phy_id == BCM5395S_PHY_ID) {
+		DEBUGOUT("WARNING: iegbe_oem_read_phy_reg_ex() has been unexpectedly called!\n");
+		return -1;
+	}
+
     /* call the GCU func that will read the phy
      * 
      * Make note that the M88 phy is what'll be used on Truxton.
@@ -1782,6 +1981,11 @@ iegbe_oem_set_trans_gasket(struct iegbe_
     }
 
      switch (hw->phy_id) {
+		case BCM5395S_PHY_ID:
+		case BCM5481_PHY_ID:
+			DEBUGOUT("WARNING: An empty iegbe_oem_set_trans_gasket() has been called!\n");
+			break;
+
          case M88E1000_I_PHY_ID:
          case M88E1141_E_PHY_ID:
          /* Gasket set correctly for Marvell Phys, so nothing to do */
@@ -1886,6 +2090,8 @@ iegbe_oem_phy_needs_reset_with_mac(struc
     switch (hw->phy_id) {
         case M88E1000_I_PHY_ID:
         case M88E1141_E_PHY_ID:
+		case BCM5481_PHY_ID:
+		case BCM5395S_PHY_ID:
             ret_val = FALSE;
         break;
         default:
@@ -1935,6 +2141,8 @@ iegbe_oem_config_dsp_after_link_change(s
     switch (hw->phy_id) {
         case M88E1000_I_PHY_ID:
         case M88E1141_E_PHY_ID:
+		case BCM5481_PHY_ID:
+		case BCM5395S_PHY_ID:
             DEBUGOUT("No DSP to configure on OEM PHY");
         break;
         default:
@@ -1978,6 +2186,12 @@ iegbe_oem_get_cable_length(struct iegbe_
     }
 
     switch (hw->phy_id) {
+		case BCM5395S_PHY_ID:
+		case BCM5481_PHY_ID:
+			*min_length = 0;
+			*max_length = iegbe_igp_cable_length_150;
+			break;
+
         case M88E1000_I_PHY_ID:
         case M88E1141_E_PHY_ID:
             ret_val = iegbe_oem_read_phy_reg_ex(hw, 
@@ -2061,6 +2275,23 @@ iegbe_oem_phy_is_link_up(struct iegbe_hw
      */
 
     switch (hw->phy_id) {
+		case BCM5395S_PHY_ID:
+			/* Link always up */
+			*isUp = TRUE;
+			return E1000_SUCCESS;
+			break;
+
+		case BCM5481_PHY_ID:
+			iegbe_oem_read_phy_reg_ex(hw, BCM5481_ESTAT, &phy_data);
+			ret_val = iegbe_oem_read_phy_reg_ex(hw, BCM5481_ESTAT, &phy_data);
+			if(ret_val)
+			{
+				DEBUGOUT("Unable to read PHY register BCM5481_ESTAT\n");
+				return ret_val;
+			}
+			statusMask = BCM5481_ESTAT_LINK;
+			break;
+
         case M88E1000_I_PHY_ID:
         case M88E1141_E_PHY_ID:
             iegbe_oem_read_phy_reg_ex(hw, M88E1000_PHY_SPEC_STATUS, &phy_data); 
@@ -2092,3 +2323,210 @@ iegbe_oem_phy_is_link_up(struct iegbe_hw
 #endif /* ifdef EXTERNAL_MDIO */
 }
 
+
+
+//-----
+// Read BCM5481 expansion register
+//
+int32_t
+bcm5481_read_ex (struct iegbe_hw *hw, uint16_t reg, uint16_t *data)
+{
+	int ret;
+	uint16_t selector;
+	uint16_t reg_data;
+
+	// Get the current value of bits 15:12
+	ret = iegbe_oem_read_phy_reg_ex (hw, 0x15, &selector);
+	if (ret)
+		return ret;
+
+	// Select the expansion register
+	selector &= 0xf000;
+	selector |= (0xf << 8) | (reg);
+	iegbe_oem_write_phy_reg_ex (hw, 0x17, selector);
+
+	// Read the expansion register
+	ret = iegbe_oem_read_phy_reg_ex (hw, 0x15, &reg_data);
+
+	// De-select the expansion registers.
+	selector &= 0xf000;
+	iegbe_oem_write_phy_reg_ex (hw, 0x17, selector);
+
+	if (ret)
+		return ret;
+
+	*data = reg_data;
+	return ret;
+}
+
+//-----
+//	Read reg 0x18 sub-register
+//
+static int32_t
+bcm5481_read_18sv (struct iegbe_hw *hw, int sv, uint16_t *data)
+{
+	int	ret;
+	uint16_t	tmp_data;
+
+	// Select reg 0x18, sv
+	tmp_data = ((sv & BCM5481_R18H_SV_MASK) << 12) | BCM5481_R18H_SV_MCTRL;
+	ret = iegbe_oem_write_phy_reg_ex (hw, BCM5481_R18H, tmp_data);
+	if(ret)
+		return ret;
+
+	// Read reg 0x18, sv
+	ret = iegbe_oem_read_phy_reg_ex (hw, BCM5481_R18H, &tmp_data);
+	if(ret)
+		return ret;
+
+	*data = tmp_data;
+	return ret;
+}
+
+//-----
+//	Read reg 0x1C sub-register
+//
+int32_t
+bcm5481_read_1csv (struct iegbe_hw *hw, int sv, uint16_t *data)
+{
+	int ret;
+	uint16_t tmp_data;
+
+	// Select reg 0x1c, sv
+	tmp_data = ((sv & BCM5481_R1CH_SV_MASK) << BCM5481_R1CH_SV_SHIFT);
+
+	ret = iegbe_oem_write_phy_reg_ex (hw, BCM5481_R1CH, tmp_data);
+	if(ret)
+		return ret;
+
+	// Read reg 0x1c, sv
+	ret = iegbe_oem_read_phy_reg_ex (hw, BCM5481_R1CH, &tmp_data);
+	if(ret)
+		return ret;
+
+	*data = tmp_data;
+	return ret;
+}
+
+//-----
+//	Read-modify-write a 0x1C register.
+//
+//	hw   - hardware access info.
+//	reg  - 0x1C register to modify.
+//	data - bits which should be set.
+//	mask - the '1' bits in this argument will be cleared in the data
+//         read from 'reg' then 'data' will be or'd in and the result
+//         will be written to 'reg'.
+
+int32_t
+bcm5481_rmw_1csv (struct iegbe_hw *hw, uint16_t reg, uint16_t data, uint16_t mask)
+{
+	int32_t		ret;
+	uint16_t	reg_data;
+
+	ret = 0;
+
+	ret = bcm5481_read_1csv (hw, reg, &reg_data);
+	if (ret)
+	{
+		DEBUGOUT("Unable to read BCM5481 1CH register\n");
+		printk (KERN_ERR "Unable to read BCM5481 1CH register [0x%x]\n", reg);
+		return ret;
+	}
+
+	reg_data &= ~mask;
+	reg_data |= (BCM5481_R1CH_WE | data);
+
+	ret = iegbe_oem_write_phy_reg_ex (hw, BCM5481_R1CH, reg_data);
+	if(ret)
+	{
+		DEBUGOUT("Unable to write BCM5481 1CH register\n");
+		printk (KERN_ERR "Unable to write BCM5481 1CH register\n");
+		return ret;
+	}
+
+	return ret;
+}
+
+int32_t
+oi_phy_setup (struct iegbe_hw *hw)
+{
+	int	ret;
+	uint16_t	pmii_data;
+	uint16_t	mctrl_data;
+	uint16_t	cacr_data;
+
+	ret = 0;
+
+	// Set low power mode via reg 0x18, sv010, bit 6
+	// Do a read-modify-write on reg 0x18, sv010 register to preserve existing bits.
+	ret = bcm5481_read_18sv (hw, BCM5481_R18H_SV_PMII, &pmii_data);
+	if (ret)
+	{
+		DEBUGOUT("Unable to read BCM5481_R18H_SV_PMII register\n");
+		printk (KERN_ERR "Unable to read BCM5481_R18H_SV_PMII register\n");
+		return ret;
+	}
+
+	// Set the LPM bit in the data just read and write back to sv010
+	// The shadow register select bits [2:0] are set by reading the sv010
+	// register.
+	pmii_data |= BCM5481_R18H_SV010_LPM;
+	ret = iegbe_oem_write_phy_reg_ex (hw, BCM5481_R18H, pmii_data);
+	if(ret)
+	{
+		DEBUGOUT("Unable to write BCM5481_R18H register\n");
+		printk (KERN_ERR "Unable to write BCM5481_R18H register\n");
+		return ret;
+	}
+
+
+	// Set the RGMII RXD to RXC skew bit in reg 0x18, sv111
+
+	if (bcm5481_read_18sv (hw, BCM5481_R18H_SV_MCTRL, &mctrl_data))
+	{
+		DEBUGOUT("Unable to read BCM5481_R18H_SV_MCTRL register\n");
+		printk (KERN_ERR "Unable to read BCM5481_R18H_SV_MCTRL register\n");
+		return ret;
+	}
+	mctrl_data |= (BCM5481_R18H_WE | BCM5481_R18H_SV111_SKEW);
+
+	ret = iegbe_oem_write_phy_reg_ex (hw, BCM5481_R18H, mctrl_data);
+	if(ret)
+	{
+		DEBUGOUT("Unable to write BCM5481_R18H register\n");
+		printk (KERN_ERR "Unable to write BCM5481_R18H register\n");
+		return ret;
+	}
+
+	// Enable RGMII transmit clock delay in reg 0x1c, sv00011
+	ret = bcm5481_read_1csv (hw, BCM5481_R1CH_CACR, &cacr_data);
+	if (ret)
+	{
+		DEBUGOUT("Unable to read BCM5481_R1CH_CACR register\n");
+		printk (KERN_ERR "Unable to read BCM5481_R1CH_CACR register\n");
+		return ret;
+	}
+
+	cacr_data |= (BCM5481_R1CH_WE | BCM5481_R1CH_CACR_TCD);
+
+	ret = iegbe_oem_write_phy_reg_ex (hw, BCM5481_R1CH, cacr_data);
+	if(ret)
+	{
+		DEBUGOUT("Unable to write BCM5481_R1CH register\n");
+		printk (KERN_ERR "Unable to write BCM5481_R1CH register\n");
+		return ret;
+	}
+
+	// Enable dual link speed indication (0x1c, sv 00010, bit 2)
+	ret = bcm5481_rmw_1csv (hw, BCM5481_R1CH_SC1, BCM5481_R1CH_SC1_LINK, BCM5481_R1CH_SC1_LINK);
+	if (ret)
+		return ret;
+
+	// Enable link and activity on ACTIVITY LED (0x1c, sv 01001, bit 4=1, bit 3=0)
+	ret = bcm5481_rmw_1csv (hw, BCM5481_R1CH_LCTRL, BCM5481_R1CH_LCTRL_ALEN, BCM5481_R1CH_LCTRL_ALEN | BCM5481_R1CH_LCTRL_AEN);
+	if (ret)
+		return ret;
+
+	return ret;
+}
--- a/Embedded/src/GbE/iegbe_oem_phy.h
+++ b/Embedded/src/GbE/iegbe_oem_phy.h
@@ -95,6 +95,8 @@ int32_t iegbe_oem_phy_is_link_up(struct
 
 #define DEFAULT_ICP_XXXX_TIPG_IPGT 8      /* Inter Packet Gap Transmit Time */
 #define ICP_XXXX_TIPG_IPGT_MASK 0x000003FFUL 
+#define BCM5481_PHY_ID		0x0143BCA0
+#define BCM5395S_PHY_ID		0x0143BCF0
 
 /* Miscellaneous defines */
 #ifdef IEGBE_10_100_ONLY
@@ -103,5 +105,65 @@ int32_t iegbe_oem_phy_is_link_up(struct
     #define ICP_XXXX_AUTONEG_ADV_DEFAULT	0x2F
 #endif
 
+/* BCM5481 specifics */
+
+#define BCM5481_ECTRL		(0x10)
+#define BCM5481_ESTAT		(0x11)
+#define BCM5481_RXERR		(0x12)
+#define BCM5481_EXPRW		(0x15)
+#define BCM5481_EXPACC		(0x17)
+#define BCM5481_ASTAT		(0x19)
+#define BCM5481_R18H		(0x18)
+#define BCM5481_R1CH		(0x1c)
+
+/* indirect register access via register 18h */
+
+#define BCM5481_R18H_SV_MASK	(7) // Mask for SV bits.
+#define BCM5481_R18H_SV_ACTRL	(0) // SV000 Aux. control
+#define BCM5481_R18H_SV_10BT	(1) // SV001 10Base-T
+#define BCM5481_R18H_SV_PMII	(2) // SV010 Power/MII control
+#define BCM5481_R18H_SV_MTEST	(4) // SV100 Misc. test
+#define BCM5481_R18H_SV_MCTRL	(7) // SV111 Misc. control
+
+#define BCM5481_R18H_SV001_POL	(1 << 13) // Polarity
+#define BCM5481_R18H_SV010_LPM	(1 << 6)
+#define BCM5481_R18H_SV111_SKEW	(1 << 8)
+#define BCM5481_R18H_WE		(1 << 15) // Write enable
+
+// 0x1c registers
+#define BCM5481_R1CH_SV_SHIFT	(10)
+#define BCM5481_R1CH_SV_MASK	(0x1f)
+#define BCM5481_R1CH_SC1	(0x02) // sv00010 Spare control 1
+#define BCM5481_R1CH_CACR	(0x03) // sv00011 Clock alignment control
+#define BCM5481_R1CH_LCTRL	(0x09) // sv01001 LED control
+#define BCM5481_R1CH_LEDS1	(0x0d) // sv01101 LED selector 1
+
+// 0x1c common
+#define BCM5481_R1CH_WE		(1 << 15) // Write enable
+
+// 0x1c, sv 00010
+#define BCM5481_R1CH_SC1_LINK	(1 << 2) // sv00010 Linkspeed
+
+// 0x1c, sv 00011
+#define BCM5481_R1CH_CACR_TCD	(1 << 9) // sv00011 RGMII tx clock delay
+
+// 0x1c, sv 01001
+#define BCM5481_R1CH_LCTRL_ALEN	(1 << 4) // Activity/Link enable on ACTIVITY LED
+#define BCM5481_R1CH_LCTRL_AEN	(1 << 3) // Activity enable on ACTIVITY LED
+
+#define BCM5481_ECTRL_DISMDIX	(1 <<14)
+
+#define BCM5481_MCTRL_AUTOMDIX	(1 <<9)
+
+#define BCM5481_ESTAT_LINK	(1 << 8)
+
+#define BCM5481_ASTAT_ANC	(1 << 15)
+#define BCM5481_ASTAT_ANHCD	(7 << 8)
+#define BCM5481_ASTAT_HCD(x)	((x >> 8) & 7)
+#define BCM5481_ASTAT_1KBTFD	(0x7)
+#define BCM5481_ASTAT_1KBTHD	(0x6)
+#define BCM5481_ASTAT_100BTXFD	(0x5)
+#define BCM5481_ASTAT_100BTXHD	(0x3)
+
 #endif /* ifndef _IEGBE_OEM_PHY_H_ */