xref: /openbmc/linux/arch/powerpc/mm/drmem.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
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