mirror of
				https://github.com/Telecominfraproject/OpenCellular.git
				synced 2025-11-03 20:07:51 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			313 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			ReStructuredText
		
	
	
	
	
	
			
		
		
	
	
			313 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			ReStructuredText
		
	
	
	
	
	
PSCI Library Integration guide for Armv8-A AArch32 systems
 | 
						|
==========================================================
 | 
						|
 | 
						|
 | 
						|
.. section-numbering::
 | 
						|
    :suffix: .
 | 
						|
 | 
						|
.. contents::
 | 
						|
 | 
						|
--------------
 | 
						|
 | 
						|
Requirements
 | 
						|
------------
 | 
						|
 | 
						|
#. A platform must export the ``plat_get_aff_count()`` and
 | 
						|
   ``plat_get_aff_state()`` APIs to enable the generic PSCI code to
 | 
						|
   populate a tree that describes the hierarchy of power domains in the
 | 
						|
   system. This approach is inflexible because a change to the topology
 | 
						|
   requires a change in the code.
 | 
						|
 | 
						|
   It would be much simpler for the platform to describe its power domain tree
 | 
						|
   in a data structure.
 | 
						|
 | 
						|
#. The generic PSCI code generates MPIDRs in order to populate the power domain
 | 
						|
   tree. It also uses an MPIDR to find a node in the tree. The assumption that
 | 
						|
   a platform will use exactly the same MPIDRs as generated by the generic PSCI
 | 
						|
   code is not scalable. The use of an MPIDR also restricts the number of
 | 
						|
   levels in the power domain tree to four.
 | 
						|
 | 
						|
   Therefore, there is a need to decouple allocation of MPIDRs from the
 | 
						|
   mechanism used to populate the power domain topology tree.
 | 
						|
 | 
						|
#. The current arrangement of the power domain tree requires a binary search
 | 
						|
   over the sibling nodes at a particular level to find a specified power
 | 
						|
   domain node. During a power management operation, the tree is traversed from
 | 
						|
   a 'start' to an 'end' power level. The binary search is required to find the
 | 
						|
   node at each level. The natural way to perform this traversal is to
 | 
						|
   start from a leaf node and follow the parent node pointer to reach the end
 | 
						|
   level.
 | 
						|
 | 
						|
   Therefore, there is a need to define data structures that implement the tree in
 | 
						|
   a way which facilitates such a traversal.
 | 
						|
 | 
						|
#. The attributes of a core power domain differ from the attributes of power
 | 
						|
   domains at higher levels. For example, only a core power domain can be identified
 | 
						|
   using an MPIDR. There is no requirement to perform state coordination while
 | 
						|
   performing a power management operation on the core power domain.
 | 
						|
 | 
						|
   Therefore, there is a need to implement the tree in a way which facilitates this
 | 
						|
   distinction between a leaf and non-leaf node and any associated
 | 
						|
   optimizations.
 | 
						|
 | 
						|
--------------
 | 
						|
 | 
						|
Design
 | 
						|
------
 | 
						|
 | 
						|
Describing a power domain tree
 | 
						|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | 
						|
 | 
						|
To fulfill requirement 1., the existing platform APIs
 | 
						|
``plat_get_aff_count()`` and ``plat_get_aff_state()`` have been
 | 
						|
removed. A platform must define an array of unsigned chars such that:
 | 
						|
 | 
						|
#. The first entry in the array specifies the number of power domains at the
 | 
						|
   highest power level implemented in the platform. This caters for platforms
 | 
						|
   where the power domain tree does not have a single root node, for example,
 | 
						|
   the FVP has two cluster power domains at the highest level (1).
 | 
						|
 | 
						|
#. Each subsequent entry corresponds to a power domain and contains the number
 | 
						|
   of power domains that are its direct children.
 | 
						|
 | 
						|
#. The size of the array minus the first entry will be equal to the number of
 | 
						|
   non-leaf power domains.
 | 
						|
 | 
						|
#. The value in each entry in the array is used to find the number of entries
 | 
						|
   to consider at the next level. The sum of the values (number of children) of
 | 
						|
   all the entries at a level specifies the number of entries in the array for
 | 
						|
   the next level.
 | 
						|
 | 
						|
The following example power domain topology tree will be used to describe the
 | 
						|
above text further. The leaf and non-leaf nodes in this tree have been numbered
 | 
						|
separately.
 | 
						|
 | 
						|
::
 | 
						|
 | 
						|
                                         +-+
 | 
						|
                                         |0|
 | 
						|
                                         +-+
 | 
						|
                                        /   \
 | 
						|
                                       /     \
 | 
						|
                                      /       \
 | 
						|
                                     /         \
 | 
						|
                                    /           \
 | 
						|
                                   /             \
 | 
						|
                                  /               \
 | 
						|
                                 /                 \
 | 
						|
                                /                   \
 | 
						|
                               /                     \
 | 
						|
                            +-+                       +-+
 | 
						|
                            |1|                       |2|
 | 
						|
                            +-+                       +-+
 | 
						|
                           /   \                     /   \
 | 
						|
                          /     \                   /     \
 | 
						|
                         /       \                 /       \
 | 
						|
                        /         \               /         \
 | 
						|
                     +-+           +-+         +-+           +-+
 | 
						|
                     |3|           |4|         |5|           |6|
 | 
						|
                     +-+           +-+         +-+           +-+
 | 
						|
            +---+-----+    +----+----|     +----+----+     +----+-----+-----+
 | 
						|
            |   |     |    |    |    |     |    |    |     |    |     |     |
 | 
						|
            |   |     |    |    |    |     |    |    |     |    |     |     |
 | 
						|
            v   v     v    v    v    v     v    v    v     v    v     v     v
 | 
						|
          +-+  +-+   +-+  +-+  +-+  +-+   +-+  +-+  +-+   +-+  +--+  +--+  +--+
 | 
						|
          |0|  |1|   |2|  |3|  |4|  |5|   |6|  |7|  |8|   |9|  |10|  |11|  |12|
 | 
						|
          +-+  +-+   +-+  +-+  +-+  +-+   +-+  +-+  +-+   +-+  +--+  +--+  +--+
 | 
						|
 | 
						|
This tree is defined by the platform as the array described above as follows:
 | 
						|
 | 
						|
::
 | 
						|
 | 
						|
        #define PLAT_NUM_POWER_DOMAINS       20
 | 
						|
        #define PLATFORM_CORE_COUNT          13
 | 
						|
        #define PSCI_NUM_NON_CPU_PWR_DOMAINS \
 | 
						|
                           (PLAT_NUM_POWER_DOMAINS - PLATFORM_CORE_COUNT)
 | 
						|
 | 
						|
        unsigned char plat_power_domain_tree_desc[] = { 1, 2, 2, 2, 3, 3, 3, 4};
 | 
						|
 | 
						|
Removing assumptions about MPIDRs used in a platform
 | 
						|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | 
						|
 | 
						|
To fulfill requirement 2., it is assumed that the platform assigns a
 | 
						|
unique number (core index) between ``0`` and ``PLAT_CORE_COUNT - 1`` to each core
 | 
						|
power domain. MPIDRs could be allocated in any manner and will not be used to
 | 
						|
populate the tree.
 | 
						|
 | 
						|
``plat_core_pos_by_mpidr(mpidr)`` will return the core index for the core
 | 
						|
corresponding to the MPIDR. It will return an error (-1) if an MPIDR is passed
 | 
						|
which is not allocated or corresponds to an absent core. The semantics of this
 | 
						|
platform API have changed since it is required to validate the passed MPIDR. It
 | 
						|
has been made a mandatory API as a result.
 | 
						|
 | 
						|
Another mandatory API, ``plat_my_core_pos()`` has been added to return the core
 | 
						|
index for the calling core. This API provides a more lightweight mechanism to get
 | 
						|
the index since there is no need to validate the MPIDR of the calling core.
 | 
						|
 | 
						|
The platform should assign the core indices (as illustrated in the diagram above)
 | 
						|
such that, if the core nodes are numbered from left to right, then the index
 | 
						|
for a core domain will be the same as the index returned by
 | 
						|
``plat_core_pos_by_mpidr()`` or ``plat_my_core_pos()`` for that core. This
 | 
						|
relationship allows the core nodes to be allocated in a separate array
 | 
						|
(requirement 4.) during ``psci_setup()`` in such an order that the index of the
 | 
						|
core in the array is the same as the return value from these APIs.
 | 
						|
 | 
						|
Dealing with holes in MPIDR allocation
 | 
						|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 | 
						|
 | 
						|
For platforms where the number of allocated MPIDRs is equal to the number of
 | 
						|
core power domains, for example, Juno and FVPs, the logic to convert an MPIDR to
 | 
						|
a core index should remain unchanged. Both Juno and FVP use a simple collision
 | 
						|
proof hash function to do this.
 | 
						|
 | 
						|
It is possible that on some platforms, the allocation of MPIDRs is not
 | 
						|
contiguous or certain cores have been disabled. This essentially means that the
 | 
						|
MPIDRs have been sparsely allocated, that is, the size of the range of MPIDRs
 | 
						|
used by the platform is not equal to the number of core power domains.
 | 
						|
 | 
						|
The platform could adopt one of the following approaches to deal with this
 | 
						|
scenario:
 | 
						|
 | 
						|
#. Implement more complex logic to convert a valid MPIDR to a core index while
 | 
						|
   maintaining the relationship described earlier. This means that the power
 | 
						|
   domain tree descriptor will not describe any core power domains which are
 | 
						|
   disabled or absent. Entries will not be allocated in the tree for these
 | 
						|
   domains.
 | 
						|
 | 
						|
#. Treat unallocated MPIDRs and disabled cores as absent but still describe them
 | 
						|
   in the power domain descriptor, that is, the number of core nodes described
 | 
						|
   is equal to the size of the range of MPIDRs allocated. This approach will
 | 
						|
   lead to memory wastage since entries will be allocated in the tree but will
 | 
						|
   allow use of a simpler logic to convert an MPIDR to a core index.
 | 
						|
 | 
						|
Traversing through and distinguishing between core and non-core power domains
 | 
						|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | 
						|
 | 
						|
To fulfill requirement 3 and 4, separate data structures have been defined
 | 
						|
to represent leaf and non-leaf power domain nodes in the tree.
 | 
						|
 | 
						|
.. code:: c
 | 
						|
 | 
						|
    /*******************************************************************************
 | 
						|
     * The following two data structures implement the power domain tree. The tree
 | 
						|
     * is used to track the state of all the nodes i.e. power domain instances
 | 
						|
     * described by the platform. The tree consists of nodes that describe CPU power
 | 
						|
     * domains i.e. leaf nodes and all other power domains which are parents of a
 | 
						|
     * CPU power domain i.e. non-leaf nodes.
 | 
						|
     ******************************************************************************/
 | 
						|
    typedef struct non_cpu_pwr_domain_node {
 | 
						|
        /*
 | 
						|
         * Index of the first CPU power domain node level 0 which has this node
 | 
						|
         * as its parent.
 | 
						|
         */
 | 
						|
        unsigned int cpu_start_idx;
 | 
						|
 | 
						|
        /*
 | 
						|
         * Number of CPU power domains which are siblings of the domain indexed
 | 
						|
         * by 'cpu_start_idx' i.e. all the domains in the range 'cpu_start_idx
 | 
						|
         * -> cpu_start_idx + ncpus' have this node as their parent.
 | 
						|
         */
 | 
						|
        unsigned int ncpus;
 | 
						|
 | 
						|
        /* Index of the parent power domain node */
 | 
						|
        unsigned int parent_node;
 | 
						|
 | 
						|
        -----
 | 
						|
    } non_cpu_pd_node_t;
 | 
						|
 | 
						|
    typedef struct cpu_pwr_domain_node {
 | 
						|
        u_register_t mpidr;
 | 
						|
 | 
						|
        /* Index of the parent power domain node */
 | 
						|
        unsigned int parent_node;
 | 
						|
 | 
						|
        -----
 | 
						|
    } cpu_pd_node_t;
 | 
						|
 | 
						|
The power domain tree is implemented as a combination of the following data
 | 
						|
structures.
 | 
						|
 | 
						|
::
 | 
						|
 | 
						|
    non_cpu_pd_node_t psci_non_cpu_pd_nodes[PSCI_NUM_NON_CPU_PWR_DOMAINS];
 | 
						|
    cpu_pd_node_t psci_cpu_pd_nodes[PLATFORM_CORE_COUNT];
 | 
						|
 | 
						|
Populating the power domain tree
 | 
						|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | 
						|
 | 
						|
The ``populate_power_domain_tree()`` function in ``psci_setup.c`` implements the
 | 
						|
algorithm to parse the power domain descriptor exported by the platform to
 | 
						|
populate the two arrays. It is essentially a breadth-first-search. The nodes for
 | 
						|
each level starting from the root are laid out one after another in the
 | 
						|
``psci_non_cpu_pd_nodes`` and ``psci_cpu_pd_nodes`` arrays as follows:
 | 
						|
 | 
						|
::
 | 
						|
 | 
						|
    psci_non_cpu_pd_nodes -> [[Level 3 nodes][Level 2 nodes][Level 1 nodes]]
 | 
						|
    psci_cpu_pd_nodes -> [Level 0 nodes]
 | 
						|
 | 
						|
For the example power domain tree illustrated above, the ``psci_cpu_pd_nodes``
 | 
						|
will be populated as follows. The value in each entry is the index of the parent
 | 
						|
node. Other fields have been ignored for simplicity.
 | 
						|
 | 
						|
::
 | 
						|
 | 
						|
                          +-------------+     ^
 | 
						|
                    CPU0  |      3      |     |
 | 
						|
                          +-------------+     |
 | 
						|
                    CPU1  |      3      |     |
 | 
						|
                          +-------------+     |
 | 
						|
                    CPU2  |      3      |     |
 | 
						|
                          +-------------+     |
 | 
						|
                    CPU3  |      4      |     |
 | 
						|
                          +-------------+     |
 | 
						|
                    CPU4  |      4      |     |
 | 
						|
                          +-------------+     |
 | 
						|
                    CPU5  |      4      |     | PLATFORM_CORE_COUNT
 | 
						|
                          +-------------+     |
 | 
						|
                    CPU6  |      5      |     |
 | 
						|
                          +-------------+     |
 | 
						|
                    CPU7  |      5      |     |
 | 
						|
                          +-------------+     |
 | 
						|
                    CPU8  |      5      |     |
 | 
						|
                          +-------------+     |
 | 
						|
                    CPU9  |      6      |     |
 | 
						|
                          +-------------+     |
 | 
						|
                    CPU10 |      6      |     |
 | 
						|
                          +-------------+     |
 | 
						|
                    CPU11 |      6      |     |
 | 
						|
                          +-------------+     |
 | 
						|
                    CPU12 |      6      |     v
 | 
						|
                          +-------------+
 | 
						|
 | 
						|
The ``psci_non_cpu_pd_nodes`` array will be populated as follows. The value in
 | 
						|
each entry is the index of the parent node.
 | 
						|
 | 
						|
::
 | 
						|
 | 
						|
                          +-------------+     ^
 | 
						|
                    PD0   |      -1     |     |
 | 
						|
                          +-------------+     |
 | 
						|
                    PD1   |      0      |     |
 | 
						|
                          +-------------+     |
 | 
						|
                    PD2   |      0      |     |
 | 
						|
                          +-------------+     |
 | 
						|
                    PD3   |      1      |     | PLAT_NUM_POWER_DOMAINS -
 | 
						|
                          +-------------+     | PLATFORM_CORE_COUNT
 | 
						|
                    PD4   |      1      |     |
 | 
						|
                          +-------------+     |
 | 
						|
                    PD5   |      2      |     |
 | 
						|
                          +-------------+     |
 | 
						|
                    PD6   |      2      |     |
 | 
						|
                          +-------------+     v
 | 
						|
 | 
						|
Each core can find its node in the ``psci_cpu_pd_nodes`` array using the
 | 
						|
``plat_my_core_pos()`` function. When a core is turned on, the normal world
 | 
						|
provides an MPIDR. The ``plat_core_pos_by_mpidr()`` function is used to validate
 | 
						|
the MPIDR before using it to find the corresponding core node. The non-core power
 | 
						|
domain nodes do not need to be identified.
 | 
						|
 | 
						|
--------------
 | 
						|
 | 
						|
*Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.*
 |