[U-Boot] [PATCH v1 14/19] driver: clk: Add support for clocks on Armada 37xx

Stefan Roese sr at denx.de
Wed Mar 21 09:30:53 UTC 2018


On 07.03.2018 22:52, Marek Behún wrote:
> The drivers are based on Linux driver by Gregory Clement.
> 
> The TBG clocks support only the .get_rate method.
>    - since setting rate is not supported, the driver computes the rates
>      when probing and so subsequent calls to the .get_rate method do not
>      read the corresponding registers again
> 
> The peripheral clocks support methods .get_rate, .enable and .disable.
> 
>    - the .set_parent method theoretically could be supported on some clocks
>      (the parent would have to be one of the TBG clocks)
> 
>    - the .set_rate method would have to try all the divider values to find
>      the best approximation of a given rate, and it doesn't seem like
>      this should be needed in U-Boot, therefore not implemented
> 
> Signed-off-by: Marek Behun <marek.behun at nic.cz>

Looks good, so:

Reviewed-by: Stefan Roese <sr at denx.de>

Thanks,
Stefan

> ---
>   arch/arm/dts/armada-37xx.dtsi          |  20 ++
>   drivers/clk/Kconfig                    |   1 +
>   drivers/clk/Makefile                   |   1 +
>   drivers/clk/mvebu/Kconfig              |  11 +
>   drivers/clk/mvebu/Makefile             |   1 +
>   drivers/clk/mvebu/armada-37xx-periph.c | 464 +++++++++++++++++++++++++++++++++
>   drivers/clk/mvebu/armada-37xx-tbg.c    | 153 +++++++++++
>   7 files changed, 651 insertions(+)
> 
> diff --git a/arch/arm/dts/armada-37xx.dtsi b/arch/arm/dts/armada-37xx.dtsi
> index d0529637f4..e848812fca 100644
> --- a/arch/arm/dts/armada-37xx.dtsi
> +++ b/arch/arm/dts/armada-37xx.dtsi
> @@ -106,6 +106,26 @@
>   				status = "disabled";
>   			};
>   
> +			nb_periph_clk: nb-periph-clk at 13000 {
> +				compatible = "marvell,armada-3700-periph-clock-nb";
> +				reg = <0x13000 0x100>;
> +				clocks = <&tbg 0>, <&tbg 1>, <&tbg 2>, <&tbg 3>;
> +				#clock-cells = <1>;
> +			};
> +
> +			sb_periph_clk: sb-periph-clk at 18000 {
> +				compatible = "marvell,armada-3700-periph-clock-sb";
> +				reg = <0x18000 0x100>;
> +				clocks = <&tbg 0>, <&tbg 1>, <&tbg 2>, <&tbg 3>;
> +				#clock-cells = <1>;
> +			};
> +
> +			tbg: tbg at 13200 {
> +				compatible = "marvell,armada-3700-tbg-clock";
> +				reg = <0x13200 0x100>;
> +				#clock-cells = <1>;
> +			};
> +
>   			pinctrl_nb: pinctrl-nb at 13800 {
>   				compatible = "marvell,armada3710-nb-pinctrl",
>   				"syscon", "simple-mfd";
> diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
> index cdfa052c16..a40c8e5c8f 100644
> --- a/drivers/clk/Kconfig
> +++ b/drivers/clk/Kconfig
> @@ -80,5 +80,6 @@ source "drivers/clk/uniphier/Kconfig"
>   source "drivers/clk/exynos/Kconfig"
>   source "drivers/clk/at91/Kconfig"
>   source "drivers/clk/renesas/Kconfig"
> +source "drivers/clk/mvebu/Kconfig"
>   
>   endmenu
> diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
> index dab106ab7f..094bcf5847 100644
> --- a/drivers/clk/Makefile
> +++ b/drivers/clk/Makefile
> @@ -11,6 +11,7 @@ obj-y += tegra/
>   obj-$(CONFIG_ARCH_ASPEED) += aspeed/
>   obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/
>   obj-$(CONFIG_CLK_AT91) += at91/
> +obj-$(CONFIG_CLK_MVEBU) += mvebu/
>   obj-$(CONFIG_CLK_BCM6345) += clk_bcm6345.o
>   obj-$(CONFIG_CLK_BOSTON) += clk_boston.o
>   obj-$(CONFIG_CLK_EXYNOS) += exynos/
> diff --git a/drivers/clk/mvebu/Kconfig b/drivers/clk/mvebu/Kconfig
> new file mode 100644
> index 0000000000..e776a15e7b
> --- /dev/null
> +++ b/drivers/clk/mvebu/Kconfig
> @@ -0,0 +1,11 @@
> +config CLK_MVEBU
> +	bool "MVEBU clock drivers"
> +	depends on CLK && ARCH_MVEBU
> +	help
> +	  Enable support for clock present on Marvell MVEBU SoCs.
> +
> +config CLK_ARMADA_3720
> +	bool "Marvell Armada 3720 clock driver"
> +	depends on CLK_MVEBU && ARM64
> +	help
> +	  Enable this to support the clocks on Marvell Armada 3720 SoC.
> diff --git a/drivers/clk/mvebu/Makefile b/drivers/clk/mvebu/Makefile
> new file mode 100644
> index 0000000000..7f80313203
> --- /dev/null
> +++ b/drivers/clk/mvebu/Makefile
> @@ -0,0 +1 @@
> +obj-$(CONFIG_CLK_ARMADA_3720) += armada-37xx-periph.o armada-37xx-tbg.o
> diff --git a/drivers/clk/mvebu/armada-37xx-periph.c b/drivers/clk/mvebu/armada-37xx-periph.c
> new file mode 100644
> index 0000000000..94eabe5f19
> --- /dev/null
> +++ b/drivers/clk/mvebu/armada-37xx-periph.c
> @@ -0,0 +1,464 @@
> +/*
> + * Marvell Armada 37xx SoC Peripheral clocks
> + *
> + * Marek Behun <marek.behun at nic.cz>
> + *
> + * Based on Linux driver by:
> + *   Gregory CLEMENT <gregory.clement at free-electrons.com>
> + *
> + * SPDX-License-Identifier:	GPL-2.0+
> + */
> +
> +#include <common.h>
> +#include <malloc.h>
> +#include <clk-uclass.h>
> +#include <clk.h>
> +#include <dm.h>
> +#include <asm/io.h>
> +#include <asm/arch/cpu.h>
> +
> +#define TBG_SEL		0x0
> +#define DIV_SEL0	0x4
> +#define DIV_SEL1	0x8
> +#define DIV_SEL2	0xC
> +#define CLK_SEL		0x10
> +#define CLK_DIS		0x14
> +
> +enum a37xx_periph_parent {
> +	TBG_A_P		= 0,
> +	TBG_B_P		= 1,
> +	TBG_A_S		= 2,
> +	TBG_B_S		= 3,
> +	MAX_TBG_PARENTS	= 4,
> +	XTAL		= 4,
> +	MAX_PARENTS	= 5,
> +};
> +
> +static const struct {
> +	const char *name;
> +	enum a37xx_periph_parent parent;
> +} a37xx_periph_parent_names[] = {
> +	{ "TBG-A-P", TBG_A_P },
> +	{ "TBG-B-P", TBG_B_P },
> +	{ "TBG-A-S", TBG_A_S },
> +	{ "TBG-B-S", TBG_B_S },
> +	{ "xtal"   , XTAL    },
> +};
> +
> +struct clk_periph;
> +
> +struct a37xx_periphclk {
> +	void __iomem *reg;
> +
> +	ulong parents[MAX_PARENTS];
> +
> +	const struct clk_periph *clks;
> +	bool clk_has_periph_parent[16];
> +	int clk_parent[16];
> +
> +	int count;
> +};
> +
> +struct clk_div_table {
> +	u32 div;
> +	u32 val;
> +};
> +
> +struct clk_periph {
> +	const char *name;
> +
> +	const char *parent_name;
> +
> +	u32 disable_bit;
> +	int mux_shift;
> +
> +	const struct clk_div_table *div_table[2];
> +	s32 div_reg_off[2];
> +	u32 div_mask[2];
> +	int div_shift[2];
> +
> +	unsigned can_gate : 1;
> +	unsigned can_mux : 1;
> +	unsigned dividers : 2;
> +};
> +
> +static const struct clk_div_table div_table1[] = {
> +	{ 1, 1 },
> +	{ 2, 2 },
> +	{ 0, 0 },
> +};
> +
> +static const struct clk_div_table div_table2[] = {
> +	{ 2, 1 },
> +	{ 4, 2 },
> +	{ 0, 0 },
> +};
> +
> +static const struct clk_div_table div_table6[] = {
> +	{ 1, 1 },
> +	{ 2, 2 },
> +	{ 3, 3 },
> +	{ 4, 4 },
> +	{ 5, 5 },
> +	{ 6, 6 },
> +	{ 0, 0 },
> +};
> +
> +#define CLK_FULL_DD(_n, _d, _mux, _r0, _r1, _s0, _s1)	\
> +	{						\
> +		.name = #_n,				\
> +		.disable_bit = BIT(_d),			\
> +		.mux_shift = _mux,			\
> +		.div_table[0] = div_table6,		\
> +		.div_table[1] = div_table6,		\
> +		.div_reg_off[0] = _r0,			\
> +		.div_reg_off[1] = _r1,			\
> +		.div_shift[0] = _s0,			\
> +		.div_shift[1] = _s1,			\
> +		.div_mask[0] = 7,			\
> +		.div_mask[1] = 7,			\
> +		.can_gate = 1,				\
> +		.can_mux = 1,				\
> +		.dividers = 2,				\
> +	}
> +
> +#define CLK_FULL(_n, _d, _mux, _r, _s, _m, _t)	\
> +	{					\
> +		.name = #_n,			\
> +		.disable_bit = BIT(_d),		\
> +		.mux_shift = _mux,		\
> +		.div_table[0] = _t,		\
> +		.div_reg_off[0] = _r,		\
> +		.div_shift[0] = _s,		\
> +		.div_mask[0] = _m,		\
> +		.can_gate = 1,			\
> +		.can_mux = 1,			\
> +		.dividers = 1,			\
> +	}
> +
> +#define CLK_GATE_DIV(_n, _d, _r, _s, _m, _t, _p)	\
> +	{						\
> +		.name = #_n,				\
> +		.parent_name = _p,			\
> +		.disable_bit = BIT(_d),			\
> +		.div_table[0] = _t,			\
> +		.div_reg_off[0] = _r,			\
> +		.div_shift[0] = _s,			\
> +		.div_mask[0] = _m,			\
> +		.can_gate = 1,				\
> +		.dividers = 1,				\
> +	}
> +
> +#define CLK_GATE(_n, _d, _p)		\
> +	{				\
> +		.name = #_n,		\
> +		.parent_name = _p,	\
> +		.disable_bit = BIT(_d),	\
> +		.can_gate = 1,		\
> +	}
> +
> +#define CLK_MUX_DIV(_n, _mux, _r, _s, _m, _t)	\
> +	{					\
> +		.name = #_n,			\
> +		.mux_shift = _mux,		\
> +		.div_table[0] = _t,		\
> +		.div_reg_off[0] = _r,		\
> +		.div_shift[0] = _s,		\
> +		.div_mask[0] = _m,		\
> +		.can_mux = 1,			\
> +		.dividers = 1,			\
> +	}
> +
> +#define CLK_MUX_DD(_n, _mux, _r0, _r1, _s0, _s1)	\
> +	{						\
> +		.name = #_n,				\
> +		.mux_shift = _mux,			\
> +		.div_table[0] = div_table6,		\
> +		.div_table[1] = div_table6,		\
> +		.div_reg_off[0] = _r0,			\
> +		.div_reg_off[1] = _r1,			\
> +		.div_shift[0] = _s0,			\
> +		.div_shift[1] = _s1,			\
> +		.div_mask[0] = 7,			\
> +		.div_mask[1] = 7,			\
> +		.can_mux = 1,				\
> +		.dividers = 2,				\
> +	}
> +
> +/* NB periph clocks */
> +static const struct clk_periph clks_nb[] ={
> +	CLK_FULL_DD(mmc, 2, 0, DIV_SEL2, DIV_SEL2, 16, 13),
> +	CLK_FULL_DD(sata_host, 3, 2, DIV_SEL2, DIV_SEL2, 10, 7),
> +	CLK_FULL_DD(sec_at, 6, 4, DIV_SEL1, DIV_SEL1, 3, 0),
> +	CLK_FULL_DD(sec_dap, 7, 6, DIV_SEL1, DIV_SEL1, 9, 6),
> +	CLK_FULL_DD(tscem, 8, 8, DIV_SEL1, DIV_SEL1, 15, 12),
> +	CLK_FULL(tscem_tmx, 10, 10, DIV_SEL1, 18, 7, div_table6),
> +	CLK_GATE(avs, 11, "xtal"),
> +	CLK_FULL_DD(sqf, 12, 12, DIV_SEL1, DIV_SEL1, 27, 24),
> +	CLK_FULL_DD(pwm, 13, 14, DIV_SEL0, DIV_SEL0, 3, 0),
> +	CLK_GATE(i2c_2, 16, "xtal"),
> +	CLK_GATE(i2c_1, 17, "xtal"),
> +	CLK_GATE_DIV(ddr_phy, 19, DIV_SEL0, 18, 1, div_table2, "TBG-A-S"),
> +	CLK_FULL_DD(ddr_fclk, 21, 16, DIV_SEL0, DIV_SEL0, 15, 12),
> +	CLK_FULL(trace, 22, 18, DIV_SEL0, 20, 7, div_table6),
> +	CLK_FULL(counter, 23, 20, DIV_SEL0, 23, 7, div_table6),
> +	CLK_FULL_DD(eip97, 24, 24, DIV_SEL2, DIV_SEL2, 22, 19),
> +	CLK_MUX_DIV(cpu, 22, DIV_SEL0, 28, 7, div_table6),
> +	{ },
> +};
> +
> +/* SB periph clocks */
> +static const struct clk_periph clks_sb[] = {
> +	CLK_MUX_DD(gbe_50, 6, DIV_SEL2, DIV_SEL2, 6, 9),
> +	CLK_MUX_DD(gbe_core, 8, DIV_SEL1, DIV_SEL1, 18, 21),
> +	CLK_MUX_DD(gbe_125, 10, DIV_SEL1, DIV_SEL1, 6, 9),
> +	CLK_GATE(gbe1_50, 0, "gbe_50"),
> +	CLK_GATE(gbe0_50, 1, "gbe_50"),
> +	CLK_GATE(gbe1_125, 2, "gbe_125"),
> +	CLK_GATE(gbe0_125, 3, "gbe_125"),
> +	CLK_GATE_DIV(gbe1_core, 4, DIV_SEL1, 13, 1, div_table1, "gbe_core"),
> +	CLK_GATE_DIV(gbe0_core, 5, DIV_SEL1, 14, 1, div_table1, "gbe_core"),
> +	CLK_GATE_DIV(gbe_bm, 12, DIV_SEL1, 0, 1, div_table1, "gbe_core"),
> +	CLK_FULL_DD(sdio, 11, 14, DIV_SEL0, DIV_SEL0, 3, 6),
> +	CLK_FULL_DD(usb32_usb2_sys, 16, 16, DIV_SEL0, DIV_SEL0, 9, 12),
> +	CLK_FULL_DD(usb32_ss_sys, 17, 18, DIV_SEL0, DIV_SEL0, 15, 18),
> +	{ },
> +};
> +
> +static inline int get_mux(struct a37xx_periphclk *priv, int shift)
> +{
> +	return (readl(priv->reg + TBG_SEL) >> shift) & 3;
> +}
> +
> +static ulong periph_clk_get_rate(struct a37xx_periphclk *priv, int id);
> +
> +static ulong get_parent_rate(struct a37xx_periphclk *priv, int id)
> +{
> +	const struct clk_periph *clk = &priv->clks[id];
> +
> +	if (clk->can_mux) {
> +		/* parent is one of TBG clocks */
> +		int tbg = get_mux(priv, clk->mux_shift);
> +
> +		return priv->parents[tbg];
> +	} else if (priv->clk_has_periph_parent[id]) {
> +		/* parent is one of other periph clocks */
> +
> +		if (priv->clk_parent[id] >= priv->count)
> +			return -EINVAL;
> +
> +		return periph_clk_get_rate(priv, priv->clk_parent[id]);
> +	} else {
> +		/* otherwise parent is one of TBGs or XTAL */
> +
> +		if (priv->clk_parent[id] >= MAX_PARENTS)
> +			return -EINVAL;
> +
> +		return priv->parents[priv->clk_parent[id]];
> +	}
> +}
> +
> +static ulong get_div(struct a37xx_periphclk *priv,
> +		     const struct clk_periph *clk, int idx)
> +{
> +	const struct clk_div_table *i;
> +	u32 reg;
> +
> +	reg = readl(priv->reg + clk->div_reg_off[idx]);
> +	reg = (reg >> clk->div_shift[idx]) & clk->div_mask[idx];
> +
> +	/* find divisor for register value val */
> +	for (i = clk->div_table[idx]; i && i->div != 0; ++i)
> +		if (i->val == reg)
> +			return i->div;
> +
> +	return 0;
> +}
> +
> +static ulong periph_clk_get_rate(struct a37xx_periphclk *priv, int id)
> +{
> +	const struct clk_periph *clk = &priv->clks[id];
> +	ulong rate, div;
> +	int i;
> +
> +	rate = get_parent_rate(priv, id);
> +	if (rate == -EINVAL)
> +		return -EINVAL;
> +
> +	/* divide the parent rate by dividers */
> +	div = 1;
> +	for (i = 0; i < clk->dividers; ++i)
> +		div *= get_div(priv, clk, i);
> +
> +	if (!div)
> +		return 0;
> +
> +	return DIV_ROUND_UP(rate, div);
> +
> +}
> +
> +static ulong armada_37xx_periph_clk_get_rate(struct clk *clk)
> +{
> +	struct a37xx_periphclk *priv = dev_get_priv(clk->dev);
> +
> +	if (clk->id >= priv->count)
> +		return -EINVAL;
> +
> +	return periph_clk_get_rate(priv, clk->id);
> +}
> +
> +static int periph_clk_enable(struct clk *clk, int enable)
> +{
> +	struct a37xx_periphclk *priv = dev_get_priv(clk->dev);
> +	const struct clk_periph *periph_clk = &priv->clks[clk->id];
> +
> +	if (clk->id >= priv->count)
> +		return -EINVAL;
> +
> +	if (!periph_clk->can_gate)
> +		return -ENOTSUPP;
> +
> +	if (enable)
> +		clrbits_le32(priv->reg + CLK_DIS, periph_clk->disable_bit);
> +	else
> +		setbits_le32(priv->reg + CLK_DIS, periph_clk->disable_bit);
> +
> +	return 0;
> +}
> +
> +static int armada_37xx_periph_clk_enable(struct clk *clk)
> +{
> +	return periph_clk_enable(clk, 1);
> +}
> +
> +static int armada_37xx_periph_clk_disable(struct clk *clk)
> +{
> +	return periph_clk_enable(clk, 0);
> +}
> +
> +int armada_37xx_periph_clk_dump(struct udevice *dev)
> +{
> +	struct a37xx_periphclk *priv = dev_get_priv(dev);
> +	const struct clk_periph *clks;
> +	int i;
> +
> +	if (!priv)
> +		return -ENODEV;
> +
> +	clks = priv->clks;
> +
> +	for (i = 0; i < priv->count; ++i)
> +		printf("  %s at %lu Hz\n", clks[i].name,
> +		       periph_clk_get_rate(priv, i));
> +	printf("\n");
> +
> +	return 0;
> +}
> +
> +static int armada_37xx_periph_clk_probe(struct udevice *dev)
> +{
> +	struct a37xx_periphclk *priv = dev_get_priv(dev);
> +	const struct clk_periph *clks;
> +	int ret, i;
> +
> +	clks = (const struct clk_periph *) dev_get_driver_data(dev);
> +	if (!clks)
> +		return -ENODEV;
> +
> +	priv->reg = dev_read_addr_ptr(dev);
> +	if (!priv->reg) {
> +		dev_err(dev, "no io address\n");
> +		return -ENODEV;
> +	}
> +
> +	/* count clk_periph nodes */
> +	priv->count = 0;
> +	while (clks[priv->count].name)
> +		priv->count++;
> +
> +	priv->clks = clks;
> +
> +	/* assign parent IDs to nodes which have non-NULL parent_name */
> +	for (i = 0; i < priv->count; ++i) {
> +		int j;
> +
> +		if (!clks[i].parent_name)
> +			continue;
> +
> +		/* first try if parent_name is one of TBGs or XTAL */
> +		for (j = 0; j < MAX_PARENTS; ++j)
> +			if (!strcmp(clks[i].parent_name,
> +				    a37xx_periph_parent_names[j].name))
> +				break;
> +
> +		if (j < MAX_PARENTS) {
> +			priv->clk_has_periph_parent[i] = false;
> +			priv->clk_parent[i] =
> +				a37xx_periph_parent_names[j].parent;
> +			continue;
> +		}
> +
> +		/* else parent_name should be one of other periph clocks */
> +		for (j = 0; j < priv->count; ++j) {
> +			if (!strcmp(clks[i].parent_name, clks[j].name))
> +				break;
> +		}
> +
> +		if (j < priv->count) {
> +			priv->clk_has_periph_parent[i] = true;
> +			priv->clk_parent[i] = j;
> +			continue;
> +		}
> +
> +		dev_err(dev, "undefined parent %s\n", clks[i].parent_name);
> +		return -EINVAL;
> +	}
> +
> +	for (i = 0; i < MAX_PARENTS; ++i) {
> +		struct clk clk;
> +
> +		if (i == XTAL) {
> +			priv->parents[i] = get_ref_clk() * 1000000;
> +			continue;
> +		}
> +
> +		ret = clk_get_by_index(dev, i, &clk);
> +		if (ret) {
> +			dev_err(dev, "one of parent clocks (%i) missing: %i\n",
> +				i, ret);
> +			return -ENODEV;
> +		}
> +
> +		priv->parents[i] = clk_get_rate(&clk);
> +		clk_free(&clk);
> +	}
> +
> +	return 0;
> +}
> +
> +static const struct clk_ops armada_37xx_periph_clk_ops = {
> +	.get_rate = armada_37xx_periph_clk_get_rate,
> +	.enable = armada_37xx_periph_clk_enable,
> +	.disable = armada_37xx_periph_clk_disable,
> +};
> +
> +static const struct udevice_id armada_37xx_periph_clk_ids[] = {
> +	{
> +		.compatible = "marvell,armada-3700-periph-clock-nb",
> +		.data = (ulong) clks_nb,
> +	},
> +	{
> +		.compatible = "marvell,armada-3700-periph-clock-sb",
> +		.data = (ulong) clks_sb,
> +	},
> +	{}
> +};
> +
> +U_BOOT_DRIVER(armada_37xx_periph_clk) = {
> +	.name		= "armada_37xx_periph_clk",
> +	.id		= UCLASS_CLK,
> +	.of_match	= armada_37xx_periph_clk_ids,
> +	.ops		= &armada_37xx_periph_clk_ops,
> +	.priv_auto_alloc_size = sizeof(struct a37xx_periphclk),
> +	.probe		= armada_37xx_periph_clk_probe,
> +};
> diff --git a/drivers/clk/mvebu/armada-37xx-tbg.c b/drivers/clk/mvebu/armada-37xx-tbg.c
> new file mode 100644
> index 0000000000..e8c654f1a6
> --- /dev/null
> +++ b/drivers/clk/mvebu/armada-37xx-tbg.c
> @@ -0,0 +1,153 @@
> +/*
> + * Marvell Armada 37xx SoC Time Base Generator clocks
> + *
> + * Marek Behun <marek.behun at nic.cz>
> + *
> + * Based on Linux driver by:
> + *   Gregory CLEMENT <gregory.clement at free-electrons.com>
> + *
> + * SPDX-License-Identifier:	GPL-2.0+
> + */
> +
> +#include <common.h>
> +#include <clk-uclass.h>
> +#include <clk.h>
> +#include <dm.h>
> +#include <asm/io.h>
> +#include <asm/arch/cpu.h>
> +
> +#define NUM_TBG	    4
> +
> +#define TBG_CTRL0		0x4
> +#define TBG_CTRL1		0x8
> +#define TBG_CTRL7		0x20
> +#define TBG_CTRL8		0x30
> +
> +#define TBG_DIV_MASK		0x1FF
> +
> +#define TBG_A_REFDIV		0
> +#define TBG_B_REFDIV		16
> +
> +#define TBG_A_FBDIV		2
> +#define TBG_B_FBDIV		18
> +
> +#define TBG_A_VCODIV_SE		0
> +#define TBG_B_VCODIV_SE		16
> +
> +#define TBG_A_VCODIV_DIFF	1
> +#define TBG_B_VCODIV_DIFF	17
> +
> +struct tbg_def {
> +	const char *name;
> +	u32 refdiv_offset;
> +	u32 fbdiv_offset;
> +	u32 vcodiv_reg;
> +	u32 vcodiv_offset;
> +};
> +
> +static const struct tbg_def tbg[NUM_TBG] = {
> +	{"TBG-A-P", TBG_A_REFDIV, TBG_A_FBDIV, TBG_CTRL8, TBG_A_VCODIV_DIFF},
> +	{"TBG-B-P", TBG_B_REFDIV, TBG_B_FBDIV, TBG_CTRL8, TBG_B_VCODIV_DIFF},
> +	{"TBG-A-S", TBG_A_REFDIV, TBG_A_FBDIV, TBG_CTRL1, TBG_A_VCODIV_SE},
> +	{"TBG-B-S", TBG_B_REFDIV, TBG_B_FBDIV, TBG_CTRL1, TBG_B_VCODIV_SE},
> +};
> +
> +struct a37xx_tbgclk {
> +	ulong rates[NUM_TBG];
> +	unsigned int mult[NUM_TBG];
> +	unsigned int div[NUM_TBG];
> +};
> +
> +static unsigned int tbg_get_mult(void __iomem *reg, const struct tbg_def *ptbg)
> +{
> +	u32 val;
> +
> +	val = readl(reg + TBG_CTRL0);
> +
> +	return ((val >> ptbg->fbdiv_offset) & TBG_DIV_MASK) << 2;
> +}
> +
> +static unsigned int tbg_get_div(void __iomem *reg, const struct tbg_def *ptbg)
> +{
> +	u32 val;
> +	unsigned int div;
> +
> +	val = readl(reg + TBG_CTRL7);
> +
> +	div = (val >> ptbg->refdiv_offset) & TBG_DIV_MASK;
> +	if (div == 0)
> +		div = 1;
> +	val = readl(reg + ptbg->vcodiv_reg);
> +
> +	div *= 1 << ((val >>  ptbg->vcodiv_offset) & TBG_DIV_MASK);
> +
> +	return div;
> +}
> +
> +static ulong armada_37xx_tbg_clk_get_rate(struct clk *clk)
> +{
> +	struct a37xx_tbgclk *priv = dev_get_priv(clk->dev);
> +
> +	if (clk->id >= NUM_TBG)
> +		return -ENODEV;
> +
> +	return priv->rates[clk->id];
> +}
> +
> +int armada_37xx_tbg_clk_dump(struct udevice *dev)
> +{
> +	struct a37xx_tbgclk *priv = dev_get_priv(dev);
> +	int i;
> +
> +	for (i = 0; i < NUM_TBG; ++i)
> +		printf("  %s at %lu Hz\n", tbg[i].name,
> +		       priv->rates[i]);
> +	printf("\n");
> +
> +	return 0;
> +}
> +
> +static int armada_37xx_tbg_clk_probe(struct udevice *dev)
> +{
> +	struct a37xx_tbgclk *priv = dev_get_priv(dev);
> +	void __iomem *reg;
> +	ulong xtal;
> +	int i;
> +
> +	reg = dev_read_addr_ptr(dev);
> +	if (!reg) {
> +		dev_err(dev, "no io address\n");
> +		return -ENODEV;
> +	}
> +
> +	xtal = (ulong) get_ref_clk() * 1000000;
> +
> +	for (i = 0; i < NUM_TBG; ++i) {
> +		unsigned int mult, div;
> +
> +		mult = tbg_get_mult(reg, &tbg[i]);
> +		div = tbg_get_div(reg, &tbg[i]);
> +
> +		priv->rates[i] = (xtal * mult) / div;
> +	}
> +
> +	return 0;
> +}
> +
> +static const struct clk_ops armada_37xx_tbg_clk_ops = {
> +	.get_rate = armada_37xx_tbg_clk_get_rate,
> +};
> +
> +static const struct udevice_id armada_37xx_tbg_clk_ids[] = {
> +	{ .compatible = "marvell,armada-3700-tbg-clock" },
> +	{}
> +};
> +
> +U_BOOT_DRIVER(armada_37xx_tbg_clk) = {
> +	.name		= "armada_37xx_tbg_clk",
> +	.id		= UCLASS_CLK,
> +	.of_match	= armada_37xx_tbg_clk_ids,
> +	.ops		= &armada_37xx_tbg_clk_ops,
> +	.priv_auto_alloc_size = sizeof(struct a37xx_tbgclk),
> +	.probe		= armada_37xx_tbg_clk_probe,
> +};
> 

Viele Grüße,
Stefan

-- 
DENX Software Engineering GmbH,      Managing Director: Wolfgang Denk
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Phone: (+49)-8142-66989-51 Fax: (+49)-8142-66989-80 Email: sr at denx.de


More information about the U-Boot mailing list