/*
 * Copyright (c) 2021, Carlo Caione <ccaione@baylibre.com>
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Generate memory regions and sections from reserved-memory nodes.
 */

#include <devicetree.h>

/* Reserved memory node */
#define _NODE_RESERVED DT_INST(0, reserved_memory)

/* Unquoted region label */
#define _DT_LABEL_TOKEN(res) DT_STRING_TOKEN(res, label)

/* _start and _end section symbols */
#define _DT_RESERVED_PREFIX(res) UTIL_CAT(__, _DT_LABEL_TOKEN(res))
#define _DT_RESERVED_START(res)  UTIL_CAT(_DT_RESERVED_PREFIX(res), _start)
#define _DT_RESERVED_END(res)    UTIL_CAT(_DT_RESERVED_PREFIX(res), _end)

/* Declare a reserved memory region */
#define _RESERVED_REGION_DECLARE(res) DT_STRING_TOKEN(res, label) (rw) :	\
				      ORIGIN = DT_REG_ADDR(res),		\
				      LENGTH = DT_REG_SIZE(res)

/* Declare a reserved memory section */
#define _RESERVED_SECTION_DECLARE(res) SECTION_DATA_PROLOGUE(_DT_LABEL_TOKEN(res), ,)	\
				       {						\
					  _DT_RESERVED_START(res) = .;			\
					  KEEP(*(._DT_LABEL_TOKEN(res)))		\
					  KEEP(*(._DT_LABEL_TOKEN(res).*))		\
					  _DT_RESERVED_END(res) =			\
					  _DT_RESERVED_START(res) + DT_REG_SIZE(res);	\
				       } GROUP_LINK_IN(_DT_LABEL_TOKEN(res))

/* Declare reserved memory linker symbols */
#define _RESERVED_SYMBOL_DECLARE(res) extern char _DT_RESERVED_START(res)[];	\
				      extern char _DT_RESERVED_END(res)[];

/* Apply a macro to a reserved memory region */
#define _RESERVED_REGION_APPLY(f)						\
	COND_CODE_1(IS_ENABLED(UTIL_CAT(_NODE_RESERVED, _EXISTS)),		\
		    (DT_FOREACH_CHILD(_NODE_RESERVED, f)), ())

/**
 * @brief Generate region definitions for all the reserved memory regions
 */
#define LINKER_DT_RESERVED_MEM_REGIONS() _RESERVED_REGION_APPLY(_RESERVED_REGION_DECLARE)

/**
 * @brief Generate section definitions for all the reserved memory regions
 */
#define LINKER_DT_RESERVED_MEM_SECTIONS() _RESERVED_REGION_APPLY(_RESERVED_SECTION_DECLARE)

/**
 * @brief Generate linker script symbols for all the reserved memory regions
 */
#define LINKER_DT_RESERVED_MEM_SYMBOLS() _RESERVED_REGION_APPLY(_RESERVED_SYMBOL_DECLARE)

/**
 * @brief Get the pointer to the reserved-memory region
 *
 * Example devicetree fragment:
 *
 *     reserved: reserved-memory {
 *         compatible = "reserved-memory";
 *         ...
 *         n: node {
 *             reg = <0x42000000 0x1000>;
 *         };
 *      };
 *
 * Example usage:
 *
 *     LINKER_DT_RESERVED_MEM_GET_PTR(DT_NODELABEL(n)) // (uint8_t *) 0x42000000
 *
 * @param node_id node identifier
 * @return pointer to the beginning of the reserved-memory region
 */
#define LINKER_DT_RESERVED_MEM_GET_PTR(node_id) _DT_RESERVED_START(node_id)

/**
 * @brief Get the size of the reserved-memory region
 *
 * Example devicetree fragment:
 *
 *     reserved: reserved-memory {
 *         compatible = "reserved-memory";
 *         ...
 *         n: node {
 *             reg = <0x42000000 0x1000>;
 *         };
 *     };
 *
 * Example usage:
 *
 *     LINKER_DT_RESERVED_MEM_GET_SIZE(DT_NODELABEL(n)) // 0x1000
 *
 * @param node_id node identifier
 * @return the size of the reserved-memory region
 */
#define LINKER_DT_RESERVED_MEM_GET_SIZE(node_id) DT_REG_SIZE(node_id)

/**
 * @brief Get the pointer to the reserved-memory region from a memory-reserved
 *        phandle
 *
 * Example devicetree fragment:
 *
 *     reserved: reserved-memory {
 *         compatible = "reserved-memory";
 *         ...
 *         res0: res {
 *             reg = <0x42000000 0x1000>;
 *             label = "res0";
 *         };
 *     };
 *
 *     n: node {
 *         memory-region = <&res0>;
 *     };
 *
 * Example usage:
 *
 *     LINKER_DT_RESERVED_MEM_GET_PTR_BY_PHANDLE(DT_NODELABEL(n), \
 *						 memory_region) // (uint8_t *) 0x42000000
 *
 * @param node_id node identifier
 * @param ph phandle to reserved-memory region
 *
 * @return pointer to the beginning of the reserved-memory region
 */
#define LINKER_DT_RESERVED_MEM_GET_PTR_BY_PHANDLE(node_id, ph) \
	LINKER_DT_RESERVED_MEM_GET_PTR(DT_PHANDLE(node_id, ph))

/**
 * @brief Get the size of the reserved-memory region from a memory-reserved
 *        phandle
 *
 * Example devicetree fragment:
 *
 *     reserved: reserved-memory {
 *         compatible = "reserved-memory";
 *         ...
 *         res0: res {
 *             reg = <0x42000000 0x1000>;
 *             label = "res0";
 *         };
 *     };
 *
 *     n: node {
 *         memory-region = <&res0>;
 *     };
 *
 * Example usage:
 *
 *     LINKER_DT_RESERVED_MEM_GET_SIZE_BY_PHANDLE(DT_NODELABEL(n), \
 *						  memory_region) // (uint8_t *) 0x42000000
 *
 * @param node_id node identifier
 * @param ph phandle to reserved-memory region
 *
 * @return size of the reserved-memory region
 */
#define LINKER_DT_RESERVED_MEM_GET_SIZE_BY_PHANDLE(node_id, ph) \
	LINKER_DT_RESERVED_MEM_GET_SIZE(DT_PHANDLE(node_id, ph))
