2 * Dynamic reconfiguration memory support
4 * Copyright 2017 IBM Corporation
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
12 #define pr_fmt(fmt) "drmem: " fmt
14 #include <linux/kernel.h>
16 #include <linux/of_fdt.h>
17 #include <linux/memblock.h>
19 #include <asm/drmem.h>
21 static struct drmem_lmb_info __drmem_info
;
22 struct drmem_lmb_info
*drmem_info
= &__drmem_info
;
24 u64
drmem_lmb_memory_max(void)
26 struct drmem_lmb
*last_lmb
;
28 last_lmb
= &drmem_info
->lmbs
[drmem_info
->n_lmbs
- 1];
29 return last_lmb
->base_addr
+ drmem_lmb_size();
32 static u32
drmem_lmb_flags(struct drmem_lmb
*lmb
)
35 * Return the value of the lmb flags field minus the reserved
36 * bit used internally for hotplug processing.
38 return lmb
->flags
& ~DRMEM_LMB_RESERVED
;
41 static struct property
*clone_property(struct property
*prop
, u32 prop_sz
)
43 struct property
*new_prop
;
45 new_prop
= kzalloc(sizeof(*new_prop
), GFP_KERNEL
);
49 new_prop
->name
= kstrdup(prop
->name
, GFP_KERNEL
);
50 new_prop
->value
= kzalloc(prop_sz
, GFP_KERNEL
);
51 if (!new_prop
->name
|| !new_prop
->value
) {
52 kfree(new_prop
->name
);
53 kfree(new_prop
->value
);
58 new_prop
->length
= prop_sz
;
59 #if defined(CONFIG_OF_DYNAMIC)
60 of_property_set_flag(new_prop
, OF_DYNAMIC
);
65 static int drmem_update_dt_v1(struct device_node
*memory
,
66 struct property
*prop
)
68 struct property
*new_prop
;
69 struct of_drconf_cell_v1
*dr_cell
;
70 struct drmem_lmb
*lmb
;
73 new_prop
= clone_property(prop
, prop
->length
);
78 *p
++ = cpu_to_be32(drmem_info
->n_lmbs
);
80 dr_cell
= (struct of_drconf_cell_v1
*)p
;
82 for_each_drmem_lmb(lmb
) {
83 dr_cell
->base_addr
= cpu_to_be64(lmb
->base_addr
);
84 dr_cell
->drc_index
= cpu_to_be32(lmb
->drc_index
);
85 dr_cell
->aa_index
= cpu_to_be32(lmb
->aa_index
);
86 dr_cell
->flags
= cpu_to_be32(drmem_lmb_flags(lmb
));
91 of_update_property(memory
, new_prop
);
95 static void init_drconf_v2_cell(struct of_drconf_cell_v2
*dr_cell
,
96 struct drmem_lmb
*lmb
)
98 dr_cell
->base_addr
= cpu_to_be64(lmb
->base_addr
);
99 dr_cell
->drc_index
= cpu_to_be32(lmb
->drc_index
);
100 dr_cell
->aa_index
= cpu_to_be32(lmb
->aa_index
);
101 dr_cell
->flags
= cpu_to_be32(drmem_lmb_flags(lmb
));
104 static int drmem_update_dt_v2(struct device_node
*memory
,
105 struct property
*prop
)
107 struct property
*new_prop
;
108 struct of_drconf_cell_v2
*dr_cell
;
109 struct drmem_lmb
*lmb
, *prev_lmb
;
110 u32 lmb_sets
, prop_sz
, seq_lmbs
;
113 /* First pass, determine how many LMB sets are needed. */
116 for_each_drmem_lmb(lmb
) {
123 if (prev_lmb
->aa_index
!= lmb
->aa_index
||
124 drmem_lmb_flags(prev_lmb
) != drmem_lmb_flags(lmb
))
130 prop_sz
= lmb_sets
* sizeof(*dr_cell
) + sizeof(__be32
);
131 new_prop
= clone_property(prop
, prop_sz
);
136 *p
++ = cpu_to_be32(lmb_sets
);
138 dr_cell
= (struct of_drconf_cell_v2
*)p
;
140 /* Second pass, populate the LMB set data */
143 for_each_drmem_lmb(lmb
) {
144 if (prev_lmb
== NULL
) {
145 /* Start of first LMB set */
147 init_drconf_v2_cell(dr_cell
, lmb
);
152 if (prev_lmb
->aa_index
!= lmb
->aa_index
||
153 drmem_lmb_flags(prev_lmb
) != drmem_lmb_flags(lmb
)) {
154 /* end of one set, start of another */
155 dr_cell
->seq_lmbs
= cpu_to_be32(seq_lmbs
);
158 init_drconf_v2_cell(dr_cell
, lmb
);
167 /* close out last LMB set */
168 dr_cell
->seq_lmbs
= cpu_to_be32(seq_lmbs
);
169 of_update_property(memory
, new_prop
);
173 int drmem_update_dt(void)
175 struct device_node
*memory
;
176 struct property
*prop
;
179 memory
= of_find_node_by_path("/ibm,dynamic-reconfiguration-memory");
183 prop
= of_find_property(memory
, "ibm,dynamic-memory", NULL
);
185 rc
= drmem_update_dt_v1(memory
, prop
);
187 prop
= of_find_property(memory
, "ibm,dynamic-memory-v2", NULL
);
189 rc
= drmem_update_dt_v2(memory
, prop
);
196 static void __init
read_drconf_v1_cell(struct drmem_lmb
*lmb
,
199 const __be32
*p
= *prop
;
201 lmb
->base_addr
= dt_mem_next_cell(dt_root_addr_cells
, &p
);
202 lmb
->drc_index
= of_read_number(p
++, 1);
204 p
++; /* skip reserved field */
206 lmb
->aa_index
= of_read_number(p
++, 1);
207 lmb
->flags
= of_read_number(p
++, 1);
212 static void __init
__walk_drmem_v1_lmbs(const __be32
*prop
, const __be32
*usm
,
213 void (*func
)(struct drmem_lmb
*, const __be32
**))
215 struct drmem_lmb lmb
;
218 n_lmbs
= of_read_number(prop
++, 1);
222 for (i
= 0; i
< n_lmbs
; i
++) {
223 read_drconf_v1_cell(&lmb
, &prop
);
228 static void __init
read_drconf_v2_cell(struct of_drconf_cell_v2
*dr_cell
,
231 const __be32
*p
= *prop
;
233 dr_cell
->seq_lmbs
= of_read_number(p
++, 1);
234 dr_cell
->base_addr
= dt_mem_next_cell(dt_root_addr_cells
, &p
);
235 dr_cell
->drc_index
= of_read_number(p
++, 1);
236 dr_cell
->aa_index
= of_read_number(p
++, 1);
237 dr_cell
->flags
= of_read_number(p
++, 1);
242 static void __init
__walk_drmem_v2_lmbs(const __be32
*prop
, const __be32
*usm
,
243 void (*func
)(struct drmem_lmb
*, const __be32
**))
245 struct of_drconf_cell_v2 dr_cell
;
246 struct drmem_lmb lmb
;
249 lmb_sets
= of_read_number(prop
++, 1);
253 for (i
= 0; i
< lmb_sets
; i
++) {
254 read_drconf_v2_cell(&dr_cell
, &prop
);
256 for (j
= 0; j
< dr_cell
.seq_lmbs
; j
++) {
257 lmb
.base_addr
= dr_cell
.base_addr
;
258 dr_cell
.base_addr
+= drmem_lmb_size();
260 lmb
.drc_index
= dr_cell
.drc_index
;
263 lmb
.aa_index
= dr_cell
.aa_index
;
264 lmb
.flags
= dr_cell
.flags
;
271 #ifdef CONFIG_PPC_PSERIES
272 void __init
walk_drmem_lmbs_early(unsigned long node
,
273 void (*func
)(struct drmem_lmb
*, const __be32
**))
275 const __be32
*prop
, *usm
;
278 prop
= of_get_flat_dt_prop(node
, "ibm,lmb-size", &len
);
279 if (!prop
|| len
< dt_root_size_cells
* sizeof(__be32
))
282 drmem_info
->lmb_size
= dt_mem_next_cell(dt_root_size_cells
, &prop
);
284 usm
= of_get_flat_dt_prop(node
, "linux,drconf-usable-memory", &len
);
286 prop
= of_get_flat_dt_prop(node
, "ibm,dynamic-memory", &len
);
288 __walk_drmem_v1_lmbs(prop
, usm
, func
);
290 prop
= of_get_flat_dt_prop(node
, "ibm,dynamic-memory-v2",
293 __walk_drmem_v2_lmbs(prop
, usm
, func
);
301 static int __init
init_drmem_lmb_size(struct device_node
*dn
)
306 if (drmem_info
->lmb_size
)
309 prop
= of_get_property(dn
, "ibm,lmb-size", &len
);
310 if (!prop
|| len
< dt_root_size_cells
* sizeof(__be32
)) {
311 pr_info("Could not determine LMB size\n");
315 drmem_info
->lmb_size
= dt_mem_next_cell(dt_root_size_cells
, &prop
);
320 * Returns the property linux,drconf-usable-memory if
321 * it exists (the property exists only in kexec/kdump kernels,
322 * added by kexec-tools)
324 static const __be32
*of_get_usable_memory(struct device_node
*dn
)
329 prop
= of_get_property(dn
, "linux,drconf-usable-memory", &len
);
330 if (!prop
|| len
< sizeof(unsigned int))
336 void __init
walk_drmem_lmbs(struct device_node
*dn
,
337 void (*func
)(struct drmem_lmb
*, const __be32
**))
339 const __be32
*prop
, *usm
;
341 if (init_drmem_lmb_size(dn
))
344 usm
= of_get_usable_memory(dn
);
346 prop
= of_get_property(dn
, "ibm,dynamic-memory", NULL
);
348 __walk_drmem_v1_lmbs(prop
, usm
, func
);
350 prop
= of_get_property(dn
, "ibm,dynamic-memory-v2", NULL
);
352 __walk_drmem_v2_lmbs(prop
, usm
, func
);
356 static void __init
init_drmem_v1_lmbs(const __be32
*prop
)
358 struct drmem_lmb
*lmb
;
360 drmem_info
->n_lmbs
= of_read_number(prop
++, 1);
361 if (drmem_info
->n_lmbs
== 0)
364 drmem_info
->lmbs
= kcalloc(drmem_info
->n_lmbs
, sizeof(*lmb
),
366 if (!drmem_info
->lmbs
)
369 for_each_drmem_lmb(lmb
)
370 read_drconf_v1_cell(lmb
, &prop
);
373 static void __init
init_drmem_v2_lmbs(const __be32
*prop
)
375 struct drmem_lmb
*lmb
;
376 struct of_drconf_cell_v2 dr_cell
;
381 lmb_sets
= of_read_number(prop
++, 1);
385 /* first pass, calculate the number of LMBs */
387 for (i
= 0; i
< lmb_sets
; i
++) {
388 read_drconf_v2_cell(&dr_cell
, &p
);
389 drmem_info
->n_lmbs
+= dr_cell
.seq_lmbs
;
392 drmem_info
->lmbs
= kcalloc(drmem_info
->n_lmbs
, sizeof(*lmb
),
394 if (!drmem_info
->lmbs
)
397 /* second pass, read in the LMB information */
401 for (i
= 0; i
< lmb_sets
; i
++) {
402 read_drconf_v2_cell(&dr_cell
, &p
);
404 for (j
= 0; j
< dr_cell
.seq_lmbs
; j
++) {
405 lmb
= &drmem_info
->lmbs
[lmb_index
++];
407 lmb
->base_addr
= dr_cell
.base_addr
;
408 dr_cell
.base_addr
+= drmem_info
->lmb_size
;
410 lmb
->drc_index
= dr_cell
.drc_index
;
413 lmb
->aa_index
= dr_cell
.aa_index
;
414 lmb
->flags
= dr_cell
.flags
;
419 static int __init
drmem_init(void)
421 struct device_node
*dn
;
424 dn
= of_find_node_by_path("/ibm,dynamic-reconfiguration-memory");
426 pr_info("No dynamic reconfiguration memory found\n");
430 if (init_drmem_lmb_size(dn
)) {
435 prop
= of_get_property(dn
, "ibm,dynamic-memory", NULL
);
437 init_drmem_v1_lmbs(prop
);
439 prop
= of_get_property(dn
, "ibm,dynamic-memory-v2", NULL
);
441 init_drmem_v2_lmbs(prop
);
447 late_initcall(drmem_init
);