All of lore.kernel.org
 help / color / mirror / Atom feed
From: Gregory CLEMENT <gregory.clement@bootlin.com>
To: Viresh Kumar <viresh.kumar@linaro.org>
Cc: "Andrew Lunn" <andrew@lunn.ch>,
	"Jason Cooper" <jason@lakedaemon.net>,
	linux-pm@vger.kernel.org,
	"Antoine Tenart" <antoine.tenart@bootlin.com>,
	"Rafael J. Wysocki" <rjw@rjwysocki.net>,
	"Maxime Chevallier" <maxime.chevallier@bootlin.com>,
	"Thomas Petazzoni" <thomas.petazzoni@bootlin.com>,
	"Miquèl Raynal" <miquel.raynal@bootlin.com>,
	linux-arm-kernel@lists.infradead.org,
	"Sebastian Hesselbarth" <sebastian.hesselbarth@gmail.com>
Subject: Re: [PATCH v3 2/2] cpufreq: ap806: add cpufreq driver for Armada 8K
Date: Tue, 18 Dec 2018 15:32:28 +0100	[thread overview]
Message-ID: <87mup2hpmr.fsf@bootlin.com> (raw)
In-Reply-To: <20181217111026.7b3tds2yz4g7opdt@vireshk-i7> (Viresh Kumar's message of "Mon, 17 Dec 2018 16:40:26 +0530")

Hi Viresh,
 
 On lun., déc. 17 2018, Viresh Kumar <viresh.kumar@linaro.org> wrote:

> On 11-12-18, 17:42, Gregory CLEMENT wrote:
>> diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm
>> index 4e1131ef85ae..7e32a241760d 100644
>> --- a/drivers/cpufreq/Kconfig.arm
>> +++ b/drivers/cpufreq/Kconfig.arm
>> @@ -25,6 +25,17 @@ config ARM_ARMADA_37XX_CPUFREQ
>>  	  This adds the CPUFreq driver support for Marvell Armada 37xx SoCs.
>>  	  The Armada 37xx PMU supports 4 frequency and VDD levels.
>>  
>> +config ARM_ARMADA_8K_CPUFREQ
>> +	tristate "Armada 8K CPUFreq driver"
>> +	depends on ARCH_MVEBU && CPUFREQ_DT
>> +	help
>> +	  This enables the CPUFreq driver support for Marvell
>> +	  Armada8k SOCs.
>> +	  Armada8K device has the AP806 which supports scaling
>> +	  to any full integer divider.
>> +
>> +	  If in doubt, say N.
>> +
>>  # big LITTLE core layer and glue drivers
>>  config ARM_BIG_LITTLE_CPUFREQ
>>  	tristate "Generic ARM big LITTLE CPUfreq driver"
>> diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile
>> index d5ee4562ed06..db1564b610ac 100644
>> --- a/drivers/cpufreq/Makefile
>> +++ b/drivers/cpufreq/Makefile
>> @@ -50,6 +50,7 @@ obj-$(CONFIG_X86_SFI_CPUFREQ)		+= sfi-cpufreq.o
>>  obj-$(CONFIG_ARM_BIG_LITTLE_CPUFREQ)	+= arm_big_little.o
>>  
>>  obj-$(CONFIG_ARM_ARMADA_37XX_CPUFREQ)	+= armada-37xx-cpufreq.o
>> +obj-$(CONFIG_ARM_ARMADA_8K_CPUFREQ)	+= armada-8k-cpufreq.o
>>  obj-$(CONFIG_ARM_BRCMSTB_AVS_CPUFREQ)	+= brcmstb-avs-cpufreq.o
>>  obj-$(CONFIG_ACPI_CPPC_CPUFREQ)		+= cppc_cpufreq.o
>>  obj-$(CONFIG_ARCH_DAVINCI)		+= davinci-cpufreq.o
>> diff --git a/drivers/cpufreq/armada-8k-cpufreq.c b/drivers/cpufreq/armada-8k-cpufreq.c
>> new file mode 100644
>> index 000000000000..1db1953fb43e
>> --- /dev/null
>> +++ b/drivers/cpufreq/armada-8k-cpufreq.c
>> @@ -0,0 +1,186 @@
>> +// SPDX-License-Identifier: GPL-2.0+
>> +/*
>> + * CPUFreq support for Armada 8K
>> + *
>> + * Copyright (C) 2018 Marvell
>> + *
>> + * Omri Itach <omrii@marvell.com>
>> + * Gregory Clement <gregory.clement@bootlin.com>
>> + */
>> +
>> +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
>> +
>> +#include <linux/clk.h>
>> +#include <linux/cpu.h>
>> +#include <linux/err.h>
>> +#include <linux/init.h>
>> +#include <linux/kernel.h>
>> +#include <linux/module.h>
>> +#include <linux/of.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/pm_opp.h>
>> +#include <linux/slab.h>
>> +
>> +/*
>> + * Setup the opps list with the divider for the max frequency, that
>> + * will be filled at runtime.
>> + */
>> +static const int opps_div[] __initconst = {1, 2, 3, 4};
>> +
>> +static struct platform_device *armada_8k_pdev;
>> +static struct freq_table *freq_tables;
>> +static int opps_index;
>> +
>> +struct freq_table {
>> +	struct device *cpu_dev;
>> +	struct clk *clk;
>> +	unsigned int freq[ARRAY_SIZE(opps_div)];
>> +};
>> +
>> +/* If the CPUs share the same clock, then they are in the same cluster. */
>> +static void __init armada_8k_get_sharing_cpus(struct clk *cur_clk,
>> +					      struct cpumask *cpumask)
>> +{
>> +	int cpu;
>> +
>> +	for_each_possible_cpu(cpu) {
>> +		struct device *cpu_dev = get_cpu_device(cpu);
>
> What if this fails ?

cpu_dev will be NULL so the clk_get() will fail to find the clock which
is handled just after.

But if needed we can add this extra test and differed the clk_get.

>
>> +		struct clk *clk = clk_get(cpu_dev, 0);
>> +
>> +		if (IS_ERR(clk))
>> +			dev_warn(cpu_dev, "Cannot get clock for CPU %d\n", cpu);
>> +		else if (clk_is_match(clk, cur_clk))
>> +			cpumask_set_cpu(cpu, cpumask);
>> +
>> +		clk_put(clk);
>
> So you will do clk_put() even if clk_get() failed, that will trigger a WARN() if
> I am not wrong.

Yes there is a WARN_ONCE in this case, I can move the clk_put in the
else part.

>
>> +	}
>> +}
>> +
>> +static int armada_8k_add_opp(struct clk *clk, struct device *cpu_dev,
>> +			     struct freq_table *freq_tables, int opps_index)
>> +{
>> +	unsigned int cur_frequency;
>> +	unsigned int freq;
>> +	int i, ret;
>> +
>> +	/* Get nominal (current) CPU frequency. */
>> +	cur_frequency = clk_get_rate(clk);
>> +
>> +	if (!cur_frequency) {
>> +		dev_err(cpu_dev,
>> +			"Failed to get clock rate for this CPU\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	freq_tables[opps_index].cpu_dev = cpu_dev;
>> +	for (i = 0; i < ARRAY_SIZE(opps_div); i++) {
>> +		freq = cur_frequency / opps_div[i];
>> +
>> +		ret = dev_pm_opp_add(cpu_dev, freq, 0);
>> +		if (ret)
>> +			return ret;
>> +
>> +		freq_tables[opps_index].freq[i] = freq;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static void armada_8k_cpufreq_free_table(void)
>> +{
>> +	opps_index--;
>> +	for (; opps_index >= 0; opps_index--) {
>> +		int i;
>> +
>> +		for (i = 0; i < ARRAY_SIZE(opps_div); i++)
>> +			dev_pm_opp_remove(freq_tables[opps_index].cpu_dev,
>> +					  freq_tables[opps_index].freq[i]);
>> +	}
>> +
>> +	kfree(freq_tables);
>> +}
>> +
>> +static int __init armada_8k_cpufreq_init(void)
>> +{
>> +	int ret = 0, cpu, nb_cpus;
>> +	struct device_node *node;
>> +
>> +	node = of_find_compatible_node(NULL, NULL, "marvell,ap806-cpu-clock");
>> +	if (!node || !of_device_is_available(node))
>> +		return -ENODEV;
>> +
>> +	nb_cpus = num_possible_cpus();
>> +	freq_tables = kcalloc(nb_cpus, sizeof(*freq_tables), GFP_KERNEL);
>
> Add a blank line here.

OK

>
>> +	/*
>> +	 * For each CPU, this loop registers the operating points
>> +	 * supported (which are the nominal CPU frequency and full integer
>> +	 * divisions of it).
>> +	 */
>> +	for_each_possible_cpu(cpu) {
>> +		struct device *cpu_dev;
>> +		struct cpumask cpus;
>> +		struct clk *clk;
>> +		int i, skip;
>> +
>> +		skip = 0;
>> +		cpu_dev = get_cpu_device(cpu);
>> +
>> +		if (!cpu_dev) {
>> +			dev_err(cpu_dev, "Cannot get CPU %d\n", cpu);
>> +			continue;
>> +		}
>> +
>> +		clk = devm_clk_get(cpu_dev, 0);
>
> I don't think you should call devm_*() helpers on the cpu_dev pointer, this is
> not like the platform device structure. The problem is that no driver gets
> attached/detached from cpu_dev and so the resources may never get
> free.

OK

>
>> +
>> +		if (IS_ERR(clk)) {
>> +			dev_err(cpu_dev, "Cannot get clock for CPU %d\n", cpu);
>> +			ret = PTR_ERR(clk);
>> +			goto remove_opp;
>> +		}
>> +
>> +		for (i = 0; i < nb_cpus; i++) {
>> +			if (clk_is_match(clk, freq_tables[i].clk)) {
>> +				skip = 1;
>
> Why not do devm_clk_put() from right here and "continue" after that. That way
> you can avoid "skip" variable as well.

We need to keep a reference to the clk when it used later in the loop.

> Also why are you doing this clk-match at all ? This looks similar to what
> armada_8k_get_sharing_cpus() is doing. You can optimize this stuff better I
> believe.
>
> I will do this:
>
> Create a cpumask variable and assign possible_cpus mask to it. And then after
> each iteration of this loop, I clear "cpus" (returned from
> armada_8k_get_sharing_cpus()) from the CPUs from that cpumask. That way the loop
> will never try the second CPU from the same cpumask again.

I will try it

>
>> +				break;
>> +			}
>> +		}
>> +		if (skip) {
>> +			devm_clk_put(cpu_dev, clk);
>> +			continue;
>> +		}
>> +
>> +		freq_tables[opps_index].clk = clk;
>> +
>> +		ret = armada_8k_add_opp(clk, cpu_dev, freq_tables, opps_index);
>
> And this opps_index thing is not very clean. Having a global variable this way
> makes things very messy, which is being updated from several routines.

I think you missed the fact that no variable are passed to the exit
function but we still need to have a reference on the OPP to free, so we
cant do without this global variable. However I could reduce the amount
by hiding them in a platform data associated to the platform_device.

>
> So if armada_8k_add_opp() fails after adding few OPPs, we will now call
> armada_8k_cpufreq_free_table() which will first do opps_index-- and so you may
> miss freeing the OPPs added during the last call to armada_8k_add_opp().
>

Actually opps_index is only modify after adding succefully an opp, so
an OPP can't be missed.


> This is very messy and requires serious cleanup to be honest. Also it will be
> more preferred if armada_8k_add_opp() cleans up all the allocations it has done
> on a call if it fails in the middle of it, rather than a bigger routine freeing
> both successful and failed ones.

OK we can duplicate code for this if you prefer.


Thanks,

Gregory

>
>> +		if (ret)
>> +			goto remove_opp;
>> +
>> +		opps_index++;
>> +		cpumask_clear(&cpus);
>> +		armada_8k_get_sharing_cpus(clk, &cpus);
>> +		dev_pm_opp_set_sharing_cpus(cpu_dev, &cpus);
>> +	}
>> +
>> +	armada_8k_pdev = platform_device_register_simple("cpufreq-dt", -1,
>> +							 NULL, 0);
>> +	ret = PTR_ERR_OR_ZERO(armada_8k_pdev);
>> +	if (ret)
>> +		goto remove_opp;
>> +	return 0;
>> +
>> +remove_opp:
>> +	armada_8k_cpufreq_free_table();
>> +	return ret;
>> +}
>> +module_init(armada_8k_cpufreq_init);
>> +
>> +static void __exit armada_8k_cpufreq_exit(void)
>> +{
>> +	armada_8k_cpufreq_free_table();
>> +	platform_device_unregister(armada_8k_pdev);
>> +}
>> +module_exit(armada_8k_cpufreq_exit);
>> +
>> +MODULE_AUTHOR("Gregory Clement <gregory.clement@bootlin.com>");
>> +MODULE_DESCRIPTION("Armada 8K cpufreq driver");
>> +MODULE_LICENSE("GPL");
>> -- 
>> 2.19.2
>
> -- 
> viresh

-- 
Gregory Clement, Bootlin
Embedded Linux and Kernel engineering
http://bootlin.com

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

  reply	other threads:[~2018-12-18 14:32 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-12-11 16:42 [PATCH v3 0/2] Add cpufreq support for Armada 8K Gregory CLEMENT
2018-12-11 16:42 ` Gregory CLEMENT
2018-12-11 16:42 ` [PATCH v3 1/2] MAINTAINERS: add new entries for Armada 8K cpufreq driver Gregory CLEMENT
2018-12-11 16:42   ` Gregory CLEMENT
2018-12-11 16:42 ` [PATCH v3 2/2] cpufreq: ap806: add cpufreq driver for Armada 8K Gregory CLEMENT
2018-12-11 16:42   ` Gregory CLEMENT
2018-12-17 11:10   ` Viresh Kumar
2018-12-17 11:10     ` Viresh Kumar
2018-12-18 14:32     ` Gregory CLEMENT [this message]
2018-12-19  4:40       ` Viresh Kumar
2018-12-19  4:40         ` Viresh Kumar

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=87mup2hpmr.fsf@bootlin.com \
    --to=gregory.clement@bootlin.com \
    --cc=andrew@lunn.ch \
    --cc=antoine.tenart@bootlin.com \
    --cc=jason@lakedaemon.net \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-pm@vger.kernel.org \
    --cc=maxime.chevallier@bootlin.com \
    --cc=miquel.raynal@bootlin.com \
    --cc=rjw@rjwysocki.net \
    --cc=sebastian.hesselbarth@gmail.com \
    --cc=thomas.petazzoni@bootlin.com \
    --cc=viresh.kumar@linaro.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.