|
@@ -123,7 +123,7 @@ static void f15h_select_dct(struct amd64_pvt *pvt, u8 dct)
|
|
|
u32 reg = 0;
|
|
|
|
|
|
amd64_read_pci_cfg(pvt->F1, DCT_CFG_SEL, ®);
|
|
|
- reg &= 0xfffffffe;
|
|
|
+ reg &= (pvt->model >= 0x30) ? ~3 : ~1;
|
|
|
reg |= dct;
|
|
|
amd64_write_pci_cfg(pvt->F1, DCT_CFG_SEL, reg);
|
|
|
}
|
|
@@ -133,8 +133,9 @@ static int f15_read_dct_pci_cfg(struct amd64_pvt *pvt, int addr, u32 *val,
|
|
|
{
|
|
|
u8 dct = 0;
|
|
|
|
|
|
+ /* For F15 M30h, the second dct is DCT 3, refer to BKDG Section 2.10 */
|
|
|
if (addr >= 0x140 && addr <= 0x1a0) {
|
|
|
- dct = 1;
|
|
|
+ dct = (pvt->model >= 0x30) ? 3 : 1;
|
|
|
addr -= 0x100;
|
|
|
}
|
|
|
|
|
@@ -205,8 +206,10 @@ static int amd64_set_scrub_rate(struct mem_ctl_info *mci, u32 bw)
|
|
|
if (boot_cpu_data.x86 == 0xf)
|
|
|
min_scrubrate = 0x0;
|
|
|
|
|
|
- /* F15h Erratum #505 */
|
|
|
- if (boot_cpu_data.x86 == 0x15)
|
|
|
+ /* Erratum #505 for F15h Model 0x00 - Model 0x01, Stepping 0 */
|
|
|
+ if (boot_cpu_data.x86 == 0x15 &&
|
|
|
+ boot_cpu_data.x86_model <= 0x01 &&
|
|
|
+ boot_cpu_data.x86_mask < 0x1)
|
|
|
f15h_select_dct(pvt, 0);
|
|
|
|
|
|
return __amd64_set_scrub_rate(pvt->F3, bw, min_scrubrate);
|
|
@@ -218,8 +221,10 @@ static int amd64_get_scrub_rate(struct mem_ctl_info *mci)
|
|
|
u32 scrubval = 0;
|
|
|
int i, retval = -EINVAL;
|
|
|
|
|
|
- /* F15h Erratum #505 */
|
|
|
- if (boot_cpu_data.x86 == 0x15)
|
|
|
+ /* Erratum #505 for F15h Model 0x00 - Model 0x01, Stepping 0 */
|
|
|
+ if (boot_cpu_data.x86 == 0x15 &&
|
|
|
+ boot_cpu_data.x86_model <= 0x01 &&
|
|
|
+ boot_cpu_data.x86_mask < 0x1)
|
|
|
f15h_select_dct(pvt, 0);
|
|
|
|
|
|
amd64_read_pci_cfg(pvt->F3, SCRCTRL, &scrubval);
|
|
@@ -335,7 +340,7 @@ static void get_cs_base_and_mask(struct amd64_pvt *pvt, int csrow, u8 dct,
|
|
|
u64 csbase, csmask, base_bits, mask_bits;
|
|
|
u8 addr_shift;
|
|
|
|
|
|
- if (boot_cpu_data.x86 == 0xf && pvt->ext_model < K8_REV_F) {
|
|
|
+ if (pvt->fam == 0xf && pvt->ext_model < K8_REV_F) {
|
|
|
csbase = pvt->csels[dct].csbases[csrow];
|
|
|
csmask = pvt->csels[dct].csmasks[csrow];
|
|
|
base_bits = GENMASK(21, 31) | GENMASK(9, 15);
|
|
@@ -343,10 +348,11 @@ static void get_cs_base_and_mask(struct amd64_pvt *pvt, int csrow, u8 dct,
|
|
|
addr_shift = 4;
|
|
|
|
|
|
/*
|
|
|
- * F16h needs two addr_shift values: 8 for high and 6 for low
|
|
|
- * (cf. F16h BKDG).
|
|
|
- */
|
|
|
- } else if (boot_cpu_data.x86 == 0x16) {
|
|
|
+ * F16h and F15h, models 30h and later need two addr_shift values:
|
|
|
+ * 8 for high and 6 for low (cf. F16h BKDG).
|
|
|
+ */
|
|
|
+ } else if (pvt->fam == 0x16 ||
|
|
|
+ (pvt->fam == 0x15 && pvt->model >= 0x30)) {
|
|
|
csbase = pvt->csels[dct].csbases[csrow];
|
|
|
csmask = pvt->csels[dct].csmasks[csrow >> 1];
|
|
|
|
|
@@ -736,13 +742,16 @@ static void dump_misc_regs(struct amd64_pvt *pvt)
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
- * see BKDG, F2x[1,0][5C:40], F2[1,0][6C:60]
|
|
|
+ * See BKDG, F2x[1,0][5C:40], F2[1,0][6C:60]
|
|
|
*/
|
|
|
static void prep_chip_selects(struct amd64_pvt *pvt)
|
|
|
{
|
|
|
- if (boot_cpu_data.x86 == 0xf && pvt->ext_model < K8_REV_F) {
|
|
|
+ if (pvt->fam == 0xf && pvt->ext_model < K8_REV_F) {
|
|
|
pvt->csels[0].b_cnt = pvt->csels[1].b_cnt = 8;
|
|
|
pvt->csels[0].m_cnt = pvt->csels[1].m_cnt = 8;
|
|
|
+ } else if (pvt->fam == 0x15 && pvt->model >= 0x30) {
|
|
|
+ pvt->csels[0].b_cnt = pvt->csels[1].b_cnt = 4;
|
|
|
+ pvt->csels[0].m_cnt = pvt->csels[1].m_cnt = 2;
|
|
|
} else {
|
|
|
pvt->csels[0].b_cnt = pvt->csels[1].b_cnt = 8;
|
|
|
pvt->csels[0].m_cnt = pvt->csels[1].m_cnt = 4;
|
|
@@ -916,15 +925,15 @@ static struct pci_dev *pci_get_related_function(unsigned int vendor,
|
|
|
static void read_dram_base_limit_regs(struct amd64_pvt *pvt, unsigned range)
|
|
|
{
|
|
|
struct amd_northbridge *nb;
|
|
|
- struct pci_dev *misc, *f1 = NULL;
|
|
|
- struct cpuinfo_x86 *c = &boot_cpu_data;
|
|
|
+ struct pci_dev *f1 = NULL;
|
|
|
+ unsigned int pci_func;
|
|
|
int off = range << 3;
|
|
|
u32 llim;
|
|
|
|
|
|
amd64_read_pci_cfg(pvt->F1, DRAM_BASE_LO + off, &pvt->ranges[range].base.lo);
|
|
|
amd64_read_pci_cfg(pvt->F1, DRAM_LIMIT_LO + off, &pvt->ranges[range].lim.lo);
|
|
|
|
|
|
- if (c->x86 == 0xf)
|
|
|
+ if (pvt->fam == 0xf)
|
|
|
return;
|
|
|
|
|
|
if (!dram_rw(pvt, range))
|
|
@@ -934,15 +943,17 @@ static void read_dram_base_limit_regs(struct amd64_pvt *pvt, unsigned range)
|
|
|
amd64_read_pci_cfg(pvt->F1, DRAM_LIMIT_HI + off, &pvt->ranges[range].lim.hi);
|
|
|
|
|
|
/* F15h: factor in CC6 save area by reading dst node's limit reg */
|
|
|
- if (c->x86 != 0x15)
|
|
|
+ if (pvt->fam != 0x15)
|
|
|
return;
|
|
|
|
|
|
nb = node_to_amd_nb(dram_dst_node(pvt, range));
|
|
|
if (WARN_ON(!nb))
|
|
|
return;
|
|
|
|
|
|
- misc = nb->misc;
|
|
|
- f1 = pci_get_related_function(misc->vendor, PCI_DEVICE_ID_AMD_15H_NB_F1, misc);
|
|
|
+ pci_func = (pvt->model == 0x30) ? PCI_DEVICE_ID_AMD_15H_M30H_NB_F1
|
|
|
+ : PCI_DEVICE_ID_AMD_15H_NB_F1;
|
|
|
+
|
|
|
+ f1 = pci_get_related_function(nb->misc->vendor, pci_func, nb->misc);
|
|
|
if (WARN_ON(!f1))
|
|
|
return;
|
|
|
|
|
@@ -1173,7 +1184,7 @@ static int f15_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct,
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
- * F16h has only limited cs_modes
|
|
|
+ * F16h and F15h model 30h have only limited cs_modes.
|
|
|
*/
|
|
|
static int f16_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct,
|
|
|
unsigned cs_mode)
|
|
@@ -1217,6 +1228,29 @@ static void read_dram_ctl_register(struct amd64_pvt *pvt)
|
|
|
amd64_read_dct_pci_cfg(pvt, DCT_SEL_HI, &pvt->dct_sel_hi);
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * Determine channel (DCT) based on the interleaving mode (see F15h M30h BKDG,
|
|
|
+ * 2.10.12 Memory Interleaving Modes).
|
|
|
+ */
|
|
|
+static u8 f15_m30h_determine_channel(struct amd64_pvt *pvt, u64 sys_addr,
|
|
|
+ u8 intlv_en, int num_dcts_intlv,
|
|
|
+ u32 dct_sel)
|
|
|
+{
|
|
|
+ u8 channel = 0;
|
|
|
+ u8 select;
|
|
|
+
|
|
|
+ if (!(intlv_en))
|
|
|
+ return (u8)(dct_sel);
|
|
|
+
|
|
|
+ if (num_dcts_intlv == 2) {
|
|
|
+ select = (sys_addr >> 8) & 0x3;
|
|
|
+ channel = select ? 0x3 : 0;
|
|
|
+ } else if (num_dcts_intlv == 4)
|
|
|
+ channel = (sys_addr >> 8) & 0x7;
|
|
|
+
|
|
|
+ return channel;
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* Determine channel (DCT) based on the interleaving mode: F10h BKDG, 2.8.9 Memory
|
|
|
* Interleaving Modes.
|
|
@@ -1366,6 +1400,10 @@ static int f1x_lookup_addr_in_dct(u64 in_addr, u8 nid, u8 dct)
|
|
|
(in_addr & cs_mask), (cs_base & cs_mask));
|
|
|
|
|
|
if ((in_addr & cs_mask) == (cs_base & cs_mask)) {
|
|
|
+ if (pvt->fam == 0x15 && pvt->model >= 0x30) {
|
|
|
+ cs_found = csrow;
|
|
|
+ break;
|
|
|
+ }
|
|
|
cs_found = f10_process_possible_spare(pvt, dct, csrow);
|
|
|
|
|
|
edac_dbg(1, " MATCH csrow=%d\n", cs_found);
|
|
@@ -1492,20 +1530,142 @@ static int f1x_match_to_this_node(struct amd64_pvt *pvt, unsigned range,
|
|
|
return cs_found;
|
|
|
}
|
|
|
|
|
|
-static int f1x_translate_sysaddr_to_cs(struct amd64_pvt *pvt, u64 sys_addr,
|
|
|
- int *chan_sel)
|
|
|
+static int f15_m30h_match_to_this_node(struct amd64_pvt *pvt, unsigned range,
|
|
|
+ u64 sys_addr, int *chan_sel)
|
|
|
+{
|
|
|
+ int cs_found = -EINVAL;
|
|
|
+ int num_dcts_intlv = 0;
|
|
|
+ u64 chan_addr, chan_offset;
|
|
|
+ u64 dct_base, dct_limit;
|
|
|
+ u32 dct_cont_base_reg, dct_cont_limit_reg, tmp;
|
|
|
+ u8 channel, alias_channel, leg_mmio_hole, dct_sel, dct_offset_en;
|
|
|
+
|
|
|
+ u64 dhar_offset = f10_dhar_offset(pvt);
|
|
|
+ u8 intlv_addr = dct_sel_interleave_addr(pvt);
|
|
|
+ u8 node_id = dram_dst_node(pvt, range);
|
|
|
+ u8 intlv_en = dram_intlv_en(pvt, range);
|
|
|
+
|
|
|
+ amd64_read_pci_cfg(pvt->F1, DRAM_CONT_BASE, &dct_cont_base_reg);
|
|
|
+ amd64_read_pci_cfg(pvt->F1, DRAM_CONT_LIMIT, &dct_cont_limit_reg);
|
|
|
+
|
|
|
+ dct_offset_en = (u8) ((dct_cont_base_reg >> 3) & BIT(0));
|
|
|
+ dct_sel = (u8) ((dct_cont_base_reg >> 4) & 0x7);
|
|
|
+
|
|
|
+ edac_dbg(1, "(range %d) SystemAddr= 0x%llx Limit=0x%llx\n",
|
|
|
+ range, sys_addr, get_dram_limit(pvt, range));
|
|
|
+
|
|
|
+ if (!(get_dram_base(pvt, range) <= sys_addr) &&
|
|
|
+ !(get_dram_limit(pvt, range) >= sys_addr))
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ if (dhar_valid(pvt) &&
|
|
|
+ dhar_base(pvt) <= sys_addr &&
|
|
|
+ sys_addr < BIT_64(32)) {
|
|
|
+ amd64_warn("Huh? Address is in the MMIO hole: 0x%016llx\n",
|
|
|
+ sys_addr);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Verify sys_addr is within DCT Range. */
|
|
|
+ dct_base = (dct_sel_baseaddr(pvt) << 27);
|
|
|
+ dct_limit = (((dct_cont_limit_reg >> 11) & 0x1FFF) << 27) | 0x7FFFFFF;
|
|
|
+
|
|
|
+ if (!(dct_cont_base_reg & BIT(0)) &&
|
|
|
+ !(dct_base <= sys_addr && dct_limit >= sys_addr))
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ /* Verify number of dct's that participate in channel interleaving. */
|
|
|
+ num_dcts_intlv = (int) hweight8(intlv_en);
|
|
|
+
|
|
|
+ if (!(num_dcts_intlv % 2 == 0) || (num_dcts_intlv > 4))
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ channel = f15_m30h_determine_channel(pvt, sys_addr, intlv_en,
|
|
|
+ num_dcts_intlv, dct_sel);
|
|
|
+
|
|
|
+ /* Verify we stay within the MAX number of channels allowed */
|
|
|
+ if (channel > 4 || channel < 0)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ leg_mmio_hole = (u8) (dct_cont_base_reg >> 1 & BIT(0));
|
|
|
+
|
|
|
+ /* Get normalized DCT addr */
|
|
|
+ if (leg_mmio_hole && (sys_addr >= BIT_64(32)))
|
|
|
+ chan_offset = dhar_offset;
|
|
|
+ else
|
|
|
+ chan_offset = dct_base;
|
|
|
+
|
|
|
+ chan_addr = sys_addr - chan_offset;
|
|
|
+
|
|
|
+ /* remove channel interleave */
|
|
|
+ if (num_dcts_intlv == 2) {
|
|
|
+ if (intlv_addr == 0x4)
|
|
|
+ chan_addr = ((chan_addr >> 9) << 8) |
|
|
|
+ (chan_addr & 0xff);
|
|
|
+ else if (intlv_addr == 0x5)
|
|
|
+ chan_addr = ((chan_addr >> 10) << 9) |
|
|
|
+ (chan_addr & 0x1ff);
|
|
|
+ else
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ } else if (num_dcts_intlv == 4) {
|
|
|
+ if (intlv_addr == 0x4)
|
|
|
+ chan_addr = ((chan_addr >> 10) << 8) |
|
|
|
+ (chan_addr & 0xff);
|
|
|
+ else if (intlv_addr == 0x5)
|
|
|
+ chan_addr = ((chan_addr >> 11) << 9) |
|
|
|
+ (chan_addr & 0x1ff);
|
|
|
+ else
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (dct_offset_en) {
|
|
|
+ amd64_read_pci_cfg(pvt->F1,
|
|
|
+ DRAM_CONT_HIGH_OFF + (int) channel * 4,
|
|
|
+ &tmp);
|
|
|
+ chan_addr += ((tmp >> 11) & 0xfff) << 27;
|
|
|
+ }
|
|
|
+
|
|
|
+ f15h_select_dct(pvt, channel);
|
|
|
+
|
|
|
+ edac_dbg(1, " Normalized DCT addr: 0x%llx\n", chan_addr);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Find Chip select:
|
|
|
+ * if channel = 3, then alias it to 1. This is because, in F15 M30h,
|
|
|
+ * there is support for 4 DCT's, but only 2 are currently functional.
|
|
|
+ * They are DCT0 and DCT3. But we have read all registers of DCT3 into
|
|
|
+ * pvt->csels[1]. So we need to use '1' here to get correct info.
|
|
|
+ * Refer F15 M30h BKDG Section 2.10 and 2.10.3 for clarifications.
|
|
|
+ */
|
|
|
+ alias_channel = (channel == 3) ? 1 : channel;
|
|
|
+
|
|
|
+ cs_found = f1x_lookup_addr_in_dct(chan_addr, node_id, alias_channel);
|
|
|
+
|
|
|
+ if (cs_found >= 0)
|
|
|
+ *chan_sel = alias_channel;
|
|
|
+
|
|
|
+ return cs_found;
|
|
|
+}
|
|
|
+
|
|
|
+static int f1x_translate_sysaddr_to_cs(struct amd64_pvt *pvt,
|
|
|
+ u64 sys_addr,
|
|
|
+ int *chan_sel)
|
|
|
{
|
|
|
int cs_found = -EINVAL;
|
|
|
unsigned range;
|
|
|
|
|
|
for (range = 0; range < DRAM_RANGES; range++) {
|
|
|
-
|
|
|
if (!dram_rw(pvt, range))
|
|
|
continue;
|
|
|
|
|
|
- if ((get_dram_base(pvt, range) <= sys_addr) &&
|
|
|
- (get_dram_limit(pvt, range) >= sys_addr)) {
|
|
|
+ if (pvt->fam == 0x15 && pvt->model >= 0x30)
|
|
|
+ cs_found = f15_m30h_match_to_this_node(pvt, range,
|
|
|
+ sys_addr,
|
|
|
+ chan_sel);
|
|
|
|
|
|
+ else if ((get_dram_base(pvt, range) <= sys_addr) &&
|
|
|
+ (get_dram_limit(pvt, range) >= sys_addr)) {
|
|
|
cs_found = f1x_match_to_this_node(pvt, range,
|
|
|
sys_addr, chan_sel);
|
|
|
if (cs_found >= 0)
|
|
@@ -1624,6 +1784,17 @@ static struct amd64_family_type amd64_family_types[] = {
|
|
|
.read_dct_pci_cfg = f15_read_dct_pci_cfg,
|
|
|
}
|
|
|
},
|
|
|
+ [F15_M30H_CPUS] = {
|
|
|
+ .ctl_name = "F15h_M30h",
|
|
|
+ .f1_id = PCI_DEVICE_ID_AMD_15H_M30H_NB_F1,
|
|
|
+ .f3_id = PCI_DEVICE_ID_AMD_15H_M30H_NB_F3,
|
|
|
+ .ops = {
|
|
|
+ .early_channel_count = f1x_early_channel_count,
|
|
|
+ .map_sysaddr_to_csrow = f1x_map_sysaddr_to_csrow,
|
|
|
+ .dbam_to_cs = f16_dbam_to_chip_select,
|
|
|
+ .read_dct_pci_cfg = f15_read_dct_pci_cfg,
|
|
|
+ }
|
|
|
+ },
|
|
|
[F16_CPUS] = {
|
|
|
.ctl_name = "F16h",
|
|
|
.f1_id = PCI_DEVICE_ID_AMD_16H_NB_F1,
|
|
@@ -2387,10 +2558,13 @@ static void setup_mci_misc_attrs(struct mem_ctl_info *mci,
|
|
|
*/
|
|
|
static struct amd64_family_type *amd64_per_family_init(struct amd64_pvt *pvt)
|
|
|
{
|
|
|
- u8 fam = boot_cpu_data.x86;
|
|
|
struct amd64_family_type *fam_type = NULL;
|
|
|
|
|
|
- switch (fam) {
|
|
|
+ pvt->ext_model = boot_cpu_data.x86_model >> 4;
|
|
|
+ pvt->model = boot_cpu_data.x86_model;
|
|
|
+ pvt->fam = boot_cpu_data.x86;
|
|
|
+
|
|
|
+ switch (pvt->fam) {
|
|
|
case 0xf:
|
|
|
fam_type = &amd64_family_types[K8_CPUS];
|
|
|
pvt->ops = &amd64_family_types[K8_CPUS].ops;
|
|
@@ -2402,6 +2576,12 @@ static struct amd64_family_type *amd64_per_family_init(struct amd64_pvt *pvt)
|
|
|
break;
|
|
|
|
|
|
case 0x15:
|
|
|
+ if (pvt->model == 0x30) {
|
|
|
+ fam_type = &amd64_family_types[F15_M30H_CPUS];
|
|
|
+ pvt->ops = &amd64_family_types[F15_M30H_CPUS].ops;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
fam_type = &amd64_family_types[F15_CPUS];
|
|
|
pvt->ops = &amd64_family_types[F15_CPUS].ops;
|
|
|
break;
|
|
@@ -2416,10 +2596,8 @@ static struct amd64_family_type *amd64_per_family_init(struct amd64_pvt *pvt)
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
- pvt->ext_model = boot_cpu_data.x86_model >> 4;
|
|
|
-
|
|
|
amd64_info("%s %sdetected (node %d).\n", fam_type->ctl_name,
|
|
|
- (fam == 0xf ?
|
|
|
+ (pvt->fam == 0xf ?
|
|
|
(pvt->ext_model >= K8_REV_F ? "revF or later "
|
|
|
: "revE or earlier ")
|
|
|
: ""), pvt->mc_node_id);
|
|
@@ -2636,6 +2814,14 @@ static DEFINE_PCI_DEVICE_TABLE(amd64_pci_table) = {
|
|
|
.class = 0,
|
|
|
.class_mask = 0,
|
|
|
},
|
|
|
+ {
|
|
|
+ .vendor = PCI_VENDOR_ID_AMD,
|
|
|
+ .device = PCI_DEVICE_ID_AMD_15H_M30H_NB_F2,
|
|
|
+ .subvendor = PCI_ANY_ID,
|
|
|
+ .subdevice = PCI_ANY_ID,
|
|
|
+ .class = 0,
|
|
|
+ .class_mask = 0,
|
|
|
+ },
|
|
|
{
|
|
|
.vendor = PCI_VENDOR_ID_AMD,
|
|
|
.device = PCI_DEVICE_ID_AMD_16H_NB_F2,
|