1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Dynamic reconfiguration memory support
5 * Copyright 2017 IBM Corporation
8 #define pr_fmt(fmt) "drmem: " fmt
10 #include <linux/kernel.h>
12 #include <linux/of_fdt.h>
13 #include <linux/memblock.h>
14 #include <linux/slab.h>
15 #include <asm/drmem.h>
17 static int n_root_addr_cells
, n_root_size_cells
;
19 static struct drmem_lmb_info __drmem_info
;
20 struct drmem_lmb_info
*drmem_info
= &__drmem_info
;
21 static bool in_drmem_update
;
23 u64
drmem_lmb_memory_max(void)
25 struct drmem_lmb
*last_lmb
;
27 last_lmb
= &drmem_info
->lmbs
[drmem_info
->n_lmbs
- 1];
28 return last_lmb
->base_addr
+ drmem_lmb_size();
31 static u32
drmem_lmb_flags(struct drmem_lmb
*lmb
)
34 * Return the value of the lmb flags field minus the reserved
35 * bit used internally for hotplug processing.
37 return lmb
->flags
& ~DRMEM_LMB_RESERVED
;
40 static struct property
*clone_property(struct property
*prop
, u32 prop_sz
)
42 struct property
*new_prop
;
44 new_prop
= kzalloc(sizeof(*new_prop
), GFP_KERNEL
);
48 new_prop
->name
= kstrdup(prop
->name
, GFP_KERNEL
);
49 new_prop
->value
= kzalloc(prop_sz
, GFP_KERNEL
);
50 if (!new_prop
->name
|| !new_prop
->value
) {
51 kfree(new_prop
->name
);
52 kfree(new_prop
->value
);
57 new_prop
->length
= prop_sz
;
58 #if defined(CONFIG_OF_DYNAMIC)
59 of_property_set_flag(new_prop
, OF_DYNAMIC
);
64 static int drmem_update_dt_v1(struct device_node
*memory
,
65 struct property
*prop
)
67 struct property
*new_prop
;
68 struct of_drconf_cell_v1
*dr_cell
;
69 struct drmem_lmb
*lmb
;
72 new_prop
= clone_property(prop
, prop
->length
);
77 *p
++ = cpu_to_be32(drmem_info
->n_lmbs
);
79 dr_cell
= (struct of_drconf_cell_v1
*)p
;
81 for_each_drmem_lmb(lmb
) {
82 dr_cell
->base_addr
= cpu_to_be64(lmb
->base_addr
);
83 dr_cell
->drc_index
= cpu_to_be32(lmb
->drc_index
);
84 dr_cell
->aa_index
= cpu_to_be32(lmb
->aa_index
);
85 dr_cell
->flags
= cpu_to_be32(drmem_lmb_flags(lmb
));
90 of_update_property(memory
, new_prop
);
94 static void init_drconf_v2_cell(struct of_drconf_cell_v2
*dr_cell
,
95 struct drmem_lmb
*lmb
)
97 dr_cell
->base_addr
= cpu_to_be64(lmb
->base_addr
);
98 dr_cell
->drc_index
= cpu_to_be32(lmb
->drc_index
);
99 dr_cell
->aa_index
= cpu_to_be32(lmb
->aa_index
);
100 dr_cell
->flags
= cpu_to_be32(drmem_lmb_flags(lmb
));
103 static int drmem_update_dt_v2(struct device_node
*memory
,
104 struct property
*prop
)
106 struct property
*new_prop
;
107 struct of_drconf_cell_v2
*dr_cell
;
108 struct drmem_lmb
*lmb
, *prev_lmb
;
109 u32 lmb_sets
, prop_sz
, seq_lmbs
;
112 /* First pass, determine how many LMB sets are needed. */
115 for_each_drmem_lmb(lmb
) {
122 if (prev_lmb
->aa_index
!= lmb
->aa_index
||
123 drmem_lmb_flags(prev_lmb
) != drmem_lmb_flags(lmb
))
129 prop_sz
= lmb_sets
* sizeof(*dr_cell
) + sizeof(__be32
);
130 new_prop
= clone_property(prop
, prop_sz
);
135 *p
++ = cpu_to_be32(lmb_sets
);
137 dr_cell
= (struct of_drconf_cell_v2
*)p
;
139 /* Second pass, populate the LMB set data */
142 for_each_drmem_lmb(lmb
) {
143 if (prev_lmb
== NULL
) {
144 /* Start of first LMB set */
146 init_drconf_v2_cell(dr_cell
, lmb
);
151 if (prev_lmb
->aa_index
!= lmb
->aa_index
||
152 drmem_lmb_flags(prev_lmb
) != drmem_lmb_flags(lmb
)) {
153 /* end of one set, start of another */
154 dr_cell
->seq_lmbs
= cpu_to_be32(seq_lmbs
);
157 init_drconf_v2_cell(dr_cell
, lmb
);
166 /* close out last LMB set */
167 dr_cell
->seq_lmbs
= cpu_to_be32(seq_lmbs
);
168 of_update_property(memory
, new_prop
);
172 int drmem_update_dt(void)
174 struct device_node
*memory
;
175 struct property
*prop
;
178 memory
= of_find_node_by_path("/ibm,dynamic-reconfiguration-memory");
183 * Set in_drmem_update to prevent the notifier callback to process the
184 * DT property back since the change is coming from the LMB tree.
186 in_drmem_update
= true;
187 prop
= of_find_property(memory
, "ibm,dynamic-memory", NULL
);
189 rc
= drmem_update_dt_v1(memory
, prop
);
191 prop
= of_find_property(memory
, "ibm,dynamic-memory-v2", NULL
);
193 rc
= drmem_update_dt_v2(memory
, prop
);
195 in_drmem_update
= false;
201 static void read_drconf_v1_cell(struct drmem_lmb
*lmb
,
204 const __be32
*p
= *prop
;
206 lmb
->base_addr
= of_read_number(p
, n_root_addr_cells
);
207 p
+= n_root_addr_cells
;
208 lmb
->drc_index
= of_read_number(p
++, 1);
210 p
++; /* skip reserved field */
212 lmb
->aa_index
= of_read_number(p
++, 1);
213 lmb
->flags
= of_read_number(p
++, 1);
219 __walk_drmem_v1_lmbs(const __be32
*prop
, const __be32
*usm
, void *data
,
220 int (*func
)(struct drmem_lmb
*, const __be32
**, void *))
222 struct drmem_lmb lmb
;
226 n_lmbs
= of_read_number(prop
++, 1);
227 for (i
= 0; i
< n_lmbs
; i
++) {
228 read_drconf_v1_cell(&lmb
, &prop
);
229 ret
= func(&lmb
, &usm
, data
);
237 static void read_drconf_v2_cell(struct of_drconf_cell_v2
*dr_cell
,
240 const __be32
*p
= *prop
;
242 dr_cell
->seq_lmbs
= of_read_number(p
++, 1);
243 dr_cell
->base_addr
= of_read_number(p
, n_root_addr_cells
);
244 p
+= n_root_addr_cells
;
245 dr_cell
->drc_index
= of_read_number(p
++, 1);
246 dr_cell
->aa_index
= of_read_number(p
++, 1);
247 dr_cell
->flags
= of_read_number(p
++, 1);
253 __walk_drmem_v2_lmbs(const __be32
*prop
, const __be32
*usm
, void *data
,
254 int (*func
)(struct drmem_lmb
*, const __be32
**, void *))
256 struct of_drconf_cell_v2 dr_cell
;
257 struct drmem_lmb lmb
;
261 lmb_sets
= of_read_number(prop
++, 1);
262 for (i
= 0; i
< lmb_sets
; i
++) {
263 read_drconf_v2_cell(&dr_cell
, &prop
);
265 for (j
= 0; j
< dr_cell
.seq_lmbs
; j
++) {
266 lmb
.base_addr
= dr_cell
.base_addr
;
267 dr_cell
.base_addr
+= drmem_lmb_size();
269 lmb
.drc_index
= dr_cell
.drc_index
;
272 lmb
.aa_index
= dr_cell
.aa_index
;
273 lmb
.flags
= dr_cell
.flags
;
275 ret
= func(&lmb
, &usm
, data
);
284 #ifdef CONFIG_PPC_PSERIES
285 int __init
walk_drmem_lmbs_early(unsigned long node
, void *data
,
286 int (*func
)(struct drmem_lmb
*, const __be32
**, void *))
288 const __be32
*prop
, *usm
;
289 int len
, ret
= -ENODEV
;
291 prop
= of_get_flat_dt_prop(node
, "ibm,lmb-size", &len
);
292 if (!prop
|| len
< dt_root_size_cells
* sizeof(__be32
))
295 /* Get the address & size cells */
296 n_root_addr_cells
= dt_root_addr_cells
;
297 n_root_size_cells
= dt_root_size_cells
;
299 drmem_info
->lmb_size
= dt_mem_next_cell(dt_root_size_cells
, &prop
);
301 usm
= of_get_flat_dt_prop(node
, "linux,drconf-usable-memory", &len
);
303 prop
= of_get_flat_dt_prop(node
, "ibm,dynamic-memory", &len
);
305 ret
= __walk_drmem_v1_lmbs(prop
, usm
, data
, func
);
307 prop
= of_get_flat_dt_prop(node
, "ibm,dynamic-memory-v2",
310 ret
= __walk_drmem_v2_lmbs(prop
, usm
, data
, func
);
318 * Update the LMB associativity index.
320 static int update_lmb(struct drmem_lmb
*updated_lmb
,
321 __maybe_unused
const __be32
**usm
,
322 __maybe_unused
void *data
)
324 struct drmem_lmb
*lmb
;
326 for_each_drmem_lmb(lmb
) {
327 if (lmb
->drc_index
!= updated_lmb
->drc_index
)
330 lmb
->aa_index
= updated_lmb
->aa_index
;
337 * Update the LMB associativity index.
339 * This needs to be called when the hypervisor is updating the
340 * dynamic-reconfiguration-memory node property.
342 void drmem_update_lmbs(struct property
*prop
)
345 * Don't update the LMBs if triggered by the update done in
346 * drmem_update_dt(), the LMB values have been used to the update the DT
347 * property in that case.
351 if (!strcmp(prop
->name
, "ibm,dynamic-memory"))
352 __walk_drmem_v1_lmbs(prop
->value
, NULL
, NULL
, update_lmb
);
353 else if (!strcmp(prop
->name
, "ibm,dynamic-memory-v2"))
354 __walk_drmem_v2_lmbs(prop
->value
, NULL
, NULL
, update_lmb
);
358 static int init_drmem_lmb_size(struct device_node
*dn
)
363 if (drmem_info
->lmb_size
)
366 prop
= of_get_property(dn
, "ibm,lmb-size", &len
);
367 if (!prop
|| len
< n_root_size_cells
* sizeof(__be32
)) {
368 pr_info("Could not determine LMB size\n");
372 drmem_info
->lmb_size
= of_read_number(prop
, n_root_size_cells
);
377 * Returns the property linux,drconf-usable-memory if
378 * it exists (the property exists only in kexec/kdump kernels,
379 * added by kexec-tools)
381 static const __be32
*of_get_usable_memory(struct device_node
*dn
)
386 prop
= of_get_property(dn
, "linux,drconf-usable-memory", &len
);
387 if (!prop
|| len
< sizeof(unsigned int))
393 int walk_drmem_lmbs(struct device_node
*dn
, void *data
,
394 int (*func
)(struct drmem_lmb
*, const __be32
**, void *))
396 struct device_node
*root
= of_find_node_by_path("/");
397 const __be32
*prop
, *usm
;
403 /* Get the address & size cells */
404 n_root_addr_cells
= of_n_addr_cells(root
);
405 n_root_size_cells
= of_n_size_cells(root
);
408 if (init_drmem_lmb_size(dn
))
411 usm
= of_get_usable_memory(dn
);
413 prop
= of_get_property(dn
, "ibm,dynamic-memory", NULL
);
415 ret
= __walk_drmem_v1_lmbs(prop
, usm
, data
, func
);
417 prop
= of_get_property(dn
, "ibm,dynamic-memory-v2", NULL
);
419 ret
= __walk_drmem_v2_lmbs(prop
, usm
, data
, func
);
425 static void __init
init_drmem_v1_lmbs(const __be32
*prop
)
427 struct drmem_lmb
*lmb
;
429 drmem_info
->n_lmbs
= of_read_number(prop
++, 1);
430 if (drmem_info
->n_lmbs
== 0)
433 drmem_info
->lmbs
= kcalloc(drmem_info
->n_lmbs
, sizeof(*lmb
),
435 if (!drmem_info
->lmbs
)
438 for_each_drmem_lmb(lmb
)
439 read_drconf_v1_cell(lmb
, &prop
);
442 static void __init
init_drmem_v2_lmbs(const __be32
*prop
)
444 struct drmem_lmb
*lmb
;
445 struct of_drconf_cell_v2 dr_cell
;
450 lmb_sets
= of_read_number(prop
++, 1);
454 /* first pass, calculate the number of LMBs */
456 for (i
= 0; i
< lmb_sets
; i
++) {
457 read_drconf_v2_cell(&dr_cell
, &p
);
458 drmem_info
->n_lmbs
+= dr_cell
.seq_lmbs
;
461 drmem_info
->lmbs
= kcalloc(drmem_info
->n_lmbs
, sizeof(*lmb
),
463 if (!drmem_info
->lmbs
)
466 /* second pass, read in the LMB information */
470 for (i
= 0; i
< lmb_sets
; i
++) {
471 read_drconf_v2_cell(&dr_cell
, &p
);
473 for (j
= 0; j
< dr_cell
.seq_lmbs
; j
++) {
474 lmb
= &drmem_info
->lmbs
[lmb_index
++];
476 lmb
->base_addr
= dr_cell
.base_addr
;
477 dr_cell
.base_addr
+= drmem_info
->lmb_size
;
479 lmb
->drc_index
= dr_cell
.drc_index
;
482 lmb
->aa_index
= dr_cell
.aa_index
;
483 lmb
->flags
= dr_cell
.flags
;
488 static int __init
drmem_init(void)
490 struct device_node
*dn
;
493 dn
= of_find_node_by_path("/ibm,dynamic-reconfiguration-memory");
497 if (init_drmem_lmb_size(dn
)) {
502 prop
= of_get_property(dn
, "ibm,dynamic-memory", NULL
);
504 init_drmem_v1_lmbs(prop
);
506 prop
= of_get_property(dn
, "ibm,dynamic-memory-v2", NULL
);
508 init_drmem_v2_lmbs(prop
);
514 late_initcall(drmem_init
);