mc-us3.c revision 8793b36b40d14ad0a0fecc97738dc118a928f46c
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <sys/ddi_impldefs.h>
#include <sys/sysmacros.h>
#include <sys/autoconf.h>
#include <sys/cpu_module.h>
#include <sys/platform_module.h>
/*
* Function prototypes
*/
/*
* Configuration data structures
*/
mc_open, /* open */
mc_close, /* close */
nulldev, /* strategy */
nulldev, /* print */
nodev, /* dump */
nulldev, /* read */
nulldev, /* write */
mc_ioctl, /* ioctl */
nodev, /* devmap */
nodev, /* mmap */
nodev, /* segmap */
nochpoll, /* poll */
ddi_prop_op, /* cb_prop_op */
0, /* streamtab */
CB_REV, /* rev */
nodev, /* cb_aread */
nodev /* cb_awrite */
};
DEVO_REV, /* rev */
0, /* refcnt */
ddi_getinfo_1to1, /* getinfo */
nulldev, /* identify */
nulldev, /* probe */
mc_attach, /* attach */
mc_detach, /* detach */
nulldev, /* reset */
&mc_cb_ops, /* cb_ops */
(struct bus_ops *)0, /* bus_ops */
nulldev /* power */
};
/*
* Driver globals
*/
static void *mcp;
static int nmcs = 0;
static int seg_id = 0;
static int nsegments = 0;
static int maxbanks = 0;
static kmutex_t mcdatamutex;
static krwlock_t mcdimmsids_rw;
/* pointer to cache of DIMM serial ids */
static dimm_sid_cache_t *mc_dimm_sids;
static int max_entries;
extern struct mod_ops mod_driverops;
&mod_driverops, /* module type, this one is a driver */
"Memory-controller", /* module name */
&mc_ops, /* driver ops */
};
static struct modlinkage modlinkage = {
MODREV_1, /* rev */
(void *)&modldrv,
};
static int mc_init_sid_cache(void);
static int mc_get_mcregs(struct mc_soft_state *);
static int mc_populate_sid_cache(void);
static int mc_get_sid_cache_index(int mcid);
#pragma weak p2get_mem_unum
#pragma weak p2get_mem_info
#pragma weak p2get_mem_sid
#pragma weak p2get_mem_offset
#pragma weak p2get_mem_addr
#pragma weak p2init_sid_cache
#pragma weak plat_add_mem_unum_label
#pragma weak plat_alloc_sid_cache
#pragma weak plat_populate_sid_cache
#define QWORD_SIZE 144
/*
* These are the module initialization routines.
*/
int
_init(void)
{
int error;
sizeof (struct mc_soft_state), 1)) != 0)
return (error);
if (error == 0) {
}
return (error);
}
int
_fini(void)
{
int error;
return (error);
if (mc_dimm_sids)
return (0);
}
int
{
}
static int
{
struct mc_soft_state *softsp;
/* get the instance of this devi */
switch (cmd) {
case DDI_ATTACH:
break;
case DDI_RESUME:
/* get the soft state pointer for this device node */
instance));
/*
* During resume, the source and target board's bank_infos
* need to be updated with the new mc MADR values. This is
* implemented with existing functionality by first removing
* the props and allocated data structs, and then adding them
* back in.
*/
MEM_CFG_PROP_NAME) == 1) {
}
instance);
}
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
return (DDI_FAILURE);
/* Set the dip in the soft state */
instance, "portid"));
goto bad;
}
/* map in the registers for this device. */
instance));
goto bad;
}
/*
* Get the label of dimms and pin routing information at memory-layout
* property if the memory controller is enabled.
*
* Basically every memory-controller node on every machine should
* have one of these properties unless the memory controller is
* physically not capable of having memory attached to it, e.g.
* Excalibur's slave processor.
*/
if (err == DDI_PROP_SUCCESS) {
/*
* Set the pointer and size of property in the soft state
*/
} else if (err == DDI_PROP_NOT_FOUND) {
/*
* This is a disable MC. Clear out the pointer and size
* of property in the soft state
*/
} else {
goto bad2;
}
/* Get MC registers and construct all needed data structure */
goto bad1;
if (nmcs == 1) {
if (&p2get_mem_unum)
if (&p2get_mem_info)
if (&p2get_mem_sid)
if (&p2get_mem_offset)
if (&p2get_mem_addr)
if (&p2init_sid_cache)
}
/*
* Update DIMM serial id information if the DIMM serial id
* cache has already been initialized.
*/
if (mc_dimm_sids) {
(void) mc_populate_sid_cache();
}
"ddi_mem_ctrl", 0) != DDI_SUCCESS) {
" failed \n"));
goto bad1;
}
return (DDI_SUCCESS);
bad1:
/* release all allocated data struture for this MC */
/* remove the libdevinfo property */
MEM_CFG_PROP_NAME) == 1) {
}
bad2:
/* unmap the registers for this device. */
bad:
return (DDI_FAILURE);
}
/* ARGSUSED */
static int
{
int instance;
struct mc_soft_state *softsp;
/* get the instance of this devi */
/* get the soft state pointer for this device node */
switch (cmd) {
case DDI_SUSPEND:
return (DDI_SUCCESS);
case DDI_DETACH:
break;
default:
return (DDI_FAILURE);
}
/* remove the libdevinfo property */
MEM_CFG_PROP_NAME) == 1) {
}
/* release all allocated data struture for this MC */
/* unmap the registers */
if (nmcs == 0) {
if (&p2get_mem_unum)
if (&p2get_mem_info)
if (&p2get_mem_sid)
if (&p2get_mem_offset)
if (&p2get_mem_addr)
if (&p2init_sid_cache)
}
/* free up the soft state */
return (DDI_SUCCESS);
}
/* ARGSUSED */
static int
{
/* verify that otyp is appropriate */
return (EINVAL);
}
return (0);
}
/* ARGSUSED */
static int
{
return (0);
}
/*
* cmd includes MCIOC_MEMCONF, MCIOC_MEM, MCIOC_SEG, MCIOC_BANK, MCIOC_DEVGRP,
* MCIOC_CTRLCONF, MCIOC_CONTROL.
*
* MCIOC_MEM, MCIOC_SEG, MCIOC_CTRLCONF, and MCIOC_CONTROL are
* associated with various length struct. If given number is less than the
* number in kernel, update the number and return EINVAL so that user could
* allocate enough space for it.
*
*/
/* ARGSUSED */
static int
int *rval_p)
{
struct mc_memconf mcmconf;
struct mctrl_info *mcport;
int i, status = 0;
switch (cmd) {
case MCIOC_MEMCONF:
return (EFAULT);
return (0);
/*
* input: nsegments and allocate space for various length of segmentids
*
* return 0: size, number of segments, and all segment ids,
* where glocal and local ids are identical.
* EINVAL: if the given nsegments is less than that in kernel and
* nsegments of struct will be updated.
* EFAULT: if other errors in kernel.
*/
case MCIOC_MEM:
sizeof (struct mc_memory)) != 0)
return (EFAULT);
sizeof (struct mc_memory)))
else
return (status);
}
sizeof (mcmem->segmentids[0]);
for (i = 0; i < nsegments; i++) {
}
return (status);
/*
* input: id, nbanks and allocate space for various length of bankids
*
* return 0: base, size, number of banks, and all bank ids,
* where global id is unique of all banks and local id
* is only unique for mc.
* EINVAL: either id isn't found or if given nbanks is less than
* that in kernel and nbanks of struct will be updated.
* EFAULT: if other errors in kernel.
*/
case MCIOC_SEG:
sizeof (struct mc_segment)) != 0)
return (EFAULT);
return (EFAULT);
}
sizeof (struct mc_segment)))
else
return (status);
}
i = 0;
}
return (status);
/*
* input: id
*
* return 0: mask, match, size, and devgrpid,
* where global id is unique of all devgrps and local id
* is only unique for mc.
* EINVAL: if id isn't found
* EFAULT: if other errors in kernel.
*/
case MCIOC_BANK:
return (EFAULT);
return (EINVAL);
}
/*
* If (Physic Address & MASK) == MATCH, Physic Address is
* located at this bank. The lower physical address bits
* are at [9-6].
*/
MADR_LK_SHIFT))) << MADR_LPA_SHIFT;
return (EFAULT);
return (0);
/*
* input:id and allocate space for various length of deviceids
*
* return 0: size and number of devices.
* EINVAL: id isn't found
* EFAULT: if other errors in kernel.
*/
case MCIOC_DEVGRP:
sizeof (struct mc_devgrp)) != 0)
return (EFAULT);
return (EINVAL);
}
return (status);
/*
* input: nmcs and allocate space for various length of mcids
*
* return 0: number of mc, and all mcids,
* where glocal and local ids are identical.
* EINVAL: if the given nmcs is less than that in kernel and
* nmcs of struct will be updated.
* EFAULT: if other errors in kernel.
*/
case MCIOC_CTRLCONF:
sizeof (struct mc_ctrlconf)) != 0)
return (EFAULT);
sizeof (struct mc_ctrlconf)))
else
return (status);
}
/*
* Cannot just use the size of the struct because of the various
* length struct
*/
sizeof (mcctrlconf->mcids[0]));
/* Get all MC ids and add to mcctrlconf */
mctrl = mctrl_head;
i = 0;
i++;
}
return (status);
/*
* input:id, ndevgrps and allocate space for various length of devgrpids
*
* return 0: number of devgrp, and all devgrpids,
* is unique of all devgrps and local id is only unique
* for mc.
* EINVAL: either if id isn't found or if the given ndevgrps is
* less than that in kernel and ndevgrps of struct will
* be updated.
* EFAULT: if other errors in kernel.
*/
case MCIOC_CONTROL:
sizeof (struct mc_control)) != 0)
return (EFAULT);
mctrl_head)) == NULL) {
return (EINVAL);
}
/*
* mcport->ndevgrps zero means Memory Controller is disable.
*/
sizeof (struct mc_control)))
return (status);
}
}
return (status);
/*
* input:id
*
* return 0: CPU flushed successfully.
* EINVAL: the id wasn't found
*/
case MCIOC_ECFLUSH:
return (EINVAL);
return (0);
default:
return (EFAULT);
}
}
/*
* Get Memory Address Decoding Registers and construct list.
* flag is to workaround Cheetah's restriction where register cannot be mapped
* if port id(MC registers on it) == cpu id(process is running on it).
*/
static int
{
int i;
int err = 0;
/* Construct lists for MC, mctrl_info, dgrp_info, and device_info */
/*
* If memlayoutp is NULL, the Memory Controller is disable, and
* doesn't need to create any bank and segment.
*/
goto exit;
/*
* Get the content of 4 Memory Address Decoding Registers, and
* construct lists of logical banks and segments.
*/
for (i = 0; i < NBANKS; i++) {
else
(i * REGOFFSET)));
ma_reg_array[i] = madreg;
break;
}
/*
* Create the logical bank property for this mc node. This
* property is an encoded array of the madr for each logical
* bank (there are NBANKS of these).
*/
MEM_CFG_PROP_NAME) != 1) {
}
exit:
if (!err) {
nmcs++;
}
return (err);
}
/*
* Translate a <DIMM, offset> pair to a physical address.
*/
static int
{
/*
* Compute the half-dimm size in bytes.
* Note that bank->size represents the number of data bytes,
* and does not include the additional bits used for ecc, mtag,
* and mtag ecc information in each 144-bit checkword.
* For calculating the offset to a checkword we need the size
* including the additional 8 bytes for each 64 data bytes of
* a cache line.
*/
/*
* Check if the offset is within this bank. This depends on the position
* of the bank, i.e., whether it is the front bank or the back bank.
*/
return (-1);
/*
* Compute the offset within the half-dimm.
*/
/*
* Compute the line within the half-dimm. This is the same as the line
* within the bank since each DIMM in a bank contributes uniformly
* 144 bits (18 bytes) to a cache line.
*/
/*
* Compute the line within the segment.
* The bank->lm field indicates the order in which cache lines are
* distributed across the banks of a segment (See the Cheetah PRM).
* The interleave factor the bank is programmed with is used instead
* of the segment interleave factor since a segment can be composed
* of banks with different interleave factors if the banks are not
* uniform in size.
*/
/*
* Compute the physical address assuming that there are 64 data bytes
* in a cache line.
*/
return (0);
}
/*
* Translate a physical address to a <DIMM, offset> pair.
*/
static void
{
/*
* Compute the line within the segment assuming that there are 64 data
* bytes in a cache line.
*/
/*
* The lm (lower match) field from the Memory Address Decoding Register
* for this bank determines which lines within a memory segment this
* bank should respond to. These are the actual address bits the
* interleave is done over (See the Cheetah PRM).
* In other words, the lm field indicates the order in which the cache
* lines are distributed across the banks of a segment, and thusly it
* can be used to compute the line within this bank. This is the same as
* the line within the half-dimm. This is because each DIMM in a bank
* contributes uniformly to every cache line.
*/
/*
* Compute the offset within the half-dimm. This depends on whether
* or not the bank is a front logical bank or a back logical bank.
*/
/*
* Compute the half-dimm size in bytes.
* Note that bank->size represents the number of data bytes,
* and does not include the additional bits used for ecc, mtag,
* and mtag ecc information in each 144-bit quadword.
* For calculating the offset to a checkword we need the size
* including the additional 8 bytes for each 64 data bytes of
* a cache line.
*/
/*
* Compute the offset within the dimm to the nearest line. This depends
* on whether or not the bank is a front logical bank or a back logical
* bank.
*/
remainder /= 16;
}
/*
* A cache line is composed of four quadwords with the associated ECC, the
* MTag along with its associated ECC. This is depicted below:
*
* | Data | ECC | Mtag |MTag ECC|
* 127 0 8 0 2 0 3 0
*
* synd_code will be mapped as the following order to mc_get_mem_unum.
* 143 16 7 4 0
*
* | Quadword 0 | Quadword 1 | Quadword 2 | Quadword 3 |
* 575 432 431 288 287 144 143 0
*
* dimm table: each bit at a cache line needs two bits to present one of
* four dimms. So it needs 144 bytes(576 * 2 / 8). The content is in
* big edian order, i.e. dimm_table[0] presents for bit 572 to 575.
*
* pin table: each bit at a cache line needs one byte to present pin position,
* where max. is 230. So it needs 576 bytes. The order of table index is
* the same as bit position at a cache line, i.e. pin_table[0] presents
* for bit 0, Mtag ECC 0 of Quadword 3.
*
* This is a mapping from syndrome code to QuadWord Logical layout at Safari.
* Referring to Figure 3-4, Excalibur Architecture Manual.
* This table could be moved to cheetah.c if other platform teams agree with
* the bit layout at QuadWord.
*/
{
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127,
128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
7, 8, 9, 10, 11, 12, 13, 14, 15, 4, 5, 6, 0, 1, 2, 3,
};
/* ARGSUSED */
static int
{
char unum[UNUM_NAMLEN];
/*
* Enforce old Openboot requirement for synd code, either a single-bit
* code from 0..QWORD_SIZE-1 or -1 (multi-bit error).
*/
return (EINVAL);
unum[0] = '\0';
/*
* Scan all logical banks to get one responding to the physical
* address. Then compute the index to look up dimm and pin tables
* to generate the unum.
*/
/*
* The Address Decoding logic decodes the different fields
* in the Memory Address Decoding register to determine
* whether a particular logical bank should respond to a
* physical address.
*/
continue;
}
/*
* single-bit error handling, we can identify specific
* DIMM.
*/
pinp++;
/* or quadword = (paddr >> 4) % 4; */
/*
* Trade-off: We couldn't add pin number to
* unum string because statistic number
* pumps up at the corresponding dimm not pin.
* (void) sprintf(unum, "Pin %1u ", (uint_t)
* pinp->pintable[pos_cacheline]);
*/
/*
* platform hook for adding label information to unum.
*/
idx4dimm);
} else {
char *p = unum;
/*
* multi-bit error handling, we can only identify
* bank of DIMMs.
*/
i == 0 ? "" : " ",
p += strlen(p);
}
/*
* platform hook for adding label information
* to unum.
*/
}
return (ENAMETOOLONG);
} else {
return (0);
}
} /* end of while loop for logical bank list */
return (ENXIO);
}
/* ARGSUSED */
static int
{
/*
* Scan all logical banks to get one responding to the physical
* address.
*/
/*
* The Address Decoding logic decodes the different fields
* in the Memory Address Decoding register to determine
* whether a particular logical bank should respond to a
* physical address.
*/
continue;
}
return (0);
}
return (ENXIO);
}
/*
* Translate a DIMM <id, offset> pair to a physical address.
*/
static int
{
int first_seg_id;
int i, found;
/*
* If DIMM serial ids have not been cached yet, tell the
* caller to try again.
*/
if (mc_dimm_sids == NULL) {
return (EAGAIN);
}
for (i = 0; i < max_entries; i++) {
break;
}
if (i == max_entries) {
return (ENODEV);
}
return (ENODEV);
}
found = 0;
for (i = 0; i < NDIMMS; i++) {
DIMM_SERIAL_ID_LEN) == 0)
break;
}
if (i == NDIMMS)
continue;
continue;
found = 1;
break;
}
if (found) {
return (0);
}
/*
* If a bank wasn't found, it may be in another segment.
* This can happen if the different logical banks of an MC
* have different interleave factors. To deal with this
* possibility, we'll do a brute-force search for banks
* for this MC with a different seg id then above.
*/
continue;
}
continue;
}
/* Ignore banks in the segment we looked in above. */
continue;
}
for (i = 0; i < NDIMMS; i++) {
DIMM_SERIAL_ID_LEN) == 0)
break;
}
if (i == NDIMMS) {
continue;
}
continue;
}
found = 1;
break;
}
if (found)
return (0);
else
return (ENOENT);
}
static int
{
return (EINVAL);
/*
* Scan all logical banks to get one responding to the physical
* address.
*/
/*
* The Address Decoding logic decodes the different fields
* in the Memory Address Decoding register to determine
* whether a particular logical bank should respond to a
* physical address.
*/
continue;
}
/*
* Get the corresponding segment.
*/
return (EFAULT);
}
return (0);
} /* end of while loop for logical bank list */
return (ENXIO);
}
/*
* Construct lists for an enabled MC where size of memory is 0.
* The lists are connected as follows:
* Attached MC -> device group list -> device list(per devgrp).
*/
static void
{
struct mctrl_info *mctrl;
struct device_info *dev;
/* allocate for mctrl_info and bank_info */
mctrl_head)) != NULL) {
return;
}
/*
* If dimminfop is NULL, the Memory Controller is disable, and
* the number of device group will be zero.
*/
return;
}
/* add the entry on dgrp_info list */
for (i = 0; i < NDGRPS; i++) {
!= NULL) {
idx);
continue;
}
/* add the entry on device_info list */
for (j = 0; j < NDIMMS; j++) {
if ((dev = (struct device_info *)
"exists\n", dmidx);
continue;
}
KM_SLEEP);
&device_tail);
} /* for loop for constructing device_info */
} /* end of for loop for constructing dgrp_info list */
}
/*
* Construct lists for Memory Configuration at logical viewpoint.
*
* Retrieve information from Memory Address Decoding Register and set up
* bank and segment lists. Link bank to its corresponding device group, and
* update size of device group and devices. Also connect bank to the segment.
*
* Memory Address Decoding Register
* -------------------------------------------------------------------------
* |63|62 53|52 41|40 37|36 20|19 18|17 14|13 12|11 8|7 0|
* |-----------|----------|------|---------|-----|------|-----|-----|-------|
* |V | - | UK | - | UM | - | LK | - | LM | - |
* -------------------------------------------------------------------------
*
*/
static int
{
int status = 0;
struct device_info *dev;
union {
struct {
} _s;
} mcreg;
/* add the entry on bank_info list */
!= NULL) {
goto exit;
}
goto exit;
}
/*
* size of a logical bank = size of segment / interleave factor
* This fomula is not only working for regular configuration,
* i.e. number of banks at a segment equals to the max
* interleave factor, but also for special case, say 3 bank
* interleave. One bank is 2 way interleave and other two are
* 4 way. So the sizes of banks are size of segment/2 and /4
* respectively.
*/
base <<= MADR_UPA_SHIFT;
/*
* The bank's position depends on which halves of the DIMMs it consists
* of. The front-side halves of the 4 DIMMs constitute the front bank
* and the back-side halves constitute the back bank. Bank numbers
* 0 and 1 are front-side banks and bank numbers 2 and 3 are back side
* banks.
*/
/*
* Workaround to keep gcc and SS12 lint happy.
* Lint expects lk, uk and um in the format statement below
* to use %lx, but this produces a warning when compiled with
* gcc.
*/
#if defined(lint)
"lk 0x%lx uk 0x%lx um 0x%lx ifactor 0x%x size 0x%lx base 0x%lx\n",
#else /* lint */
"lk 0x%x uk 0x%x um 0x%x ifactor 0x%x size 0x%lx base 0x%lx\n",
#endif /* lint */
/* connect the entry and update the size on dgrp_info list */
/* all avaiable dgrp should be linked at mc_construct */
status = -1;
goto exit;
}
/* Update the size of entry on device_info list */
for (i = 0; i < NDIMMS; i++) {
/* avaiable device should be linked at mc_construct */
device_head)) == NULL) {
dmidx);
status = -1;
goto exit;
}
}
/*
* Get the segment by matching the base address, link this bank
* to the segment. If not matched, allocate a new segment and
* add it at segment list.
*/
} else {
nsegments++;
}
/* Get the local id of bank which is only unique per segment. */
/* add bank at the end of the list; not sorted by bankid */
} else {
}
if (mc_dimm_sids) {
}
exit:
return (status);
}
/*
* Delete nodes related to the given MC on mc, device group, device,
* and bank lists. Moreover, delete corresponding segment if its connected
* banks are all removed.
*
* The "delete" argument is 1 if this is called as a result of DDI_DETACH. In
* this case, the DIMM data structures need to be deleted. The argument is
* 0 if this called as a result of DDI_SUSPEND/DDI_RESUME. In this case,
* the DIMM data structures are left alone.
*/
static void
{
struct mctrl_info *mctrl;
/* delete mctrl_info */
NULL) {
nmcs--;
/*
* There is no other list left for disabled MC.
*/
if (ndevgrps == 0) {
return;
}
} else
/* Delete device groups and devices of the detached MC */
for (i = 0; i < NDGRPS; i++) {
continue;
}
for (j = 0; j < NDIMMS; j++) {
&device_tail);
} else {
devid);
}
}
}
/* Delete banks and segments if it has no bank */
for (i = 0; i < NBANKS; i++) {
bank_head))) {
continue;
}
base <<= MADR_UPA_SHIFT;
/* Delete bank at segment and segment if no bank left */
&bank_tail);
continue;
}
/* update the bank list at the segment */
/* node is at the tail of list */
} else {
}
/* node is at the head of list */
} else {
}
&seg_tail);
nsegments--;
}
}
} /* end of for loop for four banks */
if (mc_dimm_sids && delete) {
i = mc_get_sid_cache_index(mc_id);
if (i >= 0) {
if (mc_dimm_sids[i].sids) {
}
}
}
}
/*
* Search the segment in the list starting at seg_head by base address
* input: base address
* return: pointer of found segment or null if not found.
*/
static struct seg_info *
{
break;
}
return (seg_ptr);
}
/*
* mc_dlist is a double linking list, including unique id, and pointers to
* next, and previous nodes. seg_info, bank_info, dgrp_info, device_info,
* and mctrl_info has it at the top to share the operations, add, del, and get.
*
* The new node is added at the tail and is not sorted.
*
* Input: The pointer of node to be added, head and tail of the list
*/
static void
{
} else {
}
}
/*
* Input: The pointer of node to be deleted, head and tail of the list
*
* Deleted node will be at the following positions
* 1. At the tail of the list
* 2. At the head of the list
* 3. At the head and tail of the list, i.e. only one left.
* 4. At the middle of the list
*/
static void
{
/* deleted node is at the tail of list */
} else {
}
/* deleted node is at the head of list */
} else {
}
}
/*
* Search the list from the head of the list to match the given id
* Input: id and the head of the list
* Return: pointer of found node
*/
static mc_dlist_t *
{
break;
}
return (node);
}
/*
* mc-us3 driver allows a platform to add extra label
* information to the unum string. If a platform implements a
* kernel function called plat_add_mem_unum_label() it will be
* executed. This would typically be implemented in the platmod.
*/
static void
{
if (&plat_add_mem_unum_label)
}
static int
{
int i;
for (i = 0; i < max_entries; i++) {
return (i);
}
return (-1);
}
static void
{
int i, j;
/*
* Mark the MC if DIMM sids are not available.
* Mark which segment the DIMMs belong to. Allocate
* space to store DIMM serial ids which are later
* provided by the platform layer, and update the bank_info
* structure with pointers to its serial ids.
*/
i = mc_get_sid_cache_index(mcid);
}
for (j = 0; j < NDIMMS; j++) {
}
}
static int
mc_populate_sid_cache(void)
{
if (&plat_populate_sid_cache == 0)
return (ENOTSUP);
continue;
}
}
/*
* Call to the platform layer to populate the cache
* with DIMM serial ids.
*/
}
static void
mc_init_sid_cache_thr(void)
{
(void) mc_populate_sid_cache();
}
static int
mc_init_sid_cache(void)
{
if (&plat_alloc_sid_cache) {
return (0);
} else
return (ENOTSUP);
}
static int
{
int i;
if (buflen < DIMM_SERIAL_ID_LEN)
return (ENOSPC);
/*
* If DIMM serial ids have not been cached yet, tell the
* caller to try again.
*/
return (EAGAIN);
if (mc_dimm_sids == NULL) {
return (EAGAIN);
}
/*
* Find dimm serial id using mcid and dimm #
*/
for (i = 0; i < max_entries; i++) {
break;
}
return (ENOENT);
}
return (0);
}