~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/arch/powerpc/mm/drmem.c

Version: ~ [ linux-4.18-rc1 ] ~ [ linux-4.17.2 ] ~ [ linux-4.16.16 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.50 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.109 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.138 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.19.8 ] ~ [ linux-3.18.113 ] ~ [ linux-3.17.8 ] ~ [ linux-3.16.57 ] ~ [ linux-3.15.10 ] ~ [ linux-3.14.79 ] ~ [ linux-3.13.11 ] ~ [ linux-3.12.74 ] ~ [ linux-3.11.10 ] ~ [ linux-3.10.108 ] ~ [ linux-3.9.11 ] ~ [ linux-3.8.13 ] ~ [ linux-3.7.10 ] ~ [ linux-3.6.11 ] ~ [ linux-3.5.7 ] ~ [ linux-3.4.113 ] ~ [ linux-3.3.8 ] ~ [ linux-3.2.102 ] ~ [ linux-3.1.10 ] ~ [ linux-3.0.101 ] ~ [ linux-2.6.39.4 ] ~ [ linux-2.6.38.8 ] ~ [ linux-2.6.37.6 ] ~ [ linux-2.6.36.4 ] ~ [ linux-2.6.35.14 ] ~ [ linux-2.6.34.15 ] ~ [ linux-2.6.33.20 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.27.62 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 /*
  2  * Dynamic reconfiguration memory support
  3  *
  4  * Copyright 2017 IBM Corporation
  5  *
  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.
 10  */
 11 
 12 #define pr_fmt(fmt) "drmem: " fmt
 13 
 14 #include <linux/kernel.h>
 15 #include <linux/of.h>
 16 #include <linux/of_fdt.h>
 17 #include <linux/memblock.h>
 18 #include <asm/prom.h>
 19 #include <asm/drmem.h>
 20 
 21 static struct drmem_lmb_info __drmem_info;
 22 struct drmem_lmb_info *drmem_info = &__drmem_info;
 23 
 24 u64 drmem_lmb_memory_max(void)
 25 {
 26         struct drmem_lmb *last_lmb;
 27 
 28         last_lmb = &drmem_info->lmbs[drmem_info->n_lmbs - 1];
 29         return last_lmb->base_addr + drmem_lmb_size();
 30 }
 31 
 32 static u32 drmem_lmb_flags(struct drmem_lmb *lmb)
 33 {
 34         /*
 35          * Return the value of the lmb flags field minus the reserved
 36          * bit used internally for hotplug processing.
 37          */
 38         return lmb->flags & ~DRMEM_LMB_RESERVED;
 39 }
 40 
 41 static struct property *clone_property(struct property *prop, u32 prop_sz)
 42 {
 43         struct property *new_prop;
 44 
 45         new_prop = kzalloc(sizeof(*new_prop), GFP_KERNEL);
 46         if (!new_prop)
 47                 return NULL;
 48 
 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);
 54                 kfree(new_prop);
 55                 return NULL;
 56         }
 57 
 58         new_prop->length = prop_sz;
 59 #if defined(CONFIG_OF_DYNAMIC)
 60         of_property_set_flag(new_prop, OF_DYNAMIC);
 61 #endif
 62         return new_prop;
 63 }
 64 
 65 static int drmem_update_dt_v1(struct device_node *memory,
 66                               struct property *prop)
 67 {
 68         struct property *new_prop;
 69         struct of_drconf_cell_v1 *dr_cell;
 70         struct drmem_lmb *lmb;
 71         u32 *p;
 72 
 73         new_prop = clone_property(prop, prop->length);
 74         if (!new_prop)
 75                 return -1;
 76 
 77         p = new_prop->value;
 78         *p++ = cpu_to_be32(drmem_info->n_lmbs);
 79 
 80         dr_cell = (struct of_drconf_cell_v1 *)p;
 81 
 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));
 87 
 88                 dr_cell++;
 89         }
 90 
 91         of_update_property(memory, new_prop);
 92         return 0;
 93 }
 94 
 95 static void init_drconf_v2_cell(struct of_drconf_cell_v2 *dr_cell,
 96                                 struct drmem_lmb *lmb)
 97 {
 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));
102 }
103 
104 static int drmem_update_dt_v2(struct device_node *memory,
105                               struct property *prop)
106 {
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;
111         u32 *p;
112 
113         /* First pass, determine how many LMB sets are needed. */
114         lmb_sets = 0;
115         prev_lmb = NULL;
116         for_each_drmem_lmb(lmb) {
117                 if (!prev_lmb) {
118                         prev_lmb = lmb;
119                         lmb_sets++;
120                         continue;
121                 }
122 
123                 if (prev_lmb->aa_index != lmb->aa_index ||
124                     drmem_lmb_flags(prev_lmb) != drmem_lmb_flags(lmb))
125                         lmb_sets++;
126 
127                 prev_lmb = lmb;
128         }
129 
130         prop_sz = lmb_sets * sizeof(*dr_cell) + sizeof(__be32);
131         new_prop = clone_property(prop, prop_sz);
132         if (!new_prop)
133                 return -1;
134 
135         p = new_prop->value;
136         *p++ = cpu_to_be32(lmb_sets);
137 
138         dr_cell = (struct of_drconf_cell_v2 *)p;
139 
140         /* Second pass, populate the LMB set data */
141         prev_lmb = NULL;
142         seq_lmbs = 0;
143         for_each_drmem_lmb(lmb) {
144                 if (prev_lmb == NULL) {
145                         /* Start of first LMB set */
146                         prev_lmb = lmb;
147                         init_drconf_v2_cell(dr_cell, lmb);
148                         seq_lmbs++;
149                         continue;
150                 }
151 
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);
156                         dr_cell++;
157 
158                         init_drconf_v2_cell(dr_cell, lmb);
159                         seq_lmbs = 1;
160                 } else {
161                         seq_lmbs++;
162                 }
163 
164                 prev_lmb = lmb;
165         }
166 
167         /* close out last LMB set */
168         dr_cell->seq_lmbs = cpu_to_be32(seq_lmbs);
169         of_update_property(memory, new_prop);
170         return 0;
171 }
172 
173 int drmem_update_dt(void)
174 {
175         struct device_node *memory;
176         struct property *prop;
177         int rc = -1;
178 
179         memory = of_find_node_by_path("/ibm,dynamic-reconfiguration-memory");
180         if (!memory)
181                 return -1;
182 
183         prop = of_find_property(memory, "ibm,dynamic-memory", NULL);
184         if (prop) {
185                 rc = drmem_update_dt_v1(memory, prop);
186         } else {
187                 prop = of_find_property(memory, "ibm,dynamic-memory-v2", NULL);
188                 if (prop)
189                         rc = drmem_update_dt_v2(memory, prop);
190         }
191 
192         of_node_put(memory);
193         return rc;
194 }
195 
196 static void __init read_drconf_v1_cell(struct drmem_lmb *lmb,
197                                        const __be32 **prop)
198 {
199         const __be32 *p = *prop;
200 
201         lmb->base_addr = dt_mem_next_cell(dt_root_addr_cells, &p);
202         lmb->drc_index = of_read_number(p++, 1);
203 
204         p++; /* skip reserved field */
205 
206         lmb->aa_index = of_read_number(p++, 1);
207         lmb->flags = of_read_number(p++, 1);
208 
209         *prop = p;
210 }
211 
212 static void __init __walk_drmem_v1_lmbs(const __be32 *prop, const __be32 *usm,
213                         void (*func)(struct drmem_lmb *, const __be32 **))
214 {
215         struct drmem_lmb lmb;
216         u32 i, n_lmbs;
217 
218         n_lmbs = of_read_number(prop++, 1);
219         if (n_lmbs == 0)
220                 return;
221 
222         for (i = 0; i < n_lmbs; i++) {
223                 read_drconf_v1_cell(&lmb, &prop);
224                 func(&lmb, &usm);
225         }
226 }
227 
228 static void __init read_drconf_v2_cell(struct of_drconf_cell_v2 *dr_cell,
229                                        const __be32 **prop)
230 {
231         const __be32 *p = *prop;
232 
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);
238 
239         *prop = p;
240 }
241 
242 static void __init __walk_drmem_v2_lmbs(const __be32 *prop, const __be32 *usm,
243                         void (*func)(struct drmem_lmb *, const __be32 **))
244 {
245         struct of_drconf_cell_v2 dr_cell;
246         struct drmem_lmb lmb;
247         u32 i, j, lmb_sets;
248 
249         lmb_sets = of_read_number(prop++, 1);
250         if (lmb_sets == 0)
251                 return;
252 
253         for (i = 0; i < lmb_sets; i++) {
254                 read_drconf_v2_cell(&dr_cell, &prop);
255 
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();
259 
260                         lmb.drc_index = dr_cell.drc_index;
261                         dr_cell.drc_index++;
262 
263                         lmb.aa_index = dr_cell.aa_index;
264                         lmb.flags = dr_cell.flags;
265 
266                         func(&lmb, &usm);
267                 }
268         }
269 }
270 
271 #ifdef CONFIG_PPC_PSERIES
272 void __init walk_drmem_lmbs_early(unsigned long node,
273                         void (*func)(struct drmem_lmb *, const __be32 **))
274 {
275         const __be32 *prop, *usm;
276         int len;
277 
278         prop = of_get_flat_dt_prop(node, "ibm,lmb-size", &len);
279         if (!prop || len < dt_root_size_cells * sizeof(__be32))
280                 return;
281 
282         drmem_info->lmb_size = dt_mem_next_cell(dt_root_size_cells, &prop);
283 
284         usm = of_get_flat_dt_prop(node, "linux,drconf-usable-memory", &len);
285 
286         prop = of_get_flat_dt_prop(node, "ibm,dynamic-memory", &len);
287         if (prop) {
288                 __walk_drmem_v1_lmbs(prop, usm, func);
289         } else {
290                 prop = of_get_flat_dt_prop(node, "ibm,dynamic-memory-v2",
291                                            &len);
292                 if (prop)
293                         __walk_drmem_v2_lmbs(prop, usm, func);
294         }
295 
296         memblock_dump_all();
297 }
298 
299 #endif
300 
301 static int __init init_drmem_lmb_size(struct device_node *dn)
302 {
303         const __be32 *prop;
304         int len;
305 
306         if (drmem_info->lmb_size)
307                 return 0;
308 
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");
312                 return -1;
313         }
314 
315         drmem_info->lmb_size = dt_mem_next_cell(dt_root_size_cells, &prop);
316         return 0;
317 }
318 
319 /*
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)
323  */
324 static const __be32 *of_get_usable_memory(struct device_node *dn)
325 {
326         const __be32 *prop;
327         u32 len;
328 
329         prop = of_get_property(dn, "linux,drconf-usable-memory", &len);
330         if (!prop || len < sizeof(unsigned int))
331                 return NULL;
332 
333         return prop;
334 }
335 
336 void __init walk_drmem_lmbs(struct device_node *dn,
337                             void (*func)(struct drmem_lmb *, const __be32 **))
338 {
339         const __be32 *prop, *usm;
340 
341         if (init_drmem_lmb_size(dn))
342                 return;
343 
344         usm = of_get_usable_memory(dn);
345 
346         prop = of_get_property(dn, "ibm,dynamic-memory", NULL);
347         if (prop) {
348                 __walk_drmem_v1_lmbs(prop, usm, func);
349         } else {
350                 prop = of_get_property(dn, "ibm,dynamic-memory-v2", NULL);
351                 if (prop)
352                         __walk_drmem_v2_lmbs(prop, usm, func);
353         }
354 }
355 
356 static void __init init_drmem_v1_lmbs(const __be32 *prop)
357 {
358         struct drmem_lmb *lmb;
359 
360         drmem_info->n_lmbs = of_read_number(prop++, 1);
361         if (drmem_info->n_lmbs == 0)
362                 return;
363 
364         drmem_info->lmbs = kcalloc(drmem_info->n_lmbs, sizeof(*lmb),
365                                    GFP_KERNEL);
366         if (!drmem_info->lmbs)
367                 return;
368 
369         for_each_drmem_lmb(lmb)
370                 read_drconf_v1_cell(lmb, &prop);
371 }
372 
373 static void __init init_drmem_v2_lmbs(const __be32 *prop)
374 {
375         struct drmem_lmb *lmb;
376         struct of_drconf_cell_v2 dr_cell;
377         const __be32 *p;
378         u32 i, j, lmb_sets;
379         int lmb_index;
380 
381         lmb_sets = of_read_number(prop++, 1);
382         if (lmb_sets == 0)
383                 return;
384 
385         /* first pass, calculate the number of LMBs */
386         p = prop;
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;
390         }
391 
392         drmem_info->lmbs = kcalloc(drmem_info->n_lmbs, sizeof(*lmb),
393                                    GFP_KERNEL);
394         if (!drmem_info->lmbs)
395                 return;
396 
397         /* second pass, read in the LMB information */
398         lmb_index = 0;
399         p = prop;
400 
401         for (i = 0; i < lmb_sets; i++) {
402                 read_drconf_v2_cell(&dr_cell, &p);
403 
404                 for (j = 0; j < dr_cell.seq_lmbs; j++) {
405                         lmb = &drmem_info->lmbs[lmb_index++];
406 
407                         lmb->base_addr = dr_cell.base_addr;
408                         dr_cell.base_addr += drmem_info->lmb_size;
409 
410                         lmb->drc_index = dr_cell.drc_index;
411                         dr_cell.drc_index++;
412 
413                         lmb->aa_index = dr_cell.aa_index;
414                         lmb->flags = dr_cell.flags;
415                 }
416         }
417 }
418 
419 static int __init drmem_init(void)
420 {
421         struct device_node *dn;
422         const __be32 *prop;
423 
424         dn = of_find_node_by_path("/ibm,dynamic-reconfiguration-memory");
425         if (!dn) {
426                 pr_info("No dynamic reconfiguration memory found\n");
427                 return 0;
428         }
429 
430         if (init_drmem_lmb_size(dn)) {
431                 of_node_put(dn);
432                 return 0;
433         }
434 
435         prop = of_get_property(dn, "ibm,dynamic-memory", NULL);
436         if (prop) {
437                 init_drmem_v1_lmbs(prop);
438         } else {
439                 prop = of_get_property(dn, "ibm,dynamic-memory-v2", NULL);
440                 if (prop)
441                         init_drmem_v2_lmbs(prop);
442         }
443 
444         of_node_put(dn);
445         return 0;
446 }
447 late_initcall(drmem_init);
448 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp