17532SSean.Ye@Sun.COM /*
27532SSean.Ye@Sun.COM * CDDL HEADER START
37532SSean.Ye@Sun.COM *
47532SSean.Ye@Sun.COM * The contents of this file are subject to the terms of the
57532SSean.Ye@Sun.COM * Common Development and Distribution License (the "License").
67532SSean.Ye@Sun.COM * You may not use this file except in compliance with the License.
77532SSean.Ye@Sun.COM *
87532SSean.Ye@Sun.COM * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97532SSean.Ye@Sun.COM * or http://www.opensolaris.org/os/licensing.
107532SSean.Ye@Sun.COM * See the License for the specific language governing permissions
117532SSean.Ye@Sun.COM * and limitations under the License.
127532SSean.Ye@Sun.COM *
137532SSean.Ye@Sun.COM * When distributing Covered Code, include this CDDL HEADER in each
147532SSean.Ye@Sun.COM * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157532SSean.Ye@Sun.COM * If applicable, add the following below this CDDL HEADER, with the
167532SSean.Ye@Sun.COM * fields enclosed by brackets "[]" replaced with your own identifying
177532SSean.Ye@Sun.COM * information: Portions Copyright [yyyy] [name of copyright owner]
187532SSean.Ye@Sun.COM *
197532SSean.Ye@Sun.COM * CDDL HEADER END
207532SSean.Ye@Sun.COM */
217532SSean.Ye@Sun.COM
227532SSean.Ye@Sun.COM /*
2310049SVuong.Nguyen@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
247532SSean.Ye@Sun.COM * Use is subject to license terms.
257532SSean.Ye@Sun.COM */
267532SSean.Ye@Sun.COM
277532SSean.Ye@Sun.COM #include <sys/types.h>
287532SSean.Ye@Sun.COM #include <sys/kmem.h>
297532SSean.Ye@Sun.COM #include <sys/mc.h>
307532SSean.Ye@Sun.COM #include <sys/nvpair.h>
317532SSean.Ye@Sun.COM #include <sys/fm/protocol.h>
327532SSean.Ye@Sun.COM #include <sys/cmn_err.h>
337532SSean.Ye@Sun.COM #include <sys/sunddi.h>
347532SSean.Ye@Sun.COM #include <sys/mc_intel.h>
357532SSean.Ye@Sun.COM #include "nb_log.h"
367532SSean.Ye@Sun.COM #include "rank.h"
377532SSean.Ye@Sun.COM #include "dimm_phys.h"
387532SSean.Ye@Sun.COM #include "nb5000.h"
397532SSean.Ye@Sun.COM
407532SSean.Ye@Sun.COM struct rank_base *rank_base;
417532SSean.Ye@Sun.COM
427532SSean.Ye@Sun.COM static int
fmri2unum(nvlist_t * nvl,mc_unum_t * unump)437532SSean.Ye@Sun.COM fmri2unum(nvlist_t *nvl, mc_unum_t *unump)
447532SSean.Ye@Sun.COM {
457532SSean.Ye@Sun.COM int i;
467532SSean.Ye@Sun.COM uint64_t offset;
477532SSean.Ye@Sun.COM nvlist_t **hcl, *hcsp;
487532SSean.Ye@Sun.COM uint_t npr;
497532SSean.Ye@Sun.COM
507532SSean.Ye@Sun.COM if (nvlist_lookup_nvlist(nvl, FM_FMRI_HC_SPECIFIC, &hcsp) != 0 ||
517532SSean.Ye@Sun.COM (nvlist_lookup_uint64(hcsp, "asru-" FM_FMRI_HC_SPECIFIC_OFFSET,
527532SSean.Ye@Sun.COM &offset) != 0 && nvlist_lookup_uint64(hcsp,
537532SSean.Ye@Sun.COM FM_FMRI_HC_SPECIFIC_OFFSET, &offset) != 0) ||
547532SSean.Ye@Sun.COM nvlist_lookup_nvlist_array(nvl, FM_FMRI_HC_LIST, &hcl, &npr) != 0)
557532SSean.Ye@Sun.COM return (0);
567532SSean.Ye@Sun.COM
577532SSean.Ye@Sun.COM
587532SSean.Ye@Sun.COM bzero(unump, sizeof (mc_unum_t));
597532SSean.Ye@Sun.COM for (i = 0; i < MC_UNUM_NDIMM; i++)
607532SSean.Ye@Sun.COM unump->unum_dimms[i] = MC_INVALNUM;
617532SSean.Ye@Sun.COM
627532SSean.Ye@Sun.COM for (i = 0; i < npr; i++) {
637532SSean.Ye@Sun.COM char *hcnm, *hcid;
647532SSean.Ye@Sun.COM long v;
657532SSean.Ye@Sun.COM
667532SSean.Ye@Sun.COM if (nvlist_lookup_string(hcl[i], FM_FMRI_HC_NAME, &hcnm) != 0 ||
677532SSean.Ye@Sun.COM nvlist_lookup_string(hcl[i], FM_FMRI_HC_ID, &hcid) != 0 ||
687532SSean.Ye@Sun.COM ddi_strtol(hcid, NULL, 0, &v) != 0)
697532SSean.Ye@Sun.COM return (0);
707532SSean.Ye@Sun.COM
717532SSean.Ye@Sun.COM if (strcmp(hcnm, "motherboard") == 0)
727532SSean.Ye@Sun.COM unump->unum_board = (int)v;
737532SSean.Ye@Sun.COM else if (strcmp(hcnm, "memory-controller") == 0)
747532SSean.Ye@Sun.COM unump->unum_mc = (int)v;
757532SSean.Ye@Sun.COM else if (strcmp(hcnm, "dram-channel") == 0)
767532SSean.Ye@Sun.COM unump->unum_cs = (int)v;
777532SSean.Ye@Sun.COM else if (strcmp(hcnm, "dimm") == 0)
787532SSean.Ye@Sun.COM unump->unum_dimms[0] = (int)v;
797532SSean.Ye@Sun.COM else if (strcmp(hcnm, "rank") == 0)
807532SSean.Ye@Sun.COM unump->unum_rank = (int)v;
817532SSean.Ye@Sun.COM }
827532SSean.Ye@Sun.COM
837532SSean.Ye@Sun.COM return (1);
847532SSean.Ye@Sun.COM }
857532SSean.Ye@Sun.COM
867532SSean.Ye@Sun.COM /*ARGSUSED*/
877532SSean.Ye@Sun.COM static cmi_errno_t
inb_patounum(void * arg,uint64_t pa,uint8_t valid_hi,uint8_t valid_lo,uint32_t synd,int syndtype,mc_unum_t * unump)887532SSean.Ye@Sun.COM inb_patounum(void *arg, uint64_t pa, uint8_t valid_hi, uint8_t valid_lo,
897532SSean.Ye@Sun.COM uint32_t synd, int syndtype, mc_unum_t *unump)
907532SSean.Ye@Sun.COM {
917532SSean.Ye@Sun.COM struct rank_base *rp;
927532SSean.Ye@Sun.COM int i;
937532SSean.Ye@Sun.COM int last;
947532SSean.Ye@Sun.COM uint64_t offset;
957532SSean.Ye@Sun.COM cmi_errno_t rt = CMIERR_UNKNOWN;
967532SSean.Ye@Sun.COM
977532SSean.Ye@Sun.COM last = nb_dimms_per_channel * nb_number_memory_controllers;
987532SSean.Ye@Sun.COM for (i = 0; i < last; i++) {
997532SSean.Ye@Sun.COM rp = &rank_base[i];
1007532SSean.Ye@Sun.COM if (rp && pa >= rp->base && pa < rp->limit)
1017532SSean.Ye@Sun.COM break;
1027532SSean.Ye@Sun.COM }
1037532SSean.Ye@Sun.COM if (i < last) {
1047532SSean.Ye@Sun.COM offset = pa - rp->base;
1057532SSean.Ye@Sun.COM if (offset > rp->hole)
1067532SSean.Ye@Sun.COM offset -= rp->hole_size;
1077532SSean.Ye@Sun.COM unump->unum_offset = offset / rp->interleave;
1087532SSean.Ye@Sun.COM unump->unum_mc = i / nb_dimms_per_channel;
1097532SSean.Ye@Sun.COM unump->unum_cs = 0;
1107532SSean.Ye@Sun.COM unump->unum_rank = i % nb_dimms_per_channel;
1117532SSean.Ye@Sun.COM rt = CMI_SUCCESS;
1127532SSean.Ye@Sun.COM }
1137532SSean.Ye@Sun.COM return (rt);
1147532SSean.Ye@Sun.COM }
1157532SSean.Ye@Sun.COM
1167532SSean.Ye@Sun.COM /*ARGSUSED*/
1177532SSean.Ye@Sun.COM static cmi_errno_t
inb_unumtopa(void * arg,mc_unum_t * unump,nvlist_t * nvl,uint64_t * pap)1187532SSean.Ye@Sun.COM inb_unumtopa(void *arg, mc_unum_t *unump, nvlist_t *nvl, uint64_t *pap)
1197532SSean.Ye@Sun.COM {
12010049SVuong.Nguyen@Sun.COM int num_ranks_per_branch;
1217532SSean.Ye@Sun.COM mc_unum_t unum;
1227532SSean.Ye@Sun.COM uint64_t pa;
1237532SSean.Ye@Sun.COM struct rank_base *rp;
1247532SSean.Ye@Sun.COM
1257532SSean.Ye@Sun.COM if (unump == NULL) {
1267532SSean.Ye@Sun.COM if (!fmri2unum(nvl, &unum))
1277532SSean.Ye@Sun.COM return (CMI_SUCCESS);
1287532SSean.Ye@Sun.COM unump = &unum;
1297532SSean.Ye@Sun.COM }
130*10650SVuong.Nguyen@Sun.COM if ((unump->unum_offset & OFFSET_ROW_BANK_COL)) {
131*10650SVuong.Nguyen@Sun.COM if (&dimm_getphys) {
132*10650SVuong.Nguyen@Sun.COM pa = dimm_getphys(unump->unum_mc,
133*10650SVuong.Nguyen@Sun.COM TCODE_OFFSET_RANK(unump->unum_offset),
134*10650SVuong.Nguyen@Sun.COM TCODE_OFFSET_BANK(unump->unum_offset),
135*10650SVuong.Nguyen@Sun.COM TCODE_OFFSET_RAS(unump->unum_offset),
136*10650SVuong.Nguyen@Sun.COM TCODE_OFFSET_CAS(unump->unum_offset));
137*10650SVuong.Nguyen@Sun.COM if (pa >= MAXPHYS_ADDR)
138*10650SVuong.Nguyen@Sun.COM return (CMIERR_MC_NOADDR);
139*10650SVuong.Nguyen@Sun.COM } else {
1407532SSean.Ye@Sun.COM return (CMIERR_MC_NOADDR);
141*10650SVuong.Nguyen@Sun.COM }
1427532SSean.Ye@Sun.COM *pap = pa;
1437532SSean.Ye@Sun.COM return (CMI_SUCCESS);
1447532SSean.Ye@Sun.COM }
14510049SVuong.Nguyen@Sun.COM
14610049SVuong.Nguyen@Sun.COM
14710049SVuong.Nguyen@Sun.COM /* max number of ranks per branch */
14810049SVuong.Nguyen@Sun.COM num_ranks_per_branch = (nb_chipset == INTEL_NB_5100) ?
14910049SVuong.Nguyen@Sun.COM NB_5100_RANKS_PER_CHANNEL :
15010049SVuong.Nguyen@Sun.COM nb_dimms_per_channel * nb_channels_per_branch;
15110049SVuong.Nguyen@Sun.COM rp = &rank_base[(unump->unum_mc * num_ranks_per_branch) +
1527532SSean.Ye@Sun.COM unump->unum_rank];
1537532SSean.Ye@Sun.COM pa = rp->base + (unump->unum_offset * rp->interleave);
1547532SSean.Ye@Sun.COM
1557532SSean.Ye@Sun.COM if (rp->hole && pa >= rp->hole)
1567532SSean.Ye@Sun.COM pa += rp->hole_size;
1577532SSean.Ye@Sun.COM *pap = pa;
1587532SSean.Ye@Sun.COM return (CMI_SUCCESS);
1597532SSean.Ye@Sun.COM }
1607532SSean.Ye@Sun.COM
1617532SSean.Ye@Sun.COM void
dimm_init()1627532SSean.Ye@Sun.COM dimm_init()
1637532SSean.Ye@Sun.COM {
16410049SVuong.Nguyen@Sun.COM int num_ranks_per_branch;
16510049SVuong.Nguyen@Sun.COM
16610049SVuong.Nguyen@Sun.COM
16710049SVuong.Nguyen@Sun.COM /* max number of ranks per branch */
16810049SVuong.Nguyen@Sun.COM num_ranks_per_branch = (nb_chipset == INTEL_NB_5100) ?
16910049SVuong.Nguyen@Sun.COM NB_5100_RANKS_PER_CHANNEL :
17010049SVuong.Nguyen@Sun.COM nb_dimms_per_channel * nb_channels_per_branch;
17110049SVuong.Nguyen@Sun.COM
1727532SSean.Ye@Sun.COM rank_base = kmem_zalloc(sizeof (struct rank_base) *
17310049SVuong.Nguyen@Sun.COM nb_number_memory_controllers * num_ranks_per_branch, KM_SLEEP);
1747532SSean.Ye@Sun.COM }
1757532SSean.Ye@Sun.COM
1767532SSean.Ye@Sun.COM void
dimm_fini()1777532SSean.Ye@Sun.COM dimm_fini()
1787532SSean.Ye@Sun.COM {
17910049SVuong.Nguyen@Sun.COM int num_ranks_per_branch;
18010049SVuong.Nguyen@Sun.COM
18110049SVuong.Nguyen@Sun.COM
18210049SVuong.Nguyen@Sun.COM /* max number of ranks per branch */
18310049SVuong.Nguyen@Sun.COM num_ranks_per_branch = (nb_chipset == INTEL_NB_5100) ?
18410049SVuong.Nguyen@Sun.COM NB_5100_RANKS_PER_CHANNEL :
18510049SVuong.Nguyen@Sun.COM nb_dimms_per_channel * nb_channels_per_branch;
18610049SVuong.Nguyen@Sun.COM
1877532SSean.Ye@Sun.COM kmem_free(rank_base, sizeof (struct rank_base) *
18810049SVuong.Nguyen@Sun.COM nb_number_memory_controllers * num_ranks_per_branch);
1897532SSean.Ye@Sun.COM rank_base = 0;
1907532SSean.Ye@Sun.COM }
1917532SSean.Ye@Sun.COM
1927532SSean.Ye@Sun.COM void
dimm_add_rank(int branch,int rank,int branch_interleave,int way,uint64_t base,uint32_t hole,uint32_t hole_size,int interleave,uint64_t limit)1937532SSean.Ye@Sun.COM dimm_add_rank(int branch, int rank, int branch_interleave, int way,
1947532SSean.Ye@Sun.COM uint64_t base, uint32_t hole, uint32_t hole_size, int interleave,
1957532SSean.Ye@Sun.COM uint64_t limit)
1967532SSean.Ye@Sun.COM {
1977532SSean.Ye@Sun.COM struct rank_base *rp;
19810049SVuong.Nguyen@Sun.COM int num_ranks_per_branch;
1997532SSean.Ye@Sun.COM
20010049SVuong.Nguyen@Sun.COM /* max number of ranks per branch */
20110049SVuong.Nguyen@Sun.COM num_ranks_per_branch = (nb_chipset == INTEL_NB_5100) ?
20210049SVuong.Nguyen@Sun.COM NB_5100_RANKS_PER_CHANNEL :
20310049SVuong.Nguyen@Sun.COM nb_dimms_per_channel * nb_channels_per_branch;
20410049SVuong.Nguyen@Sun.COM rp = &rank_base[(branch * num_ranks_per_branch) + rank];
2057532SSean.Ye@Sun.COM rp->branch_interleave = branch_interleave;
2067532SSean.Ye@Sun.COM rp->way = way;
2077532SSean.Ye@Sun.COM rp->base = base;
2087532SSean.Ye@Sun.COM rp->hole = hole;
2097532SSean.Ye@Sun.COM rp->hole_size = hole_size;
2107532SSean.Ye@Sun.COM rp->interleave = interleave;
2117532SSean.Ye@Sun.COM rp->limit = limit;
2127532SSean.Ye@Sun.COM }
2137532SSean.Ye@Sun.COM
2147532SSean.Ye@Sun.COM static const cmi_mc_ops_t inb_mc_ops = {
2157532SSean.Ye@Sun.COM inb_patounum,
2167532SSean.Ye@Sun.COM inb_unumtopa,
2177532SSean.Ye@Sun.COM nb_error_trap /* cmi_mc_logout */
2187532SSean.Ye@Sun.COM };
2197532SSean.Ye@Sun.COM
2207532SSean.Ye@Sun.COM /*ARGSUSED*/
2217532SSean.Ye@Sun.COM int
inb_mc_register(cmi_hdl_t hdl,void * arg1,void * arg2,void * arg3)2227532SSean.Ye@Sun.COM inb_mc_register(cmi_hdl_t hdl, void *arg1, void *arg2, void *arg3)
2237532SSean.Ye@Sun.COM {
2247532SSean.Ye@Sun.COM cmi_mc_register(hdl, &inb_mc_ops, NULL);
2257532SSean.Ye@Sun.COM return (CMI_HDL_WALK_NEXT);
2267532SSean.Ye@Sun.COM }
227