Commit 0f03610b authored by Rafael J. Wysocki's avatar Rafael J. Wysocki
Browse files

Merge tag 'cpufreq-arm-fixes-5.18-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/vireshk/pm

Pull ARM cpufreq fixes for 5.18-rc5 from Viresh Kumar:

"- Fix issues with the Qualcomm's cpufreq driver (Dmitry Baryshkov and
   Vladimir Zapolskiy).
 - Fix memory leak with the Sun501 driver (Xiaobing Luo)."

* tag 'cpufreq-arm-fixes-5.18-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/vireshk/pm:
  cpufreq: qcom-cpufreq-hw: Clear dcvs interrupts
  cpufreq: fix memory leak in sun50i_cpufreq_nvmem_probe
  cpufreq: qcom-cpufreq-hw: Fix throttle frequency value on EPSS platforms
  cpufreq: qcom-hw: provide online/offline operations
  cpufreq: qcom-hw: fix the opp entries refcounting
  cpufreq: qcom-hw: fix the race between LMH worker and cpuhp
  cpufreq: qcom-hw: drop affinity hint before freeing the IRQ
parents af2d861d e4e64486
Loading
Loading
Loading
Loading
+57 −13
Original line number Diff line number Diff line
@@ -24,13 +24,17 @@
#define CLK_HW_DIV			2
#define LUT_TURBO_IND			1

#define GT_IRQ_STATUS			BIT(2)

#define HZ_PER_KHZ			1000

struct qcom_cpufreq_soc_data {
	u32 reg_enable;
	u32 reg_domain_state;
	u32 reg_dcvs_ctrl;
	u32 reg_freq_lut;
	u32 reg_volt_lut;
	u32 reg_intr_clr;
	u32 reg_current_vote;
	u32 reg_perf_state;
	u8 lut_row_size;
@@ -280,38 +284,47 @@ static void qcom_get_related_cpus(int index, struct cpumask *m)
	}
}

static unsigned int qcom_lmh_get_throttle_freq(struct qcom_cpufreq_data *data)
static unsigned long qcom_lmh_get_throttle_freq(struct qcom_cpufreq_data *data)
{
	unsigned int val = readl_relaxed(data->base + data->soc_data->reg_current_vote);
	unsigned int lval;

	if (data->soc_data->reg_current_vote)
		lval = readl_relaxed(data->base + data->soc_data->reg_current_vote) & 0x3ff;
	else
		lval = readl_relaxed(data->base + data->soc_data->reg_domain_state) & 0xff;

	return (val & 0x3FF) * 19200;
	return lval * xo_rate;
}

static void qcom_lmh_dcvs_notify(struct qcom_cpufreq_data *data)
{
	struct cpufreq_policy *policy = data->policy;
	int cpu = cpumask_first(policy->cpus);
	int cpu = cpumask_first(policy->related_cpus);
	struct device *dev = get_cpu_device(cpu);
	unsigned long freq_hz, throttled_freq;
	struct dev_pm_opp *opp;
	unsigned int freq;

	/*
	 * Get the h/w throttled frequency, normalize it using the
	 * registered opp table and use it to calculate thermal pressure.
	 */
	freq = qcom_lmh_get_throttle_freq(data);
	freq_hz = freq * HZ_PER_KHZ;
	freq_hz = qcom_lmh_get_throttle_freq(data);

	opp = dev_pm_opp_find_freq_floor(dev, &freq_hz);
	if (IS_ERR(opp) && PTR_ERR(opp) == -ERANGE)
		dev_pm_opp_find_freq_ceil(dev, &freq_hz);
		opp = dev_pm_opp_find_freq_ceil(dev, &freq_hz);

	if (IS_ERR(opp)) {
		dev_warn(dev, "Can't find the OPP for throttling: %pe!\n", opp);
	} else {
		throttled_freq = freq_hz / HZ_PER_KHZ;

		/* Update thermal pressure (the boost frequencies are accepted) */
		arch_update_thermal_pressure(policy->related_cpus, throttled_freq);

		dev_pm_opp_put(opp);
	}

	/*
	 * In the unlikely case policy is unregistered do not enable
	 * polling or h/w interrupt
@@ -350,6 +363,10 @@ static irqreturn_t qcom_lmh_dcvs_handle_irq(int irq, void *data)
	disable_irq_nosync(c_data->throttle_irq);
	schedule_delayed_work(&c_data->throttle_work, 0);

	if (c_data->soc_data->reg_intr_clr)
		writel_relaxed(GT_IRQ_STATUS,
			       c_data->base + c_data->soc_data->reg_intr_clr);

	return IRQ_HANDLED;
}

@@ -365,9 +382,11 @@ static const struct qcom_cpufreq_soc_data qcom_soc_data = {

static const struct qcom_cpufreq_soc_data epss_soc_data = {
	.reg_enable = 0x0,
	.reg_domain_state = 0x20,
	.reg_dcvs_ctrl = 0xb0,
	.reg_freq_lut = 0x100,
	.reg_volt_lut = 0x200,
	.reg_intr_clr = 0x308,
	.reg_perf_state = 0x320,
	.lut_row_size = 4,
};
@@ -417,16 +436,39 @@ static int qcom_cpufreq_hw_lmh_init(struct cpufreq_policy *policy, int index)
	return 0;
}

static void qcom_cpufreq_hw_lmh_exit(struct qcom_cpufreq_data *data)
static int qcom_cpufreq_hw_cpu_online(struct cpufreq_policy *policy)
{
	struct qcom_cpufreq_data *data = policy->driver_data;
	struct platform_device *pdev = cpufreq_get_driver_data();
	int ret;

	ret = irq_set_affinity_hint(data->throttle_irq, policy->cpus);
	if (ret)
		dev_err(&pdev->dev, "Failed to set CPU affinity of %s[%d]\n",
			data->irq_name, data->throttle_irq);

	return ret;
}

static int qcom_cpufreq_hw_cpu_offline(struct cpufreq_policy *policy)
{
	struct qcom_cpufreq_data *data = policy->driver_data;

	if (data->throttle_irq <= 0)
		return;
		return 0;

	mutex_lock(&data->throttle_lock);
	data->cancel_throttle = true;
	mutex_unlock(&data->throttle_lock);

	cancel_delayed_work_sync(&data->throttle_work);
	irq_set_affinity_hint(data->throttle_irq, NULL);

	return 0;
}

static void qcom_cpufreq_hw_lmh_exit(struct qcom_cpufreq_data *data)
{
	free_irq(data->throttle_irq, data);
}

@@ -583,6 +625,8 @@ static struct cpufreq_driver cpufreq_qcom_hw_driver = {
	.get		= qcom_cpufreq_hw_get,
	.init		= qcom_cpufreq_hw_cpu_init,
	.exit		= qcom_cpufreq_hw_cpu_exit,
	.online		= qcom_cpufreq_hw_cpu_online,
	.offline	= qcom_cpufreq_hw_cpu_offline,
	.register_em	= cpufreq_register_em_with_opp,
	.fast_switch    = qcom_cpufreq_hw_fast_switch,
	.name		= "qcom-cpufreq-hw",
+3 −1
Original line number Diff line number Diff line
@@ -98,8 +98,10 @@ static int sun50i_cpufreq_nvmem_probe(struct platform_device *pdev)
		return -ENOMEM;

	ret = sun50i_cpufreq_get_efuse(&speed);
	if (ret)
	if (ret) {
		kfree(opp_tables);
		return ret;
	}

	snprintf(name, MAX_NAME_LEN, "speed%d", speed);