[Skiboot] [PATCH V4 3/3] occ: Add support for Version 0x90 OCC_OPAL shared memory region

ppaidipe ppaidipe at linux.vnet.ibm.com
Fri Nov 11 04:44:56 AEDT 2016


On 2016-11-09 13:44, Shilpasri G Bhat wrote:
> 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?
> 

fwts scans error messages generated by prlog call only and not prerror.

Jeremy/Deb
Is it possible to teach fwts, prerror format also?


> 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