#include <sys/ddi.h>
#include <sys/plat_ecc_dimm.h>
extern int plat_max_mc_units_per_board(void);
extern int plat_ecc_dispatch_task(plat_ecc_message_t *);
int (*p2get_mem_offset)(uint64_t, uint64_t *);
int (*p2get_mem_sid)(int, int, char *, int, int *);
int (*p2get_mem_addr)(int, char *, uint64_t, uint64_t *);
int plat_dimm_req_timeout = 30;
int plat_dimm_req_min_timeout = 6;
int plat_dimm_req_max_retries = 1;
static void plat_request_all_mem_sids(uint32_t);
int
plat_get_mem_sid(char *unum, char *buf, int buflen, int *lenp)
{
int board, pos, bank, dimm, jnumber;
int mcid;
if (p2get_mem_sid == NULL ||
(plat_ecc_capability_sc_get(PLAT_ECC_DIMM_SID_MESSAGE) == 0))
return (ENOTSUP);
if (parse_unum_memory(unum, &board, &pos, &bank, &dimm,
&jnumber) != 0)
return (EINVAL);
if (dimm < 0)
return (EINVAL);
mcid = plat_make_fru_cpuid(board, 0, pos);
dimm += (bank * 4);
return (p2get_mem_sid(mcid, dimm, buf, buflen, lenp));
}
int
plat_get_mem_offset(uint64_t paddr, uint64_t *offp)
{
if (p2get_mem_offset != NULL) {
return (p2get_mem_offset(paddr, offp));
} else
return (ENOTSUP);
}
int
plat_get_mem_addr(char *unum, char *sid, uint64_t offset, uint64_t *addrp)
{
int board, pos, bank, dimm, jnumber;
int mcid;
if (p2get_mem_addr == NULL ||
(plat_ecc_capability_sc_get(PLAT_ECC_DIMM_SID_MESSAGE) == 0))
return (ENOTSUP);
if (parse_unum_memory(unum, &board, &pos, &bank, &dimm,
&jnumber) != 0)
return (EINVAL);
mcid = plat_make_fru_cpuid(board, 0, pos);
return (p2get_mem_addr(mcid, sid, offset, addrp));
}
dimm_sid_cache_t *
plat_alloc_sid_cache(int *max_entries)
{
dimm_sid_cache_t *cache;
int i, bd, p;
int max_mc_per_bd = plat_max_mc_units_per_board();
*max_entries = plat_max_cpumem_boards() * max_mc_per_bd;
cache = (dimm_sid_cache_t *)kmem_zalloc(sizeof (dimm_sid_cache_t) *
*max_entries, KM_SLEEP);
for (i = 0; i < *max_entries; i++) {
bd = i / max_mc_per_bd;
p = i % max_mc_per_bd;
cache[i].mcid = plat_make_fru_cpuid(bd, 0, p);
}
return (cache);
}
static void
plat_populate_sid_cache_one(dimm_sid_cache_t *cache, int bd)
{
int i, j;
uint8_t valid;
dimm_sid_t *dimmsidsp;
int max_mc_per_bd = plat_max_mc_units_per_board();
ASSERT(domain_dimm_sids[bd].pdsb_valid_bitmap);
for (i = 0; i < max_mc_per_bd; i++) {
int index = bd * max_mc_per_bd + i;
if (cache[index].state != MC_DIMM_SIDS_REQUESTED)
continue;
valid = domain_dimm_sids[bd].pdsb_valid_bitmap >> (i * 8) &
0xff;
dimmsidsp = cache[index].sids;
for (j = 0; j < 8; j++) {
if (((1 << j) & valid) == 0)
continue;
(void) strncpy(dimmsidsp[j],
domain_dimm_sids[bd].pdsb_dimm_sids[(i * 8) + j],
PLAT_MAX_DIMM_SID_LEN);
}
cache[index].state = MC_DIMM_SIDS_AVAILABLE;
}
}
int
plat_populate_sid_cache(dimm_sid_cache_t *cache, int max_entries)
{
int i;
int bd;
uint32_t bds = 0, retry_bds = 0;
int max_mc_per_bd = plat_max_mc_units_per_board();
clock_t start_lbolt, current_lbolt;
ulong_t elapsed_sec;
int max_retries = plat_dimm_req_max_retries;
for (i = 0; i < max_entries; i++) {
if (cache[i].state == MC_DIMM_SIDS_REQUESTED) {
bd = i / max_mc_per_bd;
bds |= (1 << bd);
}
}
retry:
plat_request_all_mem_sids(bds);
if (plat_dimm_req_timeout < plat_dimm_req_min_timeout) {
cmn_err(CE_WARN, "plat_dimm_req_timeout (%d secs) is less "
"than the minimum value (%d secs). Resetting to "
"minimum.", plat_dimm_req_timeout,
plat_dimm_req_min_timeout);
plat_dimm_req_timeout = plat_dimm_req_min_timeout;
}
start_lbolt = ddi_get_lbolt();
while (bds) {
for (bd = 0; bd < plat_max_cpumem_boards(); bd++) {
if (((1 << bd) & bds) == 0)
continue;
switch (domain_dimm_sids[bd].pdsb_state) {
case PDSB_STATE_STORE_IN_PROGRESS:
current_lbolt = ddi_get_lbolt();
elapsed_sec = TICK_TO_SEC(current_lbolt -
start_lbolt);
if (elapsed_sec > plat_dimm_req_timeout) {
mutex_enter(&domain_dimm_sids[bd].
pdsb_lock);
domain_dimm_sids[bd].pdsb_state =
PDSB_STATE_FAILED_TO_STORE;
mutex_exit(&domain_dimm_sids[bd].
pdsb_lock);
}
continue;
case PDSB_STATE_FAILED_TO_STORE:
retry_bds |= (1 << bd);
break;
case PDSB_STATE_STORED:
plat_populate_sid_cache_one(cache, bd);
break;
default:
cmn_err(CE_PANIC, "Unknown state (0x%x) for "
"domain_dimm_sids[%d]",
domain_dimm_sids[bd].pdsb_state, bd);
}
bds &= ~(1 << bd);
}
if (bds != 0)
delay(drv_usectohz(500000));
}
if (max_retries-- && retry_bds) {
bds = retry_bds;
retry_bds = 0;
goto retry;
} else if (!max_retries && retry_bds) {
cmn_err(CE_WARN, "!Unable to retrieve DIMM serial ids for "
"boards 0x%x", retry_bds);
return (ETIMEDOUT);
}
return (0);
}
int
plat_store_mem_sids(plat_dimm_sid_board_data_t *data)
{
int bd;
int i;
bd = data->pdsbd_board_num;
mutex_enter(&domain_dimm_sids[bd].pdsb_lock);
if (data->pdsbd_errno) {
domain_dimm_sids[bd].pdsb_state = PDSB_STATE_FAILED_TO_STORE;
mutex_exit(&domain_dimm_sids[bd].pdsb_lock);
cmn_err(CE_WARN, "!plat_store_mem_sids: bd %d errno %d", bd,
data->pdsbd_errno);
return (data->pdsbd_errno);
}
domain_dimm_sids[bd].pdsb_valid_bitmap = data->pdsbd_valid_bitmap;
for (i = 0; i < PLAT_MAX_DIMMS_PER_BOARD; i++) {
if ((1 << i) & domain_dimm_sids[bd].pdsb_valid_bitmap) {
(void) strncpy(domain_dimm_sids[bd].pdsb_dimm_sids[i],
data->pdsbd_dimm_sids[i], PLAT_MAX_DIMM_SID_LEN);
}
}
domain_dimm_sids[bd].pdsb_state = PDSB_STATE_STORED;
mutex_exit(&domain_dimm_sids[bd].pdsb_lock);
return (0);
}
static void
plat_request_all_mem_sids(uint32_t bds)
{
int bd;
int ret;
for (bd = 0; bd < plat_max_cpumem_boards(); bd++) {
if (!((1 << bd) & bds))
continue;
ret = plat_request_mem_sids(bd);
if (ret) {
mutex_enter(&domain_dimm_sids[bd].pdsb_lock);
domain_dimm_sids[bd].pdsb_state =
PDSB_STATE_FAILED_TO_STORE;
mutex_exit(&domain_dimm_sids[bd].pdsb_lock);
}
}
}
int
plat_request_mem_sids(int boardnum)
{
plat_ecc_message_t *wrapperp;
plat_dimm_sid_request_data_t *dreqp;
if (domain_dimm_sids[boardnum].pdsb_state == PDSB_STATE_STORED)
return (0);
mutex_enter(&domain_dimm_sids[boardnum].pdsb_lock);
domain_dimm_sids[boardnum].pdsb_state = PDSB_STATE_STORE_IN_PROGRESS;
mutex_exit(&domain_dimm_sids[boardnum].pdsb_lock);
wrapperp = kmem_zalloc(sizeof (plat_ecc_message_t), KM_SLEEP);
wrapperp->ecc_msg_status = PLAT_ECC_NO_MSG_ACTIVE;
wrapperp->ecc_msg_type = PLAT_ECC_DIMM_SID_MESSAGE;
wrapperp->ecc_msg_len = sizeof (plat_dimm_sid_request_data_t);
wrapperp->ecc_msg_data = kmem_zalloc(wrapperp->ecc_msg_len, KM_SLEEP);
dreqp = (plat_dimm_sid_request_data_t *)wrapperp->ecc_msg_data;
dreqp->pdsrd_major_version = PLAT_ECC_DIMM_SID_VERSION_MAJOR;
dreqp->pdsrd_minor_version = PLAT_ECC_DIMM_SID_VERSION_MINOR;
dreqp->pdsrd_msg_type = PLAT_ECC_DIMM_SID_MESSAGE;
dreqp->pdsrd_msg_length = wrapperp->ecc_msg_len;
dreqp->pdsrd_board_num = boardnum;
return (plat_ecc_dispatch_task(wrapperp));
}
int
plat_discard_mem_sids(int boardnum)
{
mutex_enter(&domain_dimm_sids[boardnum].pdsb_lock);
domain_dimm_sids[boardnum].pdsb_state = PDSB_STATE_INVALID;
mutex_exit(&domain_dimm_sids[boardnum].pdsb_lock);
return (0);
}