diff options
author | Will Song <jinpengsong@google.com> | 2024-03-06 13:19:02 -0800 |
---|---|---|
committer | Will Song <jinpengsong@google.com> | 2024-03-14 02:31:44 +0000 |
commit | 9394ba8d658edd4f662e9b9c0bfa23e0f86b0d3c (patch) | |
tree | fbd71aa0eadb1135fa96d21261794a966af50847 | |
parent | 5fe325f32229cc363d5f0a921a2a5cadc6d0cc0f (diff) | |
download | gs-9394ba8d658edd4f662e9b9c0bfa23e0f86b0d3c.tar.gz |
drivers: performance: Remove Memlat Devfreq
Removing the "shadow devfreq" we have for latency governors.
Under the tick-driven design, the benefits of the devfreqs are
no longer worth the complexity trade off. Suspend/Resume will be
handled according to arch-timer activation and Start/Stop will be
handled with probe/remove.
Bug: 327482673
Bug: 325274590
Test: Flash + Perfetto + Reboot/Suspend stress test.
Change-Id: I853fd7d1fc1abfec2e2abdb83866424c181a57fa
Merged-In: I853fd7d1fc1abfec2e2abdb83866424c181a57fa
Signed-off-by: Will Song <jinpengsong@google.com>
-rw-r--r-- | drivers/performance/lat_governors/Kbuild | 2 | ||||
-rw-r--r-- | drivers/performance/lat_governors/gs_governor_memlat.c | 449 | ||||
-rw-r--r-- | drivers/performance/lat_governors/gs_governor_memlat.h | 41 | ||||
-rw-r--r-- | drivers/performance/lat_governors/gs_memlat_devfreq.c | 369 |
4 files changed, 216 insertions, 645 deletions
diff --git a/drivers/performance/lat_governors/Kbuild b/drivers/performance/lat_governors/Kbuild index 61f2d5729..a7aab6979 100644 --- a/drivers/performance/lat_governors/Kbuild +++ b/drivers/performance/lat_governors/Kbuild @@ -5,4 +5,4 @@ ccflags-y += -I$(srctree)/drivers/performance ccflags-y += -I$(srctree)/drivers/devfreq obj-$(CONFIG_GS_LATENCY_GOVERNOR) += gs_governor_memlat.o gs_governor_dsulat.o gs_governor_utils.o -obj-$(CONFIG_GS_LATENCY_DEVFREQ) += gs_memlat_devfreq.o gs_dsulat_devfreq.o +obj-$(CONFIG_GS_LATENCY_DEVFREQ) += gs_dsulat_devfreq.o diff --git a/drivers/performance/lat_governors/gs_governor_memlat.c b/drivers/performance/lat_governors/gs_governor_memlat.c index 8e95a125d..80e6ce29a 100644 --- a/drivers/performance/lat_governors/gs_governor_memlat.c +++ b/drivers/performance/lat_governors/gs_governor_memlat.c @@ -18,27 +18,37 @@ #include <soc/google/exynos-devfreq.h> #include <trace/events/power.h> -#include "gs_governor_memlat.h" #include "gs_governor_utils.h" /** + * struct frequency_vote - Contains configs and voting data. + * @vote_name: The name of the device we will vote frequency for. + * @min_freq_req: The min vote we assert for the device frequency. + */ +struct frequency_vote { + const char* vote_name; + struct exynos_pm_qos_request min_freq_req; +}; + +/** * struct memlat_data - Node containing memlat's global data. * @gov_is_on: Governor's active state. - * @devfreq_data: Pointer to the memlat's devfreq. * @attr_grp: Tuneable governor parameters exposed to userspace. + * @dev: Reference to the governor's device. * @num_cpu_clusters: Number of CPU clusters the governor will service. * @cpu_configs_arr: Configurations for each cluster's latency vote. + * @target_freq_vote: Primary target domain. */ struct memlat_data { bool gov_is_on; - bool devfreq_initialized; - struct exynos_devfreq_data *devfreq_data; struct attribute_group *attr_grp; + struct device *dev; int num_cpu_clusters; struct cluster_config *cpu_configs_arr; + struct frequency_vote target_freq_vote; }; -static void update_memlat_gov(struct gs_cpu_perf_data *data, void* private_data); +static void update_memlat_gov(struct gs_cpu_perf_data *data, void *private_data); /* Global monitor client used to get callbacks when gs_perf_mon data is updated. */ static struct gs_perf_mon_client memlat_perf_client = { @@ -49,7 +59,7 @@ static struct gs_perf_mon_client memlat_perf_client = { /* Memlat datastructure holding memlat governor configurations and metadata. */ static struct memlat_data memlat_node; -/* Macro Expansions for sysfs nodes.*/ +/* Macro expansions for sysfs nodes. */ MAKE_CLUSTER_ATTR(memlat_node, stall_floor); MAKE_CLUSTER_ATTR(memlat_node, ratio_ceil); MAKE_CLUSTER_ATTR(memlat_node, cpuidle_state_depth_threshold); @@ -72,187 +82,39 @@ static struct attribute_group memlat_dev_attr_group = { }; /** - * update_memlat_gov - Callback function from the perf monitor to service - * the memlat governor. - * - * Input: - * @data: Performance Data from the monitor. - * @private_data: Unused. - * -*/ -static void update_memlat_gov(struct gs_cpu_perf_data *data, void* private_data) -{ - struct devfreq *df; - int err; - - if (memlat_node.devfreq_initialized) { - df = memlat_node.devfreq_data->devfreq; - mutex_lock(&df->lock); - df->governor_data = data; - err = update_devfreq(df); - if (err) - dev_err(&df->dev, "Memlat update failed: %d\n", err); - df->governor_data = NULL; - mutex_unlock(&df->lock); - } -} - -/** - * gov_start - Starts the governor. - * - * This is invoked in devfreq_add_governor during probe. - * - * Input: - * @df: The devfreq to start. -*/ -static int gov_start(struct devfreq *df) -{ - int ret = gs_perf_mon_add_client(&memlat_perf_client); - if (ret) - goto err_start; - memlat_node.gov_is_on = true; - - ret = sysfs_create_group(&df->dev.kobj, memlat_node.attr_grp); - if (ret) - goto err_sysfs; - return 0; - -err_sysfs: - memlat_node.gov_is_on = false; - gs_perf_mon_remove_client(&memlat_perf_client); -err_start: - return ret; -} - -/** - * gov_suspend - Pauses the governor. - * - * This is invoked when the entering system suspends. - * - * Input: - * @df: The devfreq to suspend. -*/ -static int gov_suspend(struct devfreq *df) -{ - memlat_node.gov_is_on = false; - if (memlat_node.devfreq_initialized) { - mutex_lock(&df->lock); - update_devfreq(df); - mutex_unlock(&df->lock); - } - - return 0; -} - -/** - * gov_resume - Restarts the governor. - * - * Input: - * @df: The devfreq to resume. -*/ -static int gov_resume(struct devfreq *df) -{ - memlat_node.gov_is_on = true; - if (memlat_node.devfreq_initialized) { - mutex_lock(&df->lock); - update_devfreq(df); - mutex_unlock(&df->lock); - } - - return 0; -} - -/** - * gov_stop - Stops the governor. - * - * This is invoked by devfreq_remove_governor. - * - * Input: - * @df: The devfreq to stop. -*/ -static void gov_stop(struct devfreq *df) -{ - memlat_node.gov_is_on = false; - gs_perf_mon_remove_client(&memlat_perf_client); - if (memlat_node.devfreq_initialized) { - mutex_lock(&df->lock); - update_devfreq(df); - mutex_unlock(&df->lock); - - sysfs_remove_group(&df->dev.kobj, memlat_node.attr_grp); - } -} - -/** - * gs_governor_memlat_ev_handler - Handles governor - * Calls start/stop/resume/suspend events. + * gs_governor_memlat_update_target_freq_vote - Registers the vote for the memlat governor. * * Inputs: - * @df: The devfreq to signal. - * @event: Start/Stop/Suspend/Resume. - * @data: Unused. + * @vote: The domain to vote on. + * @target_freq: New target frequency. * - * Return: Non-zero on error. Notice this also returns - * 0 if event is not found. + * Outputs: + * Non-zero on error. */ -int gs_governor_memlat_ev_handler(struct devfreq *df, unsigned int event, void *data) +static int gs_governor_memlat_update_target_freq_vote(struct frequency_vote *vote, + unsigned long target_freq) { - int ret; - - /* Check if the governor exists. */ - if (!df) { - pr_err("Undefined devfreq for Memory Latency governor\n"); + if (!exynos_pm_qos_request_active(&vote->min_freq_req)) return -ENODEV; - } - switch (event) { - case DEVFREQ_GOV_START: - ret = gov_start(df); - if (ret) - return ret; - - dev_dbg(df->dev.parent, "Enabled Memory Latency governor\n"); - break; - - case DEVFREQ_GOV_STOP: - gov_stop(df); - dev_dbg(df->dev.parent, "Disabled Memory Latency governor\n"); - break; - - case DEVFREQ_GOV_SUSPEND: - ret = gov_suspend(df); - if (ret) { - dev_err(df->dev.parent, "Unable to suspend Memory Latency governor (%d)\n", ret); - return ret; - } + exynos_pm_qos_update_request_async(&vote->min_freq_req, target_freq); + trace_clock_set_rate(vote->vote_name, target_freq, raw_smp_processor_id()); - dev_dbg(df->dev.parent, "Suspended Memory Latency governor\n"); - break; - - case DEVFREQ_GOV_RESUME: - ret = gov_resume(df); - if (ret) { - dev_err(df->dev.parent, "Unable to resume Memory Latency governor (%d)\n", ret); - return ret; - } - - dev_dbg(df->dev.parent, "Resumed Memory Latency governor\n"); - break; - } return 0; } -EXPORT_SYMBOL(gs_governor_memlat_ev_handler); /** - * gs_governor_memlat_get_freq - Calculates memlat freq votes desired by each CPU cluster. + * gs_governor_memlat_compute_freq - Calculates memlat freq votes for each CPU cluster. * * This function determines the memlat target frequency. * * Input: - * @df: The devfreq we are deciding a vote for. - * @freq: Where to store the computed frequency. + * @cpu_perf_data_arr: CPU data to use as input. + * + * Returns: + * @max_freq: The computed target frequency. */ -int gs_governor_memlat_get_freq(struct devfreq *df, unsigned long *freq) +static unsigned long gs_governor_memlat_compute_freq(struct gs_cpu_perf_data *cpu_perf_data_arr) { int cpu; int cluster_idx; @@ -260,19 +122,6 @@ int gs_governor_memlat_get_freq(struct devfreq *df, unsigned long *freq) unsigned long max_freq = 0; char trace_name[] = { 'c', 'p', 'u', '0', 'm', 'i', 'f', '\0' }; - /* Retrieving the CPU data array from the devfreq governor_data. */ - struct gs_cpu_perf_data *cpu_perf_data_arr = df->governor_data; - - /* If the memlat governor is not active. Reset our vote to minimum. */ - if (!memlat_node.gov_is_on) { - *freq = 0; - goto trace_out; - } - - /* If monitor data is not supplied. Maintain current vote. */ - if (!cpu_perf_data_arr) - goto trace_out; - /* For each cluster, we make a frequency decision. */ for (cluster_idx = 0; cluster_idx < memlat_node.num_cpu_clusters; cluster_idx++) { cluster = &memlat_node.cpu_configs_arr[cluster_idx]; @@ -325,30 +174,126 @@ int gs_governor_memlat_get_freq(struct devfreq *df, unsigned long *freq) } } - /* We vote on the max score across all cpus. */ - *freq = max_freq; + return max_freq; +} + +/** + * update_memlat_gov - Callback function from the perf monitor to service + * the memlat governor. + * + * Input: + * @data: Performance data from the monitor. + * @private_data: Unused. + * +*/ +static void update_memlat_gov(struct gs_cpu_perf_data *data, void* private_data) +{ + unsigned long next_frequency; + + /* If the memlat governor is not active. Reset our vote to minimum. */ + if (!memlat_node.gov_is_on || !data) { + dev_dbg(memlat_node.dev, "Memlat governor is not active. Leaving vote unchanged.\n"); + return; + } + + /* Step 1: compute the frequency. */ + next_frequency = gs_governor_memlat_compute_freq(data); + + /* Step 2: send it as a vote. */ + gs_governor_memlat_update_target_freq_vote(&memlat_node.target_freq_vote, next_frequency); +} + +/** + * gs_memlat_governor_remove_all_votes - Removes all the votes for memlat governor. +*/ +static void gs_memlat_governor_remove_all_votes(void) { + /* Remove all votes. */ + struct frequency_vote *vote = &memlat_node.target_freq_vote; + exynos_pm_qos_remove_request(&vote->min_freq_req); +} + +/** + * gov_start - Starts the governor. +*/ +static int gov_start(void) +{ + int ret; + if (memlat_node.gov_is_on) + return 0; + + /* Add clients. */ + ret = gs_perf_mon_add_client(&memlat_perf_client); + if (ret) + return ret; + + memlat_node.gov_is_on = true; -trace_out: - trace_clock_set_rate("Memlat Governor", *freq, raw_smp_processor_id()); return 0; } -EXPORT_SYMBOL(gs_governor_memlat_get_freq); /** - * gs_memlat_populate_governor - Parses memlat governor data from an input device tree node. + * gov_stop - Stops the governor. +*/ +static void gov_stop(void) +{ + if (!memlat_node.gov_is_on) + return; + + memlat_node.gov_is_on = false; + + /* Remove the client. */ + gs_perf_mon_remove_client(&memlat_perf_client); + + /* Reset the vote to minimum. */ + gs_governor_memlat_update_target_freq_vote(&memlat_node.target_freq_vote, 0); + +} + +/** + * gs_memlat_governor_initialize_vote - Initializes the votes for the memlat. + * + * Input: + * @vote_node: Node containing the vote config. + * @dev: The device the governor is binded on. + * + * Output: Non-zero on error. +*/ +static int gs_memlat_governor_initialize_vote(struct device_node *vote_node, struct device *dev) { + struct frequency_vote *primary_frequency_vote = &memlat_node.target_freq_vote; + u32 pm_qos_class; + + if (of_property_read_string(vote_node, "vote_name", + &primary_frequency_vote->vote_name)) { + dev_err(dev, "The vote device name is undefined.\n"); + return -ENODEV; + } + + if (of_property_read_u32(vote_node, "pm_qos_class", + &pm_qos_class)) { + dev_err(dev, "The pm_qos_class is undefined.\n"); + return -ENODEV; + } + + exynos_pm_qos_add_request(&primary_frequency_vote->min_freq_req, (int)pm_qos_class, 0); + + return 0; +} + +/** + * gs_memlat_governor_initialize - Initializes the dsulat governor from a DT Node. * * Inputs: - * @dev: The memlat governor's underlying device. * @governor_node: The tree node contanin governor data. + * @data: The devfreq data to update frequencies. * * Returns: Non-zero on error. */ -static int gs_memlat_populate_governor(struct device *dev, struct device_node *governor_node) +static int gs_memlat_governor_initialize(struct device_node *governor_node, struct device *dev) { + int ret = 0; struct device_node *cluster_node = NULL; struct cluster_config *cluster; int cluster_idx; - int ret = 0; memlat_node.num_cpu_clusters = of_get_child_count(governor_node); @@ -356,7 +301,7 @@ static int gs_memlat_populate_governor(struct device *dev, struct device_node *g memlat_node.cpu_configs_arr = devm_kzalloc( dev, sizeof(struct cluster_config) * memlat_node.num_cpu_clusters, GFP_KERNEL); if (!memlat_node.cpu_configs_arr) { - dev_err(dev, "no mem for cluster_config\n"); + dev_err(dev, "No memory for cluster_configs.\n"); return -ENOMEM; } @@ -370,63 +315,99 @@ static int gs_memlat_populate_governor(struct device *dev, struct device_node *g /* Increment pointer. */ cluster_idx += 1; } - return 0; } -/** - * gs_memlat_governor_initialize - Initializes the dsulat governor from a DT Node. - * - * Inputs: - * @governor_node: The tree node contanin governor data. - * @data: The devfreq data to update frequencies. - * - * Returns: Non-zero on error. -*/ -int gs_memlat_governor_initialize(struct device_node *governor_node, - struct exynos_devfreq_data *data) +static int gs_governor_memlat_driver_probe(struct platform_device *pdev) { - int ret = 0; - struct device *dev = data->dev; + struct device *dev = &pdev->dev; + struct device_node *governor_config_node, *frequency_vote_node; + int ret; - /* Configure memlat governor. */ - ret = gs_memlat_populate_governor(dev, governor_node); - if (ret) + memlat_node.dev = &pdev->dev; + memlat_node.attr_grp = &memlat_dev_attr_group; + + /* Find and intitialize frequency votes. */ + frequency_vote_node = of_get_child_by_name(dev->of_node, "primary_vote_config"); + if (!frequency_vote_node) { + dev_err(dev, "Memlat frequency_votes not defined.\n"); + return -ENODEV; + } + + ret = gs_memlat_governor_initialize_vote(frequency_vote_node, dev); + if (ret) { + dev_err(dev, "Failed to parse memlat governor node data.\n"); return ret; + } - memlat_node.attr_grp = &memlat_dev_attr_group; - memlat_node.devfreq_data = data; - return 0; + /* Find and initialize governor. */ + governor_config_node = of_get_child_by_name(dev->of_node, "governor_config"); + if (!governor_config_node) { + dev_err(dev, "Memlat Governor node not defined.\n"); + ret = -ENODEV; + goto err_out; + } -} -EXPORT_SYMBOL(gs_memlat_governor_initialize); + ret = gs_memlat_governor_initialize(governor_config_node, dev); + if (ret) { + dev_err(dev, "Failed to parse private governor data.\n"); + goto err_out; + } -/* We hold the struct for the governor here. */ -static struct devfreq_governor gs_governor_memlat = { - .name = "gs_memlat", - .get_target_freq = gs_governor_memlat_get_freq, - .event_handler = gs_governor_memlat_ev_handler, -}; + /* Add sysfs nodes here. */ + ret = sysfs_create_group(&dev->kobj, memlat_node.attr_grp); + if (ret) { + dev_err(dev, "Failed to initialize governor sysfs groups.\n"); + goto err_out; + } -/* Adds this governor to a devfreq.*/ -int gs_memlat_governor_register(void) -{ - return devfreq_add_governor(&gs_governor_memlat); + /* Start the governor servicing. */ + ret = gov_start(); + if (ret) { + dev_err(dev, "Failed to start memlat governor.\n"); + goto err_gov_start; + } + + return 0; + +err_gov_start: + sysfs_remove_group(&memlat_node.dev->kobj, memlat_node.attr_grp); +err_out: + gs_memlat_governor_remove_all_votes(); + + return ret; } -EXPORT_SYMBOL(gs_memlat_governor_register); -/* Remove this governor to a devfreq.*/ -void gs_memlat_governor_unregister(void) +static int gs_governor_memlat_driver_remove(struct platform_device *pdev) { - devfreq_remove_governor(&gs_governor_memlat); -} -EXPORT_SYMBOL(gs_memlat_governor_unregister); + /* Stop governor servicing. */ + gov_stop(); + + /* Remove Sysfs here. */ + sysfs_remove_group(&memlat_node.dev->kobj, memlat_node.attr_grp); + + /* Remove pm_qos vote here. */ + gs_memlat_governor_remove_all_votes(); -void gs_memlat_governor_set_devfreq_ready(void) { - memlat_node.devfreq_initialized = true; + return 0; } -EXPORT_SYMBOL(gs_memlat_governor_set_devfreq_ready); +static const struct of_device_id gs_governor_memlat_root_match[] = { { + .compatible = "google,gs_governor_memlat", +} }; + +static struct platform_driver gs_governor_memlat_platform_driver = { + .probe = gs_governor_memlat_driver_probe, + .remove = gs_governor_memlat_driver_remove, + .driver = { + .name = "gs_governor_memlat", + .owner = THIS_MODULE, + .of_match_table = gs_governor_memlat_root_match, + .suppress_bind_attrs = true, + }, +}; + +module_platform_driver(gs_governor_memlat_platform_driver); MODULE_AUTHOR("Will Song <jinpengsong@google.com>"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Google Source Memlat Governor");
\ No newline at end of file diff --git a/drivers/performance/lat_governors/gs_governor_memlat.h b/drivers/performance/lat_governors/gs_governor_memlat.h deleted file mode 100644 index 094dff05b..000000000 --- a/drivers/performance/lat_governors/gs_governor_memlat.h +++ /dev/null @@ -1,41 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright 2024 Google, Inc. All rights reserved. - */ - -#ifndef _GS_GOVERNOR_MEMLAT_H -#define _GS_GOVERNOR_MEMLAT_H - -#include <linux/kernel.h> -#include <linux/devfreq.h> - -/** - * gs_memlat_governor_register - Adds the governor to the devfreq system. -*/ -int gs_memlat_governor_register(void); - -/** - * gs_memlat_governor_unregister - Removes governor from the devfreq system. -*/ -void gs_memlat_governor_unregister(void); - -/** - * gs_memlat_governor_initialize - Parse and init memlat governor data. - * - * Inputs: - * @governor_node: The device tree node containing memlat data. - * @data: Devfreq data for governor. - * - * Returns: Non-zero on error. -*/ -int gs_memlat_governor_initialize(struct device_node *governor_node, - struct exynos_devfreq_data *data); - -/** - * gs_memlat_governor_set_devfreq_ready: Informs governor devfreq is initialized. - * - * TODO: Remove with devfreqs in b/327482673. -*/ -void gs_memlat_governor_set_devfreq_ready(void); - -#endif /* _GS_GOVERNOR_MEMLAT_H */ diff --git a/drivers/performance/lat_governors/gs_memlat_devfreq.c b/drivers/performance/lat_governors/gs_memlat_devfreq.c deleted file mode 100644 index 1c8765e90..000000000 --- a/drivers/performance/lat_governors/gs_memlat_devfreq.c +++ /dev/null @@ -1,369 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright 2024 Google, Inc. - * - * Google Memlat Devfreq Control Source. - */ -#define pr_fmt(fmt) "gs_memlat_devfreq: " fmt - -#include <dt-bindings/soc/google/zuma-devfreq.h> -#include <governor.h> -#include <linux/of_platform.h> -#include <soc/google/cal-if.h> -#include <soc/google/ect_parser.h> -#include <soc/google/exynos-devfreq.h> -#include <trace/events/power.h> - -#include "gs_governor_memlat.h" - -#define MEMLAT_DEVFREQ_MODULE_NAME "gs-memlat-devfreq" -#define HZ_PER_KHZ 1000 - -static int gs_memlat_devfreq_target(struct device *parent, - unsigned long *target_freq, u32 flags) -{ - struct platform_device *pdev = container_of(parent, struct platform_device, dev); - struct exynos_devfreq_data *data = platform_get_drvdata(pdev); - - if (exynos_pm_qos_request_active(&data->sys_pm_qos_min)) { - exynos_pm_qos_update_request_async(&data->sys_pm_qos_min, *target_freq); - trace_clock_set_rate(dev_name(data->dev), *target_freq, raw_smp_processor_id()); - } - - return 0; -} - -static int gs_memlat_devfreq_get_cur_min_freq(struct device *dev, - unsigned long *freq) -{ - struct platform_device *pdev = container_of(dev, struct platform_device, dev); - struct exynos_devfreq_data *data = platform_get_drvdata(pdev); - int ret = 0; - - if (freq && data->pm_qos_class) - *freq = exynos_pm_qos_read_req_value(data->pm_qos_class, - &data->sys_pm_qos_min); - - return ret; -} - -static ssize_t show_scaling_devfreq_min(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct device *parent = dev->parent; - struct platform_device *pdev = - container_of(parent, struct platform_device, dev); - struct exynos_devfreq_data *data = platform_get_drvdata(pdev); - ssize_t count = 0; - int val = 0; - - if (data->pm_qos_class) - val = exynos_pm_qos_read_req_value(data->pm_qos_class, &data->sys_pm_qos_min); - - if (val <= 0) { - dev_err(dev, "failed to read requested value\n"); - return count; - } - - count += sysfs_emit_at(buf, count, "%d\n", val); - - return count; -} - -static ssize_t store_scaling_devfreq_min(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct device *parent = dev->parent; - struct platform_device *pdev = - container_of(parent, struct platform_device, dev); - struct exynos_devfreq_data *data = platform_get_drvdata(pdev); - u32 qos_value; - - if (kstrtou32(buf, 10, &qos_value)) - return -EINVAL; - - if (exynos_pm_qos_request_active(&data->sys_pm_qos_min)) - exynos_pm_qos_update_request(&data->sys_pm_qos_min, qos_value); - - return count; -} - -static DEVICE_ATTR(scaling_devfreq_min, 0440, show_scaling_devfreq_min, - store_scaling_devfreq_min); - -#ifdef CONFIG_OF -static int exynos_devfreq_parse_dt(struct device_node *np, - struct exynos_devfreq_data *data) -{ - -#if IS_ENABLED(CONFIG_ECT) - const char *devfreq_domain_name; -#endif - if (!np) { - pr_err("device tree node undefined\n"); - return -ENODEV; - } - /* Expected to be one of SIMPLE_INTERACTIVE MEM_LATENCY DSU_LATENCY. */ - if (of_property_read_u32(np, "devfreq_type", &data->devfreq_type)) { - pr_err("devfreq_type undefined\n"); - return -ENODEV; - } - if (of_property_read_u32(np, "pm_qos_class", &data->pm_qos_class)) { - pr_err("pm_qos_class undefined\n"); - return -ENODEV; - } - if (of_property_read_u32(np, "pm_qos_class_max", &data->pm_qos_class_max)) { - pr_err("pm_qos_class undefined\n"); - return -ENODEV; - } - - data->min_freq = 0; - - if (of_property_read_u32(np, "max-freq", &data->max_freq)) { - pr_debug("max-freq undefined defaulting to INT_MAX\n"); - data->max_freq = INT_MAX; - } - -#if IS_ENABLED(CONFIG_ECT) - if (of_property_read_string(np, "devfreq_domain_name", - &devfreq_domain_name)) - return -ENODEV; - - exynos_devfreq_parse_ect(data, devfreq_domain_name); -#endif - - - if (of_property_read_u32(np, "governor", &data->gov_type)) - return -ENODEV; - - if (data->gov_type == MEM_LATENCY) { - data->governor_name = "gs_memlat"; - } else { - dev_err(data->dev, "invalid governor name (%s)\n", - data->governor_name); - return -EINVAL; - } - - if (of_property_read_u32(np, "dfs_id", &data->dfs_id)) - return -ENODEV; - - of_property_read_u32(np, "polling_ms", - &data->devfreq_profile.polling_ms); - - return 0; -} -#else -static int exynos_devfreq_parse_dt(struct device_node *np, - struct exynos_devfrq_data *data) -{ - return -EINVAL; -} -#endif - -static int exynos_init_freq_table(struct exynos_devfreq_data *data) -{ - int i, ret; - u32 freq, volt; - - for (i = 0; i < data->max_state; i++) { - freq = data->opp_list[i].freq; - volt = data->opp_list[i].volt; - - data->devfreq_profile.freq_table[i] = freq; - - ret = dev_pm_opp_add(data->dev, freq, volt); - if (ret) { - dev_err(data->dev, "failed to add opp entries %uKhz\n", - freq); - return ret; - } - - dev_dbg(data->dev, "DEVFREQ : %8uKhz, %8uuV\n", freq, - volt); - } - ret = exynos_devfreq_init_freq_table(data); - if (ret) { - dev_err(data->dev, "failed init frequency table\n"); - return ret; - } - - return 0; -} - -static int gs_memlat_devfreq_probe(struct platform_device *pdev) -{ - int ret = 0; - struct exynos_devfreq_data *data; - struct dev_pm_opp; - struct device_node *governor_node; - struct device *dev = &pdev->dev; - - data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); - if (!data) { - ret = -ENOMEM; - goto err_data; - } - - data->dev = dev; - - mutex_init(&data->lock); - - /* Find and initialize governor. */ - governor_node = of_get_child_by_name(dev->of_node, "memlat_governor"); - if (!governor_node) { - dev_err(dev, "Memlat Governor Node Not Defined.\n"); - goto err_parse_dt; - } - ret = gs_memlat_governor_initialize(governor_node, data); - if (ret) { - dev_err(dev, "failed to parse private governor data\n"); - goto err_parse_dt; - } - - ret = exynos_devfreq_parse_dt(dev->of_node, data); - if (ret) { - dev_err(dev, "failed to parse private devfreq data\n"); - goto err_parse_dt; - } - - data->devfreq_profile.max_state = data->max_state; - data->devfreq_profile.target = gs_memlat_devfreq_target; - data->devfreq_profile.get_cur_freq = gs_memlat_devfreq_get_cur_min_freq; - - data->devfreq_profile.freq_table = - devm_kcalloc(dev, data->max_state, - sizeof(*data->devfreq_profile.freq_table), - GFP_KERNEL); - if (!data->devfreq_profile.freq_table) { - dev_err(dev, "failed to allocate freq_table\n"); - ret = -ENOMEM; - goto err_freqtable; - } - - ret = exynos_init_freq_table(data); - if (ret) { - dev_err(dev, "failed initialize freq_table\n"); - goto err_init_table; - } - - platform_set_drvdata(pdev, data); - - /* We add the governor before the device. */ - ret = gs_memlat_governor_register(); - - data->devfreq = - devfreq_add_device(data->dev, &data->devfreq_profile, - data->governor_name, data->governor_data); - if (IS_ERR(data->devfreq)) { - dev_err(dev, "failed devfreq device added\n"); - ret = -EINVAL; - goto err_devfreq; - } - - exynos_pm_qos_add_request(&data->sys_pm_qos_min, (int)data->pm_qos_class, data->min_freq); - ret = sysfs_create_file(&data->devfreq->dev.kobj, - &dev_attr_scaling_devfreq_min.attr); - if (ret) - dev_warn(dev, "failed create sysfs for devfreq pm_qos_min\n"); - - gs_memlat_governor_set_devfreq_ready(); - - return 0; - -err_devfreq: - platform_set_drvdata(pdev, NULL); -err_init_table: -err_freqtable: -err_parse_dt: - mutex_destroy(&data->lock); -err_data: - return ret; -} - -static int gs_memlat_devfreq_remove(struct platform_device *pdev) -{ - struct exynos_devfreq_data *data = platform_get_drvdata(pdev); - gs_memlat_governor_unregister(); - - sysfs_remove_file(&data->devfreq->dev.kobj, - &dev_attr_scaling_devfreq_min.attr); - - exynos_pm_qos_remove_request(&data->sys_pm_qos_min); - devfreq_remove_device(data->devfreq); - platform_set_drvdata(pdev, NULL); - mutex_destroy(&data->lock); - - return 0; -} - -static struct platform_device_id gs_memlat_devfreq_driver_ids[] = { - { - .name = MEMLAT_DEVFREQ_MODULE_NAME, - }, - {}, -}; - -MODULE_DEVICE_TABLE(platform, gs_memlat_devfreq_driver_ids); - -static const struct of_device_id gs_memlat_devfreq_match[] = { - { - .compatible = "gs-memlat-devfreq", - }, - {}, -}; - -MODULE_DEVICE_TABLE(of, gs_memlat_devfreq_match); - -static struct platform_driver gs_memlat_devfreq_driver = { - .probe = gs_memlat_devfreq_probe, - .remove = gs_memlat_devfreq_remove, - .id_table = gs_memlat_devfreq_driver_ids, - .driver = { - .name = MEMLAT_DEVFREQ_MODULE_NAME, - .owner = THIS_MODULE, - .of_match_table = gs_memlat_devfreq_match, - }, -}; - -static int gs_memlat_devfreq_root_probe(struct platform_device *pdev) -{ - struct device_node *np; - int num_domains; - - np = pdev->dev.of_node; - - platform_driver_register(&gs_memlat_devfreq_driver); - - /* alloc memory for devfreq data structure */ - num_domains = of_get_child_count(np); - - /* probe each devfreq node */ - of_platform_populate(np, NULL, NULL, NULL); - - return 0; -} - -static const struct of_device_id gs_memlat_devfreq_root_match[] = { - { - .compatible = "gs-memlat-devfreq-root", - }, - {} -}; - -static struct platform_driver gs_memlat_devfreq_root_driver = { - .probe = gs_memlat_devfreq_root_probe, - .driver = { - .name = "gs-devfreq-memlat", - .owner = THIS_MODULE, - .of_match_table = gs_memlat_devfreq_root_match, - }, -}; - -module_platform_driver(gs_memlat_devfreq_root_driver); -MODULE_DESCRIPTION("Google Sourced Memory Latency Driver"); -MODULE_AUTHOR("Wei Wang <wvw@google.com>"); -MODULE_AUTHOR("Will Song <jinpengsong@google.com>"); -MODULE_DESCRIPTION("Memory latency driver"); -MODULE_LICENSE("GPL"); |