diff options
author | Simon Horman <horms+renesas@verge.net.au> | 2016-03-03 11:18:06 +0900 |
---|---|---|
committer | Stephen Boyd <sboyd@codeaurora.org> | 2016-03-03 11:22:53 -0800 |
commit | b3a33077c0ddb9819398091c70e5999afa8a4526 (patch) | |
tree | 8e4ed4261483a175a4b5bcbe60db17714872f775 /drivers/clk/renesas | |
parent | 27fbd266de34af6eb2759249696bd2b4da7c8434 (diff) | |
download | linux-b3a33077c0ddb9819398091c70e5999afa8a4526.tar.gz linux-b3a33077c0ddb9819398091c70e5999afa8a4526.tar.bz2 linux-b3a33077c0ddb9819398091c70e5999afa8a4526.zip |
clk: renesas: move drivers to renesas directory
This is part of an ongoing process to migrate from ARCH_SHMOBILE to
ARCH_RENESAS the motivation for which being that RENESAS seems to be a more
appropriate name than SHMOBILE for the majority of Renesas ARM based SoCs.
Along with the above mentioned Kconfig changes it seems appropriate
to also rename directories that only hold drivers for such SoCs.
Signed-off-by: Simon Horman <horms+renesas@verge.net.au>
Acked-by: Geert Uytterhoeven <geert+renesas@glider.be>
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
Diffstat (limited to 'drivers/clk/renesas')
-rw-r--r-- | drivers/clk/renesas/Makefile | 13 | ||||
-rw-r--r-- | drivers/clk/renesas/clk-div6.c | 318 | ||||
-rw-r--r-- | drivers/clk/renesas/clk-div6.h | 7 | ||||
-rw-r--r-- | drivers/clk/renesas/clk-emev2.c | 110 | ||||
-rw-r--r-- | drivers/clk/renesas/clk-mstp.c | 329 | ||||
-rw-r--r-- | drivers/clk/renesas/clk-r8a73a4.c | 241 | ||||
-rw-r--r-- | drivers/clk/renesas/clk-r8a7740.c | 199 | ||||
-rw-r--r-- | drivers/clk/renesas/clk-r8a7778.c | 145 | ||||
-rw-r--r-- | drivers/clk/renesas/clk-r8a7779.c | 182 | ||||
-rw-r--r-- | drivers/clk/renesas/clk-rcar-gen2.c | 429 | ||||
-rw-r--r-- | drivers/clk/renesas/clk-rz.c | 106 | ||||
-rw-r--r-- | drivers/clk/renesas/clk-sh73a0.c | 218 | ||||
-rw-r--r-- | drivers/clk/renesas/r8a7795-cpg-mssr.c | 638 | ||||
-rw-r--r-- | drivers/clk/renesas/renesas-cpg-mssr.c | 601 | ||||
-rw-r--r-- | drivers/clk/renesas/renesas-cpg-mssr.h | 134 |
15 files changed, 3670 insertions, 0 deletions
diff --git a/drivers/clk/renesas/Makefile b/drivers/clk/renesas/Makefile new file mode 100644 index 000000000000..7e2579b30326 --- /dev/null +++ b/drivers/clk/renesas/Makefile @@ -0,0 +1,13 @@ +obj-$(CONFIG_ARCH_EMEV2) += clk-emev2.o +obj-$(CONFIG_ARCH_R7S72100) += clk-rz.o clk-mstp.o +obj-$(CONFIG_ARCH_R8A73A4) += clk-r8a73a4.o clk-mstp.o clk-div6.o +obj-$(CONFIG_ARCH_R8A7740) += clk-r8a7740.o clk-mstp.o clk-div6.o +obj-$(CONFIG_ARCH_R8A7778) += clk-r8a7778.o clk-mstp.o +obj-$(CONFIG_ARCH_R8A7779) += clk-r8a7779.o clk-mstp.o +obj-$(CONFIG_ARCH_R8A7790) += clk-rcar-gen2.o clk-mstp.o clk-div6.o +obj-$(CONFIG_ARCH_R8A7791) += clk-rcar-gen2.o clk-mstp.o clk-div6.o +obj-$(CONFIG_ARCH_R8A7793) += clk-rcar-gen2.o clk-mstp.o clk-div6.o +obj-$(CONFIG_ARCH_R8A7794) += clk-rcar-gen2.o clk-mstp.o clk-div6.o +obj-$(CONFIG_ARCH_R8A7795) += renesas-cpg-mssr.o \ + r8a7795-cpg-mssr.o clk-div6.o +obj-$(CONFIG_ARCH_SH73A0) += clk-sh73a0.o clk-mstp.o clk-div6.o diff --git a/drivers/clk/renesas/clk-div6.c b/drivers/clk/renesas/clk-div6.c new file mode 100644 index 000000000000..0627860233cb --- /dev/null +++ b/drivers/clk/renesas/clk-div6.c @@ -0,0 +1,318 @@ +/* + * r8a7790 Common Clock Framework support + * + * Copyright (C) 2013 Renesas Solutions Corp. + * + * Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + */ + +#include <linux/clk-provider.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/slab.h> + +#include "clk-div6.h" + +#define CPG_DIV6_CKSTP BIT(8) +#define CPG_DIV6_DIV(d) ((d) & 0x3f) +#define CPG_DIV6_DIV_MASK 0x3f + +/** + * struct div6_clock - CPG 6 bit divider clock + * @hw: handle between common and hardware-specific interfaces + * @reg: IO-remapped register + * @div: divisor value (1-64) + */ +struct div6_clock { + struct clk_hw hw; + void __iomem *reg; + unsigned int div; + u32 src_shift; + u32 src_width; + u8 *parents; +}; + +#define to_div6_clock(_hw) container_of(_hw, struct div6_clock, hw) + +static int cpg_div6_clock_enable(struct clk_hw *hw) +{ + struct div6_clock *clock = to_div6_clock(hw); + u32 val; + + val = (clk_readl(clock->reg) & ~(CPG_DIV6_DIV_MASK | CPG_DIV6_CKSTP)) + | CPG_DIV6_DIV(clock->div - 1); + clk_writel(val, clock->reg); + + return 0; +} + +static void cpg_div6_clock_disable(struct clk_hw *hw) +{ + struct div6_clock *clock = to_div6_clock(hw); + u32 val; + + val = clk_readl(clock->reg); + val |= CPG_DIV6_CKSTP; + /* + * DIV6 clocks require the divisor field to be non-zero when stopping + * the clock. However, some clocks (e.g. ZB on sh73a0) fail to be + * re-enabled later if the divisor field is changed when stopping the + * clock + */ + if (!(val & CPG_DIV6_DIV_MASK)) + val |= CPG_DIV6_DIV_MASK; + clk_writel(val, clock->reg); +} + +static int cpg_div6_clock_is_enabled(struct clk_hw *hw) +{ + struct div6_clock *clock = to_div6_clock(hw); + + return !(clk_readl(clock->reg) & CPG_DIV6_CKSTP); +} + +static unsigned long cpg_div6_clock_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct div6_clock *clock = to_div6_clock(hw); + + return parent_rate / clock->div; +} + +static unsigned int cpg_div6_clock_calc_div(unsigned long rate, + unsigned long parent_rate) +{ + unsigned int div; + + if (!rate) + rate = 1; + + div = DIV_ROUND_CLOSEST(parent_rate, rate); + return clamp_t(unsigned int, div, 1, 64); +} + +static long cpg_div6_clock_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + unsigned int div = cpg_div6_clock_calc_div(rate, *parent_rate); + + return *parent_rate / div; +} + +static int cpg_div6_clock_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct div6_clock *clock = to_div6_clock(hw); + unsigned int div = cpg_div6_clock_calc_div(rate, parent_rate); + u32 val; + + clock->div = div; + + val = clk_readl(clock->reg) & ~CPG_DIV6_DIV_MASK; + /* Only program the new divisor if the clock isn't stopped. */ + if (!(val & CPG_DIV6_CKSTP)) + clk_writel(val | CPG_DIV6_DIV(clock->div - 1), clock->reg); + + return 0; +} + +static u8 cpg_div6_clock_get_parent(struct clk_hw *hw) +{ + struct div6_clock *clock = to_div6_clock(hw); + unsigned int i; + u8 hw_index; + + if (clock->src_width == 0) + return 0; + + hw_index = (clk_readl(clock->reg) >> clock->src_shift) & + (BIT(clock->src_width) - 1); + for (i = 0; i < clk_hw_get_num_parents(hw); i++) { + if (clock->parents[i] == hw_index) + return i; + } + + pr_err("%s: %s DIV6 clock set to invalid parent %u\n", + __func__, clk_hw_get_name(hw), hw_index); + return 0; +} + +static int cpg_div6_clock_set_parent(struct clk_hw *hw, u8 index) +{ + struct div6_clock *clock = to_div6_clock(hw); + u8 hw_index; + u32 mask; + + if (index >= clk_hw_get_num_parents(hw)) + return -EINVAL; + + mask = ~((BIT(clock->src_width) - 1) << clock->src_shift); + hw_index = clock->parents[index]; + + clk_writel((clk_readl(clock->reg) & mask) | + (hw_index << clock->src_shift), clock->reg); + + return 0; +} + +static const struct clk_ops cpg_div6_clock_ops = { + .enable = cpg_div6_clock_enable, + .disable = cpg_div6_clock_disable, + .is_enabled = cpg_div6_clock_is_enabled, + .get_parent = cpg_div6_clock_get_parent, + .set_parent = cpg_div6_clock_set_parent, + .recalc_rate = cpg_div6_clock_recalc_rate, + .round_rate = cpg_div6_clock_round_rate, + .set_rate = cpg_div6_clock_set_rate, +}; + + +/** + * cpg_div6_register - Register a DIV6 clock + * @name: Name of the DIV6 clock + * @num_parents: Number of parent clocks of the DIV6 clock (1, 4, or 8) + * @parent_names: Array containing the names of the parent clocks + * @reg: Mapped register used to control the DIV6 clock + */ +struct clk * __init cpg_div6_register(const char *name, + unsigned int num_parents, + const char **parent_names, + void __iomem *reg) +{ + unsigned int valid_parents; + struct clk_init_data init; + struct div6_clock *clock; + struct clk *clk; + unsigned int i; + + clock = kzalloc(sizeof(*clock), GFP_KERNEL); + if (!clock) + return ERR_PTR(-ENOMEM); + + clock->parents = kmalloc_array(num_parents, sizeof(*clock->parents), + GFP_KERNEL); + if (!clock->parents) { + clk = ERR_PTR(-ENOMEM); + goto free_clock; + } + + clock->reg = reg; + + /* + * Read the divisor. Disabling the clock overwrites the divisor, so we + * need to cache its value for the enable operation. + */ + clock->div = (clk_readl(clock->reg) & CPG_DIV6_DIV_MASK) + 1; + + switch (num_parents) { + case 1: + /* fixed parent clock */ + clock->src_shift = clock->src_width = 0; + break; + case 4: + /* clock with EXSRC bits 6-7 */ + clock->src_shift = 6; + clock->src_width = 2; + break; + case 8: + /* VCLK with EXSRC bits 12-14 */ + clock->src_shift = 12; + clock->src_width = 3; + break; + default: + pr_err("%s: invalid number of parents for DIV6 clock %s\n", + __func__, name); + clk = ERR_PTR(-EINVAL); + goto free_parents; + } + + /* Filter out invalid parents */ + for (i = 0, valid_parents = 0; i < num_parents; i++) { + if (parent_names[i]) { + parent_names[valid_parents] = parent_names[i]; + clock->parents[valid_parents] = i; + valid_parents++; + } + } + + /* Register the clock. */ + init.name = name; + init.ops = &cpg_div6_clock_ops; + init.flags = CLK_IS_BASIC; + init.parent_names = parent_names; + init.num_parents = valid_parents; + + clock->hw.init = &init; + + clk = clk_register(NULL, &clock->hw); + if (IS_ERR(clk)) + goto free_parents; + + return clk; + +free_parents: + kfree(clock->parents); +free_clock: + kfree(clock); + return clk; +} + +static void __init cpg_div6_clock_init(struct device_node *np) +{ + unsigned int num_parents; + const char **parent_names; + const char *clk_name = np->name; + void __iomem *reg; + struct clk *clk; + unsigned int i; + + num_parents = of_clk_get_parent_count(np); + if (num_parents < 1) { + pr_err("%s: no parent found for %s DIV6 clock\n", + __func__, np->name); + return; + } + + parent_names = kmalloc_array(num_parents, sizeof(*parent_names), + GFP_KERNEL); + if (!parent_names) + return; + + reg = of_iomap(np, 0); + if (reg == NULL) { + pr_err("%s: failed to map %s DIV6 clock register\n", + __func__, np->name); + goto error; + } + + /* Parse the DT properties. */ + of_property_read_string(np, "clock-output-names", &clk_name); + + for (i = 0; i < num_parents; i++) + parent_names[i] = of_clk_get_parent_name(np, i); + + clk = cpg_div6_register(clk_name, num_parents, parent_names, reg); + if (IS_ERR(clk)) { + pr_err("%s: failed to register %s DIV6 clock (%ld)\n", + __func__, np->name, PTR_ERR(clk)); + goto error; + } + + of_clk_add_provider(np, of_clk_src_simple_get, clk); + + kfree(parent_names); + return; + +error: + if (reg) + iounmap(reg); + kfree(parent_names); +} +CLK_OF_DECLARE(cpg_div6_clk, "renesas,cpg-div6-clock", cpg_div6_clock_init); diff --git a/drivers/clk/renesas/clk-div6.h b/drivers/clk/renesas/clk-div6.h new file mode 100644 index 000000000000..9a85a95188da --- /dev/null +++ b/drivers/clk/renesas/clk-div6.h @@ -0,0 +1,7 @@ +#ifndef __SHMOBILE_CLK_DIV6_H__ +#define __SHMOBILE_CLK_DIV6_H__ + +struct clk *cpg_div6_register(const char *name, unsigned int num_parents, + const char **parent_names, void __iomem *reg); + +#endif diff --git a/drivers/clk/renesas/clk-emev2.c b/drivers/clk/renesas/clk-emev2.c new file mode 100644 index 000000000000..a91825471c79 --- /dev/null +++ b/drivers/clk/renesas/clk-emev2.c @@ -0,0 +1,110 @@ +/* + * EMMA Mobile EV2 common clock framework support + * + * Copyright (C) 2013 Takashi Yoshii <takashi.yoshii.ze@renesas.com> + * Copyright (C) 2012 Magnus Damm + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include <linux/clk-provider.h> +#include <linux/clkdev.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/of_address.h> + +/* EMEV2 SMU registers */ +#define USIAU0_RSTCTRL 0x094 +#define USIBU1_RSTCTRL 0x0ac +#define USIBU2_RSTCTRL 0x0b0 +#define USIBU3_RSTCTRL 0x0b4 +#define IIC0_RSTCTRL 0x0dc +#define IIC1_RSTCTRL 0x0e0 +#define STI_RSTCTRL 0x124 +#define STI_CLKSEL 0x688 + +static DEFINE_SPINLOCK(lock); + +/* not pretty, but hey */ +static void __iomem *smu_base; + +static void __init emev2_smu_write(unsigned long value, int offs) +{ + BUG_ON(!smu_base || (offs >= PAGE_SIZE)); + writel_relaxed(value, smu_base + offs); +} + +static const struct of_device_id smu_id[] __initconst = { + { .compatible = "renesas,emev2-smu", }, + {}, +}; + +static void __init emev2_smu_init(void) +{ + struct device_node *np; + + np = of_find_matching_node(NULL, smu_id); + BUG_ON(!np); + smu_base = of_iomap(np, 0); + BUG_ON(!smu_base); + of_node_put(np); + + /* setup STI timer to run on 32.768 kHz and deassert reset */ + emev2_smu_write(0, STI_CLKSEL); + emev2_smu_write(1, STI_RSTCTRL); + + /* deassert reset for UART0->UART3 */ + emev2_smu_write(2, USIAU0_RSTCTRL); + emev2_smu_write(2, USIBU1_RSTCTRL); + emev2_smu_write(2, USIBU2_RSTCTRL); + emev2_smu_write(2, USIBU3_RSTCTRL); + + /* deassert reset for IIC0->IIC1 */ + emev2_smu_write(1, IIC0_RSTCTRL); + emev2_smu_write(1, IIC1_RSTCTRL); +} + +static void __init emev2_smu_clkdiv_init(struct device_node *np) +{ + u32 reg[2]; + struct clk *clk; + const char *parent_name = of_clk_get_parent_name(np, 0); + if (WARN_ON(of_property_read_u32_array(np, "reg", reg, 2))) + return; + if (!smu_base) + emev2_smu_init(); + clk = clk_register_divider(NULL, np->name, parent_name, 0, + smu_base + reg[0], reg[1], 8, 0, &lock); + of_clk_add_provider(np, of_clk_src_simple_get, clk); + clk_register_clkdev(clk, np->name, NULL); + pr_debug("## %s %s %p\n", __func__, np->name, clk); +} +CLK_OF_DECLARE(emev2_smu_clkdiv, "renesas,emev2-smu-clkdiv", + emev2_smu_clkdiv_init); + +static void __init emev2_smu_gclk_init(struct device_node *np) +{ + u32 reg[2]; + struct clk *clk; + const char *parent_name = of_clk_get_parent_name(np, 0); + if (WARN_ON(of_property_read_u32_array(np, "reg", reg, 2))) + return; + if (!smu_base) + emev2_smu_init(); + clk = clk_register_gate(NULL, np->name, parent_name, 0, + smu_base + reg[0], reg[1], 0, &lock); + of_clk_add_provider(np, of_clk_src_simple_get, clk); + clk_register_clkdev(clk, np->name, NULL); + pr_debug("## %s %s %p\n", __func__, np->name, clk); +} +CLK_OF_DECLARE(emev2_smu_gclk, "renesas,emev2-smu-gclk", emev2_smu_gclk_init); diff --git a/drivers/clk/renesas/clk-mstp.c b/drivers/clk/renesas/clk-mstp.c new file mode 100644 index 000000000000..3b09716ebda2 --- /dev/null +++ b/drivers/clk/renesas/clk-mstp.c @@ -0,0 +1,329 @@ +/* + * R-Car MSTP clocks + * + * Copyright (C) 2013 Ideas On Board SPRL + * Copyright (C) 2015 Glider bvba + * + * Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + */ + +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/clkdev.h> +#include <linux/clk/shmobile.h> +#include <linux/device.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/pm_clock.h> +#include <linux/pm_domain.h> +#include <linux/spinlock.h> + +/* + * MSTP clocks. We can't use standard gate clocks as we need to poll on the + * status register when enabling the clock. + */ + +#define MSTP_MAX_CLOCKS 32 + +/** + * struct mstp_clock_group - MSTP gating clocks group + * + * @data: clocks in this group + * @smstpcr: module stop control register + * @mstpsr: module stop status register (optional) + * @lock: protects writes to SMSTPCR + */ +struct mstp_clock_group { + struct clk_onecell_data data; + void __iomem *smstpcr; + void __iomem *mstpsr; + spinlock_t lock; +}; + +/** + * struct mstp_clock - MSTP gating clock + * @hw: handle between common and hardware-specific interfaces + * @bit_index: control bit index + * @group: MSTP clocks group + */ +struct mstp_clock { + struct clk_hw hw; + u32 bit_index; + struct mstp_clock_group *group; +}; + +#define to_mstp_clock(_hw) container_of(_hw, struct mstp_clock, hw) + +static int cpg_mstp_clock_endisable(struct clk_hw *hw, bool enable) +{ + struct mstp_clock *clock = to_mstp_clock(hw); + struct mstp_clock_group *group = clock->group; + u32 bitmask = BIT(clock->bit_index); + unsigned long flags; + unsigned int i; + u32 value; + + spin_lock_irqsave(&group->lock, flags); + + value = clk_readl(group->smstpcr); + if (enable) + value &= ~bitmask; + else + value |= bitmask; + clk_writel(value, group->smstpcr); + + spin_unlock_irqrestore(&group->lock, flags); + + if (!enable || !group->mstpsr) + return 0; + + for (i = 1000; i > 0; --i) { + if (!(clk_readl(group->mstpsr) & bitmask)) + break; + cpu_relax(); + } + + if (!i) { + pr_err("%s: failed to enable %p[%d]\n", __func__, + group->smstpcr, clock->bit_index); + return -ETIMEDOUT; + } + + return 0; +} + +static int cpg_mstp_clock_enable(struct clk_hw *hw) +{ + return cpg_mstp_clock_endisable(hw, true); +} + +static void cpg_mstp_clock_disable(struct clk_hw *hw) +{ + cpg_mstp_clock_endisable(hw, false); +} + +static int cpg_mstp_clock_is_enabled(struct clk_hw *hw) +{ + struct mstp_clock *clock = to_mstp_clock(hw); + struct mstp_clock_group *group = clock->group; + u32 value; + + if (group->mstpsr) + value = clk_readl(group->mstpsr); + else + value = clk_readl(group->smstpcr); + + return !(value & BIT(clock->bit_index)); +} + +static const struct clk_ops cpg_mstp_clock_ops = { + .enable = cpg_mstp_clock_enable, + .disable = cpg_mstp_clock_disable, + .is_enabled = cpg_mstp_clock_is_enabled, +}; + +static struct clk * __init +cpg_mstp_clock_register(const char *name, const char *parent_name, + unsigned int index, struct mstp_clock_group *group) +{ + struct clk_init_data init; + struct mstp_clock *clock; + struct clk *clk; + + clock = kzalloc(sizeof(*clock), GFP_KERNEL); + if (!clock) { + pr_err("%s: failed to allocate MSTP clock.\n", __func__); + return ERR_PTR(-ENOMEM); + } + + init.name = name; + init.ops = &cpg_mstp_clock_ops; + init.flags = CLK_IS_BASIC | CLK_SET_RATE_PARENT; + init.parent_names = &parent_name; + init.num_parents = 1; + + clock->bit_index = index; + clock->group = group; + clock->hw.init = &init; + + clk = clk_register(NULL, &clock->hw); + + if (IS_ERR(clk)) + kfree(clock); + + return clk; +} + +static void __init cpg_mstp_clocks_init(struct device_node *np) +{ + struct mstp_clock_group *group; + const char *idxname; + struct clk **clks; + unsigned int i; + + group = kzalloc(sizeof(*group), GFP_KERNEL); + clks = kmalloc(MSTP_MAX_CLOCKS * sizeof(*clks), GFP_KERNEL); + if (group == NULL || clks == NULL) { + kfree(group); + kfree(clks); + pr_err("%s: failed to allocate group\n", __func__); + return; + } + + spin_lock_init(&group->lock); + group->data.clks = clks; + + group->smstpcr = of_iomap(np, 0); + group->mstpsr = of_iomap(np, 1); + + if (group->smstpcr == NULL) { + pr_err("%s: failed to remap SMSTPCR\n", __func__); + kfree(group); + kfree(clks); + return; + } + + for (i = 0; i < MSTP_MAX_CLOCKS; ++i) + clks[i] = ERR_PTR(-ENOENT); + + if (of_find_property(np, "clock-indices", &i)) + idxname = "clock-indices"; + else + idxname = "renesas,clock-indices"; + + for (i = 0; i < MSTP_MAX_CLOCKS; ++i) { + const char *parent_name; + const char *name; + u32 clkidx; + int ret; + + /* Skip clocks with no name. */ + ret = of_property_read_string_index(np, "clock-output-names", + i, &name); + if (ret < 0 || strlen(name) == 0) + continue; + + parent_name = of_clk_get_parent_name(np, i); + ret = of_property_read_u32_index(np, idxname, i, &clkidx); + if (parent_name == NULL || ret < 0) + break; + + if (clkidx >= MSTP_MAX_CLOCKS) { + pr_err("%s: invalid clock %s %s index %u\n", + __func__, np->name, name, clkidx); + continue; + } + + clks[clkidx] = cpg_mstp_clock_register(name, parent_name, + clkidx, group); + if (!IS_ERR(clks[clkidx])) { + group->data.clk_num = max(group->data.clk_num, + clkidx + 1); + /* + * Register a clkdev to let board code retrieve the + * clock by name and register aliases for non-DT + * devices. + * + * FIXME: Remove this when all devices that require a + * clock will be instantiated from DT. + */ + clk_register_clkdev(clks[clkidx], name, NULL); + } else { + pr_err("%s: failed to register %s %s clock (%ld)\n", + __func__, np->name, name, PTR_ERR(clks[clkidx])); + } + } + + of_clk_add_provider(np, of_clk_src_onecell_get, &group->data); +} +CLK_OF_DECLARE(cpg_mstp_clks, "renesas,cpg-mstp-clocks", cpg_mstp_clocks_init); + + +#ifdef CONFIG_PM_GENERIC_DOMAINS_OF +int cpg_mstp_attach_dev(struct generic_pm_domain *domain, struct device *dev) +{ + struct device_node *np = dev->of_node; + struct of_phandle_args clkspec; + struct clk *clk; + int i = 0; + int error; + + while (!of_parse_phandle_with_args(np, "clocks", "#clock-cells", i, + &clkspec)) { + if (of_device_is_compatible(clkspec.np, + "renesas,cpg-mstp-clocks")) + goto found; + + /* BSC on r8a73a4/sh73a0 uses zb_clk instead of an mstp clock */ + if (!strcmp(clkspec.np->name, "zb_clk")) + goto found; + + of_node_put(clkspec.np); + i++; + } + + return 0; + +found: + clk = of_clk_get_from_provider(&clkspec); + of_node_put(clkspec.np); + + if (IS_ERR(clk)) + return PTR_ERR(clk); + + error = pm_clk_create(dev); + if (error) { + dev_err(dev, "pm_clk_create failed %d\n", error); + goto fail_put; + } + + error = pm_clk_add_clk(dev, clk); + if (error) { + dev_err(dev, "pm_clk_add_clk %pC failed %d\n", clk, error); + goto fail_destroy; + } + + return 0; + +fail_destroy: + pm_clk_destroy(dev); +fail_put: + clk_put(clk); + return error; +} + +void cpg_mstp_detach_dev(struct generic_pm_domain *domain, struct device *dev) +{ + if (!list_empty(&dev->power.subsys_data->clock_list)) + pm_clk_destroy(dev); +} + +void __init cpg_mstp_add_clk_domain(struct device_node *np) +{ + struct generic_pm_domain *pd; + u32 ncells; + + if (of_property_read_u32(np, "#power-domain-cells", &ncells)) { + pr_warn("%s lacks #power-domain-cells\n", np->full_name); + return; + } + + pd = kzalloc(sizeof(*pd), GFP_KERNEL); + if (!pd) + return; + + pd->name = np->name; + + pd->flags = GENPD_FLAG_PM_CLK; + pm_genpd_init(pd, &simple_qos_governor, false); + pd->attach_dev = cpg_mstp_attach_dev; + pd->detach_dev = cpg_mstp_detach_dev; + + of_genpd_add_provider_simple(np, pd); +} +#endif /* !CONFIG_PM_GENERIC_DOMAINS_OF */ diff --git a/drivers/clk/renesas/clk-r8a73a4.c b/drivers/clk/renesas/clk-r8a73a4.c new file mode 100644 index 000000000000..9326204bed9d --- /dev/null +++ b/drivers/clk/renesas/clk-r8a73a4.c @@ -0,0 +1,241 @@ +/* + * r8a73a4 Core CPG Clocks + * + * Copyright (C) 2014 Ulrich Hecht + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + */ + +#include <linux/clk-provider.h> +#include <linux/clk/shmobile.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/spinlock.h> + +struct r8a73a4_cpg { + struct clk_onecell_data data; + spinlock_t lock; + void __iomem *reg; +}; + +#define CPG_CKSCR 0xc0 +#define CPG_FRQCRA 0x00 +#define CPG_FRQCRB 0x04 +#define CPG_FRQCRC 0xe0 +#define CPG_PLL0CR 0xd8 +#define CPG_PLL1CR 0x28 +#define CPG_PLL2CR 0x2c +#define CPG_PLL2HCR 0xe4 +#define CPG_PLL2SCR 0xf4 + +#define CLK_ENABLE_ON_INIT BIT(0) + +struct div4_clk { + const char *name; + unsigned int reg; + unsigned int shift; +}; + +static struct div4_clk div4_clks[] = { + { "i", CPG_FRQCRA, 20 }, + { "m3", CPG_FRQCRA, 12 }, + { "b", CPG_FRQCRA, 8 }, + { "m1", CPG_FRQCRA, 4 }, + { "m2", CPG_FRQCRA, 0 }, + { "zx", CPG_FRQCRB, 12 }, + { "zs", CPG_FRQCRB, 8 }, + { "hp", CPG_FRQCRB, 4 }, + { NULL, 0, 0 }, +}; + +static const struct clk_div_table div4_div_table[] = { + { 0, 2 }, { 1, 3 }, { 2, 4 }, { 3, 6 }, { 4, 8 }, { 5, 12 }, + { 6, 16 }, { 7, 18 }, { 8, 24 }, { 10, 36 }, { 11, 48 }, + { 12, 10 }, { 0, 0 } +}; + +static struct clk * __init +r8a73a4_cpg_register_clock(struct device_node *np, struct r8a73a4_cpg *cpg, + const char *name) +{ + const struct clk_div_table *table = NULL; + const char *parent_name; + unsigned int shift, reg; + unsigned int mult = 1; + unsigned int div = 1; + + + if (!strcmp(name, "main")) { + u32 ckscr = clk_readl(cpg->reg + CPG_CKSCR); + + switch ((ckscr >> 28) & 3) { + case 0: /* extal1 */ + parent_name = of_clk_get_parent_name(np, 0); + break; + case 1: /* extal1 / 2 */ + parent_name = of_clk_get_parent_name(np, 0); + div = 2; + break; + case 2: /* extal2 */ + parent_name = of_clk_get_parent_name(np, 1); + break; + case 3: /* extal2 / 2 */ + parent_name = of_clk_get_parent_name(np, 1); + div = 2; + break; + } + } else if (!strcmp(name, "pll0")) { + /* PLL0/1 are configurable multiplier clocks. Register them as + * fixed factor clocks for now as there's no generic multiplier + * clock implementation and we currently have no need to change + * the multiplier value. + */ + u32 value = clk_readl(cpg->reg + CPG_PLL0CR); + + parent_name = "main"; + mult = ((value >> 24) & 0x7f) + 1; + if (value & BIT(20)) + div = 2; + } else if (!strcmp(name, "pll1")) { + u32 value = clk_readl(cpg->reg + CPG_PLL1CR); + + parent_name = "main"; + /* XXX: enable bit? */ + mult = ((value >> 24) & 0x7f) + 1; + if (value & BIT(7)) + div = 2; + } else if (!strncmp(name, "pll2", 4)) { + u32 value, cr; + + switch (name[4]) { + case 0: + cr = CPG_PLL2CR; + break; + case 's': + cr = CPG_PLL2SCR; + break; + case 'h': + cr = CPG_PLL2HCR; + break; + default: + return ERR_PTR(-EINVAL); + } + value = clk_readl(cpg->reg + cr); + switch ((value >> 5) & 7) { + case 0: + parent_name = "main"; + div = 2; + break; + case 1: + parent_name = "extal2"; + div = 2; + break; + case 3: + parent_name = "extal2"; + div = 4; + break; + case 4: + parent_name = "main"; + break; + case 5: + parent_name = "extal2"; + break; + default: + pr_warn("%s: unexpected parent of %s\n", __func__, + name); + return ERR_PTR(-EINVAL); + } + /* XXX: enable bit? */ + mult = ((value >> 24) & 0x7f) + 1; + } else if (!strcmp(name, "z") || !strcmp(name, "z2")) { + u32 shift = 8; + + parent_name = "pll0"; + if (name[1] == '2') { + div = 2; + shift = 0; + } + div *= 32; + mult = 0x20 - ((clk_readl(cpg->reg + CPG_FRQCRC) >> shift) + & 0x1f); + } else { + struct div4_clk *c; + + for (c = div4_clks; c->name; c++) { + if (!strcmp(name, c->name)) + break; + } + if (!c->name) + return ERR_PTR(-EINVAL); + + parent_name = "pll1"; + table = div4_div_table; + reg = c->reg; + shift = c->shift; + } + + if (!table) { + return clk_register_fixed_factor(NULL, name, parent_name, 0, + mult, div); + } else { + return clk_register_divider_table(NULL, name, parent_name, 0, + cpg->reg + reg, shift, 4, 0, + table, &cpg->lock); + } +} + +static void __init r8a73a4_cpg_clocks_init(struct device_node *np) +{ + struct r8a73a4_cpg *cpg; + struct clk **clks; + unsigned int i; + int num_clks; + + num_clks = of_property_count_strings(np, "clock-output-names"); + if (num_clks < 0) { + pr_err("%s: failed to count clocks\n", __func__); + return; + } + + cpg = kzalloc(sizeof(*cpg), GFP_KERNEL); + clks = kcalloc(num_clks, sizeof(*clks), GFP_KERNEL); + if (cpg == NULL || clks == NULL) { + /* We're leaking memory on purpose, there's no point in cleaning + * up as the system won't boot anyway. + */ + return; + } + + spin_lock_init(&cpg->lock); + + cpg->data.clks = clks; + cpg->data.clk_num = num_clks; + + cpg->reg = of_iomap(np, 0); + if (WARN_ON(cpg->reg == NULL)) + return; + + for (i = 0; i < num_clks; ++i) { + const char *name; + struct clk *clk; + + of_property_read_string_index(np, "clock-output-names", i, + &name); + + clk = r8a73a4_cpg_register_clock(np, cpg, name); + if (IS_ERR(clk)) + pr_err("%s: failed to register %s %s clock (%ld)\n", + __func__, np->name, name, PTR_ERR(clk)); + else + cpg->data.clks[i] = clk; + } + + of_clk_add_provider(np, of_clk_src_onecell_get, &cpg->data); +} +CLK_OF_DECLARE(r8a73a4_cpg_clks, "renesas,r8a73a4-cpg-clocks", + r8a73a4_cpg_clocks_init); diff --git a/drivers/clk/renesas/clk-r8a7740.c b/drivers/clk/renesas/clk-r8a7740.c new file mode 100644 index 000000000000..1e6b1da58065 --- /dev/null +++ b/drivers/clk/renesas/clk-r8a7740.c @@ -0,0 +1,199 @@ +/* + * r8a7740 Core CPG Clocks + * + * Copyright (C) 2014 Ulrich Hecht + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + */ + +#include <linux/clk-provider.h> +#include <linux/clk/shmobile.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/spinlock.h> + +struct r8a7740_cpg { + struct clk_onecell_data data; + spinlock_t lock; + void __iomem *reg; +}; + +#define CPG_FRQCRA 0x00 +#define CPG_FRQCRB 0x04 +#define CPG_PLLC2CR 0x2c +#define CPG_USBCKCR 0x8c +#define CPG_FRQCRC 0xe0 + +#define CLK_ENABLE_ON_INIT BIT(0) + +struct div4_clk { + const char *name; + unsigned int reg; + unsigned int shift; + int flags; +}; + +static struct div4_clk div4_clks[] = { + { "i", CPG_FRQCRA, 20, CLK_ENABLE_ON_INIT }, + { "zg", CPG_FRQCRA, 16, CLK_ENABLE_ON_INIT }, + { "b", CPG_FRQCRA, 8, CLK_ENABLE_ON_INIT }, + { "m1", CPG_FRQCRA, 4, CLK_ENABLE_ON_INIT }, + { "hp", CPG_FRQCRB, 4, 0 }, + { "hpp", CPG_FRQCRC, 20, 0 }, + { "usbp", CPG_FRQCRC, 16, 0 }, + { "s", CPG_FRQCRC, 12, 0 }, + { "zb", CPG_FRQCRC, 8, 0 }, + { "m3", CPG_FRQCRC, 4, 0 }, + { "cp", CPG_FRQCRC, 0, 0 }, + { NULL, 0, 0, 0 }, +}; + +static const struct clk_div_table div4_div_table[] = { + { 0, 2 }, { 1, 3 }, { 2, 4 }, { 3, 6 }, { 4, 8 }, { 5, 12 }, + { 6, 16 }, { 7, 18 }, { 8, 24 }, { 9, 32 }, { 10, 36 }, { 11, 48 }, + { 13, 72 }, { 14, 96 }, { 0, 0 } +}; + +static u32 cpg_mode __initdata; + +static struct clk * __init +r8a7740_cpg_register_clock(struct device_node *np, struct r8a7740_cpg *cpg, + const char *name) +{ + const struct clk_div_table *table = NULL; + const char *parent_name; + unsigned int shift, reg; + unsigned int mult = 1; + unsigned int div = 1; + + if (!strcmp(name, "r")) { + switch (cpg_mode & (BIT(2) | BIT(1))) { + case BIT(1) | BIT(2): + /* extal1 */ + parent_name = of_clk_get_parent_name(np, 0); + div = 2048; + break; + case BIT(2): + /* extal1 */ + parent_name = of_clk_get_parent_name(np, 0); + div = 1024; + break; + default: + /* extalr */ + parent_name = of_clk_get_parent_name(np, 2); + break; + } + } else if (!strcmp(name, "system")) { + parent_name = of_clk_get_parent_name(np, 0); + if (cpg_mode & BIT(1)) + div = 2; + } else if (!strcmp(name, "pllc0")) { + /* PLLC0/1 are configurable multiplier clocks. Register them as + * fixed factor clocks for now as there's no generic multiplier + * clock implementation and we currently have no need to change + * the multiplier value. + */ + u32 value = clk_readl(cpg->reg + CPG_FRQCRC); + parent_name = "system"; + mult = ((value >> 24) & 0x7f) + 1; + } else if (!strcmp(name, "pllc1")) { + u32 value = clk_readl(cpg->reg + CPG_FRQCRA); + parent_name = "system"; + mult = ((value >> 24) & 0x7f) + 1; + div = 2; + } else if (!strcmp(name, "pllc2")) { + u32 value = clk_readl(cpg->reg + CPG_PLLC2CR); + parent_name = "system"; + mult = ((value >> 24) & 0x3f) + 1; + } else if (!strcmp(name, "usb24s")) { + u32 value = clk_readl(cpg->reg + CPG_USBCKCR); + if (value & BIT(7)) + /* extal2 */ + parent_name = of_clk_get_parent_name(np, 1); + else + parent_name = "system"; + if (!(value & BIT(6))) + div = 2; + } else { + struct div4_clk *c; + for (c = div4_clks; c->name; c++) { + if (!strcmp(name, c->name)) { + parent_name = "pllc1"; + table = div4_div_table; + reg = c->reg; + shift = c->shift; + break; + } + } + if (!c->name) + return ERR_PTR(-EINVAL); + } + + if (!table) { + return clk_register_fixed_factor(NULL, name, parent_name, 0, + mult, div); + } else { + return clk_register_divider_table(NULL, name, parent_name, 0, + cpg->reg + reg, shift, 4, 0, + table, &cpg->lock); + } +} + +static void __init r8a7740_cpg_clocks_init(struct device_node *np) +{ + struct r8a7740_cpg *cpg; + struct clk **clks; + unsigned int i; + int num_clks; + + if (of_property_read_u32(np, "renesas,mode", &cpg_mode)) + pr_warn("%s: missing renesas,mode property\n", __func__); + + num_clks = of_property_count_strings(np, "clock-output-names"); + if (num_clks < 0) { + pr_err("%s: failed to count clocks\n", __func__); + return; + } + + cpg = kzalloc(sizeof(*cpg), GFP_KERNEL); + clks = kzalloc(num_clks * sizeof(*clks), GFP_KERNEL); + if (cpg == NULL || clks == NULL) { + /* We're leaking memory on purpose, there's no point in cleaning + * up as the system won't boot anyway. + */ + return; + } + + spin_lock_init(&cpg->lock); + + cpg->data.clks = clks; + cpg->data.clk_num = num_clks; + + cpg->reg = of_iomap(np, 0); + if (WARN_ON(cpg->reg == NULL)) + return; + + for (i = 0; i < num_clks; ++i) { + const char *name; + struct clk *clk; + + of_property_read_string_index(np, "clock-output-names", i, + &name); + + clk = r8a7740_cpg_register_clock(np, cpg, name); + if (IS_ERR(clk)) + pr_err("%s: failed to register %s %s clock (%ld)\n", + __func__, np->name, name, PTR_ERR(clk)); + else + cpg->data.clks[i] = clk; + } + + of_clk_add_provider(np, of_clk_src_onecell_get, &cpg->data); +} +CLK_OF_DECLARE(r8a7740_cpg_clks, "renesas,r8a7740-cpg-clocks", + r8a7740_cpg_clocks_init); diff --git a/drivers/clk/renesas/clk-r8a7778.c b/drivers/clk/renesas/clk-r8a7778.c new file mode 100644 index 000000000000..b1741551fff2 --- /dev/null +++ b/drivers/clk/renesas/clk-r8a7778.c @@ -0,0 +1,145 @@ +/* + * r8a7778 Core CPG Clocks + * + * Copyright (C) 2014 Ulrich Hecht + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + */ + +#include <linux/clk-provider.h> +#include <linux/clk/shmobile.h> +#include <linux/of_address.h> +#include <linux/slab.h> + +struct r8a7778_cpg { + struct clk_onecell_data data; + spinlock_t lock; + void __iomem *reg; +}; + +/* PLL multipliers per bits 11, 12, and 18 of MODEMR */ +static const struct { + unsigned long plla_mult; + unsigned long pllb_mult; +} r8a7778_rates[] __initconst = { + [0] = { 21, 21 }, + [1] = { 24, 24 }, + [2] = { 28, 28 }, + [3] = { 32, 32 }, + [5] = { 24, 21 }, + [6] = { 28, 21 }, + [7] = { 32, 24 }, +}; + +/* Clock dividers per bits 1 and 2 of MODEMR */ +static const struct { + const char *name; + unsigned int div[4]; +} r8a7778_divs[6] __initconst = { + { "b", { 12, 12, 16, 18 } }, + { "out", { 12, 12, 16, 18 } }, + { "p", { 16, 12, 16, 12 } }, + { "s", { 4, 3, 4, 3 } }, + { "s1", { 8, 6, 8, 6 } }, +}; + +static u32 cpg_mode_rates __initdata; +static u32 cpg_mode_divs __initdata; + +static struct clk * __init +r8a7778_cpg_register_clock(struct device_node *np, struct r8a7778_cpg *cpg, + const char *name) +{ + if (!strcmp(name, "plla")) { + return clk_register_fixed_factor(NULL, "plla", + of_clk_get_parent_name(np, 0), 0, + r8a7778_rates[cpg_mode_rates].plla_mult, 1); + } else if (!strcmp(name, "pllb")) { + return clk_register_fixed_factor(NULL, "pllb", + of_clk_get_parent_name(np, 0), 0, + r8a7778_rates[cpg_mode_rates].pllb_mult, 1); + } else { + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(r8a7778_divs); i++) { + if (!strcmp(name, r8a7778_divs[i].name)) { + return clk_register_fixed_factor(NULL, + r8a7778_divs[i].name, + "plla", 0, 1, + r8a7778_divs[i].div[cpg_mode_divs]); + } + } + } + + return ERR_PTR(-EINVAL); +} + + +static void __init r8a7778_cpg_clocks_init(struct device_node *np) +{ + struct r8a7778_cpg *cpg; + struct clk **clks; + unsigned int i; + int num_clks; + + num_clks = of_property_count_strings(np, "clock-output-names"); + if (num_clks < 0) { + pr_err("%s: failed to count clocks\n", __func__); + return; + } + + cpg = kzalloc(sizeof(*cpg), GFP_KERNEL); + clks = kcalloc(num_clks, sizeof(*clks), GFP_KERNEL); + if (cpg == NULL || clks == NULL) { + /* We're leaking memory on purpose, there's no point in cleaning + * up as the system won't boot anyway. + */ + return; + } + + spin_lock_init(&cpg->lock); + + cpg->data.clks = clks; + cpg->data.clk_num = num_clks; + + cpg->reg = of_iomap(np, 0); + if (WARN_ON(cpg->reg == NULL)) + return; + + for (i = 0; i < num_clks; ++i) { + const char *name; + struct clk *clk; + + of_property_read_string_index(np, "clock-output-names", i, + &name); + + clk = r8a7778_cpg_register_clock(np, cpg, name); + if (IS_ERR(clk)) + pr_err("%s: failed to register %s %s clock (%ld)\n", + __func__, np->name, name, PTR_ERR(clk)); + else + cpg->data.clks[i] = clk; + } + + of_clk_add_provider(np, of_clk_src_onecell_get, &cpg->data); + + cpg_mstp_add_clk_domain(np); +} + +CLK_OF_DECLARE(r8a7778_cpg_clks, "renesas,r8a7778-cpg-clocks", + r8a7778_cpg_clocks_init); + +void __init r8a7778_clocks_init(u32 mode) +{ + BUG_ON(!(mode & BIT(19))); + + cpg_mode_rates = (!!(mode & BIT(18)) << 2) | + (!!(mode & BIT(12)) << 1) | + (!!(mode & BIT(11))); + cpg_mode_divs = (!!(mode & BIT(2)) << 1) | + (!!(mode & BIT(1))); + + of_clk_init(NULL); +} diff --git a/drivers/clk/renesas/clk-r8a7779.c b/drivers/clk/renesas/clk-r8a7779.c new file mode 100644 index 000000000000..92275c5f2c60 --- /dev/null +++ b/drivers/clk/renesas/clk-r8a7779.c @@ -0,0 +1,182 @@ +/* + * r8a7779 Core CPG Clocks + * + * Copyright (C) 2013, 2014 Horms Solutions Ltd. + * + * Contact: Simon Horman <horms@verge.net.au> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + */ + +#include <linux/clk-provider.h> +#include <linux/clk/shmobile.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/slab.h> +#include <linux/spinlock.h> + +#include <dt-bindings/clock/r8a7779-clock.h> + +#define CPG_NUM_CLOCKS (R8A7779_CLK_OUT + 1) + +struct r8a7779_cpg { + struct clk_onecell_data data; + spinlock_t lock; + void __iomem *reg; +}; + +/* ----------------------------------------------------------------------------- + * CPG Clock Data + */ + +/* + * MD1 = 1 MD1 = 0 + * (PLLA = 1500) (PLLA = 1600) + * (MHz) (MHz) + *------------------------------------------------+-------------------- + * clkz 1000 (2/3) 800 (1/2) + * clkzs 250 (1/6) 200 (1/8) + * clki 750 (1/2) 800 (1/2) + * clks 250 (1/6) 200 (1/8) + * clks1 125 (1/12) 100 (1/16) + * clks3 187.5 (1/8) 200 (1/8) + * clks4 93.7 (1/16) 100 (1/16) + * clkp 62.5 (1/24) 50 (1/32) + * clkg 62.5 (1/24) 66.6 (1/24) + * clkb, CLKOUT + * (MD2 = 0) 62.5 (1/24) 66.6 (1/24) + * (MD2 = 1) 41.6 (1/36) 50 (1/32) + */ + +#define CPG_CLK_CONFIG_INDEX(md) (((md) & (BIT(2)|BIT(1))) >> 1) + +struct cpg_clk_config { + unsigned int z_mult; + unsigned int z_div; + unsigned int zs_and_s_div; + unsigned int s1_div; + unsigned int p_div; + unsigned int b_and_out_div; +}; + +static const struct cpg_clk_config cpg_clk_configs[4] __initconst = { + { 1, 2, 8, 16, 32, 24 }, + { 2, 3, 6, 12, 24, 24 }, + { 1, 2, 8, 16, 32, 32 }, + { 2, 3, 6, 12, 24, 36 }, +}; + +/* + * MD PLLA Ratio + * 12 11 + *------------------------ + * 0 0 x42 + * 0 1 x48 + * 1 0 x56 + * 1 1 x64 + */ + +#define CPG_PLLA_MULT_INDEX(md) (((md) & (BIT(12)|BIT(11))) >> 11) + +static const unsigned int cpg_plla_mult[4] __initconst = { 42, 48, 56, 64 }; + +/* ----------------------------------------------------------------------------- + * Initialization + */ + +static u32 cpg_mode __initdata; + +static struct clk * __init +r8a7779_cpg_register_clock(struct device_node *np, struct r8a7779_cpg *cpg, + const struct cpg_clk_config *config, + unsigned int plla_mult, const char *name) +{ + const char *parent_name = "plla"; + unsigned int mult = 1; + unsigned int div = 1; + + if (!strcmp(name, "plla")) { + parent_name = of_clk_get_parent_name(np, 0); + mult = plla_mult; + } else if (!strcmp(name, "z")) { + div = config->z_div; + mult = config->z_mult; + } else if (!strcmp(name, "zs") || !strcmp(name, "s")) { + div = config->zs_and_s_div; + } else if (!strcmp(name, "s1")) { + div = config->s1_div; + } else if (!strcmp(name, "p")) { + div = config->p_div; + } else if (!strcmp(name, "b") || !strcmp(name, "out")) { + div = config->b_and_out_div; + } else { + return ERR_PTR(-EINVAL); + } + + return clk_register_fixed_factor(NULL, name, parent_name, 0, mult, div); +} + +static void __init r8a7779_cpg_clocks_init(struct device_node *np) +{ + const struct cpg_clk_config *config; + struct r8a7779_cpg *cpg; + struct clk **clks; + unsigned int i, plla_mult; + int num_clks; + + num_clks = of_property_count_strings(np, "clock-output-names"); + if (num_clks < 0) { + pr_err("%s: failed to count clocks\n", __func__); + return; + } + + cpg = kzalloc(sizeof(*cpg), GFP_KERNEL); + clks = kzalloc(CPG_NUM_CLOCKS * sizeof(*clks), GFP_KERNEL); + if (cpg == NULL || clks == NULL) { + /* We're leaking memory on purpose, there's no point in cleaning + * up as the system won't boot anyway. + */ + return; + } + + spin_lock_init(&cpg->lock); + + cpg->data.clks = clks; + cpg->data.clk_num = num_clks; + + config = &cpg_clk_configs[CPG_CLK_CONFIG_INDEX(cpg_mode)]; + plla_mult = cpg_plla_mult[CPG_PLLA_MULT_INDEX(cpg_mode)]; + + for (i = 0; i < num_clks; ++i) { + const char *name; + struct clk *clk; + + of_property_read_string_index(np, "clock-output-names", i, + &name); + + clk = r8a7779_cpg_register_clock(np, cpg, config, + plla_mult, name); + if (IS_ERR(clk)) + pr_err("%s: failed to register %s %s clock (%ld)\n", + __func__, np->name, name, PTR_ERR(clk)); + else + cpg->data.clks[i] = clk; + } + + of_clk_add_provider(np, of_clk_src_onecell_get, &cpg->data); + + cpg_mstp_add_clk_domain(np); +} +CLK_OF_DECLARE(r8a7779_cpg_clks, "renesas,r8a7779-cpg-clocks", + r8a7779_cpg_clocks_init); + +void __init r8a7779_clocks_init(u32 mode) +{ + cpg_mode = mode; + + of_clk_init(NULL); +} diff --git a/drivers/clk/renesas/clk-rcar-gen2.c b/drivers/clk/renesas/clk-rcar-gen2.c new file mode 100644 index 000000000000..841977240305 --- /dev/null +++ b/drivers/clk/renesas/clk-rcar-gen2.c @@ -0,0 +1,429 @@ +/* + * rcar_gen2 Core CPG Clocks + * + * Copyright (C) 2013 Ideas On Board SPRL + * + * Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + */ + +#include <linux/clk-provider.h> +#include <linux/clk/shmobile.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/math64.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/slab.h> +#include <linux/spinlock.h> + +struct rcar_gen2_cpg { + struct clk_onecell_data data; + spinlock_t lock; + void __iomem *reg; +}; + +#define CPG_FRQCRB 0x00000004 +#define CPG_FRQCRB_KICK BIT(31) +#define CPG_SDCKCR 0x00000074 +#define CPG_PLL0CR 0x000000d8 +#define CPG_FRQCRC 0x000000e0 +#define CPG_FRQCRC_ZFC_MASK (0x1f << 8) +#define CPG_FRQCRC_ZFC_SHIFT 8 +#define CPG_ADSPCKCR 0x0000025c +#define CPG_RCANCKCR 0x00000270 + +/* ----------------------------------------------------------------------------- + * Z Clock + * + * Traits of this clock: + * prepare - clk_prepare only ensures that parents are prepared + * enable - clk_enable only ensures that parents are enabled + * rate - rate is adjustable. clk->rate = parent->rate * mult / 32 + * parent - fixed parent. No clk_set_parent support + */ + +struct cpg_z_clk { + struct clk_hw hw; + void __iomem *reg; + void __iomem *kick_reg; +}; + +#define to_z_clk(_hw) container_of(_hw, struct cpg_z_clk, hw) + +static unsigned long cpg_z_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct cpg_z_clk *zclk = to_z_clk(hw); + unsigned int mult; + unsigned int val; + + val = (clk_readl(zclk->reg) & CPG_FRQCRC_ZFC_MASK) + >> CPG_FRQCRC_ZFC_SHIFT; + mult = 32 - val; + + return div_u64((u64)parent_rate * mult, 32); +} + +static long cpg_z_clk_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + unsigned long prate = *parent_rate; + unsigned int mult; + + if (!prate) + prate = 1; + + mult = div_u64((u64)rate * 32, prate); + mult = clamp(mult, 1U, 32U); + + return *parent_rate / 32 * mult; +} + +static int cpg_z_clk_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct cpg_z_clk *zclk = to_z_clk(hw); + unsigned int mult; + u32 val, kick; + unsigned int i; + + mult = div_u64((u64)rate * 32, parent_rate); + mult = clamp(mult, 1U, 32U); + + if (clk_readl(zclk->kick_reg) & CPG_FRQCRB_KICK) + return -EBUSY; + + val = clk_readl(zclk->reg); + val &= ~CPG_FRQCRC_ZFC_MASK; + val |= (32 - mult) << CPG_FRQCRC_ZFC_SHIFT; + clk_writel(val, zclk->reg); + + /* + * Set KICK bit in FRQCRB to update hardware setting and wait for + * clock change completion. + */ + kick = clk_readl(zclk->kick_reg); + kick |= CPG_FRQCRB_KICK; + clk_writel(kick, zclk->kick_reg); + + /* + * Note: There is no HW information about the worst case latency. + * + * Using experimental measurements, it seems that no more than + * ~10 iterations are needed, independently of the CPU rate. + * Since this value might be dependent on external xtal rate, pll1 + * rate or even the other emulation clocks rate, use 1000 as a + * "super" safe value. + */ + for (i = 1000; i; i--) { + if (!(clk_readl(zclk->kick_reg) & CPG_FRQCRB_KICK)) + return 0; + + cpu_relax(); + } + + return -ETIMEDOUT; +} + +static const struct clk_ops cpg_z_clk_ops = { + .recalc_rate = cpg_z_clk_recalc_rate, + .round_rate = cpg_z_clk_round_rate, + .set_rate = cpg_z_clk_set_rate, +}; + +static struct clk * __init cpg_z_clk_register(struct rcar_gen2_cpg *cpg) +{ + static const char *parent_name = "pll0"; + struct clk_init_data init; + struct cpg_z_clk *zclk; + struct clk *clk; + + zclk = kzalloc(sizeof(*zclk), GFP_KERNEL); + if (!zclk) + return ERR_PTR(-ENOMEM); + + init.name = "z"; + init.ops = &cpg_z_clk_ops; + init.flags = 0; + init.parent_names = &parent_name; + init.num_parents = 1; + + zclk->reg = cpg->reg + CPG_FRQCRC; + zclk->kick_reg = cpg->reg + CPG_FRQCRB; + zclk->hw.init = &init; + + clk = clk_register(NULL, &zclk->hw); + if (IS_ERR(clk)) + kfree(zclk); + + return clk; +} + +static struct clk * __init cpg_rcan_clk_register(struct rcar_gen2_cpg *cpg, + struct device_node *np) +{ + const char *parent_name = of_clk_get_parent_name(np, 1); + struct clk_fixed_factor *fixed; + struct clk_gate *gate; + struct clk *clk; + + fixed = kzalloc(sizeof(*fixed), GFP_KERNEL); + if (!fixed) + return ERR_PTR(-ENOMEM); + + fixed->mult = 1; + fixed->div = 6; + + gate = kzalloc(sizeof(*gate), GFP_KERNEL); + if (!gate) { + kfree(fixed); + return ERR_PTR(-ENOMEM); + } + + gate->reg = cpg->reg + CPG_RCANCKCR; + gate->bit_idx = 8; + gate->flags = CLK_GATE_SET_TO_DISABLE; + gate->lock = &cpg->lock; + + clk = clk_register_composite(NULL, "rcan", &parent_name, 1, NULL, NULL, + &fixed->hw, &clk_fixed_factor_ops, + &gate->hw, &clk_gate_ops, 0); + if (IS_ERR(clk)) { + kfree(gate); + kfree(fixed); + } + + return clk; +} + +/* ADSP divisors */ +static const struct clk_div_table cpg_adsp_div_table[] = { + { 1, 3 }, { 2, 4 }, { 3, 6 }, { 4, 8 }, + { 5, 12 }, { 6, 16 }, { 7, 18 }, { 8, 24 }, + { 10, 36 }, { 11, 48 }, { 0, 0 }, +}; + +static struct clk * __init cpg_adsp_clk_register(struct rcar_gen2_cpg *cpg) +{ + const char *parent_name = "pll1"; + struct clk_divider *div; + struct clk_gate *gate; + struct clk *clk; + + div = kzalloc(sizeof(*div), GFP_KERNEL); + if (!div) + return ERR_PTR(-ENOMEM); + + div->reg = cpg->reg + CPG_ADSPCKCR; + div->width = 4; + div->table = cpg_adsp_div_table; + div->lock = &cpg->lock; + + gate = kzalloc(sizeof(*gate), GFP_KERNEL); + if (!gate) { + kfree(div); + return ERR_PTR(-ENOMEM); + } + + gate->reg = cpg->reg + CPG_ADSPCKCR; + gate->bit_idx = 8; + gate->flags = CLK_GATE_SET_TO_DISABLE; + gate->lock = &cpg->lock; + + clk = clk_register_composite(NULL, "adsp", &parent_name, 1, NULL, NULL, + &div->hw, &clk_divider_ops, + &gate->hw, &clk_gate_ops, 0); + if (IS_ERR(clk)) { + kfree(gate); + kfree(div); + } + + return clk; +} + +/* ----------------------------------------------------------------------------- + * CPG Clock Data + */ + +/* + * MD EXTAL PLL0 PLL1 PLL3 + * 14 13 19 (MHz) *1 *1 + *--------------------------------------------------- + * 0 0 0 15 x 1 x172/2 x208/2 x106 + * 0 0 1 15 x 1 x172/2 x208/2 x88 + * 0 1 0 20 x 1 x130/2 x156/2 x80 + * 0 1 1 20 x 1 x130/2 x156/2 x66 + * 1 0 0 26 / 2 x200/2 x240/2 x122 + * 1 0 1 26 / 2 x200/2 x240/2 x102 + * 1 1 0 30 / 2 x172/2 x208/2 x106 + * 1 1 1 30 / 2 x172/2 x208/2 x88 + * + * *1 : Table 7.6 indicates VCO output (PLLx = VCO/2) + */ +#define CPG_PLL_CONFIG_INDEX(md) ((((md) & BIT(14)) >> 12) | \ + (((md) & BIT(13)) >> 12) | \ + (((md) & BIT(19)) >> 19)) +struct cpg_pll_config { + unsigned int extal_div; + unsigned int pll1_mult; + unsigned int pll3_mult; +}; + +static const struct cpg_pll_config cpg_pll_configs[8] __initconst = { + { 1, 208, 106 }, { 1, 208, 88 }, { 1, 156, 80 }, { 1, 156, 66 }, + { 2, 240, 122 }, { 2, 240, 102 }, { 2, 208, 106 }, { 2, 208, 88 }, +}; + +/* 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 }, +}; + +/* ----------------------------------------------------------------------------- + * Initialization + */ + +static u32 cpg_mode __initdata; + +static struct clk * __init +rcar_gen2_cpg_register_clock(struct device_node *np, struct rcar_gen2_cpg *cpg, + const struct cpg_pll_config *config, + const char *name) +{ + const struct clk_div_table *table = NULL; + const char *parent_name; + unsigned int shift; + unsigned int mult = 1; + unsigned int div = 1; + + if (!strcmp(name, "main")) { + parent_name = of_clk_get_parent_name(np, 0); + div = config->extal_div; + } else if (!strcmp(name, "pll0")) { + /* PLL0 is a configurable multiplier clock. Register it 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. + */ + u32 value = clk_readl(cpg->reg + CPG_PLL0CR); + parent_name = "main"; + mult = ((value >> 24) & ((1 << 7) - 1)) + 1; + } else if (!strcmp(name, "pll1")) { + parent_name = "main"; + mult = config->pll1_mult / 2; + } else if (!strcmp(name, "pll3")) { + parent_name = "main"; + mult = config->pll3_mult; + } else if (!strcmp(name, "lb")) { + parent_name = "pll1"; + div = cpg_mode & BIT(18) ? 36 : 24; + } else if (!strcmp(name, "qspi")) { + parent_name = "pll1_div2"; + div = (cpg_mode & (BIT(3) | BIT(2) | BIT(1))) == BIT(2) + ? 8 : 10; + } else if (!strcmp(name, "sdh")) { + parent_name = "pll1"; + table = cpg_sdh_div_table; + shift = 8; + } else if (!strcmp(name, "sd0")) { + parent_name = "pll1"; + table = cpg_sd01_div_table; + shift = 4; + } else if (!strcmp(name, "sd1")) { + parent_name = "pll1"; + table = cpg_sd01_div_table; + shift = 0; + } else if (!strcmp(name, "z")) { + return cpg_z_clk_register(cpg); + } else if (!strcmp(name, "rcan")) { + return cpg_rcan_clk_register(cpg, np); + } else if (!strcmp(name, "adsp")) { + return cpg_adsp_clk_register(cpg); + } else { + return ERR_PTR(-EINVAL); + } + + if (!table) + return clk_register_fixed_factor(NULL, name, parent_name, 0, + mult, div); + else + return clk_register_divider_table(NULL, name, parent_name, 0, + cpg->reg + CPG_SDCKCR, shift, + 4, 0, table, &cpg->lock); +} + +static void __init rcar_gen2_cpg_clocks_init(struct device_node *np) +{ + const struct cpg_pll_config *config; + struct rcar_gen2_cpg *cpg; + struct clk **clks; + unsigned int i; + int num_clks; + + num_clks = of_property_count_strings(np, "clock-output-names"); + if (num_clks < 0) { + pr_err("%s: failed to count clocks\n", __func__); + return; + } + + cpg = kzalloc(sizeof(*cpg), GFP_KERNEL); + clks = kzalloc(num_clks * sizeof(*clks), GFP_KERNEL); + if (cpg == NULL || clks == NULL) { + /* We're leaking memory on purpose, there's no point in cleaning + * up as the system won't boot anyway. + */ + pr_err("%s: failed to allocate cpg\n", __func__); + return; + } + + spin_lock_init(&cpg->lock); + + cpg->data.clks = clks; + cpg->data.clk_num = num_clks; + + cpg->reg = of_iomap(np, 0); + if (WARN_ON(cpg->reg == NULL)) + return; + + config = &cpg_pll_configs[CPG_PLL_CONFIG_INDEX(cpg_mode)]; + + for (i = 0; i < num_clks; ++i) { + const char *name; + struct clk *clk; + + of_property_read_string_index(np, "clock-output-names", i, + &name); + + clk = rcar_gen2_cpg_register_clock(np, cpg, config, name); + if (IS_ERR(clk)) + pr_err("%s: failed to register %s %s clock (%ld)\n", + __func__, np->name, name, PTR_ERR(clk)); + else + cpg->data.clks[i] = clk; + } + + of_clk_add_provider(np, of_clk_src_onecell_get, &cpg->data); + + cpg_mstp_add_clk_domain(np); +} +CLK_OF_DECLARE(rcar_gen2_cpg_clks, "renesas,rcar-gen2-cpg-clocks", + rcar_gen2_cpg_clocks_init); + +void __init rcar_gen2_clocks_init(u32 mode) +{ + cpg_mode = mode; + + of_clk_init(NULL); +} diff --git a/drivers/clk/renesas/clk-rz.c b/drivers/clk/renesas/clk-rz.c new file mode 100644 index 000000000000..9766e3cb595f --- /dev/null +++ b/drivers/clk/renesas/clk-rz.c @@ -0,0 +1,106 @@ +/* + * rz Core CPG Clocks + * + * Copyright (C) 2013 Ideas On Board SPRL + * Copyright (C) 2014 Wolfram Sang, Sang Engineering <wsa@sang-engineering.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + */ + +#include <linux/clk-provider.h> +#include <linux/clk/shmobile.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/slab.h> + +struct rz_cpg { + struct clk_onecell_data data; + void __iomem *reg; +}; + +#define CPG_FRQCR 0x10 +#define CPG_FRQCR2 0x14 + +/* ----------------------------------------------------------------------------- + * Initialization + */ + +static struct clk * __init +rz_cpg_register_clock(struct device_node *np, struct rz_cpg *cpg, const char *name) +{ + u32 val; + unsigned mult; + static const unsigned frqcr_tab[4] = { 3, 2, 0, 1 }; + + if (strcmp(name, "pll") == 0) { + /* FIXME: cpg_mode should be read from GPIO. But no GPIO support yet */ + unsigned cpg_mode = 0; /* hardcoded to EXTAL for now */ + const char *parent_name = of_clk_get_parent_name(np, cpg_mode); + + mult = cpg_mode ? (32 / 4) : 30; + + return clk_register_fixed_factor(NULL, name, parent_name, 0, mult, 1); + } + + /* If mapping regs failed, skip non-pll clocks. System will boot anyhow */ + if (!cpg->reg) + return ERR_PTR(-ENXIO); + + /* FIXME:"i" and "g" are variable clocks with non-integer dividers (e.g. 2/3) + * and the constraint that always g <= i. To get the rz platform started, + * let them run at fixed current speed and implement the details later. + */ + if (strcmp(name, "i") == 0) + val = (clk_readl(cpg->reg + CPG_FRQCR) >> 8) & 3; + else if (strcmp(name, "g") == 0) + val = clk_readl(cpg->reg + CPG_FRQCR2) & 3; + else + return ERR_PTR(-EINVAL); + + mult = frqcr_tab[val]; + return clk_register_fixed_factor(NULL, name, "pll", 0, mult, 3); +} + +static void __init rz_cpg_clocks_init(struct device_node *np) +{ + struct rz_cpg *cpg; + struct clk **clks; + unsigned i; + int num_clks; + + num_clks = of_property_count_strings(np, "clock-output-names"); + if (WARN(num_clks <= 0, "can't count CPG clocks\n")) + return; + + cpg = kzalloc(sizeof(*cpg), GFP_KERNEL); + clks = kzalloc(num_clks * sizeof(*clks), GFP_KERNEL); + BUG_ON(!cpg || !clks); + + cpg->data.clks = clks; + cpg->data.clk_num = num_clks; + + cpg->reg = of_iomap(np, 0); + + for (i = 0; i < num_clks; ++i) { + const char *name; + struct clk *clk; + + of_property_read_string_index(np, "clock-output-names", i, &name); + + clk = rz_cpg_register_clock(np, cpg, name); + if (IS_ERR(clk)) + pr_err("%s: failed to register %s %s clock (%ld)\n", + __func__, np->name, name, PTR_ERR(clk)); + else + cpg->data.clks[i] = clk; + } + + of_clk_add_provider(np, of_clk_src_onecell_get, &cpg->data); + + cpg_mstp_add_clk_domain(np); +} +CLK_OF_DECLARE(rz_cpg_clks, "renesas,rz-cpg-clocks", rz_cpg_clocks_init); diff --git a/drivers/clk/renesas/clk-sh73a0.c b/drivers/clk/renesas/clk-sh73a0.c new file mode 100644 index 000000000000..8966f8bbfd72 --- /dev/null +++ b/drivers/clk/renesas/clk-sh73a0.c @@ -0,0 +1,218 @@ +/* + * sh73a0 Core CPG Clocks + * + * Copyright (C) 2014 Ulrich Hecht + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + */ + +#include <linux/clk-provider.h> +#include <linux/clk/shmobile.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/slab.h> +#include <linux/spinlock.h> + +struct sh73a0_cpg { + struct clk_onecell_data data; + spinlock_t lock; + void __iomem *reg; +}; + +#define CPG_FRQCRA 0x00 +#define CPG_FRQCRB 0x04 +#define CPG_SD0CKCR 0x74 +#define CPG_SD1CKCR 0x78 +#define CPG_SD2CKCR 0x7c +#define CPG_PLLECR 0xd0 +#define CPG_PLL0CR 0xd8 +#define CPG_PLL1CR 0x28 +#define CPG_PLL2CR 0x2c +#define CPG_PLL3CR 0xdc +#define CPG_CKSCR 0xc0 +#define CPG_DSI0PHYCR 0x6c +#define CPG_DSI1PHYCR 0x70 + +#define CLK_ENABLE_ON_INIT BIT(0) + +struct div4_clk { + const char *name; + const char *parent; + unsigned int reg; + unsigned int shift; +}; + +static struct div4_clk div4_clks[] = { + { "zg", "pll0", CPG_FRQCRA, 16 }, + { "m3", "pll1", CPG_FRQCRA, 12 }, + { "b", "pll1", CPG_FRQCRA, 8 }, + { "m1", "pll1", CPG_FRQCRA, 4 }, + { "m2", "pll1", CPG_FRQCRA, 0 }, + { "zx", "pll1", CPG_FRQCRB, 12 }, + { "hp", "pll1", CPG_FRQCRB, 4 }, + { NULL, NULL, 0, 0 }, +}; + +static const struct clk_div_table div4_div_table[] = { + { 0, 2 }, { 1, 3 }, { 2, 4 }, { 3, 6 }, { 4, 8 }, { 5, 12 }, + { 6, 16 }, { 7, 18 }, { 8, 24 }, { 10, 36 }, { 11, 48 }, + { 12, 7 }, { 0, 0 } +}; + +static const struct clk_div_table z_div_table[] = { + /* ZSEL == 0 */ + { 0, 1 }, { 1, 1 }, { 2, 1 }, { 3, 1 }, { 4, 1 }, { 5, 1 }, + { 6, 1 }, { 7, 1 }, { 8, 1 }, { 9, 1 }, { 10, 1 }, { 11, 1 }, + { 12, 1 }, { 13, 1 }, { 14, 1 }, { 15, 1 }, + /* ZSEL == 1 */ + { 16, 2 }, { 17, 3 }, { 18, 4 }, { 19, 6 }, { 20, 8 }, { 21, 12 }, + { 22, 16 }, { 24, 24 }, { 27, 48 }, { 0, 0 } +}; + +static struct clk * __init +sh73a0_cpg_register_clock(struct device_node *np, struct sh73a0_cpg *cpg, + const char *name) +{ + const struct clk_div_table *table = NULL; + unsigned int shift, reg, width; + const char *parent_name; + unsigned int mult = 1; + unsigned int div = 1; + + if (!strcmp(name, "main")) { + /* extal1, extal1_div2, extal2, extal2_div2 */ + u32 parent_idx = (clk_readl(cpg->reg + CPG_CKSCR) >> 28) & 3; + + parent_name = of_clk_get_parent_name(np, parent_idx >> 1); + div = (parent_idx & 1) + 1; + } else if (!strncmp(name, "pll", 3)) { + void __iomem *enable_reg = cpg->reg; + u32 enable_bit = name[3] - '0'; + + parent_name = "main"; + switch (enable_bit) { + case 0: + enable_reg += CPG_PLL0CR; + break; + case 1: + enable_reg += CPG_PLL1CR; + break; + case 2: + enable_reg += CPG_PLL2CR; + break; + case 3: + enable_reg += CPG_PLL3CR; + break; + default: + return ERR_PTR(-EINVAL); + } + if (clk_readl(cpg->reg + CPG_PLLECR) & BIT(enable_bit)) { + mult = ((clk_readl(enable_reg) >> 24) & 0x3f) + 1; + /* handle CFG bit for PLL1 and PLL2 */ + if (enable_bit == 1 || enable_bit == 2) + if (clk_readl(enable_reg) & BIT(20)) + mult *= 2; + } + } else if (!strcmp(name, "dsi0phy") || !strcmp(name, "dsi1phy")) { + u32 phy_no = name[3] - '0'; + void __iomem *dsi_reg = cpg->reg + + (phy_no ? CPG_DSI1PHYCR : CPG_DSI0PHYCR); + + parent_name = phy_no ? "dsi1pck" : "dsi0pck"; + mult = __raw_readl(dsi_reg); + if (!(mult & 0x8000)) + mult = 1; + else + mult = (mult & 0x3f) + 1; + } else if (!strcmp(name, "z")) { + parent_name = "pll0"; + table = z_div_table; + reg = CPG_FRQCRB; + shift = 24; + width = 5; + } else { + struct div4_clk *c; + + for (c = div4_clks; c->name; c++) { + if (!strcmp(name, c->name)) { + parent_name = c->parent; + table = div4_div_table; + reg = c->reg; + shift = c->shift; + width = 4; + break; + } + } + if (!c->name) + return ERR_PTR(-EINVAL); + } + + if (!table) { + return clk_register_fixed_factor(NULL, name, parent_name, 0, + mult, div); + } else { + return clk_register_divider_table(NULL, name, parent_name, 0, + cpg->reg + reg, shift, width, 0, + table, &cpg->lock); + } +} + +static void __init sh73a0_cpg_clocks_init(struct device_node *np) +{ + struct sh73a0_cpg *cpg; + struct clk **clks; + unsigned int i; + int num_clks; + + num_clks = of_property_count_strings(np, "clock-output-names"); + if (num_clks < 0) { + pr_err("%s: failed to count clocks\n", __func__); + return; + } + + cpg = kzalloc(sizeof(*cpg), GFP_KERNEL); + clks = kcalloc(num_clks, sizeof(*clks), GFP_KERNEL); + if (cpg == NULL || clks == NULL) { + /* We're leaking memory on purpose, there's no point in cleaning + * up as the system won't boot anyway. + */ + return; + } + + spin_lock_init(&cpg->lock); + + cpg->data.clks = clks; + cpg->data.clk_num = num_clks; + + cpg->reg = of_iomap(np, 0); + if (WARN_ON(cpg->reg == NULL)) + return; + + /* Set SDHI clocks to a known state */ + clk_writel(0x108, cpg->reg + CPG_SD0CKCR); + clk_writel(0x108, cpg->reg + CPG_SD1CKCR); + clk_writel(0x108, cpg->reg + CPG_SD2CKCR); + + for (i = 0; i < num_clks; ++i) { + const char *name; + struct clk *clk; + + of_property_read_string_index(np, "clock-output-names", i, + &name); + + clk = sh73a0_cpg_register_clock(np, cpg, name); + if (IS_ERR(clk)) + pr_err("%s: failed to register %s %s clock (%ld)\n", + __func__, np->name, name, PTR_ERR(clk)); + else + cpg->data.clks[i] = clk; + } + + of_clk_add_provider(np, of_clk_src_onecell_get, &cpg->data); +} +CLK_OF_DECLARE(sh73a0_cpg_clks, "renesas,sh73a0-cpg-clocks", + sh73a0_cpg_clocks_init); diff --git a/drivers/clk/renesas/r8a7795-cpg-mssr.c b/drivers/clk/renesas/r8a7795-cpg-mssr.c new file mode 100644 index 000000000000..b2198aef5ed4 --- /dev/null +++ b/drivers/clk/renesas/r8a7795-cpg-mssr.c @@ -0,0 +1,638 @@ +/* + * r8a7795 Clock Pulse Generator / Module Standby and Software Reset + * + * Copyright (C) 2015 Glider bvba + * + * Based on clk-rcar-gen3.c + * + * Copyright (C) 2015 Renesas Electronics Corp. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + */ + +#include <linux/bug.h> +#include <linux/clk-provider.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/of.h> +#include <linux/slab.h> + +#include <dt-bindings/clock/r8a7795-cpg-mssr.h> + +#include "renesas-cpg-mssr.h" + + +enum clk_ids { + /* Core Clock Outputs exported to DT */ + LAST_DT_CORE_CLK = R8A7795_CLK_OSC, + + /* External Input Clocks */ + CLK_EXTAL, + CLK_EXTALR, + + /* Internal Core Clocks */ + CLK_MAIN, + CLK_PLL0, + CLK_PLL1, + CLK_PLL2, + CLK_PLL3, + CLK_PLL4, + CLK_PLL1_DIV2, + CLK_PLL1_DIV4, + CLK_S0, + CLK_S1, + CLK_S2, + CLK_S3, + CLK_SDSRC, + CLK_SSPSRC, + + /* Module Clocks */ + MOD_CLK_BASE +}; + +enum r8a7795_clk_types { + CLK_TYPE_GEN3_MAIN = CLK_TYPE_CUSTOM, + CLK_TYPE_GEN3_PLL0, + CLK_TYPE_GEN3_PLL1, + CLK_TYPE_GEN3_PLL2, + CLK_TYPE_GEN3_PLL3, + CLK_TYPE_GEN3_PLL4, + CLK_TYPE_GEN3_SD, +}; + +static const struct cpg_core_clk r8a7795_core_clks[] __initconst = { + /* External Clock Inputs */ + DEF_INPUT("extal", CLK_EXTAL), + DEF_INPUT("extalr", CLK_EXTALR), + + /* Internal Core Clocks */ + DEF_BASE(".main", CLK_MAIN, CLK_TYPE_GEN3_MAIN, CLK_EXTAL), + DEF_BASE(".pll0", CLK_PLL0, CLK_TYPE_GEN3_PLL0, CLK_MAIN), + DEF_BASE(".pll1", CLK_PLL1, CLK_TYPE_GEN3_PLL1, CLK_MAIN), + DEF_BASE(".pll2", CLK_PLL2, CLK_TYPE_GEN3_PLL2, CLK_MAIN), + DEF_BASE(".pll3", CLK_PLL3, CLK_TYPE_GEN3_PLL3, CLK_MAIN), + DEF_BASE(".pll4", CLK_PLL4, CLK_TYPE_GEN3_PLL4, CLK_MAIN), + + DEF_FIXED(".pll1_div2", CLK_PLL1_DIV2, CLK_PLL1, 2, 1), + DEF_FIXED(".pll1_div4", CLK_PLL1_DIV4, CLK_PLL1_DIV2, 2, 1), + DEF_FIXED(".s0", CLK_S0, CLK_PLL1_DIV2, 2, 1), + DEF_FIXED(".s1", CLK_S1, CLK_PLL1_DIV2, 3, 1), + DEF_FIXED(".s2", CLK_S2, CLK_PLL1_DIV2, 4, 1), + DEF_FIXED(".s3", CLK_S3, CLK_PLL1_DIV2, 6, 1), + + /* Core Clock Outputs */ + DEF_FIXED("ztr", R8A7795_CLK_ZTR, CLK_PLL1_DIV2, 6, 1), + DEF_FIXED("ztrd2", R8A7795_CLK_ZTRD2, CLK_PLL1_DIV2, 12, 1), + DEF_FIXED("zt", R8A7795_CLK_ZT, CLK_PLL1_DIV2, 4, 1), + DEF_FIXED("zx", R8A7795_CLK_ZX, CLK_PLL1_DIV2, 2, 1), + DEF_FIXED("s0d1", R8A7795_CLK_S0D1, CLK_S0, 1, 1), + DEF_FIXED("s0d4", R8A7795_CLK_S0D4, CLK_S0, 4, 1), + DEF_FIXED("s1d1", R8A7795_CLK_S1D1, CLK_S1, 1, 1), + DEF_FIXED("s1d2", R8A7795_CLK_S1D2, CLK_S1, 2, 1), + DEF_FIXED("s1d4", R8A7795_CLK_S1D4, CLK_S1, 4, 1), + DEF_FIXED("s2d1", R8A7795_CLK_S2D1, CLK_S2, 1, 1), + DEF_FIXED("s2d2", R8A7795_CLK_S2D2, CLK_S2, 2, 1), + DEF_FIXED("s2d4", R8A7795_CLK_S2D4, CLK_S2, 4, 1), + DEF_FIXED("s3d1", R8A7795_CLK_S3D1, CLK_S3, 1, 1), + DEF_FIXED("s3d2", R8A7795_CLK_S3D2, CLK_S3, 2, 1), + DEF_FIXED("s3d4", R8A7795_CLK_S3D4, CLK_S3, 4, 1), + + DEF_SD("sd0", R8A7795_CLK_SD0, CLK_PLL1_DIV2, 0x0074), + DEF_SD("sd1", R8A7795_CLK_SD1, CLK_PLL1_DIV2, 0x0078), + DEF_SD("sd2", R8A7795_CLK_SD2, CLK_PLL1_DIV2, 0x0268), + DEF_SD("sd3", R8A7795_CLK_SD3, CLK_PLL1_DIV2, 0x026c), + + DEF_FIXED("cl", R8A7795_CLK_CL, CLK_PLL1_DIV2, 48, 1), + DEF_FIXED("cp", R8A7795_CLK_CP, CLK_EXTAL, 2, 1), + + DEF_DIV6P1("mso", R8A7795_CLK_MSO, CLK_PLL1_DIV4, 0x014), + DEF_DIV6P1("hdmi", R8A7795_CLK_HDMI, CLK_PLL1_DIV2, 0x250), + DEF_DIV6P1("canfd", R8A7795_CLK_CANFD, CLK_PLL1_DIV4, 0x244), +}; + +static const struct mssr_mod_clk r8a7795_mod_clks[] __initconst = { + DEF_MOD("scif5", 202, R8A7795_CLK_S3D4), + DEF_MOD("scif4", 203, R8A7795_CLK_S3D4), + DEF_MOD("scif3", 204, R8A7795_CLK_S3D4), + DEF_MOD("scif1", 206, R8A7795_CLK_S3D4), + DEF_MOD("scif0", 207, R8A7795_CLK_S3D4), + DEF_MOD("msiof3", 208, R8A7795_CLK_MSO), + DEF_MOD("msiof2", 209, R8A7795_CLK_MSO), + DEF_MOD("msiof1", 210, R8A7795_CLK_MSO), + DEF_MOD("msiof0", 211, R8A7795_CLK_MSO), + DEF_MOD("sys-dmac2", 217, R8A7795_CLK_S3D1), + DEF_MOD("sys-dmac1", 218, R8A7795_CLK_S3D1), + DEF_MOD("sys-dmac0", 219, R8A7795_CLK_S3D1), + DEF_MOD("scif2", 310, R8A7795_CLK_S3D4), + DEF_MOD("sdif3", 311, R8A7795_CLK_SD3), + DEF_MOD("sdif2", 312, R8A7795_CLK_SD2), + DEF_MOD("sdif1", 313, R8A7795_CLK_SD1), + DEF_MOD("sdif0", 314, R8A7795_CLK_SD0), + DEF_MOD("pcie1", 318, R8A7795_CLK_S3D1), + DEF_MOD("pcie0", 319, R8A7795_CLK_S3D1), + DEF_MOD("usb3-if1", 327, R8A7795_CLK_S3D1), + DEF_MOD("usb3-if0", 328, R8A7795_CLK_S3D1), + DEF_MOD("usb-dmac0", 330, R8A7795_CLK_S3D1), + DEF_MOD("usb-dmac1", 331, R8A7795_CLK_S3D1), + DEF_MOD("intc-ex", 407, R8A7795_CLK_CP), + DEF_MOD("intc-ap", 408, R8A7795_CLK_S3D1), + DEF_MOD("audmac0", 502, R8A7795_CLK_S3D4), + DEF_MOD("audmac1", 501, R8A7795_CLK_S3D4), + DEF_MOD("hscif4", 516, R8A7795_CLK_S3D1), + DEF_MOD("hscif3", 517, R8A7795_CLK_S3D1), + DEF_MOD("hscif2", 518, R8A7795_CLK_S3D1), + DEF_MOD("hscif1", 519, R8A7795_CLK_S3D1), + DEF_MOD("hscif0", 520, R8A7795_CLK_S3D1), + DEF_MOD("fcpvd3", 600, R8A7795_CLK_S2D1), + DEF_MOD("fcpvd2", 601, R8A7795_CLK_S2D1), + DEF_MOD("fcpvd1", 602, R8A7795_CLK_S2D1), + DEF_MOD("fcpvd0", 603, R8A7795_CLK_S2D1), + DEF_MOD("fcpvb1", 606, R8A7795_CLK_S2D1), + DEF_MOD("fcpvb0", 607, R8A7795_CLK_S2D1), + DEF_MOD("fcpvi2", 609, R8A7795_CLK_S2D1), + DEF_MOD("fcpvi1", 610, R8A7795_CLK_S2D1), + DEF_MOD("fcpvi0", 611, R8A7795_CLK_S2D1), + DEF_MOD("fcpf2", 613, R8A7795_CLK_S2D1), + DEF_MOD("fcpf1", 614, R8A7795_CLK_S2D1), + DEF_MOD("fcpf0", 615, R8A7795_CLK_S2D1), + DEF_MOD("fcpci1", 616, R8A7795_CLK_S2D1), + DEF_MOD("fcpci0", 617, R8A7795_CLK_S2D1), + DEF_MOD("fcpcs", 619, R8A7795_CLK_S2D1), + DEF_MOD("vspd3", 620, R8A7795_CLK_S2D1), + DEF_MOD("vspd2", 621, R8A7795_CLK_S2D1), + DEF_MOD("vspd1", 622, R8A7795_CLK_S2D1), + DEF_MOD("vspd0", 623, R8A7795_CLK_S2D1), + DEF_MOD("vspbc", 624, R8A7795_CLK_S2D1), + DEF_MOD("vspbd", 626, R8A7795_CLK_S2D1), + DEF_MOD("vspi2", 629, R8A7795_CLK_S2D1), + DEF_MOD("vspi1", 630, R8A7795_CLK_S2D1), + DEF_MOD("vspi0", 631, R8A7795_CLK_S2D1), + DEF_MOD("ehci2", 701, R8A7795_CLK_S3D4), + DEF_MOD("ehci1", 702, R8A7795_CLK_S3D4), + DEF_MOD("ehci0", 703, R8A7795_CLK_S3D4), + DEF_MOD("hsusb", 704, R8A7795_CLK_S3D4), + DEF_MOD("du3", 721, R8A7795_CLK_S2D1), + DEF_MOD("du2", 722, R8A7795_CLK_S2D1), + DEF_MOD("du1", 723, R8A7795_CLK_S2D1), + DEF_MOD("du0", 724, R8A7795_CLK_S2D1), + DEF_MOD("lvds", 727, R8A7795_CLK_S2D1), + DEF_MOD("hdmi1", 728, R8A7795_CLK_HDMI), + DEF_MOD("hdmi0", 729, R8A7795_CLK_HDMI), + DEF_MOD("etheravb", 812, R8A7795_CLK_S3D2), + DEF_MOD("sata0", 815, R8A7795_CLK_S3D2), + DEF_MOD("gpio7", 905, R8A7795_CLK_CP), + DEF_MOD("gpio6", 906, R8A7795_CLK_CP), + DEF_MOD("gpio5", 907, R8A7795_CLK_CP), + DEF_MOD("gpio4", 908, R8A7795_CLK_CP), + DEF_MOD("gpio3", 909, R8A7795_CLK_CP), + DEF_MOD("gpio2", 910, R8A7795_CLK_CP), + DEF_MOD("gpio1", 911, R8A7795_CLK_CP), + DEF_MOD("gpio0", 912, R8A7795_CLK_CP), + DEF_MOD("can-fd", 914, R8A7795_CLK_S3D2), + DEF_MOD("can-if1", 915, R8A7795_CLK_S3D4), + DEF_MOD("can-if0", 916, R8A7795_CLK_S3D4), + DEF_MOD("i2c6", 918, R8A7795_CLK_S3D2), + DEF_MOD("i2c5", 919, R8A7795_CLK_S3D2), + DEF_MOD("i2c4", 927, R8A7795_CLK_S3D2), + DEF_MOD("i2c3", 928, R8A7795_CLK_S3D2), + DEF_MOD("i2c2", 929, R8A7795_CLK_S3D2), + DEF_MOD("i2c1", 930, R8A7795_CLK_S3D2), + DEF_MOD("i2c0", 931, R8A7795_CLK_S3D2), + DEF_MOD("ssi-all", 1005, R8A7795_CLK_S3D4), + DEF_MOD("ssi9", 1006, MOD_CLK_ID(1005)), + DEF_MOD("ssi8", 1007, MOD_CLK_ID(1005)), + DEF_MOD("ssi7", 1008, MOD_CLK_ID(1005)), + DEF_MOD("ssi6", 1009, MOD_CLK_ID(1005)), + DEF_MOD("ssi5", 1010, MOD_CLK_ID(1005)), + DEF_MOD("ssi4", 1011, MOD_CLK_ID(1005)), + DEF_MOD("ssi3", 1012, MOD_CLK_ID(1005)), + DEF_MOD("ssi2", 1013, MOD_CLK_ID(1005)), + DEF_MOD("ssi1", 1014, MOD_CLK_ID(1005)), + DEF_MOD("ssi0", 1015, MOD_CLK_ID(1005)), + DEF_MOD("scu-all", 1017, R8A7795_CLK_S3D4), + DEF_MOD("scu-dvc1", 1018, MOD_CLK_ID(1017)), + DEF_MOD("scu-dvc0", 1019, MOD_CLK_ID(1017)), + DEF_MOD("scu-ctu1-mix1", 1020, MOD_CLK_ID(1017)), + DEF_MOD("scu-ctu0-mix0", 1021, MOD_CLK_ID(1017)), + DEF_MOD("scu-src9", 1022, MOD_CLK_ID(1017)), + DEF_MOD("scu-src8", 1023, MOD_CLK_ID(1017)), + DEF_MOD("scu-src7", 1024, MOD_CLK_ID(1017)), + DEF_MOD("scu-src6", 1025, MOD_CLK_ID(1017)), + DEF_MOD("scu-src5", 1026, MOD_CLK_ID(1017)), + DEF_MOD("scu-src4", 1027, MOD_CLK_ID(1017)), + DEF_MOD("scu-src3", 1028, MOD_CLK_ID(1017)), + DEF_MOD("scu-src2", 1029, MOD_CLK_ID(1017)), + DEF_MOD("scu-src1", 1030, MOD_CLK_ID(1017)), + DEF_MOD("scu-src0", 1031, MOD_CLK_ID(1017)), +}; + +static const unsigned int r8a7795_crit_mod_clks[] __initconst = { + MOD_CLK_ID(408), /* INTC-AP (GIC) */ +}; + +/* ----------------------------------------------------------------------------- + * SDn Clock + * + */ +#define CPG_SD_STP_HCK BIT(9) +#define CPG_SD_STP_CK BIT(8) + +#define CPG_SD_STP_MASK (CPG_SD_STP_HCK | CPG_SD_STP_CK) +#define CPG_SD_FC_MASK (0x7 << 2 | 0x3 << 0) + +#define CPG_SD_DIV_TABLE_DATA(stp_hck, stp_ck, sd_srcfc, sd_fc, sd_div) \ +{ \ + .val = ((stp_hck) ? CPG_SD_STP_HCK : 0) | \ + ((stp_ck) ? CPG_SD_STP_CK : 0) | \ + ((sd_srcfc) << 2) | \ + ((sd_fc) << 0), \ + .div = (sd_div), \ +} + +struct sd_div_table { + u32 val; + unsigned int div; +}; + +struct sd_clock { + struct clk_hw hw; + void __iomem *reg; + const struct sd_div_table *div_table; + unsigned int div_num; + unsigned int div_min; + unsigned int div_max; +}; + +/* SDn divider + * sd_srcfc sd_fc div + * stp_hck stp_ck (div) (div) = sd_srcfc x sd_fc + *------------------------------------------------------------------- + * 0 0 0 (1) 1 (4) 4 + * 0 0 1 (2) 1 (4) 8 + * 1 0 2 (4) 1 (4) 16 + * 1 0 3 (8) 1 (4) 32 + * 1 0 4 (16) 1 (4) 64 + * 0 0 0 (1) 0 (2) 2 + * 0 0 1 (2) 0 (2) 4 + * 1 0 2 (4) 0 (2) 8 + * 1 0 3 (8) 0 (2) 16 + * 1 0 4 (16) 0 (2) 32 + */ +static const struct sd_div_table cpg_sd_div_table[] = { +/* CPG_SD_DIV_TABLE_DATA(stp_hck, stp_ck, sd_srcfc, sd_fc, sd_div) */ + CPG_SD_DIV_TABLE_DATA(0, 0, 0, 1, 4), + CPG_SD_DIV_TABLE_DATA(0, 0, 1, 1, 8), + CPG_SD_DIV_TABLE_DATA(1, 0, 2, 1, 16), + CPG_SD_DIV_TABLE_DATA(1, 0, 3, 1, 32), + CPG_SD_DIV_TABLE_DATA(1, 0, 4, 1, 64), + CPG_SD_DIV_TABLE_DATA(0, 0, 0, 0, 2), + CPG_SD_DIV_TABLE_DATA(0, 0, 1, 0, 4), + CPG_SD_DIV_TABLE_DATA(1, 0, 2, 0, 8), + CPG_SD_DIV_TABLE_DATA(1, 0, 3, 0, 16), + CPG_SD_DIV_TABLE_DATA(1, 0, 4, 0, 32), +}; + +#define to_sd_clock(_hw) container_of(_hw, struct sd_clock, hw) + +static int cpg_sd_clock_enable(struct clk_hw *hw) +{ + struct sd_clock *clock = to_sd_clock(hw); + u32 val, sd_fc; + unsigned int i; + + val = clk_readl(clock->reg); + + sd_fc = val & CPG_SD_FC_MASK; + for (i = 0; i < clock->div_num; i++) + if (sd_fc == (clock->div_table[i].val & CPG_SD_FC_MASK)) + break; + + if (i >= clock->div_num) + return -EINVAL; + + val &= ~(CPG_SD_STP_MASK); + val |= clock->div_table[i].val & CPG_SD_STP_MASK; + + clk_writel(val, clock->reg); + + return 0; +} + +static void cpg_sd_clock_disable(struct clk_hw *hw) +{ + struct sd_clock *clock = to_sd_clock(hw); + + clk_writel(clk_readl(clock->reg) | CPG_SD_STP_MASK, clock->reg); +} + +static int cpg_sd_clock_is_enabled(struct clk_hw *hw) +{ + struct sd_clock *clock = to_sd_clock(hw); + + return !(clk_readl(clock->reg) & CPG_SD_STP_MASK); +} + +static unsigned long cpg_sd_clock_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct sd_clock *clock = to_sd_clock(hw); + unsigned long rate = parent_rate; + u32 val, sd_fc; + unsigned int i; + + val = clk_readl(clock->reg); + + sd_fc = val & CPG_SD_FC_MASK; + for (i = 0; i < clock->div_num; i++) + if (sd_fc == (clock->div_table[i].val & CPG_SD_FC_MASK)) + break; + + if (i >= clock->div_num) + return -EINVAL; + + return DIV_ROUND_CLOSEST(rate, clock->div_table[i].div); +} + +static unsigned int cpg_sd_clock_calc_div(struct sd_clock *clock, + unsigned long rate, + unsigned long parent_rate) +{ + unsigned int div; + + if (!rate) + rate = 1; + + div = DIV_ROUND_CLOSEST(parent_rate, rate); + + return clamp_t(unsigned int, div, clock->div_min, clock->div_max); +} + +static long cpg_sd_clock_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct sd_clock *clock = to_sd_clock(hw); + unsigned int div = cpg_sd_clock_calc_div(clock, rate, *parent_rate); + + return DIV_ROUND_CLOSEST(*parent_rate, div); +} + +static int cpg_sd_clock_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct sd_clock *clock = to_sd_clock(hw); + unsigned int div = cpg_sd_clock_calc_div(clock, rate, parent_rate); + u32 val; + unsigned int i; + + for (i = 0; i < clock->div_num; i++) + if (div == clock->div_table[i].div) + break; + + if (i >= clock->div_num) + return -EINVAL; + + val = clk_readl(clock->reg); + val &= ~(CPG_SD_STP_MASK | CPG_SD_FC_MASK); + val |= clock->div_table[i].val & (CPG_SD_STP_MASK | CPG_SD_FC_MASK); + clk_writel(val, clock->reg); + + return 0; +} + +static const struct clk_ops cpg_sd_clock_ops = { + .enable = cpg_sd_clock_enable, + .disable = cpg_sd_clock_disable, + .is_enabled = cpg_sd_clock_is_enabled, + .recalc_rate = cpg_sd_clock_recalc_rate, + .round_rate = cpg_sd_clock_round_rate, + .set_rate = cpg_sd_clock_set_rate, +}; + +static struct clk * __init cpg_sd_clk_register(const struct cpg_core_clk *core, + void __iomem *base, + const char *parent_name) +{ + struct clk_init_data init; + struct sd_clock *clock; + struct clk *clk; + unsigned int i; + + clock = kzalloc(sizeof(*clock), GFP_KERNEL); + if (!clock) + return ERR_PTR(-ENOMEM); + + init.name = core->name; + init.ops = &cpg_sd_clock_ops; + init.flags = CLK_IS_BASIC | CLK_SET_RATE_PARENT; + init.parent_names = &parent_name; + init.num_parents = 1; + + clock->reg = base + core->offset; + clock->hw.init = &init; + clock->div_table = cpg_sd_div_table; + clock->div_num = ARRAY_SIZE(cpg_sd_div_table); + + clock->div_max = clock->div_table[0].div; + clock->div_min = clock->div_max; + for (i = 1; i < clock->div_num; i++) { + clock->div_max = max(clock->div_max, clock->div_table[i].div); + clock->div_min = min(clock->div_min, clock->div_table[i].div); + } + + clk = clk_register(NULL, &clock->hw); + if (IS_ERR(clk)) + kfree(clock); + + return clk; +} + +#define CPG_PLL0CR 0x00d8 +#define CPG_PLL2CR 0x002c +#define CPG_PLL4CR 0x01f4 + +/* + * CPG Clock Data + */ + +/* + * MD EXTAL PLL0 PLL1 PLL2 PLL3 PLL4 + * 14 13 19 17 (MHz) + *------------------------------------------------------------------- + * 0 0 0 0 16.66 x 1 x180 x192 x144 x192 x144 + * 0 0 0 1 16.66 x 1 x180 x192 x144 x128 x144 + * 0 0 1 0 Prohibited setting + * 0 0 1 1 16.66 x 1 x180 x192 x144 x192 x144 + * 0 1 0 0 20 x 1 x150 x160 x120 x160 x120 + * 0 1 0 1 20 x 1 x150 x160 x120 x106 x120 + * 0 1 1 0 Prohibited setting + * 0 1 1 1 20 x 1 x150 x160 x120 x160 x120 + * 1 0 0 0 25 x 1 x120 x128 x96 x128 x96 + * 1 0 0 1 25 x 1 x120 x128 x96 x84 x96 + * 1 0 1 0 Prohibited setting + * 1 0 1 1 25 x 1 x120 x128 x96 x128 x96 + * 1 1 0 0 33.33 / 2 x180 x192 x144 x192 x144 + * 1 1 0 1 33.33 / 2 x180 x192 x144 x128 x144 + * 1 1 1 0 Prohibited setting + * 1 1 1 1 33.33 / 2 x180 x192 x144 x192 x144 + */ +#define CPG_PLL_CONFIG_INDEX(md) ((((md) & BIT(14)) >> 11) | \ + (((md) & BIT(13)) >> 11) | \ + (((md) & BIT(19)) >> 18) | \ + (((md) & BIT(17)) >> 17)) + +struct cpg_pll_config { + unsigned int extal_div; + unsigned int pll1_mult; + unsigned int pll3_mult; +}; + +static const struct cpg_pll_config cpg_pll_configs[16] __initconst = { + /* EXTAL div PLL1 mult PLL3 mult */ + { 1, 192, 192, }, + { 1, 192, 128, }, + { 0, /* Prohibited setting */ }, + { 1, 192, 192, }, + { 1, 160, 160, }, + { 1, 160, 106, }, + { 0, /* Prohibited setting */ }, + { 1, 160, 160, }, + { 1, 128, 128, }, + { 1, 128, 84, }, + { 0, /* Prohibited setting */ }, + { 1, 128, 128, }, + { 2, 192, 192, }, + { 2, 192, 128, }, + { 0, /* Prohibited setting */ }, + { 2, 192, 192, }, +}; + +static const struct cpg_pll_config *cpg_pll_config __initdata; + +static +struct clk * __init r8a7795_cpg_clk_register(struct device *dev, + const struct cpg_core_clk *core, + const struct cpg_mssr_info *info, + struct clk **clks, + void __iomem *base) +{ + const struct clk *parent; + unsigned int mult = 1; + unsigned int div = 1; + u32 value; + + parent = clks[core->parent]; + if (IS_ERR(parent)) + return ERR_CAST(parent); + + switch (core->type) { + case CLK_TYPE_GEN3_MAIN: + div = cpg_pll_config->extal_div; + break; + + case CLK_TYPE_GEN3_PLL0: + /* + * PLL0 is a configurable multiplier clock. Register it 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. + */ + value = readl(base + CPG_PLL0CR); + mult = (((value >> 24) & 0x7f) + 1) * 2; + break; + + case CLK_TYPE_GEN3_PLL1: + mult = cpg_pll_config->pll1_mult; + break; + + case CLK_TYPE_GEN3_PLL2: + /* + * PLL2 is a configurable multiplier clock. Register it 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. + */ + value = readl(base + CPG_PLL2CR); + mult = (((value >> 24) & 0x7f) + 1) * 2; + break; + + case CLK_TYPE_GEN3_PLL3: + mult = cpg_pll_config->pll3_mult; + break; + + case CLK_TYPE_GEN3_PLL4: + /* + * PLL4 is a configurable multiplier clock. Register it 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. + */ + value = readl(base + CPG_PLL4CR); + mult = (((value >> 24) & 0x7f) + 1) * 2; + break; + + case CLK_TYPE_GEN3_SD: + return cpg_sd_clk_register(core, base, __clk_get_name(parent)); + + default: + return ERR_PTR(-EINVAL); + } + + return clk_register_fixed_factor(NULL, core->name, + __clk_get_name(parent), 0, mult, div); +} + +/* + * Reset register definitions. + */ +#define MODEMR 0xe6160060 + +static u32 rcar_gen3_read_mode_pins(void) +{ + void __iomem *modemr = ioremap_nocache(MODEMR, 4); + u32 mode; + + BUG_ON(!modemr); + mode = ioread32(modemr); + iounmap(modemr); + + return mode; +} + +static int __init r8a7795_cpg_mssr_init(struct device *dev) +{ + u32 cpg_mode = rcar_gen3_read_mode_pins(); + + cpg_pll_config = &cpg_pll_configs[CPG_PLL_CONFIG_INDEX(cpg_mode)]; + if (!cpg_pll_config->extal_div) { + dev_err(dev, "Prohibited setting (cpg_mode=0x%x)\n", cpg_mode); + return -EINVAL; + } + + return 0; +} + +const struct cpg_mssr_info r8a7795_cpg_mssr_info __initconst = { + /* Core Clocks */ + .core_clks = r8a7795_core_clks, + .num_core_clks = ARRAY_SIZE(r8a7795_core_clks), + .last_dt_core_clk = LAST_DT_CORE_CLK, + .num_total_core_clks = MOD_CLK_BASE, + + /* Module Clocks */ + .mod_clks = r8a7795_mod_clks, + .num_mod_clks = ARRAY_SIZE(r8a7795_mod_clks), + .num_hw_mod_clks = 12 * 32, + + /* Critical Module Clocks */ + .crit_mod_clks = r8a7795_crit_mod_clks, + .num_crit_mod_clks = ARRAY_SIZE(r8a7795_crit_mod_clks), + + /* Callbacks */ + .init = r8a7795_cpg_mssr_init, + .cpg_clk_register = r8a7795_cpg_clk_register, +}; diff --git a/drivers/clk/renesas/renesas-cpg-mssr.c b/drivers/clk/renesas/renesas-cpg-mssr.c new file mode 100644 index 000000000000..58e24b326a48 --- /dev/null +++ b/drivers/clk/renesas/renesas-cpg-mssr.c @@ -0,0 +1,601 @@ +/* + * Renesas Clock Pulse Generator / Module Standby and Software Reset + * + * Copyright (C) 2015 Glider bvba + * + * Based on clk-mstp.c, clk-rcar-gen2.c, and clk-rcar-gen3.c + * + * Copyright (C) 2013 Ideas On Board SPRL + * Copyright (C) 2015 Renesas Electronics Corp. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + */ + +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/device.h> +#include <linux/init.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/of_address.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/pm_clock.h> +#include <linux/pm_domain.h> +#include <linux/slab.h> + +#include <dt-bindings/clock/renesas-cpg-mssr.h> + +#include "renesas-cpg-mssr.h" +#include "clk-div6.h" + +#ifdef DEBUG +#define WARN_DEBUG(x) do { } while (0) +#else +#define WARN_DEBUG(x) WARN_ON(x) +#endif + + +/* + * Module Standby and Software Reset register offets. + * + * If the registers exist, these are valid for SH-Mobile, R-Mobile, + * R-Car Gen 2, and R-Car Gen 3. + * These are NOT valid for R-Car Gen1 and RZ/A1! + */ + +/* + * Module Stop Status Register offsets + */ + +static const u16 mstpsr[] = { + 0x030, 0x038, 0x040, 0x048, 0x04C, 0x03C, 0x1C0, 0x1C4, + 0x9A0, 0x9A4, 0x9A8, 0x9AC, +}; + +#define MSTPSR(i) mstpsr[i] + + +/* + * System Module Stop Control Register offsets + */ + +static const u16 smstpcr[] = { + 0x130, 0x134, 0x138, 0x13C, 0x140, 0x144, 0x148, 0x14C, + 0x990, 0x994, 0x998, 0x99C, +}; + +#define SMSTPCR(i) smstpcr[i] + + +/* + * Software Reset Register offsets + */ + +static const u16 srcr[] = { + 0x0A0, 0x0A8, 0x0B0, 0x0B8, 0x0BC, 0x0C4, 0x1C8, 0x1CC, + 0x920, 0x924, 0x928, 0x92C, +}; + +#define SRCR(i) srcr[i] + + +/* Realtime Module Stop Control Register offsets */ +#define RMSTPCR(i) (smstpcr[i] - 0x20) + +/* Modem Module Stop Control Register offsets (r8a73a4) */ +#define MMSTPCR(i) (smstpcr[i] + 0x20) + +/* Software Reset Clearing Register offsets */ +#define SRSTCLR(i) (0x940 + (i) * 4) + + +/** + * Clock Pulse Generator / Module Standby and Software Reset Private Data + * + * @dev: CPG/MSSR device + * @base: CPG/MSSR register block base address + * @mstp_lock: protects writes to SMSTPCR + * @clks: Array containing all Core and Module Clocks + * @num_core_clks: Number of Core Clocks in clks[] + * @num_mod_clks: Number of Module Clocks in clks[] + * @last_dt_core_clk: ID of the last Core Clock exported to DT + */ +struct cpg_mssr_priv { + struct device *dev; + void __iomem *base; + spinlock_t mstp_lock; + + struct clk **clks; + unsigned int num_core_clks; + unsigned int num_mod_clks; + unsigned int last_dt_core_clk; +}; + + +/** + * struct mstp_clock - MSTP gating clock + * @hw: handle between common and hardware-specific interfaces + * @index: MSTP clock number + * @priv: CPG/MSSR private data + */ +struct mstp_clock { + struct clk_hw hw; + u32 index; + struct cpg_mssr_priv *priv; +}; + +#define to_mstp_clock(_hw) container_of(_hw, struct mstp_clock, hw) + +static int cpg_mstp_clock_endisable(struct clk_hw *hw, bool enable) +{ + struct mstp_clock *clock = to_mstp_clock(hw); + struct cpg_mssr_priv *priv = clock->priv; + unsigned int reg = clock->index / 32; + unsigned int bit = clock->index % 32; + struct device *dev = priv->dev; + u32 bitmask = BIT(bit); + unsigned long flags; + unsigned int i; + u32 value; + + dev_dbg(dev, "MSTP %u%02u/%pC %s\n", reg, bit, hw->clk, + enable ? "ON" : "OFF"); + spin_lock_irqsave(&priv->mstp_lock, flags); + + value = clk_readl(priv->base + SMSTPCR(reg)); + if (enable) + value &= ~bitmask; + else + value |= bitmask; + clk_writel(value, priv->base + SMSTPCR(reg)); + + spin_unlock_irqrestore(&priv->mstp_lock, flags); + + if (!enable) + return 0; + + for (i = 1000; i > 0; --i) { + if (!(clk_readl(priv->base + MSTPSR(reg)) & + bitmask)) + break; + cpu_relax(); + } + + if (!i) { + dev_err(dev, "Failed to enable SMSTP %p[%d]\n", + priv->base + SMSTPCR(reg), bit); + return -ETIMEDOUT; + } + + return 0; +} + +static int cpg_mstp_clock_enable(struct clk_hw *hw) +{ + return cpg_mstp_clock_endisable(hw, true); +} + +static void cpg_mstp_clock_disable(struct clk_hw *hw) +{ + cpg_mstp_clock_endisable(hw, false); +} + +static int cpg_mstp_clock_is_enabled(struct clk_hw *hw) +{ + struct mstp_clock *clock = to_mstp_clock(hw); + struct cpg_mssr_priv *priv = clock->priv; + u32 value; + + value = clk_readl(priv->base + MSTPSR(clock->index / 32)); + + return !(value & BIT(clock->index % 32)); +} + +static const struct clk_ops cpg_mstp_clock_ops = { + .enable = cpg_mstp_clock_enable, + .disable = cpg_mstp_clock_disable, + .is_enabled = cpg_mstp_clock_is_enabled, +}; + +static +struct clk *cpg_mssr_clk_src_twocell_get(struct of_phandle_args *clkspec, + void *data) +{ + unsigned int clkidx = clkspec->args[1]; + struct cpg_mssr_priv *priv = data; + struct device *dev = priv->dev; + unsigned int idx; + const char *type; + struct clk *clk; + + switch (clkspec->args[0]) { + case CPG_CORE: + type = "core"; + if (clkidx > priv->last_dt_core_clk) { + dev_err(dev, "Invalid %s clock index %u\n", type, + clkidx); + return ERR_PTR(-EINVAL); + } + clk = priv->clks[clkidx]; + break; + + case CPG_MOD: + type = "module"; + idx = MOD_CLK_PACK(clkidx); + if (clkidx % 100 > 31 || idx >= priv->num_mod_clks) { + dev_err(dev, "Invalid %s clock index %u\n", type, + clkidx); + return ERR_PTR(-EINVAL); + } + clk = priv->clks[priv->num_core_clks + idx]; + break; + + default: + dev_err(dev, "Invalid CPG clock type %u\n", clkspec->args[0]); + return ERR_PTR(-EINVAL); + } + + if (IS_ERR(clk)) + dev_err(dev, "Cannot get %s clock %u: %ld", type, clkidx, + PTR_ERR(clk)); + else + dev_dbg(dev, "clock (%u, %u) is %pC at %pCr Hz\n", + clkspec->args[0], clkspec->args[1], clk, clk); + return clk; +} + +static void __init cpg_mssr_register_core_clk(const struct cpg_core_clk *core, + const struct cpg_mssr_info *info, + struct cpg_mssr_priv *priv) +{ + struct clk *clk = NULL, *parent; + struct device *dev = priv->dev; + unsigned int id = core->id; + const char *parent_name; + + WARN_DEBUG(id >= priv->num_core_clks); + WARN_DEBUG(PTR_ERR(priv->clks[id]) != -ENOENT); + + switch (core->type) { + case CLK_TYPE_IN: + clk = of_clk_get_by_name(priv->dev->of_node, core->name); + break; + + case CLK_TYPE_FF: + case CLK_TYPE_DIV6P1: + WARN_DEBUG(core->parent >= priv->num_core_clks); + parent = priv->clks[core->parent]; + if (IS_ERR(parent)) { + clk = parent; + goto fail; + } + + parent_name = __clk_get_name(parent); + if (core->type == CLK_TYPE_FF) { + clk = clk_register_fixed_factor(NULL, core->name, + parent_name, 0, + core->mult, core->div); + } else { + clk = cpg_div6_register(core->name, 1, &parent_name, + priv->base + core->offset); + } + break; + + default: + if (info->cpg_clk_register) + clk = info->cpg_clk_register(dev, core, info, + priv->clks, priv->base); + else + dev_err(dev, "%s has unsupported core clock type %u\n", + core->name, core->type); + break; + } + + if (IS_ERR_OR_NULL(clk)) + goto fail; + + dev_dbg(dev, "Core clock %pC at %pCr Hz\n", clk, clk); + priv->clks[id] = clk; + return; + +fail: + dev_err(dev, "Failed to register %s clock %s: %ld\n", "core,", + core->name, PTR_ERR(clk)); +} + +static void __init cpg_mssr_register_mod_clk(const struct mssr_mod_clk *mod, + const struct cpg_mssr_info *info, + struct cpg_mssr_priv *priv) +{ + struct mstp_clock *clock = NULL; + struct device *dev = priv->dev; + unsigned int id = mod->id; + struct clk_init_data init; + struct clk *parent, *clk; + const char *parent_name; + unsigned int i; + + WARN_DEBUG(id < priv->num_core_clks); + WARN_DEBUG(id >= priv->num_core_clks + priv->num_mod_clks); + WARN_DEBUG(mod->parent >= priv->num_core_clks + priv->num_mod_clks); + WARN_DEBUG(PTR_ERR(priv->clks[id]) != -ENOENT); + + parent = priv->clks[mod->parent]; + if (IS_ERR(parent)) { + clk = parent; + goto fail; + } + + clock = kzalloc(sizeof(*clock), GFP_KERNEL); + if (!clock) { + clk = ERR_PTR(-ENOMEM); + goto fail; + } + + init.name = mod->name; + init.ops = &cpg_mstp_clock_ops; + init.flags = CLK_IS_BASIC | CLK_SET_RATE_PARENT; + for (i = 0; i < info->num_crit_mod_clks; i++) + if (id == info->crit_mod_clks[i]) { +#ifdef CLK_ENABLE_HAND_OFF + dev_dbg(dev, "MSTP %s setting CLK_ENABLE_HAND_OFF\n", + mod->name); + init.flags |= CLK_ENABLE_HAND_OFF; + break; +#else + dev_dbg(dev, "Ignoring MSTP %s to prevent disabling\n", + mod->name); + kfree(clock); + return; +#endif + } + + parent_name = __clk_get_name(parent); + init.parent_names = &parent_name; + init.num_parents = 1; + + clock->index = id - priv->num_core_clks; + clock->priv = priv; + clock->hw.init = &init; + + clk = clk_register(NULL, &clock->hw); + if (IS_ERR(clk)) + goto fail; + + dev_dbg(dev, "Module clock %pC at %pCr Hz\n", clk, clk); + priv->clks[id] = clk; + return; + +fail: + dev_err(dev, "Failed to register %s clock %s: %ld\n", "module,", + mod->name, PTR_ERR(clk)); + kfree(clock); +} + + +#ifdef CONFIG_PM_GENERIC_DOMAINS_OF +struct cpg_mssr_clk_domain { + struct generic_pm_domain genpd; + struct device_node *np; + unsigned int num_core_pm_clks; + unsigned int core_pm_clks[0]; +}; + +static bool cpg_mssr_is_pm_clk(const struct of_phandle_args *clkspec, + struct cpg_mssr_clk_domain *pd) +{ + unsigned int i; + + if (clkspec->np != pd->np || clkspec->args_count != 2) + return false; + + switch (clkspec->args[0]) { + case CPG_CORE: + for (i = 0; i < pd->num_core_pm_clks; i++) + if (clkspec->args[1] == pd->core_pm_clks[i]) + return true; + return false; + + case CPG_MOD: + return true; + + default: + return false; + } +} + +static int cpg_mssr_attach_dev(struct generic_pm_domain *genpd, + struct device *dev) +{ + struct cpg_mssr_clk_domain *pd = + container_of(genpd, struct cpg_mssr_clk_domain, genpd); + struct device_node *np = dev->of_node; + struct of_phandle_args clkspec; + struct clk *clk; + int i = 0; + int error; + + while (!of_parse_phandle_with_args(np, "clocks", "#clock-cells", i, + &clkspec)) { + if (cpg_mssr_is_pm_clk(&clkspec, pd)) + goto found; + + of_node_put(clkspec.np); + i++; + } + + return 0; + +found: + clk = of_clk_get_from_provider(&clkspec); + of_node_put(clkspec.np); + + if (IS_ERR(clk)) + return PTR_ERR(clk); + + error = pm_clk_create(dev); + if (error) { + dev_err(dev, "pm_clk_create failed %d\n", error); + goto fail_put; + } + + error = pm_clk_add_clk(dev, clk); + if (error) { + dev_err(dev, "pm_clk_add_clk %pC failed %d\n", clk, error); + goto fail_destroy; + } + + return 0; + +fail_destroy: + pm_clk_destroy(dev); +fail_put: + clk_put(clk); + return error; +} + +static void cpg_mssr_detach_dev(struct generic_pm_domain *genpd, + struct device *dev) +{ + if (!list_empty(&dev->power.subsys_data->clock_list)) + pm_clk_destroy(dev); +} + +static int __init cpg_mssr_add_clk_domain(struct device *dev, + const unsigned int *core_pm_clks, + unsigned int num_core_pm_clks) +{ + struct device_node *np = dev->of_node; + struct generic_pm_domain *genpd; + struct cpg_mssr_clk_domain *pd; + size_t pm_size = num_core_pm_clks * sizeof(core_pm_clks[0]); + + pd = devm_kzalloc(dev, sizeof(*pd) + pm_size, GFP_KERNEL); + if (!pd) + return -ENOMEM; + + pd->np = np; + pd->num_core_pm_clks = num_core_pm_clks; + memcpy(pd->core_pm_clks, core_pm_clks, pm_size); + + genpd = &pd->genpd; + genpd->name = np->name; + genpd->flags = GENPD_FLAG_PM_CLK; + pm_genpd_init(genpd, &simple_qos_governor, false); + genpd->attach_dev = cpg_mssr_attach_dev; + genpd->detach_dev = cpg_mssr_detach_dev; + + of_genpd_add_provider_simple(np, genpd); + return 0; +} +#else +static inline int cpg_mssr_add_clk_domain(struct device *dev, + const unsigned int *core_pm_clks, + unsigned int num_core_pm_clks) +{ + return 0; +} +#endif /* !CONFIG_PM_GENERIC_DOMAINS_OF */ + + +static const struct of_device_id cpg_mssr_match[] = { +#ifdef CONFIG_ARCH_R8A7795 + { + .compatible = "renesas,r8a7795-cpg-mssr", + .data = &r8a7795_cpg_mssr_info, + }, +#endif + { /* sentinel */ } +}; + +static void cpg_mssr_del_clk_provider(void *data) +{ + of_clk_del_provider(data); +} + +static int __init cpg_mssr_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + const struct cpg_mssr_info *info; + struct cpg_mssr_priv *priv; + unsigned int nclks, i; + struct resource *res; + struct clk **clks; + int error; + + info = of_match_node(cpg_mssr_match, np)->data; + if (info->init) { + error = info->init(dev); + if (error) + return error; + } + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->dev = dev; + spin_lock_init(&priv->mstp_lock); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->base = devm_ioremap_resource(dev, res); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + nclks = info->num_total_core_clks + info->num_hw_mod_clks; + clks = devm_kmalloc_array(dev, nclks, sizeof(*clks), GFP_KERNEL); + if (!clks) + return -ENOMEM; + + priv->clks = clks; + priv->num_core_clks = info->num_total_core_clks; + priv->num_mod_clks = info->num_hw_mod_clks; + priv->last_dt_core_clk = info->last_dt_core_clk; + + for (i = 0; i < nclks; i++) + clks[i] = ERR_PTR(-ENOENT); + + for (i = 0; i < info->num_core_clks; i++) + cpg_mssr_register_core_clk(&info->core_clks[i], info, priv); + + for (i = 0; i < info->num_mod_clks; i++) + cpg_mssr_register_mod_clk(&info->mod_clks[i], info, priv); + + error = of_clk_add_provider(np, cpg_mssr_clk_src_twocell_get, priv); + if (error) + return error; + + error = devm_add_action_or_reset(dev, + cpg_mssr_del_clk_provider, + np); + if (error) + return error; + + error = cpg_mssr_add_clk_domain(dev, info->core_pm_clks, + info->num_core_pm_clks); + if (error) + return error; + + return 0; +} + +static struct platform_driver cpg_mssr_driver = { + .driver = { + .name = "renesas-cpg-mssr", + .of_match_table = cpg_mssr_match, + }, +}; + +static int __init cpg_mssr_init(void) +{ + return platform_driver_probe(&cpg_mssr_driver, cpg_mssr_probe); +} + +subsys_initcall(cpg_mssr_init); + +MODULE_DESCRIPTION("Renesas CPG/MSSR Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/clk/renesas/renesas-cpg-mssr.h b/drivers/clk/renesas/renesas-cpg-mssr.h new file mode 100644 index 000000000000..952b6957233b --- /dev/null +++ b/drivers/clk/renesas/renesas-cpg-mssr.h @@ -0,0 +1,134 @@ +/* + * Renesas Clock Pulse Generator / Module Standby and Software Reset + * + * Copyright (C) 2015 Glider bvba + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + */ + +#ifndef __CLK_RENESAS_CPG_MSSR_H__ +#define __CLK_RENESAS_CPG_MSSR_H__ + + /* + * Definitions of CPG Core Clocks + * + * These include: + * - Clock outputs exported to DT + * - External input clocks + * - Internal CPG clocks + */ + +struct cpg_core_clk { + /* Common */ + const char *name; + unsigned int id; + unsigned int type; + /* Depending on type */ + unsigned int parent; /* Core Clocks only */ + unsigned int div; + unsigned int mult; + unsigned int offset; +}; + +enum clk_types { + /* Generic */ + CLK_TYPE_IN, /* External Clock Input */ + CLK_TYPE_FF, /* Fixed Factor Clock */ + CLK_TYPE_DIV6P1, /* DIV6 Clock with 1 parent clock */ + + /* Custom definitions start here */ + CLK_TYPE_CUSTOM, +}; + +#define DEF_TYPE(_name, _id, _type...) \ + { .name = _name, .id = _id, .type = _type } +#define DEF_BASE(_name, _id, _type, _parent...) \ + DEF_TYPE(_name, _id, _type, .parent = _parent) + +#define DEF_INPUT(_name, _id) \ + DEF_TYPE(_name, _id, CLK_TYPE_IN) +#define DEF_FIXED(_name, _id, _parent, _div, _mult) \ + DEF_BASE(_name, _id, CLK_TYPE_FF, _parent, .div = _div, .mult = _mult) +#define DEF_DIV6P1(_name, _id, _parent, _offset) \ + DEF_BASE(_name, _id, CLK_TYPE_DIV6P1, _parent, .offset = _offset) +#define DEF_SD(_name, _id, _parent, _offset) \ + DEF_BASE(_name, _id, CLK_TYPE_GEN3_SD, _parent, .offset = _offset) + + + /* + * Definitions of Module Clocks + */ + +struct mssr_mod_clk { + const char *name; + unsigned int id; + unsigned int parent; /* Add MOD_CLK_BASE for Module Clocks */ +}; + +/* Convert from sparse base-100 to packed index space */ +#define MOD_CLK_PACK(x) ((x) - ((x) / 100) * (100 - 32)) + +#define MOD_CLK_ID(x) (MOD_CLK_BASE + MOD_CLK_PACK(x)) + +#define DEF_MOD(_name, _mod, _parent...) \ + { .name = _name, .id = MOD_CLK_ID(_mod), .parent = _parent } + + +struct device_node; + + /** + * SoC-specific CPG/MSSR Description + * + * @core_clks: Array of Core Clock definitions + * @num_core_clks: Number of entries in core_clks[] + * @last_dt_core_clk: ID of the last Core Clock exported to DT + * @num_total_core_clks: Total number of Core Clocks (exported + internal) + * + * @mod_clks: Array of Module Clock definitions + * @num_mod_clks: Number of entries in mod_clks[] + * @num_hw_mod_clks: Number of Module Clocks supported by the hardware + * + * @crit_mod_clks: Array with Module Clock IDs of critical clocks that + * should not be disabled without a knowledgeable driver + * @num_crit_mod_clks: Number of entries in crit_mod_clks[] + * + * @core_pm_clks: Array with IDs of Core Clocks that are suitable for Power + * Management, in addition to Module Clocks + * @num_core_pm_clks: Number of entries in core_pm_clks[] + * + * @init: Optional callback to perform SoC-specific initialization + * @cpg_clk_register: Optional callback to handle special Core Clock types + */ + +struct cpg_mssr_info { + /* Core Clocks */ + const struct cpg_core_clk *core_clks; + unsigned int num_core_clks; + unsigned int last_dt_core_clk; + unsigned int num_total_core_clks; + + /* Module Clocks */ + const struct mssr_mod_clk *mod_clks; + unsigned int num_mod_clks; + unsigned int num_hw_mod_clks; + + /* Critical Module Clocks that should not be disabled */ + const unsigned int *crit_mod_clks; + unsigned int num_crit_mod_clks; + + /* Core Clocks suitable for PM, in addition to the Module Clocks */ + const unsigned int *core_pm_clks; + unsigned int num_core_pm_clks; + + /* Callbacks */ + int (*init)(struct device *dev); + struct clk *(*cpg_clk_register)(struct device *dev, + const struct cpg_core_clk *core, + const struct cpg_mssr_info *info, + struct clk **clks, void __iomem *base); +}; + +extern const struct cpg_mssr_info r8a7795_cpg_mssr_info; +#endif |