
hulk inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/IBC4SJ -------------------------------- There is no way to directly observe the uncore DDRC load data. However, the hisi uncore provides a PMU that can monitor the DDRC status. Therefore, the DDRC PMU is used to implement the uncore DDRC event. Signed-off-by: Xiangwei Li <liwei728@huawei.com> --- drivers/devfreq/event/Makefile | 3 +- drivers/devfreq/event/hisi-uncore-ddrc.c | 167 +++++++++++++++++++++++ 2 files changed, 169 insertions(+), 1 deletion(-) create mode 100644 drivers/devfreq/event/hisi-uncore-ddrc.c diff --git a/drivers/devfreq/event/Makefile b/drivers/devfreq/event/Makefile index c22ba093498d..8b905a614433 100644 --- a/drivers/devfreq/event/Makefile +++ b/drivers/devfreq/event/Makefile @@ -4,4 +4,5 @@ obj-$(CONFIG_DEVFREQ_EVENT_EXYNOS_NOCP) += exynos-nocp.o obj-$(CONFIG_DEVFREQ_EVENT_EXYNOS_PPMU) += exynos-ppmu.o obj-$(CONFIG_DEVFREQ_EVENT_ROCKCHIP_DFI) += rockchip-dfi.o -obj-$(CONFIG_DEVFREQ_EVENT_HISI_UNCORE) += hisi-uncore.o hisi-uncore-l3c.o +obj-$(CONFIG_DEVFREQ_EVENT_HISI_UNCORE) += hisi-uncore.o \ + hisi-uncore-l3c.o hisi-uncore-ddrc.o diff --git a/drivers/devfreq/event/hisi-uncore-ddrc.c b/drivers/devfreq/event/hisi-uncore-ddrc.c new file mode 100644 index 000000000000..4fae68d64f94 --- /dev/null +++ b/drivers/devfreq/event/hisi-uncore-ddrc.c @@ -0,0 +1,167 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * hisi-uncore-ddrc.c - Hisi uncore PMU (Platform Performance Monitoring Unit) support + * + * Copyright (c) 2024 Hisi Electronics Co., Ltd. + * Author : Xiangwei Li <liwei728@huawei.com> + * + * This driver is based on drivers/devfreq/hisi_uncore/hisi-uncore-pmu.c + */ + +#include <linux/acpi.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/devfreq-event.h> + +#include "hisi-uncore.h" + +#define CORRECT_PERIOD (10) +#define CALC_PRECSION (1000) + +static HISI_UNCORE_EVENT_TYPE_ATTR; +static HISI_UNCORE_EVENT_CONFIG_ATTR; + +static int ddrc_get_events(struct devfreq_event_dev *edev, struct devfreq_event_data *edata) +{ + u64 load; + static int period; + static u64 last_load; + int f0, f1, p0, p1, per_step; + struct hisi_uncore_event_info *info; + + info = devfreq_event_get_drvdata(edev); + load = get_pmu_monitor_status(info); + per_step = info->freq_domain->per_step; + + if (info->is_reset) { + info->is_reset = false; + info->max_load = 0; + period = 0; + return 0; + } + + period++; + if (period == CORRECT_PERIOD) { + edata->load_count = info->max_load; + edata->total_count = info->max_load; + last_load = load; + return 0; + } + + if (period > CORRECT_PERIOD) { + period = 0; + p0 = (CALC_PRECSION * load) / last_load; + p1 = (CALC_PRECSION * last_load) / info->max_load; + f0 = (CALC_PRECSION * load) / (info->max_load * per_step); + f1 = (CALC_PRECSION * last_load) / (info->max_load * per_step); + + if (p1 < per_step || p0 > (f0 * (CALC_PRECSION * info->freq_domain->freq_max)) / + (f1 * (info->freq_domain->freq_max - info->freq_domain->freq_step))) + info->max_load = load; + } + + + info->max_load = max(info->max_load, load); + edata->load_count = load; + edata->total_count = info->max_load; + + return 0; +} + +static int ddrc_set_events(struct devfreq_event_dev *edev) +{ + return 0; +} + +static const struct devfreq_event_ops ddrc_event_ops = { + .set_event = ddrc_set_events, + .get_event = ddrc_get_events, +}; + +static int hisi_ddrc_event_probe(struct platform_device *pdev) +{ + int ret; + struct hisi_uncore_event_info *data; + struct devfreq_event_dev *edev; + struct devfreq_event_desc *desc; + struct device *dev = &pdev->dev; + + data = devm_kzalloc(dev, sizeof(struct hisi_uncore_event_info), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->dev = dev; + HISI_UNCORE_EVENT_NAME(data->name, "ddrc", dev->id); + + desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL); + if (!desc) + return -ENOMEM; + desc->ops = &ddrc_event_ops; + desc->driver_data = data; + desc->name = data->name; + data->desc = desc; + data->freq_domain = pdev->dev.platform_data; + data->freq_domain->per_step = + (CALC_PRECSION * data->freq_domain->freq_step) / data->freq_domain->freq_max; + + edev = devm_devfreq_event_add_edev(dev, desc); + if (IS_ERR(edev)) { + dev_err(dev, + "failed to add devfreq-event device\n"); + ret = PTR_ERR(edev); + return ret; + } + + data->edev = edev; + + ret = device_create_file(&edev->dev, &dev_attr_hisi_uncore_event_types); + if (ret) { + dev_err(&pdev->dev, "Failed to create custom sysfs files\n"); + return ret; + } + + ret = device_create_file(&edev->dev, &dev_attr_hisi_uncore_event_configs); + if (ret) { + dev_err(&pdev->dev, "Failed to create custom sysfs files\n"); + return ret; + } + + platform_set_drvdata(pdev, data); + + mutex_init(&data->lock); + + return 0; +} + +static int hisi_ddrc_event_remove(struct platform_device *pdev) +{ + struct hisi_uncore_event_info *data = platform_get_drvdata(pdev); + + release_pmu_monitor(data); + device_remove_file(&data->edev->dev, &dev_attr_hisi_uncore_event_types); + device_remove_file(&data->edev->dev, &dev_attr_hisi_uncore_event_configs); + + return 0; +} + +static const struct platform_device_id hisi_ddrc_pmu_plat_match[] = { + { .name = "EVT-UNCORE-DDRC", }, + {} +}; +MODULE_DEVICE_TABLE(platform, hisi_ddrc_pmu_plat_match); + +struct platform_driver hisi_ddrc_event_driver = { + .probe = hisi_ddrc_event_probe, + .remove = hisi_ddrc_event_remove, + .driver = { + .name = "EVT-UNCORE-DDRC", + }, + .id_table = hisi_ddrc_pmu_plat_match, +}; + +module_platform_driver(hisi_ddrc_event_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Xiangwei Li <liwei728@huawei.com>"); +MODULE_DESCRIPTION("Hisi uncore ddrc pmu events driver"); -- 2.25.1