[LINUX] Operating Performance Points (OPP) Library

Originally, it is a part of the Linux Kernel source code, so it will be treated as GPLv2 (recognition that it should be).

https://www.kernel.org/doc/html/latest/index.html

Licensing documentation

The following describes the license of the Linux kernel source code (GPLv2), how to properly mark the license of individual files in the source tree, as well as links to the full license text.

https://www.kernel.org/doc/html/latest/process/license-rules.html#kernel-licensing

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/plain/Documentation/power/opp.rst

Operating Performance Points (OPP) Library

(C) 2009-2010 Nishanth Menon [email protected], Texas Instruments Incorporated

Contents

  1. Introduction

  2. Initial OPP List Registration

  3. OPP Search Functions

  4. OPP Availability Control Functions

  5. OPP Data Retrieval Functions

  6. Data Structures

  7. Introduction

1.1 What is an Operating Performance Point (OPP)?

Complex SoCs of today consists of a multiple sub-modules working in conjunction.

Today's complex SoCs consist of multiple submodules that work together.

In an operational system executing varied use cases, not all modules in the SoC need to function at their highest performing frequency all the time. To facilitate this, sub-modules in a SoC are grouped into domains, allowing some domains to run at lower voltage and frequency while other domains run at voltage/frequency pairs that are higher.

In an operational system used in various use cases, not all modules in the SoC need to function at the highest performance frequency in time. To facilitate this, the submodules contained in the SoC can be grouped into domains, some domains can run at lower voltages / frequencies, and others can run at higher voltage / frequency combinations. I can do it.

The set of discrete tuples consisting of frequency and voltage pairs that the device will support per domain are called Operating Performance Points or OPPs.

The collection of individual tuples consisting of frequency and voltage pairs that each device can support is called Operating Perfomance Points or OPPs.

As an example:

Let us consider an MPU device which supports the following:

If your MPU device supports:

{300MHz at minimum voltage of 1V}, {800MHz at minimum voltage of 1.2V}, {1GHz at minimum voltage of 1.3V}

We can represent these as three OPPs as the following {Hz, uV} tuples:

These can be expressed as three Opps as the following {Hz, uV} tuples.

1.2 Operating Performance Points Library

OPP library provides a set of helper functions to organize and query the OPP information. The library is located in drivers/opp/ directory and the header is located in include/linux/pm_opp.h.

The OPP library provides a set of helper functions for organizing and referencing OPP information. The libraries are located in the drivers / opp directory and the headers are located in include / linux / pm_opp.h.

OPP library can be enabled by enabling CONFIG_PM_OPP from power management menuconfig menu. OPP library depends on CONFIG_PM as certain SoCs such as Texas Instrument's OMAP framework allows to optionally boot at a certain OPP without needing cpufreq.

The OPP library can be enabled by enabling CONFIG_PM_OPP from the power management menuconfig menu. The OPP library requires CONFIG_PM. Certain Socs, such as Texas Instrument's OMAP framework, allow optional launches on specific OPPs without the need for cpufreq.

Typical usage of the OPP library is as follows::

The general usage of the OPP library is as follows.

image.png

OPP layer expects each domain to be represented by a unique device pointer.

The OPP layer expects each domain to be represented by a unique device pointer.

SoC framework registers a set of initial OPPs per device with the OPP layer.

The SoC framework uses the OPP layer to register the initial set of OPPs for each device.

This list is expected to be an optimally small number typically around 5 per device.

This list is expected to be as small as 5 for each device.

This initial list contains a set of OPPs that the framework expects to be safely enabled by default in the system.

This initial list contains a set of OPPs that the framework can safely enable and expect default values for the system.

Note on OPP Availability

As the system proceeds to operate, SoC framework may choose to make certain OPPs available or not available on each device based on various external factors.

As the system continues to operate, the SoC framework may or may not be able to use certain OPPs on each device due to various external factors.

Example usage: Thermal management or other exceptional situations where SoC framework might choose to disable a higher frequency OPP to safely continue operations until that OPP could be re-enabled if possible.

Use Case: In temperature control or other exceptional situations, the SoC framework disables high frequency OPP so that it will continue to operate safely until the OPP can be re-enabled as much as possible. select.

OPP library facilitates this concept in its implementation. The following operational functions operate only on available opps:

The OPP library promotes this concept in implementation. The functions below are functions that are only executed for the available opps.

opp_find_freq_{ceil, floor}, dev_pm_opp_get_voltage, dev_pm_opp_get_freq, dev_pm_opp_get_opp_count

dev_pm_opp_find_freq_exact is meant to be used to find the opp pointer which can then be used for dev_pm_opp_enable/disable functions to make an opp available as required.

dev_pm_opp_find_freq_exact is intended to be used to find the opp pointer that can be used in the dev_pm_opp_enable / disable function to enable opp if needed.

WARNING: Users of OPP library should refresh their availability count using get_opp_count if dev_pm_opp_enable/disable functions are invoked for a device, the exact mechanism to trigger these or the notification mechanism to other dependent subsystems such as cpufreq are left to the discretion of the SoC specific framework which uses the OPP library.

Warning: The OPP library should use get_opp_count to update the valid count for the device when the dev_pm_opp_enable / disable function is called. The exact triggering mechanism or the mechanism to notify other required subsystems is left to the judgment of the SoC Tokutaka framework using an OPP library such as cpufreq.

Similar care needs to be taken care to refresh the cpufreq table in cases of these operations.

Similar precautions should be taken when updating the cpufreq table when manipulating these.

  1. Initial OPP List Registration

The SoC implementation calls dev_pm_opp_add function iteratively to add OPPs per device.

In the SoC implementation, the dev_pm_opp_add function is repeatedly called to add OPP for each device.

It is expected that the SoC framework will register the OPP entries optimally- typical numbers range to be less than 5.

The SoC framework is generally expected to register optimal OPP entries in the range not exceeding 5.

The list generated by registering the OPPs is maintained by OPP library throughout the device operation.

The list generated by OPP registration is maintained by the OPP library via device control.

The SoC framework can subsequently control the availability of the OPPs dynamically using the dev_pm_opp_enable / disable functions.

The SoC framework can then dynamically control the effectiveness of OPP using the dev_pm_opp_enable / disable function.

dev_pm_opp_add

Add a new OPP for a specific domain represented by the device pointer.

Add a new OPP to the unique domain represented by the device pointer.

The OPP is defined using the frequency and voltage.

OPP is defined by frequency and voltage.

Once added, the OPP is assumed to be available and control of its availability can be done with the dev_pm_opp_enable/disable functions. OPP library internally stores and manages this information in the opp struct.

Once added, the OPP considers it available and its effectiveness is controlled using the dev_pm_opp_enable / disable function. The OPP library internally retains and manages this information through the opp structure.

This function may be used by SoC framework to define a optimal list as per the demands of SoC usage environment.

This function is used by the SoC framework to define the optimal list based on the requirements of the SoC environment.

WARNING:

Do not use this function in interrupt context.

This function should not be used within an interrupt context.

Example::

 soc_pm_init()
 {
	/* Do things */
	r = dev_pm_opp_add(mpu_dev, 1000000, 900000);
	if (!r) {
		pr_err("%s: unable to register mpu opp(%d)\n", );
		goto no_cpufreq;
	}
	/* Do cpufreq things */
no_cpufreq:
	/* Do remaining things */
 }
  1. OPP Search Functions

High level framework such as cpufreq operates on frequencies.

High level framework like cpufreq control at frequency.

To map the frequency back to the corresponding OPP, OPP library provides handy functions to search the OPP list that OPP library internally manages.

To map the association of frequencies with related OPPs, the OPP library provides an easy-to-use function for searching the OPP list internally managed by the OPP library.

These search functions return the matching pointer representing the opp if a match is found, else returns error.

These search functions return a pointer to the matched opp if a match is found, or an error otherwise.

These errors are expected to be handled by standard error checks such as IS_ERR() and appropriate actions taken by the caller.

These errors are expected to be handled by standard error checking such as IS_ERR () and the appropriate action taken by the caller.

Callers of these functions shall call dev_pm_opp_put() after they have used the OPP.

Callers of these functions must call dev_pm_opp_put () after using OPP.

Otherwise the memory for the OPP will never get freed and result in memleak.

Otherwise, OPP will not be free and will result in a memory leak.

dev_pm_opp_find_freq_exact

Search for an OPP based on an exact frequency and availability. This function is especially useful to enable an OPP which is not available by default.

Search for valid OPPs based on "strict" frequencies. This function is especially useful when enabling OPP, which is not enabled by default.

Example:

In a case when SoC framework detects a situation where a higher frequency could be made available, it can use this function to find the OPP prior to call the dev_pm_opp_enable to actually make it available::

If the SoC framework determines that a higher frequency can be enabled, you can try this function to find the OPP before calling dev_pm_opp_enable to make it actually available.


	 opp = dev_pm_opp_find_freq_exact(dev, 1000000000, false);
	 dev_pm_opp_put(opp);
	 /* dont operate on the pointer.. just do a sanity check.. */
	 if (IS_ERR(opp)) {
		pr_err("frequency not disabled!\n");
		/* trigger appropriate actions.. */
	 } else {
		dev_pm_opp_enable(dev,1000000000);
	 }

NOTE:

This is the only search function that operates on OPPs which are not available.

This is the only search feature that works with OPPs that are not enabled.

dev_pm_opp_find_freq_floor

Search for an available OPP which is at most the provided frequency.

Search for valid OPPs with a given frequency of "maximum less than".

This function is useful while searching for a lesser match OR operating on OPP information in the order of decreasing frequency.

This function is useful when searching for lower frequencies or when processing OPP information in descending order of frequency.

Example:

To find the highest opp for a device::

Find the highest frequency for your device.

	 freq = ULONG_MAX;
	 opp = dev_pm_opp_find_freq_floor(dev, &freq);
	 dev_pm_opp_put(opp);

dev_pm_opp_find_freq_ceil

Search for an available OPP which is at least the provided frequency.

Search for valid OPPs with a given frequency "greater than or equal to".

This function is useful while searching for a higher match OR operating on OPP information in the order of increasing frequency.

This function is useful when searching for higher frequencies or when processing OPP information in ascending order of frequency.

Example 1:

To find the lowest opp for a device::

Find the smallest OPP on your device


	 freq = 0;
	 opp = dev_pm_opp_find_freq_ceil(dev, &freq);
	 dev_pm_opp_put(opp);

Example 2:

A simplified implementation of a SoC cpufreq_driver->target::

Simple implementation of SoC cpufreq_driver-> target

	 soc_cpufreq_target(..)
	 {
		/* Do stuff like policy checks etc. */
		/* Find the best frequency match for the req */
		opp = dev_pm_opp_find_freq_ceil(dev, &freq);
		dev_pm_opp_put(opp);
		if (!IS_ERR(opp))
			soc_switch_to_freq_voltage(freq);
		else
			/* do something when we can't satisfy the req */
		/* do other stuff */
	 }
  1. OPP Availability Control Functions

A default OPP list registered with the OPP library may not cater to all possible situation.

The OPP list registered in the OPP Library by default does not cover all situations.

The OPP library provides a set of functions to modify the availability of a OPP within the OPP list.

The OPP library provides a set of functions for changing the validity of OPPs in the OPP list.

This allows SoC frameworks to have fine grained dynamic control of which sets of OPPs are operationally available.

This gives the SoC framework fine-grained control over which set of OPPs are operational valid.

These functions are intended to temporarily remove an OPP in conditions such as thermal considerations (e.g. don't use OPPx until the temperature drops).

These functions are intended to "temporarily" remove OPP, for example, based on temperature conditions. (OPPx will be unavailable until the temperature drops).

WARNING:

Do not use these functions in interrupt context.

Do not call these functions from the interrupt context.

dev_pm_opp_enable

Make a OPP available for operation.

Make OPP processable.

Example:

Lets say that 1GHz OPP is to be made available only if the SoC temperature is lower than a certain threshold.

Suppose you want to enable 1GHz OPP because the temperature of your SoC is below the specified threshold.

The SoC framework implementation might choose to do something as follows::

The implementation of the SoC framework is as follows.

	 if (cur_temp < temp_low_thresh) {
		/* Enable 1GHz if it was disabled */
		opp = dev_pm_opp_find_freq_exact(dev, 1000000000, false);
		dev_pm_opp_put(opp);
		/* just error check */
		if (!IS_ERR(opp))
			ret = dev_pm_opp_enable(dev, 1000000000);
		else
			goto try_something_else;
	 }

dev_pm_opp_disable

Make an OPP to be not available for operation

Make OPP unprocessable.

Example:

Lets say that 1GHz OPP is to be disabled if the temperature exceeds a threshold value. The SoC framework implementation might choose to do something as follows::

Suppose you want to disable the 1GHz OPP because the SoC temperature is higher than the specified threshold. The implementation of the SoC framework is as follows.

	 if (cur_temp > temp_high_thresh) {
		/* Disable 1GHz if it was enabled */
		opp = dev_pm_opp_find_freq_exact(dev, 1000000000, true);
		dev_pm_opp_put(opp);
		/* just error check */
		if (!IS_ERR(opp))
			ret = dev_pm_opp_disable(dev, 1000000000);
		else
			goto try_something_else;
	 }
  1. OPP Data Retrieval Functions

Since OPP library abstracts away the OPP information, a set of functions to pull information from the OPP structure is necessary.

In order for the OPP library to abstract OPP information, it needs a set of functions to extract information from the OPP structure.

Once an OPP pointer is retrieved using the search functions, the following functions can be used by SoC framework to retrieve the information represented inside the OPP layer.

Once the OPP pointer is retrieved by the search function, the following function will cause the SoC framework to retrieve the relevant information from the OPP layer.

dev_pm_opp_get_voltage

Retrieve the voltage represented by the opp pointer.

Get the voltage represented by the opp pointer.

Example:

At a cpufreq transition to a different frequency, SoC framework requires to set the voltage represented by the OPP using the regulator framework to the Power Management chip providing the voltage::

If the cpufreq moves on different frequencies, the SoC framework must set the voltage represented by the OPP using the regular framework as the voltage applied to the Power Management chip.

	 soc_switch_to_freq_voltage(freq)
	 {
		/* do things */
		opp = dev_pm_opp_find_freq_ceil(dev, &freq);
		v = dev_pm_opp_get_voltage(opp);
		dev_pm_opp_put(opp);
		if (v)
			regulator_set_voltage(.., v);
		/* do other things */
	 }

dev_pm_opp_get_freq

Retrieve the freq represented by the opp pointer.

Get the frequency represented by the opp pointer.

Example:

Lets say the SoC framework uses a couple of helper functions we could pass opp pointers instead of doing additional parameters to handle quiet a bit of data parameters::

The SoC framework uses some helper functions to quietly handle a few data parameters via the opp pointer instead of executing additional parameters.

	 soc_cpufreq_target(..)
	 {
		/* do things.. */
		 max_freq = ULONG_MAX;
		 max_opp = dev_pm_opp_find_freq_floor(dev,&max_freq);
		 requested_opp = dev_pm_opp_find_freq_ceil(dev,&freq);
		 if (!IS_ERR(max_opp) && !IS_ERR(requested_opp))
			r = soc_test_validity(max_opp, requested_opp);
		 dev_pm_opp_put(max_opp);
		 dev_pm_opp_put(requested_opp);
		/* do other things */
	 }
	 soc_test_validity(..)
	 {
		 if(dev_pm_opp_get_voltage(max_opp) < dev_pm_opp_get_voltage(requested_opp))
			 return -EINVAL;
		 if(dev_pm_opp_get_freq(max_opp) < dev_pm_opp_get_freq(requested_opp))
			 return -EINVAL;
		/* do things.. */
	 }

dev_pm_opp_get_opp_count

Retrieve the number of available opps for a device

Get the number of valid opps on the device.

Example:

Lets say a co-processor in the SoC needs to know the available frequencies in a table, the main processor can notify as following::

When the coprocessor in the SoC wants to know the table of valid frequencies, the main processor notifies as follows.

	 soc_notify_coproc_available_frequencies()
	 {
		/* Do things */
		num_available = dev_pm_opp_get_opp_count(dev);
		speeds = kzalloc(sizeof(u32) * num_available, GFP_KERNEL);
		/* populate the table in increasing order */
		freq = 0;
		while (!IS_ERR(opp = dev_pm_opp_find_freq_ceil(dev, &freq))) {
			speeds[i] = freq;
			freq++;
			i++;
			dev_pm_opp_put(opp);
		}

		soc_notify_coproc(AVAILABLE_FREQs, speeds, num_available);
		/* Do other things */
	 }
  1. Data Structures

Typically an SoC contains multiple voltage domains which are variable. Each domain is represented by a device pointer. The relationship to OPP can be represented as follows::

Soc typically contains multiple variable voltage domains. Each domain is represented by a device pointer. The relationship with OPP can be expressed as follows.

image.png

OPP library maintains a internal list that the SoC framework populates and accessed by various functions as described above. However, the structures representing the actual OPPs and domains are internal to the OPP library itself to allow for suitable abstraction reusable across systems.

The OPP library is populated by the SoC framwork and maintains an internal list to access processing like the one above. However, the structure that represents the actual OPP and domain is inside the OPP library itself, with the proper abstraction that can be reused between systems.

struct dev_pm_opp

The internal data structure of OPP library which is used to represent an OPP.

A data structure inside the OPP library used to represent OPP.

In addition to the freq, voltage, availability information, it also contains internal book keeping information required for the OPP library to operate on.

In addition to frequency, voltage, and valid information, it also contains internal bookkeeping information processed by the OPP library.

Pointer to this structure is provided back to the users such as SoC framework to be used as a identifier for OPP in the interactions with OPP layer.

Pointers of this structure are provided to users like the soC framework as OPP iterators in the OPP layer iteration.

WARNING:

The struct dev_pm_opp pointer should not be parsed or modified by the users.

Do not change the pointer of the dev_pm_opp structure once it has been parsed by the user.

The defaults of for an instance is populated by dev_pm_opp_add, but the availability of the OPP can be modified by dev_pm_opp_enable/disable functions.

Instance defaults are entered by dev_pm_opp_add. And the effectiveness of OPP can be changed by the dev_pm_opp_enable / disable function.

struct device

This is used to identify a domain to the OPP layer.

This is used in OPP layrt to identify the domain.

The nature of the device and its implementation is left to the user of OPP library such as the SoC framework.

The nature of the device and its implementation are left to the user of the OPP library, such as SoC framwwork.

Overall, in a simplistic view, the data structure operations is represented as following::

Overall, in a simplified view, data structure operations are represented as:

image.png

Recommended Posts

Operating Performance Points (OPP) Library