diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index c996d65..7853a7a 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -68,6 +68,13 @@ config CLK_HSDK help Enable this to support the cgu clocks on Synopsys ARC HSDK +config CLK_VEXPRESS_OSC + bool "Enable driver for Arm Versatile Express OSC clock generators" + depends on CLK && VEXPRESS_CONFIG + help + This clock driver adds support for clock generators present on + Arm Versatile Express platforms. + config CLK_ZYNQ bool "Enable clock driver support for Zynq" depends on CLK && ARCH_ZYNQ diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 11468f2..8bc9f52 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_CLK_RENESAS) += renesas/ obj-$(CONFIG_CLK_STM32F) += clk_stm32f.o obj-$(CONFIG_CLK_STM32MP1) += clk_stm32mp1.o obj-$(CONFIG_CLK_UNIPHIER) += uniphier/ +obj-$(CONFIG_CLK_VEXPRESS_OSC) += clk_vexpress_osc.o obj-$(CONFIG_CLK_ZYNQ) += clk_zynq.o obj-$(CONFIG_CLK_ZYNQMP) += clk_zynqmp.o obj-$(CONFIG_ICS8N3QV01) += ics8n3qv01.o diff --git a/drivers/clk/clk_vexpress_osc.c b/drivers/clk/clk_vexpress_osc.c new file mode 100644 index 0000000..7fef4b2 --- /dev/null +++ b/drivers/clk/clk_vexpress_osc.c @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2018 Arm Ltd + * Author: Liviu Dudau + * + */ +#define DEBUG +#include +#include +#include +#include +#include +#include + +#define CLK_FUNCTION BIT(20) + +struct vexpress_osc_clk_priv { + u8 osc; + ulong rate_min; + ulong rate_max; +}; + +static ulong vexpress_osc_clk_get_rate(struct clk *clk) +{ + int err; + u32 data; + struct udevice *vexpress_cfg = dev_get_parent(clk->dev); + struct vexpress_osc_clk_priv *priv = dev_get_priv(clk->dev); + + data = CLK_FUNCTION | priv->osc; + err = misc_read(vexpress_cfg, 0, &data, sizeof(data)); + if (err) + return err; + + return data; +} + +#ifndef CONFIG_SPL_BUILD +static ulong vexpress_osc_clk_set_rate(struct clk *clk, ulong rate) +{ + int err; + u32 buffer[2]; + struct udevice *vexpress_cfg = dev_get_parent(clk->dev); + struct vexpress_osc_clk_priv *priv = dev_get_priv(clk->dev); + + if (rate < priv->rate_min || rate > priv->rate_max) + return -EINVAL; + + /* + * we are sending the parent the info about the oscillator + * and the value we want to set + */ + buffer[0] = CLK_FUNCTION | priv->osc; + buffer[1] = rate; + err = misc_write(vexpress_cfg, 0, buffer, 2 * sizeof(u32)); + if (err) + return err; + + return rate; +} +#endif + +static struct clk_ops vexpress_osc_clk_ops = { + .get_rate = vexpress_osc_clk_get_rate, +#ifndef CONFIG_SPL_BUILD + .set_rate = vexpress_osc_clk_set_rate, +#endif +}; + +static int vexpress_osc_clk_probe(struct udevice *dev) +{ + struct vexpress_osc_clk_priv *priv = dev_get_priv(dev); + u32 values[2]; + int err; + + err = dev_read_u32_array(dev, "freq-range", values, 2); + if (err) + return err; + priv->rate_min = values[0]; + priv->rate_max = values[1]; + + err = dev_read_u32_array(dev, "arm,vexpress-sysreg,func", values, 2); + if (err) + return err; + + if (values[0] != 1) { + dev_err(dev, "Invalid VExpress function for clock, must be '1'"); + return -EINVAL; + } + priv->osc = values[1]; + debug("clk \"%s%d\", min freq %luHz, max freq %luHz\n", dev->name, + priv->osc, priv->rate_min, priv->rate_max); + + return 0; +} + +static const struct udevice_id vexpress_osc_clk_ids[] = { + { .compatible = "arm,vexpress-osc", }, + {} +}; + +U_BOOT_DRIVER(vexpress_osc_clk) = { + .name = "vexpress_osc_clk", + .id = UCLASS_CLK, + .of_match = vexpress_osc_clk_ids, + .ops = &vexpress_osc_clk_ops, + .priv_auto_alloc_size = sizeof(struct vexpress_osc_clk_priv), + .probe = vexpress_osc_clk_probe, +};