mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-12 07:37:31 -04:00
cpufreq: qcom: Add support to register for Limits Management interrupt
Update the scheduler with the frequency cap due to limits management interrupt and update back when limits throttling is removed. Change-Id: Iebf4033757853bedbd60f83e21176ff29389aff8 Signed-off-by: Taniya Das <tdas@codeaurora.org>
This commit is contained in:
parent
37d0a368db
commit
2b4eabbaff
1 changed files with 119 additions and 0 deletions
|
|
@ -8,9 +8,11 @@
|
|||
#include <linux/cpu_cooling.h>
|
||||
#include <linux/energy_model.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/pm_opp.h>
|
||||
#include <linux/sched.h>
|
||||
|
|
@ -23,15 +25,24 @@
|
|||
#define LUT_VOLT GENMASK(11, 0)
|
||||
#define LUT_ROW_SIZE 32
|
||||
#define CLK_HW_DIV 2
|
||||
#define EQ_IRQ_STATUS BIT(0)
|
||||
#define LT_IRQ_STATUS BIT(1)
|
||||
#define MAX_FN_SIZE 12
|
||||
#define LIMITS_POLLING_DELAY_MS 10
|
||||
|
||||
#define CYCLE_CNTR_OFFSET(c, m, acc_count) \
|
||||
(acc_count ? ((c - cpumask_first(m) + 1) * 4) : 0)
|
||||
|
||||
enum {
|
||||
REG_ENABLE,
|
||||
REG_FREQ_LUT,
|
||||
REG_VOLT_LUT,
|
||||
REG_PERF_STATE,
|
||||
REG_CYCLE_CNTR,
|
||||
REG_LLM_DCVS_VC_VOTE,
|
||||
REG_INTR_EN,
|
||||
REG_INTR_CLR,
|
||||
REG_INTR_STATUS,
|
||||
|
||||
REG_ARRAY_SIZE,
|
||||
};
|
||||
|
|
@ -45,6 +56,11 @@ struct cpufreq_qcom {
|
|||
struct cpufreq_frequency_table *table;
|
||||
void __iomem *base;
|
||||
cpumask_t related_cpus;
|
||||
struct delayed_work freq_poll_work;
|
||||
struct mutex dcvsh_lock;
|
||||
int dcvsh_irq;
|
||||
char dcvsh_irq_name[MAX_FN_SIZE];
|
||||
bool is_irq_enabled;
|
||||
};
|
||||
|
||||
struct cpufreq_counter {
|
||||
|
|
@ -67,11 +83,90 @@ static const u16 cpufreq_qcom_epss_std_offsets[REG_ARRAY_SIZE] = {
|
|||
[REG_VOLT_LUT] = 0x200,
|
||||
[REG_PERF_STATE] = 0x320,
|
||||
[REG_CYCLE_CNTR] = 0x3c4,
|
||||
[REG_LLM_DCVS_VC_VOTE] = 0x024,
|
||||
[REG_INTR_EN] = 0x304,
|
||||
[REG_INTR_CLR] = 0x308,
|
||||
[REG_INTR_STATUS] = 0x30C,
|
||||
};
|
||||
|
||||
static struct cpufreq_qcom *qcom_freq_domain_map[NR_CPUS];
|
||||
static struct cpufreq_counter qcom_cpufreq_counter[NR_CPUS];
|
||||
|
||||
static unsigned long limits_mitigation_notify(struct cpufreq_qcom *c)
|
||||
{
|
||||
int i;
|
||||
u32 max_vc;
|
||||
|
||||
max_vc = readl_relaxed(c->base + offsets[REG_LLM_DCVS_VC_VOTE]) &
|
||||
GENMASK(13, 8);
|
||||
|
||||
for (i = 0; i < LUT_MAX_ENTRIES; i++) {
|
||||
if (c->table[i].driver_data != max_vc)
|
||||
continue;
|
||||
else {
|
||||
sched_update_cpu_freq_min_max(&c->related_cpus, 0,
|
||||
c->table[i].frequency);
|
||||
return c->table[i].frequency;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void limits_dcvsh_poll(struct work_struct *work)
|
||||
{
|
||||
struct cpufreq_qcom *c = container_of(work, struct cpufreq_qcom,
|
||||
freq_poll_work.work);
|
||||
struct cpufreq_policy *policy;
|
||||
unsigned long freq_limit;
|
||||
u32 regval, cpu;
|
||||
|
||||
mutex_lock(&c->dcvsh_lock);
|
||||
|
||||
cpu = cpumask_first(&c->related_cpus);
|
||||
policy = cpufreq_cpu_get_raw(cpu);
|
||||
|
||||
freq_limit = limits_mitigation_notify(c);
|
||||
if (freq_limit != policy->cpuinfo.max_freq || !freq_limit) {
|
||||
mod_delayed_work(system_highpri_wq, &c->freq_poll_work,
|
||||
msecs_to_jiffies(LIMITS_POLLING_DELAY_MS));
|
||||
} else {
|
||||
regval = readl_relaxed(c->base + offsets[REG_INTR_CLR]);
|
||||
regval &= ~LT_IRQ_STATUS;
|
||||
writel_relaxed(regval, c->base + offsets[REG_INTR_CLR]);
|
||||
|
||||
c->is_irq_enabled = true;
|
||||
enable_irq(c->dcvsh_irq);
|
||||
}
|
||||
|
||||
mutex_unlock(&c->dcvsh_lock);
|
||||
}
|
||||
|
||||
static irqreturn_t dcvsh_handle_isr(int irq, void *data)
|
||||
{
|
||||
struct cpufreq_qcom *c = data;
|
||||
u32 regval;
|
||||
|
||||
regval = readl_relaxed(c->base + offsets[REG_INTR_STATUS]);
|
||||
if (!(regval & LT_IRQ_STATUS))
|
||||
return IRQ_HANDLED;
|
||||
|
||||
mutex_lock(&c->dcvsh_lock);
|
||||
|
||||
if (c->is_irq_enabled) {
|
||||
c->is_irq_enabled = false;
|
||||
disable_irq_nosync(c->dcvsh_irq);
|
||||
limits_mitigation_notify(c);
|
||||
mod_delayed_work(system_highpri_wq, &c->freq_poll_work,
|
||||
msecs_to_jiffies(LIMITS_POLLING_DELAY_MS));
|
||||
|
||||
}
|
||||
|
||||
mutex_unlock(&c->dcvsh_lock);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static u64 qcom_cpufreq_get_cpu_cycle_counter(int cpu)
|
||||
{
|
||||
struct cpufreq_counter *cpu_counter;
|
||||
|
|
@ -184,6 +279,21 @@ static int qcom_cpufreq_hw_cpu_init(struct cpufreq_policy *policy)
|
|||
|
||||
dev_pm_opp_of_register_em(policy->cpus);
|
||||
|
||||
if (c->dcvsh_irq > 0) {
|
||||
snprintf(c->dcvsh_irq_name, sizeof(c->dcvsh_irq_name),
|
||||
"dcvsh-irq-%d", policy->cpu);
|
||||
ret = devm_request_threaded_irq(cpu_dev, c->dcvsh_irq, NULL,
|
||||
dcvsh_handle_isr, IRQF_TRIGGER_HIGH | IRQF_ONESHOT |
|
||||
IRQF_NO_SUSPEND | IRQF_SHARED, c->dcvsh_irq_name, c);
|
||||
if (ret) {
|
||||
dev_err(cpu_dev, "Failed to register irq %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
c->is_irq_enabled = true;
|
||||
writel_relaxed(LT_IRQ_STATUS, c->base + offsets[REG_INTR_EN]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -374,6 +484,15 @@ static int qcom_cpu_resources_init(struct platform_device *pdev,
|
|||
return ret;
|
||||
}
|
||||
|
||||
if (of_find_property(dev->of_node, "interrupts", NULL)) {
|
||||
c->dcvsh_irq = of_irq_get(dev->of_node, index);
|
||||
if (c->dcvsh_irq > 0) {
|
||||
mutex_init(&c->dcvsh_lock);
|
||||
INIT_DEFERRABLE_WORK(&c->freq_poll_work,
|
||||
limits_dcvsh_poll);
|
||||
}
|
||||
}
|
||||
|
||||
for_each_cpu(cpu_r, &c->related_cpus)
|
||||
qcom_freq_domain_map[cpu_r] = c;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue