aboutsummaryrefslogblamecommitdiff
path: root/resources/libreboot/patch/kgpe-d16/0034-cpu-amd-Add-initial-AMD-Family-15h-support.patch
blob: fa979fe6c233b406572e7495b14f22a368d2952f (plain) (tree)
1
2
3
4
5
6
7
8
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
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
4085
4086
4087
4088
4089
4090
4091
4092
4093
4094
4095
4096
4097
4098
4099
4100
4101
4102
4103
4104
4105
4106
4107
4108
4109
4110
4111
4112
4113
4114
4115
4116
4117
4118
4119
4120
4121
4122
4123
4124
4125
4126
4127
4128
4129
4130
4131
4132
4133
4134
4135
4136
4137
4138
4139
4140
4141
4142
4143
4144
4145
4146
4147
4148
4149
4150
4151
4152
4153
4154
4155
4156
4157
4158
4159
4160
4161
4162
4163
4164
4165
4166
4167
4168
4169
4170
4171
4172
4173
4174
4175
4176
4177
4178
4179
4180
4181
4182
4183
4184
4185
4186
4187
4188
4189
4190
4191
4192
4193
4194
4195
4196
4197
4198
4199
4200
4201
4202
4203
4204
4205
4206
4207
4208
4209
4210
4211
4212
4213
4214
4215
4216
4217
4218
4219
4220
4221
4222
4223
4224
4225
4226
4227
4228
4229
4230
4231
4232
4233
4234
4235
4236
4237
4238
4239
4240
4241
4242
4243
4244
4245
4246
4247
4248
4249
4250
4251
4252
4253
4254
4255
4256
4257
4258
4259
4260
4261
4262
4263
4264
4265
4266
4267
4268
4269
4270
4271
4272
4273
4274
4275
4276
4277
4278
4279
4280
4281
4282
4283
4284
4285
4286
4287
4288
4289
4290
4291
4292
4293
4294
4295
4296
4297
4298
4299
4300
4301
4302
4303
4304
4305
4306
4307
4308
4309
4310
4311
4312
4313
4314
4315
4316
4317
4318
4319
4320
4321
4322
4323
4324
4325
4326
4327
4328
4329
4330
4331
4332
4333
4334
4335
4336
4337
4338
4339
4340
4341
4342
4343
4344
4345
4346
4347
4348
4349
4350
4351
4352
4353
4354
4355
4356
4357
4358
4359
4360
4361
4362
4363
4364
4365
4366
4367
4368
4369
4370
4371
4372
4373
4374
4375
4376
4377
4378
4379
4380
4381
4382
4383
4384
4385
4386
4387
4388
4389
4390
4391
4392
4393
4394
4395
4396
4397
4398
4399
4400
4401
4402
4403
4404
4405
4406
4407
4408
4409
4410
4411
4412
4413
4414
4415
4416
4417
4418
4419
4420
4421
4422
4423
4424
4425
4426
4427
4428
4429
4430
4431
4432
4433
4434
4435
4436
4437
4438
4439
4440
4441
4442
4443
4444
4445
4446
4447
4448
4449
4450
4451
4452
4454
4455
4456
4457
4458
4459
4460
4461
4462
4463
4464
4465
4466
4467
4468
4469
4470
4471
4472
4473
4474
4475
4476
4477
4478
4479
4480
4481
4482
4483
4484
4485
4486
4487
4488
4489
4490
4491
4492
4493
4494
4495
4496
4497
4498
4499
4500
4501
4502
4503
4504
4505
4506
4507
4508
4509
4510
4511
4512
4513
4514
4515
4516
4517
4518
4519
4520
4521
4522
4523
4524
4525
4526
4527
4528
4529
4530
4531
4532
4533
4534
4535
4536
4537
4538
4539
4540
4541
4542
4543
4544
4545
4546
4547
4548
4549
4550
4551
4552
4553
4554
4555
4556
4557
4558
4559
4560
4561
4562
4563
4564
4565
4566
4567
4568
4569
4570
4571
4572
4573
4574
4575
4576
4577
4578
4579
4580
4581
4582
4583
4584
4585
4586
4587
4588
4589
4590
4591
4592
4593
4594
4595
4596
4597
4598
4599
4600
4601
4602
4603
4604
4605
4606
4607
4608
4609
4610
4611
4612
4613
4614
4615
4616
4617
4618
4619
4620
4621
4622
4623
4624
4625
4626
4627
4628
4629
4630
4631
4632
4633
4634
4635
4636
4637
4638
4639
4640
4641
4642
4643
4644
4645
4646
4647
4648
4649
4650
4651
4652
4653
4654
4655
4656
4657
4658
4659
4660
4661
4662
4663
4664
4665
4666
4667
4668
4669
4670
4671
4672
4673
4674
4675
4676
4677
4678
4679
4680
4681
4682
4683
4684
4685
4686
4687
4688
4689
4690
4691
4692
4693
4694
4695
4696
4697
4698
4699
4700
4701
4702
4703
4704
4705
4706
4707
4708
4709
4710
4711
4712
4713
4714
4715
4716
4717
4718
4719
4720
4721
4722
4723
4724
4725
4726
4727
4728
4729
4730
4731
4732
4733
4734
4735
4736
4737
4738
4739
4740
4741
4742
4743
4744
4745
4746
4747
4748
4749
4750
4751
4752
4753
4754
4755
4756
4757
4758
4759
4760
4761
4762
4763
4764
4765
4766
4767
4768
4769
4770
4771
4772
4773
4774
4775
4776
4777
4778
4779
4780
4781
4782
4783
4784
4785
4786
4787
4788
4789
4790
4791
4792
4793
4794
4795
4796
4797
4798
4799
4800
4801
4802
4803
4804
4805
4806
4807
4808
4809
4810
4811
4812
4813
4814
4815
4816
4817
4818
4819
4820
4821
4822
4823
4824
4825
4826
4827
4828
4829
4830
4831
4832
4833
4834
4835
4836
4837
4838
4839
4840
4841
4842
4843
4844
4845
4846
4847
4848
4849
4850
4851
4852
4853
4854
4855
4856
4857
4858
4859
4860
4861
4862
4863
4864
4865
4866
4867
4868
4869
4870
4871
4872
4873
4874
4875
4876
4877
4878
4879
4880
4881
4882
4883
4884
4885
4886
4887
4888
4889
4890
4891
4892
4893
4894
4895
4896
4897
4898
4899
4900
4901
4902
4903
4904
4905
4906
4907
4908
4909
4910
4911
4912
4913
4914
4915
4916
4917
4918
4919
4920
4921
4922
4923
4924
4925
4926
4927
4928
4929
4930
4931
4932
4933
4934
4935
4936
4937
4938
4939
4940
4941
4942
4943
4944
4945
4946
4947
4948
4949
4950
4951
4952
4953
4954
4955
4956
4957
4958
4959
4960
4961
4962
4963
4964
4965
4966
4967
4968
4969
4970
4971
4972
4973
4974
4975
4976
4977
4978
4979
4980
4981
4982
4983
4984
4985
4986
4987
4988
4989
4990
4991
4992
4993
4994
4995
4996
4997
4998
4999
5000
5001
5002
5003
5004
5005
5006
5007
5008
5009
5010
5011
5012
5013
5014
5015
5016
5017
5018
5019
5020
5021
5022
5023
5024
5025
5026
5027
5028
5029
5030
5031
5032
5033
5034
5035
5036
5037
5038
5039
5040
5041
5042
5043
5044
5045
5046
5047
5048
5049
5050
5051
5052
5053
5054
5055
5056
5057
5058
5059
5060
5061
5062
5063
5064
5065
5066
5067
5068
5069
5070
5071
5072
5073
5074
5075
5076
5077
5078
5079
5080
5081
5082
5083
5084
5085
5086
5087
5088
5089
5090
5091
5092
5093
5094
5095
5096
5097
5098
5099
5100
5101
5102
5103
5104
5105
5106
5107
5108
5109
5110
5111
5112
5113
5114
5115
5116
5117
5118
5119
5120
5121
5122
5123
5124
5125
5126
5127
5128
5129
5130
5131
5132
5133
5134
5135
5136
5137
5138
5139
5140
5141
5142
5143
5144
5145
5146
5147
5148
5149
5150
5151
5152
5153
5154
5155
5156
5157
5158
5159
5160
5161
5162
5163
5164
5165
5166
5167
5168
5169
5170
5171
5172
5173
8625
8626
8627
8628
8629
8630
8631
8632
8633
8634
8635
8636
8637
8638
8639
8640
8641
8642
8643
8644
8645
8646
8647
8648
8649
8650
8651
8652
8653
8654
8655
8656
8657
8658
8659
8660
8661
8662
8663
8664
8665
8666
8667
8668
8669
8670
8671
8672
8673
8674
8675
8676
8677
8678
8679
8680
8681
8682
8683
8684
8685
8686
8687
8688
8689
8690
8691
8692
8693
8694
8695
8696
8697
8698
8699
8700
8701
8702
8703
8704
8705
8706
8707
8708
8709
8710
8711
8712
8713
8714
8715
8716
8717
8718
8719
8720
8721
8722
8723
8724
8725
8726
8727
8728
8729
8730
8731
8732
8733
8734
8735
8736
8737
8738
8739
8740
8741
8742
8743
8744
8745
8746
8747
8748
8749
8750
8751
8752
8753
8754
8755
8756
8757
8758
8759
8760
8761
8762
8763
8764
8765
8766
8767
8768
8769
8770
8771
8772
8773
8774
8775
8776
8777
8778
8779
8780
8781
8782
8783
8784
8785
8786
8787
8788
8789
8790
8791
8792
8793
8794
8795
8796
8797
8798
8799
8800
8801
8802
8803
8804
8805
8806
8807
8808
8809
8810
8811
8812
8813
8814
8815
8816
8817
8818
8819
8820
8821
8822
8823
8824
8825
8826
8827
8828
8829
8830
8831
8832
8833
8834
8835
8836
8837
8838
8839
8840
8841
8842
8843
8844
8845
8846
8847
8848
8849
8850
8851
8852
8853
8854
8855
8856
8857
8858
8859
8860
8861
8862
8863
8864
8865
8866
8867
8868
8869
8870
8871
8872
8873
8874
8875
8876
8877
8878
8879
8880
8881
8882
8883
8884
8885
8886
8887
8888
8889
8890
8891
8892
8893
8894
8895
8896
8897
8898
8899
8900
8901
8902
8903
8904
8905
8906
8907
8908
8909
8910
8911
8912
8913
8914
8915
8916
8917
8918
8919
8920
8921
8922
8923
8924
8925
8926
8927
8928
8929
8930
8931
8932
8933
8934
8935
8936
8937
8938
8939
8940
8941
8942
8943
8944
8945
8946
8947
8948
8949
8950
8951
8952
8953
8954
8955
8956
8957
8958
8959
8960
8961
8962
8963
8964
8965
8966
8967
8968
8969
8970
8971
8972
8973
8974
8975
8976
8977
8978
8979
8980
8981
8982
8983
8984
8985
8986
8987
8988
8989
8990
8991
8992
8993
8994
8995
8996
8997
8998
8999
9000
9001
9002
9003
9004
9005
9006
9007
9008
9009
9010
9011
9012
9013
9014
9015
9016
9017
9018
9019
9020
9021
9022
9023
9024
9025
9026
9027
9028
9029
9030
9031
9032
9033
9034
9035
9036
9037
9038
9039
9040
9041
9042
9043
9045
9046
9047
9048
9049
9050
9051
9052
9053
9054
9055
9056
9057
9058
9059
9060
9061
9062
9063
9064
9065
9066
9067
9068
9069
9070
9071
9072
9073
9074
9075
9076
9077
9078
9079
9080
9081
9082
9083
9084
9085
9086
9087
9088
9089
9090
9091
9092
9093
9094
9095
9096
9097
9098
9099
9100
9101
9102
9103
9104
9105
9106
9107
9108
9109
9110
9111
9112
9113
9114
9115
9116
9117
9118
9119
9120
9121
9122
9123
9124
9125
9126
9127
9128
9129
9130
9131
9132
9133
9134
9135
9136
9137
9138
9139
9140
9141
9142
9143
9144
9145
9146
9147
9148
9149
9150
9151
9152
9153
9154
9155
9156
9157
9158
9159
9160
9161
9162
9163
9164
9165
9166
9167
9168
9169
9170
9171
9172
9173
9174
9175
9176
9177
9178
9179
9180
9181
9182
9183
9184
9185
9186
9187
9188
9189
9190
9191
9192
9193
9194
9195
9196
9197
9198
9199
9200
9201
9202
9203
9204
9205
9206
9207
9208
9209
9210
9211
9212
9213
9214
9215
9216
9217
9218
9219
9220
9221
9222
9223
9224
9225
9226
9227
9228
9229
9230
9231
9232
9233
9234
9235
9236
9237
9238
9239
9240
9241
9242
9243
9244
9245
9246
9247
9248
9249
9250
9251
9252
9253
9254
9255
9256
9257
9258
9259
9260
9261
9262
9263
9264
9265
9266
9267
9268
9269
9270
9271
9272
9273
9274
9275
9276
9277
9278
9279
9280
9281
9282
9283
9284
9285
9286
9287
9288
9289
9290
9291
9292
9293
9294
9295
9296
9297
9298
9299
9300
9301
9302
9303
9304
9305
9306
9307
9308
9309
9310
9311
9312
9313
9314
9315
9316
9317
9318
9319
9320
9321
9322
9323
9324
9325
9326
9327
9328
9329
9330
9331
9332
9333
9334
9335
9336
9337
9338
9339
9340
9341
9342
9343
9344
9345
9346
9347
9348
9349
9350
9351
9352
9353
9354
9355
9356
9357
9358
9359
9360
9361
9362
9363
9364
9365
9366
9367
9368
9369
9370
9371
9372
9373
9374
9375
9376
9377
9378
9379
9380
9381
9382
9383
9384
9385
9386
9387
9388
9389
9390
9391
9392
9393
9394
9395
9396
9397
9398
9399
9400
9401
9402
9403
9404
9405
9406
9407
9408
9409
9410
9411
9412
9413
9414
9415
9416
9417
9418
9419
9420
9421
9422
9423
9424
9425
9426
9427
9428
9429
9430
9431
9432
9433
9434
9435
9436
9437
9438
9439
9440
9441
9442
9443
9444
9445
9446
9447
9448
9449
9450
9451
9452
9453
9454
9455
9456
9457
9458
9459
9460
9461
9462
9463
9464
9465
9466
9467
9468
9469
9470
9471
9472
9473
9474
9475
9476
9477
9478
9479
9480
9481
9482
9483
9484
9485
9486
9487
9488
9489
9490
9491
9492
9493
9494
9495
9496
9497
9498
9499
9500
9501
9502
9503
9504
9505
9506
9507
9508
9509
9510
9511
9512
9513
9514
9515
9516
9517
9518
9519
9520
9521
9522
9523
9524
9525
9526
9527
9528
9529
9530
9531
9532
9533
9534
9535
9536
9537
9538
9539
9540
9541
9542
9543
9544
9545
9546
9547
9548
9549
9550
9551
9552
9553
9554
9555
9556
9557
9558
9559
9560
9561
9562
9563
9564
9565
9566
9567
9568
9569
9570
9571
9572
9573
9574
9575
9576
9577
9578
9579
9580
9581
9582
9583
9584
9585
9586
9587
9588
9589
9590
9591
9592
9593
9594
9595
9596
9597
9598
9599
9600
9601
9602
9603
9604
9605
9606
9607
9608
9609
9610
9611
9612
9613
9614
9615
9616
9617
9618
9619
9620
9621
9622
9623
9624
9625
9626
9627
9628
9629
9630
9631
9632
9633
9634
9635
9636
9637
9638
9639
9640
9641
9642
9643
9644
9645
9646
9647
9648
9649
9650
9651
9652
9653
9654
9655
9656
9657
9658
9659
9660
9661
9662
9663
9664
9665
9666
9667
9668
9669
9670
9671
9672
9673
9674
9675
9676
9677
9678
9679
9680
9681
9682
9683
9684
9685
9686
9687
9688
9689
9690
9691
9692
9693
9694
9695
9696
9697
9698
9699
9700
9701
9702
9703
9704
9705
9706
9707
9708
9709
9710
9711
9712
9713
9714
9715
9716
9717
9718
9719
9720
9721
9722
9723
9724
9725
9726
9727
9728
9729
9730
9731
9732
9733
9734
9735
9736
9737
9738
9739
9740
9741
9742
9743
9744
9745
9746
9747
9748
9749
9750
9751
9752
9753
9754
9755
9756
9757
9758
9759
9760
9761
9762
9763
9764
9765
9766
9767
9768
9769
9770
9771
9772
9773
9774
9775
9776
9777
9778
9779
9780
9781
9782
9783
9784
9785
9786
9787
9788
9789
9790
9791
9792
9793
9794
9795
9796
9797
9798
9799
9800
9801
9802
9803
9804
9805
9806
9807
9808
9809
9810
9811
9812
9813
9814
9815
9816
9817
9818
9819
9820
9821
9822
9823
9824
9825
9826
9827
9828
9829
9830
9831
9832
9833
9834
9835
9836
9837
9838
9839
9840
9841
9842
9843
9844
9845
9846
9847
9848
9849
9850
9851
10376
10377
10378
10379
10380
10381
10382
10383
10384
10385
10386
10387
10388
10389
10390
10391
10392
10393
10394
10395
10396
10397
10398
10399
10400
10401
10402
10403
10404
10405
10406
10407
10408
10409
10410
10411
10412
10413
10414
10415
10416
10417
10418
10419
10420
10421
10422
10423
10424
10425
10426
10427
10428
10429
10430
10431
10432
10433
10434
10435
10436
10437
10438
10439
10440
10441
10442
10443
10444
10445
10446
10447
10448
10449
10450
10451
10452
10453
10454
10455
10456
10457
10458
10459
10460
10461
10462
10463
10464
10465
10466
10467
10468
10469
10470
10471
10472
10473
10474
10475
10476
10477
10478
10479
10480
10481
10482
10483
10484
10485
10486
10487
10488
10489
10490
10491
10492
10493
10494
10495
10496
10497
10498
10499
10500
10501
10502
10503
10504
10505
10506
10507
10508
10509
10510
10511
10512
10513
10514
10515
10516
10517
10518
10519
10520
10521
10522
10523
10524
10525
10526
10527
10528
10529
10530
10531
10532
10533
10534
10535
10536
10537
10538
10539
10540
10541
10542
10543
10544
10545
10546
10547
10548
10549
10550
10551
10552
10553
10554
10555
10556
10557
10558
10559
10560
10561
10562
10563
10564
10565
10566
10567
10568
10569
10570
10571
10572
10573
10574
10575
10576
10577
10578
10579
10580
10581
10582
10583
10584
10585
10586
10587
10588
10589
10590
10591
10592
10593
10594
10595
10596
10597
10598
10599
10600
10601
10602
10603
10604
10605
10606
10607
10608
10609
10610
10611
10612
10613
10614
10615
10616
10617
10618
10619
10620
10621
10622
10623
10624
10625
10626
10627
10628
10629
10630
10631
10632
10633
10634
10635
10636
10637
10638
10639
10640
10641
10642
10643
10644
10645
10646
10647
10648
10649
10650
10651
10652
10653
10654
10655
10656
10657
10658
10659
10660
10661
10662
10663
10664
10665
10666
10667
10668
10669
10670
10671
10672
10673
10674
10675
10676
10677
10678
10679
10680
10681
10682
10683
10684
10685
10686
10687
10688
10689
10690
10691
10692
10693
10694
10695
10696
10697
10698
10699
10700
10701
10702
10703
10704
10705
10706
10707
10708
10709
10710
10711
10712
10713
10714
10715
10716
10717
10718
10719
10720
10721
10722
10723
10724
10725
10726
10727
10728
10729
10730
10731
10732
10733
10734
10735
10736
10737
10738
10739
10740
10741
10742
10743
10744
10745
10746
10747
10748
10749
10750
10751
10752
10753
10754
10755
10756
10757
10758
10759
10760
10761
10762
10763
10764
10765
10766
10767
10768
10769
10770
10771
10772
10773
10774
10775
10776
10777
10778
10779
10780
10781
10782
10783
10784
10785
10786
10787
10788
10789
10790
10791
10792
10793
10794
10795
10796
10797
10798
10799
10800
10801
10802
10803
10804
10805
10806
10807
10808
10809
10810
10811
10812
10813
10814
10815
10816
10817
10818
10819
10820
10821
10822
10823
10824
10825
10826
10827
10828
10829
10830
10831
10832
10833
10834
10835
10836
10837
10838
10839
10840
10841
10842
10843
10844
10845
10846
10847
10848
10849
10850
10851
10852
10853
10854
10855
10856
10857
10858
10859
10860
10861
10862
10863
10864
10865
10866
10867
10868
10869
10870
10871
10872
10873
10874
10875
10876
10877
10878
10879
10880
10881
10882
10883
10884
10885
10886
10887
10888
10889
10890
10891
10892
10893
10894
10895
10896
10897
10898
10899
10900
10901
10902
10903
10904
10905
10906
10907
10908
10909
10910
10911
10912
10913
10914
10915
10916
10917
10918
10919
10920
10921
10922
10923
10924
10925
10926
10927
10928
10929
10930
10931
10932
10933
10934
10935
10936
10937
10938
10939
10940
10941
10942
10943
10944
10945
10946
10947
10948
10949
10950
10951
10952
10953
10954
10955
10956
10957
10958
10959
10960
10961
10962
10963
10964
10965
10966
10967
10968
10969
10970
10971
10972
10973
10974
10975
10976
10977
10978
10979
10980
10981
10982
10983
10984
10985
10986
10987
10988
10989
10990
10991
10992
10993
10994
10995
10996
10997
10998
10999
11000
11001
11002
11003
11004
11005
11006
11007
11008
11009
11010
11011
11012
11013
11014
11015
11016
11017
11018
11019
11020
11021
11022
11023
11024
11025
11026
11027
11028
11029
11030
11031
11032
11033
11034
11035
11036
11037
11038
11039
11040
11041
11042
11043
11044
11045
11046
11047
11048
11049
11050
11051
11052
11053
11054
11055
11056
11057
11058
11059
11060
11061
11062
11063
11064
11065
11066
11067
11068
11069
11070
11071
11072
11073
11074
11075
11076
11077
11078
11079
11080
11081
11082
11083
11084
11085
11086
11087
11088
11089
11090
11091
11092
11093
11094
11095
11096
11097
11098
11099
11100
11101
11102
11103
11104
11105
11106
11107
11108
11109
11110
11111
11112
11113
11114
11115
11116
11117
11118
11119
11120
11121
11122
11123
11124
11125
11126
11127
11128
11129
11130
11131
11132
11133
11134
11135
11136
11137
11138
11139
11140
11141
11142
11143
11144
11145
11146
11147
11148
11149
11150
11151
11152
11153
11154
11155
11156
11157
11158
11159
11160
11161
11162
11163
11164
11165
11166
11167
11168
11169
11170
11171
11172
11173
11174
11175
11176
11177
11178
11179
11180
11181
11182
11183
11184
11185
11186
11187
11188
11189
11190
11191
11192
11193
11194
11195
11196
11197
11198
11199
11200
11201
11202
11203
11204
11205
11206
11207
11208
11209
11210
11211
11212
11213
11214
11215
11216
11217
11218
11219
11220
11221
11222
11223
11224
11225
11226
11227
11228
11229
11230
11231
11232
11233
11234
11235
11236
11237
11238
11239
11240
11241
11242
11243
11244
11245
11246
11247
11248
11249
11250
11251
11252
11253
11254
11255
11256
11257
11258
11259
11260
11261
11262
11263
11264
11265
11266
11267
11268
11269
11270
11271
11272
11273
11274
11275
11276
11277
11278
11279
11280
11281
11282
11283
11284
11285
11286
11287
11288
11289
11290
11291
11292
11293
11294
11295
11296
11297
11298
11299
11300
11301
11302
11303
11304
11305
11306
11307
11308
11309
11310
11311
11312
11313
11314
11315
11316
11317
11318
11319
11320
11321
11322
11323
11324
11325
11326
11327
11328
11329
11330
11331
11332
11333
11334
11335
11336
11337
11338
11339
11340
11341
11342
11343
11344
11345
11346
11347
11348
11349
11350
11351
11352
11353
11354
11355
11356
11357
11358
11359
11360
11361
11362
11363
11364
11365
11366
11367
11368
11369
11370
11371
11372
11373
11374
11375
11376
11377
11378
11379
11380
11381
11382
11383
11384
11385
11386
11387
11388
11389
11390
11391
11392
11393
11394
11395
11396
11397
11398
11399
11400
11401
11402
11403
11404
11405
11406
11407
11408
11409
11410
11411
11412
11413
11414
11415
11416
11417
11418
11419
11420
11421
11422
11423
11424
11425
11426
11427
11428
11429
11430
11431
11432
11433
11434
11435
11436
11437
11438
11439
11440
11441
11442
11443
11444
11445
11446
11447
11448
11449
11450
11451
11452
11453
11454
11455
11456
11457
11458
11459
11460
11461
11462
11463
11464
11465
11466
11467
11468
11469
11470
11471
11472
11473
11474
11475
11476
11477
11478
11479
11480
11481
11482
11483
11484
11485
11486
11487
11488
11489
11490
11491
11492
11493
11494
11495
11496
11497
11498
11499
11500
11501
11502
11503
11504
11505
11506
11507
11508
11509
11510
11511
11512
11513
11514
11515
11516
11517
11518
11519
11520
11521
11522
11523
11524
11525
11526
11527
11528
11529
11530
11531
11532
11533
11534
11535
11537
11538
11539
11540
11541
11542
11543
11544
11545
11546
11547
11548
11549
11550
11551
11552
11553
11554
11555
11556
11557
11558
11559
11560
11561
11562
11563
11564
11565
11566
11567
11568
11569
11570
11571
11572
11573
11574
11575
11576
11577
11578
11579
11580
11581
11582
11583
11584
11585
11586
11587
11588
11589
11590
11591
11592
11593
11594
11595
11596
11597
11598
11599
11600
11601
11602
11603
11604
11605
11606
11607
11608
11609
11610
11611
11612
11613
11614
11615
11616
11617
11618
11619
11620
11621
11622
11623
11624
11625
11626
11627
11628
11629
11630
11631
11632
11633
11634
11635
11636
11637
11638
11639
11640
11641
11642
11643
11644
11645
11646
11647
11648
11649
11650
11651
11652
11653
11654
11655
11656
11657
11658
11659
11660
11661
11662
11663
11664
11665
11666
11667
11668
11669
11670
11671
11672
11673
11674
11675
11676
11677
11678
11679
11680
11681
11682
11683
11684
11685
11686
11687
11688
11689
11690
11691
11692
11693
11694
11695
11696
11697
11698
11699
11700
11701
11702
11703
11704
11705
11706
11707
11708
11709
11710
11711
11712
11713
11714
11715
11716
11717
11718
11719
11720
11721
11722
11723
11724
11725
11726
11727
11728
11729
11730
11731
11732
11733
11734
11735
11736
11737
11738
11739
11740
11741
11742
11743
11744
11745
11746
11747
11748
11749
11750
11751
11752
11753
11754
11755
11756
11757
11758
11759
11760
11761
11762
11763
11764
11765
11766
11767
11768
11769
11770
11771
11772
11773
11774
11775
11776
11777
11778
11779
11780
11781
11782
11783
11784
11785
11786
11787
11788
11789
11790
11791
11792
11793
11794
11795
11796
11797
11798
11799
11800
11801
11802
11803
11804
11805
11806
11807
11808
11809
11810
11811
11812
11813
11814
11815
11816
11817
11818
11819
11820
11821
11822
11823
11824
11825
11826
11827
11828
11829
11830
11831
11832
11833
11834
11835
11836
11837
11838
11839
11840
11841
11842
11843
11844
11845
11846
11847
11848
11849
11850
11851
11852
11853
11854
11855
11856
11857
11858
11859
11860
11861
11862
11863
11864
11865
11866
11867
11868
11869
11870
11871
11872
11873
11874
11875
11876
11877
11878
11879
11880
11881
11882
11883
11884
11885
11886
11887
11888
11889
11890
11891
11892
11893
11894
11895
11896
11897
11898
11899
11900
11901
11902
11903
11904
11905
11906
11907
11908
11909
11910
11911
11912
11913
11914
11915
11916
11917
11918
11919
11920
11921
11922
11923
11924
11925
11926
11927
11928
11929
11930
11931
11932
11933
11934
11935
11936
11937
11938
11939
11940
11941
11942
11943
11944
11945
11946
11947
11948
11949
11950
11951
11952
11953
11954
11955
11956
11957
11958
11959
11960
11961
11962
11963
11964
11965
11966
11967
11968
11969
11970
11971
11972
11973
11974
11975
11976
11977
11978
11979
11980
11981
11982
11983
11984
11985
11986
11987
11988
11989
11990
11991
11992
11993
11994
11995
11996
11997
11998
11999
12000
12001
12002
12003
12004
12005
12006
12007
12008
12009
12010
12011
12012
12013
12014
12015
12016
12017
12018
12019
12020
12021
12022
12023
12024
12025
12026
12027
12028
12029
12030
12031
12032
12033
12034
12035
12036
12037
12038
12039
12040
12041
12042
12043
12044
12045
12046
12047
12048
12049
12050
12051
12052
12053
12054
12055
12056
12057
12058
12059
12060
12061
12062
12063
12064
12065
12066
12067
12068
12069
12070
12071
12072
12073
12074
12075
12076
12077
12078
12079
12080
12081
12082
12083
12084
12085
12086
12087
12088
12089
12090
12091
12092
12093
12094
12095
12096
12097
12098
12099
12100
12101
12102
12103
12104
12105
12106
12107
12108
12109
12110
12111
12112
12113
12114
12115
12116
12117
12118
12119
12120
12121
12122
12123
12124
12125
12126
12127
12128
12129
12130
12131
12132
12133
12134
12135
12136
12137
12138
12139
12140
12141
12142
12143
12144
12145
12146
12147
12148
12149
12150
12151
12152
12153
12154
12155
12156
12157
12158
12159
12160
12161
12162
12163
12164
12165
12166
12167
12178
12179
12180
12181
12182
12183
12184
12185
12186
12187
12188
12189
12190
12191
12192
12193
12194
12195
12196
12197
12198
12199
12200
12201
12202
12203
12204
12205
12206
12207
12208
12209
12210
12211
12212
12213
12214
12215
12216
12217
12218
12219
12220
12221
12222
12223
12224
12225
12226
12227
12228
12229
12230
12231
12232
12233
12234
12235
12236
12237
12238
12239
12240
12241
12242
12243
12244
12245
12246
12247
12248
12249
12250
12251
12252
12253
12254
12255
12256
12257
12258
12259
12260
12261
12262
12263
12264
12265
12266
12267
12268
12269
12270
12271
12272
12273
12274
12275
12276
12277
12278
12279
12280
12281
12282
12283
12284
12285
12286
12287
12288
12289
12290
12291
12292
12293
12294
12295
12296
12297
12298
12299
12300
12301
12302
12303
12304
12305
12306
12307
12308
12309
12310
12311
12312
12313
12314
12315
12316
12317
12318
12319
12320
12321
12322
12323
12324
12325
12326
12327
12328
12329
12330
12331
12332
12333
12334
12335
12336
12337
12338
12339
12340
12341
12342
12343
12344
12345
12346
12347
12348
12349
12350
12351
12352
12353
12354
12355
12356
12357
12358
12359
12360
12361
12362
12363
12364
12365
12366
12367
12368
12369
12370
12371
12372
12373
12374
12375
12376
12377
12378
12379
12380
12381
12382
12383
12384
12385
12386
12387
12388
12389
12390
12391
12392
12393
12394
12395
12396
12397
12398
12399
12400
12401
12402
12403
12404
12405
12406
12407
12408
12409
12410
12411
12412
12413
12414
12415
12416
12417
12418
12419
12420
12421
12422
12423
12424
12425
12426
12427
12428
12429
12430
12431
12432
12433
12434
12435
12436
12437
12438
12439
12440
12441
12442
12443
12444
12445
12446
12447
12448
12449
12450
12451
12452
12453
12454
12455
12456
12457
12458
12459
12460
12461
12462
12463
12464
12465
12466
12467
12468
12469
12470
12471
12472
12473
12474
12475
12476
12477
12478
12479
12480
12481
12482
12483
12484
12485
12486
12487
12488
12489
12490
12491
12492
12493
12494
12495
12496
12497
12498
12499
12500
12501
12502
12503
12504
12505
12506
12507
12508
12509
12510
12511
12512
12513
12514
12515
12516
12517
12518
12519
12520
12521
12522
12523
12524
12525
12526
12527
12528
12529
12530
12531
12532
12533
12534
12535
12536
12537
12538
12539
12540
12541
12542
12543
12544
12545
12546
12547
12548
12549
12550
12551
12552
12553
12554
12555
12556
12557
12558
12559
12560
12561
12562
12563
12564
12565
12566
12567
12568
12569
12570
12571
12572
12573
12574
12575
12576
12577
12578
12579
12580
12581
12582
12583
12584
12585
12586
12587
12588
12589
12590
12818
12819
12820
12821
12822
12823
12824
12825
12826
12827
12828
12829
12830
12831
12832
12833
12834
12835
12836
12837
12838
12839
12840
12841
12842
12843
12844
12845
12846
12847
12848
12849
12850
12851
12852
12853
12854
12855
12856
12857
12858
12859
12860
12861
12862
12863
12864
12865
12866
12867
12868
12869
12870
12871
12872
12873
12874
12875
12876
12877
12878
12879
12880
12881
12882
12883
12884
12885
12886
12887
12888
12889
12890
12891
12892
12893
12894
12895
12896
12897
12898
12899
12900
12901
12902
12903
12904
12905
12906
12907
12908
12909
12910
12911
12912
12913
12914
12915
12916
12917
12918
12919
12920
12921
12922
12923
12924
12925
12926
12927
12928
12929
12930
12931
12932
12933
12934
12935
12936
12937
12938
12939
12940
12941
12942
12943
12944
12945
12946
12947
12948
12949
12950
12951
12952
12953
12954
12955
12956
12957
12958
12959
12960
12961
12962
12963
12964
12965
12966
12967
12968
12969
12970
12971
12972
12973
12974
12975
12976
12977
12978
12979
12980
12981
12982
12983
12984
12985
12986
12987
12988
12989
12990
12991
12992
12993
12994
12995
12996
12997
12998
12999
13000
13001
13002
13003
13004
13005
13006
13007
13008
13009
13010
13011
13012
13013
13014
13015
13016
13017
13018
13019
13020
13021
13022
13023
13024
13025
13026
13027
13028
13029
13030
13031
13032
13033
13034
13035
13036
13037
13038
13039
13040
13041
13042
13043
13044
13045
13046
13047
13048
13049
13050
13051
13052
13053
13054
13055
13056
13057
13058
13059
13060
13061
13062
13063
13064
13065
13066
13067
13068
13069
13070
13071
13072
13073
13074
13075
13076
13077
13078
13079
13080
13081
13082
13083
13084
13085
13086
13087
13088
13089
13090
13091
13092
13093
13094
13095
13096
13097
13098
13099
13100
13101
13102
13103
13104
13105
13106
13107
13108
13109
13110
13111
13112
13113
13114
13115
13116
13117
13118
13119
13120
13121
13122
13123
13124
13125
13126
13127
13128
13129
13130
13131
13132
13133
13134
13135
13136
13137
13138
13139
13140
13141
13142
13143
13144
13145
13146
13147
13148
13149
13150
13151
13152
13153
13154
13155
13156
13157
13158
13159
13160
13161
13162
13163
13164
13165
13166
13167
13168
13169
13170
13171
13172
13173
13174
13175
13176
13177
13178
13179
13180
13181
13182
13183
13184
13185
13186
13187
13188
13189
13190
13191
13192
13193
13194
13195
13196
13197
13198
13199
13200
13201
13202
13203
13204
13205
13206
13207
13208
13209
13210
13211
13212
13213
13214
13215
13216
13217
13218
13219
13220
13221
13222
13223
13224
13225
13226
13227
13228
13229
13230
13231
13232
13233
13234
13235
13236
13237
13238
13239
13240
13241
13242
13243
13244
13245
13246
13247
13248
13249
13250
13251
13252
13253
13254
13255
13256
13257
13258
13259
13260
13261
13262
13263
13264
13265
13266
13267
13268
13269
13270
13271
13272
13273
13274
13275
13276
13277
13278
13279
13280
13281
13282
13283
13284
13285
13286
13287
13288
13289
13290
13291
13292
13293
13294
13295
13296
13297
13298
13299
13300
13301
13302
13303
13304
13305
13306
13307
13308
13309
13310
13311
13312
13313
13314
13315
13316
13317
13318
13319
13320
13321
13322
13323
13324
13325
13326
13327
13328
13329
13330
13331
13332
13333
13334
13335
13336
13337
13338
13339
13340
13341
13342
13343
13344
13345
13346
13347
13348
13349
13350
13351
13352
13353
13354
13355
13356
13357
13358
13359
13360
13361
13362
13363
13364
13365
13366
13367
13368
13369
13370
13371
13372
13373
13374
13375
13376
13377
13378
13379
13380
13381
13382
13383
13384
13385
13386
13387
13388
13389
13390
13391
13392
13393
13394
13395
13396
13397
13398
13399
13400
13401
13402
13403
13404
13405
13406
13407
13408
13409
13410
13411
13412
13413
13414
13415
13416
13417
13418
13419
13420
13421
13422
13423
13424
13425
13426
13427
13428
13429
13430
13431
13432
13433
13434
13435
13436
13437
13438
13439
13440
13441
13442
13443
13444
13445
13446
13447
13448
13449
13450
13451
13452
13453
13454
13455
13456
13457
13458
13459
13460
13461
13462
13463
13464
13465
13466
13467
13468
13469
13470
13471
13472
13473
13474
13475
13476
13477
13478
13479
13480
13481
13482
13483
13484
13485
13486
13487
13488
13489
13490
13491
13492
13493
13494
13495
13496
13497
13498
13499
13500
13501
13502
13503
13504
13505
13506
13507
13508
13509
13510
13511
13512
13513
13514
13515
13516
13517
13518
13519
13520
13521
13522
13523
13524
13525
13526
13527
13528
13529
13530
13531
13532
13533
13534
13535
13536
13537
13538
13539
13540
13541
13542
13543
13544
13545
13546
13547
13548
13549
13550
13551
13552
13553
13554
13555
13556
13557
13558
13559
13560
13561
13562
13563
13564
13565
13566
13567
13568
13569
13570
13571
13572
13573
13574
13575
13576
13577
13578
13579
13580
13581
13582
13583
13584
13585
13586
13587
13588
13589
13590
13591
13592
13593
13594
13595
13596
13597
13598
13599
13600
13601
13602
13603
13604
13605
13606
13607
13608
13609
13610
13611
13612
13613
13614
13615
13616
13617
13618
13619
13620
13621
13622
13623
13624
13625
13626
13627
13628
13629
13630
13631
13632
13633
13634
13635
13636
13637
13638
13639
13640
13641
13642
13643
13644
13645
13646
13647
13648
13649
13650
13651
13652
13653
13654
13655
13656
13657
13658
13659
13660
13661
13662
13663
13664
13665
13666
13667
13668
13669
13670
13671
13672
13673
13674
13675
13676
13833
13834
13835
13836
13837
13838
13839
13840
13841
13842
13843
13844
13845
13846
13847
13848
13849
13850
13851
13852
13853
13854
13855
13856
13857
13858
13859
13860
13861
13862
13863
13864
13865
13866
13867
13868
13869
13870
13871
13872
13873
13874
13875
13876
13877
13878
13879
13880
13881
13882
13883
13884
13885
13886
13887
13888
13889
13890
13891
13892
13893
13894
13895
13896
13897
13898
13899
13900
13901
13902
13903
13904
13905
13906
13907
13908
13909
13910
13911
13912
13913
13914
13915
13916
13917
13918
13919
13920
13921
13922
13923
13924
13925
13926
13927
13928
13929
13930
13931
13932
13933
13934
13935
13936
13937
13938
13939
13940
13941
13942
13943
13944
13945
13946
13947
13948
13949
13950
13951
13952
13953
13954
13955
13956
13957
13958
13959
13960
13961
13962
13963
13964
13965
13966
13967
13968
13969
13970
13971
13972
13973
13974
13975
13976
13977
13978
13979
13980
13981
13982
13983
13984
13985
13986
13987
13988
13989
13990
13991
13992
13993
13994
13995
13996
13997
13998
13999
14000
14001
14002
14003
14004
14005
14006
14007
14008
14009
14010
14011
14012
14013
14014
14015
14016
14017
14018
14019
14020
14021
14022
14023
14024
14025
14026
14027
14028
14029
14030
14031
14032
14033
14034
14035
14036
14037
14038
14039
14040
14041
14042
14043
14044
14045
14046
14047
14048
14049
14050
14051
14052
14053
14054
14055
14056
14057
14058
14059
14060
14061
14062
14063
14064
14065
14066
14067
14068
14069
14070
14071
14072
14073
14074
14075
14076
14077
14078
14079
14080
14081
14082
14083
14084
14085
14086
14087
14088
14089
14090
14091
14092
14093
14094
14095
14096
14097
14098
14099
14100
14101
14102
14103
14104
14105
14159
14160
14161
14162
14163
14164
14165
14166
14167
14168
14169
14170
14171
14172
14173
14174
14175
14176
14177
14178
14179
14180
14181
14182
14183
14184
14185
14186
14187
14188
14189
14190
14191
14192
14193
14194
14195
14196
14197
14198
14199
14200
14201
14202
14203
14204
14205
14206
14207
14208
14209
14210
14211
14212
14213
14214
14215
14216
14217
14218
14219
14220
14221
14222
14223
14224
14225
14226
14227
14228
14229
14230
14231
14232
14233
14234
14235
14236
14237
14238
14239
14240
14241
14242
14243
14244
14245
14246
14247
14248
14249
14250
14251
14252
14253
14254
14255
14256
14257
14258
14259
14260
14261
14262
14263
14264
14265
14266
14267
14268
14269
14270
14271
14272
14273
14274
14275
14276
14277
14278
14279
14280
14281
14282
14283
14284
14285
14286
14287
14288
14289
14290
14291
14292
14293
14294
14295
14296
14297
14298
14299
14300
14301
14302
14303
14304
14305
14306
14307
14308
14309
14310
14311
14312
14313
14314
14315
14316
14317
14318
14319
14320
14321
14322
14323
14324
14325
14326
14327
14328
14329
14330
14331
14332
14333
14334
14335
14336
14337
14338
14339
14340
14341
14342
14343
14344
14345
14346
14347
14348
14349
14350
14351
14352
14353
14354
14355
14356
14357
14358
14359
14360
14361
14362
14363
14364
14365
14366
14367
14368
14369
14370
14371
14372
14373
14374
14375
14376
14377
14378
14379
14380
14381
14382
14383
14384
14385
14386
14387
14388
14389
14390
14391
14392
14393
14394
14395
14396
14397
14398
14399
14400
14401
14402
14403
14404
14405
14406
14407
14408
14409
14410
14411
14412
14413
14414
14415
14416
14417
14418
14419
14420
14421
14422
14423
14424
14425
14426
14427
14428
14429
14430
14431
14432
14433
14434
14435
14436
14437
14438
14439
14440
14441
14442
14443
14444
14445
14446
14447
14448
14449
14450
14451
14452
14453
14454
14455
14456
14457
14458
14459
14460
14461
14462
14463
14464
14465
14466
14467
14468
14469
14470
14471
14472
14473
14474
14475
14476
14477
14478
14479
14480
14481
14482
14483
14484
14485
14486
14487
14488
14489
14490
14491
14492
14493
14494
14495
14496
14497
14498
14499
14500
14501
14502
14503
14504
14505
14506
14507
14508
14509
14510
14511
14512
14513
14514
14515
14516
14517
14518
14519
14520
14521
14522
14523
14524
14525
14526
14527
14528
14529
14530
14531
14532
14533
14534
14535
14536
14537
14538
14539
14540
14541
14542
14543
14544
14545
14546
14547
14548
14549
14550
14551
14552
14553
14554
14555
14556
14557
14558
14559
14560
14561
14562
14563
14564
14565
14566
14567
14568
14569
14570
14571
14572
14573
14574
14575
14576
14577
14578
14579
14580
14581
14582
14583
14584
14585
14586
14587
14588
14589
14590
14591
14592
14593
14594
14595
14596
14597
14598
14599
14600
14601
14602
14603
14604
14605
14606
14607
14608
14609
14610
14611
14612
14613
14614
14615
14616
14617
14618
14619
14620
14621
14622
14623
14624
14625
14626
14627
14628
14629
14630
14631
14632
14633
14634
14635
14636
14637
14638
14639
14640
14641
14642
14643
14644
14645
14646
14647
14648
14649
14650
14651
14652
14653
14654
14655
14656
14657
14658
14659
14660
14661
14662
14663
14664
14665
14666
14667
14668
14669
14670
14671
14672
14673
14674
14675
14676
14677
14678
14679
14680
14681
14682
14683
14684
14685
14686
14687
14688
14689
14690
14691
14692
14693
14694
14695
14696
14697
14698
14699
14700
14701
14702
14703
14704
14705
14706
14707
14708
14709
14710
14711
14712
14713
14714
14715
14716
14717
14718
14719
14720
14721
14722
14723
14724
14725
14726
14727
14728
14729
14730
14731
14732
14733
14734
14735
14736
14737
14738
14739
14740
14741
14742
14743
14744
14745
14746
14747
14748
14749
14750
14751
14752
14753
14754
14755
14756
14757
14758
14759
14760
14761
14762
14763
14764
14765
14766
14767
14768
14769
14770
14771
14772
14773
14774
14775
14776
14777
14778
14779
14780
14781
14782
14783
14784
14785
14786
14787
14788
14789
14790
14791
14792
14793
14794
14795
14796
14797
14798
14799
14800
14801
14802
14803
14804
14805
14806
14807
14808
14809
14810
14811
14812
14813
14814
14815
14816
14817
14818
14819
14820
14821
14822
14823
14824
14825
14826
14827
14828
14829
14830
14831
14832
14833
14834
14835
14836
14837
14838
14839
14840
14841
14842
14843
14844
14845
14846
14847
14848
14849
14850
14851
14852
14853
14854
14855
14856
14857
14858
14859
14860
14861
14862
14863
14864
14865
14866
14867
14868
14869
14870
14871
14872
14873
14874
14875
14876
14877
14878
14879
14880
14881
14882
14883
14884
14885
14886
14887
14888
14889
14890
14891
14892
14893
14894
14895
14896
14897
14898
14899
14900
14901
14902
14903
14904
14905
14906
14907
14908
14909
14910
14911
14912
14913
14914
14915
14916
14917
14918
14919
14920
14921
14922
14923
14924
14925
14926
14927
14928
14929
14930
14931
14932
14933
14934
14935
14936
14937
14938
14939
14940
14941
14942
14943
14944
14945
14946
14947
14948
14949
14950
14951
14952
14953
14954
14955
14956
14957
14958
14959
14960
14961
14962
14963
14964
14965
14966
14967
14968
14969
14970
14971
14972
14973
14974
14975
14976
14977
14978
14979
14980
14981
14982
14983
14984
14985
14986
14987
14988
14989
14990
14991
14992
14993
14994
14995
14996
14997
14998
14999
15000
15001
15002
15003
15004
15005
15006
15007
15008
15009
15010
15011
15012
15013
15014
15015
15016
15017
15018
15019
15020
15021
15022
15023
15024
15025
15026
15027
15028
15029
15030
15031
15032
15033
15034
15035
15036
15037
15038
15039
15040
15041
15042
15043
15044
15045
15046
15047
15048
15049
15050
15051
15052
15053
15054
15055
15056
15057
15058
15059
15060
15061
15062
15063
15064
15065
15066
15067
15068
15069
15070
15071
15072
15073
15074
15075
15076
15077
15078
15079
15080
15081
15082
15083
15084
15085
15086
15087
15088
15089
15090
15091
15092
15093
15094
15095
15096
15097
15098
15099
15100
15101
15102
15103
15104
15105
15106
15107
15108
15109
15110
15111
15112
15113
15114
15115
15116
15117
15118
15119
15120
15121
15122
15123
15124
15125
15126
15127
15128
15129
15130
15131
15132
15133
15134
15135
15136
15137
15138
15139
15140
15141
15142
15143
15144
15145
15146
15147
15148
15149
15150
15151
15152
15153
15154
15155
15156
15157
15158
15159
15160
15161
15162
15163
15164
15165
15166
15167
15168
15169
15170
15171
15172
15173
15174
15175
15176
15177
15178
15179
15180
15181
15182
15183
15184
15185
15186
15187
15188
15189
15190
15191
15192
15193
15194
15195
15196
15197
15198
15199
15200
15201
15202
15203
15204
15205
15206
15207
15208
15209
15210
15211
15212
15213
15214
15215
15216
15217
15218
15219
15220
15221
15222
15223
15224
15225
15226
15227
15228
15229
15230
15231
15232
15233
15234
15235
15236
15237
15238
15239
15240
15241
15242
15243
15244
15245
15246
15247
15248
15249
15250
15251
15252
15253
15254
15255
15256
15257
15258
15259
15260
15261
15262
15263
15264
15265
15266
15267
15268
15269
15270
15271
15272
15273
15274
15275
15276
15277
15278
15279
15280
15281
15282
15283
15284
15285
15286
15287
15288
15289
15290
15291
15292
15293
15294
15295
15296
15297
15298
15299
15300
15301
15302
15303
15304
15305
15306
15307
15308
15309
15310
15311
15312
15313
15314
15315
15316
15317
15318
15319
15320
15321
15322
15323
15324
15325
15326
15327
15328
15329
15330
15331
15332
15333
15334
15335
15336
15337
15338
15339
15340
15341
15342
15343
15344
15345
15346
15347
15348
15349
15350
15351
15352
15353
15354
15355
15356
15357
15358
15359
15360
15361
15362
15363
15364
15365
15366
15367
15368
15369
15370
15371
15372
15373
15374
15375
15376
15377
15378
15379
15380
15381
15382
15383
15384
15385
15386
15387
15388
15389
15390
15391
15392
15393
15394
15395
15396
15397
15398
15399
15400
15401
15402
15403
15404
15405
15406
15407
15408
15409
15410
15411
15412
15413
15414
15415
15416
15417
15418
15419
15420
15421
15422
15423
15424
15425
15426
15427
15428
15429
15430
15431
15432
15433
15434
15435
15436
15437
15438
15439
15440
15441
15442
15443
15444
15445
15446
15447
15448
15449
15450
15451
15452
15453
15454
15455
15456
15457
15458
15459
15460
15461
15462
15463
15464
15465
15466
15467
15468
15469
15470
15471
15472
15473
15474
15475
15476
15477
15478
15479
15480
15481
15482
15483
15484
15485
15486
15487
15488
15489
15490
15491
15492
15493
15494
15495
15496
15497
15498
15499
15500
15501
15502
15503
15504
15505
15506
15507
15508
15509
15510
15511
15512
15513
15514
15515
15516
15517
15518
15519
15520
15521
15522
15523
15524
15525
15526
15527
15528
15529
15530
15531
15532
15533
15534
15535
15536
15537
15538
15539
15540
15541
15542
15543
15544
15545
15546
15547
15548
15549
15550
15551
15552
15553
15554
15555
15556
15557
15558
15559
15560
15561
15562
15563
15564
15565
15566
15567
15568
15569
15570
15571
15572
15573
15574
15575
15576
15577
15578
15579
15580
15581
15582
15583
15584
15585
15586
15587
15588
15589
15590
15591
15592
15593
15594
15595
15596
15597
15598
15599
15600
15601
15602
15603
15604
15605
15606
15607
15608
15609
15610
15611
15612
15613
15614
15615
15616
15617
15618
15619
15620
15621
15622
15623
15624
15625
15626
15627
15628
15629
15630
15631
15632
15633
15634
15635
15636
15637
15638
15639
15640
15641
15642
15643
15644
15645
15646
15647
15648
15649
15650
15651
15652
15653
15654
15655
15656
15657
15658
15659
15660
15661
15662
15663
15664
15665
15666
15667
15668
15669
15670
15671
15672
15673
15674
15675
15676
15677
15678
15679
15680
15681
15682
15683
15684
15685
15686
15687
15688
15689
15690
15691
15692
15693
15694
15695
15696
15697
15698
15699
15700
15701
15702
15703
15704
15705
15706
15707
15708
15709
15710
15711
15712
15713
15714
15715
15716
15717
15718
15719
15720
15721
15722
15723
15724
15725
15726
15727
15728
15729
15730
15731
15732
15733
15734
15735
15736
15737
15738
15739
15740
15741
15742
15743
15744
15745
15746
15747
15748
15749
15750
15751
15752
15753
15754
15755
15756
15757
15758
15759
15760
15761
15762
15763
15764
15765
15766
15767
15768
15769
15770
15771
15772
15773
15774
15775
15776
15777
15778
15779
15780
15781
15782
15783
15784
15785
15786
15787
15788
15789
15790
15791
15792
15793
15794
15795
15796
15797
15798
15799
15800
15801
15802
15803
15804
15805
15806
15807
15808
15809
15810
15811
15812
15813
15814
15815
15816
15817
15818
15819
15820
15821
15822
15823
15824
15825
15826
15827
15828
15829
15830
15831
15832
15833
15834
15835
15836
15837
15838
15839
15840
15841
15842
15843
15844
15845
15846
15847
15848
15849
15850
15851
15852
15853
15854
15855
15856
15857
15858
15859
15860
15861
15862
15863
15864
15865
15866
15867
15868
15869
15870
15871
15872
15873
15874
15875
15876
15877
15878
15879
15880
15881
15882
15883
15884
15885
15886
15887
15888
15889
15890
15891
15892
15893
15894
15895
15896
15897
15898
15899
15900
15901
15902
15903
15904
15905
15906
15907
15908
15909
15910
15911
15912
15913
15914
15915
15916
15917
15918
15919
15920
15921
15922
15923
15924
15925
15926
15927
15928
15929
15930
15931
15932
15933
15934
15935
15936
15937
15938
15939
15940
15941
15942
15943
15944
15945
15946
15947
15948
15949
15950
15951
15952
15953
15954
15955
15956
15957
15958
15959
15960
15961
15962
15963
15964
15965
15966
15967
15968
15969
15970
15971
15972
15973
15974
15975
15976
15977
15978
15979
15980
15981
15982
15983
15984
15985
15986
15987
15988
15989
15990
15991
15992
15993
15994
15995
15996
15997
15998
15999
16000
16001
16002
16003
16004
16005
16006
16007
16008
16009
16010
16011
16012
16013
16014
16015
16016
16017
16018
16019
16020
16021
16022
16023
16024
16025
16026
16027
16028
16029
16030
16031
16032
16033
16034
16035
16036
16037
16038
16039
16040
16041
16042
16043
16044
16045
16046
16047
16048
16049
16050
16051
16052
16053
16054
16055
16056
16057
16058
16059
16060
16061
16062
16063
16064
16065
16066
16067
16068
16069
16070
16071
16072
16073
16074
16075
16076
16077
16078
16079
16080
16081
16082
16083
16084
16085
16086
16087
16088
16089
16090
16091
16092
16093
16094
16095
16096
16097
16098
16099
16100
16101
16102
16103
16104
16105
16106
16107
16108
16109
16110
16111
16112
16113
16114
16115
16116
16117
16118
16119
16120
16121
16122
16123
16124
16125
16126
16127
16128
16129
16130
16131
16132
16133
16134
16135
16136
16137
16138
16139
16140
16141
16142
16143
16144
16145
16146
16147
16148
16149
16150
16151
16152
16153
                                                                      
                                                         
                                                                    


                                                   





                                                                  
   
                                                             
                                                             
                                                             
                                                             



                                                             
                                                             







                                                             
                                                             

















































                                                                               
                                                             


                                                                                
                             













































                                                                        
                                        
             
                                       






























































                                                                                                                                                     
                                        
             
                                       

























































































































                                                                                            
                             
                                            
                 






                                                                            
                                                                                                    
                      
                                                                             











                                                                                                    
                                      
                           
                                              
                                    
                                              
                                     
                                              
                                     
                                              









                                                                                             
                                              

                               


                                                                            


                                


                                                                                     
                                        

                                      

                                         
                       


                                        
 



                                                
 



                                                           
 





                                        
       





                                           
                                                                                                        
                             
                                                  






























































































































































































































































































































































































































































































































                                                                                                                           
                                                                                                    
                             
                                                







                                                                                              






                                              
                                                           















































                                                                                               
                                                               






                                                                                           
                                                                                     



















                                                                     
                                                                                   






















































































































                                                                                           
                                                     






                                                                  
                                                     






                                                   
                                                                     






                                                                                                                                                                                              
                                                                     





                                               
                                                                    
























                                                                                                       
                                                              













                                                                             
                                                                        











                                                                             
                                                                       






                                                                                                                    
                                                                                  






                                                           
                                                                               

                                                                                

                                                                                        

                                                             
                                                                           









                                                                             
                                                                             




















                                                          
                                                                           







                                                         
                                                                                                          
                             
                                                   





















































































































































































































































































































































































                                                                                                                          
                                                                                                                        
                             
                                                          







































































































































                                                                                                  
                                                                                                                  
                             
                                                       














































































































                                                                                                                    
                                                                                                                    
                             
                                                        


































































































































































































































                                                                                            
                                                                                                                        
                             
                                                          




















                                                                            











                                                                                  


























































































































































                                                                                                            
                             



















































                                                                       
                                     














































                                                                                                        












































































                                                                                                                        


















                                                                                                

































































































































































































                                                                                                              
                                                                                        
                             
                                          
                       














































                                                                                                        
                              

































































































































                                                                                                      
                             
                                                 







                                                                                              




































































































                                                                                                    
                             
                                                
                                                            



















                                                                       
                                                                             
                                         
 
                        
                                                                    
 
                                                    
                                                                              













                                                                                      
                                                                   

































                                                                                              
 








                                                                                                             
                                                                                 




















                                                                  
                                                                                                       

























































































                                                            
                                                                                                      





                                                                                                                          
                                                                                                       










                                                                                                                     
       
                                                                                                                           
                                                              












                                        
                                                              





















                                                                                             
                                                             
                                                                    
 











                                                                          
                                                              























                                                                                                 
                                                              



























                                                                                                                     
                                                            







                                                                                     
                                                                             






                                                                                                          
                             
                                                   
                                                                               























































































































                                                                                                                               
                                                                                                      




































































































































































































                                                                                                                                                                                                               
                                                                                   
















































































































































































































































                                                                                                
                             
                                            
                                                                   























                                                                               
                                                                 







                                                                                                     
                                                                   














































































































































































































































































































































































                                                                                                                                                                       
                             














































































































































































































































































































































































































































































































































































































































































































































                                                                                                                                                                          
 



























































































































                                                                                                    
 























































































                                                                                                    
 













































































                                                                                                                                     
                                                                                  









                                                                                      
                                        
                                                                                                 
       









                                                                                       
                                   




                                                               
 







                                                                                            
 
                                                                                           
                                  
                                                                                                         
 





                                                                                         
                                         
                                                                                                        
       










                                                                                                         
                                   










                                                                              
                                         
                                                                                                        
       
                                                                                                   
                                 





                                                                                                                
                                  












                                                                                                    
                                                                                      






































































































































































































































                                                                                                                     































































































































































































                                                                                                                    
                                                                            


















                                                                                   
                                                                             












                                                                          
 

                                                                              
 







                                                                                

                                                                         
                                                       
                                                          





                                                                                                              
                                                                                   






                                                                                 
                                                                                   






                                                                                                                 
                                                                                   






                                                                                                           
                                                                                   






                                                                                   
                                                                                   






                                                                                                                            
                                                                                     



























                                                                                                                                  
                              


































































































                                                                                                              
                                                                                 

































                                                                                                                         
                                                                             









                                                                      
                                                                            


























                                                                                                          
                                                                            





                                         
                                                                              










                                                                   
                                                                                















































































































































































                                                                                                            
 
































                                                                                           

                                                    













                                                                                           
                                                                              






                                                             
                                                                              





                                                                             
                                                                                 









                                                                                  
                                                                            





                                         
                                                                             















                                                                              
                                                                              

























































                                                                                                 
                                                                              


















































                                                                           
                                                                              






































                                                                                                  
                                                                             










                                                                        
                                                                             












                                                                                 
                                                                            











                                                                                   
                                                                           






                                                                                         
                                                                           






                                                                            
                                                                             













































                                                                                           
                                                                           





                                                                              
                                                                                












                                                                   
                                                                                










                                                                                          
                                                                              








                                                                      
                                                                                









                                
                                                                                 













                                                                                                         
                                                                               






                                                                                
                                                                               






                                                         
                                                                               









                                                                    
                                                                             






                                                    
                                                                             





                                                   
                                                                             





                                                                                                                                                    
                                                                              










                                                                          
                                                                             





                                                                                             
                                                                              














                                                                                    
                                                                                       








                                                              
 













                                                                                                                      
                                                                            






                                                                                              
                                                                              






















                                                                                                                              
                                                                          







                                                                                                                       
                                                                                       








                                                           
                                                                                           





                                             
                                                                                       



















                                                                                                    
                                                                               










                                                                                                                                              
                                                                                 
















                                                                          
                                                                                  























                                                                      
                                                                                  











                                          
                                                                                






                                                     
                                                                             



















                                                                            
                                                                                         












                                                                            
                                                                                       






                                                                            
                                                                                       






                                                                            
                                                                                       






                                                                                    
                                                                                    






                                                                            
                                                                                        






                                                                                  
                                                                                     






                                                                          
                                                                                     






                                                                          
                                                                                       







































                                                                                                                     
                                                                                 








































































                                                                              
                                                                                  








































































































                                                                                               
                                                                                    
















                                                           
                                                                              






                                           
                                                                              









                                                                  
                                                                               






                                           
                                                                                 














                                                                  
                                                                               






                                           
                                                                                  





















                                                                              
 























































                                                                                                        
 






























































































































































































































































                                                                                                        
                                                                                   









                                                                                
                                                                                         























































































































































































































                                                                                                                    
                                                                                          






                                                                                   
                                                                                            












                                                                                       
                                                                                            










































                                                                                        
                                                                            































































































                                                                                         
                                                                                          







                                           
 



                                                                               


                                                                                     




                                                                                               
                      

                                                     
                                                        























                                                                                                        
 











                                                                                               


                                              











                                                                              











                                                                                      



                                                                       






                                                                               
                                                                               





                                    
                                                                                









                                                                
                                                                                 











                                                                                                         
                                                                                       













































                                                                                           
 




                                                                                   
 




                                                                                   
 




                                                                                   


                 
                                                                            





                                                                     
                                                                                            

































































































































































































































































































































































































































                                                                                                                                              
                             





































































































































































































































































































































































































































































































































































































































































































































































































































                                                                                                                                                                                                                          
 











































                                                                                                                                                 
 













































































                                                                                                                                        
 



























































































                                                                                                                                        
                                                                                                              
                                                                                             
 





                                                                                                                                                                      
 

































                                                                                                                                                                                    
                                                                                                              































































































































































































































                                                                                                             
                             




















                                                                                               
                                                                                                        




                                                                                






































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































                                                                                                                                                
                             





















































































































































































































































































































































































































































































































































































































































                                                                                                                                                                         
                                                                                






                                                                                                                                  
                                                                                  



























































































































































































































































































































































































































                                                                                                                                                                                                    
 




































































                                                                                                                                                                        
                                                                                       











                                                 
                                                                                          






                                                                              
                                                                                          






                                                                             
                                                                                                     





                          
                                                                                                       


















                                                                                  
                                                                                                     






                                                                        
                                                                                                     






                                                                            
                                                                                                     






                                                                                           
                                                                                                       














                                                                   
                                                                                   






                                               
                                                                                   






                                                                                               
                                                                                     













                                                                                               
                                                                                        













                                                                             
                                                                                       

























































































































































































































































































































































































































































































































































































































































































































































































































































































                                                                                                                                
                             
























































































































































                                                                                                                    
                                                                           















































































































































































































































































                                                                                                                                                                    
                                                                                                                                                                                   














                                                                                                                        
                                                                                                                                                                    

                                                                                                       
                                                                                                                                                            












                                                                                                             
                                                                                                                                                                                             
 






                                                                                                                                        
                                                                                                                                                                                    
                                                 
                                                                                                                                                                            
                                         
                                                                                                                                                                    









































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































                                                                                                                                                                                                   
                             









































                                                                                                      
                                                                                 







                                                   
                                                                                  


                                                                                                      
                     




                                                                              







                                                                                  
       





                                                                                                                                                                                                                          
                                                             







                                              
       
 
From 429c96728e6a22e1d53f801c8bd4075a91fe422b Mon Sep 17 00:00:00 2001
From: Timothy Pearson <tpearson@raptorengineeringinc.com>
Date: Fri, 16 Oct 2015 13:51:51 -0500
Subject: [PATCH 034/143] cpu/amd: Add initial AMD Family 15h support

TEST: Booted ASUS KGPE-D16 with single Opteron 6380
 * Unbuffered DDR3 DIMMs tested and working
 * Suspend to RAM (S3) tested and working

Conflicts:

	src/cpu/amd/car/disable_cache_as_ram.c

Change-Id: Idffd2ce36ce183fbfa087e5ba69a9148f084b45e
Signed-off-by: Timothy Pearson <tpearson@raptorengineeringinc.com>
---
 src/cpu/amd/car/cache_as_ram.inc                   |  130 +-
 src/cpu/amd/car/disable_cache_as_ram.c             |   79 +-
 src/cpu/amd/family_10h-family_15h/defaults.h       |  266 +-
 src/cpu/amd/family_10h-family_15h/fidvid.c         |  237 +-
 src/cpu/amd/family_10h-family_15h/init_cpus.c      |  232 +-
 .../amd/family_10h-family_15h/model_10xxx_init.c   |   92 +-
 src/cpu/amd/family_10h-family_15h/powernow_acpi.c  |   50 +-
 src/cpu/amd/family_10h-family_15h/processor_name.c |  194 +-
 .../amd/family_10h-family_15h/update_microcode.c   |    6 +
 src/cpu/amd/model_fxx/init_cpus.c                  |    2 +-
 src/cpu/amd/quadcore/quadcore.c                    |  109 +-
 src/cpu/amd/quadcore/quadcore_id.c                 |   43 +-
 src/include/cpu/amd/model_10xxx_msr.h              |    7 +
 src/mainboard/advansus/a785e-i/romstage.c          |    2 +-
 src/mainboard/amd/bimini_fam10/romstage.c          |    2 +-
 src/mainboard/amd/mahogany_fam10/romstage.c        |    2 +-
 .../amd/serengeti_cheetah_fam10/romstage.c         |    2 +-
 src/mainboard/amd/tilapia_fam10/romstage.c         |    2 +-
 src/mainboard/asus/kfsn4-dre/romstage.c            |    2 +-
 src/mainboard/asus/kgpe-d16/romstage.c             |    4 +-
 src/mainboard/asus/m4a78-em/romstage.c             |    2 +-
 src/mainboard/asus/m4a785-m/romstage.c             |    2 +-
 src/mainboard/asus/m5a88-v/romstage.c              |    2 +-
 src/mainboard/avalue/eax-785e/romstage.c           |    2 +-
 src/mainboard/gigabyte/ma785gm/romstage.c          |    2 +-
 src/mainboard/gigabyte/ma785gmt/romstage.c         |    2 +-
 src/mainboard/gigabyte/ma78gm/romstage.c           |    2 +-
 src/mainboard/hp/dl165_g6_fam10/romstage.c         |    2 +-
 src/mainboard/iei/kino-780am2-fam10/romstage.c     |    2 +-
 src/mainboard/jetway/pa78vm5/romstage.c            |    2 +-
 src/mainboard/msi/ms9652_fam10/romstage.c          |    2 +-
 src/mainboard/supermicro/h8dmr_fam10/romstage.c    |    2 +-
 src/mainboard/supermicro/h8qme_fam10/romstage.c    |    2 +-
 src/mainboard/supermicro/h8scm_fam10/romstage.c    |    2 +-
 src/mainboard/tyan/s2912_fam10/romstage.c          |    2 +-
 src/northbridge/amd/amdfam10/Kconfig               |    2 +-
 src/northbridge/amd/amdfam10/Makefile.inc          |    2 +
 src/northbridge/amd/amdfam10/amdfam10.h            |    6 +-
 src/northbridge/amd/amdfam10/amdfam10_util.c       |   13 +-
 src/northbridge/amd/amdfam10/link_control.c        |   86 +
 src/northbridge/amd/amdfam10/misc_control.c        |    7 +
 src/northbridge/amd/amdfam10/nb_control.c          |   85 +
 src/northbridge/amd/amdfam10/northbridge.c         |  233 +-
 src/northbridge/amd/amdfam10/raminit_amdmct.c      |  304 +-
 src/northbridge/amd/amdht/h3ncmn.c                 |  171 +-
 src/northbridge/amd/amdht/ht_wrapper.c             |   43 +-
 src/northbridge/amd/amdmct/amddefs.h               |   78 +-
 src/northbridge/amd/amdmct/mct/mct_d.c             |    4 +-
 src/northbridge/amd/amdmct/mct/mct_d.h             |   20 +-
 src/northbridge/amd/amdmct/mct/mctpro_d.c          |   21 +-
 src/northbridge/amd/amdmct/mct_ddr3/mct_d.c        | 3187 ++++++++++++++++----
 src/northbridge/amd/amdmct/mct_ddr3/mct_d.h        |  124 +-
 src/northbridge/amd/amdmct/mct_ddr3/mct_d_gcc.h    |    9 +
 src/northbridge/amd/amdmct/mct_ddr3/mctardk5.c     |   21 +-
 src/northbridge/amd/amdmct/mct_ddr3/mctcsi_d.c     |   27 +-
 src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c     | 1087 ++++++-
 src/northbridge/amd/amdmct/mct_ddr3/mctecc_d.c     |   55 +-
 src/northbridge/amd/amdmct/mct_ddr3/mcthdi.c       |    7 +-
 src/northbridge/amd/amdmct/mct_ddr3/mcthwl.c       |  105 +-
 src/northbridge/amd/amdmct/mct_ddr3/mctproc.c      |    2 +-
 src/northbridge/amd/amdmct/mct_ddr3/mctrci.c       |   24 +-
 src/northbridge/amd/amdmct/mct_ddr3/mctsdi.c       |  585 +++-
 src/northbridge/amd/amdmct/mct_ddr3/mctsrc.c       | 1342 ++++++++-
 src/northbridge/amd/amdmct/mct_ddr3/mctsrc1p.c     |   10 +-
 src/northbridge/amd/amdmct/mct_ddr3/mcttmrl.c      |   20 +-
 src/northbridge/amd/amdmct/mct_ddr3/mctwl.c        |  255 +-
 src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c      | 1007 +++++--
 src/northbridge/amd/amdmct/mct_ddr3/mutilc_d.c     |   69 +-
 src/northbridge/amd/amdmct/mct_ddr3/mwlc_d.h       |   46 +-
 src/northbridge/amd/amdmct/mct_ddr3/s3utils.c      |  652 +++-
 src/northbridge/amd/amdmct/wrappers/mcti.h         |   14 +-
 src/northbridge/amd/amdmct/wrappers/mcti_d.c       |   43 +-
 72 files changed, 9192 insertions(+), 2067 deletions(-)
 create mode 100644 src/northbridge/amd/amdfam10/link_control.c
 create mode 100644 src/northbridge/amd/amdfam10/nb_control.c

diff --git a/src/cpu/amd/car/cache_as_ram.inc b/src/cpu/amd/car/cache_as_ram.inc
index 0b2bc60..6542906 100644
--- a/src/cpu/amd/car/cache_as_ram.inc
+++ b/src/cpu/amd/car/cache_as_ram.inc
@@ -32,18 +32,23 @@
 #define CacheSizeAPStack	CONFIG_DCACHE_AP_STACK_SIZE
 
 #define MSR_MCFG_BASE		0xC0010058
-#define MSR_FAM10		0xC001102A
+#define MSR_BU_CFG2		0xC001102A
 
 #define jmp_if_k8(x)		comisd	%xmm2, %xmm1; jb x
+#define jmp_if_not_fam15h(x)	comisd	%xmm3, %xmm1; jb x
+#define jmp_if_fam15h(x)	comisd	%xmm3, %xmm1; jae x
 
 #define CPUID_MASK		0x0ff00f00
 #define CPUID_VAL_FAM10_ROTATED	0x0f000010
+#define CPUID_VAL_FAM15_ROTATED	0x0f000060
 
 /*
  * XMM map:
  *   xmm1: CPU family
  *   xmm2: Fam10h comparison value
- *   xmm3: Backup EBX
+ *   xmm3: Fam15h comparison value
+ *   xmm4: Backup EBX
+ *   xmm5: Coreboot init detect
  */
 
 	/* Save the BIST result. */
@@ -63,7 +68,7 @@ cache_as_ram_setup:
 	movl	%eax, %cr4
 
 	/* Figure out the CPU family. */
-	cvtsi2sd %ebx, %xmm3
+	cvtsi2sd %ebx, %xmm4
 	movl	$0x01, %eax
 	cpuid
 	/* Base family is bits 8..11, extended family is bits 20..27. */
@@ -73,13 +78,16 @@ cache_as_ram_setup:
 	cvtsi2sd %eax, %xmm1
 	movl	$CPUID_VAL_FAM10_ROTATED, %eax
 	cvtsi2sd %eax, %xmm2
-	cvtsd2si %xmm3, %ebx
+	movl	$CPUID_VAL_FAM15_ROTATED, %eax
+	cvtsi2sd %eax, %xmm3
+	cvtsd2si %xmm4, %ebx
 
 	/* Check if cpu_init_detected. */
 	movl	$MTRR_DEF_TYPE_MSR, %ecx
 	rdmsr
 	andl	$MTRR_DEF_TYPE_EN, %eax
 	movl	%eax, %ebx	/* We store the status. */
+	cvtsi2sd %ebx, %xmm5
 
 	jmp_if_k8(CAR_FAM10_out_post_errata)
 
@@ -120,21 +128,24 @@ cache_as_ram_setup:
 
 CAR_FAM10_out:
 
+	jmp_if_fam15h(CAR_FAM10_errata_applied)
 	/*
 	 * Errata 193: Disable clean copybacks to L3 cache to allow cached ROM.
 	 * Re-enable it in after RAM is initialized and before CAR is disabled.
 	 */
-	movl	$MSR_FAM10, %ecx
+	movl	$MSR_BU_CFG2, %ecx
 	rdmsr
-	bts	$15, %eax
+	bts	$15, %eax	/* Set bit 15 in EDX:EAX (bit 15 in EAX). */
 	wrmsr
 
 	/* Erratum 343, RevGuide for Fam10h, Pub#41322 Rev. 3.33 */
-	movl	$MSR_FAM10, %ecx
+	movl	$MSR_BU_CFG2, %ecx
 	rdmsr
 	bts	$35-32, %edx	/* Set bit 35 in EDX:EAX (bit 3 in EDX). */
 	wrmsr
 
+CAR_FAM10_errata_applied:
+
 #if CONFIG_MMCONF_SUPPORT
    #if (CONFIG_MMCONF_BASE_ADDRESS > 0xFFFFFFFF)
    #error "MMCONF_BASE_ADDRESS too big"
@@ -169,6 +180,63 @@ CAR_FAM10_out:
 
 CAR_FAM10_out_post_errata:
 
+	/* Fam15h APIC IDs do not depend on NB config bit 54 */
+	jmp_if_not_fam15h(skip_nb54_set)
+	movl	$0xc001001f, %ecx	/* NB_CFG_MSR */
+	rdmsr
+	bts	$(54 - 32), %edx	/* Set NB config bit 54 */
+	wrmsr
+
+skip_nb54_set:
+	/* On Fam15h CPUs each compute unit's MTRRs are shared between two cores */
+	jmp_if_not_fam15h(skip_cu_check)
+
+	/* Get the initial APIC ID. */
+	movl	$1, %eax
+	cpuid
+	movl	%ebx, %eax
+
+	/* Restore init detect */
+	cvtsd2si %xmm5, %ebx
+
+	/* Determine if this is the second core to start in a compute unit; if so, wait for first core start, clear init detect and skip MTRR init */
+	bt	$24, %eax
+	jnc	skip_cu_check		/* First core in the compute unit jumps to skip_cu_check */
+
+	/* Determine if this is the second core to start in a compute unit; if so, clear init detect and skip MTRR init */
+	/* Busywait until the first core sets up the MTRRs */
+check_init_detect_1:
+	/* Check if cpu_init_detected. */
+	movl	$MTRR_DEF_TYPE_MSR, %ecx
+	rdmsr
+	andl	$MTRR_DEF_TYPE_EN, %eax
+	cmp	$0x00000000, %eax
+	je	check_init_detect_1	/* First core has not yet started */
+
+check_init_detect_2:
+	movl	$SYSCFG_MSR, %ecx
+	rdmsr
+	andl	$(SYSCFG_MSR_MtrrFixDramEn | SYSCFG_MSR_MtrrVarDramEn), %eax
+	cmp	$0x00000000, %eax
+	je	check_init_detect_2	/* First core has not yet started */
+
+	/* First core has now started */
+	movl	$0x00000000, %ebx	/* Clear init detect flag */
+	cvtsi2sd %ebx, %xmm5
+	jmp	fam10_mtrr_setup_complete
+
+skip_cu_check:
+
+	jmp_if_not_fam15h(CAR_FAM15_errata_applied)
+
+	/* Erratum 714, RevGuide for Fam15h, Pub#48063 Rev. 3.24 */
+	movl	$MSR_BU_CFG2, %ecx
+	rdmsr
+	bts	$8, %eax	/* Set bit 8 in EDX:EAX (bit 8 in EAX). */
+	wrmsr
+
+CAR_FAM15_errata_applied:
+
 	/* Set MtrrFixDramModEn for clear fixed MTRR. */
 enable_fixed_mtrr_dram_modify:
 	movl	$SYSCFG_MSR, %ecx
@@ -337,8 +405,42 @@ wbcache_post_fam10_setup:
 	orl	$(SYSCFG_MSR_MtrrVarDramEn | SYSCFG_MSR_MtrrFixDramEn), %eax
 	wrmsr
 
+fam10_mtrr_setup_complete:
 	post_code(0xa1)
 
+	/* Disable conversion of INVD to WBINVD (INVDWBINVD = 0) */
+	mov	$0xc0010015, %ecx
+	rdmsr
+	btr	$4, %eax
+	wrmsr
+
+jmp_if_not_fam15h(fam15_car_msr_setup_complete)
+	/* Disable streaming store (DisSS = 1) */
+	mov	$0xc0011020, %ecx
+	rdmsr
+	bts	$28, %eax
+	wrmsr
+
+	/* Disable speculative ITLB reloads (DisSpecTlbRld = 1) */
+	mov	$0xc0011021, %ecx
+	rdmsr
+	bts	$9, %eax
+	wrmsr
+
+	/* Disable speculative DTLB reloads (DisSpecTlbRld = 1) and set DisHwPf = 1 */
+	mov	$0xc0011022, %ecx
+	rdmsr
+	bts	$4, %eax
+	bts	$13, %eax
+	wrmsr
+
+	/* Disable CR0 combining (CombineCr0Cd = 0) */
+	mov	$0xc001102b, %ecx
+	rdmsr
+	btr	$49-32, %edx
+	wrmsr
+fam15_car_msr_setup_complete:
+
 	/* Enable cache. */
 	movl	%cr0, %eax
 	andl	$(~(CR0_CacheDisable | CR0_NoWriteThrough)), %eax
@@ -393,9 +495,6 @@ CAR_FAM10_ap:
 	 * to reverse it.
 	 */
 
-	/* Store our init detected. */
-	movl	%ebx, %esi
-
 	/* Get the coreid bits at first. */
 	movl	$0x80000008, %eax
 	cpuid
@@ -414,6 +513,8 @@ CAR_FAM10_ap:
 	movl	%edi, %ecx		/* CoreID bits */
 	bt	$(54 - 32), %edx
 	jc	roll_cfg
+
+	/* Fam10h NB config bit 54 was not set */
 	rolb	%cl, %bl
 roll_cfg:
 
@@ -423,8 +524,8 @@ roll_cfg:
 	movl	$(CacheBase + (CacheSize - (CacheSizeBSPStack + CacheSizeBSPSlush))), %esp
 	subl	%eax, %esp
 
-	/* Retrive init detected. */
-	movl	%esi, %ebx
+	/* Restore init detect */
+	cvtsd2si %xmm5, %ebx
 
 	post_code(0xa4)
 
@@ -437,6 +538,8 @@ CAR_FAM10_ap_out:
 	andl	$~(3 << 9), %eax
 	movl	%eax, %cr4
 
+	post_code(0xa6)
+
 	/* Restore the BIST result. */
 	movl	%ebp, %eax
 
@@ -444,6 +547,9 @@ CAR_FAM10_ap_out:
 	movl	%esp, %ebp
 	pushl	%ebx		/* Init detected. */
 	pushl	%eax		/* BIST */
+
+	post_code(0xa7)
+
 	call	cache_as_ram_main
 
 	/* We will not go back. */
diff --git a/src/cpu/amd/car/disable_cache_as_ram.c b/src/cpu/amd/car/disable_cache_as_ram.c
index 5eccf79..86180ee 100644
--- a/src/cpu/amd/car/disable_cache_as_ram.c
+++ b/src/cpu/amd/car/disable_cache_as_ram.c
@@ -19,7 +19,7 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc.
  *
- * be warned, this file will be used other cores and core 0 / node 0
+ * WARNING: this file will be used by both any AP cores and core 0 / node 0
  */
 
 #include <cpu/x86/cache.h>
@@ -34,41 +34,80 @@ static inline __attribute__((always_inline)) uint32_t amd_fam1x_cpu_family(void)
 	return family;
 }
 
-static inline __attribute__((always_inline)) void disable_cache_as_ram(void)
+static inline __attribute__((always_inline)) void disable_cache_as_ram(uint8_t skip_sharedc_config)
 {
 	msr_t msr;
+	uint32_t family;
 
-	/* disable cache */
-	write_cr0(read_cr0() | CR0_CacheDisable);
+	if (!skip_sharedc_config) {
+		/* disable cache */
+		write_cr0(read_cr0() | CR0_CacheDisable);
 
-	msr.lo = 0;
-	msr.hi = 0;
-	wrmsr(MTRR_FIX_4K_C8000, msr);
+		msr.lo = 0;
+		msr.hi = 0;
+		wrmsr(MTRR_FIX_4K_C8000, msr);
 #if CONFIG_DCACHE_RAM_SIZE > 0x8000
-	wrmsr(MTRR_FIX_4K_C0000, msr);
+		wrmsr(MTRR_FIX_4K_C0000, msr);
 #endif
 #if CONFIG_DCACHE_RAM_SIZE > 0x10000
-	wrmsr(MTRR_FIX_4K_D0000, msr);
+		wrmsr(MTRR_FIX_4K_D0000, msr);
 #endif
 #if CONFIG_DCACHE_RAM_SIZE > 0x18000
-	wrmsr(MTRR_FIX_4K_D8000, msr);
+		wrmsr(MTRR_FIX_4K_D8000, msr);
 #endif
-	/* disable fixed mtrr from now on, it will be enabled by ramstage again*/
+		/* disable fixed mtrr from now on, it will be enabled by ramstage again */
+		msr = rdmsr(SYSCFG_MSR);
+		msr.lo &= ~(SYSCFG_MSR_MtrrFixDramEn | SYSCFG_MSR_MtrrFixDramModEn);
+		wrmsr(SYSCFG_MSR, msr);
+
+		/* Set the default memory type and disable fixed and enable variable MTRRs */
+		msr.hi = 0;
+		msr.lo = (1 << 11);
+
+		wrmsr(MTRR_DEF_TYPE_MSR, msr);
+
+		enable_cache();
+	}
 
-	msr = rdmsr(SYSCFG_MSR);
-	msr.lo &= ~(SYSCFG_MSR_MtrrFixDramEn | SYSCFG_MSR_MtrrFixDramModEn);
-	wrmsr(SYSCFG_MSR, msr);
+	/* INVDWBINVD = 1 */
+	msr = rdmsr(0xc0010015);
+	msr.lo |= (0x1 << 4);
+	wrmsr(0xc0010015, msr);
 
-	/* Set the default memory type and disable fixed and enable variable MTRRs */
-	msr.hi = 0;
-	msr.lo = (1 << 11);
+	family = amd_fam1x_cpu_family();
 
-	wrmsr(MTRR_DEF_TYPE_MSR, msr);
+#if IS_ENABLED(CPU_AMD_MODEL_10XXX)
+	if (family >= 0x6f) {
+		/* Family 15h or later */
 
-	enable_cache();
+		/* DisSS = 0 */
+		msr = rdmsr(0xc0011020);
+		msr.lo &= ~(0x1 << 28);
+		wrmsr(0xc0011020, msr);
+
+		if (!skip_sharedc_config) {
+			/* DisSpecTlbRld = 0 */
+			msr = rdmsr(0xc0011021);
+			msr.lo &= ~(0x1 << 9);
+			wrmsr(0xc0011021, msr);
+
+			/* Erratum 714: SpecNbReqDis = 0 */
+			msr = rdmsr(BU_CFG2_MSR);
+			msr.lo &= ~(0x1 << 8);
+			wrmsr(BU_CFG2_MSR, msr);
+		}
+
+		/* DisSpecTlbRld = 0 */
+		/* DisHwPf = 0 */
+		msr = rdmsr(0xc0011022);
+		msr.lo &= ~(0x1 << 4);
+		msr.lo &= ~(0x1 << 13);
+		wrmsr(0xc0011022, msr);
+	}
+#endif
 }
 
 static void disable_cache_as_ram_bsp(void)
 {
-	disable_cache_as_ram();
+	disable_cache_as_ram(0);
 }
diff --git a/src/cpu/amd/family_10h-family_15h/defaults.h b/src/cpu/amd/family_10h-family_15h/defaults.h
index 6fd1a7e..24f87ba 100644
--- a/src/cpu/amd/family_10h-family_15h/defaults.h
+++ b/src/cpu/amd/family_10h-family_15h/defaults.h
@@ -2,6 +2,7 @@
  * This file is part of the coreboot project.
  *
  * Copyright (C) 2008 Advanced Micro Devices, Inc.
+ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -25,41 +26,65 @@
  */
 static const struct {
 	u32 msr;
-	u32 revision;
+	uint64_t revision;
 	u32 platform;
 	u32 data_lo;
 	u32 data_hi;
 	u32 mask_lo;
 	u32 mask_hi;
 } fam10_msr_default[] = {
-	{ TOP_MEM2, AMD_FAM10_ALL, AMD_PTYPE_ALL,
+	{ TOP_MEM2, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL,
 	  0x00000000, 0x00000000,
 	  0xFFFFFFFF, 0xFFFFFFFF },
 
-	{ SYSCFG, AMD_FAM10_ALL, AMD_PTYPE_ALL,
+	{ SYSCFG, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL,
 	  3 << 21, 0x00000000,
 	  3 << 21, 0x00000000 },	/* [MtrrTom2En]=1,[TOM2EnWB] = 1*/
 
-	{ HWCR, AMD_FAM10_ALL, AMD_PTYPE_ALL,
-	  1 << 4, 0x00000000,
-	  1 << 4, 0x00000000 },		/* [INVD_WBINVD]=1 */
+	{ MC1_CTL_MASK, AMD_OR_B2, AMD_PTYPE_ALL,
+	  1 << 18, 0x00000000,
+	  1 << 18, 0x00000000 },	/* Erratum 586: [DEIBP]=1 */
 
-	{ MC4_CTL_MASK, AMD_FAM10_ALL, AMD_PTYPE_ALL,
+	{ MC1_CTL_MASK, AMD_OR_B2, AMD_PTYPE_ALL,
+	  1 << 15, 0x00000000,
+	  1 << 15, 0x00000000 },	/* Erratum 593: [BSRP]=1 */
+
+	{ MC1_CTL_MASK, AMD_OR_C0, AMD_PTYPE_ALL,
+	  1 << 15, 0x00000000,
+	  1 << 15, 0x00000000 },	/* Erratum 739: [BSRP]=1 */
+
+	{ 0xc0011000, AMD_FAM15_ALL, AMD_PTYPE_ALL,
+	  1 << 16, 0x00000000,
+	  1 << 16, 0x00000000 },	/* Erratum 608: [bit 16]=1 */
+
+	{ 0xc0011000, AMD_OR_C0, AMD_PTYPE_ALL,
+	  1 << 15, 0x00000000,
+	  1 << 15, 0x00000000 },	/* Erratum 727: [bit 15]=1 */
+
+	{ MC4_CTL_MASK, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL,
 	  0xF << 19, 0x00000000,
 	  0xF << 19, 0x00000000 },	/* [RtryHt[0..3]]=1 */
 
+	{ MC4_CTL_MASK, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL,
+	  1 << 10, 0x00000000,
+	  1 << 10, 0x00000000 },	/* [GartTblWkEn]=1 */
+
 	{ DC_CFG, AMD_FAM10_ALL, AMD_PTYPE_SVR,
 	  0x00000000, 0x00000004,
-	  0x00000000, 0x0000000C },	/* [REQ_CTR] = 1 for Server */
+	  0x00000000, 0x0000000C },	/* Family 10h: [REQ_CTR] = 1 for Server */
 
 	{ DC_CFG, AMD_DR_Bx, AMD_PTYPE_SVR,
 	  0x00000000, 0x00000000,
 	  0x00000000, 0x00000C00 },	/* Erratum 326 */
 
-	{ NB_CFG, AMD_FAM10_ALL, AMD_PTYPE_DC | AMD_PTYPE_MC,
+	{ NB_CFG, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_DC | AMD_PTYPE_MC,
 	  0x00000000, 1 << 22,
 	  0x00000000, 1 << 22 },	/* [ApicInitIDLo]=1 */
 
+	{ NB_CFG, AMD_FAM15_ALL, AMD_PTYPE_DC | AMD_PTYPE_MC,
+	  1 << 23, 0x00000000,
+	  1 << 23, 0x00000000 },	/* Erratum 663: [bit 23]=1 */
+
 	{ BU_CFG2, AMD_DR_Bx, AMD_PTYPE_ALL,
 	  1 << 29, 0x00000000,
 	  1 << 29, 0x00000000 },	/* For Bx Smash1GPages=1 */
@@ -72,6 +97,14 @@ static const struct {
 	  0 << 1, 0x00000000,
 	  1 << 1, 0x00000000 },		/* IDX_MATCH_ALL=0 */
 
+	{ IC_CFG, AMD_OR_C0, AMD_PTYPE_ALL,
+	  0x00000000, 1 << (39-32),
+	  0x00000000, 1 << (39-32)},	/* C0 or above [DisLoopPredictor]=1 */
+
+	{ IC_CFG, AMD_OR_C0, AMD_PTYPE_ALL,
+	  0xf << 1, 0x00000000,
+	  0xf << 1, 0x00000000},	/* C0 or above [DisIcWayFilter]=0xf */
+
 	{ BU_CFG, AMD_DR_LT_B3, AMD_PTYPE_ALL,
 	  1 << 21, 0x00000000,
 	  1 << 21, 0x00000000 },	/* Erratum #254 DR B1 BU_CFG[21]=1 */
@@ -80,19 +113,51 @@ static const struct {
 	  1 << 23, 0x00000000,
 	  1 << 23, 0x00000000 },	/* Erratum #309 BU_CFG[23]=1 */
 
+	{ BU_CFG, AMD_FAM15_ALL, AMD_PTYPE_ALL,
+	  0 << 10, 0x00000000,
+	  1 << 10, 0x00000000 },	/* [DcacheAgressivePriority]=0 */
+
 	/* CPUID_EXT_FEATURES */
-	{ CPUIDFEATURES, AMD_FAM10_ALL, AMD_PTYPE_DC | AMD_PTYPE_MC,
+	{ CPUIDFEATURES, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_DC | AMD_PTYPE_MC,
 	  1 << 28, 0x00000000,
 	  1 << 28, 0x00000000 },	/* [HyperThreadFeatEn]=1 */
 
-	{ CPUIDFEATURES, AMD_FAM10_ALL, AMD_PTYPE_DC,
+	{ CPUIDFEATURES, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_DC,
 	  0x00000000, 1 << (33-32),
 	  0x00000000, 1 << (33-32) },	/* [ExtendedFeatEn]=1 */
 
+	{ DE_CFG, AMD_OR_B2, AMD_PTYPE_ALL,
+	  1 << 10, 0x00000000,
+	  1 << 10, 0x00000000 },	/* Bx [ResyncPredSingleDispDis]=1 */
+
 	{ BU_CFG2, AMD_DRBH_Cx, AMD_PTYPE_ALL,
 	  0x00000000, 1 << (35-32),
 	  0x00000000, 1 << (35-32) },	/* Erratum 343 (set to 0 after CAR, in post_cache_as_ram()/model_10xxx_init() )  */
 
+	{ BU_CFG3, AMD_OR_B2, AMD_PTYPE_ALL,
+	  0x00000000, 1 << (42-32),
+	  0x00000000, 1 << (42-32)},	/* Bx [PwcDisableWalkerSharing]=1 */
+
+	{ BU_CFG3, AMD_OR_C0, AMD_PTYPE_ALL,
+	  1 << 22, 0x00000000,
+	  1 << 22, 0x00000000},		/* C0 or above [PfcDoubleStride]=1 */
+
+	{ EX_CFG, AMD_OR_C0, AMD_PTYPE_ALL,
+	  0x00000000, 1 << (54-32),
+	  0x00000000, 1 << (54-32)},	/* C0 or above [LateSbzResync]=1 */
+
+	{ LS_CFG2, AMD_OR_C0, AMD_PTYPE_ALL,
+	  1 << 23, 0x00000000,
+	  1 << 23, 0x00000000},		/* C0 or above [DisScbThreshold]=1 */
+
+	{ LS_CFG2, AMD_OR_C0, AMD_PTYPE_ALL,
+	  1 << 14, 0x00000000,
+	  1 << 14, 0x00000000},		/* C0 or above [ForceSmcCheckFlowStDis]=1 */
+
+	{ LS_CFG2, AMD_OR_C0, AMD_PTYPE_ALL,
+	  1 << 12, 0x00000000,
+	  1 << 12, 0x00000000},		/* C0 or above [ForceBusLockDis]=1 */
+
 	{ OSVW_ID_Length, AMD_DR_Bx | AMD_DR_Cx | AMD_DR_Dx, AMD_PTYPE_ALL,
 	  0x00000004, 0x00000000,
 	  0x00000004, 0x00000000},	/* B0 or Above, OSVW_ID_Length is 0004h */
@@ -105,9 +170,45 @@ static const struct {
 	  0x00000000, 1 << (50-32),
 	  0x00000000, 1 << (50-32)},	/* D0 or Above, RdMmExtCfgQwEn*/
 
+	{ BU_CFG2, AMD_FAM15_ALL, AMD_PTYPE_ALL,
+	  0x00000000, 0x0 << (36-32),
+	  0x00000000, 0x3 << (36-32)},	/* [ThrottleNbInterface]=0 */
+
+	{ BU_CFG2, AMD_FAM15_ALL, AMD_PTYPE_ALL,
+	  1 << 10, 0x00000000,
+	  1 << 10, 0x00000000},		/* [VicResyncChkEn]=1 */
+
+	{ BU_CFG2, AMD_FAM15_ALL, AMD_PTYPE_ALL,
+	  1 << 11, 0x00000000,
+	  1 << 11, 0x00000000},		/* Erratum 503: [bit 11]=1 */
+
 	{ CPU_ID_EXT_FEATURES_MSR, AMD_DR_Dx, AMD_PTYPE_ALL,
 	  0x00000000, 1 << (51 - 32),
 	  0x00000000, 1 << (51 - 32)},	/* G34_PKG | C32_PKG | S1G4_PKG | ASB2_PKG */
+
+	{ CPU_ID_EXT_FEATURES_MSR, AMD_FAM15_ALL, AMD_PTYPE_ALL,
+	  0x00000000, 1 << (56 - 32),
+	  0x00000000, 1 << (56 - 32)},	/* [PerfCtrExtNB]=1 */
+
+	{ CPU_ID_EXT_FEATURES_MSR, AMD_FAM15_ALL, AMD_PTYPE_ALL,
+	  0x00000000, 1 << (55 - 32),
+	  0x00000000, 1 << (55 - 32)},	/* [PerfCtrExtCore]=1 */
+
+	{ IBS_OP_DATA3, AMD_FAM15_ALL, AMD_PTYPE_ALL,
+	  0 << 16, 0x00000000,
+	  1 << 16, 0x00000000},		/* [IbsDcMabHit]=0 */
+
+	{ MC4_MISC0, AMD_FAM15_ALL, AMD_PTYPE_ALL,
+	  0x00000000, 0x1 << (52-32),
+	  0x00000000, 0xf << (52-32)},	/* [LvtOffset]=1 */
+
+	{ MC4_MISC1, AMD_FAM15_ALL, AMD_PTYPE_ALL,
+	  0x00000000, 0x1 << (52-32),
+	  0x00000000, 0xf << (52-32)},	/* [LvtOffset]=1 */
+
+	{ MC4_MISC2, AMD_FAM15_ALL, AMD_PTYPE_ALL,
+	  0x00000000, 0x1 << (52-32),
+	  0x00000000, 0xf << (52-32)},	/* [LvtOffset]=1 */
 };
 
 
@@ -117,37 +218,46 @@ static const struct {
 static const struct {
 	u8  function;
 	u16 offset;
-	u32 revision;
+	uint64_t revision;
 	u32 platform;
 	u32 data;
 	u32 mask;
 } fam10_pci_default[] = {
 
 	/* Function 0 - HT Config */
+	{ 0, 0x68, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL,
+	  0x000e0000, 0x000e0000 },		/* [19:17] for 8bit APIC config */
+
+	{ 0, 0x68, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL,
+	  0x00400000, 0x00600000 },		/* [22:21] DsNpReqLmt = 10b */
 
-	{ 0, 0x68, AMD_FAM10_ALL, AMD_PTYPE_ALL,
-	  0x004E4800, 0x006E6800 },	/* [19:17] for 8bit APIC config,
-	  [14:13] BufPriRel = 2h [11] RspPassPW set,
-	  [22:21] DsNpReqLmt = 10b */
+	{ 0, 0x68, AMD_FAM10_LT_D, AMD_PTYPE_ALL,
+	  0x00004000, 0x00006000 },		/* [14:13] BufRelPri = 2h */
+
+	{ 0, 0x68, (AMD_FAM10_REV_D | AMD_FAM15_ALL), AMD_PTYPE_ALL,
+	  0x00002000, 0x00006000 },		/* [14:13] BufRelPri = 1h */
+
+	{ 0, 0x68, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL,
+	  0x00000800, 0x00000800 },		/* [11] RspPassPW = 1 */
 
 	/* Errata 281 Workaround */
 	{ 0, 0x68, (AMD_DR_B0 | AMD_DR_B1),
 	  AMD_PTYPE_SVR, 0x00200000, 0x00600000 },	/* [22:21] DsNpReqLmt0 = 01b */
 
-	{ 0, 0x84, AMD_FAM10_ALL, AMD_PTYPE_ALL,
+	{ 0, 0x84, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL,
 	  0x00002000, 0x00002000 },	/* [13] LdtStopTriEn = 1 */
 
-	{ 0, 0xA4, AMD_FAM10_ALL, AMD_PTYPE_ALL,
+	{ 0, 0xA4, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL,
 	  0x00002000, 0x00002000 },	/* [13] LdtStopTriEn = 1 */
 
-	{ 0, 0xC4, AMD_FAM10_ALL, AMD_PTYPE_ALL,
+	{ 0, 0xC4, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL,
 	  0x00002000, 0x00002000 },	/* [13] LdtStopTriEn = 1 */
 
-	{ 0, 0xE4, AMD_FAM10_ALL, AMD_PTYPE_ALL,
+	{ 0, 0xE4, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL,
 	  0x00002000, 0x00002000 },	/* [13] LdtStopTriEn = 1 */
 
 	/* Link Global Retry Control Register */
-	{ 0, 0x150, AMD_FAM10_ALL, AMD_PTYPE_ALL,
+	{ 0, 0x150, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL,
 	  0x00073900, 0x00073F00 },
 
 	/*  Errata 351
@@ -172,13 +282,39 @@ static const struct {
 	  0x00000000, 0x00000100 },
 	{ 0, 0x18C, AMD_FAM10_ALL, AMD_PTYPE_ALL,
 	  0x00000000, 0x00000100 },
-	{ 0, 0x170, AMD_FAM10_ALL, AMD_PTYPE_ALL,
-	  0x00000000, 0x00000100 },
 
 	/* Link Global Extended Control Register */
 	{ 0, 0x16C, AMD_FAM10_ALL, AMD_PTYPE_ALL,
 	  0x00000014, 0x0000003F },	/* [15:13] ForceFullT0 = 0b,
-								 * Set T0Time 14h per BKDG */
+					 * Set T0Time 14h per BKDG */
+
+	{ 0, 0x170, AMD_FAM15_ALL, AMD_PTYPE_ALL,
+	  0x00000100, 0x00000100 },
+	{ 0, 0x174, AMD_FAM15_ALL, AMD_PTYPE_ALL,
+	  0x00000100, 0x00000100 },
+	{ 0, 0x178, AMD_FAM15_ALL, AMD_PTYPE_ALL,
+	  0x00000100, 0x00000100 },
+	{ 0, 0x17C, AMD_FAM15_ALL, AMD_PTYPE_ALL,
+	  0x00000100, 0x00000100 },
+	{ 0, 0x180, AMD_FAM15_ALL, AMD_PTYPE_ALL,
+	  0x00000100, 0x00000100 },
+	{ 0, 0x184, AMD_FAM15_ALL, AMD_PTYPE_ALL,
+	  0x00000100, 0x00000100 },
+	{ 0, 0x188, AMD_FAM15_ALL, AMD_PTYPE_ALL,
+	  0x00000100, 0x00000100 },
+	{ 0, 0x18C, AMD_FAM15_ALL, AMD_PTYPE_ALL,
+	  0x00000100, 0x00000100 },
+
+	/* Link Global Extended Control Register */
+	{ 0, 0x16C, AMD_FAM15_ALL, AMD_PTYPE_ALL,
+	  0x00000014, 0x0000003F },	/* [15:13] ForceFullT0 = 111b,
+					 * Set T0Time 26h per BKDG */
+
+	{ 0, 0x16C, AMD_FAM15_ALL, AMD_PTYPE_ALL,
+	  0x7 << 13, 0x7 << 13 },	/* [15:13] ForceFullT0 = 7h */
+
+	{ 0, 0x16C, AMD_FAM15_ALL, AMD_PTYPE_ALL,
+	  0x26, 0x3f },	/* [5:0] T0Time = 26h */
 
 
 	/* Function 1 - Map Init */
@@ -205,10 +341,10 @@ static const struct {
 	/* Function 2 - DRAM Controller */
 
 	/* Function 3 - Misc. Control */
-	{ 3, 0x40, AMD_FAM10_ALL, AMD_PTYPE_ALL,
+	{ 3, 0x40, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL,
 	  0x00000100, 0x00000100 },	/* [8] MstrAbrtEn */
 
-	{ 3, 0x44, AMD_FAM10_ALL, AMD_PTYPE_ALL,
+	{ 3, 0x44, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL,
 	  0x4A30005C, 0x4A30005C },	/* [30] SyncOnDramAdrParErrEn = 1,
 					   [27] NbMcaToMstCpuEn = 1,
 					   [25] DisPciCfgCpuErrRsp = 1,
@@ -220,8 +356,12 @@ static const struct {
 					   [2] SyncOnUcEccEn = 1 */
 
 	/* XBAR buffer settings */
-	{ 3, 0x6C, AMD_FAM10_ALL, AMD_PTYPE_ALL,
-	  0x00018052, 0x700780F7 },
+	{ 3, 0x6c, AMD_FAM10_ALL, AMD_PTYPE_ALL,
+	  0x00018052, 0x700780f7 },
+
+	/* XBAR buffer settings */
+	{ 3, 0x6c, AMD_FAM15_ALL, AMD_PTYPE_ALL,
+	  0x10010052, 0x700700f7 },
 
 	/* Errata 281 Workaround */
 	{ 3, 0x6C, ( AMD_DR_B0 | AMD_DR_B1),
@@ -233,12 +373,18 @@ static const struct {
 	{ 3, 0x70, AMD_FAM10_ALL, AMD_PTYPE_ALL,
 	  0x00041153, 0x777777F7 },
 
+	{ 3, 0x70, AMD_FAM15_ALL, AMD_PTYPE_ALL,
+	  0x10171155, 0x777777f7 },
+
 	{ 3, 0x70, AMD_FAM10_ALL, AMD_PTYPE_UMA,
 	  0x61221151, 0x777777F7 },
 
 	{ 3, 0x74, AMD_FAM10_ALL, AMD_PTYPE_UMA,
 	  0x00080101, 0x000F7777 },
 
+	{ 3, 0x74, AMD_FAM15_ALL, AMD_PTYPE_ALL,
+	  0x00172111, 0x77ff7777 },
+
 	{ 3, 0x7C, AMD_FAM10_ALL, AMD_PTYPE_ALL,
 	  0x00090914, 0x707FFF1F },
 
@@ -246,12 +392,18 @@ static const struct {
 	{ 3, 0x7C, ( AMD_DR_B0 | AMD_DR_B1),
 	 AMD_PTYPE_SVR, 0x00144514, 0x707FFF1F },
 
+	{ 3, 0x7C, AMD_FAM15_ALL, AMD_PTYPE_ALL,
+	  0x040d0f16, 0x07ffff1f },
+
 	{ 3, 0x7C, AMD_FAM10_ALL, AMD_PTYPE_UMA,
 	  0x00070814, 0x007FFF1F },
 
 	{ 3, 0x140, AMD_FAM10_ALL, AMD_PTYPE_ALL,
 	  0x00800756, 0x00F3FFFF },
 
+	{ 3, 0x140, AMD_FAM15_ALL, AMD_PTYPE_ALL,
+	  0x00a11755, 0x00f3ffff },
+
 	{ 3, 0x140, AMD_FAM10_ALL, AMD_PTYPE_UMA,
 	  0x00C37756, 0x00F3FFFF },
 
@@ -263,6 +415,9 @@ static const struct {
 	 AMD_PTYPE_SVR, 0x00000001, 0x0000000F },
 	 	/* [3:0] RspTok = 0001b */
 
+	{ 3, 0x144, AMD_FAM15_ALL, AMD_PTYPE_ALL,
+	  0x00000028, 0x000000ff },
+
 	{ 3, 0x148, AMD_FAM10_ALL, AMD_PTYPE_UMA,
 	  0x8000052A, 0xD5FFFFFF },
 
@@ -270,41 +425,53 @@ static const struct {
 	{ 3, 0x80, AMD_FAM10_ALL, AMD_PTYPE_ALL,
 	  0xE6002200, 0xFFFFFFFF },
 
+	/* ACPI Power State Control Reg1 */
+	{ 3, 0x80, AMD_FAM15_ALL, AMD_PTYPE_ALL,
+	  0xe20be200, 0xefefef00 },
+
 	/* ACPI Power State Control Reg2 */
 	{ 3, 0x84, AMD_FAM10_ALL, AMD_PTYPE_ALL,
 	  0xA0E641E6, 0xFFFFFFFF },
 
+	/* ACPI Power State Control Reg2 */
+	{ 3, 0x84, AMD_FAM15_ALL, AMD_PTYPE_ALL,
+	  0x01e200e2, 0xefef00ef },
+
 	{ 3, 0xA0, AMD_FAM10_ALL, AMD_PTYPE_MOB | AMD_PTYPE_DSK,
 	  0x00000080, 0x00000080 },	/* [7] PSIVidEnable */
 
 	{ 3, 0xA0, AMD_DR_Bx, AMD_PTYPE_ALL,
 	  0x00002800, 0x000003800 },	/* [13:11] PllLockTime = 5 */
 
-	{ 3, 0xA0, (AMD_FAM10_ALL & ~(AMD_DR_Bx)), AMD_PTYPE_ALL,
+	{ 3, 0xA0, ((AMD_FAM10_ALL | AMD_FAM15_ALL) & ~(AMD_DR_Bx)), AMD_PTYPE_ALL,
 	  0x00000800, 0x000003800 },	/* [13:11] PllLockTime = 1 */
 
 	/* Reported Temp Control Register */
-	{ 3, 0xA4, AMD_FAM10_ALL, AMD_PTYPE_ALL,
+	{ 3, 0xA4, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL,
 	  0x00000080, 0x00000080 },	/* [7] TempSlewDnEn = 1 */
 
 	/* Clock Power/Timing Control 0 Register */
-	{ 3, 0xD4, AMD_FAM10_ALL, AMD_PTYPE_ALL,
+	{ 3, 0xD4, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL,
 	  0xC0000F00, 0xF0000F00 },	/*  [31] NbClkDivApplyAll = 1,
 		[30:28] NbClkDiv = 100b,[11:8] ClkRampHystSel = 1111b */
 
 	/* Clock Power/Timing Control 1 Register */
+	{ 3, 0xD8, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL,
+	  0x03000010, 0x0F000070 },	/* [6:4] VSRampTime = 1,
+					 * [27:24] ReConDel = 3 */
+
+	/* Clock Power/Timing Control 1 Register */
 	{ 3, 0xD8, AMD_FAM10_ALL, AMD_PTYPE_ALL,
-	  0x03000016, 0x0F000077 },	/* [6:4] VSRampTime = 1,
-		[2:0] VSSlamTime = 6, [27:24] ReConDel = 3 */
+	  0x00000006, 0x00000007 },	/* [2:0] VSSlamTime = 6 */
 
 
 	/* Clock Power/Timing Control 2 Register */
-	{ 3, 0xDC, AMD_FAM10_ALL, AMD_PTYPE_ALL,
+	{ 3, 0xDC, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL,
 	  0x00005000, 0x00007000 },	/* [14:12] NbsynPtrAdj = 5 */
 
 
 	/* Extended NB MCA Config Register */
-	{ 3, 0x180, AMD_FAM10_ALL, AMD_PTYPE_ALL,
+	{ 3, 0x180, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL,
 	  0x007003E2, 0x007003E2 },	/* [22:20] = SyncFloodOn_Err = 7,
 					   [9] SyncOnUncNbAryEn = 1 ,
 					   [8] SyncOnProtEn = 1,
@@ -319,12 +486,17 @@ static const struct {
 	  0x00400000, 0x00400000 },
 
 	/* L3 Control Register */
-	{ 3, 0x1B8, AMD_FAM10_ALL, AMD_PTYPE_ALL,
+	{ 3, 0x1b8, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL,
 	  0x00001000, 0x00001000 },	/* [12] = L3PrivReplEn */
 
 	/* IBS Control Register */
-	{ 3, 0x1CC, AMD_FAM10_ALL, AMD_PTYPE_ALL,
+	{ 3, 0x1cc, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL,
 	  0x00000100, 0x00000100 },	/* [8] = LvtOffsetVal */
+
+	/* Erratum 619 - Family 15h Bx
+	 * System software should set F5x88[14] to 1b. */
+	{ 5, 0x88, AMD_OR_B2, AMD_PTYPE_ALL,
+	  1 << 14, 1 << 14 },
 };
 
 
@@ -333,7 +505,7 @@ static const struct {
  */
 static const struct {
 	u16 htreg;	/* HT Phy Register index */
-	u32 revision;
+	uint64_t revision;
 	u32 platform;
 	u32 linktype;
 	u32 data;
@@ -442,38 +614,38 @@ static const struct {
 	{ 0x530A, AMD_DR_ALL, AMD_PTYPE_ALL, HTPHY_LINKTYPE_ALL,
 	  0x00004400, 0x00006400 },	/* HT_PHY_DLL_REG */
 
-	{ 0xCF, AMD_FAM10_ALL, AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT3,
+	{ 0xCF, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT3,
 	  0x00000000, 0x000000FF },	/* Provide clear setting for logical
 					   completeness */
 
-	{ 0xDF, AMD_FAM10_ALL, AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT3,
+	{ 0xDF, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT3,
 	  0x00000000, 0x000000FF },	/* Provide clear setting for logical
 					   completeness */
 
-	{ 0xCF, AMD_FAM10_ALL, AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT1,
+	{ 0xCF, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT1,
 	  0x0000006D, 0x000000FF },	/* HT_PHY_HT1_FIFO_PTR_OPT_VALUE */
 
-	{ 0xDF, AMD_FAM10_ALL, AMD_PTYPE_ALL,  HTPHY_LINKTYPE_HT1,
+	{ 0xDF, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL,  HTPHY_LINKTYPE_HT1,
 	  0x0000006D, 0x000000FF }, 	/* HT_PHY_HT1_FIFO_PTR_OPT_VALUE */
 
 	/* Link Phy Receiver Loop Filter Registers */
-	{ 0xD1, AMD_FAM10_ALL, AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT3,
+	{ 0xD1, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT3,
 	  0x08040000, 0x3FFFC000 },	/* [29:22] LfcMax = 20h,
 					   [21:14] LfcMin = 10h */
 
-	{ 0xC1, AMD_FAM10_ALL, AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT3,
+	{ 0xC1, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT3,
 	  0x08040000, 0x3FFFC000 },	/* [29:22] LfcMax = 20h,
 					   [21:14] LfcMin = 10h */
 
-	{ 0xD1, AMD_FAM10_ALL, AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT1,
+	{ 0xD1, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT1,
 	  0x04020000, 0x3FFFC000 },	/* [29:22] LfcMax = 10h,
 					   [21:14] LfcMin = 08h */
 
-	{ 0xC1, AMD_FAM10_ALL, AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT1,
+	{ 0xC1, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT1,
 	  0x04020000, 0x3FFFC000 },	/* [29:22] LfcMax = 10h,
 					   [21:14] LfcMin = 08h */
 
-	{ 0xC0, AMD_FAM10_ALL, AMD_PTYPE_ALL, HTPHY_LINKTYPE_ALL,
+	{ 0xC0, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL, HTPHY_LINKTYPE_ALL,
 	  0x40040000, 0xe01F0000 },	/* [31:29] RttCtl = 02h,
 								   [20:16] RttIndex = 04h */
 };
diff --git a/src/cpu/amd/family_10h-family_15h/fidvid.c b/src/cpu/amd/family_10h-family_15h/fidvid.c
index 86e3179..e8e0818 100644
--- a/src/cpu/amd/family_10h-family_15h/fidvid.c
+++ b/src/cpu/amd/family_10h-family_15h/fidvid.c
@@ -2,6 +2,7 @@
  * This file is part of the coreboot project.
  *
  * Copyright (C) 2007 Advanced Micro Devices, Inc.
+ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -43,7 +44,7 @@ Fam10 Bios and Kernel Development Guide #31116, rev 3.48, April 22, 2010
 
 3.-  2.4.2.7 dualPlaneOnly(dev)
 
-4.-  2.4.2.8 applyBoostFIDOffset(dev)
+4.-  2.4.2.8 applyBoostFIDOffset(dev, nodeid)
 
 5.-  enableNbPState1(dev)
 
@@ -142,25 +143,33 @@ static void enable_fid_change(u8 fid)
 	}
 }
 
-static void applyBoostFIDOffset(  device_t dev ) {
-  // BKDG 2.4.2.8
-  // revision E only, but E is apparently not supported yet, therefore untested
-  if ((cpuid_edx(0x80000007) & CPB_MASK)
-      &&  ((cpuid_ecx(0x80000008) & NC_MASK) ==5) ) {
-      u32 core =  get_node_core_id_x().coreid;
-      u32 asymetricBoostThisCore = ((pci_read_config32(dev, 0x10C) >> (core*2))) & 3;
-      msr_t msr =  rdmsr(PS_REG_BASE);
-      u32 cpuFid = msr.lo & PS_CPU_FID_MASK;
-      cpuFid = cpuFid + asymetricBoostThisCore;
-      msr.lo &=   ~PS_CPU_FID_MASK;
-      msr.lo |= cpuFid ;
-      wrmsr(PS_REG_BASE , msr);
-
-  }
+static void applyBoostFIDOffset(device_t dev, uint32_t nodeid) {
+	// BKDG 2.4.2.8
+	// Fam10h revision E only, but E is apparently not supported yet, therefore untested
+	if ((cpuid_edx(0x80000007) & CPB_MASK)
+		&&  ((cpuid_ecx(0x80000008) & NC_MASK) == 5) ) {
+		u32 core =  get_node_core_id_x().coreid;
+		u32 asymetricBoostThisCore = ((pci_read_config32(dev, 0x10C) >> (core*2))) & 3;
+		msr_t msr =  rdmsr(PS_REG_BASE);
+		u32 cpuFid = msr.lo & PS_CPU_FID_MASK;
+		cpuFid = cpuFid + asymetricBoostThisCore;
+		msr.lo &=   ~PS_CPU_FID_MASK;
+		msr.lo |= cpuFid ;
+		wrmsr(PS_REG_BASE , msr);
+	} else if (is_fam15h()) {
+		uint32_t dword = pci_read_config32(NODE_PCI(nodeid, 4), 0x15c);
+		uint8_t boost_count = (dword >> 2) & 0x7;
+		if (boost_count > 0) {
+			/* Enable boost */
+			dword &= ~0x3;
+			dword |= 0x1;
+			pci_write_config32(NODE_PCI(nodeid, 4), 0x15c, dword);
+		}
+	}
 }
 
 static void enableNbPState1( device_t dev ) {
-  u32 cpuRev =  mctGetLogicalCPUID(0xFF);
+  uint64_t cpuRev =  mctGetLogicalCPUID(0xFF);
   if (cpuRev & AMD_FAM10_C3) {
     u32 nbPState = (pci_read_config32(dev, 0x1F0) & NB_PSTATE_MASK);
     if ( nbPState){
@@ -202,7 +211,7 @@ static u8 setPStateMaxVal( device_t dev ) {
 static void dualPlaneOnly(  device_t dev ) {
   // BKDG 2.4.2.7
 
-  u32 cpuRev =  mctGetLogicalCPUID(0xFF);
+  uint64_t cpuRev =  mctGetLogicalCPUID(0xFF);
   if ((mctGetProcessorPackageType() ==  AMD_PKGTYPE_AM3_2r2)
       && (cpuRev & AMD_DR_Cx)) { // should be rev C or rev E but there's no constant for E
     if ( (pci_read_config32(dev, 0x1FC) & DUAL_PLANE_ONLY_MASK)
@@ -282,12 +291,16 @@ static void recalculateVsSlamTimeSettingOnCorePre(device_t dev)
 	 */
 
 	/* Determine if this is a PVI or SVI system */
-	dtemp = pci_read_config32(dev, 0xA0);
-
-	if (dtemp & PVI_MODE)
-		pviModeFlag = 1;
-	else
+	if (is_fam15h()) {
 		pviModeFlag = 0;
+	} else {
+		dtemp = pci_read_config32(dev, 0xa0);
+
+		if (dtemp & PVI_MODE)
+			pviModeFlag = 1;
+		else
+			pviModeFlag = 0;
+	}
 
 	/* Get P0's voltage */
         /* MSRC001_00[68:64] are not programmed yet when called from
@@ -514,59 +527,67 @@ static void config_nb_syn_ptr_adj(device_t dev, u32 cpuRev) {
 }
 
 static void config_acpi_pwr_state_ctrl_regs(device_t dev, u32 cpuRev, u8 procPkg) {
-                /* step 1, chapter 2.4.2.6 of AMD Fam 10 BKDG #31116 Rev 3.48 22.4.2010 */
-        u32 dword;
-	u32 c1= 1;
-        if (cpuRev & (AMD_DR_Bx)) {
-            // will coreboot ever enable cache scrubbing ?
-            // if it does, will it be enough to check the current state
-            // or should we configure for what we'll set up later ?
-            dword = pci_read_config32(dev, 0x58);
-            u32 scrubbingCache = dword &
-	                        ( (0x1F << 16) // DCacheScrub
-		 	          | (0x1F << 8) ); // L2Scrub
- 	    if (scrubbingCache) {
-	        c1 = 0x80;
-	    } else {
-	        c1 = 0xA0;
-	    }
-	} else { // rev C or later
-	    // same doubt as cache scrubbing: ok to check current state ?
-            dword = pci_read_config32(dev, 0xDC);
-            u32 cacheFlushOnHalt = dword & (7 << 16);
-            if (!cacheFlushOnHalt) {
-       	       c1 = 0x80;
-       	    }
-       	}
-       	dword = (c1 << 24) | (0xE641E6);
-	pci_write_config32(dev, 0x84, dword);
-
-
-        /* FIXME: BKDG Table 100 says if the link is at a Gen1
-frequency and the chipset does not support a 10us minimum LDTSTOP
-assertion time, then { If ASB2 && SVI then smaf001 = F6h else
-smaf001=87h. } else ...  I hardly know what it means or how to check
-it from here, so I bluntly assume it is false and code here the else,
-which is easier  */
-
-        u32 smaf001 = 0xE6;
-        if (cpuRev & AMD_DR_Bx ) {
-	    smaf001 = 0xA6;
-        } else {
-            #if CONFIG_SVI_HIGH_FREQ
-                if (cpuRev & (AMD_RB_C3 | AMD_DA_C3)) {
-		       smaf001 = 0xF6;
-                }
-            #endif
-        }
-        u32 fidvidChange = 0;
-        if (((cpuRev & AMD_DA_Cx) && (procPkg & AMD_PKGTYPE_S1gX))
-		    || (cpuRev & AMD_RB_C3) ) {
-                       fidvidChange=0x0B;
-        }
-	dword = (0xE6 << 24) | (fidvidChange << 16)
-                        | (smaf001 << 8) | 0x81;
-	pci_write_config32(dev, 0x80, dword);
+	if (is_fam15h()) {
+		/* Family 15h BKDG Rev. 3.14 D18F3x80 recommended settings */
+		pci_write_config32(dev, 0x80, 0xe20be281);
+
+		/* Family 15h BKDG Rev. 3.14 D18F3x84 recommended settings */
+		pci_write_config32(dev, 0x84, 0x01e200e2);
+	} else {
+		/* step 1, chapter 2.4.2.6 of AMD Fam 10 BKDG #31116 Rev 3.48 22.4.2010 */
+		u32 dword;
+		u32 c1= 1;
+		if (cpuRev & (AMD_DR_Bx)) {
+			// will coreboot ever enable cache scrubbing ?
+			// if it does, will it be enough to check the current state
+			// or should we configure for what we'll set up later ?
+			dword = pci_read_config32(dev, 0x58);
+			u32 scrubbingCache = dword &
+						( (0x1F << 16) // DCacheScrub
+						| (0x1F << 8) ); // L2Scrub
+			if (scrubbingCache) {
+				c1 = 0x80;
+			} else {
+				c1 = 0xA0;
+			}
+		} else { // rev C or later
+			// same doubt as cache scrubbing: ok to check current state ?
+			dword = pci_read_config32(dev, 0xDC);
+			u32 cacheFlushOnHalt = dword & (7 << 16);
+			if (!cacheFlushOnHalt) {
+				c1 = 0x80;
+			}
+		}
+		dword = (c1 << 24) | (0xE641E6);
+		pci_write_config32(dev, 0x84, dword);
+
+		/* FIXME: BKDG Table 100 says if the link is at a Gen1
+		* frequency and the chipset does not support a 10us minimum LDTSTOP
+		* assertion time, then { If ASB2 && SVI then smaf001 = F6h else
+		* smaf001=87h. } else ...  I hardly know what it means or how to check
+		* it from here, so I bluntly assume it is false and code here the else,
+		* which is easier
+		*/
+
+		u32 smaf001 = 0xE6;
+		if (cpuRev & AMD_DR_Bx ) {
+			smaf001 = 0xA6;
+		} else {
+		#if CONFIG_SVI_HIGH_FREQ
+			if (cpuRev & (AMD_RB_C3 | AMD_DA_C3)) {
+				smaf001 = 0xF6;
+			}
+		#endif
+		}
+		u32 fidvidChange = 0;
+		if (((cpuRev & AMD_DA_Cx) && (procPkg & AMD_PKGTYPE_S1gX))
+			|| (cpuRev & AMD_RB_C3) ) {
+				fidvidChange=0x0B;
+		}
+		dword = (0xE6 << 24) | (fidvidChange << 16)
+				| (smaf001 << 8) | 0x81;
+		pci_write_config32(dev, 0x80, dword);
+	}
 }
 
 static void prep_fid_change(void)
@@ -583,7 +604,7 @@ static void prep_fid_change(void)
 	for (i = 0; i < nodes; i++) {
 		printk(BIOS_DEBUG, "Prep FID/VID Node:%02x\n", i);
 		dev = NODE_PCI(i, 3);
-                u32 cpuRev = mctGetLogicalCPUID(0xFF) ;
+                uint64_t cpuRev = mctGetLogicalCPUID(0xFF) ;
 	        u8 procPkg =  mctGetProcessorPackageType();
 
 		setVSRamp(dev);
@@ -611,7 +632,7 @@ static void prep_fid_change(void)
 	}
 }
 
-static void waitCurrentPstate(u32 target_pstate){
+static void waitCurrentPstate(u32 target_pstate) {
   msr_t initial_msr = rdmsr(TSC_MSR);
   msr_t pstate_msr = rdmsr(CUR_PSTATE_MSR);
   msr_t tsc_msr;
@@ -644,7 +665,7 @@ static void waitCurrentPstate(u32 target_pstate){
 
   if (pstate_msr.lo != target_pstate) {
     msr_t limit_msr = rdmsr(0xc0010061);
-    printk(BIOS_ERR, "*** Time out waiting for P-state %01x. Current P-state %01x P-state current limit MSRC001_0061=%02x\n", target_pstate, pstate_msr.lo, limit_msr.lo);
+    printk(BIOS_ERR, "*** Time out waiting for P-state %01x. Current P-state %01x P-state current limit MSRC001_0061=%08x %08x\n", target_pstate, pstate_msr.lo, limit_msr.hi, limit_msr.lo);
 
     do { // should we just go on instead ?
       pstate_msr = rdmsr(CUR_PSTATE_MSR);
@@ -654,6 +675,7 @@ static void waitCurrentPstate(u32 target_pstate){
 
 static void set_pstate(u32 nonBoostedPState) {
   	msr_t msr;
+  	uint8_t skip_wait;
 
 	// Transition P0 for calling core.
 	msr = rdmsr(0xC0010062);
@@ -661,12 +683,21 @@ static void set_pstate(u32 nonBoostedPState) {
 	msr.lo = nonBoostedPState;
 	wrmsr(0xC0010062, msr);
 
-	/* Wait for P0 to set. */
-        waitCurrentPstate(nonBoostedPState);
-}
-
-
+	if (is_fam15h()) {
+		/* Do not wait for the first (even) set of cores to transition on Family 15h systems */
+		if ((cpuid_ebx(0x00000001) & 0x01000000))
+			skip_wait = 0;
+		else
+			skip_wait = 1;
+	} else {
+		skip_wait = 0;
+	}
 
+	if (!skip_wait) {
+		/* Wait for core to transition to P0 */
+        	waitCurrentPstate(nonBoostedPState);
+	}
+}
 
 static void UpdateSinglePlaneNbVid(void)
 {
@@ -756,11 +787,14 @@ static u32 needs_NB_COF_VID_update(void)
 	u8 nodes;
 	u8 i;
 
+	if (is_fam15h())
+		return 0;
+
 	/* If any node has nb_cof_vid_update set all nodes need an update. */
 	nodes = get_nodes();
 	nb_cof_vid_update = 0;
 	for (i = 0; i < nodes; i++) {
-                u32 cpuRev = mctGetLogicalCPUID(i) ;
+                uint64_t cpuRev = mctGetLogicalCPUID(i);
                 u32 nbCofVidUpdateDefined = (cpuRev & (AMD_FAM10_LT_D));
 		if (nbCofVidUpdateDefined
                     && (pci_read_config32(NODE_PCI(i, 3), 0x1FC)
@@ -784,9 +818,11 @@ static u32 init_fidvid_core(u32 nodeid, u32 coreid)
 	/* Steps 1-6 of BIOS NB COF and VID Configuration
 	 * for SVI and Single-Plane PVI Systems. BKDG 2.4.2.9 #31116 rev 3.48
 	 */
-
 	dev = NODE_PCI(nodeid, 3);
-	pvimode = pci_read_config32(dev, PW_CTL_MISC) & PVI_MODE;
+	if (is_fam15h())
+		pvimode = 0;
+	else
+		pvimode = pci_read_config32(dev, PW_CTL_MISC) & PVI_MODE;
 	reg1fc = pci_read_config32(dev, 0x1FC);
 
 	if (nb_cof_vid_update) {
@@ -798,7 +834,7 @@ static u32 init_fidvid_core(u32 nodeid, u32 coreid)
 			fid_max = fid_max +  ((reg1fc &  DUAL_PLANE_NB_FID_OFF_MASK ) >>  DUAL_PLANE_NB_FID_SHIFT );
 		}
 		/* write newNbVid to P-state Reg's NbVid always if NbVidUpdatedAll=1 */
-		fixPsNbVidBeforeWR(vid_max, coreid,dev,pvimode);
+		fixPsNbVidBeforeWR(vid_max, coreid, dev, pvimode);
 
 		/* fid setup is handled by the BSP at the end. */
 
@@ -818,7 +854,7 @@ static void init_fidvid_ap(u32 apicid, u32 nodeid, u32 coreid)
 
 	printk(BIOS_DEBUG, "FIDVID on AP: %02x\n", apicid);
 
-        send = init_fidvid_core(nodeid,coreid);
+        send = init_fidvid_core(nodeid, coreid);
 	send |= (apicid << 24);	// ap apicid
 
 	// Send signal to BSP about this AP max fid
@@ -860,7 +896,8 @@ static void init_fidvid_bsp_stage1(u32 ap_apicid, void *gp)
 	while (--loop > 0) {
 		if (lapic_remote_read(ap_apicid, LAPIC_MSG_REG, &readback) != 0)
 			continue;
-		if ((readback & 0x3f) == F10_APSTATE_RESET) {
+		if (((readback & 0x3f) == F10_APSTATE_RESET)
+			|| (is_fam15h() && ((readback & 0x3f) == F10_APSTATE_ASLEEP))) {
 			timeout = 0;
 			break;	/* target ap is in stage 1 */
 		}
@@ -948,7 +985,10 @@ static void init_fidvid_stage2(u32 apicid, u32 nodeid)
 	/* If any node has nb_cof_vid_update set all nodes need an update. */
 
 	dev = NODE_PCI(nodeid, 3);
-	pvimode = (pci_read_config32(dev, 0xA0) >> 8) & 1;
+	if (is_fam15h())
+		pvimode = 0;
+	else
+		pvimode = (pci_read_config32(dev, 0xA0) >> 8) & 1;
 	reg1fc = pci_read_config32(dev, 0x1FC);
 	nbvid = (reg1fc >> 7) & 0x7F;
 	NbVidUpdateAll = (reg1fc >> 1) & 1;
@@ -969,15 +1009,17 @@ static void init_fidvid_stage2(u32 apicid, u32 nodeid)
 	pci_write_config32(dev, 0xA0, dtemp);
 
         dualPlaneOnly(dev);
-        applyBoostFIDOffset(dev);
+        applyBoostFIDOffset(dev, nodeid);
         enableNbPState1(dev);
 
 	finalPstateChange();
 
-	/* Set TSC to tick at the P0 ndfid rate */
-	msr = rdmsr(HWCR);
-	msr.lo |= 1 << 24;
-	wrmsr(HWCR, msr);
+	if (!is_fam15h()) {
+		/* Set TSC to tick at the P0 ndfid rate */
+		msr = rdmsr(HWCR);
+		msr.lo |= 1 << 24;
+		wrmsr(HWCR, msr);
+	}
 }
 
 
@@ -1011,8 +1053,7 @@ static int init_fidvid_bsp(u32 bsp_apicid, u32 nodes)
 	/* Steps 1-6 of BIOS NB COF and VID Configuration
 	 * for SVI and Single-Plane PVI Systems.
 	 */
-
-	fv.common_fid = init_fidvid_core(0,0);
+	fv.common_fid = init_fidvid_core(0, 0);
 
 	print_debug_fv("BSP fid = ", fv.common_fid);
 
diff --git a/src/cpu/amd/family_10h-family_15h/init_cpus.c b/src/cpu/amd/family_10h-family_15h/init_cpus.c
index 8de6d25..aced850 100644
--- a/src/cpu/amd/family_10h-family_15h/init_cpus.c
+++ b/src/cpu/amd/family_10h-family_15h/init_cpus.c
@@ -30,9 +30,12 @@
 #include <northbridge/amd/amdfam10/raminit_amdmct.c>
 #include <reset.h>
 
+#if IS_ENABLED(CONFIG_SET_FIDVID)
 static void prep_fid_change(void);
 static void init_fidvid_stage2(u32 apicid, u32 nodeid);
-void cpuSetAMDMSR(void);
+#endif
+
+void cpuSetAMDMSR(uint8_t node_id);
 
 #if CONFIG_PCI_IO_CFG_EXT
 static void set_EnableCf8ExtCfg(void)
@@ -51,43 +54,38 @@ static void set_EnableCf8ExtCfg(void) { }
 
 typedef void (*process_ap_t) (u32 apicid, void *gp);
 
-//core_range = 0 : all cores
-//core range = 1 : core 0 only
-//core range = 2 : cores other than core0
+uint32_t get_boot_apic_id(uint8_t node, uint32_t core) {
+	uint32_t ap_apicid;
 
-static void for_each_ap(u32 bsp_apicid, u32 core_range, process_ap_t process_ap,
-			void *gp)
-{
-	// here assume the OS don't change our apicid
-	u32 ap_apicid;
+	uint32_t nb_cfg_54;
+	uint32_t siblings;
+	uint32_t cores_found;
 
-	u32 nodes;
-	u32 siblings;
-	u32 disable_siblings;
-	u32 cores_found;
-	u32 nb_cfg_54;
-	int i, j;
-	u32 ApicIdCoreIdSize;
+	uint8_t fam15h = 0;
 	uint8_t rev_gte_d = 0;
 	uint8_t dual_node = 0;
 	uint32_t f3xe8;
+	uint32_t family;
+	uint32_t model;
 
-	/* get_nodes define in ht_wrapper.c */
-	nodes = get_nodes();
-
-	if (!CONFIG_LOGICAL_CPUS ||
-	    read_option(multi_core, 0) != 0) {	// 0 means multi core
-		disable_siblings = 1;
-	} else {
-		disable_siblings = 0;
-	}
+	uint32_t ApicIdCoreIdSize;
 
 	/* Assume that all node are same stepping, otherwise we can use use
 	   nb_cfg_54 from bsp for all nodes */
 	nb_cfg_54 = read_nb_cfg_54();
 	f3xe8 = pci_read_config32(NODE_PCI(0, 3), 0xe8);
 
-	if (cpuid_eax(0x80000001) >= 0x8)
+	family = model = cpuid_eax(0x80000001);
+	model = ((model & 0xf0000) >> 12) | ((model & 0xf0) >> 4);
+	family = ((family & 0xf00000) >> 16) | ((family & 0xf00) >> 8);
+
+	if (family >= 0x6f) {
+		/* Family 15h or later */
+		fam15h = 1;
+		nb_cfg_54 = 1;
+	}
+
+	if ((model >= 0x8) || fam15h)
 		/* Revision D or later */
 		rev_gte_d = 1;
 
@@ -103,10 +101,63 @@ static void for_each_ap(u32 bsp_apicid, u32 core_range, process_ap_t process_ap,
 		siblings = 3;	//quad core
 	}
 
+	cores_found = get_core_num_in_bsp(node);
+	if (siblings > cores_found)
+		siblings = cores_found;
+
+	if (dual_node) {
+		ap_apicid = 0;
+		if (fam15h) {
+			ap_apicid |= ((node >> 1) & 0x3) << 5;			/* Node ID */
+			ap_apicid |= ((node & 0x1) * (siblings + 1)) + core;	/* Core ID */
+		} else {
+			if (nb_cfg_54) {
+				ap_apicid |= ((node >> 1) & 0x3) << 4;			/* Node ID */
+				ap_apicid |= ((node & 0x1) * (siblings + 1)) + core;	/* Core ID */
+			} else {
+				ap_apicid |= node & 0x3;					/* Node ID */
+				ap_apicid |= (((node & 0x1) * (siblings + 1)) + core) << 4;	/* Core ID */
+			}
+		}
+	} else {
+		if (fam15h) {
+			ap_apicid = (node * (siblings + 1)) + core;
+		} else {
+			ap_apicid = node * (nb_cfg_54 ? (siblings + 1) : 1) +
+					core * (nb_cfg_54 ? 1 : 64);
+		}
+	}
+
+	return ap_apicid;
+}
+
+//core_range = 0 : all cores
+//core range = 1 : core 0 only
+//core range = 2 : cores other than core0
+
+static void for_each_ap(u32 bsp_apicid, u32 core_range, process_ap_t process_ap,
+			void *gp)
+{
+	// here assume the OS don't change our apicid
+	u32 ap_apicid;
+
+	u32 nodes;
+	u32 disable_siblings;
+	u32 cores_found;
+	int i, j;
+
+	/* get_nodes define in ht_wrapper.c */
+	nodes = get_nodes();
+
+	if (!CONFIG_LOGICAL_CPUS ||
+	    read_option(multi_core, 0) != 0) {	// 0 means multi core
+		disable_siblings = 1;
+	} else {
+		disable_siblings = 0;
+	}
+
 	for (i = 0; i < nodes; i++) {
 		cores_found = get_core_num_in_bsp(i);
-		if (siblings > cores_found)
-			siblings = cores_found;
 
 		u32 jstart, jend;
 
@@ -123,21 +174,7 @@ static void for_each_ap(u32 bsp_apicid, u32 core_range, process_ap_t process_ap,
 		}
 
 		for (j = jstart; j <= jend; j++) {
-			if (dual_node) {
-				ap_apicid = 0;
-				if (nb_cfg_54) {
-					ap_apicid |= ((i >> 1) & 0x3) << 4;			/* Node ID */
-					ap_apicid |= ((i & 0x1) * (siblings + 1)) + j;		/* Core ID */
-				} else {
-					ap_apicid |= i & 0x3;					/* Node ID */
-					ap_apicid |= (((i & 0x1) * (siblings + 1)) + j) << 4;	/* Core ID */
-				}
-			} else {
-				ap_apicid =
-				i * (nb_cfg_54 ? (siblings + 1) : 1) +
-				j * (nb_cfg_54 ? 1 : 64);
-			}
-
+			ap_apicid = get_boot_apic_id(i, j);
 
 #if CONFIG_ENABLE_APIC_EXT_ID && (CONFIG_APIC_ID_OFFSET > 0)
 #if !CONFIG_LIFT_BSP_APIC_ID
@@ -197,7 +234,7 @@ void print_apicid_nodeid_coreid(u32 apicid, struct node_core_id id,
 	       apicid, id.nodeid, id.coreid);
 }
 
-static u32 wait_cpu_state(u32 apicid, u32 state)
+uint32_t wait_cpu_state(uint32_t apicid, uint32_t state, uint32_t state2)
 {
 	u32 readback = 0;
 	u32 timeout = 1;
@@ -205,7 +242,7 @@ static u32 wait_cpu_state(u32 apicid, u32 state)
 	while (--loop > 0) {
 		if (lapic_remote_read(apicid, LAPIC_MSG_REG, &readback) != 0)
 			continue;
-		if ((readback & 0x3f) == state || (readback & 0x3f) == F10_APSTATE_RESET) {
+		if ((readback & 0x3f) == state || (readback & 0x3f) == state2 || (readback & 0x3f) == F10_APSTATE_RESET) {
 			timeout = 0;
 			break;	//target cpu is in stage started
 		}
@@ -222,7 +259,7 @@ static u32 wait_cpu_state(u32 apicid, u32 state)
 static void wait_ap_started(u32 ap_apicid, void *gp)
 {
 	u32 timeout;
-	timeout = wait_cpu_state(ap_apicid, F10_APSTATE_STARTED);
+	timeout = wait_cpu_state(ap_apicid, F10_APSTATE_STARTED, F10_APSTATE_ASLEEP);
 	printk(BIOS_DEBUG, "* AP %02x", ap_apicid);
 	if (timeout) {
 		printk(BIOS_DEBUG, " timed out:%08x\n", timeout);
@@ -258,16 +295,27 @@ static void enable_apic_ext_id(u32 node)
 	pci_write_config32(NODE_HT(node), 0x68, val);
 }
 
-static void STOP_CAR_AND_CPU(void)
+static void STOP_CAR_AND_CPU(uint8_t skip_sharedc_config, uint32_t apicid)
 {
 	msr_t msr;
+	uint32_t family;
+
+	family = amd_fam1x_cpu_family();		// inline
+
+	if (family < 0x6f) {
+		/* Family 10h or earlier */
+
+		/* Disable L2 IC to L3 connection (Only for CAR) */
+		msr = rdmsr(BU_CFG2);
+		msr.lo &= ~(1 << ClLinesToNbDis);
+		wrmsr(BU_CFG2, msr);
+	}
 
-	/* Disable L2 IC to L3 connection (Only for CAR) */
-	msr = rdmsr(BU_CFG2);
-	msr.lo &= ~(1 << ClLinesToNbDis);
-	wrmsr(BU_CFG2, msr);
+	disable_cache_as_ram(skip_sharedc_config);	// inline
+
+	/* Mark the core as sleeping */
+	lapic_write(LAPIC_MSG_REG, (apicid << 24) | F10_APSTATE_ASLEEP);
 
-	disable_cache_as_ram();	// inline
 	/* stop all cores except node0/core0 the bsp .... */
 	stop_this_cpu();
 }
@@ -276,6 +324,7 @@ static u32 init_cpus(u32 cpu_init_detectedx, struct sys_info *sysinfo)
 {
 	u32 bsp_apicid = 0;
 	u32 apicid;
+	uint8_t set_mtrrs;
 	struct node_core_id id;
 
 	/* Please refer to the calculations and explaination in cache_as_ram.inc before modifying these values */
@@ -362,7 +411,7 @@ static u32 init_cpus(u32 cpu_init_detectedx, struct sys_info *sysinfo)
 		 */
 		update_microcode(cpuid_eax(1));
 
-		cpuSetAMDMSR();
+		cpuSetAMDMSR(id.nodeid);
 
 #if CONFIG_SET_FIDVID
 #if CONFIG_LOGICAL_CPUS && CONFIG_SET_FIDVID_CORE0_ONLY
@@ -385,10 +434,29 @@ static u32 init_cpus(u32 cpu_init_detectedx, struct sys_info *sysinfo)
 		}
 #endif
 
+		if (is_fam15h()) {
+			/* core 1 on node 0 is special; to avoid corrupting the
+			 * BSP do not alter MTRRs on that core */
+			if (apicid == 1)
+				set_mtrrs = 0;
+			else
+				set_mtrrs = !!(apicid & 0x1);
+		} else {
+			set_mtrrs = 1;
+		}
+
 		/* AP is ready, configure MTRRs and go to sleep */
-		set_var_mtrr(0, 0x00000000, CONFIG_RAMTOP, MTRR_TYPE_WRBACK);
+		if (set_mtrrs)
+			set_var_mtrr(0, 0x00000000, CONFIG_RAMTOP, MTRR_TYPE_WRBACK);
 
-		STOP_CAR_AND_CPU();
+		printk(BIOS_DEBUG, "Disabling CAR on AP %02x\n", apicid);
+		if (is_fam15h()) {
+			/* Only modify the MSRs on the odd cores (the last cores to finish booting) */
+			STOP_CAR_AND_CPU(!set_mtrrs, apicid);
+		} else {
+			/* Modify MSRs on all cores */
+			STOP_CAR_AND_CPU(0, apicid);
+		}
 
 		printk(BIOS_DEBUG,
 		       "\nAP %02x should be halted but you are reading this....\n",
@@ -496,7 +564,7 @@ static void setup_remote_node(u8 node)
 }
 #endif				/* CONFIG_MAX_PHYSICAL_CPUS > 1 */
 
-static void AMD_Errata281(u8 node, u32 revision, u32 platform)
+static void AMD_Errata281(u8 node, uint64_t revision, u32 platform)
 {
 	/* Workaround for Transaction Scheduling Conflict in
 	 * Northbridge Cross Bar.  Implement XCS Token adjustment
@@ -794,7 +862,7 @@ static void AMD_SetHtPhyRegister(u8 node, u8 link, u8 entry)
 	} while (!(val & HTPHY_IS_COMPLETE_MASK));
 }
 
-void cpuSetAMDMSR(void)
+void cpuSetAMDMSR(uint8_t node_id)
 {
 	/* This routine loads the CPU with default settings in fam10_msr_default
 	 * table . It must be run after Cache-As-RAM has been enabled, and
@@ -804,7 +872,8 @@ void cpuSetAMDMSR(void)
 	 */
 	msr_t msr;
 	u8 i;
-	u32 revision, platform;
+	u32 platform;
+	uint64_t revision;
 
 	printk(BIOS_DEBUG, "cpuSetAMDMSR ");
 
@@ -824,6 +893,49 @@ void cpuSetAMDMSR(void)
 	}
 	AMD_Errata298();
 
+	if (revision & AMD_FAM15_ALL) {
+		uint32_t f5x80;
+		uint8_t enabled;
+		uint8_t compute_unit_count = 0;
+		f5x80 = pci_read_config32(NODE_PCI(node_id, 5), 0x80);
+		enabled = f5x80 & 0xf;
+		if (enabled == 0x1)
+			compute_unit_count = 1;
+		if (enabled == 0x3)
+			compute_unit_count = 2;
+		if (enabled == 0x7)
+			compute_unit_count = 3;
+		if (enabled == 0xf)
+			compute_unit_count = 4;
+		msr = rdmsr(BU_CFG2);
+		msr.lo &= ~(0x3 << 6);				/* ThrottleNbInterface[1:0] */
+		msr.lo |= (((compute_unit_count - 1) & 0x3) << 6);
+		wrmsr(BU_CFG2, msr);
+	}
+
+	/* Revision C0 and above */
+	if (revision & AMD_OR_C0) {
+		uint32_t f3x1fc = pci_read_config32(NODE_PCI(node_id, 3), 0x1fc);
+		msr = rdmsr(FP_CFG);
+		msr.hi &= ~(0x7 << (42-32));			/* DiDtCfg4 */
+		msr.hi |= (((f3x1fc >> 17) & 0x7) << (42-32));
+		msr.hi &= ~(0x1 << (41-32));			/* DiDtCfg5 */
+		msr.hi |= (((f3x1fc >> 22) & 0x1) << (41-32));
+		msr.hi &= ~(0x1 << (40-32));			/* DiDtCfg3 */
+		msr.hi |= (((f3x1fc >> 16) & 0x1) << (40-32));
+		msr.hi &= ~(0x7 << (32-32));			/* DiDtCfg1 (1) */
+		msr.hi |= (((f3x1fc >> 11) & 0x7) << (32-32));
+		msr.lo &= ~(0x1f << 27);			/* DiDtCfg1 (2) */
+		msr.lo |= (((f3x1fc >> 6) & 0x1f) << 27);
+		msr.lo &= ~(0x3 << 25);				/* DiDtCfg2 */
+		msr.lo |= (((f3x1fc >> 14) & 0x3) << 25);
+		msr.lo &= ~(0x1f << 18);			/* DiDtCfg0 */
+		msr.lo |= (((f3x1fc >> 1) & 0x1f) << 18);
+		msr.lo &= ~(0x1 << 16);				/* DiDtMode */
+		msr.lo |= ((f3x1fc & 0x1) << 16);
+		wrmsr(FP_CFG, msr);
+	}
+
 	printk(BIOS_DEBUG, " done\n");
 }
 
@@ -835,9 +947,10 @@ static void cpuSetAMDPCI(u8 node)
 	 * that it is run for the first core on each node
 	 */
 	u8 i, j;
-	u32 revision, platform;
+	u32 platform;
 	u32 val;
 	u8 offset;
+	uint64_t revision;
 
 	printk(BIOS_DEBUG, "cpuSetAMDPCI %02d", node);
 
@@ -899,6 +1012,7 @@ static void cpuSetAMDPCI(u8 node)
 }
 
 #ifdef UNUSED_CODE
+/* Clearing the MCA registers is apparently handled in the ramstage CPU Function 3 driver */
 static void cpuInitializeMCA(void)
 {
 	/* Clears Machine Check Architecture (MCA) registers, which power on
diff --git a/src/cpu/amd/family_10h-family_15h/model_10xxx_init.c b/src/cpu/amd/family_10h-family_15h/model_10xxx_init.c
index b942c1a..8a61f13 100644
--- a/src/cpu/amd/family_10h-family_15h/model_10xxx_init.c
+++ b/src/cpu/amd/family_10h-family_15h/model_10xxx_init.c
@@ -39,6 +39,23 @@
 
 #define MCI_STATUS 0x401
 
+static inline uint8_t is_fam15h(void)
+{
+	uint8_t fam15h = 0;
+	uint32_t family;
+
+	family = cpuid_eax(0x80000001);
+	family = ((family & 0xf00000) >> 16) | ((family & 0xf00) >> 8);
+
+	if (family >= 0x6f)
+		/* Family 15h or later */
+		fam15h = 1;
+
+	return fam15h;
+}
+
+static volatile uint8_t fam15h_startup_flags[MAX_NODES_SUPPORTED][MAX_CORES_SUPPORTED] = {{ 0 }};
+
 static void model_10xxx_init(device_t dev)
 {
 	u8 i;
@@ -47,13 +64,44 @@ static void model_10xxx_init(device_t dev)
 #if CONFIG_LOGICAL_CPUS
 	u32 siblings;
 #endif
+	uint8_t delay_start;
 
 	id = get_node_core_id(read_nb_cfg_54());	/* nb_cfg_54 can not be set */
 	printk(BIOS_DEBUG, "nodeid = %02d, coreid = %02d\n", id.nodeid, id.coreid);
 
+	if (is_fam15h())
+		delay_start = !!(id.coreid & 0x1);
+	else
+		delay_start = 0;
+
 	/* Turn on caching if we haven't already */
 	x86_enable_cache();
-	amd_setup_mtrrs();
+
+	if (!delay_start) {
+		/* Initialize all variable MTRRs except the first pair.
+		 * This prevents Linux from having to correct an inconsistent
+		 * MTRR setup, which would crash Family 15h CPUs due to the
+		 * compute unit structure sharing MTRR MSRs between AP cores.
+		 */
+		msr.hi = 0x00000000;
+		msr.lo = 0x00000000;
+
+		disable_cache();
+
+		for (i = 0x2; i < 0x10; i++) {
+ 			wrmsr(0x00000200 | i, msr);
+ 		}
+
+ 		enable_cache();
+
+		/* Set up other MTRRs */
+		amd_setup_mtrrs();
+	} else {
+		while (!fam15h_startup_flags[id.nodeid][id.coreid - 1]) {
+			/* Wait for CU first core startup */
+		}
+	}
+
 	x86_mtrr_check();
 
 	disable_cache();
@@ -88,17 +136,24 @@ static void model_10xxx_init(device_t dev)
 	printk(BIOS_DEBUG, "siblings = %02d, ", siblings);
 #endif
 
-	/* DisableCf8ExtCfg */
+	/* Disable Cf8ExtCfg */
 	msr = rdmsr(NB_CFG_MSR);
 	msr.hi &= ~(1 << (46 - 32));
 	wrmsr(NB_CFG_MSR, msr);
 
-	msr = rdmsr(BU_CFG2_MSR);
-	/* Clear ClLinesToNbDis */
-	msr.lo &= ~(1 << 15);
-	/* Clear bit 35 as per Erratum 343 */
-	msr.hi &= ~(1 << (35-32));
-	wrmsr(BU_CFG2_MSR, msr);
+	if (is_fam15h()) {
+		msr = rdmsr(BU_CFG3_MSR);
+		/* Set CombineCr0Cd */
+		msr.hi |= (1 << (49-32));
+		wrmsr(BU_CFG3_MSR, msr);
+	} else {
+		msr = rdmsr(BU_CFG2_MSR);
+		/* Clear ClLinesToNbDis */
+		msr.lo &= ~(1 << 15);
+		/* Clear bit 35 as per Erratum 343 */
+		msr.hi &= ~(1 << (35-32));
+		wrmsr(BU_CFG2_MSR, msr);
+	}
 
 	if (IS_ENABLED(CONFIG_HAVE_SMI_HANDLER)) {
 		printk(BIOS_DEBUG, "Initializing SMM ASeg memory\n");
@@ -131,6 +186,7 @@ static void model_10xxx_init(device_t dev)
 	msr.lo |= (1 << 0);
 	wrmsr(HWCR_MSR, msr);
 
+	fam15h_startup_flags[id.nodeid][id.coreid] = 1;
 }
 
 static struct device_operations cpu_dev_ops = {
@@ -147,15 +203,17 @@ static struct cpu_device_id cpu_table[] = {
 	{ X86_VENDOR_AMD, 0x100f22 },
 	{ X86_VENDOR_AMD, 0x100f23 },
 	{ X86_VENDOR_AMD, 0x100f40 },		/* RB-C0 */
-	{ X86_VENDOR_AMD, 0x100F42 },           /* RB-C2 */
-	{ X86_VENDOR_AMD, 0x100F43 },           /* RB-C3 */
-	{ X86_VENDOR_AMD, 0x100F52 },           /* BL-C2 */
-	{ X86_VENDOR_AMD, 0x100F62 },           /* DA-C2 */
-	{ X86_VENDOR_AMD, 0x100F63 },           /* DA-C3 */
-	{ X86_VENDOR_AMD, 0x100F80 },           /* HY-D0 */
-	{ X86_VENDOR_AMD, 0x100F81 },           /* HY-D1 */
-	{ X86_VENDOR_AMD, 0x100F91 },           /* HY-D1 */
-	{ X86_VENDOR_AMD, 0x100FA0 },           /* PH-E0 */
+	{ X86_VENDOR_AMD, 0x100f42 },           /* RB-C2 */
+	{ X86_VENDOR_AMD, 0x100f43 },           /* RB-C3 */
+	{ X86_VENDOR_AMD, 0x100f52 },           /* BL-C2 */
+	{ X86_VENDOR_AMD, 0x100f62 },           /* DA-C2 */
+	{ X86_VENDOR_AMD, 0x100f63 },           /* DA-C3 */
+	{ X86_VENDOR_AMD, 0x100f80 },           /* HY-D0 */
+	{ X86_VENDOR_AMD, 0x100f81 },           /* HY-D1 */
+	{ X86_VENDOR_AMD, 0x100f91 },           /* HY-D1 */
+	{ X86_VENDOR_AMD, 0x100fa0 },           /* PH-E0 */
+	{ X86_VENDOR_AMD, 0x600f12 },           /* OR-B2 */
+	{ X86_VENDOR_AMD, 0x600f20 },           /* OR-C0 */
 	{ 0, 0 },
 };
 
diff --git a/src/cpu/amd/family_10h-family_15h/powernow_acpi.c b/src/cpu/amd/family_10h-family_15h/powernow_acpi.c
index 98ef08a..84e5514 100644
--- a/src/cpu/amd/family_10h-family_15h/powernow_acpi.c
+++ b/src/cpu/amd/family_10h-family_15h/powernow_acpi.c
@@ -74,8 +74,7 @@ static void write_pstates_for_core(u8 pstate_num, u16 *pstate_feq, u32 *pstate_p
 		/* Revision C or greater single-link processor */
 		cpuid1 = cpuid(0x80000008);
 		acpigen_write_PSD_package(0, (cpuid1.ecx & 0xff) + 1, SW_ALL);
-	}
-	else {
+	} else {
 		/* Find the local APIC ID for the specified core ID */
 		struct device* cpu;
 		int cpu_index = 0;
@@ -99,7 +98,9 @@ static void write_pstates_for_core(u8 pstate_num, u16 *pstate_feq, u32 *pstate_p
 }
 
 /*
-* For details of this algorithm, please refer to the BDKG 3.62 page 69
+* For details of this algorithm, please refer to:
+* Family 10h BDKG 3.62 page 69
+* Family 15h BDKG 3.14 page 74
 *
 * WARNING: The core count algorithm below assumes that all processors
 * are identical, with the same number of active cores.  While the BKDG
@@ -149,6 +150,13 @@ void amd_generate_powernow(u32 pcontrol_blk, u8 plen, u8 onlyBSP)
 	uint8_t node_count;
 	uint8_t cores_per_node;
 	uint8_t total_core_count;
+	uint8_t fam15h;
+	uint8_t fam10h_rev_e = 0;
+
+	/* Detect Revision E processors via method used in fidvid.c */
+	if ((cpuid_edx(0x80000007) & CPB_MASK)
+		&&  ((cpuid_ecx(0x80000008) & NC_MASK) == 5))
+			fam10h_rev_e = 1;
 
 	/*
 	 * Based on the CPU socket type,cmp_cap and pwr_lmt , get the power limit.
@@ -156,11 +164,17 @@ void amd_generate_powernow(u32 pcontrol_blk, u8 plen, u8 onlyBSP)
 	 * cmp_cap : 0x0 SingleCore ; 0x1 DualCore ; 0x2 TripleCore ; 0x3 QuadCore ; 0x4 QuintupleCore ; 0x5 HexCore
 	 */
 	printk(BIOS_INFO, "Pstates algorithm ...\n");
+	fam15h = !!(mctGetLogicalCPUID(0) & AMD_FAM15_ALL);
 	/* Get number of cores */
-	dtemp = pci_read_config32(dev_find_slot(0, PCI_DEVFN(0x18, 3)), 0xE8);
-	cmp_cap = (dtemp & 0x3000) >> 12;
-	if (mctGetLogicalCPUID(0) & AMD_FAM10_REV_D)	/* revision D */
-		cmp_cap |= (dtemp & 0x8000) >> 13;
+	if (fam15h) {
+		cmp_cap = pci_read_config32(dev_find_slot(0, PCI_DEVFN(0x18, 5)), 0x84) & 0xff;
+	} else {
+		dtemp = pci_read_config32(dev_find_slot(0, PCI_DEVFN(0x18, 3)), 0xe8);
+		cmp_cap = (dtemp & 0x3000) >> 12;
+		if (mctGetLogicalCPUID(0) & (AMD_FAM10_REV_D | AMD_FAM15_ALL))	/* revision D or higher */
+			cmp_cap |= (dtemp & 0x8000) >> 13;
+	}
+
 	/* Get number of nodes */
 	dtemp = pci_read_config32(dev_find_slot(0, PCI_DEVFN(0x18, 0)), 0x60);
 	node_count = ((dtemp & 0x70) >> 4) + 1;
@@ -169,6 +183,14 @@ void amd_generate_powernow(u32 pcontrol_blk, u8 plen, u8 onlyBSP)
 	/* Compute total number of cores installed in system */
 	total_core_count = cores_per_node * node_count;
 
+	/* Get number of boost states */
+	uint8_t boost_count = 0;
+	dtemp = pci_read_config32(dev_find_slot(0, PCI_DEVFN(0x18, 4)), 0x15c);
+	if (fam10h_rev_e)
+		boost_count = (dtemp >> 2) & 0x1;
+	else if (mctGetLogicalCPUID(0) & AMD_FAM15_ALL)
+		boost_count = (dtemp >> 2) & 0x7;
+
 	Pstate_num = 0;
 
 	/* See if the CPUID(0x80000007) returned EDX[7]==1b */
@@ -205,7 +227,7 @@ void amd_generate_powernow(u32 pcontrol_blk, u8 plen, u8 onlyBSP)
 
 	/* Get PSmax's index */
 	msr = rdmsr(0xC0010061);
-	Pstate_max = (uint8_t) ((msr.lo >> PS_MAX_VAL_SHFT) & BIT_MASK_3);
+	Pstate_max = (uint8_t) ((msr.lo >> PS_MAX_VAL_SHFT) & ((fam15h)?BIT_MASK_7:BIT_MASK_3));
 
 	/* Determine if all enabled Pstates have the same fidvid */
 	uint8_t i;
@@ -219,10 +241,14 @@ void amd_generate_powernow(u32 pcontrol_blk, u8 plen, u8 onlyBSP)
 		}
 	}
 
+	/* Family 15h uses slightly different PSmax numbering */
+	if (fam15h)
+		Pstate_max++;
+
 	/* Populate tables with all Pstate information */
 	for (Pstate_num = 0; Pstate_num < Pstate_max; Pstate_num++) {
 		/* Get power state information */
-		msr = rdmsr(0xC0010064 + Pstate_num);
+		msr = rdmsr(0xC0010064 + Pstate_num + boost_count);
 		cpufid = (msr.lo & 0x3f);
 		cpudid = (msr.lo & 0x1c0) >> 6;
 		cpuvid = (msr.lo & 0xfe00) >> 9;
@@ -232,12 +258,10 @@ void amd_generate_powernow(u32 pcontrol_blk, u8 plen, u8 onlyBSP)
 		if (pviModeFlag) {
 			if (cpuvid >= 0x20) {
 				core_voltage = 7625 - (((cpuvid - 0x20) * 10000) / 80);
-			}
-			else {
+			} else {
 				core_voltage = 15500 - ((cpuvid * 10000) / 40);
 			}
-		}
-		else {
+		} else {
 			cpuvid = cpuvid & 0x7f;
 			if (cpuvid >= 0x7c)
 				core_voltage = 0;
diff --git a/src/cpu/amd/family_10h-family_15h/processor_name.c b/src/cpu/amd/family_10h-family_15h/processor_name.c
index 12c45c9..fbd0452 100644
--- a/src/cpu/amd/family_10h-family_15h/processor_name.c
+++ b/src/cpu/amd/family_10h-family_15h/processor_name.c
@@ -33,6 +33,10 @@
 #include <cpu/amd/mtrr.h>
 #include <cpu/cpu.h>
 #include <cpu/amd/model_10xxx_rev.h>
+#include <device/device.h>
+#include <device/pci.h>
+#include <device/pnp.h>
+#include <device/pci_ops.h>
 
 /* The maximum length of CPU names is 48 bytes, including the final NULL byte.
  * If you change these names your BIOS will _NOT_ pass the AMD validation and
@@ -212,104 +216,138 @@ static int strcpymax(char *dst, const char *src, int buflen)
 	return i;
 }
 
+#define NAME_STRING_MAXLEN 48
 
 int init_processor_name(void)
 {
-	/* variable names taken from fam10 revision guide for clarity */
-	u32 BrandId;	/* CPUID Fn8000_0001_EBX */
-	u8 String1;	/* BrandID[14:11] */
-	u8 String2;	/* BrandID[3:0] */
-	u8 Model;	/* BrandID[10:4] */
-	u8 Pg;		/* BrandID[15] */
-	u8 PkgTyp;	/* BrandID[31:28] */
-	u8 NC;		/* CPUID Fn8000_0008_ECX */
-	const char *processor_name_string = unknown;
-	char program_string[48];
-	u32 *p_program_string = (u32 *)program_string;
 	msr_t msr;
-	int i, j = 0, str2_checkNC = 1;
-	const struct str_s *str, *str2;
+	ssize_t i;
+	char program_string[NAME_STRING_MAXLEN];
+	u32 *p_program_string = (u32 *)program_string;
+	uint8_t fam15h = 0;
+	uint32_t family;
 
+	family = cpuid_eax(0x80000001);
+	family = ((family & 0xf00000) >> 16) | ((family & 0xf00) >> 8);
 
-	/* Find out which CPU brand it is */
-	BrandId = cpuid_ebx(0x80000001);
-	String1 = (u8)((BrandId >> 11) & 0x0F);
-	String2 = (u8)((BrandId >> 0) & 0x0F);
-	Model = (u8)((BrandId >> 4) & 0x7F);
-	Pg = (u8)((BrandId >> 15) & 0x01);
-	PkgTyp = (u8)((BrandId >> 28) & 0x0F);
-	NC = (u8)(cpuid_ecx(0x80000008) & 0xFF);
+	if (family >= 0x6f)
+		/* Family 15h or later */
+		fam15h = 1;
 
 	/* null the string */
 	memset(program_string, 0, sizeof(program_string));
 
-	if (!Model) {
-		processor_name_string = Pg ? thermal : sample;
-		goto done;
-	}
-
-	switch (PkgTyp) {
-	case 0:		/* F1207 */
-		str = String1_socket_F;
-		str2 = String2_socket_F;
-		str2_checkNC = 0;
-		break;
-	case 1:		/* AM2 */
-		str = String1_socket_AM2;
-		str2 = String2_socket_AM2;
-		break;
-	case 3:		/* G34 */
-		str = String1_socket_G34;
-		str2 = String2_socket_G34;
-		str2_checkNC = 0;
-		break;
-	case 5:		/* C32 */
-		str = String1_socket_C32;
-		str2 = String2_socket_C32;
-		break;
-	default:
-		goto done;
-	}
+	if (fam15h) {
+		/* Family 15h or later */
+		uint32_t dword;
+		device_t cpu_fn5_dev = dev_find_slot(0, PCI_DEVFN(0x18, 5));
+		pci_write_config32(cpu_fn5_dev, 0x194, 0);
+		dword = pci_read_config32(cpu_fn5_dev, 0x198);
+		if (dword == 0) {
+			strcpymax(program_string, sample, sizeof(program_string));
+		} else {
+			/* Assemble the string from PCI configuration register contents */
+			for (i = 0; i < 12; i++) {
+				pci_write_config32(cpu_fn5_dev, 0x194, i);
+				p_program_string[i] = pci_read_config32(cpu_fn5_dev, 0x198);
+			}
+
+			/* Correctly place the null terminator */
+			for (i = (NAME_STRING_MAXLEN - 2); i > 0; i--) {
+				if (program_string[i] != 0x20)
+					break;
+			}
+			program_string[i + 1] = 0;
+		}
+	} else {
+		/* variable names taken from fam10 revision guide for clarity */
+		u32 BrandId;	/* CPUID Fn8000_0001_EBX */
+		u8 String1;	/* BrandID[14:11] */
+		u8 String2;	/* BrandID[3:0] */
+		u8 Model;	/* BrandID[10:4] */
+		u8 Pg;		/* BrandID[15] */
+		u8 PkgTyp;	/* BrandID[31:28] */
+		u8 NC;		/* CPUID Fn8000_0008_ECX */
+		const char *processor_name_string = unknown;
+		int j = 0, str2_checkNC = 1;
+		const struct str_s *str, *str2;
+
+		/* Find out which CPU brand it is */
+		BrandId = cpuid_ebx(0x80000001);
+		String1 = (u8)((BrandId >> 11) & 0x0F);
+		String2 = (u8)((BrandId >> 0) & 0x0F);
+		Model = (u8)((BrandId >> 4) & 0x7F);
+		Pg = (u8)((BrandId >> 15) & 0x01);
+		PkgTyp = (u8)((BrandId >> 28) & 0x0F);
+		NC = (u8)(cpuid_ecx(0x80000008) & 0xFF);
+
+		if (!Model) {
+			processor_name_string = Pg ? thermal : sample;
+			goto done;
+		}
 
-	/* String1 */
-	for (i = 0; str[i].value; i++) {
-		if ((str[i].Pg == Pg) &&
-		    (str[i].NC == NC) &&
-		    (str[i].String == String1)) {
-			processor_name_string = str[i].value;
+		switch (PkgTyp) {
+		case 0:		/* F1207 */
+			str = String1_socket_F;
+			str2 = String2_socket_F;
+			str2_checkNC = 0;
+			break;
+		case 1:		/* AM2 */
+			str = String1_socket_AM2;
+			str2 = String2_socket_AM2;
+			break;
+		case 3:		/* G34 */
+			str = String1_socket_G34;
+			str2 = String2_socket_G34;
+			str2_checkNC = 0;
+			break;
+		case 5:		/* C32 */
+			str = String1_socket_C32;
+			str2 = String2_socket_C32;
 			break;
+		default:
+			goto done;
 		}
-	}
 
-	if (!str[i].value)
-		goto done;
+		/* String1 */
+		for (i = 0; str[i].value; i++) {
+			if ((str[i].Pg == Pg) &&
+			(str[i].NC == NC) &&
+			(str[i].String == String1)) {
+				processor_name_string = str[i].value;
+				break;
+			}
+		}
 
-	j = strcpymax(program_string, processor_name_string,
-		      sizeof(program_string));
+		if (!str[i].value)
+			goto done;
 
-	/* Translate Model from 01-99 to ASCII and put it on the end.
-	 * Numbers less than 10 should include a leading zero, e.g., 09.*/
-	if (Model < 100 && j < sizeof(program_string) - 2) {
-		program_string[j++] = (Model / 10) + '0';
-		program_string[j++] = (Model % 10) + '0';
-	}
+		j = strcpymax(program_string, processor_name_string,
+			sizeof(program_string));
 
-	processor_name_string = unknown2;
-
-	/* String 2 */
-	for(i = 0; str2[i].value; i++) {
-		if ((str2[i].Pg == Pg) &&
-		    ((str2[i].NC == NC) || !str2_checkNC) &&
-		    (str2[i].String == String2)) {
-			processor_name_string = str2[i].value;
-			break;
+		/* Translate Model from 01-99 to ASCII and put it on the end.
+		* Numbers less than 10 should include a leading zero, e.g., 09.*/
+		if (Model < 100 && j < sizeof(program_string) - 2) {
+			program_string[j++] = (Model / 10) + '0';
+			program_string[j++] = (Model % 10) + '0';
 		}
-	}
 
+		processor_name_string = unknown2;
+
+		/* String 2 */
+		for(i = 0; str2[i].value; i++) {
+			if ((str2[i].Pg == Pg) &&
+			((str2[i].NC == NC) || !str2_checkNC) &&
+			(str2[i].String == String2)) {
+				processor_name_string = str2[i].value;
+				break;
+			}
+		}
 
-done:
-	strcpymax(&program_string[j], processor_name_string,
-		  sizeof(program_string) - j);
+	done:
+		strcpymax(&program_string[j], processor_name_string,
+			sizeof(program_string) - j);
+	}
 
 	printk(BIOS_DEBUG, "CPU model: %s\n", program_string);
 
diff --git a/src/cpu/amd/family_10h-family_15h/update_microcode.c b/src/cpu/amd/family_10h-family_15h/update_microcode.c
index 51aca35..3b2f5dd 100644
--- a/src/cpu/amd/family_10h-family_15h/update_microcode.c
+++ b/src/cpu/amd/family_10h-family_15h/update_microcode.c
@@ -28,6 +28,7 @@ struct id_mapping {
 
 static u16 get_equivalent_processor_rev_id(u32 orig_id) {
 	static const struct id_mapping id_mapping_table[] = {
+		/* Family 10h */
 		{ 0x100f00, 0x1000 },
 		{ 0x100f01, 0x1000 },
 		{ 0x100f02, 0x1000 },
@@ -42,8 +43,13 @@ static u16 get_equivalent_processor_rev_id(u32 orig_id) {
 		{ 0x100f62, 0x1062 }, /* DA-C2 */
 		{ 0x100f63, 0x1043 }, /* DA-C3 */
 		{ 0x100f81, 0x1081 }, /* HY-D1 */
+		{ 0x100f91, 0x1081 }, /* HY-D1 */
 		{ 0x100fa0, 0x10A0 }, /* PH-E0 */
 
+		/* Family 15h */
+		{ 0x600f12, 0x6012 }, /* OR-B2 */
+		{ 0x600f20, 0x6020 }, /* OR-C0 */
+
 		/* Array terminator */
 		{ 0xffffff, 0x0000 },
 	};
diff --git a/src/cpu/amd/model_fxx/init_cpus.c b/src/cpu/amd/model_fxx/init_cpus.c
index 12d3a95..3960c03 100644
--- a/src/cpu/amd/model_fxx/init_cpus.c
+++ b/src/cpu/amd/model_fxx/init_cpus.c
@@ -190,7 +190,7 @@ void allow_all_aps_stop(u32 bsp_apicid)
 
 static void STOP_CAR_AND_CPU(void)
 {
-	disable_cache_as_ram();	// inline
+	disable_cache_as_ram(0);	// inline
 	/* stop all cores except node0/core0 the bsp .... */
 	stop_this_cpu();
 }
diff --git a/src/cpu/amd/quadcore/quadcore.c b/src/cpu/amd/quadcore/quadcore.c
index 9c21e94..8a9b5ed 100644
--- a/src/cpu/amd/quadcore/quadcore.c
+++ b/src/cpu/amd/quadcore/quadcore.c
@@ -2,6 +2,7 @@
  * This file is part of the coreboot project.
  *
  * Copyright (C) 2007 Advanced Micro Devices, Inc.
+ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -26,16 +27,41 @@
 
 #include "cpu/amd/quadcore/quadcore_id.c"
 
+/* get_boot_apic_id and wait_cpu_state located in init_cpus.c */
+uint32_t get_boot_apic_id(uint8_t node, uint32_t core);
+uint32_t wait_cpu_state(uint32_t apicid, uint32_t state, uint32_t state2);
+
+static inline uint8_t is_fam15h(void)
+{
+	uint8_t fam15h = 0;
+	uint32_t family;
+
+	family = cpuid_eax(0x80000001);
+	family = ((family & 0xf00000) >> 16) | ((family & 0xf00) >> 8);
+
+	if (family >= 0x6f)
+		/* Family 15h or later */
+		fam15h = 1;
+
+	return fam15h;
+}
+
 static u32 get_core_num_in_bsp(u32 nodeid)
 {
 	u32 dword;
-	dword = pci_read_config32(NODE_PCI(nodeid, 3), 0xe8);
-	dword >>= 12;
-	/* Bit 15 is CmpCap[2] since Revision D. */
-	if ((cpuid_ecx(0x80000008) & 0xff) > 3)
-	    dword = ((dword & 8) >> 1) | (dword & 3);
-	else
-	    dword &= 3;
+	if (is_fam15h()) {
+		/* Family 15h moved CmpCap to F5x84 [7:0] */
+		dword = pci_read_config32(NODE_PCI(nodeid, 5), 0x84);
+		dword &= 0xff;
+	} else {
+		dword = pci_read_config32(NODE_PCI(nodeid, 3), 0xe8);
+		dword >>= 12;
+		/* Bit 15 is CmpCap[2] since Revision D. */
+		if ((cpuid_ecx(0x80000008) & 0xff) > 3)
+	    	dword = ((dword & 8) >> 1) | (dword & 3);
+		else
+	    	dword &= 3;
+	}
 	return dword;
 }
 
@@ -50,28 +76,68 @@ static u8 set_apicid_cpuid_lo(void)
 	return 1;
 }
 
-static void real_start_other_core(u32 nodeid, u32 cores)
+static void real_start_other_core(uint32_t nodeid, uint32_t cores)
 {
-	u32 dword, i;
+	ssize_t i;
+	uint32_t dword;
 
 	printk(BIOS_DEBUG, "Start other core - nodeid: %02x  cores: %02x\n", nodeid, cores);
 
 	/* set PCI_DEV(0, 0x18+nodeid, 3), 0x44 bit 27 to redirect all MC4
 	   accesses and error logging to core0 */
 	dword = pci_read_config32(NODE_PCI(nodeid, 3), 0x44);
-	dword |= 1 << 27;	// NbMcaToMstCpuEn bit
+	dword |= 1 << 30;	/* SyncFloodOnDramAdrParErr=1 */
+	dword |= 1 << 27;	/* NbMcaToMstCpuEn=1 */
+	dword |= 1 << 21;	/* SyncFloodOnAnyUcErr=1 */
+	dword |= 1 << 20;	/* SyncFloodOnWDT=1 */
+	dword |= 1 << 2;	/* SyncFloodOnDramUcEcc=1 */
 	pci_write_config32(NODE_PCI(nodeid, 3), 0x44, dword);
-	// set PCI_DEV(0, 0x18+nodeid, 0), 0x68 bit 5 to start core1
-	dword = pci_read_config32(NODE_PCI(nodeid, 0), 0x68);
-	dword |= 1 << 5;
-	pci_write_config32(NODE_PCI(nodeid, 0), 0x68, dword);
-
-	if(cores > 1) {
-		dword = pci_read_config32(NODE_PCI(nodeid, 0), 0x168);
-		for (i = 0; i < cores - 1; i++) {
-			dword |= 1 << i;
+	if (is_fam15h()) {
+		uint32_t core_activation_flags = 0;
+		uint32_t active_cores = 0;
+
+		/* Set PCI_DEV(0, 0x18+nodeid, 0), 0x1dc bits 7:1 to start cores */
+		dword = pci_read_config32(NODE_PCI(nodeid, 0), 0x1dc);
+		for (i = 1; i < cores + 1; i++) {
+			core_activation_flags |= 1 << i;
+		}
+
+		/* Start the first core of each compute unit */
+		active_cores |= core_activation_flags & 0x55;
+		pci_write_config32(NODE_PCI(nodeid, 0), 0x1dc, dword | active_cores);
+
+		/* Each core shares a single set of MTRR registers with
+		 * another core in the same compute unit, therefore, it
+		 * is important that one core in each CU starts in advance
+		 * of the other in order to avoid one core stomping all over
+		 * the other core's settings.
+		 */
+
+		/* Wait for the first core of each compute unit to start... */
+		uint32_t timeout;
+		for (i = 1; i < cores + 1; i++) {
+			if (!(i & 0x1)) {
+				uint32_t ap_apicid = get_boot_apic_id(nodeid, i);
+				timeout = wait_cpu_state(ap_apicid, F10_APSTATE_ASLEEP, F10_APSTATE_ASLEEP);
+			}
+		}
+
+		/* Start the second core of each compute unit */
+		active_cores |= core_activation_flags & 0xaa;
+		pci_write_config32(NODE_PCI(nodeid, 0), 0x1dc, dword | active_cores);
+	} else {
+		// set PCI_DEV(0, 0x18+nodeid, 0), 0x68 bit 5 to start core1
+		dword = pci_read_config32(NODE_PCI(nodeid, 0), 0x68);
+		dword |= 1 << 5;
+		pci_write_config32(NODE_PCI(nodeid, 0), 0x68, dword);
+
+		if (cores > 1) {
+			dword = pci_read_config32(NODE_PCI(nodeid, 0), 0x168);
+			for (i = 0; i < cores - 1; i++) {
+				dword |= 1 << i;
+			}
+			pci_write_config32(NODE_PCI(nodeid, 0), 0x168, dword);
 		}
-		pci_write_config32(NODE_PCI(nodeid, 0), 0x168, dword);
 	}
 }
 
@@ -91,10 +157,9 @@ static void start_other_cores(void)
 
 	for (nodeid = 0; nodeid < nodes; nodeid++) {
 		u32 cores = get_core_num_in_bsp(nodeid);
-		printk(BIOS_DEBUG, "init node: %02x  cores: %02x \n", nodeid, cores);
+		printk(BIOS_DEBUG, "init node: %02x  cores: %02x pass 1 \n", nodeid, cores);
 		if (cores > 0) {
 			real_start_other_core(nodeid, cores);
 		}
 	}
-
 }
diff --git a/src/cpu/amd/quadcore/quadcore_id.c b/src/cpu/amd/quadcore/quadcore_id.c
index c5921de..c0537b3 100644
--- a/src/cpu/amd/quadcore/quadcore_id.c
+++ b/src/cpu/amd/quadcore/quadcore_id.c
@@ -43,9 +43,12 @@ struct node_core_id get_node_core_id(u32 nb_cfg_54)
 {
 	struct node_core_id id;
 	uint8_t apicid;
+	uint8_t fam15h = 0;
 	uint8_t rev_gte_d = 0;
 	uint8_t dual_node = 0;
 	uint32_t f3xe8;
+	uint32_t family;
+	uint32_t model;
 
 #ifdef __PRE_RAM__
 	f3xe8 = pci_read_config32(NODE_PCI(0, 3), 0xe8);
@@ -53,7 +56,17 @@ struct node_core_id get_node_core_id(u32 nb_cfg_54)
 	f3xe8 = pci_read_config32(get_node_pci(0, 3), 0xe8);
 #endif
 
-	if (cpuid_eax(0x80000001) >= 0x8)
+	family = model = cpuid_eax(0x80000001);
+	model = ((model & 0xf0000) >> 12) | ((model & 0xf0) >> 4);
+	family = ((family & 0xf00000) >> 16) | ((family & 0xf00) >> 8);
+
+	if (family >= 0x6f) {
+		/* Family 15h or later */
+		fam15h = 1;
+		nb_cfg_54 = 1;
+	}
+
+	if ((model >= 0x8) || fam15h)
 		/* Revision D or later */
 		rev_gte_d = 1;
 
@@ -67,7 +80,13 @@ struct node_core_id get_node_core_id(u32 nb_cfg_54)
 	 */
 	apicid = (cpuid_ebx(1) >> 24) & 0xff;
 	if( nb_cfg_54) {
-		if (rev_gte_d && dual_node) {
+		if (fam15h && dual_node) {
+			id.coreid = apicid & 0x1f;
+			id.nodeid = (apicid & 0x60) >> 5;
+		} else if (fam15h && !dual_node) {
+			id.coreid = apicid & 0xf;
+			id.nodeid = (apicid & 0x70) >> 4;
+		} else if (rev_gte_d && dual_node) {
 			id.coreid = apicid & 0xf;
 			id.nodeid = (apicid & 0x30) >> 4;
 		} else if (rev_gte_d && !dual_node) {
@@ -90,7 +109,25 @@ struct node_core_id get_node_core_id(u32 nb_cfg_54)
 		}
 	}
 
-	if (rev_gte_d && dual_node) {
+	if (fam15h && dual_node) {
+		/* Coreboot expects each separate processor die to be on a different nodeid.
+		 * Since the code above returns nodeid 0 even on internal node 1 some fixup is needed...
+		 */
+		uint32_t f5x84;
+		uint8_t core_count;
+
+#ifdef __PRE_RAM__
+		f5x84 = pci_read_config32(NODE_PCI(0, 5), 0x84);
+#else
+		f5x84 = pci_read_config32(get_node_pci(0, 5), 0x84);
+#endif
+		core_count = (f5x84 & 0xff) + 1;
+		id.nodeid = id.nodeid * 2;
+		if (id.coreid >= core_count) {
+			id.nodeid += 1;
+			id.coreid = id.coreid - core_count;
+		}
+	} else if (rev_gte_d && dual_node) {
 		/* Coreboot expects each separate processor die to be on a different nodeid.
 		 * Since the code above returns nodeid 0 even on internal node 1 some fixup is needed...
 		 */
diff --git a/src/include/cpu/amd/model_10xxx_msr.h b/src/include/cpu/amd/model_10xxx_msr.h
index 6c7dece..7d78e2d 100644
--- a/src/include/cpu/amd/model_10xxx_msr.h
+++ b/src/include/cpu/amd/model_10xxx_msr.h
@@ -2,6 +2,7 @@
  * This file is part of the coreboot project.
  *
  * Copyright (C) 2007 Advanced Micro Devices, Inc.
+ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -32,7 +33,13 @@
 #define IC_CFG_MSR			0xC0011021
 #define DC_CFG_MSR			0xC0011022
 #define BU_CFG_MSR			0xC0011023
+#define FP_CFG_MSR			0xC0011028
+#define DE_CFG_MSR			0xC0011029
 #define BU_CFG2_MSR			0xC001102A
+#define BU_CFG3_MSR			0xC001102B
+#define EX_CFG_MSR			0xC001102C
+#define LS_CFG2_MSR			0xC001102D
+#define IBS_OP_DATA3_MSR		0xC0011037
 
 #define CPU_ID_FEATURES_MSR		0xC0011004
 #define CPU_ID_HYPER_EXT_FEATURES	0xC001100d
diff --git a/src/mainboard/advansus/a785e-i/romstage.c b/src/mainboard/advansus/a785e-i/romstage.c
index 4c2b38a..ab717fd 100644
--- a/src/mainboard/advansus/a785e-i/romstage.c
+++ b/src/mainboard/advansus/a785e-i/romstage.c
@@ -131,7 +131,7 @@ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx)
 
 	post_code(0x33);
 
-	cpuSetAMDMSR();
+	cpuSetAMDMSR(0);
 	post_code(0x34);
 
 	amd_ht_init(sysinfo);
diff --git a/src/mainboard/amd/bimini_fam10/romstage.c b/src/mainboard/amd/bimini_fam10/romstage.c
index e2bd351..5e2cf82 100644
--- a/src/mainboard/amd/bimini_fam10/romstage.c
+++ b/src/mainboard/amd/bimini_fam10/romstage.c
@@ -123,7 +123,7 @@ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx)
 
 	post_code(0x33);
 
-	cpuSetAMDMSR();
+	cpuSetAMDMSR(0);
 	post_code(0x34);
 
 	amd_ht_init(sysinfo);
diff --git a/src/mainboard/amd/mahogany_fam10/romstage.c b/src/mainboard/amd/mahogany_fam10/romstage.c
index 74bc9d5..025a8bb 100644
--- a/src/mainboard/amd/mahogany_fam10/romstage.c
+++ b/src/mainboard/amd/mahogany_fam10/romstage.c
@@ -125,7 +125,7 @@ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx)
 
 	post_code(0x33);
 
-	cpuSetAMDMSR();
+	cpuSetAMDMSR(0);
 	post_code(0x34);
 
 	amd_ht_init(sysinfo);
diff --git a/src/mainboard/amd/serengeti_cheetah_fam10/romstage.c b/src/mainboard/amd/serengeti_cheetah_fam10/romstage.c
index 20d46e6..5063439 100644
--- a/src/mainboard/amd/serengeti_cheetah_fam10/romstage.c
+++ b/src/mainboard/amd/serengeti_cheetah_fam10/romstage.c
@@ -231,7 +231,7 @@ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx)
 
 	post_code(0x33);
 
-	cpuSetAMDMSR();
+	cpuSetAMDMSR(0);
 	post_code(0x34);
 
 	amd_ht_init(sysinfo);
diff --git a/src/mainboard/amd/tilapia_fam10/romstage.c b/src/mainboard/amd/tilapia_fam10/romstage.c
index 89100b1..e37bc08 100644
--- a/src/mainboard/amd/tilapia_fam10/romstage.c
+++ b/src/mainboard/amd/tilapia_fam10/romstage.c
@@ -125,7 +125,7 @@ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx)
 
 	post_code(0x33);
 
-	cpuSetAMDMSR();
+	cpuSetAMDMSR(0);
 	post_code(0x34);
 
 	amd_ht_init(sysinfo);
diff --git a/src/mainboard/asus/kfsn4-dre/romstage.c b/src/mainboard/asus/kfsn4-dre/romstage.c
index 5d1f5a6..dd5c7dc 100644
--- a/src/mainboard/asus/kfsn4-dre/romstage.c
+++ b/src/mainboard/asus/kfsn4-dre/romstage.c
@@ -245,7 +245,7 @@ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx)
 
 	post_code(0x33);
 
-	cpuSetAMDMSR();
+	cpuSetAMDMSR(0);
 	post_code(0x34);
 
 	amd_ht_init(sysinfo);
diff --git a/src/mainboard/asus/kgpe-d16/romstage.c b/src/mainboard/asus/kgpe-d16/romstage.c
index a3f3310..a58fd0f 100644
--- a/src/mainboard/asus/kgpe-d16/romstage.c
+++ b/src/mainboard/asus/kgpe-d16/romstage.c
@@ -354,7 +354,7 @@ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx)
 
 	post_code(0x33);
 
-	cpuSetAMDMSR();
+	cpuSetAMDMSR(0);
 	post_code(0x34);
 
 	amd_ht_init(sysinfo);
@@ -512,4 +512,4 @@ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx)
 BOOL AMD_CB_ManualBUIDSwapList (u8 node, u8 link, const u8 **List)
 {
 	return 0;
-}
\ No newline at end of file
+}
diff --git a/src/mainboard/asus/m4a78-em/romstage.c b/src/mainboard/asus/m4a78-em/romstage.c
index 82f30d9..82b96bf 100644
--- a/src/mainboard/asus/m4a78-em/romstage.c
+++ b/src/mainboard/asus/m4a78-em/romstage.c
@@ -127,7 +127,7 @@ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx)
 
 	post_code(0x33);
 
-	cpuSetAMDMSR();
+	cpuSetAMDMSR(0);
 	post_code(0x34);
 
 	amd_ht_init(sysinfo);
diff --git a/src/mainboard/asus/m4a785-m/romstage.c b/src/mainboard/asus/m4a785-m/romstage.c
index 780bf81..30975fa 100644
--- a/src/mainboard/asus/m4a785-m/romstage.c
+++ b/src/mainboard/asus/m4a785-m/romstage.c
@@ -127,7 +127,7 @@ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx)
 
 	post_code(0x33);
 
-	cpuSetAMDMSR();
+	cpuSetAMDMSR(0);
 	post_code(0x34);
 
 	amd_ht_init(sysinfo);
diff --git a/src/mainboard/asus/m5a88-v/romstage.c b/src/mainboard/asus/m5a88-v/romstage.c
index 38761a6..4edaba2 100644
--- a/src/mainboard/asus/m5a88-v/romstage.c
+++ b/src/mainboard/asus/m5a88-v/romstage.c
@@ -128,7 +128,7 @@ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx)
 
 	post_code(0x33);
 
-	cpuSetAMDMSR();
+	cpuSetAMDMSR(0);
 	post_code(0x34);
 
 	amd_ht_init(sysinfo);
diff --git a/src/mainboard/avalue/eax-785e/romstage.c b/src/mainboard/avalue/eax-785e/romstage.c
index 764a5c6..447012b 100644
--- a/src/mainboard/avalue/eax-785e/romstage.c
+++ b/src/mainboard/avalue/eax-785e/romstage.c
@@ -132,7 +132,7 @@ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx)
 
 	post_code(0x33);
 
-	cpuSetAMDMSR();
+	cpuSetAMDMSR(0);
 	post_code(0x34);
 
 	amd_ht_init(sysinfo);
diff --git a/src/mainboard/gigabyte/ma785gm/romstage.c b/src/mainboard/gigabyte/ma785gm/romstage.c
index db4e449..444e59d 100644
--- a/src/mainboard/gigabyte/ma785gm/romstage.c
+++ b/src/mainboard/gigabyte/ma785gm/romstage.c
@@ -122,7 +122,7 @@ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx)
 
 	post_code(0x33);
 
-	cpuSetAMDMSR();
+	cpuSetAMDMSR(0);
 	post_code(0x34);
 
 	amd_ht_init(sysinfo);
diff --git a/src/mainboard/gigabyte/ma785gmt/romstage.c b/src/mainboard/gigabyte/ma785gmt/romstage.c
index 4ce7c58..705d7c5 100644
--- a/src/mainboard/gigabyte/ma785gmt/romstage.c
+++ b/src/mainboard/gigabyte/ma785gmt/romstage.c
@@ -122,7 +122,7 @@ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx)
 
 	post_code(0x33);
 
-	cpuSetAMDMSR();
+	cpuSetAMDMSR(0);
 	post_code(0x34);
 
 	amd_ht_init(sysinfo);
diff --git a/src/mainboard/gigabyte/ma78gm/romstage.c b/src/mainboard/gigabyte/ma78gm/romstage.c
index d2a0b95..5d21801 100644
--- a/src/mainboard/gigabyte/ma78gm/romstage.c
+++ b/src/mainboard/gigabyte/ma78gm/romstage.c
@@ -125,7 +125,7 @@ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx)
 
 	post_code(0x33);
 
-	cpuSetAMDMSR();
+	cpuSetAMDMSR(0);
 	post_code(0x34);
 
 	amd_ht_init(sysinfo);
diff --git a/src/mainboard/hp/dl165_g6_fam10/romstage.c b/src/mainboard/hp/dl165_g6_fam10/romstage.c
index 97e60d5..26c0bb9 100644
--- a/src/mainboard/hp/dl165_g6_fam10/romstage.c
+++ b/src/mainboard/hp/dl165_g6_fam10/romstage.c
@@ -137,7 +137,7 @@ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx)
 
 	post_code(0x33);
 
-	cpuSetAMDMSR();
+	cpuSetAMDMSR(0);
 	post_code(0x34);
 
 	amd_ht_init(sysinfo);
diff --git a/src/mainboard/iei/kino-780am2-fam10/romstage.c b/src/mainboard/iei/kino-780am2-fam10/romstage.c
index edbae3a..321eea6 100644
--- a/src/mainboard/iei/kino-780am2-fam10/romstage.c
+++ b/src/mainboard/iei/kino-780am2-fam10/romstage.c
@@ -125,7 +125,7 @@ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx)
 
 	post_code(0x33);
 
-	cpuSetAMDMSR();
+	cpuSetAMDMSR(0);
 	post_code(0x34);
 
 	amd_ht_init(sysinfo);
diff --git a/src/mainboard/jetway/pa78vm5/romstage.c b/src/mainboard/jetway/pa78vm5/romstage.c
index 16bb089..93dd2ce 100644
--- a/src/mainboard/jetway/pa78vm5/romstage.c
+++ b/src/mainboard/jetway/pa78vm5/romstage.c
@@ -130,7 +130,7 @@ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx)
 
 	post_code(0x33);
 
-	cpuSetAMDMSR();
+	cpuSetAMDMSR(0);
 	post_code(0x34);
 
 	amd_ht_init(sysinfo);
diff --git a/src/mainboard/msi/ms9652_fam10/romstage.c b/src/mainboard/msi/ms9652_fam10/romstage.c
index 4ea3306..5da971f 100644
--- a/src/mainboard/msi/ms9652_fam10/romstage.c
+++ b/src/mainboard/msi/ms9652_fam10/romstage.c
@@ -150,7 +150,7 @@ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx)
 
 	post_code(0x33);
 
-	cpuSetAMDMSR();
+	cpuSetAMDMSR(0);
 	post_code(0x34);
 
 	amd_ht_init(sysinfo);
diff --git a/src/mainboard/supermicro/h8dmr_fam10/romstage.c b/src/mainboard/supermicro/h8dmr_fam10/romstage.c
index c224dbc..1425546 100644
--- a/src/mainboard/supermicro/h8dmr_fam10/romstage.c
+++ b/src/mainboard/supermicro/h8dmr_fam10/romstage.c
@@ -146,7 +146,7 @@ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx)
 
 	post_code(0x33);
 
-	cpuSetAMDMSR();
+	cpuSetAMDMSR(0);
 	post_code(0x34);
 
 	amd_ht_init(sysinfo);
diff --git a/src/mainboard/supermicro/h8qme_fam10/romstage.c b/src/mainboard/supermicro/h8qme_fam10/romstage.c
index 0f9445b..4721eba 100644
--- a/src/mainboard/supermicro/h8qme_fam10/romstage.c
+++ b/src/mainboard/supermicro/h8qme_fam10/romstage.c
@@ -214,7 +214,7 @@ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx)
 
 	post_code(0x33);
 
-	cpuSetAMDMSR();
+	cpuSetAMDMSR(0);
 	post_code(0x34);
 
 	amd_ht_init(sysinfo);
diff --git a/src/mainboard/supermicro/h8scm_fam10/romstage.c b/src/mainboard/supermicro/h8scm_fam10/romstage.c
index 4ea14fe..858aca0 100644
--- a/src/mainboard/supermicro/h8scm_fam10/romstage.c
+++ b/src/mainboard/supermicro/h8scm_fam10/romstage.c
@@ -136,7 +136,7 @@ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx)
 
 	post_code(0x33);
 
-	cpuSetAMDMSR();
+	cpuSetAMDMSR(0);
 	post_code(0x34);
 
 	/* TODO: The Kernel must support 12 processor, otherwise the interrupt
diff --git a/src/mainboard/tyan/s2912_fam10/romstage.c b/src/mainboard/tyan/s2912_fam10/romstage.c
index 0030619..cdf51b1 100644
--- a/src/mainboard/tyan/s2912_fam10/romstage.c
+++ b/src/mainboard/tyan/s2912_fam10/romstage.c
@@ -149,7 +149,7 @@ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx)
 
 	post_code(0x33);
 
-	cpuSetAMDMSR();
+	cpuSetAMDMSR(0);
 	post_code(0x34);
 
 	amd_ht_init(sysinfo);
diff --git a/src/northbridge/amd/amdfam10/Kconfig b/src/northbridge/amd/amdfam10/Kconfig
index ada5b9f..cb0d109 100644
--- a/src/northbridge/amd/amdfam10/Kconfig
+++ b/src/northbridge/amd/amdfam10/Kconfig
@@ -96,7 +96,7 @@ endif
 if HAVE_ACPI_RESUME
 	config S3_DATA_SIZE
 		int
-		default 16384
+		default 32768
 endif
 
 if DIMM_DDR2
diff --git a/src/northbridge/amd/amdfam10/Makefile.inc b/src/northbridge/amd/amdfam10/Makefile.inc
index b4097b4..4098dce 100644
--- a/src/northbridge/amd/amdfam10/Makefile.inc
+++ b/src/northbridge/amd/amdfam10/Makefile.inc
@@ -2,6 +2,8 @@ ifeq ($(CONFIG_NORTHBRIDGE_AMD_AMDFAM10),y)
 
 ramstage-y += northbridge.c
 ramstage-y += misc_control.c
+ramstage-y += link_control.c
+ramstage-y += nb_control.c
 romstage-y += amdfam10_util.c
 ramstage-y += amdfam10_util.c
 
diff --git a/src/northbridge/amd/amdfam10/amdfam10.h b/src/northbridge/amd/amdfam10/amdfam10.h
index a1e08a0..b724394 100644
--- a/src/northbridge/amd/amdfam10/amdfam10.h
+++ b/src/northbridge/amd/amdfam10/amdfam10.h
@@ -962,9 +962,12 @@ that are corresponding to 0x01, 0x02, 0x03, 0x05, 0x06, 0x07
 
 #define LAPIC_MSG_REG 0x380
 #define F10_APSTATE_STARTED 0x13  // start of AP execution
-#define F10_APSTATE_STOPPED 0x14  // allow AP to stop
+#define F10_APSTATE_ASLEEP  0x14  // AP sleeping
+#define F10_APSTATE_STOPPED 0x15  // allow AP to stop
 #define F10_APSTATE_RESET   0x01  // waiting for warm reset
 
+#define MAX_CORES_SUPPORTED 128
+
 #include "nums.h"
 
 #ifdef __PRE_RAM__
@@ -1038,7 +1041,6 @@ struct sys_info {
 
 	struct MCTStatStruc MCTstat;
 	struct DCTStatStruc DCTstatA[NODE_NUMS];
-
 } __attribute__((packed));
 
 #ifdef __PRE_RAM__
diff --git a/src/northbridge/amd/amdfam10/amdfam10_util.c b/src/northbridge/amd/amdfam10/amdfam10_util.c
index 423bb73..a4045bdf 100644
--- a/src/northbridge/amd/amdfam10/amdfam10_util.c
+++ b/src/northbridge/amd/amdfam10/amdfam10_util.c
@@ -34,14 +34,14 @@ u32 Get_NB32(u32 dev, u32 reg)
 }
 #endif
 
-u32 mctGetLogicalCPUID(u32 Node)
+uint64_t mctGetLogicalCPUID(u32 Node)
 {
 	/* Converts the CPUID to a logical ID MASK that is used to check
 	 CPU version support versions */
 	u32 dev;
 	u32 val, valx;
 	u32 family, model, stepping;
-	u32 ret;
+	uint64_t ret;
 
 	if (Node == 0xFF) { /* current node */
 		val = cpuid_eax(0x80000001);
@@ -100,9 +100,16 @@ u32 mctGetLogicalCPUID(u32 Node)
 	case 0x100a0:
 		ret = AMD_PH_E0;
 		break;
+	case 0x15012:
+	case 0x1501f:
+		ret = AMD_OR_B2;
+		break;
+	case 0x15020:
+		ret = AMD_OR_C0;
+		break;
 	default:
 		/* FIXME: mabe we should die() here. */
-		printk(BIOS_ERR, "FIXME! CPU Version unknown or not supported! \n");
+		printk(BIOS_ERR, "FIXME! CPU Version unknown or not supported! %08x\n", valx);
 		ret = 0;
 	}
 
diff --git a/src/northbridge/amd/amdfam10/link_control.c b/src/northbridge/amd/amdfam10/link_control.c
new file mode 100644
index 0000000..1091ef4
--- /dev/null
+++ b/src/northbridge/amd/amdfam10/link_control.c
@@ -0,0 +1,86 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,  MA 02110-1301 USA
+ */
+
+/* Configure various power control registers, including processor
+ * boost support.
+ */
+
+#include <console/console.h>
+#include <device/device.h>
+#include <device/pci.h>
+#include <device/pci_ids.h>
+#include <device/pci_ops.h>
+#include <pc80/mc146818rtc.h>
+#include <lib.h>
+#include <cpu/amd/model_10xxx_rev.h>
+
+#include "amdfam10.h"
+
+static inline uint8_t is_fam15h(void)
+{
+	uint8_t fam15h = 0;
+	uint32_t family;
+
+	family = cpuid_eax(0x80000001);
+	family = ((family & 0xf00000) >> 16) | ((family & 0xf00) >> 8);
+
+	if (family >= 0x6f)
+		/* Family 15h or later */
+		fam15h = 1;
+
+	return fam15h;
+}
+
+static void nb_control_init(struct device *dev)
+{
+	uint32_t dword;
+
+	printk(BIOS_DEBUG, "NB: Function 4 Link Control.. ");
+
+	if (is_fam15h()) {
+		/* Enable APM */
+		dword = pci_read_config32(dev, 0x15c);
+		dword |= (0x1 << 7);			/* ApmMasterEn = 1 */
+		pci_write_config32(dev, 0x15c, dword);
+	}
+
+	printk(BIOS_DEBUG, "done.\n");
+}
+
+
+static struct device_operations mcf4_ops  = {
+	.read_resources   = pci_dev_read_resources,
+	.set_resources    = pci_dev_set_resources,
+	.enable_resources = pci_dev_enable_resources,
+	.init             = nb_control_init,
+	.scan_bus         = 0,
+	.ops_pci          = 0,
+};
+
+static const struct pci_driver mcf4_driver_fam10 __pci_driver = {
+	.ops    = &mcf4_ops,
+	.vendor = PCI_VENDOR_ID_AMD,
+	.device = 0x1204,
+};
+
+static const struct pci_driver mcf4_driver_fam15 __pci_driver = {
+	.ops    = &mcf4_ops,
+	.vendor = PCI_VENDOR_ID_AMD,
+	.device = 0x1604,
+};
\ No newline at end of file
diff --git a/src/northbridge/amd/amdfam10/misc_control.c b/src/northbridge/amd/amdfam10/misc_control.c
index 90a4db1..8777e8f 100644
--- a/src/northbridge/amd/amdfam10/misc_control.c
+++ b/src/northbridge/amd/amdfam10/misc_control.c
@@ -4,6 +4,7 @@
  * Copyright (C) 2003 by Eric Biederman
  * Copyright (C) Stefan Reinauer
  * Copyright (C) 2007 Advanced Micro Devices, Inc.
+ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -152,3 +153,9 @@ static const struct pci_driver mcf3_driver __pci_driver = {
 	.vendor = PCI_VENDOR_ID_AMD,
 	.device = 0x1203,
 };
+
+static const struct pci_driver mcf3_driver_fam15 __pci_driver = {
+	.ops    = &mcf3_ops,
+	.vendor = PCI_VENDOR_ID_AMD,
+	.device = 0x1603,
+};
diff --git a/src/northbridge/amd/amdfam10/nb_control.c b/src/northbridge/amd/amdfam10/nb_control.c
new file mode 100644
index 0000000..f95b6f8
--- /dev/null
+++ b/src/northbridge/amd/amdfam10/nb_control.c
@@ -0,0 +1,85 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,  MA 02110-1301 USA
+ */
+
+/* Configure various power control registers, including processor boost
+ * and TDP monitoring support.
+ */
+
+#include <console/console.h>
+#include <device/device.h>
+#include <device/pci.h>
+#include <device/pci_ids.h>
+#include <device/pci_ops.h>
+#include <pc80/mc146818rtc.h>
+#include <lib.h>
+#include <cpu/amd/model_10xxx_rev.h>
+
+#include "amdfam10.h"
+
+static void nb_control_init(struct device *dev)
+{
+	uint32_t dword;
+	uint32_t f5x80;
+	uint8_t cu_enabled;
+	uint8_t compute_unit_count = 0;
+
+	printk(BIOS_DEBUG, "NB: Function 5 Northbridge Control.. ");
+
+	/* Determine the number of active compute units on this node */
+	f5x80 = pci_read_config32(dev, 0x80);
+	cu_enabled = f5x80 & 0xf;
+	if (cu_enabled == 0x1)
+		compute_unit_count = 1;
+	if (cu_enabled == 0x3)
+		compute_unit_count = 2;
+	if (cu_enabled == 0x7)
+		compute_unit_count = 3;
+	if (cu_enabled == 0xf)
+		compute_unit_count = 4;
+
+	/* Configure Processor TDP Running Average */
+	dword = pci_read_config32(dev, 0xe0);
+	dword &= ~0xf;				/* RunAvgRange = 0x9 */
+	dword |= 0x9;
+	pci_write_config32(dev, 0xe0, dword);
+
+	/* Configure northbridge P-states */
+	dword = pci_read_config32(dev, 0xe0);
+	dword &= ~(0x7 << 9);			/* NbPstateThreshold = compute_unit_count */
+	dword |= (compute_unit_count & 0x7) << 9;
+	pci_write_config32(dev, 0xe0, dword);
+
+	printk(BIOS_DEBUG, "done.\n");
+}
+
+
+static struct device_operations mcf5_ops  = {
+	.read_resources   = pci_dev_read_resources,
+	.set_resources    = pci_dev_set_resources,
+	.enable_resources = pci_dev_enable_resources,
+	.init             = nb_control_init,
+	.scan_bus         = 0,
+	.ops_pci          = 0,
+};
+
+static const struct pci_driver mcf5_driver_fam15 __pci_driver = {
+	.ops    = &mcf5_ops,
+	.vendor = PCI_VENDOR_ID_AMD,
+	.device = 0x1605,
+};
\ No newline at end of file
diff --git a/src/northbridge/amd/amdfam10/northbridge.c b/src/northbridge/amd/amdfam10/northbridge.c
index fb3b2f7..fcf85a7 100644
--- a/src/northbridge/amd/amdfam10/northbridge.c
+++ b/src/northbridge/amd/amdfam10/northbridge.c
@@ -81,6 +81,21 @@ device_t get_node_pci(u32 nodeid, u32 fn)
 #endif
 }
 
+static inline uint8_t is_fam15h(void)
+{
+	uint8_t fam15h = 0;
+	uint32_t family;
+
+	family = cpuid_eax(0x80000001);
+	family = ((family & 0xf00000) >> 16) | ((family & 0xf00) >> 8);
+
+	if (family >= 0x6f)
+		/* Family 15h or later */
+		fam15h = 1;
+
+	return fam15h;
+}
+
 static void get_fx_devs(void)
 {
 	int i;
@@ -202,7 +217,7 @@ static void amd_g34_fixup(struct bus *link, device_t dev)
 		/* Revision D or later */
 		rev_gte_d = 1;
 
-	if (rev_gte_d) {
+	if (rev_gte_d || is_fam15h()) {
 		f3xe8 = pci_read_config32(get_node_pci(0, 3), 0xe8);
 
 		/* Check for dual node capability */
@@ -215,6 +230,15 @@ static void amd_g34_fixup(struct bus *link, device_t dev)
 			*/
 			f3xe8 = pci_read_config32(get_node_pci(nodeid, 3), 0xe8);
 			uint8_t internal_node_number = ((f3xe8 & 0xc0000000) >> 30);
+			uint8_t defective_link_number_1;
+			uint8_t defective_link_number_2;
+			if (is_fam15h()) {
+				defective_link_number_1 = 4;	/* Link 0 Sublink 1 */
+				defective_link_number_2 = 7;	/* Link 3 Sublink 1 */
+			} else {
+				defective_link_number_1 = 6;	/* Link 2 Sublink 1 */
+				defective_link_number_2 = 5;	/* Link 1 Sublink 1 */
+			}
 			if (internal_node_number == 0) {
 				/* Node 0 */
 				if (link->link_num == 6)	/* Link 2 Sublink 1 */
@@ -314,6 +338,46 @@ static void amdfam10_scan_chains(device_t dev)
 {
 	struct bus *link;
 
+#if CONFIG_CPU_AMD_SOCKET_G34_NON_AGESA
+	if (is_fam15h()) {
+		uint8_t current_link_number = 0;
+
+		for (link = dev->link_list; link; link = link->next) {
+			/* The following links have changed position in Fam15h G34 processors:
+			 * Fam10  Fam15
+			 * Node 0
+			 * L3 --> L1
+			 * L0 --> L3
+			 * L1 --> L2
+			 * L2 --> L0
+			 * Node 1
+			 * L0 --> L0
+			 * L1 --> L3
+			 * L2 --> L1
+			 * L3 --> L2
+			 */
+			if (link->link_num == 0)
+				link->link_num = 3;
+			else if (link->link_num == 1)
+				link->link_num = 2;
+			else if (link->link_num == 2)
+				link->link_num = 0;
+			else if (link->link_num == 3)
+				link->link_num = 1;
+			else if (link->link_num == 5)
+				link->link_num = 7;
+			else if (link->link_num == 6)
+				link->link_num = 5;
+			else if (link->link_num == 7)
+				link->link_num = 6;
+
+			current_link_number++;
+			if (current_link_number > 3)
+				current_link_number = 0;
+		}
+	}
+#endif
+
 	/* Do sb ht chain at first, in case s2885 put sb chain (8131/8111) on link2, but put 8151 on link0 */
 	trim_ht_chain(dev);
 
@@ -620,13 +684,21 @@ static const struct pci_driver mcf0_driver __pci_driver = {
 	.device = 0x1200,
 };
 
+
 static void amdfam10_nb_init(void *chip_info)
 {
 	relocate_sb_ht_chain();
 }
 
+static const struct pci_driver mcf0_driver_fam15 __pci_driver = {
+	.ops	= &northbridge_operations,
+	.vendor = PCI_VENDOR_ID_AMD,
+	.device = 0x1600,
+};
+
+
 struct chip_operations northbridge_amd_amdfam10_ops = {
-	CHIP_NAME("AMD FAM10 Northbridge")
+	CHIP_NAME("AMD Family 10h/15h Northbridge")
 	.enable_dev = 0,
 	.init = amdfam10_nb_init,
 };
@@ -950,38 +1022,61 @@ static int amdfam10_get_smbios_data16(int* count, int handle, unsigned long *cur
 
 static uint16_t amdmct_mct_speed_enum_to_mhz(uint8_t speed)
 {
-	if (IS_ENABLED(CONFIG_DIMM_DDR2)) {
-		switch (speed) {
-			case 1:
-				return 200;
-			case 2:
-				return 266;
-			case 3:
-				return 333;
-			case 4:
-				return 400;
-			case 5:
-				return 533;
-			default:
-				return 0;
-		}
-	} else if (IS_ENABLED(CONFIG_DIMM_DDR3)) {
-		switch (speed) {
-			case 3:
-				return 333;
-			case 4:
-				return 400;
-			case 5:
-				return 533;
-			case 6:
-				return 667;
-			case 7:
-				return 800;
-			default:
-				return 0;
+	if (is_fam15h()) {
+		if (IS_ENABLED(CONFIG_DIMM_DDR3)) {
+			switch (speed) {
+				case 0x4:
+					return 333;
+				case 0x6:
+					return 400;
+				case 0xa:
+					return 533;
+				case 0xe:
+					return 667;
+				case 0x12:
+					return 800;
+				case 0x16:
+					return 933;
+				default:
+					return 0;
+			}
+		} else {
+			return 0;
 		}
 	} else {
-		return 0;
+		if (IS_ENABLED(CONFIG_DIMM_DDR2)) {
+			switch (speed) {
+				case 1:
+					return 200;
+				case 2:
+					return 266;
+				case 3:
+					return 333;
+				case 4:
+					return 400;
+				case 5:
+					return 533;
+				default:
+					return 0;
+			}
+		} else if (IS_ENABLED(CONFIG_DIMM_DDR3)) {
+			switch (speed) {
+				case 3:
+					return 333;
+				case 4:
+					return 400;
+				case 5:
+					return 533;
+				case 6:
+					return 667;
+				case 7:
+					return 800;
+				default:
+					return 0;
+			}
+		} else {
+			return 0;
+		}
 	}
 }
 
@@ -1076,6 +1171,8 @@ static int amdfam10_get_smbios_data17(int* count, int handle, int parent_handle,
 #if IS_ENABLED(CONFIG_DIMM_DDR3)
 					/* Find the maximum and minimum supported voltages */
 					uint8_t supported_voltages = mem_info->dct_stat[node].DimmSupportedVoltages[slot];
+					uint8_t configured_voltage = mem_info->dct_stat[node].DimmConfiguredVoltage[slot];
+
 					if (supported_voltages & 0x8)
 						t->minimum_voltage = 1150;
 					else if (supported_voltages & 0x4)
@@ -1094,7 +1191,14 @@ static int amdfam10_get_smbios_data17(int* count, int handle, int parent_handle,
 					else if (supported_voltages & 0x8)
 						t->maximum_voltage = 1150;
 
-					t->configured_voltage = mem_info->dct_stat[node].DimmConfiguredVoltage[slot];
+					if (configured_voltage & 0x8)
+						t->configured_voltage = 1150;
+					else if (configured_voltage & 0x4)
+						t->configured_voltage = 1250;
+					else if (configured_voltage & 0x2)
+						t->configured_voltage = 1350;
+					else if (configured_voltage & 0x1)
+						t->configured_voltage = 1500;
 #endif
 				}
 				t->memory_error_information_handle = 0xFFFE;	/* no error information handle available */
@@ -1233,12 +1337,14 @@ static void cpu_bus_scan(device_t dev)
 #if CONFIG_CBB
 	device_t pci_domain;
 #endif
+	int nvram = 0;
 	int i,j;
 	int nodes;
 	unsigned nb_cfg_54;
 	unsigned siblings;
 	int cores_found;
 	int disable_siblings;
+	uint8_t disable_cu_siblings = 0;
 	unsigned ApicIdCoreIdSize;
 
 	nb_cfg_54 = 0;
@@ -1325,14 +1431,23 @@ static void cpu_bus_scan(device_t dev)
 	/* Always use the devicetree node with lapic_id 0 for BSP. */
 	remap_bsp_lapic(cpu_bus);
 
+	if (get_option(&nvram, "compute_unit_siblings") == CB_SUCCESS)
+		disable_cu_siblings = !!nvram;
+
+	if (disable_cu_siblings)
+		printk(BIOS_DEBUG, "Disabling siblings on each compute unit as requested\n");
+
 	for(i = 0; i < nodes; i++) {
 		device_t cdb_dev;
 		unsigned busn, devn;
 		struct bus *pbus;
 
+		uint8_t fam15h = 0;
 		uint8_t rev_gte_d = 0;
 		uint8_t dual_node = 0;
 		uint32_t f3xe8;
+		uint32_t family;
+		uint32_t model;
 
 		busn = CONFIG_CBB;
 		devn = CONFIG_CDB+i;
@@ -1372,7 +1487,16 @@ static void cpu_bus_scan(device_t dev)
 
 		f3xe8 = pci_read_config32(get_node_pci(0, 3), 0xe8);
 
-		if (cpuid_eax(0x80000001) >= 0x8)
+		family = model = cpuid_eax(0x80000001);
+		model = ((model & 0xf0000) >> 12) | ((model & 0xf0) >> 4);
+
+		if (is_fam15h()) {
+			/* Family 15h or later */
+			fam15h = 1;
+			nb_cfg_54 = 1;
+		}
+
+		if ((model >= 0x8) || fam15h)
 			/* Revision D or later */
 			rev_gte_d = 1;
 
@@ -1382,13 +1506,20 @@ static void cpu_bus_scan(device_t dev)
 				dual_node = 1;
 
 		cores_found = 0; // one core
-		cdb_dev = dev_find_slot(busn, PCI_DEVFN(devn, 3));
+		if (fam15h)
+			cdb_dev = dev_find_slot(busn, PCI_DEVFN(devn, 5));
+		else
+			cdb_dev = dev_find_slot(busn, PCI_DEVFN(devn, 3));
 		int enable_node = cdb_dev && cdb_dev->enabled;
 		if (enable_node) {
-			j = pci_read_config32(cdb_dev, 0xe8);
-			cores_found = (j >> 12) & 3; // dev is func 3
-			if (siblings > 3)
-				cores_found |= (j >> 13) & 4;
+			if (fam15h) {
+				cores_found = pci_read_config32(cdb_dev, 0x84) & 0xff;
+			} else {
+				j = pci_read_config32(cdb_dev, 0xe8);
+				cores_found = (j >> 12) & 3; // dev is func 3
+				if (siblings > 3)
+					cores_found |= (j >> 13) & 4;
+			}
 			printk(BIOS_DEBUG, "  %s siblings=%d\n", dev_path(cdb_dev), cores_found);
 		}
 
@@ -1408,15 +1539,24 @@ static void cpu_bus_scan(device_t dev)
 
 			if (dual_node) {
 				apic_id = 0;
-				if (nb_cfg_54) {
-					apic_id |= ((i >> 1) & 0x3) << 4;			/* Node ID */
+				if (fam15h) {
+					apic_id |= ((i >> 1) & 0x3) << 5;			/* Node ID */
 					apic_id |= ((i & 0x1) * (siblings + 1)) + j;		/* Core ID */
 				} else {
-					apic_id |= i & 0x3;					/* Node ID */
-					apic_id |= (((i & 0x1) * (siblings + 1)) + j) << 4;	/* Core ID */
+					if (nb_cfg_54) {
+						apic_id |= ((i >> 1) & 0x3) << 4;			/* Node ID */
+						apic_id |= ((i & 0x1) * (siblings + 1)) + j;		/* Core ID */
+					} else {
+						apic_id |= i & 0x3;					/* Node ID */
+						apic_id |= (((i & 0x1) * (siblings + 1)) + j) << 4;	/* Core ID */
+					}
 				}
 			} else {
-				apic_id = i * (nb_cfg_54?(siblings+1):1) + j * (nb_cfg_54?1:64); // ?
+				if (fam15h) {
+					apic_id = (i * (siblings + 1)) + j;
+				} else {
+					apic_id = i * (nb_cfg_54?(siblings+1):1) + j * (nb_cfg_54?1:64); // ?
+				}
 			}
 
 #if CONFIG_ENABLE_APIC_EXT_ID && (CONFIG_APIC_ID_OFFSET>0)
@@ -1426,6 +1566,9 @@ static void cpu_bus_scan(device_t dev)
 				}
 			}
 #endif
+			if (disable_cu_siblings && (j & 0x1))
+				continue;
+
 			device_t cpu = add_cpu_device(cpu_bus, apic_id, enable_node);
 			if (cpu)
 				amd_cpu_topology(cpu, i, j);
@@ -1484,6 +1627,6 @@ static void root_complex_enable_dev(struct device *dev)
 }
 
 struct chip_operations northbridge_amd_amdfam10_root_complex_ops = {
-	CHIP_NAME("AMD FAM10 Root Complex")
+	CHIP_NAME("AMD Family 10h/15h Root Complex")
 	.enable_dev = root_complex_enable_dev,
 };
diff --git a/src/northbridge/amd/amdfam10/raminit_amdmct.c b/src/northbridge/amd/amdfam10/raminit_amdmct.c
index 5068e7a..cae228f 100644
--- a/src/northbridge/amd/amdfam10/raminit_amdmct.c
+++ b/src/northbridge/amd/amdfam10/raminit_amdmct.c
@@ -44,8 +44,120 @@ static  void print_tf(const char *func, const char *strval)
 #endif
 }
 
-static uint16_t mct_MaxLoadFreq(uint8_t count, uint8_t registered, uint16_t freq)
+static inline void fam15h_switch_dct(uint32_t dev, uint8_t dct)
 {
+	uint32_t dword;
+
+	dword = Get_NB32(dev, 0x10c);
+	dword &= ~0x1;
+	dword |= (dct & 0x1);
+	Set_NB32(dev, 0x10c, dword);
+}
+
+static inline void fam15h_switch_nb_pstate_config_reg(uint32_t dev, uint8_t nb_pstate)
+{
+	uint32_t dword;
+
+	dword = Get_NB32(dev, 0x10c);
+	dword &= ~(0x3 << 4);
+	dword |= (nb_pstate & 0x3) << 4;
+	Set_NB32(dev, 0x10c, dword);
+}
+
+static inline uint32_t Get_NB32_DCT(uint32_t dev, uint8_t dct, uint32_t reg)
+{
+	if (is_fam15h()) {
+		/* Obtain address of function 0x1 */
+		uint32_t dev_map = (dev & (~(0x7 << 12))) | (0x1 << 12);
+		fam15h_switch_dct(dev_map, dct);
+		return Get_NB32(dev, reg);
+	} else {
+		return Get_NB32(dev, (0x100 * dct) + reg);
+	}
+}
+
+static inline void Set_NB32_DCT(uint32_t dev, uint8_t dct, uint32_t reg, uint32_t val)
+{
+	if (is_fam15h()) {
+		/* Obtain address of function 0x1 */
+		uint32_t dev_map = (dev & (~(0x7 << 12))) | (0x1 << 12);
+		fam15h_switch_dct(dev_map, dct);
+		Set_NB32(dev, reg, val);
+	} else {
+		Set_NB32(dev, (0x100 * dct) + reg, val);
+	}
+}
+
+static inline uint32_t Get_NB32_DCT_NBPstate(uint32_t dev, uint8_t dct, uint8_t nb_pstate, uint32_t reg)
+{
+	if (is_fam15h()) {
+		/* Obtain address of function 0x1 */
+		uint32_t dev_map = (dev & (~(0x7 << 12))) | (0x1 << 12);
+		fam15h_switch_dct(dev_map, dct);
+		fam15h_switch_nb_pstate_config_reg(dev_map, nb_pstate);
+		return Get_NB32(dev, reg);
+	} else {
+		return Get_NB32(dev, (0x100 * dct) + reg);
+	}
+}
+
+static inline void Set_NB32_DCT_NBPstate(uint32_t dev, uint8_t dct, uint8_t nb_pstate, uint32_t reg, uint32_t val)
+{
+	if (is_fam15h()) {
+		/* Obtain address of function 0x1 */
+		uint32_t dev_map = (dev & (~(0x7 << 12))) | (0x1 << 12);
+		fam15h_switch_dct(dev_map, dct);
+		fam15h_switch_nb_pstate_config_reg(dev_map, nb_pstate);
+		Set_NB32(dev, reg, val);
+	} else {
+		Set_NB32(dev, (0x100 * dct) + reg, val);
+	}
+}
+
+static inline uint32_t Get_NB32_index_wait_DCT(uint32_t dev, uint8_t dct, uint32_t index_reg, uint32_t index)
+{
+	if (is_fam15h()) {
+		/* Obtain address of function 0x1 */
+		uint32_t dev_map = (dev & (~(0x7 << 12))) | (0x1 << 12);
+		fam15h_switch_dct(dev_map, dct);
+		return Get_NB32_index_wait(dev, index_reg, index);
+	} else {
+		return Get_NB32_index_wait(dev, (0x100 * dct) + index_reg, index);
+	}
+}
+
+static inline void Set_NB32_index_wait_DCT(uint32_t dev, uint8_t dct, uint32_t index_reg, uint32_t index, uint32_t data)
+{
+	if (is_fam15h()) {
+		/* Obtain address of function 0x1 */
+		uint32_t dev_map = (dev & (~(0x7 << 12))) | (0x1 << 12);
+		fam15h_switch_dct(dev_map, dct);
+		Set_NB32_index_wait(dev, index_reg, index, data);
+	} else {
+		Set_NB32_index_wait(dev, (0x100 * dct) + index_reg, index, data);
+	}
+}
+
+static uint16_t voltage_index_to_mv(uint8_t index)
+{
+	if (index & 0x8)
+		return 1150;
+	if (index & 0x4)
+		return 1250;
+	else if (index & 0x2)
+		return 1350;
+	else
+		return 1500;
+}
+
+static uint16_t mct_MaxLoadFreq(uint8_t count, uint8_t highest_rank_count, uint8_t registered, uint8_t voltage, uint16_t freq)
+{
+	/* FIXME
+	 * Mainboards need to be able to specify the maximum number of DIMMs installable per channel
+	 * For now assume a maximum of 2 DIMMs per channel can be installed
+	 */
+	uint8_t MaxDimmsInstallable = 2;
+
 	/* Return limited maximum RAM frequency */
 	if (IS_ENABLED(CONFIG_DIMM_DDR2)) {
 		if (IS_ENABLED(CONFIG_DIMM_REGISTERED) && registered) {
@@ -68,34 +180,178 @@ static uint16_t mct_MaxLoadFreq(uint8_t count, uint8_t registered, uint16_t freq
 			}
 		}
 	} else if (IS_ENABLED(CONFIG_DIMM_DDR3)) {
-		if (IS_ENABLED(CONFIG_DIMM_REGISTERED) && registered) {
-			/* K10 BKDG Rev. 3.62 Table 34 */
-			if (count > 2) {
-				/* Limit to DDR3-800 */
-				if (freq > 400) {
-					freq = 400;
-					print_tf(__func__, ": More than 2 registered DIMMs on channel; limiting to DDR3-800\n");
+		if (voltage == 0) {
+			printk(BIOS_DEBUG, "%s: WARNING: Mainboard DDR3 voltage unknown, assuming 1.5V!\n", __func__);
+			voltage = 0x1;
+		}
+
+		if (is_fam15h()) {
+			if (IS_ENABLED(CONFIG_DIMM_REGISTERED) && registered) {
+				/* Fam15h BKDG Rev. 3.14 Table 27 */
+				if (voltage & 0x4) {
+					/* 1.25V */
+					if (count > 1) {
+						if (highest_rank_count > 1) {
+							/* Limit to DDR3-1066 */
+							if (freq > 533) {
+								freq = 533;
+								printk(BIOS_DEBUG, "%s: More than 1 registered DIMM on %dmV channel; limiting to DDR3-1066\n", __func__, voltage_index_to_mv(voltage));
+							}
+						} else {
+							/* Limit to DDR3-1333 */
+							if (freq > 666) {
+								freq = 666;
+								printk(BIOS_DEBUG, "%s: More than 1 registered DIMM on %dmV channel; limiting to DDR3-1333\n", __func__, voltage_index_to_mv(voltage));
+							}
+						}
+					} else {
+						/* Limit to DDR3-1333 */
+						if (freq > 666) {
+							freq = 666;
+							printk(BIOS_DEBUG, "%s: 1 registered DIMM on %dmV channel; limiting to DDR3-1333\n", __func__, voltage_index_to_mv(voltage));
+						}
+					}
+				} else if (voltage & 0x2) {
+					/* 1.35V */
+					if (count > 1) {
+						/* Limit to DDR3-1333 */
+						if (freq > 666) {
+							freq = 666;
+							printk(BIOS_DEBUG, "%s: More than 1 registered DIMM on %dmV channel; limiting to DDR3-1333\n", __func__, voltage_index_to_mv(voltage));
+						}
+					} else {
+						/* Limit to DDR3-1600 */
+						if (freq > 800) {
+							freq = 800;
+							printk(BIOS_DEBUG, "%s: 1 registered DIMM on %dmV channel; limiting to DDR3-1600\n", __func__, voltage_index_to_mv(voltage));
+						}
+					}
+				} else if (voltage & 0x1) {
+					/* 1.50V */
+					if (count > 1) {
+						/* Limit to DDR3-1600 */
+						if (freq > 800) {
+							freq = 800;
+							printk(BIOS_DEBUG, "%s: More than 1 registered DIMM on %dmV channel; limiting to DDR3-1600\n", __func__, voltage_index_to_mv(voltage));
+						}
+					} else {
+						/* Limit to DDR3-1866 */
+						if (freq > 933) {
+							freq = 933;
+							printk(BIOS_DEBUG, "%s: 1 registered DIMM on %dmV channel; limiting to DDR3-1866\n", __func__, voltage_index_to_mv(voltage));
+						}
+					}
+				}
+			} else {
+				/* Fam15h BKDG Rev. 3.14 Table 26 */
+				if (voltage & 0x4) {
+					/* 1.25V */
+					if (count > 1) {
+						if (highest_rank_count > 1) {
+							/* Limit to DDR3-1066 */
+							if (freq > 533) {
+								freq = 533;
+								printk(BIOS_DEBUG, "%s: More than 1 unbuffered DIMM on %dmV channel; limiting to DDR3-1066\n", __func__, voltage_index_to_mv(voltage));
+							}
+						} else {
+							/* Limit to DDR3-1333 */
+							if (freq > 666) {
+								freq = 666;
+								printk(BIOS_DEBUG, "%s: More than 1 unbuffered DIMM on %dmV channel; limiting to DDR3-1333\n", __func__, voltage_index_to_mv(voltage));
+							}
+						}
+					} else {
+						/* Limit to DDR3-1333 */
+						if (freq > 666) {
+							freq = 666;
+							printk(BIOS_DEBUG, "%s: 1 unbuffered DIMM on %dmV channel; limiting to DDR3-1333\n", __func__, voltage_index_to_mv(voltage));
+						}
+					}
+				} else if (voltage & 0x2) {
+					/* 1.35V */
+					if (MaxDimmsInstallable > 1) {
+						/* Limit to DDR3-1333 */
+						if (freq > 666) {
+							freq = 666;
+							printk(BIOS_DEBUG, "%s: More than 1 unbuffered DIMM on %dmV channel; limiting to DDR3-1333\n", __func__, voltage_index_to_mv(voltage));
+						}
+					} else {
+						/* Limit to DDR3-1600 */
+						if (freq > 800) {
+							freq = 800;
+							printk(BIOS_DEBUG, "%s: 1 unbuffered DIMM on %dmV channel; limiting to DDR3-1600\n", __func__, voltage_index_to_mv(voltage));
+						}
+					}
+				} else if (voltage & 0x1) {
+					if (MaxDimmsInstallable == 1) {
+						if (count > 1) {
+							/* Limit to DDR3-1600 */
+							if (freq > 800) {
+								freq = 800;
+								printk(BIOS_DEBUG, "%s: More than 1 unbuffered DIMM on %dmV channel; limiting to DDR3-1600\n", __func__, voltage_index_to_mv(voltage));
+							}
+						} else {
+							/* Limit to DDR3-1866 */
+							if (freq > 933) {
+								freq = 933;
+								printk(BIOS_DEBUG, "%s: 1 unbuffered DIMM on %dmV channel; limiting to DDR3-1866\n", __func__, voltage_index_to_mv(voltage));
+							}
+						}
+					} else {
+						if (count > 1) {
+							if (highest_rank_count > 1) {
+								/* Limit to DDR3-1333 */
+								if (freq > 666) {
+									freq = 666;
+									printk(BIOS_DEBUG, "%s: More than 1 unbuffered DIMM on %dmV channel; limiting to DDR3-1333\n", __func__, voltage_index_to_mv(voltage));
+								}
+							} else {
+								/* Limit to DDR3-1600 */
+								if (freq > 800) {
+									freq = 800;
+									printk(BIOS_DEBUG, "%s: More than 1 unbuffered DIMM on %dmV channel; limiting to DDR3-1600\n", __func__, voltage_index_to_mv(voltage));
+								}
+							}
+						} else {
+							/* Limit to DDR3-1600 */
+							if (freq > 800) {
+								freq = 800;
+								printk(BIOS_DEBUG, "%s: 1 unbuffered DIMM on %dmV channel; limiting to DDR3-1600\n", __func__, voltage_index_to_mv(voltage));
+							}
+						}
+					}
 				}
-			} else if (count == 2) {
-				/* Limit to DDR3-1066 */
-				if (freq > 533) {
-					freq = 533;
-					print_tf(__func__, ": 2 registered DIMMs on channel; limiting to DDR3-1066\n");
+			}
+		} else {
+			if (IS_ENABLED(CONFIG_DIMM_REGISTERED) && registered) {
+				/* K10 BKDG Rev. 3.62 Table 34 */
+				if (count > 2) {
+					/* Limit to DDR3-800 */
+					if (freq > 400) {
+						freq = 400;
+						printk(BIOS_DEBUG, "%s: More than 2 registered DIMMs on %dmV channel; limiting to DDR3-800\n", __func__, voltage_index_to_mv(voltage));
+					}
+				} else if (count == 2) {
+					/* Limit to DDR3-1066 */
+					if (freq > 533) {
+						freq = 533;
+						printk(BIOS_DEBUG, "%s: 2 registered DIMMs on %dmV channel; limiting to DDR3-1066\n", __func__, voltage_index_to_mv(voltage));
+					}
+				} else {
+					/* Limit to DDR3-1333 */
+					if (freq > 666) {
+						freq = 666;
+						printk(BIOS_DEBUG, "%s: 1 registered DIMM on %dmV channel; limiting to DDR3-1333\n", __func__, voltage_index_to_mv(voltage));
+					}
 				}
 			} else {
+				/* K10 BKDG Rev. 3.62 Table 33 */
 				/* Limit to DDR3-1333 */
 				if (freq > 666) {
 					freq = 666;
-					print_tf(__func__, ": 1 registered DIMM on channel; limiting to DDR3-1333\n");
+					printk(BIOS_DEBUG, "%s: unbuffered DIMMs on %dmV channel; limiting to DDR3-1333\n", __func__, voltage_index_to_mv(voltage));
 				}
 			}
-		} else {
-			/* K10 BKDG Rev. 3.62 Table 33 */
-			/* Limit to DDR3-1333 */
-			if (freq > 666) {
-				freq = 666;
-				print_tf(__func__, ": unbuffered DIMMs on channel; limiting to DDR3-1333\n");
-			}
 		}
 	}
 
@@ -225,11 +481,13 @@ void mctGet_DIMMAddr(struct DCTStatStruc *pDCTstat, u32 node)
 
 }
 
+#if IS_ENABLED(CONFIG_SET_FIDVID)
 static u8 mctGetProcessorPackageType(void) {
 	/* FIXME: I guess this belongs wherever mctGetLogicalCPUID ends up ? */
-     u32 BrandId = cpuid_ebx(0x80000001);
-     return (u8)((BrandId >> 28) & 0x0F);
+	u32 BrandId = cpuid_ebx(0x80000001);
+	return (u8)((BrandId >> 28) & 0x0F);
 }
+#endif
 
 static void raminit_amdmct(struct sys_info *sysinfo)
 {
diff --git a/src/northbridge/amd/amdht/h3ncmn.c b/src/northbridge/amd/amdht/h3ncmn.c
index 97f9db8..8f9177f 100644
--- a/src/northbridge/amd/amdht/h3ncmn.c
+++ b/src/northbridge/amd/amdht/h3ncmn.c
@@ -43,6 +43,7 @@
 #define CPU_HTNB_FUNC_04		4
 #define CPU_ADDR_FUNC_01		1
 #define CPU_NB_FUNC_03			3
+#define CPU_NB_FUNC_05			5
 
 /* Function 0 registers */
 #define REG_ROUTE0_0X40		0x40
@@ -70,6 +71,7 @@
 #define REG_NB_CPUID_3XFC		0xFC
 #define REG_NB_LINK_XCS_TOKEN0_3X148	0x148
 #define REG_NB_DOWNCORE_3X190		0x190
+#define REG_NB_CAPABILITY_5X84		0x84
 
 /* Function 4 registers */
 
@@ -555,9 +557,10 @@ static u8 fam10GetNumCoresOnNode(u8 node, cNorthBridge *nb)
 				15, 12, &temp);
 
 	/* bits[15,13,12] specify the cores */
-	/* Support Downcoring */
 	temp = ((temp & 8) >> 1) + (temp & 3);
 	cores = temp + 1;
+
+	/* Support Downcoring */
 	AmdPCIReadBits (MAKE_SBDFO(makePCISegmentFromNode(node),
 					makePCIBusFromNode(node),
 					makePCIDeviceFromNode(node),
@@ -576,6 +579,56 @@ static u8 fam10GetNumCoresOnNode(u8 node, cNorthBridge *nb)
 
 /***************************************************************************//**
  *
+ * static u8
+ * fam15GetNumCoresOnNode(u8 node, cNorthBridge *nb)
+ *
+ *  Description:
+ *	Return the number of cores (1 based count) on node.
+ *
+ *  Parameters:
+ *	@param[in]  node      = the node that will be examined
+ *	@param[in] *nb = this northbridge
+ *	@return    = the number of cores
+ *
+ *
+ */
+static u8 fam15GetNumCoresOnNode(u8 node, cNorthBridge *nb)
+{
+	u32 temp, leveling, cores;
+	u8 i;
+
+	ASSERT((node < nb->maxNodes));
+	/* Read CmpCap [7:0] */
+	AmdPCIReadBits(MAKE_SBDFO(makePCISegmentFromNode(node),
+				makePCIBusFromNode(node),
+				makePCIDeviceFromNode(node),
+				CPU_NB_FUNC_05,
+				REG_NB_CAPABILITY_5X84),
+				7, 0, &temp);
+
+	/* bits[7:0] specify the cores */
+	temp = temp & 0xff;
+	cores = temp + 1;
+
+	/* Support Downcoring */
+	AmdPCIReadBits (MAKE_SBDFO(makePCISegmentFromNode(node),
+					makePCIBusFromNode(node),
+					makePCIDeviceFromNode(node),
+					CPU_NB_FUNC_03,
+					REG_NB_DOWNCORE_3X190),
+					31, 0, &leveling);
+	for (i=0; i<cores; i++)
+	{
+		if (leveling & ((u32) 1 << i))
+		{
+			temp--;
+		}
+	}
+	return (u8)(temp+1);
+}
+
+/***************************************************************************//**
+ *
  * static void
  * setTotalNodesAndCores(u8 node, u8 totalNodes, u8 totalCores, cNorthBridge *nb)
  *
@@ -854,6 +907,69 @@ static BOOL fam10IsCapable(u8 node, sMainData *pDat, cNorthBridge *nb)
 
 /***************************************************************************//**
  *
+ * static BOOL
+ * fam15IsCapable(u8 node, sMainData *pDat, cNorthBridge *nb)
+ *
+ *  Description:
+ *	Get node capability and update the minimum supported system capability.
+ *	Return whether the current configuration exceeds the capability.
+ *
+ *  Parameters:
+ *	@param[in]  node   = the node
+ *	@param[in,out] *pDat = sysMpCap (updated) and NodesDiscovered
+ *	@param[in] *nb   = this northbridge
+ *	@return             true: system is capable of current config.
+ *			   false: system is not capable of current config.
+ *
+ * ---------------------------------------------------------------------------------------
+ */
+static BOOL fam15IsCapable(u8 node, sMainData *pDat, cNorthBridge *nb)
+{
+#ifndef HT_BUILD_NC_ONLY
+	u32 temp;
+	u8 maxNodes;
+
+	ASSERT(node < nb->maxNodes);
+
+	AmdPCIReadBits(MAKE_SBDFO(makePCISegmentFromNode(node),
+				makePCIBusFromNode(node),
+				makePCIDeviceFromNode(node),
+				CPU_NB_FUNC_03,
+				REG_NB_CAPABILITY_3XE8),
+				18, 16, &temp);
+
+	if (temp != 0)
+	{
+		maxNodes = (1 << (~temp & 0x3));  /* That is, 1, 2, 4, or 8 */
+	}
+	else
+	{
+		/* Check if CPU package is dual node */
+		AmdPCIReadBits(MAKE_SBDFO(makePCISegmentFromNode(node),
+					makePCIBusFromNode(node),
+					makePCIDeviceFromNode(node),
+					CPU_NB_FUNC_03,
+					REG_NB_CAPABILITY_3XE8),
+					29, 29, &temp);
+		if (temp)
+			maxNodes = 4;
+		else
+			maxNodes = 8;
+	}
+
+	if (pDat->sysMpCap > maxNodes)
+	{
+		pDat->sysMpCap = maxNodes;
+	}
+	/* Note since sysMpCap is one based and NodesDiscovered is zero based, equal is false */
+	return (pDat->sysMpCap > pDat->NodesDiscovered);
+#else
+	return 1;
+#endif
+}
+
+/***************************************************************************//**
+ *
  * static void
  * fam0fStopLink(u8 currentNode, u8 currentLink, cNorthBridge *nb)
  *
@@ -2068,6 +2184,49 @@ void newNorthBridge(u8 node, cNorthBridge *nb)
 	u32 match;
 	u32 extFam, baseFam, model;
 
+	cNorthBridge fam15 =
+	{
+#ifdef HT_BUILD_NC_ONLY
+		8,
+		1,
+		12,
+#else
+		8,
+		8,
+		64,
+#endif /* HT_BUILD_NC_ONLY*/
+		writeRoutingTable,
+		writeNodeID,
+		readDefLnk,
+		enableRoutingTables,
+		verifyLinkIsCoherent,
+		readTrueLinkFailStatus,
+		readToken,
+		writeToken,
+		fam15GetNumCoresOnNode,
+		setTotalNodesAndCores,
+		limitNodes,
+		writeFullRoutingTable,
+		isCompatible,
+		fam15IsCapable,
+		(void (*)(u8, u8, cNorthBridge*))commonVoid,
+		(BOOL (*)(u8, u8, sMainData*, cNorthBridge*))commonReturnFalse,
+		readSbLink,
+		verifyLinkIsNonCoherent,
+		ht3SetCFGAddrMap,
+		convertBitsToWidth,
+		convertWidthToBits,
+		fam10NorthBridgeFreqMask,
+		gatherLinkData,
+		setLinkData,
+		ht3WriteTrafficDistribution,
+		fam10BufferOptimizations,
+		0x00000001,
+		0x00000200,
+		18,
+		0x00000f06
+	};
+
 	cNorthBridge fam10 =
 	{
 #ifdef HT_BUILD_NC_ONLY
@@ -2175,8 +2334,14 @@ void newNorthBridge(u8 node, cNorthBridge *nb)
 			7, 4, &model);
 	match = (u32)((baseFam << 8) | extFam);
 
-	/* Test each in turn looking for a match.	Init the struct if found */
-	if (match == fam10.compatibleKey)
+	/* Test each in turn looking for a match.
+	 * Initialize the struct if found.
+	 */
+	if (match == fam15.compatibleKey)
+	{
+		Amdmemcpy((void *)nb, (const void *)&fam15, (u32) sizeof(cNorthBridge));
+	}
+	else if (match == fam10.compatibleKey)
 	{
 		Amdmemcpy((void *)nb, (const void *)&fam10, (u32) sizeof(cNorthBridge));
 	}
diff --git a/src/northbridge/amd/amdht/ht_wrapper.c b/src/northbridge/amd/amdht/ht_wrapper.c
index 389b1b1..c0ccc69 100644
--- a/src/northbridge/amd/amdht/ht_wrapper.c
+++ b/src/northbridge/amd/amdht/ht_wrapper.c
@@ -174,16 +174,22 @@ void amd_ht_fixup(struct sys_info *sysinfo) {
 	printk(BIOS_DEBUG, "amd_ht_fixup()\n");
 	if (IS_ENABLED(CONFIG_CPU_AMD_MODEL_10XXX)) {
 		uint8_t rev_gte_d = 0;
+		uint8_t fam15h = 0;
 		uint8_t dual_node = 0;
 		uint32_t f3xe8;
 		uint32_t family;
 		uint32_t model;
 
 		family = model = cpuid_eax(0x80000001);
-		model = ((model & 0xf0000) >> 16) | ((model & 0xf0) >> 4);
+		model = ((model & 0xf0000) >> 12) | ((model & 0xf0) >> 4);
+		family = ((family & 0xf00000) >> 16) | ((family & 0xf00) >> 8);
 
-		if (model >= 0x8)
-			/* Revision D or later */
+		if (family >= 0x6f)
+			/* Family 15h or later */
+			fam15h = 1;
+
+		if ((model >= 0x8) || fam15h)
+			/* Family 10h Revision D or later */
 			rev_gte_d = 1;
 
 		if (rev_gte_d) {
@@ -195,7 +201,8 @@ void amd_ht_fixup(struct sys_info *sysinfo) {
 
 			if (dual_node) {
 				/* Each G34 processor contains a defective HT link.
-				* See the BKDG Rev 3.62 section 2.7.1.5 for details.
+				* See the Family 10h BKDG Rev 3.62 section 2.7.1.5 for details
+				* For Family 15h see the BKDG Rev. 3.14 section 2.12.1.5 for details.
 				*/
 				uint8_t node;
 				uint8_t node_count = get_nodes();
@@ -205,46 +212,46 @@ void amd_ht_fixup(struct sys_info *sysinfo) {
 					uint8_t internal_node_number = ((f3xe8 & 0xc0000000) >> 30);
 					printk(BIOS_DEBUG, "amd_ht_fixup(): node %d (internal node ID %d): disabling defective HT link\n", node, internal_node_number);
 					if (internal_node_number == 0) {
-						uint8_t package_link_3_connected = pci_read_config32(NODE_PCI(node, 0), 0xd8) & 0x1;
+						uint8_t package_link_3_connected = pci_read_config32(NODE_PCI(node, 0), (fam15h)?0x98:0xd8) & 0x1;
 						if (package_link_3_connected) {
 							/* Set WidthIn and WidthOut to 0 */
-							dword = pci_read_config32(NODE_PCI(node, 0), 0xc4);
+							dword = pci_read_config32(NODE_PCI(node, 0), (fam15h)?0x84:0xc4);
 							dword &= ~0x77000000;
-							pci_write_config32(NODE_PCI(node, 0), 0xc4, dword);
+							pci_write_config32(NODE_PCI(node, 0), (fam15h)?0x84:0xc4, dword);
 							/* Set Ganged to 1 */
-							dword = pci_read_config32(NODE_PCI(node, 0), 0x178);
+							dword = pci_read_config32(NODE_PCI(node, 0), (fam15h)?0x170:0x178);
 							dword |= 0x00000001;
-							pci_write_config32(NODE_PCI(node, 0), 0x178, dword);
+							pci_write_config32(NODE_PCI(node, 0), (fam15h)?0x170:0x178, dword);
 						} else {
 							/* Set ConnDly to 1 */
 							dword = pci_read_config32(NODE_PCI(node, 0), 0x16c);
 							dword |= 0x00000100;
 							pci_write_config32(NODE_PCI(node, 0), 0x16c, dword);
 							/* Set TransOff and EndOfChain to 1 */
-							dword = pci_read_config32(NODE_PCI(node, 4), 0xc4);
+							dword = pci_read_config32(NODE_PCI(node, 4), (fam15h)?0x84:0xc4);
 							dword |= 0x000000c0;
-							pci_write_config32(NODE_PCI(node, 4), 0xc4, dword);
+							pci_write_config32(NODE_PCI(node, 4), (fam15h)?0x84:0xc4, dword);
 						}
 					} else if (internal_node_number == 1) {
-						uint8_t package_link_3_connected = pci_read_config32(NODE_PCI(node, 0), 0xb8) & 0x1;
+						uint8_t package_link_3_connected = pci_read_config32(NODE_PCI(node, 0), (fam15h)?0xf8:0xb8) & 0x1;
 						if (package_link_3_connected) {
 							/* Set WidthIn and WidthOut to 0 */
-							dword = pci_read_config32(NODE_PCI(node, 0), 0xa4);
+							dword = pci_read_config32(NODE_PCI(node, 0), (fam15h)?0xe4:0xa4);
 							dword &= ~0x77000000;
-							pci_write_config32(NODE_PCI(node, 0), 0xa4, dword);
+							pci_write_config32(NODE_PCI(node, 0), (fam15h)?0xe4:0xa4, dword);
 							/* Set Ganged to 1 */
-							dword = pci_read_config32(NODE_PCI(node, 0), 0x174);
+							dword = pci_read_config32(NODE_PCI(node, 0), (fam15h)?0x18c:0x174);
 							dword |= 0x00000001;
-							pci_write_config32(NODE_PCI(node, 0), 0x174, dword);
+							pci_write_config32(NODE_PCI(node, 0), (fam15h)?0x18c:0x174, dword);
 						} else {
 							/* Set ConnDly to 1 */
 							dword = pci_read_config32(NODE_PCI(node, 0), 0x16c);
 							dword |= 0x00000100;
 							pci_write_config32(NODE_PCI(node, 0), 0x16c, dword);
 							/* Set TransOff and EndOfChain to 1 */
-							dword = pci_read_config32(NODE_PCI(node, 4), 0xa4);
+							dword = pci_read_config32(NODE_PCI(node, 4), (fam15h)?0xe4:0xa4);
 							dword |= 0x000000c0;
-							pci_write_config32(NODE_PCI(node, 4), 0xa4, dword);
+							pci_write_config32(NODE_PCI(node, 4), (fam15h)?0xe4:0xa4, dword);
 						}
 					}
 				}
diff --git a/src/northbridge/amd/amdmct/amddefs.h b/src/northbridge/amd/amdmct/amddefs.h
index 117fea5..20a77d3 100644
--- a/src/northbridge/amd/amdmct/amddefs.h
+++ b/src/northbridge/amd/amdmct/amddefs.h
@@ -20,33 +20,35 @@
 /* FIXME: this file should be moved to include/cpu/amd/amddefs.h */
 
 /* Public Revisions - USE THESE VERSIONS TO MAKE COMPARE WITH CPULOGICALID RETURN VALUE*/
-#define	AMD_SAFEMODE	0x80000000	/* Unknown future revision - SAFE MODE */
-#define	AMD_NPT_F0	0x00000001	/* F0 stepping */
-#define	AMD_NPT_F1	0x00000002	/* F1 stepping */
-#define	AMD_NPT_F2C	0x00000004
-#define	AMD_NPT_F2D	0x00000008
-#define	AMD_NPT_F2E	0x00000010	/* F2 stepping E */
-#define	AMD_NPT_F2G	0x00000020	/* F2 stepping G */
-#define	AMD_NPT_F2J	0x00000040
-#define	AMD_NPT_F2K	0x00000080
-#define	AMD_NPT_F3L	0x00000100	/* F3 Stepping */
-#define	AMD_NPT_G0A	0x00000200	/* G0 stepping */
-#define	AMD_NPT_G1B	0x00000400	/* G1 stepping */
-#define	AMD_DR_A0A	0x00010000	/* Barcelona A0 */
-#define	AMD_DR_A1B	0x00020000	/* Barcelona A1 */
-#define	AMD_DR_A2	0x00040000	/* Barcelona A2 */
-#define	AMD_DR_B0	0x00080000	/* Barcelona B0 */
-#define	AMD_DR_B1	0x00100000	/* Barcelona B1 */
-#define	AMD_DR_B2	0x00200000	/* Barcelona B2 */
-#define	AMD_DR_BA	0x00400000	/* Barcelona BA */
-#define	AMD_DR_B3	0x00800000	/* Barcelona B3 */
-#define	AMD_RB_C2	0x01000000	/* Shanghai C2 */
-#define	AMD_DA_C2	0x02000000	/* XXXX C2 */
-#define	AMD_HY_D0	0x04000000	/* Istanbul D0 */
-#define	AMD_RB_C3	0x08000000	/* ??? C3 */
-#define	AMD_DA_C3	0x10000000	/* XXXX C3 */
-#define	AMD_HY_D1	0x20000000	/* Istanbul D1 */
-#define	AMD_PH_E0	0x40000000	/* Phenom II X4 X6 */
+#define	AMD_SAFEMODE	0x8000000000000000	/* Unknown future revision - SAFE MODE */
+#define	AMD_NPT_F0	0x0000000000000001	/* F0 stepping */
+#define	AMD_NPT_F1	0x0000000000000002	/* F1 stepping */
+#define	AMD_NPT_F2C	0x0000000000000004
+#define	AMD_NPT_F2D	0x0000000000000008
+#define	AMD_NPT_F2E	0x0000000000000010	/* F2 stepping E */
+#define	AMD_NPT_F2G	0x0000000000000020	/* F2 stepping G */
+#define	AMD_NPT_F2J	0x0000000000000040
+#define	AMD_NPT_F2K	0x0000000000000080
+#define	AMD_NPT_F3L	0x0000000000000100	/* F3 Stepping */
+#define	AMD_NPT_G0A	0x0000000000000200	/* G0 stepping */
+#define	AMD_NPT_G1B	0x0000000000000400	/* G1 stepping */
+#define	AMD_DR_A0A	0x0000000000010000	/* Barcelona A0 */
+#define	AMD_DR_A1B	0x0000000000020000	/* Barcelona A1 */
+#define	AMD_DR_A2	0x0000000000040000	/* Barcelona A2 */
+#define	AMD_DR_B0	0x0000000000080000	/* Barcelona B0 */
+#define	AMD_DR_B1	0x0000000000100000	/* Barcelona B1 */
+#define	AMD_DR_B2	0x0000000000200000	/* Barcelona B2 */
+#define	AMD_DR_BA	0x0000000000400000	/* Barcelona BA */
+#define	AMD_DR_B3	0x0000000000800000	/* Barcelona B3 */
+#define	AMD_RB_C2	0x0000000001000000	/* Shanghai C2 */
+#define	AMD_DA_C2	0x0000000002000000	/* XXXX C2 */
+#define	AMD_HY_D0	0x0000000004000000	/* Istanbul D0 */
+#define	AMD_RB_C3	0x0000000008000000	/* ??? C3 */
+#define	AMD_DA_C3	0x0000000010000000	/* XXXX C3 */
+#define	AMD_HY_D1	0x0000000020000000	/* Istanbul D1 */
+#define	AMD_PH_E0	0x0000000040000000	/* Phenom II X4 X6 */
+#define	AMD_OR_B2	0x0000000080000000	/* Interlagos */
+#define	AMD_OR_C0	0x0000000100000000	/* Abu Dhabi */
 
 /*
  * Groups - Create as many as you wish, from the above public values
@@ -76,6 +78,7 @@
 #define	AMD_DRBH_Cx	(AMD_DR_Cx | AMD_HY_D0 )
 #define	AMD_DRBA23_RBC2	(AMD_DR_BA | AMD_DR_B2 | AMD_DR_B3 | AMD_RB_C2 )
 #define	AMD_DR_DAC2_OR_C3	(AMD_DA_C2 | AMD_DA_C3 | AMD_RB_C3)
+#define	AMD_FAM15_ALL	(AMD_OR_B2 | AMD_OR_C0)
 
 /*
  *  Public Platforms - USE THESE VERSIONS TO MAKE COMPARE WITH CPUPLATFORMTYPE RETURN VALUE
@@ -122,23 +125,34 @@
  */
 #define CPUID_EXT_PM		0x80000007
 #define CPUID_MODEL		1
-#define MCG_CAP		0x00000179
+#define MCG_CAP			0x00000179
 	#define MCG_CTL_P	8
-#define MC0_CTL		0x00000400
-#define MC0_STA		MC0_CTL + 1
-#define FS_Base		0xC0000100
+#define MC0_CTL			0x00000400
+#define MC0_STA			(MC0_CTL + 1)
+#define MC4_MISC0		0x00000413
+#define MC4_MISC1		0xC0000408
+#define MC4_MISC2		0xC0000409
+#define FS_Base			0xC0000100
 #define SYSCFG			0xC0010010
 #define HWCR			0xC0010015
 #define NB_CFG			0xC001001F
 #define FidVidStatus		0xC0010042
+#define MC1_CTL_MASK		0xC0010045
 #define MC4_CTL_MASK		0xC0010048
 #define OSVW_ID_Length		0xC0010140
 #define OSVW_Status		0xC0010141
 #define CPUIDFEATURES		0xC0011004
 #define LS_CFG			0xC0011020
+#define IC_CFG			0xC0011021
 #define DC_CFG			0xC0011022
 #define BU_CFG			0xC0011023
-#define BU_CFG2		0xC001102A
+#define FP_CFG			0xC0011028
+#define DE_CFG			0xC0011029
+#define BU_CFG2			0xC001102A
+#define BU_CFG3			0xC001102B
+#define EX_CFG			0xC001102C
+#define LS_CFG2			0xC001102D
+#define IBS_OP_DATA3		0xC0011037
 
 /*
  * Processor package types
diff --git a/src/northbridge/amd/amdmct/mct/mct_d.c b/src/northbridge/amd/amdmct/mct/mct_d.c
index 88910e2..be0af65 100644
--- a/src/northbridge/amd/amdmct/mct/mct_d.c
+++ b/src/northbridge/amd/amdmct/mct/mct_d.c
@@ -2189,6 +2189,7 @@ static u8 DIMMPresence_D(struct MCTStatStruc *pMCTstat,
 							pDCTstat->DimmManufacturerID[i] |= ((uint64_t)mctRead_SPD(smbaddr, SPD_MANID_START + k)) << (k * 8);
 						for (k = 0; k < SPD_PARTN_LENGTH; k++)
 							pDCTstat->DimmPartNumber[i][k] = mctRead_SPD(smbaddr, SPD_PARTN_START + k);
+						pDCTstat->DimmPartNumber[i][SPD_PARTN_LENGTH] = 0;
 						pDCTstat->DimmRevisionNumber[i] = 0;
 						for (k = 0; k < 2; k++)
 							pDCTstat->DimmRevisionNumber[i] |= ((uint16_t)mctRead_SPD(smbaddr, SPD_REVNO_START + k)) << (k * 8);
@@ -2206,8 +2207,7 @@ static u8 DIMMPresence_D(struct MCTStatStruc *pMCTstat,
 					if (byte & JED_REGADCMSK) {
 						RegDIMMPresent |= 1 << i;
 						pDCTstat->DimmRegistered[i] = 1;
-					}
-					else {
+					} else {
 						pDCTstat->DimmRegistered[i] = 0;
 					}
 					/* Check ECC capable */
diff --git a/src/northbridge/amd/amdmct/mct/mct_d.h b/src/northbridge/amd/amdmct/mct/mct_d.h
index 132bdc9..6b6194d 100644
--- a/src/northbridge/amd/amdmct/mct/mct_d.h
+++ b/src/northbridge/amd/amdmct/mct/mct_d.h
@@ -434,7 +434,7 @@ struct DCTStatStruc {		/* A per Node structure*/
 		/* CH A byte lane 0 - 7 maximum filtered window  passing DQS delay value*/
 		/* CH B byte lane 0 - 7 minimum filtered window  passing DQS delay value*/
 		/* CH B byte lane 0 - 7 maximum filtered window  passing DQS delay value*/
-	u32 LogicalCPUID;	/* The logical CPUID of the node*/
+	uint64_t LogicalCPUID;	/* The logical CPUID of the node*/
 	u16 HostBiosSrvc1;	/* Word sized general purpose field for use by host BIOS.  Scratch space.*/
 	u32 HostBiosSrvc2;	/* Dword sized general purpose field for use by host BIOS.  Scratch space.*/
 	u16 DimmQRPresent;	/* QuadRank DIMM present?*/
@@ -529,7 +529,7 @@ struct DCTStatStruc {		/* A per Node structure*/
 	uint8_t DimmRegistered[MAX_DIMMS_SUPPORTED];
 
 	uint64_t DimmManufacturerID[MAX_DIMMS_SUPPORTED];
-	char DimmPartNumber[MAX_DIMMS_SUPPORTED][SPD_PARTN_LENGTH];
+	char DimmPartNumber[MAX_DIMMS_SUPPORTED][SPD_PARTN_LENGTH+1];
 	uint16_t DimmRevisionNumber[MAX_DIMMS_SUPPORTED];
 	uint32_t DimmSerialNumber[MAX_DIMMS_SUPPORTED];
 } __attribute__((packed));
@@ -598,17 +598,18 @@ struct DCTStatStruc {		/* A per Node structure*/
 					    266=266MHz (DDR533)
 					    333=333MHz (DDR667)
 					    400=400MHz (DDR800)*/
-#define NV_ECC_CAP		4	/* Bus ECC capable (1-bits)
+#define NV_MIN_MEMCLK		4	/* Minimum platform demonstrated Memclock (10-bits) */
+#define NV_ECC_CAP		5	/* Bus ECC capable (1-bits)
 					    0=Platform not capable
 					    1=Platform is capable*/
-#define NV_4RANKType		5	/* Quad Rank DIMM slot type (2-bits)
+#define NV_4RANKType		6	/* Quad Rank DIMM slot type (2-bits)
 					    0=Normal
 					    1=R4 (4-Rank Registered DIMMs in AMD server configuration)
 					    2=S4 (Unbuffered SO-DIMMs)*/
-#define NV_BYPMAX		6	/* Value to set DcqBypassMax field (See Function 2, Offset 94h, [27:24] of BKDG for field definition).
+#define NV_BYPMAX		7	/* Value to set DcqBypassMax field (See Function 2, Offset 94h, [27:24] of BKDG for field definition).
 					    4=4 times bypass (normal for non-UMA systems)
 					    7=7 times bypass (normal for UMA systems)*/
-#define NV_RDWRQBYP		7	/* Value to set RdWrQByp field (See Function 2, Offset A0h, [3:2] of BKDG for field definition).
+#define NV_RDWRQBYP		8	/* Value to set RdWrQByp field (See Function 2, Offset A0h, [3:2] of BKDG for field definition).
 					    2=8 times (normal for non-UMA systems)
 					    3=16 times (normal for UMA systems)*/
 
@@ -671,8 +672,9 @@ struct DCTStatStruc {		/* A per Node structure*/
 #define NV_ECCRedir		54	/* Dram ECC Redirection enable*/
 #define NV_DramBKScrub		55	/* Dram ECC Background Scrubber CTL*/
 #define NV_L2BKScrub		56	/* L2 ECC Background Scrubber CTL*/
-#define NV_DCBKScrub		57	/* DCache ECC Background Scrubber CTL*/
-#define NV_CS_SpareCTL		58	/* Chip Select Spare Control bit 0:
+#define NV_L3BKScrub		57	/* L3 ECC Background Scrubber CTL*/
+#define NV_DCBKScrub		58	/* DCache ECC Background Scrubber CTL*/
+#define NV_CS_SpareCTL		59	/* Chip Select Spare Control bit 0:
 					       0=disable Spare
 					       1=enable Spare */
 					/* Chip Select Spare Control bit 1-4:
@@ -712,7 +714,7 @@ u8 mct_Get_Start_RcvrEnDly_1Pass(u8 Pass);
 u8 mct_Average_RcvrEnDly_Pass(struct DCTStatStruc *pDCTstat, u8 RcvrEnDly, u8 RcvrEnDlyLimit, u8 Channel, u8 Receiver, u8 Pass);
 void CPUMemTyping_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA);
 void UMAMemTyping_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA);
-u32 mctGetLogicalCPUID(u32 Node);
+uint64_t mctGetLogicalCPUID(u32 Node);
 u8 ECCInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA);
 void TrainReceiverEn_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA, u8 Pass);
 void mct_TrainDQSPos_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA);
diff --git a/src/northbridge/amd/amdmct/mct/mctpro_d.c b/src/northbridge/amd/amdmct/mct/mctpro_d.c
index c332357..fe56201 100644
--- a/src/northbridge/amd/amdmct/mct/mctpro_d.c
+++ b/src/northbridge/amd/amdmct/mct/mctpro_d.c
@@ -2,6 +2,7 @@
  * This file is part of the coreboot project.
  *
  * Copyright (C) 2007 Advanced Micro Devices, Inc.
+ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -23,7 +24,7 @@ void EarlySampleSupport_D(void)
 
 u32 procOdtWorkaround(struct DCTStatStruc *pDCTstat, u32 dct, u32 val)
 {
-	u32 tmp;
+	uint64_t tmp;
 	tmp = pDCTstat->LogicalCPUID;
 	if ((tmp == AMD_DR_A0A) || (tmp == AMD_DR_A1B) || (tmp == AMD_DR_A2)) {
 		val &= 0x0FFFFFFF;
@@ -42,7 +43,7 @@ u32 OtherTiming_A_D(struct DCTStatStruc *pDCTstat, u32 val)
 	 * ( F2x[1, 0]8C[1:0] > 00b).  Silicon Status: Fixed in Rev B
 	 * FIXME: check if this is still required.
 	 */
-	u32 tmp;
+	uint64_t tmp;
 	tmp = pDCTstat->LogicalCPUID;
 	if ((tmp == AMD_DR_A0A) || (tmp == AMD_DR_A1B) || (tmp == AMD_DR_A2)) {
 		if(!(val & (3<<12) ))
@@ -54,7 +55,7 @@ u32 OtherTiming_A_D(struct DCTStatStruc *pDCTstat, u32 val)
 
 void mct_ForceAutoPrecharge_D(struct DCTStatStruc *pDCTstat, u32 dct)
 {
-	u32 tmp;
+	uint64_t tmp;
 	u32 reg;
 	u32 reg_off;
 	u32 dev;
@@ -96,7 +97,7 @@ void mct_EndDQSTraining_D(struct MCTStatStruc *pMCTstat,
 	 * FIXME: check this.
 	 */
 
-	u32 tmp;
+	uint64_t tmp;
 	u32 dev;
 	u32 reg;
 	u32 val;
@@ -143,10 +144,9 @@ void mct_BeforeDQSTrain_Samp_D(struct MCTStatStruc *pMCTstat,
 	u32 index;
 	u32 reg;
 	u32 val;
-	u32 tmp;
+	uint64_t tmp;
 	u32 Channel;
 
-
 	tmp = pDCTstat->LogicalCPUID;
 	if ((tmp == AMD_DR_A0A) || (tmp == AMD_DR_A1B) || (tmp == AMD_DR_A2)) {
 
@@ -206,7 +206,7 @@ u32 Modify_D3CMP(struct DCTStatStruc *pDCTstat, u32 dct, u32 value)
 	u32 index_reg;
 	u32 index;
 	u32 val;
-	u32 tmp;
+	uint64_t tmp;
 
 	tmp = pDCTstat->LogicalCPUID;
 	if ((tmp == AMD_DR_A0A) || (tmp == AMD_DR_A1B) || (tmp == AMD_DR_A2)) {
@@ -237,7 +237,7 @@ void SyncSetting(struct DCTStatStruc *pDCTstat)
 	 * Silicon Status: Fix TBD
 	 */
 
-	u32 tmp;
+	uint64_t tmp;
 	tmp = pDCTstat->LogicalCPUID;
 	if ((tmp == AMD_DR_A0A) || (tmp == AMD_DR_A1B) || (tmp == AMD_DR_A2)) {
 		pDCTstat->CH_ODC_CTL[1] = pDCTstat->CH_ODC_CTL[0];
@@ -278,7 +278,7 @@ u32 CheckNBCOFAutoPrechg(struct DCTStatStruc *pDCTstat, u32 dct)
 
 void mct_BeforeDramInit_D(struct DCTStatStruc *pDCTstat, u32 dct)
 {
-	u32 tmp;
+	uint64_t tmp;
 	u32 Speed;
 	u32 ch, ch_start, ch_end;
 	u32 index_reg;
@@ -286,7 +286,6 @@ void mct_BeforeDramInit_D(struct DCTStatStruc *pDCTstat, u32 dct)
 	u32 dev;
 	u32 val;
 
-
 	tmp = pDCTstat->LogicalCPUID;
 	if ((tmp == AMD_DR_A0A) || (tmp == AMD_DR_A1B) || (tmp == AMD_DR_A2)) {
 		Speed = pDCTstat->Speed;
@@ -331,7 +330,7 @@ static u8 mct_checkFenceHoleAdjust_D(struct MCTStatStruc *pMCTstat,
 				u8 ChipSel,  u8 *result)
 {
 	u8 ByteLane;
-	u32 tmp;
+	uint64_t tmp;
 
 	tmp = pDCTstat->LogicalCPUID;
 	if ((tmp == AMD_DR_A0A) || (tmp == AMD_DR_A1B) || (tmp == AMD_DR_A2)) {
diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
index 12dfff1..74066b1 100644
--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
@@ -75,6 +75,8 @@ static void StitchMemory_D(struct MCTStatStruc *pMCTstat,
 				struct DCTStatStruc *pDCTstat, u8 dct);
 static u16 Get_Fk_D(u8 k);
 static u8 Get_DIMMAddress_D(struct DCTStatStruc *pDCTstat, u8 i);
+static void mct_preInitDCT(struct MCTStatStruc *pMCTstat,
+				struct DCTStatStruc *pDCTstat);
 static void mct_initDCT(struct MCTStatStruc *pMCTstat,
 				struct DCTStatStruc *pDCTstat);
 static void mct_DramInit(struct MCTStatStruc *pMCTstat,
@@ -105,11 +107,11 @@ static void Get_TrwtTO(struct MCTStatStruc *pMCTstat,
 static void Get_TrwtWB(struct MCTStatStruc *pMCTstat,
 			struct DCTStatStruc *pDCTstat);
 static void Get_DqsRcvEnGross_Diff(struct DCTStatStruc *pDCTstat,
-					u32 dev, u32 index_reg);
+					u32 dev, uint8_t dct, u32 index_reg);
 static void Get_WrDatGross_Diff(struct DCTStatStruc *pDCTstat, u8 dct,
 					u32 dev, u32 index_reg);
 static u16 Get_DqsRcvEnGross_MaxMin(struct DCTStatStruc *pDCTstat,
-				u32 dev, u32 index_reg, u32 index);
+				u32 dev, uint8_t dct, u32 index_reg, u32 index);
 static void mct_FinalMCT_D(struct MCTStatStruc *pMCTstat,
 				struct DCTStatStruc *pDCTstat);
 static u16 Get_WrDatGross_MaxMin(struct DCTStatStruc *pDCTstat, u8 dct,
@@ -128,6 +130,8 @@ static void SetCKETriState(struct MCTStatStruc *pMCTstat,
 				struct DCTStatStruc *pDCTstat, u8 dct);
 static void SetODTTriState(struct MCTStatStruc *pMCTstat,
 				struct DCTStatStruc *pDCTstat, u8 dct);
+static void InitDDRPhy(struct MCTStatStruc *pMCTstat,
+					struct DCTStatStruc *pDCTstat, u8 dct);
 static void InitPhyCompensation(struct MCTStatStruc *pMCTstat,
 					struct DCTStatStruc *pDCTstat, u8 dct);
 static u32 mct_NodePresent_D(void);
@@ -138,7 +142,9 @@ static void mct_ResetDataStruct_D(struct MCTStatStruc *pMCTstat,
 static void mct_EarlyArbEn_D(struct MCTStatStruc *pMCTstat,
 					struct DCTStatStruc *pDCTstat, u8 dct);
 static void mct_BeforeDramInit_Prod_D(struct MCTStatStruc *pMCTstat,
-					struct DCTStatStruc *pDCTstat);
+					struct DCTStatStruc *pDCTstat, u8 dct);
+static void mct_ProgramODT_D(struct MCTStatStruc *pMCTstat,
+					struct DCTStatStruc *pDCTstat, u8 dct);
 void mct_ClrClToNB_D(struct MCTStatStruc *pMCTstat,
 			struct DCTStatStruc *pDCTstat);
 static u8 CheckNBCOFEarlyArbEn(struct MCTStatStruc *pMCTstat,
@@ -158,6 +164,10 @@ static u32 mct_DisDllShutdownSR(struct MCTStatStruc *pMCTstat,
 				struct DCTStatStruc *pDCTstat, u32 DramConfigLo, u8 dct);
 static void mct_EnDllShutdownSR(struct MCTStatStruc *pMCTstat,
 				struct DCTStatStruc *pDCTstat, u8 dct);
+static void ChangeMemClk(struct MCTStatStruc *pMCTstat,
+					struct DCTStatStruc *pDCTstat);
+void SetTargetFreq(struct MCTStatStruc *pMCTstat,
+                                        struct DCTStatStruc *pDCTstat);
 
 static u32 mct_MR1Odt_RDimm(struct MCTStatStruc *pMCTstat,
 					struct DCTStatStruc *pDCTstat, u8 dct, u32 MrsChipSel);
@@ -165,7 +175,8 @@ static u32 mct_DramTermDyn_RDimm(struct MCTStatStruc *pMCTstat,
 					struct DCTStatStruc *pDCTstat, u8 dimm);
 static u32 mct_SetDramConfigMisc2(struct DCTStatStruc *pDCTstat, u8 dct, u32 misc2);
 static void mct_BeforeDQSTrainSamp(struct DCTStatStruc *pDCTstat);
-static void mct_WriteLevelization_HW(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA);
+static void mct_WriteLevelization_HW(struct MCTStatStruc *pMCTstat,
+					struct DCTStatStruc *pDCTstatA, uint8_t Pass);
 static u8 Get_Latency_Diff(struct MCTStatStruc *pMCTstat,
 					struct DCTStatStruc *pDCTstat, u8 dct);
 static void SyncSetting(struct DCTStatStruc *pDCTstat);
@@ -173,6 +184,12 @@ static u8 crcCheck(u8 smbaddr);
 static void mct_ExtMCTConfig_Bx(struct DCTStatStruc *pDCTstat);
 static void mct_ExtMCTConfig_Cx(struct DCTStatStruc *pDCTstat);
 
+static void read_dqs_receiver_enable_control_registers(uint16_t* current_total_delay,
+			uint32_t dev, uint8_t dct, uint8_t dimm, uint32_t index_reg);
+
+static void read_dqs_write_timing_control_registers(uint16_t* current_total_delay,
+			uint32_t dev, uint8_t dct, uint8_t dimm, uint32_t index_reg);
+
 /*See mctAutoInitMCT header for index relationships to CL and T*/
 static const u16 Table_F_k[]	= {00,200,266,333,400,533 };
 static const u8 Tab_BankAddr[]	= {0x3F,0x01,0x09,0x3F,0x3F,0x11,0x0A,0x19,0x12,0x1A,0x21,0x22,0x23};
@@ -223,6 +240,936 @@ static const u8 Table_Comp_Rise_Slew_15x[] = {7, 7, 3, 2, 0xFF};
 static const u8 Table_Comp_Fall_Slew_20x[] = {7, 5, 3, 2, 0xFF};
 static const u8 Table_Comp_Fall_Slew_15x[] = {7, 7, 5, 3, 0xFF};
 
+static uint8_t dct_ddr_voltage_index(struct DCTStatStruc *pDCTstat, uint8_t dct)
+{
+	uint8_t dimm;
+	uint8_t ddr_voltage_index = 0;
+
+	/* Find current DDR supply voltage for this DCT */
+	for (dimm = 0; dimm < MAX_DIMMS_SUPPORTED; dimm++) {
+		if (pDCTstat->DIMMValidDCT[dct] & (1 << dimm))
+			ddr_voltage_index |= pDCTstat->DimmConfiguredVoltage[dimm];
+	}
+	if (ddr_voltage_index > 0x7) {
+		printk(BIOS_DEBUG, "%s: Insufficient DDR supply voltage indicated!  Configuring processor for 1.25V operation, but this attempt may fail...\n", __func__);
+		ddr_voltage_index = 0x4;
+	}
+	if (ddr_voltage_index == 0x0) {
+		printk(BIOS_DEBUG, "%s: No DDR supply voltage indicated!  Configuring processor for 1.5V operation, but this attempt may fail...\n", __func__);
+		ddr_voltage_index = 0x1;
+	}
+
+	return ddr_voltage_index;
+}
+
+static uint16_t fam15h_mhz_to_memclk_config(uint16_t freq)
+{
+	uint16_t fam15h_freq_tab[] = {0, 0, 0, 0, 333, 0, 400, 0, 0, 0, 533, 0, 0, 0, 667, 0, 0, 0, 800, 0, 0, 0, 933};
+	uint16_t iter;
+
+	/* Compute the index value for the given frequency */
+	for (iter = 0; iter <= 0x16; iter++) {
+		if (fam15h_freq_tab[iter] == freq)
+			break;
+	}
+	if (fam15h_freq_tab[iter] == freq)
+		freq = iter;
+	if (freq == 0)
+		freq = 0x4;
+
+	return freq;
+}
+
+static uint16_t fam10h_mhz_to_memclk_config(uint16_t freq)
+{
+	uint16_t fam10h_freq_tab[] = {0, 0, 0, 400, 533, 667, 800};
+	uint16_t iter;
+
+	/* Compute the index value for the given frequency */
+	for (iter = 0; iter <= 0x6; iter++) {
+		if (fam10h_freq_tab[iter] == freq)
+			break;
+	}
+	if (fam10h_freq_tab[iter] == freq)
+		freq = iter;
+	if (freq == 0)
+		freq = 0x3;
+
+	return freq;
+}
+
+static uint16_t mhz_to_memclk_config(uint16_t freq)
+{
+	if (is_fam15h())
+		return fam15h_mhz_to_memclk_config(freq);
+	else
+		return fam10h_mhz_to_memclk_config(freq) + 1;
+}
+
+static uint32_t fam15h_phy_predriver_calibration_code(struct DCTStatStruc *pDCTstat, uint8_t dct, uint8_t drive_strength)
+{
+	uint8_t lrdimm = 0;
+	uint8_t package_type;
+	uint8_t ddr_voltage_index;
+	uint32_t calibration_code = 0;
+	uint16_t MemClkFreq = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0x94) & 0x1f;
+
+	ddr_voltage_index = dct_ddr_voltage_index(pDCTstat, dct);
+	package_type = mctGet_NVbits(NV_PACK_TYPE);
+
+	if (!lrdimm) {
+		/* Not an LRDIMM */
+		if ((package_type == PT_M2) || (package_type == PT_GR)) {
+			/* Socket AM3 or G34 */
+			if (ddr_voltage_index & 0x4) {
+				/* 1.25V */
+				/* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 43 */
+				if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)) {
+					/* DDR3-667 - DDR3-800 */
+					if (drive_strength == 0x0)
+						calibration_code = 0xfff;
+					else if (drive_strength == 0x1)
+						calibration_code = 0xb6d;
+					else if (drive_strength == 0x2)
+						calibration_code = 0x924;
+					else if (drive_strength == 0x3)
+						calibration_code = 0x6db;
+				} else if ((MemClkFreq == 0xa) || (MemClkFreq == 0xe)) {
+					/* DDR3-1066 - DDR3-1333 */
+					if (drive_strength == 0x0)
+						calibration_code = 0xfff;
+					else if (drive_strength == 0x1)
+						calibration_code = 0xfff;
+					else if (drive_strength == 0x2)
+						calibration_code = 0xdb6;
+					else if (drive_strength == 0x3)
+						calibration_code = 0x924;
+				} else if ((MemClkFreq == 0x12) || (MemClkFreq == 0x16)) {
+					/* DDR3-1600 - DDR3-1866 */
+					if (drive_strength == 0x0)
+						calibration_code = 0xfff;
+					else if (drive_strength == 0x1)
+						calibration_code = 0xfff;
+					else if (drive_strength == 0x2)
+						calibration_code = 0xfff;
+					else if (drive_strength == 0x3)
+						calibration_code = 0xfff;
+				}
+			}
+			else if (ddr_voltage_index & 0x2) {
+				/* 1.35V */
+				/* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 42 */
+				if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)) {
+					/* DDR3-667 - DDR3-800 */
+					if (drive_strength == 0x0)
+						calibration_code = 0xfff;
+					else if (drive_strength == 0x1)
+						calibration_code = 0x924;
+					else if (drive_strength == 0x2)
+						calibration_code = 0x6db;
+					else if (drive_strength == 0x3)
+						calibration_code = 0x492;
+				} else if ((MemClkFreq == 0xa) || (MemClkFreq == 0xe)) {
+					/* DDR3-1066 - DDR3-1333 */
+					if (drive_strength == 0x0)
+						calibration_code = 0xfff;
+					else if (drive_strength == 0x1)
+						calibration_code = 0xdb6;
+					else if (drive_strength == 0x2)
+						calibration_code = 0xbd6;
+					else if (drive_strength == 0x3)
+						calibration_code = 0x6db;
+				} else if ((MemClkFreq == 0x12) || (MemClkFreq == 0x16)) {
+					/* DDR3-1600 - DDR3-1866 */
+					if (drive_strength == 0x0)
+						calibration_code = 0xfff;
+					else if (drive_strength == 0x1)
+						calibration_code = 0xfff;
+					else if (drive_strength == 0x2)
+						calibration_code = 0xfff;
+					else if (drive_strength == 0x3)
+						calibration_code = 0xdb6;
+				}
+			}
+			else if (ddr_voltage_index & 0x1) {
+				/* 1.5V */
+				/* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 41 */
+				if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)) {
+					/* DDR3-667 - DDR3-800 */
+					if (drive_strength == 0x0)
+						calibration_code = 0xb6d;
+					else if (drive_strength == 0x1)
+						calibration_code = 0x6db;
+					else if (drive_strength == 0x2)
+						calibration_code = 0x492;
+					else if (drive_strength == 0x3)
+						calibration_code = 0x492;
+				} else if ((MemClkFreq == 0xa) || (MemClkFreq == 0xe)) {
+					/* DDR3-1066 - DDR3-1333 */
+					if (drive_strength == 0x0)
+						calibration_code = 0xfff;
+					else if (drive_strength == 0x1)
+						calibration_code = 0x924;
+					else if (drive_strength == 0x2)
+						calibration_code = 0x6db;
+					else if (drive_strength == 0x3)
+						calibration_code = 0x6db;
+				} else if ((MemClkFreq == 0x12) || (MemClkFreq == 0x16)) {
+					/* DDR3-1600 - DDR3-1866 */
+					if (drive_strength == 0x0)
+						calibration_code = 0xfff;
+					else if (drive_strength == 0x1)
+						calibration_code = 0xfff;
+					else if (drive_strength == 0x2)
+						calibration_code = 0xfff;
+					else if (drive_strength == 0x3)
+						calibration_code = 0xb6d;
+				}
+			}
+		}
+		else if (package_type == PT_C3) {
+			/* Socket C32 */
+			if (ddr_voltage_index & 0x4) {
+				/* 1.25V */
+				/* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 46 */
+				if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)) {
+					/* DDR3-667 - DDR3-800 */
+					if (drive_strength == 0x0)
+						calibration_code = 0xfff;
+					else if (drive_strength == 0x1)
+						calibration_code = 0xb6d;
+					else if (drive_strength == 0x2)
+						calibration_code = 0x924;
+					else if (drive_strength == 0x3)
+						calibration_code = 0x6db;
+				} else if (MemClkFreq == 0xa) {
+					/* DDR3-1066 */
+					if (drive_strength == 0x0)
+						calibration_code = 0xfff;
+					else if (drive_strength == 0x1)
+						calibration_code = 0xfff;
+					else if (drive_strength == 0x2)
+						calibration_code = 0xdb6;
+					else if (drive_strength == 0x3)
+						calibration_code = 0x924;
+				} else if (MemClkFreq == 0xe) {
+					/* DDR3-1333 */
+					if (drive_strength == 0x0)
+						calibration_code = 0xb6d;
+					else if (drive_strength == 0x1)
+						calibration_code = 0x6db;
+					else if (drive_strength == 0x2)
+						calibration_code = 0x492;
+					else if (drive_strength == 0x3)
+						calibration_code = 0x492;
+				} else if ((MemClkFreq == 0x12) || (MemClkFreq == 0x16)) {
+					/* DDR3-1600 - DDR3-1866 */
+					if (drive_strength == 0x0)
+						calibration_code = 0xfff;
+					else if (drive_strength == 0x1)
+						calibration_code = 0xfff;
+					else if (drive_strength == 0x2)
+						calibration_code = 0xfff;
+					else if (drive_strength == 0x3)
+						calibration_code = 0xfff;
+				}
+			}
+			else if (ddr_voltage_index & 0x2) {
+				/* 1.35V */
+				/* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 45 */
+				if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)) {
+					/* DDR3-667 - DDR3-800 */
+					if (drive_strength == 0x0)
+						calibration_code = 0xfff;
+					else if (drive_strength == 0x1)
+						calibration_code = 0x924;
+					else if (drive_strength == 0x2)
+						calibration_code = 0x6db;
+					else if (drive_strength == 0x3)
+						calibration_code = 0x492;
+				} else if (MemClkFreq == 0xa) {
+					/* DDR3-1066 */
+					if (drive_strength == 0x0)
+						calibration_code = 0xfff;
+					else if (drive_strength == 0x1)
+						calibration_code = 0xdb6;
+					else if (drive_strength == 0x2)
+						calibration_code = 0xb6d;
+					else if (drive_strength == 0x3)
+						calibration_code = 0x6db;
+				} else if (MemClkFreq == 0xe) {
+					/* DDR3-1333 */
+					if (drive_strength == 0x0)
+						calibration_code = 0xfff;
+					else if (drive_strength == 0x1)
+						calibration_code = 0x924;
+					else if (drive_strength == 0x2)
+						calibration_code = 0x6db;
+					else if (drive_strength == 0x3)
+						calibration_code = 0x492;
+				} else if ((MemClkFreq == 0x12) || (MemClkFreq == 0x16)) {
+					/* DDR3-1600 - DDR3-1866 */
+					if (drive_strength == 0x0)
+						calibration_code = 0xfff;
+					else if (drive_strength == 0x1)
+						calibration_code = 0xfff;
+					else if (drive_strength == 0x2)
+						calibration_code = 0xfff;
+					else if (drive_strength == 0x3)
+						calibration_code = 0xdb6;
+				}
+			}
+			else if (ddr_voltage_index & 0x1) {
+				/* 1.5V */
+				/* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 44 */
+				if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)) {
+					/* DDR3-667 - DDR3-800 */
+					if (drive_strength == 0x0)
+						calibration_code = 0xb6d;
+					else if (drive_strength == 0x1)
+						calibration_code = 0x6db;
+					else if (drive_strength == 0x2)
+						calibration_code = 0x492;
+					else if (drive_strength == 0x3)
+						calibration_code = 0x492;
+				} else if (MemClkFreq == 0xa) {
+					/* DDR3-1066 */
+					if (drive_strength == 0x0)
+						calibration_code = 0xfff;
+					else if (drive_strength == 0x1)
+						calibration_code = 0x924;
+					else if (drive_strength == 0x2)
+						calibration_code = 0x6db;
+					else if (drive_strength == 0x3)
+						calibration_code = 0x6db;
+				} else if (MemClkFreq == 0xe) {
+					/* DDR3-1333 */
+					if (drive_strength == 0x0)
+						calibration_code = 0xb6d;
+					else if (drive_strength == 0x1)
+						calibration_code = 0x6db;
+					else if (drive_strength == 0x2)
+						calibration_code = 0x492;
+					else if (drive_strength == 0x3)
+						calibration_code = 0x492;
+				} else if ((MemClkFreq == 0x12) || (MemClkFreq == 0x16)) {
+					/* DDR3-1600 - DDR3-1866 */
+					if (drive_strength == 0x0)
+						calibration_code = 0xfff;
+					else if (drive_strength == 0x1)
+						calibration_code = 0xfff;
+					else if (drive_strength == 0x2)
+						calibration_code = 0xfff;
+					else if (drive_strength == 0x3)
+						calibration_code = 0xb6d;
+				}
+			}
+		}
+	} else {
+		/* LRDIMM */
+
+		/* TODO
+		 * Implement LRDIMM support
+		 * See Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Tables 47 - 49
+		 */
+	}
+
+	return calibration_code;
+}
+
+static uint32_t fam15h_phy_predriver_cmd_addr_calibration_code(struct DCTStatStruc *pDCTstat, uint8_t dct, uint8_t drive_strength)
+{
+	uint8_t ddr_voltage_index;
+	uint32_t calibration_code = 0;
+	uint16_t MemClkFreq = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0x94) & 0x1f;
+
+	ddr_voltage_index = dct_ddr_voltage_index(pDCTstat, dct);
+
+	if (ddr_voltage_index & 0x4) {
+		/* 1.25V */
+		/* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 52 */
+		if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)) {
+			/* DDR3-667 - DDR3-800 */
+			if (drive_strength == 0x0)
+				calibration_code = 0x492;
+			else if (drive_strength == 0x1)
+				calibration_code = 0x492;
+			else if (drive_strength == 0x2)
+				calibration_code = 0x492;
+			else if (drive_strength == 0x3)
+				calibration_code = 0x492;
+		} else if ((MemClkFreq == 0xa) || (MemClkFreq == 0xe)) {
+			/* DDR3-1066 - DDR3-1333 */
+			if (drive_strength == 0x0)
+				calibration_code = 0xdad;
+			else if (drive_strength == 0x1)
+				calibration_code = 0x924;
+			else if (drive_strength == 0x2)
+				calibration_code = 0x6db;
+			else if (drive_strength == 0x3)
+				calibration_code = 0x492;
+		} else if ((MemClkFreq == 0x12) || (MemClkFreq == 0x16)) {
+			/* DDR3-1600 - DDR3-1866 */
+			if (drive_strength == 0x0)
+				calibration_code = 0xff6;
+			else if (drive_strength == 0x1)
+				calibration_code = 0xdad;
+			else if (drive_strength == 0x2)
+				calibration_code = 0xb64;
+			else if (drive_strength == 0x3)
+				calibration_code = 0xb64;
+		}
+	}
+	else if (ddr_voltage_index & 0x2) {
+		/* 1.35V */
+		/* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 51 */
+		if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)) {
+			/* DDR3-667 - DDR3-800 */
+			if (drive_strength == 0x0)
+				calibration_code = 0x492;
+			else if (drive_strength == 0x1)
+				calibration_code = 0x492;
+			else if (drive_strength == 0x2)
+				calibration_code = 0x492;
+			else if (drive_strength == 0x3)
+				calibration_code = 0x492;
+		} else if ((MemClkFreq == 0xa) || (MemClkFreq == 0xe)) {
+			/* DDR3-1066 - DDR3-1333 */
+			if (drive_strength == 0x0)
+				calibration_code = 0x924;
+			else if (drive_strength == 0x1)
+				calibration_code = 0x6db;
+			else if (drive_strength == 0x2)
+				calibration_code = 0x6db;
+			else if (drive_strength == 0x3)
+				calibration_code = 0x6db;
+		} else if ((MemClkFreq == 0x12) || (MemClkFreq == 0x16)) {
+			/* DDR3-1600 - DDR3-1866 */
+			if (drive_strength == 0x0)
+				calibration_code = 0xb6d;
+			else if (drive_strength == 0x1)
+				calibration_code = 0xb6d;
+			else if (drive_strength == 0x2)
+				calibration_code = 0x924;
+			else if (drive_strength == 0x3)
+				calibration_code = 0x924;
+		}
+	}
+	else if (ddr_voltage_index & 0x1) {
+		/* 1.5V */
+		/* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 50 */
+		if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)) {
+			/* DDR3-667 - DDR3-800 */
+			if (drive_strength == 0x0)
+				calibration_code = 0x492;
+			else if (drive_strength == 0x1)
+				calibration_code = 0x492;
+			else if (drive_strength == 0x2)
+				calibration_code = 0x492;
+			else if (drive_strength == 0x3)
+				calibration_code = 0x492;
+		} else if ((MemClkFreq == 0xa) || (MemClkFreq == 0xe)) {
+			/* DDR3-1066 - DDR3-1333 */
+			if (drive_strength == 0x0)
+				calibration_code = 0x6db;
+			else if (drive_strength == 0x1)
+				calibration_code = 0x6db;
+			else if (drive_strength == 0x2)
+				calibration_code = 0x6db;
+			else if (drive_strength == 0x3)
+				calibration_code = 0x6db;
+		} else if ((MemClkFreq == 0x12) || (MemClkFreq == 0x16)) {
+			/* DDR3-1600 - DDR3-1866 */
+			if (drive_strength == 0x0)
+				calibration_code = 0xb6d;
+			else if (drive_strength == 0x1)
+				calibration_code = 0xb6d;
+			else if (drive_strength == 0x2)
+				calibration_code = 0xb6d;
+			else if (drive_strength == 0x3)
+				calibration_code = 0xb6d;
+		}
+	}
+
+	return calibration_code;
+}
+
+static uint32_t fam15h_phy_predriver_clk_calibration_code(struct DCTStatStruc *pDCTstat, uint8_t dct, uint8_t drive_strength)
+{
+	uint8_t ddr_voltage_index;
+	uint32_t calibration_code = 0;
+	uint16_t MemClkFreq = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0x94) & 0x1f;
+
+	ddr_voltage_index = dct_ddr_voltage_index(pDCTstat, dct);
+
+	if (ddr_voltage_index & 0x4) {
+		/* 1.25V */
+		/* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 55 */
+		if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)) {
+			/* DDR3-667 - DDR3-800 */
+			if (drive_strength == 0x0)
+				calibration_code = 0xdad;
+			else if (drive_strength == 0x1)
+				calibration_code = 0xdad;
+			else if (drive_strength == 0x2)
+				calibration_code = 0x924;
+			else if (drive_strength == 0x3)
+				calibration_code = 0x924;
+		} else if ((MemClkFreq == 0xa) || (MemClkFreq == 0xe)) {
+			/* DDR3-1066 - DDR3-1333 */
+			if (drive_strength == 0x0)
+				calibration_code = 0xff6;
+			else if (drive_strength == 0x1)
+				calibration_code = 0xff6;
+			else if (drive_strength == 0x2)
+				calibration_code = 0xff6;
+			else if (drive_strength == 0x3)
+				calibration_code = 0xff6;
+		} else if ((MemClkFreq == 0x12) || (MemClkFreq == 0x16)) {
+			/* DDR3-1600 - DDR3-1866 */
+			if (drive_strength == 0x0)
+				calibration_code = 0xff6;
+			else if (drive_strength == 0x1)
+				calibration_code = 0xff6;
+			else if (drive_strength == 0x2)
+				calibration_code = 0xff6;
+			else if (drive_strength == 0x3)
+				calibration_code = 0xff6;
+		}
+	}
+	else if (ddr_voltage_index & 0x2) {
+		/* 1.35V */
+		/* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 54 */
+		if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)) {
+			/* DDR3-667 - DDR3-800 */
+			if (drive_strength == 0x0)
+				calibration_code = 0xdad;
+			else if (drive_strength == 0x1)
+				calibration_code = 0xdad;
+			else if (drive_strength == 0x2)
+				calibration_code = 0x924;
+			else if (drive_strength == 0x3)
+				calibration_code = 0x924;
+		} else if ((MemClkFreq == 0xa) || (MemClkFreq == 0xe)) {
+			/* DDR3-1066 - DDR3-1333 */
+			if (drive_strength == 0x0)
+				calibration_code = 0xff6;
+			else if (drive_strength == 0x1)
+				calibration_code = 0xff6;
+			else if (drive_strength == 0x2)
+				calibration_code = 0xff6;
+			else if (drive_strength == 0x3)
+				calibration_code = 0xdad;
+		} else if ((MemClkFreq == 0x12) || (MemClkFreq == 0x16)) {
+			/* DDR3-1600 - DDR3-1866 */
+			if (drive_strength == 0x0)
+				calibration_code = 0xff6;
+			else if (drive_strength == 0x1)
+				calibration_code = 0xff6;
+			else if (drive_strength == 0x2)
+				calibration_code = 0xff6;
+			else if (drive_strength == 0x3)
+				calibration_code = 0xdad;
+		}
+	}
+	else if (ddr_voltage_index & 0x1) {
+		/* 1.5V */
+		/* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 53 */
+		if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)) {
+			/* DDR3-667 - DDR3-800 */
+			if (drive_strength == 0x0)
+				calibration_code = 0x924;
+			else if (drive_strength == 0x1)
+				calibration_code = 0x924;
+			else if (drive_strength == 0x2)
+				calibration_code = 0x924;
+			else if (drive_strength == 0x3)
+				calibration_code = 0x924;
+		} else if ((MemClkFreq == 0xa) || (MemClkFreq == 0xe)) {
+			/* DDR3-1066 - DDR3-1333 */
+			if (drive_strength == 0x0)
+				calibration_code = 0xff6;
+			else if (drive_strength == 0x1)
+				calibration_code = 0xff6;
+			else if (drive_strength == 0x2)
+				calibration_code = 0xff6;
+			else if (drive_strength == 0x3)
+				calibration_code = 0xb6d;
+		} else if ((MemClkFreq == 0x12) || (MemClkFreq == 0x16)) {
+			/* DDR3-1600 - DDR3-1866 */
+			if (drive_strength == 0x0)
+				calibration_code = 0xff6;
+			else if (drive_strength == 0x1)
+				calibration_code = 0xff6;
+			else if (drive_strength == 0x2)
+				calibration_code = 0xff6;
+			else if (drive_strength == 0x3)
+				calibration_code = 0xff6;
+		}
+	}
+
+	return calibration_code;
+}
+
+static uint32_t fam15h_output_driver_compensation_code(struct DCTStatStruc *pDCTstat, uint8_t dct)
+{
+	/* FIXME
+	 * Mainboards need to be able to specify the maximum number of DIMMs installable per channel
+	 * For now assume a maximum of 2 DIMMs per channel can be installed
+	 */
+	uint8_t MaxDimmsInstallable = 2;
+
+	uint8_t package_type;
+	uint32_t calibration_code = 0;
+
+	package_type = mctGet_NVbits(NV_PACK_TYPE);
+	uint16_t MemClkFreq = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0x94) & 0x1f;
+
+	/* Obtain number of DIMMs on channel */
+	uint8_t dimm_count = pDCTstat->MAdimms[dct];
+	uint8_t rank_count_dimm0;
+	uint8_t rank_count_dimm1;
+
+	if (package_type == PT_GR) {
+		/* Socket G34 */
+		/* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 73 */
+		if (MaxDimmsInstallable == 1) {
+			if (MemClkFreq == 0x4) {
+				/* DDR3-667 */
+				calibration_code = 0x00112222;
+			}
+			else if (MemClkFreq == 0x6) {
+				/* DDR3-800 */
+				calibration_code = 0x10112222;
+			}
+			else if (MemClkFreq == 0xa) {
+				/* DDR3-1066 */
+				calibration_code = 0x20112222;
+			}
+			else if ((MemClkFreq == 0xe) || (MemClkFreq == 0x12)) {
+				/* DDR3-1333 - DDR3-1600 */
+				calibration_code = 0x30112222;
+			}
+			else if (MemClkFreq == 0x16) {
+				/* DDR3-1866 */
+				calibration_code = 0x30332222;
+			}
+		} else if (MaxDimmsInstallable == 2) {
+			if (dimm_count == 1) {
+				/* 1 DIMM detected */
+				if (MemClkFreq == 0x4) {
+					/* DDR3-667 */
+					calibration_code = 0x00112222;
+				}
+				else if (MemClkFreq == 0x6) {
+					/* DDR3-800 */
+					calibration_code = 0x10112222;
+				}
+				else if (MemClkFreq == 0xa) {
+					/* DDR3-1066 */
+					calibration_code = 0x20112222;
+				}
+				else if ((MemClkFreq == 0xe) || (MemClkFreq == 0x12)) {
+					/* DDR3-1333 - DDR3-1600 */
+					calibration_code = 0x30112222;
+				}
+			} else if (dimm_count == 2) {
+				/* 2 DIMMs detected */
+				rank_count_dimm0 = pDCTstat->C_DCTPtr[dct]->DimmRanks[0];
+				rank_count_dimm1 = pDCTstat->C_DCTPtr[dct]->DimmRanks[1];
+
+				if (MemClkFreq == 0x4) {
+					/* DDR3-667 */
+					calibration_code = 0x10222222;
+				}
+				else if (MemClkFreq == 0x6) {
+					/* DDR3-800 */
+					calibration_code = 0x20222222;
+				}
+				else if (MemClkFreq == 0xa) {
+					/* DDR3-1066 */
+					calibration_code = 0x30222222;
+				}
+				else if (MemClkFreq == 0xe) {
+					/* DDR3-1333 */
+					calibration_code = 0x30222222;
+				}
+ 				else if (MemClkFreq == 0x12) {
+ 					/* DDR3-1600 */
+ 					if ((rank_count_dimm0 == 1) && (rank_count_dimm1 == 1))
+ 						calibration_code = 0x30222222;
+ 					else
+ 						calibration_code = 0x30112222;
+				}
+			}
+		} else if (MaxDimmsInstallable == 3) {
+			/* TODO
+			 * 3 DIMM/channel support unimplemented
+			 */
+		}
+	} else {
+		/* TODO
+		 * Other socket support unimplemented
+		 */
+	}
+
+	return calibration_code;
+}
+
+static uint32_t fam15h_address_timing_compensation_code(struct DCTStatStruc *pDCTstat, uint8_t dct)
+{
+	/* FIXME
+	 * Mainboards need to be able to specify the maximum number of DIMMs installable per channel
+	 * For now assume a maximum of 2 DIMMs per channel can be installed
+	 */
+	uint8_t MaxDimmsInstallable = 2;
+
+	uint8_t package_type;
+	uint32_t calibration_code = 0;
+
+	package_type = mctGet_NVbits(NV_PACK_TYPE);
+	uint16_t MemClkFreq = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0x94) & 0x1f;
+
+	/* Obtain number of DIMMs on channel */
+	uint8_t dimm_count = pDCTstat->MAdimms[dct];
+	uint8_t rank_count_dimm0;
+	uint8_t rank_count_dimm1;
+
+	if (package_type == PT_GR) {
+		/* Socket G34 */
+		/* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 73 */
+		if (MaxDimmsInstallable == 1) {
+			rank_count_dimm0 = pDCTstat->C_DCTPtr[dct]->DimmRanks[1];
+
+			if (MemClkFreq == 0x4) {
+				/* DDR3-667 */
+				if (rank_count_dimm0 == 1)
+					calibration_code = 0x00000000;
+				else
+					calibration_code = 0x003b0000;
+			} else if (MemClkFreq == 0x6) {
+				/* DDR3-800 */
+				if (rank_count_dimm0 == 1)
+					calibration_code = 0x00000000;
+				else
+					calibration_code = 0x003b0000;
+			} else if (MemClkFreq == 0xa) {
+				/* DDR3-1066 */
+				calibration_code = 0x00383837;
+			} else if (MemClkFreq == 0xe) {
+				/* DDR3-1333 */
+				calibration_code = 0x00363635;
+			} else if (MemClkFreq == 0x12) {
+				/* DDR3-1600 */
+				if (rank_count_dimm0 == 1)
+					calibration_code = 0x00353533;
+				else
+					calibration_code = 0x00003533;
+			} else if (MemClkFreq == 0x16) {
+				/* DDR3-1866 */
+				calibration_code = 0x00333330;
+			}
+		} else if (MaxDimmsInstallable == 2) {
+			if (dimm_count == 1) {
+				/* 1 DIMM detected */
+				rank_count_dimm0 = pDCTstat->C_DCTPtr[dct]->DimmRanks[1];
+
+				if (MemClkFreq == 0x4) {
+					/* DDR3-667 */
+					if (rank_count_dimm0 == 1)
+						calibration_code = 0x00000000;
+					else
+						calibration_code = 0x003b0000;
+				} else if (MemClkFreq == 0x6) {
+					/* DDR3-800 */
+					if (rank_count_dimm0 == 1)
+						calibration_code = 0x00000000;
+					else
+						calibration_code = 0x003b0000;
+				} else if (MemClkFreq == 0xa) {
+					/* DDR3-1066 */
+					calibration_code = 0x00383837;
+				} else if (MemClkFreq == 0xe) {
+					/* DDR3-1333 */
+					calibration_code = 0x00363635;
+				} else if (MemClkFreq == 0x12) {
+					/* DDR3-1600 */
+					if (rank_count_dimm0 == 1)
+						calibration_code = 0x00353533;
+					else
+						calibration_code = 0x00003533;
+				}
+			} else if (dimm_count == 2) {
+				/* 2 DIMMs detected */
+				rank_count_dimm0 = pDCTstat->C_DCTPtr[dct]->DimmRanks[0];
+				rank_count_dimm1 = pDCTstat->C_DCTPtr[dct]->DimmRanks[1];
+
+				if (MemClkFreq == 0x4) {
+					/* DDR3-667 */
+					calibration_code = 0x00390039;
+				} else if (MemClkFreq == 0x6) {
+					/* DDR3-800 */
+					calibration_code = 0x00390039;
+				} else if (MemClkFreq == 0xa) {
+					/* DDR3-1066 */
+					calibration_code = 0x003a3a3a;
+				} else if (MemClkFreq == 0xe) {
+					/* DDR3-1333 */
+					calibration_code = 0x00003939;
+				} else if (MemClkFreq == 0x12) {
+					/* DDR3-1600 */
+					if ((rank_count_dimm0 == 1) && (rank_count_dimm1 == 1))
+						calibration_code = 0x00003738;
+				}
+			}
+		} else if (MaxDimmsInstallable == 3) {
+			/* TODO
+			 * 3 DIMM/channel support unimplemented
+			 */
+		}
+	} else {
+		/* TODO
+		 * Other socket support unimplemented
+		 */
+	}
+
+	return calibration_code;
+}
+
+static uint8_t fam15h_slow_access_mode(struct DCTStatStruc *pDCTstat, uint8_t dct)
+{
+	/* FIXME
+	 * Mainboards need to be able to specify the maximum number of DIMMs installable per channel
+	 * For now assume a maximum of 2 DIMMs per channel can be installed
+	 */
+	uint8_t MaxDimmsInstallable = 2;
+
+	uint8_t package_type;
+	uint32_t slow_access = 0;
+
+	package_type = mctGet_NVbits(NV_PACK_TYPE);
+	uint16_t MemClkFreq = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0x94) & 0x1f;
+
+	/* Obtain number of DIMMs on channel */
+	uint8_t dimm_count = pDCTstat->MAdimms[dct];
+	uint8_t rank_count_dimm0;
+	uint8_t rank_count_dimm1;
+
+	if (package_type == PT_GR) {
+		/* Socket G34 */
+		/* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 73 */
+		if (MaxDimmsInstallable == 1) {
+			rank_count_dimm0 = pDCTstat->C_DCTPtr[dct]->DimmRanks[1];
+
+			if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)
+				|| (MemClkFreq == 0xa) | (MemClkFreq == 0xe)) {
+				/* DDR3-667 - DDR3-1333 */
+				slow_access = 0;
+			} else if ((MemClkFreq == 0x12) || (MemClkFreq == 0x16)) {
+				/* DDR3-1600 - DDR3-1866 */
+				if (rank_count_dimm0 == 1)
+					slow_access = 0;
+				else
+					slow_access = 1;
+			}
+		} else if (MaxDimmsInstallable == 2) {
+			if (dimm_count == 1) {
+				/* 1 DIMM detected */
+				rank_count_dimm0 = pDCTstat->C_DCTPtr[dct]->DimmRanks[1];
+
+				if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)
+					|| (MemClkFreq == 0xa) | (MemClkFreq == 0xe)) {
+					/* DDR3-667 - DDR3-1333 */
+					slow_access = 0;
+				}
+				else if (MemClkFreq == 0x12) {
+					/* DDR3-1600 */
+					if (rank_count_dimm0 == 1)
+						slow_access = 0;
+					else
+						slow_access = 1;
+				}
+			} else if (dimm_count == 2) {
+				/* 2 DIMMs detected */
+				rank_count_dimm0 = pDCTstat->C_DCTPtr[dct]->DimmRanks[0];
+				rank_count_dimm1 = pDCTstat->C_DCTPtr[dct]->DimmRanks[1];
+
+				if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)
+					|| (MemClkFreq == 0xa)) {
+					/* DDR3-667 - DDR3-1066 */
+					slow_access = 0;
+				}
+				else if ((MemClkFreq == 0xe) || (MemClkFreq == 0x12)) {
+					/* DDR3-1333 - DDR3-1600 */
+					slow_access = 1;
+				}
+			}
+		} else if (MaxDimmsInstallable == 3) {
+			/* TODO
+			 * 3 DIMM/channel support unimplemented
+			 */
+		}
+	} else {
+		/* TODO
+		 * Other socket support unimplemented
+		 */
+	}
+
+	return slow_access;
+}
+
+static void set_2t_configuration(struct MCTStatStruc *pMCTstat,
+				struct DCTStatStruc *pDCTstat, u8 dct)
+{
+	uint32_t dev;
+	uint32_t reg;
+	uint32_t dword;
+
+	uint8_t enable_slow_access_mode = 0;
+	dev = pDCTstat->dev_dct;
+
+	if (is_fam15h()) {
+		if (pDCTstat->_2Tmode)
+			enable_slow_access_mode = 1;
+	} else {
+		if (pDCTstat->_2Tmode == 2)
+			enable_slow_access_mode = 1;
+	}
+
+	reg = 0x94; 				/* DRAM Configuration High */
+	dword = Get_NB32_DCT(dev, dct, reg);
+	if (enable_slow_access_mode)
+		dword |= (0x1 << 20);		/* Set 2T CMD mode */
+	else
+		dword &= ~(0x1 << 20);		/* Clear 2T CMD mode */
+	Set_NB32_DCT(dev, dct, reg, dword);
+}
+
+static void precise_ndelay_fam15(struct MCTStatStruc *pMCTstat, uint32_t nanoseconds) {
+	msr_t tsc_msr;
+	uint64_t cycle_count = (((uint64_t)pMCTstat->TSCFreq) * nanoseconds) / 1000;
+	uint64_t start_timestamp;
+	uint64_t current_timestamp;
+
+	tsc_msr = rdmsr(0x00000010);
+	start_timestamp = (((uint64_t)tsc_msr.hi) << 32) | tsc_msr.lo;
+        do {
+		tsc_msr = rdmsr(0x00000010);
+		current_timestamp = (((uint64_t)tsc_msr.hi) << 32) | tsc_msr.lo;
+        } while ((current_timestamp - start_timestamp) < cycle_count);
+}
+
+static void precise_memclk_delay_fam15(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, uint8_t dct, uint32_t clocks) {
+	uint16_t memclk_freq;
+	uint32_t delay_ns;
+	uint16_t fam15h_freq_tab[] = {0, 0, 0, 0, 333, 0, 400, 0, 0, 0, 533, 0, 0, 0, 667, 0, 0, 0, 800, 0, 0, 0, 933};
+
+	memclk_freq = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0x94) & 0x1f;
+
+	delay_ns = (((uint64_t)clocks * 1000) / fam15h_freq_tab[memclk_freq]);
+	precise_ndelay_fam15(pMCTstat, delay_ns);
+}
+
 static void mctAutoInitMCT_D(struct MCTStatStruc *pMCTstat,
 			struct DCTStatStruc *pDCTstatA)
 {
@@ -277,10 +1224,26 @@ static void mctAutoInitMCT_D(struct MCTStatStruc *pMCTstat,
 restartinit:
 	mctInitMemGPIOs_A_D();		/* Set any required GPIOs*/
 	if (s3resume) {
+		printk(BIOS_DEBUG, "mctAutoInitMCT_D: mct_ForceNBPState0_En_Fam15\n");
+		for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
+			struct DCTStatStruc *pDCTstat;
+			pDCTstat = pDCTstatA + Node;
+
+			mct_ForceNBPState0_En_Fam15(pMCTstat, pDCTstat);
+		}
+
 #if IS_ENABLED(CONFIG_HAVE_ACPI_RESUME)
 		printk(BIOS_DEBUG, "mctAutoInitMCT_D: Restoring DCT configuration from NVRAM\n");
 		restore_mct_information_from_nvram();
 #endif
+
+		printk(BIOS_DEBUG, "mctAutoInitMCT_D: mct_ForceNBPState0_Dis_Fam15\n");
+		for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
+			struct DCTStatStruc *pDCTstat;
+			pDCTstat = pDCTstatA + Node;
+
+			mct_ForceNBPState0_Dis_Fam15(pMCTstat, pDCTstat);
+		}
 	} else {
 		NodesWmem = 0;
 		node_sys_base = 0;
@@ -297,15 +1260,15 @@ restartinit:
 			pDCTstat->dev_map = PA_MAP(Node);
 			pDCTstat->dev_dct = PA_DCT(Node);
 			pDCTstat->dev_nbmisc = PA_NBMISC(Node);
+			pDCTstat->dev_link = PA_LINK(Node);
+			pDCTstat->dev_nbctl = PA_NBCTL(Node);
 			pDCTstat->NodeSysBase = node_sys_base;
 
 			printk(BIOS_DEBUG, "mctAutoInitMCT_D: mct_init Node %d\n", Node);
 			mct_init(pMCTstat, pDCTstat);
 			mctNodeIDDebugPort_D();
 			pDCTstat->NodePresent = NodePresent_D(Node);
-			if (pDCTstat->NodePresent) {		/* See if Node is there*/
-				printk(BIOS_DEBUG, "mctAutoInitMCT_D: clear_legacy_Mode\n");
-				clear_legacy_Mode(pMCTstat, pDCTstat);
+			if (pDCTstat->NodePresent) {
 				pDCTstat->LogicalCPUID = mctGetLogicalCPUID_D(Node);
 
 				printk(BIOS_DEBUG, "mctAutoInitMCT_D: mct_InitialMCT_D\n");
@@ -314,6 +1277,26 @@ restartinit:
 				printk(BIOS_DEBUG, "mctAutoInitMCT_D: mctSMBhub_Init\n");
 				mctSMBhub_Init(Node);		/* Switch SMBUS crossbar to proper node*/
 
+				printk(BIOS_DEBUG, "mctAutoInitMCT_D: mct_preInitDCT\n");
+				mct_preInitDCT(pMCTstat, pDCTstat);
+			}
+			node_sys_base = pDCTstat->NodeSysBase;
+			node_sys_base += (pDCTstat->NodeSysLimit + 2) & ~0x0F;
+		}
+
+#if IS_ENABLED(DIMM_VOLTAGE_SET_SUPPORT)
+		printk(BIOS_DEBUG, "mctAutoInitMCT_D: DIMMSetVoltage\n");
+		DIMMSetVoltages(pMCTstat, pDCTstatA);	/* Set the DIMM voltages (mainboard specific) */
+#endif
+
+		for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
+			struct DCTStatStruc *pDCTstat;
+			pDCTstat = pDCTstatA + Node;
+
+			if (pDCTstat->NodePresent) {
+				printk(BIOS_DEBUG, "mctAutoInitMCT_D: mctSMBhub_Init\n");
+				mctSMBhub_Init(Node);		/* Switch SMBUS crossbar to proper node*/
+
 				printk(BIOS_DEBUG, "mctAutoInitMCT_D: mct_initDCT\n");
 				mct_initDCT(pMCTstat, pDCTstat);
 				if (pDCTstat->ErrCode == SC_FatalErr) {
@@ -321,20 +1304,13 @@ restartinit:
 				} else if (pDCTstat->ErrCode < SC_StopError) {
 					NodesWmem++;
 				}
-			}	/* if Node present */
-			node_sys_base = pDCTstat->NodeSysBase;
-			node_sys_base += (pDCTstat->NodeSysLimit + 2) & ~0x0F;
+			}
 		}
 		if (NodesWmem == 0) {
 			printk(BIOS_DEBUG, "No Nodes?!\n");
 			goto fatalexit;
 		}
 
-#if IS_ENABLED(DIMM_VOLTAGE_SET_SUPPORT)
-		printk(BIOS_DEBUG, "mctAutoInitMCT_D: DIMMSetVoltage\n");
-		DIMMSetVoltages(pMCTstat, pDCTstatA);	/* Set the DIMM voltages (mainboard specific) */
-#endif
-
 		printk(BIOS_DEBUG, "mctAutoInitMCT_D: SyncDCTsReady_D\n");
 		SyncDCTsReady_D(pMCTstat, pDCTstatA);	/* Make sure DCTs are ready for accesses.*/
 
@@ -355,7 +1331,6 @@ restartinit:
 		printk(BIOS_DEBUG, "mctAutoInitMCT_D: :OtherTiming\n");
 		mct_OtherTiming(pMCTstat, pDCTstatA);
 
-
 		if (ReconfigureDIMMspare_D(pMCTstat, pDCTstatA)) { /* RESET# if 1st pass of DIMM spare enabled*/
 			goto restartinit;
 		}
@@ -369,6 +1344,14 @@ restartinit:
 			MCTMemClr_D(pMCTstat,pDCTstatA);
 		}
 
+		printk(BIOS_DEBUG, "mctAutoInitMCT_D: mct_ForceNBPState0_Dis_Fam15\n");
+		for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
+			struct DCTStatStruc *pDCTstat;
+			pDCTstat = pDCTstatA + Node;
+
+			mct_ForceNBPState0_Dis_Fam15(pMCTstat, pDCTstat);
+		}
+
 		mct_FinalMCT_D(pMCTstat, pDCTstatA);
 		printk(BIOS_DEBUG, "mctAutoInitMCT_D Done: Global Status: %x\n", pMCTstat->GStatus);
 	}
@@ -408,6 +1391,425 @@ static u8 ReconfigureDIMMspare_D(struct MCTStatStruc *pMCTstat,
 	return ret;
 }
 
+/* Enable or disable phy-assisted training mode
+ * Phy-assisted training mode applies to the follow DRAM training procedures:
+ * Write Levelization Training (2.10.5.8.1)
+ * DQS Receiver Enable Training (2.10.5.8.2)
+ */
+static void fam15EnableTrainingMode(struct MCTStatStruc *pMCTstat,
+			struct DCTStatStruc *pDCTstat, uint8_t dct, uint8_t enable)
+{
+	uint8_t index;
+	uint32_t dword;
+	uint32_t index_reg = 0x98;
+	uint32_t dev = pDCTstat->dev_dct;
+
+	if (enable) {
+		/* Enable training mode */
+		dword = Get_NB32_DCT(dev, dct, 0x78);			/* DRAM Control */
+		dword &= ~(0x1 << 17);					/* AddrCmdTriEn = 0 */
+		Set_NB32_DCT(dev, dct, 0x78, dword);			/* DRAM Control */
+
+		dword = Get_NB32_DCT(dev, dct, 0x8c);			/* DRAM Timing High */
+		dword |= (0x1 << 18);					/* DisAutoRefresh = 1 */
+		Set_NB32_DCT(dev, dct, 0x8c, dword);			/* DRAM Timing High */
+
+		dword = Get_NB32_DCT(dev, dct, 0x94);			/* DRAM Configuration High */
+		dword &= ~(0xf << 24);					/* DcqBypassMax = 0 */
+		dword &= ~(0x1 << 22);					/* BankSwizzleMode = 0 */
+		dword &= ~(0x1 << 15);					/* PowerDownEn = 0 */
+		dword &= ~(0x3 << 10);					/* ZqcsInterval = 0 */
+		Set_NB32_DCT(dev, dct, 0x94, dword);			/* DRAM Configuration High */
+
+		dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0000000d);
+		dword &= ~(0xf << 16);					/* RxMaxDurDllNoLock = 0 */
+		dword &= ~(0xf);					/* TxMaxDurDllNoLock = 0 */
+		Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0000000d, dword);
+
+		for (index = 0; index < 0x9; index++) {
+			dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f0010 | (index << 8));
+			dword &= ~(0x1 << 12);				/* EnRxPadStandby = 0 */
+			Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f0010 | (index << 8), dword);
+		}
+
+		dword = Get_NB32_DCT(dev, dct, 0xa4);			/* DRAM Controller Temperature Throttle */
+		dword &= ~(0x1 << 11);					/* BwCapEn = 0 */
+		dword &= ~(0x1 << 8);					/* ODTSEn = 0 */
+		Set_NB32_DCT(dev, dct, 0xa4, dword);			/* DRAM Controller Temperature Throttle */
+
+		dword = Get_NB32_DCT(dev, dct, 0x110);			/* DRAM Controller Select Low */
+		dword &= ~(0x1 << 2);					/* DctSelIntLvEn = 0 */
+		Set_NB32_DCT(dev, dct, 0x110, dword);			/* DRAM Controller Select Low */
+
+		dword = Get_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x58);	/* Scrub Rate Control */
+		dword &= ~(0x1f << 24);					/* L3Scrub = 0 */
+		dword &= ~(0x1f);					/* DramScrub = 0 */
+		Set_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x58, dword);	/* Scrub Rate Control */
+
+		dword = Get_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x5c);	/* DRAM Scrub Address Low */
+		dword &= ~(0x1);					/* ScrubReDirEn = 0 */
+		Set_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x5c, dword);	/* DRAM Scrub Address Low */
+
+		dword = Get_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x1b8);	/* L3 Control 1 */
+		dword |= (0x1 << 4);					/* L3ScrbRedirDis = 1 */
+		Set_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x1b8, dword);	/* L3 Control 1 */
+
+		/* Fam15h BKDG section 2.10.5.5.1 */
+		dword = Get_NB32_DCT(dev, dct, 0x218);			/* DRAM Timing 5 */
+		dword &= ~(0xf << 24);					/* TrdrdSdSc = 0xb */
+		dword |= (0xb << 24);
+		dword &= ~(0xf << 16);					/* TrdrdSdDc = 0xb */
+		dword |= (0xb << 16);
+		dword &= ~(0xf);					/* TrdrdDd = 0xb */
+		dword |= 0xb;
+		Set_NB32_DCT(dev, dct, 0x218, dword);			/* DRAM Timing 5 */
+
+		/* Fam15h BKDG section 2.10.5.5.2 */
+		dword = Get_NB32_DCT(dev, dct, 0x214);			/* DRAM Timing 4 */
+		dword &= ~(0xf << 16);					/* TwrwrSdSc = 0xb */
+		dword |= (0xb << 16);
+		dword &= ~(0xf << 8);					/* TwrwrSdDc = 0xb */
+		dword |= (0xb << 8);
+		dword &= ~(0xf);					/* TwrwrDd = 0xb */
+		dword |= 0xb;
+		Set_NB32_DCT(dev, dct, 0x214, dword);			/* DRAM Timing 4 */
+
+		/* Fam15h BKDG section 2.10.5.5.3 */
+		dword = Get_NB32_DCT(dev, dct, 0x218);			/* DRAM Timing 5 */
+		dword &= ~(0xf << 8);					/* Twrrd = 0xb */
+		dword |= (0xb << 8);
+		Set_NB32_DCT(dev, dct, 0x218, dword);			/* DRAM Timing 5 */
+
+		/* Fam15h BKDG section 2.10.5.5.4 */
+		dword = Get_NB32_DCT(dev, dct, 0x21c);			/* DRAM Timing 6 */
+		dword &= ~(0x1f << 8);					/* TrwtTO = 0x16 */
+		dword |= (0x16 << 8);
+		dword &= ~(0x1f << 16);					/* TrwtWB = TrwtTO + 1 */
+		dword |= ((((dword >> 8) & 0x1f) + 1) << 16);
+		Set_NB32_DCT(dev, dct, 0x21c, dword);			/* DRAM Timing 6 */
+	} else {
+		/* Disable training mode */
+		uint8_t lane;
+		uint8_t dimm;
+		uint8_t receiver;
+		uint8_t max_lane;
+		uint8_t ecc_enabled;
+		uint8_t x4_present = 0;
+		uint8_t x8_present = 0;
+		uint8_t memclk_index;
+		uint8_t interleave_channels = 0;
+		uint8_t redirect_ecc_scrub = 0;
+		uint16_t trdrdsddc;
+		uint16_t trdrddd;
+		uint16_t cdd_trdrddd;
+		uint16_t twrwrsddc;
+		uint16_t twrwrdd;
+		uint16_t cdd_twrwrdd;
+		uint16_t twrrd;
+		uint16_t trwtto;
+		uint8_t first_dimm;
+		uint16_t delay;
+		uint16_t delay2;
+		uint8_t read_odt_delay;
+		uint8_t write_odt_delay;
+		uint16_t difference;
+		uint16_t current_total_delay_1[MAX_BYTE_LANES];
+		uint16_t current_total_delay_2[MAX_BYTE_LANES];
+
+		/* FIXME
+		 * This should be platform configurable
+		 */
+		uint8_t dimm_event_l_pin_support = 0;
+
+		ecc_enabled = !!(pMCTstat->GStatus & 1 << GSB_ECCDIMMs);
+		if (ecc_enabled)
+			max_lane = 9;
+		else
+			max_lane = 8;
+
+		if (pDCTstat->Dimmx4Present & ((dct)?0xaa:0x55))
+			x4_present = 1;
+		if (pDCTstat->Dimmx8Present & ((dct)?0xaa:0x55))
+			x8_present = 1;
+		memclk_index = Get_NB32_DCT(dev, dct, 0x94) & 0x1f;
+
+		if (pDCTstat->DIMMValidDCT[0] && pDCTstat->DIMMValidDCT[1] && mctGet_NVbits(NV_Unganged))
+			interleave_channels = 1;
+
+		if ((pMCTstat->GStatus & 1 << GSB_ECCDIMMs) && mctGet_NVbits(NV_ECCRedir))
+			redirect_ecc_scrub = 1;
+
+		dword = (Get_NB32_DCT(dev, dct, 0x240) >> 4) & 0xf;
+		if (dword > 6)
+			read_odt_delay = dword - 6;
+		else
+			read_odt_delay = 0;
+
+		dword = Get_NB32_DCT(dev, dct, 0x240);
+		delay = (dword >> 4) & 0xf;
+		if (delay > 6)
+			read_odt_delay = delay - 6;
+		else
+			read_odt_delay = 0;
+		delay = (dword >> 12) & 0x7;
+		if (delay > 6)
+			write_odt_delay = delay - 6;
+		else
+			write_odt_delay = 0;
+
+		/* TODO:
+		 * Adjust trdrdsddc if four-rank DIMMs are installed per
+		 * section 2.10.5.5.1 of the Family 15h BKDG.
+		 * cdd_trdrdsddc will also need to be calculated in that process.
+		 */
+		trdrdsddc = 3;
+
+		/* Calculate the Critical Delay Difference for TrdrdDd */
+		cdd_trdrddd = 0;
+		first_dimm = 1;
+		for (receiver = 0; receiver < 8; receiver += 2) {
+			dimm = (receiver >> 1);
+
+			if (!mct_RcvrRankEnabled_D(pMCTstat, pDCTstat, dct, receiver))
+				continue;
+
+			read_dqs_receiver_enable_control_registers(current_total_delay_2, dev, dct, dimm, index_reg);
+
+			if (first_dimm) {
+				memcpy(current_total_delay_1, current_total_delay_2, sizeof(current_total_delay_1));
+				first_dimm = 0;
+			}
+
+			for (lane = 0; lane < max_lane; lane++) {
+				if (current_total_delay_1[lane] > current_total_delay_2[lane])
+					difference = current_total_delay_1[lane] - current_total_delay_2[lane];
+				else
+					difference = current_total_delay_2[lane] - current_total_delay_1[lane];
+
+				if (difference > cdd_trdrddd)
+					cdd_trdrddd = difference;
+			}
+		}
+
+		/* Convert the difference to MEMCLKs */
+		cdd_trdrddd = (((cdd_trdrddd >> 5) & 0x1f) + 1) / 2;
+
+		/* Calculate Trdrddd */
+		delay = (read_odt_delay + 3) * 2;
+		delay2 = cdd_trdrddd + 7;
+		if (delay2 > delay)
+			delay = delay2;
+		trdrddd = (delay + 1) / 2;	/* + 1 is equivalent to ceiling function here */
+		if (trdrdsddc > trdrddd)
+			trdrddd = trdrdsddc;
+
+		/* TODO:
+		 * Adjust twrwrsddc if four-rank DIMMs are installed per
+		 * section 2.10.5.5.1 of the Family 15h BKDG.
+		 * cdd_twrwrsddc will also need to be calculated in that process.
+		 */
+		twrwrsddc = 4;
+
+		/* Calculate the Critical Delay Difference for TwrwrDd */
+		cdd_twrwrdd = 0;
+		first_dimm = 1;
+		for (receiver = 0; receiver < 8; receiver += 2) {
+			dimm = (receiver >> 1);
+
+			if (!mct_RcvrRankEnabled_D(pMCTstat, pDCTstat, dct, receiver))
+				continue;
+
+			read_dqs_write_timing_control_registers(current_total_delay_2, dev, dct, dimm, index_reg);
+
+			if (first_dimm) {
+				memcpy(current_total_delay_1, current_total_delay_2, sizeof(current_total_delay_1));
+				first_dimm = 0;
+			}
+
+			for (lane = 0; lane < max_lane; lane++) {
+				if (current_total_delay_1[lane] > current_total_delay_2[lane])
+					difference = current_total_delay_1[lane] - current_total_delay_2[lane];
+				else
+					difference = current_total_delay_2[lane] - current_total_delay_1[lane];
+
+				if (difference > cdd_twrwrdd)
+					cdd_twrwrdd = difference;
+			}
+		}
+
+		/* Convert the difference to MEMCLKs */
+		cdd_twrwrdd = (((cdd_twrwrdd >> 5) & 0x1f) + 1) / 2;
+
+		/* Calculate Twrwrdd */
+		delay = (write_odt_delay + 3) * 2;
+		delay2 = cdd_twrwrdd + 7;
+		if (delay2 > delay)
+			delay = delay2;
+		twrwrdd = (delay + 1) / 2;	/* + 1 is equivalent to ceiling function here */
+		if (twrwrsddc > twrwrdd)
+			twrwrdd = twrwrsddc;
+
+		dword = Get_NB32_DCT(dev, dct, 0x78);			/* DRAM Control */
+		dword |= (0x1 << 17);					/* AddrCmdTriEn = 1 */
+		Set_NB32_DCT(dev, dct, 0x78, dword);			/* DRAM Control */
+
+		dword = Get_NB32_DCT(dev, dct, 0x8c);			/* DRAM Timing High */
+		dword &= ~(0x1 << 18);					/* DisAutoRefresh = 0 */
+		Set_NB32_DCT(dev, dct, 0x8c, dword);			/* DRAM Timing High */
+
+		dword = Get_NB32_DCT(dev, dct, 0x94);			/* DRAM Configuration High */
+		dword |= (0xf << 24);					/* DcqBypassMax = 0xf */
+		dword |= (0x1 << 22);					/* BankSwizzleMode = 1 */
+		dword |= (0x1 << 15);					/* PowerDownEn = 1 */
+		dword &= ~(0x3 << 10);					/* ZqcsInterval = 0x2 */
+		dword |= (0x2 << 10);
+		Set_NB32_DCT(dev, dct, 0x94, dword);			/* DRAM Configuration High */
+
+		if (x4_present && x8_present) {
+			/* Mixed channel of 4x and 8x DIMMs */
+			dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0000000d);
+			dword &= ~(0x3 << 24);					/* RxDLLWakeupTime = 0 */
+			dword &= ~(0x7 << 20);					/* RxCPUpdPeriod = 0 */
+			dword &= ~(0xf << 16);					/* RxMaxDurDllNoLock = 0 */
+			dword &= ~(0x3 << 8);					/* TxDLLWakeupTime = 0 */
+			dword &= ~(0x7 << 4);					/* TxCPUpdPeriod = 0 */
+			dword &= ~(0xf);					/* TxMaxDurDllNoLock = 0 */
+			Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0000000d, dword);
+		} else {
+			dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0000000d);
+			dword &= ~(0x3 << 24);					/* RxDLLWakeupTime = 3 */
+			dword |= (0x3 << 24);
+			dword &= ~(0x7 << 20);					/* RxCPUpdPeriod = 3 */
+			dword |= (0x3 << 20);
+			dword &= ~(0xf << 16);					/* RxMaxDurDllNoLock = 7 */
+			dword |= (0x7 << 16);
+			dword &= ~(0x3 << 8);					/* TxDLLWakeupTime = 3 */
+			dword |= (0x3 << 8);
+			dword &= ~(0x7 << 4);					/* TxCPUpdPeriod = 3 */
+			dword |= (0x3 << 4);
+			dword &= ~(0xf);					/* TxMaxDurDllNoLock = 7 */
+			dword |= 0x7;
+			Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0000000d, dword);
+		}
+
+		if ((memclk_index <= 0x12) && (x4_present != x8_present)) {
+			/* MemClkFreq <= 800MHz
+			 * Not a mixed channel of x4 and x8 DIMMs
+			 */
+			for (index = 0; index < 0x9; index++) {
+				dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f0010 | (index << 8));
+				dword |= (0x1 << 12);				/* EnRxPadStandby = 1 */
+				Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f0010 | (index << 8), dword);
+			}
+		} else {
+			for (index = 0; index < 0x9; index++) {
+				dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f0010 | (index << 8));
+				dword &= ~(0x1 << 12);				/* EnRxPadStandby = 0 */
+				Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f0010 | (index << 8), dword);
+			}
+		}
+
+		/* TODO
+		 * Calculate Twrrd per section 2.10.5.5.3 of the Family 15h BKDG
+		 */
+		twrrd = 0xb;
+
+		/* TODO
+		 * Calculate TrwtTO per section 2.10.5.5.4 of the Family 15h BKDG
+		 */
+		trwtto = 0x16;
+
+		dword = Get_NB32_DCT(dev, dct, 0xa4);			/* DRAM Controller Temperature Throttle */
+		dword &= ~(0x1 << 11);					/* BwCapEn = 0 */
+		dword &= ~(0x1 << 8);					/* ODTSEn = dimm_event_l_pin_support */
+		dword |= (dimm_event_l_pin_support & 0x1) << 8;
+		Set_NB32_DCT(dev, dct, 0xa4, dword);			/* DRAM Controller Temperature Throttle */
+
+		dword = Get_NB32_DCT(dev, dct, 0x110);			/* DRAM Controller Select Low */
+		dword &= ~(0x1 << 2);					/* DctSelIntLvEn = interleave_channels */
+		dword |= (interleave_channels & 0x1) << 2;
+		Set_NB32_DCT(dev, dct, 0x110, dword);			/* DRAM Controller Select Low */
+
+		dword = Get_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x58);	/* Scrub Rate Control */
+		dword &= ~(0x1f << 24);					/* L3Scrub = NV_L3BKScrub */
+		dword |= (mctGet_NVbits(NV_L3BKScrub) & 0x1f) << 24;
+		dword &= ~(0x1f);					/* DramScrub = NV_DramBKScrub */
+		dword |= mctGet_NVbits(NV_DramBKScrub) & 0x1f;
+		Set_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x58, dword);	/* Scrub Rate Control */
+
+		dword = Get_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x5c);	/* DRAM Scrub Address Low */
+		dword &= ~(0x1);					/* ScrubReDirEn = redirect_ecc_scrub */
+		dword |= redirect_ecc_scrub & 0x1;
+		Set_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x5c, dword);	/* DRAM Scrub Address Low */
+
+		dword = Get_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x1b8);	/* L3 Control 1 */
+		dword &= ~(0x1 << 4);					/* L3ScrbRedirDis = 0 */
+		Set_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x1b8, dword);	/* L3 Control 1 */
+
+		/* FIXME
+		 * The BKDG-recommended settings cause memory corruption on the ASUS KGPE-D16.
+		 * Investigate and fix...
+		 */
+#if 0
+		/* Fam15h BKDG section 2.10.5.5.1 */
+		dword = Get_NB32_DCT(dev, dct, 0x218);			/* DRAM Timing 5 */
+		dword &= ~(0xf << 24);					/* TrdrdSdSc = 0x1 */
+		dword |= (0x1 << 24);
+		dword &= ~(0xf << 16);					/* TrdrdSdDc = trdrdsddc */
+		dword |= ((trdrdsddc & 0xf) << 16);
+		dword &= ~(0xf);					/* TrdrdDd = trdrddd */
+		dword |= (trdrddd & 0xf);
+		Set_NB32_DCT(dev, dct, 0x218, dword);			/* DRAM Timing 5 */
+#endif
+
+		/* Fam15h BKDG section 2.10.5.5.2 */
+		dword = Get_NB32_DCT(dev, dct, 0x214);			/* DRAM Timing 4 */
+		dword &= ~(0xf << 16);					/* TwrwrSdSc = 0x1 */
+		dword |= (0x1 << 16);
+		dword &= ~(0xf << 8);					/* TwrwrSdDc = twrwrsddc */
+		dword |= ((twrwrsddc & 0xf) << 8);
+		dword &= ~(0xf);					/* TwrwrDd = twrwrdd */
+		dword |= (twrwrdd & 0xf);
+		Set_NB32_DCT(dev, dct, 0x214, dword);			/* DRAM Timing 4 */
+
+		/* Fam15h BKDG section 2.10.5.5.3 */
+		dword = Get_NB32_DCT(dev, dct, 0x218);			/* DRAM Timing 5 */
+		dword &= ~(0xf << 8);					/* Twrrd = twrrd */
+		dword |= ((twrrd & 0xf) << 8);
+		Set_NB32_DCT(dev, dct, 0x218, dword);			/* DRAM Timing 5 */
+
+		/* Fam15h BKDG section 2.10.5.5.4 */
+		dword = Get_NB32_DCT(dev, dct, 0x21c);			/* DRAM Timing 6 */
+		dword &= ~(0x1f << 8);					/* TrwtTO = trwtto */
+		dword |= ((trwtto & 0x1f) << 8);
+		dword &= ~(0x1f << 16);					/* TrwtWB = TrwtTO + 1 */
+		dword |= ((((dword >> 8) & 0x1f) + 1) << 16);
+		Set_NB32_DCT(dev, dct, 0x21c, dword);			/* DRAM Timing 6 */
+
+		/* Enable prefetchers */
+		dword = Get_NB32_DCT(dev, dct, 0x110);			/* Memory Controller Configuration High */
+		dword &= ~(0x1 << 13);					/* PrefIoDis = 0 */
+		dword &= ~(0x1 << 12);					/* PrefCpuDis = 0 */
+		Set_NB32_DCT(dev, dct, 0x110, dword);			/* Memory Controller Configuration High */
+	}
+}
+
+static void exit_training_mode_fam15(struct MCTStatStruc *pMCTstat,
+				struct DCTStatStruc *pDCTstatA)
+{
+	uint8_t node;
+	uint8_t dct;
+
+	for (node = 0; node < MAX_NODES_SUPPORTED; node++) {
+		struct DCTStatStruc *pDCTstat;
+		pDCTstat = pDCTstatA + node;
+
+		if (pDCTstat->NodePresent)
+			for (dct = 0; dct < 2; dct++)
+				fam15EnableTrainingMode(pMCTstat, pDCTstat, dct, 0);
+	}
+}
+
 static void DQSTiming_D(struct MCTStatStruc *pMCTstat,
 				struct DCTStatStruc *pDCTstatA)
 {
@@ -424,6 +1826,20 @@ static void DQSTiming_D(struct MCTStatStruc *pMCTstat,
 	mct_BeforeDQSTrain_D(pMCTstat, pDCTstatA);
 	phyAssistedMemFnceTraining(pMCTstat, pDCTstatA);
 
+	if (is_fam15h()) {
+		uint8_t Node;
+		struct DCTStatStruc *pDCTstat;
+		for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
+			pDCTstat = pDCTstatA + Node;
+			if (pDCTstat->NodePresent) {
+				if (pDCTstat->DIMMValidDCT[0])
+					InitPhyCompensation(pMCTstat, pDCTstat, 0);
+				if (pDCTstat->DIMMValidDCT[1])
+					InitPhyCompensation(pMCTstat, pDCTstat, 1);
+			}
+		}
+	}
+
 	if (nv_DQSTrainCTL) {
 		mctHookBeforeAnyTraining(pMCTstat, pDCTstatA);
 		/* TODO: should be in mctHookBeforeAnyTraining */
@@ -431,16 +1847,35 @@ static void DQSTiming_D(struct MCTStatStruc *pMCTstat,
 		_WRMSR(0x26D, 0x04040404, 0x04040404);
 		_WRMSR(0x26E, 0x04040404, 0x04040404);
 		_WRMSR(0x26F, 0x04040404, 0x04040404);
-		mct_WriteLevelization_HW(pMCTstat, pDCTstatA);
+		mct_WriteLevelization_HW(pMCTstat, pDCTstatA, FirstPass);
 
-		TrainReceiverEn_D(pMCTstat, pDCTstatA, FirstPass);
+		if (is_fam15h()) {
+			/* Receiver Enable Training Pass 1 */
+			TrainReceiverEn_D(pMCTstat, pDCTstatA, FirstPass);
+		}
 
-		mct_TrainDQSPos_D(pMCTstat, pDCTstatA);
+		mct_WriteLevelization_HW(pMCTstat, pDCTstatA, SecondPass);
+
+		if (is_fam15h()) {
+			/* Receiver Enable Training Pass 2 */
+			// TrainReceiverEn_D(pMCTstat, pDCTstatA, SecondPass);
+
+			/* TODO:
+			 * Determine why running TrainReceiverEn_D in SecondPass
+			 * mode yields less stable training values than when run
+			 * in FirstPass mode as in the HACK below.
+			 */
+			TrainReceiverEn_D(pMCTstat, pDCTstatA, FirstPass);
+		} else {
+			TrainReceiverEn_D(pMCTstat, pDCTstatA, FirstPass);
+		}
 
-		/* Second Pass never used for Barcelona! */
-		/* TrainReceiverEn_D(pMCTstat, pDCTstatA, SecondPass); */
+		mct_TrainDQSPos_D(pMCTstat, pDCTstatA);
 
-		mctSetEccDQSRcvrEn_D(pMCTstat, pDCTstatA);
+		if (is_fam15h())
+			exit_training_mode_fam15(pMCTstat, pDCTstatA);
+		else
+			mctSetEccDQSRcvrEn_D(pMCTstat, pDCTstatA);
 
 		/* FIXME - currently uses calculated value	TrainMaxReadLatency_D(pMCTstat, pDCTstatA); */
 		mctHookAfterAnyTraining();
@@ -476,7 +1911,7 @@ static void LoadDQSSigTmgRegs_D(struct MCTStatStruc *pMCTstat,
 			for (Channel = 0;Channel < 2; Channel++) {
 				/* there are four receiver pairs,
 				   loosely associated with chipselects.*/
-				index_reg = 0x98 + Channel * 0x100;
+				index_reg = 0x98;
 				for (Receiver = 0; Receiver < 8; Receiver += 2) {
 					/* Set Receiver Enable Values */
 					mct_SetRcvrEnDly_D(pDCTstat,
@@ -492,7 +1927,7 @@ static void LoadDQSSigTmgRegs_D(struct MCTStatStruc *pMCTstat,
 						txdqs = pDCTstat->CH_D_B_TxDqs[Channel][Receiver >> 1][ByteLane];
 						index = Table_DQSRcvEn_Offset[ByteLane >> 1];
 						index += (Receiver >> 1) * 3 + 0x10 + 0x20; /* Addl_Index */
-						val = Get_NB32_index_wait(dev, 0x98 + 0x100*Channel, index);
+						val = Get_NB32_index_wait_DCT(dev, Channel, 0x98, index);
 						if (ByteLane & 1) { /* odd byte lane */
 							val &= ~(0xFF << 16);
 							val |= txdqs << 16;
@@ -500,7 +1935,7 @@ static void LoadDQSSigTmgRegs_D(struct MCTStatStruc *pMCTstat,
 							val &= ~0xFF;
 							val |= txdqs;
 						}
-						Set_NB32_index_wait(dev, 0x98 + 0x100*Channel, index, val);
+						Set_NB32_index_wait_DCT(dev, Channel, 0x98, index, val);
 					}
 				}
 			}
@@ -510,7 +1945,7 @@ static void LoadDQSSigTmgRegs_D(struct MCTStatStruc *pMCTstat,
 
 			for (Channel = 0; Channel < 2; Channel++) {
 				u8 *p;
-				index_reg = 0x98 + Channel * 0x100;
+				index_reg = 0x98;
 
 				/* NOTE:
 				 * when 400, 533, 667, it will support dimm0/1/2/3,
@@ -525,7 +1960,7 @@ static void LoadDQSSigTmgRegs_D(struct MCTStatStruc *pMCTstat,
 					if (DIMM == 0) {
 						index = 0;	/* CHA Write Data Timing Low */
 					} else {
-						if (pDCTstat->Speed >= 4) {
+						if (pDCTstat->Speed >= mhz_to_memclk_config(mctGet_NVbits(NV_MIN_MEMCLK))) {
 							index = 0x100 * DIMM;
 						} else {
 							break;
@@ -534,23 +1969,23 @@ static void LoadDQSSigTmgRegs_D(struct MCTStatStruc *pMCTstat,
 					for (Dir = 0; Dir < 2; Dir++) {/* RD/WR */
 						p = pDCTstat->CH_D_DIR_B_DQS[Channel][DIMM][Dir];
 						val = stream_to_int(p); /* CHA Read Data Timing High */
-						Set_NB32_index_wait(dev, index_reg, index+1, val);
+						Set_NB32_index_wait_DCT(dev, Channel, index_reg, index+1, val);
 						val = stream_to_int(p+4); /* CHA Write Data Timing High */
-						Set_NB32_index_wait(dev, index_reg, index+2, val);
+						Set_NB32_index_wait_DCT(dev, Channel, index_reg, index+2, val);
 						val = *(p+8); /* CHA Write ECC Timing */
-						Set_NB32_index_wait(dev, index_reg, index+3, val);
+						Set_NB32_index_wait_DCT(dev, Channel, index_reg, index+3, val);
 						index += 4;
 					}
 				}
 			}
 
 			for (Channel = 0; Channel<2; Channel++) {
-				reg = 0x78 + Channel * 0x100;
-				val = Get_NB32(dev, reg);
+				reg = 0x78;
+				val = Get_NB32_DCT(dev, Channel, reg);
 				val &= ~(0x3ff<<22);
 				val |= ((u32) pDCTstat->CH_MaxRdLat[Channel] << 22);
 				val &= ~(1<<DqsRcvEnTrain);
-				Set_NB32(dev, reg, val);	/* program MaxRdLatency to correspond with current delay*/
+				Set_NB32_DCT(dev, Channel, reg, val);	/* program MaxRdLatency to correspond with current delay*/
 			}
 		}
 	}
@@ -812,49 +2247,70 @@ finish:
 	return ret;
 }
 
-static void DCTInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, u8 dct)
+static void DCTPreInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, u8 dct)
 {
 	/*
-	 * Initialize DRAM on single Athlon 64/Opteron Node.
+	 * Run DCT pre-initialization tasks
 	 */
-	u8 stopDCTflag;
-	u32 val;
+	uint32_t dword;
 
+	/* Reset DCT registers */
 	ClearDCT_D(pMCTstat, pDCTstat, dct);
-	stopDCTflag = 1;		/*preload flag with 'disable' */
-	/* enable DDR3 support */
-	val = Get_NB32(pDCTstat->dev_dct, 0x94 + dct * 0x100);
-	val |= 1 << Ddr3Mode;
-	Set_NB32(pDCTstat->dev_dct, 0x94 + dct * 0x100, val);
+	pDCTstat->stopDCT = 1;		/*preload flag with 'disable' */
+
+	if (!is_fam15h()) {
+		/* Enable DDR3 support */
+		dword = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0x94);
+		dword |= 1 << Ddr3Mode;
+		Set_NB32_DCT(pDCTstat->dev_dct, dct, 0x94, dword);
+	}
+
+	/* Read the SPD information into the data structures */
 	if (mct_DIMMPresence(pMCTstat, pDCTstat, dct) < SC_StopError) {
 		printk(BIOS_DEBUG, "\t\tDCTInit_D: mct_DIMMPresence Done\n");
-		if (mct_SPDCalcWidth(pMCTstat, pDCTstat, dct) < SC_StopError) {
-			printk(BIOS_DEBUG, "\t\tDCTInit_D: mct_SPDCalcWidth Done\n");
-			if (AutoCycTiming_D(pMCTstat, pDCTstat, dct) < SC_StopError) {
-				printk(BIOS_DEBUG, "\t\tDCTInit_D: AutoCycTiming_D Done\n");
-				if (AutoConfig_D(pMCTstat, pDCTstat, dct) < SC_StopError) {
-					printk(BIOS_DEBUG, "\t\tDCTInit_D: AutoConfig_D Done\n");
-					if (PlatformSpec_D(pMCTstat, pDCTstat, dct) < SC_StopError) {
-						printk(BIOS_DEBUG, "\t\tDCTInit_D: PlatformSpec_D Done\n");
-						stopDCTflag = 0;
-						if (!(pMCTstat->GStatus & (1 << GSB_EnDIMMSpareNW))) {
-							printk(BIOS_DEBUG, "\t\tDCTInit_D: StartupDCT_D\n");
-							StartupDCT_D(pMCTstat, pDCTstat, dct);   /*yeaahhh! */
-						}
+	}
+}
+
+static void DCTInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, u8 dct)
+{
+	/*
+	 * Initialize DRAM on single Athlon 64/Opteron Node.
+	 */
+	uint32_t dword;
+
+	if (!is_fam15h()) {
+		/* (Re)-enable DDR3 support */
+		dword = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0x94);
+		dword |= 1 << Ddr3Mode;
+		Set_NB32_DCT(pDCTstat->dev_dct, dct, 0x94, dword);
+	}
+
+	if (mct_SPDCalcWidth(pMCTstat, pDCTstat, dct) < SC_StopError) {
+		printk(BIOS_DEBUG, "\t\tDCTInit_D: mct_SPDCalcWidth Done\n");
+		if (AutoCycTiming_D(pMCTstat, pDCTstat, dct) < SC_StopError) {
+			printk(BIOS_DEBUG, "\t\tDCTInit_D: AutoCycTiming_D Done\n");
+			if (AutoConfig_D(pMCTstat, pDCTstat, dct) < SC_StopError) {
+				printk(BIOS_DEBUG, "\t\tDCTInit_D: AutoConfig_D Done\n");
+				if (PlatformSpec_D(pMCTstat, pDCTstat, dct) < SC_StopError) {
+					printk(BIOS_DEBUG, "\t\tDCTInit_D: PlatformSpec_D Done\n");
+					pDCTstat->stopDCT = 0;
+					if (!(pMCTstat->GStatus & (1 << GSB_EnDIMMSpareNW))) {
+						printk(BIOS_DEBUG, "\t\tDCTInit_D: StartupDCT_D\n");
+						StartupDCT_D(pMCTstat, pDCTstat, dct);   /*yeaahhh! */
 					}
 				}
 			}
 		}
 	}
 
-	if (stopDCTflag) {
-		u32 reg_off = dct * 0x100;
-		val = 1<<DisDramInterface;
-		Set_NB32(pDCTstat->dev_dct, reg_off+0x94, val);
-		/*To maximize power savings when DisDramInterface=1b,
-		  all of the MemClkDis bits should also be set.*/
-		val = 0xFF000000;
-		Set_NB32(pDCTstat->dev_dct, reg_off+0x88, val);
+	if (pDCTstat->stopDCT) {
+		dword = 1 << DisDramInterface;
+		Set_NB32_DCT(pDCTstat->dev_dct, dct, 0x94, dword);
+
+		/* To maximize power savings when DisDramInterface=1b,
+		 * all of the MemClkDis bits should also be set.
+		 */
+		Set_NB32_DCT(pDCTstat->dev_dct, dct, 0x88, 0xff000000);
 	} else {
 		mct_EnDllShutdownSR(pMCTstat, pDCTstat, dct);
 	}
@@ -876,20 +2332,24 @@ static void SyncDCTsReady_D(struct MCTStatStruc *pMCTstat,
 		pDCTstat = pDCTstatA + Node;
 		mct_SyncDCTsReady(pDCTstat);
 	}
-	/* v6.1.3 */
-	/* re-enable phy compensation engine when dram init is completed on all nodes. */
-	for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
-		struct DCTStatStruc *pDCTstat;
-		pDCTstat = pDCTstatA + Node;
-		if (pDCTstat->NodePresent) {
-			if (pDCTstat->DIMMValidDCT[0] > 0 || pDCTstat->DIMMValidDCT[1] > 0) {
-				/* re-enable phy compensation engine when dram init on both DCTs is completed. */
-				val = Get_NB32_index_wait(pDCTstat->dev_dct, 0x98, 0x8);
-				val &= ~(1 << DisAutoComp);
-				Set_NB32_index_wait(pDCTstat->dev_dct, 0x98, 0x8, val);
+
+	if (!is_fam15h()) {
+		/* v6.1.3 */
+		/* re-enable phy compensation engine when dram init is completed on all nodes. */
+		for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
+			struct DCTStatStruc *pDCTstat;
+			pDCTstat = pDCTstatA + Node;
+			if (pDCTstat->NodePresent) {
+				if (pDCTstat->DIMMValidDCT[0] > 0 || pDCTstat->DIMMValidDCT[1] > 0) {
+					/* re-enable phy compensation engine when dram init on both DCTs is completed. */
+					val = Get_NB32_index_wait(pDCTstat->dev_dct, 0x98, 0x8);
+					val &= ~(1 << DisAutoComp);
+					Set_NB32_index_wait(pDCTstat->dev_dct, 0x98, 0x8, val);
+				}
 			}
 		}
 	}
+
 	/* wait 750us before any memory access can be made. */
 	mct_Wait(15000);
 }
@@ -911,10 +2371,9 @@ static void StartupDCT_D(struct MCTStatStruc *pMCTstat,
 	 */
 	u32 val;
 	u32 dev;
-	u32 reg_off = dct * 0x100;
 
 	dev = pDCTstat->dev_dct;
-	val = Get_NB32(dev, 0x94 + reg_off);
+	val = Get_NB32_DCT(dev, dct, 0x94);
 	if (val & (1<<MemClkFreqVal)) {
 		mctHookBeforeDramInit();	/* generalized Hook */
 		if (!(pMCTstat->GStatus & (1 << GSB_EnDIMMSpareNW)))
@@ -929,23 +2388,23 @@ static void ClearDCT_D(struct MCTStatStruc *pMCTstat,
 {
 	u32 reg_end;
 	u32 dev = pDCTstat->dev_dct;
-	u32 reg = 0x40 + 0x100 * dct;
+	u32 reg = 0x40;
 	u32 val = 0;
 
 	if (pMCTstat->GStatus & (1 << GSB_EnDIMMSpareNW)) {
-		reg_end = 0x78 + 0x100 * dct;
+		reg_end = 0x78;
 	} else {
-		reg_end = 0xA4 + 0x100 * dct;
+		reg_end = 0xA4;
 	}
 
 	while(reg < reg_end) {
 		if ((reg & 0xFF) == 0x90) {
 			if (pDCTstat->LogicalCPUID & AMD_DR_Dx) {
-				val = Get_NB32(dev, reg); /* get DRAMConfigLow */
+				val = Get_NB32_DCT(dev, dct, reg); /* get DRAMConfigLow */
 				val |= 0x08000000; /* preserve value of DisDllShutdownSR for only Rev.D */
 			}
 		}
-		Set_NB32(dev, reg, val);
+		Set_NB32_DCT(dev, dct, reg, val);
 		val = 0;
 		reg += 4;
 	}
@@ -964,6 +2423,7 @@ static void SPD2ndTiming(struct MCTStatStruc *pMCTstat,
 	u16 Trp, Trrd, Trcd, Tras, Trc;
 	u8 Trfc[4];
 	u16 Tfaw;
+	u16 Tcwl;	/* Fam15h only */
 	u32 DramTimingLo, DramTimingHi;
 	u8 tCK16x;
 	u16 Twtr;
@@ -972,10 +2432,11 @@ static void SPD2ndTiming(struct MCTStatStruc *pMCTstat,
 	u8 byte;
 	u32 dword;
 	u32 dev;
-	u32 reg_off;
 	u32 val;
 	u16 smbaddr;
 
+	printk(BIOS_DEBUG, "%s: Start\n", __func__);
+
 	/* Gather all DIMM mini-max values for cycle timing data */
 	Trp = 0;
 	Trrd = 0;
@@ -1188,88 +2649,164 @@ static void SPD2ndTiming(struct MCTStatStruc *pMCTstat,
 
 	mctAdjustAutoCycTmg_D();
 
+	if (is_fam15h()) {
+		/* Compute Tcwl (Fam15h BKDG v3.14 Table 203) */
+		if (pDCTstat->Speed <= 0x6)
+			Tcwl = 0x5;
+		else if (pDCTstat->Speed == 0xa)
+			Tcwl = 0x6;
+		else if (pDCTstat->Speed == 0xe)
+			Tcwl = 0x7;
+		else if (pDCTstat->Speed == 0x12)
+			Tcwl = 0x8;
+		else if (pDCTstat->Speed == 0x16)
+			Tcwl = 0x9;
+		else
+			Tcwl = 0x5;	/* Power-on default */
+	}
+
 	/* Program DRAM Timing values */
-	DramTimingLo = 0;	/* Dram Timing Low init */
-	val = pDCTstat->CASL - 4; /* pDCTstat.CASL to reg. definition */
-	DramTimingLo |= val;
+	if (is_fam15h()) {
+		dev = pDCTstat->dev_dct;
 
-	val = pDCTstat->Trcd - Bias_TrcdT;
-	DramTimingLo |= val<<4;
+		dword = Get_NB32_DCT(dev, dct, 0x8c);				/* DRAM Timing High */
+		val = 2;							/* Tref = 7.8us */
+		dword &= ~(0x3 << 16);
+		dword |= (val & 0x3) << 16;
+		Set_NB32_DCT(dev, dct, 0x8c, dword);				/* DRAM Timing High */
+
+		dword = Get_NB32_DCT(dev, dct, 0x200);				/* DRAM Timing 0 */
+		dword &= ~(0x3f1f1f1f);
+		dword |= ((pDCTstat->Tras + 0xf) & 0x3f) << 24;			/* Tras */
+		dword |= ((pDCTstat->Trp + 0x5) & 0x1f) << 16;			/* Trp */
+		dword |= ((pDCTstat->Trcd + 0x5) & 0x1f) << 8;			/* Trcd */
+		dword |= (pDCTstat->CASL & 0x1f);				/* Tcl */
+		Set_NB32_DCT(dev, dct, 0x200, dword);				/* DRAM Timing 0 */
+
+		dword = Get_NB32_DCT(dev, dct, 0x204);				/* DRAM Timing 1 */
+		dword &= ~(0x0f3f0f3f);
+		dword |= ((pDCTstat->Trtp + 0x4) & 0xf) << 24;			/* Trtp */
+		if (pDCTstat->Tfaw != 0)
+			dword |= ((((pDCTstat->Tfaw - 0x1) * 2) + 0x10) & 0x3f) << 16;	/* FourActWindow */
+		dword |= ((pDCTstat->Trrd + 0x4) & 0xf) << 8;			/* Trrd */
+		dword |= ((pDCTstat->Trc + 0xb) & 0x3f);			/* Trc */
+		Set_NB32_DCT(dev, dct, 0x204, dword);				/* DRAM Timing 1 */
+
+		dword = Get_NB32_DCT(dev, dct, 0x208);				/* DRAM Timing 2 */
+		dword &= ~(0x07070707);
+		dword |= (pDCTstat->Trfc[3] & 0x7) << 24;			/* Trfc3 */
+		dword |= (pDCTstat->Trfc[2] & 0x7) << 16;			/* Trfc2 */
+		dword |= (pDCTstat->Trfc[1] & 0x7) << 8;			/* Trfc1 */
+		dword |= (pDCTstat->Trfc[0] & 0x7);				/* Trfc0 */
+		Set_NB32_DCT(dev, dct, 0x208, dword);				/* DRAM Timing 2 */
+
+		dword = Get_NB32_DCT(dev, dct, 0x20c);				/* DRAM Timing 3 */
+		dword &= ~(0x00000f00);
+		dword |= ((pDCTstat->Twtr + 0x4) & 0xf) << 8;			/* Twtr */
+		dword &= ~(0x0000001f);
+		dword |= (Tcwl & 0x1f);						/* Tcwl */
+		Set_NB32_DCT(dev, dct, 0x20c, dword);				/* DRAM Timing 3 */
+
+		dword = Get_NB32_DCT(dev, dct, 0x22c);				/* DRAM Timing 10 */
+		dword &= ~(0x0000001f);
+		dword |= ((pDCTstat->Twr + 0x4) & 0x1f);			/* Twr */
+		Set_NB32_DCT(dev, dct, 0x22c, dword);				/* DRAM Timing 10 */
+
+		if (pDCTstat->Speed > mhz_to_memclk_config(mctGet_NVbits(NV_MIN_MEMCLK))) {
+			/* Enable phy-assisted training mode */
+			fam15EnableTrainingMode(pMCTstat, pDCTstat, dct, 1);
+		}
 
-	val = pDCTstat->Trp - Bias_TrpT;
-	val = mct_AdjustSPDTimings(pMCTstat, pDCTstat, val);
-	DramTimingLo |= val<<7;
+		/* Other setup (not training specific) */
+		dword = Get_NB32_DCT(dev, dct, 0x90);				/* DRAM Configuration Low */
+		dword &= ~(0x1 << 23);						/* ForceAutoPchg = 0 */
+		dword &= ~(0x1 << 20);						/* DynPageCloseEn = 0 */
+		Set_NB32_DCT(dev, dct, 0x90, dword);				/* DRAM Configuration Low */
 
-	val = pDCTstat->Trtp - Bias_TrtpT;
-	DramTimingLo |= val<<10;
+		Set_NB32_DCT(dev, dct, 0x228, 0x14141414);			/* DRAM Timing 9 */
+	} else {
+		DramTimingLo = 0;	/* Dram Timing Low init */
+		val = pDCTstat->CASL - 4; /* pDCTstat.CASL to reg. definition */
+		DramTimingLo |= val;
 
-	val = pDCTstat->Tras - Bias_TrasT;
-	DramTimingLo |= val<<12;
+		val = pDCTstat->Trcd - Bias_TrcdT;
+		DramTimingLo |= val<<4;
 
-	val = pDCTstat->Trc - Bias_TrcT;
-	DramTimingLo |= val<<16;
+		val = pDCTstat->Trp - Bias_TrpT;
+		val = mct_AdjustSPDTimings(pMCTstat, pDCTstat, val);
+		DramTimingLo |= val<<7;
 
-	val = pDCTstat->Trrd - Bias_TrrdT;
-	DramTimingLo |= val<<22;
+		val = pDCTstat->Trtp - Bias_TrtpT;
+		DramTimingLo |= val<<10;
 
-	DramTimingHi = 0;	/* Dram Timing High init */
-	val = pDCTstat->Twtr - Bias_TwtrT;
-	DramTimingHi |= val<<8;
+		val = pDCTstat->Tras - Bias_TrasT;
+		DramTimingLo |= val<<12;
 
-	val = 2;
-	DramTimingHi |= val<<16;
+		val = pDCTstat->Trc - Bias_TrcT;
+		DramTimingLo |= val<<16;
 
-	val = 0;
-	for (i=4;i>0;i--) {
-		val <<= 3;
-		val |= Trfc[i-1];
-	}
-	DramTimingHi |= val << 20;
+		val = pDCTstat->Trrd - Bias_TrrdT;
+		DramTimingLo |= val<<22;
 
-	dev = pDCTstat->dev_dct;
-	reg_off = 0x100 * dct;
-	/* Twr */
-	val = pDCTstat->Twr;
-	if (val == 10)
-		val = 9;
-	else if (val == 12)
-		val = 10;
-	val = mct_AdjustSPDTimings(pMCTstat, pDCTstat, val);
-	val -= Bias_TwrT;
-	val <<= 4;
-	dword = Get_NB32(dev, 0x84 + reg_off);
-	dword &= ~0x70;
-	dword |= val;
-	Set_NB32(dev, 0x84 + reg_off, dword);
+		DramTimingHi = 0;	/* Dram Timing High init */
+		val = pDCTstat->Twtr - Bias_TwtrT;
+		DramTimingHi |= val<<8;
 
-	/* Tfaw */
-	val = pDCTstat->Tfaw;
-	val = mct_AdjustSPDTimings(pMCTstat, pDCTstat, val);
-	val -= Bias_TfawT;
-	val >>= 1;
-	val <<= 28;
-	dword = Get_NB32(dev, 0x94 + reg_off);
-	dword &= ~0xf0000000;
-	dword |= val;
-	Set_NB32(dev, 0x94 + reg_off, dword);
-
-	/* dev = pDCTstat->dev_dct; */
-	/* reg_off = 0x100 * dct; */
-
-	if (pDCTstat->Speed > 4) {
-		val = Get_NB32(dev, 0x88 + reg_off);
-		val &= 0xFF000000;
-		DramTimingLo |= val;
-	}
-	Set_NB32(dev, 0x88 + reg_off, DramTimingLo);	/*DCT Timing Low*/
+		val = 2;		/* Tref = 7.8us */
+		DramTimingHi |= val<<16;
+
+		val = 0;
+		for (i=4;i>0;i--) {
+			val <<= 3;
+			val |= Trfc[i-1];
+		}
+		DramTimingHi |= val << 20;
+
+		dev = pDCTstat->dev_dct;
+		/* Twr */
+		val = pDCTstat->Twr;
+		if (val == 10)
+			val = 9;
+		else if (val == 12)
+			val = 10;
+		val = mct_AdjustSPDTimings(pMCTstat, pDCTstat, val);
+		val -= Bias_TwrT;
+		val <<= 4;
+		dword = Get_NB32_DCT(dev, dct, 0x84);
+		dword &= ~0x70;
+		dword |= val;
+		Set_NB32_DCT(dev, dct, 0x84, dword);
+
+		/* Tfaw */
+		val = pDCTstat->Tfaw;
+		val = mct_AdjustSPDTimings(pMCTstat, pDCTstat, val);
+		val -= Bias_TfawT;
+		val >>= 1;
+		val <<= 28;
+		dword = Get_NB32_DCT(dev, dct, 0x94);
+		dword &= ~0xf0000000;
+		dword |= val;
+		Set_NB32_DCT(dev, dct, 0x94, dword);
+
+		/* dev = pDCTstat->dev_dct; */
+
+		if (pDCTstat->Speed > mhz_to_memclk_config(mctGet_NVbits(NV_MIN_MEMCLK))) {
+			val = Get_NB32_DCT(dev, dct, 0x88);
+			val &= 0xFF000000;
+			DramTimingLo |= val;
+		}
+		Set_NB32_DCT(dev, dct, 0x88, DramTimingLo);	/*DCT Timing Low*/
 
-	if (pDCTstat->Speed > 4) {
-		DramTimingHi |= 1 << DisAutoRefresh;
+		if (pDCTstat->Speed > mhz_to_memclk_config(mctGet_NVbits(NV_MIN_MEMCLK))) {
+			DramTimingHi |= 1 << DisAutoRefresh;
+		}
+		DramTimingHi |= 0x000018FF;
+		Set_NB32_DCT(dev, dct, 0x8c, DramTimingHi);	/*DCT Timing Hi*/
 	}
-	DramTimingHi |= 0x000018FF;
-	Set_NB32(dev, 0x8c + reg_off, DramTimingHi);	/*DCT Timing Hi*/
 
 	/* dump_pci_device(PCI_DEV(0, 0x18+pDCTstat->Node_ID, 2)); */
+
+	printk(BIOS_DEBUG, "%s: Done\n", __func__);
 }
 
 static u8 AutoCycTiming_D(struct MCTStatStruc *pMCTstat,
@@ -1303,6 +2840,8 @@ static u8 AutoCycTiming_D(struct MCTStatStruc *pMCTstat,
 	 * timing mode is 'Auto'.
 	 */
 
+	printk(BIOS_DEBUG, "%s: Start\n", __func__);
+
 	/* Get primary timing (CAS Latency and Cycle Time) */
 	if (pDCTstat->Speed == 0) {
 		mctGet_MaxLoadFreq(pDCTstat);
@@ -1312,6 +2851,7 @@ static u8 AutoCycTiming_D(struct MCTStatStruc *pMCTstat,
 
 		/* Go get best T and CL as specified by DIMM mfgs. and OEM */
 		SPDGetTCL_D(pMCTstat, pDCTstat, dct);
+
 		/* skip callback mctForce800to1067_D */
 		pDCTstat->Speed = pDCTstat->DIMMAutoSpeed;
 		pDCTstat->CASL = pDCTstat->DIMMCASL;
@@ -1344,7 +2884,10 @@ static void GetPresetmaxF_D(struct MCTStatStruc *pMCTstat,
 	u16 word;
 
 	/* Get CPU Si Revision defined limit (NPT) */
-	proposedFreq = 800;	 /* Rev F0 programmable max memclock is */
+	if (is_fam15h())
+		proposedFreq = 933;
+	else
+		proposedFreq = 800;	 /* Rev F0 programmable max memclock is */
 
 	/*Get User defined limit if  "limit" mode */
 	if ( mctGet_NVbits(NV_MCTUSRTMGMODE) == 1) {
@@ -1381,6 +2924,7 @@ static void SPDGetTCL_D(struct MCTStatStruc *pMCTstat,
 	u16 tCKmin16x;
 	u16 tCKproposed16x;
 	u8 CLactual, CLdesired, CLT_Fail;
+	uint16_t min_frequency_tck16x;
 
 	u8 smbaddr, byte = 0, bytex = 0;
 
@@ -1390,6 +2934,17 @@ static void SPDGetTCL_D(struct MCTStatStruc *pMCTstat,
 	tCKmin16x = 0;
 	CLT_Fail = 0;
 
+	printk(BIOS_DEBUG, "%s: Start\n", __func__);
+
+	if (is_fam15h()) {
+		uint16_t minimum_frequency_mhz = mctGet_NVbits(NV_MIN_MEMCLK);
+		if (minimum_frequency_mhz == 0)
+			minimum_frequency_mhz = 333;
+		min_frequency_tck16x = 16000 / minimum_frequency_mhz;
+	} else {
+		min_frequency_tck16x = 40;
+	}
+
 	for (i = 0; i < MAX_DIMMS_SUPPORTED; i++) {
 		if (pDCTstat->DIMMValid & (1 << i)) {
 			smbaddr = Get_DIMMAddress_D(pDCTstat, (dct + i));
@@ -1419,27 +2974,44 @@ static void SPDGetTCL_D(struct MCTStatStruc *pMCTstat,
 				tCKmin16x = byte * MTB16x;
 		}
 	}
-	/* calculate tCKproposed16x */
+	/* calculate tCKproposed16x (proposed clock period in ns * 16) */
 	tCKproposed16x =  16000 / pDCTstat->PresetmaxFreq;
 	if (tCKmin16x > tCKproposed16x)
 		tCKproposed16x = tCKmin16x;
 
-	/* mctHookTwo1333DimmOverride(); */
-	/* For UDIMM, if there are two DDR3-1333 on the same channel,
-	   downgrade DDR speed to 1066. */
-
 	/* TODO: get user manual tCK16x(Freq.) and overwrite current tCKproposed16x if manual. */
-	if (tCKproposed16x == 20)
-		pDCTstat->TargetFreq = 7;
-	else if (tCKproposed16x <= 24) {
-		pDCTstat->TargetFreq = 6;
-		tCKproposed16x = 24;
-	} else if (tCKproposed16x <= 30) {
-		pDCTstat->TargetFreq = 5;
-		tCKproposed16x = 30;
+	if (is_fam15h()) {
+		if (tCKproposed16x == 17)
+			pDCTstat->TargetFreq = 0x16;
+		else if (tCKproposed16x <= 20) {
+			pDCTstat->TargetFreq = 0x12;
+			tCKproposed16x = 20;
+		} else if (tCKproposed16x <= 24) {
+			pDCTstat->TargetFreq = 0xe;
+			tCKproposed16x = 24;
+		} else if (tCKproposed16x <= 30) {
+			pDCTstat->TargetFreq = 0xa;
+			tCKproposed16x = 30;
+		} else if (tCKproposed16x <= 40) {
+			pDCTstat->TargetFreq = 0x6;
+			tCKproposed16x = 40;
+		} else {
+			pDCTstat->TargetFreq = 0x4;
+			tCKproposed16x = 48;
+		}
 	} else {
-		pDCTstat->TargetFreq = 4;
-		tCKproposed16x = 40;
+		if (tCKproposed16x == 20)
+			pDCTstat->TargetFreq = 7;
+		else if (tCKproposed16x <= 24) {
+			pDCTstat->TargetFreq = 6;
+			tCKproposed16x = 24;
+		} else if (tCKproposed16x <= 30) {
+			pDCTstat->TargetFreq = 5;
+			tCKproposed16x = 30;
+		} else {
+			pDCTstat->TargetFreq = 4;
+			tCKproposed16x = 40;
+		}
 	}
 	/* Running through this loop twice:
 	   - First time find tCL at target frequency
@@ -1478,27 +3050,42 @@ static void SPDGetTCL_D(struct MCTStatStruc *pMCTstat,
 		/* get CL and T */
 		if (!CLT_Fail) {
 			bytex = CLactual;
-			if (tCKproposed16x == 20)
-				byte = 7;
-			else if (tCKproposed16x == 24)
-				byte = 6;
-			else if (tCKproposed16x == 30)
-				byte = 5;
-			else
-				byte = 4;
+			if (is_fam15h()) {
+				if (tCKproposed16x == 17)
+					byte = 0x16;
+				else if (tCKproposed16x == 20)
+					byte = 0x12;
+				else if (tCKproposed16x == 24)
+					byte = 0xe;
+				else if (tCKproposed16x == 30)
+					byte = 0xa;
+				else if (tCKproposed16x == 40)
+					byte = 0x6;
+				else
+					byte = 0x4;
+			} else {
+				if (tCKproposed16x == 20)
+					byte = 7;
+				else if (tCKproposed16x == 24)
+					byte = 6;
+				else if (tCKproposed16x == 30)
+					byte = 5;
+				else
+					byte = 4;
+			}
 		} else {
 			/* mctHookManualCLOverride */
 			/* TODO: */
 		}
 
-		if (tCKproposed16x != 40) {
+		if (tCKproposed16x != min_frequency_tck16x) {
 			if (pMCTstat->GStatus & (1 << GSB_EnDIMMSpareNW)) {
 				pDCTstat->DIMMAutoSpeed = byte;
 				pDCTstat->DIMMCASL = bytex;
 				break;
 			} else {
 				pDCTstat->TargetCASL = bytex;
-				tCKproposed16x = 40;
+				tCKproposed16x = min_frequency_tck16x;
 			}
 		} else {
 			pDCTstat->DIMMAutoSpeed = byte;
@@ -1519,29 +3106,21 @@ static void SPDGetTCL_D(struct MCTStatStruc *pMCTstat,
 static u8 PlatformSpec_D(struct MCTStatStruc *pMCTstat,
 				struct DCTStatStruc *pDCTstat, u8 dct)
 {
-	u32 dev;
-	u32 reg;
-	u32 val;
+	if (!is_fam15h()) {
+		mctGet_PS_Cfg_D(pMCTstat, pDCTstat, dct);
 
-	mctGet_PS_Cfg_D(pMCTstat, pDCTstat, dct);
+		if (pDCTstat->GangedMode == 1) {
+			mctGet_PS_Cfg_D(pMCTstat, pDCTstat, 1);
+			mct_BeforePlatformSpec(pMCTstat, pDCTstat, 1);
+		}
 
-	if (pDCTstat->GangedMode == 1) {
-		mctGet_PS_Cfg_D(pMCTstat, pDCTstat, 1);
-		mct_BeforePlatformSpec(pMCTstat, pDCTstat, 1);
-	}
+		set_2t_configuration(pMCTstat, pDCTstat, dct);
 
-	if ( pDCTstat->_2Tmode == 2) {
-		dev = pDCTstat->dev_dct;
-		reg = 0x94 + 0x100 * dct; /* Dram Configuration Hi */
-		val = Get_NB32(dev, reg);
-		val |= 1 << 20;		       /* 2T CMD mode */
-		Set_NB32(dev, reg, val);
+		mct_BeforePlatformSpec(pMCTstat, pDCTstat, dct);
+		mct_PlatformSpec(pMCTstat, pDCTstat, dct);
+		if (pDCTstat->DIMMAutoSpeed == mhz_to_memclk_config(mctGet_NVbits(NV_MIN_MEMCLK)))
+			InitPhyCompensation(pMCTstat, pDCTstat, dct);
 	}
-
-	mct_BeforePlatformSpec(pMCTstat, pDCTstat, dct);
-	mct_PlatformSpec(pMCTstat, pDCTstat, dct);
-	if (pDCTstat->DIMMAutoSpeed == 4)
-		InitPhyCompensation(pMCTstat, pDCTstat, dct);
 	mctHookAfterPSCfg();
 
 	return pDCTstat->ErrCode;
@@ -1553,11 +3132,11 @@ static u8 AutoConfig_D(struct MCTStatStruc *pMCTstat,
 	u32 DramControl, DramTimingLo, Status;
 	u32 DramConfigLo, DramConfigHi, DramConfigMisc, DramConfigMisc2;
 	u32 val;
-	u32 reg_off;
 	u32 dev;
 	u16 word;
 	u32 dword;
 	u8 byte;
+	uint32_t offset;
 
 	DramConfigLo = 0;
 	DramConfigHi = 0;
@@ -1577,12 +3156,10 @@ static u8 AutoConfig_D(struct MCTStatStruc *pMCTstat,
 	Status = pDCTstat->Status;
 
 	dev = pDCTstat->dev_dct;
-	reg_off = 0x100 * dct;
-
 
 	/* Build Dram Control Register Value */
-	DramConfigMisc2 = Get_NB32 (dev, 0xA8 + reg_off);	/* Dram Control*/
-	DramControl = Get_NB32 (dev, 0x78 + reg_off);		/* Dram Control*/
+	DramConfigMisc2 = Get_NB32_DCT(dev, dct, 0xA8);		/* Dram Control*/
+	DramControl = Get_NB32_DCT(dev, dct, 0x78);		/* Dram Control*/
 
 	/* FIXME: Skip mct_checkForDxSupport */
 	/* REV_CALL mct_DoRdPtrInit if not Dx */
@@ -1624,8 +3201,12 @@ static u8 AutoConfig_D(struct MCTStatStruc *pMCTstat,
 	DramConfigLo = mct_DisDllShutdownSR(pMCTstat, pDCTstat, DramConfigLo, dct);
 
 	/* Build Dram Config Hi Register Value */
+	if (is_fam15h())
+		offset = 0x0;
+	else
+		offset = 0x1;
 	dword = pDCTstat->Speed;
-	DramConfigHi |= dword - 1;	/* get MemClk encoding */
+	DramConfigHi |= dword - offset;	/* get MemClk encoding */
 	DramConfigHi |= 1 << MemClkFreqVal;
 
 	if (Status & (1 << SB_Registered))
@@ -1658,7 +3239,7 @@ static u8 AutoConfig_D(struct MCTStatStruc *pMCTstat,
 		val = 0x0f; /* recommended setting (default) */
 	DramConfigHi |= val << 24;
 
-	if (pDCTstat->LogicalCPUID & (AMD_DR_Dx | AMD_DR_Cx | AMD_DR_Bx))
+	if (pDCTstat->LogicalCPUID & (AMD_DR_Dx | AMD_DR_Cx | AMD_DR_Bx | AMD_FAM15_ALL))
 		DramConfigHi |= 1 << DcqArbBypassEn;
 
 	/* Build MemClkDis Value from Dram Timing Lo and
@@ -1669,7 +3250,7 @@ static u8 AutoConfig_D(struct MCTStatStruc *pMCTstat,
 	    NV_AllMemClks <>0 AND SB_DiagClks ==0 */
 
 	/* Dram Timing Low (owns Clock Enable bits) */
-	DramTimingLo = Get_NB32(dev, 0x88 + reg_off);
+	DramTimingLo = Get_NB32_DCT(dev, dct, 0x88);
 	if (mctGet_NVbits(NV_AllMemClks) == 0) {
 		/* Special Jedec SPD diagnostic bit - "enable all clocks" */
 		if (!(pDCTstat->Status & (1<<SB_DiagClks))) {
@@ -1700,28 +3281,34 @@ static u8 AutoConfig_D(struct MCTStatStruc *pMCTstat,
 				}
 				dword++ ;
 			}
+			DramTimingLo &= ~(0xff << 24);
 			DramTimingLo |= byte << 24;
 		}
 	}
 
-	printk(BIOS_DEBUG, "AutoConfig_D: DramControl: %x\n", DramControl);
-	printk(BIOS_DEBUG, "AutoConfig_D: DramTimingLo: %x\n", DramTimingLo);
-	printk(BIOS_DEBUG, "AutoConfig_D: DramConfigMisc: %x\n", DramConfigMisc);
-	printk(BIOS_DEBUG, "AutoConfig_D: DramConfigMisc2: %x\n", DramConfigMisc2);
-	printk(BIOS_DEBUG, "AutoConfig_D: DramConfigLo: %x\n", DramConfigLo);
-	printk(BIOS_DEBUG, "AutoConfig_D: DramConfigHi: %x\n", DramConfigHi);
+	printk(BIOS_DEBUG, "AutoConfig_D: DramControl:     %08x\n", DramControl);
+	printk(BIOS_DEBUG, "AutoConfig_D: DramTimingLo:    %08x\n", DramTimingLo);
+	printk(BIOS_DEBUG, "AutoConfig_D: DramConfigMisc:  %08x\n", DramConfigMisc);
+	printk(BIOS_DEBUG, "AutoConfig_D: DramConfigMisc2: %08x\n", DramConfigMisc2);
+	printk(BIOS_DEBUG, "AutoConfig_D: DramConfigLo:    %08x\n", DramConfigLo);
+	printk(BIOS_DEBUG, "AutoConfig_D: DramConfigHi:    %08x\n", DramConfigHi);
 
 	/* Write Values to the registers */
-	Set_NB32(dev, 0x78 + reg_off, DramControl);
-	Set_NB32(dev, 0x88 + reg_off, DramTimingLo);
-	Set_NB32(dev, 0xA0 + reg_off, DramConfigMisc);
+	Set_NB32_DCT(dev, dct, 0x78, DramControl);
+	Set_NB32_DCT(dev, dct, 0x88, DramTimingLo);
+	Set_NB32_DCT(dev, dct, 0xa0, DramConfigMisc);
 	DramConfigMisc2 = mct_SetDramConfigMisc2(pDCTstat, dct, DramConfigMisc2);
-	Set_NB32(dev, 0xA8 + reg_off, DramConfigMisc2);
-	Set_NB32(dev, 0x90 + reg_off, DramConfigLo);
+	Set_NB32_DCT(dev, dct, 0xa8, DramConfigMisc2);
+	Set_NB32_DCT(dev, dct, 0x90, DramConfigLo);
 	ProgDramMRSReg_D(pMCTstat, pDCTstat, dct);
-	dword = Get_NB32(dev, 0x94 + reg_off);
+
+	if (is_fam15h())
+		InitDDRPhy(pMCTstat, pDCTstat, dct);
+
+	/* Write the DRAM Configuration High register, including memory frequency change */
+	dword = Get_NB32_DCT(dev, dct, 0x94);
 	DramConfigHi |= dword;
-	mct_SetDramConfigHi_D(pDCTstat, dct, DramConfigHi);
+	mct_SetDramConfigHi_D(pMCTstat, pDCTstat, dct, DramConfigHi);
 	mct_EarlyArbEn_D(pMCTstat, pDCTstat, dct);
 	mctHookAfterAutoCfg();
 
@@ -1731,6 +3318,7 @@ static u8 AutoConfig_D(struct MCTStatStruc *pMCTstat,
 	printk(BIOS_DEBUG, "AutoConfig: ErrStatus %x\n", pDCTstat->ErrStatus);
 	printk(BIOS_DEBUG, "AutoConfig: ErrCode %x\n", pDCTstat->ErrCode);
 	printk(BIOS_DEBUG, "AutoConfig: Done\n\n");
+
 AutoConfig_exit:
 	return pDCTstat->ErrCode;
 }
@@ -1748,14 +3336,12 @@ static void SPDSetBanks_D(struct MCTStatStruc *pMCTstat,
 	u32 val;
 	u32 reg;
 	u32 dev;
-	u32 reg_off;
 	u8 byte;
 	u16 word;
 	u32 dword;
 	u16 smbaddr;
 
 	dev = pDCTstat->dev_dct;
-	reg_off = 0x100 * dct;
 
 	BankAddrReg = 0;
 	for (ChipSel = 0; ChipSel < MAX_CS_SUPPORTED; ChipSel+=2) {
@@ -1820,10 +3406,10 @@ static void SPDSetBanks_D(struct MCTStatStruc *pMCTstat,
 				/*set ChipSelect population indicator odd bits*/
 				pDCTstat->CSPresent |= 1 << (ChipSel + 1);
 
-			reg = 0x60+(ChipSel<<1) + reg_off;	/*Dram CS Mask Register */
+			reg = 0x60+(ChipSel<<1);	/*Dram CS Mask Register */
 			val = csMask;
 			val &= 0x1FF83FE0;	/* Mask out reserved bits.*/
-			Set_NB32(dev, reg, val);
+			Set_NB32_DCT(dev, dct, reg, val);
 		} else {
 			if (pDCTstat->DIMMSPDCSE & (1<<ChipSel))
 				pDCTstat->CSTestFail |= (1<<ChipSel);
@@ -1847,8 +3433,8 @@ static void SPDSetBanks_D(struct MCTStatStruc *pMCTstat,
 	if (!pDCTstat->CSPresent)
 		pDCTstat->ErrCode = SC_StopError;
 
-	reg = 0x80 + reg_off;		/* Bank Addressing Register */
-	Set_NB32(dev, reg, BankAddrReg);
+	reg = 0x80;		/* Bank Addressing Register */
+	Set_NB32_DCT(dev, dct, reg, BankAddrReg);
 
 	pDCTstat->CSPresent_DCT[dct] = pDCTstat->CSPresent;
 	/* dump_pci_device(PCI_DEV(0, 0x18+pDCTstat->Node_ID, 2)); */
@@ -1933,11 +3519,9 @@ static void StitchMemory_D(struct MCTStatStruc *pMCTstat,
 	u16 word;
 	u32 dev;
 	u32 reg;
-	u32 reg_off;
 	u32 val;
 
 	dev = pDCTstat->dev_dct;
-	reg_off = 0x100 * dct;
 
 	_DSpareEn = 0;
 
@@ -1974,11 +3558,11 @@ static void StitchMemory_D(struct MCTStatStruc *pMCTstat,
 		BiggestBank = 0;
 		for (q = 0; q < MAX_CS_SUPPORTED; q++) { /* from DIMMS to CS */
 			if (pDCTstat->CSPresent & (1 << q)) {  /* bank present? */
-				reg  = 0x40 + (q << 2) + reg_off;  /* Base[q] reg.*/
-				val = Get_NB32(dev, reg);
+				reg  = 0x40 + (q << 2);  /* Base[q] reg.*/
+				val = Get_NB32_DCT(dev, dct, reg);
 				if (!(val & 3)) {	/* (CSEnable|Spare==1)bank is enabled already? */
-					reg = 0x60 + (q << 1) + reg_off; /*Mask[q] reg.*/
-					val = Get_NB32(dev, reg);
+					reg = 0x60 + (q << 1); /*Mask[q] reg.*/
+					val = Get_NB32_DCT(dev, dct, reg);
 					val >>= 19;
 					val++;
 					val <<= 19;
@@ -1994,7 +3578,7 @@ static void StitchMemory_D(struct MCTStatStruc *pMCTstat,
 		if (BiggestBank !=0) {
 			curcsBase = nxtcsBase;		/* curcsBase=nxtcsBase*/
 			/* DRAM CS Base b Address Register offset */
-			reg = 0x40 + (b << 2) + reg_off;
+			reg = 0x40 + (b << 2);
 			if (_DSpareEn) {
 				BiggestBank = 0;
 				val = 1 << Spare;	/* Spare Enable*/
@@ -2013,7 +3597,7 @@ static void StitchMemory_D(struct MCTStatStruc *pMCTstat,
 					}
 				}
 			}
-			Set_NB32(dev, reg, val);
+			Set_NB32_DCT(dev, dct, reg, val);
 			if (_DSpareEn)
 				_DSpareEn = 0;
 			else
@@ -2024,9 +3608,9 @@ static void StitchMemory_D(struct MCTStatStruc *pMCTstat,
 		/* bank present but disabled?*/
 		if ( pDCTstat->CSTestFail & (1 << p)) {
 			/* DRAM CS Base b Address Register offset */
-			reg = (p << 2) + 0x40 + reg_off;
+			reg = (p << 2) + 0x40;
 			val = 1 << TestFail;
-			Set_NB32(dev, reg, val);
+			Set_NB32_DCT(dev, dct, reg, val);
 		}
 	}
 
@@ -2064,7 +3648,7 @@ static u8 DIMMPresence_D(struct MCTStatStruc *pMCTstat,
 	u16 i, j, k;
 	u8 smbaddr;
 	u8 SPDCtrl;
-	u16 RegDIMMPresent, MaxDimms;
+	u16 RegDIMMPresent, LRDIMMPresent, MaxDimms;
 	u8 devwidth;
 	u16 DimmSlots;
 	u8 byte = 0, bytex;
@@ -2077,6 +3661,7 @@ static u8 DIMMPresence_D(struct MCTStatStruc *pMCTstat,
 	SPDCtrl = mctGet_NVbits(NV_SPDCHK_RESTRT);
 
 	RegDIMMPresent = 0;
+	LRDIMMPresent = 0;
 	pDCTstat->DimmQRPresent = 0;
 
 	for (i = 0; i < MAX_DIMMS_SUPPORTED; i++) {
@@ -2115,6 +3700,7 @@ static u8 DIMMPresence_D(struct MCTStatStruc *pMCTstat,
 						pDCTstat->DimmManufacturerID[i] |= ((uint64_t)mctRead_SPD(smbaddr, SPD_MANID_START + k)) << (k * 8);
 					for (k = 0; k < SPD_PARTN_LENGTH; k++)
 						pDCTstat->DimmPartNumber[i][k] = mctRead_SPD(smbaddr, SPD_PARTN_START + k);
+					pDCTstat->DimmPartNumber[i][SPD_PARTN_LENGTH] = 0;
 					pDCTstat->DimmRevisionNumber[i] = 0;
 					for (k = 0; k < 2; k++)
 						pDCTstat->DimmRevisionNumber[i] |= ((uint16_t)mctRead_SPD(smbaddr, SPD_REVNO_START + k)) << (k * 8);
@@ -2138,6 +3724,12 @@ static u8 DIMMPresence_D(struct MCTStatStruc *pMCTstat,
 				} else {
 					pDCTstat->DimmRegistered[i] = 0;
 				}
+				if (byte == JED_LRDIMM) {
+					LRDIMMPresent |= 1 << i;
+					pDCTstat->DimmLoadReduced[i] = 1;
+				} else {
+					pDCTstat->DimmLoadReduced[i] = 0;
+				}
 				/* Check ECC capable */
 				byte = mctRead_SPD(smbaddr, SPD_BusWidth);
 				if (byte & JED_ECC) {
@@ -2221,6 +3813,7 @@ static u8 DIMMPresence_D(struct MCTStatStruc *pMCTstat,
 	printk(BIOS_DEBUG, "\t DIMMPresence: DIMMValid=%x\n", pDCTstat->DIMMValid);
 	printk(BIOS_DEBUG, "\t DIMMPresence: DIMMPresent=%x\n", pDCTstat->DIMMPresent);
 	printk(BIOS_DEBUG, "\t DIMMPresence: RegDIMMPresent=%x\n", RegDIMMPresent);
+	printk(BIOS_DEBUG, "\t DIMMPresence: LRDIMMPresent=%x\n", LRDIMMPresent);
 	printk(BIOS_DEBUG, "\t DIMMPresence: DimmECCPresent=%x\n", pDCTstat->DimmECCPresent);
 	printk(BIOS_DEBUG, "\t DIMMPresence: DimmPARPresent=%x\n", pDCTstat->DimmPARPresent);
 	printk(BIOS_DEBUG, "\t DIMMPresence: Dimmx4Present=%x\n", pDCTstat->Dimmx4Present);
@@ -2247,6 +3840,16 @@ static u8 DIMMPresence_D(struct MCTStatStruc *pMCTstat,
 				pDCTstat->Status |= 1<<SB_Registered;
 			}
 		}
+		if (LRDIMMPresent != 0) {
+			if ((LRDIMMPresent ^ pDCTstat->DIMMValid) !=0) {
+				/* module type DIMM mismatch (reg'ed, unbuffered) */
+				pDCTstat->ErrStatus |= 1<<SB_DimmMismatchM;
+				pDCTstat->ErrCode = SC_StopError;
+			} else{
+				/* all DIMMs are registered */
+				pDCTstat->Status |= 1<<SB_LoadReduced;
+			}
+		}
 		if (pDCTstat->DimmECCPresent != 0) {
 			if ((pDCTstat->DimmECCPresent ^ pDCTstat->DIMMValid )== 0) {
 				/* all DIMMs are ECC capable */
@@ -2284,6 +3887,26 @@ static u8 Get_DIMMAddress_D(struct DCTStatStruc *pDCTstat, u8 i)
 	return p[i];
 }
 
+static void mct_preInitDCT(struct MCTStatStruc *pMCTstat,
+				struct DCTStatStruc *pDCTstat)
+{
+	u8 err_code;
+
+	/* Preconfigure DCT0 */
+	DCTPreInit_D(pMCTstat, pDCTstat, 0);
+
+	/* Configure DCT1 if unganged and enabled*/
+	if (!pDCTstat->GangedMode) {
+		if (pDCTstat->DIMMValidDCT[1] > 0) {
+			err_code = pDCTstat->ErrCode;		/* save DCT0 errors */
+			pDCTstat->ErrCode = 0;
+			DCTPreInit_D(pMCTstat, pDCTstat, 1);
+			if (pDCTstat->ErrCode == 2)		/* DCT1 is not Running */
+				pDCTstat->ErrCode = err_code;	/* Using DCT0 Error code to update pDCTstat.ErrCode */
+		}
+	}
+}
+
 static void mct_initDCT(struct MCTStatStruc *pMCTstat,
 				struct DCTStatStruc *pDCTstat)
 {
@@ -2295,7 +3918,7 @@ static void mct_initDCT(struct MCTStatStruc *pMCTstat,
 	if (pDCTstat->ErrCode == SC_FatalErr) {
 		/* Do nothing goto exitDCTInit; any fatal errors? */
 	} else {
-		/* Configure DCT1 if unganged and enabled*/
+		/* Configure DCT1 if unganged and enabled */
 		if (!pDCTstat->GangedMode) {
 			if (pDCTstat->DIMMValidDCT[1] > 0) {
 				err_code = pDCTstat->ErrCode;		/* save DCT0 errors */
@@ -2305,17 +3928,21 @@ static void mct_initDCT(struct MCTStatStruc *pMCTstat,
 					pDCTstat->ErrCode = err_code;	/* Using DCT0 Error code to update pDCTstat.ErrCode */
 			} else {
 				val = 1 << DisDramInterface;
-				Set_NB32(pDCTstat->dev_dct, 0x100 + 0x94, val);
+				Set_NB32_DCT(pDCTstat->dev_dct, 1, 0x94, val);
+
+				/* To maximize power savings when DisDramInterface=1b,
+				 * all of the MemClkDis bits should also be set.
+				 */
+				Set_NB32_DCT(pDCTstat->dev_dct, 1, 0x88, 0xff000000);
 			}
 		}
 	}
-/* exitDCTInit: */
 }
 
 static void mct_DramInit(struct MCTStatStruc *pMCTstat,
 				struct DCTStatStruc *pDCTstat, u8 dct)
 {
-	mct_BeforeDramInit_Prod_D(pMCTstat, pDCTstat);
+	mct_BeforeDramInit_Prod_D(pMCTstat, pDCTstat, dct);
 	mct_DramInit_Sw_D(pMCTstat, pDCTstat, dct);
 	/* mct_DramInit_Hw_D(pMCTstat, pDCTstat, dct); */
 }
@@ -2343,7 +3970,8 @@ static u8 mct_setMode(struct MCTStatStruc *pMCTstat,
 		if (byte)
 			pDCTstat->ErrStatus |= (1 << SB_DimmMismatchO); /* Set temp. to avoid setting of ganged mode */
 
-		if (!(pDCTstat->ErrStatus & (1 << SB_DimmMismatchO))) {
+		if ((!(pDCTstat->ErrStatus & (1 << SB_DimmMismatchO))) && (pDCTstat->LogicalCPUID & AMD_FAM10_ALL)) {
+			/* Ganged channel mode not supported on Family 15h or higher */
 			pDCTstat->GangedMode = 1;
 			/* valid 128-bit mode population. */
 			pDCTstat->Status |= 1 << SB_128bitmode;
@@ -2387,10 +4015,8 @@ void Set_NB32_index(u32 dev, u32 index_reg, u32 index, u32 data)
 
 u32 Get_NB32_index_wait(u32 dev, u32 index_reg, u32 index)
 {
-
 	u32 dword;
 
-
 	index &= ~(1 << DctAccessWrite);
 	Set_NB32(dev, index_reg, index);
 	do {
@@ -2405,7 +4031,6 @@ void Set_NB32_index_wait(u32 dev, u32 index_reg, u32 index, u32 data)
 {
 	u32 dword;
 
-
 	Set_NB32(dev, index_reg + 0x4, data);
 	index |= (1 << DctAccessWrite);
 	Set_NB32(dev, index_reg, index);
@@ -2420,16 +4045,17 @@ static u8 mct_BeforePlatformSpec(struct MCTStatStruc *pMCTstat,
 {
 	/* mct_checkForCxDxSupport_D */
 	if (pDCTstat->LogicalCPUID & AMD_DR_GT_Bx) {
+		/* Family 10h Errata 322: Address and Command Fine Delay Values May Be Incorrect */
 		/* 1. Write 00000000h to F2x[1,0]9C_xD08E000 */
-		Set_NB32_index_wait(pDCTstat->dev_dct, 0x98 + dct * 0x100, 0x0D08E000, 0);
+		Set_NB32_index_wait_DCT(pDCTstat->dev_dct, dct, 0x98, 0x0D08E000, 0);
 		/* 2. If DRAM Configuration Register[MemClkFreq] (F2x[1,0]94[2:0]) is
 		   greater than or equal to 011b (DDR-800 and higher),
 		   then write 00000080h to F2x[1,0]9C_xD02E001,
 		   else write 00000090h to F2x[1,0]9C_xD02E001. */
-		if (pDCTstat->Speed >= 4)
-			Set_NB32_index_wait(pDCTstat->dev_dct, 0x98 + dct * 0x100, 0xD02E001, 0x80);
+		if (pDCTstat->Speed >= mhz_to_memclk_config(mctGet_NVbits(NV_MIN_MEMCLK)))
+			Set_NB32_index_wait_DCT(pDCTstat->dev_dct, dct, 0x98, 0x0D02E001, 0x80);
 		else
-			Set_NB32_index_wait(pDCTstat->dev_dct, 0x98 + dct * 0x100, 0xD02E001, 0x90);
+			Set_NB32_index_wait_DCT(pDCTstat->dev_dct, dct, 0x98, 0x0D02E001, 0x90);
 	}
 	return pDCTstat->ErrCode;
 }
@@ -2455,9 +4081,9 @@ static u8 mct_PlatformSpec(struct MCTStatStruc *pMCTstat,
 		i_end = dct + 1;
 	}
 	for (i=i_start; i<i_end; i++) {
-		index_reg = 0x98 + (i * 0x100);
-		Set_NB32_index_wait(dev, index_reg, 0x00, pDCTstat->CH_ODC_CTL[i]); /* Channel A Output Driver Compensation Control */
-		Set_NB32_index_wait(dev, index_reg, 0x04, pDCTstat->CH_ADDR_TMG[i]); /* Channel A Output Driver Compensation Control */
+		index_reg = 0x98;
+		Set_NB32_index_wait_DCT(dev, i, index_reg, 0x00, pDCTstat->CH_ODC_CTL[i]); /* Channel A Output Driver Compensation Control */
+		Set_NB32_index_wait_DCT(dev, i, index_reg, 0x04, pDCTstat->CH_ADDR_TMG[i]); /* Channel A Output Driver Compensation Control */
 	}
 
 	return pDCTstat->ErrCode;
@@ -2511,14 +4137,14 @@ static u8 mct_SPDCalcWidth(struct MCTStatStruc *pMCTstat,
 	}
 
 	if (pDCTstat->DIMMValidDCT[0] == 0) {
-		val = Get_NB32(pDCTstat->dev_dct, 0x94);
+		val = Get_NB32_DCT(pDCTstat->dev_dct, 0, 0x94);
 		val |= 1 << DisDramInterface;
-		Set_NB32(pDCTstat->dev_dct, 0x94, val);
+		Set_NB32_DCT(pDCTstat->dev_dct, 0, 0x94, val);
 	}
 	if (pDCTstat->DIMMValidDCT[1] == 0) {
-		val = Get_NB32(pDCTstat->dev_dct, 0x94 + 0x100);
+		val = Get_NB32_DCT(pDCTstat->dev_dct, 1, 0x94);
 		val |= 1 << DisDramInterface;
-		Set_NB32(pDCTstat->dev_dct, 0x94 + 0x100, val);
+		Set_NB32_DCT(pDCTstat->dev_dct, 1, 0x94, val);
 	}
 
 	printk(BIOS_DEBUG, "SPDCalcWidth: Status %x\n", pDCTstat->Status);
@@ -2648,21 +4274,20 @@ static void Set_OtherTiming(struct MCTStatStruc *pMCTstat,
 				struct DCTStatStruc *pDCTstat, u8 dct)
 {
 	u32 reg;
-	u32 reg_off = 0x100 * dct;
 	u32 val;
 	u32 dword;
 	u32 dev = pDCTstat->dev_dct;
 
-	Get_DqsRcvEnGross_Diff(pDCTstat, dev, 0x98 + reg_off);
-	Get_WrDatGross_Diff(pDCTstat, dct, dev, 0x98 + reg_off);
+	Get_DqsRcvEnGross_Diff(pDCTstat, dev, dct, 0x98);
+	Get_WrDatGross_Diff(pDCTstat, dct, dev, 0x98);
 	Get_Trdrd(pMCTstat, pDCTstat, dct);
 	Get_Twrwr(pMCTstat, pDCTstat, dct);
 	Get_Twrrd(pMCTstat, pDCTstat, dct);
 	Get_TrwtTO(pMCTstat, pDCTstat, dct);
 	Get_TrwtWB(pMCTstat, pDCTstat);
 
-	reg = 0x8C + reg_off;		/* Dram Timing Hi */
-	val = Get_NB32(dev, reg);
+	reg = 0x8C;		/* Dram Timing Hi */
+	val = Get_NB32_DCT(dev, dct, reg);
 	val &= 0xffff0300;
 	dword = pDCTstat->TrwtTO;
 	val |= dword << 4;
@@ -2674,10 +4299,10 @@ static void Set_OtherTiming(struct MCTStatStruc *pMCTstat,
 	val |= dword << 14;
 	dword = pDCTstat->TrwtWB;
 	val |= dword;
-	Set_NB32(dev, reg, val);
+	Set_NB32_DCT(dev, dct, reg, val);
 
-	reg = 0x78 + reg_off;
-	val = Get_NB32(dev, reg);
+	reg = 0x78;
+	val = Get_NB32_DCT(dev, dct, reg);
 	val &= 0xFFFFC0FF;
 	dword = pDCTstat->Twrrd >> 2;
 	val |= dword << 8;
@@ -2685,7 +4310,7 @@ static void Set_OtherTiming(struct MCTStatStruc *pMCTstat,
 	val |= dword << 10;
 	dword = pDCTstat->Trdrd >> 2;
 	val |= dword << 12;
-	Set_NB32(dev, reg, val);
+	Set_NB32_DCT(dev, dct, reg, val);
 }
 
 static void Get_Trdrd(struct MCTStatStruc *pMCTstat,
@@ -2755,18 +4380,17 @@ static void Get_TrwtWB(struct MCTStatStruc *pMCTstat,
 static u8 Get_Latency_Diff(struct MCTStatStruc *pMCTstat,
 			   struct DCTStatStruc *pDCTstat, u8 dct)
 {
-	u32 reg_off =  0x100 * dct;
 	u32 dev = pDCTstat->dev_dct;
 	u32 val1, val2;
 
-	val1 = Get_NB32(dev, reg_off + 0x88) & 0xF;
-	val2 = (Get_NB32(dev, reg_off + 0x84) >> 20) & 7;
+	val1 = Get_NB32_DCT(dev, dct, 0x88) & 0xF;
+	val2 = (Get_NB32_DCT(dev, dct, 0x84) >> 20) & 7;
 
 	return val1 - val2;
 }
 
 static void Get_DqsRcvEnGross_Diff(struct DCTStatStruc *pDCTstat,
-					u32 dev, u32 index_reg)
+					u32 dev, uint8_t dct, u32 index_reg)
 {
 	u8 Smallest, Largest;
 	u32 val;
@@ -2776,12 +4400,12 @@ static void Get_DqsRcvEnGross_Diff(struct DCTStatStruc *pDCTstat,
 	   DqsRcvEnGrossDelay of any other DIMM is equal to the Critical
 	   Gross Delay Difference (CGDD) */
 	/* DqsRcvEn byte 1,0 */
-	val = Get_DqsRcvEnGross_MaxMin(pDCTstat, dev, index_reg, 0x10);
+	val = Get_DqsRcvEnGross_MaxMin(pDCTstat, dev, dct, index_reg, 0x10);
 	Largest = val & 0xFF;
 	Smallest = (val >> 8) & 0xFF;
 
 	/* DqsRcvEn byte 3,2 */
-	val = Get_DqsRcvEnGross_MaxMin(pDCTstat, dev, index_reg, 0x11);
+	val = Get_DqsRcvEnGross_MaxMin(pDCTstat, dev, dct, index_reg, 0x11);
 	byte = val & 0xFF;
 	bytex = (val >> 8) & 0xFF;
 	if (bytex < Smallest)
@@ -2790,7 +4414,7 @@ static void Get_DqsRcvEnGross_Diff(struct DCTStatStruc *pDCTstat,
 		Largest = byte;
 
 	/* DqsRcvEn byte 5,4 */
-	val = Get_DqsRcvEnGross_MaxMin(pDCTstat, dev, index_reg, 0x20);
+	val = Get_DqsRcvEnGross_MaxMin(pDCTstat, dev, dct, index_reg, 0x20);
 	byte = val & 0xFF;
 	bytex = (val >> 8) & 0xFF;
 	if (bytex < Smallest)
@@ -2799,7 +4423,7 @@ static void Get_DqsRcvEnGross_Diff(struct DCTStatStruc *pDCTstat,
 		Largest = byte;
 
 	/* DqsRcvEn byte 7,6 */
-	val = Get_DqsRcvEnGross_MaxMin(pDCTstat, dev, index_reg, 0x21);
+	val = Get_DqsRcvEnGross_MaxMin(pDCTstat, dev, dct, index_reg, 0x21);
 	byte = val & 0xFF;
 	bytex = (val >> 8) & 0xFF;
 	if (bytex < Smallest)
@@ -2809,7 +4433,7 @@ static void Get_DqsRcvEnGross_Diff(struct DCTStatStruc *pDCTstat,
 
 	if (pDCTstat->DimmECCPresent> 0) {
 		/*DqsRcvEn Ecc */
-		val = Get_DqsRcvEnGross_MaxMin(pDCTstat, dev, index_reg, 0x12);
+		val = Get_DqsRcvEnGross_MaxMin(pDCTstat, dev, dct, index_reg, 0x12);
 		byte = val & 0xFF;
 		bytex = (val >> 8) & 0xFF;
 		if (bytex < Smallest)
@@ -2873,7 +4497,7 @@ static void Get_WrDatGross_Diff(struct DCTStatStruc *pDCTstat,
 }
 
 static u16 Get_DqsRcvEnGross_MaxMin(struct DCTStatStruc *pDCTstat,
-					u32 dev, u32 index_reg,
+					u32 dev, uint8_t dct, u32 index_reg,
 					u32 index)
 {
 	u8 Smallest, Largest;
@@ -2891,7 +4515,7 @@ static u16 Get_DqsRcvEnGross_MaxMin(struct DCTStatStruc *pDCTstat,
 
 	for (i=0; i < 8; i+=2) {
 		if ( pDCTstat->DIMMValid & (1 << i)) {
-			val = Get_NB32_index_wait(dev, index_reg, index);
+			val = Get_NB32_index_wait_DCT(dev, dct, index_reg, index);
 			val &= 0x00E000E0;
 			byte = (val >> 5) & 0xFF;
 			if (byte < Smallest)
@@ -2929,7 +4553,7 @@ static u16 Get_WrDatGross_MaxMin(struct DCTStatStruc *pDCTstat,
 	Smallest = 3;
 	Largest = 0;
 	for (i=0; i < 2; i++) {
-		val = Get_NB32_index_wait(dev, index_reg, index);
+		val = Get_NB32_index_wait_DCT(dev, dct, index_reg, index);
 		val &= 0x60606060;
 		val >>= 5;
 		for (j=0; j < 4; j++) {
@@ -2945,7 +4569,7 @@ static u16 Get_WrDatGross_MaxMin(struct DCTStatStruc *pDCTstat,
 
 	if (pDCTstat->DimmECCPresent > 0) {
 		index++;
-		val = Get_NB32_index_wait(dev, index_reg, index);
+		val = Get_NB32_index_wait_DCT(dev, dct, index_reg, index);
 		val &= 0x00000060;
 		val >>= 5;
 		byte = val & 0xFF;
@@ -2965,25 +4589,30 @@ static u16 Get_WrDatGross_MaxMin(struct DCTStatStruc *pDCTstat,
 static void mct_PhyController_Config(struct MCTStatStruc *pMCTstat,
 				     struct DCTStatStruc *pDCTstat, u8 dct)
 {
-	u32 index_reg = 0x98 + 0x100 * dct;
+	uint8_t index;
+	uint32_t dword;
+	u32 index_reg = 0x98;
 	u32 dev = pDCTstat->dev_dct;
-	u32 val;
 
-	if (pDCTstat->LogicalCPUID & (AMD_DR_DAC2_OR_C3 | AMD_RB_C3)) {
+	if (pDCTstat->LogicalCPUID & (AMD_DR_DAC2_OR_C3 | AMD_RB_C3 | AMD_FAM15_ALL)) {
 		if (pDCTstat->Dimmx4Present == 0) {
-			/* Set bit7 RxDqsUDllPowerDown  to register F2x[1, 0]98_x0D0F0F13 for power saving */
-			val = Get_NB32_index_wait(dev, index_reg, 0x0D0F0F13); /* Agesa v3 v6 might be wrong here. */
-			val |= 1 << 7; /* BIOS should set this bit when x4 DIMMs are not present */
-			Set_NB32_index_wait(dev, index_reg, 0x0D0F0F13, val);
+			/* Set bit7 RxDqsUDllPowerDown to register F2x[1, 0]98_x0D0F0F13 for
+			 * additional power saving when x4 DIMMs are not present.
+			 */
+			for (index = 0; index < 0x9; index++) {
+				dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f0013 | (index << 8));
+				dword |= (0x1 << 7);				/* RxDqsUDllPowerDown = 1 */
+				Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f0013 | (index << 8), dword);
+			}
 		}
 	}
 
-	if (pDCTstat->LogicalCPUID & AMD_DR_DAC2_OR_C3) {
+	if (pDCTstat->LogicalCPUID & (AMD_DR_DAC2_OR_C3 | AMD_FAM15_ALL)) {
 		if (pDCTstat->DimmECCPresent == 0) {
 			/* Set bit4 PwrDn to register F2x[1, 0]98_x0D0F0830 for power saving */
-			val = Get_NB32_index_wait(dev, index_reg, 0x0D0F0830);
-			val |= 1 << 4; /* BIOS should set this bit if ECC DIMMs are not present */
-			Set_NB32_index_wait(dev, index_reg, 0x0D0F0830, val);
+			dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f0830);
+			dword |= 1 << 4; /* BIOS should set this bit if ECC DIMMs are not present */
+			Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f0830, dword);
 		}
 	}
 
@@ -3024,21 +4653,61 @@ static void mct_FinalMCT_D(struct MCTStatStruc *pMCTstat,
 				val &= ~(1 << 12);
 
 			val &= 0x0FFFFFFF;
-			switch (pDCTstat->Speed) {
-			case 4:
-				val |= 0x50000000; /* 5 for DDR800 */
-				break;
-			case 5:
-				val |= 0x60000000; /* 6 for DDR1066 */
-				break;
-			case 6:
-				val |= 0x80000000; /* 8 for DDR800 */
-				break;
-			default:
-				val |= 0x90000000; /* 9 for DDR1600 */
-				break;
+			if (!is_fam15h()) {
+				switch (pDCTstat->Speed) {
+				case 4:
+					val |= 0x50000000; /* 5 for DDR800 */
+					break;
+				case 5:
+					val |= 0x60000000; /* 6 for DDR1066 */
+					break;
+				case 6:
+					val |= 0x80000000; /* 8 for DDR800 */
+					break;
+				default:
+					val |= 0x90000000; /* 9 for DDR1600 */
+					break;
+				}
 			}
 			Set_NB32(pDCTstat->dev_dct, 0x1B0, val);
+
+			if (is_fam15h()) {
+				uint8_t wm1;
+				uint8_t wm2;
+
+				switch (pDCTstat->Speed) {
+				case 0x4:
+					wm1 = 0x3;
+					wm2 = 0x4;
+					break;
+				case 0x6:
+					wm1 = 0x3;
+					wm2 = 0x5;
+					break;
+				case 0xa:
+					wm1 = 0x4;
+					wm2 = 0x6;
+					break;
+				case 0xe:
+					wm1 = 0x5;
+					wm2 = 0x8;
+					break;
+				case 0x12:
+					wm1 = 0x6;
+					wm2 = 0x9;
+					break;
+				default:
+					wm1 = 0x7;
+					wm2 = 0xa;
+					break;
+				}
+
+				val = Get_NB32(pDCTstat->dev_dct, 0x1B4);
+				val &= ~(0x3ff);
+				val |= ((wm2 & 0x1f) << 5);
+				val |= (wm1 & 0x1f);
+				Set_NB32(pDCTstat->dev_dct, 0x1B4, val);
+			}
 		}
 	}
 
@@ -3055,16 +4724,103 @@ static void mct_FinalMCT_D(struct MCTStatStruc *pMCTstat,
 	}
 }
 
+void mct_ForceNBPState0_En_Fam15(struct MCTStatStruc *pMCTstat,
+				struct DCTStatStruc *pDCTstat)
+{
+	/* Force the NB P-state to P0 */
+	uint32_t dword;
+	uint32_t dword2;
+
+	dword = Get_NB32(pDCTstat->dev_nbctl, 0x174);
+	if (!(dword & 0x1)) {
+		dword = Get_NB32(pDCTstat->dev_nbctl, 0x170);
+		pDCTstat->SwNbPstateLoDis = (dword >> 14) & 0x1;
+		pDCTstat->NbPstateDisOnP0 = (dword >> 13) & 0x1;
+		pDCTstat->NbPstateThreshold = (dword >> 9) & 0x7;
+		pDCTstat->NbPstateHi = (dword >> 6) & 0x3;
+		dword &= ~(0x1 << 14);		/* SwNbPstateLoDis = 0 */
+		dword &= ~(0x1 << 13);		/* NbPstateDisOnP0 = 0 */
+		dword &= ~(0x7 << 9);		/* NbPstateThreshold = 0 */
+		dword &= ~(0x3 << 3);		/* NbPstateLo = NbPstateMaxVal */
+		dword |= ((dword & 0x3) << 3);
+		Set_NB32(pDCTstat->dev_nbctl, 0x170, dword);
+
+		/* Wait until CurNbPState == NbPstateLo */
+		do {
+			dword2 = Get_NB32(pDCTstat->dev_nbctl, 0x174);
+		} while (((dword2 << 19) & 0x7) != (dword & 0x3));
+
+		dword = Get_NB32(pDCTstat->dev_nbctl, 0x170);
+		dword &= ~(0x3 << 6);		/* NbPstateHi = 0 */
+		dword |= (0x3 << 14);		/* SwNbPstateLoDis = 1 */
+		Set_NB32(pDCTstat->dev_nbctl, 0x170, dword);
+
+		/* Wait until CurNbPState == 0 */
+		do {
+			dword2 = Get_NB32(pDCTstat->dev_nbctl, 0x174);
+		} while (((dword2 << 19) & 0x7) != 0);
+	}
+}
+
+void mct_ForceNBPState0_Dis_Fam15(struct MCTStatStruc *pMCTstat,
+				struct DCTStatStruc *pDCTstat)
+{
+	/* Restore normal NB P-state functionailty */
+	uint32_t dword;
+
+	dword = Get_NB32(pDCTstat->dev_nbctl, 0x174);
+	if (!(dword & 0x1)) {
+		dword = Get_NB32(pDCTstat->dev_nbctl, 0x170);
+		dword &= ~(0x1 << 14);					/* SwNbPstateLoDis*/
+		dword |= ((pDCTstat->SwNbPstateLoDis & 0x1) << 14);
+		dword &= ~(0x1 << 13);					/* NbPstateDisOnP0 */
+		dword |= ((pDCTstat->NbPstateDisOnP0 & 0x1) << 13);
+		dword &= ~(0x7 << 9);					/* NbPstateThreshold */
+		dword |= ((pDCTstat->NbPstateThreshold & 0x7) << 9);
+		dword &= ~(0x3 << 6);					/* NbPstateHi */
+		dword |= ((pDCTstat->NbPstateHi & 0x3) << 3);
+		Set_NB32(pDCTstat->dev_nbctl, 0x170, dword);
+	}
+}
+
 static void mct_InitialMCT_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat)
 {
-	mct_SetClToNB_D(pMCTstat, pDCTstat);
-	mct_SetWbEnhWsbDis_D(pMCTstat, pDCTstat);
+	if (is_fam15h()) {
+		msr_t p0_state_msr;
+		uint8_t cpu_fid;
+		uint8_t cpu_did;
+		uint32_t cpu_divisor;
+		uint8_t boost_states;
+
+		/* Retrieve the number of boost states */
+		boost_states = (Get_NB32(pDCTstat->dev_link, 0x15c) >> 2) & 0x7;
+
+		/* Retrieve and store the TSC frequency (P0 COF) */
+		p0_state_msr = rdmsr(0xc0010064 + boost_states);
+		cpu_fid = p0_state_msr.lo & 0x3f;
+		cpu_did = (p0_state_msr.lo >> 6) & 0x7;
+		cpu_divisor = (0x1 << cpu_did);
+		pMCTstat->TSCFreq = (100 * (cpu_fid + 0x10)) / cpu_divisor;
+
+		mct_ForceNBPState0_En_Fam15(pMCTstat, pDCTstat);
+	} else {
+		/* K10 BKDG v3.62 section 2.8.9.2 */
+		printk(BIOS_DEBUG, "mct_InitialMCT_D: clear_legacy_Mode\n");
+		clear_legacy_Mode(pMCTstat, pDCTstat);
+
+		/* Northbridge configuration */
+		mct_SetClToNB_D(pMCTstat, pDCTstat);
+		mct_SetWbEnhWsbDis_D(pMCTstat, pDCTstat);
+	}
 }
 
 static u32 mct_NodePresent_D(void)
 {
 	u32 val;
-	val = 0x12001022;
+	if (is_fam15h())
+		val = 0x16001022;
+	else
+		val = 0x12001022;
 	return val;
 }
 
@@ -3097,14 +4853,13 @@ static void clear_legacy_Mode(struct MCTStatStruc *pMCTstat,
 
 	/* Clear Legacy BIOS Mode bit */
 	reg = 0x94;
-	val = Get_NB32(dev, reg);
+	val = Get_NB32_DCT(dev, 0, reg);
 	val &= ~(1<<LegacyBiosMode);
-	Set_NB32(dev, reg, val);
+	Set_NB32_DCT(dev, 0, reg, val);
 
-	reg = 0x94 + 0x100;
-	val = Get_NB32(dev, reg);
+	val = Get_NB32_DCT(dev, 1, reg);
 	val &= ~(1<<LegacyBiosMode);
-	Set_NB32(dev, reg, val);
+	Set_NB32_DCT(dev, 1, reg, val);
 }
 
 static void mct_HTMemMapExt(struct MCTStatStruc *pMCTstat,
@@ -3171,7 +4926,7 @@ static void SetCSTriState(struct MCTStatStruc *pMCTstat,
 {
 	u32 val;
 	u32 dev = pDCTstat->dev_dct;
-	u32 index_reg = 0x98 + 0x100 * dct;
+	u32 index_reg = 0x98;
 	u32 index;
 	u16 word;
 
@@ -3186,9 +4941,9 @@ static void SetCSTriState(struct MCTStatStruc *pMCTstat,
 	}
 	word = (~word) & 0xFF;
 	index  = 0x0c;
-	val = Get_NB32_index_wait(dev, index_reg, index);
+	val = Get_NB32_index_wait_DCT(dev, dct, index_reg, index);
 	val |= word;
-	Set_NB32_index_wait(dev, index_reg, index, val);
+	Set_NB32_index_wait_DCT(dev, dct, index_reg, index, val);
 }
 
 static void SetCKETriState(struct MCTStatStruc *pMCTstat,
@@ -3196,7 +4951,7 @@ static void SetCKETriState(struct MCTStatStruc *pMCTstat,
 {
 	u32 val;
 	u32 dev;
-	u32 index_reg = 0x98 + 0x100 * dct;
+	u32 index_reg = 0x98;
 	u32 index;
 	u16 word;
 
@@ -3208,14 +4963,14 @@ static void SetCKETriState(struct MCTStatStruc *pMCTstat,
 	word = pDCTstat->CSPresent;
 
 	index  = 0x0c;
-	val = Get_NB32_index_wait(dev, index_reg, index);
+	val = Get_NB32_index_wait_DCT(dev, dct, index_reg, index);
 	if ((word & 0x55) == 0)
 		val |= 1 << 12;
 
 	if ((word & 0xAA) == 0)
 		val |= 1 << 13;
 
-	Set_NB32_index_wait(dev, index_reg, index, val);
+	Set_NB32_index_wait_DCT(dev, dct, index_reg, index, val);
 }
 
 static void SetODTTriState(struct MCTStatStruc *pMCTstat,
@@ -3223,7 +4978,7 @@ static void SetODTTriState(struct MCTStatStruc *pMCTstat,
 {
 	u32 val;
 	u32 dev;
-	u32 index_reg = 0x98 + 0x100 * dct;
+	u32 index_reg = 0x98;
 	u8 cs;
 	u32 index;
 	u8 odt;
@@ -3257,86 +5012,281 @@ static void SetODTTriState(struct MCTStatStruc *pMCTstat,
 	}
 
 	index  = 0x0C;
-	val = Get_NB32_index_wait(dev, index_reg, index);
+	val = Get_NB32_index_wait_DCT(dev, dct, index_reg, index);
 	val |= ((odt & 0xFF) << 8);	/* set bits 11:8 ODTTriState[3:0] */
-	Set_NB32_index_wait(dev, index_reg, index, val);
+	Set_NB32_index_wait_DCT(dev, dct, index_reg, index, val);
+
+}
+
+/* Family 15h */
+static void InitDDRPhy(struct MCTStatStruc *pMCTstat,
+					struct DCTStatStruc *pDCTstat, u8 dct)
+{
+	uint8_t index;
+	uint32_t dword;
+	uint8_t ddr_voltage_index;
+	uint8_t amd_voltage_level_index = 0;
+	uint32_t index_reg = 0x98;
+	uint32_t dev = pDCTstat->dev_dct;
+
+	printk(BIOS_DEBUG, "%s: Start\n", __func__);
 
+	/* Find current DDR supply voltage for this DCT */
+	ddr_voltage_index = dct_ddr_voltage_index(pDCTstat, dct);
+
+	/* Fam15h BKDG v3.14 section 2.10.5.3
+	 * The remainder of the Phy Initialization algorithm picks up in phyAssistedMemFnceTraining
+	 */
+	for (dct = 0; dct < 2; dct++) {
+		Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0000000b, 0x80000000);
+		Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fe013, 0x00000118);
+
+		/* Program desired VDDIO level */
+		if (ddr_voltage_index & 0x4) {
+			/* 1.25V */
+			amd_voltage_level_index = 0x2;
+		} else if (ddr_voltage_index & 0x2) {
+			/* 1.35V */
+			amd_voltage_level_index = 0x1;
+		} else if (ddr_voltage_index & 0x1) {
+			/* 1.50V */
+			amd_voltage_level_index = 0x0;
+		}
+
+		/* D18F2x9C_x0D0F_0[F,8:0]1F_dct[1:0][RxVioLvl] */
+		for (index = 0; index < 0x9; index++) {
+			dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f001f | (index << 8));
+			dword &= ~(0x3 << 3);
+			dword |= (amd_voltage_level_index << 3);
+			Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f001f | (index << 8), dword);
+		}
+
+		/* D18F2x9C_x0D0F_[C,8,2][2:0]1F_dct[1:0][RxVioLvl] */
+		for (index = 0; index < 0x3; index++) {
+			dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f201f | (index << 8));
+			dword &= ~(0x3 << 3);
+			dword |= (amd_voltage_level_index << 3);
+			Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f201f | (index << 8), dword);
+		}
+		for (index = 0; index < 0x2; index++) {
+			dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f801f | (index << 8));
+			dword &= ~(0x3 << 3);
+			dword |= (amd_voltage_level_index << 3);
+			Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f801f | (index << 8), dword);
+		}
+		for (index = 0; index < 0x1; index++) {
+			dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fc01f | (index << 8));
+			dword &= ~(0x3 << 3);
+			dword |= (amd_voltage_level_index << 3);
+			Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fc01f | (index << 8), dword);
+		}
+
+		/* D18F2x9C_x0D0F_4009_dct[1:0][CmpVioLvl, ComparatorAdjust] */
+		dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f4009);
+		dword &= ~(0x0000c00c);
+		dword |= (amd_voltage_level_index << 14);
+		dword |= (amd_voltage_level_index << 2);
+		Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f4009, dword);
+	}
+
+	printk(BIOS_DEBUG, "%s: Done\n", __func__);
 }
 
 static void InitPhyCompensation(struct MCTStatStruc *pMCTstat,
 					struct DCTStatStruc *pDCTstat, u8 dct)
 {
 	u8 i;
-	u32 index_reg = 0x98 + 0x100 * dct;
+	u32 index_reg = 0x98;
 	u32 dev = pDCTstat->dev_dct;
-	u32 val;
 	u32 valx = 0;
-	u32 dword;
+	uint8_t index;
+	uint32_t dword;
 	const u8 *p;
 
-	val = Get_NB32_index_wait(dev, index_reg, 0x00);
-	dword = 0;
-	for (i=0; i < 6; i++) {
-		switch (i) {
-			case 0:
-			case 4:
-				p = Table_Comp_Rise_Slew_15x;
-				valx = p[(val >> 16) & 3];
-				break;
-			case 1:
-			case 5:
-				p = Table_Comp_Fall_Slew_15x;
-				valx = p[(val >> 16) & 3];
-				break;
-			case 2:
-				p = Table_Comp_Rise_Slew_20x;
-				valx = p[(val >> 8) & 3];
-				break;
-			case 3:
-				p = Table_Comp_Fall_Slew_20x;
-				valx = p[(val >> 8) & 3];
-				break;
+	printk(BIOS_DEBUG, "%s: Start\n", __func__);
+
+	if (is_fam15h()) {
+		/* Algorithm detailed in the Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 */
+		uint32_t tx_pre;
+		uint32_t drive_strength;
+
+		/* Program D18F2x9C_x0D0F_E003_dct[1:0][DisAutoComp, DisablePredriverCal] */
+		dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fe003);
+		dword |= (0x3 << 13);
+		Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fe003, dword);
+
+		/* Determine TxPreP/TxPreN for data lanes (Stage 1) */
+		dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000000);
+		drive_strength = (dword >> 20) & 0x7;	/* DqsDrvStren */
+		tx_pre = fam15h_phy_predriver_calibration_code(pDCTstat, dct, drive_strength);
+
+		/* Program TxPreP/TxPreN for data lanes (Stage 1) */
+		for (index = 0; index < 0x9; index++) {
+			dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f0006 | (index << 8));
+			dword &= ~(0xfff);
+			dword |= tx_pre;
+			Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f0006 | (index << 8), dword);
+		}
 
+		/* Determine TxPreP/TxPreN for data lanes (Stage 2) */
+		dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000000);
+		drive_strength = (dword >> 16) & 0x7;	/* DataDrvStren */
+		tx_pre = fam15h_phy_predriver_calibration_code(pDCTstat, dct, drive_strength);
+
+		/* Program TxPreP/TxPreN for data lanes (Stage 2) */
+		for (index = 0; index < 0x9; index++) {
+			dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f000a | (index << 8));
+			dword &= ~(0xfff);
+			dword |= tx_pre;
+			Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f000a | (index << 8), dword);
+		}
+		for (index = 0; index < 0x9; index++) {
+			dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f0002 | (index << 8));
+			dword &= ~(0xfff);
+			dword |= (0x8000 | tx_pre);
+			Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f0002 | (index << 8), dword);
 		}
-		dword |= valx << (5 * i);
-	}
 
-	/* Override/Exception */
-	if (!pDCTstat->GangedMode) {
-		i = 0; /* use i for the dct setting required */
-		if (pDCTstat->MAdimms[0] < 4)
-			i = 1;
-		if (((pDCTstat->Speed == 2) || (pDCTstat->Speed == 3)) && (pDCTstat->MAdimms[i] == 4)) {
-			dword &= 0xF18FFF18;
-			index_reg = 0x98;	/* force dct = 0 */
+		/* Determine TxPreP/TxPreN for command/address lines (Stage 1) */
+		dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000000);
+		drive_strength = (dword >> 4) & 0x7;	/* CsOdtDrvStren */
+		tx_pre = fam15h_phy_predriver_cmd_addr_calibration_code(pDCTstat, dct, drive_strength);
+
+		/* Program TxPreP/TxPreN for command/address lines (Stage 1) */
+		dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f8006);
+		dword &= ~(0xfff);
+		dword |= tx_pre;
+		Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f8006, dword);
+		dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f800a);
+		dword &= ~(0xfff);
+		dword |= tx_pre;
+		Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f800a, dword);
+		dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f8002);
+		dword &= ~(0xfff);
+		dword |= (0x8000 | tx_pre);
+		Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f8002, dword);
+
+		/* Determine TxPreP/TxPreN for command/address lines (Stage 2) */
+		dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000000);
+		drive_strength = (dword >> 8) & 0x7;	/* AddrCmdDrvStren */
+		tx_pre = fam15h_phy_predriver_cmd_addr_calibration_code(pDCTstat, dct, drive_strength);
+
+		/* Program TxPreP/TxPreN for command/address lines (Stage 2) */
+		dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f8106);
+		dword &= ~(0xfff);
+		dword |= tx_pre;
+		Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f8106, dword);
+		dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f810a);
+		dword &= ~(0xfff);
+		dword |= tx_pre;
+		Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f810a, dword);
+		dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fc006);
+		dword &= ~(0xfff);
+		dword |= tx_pre;
+		Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fc006, dword);
+		dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fc00a);
+		dword &= ~(0xfff);
+		dword |= tx_pre;
+		Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fc00a, dword);
+		dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fc00e);
+		dword &= ~(0xfff);
+		dword |= tx_pre;
+		Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fc00e, dword);
+		dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fc012);
+		dword &= ~(0xfff);
+		dword |= tx_pre;
+		Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fc012, dword);
+		dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f8102);
+		dword &= ~(0xfff);
+		dword |= (0x8000 | tx_pre);
+		Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f8102, dword);
+
+		/* Determine TxPreP/TxPreN for command/address lines (Stage 3) */
+		dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000000);
+		drive_strength = (dword >> 0) & 0x7;	/* CkeDrvStren */
+		tx_pre = fam15h_phy_predriver_cmd_addr_calibration_code(pDCTstat, dct, drive_strength);
+
+		/* Program TxPreP/TxPreN for command/address lines (Stage 3) */
+		dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fc002);
+		dword &= ~(0xfff);
+		dword |= (0x8000 | tx_pre);
+		Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fc002, dword);
+
+		/* Determine TxPreP/TxPreN for clock lines */
+		dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000000);
+		drive_strength = (dword >> 12) & 0x7;	/* ClkDrvStren */
+		tx_pre = fam15h_phy_predriver_clk_calibration_code(pDCTstat, dct, drive_strength);
+
+		/* Program TxPreP/TxPreN for clock lines */
+		dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f2002);
+		dword &= ~(0xfff);
+		dword |= (0x8000 | tx_pre);
+		Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f2002, dword);
+		dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f2102);
+		dword &= ~(0xfff);
+		dword |= (0x8000 | tx_pre);
+		Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f2102, dword);
+		dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f2202);
+		dword &= ~(0xfff);
+		dword |= (0x8000 | tx_pre);
+		Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f2202, dword);
+	} else {
+		dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x00);
+		dword = 0;
+		for (i=0; i < 6; i++) {
+			switch (i) {
+				case 0:
+				case 4:
+					p = Table_Comp_Rise_Slew_15x;
+					valx = p[(dword >> 16) & 3];
+					break;
+				case 1:
+				case 5:
+					p = Table_Comp_Fall_Slew_15x;
+					valx = p[(dword >> 16) & 3];
+					break;
+				case 2:
+					p = Table_Comp_Rise_Slew_20x;
+					valx = p[(dword >> 8) & 3];
+					break;
+				case 3:
+					p = Table_Comp_Fall_Slew_20x;
+					valx = p[(dword >> 8) & 3];
+					break;
+			}
+			dword |= valx << (5 * i);
 		}
+
+		Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0a, dword);
 	}
 
-	Set_NB32_index_wait(dev, index_reg, 0x0a, dword);
+	printk(BIOS_DEBUG, "%s: Done\n", __func__);
 }
 
 static void mct_EarlyArbEn_D(struct MCTStatStruc *pMCTstat,
 					struct DCTStatStruc *pDCTstat, u8 dct)
 {
-	u32 reg;
-	u32 val;
-	u32 dev = pDCTstat->dev_dct;
-
-	/* GhEnhancement #18429 modified by askar: For low NB CLK :
-	 * Memclk ratio, the DCT may need to arbitrate early to avoid
-	 * unnecessary bubbles.
-	 * bit 19 of F2x[1,0]78 Dram  Control Register, set this bit only when
-	 * NB CLK : Memclk ratio is between 3:1 (inclusive) to 4:5 (inclusive)
-	 */
-	reg = 0x78 + 0x100 * dct;
-	val = Get_NB32(dev, reg);
-
-	if (pDCTstat->LogicalCPUID & (AMD_DR_Cx | AMD_DR_Dx))
-		val |= (1 << EarlyArbEn);
-	else if (CheckNBCOFEarlyArbEn(pMCTstat, pDCTstat))
-		val |= (1 << EarlyArbEn);
-
-	Set_NB32(dev, reg, val);
+	if (!is_fam15h()) {
+		u32 reg;
+		u32 val;
+		u32 dev = pDCTstat->dev_dct;
+
+		/* GhEnhancement #18429 modified by askar: For low NB CLK :
+		* Memclk ratio, the DCT may need to arbitrate early to avoid
+		* unnecessary bubbles.
+		* bit 19 of F2x[1,0]78 Dram  Control Register, set this bit only when
+		* NB CLK : Memclk ratio is between 3:1 (inclusive) to 4:5 (inclusive)
+		*/
+		reg = 0x78;
+		val = Get_NB32_DCT(dev, dct, reg);
+
+		if (pDCTstat->LogicalCPUID & (AMD_DR_Cx | AMD_DR_Dx))
+			val |= (1 << EarlyArbEn);
+		else if (CheckNBCOFEarlyArbEn(pMCTstat, pDCTstat))
+			val |= (1 << EarlyArbEn);
+
+		Set_NB32_DCT(dev, dct, reg, val);
+	}
 }
 
 static u8 CheckNBCOFEarlyArbEn(struct MCTStatStruc *pMCTstat,
@@ -3359,9 +5309,9 @@ static u8 CheckNBCOFEarlyArbEn(struct MCTStatStruc *pMCTstat,
 		NbDid |= 1;
 
 	reg = 0x94;
-	val = Get_NB32(dev, reg);
+	val = Get_NB32_DCT(dev, 0, reg);
 	if (!(val & (1 << MemClkFreqVal)))
-		val = Get_NB32(dev, reg + 0x100);	/* get the DCT1 value */
+		val = Get_NB32_DCT(dev, 1, reg);	/* get the DCT1 value */
 
 	val &= 0x07;
 	val += 3;
@@ -3430,28 +5380,204 @@ static void mct_ResetDataStruct_D(struct MCTStatStruc *pMCTstat,
 }
 
 static void mct_BeforeDramInit_Prod_D(struct MCTStatStruc *pMCTstat,
-					struct DCTStatStruc *pDCTstat)
+					struct DCTStatStruc *pDCTstat, u8 dct)
+{
+	mct_ProgramODT_D(pMCTstat, pDCTstat, dct);
+}
+
+static void mct_ProgramODT_D(struct MCTStatStruc *pMCTstat,
+					struct DCTStatStruc *pDCTstat, u8 dct)
 {
 	u8 i;
-	u32 reg_off, dword;
+	u32 dword;
 	u32 dev = pDCTstat->dev_dct;
 
-	if (pDCTstat->LogicalCPUID & AMD_DR_Dx) {
+	/* FIXME
+	 * Mainboards need to be able to specify the maximum number of DIMMs installable per channel
+	 * For now assume a maximum of 2 DIMMs per channel can be installed
+	 */
+	uint8_t MaxDimmsInstallable = 2;
+
+	if (is_fam15h()) {
+		/* Obtain number of DIMMs on channel */
+		uint8_t dimm_count = pDCTstat->MAdimms[dct];
+		uint8_t rank_count_dimm0;
+		uint8_t rank_count_dimm1;
+		uint32_t odt_pattern_0;
+		uint32_t odt_pattern_1;
+		uint32_t odt_pattern_2;
+		uint32_t odt_pattern_3;
+		uint8_t write_odt_duration;
+		uint8_t read_odt_duration;
+		uint8_t write_odt_delay;
+		uint8_t read_odt_delay;
+
+		/* Select appropriate ODT pattern for installed DIMMs
+		 * Refer to the Fam15h BKDG Rev. 3.14, page 149 onwards
+		 */
+		if (pDCTstat->C_DCTPtr[dct]->Status[DCT_STATUS_REGISTERED]) {
+			if (MaxDimmsInstallable == 2) {
+				if (dimm_count == 1) {
+					/* 1 DIMM detected */
+					rank_count_dimm1 = pDCTstat->C_DCTPtr[dct]->DimmRanks[1];
+					if (rank_count_dimm1 == 1) {
+						odt_pattern_0 = 0x00000000;
+						odt_pattern_1 = 0x00000000;
+						odt_pattern_2 = 0x00000000;
+						odt_pattern_3 = 0x00020000;
+					} else if (rank_count_dimm1 == 2) {
+						odt_pattern_0 = 0x00000000;
+						odt_pattern_1 = 0x00000000;
+						odt_pattern_2 = 0x00000000;
+						odt_pattern_3 = 0x08020000;
+					} else if (rank_count_dimm1 == 4) {
+						odt_pattern_0 = 0x00000000;
+						odt_pattern_1 = 0x00000000;
+						odt_pattern_2 = 0x020a0000;
+						odt_pattern_3 = 0x080a0000;
+					} else {
+						/* Fallback */
+						odt_pattern_0 = 0x00000000;
+						odt_pattern_1 = 0x00000000;
+						odt_pattern_2 = 0x00000000;
+						odt_pattern_3 = 0x08020000;
+					}
+				} else {
+					/* 2 DIMMs detected */
+					rank_count_dimm0 = pDCTstat->C_DCTPtr[dct]->DimmRanks[0];
+					rank_count_dimm1 = pDCTstat->C_DCTPtr[dct]->DimmRanks[1];
+					if ((rank_count_dimm0 < 4) && (rank_count_dimm1 < 4)) {
+						odt_pattern_0 = 0x00000000;
+						odt_pattern_1 = 0x01010202;
+						odt_pattern_2 = 0x00000000;
+						odt_pattern_3 = 0x09030603;
+					} else if ((rank_count_dimm0 < 4) && (rank_count_dimm1 == 4)) {
+						odt_pattern_0 = 0x01010000;
+						odt_pattern_1 = 0x01010a0a;
+						odt_pattern_2 = 0x01090000;
+						odt_pattern_3 = 0x01030e0b;
+					} else if ((rank_count_dimm0 == 4) && (rank_count_dimm1 < 4)) {
+						odt_pattern_0 = 0x00000202;
+						odt_pattern_1 = 0x05050202;
+						odt_pattern_2 = 0x00000206;
+						odt_pattern_3 = 0x0d070203;
+					} else if ((rank_count_dimm0 == 4) && (rank_count_dimm1 == 4)) {
+						odt_pattern_0 = 0x05050a0a;
+						odt_pattern_1 = 0x05050a0a;
+						odt_pattern_2 = 0x050d0a0e;
+						odt_pattern_3 = 0x05070a0b;
+					} else {
+						/* Fallback */
+						odt_pattern_0 = 0x00000000;
+						odt_pattern_1 = 0x00000000;
+						odt_pattern_2 = 0x00000000;
+						odt_pattern_3 = 0x00000000;
+					}
+				}
+			} else {
+				/* FIXME
+				 * 3 DIMMs per channel UNIMPLEMENTED
+				 */
+				odt_pattern_0 = 0x00000000;
+				odt_pattern_1 = 0x00000000;
+				odt_pattern_2 = 0x00000000;
+				odt_pattern_3 = 0x00000000;
+			}
+		} else if (pDCTstat->C_DCTPtr[dct]->Status[DCT_STATUS_LOAD_REDUCED]) {
+			/* TODO
+			 * Load reduced dimms UNIMPLEMENTED
+			 */
+			odt_pattern_0 = 0x00000000;
+			odt_pattern_1 = 0x00000000;
+			odt_pattern_2 = 0x00000000;
+			odt_pattern_3 = 0x00000000;
+		} else {
+			if (MaxDimmsInstallable == 2) {
+				if (dimm_count == 1) {
+					/* 1 DIMM detected */
+					rank_count_dimm1 = pDCTstat->C_DCTPtr[dct]->DimmRanks[1];
+					if (rank_count_dimm1 == 1) {
+						odt_pattern_0 = 0x00000000;
+						odt_pattern_1 = 0x00000000;
+						odt_pattern_2 = 0x00000000;
+						odt_pattern_3 = 0x00020000;
+					} else if (rank_count_dimm1 == 2) {
+						odt_pattern_0 = 0x00000000;
+						odt_pattern_1 = 0x00000000;
+						odt_pattern_2 = 0x00000000;
+						odt_pattern_3 = 0x08020000;
+					} else {
+						/* Fallback */
+						odt_pattern_0 = 0x00000000;
+						odt_pattern_1 = 0x00000000;
+						odt_pattern_2 = 0x00000000;
+						odt_pattern_3 = 0x08020000;
+					}
+				} else {
+					/* 2 DIMMs detected */
+					odt_pattern_0 = 0x00000000;
+					odt_pattern_1 = 0x01010202;
+					odt_pattern_2 = 0x00000000;
+					odt_pattern_3 = 0x09030603;
+				}
+			} else {
+				/* FIXME
+				 * 3 DIMMs per channel UNIMPLEMENTED
+				 */
+				odt_pattern_0 = 0x00000000;
+				odt_pattern_1 = 0x00000000;
+				odt_pattern_2 = 0x00000000;
+				odt_pattern_3 = 0x00000000;
+			}
+		}
+
+		if (pDCTstat->C_DCTPtr[dct]->Status[DCT_STATUS_LOAD_REDUCED]) {
+			/* TODO
+			 * Load reduced dimms UNIMPLEMENTED
+			 */
+			write_odt_duration = 0x0;
+			read_odt_duration = 0x0;
+			write_odt_delay = 0x0;
+			read_odt_delay = 0x0;
+		} else {
+			uint8_t tcl;
+			uint8_t tcwl;
+			tcl = Get_NB32_DCT(dev, dct, 0x200) & 0x1f;
+			tcwl = Get_NB32_DCT(dev, dct, 0x20c) & 0x1f;
+
+			write_odt_duration = 0x6;
+			read_odt_duration = 0x6;
+			write_odt_delay = 0x0;
+			if (tcl > tcwl)
+				read_odt_delay = tcl - tcwl;
+			else
+				read_odt_delay = 0x0;
+		}
+
+		/* Program ODT pattern */
+		Set_NB32_DCT(dev, dct, 0x230, odt_pattern_1);
+		Set_NB32_DCT(dev, dct, 0x234, odt_pattern_0);
+		Set_NB32_DCT(dev, dct, 0x238, odt_pattern_3);
+		Set_NB32_DCT(dev, dct, 0x23c, odt_pattern_2);
+		dword = Get_NB32_DCT(dev, dct, 0x240);
+		dword &= ~(0x7 << 12);				/* WrOdtOnDuration = write_odt_duration */
+		dword |= (write_odt_duration & 0x7) << 12;
+		dword &= ~(0x7 << 8);				/* WrOdtTrnOnDly = write_odt_delay */
+		dword |= (write_odt_delay & 0x7) << 8;
+		dword &= ~(0xf << 4);				/* RdOdtOnDuration = read_odt_duration */
+		dword |= (read_odt_duration & 0xf) << 4;
+		dword &= ~(0xf);				/* RdOdtTrnOnDly = read_odt_delay */
+		dword |= (read_odt_delay & 0xf);
+		Set_NB32_DCT(dev, dct, 0x240, dword);
+	} else if (pDCTstat->LogicalCPUID & AMD_DR_Dx) {
 		if (pDCTstat->Speed == 3)
 			dword = 0x00000800;
 		else
 			dword = 0x00000000;
 		for (i=0; i < 2; i++) {
-			reg_off = 0x100 * i;
-			Set_NB32(dev,  0x98 + reg_off, 0x0D000030);
-			Set_NB32(dev,  0x9C + reg_off, dword);
-			Set_NB32(dev,  0x98 + reg_off, 0x4D040F30);
-
-			/* FIXME
-			 * Mainboards need to be able to specify the maximum number of DIMMs installable per channel
-			 * For now assume a maximum of 2 DIMMs per channel can be installed
-			 */
-			uint8_t MaxDimmsInstallable = 2;
+			Set_NB32_DCT(dev, i, 0x98, 0x0D000030);
+			Set_NB32_DCT(dev, i, 0x9C, dword);
+			Set_NB32_DCT(dev, i, 0x98, 0x4D040F30);
 
 			/* Obtain number of DIMMs on channel */
 			uint8_t dimm_count = pDCTstat->MAdimms[i];
@@ -3463,7 +5589,7 @@ static void mct_BeforeDramInit_Prod_D(struct MCTStatStruc *pMCTstat,
 			uint32_t odt_pattern_3;
 
 			/* Select appropriate ODT pattern for installed DIMMs
-			 * Refer to the BKDG Rev. 3.62, page 120 onwards
+			 * Refer to the Fam10h BKDG Rev. 3.62, page 120 onwards
 			 */
 			if (pDCTstat->C_DCTPtr[i]->Status[DCT_STATUS_REGISTERED]) {
 				if (MaxDimmsInstallable == 2) {
@@ -3574,10 +5700,10 @@ static void mct_BeforeDramInit_Prod_D(struct MCTStatStruc *pMCTstat,
 			}
 
 			/* Program ODT pattern */
-			Set_NB32_index_wait(dev, 0xf0 + reg_off, 0x180, odt_pattern_1);
-			Set_NB32_index_wait(dev, 0xf0 + reg_off, 0x181, odt_pattern_0);
-			Set_NB32_index_wait(dev, 0xf0 + reg_off, 0x182, odt_pattern_3);
-			Set_NB32_index_wait(dev, 0xf0 + reg_off, 0x183, odt_pattern_2);
+			Set_NB32_index_wait_DCT(dev, i, 0xf0, 0x180, odt_pattern_1);
+			Set_NB32_index_wait_DCT(dev, i, 0xf0, 0x181, odt_pattern_0);
+			Set_NB32_index_wait_DCT(dev, i, 0xf0, 0x182, odt_pattern_3);
+			Set_NB32_index_wait_DCT(dev, i, 0xf0, 0x183, odt_pattern_2);
 		}
 	}
 }
@@ -3585,34 +5711,32 @@ static void mct_BeforeDramInit_Prod_D(struct MCTStatStruc *pMCTstat,
 static void mct_EnDllShutdownSR(struct MCTStatStruc *pMCTstat,
 				struct DCTStatStruc *pDCTstat, u8 dct)
 {
-	u32 reg_off = 0x100 * dct;
 	u32 dev = pDCTstat->dev_dct, val;
 
 	/* Write 0000_07D0h to register F2x[1, 0]98_x4D0FE006 */
 	if (pDCTstat->LogicalCPUID & (AMD_DR_DAC2_OR_C3)) {
-		Set_NB32(dev,  0x9C + reg_off, 0x1C);
-		Set_NB32(dev,  0x98 + reg_off, 0x4D0FE006);
-		Set_NB32(dev,  0x9C + reg_off, 0x13D);
-		Set_NB32(dev,  0x98 + reg_off, 0x4D0FE007);
+		Set_NB32_DCT(dev, dct, 0x9C, 0x1C);
+		Set_NB32_DCT(dev, dct, 0x98, 0x4D0FE006);
+		Set_NB32_DCT(dev, dct, 0x9C, 0x13D);
+		Set_NB32_DCT(dev, dct, 0x98, 0x4D0FE007);
 
-		val = Get_NB32(dev, 0x90 + reg_off);
+		val = Get_NB32_DCT(dev, dct, 0x90);
 		val &= ~(1 << 27/* DisDllShutdownSR */);
-		Set_NB32(dev, 0x90 + reg_off, val);
+		Set_NB32_DCT(dev, dct, 0x90, val);
 	}
 }
 
 static u32 mct_DisDllShutdownSR(struct MCTStatStruc *pMCTstat,
 				struct DCTStatStruc *pDCTstat, u32 DramConfigLo, u8 dct)
 {
-	u32 reg_off = 0x100 * dct;
 	u32 dev = pDCTstat->dev_dct;
 
 	/* Write 0000_07D0h to register F2x[1, 0]98_x4D0FE006 */
 	if (pDCTstat->LogicalCPUID & (AMD_DR_DAC2_OR_C3)) {
-		Set_NB32(dev,  0x9C + reg_off, 0x7D0);
-		Set_NB32(dev,  0x98 + reg_off, 0x4D0FE006);
-		Set_NB32(dev,  0x9C + reg_off, 0x190);
-		Set_NB32(dev,  0x98 + reg_off, 0x4D0FE007);
+		Set_NB32_DCT(dev, dct, 0x9C, 0x7D0);
+		Set_NB32_DCT(dev, dct, 0x98, 0x4D0FE006);
+		Set_NB32_DCT(dev, dct, 0x9C, 0x190);
+		Set_NB32_DCT(dev, dct, 0x98, 0x4D0FE007);
 
 		DramConfigLo |=  /* DisDllShutdownSR */ 1 << 27;
 	}
@@ -3704,52 +5828,61 @@ void ProgDramMRSReg_D(struct MCTStatStruc *pMCTstat,
 				DramMRS |= 1 << 23;
 		}
 	}
-	/*
-	 DRAM MRS Register
-	 DrvImpCtrl: drive impedance control.01b(34 ohm driver; Ron34 = Rzq/7)
-	*/
-	DramMRS |= 1 << 2;
-	/* Dram nominal termination: */
-	byte = pDCTstat->MAdimms[dct];
-	if (!(pDCTstat->Status & (1 << SB_Registered))) {
-		DramMRS |= 1 << 7; /* 60 ohms */
-		if (byte & 2) {
-			if (pDCTstat->Speed < 6)
-				DramMRS |= 1 << 8; /* 40 ohms */
-			else
-				DramMRS |= 1 << 9; /* 30 ohms */
+
+	if (is_fam15h()) {
+		DramMRS |= (0x1 << 23);		/* PchgPDModeSel = 1 */
+	} else {
+		/*
+		DRAM MRS Register
+		DrvImpCtrl: drive impedance control.01b(34 ohm driver; Ron34 = Rzq/7)
+		*/
+		DramMRS |= 1 << 2;
+		/* Dram nominal termination: */
+		byte = pDCTstat->MAdimms[dct];
+		if (!(pDCTstat->Status & (1 << SB_Registered))) {
+			DramMRS |= 1 << 7; /* 60 ohms */
+			if (byte & 2) {
+				if (pDCTstat->Speed < 6)
+					DramMRS |= 1 << 8; /* 40 ohms */
+				else
+					DramMRS |= 1 << 9; /* 30 ohms */
+			}
 		}
-	}
-	/* Dram dynamic termination: Disable(1DIMM), 120ohm(>=2DIMM) */
-	if (!(pDCTstat->Status & (1 << SB_Registered))) {
-		if (byte >= 2) {
-			if (pDCTstat->Speed == 7)
-				DramMRS |= 1 << 10;
-			else
-				DramMRS |= 1 << 11;
+		/* Dram dynamic termination: Disable(1DIMM), 120ohm(>=2DIMM) */
+		if (!(pDCTstat->Status & (1 << SB_Registered))) {
+			if (byte >= 2) {
+				if (pDCTstat->Speed == 7)
+					DramMRS |= 1 << 10;
+				else
+					DramMRS |= 1 << 11;
+			}
+		} else {
+			DramMRS |= mct_DramTermDyn_RDimm(pMCTstat, pDCTstat, byte);
 		}
-	} else {
-		DramMRS |= mct_DramTermDyn_RDimm(pMCTstat, pDCTstat, byte);
+
+		/* Qoff=0, output buffers enabled */
+		/* Tcwl */
+		DramMRS |= (pDCTstat->Speed - 4) << 20;
+		/* ASR=1, auto self refresh */
+		/* SRT=0 */
+		DramMRS |= 1 << 18;
 	}
 
 	/* burst length control */
 	if (pDCTstat->Status & (1 << SB_128bitmode))
 		DramMRS |= 1 << 1;
-	/* Qoff=0, output buffers enabled */
-	/* Tcwl */
-	DramMRS |= (pDCTstat->Speed - 4) << 20;
-	/* ASR=1, auto self refresh */
-	/* SRT=0 */
-	DramMRS |= 1 << 18;
-
-	dword = Get_NB32(pDCTstat->dev_dct, 0x100 * dct + 0x84);
-	dword &= ~0x00FC2F8F;
+
+	dword = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0x84);
+	if (is_fam15h())
+		dword &= ~0x00800003;
+	else
+		dword &= ~0x00fc2f8f;
 	dword |= DramMRS;
-	Set_NB32(pDCTstat->dev_dct, 0x100 * dct + 0x84, dword);
+	Set_NB32_DCT(pDCTstat->dev_dct, dct, 0x84, dword);
 }
 
-void mct_SetDramConfigHi_D(struct DCTStatStruc *pDCTstat, u32 dct,
-				u32 DramConfigHi)
+void mct_SetDramConfigHi_D(struct MCTStatStruc *pMCTstat,
+				struct DCTStatStruc *pDCTstat, u32 dct, u32 DramConfigHi)
 {
 	/* Bug#15114: Comp. update interrupted by Freq. change can cause
 	 * subsequent update to be invalid during any MemClk frequency change:
@@ -3778,45 +5911,86 @@ void mct_SetDramConfigHi_D(struct DCTStatStruc *pDCTstat, u32 dct,
 	 */
 
 	u32 dev = pDCTstat->dev_dct;
-	u32 index_reg = 0x98 + 0x100 * dct;
+	u32 index_reg = 0x98;
 	u32 index;
 
-	u32 val;
+	uint32_t dword;
+
+	if (is_fam15h()) {
+		/* Initial setup for frequency change
+		 * 9C_x0000_0004 must be configured before MemClkFreqVal is set
+		 */
 
-	index = 0x08;
-	val = Get_NB32_index_wait(dev, index_reg, index);
-	if (!(val & (1 << DisAutoComp)))
-		Set_NB32_index_wait(dev, index_reg, index, val | (1 << DisAutoComp));
+		/* Program D18F2x9C_x0D0F_E006_dct[1:0][PllLockTime] = 0x190 */
+		dword = Get_NB32_index_wait_DCT(pDCTstat->dev_dct, dct, index_reg, 0x0d0fe006);
+		dword &= ~(0x0000ffff);
+		dword |= 0x00000190;
+		Set_NB32_index_wait_DCT(pDCTstat->dev_dct, dct, index_reg, 0x0d0fe006, dword);
 
-	mct_Wait(100);
+		dword = Get_NB32_DCT(dev, dct, 0x94);
+		dword &= ~(1 << MemClkFreqVal);
+		Set_NB32_DCT(dev, dct, 0x94, dword);
 
-	Set_NB32(dev, 0x94 + 0x100 * dct, DramConfigHi);
+		dword = DramConfigHi;
+		dword &= ~(1 << MemClkFreqVal);
+		Set_NB32_DCT(dev, dct, 0x94, dword);
+
+		mctGet_PS_Cfg_D(pMCTstat, pDCTstat, dct);
+		set_2t_configuration(pMCTstat, pDCTstat, dct);
+		mct_BeforePlatformSpec(pMCTstat, pDCTstat, dct);
+		mct_PlatformSpec(pMCTstat, pDCTstat, dct);
+	} else {
+		index = 0x08;
+		dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, index);
+		if (!(dword & (1 << DisAutoComp)))
+			Set_NB32_index_wait_DCT(dev, dct, index_reg, index, dword | (1 << DisAutoComp));
+
+		mct_Wait(100);
+	}
+
+	/* Program the DRAM Configuration High register */
+	Set_NB32_DCT(dev, dct, 0x94, DramConfigHi);
+
+	if (is_fam15h()) {
+		/* Wait until F2x[1, 0]94[FreqChgInProg]=0. */
+		do {
+			dword = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0x94);
+		} while (dword & (1 << FreqChgInProg));
+
+		/* Program D18F2x9C_x0D0F_E006_dct[1:0][PllLockTime] = 0xf */
+		dword = Get_NB32_index_wait_DCT(pDCTstat->dev_dct, dct, index_reg, 0x0d0fe006);
+		dword &= ~(0x0000ffff);
+		dword |= 0x0000000f;
+		Set_NB32_index_wait_DCT(pDCTstat->dev_dct, dct, index_reg, 0x0d0fe006, dword);
+	}
 }
 
 static void mct_BeforeDQSTrain_D(struct MCTStatStruc *pMCTstat,
 					struct DCTStatStruc *pDCTstatA)
 {
-	u8 Node;
-	struct DCTStatStruc *pDCTstat;
+	if (!is_fam15h()) {
+		u8 Node;
+		struct DCTStatStruc *pDCTstat;
 
-	/* Errata 178
-	 *
-	 * Bug#15115: Uncertainty In The Sync Chain Leads To Setup Violations
-	 *            In TX FIFO
-	 * Solution: BIOS should program DRAM Control Register[RdPtrInit] =
-	 *            5h, (F2x[1, 0]78[3:0] = 5h).
-	 * Silicon Status: Fixed In Rev B0
-	 *
-	 * Bug#15880: Determine validity of reset settings for DDR PHY timing.
-	 * Solution: At least, set WrDqs fine delay to be 0 for DDR3 training.
-	 */
-	for (Node = 0; Node < 8; Node++) {
-		pDCTstat = pDCTstatA + Node;
+		/* Errata 178
+		 *
+		 * Bug#15115: Uncertainty In The Sync Chain Leads To Setup Violations
+		 *            In TX FIFO
+		 * Solution: BIOS should program DRAM Control Register[RdPtrInit] =
+		 *            5h, (F2x[1, 0]78[3:0] = 5h).
+		 * Silicon Status: Fixed In Rev B0
+		 *
+		 * Bug#15880: Determine validity of reset settings for DDR PHY timing.
+		 * Solution: At least, set WrDqs fine delay to be 0 for DDR3 training.
+		 */
+		for (Node = 0; Node < 8; Node++) {
+			pDCTstat = pDCTstatA + Node;
 
-		if (pDCTstat->NodePresent) {
-			mct_BeforeDQSTrainSamp(pDCTstat); /* only Bx */
-			mct_ResetDLL_D(pMCTstat, pDCTstat, 0);
-			mct_ResetDLL_D(pMCTstat, pDCTstat, 1);
+			if (pDCTstat->NodePresent) {
+				mct_BeforeDQSTrainSamp(pDCTstat); /* only Bx */
+				mct_ResetDLL_D(pMCTstat, pDCTstat, 0);
+				mct_ResetDLL_D(pMCTstat, pDCTstat, 1);
+			}
 		}
 	}
 }
@@ -3827,7 +6001,6 @@ static void mct_ResetDLL_D(struct MCTStatStruc *pMCTstat,
 {
 	u8 Receiver;
 	u32 dev = pDCTstat->dev_dct;
-	u32 reg_off = 0x100 * dct;
 	u32 addr;
 	u32 lo, hi;
 	u8 wrap32dis = 0;
@@ -3838,6 +6011,11 @@ static void mct_ResetDLL_D(struct MCTStatStruc *pMCTstat,
 		return;
 	}
 
+	/* Skip reset DLL for Family 15h */
+	if (is_fam15h()) {
+		return;
+	}
+
 	addr = HWCR;
 	_RDMSR(addr, &lo, &hi);
 	if(lo & (1<<17)) {		/* save the old value */
@@ -3857,11 +6035,11 @@ static void mct_ResetDLL_D(struct MCTStatStruc *pMCTstat,
 				mct_Read1LTestPattern_D(pMCTstat, pDCTstat, addr);	/* cache fills */
 
 				/* Write 0000_8000h to register F2x[1,0]9C_xD080F0C */
-				Set_NB32_index_wait(dev, 0x98 + reg_off, 0xD080F0C, 0x00008000);
+				Set_NB32_index_wait_DCT(dev, dct, 0x98, 0xD080F0C, 0x00008000);
 				mct_Wait(80); /* wait >= 300ns */
 
 				/* Write 0000_0000h to register F2x[1,0]9C_xD080F0C */
-				Set_NB32_index_wait(dev, 0x98 + reg_off, 0xD080F0C, 0x00000000);
+				Set_NB32_index_wait_DCT(dev, dct, 0x98, 0xD080F0C, 0x00000000);
 				mct_Wait(800); /* wait >= 2us */
 				break;
 			}
@@ -3901,39 +6079,39 @@ static void mct_EnableDatIntlv_D(struct MCTStatStruc *pMCTstat,
 static void SetDllSpeedUp_D(struct MCTStatStruc *pMCTstat,
 				struct DCTStatStruc *pDCTstat, u8 dct)
 {
-	u32 val;
-	u32 dev = pDCTstat->dev_dct;
-	u32 reg_off = 0x100 * dct;
-
-	if (pDCTstat->Speed >= 7) { /* DDR1600 and above */
-		/* Set bit13 PowerDown to register F2x[1, 0]98_x0D080F10 */
-		Set_NB32(dev, reg_off + 0x98, 0x0D080F10);
-		val = Get_NB32(dev, reg_off + 0x9C);
-		val |= 1 < 13;
-		Set_NB32(dev, reg_off + 0x9C, val);
-		Set_NB32(dev, reg_off + 0x98, 0x4D080F10);
-
-		/* Set bit13 PowerDown to register F2x[1, 0]98_x0D080F11 */
-		Set_NB32(dev, reg_off + 0x98, 0x0D080F11);
-		val = Get_NB32(dev, reg_off + 0x9C);
-		val |= 1 < 13;
-		Set_NB32(dev, reg_off + 0x9C, val);
-		Set_NB32(dev, reg_off + 0x98, 0x4D080F11);
-
-		/* Set bit13 PowerDown to register F2x[1, 0]98_x0D088F30 */
-		Set_NB32(dev, reg_off + 0x98, 0x0D088F30);
-		val = Get_NB32(dev, reg_off + 0x9C);
-		val |= 1 < 13;
-		Set_NB32(dev, reg_off + 0x9C, val);
-		Set_NB32(dev, reg_off + 0x98, 0x4D088F30);
-
-		/* Set bit13 PowerDown to register F2x[1, 0]98_x0D08CF30 */
-		Set_NB32(dev, reg_off + 0x98, 0x0D08CF30);
-		val = Get_NB32(dev, reg_off + 0x9C);
-		val |= 1 < 13;
-		Set_NB32(dev, reg_off + 0x9C, val);
-		Set_NB32(dev, reg_off + 0x98, 0x4D08CF30);
-
+	if (!is_fam15h()) {
+		u32 val;
+		u32 dev = pDCTstat->dev_dct;
+
+		if (pDCTstat->Speed >= mhz_to_memclk_config(800)) { /* DDR1600 and above */
+			/* Set bit13 PowerDown to register F2x[1, 0]98_x0D080F10 */
+			Set_NB32_DCT(dev, dct, 0x98, 0x0D080F10);
+			val = Get_NB32_DCT(dev, dct, 0x9C);
+			val |= 1 < 13;
+			Set_NB32_DCT(dev, dct, 0x9C, val);
+			Set_NB32_DCT(dev, dct, 0x98, 0x4D080F10);
+
+			/* Set bit13 PowerDown to register F2x[1, 0]98_x0D080F11 */
+			Set_NB32_DCT(dev, dct, 0x98, 0x0D080F11);
+			val = Get_NB32_DCT(dev, dct, 0x9C);
+			val |= 1 < 13;
+			Set_NB32_DCT(dev, dct, 0x9C, val);
+			Set_NB32_DCT(dev, dct, 0x98, 0x4D080F11);
+
+			/* Set bit13 PowerDown to register F2x[1, 0]98_x0D088F30 */
+			Set_NB32_DCT(dev, dct, 0x98, 0x0D088F30);
+			val = Get_NB32_DCT(dev, dct, 0x9C);
+			val |= 1 < 13;
+			Set_NB32_DCT(dev, dct, 0x9C, val);
+			Set_NB32_DCT(dev, dct, 0x98, 0x4D088F30);
+
+			/* Set bit13 PowerDown to register F2x[1, 0]98_x0D08CF30 */
+			Set_NB32_DCT(dev, dct, 0x98, 0x0D08CF30);
+			val = Get_NB32_DCT(dev, dct, 0x9C);
+			val |= 1 < 13;
+			Set_NB32_DCT(dev, dct, 0x9C, val);
+			Set_NB32_DCT(dev, dct, 0x98, 0x4D08CF30);
+		}
 	}
 }
 
@@ -3961,7 +6139,6 @@ static void SyncSetting(struct DCTStatStruc *pDCTstat)
 static void AfterDramInit_D(struct DCTStatStruc *pDCTstat, u8 dct) {
 
 	u32 val;
-	u32 reg_off = 0x100 * dct;
 	u32 dev = pDCTstat->dev_dct;
 
 	if (pDCTstat->LogicalCPUID & (AMD_DR_B2 | AMD_DR_B3)) {
@@ -3969,16 +6146,16 @@ static void AfterDramInit_D(struct DCTStatStruc *pDCTstat, u8 dct) {
 		val = Get_NB32(dev, 0x110);
 		if (!(val & (1 << DramEnabled))) {
 			/* If 50 us expires while DramEnable =0 then do the following */
-			val = Get_NB32(dev, 0x90 + reg_off);
+			val = Get_NB32_DCT(dev, dct, 0x90);
 			val &= ~(1 << Width128);		/* Program Width128 = 0 */
-			Set_NB32(dev, 0x90 + reg_off, val);
+			Set_NB32_DCT(dev, dct, 0x90, val);
 
-			val = Get_NB32_index_wait(dev, 0x98 + reg_off, 0x05);	/* Perform dummy CSR read to F2x09C_x05 */
+			val = Get_NB32_index_wait_DCT(dev, dct, 0x98, 0x05);	/* Perform dummy CSR read to F2x09C_x05 */
 
 			if (pDCTstat->GangedMode) {
-				val = Get_NB32(dev, 0x90 + reg_off);
+				val = Get_NB32_DCT(dev, dct, 0x90);
 				val |= 1 << Width128;		/* Program Width128 = 0 */
-				Set_NB32(dev, 0x90 + reg_off, val);
+				Set_NB32_DCT(dev, dct, 0x90, val);
 			}
 		}
 	}
diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h
index a947c2d..50fbff7 100644
--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h
+++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h
@@ -76,6 +76,8 @@
 /* #define PA_EXT_DCTADDL (((00 << 3)+5) << 8) */	/*Node x DCT function, Additional Registers PCI Address bits [15:0]*/
 
 #define PA_NBMISC(Node)	((((0x18+Node) << 3)+3) << 12)	/*Node 0 Misc PCI Address bits [15:0]*/
+#define PA_LINK(Node)	((((0x18+Node) << 3)+4) << 12)	/*Node 0 Link Control bits [15:0]*/
+#define PA_NBCTL(Node)	((((0x18+Node) << 3)+5) << 12)	/*Node 0 NB Control PCI Address bits [15:0]*/
 /* #define PA_NBDEVOP	(((00 << 3)+3) << 8) */  /*Node 0 Misc PCI Address bits [15:0]*/
 
 #define DCC_EN		1		/* X:2:0x94[19]*/
@@ -129,7 +131,7 @@
 #define X4Dimm			12	/* func 2, offset 90h, bit 12*/
 #define UnBuffDimm		16	/* func 2, offset 90h, bit 16*/
 #define DimmEcEn		19	/* func 2, offset 90h, bit 19*/
-#define MemClkFreqVal		3	/* func 2, offset 94h, bit 3*/
+#define MemClkFreqVal		((is_fam15h())?7:3)	/* func 2, offset 94h, bit 3 or 7*/
 #define RDqsEn			12	/* func 2, offset 94h, bit 12*/
 #define DisDramInterface	14	/* func 2, offset 94h, bit 14*/
 #define PowerDownEn      	15	/* func 2, offset 94h, bit 15*/
@@ -204,6 +206,7 @@
 	#define JED_PROBEMSK	0x40	/*Analysis Probe installed*/
 	#define JED_RDIMM	0x1	/* RDIMM */
 	#define JED_MiniRDIMM	0x5	/* Mini-RDIMM */
+	#define JED_LRDIMM	0xb	/* Load-reduced DIMM */
 #define SPD_Density	4		/* Bank address bits,SDRAM capacity */
 #define SPD_Addressing	5		/* Row/Column address bits */
 #define SPD_Voltage	6		/* Supported voltage bitfield */
@@ -297,6 +300,7 @@ struct MCTStatStruc {
 				      of sub 4GB dram hole for HW remapping.*/
 	u32 Sub4GCacheTop;	/* If not zero, the 32-bit top of cacheable memory.*/
 	u32 SysLimit;		/* LIMIT[39:8] (system address)*/
+	uint32_t TSCFreq;
 } __attribute__((packed));
 
 /*=============================================================================
@@ -320,7 +324,8 @@ struct MCTStatStruc {
 
 struct DCTStatStruc {		/* A per Node structure*/
 /* DCTStatStruct_F -  start */
-	u8 Node_ID;		/* Node ID of current controller*/
+	u8 Node_ID;		/* Node ID of current controller */
+	uint8_t stopDCT;	/* Set if the DCT will be stopped */
 	u8 ErrCode;		/* Current error condition of Node
 		0= no error
 		1= Variance Error, DCT is running but not in an optimal configuration.
@@ -464,7 +469,7 @@ struct DCTStatStruc {		/* A per Node structure*/
 		/* CH A byte lane 0 - 7 maximum filtered window  passing DQS delay value*/
 		/* CH B byte lane 0 - 7 minimum filtered window  passing DQS delay value*/
 		/* CH B byte lane 0 - 7 maximum filtered window  passing DQS delay value*/
-	u32 LogicalCPUID;	/* The logical CPUID of the node*/
+	uint64_t LogicalCPUID;	/* The logical CPUID of the node*/
 	u16 HostBiosSrvc1;	/* Word sized general purpose field for use by host BIOS.  Scratch space.*/
 	u32 HostBiosSrvc2;	/* Dword sized general purpose field for use by host BIOS.  Scratch space.*/
 	u16 DimmQRPresent;	/* QuadRank DIMM present?*/
@@ -558,12 +563,20 @@ struct DCTStatStruc {		/* A per Node structure*/
 	u8 ClToNB_flag;	/* is used to restore ClLinesToNbDis bit after memory */
 	u32 NodeSysBase;	/* for channel interleave usage */
 
+	/* Fam15h specific backup variables */
+	uint8_t SwNbPstateLoDis;
+	uint8_t NbPstateDisOnP0;
+	uint8_t NbPstateThreshold;
+	uint8_t NbPstateHi;
+
 /* New for LB Support */
 	u8 NodePresent;
 	u32 dev_host;
 	u32 dev_map;
 	u32 dev_dct;
 	u32 dev_nbmisc;
+	u32 dev_link;
+	u32 dev_nbctl;
 	u8 TargetFreq;
 	u8 TargetCASL;
 	u8 CtrlWrd3;
@@ -596,9 +609,10 @@ struct DCTStatStruc {		/* A per Node structure*/
 	uint8_t DimmBanks[MAX_DIMMS_SUPPORTED];
 	uint8_t DimmWidth[MAX_DIMMS_SUPPORTED];
 	uint8_t DimmRegistered[MAX_DIMMS_SUPPORTED];
+	uint8_t DimmLoadReduced[MAX_DIMMS_SUPPORTED];
 
 	uint64_t DimmManufacturerID[MAX_DIMMS_SUPPORTED];
-	char DimmPartNumber[MAX_DIMMS_SUPPORTED][SPD_PARTN_LENGTH];
+	char DimmPartNumber[MAX_DIMMS_SUPPORTED][SPD_PARTN_LENGTH+1];
 	uint16_t DimmRevisionNumber[MAX_DIMMS_SUPPORTED];
 	uint32_t DimmSerialNumber[MAX_DIMMS_SUPPORTED];
 } __attribute__((packed));
@@ -701,7 +715,64 @@ struct amd_s3_persistent_mct_channel_data {
 	/* Other (1 dword) */
 	uint32_t f3x58;
 
-	/* TOTAL: 250 dwords */
+	/* Family 15h-specific registers (90 dwords) */
+	uint32_t f2x200;
+	uint32_t f2x204;
+	uint32_t f2x208;
+	uint32_t f2x20c;
+	uint32_t f2x210[4];			/* [nb pstate] */
+	uint32_t f2x214;
+	uint32_t f2x218;
+	uint32_t f2x21c;
+	uint32_t f2x22c;
+	uint32_t f2x230;
+	uint32_t f2x234;
+	uint32_t f2x238;
+	uint32_t f2x23c;
+	uint32_t f2x240;
+	uint32_t f2x9cx0d0fe003;
+	uint32_t f2x9cx0d0fe013;
+	uint32_t f2x9cx0d0f0_8_0_1f[9];		/* [lane]*/
+	uint32_t f2x9cx0d0f201f;
+	uint32_t f2x9cx0d0f211f;
+	uint32_t f2x9cx0d0f221f;
+	uint32_t f2x9cx0d0f801f;
+	uint32_t f2x9cx0d0f811f;
+	uint32_t f2x9cx0d0f821f;
+	uint32_t f2x9cx0d0fc01f;
+	uint32_t f2x9cx0d0fc11f;
+	uint32_t f2x9cx0d0fc21f;
+	uint32_t f2x9cx0d0f4009;
+	uint32_t f2x9cx0d0f0_8_0_02[9];		/* [lane]*/
+	uint32_t f2x9cx0d0f0_8_0_06[9];		/* [lane]*/
+	uint32_t f2x9cx0d0f0_8_0_0a[9];		/* [lane]*/
+	uint32_t f2x9cx0d0f2002;
+	uint32_t f2x9cx0d0f2102;
+	uint32_t f2x9cx0d0f2202;
+	uint32_t f2x9cx0d0f8002;
+	uint32_t f2x9cx0d0f8006;
+	uint32_t f2x9cx0d0f800a;
+	uint32_t f2x9cx0d0f8102;
+	uint32_t f2x9cx0d0f8106;
+	uint32_t f2x9cx0d0f810a;
+	uint32_t f2x9cx0d0fc002;
+	uint32_t f2x9cx0d0fc006;
+	uint32_t f2x9cx0d0fc00a;
+	uint32_t f2x9cx0d0fc00e;
+	uint32_t f2x9cx0d0fc012;
+	uint32_t f2x9cx0d0f2031;
+	uint32_t f2x9cx0d0f2131;
+	uint32_t f2x9cx0d0f2231;
+	uint32_t f2x9cx0d0f8031;
+	uint32_t f2x9cx0d0f8131;
+	uint32_t f2x9cx0d0f8231;
+	uint32_t f2x9cx0d0fc031;
+	uint32_t f2x9cx0d0fc131;
+	uint32_t f2x9cx0d0fc231;
+	uint32_t f2x9cx0d0f0_0_f_31[9];		/* [lane] */
+	uint32_t f2x9cx0d0f8021;
+
+	/* TOTAL: 340 dwords */
 } __attribute__((packed));
 
 struct amd_s3_persistent_node_data {
@@ -746,18 +817,19 @@ struct amd_s3_persistent_data {
 	Local Configuration Status (DCTStatStruc.Status[31:0])
 ===============================================================================*/
 #define SB_Registered		0	/* All DIMMs are Registered*/
-#define SB_ECCDIMMs		1	/* All banks ECC capable*/
-#define SB_PARDIMMs		2	/* All banks Addr/CMD Parity capable*/
-#define SB_DiagClks		3	/* Jedec ALL slots clock enable diag mode*/
-#define SB_128bitmode		4	/* DCT in 128-bit mode operation*/
-#define SB_64MuxedMode		5	/* DCT in 64-bit mux'ed mode.*/
-#define SB_2TMode		6	/* 2T CMD timing mode is enabled.*/
-#define SB_SWNodeHole		7	/* Remapping of Node Base on this Node to create a gap.*/
-#define SB_HWHole		8	/* Memory Hole created on this Node using HW remapping.*/
-#define SB_Over400MHz		9	/* DCT freq >= 400MHz flag*/
-#define SB_DQSPos_Pass2	10	/* Using for TrainDQSPos DIMM0/1, when freq>=400MHz*/
-#define SB_DQSRcvLimit		11	/* Using for DQSRcvEnTrain to know we have reached to upper bound.*/
-#define SB_ExtConfig		12	/* Indicator the default setting for extend PCI configuration support*/
+#define SB_LoadReduced		1	/* All DIMMs are Load-Reduced*/
+#define SB_ECCDIMMs		2	/* All banks ECC capable*/
+#define SB_PARDIMMs		3	/* All banks Addr/CMD Parity capable*/
+#define SB_DiagClks		4	/* Jedec ALL slots clock enable diag mode*/
+#define SB_128bitmode		5	/* DCT in 128-bit mode operation*/
+#define SB_64MuxedMode		6	/* DCT in 64-bit mux'ed mode.*/
+#define SB_2TMode		7	/* 2T CMD timing mode is enabled.*/
+#define SB_SWNodeHole		8	/* Remapping of Node Base on this Node to create a gap.*/
+#define SB_HWHole		9	/* Memory Hole created on this Node using HW remapping.*/
+#define SB_Over400MHz		10	/* DCT freq >= 400MHz flag*/
+#define SB_DQSPos_Pass2		11	/* Using for TrainDQSPos DIMM0/1, when freq>=400MHz*/
+#define SB_DQSRcvLimit		12	/* Using for DQSRcvEnTrain to know we have reached to upper bound.*/
+#define SB_ExtConfig		13	/* Indicator the default setting for extend PCI configuration support*/
 
 
 /*===============================================================================
@@ -775,17 +847,18 @@ struct amd_s3_persistent_data {
 					    266=266MHz (DDR533)
 					    333=333MHz (DDR667)
 					    400=400MHz (DDR800)*/
-#define NV_ECC_CAP		4	/* Bus ECC capable (1-bits)
+#define NV_MIN_MEMCLK		4	/* Minimum platform demonstrated Memclock (10-bits) */
+#define NV_ECC_CAP		5	/* Bus ECC capable (1-bits)
 					    0=Platform not capable
 					    1=Platform is capable*/
-#define NV_4RANKType		5	/* Quad Rank DIMM slot type (2-bits)
+#define NV_4RANKType		6	/* Quad Rank DIMM slot type (2-bits)
 					    0=Normal
 					    1=R4 (4-Rank Registered DIMMs in AMD server configuration)
 					    2=S4 (Unbuffered SO-DIMMs)*/
-#define NV_BYPMAX		6	/* Value to set DcqBypassMax field (See Function 2, Offset 94h, [27:24] of BKDG for field definition).
+#define NV_BYPMAX		7	/* Value to set DcqBypassMax field (See Function 2, Offset 94h, [27:24] of BKDG for field definition).
 					    4=4 times bypass (normal for non-UMA systems)
 					    7=7 times bypass (normal for UMA systems)*/
-#define NV_RDWRQBYP		7	/* Value to set RdWrQByp field (See Function 2, Offset A0h, [3:2] of BKDG for field definition).
+#define NV_RDWRQBYP		8	/* Value to set RdWrQByp field (See Function 2, Offset A0h, [3:2] of BKDG for field definition).
 					    2=8 times (normal for non-UMA systems)
 					    3=16 times (normal for UMA systems)*/
 
@@ -848,8 +921,9 @@ struct amd_s3_persistent_data {
 #define NV_ECCRedir		54	/* Dram ECC Redirection enable*/
 #define NV_DramBKScrub		55	/* Dram ECC Background Scrubber CTL*/
 #define NV_L2BKScrub		56	/* L2 ECC Background Scrubber CTL*/
-#define NV_DCBKScrub		57	/* DCache ECC Background Scrubber CTL*/
-#define NV_CS_SpareCTL		58	/* Chip Select Spare Control bit 0:
+#define NV_L3BKScrub		57	/* L3 ECC Background Scrubber CTL*/
+#define NV_DCBKScrub		58	/* DCache ECC Background Scrubber CTL*/
+#define NV_CS_SpareCTL		59	/* Chip Select Spare Control bit 0:
 					       0=disable Spare
 					       1=enable Spare */
 					/* Chip Select Spare Control bit 1-4:
@@ -900,10 +974,12 @@ void mct_SetRcvrEnDly_D(struct DCTStatStruc *pDCTstat, u16 RcvrEnDly, u8 FinalVa
 void SetEccDQSRcvrEn_D(struct DCTStatStruc *pDCTstat, u8 Channel);
 void mctGet_PS_Cfg_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, u32 dct);
 void InterleaveBanks_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, u8 dct);
-void mct_SetDramConfigHi_D(struct DCTStatStruc *pDCTstat, u32 dct, u32 DramConfigHi);
+void mct_SetDramConfigHi_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, u32 dct, u32 DramConfigHi);
 void mct_DramInit_Hw_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, u8 dct);
 void mct_SetClToNB_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat);
 void mct_SetWbEnhWsbDis_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat);
+void mct_ForceNBPState0_En_Fam15(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat);
+void mct_ForceNBPState0_Dis_Fam15(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat);
 void mct_TrainRcvrEn_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, u8 Pass);
 void mct_EnableDimmEccEn_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, u8 _DisableDramECC);
 u32 procOdtWorkaround(struct DCTStatStruc *pDCTstat, u32 dct, u32 val);
diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d_gcc.h b/src/northbridge/amd/amdmct/mct_ddr3/mct_d_gcc.h
index c40ea1a..f6aa755 100644
--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d_gcc.h
+++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d_gcc.h
@@ -98,6 +98,15 @@ static u32 bsf(u32 x)
 
 u32 SetUpperFSbase(u32 addr_hi);
 
+static void proc_MFENCE(void)
+{
+	__asm__ volatile (
+		"outb %%al, $0xed\n\t"  /* _EXECFENCE */
+		"mfence\n\t"
+		:::"memory"
+	);
+}
+
 static void proc_CLFLUSH(u32 addr_hi)
 {
 	SetUpperFSbase(addr_hi);
diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctardk5.c b/src/northbridge/amd/amdmct/mct_ddr3/mctardk5.c
index 126642b..3df262b 100644
--- a/src/northbridge/amd/amdmct/mct_ddr3/mctardk5.c
+++ b/src/northbridge/amd/amdmct/mct_ddr3/mctardk5.c
@@ -2,6 +2,7 @@
  * This file is part of the coreboot project.
  *
  * Copyright (C) 2010 Advanced Micro Devices, Inc.
+ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -17,6 +18,8 @@
  * Foundation, Inc.
  */
 
+/* AM3/ASB2/C32/G34 DDR3 */
+
 static void Get_ChannelPS_Cfg0_D(u8 MAAdimms, u8 Speed, u8 MAAload,
 				u32 *AddrTmgCTL, u32 *ODC_CTL,
 				u8 *CMDmode);
@@ -24,17 +27,23 @@ static void Get_ChannelPS_Cfg0_D(u8 MAAdimms, u8 Speed, u8 MAAload,
 void mctGet_PS_Cfg_D(struct MCTStatStruc *pMCTstat,
 			 struct DCTStatStruc *pDCTstat, u32 dct)
 {
-	Get_ChannelPS_Cfg0_D(pDCTstat->MAdimms[dct], pDCTstat->Speed,
-				pDCTstat->MAload[dct],
-				&(pDCTstat->CH_ADDR_TMG[dct]), &(pDCTstat->CH_ODC_CTL[dct]),
-				&pDCTstat->_2Tmode);
+	if (is_fam15h()) {
+		pDCTstat->CH_ADDR_TMG[dct] = fam15h_address_timing_compensation_code(pDCTstat, dct);
+		pDCTstat->CH_ODC_CTL[dct] = fam15h_output_driver_compensation_code(pDCTstat, dct);
+		pDCTstat->_2Tmode = fam15h_slow_access_mode(pDCTstat, dct);
+	} else {
+		Get_ChannelPS_Cfg0_D(pDCTstat->MAdimms[dct], pDCTstat->Speed,
+					pDCTstat->MAload[dct],
+					&(pDCTstat->CH_ADDR_TMG[dct]), &(pDCTstat->CH_ODC_CTL[dct]),
+					&pDCTstat->_2Tmode);
+
+		pDCTstat->CH_ODC_CTL[dct] |= 0x20000000;	/* 60ohms */
+	}
 
 	pDCTstat->CH_EccDQSLike[0]  = 0x0403;
 	pDCTstat->CH_EccDQSScale[0] = 0x70;
 	pDCTstat->CH_EccDQSLike[1]  = 0x0403;
 	pDCTstat->CH_EccDQSScale[1] = 0x70;
-
-	pDCTstat->CH_ODC_CTL[dct] |= 0x20000000;	/* 60ohms */
 }
 
 /*
diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctcsi_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mctcsi_d.c
index f1fd7a5..a1cdfa6 100644
--- a/src/northbridge/amd/amdmct/mct_ddr3/mctcsi_d.c
+++ b/src/northbridge/amd/amdmct/mct_ddr3/mctcsi_d.c
@@ -2,6 +2,7 @@
  * This file is part of the coreboot project.
  *
  * Copyright (C) 2010 Advanced Micro Devices, Inc.
+ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -35,7 +36,6 @@ void InterleaveBanks_D(struct MCTStatStruc *pMCTstat,
 
 	u32 dev;
 	u32 reg;
-	u32 reg_off;
 	u32 val;
 	u32 val_lo, val_hi;
 
@@ -44,16 +44,15 @@ void InterleaveBanks_D(struct MCTStatStruc *pMCTstat,
 	EnChipSels = 0;
 
 	dev = pDCTstat->dev_dct;
-	reg_off = 0x100 * dct;
 
 	ChipSel = 0;		/* Find out if current configuration is capable */
 	while (DoIntlv && (ChipSel < MAX_CS_SUPPORTED)) {
-		reg = 0x40+(ChipSel<<2) + reg_off;	/* Dram CS Base 0 */
-		val = Get_NB32(dev, reg);
+		reg = 0x40+(ChipSel<<2);	/* Dram CS Base 0 */
+		val = Get_NB32_DCT(dev, dct, reg);
 		if ( val & (1<<CSEnable)) {
 			EnChipSels++;
-			reg = 0x60+((ChipSel>>1)<<2)+reg_off; /*Dram CS Mask 0 */
-			val = Get_NB32(dev, reg);
+			reg = 0x60+((ChipSel>>1)<<2); /*Dram CS Mask 0 */
+			val = Get_NB32_DCT(dev, dct, reg);
 			val >>= 19;
 			val &= 0x3ff;
 			val++;
@@ -63,8 +62,8 @@ void InterleaveBanks_D(struct MCTStatStruc *pMCTstat,
 				/*If mask sizes not same then skip */
 				if (val != MemSize)
 					break;
-			reg = 0x80 + reg_off;		/*Dram Bank Addressing */
-			val = Get_NB32(dev, reg);
+			reg = 0x80;		/*Dram Bank Addressing */
+			val = Get_NB32_DCT(dev, dct, reg);
 			val >>= (ChipSel>>1)<<2;
 			val &= 0x0f;
 			if(EnChipSels == 1)
@@ -103,8 +102,8 @@ void InterleaveBanks_D(struct MCTStatStruc *pMCTstat,
 		BitDelta = bsf(AddrHiMask) - bsf(AddrLoMask);
 
 		for (ChipSel = 0; ChipSel < MAX_CS_SUPPORTED; ChipSel++) {
-			reg = 0x40+(ChipSel<<2) + reg_off;	/*Dram CS Base 0 */
-			val = Get_NB32(dev, reg);
+			reg = 0x40+(ChipSel<<2);	/*Dram CS Base 0 */
+			val = Get_NB32_DCT(dev, dct, reg);
 			if (val & 3) {
 				val_lo = val & AddrLoMask;
 				val_hi = val & AddrHiMask;
@@ -114,13 +113,13 @@ void InterleaveBanks_D(struct MCTStatStruc *pMCTstat,
 				val_hi >>= BitDelta;
 				val |= val_lo;
 				val |= val_hi;
-				Set_NB32(dev, reg, val);
+				Set_NB32_DCT(dev, dct, reg, val);
 
 				if(ChipSel & 1)
 					continue;
 
-				reg = 0x60 + ((ChipSel>>1)<<2) + reg_off; /*Dram CS Mask 0 */
-				val = Get_NB32(dev, reg);
+				reg = 0x60 + ((ChipSel>>1)<<2); /*Dram CS Mask 0 */
+				val = Get_NB32_DCT(dev, dct, reg);
 				val_lo = val & AddrLoMask;
 				val_hi = val & AddrHiMask;
 				val &= AddrLoMaskN;
@@ -129,7 +128,7 @@ void InterleaveBanks_D(struct MCTStatStruc *pMCTstat,
 				val_hi >>= BitDelta;
 				val |= val_lo;
 				val |= val_hi;
-				Set_NB32(dev, reg, val);
+				Set_NB32_DCT(dev, dct, reg, val);
 			}
 		}
 	}	/* DoIntlv */
diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c
index cc2f43a..740edae 100644
--- a/src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c
+++ b/src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c
@@ -18,6 +18,12 @@
  * Foundation, Inc.
  */
 
+static void write_dqs_receiver_enable_control_registers(uint16_t* current_total_delay,
+			uint32_t dev, uint8_t dct, uint8_t dimm, uint32_t index_reg);
+
+static void read_read_dqs_timing_control_registers(uint16_t* current_total_delay,
+			uint32_t dev, uint8_t dct, uint8_t dimm, uint32_t index_reg);
+
 static void CalcEccDQSPos_D(struct MCTStatStruc *pMCTstat,
 				struct DCTStatStruc *pDCTstat, u16 like,
 				u8 scale, u8 ChipSel);
@@ -37,7 +43,7 @@ static void FlushDQSTestPattern_D(struct DCTStatStruc *pDCTstat,
 					u32 addr_lo);
 static void SetTargetWTIO_D(u32 TestAddr);
 static void ResetTargetWTIO_D(void);
-void ResetDCTWrPtr_D(u32 dev, u32 index_reg, u32 index);
+void ResetDCTWrPtr_D(u32 dev, uint8_t dct, u32 index_reg, u32 index);
 u8 mct_DisableDimmEccEn_D(struct MCTStatStruc *pMCTstat,
 				struct DCTStatStruc *pDCTstat);
 static void mct_SetDQSDelayCSR_D(struct MCTStatStruc *pMCTstat,
@@ -54,6 +60,7 @@ static void proc_IOCLFLUSH_D(u32 addr_hi);
 static void StoreDQSDatStrucVal_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, u8 ChipSel);
 
 #define DQS_TRAIN_DEBUG 0
+// #define PRINT_PASS_FAIL_BITMAPS 1
 
 static void print_debug_dqs(const char *str, u32 val, u8 level)
 {
@@ -198,18 +205,20 @@ void TrainReceiverEn_D(struct MCTStatStruc *pMCTstat,
 		pDCTstat = pDCTstatA + Node;
 
 		if (pDCTstat->DCTSysLimit) {
-			val = Get_NB32(pDCTstat->dev_dct, 0x78);
-			val |= 1 <<DqsRcvEnTrain;
-			Set_NB32(pDCTstat->dev_dct, 0x78, val);
-			val = Get_NB32(pDCTstat->dev_dct, 0x78 + 0x100);
-			val |= 1 <<DqsRcvEnTrain;
-			Set_NB32(pDCTstat->dev_dct, 0x78 + 0x100, val);
+			if (!is_fam15h()) {
+				val = Get_NB32_DCT(pDCTstat->dev_dct, 0, 0x78);
+				val |= 1 <<DqsRcvEnTrain;
+				Set_NB32_DCT(pDCTstat->dev_dct, 0, 0x78, val);
+				val = Get_NB32_DCT(pDCTstat->dev_dct, 1, 0x78);
+				val |= 1 <<DqsRcvEnTrain;
+				Set_NB32_DCT(pDCTstat->dev_dct, 1, 0x78, val);
+			}
 			mct_TrainRcvrEn_D(pMCTstat, pDCTstat, Pass);
 		}
 	}
 }
 
-static void SetEccDQSRdWrPos_D(struct MCTStatStruc *pMCTstat,
+static void SetEccDQSRdWrPos_D_Fam10(struct MCTStatStruc *pMCTstat,
 				struct DCTStatStruc *pDCTstat, u8 ChipSel)
 {
 	u8 channel;
@@ -268,68 +277,150 @@ static void CalcEccDQSPos_D(struct MCTStatStruc *pMCTstat,
 	pDCTstat->DQSDelay = (u8)DQSDelay;
 }
 
-static void write_dqs_write_data_timing_registers(uint16_t* delay, uint32_t dev, uint8_t dimm, uint32_t index_reg)
+static void read_dqs_write_data_timing_registers(uint16_t* delay, uint32_t dev, uint8_t dct, uint8_t dimm, uint32_t index_reg)
+{
+	uint32_t dword;
+	uint32_t mask;
+
+	if (is_fam15h())
+		mask = 0xff;
+	else
+		mask = 0x7f;
+
+	/* Lanes 0 - 3 */
+	dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x1 | (dimm << 8));
+	delay[3] = (dword >> 24) & mask;
+	delay[2] = (dword >> 16) & mask;
+	delay[1] = (dword >> 8) & mask;
+	delay[0] = dword & mask;
+
+	/* Lanes 4 - 7 */
+	dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x2 | (dimm << 8));
+	delay[7] = (dword >> 24) & mask;
+	delay[6] = (dword >> 16) & mask;
+	delay[5] = (dword >> 8) & mask;
+	delay[4] = dword & mask;
+
+	/* Lane 8 (ECC) */
+	dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x3 | (dimm << 8));
+	delay[8] = dword & mask;
+}
+
+static void write_dqs_write_data_timing_registers(uint16_t* delay, uint32_t dev, uint8_t dct, uint8_t dimm, uint32_t index_reg)
 {
 	uint32_t dword;
+	uint32_t mask;
+
+	if (is_fam15h())
+		mask = 0xff;
+	else
+		mask = 0x7f;
 
 	/* Lanes 0 - 3 */
-	dword = Get_NB32_index_wait(dev, index_reg, 0x1 | (dimm << 8));
-	dword &= ~0x7f7f7f7f;
-	dword |= (delay[3] & 0x7f) << 24;
-	dword |= (delay[2] & 0x7f) << 16;
-	dword |= (delay[1] & 0x7f) << 8;
-	dword |= delay[0] & 0x7f;
-	Set_NB32_index_wait(dev, index_reg, 0x1 | (dimm << 8), dword);
+	dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x1 | (dimm << 8));
+	dword &= ~(mask << 24);
+	dword &= ~(mask << 16);
+	dword &= ~(mask << 8);
+	dword &= ~mask;
+	dword |= (delay[3] & mask) << 24;
+	dword |= (delay[2] & mask) << 16;
+	dword |= (delay[1] & mask) << 8;
+	dword |= delay[0] & mask;
+	Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x1 | (dimm << 8), dword);
 
 	/* Lanes 4 - 7 */
-	dword = Get_NB32_index_wait(dev, index_reg, 0x2 | (dimm << 8));
-	dword &= ~0x7f7f7f7f;
-	dword |= (delay[7] & 0x7f) << 24;
-	dword |= (delay[6] & 0x7f) << 16;
-	dword |= (delay[5] & 0x7f) << 8;
-	dword |= delay[4] & 0x7f;
-	Set_NB32_index_wait(dev, index_reg, 0x2 | (dimm << 8), dword);
+	dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x2 | (dimm << 8));
+	dword &= ~(mask << 24);
+	dword &= ~(mask << 16);
+	dword &= ~(mask << 8);
+	dword &= ~mask;
+	dword |= (delay[7] & mask) << 24;
+	dword |= (delay[6] & mask) << 16;
+	dword |= (delay[5] & mask) << 8;
+	dword |= delay[4] & mask;
+	Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x2 | (dimm << 8), dword);
 
 	/* Lane 8 (ECC) */
-	dword = Get_NB32_index_wait(dev, index_reg, 0x3 | (dimm << 8));
-	dword &= ~0x0000007f;
-	dword |= delay[8] & 0x7f;
-	Set_NB32_index_wait(dev, index_reg, 0x3 | (dimm << 8), dword);
+	dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x3 | (dimm << 8));
+	dword &= ~mask;
+	dword |= delay[8] & mask;
+	Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x3 | (dimm << 8), dword);
 }
 
-static void write_dqs_read_data_timing_registers(uint16_t* delay, uint32_t dev, uint8_t dimm, uint32_t index_reg)
+static void read_dqs_read_data_timing_registers(uint16_t* delay, uint32_t dev, uint8_t dct, uint8_t dimm, uint32_t index_reg)
 {
 	uint32_t dword;
+	uint32_t mask;
+
+	if (is_fam15h())
+		mask = 0x3e;
+	else
+		mask = 0x3f;
 
 	/* Lanes 0 - 3 */
-	dword = Get_NB32_index_wait(dev, index_reg, 0x5 | (dimm << 8));
-	dword &= ~0x3f3f3f3f;
-	dword |= (delay[3] & 0x3f) << 24;
-	dword |= (delay[2] & 0x3f) << 16;
-	dword |= (delay[1] & 0x3f) << 8;
-	dword |= delay[0] & 0x3f;
-	Set_NB32_index_wait(dev, index_reg, 0x5 | (dimm << 8), dword);
+	dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x5 | (dimm << 8));
+	delay[3] = (dword >> 24) & mask;
+	delay[2] = (dword >> 16) & mask;
+	delay[1] = (dword >> 8) & mask;
+	delay[0] = dword & mask;
 
 	/* Lanes 4 - 7 */
-	dword = Get_NB32_index_wait(dev, index_reg, 0x6 | (dimm << 8));
-	dword &= ~0x3f3f3f3f;
-	dword |= (delay[7] & 0x3f) << 24;
-	dword |= (delay[6] & 0x3f) << 16;
-	dword |= (delay[5] & 0x3f) << 8;
-	dword |= delay[4] & 0x3f;
-	Set_NB32_index_wait(dev, index_reg, 0x6 | (dimm << 8), dword);
+	dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x6 | (dimm << 8));
+	delay[7] = (dword >> 24) & mask;
+	delay[6] = (dword >> 16) & mask;
+	delay[5] = (dword >> 8) & mask;
+	delay[4] = dword & mask;
 
 	/* Lane 8 (ECC) */
-	dword = Get_NB32_index_wait(dev, index_reg, 0x7 | (dimm << 8));
-	dword &= ~0x0000003f;
-	dword |= delay[8] & 0x3f;
-	Set_NB32_index_wait(dev, index_reg, 0x7 | (dimm << 8), dword);
+	dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x7 | (dimm << 8));
+	delay[8] = dword & mask;
+}
+
+static void write_dqs_read_data_timing_registers(uint16_t* delay, uint32_t dev, uint8_t dct, uint8_t dimm, uint32_t index_reg)
+{
+	uint32_t dword;
+	uint32_t mask;
+
+	if (is_fam15h())
+		mask = 0x3e;
+	else
+		mask = 0x3f;
+
+	/* Lanes 0 - 3 */
+	dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x5 | (dimm << 8));
+	dword &= ~(mask << 24);
+	dword &= ~(mask << 16);
+	dword &= ~(mask << 8);
+	dword &= ~mask;
+	dword |= (delay[3] & mask) << 24;
+	dword |= (delay[2] & mask) << 16;
+	dword |= (delay[1] & mask) << 8;
+	dword |= delay[0] & mask;
+	Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x5 | (dimm << 8), dword);
+
+	/* Lanes 4 - 7 */
+	dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x6 | (dimm << 8));
+	dword &= ~(mask << 24);
+	dword &= ~(mask << 16);
+	dword &= ~(mask << 8);
+	dword &= ~mask;
+	dword |= (delay[7] & mask) << 24;
+	dword |= (delay[6] & mask) << 16;
+	dword |= (delay[5] & mask) << 8;
+	dword |= delay[4] & mask;
+	Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x6 | (dimm << 8), dword);
+
+	/* Lane 8 (ECC) */
+	dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x7 | (dimm << 8));
+	dword &= ~mask;
+	dword |= delay[8] & mask;
+	Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x7 | (dimm << 8), dword);
 }
 
 /* DQS Position Training
  * Algorithm detailed in the Fam10h BKDG Rev. 3.62 section 2.8.9.9.3
  */
-static void TrainDQSRdWrPos_D(struct MCTStatStruc *pMCTstat,
+static void TrainDQSRdWrPos_D_Fam10(struct MCTStatStruc *pMCTstat,
 				struct DCTStatStruc *pDCTstat)
 {
 	u32 Errors;
@@ -406,7 +497,7 @@ static void TrainDQSRdWrPos_D(struct MCTStatStruc *pMCTstat,
 		if (pDCTstat->DIMMValidDCT[Channel] == 0)	/* mct_BeforeTrainDQSRdWrPos_D */
 			continue;
 
-		index_reg = 0x98 + 0x100 * Channel;
+		index_reg = 0x98;
 
 		dual_rank = 0;
 		Receiver = mct_InitReceiver_D(pDCTstat, Channel);
@@ -462,7 +553,7 @@ static void TrainDQSRdWrPos_D(struct MCTStatStruc *pMCTstat,
 					break;
 
 				/* Commit the current Write Data Timing settings to the hardware registers */
-				write_dqs_write_data_timing_registers(current_write_dqs_delay, dev, (Receiver >> 1), index_reg);
+				write_dqs_write_data_timing_registers(current_write_dqs_delay, dev, Channel, (Receiver >> 1), index_reg);
 
 				/* Write the DRAM training pattern to the base test address */
 				WriteDQSTestPattern_D(pMCTstat, pDCTstat, TestAddr << 8);
@@ -479,7 +570,7 @@ static void TrainDQSRdWrPos_D(struct MCTStatStruc *pMCTstat,
 							current_read_dqs_delay[lane] = test_read_dqs_delay;
 
 					/* Commit the current Read DQS Timing Control settings to the hardware registers */
-					write_dqs_read_data_timing_registers(current_read_dqs_delay, dev, (Receiver >> 1), index_reg);
+					write_dqs_read_data_timing_registers(current_read_dqs_delay, dev, Channel, (Receiver >> 1), index_reg);
 
 					/* Initialize test result variable */
 					bytelane_test_results = 0xff;
@@ -545,7 +636,7 @@ static void TrainDQSRdWrPos_D(struct MCTStatStruc *pMCTstat,
 						passing_dqs_delay_found[lane] = 1;
 
 						/* Commit the current Read DQS Timing Control settings to the hardware registers */
-						write_dqs_read_data_timing_registers(current_read_dqs_delay, dev, (Receiver >> 1), index_reg);
+						write_dqs_read_data_timing_registers(current_read_dqs_delay, dev, Channel, (Receiver >> 1), index_reg);
 
 						/* Exit the DRAM Write Data Timing Loop */
 						write_dqs_delay_stepping_done[lane] = 1;
@@ -579,7 +670,7 @@ static void TrainDQSRdWrPos_D(struct MCTStatStruc *pMCTstat,
 					current_write_dqs_delay[lane] = test_write_dqs_delay;
 
 				/* Commit the current Write Data Timing settings to the hardware registers */
-				write_dqs_write_data_timing_registers(current_write_dqs_delay, dev, (Receiver >> 1), index_reg);
+				write_dqs_write_data_timing_registers(current_write_dqs_delay, dev, Channel, (Receiver >> 1), index_reg);
 
 				/* Write the DRAM training pattern to the base test address */
 				WriteDQSTestPattern_D(pMCTstat, pDCTstat, TestAddr << 8);
@@ -674,7 +765,7 @@ static void TrainDQSRdWrPos_D(struct MCTStatStruc *pMCTstat,
 						current_read_dqs_delay[lane] = (best_pos + (best_count / 2));
 
 						/* Commit the current Read DQS Timing Control settings to the hardware registers */
-						write_dqs_read_data_timing_registers(current_read_dqs_delay, dev, (Receiver >> 1), index_reg);
+						write_dqs_read_data_timing_registers(current_read_dqs_delay, dev, Channel, (Receiver >> 1), index_reg);
 
 						/* Save the final Read DQS Timing Control settings for later use */
 						pDCTstat->CH_D_DIR_B_DQS[Channel][Receiver >> 1][DQS_READDIR][lane] = current_read_dqs_delay[lane];
@@ -717,7 +808,7 @@ static void TrainDQSRdWrPos_D(struct MCTStatStruc *pMCTstat,
 						current_write_dqs_delay[lane] = (best_pos + (best_count / 2));
 
 						/* Commit the current Write Data Timing settings to the hardware registers */
-						write_dqs_write_data_timing_registers(current_write_dqs_delay, dev, (Receiver >> 1), index_reg);
+						write_dqs_write_data_timing_registers(current_write_dqs_delay, dev, Channel, (Receiver >> 1), index_reg);
 
 						/* Save the final Write Data Timing settings for later use */
 						pDCTstat->CH_D_DIR_B_DQS[Channel][Receiver >> 1][DQS_WRITEDIR][lane] = current_write_dqs_delay[lane];
@@ -787,6 +878,831 @@ static void TrainDQSRdWrPos_D(struct MCTStatStruc *pMCTstat,
 	printk(BIOS_DEBUG, "TrainDQSRdWrPos: Done\n\n");
 }
 
+/* Calcuate and set MaxRdLatency
+ * Algorithm detailed in the Fam15h BKDG Rev. 3.14 section 2.10.5.8.5
+ */
+static void Calc_SetMaxRdLatency_D_Fam15(struct MCTStatStruc *pMCTstat,
+				struct DCTStatStruc *pDCTstat, uint8_t dct)
+{
+	uint8_t dimm;
+	uint8_t lane;
+	uint32_t dword;
+	uint32_t dword2;
+	uint32_t max_delay;
+	uint8_t mem_clk = 0;
+	uint8_t nb_pstate;
+	uint32_t nb_clk;
+	uint32_t p = 0;
+	uint32_t n = 0;
+	uint32_t t = 0;
+	uint16_t current_phy_phase_delay[MAX_BYTE_LANES];
+	uint16_t current_read_dqs_delay[MAX_BYTE_LANES];
+
+	uint32_t index_reg = 0x98;
+	uint32_t dev = pDCTstat->dev_dct;
+	uint16_t fam15h_freq_tab[] = {0, 0, 0, 0, 333, 0, 400, 0, 0, 0, 533, 0, 0, 0, 667, 0, 0, 0, 800, 0, 0, 0, 933};
+
+	/* P is specified in PhyCLKs (1/2 MEMCLKs) */
+	for (nb_pstate = 0; nb_pstate < 2; nb_pstate++) {
+		/* 2.10.5.8.5 (2) */
+		dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000004);
+		if ((!(dword & (0x1 << 21))) && (!(dword & (0x1 << 13))) && (!(dword & (0x1 << 5))))
+			p += 1;
+		else
+			p += 2;
+
+		/* 2.10.5.8.5 (3) */
+		dword = Get_NB32_DCT_NBPstate(dev, dct, nb_pstate, 0x210) & 0xf;	/* Retrieve RdPtrInit */
+		p += (9 - dword);
+
+		/* 2.10.5.8.5 (4) */
+		p += 5;
+
+		/* 2.10.5.8.5 (5) */
+		dword = Get_NB32_DCT(dev, dct, 0xa8);
+		dword2 = Get_NB32_DCT(dev, dct, 0x90);
+		if ((!(dword & (0x1 << 5))) && (!(dword2 & (0x1 << 16))))
+			p += 2;
+
+		/* 2.10.5.8.5 (6) */
+		dword = Get_NB32_DCT(dev, dct, 0x200) & 0x1f;	/* Retrieve Tcl */
+		p += (2 * (dword - 1));
+
+		/* 2.10.5.8.5 (7) */
+		max_delay = 0;
+		for (dimm = 0; dimm < 4; dimm++) {
+			if (!mct_RcvrRankEnabled_D(pMCTstat, pDCTstat, dct, dimm * 2))
+				continue;
+
+			read_dqs_receiver_enable_control_registers(current_phy_phase_delay, dev, dct, dimm, index_reg);
+			read_read_dqs_timing_control_registers(current_read_dqs_delay, dev, dct, dimm, index_reg);
+			for (lane = 0; lane < MAX_BYTE_LANES; lane++)
+				if ((current_phy_phase_delay[lane] + current_read_dqs_delay[lane]) > max_delay)
+					max_delay = (current_phy_phase_delay[lane] + current_read_dqs_delay[lane]);
+		}
+		p += (max_delay >> 5);
+
+		/* 2.10.5.8.5 (8) */
+		p += 5;
+
+		/* 2.10.5.8.5 (9) */
+		t += 800;
+
+		/* 2.10.5.8.5 (10) */
+		mem_clk = Get_NB32_DCT(dev, dct, 0x94) & 0x1f;
+		dword = Get_NB32(pDCTstat->dev_nbctl, (0x160 + (nb_pstate * 4)));		/* Retrieve NbDid, NbFid */
+		nb_clk = (200 * (((dword >> 1) & 0x1f) + 0x4)) / (((dword >> 7) & 0x1)?2:1);
+		n = (((((uint64_t)p * 1000000000000ULL)/(((uint64_t)fam15h_freq_tab[mem_clk] * 1000000ULL) * 2)) + ((uint64_t)t)) * ((uint64_t)nb_clk * 1000)) / 1000000000ULL;
+
+		/* 2.10.5.8.5 (11) */
+		n -= 1;
+
+		/* 2.10.5.8.5 (12) */
+		dword = Get_NB32_DCT_NBPstate(dev, dct, nb_pstate, 0x210);
+		dword &= ~(0x3ff << 22);
+		dword |= (((n - 1) & 0x3ff) << 22);
+		Set_NB32_DCT_NBPstate(dev, dct, nb_pstate, 0x210, dword);
+
+		/* Save result for later use */
+		pDCTstat->CH_MaxRdLat[dct] = n;
+	}
+}
+
+static void start_dram_dqs_training_pattern_fam15(struct MCTStatStruc *pMCTstat,
+				struct DCTStatStruc *pDCTstat, uint8_t dct, uint8_t Receiver)
+{
+	uint32_t dword;
+	uint32_t dev = pDCTstat->dev_dct;
+
+	/* 2.10.5.7.1.1
+	 * It appears that the DCT only supports 8-beat burst length mode,
+	 * so do nothing here...
+	 */
+
+	/* Wait for CmdSendInProg == 0 */
+	do {
+		dword = Get_NB32_DCT(dev, dct, 0x250);
+	} while (dword & (0x1 << 12));
+
+	/* Set CmdTestEnable = 1 */
+	dword = Get_NB32_DCT(dev, dct, 0x250);
+	dword |= (0x1 << 2);
+	Set_NB32_DCT(dev, dct, 0x250, dword);
+
+	/* 2.10.5.8.6.1.1 Send Activate Command (Target A) */
+	dword = Get_NB32_DCT(dev, dct, 0x28c);
+	dword &= ~(0xff << 22);				/* CmdChipSelect = Receiver */
+	dword |= ((0x1 << Receiver) << 22);
+	dword &= ~(0x7 << 19);				/* CmdBank = 0 */
+	dword &= ~(0x3ffff);				/* CmdAddress = 0 */
+	dword |= (0x1 << 31);				/* SendActCmd = 1 */
+	Set_NB32_DCT(dev, dct, 0x28c, dword);
+
+	/* Wait for SendActCmd == 0 */
+	do {
+		dword = Get_NB32_DCT(dev, dct, 0x28c);
+	} while (dword & (0x1 << 31));
+
+	/* Wait 75 MEMCLKs. */
+	precise_memclk_delay_fam15(pMCTstat, pDCTstat, dct, 75);
+
+	/* 2.10.5.8.6.1.1 Send Activate Command (Target B) */
+	dword = Get_NB32_DCT(dev, dct, 0x28c);
+	dword &= ~(0xff << 22);				/* CmdChipSelect = Receiver */
+	dword |= ((0x1 << Receiver) << 22);
+	dword &= ~(0x7 << 19);				/* CmdBank = 1 */
+	dword |= (0x1 << 19);
+	dword &= ~(0x3ffff);				/* CmdAddress = 0 */
+	dword |= (0x1 << 31);				/* SendActCmd = 1 */
+	Set_NB32_DCT(dev, dct, 0x28c, dword);
+
+	/* Wait for SendActCmd == 0 */
+	do {
+		dword = Get_NB32_DCT(dev, dct, 0x28c);
+	} while (dword & (0x1 << 31));
+
+	/* Wait 75 MEMCLKs. */
+	precise_memclk_delay_fam15(pMCTstat, pDCTstat, dct, 75);
+}
+
+static void stop_dram_dqs_training_pattern_fam15(struct MCTStatStruc *pMCTstat,
+				struct DCTStatStruc *pDCTstat, uint8_t dct, uint8_t Receiver)
+{
+	uint32_t dword;
+	uint32_t dev = pDCTstat->dev_dct;
+
+	/* 2.10.5.8.6.1.1 Send Precharge Command */
+	/* Wait 25 MEMCLKs. */
+	precise_memclk_delay_fam15(pMCTstat, pDCTstat, dct, 25);
+
+	dword = Get_NB32_DCT(dev, dct, 0x28c);
+	dword &= ~(0xff << 22);				/* CmdChipSelect = Receiver */
+	dword |= ((0x1 << Receiver) << 22);
+	dword &= ~(0x7 << 19);				/* CmdBank = 0 */
+	dword &= ~(0x3ffff);				/* CmdAddress = 0x400 */
+	dword |= 0x400;
+	dword |= (0x1 << 30);				/* SendPchgCmd = 1 */
+	Set_NB32_DCT(dev, dct, 0x28c, dword);
+
+	/* Wait for SendPchgCmd == 0 */
+	do {
+		dword = Get_NB32_DCT(dev, dct, 0x28c);
+	} while (dword & (0x1 << 30));
+
+	/* Wait 25 MEMCLKs. */
+	precise_memclk_delay_fam15(pMCTstat, pDCTstat, dct, 25);
+
+	/* Set CmdTestEnable = 0 */
+	dword = Get_NB32_DCT(dev, dct, 0x250);
+	dword &= ~(0x1 << 2);
+	Set_NB32_DCT(dev, dct, 0x250, dword);
+}
+
+static void read_dram_dqs_training_pattern_fam15(struct MCTStatStruc *pMCTstat,
+				struct DCTStatStruc *pDCTstat, uint8_t dct, uint8_t Receiver, uint8_t lane)
+{
+	uint32_t dword;
+	uint32_t dev = pDCTstat->dev_dct;
+
+	start_dram_dqs_training_pattern_fam15(pMCTstat, pDCTstat, dct, Receiver);
+
+	/* 2.10.5.8.6.1.2 */
+	/* Configure DQMask */
+	if (lane < 4) {
+		Set_NB32_DCT(dev, dct, 0x274, ~(0xff << (lane * 8)));
+		Set_NB32_DCT(dev, dct, 0x278, ~0x0);
+	} else if (lane < 8) {
+		Set_NB32_DCT(dev, dct, 0x274, ~0x0);
+		Set_NB32_DCT(dev, dct, 0x278, ~(0xff << (lane * 8)));
+	} else {
+		Set_NB32_DCT(dev, dct, 0x274, ~0x0);
+		Set_NB32_DCT(dev, dct, 0x278, ~0x0);
+	}
+
+	dword = Get_NB32_DCT(dev, dct, 0x27c);
+	dword &= ~(0xff);				/* EccMask = 0 */
+	if ((lane != 8) || (pDCTstat->DimmECCPresent == 0))
+		dword |= 0xff;				/* EccMask = 0xff */
+	Set_NB32_DCT(dev, dct, 0x27c, dword);
+
+	dword = Get_NB32_DCT(dev, dct, 0x270);
+	dword &= ~(0x7ffff);				/* DataPrbsSeed = 55555 */
+// 	dword |= (0x55555);
+	dword |= (0x44443);				/* Use AGESA seed */
+	Set_NB32_DCT(dev, dct, 0x270, dword);
+
+	/* 2.10.5.8.4 */
+	dword = Get_NB32_DCT(dev, dct, 0x260);
+	dword &= ~(0x1fffff);				/* CmdCount = 256 */
+	dword |= 256;
+	Set_NB32_DCT(dev, dct, 0x260, dword);
+
+	/* Configure Target A */
+	dword = Get_NB32_DCT(dev, dct, 0x254);
+	dword &= ~(0x7 << 24);				/* TgtChipSelect = Receiver */
+	dword |= (Receiver & 0x7) << 24;
+	dword &= ~(0x7 << 21);				/* TgtBank = 0 */
+	dword &= ~(0x3ff);				/* TgtAddress = 0 */
+	Set_NB32_DCT(dev, dct, 0x254, dword);
+
+	/* Configure Target B */
+	dword = Get_NB32_DCT(dev, dct, 0x258);
+	dword &= ~(0x7 << 24);				/* TgtChipSelect = Receiver */
+	dword |= (Receiver & 0x7) << 24;
+	dword &= ~(0x7 << 21);				/* TgtBank = 1 */
+	dword |= (0x1 << 21);
+	dword &= ~(0x3ff);				/* TgtAddress = 0 */
+	Set_NB32_DCT(dev, dct, 0x258, dword);
+
+	dword = Get_NB32_DCT(dev, dct, 0x250);
+	dword |= (0x1 << 3);				/* ResetAllErr = 1 */
+	dword &= ~(0x1 << 4);				/* StopOnErr = 0 */
+	dword &= ~(0x3 << 8);				/* CmdTgt = 1 (Alternate between Target A and Target B) */
+	dword |= (0x1 << 8);
+	dword &= ~(0x7 << 5);				/* CmdType = 0 (Read) */
+	dword |= (0x1 << 11);				/* SendCmd = 1 */
+	Set_NB32_DCT(dev, dct, 0x250, dword);
+
+	/* 2.10.5.8.6.1.2 Wait for TestStatus == 1 and CmdSendInProg == 0 */
+	do {
+		dword = Get_NB32_DCT(dev, dct, 0x250);
+	} while ((dword & (0x1 << 12)) || (!(dword & (0x1 << 10))));
+
+	dword = Get_NB32_DCT(dev, dct, 0x250);
+	dword &= ~(0x1 << 11);				/* SendCmd = 0 */
+	Set_NB32_DCT(dev, dct, 0x250, dword);
+
+	stop_dram_dqs_training_pattern_fam15(pMCTstat, pDCTstat, dct, Receiver);
+}
+
+static void write_dram_dqs_training_pattern_fam15(struct MCTStatStruc *pMCTstat,
+				struct DCTStatStruc *pDCTstat, uint8_t dct, uint8_t Receiver, uint8_t lane)
+{
+	uint32_t dword;
+	uint32_t dev = pDCTstat->dev_dct;
+
+	start_dram_dqs_training_pattern_fam15(pMCTstat, pDCTstat, dct, Receiver);
+
+	/* 2.10.5.8.6.1.2 */
+	/* Configure DQMask */
+	if (lane < 4) {
+		Set_NB32_DCT(dev, dct, 0x274, ~(0xff << (lane * 8)));
+		Set_NB32_DCT(dev, dct, 0x278, ~0x0);
+	} else if (lane < 8) {
+		Set_NB32_DCT(dev, dct, 0x274, ~0x0);
+		Set_NB32_DCT(dev, dct, 0x278, ~(0xff << (lane * 8)));
+	} else {
+		Set_NB32_DCT(dev, dct, 0x274, ~0x0);
+		Set_NB32_DCT(dev, dct, 0x278, ~0x0);
+	}
+
+	dword = Get_NB32_DCT(dev, dct, 0x27c);
+	dword &= ~(0xff);				/* EccMask = 0 */
+	if ((lane != 8) || (pDCTstat->DimmECCPresent == 0))
+		dword |= 0xff;				/* EccMask = 0xff */
+	Set_NB32_DCT(dev, dct, 0x27c, dword);
+
+	dword = Get_NB32_DCT(dev, dct, 0x270);
+	dword &= ~(0x7ffff);				/* DataPrbsSeed = 55555 */
+// 	dword |= (0x55555);
+	dword |= (0x44443);				/* Use AGESA seed */
+	Set_NB32_DCT(dev, dct, 0x270, dword);
+
+	/* 2.10.5.8.4 */
+	dword = Get_NB32_DCT(dev, dct, 0x260);
+	dword &= ~(0x1fffff);				/* CmdCount = 256 */
+	dword |= 256;
+	Set_NB32_DCT(dev, dct, 0x260, dword);
+
+	/* Configure Target A */
+	dword = Get_NB32_DCT(dev, dct, 0x254);
+	dword &= ~(0x7 << 24);				/* TgtChipSelect = Receiver */
+	dword |= (Receiver & 0x7) << 24;
+	dword &= ~(0x7 << 21);				/* TgtBank = 0 */
+	dword &= ~(0x3ff);				/* TgtAddress = 0 */
+	Set_NB32_DCT(dev, dct, 0x254, dword);
+
+	/* Configure Target B */
+	dword = Get_NB32_DCT(dev, dct, 0x258);
+	dword &= ~(0x7 << 24);				/* TgtChipSelect = Receiver */
+	dword |= (Receiver & 0x7) << 24;
+	dword &= ~(0x7 << 21);				/* TgtBank = 1 */
+	dword |= (0x1 << 21);
+	dword &= ~(0x3ff);				/* TgtAddress = 0 */
+	Set_NB32_DCT(dev, dct, 0x258, dword);
+
+	dword = Get_NB32_DCT(dev, dct, 0x250);
+	dword |= (0x1 << 3);				/* ResetAllErr = 1 */
+	dword &= ~(0x1 << 4);				/* StopOnErr = 0 */
+	dword &= ~(0x3 << 8);				/* CmdTgt = 1 (Alternate between Target A and Target B) */
+	dword |= (0x1 << 8);
+	dword &= ~(0x7 << 5);				/* CmdType = 1 (Write) */
+	dword |= (0x1 << 5);
+	dword |= (0x1 << 11);				/* SendCmd = 1 */
+	Set_NB32_DCT(dev, dct, 0x250, dword);
+
+	/* 2.10.5.8.6.1.2 Wait for TestStatus == 1 and CmdSendInProg == 0 */
+	do {
+		dword = Get_NB32_DCT(dev, dct, 0x250);
+	} while ((dword & (0x1 << 12)) || (!(dword & (0x1 << 10))));
+
+	dword = Get_NB32_DCT(dev, dct, 0x250);
+	dword &= ~(0x1 << 11);				/* SendCmd = 0 */
+	Set_NB32_DCT(dev, dct, 0x250, dword);
+
+	stop_dram_dqs_training_pattern_fam15(pMCTstat, pDCTstat, dct, Receiver);
+}
+
+/* DQS Position Training
+ * Algorithm detailed in the Fam15h BKDG Rev. 3.14 section 2.10.5.8.4
+ */
+static uint8_t TrainDQSRdWrPos_D_Fam15(struct MCTStatStruc *pMCTstat,
+				struct DCTStatStruc *pDCTstat, uint8_t dct, uint8_t receiver_start, uint8_t receiver_end, uint8_t lane_start, uint8_t lane_end)
+{
+	uint8_t dimm;
+	uint8_t lane;
+	uint32_t dword;
+	uint32_t Errors;
+	uint8_t Receiver;
+	uint8_t dual_rank;
+	uint8_t write_iter;
+	uint8_t read_iter;
+	uint16_t initial_write_dqs_delay[MAX_BYTE_LANES];
+	uint16_t initial_read_dqs_delay[MAX_BYTE_LANES];
+	uint16_t initial_write_data_timing[MAX_BYTE_LANES];
+	uint16_t current_write_data_delay[MAX_BYTE_LANES];
+	uint16_t current_read_dqs_delay[MAX_BYTE_LANES];
+	uint16_t current_write_dqs_delay[MAX_BYTE_LANES];
+	uint8_t passing_dqs_delay_found[MAX_BYTE_LANES];
+	uint8_t dqs_results_array[2][(lane_end - lane_start)][32][32];		/* [rank][lane][write step][read step] */
+
+	uint8_t last_pos = 0;
+	uint8_t cur_count = 0;
+	uint8_t best_pos = 0;
+	uint8_t best_count = 0;
+
+	uint32_t index_reg = 0x98;
+	uint32_t dev = pDCTstat->dev_dct;
+
+	/* Calculate and program MaxRdLatency */
+	Calc_SetMaxRdLatency_D_Fam15(pMCTstat, pDCTstat, dct);
+
+	Errors = 0;
+	dual_rank = 0;
+	Receiver = mct_InitReceiver_D(pDCTstat, dct);
+	if (receiver_start > Receiver)
+		Receiver = receiver_start;
+
+	/* There are four receiver pairs, loosely associated with chipselects.
+	 * This is essentially looping over each DIMM.
+	 */
+	for (; Receiver < receiver_end; Receiver += 2) {
+		dimm = (Receiver >> 1);
+		if ((Receiver & 0x1) == 0) {
+			/* Even rank of DIMM */
+			if(mct_RcvrRankEnabled_D(pMCTstat, pDCTstat, dct, Receiver+1))
+				dual_rank = 1;
+			else
+				dual_rank = 0;
+		}
+
+		if (!mct_RcvrRankEnabled_D(pMCTstat, pDCTstat, dct, Receiver)) {
+			continue;
+		}
+
+		/* Initialize variables */
+		for (lane = lane_start; lane < lane_end; lane++) {
+			passing_dqs_delay_found[lane] = 0;
+		}
+		memset(dqs_results_array, 0, sizeof(dqs_results_array));
+
+		/* Read initial read / write DQS delays */
+		read_dqs_write_timing_control_registers(initial_write_dqs_delay, dev, dct, dimm, index_reg);
+		read_dqs_read_data_timing_registers(initial_read_dqs_delay, dev, dct, dimm, index_reg);
+
+		/* Read current settings of other (previously trained) lanes */
+		read_dqs_write_data_timing_registers(initial_write_data_timing, dev, dct, dimm, index_reg);
+		memcpy(current_write_data_delay, initial_write_data_timing, sizeof(current_write_data_delay));
+
+		for (lane = lane_start; lane < lane_end; lane++) {
+			/* 2.10.5.8.4 (2)
+			 * For each Write Data Delay value from Write DQS Delay to Write DQS Delay + 1 UI
+			 */
+			for (current_write_data_delay[lane] = initial_write_dqs_delay[lane]; current_write_data_delay[lane] < (initial_write_dqs_delay[lane] + 0x20); current_write_data_delay[lane]++) {
+				print_debug_dqs("\t\t\t\tTrainDQSRdWrPos: 16 current_write_data_delay[lane] ", current_write_data_delay[lane], 6);
+
+				/* 2.10.5.8.4 (2 A)
+				 * Commit the current Write Data Timing settings to the hardware registers
+				 */
+				write_dqs_write_data_timing_registers(current_write_data_delay, dev, dct, dimm, index_reg);
+
+				/* 2.10.5.8.4 (2 B)
+				 * Write the DRAM training pattern to the test address
+				 */
+				write_dram_dqs_training_pattern_fam15(pMCTstat, pDCTstat, dct, Receiver, lane);
+
+				/* Read current settings of other (previously trained) lanes */
+				read_dqs_read_data_timing_registers(current_read_dqs_delay, dev, dct, dimm, index_reg);
+
+				/* 2.10.5.8.4 (2 C)
+				 * For each Read DQS Delay value from 0 to 1 UI
+				 */
+				for (current_read_dqs_delay[lane] = 0; current_read_dqs_delay[lane] < 0x40; current_read_dqs_delay[lane] += 2) {
+					print_debug_dqs("\t\t\t\t\tTrainDQSRdWrPos: 161 current_read_dqs_delay[lane] ", current_read_dqs_delay[lane], 6);
+
+					/* 2.10.5.8.4 (2 A i)
+					 * Commit the current Read DQS Timing Control settings to the hardware registers
+					 */
+					write_dqs_read_data_timing_registers(current_read_dqs_delay, dev, dct, dimm, index_reg);
+
+					/* 2.10.5.8.4 (2 A ii)
+					 * Read the DRAM training pattern from the test address
+					 */
+					read_dram_dqs_training_pattern_fam15(pMCTstat, pDCTstat, dct, Receiver, lane);
+
+					/* 2.10.5.8.4 (2 A iii)
+					 * Record pass / fail status
+					 */
+					dword = Get_NB32_DCT(dev, dct, 0x268) & 0x3ffff;
+					if (dword & (0x3 << (lane * 2)))
+						dqs_results_array[Receiver & 0x1][lane - lane_start][current_write_data_delay[lane] - initial_write_dqs_delay[lane]][current_read_dqs_delay[lane] >> 1] = 0;	/* Fail */
+					else
+						dqs_results_array[Receiver & 0x1][lane - lane_start][current_write_data_delay[lane] - initial_write_dqs_delay[lane]][current_read_dqs_delay[lane] >> 1] = 1;	/* Pass */
+				}
+			}
+
+			if (dual_rank && (Receiver & 0x1)) {
+				/* Overlay the previous rank test results with the current rank */
+				for (write_iter = 0; write_iter < 32; write_iter++) {
+					for (read_iter = 0; read_iter < 32; read_iter++) {
+						if ((dqs_results_array[0][lane - lane_start][write_iter][read_iter])
+							&& (dqs_results_array[1][lane - lane_start][write_iter][read_iter]))
+							dqs_results_array[1][lane - lane_start][write_iter][read_iter] = 1;
+						else
+							dqs_results_array[1][lane - lane_start][write_iter][read_iter] = 0;
+					}
+				}
+			}
+
+			/* Determine location and length of longest consecutive string of read passing values
+			 * Output is stored in best_pos and best_count
+			 */
+			last_pos = 0;
+			cur_count = 0;
+			best_pos = 0;
+			best_count = 0;
+			for (write_iter = 0; write_iter < 32; write_iter++) {
+				for (read_iter = 0; read_iter < 32; read_iter++) {
+					if ((dqs_results_array[Receiver & 0x1][lane - lane_start][write_iter][read_iter]) && (read_iter < 31)) {
+						/* Pass */
+						cur_count++;
+					} else {
+						/* Failure or end of loop */
+						if (cur_count > best_count) {
+							best_count = cur_count;
+							best_pos = last_pos;
+						}
+						cur_count = 0;
+						last_pos = read_iter;
+					}
+				}
+				last_pos = 0;
+			}
+
+			if (best_count > 2) {
+				/* Restore current settings of other (previously trained) lanes to the active array */
+				memcpy(current_read_dqs_delay, initial_read_dqs_delay, sizeof(current_read_dqs_delay));
+
+				/* Program the Read DQS Timing Control register with the center of the passing window */
+				current_read_dqs_delay[lane] = ((best_pos << 1) + ((best_count << 1) / 2));
+				passing_dqs_delay_found[lane] = 1;
+
+				/* Commit the current Read DQS Timing Control settings to the hardware registers */
+				write_dqs_read_data_timing_registers(current_read_dqs_delay, dev, dct, dimm, index_reg);
+
+				/* Save the final Read DQS Timing Control settings for later use */
+				pDCTstat->CH_D_DIR_B_DQS[dct][Receiver >> 1][DQS_READDIR][lane] = current_read_dqs_delay[lane];
+
+				print_debug_dqs("\t\t\t\tTrainDQSRdWrPos: 142 largest read passing region ", best_count, 4);
+				print_debug_dqs("\t\t\t\tTrainDQSRdWrPos: 143 largest read passing region start ", best_pos, 4);
+			} else {
+				/* Reprogram the Read DQS Timing Control register with the original settings */
+				write_dqs_read_data_timing_registers(initial_read_dqs_delay, dev, dct, dimm, index_reg);
+			}
+
+			/* Determine location and length of longest consecutive string of write passing values
+			 * Output is stored in best_pos and best_count
+			 */
+			last_pos = 0;
+			cur_count = 0;
+			best_pos = 0;
+			best_count = 0;
+			for (read_iter = 0; read_iter < 32; read_iter++) {
+				for (write_iter = 0; write_iter < 32; write_iter++) {
+					if ((dqs_results_array[Receiver & 0x1][lane - lane_start][write_iter][read_iter]) && (write_iter < 31)) {
+						/* Pass */
+						cur_count++;
+					} else {
+						/* Failure or end of loop */
+						if (cur_count > best_count) {
+							best_count = cur_count;
+							best_pos = last_pos;
+						}
+						cur_count = 0;
+						last_pos = write_iter;
+					}
+				}
+				last_pos = 0;
+			}
+
+			if (best_count > 2) {
+				/* Restore current settings of other (previously trained) lanes to the active array */
+				memcpy(current_write_dqs_delay, initial_write_data_timing, sizeof(current_write_data_delay));
+
+				/* Program the Write DQS Timing Control register with the optimal region within the passing window */
+				if (pDCTstat->Status & (1 << SB_LoadReduced))
+					current_write_dqs_delay[lane] = ((best_pos + initial_write_dqs_delay[lane]) + (best_count / 3));
+				else
+					current_write_dqs_delay[lane] = ((best_pos + initial_write_dqs_delay[lane]) + (best_count / 2));
+				passing_dqs_delay_found[lane] = 1;
+
+				/* Commit the current Write DQS Timing Control settings to the hardware registers */
+				write_dqs_write_data_timing_registers(current_write_dqs_delay, dev, dct, dimm, index_reg);
+
+				/* Save the final Write Data Timing settings for later use */
+				pDCTstat->CH_D_DIR_B_DQS[dct][Receiver >> 1][DQS_WRITEDIR][lane] = current_write_dqs_delay[lane];
+
+				print_debug_dqs("\t\t\t\tTrainDQSRdWrPos: 144 largest write passing region ", best_count, 4);
+				print_debug_dqs("\t\t\t\tTrainDQSRdWrPos: 145 largest write passing region start ", best_pos, 4);
+			} else {
+				/* Reprogram the Write DQS Timing Control register with the original settings */
+				write_dqs_write_data_timing_registers(current_write_dqs_delay, dev, dct, dimm, index_reg);
+			}
+		}
+
+#ifdef PRINT_PASS_FAIL_BITMAPS
+		for (lane = lane_start; lane < lane_end; lane++) {
+			for (read_iter = 0; read_iter < 32; read_iter++) {
+				for (write_iter = 0; write_iter < 32; write_iter++) {
+					if (dqs_results_array[Receiver & 0x1][lane - lane_start][write_iter][read_iter])
+						printk(BIOS_DEBUG, "+");
+					else
+						printk(BIOS_DEBUG, ".");
+				}
+				printk(BIOS_DEBUG, "\n");
+			}
+			printk(BIOS_DEBUG, "\n\n");
+		}
+#endif
+
+		/* Flag failure(s) if present */
+		for (lane = lane_start; lane < lane_end; lane++) {
+			if (!passing_dqs_delay_found[lane]) {
+				print_debug_dqs("\t\t\t\tTrainDQSRdWrPos: 121 Unable to find passing region for lane ", lane, 2);
+
+				/* Flag absence of passing window */
+				Errors |= 1 << SB_NODQSPOS;
+			}
+		}
+
+		pDCTstat->TrainErrors |= Errors;
+		pDCTstat->ErrStatus |= Errors;
+
+#if DQS_TRAIN_DEBUG > 0
+		{
+			u8 val;
+			u8 i;
+			u8 ChannelDTD, ReceiverDTD, Dir;
+			u8 *p;
+
+			for (Dir = 0; Dir < 2; Dir++) {
+				if (Dir == 1) {
+					printk(BIOS_DEBUG, "TrainDQSRdWrPos: CH_D_DIR_B_DQS WR:\n");
+				} else {
+					printk(BIOS_DEBUG, "TrainDQSRdWrPos: CH_D_DIR_B_DQS RD:\n");
+				}
+				for (ChannelDTD = 0; ChannelDTD < 2; ChannelDTD++) {
+					printk(BIOS_DEBUG, "Channel: %02x\n", ChannelDTD);
+					for (ReceiverDTD = 0; ReceiverDTD < MAX_CS_SUPPORTED; ReceiverDTD += 2) {
+						printk(BIOS_DEBUG, "\t\tReceiver: %02x:", ReceiverDTD);
+						p = pDCTstat->CH_D_DIR_B_DQS[ChannelDTD][ReceiverDTD >> 1][Dir];
+						for (i=0;i<8; i++) {
+							val  = p[i];
+							printk(BIOS_DEBUG, " %02x", val);
+						}
+						printk(BIOS_DEBUG, "\n");
+					}
+				}
+			}
+
+		}
+#endif
+	}
+
+	/* Return 1 on success, 0 on failure */
+	return !Errors;
+}
+
+/* DQS Receiver Enable Cycle Training
+ * Algorithm detailed in the Fam15h BKDG Rev. 3.14 section 2.10.5.8.3
+ */
+static void TrainDQSReceiverEnCyc_D_Fam15(struct MCTStatStruc *pMCTstat,
+				struct DCTStatStruc *pDCTstat)
+{
+	u32 Errors;
+	u8 Receiver;
+	u8 _DisableDramECC = 0;
+	u8 _Wrap32Dis = 0, _SSE2 = 0;
+
+	u32 addr;
+	u32 cr4;
+	u32 lo, hi;
+
+	uint8_t dct;
+	uint8_t prev;
+	uint8_t dimm;
+	uint8_t lane;
+	uint32_t dword;
+	uint32_t rx_en_offset;
+	uint16_t initial_phy_phase_delay[MAX_BYTE_LANES];
+	uint16_t current_phy_phase_delay[MAX_BYTE_LANES];
+	uint8_t dqs_results_array[1024];
+
+	uint16_t ren_step = 0x40;
+	uint32_t index_reg = 0x98;
+	uint32_t dev = pDCTstat->dev_dct;
+
+	print_debug_dqs("\nTrainDQSReceiverEnCyc: Node_ID ", pDCTstat->Node_ID, 0);
+	cr4 = read_cr4();
+	if (cr4 & (1<<9)) {
+		_SSE2 = 1;
+	}
+	cr4 |= (1<<9);		/* OSFXSR enable SSE2 */
+	write_cr4(cr4);
+
+	addr = HWCR;
+	_RDMSR(addr, &lo, &hi);
+	if (lo & (1<<17)) {
+		_Wrap32Dis = 1;
+	}
+	lo |= (1<<17);		/* HWCR.wrap32dis */
+	_WRMSR(addr, lo, hi);	/* allow 64-bit memory references in real mode */
+
+	/* Disable ECC correction of reads on the dram bus. */
+	_DisableDramECC = mct_DisableDimmEccEn_D(pMCTstat, pDCTstat);
+
+	Errors = 0;
+
+	for (dct = 0; dct < 2; dct++) {
+		/* Program D18F2x9C_x0D0F_E003_dct[1:0][DisAutoComp, DisablePredriverCal] */
+		dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fe003);
+		dword &= ~(0x3 << 13);
+		dword |= (0x1 << 13);
+		Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fe003, dword);
+	}
+
+	for (dct = 0; dct < 2; dct++) {
+		/* 2.10.5.6 */
+		fam15EnableTrainingMode(pMCTstat, pDCTstat, dct, 1);
+
+		/* 2.10.5.8.3 */
+		Receiver = mct_InitReceiver_D(pDCTstat, dct);
+
+		/* There are four receiver pairs, loosely associated with chipselects.
+		 * This is essentially looping over each DIMM.
+		 */
+		for (; Receiver < 8; Receiver += 2) {
+			dimm = (Receiver >> 1);
+
+			if (!mct_RcvrRankEnabled_D(pMCTstat, pDCTstat, dct, Receiver)) {
+				continue;
+			}
+
+			/* 2.10.5.8.3 (2) */
+			read_dqs_receiver_enable_control_registers(initial_phy_phase_delay, dev, dct, dimm, index_reg);
+
+			for (lane = 0; lane < MAX_BYTE_LANES; lane++) {
+				/* Initialize variables */
+				memset(dqs_results_array, 0, sizeof(dqs_results_array));
+
+				/* 2.10.5.8.3 (1) */
+				dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f0030 | (lane << 8));
+				dword |= (0x1 << 8);								/* BlockRxDqsLock = 1 */
+				Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f0030 | (lane << 8), dword);
+
+				/* 2.10.5.8.3 (3) */
+				rx_en_offset = (initial_phy_phase_delay[lane] + 0x10) % 0x40;
+
+				/* 2.10.5.8.3 (4) */
+				for (current_phy_phase_delay[lane] = rx_en_offset; current_phy_phase_delay[lane] < 0x3ff; current_phy_phase_delay[lane] += ren_step) {
+					/* 2.10.5.8.3 (4 A) */
+					write_dqs_receiver_enable_control_registers(current_phy_phase_delay, dev, dct, dimm, index_reg);
+
+					/* Calculate and program MaxRdLatency */
+					Calc_SetMaxRdLatency_D_Fam15(pMCTstat, pDCTstat, dct);
+
+					/* 2.10.5.8.3 (4 B) */
+					dqs_results_array[current_phy_phase_delay[lane]] = TrainDQSRdWrPos_D_Fam15(pMCTstat, pDCTstat, dct, Receiver, Receiver + 2, lane, lane + 1);
+				}
+
+#ifdef PRINT_PASS_FAIL_BITMAPS
+				uint16_t iter;
+				for (iter = 0; iter < 0x3ff; iter++) {
+					if (dqs_results_array[iter])
+						printk(BIOS_DEBUG, "+");
+					else
+						printk(BIOS_DEBUG, ".");
+				}
+				printk(BIOS_DEBUG, "\n");
+#endif
+
+				/* 2.10.5.8.3 (5) */
+				prev = 0;
+				for (current_phy_phase_delay[lane] = rx_en_offset; current_phy_phase_delay[lane] < 0x3ff; current_phy_phase_delay[lane] += ren_step) {
+					if ((dqs_results_array[current_phy_phase_delay[lane]] == 0) && (prev == 1)) {
+						/* Restore last known good delay */
+						current_phy_phase_delay[lane] -= ren_step;
+
+						/* 2.10.5.8.3 (5 A B) */
+						current_phy_phase_delay[lane] -= 0x10;
+
+						/* Update hardware registers with final values */
+						write_dqs_receiver_enable_control_registers(current_phy_phase_delay, dev, dct, dimm, index_reg);
+						break;
+					}
+					prev = dqs_results_array[current_phy_phase_delay[lane]];
+				}
+
+				/* 2.10.5.8.3 (6) */
+				dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f0030 | (lane << 8));
+				dword &= ~(0x1 << 8);								/* BlockRxDqsLock = 0 */
+				Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f0030 | (lane << 8), dword);
+			}
+
+#if DQS_TRAIN_DEBUG > 0
+			printk(BIOS_DEBUG, "TrainDQSReceiverEnCyc_D_Fam15 DQS receiver enable timing: ");
+			for (lane = 0; lane < MAX_BYTE_LANES; lane++) {
+				printk(BIOS_DEBUG, " %03x", current_phy_phase_delay[lane]);
+			}
+			printk(BIOS_DEBUG, "\n");
+#endif
+		}
+	}
+
+	pDCTstat->TrainErrors |= Errors;
+	pDCTstat->ErrStatus |= Errors;
+
+#if DQS_TRAIN_DEBUG > 0
+	{
+		u8 val;
+		u8 i;
+		u8 ChannelDTD, ReceiverDTD, Dir;
+		u8 *p;
+
+		for (Dir = 0; Dir < 2; Dir++) {
+			if (Dir == 1) {
+				printk(BIOS_DEBUG, "TrainDQSRdWrPos: CH_D_DIR_B_DQS WR:\n");
+			} else {
+				printk(BIOS_DEBUG, "TrainDQSRdWrPos: CH_D_DIR_B_DQS RD:\n");
+			}
+			for (ChannelDTD = 0; ChannelDTD < 2; ChannelDTD++) {
+				printk(BIOS_DEBUG, "Channel: %02x\n", ChannelDTD);
+				for (ReceiverDTD = 0; ReceiverDTD < MAX_CS_SUPPORTED; ReceiverDTD += 2) {
+					printk(BIOS_DEBUG, "\t\tReceiver: %02x:", ReceiverDTD);
+					p = pDCTstat->CH_D_DIR_B_DQS[ChannelDTD][ReceiverDTD >> 1][Dir];
+					for (i=0;i<8; i++) {
+						val  = p[i];
+						printk(BIOS_DEBUG, " %02x", val);
+					}
+					printk(BIOS_DEBUG, "\n");
+				}
+			}
+		}
+
+	}
+#endif
+	if (_DisableDramECC) {
+		mct_EnableDimmEccEn_D(pMCTstat, pDCTstat, _DisableDramECC);
+	}
+	if (!_Wrap32Dis) {
+		addr = HWCR;
+		_RDMSR(addr, &lo, &hi);
+		lo &= ~(1<<17);		/* restore HWCR.wrap32dis */
+		_WRMSR(addr, lo, hi);
+	}
+	if (!_SSE2){
+		cr4 = read_cr4();
+		cr4 &= ~(1<<9);		/* restore cr4.OSFXSR */
+		write_cr4(cr4);
+	}
+
+	printk(BIOS_DEBUG, "TrainDQSReceiverEnCyc: Status %x\n", pDCTstat->Status);
+	printk(BIOS_DEBUG, "TrainDQSReceiverEnCyc: TrainErrors %x\n", pDCTstat->TrainErrors);
+	printk(BIOS_DEBUG, "TrainDQSReceiverEnCyc: ErrStatus %x\n", pDCTstat->ErrStatus);
+	printk(BIOS_DEBUG, "TrainDQSReceiverEnCyc: ErrCode %x\n", pDCTstat->ErrCode);
+	printk(BIOS_DEBUG, "TrainDQSReceiverEnCyc: Done\n\n");
+}
+
 static void SetupDqsPattern_D(struct MCTStatStruc *pMCTstat,
 				struct DCTStatStruc *pDCTstat, u32 *buffer)
 {
@@ -869,18 +1785,17 @@ static u8 ChipSelPresent_D(struct MCTStatStruc *pMCTstat,
 	u32 val;
 	u32 reg;
 	u32 dev = pDCTstat->dev_dct;
-	u32 reg_off;
+	uint8_t dct = 0;
 	u8 ret = 0;
 
-	if (!pDCTstat->GangedMode) {
-		reg_off = 0x100 * Channel;
-	} else {
-		reg_off = 0;
-	}
+	if (!pDCTstat->GangedMode)
+		dct = Channel;
+	else
+		dct = 0;
 
 	if (ChipSel < MAX_CS_SUPPORTED){
-		reg = 0x40 + (ChipSel << 2) + reg_off;
-		val = Get_NB32(dev, reg);
+		reg = 0x40 + (ChipSel << 2);
+		val = Get_NB32_DCT(dev, dct, reg);
 		if (val & ( 1 << 0))
 			ret = 1;
 	}
@@ -1085,12 +2000,12 @@ u32 SetUpperFSbase(u32 addr_hi)
 	return addr_hi << 8;
 }
 
-void ResetDCTWrPtr_D(u32 dev, u32 index_reg, u32 index)
+void ResetDCTWrPtr_D(u32 dev, uint8_t dct, u32 index_reg, u32 index)
 {
 	u32 val;
 
-	val = Get_NB32_index_wait(dev, index_reg, index);
-	Set_NB32_index_wait(dev, index_reg, index, val);
+	val = Get_NB32_index_wait_DCT(dev, dct, index_reg, index);
+	Set_NB32_index_wait_DCT(dev, dct, index_reg, index, val);
 }
 
 void mct_TrainDQSPos_D(struct MCTStatStruc *pMCTstat,
@@ -1103,9 +2018,13 @@ void mct_TrainDQSPos_D(struct MCTStatStruc *pMCTstat,
 	for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
 		pDCTstat = pDCTstatA + Node;
 		if (pDCTstat->DCTSysLimit) {
-			TrainDQSRdWrPos_D(pMCTstat, pDCTstat);
-			for (ChipSel = 0; ChipSel < MAX_CS_SUPPORTED; ChipSel += 2) {
-				SetEccDQSRdWrPos_D(pMCTstat, pDCTstat, ChipSel);
+			if (is_fam15h()) {
+				TrainDQSReceiverEnCyc_D_Fam15(pMCTstat, pDCTstat);
+			} else {
+				TrainDQSRdWrPos_D_Fam10(pMCTstat, pDCTstat);
+				for (ChipSel = 0; ChipSel < MAX_CS_SUPPORTED; ChipSel += 2) {
+					SetEccDQSRdWrPos_D_Fam10(pMCTstat, pDCTstat, ChipSel);
+				}
 			}
 		}
 	}
@@ -1126,19 +2045,18 @@ u8 mct_DisableDimmEccEn_D(struct MCTStatStruc *pMCTstat,
 
 	dev = pDCTstat->dev_dct;
 	reg = 0x90;
-	val = Get_NB32(dev, reg);
+	val = Get_NB32_DCT(dev, 0, reg);
 	if (val & (1<<DimmEcEn)) {
 		_DisableDramECC |= 0x01;
 		val &= ~(1<<DimmEcEn);
-		Set_NB32(dev, reg, val);
+		Set_NB32_DCT(dev, 0, reg, val);
 	}
 	if (!pDCTstat->GangedMode) {
-		reg = 0x190;
-		val = Get_NB32(dev, reg);
+		val = Get_NB32_DCT(dev, 1, reg);
 		if (val & (1<<DimmEcEn)) {
 			_DisableDramECC |= 0x02;
 			val &= ~(1<<DimmEcEn);
-			Set_NB32(dev, reg, val);
+			Set_NB32_DCT(dev, 1, reg, val);
 		}
 	}
 	return _DisableDramECC;
@@ -1157,15 +2075,14 @@ void mct_EnableDimmEccEn_D(struct MCTStatStruc *pMCTstat,
 
 	if ((_DisableDramECC & 0x01) == 0x01) {
 		reg = 0x90;
-		val = Get_NB32(dev, reg);
+		val = Get_NB32_DCT(dev, 0, reg);
 		val |= (1<<DimmEcEn);
-		Set_NB32(dev, reg, val);
+		Set_NB32_DCT(dev, 0, reg, val);
 	}
 	if ((_DisableDramECC & 0x02) == 0x02) {
-		reg = 0x190;
-		val = Get_NB32(dev, reg);
+		val = Get_NB32_DCT(dev, 1, reg);
 		val |= (1<<DimmEcEn);
-		Set_NB32(dev, reg, val);
+		Set_NB32_DCT(dev, 1, reg, val);
 	}
 }
 
@@ -1177,7 +2094,7 @@ static void mct_SetDQSDelayCSR_D(struct MCTStatStruc *pMCTstat,
 {
 	u8 ByteLane;
 	u32 val;
-	u32 index_reg = 0x98 + 0x100 * pDCTstat->Channel;
+	u32 index_reg = 0x98;
 	u8 shift;
 	u32 dqs_delay = (u32)pDCTstat->DQSDelay;
 	u32 dev = pDCTstat->dev_dct;
@@ -1205,7 +2122,7 @@ static void mct_SetDQSDelayCSR_D(struct MCTStatStruc *pMCTstat,
 
 		index += (ChipSel>>1) << 8;
 
-		val = Get_NB32_index_wait(dev, index_reg, index);
+		val = Get_NB32_index_wait_DCT(dev, pDCTstat->Channel, index_reg, index);
 		if (ByteLane < 8) {
 			if (pDCTstat->Direction == DQS_WRITEDIR) {
 				dqs_delay += pDCTstat->CH_D_B_TxDqs[pDCTstat->Channel][ChipSel>>1][ByteLane];
@@ -1215,7 +2132,7 @@ static void mct_SetDQSDelayCSR_D(struct MCTStatStruc *pMCTstat,
 		}
 		val &= ~(0x7f << shift);
 		val |= (dqs_delay << shift);
-		Set_NB32_index_wait(dev, index_reg, index, val);
+		Set_NB32_index_wait_DCT(dev, pDCTstat->Channel, index_reg, index, val);
 	}
 }
 
@@ -1241,7 +2158,7 @@ u32 mct_GetMCTSysAddr_D(struct MCTStatStruc *pMCTstat,
 				u8 Channel, u8 receiver, u8 *valid)
 {
 	u32 val;
-	u32 reg_off = 0;
+	uint8_t dct = 0;
 	u32 reg;
 	u32 dword;
 	u32 dev = pDCTstat->dev_dct;
@@ -1250,12 +2167,12 @@ u32 mct_GetMCTSysAddr_D(struct MCTStatStruc *pMCTstat,
 
 
 	if (!pDCTstat->GangedMode) {
-		reg_off = 0x100 * Channel;
+		dct = Channel;
 	}
 
 	/* get the local base addr of the chipselect */
-	reg = 0x40 + (receiver << 2) + reg_off;
-	val = Get_NB32(dev, reg);
+	reg = 0x40 + (receiver << 2);
+	val = Get_NB32_DCT(dev, dct, reg);
 
 	val &= ~0xe007c01f;
 
diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctecc_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mctecc_d.c
index 0c52791..11f1b2c 100644
--- a/src/northbridge/amd/amdmct/mct_ddr3/mctecc_d.c
+++ b/src/northbridge/amd/amdmct/mct_ddr3/mctecc_d.c
@@ -2,6 +2,7 @@
  * This file is part of the coreboot project.
  *
  * Copyright (C) 2010 Advanced Micro Devices, Inc.
+ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -91,19 +92,21 @@ u8 ECCInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA)
 
 	/* Construct these booleans, based on setup options, for easy handling
 	later in this procedure */
-	OB_NBECC = mctGet_NVbits(NV_NBECC);	/* MCA ECC (MCE) enable bit */
+	OB_NBECC = mctGet_NVbits(NV_NBECC);			/* MCA ECC (MCE) enable bit */
 
-	OB_ECCRedir =  mctGet_NVbits(NV_ECCRedir);	/* ECC Redirection */
+	OB_ECCRedir =  mctGet_NVbits(NV_ECCRedir);		/* ECC Redirection */
 
-	OB_ChipKill = mctGet_NVbits(NV_ChipKill); 	/* ECC Chip-kill mode */
+	OB_ChipKill = mctGet_NVbits(NV_ChipKill); 		/* ECC Chip-kill mode */
+	OF_ScrubCTL = 0;					/* Scrub CTL for Dcache, L2, and dram */
 
-	OF_ScrubCTL = 0;		/* Scrub CTL for Dcache, L2, and dram */
-	nvbits = mctGet_NVbits(NV_DCBKScrub);
-	/* mct_AdjustScrub_D(pDCTstatA, &nvbits); */ /* Need not adjust */
-	OF_ScrubCTL |= (u32) nvbits << 16;
+	if (!is_fam15h()) {
+		nvbits = mctGet_NVbits(NV_DCBKScrub);
+		/* mct_AdjustScrub_D(pDCTstatA, &nvbits); */ 	/* Need not adjust */
+		OF_ScrubCTL |= (u32) nvbits << 16;
 
-	nvbits = mctGet_NVbits(NV_L2BKScrub);
-	OF_ScrubCTL |= (u32) nvbits << 8;
+		nvbits = mctGet_NVbits(NV_L2BKScrub);
+		OF_ScrubCTL |= (u32) nvbits << 8;
+	}
 
 	nvbits = mctGet_NVbits(NV_DramBKScrub);
 	OF_ScrubCTL |= nvbits;
@@ -131,7 +134,7 @@ u8 ECCInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA)
 							pDCTstat->ErrStatus |= (1 << SB_DramECCDis);
 						}
 						AllECC = 0;
-						LDramECC =0;
+						LDramECC = 0;
 					}
 				} else {
 					AllECC = 0;
@@ -140,7 +143,7 @@ u8 ECCInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA)
 					if (OB_NBECC) {
 						mct_EnableDatIntlv_D(pMCTstat, pDCTstat);
 						dev = pDCTstat->dev_nbmisc;
-						reg =0x44;	/* MCA NB Configuration */
+						reg = 0x44;	/* MCA NB Configuration */
 						val = Get_NB32(dev, reg);
 						val |= 1 << 22;	/* EccEn */
 						Set_NB32(dev, reg, val);
@@ -177,6 +180,10 @@ u8 ECCInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA)
 			/*WE/RE is checked because memory config may have been */
 			if((val & 3)==3) {	/* Node has dram populated */
 				if (isDramECCEn_D(pDCTstat)) {	/* if ECC is enabled on this dram */
+					if (is_fam15h()) {
+						/* Erratum 505 */
+						fam15h_switch_dct(pDCTstat->dev_map, 0);
+					}
 					dev = pDCTstat->dev_nbmisc;
 					val = curBase << 8;
 					if(OB_ECCRedir) {
@@ -187,16 +194,18 @@ u8 ECCInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA)
 					Set_NB32(dev, 0x60, val); /* Dram Scrub Addr High */
 					Set_NB32(dev, 0x58, OF_ScrubCTL);	/*Scrub Control */
 
-					/* Divisor should not be set deeper than
-					 * divide by 16 when Dcache scrubber or
-					 * L2 scrubber is enabled.
-					 */
-					if ((OF_ScrubCTL & (0x1F << 16)) || (OF_ScrubCTL & (0x1F << 8))) {
-						val = Get_NB32(dev, 0x84);
-						if ((val & 0xE0000000) > 0x80000000) {	/* Get F3x84h[31:29]ClkDivisor for C1 */
-							val &= 0x1FFFFFFF;	/* If ClkDivisor is deeper than divide-by-16 */
-							val |= 0x80000000;	/* set it to divide-by-16 */
-							Set_NB32(dev, 0x84, val);
+					if (!is_fam15h()) {
+						/* Divisor should not be set deeper than
+						 * divide by 16 when Dcache scrubber or
+						 * L2 scrubber is enabled.
+						 */
+						if ((OF_ScrubCTL & (0x1F << 16)) || (OF_ScrubCTL & (0x1F << 8))) {
+							val = Get_NB32(dev, 0x84);
+							if ((val & 0xE0000000) > 0x80000000) {	/* Get F3x84h[31:29]ClkDivisor for C1 */
+								val &= 0x1FFFFFFF;	/* If ClkDivisor is deeper than divide-by-16 */
+								val |= 0x80000000;	/* set it to divide-by-16 */
+								Set_NB32(dev, 0x84, val);
+							}
 						}
 					}
 				}	/* this node has ECC enabled dram */
@@ -267,8 +276,8 @@ static u8 isDramECCEn_D(struct DCTStatStruc *pDCTstat)
 	}
 	for(i=0; i<ch_end; i++) {
 		if(pDCTstat->DIMMValidDCT[i] > 0){
-			reg = 0x90 + i * 0x100;		/* Dram Config Low */
-			val = Get_NB32(dev, reg);
+			reg = 0x90;		/* Dram Config Low */
+			val = Get_NB32_DCT(dev, i, reg);
 			if(val & (1<<DimmEcEn)) {
 				/* set local flag 'dram ecc capable' */
 				isDimmECCEn = 1;
diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mcthdi.c b/src/northbridge/amd/amdmct/mct_ddr3/mcthdi.c
index 0112732..a6b9dcb 100644
--- a/src/northbridge/amd/amdmct/mct_ddr3/mcthdi.c
+++ b/src/northbridge/amd/amdmct/mct_ddr3/mcthdi.c
@@ -2,6 +2,7 @@
  * This file is part of the coreboot project.
  *
  * Copyright (C) 2010 Advanced Micro Devices, Inc.
+ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -25,8 +26,8 @@ void mct_DramInit_Hw_D(struct MCTStatStruc *pMCTstat,
 	u32 dev = pDCTstat->dev_dct;
 
 	/*flag for selecting HW/SW DRAM Init HW DRAM Init */
-	reg = 0x90 + 0x100 * dct; /*DRAM Configuration Low */
-	val = Get_NB32(dev, reg);
+	reg = 0x90; /*DRAM Configuration Low */
+	val = Get_NB32_DCT(dev, dct, reg);
 	val |= (1<<InitDram);
-	Set_NB32(dev, reg, val);
+	Set_NB32_DCT(dev, dct, reg, val);
 }
diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mcthwl.c b/src/northbridge/amd/amdmct/mct_ddr3/mcthwl.c
index 60bc01d..5e81808 100644
--- a/src/northbridge/amd/amdmct/mct_ddr3/mcthwl.c
+++ b/src/northbridge/amd/amdmct/mct_ddr3/mcthwl.c
@@ -18,10 +18,12 @@
  * Foundation, Inc.
  */
 
-static void SetTargetFreq(struct MCTStatStruc *pMCTstat,
-					struct DCTStatStruc *pDCTstat);
-static void AgesaHwWlPhase1(sMCTStruct *pMCTData,
-					sDCTStruct *pDCTData, u8 dimm, u8 pass);
+static void AgesaHwWlPhase1(struct MCTStatStruc *pMCTstat,
+					struct DCTStatStruc *pDCTstat, u8 dct, u8 dimm, u8 pass);
+static void AgesaHwWlPhase2(struct MCTStatStruc *pMCTstat,
+					struct DCTStatStruc *pDCTstat, u8 dct, u8 dimm, u8 pass);
+static void AgesaHwWlPhase3(struct MCTStatStruc *pMCTstat,
+					struct DCTStatStruc *pDCTstat, u8 dct, u8 dimm, u8 pass);
 static void EnableZQcalibration(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat);
 static void DisableZQcalibration(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat);
 static void PrepareC_MCT(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat);
@@ -56,7 +58,7 @@ static void SetEccWrDQS_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pD
 					Addl_Index = 0x32;
 				Addl_Index += DimmNum * 3;
 
-				val = Get_NB32_index_wait(pDCTstat->dev_dct, Channel * 0x100 + 0x98, Addl_Index);
+				val = Get_NB32_index_wait_DCT(pDCTstat->dev_dct, Channel, 0x98, Addl_Index);
 				if (OddByte)
 					val >>= 16;
 				/* Save WrDqs to stack for later usage */
@@ -74,13 +76,13 @@ static void EnableAutoRefresh_D(struct MCTStatStruc *pMCTstat, struct DCTStatStr
 {
 	u32 val;
 
-	val = Get_NB32(pDCTstat->dev_dct, 0x8C);
+	val = Get_NB32_DCT(pDCTstat->dev_dct, 0, 0x8C);
 	val &= ~(1 << DisAutoRefresh);
-	Set_NB32(pDCTstat->dev_dct, 0x8C, val);
+	Set_NB32_DCT(pDCTstat->dev_dct, 0, 0x8C, val);
 
-	val = Get_NB32(pDCTstat->dev_dct, 0x8C + 0x100);
+	val = Get_NB32_DCT(pDCTstat->dev_dct, 1, 0x8C);
 	val &= ~(1 << DisAutoRefresh);
-	Set_NB32(pDCTstat->dev_dct, 0x8C + 0x100, val);
+	Set_NB32_DCT(pDCTstat->dev_dct, 1, 0x8C, val);
 }
 
 static void DisableAutoRefresh_D(struct MCTStatStruc *pMCTstat,
@@ -88,13 +90,13 @@ static void DisableAutoRefresh_D(struct MCTStatStruc *pMCTstat,
 {
 	u32 val;
 
-	val = Get_NB32(pDCTstat->dev_dct, 0x8C);
+	val = Get_NB32_DCT(pDCTstat->dev_dct, 0, 0x8C);
 	val |= 1 << DisAutoRefresh;
-	Set_NB32(pDCTstat->dev_dct, 0x8C, val);
+	Set_NB32_DCT(pDCTstat->dev_dct, 0, 0x8C, val);
 
-	val = Get_NB32(pDCTstat->dev_dct, 0x8C + 0x100);
+	val = Get_NB32_DCT(pDCTstat->dev_dct, 1, 0x8C);
 	val |= 1 << DisAutoRefresh;
-	Set_NB32(pDCTstat->dev_dct, 0x8C + 0x100, val);
+	Set_NB32_DCT(pDCTstat->dev_dct, 1, 0x8C, val);
 }
 
 
@@ -118,8 +120,11 @@ static void PhyWLPass1(struct MCTStatStruc *pMCTstat,
 		DIMMValid = pDCTstat->DIMMValid;
 		PrepareC_DCT(pMCTstat, pDCTstat, dct);
 		for (dimm = 0; dimm < MAX_DIMMS_SUPPORTED; dimm ++) {
-			if (DIMMValid & (1 << (dimm << 1)))
-				AgesaHwWlPhase1(pDCTstat->C_MCTPtr, DCTPtr, dimm, FirstPass);
+			if (DIMMValid & (1 << (dimm << 1))) {
+				AgesaHwWlPhase1(pMCTstat, pDCTstat, dct, dimm, FirstPass);
+				AgesaHwWlPhase2(pMCTstat, pDCTstat, dct, dimm, FirstPass);
+				AgesaHwWlPhase3(pMCTstat, pDCTstat, dct, dimm, FirstPass);
+			}
 		}
 	}
 }
@@ -146,27 +151,40 @@ static void PhyWLPass2(struct MCTStatStruc *pMCTstat,
 		pDCTstat->Speed = pDCTstat->DIMMAutoSpeed = pDCTstat->TargetFreq;
 		pDCTstat->CASL = pDCTstat->DIMMCASL = pDCTstat->TargetCASL;
 		SPD2ndTiming(pMCTstat, pDCTstat, dct);
-		ProgDramMRSReg_D(pMCTstat, pDCTstat, dct);
-		PlatformSpec_D(pMCTstat, pDCTstat, dct);
-		fenceDynTraining_D(pMCTstat, pDCTstat, dct);
+		if (!is_fam15h()) {
+			ProgDramMRSReg_D(pMCTstat, pDCTstat, dct);
+			PlatformSpec_D(pMCTstat, pDCTstat, dct);
+			fenceDynTraining_D(pMCTstat, pDCTstat, dct);
+		}
 		Restore_OnDimmMirror(pMCTstat, pDCTstat);
 		StartupDCT_D(pMCTstat, pDCTstat, dct);
 		Clear_OnDimmMirror(pMCTstat, pDCTstat);
 		SetDllSpeedUp_D(pMCTstat, pDCTstat, dct);
 		DisableAutoRefresh_D(pMCTstat, pDCTstat);
 		for (dimm = 0; dimm < MAX_DIMMS_SUPPORTED; dimm ++) {
-			if (DIMMValid & (1 << (dimm << 1)))
-				AgesaHwWlPhase1(pDCTstat->C_MCTPtr, pDCTstat->C_DCTPtr[dct], dimm, SecondPass);
+			if (DIMMValid & (1 << (dimm << 1))) {
+				AgesaHwWlPhase1(pMCTstat, pDCTstat, dct, dimm, SecondPass);
+				AgesaHwWlPhase2(pMCTstat, pDCTstat, dct, dimm, SecondPass);
+				AgesaHwWlPhase3(pMCTstat, pDCTstat, dct, dimm, SecondPass);
+			}
 		}
 	}
 }
 
+static uint16_t fam15h_next_highest_memclk_freq(uint16_t memclk_freq)
+{
+	uint16_t fam15h_next_highest_freq_tab[] = {0, 0, 0, 0, 0x6, 0, 0xa, 0, 0, 0, 0xe, 0, 0, 0, 0x12, 0, 0, 0, 0x16, 0, 0, 0, 0x16};
+	return fam15h_next_highest_freq_tab[memclk_freq];
+}
+
 /* Write Levelization Training
  * Algorithm detailed in the Fam10h BKDG Rev. 3.62 section 2.8.9.9.1
  */
 static void WriteLevelization_HW(struct MCTStatStruc *pMCTstat,
-					struct DCTStatStruc *pDCTstat)
+					struct DCTStatStruc *pDCTstat, uint8_t Pass)
 {
+	uint16_t final_target_freq;
+
 	pDCTstat->C_MCTPtr  = &(pDCTstat->s_C_MCTPtr);
 	pDCTstat->C_DCTPtr[0] = &(pDCTstat->s_C_DCTPtr[0]);
 	pDCTstat->C_DCTPtr[1] = &(pDCTstat->s_C_DCTPtr[1]);
@@ -182,16 +200,39 @@ static void WriteLevelization_HW(struct MCTStatStruc *pMCTstat,
 		pDCTstat->DIMMValidDCT[1] = pDCTstat->DIMMValidDCT[0];
 	}
 
-	PhyWLPass1(pMCTstat, pDCTstat, 0);
-	PhyWLPass1(pMCTstat, pDCTstat, 1);
+	if (Pass == FirstPass) {
+		PhyWLPass1(pMCTstat, pDCTstat, 0);
+		PhyWLPass1(pMCTstat, pDCTstat, 1);
+	}
+
+	if (Pass == SecondPass) {
+		if (pDCTstat->TargetFreq > mhz_to_memclk_config(mctGet_NVbits(NV_MIN_MEMCLK))) {
+			/* 8.Prepare the memory subsystem for the target MEMCLK frequency.
+			 * NOTE: BIOS must program both DCTs to the same frequency.
+			 * NOTE: Fam15h steps the frequency, Fam10h slams the frequency.
+			 */
+			final_target_freq = pDCTstat->TargetFreq;
+
+			while (pDCTstat->Speed != final_target_freq) {
+				if (is_fam15h())
+					pDCTstat->TargetFreq = fam15h_next_highest_memclk_freq(pDCTstat->Speed);
+				else
+					pDCTstat->TargetFreq = final_target_freq;
+				SetTargetFreq(pMCTstat, pDCTstat);
+				PhyWLPass2(pMCTstat, pDCTstat, 0);
+				PhyWLPass2(pMCTstat, pDCTstat, 1);
+			}
+
+			pDCTstat->TargetFreq = final_target_freq;
 
-	if (pDCTstat->TargetFreq > 4) {
-		/* 8.Prepare the memory subsystem for the target MEMCLK frequency.
-		 * Note: BIOS must program both DCTs to the same frequency.
-		 */
-		SetTargetFreq(pMCTstat, pDCTstat);
-		PhyWLPass2(pMCTstat, pDCTstat, 0);
-		PhyWLPass2(pMCTstat, pDCTstat, 1);
+			uint8_t dct;
+			for (dct = 0; dct < 2; dct++) {
+				sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[dct];
+				memcpy(pDCTData->WLGrossDelayFinalPass, pDCTData->WLGrossDelayPrevPass, sizeof(pDCTData->WLGrossDelayPrevPass));
+				memcpy(pDCTData->WLFineDelayFinalPass, pDCTData->WLFineDelayPrevPass, sizeof(pDCTData->WLFineDelayPrevPass));
+				pDCTData->WLCriticalGrossDelayFinalPass = pDCTData->WLCriticalGrossDelayPrevPass;
+			}
+		}
 	}
 
 	SetEccWrDQS_D(pMCTstat, pDCTstat);
@@ -200,7 +241,7 @@ static void WriteLevelization_HW(struct MCTStatStruc *pMCTstat,
 }
 
 void mct_WriteLevelization_HW(struct MCTStatStruc *pMCTstat,
-					struct DCTStatStruc *pDCTstatA)
+					struct DCTStatStruc *pDCTstatA, uint8_t Pass)
 {
 	u8 Node;
 
@@ -211,7 +252,7 @@ void mct_WriteLevelization_HW(struct MCTStatStruc *pMCTstat,
 		if (pDCTstat->NodePresent) {
 			mctSMBhub_Init(Node);
 			Clear_OnDimmMirror(pMCTstat, pDCTstat);
-			WriteLevelization_HW(pMCTstat, pDCTstat);
+			WriteLevelization_HW(pMCTstat, pDCTstat, Pass);
 			Restore_OnDimmMirror(pMCTstat, pDCTstat);
 		}
 	}
diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctproc.c b/src/northbridge/amd/amdmct/mct_ddr3/mctproc.c
index cda9c6b..5ef4a2c 100644
--- a/src/northbridge/amd/amdmct/mct_ddr3/mctproc.c
+++ b/src/northbridge/amd/amdmct/mct_ddr3/mctproc.c
@@ -34,7 +34,7 @@ u32 mct_SetDramConfigMisc2(struct DCTStatStruc *pDCTstat, u8 dct, u32 misc2)
 
 		if (pDCTstat->LogicalCPUID & AMD_DR_Cx)
 			misc2 |= 1 << OdtSwizzle;
-		val = Get_NB32(pDCTstat->dev_dct, dct * 0x100 + 0x78);
+		val = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0x78);
 
 		val &= 7;
 		val = ((~val) & 0xff) + 1;
diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctrci.c b/src/northbridge/amd/amdmct/mct_ddr3/mctrci.c
index bd8b7fb..5ea7fa6 100644
--- a/src/northbridge/amd/amdmct/mct_ddr3/mctrci.c
+++ b/src/northbridge/amd/amdmct/mct_ddr3/mctrci.c
@@ -2,6 +2,7 @@
  * This file is part of the coreboot project.
  *
  * Copyright (C) 2010 Advanced Micro Devices, Inc.
+ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -23,7 +24,6 @@ static u32 mct_ControlRC(struct MCTStatStruc *pMCTstat,
 	u8 Dimms, DimmNum, MaxDimm, Speed;
 	u32 val;
 	u32 dct = 0;
-	u32 reg_off = 0;
 
 	DimmNum = (MrsChipSel >> 20) & 0xFE;
 
@@ -41,7 +41,6 @@ static u32 mct_ControlRC(struct MCTStatStruc *pMCTstat,
 		dct = 1;
 		DimmNum ++;
 	}
-	reg_off = 0x100 * dct;
 	Dimms = pDCTstat->MAdimms[dct];
 
 	val = 0;
@@ -95,21 +94,21 @@ static u32 mct_ControlRC(struct MCTStatStruc *pMCTstat,
 static void mct_SendCtrlWrd(struct MCTStatStruc *pMCTstat,
 			struct DCTStatStruc *pDCTstat, u32 val)
 {
-	u32 reg_off = 0;
+	uint8_t dct = 0;
 	u32 dev = pDCTstat->dev_dct;
 
 	if (pDCTstat->CSPresent_DCT[0] > 0) {
-		reg_off = 0;
+		dct = 0;
 	} else if (pDCTstat->CSPresent_DCT[1] > 0 ){
-		reg_off = 0x100;
+		dct = 1;
 	}
 
-	val |= Get_NB32(dev, reg_off + 0x7C) & ~0xFFFFFF;
+	val |= Get_NB32_DCT(dev, dct, 0x7C) & ~0xFFFFFF;
 	val |= 1 << SendControlWord;
-	Set_NB32(dev, reg_off + 0x7C, val);
+	Set_NB32_DCT(dev, dct, 0x7C, val);
 
 	do {
-		val = Get_NB32(dev, reg_off + 0x7C);
+		val = Get_NB32_DCT(dev, dct, 0x7C);
 	} while (val & (1 << SendControlWord));
 }
 
@@ -119,7 +118,6 @@ void mct_DramControlReg_Init_D(struct MCTStatStruc *pMCTstat,
 	u8 MrsChipSel;
 	u32 dev = pDCTstat->dev_dct;
 	u32 val, cw;
-	u32 reg_off = 0x100 * dct;
 
 	mct_Wait(1600);
 
@@ -127,7 +125,7 @@ void mct_DramControlReg_Init_D(struct MCTStatStruc *pMCTstat,
 
 	for (MrsChipSel = 0; MrsChipSel < 8; MrsChipSel ++, MrsChipSel ++) {
 		if (pDCTstat->CSPresent & (1 << MrsChipSel)) {
-			val = Get_NB32(dev, reg_off + 0xA8);
+			val = Get_NB32_DCT(dev, dct, 0xa8);
 			val &= ~(0xF << 8);
 
 			switch (MrsChipSel) {
@@ -144,7 +142,7 @@ void mct_DramControlReg_Init_D(struct MCTStatStruc *pMCTstat,
 			case 7:
 				val |= (3 << 6) << 8;
 			}
-			Set_NB32(dev, reg_off + 0xA8 , val);
+			Set_NB32_DCT(dev, dct, 0xa8, val);
 
 			for (cw=0; cw <=15; cw ++) {
 				mct_Wait(1600);
@@ -171,10 +169,10 @@ void FreqChgCtrlWrd(struct MCTStatStruc *pMCTstat,
 	for (MrsChipSel=0; MrsChipSel < 8; MrsChipSel++, MrsChipSel++) {
 		if (pDCTstat->CSPresent & (1 << MrsChipSel)) {
 			/* 2. Program F2x[1, 0]A8[CtrlWordCS]=bit mask for target chip selects. */
-			val = Get_NB32(dev, 0xA8); /* TODO: dct * 0x100 + 0xA8 */
+			val = Get_NB32_DCT(dev, 0, 0xA8); /* TODO: dct 0 / 1 select */
 			val &= ~(0xFF << 8);
 			val |= (0x3 << (MrsChipSel & 0xFE)) << 8;
-			Set_NB32(dev, 0xA8, val); /* TODO: dct * 0x100 + 0xA8 */
+			Set_NB32_DCT(dev, 0, 0xA8, val); /* TODO: dct 0 / 1 select */
 
 			/* Resend control word 10 */
 			mct_Wait(1600);
diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctsdi.c b/src/northbridge/amd/amdmct/mct_ddr3/mctsdi.c
index b21b96a..51cbf16 100644
--- a/src/northbridge/amd/amdmct/mct_ddr3/mctsdi.c
+++ b/src/northbridge/amd/amdmct/mct_ddr3/mctsdi.c
@@ -18,17 +18,182 @@
  * Foundation, Inc.
  */
 
+static uint8_t fam15_dimm_dic(struct DCTStatStruc *pDCTstat, uint8_t dct, uint8_t dimm, uint8_t rank, uint8_t package_type)
+{
+	uint8_t dic;
+
+	/* Calculate DIC based on recommendations in MR1_dct[1:0] */
+	if (pDCTstat->Status & (1 << SB_LoadReduced)) {
+		/* TODO
+		* LRDIMM unimplemented
+		*/
+		dic = 0x0;
+	} else {
+		dic = 0x1;
+	}
+
+	return dic;
+}
+
+static uint8_t fam15_rttwr(struct DCTStatStruc *pDCTstat, uint8_t dct, uint8_t dimm, uint8_t rank, uint8_t package_type)
+{
+	uint8_t term = 0;
+	sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[dct];
+	uint8_t number_of_dimms = pDCTData->MaxDimmsInstalled;
+	uint8_t frequency_index;
+	uint8_t rank_count = pDCTData->DimmRanks[dimm];
+
+	if (is_fam15h())
+		frequency_index = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0x94) & 0x1f;
+	else
+		frequency_index = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0x94) & 0x7;
+
+	/* FIXME
+	 * Mainboards need to be able to specify the maximum number of DIMMs installable per channel
+	 * For now assume a maximum of 2 DIMMs per channel can be installed
+	 */
+	uint8_t MaxDimmsInstallable = 2;
+
+	if (is_fam15h()) {
+		if (pDCTstat->Status & (1 << SB_Registered)) {
+			/* TODO
+			 * RDIMM unimplemented
+			 */
+		} else {
+			if (package_type == PT_GR) {
+				/* Socket G34: Fam15h BKDG v3.14 Table 56 */
+				if (MaxDimmsInstallable == 1) {
+					term = 0x0;
+				} else if (MaxDimmsInstallable == 2) {
+					if ((number_of_dimms == 2) && (frequency_index == 0x12)) {
+						term = 0x1;
+					} else if (number_of_dimms == 1) {
+						term = 0x0;
+					} else {
+						term = 0x2;
+					}
+				} else if (MaxDimmsInstallable == 3) {
+					if (number_of_dimms == 1) {
+						if (frequency_index <= 0xa) {
+							term = 0x2;
+						} else {
+							if (rank_count < 3) {
+								term = 0x1;
+							} else {
+								term = 0x2;
+							}
+						}
+					} else if (number_of_dimms == 2) {
+						term = 0x2;
+					}
+				}
+			} else {
+				/* TODO
+				* Other sockets unimplemented
+				*/
+			}
+		}
+	}
+
+	return term;
+}
+
+static uint8_t fam15_rttnom(struct DCTStatStruc *pDCTstat, uint8_t dct, uint8_t dimm, uint8_t rank, uint8_t package_type)
+{
+	uint8_t term = 0;
+	sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[dct];
+	uint8_t number_of_dimms = pDCTData->MaxDimmsInstalled;
+	uint8_t frequency_index;
+
+	if (is_fam15h())
+		frequency_index = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0x94) & 0x1f;
+	else
+		frequency_index = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0x94) & 0x7;
+
+	/* FIXME
+	 * Mainboards need to be able to specify the maximum number of DIMMs installable per channel
+	 * For now assume a maximum of 2 DIMMs per channel can be installed
+	 */
+	uint8_t MaxDimmsInstallable = 2;
+
+	if (is_fam15h()) {
+		if (pDCTstat->Status & (1 << SB_LoadReduced)) {
+			/* TODO
+			 * LRDIMM unimplemented
+			 */
+		} else if (pDCTstat->Status & (1 << SB_Registered)) {
+			/* TODO
+			 * RDIMM unimplemented
+			 */
+		} else {
+			if (package_type == PT_GR) {
+				/* Socket G34: Fam15h BKDG v3.14 Table 56 */
+				if (MaxDimmsInstallable == 1) {
+					if ((frequency_index == 0x4) || (frequency_index == 0x6))
+						term = 0x2;
+					else if ((frequency_index == 0xa) || (frequency_index == 0xe))
+						term = 0x1;
+					else
+						term = 0x3;
+				}
+				if (MaxDimmsInstallable == 2) {
+					if (number_of_dimms == 1) {
+						if (frequency_index <= 0x6) {
+							term = 0x2;
+						} else if (frequency_index <= 0xe) {
+							term = 0x1;
+						} else {
+							term = 0x3;
+						}
+					} else {
+						if (frequency_index <= 0xa) {
+							term = 0x3;
+						} else if (frequency_index <= 0xe) {
+							term = 0x5;
+						} else {
+							term = 0x4;
+						}
+					}
+				} else if (MaxDimmsInstallable == 3) {
+					if (number_of_dimms == 1) {
+						term = 0x0;
+					} else if (number_of_dimms == 2) {
+						if (frequency_index <= 0xa) {
+							if (rank == 1) {
+								term = 0x0;
+							} else {
+								term = 0x3;
+							}
+						} else if (frequency_index <= 0xe) {
+							if (rank == 1) {
+								term = 0x0;
+							} else {
+								term = 0x5;
+							}
+						}
+					}
+				}
+			} else {
+				/* TODO
+				 * Other sockets unimplemented
+				 */
+			}
+		}
+	}
+
+	return term;
+}
+
 static void mct_DramControlReg_Init_D(struct MCTStatStruc *pMCTstat,
 				struct DCTStatStruc *pDCTstat, u8 dct);
 
 static void mct_DCTAccessDone(struct DCTStatStruc *pDCTstat, u8 dct)
 {
-	u32 reg_off = 0x100 * dct;
 	u32 dev = pDCTstat->dev_dct;
 	u32 val;
 
 	do {
-		val = Get_NB32(dev, reg_off + 0x98);
+		val = Get_NB32_DCT(dev, dct, 0x98);
 	} while (!(val & (1 << DctAccessDone)));
 }
 
@@ -54,9 +219,15 @@ static u32 swapAddrBits(struct DCTStatStruc *pDCTstat, u32 MR_register_setting,
 			if (MR_register_setting & (1 << 6)) ret |= 1 << 5;
 			if (MR_register_setting & (1 << 7)) ret |= 1 << 8;
 			if (MR_register_setting & (1 << 8)) ret |= 1 << 7;
-			if (MR_register_setting & (1 << 16)) ret |= 1 << 17;
-			if (MR_register_setting & (1 << 17)) ret |= 1 << 16;
-			MR_register_setting &= ~0x301f8;
+			if (is_fam15h()) {
+				if (MR_register_setting & (1 << 18)) ret |= 1 << 19;
+				if (MR_register_setting & (1 << 19)) ret |= 1 << 18;
+				MR_register_setting &= ~0x000c01f8;
+			} else {
+				if (MR_register_setting & (1 << 16)) ret |= 1 << 17;
+				if (MR_register_setting & (1 << 17)) ret |= 1 << 16;
+				MR_register_setting &= ~0x000301f8;
+			}
 			MR_register_setting |= ret;
 		}
 	}
@@ -65,47 +236,76 @@ static u32 swapAddrBits(struct DCTStatStruc *pDCTstat, u32 MR_register_setting,
 
 static void mct_SendMrsCmd(struct DCTStatStruc *pDCTstat, u8 dct, u32 EMRS)
 {
-	u32 reg_off = 0x100 * dct;
 	u32 dev = pDCTstat->dev_dct;
 	u32 val;
 
-	val = Get_NB32(dev, reg_off + 0x7C);
-	val &= ~0xFFFFFF;
+	val = Get_NB32_DCT(dev, dct, 0x7c);
+	val &= ~0x00ffffff;
 	val |= EMRS;
 	val |= 1 << SendMrsCmd;
-	Set_NB32(dev, reg_off + 0x7C, val);
+	Set_NB32_DCT(dev, dct, 0x7c, val);
 
 	do {
-		val = Get_NB32(dev, reg_off + 0x7C);
+		val = Get_NB32_DCT(dev, dct, 0x7c);
 	} while (val & (1 << SendMrsCmd));
 }
 
 static u32 mct_MR2(struct MCTStatStruc *pMCTstat,
 				struct DCTStatStruc *pDCTstat, u8 dct, u32 MrsChipSel)
 {
-	u32 reg_off = 0x100 * dct;
 	u32 dev = pDCTstat->dev_dct;
 	u32 dword, ret;
 
-	ret = 0x20000;
-	ret |= MrsChipSel;
+	if (is_fam15h()) {
+		uint8_t package_type = mctGet_NVbits(NV_PACK_TYPE);
+
+		/* The formula for chip select number is: CS = dimm*2+rank */
+		uint8_t dimm = MrsChipSel / 2;
+		uint8_t rank = MrsChipSel % 2;
 
-	/* program MrsAddress[5:3]=CAS write latency (CWL):
-	 * based on F2x[1,0]84[Tcwl] */
-	dword = Get_NB32(dev, reg_off + 0x84);
-	dword = mct_AdjustSPDTimings(pMCTstat, pDCTstat, dword);
+		/* FIXME: These parameters should be configurable
+		 * For now, err on the side of caution and enable automatic 2x refresh
+		 * when the DDR temperature rises above the internal limits
+		 */
+		uint8_t force_2x_self_refresh = 0;	/* ASR */
+		uint8_t auto_2x_self_refresh = 1;	/* SRT */
 
-	ret |= ((dword >> 20) & 7) << 3;
+		ret = 0x80000;
+		ret |= (MrsChipSel << 21);
 
-	/* program MrsAddress[6]=auto self refresh method (ASR):
-	   based on F2x[1,0]84[ASR]
-	   program MrsAddress[7]=self refresh temperature range (SRT):
-	   based on F2x[1,0]84[ASR and SRT] */
-	ret |= ((dword >> 18) & 3) << 6;
+		/* Set self refresh parameters */
+		ret |= (force_2x_self_refresh << 6);
+		ret |= (auto_2x_self_refresh << 7);
 
-	/* program MrsAddress[10:9]=dynamic termination during writes (RTT_WR)
-	   based on F2x[1,0]84[DramTermDyn] */
-	ret |= ((dword >> 10) & 3) << 9;
+		/* Obtain Tcwl, adjust, and set CWL with the adjusted value */
+		dword = Get_NB32_DCT(dev, dct, 0x20c) & 0x1f;
+		ret |= ((dword - 5) << 3);
+
+		/* Obtain and set RttWr */
+		ret |= (fam15_rttwr(pDCTstat, dct, dimm, rank, package_type) << 9);
+	} else {
+		ret = 0x20000;
+		ret |= (MrsChipSel << 20);
+
+		/* program MrsAddress[5:3]=CAS write latency (CWL):
+		 * based on F2x[1,0]84[Tcwl] */
+		dword = Get_NB32_DCT(dev, dct, 0x84);
+		dword = mct_AdjustSPDTimings(pMCTstat, pDCTstat, dword);
+
+		ret |= ((dword >> 20) & 7) << 3;
+
+		/* program MrsAddress[6]=auto self refresh method (ASR):
+		 * based on F2x[1,0]84[ASR]
+		 * program MrsAddress[7]=self refresh temperature range (SRT):
+		 * based on F2x[1,0]84[ASR and SRT]
+		 */
+		ret |= ((dword >> 18) & 3) << 6;
+
+		/* program MrsAddress[10:9]=dynamic termination during writes (RTT_WR)
+		 * based on F2x[1,0]84[DramTermDyn]
+		 */
+		ret |= ((dword >> 10) & 3) << 9;
+	}
 
 	return ret;
 }
@@ -113,20 +313,28 @@ static u32 mct_MR2(struct MCTStatStruc *pMCTstat,
 static u32 mct_MR3(struct MCTStatStruc *pMCTstat,
 				struct DCTStatStruc *pDCTstat, u8 dct, u32 MrsChipSel)
 {
-	u32 reg_off = 0x100 * dct;
 	u32 dev = pDCTstat->dev_dct;
 	u32 dword, ret;
 
-	ret = 0x30000;
-	ret |= MrsChipSel;
+	if (is_fam15h()) {
+		ret = 0xc0000;
+		ret |= (MrsChipSel << 21);
 
-	/* program MrsAddress[1:0]=multi purpose register address location
-	   (MPR Location):based on F2x[1,0]84[MprLoc]
-	   program MrsAddress[2]=multi purpose register
-	   (MPR):based on F2x[1,0]84[MprEn]
-	*/
-	dword = Get_NB32(dev, reg_off + 0x84);
-	ret |= (dword >> 24) & 7;
+		/* Program MPR and MPRLoc to 0 */
+		// ret |= 0x0;		/* MPR */
+		// ret |= (0x0 << 2);	/* MPRLoc */
+	} else {
+		ret = 0x30000;
+		ret |= (MrsChipSel << 20);
+
+		/* program MrsAddress[1:0]=multi purpose register address location
+		 * (MPR Location):based on F2x[1,0]84[MprLoc]
+		 * program MrsAddress[2]=multi purpose register
+		 * (MPR):based on F2x[1,0]84[MprEn]
+		 */
+		dword = Get_NB32_DCT(dev, dct, 0x84);
+		ret |= (dword >> 24) & 7;
+	}
 
 	return ret;
 }
@@ -134,48 +342,93 @@ static u32 mct_MR3(struct MCTStatStruc *pMCTstat,
 static u32 mct_MR1(struct MCTStatStruc *pMCTstat,
 				struct DCTStatStruc *pDCTstat, u8 dct, u32 MrsChipSel)
 {
-	u32 reg_off = 0x100 * dct;
 	u32 dev = pDCTstat->dev_dct;
 	u32 dword, ret;
 
-	ret = 0x10000;
-	ret |= MrsChipSel;
-
-	/* program MrsAddress[5,1]=output driver impedance control (DIC):
-	 * based on F2x[1,0]84[DrvImpCtrl] */
-	dword = Get_NB32(dev, reg_off + 0x84);
-	if (dword & (1 << 3))
-		ret |= 1 << 5;
-	if (dword & (1 << 2))
-		ret |= 1 << 1;
-
-	/* program MrsAddress[9,6,2]=nominal termination resistance of ODT (RTT):
-	   based on F2x[1,0]84[DramTerm] */
-	if (!(pDCTstat->Status & (1 << SB_Registered))) {
-		if (dword & (1 << 9))
-			ret |= 1 << 9;
-		if (dword & (1 << 8))
-			ret |= 1 << 6;
-		if (dword & (1 << 7))
-			ret |= 1 << 2;
+	if (is_fam15h()) {
+		uint8_t package_type = mctGet_NVbits(NV_PACK_TYPE);
+
+		/* Set defaults */
+		uint8_t qoff = 0;	/* Enable output buffers */
+		uint8_t wrlvl = 0;	/* Disable write levelling */
+		uint8_t tqds = 0;
+		uint8_t rttnom = 0;
+		uint8_t dic = 0;
+		uint8_t additive_latency = 0;
+		uint8_t dll_enable = 0;
+
+		ret = 0x40000;
+		ret |= (MrsChipSel << 21);
+
+		/* The formula for chip select number is: CS = dimm*2+rank */
+		uint8_t dimm = MrsChipSel / 2;
+		uint8_t rank = MrsChipSel % 2;
+
+		/* Determine if TQDS should be set */
+		if ((pDCTstat->Dimmx8Present & (1 << dimm))
+			&& (((dimm & 0x1)?(pDCTstat->Dimmx4Present&0x55):(pDCTstat->Dimmx4Present&0xaa)) != 0x0)
+			&& (pDCTstat->Status & (1 << SB_LoadReduced)))
+			tqds = 1;
+
+		/* Obtain RttNom */
+		rttnom = fam15_rttnom(pDCTstat, dct, dimm, rank, package_type);
+
+		/* Obtain DIC */
+		dic = fam15_dimm_dic(pDCTstat, dct, dimm, rank, package_type);
+
+		/* Load data into MRS word */
+		ret |= (qoff & 0x1) << 12;
+		ret |= (tqds & 0x1) << 11;
+		ret |= ((rttnom & 0x4) >> 2) << 9;
+		ret |= ((rttnom & 0x2) >> 1) << 6;
+		ret |= ((rttnom & 0x1) >> 0) << 2;
+		ret |= (wrlvl & 0x1) << 7;
+		ret |= ((dic & 0x2) >> 1) << 5;
+		ret |= ((dic & 0x1) >> 0) << 1;
+		ret |= (additive_latency & 0x3) << 3;
+		ret |= (dll_enable & 0x1);
 	} else {
-		ret |= mct_MR1Odt_RDimm(pMCTstat, pDCTstat, dct, MrsChipSel);
-	}
+		ret = 0x10000;
+		ret |= (MrsChipSel << 20);
+
+		/* program MrsAddress[5,1]=output driver impedance control (DIC):
+		 * based on F2x[1,0]84[DrvImpCtrl]
+		 */
+		dword = Get_NB32_DCT(dev, dct, 0x84);
+		if (dword & (1 << 3))
+			ret |= 1 << 5;
+		if (dword & (1 << 2))
+			ret |= 1 << 1;
+
+		/* program MrsAddress[9,6,2]=nominal termination resistance of ODT (RTT):
+		 * based on F2x[1,0]84[DramTerm]
+		 */
+		if (!(pDCTstat->Status & (1 << SB_Registered))) {
+			if (dword & (1 << 9))
+				ret |= 1 << 9;
+			if (dword & (1 << 8))
+				ret |= 1 << 6;
+			if (dword & (1 << 7))
+				ret |= 1 << 2;
+		} else {
+			ret |= mct_MR1Odt_RDimm(pMCTstat, pDCTstat, dct, MrsChipSel);
+		}
 
-	/* program MrsAddress[11]=TDQS: based on F2x[1,0]94[RDqsEn] */
-	if (Get_NB32(dev, reg_off + 0x94) & (1 << RDqsEn)) {
-		u8 bit;
-		/* Set TDQS=1b for x8 DIMM, TDQS=0b for x4 DIMM, when mixed x8 & x4 */
-		bit = (ret >> 21) << 1;
-		if ((dct & 1) != 0)
-			bit ++;
-		if (pDCTstat->Dimmx8Present & (1 << bit))
-			ret |= 1 << 11;
-	}
+		/* program MrsAddress[11]=TDQS: based on F2x[1,0]94[RDqsEn] */
+		if (Get_NB32_DCT(dev, dct, 0x94) & (1 << RDqsEn)) {
+			u8 bit;
+			/* Set TDQS=1b for x8 DIMM, TDQS=0b for x4 DIMM, when mixed x8 & x4 */
+			bit = (ret >> 21) << 1;
+			if ((dct & 1) != 0)
+				bit ++;
+			if (pDCTstat->Dimmx8Present & (1 << bit))
+				ret |= 1 << 11;
+		}
 
-	/* program MrsAddress[12]=QOFF: based on F2x[1,0]84[Qoff] */
-	if (dword & (1 << 13))
-		ret |= 1 << 12;
+		/* program MrsAddress[12]=QOFF: based on F2x[1,0]84[Qoff] */
+		if (dword & (1 << 13))
+			ret |= 1 << 12;
+	}
 
 	return ret;
 }
@@ -183,60 +436,139 @@ static u32 mct_MR1(struct MCTStatStruc *pMCTstat,
 static u32 mct_MR0(struct MCTStatStruc *pMCTstat,
 				struct DCTStatStruc *pDCTstat, u8 dct, u32 MrsChipSel)
 {
-	u32 reg_off = 0x100 * dct;
 	u32 dev = pDCTstat->dev_dct;
 	u32 dword, ret, dword2;
 
-	ret = 0x00000;
-	ret |= MrsChipSel;
-
-	/* program MrsAddress[1:0]=burst length and control method
-	   (BL):based on F2x[1,0]84[BurstCtrl] */
-	dword = Get_NB32(dev, reg_off + 0x84);
-	ret |= dword & 3;
-
-	/* program MrsAddress[3]=1 (BT):interleaved */
-	ret |= 1 << 3;
-
-	/* program MrsAddress[6:4,2]=read CAS latency
-	   (CL):based on F2x[1,0]88[Tcl] */
-	dword2 = Get_NB32(dev, reg_off + 0x88);
-	ret |= (dword2 & 0x7) << 4;		/* F2x88[2:0] to MrsAddress[6:4] */
-	ret |= ((dword2 & 0x8) >> 3) << 2;	/* F2x88[3] to MrsAddress[2] */
-
-	/* program MrsAddress[12]=0 (PPD):slow exit */
-	if (dword & (1 << 23))
-		ret |= 1 << 12;
-
-	/* program MrsAddress[11:9]=write recovery for auto-precharge
-	   (WR):based on F2x[1,0]84[Twr] */
-	ret |= ((dword >> 4) & 7) << 9;
-
-	/* program MrsAddress[8]=1 (DLL):DLL reset
-	   just issue DLL reset at first time */
-	ret |= 1 << 8;
+	if (is_fam15h()) {
+		ret = 0x00000;
+		ret |= (MrsChipSel << 21);
+
+		/* Set defaults */
+		uint8_t ppd = 0;
+		uint8_t wr_ap = 0;
+		uint8_t dll_reset = 1;
+		uint8_t test_mode = 0;
+		uint8_t cas_latency = 0;
+		uint8_t read_burst_type = 1;
+		uint8_t burst_length = 0;
+
+		/* Obtain PchgPDModeSel */
+		dword = Get_NB32_DCT(dev, dct, 0x84);
+		ppd = (dword >> 23) & 0x1;
+
+		/* Obtain Twr */
+		dword = Get_NB32_DCT(dev, dct, 0x22c) & 0x1f;
+
+		/* Calculate wr_ap (Fam15h BKDG v3.14 Table 82) */
+		if (dword == 0x10)
+			wr_ap = 0x0;
+		else if (dword == 0x5)
+			wr_ap = 0x1;
+		else if (dword == 0x6)
+			wr_ap = 0x2;
+		else if (dword == 0x7)
+			wr_ap = 0x3;
+		else if (dword == 0x8)
+			wr_ap = 0x4;
+		else if (dword == 0xa)
+			wr_ap = 0x5;
+		else if (dword == 0xc)
+			wr_ap = 0x6;
+		else if (dword == 0xe)
+			wr_ap = 0x7;
+
+		/* Obtain Tcl */
+		dword = Get_NB32_DCT(dev, dct, 0x200) & 0x1f;
+
+		/* Calculate cas_latency (Fam15h BKDG v3.14 Table 83) */
+		if (dword == 0x5)
+			cas_latency = 0x2;
+		else if (dword == 0x6)
+			cas_latency = 0x4;
+		else if (dword == 0x7)
+			cas_latency = 0x6;
+		else if (dword == 0x8)
+			cas_latency = 0x8;
+		else if (dword == 0x9)
+			cas_latency = 0xa;
+		else if (dword == 0xa)
+			cas_latency = 0xc;
+		else if (dword == 0xb)
+			cas_latency = 0xe;
+		else if (dword == 0xc)
+			cas_latency = 0x1;
+		else if (dword == 0xd)
+			cas_latency = 0x3;
+		else if (dword == 0xe)
+			cas_latency = 0x5;
+		else if (dword == 0xf)
+			cas_latency = 0x7;
+		else if (dword == 0x10)
+			cas_latency = 0x9;
+
+		/* Obtain BurstCtrl */
+		burst_length = Get_NB32_DCT(dev, dct, 0x84) & 0x3;
+
+		/* Load data into MRS word */
+		ret |= (ppd & 0x1) << 12;
+		ret |= (wr_ap & 0x3) << 9;
+		ret |= (dll_reset & 0x1) << 8;
+		ret |= (test_mode & 0x1) << 7;
+		ret |= ((cas_latency & 0xe) >> 1) << 4;
+		ret |= ((cas_latency & 0x1) >> 0) << 2;
+		ret |= (read_burst_type & 0x1) << 3;
+		ret |= (burst_length & 0x3);
+	} else {
+		ret = 0x00000;
+		ret |= (MrsChipSel << 20);
+
+		/* program MrsAddress[1:0]=burst length and control method
+		(BL):based on F2x[1,0]84[BurstCtrl] */
+		dword = Get_NB32_DCT(dev, dct, 0x84);
+		ret |= dword & 3;
+
+		/* program MrsAddress[3]=1 (BT):interleaved */
+		ret |= 1 << 3;
+
+		/* program MrsAddress[6:4,2]=read CAS latency
+		(CL):based on F2x[1,0]88[Tcl] */
+		dword2 = Get_NB32_DCT(dev, dct, 0x88);
+		ret |= (dword2 & 0x7) << 4;		/* F2x88[2:0] to MrsAddress[6:4] */
+		ret |= ((dword2 & 0x8) >> 3) << 2;	/* F2x88[3] to MrsAddress[2] */
+
+		/* program MrsAddress[12]=0 (PPD):slow exit */
+		if (dword & (1 << 23))
+			ret |= 1 << 12;
+
+		/* program MrsAddress[11:9]=write recovery for auto-precharge
+		(WR):based on F2x[1,0]84[Twr] */
+		ret |= ((dword >> 4) & 7) << 9;
+
+		/* program MrsAddress[8]=1 (DLL):DLL reset
+		just issue DLL reset at first time */
+		ret |= 1 << 8;
+	}
 
 	return ret;
 }
 
 static void mct_SendZQCmd(struct DCTStatStruc *pDCTstat, u8 dct)
 {
-	u32 reg_off = 0x100 * dct;
 	u32 dev = pDCTstat->dev_dct;
 	u32 dword;
 
 	/*1.Program MrsAddress[10]=1
 	  2.Set SendZQCmd=1
 	 */
-	dword = Get_NB32(dev, reg_off + 0x7C);
+	dword = Get_NB32_DCT(dev, dct, 0x7C);
 	dword &= ~0xFFFFFF;
 	dword |= 1 << 10;
 	dword |= 1 << SendZQCmd;
-	Set_NB32(dev, reg_off + 0x7C, dword);
+	Set_NB32_DCT(dev, dct, 0x7C, dword);
 
 	/* Wait for SendZQCmd=0 */
 	do {
-		dword = Get_NB32(dev, reg_off + 0x7C);
+		dword = Get_NB32_DCT(dev, dct, 0x7C);
 	} while (dword & (1 << SendZQCmd));
 
 	/* 4.Wait 512 MEMCLKs */
@@ -248,31 +580,30 @@ void mct_DramInit_Sw_D(struct MCTStatStruc *pMCTstat,
 {
 	u8 MrsChipSel;
 	u32 dword;
-	u32 reg_off = 0x100 * dct;
 	u32 dev = pDCTstat->dev_dct;
 
-	if (pDCTstat->DIMMAutoSpeed == 4) {
+	if (pDCTstat->DIMMAutoSpeed == mhz_to_memclk_config(mctGet_NVbits(NV_MIN_MEMCLK))) {
 		/* 3.Program F2x[1,0]7C[EnDramInit]=1 */
-		dword = Get_NB32(dev, reg_off + 0x7C);
+		dword = Get_NB32_DCT(dev, dct, 0x7c);
 		dword |= 1 << EnDramInit;
-		Set_NB32(dev, reg_off + 0x7C, dword);
+		Set_NB32_DCT(dev, dct, 0x7c, dword);
 		mct_DCTAccessDone(pDCTstat, dct);
 
 		/* 4.wait 200us */
 		mct_Wait(40000);
 
-		/* 5.On revision C processors, program F2x[1, 0]7C[DeassertMemRstX] = 1. */
-		dword = Get_NB32(dev, reg_off + 0x7C);
+		/* 5.Program F2x[1, 0]7C[DeassertMemRstX] = 1. */
+		dword = Get_NB32_DCT(dev, dct, 0x7c);
 		dword |= 1 << DeassertMemRstX;
-		Set_NB32(dev, reg_off + 0x7C, dword);
+		Set_NB32_DCT(dev, dct, 0x7c, dword);
 
 		/* 6.wait 500us */
 		mct_Wait(200000);
 
 		/* 7.Program F2x[1,0]7C[AssertCke]=1 */
-		dword = Get_NB32(dev, reg_off + 0x7C);
+		dword = Get_NB32_DCT(dev, dct, 0x7c);
 		dword |= 1 << AssertCke;
-		Set_NB32(dev, reg_off + 0x7C, dword);
+		Set_NB32_DCT(dev, dct, 0x7c, dword);
 
 		/* 8.wait 360ns */
 		mct_Wait(80);
@@ -281,6 +612,13 @@ void mct_DramInit_Sw_D(struct MCTStatStruc *pMCTstat,
 		 * must be done for each chip select pair */
 		if (pDCTstat->Status & (1 << SB_Registered))
 			mct_DramControlReg_Init_D(pMCTstat, pDCTstat, dct);
+
+		/* The following steps are performed with load reduced DIMMs only and
+		 * must be done for each DIMM */
+		// if (pDCTstat->Status & (1 << SB_LoadReduced))
+			/* TODO
+			 * Implement LRDIMM configuration
+			 */
 	}
 
 	/* The following steps are performed once for unbuffered DIMMs and once for each
@@ -289,23 +627,23 @@ void mct_DramInit_Sw_D(struct MCTStatStruc *pMCTstat,
 		if (pDCTstat->CSPresent & (1 << MrsChipSel)) {
 			u32 EMRS;
 			/* 13.Send EMRS(2) */
-			EMRS = mct_MR2(pMCTstat, pDCTstat, dct, MrsChipSel << 20);
+			EMRS = mct_MR2(pMCTstat, pDCTstat, dct, MrsChipSel);
 			EMRS = swapAddrBits(pDCTstat, EMRS, MrsChipSel, dct);
 			mct_SendMrsCmd(pDCTstat, dct, EMRS);
 			/* 14.Send EMRS(3). Ordinarily at this time, MrsAddress[2:0]=000b */
-			EMRS= mct_MR3(pMCTstat, pDCTstat, dct, MrsChipSel << 20);
+			EMRS= mct_MR3(pMCTstat, pDCTstat, dct, MrsChipSel);
 			EMRS = swapAddrBits(pDCTstat, EMRS, MrsChipSel, dct);
 			mct_SendMrsCmd(pDCTstat, dct, EMRS);
 			/* 15.Send EMRS(1) */
-			EMRS= mct_MR1(pMCTstat, pDCTstat, dct, MrsChipSel << 20);
+			EMRS= mct_MR1(pMCTstat, pDCTstat, dct, MrsChipSel);
 			EMRS = swapAddrBits(pDCTstat, EMRS, MrsChipSel, dct);
 			mct_SendMrsCmd(pDCTstat, dct, EMRS);
 			/* 16.Send MRS with MrsAddress[8]=1(reset the DLL) */
-			EMRS= mct_MR0(pMCTstat, pDCTstat, dct, MrsChipSel << 20);
+			EMRS= mct_MR0(pMCTstat, pDCTstat, dct, MrsChipSel);
 			EMRS = swapAddrBits(pDCTstat, EMRS, MrsChipSel, dct);
 			mct_SendMrsCmd(pDCTstat, dct, EMRS);
 
-			if (pDCTstat->DIMMAutoSpeed == 4)
+			if (pDCTstat->DIMMAutoSpeed == mhz_to_memclk_config(mctGet_NVbits(NV_MIN_MEMCLK)))
 				if (!(pDCTstat->Status & (1 << SB_Registered)))
 					break; /* For UDIMM, only send MR commands once per channel */
 		}
@@ -314,16 +652,15 @@ void mct_DramInit_Sw_D(struct MCTStatStruc *pMCTstat,
 				MrsChipSel ++;
 	}
 
-	mct_Wait(100000);
-
-	if (pDCTstat->DIMMAutoSpeed == 4) {
+	if (pDCTstat->DIMMAutoSpeed == mhz_to_memclk_config(mctGet_NVbits(NV_MIN_MEMCLK))) {
 		/* 17.Send two ZQCL commands */
 		mct_SendZQCmd(pDCTstat, dct);
 		mct_SendZQCmd(pDCTstat, dct);
+
 		/* 18.Program F2x[1,0]7C[EnDramInit]=0 */
-		dword = Get_NB32(dev, reg_off + 0x7C);
+		dword = Get_NB32_DCT(dev, dct, 0x7C);
 		dword &= ~(1 << EnDramInit);
-		Set_NB32(dev, reg_off + 0x7C, dword);
+		Set_NB32_DCT(dev, dct, 0x7C, dword);
 		mct_DCTAccessDone(pDCTstat, dct);
 	}
 }
diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctsrc.c b/src/northbridge/amd/amdmct/mct_ddr3/mctsrc.c
index 91e8f77..011a94f 100644
--- a/src/northbridge/amd/amdmct/mct_ddr3/mctsrc.c
+++ b/src/northbridge/amd/amdmct/mct_ddr3/mctsrc.c
@@ -23,7 +23,10 @@
  Description: Receiver En and DQS Timing Training feature for DDR 3 MCT
 ******************************************************************************/
 
-static void dqsTrainRcvrEn_SW(struct MCTStatStruc *pMCTstat,
+static int32_t abs(int32_t val);
+static void dqsTrainRcvrEn_SW_Fam10(struct MCTStatStruc *pMCTstat,
+				struct DCTStatStruc *pDCTstat, u8 Pass);
+static void dqsTrainRcvrEn_SW_Fam15(struct MCTStatStruc *pMCTstat,
 				struct DCTStatStruc *pDCTstat, u8 Pass);
 static void mct_InitDQSPos4RcvrEn_D(struct MCTStatStruc *pMCTstat,
 					 struct DCTStatStruc *pDCTstat);
@@ -32,7 +35,7 @@ static void InitDQSPos4RcvrEn_D(struct MCTStatStruc *pMCTstat,
 static void CalcEccDQSRcvrEn_D(struct MCTStatStruc *pMCTstat,
 				struct DCTStatStruc *pDCTstat, u8 Channel);
 static void mct_SetMaxLatency_D(struct DCTStatStruc *pDCTstat, u8 Channel, u16 DQSRcvEnDly);
-static void fenceDynTraining_D(struct MCTStatStruc *pMCTstat,
+static uint32_t fenceDynTraining_D(struct MCTStatStruc *pMCTstat,
 			struct DCTStatStruc *pDCTstat, u8 dct);
 static void mct_DisableDQSRcvEn_D(struct DCTStatStruc *pDCTstat);
 
@@ -89,11 +92,154 @@ static void SetupRcvrPattern(struct MCTStatStruc *pMCTstat,
 void mct_TrainRcvrEn_D(struct MCTStatStruc *pMCTstat,
 			struct DCTStatStruc *pDCTstat, u8 Pass)
 {
-	if(mct_checkNumberOfDqsRcvEn_1Pass(Pass))
-		dqsTrainRcvrEn_SW(pMCTstat, pDCTstat, Pass);
+	if(mct_checkNumberOfDqsRcvEn_1Pass(Pass)) {
+		if (is_fam15h())
+			dqsTrainRcvrEn_SW_Fam15(pMCTstat, pDCTstat, Pass);
+		else
+			dqsTrainRcvrEn_SW_Fam10(pMCTstat, pDCTstat, Pass);
+	}
 }
 
-static void read_dqs_write_timing_control_registers(uint16_t* current_total_delay, uint32_t dev, uint8_t dimm, uint32_t index_reg)
+static uint16_t fam15_receiver_enable_training_seed(struct DCTStatStruc *pDCTstat, uint8_t dct, uint8_t dimm, uint8_t rank, uint8_t package_type)
+{
+	uint32_t dword;
+	uint16_t seed = 0;
+
+	/* FIXME
+	 * Mainboards need to be able to specify the maximum number of DIMMs installable per channel
+	 * For now assume a maximum of 2 DIMMs per channel can be installed
+	 */
+	uint8_t MaxDimmsInstallable = 2;
+
+	uint8_t channel = dct;
+	if (package_type == PT_GR) {
+		/* Get the internal node number */
+		dword = Get_NB32(pDCTstat->dev_nbmisc, 0xe8);
+		dword = (dword >> 30) & 0x3;
+		if (dword == 1) {
+			channel += 2;
+		}
+	}
+
+	if (pDCTstat->Status & (1 << SB_Registered)) {
+		if (package_type == PT_GR) {
+			/* Socket G34: Fam15h BKDG v3.14 Table 99 */
+			if (MaxDimmsInstallable == 1) {
+				if (channel == 0)
+					seed = 0x43;
+				else if (channel == 1)
+					seed = 0x3f;
+				else if (channel == 2)
+					seed = 0x3a;
+				else if (channel == 3)
+					seed = 0x35;
+			} else if (MaxDimmsInstallable == 2) {
+				if (channel == 0)
+					seed = 0x54;
+				else if (channel == 1)
+					seed = 0x4d;
+				else if (channel == 2)
+					seed = 0x45;
+				else if (channel == 3)
+					seed = 0x40;
+			} else if (MaxDimmsInstallable == 3) {
+				if (channel == 0)
+					seed = 0x6b;
+				else if (channel == 1)
+					seed = 0x5e;
+				else if (channel == 2)
+					seed = 0x4b;
+				else if (channel == 3)
+					seed = 0x3d;
+			}
+		} else if (package_type == PT_C3) {
+			/* Socket C32: Fam15h BKDG v3.14 Table 100 */
+			if ((MaxDimmsInstallable == 1) || (MaxDimmsInstallable == 2)) {
+				if (channel == 0)
+					seed = 0x3f;
+				else if (channel == 1)
+					seed = 0x3e;
+			} else if (MaxDimmsInstallable == 3) {
+				if (channel == 0)
+					seed = 0x47;
+				else if (channel == 1)
+					seed = 0x38;
+			}
+		}
+	} else if (pDCTstat->Status & (1 << SB_LoadReduced)) {
+		if (package_type == PT_GR) {
+			/* Socket G34: Fam15h BKDG v3.14 Table 99 */
+			if (MaxDimmsInstallable == 1) {
+				if (channel == 0)
+					seed = 0x123;
+				else if (channel == 1)
+					seed = 0x122;
+				else if (channel == 2)
+					seed = 0x112;
+				else if (channel == 3)
+					seed = 0x102;
+			}
+		} else if (package_type == PT_C3) {
+			/* Socket C32: Fam15h BKDG v3.14 Table 100 */
+			if (channel == 0)
+				seed = 0x132;
+			else if (channel == 1)
+				seed = 0x122;
+		}
+	} else {
+		if (package_type == PT_GR) {
+			/* Socket G34: Fam15h BKDG v3.14 Table 99 */
+			if (MaxDimmsInstallable == 1) {
+				if (channel == 0)
+					seed = 0x3e;
+				else if (channel == 1)
+					seed = 0x38;
+				else if (channel == 2)
+					seed = 0x37;
+				else if (channel == 3)
+					seed = 0x31;
+			} else if (MaxDimmsInstallable == 2) {
+				if (channel == 0)
+					seed = 0x51;
+				else if (channel == 1)
+					seed = 0x4a;
+				else if (channel == 2)
+					seed = 0x46;
+				else if (channel == 3)
+					seed = 0x3f;
+			} else if (MaxDimmsInstallable == 3) {
+				if (channel == 0)
+					seed = 0x5e;
+				else if (channel == 1)
+					seed = 0x52;
+				else if (channel == 2)
+					seed = 0x48;
+				else if (channel == 3)
+					seed = 0x3c;
+			}
+		} else if (package_type == PT_C3) {
+			/* Socket C32: Fam15h BKDG v3.14 Table 100 */
+			if ((MaxDimmsInstallable == 1) || (MaxDimmsInstallable == 2)) {
+				if (channel == 0)
+					seed = 0x39;
+				else if (channel == 1)
+					seed = 0x32;
+			} else if (MaxDimmsInstallable == 3) {
+				if (channel == 0)
+					seed = 0x45;
+				else if (channel == 1)
+					seed = 0x37;
+			}
+		} else if (package_type == PT_M2) {
+			/* Socket AM3: Fam15h BKDG v3.14 Table 101 */
+			seed = 0x3a;
+		}
+	}
+
+	return seed;
+}
+
+static void read_dqs_write_timing_control_registers(uint16_t* current_total_delay, uint32_t dev, uint8_t dct, uint8_t dimm, uint32_t index_reg)
 {
 	uint8_t lane;
 	uint32_t dword;
@@ -111,7 +257,7 @@ static void read_dqs_write_timing_control_registers(uint16_t* current_total_dela
 		if (lane == 8)
 			wdt_reg = 0x32;
 		wdt_reg += dimm * 3;
-		dword = Get_NB32_index_wait(dev, index_reg, wdt_reg);
+		dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, wdt_reg);
 		if ((lane == 7) || (lane == 5) || (lane == 3) || (lane == 1))
 			current_total_delay[lane] = (dword & 0x00ff0000) >> 16;
 		if ((lane == 8) || (lane == 6) || (lane == 4) || (lane == 2) || (lane == 0))
@@ -119,12 +265,124 @@ static void read_dqs_write_timing_control_registers(uint16_t* current_total_dela
 	}
 }
 
-static void write_dqs_receiver_enable_control_registers(uint16_t* current_total_delay, uint32_t dev, uint8_t dimm, uint32_t index_reg)
+#ifdef UNUSED_CODE
+static void write_dqs_write_timing_control_registers(uint16_t* current_total_delay, uint32_t dev, uint8_t dct, uint8_t dimm, uint32_t index_reg)
+{
+	uint8_t lane;
+	uint32_t dword;
+
+	for (lane = 0; lane < MAX_BYTE_LANES; lane++) {
+		uint32_t ret_reg;
+		if ((lane == 0) || (lane == 1))
+			ret_reg = 0x30;
+		if ((lane == 2) || (lane == 3))
+			ret_reg = 0x31;
+		if ((lane == 4) || (lane == 5))
+			ret_reg = 0x40;
+		if ((lane == 6) || (lane == 7))
+			ret_reg = 0x41;
+		if (lane == 8)
+			ret_reg = 0x32;
+		ret_reg += dimm * 3;
+		dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, ret_reg);
+		if ((lane == 7) || (lane == 5) || (lane == 3) || (lane == 1)) {
+			dword &= ~(0xff << 16);
+			dword |= (current_total_delay[lane] & 0xff) << 16;
+		}
+		if ((lane == 8) || (lane == 6) || (lane == 4) || (lane == 2) || (lane == 0)) {
+			dword &= ~0xff;
+			dword |= current_total_delay[lane] & 0xff;
+		}
+		Set_NB32_index_wait_DCT(dev, dct, index_reg, ret_reg, dword);
+	}
+}
+#endif
+
+static void write_write_data_timing_control_registers(uint16_t* current_total_delay, uint32_t dev, uint8_t dct, uint8_t dimm, uint32_t index_reg)
+{
+	uint8_t lane;
+	uint32_t dword;
+
+	for (lane = 0; lane < MAX_BYTE_LANES; lane++) {
+		uint32_t wdt_reg;
+
+		/* Calculate Write Data Timing register location */
+		if ((lane == 0) || (lane == 1) || (lane == 2) || (lane == 3))
+			wdt_reg = 0x1;
+		if ((lane == 4) || (lane == 5) || (lane == 6) || (lane == 7))
+			wdt_reg = 0x2;
+		if (lane == 8)
+			wdt_reg = 0x3;
+		wdt_reg |= (dimm << 8);
+
+		/* Set Write Data Timing register values */
+		dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, wdt_reg);
+		if ((lane == 7) || (lane == 3)) {
+			dword &= ~(0x7f << 24);
+			dword |= (current_total_delay[lane] & 0x7f) << 24;
+		}
+		if ((lane == 6) || (lane == 2)) {
+			dword &= ~(0x7f << 16);
+			dword |= (current_total_delay[lane] & 0x7f) << 16;
+		}
+		if ((lane == 5) || (lane == 1)) {
+			dword &= ~(0x7f << 8);
+			dword |= (current_total_delay[lane] & 0x7f) << 8;
+		}
+		if ((lane == 8) || (lane == 4) || (lane == 0)) {
+			dword &= ~0x7f;
+			dword |= current_total_delay[lane] & 0x7f;
+		}
+		Set_NB32_index_wait_DCT(dev, dct, index_reg, wdt_reg, dword);
+	}
+}
+
+static void read_dqs_receiver_enable_control_registers(uint16_t* current_total_delay, uint32_t dev, uint8_t dct, uint8_t dimm, uint32_t index_reg)
+{
+	uint8_t lane;
+	uint32_t mask;
+	uint32_t dword;
+
+	if (is_fam15h())
+		mask = 0x3ff;
+	else
+		mask = 0x1ff;
+
+	for (lane = 0; lane < MAX_BYTE_LANES; lane++) {
+		uint32_t ret_reg;
+		if ((lane == 0) || (lane == 1))
+			ret_reg = 0x10;
+		if ((lane == 2) || (lane == 3))
+			ret_reg = 0x11;
+		if ((lane == 4) || (lane == 5))
+			ret_reg = 0x20;
+		if ((lane == 6) || (lane == 7))
+			ret_reg = 0x21;
+		if (lane == 8)
+			ret_reg = 0x12;
+		ret_reg += dimm * 3;
+		dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, ret_reg);
+		if ((lane == 7) || (lane == 5) || (lane == 3) || (lane == 1)) {
+			current_total_delay[lane] = (dword & (mask << 16)) >> 16;
+		}
+		if ((lane == 8) || (lane == 6) || (lane == 4) || (lane == 2) || (lane == 0)) {
+			current_total_delay[lane] = dword & mask;
+		}
+	}
+}
+
+static void write_dqs_receiver_enable_control_registers(uint16_t* current_total_delay, uint32_t dev, uint8_t dct, uint8_t dimm, uint32_t index_reg)
 {
 	uint8_t lane;
+	uint32_t mask;
 	uint32_t dword;
 
-	for (lane = 0; lane < 8; lane++) {
+	if (is_fam15h())
+		mask = 0x3ff;
+	else
+		mask = 0x1ff;
+
+	for (lane = 0; lane < MAX_BYTE_LANES; lane++) {
 		uint32_t ret_reg;
 		if ((lane == 0) || (lane == 1))
 			ret_reg = 0x10;
@@ -134,17 +392,125 @@ static void write_dqs_receiver_enable_control_registers(uint16_t* current_total_
 			ret_reg = 0x20;
 		if ((lane == 6) || (lane == 7))
 			ret_reg = 0x21;
+		if (lane == 8)
+			ret_reg = 0x12;
 		ret_reg += dimm * 3;
-		dword = Get_NB32_index_wait(dev, index_reg, ret_reg);
+		dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, ret_reg);
 		if ((lane == 7) || (lane == 5) || (lane == 3) || (lane == 1)) {
-			dword &= ~(0x1ff << 16);
-			dword |= (current_total_delay[lane] & 0x1ff) << 16;
+			dword &= ~(mask << 16);
+			dword |= (current_total_delay[lane] & mask) << 16;
 		}
-		if ((lane == 6) || (lane == 4) || (lane == 2) || (lane == 0)) {
-			dword &= ~0x1ff;
-			dword |= current_total_delay[lane] & 0x1ff;
+		if ((lane == 8) || (lane == 6) || (lane == 4) || (lane == 2) || (lane == 0)) {
+			dword &= ~mask;
+			dword |= current_total_delay[lane] & mask;
 		}
-		Set_NB32_index_wait(dev, index_reg, ret_reg, dword);
+		Set_NB32_index_wait_DCT(dev, dct, index_reg, ret_reg, dword);
+	}
+}
+
+static void read_dram_phase_recovery_control_registers(uint16_t* current_total_delay, uint32_t dev, uint8_t dct, uint8_t dimm, uint32_t index_reg)
+{
+	uint8_t lane;
+	uint32_t dword;
+
+	for (lane = 0; lane < MAX_BYTE_LANES; lane++) {
+		uint32_t prc_reg;
+
+		/* Calculate DRAM Phase Recovery Control register location */
+		if ((lane == 0) || (lane == 1) || (lane == 2) || (lane == 3))
+			prc_reg = 0x50;
+		if ((lane == 4) || (lane == 5) || (lane == 6) || (lane == 7))
+			prc_reg = 0x51;
+		if (lane == 8)
+			prc_reg = 0x52;
+
+		dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, prc_reg);
+		if ((lane == 7) || (lane == 3)) {
+			current_total_delay[lane] = (dword >> 24) & 0x7f;
+		}
+		if ((lane == 6) || (lane == 2)) {
+			current_total_delay[lane] = (dword >> 16) & 0x7f;
+		}
+		if ((lane == 5) || (lane == 1)) {
+			current_total_delay[lane] = (dword >> 8) & 0x7f;
+		}
+		if ((lane == 8) || (lane == 4) || (lane == 0)) {
+			current_total_delay[lane] = dword & 0x7f;
+		}
+	}
+}
+
+static void write_dram_phase_recovery_control_registers(uint16_t* current_total_delay, uint32_t dev, uint8_t dct, uint8_t dimm, uint32_t index_reg)
+{
+	uint8_t lane;
+	uint32_t dword;
+
+	for (lane = 0; lane < MAX_BYTE_LANES; lane++) {
+		uint32_t prc_reg;
+
+		/* Calculate DRAM Phase Recovery Control register location */
+		if ((lane == 0) || (lane == 1) || (lane == 2) || (lane == 3))
+			prc_reg = 0x50;
+		if ((lane == 4) || (lane == 5) || (lane == 6) || (lane == 7))
+			prc_reg = 0x51;
+		if (lane == 8)
+			prc_reg = 0x52;
+
+		/* Set DRAM Phase Recovery Control register values */
+		dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, prc_reg);
+		if ((lane == 7) || (lane == 3)) {
+			dword &= ~(0x7f << 24);
+			dword |= (current_total_delay[lane] & 0x7f) << 24;
+		}
+		if ((lane == 6) || (lane == 2)) {
+			dword &= ~(0x7f << 16);
+			dword |= (current_total_delay[lane] & 0x7f) << 16;
+		}
+		if ((lane == 5) || (lane == 1)) {
+			dword &= ~(0x7f << 8);
+			dword |= (current_total_delay[lane] & 0x7f) << 8;
+		}
+		if ((lane == 8) || (lane == 4) || (lane == 0)) {
+			dword &= ~0x7f;
+			dword |= current_total_delay[lane] & 0x7f;
+		}
+		Set_NB32_index_wait_DCT(dev, dct, index_reg, prc_reg, dword);
+	}
+}
+
+static void read_read_dqs_timing_control_registers(uint16_t* current_total_delay, uint32_t dev, uint8_t dct, uint8_t dimm, uint32_t index_reg)
+{
+	uint8_t lane;
+	uint32_t dword;
+
+	for (lane = 0; lane < MAX_BYTE_LANES; lane++) {
+		uint32_t rdt_reg;
+
+		/* Calculate DRAM Read DQS Timing register location */
+		if ((lane == 0) || (lane == 1) || (lane == 2) || (lane == 3))
+			rdt_reg = 0x5;
+		if ((lane == 4) || (lane == 5) || (lane == 6) || (lane == 7))
+			rdt_reg = 0x6;
+		if (lane == 8)
+			rdt_reg = 0x7;
+		rdt_reg |= (dimm << 8);
+
+		dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, rdt_reg);
+		if ((lane == 7) || (lane == 3)) {
+			current_total_delay[lane] = (dword >> 24) & 0x3f;
+		}
+		if ((lane == 6) || (lane == 2)) {
+			current_total_delay[lane] = (dword >> 16) & 0x3f;
+		}
+		if ((lane == 5) || (lane == 1)) {
+			current_total_delay[lane] = (dword >> 8) & 0x3f;
+		}
+		if ((lane == 8) || (lane == 4) || (lane == 0)) {
+			current_total_delay[lane] = dword & 0x3f;
+		}
+
+		if (is_fam15h())
+			current_total_delay[lane] >>= 1;
 	}
 }
 
@@ -160,10 +526,11 @@ static uint32_t convert_testaddr_and_channel_to_address(struct DCTStatStruc *pDC
 	return testaddr;
 }
 
-/* DQS Receiver Enable Training
- * Algorithm detailed in the Fam10h BKDG Rev. 3.62 section 2.8.9.9.2
+/* DQS Receiver Enable Training (Family 10h)
+ * Algorithm detailed in:
+ * The Fam10h BKDG Rev. 3.62 section 2.8.9.9.2
  */
-static void dqsTrainRcvrEn_SW(struct MCTStatStruc *pMCTstat,
+static void dqsTrainRcvrEn_SW_Fam10(struct MCTStatStruc *pMCTstat,
 				struct DCTStatStruc *pDCTstat, u8 Pass)
 {
 	u8 Channel;
@@ -171,7 +538,6 @@ static void dqsTrainRcvrEn_SW(struct MCTStatStruc *pMCTstat,
 	u8 Addl_Index = 0;
 	u8 Receiver;
 	u8 _DisableDramECC = 0, _Wrap32Dis = 0, _SSE2 = 0;
-	u8 Final_Value;
 	u16 CTLRMaxDelay;
 	u16 MaxDelay_CH[2];
 	u32 TestAddr0, TestAddr1, TestAddr0B, TestAddr1B;
@@ -188,6 +554,7 @@ static void dqsTrainRcvrEn_SW(struct MCTStatStruc *pMCTstat,
 	u32 lo, hi;
 
 	uint32_t dword;
+	uint8_t dimm;
 	uint8_t rank;
 	uint8_t lane;
 	uint16_t current_total_delay[MAX_BYTE_LANES];
@@ -214,14 +581,13 @@ static void dqsTrainRcvrEn_SW(struct MCTStatStruc *pMCTstat,
 	}
 
 	for (ch = ch_start; ch < ch_end; ch++) {
-		reg = 0x78 + (0x100 * ch);
-		val = Get_NB32(dev, reg);
+		reg = 0x78;
+		val = Get_NB32_DCT(dev, ch, reg);
 		val &= ~(0x3ff << 22);
-		val |= (0x0c8 << 22);		/* Max Rd Lat */
-		Set_NB32(dev, reg, val);
+		val |= (0x0c8 << 22);		/* MaxRdLatency = 0xc8 */
+		Set_NB32_DCT(dev, ch, reg, val);
 	}
 
-	Final_Value = 1;
 	if (Pass == FirstPass) {
 		mct_InitDQSPos4RcvrEn_D(pMCTstat, pDCTstat);
 	} else {
@@ -260,7 +626,7 @@ static void dqsTrainRcvrEn_SW(struct MCTStatStruc *pMCTstat,
 
 		CTLRMaxDelay = 0;
 		MaxDelay_CH[Channel] = 0;
-		index_reg = 0x98 + 0x100 * Channel;
+		index_reg = 0x98;
 
 		Receiver = mct_InitReceiver_D(pDCTstat, Channel);
 		/* There are four receiver pairs, loosely associated with chipselects.
@@ -268,6 +634,7 @@ static void dqsTrainRcvrEn_SW(struct MCTStatStruc *pMCTstat,
 		 */
 		for (; Receiver < 8; Receiver += 2) {
 			Addl_Index = (Receiver >> 1) * 3 + 0x10;
+			dimm = (Receiver >> 1);
 
 			print_debug_dqs("\t\tTrainRcvEnd52: index ", Addl_Index, 2);
 
@@ -284,45 +651,14 @@ static void dqsTrainRcvrEn_SW(struct MCTStatStruc *pMCTstat,
 			/* 2.8.9.9.2 (1, 6)
 			 * Retrieve gross and fine timing fields from write DQS registers
 			 */
-			read_dqs_write_timing_control_registers(current_total_delay, dev, (Receiver >> 1), index_reg);
+			read_dqs_write_timing_control_registers(current_total_delay, dev, Channel, dimm, index_reg);
 
 			/* 2.8.9.9.2 (1)
 			 * Program the Write Data Timing and Write ECC Timing register to
 			 * the values stored in the DQS Write Timing Control register
 			 * for each lane
 			 */
-			for (lane = 0; lane < MAX_BYTE_LANES; lane++) {
-				uint32_t wdt_reg;
-
-				/* Calculate Write Data Timing register location */
-				if ((lane == 0) || (lane == 1) || (lane == 2) || (lane == 3))
-					wdt_reg = 0x1;
-				if ((lane == 4) || (lane == 5) || (lane == 6) || (lane == 7))
-					wdt_reg = 0x2;
-				if (lane == 8)
-					wdt_reg = 0x3;
-				wdt_reg |= ((Receiver / 2) << 8);
-
-				/* Set Write Data Timing register values */
-				dword = Get_NB32_index_wait(dev, index_reg, wdt_reg);
-				if ((lane == 7) || (lane == 3)) {
-					dword &= ~(0x7f << 24);
-					dword |= (current_total_delay[lane] & 0x7f) << 24;
-				}
-				if ((lane == 6) || (lane == 2)) {
-					dword &= ~(0x7f << 16);
-					dword |= (current_total_delay[lane] & 0x7f) << 16;
-				}
-				if ((lane == 5) || (lane == 1)) {
-					dword &= ~(0x7f << 8);
-					dword |= (current_total_delay[lane] & 0x7f) << 8;
-				}
-				if ((lane == 8) || (lane == 4) || (lane == 0)) {
-					dword &= ~0x7f;
-					dword |= current_total_delay[lane] & 0x7f;
-				}
-				Set_NB32_index_wait(dev, index_reg, wdt_reg, dword);
-			}
+			write_write_data_timing_control_registers(current_total_delay, dev, Channel, dimm, index_reg);
 
 			/* 2.8.9.9.2 (2)
 			 * Program the Read DQS Timing Control and the Read DQS ECC Timing Control registers
@@ -336,12 +672,12 @@ static void dqsTrainRcvrEn_SW(struct MCTStatStruc *pMCTstat,
 					rdt_reg = 0x6;
 				if (lane == 8)
 					rdt_reg = 0x7;
-				rdt_reg |= ((Receiver / 2) << 8);
+				rdt_reg |= (dimm << 8);
 				if (lane == 8)
 					dword = 0x0000003f;
 				else
 					dword = 0x3f3f3f3f;
-				Set_NB32_index_wait(dev, index_reg, rdt_reg, dword);
+				Set_NB32_index_wait_DCT(dev, Channel, index_reg, rdt_reg, dword);
 			}
 
 			/* 2.8.9.9.2 (3)
@@ -371,7 +707,7 @@ static void dqsTrainRcvrEn_SW(struct MCTStatStruc *pMCTstat,
 			print_debug_dqs("\t\tTrainRcvEn53: TestAddr1B ", TestAddr1B, 2);
 
 			/* 2.8.9.9.2 (4, 5)
-			 * Write 1 cache line of the appropriate test pattern to each test addresse
+			 * Write 1 cache line of the appropriate test pattern to each test address
 			 */
 			mct_Write1LTestPattern_D(pMCTstat, pDCTstat, TestAddr0, 0); /* rank 0 of DIMM, testpattern 0 */
 			mct_Write1LTestPattern_D(pMCTstat, pDCTstat, TestAddr0B, 1); /* rank 0 of DIMM, testpattern 1 */
@@ -390,7 +726,7 @@ static void dqsTrainRcvrEn_SW(struct MCTStatStruc *pMCTstat,
 			/* 2.8.9.9.2 (6)
 			 * Write gross and fine timing fields to read DQS registers
 			 */
-			write_dqs_receiver_enable_control_registers(current_total_delay, dev, (Receiver >> 1), index_reg);
+			write_dqs_receiver_enable_control_registers(current_total_delay, dev, Channel, dimm, index_reg);
 
 			/* 2.8.9.9.2 (7)
 			 * Loop over all delay values up to 1 MEMCLK (0x40 delay steps) from the initial delay values
@@ -417,8 +753,8 @@ static void dqsTrainRcvrEn_SW(struct MCTStatStruc *pMCTstat,
 					break;
 
 				/* 2.8.9.9.2 (7 A)
-				* Loop over all ranks
-				*/
+				 * Loop over all ranks
+				 */
 				for (rank = 0; rank < (_2Ranks + 1); rank++) {
 					/* 2.8.9.9.2 (7 A a-d)
 					 * Read the first test address of the current rank
@@ -434,17 +770,17 @@ static void dqsTrainRcvrEn_SW(struct MCTStatStruc *pMCTstat,
 						 */
 						proc_IOCLFLUSH_D((rank == 0)?TestAddr0B:TestAddr1B);
 						result_qword2 = read64_fs(convert_testaddr_and_channel_to_address(pDCTstat, (rank == 0)?TestAddr0B:TestAddr1B, Channel));
-						write_dqs_receiver_enable_control_registers(current_total_delay, dev, (Receiver >> 1), index_reg);
+						write_dqs_receiver_enable_control_registers(current_total_delay, dev, Channel, dimm, index_reg);
 						proc_IOCLFLUSH_D((rank == 0)?TestAddr0:TestAddr1);
 						result_qword1 = read64_fs(convert_testaddr_and_channel_to_address(pDCTstat, (rank == 0)?TestAddr0:TestAddr1, Channel));
-						write_dqs_receiver_enable_control_registers(current_total_delay, dev, (Receiver >> 1), index_reg);
+						write_dqs_receiver_enable_control_registers(current_total_delay, dev, Channel, dimm, index_reg);
 					} else {
 						proc_IOCLFLUSH_D((rank == 0)?TestAddr0:TestAddr1);
 						result_qword1 = read64_fs(convert_testaddr_and_channel_to_address(pDCTstat, (rank == 0)?TestAddr0:TestAddr1, Channel));
-						write_dqs_receiver_enable_control_registers(current_total_delay, dev, (Receiver >> 1), index_reg);
+						write_dqs_receiver_enable_control_registers(current_total_delay, dev, Channel, dimm, index_reg);
 						proc_IOCLFLUSH_D((rank == 0)?TestAddr0B:TestAddr1B);
 						result_qword2 = read64_fs(convert_testaddr_and_channel_to_address(pDCTstat, (rank == 0)?TestAddr0B:TestAddr1B, Channel));
-						write_dqs_receiver_enable_control_registers(current_total_delay, dev, (Receiver >> 1), index_reg);
+						write_dqs_receiver_enable_control_registers(current_total_delay, dev, Channel, dimm, index_reg);
 					}
 					/* 2.8.9.9.2 (7 A e)
 					 * Compare both read patterns and flag passing ranks/lanes
@@ -533,7 +869,7 @@ static void dqsTrainRcvrEn_SW(struct MCTStatStruc *pMCTstat,
 				}
 
 				/* Update delays in hardware */
-				write_dqs_receiver_enable_control_registers(current_total_delay, dev, (Receiver >> 1), index_reg);
+				write_dqs_receiver_enable_control_registers(current_total_delay, dev, Channel, dimm, index_reg);
 
 				/* Save previous results for comparison in the next iteration */
 				for (lane = 0; lane < 8; lane++)
@@ -587,7 +923,483 @@ static void dqsTrainRcvrEn_SW(struct MCTStatStruc *pMCTstat,
 		mct_SetMaxLatency_D(pDCTstat, Channel, CTLRMaxDelay); /* program Ch A/B MaxAsyncLat to correspond with max delay */
 	}
 
-	ResetDCTWrPtr_D(dev, index_reg, Addl_Index);
+	for (Channel = 0; Channel < 2; Channel++) {
+		ResetDCTWrPtr_D(dev, Channel, index_reg, Addl_Index);
+	}
+
+	if(_DisableDramECC) {
+		mct_EnableDimmEccEn_D(pMCTstat, pDCTstat, _DisableDramECC);
+	}
+
+	if (Pass == FirstPass) {
+		/*Disable DQSRcvrEn training mode */
+		mct_DisableDQSRcvEn_D(pDCTstat);
+	}
+
+	if(!_Wrap32Dis) {
+		msr = HWCR;
+		_RDMSR(msr, &lo, &hi);
+		lo &= ~(1<<17);		/* restore HWCR.wrap32dis */
+		_WRMSR(msr, lo, hi);
+	}
+	if(!_SSE2){
+		cr4 = read_cr4();
+		cr4 &= ~(1<<9); 	/* restore cr4.OSFXSR */
+		write_cr4(cr4);
+	}
+
+#if DQS_TRAIN_DEBUG > 0
+	{
+		u8 ChannelDTD;
+		printk(BIOS_DEBUG, "TrainRcvrEn: CH_MaxRdLat:\n");
+		for(ChannelDTD = 0; ChannelDTD<2; ChannelDTD++) {
+			printk(BIOS_DEBUG, "Channel:%x: %x\n",
+			       ChannelDTD, pDCTstat->CH_MaxRdLat[ChannelDTD]);
+		}
+	}
+#endif
+
+#if DQS_TRAIN_DEBUG > 0
+	{
+		u16 valDTD;
+		u8 ChannelDTD, ReceiverDTD;
+		u8 i;
+		u16 *p;
+
+		printk(BIOS_DEBUG, "TrainRcvrEn: CH_D_B_RCVRDLY:\n");
+		for(ChannelDTD = 0; ChannelDTD < 2; ChannelDTD++) {
+			printk(BIOS_DEBUG, "Channel:%x\n", ChannelDTD);
+			for(ReceiverDTD = 0; ReceiverDTD<8; ReceiverDTD+=2) {
+				printk(BIOS_DEBUG, "\t\tReceiver:%x:", ReceiverDTD);
+				p = pDCTstat->CH_D_B_RCVRDLY[ChannelDTD][ReceiverDTD>>1];
+				for (i=0;i<8; i++) {
+					valDTD = p[i];
+					printk(BIOS_DEBUG, " %03x", valDTD);
+				}
+				printk(BIOS_DEBUG, "\n");
+			}
+		}
+	}
+#endif
+
+	printk(BIOS_DEBUG, "TrainRcvrEn: Status %x\n", pDCTstat->Status);
+	printk(BIOS_DEBUG, "TrainRcvrEn: ErrStatus %x\n", pDCTstat->ErrStatus);
+	printk(BIOS_DEBUG, "TrainRcvrEn: ErrCode %x\n", pDCTstat->ErrCode);
+	printk(BIOS_DEBUG, "TrainRcvrEn: Done\n\n");
+}
+
+/* DQS Receiver Enable Training Pattern Generation (Family 15h)
+ * Algorithm detailed in:
+ * The Fam15h BKDG Rev. 3.14 section 2.10.5.8.2 (4)
+ */
+static void generate_dram_receiver_enable_training_pattern_fam15(struct MCTStatStruc *pMCTstat,
+				struct DCTStatStruc *pDCTstat, uint8_t dct, uint8_t Receiver)
+{
+	uint32_t dword;
+	uint32_t dev = pDCTstat->dev_dct;
+
+	/* 2.10.5.7.1.1
+	 * It appears that the DCT only supports 8-beat burst length mode,
+	 * so do nothing here...
+	 */
+
+	/* Wait for CmdSendInProg == 0 */
+	do {
+		dword = Get_NB32_DCT(dev, dct, 0x250);
+	} while (dword & (0x1 << 12));
+
+	/* Set CmdTestEnable = 1 */
+	dword = Get_NB32_DCT(dev, dct, 0x250);
+	dword |= (0x1 << 2);
+	Set_NB32_DCT(dev, dct, 0x250, dword);
+
+	/* 2.10.5.8.6.1.1 Send Activate Command */
+	dword = Get_NB32_DCT(dev, dct, 0x28c);
+	dword &= ~(0xff << 22);				/* CmdChipSelect = Receiver */
+	dword |= ((0x1 << Receiver) << 22);
+	dword &= ~(0x7 << 19);				/* CmdBank = 0 */
+	dword &= ~(0x3ffff);				/* CmdAddress = 0 */
+	dword |= (0x1 << 31);				/* SendActCmd = 1 */
+	Set_NB32_DCT(dev, dct, 0x28c, dword);
+
+	/* Wait for SendActCmd == 0 */
+	do {
+		dword = Get_NB32_DCT(dev, dct, 0x28c);
+	} while (dword & (0x1 << 31));
+
+	/* Wait 75 MEMCLKs. */
+	precise_memclk_delay_fam15(pMCTstat, pDCTstat, dct, 75);
+
+	/* 2.10.5.8.6.1.2 */
+	Set_NB32_DCT(dev, dct, 0x274, 0x0);		/* DQMask = 0 */
+	Set_NB32_DCT(dev, dct, 0x278, 0x0);
+
+	dword = Get_NB32_DCT(dev, dct, 0x27c);
+	dword &= ~(0xff);				/* EccMask = 0 */
+	if (pDCTstat->DimmECCPresent == 0)
+		dword |= 0xff;				/* EccMask = 0xff */
+	Set_NB32_DCT(dev, dct, 0x27c, dword);
+
+	/* 2.10.5.8.6.1.2 */
+	dword = Get_NB32_DCT(dev, dct, 0x270);
+	dword &= ~(0x7ffff);				/* DataPrbsSeed = 55555 */
+// 	dword |= (0x55555);
+	dword |= (0x44443);				/* Use AGESA seed */
+	Set_NB32_DCT(dev, dct, 0x270, dword);
+
+	/* 2.10.5.8.2 (4) */
+	dword = Get_NB32_DCT(dev, dct, 0x260);
+	dword &= ~(0x1fffff);				/* CmdCount = 192 */
+	dword |= 192;
+	Set_NB32_DCT(dev, dct, 0x260, dword);
+
+#if 0
+	/* TODO: This applies to Fam15h model 10h and above only */
+	/* Program Bubble Count and CmdStreamLen */
+	dword = Get_NB32_DCT(dev, dct, 0x25c);
+	dword &= ~(0x3ff << 12);			/* BubbleCnt = 0 */
+	dword &= ~(0x3ff << 22);			/* BubbleCnt2 = 0 */
+	dword &= ~(0xff);				/* CmdStreamLen = 1 */
+	dword |= 0x1;
+	Set_NB32_DCT(dev, dct, 0x25c, dword);
+#endif
+
+	/* Configure Target A */
+	dword = Get_NB32_DCT(dev, dct, 0x254);
+	dword &= ~(0x7 << 24);				/* TgtChipSelect = Receiver */
+	dword |= (Receiver & 0x7) << 24;
+	dword &= ~(0x7 << 21);				/* TgtBank = 0 */
+	dword &= ~(0x3ff);				/* TgtAddress = 0 */
+	Set_NB32_DCT(dev, dct, 0x254, dword);
+
+	dword = Get_NB32_DCT(dev, dct, 0x250);
+	dword |= (0x1 << 3);				/* ResetAllErr = 1 */
+	dword &= ~(0x1 << 4);				/* StopOnErr = 0 */
+	dword &= ~(0x3 << 8);				/* CmdTgt = 0 (Target A) */
+	dword &= ~(0x7 << 5);				/* CmdType = 0 (Read) */
+	dword |= (0x1 << 11);				/* SendCmd = 1 */
+	Set_NB32_DCT(dev, dct, 0x250, dword);
+
+	/* 2.10.5.8.6.1.2 Wait for TestStatus == 1 and CmdSendInProg == 0 */
+	do {
+		dword = Get_NB32_DCT(dev, dct, 0x250);
+	} while ((dword & (0x1 << 12)) || (!(dword & (0x1 << 10))));
+
+	dword = Get_NB32_DCT(dev, dct, 0x250);
+	dword &= ~(0x1 << 11);				/* SendCmd = 0 */
+	Set_NB32_DCT(dev, dct, 0x250, dword);
+
+	/* 2.10.5.8.6.1.1 Send Precharge Command */
+	/* Wait 25 MEMCLKs. */
+	precise_memclk_delay_fam15(pMCTstat, pDCTstat, dct, 25);
+
+	dword = Get_NB32_DCT(dev, dct, 0x28c);
+	dword &= ~(0xff << 22);				/* CmdChipSelect = Receiver */
+	dword |= ((0x1 << Receiver) << 22);
+	dword &= ~(0x7 << 19);				/* CmdBank = 0 */
+	dword &= ~(0x3ffff);				/* CmdAddress = 0x400 */
+	dword |= 0x400;
+	dword |= (0x1 << 30);				/* SendPchgCmd = 1 */
+	Set_NB32_DCT(dev, dct, 0x28c, dword);
+
+	/* Wait for SendPchgCmd == 0 */
+	do {
+		dword = Get_NB32_DCT(dev, dct, 0x28c);
+	} while (dword & (0x1 << 30));
+
+	/* Wait 25 MEMCLKs. */
+	precise_memclk_delay_fam15(pMCTstat, pDCTstat, dct, 25);
+
+	/* Set CmdTestEnable = 0 */
+	dword = Get_NB32_DCT(dev, dct, 0x250);
+	dword &= ~(0x1 << 2);
+	Set_NB32_DCT(dev, dct, 0x250, dword);
+}
+
+/* DQS Receiver Enable Training (Family 15h)
+ * Algorithm detailed in:
+ * The Fam15h BKDG Rev. 3.14 section 2.10.5.8.2
+ * This algorithm runs once at the lowest supported MEMCLK,
+ * then once again at the highest supported MEMCLK.
+ */
+static void dqsTrainRcvrEn_SW_Fam15(struct MCTStatStruc *pMCTstat,
+				struct DCTStatStruc *pDCTstat, u8 Pass)
+{
+	u8 Channel;
+	u8 _2Ranks;
+	u8 Addl_Index = 0;
+	u8 Receiver;
+	u8 _DisableDramECC = 0, _Wrap32Dis = 0, _SSE2 = 0;
+	u32 Errors;
+
+	u32 val;
+	u32 dev;
+	u32 index_reg;
+	u32 ch_start, ch_end, ch;
+	u32 msr;
+	u32 cr4;
+	u32 lo, hi;
+
+	uint32_t dword;
+	uint8_t dimm;
+	uint8_t rank;
+	uint8_t lane;
+	uint8_t mem_clk;
+	uint16_t initial_seed;
+	uint16_t current_total_delay[MAX_BYTE_LANES];
+	uint16_t dqs_ret_pass1_total_delay[MAX_BYTE_LANES];
+	uint16_t rank0_current_total_delay[MAX_BYTE_LANES];
+	uint16_t phase_recovery_delays[MAX_BYTE_LANES];
+	uint16_t seed[MAX_BYTE_LANES];
+	uint16_t seed_gross[MAX_BYTE_LANES];
+	uint16_t seed_fine[MAX_BYTE_LANES];
+	uint16_t seed_pre_gross[MAX_BYTE_LANES];
+
+	uint8_t package_type = mctGet_NVbits(NV_PACK_TYPE);
+	uint16_t fam15h_freq_tab[] = {0, 0, 0, 0, 333, 0, 400, 0, 0, 0, 533, 0, 0, 0, 667, 0, 0, 0, 800, 0, 0, 0, 933};
+
+	print_debug_dqs("\nTrainRcvEn: Node", pDCTstat->Node_ID, 0);
+	print_debug_dqs("TrainRcvEn: Pass", Pass, 0);
+
+	dev = pDCTstat->dev_dct;
+	index_reg = 0x98;
+	ch_start = 0;
+	ch_end = 2;
+
+	for (ch = ch_start; ch < ch_end; ch++) {
+		uint8_t max_rd_latency = 0x55;
+		uint8_t p_state;
+
+		/* 2.10.5.6 */
+		fam15EnableTrainingMode(pMCTstat, pDCTstat, ch, 1);
+
+		/* 2.10.5.2 */
+		for (p_state = 0; p_state < 3; p_state++) {
+			val = Get_NB32_DCT_NBPstate(dev, ch, p_state, 0x210);
+			val &= ~(0x3ff << 22);			/* MaxRdLatency = max_rd_latency */
+			val |= (max_rd_latency & 0x3ff) << 22;
+			Set_NB32_DCT_NBPstate(dev, ch, p_state, 0x210, val);
+		}
+	}
+
+	if (Pass != FirstPass) {
+		pDCTstat->DimmTrainFail = 0;
+		pDCTstat->CSTrainFail = ~pDCTstat->CSPresent;
+	}
+
+	cr4 = read_cr4();
+	if(cr4 & ( 1 << 9)) {	/* save the old value */
+		_SSE2 = 1;
+	}
+	cr4 |= (1 << 9);	/* OSFXSR enable SSE2 */
+	write_cr4(cr4);
+
+	msr = HWCR;
+	_RDMSR(msr, &lo, &hi);
+	/* FIXME: Why use SSEDIS */
+	if(lo & (1 << 17)) {	/* save the old value */
+		_Wrap32Dis = 1;
+	}
+	lo |= (1 << 17);	/* HWCR.wrap32dis */
+	lo &= ~(1 << 15);	/* SSEDIS */
+	_WRMSR(msr, lo, hi);	/* Setting wrap32dis allows 64-bit memory references in real mode */
+
+	_DisableDramECC = mct_DisableDimmEccEn_D(pMCTstat, pDCTstat);
+
+	Errors = 0;
+	dev = pDCTstat->dev_dct;
+
+	for (Channel = 0; Channel < 2; Channel++) {
+		print_debug_dqs("\tTrainRcvEn51: Node ", pDCTstat->Node_ID, 1);
+		print_debug_dqs("\tTrainRcvEn51: Channel ", Channel, 1);
+		pDCTstat->Channel = Channel;
+
+		mem_clk = Get_NB32_DCT(dev, Channel, 0x94) & 0x1f;
+
+		Receiver = mct_InitReceiver_D(pDCTstat, Channel);
+		/* There are four receiver pairs, loosely associated with chipselects.
+		 * This is essentially looping over each DIMM.
+		 */
+		for (; Receiver < 8; Receiver += 2) {
+			Addl_Index = (Receiver >> 1) * 3 + 0x10;
+			dimm = (Receiver >> 1);
+
+			print_debug_dqs("\t\tTrainRcvEnd52: index ", Addl_Index, 2);
+
+			if (!mct_RcvrRankEnabled_D(pMCTstat, pDCTstat, Channel, Receiver)) {
+				continue;
+			}
+
+			/* Retrieve the total delay values from pass 1 of DQS receiver enable training */
+			if (Pass != FirstPass) {
+				read_dqs_receiver_enable_control_registers(dqs_ret_pass1_total_delay, dev, Channel, dimm, index_reg);
+			}
+
+			/* 2.10.5.8.2
+			 * Loop over all ranks
+			 */
+			if (mct_RcvrRankEnabled_D(pMCTstat, pDCTstat, Channel, Receiver+1))
+				_2Ranks = 1;
+			else
+				_2Ranks = 0;
+			for (rank = 0; rank < (_2Ranks + 1); rank++) {
+				/* 2.10.5.8.2 (1)
+				 * Specify the target DIMM to be trained
+				 * Set TrNibbleSel = 0
+				 *
+				 * TODO: Add support for x4 DIMMs
+				 */
+				dword = Get_NB32_index_wait_DCT(dev, Channel, index_reg, 0x00000008);
+				dword &= ~(0x3 << 4);		/* TrDimmSel */
+				dword |= ((dimm & 0x3) << 4);
+				dword &= ~(0x1 << 2);		/* TrNibbleSel */
+				Set_NB32_index_wait_DCT(dev, Channel, index_reg, 0x00000008, dword);
+
+				/* 2.10.5.8.2 (2)
+				 * Retrieve gross and fine timing fields from write DQS registers
+				 */
+				read_dqs_write_timing_control_registers(current_total_delay, dev, Channel, dimm, index_reg);
+
+				/* 2.10.5.8.2.1
+				 * Generate the DQS Receiver Enable Training Seed Values
+				 */
+				if (Pass == FirstPass) {
+					initial_seed = fam15_receiver_enable_training_seed(pDCTstat, Channel, dimm, rank, package_type);
+
+					/* Adjust seed for the minimum platform supported frequency */
+					initial_seed = (uint16_t) (((((uint64_t) initial_seed) *
+						fam15h_freq_tab[mem_clk] * 100) / (mctGet_NVbits(NV_MIN_MEMCLK) * 100)));
+
+					for (lane = 0; lane < MAX_BYTE_LANES; lane++) {
+						uint16_t wl_pass1_delay;
+						wl_pass1_delay = current_total_delay[lane];
+
+						seed[lane] = initial_seed + wl_pass1_delay;
+					}
+				} else {
+					uint8_t addr_prelaunch = 0;		/* TODO: Fetch the correct value from RC2[0] */
+					uint16_t register_delay;
+					int16_t seed_prescaling;
+
+					memcpy(current_total_delay, dqs_ret_pass1_total_delay, sizeof(current_total_delay));
+					if ((pDCTstat->Status & (1 << SB_Registered))) {
+						if (addr_prelaunch)
+							register_delay = 0x30;
+						else
+							register_delay = 0x20;
+					} else if ((pDCTstat->Status & (1 << SB_LoadReduced))) {
+						/* TODO
+						* Load reduced DIMM support unimplemented
+						*/
+						register_delay = 0x0;
+					} else {
+						register_delay = 0x0;
+					}
+
+					for (lane = 0; lane < MAX_BYTE_LANES; lane++) {
+						seed_prescaling = current_total_delay[lane] - register_delay - 0x20;
+						seed[lane] = (uint16_t) (register_delay + ((((uint64_t) seed_prescaling) * fam15h_freq_tab[mem_clk] * 100) / (mctGet_NVbits(NV_MIN_MEMCLK) * 100)));
+					}
+				}
+
+				for (lane = 0; lane < MAX_BYTE_LANES; lane++) {
+					seed_gross[lane] = (seed[lane] >> 5) & 0x1f;
+					seed_fine[lane] = seed[lane] & 0x1f;
+
+					/*if (seed_gross[lane] == 0)
+						seed_pre_gross[lane] = 0;
+					else */if (seed_gross[lane] & 0x1)
+						seed_pre_gross[lane] = 1;
+					else
+						seed_pre_gross[lane] = 2;
+
+					/* Calculate phase recovery delays */
+					phase_recovery_delays[lane] = ((seed_pre_gross[lane] & 0x1f) << 5) | (seed_fine[lane] & 0x1f);
+
+					/* Set the gross delay.
+					 * NOTE: While the BKDG states to only program DqsRcvEnGrossDelay, this appears
+					 * to have been a misprint as DqsRcvEnFineDelay should be set to zero as well.
+					 */
+					current_total_delay[lane] = ((seed_gross[lane] & 0x1f) << 5);
+				}
+
+				/* 2.10.5.8.2 (2) / 2.10.5.8.2.1 (5 6)
+				 * Program PhRecFineDly and PhRecGrossDly
+				 */
+				write_dram_phase_recovery_control_registers(phase_recovery_delays, dev, Channel, dimm, index_reg);
+
+				/* 2.10.5.8.2 (2) / 2.10.5.8.2.1 (7)
+				 * Program the DQS Receiver Enable delay values for each lane
+				 */
+				write_dqs_receiver_enable_control_registers(current_total_delay, dev, Channel, dimm, index_reg);
+
+				/* 2.10.5.8.2 (3)
+				 * Program DqsRcvTrEn = 1
+				 */
+				dword = Get_NB32_index_wait_DCT(dev, Channel, index_reg, 0x00000008);
+				dword |= (0x1 << 13);
+				Set_NB32_index_wait_DCT(dev, Channel, index_reg, 0x00000008, dword);
+
+				/* 2.10.5.8.2 (4)
+				 * Issue 192 read requests to the target rank
+				 */
+				generate_dram_receiver_enable_training_pattern_fam15(pMCTstat, pDCTstat, Channel, Receiver + (rank & 0x1));
+
+				/* 2.10.5.8.2 (5)
+				 * Program DqsRcvTrEn = 0
+				 */
+				dword = Get_NB32_index_wait_DCT(dev, Channel, index_reg, 0x00000008);
+				dword &= ~(0x1 << 13);
+				Set_NB32_index_wait_DCT(dev, Channel, index_reg, 0x00000008, dword);
+
+				/* 2.10.5.8.2 (6)
+				 * Read PhRecGrossDly, PhRecFineDly
+				 */
+				read_dram_phase_recovery_control_registers(phase_recovery_delays, dev, Channel, dimm, index_reg);
+
+				/* 2.10.5.8.2 (7)
+				 * Calculate and program the DQS Receiver Enable delay values
+				 */
+				for (lane = 0; lane < MAX_BYTE_LANES; lane++) {
+					current_total_delay[lane] = (phase_recovery_delays[lane] & 0x1f);
+					current_total_delay[lane] |= ((seed_gross[lane] + ((phase_recovery_delays[lane] >> 5) & 0x1f) - seed_pre_gross[lane] + 1) << 5);
+					if (lane == 8)
+						pDCTstat->CH_D_BC_RCVRDLY[Channel][dimm] = current_total_delay[lane];
+					else
+						pDCTstat->CH_D_B_RCVRDLY[Channel][dimm][lane] = current_total_delay[lane];
+				}
+				write_dqs_receiver_enable_control_registers(current_total_delay, dev, Channel, dimm, index_reg);
+
+				if (rank == 0) {
+					/* Back up the Rank 0 delays for later use */
+					memcpy(rank0_current_total_delay, current_total_delay, sizeof(current_total_delay));
+				}
+
+				if (rank == 1) {
+					/* 2.10.5.8.2 (8)
+					 * Compute the average delay across both ranks and program the result into
+					 * the DQS Receiver Enable delay registers
+					 */
+					for (lane = 0; lane < MAX_BYTE_LANES; lane++) {
+						current_total_delay[lane] = (rank0_current_total_delay[lane] + current_total_delay[lane]) / 2;
+						if (lane == 8)
+							pDCTstat->CH_D_BC_RCVRDLY[Channel][dimm] = current_total_delay[lane];
+						else
+							pDCTstat->CH_D_B_RCVRDLY[Channel][dimm][lane] = current_total_delay[lane];
+					}
+					write_dqs_receiver_enable_control_registers(current_total_delay, dev, Channel, dimm, index_reg);
+				}
+			}
+
+#if DQS_TRAIN_DEBUG > 0
+			for (lane = 0; lane < 8; lane++)
+				print_debug_dqs_pair("\t\tTrainRcvEn55: Lane ", lane, " current_total_delay ", current_total_delay[lane], 2);
+#endif
+		}
+	}
+
+	/* Calculate and program MaxRdLatency */
+	Calc_SetMaxRdLatency_D_Fam15(pMCTstat, pDCTstat, Channel);
 
 	if(_DisableDramECC) {
 		mct_EnableDimmEccEn_D(pMCTstat, pDCTstat, _DisableDramECC);
@@ -674,10 +1486,10 @@ static void mct_DisableDQSRcvEn_D(struct DCTStatStruc *pDCTstat)
 	}
 
 	for (ch=0; ch<ch_end; ch++) {
-		reg = 0x78 + 0x100 * ch;
-		val = Get_NB32(dev, reg);
+		reg = 0x78;
+		val = Get_NB32_DCT(dev, ch, reg);
 		val &= ~(1 << DqsRcvEnTrain);
-		Set_NB32(dev, reg, val);
+		Set_NB32_DCT(dev, ch, reg, val);
 	}
 }
 
@@ -718,7 +1530,7 @@ void mct_SetRcvrEnDly_D(struct DCTStatStruc *pDCTstat, u16 RcvrEnDly,
 		/* get the register index from table */
 		index = Table_DQSRcvEn_Offset[i >> 1];
 		index += Addl_Index;	/* DIMMx DqsRcvEn byte0 */
-		val = Get_NB32_index_wait(dev, index_reg, index);
+		val = Get_NB32_index_wait_DCT(dev, Channel, index_reg, index);
 		if(i & 1) {
 			/* odd byte lane */
 			val &= ~(0x1ff << 16);
@@ -728,7 +1540,7 @@ void mct_SetRcvrEnDly_D(struct DCTStatStruc *pDCTstat, u16 RcvrEnDly,
 			val &= ~0x1ff;
 			val |= (RcvrEnDly & 0x1ff);
 		}
-		Set_NB32_index_wait(dev, index_reg, index, val);
+		Set_NB32_index_wait_DCT(dev, Channel, index_reg, index, val);
 	}
 
 }
@@ -742,7 +1554,6 @@ static void mct_SetMaxLatency_D(struct DCTStatStruc *pDCTstat, u8 Channel, u16 D
 	u32 reg;
 	u32 SubTotal;
 	u32 index_reg;
-	u32 reg_off;
 	u32 val;
 
 	uint8_t cpu_val_n;
@@ -777,17 +1588,16 @@ static void mct_SetMaxLatency_D(struct DCTStatStruc *pDCTstat, u8 Channel, u16 D
 		Channel = 0;
 
 	dev = pDCTstat->dev_dct;
-	reg_off = 0x100 * Channel;
-	index_reg = 0x98 + reg_off;
+	index_reg = 0x98;
 
 	/* Multiply the CAS Latency by two to get a number of 1/2 MEMCLKs units.*/
-	val = Get_NB32(dev, 0x88 + reg_off);
+	val = Get_NB32_DCT(dev, Channel, 0x88);
 	SubTotal = ((val & 0x0f) + 4) << 1;	/* SubTotal is 1/2 Memclk unit */
 
 	/* If registered DIMMs are being used then
 	 *  add 1 MEMCLK to the sub-total.
 	 */
-	val = Get_NB32(dev, 0x90 + reg_off);
+	val = Get_NB32_DCT(dev, Channel, 0x90);
 	if(!(val & (1 << UnBuffDimm)))
 		SubTotal += 2;
 
@@ -795,7 +1605,7 @@ static void mct_SetMaxLatency_D(struct DCTStatStruc *pDCTstat, u8 Channel, u16 D
 	 *  add 1, else add 2 to the sub-total.
 	 *  if (AddrCmdSetup || CsOdtSetup || CkeSetup) then K := K + 2;
 	 */
-	val = Get_NB32_index_wait(dev, index_reg, 0x04);
+	val = Get_NB32_index_wait_DCT(dev, Channel, index_reg, 0x04);
 	if(!(val & 0x00202020))
 		SubTotal += 1;
 	else
@@ -803,7 +1613,7 @@ static void mct_SetMaxLatency_D(struct DCTStatStruc *pDCTstat, u8 Channel, u16 D
 
 	/* If the F2x[1, 0]78[RdPtrInit] field is 4, 5, 6 or 7 MEMCLKs,
 	 * then add 4, 3, 2, or 1 MEMCLKs, respectively to the sub-total. */
-	val = Get_NB32(dev, 0x78 + reg_off);
+	val = Get_NB32_DCT(dev, Channel, 0x78);
 	SubTotal += 8 - (val & 0x0f);
 
 	/* Convert bits 7-5 (also referred to as the coarse delay) of
@@ -824,7 +1634,7 @@ static void mct_SetMaxLatency_D(struct DCTStatStruc *pDCTstat, u8 Channel, u16 D
 	 * clocks (NCLKs)
 	 */
 	SubTotal *= 200 * ((Get_NB32(pDCTstat->dev_nbmisc, 0xd4) & 0x1f) + 4);
-	SubTotal /= freq_tab[((Get_NB32(pDCTstat->dev_dct, 0x94 + reg_off) & 0x7) - 3)];
+	SubTotal /= freq_tab[((Get_NB32_DCT(pDCTstat->dev_dct, Channel, 0x94) & 0x7) - 3)];
 	SubTotal = (SubTotal + (2 - 1)) / 2;	/* Round up */
 
 	/* Add "N" NCLKs to the sub-total. "N" represents part of the
@@ -841,13 +1651,13 @@ static void mct_SetMaxLatency_D(struct DCTStatStruc *pDCTstat, u8 Channel, u16 D
 	/* Program the F2x[1, 0]78[MaxRdLatency] register with
 	 * the total delay value (in NCLKs).
 	 */
-	reg = 0x78 + reg_off;
-	val = Get_NB32(dev, reg);
+	reg = 0x78;
+	val = Get_NB32_DCT(dev, Channel, reg);
 	val &= ~(0x3ff << 22);
 	val |= (SubTotal & 0x3ff) << 22;
 
 	/* program MaxRdLatency to correspond with current delay */
-	Set_NB32(dev, reg, val);
+	Set_NB32_DCT(dev, Channel, reg, val);
 }
 
 static void mct_InitDQSPos4RcvrEn_D(struct MCTStatStruc *pMCTstat,
@@ -877,7 +1687,7 @@ static void InitDQSPos4RcvrEn_D(struct MCTStatStruc *pMCTstat,
 	u32 dword;
 	u8 dn = 4; /* TODO: Rev C could be 4 */
 	u32 dev = pDCTstat->dev_dct;
-	u32 index_reg = 0x98 + 0x100 * Channel;
+	u32 index_reg = 0x98;
 
 	/* FIXME: add Cx support */
 	dword = 0x00000000;
@@ -885,7 +1695,7 @@ static void InitDQSPos4RcvrEn_D(struct MCTStatStruc *pMCTstat,
 		for(j=0; j<dn; j++)
 			/* DIMM0 Write Data Timing Low */
 			/* DIMM0 Write ECC Timing */
-			Set_NB32_index_wait(dev, index_reg, i + 0x100 * j, dword);
+			Set_NB32_index_wait_DCT(dev, Channel, index_reg, i + 0x100 * j, dword);
 	}
 
 	/* errata #180 */
@@ -893,13 +1703,13 @@ static void InitDQSPos4RcvrEn_D(struct MCTStatStruc *pMCTstat,
 	for(i=5; i<=6; i++) {
 		for(j=0; j<dn; j++)
 			/* DIMM0 Read DQS Timing Control Low */
-			Set_NB32_index_wait(dev, index_reg, i + 0x100 * j, dword);
+			Set_NB32_index_wait_DCT(dev, Channel, index_reg, i + 0x100 * j, dword);
 	}
 
 	dword = 0x0000002f;
 	for(j=0; j<dn; j++)
 		/* DIMM0 Read DQS ECC Timing Control */
-		Set_NB32_index_wait(dev, index_reg, 7 + 0x100 * j, dword);
+		Set_NB32_index_wait_DCT(dev, Channel, index_reg, 7 + 0x100 * j, dword);
 }
 
 void SetEccDQSRcvrEn_D(struct DCTStatStruc *pDCTstat, u8 Channel)
@@ -912,13 +1722,13 @@ void SetEccDQSRcvrEn_D(struct DCTStatStruc *pDCTstat, u8 Channel)
 	u32 val;
 
 	dev = pDCTstat->dev_dct;
-	index_reg = 0x98 + Channel * 0x100;
+	index_reg = 0x98;
 	index = 0x12;
 	p = pDCTstat->CH_D_BC_RCVRDLY[Channel];
 	print_debug_dqs("\t\tSetEccDQSRcvrPos: Channel ", Channel,  2);
 	for(ChipSel = 0; ChipSel < MAX_CS_SUPPORTED; ChipSel += 2) {
 		val = p[ChipSel>>1];
-		Set_NB32_index_wait(dev, index_reg, index, val);
+		Set_NB32_index_wait_DCT(dev, Channel, index_reg, index, val);
 		print_debug_dqs_pair("\t\tSetEccDQSRcvrPos: ChipSel ",
 					ChipSel, " rcvr_delay ",  val, 2);
 		index += 3;
@@ -1002,95 +1812,305 @@ void phyAssistedMemFnceTraining(struct MCTStatStruc *pMCTstat,
 	u8 Node = 0;
 	struct DCTStatStruc *pDCTstat;
 
+	printk(BIOS_DEBUG, "%s: Start\n", __func__);
+
 	/* FIXME: skip for Ax */
-	while (Node < MAX_NODES_SUPPORTED) {
+	for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
 		pDCTstat = pDCTstatA + Node;
+		if (!pDCTstat->NodePresent)
+			continue;
+
+		if (pDCTstat->DCTSysLimit) {
+			if (is_fam15h()) {
+				/* Fam15h BKDG v3.14 section 2.10.5.3.3
+				 * This picks up where InitDDRPhy left off
+				 */
+				uint8_t dct;
+				uint8_t index;
+				uint32_t dword;
+				uint32_t datc_backup;
+				uint32_t training_dword;
+				uint32_t fence2_config_dword;
+				uint32_t fence_tx_pad_config_dword;
+				uint32_t index_reg = 0x98;
+				uint32_t dev = pDCTstat->dev_dct;
+
+				for (dct = 0; dct < 2; dct++) {
+					if (!pDCTstat->DIMMValidDCT[dct])
+						continue;
+
+					/* Back up D18F2x9C_x0000_0004_dct[1:0] */
+					datc_backup = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000004);
+
+					/* FenceTrSel = 0x2 */
+					dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000008);
+					dword &= ~(0x3 << 6);
+					dword |= (0x2 << 6);
+					Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000008, dword);
+
+					/* Set phase recovery seed values */
+					Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000050, 0x13131313);
+					Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000051, 0x13131313);
+					Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000052, 0x00000013);
+
+					training_dword = fenceDynTraining_D(pMCTstat, pDCTstat, dct);
+
+					/* Save calculated fence value to the TX DLL */
+					dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0000000c);
+					dword &= ~(0x1f << 26);
+					dword |= ((training_dword & 0x1f) << 26);
+					Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0000000c, dword);
+
+					/* D18F2x9C_x0D0F_0[F,8:0]0F_dct[1:0][AlwaysEnDllClks]=0x1 */
+					for (index = 0; index < 0x9; index++) {
+						dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f000f | (index << 8));
+						dword &= ~(0x7 << 12);
+						dword |= (0x1 << 12);
+						Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f000f | (index << 8), dword);
+					}
+
+					/* FenceTrSel = 0x1 */
+					dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000008);
+					dword &= ~(0x3 << 6);
+					dword |= (0x1 << 6);
+					Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000008, dword);
+
+					/* Set phase recovery seed values */
+					Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000050, 0x13131313);
+					Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000051, 0x13131313);
+					Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000052, 0x00000013);
+
+					training_dword = fenceDynTraining_D(pMCTstat, pDCTstat, dct);
+
+					/* Save calculated fence value to the RX DLL */
+					dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0000000c);
+					dword &= ~(0x1f << 21);
+					dword |= ((training_dword & 0x1f) << 21);
+					Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0000000c, dword);
+
+					/* D18F2x9C_x0D0F_0[F,8:0]0F_dct[1:0][AlwaysEnDllClks]=0x0 */
+					for (index = 0; index < 0x9; index++) {
+						dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f000f | (index << 8));
+						dword &= ~(0x7 << 12);
+						Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f000f | (index << 8), dword);
+					}
+
+					/* FenceTrSel = 0x3 */
+					dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000008);
+					dword &= ~(0x3 << 6);
+					dword |= (0x3 << 6);
+					Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000008, dword);
+
+					/* Set phase recovery seed values */
+					Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000050, 0x13131313);
+					Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000051, 0x13131313);
+					Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000052, 0x00000013);
+
+					fence_tx_pad_config_dword = fenceDynTraining_D(pMCTstat, pDCTstat, dct);
+
+					/* Save calculated fence value to the TX Pad */
+					dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0000000c);
+					dword &= ~(0x1f << 16);
+					dword |= ((fence_tx_pad_config_dword & 0x1f) << 16);
+					Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0000000c, dword);
+
+					/* Program D18F2x9C_x0D0F_[C,8,2][2:0]31_dct[1:0] */
+					training_dword = fence_tx_pad_config_dword;
+					if (fence_tx_pad_config_dword < 16)
+						training_dword |= (0x1 << 4);
+					else
+						training_dword = 0;
+					for (index = 0; index < 0x3; index++) {
+						dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f2031 | (index << 8));
+						dword &= ~(0x1f);
+						dword |= (training_dword & 0x1f);
+						Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f2031 | (index << 8), dword);
+					}
+					for (index = 0; index < 0x3; index++) {
+						dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f8031 | (index << 8));
+						dword &= ~(0x1f);
+						dword |= (training_dword & 0x1f);
+						Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f8031 | (index << 8), dword);
+					}
+					for (index = 0; index < 0x3; index++) {
+						dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fc031 | (index << 8));
+						dword &= ~(0x1f);
+						dword |= (training_dword & 0x1f);
+						Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fc031 | (index << 8), dword);
+					}
+
+					/* Assemble Fence2 configuration word (Fam15h BKDG v3.14 page 331) */
+					dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0000000c);
+					fence2_config_dword = 0;
+
+					/* TxPad */
+					training_dword = (dword >> 16) & 0x1f;
+					if (training_dword < 16)
+						training_dword |= 0x10;
+					else
+						training_dword = 0;
+					fence2_config_dword |= training_dword;
+
+					/* RxDll */
+					training_dword = (dword >> 21) & 0x1f;
+					if (training_dword < 16)
+						training_dword |= 0x10;
+					else
+						training_dword = 0;
+					fence2_config_dword |= (training_dword << 10);
+
+					/* TxDll */
+					training_dword = (dword >> 26) & 0x1f;
+					if (training_dword < 16)
+						training_dword |= 0x10;
+					else
+						training_dword = 0;
+					fence2_config_dword |= (training_dword << 5);
+
+					/* Program D18F2x9C_x0D0F_0[F,8:0]31_dct[1:0] */
+					for (index = 0; index < 0x9; index++) {
+						dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f0031 | (index << 8));
+						dword &= ~(0x7fff);
+						dword |= fence2_config_dword;
+						Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f0031 | (index << 8), dword);
+					}
 
-		if(pDCTstat->DCTSysLimit) {
-			fenceDynTraining_D(pMCTstat, pDCTstat, 0);
-			fenceDynTraining_D(pMCTstat, pDCTstat, 1);
+					/* Restore D18F2x9C_x0000_0004_dct[1:0] */
+					Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000004, datc_backup);
+				}
+			} else {
+				fenceDynTraining_D(pMCTstat, pDCTstat, 0);
+				fenceDynTraining_D(pMCTstat, pDCTstat, 1);
+			}
 		}
-		Node++;
 	}
+
+	printk(BIOS_DEBUG, "%s: Done\n", __func__);
 }
 
-static void fenceDynTraining_D(struct MCTStatStruc *pMCTstat,
+static uint32_t fenceDynTraining_D(struct MCTStatStruc *pMCTstat,
 			struct DCTStatStruc *pDCTstat, u8 dct)
 {
 	u16 avRecValue;
 	u32 val;
 	u32 dev;
-	u32 index_reg = 0x98 + 0x100 * dct;
+	u32 index_reg = 0x98;
 	u32 index;
 
-	/* BIOS first programs a seed value to the phase recovery engine
-	 *  (recommended 19) registers.
-	 * Dram Phase Recovery Control Register (F2x[1,0]9C_x[51:50] and
-	 * F2x[1,0]9C_x52.) .
-	 */
 	dev = pDCTstat->dev_dct;
-	for (index = 0x50; index <= 0x52; index ++) {
-		val = (FenceTrnFinDlySeed & 0x1F);
-		if (index != 0x52) {
-			val |= val << 8 | val << 16 | val << 24;
+
+	if (is_fam15h()) {
+		/* Set F2x[1,0]9C_x08[PhyFenceTrEn] */
+		val = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x08);
+		val |= 1 << PhyFenceTrEn;
+		Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x08, val);
+
+		/* Wait 2000 MEMCLKs */
+		precise_memclk_delay_fam15(pMCTstat, pDCTstat, dct, 2000);
+
+		/* Clear F2x[1,0]9C_x08[PhyFenceTrEn] */
+		val = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x08);
+		val &= ~(1 << PhyFenceTrEn);
+		Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x08, val);
+
+		/* BIOS reads the phase recovery engine registers
+		 * F2x[1,0]9C_x[51:50] and F2x[1,0]9C_x52.
+		 * Average the fine delay components only.
+		 */
+		avRecValue = 0;
+		for (index = 0x50; index <= 0x52; index++) {
+			val = Get_NB32_index_wait_DCT(dev, dct, index_reg, index);
+			avRecValue += val & 0x1f;
+			if (index != 0x52) {
+				avRecValue += (val >> 8) & 0x1f;
+				avRecValue += (val >> 16) & 0x1f;
+				avRecValue += (val >> 24) & 0x1f;
+			}
 		}
-		Set_NB32_index_wait(dev, index_reg, index, val);
-	}
 
-	/* Set F2x[1,0]9C_x08[PhyFenceTrEn]=1. */
-	val = Get_NB32_index_wait(dev, index_reg, 0x08);
-	val |= 1 << PhyFenceTrEn;
-	Set_NB32_index_wait(dev, index_reg, 0x08, val);
-
-	/* Wait 200 MEMCLKs. */
-	mct_Wait(50000);		/* wait 200us */
-
-	/* Clear F2x[1,0]9C_x08[PhyFenceTrEn]=0. */
-	val = Get_NB32_index_wait(dev, index_reg, 0x08);
-	val &= ~(1 << PhyFenceTrEn);
-	Set_NB32_index_wait(dev, index_reg, 0x08, val);
-
-	/* BIOS reads the phase recovery engine registers
-	 * F2x[1,0]9C_x[51:50] and F2x[1,0]9C_x52. */
-	avRecValue = 0;
-	for (index = 0x50; index <= 0x52; index ++) {
-		val = Get_NB32_index_wait(dev, index_reg, index);
-		avRecValue += val & 0x7F;
-		if (index != 0x52) {
-			avRecValue += (val >> 8) & 0x7F;
-			avRecValue += (val >> 16) & 0x7F;
-			avRecValue += (val >> 24) & 0x7F;
+		val = avRecValue / 9;
+		if (avRecValue % 9)
+			val++;
+		avRecValue = val;
+
+		if (avRecValue < 6)
+			avRecValue = 0;
+		else
+			avRecValue -= 6;
+
+		return avRecValue;
+	} else {
+		/* BIOS first programs a seed value to the phase recovery engine
+		 *  (recommended 19) registers.
+		 * Dram Phase Recovery Control Register (F2x[1,0]9C_x[51:50] and
+		 * F2x[1,0]9C_x52.) .
+		 */
+		for (index = 0x50; index <= 0x52; index ++) {
+			val = (FenceTrnFinDlySeed & 0x1F);
+			if (index != 0x52) {
+				val |= val << 8 | val << 16 | val << 24;
+			}
+			Set_NB32_index_wait_DCT(dev, dct, index_reg, index, val);
 		}
-	}
 
-	val = avRecValue / 9;
-	if (avRecValue % 9)
-		val++;
-	avRecValue = val;
+		/* Set F2x[1,0]9C_x08[PhyFenceTrEn]=1. */
+		val = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x08);
+		val |= 1 << PhyFenceTrEn;
+		Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x08, val);
+
+		/* Wait 200 MEMCLKs. */
+		mct_Wait(50000);		/* wait 200us */
+
+		/* Clear F2x[1,0]9C_x08[PhyFenceTrEn]=0. */
+		val = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x08);
+		val &= ~(1 << PhyFenceTrEn);
+		Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x08, val);
+
+		/* BIOS reads the phase recovery engine registers
+		 * F2x[1,0]9C_x[51:50] and F2x[1,0]9C_x52. */
+		avRecValue = 0;
+		for (index = 0x50; index <= 0x52; index ++) {
+			val = Get_NB32_index_wait_DCT(dev, dct, index_reg, index);
+			avRecValue += val & 0x7F;
+			if (index != 0x52) {
+				avRecValue += (val >> 8) & 0x7F;
+				avRecValue += (val >> 16) & 0x7F;
+				avRecValue += (val >> 24) & 0x7F;
+			}
+		}
 
-	/* Write the (averaged value -8) to F2x[1,0]9C_x0C[PhyFence]. */
-	/* inlined mct_AdjustFenceValue() */
-	/* TODO: The RBC0 is not supported. */
-	/* if (pDCTstat->LogicalCPUID & AMD_RB_C0)
-		avRecValue -= 3;
-	else
-	*/
-	if (pDCTstat->LogicalCPUID & AMD_DR_Dx)
-		avRecValue -= 8;
-	else if (pDCTstat->LogicalCPUID & AMD_DR_Cx)
-		avRecValue -= 8;
-	else if (pDCTstat->LogicalCPUID & AMD_DR_Bx)
-		avRecValue -= 8;
-
-	val = Get_NB32_index_wait(dev, index_reg, 0x0C);
-	val &= ~(0x1F << 16);
-	val |= (avRecValue & 0x1F) << 16;
-	Set_NB32_index_wait(dev, index_reg, 0x0C, val);
-
-	/* Rewrite F2x[1,0]9C_x04-DRAM Address/Command Timing Control Register
-	 * delays (both channels). */
-	val = Get_NB32_index_wait(dev, index_reg, 0x04);
-	Set_NB32_index_wait(dev, index_reg, 0x04, val);
+		val = avRecValue / 9;
+		if (avRecValue % 9)
+			val++;
+		avRecValue = val;
+
+		/* Write the (averaged value -8) to F2x[1,0]9C_x0C[PhyFence]. */
+		/* inlined mct_AdjustFenceValue() */
+		/* TODO: The RBC0 is not supported. */
+		/* if (pDCTstat->LogicalCPUID & AMD_RB_C0)
+			avRecValue -= 3;
+		else
+		*/
+		if (pDCTstat->LogicalCPUID & AMD_DR_Dx)
+			avRecValue -= 8;
+		else if (pDCTstat->LogicalCPUID & AMD_DR_Cx)
+			avRecValue -= 8;
+		else if (pDCTstat->LogicalCPUID & AMD_DR_Bx)
+			avRecValue -= 8;
+
+		val = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0C);
+		val &= ~(0x1F << 16);
+		val |= (avRecValue & 0x1F) << 16;
+		Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0C, val);
+
+		/* Rewrite F2x[1,0]9C_x04-DRAM Address/Command Timing Control Register
+		 * delays (both channels).
+		 */
+		val = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x04);
+		Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x04, val);
+
+		return avRecValue;
+	}
 }
 
 void mct_Wait(u32 cycles)
diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctsrc1p.c b/src/northbridge/amd/amdmct/mct_ddr3/mctsrc1p.c
index f01e011..55068ce 100644
--- a/src/northbridge/amd/amdmct/mct_ddr3/mctsrc1p.c
+++ b/src/northbridge/amd/amdmct/mct_ddr3/mctsrc1p.c
@@ -21,8 +21,14 @@
 u8 mct_checkNumberOfDqsRcvEn_1Pass(u8 pass)
 {
 	u8 ret = 1;
-	if (pass == SecondPass)
-		ret = 0;
+
+	if (is_fam15h()) {
+		/* Fam15h needs two passes */
+		ret = 1;
+	} else {
+		if (pass == SecondPass)
+			ret = 0;
+	}
 
 	return ret;
 }
diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mcttmrl.c b/src/northbridge/amd/amdmct/mct_ddr3/mcttmrl.c
index 920f514..68acc75 100644
--- a/src/northbridge/amd/amdmct/mct_ddr3/mcttmrl.c
+++ b/src/northbridge/amd/amdmct/mct_ddr3/mcttmrl.c
@@ -218,12 +218,12 @@ static void mct_setMaxRdLatTrnVal_D(struct DCTStatStruc *pDCTstat,
 	}
 
 	dev = pDCTstat->dev_dct;
-	reg = 0x78 + Channel * 0x100;
-	val = Get_NB32(dev, reg);
+	reg = 0x78;
+	val = Get_NB32_DCT(dev, Channel, reg);
 	val &= ~(0x3ff<<22);
 	val |= MaxRdLatVal<<22;
 	/* program MaxRdLatency to correspond with current delay */
-	Set_NB32(dev, reg, val);
+	Set_NB32_DCT(dev, Channel, reg, val);
 }
 
 static u8 CompareMaxRdLatTestPattern_D(u32 pattern_buf, u32 addr)
@@ -320,30 +320,28 @@ u8 mct_GetStartMaxRdLat_D(struct MCTStatStruc *pMCTstat,
 	u32 valx;
 	u32 valxx;
 	u32 index_reg;
-	u32 reg_off;
 	u32 dev;
 
 	if(pDCTstat->GangedMode)
 		Channel =  0;
 
-	index_reg = 0x98 + 0x100 * Channel;
+	index_reg = 0x98;
 
-	reg_off = 0x100 * Channel;
 	dev = pDCTstat->dev_dct;
 
 	/* Multiply the CAS Latency by two to get a number of 1/2 MEMCLKs units.*/
-	val = Get_NB32(dev, 0x88 + reg_off);
+	val = Get_NB32_DCT(dev, Channel, 0x88);
 	SubTotal = ((val & 0x0f) + 1) << 1;	/* SubTotal is 1/2 Memclk unit */
 
 	/* If registered DIMMs are being used then add 1 MEMCLK to the sub-total*/
-	val = Get_NB32(dev, 0x90 + reg_off);
+	val = Get_NB32_DCT(dev, Channel, 0x90);
 	if(!(val & (1 << UnBuffDimm)))
 		SubTotal += 2;
 
 	/*If the address prelaunch is setup for 1/2 MEMCLKs then add 1,
 	 *  else add 2 to the sub-total. if (AddrCmdSetup || CsOdtSetup
 	 *  || CkeSetup) then K := K + 2; */
-	val = Get_NB32_index_wait(dev, index_reg, 0x04);
+	val = Get_NB32_index_wait_DCT(dev, Channel, index_reg, 0x04);
 	if(!(val & 0x00202020))
 		SubTotal += 1;
 	else
@@ -351,7 +349,7 @@ u8 mct_GetStartMaxRdLat_D(struct MCTStatStruc *pMCTstat,
 
 	/* If the F2x[1, 0]78[RdPtrInit] field is 4, 5, 6 or 7 MEMCLKs,
 	 *  then add 4, 3, 2, or 1 MEMCLKs, respectively to the sub-total. */
-	val = Get_NB32(dev, 0x78 + reg_off);
+	val = Get_NB32_DCT(dev, Channel, 0x78);
 	SubTotal += 8 - (val & 0x0f);
 
 	/* Convert bits 7-5 (also referred to as the course delay) of the current
@@ -367,7 +365,7 @@ u8 mct_GetStartMaxRdLat_D(struct MCTStatStruc *pMCTstat,
 
 	/*New formula:
 	SubTotal *= 3*(Fn2xD4[NBFid]+4)/(3+Fn2x94[MemClkFreq])/2 */
-	val = Get_NB32(dev, 0x94 + reg_off);
+	val = Get_NB32_DCT(dev, Channel, 0x94);
 	/* SubTotal div 4 to scale 1/4 MemClk back to MemClk */
 	val &= 7;
 	if (val >= 3) {
diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctwl.c b/src/northbridge/amd/amdmct/mct_ddr3/mctwl.c
index 1c3e322..0ff4484 100644
--- a/src/northbridge/amd/amdmct/mct_ddr3/mctwl.c
+++ b/src/northbridge/amd/amdmct/mct_ddr3/mctwl.c
@@ -83,6 +83,12 @@ void PrepareC_DCT(struct MCTStatStruc *pMCTstat,
 		pDCTstat->C_DCTPtr[dct]->Status[DCT_STATUS_REGISTERED] = 0;
 	}
 
+	if (pDCTstat->Status & (1 << SB_LoadReduced)) {
+		pDCTstat->C_DCTPtr[dct]->Status[DCT_STATUS_LOAD_REDUCED] = 1;
+	} else {
+		pDCTstat->C_DCTPtr[dct]->Status[DCT_STATUS_LOAD_REDUCED] = 0;
+	}
+
 	pDCTstat->C_DCTPtr[dct]->RegMan1Present = pDCTstat->RegMan1Present;
 
 	for (dimm = 0; dimm < MAX_TOTAL_DIMMS; dimm++) {
@@ -103,13 +109,13 @@ void EnableZQcalibration(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDC
 {
 	u32 val;
 
-	val = Get_NB32(pDCTstat->dev_dct, 0x94);
+	val = Get_NB32_DCT(pDCTstat->dev_dct, 0, 0x94);
 	val |= 1 << 11;
-	Set_NB32(pDCTstat->dev_dct, 0x94, val);
+	Set_NB32_DCT(pDCTstat->dev_dct, 0, 0x94, val);
 
-	val = Get_NB32(pDCTstat->dev_dct, 0x94 + 0x100);
+	val = Get_NB32_DCT(pDCTstat->dev_dct, 1, 0x94);
 	val |= 1 << 11;
-	Set_NB32(pDCTstat->dev_dct, 0x94 + 0x100, val);
+	Set_NB32_DCT(pDCTstat->dev_dct, 1, 0x94, val);
 }
 
 void DisableZQcalibration(struct MCTStatStruc *pMCTstat,
@@ -117,15 +123,15 @@ void DisableZQcalibration(struct MCTStatStruc *pMCTstat,
 {
 	u32 val;
 
-	val = Get_NB32(pDCTstat->dev_dct, 0x94);
+	val = Get_NB32_DCT(pDCTstat->dev_dct, 0, 0x94);
 	val &= ~(1 << 11);
 	val &= ~(1 << 10);
-	Set_NB32(pDCTstat->dev_dct, 0x94, val);
+	Set_NB32_DCT(pDCTstat->dev_dct, 0, 0x94, val);
 
-	val = Get_NB32(pDCTstat->dev_dct, 0x94 + 0x100);
+	val = Get_NB32_DCT(pDCTstat->dev_dct, 1, 0x94);
 	val &= ~(1 << 11);
 	val &= ~(1 << 10);
-	Set_NB32(pDCTstat->dev_dct, 0x94 + 0x100, val);
+	Set_NB32_DCT(pDCTstat->dev_dct, 1, 0x94, val);
 }
 
 static void EnterSelfRefresh(struct MCTStatStruc *pMCTstat,
@@ -142,23 +148,23 @@ static void EnterSelfRefresh(struct MCTStatStruc *pMCTstat,
 
 	/* Program F2x[1, 0]90[EnterSelfRefresh]=1. */
 	if (DCT0Present) {
-		val = Get_NB32(pDCTstat->dev_dct, 0x90);
+		val = Get_NB32_DCT(pDCTstat->dev_dct, 0, 0x90);
 		val |= 1 << EnterSelfRef;
-		Set_NB32(pDCTstat->dev_dct, 0x90, val);
+		Set_NB32_DCT(pDCTstat->dev_dct, 0, 0x90, val);
 	}
 	if (DCT1Present) {
-		val = Get_NB32(pDCTstat->dev_dct, 0x90 + 0x100);
+		val = Get_NB32_DCT(pDCTstat->dev_dct, 1, 0x90);
 		val |= 1 << EnterSelfRef;
-		Set_NB32(pDCTstat->dev_dct, 0x90 + 0x100, val);
+		Set_NB32_DCT(pDCTstat->dev_dct, 1, 0x90, val);
 	}
 	/* Wait until the hardware resets F2x[1, 0]90[EnterSelfRefresh]=0. */
 	if (DCT0Present)
 		do {
-			val = Get_NB32(pDCTstat->dev_dct, 0x90);
+			val = Get_NB32_DCT(pDCTstat->dev_dct, 0, 0x90);
 		} while (val & (1 <<EnterSelfRef));
 	if (DCT1Present)
 		do {
-			val = Get_NB32(pDCTstat->dev_dct, 0x90 + 0x100);
+			val = Get_NB32_DCT(pDCTstat->dev_dct, 1, 0x90);
 		} while (val & (1 <<EnterSelfRef));
 }
 
@@ -168,8 +174,11 @@ static void EnterSelfRefresh(struct MCTStatStruc *pMCTstat,
 static void ChangeMemClk(struct MCTStatStruc *pMCTstat,
 					struct DCTStatStruc *pDCTstat)
 {
-	u8 DCT0Present, DCT1Present;
-	u32 val;
+	uint8_t DCT0Present;
+	uint8_t DCT1Present;
+	uint32_t dword;
+	uint32_t mask;
+	uint32_t offset;
 
 	DCT0Present = pDCTstat->DIMMValidDCT[0];
 	if (pDCTstat->GangedMode)
@@ -177,76 +186,134 @@ static void ChangeMemClk(struct MCTStatStruc *pMCTstat,
 	else
 		DCT1Present = pDCTstat->DIMMValidDCT[1];
 
-	/* Program F2x[1, 0]90[EnterSelfRefresh]=1. */
-	if (DCT0Present) {
-		val = Get_NB32_index_wait(pDCTstat->dev_dct, 0x98, 8);
-		val |= 1 << DisAutoComp;
-		Set_NB32_index_wait(pDCTstat->dev_dct, 0x98, 8, val);
-	}
-	if (DCT1Present) {
-		val = Get_NB32_index_wait(pDCTstat->dev_dct, 0x98 + 0x100, 8);
-		val |= 1 << DisAutoComp;
-		Set_NB32_index_wait(pDCTstat->dev_dct, 0x98 + 0x100, 8, val);
+	if (is_fam15h()) {
+		/* Program D18F2x9C_x0D0F_E006_dct[1:0][PllLockTime] = 0x190 */
+		if (DCT0Present) {
+			dword = Get_NB32_index_wait_DCT(pDCTstat->dev_dct, 0, 0x98, 0x0d0fe006);
+			dword &= ~(0x0000ffff);
+			dword |= 0x00000190;
+			Set_NB32_index_wait_DCT(pDCTstat->dev_dct, 0, 0x98, 0x0d0fe006, dword);
+		}
+		if (DCT1Present) {
+			dword = Get_NB32_index_wait_DCT(pDCTstat->dev_dct, 1, 0x98, 0x0d0fe006);
+			dword &= ~(0x0000ffff);
+			dword |= 0x00000190;
+			Set_NB32_index_wait_DCT(pDCTstat->dev_dct, 1, 0x98, 0x0d0fe006, dword);
+		}
+	} else {
+		/* Program F2x[1, 0]9C[DisAutoComp]=1. */
+		if (DCT0Present) {
+			dword = Get_NB32_index_wait_DCT(pDCTstat->dev_dct, 0, 0x98, 8);
+			dword |= 1 << DisAutoComp;
+			Set_NB32_index_wait_DCT(pDCTstat->dev_dct, 0, 0x98, 8, dword);
+			mct_Wait(100);	/* Wait for 5us */
+		}
+		if (DCT1Present) {
+			dword = Get_NB32_index_wait_DCT(pDCTstat->dev_dct, 1, 0x98, 8);
+			dword |= 1 << DisAutoComp;
+			Set_NB32_index_wait_DCT(pDCTstat->dev_dct, 1, 0x98, 8, dword);
+			mct_Wait(100);	/* Wait for 5us */
+		}
 	}
 
 	/* Program F2x[1, 0]94[MemClkFreqVal] = 0. */
 	if (DCT0Present) {
-		val = Get_NB32(pDCTstat->dev_dct, 0x94);
-		val &= ~(1 << MemClkFreqVal);
-		Set_NB32(pDCTstat->dev_dct, 0x94, val);
+		dword = Get_NB32_DCT(pDCTstat->dev_dct, 0, 0x94);
+		dword &= ~(1 << MemClkFreqVal);
+		Set_NB32_DCT(pDCTstat->dev_dct, 0, 0x94, dword);
 	}
 	if (DCT1Present) {
-		val = Get_NB32(pDCTstat->dev_dct, 0x94 + 0x100);
-		val &= ~(1 << MemClkFreqVal);
-		Set_NB32(pDCTstat->dev_dct, 0x94 + 0x100, val);
+		dword = Get_NB32_DCT(pDCTstat->dev_dct, 1, 0x94);
+		dword &= ~(1 << MemClkFreqVal);
+		Set_NB32_DCT(pDCTstat->dev_dct, 1, 0x94, dword);
 	}
 
 	/* Program F2x[1, 0]94[MemClkFreq] to specify the target MEMCLK frequency. */
+	if (is_fam15h()) {
+		offset = 0x0;
+		mask = 0x1f;
+	} else {
+		offset = 0x1;
+		mask = 0x7;
+	}
 	if (DCT0Present) {
-		val = Get_NB32(pDCTstat->dev_dct, 0x94);
-		val &= 0xFFFFFFF8;
-		val |= pDCTstat->TargetFreq - 1;
-		Set_NB32(pDCTstat->dev_dct, 0x94, val);
+		dword = Get_NB32_DCT(pDCTstat->dev_dct, 0, 0x94);
+		dword &= ~mask;
+		dword |= (pDCTstat->TargetFreq - offset) & mask;
+		Set_NB32_DCT(pDCTstat->dev_dct, 0, 0x94, dword);
 	}
 	if (DCT1Present) {
-		val = Get_NB32(pDCTstat->dev_dct, 0x94 + 0x100);
-		val &= 0xFFFFFFF8;
-		val |= pDCTstat->TargetFreq - 1;
-		Set_NB32(pDCTstat->dev_dct, 0x94 + 0x100, val);
+		dword = Get_NB32_DCT(pDCTstat->dev_dct, 1, 0x94);
+		dword &= ~mask;
+		dword |= (pDCTstat->TargetFreq - offset) & mask;
+		Set_NB32_DCT(pDCTstat->dev_dct, 1, 0x94, dword);
+	}
+
+	if (is_fam15h()) {
+		if (DCT0Present) {
+			mctGet_PS_Cfg_D(pMCTstat, pDCTstat, 0);
+			set_2t_configuration(pMCTstat, pDCTstat, 0);
+			mct_BeforePlatformSpec(pMCTstat, pDCTstat, 0);
+			mct_PlatformSpec(pMCTstat, pDCTstat, 0);
+		}
+		if (DCT1Present) {
+			mctGet_PS_Cfg_D(pMCTstat, pDCTstat, 1);
+			set_2t_configuration(pMCTstat, pDCTstat, 1);
+			mct_BeforePlatformSpec(pMCTstat, pDCTstat, 1);
+			mct_PlatformSpec(pMCTstat, pDCTstat, 1);
+		}
 	}
 
 	/* Program F2x[1, 0]94[MemClkFreqVal] = 1. */
 	if (DCT0Present) {
-		val = Get_NB32(pDCTstat->dev_dct, 0x94);
-		val |= 1 << MemClkFreqVal;
-		Set_NB32(pDCTstat->dev_dct, 0x94, val);
+		dword = Get_NB32_DCT(pDCTstat->dev_dct, 0, 0x94);
+		dword |= 1 << MemClkFreqVal;
+		Set_NB32_DCT(pDCTstat->dev_dct, 0, 0x94, dword);
 	}
 	if (DCT1Present) {
-		val = Get_NB32(pDCTstat->dev_dct, 0x94 + 0x100);
-		val |= 1 << MemClkFreqVal;
-		Set_NB32(pDCTstat->dev_dct, 0x94 + 0x100, val);
+		dword = Get_NB32_DCT(pDCTstat->dev_dct, 1, 0x94);
+		dword |= 1 << MemClkFreqVal;
+		Set_NB32_DCT(pDCTstat->dev_dct, 1, 0x94, dword);
 	}
 
 	/* Wait until F2x[1, 0]94[FreqChgInProg]=0. */
 	if (DCT0Present)
 		do {
-			val = Get_NB32(pDCTstat->dev_dct, 0x94);
-		} while (val & (1 << FreqChgInProg));
+			dword = Get_NB32_DCT(pDCTstat->dev_dct, 0, 0x94);
+		} while (dword & (1 << FreqChgInProg));
 	if (DCT1Present)
 		do {
-			val = Get_NB32(pDCTstat->dev_dct, 0x94 + 0x100);
-		} while (val & (1 << FreqChgInProg));
-
-	/* Program F2x[1, 0]94[MemClkFreqVal] = 0. */
-	if (DCT0Present) {
-		val = Get_NB32_index_wait(pDCTstat->dev_dct, 0x98, 8);
-		val &= ~(1 << DisAutoComp);
-		Set_NB32_index_wait(pDCTstat->dev_dct, 0x98, 8, val);
-	}
-	if (DCT1Present) {
-		val = Get_NB32_index_wait(pDCTstat->dev_dct, 0x98 + 0x100, 8);
-		val &= ~(1 << DisAutoComp);
-		Set_NB32_index_wait(pDCTstat->dev_dct, 0x98 + 0x100, 8, val);
+			dword = Get_NB32_DCT(pDCTstat->dev_dct, 1, 0x94);
+		} while (dword & (1 << FreqChgInProg));
+
+	if (is_fam15h()) {
+		/* Program D18F2x9C_x0D0F_E006_dct[1:0][PllLockTime] = 0xf */
+		if (DCT0Present) {
+			dword = Get_NB32_index_wait_DCT(pDCTstat->dev_dct, 0, 0x98, 0x0d0fe006);
+			dword &= ~(0x0000ffff);
+			dword |= 0x0000000f;
+			Set_NB32_index_wait_DCT(pDCTstat->dev_dct, 0, 0x98, 0x0d0fe006, dword);
+		}
+		if (DCT1Present) {
+			dword = Get_NB32_index_wait_DCT(pDCTstat->dev_dct, 1, 0x98, 0x0d0fe006);
+			dword &= ~(0x0000ffff);
+			dword |= 0x0000000f;
+			Set_NB32_index_wait_DCT(pDCTstat->dev_dct, 1, 0x98, 0x0d0fe006, dword);
+		}
+	} else {
+		/* Program F2x[1, 0]9C[DisAutoComp] = 0. */
+		if (DCT0Present) {
+			dword = Get_NB32_index_wait_DCT(pDCTstat->dev_dct, 0, 0x98, 8);
+			dword &= ~(1 << DisAutoComp);
+			Set_NB32_index_wait_DCT(pDCTstat->dev_dct, 0, 0x98, 8, dword);
+			mct_Wait(15000);	/* Wait for 750us */
+		}
+		if (DCT1Present) {
+			dword = Get_NB32_index_wait_DCT(pDCTstat->dev_dct, 1, 0x98, 8);
+			dword &= ~(1 << DisAutoComp);
+			Set_NB32_index_wait_DCT(pDCTstat->dev_dct, 1, 0x98, 8, dword);
+			mct_Wait(15000);	/* Wait for 750us */
+		}
 	}
 }
 
@@ -267,29 +334,46 @@ static void ExitSelfRefresh(struct MCTStatStruc *pMCTstat,
 
 	/* Program F2x[1, 0]90[ExitSelfRef]=1 for both DCTs. */
 	if (DCT0Present) {
-		val = Get_NB32(pDCTstat->dev_dct, 0x90);
+		val = Get_NB32_DCT(pDCTstat->dev_dct, 0, 0x90);
 		val |= 1 << ExitSelfRef;
-		Set_NB32(pDCTstat->dev_dct, 0x90, val);
+		Set_NB32_DCT(pDCTstat->dev_dct, 0, 0x90, val);
 	}
 	if (DCT1Present) {
-		val = Get_NB32(pDCTstat->dev_dct, 0x90 + 0x100);
+		val = Get_NB32_DCT(pDCTstat->dev_dct, 1, 0x90);
 		val |= 1 << ExitSelfRef;
-		Set_NB32(pDCTstat->dev_dct, 0x90 + 0x100, val);
+		Set_NB32_DCT(pDCTstat->dev_dct, 1, 0x90, val);
 	}
 	/* Wait until the hardware resets F2x[1, 0]90[ExitSelfRef]=0. */
 	if (DCT0Present)
 		do {
-			val = Get_NB32(pDCTstat->dev_dct, 0x90);
+			val = Get_NB32_DCT(pDCTstat->dev_dct, 0, 0x90);
 		} while (val & (1 << ExitSelfRef));
 	if (DCT1Present)
 		do {
-			val = Get_NB32(pDCTstat->dev_dct, 0x90 + 0x100);
+			val = Get_NB32_DCT(pDCTstat->dev_dct, 1, 0x90);
 		} while (val & (1 << ExitSelfRef));
 }
 
 void SetTargetFreq(struct MCTStatStruc *pMCTstat,
 					struct DCTStatStruc *pDCTstat)
 {
+	uint32_t dword;
+	uint8_t package_type = mctGet_NVbits(NV_PACK_TYPE);
+
+	if (is_fam15h()) {
+		/* Program F2x[1, 0]90[DisDllShutDownSR]=1. */
+		if (pDCTstat->DIMMValidDCT[0]) {
+			dword = Get_NB32_DCT(pDCTstat->dev_dct, 0, 0x90);
+			dword |= (0x1 << 27);
+			Set_NB32_DCT(pDCTstat->dev_dct, 0, 0x90, dword);
+		}
+		if (pDCTstat->DIMMValidDCT[1]) {
+			dword = Get_NB32_DCT(pDCTstat->dev_dct, 1, 0x90);
+			dword |= (0x1 << 27);
+			Set_NB32_DCT(pDCTstat->dev_dct, 1, 0x90, dword);
+		}
+	}
+
 	/* Program F2x[1,0]90[EnterSelfRefresh]=1.
 	 * Wait until the hardware resets F2x[1,0]90[EnterSelfRefresh]=0.
 	 */
@@ -305,11 +389,38 @@ void SetTargetFreq(struct MCTStatStruc *pMCTstat,
 	 */
 	ChangeMemClk(pMCTstat, pDCTstat);
 
+	if (is_fam15h()) {
+		uint8_t dct;
+		for (dct = 0; dct < 2; dct++) {
+			if (pDCTstat->DIMMValidDCT[dct]) {
+				phyAssistedMemFnceTraining(pMCTstat, pDCTstat);
+				InitPhyCompensation(pMCTstat, pDCTstat, dct);
+			}
+		}
+	}
+
 	/* Program F2x[1,0]90[ExitSelfRef]=1 for both DCTs.
 	 * Wait until the hardware resets F2x[1, 0]90[ExitSelfRef]=0.
 	 */
 	ExitSelfRefresh(pMCTstat, pDCTstat);
 
+	if (is_fam15h()) {
+		if ((package_type == PT_C3) || (package_type == PT_GR)) {
+			/* Socket C32 or G34 */
+			/* Program F2x[1, 0]90[DisDllShutDownSR]=0. */
+			if (pDCTstat->DIMMValidDCT[0]) {
+				dword = Get_NB32_DCT(pDCTstat->dev_dct, 0, 0x90);
+				dword &= ~(0x1 << 27);
+				Set_NB32_DCT(pDCTstat->dev_dct, 0, 0x90, dword);
+			}
+			if (pDCTstat->DIMMValidDCT[1]) {
+				dword = Get_NB32_DCT(pDCTstat->dev_dct, 1, 0x90);
+				dword &= ~(0x1 << 27);
+				Set_NB32_DCT(pDCTstat->dev_dct, 1, 0x90, dword);
+			}
+		}
+	}
+
 	/* wait for 500 MCLKs after ExitSelfRef, 500*2.5ns=1250ns */
 	mct_Wait(250);
 
@@ -336,13 +447,13 @@ void SetTargetFreq(struct MCTStatStruc *pMCTstat,
 static void Modify_OnDimmMirror(struct DCTStatStruc *pDCTstat, u8 dct, u8 set)
 {
 	u32 val;
-	u32 reg_off = dct * 0x100 + 0x44;
-	while (reg_off < (dct * 0x100 + 0x60)) {
-		val = Get_NB32(pDCTstat->dev_dct, reg_off);
+	u32 reg = 0x44;
+	while (reg < 0x60) {
+		val = Get_NB32_DCT(pDCTstat->dev_dct, dct, reg);
 		if (val & (1 << CSEnable))
 			set ? (val |= 1 << onDimmMirror) : (val &= ~(1<<onDimmMirror));
-		Set_NB32(pDCTstat->dev_dct, reg_off, val);
-		reg_off += 8;
+		Set_NB32_DCT(pDCTstat->dev_dct, dct, reg, val);
+		reg += 8;
 	}
 }
 
diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c
index 397fd77..35378c8 100644
--- a/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c
+++ b/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c
@@ -30,13 +30,22 @@
  *
  *----------------------------------------------------------------------------
  */
-u32 swapAddrBits_wl(sDCTStruct *pDCTData, u32 MRSValue);
-u32 swapBankBits(sDCTStruct *pDCTData, u32 MRSValue);
-void prepareDimms(sMCTStruct *pMCTData, sDCTStruct *pDCTData, u8 dimm, BOOL wl);
-void programODT(sMCTStruct *pMCTData, sDCTStruct *pDCTData, u8 dimm);
-void procConifg(sMCTStruct *pMCTData,sDCTStruct *pDCTData, u8 dimm, u8 pass);
-void setWLByteDelay(sDCTStruct *pDCTData, u8 ByteLane, u8 dimm, u8 targetAddr);
-void getWLByteDelay(sDCTStruct *pDCTData, u8 ByteLane, u8 dimm);
+u32 swapAddrBits_wl(struct DCTStatStruc *pDCTstat, uint8_t dct, uint32_t MRSValue);
+u32 swapBankBits(struct DCTStatStruc *pDCTstat, uint8_t dct, uint32_t MRSValue);
+void prepareDimms(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat,
+	u8 dct, u8 dimm, BOOL wl);
+void programODT(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, uint8_t dct, u8 dimm);
+void procConfig(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, uint8_t dct, u8 dimm, u8 pass);
+void setWLByteDelay(struct DCTStatStruc *pDCTstat, uint8_t dct, u8 ByteLane, u8 dimm, u8 targetAddr, uint8_t pass);
+void getWLByteDelay(struct DCTStatStruc *pDCTstat, uint8_t dct, u8 ByteLane, u8 dimm, uint8_t pass);
+
+static int32_t abs(int32_t val) {
+	if (val < 0)
+		val *= -1;
+
+	return val;
+}
+
 /*
  *-----------------------------------------------------------------------------
  *		EXPORTED FUNCTIONS
@@ -62,34 +71,55 @@ void getWLByteDelay(sDCTStruct *pDCTData, u8 ByteLane, u8 dimm);
  *       OUT
  *-----------------------------------------------------------------------------
  */
-void AgesaHwWlPhase1(sMCTStruct *pMCTData, sDCTStruct *pDCTData,
-		u8 dimm, u8 pass)
+void AgesaHwWlPhase1(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat,
+		u8 dct, u8 dimm, u8 pass)
 {
 	u8 ByteLane;
 	u32 Value, Addr;
 	u16 Addl_Data_Offset, Addl_Data_Port;
+	sMCTStruct *pMCTData = pDCTstat->C_MCTPtr;
+	sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[dct];
 
 	pDCTData->WLPass = pass;
 	/* 1. Specify the target DIMM that is to be trained by programming
 	 * F2x[1, 0]9C_x08[TrDimmSel].
 	 */
-	set_DCT_ADDR_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, FUN_DCT,
+	set_DCT_ADDR_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT,
 			DRAM_ADD_DCT_PHY_CONTROL_REG, TrDimmSelStart,
-			TrDimmSelEnd,(u32)dimm);
+			TrDimmSelEnd, (u32)dimm);
+
+	if (is_fam15h()) {
+		/* Set TrNibbleSel = 0
+		 *
+		 * TODO: Add support for x4 DIMMs
+		 */
+		set_DCT_ADDR_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT,
+				DRAM_ADD_DCT_PHY_CONTROL_REG, 2,
+				2, (u32)0);
+	}
+
 	/* 2. Prepare the DIMMs for write levelization using DDR3-defined
 	 * MR commands. */
-	prepareDimms(pMCTData, pDCTData,dimm, TRUE);
+	prepareDimms(pMCTstat, pDCTstat, dct, dimm, TRUE);
+
 	/* 3. After the DIMMs are configured, BIOS waits 40 MEMCLKs to
 	 *    satisfy DDR3-defined internal DRAM timing.
 	 */
-	pMCTData->AgesaDelay(40);
+	if (is_fam15h())
+		precise_memclk_delay_fam15(pMCTstat, pDCTstat, dct, 40);
+	else
+		pMCTData->AgesaDelay(40);
+
 	/* 4. Configure the processor's DDR phy for write levelization training: */
-	procConifg(pMCTData,pDCTData, dimm, pass);
+	procConfig(pMCTstat, pDCTstat, dct, dimm, pass);
+
 	/* 5. Begin write levelization training:
-	 *  Program F2x[1, 0]9C_x08[WrtLevelTrEn]=1. */
-	if (pDCTData->LogicalCPUID & (AMD_DR_Cx | AMD_DR_Dx))
-		set_DCT_ADDR_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, FUN_DCT,
+	 *  Program F2x[1, 0]9C_x08[WrtLvTrEn]=1. */
+	if (pDCTData->LogicalCPUID & (AMD_DR_Cx | AMD_DR_Dx | AMD_FAM15_ALL))
+	{
+		set_DCT_ADDR_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT,
 				DRAM_ADD_DCT_PHY_CONTROL_REG, WrtLvTrEn, WrtLvTrEn, 1);
+	}
 	else
 	{
 		/* Broadcast write to all D3Dbyte chipset register offset 0xc
@@ -98,7 +128,7 @@ void AgesaHwWlPhase1(sMCTStruct *pMCTData, sDCTStruct *pDCTData,
 		 * retain value of 3:2 (Trdimmsel)
 		 * reset bit 5 (FrzPR)
 		 */
-		if (pDCTData->DctTrain)
+		if (dct)
 		{
 			Addl_Data_Offset=0x198;
 			Addl_Data_Port=0x19C;
@@ -123,29 +153,127 @@ void AgesaHwWlPhase1(sMCTStruct *pMCTData, sDCTStruct *pDCTData,
 				DctAccessDone, DctAccessDone)) == 0);
 	}
 
+	if (is_fam15h())
+		proc_MFENCE();
+
 	/* Wait 200 MEMCLKs. If executing pass 2, wait 32 MEMCLKs. */
-	pMCTData->AgesaDelay(140);
+	if (is_fam15h())
+		precise_memclk_delay_fam15(pMCTstat, pDCTstat, dct, 200);
+	else
+		pMCTData->AgesaDelay(140);
+
 	/* Program F2x[1, 0]9C_x08[WrtLevelTrEn]=0. */
-	set_DCT_ADDR_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, FUN_DCT,
+	set_DCT_ADDR_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT,
 			DRAM_ADD_DCT_PHY_CONTROL_REG, WrtLvTrEn, WrtLvTrEn, 0);
+
 	/* Read from registers F2x[1, 0]9C_x[51:50] and F2x[1, 0]9C_x52
 	 * to get the gross and fine delay settings
 	 * for the target DIMM and save these values. */
-	ByteLane = 0;
-	while (ByteLane < MAX_BYTE_LANES)
-	{
-		getWLByteDelay(pDCTData,ByteLane, dimm);
-		setWLByteDelay(pDCTData,ByteLane, dimm, 1);
-		ByteLane++;
+	for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++) {
+		getWLByteDelay(pDCTstat, dct, ByteLane, dimm, pass);
+	}
+
+	pDCTData->WLCriticalGrossDelayPrevPass = 0x1f;
+}
+
+void AgesaHwWlPhase2(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat,
+		u8 dct, u8 dimm, u8 pass)
+{
+	u8 ByteLane;
+	sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[dct];
+
+	if (is_fam15h()) {
+		int32_t gross_diff[MAX_BYTE_LANES];
+		int32_t cgd = pDCTData->WLCriticalGrossDelayPrevPass;
+		uint8_t index = (uint8_t)(MAX_BYTE_LANES * dimm);
+
+		/* Calculate the Critical Gross Delay */
+		for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++) {
+			/* Calculate the gross delay differential for this lane */
+			gross_diff[ByteLane] = pDCTData->WLSeedGrossDelay[index+ByteLane] + pDCTData->WLGrossDelay[index+ByteLane];
+			gross_diff[ByteLane] -= pDCTData->WLSeedPreGrossDelay[index+ByteLane];
+
+			/* WrDqDqsEarly values greater than 2 are reserved */
+			if (gross_diff[ByteLane] < -2)
+				gross_diff[ByteLane] = -2;
+
+			/* Update the Critical Gross Delay */
+			if (gross_diff[ByteLane] < cgd)
+				cgd = gross_diff[ByteLane];
+		}
+
+		pDCTData->WLCriticalGrossDelayPrevPass = cgd;
+
+		/* Compensate for occasional noise/instability causing sporadic training failure */
+		for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++) {
+			uint16_t total_delay_seed = ((pDCTData->WLSeedGrossDelay[index+ByteLane] & 0x1f) << 5) | (pDCTData->WLSeedFineDelay[index+ByteLane] & 0x1f);
+			uint16_t total_delay_phy = ((pDCTData->WLGrossDelay[index+ByteLane] & 0x1f) << 5) | (pDCTData->WLFineDelay[index+ByteLane] & 0x1f);
+			if (abs(total_delay_phy - total_delay_seed) > 0x20) {
+				printk(BIOS_DEBUG, "%s: overriding faulty phy value\n", __func__);
+				pDCTData->WLGrossDelay[index+ByteLane] = pDCTData->WLSeedGrossDelay[index+ByteLane];
+				pDCTData->WLFineDelay[index+ByteLane] = pDCTData->WLSeedFineDelay[index+ByteLane];
+			}
+		}
+	}
+}
+
+void AgesaHwWlPhase3(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat,
+		u8 dct, u8 dimm, u8 pass)
+{
+	u8 ByteLane;
+	sMCTStruct *pMCTData = pDCTstat->C_MCTPtr;
+	sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[dct];
+
+	if (is_fam15h()) {
+		uint32_t dword;
+		int32_t gross_diff[MAX_BYTE_LANES];
+		int32_t cgd = pDCTData->WLCriticalGrossDelayPrevPass;
+		uint8_t index = (uint8_t)(MAX_BYTE_LANES * dimm);
+
+		/* Apply offset(s) if needed */
+		if (cgd < 0) {
+			dword = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0xa8);
+			dword &= ~(0x3 << 24);			/* WrDqDqsEarly = abs(cgd) */
+			dword |= ((abs(cgd) & 0x3) << 24);
+			Set_NB32_DCT(pDCTstat->dev_dct, dct, 0xa8, dword);
+
+			for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++) {
+				/* Calculate the gross delay differential for this lane */
+				gross_diff[ByteLane] = pDCTData->WLSeedGrossDelay[index+ByteLane] + pDCTData->WLGrossDelay[index+ByteLane];
+				gross_diff[ByteLane] -= pDCTData->WLSeedPreGrossDelay[index+ByteLane];
+
+				/* Prevent underflow in the presence of noise / instability*/
+				if (gross_diff[ByteLane] < cgd)
+					gross_diff[ByteLane] = cgd;
+
+				pDCTData->WLGrossDelay[index+ByteLane] = (gross_diff[ByteLane] + (abs(cgd) & 0x3));
+			}
+		} else {
+			dword = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0xa8);
+			dword &= ~(0x3 << 24);			/* WrDqDqsEarly = 0 */
+			Set_NB32_DCT(pDCTstat->dev_dct, dct, 0xa8, dword);
+		}
+	}
+
+	/* Write the adjusted gross and fine delay settings
+	 * to the target DIMM. */
+	for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++) {
+		setWLByteDelay(pDCTstat, dct, ByteLane, dimm, 1, pass);
 	}
 
 	/* 6. Configure DRAM Phy Control Register so that the phy stops driving
 	 *    write levelization ODT. */
-	set_DCT_ADDR_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, FUN_DCT,
+	set_DCT_ADDR_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT,
 			DRAM_ADD_DCT_PHY_CONTROL_REG, WrLvOdtEn, WrLvOdtEn, 0);
 
+	if (is_fam15h())
+		proc_MFENCE();
+
 	/* Wait 10 MEMCLKs to allow for ODT signal settling. */
-	pMCTData->AgesaDelay(10);
+	if (is_fam15h())
+		precise_memclk_delay_fam15(pMCTstat, pDCTstat, dct, 10);
+	else
+		pMCTData->AgesaDelay(10);
 
 	/* 7. Program the target DIMM back to normal operation by configuring
 	 * the following (See section 2.8.5.4.1.1
@@ -155,7 +283,7 @@ void AgesaHwWlPhase1(sMCTStruct *pMCTData, sDCTStruct *pDCTData,
 	 * For a two DIMM system, program the Rtt value for the target DIMM
 	 * to the normal operating termination:
 	 */
-	prepareDimms(pMCTData, pDCTData,dimm,FALSE);
+	prepareDimms(pMCTstat, pDCTstat, dct, dimm, FALSE);
 }
 
 /*----------------------------------------------------------------------------
@@ -165,7 +293,7 @@ void AgesaHwWlPhase1(sMCTStruct *pMCTData, sDCTStruct *pDCTData,
  */
 
 /*-----------------------------------------------------------------------------
- * u32 swapAddrBits_wl(sDCTStruct *pDCTData, u32 MRSValue)
+ * u32 swapAddrBits_wl(struct DCTStatStruc *pDCTstat, uint8_t dct, u32 MRSValue)
  *
  * Description:
  *	This function swaps the bits in MSR register value
@@ -177,12 +305,17 @@ void AgesaHwWlPhase1(sMCTStruct *pMCTData, sDCTStruct *pDCTData,
  *
  * ----------------------------------------------------------------------------
  */
-u32 swapAddrBits_wl(sDCTStruct *pDCTData, u32 MRSValue)
+u32 swapAddrBits_wl(struct DCTStatStruc *pDCTstat, uint8_t dct, uint32_t MRSValue)
 {
+	sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[dct];
 	u32 tempW, tempW1;
 
-	tempW1 = get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId,
-			FUN_DCT, DRAM_INIT, MrsChipSelStart, MrsChipSelEnd);
+	if (is_fam15h())
+		tempW1 = get_Bits(pDCTData, dct, pDCTData->NodeId,
+				FUN_DCT, DRAM_INIT, MrsChipSelStartFam15, MrsChipSelEndFam15);
+	else
+		tempW1 = get_Bits(pDCTData, dct, pDCTData->NodeId,
+			FUN_DCT, DRAM_INIT, MrsChipSelStartFam10, MrsChipSelEndFam10);
 	if (tempW1 & 1)
 	{
 		if ((pDCTData->Status[DCT_STATUS_OnDimmMirror]))
@@ -201,7 +334,7 @@ u32 swapAddrBits_wl(sDCTStruct *pDCTData, u32 MRSValue)
 }
 
 /*-----------------------------------------------------------------------------
- *  u32 swapBankBits(sDCTStruct *pDCTData, u32 MRSValue)
+ *  u32 swapBankBits(struct DCTStatStruc *pDCTstat, uint8_t dct, u32 MRSValue)
  *
  *  Description:
  *       This function swaps the bits in MSR register value
@@ -213,12 +346,17 @@ u32 swapAddrBits_wl(sDCTStruct *pDCTData, u32 MRSValue)
  *
  * ----------------------------------------------------------------------------
  */
-u32 swapBankBits(sDCTStruct *pDCTData, u32 MRSValue)
+u32 swapBankBits(struct DCTStatStruc *pDCTstat, uint8_t dct, u32 MRSValue)
 {
+	sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[dct];
 	u32 tempW, tempW1;
 
-	tempW1 = get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId,
-			FUN_DCT, DRAM_INIT, MrsChipSelStart, MrsChipSelEnd);
+	if (is_fam15h())
+		tempW1 = get_Bits(pDCTData, dct, pDCTData->NodeId,
+				FUN_DCT, DRAM_INIT, MrsChipSelStartFam15, MrsChipSelEndFam15);
+	else
+		tempW1 = get_Bits(pDCTData, dct, pDCTData->NodeId,
+				FUN_DCT, DRAM_INIT, MrsChipSelStartFam10, MrsChipSelEndFam10);
 	if (tempW1 & 1)
 	{
 		if ((pDCTData->Status[DCT_STATUS_OnDimmMirror]))
@@ -269,7 +407,7 @@ static uint16_t unbuffered_dimm_nominal_termination_emrs(uint8_t number_of_dimms
 	return term;
 }
 
-static uint16_t unbuffered_dimm_dynamic_termination_emrs(uint8_t number_of_dimms, uint8_t frequency_index, uint8_t rank_count, uint8_t rank)
+static uint16_t unbuffered_dimm_dynamic_termination_emrs(uint8_t number_of_dimms, uint8_t frequency_index, uint8_t rank_count)
 {
 	uint16_t term;
 
@@ -300,27 +438,27 @@ static uint16_t unbuffered_dimm_dynamic_termination_emrs(uint8_t number_of_dimms
  *
  *  Description:
  *       This function prepares DIMMS for training
- *
- *   Parameters:
- *       IN  OUT   *DCTData - Pointer to buffer with information about each DCT
- *		 *SPDData - Pointer to buffer with information about each DIMMs
- *			    SPD information
- *		 *MCTData - Pointer to buffer with runtime parameters,
- *       IN	Dimm - Logical DIMM number
- *		 WL - indicates if the routine is used for Write levelization
- *		      training
- *
- *       OUT
- *
+ *       Fam10h: BKDG Rev. 3.62 section 2.8.9.9.1
+ *       Fam15h: BKDG Rev. 3.14 section 2.10.5.8.1
  * ----------------------------------------------------------------------------
  */
-void prepareDimms(sMCTStruct *pMCTData, sDCTStruct *pDCTData, u8 dimm, BOOL wl)
+void prepareDimms(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat,
+	u8 dct, u8 dimm, BOOL wl)
 {
 	u32 tempW, tempW1, tempW2, MrsBank;
 	u8 rank, currDimm, MemClkFreq;
+	sMCTStruct *pMCTData = pDCTstat->C_MCTPtr;
+	sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[dct];
+	uint8_t package_type = mctGet_NVbits(NV_PACK_TYPE);
+	uint8_t number_of_dimms = pDCTData->MaxDimmsInstalled;
 
-	MemClkFreq = get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId,
+	if (is_fam15h()) {
+		MemClkFreq = get_Bits(pDCTData, dct, pDCTData->NodeId,
+			FUN_DCT, DRAM_CONFIG_HIGH, 0, 4);
+	} else {
+		MemClkFreq = get_Bits(pDCTData, dct, pDCTData->NodeId,
 			FUN_DCT, DRAM_CONFIG_HIGH, 0, 2);
+	}
 	/* Configure the DCT to send initialization MR commands to the target DIMM
 	 * by programming the F2x[1,0]7C register using the following steps.
 	 */
@@ -328,52 +466,95 @@ void prepareDimms(sMCTStruct *pMCTData, sDCTStruct *pDCTData, u8 dimm, BOOL wl)
 	while ((rank < pDCTData->DimmRanks[dimm]) && (rank < 2))
 	{
 		/* Program F2x[1, 0]7C[MrsChipSel[2:0]] for the current rank to be trained. */
-		set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, FUN_DCT,
-			DRAM_INIT, MrsChipSelStart, MrsChipSelEnd, dimm*2+rank);
+		if (is_fam15h())
+			set_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT,
+				DRAM_INIT, MrsChipSelStartFam15, MrsChipSelEndFam15, dimm*2+rank);
+		else
+			set_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT,
+				DRAM_INIT, MrsChipSelStartFam10, MrsChipSelEndFam10, dimm*2+rank);
+
 		/* Program F2x[1, 0]7C[MrsBank[2:0]] for the appropriate internal DRAM
 		 * register that defines the required DDR3-defined function for write
 		 * levelization.
 		 */
-		MrsBank = swapBankBits(pDCTData,1);
-		set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, FUN_DCT,
-			DRAM_INIT, MrsBankStart, MrsBankEnd, MrsBank);
+		MrsBank = swapBankBits(pDCTstat, dct, 1);
+		if (is_fam15h())
+			set_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT,
+				DRAM_INIT, MrsBankStartFam15, MrsBankEndFam15, MrsBank);
+		else
+			set_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT,
+				DRAM_INIT, MrsBankStartFam10, MrsBankEndFam10, MrsBank);
+
 		/* Program F2x[1, 0]7C[MrsAddress[15:0]] to the required DDR3-defined function
 		 * for write levelization.
 		 */
 		tempW = 0;/* DLL_DIS = 0, DIC = 0, AL = 0, TDQS = 0 */
 
-		/* Set TDQS=1b for x8 DIMM, TDQS=0b for x4 DIMM, when mixed x8 & x4 */
-		tempW2 = get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId,
-				FUN_DCT, DRAM_CONFIG_HIGH, RDqsEn, RDqsEn);
-		if (tempW2)
-		{
-			if (pDCTData->DimmX8Present[dimm])
-				tempW |= 0x800;
+		/* Retrieve normal settings of the MRS control word and clear Rtt_Nom */
+		if (is_fam15h()) {
+			tempW = mct_MR1(pMCTstat, pDCTstat, dct, dimm*2+rank) & 0xffff;
+			tempW &= ~(0x0244);
+		} else {
+			/* Set TDQS=1b for x8 DIMM, TDQS=0b for x4 DIMM, when mixed x8 & x4 */
+			tempW2 = get_Bits(pDCTData, dct, pDCTData->NodeId,
+					FUN_DCT, DRAM_CONFIG_HIGH, RDqsEn, RDqsEn);
+			if (tempW2)
+			{
+				if (pDCTData->DimmX8Present[dimm])
+					tempW |= 0x800;
+			}
 		}
 
 		/* determine Rtt_Nom for WL & Normal mode */
-		if (pDCTData->Status[DCT_STATUS_REGISTERED]) {
-			tempW1 = RttNomTargetRegDimm(pMCTData, pDCTData, dimm, wl, MemClkFreq, rank);
-		} else {
+		if (is_fam15h()) {
 			if (wl) {
-				if (rank == 0) {
-					/* Get Rtt_WR for the current DIMM and rank */
-					uint16_t dynamic_term = unbuffered_dimm_dynamic_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[dimm], rank);
-
-					/* Convert dynamic termination code to corresponding nominal termination code */
-					if (dynamic_term == 0x200)
-						tempW1 = 0x04;
-					else if (dynamic_term == 0x400)
-						tempW1 = 0x40;
-					else
-						tempW1 = 0x0;
+				if (number_of_dimms > 1) {
+					if (rank == 0) {
+						/* Get Rtt_WR for the current DIMM and rank */
+						tempW2 = fam15_rttwr(pDCTstat, dct, dimm, rank, package_type);
+					} else {
+						tempW2 = fam15_rttnom(pDCTstat, dct, dimm, rank, package_type);
+					}
 				} else {
-					tempW1 = unbuffered_dimm_nominal_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[dimm], rank);
+					tempW2 = fam15_rttnom(pDCTstat, dct, dimm, rank, package_type);
 				}
 			} else {
-				tempW1 = unbuffered_dimm_nominal_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[dimm], rank);
+				tempW2 = fam15_rttnom(pDCTstat, dct, dimm, rank, package_type);
+			}
+			tempW1 = 0;
+			tempW1 |= ((tempW2 & 0x4) >> 2) << 9;
+			tempW1 |= ((tempW2 & 0x2) >> 1) << 6;
+			tempW1 |= ((tempW2 & 0x1) >> 0) << 2;
+		} else {
+			if (pDCTData->Status[DCT_STATUS_REGISTERED]) {
+				tempW1 = RttNomTargetRegDimm(pMCTData, pDCTData, dimm, wl, MemClkFreq, rank);
+			} else {
+				if (wl) {
+					if (number_of_dimms > 1) {
+						if (rank == 0) {
+							/* Get Rtt_WR for the current DIMM and rank */
+							uint16_t dynamic_term = unbuffered_dimm_dynamic_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[dimm]);
+
+							/* Convert dynamic termination code to corresponding nominal termination code */
+							if (dynamic_term == 0x200)
+								tempW1 = 0x04;
+							else if (dynamic_term == 0x400)
+								tempW1 = 0x40;
+							else
+								tempW1 = 0x0;
+						} else {
+							tempW1 = unbuffered_dimm_nominal_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[dimm], rank);
+						}
+					} else {
+						tempW1 = unbuffered_dimm_nominal_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[dimm], rank);
+					}
+				} else {
+					tempW1 = unbuffered_dimm_nominal_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[dimm], rank);
+				}
 			}
 		}
+
+		/* Apply Rtt_Nom to the MRS control word */
 		tempW=tempW|tempW1;
 
 		/* All ranks of the target DIMM are set to write levelization mode. */
@@ -393,68 +574,105 @@ void prepareDimms(sMCTStruct *pMCTData, sDCTStruct *pDCTData, u8 dimm, BOOL wl)
 				tempW = bitTestSet(tempW1, Qoff);
 			}
 		}
-		/* Program MrsAddress[5,1]=output driver impedance control (DIC):
-		 * based on F2x[1,0]84[DrvImpCtrl]
-		 */
-		tempW1 = get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId,
-				FUN_DCT, DRAM_MRS_REGISTER, DrvImpCtrlStart, DrvImpCtrlEnd);
+
+		/* Program MrsAddress[5,1]=output driver impedance control (DIC) */
+		if (is_fam15h()) {
+			tempW1 = fam15_dimm_dic(pDCTstat, dct, dimm, rank, package_type);
+		} else {
+			/* Read DIC from F2x[1,0]84[DrvImpCtrl] */
+			tempW1 = get_Bits(pDCTData, dct, pDCTData->NodeId,
+					FUN_DCT, DRAM_MRS_REGISTER, DrvImpCtrlStart, DrvImpCtrlEnd);
+		}
+
+		/* Apply DIC to the MRS control word */
 		if (bitTest(tempW1, 1))
 			tempW = bitTestSet(tempW, 5);
 		if (bitTest(tempW1, 0))
 			tempW = bitTestSet(tempW, 1);
 
-		tempW = swapAddrBits_wl(pDCTData, tempW);
+		tempW = swapAddrBits_wl(pDCTstat, dct, tempW);
+
+		if (is_fam15h())
+			set_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT,
+				DRAM_INIT, MrsAddressStartFam15, MrsAddressEndFam15, tempW);
+		else
+			set_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT,
+				DRAM_INIT, MrsAddressStartFam10, MrsAddressEndFam10, tempW);
 
-		set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, FUN_DCT,
-			DRAM_INIT, MrsAddressStart, MrsAddressEnd, tempW);
 		/* Program F2x[1, 0]7C[SendMrsCmd]=1 to initiate the command to
 		 * the specified DIMM.
 		 */
-		set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, FUN_DCT,
+		set_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT,
 			DRAM_INIT, SendMrsCmd, SendMrsCmd, 1);
 		/* Wait for F2x[1, 0]7C[SendMrsCmd] to be cleared by hardware. */
-		while ((get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId,
+		while ((get_Bits(pDCTData, dct, pDCTData->NodeId,
 				FUN_DCT, DRAM_INIT, SendMrsCmd, SendMrsCmd)) == 0x1)
 		{
 		}
+
 		/* Program F2x[1, 0]7C[MrsBank[2:0]] for the appropriate internal DRAM
 		 * register that defines the required DDR3-defined function for Rtt_WR.
 		 */
-		MrsBank = swapBankBits(pDCTData,2);
-		set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, FUN_DCT,
-			DRAM_INIT, MrsBankStart, MrsBankEnd, MrsBank);
+		MrsBank = swapBankBits(pDCTstat, dct, 2);
+		if (is_fam15h())
+			set_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT,
+				DRAM_INIT, MrsBankStartFam15, MrsBankEndFam15, MrsBank);
+		else
+			set_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT,
+				DRAM_INIT, MrsBankStartFam10, MrsBankEndFam10, MrsBank);
+
 		/* Program F2x[1, 0]7C[MrsAddress[15:0]] to the required DDR3-defined function
 		 * for Rtt_WR (DRAMTermDyn).
 		 */
 		tempW = 0;/* PASR = 0,*/
-		/* program MrsAddress[7,6,5:3]=SRT,ASR,CWL,
-		 * based on F2x[1,0]84[19,18,22:20]=,SRT,ASR,Tcwl */
-		tempW1 = get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId,
-				FUN_DCT, DRAM_MRS_REGISTER, PCI_MIN_LOW, PCI_MAX_HIGH);
-		if (bitTest(tempW1,19))
-		{tempW = bitTestSet(tempW, 7);}
-		if (bitTest(tempW1,18))
-		{tempW = bitTestSet(tempW, 6);}
-		/* tempW=tempW|(((tempW1>>20)&0x7)<<3); */
-		tempW=tempW|((tempW1&0x00700000)>>17);
-		/* workaround for DR-B0 */
-		if ((pDCTData->LogicalCPUID & AMD_DR_Bx) && (pDCTData->Status[DCT_STATUS_REGISTERED]))
-			tempW+=0x8;
+
+		/* Retrieve normal settings of the MRS control word and clear Rtt_WR */
+		if (is_fam15h()) {
+			tempW = mct_MR2(pMCTstat, pDCTstat, dct, dimm*2+rank) & 0xffff;
+			tempW &= ~(0x0600);
+		} else {
+			/* program MrsAddress[7,6,5:3]=SRT,ASR,CWL,
+			* based on F2x[1,0]84[19,18,22:20]=,SRT,ASR,Tcwl */
+			tempW1 = get_Bits(pDCTData, dct, pDCTData->NodeId,
+					FUN_DCT, DRAM_MRS_REGISTER, PCI_MIN_LOW, PCI_MAX_HIGH);
+			if (bitTest(tempW1,19))
+			{tempW = bitTestSet(tempW, 7);}
+			if (bitTest(tempW1,18))
+			{tempW = bitTestSet(tempW, 6);}
+			/* tempW=tempW|(((tempW1>>20)&0x7)<<3); */
+			tempW=tempW|((tempW1&0x00700000)>>17);
+			/* workaround for DR-B0 */
+			if ((pDCTData->LogicalCPUID & AMD_DR_Bx) && (pDCTData->Status[DCT_STATUS_REGISTERED]))
+				tempW+=0x8;
+		}
+
 		/* determine Rtt_WR for WL & Normal mode */
-		if (pDCTData->Status[DCT_STATUS_REGISTERED])
-			tempW1 = RttWrRegDimm(pMCTData, pDCTData, dimm, wl, MemClkFreq, rank);
-		else
-			tempW1 = unbuffered_dimm_dynamic_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[dimm], rank);
+		if (is_fam15h()) {
+			tempW1 = (fam15_rttwr(pDCTstat, dct, dimm, rank, package_type) << 9);
+		} else {
+			if (pDCTData->Status[DCT_STATUS_REGISTERED])
+				tempW1 = RttWrRegDimm(pMCTData, pDCTData, dimm, wl, MemClkFreq, rank);
+			else
+				tempW1 = unbuffered_dimm_dynamic_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[dimm]);
+		}
+
+		/* Apply Rtt_WR to the MRS control word */
 		tempW=tempW|tempW1;
-		tempW = swapAddrBits_wl(pDCTData,tempW);
-		set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, FUN_DCT,
-			DRAM_INIT, MrsAddressStart, MrsAddressEnd, tempW);
+		tempW = swapAddrBits_wl(pDCTstat, dct, tempW);
+		if (is_fam15h())
+			set_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT,
+				DRAM_INIT, MrsAddressStartFam15, MrsAddressEndFam15, tempW);
+		else
+			set_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT,
+				DRAM_INIT, MrsAddressStartFam10, MrsAddressEndFam10, tempW);
+
 		/* Program F2x[1, 0]7C[SendMrsCmd]=1 to initiate the command to
 		   the specified DIMM.*/
-		set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, FUN_DCT,
+		set_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT,
 			DRAM_INIT, SendMrsCmd, SendMrsCmd, 1);
+
 		/* Wait for F2x[1, 0]7C[SendMrsCmd] to be cleared by hardware. */
-		while ((get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId,
+		while ((get_Bits(pDCTData, dct, pDCTData->NodeId,
 				FUN_DCT, DRAM_INIT, SendMrsCmd, SendMrsCmd)) == 0x1)
 		{
 		}
@@ -473,97 +691,163 @@ void prepareDimms(sMCTStruct *pMCTData, sDCTStruct *pDCTData, u8 dimm, BOOL wl)
 				rank = 0;
 				while ((rank < pDCTData->DimmRanks[currDimm]) && (rank < 2))
 				{
-
 					/* Program F2x[1, 0]7C[MrsChipSel[2:0]] for the current rank
 					 * to be trained.
 					 */
-					set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId,
-						FUN_DCT, DRAM_INIT, MrsChipSelStart, MrsChipSelEnd, currDimm*2+rank);
+					if (is_fam15h())
+						set_Bits(pDCTData, dct, pDCTData->NodeId,
+							FUN_DCT, DRAM_INIT, MrsChipSelStartFam15, MrsChipSelEndFam15, currDimm*2+rank);
+					else
+						set_Bits(pDCTData, dct, pDCTData->NodeId,
+							FUN_DCT, DRAM_INIT, MrsChipSelStartFam10, MrsChipSelEndFam10, currDimm*2+rank);
+
 					/* Program F2x[1, 0]7C[MrsBank[2:0]] for the appropriate internal
 					 * DRAM register that defines the required DDR3-defined function
 					 * for write levelization.
 					 */
-					MrsBank = swapBankBits(pDCTData,1);
-					set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId,
-						FUN_DCT, DRAM_INIT, MrsBankStart, MrsBankEnd, MrsBank);
+					MrsBank = swapBankBits(pDCTstat, dct, 1);
+					if (is_fam15h())
+						set_Bits(pDCTData, dct, pDCTData->NodeId,
+							FUN_DCT, DRAM_INIT, MrsBankStartFam15, MrsBankEndFam15, MrsBank);
+					else
+						set_Bits(pDCTData, dct, pDCTData->NodeId,
+							FUN_DCT, DRAM_INIT, MrsBankStartFam10, MrsBankEndFam10, MrsBank);
+
 					/* Program F2x[1, 0]7C[MrsAddress[15:0]] to the required
 					 * DDR3-defined function for write levelization.
 					 */
 					tempW = 0;/* DLL_DIS = 0, DIC = 0, AL = 0, TDQS = 0, Level=0, Qoff=0 */
 
-					/* Set TDQS=1b for x8 DIMM, TDQS=0b for x4 DIMM, when mixed x8 & x4 */
-					tempW2 = get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId,
-							FUN_DCT, DRAM_CONFIG_HIGH, RDqsEn, RDqsEn);
-					if (tempW2)
-					{
-						if (pDCTData->DimmX8Present[currDimm])
-							tempW |= 0x800;
+					/* Retrieve normal settings of the MRS control word and clear Rtt_Nom */
+					if (is_fam15h()) {
+						tempW = mct_MR1(pMCTstat, pDCTstat, dct, dimm*2+rank) & 0xffff;
+						tempW &= ~(0x0244);
+					} else {
+						/* Set TDQS=1b for x8 DIMM, TDQS=0b for x4 DIMM, when mixed x8 & x4 */
+						tempW2 = get_Bits(pDCTData, dct, pDCTData->NodeId,
+								FUN_DCT, DRAM_CONFIG_HIGH, RDqsEn, RDqsEn);
+						if (tempW2)
+						{
+							if (pDCTData->DimmX8Present[currDimm])
+								tempW |= 0x800;
+						}
 					}
 
 					/* determine Rtt_Nom for WL & Normal mode */
-					if (pDCTData->Status[DCT_STATUS_REGISTERED])
-						tempW1 = RttNomNonTargetRegDimm(pMCTData, pDCTData, currDimm, wl, MemClkFreq, rank);
-					else
-						tempW1 = unbuffered_dimm_nominal_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[currDimm], rank);
+					if (is_fam15h()) {
+						tempW2 = fam15_rttnom(pDCTstat, dct, dimm, rank, package_type);
+						tempW1 = 0;
+						tempW1 |= ((tempW2 & 0x4) >> 2) << 9;
+						tempW1 |= ((tempW2 & 0x2) >> 1) << 6;
+						tempW1 |= ((tempW2 & 0x1) >> 0) << 2;
+					} else {
+						if (pDCTData->Status[DCT_STATUS_REGISTERED])
+							tempW1 = RttNomNonTargetRegDimm(pMCTData, pDCTData, currDimm, wl, MemClkFreq, rank);
+						else
+							tempW1 = unbuffered_dimm_nominal_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[currDimm], rank);
+					}
+
+					/* Apply Rtt_Nom to the MRS control word */
 					tempW=tempW|tempW1;
-					/* program MrsAddress[5,1]=output driver impedance control (DIC):
-					 * based on F2x[1,0]84[DrvImpCtrl] */
-					tempW1 = get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId,
-							FUN_DCT, DRAM_MRS_REGISTER, DrvImpCtrlStart, DrvImpCtrlEnd);
+
+					/* Program MrsAddress[5,1]=output driver impedance control (DIC) */
+					if (is_fam15h()) {
+						tempW1 = fam15_dimm_dic(pDCTstat, dct, dimm, rank, package_type);
+					} else {
+						/* Read DIC from F2x[1,0]84[DrvImpCtrl] */
+						tempW1 = get_Bits(pDCTData, dct, pDCTData->NodeId,
+								FUN_DCT, DRAM_MRS_REGISTER, DrvImpCtrlStart, DrvImpCtrlEnd);
+					}
+
+					/* Apply DIC to the MRS control word */
 					if (bitTest(tempW1,1))
 					{tempW = bitTestSet(tempW, 5);}
 					if (bitTest(tempW1,0))
 					{tempW = bitTestSet(tempW, 1);}
-					tempW = swapAddrBits_wl(pDCTData,tempW);
-					set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId,
-						FUN_DCT, DRAM_INIT, MrsAddressStart, MrsAddressEnd, tempW);
+
+					tempW = swapAddrBits_wl(pDCTstat, dct, tempW);
+
+					if (is_fam15h())
+						set_Bits(pDCTData, dct, pDCTData->NodeId,
+							FUN_DCT, DRAM_INIT, MrsAddressStartFam15, MrsAddressEndFam15, tempW);
+					else
+						set_Bits(pDCTData, dct, pDCTData->NodeId,
+							FUN_DCT, DRAM_INIT, MrsAddressStartFam10, MrsAddressEndFam10, tempW);
+
 					/* Program F2x[1, 0]7C[SendMrsCmd]=1 to initiate the command
 					 * to the specified DIMM.
 					 */
-					set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId,
+					set_Bits(pDCTData, dct, pDCTData->NodeId,
 						FUN_DCT, DRAM_INIT, SendMrsCmd, SendMrsCmd, 1);
+
 					/* Wait for F2x[1, 0]7C[SendMrsCmd] to be cleared by hardware. */
-					while ((get_Bits(pDCTData, pDCTData->CurrDct,
+					while ((get_Bits(pDCTData, dct,
 							pDCTData->NodeId, FUN_DCT, DRAM_INIT,
 							SendMrsCmd, SendMrsCmd)) == 1);
+
 					/* Program F2x[1, 0]7C[MrsBank[2:0]] for the appropriate internal DRAM
 					 * register that defines the required DDR3-defined function for Rtt_WR.
 					 */
-					MrsBank = swapBankBits(pDCTData,2);
-					set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, FUN_DCT,
-						DRAM_INIT, MrsBankStart, MrsBankEnd, MrsBank);
+					MrsBank = swapBankBits(pDCTstat, dct, 2);
+					if (is_fam15h())
+						set_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT,
+							DRAM_INIT, MrsBankStartFam15, MrsBankEndFam15, MrsBank);
+					else
+						set_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT,
+							DRAM_INIT, MrsBankStartFam10, MrsBankEndFam10, MrsBank);
+
 					/* Program F2x[1, 0]7C[MrsAddress[15:0]] to the required DDR3-defined function
 					 * for Rtt_WR (DRAMTermDyn).
 					 */
 					tempW = 0;/* PASR = 0,*/
-					/* program MrsAddress[7,6,5:3]=SRT,ASR,CWL,
-					 * based on F2x[1,0]84[19,18,22:20]=,SRT,ASR,Tcwl */
-					tempW1 = get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId,
-							FUN_DCT, DRAM_MRS_REGISTER, PCI_MIN_LOW, PCI_MAX_HIGH);
-					if (bitTest(tempW1,19))
-					{tempW = bitTestSet(tempW, 7);}
-					if (bitTest(tempW1,18))
-					{tempW = bitTestSet(tempW, 6);}
-					/* tempW=tempW|(((tempW1>>20)&0x7)<<3); */
-					tempW=tempW|((tempW1&0x00700000)>>17);
-					/* workaround for DR-B0 */
-					if ((pDCTData->LogicalCPUID & AMD_DR_Bx) && (pDCTData->Status[DCT_STATUS_REGISTERED]))
-						tempW+=0x8;
+
+					/* Retrieve normal settings of the MRS control word and clear Rtt_WR */
+					if (is_fam15h()) {
+						tempW = mct_MR2(pMCTstat, pDCTstat, dct, dimm*2+rank) & 0xffff;
+						tempW &= ~(0x0600);
+					} else {
+						/* program MrsAddress[7,6,5:3]=SRT,ASR,CWL,
+						* based on F2x[1,0]84[19,18,22:20]=,SRT,ASR,Tcwl */
+						tempW1 = get_Bits(pDCTData, dct, pDCTData->NodeId,
+								FUN_DCT, DRAM_MRS_REGISTER, PCI_MIN_LOW, PCI_MAX_HIGH);
+						if (bitTest(tempW1,19))
+						{tempW = bitTestSet(tempW, 7);}
+						if (bitTest(tempW1,18))
+						{tempW = bitTestSet(tempW, 6);}
+						/* tempW=tempW|(((tempW1>>20)&0x7)<<3); */
+						tempW=tempW|((tempW1&0x00700000)>>17);
+						/* workaround for DR-B0 */
+						if ((pDCTData->LogicalCPUID & AMD_DR_Bx) && (pDCTData->Status[DCT_STATUS_REGISTERED]))
+							tempW+=0x8;
+					}
+
 					/* determine Rtt_WR for WL & Normal mode */
-					if (pDCTData->Status[DCT_STATUS_REGISTERED])
-						tempW1 = RttWrRegDimm(pMCTData, pDCTData, currDimm, wl, MemClkFreq, rank);
-					else
-						tempW1 = unbuffered_dimm_dynamic_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[currDimm], rank);
+					if (is_fam15h()) {
+						tempW1 = (fam15_rttwr(pDCTstat, dct, dimm, rank, package_type) << 9);
+					} else {
+						if (pDCTData->Status[DCT_STATUS_REGISTERED])
+							tempW1 = RttWrRegDimm(pMCTData, pDCTData, currDimm, wl, MemClkFreq, rank);
+						else
+							tempW1 = unbuffered_dimm_dynamic_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[currDimm]);
+					}
+
+					/* Apply Rtt_WR to the MRS control word */
 					tempW=tempW|tempW1;
-					tempW = swapAddrBits_wl(pDCTData,tempW);
-					set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, FUN_DCT,
-						DRAM_INIT, MrsAddressStart, MrsAddressEnd, tempW);
+					tempW = swapAddrBits_wl(pDCTstat, dct, tempW);
+					if (is_fam15h())
+						set_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT,
+							DRAM_INIT, MrsAddressStartFam15, MrsAddressEndFam15, tempW);
+					else
+						set_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT,
+							DRAM_INIT, MrsAddressStartFam10, MrsAddressEndFam10, tempW);
+
 					/* Program F2x[1, 0]7C[SendMrsCmd]=1 to initiate the command to
 					   the specified DIMM.*/
-					set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, FUN_DCT,
+					set_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT,
 						DRAM_INIT, SendMrsCmd, SendMrsCmd, 1);
+
 					/* Wait for F2x[1, 0]7C[SendMrsCmd] to be cleared by hardware. */
-					while ((get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId,
+					while ((get_Bits(pDCTData, dct, pDCTData->NodeId,
 							FUN_DCT, DRAM_INIT, SendMrsCmd, SendMrsCmd)) == 0x1)
 					{
 					}
@@ -587,29 +871,60 @@ void prepareDimms(sMCTStruct *pMCTData, sDCTStruct *pDCTData, u8 dimm, BOOL wl)
  *       OUT
  * ----------------------------------------------------------------------------
  */
-void programODT(sMCTStruct *pMCTData, sDCTStruct *pDCTData, u8 dimm)
+void programODT(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, uint8_t dct, u8 dimm)
 {
+	sMCTStruct *pMCTData = pDCTstat->C_MCTPtr;
+	sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[dct];
+
 	u8 WrLvOdt1=0;
 
-	if (pDCTData->Status[DCT_STATUS_REGISTERED] == 0) {
-		if ((pDCTData->DctCSPresent & 0x05) == 0x05) {
-			WrLvOdt1 = 0x03;
-		} else if (bitTest((u32)pDCTData->DctCSPresent,(u8)(dimm*2+1))) {
-			WrLvOdt1 = (u8)bitTestSet(WrLvOdt1, dimm+2);
+	if (is_fam15h()) {
+		/* Convert DIMM number to CS */
+		uint32_t dword;
+		uint8_t cs;
+		uint8_t rank = 0;
+
+		cs = (dimm * 2) + rank;
+
+		/* Fetch preprogammed ODT pattern from configuration registers */
+		dword = Get_NB32_DCT(pDCTstat->dev_dct, dct, ((cs>3)?0x23c:0x238));
+		if ((cs == 7) || (cs == 3))
+			WrLvOdt1 = ((dword >> 24) & 0xf);
+		else if ((cs == 6) || (cs == 2))
+			WrLvOdt1 = ((dword >> 16) & 0xf);
+		else if ((cs == 5) || (cs == 1))
+			WrLvOdt1 = ((dword >> 8) & 0xf);
+		else if ((cs == 4) || (cs == 0))
+			WrLvOdt1 = (dword & 0xf);
+	} else {
+		if (pDCTData->Status[DCT_STATUS_REGISTERED] == 0) {
+			if ((pDCTData->DctCSPresent & 0x05) == 0x05) {
+				WrLvOdt1 = 0x03;
+			} else if (bitTest((u32)pDCTData->DctCSPresent,(u8)(dimm*2+1))) {
+				WrLvOdt1 = (u8)bitTestSet(WrLvOdt1, dimm+2);
+			} else {
+				WrLvOdt1 = (u8)bitTestSet(WrLvOdt1, dimm);
+			}
 		} else {
-			WrLvOdt1 = (u8)bitTestSet(WrLvOdt1, dimm);
+			WrLvOdt1 = WrLvOdtRegDimm(pMCTData, pDCTData, dimm);
 		}
-	} else {
-		WrLvOdt1 = WrLvOdtRegDimm(pMCTData, pDCTData, dimm);
 	}
 
-	set_DCT_ADDR_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, FUN_DCT,
+	set_DCT_ADDR_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT,
 			DRAM_ADD_DCT_PHY_CONTROL_REG, 8, 11, (u32)WrLvOdt1);
 
 }
 
+#ifdef UNUSED_CODE
+static uint16_t fam15h_next_lowest_memclk_freq(uint16_t memclk_freq)
+{
+	uint16_t fam15h_next_lowest_freq_tab[] = {0, 0, 0, 0, 0x4, 0, 0x4, 0, 0, 0, 0x6, 0, 0, 0, 0xa, 0, 0, 0, 0xe, 0, 0, 0, 0x12};
+	return fam15h_next_lowest_freq_tab[memclk_freq];
+}
+#endif
+
 /*-----------------------------------------------------------------------------
- * void procConifg(MCTStruct *MCTData,DCTStruct *DCTData, u8 Dimm, u8 Pass)
+ * void procConfig(MCTStruct *MCTData,DCTStruct *DCTData, u8 Dimm, u8 Pass)
  *
  *  Description:
  *       This function programs the ODT values for the NB
@@ -622,31 +937,43 @@ void programODT(sMCTStruct *pMCTData, sDCTStruct *pDCTData, u8 dimm)
  *       OUT
  * ----------------------------------------------------------------------------
  */
-void procConifg(sMCTStruct *pMCTData,sDCTStruct *pDCTData, u8 dimm, u8 pass)
+void procConfig(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, uint8_t dct, u8 dimm, u8 pass)
 {
-	u8 ByteLane, Seed_Gross, Seed_Fine, MemClkFreq;
+	u8 ByteLane, MemClkFreq;
+	int32_t Seed_Gross;
+	int32_t Seed_Fine;
+	uint8_t Seed_PreGross;
 	u32 Value, Addr;
 	u16 Addl_Data_Offset, Addl_Data_Port;
-	u16 freq_tab[] = {400, 533, 667, 800};
+	sMCTStruct *pMCTData = pDCTstat->C_MCTPtr;
+	sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[dct];
+	u16 fam10h_freq_tab[] = {400, 533, 667, 800};
+	uint16_t fam15h_freq_tab[] = {0, 0, 0, 0, 333, 0, 400, 0, 0, 0, 533, 0, 0, 0, 667, 0, 0, 0, 800, 0, 0, 0, 933};
 
-	/* MemClkFreq: 3: 400MHz; 4: 533MHz; 5: 667MHz; 6: 800MHz */
-	MemClkFreq = get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId,
-				FUN_DCT, DRAM_CONFIG_HIGH, 0, 2);
+	if (is_fam15h()) {
+		/* MemClkFreq: 0x4: 333MHz; 0x6: 400MHz; 0xa: 533MHz; 0xe: 667MHz; 0x12: 800MHz; 0x16: 933MHz */
+		MemClkFreq = get_Bits(pDCTData, dct, pDCTData->NodeId,
+					FUN_DCT, DRAM_CONFIG_HIGH, 0, 4);
+	} else {
+		/* MemClkFreq: 3: 400MHz; 4: 533MHz; 5: 667MHz; 6: 800MHz */
+		MemClkFreq = get_Bits(pDCTData, dct, pDCTData->NodeId,
+					FUN_DCT, DRAM_CONFIG_HIGH, 0, 2);
+	}
 
 	/* Program F2x[1, 0]9C_x08[WrLvOdt[3:0]] to the proper ODT settings for the
 	 * current memory subsystem configuration.
 	 */
-	programODT(pMCTData, pDCTData, dimm);
+	programODT(pMCTstat, pDCTstat, dct, dimm);
 
 	/* Program F2x[1,0]9C_x08[WrLvOdtEn]=1 */
-	if (pDCTData->LogicalCPUID & (AMD_DR_Cx | AMD_DR_Dx)) {
-		set_DCT_ADDR_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, FUN_DCT,
+	if (pDCTData->LogicalCPUID & (AMD_DR_Cx | AMD_DR_Dx | AMD_FAM15_ALL)) {
+		set_DCT_ADDR_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT,
 				DRAM_ADD_DCT_PHY_CONTROL_REG, WrLvOdtEn, WrLvOdtEn, (u32)1);
 	}
 	else
 	{
 		/* Program WrLvOdtEn=1 through set bit 12 of D3CSODT reg offset 0 for Rev.B */
-		if (pDCTData->DctTrain)
+		if (dct)
 		{
 			Addl_Data_Offset=0x198;
 			Addl_Data_Port=0x19C;
@@ -669,33 +996,94 @@ void procConifg(sMCTStruct *pMCTData,sDCTStruct *pDCTData, u8 dimm, u8 pass)
 				DctAccessDone, DctAccessDone)) == 0);
 	}
 
+	if (is_fam15h())
+		proc_MFENCE();
+
 	/* Wait 10 MEMCLKs to allow for ODT signal settling. */
-	pMCTData->AgesaDelay(10);
+	if (is_fam15h())
+		precise_memclk_delay_fam15(pMCTstat, pDCTstat, dct, 10);
+	else
+		pMCTData->AgesaDelay(10);
+
+	/* Program write levelling seed values */
 	if (pass == 1)
 	{
-		if (pDCTData->Status[DCT_STATUS_REGISTERED])
-		{
-			if(pDCTData->RegMan1Present & ((1<<(dimm*2+pDCTData->DctTrain))))
+		/* Pass 1 */
+		if (is_fam15h()) {
+			uint8_t AddrCmdPrelaunch = 0;		/* TODO: Fetch the correct value from RC2[0] */
+			uint8_t package_type = mctGet_NVbits(NV_PACK_TYPE);
+			uint16_t Seed_Total = 0;
+			if (package_type == PT_GR) {
+				/* Socket G34: Fam15h BKDG v3.14 Table 96 */
+				if (pDCTData->Status[DCT_STATUS_REGISTERED]) {
+					Seed_Total = 0x41;
+				} else if (pDCTData->Status[DCT_STATUS_LOAD_REDUCED]) {
+					Seed_Total = 0x0;
+				} else {
+					Seed_Total = 0xf;
+				}
+			} else if (package_type == PT_C3) {
+				/* Socket C32: Fam15h BKDG v3.14 Table 97 */
+				if (pDCTData->Status[DCT_STATUS_REGISTERED]) {
+					Seed_Total = 0x3e;
+				} else if (pDCTData->Status[DCT_STATUS_LOAD_REDUCED]) {
+					Seed_Total = 0x0;
+				} else {
+					Seed_Total = 0x12;
+				}
+			} else if (package_type == PT_M2) {
+				/* Socket AM3: Fam15h BKDG v3.14 Table 98 */
+				Seed_Total = 0xf;
+			}
+			if (pDCTData->Status[DCT_STATUS_REGISTERED])
+				Seed_Total += ((AddrCmdPrelaunch)?0x10:0x0);
+
+			/* Adjust seed for the minimum platform supported frequency */
+			Seed_Total = (int32_t) (((((int64_t) Seed_Total) *
+				fam15h_freq_tab[MemClkFreq] * 100) / (mctGet_NVbits(NV_MIN_MEMCLK) * 100)));
+
+			Seed_Gross = (Seed_Total >> 5) & 0x1f;
+			Seed_Fine = Seed_Total & 0x1f;
+
+			/* Save seed values for later use */
+			for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++) {
+				pDCTData->WLSeedGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Gross;
+				pDCTData->WLSeedFineDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Fine;
+
+				if (Seed_Gross == 0)
+					Seed_PreGross = 0;
+				else if (Seed_Gross & 0x1)
+					Seed_PreGross = 1;
+				else
+					Seed_PreGross = 2;
+
+				pDCTData->WLSeedPreGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_PreGross;
+			}
+		} else {
+			if (pDCTData->Status[DCT_STATUS_REGISTERED])
 			{
-				Seed_Gross = 0x02;
-				Seed_Fine = 0x16;
+				if(pDCTData->RegMan1Present & ((1<<(dimm*2+dct))))
+				{
+					Seed_Gross = 0x02;
+					Seed_Fine = 0x16;
+				}
+				else
+				{
+					Seed_Gross = 0x02;
+					Seed_Fine = 0x00;
+				}
 			}
 			else
 			{
-				Seed_Gross = 0x02;
-				Seed_Fine = 0x00;
-			}
-		}
-		else
-		{
-			if (MemClkFreq == 6) {
-				/* DDR-800 */
-				Seed_Gross = 0x00;
-				Seed_Fine = 0x1a;
-			} else {
-				/* Use settings for DDR-400 (interpolated from BKDG) */
-				Seed_Gross = 0x00;
-				Seed_Fine = 0x0d;
+				if (MemClkFreq == 6) {
+					/* DDR-800 */
+					Seed_Gross = 0x00;
+					Seed_Fine = 0x1a;
+				} else {
+					/* Use settings for DDR-400 (interpolated from BKDG) */
+					Seed_Gross = 0x00;
+					Seed_Fine = 0x0d;
+				}
 			}
 		}
 		for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++)
@@ -711,39 +1099,91 @@ void procConifg(sMCTStruct *pMCTData,sDCTStruct *pDCTData, u8 dimm, u8 pass)
 			pDCTData->WLGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Gross;
 			pDCTData->WLFineDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Fine;
 		}
-	} else { 		/* Pass 2 */
+	} else {
+		/* Pass 2 */
 		/* From BKDG, Write Leveling Seed Value. */
-		u32 RegisterDelay, SeedTotal;
-		for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++)
-		{
-			if (pDCTData->Status[DCT_STATUS_REGISTERED])
-				RegisterDelay = 0x20; /* TODO: ((RCW2 & BIT0) == 0) ? 0x20 : 0x30; */
-			else
-				RegisterDelay = 0;
-			SeedTotal = (pDCTData->WLFineDelay[MAX_BYTE_LANES*dimm+ByteLane] & 0x1f) |
-				(pDCTData->WLGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] << 5);
-			/* SeedTotalPreScaling = (the total delay value in F2x[1, 0]9C_x[4A:30] from pass 1 of write levelization
-			   training) - RegisterDelay. */
-			SeedTotal = (uint16_t) (RegisterDelay + ((((uint64_t) SeedTotal - RegisterDelay) *
-								freq_tab[MemClkFreq-3] * 100) / (freq_tab[0] * 100)));
-			Seed_Gross = SeedTotal / 32;
-			Seed_Fine = SeedTotal & 0x1f;
-			if (Seed_Gross == 0)
-				Seed_Gross = 0;
-			else if (Seed_Gross & 0x1)
-				Seed_Gross = 1;
-			else
-				Seed_Gross = 2;
-			pDCTData->WLGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Gross;
-			pDCTData->WLFineDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Fine;
+		if (is_fam15h()) {
+			uint32_t RegisterDelay;
+			int32_t SeedTotal;
+			int32_t SeedTotalPreScaling;
+			uint8_t AddrCmdPrelaunch = 0;		/* TODO: Fetch the correct value from RC2[0] */
+
+			for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++) {
+				if (pDCTData->Status[DCT_STATUS_REGISTERED]) {
+					if (AddrCmdPrelaunch)
+						RegisterDelay = 0x30;
+					else
+						RegisterDelay = 0x20;
+				} else {
+					RegisterDelay = 0;
+				}
+				/* Retrieve WrDqDqsEarly */
+				AmdMemPCIReadBits(MAKE_SBDFO(0,0,24+(pDCTData->NodeId), FUN_DCT, 0xa8), 25, 24, &Value);
+
+				/* Calculate adjusted seed values */
+				SeedTotal = (pDCTData->WLFineDelayPrevPass[MAX_BYTE_LANES*dimm+ByteLane] & 0x1f) |
+					((pDCTData->WLGrossDelayPrevPass[MAX_BYTE_LANES*dimm+ByteLane] & 0x1f) << 5);
+				SeedTotalPreScaling = (SeedTotal - RegisterDelay - (0x20 * Value));
+				SeedTotal = (int32_t) (RegisterDelay + ((((int64_t) SeedTotalPreScaling) *
+					fam15h_freq_tab[MemClkFreq] * 100) / (fam15h_freq_tab[pDCTData->WLPrevMemclkFreq] * 100)));
+
+				if (SeedTotal >= 0) {
+					Seed_Gross = SeedTotal / 32;
+					Seed_Fine = SeedTotal % 32;
+				} else {
+					Seed_Gross = (SeedTotal / 32) - 1;
+					Seed_Fine = (SeedTotal % 32) + 32;
+				}
+
+				if (Seed_Gross == 0)
+					Seed_PreGross = 0;
+				else if (Seed_Gross & 0x1)
+					Seed_PreGross = 1;
+				else
+					Seed_PreGross = 2;
+
+				/* Save seed values for later use */
+				pDCTData->WLSeedGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Gross;
+				pDCTData->WLSeedFineDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Fine;
+				pDCTData->WLSeedPreGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_PreGross;
+
+				pDCTData->WLGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_PreGross;
+				pDCTData->WLFineDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Fine;
+			}
+		} else {
+			u32 RegisterDelay, SeedTotal;
+			for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++)
+			{
+				if (pDCTData->Status[DCT_STATUS_REGISTERED])
+					RegisterDelay = 0x20; /* TODO: ((RCW2 & BIT0) == 0) ? 0x20 : 0x30; */
+				else
+					RegisterDelay = 0;
+				SeedTotal = (pDCTData->WLFineDelay[MAX_BYTE_LANES*dimm+ByteLane] & 0x1f) |
+					(pDCTData->WLGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] << 5);
+				/* SeedTotalPreScaling = (the total delay value in F2x[1, 0]9C_x[4A:30] from pass 1 of write levelization
+				training) - RegisterDelay. */
+				SeedTotal = (uint16_t) (RegisterDelay + ((((uint64_t) SeedTotal - RegisterDelay) *
+									fam10h_freq_tab[MemClkFreq-3] * 100) / (fam10h_freq_tab[0] * 100)));
+				Seed_Gross = SeedTotal / 32;
+				Seed_Fine = SeedTotal & 0x1f;
+				if (Seed_Gross == 0)
+					Seed_Gross = 0;
+				else if (Seed_Gross & 0x1)
+					Seed_Gross = 1;
+				else
+					Seed_Gross = 2;
+				pDCTData->WLGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Gross;
+				pDCTData->WLFineDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Fine;
+			}
 		}
 	}
 
-	setWLByteDelay(pDCTData, ByteLane, dimm, 0);
+	pDCTData->WLPrevMemclkFreq = MemClkFreq;
+	setWLByteDelay(pDCTstat, dct, ByteLane, dimm, 0, pass);
 }
 
 /*-----------------------------------------------------------------------------
- *  void setWLByteDelay(DCTStruct *DCTData, u8 ByteLane, u8 Dimm){
+ *  void setWLByteDelay(struct DCTStatStruc *pDCTstat, uint8_t dct, u8 ByteLane, u8 Dimm){
  *
  *  Description:
  *       This function writes the write levelization byte delay for the Phase
@@ -763,8 +1203,9 @@ void procConifg(sMCTStruct *pMCTData,sDCTStruct *pDCTData, u8 dimm, u8 pass)
  *
  *-----------------------------------------------------------------------------
  */
-void setWLByteDelay(sDCTStruct *pDCTData, u8 ByteLane, u8 dimm, u8 targetAddr)
+void setWLByteDelay(struct DCTStatStruc *pDCTstat, uint8_t dct, u8 ByteLane, u8 dimm, u8 targetAddr, uint8_t pass)
 {
+	sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[dct];
 	u8 fineStartLoc, fineEndLoc, grossStartLoc, grossEndLoc, tempB, index, offsetAddr;
 	u32 addr, fineDelayValue, grossDelayValue, ValueLow, ValueHigh, EccValue, tempW;
 
@@ -777,22 +1218,26 @@ void setWLByteDelay(sDCTStruct *pDCTData, u8 ByteLane, u8 dimm, u8 targetAddr)
 		EccValue = 0;
 		while (ByteLane < MAX_BYTE_LANES)
 		{
-			/* This subtract 0xC workaround might be temporary. */
-			if ((pDCTData->WLPass==2) && (pDCTData->RegMan1Present & (1<<(dimm*2+pDCTData->DctTrain))))
-			{
-				tempW = (pDCTData->WLGrossDelay[index+ByteLane] << 5) | pDCTData->WLFineDelay[index+ByteLane];
-				tempW -= 0xC;
-				pDCTData->WLGrossDelay[index+ByteLane] = (u8)(tempW >> 5);
-				pDCTData->WLFineDelay[index+ByteLane] = (u8)(tempW & 0x1F);
-			}
-			grossDelayValue = pDCTData->WLGrossDelay[index+ByteLane];
-			/* Adjust seed gross delay overflow (greater than 3):
-			 *      - Program seed gross delay as 2 (gross is 4 or 6) or 1 (gross is 5).
-			 *      - Keep original seed gross delay for later reference.
-			 */
-			if(grossDelayValue >= 3)
-			{
-				grossDelayValue = (grossDelayValue&1)? 1 : 2;
+			if (is_fam15h()) {
+				grossDelayValue = pDCTData->WLGrossDelay[index+ByteLane];
+			} else {
+				/* This subtract 0xC workaround might be temporary. */
+				if ((pDCTData->WLPass==2) && (pDCTData->RegMan1Present & (1<<(dimm*2+dct))))
+				{
+					tempW = (pDCTData->WLGrossDelay[index+ByteLane] << 5) | pDCTData->WLFineDelay[index+ByteLane];
+					tempW -= 0xC;
+					pDCTData->WLGrossDelay[index+ByteLane] = (u8)(tempW >> 5);
+					pDCTData->WLFineDelay[index+ByteLane] = (u8)(tempW & 0x1F);
+				}
+				grossDelayValue = pDCTData->WLGrossDelay[index+ByteLane];
+				/* Adjust seed gross delay overflow (greater than 3):
+				 *      - Program seed gross delay as 2 (gross is 4 or 6) or 1 (gross is 5).
+				 *      - Keep original seed gross delay for later reference.
+				 */
+				if(grossDelayValue >= 3)
+				{
+					grossDelayValue = (grossDelayValue&1)? 1 : 2;
+				}
 			}
 			fineDelayValue = pDCTData->WLFineDelay[index+ByteLane];
 			if (ByteLane < 4)
@@ -803,15 +1248,16 @@ void setWLByteDelay(sDCTStruct *pDCTData, u8 ByteLane, u8 dimm, u8 targetAddr)
 				EccValue = ((grossDelayValue << 5) | fineDelayValue);
 			ByteLane++;
 		}
-		set_DCT_ADDR_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, FUN_DCT,
+		set_DCT_ADDR_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT,
 				DRAM_CONT_ADD_PHASE_REC_CTRL_LOW, 0, 31, (u32)ValueLow);
-		set_DCT_ADDR_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, FUN_DCT,
+		set_DCT_ADDR_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT,
 				DRAM_CONT_ADD_PHASE_REC_CTRL_HIGH, 0, 31, (u32)ValueHigh);
-		set_DCT_ADDR_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, FUN_DCT,
+		set_DCT_ADDR_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT,
 				DRAM_CONT_ADD_ECC_PHASE_REC_CTRL, 0, 31, (u32)EccValue);
 	}
 	else
 	{
+		/* Fam10h BKDG Rev. 3.62 2.8.9.9.1 (6) */
 		index = (u8)(MAX_BYTE_LANES * dimm);
 		grossDelayValue = pDCTData->WLGrossDelay[index+ByteLane];
 		fineDelayValue = pDCTData->WLFineDelay[index+ByteLane];
@@ -841,16 +1287,24 @@ void setWLByteDelay(sDCTStruct *pDCTData, u8 ByteLane, u8 dimm, u8 targetAddr)
 		grossStartLoc = (u8)(fineEndLoc + 1);
 		grossEndLoc = (u8)(grossStartLoc + 1);
 
-		set_DCT_ADDR_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, FUN_DCT,
+		set_DCT_ADDR_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT,
 				(u16)addr, fineStartLoc, fineEndLoc,(u32)fineDelayValue);
-		set_DCT_ADDR_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, FUN_DCT,
+		set_DCT_ADDR_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT,
 				(u16)addr, grossStartLoc, grossEndLoc, (u32)grossDelayValue);
+
+		pDCTData->WLFineDelayPrevPass[index+ByteLane] = fineDelayValue;
+		pDCTData->WLGrossDelayPrevPass[index+ByteLane] = grossDelayValue;
+		if (pass == FirstPass) {
+			pDCTData->WLFineDelayFirstPass[index+ByteLane] = fineDelayValue;
+			pDCTData->WLGrossDelayFirstPass[index+ByteLane] = grossDelayValue;
+			pDCTData->WLCriticalGrossDelayFirstPass = pDCTData->WLCriticalGrossDelayPrevPass;
+		}
 	}
 
 }
 
 /*-----------------------------------------------------------------------------
- *  void getWLByteDelay(DCTStruct *DCTData, u8 ByteLane, u8 Dimm)
+ *  void getWLByteDelay(struct DCTStatStruc *pDCTstat, uint8_t dct, u8 ByteLane, u8 Dimm)
  *
  *  Description:
  *       This function reads the write levelization byte delay from the Phase
@@ -868,8 +1322,9 @@ void setWLByteDelay(sDCTStruct *pDCTData, u8 ByteLane, u8 dimm, u8 targetAddr)
  *
  *-----------------------------------------------------------------------------
  */
-void getWLByteDelay(sDCTStruct *pDCTData, u8 ByteLane, u8 dimm)
+void getWLByteDelay(struct DCTStatStruc *pDCTstat, uint8_t dct, u8 ByteLane, u8 dimm, uint8_t pass)
 {
+	sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[dct];
 	u8 fineStartLoc, fineEndLoc, grossStartLoc, grossEndLoc, tempB, tempB1, index;
 	u32 addr, fine, gross;
 	tempB = 0;
@@ -890,25 +1345,31 @@ void getWLByteDelay(sDCTStruct *pDCTData, u8 ByteLane, u8 dimm)
 	grossStartLoc = (u8)(fineEndLoc + 1);
 	grossEndLoc = (u8)(grossStartLoc + 1);
 
-	fine = get_ADD_DCT_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId,
+	fine = get_ADD_DCT_Bits(pDCTData, dct, pDCTData->NodeId,
 				FUN_DCT, (u16)addr, fineStartLoc, fineEndLoc);
-	gross = get_ADD_DCT_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId,
+	gross = get_ADD_DCT_Bits(pDCTData, dct, pDCTData->NodeId,
 				FUN_DCT, (u16)addr, grossStartLoc, grossEndLoc);
-	/* Adjust seed gross delay overflow (greater than 3):
-	 * - Adjust the trained gross delay to the original seed gross delay.
-	 */
-	if (pDCTData->WLGrossDelay[index+ByteLane] >= 3) {
-		gross += pDCTData->WLGrossDelay[index+ByteLane];
-		if(pDCTData->WLGrossDelay[index+ByteLane] & 1)
-			gross -= 1;
-		else
-			gross -= 2;
-	} else if ((pDCTData->WLGrossDelay[index+ByteLane] == 0) && (gross == 3)) {
-		/* If seed gross delay is 0 but PRE result gross delay is 3, it is negative.
-		 * We will then round the negative number to 0.
+
+	if (!is_fam15h()) {
+		/* Adjust seed gross delay overflow (greater than 3):
+		 * - Adjust the trained gross delay to the original seed gross delay.
 		 */
-		gross = 0;
-		fine = 0;
+		if(pDCTData->WLGrossDelay[index+ByteLane] >= 3)
+		{
+			gross += pDCTData->WLGrossDelay[index+ByteLane];
+			if(pDCTData->WLGrossDelay[index+ByteLane] & 1)
+				gross -= 1;
+			else
+				gross -= 2;
+		}
+		else if((pDCTData->WLGrossDelay[index+ByteLane] == 0) && (gross == 3))
+		{
+			/* If seed gross delay is 0 but PRE result gross delay is 3, it is negative.
+			 * We will then round the negative number to 0.
+			 */
+			gross = 0;
+			fine = 0;
+		}
 	}
 	pDCTData->WLFineDelay[index+ByteLane] = (u8)fine;
 	pDCTData->WLGrossDelay[index+ByteLane] = (u8)gross;
diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mutilc_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mutilc_d.c
index 0466c77..cf6afaa 100644
--- a/src/northbridge/amd/amdmct/mct_ddr3/mutilc_d.c
+++ b/src/northbridge/amd/amdmct/mct_ddr3/mutilc_d.c
@@ -2,6 +2,7 @@
  * This file is part of the coreboot project.
  *
  * Copyright (C) 2010 Advanced Micro Devices, Inc.
+ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -134,24 +135,48 @@ static u32 get_Bits(sDCTStruct *pDCTData,
 		u16 offset, u8 low, u8 high)
 {
 	u32 temp;
+	uint32_t dword;
+
 	/* ASSERT(node < MAX_NODES); */
 	if (dct == BOTH_DCTS)
 	{
 		/* Registers exist on DCT0 only */
+		if (is_fam15h())
+		{
+			/* Select DCT 0 */
+			AmdMemPCIRead(MAKE_SBDFO(0,0,24+node,1,0x10c), &dword);
+			dword &= ~0x1;
+			AmdMemPCIWrite(MAKE_SBDFO(0,0,24+node,1,0x10c), &dword);
+		}
+
 		AmdMemPCIReadBits(MAKE_SBDFO(0,0,24+node,func,offset), high, low, &temp);
 	}
 	else
 	{
-		if (dct == 1)
+		if (is_fam15h())
 		{
-			/* Write to dct 1 */
-			offset += 0x100;
+			/* Select DCT */
+			AmdMemPCIRead(MAKE_SBDFO(0,0,24+node,1,0x10c), &dword);
+			dword &= ~0x1;
+			dword |= (dct & 0x1);
+			AmdMemPCIWrite(MAKE_SBDFO(0,0,24+node,1,0x10c), &dword);
+
+			/* Read from the selected DCT */
 			AmdMemPCIReadBits(MAKE_SBDFO(0,0,24+node,func,offset), high, low, &temp);
 		}
 		else
 		{
-			/* Write to dct 0 */
-			AmdMemPCIReadBits(MAKE_SBDFO(0,0,24+node,func,offset), high, low, &temp);
+			if (dct == 1)
+			{
+				/* Read from dct 1 */
+				offset += 0x100;
+				AmdMemPCIReadBits(MAKE_SBDFO(0,0,24+node,func,offset), high, low, &temp);
+			}
+			else
+			{
+				/* Read from dct 0 */
+				AmdMemPCIReadBits(MAKE_SBDFO(0,0,24+node,func,offset), high, low, &temp);
+			}
 		}
 	}
 	return temp;
@@ -184,25 +209,49 @@ static void set_Bits(sDCTStruct *pDCTData,
 		u16 offset, u8 low, u8 high, u32 value)
 {
 	u32 temp;
+	uint32_t dword;
+
 	temp = value;
 
 	if (dct == BOTH_DCTS)
 	{
 		/* Registers exist on DCT0 only */
+		if (is_fam15h())
+		{
+			/* Select DCT 0 */
+			AmdMemPCIRead(MAKE_SBDFO(0,0,24+node,1,0x10c), &dword);
+			dword &= ~0x1;
+			AmdMemPCIWrite(MAKE_SBDFO(0,0,24+node,1,0x10c), &dword);
+		}
+
 		AmdMemPCIWriteBits(MAKE_SBDFO(0,0,24+node,func,offset), high, low, &temp);
 	}
 	else
 	{
-		if (dct == 1)
+		if (is_fam15h())
 		{
-			/* Write to dct 1 */
-			offset += 0x100;
+			/* Select DCT */
+			AmdMemPCIRead(MAKE_SBDFO(0,0,24+node,1,0x10c), &dword);
+			dword &= ~0x1;
+			dword |= (dct & 0x1);
+			AmdMemPCIWrite(MAKE_SBDFO(0,0,24+node,1,0x10c), &dword);
+
+			/* Write to the selected DCT */
 			AmdMemPCIWriteBits(MAKE_SBDFO(0,0,24+node,func,offset), high, low, &temp);
 		}
 		else
 		{
-			/* Write to dct 0 */
-			AmdMemPCIWriteBits(MAKE_SBDFO(0,0,24+node,func,offset), high, low, &temp);
+			if (dct == 1)
+			{
+				/* Write to dct 1 */
+				offset += 0x100;
+				AmdMemPCIWriteBits(MAKE_SBDFO(0,0,24+node,func,offset), high, low, &temp);
+			}
+			else
+			{
+				/* Write to dct 0 */
+				AmdMemPCIWriteBits(MAKE_SBDFO(0,0,24+node,func,offset), high, low, &temp);
+			}
 		}
 	}
 }
diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mwlc_d.h b/src/northbridge/amd/amdmct/mct_ddr3/mwlc_d.h
index f846d87..162340e 100644
--- a/src/northbridge/amd/amdmct/mct_ddr3/mwlc_d.h
+++ b/src/northbridge/amd/amdmct/mct_ddr3/mwlc_d.h
@@ -2,6 +2,7 @@
  * This file is part of the coreboot project.
  *
  * Copyright (C) 2010 Advanced Micro Devices, Inc.
+ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -33,7 +34,8 @@
 #define C_MAX_DIMMS 4		/* Maximum Number of DIMMs on each DCT */
 
 /* STATUS Definition */
-#define DCT_STATUS_REGISTERED 3	/* Registered DIMMs support */
+#define DCT_STATUS_REGISTERED 3		/* Registered DIMMs support */
+#define DCT_STATUS_LOAD_REDUCED 4	/* Load-Reduced DIMMs support */
 #define DCT_STATUS_OnDimmMirror 24	/* OnDimmMirror support */
 
 /* PCI Defintions */
@@ -78,12 +80,18 @@
 #define SendMrsCmd 26
 #define Qoff 12
 #define MRS_Level 7
-#define MrsAddressStart 0
-#define MrsAddressEnd 15
-#define MrsBankStart 16
-#define MrsBankEnd 18
-#define MrsChipSelStart 20
-#define MrsChipSelEnd 22
+#define MrsAddressStartFam10 0
+#define MrsAddressEndFam10 15
+#define MrsAddressStartFam15 0
+#define MrsAddressEndFam15 17
+#define MrsBankStartFam10 16
+#define MrsBankEndFam10 18
+#define MrsBankStartFam15 18
+#define MrsBankEndFam15 20
+#define MrsChipSelStartFam10 20
+#define MrsChipSelEndFam10 22
+#define MrsChipSelStartFam15 21
+#define MrsChipSelEndFam15 23
 #define ASR 18
 #define SRT 19
 #define DramTermDynStart 10
@@ -115,10 +123,32 @@ typedef struct _sDCTStruct
 	u8 DctTrain;			/* Current DCT being trained */
 	u8 CurrDct;			/* Current DCT number (0 or 1) */
 	u8 DctCSPresent;		/* Current DCT CS mapping */
+	int32_t WLSeedGrossDelay[MAX_BYTE_LANES*MAX_LDIMMS];	/* Write Levelization Seed Gross Delay */
+								/* per byte Lane Per Logical DIMM*/
+	int32_t WLSeedFineDelay[MAX_BYTE_LANES*MAX_LDIMMS];	/* Write Levelization Seed Fine Delay */
+								/* per byte Lane Per Logical DIMM*/
+	int32_t WLSeedPreGrossDelay[MAX_BYTE_LANES*MAX_LDIMMS];	/* Write Levelization Seed Pre-Gross Delay */
+								/* per byte Lane Per Logical DIMM*/
 	u8 WLGrossDelay[MAX_BYTE_LANES*MAX_LDIMMS];	/* Write Levelization Gross Delay */
 							/* per byte Lane Per Logical DIMM*/
 	u8 WLFineDelay[MAX_BYTE_LANES*MAX_LDIMMS];	/* Write Levelization Fine Delay */
 							/* per byte Lane Per Logical DIMM*/
+	u8 WLGrossDelayFirstPass[MAX_BYTE_LANES*MAX_LDIMMS];	/* First-Pass Write Levelization Gross Delay */
+								/* per byte Lane Per Logical DIMM*/
+	u8 WLFineDelayFirstPass[MAX_BYTE_LANES*MAX_LDIMMS];	/* First-Pass Write Levelization Fine Delay */
+								/* per byte Lane Per Logical DIMM*/
+	u8 WLGrossDelayPrevPass[MAX_BYTE_LANES*MAX_LDIMMS];	/* Previous Pass Write Levelization Gross Delay */
+								/* per byte Lane Per Logical DIMM*/
+	u8 WLFineDelayPrevPass[MAX_BYTE_LANES*MAX_LDIMMS];	/* Previous Pass Write Levelization Fine Delay */
+								/* per byte Lane Per Logical DIMM*/
+	u8 WLGrossDelayFinalPass[MAX_BYTE_LANES*MAX_LDIMMS];	/* Final-Pass Write Levelization Gross Delay */
+								/* per byte Lane Per Logical DIMM*/
+	u8 WLFineDelayFinalPass[MAX_BYTE_LANES*MAX_LDIMMS];	/* Final-Pass Write Levelization Fine Delay */
+								/* per byte Lane Per Logical DIMM*/
+	int32_t WLCriticalGrossDelayFirstPass;
+	int32_t WLCriticalGrossDelayPrevPass;
+	int32_t WLCriticalGrossDelayFinalPass;
+	uint16_t WLPrevMemclkFreq;
 	u16 RegMan1Present;
 	u8 DimmPresent[MAX_TOTAL_DIMMS];/* Indicates which DIMMs are present */
 					/* from Total Number of DIMMs(per Node)*/
@@ -132,7 +162,7 @@ typedef struct _sDCTStruct
 					/* per byte lane */
 	u8 MaxDimmsInstalled;		/* Max Dimms Installed for current DCT */
 	u8 DimmRanks[MAX_TOTAL_DIMMS];	/* Total Number of Ranks(per Dimm) */
-	u32 LogicalCPUID;
+	uint64_t LogicalCPUID;
 	u8 WLPass;
 } sDCTStruct;
 
diff --git a/src/northbridge/amd/amdmct/mct_ddr3/s3utils.c b/src/northbridge/amd/amdmct/mct_ddr3/s3utils.c
index c9bcac1..aa23951 100644
--- a/src/northbridge/amd/amdmct/mct_ddr3/s3utils.c
+++ b/src/northbridge/amd/amdmct/mct_ddr3/s3utils.c
@@ -18,6 +18,7 @@
  */
 
 #include <string.h>
+#include <arch/cpu.h>
 #include <arch/acpi.h>
 #include <cpu/x86/msr.h>
 #include <device/device.h>
@@ -32,6 +33,23 @@
 
 #define S3NV_FILE_NAME "s3nv"
 
+#ifdef __RAMSTAGE__
+static inline uint8_t is_fam15h(void)
+{
+	uint8_t fam15h = 0;
+	uint32_t family;
+
+	family = cpuid_eax(0x80000001);
+	family = ((family & 0xf00000) >> 16) | ((family & 0xf00) >> 8);
+
+	if (family >= 0x6f)
+		/* Family 15h or later */
+		fam15h = 1;
+
+	return fam15h;
+}
+#endif
+
 static ssize_t get_s3nv_file_offset(void);
 
 ssize_t get_s3nv_file_offset(void)
@@ -47,6 +65,28 @@ ssize_t get_s3nv_file_offset(void)
 	return s3nv_region.region.offset;
 }
 
+static uint32_t read_config32_dct(device_t dev, uint8_t node, uint8_t dct, uint32_t reg) {
+	if (is_fam15h()) {
+		uint32_t dword;
+#ifdef __PRE_RAM__
+		device_t dev_fn1 = PCI_DEV(0, 0x18 + node, 1);
+#else
+		device_t dev_fn1 = dev_find_slot(0, PCI_DEVFN(0x18 + node, 1));
+#endif
+
+		/* Select DCT */
+		dword = pci_read_config32(dev_fn1, 0x10c);
+		dword &= ~0x1;
+		dword |= (dct & 0x1);
+		pci_write_config32(dev_fn1, 0x10c, dword);
+	} else {
+		/* Apply offset */
+		reg += dct * 0x100;
+	}
+
+	return pci_read_config32(dev, reg);
+}
+
 static uint32_t read_amd_dct_index_register(device_t dev, uint32_t index_ctl_reg, uint32_t index)
 {
 	uint32_t dword;
@@ -61,12 +101,54 @@ static uint32_t read_amd_dct_index_register(device_t dev, uint32_t index_ctl_reg
 	return dword;
 }
 
+static uint32_t read_amd_dct_index_register_dct(device_t dev, uint8_t node, uint8_t dct, uint32_t index_ctl_reg, uint32_t index)
+{
+	if (is_fam15h()) {
+		uint32_t dword;
+#ifdef __PRE_RAM__
+		device_t dev_fn1 = PCI_DEV(0, 0x18 + node, 1);
+#else
+		device_t dev_fn1 = dev_find_slot(0, PCI_DEVFN(0x18 + node, 1));
+#endif
+
+		/* Select DCT */
+		dword = pci_read_config32(dev_fn1, 0x10c);
+		dword &= ~0x1;
+		dword |= (dct & 0x1);
+		pci_write_config32(dev_fn1, 0x10c, dword);
+	} else {
+		/* Apply offset */
+		index_ctl_reg += dct * 0x100;
+	}
+
+	return read_amd_dct_index_register(dev, index_ctl_reg, index);
+}
+
 #ifdef __RAMSTAGE__
 static uint64_t rdmsr_uint64_t(unsigned long index) {
 	msr_t msr = rdmsr(index);
 	return (((uint64_t)msr.hi) << 32) | ((uint64_t)msr.lo);
 }
 
+static uint32_t read_config32_dct_nbpstate(device_t dev, uint8_t node, uint8_t dct, uint8_t nb_pstate, uint32_t reg) {
+	uint32_t dword;
+	device_t dev_fn1 = dev_find_slot(0, PCI_DEVFN(0x18 + node, 1));
+
+	/* Select DCT */
+	dword = pci_read_config32(dev_fn1, 0x10c);
+	dword &= ~0x1;
+	dword |= (dct & 0x1);
+	pci_write_config32(dev_fn1, 0x10c, dword);
+
+	/* Select NB Pstate index */
+	dword = pci_read_config32(dev_fn1, 0x10c);
+	dword &= ~(0x3 << 4);
+	dword |= (nb_pstate & 0x3) << 4;
+	pci_write_config32(dev_fn1, 0x10c, dword);
+
+	return pci_read_config32(dev, reg);
+}
+
 void copy_mct_data_to_save_variable(struct amd_s3_persistent_data* persistent_data)
 {
 	uint8_t i;
@@ -82,7 +164,8 @@ void copy_mct_data_to_save_variable(struct amd_s3_persistent_data* persistent_da
 		device_t dev_fn1 = dev_find_slot(0, PCI_DEVFN(0x18 + node, 1));
 		device_t dev_fn2 = dev_find_slot(0, PCI_DEVFN(0x18 + node, 2));
 		device_t dev_fn3 = dev_find_slot(0, PCI_DEVFN(0x18 + node, 3));
-		if ((!dev_fn1) || (!dev_fn2) || (!dev_fn3)) {
+		/* Test for node presence */
+		if ((!dev_fn1) || (pci_read_config32(dev_fn1, PCI_VENDOR_ID) == 0xffffffff)) {
 			persistent_data->node[node].node_present = 0;
 			continue;
 		}
@@ -95,22 +178,22 @@ void copy_mct_data_to_save_variable(struct amd_s3_persistent_data* persistent_da
 			data->f2x110 = pci_read_config32(dev_fn2, 0x110);
 
 			/* Stage 2 */
-			data->f1x40 = pci_read_config32(dev_fn1, 0x40 + (0x100 * channel));
-			data->f1x44 = pci_read_config32(dev_fn1, 0x44 + (0x100 * channel));
-			data->f1x48 = pci_read_config32(dev_fn1, 0x48 + (0x100 * channel));
-			data->f1x4c = pci_read_config32(dev_fn1, 0x4c + (0x100 * channel));
-			data->f1x50 = pci_read_config32(dev_fn1, 0x50 + (0x100 * channel));
-			data->f1x54 = pci_read_config32(dev_fn1, 0x54 + (0x100 * channel));
-			data->f1x58 = pci_read_config32(dev_fn1, 0x58 + (0x100 * channel));
-			data->f1x5c = pci_read_config32(dev_fn1, 0x5c + (0x100 * channel));
-			data->f1x60 = pci_read_config32(dev_fn1, 0x60 + (0x100 * channel));
-			data->f1x64 = pci_read_config32(dev_fn1, 0x64 + (0x100 * channel));
-			data->f1x68 = pci_read_config32(dev_fn1, 0x68 + (0x100 * channel));
-			data->f1x6c = pci_read_config32(dev_fn1, 0x6c + (0x100 * channel));
-			data->f1x70 = pci_read_config32(dev_fn1, 0x70 + (0x100 * channel));
-			data->f1x74 = pci_read_config32(dev_fn1, 0x74 + (0x100 * channel));
-			data->f1x78 = pci_read_config32(dev_fn1, 0x78 + (0x100 * channel));
-			data->f1x7c = pci_read_config32(dev_fn1, 0x7c + (0x100 * channel));
+			data->f1x40 = read_config32_dct(dev_fn1, node, channel, 0x40);
+			data->f1x44 = read_config32_dct(dev_fn1, node, channel, 0x44);
+			data->f1x48 = read_config32_dct(dev_fn1, node, channel, 0x48);
+			data->f1x4c = read_config32_dct(dev_fn1, node, channel, 0x4c);
+			data->f1x50 = read_config32_dct(dev_fn1, node, channel, 0x50);
+			data->f1x54 = read_config32_dct(dev_fn1, node, channel, 0x54);
+			data->f1x58 = read_config32_dct(dev_fn1, node, channel, 0x58);
+			data->f1x5c = read_config32_dct(dev_fn1, node, channel, 0x5c);
+			data->f1x60 = read_config32_dct(dev_fn1, node, channel, 0x60);
+			data->f1x64 = read_config32_dct(dev_fn1, node, channel, 0x64);
+			data->f1x68 = read_config32_dct(dev_fn1, node, channel, 0x68);
+			data->f1x6c = read_config32_dct(dev_fn1, node, channel, 0x6c);
+			data->f1x70 = read_config32_dct(dev_fn1, node, channel, 0x70);
+			data->f1x74 = read_config32_dct(dev_fn1, node, channel, 0x74);
+			data->f1x78 = read_config32_dct(dev_fn1, node, channel, 0x78);
+			data->f1x7c = read_config32_dct(dev_fn1, node, channel, 0x7c);
 			data->f1xf0 = pci_read_config32(dev_fn1, 0xf0);
 			data->f1x120 = pci_read_config32(dev_fn1, 0x120);
 			data->f1x124 = pci_read_config32(dev_fn1, 0x124);
@@ -134,75 +217,144 @@ void copy_mct_data_to_save_variable(struct amd_s3_persistent_data* persistent_da
 			data->msrc001001f = rdmsr_uint64_t(0xc001001f);
 
 			/* Stage 3 */
-			data->f2x40 = pci_read_config32(dev_fn2, 0x40 + (0x100 * channel));
-			data->f2x44 = pci_read_config32(dev_fn2, 0x44 + (0x100 * channel));
-			data->f2x48 = pci_read_config32(dev_fn2, 0x48 + (0x100 * channel));
-			data->f2x4c = pci_read_config32(dev_fn2, 0x4c + (0x100 * channel));
-			data->f2x50 = pci_read_config32(dev_fn2, 0x50 + (0x100 * channel));
-			data->f2x54 = pci_read_config32(dev_fn2, 0x54 + (0x100 * channel));
-			data->f2x58 = pci_read_config32(dev_fn2, 0x58 + (0x100 * channel));
-			data->f2x5c = pci_read_config32(dev_fn2, 0x5c + (0x100 * channel));
-			data->f2x60 = pci_read_config32(dev_fn2, 0x60 + (0x100 * channel));
-			data->f2x64 = pci_read_config32(dev_fn2, 0x64 + (0x100 * channel));
-			data->f2x68 = pci_read_config32(dev_fn2, 0x68 + (0x100 * channel));
-			data->f2x6c = pci_read_config32(dev_fn2, 0x6c + (0x100 * channel));
-			data->f2x78 = pci_read_config32(dev_fn2, 0x78 + (0x100 * channel));
-			data->f2x7c = pci_read_config32(dev_fn2, 0x7c + (0x100 * channel));
-			data->f2x80 = pci_read_config32(dev_fn2, 0x80 + (0x100 * channel));
-			data->f2x84 = pci_read_config32(dev_fn2, 0x84 + (0x100 * channel));
-			data->f2x88 = pci_read_config32(dev_fn2, 0x88 + (0x100 * channel));
-			data->f2x8c = pci_read_config32(dev_fn2, 0x8c + (0x100 * channel));
-			data->f2x90 = pci_read_config32(dev_fn2, 0x90 + (0x100 * channel));
-			data->f2xa4 = pci_read_config32(dev_fn2, 0xa4 + (0x100 * channel));
-			data->f2xa8 = pci_read_config32(dev_fn2, 0xa8 + (0x100 * channel));
+			data->f2x40 = read_config32_dct(dev_fn2, node, channel, 0x40);
+			data->f2x44 = read_config32_dct(dev_fn2, node, channel, 0x44);
+			data->f2x48 = read_config32_dct(dev_fn2, node, channel, 0x48);
+			data->f2x4c = read_config32_dct(dev_fn2, node, channel, 0x4c);
+			data->f2x50 = read_config32_dct(dev_fn2, node, channel, 0x50);
+			data->f2x54 = read_config32_dct(dev_fn2, node, channel, 0x54);
+			data->f2x58 = read_config32_dct(dev_fn2, node, channel, 0x58);
+			data->f2x5c = read_config32_dct(dev_fn2, node, channel, 0x5c);
+			data->f2x60 = read_config32_dct(dev_fn2, node, channel, 0x60);
+			data->f2x64 = read_config32_dct(dev_fn2, node, channel, 0x64);
+			data->f2x68 = read_config32_dct(dev_fn2, node, channel, 0x68);
+			data->f2x6c = read_config32_dct(dev_fn2, node, channel, 0x6c);
+			data->f2x78 = read_config32_dct(dev_fn2, node, channel, 0x78);
+			data->f2x7c = read_config32_dct(dev_fn2, node, channel, 0x7c);
+			data->f2x80 = read_config32_dct(dev_fn2, node, channel, 0x80);
+			data->f2x84 = read_config32_dct(dev_fn2, node, channel, 0x84);
+			data->f2x88 = read_config32_dct(dev_fn2, node, channel, 0x88);
+			data->f2x8c = read_config32_dct(dev_fn2, node, channel, 0x8c);
+			data->f2x90 = read_config32_dct(dev_fn2, node, channel, 0x90);
+			data->f2xa4 = read_config32_dct(dev_fn2, node, channel, 0xa4);
+			data->f2xa8 = read_config32_dct(dev_fn2, node, channel, 0xa8);
+
+			/* Family 15h-specific configuration */
+			if (is_fam15h()) {
+				data->f2x200 = read_config32_dct(dev_fn2, node, channel, 0x200);
+				data->f2x204 = read_config32_dct(dev_fn2, node, channel, 0x204);
+				data->f2x208 = read_config32_dct(dev_fn2, node, channel, 0x208);
+				data->f2x20c = read_config32_dct(dev_fn2, node, channel, 0x20c);
+				for (i=0; i<4; i++)
+					data->f2x210[i] = read_config32_dct_nbpstate(dev_fn2, node, channel, i, 0x210);
+				data->f2x214 = read_config32_dct(dev_fn2, node, channel, 0x214);
+				data->f2x218 = read_config32_dct(dev_fn2, node, channel, 0x218);
+				data->f2x21c = read_config32_dct(dev_fn2, node, channel, 0x21c);
+				data->f2x22c = read_config32_dct(dev_fn2, node, channel, 0x22c);
+				data->f2x230 = read_config32_dct(dev_fn2, node, channel, 0x230);
+				data->f2x234 = read_config32_dct(dev_fn2, node, channel, 0x234);
+				data->f2x238 = read_config32_dct(dev_fn2, node, channel, 0x238);
+				data->f2x23c = read_config32_dct(dev_fn2, node, channel, 0x23c);
+				data->f2x240 = read_config32_dct(dev_fn2, node, channel, 0x240);
+
+				data->f2x9cx0d0fe003 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fe003);
+				data->f2x9cx0d0fe013 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fe013);
+				for (i=0; i<9; i++)
+					data->f2x9cx0d0f0_8_0_1f[i] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f001f | (i << 8));
+				data->f2x9cx0d0f201f = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f201f);
+				data->f2x9cx0d0f211f = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f211f);
+				data->f2x9cx0d0f221f = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f221f);
+				data->f2x9cx0d0f801f = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f801f);
+				data->f2x9cx0d0f811f = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f811f);
+				data->f2x9cx0d0f821f = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f821f);
+				data->f2x9cx0d0fc01f = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fc01f);
+				data->f2x9cx0d0fc11f = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fc11f);
+				data->f2x9cx0d0fc21f = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fc21f);
+				data->f2x9cx0d0f4009 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f4009);
+				for (i=0; i<9; i++)
+					data->f2x9cx0d0f0_8_0_02[i] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f0002 | (i << 8));
+				for (i=0; i<9; i++)
+					data->f2x9cx0d0f0_8_0_06[i] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f0006 | (i << 8));
+				for (i=0; i<9; i++)
+					data->f2x9cx0d0f0_8_0_0a[i] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f000a | (i << 8));
+
+				data->f2x9cx0d0f2002 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f2002);
+				data->f2x9cx0d0f2102 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f2102);
+				data->f2x9cx0d0f2202 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f2202);
+				data->f2x9cx0d0f8002 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f8002);
+				data->f2x9cx0d0f8006 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f8006);
+				data->f2x9cx0d0f800a = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f800a);
+				data->f2x9cx0d0f8102 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f8102);
+				data->f2x9cx0d0f8106 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f8106);
+				data->f2x9cx0d0f810a = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f810a);
+				data->f2x9cx0d0fc002 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fc002);
+				data->f2x9cx0d0fc006 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fc006);
+				data->f2x9cx0d0fc00a = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fc00a);
+				data->f2x9cx0d0fc00e = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fc00e);
+				data->f2x9cx0d0fc012 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fc012);
+
+				data->f2x9cx0d0f2031 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f2031);
+				data->f2x9cx0d0f2131 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f2131);
+				data->f2x9cx0d0f2231 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f2231);
+				data->f2x9cx0d0f8031 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f8031);
+				data->f2x9cx0d0f8131 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f8131);
+				data->f2x9cx0d0f8231 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f8231);
+				data->f2x9cx0d0fc031 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fc031);
+				data->f2x9cx0d0fc131 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fc131);
+				data->f2x9cx0d0fc231 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fc231);
+				for (i=0; i<9; i++)
+					data->f2x9cx0d0f0_0_f_31[i] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f0031 | (i << 8));
+
+				data->f2x9cx0d0f8021 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f8021);
+			}
 
 			/* Stage 4 */
-			data->f2x94 = pci_read_config32(dev_fn2, 0x94 + (0x100 * channel));
+			data->f2x94 = read_config32_dct(dev_fn2, node, channel, 0x94);
 
 			/* Stage 6 */
 			for (i=0; i<9; i++)
 				for (j=0; j<3; j++)
-					data->f2x9cx0d0f0_f_8_0_0_8_4_0[i][j] = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x0d0f0000 | (i << 8) | (j * 4));
-			data->f2x9cx00 = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x00);
-			data->f2x9cx0a = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x0a);
-			data->f2x9cx0c = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x0c);
+					data->f2x9cx0d0f0_f_8_0_0_8_4_0[i][j] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f0000 | (i << 8) | (j * 4));
+			data->f2x9cx00 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x00);
+			data->f2x9cx0a = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0a);
+			data->f2x9cx0c = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0c);
 
 			/* Stage 7 */
-			data->f2x9cx04 = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x04);
+			data->f2x9cx04 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x04);
 
 			/* Stage 9 */
-			data->f2x9cx0d0fe006 = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x0d0fe006);
-			data->f2x9cx0d0fe007 = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x0d0fe007);
+			data->f2x9cx0d0fe006 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fe006);
+			data->f2x9cx0d0fe007 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fe007);
 
 			/* Stage 10 */
 			for (i=0; i<12; i++)
-				data->f2x9cx10[i] = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x10 + i);
+				data->f2x9cx10[i] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x10 + i);
 			for (i=0; i<12; i++)
-				data->f2x9cx20[i] = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x20 + i);
+				data->f2x9cx20[i] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x20 + i);
 			for (i=0; i<4; i++)
 				for (j=0; j<3; j++)
-					data->f2x9cx3_0_0_3_1[i][j] = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), (0x01 + i) + (0x100 * j));
+					data->f2x9cx3_0_0_3_1[i][j] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, (0x01 + i) + (0x100 * j));
 			for (i=0; i<4; i++)
 				for (j=0; j<3; j++)
-					data->f2x9cx3_0_0_7_5[i][j] = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), (0x05 + i) + (0x100 * j));
-			data->f2x9cx0d = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x0d);
+					data->f2x9cx3_0_0_7_5[i][j] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, (0x05 + i) + (0x100 * j));
+			data->f2x9cx0d = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d);
 			for (i=0; i<9; i++)
-				data->f2x9cx0d0f0_f_0_13[i] = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x0d0f0013 | (i << 8));
+				data->f2x9cx0d0f0_f_0_13[i] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f0013 | (i << 8));
 			for (i=0; i<9; i++)
-				data->f2x9cx0d0f0_f_0_30[i] = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x0d0f0030 | (i << 8));
+				data->f2x9cx0d0f0_f_0_30[i] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f0030 | (i << 8));
 			for (i=0; i<4; i++)
-				data->f2x9cx0d0f2_f_0_30[i] = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x0d0f2030 | (i << 8));
+				data->f2x9cx0d0f2_f_0_30[i] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f2030 | (i << 8));
 			for (i=0; i<2; i++)
 				for (j=0; j<3; j++)
-					data->f2x9cx0d0f8_8_4_0[i][j] = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x0d0f0000 | (i << 8) | (j * 4));
-			data->f2x9cx0d0f812f = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x0d0f812f);
+					data->f2x9cx0d0f8_8_4_0[i][j] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f0000 | (i << 8) | (j * 4));
+			data->f2x9cx0d0f812f = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f812f);
 
 			/* Stage 11 */
 			if (IS_ENABLED(CONFIG_DIMM_DDR3)) {
 				for (i=0; i<12; i++)
-					data->f2x9cx30[i] = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x30 + i);
+					data->f2x9cx30[i] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x30 + i);
 				for (i=0; i<12; i++)
-					data->f2x9cx40[i] = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x40 + i);
+					data->f2x9cx40[i] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x40 + i);
 			}
 
 			/* Other */
@@ -212,6 +364,43 @@ void copy_mct_data_to_save_variable(struct amd_s3_persistent_data* persistent_da
 	}
 }
 #else
+static void write_config32_dct(device_t dev, uint8_t node, uint8_t dct, uint32_t reg, uint32_t value) {
+	if (is_fam15h()) {
+		uint32_t dword;
+		device_t dev_fn1 = PCI_DEV(0, 0x18 + node, 1);
+
+		/* Select DCT */
+		dword = pci_read_config32(dev_fn1, 0x10c);
+		dword &= ~0x1;
+		dword |= (dct & 0x1);
+		pci_write_config32(dev_fn1, 0x10c, dword);
+	} else {
+		/* Apply offset */
+		reg += dct * 0x100;
+	}
+
+	pci_write_config32(dev, reg, value);
+}
+
+static void write_config32_dct_nbpstate(device_t dev, uint8_t node, uint8_t dct, uint8_t nb_pstate, uint32_t reg, uint32_t value) {
+	uint32_t dword;
+	device_t dev_fn1 = PCI_DEV(0, 0x18 + node, 1);
+
+	/* Select DCT */
+	dword = pci_read_config32(dev_fn1, 0x10c);
+	dword &= ~0x1;
+	dword |= (dct & 0x1);
+	pci_write_config32(dev_fn1, 0x10c, dword);
+
+	/* Select NB Pstate index */
+	dword = pci_read_config32(dev_fn1, 0x10c);
+	dword &= ~(0x3 << 4);
+	dword |= (nb_pstate & 0x3) << 4;
+	pci_write_config32(dev_fn1, 0x10c, dword);
+
+	pci_write_config32(dev, reg, value);
+}
+
 static void write_amd_dct_index_register(device_t dev, uint32_t index_ctl_reg, uint32_t index, uint32_t value)
 {
 	uint32_t dword;
@@ -223,6 +412,25 @@ static void write_amd_dct_index_register(device_t dev, uint32_t index_ctl_reg, u
 		dword = pci_read_config32(dev, index_ctl_reg);
 	} while (!(dword & (1 << 31)));
 }
+
+static void write_amd_dct_index_register_dct(device_t dev, uint8_t node, uint8_t dct, uint32_t index_ctl_reg, uint32_t index, uint32_t value)
+{
+	if (is_fam15h()) {
+		uint32_t dword;
+		device_t dev_fn1 = PCI_DEV(0, 0x18 + node, 1);
+
+		/* Select DCT */
+		dword = pci_read_config32(dev_fn1, 0x10c);
+		dword &= ~0x1;
+		dword |= (dct & 0x1);
+		pci_write_config32(dev_fn1, 0x10c, dword);
+	} else {
+		/* Apply offset */
+		index_ctl_reg += dct * 0x100;
+	}
+
+	return write_amd_dct_index_register(dev, index_ctl_reg, index, value);
+}
 #endif
 
 #ifdef __PRE_RAM__
@@ -262,31 +470,31 @@ void restore_mct_data_from_save_variable(struct amd_s3_persistent_data* persiste
 			if (!persistent_data->node[node].node_present)
 				continue;
 
-			pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x40 + (0x100 * channel), data->f1x40);
-			pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x44 + (0x100 * channel), data->f1x44);
-			pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x48 + (0x100 * channel), data->f1x48);
-			pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x4c + (0x100 * channel), data->f1x4c);
-			pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x50 + (0x100 * channel), data->f1x50);
-			pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x54 + (0x100 * channel), data->f1x54);
-			pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x58 + (0x100 * channel), data->f1x58);
-			pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x5c + (0x100 * channel), data->f1x5c);
-			pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x60 + (0x100 * channel), data->f1x60);
-			pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x64 + (0x100 * channel), data->f1x64);
-			pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x68 + (0x100 * channel), data->f1x68);
-			pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x6c + (0x100 * channel), data->f1x6c);
-			pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x70 + (0x100 * channel), data->f1x70);
-			pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x74 + (0x100 * channel), data->f1x74);
-			pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x78 + (0x100 * channel), data->f1x78);
-			pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x7c + (0x100 * channel), data->f1x7c);
-			pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0xf0 + (0x100 * channel), data->f1xf0);
-			pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x120 + (0x100 * channel), data->f1x120);
-			pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x124 + (0x100 * channel), data->f1x124);
-			pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x10c + (0x100 * channel), data->f2x10c);
-			pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x114 + (0x100 * channel), data->f2x114);
-			pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x118 + (0x100 * channel), data->f2x118);
-			pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x11c + (0x100 * channel), data->f2x11c);
-			pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x1b0 + (0x100 * channel), data->f2x1b0);
-			pci_write_config32(PCI_DEV(0, 0x18 + node, 3), 0x44 + (0x100 * channel), data->f3x44);
+			write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x40, data->f1x40);
+			write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x44, data->f1x44);
+			write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x48, data->f1x48);
+			write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x4c, data->f1x4c);
+			write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x50, data->f1x50);
+			write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x54, data->f1x54);
+			write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x58, data->f1x58);
+			write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x5c, data->f1x5c);
+			write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x60, data->f1x60);
+			write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x64, data->f1x64);
+			write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x68, data->f1x68);
+			write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x6c, data->f1x6c);
+			write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x70, data->f1x70);
+			write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x74, data->f1x74);
+			write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x78, data->f1x78);
+			write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x7c, data->f1x7c);
+			write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0xf0, data->f1xf0);
+			write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x120, data->f1x120);
+			write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x124, data->f1x124);
+			write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x10c, data->f2x10c);
+			write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x114, data->f2x114);
+			write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x118, data->f2x118);
+			write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x11c, data->f2x11c);
+			write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x1b0, data->f2x1b0);
+			write_config32_dct(PCI_DEV(0, 0x18 + node, 3), node, channel, 0x44, data->f3x44);
 			for (i=0; i<16; i++) {
 				wrmsr_uint64_t(0x00000200 | i, data->msr0000020[i]);
 			}
@@ -313,31 +521,97 @@ void restore_mct_data_from_save_variable(struct amd_s3_persistent_data* persiste
 			if (!persistent_data->node[node].node_present)
 				continue;
 
-			ganged = !!(data->f2x110 & 0x10);
+			if (is_fam15h())
+				ganged = 0;
+			else
+				ganged = !!(data->f2x110 & 0x10);
 			if ((ganged == 1) && (channel > 0))
 				continue;
 
-			pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x40 + (0x100 * channel), data->f2x40);
-			pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x44 + (0x100 * channel), data->f2x44);
-			pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x48 + (0x100 * channel), data->f2x48);
-			pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x4c + (0x100 * channel), data->f2x4c);
-			pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x50 + (0x100 * channel), data->f2x50);
-			pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x54 + (0x100 * channel), data->f2x54);
-			pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x58 + (0x100 * channel), data->f2x58);
-			pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x5c + (0x100 * channel), data->f2x5c);
-			pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x60 + (0x100 * channel), data->f2x60);
-			pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x64 + (0x100 * channel), data->f2x64);
-			pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x68 + (0x100 * channel), data->f2x68);
-			pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x6c + (0x100 * channel), data->f2x6c);
-			pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x78 + (0x100 * channel), data->f2x78);
-			pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x7c + (0x100 * channel), data->f2x7c);
-			pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x80 + (0x100 * channel), data->f2x80);
-			pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x84 + (0x100 * channel), data->f2x84);
-			pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x88 + (0x100 * channel), data->f2x88);
-			pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x8c + (0x100 * channel), data->f2x8c);
-			pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x90 + (0x100 * channel), data->f2x90);
-			pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0xa4 + (0x100 * channel), data->f2xa4);
-			pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0xa8 + (0x100 * channel), data->f2xa8);
+			write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x40, data->f2x40);
+			write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x44, data->f2x44);
+			write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x48, data->f2x48);
+			write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x4c, data->f2x4c);
+			write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x50, data->f2x50);
+			write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x54, data->f2x54);
+			write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x58, data->f2x58);
+			write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x5c, data->f2x5c);
+			write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x60, data->f2x60);
+			write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x64, data->f2x64);
+			write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x68, data->f2x68);
+			write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x6c, data->f2x6c);
+			write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x78, data->f2x78);
+			write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x7c, data->f2x7c);
+			write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x80, data->f2x80);
+			write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x84, data->f2x84);
+			write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x88, data->f2x88);
+			write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x8c, data->f2x8c);
+			write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x90, data->f2x90);
+			write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0xa4, data->f2xa4);
+			write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0xa8, data->f2xa8);
+		}
+	}
+
+	/* Family 15h-specific configuration */
+	if (is_fam15h()) {
+		for (node = 0; node < MAX_NODES_SUPPORTED; node++) {
+			for (channel = 0; channel < 2; channel++) {
+				struct amd_s3_persistent_mct_channel_data* data = &persistent_data->node[node].channel[channel];
+				if (!persistent_data->node[node].node_present)
+					continue;
+
+				/* Initialize DCT */
+				write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0000000b, 0x80000000);
+				dword = read_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fe013);
+				dword &= ~0xffff;
+				dword |= 0x118;
+				write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fe013, dword);
+
+				/* Restore values */
+				write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x200, data->f2x200);
+				write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x204, data->f2x204);
+				write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x208, data->f2x208);
+				write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x20c, data->f2x20c);
+				for (i=0; i<4; i++)
+					write_config32_dct_nbpstate(PCI_DEV(0, 0x18 + node, 2), node, channel, i, 0x210, data->f2x210[i]);
+				write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x214, data->f2x214);
+				write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x218, data->f2x218);
+				write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x21c, data->f2x21c);
+				write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x22c, data->f2x22c);
+				write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x230, data->f2x230);
+				write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x234, data->f2x234);
+				write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x238, data->f2x238);
+				write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x23c, data->f2x23c);
+				write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x240, data->f2x240);
+
+				write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fe013, data->f2x9cx0d0fe013);
+				for (i=0; i<9; i++)
+					write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f001f | (i << 8), data->f2x9cx0d0f0_8_0_1f[i]);
+				write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f201f, data->f2x9cx0d0f201f);
+				write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f211f, data->f2x9cx0d0f211f);
+				write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f221f, data->f2x9cx0d0f221f);
+				write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f801f, data->f2x9cx0d0f801f);
+				write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f811f, data->f2x9cx0d0f811f);
+				write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f821f, data->f2x9cx0d0f821f);
+				write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fc01f, data->f2x9cx0d0fc01f);
+				write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fc11f, data->f2x9cx0d0fc11f);
+				write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fc21f, data->f2x9cx0d0fc21f);
+				write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f4009, data->f2x9cx0d0f4009);
+
+				write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f2031, data->f2x9cx0d0f2031);
+				write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f2131, data->f2x9cx0d0f2131);
+				write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f2231, data->f2x9cx0d0f2231);
+				write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f8031, data->f2x9cx0d0f8031);
+				write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f8131, data->f2x9cx0d0f8131);
+				write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f8231, data->f2x9cx0d0f8231);
+				write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fc031, data->f2x9cx0d0fc031);
+				write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fc131, data->f2x9cx0d0fc131);
+				write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fc231, data->f2x9cx0d0fc231);
+				for (i=0; i<9; i++)
+					write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f0031 | (i << 8), data->f2x9cx0d0f0_0_f_31[i]);
+
+				write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f8021, data->f2x9cx0d0f8021);
+			}
 		}
 	}
 
@@ -348,33 +622,44 @@ void restore_mct_data_from_save_variable(struct amd_s3_persistent_data* persiste
 			if (!persistent_data->node[node].node_present)
 				continue;
 
-			ganged = !!(data->f2x110 & 0x10);
+			if (is_fam15h())
+				ganged = 0;
+			else
+				ganged = !!(data->f2x110 & 0x10);
 			if ((ganged == 1) && (channel > 0))
 				continue;
 
-			/* Disable PHY auto-compensation engine */
-			dword = read_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x08);
-			if (!(dword & (1 << 30))) {
-				dword |= (1 << 30);
-				write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x08, dword);
-
-				/* Wait for 5us */
-				mct_Wait(100);
+			if (is_fam15h()) {
+				/* Program PllLockTime = 0x190 */
+				dword = read_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fe006);
+				dword &= ~0xffff;
+				dword |= 0x190;
+				write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fe006, dword);
+
+				/* Program MemClkFreqVal = 0 */
+				dword = read_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x94);
+				dword &= (0x1 << 7);
+				write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x94, dword);
+
+				/* Restore DRAM Adddress/Timing Control Register */
+				write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x04, data->f2x9cx04);
+			} else {
+				/* Disable PHY auto-compensation engine */
+				dword = read_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x08);
+				if (!(dword & (1 << 30))) {
+					dword |= (1 << 30);
+					write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x08, dword);
+
+					/* Wait for 5us */
+					mct_Wait(100);
+				}
 			}
 
 			/* Restore DRAM Configuration High Register */
-			pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x94 + (0x100 * channel), data->f2x94);
-
-			/* Enable PHY auto-compensation engine */
-			dword = read_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x08);
-			dword &= ~(1 << 30);
-			write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x08, dword);
+			write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x94, data->f2x94);
 		}
 	}
 
-	/* Wait for 750us */
-	mct_Wait(15000);
-
 	/* Stage 5 */
 	for (node = 0; node < MAX_NODES_SUPPORTED; node++) {
 		for (channel = 0; channel < 2; channel++) {
@@ -382,17 +667,40 @@ void restore_mct_data_from_save_variable(struct amd_s3_persistent_data* persiste
 			if (!persistent_data->node[node].node_present)
 				continue;
 
-			ganged = !!(data->f2x110 & 0x10);
+			if (is_fam15h())
+				ganged = 0;
+			else
+				ganged = !!(data->f2x110 & 0x10);
 			if ((ganged == 1) && (channel > 0))
 				continue;
 
+			dct_enabled = !(data->f2x94 & (1 << 14));
+			if (!dct_enabled)
+				continue;
+
 			/* Wait for any pending PHY frequency changes to complete */
 			do {
-				dword = read_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x08);
+				dword = read_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x94);
 			} while (dword & (1 << 21));
+
+			if (is_fam15h()) {
+				/* Program PllLockTime = 0xf */
+				dword = read_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fe006);
+				dword &= ~0xffff;
+				dword |= 0xf;
+				write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fe006, dword);
+			} else {
+				/* Enable PHY auto-compensation engine */
+				dword = read_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x08);
+				dword &= ~(1 << 30);
+				write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x08, dword);
+			}
 		}
 	}
 
+	/* Wait for 750us */
+	mct_Wait(15000);
+
 	/* Stage 6 */
 	for (node = 0; node < MAX_NODES_SUPPORTED; node++) {
 		for (channel = 0; channel < 2; channel++) {
@@ -402,10 +710,49 @@ void restore_mct_data_from_save_variable(struct amd_s3_persistent_data* persiste
 
 			for (i=0; i<9; i++)
 				for (j=0; j<3; j++)
-					write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x0d0f0000 | (i << 8) | (j * 4), data->f2x9cx0d0f0_f_8_0_0_8_4_0[i][j]);
-			write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x00, data->f2x9cx00);
-			write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x0a, data->f2x9cx0a);
-			write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x0c, data->f2x9cx0c);
+					write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f0000 | (i << 8) | (j * 4), data->f2x9cx0d0f0_f_8_0_0_8_4_0[i][j]);
+			write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x00, data->f2x9cx00);
+			write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0a, data->f2x9cx0a);
+			write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0c, data->f2x9cx0c);
+		}
+	}
+
+	/* Family 15h-specific configuration */
+	if (is_fam15h()) {
+		for (node = 0; node < MAX_NODES_SUPPORTED; node++) {
+			for (channel = 0; channel < 2; channel++) {
+				struct amd_s3_persistent_mct_channel_data* data = &persistent_data->node[node].channel[channel];
+				if (!persistent_data->node[node].node_present)
+					continue;
+
+				dword = read_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fe003);
+				dword |= (0x3 << 13);			/* DisAutoComp, DisablePredriverCal = 1 */
+				write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fe003, dword);
+
+				for (i=0; i<9; i++)
+					write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f0006 | (i << 8), data->f2x9cx0d0f0_8_0_06[i]);
+				for (i=0; i<9; i++)
+					write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f000a | (i << 8), data->f2x9cx0d0f0_8_0_0a[i]);
+				for (i=0; i<9; i++)
+					write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f0002 | (i << 8), (0x8000 | data->f2x9cx0d0f0_8_0_02[i]));
+
+				write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f8006, data->f2x9cx0d0f8006);
+				write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f800a, data->f2x9cx0d0f800a);
+				write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f8106, data->f2x9cx0d0f8106);
+				write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f810a, data->f2x9cx0d0f810a);
+				write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fc006, data->f2x9cx0d0fc006);
+				write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fc00a, data->f2x9cx0d0fc00a);
+				write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fc00e, data->f2x9cx0d0fc00e);
+				write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fc012, data->f2x9cx0d0fc012);
+				write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f8002, (0x8000 | data->f2x9cx0d0f8002));
+				write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f8102, (0x8000 | data->f2x9cx0d0f8102));
+				write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fc002, (0x8000 | data->f2x9cx0d0fc002));
+				write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f2002, (0x8000 | data->f2x9cx0d0f2002));
+				write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f2102, (0x8000 | data->f2x9cx0d0f2102));
+				write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f2202, (0x8000 | data->f2x9cx0d0f2202));
+
+				write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fe003, data->f2x9cx0d0fe003);
+			}
 		}
 	}
 
@@ -416,11 +763,15 @@ void restore_mct_data_from_save_variable(struct amd_s3_persistent_data* persiste
 			if (!persistent_data->node[node].node_present)
 				continue;
 
-			ganged = !!(data->f2x110 & 0x10);
+			if (is_fam15h())
+				ganged = 0;
+			else
+				ganged = !!(data->f2x110 & 0x10);
 			if ((ganged == 1) && (channel > 0))
 				continue;
 
-			write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x04, data->f2x9cx04);
+			if (!is_fam15h())
+				write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x04, data->f2x9cx04);
 		}
 	}
 
@@ -435,16 +786,19 @@ void restore_mct_data_from_save_variable(struct amd_s3_persistent_data* persiste
 			if (!dct_enabled)
 				continue;
 
-			ganged = !!(data->f2x110 & 0x10);
+			if (is_fam15h())
+				ganged = 0;
+			else
+				ganged = !!(data->f2x110 & 0x10);
 			if ((ganged == 1) && (channel > 0))
 				continue;
 
 			printk(BIOS_SPEW, "Taking DIMMs out of self refresh node: %d channel: %d\n", node, channel);
 
 			/* Exit self refresh mode */
-			dword = pci_read_config32(PCI_DEV(0, 0x18 + node, 2), 0x90 + (0x100 * channel));
+			dword = read_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x90);
 			dword |= (1 << 1);
-			pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x90 + (0x100 * channel), dword);
+			write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x90, dword);
 		}
 	}
 
@@ -463,12 +817,12 @@ void restore_mct_data_from_save_variable(struct amd_s3_persistent_data* persiste
 
 			/* Wait for transition from self refresh mode to complete */
 			do {
-				dword = pci_read_config32(PCI_DEV(0, 0x18 + node, 2), 0x90 + (0x100 * channel));
+				dword = read_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x90);
 			} while (dword & (1 << 1));
 
 			/* Restore registers */
-			write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x0d0fe006, data->f2x9cx0d0fe006);
-			write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x0d0fe007, data->f2x9cx0d0fe007);
+			write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fe006, data->f2x9cx0d0fe006);
+			write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fe007, data->f2x9cx0d0fe007);
 		}
 	}
 
@@ -480,26 +834,26 @@ void restore_mct_data_from_save_variable(struct amd_s3_persistent_data* persiste
 				continue;
 
 			for (i=0; i<12; i++)
-				write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x10 + i, data->f2x9cx10[i]);
+				write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x10 + i, data->f2x9cx10[i]);
 			for (i=0; i<12; i++)
-				write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x20 + i, data->f2x9cx20[i]);
+				write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x20 + i, data->f2x9cx20[i]);
 			for (i=0; i<4; i++)
 				for (j=0; j<3; j++)
-					write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), (0x01 + i) + (0x100 * j), data->f2x9cx3_0_0_3_1[i][j]);
+					write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, (0x01 + i) + (0x100 * j), data->f2x9cx3_0_0_3_1[i][j]);
 			for (i=0; i<4; i++)
 				for (j=0; j<3; j++)
-					write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), (0x05 + i) + (0x100 * j), data->f2x9cx3_0_0_7_5[i][j]);
-			write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x0d, data->f2x9cx0d);
+					write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, (0x05 + i) + (0x100 * j), data->f2x9cx3_0_0_7_5[i][j]);
+			write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d, data->f2x9cx0d);
 			for (i=0; i<9; i++)
-				write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x0d0f0013 | (i << 8), data->f2x9cx0d0f0_f_0_13[i]);
+				write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f0013 | (i << 8), data->f2x9cx0d0f0_f_0_13[i]);
 			for (i=0; i<9; i++)
-				write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x0d0f0030 | (i << 8), data->f2x9cx0d0f0_f_0_30[i]);
+				write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f0030 | (i << 8), data->f2x9cx0d0f0_f_0_30[i]);
 			for (i=0; i<4; i++)
-				write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x0d0f2030 | (i << 8), data->f2x9cx0d0f2_f_0_30[i]);
+				write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f2030 | (i << 8), data->f2x9cx0d0f2_f_0_30[i]);
 			for (i=0; i<2; i++)
 				for (j=0; j<3; j++)
-					write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x0d0f0000 | (i << 8) | (j * 4), data->f2x9cx0d0f8_8_4_0[i][j]);
-			write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x0d0f812f, data->f2x9cx0d0f812f);
+					write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f0000 | (i << 8) | (j * 4), data->f2x9cx0d0f8_8_4_0[i][j]);
+			write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f812f, data->f2x9cx0d0f812f);
 		}
 	}
 
@@ -512,9 +866,9 @@ void restore_mct_data_from_save_variable(struct amd_s3_persistent_data* persiste
 					continue;
 
 				for (i=0; i<12; i++)
-					write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x30 + i, data->f2x9cx30[i]);
+					write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x30 + i, data->f2x9cx30[i]);
 				for (i=0; i<12; i++)
-					write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x40 + i, data->f2x9cx40[i]);
+					write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x40 + i, data->f2x9cx40[i]);
 			}
 		}
 	}
diff --git a/src/northbridge/amd/amdmct/wrappers/mcti.h b/src/northbridge/amd/amdmct/wrappers/mcti.h
index 38e66e1..2aba377 100644
--- a/src/northbridge/amd/amdmct/wrappers/mcti.h
+++ b/src/northbridge/amd/amdmct/wrappers/mcti.h
@@ -2,6 +2,7 @@
  * This file is part of the coreboot project.
  *
  * Copyright (C) 2007 Advanced Micro Devices, Inc.
+ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -62,10 +63,15 @@ UPDATE AS NEEDED
 #endif
 
 #ifndef MEM_MAX_LOAD_FREQ
-#if (CONFIG_DIMM_SUPPORT & 0x000F)==0x0005 /* AMD_FAM10_DDR3 */
- #define MEM_MAX_LOAD_FREQ		800
-#else
- #define MEM_MAX_LOAD_FREQ		400
+#if (CONFIG_DIMM_SUPPORT & 0x000F)==0x0005 	/* AMD_FAM10_DDR3 */
+ #define MEM_MAX_LOAD_FREQ			933
+ #define MEM_MIN_PLATFORM_FREQ_FAM10		400
+ #define MEM_MIN_PLATFORM_FREQ_FAM15		333
+#else						 /* AMD_FAM10_DDR2 */
+ #define MEM_MAX_LOAD_FREQ			400
+ #define MEM_MIN_PLATFORM_FREQ_FAM10		200
+ /* DDR2 not available on Family 15h */
+ #define MEM_MIN_PLATFORM_FREQ_FAM15		0
 #endif
 #endif
 
diff --git a/src/northbridge/amd/amdmct/wrappers/mcti_d.c b/src/northbridge/amd/amdmct/wrappers/mcti_d.c
index 444adc5..9969c4f 100644
--- a/src/northbridge/amd/amdmct/wrappers/mcti_d.c
+++ b/src/northbridge/amd/amdmct/wrappers/mcti_d.c
@@ -44,7 +44,7 @@
 #define MINIMUM_DRAM_BELOW_4G 0x1000000
 
 static const uint16_t ddr2_limits[4] = {400, 333, 266, 200};
-static const uint16_t ddr3_limits[4] = {800, 666, 533, 400};
+static const uint16_t ddr3_limits[16] = {933, 800, 666, 533, 400, 333, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
 
 static u16 mctGet_NVbits(u8 index)
 {
@@ -81,12 +81,19 @@ static u16 mctGet_NVbits(u8 index)
 		if (get_option(&nvram, "max_mem_clock") == CB_SUCCESS) {
 			int limit = val;
 			if (IS_ENABLED(CONFIG_DIMM_DDR3))
-				limit = ddr3_limits[nvram & 3];
+				limit = ddr3_limits[nvram & 0xf];
 			else if (IS_ENABLED(CONFIG_DIMM_DDR2))
-				limit = ddr2_limits[nvram & 3];
+				limit = ddr2_limits[nvram & 0x3];
 			val = min(limit, val);
 		}
 		break;
+	case NV_MIN_MEMCLK:
+		/* Minimum platform supported memclk */
+		if (is_fam15h())
+			val =  MEM_MIN_PLATFORM_FREQ_FAM15;
+		else
+			val =  MEM_MIN_PLATFORM_FREQ_FAM10;
+		break;
 	case NV_ECC_CAP:
 #if SYSTEM_TYPE == SERVER
 		val = 1;	/* memory bus ECC capable */
@@ -254,6 +261,9 @@ static u16 mctGet_NVbits(u8 index)
 	case NV_L2BKScrub:
 		val = 0;	/* Disabled - See L2Scrub in BKDG */
 		break;
+	case NV_L3BKScrub:
+		val = 0;	/* Disabled - See L3Scrub in BKDG */
+		break;
 	case NV_DCBKScrub:
 		val = 0;	/* Disabled - See DcacheScrub in BKDG */
 		break;
@@ -303,6 +313,9 @@ static void mctGet_MaxLoadFreq(struct DCTStatStruc *pDCTstat)
 	int ch2_count = 0;
 	uint8_t ch1_registered = 0;
 	uint8_t ch2_registered = 0;
+	uint8_t ch1_voltage = 0;
+	uint8_t ch2_voltage = 0;
+	uint8_t highest_rank_count[2];
 	int i;
 	for (i = 0; i < 15; i = i + 2) {
 		if (pDCTstat->DIMMValid & (1 << i))
@@ -321,8 +334,28 @@ static void mctGet_MaxLoadFreq(struct DCTStatStruc *pDCTstat)
 		printk(BIOS_DEBUG, "mctGet_MaxLoadFreq: Channel 2: %d DIMM(s) detected\n", ch2_count);
 	}
 
+#if (CONFIG_DIMM_SUPPORT & 0x000F)==0x0005 /* AMD_FAM10_DDR3 */
+	uint8_t dimm;
+
+	for (i = 0; i < 15; i = i + 2) {
+		if (pDCTstat->DIMMValid & (1 << i))
+			ch1_voltage |= pDCTstat->DimmConfiguredVoltage[i];
+		if (pDCTstat->DIMMValid & (1 << (i + 1)))
+			ch2_voltage |= pDCTstat->DimmConfiguredVoltage[i + 1];
+	}
+
+	for (i = 0; i < 2; i++) {
+		sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[i];
+		highest_rank_count[i] = 0x0;
+		for (dimm = 0; dimm < 8; dimm++) {
+			if (pDCTData->DimmRanks[dimm] > highest_rank_count[i])
+				highest_rank_count[i] = pDCTData->DimmRanks[dimm];
+		}
+	}
+#endif
+
 	/* Set limits if needed */
-	pDCTstat->PresetmaxFreq = mct_MaxLoadFreq(max(ch1_count, ch2_count), (ch1_registered || ch2_registered), pDCTstat->PresetmaxFreq);
+	pDCTstat->PresetmaxFreq = mct_MaxLoadFreq(max(ch1_count, ch2_count), max(highest_rank_count[0], highest_rank_count[1]), (ch1_registered || ch2_registered), (ch1_voltage | ch2_voltage), pDCTstat->PresetmaxFreq);
 }
 
 #ifdef UNUSED_CODE
@@ -486,7 +519,7 @@ static void mctHookAfterAnyTraining(void)
 {
 }
 
-static u32 mctGetLogicalCPUID_D(u8 node)
+static uint64_t mctGetLogicalCPUID_D(u8 node)
 {
 	return mctGetLogicalCPUID(node);
 }
-- 
1.7.9.5