/*******************************************************************************
 * This function takes the base address of the CCN's programmer's view (PV), a
 * region ID of one of the 256 regions (0-255) and a register offset within the
 * region. It converts the first two parameters into a base address and uses it
 * to read the register at the offset.
 ******************************************************************************/
static inline unsigned long long ccn_reg_read(uintptr_t periphbase,
			     unsigned int region_id,
			     unsigned int register_offset)
{
	uintptr_t region_base;

	assert(periphbase);
	assert(region_id < REGION_ID_LIMIT);

	region_base = periphbase + region_id_to_base(region_id);
	return mmio_read_64(region_base + register_offset);
}
/*
 * function: read parameters info(ns-regions) and try to parse s-regions info
 *
 * @addr: head address to the ddr usage struct from miniloader
 * @max_mb: the max ddr capacity(MB) that the platform support
 */
struct param_ddr_usage ddr_region_usage_parse(uint64_t addr, uint64_t max_mb)
{
	uint64_t base, top;
	uint32_t i, addr_offset, size_offset;
	struct param_ddr_usage p;

	memset(&p, 0, sizeof(p));

	/* read how many blocks of ns-regions, read from offset: 0x0 */
	p.ns_nr = mmio_read_32(addr + REGION_NR_OFFSET);
	if ((p.ns_nr > DDR_REGION_NR_MAX) || (p.ns_nr == 0)) {
		ERROR("over or zero region, nr=%d, max=%d\n",
		      p.ns_nr, DDR_REGION_NR_MAX);
		return p;
	}

	/* whole ddr regions boundary, it will be used when parse s-regions */
	p.boundary = max_mb;

	/* calculate ns-region base addr and size offset */
	addr_offset = REGION_ADDR_OFFSET;
	size_offset = REGION_ADDR_OFFSET + p.ns_nr * REGION_DATA_PER_BYTES;

	/* read all ns-regions base and top address */
	for (i = 0; i < p.ns_nr; i++) {
		base = mmio_read_64(addr + addr_offset);
		top = base + mmio_read_64(addr + size_offset);
		/*
		 * translate byte to MB and store info,
		 * Miniloader will promise every ns-region is MB aligned.
		 */
		p.ns_base[i] = RG_SIZE_MB(base);
		p.ns_top[i] = RG_SIZE_MB(top);

		addr_offset += REGION_DATA_PER_BYTES;
		size_offset += REGION_DATA_PER_BYTES;
	}

	/*
	 * a s-region's base starts from previous ns-region's top, and a
	 * s-region's top ends with next ns-region's base. maybe like this:
	 *
	 *	   case1: ns-regison start from 0MB
	 *	 -----------------------------------------------
	 *	 |    ns0   |  S0  |  ns1  |   S1  |    ns2    |
	 *	0----------------------------------------------- max_mb
	 *
	 *
	 *	   case2: ns-regison not start from 0MB
	 *	 -----------------------------------------------
	 *	 |    S0   |  ns0  |  ns1  |   ns2  |    S1    |
	 *	0----------------------------------------------- max_mb
	 */

	/* like above case2 figure, ns-region is not start from 0MB */
	if (p.ns_base[0] != 0) {
		p.s_base[p.s_nr] = 0;
		p.s_top[p.s_nr] = p.ns_base[0];
		p.s_nr++;
	}

	/*
	 * notice: if ns-regions not start from 0MB, p.s_nr = 1 now, otherwise 0
	 */
	for (i = 0; i < p.ns_nr; i++) {
		/*
		 * if current ns-regions top covers boundary,
		 * that means s-regions are all parsed yet, so finsh.
		 */
		if (p.ns_top[i] == p.boundary)
			goto out;

		/* s-region's base starts from previous ns-region's top */
		p.s_base[p.s_nr] = p.ns_top[i];

		/* s-region's top ends with next ns-region's base */
		if (i + 1 < p.ns_nr)
			p.s_top[p.s_nr] = p.ns_base[i + 1];
		else
			p.s_top[p.s_nr] = p.boundary;
		p.s_nr++;
	}
out:
	return p;
}