12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
26c6ea537SNathan Fontenot /*
36c6ea537SNathan Fontenot * Dynamic reconfiguration memory support
46c6ea537SNathan Fontenot *
56c6ea537SNathan Fontenot * Copyright 2017 IBM Corporation
66c6ea537SNathan Fontenot */
76c6ea537SNathan Fontenot
86c6ea537SNathan Fontenot #define pr_fmt(fmt) "drmem: " fmt
96c6ea537SNathan Fontenot
106c6ea537SNathan Fontenot #include <linux/kernel.h>
116c6ea537SNathan Fontenot #include <linux/of.h>
126c6ea537SNathan Fontenot #include <linux/of_fdt.h>
136c6ea537SNathan Fontenot #include <linux/memblock.h>
14*e6f6390aSChristophe Leroy #include <linux/slab.h>
156c6ea537SNathan Fontenot #include <asm/drmem.h>
166c6ea537SNathan Fontenot
17adfefc60SHari Bathini static int n_root_addr_cells, n_root_size_cells;
18adfefc60SHari Bathini
196c6ea537SNathan Fontenot static struct drmem_lmb_info __drmem_info;
206c6ea537SNathan Fontenot struct drmem_lmb_info *drmem_info = &__drmem_info;
21d144f4d5SLaurent Dufour static bool in_drmem_update;
226c6ea537SNathan Fontenot
drmem_lmb_memory_max(void)23514a9cb3SNathan Fontenot u64 drmem_lmb_memory_max(void)
24514a9cb3SNathan Fontenot {
25514a9cb3SNathan Fontenot struct drmem_lmb *last_lmb;
26514a9cb3SNathan Fontenot
27514a9cb3SNathan Fontenot last_lmb = &drmem_info->lmbs[drmem_info->n_lmbs - 1];
28514a9cb3SNathan Fontenot return last_lmb->base_addr + drmem_lmb_size();
29514a9cb3SNathan Fontenot }
30514a9cb3SNathan Fontenot
drmem_lmb_flags(struct drmem_lmb * lmb)316195a500SNathan Fontenot static u32 drmem_lmb_flags(struct drmem_lmb *lmb)
326195a500SNathan Fontenot {
336195a500SNathan Fontenot /*
346195a500SNathan Fontenot * Return the value of the lmb flags field minus the reserved
356195a500SNathan Fontenot * bit used internally for hotplug processing.
366195a500SNathan Fontenot */
376195a500SNathan Fontenot return lmb->flags & ~DRMEM_LMB_RESERVED;
386195a500SNathan Fontenot }
396195a500SNathan Fontenot
clone_property(struct property * prop,u32 prop_sz)406195a500SNathan Fontenot static struct property *clone_property(struct property *prop, u32 prop_sz)
416195a500SNathan Fontenot {
426195a500SNathan Fontenot struct property *new_prop;
436195a500SNathan Fontenot
446195a500SNathan Fontenot new_prop = kzalloc(sizeof(*new_prop), GFP_KERNEL);
456195a500SNathan Fontenot if (!new_prop)
466195a500SNathan Fontenot return NULL;
476195a500SNathan Fontenot
486195a500SNathan Fontenot new_prop->name = kstrdup(prop->name, GFP_KERNEL);
496195a500SNathan Fontenot new_prop->value = kzalloc(prop_sz, GFP_KERNEL);
506195a500SNathan Fontenot if (!new_prop->name || !new_prop->value) {
516195a500SNathan Fontenot kfree(new_prop->name);
526195a500SNathan Fontenot kfree(new_prop->value);
536195a500SNathan Fontenot kfree(new_prop);
546195a500SNathan Fontenot return NULL;
556195a500SNathan Fontenot }
566195a500SNathan Fontenot
576195a500SNathan Fontenot new_prop->length = prop_sz;
586195a500SNathan Fontenot #if defined(CONFIG_OF_DYNAMIC)
596195a500SNathan Fontenot of_property_set_flag(new_prop, OF_DYNAMIC);
606195a500SNathan Fontenot #endif
616195a500SNathan Fontenot return new_prop;
626195a500SNathan Fontenot }
636195a500SNathan Fontenot
drmem_update_dt_v1(struct device_node * memory,struct property * prop)646195a500SNathan Fontenot static int drmem_update_dt_v1(struct device_node *memory,
656195a500SNathan Fontenot struct property *prop)
666195a500SNathan Fontenot {
676195a500SNathan Fontenot struct property *new_prop;
682c777215SNathan Fontenot struct of_drconf_cell_v1 *dr_cell;
696195a500SNathan Fontenot struct drmem_lmb *lmb;
706195a500SNathan Fontenot u32 *p;
716195a500SNathan Fontenot
726195a500SNathan Fontenot new_prop = clone_property(prop, prop->length);
736195a500SNathan Fontenot if (!new_prop)
746195a500SNathan Fontenot return -1;
756195a500SNathan Fontenot
766195a500SNathan Fontenot p = new_prop->value;
776195a500SNathan Fontenot *p++ = cpu_to_be32(drmem_info->n_lmbs);
786195a500SNathan Fontenot
792c777215SNathan Fontenot dr_cell = (struct of_drconf_cell_v1 *)p;
806195a500SNathan Fontenot
816195a500SNathan Fontenot for_each_drmem_lmb(lmb) {
826195a500SNathan Fontenot dr_cell->base_addr = cpu_to_be64(lmb->base_addr);
836195a500SNathan Fontenot dr_cell->drc_index = cpu_to_be32(lmb->drc_index);
846195a500SNathan Fontenot dr_cell->aa_index = cpu_to_be32(lmb->aa_index);
856195a500SNathan Fontenot dr_cell->flags = cpu_to_be32(drmem_lmb_flags(lmb));
866195a500SNathan Fontenot
876195a500SNathan Fontenot dr_cell++;
886195a500SNathan Fontenot }
896195a500SNathan Fontenot
906195a500SNathan Fontenot of_update_property(memory, new_prop);
916195a500SNathan Fontenot return 0;
926195a500SNathan Fontenot }
936195a500SNathan Fontenot
init_drconf_v2_cell(struct of_drconf_cell_v2 * dr_cell,struct drmem_lmb * lmb)942b31e3aeSNathan Fontenot static void init_drconf_v2_cell(struct of_drconf_cell_v2 *dr_cell,
952b31e3aeSNathan Fontenot struct drmem_lmb *lmb)
962b31e3aeSNathan Fontenot {
972b31e3aeSNathan Fontenot dr_cell->base_addr = cpu_to_be64(lmb->base_addr);
982b31e3aeSNathan Fontenot dr_cell->drc_index = cpu_to_be32(lmb->drc_index);
992b31e3aeSNathan Fontenot dr_cell->aa_index = cpu_to_be32(lmb->aa_index);
1002f7d03e0SBharata B Rao dr_cell->flags = cpu_to_be32(drmem_lmb_flags(lmb));
1012b31e3aeSNathan Fontenot }
1022b31e3aeSNathan Fontenot
drmem_update_dt_v2(struct device_node * memory,struct property * prop)1032b31e3aeSNathan Fontenot static int drmem_update_dt_v2(struct device_node *memory,
1042b31e3aeSNathan Fontenot struct property *prop)
1052b31e3aeSNathan Fontenot {
1062b31e3aeSNathan Fontenot struct property *new_prop;
1072b31e3aeSNathan Fontenot struct of_drconf_cell_v2 *dr_cell;
1082b31e3aeSNathan Fontenot struct drmem_lmb *lmb, *prev_lmb;
1092b31e3aeSNathan Fontenot u32 lmb_sets, prop_sz, seq_lmbs;
1102b31e3aeSNathan Fontenot u32 *p;
1112b31e3aeSNathan Fontenot
1122b31e3aeSNathan Fontenot /* First pass, determine how many LMB sets are needed. */
1132b31e3aeSNathan Fontenot lmb_sets = 0;
1142b31e3aeSNathan Fontenot prev_lmb = NULL;
1152b31e3aeSNathan Fontenot for_each_drmem_lmb(lmb) {
1162b31e3aeSNathan Fontenot if (!prev_lmb) {
1172b31e3aeSNathan Fontenot prev_lmb = lmb;
1182b31e3aeSNathan Fontenot lmb_sets++;
1192b31e3aeSNathan Fontenot continue;
1202b31e3aeSNathan Fontenot }
1212b31e3aeSNathan Fontenot
1222b31e3aeSNathan Fontenot if (prev_lmb->aa_index != lmb->aa_index ||
1232f7d03e0SBharata B Rao drmem_lmb_flags(prev_lmb) != drmem_lmb_flags(lmb))
1242b31e3aeSNathan Fontenot lmb_sets++;
1252b31e3aeSNathan Fontenot
1262b31e3aeSNathan Fontenot prev_lmb = lmb;
1272b31e3aeSNathan Fontenot }
1282b31e3aeSNathan Fontenot
1292b31e3aeSNathan Fontenot prop_sz = lmb_sets * sizeof(*dr_cell) + sizeof(__be32);
1302b31e3aeSNathan Fontenot new_prop = clone_property(prop, prop_sz);
1312b31e3aeSNathan Fontenot if (!new_prop)
1322b31e3aeSNathan Fontenot return -1;
1332b31e3aeSNathan Fontenot
1342b31e3aeSNathan Fontenot p = new_prop->value;
1352b31e3aeSNathan Fontenot *p++ = cpu_to_be32(lmb_sets);
1362b31e3aeSNathan Fontenot
1372b31e3aeSNathan Fontenot dr_cell = (struct of_drconf_cell_v2 *)p;
1382b31e3aeSNathan Fontenot
1392b31e3aeSNathan Fontenot /* Second pass, populate the LMB set data */
1402b31e3aeSNathan Fontenot prev_lmb = NULL;
1412b31e3aeSNathan Fontenot seq_lmbs = 0;
1422b31e3aeSNathan Fontenot for_each_drmem_lmb(lmb) {
1432b31e3aeSNathan Fontenot if (prev_lmb == NULL) {
1442b31e3aeSNathan Fontenot /* Start of first LMB set */
1452b31e3aeSNathan Fontenot prev_lmb = lmb;
1462b31e3aeSNathan Fontenot init_drconf_v2_cell(dr_cell, lmb);
1472b31e3aeSNathan Fontenot seq_lmbs++;
1482b31e3aeSNathan Fontenot continue;
1492b31e3aeSNathan Fontenot }
1502b31e3aeSNathan Fontenot
1512b31e3aeSNathan Fontenot if (prev_lmb->aa_index != lmb->aa_index ||
1522f7d03e0SBharata B Rao drmem_lmb_flags(prev_lmb) != drmem_lmb_flags(lmb)) {
1532b31e3aeSNathan Fontenot /* end of one set, start of another */
1542b31e3aeSNathan Fontenot dr_cell->seq_lmbs = cpu_to_be32(seq_lmbs);
1552b31e3aeSNathan Fontenot dr_cell++;
1562b31e3aeSNathan Fontenot
1572b31e3aeSNathan Fontenot init_drconf_v2_cell(dr_cell, lmb);
1582b31e3aeSNathan Fontenot seq_lmbs = 1;
1592b31e3aeSNathan Fontenot } else {
1602b31e3aeSNathan Fontenot seq_lmbs++;
1612b31e3aeSNathan Fontenot }
1622b31e3aeSNathan Fontenot
1632b31e3aeSNathan Fontenot prev_lmb = lmb;
1642b31e3aeSNathan Fontenot }
1652b31e3aeSNathan Fontenot
1662b31e3aeSNathan Fontenot /* close out last LMB set */
1672b31e3aeSNathan Fontenot dr_cell->seq_lmbs = cpu_to_be32(seq_lmbs);
1682b31e3aeSNathan Fontenot of_update_property(memory, new_prop);
1692b31e3aeSNathan Fontenot return 0;
1702b31e3aeSNathan Fontenot }
1712b31e3aeSNathan Fontenot
drmem_update_dt(void)1726195a500SNathan Fontenot int drmem_update_dt(void)
1736195a500SNathan Fontenot {
1746195a500SNathan Fontenot struct device_node *memory;
1756195a500SNathan Fontenot struct property *prop;
1766195a500SNathan Fontenot int rc = -1;
1776195a500SNathan Fontenot
1786195a500SNathan Fontenot memory = of_find_node_by_path("/ibm,dynamic-reconfiguration-memory");
1796195a500SNathan Fontenot if (!memory)
1806195a500SNathan Fontenot return -1;
1816195a500SNathan Fontenot
182d144f4d5SLaurent Dufour /*
183d144f4d5SLaurent Dufour * Set in_drmem_update to prevent the notifier callback to process the
184d144f4d5SLaurent Dufour * DT property back since the change is coming from the LMB tree.
185d144f4d5SLaurent Dufour */
186d144f4d5SLaurent Dufour in_drmem_update = true;
1876195a500SNathan Fontenot prop = of_find_property(memory, "ibm,dynamic-memory", NULL);
1882b31e3aeSNathan Fontenot if (prop) {
1896195a500SNathan Fontenot rc = drmem_update_dt_v1(memory, prop);
1902b31e3aeSNathan Fontenot } else {
1912b31e3aeSNathan Fontenot prop = of_find_property(memory, "ibm,dynamic-memory-v2", NULL);
1922b31e3aeSNathan Fontenot if (prop)
1932b31e3aeSNathan Fontenot rc = drmem_update_dt_v2(memory, prop);
1942b31e3aeSNathan Fontenot }
195d144f4d5SLaurent Dufour in_drmem_update = false;
1966195a500SNathan Fontenot
1976195a500SNathan Fontenot of_node_put(memory);
1986195a500SNathan Fontenot return rc;
1996195a500SNathan Fontenot }
2006195a500SNathan Fontenot
read_drconf_v1_cell(struct drmem_lmb * lmb,const __be32 ** prop)201adfefc60SHari Bathini static void read_drconf_v1_cell(struct drmem_lmb *lmb,
2026c6ea537SNathan Fontenot const __be32 **prop)
2036c6ea537SNathan Fontenot {
2046c6ea537SNathan Fontenot const __be32 *p = *prop;
2056c6ea537SNathan Fontenot
206adfefc60SHari Bathini lmb->base_addr = of_read_number(p, n_root_addr_cells);
207adfefc60SHari Bathini p += n_root_addr_cells;
2086c6ea537SNathan Fontenot lmb->drc_index = of_read_number(p++, 1);
2096c6ea537SNathan Fontenot
2106c6ea537SNathan Fontenot p++; /* skip reserved field */
2116c6ea537SNathan Fontenot
2126c6ea537SNathan Fontenot lmb->aa_index = of_read_number(p++, 1);
2136c6ea537SNathan Fontenot lmb->flags = of_read_number(p++, 1);
2146c6ea537SNathan Fontenot
2156c6ea537SNathan Fontenot *prop = p;
2166c6ea537SNathan Fontenot }
2176c6ea537SNathan Fontenot
218adfefc60SHari Bathini static int
__walk_drmem_v1_lmbs(const __be32 * prop,const __be32 * usm,void * data,int (* func)(struct drmem_lmb *,const __be32 **,void *))219adfefc60SHari Bathini __walk_drmem_v1_lmbs(const __be32 *prop, const __be32 *usm, void *data,
220adfefc60SHari Bathini int (*func)(struct drmem_lmb *, const __be32 **, void *))
2216c6ea537SNathan Fontenot {
2226c6ea537SNathan Fontenot struct drmem_lmb lmb;
2236c6ea537SNathan Fontenot u32 i, n_lmbs;
224adfefc60SHari Bathini int ret = 0;
2256c6ea537SNathan Fontenot
2266c6ea537SNathan Fontenot n_lmbs = of_read_number(prop++, 1);
2276c6ea537SNathan Fontenot for (i = 0; i < n_lmbs; i++) {
2286c6ea537SNathan Fontenot read_drconf_v1_cell(&lmb, &prop);
229adfefc60SHari Bathini ret = func(&lmb, &usm, data);
230adfefc60SHari Bathini if (ret)
231adfefc60SHari Bathini break;
2326c6ea537SNathan Fontenot }
2336c6ea537SNathan Fontenot
234adfefc60SHari Bathini return ret;
235adfefc60SHari Bathini }
236adfefc60SHari Bathini
read_drconf_v2_cell(struct of_drconf_cell_v2 * dr_cell,const __be32 ** prop)237adfefc60SHari Bathini static void read_drconf_v2_cell(struct of_drconf_cell_v2 *dr_cell,
2382b31e3aeSNathan Fontenot const __be32 **prop)
2392b31e3aeSNathan Fontenot {
2402b31e3aeSNathan Fontenot const __be32 *p = *prop;
2412b31e3aeSNathan Fontenot
2422b31e3aeSNathan Fontenot dr_cell->seq_lmbs = of_read_number(p++, 1);
243adfefc60SHari Bathini dr_cell->base_addr = of_read_number(p, n_root_addr_cells);
244adfefc60SHari Bathini p += n_root_addr_cells;
2452b31e3aeSNathan Fontenot dr_cell->drc_index = of_read_number(p++, 1);
2462b31e3aeSNathan Fontenot dr_cell->aa_index = of_read_number(p++, 1);
2472b31e3aeSNathan Fontenot dr_cell->flags = of_read_number(p++, 1);
2482b31e3aeSNathan Fontenot
2492b31e3aeSNathan Fontenot *prop = p;
2502b31e3aeSNathan Fontenot }
2512b31e3aeSNathan Fontenot
252adfefc60SHari Bathini static int
__walk_drmem_v2_lmbs(const __be32 * prop,const __be32 * usm,void * data,int (* func)(struct drmem_lmb *,const __be32 **,void *))253adfefc60SHari Bathini __walk_drmem_v2_lmbs(const __be32 *prop, const __be32 *usm, void *data,
254adfefc60SHari Bathini int (*func)(struct drmem_lmb *, const __be32 **, void *))
2552b31e3aeSNathan Fontenot {
2562b31e3aeSNathan Fontenot struct of_drconf_cell_v2 dr_cell;
2572b31e3aeSNathan Fontenot struct drmem_lmb lmb;
2582b31e3aeSNathan Fontenot u32 i, j, lmb_sets;
259adfefc60SHari Bathini int ret = 0;
2602b31e3aeSNathan Fontenot
2612b31e3aeSNathan Fontenot lmb_sets = of_read_number(prop++, 1);
2622b31e3aeSNathan Fontenot for (i = 0; i < lmb_sets; i++) {
2632b31e3aeSNathan Fontenot read_drconf_v2_cell(&dr_cell, &prop);
2642b31e3aeSNathan Fontenot
2652b31e3aeSNathan Fontenot for (j = 0; j < dr_cell.seq_lmbs; j++) {
2662b31e3aeSNathan Fontenot lmb.base_addr = dr_cell.base_addr;
2672b31e3aeSNathan Fontenot dr_cell.base_addr += drmem_lmb_size();
2682b31e3aeSNathan Fontenot
2692b31e3aeSNathan Fontenot lmb.drc_index = dr_cell.drc_index;
2702b31e3aeSNathan Fontenot dr_cell.drc_index++;
2712b31e3aeSNathan Fontenot
2722b31e3aeSNathan Fontenot lmb.aa_index = dr_cell.aa_index;
2732b31e3aeSNathan Fontenot lmb.flags = dr_cell.flags;
2742b31e3aeSNathan Fontenot
275adfefc60SHari Bathini ret = func(&lmb, &usm, data);
276adfefc60SHari Bathini if (ret)
277adfefc60SHari Bathini break;
2782b31e3aeSNathan Fontenot }
2792b31e3aeSNathan Fontenot }
280adfefc60SHari Bathini
281adfefc60SHari Bathini return ret;
2822b31e3aeSNathan Fontenot }
2832b31e3aeSNathan Fontenot
284514a9cb3SNathan Fontenot #ifdef CONFIG_PPC_PSERIES
walk_drmem_lmbs_early(unsigned long node,void * data,int (* func)(struct drmem_lmb *,const __be32 **,void *))285adfefc60SHari Bathini int __init walk_drmem_lmbs_early(unsigned long node, void *data,
286adfefc60SHari Bathini int (*func)(struct drmem_lmb *, const __be32 **, void *))
2876c6ea537SNathan Fontenot {
2886c6ea537SNathan Fontenot const __be32 *prop, *usm;
289adfefc60SHari Bathini int len, ret = -ENODEV;
2906c6ea537SNathan Fontenot
2916c6ea537SNathan Fontenot prop = of_get_flat_dt_prop(node, "ibm,lmb-size", &len);
2926c6ea537SNathan Fontenot if (!prop || len < dt_root_size_cells * sizeof(__be32))
293adfefc60SHari Bathini return ret;
294adfefc60SHari Bathini
295adfefc60SHari Bathini /* Get the address & size cells */
296adfefc60SHari Bathini n_root_addr_cells = dt_root_addr_cells;
297adfefc60SHari Bathini n_root_size_cells = dt_root_size_cells;
2986c6ea537SNathan Fontenot
2996c6ea537SNathan Fontenot drmem_info->lmb_size = dt_mem_next_cell(dt_root_size_cells, &prop);
3006c6ea537SNathan Fontenot
3016c6ea537SNathan Fontenot usm = of_get_flat_dt_prop(node, "linux,drconf-usable-memory", &len);
3026c6ea537SNathan Fontenot
3036c6ea537SNathan Fontenot prop = of_get_flat_dt_prop(node, "ibm,dynamic-memory", &len);
3042b31e3aeSNathan Fontenot if (prop) {
305adfefc60SHari Bathini ret = __walk_drmem_v1_lmbs(prop, usm, data, func);
3062b31e3aeSNathan Fontenot } else {
3072b31e3aeSNathan Fontenot prop = of_get_flat_dt_prop(node, "ibm,dynamic-memory-v2",
3082b31e3aeSNathan Fontenot &len);
3092b31e3aeSNathan Fontenot if (prop)
310adfefc60SHari Bathini ret = __walk_drmem_v2_lmbs(prop, usm, data, func);
3112b31e3aeSNathan Fontenot }
3126c6ea537SNathan Fontenot
3136c6ea537SNathan Fontenot memblock_dump_all();
314adfefc60SHari Bathini return ret;
3156c6ea537SNathan Fontenot }
3166c6ea537SNathan Fontenot
317d144f4d5SLaurent Dufour /*
318d144f4d5SLaurent Dufour * Update the LMB associativity index.
319d144f4d5SLaurent Dufour */
update_lmb(struct drmem_lmb * updated_lmb,__maybe_unused const __be32 ** usm,__maybe_unused void * data)320d144f4d5SLaurent Dufour static int update_lmb(struct drmem_lmb *updated_lmb,
321d144f4d5SLaurent Dufour __maybe_unused const __be32 **usm,
322d144f4d5SLaurent Dufour __maybe_unused void *data)
323d144f4d5SLaurent Dufour {
324d144f4d5SLaurent Dufour struct drmem_lmb *lmb;
325d144f4d5SLaurent Dufour
326d144f4d5SLaurent Dufour for_each_drmem_lmb(lmb) {
327d144f4d5SLaurent Dufour if (lmb->drc_index != updated_lmb->drc_index)
328d144f4d5SLaurent Dufour continue;
329d144f4d5SLaurent Dufour
330d144f4d5SLaurent Dufour lmb->aa_index = updated_lmb->aa_index;
331d144f4d5SLaurent Dufour break;
332d144f4d5SLaurent Dufour }
333d144f4d5SLaurent Dufour return 0;
334d144f4d5SLaurent Dufour }
335d144f4d5SLaurent Dufour
336d144f4d5SLaurent Dufour /*
337d144f4d5SLaurent Dufour * Update the LMB associativity index.
338d144f4d5SLaurent Dufour *
339d144f4d5SLaurent Dufour * This needs to be called when the hypervisor is updating the
340d144f4d5SLaurent Dufour * dynamic-reconfiguration-memory node property.
341d144f4d5SLaurent Dufour */
drmem_update_lmbs(struct property * prop)342d144f4d5SLaurent Dufour void drmem_update_lmbs(struct property *prop)
343d144f4d5SLaurent Dufour {
344d144f4d5SLaurent Dufour /*
345d144f4d5SLaurent Dufour * Don't update the LMBs if triggered by the update done in
346d144f4d5SLaurent Dufour * drmem_update_dt(), the LMB values have been used to the update the DT
347d144f4d5SLaurent Dufour * property in that case.
348d144f4d5SLaurent Dufour */
349d144f4d5SLaurent Dufour if (in_drmem_update)
350d144f4d5SLaurent Dufour return;
351d144f4d5SLaurent Dufour if (!strcmp(prop->name, "ibm,dynamic-memory"))
352d144f4d5SLaurent Dufour __walk_drmem_v1_lmbs(prop->value, NULL, NULL, update_lmb);
353d144f4d5SLaurent Dufour else if (!strcmp(prop->name, "ibm,dynamic-memory-v2"))
354d144f4d5SLaurent Dufour __walk_drmem_v2_lmbs(prop->value, NULL, NULL, update_lmb);
355d144f4d5SLaurent Dufour }
3566c6ea537SNathan Fontenot #endif
357514a9cb3SNathan Fontenot
init_drmem_lmb_size(struct device_node * dn)358adfefc60SHari Bathini static int init_drmem_lmb_size(struct device_node *dn)
359514a9cb3SNathan Fontenot {
360514a9cb3SNathan Fontenot const __be32 *prop;
361514a9cb3SNathan Fontenot int len;
362514a9cb3SNathan Fontenot
363514a9cb3SNathan Fontenot if (drmem_info->lmb_size)
364514a9cb3SNathan Fontenot return 0;
365514a9cb3SNathan Fontenot
366514a9cb3SNathan Fontenot prop = of_get_property(dn, "ibm,lmb-size", &len);
367adfefc60SHari Bathini if (!prop || len < n_root_size_cells * sizeof(__be32)) {
368514a9cb3SNathan Fontenot pr_info("Could not determine LMB size\n");
369514a9cb3SNathan Fontenot return -1;
370514a9cb3SNathan Fontenot }
371514a9cb3SNathan Fontenot
372adfefc60SHari Bathini drmem_info->lmb_size = of_read_number(prop, n_root_size_cells);
373514a9cb3SNathan Fontenot return 0;
374514a9cb3SNathan Fontenot }
375514a9cb3SNathan Fontenot
376514a9cb3SNathan Fontenot /*
377514a9cb3SNathan Fontenot * Returns the property linux,drconf-usable-memory if
378514a9cb3SNathan Fontenot * it exists (the property exists only in kexec/kdump kernels,
379514a9cb3SNathan Fontenot * added by kexec-tools)
380514a9cb3SNathan Fontenot */
of_get_usable_memory(struct device_node * dn)381514a9cb3SNathan Fontenot static const __be32 *of_get_usable_memory(struct device_node *dn)
382514a9cb3SNathan Fontenot {
383514a9cb3SNathan Fontenot const __be32 *prop;
384514a9cb3SNathan Fontenot u32 len;
385514a9cb3SNathan Fontenot
386514a9cb3SNathan Fontenot prop = of_get_property(dn, "linux,drconf-usable-memory", &len);
387514a9cb3SNathan Fontenot if (!prop || len < sizeof(unsigned int))
388514a9cb3SNathan Fontenot return NULL;
389514a9cb3SNathan Fontenot
390514a9cb3SNathan Fontenot return prop;
391514a9cb3SNathan Fontenot }
392514a9cb3SNathan Fontenot
walk_drmem_lmbs(struct device_node * dn,void * data,int (* func)(struct drmem_lmb *,const __be32 **,void *))393adfefc60SHari Bathini int walk_drmem_lmbs(struct device_node *dn, void *data,
394adfefc60SHari Bathini int (*func)(struct drmem_lmb *, const __be32 **, void *))
395514a9cb3SNathan Fontenot {
396514a9cb3SNathan Fontenot const __be32 *prop, *usm;
397adfefc60SHari Bathini int ret = -ENODEV;
398adfefc60SHari Bathini
399adfefc60SHari Bathini if (!of_root)
400adfefc60SHari Bathini return ret;
401adfefc60SHari Bathini
402adfefc60SHari Bathini /* Get the address & size cells */
403adfefc60SHari Bathini of_node_get(of_root);
404adfefc60SHari Bathini n_root_addr_cells = of_n_addr_cells(of_root);
405adfefc60SHari Bathini n_root_size_cells = of_n_size_cells(of_root);
406adfefc60SHari Bathini of_node_put(of_root);
407514a9cb3SNathan Fontenot
408514a9cb3SNathan Fontenot if (init_drmem_lmb_size(dn))
409adfefc60SHari Bathini return ret;
410514a9cb3SNathan Fontenot
411514a9cb3SNathan Fontenot usm = of_get_usable_memory(dn);
412514a9cb3SNathan Fontenot
413514a9cb3SNathan Fontenot prop = of_get_property(dn, "ibm,dynamic-memory", NULL);
4142b31e3aeSNathan Fontenot if (prop) {
415adfefc60SHari Bathini ret = __walk_drmem_v1_lmbs(prop, usm, data, func);
4162b31e3aeSNathan Fontenot } else {
4172b31e3aeSNathan Fontenot prop = of_get_property(dn, "ibm,dynamic-memory-v2", NULL);
4182b31e3aeSNathan Fontenot if (prop)
419adfefc60SHari Bathini ret = __walk_drmem_v2_lmbs(prop, usm, data, func);
4202b31e3aeSNathan Fontenot }
421adfefc60SHari Bathini
422adfefc60SHari Bathini return ret;
423514a9cb3SNathan Fontenot }
424514a9cb3SNathan Fontenot
init_drmem_v1_lmbs(const __be32 * prop)425514a9cb3SNathan Fontenot static void __init init_drmem_v1_lmbs(const __be32 *prop)
426514a9cb3SNathan Fontenot {
427514a9cb3SNathan Fontenot struct drmem_lmb *lmb;
428514a9cb3SNathan Fontenot
429514a9cb3SNathan Fontenot drmem_info->n_lmbs = of_read_number(prop++, 1);
4302c10636aSNathan Fontenot if (drmem_info->n_lmbs == 0)
4312c10636aSNathan Fontenot return;
432514a9cb3SNathan Fontenot
433514a9cb3SNathan Fontenot drmem_info->lmbs = kcalloc(drmem_info->n_lmbs, sizeof(*lmb),
434514a9cb3SNathan Fontenot GFP_KERNEL);
435514a9cb3SNathan Fontenot if (!drmem_info->lmbs)
436514a9cb3SNathan Fontenot return;
437514a9cb3SNathan Fontenot
438e5e179aaSScott Cheloha for_each_drmem_lmb(lmb)
439514a9cb3SNathan Fontenot read_drconf_v1_cell(lmb, &prop);
440514a9cb3SNathan Fontenot }
441514a9cb3SNathan Fontenot
init_drmem_v2_lmbs(const __be32 * prop)4422b31e3aeSNathan Fontenot static void __init init_drmem_v2_lmbs(const __be32 *prop)
4432b31e3aeSNathan Fontenot {
4442b31e3aeSNathan Fontenot struct drmem_lmb *lmb;
4452b31e3aeSNathan Fontenot struct of_drconf_cell_v2 dr_cell;
4462b31e3aeSNathan Fontenot const __be32 *p;
4472b31e3aeSNathan Fontenot u32 i, j, lmb_sets;
4482b31e3aeSNathan Fontenot int lmb_index;
4492b31e3aeSNathan Fontenot
4502b31e3aeSNathan Fontenot lmb_sets = of_read_number(prop++, 1);
4512c10636aSNathan Fontenot if (lmb_sets == 0)
4522c10636aSNathan Fontenot return;
4532b31e3aeSNathan Fontenot
4542b31e3aeSNathan Fontenot /* first pass, calculate the number of LMBs */
4552b31e3aeSNathan Fontenot p = prop;
4562b31e3aeSNathan Fontenot for (i = 0; i < lmb_sets; i++) {
4572b31e3aeSNathan Fontenot read_drconf_v2_cell(&dr_cell, &p);
4582b31e3aeSNathan Fontenot drmem_info->n_lmbs += dr_cell.seq_lmbs;
4592b31e3aeSNathan Fontenot }
4602b31e3aeSNathan Fontenot
4612b31e3aeSNathan Fontenot drmem_info->lmbs = kcalloc(drmem_info->n_lmbs, sizeof(*lmb),
4622b31e3aeSNathan Fontenot GFP_KERNEL);
4632b31e3aeSNathan Fontenot if (!drmem_info->lmbs)
4642b31e3aeSNathan Fontenot return;
4652b31e3aeSNathan Fontenot
4662b31e3aeSNathan Fontenot /* second pass, read in the LMB information */
4672b31e3aeSNathan Fontenot lmb_index = 0;
4682b31e3aeSNathan Fontenot p = prop;
4692b31e3aeSNathan Fontenot
4702b31e3aeSNathan Fontenot for (i = 0; i < lmb_sets; i++) {
4712b31e3aeSNathan Fontenot read_drconf_v2_cell(&dr_cell, &p);
4722b31e3aeSNathan Fontenot
4732b31e3aeSNathan Fontenot for (j = 0; j < dr_cell.seq_lmbs; j++) {
4742b31e3aeSNathan Fontenot lmb = &drmem_info->lmbs[lmb_index++];
4752b31e3aeSNathan Fontenot
4762b31e3aeSNathan Fontenot lmb->base_addr = dr_cell.base_addr;
4772b31e3aeSNathan Fontenot dr_cell.base_addr += drmem_info->lmb_size;
4782b31e3aeSNathan Fontenot
4792b31e3aeSNathan Fontenot lmb->drc_index = dr_cell.drc_index;
4802b31e3aeSNathan Fontenot dr_cell.drc_index++;
4812b31e3aeSNathan Fontenot
4822b31e3aeSNathan Fontenot lmb->aa_index = dr_cell.aa_index;
4832b31e3aeSNathan Fontenot lmb->flags = dr_cell.flags;
4842b31e3aeSNathan Fontenot }
4852b31e3aeSNathan Fontenot }
4862b31e3aeSNathan Fontenot }
4872b31e3aeSNathan Fontenot
drmem_init(void)488514a9cb3SNathan Fontenot static int __init drmem_init(void)
489514a9cb3SNathan Fontenot {
490514a9cb3SNathan Fontenot struct device_node *dn;
491514a9cb3SNathan Fontenot const __be32 *prop;
492514a9cb3SNathan Fontenot
493514a9cb3SNathan Fontenot dn = of_find_node_by_path("/ibm,dynamic-reconfiguration-memory");
494514a9cb3SNathan Fontenot if (!dn) {
495514a9cb3SNathan Fontenot pr_info("No dynamic reconfiguration memory found\n");
496514a9cb3SNathan Fontenot return 0;
497514a9cb3SNathan Fontenot }
498514a9cb3SNathan Fontenot
499514a9cb3SNathan Fontenot if (init_drmem_lmb_size(dn)) {
500514a9cb3SNathan Fontenot of_node_put(dn);
501514a9cb3SNathan Fontenot return 0;
502514a9cb3SNathan Fontenot }
503514a9cb3SNathan Fontenot
504514a9cb3SNathan Fontenot prop = of_get_property(dn, "ibm,dynamic-memory", NULL);
5052b31e3aeSNathan Fontenot if (prop) {
506514a9cb3SNathan Fontenot init_drmem_v1_lmbs(prop);
5072b31e3aeSNathan Fontenot } else {
5082b31e3aeSNathan Fontenot prop = of_get_property(dn, "ibm,dynamic-memory-v2", NULL);
5092b31e3aeSNathan Fontenot if (prop)
5102b31e3aeSNathan Fontenot init_drmem_v2_lmbs(prop);
5112b31e3aeSNathan Fontenot }
512514a9cb3SNathan Fontenot
513514a9cb3SNathan Fontenot of_node_put(dn);
514514a9cb3SNathan Fontenot return 0;
515514a9cb3SNathan Fontenot }
516514a9cb3SNathan Fontenot late_initcall(drmem_init);
517