diff options
-rw-r--r-- | core/Makefile.inc | 2 | ||||
-rw-r--r-- | core/powercap.c | 40 | ||||
-rw-r--r-- | doc/device-tree/ibm,opal/power-mgt/powercap.rst | 48 | ||||
-rw-r--r-- | doc/opal-api/opal-powercap.rst | 96 | ||||
-rw-r--r-- | hw/occ.c | 119 | ||||
-rw-r--r-- | include/opal-api.h | 4 | ||||
-rw-r--r-- | include/powercap.h | 43 |
7 files changed, 348 insertions, 4 deletions
diff --git a/core/Makefile.inc b/core/Makefile.inc index b09c30c0..b3c9e911 100644 --- a/core/Makefile.inc +++ b/core/Makefile.inc @@ -8,7 +8,7 @@ CORE_OBJS += pci-opal.o fast-reboot.o device.o exceptions.o trace.o affinity.o CORE_OBJS += vpd.o hostservices.o platform.o nvram.o nvram-format.o hmi.o CORE_OBJS += console-log.o ipmi.o time-utils.o pel.o pool.o errorlog.o CORE_OBJS += timer.o i2c.o rtc.o flash.o sensor.o ipmi-opal.o -CORE_OBJS += flash-subpartition.o bitmap.o buddy.o pci-quirk.o +CORE_OBJS += flash-subpartition.o bitmap.o buddy.o pci-quirk.o powercap.o ifeq ($(SKIBOOT_GCOV),1) CORE_OBJS += gcov-profiling.o diff --git a/core/powercap.c b/core/powercap.c new file mode 100644 index 00000000..50b5996c --- /dev/null +++ b/core/powercap.c @@ -0,0 +1,40 @@ +/* Copyright 2017 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <powercap.h> + +static int opal_get_powercap(u32 handle, int token __unused, u32 *pcap) +{ + if (!pcap || !opal_addr_valid(pcap)) + return OPAL_PARAMETER; + + if (powercap_get_class(handle) == POWERCAP_CLASS_OCC) + return occ_get_powercap(handle, pcap); + + return OPAL_UNSUPPORTED; +}; + +opal_call(OPAL_GET_POWERCAP, opal_get_powercap, 3); + +static int opal_set_powercap(u32 handle, int token, u32 pcap) +{ + if (powercap_get_class(handle) == POWERCAP_CLASS_OCC) + return occ_set_powercap(handle, token, pcap); + + return OPAL_UNSUPPORTED; +}; + +opal_call(OPAL_SET_POWERCAP, opal_set_powercap, 3); diff --git a/doc/device-tree/ibm,opal/power-mgt/powercap.rst b/doc/device-tree/ibm,opal/power-mgt/powercap.rst new file mode 100644 index 00000000..e47caa46 --- /dev/null +++ b/doc/device-tree/ibm,opal/power-mgt/powercap.rst @@ -0,0 +1,48 @@ +power-mgt/powercap +------------------ + +The powercap sensors are populated in this node. Each child node in +the "powercap" node represents a power-cappable component. + +For example : :: + system-powercap/ + +The OPAL_GET_POWERCAP and OPAL_SET_POWERCAP calls take a handle for +what powercap property to get/set which is defined in the child node. + +The compatible property for the linux driver which will be +"ibm,opal-powercap" + +Each child node has below properties: + +`powercap-current` + Handle to indicate the current powercap + +`powercap-min` + Minimum possible powercap + +`powercap-max` + Maximum possible powercap + +Powercap handle uses the following encoding: :: + + | Class | Reserved | Attribute | + |-------|---------------|-----------| + +Note: The format of the powercap handle is ``NOT`` ABI and may change in +the future. + +.. code-block:: dts + + power-mgt { + powercap { + compatible = "ibm,opal-powercap"; + + system-powercap { + name = "system-powercap" + powercap-current = <0x00000002> + powercap-min = <0x00000000> + powercap-max = <0x00000001> + }; + }; + } diff --git a/doc/opal-api/opal-powercap.rst b/doc/opal-api/opal-powercap.rst new file mode 100644 index 00000000..00f68668 --- /dev/null +++ b/doc/opal-api/opal-powercap.rst @@ -0,0 +1,96 @@ +.. _opal-powercap: + +OPAL_GET_POWERCAP +================== +The OPAL_GET_POWERCAP call retreives current information on the power +cap. + +For each entity that can be power capped, the device tree +binding indicates what handle should be passed for each of the power cap +properties (minimum possible, maximum possible, current powercap). + +The current power cap must be between the minimium possible and maximum +possible power cap. The minimum and maximum values are dynamic to allow +for them possibly being changed by other factors or entities +(e.g. service processor). + +The call can be asynchronus, where the token parameter is used to wait +for the completion. + +Parameters +---------- +:: + u32 handle + int token + u32 *pcap + +Returns +------- +OPAL_SUCCESS + Success + +OPAL_PARAMETER + Invalid pcap pointer + +OPAL_UNSUPPORTED + No support for reading powercap sensor + +OPAL_HARDWARE + Unable to procced due to the current hardware state + +OPAL_ASYNC_COMPLETION + Request was sent and an async completion message will be sent with + token and status of the request. + +OPAL_SET_POWERCAP +============================ +The OPAL_SET_POWERCAP call sets a power cap. + +For each entity that can be power capped, the device tree +binding indicates what handle should be passed for each of the power cap +properties (minimum possible, maximum possible, current powercap). + +The current power cap must be between the minimium possible and maximum +possible power cap. + +You cannot currently set the minimum or maximum power cap, and thus +OPAL_PERMISSION will be returned if it is attempted to set. In the +future, this may change - but for now, the correct behaviour for an +Operating System is to not attempt to set them. + +Parameters +---------- +:: + u32 handle + int token + u32 pcap + +Returns +------- +OPAL_SUCCESS + Success + +OPAL_PARAMETER + Invalid powercap requested beyond powercap limits + +OPAL_UNSUPPORTED + No support for changing the powercap + +OPAL_PERMISSION + Hardware cannot take the request + +OPAL_ASYNC_COMPLETION + Request was sent and an async completion message will be sent with + token and status of the request. + +OPAL_HARDWARE + Unable to procced due to the current hardware state + +OPAL_BUSY + Previous request in progress + +OPAL_INTERNAL_ERROR + Error in request response + +OPAL_TIMEOUT + Timeout in request completion @@ -28,6 +28,7 @@ #include <opal-msg.h> #include <timer.h> #include <i2c.h> +#include <powercap.h> /* OCC Communication Area for PStates */ @@ -1060,8 +1061,8 @@ static int write_occ_cmd(struct cmd_interface *chip) return OPAL_ASYNC_COMPLETION; } -static int64_t __unused opal_occ_command(struct cmd_interface *chip, int token, - struct opal_occ_cmd_data *cdata) +static int64_t opal_occ_command(struct cmd_interface *chip, int token, + struct opal_occ_cmd_data *cdata) { int rc; @@ -1213,10 +1214,13 @@ exit: unlock(&chip->queue_lock); } +static void occ_add_powercap_sensors(struct dt_node *power_mgt); + static void occ_cmd_interface_init(void) { struct occ_dynamic_data *data; struct occ_pstate_table *pdata; + struct dt_node *power_mgt; struct proc_chip *chip; int i = 0; @@ -1247,8 +1251,119 @@ static void occ_cmd_interface_init(void) &chips[i]); i++; } + + power_mgt = dt_find_by_path(dt_root, "/ibm,opal/power-mgt"); + if (!power_mgt) { + prerror("OCC: dt node /ibm,opal/power-mgt not found\n"); + return; + } + + /* Add powercap sensors to DT */ + occ_add_powercap_sensors(power_mgt); +} + +/* Powercap interface */ +enum sensor_powercap_occ_attr { + POWERCAP_OCC_MIN, + POWERCAP_OCC_MAX, + POWERCAP_OCC_CUR, +}; + +static void occ_add_powercap_sensors(struct dt_node *power_mgt) +{ + struct dt_node *pcap, *node; + u32 handle; + + pcap = dt_new(power_mgt, "powercap"); + if (!pcap) { + prerror("OCC: Failed to create powercap node\n"); + return; + } + + dt_add_property_string(pcap, "compatible", "ibm,opal-powercap"); + node = dt_new(pcap, "system-powercap"); + if (!node) { + prerror("OCC: Failed to create system powercap node\n"); + return; + } + + handle = powercap_make_handle(POWERCAP_CLASS_OCC, POWERCAP_OCC_CUR); + dt_add_property_cells(node, "powercap-current", handle); + + handle = powercap_make_handle(POWERCAP_CLASS_OCC, POWERCAP_OCC_MIN); + dt_add_property_cells(node, "powercap-min", handle); + + handle = powercap_make_handle(POWERCAP_CLASS_OCC, POWERCAP_OCC_MAX); + dt_add_property_cells(node, "powercap-max", handle); +} + +int occ_get_powercap(u32 handle, u32 *pcap) +{ + struct occ_pstate_table *pdata; + struct occ_dynamic_data *ddata; + struct proc_chip *chip; + + chip = next_chip(NULL); + pdata = get_occ_pstate_table(chip); + ddata = get_occ_dynamic_data(chip); + + if (!pdata->valid) + return OPAL_HARDWARE; + + switch (powercap_get_attr(handle)) { + case POWERCAP_OCC_MIN: + *pcap = ddata->min_pwr_cap; + break; + case POWERCAP_OCC_MAX: + *pcap = ddata->max_pwr_cap; + break; + case POWERCAP_OCC_CUR: + *pcap = ddata->cur_pwr_cap; + break; + default: + *pcap = 0; + return OPAL_UNSUPPORTED; + } + + return OPAL_SUCCESS; } +static u16 pcap_cdata; +static struct opal_occ_cmd_data pcap_data = { + .data = (u8 *)&pcap_cdata, + .cmd = OCC_CMD_SET_POWER_CAP, +}; + +int occ_set_powercap(u32 handle, int token, u32 pcap) +{ + struct occ_dynamic_data *ddata; + struct proc_chip *chip; + int i; + + if (powercap_get_attr(handle) != POWERCAP_OCC_CUR) + return OPAL_PERMISSION; + + for (i = 0; i < nr_occs; i++) + if (chips[i].occ_role == OCC_ROLE_MASTER) + break; + + if (!(*chips[i].valid)) + return OPAL_HARDWARE; + + chip = get_chip(chips[i].chip_id); + ddata = get_occ_dynamic_data(chip); + + if (pcap == ddata->cur_pwr_cap) + return OPAL_SUCCESS; + + if (pcap && (pcap > ddata->max_pwr_cap || + pcap < ddata->min_pwr_cap)) + return OPAL_PARAMETER; + + pcap_cdata = pcap; + return opal_occ_command(&chips[i], token, &pcap_data); +}; + /* CPU-OCC PState init */ /* Called after OCC init on P8 and P9 */ void occ_pstates_init(void) diff --git a/include/opal-api.h b/include/opal-api.h index d2137d6a..3ad4898d 100644 --- a/include/opal-api.h +++ b/include/opal-api.h @@ -208,7 +208,9 @@ #define OPAL_IMC_COUNTERS_INIT 149 #define OPAL_IMC_COUNTERS_START 150 #define OPAL_IMC_COUNTERS_STOP 151 -#define OPAL_LAST 151 +#define OPAL_GET_POWERCAP 152 +#define OPAL_SET_POWERCAP 153 +#define OPAL_LAST 153 /* Device tree flags */ diff --git a/include/powercap.h b/include/powercap.h new file mode 100644 index 00000000..ec5e1269 --- /dev/null +++ b/include/powercap.h @@ -0,0 +1,43 @@ +/* Copyright 2017 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __POWERCAP_H +#define __POWERCAP_H + +#include <opal.h> + +enum powercap_class { + POWERCAP_CLASS_OCC, +}; + +/* + * Powercap handle is defined as u32. The first and last bytes are + * used to indicate the class and attribute. + * + * | Class | Reserved | Attribute | + * |-------|---------------|-----------| + */ + +#define powercap_make_handle(class, attr) (((class & 0xF) << 24) | (attr & 0xF)) + +#define powercap_get_class(handle) ((handle >> 24) & 0xF) +#define powercap_get_attr(handle) (handle & 0xF) + +/* Powercap OCC interface */ +int occ_get_powercap(u32 handle, u32 *pcap); +int occ_set_powercap(u32 handle, int token, u32 pcap); + +#endif /* __POWERCAP_H */ |