forked from mirrors/linux
		
	clk: meson: Add support for Meson clock controller
This patchset adds the infrastructure for registering and managing the core clocks found on Amlogic MesonX SoCs. In particular: - PLLs - CPU clock - Fixed rate clocks, fixed factor clocks, ... Signed-off-by: Carlo Caione <carlo@endlessm.com> Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
This commit is contained in:
		
							parent
							
								
									b787f68c36
								
							
						
					
					
						commit
						7a29a86943
					
				
					 7 changed files with 929 additions and 0 deletions
				
			
		| 
						 | 
					@ -55,6 +55,7 @@ ifeq ($(CONFIG_COMMON_CLK), y)
 | 
				
			||||||
obj-$(CONFIG_ARCH_MMP)			+= mmp/
 | 
					obj-$(CONFIG_ARCH_MMP)			+= mmp/
 | 
				
			||||||
endif
 | 
					endif
 | 
				
			||||||
obj-$(CONFIG_PLAT_ORION)		+= mvebu/
 | 
					obj-$(CONFIG_PLAT_ORION)		+= mvebu/
 | 
				
			||||||
 | 
					obj-$(CONFIG_ARCH_MESON)		+= meson/
 | 
				
			||||||
obj-$(CONFIG_ARCH_MXS)			+= mxs/
 | 
					obj-$(CONFIG_ARCH_MXS)			+= mxs/
 | 
				
			||||||
obj-$(CONFIG_MACH_PISTACHIO)		+= pistachio/
 | 
					obj-$(CONFIG_MACH_PISTACHIO)		+= pistachio/
 | 
				
			||||||
obj-$(CONFIG_COMMON_CLK_PXA)		+= pxa/
 | 
					obj-$(CONFIG_COMMON_CLK_PXA)		+= pxa/
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										5
									
								
								drivers/clk/meson/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								drivers/clk/meson/Makefile
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,5 @@
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# Makefile for Meson specific clk
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					obj-y += clkc.o clk-pll.o clk-cpu.o
 | 
				
			||||||
							
								
								
									
										234
									
								
								drivers/clk/meson/clk-cpu.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										234
									
								
								drivers/clk/meson/clk-cpu.c
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,234 @@
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (c) 2015 Endless Mobile, Inc.
 | 
				
			||||||
 | 
					 * Author: Carlo Caione <carlo@endlessm.com>
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is free software; you can redistribute it and/or modify it
 | 
				
			||||||
 | 
					 * under the terms and conditions of the GNU General Public License,
 | 
				
			||||||
 | 
					 * version 2, as published by the Free Software Foundation.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * CPU clock path:
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *                           +-[/N]-----|3|
 | 
				
			||||||
 | 
					 *             MUX2  +--[/3]-+----------|2| MUX1
 | 
				
			||||||
 | 
					 * [sys_pll]---|1|   |--[/2]------------|1|-|1|
 | 
				
			||||||
 | 
					 *             | |---+------------------|0| | |----- [a5_clk]
 | 
				
			||||||
 | 
					 *          +--|0|                          | |
 | 
				
			||||||
 | 
					 * [xtal]---+-------------------------------|0|
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <linux/delay.h>
 | 
				
			||||||
 | 
					#include <linux/err.h>
 | 
				
			||||||
 | 
					#include <linux/io.h>
 | 
				
			||||||
 | 
					#include <linux/module.h>
 | 
				
			||||||
 | 
					#include <linux/of_address.h>
 | 
				
			||||||
 | 
					#include <linux/slab.h>
 | 
				
			||||||
 | 
					#include <linux/clk-provider.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define MESON_CPU_CLK_CNTL1		0x00
 | 
				
			||||||
 | 
					#define MESON_CPU_CLK_CNTL		0x40
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define MESON_CPU_CLK_MUX1		BIT(7)
 | 
				
			||||||
 | 
					#define MESON_CPU_CLK_MUX2		BIT(0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define MESON_N_WIDTH			9
 | 
				
			||||||
 | 
					#define MESON_N_SHIFT			20
 | 
				
			||||||
 | 
					#define MESON_SEL_WIDTH			2
 | 
				
			||||||
 | 
					#define MESON_SEL_SHIFT			2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "clkc.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct meson_clk_cpu {
 | 
				
			||||||
 | 
						struct notifier_block		clk_nb;
 | 
				
			||||||
 | 
						const struct clk_div_table	*div_table;
 | 
				
			||||||
 | 
						struct clk_hw			hw;
 | 
				
			||||||
 | 
						void __iomem			*base;
 | 
				
			||||||
 | 
						u16				reg_off;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					#define to_meson_clk_cpu_hw(_hw) container_of(_hw, struct meson_clk_cpu, hw)
 | 
				
			||||||
 | 
					#define to_meson_clk_cpu_nb(_nb) container_of(_nb, struct meson_clk_cpu, clk_nb)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static long meson_clk_cpu_round_rate(struct clk_hw *hw, unsigned long rate,
 | 
				
			||||||
 | 
									     unsigned long *prate)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct meson_clk_cpu *clk_cpu = to_meson_clk_cpu_hw(hw);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return divider_round_rate(hw, rate, prate, clk_cpu->div_table,
 | 
				
			||||||
 | 
									  MESON_N_WIDTH, CLK_DIVIDER_ROUND_CLOSEST);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int meson_clk_cpu_set_rate(struct clk_hw *hw, unsigned long rate,
 | 
				
			||||||
 | 
									  unsigned long parent_rate)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct meson_clk_cpu *clk_cpu = to_meson_clk_cpu_hw(hw);
 | 
				
			||||||
 | 
						unsigned int div, sel, N = 0;
 | 
				
			||||||
 | 
						u32 reg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						div = DIV_ROUND_UP(parent_rate, rate);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (div <= 3) {
 | 
				
			||||||
 | 
							sel = div - 1;
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							sel = 3;
 | 
				
			||||||
 | 
							N = div / 2;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						reg = readl(clk_cpu->base + clk_cpu->reg_off + MESON_CPU_CLK_CNTL1);
 | 
				
			||||||
 | 
						reg = PARM_SET(MESON_N_WIDTH, MESON_N_SHIFT, reg, N);
 | 
				
			||||||
 | 
						writel(reg, clk_cpu->base + clk_cpu->reg_off + MESON_CPU_CLK_CNTL1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						reg = readl(clk_cpu->base + clk_cpu->reg_off + MESON_CPU_CLK_CNTL);
 | 
				
			||||||
 | 
						reg = PARM_SET(MESON_SEL_WIDTH, MESON_SEL_SHIFT, reg, sel);
 | 
				
			||||||
 | 
						writel(reg, clk_cpu->base + clk_cpu->reg_off + MESON_CPU_CLK_CNTL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static unsigned long meson_clk_cpu_recalc_rate(struct clk_hw *hw,
 | 
				
			||||||
 | 
										       unsigned long parent_rate)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct meson_clk_cpu *clk_cpu = to_meson_clk_cpu_hw(hw);
 | 
				
			||||||
 | 
						unsigned int N, sel;
 | 
				
			||||||
 | 
						unsigned int div = 1;
 | 
				
			||||||
 | 
						u32 reg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						reg = readl(clk_cpu->base + clk_cpu->reg_off + MESON_CPU_CLK_CNTL1);
 | 
				
			||||||
 | 
						N = PARM_GET(MESON_N_WIDTH, MESON_N_SHIFT, reg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						reg = readl(clk_cpu->base + clk_cpu->reg_off + MESON_CPU_CLK_CNTL);
 | 
				
			||||||
 | 
						sel = PARM_GET(MESON_SEL_WIDTH, MESON_SEL_SHIFT, reg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (sel < 3)
 | 
				
			||||||
 | 
							div = sel + 1;
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							div = 2 * N;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return parent_rate / div;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int meson_clk_cpu_pre_rate_change(struct meson_clk_cpu *clk_cpu,
 | 
				
			||||||
 | 
										 struct clk_notifier_data *ndata)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						u32 cpu_clk_cntl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* switch MUX1 to xtal */
 | 
				
			||||||
 | 
						cpu_clk_cntl = readl(clk_cpu->base + clk_cpu->reg_off
 | 
				
			||||||
 | 
									+ MESON_CPU_CLK_CNTL);
 | 
				
			||||||
 | 
						cpu_clk_cntl &= ~MESON_CPU_CLK_MUX1;
 | 
				
			||||||
 | 
						writel(cpu_clk_cntl, clk_cpu->base + clk_cpu->reg_off
 | 
				
			||||||
 | 
									+ MESON_CPU_CLK_CNTL);
 | 
				
			||||||
 | 
						udelay(100);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* switch MUX2 to sys-pll */
 | 
				
			||||||
 | 
						cpu_clk_cntl |= MESON_CPU_CLK_MUX2;
 | 
				
			||||||
 | 
						writel(cpu_clk_cntl, clk_cpu->base + clk_cpu->reg_off
 | 
				
			||||||
 | 
									+ MESON_CPU_CLK_CNTL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int meson_clk_cpu_post_rate_change(struct meson_clk_cpu *clk_cpu,
 | 
				
			||||||
 | 
										  struct clk_notifier_data *ndata)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						u32 cpu_clk_cntl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* switch MUX1 to divisors' output */
 | 
				
			||||||
 | 
						cpu_clk_cntl = readl(clk_cpu->base + clk_cpu->reg_off
 | 
				
			||||||
 | 
									+ MESON_CPU_CLK_CNTL);
 | 
				
			||||||
 | 
						cpu_clk_cntl |= MESON_CPU_CLK_MUX1;
 | 
				
			||||||
 | 
						writel(cpu_clk_cntl, clk_cpu->base + clk_cpu->reg_off
 | 
				
			||||||
 | 
									+ MESON_CPU_CLK_CNTL);
 | 
				
			||||||
 | 
						udelay(100);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * This clock notifier is called when the frequency of the of the parent
 | 
				
			||||||
 | 
					 * PLL clock is to be changed. We use the xtal input as temporary parent
 | 
				
			||||||
 | 
					 * while the PLL frequency is stabilized.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static int meson_clk_cpu_notifier_cb(struct notifier_block *nb,
 | 
				
			||||||
 | 
									     unsigned long event, void *data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct clk_notifier_data *ndata = data;
 | 
				
			||||||
 | 
						struct meson_clk_cpu *clk_cpu = to_meson_clk_cpu_nb(nb);
 | 
				
			||||||
 | 
						int ret = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (event == PRE_RATE_CHANGE)
 | 
				
			||||||
 | 
							ret = meson_clk_cpu_pre_rate_change(clk_cpu, ndata);
 | 
				
			||||||
 | 
						else if (event == POST_RATE_CHANGE)
 | 
				
			||||||
 | 
							ret = meson_clk_cpu_post_rate_change(clk_cpu, ndata);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return notifier_from_errno(ret);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const struct clk_ops meson_clk_cpu_ops = {
 | 
				
			||||||
 | 
						.recalc_rate	= meson_clk_cpu_recalc_rate,
 | 
				
			||||||
 | 
						.round_rate	= meson_clk_cpu_round_rate,
 | 
				
			||||||
 | 
						.set_rate	= meson_clk_cpu_set_rate,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct clk *meson_clk_register_cpu(const struct clk_conf *clk_conf,
 | 
				
			||||||
 | 
									   void __iomem *reg_base,
 | 
				
			||||||
 | 
									   spinlock_t *lock)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct clk *clk;
 | 
				
			||||||
 | 
						struct clk *pclk;
 | 
				
			||||||
 | 
						struct meson_clk_cpu *clk_cpu;
 | 
				
			||||||
 | 
						struct clk_init_data init;
 | 
				
			||||||
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						clk_cpu = kzalloc(sizeof(*clk_cpu), GFP_KERNEL);
 | 
				
			||||||
 | 
						if (!clk_cpu)
 | 
				
			||||||
 | 
							return ERR_PTR(-ENOMEM);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						clk_cpu->base = reg_base;
 | 
				
			||||||
 | 
						clk_cpu->reg_off = clk_conf->reg_off;
 | 
				
			||||||
 | 
						clk_cpu->div_table = clk_conf->conf.div_table;
 | 
				
			||||||
 | 
						clk_cpu->clk_nb.notifier_call = meson_clk_cpu_notifier_cb;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						init.name = clk_conf->clk_name;
 | 
				
			||||||
 | 
						init.ops = &meson_clk_cpu_ops;
 | 
				
			||||||
 | 
						init.flags = clk_conf->flags | CLK_GET_RATE_NOCACHE;
 | 
				
			||||||
 | 
						init.flags |= CLK_SET_RATE_PARENT;
 | 
				
			||||||
 | 
						init.parent_names = clk_conf->clks_parent;
 | 
				
			||||||
 | 
						init.num_parents = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						clk_cpu->hw.init = &init;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pclk = __clk_lookup(clk_conf->clks_parent[0]);
 | 
				
			||||||
 | 
						if (!pclk) {
 | 
				
			||||||
 | 
							pr_err("%s: could not lookup parent clock %s\n",
 | 
				
			||||||
 | 
									__func__, clk_conf->clks_parent[0]);
 | 
				
			||||||
 | 
							return ERR_PTR(-EINVAL);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ret = clk_notifier_register(pclk, &clk_cpu->clk_nb);
 | 
				
			||||||
 | 
						if (ret) {
 | 
				
			||||||
 | 
							pr_err("%s: failed to register clock notifier for %s\n",
 | 
				
			||||||
 | 
									__func__, clk_conf->clk_name);
 | 
				
			||||||
 | 
							return ERR_PTR(-EINVAL);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						clk = clk_register(NULL, &clk_cpu->hw);
 | 
				
			||||||
 | 
						if (IS_ERR(clk)) {
 | 
				
			||||||
 | 
							clk_notifier_unregister(pclk, &clk_cpu->clk_nb);
 | 
				
			||||||
 | 
							kfree(clk_cpu);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return clk;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										227
									
								
								drivers/clk/meson/clk-pll.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										227
									
								
								drivers/clk/meson/clk-pll.c
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,227 @@
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (c) 2015 Endless Mobile, Inc.
 | 
				
			||||||
 | 
					 * Author: Carlo Caione <carlo@endlessm.com>
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is free software; you can redistribute it and/or modify it
 | 
				
			||||||
 | 
					 * under the terms and conditions of the GNU General Public License,
 | 
				
			||||||
 | 
					 * version 2, as published by the Free Software Foundation.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * In the most basic form, a Meson PLL is composed as follows:
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *                     PLL
 | 
				
			||||||
 | 
					 *      +------------------------------+
 | 
				
			||||||
 | 
					 *      |                              |
 | 
				
			||||||
 | 
					 * in -----[ /N ]---[ *M ]---[ >>OD ]----->> out
 | 
				
			||||||
 | 
					 *      |         ^        ^           |
 | 
				
			||||||
 | 
					 *      +------------------------------+
 | 
				
			||||||
 | 
					 *                |        |
 | 
				
			||||||
 | 
					 *               FREF     VCO
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * out = (in * M / N) >> OD
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <linux/clk-provider.h>
 | 
				
			||||||
 | 
					#include <linux/delay.h>
 | 
				
			||||||
 | 
					#include <linux/err.h>
 | 
				
			||||||
 | 
					#include <linux/io.h>
 | 
				
			||||||
 | 
					#include <linux/module.h>
 | 
				
			||||||
 | 
					#include <linux/of_address.h>
 | 
				
			||||||
 | 
					#include <linux/slab.h>
 | 
				
			||||||
 | 
					#include <linux/string.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "clkc.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define MESON_PLL_RESET				BIT(29)
 | 
				
			||||||
 | 
					#define MESON_PLL_LOCK				BIT(31)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct meson_clk_pll {
 | 
				
			||||||
 | 
						struct clk_hw	hw;
 | 
				
			||||||
 | 
						void __iomem	*base;
 | 
				
			||||||
 | 
						struct pll_conf	*conf;
 | 
				
			||||||
 | 
						unsigned int	rate_count;
 | 
				
			||||||
 | 
						spinlock_t	*lock;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					#define to_meson_clk_pll(_hw) container_of(_hw, struct meson_clk_pll, hw)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static unsigned long meson_clk_pll_recalc_rate(struct clk_hw *hw,
 | 
				
			||||||
 | 
											unsigned long parent_rate)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct meson_clk_pll *pll = to_meson_clk_pll(hw);
 | 
				
			||||||
 | 
						struct parm *p;
 | 
				
			||||||
 | 
						unsigned long parent_rate_mhz = parent_rate / 1000000;
 | 
				
			||||||
 | 
						unsigned long rate_mhz;
 | 
				
			||||||
 | 
						u16 n, m, od;
 | 
				
			||||||
 | 
						u32 reg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						p = &pll->conf->n;
 | 
				
			||||||
 | 
						reg = readl(pll->base + p->reg_off);
 | 
				
			||||||
 | 
						n = PARM_GET(p->width, p->shift, reg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						p = &pll->conf->m;
 | 
				
			||||||
 | 
						reg = readl(pll->base + p->reg_off);
 | 
				
			||||||
 | 
						m = PARM_GET(p->width, p->shift, reg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						p = &pll->conf->od;
 | 
				
			||||||
 | 
						reg = readl(pll->base + p->reg_off);
 | 
				
			||||||
 | 
						od = PARM_GET(p->width, p->shift, reg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rate_mhz = (parent_rate_mhz * m / n) >> od;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return rate_mhz * 1000000;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static long meson_clk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
 | 
				
			||||||
 | 
									     unsigned long *parent_rate)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct meson_clk_pll *pll = to_meson_clk_pll(hw);
 | 
				
			||||||
 | 
						const struct pll_rate_table *rate_table = pll->conf->rate_table;
 | 
				
			||||||
 | 
						int i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (i = 0; i < pll->rate_count; i++) {
 | 
				
			||||||
 | 
							if (rate <= rate_table[i].rate)
 | 
				
			||||||
 | 
								return rate_table[i].rate;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* else return the smallest value */
 | 
				
			||||||
 | 
						return rate_table[0].rate;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const struct pll_rate_table *meson_clk_get_pll_settings(struct meson_clk_pll *pll,
 | 
				
			||||||
 | 
												       unsigned long rate)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						const struct pll_rate_table *rate_table = pll->conf->rate_table;
 | 
				
			||||||
 | 
						int i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (i = 0; i < pll->rate_count; i++) {
 | 
				
			||||||
 | 
							if (rate == rate_table[i].rate)
 | 
				
			||||||
 | 
								return &rate_table[i];
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return NULL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int meson_clk_pll_wait_lock(struct meson_clk_pll *pll,
 | 
				
			||||||
 | 
									   struct parm *p_n)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int delay = 24000000;
 | 
				
			||||||
 | 
						u32 reg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						while (delay > 0) {
 | 
				
			||||||
 | 
							reg = readl(pll->base + p_n->reg_off);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (reg & MESON_PLL_LOCK)
 | 
				
			||||||
 | 
								return 0;
 | 
				
			||||||
 | 
							delay--;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return -ETIMEDOUT;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int meson_clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
 | 
				
			||||||
 | 
									  unsigned long parent_rate)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct meson_clk_pll *pll = to_meson_clk_pll(hw);
 | 
				
			||||||
 | 
						struct parm *p;
 | 
				
			||||||
 | 
						const struct pll_rate_table *rate_set;
 | 
				
			||||||
 | 
						unsigned long old_rate;
 | 
				
			||||||
 | 
						int ret = 0;
 | 
				
			||||||
 | 
						u32 reg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (parent_rate == 0 || rate == 0)
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						old_rate = rate;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rate_set = meson_clk_get_pll_settings(pll, rate);
 | 
				
			||||||
 | 
						if (!rate_set)
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* PLL reset */
 | 
				
			||||||
 | 
						p = &pll->conf->n;
 | 
				
			||||||
 | 
						reg = readl(pll->base + p->reg_off);
 | 
				
			||||||
 | 
						writel(reg | MESON_PLL_RESET, pll->base + p->reg_off);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						reg = PARM_SET(p->width, p->shift, reg, rate_set->n);
 | 
				
			||||||
 | 
						writel(reg, pll->base + p->reg_off);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						p = &pll->conf->m;
 | 
				
			||||||
 | 
						reg = readl(pll->base + p->reg_off);
 | 
				
			||||||
 | 
						reg = PARM_SET(p->width, p->shift, reg, rate_set->m);
 | 
				
			||||||
 | 
						writel(reg, pll->base + p->reg_off);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						p = &pll->conf->od;
 | 
				
			||||||
 | 
						reg = readl(pll->base + p->reg_off);
 | 
				
			||||||
 | 
						reg = PARM_SET(p->width, p->shift, reg, rate_set->od);
 | 
				
			||||||
 | 
						writel(reg, pll->base + p->reg_off);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						p = &pll->conf->n;
 | 
				
			||||||
 | 
						ret = meson_clk_pll_wait_lock(pll, p);
 | 
				
			||||||
 | 
						if (ret) {
 | 
				
			||||||
 | 
							pr_warn("%s: pll did not lock, trying to restore old rate %lu\n",
 | 
				
			||||||
 | 
								__func__, old_rate);
 | 
				
			||||||
 | 
							meson_clk_pll_set_rate(hw, old_rate, parent_rate);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return ret;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const struct clk_ops meson_clk_pll_ops = {
 | 
				
			||||||
 | 
						.recalc_rate	= meson_clk_pll_recalc_rate,
 | 
				
			||||||
 | 
						.round_rate	= meson_clk_pll_round_rate,
 | 
				
			||||||
 | 
						.set_rate	= meson_clk_pll_set_rate,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const struct clk_ops meson_clk_pll_ro_ops = {
 | 
				
			||||||
 | 
						.recalc_rate	= meson_clk_pll_recalc_rate,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct clk *meson_clk_register_pll(const struct clk_conf *clk_conf,
 | 
				
			||||||
 | 
									   void __iomem *reg_base,
 | 
				
			||||||
 | 
									   spinlock_t *lock)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct clk *clk;
 | 
				
			||||||
 | 
						struct meson_clk_pll *clk_pll;
 | 
				
			||||||
 | 
						struct clk_init_data init;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						clk_pll = kzalloc(sizeof(*clk_pll), GFP_KERNEL);
 | 
				
			||||||
 | 
						if (!clk_pll)
 | 
				
			||||||
 | 
							return ERR_PTR(-ENOMEM);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						clk_pll->base = reg_base + clk_conf->reg_off;
 | 
				
			||||||
 | 
						clk_pll->lock = lock;
 | 
				
			||||||
 | 
						clk_pll->conf = clk_conf->conf.pll;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						init.name = clk_conf->clk_name;
 | 
				
			||||||
 | 
						init.flags = clk_conf->flags | CLK_GET_RATE_NOCACHE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						init.parent_names = &clk_conf->clks_parent[0];
 | 
				
			||||||
 | 
						init.num_parents = 1;
 | 
				
			||||||
 | 
						init.ops = &meson_clk_pll_ro_ops;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* If no rate_table is specified we assume the PLL is read-only */
 | 
				
			||||||
 | 
						if (clk_pll->conf->rate_table) {
 | 
				
			||||||
 | 
							int len;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for (len = 0; clk_pll->conf->rate_table[len].rate != 0; )
 | 
				
			||||||
 | 
								len++;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							 clk_pll->rate_count = len;
 | 
				
			||||||
 | 
							 init.ops = &meson_clk_pll_ops;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						clk_pll->hw.init = &init;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						clk = clk_register(NULL, &clk_pll->hw);
 | 
				
			||||||
 | 
						if (IS_ERR(clk))
 | 
				
			||||||
 | 
							kfree(clk_pll);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return clk;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										250
									
								
								drivers/clk/meson/clkc.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										250
									
								
								drivers/clk/meson/clkc.c
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,250 @@
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (c) 2015 Endless Mobile, Inc.
 | 
				
			||||||
 | 
					 * Author: Carlo Caione <carlo@endlessm.com>
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is free software; you can redistribute it and/or modify it
 | 
				
			||||||
 | 
					 * under the terms and conditions of the GNU General Public License,
 | 
				
			||||||
 | 
					 * version 2, as published by the Free Software Foundation.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <linux/clk.h>
 | 
				
			||||||
 | 
					#include <linux/clk-provider.h>
 | 
				
			||||||
 | 
					#include <linux/mfd/syscon.h>
 | 
				
			||||||
 | 
					#include <linux/slab.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "clkc.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static DEFINE_SPINLOCK(clk_lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static struct clk **clks;
 | 
				
			||||||
 | 
					static struct clk_onecell_data clk_data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct clk ** __init meson_clk_init(struct device_node *np,
 | 
				
			||||||
 | 
									   unsigned long nr_clks)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						clks = kcalloc(nr_clks, sizeof(*clks), GFP_KERNEL);
 | 
				
			||||||
 | 
						if (!clks)
 | 
				
			||||||
 | 
							return ERR_PTR(-ENOMEM);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						clk_data.clks = clks;
 | 
				
			||||||
 | 
						clk_data.clk_num = nr_clks;
 | 
				
			||||||
 | 
						of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return clks;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void meson_clk_add_lookup(struct clk *clk, unsigned int id)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (clks && id)
 | 
				
			||||||
 | 
							clks[id] = clk;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static struct clk * __init
 | 
				
			||||||
 | 
					meson_clk_register_composite(const struct clk_conf *clk_conf,
 | 
				
			||||||
 | 
								     void __iomem *clk_base)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct clk *clk;
 | 
				
			||||||
 | 
						struct clk_mux *mux = NULL;
 | 
				
			||||||
 | 
						struct clk_divider *div = NULL;
 | 
				
			||||||
 | 
						struct clk_gate *gate = NULL;
 | 
				
			||||||
 | 
						const struct clk_ops *mux_ops = NULL;
 | 
				
			||||||
 | 
						const struct composite_conf *composite_conf;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						composite_conf = clk_conf->conf.composite;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (clk_conf->num_parents > 1) {
 | 
				
			||||||
 | 
							mux = kzalloc(sizeof(*mux), GFP_KERNEL);
 | 
				
			||||||
 | 
							if (!mux)
 | 
				
			||||||
 | 
								return ERR_PTR(-ENOMEM);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							mux->reg = clk_base + clk_conf->reg_off
 | 
				
			||||||
 | 
									+ composite_conf->mux_parm.reg_off;
 | 
				
			||||||
 | 
							mux->shift = composite_conf->mux_parm.shift;
 | 
				
			||||||
 | 
							mux->mask = BIT(composite_conf->mux_parm.width) - 1;
 | 
				
			||||||
 | 
							mux->flags = composite_conf->mux_flags;
 | 
				
			||||||
 | 
							mux->lock = &clk_lock;
 | 
				
			||||||
 | 
							mux->table = composite_conf->mux_table;
 | 
				
			||||||
 | 
							mux_ops = (composite_conf->mux_flags & CLK_MUX_READ_ONLY) ?
 | 
				
			||||||
 | 
								  &clk_mux_ro_ops : &clk_mux_ops;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (MESON_PARM_APPLICABLE(&composite_conf->div_parm)) {
 | 
				
			||||||
 | 
							div = kzalloc(sizeof(*div), GFP_KERNEL);
 | 
				
			||||||
 | 
							if (!div) {
 | 
				
			||||||
 | 
								clk = ERR_PTR(-ENOMEM);
 | 
				
			||||||
 | 
								goto error;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							div->reg = clk_base + clk_conf->reg_off
 | 
				
			||||||
 | 
									+ composite_conf->div_parm.reg_off;
 | 
				
			||||||
 | 
							div->shift = composite_conf->div_parm.shift;
 | 
				
			||||||
 | 
							div->width = composite_conf->div_parm.width;
 | 
				
			||||||
 | 
							div->lock = &clk_lock;
 | 
				
			||||||
 | 
							div->flags = composite_conf->div_flags;
 | 
				
			||||||
 | 
							div->table = composite_conf->div_table;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (MESON_PARM_APPLICABLE(&composite_conf->gate_parm)) {
 | 
				
			||||||
 | 
							gate = kzalloc(sizeof(*gate), GFP_KERNEL);
 | 
				
			||||||
 | 
							if (!gate) {
 | 
				
			||||||
 | 
								clk = ERR_PTR(-ENOMEM);
 | 
				
			||||||
 | 
								goto error;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							gate->reg = clk_base + clk_conf->reg_off
 | 
				
			||||||
 | 
									+ composite_conf->div_parm.reg_off;
 | 
				
			||||||
 | 
							gate->bit_idx = composite_conf->gate_parm.shift;
 | 
				
			||||||
 | 
							gate->flags = composite_conf->gate_flags;
 | 
				
			||||||
 | 
							gate->lock = &clk_lock;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						clk = clk_register_composite(NULL, clk_conf->clk_name,
 | 
				
			||||||
 | 
									    clk_conf->clks_parent,
 | 
				
			||||||
 | 
									    clk_conf->num_parents,
 | 
				
			||||||
 | 
									    mux ? &mux->hw : NULL, mux_ops,
 | 
				
			||||||
 | 
									    div ? &div->hw : NULL, &clk_divider_ops,
 | 
				
			||||||
 | 
									    gate ? &gate->hw : NULL, &clk_gate_ops,
 | 
				
			||||||
 | 
									    clk_conf->flags);
 | 
				
			||||||
 | 
						if (IS_ERR(clk))
 | 
				
			||||||
 | 
							goto error;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return clk;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					error:
 | 
				
			||||||
 | 
						kfree(gate);
 | 
				
			||||||
 | 
						kfree(div);
 | 
				
			||||||
 | 
						kfree(mux);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return clk;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static struct clk * __init
 | 
				
			||||||
 | 
					meson_clk_register_fixed_factor(const struct clk_conf *clk_conf,
 | 
				
			||||||
 | 
									void __iomem *clk_base)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct clk *clk;
 | 
				
			||||||
 | 
						const struct fixed_fact_conf *fixed_fact_conf;
 | 
				
			||||||
 | 
						const struct parm *p;
 | 
				
			||||||
 | 
						unsigned int mult, div;
 | 
				
			||||||
 | 
						u32 reg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fixed_fact_conf = &clk_conf->conf.fixed_fact;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mult = clk_conf->conf.fixed_fact.mult;
 | 
				
			||||||
 | 
						div = clk_conf->conf.fixed_fact.div;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!mult) {
 | 
				
			||||||
 | 
							mult = 1;
 | 
				
			||||||
 | 
							p = &fixed_fact_conf->mult_parm;
 | 
				
			||||||
 | 
							if (MESON_PARM_APPLICABLE(p)) {
 | 
				
			||||||
 | 
								reg = readl(clk_base + clk_conf->reg_off + p->reg_off);
 | 
				
			||||||
 | 
								mult = PARM_GET(p->width, p->shift, reg);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!div) {
 | 
				
			||||||
 | 
							div = 1;
 | 
				
			||||||
 | 
							p = &fixed_fact_conf->div_parm;
 | 
				
			||||||
 | 
							if (MESON_PARM_APPLICABLE(p)) {
 | 
				
			||||||
 | 
								reg = readl(clk_base + clk_conf->reg_off + p->reg_off);
 | 
				
			||||||
 | 
								mult = PARM_GET(p->width, p->shift, reg);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						clk = clk_register_fixed_factor(NULL,
 | 
				
			||||||
 | 
								clk_conf->clk_name,
 | 
				
			||||||
 | 
								clk_conf->clks_parent[0],
 | 
				
			||||||
 | 
								clk_conf->flags,
 | 
				
			||||||
 | 
								mult, div);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return clk;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static struct clk * __init
 | 
				
			||||||
 | 
					meson_clk_register_fixed_rate(const struct clk_conf *clk_conf,
 | 
				
			||||||
 | 
								      void __iomem *clk_base)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct clk *clk;
 | 
				
			||||||
 | 
						const struct fixed_rate_conf *fixed_rate_conf;
 | 
				
			||||||
 | 
						const struct parm *r;
 | 
				
			||||||
 | 
						unsigned long rate;
 | 
				
			||||||
 | 
						u32 reg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fixed_rate_conf = &clk_conf->conf.fixed_rate;
 | 
				
			||||||
 | 
						rate = fixed_rate_conf->rate;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!rate) {
 | 
				
			||||||
 | 
							r = &fixed_rate_conf->rate_parm;
 | 
				
			||||||
 | 
							reg = readl(clk_base + clk_conf->reg_off + r->reg_off);
 | 
				
			||||||
 | 
							rate = PARM_GET(r->width, r->shift, reg);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rate *= 1000000;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						clk = clk_register_fixed_rate(NULL,
 | 
				
			||||||
 | 
								clk_conf->clk_name,
 | 
				
			||||||
 | 
								clk_conf->num_parents
 | 
				
			||||||
 | 
									? clk_conf->clks_parent[0] : NULL,
 | 
				
			||||||
 | 
								clk_conf->flags, rate);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return clk;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void __init meson_clk_register_clks(const struct clk_conf *clk_confs,
 | 
				
			||||||
 | 
									    size_t nr_confs,
 | 
				
			||||||
 | 
									    void __iomem *clk_base)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						unsigned int i;
 | 
				
			||||||
 | 
						struct clk *clk = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (i = 0; i < nr_confs; i++) {
 | 
				
			||||||
 | 
							const struct clk_conf *clk_conf = &clk_confs[i];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							switch (clk_conf->clk_type) {
 | 
				
			||||||
 | 
							case CLK_FIXED_RATE:
 | 
				
			||||||
 | 
								clk = meson_clk_register_fixed_rate(clk_conf,
 | 
				
			||||||
 | 
												    clk_base);
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							case CLK_FIXED_FACTOR:
 | 
				
			||||||
 | 
								clk = meson_clk_register_fixed_factor(clk_conf,
 | 
				
			||||||
 | 
												      clk_base);
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							case CLK_COMPOSITE:
 | 
				
			||||||
 | 
								clk = meson_clk_register_composite(clk_conf,
 | 
				
			||||||
 | 
												   clk_base);
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							case CLK_CPU:
 | 
				
			||||||
 | 
								clk = meson_clk_register_cpu(clk_conf, clk_base,
 | 
				
			||||||
 | 
											     &clk_lock);
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							case CLK_PLL:
 | 
				
			||||||
 | 
								clk = meson_clk_register_pll(clk_conf, clk_base,
 | 
				
			||||||
 | 
											     &clk_lock);
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								clk = NULL;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (!clk) {
 | 
				
			||||||
 | 
								pr_err("%s: unknown clock type %d\n", __func__,
 | 
				
			||||||
 | 
								       clk_conf->clk_type);
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (IS_ERR(clk)) {
 | 
				
			||||||
 | 
								pr_warn("%s: Unable to create %s clock\n", __func__,
 | 
				
			||||||
 | 
									clk_conf->clk_name);
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							meson_clk_add_lookup(clk, clk_conf->clk_id);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										187
									
								
								drivers/clk/meson/clkc.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										187
									
								
								drivers/clk/meson/clkc.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,187 @@
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (c) 2015 Endless Mobile, Inc.
 | 
				
			||||||
 | 
					 * Author: Carlo Caione <carlo@endlessm.com>
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is free software; you can redistribute it and/or modify it
 | 
				
			||||||
 | 
					 * under the terms and conditions of the GNU General Public License,
 | 
				
			||||||
 | 
					 * version 2, as published by the Free Software Foundation.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef __CLKC_H
 | 
				
			||||||
 | 
					#define __CLKC_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define PMASK(width)			GENMASK(width - 1, 0)
 | 
				
			||||||
 | 
					#define SETPMASK(width, shift)		GENMASK(shift + width - 1, shift)
 | 
				
			||||||
 | 
					#define CLRPMASK(width, shift)		(~SETPMASK(width, shift))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define PARM_GET(width, shift, reg)					\
 | 
				
			||||||
 | 
						(((reg) & SETPMASK(width, shift)) >> (shift))
 | 
				
			||||||
 | 
					#define PARM_SET(width, shift, reg, val)				\
 | 
				
			||||||
 | 
						(((reg) & CLRPMASK(width, shift)) | (val << (shift)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define MESON_PARM_APPLICABLE(p)		(!!((p)->width))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct parm {
 | 
				
			||||||
 | 
						u16	reg_off;
 | 
				
			||||||
 | 
						u8	shift;
 | 
				
			||||||
 | 
						u8	width;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					#define PARM(_r, _s, _w)						\
 | 
				
			||||||
 | 
						{								\
 | 
				
			||||||
 | 
							.reg_off	= (_r),					\
 | 
				
			||||||
 | 
							.shift		= (_s),					\
 | 
				
			||||||
 | 
							.width		= (_w),					\
 | 
				
			||||||
 | 
						}								\
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct pll_rate_table {
 | 
				
			||||||
 | 
						unsigned long	rate;
 | 
				
			||||||
 | 
						u16		m;
 | 
				
			||||||
 | 
						u16		n;
 | 
				
			||||||
 | 
						u16		od;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					#define PLL_RATE(_r, _m, _n, _od)					\
 | 
				
			||||||
 | 
						{								\
 | 
				
			||||||
 | 
							.rate		= (_r),					\
 | 
				
			||||||
 | 
							.m		= (_m),					\
 | 
				
			||||||
 | 
							.n		= (_n),					\
 | 
				
			||||||
 | 
							.od		= (_od),				\
 | 
				
			||||||
 | 
						}								\
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct pll_conf {
 | 
				
			||||||
 | 
						const struct pll_rate_table	*rate_table;
 | 
				
			||||||
 | 
						struct parm			m;
 | 
				
			||||||
 | 
						struct parm			n;
 | 
				
			||||||
 | 
						struct parm			od;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct fixed_fact_conf {
 | 
				
			||||||
 | 
						unsigned int	div;
 | 
				
			||||||
 | 
						unsigned int	mult;
 | 
				
			||||||
 | 
						struct parm	div_parm;
 | 
				
			||||||
 | 
						struct parm	mult_parm;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct fixed_rate_conf {
 | 
				
			||||||
 | 
						unsigned long	rate;
 | 
				
			||||||
 | 
						struct parm	rate_parm;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct composite_conf {
 | 
				
			||||||
 | 
						struct parm		mux_parm;
 | 
				
			||||||
 | 
						struct parm		div_parm;
 | 
				
			||||||
 | 
						struct parm		gate_parm;
 | 
				
			||||||
 | 
						struct clk_div_table	*div_table;
 | 
				
			||||||
 | 
						u32			*mux_table;
 | 
				
			||||||
 | 
						u8			mux_flags;
 | 
				
			||||||
 | 
						u8			div_flags;
 | 
				
			||||||
 | 
						u8			gate_flags;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define PNAME(x) static const char *x[]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum clk_type {
 | 
				
			||||||
 | 
						CLK_FIXED_FACTOR,
 | 
				
			||||||
 | 
						CLK_FIXED_RATE,
 | 
				
			||||||
 | 
						CLK_COMPOSITE,
 | 
				
			||||||
 | 
						CLK_CPU,
 | 
				
			||||||
 | 
						CLK_PLL,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct clk_conf {
 | 
				
			||||||
 | 
						u16				reg_off;
 | 
				
			||||||
 | 
						enum clk_type			clk_type;
 | 
				
			||||||
 | 
						unsigned int			clk_id;
 | 
				
			||||||
 | 
						const char			*clk_name;
 | 
				
			||||||
 | 
						const char			**clks_parent;
 | 
				
			||||||
 | 
						int				num_parents;
 | 
				
			||||||
 | 
						unsigned long			flags;
 | 
				
			||||||
 | 
						union {
 | 
				
			||||||
 | 
							struct fixed_fact_conf		fixed_fact;
 | 
				
			||||||
 | 
							struct fixed_rate_conf		fixed_rate;
 | 
				
			||||||
 | 
							const struct composite_conf		*composite;
 | 
				
			||||||
 | 
							struct pll_conf			*pll;
 | 
				
			||||||
 | 
							const struct clk_div_table	*div_table;
 | 
				
			||||||
 | 
						} conf;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define FIXED_RATE_P(_ro, _ci, _cn, _f, _c)				\
 | 
				
			||||||
 | 
						{								\
 | 
				
			||||||
 | 
							.reg_off			= (_ro),		\
 | 
				
			||||||
 | 
							.clk_type			= CLK_FIXED_RATE,	\
 | 
				
			||||||
 | 
							.clk_id				= (_ci),		\
 | 
				
			||||||
 | 
							.clk_name			= (_cn),		\
 | 
				
			||||||
 | 
							.flags				= (_f),			\
 | 
				
			||||||
 | 
							.conf.fixed_rate.rate_parm	= _c,			\
 | 
				
			||||||
 | 
						}								\
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define FIXED_RATE(_ci, _cn, _f, _r)					\
 | 
				
			||||||
 | 
						{								\
 | 
				
			||||||
 | 
							.clk_type			= CLK_FIXED_RATE,	\
 | 
				
			||||||
 | 
							.clk_id				= (_ci),		\
 | 
				
			||||||
 | 
							.clk_name			= (_cn),		\
 | 
				
			||||||
 | 
							.flags				= (_f),			\
 | 
				
			||||||
 | 
							.conf.fixed_rate.rate		= (_r),			\
 | 
				
			||||||
 | 
						}								\
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define PLL(_ro, _ci, _cn, _cp, _f, _c)					\
 | 
				
			||||||
 | 
						{								\
 | 
				
			||||||
 | 
							.reg_off			= (_ro),		\
 | 
				
			||||||
 | 
							.clk_type			= CLK_PLL,		\
 | 
				
			||||||
 | 
							.clk_id				= (_ci),		\
 | 
				
			||||||
 | 
							.clk_name			= (_cn),		\
 | 
				
			||||||
 | 
							.clks_parent			= (_cp),		\
 | 
				
			||||||
 | 
							.num_parents			= ARRAY_SIZE(_cp),	\
 | 
				
			||||||
 | 
							.flags				= (_f),			\
 | 
				
			||||||
 | 
							.conf.pll			= (_c),			\
 | 
				
			||||||
 | 
						}								\
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define FIXED_FACTOR_DIV(_ci, _cn, _cp, _f, _d)				\
 | 
				
			||||||
 | 
						{								\
 | 
				
			||||||
 | 
							.clk_type			= CLK_FIXED_FACTOR,	\
 | 
				
			||||||
 | 
							.clk_id				= (_ci),		\
 | 
				
			||||||
 | 
							.clk_name			= (_cn),		\
 | 
				
			||||||
 | 
							.clks_parent			= (_cp),		\
 | 
				
			||||||
 | 
							.num_parents			= ARRAY_SIZE(_cp),	\
 | 
				
			||||||
 | 
							.conf.fixed_fact.div		= (_d),			\
 | 
				
			||||||
 | 
						}								\
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define CPU(_ro, _ci, _cn, _cp, _dt)					\
 | 
				
			||||||
 | 
						{								\
 | 
				
			||||||
 | 
							.reg_off			= (_ro),		\
 | 
				
			||||||
 | 
							.clk_type			= CLK_CPU,		\
 | 
				
			||||||
 | 
							.clk_id				= (_ci),		\
 | 
				
			||||||
 | 
							.clk_name			= (_cn),		\
 | 
				
			||||||
 | 
							.clks_parent			= (_cp),		\
 | 
				
			||||||
 | 
							.num_parents			= ARRAY_SIZE(_cp),	\
 | 
				
			||||||
 | 
							.conf.div_table			= (_dt),		\
 | 
				
			||||||
 | 
						}								\
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define COMPOSITE(_ro, _ci, _cn, _cp, _f, _c)				\
 | 
				
			||||||
 | 
						{								\
 | 
				
			||||||
 | 
							.reg_off			= (_ro),		\
 | 
				
			||||||
 | 
							.clk_type			= CLK_COMPOSITE,	\
 | 
				
			||||||
 | 
							.clk_id				= (_ci),		\
 | 
				
			||||||
 | 
							.clk_name			= (_cn),		\
 | 
				
			||||||
 | 
							.clks_parent			= (_cp),		\
 | 
				
			||||||
 | 
							.num_parents			= ARRAY_SIZE(_cp),	\
 | 
				
			||||||
 | 
							.flags				= (_f),			\
 | 
				
			||||||
 | 
							.conf.composite			= (_c),			\
 | 
				
			||||||
 | 
						}								\
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct clk **meson_clk_init(struct device_node *np, unsigned long nr_clks);
 | 
				
			||||||
 | 
					void meson_clk_register_clks(const struct clk_conf *clk_confs,
 | 
				
			||||||
 | 
								     unsigned int nr_confs, void __iomem *clk_base);
 | 
				
			||||||
 | 
					struct clk *meson_clk_register_cpu(const struct clk_conf *clk_conf,
 | 
				
			||||||
 | 
									   void __iomem *reg_base, spinlock_t *lock);
 | 
				
			||||||
 | 
					struct clk *meson_clk_register_pll(const struct clk_conf *clk_conf,
 | 
				
			||||||
 | 
									   void __iomem *reg_base, spinlock_t *lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif /* __CLKC_H */
 | 
				
			||||||
							
								
								
									
										25
									
								
								include/dt-bindings/clock/meson8b-clkc.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								include/dt-bindings/clock/meson8b-clkc.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,25 @@
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Meson8b clock tree IDs
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef __MESON8B_CLKC_H
 | 
				
			||||||
 | 
					#define __MESON8B_CLKC_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define CLKID_UNUSED		0
 | 
				
			||||||
 | 
					#define CLKID_XTAL		1
 | 
				
			||||||
 | 
					#define CLKID_PLL_FIXED		2
 | 
				
			||||||
 | 
					#define CLKID_PLL_VID		3
 | 
				
			||||||
 | 
					#define CLKID_PLL_SYS		4
 | 
				
			||||||
 | 
					#define CLKID_FCLK_DIV2		5
 | 
				
			||||||
 | 
					#define CLKID_FCLK_DIV3		6
 | 
				
			||||||
 | 
					#define CLKID_FCLK_DIV4		7
 | 
				
			||||||
 | 
					#define CLKID_FCLK_DIV5		8
 | 
				
			||||||
 | 
					#define CLKID_FCLK_DIV7		9
 | 
				
			||||||
 | 
					#define CLKID_CLK81		10
 | 
				
			||||||
 | 
					#define CLKID_MALI		11
 | 
				
			||||||
 | 
					#define CLKID_CPUCLK		12
 | 
				
			||||||
 | 
					#define CLKID_ZERO		13
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define CLK_NR_CLKS		(CLKID_ZERO + 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif /* __MESON8B_CLKC_H */
 | 
				
			||||||
		Loading…
	
		Reference in a new issue