summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWill Song <jinpengsong@google.com>2024-03-06 13:19:02 -0800
committerWill Song <jinpengsong@google.com>2024-03-14 02:31:44 +0000
commit9394ba8d658edd4f662e9b9c0bfa23e0f86b0d3c (patch)
treefbd71aa0eadb1135fa96d21261794a966af50847
parent5fe325f32229cc363d5f0a921a2a5cadc6d0cc0f (diff)
downloadgs-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/Kbuild2
-rw-r--r--drivers/performance/lat_governors/gs_governor_memlat.c449
-rw-r--r--drivers/performance/lat_governors/gs_governor_memlat.h41
-rw-r--r--drivers/performance/lat_governors/gs_memlat_devfreq.c369
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");