0001
0002
0003
0004
0005
0006
0007
0008 #define pr_fmt(fmt) "drmem: " fmt
0009
0010 #include <linux/kernel.h>
0011 #include <linux/of.h>
0012 #include <linux/of_fdt.h>
0013 #include <linux/memblock.h>
0014 #include <linux/slab.h>
0015 #include <asm/drmem.h>
0016
0017 static int n_root_addr_cells, n_root_size_cells;
0018
0019 static struct drmem_lmb_info __drmem_info;
0020 struct drmem_lmb_info *drmem_info = &__drmem_info;
0021 static bool in_drmem_update;
0022
0023 u64 drmem_lmb_memory_max(void)
0024 {
0025 struct drmem_lmb *last_lmb;
0026
0027 last_lmb = &drmem_info->lmbs[drmem_info->n_lmbs - 1];
0028 return last_lmb->base_addr + drmem_lmb_size();
0029 }
0030
0031 static u32 drmem_lmb_flags(struct drmem_lmb *lmb)
0032 {
0033
0034
0035
0036
0037 return lmb->flags & ~DRMEM_LMB_RESERVED;
0038 }
0039
0040 static struct property *clone_property(struct property *prop, u32 prop_sz)
0041 {
0042 struct property *new_prop;
0043
0044 new_prop = kzalloc(sizeof(*new_prop), GFP_KERNEL);
0045 if (!new_prop)
0046 return NULL;
0047
0048 new_prop->name = kstrdup(prop->name, GFP_KERNEL);
0049 new_prop->value = kzalloc(prop_sz, GFP_KERNEL);
0050 if (!new_prop->name || !new_prop->value) {
0051 kfree(new_prop->name);
0052 kfree(new_prop->value);
0053 kfree(new_prop);
0054 return NULL;
0055 }
0056
0057 new_prop->length = prop_sz;
0058 #if defined(CONFIG_OF_DYNAMIC)
0059 of_property_set_flag(new_prop, OF_DYNAMIC);
0060 #endif
0061 return new_prop;
0062 }
0063
0064 static int drmem_update_dt_v1(struct device_node *memory,
0065 struct property *prop)
0066 {
0067 struct property *new_prop;
0068 struct of_drconf_cell_v1 *dr_cell;
0069 struct drmem_lmb *lmb;
0070 u32 *p;
0071
0072 new_prop = clone_property(prop, prop->length);
0073 if (!new_prop)
0074 return -1;
0075
0076 p = new_prop->value;
0077 *p++ = cpu_to_be32(drmem_info->n_lmbs);
0078
0079 dr_cell = (struct of_drconf_cell_v1 *)p;
0080
0081 for_each_drmem_lmb(lmb) {
0082 dr_cell->base_addr = cpu_to_be64(lmb->base_addr);
0083 dr_cell->drc_index = cpu_to_be32(lmb->drc_index);
0084 dr_cell->aa_index = cpu_to_be32(lmb->aa_index);
0085 dr_cell->flags = cpu_to_be32(drmem_lmb_flags(lmb));
0086
0087 dr_cell++;
0088 }
0089
0090 of_update_property(memory, new_prop);
0091 return 0;
0092 }
0093
0094 static void init_drconf_v2_cell(struct of_drconf_cell_v2 *dr_cell,
0095 struct drmem_lmb *lmb)
0096 {
0097 dr_cell->base_addr = cpu_to_be64(lmb->base_addr);
0098 dr_cell->drc_index = cpu_to_be32(lmb->drc_index);
0099 dr_cell->aa_index = cpu_to_be32(lmb->aa_index);
0100 dr_cell->flags = cpu_to_be32(drmem_lmb_flags(lmb));
0101 }
0102
0103 static int drmem_update_dt_v2(struct device_node *memory,
0104 struct property *prop)
0105 {
0106 struct property *new_prop;
0107 struct of_drconf_cell_v2 *dr_cell;
0108 struct drmem_lmb *lmb, *prev_lmb;
0109 u32 lmb_sets, prop_sz, seq_lmbs;
0110 u32 *p;
0111
0112
0113 lmb_sets = 0;
0114 prev_lmb = NULL;
0115 for_each_drmem_lmb(lmb) {
0116 if (!prev_lmb) {
0117 prev_lmb = lmb;
0118 lmb_sets++;
0119 continue;
0120 }
0121
0122 if (prev_lmb->aa_index != lmb->aa_index ||
0123 drmem_lmb_flags(prev_lmb) != drmem_lmb_flags(lmb))
0124 lmb_sets++;
0125
0126 prev_lmb = lmb;
0127 }
0128
0129 prop_sz = lmb_sets * sizeof(*dr_cell) + sizeof(__be32);
0130 new_prop = clone_property(prop, prop_sz);
0131 if (!new_prop)
0132 return -1;
0133
0134 p = new_prop->value;
0135 *p++ = cpu_to_be32(lmb_sets);
0136
0137 dr_cell = (struct of_drconf_cell_v2 *)p;
0138
0139
0140 prev_lmb = NULL;
0141 seq_lmbs = 0;
0142 for_each_drmem_lmb(lmb) {
0143 if (prev_lmb == NULL) {
0144
0145 prev_lmb = lmb;
0146 init_drconf_v2_cell(dr_cell, lmb);
0147 seq_lmbs++;
0148 continue;
0149 }
0150
0151 if (prev_lmb->aa_index != lmb->aa_index ||
0152 drmem_lmb_flags(prev_lmb) != drmem_lmb_flags(lmb)) {
0153
0154 dr_cell->seq_lmbs = cpu_to_be32(seq_lmbs);
0155 dr_cell++;
0156
0157 init_drconf_v2_cell(dr_cell, lmb);
0158 seq_lmbs = 1;
0159 } else {
0160 seq_lmbs++;
0161 }
0162
0163 prev_lmb = lmb;
0164 }
0165
0166
0167 dr_cell->seq_lmbs = cpu_to_be32(seq_lmbs);
0168 of_update_property(memory, new_prop);
0169 return 0;
0170 }
0171
0172 int drmem_update_dt(void)
0173 {
0174 struct device_node *memory;
0175 struct property *prop;
0176 int rc = -1;
0177
0178 memory = of_find_node_by_path("/ibm,dynamic-reconfiguration-memory");
0179 if (!memory)
0180 return -1;
0181
0182
0183
0184
0185
0186 in_drmem_update = true;
0187 prop = of_find_property(memory, "ibm,dynamic-memory", NULL);
0188 if (prop) {
0189 rc = drmem_update_dt_v1(memory, prop);
0190 } else {
0191 prop = of_find_property(memory, "ibm,dynamic-memory-v2", NULL);
0192 if (prop)
0193 rc = drmem_update_dt_v2(memory, prop);
0194 }
0195 in_drmem_update = false;
0196
0197 of_node_put(memory);
0198 return rc;
0199 }
0200
0201 static void read_drconf_v1_cell(struct drmem_lmb *lmb,
0202 const __be32 **prop)
0203 {
0204 const __be32 *p = *prop;
0205
0206 lmb->base_addr = of_read_number(p, n_root_addr_cells);
0207 p += n_root_addr_cells;
0208 lmb->drc_index = of_read_number(p++, 1);
0209
0210 p++;
0211
0212 lmb->aa_index = of_read_number(p++, 1);
0213 lmb->flags = of_read_number(p++, 1);
0214
0215 *prop = p;
0216 }
0217
0218 static int
0219 __walk_drmem_v1_lmbs(const __be32 *prop, const __be32 *usm, void *data,
0220 int (*func)(struct drmem_lmb *, const __be32 **, void *))
0221 {
0222 struct drmem_lmb lmb;
0223 u32 i, n_lmbs;
0224 int ret = 0;
0225
0226 n_lmbs = of_read_number(prop++, 1);
0227 for (i = 0; i < n_lmbs; i++) {
0228 read_drconf_v1_cell(&lmb, &prop);
0229 ret = func(&lmb, &usm, data);
0230 if (ret)
0231 break;
0232 }
0233
0234 return ret;
0235 }
0236
0237 static void read_drconf_v2_cell(struct of_drconf_cell_v2 *dr_cell,
0238 const __be32 **prop)
0239 {
0240 const __be32 *p = *prop;
0241
0242 dr_cell->seq_lmbs = of_read_number(p++, 1);
0243 dr_cell->base_addr = of_read_number(p, n_root_addr_cells);
0244 p += n_root_addr_cells;
0245 dr_cell->drc_index = of_read_number(p++, 1);
0246 dr_cell->aa_index = of_read_number(p++, 1);
0247 dr_cell->flags = of_read_number(p++, 1);
0248
0249 *prop = p;
0250 }
0251
0252 static int
0253 __walk_drmem_v2_lmbs(const __be32 *prop, const __be32 *usm, void *data,
0254 int (*func)(struct drmem_lmb *, const __be32 **, void *))
0255 {
0256 struct of_drconf_cell_v2 dr_cell;
0257 struct drmem_lmb lmb;
0258 u32 i, j, lmb_sets;
0259 int ret = 0;
0260
0261 lmb_sets = of_read_number(prop++, 1);
0262 for (i = 0; i < lmb_sets; i++) {
0263 read_drconf_v2_cell(&dr_cell, &prop);
0264
0265 for (j = 0; j < dr_cell.seq_lmbs; j++) {
0266 lmb.base_addr = dr_cell.base_addr;
0267 dr_cell.base_addr += drmem_lmb_size();
0268
0269 lmb.drc_index = dr_cell.drc_index;
0270 dr_cell.drc_index++;
0271
0272 lmb.aa_index = dr_cell.aa_index;
0273 lmb.flags = dr_cell.flags;
0274
0275 ret = func(&lmb, &usm, data);
0276 if (ret)
0277 break;
0278 }
0279 }
0280
0281 return ret;
0282 }
0283
0284 #ifdef CONFIG_PPC_PSERIES
0285 int __init walk_drmem_lmbs_early(unsigned long node, void *data,
0286 int (*func)(struct drmem_lmb *, const __be32 **, void *))
0287 {
0288 const __be32 *prop, *usm;
0289 int len, ret = -ENODEV;
0290
0291 prop = of_get_flat_dt_prop(node, "ibm,lmb-size", &len);
0292 if (!prop || len < dt_root_size_cells * sizeof(__be32))
0293 return ret;
0294
0295
0296 n_root_addr_cells = dt_root_addr_cells;
0297 n_root_size_cells = dt_root_size_cells;
0298
0299 drmem_info->lmb_size = dt_mem_next_cell(dt_root_size_cells, &prop);
0300
0301 usm = of_get_flat_dt_prop(node, "linux,drconf-usable-memory", &len);
0302
0303 prop = of_get_flat_dt_prop(node, "ibm,dynamic-memory", &len);
0304 if (prop) {
0305 ret = __walk_drmem_v1_lmbs(prop, usm, data, func);
0306 } else {
0307 prop = of_get_flat_dt_prop(node, "ibm,dynamic-memory-v2",
0308 &len);
0309 if (prop)
0310 ret = __walk_drmem_v2_lmbs(prop, usm, data, func);
0311 }
0312
0313 memblock_dump_all();
0314 return ret;
0315 }
0316
0317
0318
0319
0320 static int update_lmb(struct drmem_lmb *updated_lmb,
0321 __maybe_unused const __be32 **usm,
0322 __maybe_unused void *data)
0323 {
0324 struct drmem_lmb *lmb;
0325
0326 for_each_drmem_lmb(lmb) {
0327 if (lmb->drc_index != updated_lmb->drc_index)
0328 continue;
0329
0330 lmb->aa_index = updated_lmb->aa_index;
0331 break;
0332 }
0333 return 0;
0334 }
0335
0336
0337
0338
0339
0340
0341
0342 void drmem_update_lmbs(struct property *prop)
0343 {
0344
0345
0346
0347
0348
0349 if (in_drmem_update)
0350 return;
0351 if (!strcmp(prop->name, "ibm,dynamic-memory"))
0352 __walk_drmem_v1_lmbs(prop->value, NULL, NULL, update_lmb);
0353 else if (!strcmp(prop->name, "ibm,dynamic-memory-v2"))
0354 __walk_drmem_v2_lmbs(prop->value, NULL, NULL, update_lmb);
0355 }
0356 #endif
0357
0358 static int init_drmem_lmb_size(struct device_node *dn)
0359 {
0360 const __be32 *prop;
0361 int len;
0362
0363 if (drmem_info->lmb_size)
0364 return 0;
0365
0366 prop = of_get_property(dn, "ibm,lmb-size", &len);
0367 if (!prop || len < n_root_size_cells * sizeof(__be32)) {
0368 pr_info("Could not determine LMB size\n");
0369 return -1;
0370 }
0371
0372 drmem_info->lmb_size = of_read_number(prop, n_root_size_cells);
0373 return 0;
0374 }
0375
0376
0377
0378
0379
0380
0381 static const __be32 *of_get_usable_memory(struct device_node *dn)
0382 {
0383 const __be32 *prop;
0384 u32 len;
0385
0386 prop = of_get_property(dn, "linux,drconf-usable-memory", &len);
0387 if (!prop || len < sizeof(unsigned int))
0388 return NULL;
0389
0390 return prop;
0391 }
0392
0393 int walk_drmem_lmbs(struct device_node *dn, void *data,
0394 int (*func)(struct drmem_lmb *, const __be32 **, void *))
0395 {
0396 const __be32 *prop, *usm;
0397 int ret = -ENODEV;
0398
0399 if (!of_root)
0400 return ret;
0401
0402
0403 of_node_get(of_root);
0404 n_root_addr_cells = of_n_addr_cells(of_root);
0405 n_root_size_cells = of_n_size_cells(of_root);
0406 of_node_put(of_root);
0407
0408 if (init_drmem_lmb_size(dn))
0409 return ret;
0410
0411 usm = of_get_usable_memory(dn);
0412
0413 prop = of_get_property(dn, "ibm,dynamic-memory", NULL);
0414 if (prop) {
0415 ret = __walk_drmem_v1_lmbs(prop, usm, data, func);
0416 } else {
0417 prop = of_get_property(dn, "ibm,dynamic-memory-v2", NULL);
0418 if (prop)
0419 ret = __walk_drmem_v2_lmbs(prop, usm, data, func);
0420 }
0421
0422 return ret;
0423 }
0424
0425 static void __init init_drmem_v1_lmbs(const __be32 *prop)
0426 {
0427 struct drmem_lmb *lmb;
0428
0429 drmem_info->n_lmbs = of_read_number(prop++, 1);
0430 if (drmem_info->n_lmbs == 0)
0431 return;
0432
0433 drmem_info->lmbs = kcalloc(drmem_info->n_lmbs, sizeof(*lmb),
0434 GFP_KERNEL);
0435 if (!drmem_info->lmbs)
0436 return;
0437
0438 for_each_drmem_lmb(lmb)
0439 read_drconf_v1_cell(lmb, &prop);
0440 }
0441
0442 static void __init init_drmem_v2_lmbs(const __be32 *prop)
0443 {
0444 struct drmem_lmb *lmb;
0445 struct of_drconf_cell_v2 dr_cell;
0446 const __be32 *p;
0447 u32 i, j, lmb_sets;
0448 int lmb_index;
0449
0450 lmb_sets = of_read_number(prop++, 1);
0451 if (lmb_sets == 0)
0452 return;
0453
0454
0455 p = prop;
0456 for (i = 0; i < lmb_sets; i++) {
0457 read_drconf_v2_cell(&dr_cell, &p);
0458 drmem_info->n_lmbs += dr_cell.seq_lmbs;
0459 }
0460
0461 drmem_info->lmbs = kcalloc(drmem_info->n_lmbs, sizeof(*lmb),
0462 GFP_KERNEL);
0463 if (!drmem_info->lmbs)
0464 return;
0465
0466
0467 lmb_index = 0;
0468 p = prop;
0469
0470 for (i = 0; i < lmb_sets; i++) {
0471 read_drconf_v2_cell(&dr_cell, &p);
0472
0473 for (j = 0; j < dr_cell.seq_lmbs; j++) {
0474 lmb = &drmem_info->lmbs[lmb_index++];
0475
0476 lmb->base_addr = dr_cell.base_addr;
0477 dr_cell.base_addr += drmem_info->lmb_size;
0478
0479 lmb->drc_index = dr_cell.drc_index;
0480 dr_cell.drc_index++;
0481
0482 lmb->aa_index = dr_cell.aa_index;
0483 lmb->flags = dr_cell.flags;
0484 }
0485 }
0486 }
0487
0488 static int __init drmem_init(void)
0489 {
0490 struct device_node *dn;
0491 const __be32 *prop;
0492
0493 dn = of_find_node_by_path("/ibm,dynamic-reconfiguration-memory");
0494 if (!dn) {
0495 pr_info("No dynamic reconfiguration memory found\n");
0496 return 0;
0497 }
0498
0499 if (init_drmem_lmb_size(dn)) {
0500 of_node_put(dn);
0501 return 0;
0502 }
0503
0504 prop = of_get_property(dn, "ibm,dynamic-memory", NULL);
0505 if (prop) {
0506 init_drmem_v1_lmbs(prop);
0507 } else {
0508 prop = of_get_property(dn, "ibm,dynamic-memory-v2", NULL);
0509 if (prop)
0510 init_drmem_v2_lmbs(prop);
0511 }
0512
0513 of_node_put(dn);
0514 return 0;
0515 }
0516 late_initcall(drmem_init);