[Skiboot] [PATCH V4 3/3] occ: Add support for Version 0x90 OCC_OPAL shared memory region
Shilpasri G Bhat
shilpa.bhat at linux.vnet.ibm.com
Wed Nov 9 19:14:13 AEDT 2016
Hi Pridhiviraj,
On 11/08/2016 02:37 PM, ppaidipe wrote:
> On 2016-11-02 14:08, Shilpasri G Bhat wrote:
>> This patch enables pstate table parsing support for P9. It
>> introduces below device tree changes.
>> - Add a new node per chip in /ibm,opal/power-mgt called occ.
>> occ at 3ffd9f8000 {
>> reg = <0x3f 0xfd9f8000>;
>> ibm,chip-id = <0x0>;
>> ibm,pstate-vdds = <0x4e4e4f4f 0x4f505152 0x53545556
>> 0x5758595a 0x5b5c5d5e 0x5f606162 0x63646565 0x66676868
>> 0x696a6a6b 0x6c6d6d6e 0x6f6f7071 0x72727374 0x74757677
>> 0x7778797a 0x7a7b7c7c>;
>> ibm,pstate-vcss = <0x4446484a 0x4c4e4f50 0x50515253
>> 0x54555556 0x5758595a 0x5a5b5c5d 0x5d5e5e5e 0x5e5f5f5f
>> 0x5f606060 0x61616161 0x62626263 0x63636364 0x64646465
>> 0x65656666 0x66666767>;
>> ibm,pstate-core-max = <0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0>;
>> phandle = <0x10000326>;
>> };
>> - Move VID (ibm,pstate-vdds, ibm,pstate-vcss) and max pstate for
>> #n active cores array (ibm,pstate-core-max) in to the new per-chip
>> /ibm,opal/power-mgt/occ node as these properties are unique to
>> chip.
>> - WOF is supported from version 0x02. Till now we have been adding
>> max ultra-turbo pstate(ibm,pstate-ultra-turbo), max turbo pstate
>> (ibm,pstate-turbo) and max pstate-per-n-core (ibm,pstate-core-max)
>> only when WOF is enabled. This patch will add these properties
>> even when WOF is disabled. When WOF is disabled max ultra turbo pstate
>> equals to max turbo pstate and max pstate-per-n-core array has all
>> entries equal to max turbo pstate. So the above three properties
>> are added whenever WOF is supported to ease the reporting of these
>> data in host.
>>
>> Signed-off-by: Shilpasri G Bhat <shilpa.bhat at linux.vnet.ibm.com>
>> ---
>> Changes from v3:
>> - Changed the refernece document from OCC source code to OCC
>> FW interface document. Vx90 is not yet published in the docs as it
>> is still under review.
>> - Minor modifications in comments
>>
>> Changes from v2:
>> - Moved 'struct occ_dynamic_data' outside the pstate table
>> - s/P8_HOMER_SAPPHIRE_DATA_OFFSET/P8_HOMER_OPAL_DATA_OFFSET
>> - s/chip_occ_data/get_occ_pstate_table
>> - Added c-style doc for occ related structures
>> - Removed command/response buffer definitions, will add them in the
>> subsequent patches
>>
>> hw/occ.c | 584 ++++++++++++++++++++++++++++++++++++++++++---------------------
>> 1 file changed, 391 insertions(+), 193 deletions(-)
>>
>> diff --git a/hw/occ.c b/hw/occ.c
>> index 3a23043..085c1b1 100644
>> --- a/hw/occ.c
>> +++ b/hw/occ.c
>> @@ -30,44 +30,137 @@
>>
>> /* OCC Communication Area for PStates */
>>
>> -#define P8_HOMER_SAPPHIRE_DATA_OFFSET 0x1F8000
>> +#define P8_HOMER_OPAL_DATA_OFFSET 0x1F8000
>> +#define P9_HOMER_OPAL_DATA_OFFSET 0x0E2000
>> +#define MAX_PSTATES 256
>> +#define MAX_P8_CORES 12
>> +#define MAX_P9_CORES 24
>>
>> -#define MAX_PSTATES 256
>> -
>> -#define chip_occ_data(chip) \
>> - ((struct occ_pstate_table *)(chip->homer_base + \
>> - P8_HOMER_SAPPHIRE_DATA_OFFSET))
>> -
>> -static bool occ_reset;
>> -static struct lock occ_lock = LOCK_UNLOCKED;
>> -
>> -struct occ_pstate_entry {
>> - s8 id;
>> - u8 flags;
>> - u8 vdd;
>> - u8 vcs;
>> - u32 freq_khz;
>> -} __packed;
>> -
>> -/*
>> - * OCC-OPAL Shared Memory Region Version 2
>> - * https://github.com/open-power/occ/blob/master/src/occ/proc/proc_pstate.h
>> - * Interface defined in 'sapphire_table_t'
>> +/**
>> + * OCC-OPAL Shared Memory Region
>> + *
>> + * Reference document :
>> + *
>> https://github.com/open-power/docs/blob/master/occ/OCC_OpenPwr_FW_Interfaces.pdf
>> + *
>> + * Supported layout versions:
>> + * - 0x01 : P8
>> + * - 0x02 : P8
>> + * - 0x90 : P9
>> + * In 0x90 the data is separated into :-
>> + * -- Static Data (struct occ_pstate_table): Data is written once by OCC
>> + * -- Dynamic Data (struct occ_dynamic_data): Data is updated at runtime
>> + *
>> + * struct occ_pstate_table - Pstate table layout
>> + * @valid: Indicates if data is valid
>> + * @version: Layout version
>> + * @v2.throttle: Reason for limiting the max pstate
>> + * @v90.occ_role: OCC role (Master/Slave)
>> + * @v#.pstate_min: Minimum pstate ever allowed
>> + * @v#.pstate_nom: Nominal pstate
>> + * @v#.pstate_turbo: Maximum turbo pstate
>> + * @v#.pstate_ultra_turbo: Maximum ultra turbo pstate and the maximum
>> + * pstate ever allowed
>> + * @v#.pstates: Pstate-id and frequency list from Pmax to Pmin
>> + * @v#.pstates.id: Pstate-id
>> + * @v#.pstates.flags: Pstate-flag(reserved)
>> + * @v2.pstates.vdd: Voltage Identifier
>> + * @v2.pstates.vcs: Voltage Identifier
>> + * @v#.pstates.freq_khz: Frequency in KHz
>> + * @v#.core_max[1..N]: Max pstate with N active cores
>> + * @spare/reserved/pad: Unused data
>> */
>> struct occ_pstate_table {
>> u8 valid;
>> u8 version;
>> - u8 throttle;
>> - s8 pstate_min;
>> - s8 pstate_nom;
>> - s8 pstate_turbo;
>> - s8 pstate_ultra_turbo;
>> - u8 spare;
>> + union __packed {
>> + struct __packed { /* Version 0x01 and 0x02 */
>> + u8 throttle;
>> + s8 pstate_min;
>> + s8 pstate_nom;
>> + s8 pstate_turbo;
>> + s8 pstate_ultra_turbo;
>> + u8 spare;
>> + u64 reserved;
>> + struct __packed {
>> + s8 id;
>> + u8 flags;
>> + u8 vdd;
>> + u8 vcs;
>> + u32 freq_khz;
>> + } pstates[MAX_PSTATES];
>> + s8 core_max[MAX_P8_CORES];
>> + u8 pad[100];
>> + } v2;
>> + struct __packed { /* Version 0x90 */
>> + u8 occ_role;
>> + u8 pstate_min;
>> + u8 pstate_nom;
>> + u8 pstate_turbo;
>> + u8 pstate_ultra_turbo;
>> + u8 spare;
>> + u64 reserved1;
>> + u64 reserved2;
>> + struct __packed {
>> + u8 id;
>> + u8 flags;
>> + u16 reserved;
>> + u32 freq_khz;
>> + } pstates[MAX_PSTATES];
>> + u8 core_max[MAX_P9_CORES];
>> + u8 pad[1024];
>> + } v90;
>> + };
>> +} __packed;
>> +
>> +/**
>> + * OCC-OPAL Shared Memory Interface Dynamic Data Vx90
>> + *
>> + * struct occ_dynamic_data - Contains runtime attributes
>> + * @occ_state: Current state of OCC
>> + * @cpu_throttle: Reason for limiting the max pstate
>> + * @mem_throttle: Reason for throttling memory
>> + * @quick_pwr_drop: Indicates if QPD is asserted
>> + * @pwr_shifting_ratio: Indicates the current percentage of power to
>> + * take away from the CPU vs GPU when shifting
>> + * power to maintain a power cap. Value of 100
>> + * means take all power from CPU.
>> + * @pwr_cap_type: Indicates type of power cap in effect
>> + * @min_pwr_cap: Minimum allowed system power cap in Watts
>> + * @max_pwr_cap: Maximum allowed system power cap in Watts
>> + * @cur_pwr_cap: Current system power cap
>> + * @spare/reserved: Unused data
>> + */
>> +struct occ_dynamic_data {
>> + u8 occ_state;
>> + u8 spare1;
>> + u8 spare2;
>> + u8 spare3;
>> + u8 spare4;
>> + u8 cpu_throttle;
>> + u8 mem_throttle;
>> + u8 quick_pwr_drop;
>> + u8 pwr_shifting_ratio;
>> + u8 pwr_cap_type;
>> + u16 min_pwr_cap;
>> + u16 max_pwr_cap;
>> + u16 cur_pwr_cap;
>> u64 reserved;
>> - struct occ_pstate_entry pstates[MAX_PSTATES];
>> - s8 core_max[16];
>> } __packed;
>>
>> +#define get_homer_opal_data(chip) \
>> + (chip->homer_base + homer_opal_data_offset)
>> +
>> +#define get_occ_pstate_table(chip) \
>> + ((struct occ_pstate_table *)(get_homer_opal_data(chip)))
>> +
>> +#define get_occ_dynamic_data(chip) \
>> + ((struct occ_dynamic_data *)(get_homer_opal_data(chip) + \
>> + sizeof(struct occ_pstate_table)))
>> +
>> +static bool occ_reset;
>> +static struct lock occ_lock = LOCK_UNLOCKED;
>> +static unsigned long homer_opal_data_offset;
>> +
>> DEFINE_LOG_ENTRY(OPAL_RC_OCC_LOAD, OPAL_PLATFORM_ERR_EVT, OPAL_OCC,
>> OPAL_CEC_HARDWARE, OPAL_PREDICTIVE_ERR_GENERAL,
>> OPAL_NA);
>> @@ -122,7 +215,6 @@ static int cmp_negative_pstates(int a, int b)
>> static bool wait_for_all_occ_init(void)
>> {
>> struct proc_chip *chip;
>> - uint64_t occ_data_area;
>> struct occ_pstate_table *occ_data;
>> int tries;
>> uint64_t start_time, end_time;
>> @@ -156,8 +248,7 @@ static bool wait_for_all_occ_init(void)
>> }
>>
>> /* Get PState table address */
>> - occ_data_area = chip->homer_base + P8_HOMER_SAPPHIRE_DATA_OFFSET;
>> - occ_data = (struct occ_pstate_table *)occ_data_area;
>> + occ_data = get_occ_pstate_table(chip);
>>
>> /*
>> * Checking for occ_data->valid == 1 is ok because we clear all
>> @@ -184,8 +275,7 @@ static bool wait_for_all_occ_init(void)
>> return false;
>> }
>> prlog(PR_DEBUG, "OCC: Chip %02x Data (%016llx) = %016llx\n",
>> - chip->id, occ_data_area,
>> - *(uint64_t *)occ_data_area);
>> + chip->id, (uint64_t)occ_data, *(uint64_t *)occ_data);
>> }
>> end_time = mftb();
>> prlog(PR_NOTICE, "OCC: All Chip Rdy after %lu ms\n",
>> @@ -193,39 +283,123 @@ static bool wait_for_all_occ_init(void)
>> return true;
>> }
>>
>> +#define parse_pstate_limits(pstate_data) \
>> +do { \
>> + pmin = pstate_data.pstate_min; \
>> + pnom = pstate_data.pstate_nom; \
>> + pmax = ultra_turbo_supported ? pstate_data.pstate_ultra_turbo : \
>> + pstate_data.pstate_turbo; \
>> +} while (0)
>> +
>> +/*
>> + * OCC provides pstate table entries in continuous descending order.
>> + * Parse the pstate table to skip pstate_ids that are greater
>> + * than Pmax. If a pstate_id is equal to Pmin then add it to
>> + * the list and break from the loop as this is the last valid
>> + * element in the pstate table.
>> + */
>> +#define parse_cpu_pstate_properties(pstate_data) \
>> +do { \
>> + for (i = 0, j = 0; i < MAX_PSTATES && j < nr_pstates; i++) { \
>> + if (cmp_pstates(pstate_data.pstates[i].id, pmax) > 0) \
>> + continue; \
>> + \
>> + dt_id[j] = pstate_data.pstates[i].id; \
>> + dt_freq[j] = pstate_data.pstates[i].freq_khz / 1000; \
>> + j++; \
>> + if (pstate_data.pstates[i].id == pmin) \
>> + break; \
>> + } \
>> + \
>> + if (j != nr_pstates) { \
>> + /** \
>> + * @fwts-label OCCNrPstatesMissmatch \
>> + * @fwts-advice The number of pstates parsed from the \
>> + * pstate table does not match the total no of pstates \
>> + * expected. No PStates in Device Tree: non-functional \
>> + * power/frequency management. \
>> + */ \
>> + prerror("OCC: Mismatch in nr_pstates. Expected " \
>> + "nr_pstates = %d, Actual nr_pstates parsed = %d\n", \
>> + nr_pstates, j);
>
> Hi
> Here all fwts annotations should use prlog(loglevel, msg) format. If we use
> prerror format
> olog.json file generated using script /external/fwts/generate-fwts-olog will not
> contain your
> fwts annotation. In order make use of these annotations use prlog(loglevel, msg)
> format.
>
Okay will do.
Is this a limitation in fwts? Can fwts be taught to pickup prerror annotations
as well?
Thanks and Regards,
Shilpa
>
> Thanks
> Pridhiviraj
>
>
>
>> + goto out_free_vcs; \
>> + } \
>> + \
>> + if (ultra_turbo_supported) { \
>> + dt_add_property_cells(power_mgt, "ibm,pstate-turbo", \
>> + pstate_data.pstate_turbo); \
>> + dt_add_property_cells(power_mgt, "ibm,pstate-ultra-turbo",\
>> + pstate_data.pstate_ultra_turbo); \
>> + } \
>> +} while (0)
>> +
>> +/* core_max[1..n] array provides the max sustainable pstate that can be
>> + * achieved with i active cores in the chip.
>> + */
>> +#define parse_core_max(pstate_data) \
>> +do { \
>> + for (i = 0; i < nr_cores; i++) \
>> + dt_core_max[i] = pstate_data.core_max[i]; \
>> +} while (0)
>> +
>> +#define parse_vid(pstate_data) \
>> +do { \
>> + for (i = 0, j = 0; i < MAX_PSTATES && j < nr_pstates; i++) { \
>> + if (cmp_pstates(pstate_data.pstates[i].id, pmax) > 0) \
>> + continue; \
>> + \
>> + dt_vdd[j] = pstate_data.pstates[i].vdd; \
>> + dt_vcs[j] = pstate_data.pstates[i].vcs; \
>> + j++; \
>> + \
>> + if (pstate_data.pstates[i].id == pmin) \
>> + break; \
>> + } \
>> +} while (0)
>> +
>> +#define allocate(ptr, size, fail_label) \
>> +do { \
>> + ptr = malloc((size)); \
>> + if (!ptr) { \
>> + /** \
>> + * @fwts-label OCCPstatesDTArrayENOMEM \
>> + * @fwts-advice Out of memory when allocating pstates \
>> + * array. No Pstates added to device tree, pstates not \
>> + * functional. \
>> + */ \
>> + prerror("OCC:" #ptr " array alloc failure\n"); \
>> + goto fail_label; \
>> + } \
>> +} while (0)
>> +
>> /* Add device tree properties to describe pstates states */
>> -/* Retrun nominal pstate to set in each core */
>> -static bool add_cpu_pstate_properties(s8 *pstate_nom)
>> +/* Return nominal pstate to set in each core */
>> +static bool add_cpu_pstate_properties(int *pstate_nom)
>> {
>> struct proc_chip *chip;
>> uint64_t occ_data_area;
>> struct occ_pstate_table *occ_data;
>> struct dt_node *power_mgt;
>> - u8 nr_pstates, nr_cores = 0;
>> - s8 pmax;
>> - /* Arrays for device tree */
>> - u32 *dt_id, *dt_freq;
>> - u8 *dt_vdd, *dt_vcs;
>> - s8 *dt_core_max = NULL;
>> - bool rc, ultra_turbo_en;
>> + u32 *dt_id, *dt_freq, *dt_core_max = NULL;
>> + int pmax, pmin, pnom;
>> + u8 *dt_vdd = NULL, *dt_vcs = NULL, nr_pstates, max_cores;
>> + bool ultra_turbo_supported, rc = false;
>> int i, j;
>>
>> prlog(PR_DEBUG, "OCC: CPU pstate state device tree init\n");
>>
>> - /* Find first chip and core */
>> + /* Find first chip */
>> chip = next_chip(NULL);
>>
>> /* Extract PState information from OCC */
>> + occ_data = get_occ_pstate_table(chip);
>>
>> - /* Dump state table */
>> - occ_data_area = chip->homer_base + P8_HOMER_SAPPHIRE_DATA_OFFSET;
>> -
>> + /* Dump first 16 bytes of PState table */
>> + occ_data_area = (uint64_t)occ_data;
>> prlog(PR_DEBUG, "OCC: Data (%16llx) = %16llx %16llx\n",
>> occ_data_area,
>> *(uint64_t *)occ_data_area,
>> *(uint64_t *)(occ_data_area+8));
>> -
>> - occ_data = (struct occ_pstate_table *)occ_data_area;
>>
>> if (!occ_data->valid) {
>> /**
>> @@ -243,21 +417,61 @@ static bool add_cpu_pstate_properties(s8 *pstate_nom)
>>
>> /*
>> * Workload-Optimized-Frequency(WOF) or Ultra-Turbo is supported
>> - * from version 2 onwards. If WOF is disabled then, the max
>> + * from version 0x02 onwards. If WOF is disabled then, the max
>> * ultra_turbo pstate will be equal to max turbo pstate.
>> */
>> - if (occ_data->version > 1 &&
>> - cmp_pstates(occ_data->pstate_turbo,
>> - occ_data->pstate_ultra_turbo) > 0)
>> - ultra_turbo_en = true;
>> - else
>> - ultra_turbo_en = false;
>> + ultra_turbo_supported = true;
>>
>> - pmax = ultra_turbo_en ? occ_data->pstate_ultra_turbo :
>> - occ_data->pstate_turbo;
>> + switch (occ_data->version) {
>> + case 0x01:
>> + ultra_turbo_supported = false;
>> + case 0x02:
>> + if (proc_gen == proc_gen_p9) {
>> + /**
>> + * @fwts-label OCCInvalidVersion
>> + * @fwts-advice The PState table layout version is not
>> + * supported in P9. So OPAL will not parse the PState
>> + * table. CPU frequency scaling will not be functional
>> + * as frequency and pstate-ids are not added to DT.
>> + */
>> + prerror("OCC: Version %x is not supported in P9\n",
>> + occ_data->version);
>> + return false;
>> + }
>> + parse_pstate_limits(occ_data->v2);
>> + max_cores = MAX_P8_CORES;
>> + break;
>> + case 0x90:
>> + if (proc_gen == proc_gen_p8) {
>> + /**
>> + * @fwts-label OCCInvalidVersion
>> + * @fwts-advice The PState table layout version is not
>> + * supported in P8. So OPAL will not parse the PState
>> + * table. CPU frequency scaling will not be functional
>> + * as frequency and pstate-ids are not added to DT.
>> + */
>> + prerror("OCC: Version %x is not supported in P8\n",
>> + occ_data->version);
>> + return false;
>> + }
>> + parse_pstate_limits(occ_data->v90);
>> + max_cores = MAX_P9_CORES;
>> + break;
>> + default:
>> + /**
>> + * @fwts-label OCCUnsupportedVersion
>> + * @fwts-advice The PState table layout version is not
>> + * supported. So OPAL will not parse the PState table.
>> + * CPU frequency scaling will not be functional as OPAL
>> + * doesn't populate the device tree with pstates.
>> + */
>> + prerror("OCC: Unsupported pstate table layout version %d\n",
>> + occ_data->version);
>> + return false;
>> + }
>>
>> /* Sanity check for pstate limits */
>> - if (cmp_pstates(occ_data->pstate_min, pmax) > 0) {
>> + if (cmp_pstates(pmin, pmax) > 0) {
>> /**
>> * @fwts-label OCCInvalidPStateLimits
>> * @fwts-advice The min pstate is greater than the
>> @@ -268,15 +482,13 @@ static bool add_cpu_pstate_properties(s8 *pstate_nom)
>> * the host.
>> */
>> prlog(PR_ERR, "OCC: Invalid Pstate Limits. Pmin(%d) > Pmax (%d)\n",
>> - occ_data->pstate_min, pmax);
>> + pmin, pmax);
>> return false;
>> }
>>
>> - nr_pstates = labs(pmax - occ_data->pstate_min) + 1;
>> - prlog(PR_DEBUG, "OCC: Min %d Nom %d Max %d Nr States %d\n",
>> - occ_data->pstate_min, occ_data->pstate_nom,
>> - pmax, nr_pstates);
>> -
>> + nr_pstates = labs(pmax - pmin) + 1;
>> + prlog(PR_DEBUG, "OCC: Version %x Min %d Nom %d Max %d Nr States %d\n",
>> + occ_data->version, pmin, pnom, pmax, nr_pstates);
>> if (nr_pstates <= 1 || nr_pstates > 128) {
>> /**
>> * @fwts-label OCCInvalidPStateRange
>> @@ -287,7 +499,8 @@ static bool add_cpu_pstate_properties(s8 *pstate_nom)
>> * that CPU idle states and CPU frequency scaling
>> * will not be functional.
>> */
>> - prlog(PR_ERR, "OCC: OCC range is not valid\n");
>> + prerror("OCC: OCC range is not valid; No of pstates = %d\n",
>> + nr_pstates);
>> return false;
>> }
>>
>> @@ -303,104 +516,26 @@ static bool add_cpu_pstate_properties(s8 *pstate_nom)
>> return false;
>> }
>>
>> - rc = false;
>> -
>> /* Setup arrays for device-tree */
>> /* Allocate memory */
>> - dt_id = malloc(nr_pstates * sizeof(u32));
>> - if (!dt_id) {
>> - /**
>> - * @fwts-label OCCdt_idENOMEM
>> - * @fwts-advice Out of memory when allocating pstates array.
>> - * No Pstates added to device tree, pstates not functional.
>> - */
>> - prlog(PR_ERR, "OCC: dt_id array alloc failure\n");
>> - goto out;
>> - }
>> -
>> - dt_freq = malloc(nr_pstates * sizeof(u32));
>> - if (!dt_freq) {
>> - /**
>> - * @fwts-label OCCdt_freqENOMEM
>> - * @fwts-advice Out of memory when allocating pstates array.
>> - * No Pstates added to device tree, pstates not functional.
>> - */
>> - prlog(PR_ERR, "OCC: dt_freq array alloc failure\n");
>> - goto out_free_id;
>> - }
>> -
>> - dt_vdd = malloc(nr_pstates * sizeof(u8));
>> - if (!dt_vdd) {
>> - /**
>> - * @fwts-label OCCdt_vddENOMEM
>> - * @fwts-advice Out of memory when allocating pstates array.
>> - * No Pstates added to device tree, pstates not functional.
>> - */
>> - prlog(PR_ERR, "OCC: dt_vdd array alloc failure\n");
>> - goto out_free_freq;
>> - }
>> -
>> - dt_vcs = malloc(nr_pstates * sizeof(u8));
>> - if (!dt_vcs) {
>> - /**
>> - * @fwts-label OCCdt_vcsENOMEM
>> - * @fwts-advice Out of memory when allocating pstates array.
>> - * No Pstates added to device tree, pstates not functional.
>> - */
>> - prlog(PR_ERR, "OCC: dt_vcs array alloc failure\n");
>> - goto out_free_vdd;
>> - }
>> -
>> - if (ultra_turbo_en) {
>> - nr_cores = get_available_nr_cores_in_chip(chip->id);
>> - dt_core_max = malloc(nr_cores * sizeof(s8));
>> - if (!dt_core_max) {
>> - /**
>> - * @fwts-label OCCdt_core_maxENOMEM
>> - * @fwts-advice Out of memory allocating dt_core_max
>> - * array. No PStates in Device Tree: non-functional
>> - * power/frequency management.
>> - */
>> - prlog(PR_ERR, "OCC: dt_core_max alloc failure\n");
>> - goto out_free_vcs;
>> - }
>> -
>> - for (i = 0; i < nr_cores; i++)
>> - dt_core_max[i] = occ_data->core_max[i];
>> - }
>> -
>> - /*
>> - * OCC provides pstate table entries in continuous descending order.
>> - * Parse the pstate table to skip pstate_ids that are greater
>> - * than Pmax. If a pstate_id is equal to Pmin then add it to
>> - * the list and break from the loop as this is the last valid
>> - * element in the pstate table.
>> - */
>> - for (i = 0, j = 0; i < MAX_PSTATES && j < nr_pstates; i++) {
>> - if (cmp_pstates(occ_data->pstates[i].id, pmax) > 0)
>> - continue;
>> -
>> - dt_id[j] = occ_data->pstates[i].id;
>> - dt_freq[j] = occ_data->pstates[i].freq_khz / 1000;
>> - dt_vdd[j] = occ_data->pstates[i].vdd;
>> - dt_vcs[j] = occ_data->pstates[i].vcs;
>> - j++;
>> -
>> - if (occ_data->pstates[i].id == occ_data->pstate_min)
>> - break;
>> - }
>> -
>> - if (j != nr_pstates) {
>> - /**
>> - * @fwts-label OCCNrPstatesMismatch
>> - * @fwts-advice The number of pstates parsed from the
>> - * pstate table does not match the total no of pstates
>> - * expected. No PStates in Device Tree: non-functional
>> - * power/frequency management.
>> - */
>> - prlog(PR_ERR, "OCC: Mismatch in nr_pstates. Expected nr_pstates =
>> %d, Actual nr_pstates parsed = %d\n",
>> - nr_pstates, j);
>> - goto out_free_vcs;
>> + allocate(dt_id, nr_pstates * sizeof(u32), out);
>> + allocate(dt_freq, nr_pstates * sizeof(u32), out_free_id);
>> +
>> + if (ultra_turbo_supported)
>> + allocate(dt_core_max, max_cores * sizeof(u32), out_free_freq);
>> +
>> + switch (occ_data->version) {
>> + case 0x01:
>> + case 0x02:
>> + allocate(dt_vdd, nr_pstates, out_free_core_max);
>> + allocate(dt_vcs, nr_pstates, out_free_vdd);
>> + parse_cpu_pstate_properties(occ_data->v2);
>> + break;
>> + case 0x90:
>> + parse_cpu_pstate_properties(occ_data->v90);
>> + break;
>> + default:
>> + return false;
>> }
>>
>> /* Add the device-tree entries */
>> @@ -408,34 +543,73 @@ static bool add_cpu_pstate_properties(s8 *pstate_nom)
>> nr_pstates * sizeof(u32));
>> dt_add_property(power_mgt, "ibm,pstate-frequencies-mhz", dt_freq,
>> nr_pstates * sizeof(u32));
>> - dt_add_property(power_mgt, "ibm,pstate-vdds", dt_vdd, nr_pstates);
>> - dt_add_property(power_mgt, "ibm,pstate-vcss", dt_vcs, nr_pstates);
>> - dt_add_property_cells(power_mgt, "ibm,pstate-min", occ_data->pstate_min);
>> - dt_add_property_cells(power_mgt, "ibm,pstate-nominal",
>> occ_data->pstate_nom);
>> + dt_add_property_cells(power_mgt, "ibm,pstate-min", pmin);
>> + dt_add_property_cells(power_mgt, "ibm,pstate-nominal", pnom);
>> dt_add_property_cells(power_mgt, "ibm,pstate-max", pmax);
>>
>> - if (ultra_turbo_en) {
>> - dt_add_property_cells(power_mgt, "ibm,pstate-turbo",
>> - occ_data->pstate_turbo);
>> - dt_add_property_cells(power_mgt, "ibm,pstate-ultra-turbo",
>> - occ_data->pstate_ultra_turbo);
>> - dt_add_property(power_mgt, "ibm,pstate-core-max", dt_core_max,
>> - nr_cores);
>> - free(dt_core_max);
>> + dt_add_property_cells(power_mgt, "#address-cells", 2);
>> + dt_add_property_cells(power_mgt, "#size-cells", 0);
>> + /* Add chip specific pstate properties */
>> + for_each_chip(chip) {
>> + struct dt_node *occ_node;
>> + u8 nr_cores = get_available_nr_cores_in_chip(chip->id);
>> +
>> + occ_data = get_occ_pstate_table(chip);
>> + occ_node = dt_new_addr(power_mgt, "occ", (uint64_t)occ_data);
>> + if (!occ_node) {
>> + /**
>> + * @fwts-label OCCDTFailedNodeCreation
>> + * @fwts-advice Failed to create
>> + * /ibm,opal/power-mgt/occ. Per-chip pstate properties
>> + * are not added to Device Tree.
>> + */
>> + prerror("OCC: Failed to create /ibm,opal/power-mgt/occ@%llx\n",
>> + (uint64_t)occ_data);
>> + goto out_free_vcs;
>> + }
>> + dt_add_property_cells(occ_node, "reg", hi32((uint64_t)occ_data),
>> + lo32((uint64_t)occ_data));
>> + dt_add_property_cells(occ_node, "ibm,chip-id", chip->id);
>> + /*
>> + * Parse and add pstate Voltage Identifiers (VID) to DT which
>> + * are provided by OCC in version 0x01 and 0x02
>> + */
>> + if (occ_data->version <= 0x02) {
>> + parse_vid(occ_data->v2);
>> + dt_add_property(occ_node, "ibm,pstate-vdds", dt_vdd,
>> + nr_pstates);
>> + dt_add_property(occ_node, "ibm,pstate-vcss", dt_vcs,
>> + nr_pstates);
>> + }
>> + /* Parse and add core_max array */
>> + if (ultra_turbo_supported) {
>> + if (occ_data->version == 0x02)
>> + parse_core_max(occ_data->v2);
>> + else if (occ_data->version == 0x90)
>> + parse_core_max(occ_data->v90);
>> +
>> + dt_add_property(occ_node, "ibm,pstate-core-max",
>> + dt_core_max, nr_cores * sizeof(u32));
>> + }
>> }
>>
>> /* Return pstate to set for each core */
>> - *pstate_nom = occ_data->pstate_nom;
>> + *pstate_nom = pnom;
>> rc = true;
>>
>> out_free_vcs:
>> - free(dt_vcs);
>> + if (occ_data->version <= 0x02)
>> + free(dt_vcs);
>> out_free_vdd:
>> - free(dt_vdd);
>> -out_free_id:
>> - free(dt_id);
>> + if (occ_data->version <= 0x02)
>> + free(dt_vdd);
>> +out_free_core_max:
>> + if (ultra_turbo_supported)
>> + free(dt_core_max);
>> out_free_freq:
>> free(dt_freq);
>> +out_free_id:
>> + free(dt_id);
>> out:
>> return rc;
>> }
>> @@ -444,7 +618,9 @@ out:
>> * Prepare chip for pstate transitions
>> */
>>
>> -static bool cpu_pstates_prepare_core(struct proc_chip *chip, struct
>> cpu_thread *c, s8 pstate_nom)
>> +static bool cpu_pstates_prepare_core(struct proc_chip *chip,
>> + struct cpu_thread *c,
>> + int pstate_nom)
>> {
>> uint32_t core = pir_to_core_id(c->pir);
>> uint64_t tmp, pstate;
>> @@ -532,6 +708,23 @@ static void occ_msg_consumed(void *data __unused)
>> unlock(&occ_lock);
>> }
>>
>> +static inline u8 get_cpu_throttle(struct proc_chip *chip)
>> +{
>> + struct occ_pstate_table *pdata = get_occ_pstate_table(chip);
>> + struct occ_dynamic_data *data;
>> +
>> + switch (pdata->version) {
>> + case 0x01:
>> + case 0x02:
>> + return pdata->v2.throttle;
>> + case 0x90:
>> + data = get_occ_dynamic_data(chip);
>> + return data->cpu_throttle;
>> + default:
>> + return 0;
>> + };
>> +}
>> +
>> static void occ_throttle_poll(void *data __unused)
>> {
>> struct proc_chip *chip;
>> @@ -545,7 +738,7 @@ static void occ_throttle_poll(void *data __unused)
>> int inactive = 0;
>>
>> for_each_chip(chip) {
>> - occ_data = chip_occ_data(chip);
>> + occ_data = get_occ_pstate_table(chip);
>> if (occ_data->valid != 1) {
>> inactive = 1;
>> break;
>> @@ -568,18 +761,21 @@ static void occ_throttle_poll(void *data __unused)
>> if (occ_opal_msg_outstanding)
>> goto done;
>> for_each_chip(chip) {
>> - occ_data = chip_occ_data(chip);
>> + u8 throttle;
>> +
>> + occ_data = get_occ_pstate_table(chip);
>> + throttle = get_cpu_throttle(chip);
>> if ((occ_data->valid == 1) &&
>> - (chip->throttle != occ_data->throttle) &&
>> - (occ_data->throttle <= OCC_MAX_THROTTLE_STATUS)) {
>> + (chip->throttle != throttle) &&
>> + (throttle <= OCC_MAX_THROTTLE_STATUS)) {
>> occ_msg.type = cpu_to_be64(OCC_THROTTLE);
>> occ_msg.chip = cpu_to_be64(chip->id);
>> - occ_msg.throttle_status = cpu_to_be64(occ_data->throttle);
>> + occ_msg.throttle_status = cpu_to_be64(throttle);
>> rc = _opal_queue_msg(OPAL_MSG_OCC, NULL,
>> occ_msg_consumed,
>> 3, (uint64_t *)&occ_msg);
>> if (!rc) {
>> - chip->throttle = occ_data->throttle;
>> + chip->throttle = throttle;
>> occ_opal_msg_outstanding = true;
>> break;
>> }
>> @@ -591,16 +787,16 @@ done:
>> }
>>
>> /* CPU-OCC PState init */
>> -/* Called after OCC init on P8 */
>> +/* Called after OCC init on P8 and P9 */
>> void occ_pstates_init(void)
>> {
>> struct proc_chip *chip;
>> struct cpu_thread *c;
>> - s8 pstate_nom;
>> + int pstate_nom;
>> static bool occ_pstates_initialized;
>>
>> - /* OCC is P8 only */
>> - if (proc_gen != proc_gen_p8)
>> + /* OCC is supported in P8 and P9 */
>> + if (proc_gen < proc_gen_p8)
>> return;
>> /* Handle fast reboots */
>> if (occ_pstates_initialized)
>> @@ -609,9 +805,11 @@ void occ_pstates_init(void)
>> switch (proc_gen) {
>> case proc_gen_p8:
>> cmp_pstates = cmp_negative_pstates;
>> + homer_opal_data_offset = P8_HOMER_OPAL_DATA_OFFSET;
>> break;
>> case proc_gen_p9:
>> cmp_pstates = cmp_positive_pstates;
>> + homer_opal_data_offset = P9_HOMER_OPAL_DATA_OFFSET;
>> break;
>> default:
>> return;
>> @@ -852,7 +1050,7 @@ int occ_msg_queue_occ_reset(void)
>> goto out;
>> }
>> /*
>> - * Set 'valid' byte of chip_occ_data to 0 since OCC
>> + * Set 'valid' byte of occ_pstate_table to 0 since OCC
>> * may not clear this byte on a reset.
>> * OCC will set the 'valid' byte to 1 when it becomes
>> * active again.
>> @@ -860,7 +1058,7 @@ int occ_msg_queue_occ_reset(void)
>> for_each_chip(chip) {
>> struct occ_pstate_table *occ_data;
>>
>> - occ_data = chip_occ_data(chip);
>> + occ_data = get_occ_pstate_table(chip);
>> occ_data->valid = 0;
>> chip->throttle = 0;
>> }
More information about the Skiboot
mailing list