diff options
Diffstat (limited to 'drivers/clk/tegra/clk-tegra-fixed.c')
| -rw-r--r-- | drivers/clk/tegra/clk-tegra-fixed.c | 111 | 
1 files changed, 111 insertions, 0 deletions
diff --git a/drivers/clk/tegra/clk-tegra-fixed.c b/drivers/clk/tegra/clk-tegra-fixed.c new file mode 100644 index 00000000000..f3b77383342 --- /dev/null +++ b/drivers/clk/tegra/clk-tegra-fixed.c @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2012, 2013, NVIDIA CORPORATION.  All rights reserved. + * + * 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/io.h> +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/delay.h> +#include <linux/export.h> +#include <linux/clk/tegra.h> + +#include "clk.h" +#include "clk-id.h" + +#define OSC_CTRL			0x50 +#define OSC_CTRL_OSC_FREQ_SHIFT		28 +#define OSC_CTRL_PLL_REF_DIV_SHIFT	26 + +int __init tegra_osc_clk_init(void __iomem *clk_base, +				struct tegra_clk *tegra_clks, +				unsigned long *input_freqs, int num, +				unsigned long *osc_freq, +				unsigned long *pll_ref_freq) +{ +	struct clk *clk; +	struct clk **dt_clk; +	u32 val, pll_ref_div; +	unsigned osc_idx; + +	val = readl_relaxed(clk_base + OSC_CTRL); +	osc_idx = val >> OSC_CTRL_OSC_FREQ_SHIFT; + +	if (osc_idx < num) +		*osc_freq = input_freqs[osc_idx]; +	else +		*osc_freq = 0; + +	if (!*osc_freq) { +		WARN_ON(1); +		return -EINVAL; +	} + +	dt_clk = tegra_lookup_dt_id(tegra_clk_clk_m, tegra_clks); +	if (!dt_clk) +		return 0; + +	clk = clk_register_fixed_rate(NULL, "clk_m", NULL, CLK_IS_ROOT, +				      *osc_freq); +	*dt_clk = clk; + +	/* pll_ref */ +	val = (val >> OSC_CTRL_PLL_REF_DIV_SHIFT) & 3; +	pll_ref_div = 1 << val; +	dt_clk = tegra_lookup_dt_id(tegra_clk_pll_ref, tegra_clks); +	if (!dt_clk) +		return 0; + +	clk = clk_register_fixed_factor(NULL, "pll_ref", "clk_m", +					0, 1, pll_ref_div); +	*dt_clk = clk; + +	if (pll_ref_freq) +		*pll_ref_freq = *osc_freq / pll_ref_div; + +	return 0; +} + +void __init tegra_fixed_clk_init(struct tegra_clk *tegra_clks) +{ +	struct clk *clk; +	struct clk **dt_clk; + +	/* clk_32k */ +	dt_clk = tegra_lookup_dt_id(tegra_clk_clk_32k, tegra_clks); +	if (dt_clk) { +		clk = clk_register_fixed_rate(NULL, "clk_32k", NULL, +					CLK_IS_ROOT, 32768); +		*dt_clk = clk; +	} + +	/* clk_m_div2 */ +	dt_clk = tegra_lookup_dt_id(tegra_clk_clk_m_div2, tegra_clks); +	if (dt_clk) { +		clk = clk_register_fixed_factor(NULL, "clk_m_div2", "clk_m", +					CLK_SET_RATE_PARENT, 1, 2); +		*dt_clk = clk; +	} + +	/* clk_m_div4 */ +	dt_clk = tegra_lookup_dt_id(tegra_clk_clk_m_div4, tegra_clks); +	if (dt_clk) { +		clk = clk_register_fixed_factor(NULL, "clk_m_div4", "clk_m", +					CLK_SET_RATE_PARENT, 1, 4); +		*dt_clk = clk; +	} +} +  | 
