static int sram_reserve_regions()

in sram.c [173:338]


static int sram_reserve_regions(struct sram_dev *sram, struct resource *res)
{
	struct device_node *np = sram->dev->of_node, *child;
	unsigned long size, cur_start, cur_size;
	struct sram_reserve *rblocks, *block;
	struct list_head reserve_list;
	unsigned int nblocks, exports = 0;
	const char *label;
	int ret = 0;

	INIT_LIST_HEAD(&reserve_list);

	size = resource_size(res);

	/*
	 * We need an additional block to mark the end of the memory region
	 * after the reserved blocks from the dt are processed.
	 */
	nblocks = (np) ? of_get_available_child_count(np) + 1 : 1;
	rblocks = kcalloc(nblocks, sizeof(*rblocks), GFP_KERNEL);
	if (!rblocks)
		return -ENOMEM;

	block = &rblocks[0];
	for_each_available_child_of_node(np, child) {
		struct resource child_res;

		ret = of_address_to_resource(child, 0, &child_res);
		if (ret < 0) {
			dev_err(sram->dev,
				"could not get address for node %pOF\n",
				child);
			goto err_chunks;
		}

		if (child_res.start < res->start || child_res.end > res->end) {
			dev_err(sram->dev,
				"reserved block %pOF outside the sram area\n",
				child);
			ret = -EINVAL;
			goto err_chunks;
		}

		block->start = child_res.start - res->start;
		block->size = resource_size(&child_res);
		block->res = child_res;
		list_add_tail(&block->list, &reserve_list);

		if (of_find_property(child, "export", NULL))
			block->export = true;

		if (of_find_property(child, "pool", NULL))
			block->pool = true;

		if (of_find_property(child, "protect-exec", NULL))
			block->protect_exec = true;

		if ((block->export || block->pool || block->protect_exec) &&
		    block->size) {
			exports++;

			label = NULL;
			ret = of_property_read_string(child, "label", &label);
			if (ret && ret != -EINVAL) {
				dev_err(sram->dev,
					"%pOF has invalid label name\n",
					child);
				goto err_chunks;
			}
			if (!label)
				label = child->name;

			block->label = devm_kstrdup(sram->dev,
						    label, GFP_KERNEL);
			if (!block->label) {
				ret = -ENOMEM;
				goto err_chunks;
			}

			dev_dbg(sram->dev, "found %sblock '%s' 0x%x-0x%x\n",
				block->export ? "exported " : "", block->label,
				block->start, block->start + block->size);
		} else {
			dev_dbg(sram->dev, "found reserved block 0x%x-0x%x\n",
				block->start, block->start + block->size);
		}

		block++;
	}
	child = NULL;

	/* the last chunk marks the end of the region */
	rblocks[nblocks - 1].start = size;
	rblocks[nblocks - 1].size = 0;
	list_add_tail(&rblocks[nblocks - 1].list, &reserve_list);

	list_sort(NULL, &reserve_list, sram_reserve_cmp);

	if (exports) {
		sram->partition = devm_kcalloc(sram->dev,
				       exports, sizeof(*sram->partition),
				       GFP_KERNEL);
		if (!sram->partition) {
			ret = -ENOMEM;
			goto err_chunks;
		}
	}

	cur_start = 0;
	list_for_each_entry(block, &reserve_list, list) {
		/* can only happen if sections overlap */
		if (block->start < cur_start) {
			dev_err(sram->dev,
				"block at 0x%x starts after current offset 0x%lx\n",
				block->start, cur_start);
			ret = -EINVAL;
			sram_free_partitions(sram);
			goto err_chunks;
		}

		if ((block->export || block->pool || block->protect_exec) &&
		    block->size) {
			ret = sram_add_partition(sram, block,
						 res->start + block->start);
			if (ret) {
				sram_free_partitions(sram);
				goto err_chunks;
			}
		}

		/* current start is in a reserved block, so continue after it */
		if (block->start == cur_start) {
			cur_start = block->start + block->size;
			continue;
		}

		/*
		 * allocate the space between the current starting
		 * address and the following reserved block, or the
		 * end of the region.
		 */
		cur_size = block->start - cur_start;

		if (sram->pool) {
			dev_dbg(sram->dev, "adding chunk 0x%lx-0x%lx\n",
				cur_start, cur_start + cur_size);

			ret = gen_pool_add_virt(sram->pool,
					(unsigned long)sram->virt_base + cur_start,
					res->start + cur_start, cur_size, -1);
			if (ret < 0) {
				sram_free_partitions(sram);
				goto err_chunks;
			}
		}

		/* next allocation after this reserved block */
		cur_start = block->start + block->size;
	}

err_chunks:
	of_node_put(child);
	kfree(rblocks);

	return ret;
}