Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

Merge back cpufreq material for v4.18.

parents 0cf442c6 144ec880
Loading
Loading
Loading
Loading
+87 −13
Original line number Diff line number Diff line
@@ -23,6 +23,8 @@
#include <linux/regmap.h>
#include <linux/slab.h>

#include "cpufreq-dt.h"

/* Power management in North Bridge register set */
#define ARMADA_37XX_NB_L0L1	0x18
#define ARMADA_37XX_NB_L2L3	0x1C
@@ -56,6 +58,16 @@
 */
#define LOAD_LEVEL_NR	4

struct armada37xx_cpufreq_state {
	struct regmap *regmap;
	u32 nb_l0l1;
	u32 nb_l2l3;
	u32 nb_dyn_mod;
	u32 nb_cpu_load;
};

static struct armada37xx_cpufreq_state *armada37xx_cpufreq_state;

struct armada_37xx_dvfs {
	u32 cpu_freq_max;
	u8 divider[LOAD_LEVEL_NR];
@@ -136,7 +148,7 @@ static void __init armada37xx_cpufreq_dvfs_setup(struct regmap *base,
	clk_set_parent(clk, parent);
}

static void __init armada37xx_cpufreq_disable_dvfs(struct regmap *base)
static void armada37xx_cpufreq_disable_dvfs(struct regmap *base)
{
	unsigned int reg = ARMADA_37XX_NB_DYN_MOD,
		mask = ARMADA_37XX_NB_DFS_EN;
@@ -162,10 +174,47 @@ static void __init armada37xx_cpufreq_enable_dvfs(struct regmap *base)
	regmap_update_bits(base, reg, mask, mask);
}

static int armada37xx_cpufreq_suspend(struct cpufreq_policy *policy)
{
	struct armada37xx_cpufreq_state *state = armada37xx_cpufreq_state;

	regmap_read(state->regmap, ARMADA_37XX_NB_L0L1, &state->nb_l0l1);
	regmap_read(state->regmap, ARMADA_37XX_NB_L2L3, &state->nb_l2l3);
	regmap_read(state->regmap, ARMADA_37XX_NB_CPU_LOAD,
		    &state->nb_cpu_load);
	regmap_read(state->regmap, ARMADA_37XX_NB_DYN_MOD, &state->nb_dyn_mod);

	return 0;
}

static int armada37xx_cpufreq_resume(struct cpufreq_policy *policy)
{
	struct armada37xx_cpufreq_state *state = armada37xx_cpufreq_state;

	/* Ensure DVFS is disabled otherwise the following registers are RO */
	armada37xx_cpufreq_disable_dvfs(state->regmap);

	regmap_write(state->regmap, ARMADA_37XX_NB_L0L1, state->nb_l0l1);
	regmap_write(state->regmap, ARMADA_37XX_NB_L2L3, state->nb_l2l3);
	regmap_write(state->regmap, ARMADA_37XX_NB_CPU_LOAD,
		     state->nb_cpu_load);

	/*
	 * NB_DYN_MOD register is the one that actually enable back DVFS if it
	 * was enabled before the suspend operation. This must be done last
	 * otherwise other registers are not writable.
	 */
	regmap_write(state->regmap, ARMADA_37XX_NB_DYN_MOD, state->nb_dyn_mod);

	return 0;
}

static int __init armada37xx_cpufreq_driver_init(void)
{
	struct cpufreq_dt_platform_data pdata;
	struct armada_37xx_dvfs *dvfs;
	struct platform_device *pdev;
	unsigned long freq;
	unsigned int cur_frequency;
	struct regmap *nb_pm_base;
	struct device *cpu_dev;
@@ -207,33 +256,58 @@ static int __init armada37xx_cpufreq_driver_init(void)
	}

	dvfs = armada_37xx_cpu_freq_info_get(cur_frequency);
	if (!dvfs)
	if (!dvfs) {
		clk_put(clk);
		return -EINVAL;
	}

	armada37xx_cpufreq_state = kmalloc(sizeof(*armada37xx_cpufreq_state),
					   GFP_KERNEL);
	if (!armada37xx_cpufreq_state) {
		clk_put(clk);
		return -ENOMEM;
	}

	armada37xx_cpufreq_state->regmap = nb_pm_base;

	armada37xx_cpufreq_dvfs_setup(nb_pm_base, clk, dvfs->divider);
	clk_put(clk);

	for (load_lvl = ARMADA_37XX_DVFS_LOAD_0; load_lvl < LOAD_LEVEL_NR;
	     load_lvl++) {
		unsigned long freq = cur_frequency / dvfs->divider[load_lvl];
		freq = cur_frequency / dvfs->divider[load_lvl];

		ret = dev_pm_opp_add(cpu_dev, freq, 0);
		if (ret) {
		if (ret)
			goto remove_opp;
	}

	/* Now that everything is setup, enable the DVFS at hardware level */
	armada37xx_cpufreq_enable_dvfs(nb_pm_base);

	pdata.suspend = armada37xx_cpufreq_suspend;
	pdata.resume = armada37xx_cpufreq_resume;

	pdev = platform_device_register_data(NULL, "cpufreq-dt", -1, &pdata,
					     sizeof(pdata));
	ret = PTR_ERR_OR_ZERO(pdev);
	if (ret)
		goto disable_dvfs;

	return 0;

disable_dvfs:
	armada37xx_cpufreq_disable_dvfs(nb_pm_base);
remove_opp:
	/* clean-up the already added opp before leaving */
	while (load_lvl-- > ARMADA_37XX_DVFS_LOAD_0) {
		freq = cur_frequency / dvfs->divider[load_lvl];
		dev_pm_opp_remove(cpu_dev, freq);
	}
			return ret;
		}
	}

	/* Now that everything is setup, enable the DVFS at hardware level */
	armada37xx_cpufreq_enable_dvfs(nb_pm_base);
	kfree(armada37xx_cpufreq_state);

	pdev = platform_device_register_simple("cpufreq-dt", -1, NULL, 0);

	return PTR_ERR_OR_ZERO(pdev);
	return ret;
}
/* late_initcall, to guarantee the driver is loaded after A37xx clock driver */
late_initcall(armada37xx_cpufreq_driver_init);
+0 −2
Original line number Diff line number Diff line
@@ -66,8 +66,6 @@ static const struct of_device_id whitelist[] __initconst = {
	{ .compatible = "renesas,r8a7792", },
	{ .compatible = "renesas,r8a7793", },
	{ .compatible = "renesas,r8a7794", },
	{ .compatible = "renesas,r8a7795", },
	{ .compatible = "renesas,r8a7796", },
	{ .compatible = "renesas,sh73a0", },

	{ .compatible = "rockchip,rk2928", },
+8 −2
Original line number Diff line number Diff line
@@ -346,9 +346,15 @@ static int dt_cpufreq_probe(struct platform_device *pdev)
	if (ret)
		return ret;

	if (data && data->have_governor_per_policy)
	if (data) {
		if (data->have_governor_per_policy)
			dt_cpufreq_driver.flags |= CPUFREQ_HAVE_GOVERNOR_PER_POLICY;

		dt_cpufreq_driver.resume = data->resume;
		if (data->suspend)
			dt_cpufreq_driver.suspend = data->suspend;
	}

	ret = cpufreq_register_driver(&dt_cpufreq_driver);
	if (ret)
		dev_err(&pdev->dev, "failed register driver: %d\n", ret);
+5 −0
Original line number Diff line number Diff line
@@ -12,8 +12,13 @@

#include <linux/types.h>

struct cpufreq_policy;

struct cpufreq_dt_platform_data {
	bool have_governor_per_policy;

	int (*suspend)(struct cpufreq_policy *policy);
	int (*resume)(struct cpufreq_policy *policy);
};

#endif /* __CPUFREQ_DT_H__ */
+32 −31
Original line number Diff line number Diff line
@@ -300,8 +300,19 @@ static void adjust_jiffies(unsigned long val, struct cpufreq_freqs *ci)
#endif
}

static void __cpufreq_notify_transition(struct cpufreq_policy *policy,
		struct cpufreq_freqs *freqs, unsigned int state)
/**
 * cpufreq_notify_transition - Notify frequency transition and adjust_jiffies.
 * @policy: cpufreq policy to enable fast frequency switching for.
 * @freqs: contain details of the frequency update.
 * @state: set to CPUFREQ_PRECHANGE or CPUFREQ_POSTCHANGE.
 *
 * This function calls the transition notifiers and the "adjust_jiffies"
 * function. It is called twice on all CPU frequency changes that have
 * external effects.
 */
static void cpufreq_notify_transition(struct cpufreq_policy *policy,
				      struct cpufreq_freqs *freqs,
				      unsigned int state)
{
	BUG_ON(irqs_disabled());

@@ -313,52 +324,42 @@ static void __cpufreq_notify_transition(struct cpufreq_policy *policy,
		 state, freqs->new);

	switch (state) {

	case CPUFREQ_PRECHANGE:
		/* detect if the driver reported a value as "old frequency"
		/*
		 * Detect if the driver reported a value as "old frequency"
		 * which is not equal to what the cpufreq core thinks is
		 * "old frequency".
		 */
		if (!(cpufreq_driver->flags & CPUFREQ_CONST_LOOPS)) {
			if ((policy) && (policy->cpu == freqs->cpu) &&
			    (policy->cur) && (policy->cur != freqs->old)) {
			if (policy->cur && (policy->cur != freqs->old)) {
				pr_debug("Warning: CPU frequency is %u, cpufreq assumed %u kHz\n",
					 freqs->old, policy->cur);
				freqs->old = policy->cur;
			}
		}

		for_each_cpu(freqs->cpu, policy->cpus) {
			srcu_notifier_call_chain(&cpufreq_transition_notifier_list,
						 CPUFREQ_PRECHANGE, freqs);
		}

		adjust_jiffies(CPUFREQ_PRECHANGE, freqs);
		break;

	case CPUFREQ_POSTCHANGE:
		adjust_jiffies(CPUFREQ_POSTCHANGE, freqs);
		pr_debug("FREQ: %lu - CPU: %lu\n",
			 (unsigned long)freqs->new, (unsigned long)freqs->cpu);
		pr_debug("FREQ: %u - CPUs: %*pbl\n", freqs->new,
			 cpumask_pr_args(policy->cpus));

		for_each_cpu(freqs->cpu, policy->cpus) {
			trace_cpu_frequency(freqs->new, freqs->cpu);
		cpufreq_stats_record_transition(policy, freqs->new);
			srcu_notifier_call_chain(&cpufreq_transition_notifier_list,
						 CPUFREQ_POSTCHANGE, freqs);
		if (likely(policy) && likely(policy->cpu == freqs->cpu))
			policy->cur = freqs->new;
		break;
	}
		}

/**
 * cpufreq_notify_transition - call notifier chain and adjust_jiffies
 * on frequency transition.
 *
 * This function calls the transition notifiers and the "adjust_jiffies"
 * function. It is called twice on all CPU frequency changes that have
 * external effects.
 */
static void cpufreq_notify_transition(struct cpufreq_policy *policy,
		struct cpufreq_freqs *freqs, unsigned int state)
{
	for_each_cpu(freqs->cpu, policy->cpus)
		__cpufreq_notify_transition(policy, freqs, state);
		cpufreq_stats_record_transition(policy, freqs->new);
		policy->cur = freqs->new;
	}
}

/* Do post notifications when there are chances that transition has failed */
Loading