linux-arm-kernel.lists.infradead.org archive mirror
 help / color / mirror / Atom feed
From: Stefan Wahren <stefan.wahren@i2se.com>
To: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>,
	Florian Fainelli <f.fainelli@gmail.com>,
	Ray Jui <rjui@broadcom.com>,
	Scott Branden <sbranden@broadcom.com>,
	bcm-kernel-feedback-list@broadcom.com,
	Eric Anholt <eric@anholt.net>
Cc: linux-pm@vger.kernel.org, sboyd@kernel.org,
	viresh.kumar@linaro.org, mturquette@baylibre.com,
	ptesarik@suse.com, rjw@rjwysocki.net,
	linux-kernel@vger.kernel.org, mbrugger@suse.de,
	linux-rpi-kernel@lists.infradead.org, linux-clk@vger.kernel.org,
	linux-arm-kernel@lists.infradead.org, ssuloev@orpaltech.com
Subject: Re: [RFC v2 3/5] clk: bcm2835: use firmware interface to update pllb
Date: Mon, 20 May 2019 14:11:54 +0200	[thread overview]
Message-ID: <ebc78880-418f-f507-021c-41295113e041@i2se.com> (raw)
In-Reply-To: <20190520104708.11980-4-nsaenzjulienne@suse.de>

Hi Nicolas,

the following comments applies only in case Eric is fine with the whole
approach.

On 20.05.19 12:47, Nicolas Saenz Julienne wrote:
> Raspberry Pi's firmware, which runs in a dedicated processor, keeps
maybe we should clarify that the firmware is running in the VPU
> track of the board's temperature and voltage. It's resposible for
> scaling the CPU frequency whenever it deems the device reached an unsafe
> state. On top of that the firmware provides an interface which allows
> Linux to to query the clock's state or change it's frequency.
I think this requires a separate update of the devicetree binding.
>
> Being the sole user of the bcm2835 clock driver, this integrates the
> firmware interface into the clock driver and adds a first user: the CPU
> pll, also known as 'pllb'.

Please verify that the kernel still works (and this clock driver probe)
under the following conditions:

- CONFIG_RASPBERRYPI_FIRMWARE=n
- CONFIG_RASPBERRYPI_FIRMWARE=m
- older DTBs without patch #1

>
> Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
> ---
>  drivers/clk/bcm/clk-bcm2835.c | 274 ++++++++++++++++++++++++++++++++--
>  1 file changed, 259 insertions(+), 15 deletions(-)
>
> diff --git a/drivers/clk/bcm/clk-bcm2835.c b/drivers/clk/bcm/clk-bcm2835.c
> index 5aea110672f3..ce5b16f3704e 100644
> --- a/drivers/clk/bcm/clk-bcm2835.c
> +++ b/drivers/clk/bcm/clk-bcm2835.c
> @@ -35,6 +35,7 @@
>  #include <linux/platform_device.h>
>  #include <linux/slab.h>
>  #include <dt-bindings/clock/bcm2835.h>
> +#include <soc/bcm2835/raspberrypi-firmware.h>
>  
>  #define CM_PASSWORD		0x5a000000
>  
> @@ -289,6 +290,30 @@
>  #define LOCK_TIMEOUT_NS		100000000
>  #define BCM2835_MAX_FB_RATE	1750000000u
>  
> +#define RPI_FIRMWARE_ARM_CLK_ID		0x000000003
> +#define RPI_FIRMWARE_STATE_ENABLE_BIT	0x1
> +#define RPI_FIRMWARE_STATE_WAIT_BIT	0x2
> +
> +/*
> + * Structure of the message passed to Raspberry Pi's firmware in order to
> + * change clock rates. The 'disable_turbo' option is only available to the ARM
> + * clock (pllb) which we enable by default as turbo mode will alter multiple
> + * clocks at once.
> + *
> + * Even though we're able to access the clock registers directly we're bound to
> + * use the firmware interface as the firmware ultimately takes care of
> + * mitigating overheating/undervoltage situations and we would be changing
> + * frequencies behind his back.
> + *
> + * For more information on the firmware interface check:
> + * https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface
> + */
> +struct bcm2835_firmware_prop {
> +       u32 id;
> +       u32 val;
> +       u32 disable_turbo;
> +} __packed;
> +
>  /*
>   * Names of clocks used within the driver that need to be replaced
>   * with an external parent's name.  This array is in the order that
> @@ -316,6 +341,8 @@ struct bcm2835_cprman {
>  	 */
>  	const char *real_parent_names[ARRAY_SIZE(cprman_parent_names)];
>  
> +	struct rpi_firmware *firmware;
> +
>  	/* Must be last */
>  	struct clk_hw_onecell_data onecell;
>  };
> @@ -424,6 +451,23 @@ struct bcm2835_pll_data {
>  	unsigned long max_fb_rate;
>  };
>  
> +struct bcm2835_fw_pll_data {
> +	const char *name;
> +	int firmware_clk_id;
> +	u32 flags;
> +
> +	unsigned long min_rate;
> +	unsigned long max_rate;
> +
> +	/*
> +	 * The rate provided to the firmware interface might not always match
> +	 * the clock subsystem's. For instance, in the case of the ARM cpu
> +	 * clock, even though the firmware ultimately edits 'pllb' it takes the
> +	 * expected 'pllb_arm' rate as it's input.
> +	 */
> +	unsigned int rate_rescale;
> +};
> +
>  struct bcm2835_pll_ana_bits {
>  	u32 mask0;
>  	u32 set0;
> @@ -505,6 +549,7 @@ struct bcm2835_pll {
>  	struct clk_hw hw;
>  	struct bcm2835_cprman *cprman;
>  	const struct bcm2835_pll_data *data;
> +	const struct bcm2835_fw_pll_data *fw_data;
>  };
>  
>  static int bcm2835_pll_is_on(struct clk_hw *hw)
> @@ -517,6 +562,41 @@ static int bcm2835_pll_is_on(struct clk_hw *hw)
>  		A2W_PLL_CTRL_PRST_DISABLE;
>  }
>  
> +static int raspberrypi_clock_property(struct rpi_firmware *firmware, u32 tag,
> +				      u32 clk, u32 *val)
> +{
> +	int ret;
> +	struct bcm2835_firmware_prop msg = {
> +		.id = clk,
> +		.val = *val,
> +		.disable_turbo = 1,
> +	};
> +
> +	ret = rpi_firmware_property(firmware, tag, &msg, sizeof(msg));
> +	if (ret)
> +		return ret;
> +
> +	*val = msg.val;
> +
> +	return 0;
> +}
> +
> +static int bcm2835_fw_pll_is_on(struct clk_hw *hw)
> +{
> +	struct bcm2835_pll *pll = container_of(hw, struct bcm2835_pll, hw);
> +	struct bcm2835_cprman *cprman = pll->cprman;
> +	u32 val = 0;
> +	int ret;
> +
> +	ret = raspberrypi_clock_property(cprman->firmware,
> +					 RPI_FIRMWARE_GET_CLOCK_STATE,
> +					 pll->fw_data->firmware_clk_id, &val);
> +	if (ret)
> +		return 0;
> +
> +	return !!(val & RPI_FIRMWARE_STATE_ENABLE_BIT);
> +}
> +
>  static void bcm2835_pll_choose_ndiv_and_fdiv(unsigned long rate,
>  					     unsigned long parent_rate,
>  					     u32 *ndiv, u32 *fdiv)
> @@ -547,16 +627,37 @@ static long bcm2835_pll_round_rate(struct clk_hw *hw, unsigned long rate,
>  				   unsigned long *parent_rate)
>  {
>  	struct bcm2835_pll *pll = container_of(hw, struct bcm2835_pll, hw);
> -	const struct bcm2835_pll_data *data = pll->data;
>  	u32 ndiv, fdiv;
>  
> -	rate = clamp(rate, data->min_rate, data->max_rate);
> +	if (pll->data)
> +		rate = clamp(rate, pll->data->min_rate, pll->data->max_rate);
> +	else
> +		rate = clamp(rate, pll->fw_data->min_rate,
> +			     pll->fw_data->max_rate);
>  
>  	bcm2835_pll_choose_ndiv_and_fdiv(rate, *parent_rate, &ndiv, &fdiv);
>  
>  	return bcm2835_pll_rate_from_divisors(*parent_rate, ndiv, fdiv, 1);
>  }
>  
> +static unsigned long bcm2835_fw_pll_get_rate(struct clk_hw *hw,
> +					     unsigned long parent_rate)
> +{
> +	struct bcm2835_pll *pll = container_of(hw, struct bcm2835_pll, hw);
> +	struct bcm2835_cprman *cprman = pll->cprman;
> +	u32 val = 0;
> +	int ret;
> +
> +	ret = raspberrypi_clock_property(cprman->firmware,
> +					 RPI_FIRMWARE_GET_CLOCK_RATE,
> +					 pll->fw_data->firmware_clk_id,
> +					 &val);
> +	if (ret)
> +		return ret;
> +
> +	return val * pll->fw_data->rate_rescale;
> +}
> +
>  static unsigned long bcm2835_pll_get_rate(struct clk_hw *hw,
>  					  unsigned long parent_rate)
>  {
> @@ -584,6 +685,35 @@ static unsigned long bcm2835_pll_get_rate(struct clk_hw *hw,
>  	return bcm2835_pll_rate_from_divisors(parent_rate, ndiv, fdiv, pdiv);
>  }
>  
> +static int bcm2835_fw_pll_on(struct clk_hw *hw)
> +{
> +	struct bcm2835_pll *pll = container_of(hw, struct bcm2835_pll, hw);
> +	struct bcm2835_cprman *cprman = pll->cprman;
> +	u32 val;
> +	int ret;
> +
> +	val = RPI_FIRMWARE_STATE_ENABLE_BIT | RPI_FIRMWARE_STATE_WAIT_BIT;
> +
> +	ret = raspberrypi_clock_property(cprman->firmware,
> +					 RPI_FIRMWARE_SET_CLOCK_STATE,
> +					 pll->fw_data->firmware_clk_id, &val);
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +static void bcm2835_fw_pll_off(struct clk_hw *hw)
> +{
> +	struct bcm2835_pll *pll = container_of(hw, struct bcm2835_pll, hw);
> +	struct bcm2835_cprman *cprman = pll->cprman;
> +	u32 val = RPI_FIRMWARE_STATE_WAIT_BIT;
> +
> +	raspberrypi_clock_property(cprman->firmware,
> +				   RPI_FIRMWARE_SET_CLOCK_STATE,
> +				   pll->fw_data->firmware_clk_id, &val);
> +}
> +
>  static void bcm2835_pll_off(struct clk_hw *hw)
>  {
>  	struct bcm2835_pll *pll = container_of(hw, struct bcm2835_pll, hw);
> @@ -651,6 +781,25 @@ bcm2835_pll_write_ana(struct bcm2835_cprman *cprman, u32 ana_reg_base, u32 *ana)
>  		cprman_write(cprman, ana_reg_base + i * 4, ana[i]);
>  }
>  
> +static int bcm2835_fw_pll_set_rate(struct clk_hw *hw, unsigned long rate,
> +				   unsigned long parent_rate)
> +{
> +	struct bcm2835_pll *pll = container_of(hw, struct bcm2835_pll, hw);
> +	u32 new_rate = rate / pll->fw_data->rate_rescale;
> +	struct bcm2835_cprman *cprman = pll->cprman;
> +	int ret;
> +
> +	ret = raspberrypi_clock_property(cprman->firmware,
> +					 RPI_FIRMWARE_SET_CLOCK_RATE,
> +					 pll->fw_data->firmware_clk_id,
> +					 &new_rate);
> +	if (ret)
> +		dev_err(cprman->dev, "Failed to change %s frequency: %d",
> +			clk_hw_get_name(hw), ret);
I think we should print this error only once or at least rate limited.
> +
> +	return ret;
> +}
> +
>  static int bcm2835_pll_set_rate(struct clk_hw *hw,
>  				unsigned long rate, unsigned long parent_rate)
>  {
> @@ -759,6 +908,15 @@ static const struct clk_ops bcm2835_pll_clk_ops = {
>  	.debug_init = bcm2835_pll_debug_init,
>  };
>  
> +static const struct clk_ops bcm2835_firmware_pll_clk_ops = {
> +	.is_prepared = bcm2835_fw_pll_is_on,
> +	.prepare = bcm2835_fw_pll_on,
> +	.unprepare = bcm2835_fw_pll_off,
> +	.recalc_rate = bcm2835_fw_pll_get_rate,
> +	.set_rate = bcm2835_fw_pll_set_rate,
> +	.round_rate = bcm2835_pll_round_rate,
> +};
> +
>  struct bcm2835_pll_divider {
>  	struct clk_divider div;
>  	struct bcm2835_cprman *cprman;
> @@ -1287,6 +1445,83 @@ static const struct clk_ops bcm2835_vpu_clock_clk_ops = {
>  	.debug_init = bcm2835_clock_debug_init,
>  };
>  
> +static int bcm2835_firmware_get_min_max_rates(struct bcm2835_cprman *cprman,
> +					      struct bcm2835_fw_pll_data *data)
> +{
> +	u32 min_rate = 0, max_rate = 0;
> +	int ret;
> +
> +	ret = raspberrypi_clock_property(cprman->firmware,
> +					 RPI_FIRMWARE_GET_MIN_CLOCK_RATE,
> +					 data->firmware_clk_id,
> +					 &min_rate);
> +	if (ret) {
> +		dev_err(cprman->dev, "Failed to get %s min freq: %d\n",
> +			data->name, ret);
> +		return ret;
> +	}
> +
> +	ret = raspberrypi_clock_property(cprman->firmware,
> +					 RPI_FIRMWARE_GET_MAX_CLOCK_RATE,
> +					 data->firmware_clk_id,
> +					 &max_rate);
> +	if (ret) {
> +		dev_err(cprman->dev, "Failed to get %s max freq: %d\n",
> +			data->name, ret);
> +		return ret;
> +	}
> +
> +	data->min_rate = min_rate * data->rate_rescale;
> +	data->max_rate = max_rate * data->rate_rescale;
> +
> +	dev_info(cprman->dev, "min %lu, max %lu\n", data->min_rate, data->max_rate);
> +	return 0;
> +}
> +
> +static struct clk_hw *bcm2835_register_fw_pll(struct bcm2835_cprman *cprman,
> +					const struct bcm2835_fw_pll_data *data)
> +{
> +	struct bcm2835_fw_pll_data *fw_data;
> +	struct clk_init_data init;
> +	struct bcm2835_pll *pll;
> +	int ret;
> +
> +	memset(&init, 0, sizeof(init));
> +
> +	/* All of the PLLs derive from the external oscillator. */
> +	init.parent_names = &cprman->real_parent_names[0];
> +	init.num_parents = 1;
> +	init.name = data->name;
> +	init.ops = &bcm2835_firmware_pll_clk_ops;
> +	init.flags = data->flags | CLK_IGNORE_UNUSED;
> +
> +	pll = kzalloc(sizeof(*pll), GFP_KERNEL);
> +	if (!pll)
> +		return NULL;
> +
> +	/*
> +	 * As the max and min rate values are firmware dependent we need a non
> +	 * constant version of struct bcm2835_fw_pll_data.
> +	 */
> +	fw_data = devm_kmemdup(cprman->dev, data, sizeof(*data),
> +				     GFP_KERNEL);
> +	if (!fw_data)
> +		return NULL;
> +
> +	ret = bcm2835_firmware_get_min_max_rates(cprman, fw_data);
> +	if (ret)
> +		return NULL;
> +
> +	pll->cprman = cprman;
> +	pll->hw.init = &init;
> +	pll->fw_data = fw_data;
> +
> +	ret = devm_clk_hw_register(cprman->dev, &pll->hw);
> +	if (ret)
> +		return NULL;
> +	return &pll->hw;
> +}
> +
>  static struct clk_hw *bcm2835_register_pll(struct bcm2835_cprman *cprman,
>  					   const struct bcm2835_pll_data *data)
>  {
> @@ -1462,6 +1697,9 @@ struct bcm2835_clk_desc {
>  #define REGISTER_PLL(...)	_REGISTER(&bcm2835_register_pll,	\
>  					  &(struct bcm2835_pll_data)	\
>  					  {__VA_ARGS__})
> +#define REGISTER_FW_PLL(...)	_REGISTER(&bcm2835_register_fw_pll,	\
> +					  &(struct bcm2835_fw_pll_data)	\
> +					  {__VA_ARGS__})
>  #define REGISTER_PLL_DIV(...)	_REGISTER(&bcm2835_register_pll_divider, \
>  					  &(struct bcm2835_pll_divider_data) \
>  					  {__VA_ARGS__})
> @@ -1654,21 +1892,11 @@ static const struct bcm2835_clk_desc clk_desc_array[] = {
>  		.flags = CLK_SET_RATE_PARENT),
>  
>  	/* PLLB is used for the ARM's clock. */
> -	[BCM2835_PLLB]		= REGISTER_PLL(
> +	[BCM2835_PLLB]		= REGISTER_FW_PLL(
>  		.name = "pllb",
> -		.cm_ctrl_reg = CM_PLLB,
> -		.a2w_ctrl_reg = A2W_PLLB_CTRL,
> -		.frac_reg = A2W_PLLB_FRAC,
> -		.ana_reg_base = A2W_PLLB_ANA0,
> -		.reference_enable_mask = A2W_XOSC_CTRL_PLLB_ENABLE,
> -		.lock_mask = CM_LOCK_FLOCKB,
>  		.flags = CLK_GET_RATE_NOCACHE,
> -
> -		.ana = &bcm2835_ana_default,
> -
> -		.min_rate = 600000000u,
> -		.max_rate = 3000000000u,
> -		.max_fb_rate = BCM2835_MAX_FB_RATE),
> +		.rate_rescale = 2,
> +		.firmware_clk_id = RPI_FIRMWARE_ARM_CLK_ID),
>  	[BCM2835_PLLB_ARM]	= REGISTER_PLL_DIV(
>  		.name = "pllb_arm",
>  		.source_pll = "pllb",
> @@ -2133,9 +2361,24 @@ static int bcm2835_clk_probe(struct platform_device *pdev)
>  	struct resource *res;
>  	const struct bcm2835_clk_desc *desc;
>  	const size_t asize = ARRAY_SIZE(clk_desc_array);
> +	struct device_node *firmware_node;
> +	struct rpi_firmware *firmware;
>  	size_t i;
>  	int ret;
>  
> +	firmware_node = of_find_node_by_name(NULL, "firmware");
I prefer of_find_compatible_node() which is more specific.
> +	if (!firmware_node) {
> +		dev_err(dev, "Missing firmware node\n");
> +		return -ENOENT;
> +	}
The firmware node should be optional for the driver.
> +
> +	firmware = rpi_firmware_get(firmware_node);
> +	of_node_put(firmware_node);
> +	if (!firmware) {
> +		dev_err(dev, "Can't get raspberrypi's firmware\n");

For in case the driver for the Raspberry Pifirmware is enabled, we
shouldn't spam with errors here.

Stefan


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

  reply	other threads:[~2019-05-20 12:12 UTC|newest]

Thread overview: 24+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-05-20 10:47 [RFC v2 0/5] cpufreq support for the Raspberry Pi Nicolas Saenz Julienne
2019-05-20 10:47 ` [RFC v2 1/5] clk: bcm2835: set CLK_GET_RATE_NOCACHE on CPU clocks Nicolas Saenz Julienne
2019-05-20 11:42   ` Stefan Wahren
2019-05-20 10:47 ` [RFC v2 2/5] clk: bcm2835: set pllb_arm divisor as readonly Nicolas Saenz Julienne
2019-05-20 11:43   ` Stefan Wahren
2019-05-20 10:47 ` [RFC v2 3/5] clk: bcm2835: use firmware interface to update pllb Nicolas Saenz Julienne
2019-05-20 12:11   ` Stefan Wahren [this message]
2019-05-20 12:14     ` Stefan Wahren
2019-05-21 12:40     ` Stefan Wahren
2019-05-21 15:47       ` Nicolas Saenz Julienne
2019-05-21 21:43         ` Stefan Wahren
2019-05-23  8:51           ` Nicolas Saenz Julienne
2019-05-20 12:43   ` Oliver Neukum
2019-05-21 11:39     ` Nicolas Saenz Julienne
2019-05-21 12:14       ` Petr Tesarik
2019-05-21 12:18         ` Nicolas Saenz Julienne
2019-05-20 10:47 ` [RFC v2 4/5] dts: bcm2837: add per-cpu clock devices Nicolas Saenz Julienne
2019-05-20 12:19   ` Stefan Wahren
2019-05-21 11:40     ` Nicolas Saenz Julienne
2019-05-20 10:47 ` [RFC v2 5/5] cpufreq: add driver for Raspbery Pi Nicolas Saenz Julienne
2019-05-20 10:51   ` Viresh Kumar
2019-05-20 12:30   ` Stefan Wahren
2019-05-20 10:51 ` [RFC v2 0/5] cpufreq support for the Raspberry Pi Viresh Kumar
2019-05-21 12:02   ` Nicolas Saenz Julienne

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=ebc78880-418f-f507-021c-41295113e041@i2se.com \
    --to=stefan.wahren@i2se.com \
    --cc=bcm-kernel-feedback-list@broadcom.com \
    --cc=eric@anholt.net \
    --cc=f.fainelli@gmail.com \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-clk@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-pm@vger.kernel.org \
    --cc=linux-rpi-kernel@lists.infradead.org \
    --cc=mbrugger@suse.de \
    --cc=mturquette@baylibre.com \
    --cc=nsaenzjulienne@suse.de \
    --cc=ptesarik@suse.com \
    --cc=rjui@broadcom.com \
    --cc=rjw@rjwysocki.net \
    --cc=sboyd@kernel.org \
    --cc=sbranden@broadcom.com \
    --cc=ssuloev@orpaltech.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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).