#include <mcamd_api.h>
#include <mcamd_err.h>
#include <mcamd_rowcol_impl.h>
struct mcprops {
mcamd_prop_t num;
mcamd_prop_t rev;
mcamd_prop_t width;
mcamd_prop_t base;
mcamd_prop_t lim;
mcamd_prop_t csbnkmap_reg;
mcamd_prop_t intlven;
mcamd_prop_t intlvsel;
mcamd_prop_t csintlvfctr;
mcamd_prop_t bnkswzl;
mcamd_prop_t sparecs;
mcamd_prop_t badcs;
};
struct csprops {
mcamd_prop_t num;
mcamd_prop_t base;
mcamd_prop_t mask;
mcamd_prop_t testfail;
mcamd_prop_t dimmrank;
};
static int
getmcprops(struct mcamd_hdl *hdl, mcamd_node_t *mc, const char *caller,
struct mcprops *pp)
{
if (!mcamd_get_numprops(hdl,
mc, MCAMD_PROP_NUM, &pp->num,
mc, MCAMD_PROP_REV, &pp->rev,
mc, MCAMD_PROP_ACCESS_WIDTH, &pp->width,
mc, MCAMD_PROP_BASE_ADDR, &pp->base,
mc, MCAMD_PROP_LIM_ADDR, &pp->lim,
mc, MCAMD_PROP_CSBANKMAPREG, &pp->csbnkmap_reg,
mc, MCAMD_PROP_ILEN, &pp->intlven,
mc, MCAMD_PROP_ILSEL, &pp->intlvsel,
mc, MCAMD_PROP_CSINTLVFCTR, &pp->csintlvfctr,
mc, MCAMD_PROP_BANKSWZL, &pp->bnkswzl,
mc, MCAMD_PROP_SPARECS, &pp->sparecs,
mc, MCAMD_PROP_BADCS, &pp->badcs,
NULL)) {
mcamd_dprintf(hdl, MCAMD_DBG_ERR, "%s: failed to read mc "
"props for mc 0x%p\n", caller, mc);
return (mcamd_set_errno(hdl, EMCAMD_TREEINVALID));
}
return (0);
}
static int
getcsprops(struct mcamd_hdl *hdl, mcamd_node_t *cs, const char *caller,
struct csprops *csp)
{
if (!mcamd_get_numprops(hdl,
cs, MCAMD_PROP_NUM, &csp->num,
cs, MCAMD_PROP_BASE_ADDR, &csp->base,
cs, MCAMD_PROP_MASK, &csp->mask,
cs, MCAMD_PROP_TESTFAIL, &csp->testfail,
cs, MCAMD_PROP_DIMMRANK, &csp->dimmrank,
NULL)) {
mcamd_dprintf(hdl, MCAMD_DBG_ERR, "%s: failed to read cs "
"props for cs 0x%p\n", caller, cs);
return (mcamd_set_errno(hdl, EMCAMD_TREEINVALID));
}
return (0);
}
static int
gettbls(struct mcamd_hdl *hdl, uint_t csmode, struct mcprops *mcpp,
const struct rct_bnkaddrmode **bamp, const struct rct_rcbmap **rcbmp,
const struct rct_bnkswzlinfo **swzlp, struct rct_csintlv *csid,
const char *caller)
{
uint_t rev = (uint_t)mcpp->rev;
int width = (int)mcpp->width;
if (bamp && (*bamp = rct_bnkaddrmode(rev, csmode)) == NULL) {
mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "%s: no bank address mode "
"table for MC rev %d csmode %d\n", caller, rev, csmode);
return (mcamd_set_errno(hdl, EMCAMD_NOTSUP));
}
if (rcbmp && (*rcbmp = rct_rcbmap(rev, width, csmode)) == NULL) {
mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "%s: no dram address map "
"table for MC rev %d csmode %d\n", caller,
rev, csmode);
return (mcamd_set_errno(hdl, EMCAMD_NOTSUP));
}
if (swzlp && (*swzlp = rct_bnkswzlinfo(rev, width)) == NULL) {
mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "%s: no bank swizzling "
"table for MC rev %d width %d\n", caller, rev, width);
return (mcamd_set_errno(hdl, EMCAMD_NOTSUP));
}
if (csid) {
if (mcpp->csintlvfctr > 1) {
rct_csintlv_bits(rev, width, csmode,
mcpp->csintlvfctr, csid);
if (csid->csi_factor == 0) {
mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "%s: "
"could not work out cs interleave "
"paramters for MC rev %d, width %d, "
"csmode %d, factor %d\n", caller,
rev, width, csmode,
(int)mcpp->csintlvfctr);
return (mcamd_set_errno(hdl, EMCAMD_NOTSUP));
}
} else {
csid->csi_factor = 0;
}
}
return (0);
}
static uint64_t
iaddr_add(struct mcamd_hdl *hdl, uint64_t in, uint64_t add, const char *what)
{
uint64_t new = in | add;
mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "%s: 0x%llx | 0x%llx --> 0x%llx",
what, in, add, new);
return (add);
}
static uint32_t
iaddr_to_row(struct mcamd_hdl *hdl, const struct rct_bnkaddrmode *bamp,
const struct rct_rcbmap *rcbm, struct rct_csintlv *csid, uint64_t iaddr)
{
uint32_t addr = 0;
int abitno, ibitno;
int nbits = bamp->bam_nrows;
int swapped = 0;
for (abitno = 0; abitno < nbits; abitno++) {
ibitno = rcbm->rcb_rowbit[abitno];
if (MC_RC_CSI_SWAPPED_BIT(csid, ibitno)) {
ibitno = MC_RC_CSI_BITSWAP(csid, ibitno);
swapped++;
}
if (BITVAL(iaddr, ibitno) != 0)
SETBIT(addr, abitno);
}
mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "iaddr_to_row: iaddr 0x%llx --> "
"row 0x%x (%d bits swapped for cs intlv)\n", iaddr, addr, swapped);
return (addr);
}
static uint64_t
row_to_iaddr(struct mcamd_hdl *hdl, const struct rct_bnkaddrmode *bamp,
const struct rct_rcbmap *rcbm, struct rct_csintlv *csid, uint32_t rowaddr)
{
uint64_t iaddr = 0;
int abitno, ibitno;
int nbits = bamp->bam_nrows;
for (abitno = 0; abitno < nbits; abitno++) {
if (BIT(rowaddr, abitno) == 0)
continue;
ibitno = rcbm->rcb_rowbit[abitno];
if (MC_RC_CSI_SWAPPED_BIT(csid, ibitno)) {
ibitno = MC_RC_CSI_BITSWAP(csid, ibitno);
}
SETBIT(iaddr, ibitno);
}
return (iaddr);
}
static uint32_t
iaddr_to_col(struct mcamd_hdl *hdl, const struct rct_bnkaddrmode *bamp,
const struct rct_rcbmap *rcbm, uint64_t iaddr)
{
uint32_t addr = 0;
int abitno, ibitno, bias = 0;
int nbits = bamp->bam_ncols;
if (bamp->bam_ambig)
nbits--;
for (abitno = 0; abitno < nbits; abitno++) {
if (abitno == MC_PC_COLADDRBIT)
bias = 1;
ibitno = rcbm->rcb_colbit[abitno + bias];
if (BITVAL(iaddr, ibitno) != 0)
SETBIT(addr, abitno);
}
mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "iaddr_to_col: iaddr 0x%llx --> "
"col 0x%x\n", iaddr, addr);
return (addr);
}
static uint64_t
col_to_iaddr(struct mcamd_hdl *hdl, const struct rct_bnkaddrmode *bamp,
const struct rct_rcbmap *rcbm, uint32_t coladdr)
{
uint64_t iaddr = 0;
int abitno, ibitno, bias = 0;
int nbits = bamp->bam_ncols;
if (bamp->bam_ambig)
nbits--;
for (abitno = 0; abitno < nbits; abitno++) {
if (BIT(coladdr, abitno) == 0)
continue;
if (abitno == MC_PC_COLADDRBIT)
bias = 1;
ibitno = rcbm->rcb_colbit[abitno + bias];
SETBIT(iaddr, ibitno);
}
return (iaddr);
}
static uint32_t
iaddr_to_bank(struct mcamd_hdl *hdl, const struct rct_rcbmap *rcbm,
const struct rct_bnkswzlinfo *swzlp, uint64_t iaddr)
{
uint32_t addr = 0;
int abitno, ibitno, i;
for (abitno = 0; abitno < rcbm->rcb_nbankbits; abitno++) {
uint32_t val;
ibitno = rcbm->rcb_bankbit[abitno];
val = BITVAL(iaddr, ibitno);
if (swzlp) {
for (i = 0; i < MC_RC_SWZLBITS; i++) {
ibitno = swzlp->bswz_rowbits[abitno][i];
val ^= BITVAL(iaddr, ibitno);
}
}
if (val)
SETBIT(addr, abitno);
}
mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "iaddr_to_bank: iaddr 0x%llx --> "
"bank 0x%x\n", iaddr, addr);
return (addr);
}
static uint64_t
bank_to_iaddr(struct mcamd_hdl *hdl, const struct rct_rcbmap *rcbm,
const struct rct_bnkswzlinfo *swzlp, uint64_t partiaddr, uint32_t bankaddr)
{
uint64_t iaddr = 0;
int abitno, pibitno, i;
for (abitno = 0; abitno < rcbm->rcb_nbankbits; abitno++) {
uint32_t val = BITVAL(bankaddr, abitno);
if (swzlp) {
for (i = 0; i < MC_RC_SWZLBITS; i++) {
pibitno = swzlp->bswz_rowbits[abitno][i];
val ^= BITVAL(partiaddr, pibitno);
}
}
if (val)
SETBIT(iaddr, rcbm->rcb_bankbit[abitno]);
}
return (iaddr);
}
static int
iaddr_to_rcb(struct mcamd_hdl *hdl, uint_t csmode, struct mcprops *mcpp,
uint64_t iaddr, uint32_t *rowp, uint32_t *colp, uint32_t *bankp)
{
const struct rct_bnkaddrmode *bamp;
const struct rct_rcbmap *rcbmp;
const struct rct_bnkswzlinfo *swzlp = NULL;
struct rct_csintlv csi;
if (gettbls(hdl, csmode, mcpp, &bamp, &rcbmp,
mcpp->bnkswzl ? &swzlp : NULL, &csi,
"iaddr_to_rcb") < 0)
return (-1);
*rowp = iaddr_to_row(hdl, bamp, rcbmp, &csi, iaddr);
*colp = iaddr_to_col(hdl, bamp, rcbmp, iaddr);
*bankp = iaddr_to_bank(hdl, rcbmp, swzlp, iaddr);
return (0);
}
static int
iaddr_unnormalize(struct mcamd_hdl *hdl, struct mcprops *mcpp, uint64_t iaddr,
uint64_t *rsltp)
{
uint64_t dramaddr;
int intlvbits;
switch (mcpp->intlven) {
case 0x0:
intlvbits = 0;
break;
case 0x1:
intlvbits = 1;
break;
case 0x3:
intlvbits = 2;
break;
case 0x7:
intlvbits = 3;
break;
default:
mcamd_dprintf(hdl, MCAMD_DBG_ERR, "iaddr_unnormalize: "
"illegal IntlvEn of %d for MC 0x%p\n",
(int)mcpp->intlven, (int)mcpp->num);
return (mcamd_set_errno(hdl, EMCAMD_TREEINVALID));
}
if (intlvbits != 0) {
dramaddr = (BITS(iaddr, 35, 12) << intlvbits) |
(mcpp->intlvsel << 12) | BITS(iaddr, 11, 0);
} else {
dramaddr = iaddr + mcpp->base;
}
*rsltp = dramaddr;
mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "iaddr_unnormalize: iaddr 0x%llx "
"intlven 0x%x intlvsel 0x%x MC base 0x%llx --> 0x%llx\n",
iaddr, (int)mcpp->intlven, (int)mcpp->intlvsel, (int)mcpp->base,
dramaddr);
return (0);
}
int
mc_pa_to_offset(struct mcamd_hdl *hdl, mcamd_node_t *mc, mcamd_node_t *cs,
uint64_t iaddr, uint64_t *offsetp)
{
mcamd_dimm_offset_un_t offset_un;
uint_t csmode;
uint32_t bankaddr, rowaddr, coladdr;
struct mcprops mcp;
struct csprops csp;
*offsetp = MCAMD_RC_INVALID_OFFSET;
if (getmcprops(hdl, mc, "mc_dimm_offset", &mcp) < 0 ||
getcsprops(hdl, cs, "mc_dimm_offset", &csp) < 0)
return (-1);
csmode = MC_CS_MODE(mcp.csbnkmap_reg, csp.num);
if (iaddr_to_rcb(hdl, csmode, &mcp, iaddr, &rowaddr,
&coladdr, &bankaddr) < 0)
return (-1);
offset_un.do_offset = 0;
offset_un.do_valid = 1;
offset_un.do_version = MCAMD_OFFSET_VERSION;
offset_un.do_rank = (uint32_t)csp.dimmrank;
offset_un.do_row = rowaddr;
offset_un.do_bank = bankaddr;
offset_un.do_col = coladdr;
*offsetp = offset_un.do_offset;
return (0);
}
int
mc_offset_to_pa(struct mcamd_hdl *hdl, mcamd_node_t *mc, mcamd_node_t *dimm,
uint64_t offset, uint64_t *pap)
{
mcamd_node_t *cs;
mcamd_dimm_offset_un_t off_un;
uint32_t rank, rowaddr, bankaddr, coladdr;
uint64_t iaddr = 0;
const struct rct_bnkaddrmode *bamp;
const struct rct_rcbmap *rcbmp;
const struct rct_bnkswzlinfo *swzlp = NULL;
struct rct_csintlv csi;
struct mcprops mcp;
struct csprops csp;
uint64_t csmode;
int maskhi_hi, maskhi_lo, masklo_hi, masklo_lo;
off_un.do_offset = offset;
rank = off_un.do_rank;
bankaddr = off_un.do_bank;
rowaddr = off_un.do_row;
coladdr = off_un.do_col;
mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mc_offset_to_pa: offset 0x%llx "
"-> rank %d bank %d row 0x%x col 0x%x\n", offset,
rank, bankaddr, rowaddr, coladdr);
if (getmcprops(hdl, mc, "mc_offset_to_pa", &mcp) < 0)
return (-1);
maskhi_hi = MC_CSMASKHI_HIBIT(mcp.rev);
maskhi_lo = MC_CSMASKHI_LOBIT(mcp.rev);
masklo_hi = MC_CSMASKLO_HIBIT(mcp.rev);
masklo_lo = MC_CSMASKLO_LOBIT(mcp.rev);
for (cs = mcamd_cs_next(hdl, dimm, NULL); cs != NULL;
cs = mcamd_cs_next(hdl, dimm, cs)) {
if (getcsprops(hdl, cs, "mc_offset_to_pa", &csp) < 0)
return (-1);
if (csp.dimmrank == rank)
break;
}
if (cs == NULL) {
mcamd_dprintf(hdl, MCAMD_DBG_FLOW, "mc_offset_to_pa: Current "
"dimm in this slot does not have a cs using rank %d\n",
rank);
return (mcamd_set_errno(hdl, EMCAMD_NOADDR));
}
if (mcp.badcs != MC_INVALNUM && csp.num == mcp.badcs) {
return (mcamd_set_errno(hdl, EMCAMD_NOADDR));
} else if (csp.testfail) {
return (mcamd_set_errno(hdl, EMCAMD_NOADDR));
} else if (mcp.sparecs != MC_INVALNUM && csp.num == mcp.sparecs &&
mcp.badcs != MC_INVALNUM) {
for (cs = mcamd_cs_next(hdl, mc, NULL); cs != NULL;
cs = mcamd_cs_next(hdl, mc, cs)) {
mcamd_prop_t csnum;
if (!mcamd_get_numprop(hdl, cs, MCAMD_PROP_NUM,
&csnum)) {
mcamd_dprintf(hdl, MCAMD_DBG_ERR,
"mcamd_offset_to_pa: csnum lookup failed "
"while looking for bad cs#");
return (mcamd_set_errno(hdl,
EMCAMD_TREEINVALID));
}
if (csnum == mcp.badcs)
break;
}
if (cs == NULL) {
mcamd_dprintf(hdl, MCAMD_DBG_ERR, "mcamd_offset_to_pa: "
"failed to find cs for bad cs#%d\n", mcp.badcs);
return (mcamd_set_errno(hdl, EMCAMD_TREEINVALID));
}
if (getcsprops(hdl, cs, "mc_offset_to_pa", &csp) < 0)
return (-1);
}
csmode = MC_CS_MODE(mcp.csbnkmap_reg, csp.num);
if (gettbls(hdl, csmode, &mcp, &bamp, &rcbmp,
mcp.bnkswzl ? &swzlp : NULL, &csi,
"mc_offset_to_pa") < 0)
return (-1);
if (MC_CSMASK_UNMASKABLE(mcp.rev) != 0) {
iaddr |= iaddr_add(hdl, iaddr,
BITS(csp.base, maskhi_hi + MC_CSMASK_UNMASKABLE(mcp.rev),
maskhi_hi + 1), "unmaskable cs basehi bits");
}
iaddr |= iaddr_add(hdl, iaddr,
BITS(csp.base, maskhi_hi, maskhi_lo) &
~BITS(csp.mask, maskhi_hi, maskhi_lo),
"cs basehi bits not being masked");
if (mcp.csintlvfctr > 1) {
iaddr |= iaddr_add(hdl, iaddr,
BITS(csp.base, masklo_hi, masklo_lo) &
~BITS(csp.mask, masklo_hi, masklo_lo),
"cs baselo bits not being masked");
}
iaddr |= iaddr_add(hdl, iaddr,
row_to_iaddr(hdl, bamp, rcbmp, &csi, rowaddr),
"add iaddr bits from row");
iaddr |= iaddr_add(hdl, iaddr,
col_to_iaddr(hdl, bamp, rcbmp, coladdr),
"add iaddr bits from col");
iaddr |= iaddr_add(hdl, iaddr,
bank_to_iaddr(hdl, rcbmp, swzlp, iaddr, bankaddr),
"add iaddr bits from bank");
if (iaddr_unnormalize(hdl, &mcp, iaddr, pap) < 0)
return (-1);
return (0);
}
int
mcamd_cs_size(struct mcamd_hdl *hdl, mcamd_node_t *mc, int csnum, size_t *szp)
{
uint_t csmode;
struct mcprops mcp;
const struct rct_bnkaddrmode *bamp;
if (getmcprops(hdl, mc, "mcamd_cs_size", &mcp) < 0)
return (-1);
csmode = MC_CS_MODE(mcp.csbnkmap_reg, csnum);
if (gettbls(hdl, csmode, &mcp, &bamp, NULL, NULL, NULL,
"mcamd_cs_size") < 0)
return (-1);
*szp = MC_CS_SIZE(bamp, mcp.width);
return (0);
}