All of lore.kernel.org
 help / color / mirror / Atom feed
From: Ley Foon Tan <ley.foon.tan@intel.com>
To: u-boot@lists.denx.de
Subject: [U-Boot] [PATCH] clk: socfpga: Add initial Arria10 clock driver
Date: Thu, 09 Aug 2018 16:39:49 +0800	[thread overview]
Message-ID: <1533803989.38452.11.camel@intel.com> (raw)
In-Reply-To: <20180808201256.2135-1-marex@denx.de>

On Wed, 2018-08-08 at 22:12 +0200, Marek Vasut wrote:
> Add clock driver for the Arria10, which allows reading the clock
> frequency from all the clock described in the DT. The driver also
> allows enabling and disabling the clock. Reconfiguring frequency
> is not supported thus far.
> 
> Since the DT bindings for the SoCFPGA clock are massively misdesigned
> and the handoff DT adds additional incorrectly described entries to
> the DT, the driver contains workarounds which attempt to rectify all
> of those problems.
> 
> Signed-off-by: Marek Vasut <marex@denx.de>
> Cc: Chin Liang See <chin.liang.see@intel.com>
> Cc: Dinh Nguyen <dinguyen@kernel.org>
> Cc: Ley Foon Tan <ley.foon.tan@intel.com>
> ---
>  drivers/clk/Makefile             |   1 +
>  drivers/clk/altera/Makefile      |   7 +
>  drivers/clk/altera/clk-arria10.c | 363
> +++++++++++++++++++++++++++++++++++++++
>  3 files changed, 371 insertions(+)
>  create mode 100644 drivers/clk/altera/Makefile
>  create mode 100644 drivers/clk/altera/clk-arria10.c
> 
> diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
> index 146283c723..034bf44078 100644
> --- a/drivers/clk/Makefile
> +++ b/drivers/clk/Makefile
> @@ -11,6 +11,7 @@ obj-y += tegra/
>  obj-$(CONFIG_ARCH_ASPEED) += aspeed/
>  obj-$(CONFIG_ARCH_MESON) += clk_meson.o
>  obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/
> +obj-$(CONFIG_ARCH_SOCFPGA) += altera/
>  obj-$(CONFIG_CLK_AT91) += at91/
>  obj-$(CONFIG_CLK_MVEBU) += mvebu/
>  obj-$(CONFIG_CLK_BCM6345) += clk_bcm6345.o
> diff --git a/drivers/clk/altera/Makefile
> b/drivers/clk/altera/Makefile
> new file mode 100644
> index 0000000000..2542b7f51b
> --- /dev/null
> +++ b/drivers/clk/altera/Makefile
> @@ -0,0 +1,7 @@
> +#
> +# Copyright (C) 2018 Marek Vasut <marex@denx.de>
> +#
> +# SPDX-License-Identifier:      GPL-2.0+
> +#
> +
> +obj-$(CONFIG_TARGET_SOCFPGA_ARRIA10) += clk-arria10.o
> diff --git a/drivers/clk/altera/clk-arria10.c
> b/drivers/clk/altera/clk-arria10.c
> new file mode 100644
> index 0000000000..78102c760d
> --- /dev/null
> +++ b/drivers/clk/altera/clk-arria10.c
> @@ -0,0 +1,363 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2018 Marek Vasut <marex@denx.de>
> + */
> +
> +#include <common.h>
> +#include <asm/io.h>
> +#include <clk-uclass.h>
> +#include <dm.h>
> +#include <dm/lists.h>
> +#include <dm/util.h>
> +
> +#include <asm/arch/clock_manager.h>
> +
> +enum socfpga_a10_clk_type {
> +	SOCFPGA_A10_CLK_MAIN_PLL,
> +	SOCFPGA_A10_CLK_PER_PLL,
> +	SOCFPGA_A10_CLK_PERIP_CLK,
> +	SOCFPGA_A10_CLK_GATE_CLK,
> +	SOCFPGA_A10_CLK_UNKNOWN_CLK,
> +};
> +
> +struct socfpga_a10_clk_platdata {
> +	enum socfpga_a10_clk_type type;
> +	struct clk_bulk	clks;
> +	u32		regs;
> +	/* Fixed divider */
> +	u16		fix_div;
> +	/* Control register */
> +	u16		ctl_reg;
> +	/* Divider register */
> +	u16		div_reg;
> +	u8		div_len;
> +	u8		div_off;
> +	/* Clock gating register */
> +	u16		gate_reg;
> +	u8		gate_bit;
> +};
> +
> +static int socfpga_a10_clk_get_upstream(struct clk *clk, struct clk
> **upclk)
> +{
> +	struct socfpga_a10_clk_platdata *plat =
> dev_get_platdata(clk->dev);
> +	u32 reg, maxval;
> +
> +	if (plat->clks.count == 0)
> +		return 0;
> +
> +	if (plat->clks.count == 1) {
> +		*upclk = &plat->clks.clks[0];
> +		return 0;
> +	}
> +
> +	if (!plat->ctl_reg) {
> +		dev_err(clk->dev, "Invalid control register\n");
> +		return -EINVAL;
> +	}
> +
> +	reg = readl(plat->regs + plat->ctl_reg);
> +
> +	/* Assume PLLs are ON for now */
> +	if (plat->type == SOCFPGA_A10_CLK_MAIN_PLL) {
> +		reg = (reg >> 8) & 0x3;
> +		maxval = 2;
> +	} else if (plat->type == SOCFPGA_A10_CLK_PER_PLL) {
> +		reg = (reg >> 8) & 0x3;
> +		maxval = 3;
> +	} else {
> +		reg = (reg >> 16) & 0x7;
> +		maxval = 4;
> +	}
> +
> +	if (reg > maxval) {
> +		dev_err(clk->dev, "Invalid clock source\n");
> +		return -EINVAL;
> +	}
> +
> +	*upclk = &plat->clks.clks[reg];
> +	return 0;
> +}
> +
> +static int socfpga_a10_clk_endisable(struct clk *clk, bool enable)
> +{
> +	struct socfpga_a10_clk_platdata *plat =
> dev_get_platdata(clk->dev);
> +	struct clk *upclk = NULL;
> +	int ret;
> +
> +	if (!enable && plat->gate_reg)
> +		clrbits_le32(plat->regs + plat->gate_reg, BIT(plat-
> >gate_bit));
> +
> +	ret = socfpga_a10_clk_get_upstream(clk, &upclk);
> +	if (ret)
> +		return ret;
> +
> +	if (upclk) {
> +		if (enable)
> +			clk_enable(upclk);
> +		else
> +			clk_disable(upclk);
> +	}
> +
> +	if (enable && plat->gate_reg)
> +		setbits_le32(plat->regs + plat->gate_reg, BIT(plat-
> >gate_bit));
> +
> +	return 0;
> +}
> +
> +static int socfpga_a10_clk_enable(struct clk *clk)
> +{
> +	return socfpga_a10_clk_endisable(clk, true);
> +}
> +
> +static int socfpga_a10_clk_disable(struct clk *clk)
> +{
> +	return socfpga_a10_clk_endisable(clk, false);
> +}
> +
> +static ulong socfpga_a10_clk_get_rate(struct clk *clk)
> +{
> +	struct socfpga_a10_clk_platdata *plat =
> dev_get_platdata(clk->dev);
> +	struct clk *upclk = NULL;
> +	ulong rate = 0, reg, numer, denom;
> +	int ret;
> +
> +	ret = socfpga_a10_clk_get_upstream(clk, &upclk);
> +	if (ret || !upclk)
> +		return 0;
> +
> +	rate = clk_get_rate(upclk);
> +
> +	if (plat->type == SOCFPGA_A10_CLK_MAIN_PLL) {
> +		reg = readl(plat->regs + plat->ctl_reg + 4);	
> /* VCO1 */
> +		numer = reg & CLKMGR_MAINPLL_VCO1_NUMER_MSK;
> +		denom = (reg >> CLKMGR_MAINPLL_VCO1_DENOM_LSB) &
> +			CLKMGR_MAINPLL_VCO1_DENOM_MSK;
> +
> +		rate /= denom + 1;
> +		rate *= numer + 1;
> +	} else if (plat->type == SOCFPGA_A10_CLK_PER_PLL) {
> +		reg = readl(plat->regs + plat->ctl_reg + 4);	
> /* VCO1 */
> +		numer = reg & CLKMGR_PERPLL_VCO1_NUMER_MSK;
> +		denom = (reg >> CLKMGR_PERPLL_VCO1_DENOM_LSB) &
> +			CLKMGR_PERPLL_VCO1_DENOM_MSK;
> +
> +		rate /= denom + 1;
> +		rate *= numer + 1;
> +	} else {
> +		rate /= plat->fix_div;
> +
> +		if (plat->fix_div == 1 && plat->ctl_reg) {
> +			reg = readl(plat->regs + plat->ctl_reg);
> +			reg &= 0x7ff;
> +			rate /= reg + 1;
> +		}
> +
> +		if (plat->div_reg) {
> +			reg = readl(plat->regs + plat->div_reg);
> +			reg >>= plat->div_off;
> +			reg &= (1 << plat->div_len) - 1;
> +			if (plat->type == SOCFPGA_A10_CLK_PERIP_CLK)
> +				rate /= reg + 1;
> +			if (plat->type == SOCFPGA_A10_CLK_GATE_CLK)
> +				rate /= 1 << reg;
> +		}
> +	}
> +
> +	return rate;
> +}
> +
> +static struct clk_ops socfpga_a10_clk_ops = {
> +	.enable		= socfpga_a10_clk_enable,
> +	.disable	= socfpga_a10_clk_disable,
> +	.get_rate	= socfpga_a10_clk_get_rate,
> +};
> +
> +/*
> + * This workaround tries to fix the massively broken generated
> "handoff" DT,
> + * which contains duplicate clock nodes without any connection to
> the clock
> + * manager DT node. Yet, those "handoff" DT nodes contain
> configuration of
> + * the fixed input clock of the Arria10 which are missing from the
> base DT
> + * for Arria10.
> + *
> + * This workaround sets up upstream clock for the fixed input clocks
> of the
> + * A10 described in the base DT such that they map to the fixed
> clock from
> + * the "handoff" DT. This does not fully match how the clock look on
> the
> + * A10, but it is the least intrusive way to fix this mess.
> + */
> +static void socfpga_a10_handoff_workaround(struct udevice *dev)
> +{
> +	struct socfpga_a10_clk_platdata *plat =
> dev_get_platdata(dev);
> +	const void *fdt = gd->fdt_blob;
> +	struct clk_bulk	*bulk = &plat->clks;
> +	int i, ret, offset = dev_of_offset(dev);
> +	static const char * const socfpga_a10_fixedclk_map[] = {
> +		"osc1", "altera_arria10_hps_eosc1",
> +		"cb_intosc_ls_clk",
> "altera_arria10_hps_cb_intosc_ls",
> +		"f2s_free_clk", "altera_arria10_hps_f2h_free",
> +	};
> +
> +	if (fdt_node_check_compatible(fdt, offset, "fixed-clock"))
> +		return;
> +
> +	for (i = 0; i < ARRAY_SIZE(socfpga_a10_fixedclk_map); i +=
> 2)
> +		if (!strcmp(dev->name, socfpga_a10_fixedclk_map[i]))
> +			break;
> +
> +	if (i == ARRAY_SIZE(socfpga_a10_fixedclk_map))
> +		return;
> +
> +	ret = uclass_get_device_by_name(UCLASS_CLK,
> +					socfpga_a10_fixedclk_map[i +
> 1], &dev);
> +	if (ret)
> +		return;
> +
> +	bulk->count = 1;
> +	bulk->clks = devm_kcalloc(dev, bulk->count,
> +				  sizeof(struct clk), GFP_KERNEL);
> +	if (!bulk->clks)
> +		return;
> +
> +	ret = clk_request(dev, &bulk->clks[0]);
> +	if (ret)
> +		free(bulk->clks);
> +}
> +
> +static int socfpga_a10_clk_bind(struct udevice *dev)
> +{
> +	const void *fdt = gd->fdt_blob;
> +	int offset = dev_of_offset(dev);
> +	bool pre_reloc_only = !(gd->flags & GD_FLG_RELOC);
> +	const char *name;
> +	int ret;
> +
> +	for (offset = fdt_first_subnode(fdt, offset);
> +	     offset > 0;
> +	     offset = fdt_next_subnode(fdt, offset)) {
> +		name = fdt_get_name(fdt, offset, NULL);
> +		if (!name)
> +			return -EINVAL;
> +
> +		if (!strcmp(name, "clocks")) {
> +			offset = fdt_first_subnode(fdt, offset);
> +			name = fdt_get_name(fdt, offset, NULL);
> +			if (!name)
> +				return -EINVAL;
> +		}
> +
> +		/* Filter out supported sub-clock */
> +		if (fdt_node_check_compatible(fdt, offset,
> +					      "altr,socfpga-a10-pll-
> clock") &&
> +		    fdt_node_check_compatible(fdt, offset,
> +					      "altr,socfpga-a10-
> perip-clk") &&
> +		    fdt_node_check_compatible(fdt, offset,
> +					      "altr,socfpga-a10-
> gate-clk") &&
> +		    fdt_node_check_compatible(fdt, offset, "fixed-
> clock"))
> +			continue;
> +
> +		if (pre_reloc_only && !dm_fdt_pre_reloc(fdt,
> offset))
> +			continue;
> +
> +		ret = device_bind_driver_to_node(dev, "clk-a10",
> name,
> +						 offset_to_ofnode(of
> fset),
> +						 NULL);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int socfpga_a10_clk_probe(struct udevice *dev)
> +{
> +	struct socfpga_a10_clk_platdata *plat =
> dev_get_platdata(dev);
> +	const void *fdt = gd->fdt_blob;
> +	int offset = dev_of_offset(dev);
> +
> +	clk_get_bulk(dev, &plat->clks);
> +
> +	socfpga_a10_handoff_workaround(dev);
> +
> +	if (!fdt_node_check_compatible(fdt, offset,
> +				       "altr,socfpga-a10-pll-
> clock")) {
> +		/* Main PLL has 3 upstream clock */
> +		if (plat->clks.count == 3)
> +			plat->type = SOCFPGA_A10_CLK_MAIN_PLL;
> +		else
> +			plat->type = SOCFPGA_A10_CLK_PER_PLL;
> +	} else if (!fdt_node_check_compatible(fdt, offset,
> +					      "altr,socfpga-a10-
> perip-clk")) {
> +		plat->type = SOCFPGA_A10_CLK_PERIP_CLK;
> +	} else if (!fdt_node_check_compatible(fdt, offset,
> +					      "altr,socfpga-a10-
> gate-clk")) {
> +		plat->type = SOCFPGA_A10_CLK_GATE_CLK;
> +	} else {
> +		plat->type = SOCFPGA_A10_CLK_UNKNOWN_CLK;
> +	}
> +
> +	return 0;
> +}
> +
> +static int socfpga_a10_ofdata_to_platdata(struct udevice *dev)
> +{
> +	struct socfpga_a10_clk_platdata *plat =
> dev_get_platdata(dev);
> +	struct socfpga_a10_clk_platdata *pplat;
> +	struct udevice *pdev;
> +	const void *fdt = gd->fdt_blob;
> +	unsigned int divreg[3], gatereg[2];
> +	int ret, offset = dev_of_offset(dev);
> +	u32 regs;
> +
> +	regs = dev_read_u32_default(dev, "reg", 0x0);
> +
> +	if (!fdt_node_check_compatible(fdt, offset, "altr,clk-mgr")) 
> {
> +		plat->regs = devfdt_get_addr(dev);
> +	} else {
> +		pdev = dev_get_parent(dev);
> +		if (!pdev)
> +			return -ENODEV;
> +
> +		pplat = dev_get_platdata(pdev);
> +		if (!pplat)
> +			return -EINVAL;
> +
> +		plat->ctl_reg = regs;
> +		plat->regs = pplat->regs;
> +	}
> +
> +	plat->type = SOCFPGA_A10_CLK_UNKNOWN_CLK;
> +
> +	plat->fix_div = dev_read_u32_default(dev, "fixed-divider",
> 1);
> +
> +	ret = dev_read_u32_array(dev, "div-reg", divreg,
> ARRAY_SIZE(divreg));
> +	if (!ret) {
> +		plat->div_reg = divreg[0];
> +		plat->div_len = divreg[2];
> +		plat->div_off = divreg[1];
> +	}
> +
> +	ret = dev_read_u32_array(dev, "clk-gate", gatereg,
> ARRAY_SIZE(gatereg));
> +	if (!ret) {
> +		plat->gate_reg = gatereg[0];
> +		plat->gate_bit = gatereg[1];
> +	}
> +
> +	return 0;
> +}
> +
> +static const struct udevice_id socfpga_a10_clk_match[] = {
> +	{ .compatible = "altr,clk-mgr" },
> +	{}
> +};
> +
> +U_BOOT_DRIVER(socfpga_a10_clk) = {
> +	.name		= "clk-a10",
> +	.id		= UCLASS_CLK,
> +	.flags		= DM_FLAG_PRE_RELOC,
> +	.of_match	= socfpga_a10_clk_match,
> +	.ops		= &socfpga_a10_clk_ops,
> +	.bind		= socfpga_a10_clk_bind,
> +	.probe		= socfpga_a10_clk_probe,
> +	.ofdata_to_platdata = socfpga_a10_ofdata_to_platdata,
> +
> +	.platdata_auto_alloc_size = sizeof(struct
> socfpga_a10_clk_platdata),
> +};

Reviewed-by: Ley Foon Tan <ley.foon.tan@intel.com>

      reply	other threads:[~2018-08-09  8:39 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-08-08 20:12 [U-Boot] [PATCH] clk: socfpga: Add initial Arria10 clock driver Marek Vasut
2018-08-09  8:39 ` Ley Foon Tan [this message]

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=1533803989.38452.11.camel@intel.com \
    --to=ley.foon.tan@intel.com \
    --cc=u-boot@lists.denx.de \
    /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.