diff options
author | Marek Vasut <marek.vasut+renesas@gmail.com> | 2018-01-08 16:38:51 +0100 |
---|---|---|
committer | Marek Vasut <marex@denx.de> | 2018-01-24 23:27:22 +0100 |
commit | dedb60fb5bebb9830f76247460b02140bdeb8661 (patch) | |
tree | e26d152d60f72d7f88a70f50a0af3d9370f461fb /drivers | |
parent | 28b8f225b49ab24d295d4690cfb52ec8e7104db8 (diff) |
clk: renesas: Add Gen2 clock core
Add common clock code for Renesas RCar Gen2 platforms.
Signed-off-by: Marek Vasut <marek.vasut+renesas@gmail.com>
Cc: Nobuhiro Iwamatsu <iwamatsu@nigauri.org>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/clk/renesas/Kconfig | 7 | ||||
-rw-r--r-- | drivers/clk/renesas/Makefile | 1 | ||||
-rw-r--r-- | drivers/clk/renesas/clk-rcar-gen2.c | 279 | ||||
-rw-r--r-- | drivers/clk/renesas/rcar-gen2-cpg.h | 49 | ||||
-rw-r--r-- | drivers/clk/renesas/renesas-cpg-mssr.h | 3 |
5 files changed, 339 insertions, 0 deletions
diff --git a/drivers/clk/renesas/Kconfig b/drivers/clk/renesas/Kconfig index d805f8b36e..a7aa565fb8 100644 --- a/drivers/clk/renesas/Kconfig +++ b/drivers/clk/renesas/Kconfig @@ -4,6 +4,13 @@ config CLK_RENESAS help Enable support for clock present on Renesas RCar SoCs. +config CLK_RCAR_GEN2 + bool "Renesas RCar Gen2 clock driver" + def_bool y if RCAR_32 + depends on CLK_RENESAS + help + Enable this to support the clocks on Renesas RCar Gen2 SoC. + config CLK_RCAR_GEN3 bool "Renesas RCar Gen3 clock driver" def_bool y if RCAR_GEN3 diff --git a/drivers/clk/renesas/Makefile b/drivers/clk/renesas/Makefile index 4be9852729..14105a0da4 100644 --- a/drivers/clk/renesas/Makefile +++ b/drivers/clk/renesas/Makefile @@ -1,4 +1,5 @@ obj-$(CONFIG_CLK_RENESAS) += renesas-cpg-mssr.o +obj-$(CONFIG_CLK_RCAR_GEN2) += clk-rcar-gen2.o obj-$(CONFIG_CLK_RCAR_GEN3) += clk-rcar-gen3.o obj-$(CONFIG_CLK_R8A7795) += r8a7795-cpg-mssr.o obj-$(CONFIG_CLK_R8A7796) += r8a7796-cpg-mssr.o diff --git a/drivers/clk/renesas/clk-rcar-gen2.c b/drivers/clk/renesas/clk-rcar-gen2.c new file mode 100644 index 0000000000..f65b18c7f6 --- /dev/null +++ b/drivers/clk/renesas/clk-rcar-gen2.c @@ -0,0 +1,279 @@ +/* + * Renesas RCar Gen2 CPG MSSR driver + * + * Copyright (C) 2017 Marek Vasut <marek.vasut@gmail.com> + * + * Based on the following driver from Linux kernel: + * r8a7796 Clock Pulse Generator / Module Standby and Software Reset + * + * Copyright (C) 2016 Glider bvba + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <clk-uclass.h> +#include <dm.h> +#include <errno.h> +#include <asm/io.h> + +#include <dt-bindings/clock/renesas-cpg-mssr.h> + +#include "renesas-cpg-mssr.h" +#include "rcar-gen2-cpg.h" + +#define CPG_RST_MODEMR 0x0060 + +#define CPG_PLL0CR 0x00d8 +#define CPG_SDCKCR 0x0074 + +struct clk_div_table { + u8 val; + u8 div; +}; + +/* SDHI divisors */ +static const struct clk_div_table cpg_sdh_div_table[] = { + { 0, 2 }, { 1, 3 }, { 2, 4 }, { 3, 6 }, + { 4, 8 }, { 5, 12 }, { 6, 16 }, { 7, 18 }, + { 8, 24 }, { 10, 36 }, { 11, 48 }, { 0, 0 }, +}; + +static const struct clk_div_table cpg_sd01_div_table[] = { + { 4, 8 }, { 5, 12 }, { 6, 16 }, { 7, 18 }, + { 8, 24 }, { 10, 36 }, { 11, 48 }, { 12, 10 }, + { 0, 0 }, +}; + +static u8 gen2_clk_get_sdh_div(const struct clk_div_table *table, u8 div) +{ + while ((*table++).val) { + if ((*table).div == div) + return div; + } + return 0xff; +} + +static int gen2_clk_enable(struct clk *clk) +{ + struct gen2_clk_priv *priv = dev_get_priv(clk->dev); + + return renesas_clk_endisable(clk, priv->base, true); +} + +static int gen2_clk_disable(struct clk *clk) +{ + struct gen2_clk_priv *priv = dev_get_priv(clk->dev); + + return renesas_clk_endisable(clk, priv->base, false); +} + +static ulong gen2_clk_get_rate(struct clk *clk) +{ + struct gen2_clk_priv *priv = dev_get_priv(clk->dev); + struct cpg_mssr_info *info = priv->info; + struct clk parent; + const struct cpg_core_clk *core; + const struct rcar_gen2_cpg_pll_config *pll_config = + priv->cpg_pll_config; + u32 value, mult, div, rate = 0; + int ret; + + debug("%s[%i] Clock: id=%lu\n", __func__, __LINE__, clk->id); + + ret = renesas_clk_get_parent(clk, info, &parent); + if (ret) { + printf("%s[%i] parent fail, ret=%i\n", __func__, __LINE__, ret); + return ret; + } + + if (renesas_clk_is_mod(clk)) { + rate = gen2_clk_get_rate(&parent); + debug("%s[%i] MOD clk: parent=%lu => rate=%u\n", + __func__, __LINE__, parent.id, rate); + return rate; + } + + ret = renesas_clk_get_core(clk, info, &core); + if (ret) + return ret; + + switch (core->type) { + case CLK_TYPE_IN: + if (core->id == info->clk_extal_id) { + rate = clk_get_rate(&priv->clk_extal); + debug("%s[%i] EXTAL clk: rate=%u\n", + __func__, __LINE__, rate); + return rate; + } + + if (core->id == info->clk_extal_usb_id) { + rate = clk_get_rate(&priv->clk_extal_usb); + debug("%s[%i] EXTALR clk: rate=%u\n", + __func__, __LINE__, rate); + return rate; + } + + return -EINVAL; + + case CLK_TYPE_FF: + rate = (gen2_clk_get_rate(&parent) * core->mult) / core->div; + debug("%s[%i] FIXED clk: parent=%i div=%i mul=%i => rate=%u\n", + __func__, __LINE__, + core->parent, core->mult, core->div, rate); + return rate; + + case CLK_TYPE_DIV6P1: /* DIV6 Clock with 1 parent clock */ + value = (readl(priv->base + core->offset) & 0x3f) + 1; + rate = gen2_clk_get_rate(&parent) / value; + debug("%s[%i] DIV6P1 clk: parent=%i div=%i => rate=%u\n", + __func__, __LINE__, + core->parent, value, rate); + return rate; + + case CLK_TYPE_GEN2_MAIN: + rate = gen2_clk_get_rate(&parent) / pll_config->extal_div; + debug("%s[%i] MAIN clk: parent=%i extal_div=%i => rate=%u\n", + __func__, __LINE__, + core->parent, pll_config->extal_div, rate); + return rate; + + case CLK_TYPE_GEN2_PLL0: + /* + * PLL0 is a configurable multiplier clock except on R-Car + * V2H/E2. Register the PLL0 clock as a fixed factor clock for + * now as there's no generic multiplier clock implementation and + * we currently have no need to change the multiplier value. + */ + mult = pll_config->pll0_mult; + if (!mult) { + value = readl(priv->base + CPG_PLL0CR); + mult = (((value >> 24) & 0x7f) + 1) * 2; + } + + rate = (gen2_clk_get_rate(&parent) * mult) / info->pll0_div; + debug("%s[%i] PLL0 clk: parent=%i mult=%u => rate=%u\n", + __func__, __LINE__, core->parent, mult, rate); + return rate; + + case CLK_TYPE_GEN2_PLL1: + rate = (gen2_clk_get_rate(&parent) * pll_config->pll1_mult) / 2; + debug("%s[%i] PLL1 clk: parent=%i mul=%i => rate=%u\n", + __func__, __LINE__, + core->parent, pll_config->pll1_mult, rate); + return rate; + + case CLK_TYPE_GEN2_PLL3: + rate = gen2_clk_get_rate(&parent) * pll_config->pll3_mult; + debug("%s[%i] PLL3 clk: parent=%i mul=%i => rate=%u\n", + __func__, __LINE__, + core->parent, pll_config->pll3_mult, rate); + return rate; + + case CLK_TYPE_GEN2_SDH: + value = (readl(priv->base + CPG_SDCKCR) >> 8) & 0xf; + div = gen2_clk_get_sdh_div(cpg_sdh_div_table, value); + rate = gen2_clk_get_rate(&parent) / div; + debug("%s[%i] SDH clk: parent=%i div=%i => rate=%u\n", + __func__, __LINE__, + core->parent, div, rate); + return rate; + + case CLK_TYPE_GEN2_SD0: + value = (readl(priv->base + CPG_SDCKCR) >> 4) & 0xf; + div = gen2_clk_get_sdh_div(cpg_sd01_div_table, value); + rate = gen2_clk_get_rate(&parent) / div; + debug("%s[%i] SD0 clk: parent=%i div=%i => rate=%u\n", + __func__, __LINE__, + core->parent, div, rate); + return rate; + + case CLK_TYPE_GEN2_SD1: + value = (readl(priv->base + CPG_SDCKCR) >> 0) & 0xf; + div = gen2_clk_get_sdh_div(cpg_sd01_div_table, value); + rate = gen2_clk_get_rate(&parent) / div; + debug("%s[%i] SD1 clk: parent=%i div=%i => rate=%u\n", + __func__, __LINE__, + core->parent, div, rate); + return rate; + } + + printf("%s[%i] unknown fail\n", __func__, __LINE__); + + return -ENOENT; +} + +static ulong gen2_clk_set_rate(struct clk *clk, ulong rate) +{ + return gen2_clk_get_rate(clk); +} + +static int gen2_clk_of_xlate(struct clk *clk, struct ofnode_phandle_args *args) +{ + if (args->args_count != 2) { + debug("Invaild args_count: %d\n", args->args_count); + return -EINVAL; + } + + clk->id = (args->args[0] << 16) | args->args[1]; + + return 0; +} + +const struct clk_ops gen2_clk_ops = { + .enable = gen2_clk_enable, + .disable = gen2_clk_disable, + .get_rate = gen2_clk_get_rate, + .set_rate = gen2_clk_set_rate, + .of_xlate = gen2_clk_of_xlate, +}; + +int gen2_clk_probe(struct udevice *dev) +{ + struct gen2_clk_priv *priv = dev_get_priv(dev); + struct cpg_mssr_info *info = + (struct cpg_mssr_info *)dev_get_driver_data(dev); + fdt_addr_t rst_base; + u32 cpg_mode; + int ret; + + priv->base = (struct gen2_base *)devfdt_get_addr(dev); + if (!priv->base) + return -EINVAL; + + priv->info = info; + ret = fdt_node_offset_by_compatible(gd->fdt_blob, -1, info->reset_node); + if (ret < 0) + return ret; + + rst_base = fdtdec_get_addr(gd->fdt_blob, ret, "reg"); + if (rst_base == FDT_ADDR_T_NONE) + return -EINVAL; + + cpg_mode = readl(rst_base + CPG_RST_MODEMR); + + priv->cpg_pll_config = + (struct rcar_gen2_cpg_pll_config *)info->get_pll_config(cpg_mode); + if (!priv->cpg_pll_config->extal_div) + return -EINVAL; + + ret = clk_get_by_name(dev, "extal", &priv->clk_extal); + if (ret < 0) + return ret; + + if (info->extal_usb_node) { + ret = clk_get_by_name(dev, info->extal_usb_node, + &priv->clk_extal_usb); + if (ret < 0) + return ret; + } + + return 0; +} + +int gen2_clk_remove(struct udevice *dev) +{ + struct gen2_clk_priv *priv = dev_get_priv(dev); + + return renesas_clk_remove(priv->base, priv->info); +} diff --git a/drivers/clk/renesas/rcar-gen2-cpg.h b/drivers/clk/renesas/rcar-gen2-cpg.h new file mode 100644 index 0000000000..913c932620 --- /dev/null +++ b/drivers/clk/renesas/rcar-gen2-cpg.h @@ -0,0 +1,49 @@ +/* + * R-Car Gen2 Clock Pulse Generator + * + * Copyright (C) 2016 Cogent Embedded Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation; version 2 of the License. + */ + +#ifndef __CLK_RENESAS_RCAR_GEN2_CPG_H__ +#define __CLK_RENESAS_RCAR_GEN2_CPG_H__ + +enum rcar_gen2_clk_types { + CLK_TYPE_GEN2_MAIN = CLK_TYPE_CUSTOM, + CLK_TYPE_GEN2_PLL0, + CLK_TYPE_GEN2_PLL1, + CLK_TYPE_GEN2_PLL3, + CLK_TYPE_GEN2_Z, + CLK_TYPE_GEN2_LB, + CLK_TYPE_GEN2_ADSP, + CLK_TYPE_GEN2_SDH, + CLK_TYPE_GEN2_SD0, + CLK_TYPE_GEN2_SD1, + CLK_TYPE_GEN2_QSPI, + CLK_TYPE_GEN2_RCAN, +}; + +struct rcar_gen2_cpg_pll_config { + unsigned int extal_div; + unsigned int pll1_mult; + unsigned int pll3_mult; + unsigned int pll0_mult; /* leave as zero if PLL0CR exists */ +}; + +struct gen2_clk_priv { + void __iomem *base; + struct cpg_mssr_info *info; + struct clk clk_extal; + struct clk clk_extal_usb; + const struct rcar_gen2_cpg_pll_config *cpg_pll_config; +}; + +int gen2_clk_probe(struct udevice *dev); +int gen2_clk_remove(struct udevice *dev); + +extern const struct clk_ops gen2_clk_ops; + +#endif diff --git a/drivers/clk/renesas/renesas-cpg-mssr.h b/drivers/clk/renesas/renesas-cpg-mssr.h index 2bd98bc510..c19a466be1 100644 --- a/drivers/clk/renesas/renesas-cpg-mssr.h +++ b/drivers/clk/renesas/renesas-cpg-mssr.h @@ -23,9 +23,12 @@ struct cpg_mssr_info { unsigned int mstp_table_size; const char *reset_node; const char *extalr_node; + const char *extal_usb_node; unsigned int mod_clk_base; unsigned int clk_extal_id; unsigned int clk_extalr_id; + unsigned int clk_extal_usb_id; + unsigned int pll0_div; const void *(*get_pll_config)(const u32 cpg_mode); }; |