Offering: HULK hulk inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I67QNJ CVE: NA
------------------------------------------------------------------
Platform devices which supports power control are often required to be power off/on together with the devices in the same power domain. However, there isn't a generic driver that support the power control logic of these devices.
ACPI container seems to be a good place to hold these control logic. Add platform devices in the same power domain in a ACPI container, we can easily get the locality information about these devices and can moniter the power of these devices in the same power domain together.
This patch provide three userspace control interface to control the power of devices together in the container: - state: Echo online to state to power up the devices in the container and then online these devices which will be triggered by BIOS. Echo offline to the state to offline and eject the child devices in the container which are ejectable. - pxms: show the pxms of devices which are present in the container.
In our scenario, we need to control the power of HBM memory devices which can be power consuming and will only be used in some specialized scenarios, such as HPC. HBM memory devices in a socket are in the same power domain, and should be power off/on together. We have come up with an idea that put these power control logic in a specialized driver, but ACPI container seems to be a more generic place to hold these control logic.
Signed-off-by: Zhang Zekun zhangzekun11@huawei.com --- v3: - move the common code to hisi_internal.h
drivers/soc/Kconfig | 1 + drivers/soc/Makefile | 1 + drivers/soc/hisilicon/Kconfig | 19 +++ drivers/soc/hisilicon/Makefile | 3 + drivers/soc/hisilicon/hisi_hbmdev.c | 166 ++++++++++++++++++++++++++ drivers/soc/hisilicon/hisi_internal.h | 31 +++++ 6 files changed, 221 insertions(+) create mode 100644 drivers/soc/hisilicon/Kconfig create mode 100644 drivers/soc/hisilicon/Makefile create mode 100644 drivers/soc/hisilicon/hisi_hbmdev.c create mode 100644 drivers/soc/hisilicon/hisi_internal.h
diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig index 425ab6f7e375..f7c59b063321 100644 --- a/drivers/soc/Kconfig +++ b/drivers/soc/Kconfig @@ -23,5 +23,6 @@ source "drivers/soc/versatile/Kconfig" source "drivers/soc/xilinx/Kconfig" source "drivers/soc/zte/Kconfig" source "drivers/soc/kendryte/Kconfig" +source "drivers/soc/hisilicon/Kconfig"
endmenu diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile index 36452bed86ef..68f186e00e44 100644 --- a/drivers/soc/Makefile +++ b/drivers/soc/Makefile @@ -29,3 +29,4 @@ obj-$(CONFIG_PLAT_VERSATILE) += versatile/ obj-y += xilinx/ obj-$(CONFIG_ARCH_ZX) += zte/ obj-$(CONFIG_SOC_KENDRYTE) += kendryte/ +obj-y += hisilicon/ diff --git a/drivers/soc/hisilicon/Kconfig b/drivers/soc/hisilicon/Kconfig new file mode 100644 index 000000000000..497787af004e --- /dev/null +++ b/drivers/soc/hisilicon/Kconfig @@ -0,0 +1,19 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Hisilicon SoC drivers +# +menu "Hisilicon SoC driver support" + +config HISI_HBMDEV + tristate "add extra support for hbm memory device" + depends on ACPI_HOTPLUG_MEMORY + select ACPI_CONTAINER + help + This driver add extra supports for memory devices. The driver + provides methods for userpace to control the power of memory + devices in a container. + + To compile this driver as a module, choose M here: + the module will be called hisi_hbmdev. + +endmenu diff --git a/drivers/soc/hisilicon/Makefile b/drivers/soc/hisilicon/Makefile new file mode 100644 index 000000000000..22e87acb1ab3 --- /dev/null +++ b/drivers/soc/hisilicon/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_HISI_HBMDEV) += hisi_hbmdev.o diff --git a/drivers/soc/hisilicon/hisi_hbmdev.c b/drivers/soc/hisilicon/hisi_hbmdev.c new file mode 100644 index 000000000000..82943cd35fa2 --- /dev/null +++ b/drivers/soc/hisilicon/hisi_hbmdev.c @@ -0,0 +1,166 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) Huawei Technologies Co., Ltd. 2023. All rights reserved. + */ + +#include <linux/kobject.h> +#include <linux/module.h> +#include <linux/nodemask.h> +#include <linux/acpi.h> +#include <linux/container.h> + +#include "hisi_internal.h" + +struct memory_dev { + struct kobject *memdev_kobj; +}; + +static struct memory_dev *mdev; + +static int get_pxm(struct acpi_device *acpi_device, void *arg) +{ + int nid; + unsigned long long sta; + acpi_handle handle; + nodemask_t *mask; + acpi_status status; + + mask = arg; + handle = acpi_device->handle; + + status = acpi_evaluate_integer(handle, "_STA", NULL, &sta); + if (ACPI_SUCCESS(status) && (sta & ACPI_STA_DEVICE_ENABLED)) { + nid = acpi_get_node(handle); + if (nid >= 0) + node_set(nid, *mask); + } + + return 0; +} + +static ssize_t pxms_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + nodemask_t mask; + struct acpi_device *adev; + + adev = to_acpi_device(dev); + nodes_clear(mask); + acpi_dev_for_each_child(adev, get_pxm, &mask); + + return sysfs_emit(buf, "%*pbl\n", + nodemask_pr_args(&mask)); +} +static DEVICE_ATTR_RO(pxms); + +static int memdev_power_on(struct acpi_device *adev) +{ + acpi_status status; + acpi_handle handle; + + handle = adev->handle; + status = acpi_evaluate_object(handle, "_ON", NULL, NULL); + if (ACPI_FAILURE(status)) { + acpi_handle_warn(handle, "Power on failed (0x%x)\n", status); + return -ENODEV; + } + + return 0; +} + +static int eject_device(struct acpi_device *acpi_device, void *not_used) +{ + acpi_object_type unused; + acpi_status status; + + status = acpi_get_type(acpi_device->handle, &unused); + if (ACPI_FAILURE(status) || !acpi_device->flags.ejectable) + return -ENODEV; + + get_device(&acpi_device->dev); + status = acpi_hotplug_schedule(acpi_device, ACPI_OST_EC_OSPM_EJECT); + if (ACPI_SUCCESS(status)) + return 0; + + put_device(&acpi_device->dev); + acpi_evaluate_ost(acpi_device->handle, ACPI_OST_EC_OSPM_EJECT, + ACPI_OST_SC_NON_SPECIFIC_FAILURE, NULL); + + return status == AE_NO_MEMORY ? -ENOMEM : -EAGAIN; +} + +static int memdev_power_off(struct acpi_device *adev) +{ + return acpi_dev_for_each_child(adev, eject_device, NULL); +} + +static ssize_t state_store(struct device *d, struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret; + struct acpi_device *adev; + const int online_type = online_type_from_str(buf); + + if (online_type < 0) + return -EINVAL; + + adev = to_acpi_device(d); + switch (online_type) { + case STATE_ONLINE: + ret = memdev_power_on(adev); + if (!ret) + return count; + break; + case STATE_OFFLINE: + ret = memdev_power_off(adev); + if (!ret) + return count; + break; + default: + return -EINVAL; + } + + return ret; +} +static DEVICE_ATTR_WO(state); + +static int __init mdev_init(void) +{ + struct cdev_node *cnode; + + mdev = kzalloc(sizeof(struct memory_dev), GFP_KERNEL); + if (!mdev) + return -ENOMEM; + + mdev->memdev_kobj = kobject_create_and_add("hbm_memory", kernel_kobj); + if (!mdev->memdev_kobj) { + kfree(mdev); + return -ENOMEM; + } + + list_for_each_entry(cnode, &cdev_list->clist, clist) { + device_create_file(cnode->dev, &dev_attr_state); + device_create_file(cnode->dev, &dev_attr_pxms); + } + + return 0; +} +module_init(mdev_init); + +static void __exit mdev_exit(void) +{ + struct cdev_node *cnode; + + list_for_each_entry(cnode, &cdev_list->clist, clist) { + device_remove_file(cnode->dev, &dev_attr_state); + device_remove_file(cnode->dev, &dev_attr_pxms); + } + + kobject_put(mdev->memdev_kobj); + kfree(mdev); +} +module_exit(mdev_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Zhang Zekun zhangzekun11@huawei.com"); diff --git a/drivers/soc/hisilicon/hisi_internal.h b/drivers/soc/hisilicon/hisi_internal.h new file mode 100644 index 000000000000..f14596f58a05 --- /dev/null +++ b/drivers/soc/hisilicon/hisi_internal.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) Huawei Technologies Co., Ltd. 2023. All rights reserved. + */ + +#ifndef _HISI_INTERNAL_H +#define _HISI_INTERNAL_H + +enum { + STATE_ONLINE, + STATE_OFFLINE, +}; + +static const char *const online_type_to_str[] = { + [STATE_ONLINE] = "online", + [STATE_OFFLINE] = "offline", +}; + +static int online_type_from_str(const char *str) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(online_type_to_str); i++) { + if (sysfs_streq(str, online_type_to_str[i])) + return i; + } + + return -EINVAL; +} + +#endif