18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Dynamic reconfiguration memory support
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright 2017 IBM Corporation
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "drmem: " fmt
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/kernel.h>
118c2ecf20Sopenharmony_ci#include <linux/of.h>
128c2ecf20Sopenharmony_ci#include <linux/of_fdt.h>
138c2ecf20Sopenharmony_ci#include <linux/memblock.h>
148c2ecf20Sopenharmony_ci#include <asm/prom.h>
158c2ecf20Sopenharmony_ci#include <asm/drmem.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_cistatic int n_root_addr_cells, n_root_size_cells;
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_cistatic struct drmem_lmb_info __drmem_info;
208c2ecf20Sopenharmony_cistruct drmem_lmb_info *drmem_info = &__drmem_info;
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ciu64 drmem_lmb_memory_max(void)
238c2ecf20Sopenharmony_ci{
248c2ecf20Sopenharmony_ci	struct drmem_lmb *last_lmb;
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci	last_lmb = &drmem_info->lmbs[drmem_info->n_lmbs - 1];
278c2ecf20Sopenharmony_ci	return last_lmb->base_addr + drmem_lmb_size();
288c2ecf20Sopenharmony_ci}
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_cistatic u32 drmem_lmb_flags(struct drmem_lmb *lmb)
318c2ecf20Sopenharmony_ci{
328c2ecf20Sopenharmony_ci	/*
338c2ecf20Sopenharmony_ci	 * Return the value of the lmb flags field minus the reserved
348c2ecf20Sopenharmony_ci	 * bit used internally for hotplug processing.
358c2ecf20Sopenharmony_ci	 */
368c2ecf20Sopenharmony_ci	return lmb->flags & ~DRMEM_LMB_RESERVED;
378c2ecf20Sopenharmony_ci}
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cistatic struct property *clone_property(struct property *prop, u32 prop_sz)
408c2ecf20Sopenharmony_ci{
418c2ecf20Sopenharmony_ci	struct property *new_prop;
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	new_prop = kzalloc(sizeof(*new_prop), GFP_KERNEL);
448c2ecf20Sopenharmony_ci	if (!new_prop)
458c2ecf20Sopenharmony_ci		return NULL;
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	new_prop->name = kstrdup(prop->name, GFP_KERNEL);
488c2ecf20Sopenharmony_ci	new_prop->value = kzalloc(prop_sz, GFP_KERNEL);
498c2ecf20Sopenharmony_ci	if (!new_prop->name || !new_prop->value) {
508c2ecf20Sopenharmony_ci		kfree(new_prop->name);
518c2ecf20Sopenharmony_ci		kfree(new_prop->value);
528c2ecf20Sopenharmony_ci		kfree(new_prop);
538c2ecf20Sopenharmony_ci		return NULL;
548c2ecf20Sopenharmony_ci	}
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	new_prop->length = prop_sz;
578c2ecf20Sopenharmony_ci#if defined(CONFIG_OF_DYNAMIC)
588c2ecf20Sopenharmony_ci	of_property_set_flag(new_prop, OF_DYNAMIC);
598c2ecf20Sopenharmony_ci#endif
608c2ecf20Sopenharmony_ci	return new_prop;
618c2ecf20Sopenharmony_ci}
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_cistatic int drmem_update_dt_v1(struct device_node *memory,
648c2ecf20Sopenharmony_ci			      struct property *prop)
658c2ecf20Sopenharmony_ci{
668c2ecf20Sopenharmony_ci	struct property *new_prop;
678c2ecf20Sopenharmony_ci	struct of_drconf_cell_v1 *dr_cell;
688c2ecf20Sopenharmony_ci	struct drmem_lmb *lmb;
698c2ecf20Sopenharmony_ci	u32 *p;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	new_prop = clone_property(prop, prop->length);
728c2ecf20Sopenharmony_ci	if (!new_prop)
738c2ecf20Sopenharmony_ci		return -1;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	p = new_prop->value;
768c2ecf20Sopenharmony_ci	*p++ = cpu_to_be32(drmem_info->n_lmbs);
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	dr_cell = (struct of_drconf_cell_v1 *)p;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	for_each_drmem_lmb(lmb) {
818c2ecf20Sopenharmony_ci		dr_cell->base_addr = cpu_to_be64(lmb->base_addr);
828c2ecf20Sopenharmony_ci		dr_cell->drc_index = cpu_to_be32(lmb->drc_index);
838c2ecf20Sopenharmony_ci		dr_cell->aa_index = cpu_to_be32(lmb->aa_index);
848c2ecf20Sopenharmony_ci		dr_cell->flags = cpu_to_be32(drmem_lmb_flags(lmb));
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci		dr_cell++;
878c2ecf20Sopenharmony_ci	}
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	of_update_property(memory, new_prop);
908c2ecf20Sopenharmony_ci	return 0;
918c2ecf20Sopenharmony_ci}
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_cistatic void init_drconf_v2_cell(struct of_drconf_cell_v2 *dr_cell,
948c2ecf20Sopenharmony_ci				struct drmem_lmb *lmb)
958c2ecf20Sopenharmony_ci{
968c2ecf20Sopenharmony_ci	dr_cell->base_addr = cpu_to_be64(lmb->base_addr);
978c2ecf20Sopenharmony_ci	dr_cell->drc_index = cpu_to_be32(lmb->drc_index);
988c2ecf20Sopenharmony_ci	dr_cell->aa_index = cpu_to_be32(lmb->aa_index);
998c2ecf20Sopenharmony_ci	dr_cell->flags = cpu_to_be32(drmem_lmb_flags(lmb));
1008c2ecf20Sopenharmony_ci}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_cistatic int drmem_update_dt_v2(struct device_node *memory,
1038c2ecf20Sopenharmony_ci			      struct property *prop)
1048c2ecf20Sopenharmony_ci{
1058c2ecf20Sopenharmony_ci	struct property *new_prop;
1068c2ecf20Sopenharmony_ci	struct of_drconf_cell_v2 *dr_cell;
1078c2ecf20Sopenharmony_ci	struct drmem_lmb *lmb, *prev_lmb;
1088c2ecf20Sopenharmony_ci	u32 lmb_sets, prop_sz, seq_lmbs;
1098c2ecf20Sopenharmony_ci	u32 *p;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	/* First pass, determine how many LMB sets are needed. */
1128c2ecf20Sopenharmony_ci	lmb_sets = 0;
1138c2ecf20Sopenharmony_ci	prev_lmb = NULL;
1148c2ecf20Sopenharmony_ci	for_each_drmem_lmb(lmb) {
1158c2ecf20Sopenharmony_ci		if (!prev_lmb) {
1168c2ecf20Sopenharmony_ci			prev_lmb = lmb;
1178c2ecf20Sopenharmony_ci			lmb_sets++;
1188c2ecf20Sopenharmony_ci			continue;
1198c2ecf20Sopenharmony_ci		}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci		if (prev_lmb->aa_index != lmb->aa_index ||
1228c2ecf20Sopenharmony_ci		    drmem_lmb_flags(prev_lmb) != drmem_lmb_flags(lmb))
1238c2ecf20Sopenharmony_ci			lmb_sets++;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci		prev_lmb = lmb;
1268c2ecf20Sopenharmony_ci	}
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	prop_sz = lmb_sets * sizeof(*dr_cell) + sizeof(__be32);
1298c2ecf20Sopenharmony_ci	new_prop = clone_property(prop, prop_sz);
1308c2ecf20Sopenharmony_ci	if (!new_prop)
1318c2ecf20Sopenharmony_ci		return -1;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	p = new_prop->value;
1348c2ecf20Sopenharmony_ci	*p++ = cpu_to_be32(lmb_sets);
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	dr_cell = (struct of_drconf_cell_v2 *)p;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	/* Second pass, populate the LMB set data */
1398c2ecf20Sopenharmony_ci	prev_lmb = NULL;
1408c2ecf20Sopenharmony_ci	seq_lmbs = 0;
1418c2ecf20Sopenharmony_ci	for_each_drmem_lmb(lmb) {
1428c2ecf20Sopenharmony_ci		if (prev_lmb == NULL) {
1438c2ecf20Sopenharmony_ci			/* Start of first LMB set */
1448c2ecf20Sopenharmony_ci			prev_lmb = lmb;
1458c2ecf20Sopenharmony_ci			init_drconf_v2_cell(dr_cell, lmb);
1468c2ecf20Sopenharmony_ci			seq_lmbs++;
1478c2ecf20Sopenharmony_ci			continue;
1488c2ecf20Sopenharmony_ci		}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci		if (prev_lmb->aa_index != lmb->aa_index ||
1518c2ecf20Sopenharmony_ci		    drmem_lmb_flags(prev_lmb) != drmem_lmb_flags(lmb)) {
1528c2ecf20Sopenharmony_ci			/* end of one set, start of another */
1538c2ecf20Sopenharmony_ci			dr_cell->seq_lmbs = cpu_to_be32(seq_lmbs);
1548c2ecf20Sopenharmony_ci			dr_cell++;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci			init_drconf_v2_cell(dr_cell, lmb);
1578c2ecf20Sopenharmony_ci			seq_lmbs = 1;
1588c2ecf20Sopenharmony_ci		} else {
1598c2ecf20Sopenharmony_ci			seq_lmbs++;
1608c2ecf20Sopenharmony_ci		}
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci		prev_lmb = lmb;
1638c2ecf20Sopenharmony_ci	}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	/* close out last LMB set */
1668c2ecf20Sopenharmony_ci	dr_cell->seq_lmbs = cpu_to_be32(seq_lmbs);
1678c2ecf20Sopenharmony_ci	of_update_property(memory, new_prop);
1688c2ecf20Sopenharmony_ci	return 0;
1698c2ecf20Sopenharmony_ci}
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ciint drmem_update_dt(void)
1728c2ecf20Sopenharmony_ci{
1738c2ecf20Sopenharmony_ci	struct device_node *memory;
1748c2ecf20Sopenharmony_ci	struct property *prop;
1758c2ecf20Sopenharmony_ci	int rc = -1;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	memory = of_find_node_by_path("/ibm,dynamic-reconfiguration-memory");
1788c2ecf20Sopenharmony_ci	if (!memory)
1798c2ecf20Sopenharmony_ci		return -1;
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	prop = of_find_property(memory, "ibm,dynamic-memory", NULL);
1828c2ecf20Sopenharmony_ci	if (prop) {
1838c2ecf20Sopenharmony_ci		rc = drmem_update_dt_v1(memory, prop);
1848c2ecf20Sopenharmony_ci	} else {
1858c2ecf20Sopenharmony_ci		prop = of_find_property(memory, "ibm,dynamic-memory-v2", NULL);
1868c2ecf20Sopenharmony_ci		if (prop)
1878c2ecf20Sopenharmony_ci			rc = drmem_update_dt_v2(memory, prop);
1888c2ecf20Sopenharmony_ci	}
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	of_node_put(memory);
1918c2ecf20Sopenharmony_ci	return rc;
1928c2ecf20Sopenharmony_ci}
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_cistatic void read_drconf_v1_cell(struct drmem_lmb *lmb,
1958c2ecf20Sopenharmony_ci				       const __be32 **prop)
1968c2ecf20Sopenharmony_ci{
1978c2ecf20Sopenharmony_ci	const __be32 *p = *prop;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	lmb->base_addr = of_read_number(p, n_root_addr_cells);
2008c2ecf20Sopenharmony_ci	p += n_root_addr_cells;
2018c2ecf20Sopenharmony_ci	lmb->drc_index = of_read_number(p++, 1);
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	p++; /* skip reserved field */
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	lmb->aa_index = of_read_number(p++, 1);
2068c2ecf20Sopenharmony_ci	lmb->flags = of_read_number(p++, 1);
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	*prop = p;
2098c2ecf20Sopenharmony_ci}
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_cistatic int
2128c2ecf20Sopenharmony_ci__walk_drmem_v1_lmbs(const __be32 *prop, const __be32 *usm, void *data,
2138c2ecf20Sopenharmony_ci		     int (*func)(struct drmem_lmb *, const __be32 **, void *))
2148c2ecf20Sopenharmony_ci{
2158c2ecf20Sopenharmony_ci	struct drmem_lmb lmb;
2168c2ecf20Sopenharmony_ci	u32 i, n_lmbs;
2178c2ecf20Sopenharmony_ci	int ret = 0;
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	n_lmbs = of_read_number(prop++, 1);
2208c2ecf20Sopenharmony_ci	for (i = 0; i < n_lmbs; i++) {
2218c2ecf20Sopenharmony_ci		read_drconf_v1_cell(&lmb, &prop);
2228c2ecf20Sopenharmony_ci		ret = func(&lmb, &usm, data);
2238c2ecf20Sopenharmony_ci		if (ret)
2248c2ecf20Sopenharmony_ci			break;
2258c2ecf20Sopenharmony_ci	}
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	return ret;
2288c2ecf20Sopenharmony_ci}
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_cistatic void read_drconf_v2_cell(struct of_drconf_cell_v2 *dr_cell,
2318c2ecf20Sopenharmony_ci				       const __be32 **prop)
2328c2ecf20Sopenharmony_ci{
2338c2ecf20Sopenharmony_ci	const __be32 *p = *prop;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	dr_cell->seq_lmbs = of_read_number(p++, 1);
2368c2ecf20Sopenharmony_ci	dr_cell->base_addr = of_read_number(p, n_root_addr_cells);
2378c2ecf20Sopenharmony_ci	p += n_root_addr_cells;
2388c2ecf20Sopenharmony_ci	dr_cell->drc_index = of_read_number(p++, 1);
2398c2ecf20Sopenharmony_ci	dr_cell->aa_index = of_read_number(p++, 1);
2408c2ecf20Sopenharmony_ci	dr_cell->flags = of_read_number(p++, 1);
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	*prop = p;
2438c2ecf20Sopenharmony_ci}
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_cistatic int
2468c2ecf20Sopenharmony_ci__walk_drmem_v2_lmbs(const __be32 *prop, const __be32 *usm, void *data,
2478c2ecf20Sopenharmony_ci		     int (*func)(struct drmem_lmb *, const __be32 **, void *))
2488c2ecf20Sopenharmony_ci{
2498c2ecf20Sopenharmony_ci	struct of_drconf_cell_v2 dr_cell;
2508c2ecf20Sopenharmony_ci	struct drmem_lmb lmb;
2518c2ecf20Sopenharmony_ci	u32 i, j, lmb_sets;
2528c2ecf20Sopenharmony_ci	int ret = 0;
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	lmb_sets = of_read_number(prop++, 1);
2558c2ecf20Sopenharmony_ci	for (i = 0; i < lmb_sets; i++) {
2568c2ecf20Sopenharmony_ci		read_drconf_v2_cell(&dr_cell, &prop);
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci		for (j = 0; j < dr_cell.seq_lmbs; j++) {
2598c2ecf20Sopenharmony_ci			lmb.base_addr = dr_cell.base_addr;
2608c2ecf20Sopenharmony_ci			dr_cell.base_addr += drmem_lmb_size();
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci			lmb.drc_index = dr_cell.drc_index;
2638c2ecf20Sopenharmony_ci			dr_cell.drc_index++;
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci			lmb.aa_index = dr_cell.aa_index;
2668c2ecf20Sopenharmony_ci			lmb.flags = dr_cell.flags;
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci			ret = func(&lmb, &usm, data);
2698c2ecf20Sopenharmony_ci			if (ret)
2708c2ecf20Sopenharmony_ci				break;
2718c2ecf20Sopenharmony_ci		}
2728c2ecf20Sopenharmony_ci	}
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	return ret;
2758c2ecf20Sopenharmony_ci}
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC_PSERIES
2788c2ecf20Sopenharmony_ciint __init walk_drmem_lmbs_early(unsigned long node, void *data,
2798c2ecf20Sopenharmony_ci		int (*func)(struct drmem_lmb *, const __be32 **, void *))
2808c2ecf20Sopenharmony_ci{
2818c2ecf20Sopenharmony_ci	const __be32 *prop, *usm;
2828c2ecf20Sopenharmony_ci	int len, ret = -ENODEV;
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	prop = of_get_flat_dt_prop(node, "ibm,lmb-size", &len);
2858c2ecf20Sopenharmony_ci	if (!prop || len < dt_root_size_cells * sizeof(__be32))
2868c2ecf20Sopenharmony_ci		return ret;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	/* Get the address & size cells */
2898c2ecf20Sopenharmony_ci	n_root_addr_cells = dt_root_addr_cells;
2908c2ecf20Sopenharmony_ci	n_root_size_cells = dt_root_size_cells;
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	drmem_info->lmb_size = dt_mem_next_cell(dt_root_size_cells, &prop);
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	usm = of_get_flat_dt_prop(node, "linux,drconf-usable-memory", &len);
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	prop = of_get_flat_dt_prop(node, "ibm,dynamic-memory", &len);
2978c2ecf20Sopenharmony_ci	if (prop) {
2988c2ecf20Sopenharmony_ci		ret = __walk_drmem_v1_lmbs(prop, usm, data, func);
2998c2ecf20Sopenharmony_ci	} else {
3008c2ecf20Sopenharmony_ci		prop = of_get_flat_dt_prop(node, "ibm,dynamic-memory-v2",
3018c2ecf20Sopenharmony_ci					   &len);
3028c2ecf20Sopenharmony_ci		if (prop)
3038c2ecf20Sopenharmony_ci			ret = __walk_drmem_v2_lmbs(prop, usm, data, func);
3048c2ecf20Sopenharmony_ci	}
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	memblock_dump_all();
3078c2ecf20Sopenharmony_ci	return ret;
3088c2ecf20Sopenharmony_ci}
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci#endif
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_cistatic int init_drmem_lmb_size(struct device_node *dn)
3138c2ecf20Sopenharmony_ci{
3148c2ecf20Sopenharmony_ci	const __be32 *prop;
3158c2ecf20Sopenharmony_ci	int len;
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	if (drmem_info->lmb_size)
3188c2ecf20Sopenharmony_ci		return 0;
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	prop = of_get_property(dn, "ibm,lmb-size", &len);
3218c2ecf20Sopenharmony_ci	if (!prop || len < n_root_size_cells * sizeof(__be32)) {
3228c2ecf20Sopenharmony_ci		pr_info("Could not determine LMB size\n");
3238c2ecf20Sopenharmony_ci		return -1;
3248c2ecf20Sopenharmony_ci	}
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	drmem_info->lmb_size = of_read_number(prop, n_root_size_cells);
3278c2ecf20Sopenharmony_ci	return 0;
3288c2ecf20Sopenharmony_ci}
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci/*
3318c2ecf20Sopenharmony_ci * Returns the property linux,drconf-usable-memory if
3328c2ecf20Sopenharmony_ci * it exists (the property exists only in kexec/kdump kernels,
3338c2ecf20Sopenharmony_ci * added by kexec-tools)
3348c2ecf20Sopenharmony_ci */
3358c2ecf20Sopenharmony_cistatic const __be32 *of_get_usable_memory(struct device_node *dn)
3368c2ecf20Sopenharmony_ci{
3378c2ecf20Sopenharmony_ci	const __be32 *prop;
3388c2ecf20Sopenharmony_ci	u32 len;
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	prop = of_get_property(dn, "linux,drconf-usable-memory", &len);
3418c2ecf20Sopenharmony_ci	if (!prop || len < sizeof(unsigned int))
3428c2ecf20Sopenharmony_ci		return NULL;
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	return prop;
3458c2ecf20Sopenharmony_ci}
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ciint walk_drmem_lmbs(struct device_node *dn, void *data,
3488c2ecf20Sopenharmony_ci		    int (*func)(struct drmem_lmb *, const __be32 **, void *))
3498c2ecf20Sopenharmony_ci{
3508c2ecf20Sopenharmony_ci	const __be32 *prop, *usm;
3518c2ecf20Sopenharmony_ci	int ret = -ENODEV;
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	if (!of_root)
3548c2ecf20Sopenharmony_ci		return ret;
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	/* Get the address & size cells */
3578c2ecf20Sopenharmony_ci	of_node_get(of_root);
3588c2ecf20Sopenharmony_ci	n_root_addr_cells = of_n_addr_cells(of_root);
3598c2ecf20Sopenharmony_ci	n_root_size_cells = of_n_size_cells(of_root);
3608c2ecf20Sopenharmony_ci	of_node_put(of_root);
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	if (init_drmem_lmb_size(dn))
3638c2ecf20Sopenharmony_ci		return ret;
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	usm = of_get_usable_memory(dn);
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	prop = of_get_property(dn, "ibm,dynamic-memory", NULL);
3688c2ecf20Sopenharmony_ci	if (prop) {
3698c2ecf20Sopenharmony_ci		ret = __walk_drmem_v1_lmbs(prop, usm, data, func);
3708c2ecf20Sopenharmony_ci	} else {
3718c2ecf20Sopenharmony_ci		prop = of_get_property(dn, "ibm,dynamic-memory-v2", NULL);
3728c2ecf20Sopenharmony_ci		if (prop)
3738c2ecf20Sopenharmony_ci			ret = __walk_drmem_v2_lmbs(prop, usm, data, func);
3748c2ecf20Sopenharmony_ci	}
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	return ret;
3778c2ecf20Sopenharmony_ci}
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_cistatic void __init init_drmem_v1_lmbs(const __be32 *prop)
3808c2ecf20Sopenharmony_ci{
3818c2ecf20Sopenharmony_ci	struct drmem_lmb *lmb;
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	drmem_info->n_lmbs = of_read_number(prop++, 1);
3848c2ecf20Sopenharmony_ci	if (drmem_info->n_lmbs == 0)
3858c2ecf20Sopenharmony_ci		return;
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	drmem_info->lmbs = kcalloc(drmem_info->n_lmbs, sizeof(*lmb),
3888c2ecf20Sopenharmony_ci				   GFP_KERNEL);
3898c2ecf20Sopenharmony_ci	if (!drmem_info->lmbs)
3908c2ecf20Sopenharmony_ci		return;
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	for_each_drmem_lmb(lmb)
3938c2ecf20Sopenharmony_ci		read_drconf_v1_cell(lmb, &prop);
3948c2ecf20Sopenharmony_ci}
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_cistatic void __init init_drmem_v2_lmbs(const __be32 *prop)
3978c2ecf20Sopenharmony_ci{
3988c2ecf20Sopenharmony_ci	struct drmem_lmb *lmb;
3998c2ecf20Sopenharmony_ci	struct of_drconf_cell_v2 dr_cell;
4008c2ecf20Sopenharmony_ci	const __be32 *p;
4018c2ecf20Sopenharmony_ci	u32 i, j, lmb_sets;
4028c2ecf20Sopenharmony_ci	int lmb_index;
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	lmb_sets = of_read_number(prop++, 1);
4058c2ecf20Sopenharmony_ci	if (lmb_sets == 0)
4068c2ecf20Sopenharmony_ci		return;
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	/* first pass, calculate the number of LMBs */
4098c2ecf20Sopenharmony_ci	p = prop;
4108c2ecf20Sopenharmony_ci	for (i = 0; i < lmb_sets; i++) {
4118c2ecf20Sopenharmony_ci		read_drconf_v2_cell(&dr_cell, &p);
4128c2ecf20Sopenharmony_ci		drmem_info->n_lmbs += dr_cell.seq_lmbs;
4138c2ecf20Sopenharmony_ci	}
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	drmem_info->lmbs = kcalloc(drmem_info->n_lmbs, sizeof(*lmb),
4168c2ecf20Sopenharmony_ci				   GFP_KERNEL);
4178c2ecf20Sopenharmony_ci	if (!drmem_info->lmbs)
4188c2ecf20Sopenharmony_ci		return;
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	/* second pass, read in the LMB information */
4218c2ecf20Sopenharmony_ci	lmb_index = 0;
4228c2ecf20Sopenharmony_ci	p = prop;
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	for (i = 0; i < lmb_sets; i++) {
4258c2ecf20Sopenharmony_ci		read_drconf_v2_cell(&dr_cell, &p);
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci		for (j = 0; j < dr_cell.seq_lmbs; j++) {
4288c2ecf20Sopenharmony_ci			lmb = &drmem_info->lmbs[lmb_index++];
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci			lmb->base_addr = dr_cell.base_addr;
4318c2ecf20Sopenharmony_ci			dr_cell.base_addr += drmem_info->lmb_size;
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci			lmb->drc_index = dr_cell.drc_index;
4348c2ecf20Sopenharmony_ci			dr_cell.drc_index++;
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci			lmb->aa_index = dr_cell.aa_index;
4378c2ecf20Sopenharmony_ci			lmb->flags = dr_cell.flags;
4388c2ecf20Sopenharmony_ci		}
4398c2ecf20Sopenharmony_ci	}
4408c2ecf20Sopenharmony_ci}
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_cistatic int __init drmem_init(void)
4438c2ecf20Sopenharmony_ci{
4448c2ecf20Sopenharmony_ci	struct device_node *dn;
4458c2ecf20Sopenharmony_ci	const __be32 *prop;
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci	dn = of_find_node_by_path("/ibm,dynamic-reconfiguration-memory");
4488c2ecf20Sopenharmony_ci	if (!dn) {
4498c2ecf20Sopenharmony_ci		pr_info("No dynamic reconfiguration memory found\n");
4508c2ecf20Sopenharmony_ci		return 0;
4518c2ecf20Sopenharmony_ci	}
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	if (init_drmem_lmb_size(dn)) {
4548c2ecf20Sopenharmony_ci		of_node_put(dn);
4558c2ecf20Sopenharmony_ci		return 0;
4568c2ecf20Sopenharmony_ci	}
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	prop = of_get_property(dn, "ibm,dynamic-memory", NULL);
4598c2ecf20Sopenharmony_ci	if (prop) {
4608c2ecf20Sopenharmony_ci		init_drmem_v1_lmbs(prop);
4618c2ecf20Sopenharmony_ci	} else {
4628c2ecf20Sopenharmony_ci		prop = of_get_property(dn, "ibm,dynamic-memory-v2", NULL);
4638c2ecf20Sopenharmony_ci		if (prop)
4648c2ecf20Sopenharmony_ci			init_drmem_v2_lmbs(prop);
4658c2ecf20Sopenharmony_ci	}
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci	of_node_put(dn);
4688c2ecf20Sopenharmony_ci	return 0;
4698c2ecf20Sopenharmony_ci}
4708c2ecf20Sopenharmony_cilate_initcall(drmem_init);
471