[U-Boot] [PATCH 1/2] clk: zynqmp: Add clock driver support for zynqmp

From: Siva Durga Prasad Paladugu siva.durga.paladugu@xilinx.com
Add basic clock driver support for zynqmp which sets the required clock for GEM controller
Signed-off-by: Siva Durga Prasad Paladugu sivadur@xilinx.com Signed-off-by: Michal Simek michal.simek@xilinx.com ---
drivers/clk/Kconfig | 7 ++ drivers/clk/Makefile | 1 + drivers/clk/clk_zynqmp.c | 241 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 249 insertions(+) create mode 100644 drivers/clk/clk_zynqmp.c
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index c05ce2a9efa3..335ef9e1d7cf 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -28,6 +28,13 @@ config CLK_BOSTON help Enable this to support the clocks
+config CLK_ZYNQMP + bool "Enable clock driver support for ZynqMP" + depends on ARCH_ZYNQMP + help + This clock driver adds support for clock realted settings for + ZynqMP platform. + source "drivers/clk/tegra/Kconfig" source "drivers/clk/uniphier/Kconfig" source "drivers/clk/exynos/Kconfig" diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 40a5e8cae868..f55348e8f15f 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/ obj-$(CONFIG_SANDBOX) += clk_sandbox.o obj-$(CONFIG_SANDBOX) += clk_sandbox_test.o obj-$(CONFIG_MACH_PIC32) += clk_pic32.o +obj-$(CONFIG_CLK_ZYNQMP) += clk_zynqmp.o
obj-y += tegra/ obj-$(CONFIG_CLK_UNIPHIER) += uniphier/ diff --git a/drivers/clk/clk_zynqmp.c b/drivers/clk/clk_zynqmp.c new file mode 100644 index 000000000000..694274d99103 --- /dev/null +++ b/drivers/clk/clk_zynqmp.c @@ -0,0 +1,241 @@ +/* + * ZynqMP clock driver + * + * Copyright (C) 2016 Xilinx, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <linux/bitops.h> +#include <clk-uclass.h> +#include <dm/device.h> +#include <clk.h> + +#define ZYNQMP_GEM0_REF_CTRL 0xFF5E0050 +#define ZYNQMP_IOPLL_CTRL 0xFF5E0020 +#define ZYNQMP_RPLL_CTRL 0xFF5E0030 +#define ZYNQMP_DPLL_CTRL 0xFD1A002C +#define ZYNQMP_SIP_SVC_MMIO_WRITE 0xC2000013 +#define ZYNQMP_SIP_SVC_MMIO_WRITE 0xC2000013 +#define ZYNQMP_SIP_SVC_MMIO_WRITE 0xC2000013 +#define ZYNQMP_SIP_SVC_MMIO_READ 0xC2000014 +#define ZYNQMP_DIV_MAX_VAL 0x3F +#define ZYNQMP_DIV1_SHFT 8 +#define ZYNQMP_DIV1_SHFT 8 +#define ZYNQMP_DIV2_SHFT 16 +#define ZYNQMP_DIV_MASK 0x3F +#define ZYNQMP_PLL_CTRL_FBDIV_MASK 0x7F +#define ZYNQMP_PLL_CTRL_FBDIV_SHFT 8 +#define ZYNQMP_GEM_REF_CTRL_SRC_MASK 0x7 +#define ZYNQMP_GEM0_CLK_ID 45 +#define ZYNQMP_GEM1_CLK_ID 46 +#define ZYNQMP_GEM2_CLK_ID 47 +#define ZYNQMP_GEM3_CLK_ID 48 + +static unsigned long pss_ref_clk; + +static int zynqmp_calculate_divisors(unsigned long req_rate, + unsigned long parent_rate, + u32 *div1, u32 *div2) +{ + u32 req_div = 1; + u32 i; + + /* + * calculate two divisors to get + * required rate and each divisor + * should be less than 63 + */ + req_div = DIV_ROUND_UP(parent_rate, req_rate); + + for (i = 1; i <= req_div; i++) { + if ((req_div % i) == 0) { + *div1 = req_div / i; + *div2 = i; + if ((*div1 < ZYNQMP_DIV_MAX_VAL) && + (*div2 < ZYNQMP_DIV_MAX_VAL)) + return 0; + } + } + + return -1; +} + +static int zynqmp_get_periph_id(unsigned long id) +{ + int periph_id; + + switch (id) { + case ZYNQMP_GEM0_CLK_ID: + periph_id = 0; + break; + case ZYNQMP_GEM1_CLK_ID: + periph_id = 1; + break; + case ZYNQMP_GEM2_CLK_ID: + periph_id = 2; + break; + case ZYNQMP_GEM3_CLK_ID: + periph_id = 3; + break; + default: + printf("%s, Invalid clock id:%ld\n", __func__, id); + return -EINVAL; + } + + return periph_id; +} + +static int zynqmp_set_clk(unsigned long id, u32 div1, u32 div2) +{ + struct pt_regs regs; + ulong reg; + u32 mask, value; + + id = zynqmp_get_periph_id(id); + if (id < 0) + return -EINVAL; + + reg = (ulong)((u32 *)ZYNQMP_GEM0_REF_CTRL + id); + mask = (ZYNQMP_DIV_MASK << ZYNQMP_DIV1_SHFT) | + (ZYNQMP_DIV_MASK << ZYNQMP_DIV2_SHFT); + value = (div1 << ZYNQMP_DIV1_SHFT) | (div2 << ZYNQMP_DIV2_SHFT); + + debug("%s: reg:0x%lx, mask:0x%x, value:0x%x\n", __func__, reg, mask, + value); + + regs.regs[0] = ZYNQMP_SIP_SVC_MMIO_WRITE; + regs.regs[1] = ((u64)mask << 32) | reg; + regs.regs[2] = value; + regs.regs[3] = 0; + + smc_call(®s); + + return regs.regs[0]; +} + +static unsigned long zynqmp_clk_get_rate(struct clk *clk) +{ + struct pt_regs regs; + ulong reg; + unsigned long value; + int id; + + id = zynqmp_get_periph_id(clk->id); + if (id < 0) + return -EINVAL; + + reg = (ulong)((u32 *)ZYNQMP_GEM0_REF_CTRL + id); + + regs.regs[0] = ZYNQMP_SIP_SVC_MMIO_READ; + regs.regs[1] = reg; + regs.regs[2] = 0; + regs.regs[3] = 0; + + smc_call(®s); + + value = upper_32_bits(regs.regs[0]); + + value &= ZYNQMP_GEM_REF_CTRL_SRC_MASK; + + switch (value) { + case 0: + regs.regs[1] = ZYNQMP_IOPLL_CTRL; + break; + case 2: + regs.regs[1] = ZYNQMP_RPLL_CTRL; + break; + case 3: + regs.regs[1] = ZYNQMP_DPLL_CTRL; + break; + default: + return -EINVAL; + } + + regs.regs[0] = ZYNQMP_SIP_SVC_MMIO_READ; + regs.regs[2] = 0; + regs.regs[3] = 0; + + smc_call(®s); + + value = upper_32_bits(regs.regs[0]) & + (ZYNQMP_PLL_CTRL_FBDIV_MASK << + ZYNQMP_PLL_CTRL_FBDIV_SHFT); + value >>= ZYNQMP_PLL_CTRL_FBDIV_SHFT; + value *= pss_ref_clk; + + return value; +} + +static ulong zynqmp_clk_set_rate(struct clk *clk, unsigned long clk_rate) +{ + int ret; + u32 div1 = 0; + u32 div2 = 0; + unsigned long input_clk; + + input_clk = zynqmp_clk_get_rate(clk); + if (IS_ERR_VALUE(input_clk)) { + dev_err(dev, "failed to get input_clk\n"); + return -EINVAL; + } + + debug("%s: i/p CLK %ld, clk_rate:0x%ld\n", __func__, input_clk, + clk_rate); + + ret = zynqmp_calculate_divisors(clk_rate, input_clk, &div1, &div2); + if (ret) { + dev_err(dev, "failed to proper divisors\n"); + return -EINVAL; + } + + debug("%s: Div1:%d, Div2:%d\n", __func__, div1, div2); + + ret = zynqmp_set_clk(clk->id, div1, div2); + if (ret) { + dev_err(dev, "failed to set gem clk\n"); + return -EINVAL; + } + + return 0; +} + +static int zynqmp_clk_probe(struct udevice *dev) +{ + struct clk clk; + int ret; + + debug("%s\n", __func__); + ret = clk_get_by_name(dev, "pss_ref_clk", &clk); + if (ret < 0) { + dev_err(dev, "failed to get pss_ref_clk\n"); + return ret; + } + + pss_ref_clk = clk_get_rate(&clk); + if (IS_ERR_VALUE(pss_ref_clk)) { + dev_err(dev, "failed to get rate pss_ref_clk\n"); + return -EINVAL; + } + + return 0; +} + +static struct clk_ops zynqmp_clk_ops = { + .set_rate = zynqmp_clk_set_rate, + .get_rate = zynqmp_clk_get_rate, +}; + +static const struct udevice_id zynqmp_clk_ids[] = { + { .compatible = "xlnx,zynqmp-clkc" }, + { } +}; + +U_BOOT_DRIVER(zynqmp_clk) = { + .name = "zynqmp-clk", + .id = UCLASS_CLK, + .of_match = zynqmp_clk_ids, + .probe = zynqmp_clk_probe, + .ops = &zynqmp_clk_ops, +};

From: Siva Durga Prasad Paladugu siva.durga.paladugu@xilinx.com
Enable and use the clock driver routine defined in clock driver toset required clock appropriately.
Signed-off-by: Siva Durga Prasad Paladugu sivadur@xilinx.com Signed-off-by: Michal Simek michal.simek@xilinx.com ---
arch/arm/include/asm/arch-zynqmp/sys_proto.h | 2 ++ drivers/net/zynq_gem.c | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+)
diff --git a/arch/arm/include/asm/arch-zynqmp/sys_proto.h b/arch/arm/include/asm/arch-zynqmp/sys_proto.h index 1db2bd6a4f7a..95fd91da2915 100644 --- a/arch/arm/include/asm/arch-zynqmp/sys_proto.h +++ b/arch/arm/include/asm/arch-zynqmp/sys_proto.h @@ -8,10 +8,12 @@ #ifndef _ASM_ARCH_SYS_PROTO_H #define _ASM_ARCH_SYS_PROTO_H
+#ifndef CONFIG_CLK_ZYNQMP /* Setup clk for network */ static inline void zynq_slcr_gem_clk_setup(u32 gem_id, unsigned long clk_rate) { } +#endif
int zynq_slcr_get_mio_pin_status(const char *periph);
diff --git a/drivers/net/zynq_gem.c b/drivers/net/zynq_gem.c index 95b4d6e4f0aa..6dd87cf28f95 100644 --- a/drivers/net/zynq_gem.c +++ b/drivers/net/zynq_gem.c @@ -9,6 +9,7 @@ * SPDX-License-Identifier: GPL-2.0+ */
+#include <clk.h> #include <common.h> #include <dm.h> #include <net.h> @@ -181,6 +182,9 @@ struct zynq_gem_priv { struct phy_device *phydev; int phy_of_handle; struct mii_dev *bus; +#ifdef CONFIG_CLK_ZYNQMP + struct clk clk; +#endif };
static u32 phy_setup_op(struct zynq_gem_priv *priv, u32 phy_addr, u32 regnum, @@ -455,8 +459,14 @@ static int zynq_gem_init(struct udevice *dev)
/* Change the rclk and clk only not using EMIO interface */ if (!priv->emio) +#ifndef CONFIG_CLK_ZYNQMP zynq_slcr_gem_clk_setup((ulong)priv->iobase != ZYNQ_GEM_BASEADDR0, clk_rate); +#else + ret = clk_set_rate(&priv->clk, clk_rate); + if (IS_ERR_VALUE(ret)) + return -1; +#endif
setbits_le32(®s->nwctrl, ZYNQ_GEM_NWCTRL_RXEN_MASK | ZYNQ_GEM_NWCTRL_TXEN_MASK); @@ -629,6 +639,14 @@ static int zynq_gem_probe(struct udevice *dev) priv->tx_bd = (struct emac_bd *)bd_space; priv->rx_bd = (struct emac_bd *)((ulong)bd_space + BD_SEPRN_SPACE);
+#ifdef CONFIG_CLK_ZYNQMP + ret = clk_get_by_name(dev, "tx_clk", &priv->clk); + if (ret < 0) { + dev_err(dev, "failed to get clock\n"); + return -EINVAL; + } +#endif + priv->bus = mdio_alloc(); priv->bus->read = zynq_gem_miiphy_read; priv->bus->write = zynq_gem_miiphy_write;

On 16 December 2016 at 05:31, Michal Simek michal.simek@xilinx.com wrote:
From: Siva Durga Prasad Paladugu siva.durga.paladugu@xilinx.com
Add basic clock driver support for zynqmp which sets the required clock for GEM controller
Signed-off-by: Siva Durga Prasad Paladugu sivadur@xilinx.com Signed-off-by: Michal Simek michal.simek@xilinx.com
drivers/clk/Kconfig | 7 ++ drivers/clk/Makefile | 1 + drivers/clk/clk_zynqmp.c | 241 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 249 insertions(+) create mode 100644 drivers/clk/clk_zynqmp.c
Reviewed-by: Simon Glass sjg@chromium.org
participants (2)
-
Michal Simek
-
Simon Glass