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

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

Merge back cpufreq changes for 4.19.

parents 01e61a42 5a4c9967
Loading
Loading
Loading
Loading
+15 −0
Original line number Diff line number Diff line
@@ -33,3 +33,18 @@ nb_pm: syscon@14000 {
	compatible = "marvell,armada-3700-nb-pm", "syscon";
	reg = <0x14000 0x60>;
}

AVS
---

For AVS an other component is needed:

Required properties:
- compatible     : should contain "marvell,armada-3700-avs", "syscon";
- reg            : the register start and length for the AVS

Example:
avs: avs@11500 {
	compatible = "marvell,armada-3700-avs", "syscon";
	reg = <0x11500 0x40>;
}
+1 −0
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ cpufreq.

  cpu_idle		"state=%lu cpu_id=%lu"
  cpu_frequency		"state=%lu cpu_id=%lu"
  cpu_frequency_limits	"min=%lu max=%lu cpu_id=%lu"

A suspend event is used to indicate the system going in and out of the
suspend mode:
+160 −3
Original line number Diff line number Diff line
@@ -51,6 +51,16 @@
#define  ARMADA_37XX_DVFS_LOAD_2	2
#define  ARMADA_37XX_DVFS_LOAD_3	3

/* AVS register set */
#define ARMADA_37XX_AVS_CTL0		0x0
#define	 ARMADA_37XX_AVS_ENABLE		BIT(30)
#define	 ARMADA_37XX_AVS_HIGH_VDD_LIMIT	16
#define	 ARMADA_37XX_AVS_LOW_VDD_LIMIT	22
#define	 ARMADA_37XX_AVS_VDD_MASK	0x3F
#define ARMADA_37XX_AVS_CTL2		0x8
#define	 ARMADA_37XX_AVS_LOW_VDD_EN	BIT(6)
#define ARMADA_37XX_AVS_VSET(x)	    (0x1C + 4 * (x))

/*
 * On Armada 37xx the Power management manages 4 level of CPU load,
 * each level can be associated with a CPU clock source, a CPU
@@ -58,6 +68,17 @@
 */
#define LOAD_LEVEL_NR	4

#define MIN_VOLT_MV 1000

/*  AVS value for the corresponding voltage (in mV) */
static int avs_map[] = {
	747, 758, 770, 782, 793, 805, 817, 828, 840, 852, 863, 875, 887, 898,
	910, 922, 933, 945, 957, 968, 980, 992, 1003, 1015, 1027, 1038, 1050,
	1062, 1073, 1085, 1097, 1108, 1120, 1132, 1143, 1155, 1167, 1178, 1190,
	1202, 1213, 1225, 1237, 1248, 1260, 1272, 1283, 1295, 1307, 1318, 1330,
	1342
};

struct armada37xx_cpufreq_state {
	struct regmap *regmap;
	u32 nb_l0l1;
@@ -71,6 +92,7 @@ static struct armada37xx_cpufreq_state *armada37xx_cpufreq_state;
struct armada_37xx_dvfs {
	u32 cpu_freq_max;
	u8 divider[LOAD_LEVEL_NR];
	u32 avs[LOAD_LEVEL_NR];
};

static struct armada_37xx_dvfs armada_37xx_dvfs[] = {
@@ -148,6 +170,128 @@ static void __init armada37xx_cpufreq_dvfs_setup(struct regmap *base,
	clk_set_parent(clk, parent);
}

/*
 * Find out the armada 37x supported AVS value whose voltage value is
 * the round-up closest to the target voltage value.
 */
static u32 armada_37xx_avs_val_match(int target_vm)
{
	u32 avs;

	/* Find out the round-up closest supported voltage value */
	for (avs = 0; avs < ARRAY_SIZE(avs_map); avs++)
		if (avs_map[avs] >= target_vm)
			break;

	/*
	 * If all supported voltages are smaller than target one,
	 * choose the largest supported voltage
	 */
	if (avs == ARRAY_SIZE(avs_map))
		avs = ARRAY_SIZE(avs_map) - 1;

	return avs;
}

/*
 * For Armada 37xx soc, L0(VSET0) VDD AVS value is set to SVC revision
 * value or a default value when SVC is not supported.
 * - L0 can be read out from the register of AVS_CTRL_0 and L0 voltage
 *   can be got from the mapping table of avs_map.
 * - L1 voltage should be about 100mv smaller than L0 voltage
 * - L2 & L3 voltage should be about 150mv smaller than L0 voltage.
 * This function calculates L1 & L2 & L3 AVS values dynamically based
 * on L0 voltage and fill all AVS values to the AVS value table.
 */
static void __init armada37xx_cpufreq_avs_configure(struct regmap *base,
						struct armada_37xx_dvfs *dvfs)
{
	unsigned int target_vm;
	int load_level = 0;
	u32 l0_vdd_min;

	if (base == NULL)
		return;

	/* Get L0 VDD min value */
	regmap_read(base, ARMADA_37XX_AVS_CTL0, &l0_vdd_min);
	l0_vdd_min = (l0_vdd_min >> ARMADA_37XX_AVS_LOW_VDD_LIMIT) &
		ARMADA_37XX_AVS_VDD_MASK;
	if (l0_vdd_min >= ARRAY_SIZE(avs_map))  {
		pr_err("L0 VDD MIN %d is not correct.\n", l0_vdd_min);
		return;
	}
	dvfs->avs[0] = l0_vdd_min;

	if (avs_map[l0_vdd_min] <= MIN_VOLT_MV) {
		/*
		 * If L0 voltage is smaller than 1000mv, then all VDD sets
		 * use L0 voltage;
		 */
		u32 avs_min = armada_37xx_avs_val_match(MIN_VOLT_MV);

		for (load_level = 1; load_level < LOAD_LEVEL_NR; load_level++)
			dvfs->avs[load_level] = avs_min;

		return;
	}

	/*
	 * L1 voltage is equal to L0 voltage - 100mv and it must be
	 * larger than 1000mv
	 */

	target_vm = avs_map[l0_vdd_min] - 100;
	target_vm = target_vm > MIN_VOLT_MV ? target_vm : MIN_VOLT_MV;
	dvfs->avs[1] = armada_37xx_avs_val_match(target_vm);

	/*
	 * L2 & L3 voltage is equal to L0 voltage - 150mv and it must
	 * be larger than 1000mv
	 */
	target_vm = avs_map[l0_vdd_min] - 150;
	target_vm = target_vm > MIN_VOLT_MV ? target_vm : MIN_VOLT_MV;
	dvfs->avs[2] = dvfs->avs[3] = armada_37xx_avs_val_match(target_vm);
}

static void __init armada37xx_cpufreq_avs_setup(struct regmap *base,
						struct armada_37xx_dvfs *dvfs)
{
	unsigned int avs_val = 0, freq;
	int load_level = 0;

	if (base == NULL)
		return;

	/* Disable AVS before the configuration */
	regmap_update_bits(base, ARMADA_37XX_AVS_CTL0,
			   ARMADA_37XX_AVS_ENABLE, 0);


	/* Enable low voltage mode */
	regmap_update_bits(base, ARMADA_37XX_AVS_CTL2,
			   ARMADA_37XX_AVS_LOW_VDD_EN,
			   ARMADA_37XX_AVS_LOW_VDD_EN);


	for (load_level = 1; load_level < LOAD_LEVEL_NR; load_level++) {
		freq = dvfs->cpu_freq_max / dvfs->divider[load_level];

		avs_val = dvfs->avs[load_level];
		regmap_update_bits(base, ARMADA_37XX_AVS_VSET(load_level-1),
		    ARMADA_37XX_AVS_VDD_MASK << ARMADA_37XX_AVS_HIGH_VDD_LIMIT |
		    ARMADA_37XX_AVS_VDD_MASK << ARMADA_37XX_AVS_LOW_VDD_LIMIT,
		    avs_val << ARMADA_37XX_AVS_HIGH_VDD_LIMIT |
		    avs_val << ARMADA_37XX_AVS_LOW_VDD_LIMIT);
	}

	/* Enable AVS after the configuration */
	regmap_update_bits(base, ARMADA_37XX_AVS_CTL0,
			   ARMADA_37XX_AVS_ENABLE,
			   ARMADA_37XX_AVS_ENABLE);

}

static void armada37xx_cpufreq_disable_dvfs(struct regmap *base)
{
	unsigned int reg = ARMADA_37XX_NB_DYN_MOD,
@@ -216,7 +360,7 @@ static int __init armada37xx_cpufreq_driver_init(void)
	struct platform_device *pdev;
	unsigned long freq;
	unsigned int cur_frequency;
	struct regmap *nb_pm_base;
	struct regmap *nb_pm_base, *avs_base;
	struct device *cpu_dev;
	int load_lvl, ret;
	struct clk *clk;
@@ -227,6 +371,14 @@ static int __init armada37xx_cpufreq_driver_init(void)
	if (IS_ERR(nb_pm_base))
		return -ENODEV;

	avs_base =
		syscon_regmap_lookup_by_compatible("marvell,armada-3700-avs");

	/* if AVS is not present don't use it but still try to setup dvfs */
	if (IS_ERR(avs_base)) {
		pr_info("Syscon failed for Adapting Voltage Scaling: skip it\n");
		avs_base = NULL;
	}
	/* Before doing any configuration on the DVFS first, disable it */
	armada37xx_cpufreq_disable_dvfs(nb_pm_base);

@@ -270,16 +422,21 @@ static int __init armada37xx_cpufreq_driver_init(void)

	armada37xx_cpufreq_state->regmap = nb_pm_base;

	armada37xx_cpufreq_avs_configure(avs_base, dvfs);
	armada37xx_cpufreq_avs_setup(avs_base, dvfs);

	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 u_volt = avs_map[dvfs->avs[load_lvl]] * 1000;
		freq = cur_frequency / dvfs->divider[load_lvl];

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


	}

	/* Now that everything is setup, enable the DVFS at hardware level */
+52 −0
Original line number Diff line number Diff line
@@ -296,10 +296,62 @@ static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy)
	return ret;
}

static inline u64 get_delta(u64 t1, u64 t0)
{
	if (t1 > t0 || t0 > ~(u32)0)
		return t1 - t0;

	return (u32)t1 - (u32)t0;
}

static int cppc_get_rate_from_fbctrs(struct cppc_cpudata *cpu,
				     struct cppc_perf_fb_ctrs fb_ctrs_t0,
				     struct cppc_perf_fb_ctrs fb_ctrs_t1)
{
	u64 delta_reference, delta_delivered;
	u64 reference_perf, delivered_perf;

	reference_perf = fb_ctrs_t0.reference_perf;

	delta_reference = get_delta(fb_ctrs_t1.reference,
				    fb_ctrs_t0.reference);
	delta_delivered = get_delta(fb_ctrs_t1.delivered,
				    fb_ctrs_t0.delivered);

	/* Check to avoid divide-by zero */
	if (delta_reference || delta_delivered)
		delivered_perf = (reference_perf * delta_delivered) /
					delta_reference;
	else
		delivered_perf = cpu->perf_ctrls.desired_perf;

	return cppc_cpufreq_perf_to_khz(cpu, delivered_perf);
}

static unsigned int cppc_cpufreq_get_rate(unsigned int cpunum)
{
	struct cppc_perf_fb_ctrs fb_ctrs_t0 = {0}, fb_ctrs_t1 = {0};
	struct cppc_cpudata *cpu = all_cpu_data[cpunum];
	int ret;

	ret = cppc_get_perf_ctrs(cpunum, &fb_ctrs_t0);
	if (ret)
		return ret;

	udelay(2); /* 2usec delay between sampling */

	ret = cppc_get_perf_ctrs(cpunum, &fb_ctrs_t1);
	if (ret)
		return ret;

	return cppc_get_rate_from_fbctrs(cpu, fb_ctrs_t0, fb_ctrs_t1);
}

static struct cpufreq_driver cppc_cpufreq_driver = {
	.flags = CPUFREQ_CONST_LOOPS,
	.verify = cppc_verify_policy,
	.target = cppc_cpufreq_set_target,
	.get = cppc_cpufreq_get_rate,
	.init = cppc_cpufreq_cpu_init,
	.stop_cpu = cppc_cpufreq_stop_cpu,
	.name = "cppc_cpufreq",
+7 −1
Original line number Diff line number Diff line
@@ -923,7 +923,12 @@ static ssize_t store(struct kobject *kobj, struct attribute *attr,
	struct freq_attr *fattr = to_attr(attr);
	ssize_t ret = -EINVAL;

	cpus_read_lock();
	/*
	 * cpus_read_trylock() is used here to work around a circular lock
	 * dependency problem with respect to the cpufreq_register_driver().
	 */
	if (!cpus_read_trylock())
		return -EBUSY;

	if (cpu_online(policy->cpu)) {
		down_write(&policy->rwsem);
@@ -2236,6 +2241,7 @@ static int cpufreq_set_policy(struct cpufreq_policy *policy,

	policy->min = new_policy->min;
	policy->max = new_policy->max;
	trace_cpu_frequency_limits(policy);

	policy->cached_target_freq = UINT_MAX;

Loading