You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
167 lines
3.6 KiB
167 lines
3.6 KiB
7 years ago
|
/*
|
||
|
* Copyright (c) 2018 The Linux Foundation. All rights reserved.
|
||
|
*
|
||
|
* This program is free software; you can redistribute it and/or modify
|
||
|
* it under the terms of the GNU General Public License version 2 and
|
||
|
* only version 2 as published by the Free Software Foundation.
|
||
|
*
|
||
|
* This program is distributed in the hope that it will be useful,
|
||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
* GNU General Public License for more details.
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
#define pr_fmt(fmt) "governor_cdspl3: " fmt
|
||
|
|
||
|
#include <linux/types.h>
|
||
|
#include <linux/kernel.h>
|
||
|
#include <linux/module.h>
|
||
|
#include <linux/string.h>
|
||
|
#include <linux/err.h>
|
||
|
#include <linux/devfreq.h>
|
||
|
#include <linux/platform_device.h>
|
||
|
#include <linux/of.h>
|
||
|
#include <linux/of_device.h>
|
||
|
#include <linux/soc/qcom/cdsprm.h>
|
||
|
|
||
|
#include "governor.h"
|
||
|
|
||
|
struct cdspl3 {
|
||
|
struct device_node *of_node;
|
||
|
struct devfreq *df;
|
||
|
unsigned int l3_freq_hz;
|
||
|
};
|
||
|
|
||
|
static struct cdspl3 p_me;
|
||
|
|
||
|
static int cdsp_l3_request_callback(unsigned int freq_khz)
|
||
|
{
|
||
|
if (p_me.df) {
|
||
|
mutex_lock(&p_me.df->lock);
|
||
|
p_me.l3_freq_hz = freq_khz * 1000;
|
||
|
update_devfreq(p_me.df);
|
||
|
mutex_unlock(&p_me.df->lock);
|
||
|
} else {
|
||
|
pr_err("CDSP L3 request for %dKHz not served", freq_khz);
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static struct cdsprm_l3 cdsprm = {
|
||
|
.set_l3_freq = cdsp_l3_request_callback,
|
||
|
};
|
||
|
|
||
|
static int devfreq_get_target_freq(struct devfreq *df,
|
||
|
unsigned long *freq)
|
||
|
{
|
||
|
if (freq)
|
||
|
*freq = (unsigned long)p_me.l3_freq_hz;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int gov_start(struct devfreq *df)
|
||
|
{
|
||
|
if (p_me.of_node != df->dev.parent->of_node) {
|
||
|
dev_err(df->dev.parent,
|
||
|
"Device match error in CDSP L3 frequency governor\n");
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
p_me.df = df;
|
||
|
p_me.l3_freq_hz = 0;
|
||
|
/*
|
||
|
* Send governor start message to CDSP RM driver
|
||
|
*/
|
||
|
cdsprm_register_cdspl3gov(&cdsprm);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int gov_stop(struct devfreq *df)
|
||
|
{
|
||
|
p_me.df = 0;
|
||
|
p_me.l3_freq_hz = 0;
|
||
|
/*
|
||
|
* Send governor stop message to CDSP RM driver
|
||
|
*/
|
||
|
cdsprm_unregister_cdspl3gov();
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int devfreq_event_handler(struct devfreq *df,
|
||
|
unsigned int event, void *data)
|
||
|
{
|
||
|
int ret;
|
||
|
|
||
|
switch (event) {
|
||
|
case DEVFREQ_GOV_START:
|
||
|
ret = gov_start(df);
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
dev_info(df->dev.parent,
|
||
|
"Successfully started CDSP L3 governor\n");
|
||
|
break;
|
||
|
case DEVFREQ_GOV_STOP:
|
||
|
dev_info(df->dev.parent,
|
||
|
"Received stop CDSP L3 governor event\n");
|
||
|
ret = gov_stop(df);
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static struct devfreq_governor cdsp_l3_gov = {
|
||
|
.name = "cdspl3",
|
||
|
.get_target_freq = devfreq_get_target_freq,
|
||
|
.event_handler = devfreq_event_handler,
|
||
|
};
|
||
|
|
||
|
static int cdsp_l3_driver_probe(struct platform_device *pdev)
|
||
|
{
|
||
|
struct device *dev = &pdev->dev;
|
||
|
int ret;
|
||
|
|
||
|
p_me.of_node = of_parse_phandle(dev->of_node, "qcom,target-dev", 0);
|
||
|
if (!p_me.of_node) {
|
||
|
dev_err(dev, "Couldn't find a target device\n");
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
ret = devfreq_add_governor(&cdsp_l3_gov);
|
||
|
if (ret)
|
||
|
dev_err(dev, "Failed registering CDSP L3 requests %d\n",
|
||
|
ret);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static const struct of_device_id cdsp_l3_match_table[] = {
|
||
|
{ .compatible = "qcom,cdsp-l3" },
|
||
|
{}
|
||
|
};
|
||
|
|
||
|
static struct platform_driver cdsp_l3 = {
|
||
|
.probe = cdsp_l3_driver_probe,
|
||
|
.driver = {
|
||
|
.name = "cdsp-l3",
|
||
|
.of_match_table = cdsp_l3_match_table,
|
||
|
}
|
||
|
};
|
||
|
|
||
|
static int __init cdsp_l3_gov_module_init(void)
|
||
|
{
|
||
|
return platform_driver_register(&cdsp_l3);
|
||
|
|
||
|
}
|
||
|
module_init(cdsp_l3_gov_module_init);
|
||
|
|
||
|
static void __exit cdsp_l3_gov_module_exit(void)
|
||
|
{
|
||
|
devfreq_remove_governor(&cdsp_l3_gov);
|
||
|
platform_driver_unregister(&cdsp_l3);
|
||
|
}
|
||
|
module_exit(cdsp_l3_gov_module_exit);
|
||
|
MODULE_LICENSE("GPL v2");
|