summaryrefslogtreecommitdiffstats
path: root/hdata
diff options
context:
space:
mode:
authorOliver O'Halloran <oohall@gmail.com>2019-02-19 18:53:39 +1100
committerStewart Smith <stewart@linux.ibm.com>2019-02-20 00:33:13 -0600
commit4822a7ba9d33468a15d1c90ee8c0823826d33320 (patch)
treef0e54964e6233cb71672d7940faae8f4ca264d60 /hdata
parentf35a3c376a44f5675d8802a9915950a033fba58d (diff)
downloadblackbird-skiboot-4822a7ba9d33468a15d1c90ee8c0823826d33320.tar.gz
blackbird-skiboot-4822a7ba9d33468a15d1c90ee8c0823826d33320.zip
hdata/memory: Add NVDIMM support
NVDIMMs are memory modules that use a battery backup system to allow the contents RAM to be saved to non-volatile storage if system power goes away unexpectedly. This allows them to be used a high-performance storage device, suitable for serving as a cache for SSDs and the like. Configuration of NVDIMMs is handled by hostboot and communicated to OPAL via the HDAT. We need to parse out the NVDIMM memory ranges and create memory regions with the "pmem-region" compatible label to make them available to the host. Signed-off-by: Oliver O'Halloran <oohall@gmail.com> Signed-off-by: Stewart Smith <stewart@linux.ibm.com>
Diffstat (limited to 'hdata')
-rw-r--r--hdata/memory.c110
1 files changed, 100 insertions, 10 deletions
diff --git a/hdata/memory.c b/hdata/memory.c
index 3e627144..1ac03c13 100644
--- a/hdata/memory.c
+++ b/hdata/memory.c
@@ -48,7 +48,25 @@ struct HDIF_ms_area_address_range {
__be32 mirror_attr;
__be64 mirror_start;
__be32 controller_id;
+ __be32 phys_attr;
} __packed;
+#define PHYS_ATTR_TYPE_MASK 0xff000000
+#define PHYS_ATTR_TYPE_STD 0
+#define PHYS_ATTR_TYPE_NVDIMM 1
+#define PHYS_ATTR_TYPE_MRAM 2
+#define PHYS_ATTR_TYPE_PCM 3
+
+#define PHYS_ATTR_STATUS_MASK 0x00ff0000
+/*
+ * The values here are mutually exclusive. I have no idea why anyone
+ * decided encoding these are flags rather than sequential numbers was
+ * a good idea, but here we are.
+ */
+#define PHYS_ATTR_STATUS_CANT_SAVE 0x01
+#define PHYS_ATTR_STATUS_SAVE_FAILED 0x02
+#define PHYS_ATTR_STATUS_SAVED 0x04
+#define PHYS_ATTR_STATUS_NOT_SAVED 0x08
+#define PHYS_ATTR_STATUS_MEM_INVALID 0xff
#define MS_CONTROLLER_MCBIST_ID(id) GETFIELD(PPC_BITMASK32(0, 1), id)
#define MS_CONTROLLER_MCS_ID(id) GETFIELD(PPC_BITMASK32(4, 7), id)
@@ -92,42 +110,99 @@ static void append_chip_id(struct dt_node *mem, u32 id)
p[len] = cpu_to_be32(id);
}
+static void update_status(struct dt_node *mem, uint32_t status)
+{
+ switch (status) {
+ case PHYS_ATTR_STATUS_CANT_SAVE:
+ if (!dt_find_property(mem, "save-trigged-unarmed"))
+ dt_add_property(mem, "save-trigger-unarmed", NULL, 0);
+ break;
+
+ case PHYS_ATTR_STATUS_SAVE_FAILED:
+ if (!dt_find_property(mem, "save-failed"))
+ dt_add_property(mem, "save-failed", NULL, 0);
+
+ break;
+
+ case PHYS_ATTR_STATUS_MEM_INVALID:
+ if (dt_find_property(mem, "save-trigged-unarmed"))
+ dt_add_property_string(mem, "status",
+ "disabled-memory-invalid");
+ break;
+ }
+}
+
static bool add_address_range(struct dt_node *root,
const struct HDIF_ms_area_id *id,
- const struct HDIF_ms_area_address_range *arange)
+ const struct HDIF_ms_area_address_range *arange,
+ uint32_t mem_type, uint32_t mem_status)
{
+ const char *compat = NULL, *dev_type = NULL, *name = NULL;
struct dt_node *mem;
- u32 chip_id, type;
+ u32 chip_id;
u64 reg[2];
chip_id = pcid_to_chip_id(be32_to_cpu(arange->chip));
prlog(PR_DEBUG, " Range: 0x%016llx..0x%016llx "
- "on Chip 0x%x mattr: 0x%x\n",
+ "on Chip 0x%x mattr: 0x%x pattr: 0x%x status:0x%x\n",
(long long)be64_to_cpu(arange->start),
(long long)be64_to_cpu(arange->end),
- chip_id, arange->mirror_attr);
+ chip_id, arange->mirror_attr, mem_type, mem_status);
/* reg contains start and length */
reg[0] = cleanup_addr(be64_to_cpu(arange->start));
reg[1] = cleanup_addr(be64_to_cpu(arange->end)) - reg[0];
+ switch (mem_type) {
+ case PHYS_ATTR_TYPE_STD:
+ name = "memory";
+ dev_type = "memory";
+ break;
+
+ case PHYS_ATTR_TYPE_NVDIMM:
+ case PHYS_ATTR_TYPE_MRAM:
+ case PHYS_ATTR_TYPE_PCM:
+ /* fall through */
+ name = "nvdimm";
+ compat = "pmem-region";
+ break;
+
+ /*
+ * Future memory types could be volatile or non-volatile. Bail if don't
+ * recognise the type so we don't end up trashing data accidently.
+ */
+ default:
+ return false;
+ }
+
if (be16_to_cpu(id->flags) & MS_AREA_SHARED) {
- mem = dt_find_by_name_addr("memory", reg[0]);
+ mem = dt_find_by_name_addr(dt_root, name, reg[0]);
if (mem) {
append_chip_id(mem, chip_id);
+ if (mem_type == PHYS_ATTR_TYPE_NVDIMM)
+ update_status(mem, mem_status);
return true;
}
}
mem = dt_new_addr(root, name, reg[0]);
- dt_add_property_string(mem, "device_type", "memory");
- dt_add_property_cells(mem, "ibm,chip-id", chip_id);
+ if (compat)
+ dt_add_property_string(mem, "compatible", compat);
+ if (dev_type)
+ dt_add_property_string(mem, "device_type", dev_type);
+
+ /* add in the nvdimm backup status flags */
+ if (mem_type == PHYS_ATTR_TYPE_NVDIMM)
+ update_status(mem, mem_status);
+
+ /* common properties */
+
dt_add_property_u64s(mem, "reg", reg[0], reg[1]);
+ dt_add_property_cells(mem, "ibm,chip-id", chip_id);
if (be16_to_cpu(id->flags) & MS_AREA_SHARED)
dt_add_property_cells(mem, DT_PRIVATE "share-id",
be16_to_cpu(id->share_id));
-
return true;
}
@@ -515,12 +590,27 @@ static void get_msareas(struct dt_node *root,
/* This offset is from the arr, not the header! */
arange = (void *)arr + be32_to_cpu(arr->offset);
for (j = 0; j < be32_to_cpu(arr->ecnt); j++) {
+ uint32_t type = 0, status = 0;
+
+ /*
+ * Check that the required fields are present in this
+ * version of the HDAT structure.
+ */
offset = offsetof(struct HDIF_ms_area_address_range, controller_id);
if (be32_to_cpu(arr->eactsz) >= offset)
add_memory_controller(msarea, arange);
- if (!add_address_range(root, id, arange))
- return;
+ offset = offsetof(struct HDIF_ms_area_address_range, phys_attr);
+ if (be32_to_cpu(arr->eactsz) >= offset) {
+ uint32_t attr = be32_to_cpu(arange->phys_attr);
+
+ type = GETFIELD(PHYS_ATTR_TYPE_MASK, attr);
+ status = GETFIELD(PHYS_ATTR_STATUS_MASK, attr);
+ }
+
+ if (add_address_range(root, id, arange, type, status))
+ prerror("Unable to use memory range %d from MSAREA %d\n", j, i);
+
arange = (void *)arange + be32_to_cpu(arr->esize);
}
}
OpenPOWER on IntegriCloud