#include "zen_umc.h"
#ifndef _KERNEL
#include <strings.h>
#endif
#define ZEN_UMC_TOM2_START 0x100000000ULL
#define ZEN_UMC_TOM2_RSVD_BEGIN 0xfd00000000ULL
#define ZEN_UMC_TOM2_RSVD_END 0x10000000000ULL
#define ZEN_UMC_COD_NBITS 3
#define ZEN_UMC_NPS_MOD_NBITS 3
typedef enum {
ZEN_UMC_NP2_K_HASH_8 = 0,
ZEN_UMC_NP2_K_HASH_9,
ZEN_UMC_NP2_K_HASH_12,
ZEN_UMC_NP2_K_HASH_13
} zen_umc_np2_k_hash_t;
typedef struct {
df_chan_ileave_t zukr_type;
uint32_t zukr_mod;
boolean_t zukr_sock;
uint32_t zukr_high;
uint32_t zukr_mod_shift;
uint32_t zukr_mod_fill[5];
uint32_t zukr_chan_mod_shift;
zen_umc_np2_k_hash_t zukr_chan_fill[2];
uint32_t zukr_div_addr;
uint32_t zukr_div_naddr;
uint32_t zukr_norm_addr;
uint32_t zukr_norm_naddr;
} zen_umc_np2_k_rule_t;
const zen_umc_np2_k_rule_t zen_umc_np2_k_rules[] = { {
.zukr_type = DF_CHAN_ILEAVE_NPS4_3CH_1K,
.zukr_mod = 3,
.zukr_high = 12,
.zukr_mod_shift = 2,
.zukr_mod_fill = { 8, 9 },
.zukr_chan_mod_shift = 0,
.zukr_div_addr = 8,
.zukr_div_naddr = 2,
.zukr_norm_addr = 10,
.zukr_norm_naddr = 2
}, {
.zukr_type = DF_CHAN_ILEAVE_NPS4_3CH_2K,
.zukr_mod = 3,
.zukr_high = 12,
.zukr_mod_shift = 2,
.zukr_mod_fill = { 0, 8 },
.zukr_chan_mod_shift = 0,
.zukr_div_addr = 8,
.zukr_div_naddr = 1,
.zukr_norm_addr = 9,
.zukr_norm_naddr = 3
}, {
.zukr_type = DF_CHAN_ILEAVE_NPS2_6CH_1K,
.zukr_mod = 3,
.zukr_high = 12,
.zukr_mod_shift = 2,
.zukr_mod_fill = { 0, 9 },
.zukr_chan_mod_shift = 1,
.zukr_chan_fill = { ZEN_UMC_NP2_K_HASH_8 },
.zukr_div_addr = 9,
.zukr_div_naddr = 1,
.zukr_norm_addr = 10,
.zukr_norm_naddr = 2
}, {
.zukr_type = DF_CHAN_ILEAVE_NPS2_6CH_2K,
.zukr_mod = 3,
.zukr_high = 12,
.zukr_mod_shift = 2,
.zukr_mod_fill = { 0, 0 },
.zukr_chan_mod_shift = 1,
.zukr_chan_fill = { ZEN_UMC_NP2_K_HASH_8 },
.zukr_div_naddr = 0,
.zukr_norm_addr = 9,
.zukr_norm_naddr = 3
}, {
.zukr_type = DF_CHAN_ILEAVE_NPS1_12CH_1K,
.zukr_mod = 3,
.zukr_high = 12,
.zukr_mod_shift = 2,
.zukr_mod_fill = { 0, 0 },
.zukr_chan_mod_shift = 2,
.zukr_chan_fill = { ZEN_UMC_NP2_K_HASH_8, ZEN_UMC_NP2_K_HASH_9 },
.zukr_div_naddr = 0,
.zukr_norm_addr = 10,
.zukr_norm_naddr = 2
}, {
.zukr_type = DF_CHAN_ILEAVE_NPS1_12CH_1K,
.zukr_mod = 3,
.zukr_high = 12,
.zukr_mod_shift = 2,
.zukr_mod_fill = { 0, 0 },
.zukr_chan_mod_shift = 2,
.zukr_chan_fill = { ZEN_UMC_NP2_K_HASH_8, ZEN_UMC_NP2_K_HASH_9 },
.zukr_div_naddr = 0,
.zukr_norm_addr = 10,
.zukr_norm_naddr = 2
}, {
.zukr_type = DF_CHAN_ILEAVE_NPS1_12CH_2K,
.zukr_mod = 3,
.zukr_high = 13,
.zukr_mod_shift = 3,
.zukr_mod_fill = { 0, 0, 0 },
.zukr_chan_mod_shift = 2,
.zukr_chan_fill = { ZEN_UMC_NP2_K_HASH_8, ZEN_UMC_NP2_K_HASH_12 },
.zukr_div_naddr = 0,
.zukr_norm_addr = 9,
.zukr_norm_naddr = 3
}, {
.zukr_type = DF_CHAN_ILEAVE_NPS0_24CH_1K,
.zukr_mod = 3,
.zukr_sock = B_TRUE,
.zukr_high = 13,
.zukr_mod_shift = 3,
.zukr_mod_fill = { 0, 0, 0 },
.zukr_chan_mod_shift = 2,
.zukr_chan_fill = { ZEN_UMC_NP2_K_HASH_9, ZEN_UMC_NP2_K_HASH_12 },
.zukr_div_naddr = 0,
.zukr_norm_addr = 10,
.zukr_norm_naddr = 2
}, {
.zukr_type = DF_CHAN_ILEAVE_NPS0_24CH_2K,
.zukr_mod = 3,
.zukr_sock = B_TRUE,
.zukr_high = 14,
.zukr_mod_shift = 4,
.zukr_mod_fill = { 0, 0, 0, 0 },
.zukr_chan_mod_shift = 2,
.zukr_chan_fill = { ZEN_UMC_NP2_K_HASH_12, ZEN_UMC_NP2_K_HASH_13 },
.zukr_div_naddr = 0,
.zukr_norm_addr = 9,
.zukr_norm_naddr = 3
}, {
.zukr_type = DF_CHAN_ILEAVE_NPS2_5CH_1K,
.zukr_mod = 5,
.zukr_high = 12,
.zukr_mod_shift = 2,
.zukr_mod_fill = { 8, 9 },
.zukr_chan_mod_shift = 0,
.zukr_div_addr = 8,
.zukr_div_naddr = 2,
.zukr_norm_addr = 10,
.zukr_norm_naddr = 2
}, {
.zukr_type = DF_CHAN_ILEAVE_NPS2_5CH_2K,
.zukr_mod = 5,
.zukr_high = 12,
.zukr_mod_shift = 2,
.zukr_mod_fill = { 0, 8 },
.zukr_chan_mod_shift = 0,
.zukr_div_addr = 8,
.zukr_div_naddr = 1,
.zukr_norm_addr = 9,
.zukr_norm_naddr = 3
}, {
.zukr_type = DF_CHAN_ILEAVE_NPS1_10CH_1K,
.zukr_mod = 5,
.zukr_high = 12,
.zukr_mod_shift = 2,
.zukr_mod_fill = { 0, 9 },
.zukr_chan_mod_shift = 1,
.zukr_chan_fill = { ZEN_UMC_NP2_K_HASH_8 },
.zukr_div_addr = 9,
.zukr_div_naddr = 1,
.zukr_norm_addr = 10,
.zukr_norm_naddr = 2
}, {
.zukr_type = DF_CHAN_ILEAVE_NPS1_10CH_2K,
.zukr_mod = 5,
.zukr_high = 12,
.zukr_mod_shift = 2,
.zukr_mod_fill = { 0, 0 },
.zukr_chan_mod_shift = 1,
.zukr_chan_fill = { ZEN_UMC_NP2_K_HASH_8 },
.zukr_div_naddr = 0,
.zukr_norm_addr = 9,
.zukr_norm_naddr = 3
} };
static boolean_t
zen_umc_decode_is_dram(const zen_umc_t *umc, zen_umc_decoder_t *dec)
{
if (dec->dec_pa < umc->umc_tom) {
return (B_TRUE);
}
if (dec->dec_pa >= umc->umc_tom2) {
dec->dec_fail = ZEN_UMC_DECODE_F_OUTSIDE_DRAM;
return (B_FALSE);
}
if (dec->dec_pa >= ZEN_UMC_TOM2_RSVD_BEGIN &&
dec->dec_pa < ZEN_UMC_TOM2_RSVD_END) {
dec->dec_fail = ZEN_UMC_DECODE_F_OUTSIDE_DRAM;
return (B_FALSE);
}
if (dec->dec_pa >= ZEN_UMC_TOM2_START &&
dec->dec_pa < umc->umc_tom2) {
return (B_TRUE);
}
dec->dec_fail = ZEN_UMC_DECODE_F_OUTSIDE_DRAM;
return (B_FALSE);
}
static boolean_t
zen_umc_decode_find_df_rule(const zen_umc_t *umc, zen_umc_decoder_t *dec)
{
const zen_umc_df_t *df = &umc->umc_dfs[0];
for (uint_t i = 0; i < df->zud_dram_nrules; i++) {
const df_dram_rule_t *rule = &df->zud_rules[i];
if ((rule->ddr_flags & DF_DRAM_F_VALID) == 0)
continue;
if (dec->dec_pa >= rule->ddr_base &&
dec->dec_pa < rule->ddr_limit) {
dec->dec_df_ruleno = i;
dec->dec_df_rule = rule;
dec->dec_df_rulesrc = df;
return (B_TRUE);
}
}
dec->dec_fail = ZEN_UMC_DECODE_F_NO_DF_RULE;
return (B_FALSE);
}
static boolean_t
zen_umc_adjust_dram_addr(const zen_umc_t *umc, zen_umc_decoder_t *dec,
uint64_t *addrp, zen_umc_decode_failure_t errno)
{
const uint64_t init_addr = *addrp;
const df_dram_rule_t *rule = dec->dec_df_rule;
const zen_umc_df_t *df = dec->dec_df_rulesrc;
uint64_t mod_addr = init_addr;
ASSERT3U(init_addr, >=, rule->ddr_base);
ASSERT3U(init_addr, <, rule->ddr_limit);
mod_addr -= rule->ddr_base;
if ((rule->ddr_flags & DF_DRAM_F_HOLE) != 0 &&
(df->zud_flags & ZEN_UMC_DF_F_HOLE_VALID) != 0 &&
init_addr >= ZEN_UMC_TOM2_START) {
uint64_t hole_size;
hole_size = ZEN_UMC_TOM2_START -
umc->umc_dfs[0].zud_hole_base;
if (mod_addr < hole_size) {
dec->dec_fail = errno;
dec->dec_fail_data = dec->dec_df_ruleno;
return (B_FALSE);
}
mod_addr -= hole_size;
}
*addrp = mod_addr;
return (B_TRUE);
}
static boolean_t
zen_umc_determine_ileave_addr(const zen_umc_t *umc, zen_umc_decoder_t *dec)
{
const df_dram_rule_t *rule = dec->dec_df_rule;
if ((umc->umc_df_rev <= DF_REV_3 &&
rule->ddr_chan_ileave != DF_CHAN_ILEAVE_6CH) ||
umc->umc_df_rev >= DF_REV_4D2) {
dec->dec_ilv_pa = dec->dec_pa;
return (B_TRUE);
}
dec->dec_ilv_pa = dec->dec_pa;
if (!zen_umc_adjust_dram_addr(umc, dec, &dec->dec_ilv_pa,
ZEN_UMC_DECODE_F_ILEAVE_UNDERFLOW)) {
return (B_FALSE);
}
return (B_TRUE);
}
static boolean_t
zen_umc_decode_ileave_nohash(const zen_umc_t *umc, zen_umc_decoder_t *dec)
{
uint32_t nchan_bit, ndie_bit, nsock_bit, addr_bit;
const df_dram_rule_t *rule = dec->dec_df_rule;
nsock_bit = rule->ddr_sock_ileave_bits;
ndie_bit = rule->ddr_die_ileave_bits;
switch (rule->ddr_chan_ileave) {
case DF_CHAN_ILEAVE_1CH:
nchan_bit = 0;
break;
case DF_CHAN_ILEAVE_2CH:
nchan_bit = 1;
break;
case DF_CHAN_ILEAVE_4CH:
nchan_bit = 2;
break;
case DF_CHAN_ILEAVE_8CH:
nchan_bit = 3;
break;
case DF_CHAN_ILEAVE_16CH:
nchan_bit = 4;
break;
case DF_CHAN_ILEAVE_32CH:
nchan_bit = 5;
break;
default:
dec->dec_fail = ZEN_UMC_DECODE_F_CHAN_ILEAVE_NOTSUP;
dec->dec_fail_data = rule->ddr_chan_ileave;
return (B_FALSE);
}
dec->dec_ilv_sock = dec->dec_ilv_die = dec->dec_ilv_chan = 0;
addr_bit = rule->ddr_addr_start;
if (nchan_bit > 0) {
dec->dec_ilv_chan = bitx64(dec->dec_ilv_pa,
addr_bit + nchan_bit - 1, addr_bit);
addr_bit += nchan_bit;
}
if (ndie_bit > 0) {
dec->dec_ilv_die = bitx64(dec->dec_ilv_pa,
addr_bit + ndie_bit - 1, addr_bit);
addr_bit += ndie_bit;
}
if (nsock_bit > 0) {
dec->dec_ilv_sock = bitx64(dec->dec_ilv_pa,
addr_bit + nsock_bit - 1, addr_bit);
addr_bit += nsock_bit;
}
return (B_TRUE);
}
static boolean_t
zen_umc_decode_ileave_cod(const zen_umc_t *umc, zen_umc_decoder_t *dec)
{
uint32_t nchan_bit;
const df_dram_rule_t *rule = dec->dec_df_rule;
const uint32_t addr_bits[3] = { rule->ddr_addr_start, 12, 13 };
if (rule->ddr_sock_ileave_bits != 0 || rule->ddr_die_ileave_bits != 0) {
dec->dec_fail = ZEN_UMC_DECODE_F_COD_BAD_ILEAVE;
dec->dec_fail_data = dec->dec_df_ruleno;
return (B_FALSE);
}
switch (rule->ddr_chan_ileave) {
case DF_CHAN_ILEAVE_COD4_2CH:
nchan_bit = 1;
break;
case DF_CHAN_ILEAVE_COD2_4CH:
nchan_bit = 2;
break;
case DF_CHAN_ILEAVE_COD1_8CH:
nchan_bit = 3;
break;
default:
dec->dec_fail = ZEN_UMC_DECODE_F_CHAN_ILEAVE_NOTSUP;
dec->dec_fail_data = rule->ddr_chan_ileave;
return (B_FALSE);
}
dec->dec_ilv_sock = dec->dec_ilv_die = dec->dec_ilv_chan = 0;
for (uint_t i = 0; i < nchan_bit; i++) {
uint8_t hash = 0;
hash = bitx64(dec->dec_ilv_pa, addr_bits[i], addr_bits[i]);
if ((rule->ddr_flags & DF_DRAM_F_HASH_16_18) != 0) {
uint8_t val = bitx64(dec->dec_ilv_pa, 16 + i, 16 + i);
hash ^= val;
}
if ((rule->ddr_flags & DF_DRAM_F_HASH_21_23) != 0) {
uint8_t val = bitx64(dec->dec_ilv_pa, 21 + i, 21 + i);
hash ^= val;
}
if ((rule->ddr_flags & DF_DRAM_F_HASH_30_32) != 0) {
uint8_t val = bitx64(dec->dec_ilv_pa, 30 + i, 30 + i);
hash ^= val;
}
dec->dec_ilv_chan |= hash << i;
}
return (B_TRUE);
}
static void
zen_umc_decode_ileave_nps_common(zen_umc_decoder_t *dec,
const uint32_t *addr_bits, const uint32_t *adj, uint32_t nsock_bits,
uint32_t nchan_bits, boolean_t df4p0)
{
const df_dram_rule_t *rule = dec->dec_df_rule;
for (uint32_t i = 0; i < nchan_bits + nsock_bits; i++) {
uint8_t hash = 0;
hash = bitx64(dec->dec_ilv_pa, addr_bits[i], addr_bits[i]);
if ((rule->ddr_flags & DF_DRAM_F_HASH_16_18) != 0) {
uint8_t val = bitx64(dec->dec_ilv_pa, 16 + adj[i],
16 + adj[i]);
hash ^= val;
}
if ((rule->ddr_flags & DF_DRAM_F_HASH_21_23) != 0) {
uint8_t val = bitx64(dec->dec_ilv_pa, 21 + adj[i],
21 + adj[i]);
hash ^= val;
}
if ((rule->ddr_flags & DF_DRAM_F_HASH_30_32) != 0) {
uint8_t val = bitx64(dec->dec_ilv_pa, 30 + adj[i], 30 +
adj[i]);
hash ^= val;
}
if ((rule->ddr_flags & DF_DRAM_F_HASH_40_42) != 0) {
uint8_t val = bitx64(dec->dec_ilv_pa, 40 + adj[i],
40 + adj[i]);
hash ^= val;
}
if (i == 0 && nsock_bits == 0 && df4p0) {
uint8_t val = bitx64(dec->dec_ilv_pa, 14, 14);
hash ^= val;
}
if (nsock_bits > 0) {
if (i == 0) {
dec->dec_ilv_sock = hash;
} else {
dec->dec_ilv_chan |= hash << (i - 1);
}
} else {
dec->dec_ilv_chan |= hash << i;
}
}
}
static boolean_t
zen_umc_decode_ileave_nps(const zen_umc_t *umc, zen_umc_decoder_t *dec)
{
uint32_t nchan_bit, nsock_bit;
const df_dram_rule_t *rule = dec->dec_df_rule;
const uint32_t addr_bits[4] = { rule->ddr_addr_start, 12, 13, 14 };
const uint32_t adj[4] = { 0, 1, 2, 3 };
if (rule->ddr_die_ileave_bits != 0) {
dec->dec_fail = ZEN_UMC_DECODE_F_NPS_BAD_ILEAVE;
dec->dec_fail_data = dec->dec_df_ruleno;
return (B_FALSE);
}
nsock_bit = rule->ddr_sock_ileave_bits;
switch (rule->ddr_chan_ileave) {
case DF_CHAN_ILEAVE_NPS4_2CH:
nchan_bit = 1;
break;
case DF_CHAN_ILEAVE_NPS2_4CH:
nchan_bit = 2;
break;
case DF_CHAN_ILEAVE_NPS1_8CH:
nchan_bit = 3;
break;
default:
dec->dec_fail = ZEN_UMC_DECODE_F_CHAN_ILEAVE_NOTSUP;
dec->dec_fail_data = rule->ddr_chan_ileave;
return (B_FALSE);
}
ASSERT3U(nchan_bit + nsock_bit, <=, 4);
dec->dec_ilv_sock = dec->dec_ilv_die = dec->dec_ilv_chan = 0;
zen_umc_decode_ileave_nps_common(dec, addr_bits, adj, nsock_bit,
nchan_bit, B_TRUE);
return (B_TRUE);
}
static boolean_t
zen_umc_decode_ileave_nps_k(const zen_umc_t *umc, zen_umc_decoder_t *dec)
{
uint32_t nchan_bit, nsock_bit;
const df_dram_rule_t *rule = dec->dec_df_rule;
const uint32_t addr_bits_1k[5] = { rule->ddr_addr_start, 9, 12, 13,
14 };
const uint32_t addr_bits_2k[4] = { rule->ddr_addr_start, 12, 13, 14 };
const uint32_t adj_1k[5] = { 0, 1, 2, 3, 4 };
const uint32_t adj_2k[4] = { 0, 2, 3, 4 };
const uint32_t *addr_bits;
const uint32_t *adj;
if (rule->ddr_die_ileave_bits != 0 || rule->ddr_addr_start != 8) {
dec->dec_fail = ZEN_UMC_DECODE_F_NPS_BAD_ILEAVE;
dec->dec_fail_data = dec->dec_df_ruleno;
return (B_FALSE);
}
nsock_bit = rule->ddr_sock_ileave_bits;
switch (rule->ddr_chan_ileave) {
case DF_CHAN_ILEAVE_NPS4_2CH_1K:
case DF_CHAN_ILEAVE_NPS4_2CH_2K:
nchan_bit = 1;
break;
case DF_CHAN_ILEAVE_NPS2_4CH_1K:
case DF_CHAN_ILEAVE_NPS2_4CH_2K:
nchan_bit = 2;
break;
case DF_CHAN_ILEAVE_NPS1_8CH_1K:
case DF_CHAN_ILEAVE_NPS1_8CH_2K:
nchan_bit = 3;
break;
case DF_CHAN_ILEAVE_NPS1_16CH_1K:
case DF_CHAN_ILEAVE_NPS1_16CH_2K:
nchan_bit = 4;
break;
default:
dec->dec_fail = ZEN_UMC_DECODE_F_CHAN_ILEAVE_NOTSUP;
dec->dec_fail_data = rule->ddr_chan_ileave;
return (B_FALSE);
}
switch (rule->ddr_chan_ileave) {
case DF_CHAN_ILEAVE_NPS4_2CH_1K:
case DF_CHAN_ILEAVE_NPS2_4CH_1K:
case DF_CHAN_ILEAVE_NPS1_8CH_1K:
case DF_CHAN_ILEAVE_NPS1_16CH_1K:
ASSERT3U(nchan_bit + nsock_bit, <=, 5);
addr_bits = addr_bits_1k;
adj = adj_1k;
break;
case DF_CHAN_ILEAVE_NPS4_2CH_2K:
case DF_CHAN_ILEAVE_NPS2_4CH_2K:
case DF_CHAN_ILEAVE_NPS1_8CH_2K:
case DF_CHAN_ILEAVE_NPS1_16CH_2K:
ASSERT3U(nchan_bit + nsock_bit, <=, 4);
addr_bits = addr_bits_2k;
adj = adj_2k;
break;
default:
dec->dec_fail = ZEN_UMC_DECODE_F_CHAN_ILEAVE_NOTSUP;
dec->dec_fail_data = rule->ddr_chan_ileave;
return (B_FALSE);
}
dec->dec_ilv_sock = dec->dec_ilv_die = dec->dec_ilv_chan = 0;
zen_umc_decode_ileave_nps_common(dec, addr_bits, adj, nsock_bit,
nchan_bit, B_FALSE);
return (B_TRUE);
}
static void
zen_umc_decode_hash_zen3_6ch(const df_dram_rule_t *rule, uint64_t pa,
uint8_t hashes[3])
{
uint32_t addr_bit = rule->ddr_addr_start;
const uint32_t bits_2M[3] = { 23, 21, 22 };
const uint32_t bits_1G[3] = { 32, 30, 31 };
hashes[0] = hashes[1] = hashes[2] = 0;
for (uint_t i = 0; i < ZEN_UMC_COD_NBITS; i++) {
hashes[i] = bitx64(pa, addr_bit + i, addr_bit + i);
if (i == 0) {
uint8_t val = bitx64(pa, addr_bit + 3, addr_bit + 3);
hashes[i] ^= val;
}
if ((rule->ddr_flags & DF_DRAM_F_HASH_21_23) != 0) {
uint8_t val = bitx64(pa, bits_2M[i], bits_2M[i]);
hashes[i] ^= val;
}
if ((rule->ddr_flags & DF_DRAM_F_HASH_30_32) != 0) {
uint8_t val = bitx64(pa, bits_1G[i], bits_1G[i]);
hashes[i] ^= val;
}
}
}
static boolean_t
zen_umc_decode_ileave_zen3_6ch(const zen_umc_t *umc, zen_umc_decoder_t *dec)
{
uint8_t hashes[3] = { 0 };
const df_dram_rule_t *rule = dec->dec_df_rule;
uint32_t addr_bit = rule->ddr_addr_start;
if (rule->ddr_sock_ileave_bits != 0 || rule->ddr_die_ileave_bits != 0) {
dec->dec_fail = ZEN_UMC_DECODE_F_COD_BAD_ILEAVE;
dec->dec_fail_data = dec->dec_df_ruleno;
return (B_FALSE);
}
zen_umc_decode_hash_zen3_6ch(rule, dec->dec_ilv_pa, hashes);
dec->dec_ilv_sock = dec->dec_ilv_die = dec->dec_ilv_chan = 0;
dec->dec_ilv_chan = hashes[0];
if (hashes[1] == 1 && hashes[2] == 1) {
uint64_t mod_addr = dec->dec_ilv_pa >> (addr_bit + 3);
dec->dec_ilv_chan |= (mod_addr % 3) << 1;
} else {
dec->dec_ilv_chan |= hashes[1] << 1;
dec->dec_ilv_chan |= hashes[2] << 2;
}
return (B_TRUE);
}
static void
zen_umc_decode_hash_nps_mod(const df_dram_rule_t *rule, uint64_t pa,
uint8_t hashes[3])
{
const uint32_t addr_bits[3] = { rule->ddr_addr_start, 12, 13 };
for (uint_t i = 0; i < ZEN_UMC_NPS_MOD_NBITS; i++) {
hashes[i] = bitx64(pa, addr_bits[i], addr_bits[i]);
if (i == 0) {
uint8_t val = bitx64(pa, 14, 14);
hashes[i] ^= val;
}
if ((rule->ddr_flags & DF_DRAM_F_HASH_16_18) != 0) {
uint8_t val = bitx64(pa, 16 + i, 16 + i);
hashes[i] ^= val;
}
if ((rule->ddr_flags & DF_DRAM_F_HASH_21_23) != 0) {
uint8_t val = bitx64(pa, 21 + i, 21 + i);
hashes[i] ^= val;
}
if ((rule->ddr_flags & DF_DRAM_F_HASH_30_32) != 0) {
uint8_t val = bitx64(pa, 30 + i, 30 + i);
hashes[i] ^= val;
}
}
}
static void
zen_umc_decode_hash_nps_k_mod(const df_dram_rule_t *rule, uint64_t pa,
uint8_t hashes[4])
{
const uint32_t addr_bits[4] = { rule->ddr_addr_start, 9, 12, 13 };
for (size_t i = 0; i < ARRAY_SIZE(addr_bits); i++) {
hashes[i] = bitx64(pa, addr_bits[i], addr_bits[i]);
if (i == 0) {
uint8_t val = bitx64(pa, 14, 14);
hashes[i] ^= val;
}
if ((rule->ddr_flags & DF_DRAM_F_HASH_16_18) != 0) {
uint8_t val = bitx64(pa, 16 + i, 16 + i);
hashes[i] ^= val;
}
if ((rule->ddr_flags & DF_DRAM_F_HASH_21_23) != 0) {
uint8_t val = bitx64(pa, 21 + i, 21 + i);
hashes[i] ^= val;
}
if ((rule->ddr_flags & DF_DRAM_F_HASH_30_32) != 0) {
uint8_t val = bitx64(pa, 30 + i, 30 + i);
hashes[i] ^= val;
}
if ((rule->ddr_flags & DF_DRAM_F_HASH_40_42) != 0) {
uint8_t val = bitx64(pa, 40 + i, 40 + i);
hashes[i] ^= val;
}
}
}
static boolean_t
zen_umc_decode_ileave_nps_mod(const zen_umc_t *umc, zen_umc_decoder_t *dec)
{
uint8_t hashes[3] = { 0 };
uint32_t nsock_bit, chan_mod;
const df_dram_rule_t *rule = dec->dec_df_rule;
if (rule->ddr_die_ileave_bits != 0) {
dec->dec_fail = ZEN_UMC_DECODE_F_NPS_BAD_ILEAVE;
dec->dec_fail_data = dec->dec_df_ruleno;
return (B_FALSE);
}
nsock_bit = rule->ddr_sock_ileave_bits;
switch (rule->ddr_chan_ileave) {
case DF_CHAN_ILEAVE_NPS4_3CH:
case DF_CHAN_ILEAVE_NPS2_6CH:
case DF_CHAN_ILEAVE_NPS1_12CH:
chan_mod = 3;
break;
case DF_CHAN_ILEAVE_NPS2_5CH:
case DF_CHAN_ILEAVE_NPS1_10CH:
chan_mod = 5;
break;
default:
dec->dec_fail = ZEN_UMC_DECODE_F_CHAN_ILEAVE_NOTSUP;
dec->dec_fail_data = rule->ddr_chan_ileave;
return (B_FALSE);
}
dec->dec_ilv_sock = dec->dec_ilv_die = dec->dec_ilv_chan = 0;
zen_umc_decode_hash_nps_mod(rule, dec->dec_ilv_pa, hashes);
if (nsock_bit > 0) {
ASSERT3U(nsock_bit, ==, 1);
dec->dec_ilv_sock = hashes[0];
}
dec->dec_ilv_chan = bitx64(dec->dec_ilv_pa, 63, 14) % chan_mod;
if (hashes[0] == 1) {
dec->dec_ilv_chan = (dec->dec_ilv_chan + 1) % chan_mod;
}
switch (rule->ddr_chan_ileave) {
case DF_CHAN_ILEAVE_NPS4_3CH:
case DF_CHAN_ILEAVE_NPS2_5CH:
break;
case DF_CHAN_ILEAVE_NPS2_6CH:
case DF_CHAN_ILEAVE_NPS1_10CH:
dec->dec_ilv_chan += hashes[2] * chan_mod;
break;
case DF_CHAN_ILEAVE_NPS1_12CH:
dec->dec_ilv_chan += ((hashes[2] << 1) | hashes[1]) * chan_mod;
break;
default:
dec->dec_fail = ZEN_UMC_DECODE_F_CHAN_ILEAVE_NOTSUP;
dec->dec_fail_data = rule->ddr_chan_ileave;
return (B_FALSE);
}
return (B_TRUE);
}
static boolean_t
zen_umc_decode_ileave_nps_k_mod(const zen_umc_t *umc, zen_umc_decoder_t *dec)
{
uint8_t hashes[4] = { 0 };
uint32_t chan, mod_val;
uint64_t mod_addr;
const df_dram_rule_t *rule = dec->dec_df_rule;
const zen_umc_np2_k_rule_t *np2 = NULL;
for (size_t i = 0; i < ARRAY_SIZE(zen_umc_np2_k_rules); i++) {
if (rule->ddr_chan_ileave == zen_umc_np2_k_rules[i].zukr_type) {
np2 = &zen_umc_np2_k_rules[i];
break;
}
}
if (np2 == NULL) {
dec->dec_fail = ZEN_UMC_DECODE_F_CHAN_ILEAVE_NOTSUP;
dec->dec_fail_data = rule->ddr_chan_ileave;
return (B_FALSE);
}
if (rule->ddr_die_ileave_bits != 0 || rule->ddr_addr_start != 8) {
dec->dec_fail = ZEN_UMC_DECODE_F_NPS_BAD_ILEAVE;
dec->dec_fail_data = dec->dec_df_ruleno;
return (B_FALSE);
}
if (np2->zukr_sock != (rule->ddr_sock_ileave_bits == 1)) {
dec->dec_fail = ZEN_UMC_DECODE_F_NPS_BAD_ILEAVE;
dec->dec_fail_data = dec->dec_df_ruleno;
return (B_FALSE);
}
dec->dec_ilv_sock = dec->dec_ilv_die = dec->dec_ilv_chan = 0;
zen_umc_decode_hash_nps_k_mod(rule, dec->dec_ilv_pa, hashes);
if (rule->ddr_sock_ileave_bits > 0) {
ASSERT3U(rule->ddr_sock_ileave_bits, ==, 1);
dec->dec_ilv_sock = hashes[0];
}
mod_addr = bitx64(dec->dec_ilv_pa, 63, np2->zukr_high);
mod_addr = mod_addr << np2->zukr_mod_shift;
for (uint32_t i = 0; i < np2->zukr_mod_shift; i++) {
uint32_t bit = np2->zukr_mod_fill[i];
if (bit != 0) {
uint64_t val = bitx64(dec->dec_ilv_pa, bit, bit);
mod_addr = bitset64(mod_addr, i, i, val);
}
}
mod_val = (uint32_t)(mod_addr % np2->zukr_mod);
chan = mod_val << np2->zukr_chan_mod_shift;
for (uint32_t i = 0; i < np2->zukr_chan_mod_shift; i++) {
VERIFY3U(np2->zukr_chan_fill[i], <, ARRAY_SIZE(hashes));
uint32_t bit = np2->zukr_chan_fill[i];
uint32_t val = hashes[np2->zukr_chan_fill[i]];
chan = bitset32(chan, bit, bit, val);
}
dec->dec_ilv_chan = chan;
return (B_TRUE);
}
static boolean_t
zen_umc_decode_sysaddr_to_csid(const zen_umc_t *umc, zen_umc_decoder_t *dec)
{
uint32_t sock, die, chan, remap_ruleset;
const df_dram_rule_t *rule = dec->dec_df_rule;
const zen_umc_cs_remap_t *remap;
if (!zen_umc_determine_ileave_addr(umc, dec)) {
return (B_FALSE);
}
switch (rule->ddr_chan_ileave) {
case DF_CHAN_ILEAVE_1CH:
case DF_CHAN_ILEAVE_2CH:
case DF_CHAN_ILEAVE_4CH:
case DF_CHAN_ILEAVE_8CH:
case DF_CHAN_ILEAVE_16CH:
case DF_CHAN_ILEAVE_32CH:
if (!zen_umc_decode_ileave_nohash(umc, dec)) {
return (B_FALSE);
}
break;
case DF_CHAN_ILEAVE_COD4_2CH:
case DF_CHAN_ILEAVE_COD2_4CH:
case DF_CHAN_ILEAVE_COD1_8CH:
if (!zen_umc_decode_ileave_cod(umc, dec)) {
return (B_FALSE);
}
break;
case DF_CHAN_ILEAVE_NPS4_2CH:
case DF_CHAN_ILEAVE_NPS2_4CH:
case DF_CHAN_ILEAVE_NPS1_8CH:
if (!zen_umc_decode_ileave_nps(umc, dec)) {
return (B_FALSE);
}
break;
case DF_CHAN_ILEAVE_6CH:
if (!zen_umc_decode_ileave_zen3_6ch(umc, dec)) {
return (B_FALSE);
}
break;
case DF_CHAN_ILEAVE_NPS4_3CH:
case DF_CHAN_ILEAVE_NPS2_6CH:
case DF_CHAN_ILEAVE_NPS1_12CH:
case DF_CHAN_ILEAVE_NPS2_5CH:
case DF_CHAN_ILEAVE_NPS1_10CH:
if (!zen_umc_decode_ileave_nps_mod(umc, dec)) {
return (B_FALSE);
}
break;
case DF_CHAN_ILEAVE_NPS4_2CH_1K:
case DF_CHAN_ILEAVE_NPS2_4CH_1K:
case DF_CHAN_ILEAVE_NPS1_8CH_1K:
case DF_CHAN_ILEAVE_NPS1_16CH_1K:
case DF_CHAN_ILEAVE_NPS4_2CH_2K:
case DF_CHAN_ILEAVE_NPS2_4CH_2K:
case DF_CHAN_ILEAVE_NPS1_8CH_2K:
case DF_CHAN_ILEAVE_NPS1_16CH_2K:
if (!zen_umc_decode_ileave_nps_k(umc, dec)) {
return (B_FALSE);
}
break;
case DF_CHAN_ILEAVE_NPS4_3CH_1K:
case DF_CHAN_ILEAVE_NPS2_6CH_1K:
case DF_CHAN_ILEAVE_NPS1_12CH_1K:
case DF_CHAN_ILEAVE_NPS0_24CH_1K:
case DF_CHAN_ILEAVE_NPS2_5CH_1K:
case DF_CHAN_ILEAVE_NPS1_10CH_1K:
case DF_CHAN_ILEAVE_NPS4_3CH_2K:
case DF_CHAN_ILEAVE_NPS2_6CH_2K:
case DF_CHAN_ILEAVE_NPS1_12CH_2K:
case DF_CHAN_ILEAVE_NPS0_24CH_2K:
case DF_CHAN_ILEAVE_NPS2_5CH_2K:
case DF_CHAN_ILEAVE_NPS1_10CH_2K:
if (!zen_umc_decode_ileave_nps_k_mod(umc, dec)) {
return (B_FALSE);
}
break;
case DF_CHAN_ILEAVE_MI3H_8CH:
case DF_CHAN_ILEAVE_MI3H_16CH:
case DF_CHAN_ILEAVE_MI3H_32CH:
default:
dec->dec_fail = ZEN_UMC_DECODE_F_CHAN_ILEAVE_NOTSUP;
dec->dec_fail_data = rule->ddr_chan_ileave;
return (B_FALSE);
}
zen_fabric_id_compose(&umc->umc_decomp, dec->dec_ilv_sock,
dec->dec_ilv_die, dec->dec_ilv_chan, &dec->dec_ilv_fabid);
dec->dec_log_fabid = dec->dec_ilv_fabid + rule->ddr_dest_fabid;
zen_fabric_id_decompose(&umc->umc_decomp, dec->dec_log_fabid, &sock,
&die, &chan);
if ((rule->ddr_flags & DF_DRAM_F_REMAP_EN) == 0) {
dec->dec_targ_fabid = dec->dec_log_fabid;
return (B_TRUE);
}
if ((rule->ddr_flags & DF_DRAM_F_REMAP_SOCK) != 0) {
remap_ruleset = sock;
} else {
remap_ruleset = rule->ddr_remap_ent;
}
if (remap_ruleset >= dec->dec_df_rulesrc->zud_cs_nremap) {
dec->dec_fail = ZEN_UMC_DECODE_F_BAD_REMAP_SET;
dec->dec_fail_data = remap_ruleset;
return (B_FALSE);
}
remap = &dec->dec_df_rulesrc->zud_remap[remap_ruleset];
if (chan >= remap->csr_nremaps) {
dec->dec_fail = ZEN_UMC_DECODE_F_BAD_REMAP_ENTRY;
dec->dec_fail_data = chan;
return (B_FALSE);
}
dec->dec_remap_comp = remap->csr_remaps[chan];
if ((dec->dec_remap_comp & ~umc->umc_decomp.dfd_comp_mask) != 0) {
dec->dec_fail = ZEN_UMC_DECODE_F_REMAP_HAS_BAD_COMP;
dec->dec_fail_data = dec->dec_remap_comp;
return (B_FALSE);
}
zen_fabric_id_compose(&umc->umc_decomp, sock, die, dec->dec_remap_comp,
&dec->dec_targ_fabid);
return (B_TRUE);
}
static boolean_t
zen_umc_decode_find_umc_rule(const zen_umc_t *umc, zen_umc_decoder_t *dec)
{
for (uint_t dfno = 0; dfno < umc->umc_ndfs; dfno++) {
const zen_umc_df_t *df = &umc->umc_dfs[dfno];
for (uint_t umcno = 0; umcno < df->zud_nchan; umcno++) {
const zen_umc_chan_t *chan = &df->zud_chan[umcno];
if (chan->chan_fabid != dec->dec_targ_fabid) {
continue;
}
dec->dec_umc_chan = chan;
for (uint32_t ruleno = 0; ruleno < chan->chan_nrules;
ruleno++) {
const df_dram_rule_t *rule =
&chan->chan_rules[ruleno];
if ((rule->ddr_flags & DF_DRAM_F_VALID) == 0) {
continue;
}
if (dec->dec_pa >= rule->ddr_base &&
dec->dec_pa < rule->ddr_limit) {
dec->dec_umc_ruleno = ruleno;
return (B_TRUE);
}
}
dec->dec_fail = ZEN_UMC_DECODE_F_UMC_DOESNT_HAVE_PA;
return (B_FALSE);
}
}
dec->dec_fail = ZEN_UMC_DECODE_F_CANNOT_MAP_FABID;
return (B_FALSE);
}
static boolean_t
zen_umc_decode_normalize_nohash(const zen_umc_t *umc, zen_umc_decoder_t *dec)
{
uint_t nbits = 0;
const df_dram_rule_t *rule = dec->dec_df_rule;
nbits += rule->ddr_sock_ileave_bits;
nbits += rule->ddr_die_ileave_bits;
switch (rule->ddr_chan_ileave) {
case DF_CHAN_ILEAVE_1CH:
break;
case DF_CHAN_ILEAVE_2CH:
nbits += 1;
break;
case DF_CHAN_ILEAVE_4CH:
nbits += 2;
break;
case DF_CHAN_ILEAVE_8CH:
nbits += 3;
break;
case DF_CHAN_ILEAVE_16CH:
nbits += 4;
break;
case DF_CHAN_ILEAVE_32CH:
nbits += 5;
break;
default:
dec->dec_fail = ZEN_UMC_DECODE_F_CHAN_ILEAVE_NOTSUP;
dec->dec_fail_data = rule->ddr_chan_ileave;
return (B_FALSE);
}
if (nbits > 0) {
dec->dec_norm_addr = bitdel64(dec->dec_norm_addr,
rule->ddr_addr_start + nbits - 1, rule->ddr_addr_start);
}
return (B_TRUE);
}
static boolean_t
zen_umc_decode_normalize_hash(const zen_umc_t *umc, zen_umc_decoder_t *dec)
{
uint_t nbits = 0, nstart = 0;
const df_dram_rule_t *rule = dec->dec_df_rule;
switch (rule->ddr_chan_ileave) {
case DF_CHAN_ILEAVE_NPS4_2CH_1K:
case DF_CHAN_ILEAVE_NPS2_4CH_1K:
case DF_CHAN_ILEAVE_NPS1_8CH_1K:
case DF_CHAN_ILEAVE_NPS1_16CH_1K:
nstart = 2;
break;
default:
nstart = 1;
break;
}
nbits += rule->ddr_sock_ileave_bits;
switch (rule->ddr_chan_ileave) {
case DF_CHAN_ILEAVE_COD4_2CH:
case DF_CHAN_ILEAVE_NPS4_2CH:
case DF_CHAN_ILEAVE_NPS4_2CH_1K:
case DF_CHAN_ILEAVE_NPS4_2CH_2K:
nbits += 1;
break;
case DF_CHAN_ILEAVE_COD2_4CH:
case DF_CHAN_ILEAVE_NPS2_4CH:
case DF_CHAN_ILEAVE_NPS2_4CH_1K:
case DF_CHAN_ILEAVE_NPS2_4CH_2K:
nbits += 2;
break;
case DF_CHAN_ILEAVE_COD1_8CH:
case DF_CHAN_ILEAVE_NPS1_8CH:
case DF_CHAN_ILEAVE_NPS1_8CH_1K:
case DF_CHAN_ILEAVE_NPS1_8CH_2K:
nbits += 3;
break;
case DF_CHAN_ILEAVE_NPS1_16CH_1K:
case DF_CHAN_ILEAVE_NPS1_16CH_2K:
nbits += 4;
break;
default:
dec->dec_fail = ZEN_UMC_DECODE_F_CHAN_ILEAVE_NOTSUP;
dec->dec_fail_data = rule->ddr_chan_ileave;
}
if (nstart > nbits) {
nstart = nbits;
}
if (nbits > nstart) {
uint_t start = 12;
uint_t end = start + (nbits - nstart - 1);
dec->dec_norm_addr = bitdel64(dec->dec_norm_addr, end, start);
}
dec->dec_norm_addr = bitdel64(dec->dec_norm_addr,
rule->ddr_addr_start + nstart - 1, rule->ddr_addr_start);
return (B_TRUE);
}
static boolean_t
zen_umc_decode_normalize_zen3_6ch(const zen_umc_t *umc, zen_umc_decoder_t *dec)
{
uint8_t hashes[3] = { 0 };
uint_t start, end;
const df_dram_rule_t *rule = dec->dec_df_rule;
zen_umc_decode_hash_zen3_6ch(rule, dec->dec_norm_addr, hashes);
start = rule->ddr_addr_start;
end = rule->ddr_addr_start + ZEN_UMC_COD_NBITS - 1;
dec->dec_norm_addr = bitdel64(dec->dec_norm_addr, end, start);
if (hashes[1] == 1 && hashes[2] == 1) {
uint_t start = 14 - ZEN_UMC_COD_NBITS +
dec->dec_umc_chan->chan_np2_space0;
dec->dec_norm_addr = bitset64(dec->dec_norm_addr, start + 1,
start, 0x3);
}
return (B_TRUE);
}
static boolean_t
zen_umc_decode_normalize_nps_mod(const zen_umc_t *umc, zen_umc_decoder_t *dec)
{
uint64_t low, high, mid;
uint_t nbits, chan_mod, sock_bits, nmid_bits;
uint_t mid_start, mid_end;
uint8_t hashes[3] = { 0 };
const df_dram_rule_t *rule = dec->dec_df_rule;
sock_bits = rule->ddr_sock_ileave_bits;
switch (rule->ddr_chan_ileave) {
case DF_CHAN_ILEAVE_NPS4_3CH:
chan_mod = 3;
nbits = 1;
break;
case DF_CHAN_ILEAVE_NPS2_5CH:
chan_mod = 5;
nbits = 1;
break;
case DF_CHAN_ILEAVE_NPS2_6CH:
chan_mod = 3;
nbits = 2;
break;
case DF_CHAN_ILEAVE_NPS1_10CH:
chan_mod = 5;
nbits = 2;
break;
case DF_CHAN_ILEAVE_NPS1_12CH:
chan_mod = 3;
nbits = 3;
break;
default:
dec->dec_fail = ZEN_UMC_DECODE_F_CHAN_ILEAVE_NOTSUP;
dec->dec_fail_data = rule->ddr_chan_ileave;
return (B_FALSE);
}
low = bitx64(dec->dec_norm_addr, rule->ddr_addr_start - 1, 0);
high = bitx64(dec->dec_norm_addr, 63, 14) / chan_mod;
zen_umc_decode_hash_nps_mod(rule, dec->dec_norm_addr, hashes);
if (sock_bits == 0) {
high = (high << 1) | hashes[0];
}
nmid_bits = 6 - nbits;
mid_start = rule->ddr_addr_start + 1;
mid_end = mid_start + nmid_bits - 1;
mid = bitx64(dec->dec_norm_addr, mid_end, mid_start);
dec->dec_norm_addr = low | (mid << rule->ddr_addr_start) | (high <<
(rule->ddr_addr_start + nmid_bits));
return (B_TRUE);
}
static boolean_t
zen_umc_decode_normalize_nps_k_mod(const zen_umc_t *umc, zen_umc_decoder_t *dec)
{
uint64_t high, mid, low;
uint_t mid_end;
const df_dram_rule_t *rule = dec->dec_df_rule;
const zen_umc_np2_k_rule_t *np2 = NULL;
for (size_t i = 0; i < ARRAY_SIZE(zen_umc_np2_k_rules); i++) {
if (rule->ddr_chan_ileave == zen_umc_np2_k_rules[i].zukr_type) {
np2 = &zen_umc_np2_k_rules[i];
break;
}
}
if (np2 == NULL) {
dec->dec_fail = ZEN_UMC_DECODE_F_CHAN_ILEAVE_NOTSUP;
dec->dec_fail_data = rule->ddr_chan_ileave;
return (B_FALSE);
}
low = bitx64(dec->dec_norm_addr, rule->ddr_addr_start - 1, 0);
mid_end = np2->zukr_norm_addr + np2->zukr_norm_naddr - 1;
VERIFY3U(mid_end, >=, rule->ddr_addr_start);
mid = bitx64(dec->dec_norm_addr, mid_end, np2->zukr_norm_addr);
high = bitx64(dec->dec_norm_addr, 63, np2->zukr_high);
if (np2->zukr_div_naddr > 0) {
uint_t ins_end = np2->zukr_div_addr + np2->zukr_div_naddr - 1;
uint64_t insert = bitx64(dec->dec_norm_addr, ins_end,
np2->zukr_div_addr);
high = high << np2->zukr_div_naddr;
high = bitset64(high, np2->zukr_div_naddr - 1, 0, insert);
}
high = high / np2->zukr_mod;
dec->dec_norm_addr = low | (mid << rule->ddr_addr_start) | (high <<
(rule->ddr_addr_start + np2->zukr_norm_naddr));
return (B_TRUE);
}
static boolean_t
zen_umc_decode_sysaddr_to_norm(const zen_umc_t *umc, zen_umc_decoder_t *dec)
{
const zen_umc_chan_t *chan = dec->dec_umc_chan;
const df_dram_rule_t *rule = dec->dec_df_rule;
dec->dec_norm_addr = dec->dec_pa;
if (!zen_umc_adjust_dram_addr(umc, dec, &dec->dec_norm_addr,
ZEN_UMC_DECODE_F_CALC_NORM_UNDERFLOW)) {
return (B_FALSE);
}
switch (rule->ddr_chan_ileave) {
case DF_CHAN_ILEAVE_1CH:
case DF_CHAN_ILEAVE_2CH:
case DF_CHAN_ILEAVE_4CH:
case DF_CHAN_ILEAVE_8CH:
case DF_CHAN_ILEAVE_16CH:
case DF_CHAN_ILEAVE_32CH:
if (!zen_umc_decode_normalize_nohash(umc, dec)) {
return (B_FALSE);
}
break;
case DF_CHAN_ILEAVE_COD4_2CH:
case DF_CHAN_ILEAVE_COD2_4CH:
case DF_CHAN_ILEAVE_COD1_8CH:
case DF_CHAN_ILEAVE_NPS4_2CH:
case DF_CHAN_ILEAVE_NPS2_4CH:
case DF_CHAN_ILEAVE_NPS1_8CH:
case DF_CHAN_ILEAVE_NPS4_2CH_1K:
case DF_CHAN_ILEAVE_NPS2_4CH_1K:
case DF_CHAN_ILEAVE_NPS1_8CH_1K:
case DF_CHAN_ILEAVE_NPS1_16CH_1K:
case DF_CHAN_ILEAVE_NPS4_2CH_2K:
case DF_CHAN_ILEAVE_NPS2_4CH_2K:
case DF_CHAN_ILEAVE_NPS1_8CH_2K:
case DF_CHAN_ILEAVE_NPS1_16CH_2K:
if (!zen_umc_decode_normalize_hash(umc, dec)) {
return (B_FALSE);
}
break;
case DF_CHAN_ILEAVE_6CH:
if (!zen_umc_decode_normalize_zen3_6ch(umc, dec)) {
return (B_FALSE);
}
break;
case DF_CHAN_ILEAVE_NPS4_3CH:
case DF_CHAN_ILEAVE_NPS2_6CH:
case DF_CHAN_ILEAVE_NPS1_12CH:
case DF_CHAN_ILEAVE_NPS2_5CH:
case DF_CHAN_ILEAVE_NPS1_10CH:
if (!zen_umc_decode_normalize_nps_mod(umc, dec)) {
return (B_FALSE);
}
break;
case DF_CHAN_ILEAVE_NPS4_3CH_1K:
case DF_CHAN_ILEAVE_NPS2_6CH_1K:
case DF_CHAN_ILEAVE_NPS1_12CH_1K:
case DF_CHAN_ILEAVE_NPS0_24CH_1K:
case DF_CHAN_ILEAVE_NPS2_5CH_1K:
case DF_CHAN_ILEAVE_NPS1_10CH_1K:
case DF_CHAN_ILEAVE_NPS4_3CH_2K:
case DF_CHAN_ILEAVE_NPS2_6CH_2K:
case DF_CHAN_ILEAVE_NPS1_12CH_2K:
case DF_CHAN_ILEAVE_NPS0_24CH_2K:
case DF_CHAN_ILEAVE_NPS2_5CH_2K:
case DF_CHAN_ILEAVE_NPS1_10CH_2K:
if (!zen_umc_decode_normalize_nps_k_mod(umc, dec)) {
return (B_FALSE);
}
break;
case DF_CHAN_ILEAVE_MI3H_8CH:
case DF_CHAN_ILEAVE_MI3H_16CH:
case DF_CHAN_ILEAVE_MI3H_32CH:
default:
dec->dec_fail = ZEN_UMC_DECODE_F_CHAN_ILEAVE_NOTSUP;
dec->dec_fail_data = rule->ddr_chan_ileave;
return (B_FALSE);
}
if (dec->dec_umc_ruleno > 0) {
uint32_t offno = dec->dec_umc_ruleno - 1;
const chan_offset_t *offset = &chan->chan_offsets[offno];
if (offset->cho_valid) {
dec->dec_norm_addr += offset->cho_offset;
}
}
return (B_TRUE);
}
static boolean_t
zen_umc_decoder_cs_matches(const umc_cs_t *cs, const uint64_t norm,
boolean_t *matched_sec)
{
if ((cs->ucs_flags & UMC_CS_F_DECODE_EN) == 0) {
return (B_FALSE);
}
if (cs->ucs_base.udb_valid != 0) {
uint64_t imask = ~cs->ucs_base_mask;
if ((norm & imask) == (cs->ucs_base.udb_base & imask)) {
*matched_sec = B_FALSE;
return (B_TRUE);
}
}
if (cs->ucs_sec.udb_valid != 0) {
uint64_t imask = ~cs->ucs_sec_mask;
if ((norm & imask) == (cs->ucs_sec.udb_base & imask)) {
*matched_sec = B_TRUE;
return (B_TRUE);
}
}
return (B_FALSE);
}
static boolean_t
zen_umc_decode_find_cs(const zen_umc_t *umc, zen_umc_decoder_t *dec)
{
const zen_umc_chan_t *chan = dec->dec_umc_chan;
for (uint_t dimmno = 0; dimmno < ZEN_UMC_MAX_DIMMS; dimmno++) {
const umc_dimm_t *dimm = &chan->chan_dimms[dimmno];
if ((dimm->ud_flags & UMC_DIMM_F_VALID) == 0)
continue;
for (uint_t csno = 0; csno < ZEN_UMC_MAX_CS_PER_DIMM; csno++) {
const umc_cs_t *cs = &dimm->ud_cs[csno];
boolean_t is_sec = B_FALSE;
if (zen_umc_decoder_cs_matches(cs, dec->dec_norm_addr,
&is_sec)) {
dec->dec_dimm = dimm;
dec->dec_cs = cs;
dec->dec_log_csno = dimmno * ZEN_UMC_MAX_DIMMS +
csno;
dec->dec_cs_sec = is_sec;
return (B_TRUE);
}
}
}
dec->dec_fail = ZEN_UMC_DECODE_F_NO_CS_BASE_MATCH;
return (B_FALSE);
}
static boolean_t
zen_umc_decode_cols(const zen_umc_t *umc, zen_umc_decoder_t *dec)
{
uint32_t cols = 0;
const umc_cs_t *cs = dec->dec_cs;
for (uint_t i = 0; i < cs->ucs_ncol; i++) {
uint32_t index;
index = cs->ucs_col_bits[i];
cols |= bitx64(dec->dec_norm_addr, index, index) << i;
}
dec->dec_dimm_col = cols;
return (B_TRUE);
}
static boolean_t
zen_umc_decode_rows(const zen_umc_t *umc, zen_umc_decoder_t *dec)
{
uint32_t row = 0;
uint8_t inv;
const umc_cs_t *cs = dec->dec_cs;
const uint_t total_bits = cs->ucs_nrow_lo + cs->ucs_nrow_hi;
const uint_t lo_end = cs->ucs_nrow_lo + cs->ucs_row_low_bit - 1;
row = bitx64(dec->dec_norm_addr, lo_end, cs->ucs_row_low_bit);
if (cs->ucs_nrow_hi > 0) {
const uint_t hi_end = cs->ucs_nrow_hi + cs->ucs_row_hi_bit - 1;
const uint32_t hi = bitx64(dec->dec_norm_addr, hi_end,
cs->ucs_row_hi_bit);
row |= hi << cs->ucs_nrow_lo;
}
if (dec->dec_cs_sec) {
inv = cs->ucs_inv_msbs_sec;
} else {
inv = cs->ucs_inv_msbs;
}
inv = inv << (total_bits - 2);
row = row ^ inv;
dec->dec_dimm_row = row;
return (B_TRUE);
}
static uint8_t
zen_umc_running_xor32(const uint32_t in)
{
uint8_t run = 0;
for (uint_t i = 0; i < sizeof (in) * NBBY; i++) {
run ^= bitx32(in, i, i);
}
return (run);
}
static uint8_t
zen_umc_running_xor64(const uint64_t in)
{
uint8_t run = 0;
for (uint_t i = 0; i < sizeof (in) * NBBY; i++) {
run ^= bitx64(in, i, i);
}
return (run);
}
static boolean_t
zen_umc_decode_banks(const zen_umc_t *umc, zen_umc_decoder_t *dec)
{
uint8_t bank = 0;
const umc_cs_t *cs = dec->dec_cs;
const umc_chan_hash_t *hash = &dec->dec_umc_chan->chan_hash;
for (uint_t i = 0; i < cs->ucs_nbanks; i++) {
uint32_t row_hash, col_hash;
uint8_t row_xor, col_xor;
uint_t targ = cs->ucs_bank_bits[i];
uint8_t val = bitx64(dec->dec_norm_addr, targ, targ);
const umc_bank_hash_t *bank_hash = &hash->uch_bank_hashes[i];
if ((hash->uch_flags & UMC_CHAN_HASH_F_BANK) == 0 ||
!hash->uch_bank_hashes[i].ubh_en) {
bank |= val << i;
continue;
}
row_hash = dec->dec_dimm_row & bank_hash->ubh_row_xor;
col_hash = dec->dec_dimm_col & bank_hash->ubh_col_xor;
row_xor = zen_umc_running_xor32(row_hash);
col_xor = zen_umc_running_xor32(col_hash);
bank |= (row_xor ^ col_xor ^ val) << i;
}
dec->dec_dimm_bank_group = bitx8(bank, cs->ucs_nbank_groups - 1, 0);
dec->dec_dimm_bank = bitx8(bank, cs->ucs_nbanks, cs->ucs_nbank_groups);
return (B_TRUE);
}
static boolean_t
zen_umc_decode_subchan(const zen_umc_t *umc, zen_umc_decoder_t *dec)
{
uint8_t subchan;
uint32_t row_hash, col_hash, bank_hash;
uint8_t row_xor, col_xor, bank_xor;
const umc_cs_t *cs = dec->dec_cs;
const umc_chan_hash_t *hash = &dec->dec_umc_chan->chan_hash;
switch (dec->dec_umc_chan->chan_type) {
case UMC_DIMM_T_DDR5:
case UMC_DIMM_T_LPDDR5:
break;
default:
dec->dec_dimm_subchan = 0;
return (B_TRUE);
}
subchan = bitx64(dec->dec_norm_addr, cs->ucs_subchan, cs->ucs_subchan);
if ((hash->uch_flags & UMC_CHAN_HASH_F_PC) == 0 ||
!hash->uch_pc_hash.uph_en) {
dec->dec_dimm_subchan = subchan;
return (B_TRUE);
}
row_hash = dec->dec_dimm_row & hash->uch_pc_hash.uph_row_xor;
col_hash = dec->dec_dimm_col & hash->uch_pc_hash.uph_col_xor;
bank_hash = dec->dec_dimm_bank & hash->uch_pc_hash.uph_bank_xor;
row_xor = zen_umc_running_xor32(row_hash);
col_xor = zen_umc_running_xor32(col_hash);
bank_xor = zen_umc_running_xor32(bank_hash);
dec->dec_dimm_subchan = subchan ^ row_xor ^ col_xor ^ bank_xor;
return (B_TRUE);
}
static boolean_t
zen_umc_decode_rank_mul(const zen_umc_t *umc, zen_umc_decoder_t *dec)
{
uint8_t rm = 0;
const umc_cs_t *cs = dec->dec_cs;
const umc_chan_hash_t *hash = &dec->dec_umc_chan->chan_hash;
for (uint_t i = 0; i < cs->ucs_nrm; i++) {
uint8_t index = cs->ucs_rm_bits[i];
uint8_t bit = bitx64(dec->dec_norm_addr, index, index);
if ((hash->uch_flags & UMC_CHAN_HASH_F_RM) != 0 &&
hash->uch_rm_hashes[i].uah_en) {
uint64_t norm_mask = dec->dec_norm_addr &
hash->uch_rm_hashes[i].uah_addr_xor;
uint8_t norm_hash = zen_umc_running_xor64(norm_mask);
bit = bit ^ norm_hash;
}
rm |= bit << i;
}
dec->dec_dimm_rm = rm;
return (B_TRUE);
}
static boolean_t
zen_umc_decode_chipsel(const zen_umc_t *umc, zen_umc_decoder_t *dec)
{
uint8_t csno = 0;
const umc_cs_t *cs = dec->dec_cs;
const umc_chan_hash_t *hash = &dec->dec_umc_chan->chan_hash;
for (uint_t i = 0; i < ZEN_UMC_MAX_CS_BITS; i++) {
uint8_t bit = bitx8(dec->dec_log_csno, i, i);
if ((hash->uch_flags & UMC_CHAN_HASH_F_CS) != 0 &&
hash->uch_cs_hashes[i].uah_en) {
uint64_t mask = dec->dec_norm_addr &
hash->uch_cs_hashes[i].uah_addr_xor;
uint8_t rxor = zen_umc_running_xor64(mask);
bit = bit ^ rxor;
}
csno |= bit << i;
}
dec->dec_chan_csno = (csno ^ cs->ucs_cs_xor) & 0x3;
dec->dec_dimm_no = dec->dec_chan_csno >> 1;
dec->dec_dimm_csno = dec->dec_chan_csno % 2;
return (B_TRUE);
}
static void
zen_umc_decoder_init(zen_umc_decoder_t *dec)
{
bzero(dec, sizeof (*dec));
dec->dec_pa = dec->dec_ilv_pa = UINT64_MAX;
dec->dec_df_ruleno = UINT32_MAX;
dec->dec_ilv_sock = dec->dec_ilv_die = dec->dec_ilv_chan =
dec->dec_ilv_fabid = dec->dec_log_fabid = dec->dec_remap_comp =
dec->dec_targ_fabid = UINT32_MAX;
dec->dec_umc_ruleno = UINT32_MAX;
dec->dec_norm_addr = UINT64_MAX;
dec->dec_dimm_col = dec->dec_dimm_row = UINT32_MAX;
dec->dec_log_csno = dec->dec_dimm_bank = dec->dec_dimm_bank_group =
dec->dec_dimm_subchan = dec->dec_dimm_rm = dec->dec_chan_csno =
dec->dec_dimm_no = dec->dec_dimm_csno = UINT8_MAX;
}
boolean_t
zen_umc_decode_pa(const zen_umc_t *umc, const uint64_t pa,
zen_umc_decoder_t *dec)
{
zen_umc_decoder_init(dec);
dec->dec_pa = pa;
if (!zen_umc_decode_is_dram(umc, dec)) {
ASSERT3U(dec->dec_fail, !=, ZEN_UMC_DECODE_F_NONE);
return (B_FALSE);
}
if (!zen_umc_decode_find_df_rule(umc, dec)) {
ASSERT3U(dec->dec_fail, !=, ZEN_UMC_DECODE_F_NONE);
return (B_FALSE);
}
if (!zen_umc_decode_sysaddr_to_csid(umc, dec)) {
ASSERT3U(dec->dec_fail, !=, ZEN_UMC_DECODE_F_NONE);
return (B_FALSE);
}
if (!zen_umc_decode_find_umc_rule(umc, dec)) {
ASSERT3U(dec->dec_fail, !=, ZEN_UMC_DECODE_F_NONE);
return (B_FALSE);
}
if (!zen_umc_decode_sysaddr_to_norm(umc, dec)) {
ASSERT3U(dec->dec_fail, !=, ZEN_UMC_DECODE_F_NONE);
return (B_FALSE);
}
if (!zen_umc_decode_find_cs(umc, dec)) {
ASSERT3U(dec->dec_fail, !=, ZEN_UMC_DECODE_F_NONE);
return (B_FALSE);
}
if (!zen_umc_decode_rows(umc, dec)) {
ASSERT3U(dec->dec_fail, !=, ZEN_UMC_DECODE_F_NONE);
return (B_FALSE);
}
if (!zen_umc_decode_cols(umc, dec)) {
ASSERT3U(dec->dec_fail, !=, ZEN_UMC_DECODE_F_NONE);
return (B_FALSE);
}
if (!zen_umc_decode_banks(umc, dec)) {
ASSERT3U(dec->dec_fail, !=, ZEN_UMC_DECODE_F_NONE);
return (B_FALSE);
}
if (!zen_umc_decode_subchan(umc, dec)) {
ASSERT3U(dec->dec_fail, !=, ZEN_UMC_DECODE_F_NONE);
return (B_FALSE);
}
if (!zen_umc_decode_rank_mul(umc, dec)) {
ASSERT3U(dec->dec_fail, !=, ZEN_UMC_DECODE_F_NONE);
return (B_FALSE);
}
if (!zen_umc_decode_chipsel(umc, dec)) {
ASSERT3U(dec->dec_fail, !=, ZEN_UMC_DECODE_F_NONE);
return (B_FALSE);
}
return (B_TRUE);
}