diff options
Diffstat (limited to 'drivers/clk')
-rw-r--r-- | drivers/clk/renesas/Kconfig | 4 | ||||
-rw-r--r-- | drivers/clk/renesas/Makefile | 1 | ||||
-rw-r--r-- | drivers/clk/renesas/clk-rcar-gen3.c | 145 | ||||
-rw-r--r-- | drivers/clk/renesas/rcar-cpg-lib.c | 169 | ||||
-rw-r--r-- | drivers/clk/renesas/rcar-cpg-lib.h | 33 |
5 files changed, 230 insertions, 122 deletions
diff --git a/drivers/clk/renesas/Kconfig b/drivers/clk/renesas/Kconfig index a538e7e7aa0..1686410d6d3 100644 --- a/drivers/clk/renesas/Kconfig +++ b/drivers/clk/renesas/Kconfig @@ -4,6 +4,9 @@ config CLK_RENESAS help Enable support for clock present on Renesas RCar SoCs. +config CLK_RCAR_CPG_LIB + bool "CPG/MSSR library functions" + config CLK_RCAR_GEN2 bool "Renesas RCar Gen2 clock driver" def_bool y if RCAR_32 @@ -45,6 +48,7 @@ config CLK_RCAR_GEN3 bool "Renesas RCar Gen3 clock driver" def_bool y if RCAR_GEN3 depends on CLK_RENESAS + select CLK_RCAR_CPG_LIB help Enable this to support the clocks on Renesas RCar Gen3 SoC. diff --git a/drivers/clk/renesas/Makefile b/drivers/clk/renesas/Makefile index df6bbc20bc3..8f82a7aa3e0 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_CPG_LIB) += rcar-cpg-lib.o obj-$(CONFIG_CLK_RCAR_GEN2) += clk-rcar-gen2.o obj-$(CONFIG_CLK_R8A774A1) += r8a774a1-cpg-mssr.o obj-$(CONFIG_CLK_R8A774B1) += r8a774b1-cpg-mssr.o diff --git a/drivers/clk/renesas/clk-rcar-gen3.c b/drivers/clk/renesas/clk-rcar-gen3.c index f8a23623223..d778db6569d 100644 --- a/drivers/clk/renesas/clk-rcar-gen3.c +++ b/drivers/clk/renesas/clk-rcar-gen3.c @@ -29,34 +29,16 @@ #include "renesas-cpg-mssr.h" #include "rcar-gen3-cpg.h" +#include "rcar-cpg-lib.h" #define CPG_PLL0CR 0x00d8 #define CPG_PLL2CR 0x002c #define CPG_PLL4CR 0x01f4 -#define SDnSRCFC_SHIFT 2 -#define STPnHCK_TABLE (CPG_SDCKCR_STPnHCK >> SDnSRCFC_SHIFT) - -/* Non-constant mask variant of FIELD_GET/FIELD_PREP */ -#define field_get(_mask, _reg) (((_reg) & (_mask)) >> (ffs(_mask) - 1)) - static const struct clk_div_table cpg_rpcsrc_div_table[] = { { 2, 5 }, { 3, 6 }, { 0, 0 }, }; -static const struct clk_div_table cpg_rpc_div_table[] = { - { 1, 2 }, { 3, 4 }, { 5, 6 }, { 7, 8 }, { 0, 0 }, -}; - -static const struct clk_div_table cpg_sdh_div_table[] = { - { 0, 1 }, { 1, 2 }, { STPnHCK_TABLE | 2, 4 }, { STPnHCK_TABLE | 3, 8 }, - { STPnHCK_TABLE | 4, 16 }, { 0, 0 }, -}; - -static const struct clk_div_table cpg_sd_div_table[] = { - { 0, 2 }, { 1, 4 }, { 0, 0 }, -}; - static const struct clk_div_table r8a77970_cpg_sd0h_div_table[] = { { 0, 2 }, { 1, 3 }, { 2, 4 }, { 3, 6 }, { 4, 8 }, { 5, 12 }, { 6, 16 }, { 7, 18 }, @@ -69,48 +51,6 @@ static const struct clk_div_table r8a77970_cpg_sd0_div_table[] = { { 0, 0 }, }; -static unsigned int rcar_clk_get_table_div(const struct clk_div_table *table, - const u32 value) -{ - const struct clk_div_table *clkt; - - for (clkt = table; clkt->div; clkt++) - if (clkt->val == value) - return clkt->div; - return 0; -} - -static int rcar_clk_get_table_val(const struct clk_div_table *table, - unsigned int div) -{ - const struct clk_div_table *clkt; - - for (clkt = table; clkt->div; clkt++) - if (clkt->div == div) - return clkt->val; - return -EINVAL; -} - -static __always_inline s64 -rcar_clk_get_rate64_div_table(unsigned int parent, u64 parent_rate, - void __iomem *reg, const u32 mask, - const struct clk_div_table *table, char *name) -{ - u32 value, div; - u64 rate; - - value = field_get(mask, readl(reg)); - div = rcar_clk_get_table_div(table, value); - if (!div) - return -EINVAL; - - rate = parent_rate / div; - debug("%s[%i] %s clk: parent=%i div=%u => rate=%llu\n", - __func__, __LINE__, name, parent, div, rate); - - return rate; -} - static int gen3_clk_get_parent(struct gen3_clk_priv *priv, struct clk *clk, struct cpg_mssr_info *info, struct clk *parent) { @@ -156,7 +96,6 @@ static int gen3_clk_setup_sdif_div(struct clk *clk, ulong rate) const struct cpg_core_clk *core; struct clk parent, grandparent; int ret; - u32 value = 0, div = 0; /* * The clk may be either CPG_MOD or core clock, in case this is MOD @@ -191,45 +130,23 @@ static int gen3_clk_setup_sdif_div(struct clk *clk, ulong rate) case CLK_TYPE_GEN3_SDH: fallthrough; case CLK_TYPE_GEN4_SDH: - div = DIV_ROUND_CLOSEST(gen3_clk_get_rate64(&grandparent), rate); - value = rcar_clk_get_table_val(cpg_sdh_div_table, div); - if (value < 0) - return value; - - clrsetbits_le32(priv->base + core->offset, - GENMASK(9, 2), value << 2); - - debug("%s[%i] SDH clk: parent=%i offset=%x div=%u rate=%lu => val=%u\n", - __func__, __LINE__, core->parent, core->offset, div, rate, value); - break; + return rcar_clk_set_rate64_sdh(core->parent, + gen3_clk_get_rate64(&grandparent), + rate, priv->base + core->offset); case CLK_TYPE_GEN3_SD: fallthrough; case CLK_TYPE_GEN4_SD: - div = DIV_ROUND_CLOSEST(gen3_clk_get_rate64(&grandparent), rate); - value = rcar_clk_get_table_val(cpg_sd_div_table, div); - if (value < 0) - return value; - - clrsetbits_le32(priv->base + core->offset, - GENMASK(1, 0), value); - - debug("%s[%i] SD clk: parent=%i offset=%x div=%u rate=%lu => val=%u\n", - __func__, __LINE__, core->parent, core->offset, div, rate, value); - break; + return rcar_clk_set_rate64_sd(core->parent, + gen3_clk_get_rate64(&grandparent), + rate, priv->base + core->offset); case CLK_TYPE_R8A77970_SD0: - div = gen3_clk_get_rate64(&grandparent) / rate; - value = rcar_clk_get_table_val(cpg_sd_div_table, div); - if (!value) - return -EINVAL; - - clrsetbits_le32(priv->base + core->offset, - GENMASK(7, 4), value << 4); - - debug("%s[%i] SD clk: parent=%i offset=%x div=%u rate=%lu => val=%u\n", - __func__, __LINE__, core->parent, core->offset, div, rate, value); - break; + return rcar_clk_set_rate64_div_table(core->parent, + gen3_clk_get_rate64(&grandparent), + rate, priv->base + core->offset, + CPG_SDCKCR_SD0FC_MASK, + r8a77970_cpg_sd0_div_table, "SD"); } return 0; @@ -371,17 +288,9 @@ static u64 gen3_clk_get_rate64(struct clk *clk) case CLK_TYPE_GEN3_SDH: /* Fixed factor 1:1 */ fallthrough; case CLK_TYPE_GEN4_SDH: /* Fixed factor 1:1 */ - /* - * This takes STPnHCK and STPnCK bits into consideration - * in the table look up too, hence the inobvious GENMASK - * below. Bits [7:5] always read zero, so this is OKish. - */ - return rcar_clk_get_rate64_div_table(core->parent, - gen3_clk_get_rate64(&parent), - priv->base + core->offset, - CPG_SDCKCR_SRCFC_MASK | - GENMASK(9, 5), - cpg_sdh_div_table, "SDH"); + return rcar_clk_get_rate64_sdh(core->parent, + gen3_clk_get_rate64(&parent), + priv->base + core->offset); case CLK_TYPE_R8A77970_SD0H: return rcar_clk_get_rate64_div_table(core->parent, @@ -393,11 +302,9 @@ static u64 gen3_clk_get_rate64(struct clk *clk) case CLK_TYPE_GEN3_SD: fallthrough; case CLK_TYPE_GEN4_SD: - return rcar_clk_get_rate64_div_table(core->parent, - gen3_clk_get_rate64(&parent), - priv->base + core->offset, - CPG_SDCKCR_FC_MASK, - cpg_sd_div_table, "SD"); + return rcar_clk_get_rate64_sd(core->parent, + gen3_clk_get_rate64(&parent), + priv->base + core->offset); case CLK_TYPE_R8A77970_SD0: return rcar_clk_get_rate64_div_table(core->parent, @@ -446,20 +353,14 @@ static u64 gen3_clk_get_rate64(struct clk *clk) case CLK_TYPE_GEN3_RPC: case CLK_TYPE_GEN4_RPC: - return rcar_clk_get_rate64_div_table(core->parent, - gen3_clk_get_rate64(&parent), - priv->base + CPG_RPCCKCR, - CPG_RPCCKCR_DIV_PRE_MASK, - cpg_rpc_div_table, "RPC"); + return rcar_clk_get_rate64_rpc(core->parent, + gen3_clk_get_rate64(&parent), + priv->base + CPG_RPCCKCR); case CLK_TYPE_GEN3_RPCD2: case CLK_TYPE_GEN4_RPCD2: - rate = gen3_clk_get_rate64(&parent) / 2; - - debug("%s[%i] RPCD2 clk: parent=%i => rate=%llu\n", - __func__, __LINE__, core->parent, rate); - - return rate; + return rcar_clk_get_rate64_rpcd2(core->parent, + gen3_clk_get_rate64(&parent)); } diff --git a/drivers/clk/renesas/rcar-cpg-lib.c b/drivers/clk/renesas/rcar-cpg-lib.c new file mode 100644 index 00000000000..a2fca660a8e --- /dev/null +++ b/drivers/clk/renesas/rcar-cpg-lib.c @@ -0,0 +1,169 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Renesas RCar Gen3 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 + */ + +#include <common.h> +#include <clk-uclass.h> +#include <dm.h> +#include <errno.h> +#include <log.h> +#include <wait_bit.h> +#include <asm/global_data.h> +#include <asm/io.h> +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/clk-provider.h> + +#include <dt-bindings/clock/renesas-cpg-mssr.h> + +#include "renesas-cpg-mssr.h" +#include "rcar-gen3-cpg.h" +#include "rcar-cpg-lib.h" + +#define SDnSRCFC_SHIFT 2 +#define STPnHCK_TABLE (CPG_SDCKCR_STPnHCK >> SDnSRCFC_SHIFT) + +/* Non-constant mask variant of FIELD_GET/FIELD_PREP */ +#define field_get(_mask, _reg) (((_reg) & (_mask)) >> (ffs(_mask) - 1)) +#define field_prep(_mask, _val) (((_val) << (ffs(_mask) - 1)) & (_mask)) + +static const struct clk_div_table cpg_sdh_div_table[] = { + { 0, 1 }, { 1, 2 }, { STPnHCK_TABLE | 2, 4 }, { STPnHCK_TABLE | 3, 8 }, + { STPnHCK_TABLE | 4, 16 }, { 0, 0 }, +}; + +static const struct clk_div_table cpg_sd_div_table[] = { + { 0, 2 }, { 1, 4 }, { 0, 0 }, +}; + +static const struct clk_div_table cpg_rpc_div_table[] = { + { 1, 2 }, { 3, 4 }, { 5, 6 }, { 7, 8 }, { 0, 0 }, +}; + +static unsigned int rcar_clk_get_table_div(const struct clk_div_table *table, + const u32 value) +{ + const struct clk_div_table *clkt; + + for (clkt = table; clkt->div; clkt++) + if (clkt->val == value) + return clkt->div; + return 0; +} + +static int rcar_clk_get_table_val(const struct clk_div_table *table, + unsigned int div) +{ + const struct clk_div_table *clkt; + + for (clkt = table; clkt->div; clkt++) + if (clkt->div == div) + return clkt->val; + return -EINVAL; +} + +s64 rcar_clk_get_rate64_div_table(unsigned int parent, u64 parent_rate, + void __iomem *reg, const u32 mask, + const struct clk_div_table *table, char *name) +{ + u32 value, div; + u64 rate; + + value = field_get(mask, readl(reg)); + div = rcar_clk_get_table_div(table, value); + if (!div) + return -EINVAL; + + rate = parent_rate / div; + debug("%s[%i] %s clk: parent=%i div=%u => rate=%llu\n", + __func__, __LINE__, name, parent, div, rate); + + return rate; +} + +int rcar_clk_set_rate64_div_table(unsigned int parent, u64 parent_rate, ulong rate, + void __iomem *reg, const u32 mask, + const struct clk_div_table *table, char *name) +{ + u32 value = 0, div = 0; + + div = DIV_ROUND_CLOSEST(parent_rate, rate); + value = rcar_clk_get_table_val(table, div); + if (value < 0) + return value; + + clrsetbits_le32(reg, mask, field_prep(mask, value)); + + debug("%s[%i] %s clk: parent=%i div=%u rate=%lu => val=%u\n", + __func__, __LINE__, name, parent, div, rate, value); + + return 0; +} + +s64 rcar_clk_get_rate64_rpc(unsigned int parent, u64 parent_rate, void __iomem *reg) +{ + return rcar_clk_get_rate64_div_table(parent, parent_rate, reg, + CPG_RPCCKCR_DIV_PRE_MASK, + cpg_rpc_div_table, "RPC"); +} + +u64 rcar_clk_get_rate64_rpcd2(unsigned int parent, u64 parent_rate) +{ + u64 rate = 0; + + rate = parent_rate / 2; + debug("%s[%i] RPCD2 clk: parent=%i => rate=%llu\n", + __func__, __LINE__, parent, rate); + + return rate; +} + +s64 rcar_clk_get_rate64_sdh(unsigned int parent, u64 parent_rate, void __iomem *reg) +{ + /* + * This takes STPnHCK and STPnCK bits into consideration + * in the table look up too, hence the inobvious GENMASK + * below. Bits [7:5] always read zero, so this is OKish. + */ + return rcar_clk_get_rate64_div_table(parent, parent_rate, reg, + CPG_SDCKCR_SRCFC_MASK | + GENMASK(9, 5), + cpg_sdh_div_table, "SDH"); +} + +s64 rcar_clk_get_rate64_sd(unsigned int parent, u64 parent_rate, void __iomem *reg) +{ + return rcar_clk_get_rate64_div_table(parent, parent_rate, reg, + CPG_SDCKCR_FC_MASK, + cpg_sd_div_table, "SD"); +} + +int rcar_clk_set_rate64_sdh(unsigned int parent, u64 parent_rate, ulong rate, + void __iomem *reg) +{ + /* + * This takes STPnHCK and STPnCK bits into consideration + * in the table look up too, hence the inobvious GENMASK + * below. Bits [7:5] always read zero, so this is OKish. + */ + return rcar_clk_set_rate64_div_table(parent, parent_rate, rate, reg, + CPG_SDCKCR_SRCFC_MASK | + GENMASK(9, 5), + cpg_sdh_div_table, "SDH"); +} + +int rcar_clk_set_rate64_sd(unsigned int parent, u64 parent_rate, ulong rate, + void __iomem *reg) +{ + return rcar_clk_set_rate64_div_table(parent, parent_rate, rate, reg, + CPG_SDCKCR_FC_MASK, + cpg_sd_div_table, "SD"); +} diff --git a/drivers/clk/renesas/rcar-cpg-lib.h b/drivers/clk/renesas/rcar-cpg-lib.h new file mode 100644 index 00000000000..09b3e50918b --- /dev/null +++ b/drivers/clk/renesas/rcar-cpg-lib.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * R-Car Gen3 Clock Pulse Generator Library + * + * Copyright (C) 2015-2018 Glider bvba + * Copyright (C) 2019 Renesas Electronics Corp. + * + * Based on clk-rcar-gen3.c + * + * Copyright (C) 2015 Renesas Electronics Corp. + */ + +#ifndef __CLK_RENESAS_RCAR_CPG_LIB_H__ +#define __CLK_RENESAS_RCAR_CPG_LIB_H__ + +s64 rcar_clk_get_rate64_div_table(unsigned int parent, u64 parent_rate, + void __iomem *reg, const u32 mask, + const struct clk_div_table *table, char *name); + +int rcar_clk_set_rate64_div_table(unsigned int parent, u64 parent_rate, ulong rate, + void __iomem *reg, const u32 mask, + const struct clk_div_table *table, char *name); + +s64 rcar_clk_get_rate64_sdh(unsigned int parent, u64 parent_rate, void __iomem *reg); +s64 rcar_clk_get_rate64_sd(unsigned int parent, u64 parent_rate, void __iomem *reg); +s64 rcar_clk_get_rate64_rpc(unsigned int parent, u64 parent_rate, void __iomem *reg); +u64 rcar_clk_get_rate64_rpcd2(unsigned int parent, u64 parent_rate); +int rcar_clk_set_rate64_sdh(unsigned int parent, u64 parent_rate, ulong rate, + void __iomem *reg); +int rcar_clk_set_rate64_sd(unsigned int parent, u64 parent_rate, ulong rate, + void __iomem *reg); + +#endif |