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