From 55b8fd4f428501b0f35d62b8313311fd9863c188 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 10 Apr 2012 09:02:35 +0530 Subject: SPEAr: clk: Add VCO-PLL Synthesizer clock All SPEAr SoC's contain PLLs. Their Fout is derived based on following equations - In normal mode vco = (2 * M[15:8] * Fin)/N - In Dithered mode vco = (2 * M[15:0] * Fin)/(256 * N) pll_rate = vco/2^p vco and pll are very closely bound to each other, "vco needs to program: mode, m & n" and "pll needs to program p", both share common enable/disable logic and registers. This patch adds in support for this type of clock. Signed-off-by: Viresh Kumar Reviewed-by: Mike Turquette --- drivers/clk/Makefile | 3 + drivers/clk/spear/Makefile | 5 + drivers/clk/spear/clk-vco-pll.c | 363 ++++++++++++++++++++++++++++++++++++++++ drivers/clk/spear/clk.c | 36 ++++ drivers/clk/spear/clk.h | 58 +++++++ 5 files changed, 465 insertions(+) create mode 100644 drivers/clk/spear/Makefile create mode 100644 drivers/clk/spear/clk-vco-pll.c create mode 100644 drivers/clk/spear/clk.c create mode 100644 drivers/clk/spear/clk.h (limited to 'drivers/clk') diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 24aa714..0f5e03d 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -2,3 +2,6 @@ obj-$(CONFIG_CLKDEV_LOOKUP) += clkdev.o obj-$(CONFIG_COMMON_CLK) += clk.o clk-fixed-rate.o clk-gate.o \ clk-mux.o clk-divider.o clk-fixed-factor.o + +# SoCs specific +obj-$(CONFIG_PLAT_SPEAR) += spear/ diff --git a/drivers/clk/spear/Makefile b/drivers/clk/spear/Makefile new file mode 100644 index 0000000..3fc2a30 --- /dev/null +++ b/drivers/clk/spear/Makefile @@ -0,0 +1,5 @@ +# +# SPEAr Clock specific Makefile +# + +obj-y += clk.o clk-vco-pll.o diff --git a/drivers/clk/spear/clk-vco-pll.c b/drivers/clk/spear/clk-vco-pll.c new file mode 100644 index 0000000..dcd4bdf --- /dev/null +++ b/drivers/clk/spear/clk-vco-pll.c @@ -0,0 +1,363 @@ +/* + * Copyright (C) 2012 ST Microelectronics + * Viresh Kumar + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + * + * VCO-PLL clock implementation + */ + +#define pr_fmt(fmt) "clk-vco-pll: " fmt + +#include +#include +#include +#include +#include "clk.h" + +/* + * DOC: VCO-PLL clock + * + * VCO and PLL rate are derived from following equations: + * + * In normal mode + * vco = (2 * M[15:8] * Fin)/N + * + * In Dithered mode + * vco = (2 * M[15:0] * Fin)/(256 * N) + * + * pll_rate = pll/2^p + * + * vco and pll are very closely bound to each other, "vco needs to program: + * mode, m & n" and "pll needs to program p", both share common enable/disable + * logic. + * + * clk_register_vco_pll() registers instances of both vco & pll. + * CLK_SET_RATE_PARENT flag is forced for pll, as it will always pass its + * set_rate to vco. A single rate table exists for both the clocks, which + * configures m, n and p. + */ + +/* PLL_CTR register masks */ +#define PLL_MODE_NORMAL 0 +#define PLL_MODE_FRACTION 1 +#define PLL_MODE_DITH_DSM 2 +#define PLL_MODE_DITH_SSM 3 +#define PLL_MODE_MASK 3 +#define PLL_MODE_SHIFT 3 +#define PLL_ENABLE 2 + +#define PLL_LOCK_SHIFT 0 +#define PLL_LOCK_MASK 1 + +/* PLL FRQ register masks */ +#define PLL_NORM_FDBK_M_MASK 0xFF +#define PLL_NORM_FDBK_M_SHIFT 24 +#define PLL_DITH_FDBK_M_MASK 0xFFFF +#define PLL_DITH_FDBK_M_SHIFT 16 +#define PLL_DIV_P_MASK 0x7 +#define PLL_DIV_P_SHIFT 8 +#define PLL_DIV_N_MASK 0xFF +#define PLL_DIV_N_SHIFT 0 + +#define to_clk_vco(_hw) container_of(_hw, struct clk_vco, hw) +#define to_clk_pll(_hw) container_of(_hw, struct clk_pll, hw) + +/* Calculates pll clk rate for specific value of mode, m, n and p */ +static unsigned long pll_calc_rate(struct pll_rate_tbl *rtbl, + unsigned long prate, int index, unsigned long *pll_rate) +{ + unsigned long rate = prate; + unsigned int mode; + + mode = rtbl[index].mode ? 256 : 1; + rate = (((2 * rate / 10000) * rtbl[index].m) / (mode * rtbl[index].n)); + + if (pll_rate) + *pll_rate = (rate / (1 << rtbl[index].p)) * 10000; + + return rate * 10000; +} + +static long clk_pll_round_rate_index(struct clk_hw *hw, unsigned long drate, + unsigned long *prate, int *index) +{ + struct clk_pll *pll = to_clk_pll(hw); + unsigned long prev_rate, vco_prev_rate, rate = 0; + unsigned long vco_parent_rate = + __clk_get_rate(__clk_get_parent(__clk_get_parent(hw->clk))); + + if (!prate) { + pr_err("%s: prate is must for pll clk\n", __func__); + return -EINVAL; + } + + for (*index = 0; *index < pll->vco->rtbl_cnt; (*index)++) { + prev_rate = rate; + vco_prev_rate = *prate; + *prate = pll_calc_rate(pll->vco->rtbl, vco_parent_rate, *index, + &rate); + if (drate < rate) { + /* previous clock was best */ + if (*index) { + rate = prev_rate; + *prate = vco_prev_rate; + (*index)--; + } + break; + } + } + + return rate; +} + +static long clk_pll_round_rate(struct clk_hw *hw, unsigned long drate, + unsigned long *prate) +{ + int unused; + + return clk_pll_round_rate_index(hw, drate, prate, &unused); +} + +static unsigned long clk_pll_recalc_rate(struct clk_hw *hw, unsigned long + parent_rate) +{ + struct clk_pll *pll = to_clk_pll(hw); + unsigned long flags = 0; + unsigned int p; + + if (pll->vco->lock) + spin_lock_irqsave(pll->vco->lock, flags); + + p = readl_relaxed(pll->vco->cfg_reg); + + if (pll->vco->lock) + spin_unlock_irqrestore(pll->vco->lock, flags); + + p = (p >> PLL_DIV_P_SHIFT) & PLL_DIV_P_MASK; + + return parent_rate / (1 << p); +} + +static int clk_pll_set_rate(struct clk_hw *hw, unsigned long drate, + unsigned long prate) +{ + struct clk_pll *pll = to_clk_pll(hw); + struct pll_rate_tbl *rtbl = pll->vco->rtbl; + unsigned long flags = 0, val; + int i; + + clk_pll_round_rate_index(hw, drate, NULL, &i); + + if (pll->vco->lock) + spin_lock_irqsave(pll->vco->lock, flags); + + val = readl_relaxed(pll->vco->cfg_reg); + val &= ~(PLL_DIV_P_MASK << PLL_DIV_P_SHIFT); + val |= (rtbl[i].p & PLL_DIV_P_MASK) << PLL_DIV_P_SHIFT; + writel_relaxed(val, pll->vco->cfg_reg); + + if (pll->vco->lock) + spin_unlock_irqrestore(pll->vco->lock, flags); + + return 0; +} + +static struct clk_ops clk_pll_ops = { + .recalc_rate = clk_pll_recalc_rate, + .round_rate = clk_pll_round_rate, + .set_rate = clk_pll_set_rate, +}; + +static inline unsigned long vco_calc_rate(struct clk_hw *hw, + unsigned long prate, int index) +{ + struct clk_vco *vco = to_clk_vco(hw); + + return pll_calc_rate(vco->rtbl, prate, index, NULL); +} + +static long clk_vco_round_rate(struct clk_hw *hw, unsigned long drate, + unsigned long *prate) +{ + struct clk_vco *vco = to_clk_vco(hw); + int unused; + + return clk_round_rate_index(hw, drate, *prate, vco_calc_rate, + vco->rtbl_cnt, &unused); +} + +static unsigned long clk_vco_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_vco *vco = to_clk_vco(hw); + unsigned long flags = 0; + unsigned int num = 2, den = 0, val, mode = 0; + + if (vco->lock) + spin_lock_irqsave(vco->lock, flags); + + mode = (readl_relaxed(vco->mode_reg) >> PLL_MODE_SHIFT) & PLL_MODE_MASK; + + val = readl_relaxed(vco->cfg_reg); + + if (vco->lock) + spin_unlock_irqrestore(vco->lock, flags); + + den = (val >> PLL_DIV_N_SHIFT) & PLL_DIV_N_MASK; + + /* calculate numerator & denominator */ + if (!mode) { + /* Normal mode */ + num *= (val >> PLL_NORM_FDBK_M_SHIFT) & PLL_NORM_FDBK_M_MASK; + } else { + /* Dithered mode */ + num *= (val >> PLL_DITH_FDBK_M_SHIFT) & PLL_DITH_FDBK_M_MASK; + den *= 256; + } + + if (!den) { + WARN(1, "%s: denominator can't be zero\n", __func__); + return 0; + } + + return (((parent_rate / 10000) * num) / den) * 10000; +} + +/* Configures new clock rate of vco */ +static int clk_vco_set_rate(struct clk_hw *hw, unsigned long drate, + unsigned long prate) +{ + struct clk_vco *vco = to_clk_vco(hw); + struct pll_rate_tbl *rtbl = vco->rtbl; + unsigned long flags = 0, val; + int i; + + clk_round_rate_index(hw, drate, prate, vco_calc_rate, vco->rtbl_cnt, + &i); + + if (vco->lock) + spin_lock_irqsave(vco->lock, flags); + + val = readl_relaxed(vco->mode_reg); + val &= ~(PLL_MODE_MASK << PLL_MODE_SHIFT); + val |= (rtbl[i].mode & PLL_MODE_MASK) << PLL_MODE_SHIFT; + writel_relaxed(val, vco->mode_reg); + + val = readl_relaxed(vco->cfg_reg); + val &= ~(PLL_DIV_N_MASK << PLL_DIV_N_SHIFT); + val |= (rtbl[i].n & PLL_DIV_N_MASK) << PLL_DIV_N_SHIFT; + + val &= ~(PLL_DITH_FDBK_M_MASK << PLL_DITH_FDBK_M_SHIFT); + if (rtbl[i].mode) + val |= (rtbl[i].m & PLL_DITH_FDBK_M_MASK) << + PLL_DITH_FDBK_M_SHIFT; + else + val |= (rtbl[i].m & PLL_NORM_FDBK_M_MASK) << + PLL_NORM_FDBK_M_SHIFT; + + writel_relaxed(val, vco->cfg_reg); + + if (vco->lock) + spin_unlock_irqrestore(vco->lock, flags); + + return 0; +} + +static struct clk_ops clk_vco_ops = { + .recalc_rate = clk_vco_recalc_rate, + .round_rate = clk_vco_round_rate, + .set_rate = clk_vco_set_rate, +}; + +struct clk *clk_register_vco_pll(const char *vco_name, const char *pll_name, + const char *vco_gate_name, const char *parent_name, + unsigned long flags, void __iomem *mode_reg, void __iomem + *cfg_reg, struct pll_rate_tbl *rtbl, u8 rtbl_cnt, + spinlock_t *lock, struct clk **pll_clk, + struct clk **vco_gate_clk) +{ + struct clk_vco *vco; + struct clk_pll *pll; + struct clk *vco_clk, *tpll_clk, *tvco_gate_clk; + struct clk_init_data vco_init, pll_init; + const char **vco_parent_name; + + if (!vco_name || !pll_name || !parent_name || !mode_reg || !cfg_reg || + !rtbl || !rtbl_cnt) { + pr_err("Invalid arguments passed"); + return ERR_PTR(-EINVAL); + } + + vco = kzalloc(sizeof(*vco), GFP_KERNEL); + if (!vco) { + pr_err("could not allocate vco clk\n"); + return ERR_PTR(-ENOMEM); + } + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) { + pr_err("could not allocate pll clk\n"); + goto free_vco; + } + + /* struct clk_vco assignments */ + vco->mode_reg = mode_reg; + vco->cfg_reg = cfg_reg; + vco->rtbl = rtbl; + vco->rtbl_cnt = rtbl_cnt; + vco->lock = lock; + vco->hw.init = &vco_init; + + pll->vco = vco; + pll->hw.init = &pll_init; + + if (vco_gate_name) { + tvco_gate_clk = clk_register_gate(NULL, vco_gate_name, + parent_name, 0, mode_reg, PLL_ENABLE, 0, lock); + if (IS_ERR_OR_NULL(tvco_gate_clk)) + goto free_pll; + + if (vco_gate_clk) + *vco_gate_clk = tvco_gate_clk; + vco_parent_name = &vco_gate_name; + } else { + vco_parent_name = &parent_name; + } + + vco_init.name = vco_name; + vco_init.ops = &clk_vco_ops; + vco_init.flags = flags; + vco_init.parent_names = vco_parent_name; + vco_init.num_parents = 1; + + pll_init.name = pll_name; + pll_init.ops = &clk_pll_ops; + pll_init.flags = CLK_SET_RATE_PARENT; + pll_init.parent_names = &vco_name; + pll_init.num_parents = 1; + + vco_clk = clk_register(NULL, &vco->hw); + if (IS_ERR_OR_NULL(vco_clk)) + goto free_pll; + + tpll_clk = clk_register(NULL, &pll->hw); + if (IS_ERR_OR_NULL(tpll_clk)) + goto free_pll; + + if (pll_clk) + *pll_clk = tpll_clk; + + return vco_clk; + +free_pll: + kfree(pll); +free_vco: + kfree(vco); + + pr_err("Failed to register vco pll clock\n"); + + return ERR_PTR(-ENOMEM); +} diff --git a/drivers/clk/spear/clk.c b/drivers/clk/spear/clk.c new file mode 100644 index 0000000..376d4e5 --- /dev/null +++ b/drivers/clk/spear/clk.c @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2012 ST Microelectronics + * Viresh Kumar + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + * + * SPEAr clk - Common routines + */ + +#include +#include +#include "clk.h" + +long clk_round_rate_index(struct clk_hw *hw, unsigned long drate, + unsigned long parent_rate, clk_calc_rate calc_rate, u8 rtbl_cnt, + int *index) +{ + unsigned long prev_rate, rate = 0; + + for (*index = 0; *index < rtbl_cnt; (*index)++) { + prev_rate = rate; + rate = calc_rate(hw, parent_rate, *index); + if (drate < rate) { + /* previous clock was best */ + if (*index) { + rate = prev_rate; + (*index)--; + } + break; + } + } + + return rate; +} diff --git a/drivers/clk/spear/clk.h b/drivers/clk/spear/clk.h new file mode 100644 index 0000000..9979b7f --- /dev/null +++ b/drivers/clk/spear/clk.h @@ -0,0 +1,58 @@ +/* + * Clock framework definitions for SPEAr platform + * + * Copyright (C) 2012 ST Microelectronics + * Viresh Kumar + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#ifndef __SPEAR_CLK_H +#define __SPEAR_CLK_H + +#include +#include +#include + +/* VCO-PLL clk */ +struct pll_rate_tbl { + u8 mode; + u16 m; + u8 n; + u8 p; +}; + +struct clk_vco { + struct clk_hw hw; + void __iomem *mode_reg; + void __iomem *cfg_reg; + struct pll_rate_tbl *rtbl; + u8 rtbl_cnt; + spinlock_t *lock; +}; + +struct clk_pll { + struct clk_hw hw; + struct clk_vco *vco; + const char *parent[1]; + spinlock_t *lock; +}; + +typedef unsigned long (*clk_calc_rate)(struct clk_hw *hw, unsigned long prate, + int index); + +/* clk register routines */ +struct clk *clk_register_vco_pll(const char *vco_name, const char *pll_name, + const char *vco_gate_name, const char *parent_name, + unsigned long flags, void __iomem *mode_reg, void __iomem + *cfg_reg, struct pll_rate_tbl *rtbl, u8 rtbl_cnt, + spinlock_t *lock, struct clk **pll_clk, + struct clk **vco_gate_clk); + +long clk_round_rate_index(struct clk_hw *hw, unsigned long drate, + unsigned long parent_rate, clk_calc_rate calc_rate, u8 rtbl_cnt, + int *index); + +#endif /* __SPEAR_CLK_H */ -- cgit v1.1 From 5335a639ecc5646cbe8e99086fb7e743b801ac58 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 11 Apr 2012 18:04:23 +0530 Subject: SPEAr: clk: Add Auxiliary Synthesizer clock All SPEAr SoC's contain Auxiliary Synthesizers. Their Fout is derived based on values of eq, x and y. Fout from synthesizer can be given from two equations: Fout1 = (Fin * X/Y)/2 EQ1 Fout2 = Fin * X/Y EQ2 This patch adds in support for this type of clock. Signed-off-by: Viresh Kumar Reviewed-by: Mike Turquette --- drivers/clk/spear/Makefile | 2 +- drivers/clk/spear/clk-aux-synth.c | 198 ++++++++++++++++++++++++++++++++++++++ drivers/clk/spear/clk.h | 43 +++++++++ 3 files changed, 242 insertions(+), 1 deletion(-) create mode 100644 drivers/clk/spear/clk-aux-synth.c (limited to 'drivers/clk') diff --git a/drivers/clk/spear/Makefile b/drivers/clk/spear/Makefile index 3fc2a30..e36d8c6 100644 --- a/drivers/clk/spear/Makefile +++ b/drivers/clk/spear/Makefile @@ -2,4 +2,4 @@ # SPEAr Clock specific Makefile # -obj-y += clk.o clk-vco-pll.o +obj-y += clk.o clk-aux-synth.o clk-vco-pll.o diff --git a/drivers/clk/spear/clk-aux-synth.c b/drivers/clk/spear/clk-aux-synth.c new file mode 100644 index 0000000..af34074 --- /dev/null +++ b/drivers/clk/spear/clk-aux-synth.c @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2012 ST Microelectronics + * Viresh Kumar + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + * + * Auxiliary Synthesizer clock implementation + */ + +#define pr_fmt(fmt) "clk-aux-synth: " fmt + +#include +#include +#include +#include +#include "clk.h" + +/* + * DOC: Auxiliary Synthesizer clock + * + * Aux synth gives rate for different values of eq, x and y + * + * Fout from synthesizer can be given from two equations: + * Fout1 = (Fin * X/Y)/2 EQ1 + * Fout2 = Fin * X/Y EQ2 + */ + +#define to_clk_aux(_hw) container_of(_hw, struct clk_aux, hw) + +static struct aux_clk_masks default_aux_masks = { + .eq_sel_mask = AUX_EQ_SEL_MASK, + .eq_sel_shift = AUX_EQ_SEL_SHIFT, + .eq1_mask = AUX_EQ1_SEL, + .eq2_mask = AUX_EQ2_SEL, + .xscale_sel_mask = AUX_XSCALE_MASK, + .xscale_sel_shift = AUX_XSCALE_SHIFT, + .yscale_sel_mask = AUX_YSCALE_MASK, + .yscale_sel_shift = AUX_YSCALE_SHIFT, + .enable_bit = AUX_SYNT_ENB, +}; + +static unsigned long aux_calc_rate(struct clk_hw *hw, unsigned long prate, + int index) +{ + struct clk_aux *aux = to_clk_aux(hw); + struct aux_rate_tbl *rtbl = aux->rtbl; + u8 eq = rtbl[index].eq ? 1 : 2; + + return (((prate / 10000) * rtbl[index].xscale) / + (rtbl[index].yscale * eq)) * 10000; +} + +static long clk_aux_round_rate(struct clk_hw *hw, unsigned long drate, + unsigned long *prate) +{ + struct clk_aux *aux = to_clk_aux(hw); + int unused; + + return clk_round_rate_index(hw, drate, *prate, aux_calc_rate, + aux->rtbl_cnt, &unused); +} + +static unsigned long clk_aux_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_aux *aux = to_clk_aux(hw); + unsigned int num = 1, den = 1, val, eqn; + unsigned long flags = 0; + + if (aux->lock) + spin_lock_irqsave(aux->lock, flags); + + val = readl_relaxed(aux->reg); + + if (aux->lock) + spin_unlock_irqrestore(aux->lock, flags); + + eqn = (val >> aux->masks->eq_sel_shift) & aux->masks->eq_sel_mask; + if (eqn == aux->masks->eq1_mask) + den = 2; + + /* calculate numerator */ + num = (val >> aux->masks->xscale_sel_shift) & + aux->masks->xscale_sel_mask; + + /* calculate denominator */ + den *= (val >> aux->masks->yscale_sel_shift) & + aux->masks->yscale_sel_mask; + + if (!den) + return 0; + + return (((parent_rate / 10000) * num) / den) * 10000; +} + +/* Configures new clock rate of aux */ +static int clk_aux_set_rate(struct clk_hw *hw, unsigned long drate, + unsigned long prate) +{ + struct clk_aux *aux = to_clk_aux(hw); + struct aux_rate_tbl *rtbl = aux->rtbl; + unsigned long val, flags = 0; + int i; + + clk_round_rate_index(hw, drate, prate, aux_calc_rate, aux->rtbl_cnt, + &i); + + if (aux->lock) + spin_lock_irqsave(aux->lock, flags); + + val = readl_relaxed(aux->reg) & + ~(aux->masks->eq_sel_mask << aux->masks->eq_sel_shift); + val |= (rtbl[i].eq & aux->masks->eq_sel_mask) << + aux->masks->eq_sel_shift; + val &= ~(aux->masks->xscale_sel_mask << aux->masks->xscale_sel_shift); + val |= (rtbl[i].xscale & aux->masks->xscale_sel_mask) << + aux->masks->xscale_sel_shift; + val &= ~(aux->masks->yscale_sel_mask << aux->masks->yscale_sel_shift); + val |= (rtbl[i].yscale & aux->masks->yscale_sel_mask) << + aux->masks->yscale_sel_shift; + writel_relaxed(val, aux->reg); + + if (aux->lock) + spin_unlock_irqrestore(aux->lock, flags); + + return 0; +} + +static struct clk_ops clk_aux_ops = { + .recalc_rate = clk_aux_recalc_rate, + .round_rate = clk_aux_round_rate, + .set_rate = clk_aux_set_rate, +}; + +struct clk *clk_register_aux(const char *aux_name, const char *gate_name, + const char *parent_name, unsigned long flags, void __iomem *reg, + struct aux_clk_masks *masks, struct aux_rate_tbl *rtbl, + u8 rtbl_cnt, spinlock_t *lock, struct clk **gate_clk) +{ + struct clk_aux *aux; + struct clk_init_data init; + struct clk *clk; + + if (!aux_name || !parent_name || !reg || !rtbl || !rtbl_cnt) { + pr_err("Invalid arguments passed"); + return ERR_PTR(-EINVAL); + } + + aux = kzalloc(sizeof(*aux), GFP_KERNEL); + if (!aux) { + pr_err("could not allocate aux clk\n"); + return ERR_PTR(-ENOMEM); + } + + /* struct clk_aux assignments */ + if (!masks) + aux->masks = &default_aux_masks; + else + aux->masks = masks; + + aux->reg = reg; + aux->rtbl = rtbl; + aux->rtbl_cnt = rtbl_cnt; + aux->lock = lock; + aux->hw.init = &init; + + init.name = aux_name; + init.ops = &clk_aux_ops; + init.flags = flags; + init.parent_names = &parent_name; + init.num_parents = 1; + + clk = clk_register(NULL, &aux->hw); + if (IS_ERR_OR_NULL(clk)) + goto free_aux; + + if (gate_name) { + struct clk *tgate_clk; + + tgate_clk = clk_register_gate(NULL, gate_name, aux_name, 0, reg, + aux->masks->enable_bit, 0, lock); + if (IS_ERR_OR_NULL(tgate_clk)) + goto free_aux; + + if (gate_clk) + *gate_clk = tgate_clk; + } + + return clk; + +free_aux: + kfree(aux); + pr_err("clk register failed\n"); + + return NULL; +} diff --git a/drivers/clk/spear/clk.h b/drivers/clk/spear/clk.h index 9979b7f..c229080 100644 --- a/drivers/clk/spear/clk.h +++ b/drivers/clk/spear/clk.h @@ -16,6 +16,45 @@ #include #include +/* Auxiliary Synth clk */ +/* Default masks */ +#define AUX_EQ_SEL_SHIFT 30 +#define AUX_EQ_SEL_MASK 1 +#define AUX_EQ1_SEL 0 +#define AUX_EQ2_SEL 1 +#define AUX_XSCALE_SHIFT 16 +#define AUX_XSCALE_MASK 0xFFF +#define AUX_YSCALE_SHIFT 0 +#define AUX_YSCALE_MASK 0xFFF +#define AUX_SYNT_ENB 31 + +struct aux_clk_masks { + u32 eq_sel_mask; + u32 eq_sel_shift; + u32 eq1_mask; + u32 eq2_mask; + u32 xscale_sel_mask; + u32 xscale_sel_shift; + u32 yscale_sel_mask; + u32 yscale_sel_shift; + u32 enable_bit; +}; + +struct aux_rate_tbl { + u16 xscale; + u16 yscale; + u8 eq; +}; + +struct clk_aux { + struct clk_hw hw; + void __iomem *reg; + struct aux_clk_masks *masks; + struct aux_rate_tbl *rtbl; + u8 rtbl_cnt; + spinlock_t *lock; +}; + /* VCO-PLL clk */ struct pll_rate_tbl { u8 mode; @@ -44,6 +83,10 @@ typedef unsigned long (*clk_calc_rate)(struct clk_hw *hw, unsigned long prate, int index); /* clk register routines */ +struct clk *clk_register_aux(const char *aux_name, const char *gate_name, + const char *parent_name, unsigned long flags, void __iomem *reg, + struct aux_clk_masks *masks, struct aux_rate_tbl *rtbl, + u8 rtbl_cnt, spinlock_t *lock, struct clk **gate_clk); struct clk *clk_register_vco_pll(const char *vco_name, const char *pll_name, const char *vco_gate_name, const char *parent_name, unsigned long flags, void __iomem *mode_reg, void __iomem -- cgit v1.1 From 270b9f421e66ee5d135c99ba1c2b883c7750ab6c Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 11 Apr 2012 18:04:23 +0530 Subject: SPEAr: clk: Add Fractional Synthesizer clock All SPEAr SoC's contain Fractional Synthesizers. Their Fout is derived from following equations: Fout = Fin / (2 * div) (division factor) div is 17 bits:- 0-13 (fractional part) 14-16 (integer part) div is (16-14 bits).(13-0 bits) (in binary) Fout = Fin/(2 * div) Fout = ((Fin / 10000)/(2 * div)) * 10000 Fout = (2^14 * (Fin / 10000)/(2^14 * (2 * div))) * 10000 Fout = (((Fin / 10000) << 14)/(2 * (div << 14))) * 10000 div << 14 is simply 17 bit value written at register. This patch adds in support for this type of clock. Signed-off-by: Viresh Kumar Reviewed-by: Mike Turquette --- drivers/clk/spear/Makefile | 2 +- drivers/clk/spear/clk-frac-synth.c | 165 +++++++++++++++++++++++++++++++++++++ drivers/clk/spear/clk.h | 16 ++++ 3 files changed, 182 insertions(+), 1 deletion(-) create mode 100644 drivers/clk/spear/clk-frac-synth.c (limited to 'drivers/clk') diff --git a/drivers/clk/spear/Makefile b/drivers/clk/spear/Makefile index e36d8c6..1827036 100644 --- a/drivers/clk/spear/Makefile +++ b/drivers/clk/spear/Makefile @@ -2,4 +2,4 @@ # SPEAr Clock specific Makefile # -obj-y += clk.o clk-aux-synth.o clk-vco-pll.o +obj-y += clk.o clk-aux-synth.o clk-frac-synth.o clk-vco-pll.o diff --git a/drivers/clk/spear/clk-frac-synth.c b/drivers/clk/spear/clk-frac-synth.c new file mode 100644 index 0000000..4dbdb3f --- /dev/null +++ b/drivers/clk/spear/clk-frac-synth.c @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2012 ST Microelectronics + * Viresh Kumar + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + * + * Fractional Synthesizer clock implementation + */ + +#define pr_fmt(fmt) "clk-frac-synth: " fmt + +#include +#include +#include +#include +#include "clk.h" + +#define DIV_FACTOR_MASK 0x1FFFF + +/* + * DOC: Fractional Synthesizer clock + * + * Fout from synthesizer can be given from below equation: + * + * Fout= Fin/2*div (division factor) + * div is 17 bits:- + * 0-13 (fractional part) + * 14-16 (integer part) + * div is (16-14 bits).(13-0 bits) (in binary) + * + * Fout = Fin/(2 * div) + * Fout = ((Fin / 10000)/(2 * div)) * 10000 + * Fout = (2^14 * (Fin / 10000)/(2^14 * (2 * div))) * 10000 + * Fout = (((Fin / 10000) << 14)/(2 * (div << 14))) * 10000 + * + * div << 14 simply 17 bit value written at register. + * Max error due to scaling down by 10000 is 10 KHz + */ + +#define to_clk_frac(_hw) container_of(_hw, struct clk_frac, hw) + +static unsigned long frac_calc_rate(struct clk_hw *hw, unsigned long prate, + int index) +{ + struct clk_frac *frac = to_clk_frac(hw); + struct frac_rate_tbl *rtbl = frac->rtbl; + + prate /= 10000; + prate <<= 14; + prate /= (2 * rtbl[index].div); + prate *= 10000; + + return prate; +} + +static long clk_frac_round_rate(struct clk_hw *hw, unsigned long drate, + unsigned long *prate) +{ + struct clk_frac *frac = to_clk_frac(hw); + int unused; + + return clk_round_rate_index(hw, drate, *prate, frac_calc_rate, + frac->rtbl_cnt, &unused); +} + +static unsigned long clk_frac_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_frac *frac = to_clk_frac(hw); + unsigned long flags = 0; + unsigned int div = 1, val; + + if (frac->lock) + spin_lock_irqsave(frac->lock, flags); + + val = readl_relaxed(frac->reg); + + if (frac->lock) + spin_unlock_irqrestore(frac->lock, flags); + + div = val & DIV_FACTOR_MASK; + + if (!div) + return 0; + + parent_rate = parent_rate / 10000; + + parent_rate = (parent_rate << 14) / (2 * div); + return parent_rate * 10000; +} + +/* Configures new clock rate of frac */ +static int clk_frac_set_rate(struct clk_hw *hw, unsigned long drate, + unsigned long prate) +{ + struct clk_frac *frac = to_clk_frac(hw); + struct frac_rate_tbl *rtbl = frac->rtbl; + unsigned long flags = 0, val; + int i; + + clk_round_rate_index(hw, drate, prate, frac_calc_rate, frac->rtbl_cnt, + &i); + + if (frac->lock) + spin_lock_irqsave(frac->lock, flags); + + val = readl_relaxed(frac->reg) & ~DIV_FACTOR_MASK; + val |= rtbl[i].div & DIV_FACTOR_MASK; + writel_relaxed(val, frac->reg); + + if (frac->lock) + spin_unlock_irqrestore(frac->lock, flags); + + return 0; +} + +struct clk_ops clk_frac_ops = { + .recalc_rate = clk_frac_recalc_rate, + .round_rate = clk_frac_round_rate, + .set_rate = clk_frac_set_rate, +}; + +struct clk *clk_register_frac(const char *name, const char *parent_name, + unsigned long flags, void __iomem *reg, + struct frac_rate_tbl *rtbl, u8 rtbl_cnt, spinlock_t *lock) +{ + struct clk_init_data init; + struct clk_frac *frac; + struct clk *clk; + + if (!name || !parent_name || !reg || !rtbl || !rtbl_cnt) { + pr_err("Invalid arguments passed"); + return ERR_PTR(-EINVAL); + } + + frac = kzalloc(sizeof(*frac), GFP_KERNEL); + if (!frac) { + pr_err("could not allocate frac clk\n"); + return ERR_PTR(-ENOMEM); + } + + /* struct clk_frac assignments */ + frac->reg = reg; + frac->rtbl = rtbl; + frac->rtbl_cnt = rtbl_cnt; + frac->lock = lock; + frac->hw.init = &init; + + init.name = name; + init.ops = &clk_frac_ops; + init.flags = flags; + init.parent_names = &parent_name; + init.num_parents = 1; + + clk = clk_register(NULL, &frac->hw); + if (!IS_ERR_OR_NULL(clk)) + return clk; + + pr_err("clk register failed\n"); + kfree(frac); + + return NULL; +} diff --git a/drivers/clk/spear/clk.h b/drivers/clk/spear/clk.h index c229080..ac9030b 100644 --- a/drivers/clk/spear/clk.h +++ b/drivers/clk/spear/clk.h @@ -55,6 +55,19 @@ struct clk_aux { spinlock_t *lock; }; +/* Fractional Synth clk */ +struct frac_rate_tbl { + u32 div; +}; + +struct clk_frac { + struct clk_hw hw; + void __iomem *reg; + struct frac_rate_tbl *rtbl; + u8 rtbl_cnt; + spinlock_t *lock; +}; + /* VCO-PLL clk */ struct pll_rate_tbl { u8 mode; @@ -87,6 +100,9 @@ struct clk *clk_register_aux(const char *aux_name, const char *gate_name, const char *parent_name, unsigned long flags, void __iomem *reg, struct aux_clk_masks *masks, struct aux_rate_tbl *rtbl, u8 rtbl_cnt, spinlock_t *lock, struct clk **gate_clk); +struct clk *clk_register_frac(const char *name, const char *parent_name, + unsigned long flags, void __iomem *reg, + struct frac_rate_tbl *rtbl, u8 rtbl_cnt, spinlock_t *lock); struct clk *clk_register_vco_pll(const char *vco_name, const char *pll_name, const char *vco_gate_name, const char *parent_name, unsigned long flags, void __iomem *mode_reg, void __iomem -- cgit v1.1 From a45896bd3a4b7beb571fa704efa7c2782b791093 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 11 Apr 2012 18:04:23 +0530 Subject: SPEAr: clk: Add General Purpose Timer Synthesizer clock All SPEAr SoC's contain GPT Synthesizers. Their Fout is derived from following equations: Fout= Fin/((2 ^ (N+1)) * (M+1)) This patch adds in support for this type of clock. Signed-off-by: Viresh Kumar Reviewed-by: Mike Turquette --- drivers/clk/spear/Makefile | 2 +- drivers/clk/spear/clk-gpt-synth.c | 154 ++++++++++++++++++++++++++++++++++++++ drivers/clk/spear/clk.h | 17 +++++ 3 files changed, 172 insertions(+), 1 deletion(-) create mode 100644 drivers/clk/spear/clk-gpt-synth.c (limited to 'drivers/clk') diff --git a/drivers/clk/spear/Makefile b/drivers/clk/spear/Makefile index 1827036..9e64824 100644 --- a/drivers/clk/spear/Makefile +++ b/drivers/clk/spear/Makefile @@ -2,4 +2,4 @@ # SPEAr Clock specific Makefile # -obj-y += clk.o clk-aux-synth.o clk-frac-synth.o clk-vco-pll.o +obj-y += clk.o clk-aux-synth.o clk-frac-synth.o clk-gpt-synth.o clk-vco-pll.o diff --git a/drivers/clk/spear/clk-gpt-synth.c b/drivers/clk/spear/clk-gpt-synth.c new file mode 100644 index 0000000..b471c97 --- /dev/null +++ b/drivers/clk/spear/clk-gpt-synth.c @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2012 ST Microelectronics + * Viresh Kumar + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + * + * General Purpose Timer Synthesizer clock implementation + */ + +#define pr_fmt(fmt) "clk-gpt-synth: " fmt + +#include +#include +#include +#include +#include "clk.h" + +#define GPT_MSCALE_MASK 0xFFF +#define GPT_NSCALE_SHIFT 12 +#define GPT_NSCALE_MASK 0xF + +/* + * DOC: General Purpose Timer Synthesizer clock + * + * Calculates gpt synth clk rate for different values of mscale and nscale + * + * Fout= Fin/((2 ^ (N+1)) * (M+1)) + */ + +#define to_clk_gpt(_hw) container_of(_hw, struct clk_gpt, hw) + +static unsigned long gpt_calc_rate(struct clk_hw *hw, unsigned long prate, + int index) +{ + struct clk_gpt *gpt = to_clk_gpt(hw); + struct gpt_rate_tbl *rtbl = gpt->rtbl; + + prate /= ((1 << (rtbl[index].nscale + 1)) * (rtbl[index].mscale + 1)); + + return prate; +} + +static long clk_gpt_round_rate(struct clk_hw *hw, unsigned long drate, + unsigned long *prate) +{ + struct clk_gpt *gpt = to_clk_gpt(hw); + int unused; + + return clk_round_rate_index(hw, drate, *prate, gpt_calc_rate, + gpt->rtbl_cnt, &unused); +} + +static unsigned long clk_gpt_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_gpt *gpt = to_clk_gpt(hw); + unsigned long flags = 0; + unsigned int div = 1, val; + + if (gpt->lock) + spin_lock_irqsave(gpt->lock, flags); + + val = readl_relaxed(gpt->reg); + + if (gpt->lock) + spin_unlock_irqrestore(gpt->lock, flags); + + div += val & GPT_MSCALE_MASK; + div *= 1 << (((val >> GPT_NSCALE_SHIFT) & GPT_NSCALE_MASK) + 1); + + if (!div) + return 0; + + return parent_rate / div; +} + +/* Configures new clock rate of gpt */ +static int clk_gpt_set_rate(struct clk_hw *hw, unsigned long drate, + unsigned long prate) +{ + struct clk_gpt *gpt = to_clk_gpt(hw); + struct gpt_rate_tbl *rtbl = gpt->rtbl; + unsigned long flags = 0, val; + int i; + + clk_round_rate_index(hw, drate, prate, gpt_calc_rate, gpt->rtbl_cnt, + &i); + + if (gpt->lock) + spin_lock_irqsave(gpt->lock, flags); + + val = readl(gpt->reg) & ~GPT_MSCALE_MASK; + val &= ~(GPT_NSCALE_MASK << GPT_NSCALE_SHIFT); + + val |= rtbl[i].mscale & GPT_MSCALE_MASK; + val |= (rtbl[i].nscale & GPT_NSCALE_MASK) << GPT_NSCALE_SHIFT; + + writel_relaxed(val, gpt->reg); + + if (gpt->lock) + spin_unlock_irqrestore(gpt->lock, flags); + + return 0; +} + +static struct clk_ops clk_gpt_ops = { + .recalc_rate = clk_gpt_recalc_rate, + .round_rate = clk_gpt_round_rate, + .set_rate = clk_gpt_set_rate, +}; + +struct clk *clk_register_gpt(const char *name, const char *parent_name, unsigned + long flags, void __iomem *reg, struct gpt_rate_tbl *rtbl, u8 + rtbl_cnt, spinlock_t *lock) +{ + struct clk_init_data init; + struct clk_gpt *gpt; + struct clk *clk; + + if (!name || !parent_name || !reg || !rtbl || !rtbl_cnt) { + pr_err("Invalid arguments passed"); + return ERR_PTR(-EINVAL); + } + + gpt = kzalloc(sizeof(*gpt), GFP_KERNEL); + if (!gpt) { + pr_err("could not allocate gpt clk\n"); + return ERR_PTR(-ENOMEM); + } + + /* struct clk_gpt assignments */ + gpt->reg = reg; + gpt->rtbl = rtbl; + gpt->rtbl_cnt = rtbl_cnt; + gpt->lock = lock; + gpt->hw.init = &init; + + init.name = name; + init.ops = &clk_gpt_ops; + init.flags = flags; + init.parent_names = &parent_name; + init.num_parents = 1; + + clk = clk_register(NULL, &gpt->hw); + if (!IS_ERR_OR_NULL(clk)) + return clk; + + pr_err("clk register failed\n"); + kfree(gpt); + + return NULL; +} diff --git a/drivers/clk/spear/clk.h b/drivers/clk/spear/clk.h index ac9030b..3321c46 100644 --- a/drivers/clk/spear/clk.h +++ b/drivers/clk/spear/clk.h @@ -68,6 +68,20 @@ struct clk_frac { spinlock_t *lock; }; +/* GPT clk */ +struct gpt_rate_tbl { + u16 mscale; + u16 nscale; +}; + +struct clk_gpt { + struct clk_hw hw; + void __iomem *reg; + struct gpt_rate_tbl *rtbl; + u8 rtbl_cnt; + spinlock_t *lock; +}; + /* VCO-PLL clk */ struct pll_rate_tbl { u8 mode; @@ -103,6 +117,9 @@ struct clk *clk_register_aux(const char *aux_name, const char *gate_name, struct clk *clk_register_frac(const char *name, const char *parent_name, unsigned long flags, void __iomem *reg, struct frac_rate_tbl *rtbl, u8 rtbl_cnt, spinlock_t *lock); +struct clk *clk_register_gpt(const char *name, const char *parent_name, unsigned + long flags, void __iomem *reg, struct gpt_rate_tbl *rtbl, u8 + rtbl_cnt, spinlock_t *lock); struct clk *clk_register_vco_pll(const char *vco_name, const char *pll_name, const char *vco_gate_name, const char *parent_name, unsigned long flags, void __iomem *mode_reg, void __iomem -- cgit v1.1 From 5df33a62c4a028d6fc7f2dcc159827d09b7334b8 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 10 Apr 2012 09:02:35 +0530 Subject: SPEAr: Switch to common clock framework SPEAr SoCs used its own clock framework since now. From now on they will move to use common clock framework. This patch updates existing SPEAr machine support to adapt for common clock framework. Signed-off-by: Viresh Kumar Reviewed-by: Mike Turquette Acked-by: Arnd Bergmann --- drivers/clk/spear/Makefile | 3 + drivers/clk/spear/spear3xx_clock.c | 612 +++++++++++++++++++++++++++++++++++++ drivers/clk/spear/spear6xx_clock.c | 342 +++++++++++++++++++++ 3 files changed, 957 insertions(+) create mode 100644 drivers/clk/spear/spear3xx_clock.c create mode 100644 drivers/clk/spear/spear6xx_clock.c (limited to 'drivers/clk') diff --git a/drivers/clk/spear/Makefile b/drivers/clk/spear/Makefile index 9e64824..3358860 100644 --- a/drivers/clk/spear/Makefile +++ b/drivers/clk/spear/Makefile @@ -3,3 +3,6 @@ # obj-y += clk.o clk-aux-synth.o clk-frac-synth.o clk-gpt-synth.o clk-vco-pll.o + +obj-$(CONFIG_ARCH_SPEAR3XX) += spear3xx_clock.o +obj-$(CONFIG_ARCH_SPEAR6XX) += spear6xx_clock.o diff --git a/drivers/clk/spear/spear3xx_clock.c b/drivers/clk/spear/spear3xx_clock.c new file mode 100644 index 0000000..440bb3e --- /dev/null +++ b/drivers/clk/spear/spear3xx_clock.c @@ -0,0 +1,612 @@ +/* + * SPEAr3xx machines clock framework source file + * + * Copyright (C) 2012 ST Microelectronics + * Viresh Kumar + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "clk.h" + +static DEFINE_SPINLOCK(_lock); + +#define PLL1_CTR (MISC_BASE + 0x008) +#define PLL1_FRQ (MISC_BASE + 0x00C) +#define PLL2_CTR (MISC_BASE + 0x014) +#define PLL2_FRQ (MISC_BASE + 0x018) +#define PLL_CLK_CFG (MISC_BASE + 0x020) + /* PLL_CLK_CFG register masks */ + #define MCTR_CLK_SHIFT 28 + #define MCTR_CLK_MASK 3 + +#define CORE_CLK_CFG (MISC_BASE + 0x024) + /* CORE CLK CFG register masks */ + #define GEN_SYNTH2_3_CLK_SHIFT 18 + #define GEN_SYNTH2_3_CLK_MASK 1 + + #define HCLK_RATIO_SHIFT 10 + #define HCLK_RATIO_MASK 2 + #define PCLK_RATIO_SHIFT 8 + #define PCLK_RATIO_MASK 2 + +#define PERIP_CLK_CFG (MISC_BASE + 0x028) + /* PERIP_CLK_CFG register masks */ + #define UART_CLK_SHIFT 4 + #define UART_CLK_MASK 1 + #define FIRDA_CLK_SHIFT 5 + #define FIRDA_CLK_MASK 2 + #define GPT0_CLK_SHIFT 8 + #define GPT1_CLK_SHIFT 11 + #define GPT2_CLK_SHIFT 12 + #define GPT_CLK_MASK 1 + +#define PERIP1_CLK_ENB (MISC_BASE + 0x02C) + /* PERIP1_CLK_ENB register masks */ + #define UART_CLK_ENB 3 + #define SSP_CLK_ENB 5 + #define I2C_CLK_ENB 7 + #define JPEG_CLK_ENB 8 + #define FIRDA_CLK_ENB 10 + #define GPT1_CLK_ENB 11 + #define GPT2_CLK_ENB 12 + #define ADC_CLK_ENB 15 + #define RTC_CLK_ENB 17 + #define GPIO_CLK_ENB 18 + #define DMA_CLK_ENB 19 + #define SMI_CLK_ENB 21 + #define GMAC_CLK_ENB 23 + #define USBD_CLK_ENB 24 + #define USBH_CLK_ENB 25 + #define C3_CLK_ENB 31 + +#define RAS_CLK_ENB (MISC_BASE + 0x034) + #define RAS_AHB_CLK_ENB 0 + #define RAS_PLL1_CLK_ENB 1 + #define RAS_APB_CLK_ENB 2 + #define RAS_32K_CLK_ENB 3 + #define RAS_24M_CLK_ENB 4 + #define RAS_48M_CLK_ENB 5 + #define RAS_PLL2_CLK_ENB 7 + #define RAS_SYNT0_CLK_ENB 8 + #define RAS_SYNT1_CLK_ENB 9 + #define RAS_SYNT2_CLK_ENB 10 + #define RAS_SYNT3_CLK_ENB 11 + +#define PRSC0_CLK_CFG (MISC_BASE + 0x044) +#define PRSC1_CLK_CFG (MISC_BASE + 0x048) +#define PRSC2_CLK_CFG (MISC_BASE + 0x04C) +#define AMEM_CLK_CFG (MISC_BASE + 0x050) + #define AMEM_CLK_ENB 0 + +#define CLCD_CLK_SYNT (MISC_BASE + 0x05C) +#define FIRDA_CLK_SYNT (MISC_BASE + 0x060) +#define UART_CLK_SYNT (MISC_BASE + 0x064) +#define GMAC_CLK_SYNT (MISC_BASE + 0x068) +#define GEN0_CLK_SYNT (MISC_BASE + 0x06C) +#define GEN1_CLK_SYNT (MISC_BASE + 0x070) +#define GEN2_CLK_SYNT (MISC_BASE + 0x074) +#define GEN3_CLK_SYNT (MISC_BASE + 0x078) + +/* pll rate configuration table, in ascending order of rates */ +static struct pll_rate_tbl pll_rtbl[] = { + {.mode = 0, .m = 0x53, .n = 0x0C, .p = 0x1}, /* vco 332 & pll 166 MHz */ + {.mode = 0, .m = 0x85, .n = 0x0C, .p = 0x1}, /* vco 532 & pll 266 MHz */ + {.mode = 0, .m = 0xA6, .n = 0x0C, .p = 0x1}, /* vco 664 & pll 332 MHz */ +}; + +/* aux rate configuration table, in ascending order of rates */ +static struct aux_rate_tbl aux_rtbl[] = { + /* For PLL1 = 332 MHz */ + {.xscale = 2, .yscale = 27, .eq = 0}, /* 12.296 MHz */ + {.xscale = 2, .yscale = 8, .eq = 0}, /* 41.5 MHz */ + {.xscale = 2, .yscale = 4, .eq = 0}, /* 83 MHz */ + {.xscale = 1, .yscale = 2, .eq = 1}, /* 166 MHz */ +}; + +/* gpt rate configuration table, in ascending order of rates */ +static struct gpt_rate_tbl gpt_rtbl[] = { + /* For pll1 = 332 MHz */ + {.mscale = 4, .nscale = 0}, /* 41.5 MHz */ + {.mscale = 2, .nscale = 0}, /* 55.3 MHz */ + {.mscale = 1, .nscale = 0}, /* 83 MHz */ +}; + +/* clock parents */ +static const char *uart0_parents[] = { "pll3_48m_clk", "uart_synth_gate_clk", }; +static const char *firda_parents[] = { "pll3_48m_clk", "firda_synth_gate_clk", +}; +static const char *gpt0_parents[] = { "pll3_48m_clk", "gpt0_synth_clk", }; +static const char *gpt1_parents[] = { "pll3_48m_clk", "gpt1_synth_clk", }; +static const char *gpt2_parents[] = { "pll3_48m_clk", "gpt2_synth_clk", }; +static const char *gen2_3_parents[] = { "pll1_clk", "pll2_clk", }; +static const char *ddr_parents[] = { "ahb_clk", "ahbmult2_clk", "none", + "pll2_clk", }; + +#ifdef CONFIG_MACH_SPEAR300 +static void __init spear300_clk_init(void) +{ + struct clk *clk; + + clk = clk_register_fixed_factor(NULL, "clcd_clk", "ras_pll3_48m_clk", 0, + 1, 1); + clk_register_clkdev(clk, NULL, "60000000.clcd"); + + clk = clk_register_fixed_factor(NULL, "fsmc_clk", "ras_ahb_clk", 0, 1, + 1); + clk_register_clkdev(clk, NULL, "94000000.flash"); + + clk = clk_register_fixed_factor(NULL, "sdhci_clk", "ras_ahb_clk", 0, 1, + 1); + clk_register_clkdev(clk, NULL, "70000000.sdhci"); + + clk = clk_register_fixed_factor(NULL, "gpio1_clk", "ras_apb_clk", 0, 1, + 1); + clk_register_clkdev(clk, NULL, "a9000000.gpio"); + + clk = clk_register_fixed_factor(NULL, "kbd_clk", "ras_apb_clk", 0, 1, + 1); + clk_register_clkdev(clk, NULL, "a0000000.kbd"); +} +#endif + +/* array of all spear 310 clock lookups */ +#ifdef CONFIG_MACH_SPEAR310 +static void __init spear310_clk_init(void) +{ + struct clk *clk; + + clk = clk_register_fixed_factor(NULL, "emi_clk", "ras_ahb_clk", 0, 1, + 1); + clk_register_clkdev(clk, "emi", NULL); + + clk = clk_register_fixed_factor(NULL, "fsmc_clk", "ras_ahb_clk", 0, 1, + 1); + clk_register_clkdev(clk, NULL, "44000000.flash"); + + clk = clk_register_fixed_factor(NULL, "tdm_clk", "ras_ahb_clk", 0, 1, + 1); + clk_register_clkdev(clk, NULL, "tdm"); + + clk = clk_register_fixed_factor(NULL, "uart1_clk", "ras_apb_clk", 0, 1, + 1); + clk_register_clkdev(clk, NULL, "b2000000.serial"); + + clk = clk_register_fixed_factor(NULL, "uart2_clk", "ras_apb_clk", 0, 1, + 1); + clk_register_clkdev(clk, NULL, "b2080000.serial"); + + clk = clk_register_fixed_factor(NULL, "uart3_clk", "ras_apb_clk", 0, 1, + 1); + clk_register_clkdev(clk, NULL, "b2100000.serial"); + + clk = clk_register_fixed_factor(NULL, "uart4_clk", "ras_apb_clk", 0, 1, + 1); + clk_register_clkdev(clk, NULL, "b2180000.serial"); + + clk = clk_register_fixed_factor(NULL, "uart5_clk", "ras_apb_clk", 0, 1, + 1); + clk_register_clkdev(clk, NULL, "b2200000.serial"); +} +#endif + +/* array of all spear 320 clock lookups */ +#ifdef CONFIG_MACH_SPEAR320 + #define SMII_PCLK_SHIFT 18 + #define SMII_PCLK_MASK 2 + #define SMII_PCLK_VAL_PAD 0x0 + #define SMII_PCLK_VAL_PLL2 0x1 + #define SMII_PCLK_VAL_SYNTH0 0x2 + #define SDHCI_PCLK_SHIFT 15 + #define SDHCI_PCLK_MASK 1 + #define SDHCI_PCLK_VAL_48M 0x0 + #define SDHCI_PCLK_VAL_SYNTH3 0x1 + #define I2S_REF_PCLK_SHIFT 8 + #define I2S_REF_PCLK_MASK 1 + #define I2S_REF_PCLK_SYNTH_VAL 0x1 + #define I2S_REF_PCLK_PLL2_VAL 0x0 + #define UART1_PCLK_SHIFT 6 + #define UART1_PCLK_MASK 1 + #define SPEAR320_UARTX_PCLK_VAL_SYNTH1 0x0 + #define SPEAR320_UARTX_PCLK_VAL_APB 0x1 + +static const char *i2s_ref_parents[] = { "ras_pll2_clk", + "ras_gen2_synth_gate_clk", }; +static const char *sdhci_parents[] = { "ras_pll3_48m_clk", + "ras_gen3_synth_gate_clk", +}; +static const char *smii0_parents[] = { "smii_125m_pad", "ras_pll2_clk", + "ras_gen0_synth_gate_clk", }; +static const char *uartx_parents[] = { "ras_gen1_synth_gate_clk", "ras_apb_clk", +}; + +static void __init spear320_clk_init(void) +{ + struct clk *clk; + + clk = clk_register_fixed_rate(NULL, "smii_125m_pad_clk", NULL, + CLK_IS_ROOT, 125000000); + clk_register_clkdev(clk, "smii_125m_pad", NULL); + + clk = clk_register_fixed_factor(NULL, "clcd_clk", "ras_pll3_48m_clk", 0, + 1, 1); + clk_register_clkdev(clk, NULL, "90000000.clcd"); + + clk = clk_register_fixed_factor(NULL, "emi_clk", "ras_ahb_clk", 0, 1, + 1); + clk_register_clkdev(clk, "emi", NULL); + + clk = clk_register_fixed_factor(NULL, "fsmc_clk", "ras_ahb_clk", 0, 1, + 1); + clk_register_clkdev(clk, NULL, "4c000000.flash"); + + clk = clk_register_fixed_factor(NULL, "i2c1_clk", "ras_ahb_clk", 0, 1, + 1); + clk_register_clkdev(clk, NULL, "a7000000.i2c"); + + clk = clk_register_fixed_factor(NULL, "pwm_clk", "ras_ahb_clk", 0, 1, + 1); + clk_register_clkdev(clk, "pwm", NULL); + + clk = clk_register_fixed_factor(NULL, "ssp1_clk", "ras_ahb_clk", 0, 1, + 1); + clk_register_clkdev(clk, NULL, "a5000000.spi"); + + clk = clk_register_fixed_factor(NULL, "ssp2_clk", "ras_ahb_clk", 0, 1, + 1); + clk_register_clkdev(clk, NULL, "a6000000.spi"); + + clk = clk_register_fixed_factor(NULL, "can0_clk", "ras_apb_clk", 0, 1, + 1); + clk_register_clkdev(clk, NULL, "c_can_platform.0"); + + clk = clk_register_fixed_factor(NULL, "can1_clk", "ras_apb_clk", 0, 1, + 1); + clk_register_clkdev(clk, NULL, "c_can_platform.1"); + + clk = clk_register_fixed_factor(NULL, "i2s_clk", "ras_apb_clk", 0, 1, + 1); + clk_register_clkdev(clk, NULL, "i2s"); + + clk = clk_register_mux(NULL, "i2s_ref_clk", i2s_ref_parents, + ARRAY_SIZE(i2s_ref_parents), 0, SPEAR320_CONTROL_REG, + I2S_REF_PCLK_SHIFT, I2S_REF_PCLK_MASK, 0, &_lock); + clk_register_clkdev(clk, "i2s_ref_clk", NULL); + + clk = clk_register_fixed_factor(NULL, "i2s_sclk", "i2s_ref_clk", 0, 1, + 4); + clk_register_clkdev(clk, "i2s_sclk", NULL); + + clk = clk_register_mux(NULL, "rs485_clk", uartx_parents, + ARRAY_SIZE(uartx_parents), 0, SPEAR320_EXT_CTRL_REG, + SPEAR320_RS485_PCLK_SHIFT, SPEAR320_UARTX_PCLK_MASK, 0, + &_lock); + clk_register_clkdev(clk, NULL, "a9300000.serial"); + + clk = clk_register_mux(NULL, "sdhci_clk", sdhci_parents, + ARRAY_SIZE(sdhci_parents), 0, SPEAR320_CONTROL_REG, + SDHCI_PCLK_SHIFT, SDHCI_PCLK_MASK, 0, &_lock); + clk_register_clkdev(clk, NULL, "70000000.sdhci"); + + clk = clk_register_mux(NULL, "smii_pclk", smii0_parents, + ARRAY_SIZE(smii0_parents), 0, SPEAR320_CONTROL_REG, + SMII_PCLK_SHIFT, SMII_PCLK_MASK, 0, &_lock); + clk_register_clkdev(clk, NULL, "smii_pclk"); + + clk = clk_register_fixed_factor(NULL, "smii_clk", "smii_pclk", 0, 1, 1); + clk_register_clkdev(clk, NULL, "smii"); + + clk = clk_register_mux(NULL, "uart1_clk", uartx_parents, + ARRAY_SIZE(uartx_parents), 0, SPEAR320_CONTROL_REG, + UART1_PCLK_SHIFT, UART1_PCLK_MASK, 0, &_lock); + clk_register_clkdev(clk, NULL, "a3000000.serial"); + + clk = clk_register_mux(NULL, "uart2_clk", uartx_parents, + ARRAY_SIZE(uartx_parents), 0, SPEAR320_EXT_CTRL_REG, + SPEAR320_UART2_PCLK_SHIFT, SPEAR320_UARTX_PCLK_MASK, 0, + &_lock); + clk_register_clkdev(clk, NULL, "a4000000.serial"); + + clk = clk_register_mux(NULL, "uart3_clk", uartx_parents, + ARRAY_SIZE(uartx_parents), 0, SPEAR320_EXT_CTRL_REG, + SPEAR320_UART3_PCLK_SHIFT, SPEAR320_UARTX_PCLK_MASK, 0, + &_lock); + clk_register_clkdev(clk, NULL, "a9100000.serial"); + + clk = clk_register_mux(NULL, "uart4_clk", uartx_parents, + ARRAY_SIZE(uartx_parents), 0, SPEAR320_EXT_CTRL_REG, + SPEAR320_UART4_PCLK_SHIFT, SPEAR320_UARTX_PCLK_MASK, 0, + &_lock); + clk_register_clkdev(clk, NULL, "a9200000.serial"); + + clk = clk_register_mux(NULL, "uart5_clk", uartx_parents, + ARRAY_SIZE(uartx_parents), 0, SPEAR320_EXT_CTRL_REG, + SPEAR320_UART5_PCLK_SHIFT, SPEAR320_UARTX_PCLK_MASK, 0, + &_lock); + clk_register_clkdev(clk, NULL, "60000000.serial"); + + clk = clk_register_mux(NULL, "uart6_clk", uartx_parents, + ARRAY_SIZE(uartx_parents), 0, SPEAR320_EXT_CTRL_REG, + SPEAR320_UART6_PCLK_SHIFT, SPEAR320_UARTX_PCLK_MASK, 0, + &_lock); + clk_register_clkdev(clk, NULL, "60100000.serial"); +} +#endif + +void __init spear3xx_clk_init(void) +{ + struct clk *clk, *clk1; + + clk = clk_register_fixed_rate(NULL, "apb_pclk", NULL, CLK_IS_ROOT, 0); + clk_register_clkdev(clk, "apb_pclk", NULL); + + clk = clk_register_fixed_rate(NULL, "osc_32k_clk", NULL, CLK_IS_ROOT, + 32000); + clk_register_clkdev(clk, "osc_32k_clk", NULL); + + clk = clk_register_fixed_rate(NULL, "osc_24m_clk", NULL, CLK_IS_ROOT, + 24000000); + clk_register_clkdev(clk, "osc_24m_clk", NULL); + + /* clock derived from 32 KHz osc clk */ + clk = clk_register_gate(NULL, "rtc-spear", "osc_32k_clk", 0, + PERIP1_CLK_ENB, RTC_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "fc900000.rtc"); + + /* clock derived from 24 MHz osc clk */ + clk = clk_register_fixed_rate(NULL, "pll3_48m_clk", "osc_24m_clk", 0, + 48000000); + clk_register_clkdev(clk, "pll3_48m_clk", NULL); + + clk = clk_register_fixed_factor(NULL, "wdt_clk", "osc_24m_clk", 0, 1, + 1); + clk_register_clkdev(clk, NULL, "fc880000.wdt"); + + clk = clk_register_vco_pll("vco1_clk", "pll1_clk", NULL, + "osc_24m_clk", 0, PLL1_CTR, PLL1_FRQ, pll_rtbl, + ARRAY_SIZE(pll_rtbl), &_lock, &clk1, NULL); + clk_register_clkdev(clk, "vco1_clk", NULL); + clk_register_clkdev(clk1, "pll1_clk", NULL); + + clk = clk_register_vco_pll("vco2_clk", "pll2_clk", NULL, + "osc_24m_clk", 0, PLL2_CTR, PLL2_FRQ, pll_rtbl, + ARRAY_SIZE(pll_rtbl), &_lock, &clk1, NULL); + clk_register_clkdev(clk, "vco2_clk", NULL); + clk_register_clkdev(clk1, "pll2_clk", NULL); + + /* clock derived from pll1 clk */ + clk = clk_register_fixed_factor(NULL, "cpu_clk", "pll1_clk", 0, 1, 1); + clk_register_clkdev(clk, "cpu_clk", NULL); + + clk = clk_register_divider(NULL, "ahb_clk", "pll1_clk", + CLK_SET_RATE_PARENT, CORE_CLK_CFG, HCLK_RATIO_SHIFT, + HCLK_RATIO_MASK, 0, &_lock); + clk_register_clkdev(clk, "ahb_clk", NULL); + + clk = clk_register_aux("uart_synth_clk", "uart_synth_gate_clk", + "pll1_clk", 0, UART_CLK_SYNT, NULL, aux_rtbl, + ARRAY_SIZE(aux_rtbl), &_lock, &clk1); + clk_register_clkdev(clk, "uart_synth_clk", NULL); + clk_register_clkdev(clk1, "uart_synth_gate_clk", NULL); + + clk = clk_register_mux(NULL, "uart0_mux_clk", uart0_parents, + ARRAY_SIZE(uart0_parents), 0, PERIP_CLK_CFG, + UART_CLK_SHIFT, UART_CLK_MASK, 0, &_lock); + clk_register_clkdev(clk, "uart0_mux_clk", NULL); + + clk = clk_register_gate(NULL, "uart0", "uart0_mux_clk", 0, + PERIP1_CLK_ENB, UART_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "d0000000.serial"); + + clk = clk_register_aux("firda_synth_clk", "firda_synth_gate_clk", + "pll1_clk", 0, FIRDA_CLK_SYNT, NULL, aux_rtbl, + ARRAY_SIZE(aux_rtbl), &_lock, &clk1); + clk_register_clkdev(clk, "firda_synth_clk", NULL); + clk_register_clkdev(clk1, "firda_synth_gate_clk", NULL); + + clk = clk_register_mux(NULL, "firda_mux_clk", firda_parents, + ARRAY_SIZE(firda_parents), 0, PERIP_CLK_CFG, + FIRDA_CLK_SHIFT, FIRDA_CLK_MASK, 0, &_lock); + clk_register_clkdev(clk, "firda_mux_clk", NULL); + + clk = clk_register_gate(NULL, "firda_clk", "firda_mux_clk", 0, + PERIP1_CLK_ENB, FIRDA_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "firda"); + + /* gpt clocks */ + clk_register_gpt("gpt0_synth_clk", "pll1_clk", 0, PRSC0_CLK_CFG, + gpt_rtbl, ARRAY_SIZE(gpt_rtbl), &_lock); + clk = clk_register_mux(NULL, "gpt0_clk", gpt0_parents, + ARRAY_SIZE(gpt0_parents), 0, PERIP_CLK_CFG, + GPT0_CLK_SHIFT, GPT_CLK_MASK, 0, &_lock); + clk_register_clkdev(clk, NULL, "gpt0"); + + clk_register_gpt("gpt1_synth_clk", "pll1_clk", 0, PRSC1_CLK_CFG, + gpt_rtbl, ARRAY_SIZE(gpt_rtbl), &_lock); + clk = clk_register_mux(NULL, "gpt1_mux_clk", gpt1_parents, + ARRAY_SIZE(gpt1_parents), 0, PERIP_CLK_CFG, + GPT1_CLK_SHIFT, GPT_CLK_MASK, 0, &_lock); + clk_register_clkdev(clk, "gpt1_mux_clk", NULL); + clk = clk_register_gate(NULL, "gpt1_clk", "gpt1_mux_clk", 0, + PERIP1_CLK_ENB, GPT1_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "gpt1"); + + clk_register_gpt("gpt2_synth_clk", "pll1_clk", 0, PRSC2_CLK_CFG, + gpt_rtbl, ARRAY_SIZE(gpt_rtbl), &_lock); + clk = clk_register_mux(NULL, "gpt2_mux_clk", gpt2_parents, + ARRAY_SIZE(gpt2_parents), 0, PERIP_CLK_CFG, + GPT2_CLK_SHIFT, GPT_CLK_MASK, 0, &_lock); + clk_register_clkdev(clk, "gpt2_mux_clk", NULL); + clk = clk_register_gate(NULL, "gpt2_clk", "gpt2_mux_clk", 0, + PERIP1_CLK_ENB, GPT2_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "gpt2"); + + /* general synths clocks */ + clk = clk_register_aux("gen0_synth_clk", "gen0_synth_gate_clk", + "pll1_clk", 0, GEN0_CLK_SYNT, NULL, aux_rtbl, + ARRAY_SIZE(aux_rtbl), &_lock, &clk1); + clk_register_clkdev(clk, "gen0_synth_clk", NULL); + clk_register_clkdev(clk1, "gen0_synth_gate_clk", NULL); + + clk = clk_register_aux("gen1_synth_clk", "gen1_synth_gate_clk", + "pll1_clk", 0, GEN1_CLK_SYNT, NULL, aux_rtbl, + ARRAY_SIZE(aux_rtbl), &_lock, &clk1); + clk_register_clkdev(clk, "gen1_synth_clk", NULL); + clk_register_clkdev(clk1, "gen1_synth_gate_clk", NULL); + + clk = clk_register_mux(NULL, "gen2_3_parent_clk", gen2_3_parents, + ARRAY_SIZE(gen2_3_parents), 0, CORE_CLK_CFG, + GEN_SYNTH2_3_CLK_SHIFT, GEN_SYNTH2_3_CLK_MASK, 0, + &_lock); + clk_register_clkdev(clk, "gen2_3_parent_clk", NULL); + + clk = clk_register_aux("gen2_synth_clk", "gen2_synth_gate_clk", + "gen2_3_parent_clk", 0, GEN2_CLK_SYNT, NULL, aux_rtbl, + ARRAY_SIZE(aux_rtbl), &_lock, &clk1); + clk_register_clkdev(clk, "gen2_synth_clk", NULL); + clk_register_clkdev(clk1, "gen2_synth_gate_clk", NULL); + + clk = clk_register_aux("gen3_synth_clk", "gen3_synth_gate_clk", + "gen2_3_parent_clk", 0, GEN3_CLK_SYNT, NULL, aux_rtbl, + ARRAY_SIZE(aux_rtbl), &_lock, &clk1); + clk_register_clkdev(clk, "gen3_synth_clk", NULL); + clk_register_clkdev(clk1, "gen3_synth_gate_clk", NULL); + + /* clock derived from pll3 clk */ + clk = clk_register_gate(NULL, "usbh_clk", "pll3_48m_clk", 0, + PERIP1_CLK_ENB, USBH_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, "usbh_clk", NULL); + + clk = clk_register_fixed_factor(NULL, "usbh.0_clk", "usbh_clk", 0, 1, + 1); + clk_register_clkdev(clk, "usbh.0_clk", NULL); + + clk = clk_register_fixed_factor(NULL, "usbh.1_clk", "usbh_clk", 0, 1, + 1); + clk_register_clkdev(clk, "usbh.1_clk", NULL); + + clk = clk_register_gate(NULL, "usbd_clk", "pll3_48m_clk", 0, + PERIP1_CLK_ENB, USBD_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "designware_udc"); + + /* clock derived from ahb clk */ + clk = clk_register_fixed_factor(NULL, "ahbmult2_clk", "ahb_clk", 0, 2, + 1); + clk_register_clkdev(clk, "ahbmult2_clk", NULL); + + clk = clk_register_mux(NULL, "ddr_clk", ddr_parents, + ARRAY_SIZE(ddr_parents), 0, PLL_CLK_CFG, MCTR_CLK_SHIFT, + MCTR_CLK_MASK, 0, &_lock); + clk_register_clkdev(clk, "ddr_clk", NULL); + + clk = clk_register_divider(NULL, "apb_clk", "ahb_clk", + CLK_SET_RATE_PARENT, CORE_CLK_CFG, PCLK_RATIO_SHIFT, + PCLK_RATIO_MASK, 0, &_lock); + clk_register_clkdev(clk, "apb_clk", NULL); + + clk = clk_register_gate(NULL, "amem_clk", "ahb_clk", 0, AMEM_CLK_CFG, + AMEM_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, "amem_clk", NULL); + + clk = clk_register_gate(NULL, "c3_clk", "ahb_clk", 0, PERIP1_CLK_ENB, + C3_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "c3_clk"); + + clk = clk_register_gate(NULL, "dma_clk", "ahb_clk", 0, PERIP1_CLK_ENB, + DMA_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "fc400000.dma"); + + clk = clk_register_gate(NULL, "gmac_clk", "ahb_clk", 0, PERIP1_CLK_ENB, + GMAC_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "e0800000.eth"); + + clk = clk_register_gate(NULL, "i2c0_clk", "ahb_clk", 0, PERIP1_CLK_ENB, + I2C_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "d0180000.i2c"); + + clk = clk_register_gate(NULL, "jpeg_clk", "ahb_clk", 0, PERIP1_CLK_ENB, + JPEG_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "jpeg"); + + clk = clk_register_gate(NULL, "smi_clk", "ahb_clk", 0, PERIP1_CLK_ENB, + SMI_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "fc000000.flash"); + + /* clock derived from apb clk */ + clk = clk_register_gate(NULL, "adc_clk", "apb_clk", 0, PERIP1_CLK_ENB, + ADC_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "adc"); + + clk = clk_register_gate(NULL, "gpio0_clk", "apb_clk", 0, PERIP1_CLK_ENB, + GPIO_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "fc980000.gpio"); + + clk = clk_register_gate(NULL, "ssp0_clk", "apb_clk", 0, PERIP1_CLK_ENB, + SSP_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "d0100000.spi"); + + /* RAS clk enable */ + clk = clk_register_gate(NULL, "ras_ahb_clk", "ahb_clk", 0, RAS_CLK_ENB, + RAS_AHB_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, "ras_ahb_clk", NULL); + + clk = clk_register_gate(NULL, "ras_apb_clk", "apb_clk", 0, RAS_CLK_ENB, + RAS_APB_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, "ras_apb_clk", NULL); + + clk = clk_register_gate(NULL, "ras_32k_clk", "osc_32k_clk", 0, + RAS_CLK_ENB, RAS_32K_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, "ras_32k_clk", NULL); + + clk = clk_register_gate(NULL, "ras_24m_clk", "osc_24m_clk", 0, + RAS_CLK_ENB, RAS_24M_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, "ras_24m_clk", NULL); + + clk = clk_register_gate(NULL, "ras_pll1_clk", "pll1_clk", 0, + RAS_CLK_ENB, RAS_PLL1_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, "ras_pll1_clk", NULL); + + clk = clk_register_gate(NULL, "ras_pll2_clk", "pll2_clk", 0, + RAS_CLK_ENB, RAS_PLL2_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, "ras_pll2_clk", NULL); + + clk = clk_register_gate(NULL, "ras_pll3_48m_clk", "pll3_48m_clk", 0, + RAS_CLK_ENB, RAS_48M_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, "ras_pll3_48m_clk", NULL); + + clk = clk_register_gate(NULL, "ras_gen0_synth_gate_clk", + "gen0_synth_gate_clk", 0, RAS_CLK_ENB, + RAS_SYNT0_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, "ras_gen0_synth_gate_clk", NULL); + + clk = clk_register_gate(NULL, "ras_gen1_synth_gate_clk", + "gen1_synth_gate_clk", 0, RAS_CLK_ENB, + RAS_SYNT1_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, "ras_gen1_synth_gate_clk", NULL); + + clk = clk_register_gate(NULL, "ras_gen2_synth_gate_clk", + "gen2_synth_gate_clk", 0, RAS_CLK_ENB, + RAS_SYNT2_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, "ras_gen2_synth_gate_clk", NULL); + + clk = clk_register_gate(NULL, "ras_gen3_synth_gate_clk", + "gen3_synth_gate_clk", 0, RAS_CLK_ENB, + RAS_SYNT3_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, "ras_gen3_synth_gate_clk", NULL); + + if (of_machine_is_compatible("st,spear300")) + spear300_clk_init(); + else if (of_machine_is_compatible("st,spear310")) + spear310_clk_init(); + else if (of_machine_is_compatible("st,spear320")) + spear320_clk_init(); +} diff --git a/drivers/clk/spear/spear6xx_clock.c b/drivers/clk/spear/spear6xx_clock.c new file mode 100644 index 0000000..f9a20b3 --- /dev/null +++ b/drivers/clk/spear/spear6xx_clock.c @@ -0,0 +1,342 @@ +/* + * SPEAr6xx machines clock framework source file + * + * Copyright (C) 2012 ST Microelectronics + * Viresh Kumar + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include "clk.h" + +static DEFINE_SPINLOCK(_lock); + +#define PLL1_CTR (MISC_BASE + 0x008) +#define PLL1_FRQ (MISC_BASE + 0x00C) +#define PLL2_CTR (MISC_BASE + 0x014) +#define PLL2_FRQ (MISC_BASE + 0x018) +#define PLL_CLK_CFG (MISC_BASE + 0x020) + /* PLL_CLK_CFG register masks */ + #define MCTR_CLK_SHIFT 28 + #define MCTR_CLK_MASK 3 + +#define CORE_CLK_CFG (MISC_BASE + 0x024) + /* CORE CLK CFG register masks */ + #define HCLK_RATIO_SHIFT 10 + #define HCLK_RATIO_MASK 2 + #define PCLK_RATIO_SHIFT 8 + #define PCLK_RATIO_MASK 2 + +#define PERIP_CLK_CFG (MISC_BASE + 0x028) + /* PERIP_CLK_CFG register masks */ + #define CLCD_CLK_SHIFT 2 + #define CLCD_CLK_MASK 2 + #define UART_CLK_SHIFT 4 + #define UART_CLK_MASK 1 + #define FIRDA_CLK_SHIFT 5 + #define FIRDA_CLK_MASK 2 + #define GPT0_CLK_SHIFT 8 + #define GPT1_CLK_SHIFT 10 + #define GPT2_CLK_SHIFT 11 + #define GPT3_CLK_SHIFT 12 + #define GPT_CLK_MASK 1 + +#define PERIP1_CLK_ENB (MISC_BASE + 0x02C) + /* PERIP1_CLK_ENB register masks */ + #define UART0_CLK_ENB 3 + #define UART1_CLK_ENB 4 + #define SSP0_CLK_ENB 5 + #define SSP1_CLK_ENB 6 + #define I2C_CLK_ENB 7 + #define JPEG_CLK_ENB 8 + #define FSMC_CLK_ENB 9 + #define FIRDA_CLK_ENB 10 + #define GPT2_CLK_ENB 11 + #define GPT3_CLK_ENB 12 + #define GPIO2_CLK_ENB 13 + #define SSP2_CLK_ENB 14 + #define ADC_CLK_ENB 15 + #define GPT1_CLK_ENB 11 + #define RTC_CLK_ENB 17 + #define GPIO1_CLK_ENB 18 + #define DMA_CLK_ENB 19 + #define SMI_CLK_ENB 21 + #define CLCD_CLK_ENB 22 + #define GMAC_CLK_ENB 23 + #define USBD_CLK_ENB 24 + #define USBH0_CLK_ENB 25 + #define USBH1_CLK_ENB 26 + +#define PRSC0_CLK_CFG (MISC_BASE + 0x044) +#define PRSC1_CLK_CFG (MISC_BASE + 0x048) +#define PRSC2_CLK_CFG (MISC_BASE + 0x04C) + +#define CLCD_CLK_SYNT (MISC_BASE + 0x05C) +#define FIRDA_CLK_SYNT (MISC_BASE + 0x060) +#define UART_CLK_SYNT (MISC_BASE + 0x064) + +/* vco rate configuration table, in ascending order of rates */ +static struct pll_rate_tbl pll_rtbl[] = { + {.mode = 0, .m = 0x53, .n = 0x0F, .p = 0x1}, /* vco 332 & pll 166 MHz */ + {.mode = 0, .m = 0x85, .n = 0x0F, .p = 0x1}, /* vco 532 & pll 266 MHz */ + {.mode = 0, .m = 0xA6, .n = 0x0F, .p = 0x1}, /* vco 664 & pll 332 MHz */ +}; + +/* aux rate configuration table, in ascending order of rates */ +static struct aux_rate_tbl aux_rtbl[] = { + /* For PLL1 = 332 MHz */ + {.xscale = 2, .yscale = 8, .eq = 0}, /* 41.5 MHz */ + {.xscale = 2, .yscale = 4, .eq = 0}, /* 83 MHz */ + {.xscale = 1, .yscale = 2, .eq = 1}, /* 166 MHz */ +}; + +static const char *clcd_parents[] = { "pll3_48m_clk", "clcd_synth_gate_clk", }; +static const char *firda_parents[] = { "pll3_48m_clk", "firda_synth_gate_clk", +}; +static const char *uart_parents[] = { "pll3_48m_clk", "uart_synth_gate_clk", }; +static const char *gpt0_1_parents[] = { "pll3_48m_clk", "gpt0_1_synth_clk", }; +static const char *gpt2_parents[] = { "pll3_48m_clk", "gpt2_synth_clk", }; +static const char *gpt3_parents[] = { "pll3_48m_clk", "gpt3_synth_clk", }; +static const char *ddr_parents[] = { "ahb_clk", "ahbmult2_clk", "none", + "pll2_clk", }; + +/* gpt rate configuration table, in ascending order of rates */ +static struct gpt_rate_tbl gpt_rtbl[] = { + /* For pll1 = 332 MHz */ + {.mscale = 4, .nscale = 0}, /* 41.5 MHz */ + {.mscale = 2, .nscale = 0}, /* 55.3 MHz */ + {.mscale = 1, .nscale = 0}, /* 83 MHz */ +}; + +void __init spear6xx_clk_init(void) +{ + struct clk *clk, *clk1; + + clk = clk_register_fixed_rate(NULL, "apb_pclk", NULL, CLK_IS_ROOT, 0); + clk_register_clkdev(clk, "apb_pclk", NULL); + + clk = clk_register_fixed_rate(NULL, "osc_32k_clk", NULL, CLK_IS_ROOT, + 32000); + clk_register_clkdev(clk, "osc_32k_clk", NULL); + + clk = clk_register_fixed_rate(NULL, "osc_30m_clk", NULL, CLK_IS_ROOT, + 30000000); + clk_register_clkdev(clk, "osc_30m_clk", NULL); + + /* clock derived from 32 KHz osc clk */ + clk = clk_register_gate(NULL, "rtc_spear", "osc_32k_clk", 0, + PERIP1_CLK_ENB, RTC_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "rtc-spear"); + + /* clock derived from 30 MHz osc clk */ + clk = clk_register_fixed_rate(NULL, "pll3_48m_clk", "osc_24m_clk", 0, + 48000000); + clk_register_clkdev(clk, "pll3_48m_clk", NULL); + + clk = clk_register_vco_pll("vco1_clk", "pll1_clk", NULL, "osc_30m_clk", + 0, PLL1_CTR, PLL1_FRQ, pll_rtbl, ARRAY_SIZE(pll_rtbl), + &_lock, &clk1, NULL); + clk_register_clkdev(clk, "vco1_clk", NULL); + clk_register_clkdev(clk1, "pll1_clk", NULL); + + clk = clk_register_vco_pll("vco2_clk", "pll2_clk", NULL, + "osc_30m_clk", 0, PLL2_CTR, PLL2_FRQ, pll_rtbl, + ARRAY_SIZE(pll_rtbl), &_lock, &clk1, NULL); + clk_register_clkdev(clk, "vco2_clk", NULL); + clk_register_clkdev(clk1, "pll2_clk", NULL); + + clk = clk_register_fixed_factor(NULL, "wdt_clk", "osc_30m_clk", 0, 1, + 1); + clk_register_clkdev(clk, NULL, "wdt"); + + /* clock derived from pll1 clk */ + clk = clk_register_fixed_factor(NULL, "cpu_clk", "pll1_clk", 0, 1, 1); + clk_register_clkdev(clk, "cpu_clk", NULL); + + clk = clk_register_divider(NULL, "ahb_clk", "pll1_clk", + CLK_SET_RATE_PARENT, CORE_CLK_CFG, HCLK_RATIO_SHIFT, + HCLK_RATIO_MASK, 0, &_lock); + clk_register_clkdev(clk, "ahb_clk", NULL); + + clk = clk_register_aux("uart_synth_clk", "uart_synth_gate_clk", + "pll1_clk", 0, UART_CLK_SYNT, NULL, aux_rtbl, + ARRAY_SIZE(aux_rtbl), &_lock, &clk1); + clk_register_clkdev(clk, "uart_synth_clk", NULL); + clk_register_clkdev(clk1, "uart_synth_gate_clk", NULL); + + clk = clk_register_mux(NULL, "uart_mux_clk", uart_parents, + ARRAY_SIZE(uart_parents), 0, PERIP_CLK_CFG, + UART_CLK_SHIFT, UART_CLK_MASK, 0, &_lock); + clk_register_clkdev(clk, "uart_mux_clk", NULL); + + clk = clk_register_gate(NULL, "uart0", "uart_mux_clk", 0, + PERIP1_CLK_ENB, UART0_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "d0000000.serial"); + + clk = clk_register_gate(NULL, "uart1", "uart_mux_clk", 0, + PERIP1_CLK_ENB, UART1_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "d0080000.serial"); + + clk = clk_register_aux("firda_synth_clk", "firda_synth_gate_clk", + "pll1_clk", 0, FIRDA_CLK_SYNT, NULL, aux_rtbl, + ARRAY_SIZE(aux_rtbl), &_lock, &clk1); + clk_register_clkdev(clk, "firda_synth_clk", NULL); + clk_register_clkdev(clk1, "firda_synth_gate_clk", NULL); + + clk = clk_register_mux(NULL, "firda_mux_clk", firda_parents, + ARRAY_SIZE(firda_parents), 0, PERIP_CLK_CFG, + FIRDA_CLK_SHIFT, FIRDA_CLK_MASK, 0, &_lock); + clk_register_clkdev(clk, "firda_mux_clk", NULL); + + clk = clk_register_gate(NULL, "firda_clk", "firda_mux_clk", 0, + PERIP1_CLK_ENB, FIRDA_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "firda"); + + clk = clk_register_aux("clcd_synth_clk", "clcd_synth_gate_clk", + "pll1_clk", 0, CLCD_CLK_SYNT, NULL, aux_rtbl, + ARRAY_SIZE(aux_rtbl), &_lock, &clk1); + clk_register_clkdev(clk, "clcd_synth_clk", NULL); + clk_register_clkdev(clk1, "clcd_synth_gate_clk", NULL); + + clk = clk_register_mux(NULL, "clcd_mux_clk", clcd_parents, + ARRAY_SIZE(clcd_parents), 0, PERIP_CLK_CFG, + CLCD_CLK_SHIFT, CLCD_CLK_MASK, 0, &_lock); + clk_register_clkdev(clk, "clcd_mux_clk", NULL); + + clk = clk_register_gate(NULL, "clcd_clk", "clcd_mux_clk", 0, + PERIP1_CLK_ENB, CLCD_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "clcd"); + + /* gpt clocks */ + clk = clk_register_gpt("gpt0_1_synth_clk", "pll1_clk", 0, PRSC0_CLK_CFG, + gpt_rtbl, ARRAY_SIZE(gpt_rtbl), &_lock); + clk_register_clkdev(clk, "gpt0_1_synth_clk", NULL); + + clk = clk_register_mux(NULL, "gpt0_mux_clk", gpt0_1_parents, + ARRAY_SIZE(gpt0_1_parents), 0, PERIP_CLK_CFG, + GPT0_CLK_SHIFT, GPT_CLK_MASK, 0, &_lock); + clk_register_clkdev(clk, NULL, "gpt0"); + + clk = clk_register_mux(NULL, "gpt1_mux_clk", gpt0_1_parents, + ARRAY_SIZE(gpt0_1_parents), 0, PERIP_CLK_CFG, + GPT1_CLK_SHIFT, GPT_CLK_MASK, 0, &_lock); + clk_register_clkdev(clk, "gpt1_mux_clk", NULL); + + clk = clk_register_gate(NULL, "gpt1_clk", "gpt1_mux_clk", 0, + PERIP1_CLK_ENB, GPT1_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "gpt1"); + + clk = clk_register_gpt("gpt2_synth_clk", "pll1_clk", 0, PRSC1_CLK_CFG, + gpt_rtbl, ARRAY_SIZE(gpt_rtbl), &_lock); + clk_register_clkdev(clk, "gpt2_synth_clk", NULL); + + clk = clk_register_mux(NULL, "gpt2_mux_clk", gpt2_parents, + ARRAY_SIZE(gpt2_parents), 0, PERIP_CLK_CFG, + GPT2_CLK_SHIFT, GPT_CLK_MASK, 0, &_lock); + clk_register_clkdev(clk, "gpt2_mux_clk", NULL); + + clk = clk_register_gate(NULL, "gpt2_clk", "gpt2_mux_clk", 0, + PERIP1_CLK_ENB, GPT2_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "gpt2"); + + clk = clk_register_gpt("gpt3_synth_clk", "pll1_clk", 0, PRSC2_CLK_CFG, + gpt_rtbl, ARRAY_SIZE(gpt_rtbl), &_lock); + clk_register_clkdev(clk, "gpt3_synth_clk", NULL); + + clk = clk_register_mux(NULL, "gpt3_mux_clk", gpt3_parents, + ARRAY_SIZE(gpt3_parents), 0, PERIP_CLK_CFG, + GPT3_CLK_SHIFT, GPT_CLK_MASK, 0, &_lock); + clk_register_clkdev(clk, "gpt3_mux_clk", NULL); + + clk = clk_register_gate(NULL, "gpt3_clk", "gpt3_mux_clk", 0, + PERIP1_CLK_ENB, GPT3_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "gpt3"); + + /* clock derived from pll3 clk */ + clk = clk_register_gate(NULL, "usbh0_clk", "pll3_48m_clk", 0, + PERIP1_CLK_ENB, USBH0_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "usbh.0_clk"); + + clk = clk_register_gate(NULL, "usbh1_clk", "pll3_48m_clk", 0, + PERIP1_CLK_ENB, USBH1_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "usbh.1_clk"); + + clk = clk_register_gate(NULL, "usbd_clk", "pll3_48m_clk", 0, + PERIP1_CLK_ENB, USBD_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "designware_udc"); + + /* clock derived from ahb clk */ + clk = clk_register_fixed_factor(NULL, "ahbmult2_clk", "ahb_clk", 0, 2, + 1); + clk_register_clkdev(clk, "ahbmult2_clk", NULL); + + clk = clk_register_mux(NULL, "ddr_clk", ddr_parents, + ARRAY_SIZE(ddr_parents), + 0, PLL_CLK_CFG, MCTR_CLK_SHIFT, MCTR_CLK_MASK, 0, + &_lock); + clk_register_clkdev(clk, "ddr_clk", NULL); + + clk = clk_register_divider(NULL, "apb_clk", "ahb_clk", + CLK_SET_RATE_PARENT, CORE_CLK_CFG, PCLK_RATIO_SHIFT, + PCLK_RATIO_MASK, 0, &_lock); + clk_register_clkdev(clk, "apb_clk", NULL); + + clk = clk_register_gate(NULL, "dma_clk", "ahb_clk", 0, PERIP1_CLK_ENB, + DMA_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "fc400000.dma"); + + clk = clk_register_gate(NULL, "fsmc_clk", "ahb_clk", 0, PERIP1_CLK_ENB, + FSMC_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "d1800000.flash"); + + clk = clk_register_gate(NULL, "gmac_clk", "ahb_clk", 0, PERIP1_CLK_ENB, + GMAC_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "gmac"); + + clk = clk_register_gate(NULL, "i2c_clk", "ahb_clk", 0, PERIP1_CLK_ENB, + I2C_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "d0200000.i2c"); + + clk = clk_register_gate(NULL, "jpeg_clk", "ahb_clk", 0, PERIP1_CLK_ENB, + JPEG_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "jpeg"); + + clk = clk_register_gate(NULL, "smi_clk", "ahb_clk", 0, PERIP1_CLK_ENB, + SMI_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "fc000000.flash"); + + /* clock derived from apb clk */ + clk = clk_register_gate(NULL, "adc_clk", "apb_clk", 0, PERIP1_CLK_ENB, + ADC_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "adc"); + + clk = clk_register_fixed_factor(NULL, "gpio0_clk", "apb_clk", 0, 1, 1); + clk_register_clkdev(clk, NULL, "f0100000.gpio"); + + clk = clk_register_gate(NULL, "gpio1_clk", "apb_clk", 0, PERIP1_CLK_ENB, + GPIO1_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "fc980000.gpio"); + + clk = clk_register_gate(NULL, "gpio2_clk", "apb_clk", 0, PERIP1_CLK_ENB, + GPIO2_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "d8100000.gpio"); + + clk = clk_register_gate(NULL, "ssp0_clk", "apb_clk", 0, PERIP1_CLK_ENB, + SSP0_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "ssp-pl022.0"); + + clk = clk_register_gate(NULL, "ssp1_clk", "apb_clk", 0, PERIP1_CLK_ENB, + SSP1_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "ssp-pl022.1"); + + clk = clk_register_gate(NULL, "ssp2_clk", "apb_clk", 0, PERIP1_CLK_ENB, + SSP2_CLK_ENB, 0, &_lock); + clk_register_clkdev(clk, NULL, "ssp-pl022.2"); +} -- cgit v1.1