diff mbox

[V2,1/2] fwts/opal: Power management DT Validation tests.

Message ID 1492921234-24902-1-git-send-email-ppaidipe@linux.vnet.ibm.com
State Superseded
Headers show

Commit Message

ppaidipe April 23, 2017, 4:20 a.m. UTC
This patch contains testcases for below Power Processor
energey management subsystems.
 	a. cpuidle
 	b. cpufreq

These testcases validate the device tree properties for these two
subsystems which are got exposed to linux from the system
firmware(OPAL).

This patch is enhanced based on the initial patch developed by shilpa
for pstates.
https://lists.ubuntu.com/archives/fwts-devel/2016-May/007874.html

Added cpuidle states DT Validation tests.

Signed-off-by: Pridhiviraj Paidipeddi <ppaidipe@linux.vnet.ibm.com>
---
Changes since V1:
        - Changed to tabs instead of spaces wherever it is required
        - Changed to new typedef proc_gen_t
        - For unused args, used the FWTS_UNUSED macro
        - Changed multi line comment style to fwts block comment style
        - Changed mask type from int to const inst
---
 src/Makefile.am                   |   1 +
 src/lib/include/fwts_cpu.h        |  18 ++
 src/lib/include/fwts_devicetree.h |  85 ++++++++
 src/lib/src/fwts_devicetree.c     | 196 ++++++++++++++++++
 src/opal/power_mgmt_info.c        | 409 ++++++++++++++++++++++++++++++++++++++
 5 files changed, 709 insertions(+)
 create mode 100644 src/opal/power_mgmt_info.c

Comments

Colin Ian King April 24, 2017, 8:11 a.m. UTC | #1
On 23/04/17 05:20, Pridhiviraj Paidipeddi wrote:
> This patch contains testcases for below Power Processor
> energey management subsystems.
>  	a. cpuidle
>  	b. cpufreq
> 
> These testcases validate the device tree properties for these two
> subsystems which are got exposed to linux from the system
> firmware(OPAL).
> 
> This patch is enhanced based on the initial patch developed by shilpa
> for pstates.
> https://lists.ubuntu.com/archives/fwts-devel/2016-May/007874.html
> 
> Added cpuidle states DT Validation tests.
> 
> Signed-off-by: Pridhiviraj Paidipeddi <ppaidipe@linux.vnet.ibm.com>
> ---
> Changes since V1:
>         - Changed to tabs instead of spaces wherever it is required
>         - Changed to new typedef proc_gen_t
>         - For unused args, used the FWTS_UNUSED macro
>         - Changed multi line comment style to fwts block comment style
>         - Changed mask type from int to const inst
> ---
>  src/Makefile.am                   |   1 +
>  src/lib/include/fwts_cpu.h        |  18 ++
>  src/lib/include/fwts_devicetree.h |  85 ++++++++
>  src/lib/src/fwts_devicetree.c     | 196 ++++++++++++++++++
>  src/opal/power_mgmt_info.c        | 409 ++++++++++++++++++++++++++++++++++++++
>  5 files changed, 709 insertions(+)
>  create mode 100644 src/opal/power_mgmt_info.c
> 
> diff --git a/src/Makefile.am b/src/Makefile.am
> index c1eb285..e833554 100644
> --- a/src/Makefile.am
> +++ b/src/Makefile.am
> @@ -147,6 +147,7 @@ fwts_SOURCES = main.c 				\
>  	kernel/version/version.c 		\
>  	opal/mtd_info.c				\
>  	opal/prd_info.c				\
> +	opal/power_mgmt_info.c			\
>  	pci/aspm/aspm.c 			\
>  	pci/crs/crs.c 				\
>  	pci/maxreadreq/maxreadreq.c 		\
> diff --git a/src/lib/include/fwts_cpu.h b/src/lib/include/fwts_cpu.h
> index 9ab2ccf..be1c6cb 100644
> --- a/src/lib/include/fwts_cpu.h
> +++ b/src/lib/include/fwts_cpu.h
> @@ -33,6 +33,24 @@ typedef struct cpuinfo_x86 {
>  	char *flags;		/* String containing flags */
>  } fwts_cpuinfo_x86;
>  
> +/* PowerPC Processor specific bits */
> +/* PVR definitions */
> +#define PVR_TYPE_P7     0x003f
> +#define PVR_TYPE_P7P    0x004a
> +#define PVR_TYPE_P8E    0x004b /* Murano */
> +#define PVR_TYPE_P8     0x004d /* Venice */
> +#define PVR_TYPE_P8NVL  0x004c /* Naples */
> +#define PVR_TYPE_P9     0x004e
> +
> +/* Processor generation */
> +typedef enum proc_gen {
> +	proc_gen_unknown,
> +	proc_gen_p7,            /* P7 and P7+ */
> +	proc_gen_p8,
> +	proc_gen_p9,
> +} proc_gen_t;
> +extern proc_gen_t proc_gen;
> +
>  typedef struct cpu_benchmark_result {
>  	bool		cycles_valid;
>  	uint64_t	loops;
> diff --git a/src/lib/include/fwts_devicetree.h b/src/lib/include/fwts_devicetree.h
> index 662f6ec..21c7807 100644
> --- a/src/lib/include/fwts_devicetree.h
> +++ b/src/lib/include/fwts_devicetree.h
> @@ -42,6 +42,28 @@
>  #if FWTS_HAS_DEVICETREE
>  
>  int fwts_devicetree_read(fwts_framework *fwts);
> +int fwts_dt_property_read_u32(
> +	void *fdt,
> +	int offset,
> +	const char *pname,
> +	int *value);
> +int fwts_dt_property_read_u32_arr(
> +	void *fdt,
> +	int offset,
> +	const char *pname,
> +	int *value,
> +	int *len);
> +int fwts_dt_property_read_u64_arr(
> +	void *fdt,
> +	int offset,
> +	const char *pname,
> +	uint64_t *value,
> +	int *len);
> +int fwts_dt_stringlist_count(
> +	fwts_framework *fw,
> +	const void *fdt,
> +	int nodeoffset,
> +	const char *property);
>  
>  #else /* !FWTS_HAS_DEVICETREE */
>  static inline int fwts_devicetree_read(fwts_framework *fwts)
> @@ -50,6 +72,67 @@ static inline int fwts_devicetree_read(fwts_framework *fwts)
>  
>  	return FWTS_OK;
>  }
> +
> +int fwts_dt_property_read_u32(
> +	void *fdt,
> +	int offset,
> +	const char *pname,
> +	int *value)
> +{
> +	FWTS_UNUSED(fdt);
> +	FWTS_UNUSED(offset);
> +	FWTS_UNUSED(pname);
> +	FWTS_UNUSED(value);
> +
> +	return FWTS_OK;
> +}
> +
> +int fwts_dt_property_read_u32_arr(
> +	void *fdt,
> +	int offset,
> +	const char *pname,
> +	int *value,
> +	int *len)
> +{
> +	FWTS_UNUSED(fdt);
> +	FWTS_UNUSED(offset);
> +	FWTS_UNUSED(pname);
> +	FWTS_UNUSED(value);
> +	FWTS_UNUSED(len);
> +
> +	return FWTS_OK;
> +}
> +
> +int fwts_dt_property_read_u64_arr(
> +	void *fdt,
> +	int offset,
> +	const char *pname,
> +	uint64_t *value,
> +	int *len)
> +{
> +	FWTS_UNUSED(fdt);
> +	FWTS_UNUSED(offset);
> +	FWTS_UNUSED(pname);
> +	FWTS_UNUSED(value);
> +	FWTS_UNUSED(len);
> +
> +	return FWTS_OK;
> +}
> +
> +int fwts_dt_stringlist_count(
> +	fwts_framework *fw,
> +	const void *fdt,
> +	int nodeoffset,
> +	const char *property)
> +{
> +	FWTS_UNUSED(fw);
> +	FWTS_UNUSED(fdt);
> +	FWTS_UNUSED(nodeoffset);
> +	FWTS_UNUSED(property);
> +
> +	return FWTS_OK;
> +}
> +
>  #endif
>  
>  bool check_status_property_okay(fwts_framework *fw,
> @@ -60,4 +143,6 @@ int check_property_printable(fwts_framework *fw,
>  
>  char *hidewhitespace(char *name);
>  
> +int get_proc_gen(fwts_framework *fw);
> +
>  #endif
> diff --git a/src/lib/src/fwts_devicetree.c b/src/lib/src/fwts_devicetree.c
> index bf5686a..2fea3c8 100644
> --- a/src/lib/src/fwts_devicetree.c
> +++ b/src/lib/src/fwts_devicetree.c
> @@ -26,6 +26,8 @@
>  
>  #include <libfdt.h>
>  
> +proc_gen_t proc_gen;
> +
>  int fwts_devicetree_read(fwts_framework *fwts)
>  {
>  	char *command, *data = NULL;
> @@ -171,3 +173,197 @@ char *hidewhitespace(char *name)
>  	return name;
>  
>  }
> +
> +/*
> + * fwts_dt_property_read_u32 This function reads one u32 DT property
> + * 	Returns FWTS_OK on success:	*value will contain one int
> + * 	FWTS_ERROR on error:		*value will contain error code
> + */
> +
> +int fwts_dt_property_read_u32(
> +	void *fdt,
> +	int offset,
> +	const char *pname,
> +	int *value)
> +{
> +	int len;
> +	const int *buf;
> +
> +	buf = fdt_getprop(fdt, offset, pname, &len);
> +	if (buf == NULL) {
> +		*value = len;
> +		return FWTS_ERROR;
> +	}
> +	*value = be32toh(*buf);
> +	return FWTS_OK;
> +}
> +
> +/*
> + * This function reads DT property array of u32's
> + * 	Return FWTS_OK on success:	*value contain full array of int's
> + *  	FWTS_ERROR on error:		*value will contain error code which
> + *  						comes from *len
> + */
> +
> +int fwts_dt_property_read_u32_arr(
> +	void *fdt,
> +	int offset,
> +	const char *pname,
> +	int *value,
> +	int *len)
> +{
> +	int i;
> +	const int *buf;
> +
> +	buf = fdt_getprop(fdt, offset, pname, len);
> +	if (buf == NULL) {
> +		*value = *len;
> +		return FWTS_ERROR;
> +	}
> +
> +	*len = *len / sizeof(int);
> +	for (i = 0; i < *len; i++)
> +		value[i] = be32toh(buf[i]);
> +	return FWTS_OK;
> +}
> +
> +/*
> + * This function reads DT property array of u64's
> + * 	Return FWTS_OK on success:	*value contain full array of u64's
> + * 	FWTS_ERROR on error:		*value will contain error code which
> + * 						comes from *len
> + */
> +
> +int fwts_dt_property_read_u64_arr(
> +	void *fdt,
> +	int offset,
> +	const char *pname,
> +	uint64_t *value,
> +	int *len)
> +{
> +	int i;
> +	const int *buf;
> +
> +	buf = fdt_getprop(fdt, offset, pname, len);
> +	if (buf == NULL) {
> +		*value = *len;
> +		return FWTS_ERROR;
> +	}
> +
> +	*len = *len / sizeof(uint64_t);
> +	for (i = 0; i < *len; i++)
> +		value[i] = be64toh(buf[i]);
> +	return FWTS_OK;
> +}
> +
> +/* Get's the length of DT property string list */
> +
> +int fwts_dt_stringlist_count(
> +	fwts_framework *fw,
> +	const void *fdt,
> +	int nodeoffset,
> +	const char *property)
> +{
> +	const char *list, *end;
> +	int length, count = 0;
> +
> +	list = fdt_getprop(fdt, nodeoffset, property, &length);
> +	if (!list) {
> +		fwts_failed(fw, LOG_LEVEL_HIGH, "PropertyNotFound",
> +			"Failed to get property %s rc %d", property, length);
> +		return FWTS_ERROR;
> +	}
> +
> +	end = list + length;
> +
> +	while (list < end) {
> +		length = strnlen(list, end - list) + 1;
> +
> +		/* Check if the last string isn't properly NUL-terminated. */
> +		if (list + length > end) {
> +			fwts_failed(fw, LOG_LEVEL_HIGH, "NotNULLTerminated",
> +				"Last string is not properly NULL terminated");
> +			return FWTS_ERROR;
> +		}
> +
> +		list += length;
> +		count++;
> +	}
> +
> +	return count;
> +}
> +
> +static int get_cpu_version(fwts_framework *fw, int *value)
> +{
> +	const char *cpus_path = "/cpus/";
> +	int offset;
> +	int cpu_version; int ret;
> +
> +	if (!fw->fdt) {
> +		fwts_skipped(fw, "Device tree not found");
> +		return FWTS_SKIP;
> +	}
> +
> +	offset = fdt_path_offset(fw->fdt, cpus_path);
> +	if (offset < 0) {
> +		fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTNodeMissing",
> +			"/cpus node is missing");
> +		return FWTS_ERROR;
> +	}
> +
> +	offset = fdt_node_offset_by_prop_value(fw->fdt, -1, "device_type",
> +				"cpu", sizeof("cpu"));
> +	if (offset < 0) {
> +		fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTNodeMissing",
> +			"cpu node is missing");
> +		return FWTS_ERROR;
> +	}
> +
> +	ret = fwts_dt_property_read_u32(fw->fdt, offset,
> +			"cpu-version", &cpu_version);
> +	if (ret != FWTS_OK) {
> +		fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTPropertyReadError",
> +			"Failed to read property cpu-version %s",
> +			fdt_strerror(cpu_version));
> +		return FWTS_ERROR;
> +	}
> +	*value = cpu_version;
> +	return FWTS_OK;
> +}
> +
> +int get_proc_gen(fwts_framework *fw)
> +{
> +	int version; int ret;
> +	const int mask = 0xFFFF0000;
> +	int pvr;
> +
> +	ret = get_cpu_version(fw, &version);
> +	if (ret != FWTS_OK) {
> +		fwts_failed(fw, LOG_LEVEL_HIGH, "DTNoCPUVersion",
> +			"Not able to get the CPU version");
> +		return FWTS_ERROR;
> +	}
> +
> +	pvr = (mask & version) >> 16;
> +	switch (pvr) {
> +	/* Get CPU family and other flags based on PVR */
> +	case PVR_TYPE_P7:
> +	case PVR_TYPE_P7P:
> +		proc_gen = proc_gen_p7;
> +		break;
> +	case PVR_TYPE_P8E:
> +	case PVR_TYPE_P8:
> +		proc_gen = proc_gen_p8;
> +		break;
> +	case PVR_TYPE_P8NVL:
> +		proc_gen = proc_gen_p8;
> +		break;
> +	case PVR_TYPE_P9:
> +		proc_gen = proc_gen_p9;
> +		break;
> +	default:
> +		proc_gen = proc_gen_unknown;
> +	}
> +
> +	return FWTS_OK;
> +}
> diff --git a/src/opal/power_mgmt_info.c b/src/opal/power_mgmt_info.c
> new file mode 100644
> index 0000000..5456c43
> --- /dev/null
> +++ b/src/opal/power_mgmt_info.c
> @@ -0,0 +1,409 @@
> +/*
> + * Copyright (C) 2010-2017 Canonical
> + * Some of this work - Copyright (C) 2016-2017 IBM
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; either version 2
> + * of the License, or (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
> + *
> + */
> +
> +#include <fcntl.h>
> +#include <stdlib.h>
> +#include <sys/ioctl.h>
> +
> +#include "fwts.h"
> +
> +#ifdef HAVE_LIBFDT
> +#include <libfdt.h>
> +#endif
> +
> +#define MAX_PSTATES 256
> +
> +#define CPUIDLE_STATE_MAX   10
> +
> +proc_gen_t proc_gen;
> +
> +static const char *power_mgt_path = "/ibm,opal/power-mgt/";
> +
> +/**
> + * cmp_pstates: Compares the given two pstates and determines which
> + *              among them is associated with a higher pstate.
> + *
> + * @a,@b: The pstate ids of the pstates being compared.
> + *
> + * Returns: -1 : If pstate associated with @a is smaller than
> + *               the pstate associated with @b.
> + *      0 : If pstates associated with @a and @b are equal.
> + *      1 : If pstate associated with @a is greater than
> + *               the pstate associated with @b.
> + */
> +static int (*cmp_pstates)(int a, int b);
> +
> +static int cmp_positive_pstates(int a, int b)
> +{
> +	if (a > b)
> +		return -1;
> +	else if (a < b)
> +		return 1;
> +
> +	return 0;
> +}
> +
> +static int cmp_negative_pstates(int a, int b)
> +{
> +	return cmp_positive_pstates(b, a);
> +}
> +
> +static int validate_dt_prop_sizes(
> +	fwts_framework *fw,
> +	const char *prop1,
> +	int prop1_len,
> +	const char *prop2,
> +	int prop2_len)
> +{
> +	if (prop1_len == prop2_len)
> +		return FWTS_OK;
> +
> +	fwts_failed(fw, LOG_LEVEL_HIGH, "SizeMismatch",
> +		"array sizes don't match for %s len %d and %s len %d\n",
> +		prop1, prop1_len, prop2, prop2_len);
> +
> +	return FWTS_ERROR;
> +}
> +
> +static int power_mgmt_init(fwts_framework *fw)
> +{
> +	int ret;
> +
> +	if (fwts_firmware_detect() != FWTS_FIRMWARE_OPAL) {
> +		fwts_skipped(fw,
> +			"The firmware type detected was non OPAL "
> +			"so skipping the OPAL Power Management DT checks.");
> +		return FWTS_SKIP;
> +	}
> +
> +	if (!fw->fdt) {
> +		fwts_failed(fw, LOG_LEVEL_HIGH, "NoDeviceTree",
> +			"Device tree not found");
> +		return FWTS_ERROR;
> +	}
> +
> +	ret = get_proc_gen(fw);
> +	if (ret != FWTS_OK) {
> +		fwts_failed(fw, LOG_LEVEL_HIGH, "ProcGenFail",
> +			"Failed to get the Processor generation");
> +		return FWTS_ERROR;
> +	}
> +
> +	return FWTS_OK;
> +}
> +
> +
> +static int pstate_limits_test(fwts_framework *fw)
> +{
> +	int pstate_min, pstate_max, pstates[MAX_PSTATES];
> +	bool ok = true;
> +	int  nr_pstates, offset, len, ret, i;
> +
> +	switch (proc_gen) {
> +	case proc_gen_p8:
> +		cmp_pstates = cmp_negative_pstates;
> +		break;
> +	case proc_gen_p9:
> +		cmp_pstates = cmp_positive_pstates;
> +		break;
> +	default:
> +		fwts_failed(fw, LOG_LEVEL_HIGH, "UnknownProcessorChip",
> +			"Unknown processor generation %d", proc_gen);
> +		return FWTS_ERROR;
> +	}
> +
> +	offset = fdt_path_offset(fw->fdt, power_mgt_path);
> +	if (offset < 0) {
> +		fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTNodeMissing",
> +			"power management node %s is missing", power_mgt_path);
> +		return FWTS_ERROR;
> +	}
> +
> +	ret = fwts_dt_property_read_u32(fw->fdt, offset, "ibm,pstate-min",
> +					&pstate_min);
> +	if (ret != FWTS_OK) {
> +		fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTPropertyReadError",
> +			"Failed to read property ibm,pstate-min %s",
> +			fdt_strerror(pstate_min));
> +		return FWTS_ERROR;
> +	}
> +
> +	ret = fwts_dt_property_read_u32(fw->fdt, offset, "ibm,pstate-max",
> +					&pstate_max);
> +	if (ret != FWTS_OK) {
> +		fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTPropertyReadError",
> +			"Failed to read property ibm,pstate-max %s",
> +			fdt_strerror(pstate_max));
> +		return FWTS_ERROR;
> +	}
> +
> +	ret = fwts_dt_property_read_u32_arr(fw->fdt, offset, "ibm,pstate-ids",
> +					pstates, &len);
> +	if (ret != FWTS_OK) {
> +		fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTPropertyReadError",
> +			"Failed to read property ibm,pstate-ids %s",
> +			fdt_strerror(len));
> +		return FWTS_ERROR;
> +	}
> +
> +	nr_pstates = abs(pstate_max - pstate_min) + 1;
> +
> +	fwts_log_info(fw, "Pstates info: "
> +			"Pstate min: %d "
> +			"Pstate max: %d "
> +			"nr_pstates: %d "
> +			"Pstate ID's:  ",
> +			pstate_min, pstate_max, nr_pstates);
> +
> +	for (i = 0; i < nr_pstates; i++)
> +		fwts_log_info(fw, " %d ", pstates[i]);
> +
> +	if (nr_pstates <= 1 || nr_pstates > 128) {
> +		if (proc_gen == proc_gen_p8)
> +			fwts_log_warning(fw, "Pstates range %d is not valid",
> +					nr_pstates);
> +		else if (proc_gen == proc_gen_p9)
> +			fwts_log_warning(fw,
> +				"More than 128 pstates in pstate table %d",
> +				nr_pstates);
> +	}
> +
> +	if (len != nr_pstates)
> +		fwts_log_warning(fw, "Wrong number of pstates."
> +				"Expected %d pstates, found %d pstates",
> +				nr_pstates, len);
> +
> +	for (i = 0; i < nr_pstates; i++) {
> +		if (cmp_pstates(pstate_max, pstates[i]) < 0) {
> +			fwts_log_warning(fw, "Invalid Pstate id %d "
> +					"greater than max pstate %d",
> +					pstates[i], pstate_max);
> +			ok = false;
> +		}
> +		if (cmp_pstates(pstates[i], pstate_min) < 0) {
> +			fwts_log_warning(fw, "Invalid Pstate id %d "
> +					"lesser than min pstate %d",
> +					pstates[i], pstate_min);
> +			ok = false;
> +		}
> +	}
> +
> +	/* Pstates should be in monotonic descending order */
> +	for (i = 0; i < nr_pstates; i++) {
> +		if ((i == 0) && (cmp_pstates(pstates[i], pstate_max) != 0)) {
> +			fwts_log_warning(fw, "Pstates mismatch: "
> +					"Expected Pmax %d,"
> +					"Actual Pmax %d",
> +					pstate_max, pstates[i]);
> +			ok = false;
> +		} else if ((i == nr_pstates - 1) &&
> +			(cmp_pstates(pstates[i], pstate_min) != 0)) {
> +			fwts_log_warning(fw, "Pstates mismatch: "
> +					"Expected Pmin %d,"
> +					"Actual Pmin %d",
> +					pstate_min, pstates[i]);
> +			ok = false;
> +		} else {
> +			int previous_pstate;
> +			previous_pstate = pstates[i-1];
> +			if (cmp_pstates(pstates[i], previous_pstate) > 0) {
> +				fwts_log_warning(fw, "Non monotonicity ...,"
> +						"Pstate %d greater then"
> +						" previous Pstate %d",
> +						pstates[i], previous_pstate);
> +				ok = false;
> +			}
> +		}
> +	}
> +
> +	if (!ok) {
> +		fwts_failed(fw, LOG_LEVEL_MEDIUM, "CPUPstateLimitsTestFail",
> +		"One or few CPU Pstates DT validation tests failed");
> +		return FWTS_ERROR;
> +	}
> +	fwts_passed(fw, "CPU Frequency pstates are validated");
> +	return FWTS_OK;
> +
> +}
> +
> +static int cpuidle_states_test(fwts_framework *fw)
> +{
> +	int offset, len, test_len, ret;
> +	int latency_ns[CPUIDLE_STATE_MAX];
> +	int residency_ns[CPUIDLE_STATE_MAX];
> +	int flags[CPUIDLE_STATE_MAX];
> +	uint64_t pm_cr[CPUIDLE_STATE_MAX];
> +	uint64_t pm_cr_mask[CPUIDLE_STATE_MAX];
> +	bool has_stop_inst = false;
> +	bool ok = true;
> +	char *control_prop, *mask_prop;
> +
> +	switch (proc_gen) {
> +	case proc_gen_p8:
> +		has_stop_inst = false;
> +		control_prop = "ibm,cpu-idle-state-pmicr";
> +		mask_prop = "ibm,cpu-idle-state-pmicr-mask";
> +		break;
> +	case proc_gen_p9:
> +		has_stop_inst = true;
> +		control_prop = "ibm,cpu-idle-state-psscr";
> +		mask_prop = "ibm,cpu-idle-state-psscr-mask";
> +		break;
> +	default:
> +		fwts_failed(fw, LOG_LEVEL_HIGH, "UnknownProcessorChip",
> +			"Unknown processor generation %d", proc_gen);
> +		return FWTS_ERROR;
> +	}
> +
> +	offset = fdt_path_offset(fw->fdt, power_mgt_path);
> +	if (offset < 0) {
> +		fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTNodeMissing",
> +			"power management node %s is missing", power_mgt_path);
> +		return FWTS_ERROR;
> +	}
> +
> +	/* In P9 ibm,enabled-stop-levels present in /ibm,opal/power-mgt/ */
> +	if (has_stop_inst) {
> +		const int *buf;
> +
> +		buf = fdt_getprop(fw->fdt, offset, "ibm,enabled-stop-levels",
> +					&len);
> +		if (!buf) {
> +			fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTPropertyMissing",
> +				"ibm,enabled-stop-levels missing under %s",
> +				power_mgt_path);
> +			return FWTS_ERROR;
> +		}
> +	}
> +
> +	/* Validate ibm,cpu-idle-state-flags property */
> +	ret = fwts_dt_property_read_u32_arr(fw->fdt, offset,
> +			"ibm,cpu-idle-state-flags", flags, &len);
> +	if (ret != FWTS_OK) {
> +		fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTPropertyReadError",
> +			"Failed to read property ibm,cpu-idle-state-flags %s",
> +			fdt_strerror(len));
> +		return FWTS_ERROR;
> +	}
> +
> +	if (len < 0) {
> +		fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTNoIdleStates",
> +			"No idle states found in DT");
> +		return FWTS_ERROR;
> +	}
> +
> +	if (len > CPUIDLE_STATE_MAX-1) {
> +		fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTMoreIdleStates",
> +			"More idle states found in DT than the expected");
> +		return FWTS_ERROR;
> +	}
> +
> +	/* Validate ibm,cpu-idle-state-latencies-ns property */
> +	ret = fwts_dt_property_read_u32_arr(fw->fdt, offset,
> +			"ibm,cpu-idle-state-latencies-ns",
> +			latency_ns, &test_len);
> +	if (ret != FWTS_OK) {
> +		fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTPropertyReadError",
> +			"Failed to read property"
> +			" ibm,cpu-idle-state-latencies-ns %s",
> +			fdt_strerror(test_len));
> +		return FWTS_ERROR;
> +	}
> +
> +	if (validate_dt_prop_sizes(fw, "ibm,cpu-idle-state-flags", len,
> +		"ibm,cpu-idle-state-latencies-ns", test_len) != FWTS_OK)
> +		ok = false;
> +
> +	/* Validate ibm,cpu-idle-state-names property */
> +	test_len = fwts_dt_stringlist_count(fw, fw->fdt, offset,
> +				"ibm,cpu-idle-state-names");
> +	if (test_len > 0) {
> +		if (validate_dt_prop_sizes(fw, "ibm,cpu-idle-state-flags", len,
> +			"ibm,cpu-idle-state-names", test_len) != FWTS_OK)
> +			ok = false;
> +	}
> +
> +	/* Validate ibm,cpu-idle-state-residency-ns property */
> +	ret = fwts_dt_property_read_u32_arr(fw->fdt, offset,
> +			"ibm,cpu-idle-state-residency-ns",
> +			residency_ns, &test_len);
> +	if (ret != FWTS_OK) {
> +		fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTPropertyReadError",
> +			"Failed to read property "
> +			"ibm,cpu-idle-state-residency-ns %s",
> +			fdt_strerror(test_len));
> +		return FWTS_ERROR;
> +	}
> +
> +	if (validate_dt_prop_sizes(fw, "ibm,cpu-idle-state-flags", len,
> +		"ibm,cpu-idle-state-residency-ns", test_len) != FWTS_OK)
> +		ok = false;
> +
> +	/* Validate pmicr and psscr value and mask bits */
> +	ret = fwts_dt_property_read_u64_arr(fw->fdt, offset,
> +			control_prop, pm_cr, &test_len);
> +	if (ret != FWTS_OK) {
> +		fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTPropertyReadError",
> +			"Failed to read property %s rc: %s", control_prop,
> +			fdt_strerror(test_len));
> +		return FWTS_ERROR;
> +	}
> +
> +	if (validate_dt_prop_sizes(fw, "ibm,cpu-idle-state-flags", len,
> +			control_prop, test_len) != FWTS_OK)
> +		ok = false;
> +
> +	ret = fwts_dt_property_read_u64_arr(fw->fdt, offset,
> +		mask_prop, pm_cr_mask, &test_len);
> +	if (ret != FWTS_OK) {
> +		fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTPropertyReadError",
> +			"Failed to read property %s rc: %s", mask_prop,
> +			fdt_strerror(test_len));
> +		return FWTS_ERROR;
> +	}
> +
> +	if (validate_dt_prop_sizes(fw, "ibm,cpu-idle-state-flags", len,
> +			mask_prop, test_len) != FWTS_OK)
> +		ok = false;
> +
> +	if (!ok) {
> +		fwts_failed(fw, LOG_LEVEL_HIGH, "CPUIDLEStatesFail",
> +			"One or few CPU IDLE DT Validation tests failed");
> +		return FWTS_ERROR;
> +	}
> +	fwts_passed(fw, "CPU IDLE States are validated");
> +	return FWTS_OK;
> +}
> +
> +static fwts_framework_minor_test power_mgmt_tests[] = {
> +	{ pstate_limits_test, "OPAL Processor Frequency States Info" },
> +	{ cpuidle_states_test, "OPAL Processor Idle States Info" },
> +	{ NULL, NULL }
> +};
> +
> +static fwts_framework_ops power_mgmt_tests_ops = {
> +	.description = "OPAL Processor Power Management DT Validation Tests",
> +	.init        = power_mgmt_init,
> +	.minor_tests = power_mgmt_tests
> +};
> +
> +FWTS_REGISTER_FEATURES("power_mgmt", &power_mgmt_tests_ops, FWTS_TEST_EARLY,
> +		FWTS_FLAG_BATCH | FWTS_FLAG_ROOT_PRIV,
> +		FWTS_FW_FEATURE_DEVICETREE)
> 

That looks great. Thanks for the re-working. Much appreciated.

Acked-by: Colin Ian King <colin.king@canonical.com>
Alex Hung April 26, 2017, 3:14 p.m. UTC | #2
On 2017-04-22 09:20 PM, Pridhiviraj Paidipeddi wrote:
> This patch contains testcases for below Power Processor
> energey management subsystems.
>  	a. cpuidle
>  	b. cpufreq
>
> These testcases validate the device tree properties for these two
> subsystems which are got exposed to linux from the system
> firmware(OPAL).
>
> This patch is enhanced based on the initial patch developed by shilpa
> for pstates.
> https://lists.ubuntu.com/archives/fwts-devel/2016-May/007874.html
>
> Added cpuidle states DT Validation tests.
>
> Signed-off-by: Pridhiviraj Paidipeddi <ppaidipe@linux.vnet.ibm.com>
> ---
> Changes since V1:
>         - Changed to tabs instead of spaces wherever it is required
>         - Changed to new typedef proc_gen_t
>         - For unused args, used the FWTS_UNUSED macro
>         - Changed multi line comment style to fwts block comment style
>         - Changed mask type from int to const inst
> ---
>  src/Makefile.am                   |   1 +
>  src/lib/include/fwts_cpu.h        |  18 ++
>  src/lib/include/fwts_devicetree.h |  85 ++++++++
>  src/lib/src/fwts_devicetree.c     | 196 ++++++++++++++++++
>  src/opal/power_mgmt_info.c        | 409 ++++++++++++++++++++++++++++++++++++++
>  5 files changed, 709 insertions(+)
>  create mode 100644 src/opal/power_mgmt_info.c
>
> diff --git a/src/Makefile.am b/src/Makefile.am
> index c1eb285..e833554 100644
> --- a/src/Makefile.am
> +++ b/src/Makefile.am
> @@ -147,6 +147,7 @@ fwts_SOURCES = main.c 				\
>  	kernel/version/version.c 		\
>  	opal/mtd_info.c				\
>  	opal/prd_info.c				\
> +	opal/power_mgmt_info.c			\
>  	pci/aspm/aspm.c 			\
>  	pci/crs/crs.c 				\
>  	pci/maxreadreq/maxreadreq.c 		\
> diff --git a/src/lib/include/fwts_cpu.h b/src/lib/include/fwts_cpu.h
> index 9ab2ccf..be1c6cb 100644
> --- a/src/lib/include/fwts_cpu.h
> +++ b/src/lib/include/fwts_cpu.h
> @@ -33,6 +33,24 @@ typedef struct cpuinfo_x86 {
>  	char *flags;		/* String containing flags */
>  } fwts_cpuinfo_x86;
>
> +/* PowerPC Processor specific bits */
> +/* PVR definitions */
> +#define PVR_TYPE_P7     0x003f
> +#define PVR_TYPE_P7P    0x004a
> +#define PVR_TYPE_P8E    0x004b /* Murano */
> +#define PVR_TYPE_P8     0x004d /* Venice */
> +#define PVR_TYPE_P8NVL  0x004c /* Naples */
> +#define PVR_TYPE_P9     0x004e
> +
> +/* Processor generation */
> +typedef enum proc_gen {
> +	proc_gen_unknown,
> +	proc_gen_p7,            /* P7 and P7+ */
> +	proc_gen_p8,
> +	proc_gen_p9,
> +} proc_gen_t;
> +extern proc_gen_t proc_gen;
> +
>  typedef struct cpu_benchmark_result {
>  	bool		cycles_valid;
>  	uint64_t	loops;
> diff --git a/src/lib/include/fwts_devicetree.h b/src/lib/include/fwts_devicetree.h
> index 662f6ec..21c7807 100644
> --- a/src/lib/include/fwts_devicetree.h
> +++ b/src/lib/include/fwts_devicetree.h
> @@ -42,6 +42,28 @@
>  #if FWTS_HAS_DEVICETREE
>
>  int fwts_devicetree_read(fwts_framework *fwts);
> +int fwts_dt_property_read_u32(
> +	void *fdt,
> +	int offset,
> +	const char *pname,
> +	int *value);
> +int fwts_dt_property_read_u32_arr(
> +	void *fdt,
> +	int offset,
> +	const char *pname,
> +	int *value,
> +	int *len);
> +int fwts_dt_property_read_u64_arr(
> +	void *fdt,
> +	int offset,
> +	const char *pname,
> +	uint64_t *value,
> +	int *len);
> +int fwts_dt_stringlist_count(
> +	fwts_framework *fw,
> +	const void *fdt,
> +	int nodeoffset,
> +	const char *property);
>
>  #else /* !FWTS_HAS_DEVICETREE */
>  static inline int fwts_devicetree_read(fwts_framework *fwts)
> @@ -50,6 +72,67 @@ static inline int fwts_devicetree_read(fwts_framework *fwts)
>
>  	return FWTS_OK;
>  }
> +
> +int fwts_dt_property_read_u32(
> +	void *fdt,
> +	int offset,
> +	const char *pname,
> +	int *value)
> +{
> +	FWTS_UNUSED(fdt);
> +	FWTS_UNUSED(offset);
> +	FWTS_UNUSED(pname);
> +	FWTS_UNUSED(value);
> +
> +	return FWTS_OK;
> +}
> +
> +int fwts_dt_property_read_u32_arr(
> +	void *fdt,
> +	int offset,
> +	const char *pname,
> +	int *value,
> +	int *len)
> +{
> +	FWTS_UNUSED(fdt);
> +	FWTS_UNUSED(offset);
> +	FWTS_UNUSED(pname);
> +	FWTS_UNUSED(value);
> +	FWTS_UNUSED(len);
> +
> +	return FWTS_OK;
> +}
> +
> +int fwts_dt_property_read_u64_arr(
> +	void *fdt,
> +	int offset,
> +	const char *pname,
> +	uint64_t *value,
> +	int *len)
> +{
> +	FWTS_UNUSED(fdt);
> +	FWTS_UNUSED(offset);
> +	FWTS_UNUSED(pname);
> +	FWTS_UNUSED(value);
> +	FWTS_UNUSED(len);
> +
> +	return FWTS_OK;
> +}
> +
> +int fwts_dt_stringlist_count(
> +	fwts_framework *fw,
> +	const void *fdt,
> +	int nodeoffset,
> +	const char *property)
> +{
> +	FWTS_UNUSED(fw);
> +	FWTS_UNUSED(fdt);
> +	FWTS_UNUSED(nodeoffset);
> +	FWTS_UNUSED(property);
> +
> +	return FWTS_OK;
> +}
> +
>  #endif
>
>  bool check_status_property_okay(fwts_framework *fw,
> @@ -60,4 +143,6 @@ int check_property_printable(fwts_framework *fw,
>
>  char *hidewhitespace(char *name);
>
> +int get_proc_gen(fwts_framework *fw);
> +
>  #endif
> diff --git a/src/lib/src/fwts_devicetree.c b/src/lib/src/fwts_devicetree.c
> index bf5686a..2fea3c8 100644
> --- a/src/lib/src/fwts_devicetree.c
> +++ b/src/lib/src/fwts_devicetree.c
> @@ -26,6 +26,8 @@
>
>  #include <libfdt.h>
>
> +proc_gen_t proc_gen;
> +
>  int fwts_devicetree_read(fwts_framework *fwts)
>  {
>  	char *command, *data = NULL;
> @@ -171,3 +173,197 @@ char *hidewhitespace(char *name)
>  	return name;
>
>  }
> +
> +/*
> + * fwts_dt_property_read_u32 This function reads one u32 DT property
> + * 	Returns FWTS_OK on success:	*value will contain one int
> + * 	FWTS_ERROR on error:		*value will contain error code
> + */
> +
> +int fwts_dt_property_read_u32(
> +	void *fdt,
> +	int offset,
> +	const char *pname,
> +	int *value)
> +{
> +	int len;
> +	const int *buf;
> +
> +	buf = fdt_getprop(fdt, offset, pname, &len);
> +	if (buf == NULL) {
> +		*value = len;
> +		return FWTS_ERROR;
> +	}
> +	*value = be32toh(*buf);
> +	return FWTS_OK;
> +}
> +
> +/*
> + * This function reads DT property array of u32's
> + * 	Return FWTS_OK on success:	*value contain full array of int's
> + *  	FWTS_ERROR on error:		*value will contain error code which
> + *  						comes from *len
> + */
> +
> +int fwts_dt_property_read_u32_arr(
> +	void *fdt,
> +	int offset,
> +	const char *pname,
> +	int *value,
> +	int *len)
> +{
> +	int i;
> +	const int *buf;
> +
> +	buf = fdt_getprop(fdt, offset, pname, len);
> +	if (buf == NULL) {
> +		*value = *len;
> +		return FWTS_ERROR;
> +	}
> +
> +	*len = *len / sizeof(int);
> +	for (i = 0; i < *len; i++)
> +		value[i] = be32toh(buf[i]);
> +	return FWTS_OK;
> +}
> +
> +/*
> + * This function reads DT property array of u64's
> + * 	Return FWTS_OK on success:	*value contain full array of u64's
> + * 	FWTS_ERROR on error:		*value will contain error code which
> + * 						comes from *len
> + */
> +
> +int fwts_dt_property_read_u64_arr(
> +	void *fdt,
> +	int offset,
> +	const char *pname,
> +	uint64_t *value,
> +	int *len)
> +{
> +	int i;
> +	const int *buf;
> +
> +	buf = fdt_getprop(fdt, offset, pname, len);
> +	if (buf == NULL) {
> +		*value = *len;
> +		return FWTS_ERROR;
> +	}
> +
> +	*len = *len / sizeof(uint64_t);
> +	for (i = 0; i < *len; i++)
> +		value[i] = be64toh(buf[i]);
> +	return FWTS_OK;
> +}
> +
> +/* Get's the length of DT property string list */
> +
> +int fwts_dt_stringlist_count(
> +	fwts_framework *fw,
> +	const void *fdt,
> +	int nodeoffset,
> +	const char *property)
> +{
> +	const char *list, *end;
> +	int length, count = 0;
> +
> +	list = fdt_getprop(fdt, nodeoffset, property, &length);
> +	if (!list) {
> +		fwts_failed(fw, LOG_LEVEL_HIGH, "PropertyNotFound",
> +			"Failed to get property %s rc %d", property, length);
> +		return FWTS_ERROR;
> +	}
> +
> +	end = list + length;
> +
> +	while (list < end) {
> +		length = strnlen(list, end - list) + 1;
> +
> +		/* Check if the last string isn't properly NUL-terminated. */
> +		if (list + length > end) {
> +			fwts_failed(fw, LOG_LEVEL_HIGH, "NotNULLTerminated",
> +				"Last string is not properly NULL terminated");
> +			return FWTS_ERROR;
> +		}
> +
> +		list += length;
> +		count++;
> +	}
> +
> +	return count;
> +}
> +
> +static int get_cpu_version(fwts_framework *fw, int *value)
> +{
> +	const char *cpus_path = "/cpus/";
> +	int offset;
> +	int cpu_version; int ret;
> +
> +	if (!fw->fdt) {
> +		fwts_skipped(fw, "Device tree not found");
> +		return FWTS_SKIP;
> +	}
> +
> +	offset = fdt_path_offset(fw->fdt, cpus_path);
> +	if (offset < 0) {
> +		fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTNodeMissing",
> +			"/cpus node is missing");
> +		return FWTS_ERROR;
> +	}
> +
> +	offset = fdt_node_offset_by_prop_value(fw->fdt, -1, "device_type",
> +				"cpu", sizeof("cpu"));
> +	if (offset < 0) {
> +		fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTNodeMissing",
> +			"cpu node is missing");
> +		return FWTS_ERROR;
> +	}
> +
> +	ret = fwts_dt_property_read_u32(fw->fdt, offset,
> +			"cpu-version", &cpu_version);
> +	if (ret != FWTS_OK) {
> +		fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTPropertyReadError",
> +			"Failed to read property cpu-version %s",
> +			fdt_strerror(cpu_version));
> +		return FWTS_ERROR;
> +	}
> +	*value = cpu_version;
> +	return FWTS_OK;
> +}
> +
> +int get_proc_gen(fwts_framework *fw)
> +{
> +	int version; int ret;
> +	const int mask = 0xFFFF0000;
> +	int pvr;
> +
> +	ret = get_cpu_version(fw, &version);
> +	if (ret != FWTS_OK) {
> +		fwts_failed(fw, LOG_LEVEL_HIGH, "DTNoCPUVersion",
> +			"Not able to get the CPU version");
> +		return FWTS_ERROR;
> +	}
> +
> +	pvr = (mask & version) >> 16;
> +	switch (pvr) {
> +	/* Get CPU family and other flags based on PVR */
> +	case PVR_TYPE_P7:
> +	case PVR_TYPE_P7P:
> +		proc_gen = proc_gen_p7;
> +		break;
> +	case PVR_TYPE_P8E:
> +	case PVR_TYPE_P8:
> +		proc_gen = proc_gen_p8;
> +		break;
> +	case PVR_TYPE_P8NVL:
> +		proc_gen = proc_gen_p8;
> +		break;
> +	case PVR_TYPE_P9:
> +		proc_gen = proc_gen_p9;
> +		break;
> +	default:
> +		proc_gen = proc_gen_unknown;
> +	}
> +
> +	return FWTS_OK;
> +}
> diff --git a/src/opal/power_mgmt_info.c b/src/opal/power_mgmt_info.c
> new file mode 100644
> index 0000000..5456c43
> --- /dev/null
> +++ b/src/opal/power_mgmt_info.c
> @@ -0,0 +1,409 @@
> +/*
> + * Copyright (C) 2010-2017 Canonical
> + * Some of this work - Copyright (C) 2016-2017 IBM
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; either version 2
> + * of the License, or (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
> + *
> + */
> +
> +#include <fcntl.h>
> +#include <stdlib.h>
> +#include <sys/ioctl.h>
> +
> +#include "fwts.h"
> +
> +#ifdef HAVE_LIBFDT
> +#include <libfdt.h>
> +#endif
> +
> +#define MAX_PSTATES 256
> +
> +#define CPUIDLE_STATE_MAX   10
> +
> +proc_gen_t proc_gen;
> +
> +static const char *power_mgt_path = "/ibm,opal/power-mgt/";
> +
> +/**
> + * cmp_pstates: Compares the given two pstates and determines which
> + *              among them is associated with a higher pstate.
> + *
> + * @a,@b: The pstate ids of the pstates being compared.
> + *
> + * Returns: -1 : If pstate associated with @a is smaller than
> + *               the pstate associated with @b.
> + *      0 : If pstates associated with @a and @b are equal.
> + *      1 : If pstate associated with @a is greater than
> + *               the pstate associated with @b.
> + */
> +static int (*cmp_pstates)(int a, int b);
> +
> +static int cmp_positive_pstates(int a, int b)
> +{
> +	if (a > b)
> +		return -1;
> +	else if (a < b)
> +		return 1;
> +
> +	return 0;
> +}
> +
> +static int cmp_negative_pstates(int a, int b)
> +{
> +	return cmp_positive_pstates(b, a);
> +}
> +
> +static int validate_dt_prop_sizes(
> +	fwts_framework *fw,
> +	const char *prop1,
> +	int prop1_len,
> +	const char *prop2,
> +	int prop2_len)
> +{
> +	if (prop1_len == prop2_len)
> +		return FWTS_OK;
> +
> +	fwts_failed(fw, LOG_LEVEL_HIGH, "SizeMismatch",
> +		"array sizes don't match for %s len %d and %s len %d\n",
> +		prop1, prop1_len, prop2, prop2_len);
> +
> +	return FWTS_ERROR;
> +}
> +
> +static int power_mgmt_init(fwts_framework *fw)
> +{
> +	int ret;
> +
> +	if (fwts_firmware_detect() != FWTS_FIRMWARE_OPAL) {
> +		fwts_skipped(fw,
> +			"The firmware type detected was non OPAL "
> +			"so skipping the OPAL Power Management DT checks.");
> +		return FWTS_SKIP;
> +	}
> +
> +	if (!fw->fdt) {
> +		fwts_failed(fw, LOG_LEVEL_HIGH, "NoDeviceTree",
> +			"Device tree not found");
> +		return FWTS_ERROR;
> +	}
> +
> +	ret = get_proc_gen(fw);
> +	if (ret != FWTS_OK) {
> +		fwts_failed(fw, LOG_LEVEL_HIGH, "ProcGenFail",
> +			"Failed to get the Processor generation");
> +		return FWTS_ERROR;
> +	}
> +
> +	return FWTS_OK;
> +}
> +
> +
> +static int pstate_limits_test(fwts_framework *fw)
> +{
> +	int pstate_min, pstate_max, pstates[MAX_PSTATES];
> +	bool ok = true;
> +	int  nr_pstates, offset, len, ret, i;
> +
> +	switch (proc_gen) {
> +	case proc_gen_p8:
> +		cmp_pstates = cmp_negative_pstates;
> +		break;
> +	case proc_gen_p9:
> +		cmp_pstates = cmp_positive_pstates;
> +		break;
> +	default:
> +		fwts_failed(fw, LOG_LEVEL_HIGH, "UnknownProcessorChip",
> +			"Unknown processor generation %d", proc_gen);
> +		return FWTS_ERROR;
> +	}
> +
> +	offset = fdt_path_offset(fw->fdt, power_mgt_path);
> +	if (offset < 0) {
> +		fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTNodeMissing",
> +			"power management node %s is missing", power_mgt_path);
> +		return FWTS_ERROR;
> +	}
> +
> +	ret = fwts_dt_property_read_u32(fw->fdt, offset, "ibm,pstate-min",
> +					&pstate_min);
> +	if (ret != FWTS_OK) {
> +		fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTPropertyReadError",
> +			"Failed to read property ibm,pstate-min %s",
> +			fdt_strerror(pstate_min));
> +		return FWTS_ERROR;
> +	}
> +
> +	ret = fwts_dt_property_read_u32(fw->fdt, offset, "ibm,pstate-max",
> +					&pstate_max);
> +	if (ret != FWTS_OK) {
> +		fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTPropertyReadError",
> +			"Failed to read property ibm,pstate-max %s",
> +			fdt_strerror(pstate_max));
> +		return FWTS_ERROR;
> +	}
> +
> +	ret = fwts_dt_property_read_u32_arr(fw->fdt, offset, "ibm,pstate-ids",
> +					pstates, &len);
> +	if (ret != FWTS_OK) {
> +		fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTPropertyReadError",
> +			"Failed to read property ibm,pstate-ids %s",
> +			fdt_strerror(len));
> +		return FWTS_ERROR;
> +	}
> +
> +	nr_pstates = abs(pstate_max - pstate_min) + 1;
> +
> +	fwts_log_info(fw, "Pstates info: "
> +			"Pstate min: %d "
> +			"Pstate max: %d "
> +			"nr_pstates: %d "
> +			"Pstate ID's:  ",
> +			pstate_min, pstate_max, nr_pstates);
> +
> +	for (i = 0; i < nr_pstates; i++)
> +		fwts_log_info(fw, " %d ", pstates[i]);
> +
> +	if (nr_pstates <= 1 || nr_pstates > 128) {
> +		if (proc_gen == proc_gen_p8)
> +			fwts_log_warning(fw, "Pstates range %d is not valid",
> +					nr_pstates);
> +		else if (proc_gen == proc_gen_p9)
> +			fwts_log_warning(fw,
> +				"More than 128 pstates in pstate table %d",
> +				nr_pstates);
> +	}
> +
> +	if (len != nr_pstates)
> +		fwts_log_warning(fw, "Wrong number of pstates."
> +				"Expected %d pstates, found %d pstates",
> +				nr_pstates, len);
> +
> +	for (i = 0; i < nr_pstates; i++) {
> +		if (cmp_pstates(pstate_max, pstates[i]) < 0) {
> +			fwts_log_warning(fw, "Invalid Pstate id %d "
> +					"greater than max pstate %d",
> +					pstates[i], pstate_max);
> +			ok = false;
> +		}
> +		if (cmp_pstates(pstates[i], pstate_min) < 0) {
> +			fwts_log_warning(fw, "Invalid Pstate id %d "
> +					"lesser than min pstate %d",
> +					pstates[i], pstate_min);
> +			ok = false;
> +		}
> +	}
> +
> +	/* Pstates should be in monotonic descending order */
> +	for (i = 0; i < nr_pstates; i++) {
> +		if ((i == 0) && (cmp_pstates(pstates[i], pstate_max) != 0)) {
> +			fwts_log_warning(fw, "Pstates mismatch: "
> +					"Expected Pmax %d,"
> +					"Actual Pmax %d",
> +					pstate_max, pstates[i]);
> +			ok = false;
> +		} else if ((i == nr_pstates - 1) &&
> +			(cmp_pstates(pstates[i], pstate_min) != 0)) {
> +			fwts_log_warning(fw, "Pstates mismatch: "
> +					"Expected Pmin %d,"
> +					"Actual Pmin %d",
> +					pstate_min, pstates[i]);
> +			ok = false;
> +		} else {
> +			int previous_pstate;
> +			previous_pstate = pstates[i-1];
> +			if (cmp_pstates(pstates[i], previous_pstate) > 0) {
> +				fwts_log_warning(fw, "Non monotonicity ...,"
> +						"Pstate %d greater then"
> +						" previous Pstate %d",
> +						pstates[i], previous_pstate);
> +				ok = false;
> +			}
> +		}
> +	}
> +
> +	if (!ok) {
> +		fwts_failed(fw, LOG_LEVEL_MEDIUM, "CPUPstateLimitsTestFail",
> +		"One or few CPU Pstates DT validation tests failed");
> +		return FWTS_ERROR;
> +	}
> +	fwts_passed(fw, "CPU Frequency pstates are validated");
> +	return FWTS_OK;
> +
> +}
> +
> +static int cpuidle_states_test(fwts_framework *fw)
> +{
> +	int offset, len, test_len, ret;
> +	int latency_ns[CPUIDLE_STATE_MAX];
> +	int residency_ns[CPUIDLE_STATE_MAX];
> +	int flags[CPUIDLE_STATE_MAX];
> +	uint64_t pm_cr[CPUIDLE_STATE_MAX];
> +	uint64_t pm_cr_mask[CPUIDLE_STATE_MAX];
> +	bool has_stop_inst = false;
> +	bool ok = true;
> +	char *control_prop, *mask_prop;
> +
> +	switch (proc_gen) {
> +	case proc_gen_p8:
> +		has_stop_inst = false;
> +		control_prop = "ibm,cpu-idle-state-pmicr";
> +		mask_prop = "ibm,cpu-idle-state-pmicr-mask";
> +		break;
> +	case proc_gen_p9:
> +		has_stop_inst = true;
> +		control_prop = "ibm,cpu-idle-state-psscr";
> +		mask_prop = "ibm,cpu-idle-state-psscr-mask";
> +		break;
> +	default:
> +		fwts_failed(fw, LOG_LEVEL_HIGH, "UnknownProcessorChip",
> +			"Unknown processor generation %d", proc_gen);
> +		return FWTS_ERROR;
> +	}
> +
> +	offset = fdt_path_offset(fw->fdt, power_mgt_path);
> +	if (offset < 0) {
> +		fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTNodeMissing",
> +			"power management node %s is missing", power_mgt_path);
> +		return FWTS_ERROR;
> +	}
> +
> +	/* In P9 ibm,enabled-stop-levels present in /ibm,opal/power-mgt/ */
> +	if (has_stop_inst) {
> +		const int *buf;
> +
> +		buf = fdt_getprop(fw->fdt, offset, "ibm,enabled-stop-levels",
> +					&len);
> +		if (!buf) {
> +			fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTPropertyMissing",
> +				"ibm,enabled-stop-levels missing under %s",
> +				power_mgt_path);
> +			return FWTS_ERROR;
> +		}
> +	}
> +
> +	/* Validate ibm,cpu-idle-state-flags property */
> +	ret = fwts_dt_property_read_u32_arr(fw->fdt, offset,
> +			"ibm,cpu-idle-state-flags", flags, &len);
> +	if (ret != FWTS_OK) {
> +		fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTPropertyReadError",
> +			"Failed to read property ibm,cpu-idle-state-flags %s",
> +			fdt_strerror(len));
> +		return FWTS_ERROR;
> +	}
> +
> +	if (len < 0) {
> +		fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTNoIdleStates",
> +			"No idle states found in DT");
> +		return FWTS_ERROR;
> +	}
> +
> +	if (len > CPUIDLE_STATE_MAX-1) {
> +		fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTMoreIdleStates",
> +			"More idle states found in DT than the expected");
> +		return FWTS_ERROR;
> +	}
> +
> +	/* Validate ibm,cpu-idle-state-latencies-ns property */
> +	ret = fwts_dt_property_read_u32_arr(fw->fdt, offset,
> +			"ibm,cpu-idle-state-latencies-ns",
> +			latency_ns, &test_len);
> +	if (ret != FWTS_OK) {
> +		fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTPropertyReadError",
> +			"Failed to read property"
> +			" ibm,cpu-idle-state-latencies-ns %s",
> +			fdt_strerror(test_len));
> +		return FWTS_ERROR;
> +	}
> +
> +	if (validate_dt_prop_sizes(fw, "ibm,cpu-idle-state-flags", len,
> +		"ibm,cpu-idle-state-latencies-ns", test_len) != FWTS_OK)
> +		ok = false;
> +
> +	/* Validate ibm,cpu-idle-state-names property */
> +	test_len = fwts_dt_stringlist_count(fw, fw->fdt, offset,
> +				"ibm,cpu-idle-state-names");
> +	if (test_len > 0) {
> +		if (validate_dt_prop_sizes(fw, "ibm,cpu-idle-state-flags", len,
> +			"ibm,cpu-idle-state-names", test_len) != FWTS_OK)
> +			ok = false;
> +	}
> +
> +	/* Validate ibm,cpu-idle-state-residency-ns property */
> +	ret = fwts_dt_property_read_u32_arr(fw->fdt, offset,
> +			"ibm,cpu-idle-state-residency-ns",
> +			residency_ns, &test_len);
> +	if (ret != FWTS_OK) {
> +		fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTPropertyReadError",
> +			"Failed to read property "
> +			"ibm,cpu-idle-state-residency-ns %s",
> +			fdt_strerror(test_len));
> +		return FWTS_ERROR;
> +	}
> +
> +	if (validate_dt_prop_sizes(fw, "ibm,cpu-idle-state-flags", len,
> +		"ibm,cpu-idle-state-residency-ns", test_len) != FWTS_OK)
> +		ok = false;
> +
> +	/* Validate pmicr and psscr value and mask bits */
> +	ret = fwts_dt_property_read_u64_arr(fw->fdt, offset,
> +			control_prop, pm_cr, &test_len);
> +	if (ret != FWTS_OK) {
> +		fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTPropertyReadError",
> +			"Failed to read property %s rc: %s", control_prop,
> +			fdt_strerror(test_len));
> +		return FWTS_ERROR;
> +	}
> +
> +	if (validate_dt_prop_sizes(fw, "ibm,cpu-idle-state-flags", len,
> +			control_prop, test_len) != FWTS_OK)
> +		ok = false;
> +
> +	ret = fwts_dt_property_read_u64_arr(fw->fdt, offset,
> +		mask_prop, pm_cr_mask, &test_len);
> +	if (ret != FWTS_OK) {
> +		fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTPropertyReadError",
> +			"Failed to read property %s rc: %s", mask_prop,
> +			fdt_strerror(test_len));
> +		return FWTS_ERROR;
> +	}
> +
> +	if (validate_dt_prop_sizes(fw, "ibm,cpu-idle-state-flags", len,
> +			mask_prop, test_len) != FWTS_OK)
> +		ok = false;
> +
> +	if (!ok) {
> +		fwts_failed(fw, LOG_LEVEL_HIGH, "CPUIDLEStatesFail",
> +			"One or few CPU IDLE DT Validation tests failed");
> +		return FWTS_ERROR;
> +	}
> +	fwts_passed(fw, "CPU IDLE States are validated");
> +	return FWTS_OK;
> +}
> +
> +static fwts_framework_minor_test power_mgmt_tests[] = {
> +	{ pstate_limits_test, "OPAL Processor Frequency States Info" },
> +	{ cpuidle_states_test, "OPAL Processor Idle States Info" },
> +	{ NULL, NULL }
> +};
> +
> +static fwts_framework_ops power_mgmt_tests_ops = {
> +	.description = "OPAL Processor Power Management DT Validation Tests",
> +	.init        = power_mgmt_init,
> +	.minor_tests = power_mgmt_tests
> +};
> +
> +FWTS_REGISTER_FEATURES("power_mgmt", &power_mgmt_tests_ops, FWTS_TEST_EARLY,
> +		FWTS_FLAG_BATCH | FWTS_FLAG_ROOT_PRIV,
> +		FWTS_FW_FEATURE_DEVICETREE)
>

When I merged and compiled this patch, I got a lot of error messages 
such as below:

.libs/libfwts_la-fwts_acpi_object_eval.o: In function 
`fwts_dt_property_read_u32':
/home/alexhung/src/fwts/src/lib/src/../../../src/lib/include/fwts_devicetree.h:88: 
multiple definition of `fwts_dt_property_read_u32'
.libs/libfwts_la-fwts_ac_adapter.o:/home/alexhung/src/fwts/src/lib/src/../../../src/lib/include/fwts_devicetree.h:88: 
first defined here
.libs/libfwts_la-fwts_acpi_object_eval.o: In function 
`fwts_dt_property_read_u32_arr':
/home/alexhung/src/fwts/src/lib/src/../../../src/lib/include/fwts_devicetree.h:88: 
multiple definition of `fwts_dt_property_read_u32_arr'
.libs/libfwts_la-fwts_ac_adapter.o:/home/alexhung/src/fwts/src/lib/src/../../../src/lib/include/fwts_devicetree.h:88: 
first defined here
.libs/libfwts_la-fwts_acpi_object_eval.o: In function 
`fwts_dt_property_read_u64_arr':
/home/alexhung/src/fwts/src/lib/src/../../../src/lib/include/fwts_devicetree.h:120: 
multiple definition of `fwts_dt_property_read_u64_arr'
.libs/libfwts_la-fwts_ac_adapter.o:/home/alexhung/src/fwts/src/lib/src/../../../src/lib/include/fwts_devicetree.h:120: 
first defined here
.libs/libfwts_la-fwts_acpi_object_eval.o: In function 
`fwts_dt_stringlist_count':
/home/alexhung/src/fwts/src/lib/src/../../../src/lib/include/fwts_devicetree.h:134: 
multiple definition of `fwts_dt_stringlist_count'
.libs/libfwts_la-fwts_ac_adapter.o:/home/alexhung/src/fwts/src/lib/src/../../../src/lib/include/fwts_devicetree.h:134: 
first defined here
.libs/libfwts_la-fwts_acpi_tables.o: In function 
`fwts_dt_property_read_u32':
/home/alexhung/src/fwts/src/lib/src/../../../src/lib/include/fwts_devicetree.h:88: 
multiple definition of `fwts_dt_property_read_u32'
.libs/libfwts_la-fwts_ac_adapter.o:/home/alexhung/src/fwts/src/lib/src/../../../src/lib/include/fwts_devicetree.h:88: 
first defined here
.libs/libfwts_la-fwts_acpi_tables.o: In function 
`fwts_dt_property_read_u32_arr':
/home/alexhung/src/fwts/src/lib/src/../../../src/lib/include/fwts_devicetree.h:88: 
multiple definition of `fwts_dt_property_read_u32_arr'
.libs/libfwts_la-fwts_ac_adapter.o:/home/alexhung/src/fwts/src/lib/src/../../../src/lib/include/fwts_devicetree.h:88: 
first defined here
.libs/libfwts_la-fwts_acpi_tables.o: In function 
`fwts_dt_property_read_u64_arr':
/home/alexhung/src/fwts/src/lib/src/../../../src/lib/include/fwts_devicetree.h:120: 
multiple definition of `fwts_dt_property_read_u64_arr'
.libs/libfwts_la-fwts_ac_adapter.o:/home/alexhung/src/fwts/src/lib/src/../../../src/lib/include/fwts_devicetree.h:120: 
first defined here
.libs/libfwts_la-fwts_acpi_tables.o: In function `fwts_dt_stringlist_count':
/home/alexhung/src/fwts/src/lib/src/../../../src/lib/include/fwts_devicetree.h:134: 
multiple definition of `fwts_dt_stringlist_count'
.libs/libfwts_la-fwts_ac_adapter.o:/home/alexhung/src/fwts/src/lib/src/../../../src/lib/include/fwts_devicetree.h:134: 
first defined here
.libs/libfwts_la-fwts_acpi.o: In function `fwts_dt_property_read_u32':
/home/alexhung/src/fwts/src/lib/src/../../../src/lib/include/fwts_devicetree.h:88: 
multiple definition of `fwts_dt_property_read_u32'
.libs/libfwts_la-fwts_ac_adapter.o:/home/alexhung/src/fwts/src/lib/src/../../../src/lib/include/fwts_devicetree.h:88: 
first defined here
.libs/libfwts_la-fwts_acpi.o: In function `fwts_dt_property_read_u32_arr':
/home/alexhung/src/fwts/src/lib/src/../../../src/lib/include/fwts_devicetree.h:88: 
multiple definition of `fwts_dt_property_read_u32_arr'
.libs/libfwts_la-fwts_ac_adapter.o:/home/alexhung/src/fwts/src/lib/src/../../../src/lib/include/fwts_devicetree.h:88: 
first defined here
.libs/libfwts_la-fwts_acpi.o: In function `fwts_dt_property_read_u64_arr':
/home/alexhung/src/fwts/src/lib/src/../../../src/lib/include/fwts_devicetree.h:120: 
multiple definition of `fwts_dt_property_read_u64_arr'
......

Cheers,
Alex Hung
ppaidipe April 27, 2017, 3:08 a.m. UTC | #3
On 2017-04-24 13:41, Colin Ian King wrote:
> On 23/04/17 05:20, Pridhiviraj Paidipeddi wrote:
>> This patch contains testcases for below Power Processor
>> energey management subsystems.
>>  	a. cpuidle
>>  	b. cpufreq
>> 
>> These testcases validate the device tree properties for these two
>> subsystems which are got exposed to linux from the system
>> firmware(OPAL).
>> 
>> This patch is enhanced based on the initial patch developed by shilpa
>> for pstates.
>> https://lists.ubuntu.com/archives/fwts-devel/2016-May/007874.html
>> 
>> Added cpuidle states DT Validation tests.
>> 
>> Signed-off-by: Pridhiviraj Paidipeddi <ppaidipe@linux.vnet.ibm.com>
>> ---
>> Changes since V1:
>>         - Changed to tabs instead of spaces wherever it is required
>>         - Changed to new typedef proc_gen_t
>>         - For unused args, used the FWTS_UNUSED macro
>>         - Changed multi line comment style to fwts block comment style
>>         - Changed mask type from int to const inst
>> ---
>>  src/Makefile.am                   |   1 +
>>  src/lib/include/fwts_cpu.h        |  18 ++
>>  src/lib/include/fwts_devicetree.h |  85 ++++++++
>>  src/lib/src/fwts_devicetree.c     | 196 ++++++++++++++++++
>>  src/opal/power_mgmt_info.c        | 409 
>> ++++++++++++++++++++++++++++++++++++++
>>  5 files changed, 709 insertions(+)
>>  create mode 100644 src/opal/power_mgmt_info.c
>> 
>> diff --git a/src/Makefile.am b/src/Makefile.am
>> index c1eb285..e833554 100644
>> --- a/src/Makefile.am
>> +++ b/src/Makefile.am
>> @@ -147,6 +147,7 @@ fwts_SOURCES = main.c 				\
>>  	kernel/version/version.c 		\
>>  	opal/mtd_info.c				\
>>  	opal/prd_info.c				\
>> +	opal/power_mgmt_info.c			\
>>  	pci/aspm/aspm.c 			\
>>  	pci/crs/crs.c 				\
>>  	pci/maxreadreq/maxreadreq.c 		\
>> diff --git a/src/lib/include/fwts_cpu.h b/src/lib/include/fwts_cpu.h
>> index 9ab2ccf..be1c6cb 100644
>> --- a/src/lib/include/fwts_cpu.h
>> +++ b/src/lib/include/fwts_cpu.h
>> @@ -33,6 +33,24 @@ typedef struct cpuinfo_x86 {
>>  	char *flags;		/* String containing flags */
>>  } fwts_cpuinfo_x86;
>> 
>> +/* PowerPC Processor specific bits */
>> +/* PVR definitions */
>> +#define PVR_TYPE_P7     0x003f
>> +#define PVR_TYPE_P7P    0x004a
>> +#define PVR_TYPE_P8E    0x004b /* Murano */
>> +#define PVR_TYPE_P8     0x004d /* Venice */
>> +#define PVR_TYPE_P8NVL  0x004c /* Naples */
>> +#define PVR_TYPE_P9     0x004e
>> +
>> +/* Processor generation */
>> +typedef enum proc_gen {
>> +	proc_gen_unknown,
>> +	proc_gen_p7,            /* P7 and P7+ */
>> +	proc_gen_p8,
>> +	proc_gen_p9,
>> +} proc_gen_t;
>> +extern proc_gen_t proc_gen;
>> +
>>  typedef struct cpu_benchmark_result {
>>  	bool		cycles_valid;
>>  	uint64_t	loops;
>> diff --git a/src/lib/include/fwts_devicetree.h 
>> b/src/lib/include/fwts_devicetree.h
>> index 662f6ec..21c7807 100644
>> --- a/src/lib/include/fwts_devicetree.h
>> +++ b/src/lib/include/fwts_devicetree.h
>> @@ -42,6 +42,28 @@
>>  #if FWTS_HAS_DEVICETREE
>> 
>>  int fwts_devicetree_read(fwts_framework *fwts);
>> +int fwts_dt_property_read_u32(
>> +	void *fdt,
>> +	int offset,
>> +	const char *pname,
>> +	int *value);
>> +int fwts_dt_property_read_u32_arr(
>> +	void *fdt,
>> +	int offset,
>> +	const char *pname,
>> +	int *value,
>> +	int *len);
>> +int fwts_dt_property_read_u64_arr(
>> +	void *fdt,
>> +	int offset,
>> +	const char *pname,
>> +	uint64_t *value,
>> +	int *len);
>> +int fwts_dt_stringlist_count(
>> +	fwts_framework *fw,
>> +	const void *fdt,
>> +	int nodeoffset,
>> +	const char *property);
>> 
>>  #else /* !FWTS_HAS_DEVICETREE */
>>  static inline int fwts_devicetree_read(fwts_framework *fwts)
>> @@ -50,6 +72,67 @@ static inline int 
>> fwts_devicetree_read(fwts_framework *fwts)
>> 
>>  	return FWTS_OK;
>>  }
>> +
>> +int fwts_dt_property_read_u32(
>> +	void *fdt,
>> +	int offset,
>> +	const char *pname,
>> +	int *value)
>> +{
>> +	FWTS_UNUSED(fdt);
>> +	FWTS_UNUSED(offset);
>> +	FWTS_UNUSED(pname);
>> +	FWTS_UNUSED(value);
>> +
>> +	return FWTS_OK;
>> +}
>> +
>> +int fwts_dt_property_read_u32_arr(
>> +	void *fdt,
>> +	int offset,
>> +	const char *pname,
>> +	int *value,
>> +	int *len)
>> +{
>> +	FWTS_UNUSED(fdt);
>> +	FWTS_UNUSED(offset);
>> +	FWTS_UNUSED(pname);
>> +	FWTS_UNUSED(value);
>> +	FWTS_UNUSED(len);
>> +
>> +	return FWTS_OK;
>> +}
>> +
>> +int fwts_dt_property_read_u64_arr(
>> +	void *fdt,
>> +	int offset,
>> +	const char *pname,
>> +	uint64_t *value,
>> +	int *len)
>> +{
>> +	FWTS_UNUSED(fdt);
>> +	FWTS_UNUSED(offset);
>> +	FWTS_UNUSED(pname);
>> +	FWTS_UNUSED(value);
>> +	FWTS_UNUSED(len);
>> +
>> +	return FWTS_OK;
>> +}
>> +
>> +int fwts_dt_stringlist_count(
>> +	fwts_framework *fw,
>> +	const void *fdt,
>> +	int nodeoffset,
>> +	const char *property)
>> +{
>> +	FWTS_UNUSED(fw);
>> +	FWTS_UNUSED(fdt);
>> +	FWTS_UNUSED(nodeoffset);
>> +	FWTS_UNUSED(property);
>> +
>> +	return FWTS_OK;
>> +}
>> +
>>  #endif
>> 
>>  bool check_status_property_okay(fwts_framework *fw,
>> @@ -60,4 +143,6 @@ int check_property_printable(fwts_framework *fw,
>> 
>>  char *hidewhitespace(char *name);
>> 
>> +int get_proc_gen(fwts_framework *fw);
>> +
>>  #endif
>> diff --git a/src/lib/src/fwts_devicetree.c 
>> b/src/lib/src/fwts_devicetree.c
>> index bf5686a..2fea3c8 100644
>> --- a/src/lib/src/fwts_devicetree.c
>> +++ b/src/lib/src/fwts_devicetree.c
>> @@ -26,6 +26,8 @@
>> 
>>  #include <libfdt.h>
>> 
>> +proc_gen_t proc_gen;
>> +
>>  int fwts_devicetree_read(fwts_framework *fwts)
>>  {
>>  	char *command, *data = NULL;
>> @@ -171,3 +173,197 @@ char *hidewhitespace(char *name)
>>  	return name;
>> 
>>  }
>> +
>> +/*
>> + * fwts_dt_property_read_u32 This function reads one u32 DT property
>> + * 	Returns FWTS_OK on success:	*value will contain one int
>> + * 	FWTS_ERROR on error:		*value will contain error code
>> + */
>> +
>> +int fwts_dt_property_read_u32(
>> +	void *fdt,
>> +	int offset,
>> +	const char *pname,
>> +	int *value)
>> +{
>> +	int len;
>> +	const int *buf;
>> +
>> +	buf = fdt_getprop(fdt, offset, pname, &len);
>> +	if (buf == NULL) {
>> +		*value = len;
>> +		return FWTS_ERROR;
>> +	}
>> +	*value = be32toh(*buf);
>> +	return FWTS_OK;
>> +}
>> +
>> +/*
>> + * This function reads DT property array of u32's
>> + * 	Return FWTS_OK on success:	*value contain full array of int's
>> + *  	FWTS_ERROR on error:		*value will contain error code which
>> + *  						comes from *len
>> + */
>> +
>> +int fwts_dt_property_read_u32_arr(
>> +	void *fdt,
>> +	int offset,
>> +	const char *pname,
>> +	int *value,
>> +	int *len)
>> +{
>> +	int i;
>> +	const int *buf;
>> +
>> +	buf = fdt_getprop(fdt, offset, pname, len);
>> +	if (buf == NULL) {
>> +		*value = *len;
>> +		return FWTS_ERROR;
>> +	}
>> +
>> +	*len = *len / sizeof(int);
>> +	for (i = 0; i < *len; i++)
>> +		value[i] = be32toh(buf[i]);
>> +	return FWTS_OK;
>> +}
>> +
>> +/*
>> + * This function reads DT property array of u64's
>> + * 	Return FWTS_OK on success:	*value contain full array of u64's
>> + * 	FWTS_ERROR on error:		*value will contain error code which
>> + * 						comes from *len
>> + */
>> +
>> +int fwts_dt_property_read_u64_arr(
>> +	void *fdt,
>> +	int offset,
>> +	const char *pname,
>> +	uint64_t *value,
>> +	int *len)
>> +{
>> +	int i;
>> +	const int *buf;
>> +
>> +	buf = fdt_getprop(fdt, offset, pname, len);
>> +	if (buf == NULL) {
>> +		*value = *len;
>> +		return FWTS_ERROR;
>> +	}
>> +
>> +	*len = *len / sizeof(uint64_t);
>> +	for (i = 0; i < *len; i++)
>> +		value[i] = be64toh(buf[i]);
>> +	return FWTS_OK;
>> +}
>> +
>> +/* Get's the length of DT property string list */
>> +
>> +int fwts_dt_stringlist_count(
>> +	fwts_framework *fw,
>> +	const void *fdt,
>> +	int nodeoffset,
>> +	const char *property)
>> +{
>> +	const char *list, *end;
>> +	int length, count = 0;
>> +
>> +	list = fdt_getprop(fdt, nodeoffset, property, &length);
>> +	if (!list) {
>> +		fwts_failed(fw, LOG_LEVEL_HIGH, "PropertyNotFound",
>> +			"Failed to get property %s rc %d", property, length);
>> +		return FWTS_ERROR;
>> +	}
>> +
>> +	end = list + length;
>> +
>> +	while (list < end) {
>> +		length = strnlen(list, end - list) + 1;
>> +
>> +		/* Check if the last string isn't properly NUL-terminated. */
>> +		if (list + length > end) {
>> +			fwts_failed(fw, LOG_LEVEL_HIGH, "NotNULLTerminated",
>> +				"Last string is not properly NULL terminated");
>> +			return FWTS_ERROR;
>> +		}
>> +
>> +		list += length;
>> +		count++;
>> +	}
>> +
>> +	return count;
>> +}
>> +
>> +static int get_cpu_version(fwts_framework *fw, int *value)
>> +{
>> +	const char *cpus_path = "/cpus/";
>> +	int offset;
>> +	int cpu_version; int ret;
>> +
>> +	if (!fw->fdt) {
>> +		fwts_skipped(fw, "Device tree not found");
>> +		return FWTS_SKIP;
>> +	}
>> +
>> +	offset = fdt_path_offset(fw->fdt, cpus_path);
>> +	if (offset < 0) {
>> +		fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTNodeMissing",
>> +			"/cpus node is missing");
>> +		return FWTS_ERROR;
>> +	}
>> +
>> +	offset = fdt_node_offset_by_prop_value(fw->fdt, -1, "device_type",
>> +				"cpu", sizeof("cpu"));
>> +	if (offset < 0) {
>> +		fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTNodeMissing",
>> +			"cpu node is missing");
>> +		return FWTS_ERROR;
>> +	}
>> +
>> +	ret = fwts_dt_property_read_u32(fw->fdt, offset,
>> +			"cpu-version", &cpu_version);
>> +	if (ret != FWTS_OK) {
>> +		fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTPropertyReadError",
>> +			"Failed to read property cpu-version %s",
>> +			fdt_strerror(cpu_version));
>> +		return FWTS_ERROR;
>> +	}
>> +	*value = cpu_version;
>> +	return FWTS_OK;
>> +}
>> +
>> +int get_proc_gen(fwts_framework *fw)
>> +{
>> +	int version; int ret;
>> +	const int mask = 0xFFFF0000;
>> +	int pvr;
>> +
>> +	ret = get_cpu_version(fw, &version);
>> +	if (ret != FWTS_OK) {
>> +		fwts_failed(fw, LOG_LEVEL_HIGH, "DTNoCPUVersion",
>> +			"Not able to get the CPU version");
>> +		return FWTS_ERROR;
>> +	}
>> +
>> +	pvr = (mask & version) >> 16;
>> +	switch (pvr) {
>> +	/* Get CPU family and other flags based on PVR */
>> +	case PVR_TYPE_P7:
>> +	case PVR_TYPE_P7P:
>> +		proc_gen = proc_gen_p7;
>> +		break;
>> +	case PVR_TYPE_P8E:
>> +	case PVR_TYPE_P8:
>> +		proc_gen = proc_gen_p8;
>> +		break;
>> +	case PVR_TYPE_P8NVL:
>> +		proc_gen = proc_gen_p8;
>> +		break;
>> +	case PVR_TYPE_P9:
>> +		proc_gen = proc_gen_p9;
>> +		break;
>> +	default:
>> +		proc_gen = proc_gen_unknown;
>> +	}
>> +
>> +	return FWTS_OK;
>> +}
>> diff --git a/src/opal/power_mgmt_info.c b/src/opal/power_mgmt_info.c
>> new file mode 100644
>> index 0000000..5456c43
>> --- /dev/null
>> +++ b/src/opal/power_mgmt_info.c
>> @@ -0,0 +1,409 @@
>> +/*
>> + * Copyright (C) 2010-2017 Canonical
>> + * Some of this work - Copyright (C) 2016-2017 IBM
>> + *
>> + * This program is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU General Public License
>> + * as published by the Free Software Foundation; either version 2
>> + * of the License, or (at your option) any later version.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU General Public License
>> + * along with this program; if not, write to the Free Software
>> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 
>> 02110-1301, USA.
>> + *
>> + */
>> +
>> +#include <fcntl.h>
>> +#include <stdlib.h>
>> +#include <sys/ioctl.h>
>> +
>> +#include "fwts.h"
>> +
>> +#ifdef HAVE_LIBFDT
>> +#include <libfdt.h>
>> +#endif
>> +
>> +#define MAX_PSTATES 256
>> +
>> +#define CPUIDLE_STATE_MAX   10
>> +
>> +proc_gen_t proc_gen;
>> +
>> +static const char *power_mgt_path = "/ibm,opal/power-mgt/";
>> +
>> +/**
>> + * cmp_pstates: Compares the given two pstates and determines which
>> + *              among them is associated with a higher pstate.
>> + *
>> + * @a,@b: The pstate ids of the pstates being compared.
>> + *
>> + * Returns: -1 : If pstate associated with @a is smaller than
>> + *               the pstate associated with @b.
>> + *      0 : If pstates associated with @a and @b are equal.
>> + *      1 : If pstate associated with @a is greater than
>> + *               the pstate associated with @b.
>> + */
>> +static int (*cmp_pstates)(int a, int b);
>> +
>> +static int cmp_positive_pstates(int a, int b)
>> +{
>> +	if (a > b)
>> +		return -1;
>> +	else if (a < b)
>> +		return 1;
>> +
>> +	return 0;
>> +}
>> +
>> +static int cmp_negative_pstates(int a, int b)
>> +{
>> +	return cmp_positive_pstates(b, a);
>> +}
>> +
>> +static int validate_dt_prop_sizes(
>> +	fwts_framework *fw,
>> +	const char *prop1,
>> +	int prop1_len,
>> +	const char *prop2,
>> +	int prop2_len)
>> +{
>> +	if (prop1_len == prop2_len)
>> +		return FWTS_OK;
>> +
>> +	fwts_failed(fw, LOG_LEVEL_HIGH, "SizeMismatch",
>> +		"array sizes don't match for %s len %d and %s len %d\n",
>> +		prop1, prop1_len, prop2, prop2_len);
>> +
>> +	return FWTS_ERROR;
>> +}
>> +
>> +static int power_mgmt_init(fwts_framework *fw)
>> +{
>> +	int ret;
>> +
>> +	if (fwts_firmware_detect() != FWTS_FIRMWARE_OPAL) {
>> +		fwts_skipped(fw,
>> +			"The firmware type detected was non OPAL "
>> +			"so skipping the OPAL Power Management DT checks.");
>> +		return FWTS_SKIP;
>> +	}
>> +
>> +	if (!fw->fdt) {
>> +		fwts_failed(fw, LOG_LEVEL_HIGH, "NoDeviceTree",
>> +			"Device tree not found");
>> +		return FWTS_ERROR;
>> +	}
>> +
>> +	ret = get_proc_gen(fw);
>> +	if (ret != FWTS_OK) {
>> +		fwts_failed(fw, LOG_LEVEL_HIGH, "ProcGenFail",
>> +			"Failed to get the Processor generation");
>> +		return FWTS_ERROR;
>> +	}
>> +
>> +	return FWTS_OK;
>> +}
>> +
>> +
>> +static int pstate_limits_test(fwts_framework *fw)
>> +{
>> +	int pstate_min, pstate_max, pstates[MAX_PSTATES];
>> +	bool ok = true;
>> +	int  nr_pstates, offset, len, ret, i;
>> +
>> +	switch (proc_gen) {
>> +	case proc_gen_p8:
>> +		cmp_pstates = cmp_negative_pstates;
>> +		break;
>> +	case proc_gen_p9:
>> +		cmp_pstates = cmp_positive_pstates;
>> +		break;
>> +	default:
>> +		fwts_failed(fw, LOG_LEVEL_HIGH, "UnknownProcessorChip",
>> +			"Unknown processor generation %d", proc_gen);
>> +		return FWTS_ERROR;
>> +	}
>> +
>> +	offset = fdt_path_offset(fw->fdt, power_mgt_path);
>> +	if (offset < 0) {
>> +		fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTNodeMissing",
>> +			"power management node %s is missing", power_mgt_path);
>> +		return FWTS_ERROR;
>> +	}
>> +
>> +	ret = fwts_dt_property_read_u32(fw->fdt, offset, "ibm,pstate-min",
>> +					&pstate_min);
>> +	if (ret != FWTS_OK) {
>> +		fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTPropertyReadError",
>> +			"Failed to read property ibm,pstate-min %s",
>> +			fdt_strerror(pstate_min));
>> +		return FWTS_ERROR;
>> +	}
>> +
>> +	ret = fwts_dt_property_read_u32(fw->fdt, offset, "ibm,pstate-max",
>> +					&pstate_max);
>> +	if (ret != FWTS_OK) {
>> +		fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTPropertyReadError",
>> +			"Failed to read property ibm,pstate-max %s",
>> +			fdt_strerror(pstate_max));
>> +		return FWTS_ERROR;
>> +	}
>> +
>> +	ret = fwts_dt_property_read_u32_arr(fw->fdt, offset, 
>> "ibm,pstate-ids",
>> +					pstates, &len);
>> +	if (ret != FWTS_OK) {
>> +		fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTPropertyReadError",
>> +			"Failed to read property ibm,pstate-ids %s",
>> +			fdt_strerror(len));
>> +		return FWTS_ERROR;
>> +	}
>> +
>> +	nr_pstates = abs(pstate_max - pstate_min) + 1;
>> +
>> +	fwts_log_info(fw, "Pstates info: "
>> +			"Pstate min: %d "
>> +			"Pstate max: %d "
>> +			"nr_pstates: %d "
>> +			"Pstate ID's:  ",
>> +			pstate_min, pstate_max, nr_pstates);
>> +
>> +	for (i = 0; i < nr_pstates; i++)
>> +		fwts_log_info(fw, " %d ", pstates[i]);
>> +
>> +	if (nr_pstates <= 1 || nr_pstates > 128) {
>> +		if (proc_gen == proc_gen_p8)
>> +			fwts_log_warning(fw, "Pstates range %d is not valid",
>> +					nr_pstates);
>> +		else if (proc_gen == proc_gen_p9)
>> +			fwts_log_warning(fw,
>> +				"More than 128 pstates in pstate table %d",
>> +				nr_pstates);
>> +	}
>> +
>> +	if (len != nr_pstates)
>> +		fwts_log_warning(fw, "Wrong number of pstates."
>> +				"Expected %d pstates, found %d pstates",
>> +				nr_pstates, len);
>> +
>> +	for (i = 0; i < nr_pstates; i++) {
>> +		if (cmp_pstates(pstate_max, pstates[i]) < 0) {
>> +			fwts_log_warning(fw, "Invalid Pstate id %d "
>> +					"greater than max pstate %d",
>> +					pstates[i], pstate_max);
>> +			ok = false;
>> +		}
>> +		if (cmp_pstates(pstates[i], pstate_min) < 0) {
>> +			fwts_log_warning(fw, "Invalid Pstate id %d "
>> +					"lesser than min pstate %d",
>> +					pstates[i], pstate_min);
>> +			ok = false;
>> +		}
>> +	}
>> +
>> +	/* Pstates should be in monotonic descending order */
>> +	for (i = 0; i < nr_pstates; i++) {
>> +		if ((i == 0) && (cmp_pstates(pstates[i], pstate_max) != 0)) {
>> +			fwts_log_warning(fw, "Pstates mismatch: "
>> +					"Expected Pmax %d,"
>> +					"Actual Pmax %d",
>> +					pstate_max, pstates[i]);
>> +			ok = false;
>> +		} else if ((i == nr_pstates - 1) &&
>> +			(cmp_pstates(pstates[i], pstate_min) != 0)) {
>> +			fwts_log_warning(fw, "Pstates mismatch: "
>> +					"Expected Pmin %d,"
>> +					"Actual Pmin %d",
>> +					pstate_min, pstates[i]);
>> +			ok = false;
>> +		} else {
>> +			int previous_pstate;
>> +			previous_pstate = pstates[i-1];
>> +			if (cmp_pstates(pstates[i], previous_pstate) > 0) {
>> +				fwts_log_warning(fw, "Non monotonicity ...,"
>> +						"Pstate %d greater then"
>> +						" previous Pstate %d",
>> +						pstates[i], previous_pstate);
>> +				ok = false;
>> +			}
>> +		}
>> +	}
>> +
>> +	if (!ok) {
>> +		fwts_failed(fw, LOG_LEVEL_MEDIUM, "CPUPstateLimitsTestFail",
>> +		"One or few CPU Pstates DT validation tests failed");
>> +		return FWTS_ERROR;
>> +	}
>> +	fwts_passed(fw, "CPU Frequency pstates are validated");
>> +	return FWTS_OK;
>> +
>> +}
>> +
>> +static int cpuidle_states_test(fwts_framework *fw)
>> +{
>> +	int offset, len, test_len, ret;
>> +	int latency_ns[CPUIDLE_STATE_MAX];
>> +	int residency_ns[CPUIDLE_STATE_MAX];
>> +	int flags[CPUIDLE_STATE_MAX];
>> +	uint64_t pm_cr[CPUIDLE_STATE_MAX];
>> +	uint64_t pm_cr_mask[CPUIDLE_STATE_MAX];
>> +	bool has_stop_inst = false;
>> +	bool ok = true;
>> +	char *control_prop, *mask_prop;
>> +
>> +	switch (proc_gen) {
>> +	case proc_gen_p8:
>> +		has_stop_inst = false;
>> +		control_prop = "ibm,cpu-idle-state-pmicr";
>> +		mask_prop = "ibm,cpu-idle-state-pmicr-mask";
>> +		break;
>> +	case proc_gen_p9:
>> +		has_stop_inst = true;
>> +		control_prop = "ibm,cpu-idle-state-psscr";
>> +		mask_prop = "ibm,cpu-idle-state-psscr-mask";
>> +		break;
>> +	default:
>> +		fwts_failed(fw, LOG_LEVEL_HIGH, "UnknownProcessorChip",
>> +			"Unknown processor generation %d", proc_gen);
>> +		return FWTS_ERROR;
>> +	}
>> +
>> +	offset = fdt_path_offset(fw->fdt, power_mgt_path);
>> +	if (offset < 0) {
>> +		fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTNodeMissing",
>> +			"power management node %s is missing", power_mgt_path);
>> +		return FWTS_ERROR;
>> +	}
>> +
>> +	/* In P9 ibm,enabled-stop-levels present in /ibm,opal/power-mgt/ */
>> +	if (has_stop_inst) {
>> +		const int *buf;
>> +
>> +		buf = fdt_getprop(fw->fdt, offset, "ibm,enabled-stop-levels",
>> +					&len);
>> +		if (!buf) {
>> +			fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTPropertyMissing",
>> +				"ibm,enabled-stop-levels missing under %s",
>> +				power_mgt_path);
>> +			return FWTS_ERROR;
>> +		}
>> +	}
>> +
>> +	/* Validate ibm,cpu-idle-state-flags property */
>> +	ret = fwts_dt_property_read_u32_arr(fw->fdt, offset,
>> +			"ibm,cpu-idle-state-flags", flags, &len);
>> +	if (ret != FWTS_OK) {
>> +		fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTPropertyReadError",
>> +			"Failed to read property ibm,cpu-idle-state-flags %s",
>> +			fdt_strerror(len));
>> +		return FWTS_ERROR;
>> +	}
>> +
>> +	if (len < 0) {
>> +		fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTNoIdleStates",
>> +			"No idle states found in DT");
>> +		return FWTS_ERROR;
>> +	}
>> +
>> +	if (len > CPUIDLE_STATE_MAX-1) {
>> +		fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTMoreIdleStates",
>> +			"More idle states found in DT than the expected");
>> +		return FWTS_ERROR;
>> +	}
>> +
>> +	/* Validate ibm,cpu-idle-state-latencies-ns property */
>> +	ret = fwts_dt_property_read_u32_arr(fw->fdt, offset,
>> +			"ibm,cpu-idle-state-latencies-ns",
>> +			latency_ns, &test_len);
>> +	if (ret != FWTS_OK) {
>> +		fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTPropertyReadError",
>> +			"Failed to read property"
>> +			" ibm,cpu-idle-state-latencies-ns %s",
>> +			fdt_strerror(test_len));
>> +		return FWTS_ERROR;
>> +	}
>> +
>> +	if (validate_dt_prop_sizes(fw, "ibm,cpu-idle-state-flags", len,
>> +		"ibm,cpu-idle-state-latencies-ns", test_len) != FWTS_OK)
>> +		ok = false;
>> +
>> +	/* Validate ibm,cpu-idle-state-names property */
>> +	test_len = fwts_dt_stringlist_count(fw, fw->fdt, offset,
>> +				"ibm,cpu-idle-state-names");
>> +	if (test_len > 0) {
>> +		if (validate_dt_prop_sizes(fw, "ibm,cpu-idle-state-flags", len,
>> +			"ibm,cpu-idle-state-names", test_len) != FWTS_OK)
>> +			ok = false;
>> +	}
>> +
>> +	/* Validate ibm,cpu-idle-state-residency-ns property */
>> +	ret = fwts_dt_property_read_u32_arr(fw->fdt, offset,
>> +			"ibm,cpu-idle-state-residency-ns",
>> +			residency_ns, &test_len);
>> +	if (ret != FWTS_OK) {
>> +		fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTPropertyReadError",
>> +			"Failed to read property "
>> +			"ibm,cpu-idle-state-residency-ns %s",
>> +			fdt_strerror(test_len));
>> +		return FWTS_ERROR;
>> +	}
>> +
>> +	if (validate_dt_prop_sizes(fw, "ibm,cpu-idle-state-flags", len,
>> +		"ibm,cpu-idle-state-residency-ns", test_len) != FWTS_OK)
>> +		ok = false;
>> +
>> +	/* Validate pmicr and psscr value and mask bits */
>> +	ret = fwts_dt_property_read_u64_arr(fw->fdt, offset,
>> +			control_prop, pm_cr, &test_len);
>> +	if (ret != FWTS_OK) {
>> +		fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTPropertyReadError",
>> +			"Failed to read property %s rc: %s", control_prop,
>> +			fdt_strerror(test_len));
>> +		return FWTS_ERROR;
>> +	}
>> +
>> +	if (validate_dt_prop_sizes(fw, "ibm,cpu-idle-state-flags", len,
>> +			control_prop, test_len) != FWTS_OK)
>> +		ok = false;
>> +
>> +	ret = fwts_dt_property_read_u64_arr(fw->fdt, offset,
>> +		mask_prop, pm_cr_mask, &test_len);
>> +	if (ret != FWTS_OK) {
>> +		fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTPropertyReadError",
>> +			"Failed to read property %s rc: %s", mask_prop,
>> +			fdt_strerror(test_len));
>> +		return FWTS_ERROR;
>> +	}
>> +
>> +	if (validate_dt_prop_sizes(fw, "ibm,cpu-idle-state-flags", len,
>> +			mask_prop, test_len) != FWTS_OK)
>> +		ok = false;
>> +
>> +	if (!ok) {
>> +		fwts_failed(fw, LOG_LEVEL_HIGH, "CPUIDLEStatesFail",
>> +			"One or few CPU IDLE DT Validation tests failed");
>> +		return FWTS_ERROR;
>> +	}
>> +	fwts_passed(fw, "CPU IDLE States are validated");
>> +	return FWTS_OK;
>> +}
>> +
>> +static fwts_framework_minor_test power_mgmt_tests[] = {
>> +	{ pstate_limits_test, "OPAL Processor Frequency States Info" },
>> +	{ cpuidle_states_test, "OPAL Processor Idle States Info" },
>> +	{ NULL, NULL }
>> +};
>> +
>> +static fwts_framework_ops power_mgmt_tests_ops = {
>> +	.description = "OPAL Processor Power Management DT Validation 
>> Tests",
>> +	.init        = power_mgmt_init,
>> +	.minor_tests = power_mgmt_tests
>> +};
>> +
>> +FWTS_REGISTER_FEATURES("power_mgmt", &power_mgmt_tests_ops, 
>> FWTS_TEST_EARLY,
>> +		FWTS_FLAG_BATCH | FWTS_FLAG_ROOT_PRIV,
>> +		FWTS_FW_FEATURE_DEVICETREE)
>> 
> 
> That looks great. Thanks for the re-working. Much appreciated.
> 
> Acked-by: Colin Ian King <colin.king@canonical.com>


Thanks Colin\Alex.
Alex reported some failure when he try to merge and compile the patch.
https://lists.ubuntu.com/archives/fwts-devel/2017-April/009237.html
Some how this reply mail from Alex didn't came to my mail box.
That's why i am replying here.

Sent a V3, possibly it fixes the new failures. I tried applying patch
locally on both x86 and PowerPc boxes. It applied smoothly and compiled
fine with no issues. Let me know about this V3 patchset.

Whatever the failure Alex reported on V2 i am not able to re-create 
locally
but based on the errors, i gave the fix in V3. So please pick up V3
and let me know the status.

Thanks
diff mbox

Patch

diff --git a/src/Makefile.am b/src/Makefile.am
index c1eb285..e833554 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -147,6 +147,7 @@  fwts_SOURCES = main.c 				\
 	kernel/version/version.c 		\
 	opal/mtd_info.c				\
 	opal/prd_info.c				\
+	opal/power_mgmt_info.c			\
 	pci/aspm/aspm.c 			\
 	pci/crs/crs.c 				\
 	pci/maxreadreq/maxreadreq.c 		\
diff --git a/src/lib/include/fwts_cpu.h b/src/lib/include/fwts_cpu.h
index 9ab2ccf..be1c6cb 100644
--- a/src/lib/include/fwts_cpu.h
+++ b/src/lib/include/fwts_cpu.h
@@ -33,6 +33,24 @@  typedef struct cpuinfo_x86 {
 	char *flags;		/* String containing flags */
 } fwts_cpuinfo_x86;
 
+/* PowerPC Processor specific bits */
+/* PVR definitions */
+#define PVR_TYPE_P7     0x003f
+#define PVR_TYPE_P7P    0x004a
+#define PVR_TYPE_P8E    0x004b /* Murano */
+#define PVR_TYPE_P8     0x004d /* Venice */
+#define PVR_TYPE_P8NVL  0x004c /* Naples */
+#define PVR_TYPE_P9     0x004e
+
+/* Processor generation */
+typedef enum proc_gen {
+	proc_gen_unknown,
+	proc_gen_p7,            /* P7 and P7+ */
+	proc_gen_p8,
+	proc_gen_p9,
+} proc_gen_t;
+extern proc_gen_t proc_gen;
+
 typedef struct cpu_benchmark_result {
 	bool		cycles_valid;
 	uint64_t	loops;
diff --git a/src/lib/include/fwts_devicetree.h b/src/lib/include/fwts_devicetree.h
index 662f6ec..21c7807 100644
--- a/src/lib/include/fwts_devicetree.h
+++ b/src/lib/include/fwts_devicetree.h
@@ -42,6 +42,28 @@ 
 #if FWTS_HAS_DEVICETREE
 
 int fwts_devicetree_read(fwts_framework *fwts);
+int fwts_dt_property_read_u32(
+	void *fdt,
+	int offset,
+	const char *pname,
+	int *value);
+int fwts_dt_property_read_u32_arr(
+	void *fdt,
+	int offset,
+	const char *pname,
+	int *value,
+	int *len);
+int fwts_dt_property_read_u64_arr(
+	void *fdt,
+	int offset,
+	const char *pname,
+	uint64_t *value,
+	int *len);
+int fwts_dt_stringlist_count(
+	fwts_framework *fw,
+	const void *fdt,
+	int nodeoffset,
+	const char *property);
 
 #else /* !FWTS_HAS_DEVICETREE */
 static inline int fwts_devicetree_read(fwts_framework *fwts)
@@ -50,6 +72,67 @@  static inline int fwts_devicetree_read(fwts_framework *fwts)
 
 	return FWTS_OK;
 }
+
+int fwts_dt_property_read_u32(
+	void *fdt,
+	int offset,
+	const char *pname,
+	int *value)
+{
+	FWTS_UNUSED(fdt);
+	FWTS_UNUSED(offset);
+	FWTS_UNUSED(pname);
+	FWTS_UNUSED(value);
+
+	return FWTS_OK;
+}
+
+int fwts_dt_property_read_u32_arr(
+	void *fdt,
+	int offset,
+	const char *pname,
+	int *value,
+	int *len)
+{
+	FWTS_UNUSED(fdt);
+	FWTS_UNUSED(offset);
+	FWTS_UNUSED(pname);
+	FWTS_UNUSED(value);
+	FWTS_UNUSED(len);
+
+	return FWTS_OK;
+}
+
+int fwts_dt_property_read_u64_arr(
+	void *fdt,
+	int offset,
+	const char *pname,
+	uint64_t *value,
+	int *len)
+{
+	FWTS_UNUSED(fdt);
+	FWTS_UNUSED(offset);
+	FWTS_UNUSED(pname);
+	FWTS_UNUSED(value);
+	FWTS_UNUSED(len);
+
+	return FWTS_OK;
+}
+
+int fwts_dt_stringlist_count(
+	fwts_framework *fw,
+	const void *fdt,
+	int nodeoffset,
+	const char *property)
+{
+	FWTS_UNUSED(fw);
+	FWTS_UNUSED(fdt);
+	FWTS_UNUSED(nodeoffset);
+	FWTS_UNUSED(property);
+
+	return FWTS_OK;
+}
+
 #endif
 
 bool check_status_property_okay(fwts_framework *fw,
@@ -60,4 +143,6 @@  int check_property_printable(fwts_framework *fw,
 
 char *hidewhitespace(char *name);
 
+int get_proc_gen(fwts_framework *fw);
+
 #endif
diff --git a/src/lib/src/fwts_devicetree.c b/src/lib/src/fwts_devicetree.c
index bf5686a..2fea3c8 100644
--- a/src/lib/src/fwts_devicetree.c
+++ b/src/lib/src/fwts_devicetree.c
@@ -26,6 +26,8 @@ 
 
 #include <libfdt.h>
 
+proc_gen_t proc_gen;
+
 int fwts_devicetree_read(fwts_framework *fwts)
 {
 	char *command, *data = NULL;
@@ -171,3 +173,197 @@  char *hidewhitespace(char *name)
 	return name;
 
 }
+
+/*
+ * fwts_dt_property_read_u32 This function reads one u32 DT property
+ * 	Returns FWTS_OK on success:	*value will contain one int
+ * 	FWTS_ERROR on error:		*value will contain error code
+ */
+
+int fwts_dt_property_read_u32(
+	void *fdt,
+	int offset,
+	const char *pname,
+	int *value)
+{
+	int len;
+	const int *buf;
+
+	buf = fdt_getprop(fdt, offset, pname, &len);
+	if (buf == NULL) {
+		*value = len;
+		return FWTS_ERROR;
+	}
+	*value = be32toh(*buf);
+	return FWTS_OK;
+}
+
+/*
+ * This function reads DT property array of u32's
+ * 	Return FWTS_OK on success:	*value contain full array of int's
+ *  	FWTS_ERROR on error:		*value will contain error code which
+ *  						comes from *len
+ */
+
+int fwts_dt_property_read_u32_arr(
+	void *fdt,
+	int offset,
+	const char *pname,
+	int *value,
+	int *len)
+{
+	int i;
+	const int *buf;
+
+	buf = fdt_getprop(fdt, offset, pname, len);
+	if (buf == NULL) {
+		*value = *len;
+		return FWTS_ERROR;
+	}
+
+	*len = *len / sizeof(int);
+	for (i = 0; i < *len; i++)
+		value[i] = be32toh(buf[i]);
+	return FWTS_OK;
+}
+
+/*
+ * This function reads DT property array of u64's
+ * 	Return FWTS_OK on success:	*value contain full array of u64's
+ * 	FWTS_ERROR on error:		*value will contain error code which
+ * 						comes from *len
+ */
+
+int fwts_dt_property_read_u64_arr(
+	void *fdt,
+	int offset,
+	const char *pname,
+	uint64_t *value,
+	int *len)
+{
+	int i;
+	const int *buf;
+
+	buf = fdt_getprop(fdt, offset, pname, len);
+	if (buf == NULL) {
+		*value = *len;
+		return FWTS_ERROR;
+	}
+
+	*len = *len / sizeof(uint64_t);
+	for (i = 0; i < *len; i++)
+		value[i] = be64toh(buf[i]);
+	return FWTS_OK;
+}
+
+/* Get's the length of DT property string list */
+
+int fwts_dt_stringlist_count(
+	fwts_framework *fw,
+	const void *fdt,
+	int nodeoffset,
+	const char *property)
+{
+	const char *list, *end;
+	int length, count = 0;
+
+	list = fdt_getprop(fdt, nodeoffset, property, &length);
+	if (!list) {
+		fwts_failed(fw, LOG_LEVEL_HIGH, "PropertyNotFound",
+			"Failed to get property %s rc %d", property, length);
+		return FWTS_ERROR;
+	}
+
+	end = list + length;
+
+	while (list < end) {
+		length = strnlen(list, end - list) + 1;
+
+		/* Check if the last string isn't properly NUL-terminated. */
+		if (list + length > end) {
+			fwts_failed(fw, LOG_LEVEL_HIGH, "NotNULLTerminated",
+				"Last string is not properly NULL terminated");
+			return FWTS_ERROR;
+		}
+
+		list += length;
+		count++;
+	}
+
+	return count;
+}
+
+static int get_cpu_version(fwts_framework *fw, int *value)
+{
+	const char *cpus_path = "/cpus/";
+	int offset;
+	int cpu_version; int ret;
+
+	if (!fw->fdt) {
+		fwts_skipped(fw, "Device tree not found");
+		return FWTS_SKIP;
+	}
+
+	offset = fdt_path_offset(fw->fdt, cpus_path);
+	if (offset < 0) {
+		fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTNodeMissing",
+			"/cpus node is missing");
+		return FWTS_ERROR;
+	}
+
+	offset = fdt_node_offset_by_prop_value(fw->fdt, -1, "device_type",
+				"cpu", sizeof("cpu"));
+	if (offset < 0) {
+		fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTNodeMissing",
+			"cpu node is missing");
+		return FWTS_ERROR;
+	}
+
+	ret = fwts_dt_property_read_u32(fw->fdt, offset,
+			"cpu-version", &cpu_version);
+	if (ret != FWTS_OK) {
+		fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTPropertyReadError",
+			"Failed to read property cpu-version %s",
+			fdt_strerror(cpu_version));
+		return FWTS_ERROR;
+	}
+	*value = cpu_version;
+	return FWTS_OK;
+}
+
+int get_proc_gen(fwts_framework *fw)
+{
+	int version; int ret;
+	const int mask = 0xFFFF0000;
+	int pvr;
+
+	ret = get_cpu_version(fw, &version);
+	if (ret != FWTS_OK) {
+		fwts_failed(fw, LOG_LEVEL_HIGH, "DTNoCPUVersion",
+			"Not able to get the CPU version");
+		return FWTS_ERROR;
+	}
+
+	pvr = (mask & version) >> 16;
+	switch (pvr) {
+	/* Get CPU family and other flags based on PVR */
+	case PVR_TYPE_P7:
+	case PVR_TYPE_P7P:
+		proc_gen = proc_gen_p7;
+		break;
+	case PVR_TYPE_P8E:
+	case PVR_TYPE_P8:
+		proc_gen = proc_gen_p8;
+		break;
+	case PVR_TYPE_P8NVL:
+		proc_gen = proc_gen_p8;
+		break;
+	case PVR_TYPE_P9:
+		proc_gen = proc_gen_p9;
+		break;
+	default:
+		proc_gen = proc_gen_unknown;
+	}
+
+	return FWTS_OK;
+}
diff --git a/src/opal/power_mgmt_info.c b/src/opal/power_mgmt_info.c
new file mode 100644
index 0000000..5456c43
--- /dev/null
+++ b/src/opal/power_mgmt_info.c
@@ -0,0 +1,409 @@ 
+/*
+ * Copyright (C) 2010-2017 Canonical
+ * Some of this work - Copyright (C) 2016-2017 IBM
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+
+#include "fwts.h"
+
+#ifdef HAVE_LIBFDT
+#include <libfdt.h>
+#endif
+
+#define MAX_PSTATES 256
+
+#define CPUIDLE_STATE_MAX   10
+
+proc_gen_t proc_gen;
+
+static const char *power_mgt_path = "/ibm,opal/power-mgt/";
+
+/**
+ * cmp_pstates: Compares the given two pstates and determines which
+ *              among them is associated with a higher pstate.
+ *
+ * @a,@b: The pstate ids of the pstates being compared.
+ *
+ * Returns: -1 : If pstate associated with @a is smaller than
+ *               the pstate associated with @b.
+ *      0 : If pstates associated with @a and @b are equal.
+ *      1 : If pstate associated with @a is greater than
+ *               the pstate associated with @b.
+ */
+static int (*cmp_pstates)(int a, int b);
+
+static int cmp_positive_pstates(int a, int b)
+{
+	if (a > b)
+		return -1;
+	else if (a < b)
+		return 1;
+
+	return 0;
+}
+
+static int cmp_negative_pstates(int a, int b)
+{
+	return cmp_positive_pstates(b, a);
+}
+
+static int validate_dt_prop_sizes(
+	fwts_framework *fw,
+	const char *prop1,
+	int prop1_len,
+	const char *prop2,
+	int prop2_len)
+{
+	if (prop1_len == prop2_len)
+		return FWTS_OK;
+
+	fwts_failed(fw, LOG_LEVEL_HIGH, "SizeMismatch",
+		"array sizes don't match for %s len %d and %s len %d\n",
+		prop1, prop1_len, prop2, prop2_len);
+
+	return FWTS_ERROR;
+}
+
+static int power_mgmt_init(fwts_framework *fw)
+{
+	int ret;
+
+	if (fwts_firmware_detect() != FWTS_FIRMWARE_OPAL) {
+		fwts_skipped(fw,
+			"The firmware type detected was non OPAL "
+			"so skipping the OPAL Power Management DT checks.");
+		return FWTS_SKIP;
+	}
+
+	if (!fw->fdt) {
+		fwts_failed(fw, LOG_LEVEL_HIGH, "NoDeviceTree",
+			"Device tree not found");
+		return FWTS_ERROR;
+	}
+
+	ret = get_proc_gen(fw);
+	if (ret != FWTS_OK) {
+		fwts_failed(fw, LOG_LEVEL_HIGH, "ProcGenFail",
+			"Failed to get the Processor generation");
+		return FWTS_ERROR;
+	}
+
+	return FWTS_OK;
+}
+
+
+static int pstate_limits_test(fwts_framework *fw)
+{
+	int pstate_min, pstate_max, pstates[MAX_PSTATES];
+	bool ok = true;
+	int  nr_pstates, offset, len, ret, i;
+
+	switch (proc_gen) {
+	case proc_gen_p8:
+		cmp_pstates = cmp_negative_pstates;
+		break;
+	case proc_gen_p9:
+		cmp_pstates = cmp_positive_pstates;
+		break;
+	default:
+		fwts_failed(fw, LOG_LEVEL_HIGH, "UnknownProcessorChip",
+			"Unknown processor generation %d", proc_gen);
+		return FWTS_ERROR;
+	}
+
+	offset = fdt_path_offset(fw->fdt, power_mgt_path);
+	if (offset < 0) {
+		fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTNodeMissing",
+			"power management node %s is missing", power_mgt_path);
+		return FWTS_ERROR;
+	}
+
+	ret = fwts_dt_property_read_u32(fw->fdt, offset, "ibm,pstate-min",
+					&pstate_min);
+	if (ret != FWTS_OK) {
+		fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTPropertyReadError",
+			"Failed to read property ibm,pstate-min %s",
+			fdt_strerror(pstate_min));
+		return FWTS_ERROR;
+	}
+
+	ret = fwts_dt_property_read_u32(fw->fdt, offset, "ibm,pstate-max",
+					&pstate_max);
+	if (ret != FWTS_OK) {
+		fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTPropertyReadError",
+			"Failed to read property ibm,pstate-max %s",
+			fdt_strerror(pstate_max));
+		return FWTS_ERROR;
+	}
+
+	ret = fwts_dt_property_read_u32_arr(fw->fdt, offset, "ibm,pstate-ids",
+					pstates, &len);
+	if (ret != FWTS_OK) {
+		fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTPropertyReadError",
+			"Failed to read property ibm,pstate-ids %s",
+			fdt_strerror(len));
+		return FWTS_ERROR;
+	}
+
+	nr_pstates = abs(pstate_max - pstate_min) + 1;
+
+	fwts_log_info(fw, "Pstates info: "
+			"Pstate min: %d "
+			"Pstate max: %d "
+			"nr_pstates: %d "
+			"Pstate ID's:  ",
+			pstate_min, pstate_max, nr_pstates);
+
+	for (i = 0; i < nr_pstates; i++)
+		fwts_log_info(fw, " %d ", pstates[i]);
+
+	if (nr_pstates <= 1 || nr_pstates > 128) {
+		if (proc_gen == proc_gen_p8)
+			fwts_log_warning(fw, "Pstates range %d is not valid",
+					nr_pstates);
+		else if (proc_gen == proc_gen_p9)
+			fwts_log_warning(fw,
+				"More than 128 pstates in pstate table %d",
+				nr_pstates);
+	}
+
+	if (len != nr_pstates)
+		fwts_log_warning(fw, "Wrong number of pstates."
+				"Expected %d pstates, found %d pstates",
+				nr_pstates, len);
+
+	for (i = 0; i < nr_pstates; i++) {
+		if (cmp_pstates(pstate_max, pstates[i]) < 0) {
+			fwts_log_warning(fw, "Invalid Pstate id %d "
+					"greater than max pstate %d",
+					pstates[i], pstate_max);
+			ok = false;
+		}
+		if (cmp_pstates(pstates[i], pstate_min) < 0) {
+			fwts_log_warning(fw, "Invalid Pstate id %d "
+					"lesser than min pstate %d",
+					pstates[i], pstate_min);
+			ok = false;
+		}
+	}
+
+	/* Pstates should be in monotonic descending order */
+	for (i = 0; i < nr_pstates; i++) {
+		if ((i == 0) && (cmp_pstates(pstates[i], pstate_max) != 0)) {
+			fwts_log_warning(fw, "Pstates mismatch: "
+					"Expected Pmax %d,"
+					"Actual Pmax %d",
+					pstate_max, pstates[i]);
+			ok = false;
+		} else if ((i == nr_pstates - 1) &&
+			(cmp_pstates(pstates[i], pstate_min) != 0)) {
+			fwts_log_warning(fw, "Pstates mismatch: "
+					"Expected Pmin %d,"
+					"Actual Pmin %d",
+					pstate_min, pstates[i]);
+			ok = false;
+		} else {
+			int previous_pstate;
+			previous_pstate = pstates[i-1];
+			if (cmp_pstates(pstates[i], previous_pstate) > 0) {
+				fwts_log_warning(fw, "Non monotonicity ...,"
+						"Pstate %d greater then"
+						" previous Pstate %d",
+						pstates[i], previous_pstate);
+				ok = false;
+			}
+		}
+	}
+
+	if (!ok) {
+		fwts_failed(fw, LOG_LEVEL_MEDIUM, "CPUPstateLimitsTestFail",
+		"One or few CPU Pstates DT validation tests failed");
+		return FWTS_ERROR;
+	}
+	fwts_passed(fw, "CPU Frequency pstates are validated");
+	return FWTS_OK;
+
+}
+
+static int cpuidle_states_test(fwts_framework *fw)
+{
+	int offset, len, test_len, ret;
+	int latency_ns[CPUIDLE_STATE_MAX];
+	int residency_ns[CPUIDLE_STATE_MAX];
+	int flags[CPUIDLE_STATE_MAX];
+	uint64_t pm_cr[CPUIDLE_STATE_MAX];
+	uint64_t pm_cr_mask[CPUIDLE_STATE_MAX];
+	bool has_stop_inst = false;
+	bool ok = true;
+	char *control_prop, *mask_prop;
+
+	switch (proc_gen) {
+	case proc_gen_p8:
+		has_stop_inst = false;
+		control_prop = "ibm,cpu-idle-state-pmicr";
+		mask_prop = "ibm,cpu-idle-state-pmicr-mask";
+		break;
+	case proc_gen_p9:
+		has_stop_inst = true;
+		control_prop = "ibm,cpu-idle-state-psscr";
+		mask_prop = "ibm,cpu-idle-state-psscr-mask";
+		break;
+	default:
+		fwts_failed(fw, LOG_LEVEL_HIGH, "UnknownProcessorChip",
+			"Unknown processor generation %d", proc_gen);
+		return FWTS_ERROR;
+	}
+
+	offset = fdt_path_offset(fw->fdt, power_mgt_path);
+	if (offset < 0) {
+		fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTNodeMissing",
+			"power management node %s is missing", power_mgt_path);
+		return FWTS_ERROR;
+	}
+
+	/* In P9 ibm,enabled-stop-levels present in /ibm,opal/power-mgt/ */
+	if (has_stop_inst) {
+		const int *buf;
+
+		buf = fdt_getprop(fw->fdt, offset, "ibm,enabled-stop-levels",
+					&len);
+		if (!buf) {
+			fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTPropertyMissing",
+				"ibm,enabled-stop-levels missing under %s",
+				power_mgt_path);
+			return FWTS_ERROR;
+		}
+	}
+
+	/* Validate ibm,cpu-idle-state-flags property */
+	ret = fwts_dt_property_read_u32_arr(fw->fdt, offset,
+			"ibm,cpu-idle-state-flags", flags, &len);
+	if (ret != FWTS_OK) {
+		fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTPropertyReadError",
+			"Failed to read property ibm,cpu-idle-state-flags %s",
+			fdt_strerror(len));
+		return FWTS_ERROR;
+	}
+
+	if (len < 0) {
+		fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTNoIdleStates",
+			"No idle states found in DT");
+		return FWTS_ERROR;
+	}
+
+	if (len > CPUIDLE_STATE_MAX-1) {
+		fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTMoreIdleStates",
+			"More idle states found in DT than the expected");
+		return FWTS_ERROR;
+	}
+
+	/* Validate ibm,cpu-idle-state-latencies-ns property */
+	ret = fwts_dt_property_read_u32_arr(fw->fdt, offset,
+			"ibm,cpu-idle-state-latencies-ns",
+			latency_ns, &test_len);
+	if (ret != FWTS_OK) {
+		fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTPropertyReadError",
+			"Failed to read property"
+			" ibm,cpu-idle-state-latencies-ns %s",
+			fdt_strerror(test_len));
+		return FWTS_ERROR;
+	}
+
+	if (validate_dt_prop_sizes(fw, "ibm,cpu-idle-state-flags", len,
+		"ibm,cpu-idle-state-latencies-ns", test_len) != FWTS_OK)
+		ok = false;
+
+	/* Validate ibm,cpu-idle-state-names property */
+	test_len = fwts_dt_stringlist_count(fw, fw->fdt, offset,
+				"ibm,cpu-idle-state-names");
+	if (test_len > 0) {
+		if (validate_dt_prop_sizes(fw, "ibm,cpu-idle-state-flags", len,
+			"ibm,cpu-idle-state-names", test_len) != FWTS_OK)
+			ok = false;
+	}
+
+	/* Validate ibm,cpu-idle-state-residency-ns property */
+	ret = fwts_dt_property_read_u32_arr(fw->fdt, offset,
+			"ibm,cpu-idle-state-residency-ns",
+			residency_ns, &test_len);
+	if (ret != FWTS_OK) {
+		fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTPropertyReadError",
+			"Failed to read property "
+			"ibm,cpu-idle-state-residency-ns %s",
+			fdt_strerror(test_len));
+		return FWTS_ERROR;
+	}
+
+	if (validate_dt_prop_sizes(fw, "ibm,cpu-idle-state-flags", len,
+		"ibm,cpu-idle-state-residency-ns", test_len) != FWTS_OK)
+		ok = false;
+
+	/* Validate pmicr and psscr value and mask bits */
+	ret = fwts_dt_property_read_u64_arr(fw->fdt, offset,
+			control_prop, pm_cr, &test_len);
+	if (ret != FWTS_OK) {
+		fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTPropertyReadError",
+			"Failed to read property %s rc: %s", control_prop,
+			fdt_strerror(test_len));
+		return FWTS_ERROR;
+	}
+
+	if (validate_dt_prop_sizes(fw, "ibm,cpu-idle-state-flags", len,
+			control_prop, test_len) != FWTS_OK)
+		ok = false;
+
+	ret = fwts_dt_property_read_u64_arr(fw->fdt, offset,
+		mask_prop, pm_cr_mask, &test_len);
+	if (ret != FWTS_OK) {
+		fwts_failed(fw, LOG_LEVEL_MEDIUM, "DTPropertyReadError",
+			"Failed to read property %s rc: %s", mask_prop,
+			fdt_strerror(test_len));
+		return FWTS_ERROR;
+	}
+
+	if (validate_dt_prop_sizes(fw, "ibm,cpu-idle-state-flags", len,
+			mask_prop, test_len) != FWTS_OK)
+		ok = false;
+
+	if (!ok) {
+		fwts_failed(fw, LOG_LEVEL_HIGH, "CPUIDLEStatesFail",
+			"One or few CPU IDLE DT Validation tests failed");
+		return FWTS_ERROR;
+	}
+	fwts_passed(fw, "CPU IDLE States are validated");
+	return FWTS_OK;
+}
+
+static fwts_framework_minor_test power_mgmt_tests[] = {
+	{ pstate_limits_test, "OPAL Processor Frequency States Info" },
+	{ cpuidle_states_test, "OPAL Processor Idle States Info" },
+	{ NULL, NULL }
+};
+
+static fwts_framework_ops power_mgmt_tests_ops = {
+	.description = "OPAL Processor Power Management DT Validation Tests",
+	.init        = power_mgmt_init,
+	.minor_tests = power_mgmt_tests
+};
+
+FWTS_REGISTER_FEATURES("power_mgmt", &power_mgmt_tests_ops, FWTS_TEST_EARLY,
+		FWTS_FLAG_BATCH | FWTS_FLAG_ROOT_PRIV,
+		FWTS_FW_FEATURE_DEVICETREE)