diff options
author | Michael Turquette <mturquette@linaro.org> | 2014-11-13 17:20:39 -0800 |
---|---|---|
committer | Michael Turquette <mturquette@linaro.org> | 2014-11-13 17:20:39 -0800 |
commit | 280da705802c7606e5b943f885390bbbf1d8049d (patch) | |
tree | a97e1560bb2a5c75fcfb04146dfbd7ecfcd32d27 /drivers/clk | |
parent | baeb0d9b98c3450e22f8f8e4a6619b8b1d5106ff (diff) | |
parent | d41ef54027cc6e0697e5b77d7da393998bed7ee4 (diff) | |
download | op-kernel-dev-280da705802c7606e5b943f885390bbbf1d8049d.zip op-kernel-dev-280da705802c7606e5b943f885390bbbf1d8049d.tar.gz |
Merge branch 'clk-next-mmp' into clk-next
Diffstat (limited to 'drivers/clk')
-rw-r--r-- | drivers/clk/mmp/Makefile | 7 | ||||
-rw-r--r-- | drivers/clk/mmp/clk-frac.c | 74 | ||||
-rw-r--r-- | drivers/clk/mmp/clk-gate.c | 133 | ||||
-rw-r--r-- | drivers/clk/mmp/clk-mix.c | 513 | ||||
-rw-r--r-- | drivers/clk/mmp/clk-mmp2.c | 6 | ||||
-rw-r--r-- | drivers/clk/mmp/clk-of-mmp2.c | 334 | ||||
-rw-r--r-- | drivers/clk/mmp/clk-of-pxa168.c | 279 | ||||
-rw-r--r-- | drivers/clk/mmp/clk-of-pxa910.c | 301 | ||||
-rw-r--r-- | drivers/clk/mmp/clk-pxa168.c | 6 | ||||
-rw-r--r-- | drivers/clk/mmp/clk-pxa910.c | 6 | ||||
-rw-r--r-- | drivers/clk/mmp/clk.c | 192 | ||||
-rw-r--r-- | drivers/clk/mmp/clk.h | 226 | ||||
-rw-r--r-- | drivers/clk/mmp/reset.c | 99 | ||||
-rw-r--r-- | drivers/clk/mmp/reset.h | 31 |
14 files changed, 2170 insertions, 37 deletions
diff --git a/drivers/clk/mmp/Makefile b/drivers/clk/mmp/Makefile index 392d780..3caaf7c 100644 --- a/drivers/clk/mmp/Makefile +++ b/drivers/clk/mmp/Makefile @@ -2,7 +2,12 @@ # Makefile for mmp specific clk # -obj-y += clk-apbc.o clk-apmu.o clk-frac.o +obj-y += clk-apbc.o clk-apmu.o clk-frac.o clk-mix.o clk-gate.o clk.o + +obj-$(CONFIG_RESET_CONTROLLER) += reset.o + +obj-$(CONFIG_MACH_MMP_DT) += clk-of-pxa168.o clk-of-pxa910.o +obj-$(CONFIG_MACH_MMP2_DT) += clk-of-mmp2.o obj-$(CONFIG_CPU_PXA168) += clk-pxa168.o obj-$(CONFIG_CPU_PXA910) += clk-pxa910.o diff --git a/drivers/clk/mmp/clk-frac.c b/drivers/clk/mmp/clk-frac.c index 23a56f5..eeba52c 100644 --- a/drivers/clk/mmp/clk-frac.c +++ b/drivers/clk/mmp/clk-frac.c @@ -22,19 +22,12 @@ * numerator/denominator = Fin / (Fout * factor) */ -#define to_clk_factor(hw) container_of(hw, struct clk_factor, hw) -struct clk_factor { - struct clk_hw hw; - void __iomem *base; - struct clk_factor_masks *masks; - struct clk_factor_tbl *ftbl; - unsigned int ftbl_cnt; -}; +#define to_clk_factor(hw) container_of(hw, struct mmp_clk_factor, hw) static long clk_factor_round_rate(struct clk_hw *hw, unsigned long drate, unsigned long *prate) { - struct clk_factor *factor = to_clk_factor(hw); + struct mmp_clk_factor *factor = to_clk_factor(hw); unsigned long rate = 0, prev_rate; int i; @@ -58,8 +51,8 @@ static long clk_factor_round_rate(struct clk_hw *hw, unsigned long drate, static unsigned long clk_factor_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { - struct clk_factor *factor = to_clk_factor(hw); - struct clk_factor_masks *masks = factor->masks; + struct mmp_clk_factor *factor = to_clk_factor(hw); + struct mmp_clk_factor_masks *masks = factor->masks; unsigned int val, num, den; val = readl_relaxed(factor->base); @@ -81,11 +74,12 @@ static unsigned long clk_factor_recalc_rate(struct clk_hw *hw, static int clk_factor_set_rate(struct clk_hw *hw, unsigned long drate, unsigned long prate) { - struct clk_factor *factor = to_clk_factor(hw); - struct clk_factor_masks *masks = factor->masks; + struct mmp_clk_factor *factor = to_clk_factor(hw); + struct mmp_clk_factor_masks *masks = factor->masks; int i; unsigned long val; unsigned long prev_rate, rate = 0; + unsigned long flags = 0; for (i = 0; i < factor->ftbl_cnt; i++) { prev_rate = rate; @@ -97,6 +91,9 @@ static int clk_factor_set_rate(struct clk_hw *hw, unsigned long drate, if (i > 0) i--; + if (factor->lock) + spin_lock_irqsave(factor->lock, flags); + val = readl_relaxed(factor->base); val &= ~(masks->num_mask << masks->num_shift); @@ -107,21 +104,65 @@ static int clk_factor_set_rate(struct clk_hw *hw, unsigned long drate, writel_relaxed(val, factor->base); + if (factor->lock) + spin_unlock_irqrestore(factor->lock, flags); + return 0; } +void clk_factor_init(struct clk_hw *hw) +{ + struct mmp_clk_factor *factor = to_clk_factor(hw); + struct mmp_clk_factor_masks *masks = factor->masks; + u32 val, num, den; + int i; + unsigned long flags = 0; + + if (factor->lock) + spin_lock_irqsave(factor->lock, flags); + + val = readl(factor->base); + + /* calculate numerator */ + num = (val >> masks->num_shift) & masks->num_mask; + + /* calculate denominator */ + den = (val >> masks->den_shift) & masks->den_mask; + + for (i = 0; i < factor->ftbl_cnt; i++) + if (den == factor->ftbl[i].den && num == factor->ftbl[i].num) + break; + + if (i >= factor->ftbl_cnt) { + val &= ~(masks->num_mask << masks->num_shift); + val |= (factor->ftbl[0].num & masks->num_mask) << + masks->num_shift; + + val &= ~(masks->den_mask << masks->den_shift); + val |= (factor->ftbl[0].den & masks->den_mask) << + masks->den_shift; + + writel(val, factor->base); + } + + if (factor->lock) + spin_unlock_irqrestore(factor->lock, flags); +} + static struct clk_ops clk_factor_ops = { .recalc_rate = clk_factor_recalc_rate, .round_rate = clk_factor_round_rate, .set_rate = clk_factor_set_rate, + .init = clk_factor_init, }; struct clk *mmp_clk_register_factor(const char *name, const char *parent_name, unsigned long flags, void __iomem *base, - struct clk_factor_masks *masks, struct clk_factor_tbl *ftbl, - unsigned int ftbl_cnt) + struct mmp_clk_factor_masks *masks, + struct mmp_clk_factor_tbl *ftbl, + unsigned int ftbl_cnt, spinlock_t *lock) { - struct clk_factor *factor; + struct mmp_clk_factor *factor; struct clk_init_data init; struct clk *clk; @@ -142,6 +183,7 @@ struct clk *mmp_clk_register_factor(const char *name, const char *parent_name, factor->ftbl = ftbl; factor->ftbl_cnt = ftbl_cnt; factor->hw.init = &init; + factor->lock = lock; init.name = name; init.ops = &clk_factor_ops; diff --git a/drivers/clk/mmp/clk-gate.c b/drivers/clk/mmp/clk-gate.c new file mode 100644 index 0000000..adbd9d6 --- /dev/null +++ b/drivers/clk/mmp/clk-gate.c @@ -0,0 +1,133 @@ +/* + * mmp gate clock operation source file + * + * Copyright (C) 2014 Marvell + * Chao Xie <chao.xie@marvell.com> + * + * 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 <linux/clk-provider.h> +#include <linux/slab.h> +#include <linux/io.h> +#include <linux/err.h> +#include <linux/delay.h> + +#include "clk.h" + +/* + * Some clocks will have mutiple bits to enable the clocks, and + * the bits to disable the clock is not same as enabling bits. + */ + +#define to_clk_mmp_gate(hw) container_of(hw, struct mmp_clk_gate, hw) + +static int mmp_clk_gate_enable(struct clk_hw *hw) +{ + struct mmp_clk_gate *gate = to_clk_mmp_gate(hw); + struct clk *clk = hw->clk; + unsigned long flags = 0; + unsigned long rate; + u32 tmp; + + if (gate->lock) + spin_lock_irqsave(gate->lock, flags); + + tmp = readl(gate->reg); + tmp &= ~gate->mask; + tmp |= gate->val_enable; + writel(tmp, gate->reg); + + if (gate->lock) + spin_unlock_irqrestore(gate->lock, flags); + + if (gate->flags & MMP_CLK_GATE_NEED_DELAY) { + rate = __clk_get_rate(clk); + /* Need delay 2 cycles. */ + udelay(2000000/rate); + } + + return 0; +} + +static void mmp_clk_gate_disable(struct clk_hw *hw) +{ + struct mmp_clk_gate *gate = to_clk_mmp_gate(hw); + unsigned long flags = 0; + u32 tmp; + + if (gate->lock) + spin_lock_irqsave(gate->lock, flags); + + tmp = readl(gate->reg); + tmp &= ~gate->mask; + tmp |= gate->val_disable; + writel(tmp, gate->reg); + + if (gate->lock) + spin_unlock_irqrestore(gate->lock, flags); +} + +static int mmp_clk_gate_is_enabled(struct clk_hw *hw) +{ + struct mmp_clk_gate *gate = to_clk_mmp_gate(hw); + unsigned long flags = 0; + u32 tmp; + + if (gate->lock) + spin_lock_irqsave(gate->lock, flags); + + tmp = readl(gate->reg); + + if (gate->lock) + spin_unlock_irqrestore(gate->lock, flags); + + return (tmp & gate->mask) == gate->val_enable; +} + +const struct clk_ops mmp_clk_gate_ops = { + .enable = mmp_clk_gate_enable, + .disable = mmp_clk_gate_disable, + .is_enabled = mmp_clk_gate_is_enabled, +}; + +struct clk *mmp_clk_register_gate(struct device *dev, const char *name, + const char *parent_name, unsigned long flags, + void __iomem *reg, u32 mask, u32 val_enable, u32 val_disable, + unsigned int gate_flags, spinlock_t *lock) +{ + struct mmp_clk_gate *gate; + struct clk *clk; + struct clk_init_data init; + + /* allocate the gate */ + gate = kzalloc(sizeof(*gate), GFP_KERNEL); + if (!gate) { + pr_err("%s:%s could not allocate gate clk\n", __func__, name); + return ERR_PTR(-ENOMEM); + } + + init.name = name; + init.ops = &mmp_clk_gate_ops; + init.flags = flags | CLK_IS_BASIC; + init.parent_names = (parent_name ? &parent_name : NULL); + init.num_parents = (parent_name ? 1 : 0); + + /* struct clk_gate assignments */ + gate->reg = reg; + gate->mask = mask; + gate->val_enable = val_enable; + gate->val_disable = val_disable; + gate->flags = gate_flags; + gate->lock = lock; + gate->hw.init = &init; + + clk = clk_register(dev, &gate->hw); + + if (IS_ERR(clk)) + kfree(gate); + + return clk; +} diff --git a/drivers/clk/mmp/clk-mix.c b/drivers/clk/mmp/clk-mix.c new file mode 100644 index 0000000..b79742c --- /dev/null +++ b/drivers/clk/mmp/clk-mix.c @@ -0,0 +1,513 @@ +/* + * mmp mix(div and mux) clock operation source file + * + * Copyright (C) 2014 Marvell + * Chao Xie <chao.xie@marvell.com> + * + * 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 <linux/clk-provider.h> +#include <linux/slab.h> +#include <linux/io.h> +#include <linux/err.h> + +#include "clk.h" + +/* + * The mix clock is a clock combined mux and div type clock. + * Because the div field and mux field need to be set at same + * time, we can not divide it into 2 types of clock + */ + +#define to_clk_mix(hw) container_of(hw, struct mmp_clk_mix, hw) + +static unsigned int _get_maxdiv(struct mmp_clk_mix *mix) +{ + unsigned int div_mask = (1 << mix->reg_info.width_div) - 1; + unsigned int maxdiv = 0; + struct clk_div_table *clkt; + + if (mix->div_flags & CLK_DIVIDER_ONE_BASED) + return div_mask; + if (mix->div_flags & CLK_DIVIDER_POWER_OF_TWO) + return 1 << div_mask; + if (mix->div_table) { + for (clkt = mix->div_table; clkt->div; clkt++) + if (clkt->div > maxdiv) + maxdiv = clkt->div; + return maxdiv; + } + return div_mask + 1; +} + +static unsigned int _get_div(struct mmp_clk_mix *mix, unsigned int val) +{ + struct clk_div_table *clkt; + + if (mix->div_flags & CLK_DIVIDER_ONE_BASED) + return val; + if (mix->div_flags & CLK_DIVIDER_POWER_OF_TWO) + return 1 << val; + if (mix->div_table) { + for (clkt = mix->div_table; clkt->div; clkt++) + if (clkt->val == val) + return clkt->div; + if (clkt->div == 0) + return 0; + } + return val + 1; +} + +static unsigned int _get_mux(struct mmp_clk_mix *mix, unsigned int val) +{ + int num_parents = __clk_get_num_parents(mix->hw.clk); + int i; + + if (mix->mux_flags & CLK_MUX_INDEX_BIT) + return ffs(val) - 1; + if (mix->mux_flags & CLK_MUX_INDEX_ONE) + return val - 1; + if (mix->mux_table) { + for (i = 0; i < num_parents; i++) + if (mix->mux_table[i] == val) + return i; + if (i == num_parents) + return 0; + } + + return val; +} +static unsigned int _get_div_val(struct mmp_clk_mix *mix, unsigned int div) +{ + struct clk_div_table *clkt; + + if (mix->div_flags & CLK_DIVIDER_ONE_BASED) + return div; + if (mix->div_flags & CLK_DIVIDER_POWER_OF_TWO) + return __ffs(div); + if (mix->div_table) { + for (clkt = mix->div_table; clkt->div; clkt++) + if (clkt->div == div) + return clkt->val; + if (clkt->div == 0) + return 0; + } + + return div - 1; +} + +static unsigned int _get_mux_val(struct mmp_clk_mix *mix, unsigned int mux) +{ + if (mix->mux_table) + return mix->mux_table[mux]; + + return mux; +} + +static void _filter_clk_table(struct mmp_clk_mix *mix, + struct mmp_clk_mix_clk_table *table, + unsigned int table_size) +{ + int i; + struct mmp_clk_mix_clk_table *item; + struct clk *parent, *clk; + unsigned long parent_rate; + + clk = mix->hw.clk; + + for (i = 0; i < table_size; i++) { + item = &table[i]; + parent = clk_get_parent_by_index(clk, item->parent_index); + parent_rate = __clk_get_rate(parent); + if (parent_rate % item->rate) { + item->valid = 0; + } else { + item->divisor = parent_rate / item->rate; + item->valid = 1; + } + } +} + +static int _set_rate(struct mmp_clk_mix *mix, u32 mux_val, u32 div_val, + unsigned int change_mux, unsigned int change_div) +{ + struct mmp_clk_mix_reg_info *ri = &mix->reg_info; + u8 width, shift; + u32 mux_div, fc_req; + int ret, timeout = 50; + unsigned long flags = 0; + + if (!change_mux && !change_div) + return -EINVAL; + + if (mix->lock) + spin_lock_irqsave(mix->lock, flags); + + if (mix->type == MMP_CLK_MIX_TYPE_V1 + || mix->type == MMP_CLK_MIX_TYPE_V2) + mux_div = readl(ri->reg_clk_ctrl); + else + mux_div = readl(ri->reg_clk_sel); + + if (change_div) { + width = ri->width_div; + shift = ri->shift_div; + mux_div &= ~MMP_CLK_BITS_MASK(width, shift); + mux_div |= MMP_CLK_BITS_SET_VAL(div_val, width, shift); + } + + if (change_mux) { + width = ri->width_mux; + shift = ri->shift_mux; + mux_div &= ~MMP_CLK_BITS_MASK(width, shift); + mux_div |= MMP_CLK_BITS_SET_VAL(mux_val, width, shift); + } + + if (mix->type == MMP_CLK_MIX_TYPE_V1) { + writel(mux_div, ri->reg_clk_ctrl); + } else if (mix->type == MMP_CLK_MIX_TYPE_V2) { + mux_div |= (1 << ri->bit_fc); + writel(mux_div, ri->reg_clk_ctrl); + + do { + fc_req = readl(ri->reg_clk_ctrl); + timeout--; + if (!(fc_req & (1 << ri->bit_fc))) + break; + } while (timeout); + + if (timeout == 0) { + pr_err("%s:%s cannot do frequency change\n", + __func__, __clk_get_name(mix->hw.clk)); + ret = -EBUSY; + goto error; + } + } else { + fc_req = readl(ri->reg_clk_ctrl); + fc_req |= 1 << ri->bit_fc; + writel(fc_req, ri->reg_clk_ctrl); + writel(mux_div, ri->reg_clk_sel); + fc_req &= ~(1 << ri->bit_fc); + } + + ret = 0; +error: + if (mix->lock) + spin_unlock_irqrestore(mix->lock, flags); + + return ret; +} + +static long mmp_clk_mix_determine_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *best_parent_rate, + struct clk **best_parent_clk) +{ + struct mmp_clk_mix *mix = to_clk_mix(hw); + struct mmp_clk_mix_clk_table *item; + struct clk *parent, *parent_best, *mix_clk; + unsigned long parent_rate, mix_rate, mix_rate_best, parent_rate_best; + unsigned long gap, gap_best; + u32 div_val_max; + unsigned int div; + int i, j; + + mix_clk = hw->clk; + + parent = NULL; + mix_rate_best = 0; + parent_rate_best = 0; + gap_best = rate; + parent_best = NULL; + + if (mix->table) { + for (i = 0; i < mix->table_size; i++) { + item = &mix->table[i]; + if (item->valid == 0) + continue; + parent = clk_get_parent_by_index(mix_clk, + item->parent_index); + parent_rate = __clk_get_rate(parent); + mix_rate = parent_rate / item->divisor; + gap = abs(mix_rate - rate); + if (parent_best == NULL || gap < gap_best) { + parent_best = parent; + parent_rate_best = parent_rate; + mix_rate_best = mix_rate; + gap_best = gap; + if (gap_best == 0) + goto found; + } + } + } else { + for (i = 0; i < __clk_get_num_parents(mix_clk); i++) { + parent = clk_get_parent_by_index(mix_clk, i); + parent_rate = __clk_get_rate(parent); + div_val_max = _get_maxdiv(mix); + for (j = 0; j < div_val_max; j++) { + div = _get_div(mix, j); + mix_rate = parent_rate / div; + gap = abs(mix_rate - rate); + if (parent_best == NULL || gap < gap_best) { + parent_best = parent; + parent_rate_best = parent_rate; + mix_rate_best = mix_rate; + gap_best = gap; + if (gap_best == 0) + goto found; + } + } + } + } + +found: + *best_parent_rate = parent_rate_best; + *best_parent_clk = parent_best; + + return mix_rate_best; +} + +static int mmp_clk_mix_set_rate_and_parent(struct clk_hw *hw, + unsigned long rate, + unsigned long parent_rate, + u8 index) +{ + struct mmp_clk_mix *mix = to_clk_mix(hw); + unsigned int div; + u32 div_val, mux_val; + + div = parent_rate / rate; + div_val = _get_div_val(mix, div); + mux_val = _get_mux_val(mix, index); + + return _set_rate(mix, mux_val, div_val, 1, 1); +} + +static u8 mmp_clk_mix_get_parent(struct clk_hw *hw) +{ + struct mmp_clk_mix *mix = to_clk_mix(hw); + struct mmp_clk_mix_reg_info *ri = &mix->reg_info; + unsigned long flags = 0; + u32 mux_div = 0; + u8 width, shift; + u32 mux_val; + + if (mix->lock) + spin_lock_irqsave(mix->lock, flags); + + if (mix->type == MMP_CLK_MIX_TYPE_V1 + || mix->type == MMP_CLK_MIX_TYPE_V2) + mux_div = readl(ri->reg_clk_ctrl); + else + mux_div = readl(ri->reg_clk_sel); + + if (mix->lock) + spin_unlock_irqrestore(mix->lock, flags); + + width = mix->reg_info.width_mux; + shift = mix->reg_info.shift_mux; + + mux_val = MMP_CLK_BITS_GET_VAL(mux_div, width, shift); + + return _get_mux(mix, mux_val); +} + +static unsigned long mmp_clk_mix_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct mmp_clk_mix *mix = to_clk_mix(hw); + struct mmp_clk_mix_reg_info *ri = &mix->reg_info; + unsigned long flags = 0; + u32 mux_div = 0; + u8 width, shift; + unsigned int div; + + if (mix->lock) + spin_lock_irqsave(mix->lock, flags); + + if (mix->type == MMP_CLK_MIX_TYPE_V1 + || mix->type == MMP_CLK_MIX_TYPE_V2) + mux_div = readl(ri->reg_clk_ctrl); + else + mux_div = readl(ri->reg_clk_sel); + + if (mix->lock) + spin_unlock_irqrestore(mix->lock, flags); + + width = mix->reg_info.width_div; + shift = mix->reg_info.shift_div; + + div = _get_div(mix, MMP_CLK_BITS_GET_VAL(mux_div, width, shift)); + + return parent_rate / div; +} + +static int mmp_clk_set_parent(struct clk_hw *hw, u8 index) +{ + struct mmp_clk_mix *mix = to_clk_mix(hw); + struct mmp_clk_mix_clk_table *item; + int i; + u32 div_val, mux_val; + + if (mix->table) { + for (i = 0; i < mix->table_size; i++) { + item = &mix->table[i]; + if (item->valid == 0) + continue; + if (item->parent_index == index) + break; + } + if (i < mix->table_size) { + div_val = _get_div_val(mix, item->divisor); + mux_val = _get_mux_val(mix, item->parent_index); + } else + return -EINVAL; + } else { + mux_val = _get_mux_val(mix, index); + div_val = 0; + } + + return _set_rate(mix, mux_val, div_val, 1, div_val ? 1 : 0); +} + +static int mmp_clk_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long best_parent_rate) +{ + struct mmp_clk_mix *mix = to_clk_mix(hw); + struct mmp_clk_mix_clk_table *item; + unsigned long parent_rate; + unsigned int best_divisor; + struct clk *mix_clk, *parent; + int i; + + best_divisor = best_parent_rate / rate; + + mix_clk = hw->clk; + if (mix->table) { + for (i = 0; i < mix->table_size; i++) { + item = &mix->table[i]; + if (item->valid == 0) + continue; + parent = clk_get_parent_by_index(mix_clk, + item->parent_index); + parent_rate = __clk_get_rate(parent); + if (parent_rate == best_parent_rate + && item->divisor == best_divisor) + break; + } + if (i < mix->table_size) + return _set_rate(mix, + _get_mux_val(mix, item->parent_index), + _get_div_val(mix, item->divisor), + 1, 1); + else + return -EINVAL; + } else { + for (i = 0; i < __clk_get_num_parents(mix_clk); i++) { + parent = clk_get_parent_by_index(mix_clk, i); + parent_rate = __clk_get_rate(parent); + if (parent_rate == best_parent_rate) + break; + } + if (i < __clk_get_num_parents(mix_clk)) + return _set_rate(mix, _get_mux_val(mix, i), + _get_div_val(mix, best_divisor), 1, 1); + else + return -EINVAL; + } +} + +static void mmp_clk_mix_init(struct clk_hw *hw) +{ + struct mmp_clk_mix *mix = to_clk_mix(hw); + + if (mix->table) + _filter_clk_table(mix, mix->table, mix->table_size); +} + +const struct clk_ops mmp_clk_mix_ops = { + .determine_rate = mmp_clk_mix_determine_rate, + .set_rate_and_parent = mmp_clk_mix_set_rate_and_parent, + .set_rate = mmp_clk_set_rate, + .set_parent = mmp_clk_set_parent, + .get_parent = mmp_clk_mix_get_parent, + .recalc_rate = mmp_clk_mix_recalc_rate, + .init = mmp_clk_mix_init, +}; + +struct clk *mmp_clk_register_mix(struct device *dev, + const char *name, + const char **parent_names, + u8 num_parents, + unsigned long flags, + struct mmp_clk_mix_config *config, + spinlock_t *lock) +{ + struct mmp_clk_mix *mix; + struct clk *clk; + struct clk_init_data init; + size_t table_bytes; + + mix = kzalloc(sizeof(*mix), GFP_KERNEL); + if (!mix) { + pr_err("%s:%s: could not allocate mmp mix clk\n", + __func__, name); + return ERR_PTR(-ENOMEM); + } + + init.name = name; + init.flags = flags | CLK_GET_RATE_NOCACHE; + init.parent_names = parent_names; + init.num_parents = num_parents; + init.ops = &mmp_clk_mix_ops; + + memcpy(&mix->reg_info, &config->reg_info, sizeof(config->reg_info)); + if (config->table) { + table_bytes = sizeof(*config->table) * config->table_size; + mix->table = kzalloc(table_bytes, GFP_KERNEL); + if (!mix->table) { + pr_err("%s:%s: could not allocate mmp mix table\n", + __func__, name); + kfree(mix); + return ERR_PTR(-ENOMEM); + } + memcpy(mix->table, config->table, table_bytes); + mix->table_size = config->table_size; + } + + if (config->mux_table) { + table_bytes = sizeof(u32) * num_parents; + mix->mux_table = kzalloc(table_bytes, GFP_KERNEL); + if (!mix->mux_table) { + pr_err("%s:%s: could not allocate mmp mix mux-table\n", + __func__, name); + kfree(mix->table); + kfree(mix); + return ERR_PTR(-ENOMEM); + } + memcpy(mix->mux_table, config->mux_table, table_bytes); + } + + mix->div_flags = config->div_flags; + mix->mux_flags = config->mux_flags; + mix->lock = lock; + mix->hw.init = &init; + + if (config->reg_info.bit_fc >= 32) + mix->type = MMP_CLK_MIX_TYPE_V1; + else if (config->reg_info.reg_clk_sel) + mix->type = MMP_CLK_MIX_TYPE_V3; + else + mix->type = MMP_CLK_MIX_TYPE_V2; + clk = clk_register(dev, &mix->hw); + + if (IS_ERR(clk)) { + kfree(mix->mux_table); + kfree(mix->table); + kfree(mix); + } + + return clk; +} diff --git a/drivers/clk/mmp/clk-mmp2.c b/drivers/clk/mmp/clk-mmp2.c index b2721ca..5c90a42 100644 --- a/drivers/clk/mmp/clk-mmp2.c +++ b/drivers/clk/mmp/clk-mmp2.c @@ -54,7 +54,7 @@ static DEFINE_SPINLOCK(clk_lock); -static struct clk_factor_masks uart_factor_masks = { +static struct mmp_clk_factor_masks uart_factor_masks = { .factor = 2, .num_mask = 0x1fff, .den_mask = 0x1fff, @@ -62,7 +62,7 @@ static struct clk_factor_masks uart_factor_masks = { .den_shift = 0, }; -static struct clk_factor_tbl uart_factor_tbl[] = { +static struct mmp_clk_factor_tbl uart_factor_tbl[] = { {.num = 14634, .den = 2165}, /*14.745MHZ */ {.num = 3521, .den = 689}, /*19.23MHZ */ {.num = 9679, .den = 5728}, /*58.9824MHZ */ @@ -191,7 +191,7 @@ void __init mmp2_clk_init(void) clk = mmp_clk_register_factor("uart_pll", "pll1_4", 0, mpmu_base + MPMU_UART_PLL, &uart_factor_masks, uart_factor_tbl, - ARRAY_SIZE(uart_factor_tbl)); + ARRAY_SIZE(uart_factor_tbl), &clk_lock); clk_set_rate(clk, 14745600); clk_register_clkdev(clk, "uart_pll", NULL); diff --git a/drivers/clk/mmp/clk-of-mmp2.c b/drivers/clk/mmp/clk-of-mmp2.c new file mode 100644 index 0000000..2cbc2b4 --- /dev/null +++ b/drivers/clk/mmp/clk-of-mmp2.c @@ -0,0 +1,334 @@ +/* + * mmp2 clock framework source file + * + * Copyright (C) 2012 Marvell + * Chao Xie <xiechao.mail@gmail.com> + * + * 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 <linux/module.h> +#include <linux/kernel.h> +#include <linux/spinlock.h> +#include <linux/io.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/of_address.h> + +#include <dt-bindings/clock/marvell,mmp2.h> + +#include "clk.h" +#include "reset.h" + +#define APBC_RTC 0x0 +#define APBC_TWSI0 0x4 +#define APBC_TWSI1 0x8 +#define APBC_TWSI2 0xc +#define APBC_TWSI3 0x10 +#define APBC_TWSI4 0x7c +#define APBC_TWSI5 0x80 +#define APBC_KPC 0x18 +#define APBC_UART0 0x2c +#define APBC_UART1 0x30 +#define APBC_UART2 0x34 +#define APBC_UART3 0x88 +#define APBC_GPIO 0x38 +#define APBC_PWM0 0x3c +#define APBC_PWM1 0x40 +#define APBC_PWM2 0x44 +#define APBC_PWM3 0x48 +#define APBC_SSP0 0x50 +#define APBC_SSP1 0x54 +#define APBC_SSP2 0x58 +#define APBC_SSP3 0x5c +#define APMU_SDH0 0x54 +#define APMU_SDH1 0x58 +#define APMU_SDH2 0xe8 +#define APMU_SDH3 0xec +#define APMU_USB 0x5c +#define APMU_DISP0 0x4c +#define APMU_DISP1 0x110 +#define APMU_CCIC0 0x50 +#define APMU_CCIC1 0xf4 +#define MPMU_UART_PLL 0x14 + +struct mmp2_clk_unit { + struct mmp_clk_unit unit; + void __iomem *mpmu_base; + void __iomem *apmu_base; + void __iomem *apbc_base; +}; + +static struct mmp_param_fixed_rate_clk fixed_rate_clks[] = { + {MMP2_CLK_CLK32, "clk32", NULL, CLK_IS_ROOT, 32768}, + {MMP2_CLK_VCTCXO, "vctcxo", NULL, CLK_IS_ROOT, 26000000}, + {MMP2_CLK_PLL1, "pll1", NULL, CLK_IS_ROOT, 800000000}, + {MMP2_CLK_PLL2, "pll2", NULL, CLK_IS_ROOT, 960000000}, + {MMP2_CLK_USB_PLL, "usb_pll", NULL, CLK_IS_ROOT, 480000000}, +}; + +static struct mmp_param_fixed_factor_clk fixed_factor_clks[] = { + {MMP2_CLK_PLL1_2, "pll1_2", "pll1", 1, 2, 0}, + {MMP2_CLK_PLL1_4, "pll1_4", "pll1_2", 1, 2, 0}, + {MMP2_CLK_PLL1_8, "pll1_8", "pll1_4", 1, 2, 0}, + {MMP2_CLK_PLL1_16, "pll1_16", "pll1_8", 1, 2, 0}, + {MMP2_CLK_PLL1_20, "pll1_20", "pll1_4", 1, 5, 0}, + {MMP2_CLK_PLL1_3, "pll1_3", "pll1", 1, 3, 0}, + {MMP2_CLK_PLL1_6, "pll1_6", "pll1_3", 1, 2, 0}, + {MMP2_CLK_PLL1_12, "pll1_12", "pll1_6", 1, 2, 0}, + {MMP2_CLK_PLL2_2, "pll2_2", "pll2", 1, 2, 0}, + {MMP2_CLK_PLL2_4, "pll2_4", "pll2_2", 1, 2, 0}, + {MMP2_CLK_PLL2_8, "pll2_8", "pll2_4", 1, 2, 0}, + {MMP2_CLK_PLL2_16, "pll2_16", "pll2_8", 1, 2, 0}, + {MMP2_CLK_PLL2_3, "pll2_3", "pll2", 1, 3, 0}, + {MMP2_CLK_PLL2_6, "pll2_6", "pll2_3", 1, 2, 0}, + {MMP2_CLK_PLL2_12, "pll2_12", "pll2_6", 1, 2, 0}, + {MMP2_CLK_VCTCXO_2, "vctcxo_2", "vctcxo", 1, 2, 0}, + {MMP2_CLK_VCTCXO_4, "vctcxo_4", "vctcxo_2", 1, 2, 0}, +}; + +static struct mmp_clk_factor_masks uart_factor_masks = { + .factor = 2, + .num_mask = 0x1fff, + .den_mask = 0x1fff, + .num_shift = 16, + .den_shift = 0, +}; + +static struct mmp_clk_factor_tbl uart_factor_tbl[] = { + {.num = 14634, .den = 2165}, /*14.745MHZ */ + {.num = 3521, .den = 689}, /*19.23MHZ */ + {.num = 9679, .den = 5728}, /*58.9824MHZ */ + {.num = 15850, .den = 9451}, /*59.429MHZ */ +}; + +static void mmp2_pll_init(struct mmp2_clk_unit *pxa_unit) +{ + struct clk *clk; + struct mmp_clk_unit *unit = &pxa_unit->unit; + + mmp_register_fixed_rate_clks(unit, fixed_rate_clks, + ARRAY_SIZE(fixed_rate_clks)); + + mmp_register_fixed_factor_clks(unit, fixed_factor_clks, + ARRAY_SIZE(fixed_factor_clks)); + + clk = mmp_clk_register_factor("uart_pll", "pll1_4", + CLK_SET_RATE_PARENT, + pxa_unit->mpmu_base + MPMU_UART_PLL, + &uart_factor_masks, uart_factor_tbl, + ARRAY_SIZE(uart_factor_tbl), NULL); + mmp_clk_add(unit, MMP2_CLK_UART_PLL, clk); +} + +static DEFINE_SPINLOCK(uart0_lock); +static DEFINE_SPINLOCK(uart1_lock); +static DEFINE_SPINLOCK(uart2_lock); +static const char *uart_parent_names[] = {"uart_pll", "vctcxo"}; + +static DEFINE_SPINLOCK(ssp0_lock); +static DEFINE_SPINLOCK(ssp1_lock); +static DEFINE_SPINLOCK(ssp2_lock); +static DEFINE_SPINLOCK(ssp3_lock); +static const char *ssp_parent_names[] = {"vctcxo_4", "vctcxo_2", "vctcxo", "pll1_16"}; + +static DEFINE_SPINLOCK(reset_lock); + +static struct mmp_param_mux_clk apbc_mux_clks[] = { + {0, "uart0_mux", uart_parent_names, ARRAY_SIZE(uart_parent_names), CLK_SET_RATE_PARENT, APBC_UART0, 4, 3, 0, &uart0_lock}, + {0, "uart1_mux", uart_parent_names, ARRAY_SIZE(uart_parent_names), CLK_SET_RATE_PARENT, APBC_UART1, 4, 3, 0, &uart1_lock}, + {0, "uart2_mux", uart_parent_names, ARRAY_SIZE(uart_parent_names), CLK_SET_RATE_PARENT, APBC_UART2, 4, 3, 0, &uart2_lock}, + {0, "uart3_mux", uart_parent_names, ARRAY_SIZE(uart_parent_names), CLK_SET_RATE_PARENT, APBC_UART3, 4, 3, 0, &uart2_lock}, + {0, "ssp0_mux", ssp_parent_names, ARRAY_SIZE(ssp_parent_names), CLK_SET_RATE_PARENT, APBC_SSP0, 4, 3, 0, &ssp0_lock}, + {0, "ssp1_mux", ssp_parent_names, ARRAY_SIZE(ssp_parent_names), CLK_SET_RATE_PARENT, APBC_SSP1, 4, 3, 0, &ssp1_lock}, + {0, "ssp2_mux", ssp_parent_names, ARRAY_SIZE(ssp_parent_names), CLK_SET_RATE_PARENT, APBC_SSP2, 4, 3, 0, &ssp2_lock}, + {0, "ssp3_mux", ssp_parent_names, ARRAY_SIZE(ssp_parent_names), CLK_SET_RATE_PARENT, APBC_SSP3, 4, 3, 0, &ssp3_lock}, +}; + +static struct mmp_param_gate_clk apbc_gate_clks[] = { + {MMP2_CLK_TWSI0, "twsi0_clk", "vctcxo", CLK_SET_RATE_PARENT, APBC_TWSI0, 0x7, 0x3, 0x0, 0, &reset_lock}, + {MMP2_CLK_TWSI1, "twsi1_clk", "vctcxo", CLK_SET_RATE_PARENT, APBC_TWSI1, 0x7, 0x3, 0x0, 0, &reset_lock}, + {MMP2_CLK_TWSI2, "twsi2_clk", "vctcxo", CLK_SET_RATE_PARENT, APBC_TWSI2, 0x7, 0x3, 0x0, 0, &reset_lock}, + {MMP2_CLK_TWSI3, "twsi3_clk", "vctcxo", CLK_SET_RATE_PARENT, APBC_TWSI3, 0x7, 0x3, 0x0, 0, &reset_lock}, + {MMP2_CLK_TWSI4, "twsi4_clk", "vctcxo", CLK_SET_RATE_PARENT, APBC_TWSI4, 0x7, 0x3, 0x0, 0, &reset_lock}, + {MMP2_CLK_TWSI5, "twsi5_clk", "vctcxo", CLK_SET_RATE_PARENT, APBC_TWSI5, 0x7, 0x3, 0x0, 0, &reset_lock}, + {MMP2_CLK_GPIO, "gpio_clk", "vctcxo", CLK_SET_RATE_PARENT, APBC_GPIO, 0x7, 0x3, 0x0, 0, &reset_lock}, + {MMP2_CLK_KPC, "kpc_clk", "clk32", CLK_SET_RATE_PARENT, APBC_KPC, 0x7, 0x3, 0x0, MMP_CLK_GATE_NEED_DELAY, &reset_lock}, + {MMP2_CLK_RTC, "rtc_clk", "clk32", CLK_SET_RATE_PARENT, APBC_RTC, 0x87, 0x83, 0x0, MMP_CLK_GATE_NEED_DELAY, &reset_lock}, + {MMP2_CLK_PWM0, "pwm0_clk", "pll1_48", CLK_SET_RATE_PARENT, APBC_PWM0, 0x7, 0x3, 0x0, 0, &reset_lock}, + {MMP2_CLK_PWM1, "pwm1_clk", "pll1_48", CLK_SET_RATE_PARENT, APBC_PWM1, 0x7, 0x3, 0x0, 0, &reset_lock}, + {MMP2_CLK_PWM2, "pwm2_clk", "pll1_48", CLK_SET_RATE_PARENT, APBC_PWM2, 0x7, 0x3, 0x0, 0, &reset_lock}, + {MMP2_CLK_PWM3, "pwm3_clk", "pll1_48", CLK_SET_RATE_PARENT, APBC_PWM3, 0x7, 0x3, 0x0, 0, &reset_lock}, + /* The gate clocks has mux parent. */ + {MMP2_CLK_UART0, "uart0_clk", "uart0_mux", CLK_SET_RATE_PARENT, APBC_UART0, 0x7, 0x3, 0x0, 0, &uart0_lock}, + {MMP2_CLK_UART1, "uart1_clk", "uart1_mux", CLK_SET_RATE_PARENT, APBC_UART1, 0x7, 0x3, 0x0, 0, &uart1_lock}, + {MMP2_CLK_UART2, "uart2_clk", "uart2_mux", CLK_SET_RATE_PARENT, APBC_UART2, 0x7, 0x3, 0x0, 0, &uart2_lock}, + {MMP2_CLK_UART3, "uart3_clk", "uart3_mux", CLK_SET_RATE_PARENT, APBC_UART3, 0x7, 0x3, 0x0, 0, &uart2_lock}, + {MMP2_CLK_SSP0, "ssp0_clk", "ssp0_mux", CLK_SET_RATE_PARENT, APBC_SSP0, 0x7, 0x3, 0x0, 0, &ssp0_lock}, + {MMP2_CLK_SSP1, "ssp1_clk", "ssp1_mux", CLK_SET_RATE_PARENT, APBC_SSP1, 0x7, 0x3, 0x0, 0, &ssp1_lock}, + {MMP2_CLK_SSP2, "ssp2_clk", "ssp2_mux", CLK_SET_RATE_PARENT, APBC_SSP2, 0x7, 0x3, 0x0, 0, &ssp2_lock}, + {MMP2_CLK_SSP3, "ssp3_clk", "ssp3_mux", CLK_SET_RATE_PARENT, APBC_SSP3, 0x7, 0x3, 0x0, 0, &ssp3_lock}, +}; + +static void mmp2_apb_periph_clk_init(struct mmp2_clk_unit *pxa_unit) +{ + struct mmp_clk_unit *unit = &pxa_unit->unit; + + mmp_register_mux_clks(unit, apbc_mux_clks, pxa_unit->apbc_base, + ARRAY_SIZE(apbc_mux_clks)); + + mmp_register_gate_clks(unit, apbc_gate_clks, pxa_unit->apbc_base, + ARRAY_SIZE(apbc_gate_clks)); +} + +static DEFINE_SPINLOCK(sdh_lock); +static const char *sdh_parent_names[] = {"pll1_4", "pll2", "usb_pll", "pll1"}; +static struct mmp_clk_mix_config sdh_mix_config = { + .reg_info = DEFINE_MIX_REG_INFO(4, 10, 2, 8, 32), +}; + +static DEFINE_SPINLOCK(usb_lock); + +static DEFINE_SPINLOCK(disp0_lock); +static DEFINE_SPINLOCK(disp1_lock); +static const char *disp_parent_names[] = {"pll1", "pll1_16", "pll2", "vctcxo"}; + +static DEFINE_SPINLOCK(ccic0_lock); +static DEFINE_SPINLOCK(ccic1_lock); +static const char *ccic_parent_names[] = {"pll1_2", "pll1_16", "vctcxo"}; +static struct mmp_clk_mix_config ccic0_mix_config = { + .reg_info = DEFINE_MIX_REG_INFO(4, 17, 2, 6, 32), +}; +static struct mmp_clk_mix_config ccic1_mix_config = { + .reg_info = DEFINE_MIX_REG_INFO(4, 16, 2, 6, 32), +}; + +static struct mmp_param_mux_clk apmu_mux_clks[] = { + {MMP2_CLK_DISP0_MUX, "disp0_mux", disp_parent_names, ARRAY_SIZE(disp_parent_names), CLK_SET_RATE_PARENT, APMU_DISP0, 6, 2, 0, &disp0_lock}, + {MMP2_CLK_DISP1_MUX, "disp1_mux", disp_parent_names, ARRAY_SIZE(disp_parent_names), CLK_SET_RATE_PARENT, APMU_DISP1, 6, 2, 0, &disp1_lock}, +}; + +static struct mmp_param_div_clk apmu_div_clks[] = { + {0, "disp0_div", "disp0_mux", CLK_SET_RATE_PARENT, APMU_DISP0, 8, 4, 0, &disp0_lock}, + {0, "disp0_sphy_div", "disp0_mux", CLK_SET_RATE_PARENT, APMU_DISP0, 15, 5, 0, &disp0_lock}, + {0, "disp1_div", "disp1_mux", CLK_SET_RATE_PARENT, APMU_DISP1, 8, 4, 0, &disp1_lock}, + {0, "ccic0_sphy_div", "ccic0_mix_clk", CLK_SET_RATE_PARENT, APMU_CCIC0, 10, 5, 0, &ccic0_lock}, + {0, "ccic1_sphy_div", "ccic1_mix_clk", CLK_SET_RATE_PARENT, APMU_CCIC1, 10, 5, 0, &ccic1_lock}, +}; + +static struct mmp_param_gate_clk apmu_gate_clks[] = { + {MMP2_CLK_USB, "usb_clk", "usb_pll", 0, APMU_USB, 0x9, 0x9, 0x0, 0, &usb_lock}, + /* The gate clocks has mux parent. */ + {MMP2_CLK_SDH0, "sdh0_clk", "sdh_mix_clk", CLK_SET_RATE_PARENT, APMU_SDH0, 0x1b, 0x1b, 0x0, 0, &sdh_lock}, + {MMP2_CLK_SDH1, "sdh1_clk", "sdh_mix_clk", CLK_SET_RATE_PARENT, APMU_SDH1, 0x1b, 0x1b, 0x0, 0, &sdh_lock}, + {MMP2_CLK_SDH1, "sdh2_clk", "sdh_mix_clk", CLK_SET_RATE_PARENT, APMU_SDH2, 0x1b, 0x1b, 0x0, 0, &sdh_lock}, + {MMP2_CLK_SDH1, "sdh3_clk", "sdh_mix_clk", CLK_SET_RATE_PARENT, APMU_SDH3, 0x1b, 0x1b, 0x0, 0, &sdh_lock}, + {MMP2_CLK_DISP0, "disp0_clk", "disp0_div", CLK_SET_RATE_PARENT, APMU_DISP0, 0x1b, 0x1b, 0x0, 0, &disp0_lock}, + {MMP2_CLK_DISP0_SPHY, "disp0_sphy_clk", "disp0_sphy_div", CLK_SET_RATE_PARENT, APMU_DISP0, 0x1024, 0x1024, 0x0, 0, &disp0_lock}, + {MMP2_CLK_DISP1, "disp1_clk", "disp1_div", CLK_SET_RATE_PARENT, APMU_DISP1, 0x1b, 0x1b, 0x0, 0, &disp1_lock}, + {MMP2_CLK_CCIC_ARBITER, "ccic_arbiter", "vctcxo", CLK_SET_RATE_PARENT, APMU_CCIC0, 0x1800, 0x1800, 0x0, 0, &ccic0_lock}, + {MMP2_CLK_CCIC0, "ccic0_clk", "ccic0_mix_clk", CLK_SET_RATE_PARENT, APMU_CCIC0, 0x1b, 0x1b, 0x0, 0, &ccic0_lock}, + {MMP2_CLK_CCIC0_PHY, "ccic0_phy_clk", "ccic0_mix_clk", CLK_SET_RATE_PARENT, APMU_CCIC0, 0x24, 0x24, 0x0, 0, &ccic0_lock}, + {MMP2_CLK_CCIC0_SPHY, "ccic0_sphy_clk", "ccic0_sphy_div", CLK_SET_RATE_PARENT, APMU_CCIC0, 0x300, 0x300, 0x0, 0, &ccic0_lock}, + {MMP2_CLK_CCIC1, "ccic1_clk", "ccic1_mix_clk", CLK_SET_RATE_PARENT, APMU_CCIC1, 0x1b, 0x1b, 0x0, 0, &ccic1_lock}, + {MMP2_CLK_CCIC1_PHY, "ccic1_phy_clk", "ccic1_mix_clk", CLK_SET_RATE_PARENT, APMU_CCIC1, 0x24, 0x24, 0x0, 0, &ccic1_lock}, + {MMP2_CLK_CCIC1_SPHY, "ccic1_sphy_clk", "ccic1_sphy_div", CLK_SET_RATE_PARENT, APMU_CCIC1, 0x300, 0x300, 0x0, 0, &ccic1_lock}, +}; + +static void mmp2_axi_periph_clk_init(struct mmp2_clk_unit *pxa_unit) +{ + struct clk *clk; + struct mmp_clk_unit *unit = &pxa_unit->unit; + + sdh_mix_config.reg_info.reg_clk_ctrl = pxa_unit->apmu_base + APMU_SDH0; + clk = mmp_clk_register_mix(NULL, "sdh_mix_clk", sdh_parent_names, + ARRAY_SIZE(sdh_parent_names), + CLK_SET_RATE_PARENT, + &sdh_mix_config, &sdh_lock); + + ccic0_mix_config.reg_info.reg_clk_ctrl = pxa_unit->apmu_base + APMU_CCIC0; + clk = mmp_clk_register_mix(NULL, "ccic0_mix_clk", ccic_parent_names, + ARRAY_SIZE(ccic_parent_names), + CLK_SET_RATE_PARENT, + &ccic0_mix_config, &ccic0_lock); + mmp_clk_add(unit, MMP2_CLK_CCIC0_MIX, clk); + + ccic1_mix_config.reg_info.reg_clk_ctrl = pxa_unit->apmu_base + APMU_CCIC1; + clk = mmp_clk_register_mix(NULL, "ccic1_mix_clk", ccic_parent_names, + ARRAY_SIZE(ccic_parent_names), + CLK_SET_RATE_PARENT, + &ccic1_mix_config, &ccic1_lock); + mmp_clk_add(unit, MMP2_CLK_CCIC1_MIX, clk); + + mmp_register_mux_clks(unit, apmu_mux_clks, pxa_unit->apmu_base, + ARRAY_SIZE(apmu_mux_clks)); + + mmp_register_div_clks(unit, apmu_div_clks, pxa_unit->apmu_base, + ARRAY_SIZE(apmu_div_clks)); + + mmp_register_gate_clks(unit, apmu_gate_clks, pxa_unit->apmu_base, + ARRAY_SIZE(apmu_gate_clks)); +} + +static void mmp2_clk_reset_init(struct device_node *np, + struct mmp2_clk_unit *pxa_unit) +{ + struct mmp_clk_reset_cell *cells; + int i, nr_resets; + + nr_resets = ARRAY_SIZE(apbc_gate_clks); + cells = kcalloc(nr_resets, sizeof(*cells), GFP_KERNEL); + if (!cells) + return; + + for (i = 0; i < nr_resets; i++) { + cells[i].clk_id = apbc_gate_clks[i].id; + cells[i].reg = pxa_unit->apbc_base + apbc_gate_clks[i].offset; + cells[i].flags = 0; + cells[i].lock = apbc_gate_clks[i].lock; + cells[i].bits = 0x4; + } + + mmp_clk_reset_register(np, cells, nr_resets); +} + +static void __init mmp2_clk_init(struct device_node *np) +{ + struct mmp2_clk_unit *pxa_unit; + + pxa_unit = kzalloc(sizeof(*pxa_unit), GFP_KERNEL); + if (!pxa_unit) + return; + + pxa_unit->mpmu_base = of_iomap(np, 0); + if (!pxa_unit->mpmu_base) { + pr_err("failed to map mpmu registers\n"); + return; + } + + pxa_unit->apmu_base = of_iomap(np, 1); + if (!pxa_unit->mpmu_base) { + pr_err("failed to map apmu registers\n"); + return; + } + + pxa_unit->apbc_base = of_iomap(np, 2); + if (!pxa_unit->apbc_base) { + pr_err("failed to map apbc registers\n"); + return; + } + + mmp_clk_init(np, &pxa_unit->unit, MMP2_NR_CLKS); + + mmp2_pll_init(pxa_unit); + + mmp2_apb_periph_clk_init(pxa_unit); + + mmp2_axi_periph_clk_init(pxa_unit); + + mmp2_clk_reset_init(np, pxa_unit); +} + +CLK_OF_DECLARE(mmp2_clk, "marvell,mmp2-clock", mmp2_clk_init); diff --git a/drivers/clk/mmp/clk-of-pxa168.c b/drivers/clk/mmp/clk-of-pxa168.c new file mode 100644 index 0000000..5b1810d --- /dev/null +++ b/drivers/clk/mmp/clk-of-pxa168.c @@ -0,0 +1,279 @@ +/* + * pxa168 clock framework source file + * + * Copyright (C) 2012 Marvell + * Chao Xie <xiechao.mail@gmail.com> + * + * 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 <linux/module.h> +#include <linux/kernel.h> +#include <linux/spinlock.h> +#include <linux/io.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/of_address.h> + +#include <dt-bindings/clock/marvell,pxa168.h> + +#include "clk.h" +#include "reset.h" + +#define APBC_RTC 0x28 +#define APBC_TWSI0 0x2c +#define APBC_KPC 0x30 +#define APBC_UART0 0x0 +#define APBC_UART1 0x4 +#define APBC_GPIO 0x8 +#define APBC_PWM0 0xc +#define APBC_PWM1 0x10 +#define APBC_PWM2 0x14 +#define APBC_PWM3 0x18 +#define APBC_SSP0 0x81c +#define APBC_SSP1 0x820 +#define APBC_SSP2 0x84c +#define APBC_SSP3 0x858 +#define APBC_SSP4 0x85c +#define APBC_TWSI1 0x6c +#define APBC_UART2 0x70 +#define APMU_SDH0 0x54 +#define APMU_SDH1 0x58 +#define APMU_USB 0x5c +#define APMU_DISP0 0x4c +#define APMU_CCIC0 0x50 +#define APMU_DFC 0x60 +#define MPMU_UART_PLL 0x14 + +struct pxa168_clk_unit { + struct mmp_clk_unit unit; + void __iomem *mpmu_base; + void __iomem *apmu_base; + void __iomem *apbc_base; +}; + +static struct mmp_param_fixed_rate_clk fixed_rate_clks[] = { + {PXA168_CLK_CLK32, "clk32", NULL, CLK_IS_ROOT, 32768}, + {PXA168_CLK_VCTCXO, "vctcxo", NULL, CLK_IS_ROOT, 26000000}, + {PXA168_CLK_PLL1, "pll1", NULL, CLK_IS_ROOT, 624000000}, +}; + +static struct mmp_param_fixed_factor_clk fixed_factor_clks[] = { + {PXA168_CLK_PLL1_2, "pll1_2", "pll1", 1, 2, 0}, + {PXA168_CLK_PLL1_4, "pll1_4", "pll1_2", 1, 2, 0}, + {PXA168_CLK_PLL1_8, "pll1_8", "pll1_4", 1, 2, 0}, + {PXA168_CLK_PLL1_16, "pll1_16", "pll1_8", 1, 2, 0}, + {PXA168_CLK_PLL1_6, "pll1_6", "pll1_2", 1, 3, 0}, + {PXA168_CLK_PLL1_12, "pll1_12", "pll1_6", 1, 2, 0}, + {PXA168_CLK_PLL1_24, "pll1_24", "pll1_12", 1, 2, 0}, + {PXA168_CLK_PLL1_48, "pll1_48", "pll1_24", 1, 2, 0}, + {PXA168_CLK_PLL1_96, "pll1_96", "pll1_48", 1, 2, 0}, + {PXA168_CLK_PLL1_13, "pll1_13", "pll1", 1, 13, 0}, + {PXA168_CLK_PLL1_13_1_5, "pll1_13_1_5", "pll1_13", 2, 3, 0}, + {PXA168_CLK_PLL1_2_1_5, "pll1_2_1_5", "pll1_2", 2, 3, 0}, + {PXA168_CLK_PLL1_3_16, "pll1_3_16", "pll1", 3, 16, 0}, +}; + +static struct mmp_clk_factor_masks uart_factor_masks = { + .factor = 2, + .num_mask = 0x1fff, + .den_mask = 0x1fff, + .num_shift = 16, + .den_shift = 0, +}; + +static struct mmp_clk_factor_tbl uart_factor_tbl[] = { + {.num = 8125, .den = 1536}, /*14.745MHZ */ +}; + +static void pxa168_pll_init(struct pxa168_clk_unit *pxa_unit) +{ + struct clk *clk; + struct mmp_clk_unit *unit = &pxa_unit->unit; + + mmp_register_fixed_rate_clks(unit, fixed_rate_clks, + ARRAY_SIZE(fixed_rate_clks)); + + mmp_register_fixed_factor_clks(unit, fixed_factor_clks, + ARRAY_SIZE(fixed_factor_clks)); + + clk = mmp_clk_register_factor("uart_pll", "pll1_4", + CLK_SET_RATE_PARENT, + pxa_unit->mpmu_base + MPMU_UART_PLL, + &uart_factor_masks, uart_factor_tbl, + ARRAY_SIZE(uart_factor_tbl), NULL); + mmp_clk_add(unit, PXA168_CLK_UART_PLL, clk); +} + +static DEFINE_SPINLOCK(uart0_lock); +static DEFINE_SPINLOCK(uart1_lock); +static DEFINE_SPINLOCK(uart2_lock); +static const char *uart_parent_names[] = {"pll1_3_16", "uart_pll"}; + +static DEFINE_SPINLOCK(ssp0_lock); +static DEFINE_SPINLOCK(ssp1_lock); +static DEFINE_SPINLOCK(ssp2_lock); +static DEFINE_SPINLOCK(ssp3_lock); +static DEFINE_SPINLOCK(ssp4_lock); +static const char *ssp_parent_names[] = {"pll1_96", "pll1_48", "pll1_24", "pll1_12"}; + +static DEFINE_SPINLOCK(reset_lock); + +static struct mmp_param_mux_clk apbc_mux_clks[] = { + {0, "uart0_mux", uart_parent_names, ARRAY_SIZE(uart_parent_names), CLK_SET_RATE_PARENT, APBC_UART0, 4, 3, 0, &uart0_lock}, + {0, "uart1_mux", uart_parent_names, ARRAY_SIZE(uart_parent_names), CLK_SET_RATE_PARENT, APBC_UART1, 4, 3, 0, &uart1_lock}, + {0, "uart2_mux", uart_parent_names, ARRAY_SIZE(uart_parent_names), CLK_SET_RATE_PARENT, APBC_UART2, 4, 3, 0, &uart2_lock}, + {0, "ssp0_mux", ssp_parent_names, ARRAY_SIZE(ssp_parent_names), CLK_SET_RATE_PARENT, APBC_SSP0, 4, 3, 0, &ssp0_lock}, + {0, "ssp1_mux", ssp_parent_names, ARRAY_SIZE(ssp_parent_names), CLK_SET_RATE_PARENT, APBC_SSP1, 4, 3, 0, &ssp1_lock}, + {0, "ssp2_mux", ssp_parent_names, ARRAY_SIZE(ssp_parent_names), CLK_SET_RATE_PARENT, APBC_SSP2, 4, 3, 0, &ssp2_lock}, + {0, "ssp3_mux", ssp_parent_names, ARRAY_SIZE(ssp_parent_names), CLK_SET_RATE_PARENT, APBC_SSP3, 4, 3, 0, &ssp3_lock}, + {0, "ssp4_mux", ssp_parent_names, ARRAY_SIZE(ssp_parent_names), CLK_SET_RATE_PARENT, APBC_SSP4, 4, 3, 0, &ssp4_lock}, +}; + +static struct mmp_param_gate_clk apbc_gate_clks[] = { + {PXA168_CLK_TWSI0, "twsi0_clk", "pll1_13_1_5", CLK_SET_RATE_PARENT, APBC_TWSI0, 0x3, 0x3, 0x0, 0, &reset_lock}, + {PXA168_CLK_TWSI1, "twsi1_clk", "pll1_13_1_5", CLK_SET_RATE_PARENT, APBC_TWSI1, 0x3, 0x3, 0x0, 0, &reset_lock}, + {PXA168_CLK_GPIO, "gpio_clk", "vctcxo", CLK_SET_RATE_PARENT, APBC_GPIO, 0x3, 0x3, 0x0, 0, &reset_lock}, + {PXA168_CLK_KPC, "kpc_clk", "clk32", CLK_SET_RATE_PARENT, APBC_KPC, 0x3, 0x3, 0x0, MMP_CLK_GATE_NEED_DELAY, NULL}, + {PXA168_CLK_RTC, "rtc_clk", "clk32", CLK_SET_RATE_PARENT, APBC_RTC, 0x83, 0x83, 0x0, MMP_CLK_GATE_NEED_DELAY, NULL}, + {PXA168_CLK_PWM0, "pwm0_clk", "pll1_48", CLK_SET_RATE_PARENT, APBC_PWM0, 0x3, 0x3, 0x0, 0, &reset_lock}, + {PXA168_CLK_PWM1, "pwm1_clk", "pll1_48", CLK_SET_RATE_PARENT, APBC_PWM1, 0x3, 0x3, 0x0, 0, &reset_lock}, + {PXA168_CLK_PWM2, "pwm2_clk", "pll1_48", CLK_SET_RATE_PARENT, APBC_PWM2, 0x3, 0x3, 0x0, 0, &reset_lock}, + {PXA168_CLK_PWM3, "pwm3_clk", "pll1_48", CLK_SET_RATE_PARENT, APBC_PWM3, 0x3, 0x3, 0x0, 0, &reset_lock}, + /* The gate clocks has mux parent. */ + {PXA168_CLK_UART0, "uart0_clk", "uart0_mux", CLK_SET_RATE_PARENT, APBC_UART0, 0x3, 0x3, 0x0, 0, &uart0_lock}, + {PXA168_CLK_UART1, "uart1_clk", "uart1_mux", CLK_SET_RATE_PARENT, APBC_UART1, 0x3, 0x3, 0x0, 0, &uart1_lock}, + {PXA168_CLK_UART2, "uart2_clk", "uart2_mux", CLK_SET_RATE_PARENT, APBC_UART2, 0x3, 0x3, 0x0, 0, &uart2_lock}, + {PXA168_CLK_SSP0, "ssp0_clk", "ssp0_mux", CLK_SET_RATE_PARENT, APBC_SSP0, 0x3, 0x3, 0x0, 0, &ssp0_lock}, + {PXA168_CLK_SSP1, "ssp1_clk", "ssp1_mux", CLK_SET_RATE_PARENT, APBC_SSP1, 0x3, 0x3, 0x0, 0, &ssp1_lock}, + {PXA168_CLK_SSP2, "ssp2_clk", "ssp2_mux", CLK_SET_RATE_PARENT, APBC_SSP2, 0x3, 0x3, 0x0, 0, &ssp2_lock}, + {PXA168_CLK_SSP3, "ssp3_clk", "ssp3_mux", CLK_SET_RATE_PARENT, APBC_SSP3, 0x3, 0x3, 0x0, 0, &ssp3_lock}, + {PXA168_CLK_SSP4, "ssp4_clk", "ssp4_mux", CLK_SET_RATE_PARENT, APBC_SSP4, 0x3, 0x3, 0x0, 0, &ssp4_lock}, +}; + +static void pxa168_apb_periph_clk_init(struct pxa168_clk_unit *pxa_unit) +{ + struct mmp_clk_unit *unit = &pxa_unit->unit; + + mmp_register_mux_clks(unit, apbc_mux_clks, pxa_unit->apbc_base, + ARRAY_SIZE(apbc_mux_clks)); + + mmp_register_gate_clks(unit, apbc_gate_clks, pxa_unit->apbc_base, + ARRAY_SIZE(apbc_gate_clks)); + +} + +static DEFINE_SPINLOCK(sdh0_lock); +static DEFINE_SPINLOCK(sdh1_lock); +static const char *sdh_parent_names[] = {"pll1_12", "pll1_13"}; + +static DEFINE_SPINLOCK(usb_lock); + +static DEFINE_SPINLOCK(disp0_lock); +static const char *disp_parent_names[] = {"pll1_2", "pll1_12"}; + +static DEFINE_SPINLOCK(ccic0_lock); +static const char *ccic_parent_names[] = {"pll1_2", "pll1_12"}; +static const char *ccic_phy_parent_names[] = {"pll1_6", "pll1_12"}; + +static struct mmp_param_mux_clk apmu_mux_clks[] = { + {0, "sdh0_mux", sdh_parent_names, ARRAY_SIZE(sdh_parent_names), CLK_SET_RATE_PARENT, APMU_SDH0, 6, 1, 0, &sdh0_lock}, + {0, "sdh1_mux", sdh_parent_names, ARRAY_SIZE(sdh_parent_names), CLK_SET_RATE_PARENT, APMU_SDH1, 6, 1, 0, &sdh1_lock}, + {0, "disp0_mux", disp_parent_names, ARRAY_SIZE(disp_parent_names), CLK_SET_RATE_PARENT, APMU_DISP0, 6, 1, 0, &disp0_lock}, + {0, "ccic0_mux", ccic_parent_names, ARRAY_SIZE(ccic_parent_names), CLK_SET_RATE_PARENT, APMU_CCIC0, 6, 1, 0, &ccic0_lock}, + {0, "ccic0_phy_mux", ccic_phy_parent_names, ARRAY_SIZE(ccic_phy_parent_names), CLK_SET_RATE_PARENT, APMU_CCIC0, 7, 1, 0, &ccic0_lock}, +}; + +static struct mmp_param_div_clk apmu_div_clks[] = { + {0, "ccic0_sphy_div", "ccic0_mux", CLK_SET_RATE_PARENT, APMU_CCIC0, 10, 5, 0, &ccic0_lock}, +}; + +static struct mmp_param_gate_clk apmu_gate_clks[] = { + {PXA168_CLK_DFC, "dfc_clk", "pll1_4", CLK_SET_RATE_PARENT, APMU_DFC, 0x19b, 0x19b, 0x0, 0, NULL}, + {PXA168_CLK_USB, "usb_clk", "usb_pll", 0, APMU_USB, 0x9, 0x9, 0x0, 0, &usb_lock}, + {PXA168_CLK_SPH, "sph_clk", "usb_pll", 0, APMU_USB, 0x12, 0x12, 0x0, 0, &usb_lock}, + /* The gate clocks has mux parent. */ + {PXA168_CLK_SDH0, "sdh0_clk", "sdh0_mux", CLK_SET_RATE_PARENT, APMU_SDH0, 0x1b, 0x1b, 0x0, 0, &sdh0_lock}, + {PXA168_CLK_SDH1, "sdh1_clk", "sdh1_mux", CLK_SET_RATE_PARENT, APMU_SDH1, 0x1b, 0x1b, 0x0, 0, &sdh1_lock}, + {PXA168_CLK_DISP0, "disp0_clk", "disp0_mux", CLK_SET_RATE_PARENT, APMU_DISP0, 0x1b, 0x1b, 0x0, 0, &disp0_lock}, + {PXA168_CLK_CCIC0, "ccic0_clk", "ccic0_mux", CLK_SET_RATE_PARENT, APMU_CCIC0, 0x1b, 0x1b, 0x0, 0, &ccic0_lock}, + {PXA168_CLK_CCIC0_PHY, "ccic0_phy_clk", "ccic0_phy_mux", CLK_SET_RATE_PARENT, APMU_CCIC0, 0x24, 0x24, 0x0, 0, &ccic0_lock}, + {PXA168_CLK_CCIC0_SPHY, "ccic0_sphy_clk", "ccic0_sphy_div", CLK_SET_RATE_PARENT, APMU_CCIC0, 0x300, 0x300, 0x0, 0, &ccic0_lock}, +}; + +static void pxa168_axi_periph_clk_init(struct pxa168_clk_unit *pxa_unit) +{ + struct mmp_clk_unit *unit = &pxa_unit->unit; + + mmp_register_mux_clks(unit, apmu_mux_clks, pxa_unit->apmu_base, + ARRAY_SIZE(apmu_mux_clks)); + + mmp_register_div_clks(unit, apmu_div_clks, pxa_unit->apmu_base, + ARRAY_SIZE(apmu_div_clks)); + + mmp_register_gate_clks(unit, apmu_gate_clks, pxa_unit->apmu_base, + ARRAY_SIZE(apmu_gate_clks)); +} + +static void pxa168_clk_reset_init(struct device_node *np, + struct pxa168_clk_unit *pxa_unit) +{ + struct mmp_clk_reset_cell *cells; + int i, nr_resets; + + nr_resets = ARRAY_SIZE(apbc_gate_clks); + cells = kcalloc(nr_resets, sizeof(*cells), GFP_KERNEL); + if (!cells) + return; + + for (i = 0; i < nr_resets; i++) { + cells[i].clk_id = apbc_gate_clks[i].id; + cells[i].reg = pxa_unit->apbc_base + apbc_gate_clks[i].offset; + cells[i].flags = 0; + cells[i].lock = apbc_gate_clks[i].lock; + cells[i].bits = 0x4; + } + + mmp_clk_reset_register(np, cells, nr_resets); +} + +static void __init pxa168_clk_init(struct device_node *np) +{ + struct pxa168_clk_unit *pxa_unit; + + pxa_unit = kzalloc(sizeof(*pxa_unit), GFP_KERNEL); + if (!pxa_unit) + return; + + pxa_unit->mpmu_base = of_iomap(np, 0); + if (!pxa_unit->mpmu_base) { + pr_err("failed to map mpmu registers\n"); + return; + } + + pxa_unit->apmu_base = of_iomap(np, 1); + if (!pxa_unit->mpmu_base) { + pr_err("failed to map apmu registers\n"); + return; + } + + pxa_unit->apbc_base = of_iomap(np, 2); + if (!pxa_unit->apbc_base) { + pr_err("failed to map apbc registers\n"); + return; + } + + mmp_clk_init(np, &pxa_unit->unit, PXA168_NR_CLKS); + + pxa168_pll_init(pxa_unit); + + pxa168_apb_periph_clk_init(pxa_unit); + + pxa168_axi_periph_clk_init(pxa_unit); + + pxa168_clk_reset_init(np, pxa_unit); +} + +CLK_OF_DECLARE(pxa168_clk, "marvell,pxa168-clock", pxa168_clk_init); diff --git a/drivers/clk/mmp/clk-of-pxa910.c b/drivers/clk/mmp/clk-of-pxa910.c new file mode 100644 index 0000000..5e3c80d --- /dev/null +++ b/drivers/clk/mmp/clk-of-pxa910.c @@ -0,0 +1,301 @@ +/* + * pxa910 clock framework source file + * + * Copyright (C) 2012 Marvell + * Chao Xie <xiechao.mail@gmail.com> + * + * 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 <linux/module.h> +#include <linux/kernel.h> +#include <linux/spinlock.h> +#include <linux/io.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/of_address.h> + +#include <dt-bindings/clock/marvell,pxa910.h> + +#include "clk.h" +#include "reset.h" + +#define APBC_RTC 0x28 +#define APBC_TWSI0 0x2c +#define APBC_KPC 0x18 +#define APBC_UART0 0x0 +#define APBC_UART1 0x4 +#define APBC_GPIO 0x8 +#define APBC_PWM0 0xc +#define APBC_PWM1 0x10 +#define APBC_PWM2 0x14 +#define APBC_PWM3 0x18 +#define APBC_SSP0 0x1c +#define APBC_SSP1 0x20 +#define APBC_SSP2 0x4c +#define APBCP_TWSI1 0x28 +#define APBCP_UART2 0x1c +#define APMU_SDH0 0x54 +#define APMU_SDH1 0x58 +#define APMU_USB 0x5c +#define APMU_DISP0 0x4c +#define APMU_CCIC0 0x50 +#define APMU_DFC 0x60 +#define MPMU_UART_PLL 0x14 + +struct pxa910_clk_unit { + struct mmp_clk_unit unit; + void __iomem *mpmu_base; + void __iomem *apmu_base; + void __iomem *apbc_base; + void __iomem *apbcp_base; +}; + +static struct mmp_param_fixed_rate_clk fixed_rate_clks[] = { + {PXA910_CLK_CLK32, "clk32", NULL, CLK_IS_ROOT, 32768}, + {PXA910_CLK_VCTCXO, "vctcxo", NULL, CLK_IS_ROOT, 26000000}, + {PXA910_CLK_PLL1, "pll1", NULL, CLK_IS_ROOT, 624000000}, +}; + +static struct mmp_param_fixed_factor_clk fixed_factor_clks[] = { + {PXA910_CLK_PLL1_2, "pll1_2", "pll1", 1, 2, 0}, + {PXA910_CLK_PLL1_4, "pll1_4", "pll1_2", 1, 2, 0}, + {PXA910_CLK_PLL1_8, "pll1_8", "pll1_4", 1, 2, 0}, + {PXA910_CLK_PLL1_16, "pll1_16", "pll1_8", 1, 2, 0}, + {PXA910_CLK_PLL1_6, "pll1_6", "pll1_2", 1, 3, 0}, + {PXA910_CLK_PLL1_12, "pll1_12", "pll1_6", 1, 2, 0}, + {PXA910_CLK_PLL1_24, "pll1_24", "pll1_12", 1, 2, 0}, + {PXA910_CLK_PLL1_48, "pll1_48", "pll1_24", 1, 2, 0}, + {PXA910_CLK_PLL1_96, "pll1_96", "pll1_48", 1, 2, 0}, + {PXA910_CLK_PLL1_13, "pll1_13", "pll1", 1, 13, 0}, + {PXA910_CLK_PLL1_13_1_5, "pll1_13_1_5", "pll1_13", 2, 3, 0}, + {PXA910_CLK_PLL1_2_1_5, "pll1_2_1_5", "pll1_2", 2, 3, 0}, + {PXA910_CLK_PLL1_3_16, "pll1_3_16", "pll1", 3, 16, 0}, +}; + +static struct mmp_clk_factor_masks uart_factor_masks = { + .factor = 2, + .num_mask = 0x1fff, + .den_mask = 0x1fff, + .num_shift = 16, + .den_shift = 0, +}; + +static struct mmp_clk_factor_tbl uart_factor_tbl[] = { + {.num = 8125, .den = 1536}, /*14.745MHZ */ +}; + +static void pxa910_pll_init(struct pxa910_clk_unit *pxa_unit) +{ + struct clk *clk; + struct mmp_clk_unit *unit = &pxa_unit->unit; + + mmp_register_fixed_rate_clks(unit, fixed_rate_clks, + ARRAY_SIZE(fixed_rate_clks)); + + mmp_register_fixed_factor_clks(unit, fixed_factor_clks, + ARRAY_SIZE(fixed_factor_clks)); + + clk = mmp_clk_register_factor("uart_pll", "pll1_4", + CLK_SET_RATE_PARENT, + pxa_unit->mpmu_base + MPMU_UART_PLL, + &uart_factor_masks, uart_factor_tbl, + ARRAY_SIZE(uart_factor_tbl), NULL); + mmp_clk_add(unit, PXA910_CLK_UART_PLL, clk); +} + +static DEFINE_SPINLOCK(uart0_lock); +static DEFINE_SPINLOCK(uart1_lock); +static DEFINE_SPINLOCK(uart2_lock); +static const char *uart_parent_names[] = {"pll1_3_16", "uart_pll"}; + +static DEFINE_SPINLOCK(ssp0_lock); +static DEFINE_SPINLOCK(ssp1_lock); +static const char *ssp_parent_names[] = {"pll1_96", "pll1_48", "pll1_24", "pll1_12"}; + +static DEFINE_SPINLOCK(reset_lock); + +static struct mmp_param_mux_clk apbc_mux_clks[] = { + {0, "uart0_mux", uart_parent_names, ARRAY_SIZE(uart_parent_names), CLK_SET_RATE_PARENT, APBC_UART0, 4, 3, 0, &uart0_lock}, + {0, "uart1_mux", uart_parent_names, ARRAY_SIZE(uart_parent_names), CLK_SET_RATE_PARENT, APBC_UART1, 4, 3, 0, &uart1_lock}, + {0, "ssp0_mux", ssp_parent_names, ARRAY_SIZE(ssp_parent_names), CLK_SET_RATE_PARENT, APBC_SSP0, 4, 3, 0, &ssp0_lock}, + {0, "ssp1_mux", ssp_parent_names, ARRAY_SIZE(ssp_parent_names), CLK_SET_RATE_PARENT, APBC_SSP1, 4, 3, 0, &ssp1_lock}, +}; + +static struct mmp_param_mux_clk apbcp_mux_clks[] = { + {0, "uart2_mux", uart_parent_names, ARRAY_SIZE(uart_parent_names), CLK_SET_RATE_PARENT, APBCP_UART2, 4, 3, 0, &uart2_lock}, +}; + +static struct mmp_param_gate_clk apbc_gate_clks[] = { + {PXA910_CLK_TWSI0, "twsi0_clk", "pll1_13_1_5", CLK_SET_RATE_PARENT, APBC_TWSI0, 0x3, 0x3, 0x0, 0, &reset_lock}, + {PXA910_CLK_GPIO, "gpio_clk", "vctcxo", CLK_SET_RATE_PARENT, APBC_GPIO, 0x3, 0x3, 0x0, 0, &reset_lock}, + {PXA910_CLK_KPC, "kpc_clk", "clk32", CLK_SET_RATE_PARENT, APBC_KPC, 0x3, 0x3, 0x0, MMP_CLK_GATE_NEED_DELAY, NULL}, + {PXA910_CLK_RTC, "rtc_clk", "clk32", CLK_SET_RATE_PARENT, APBC_RTC, 0x83, 0x83, 0x0, MMP_CLK_GATE_NEED_DELAY, NULL}, + {PXA910_CLK_PWM0, "pwm0_clk", "pll1_48", CLK_SET_RATE_PARENT, APBC_PWM0, 0x3, 0x3, 0x0, 0, &reset_lock}, + {PXA910_CLK_PWM1, "pwm1_clk", "pll1_48", CLK_SET_RATE_PARENT, APBC_PWM1, 0x3, 0x3, 0x0, 0, &reset_lock}, + {PXA910_CLK_PWM2, "pwm2_clk", "pll1_48", CLK_SET_RATE_PARENT, APBC_PWM2, 0x3, 0x3, 0x0, 0, &reset_lock}, + {PXA910_CLK_PWM3, "pwm3_clk", "pll1_48", CLK_SET_RATE_PARENT, APBC_PWM3, 0x3, 0x3, 0x0, 0, &reset_lock}, + /* The gate clocks has mux parent. */ + {PXA910_CLK_UART0, "uart0_clk", "uart0_mux", CLK_SET_RATE_PARENT, APBC_UART0, 0x3, 0x3, 0x0, 0, &uart0_lock}, + {PXA910_CLK_UART1, "uart1_clk", "uart1_mux", CLK_SET_RATE_PARENT, APBC_UART1, 0x3, 0x3, 0x0, 0, &uart1_lock}, + {PXA910_CLK_SSP0, "ssp0_clk", "ssp0_mux", CLK_SET_RATE_PARENT, APBC_SSP0, 0x3, 0x3, 0x0, 0, &ssp0_lock}, + {PXA910_CLK_SSP1, "ssp1_clk", "ssp1_mux", CLK_SET_RATE_PARENT, APBC_SSP1, 0x3, 0x3, 0x0, 0, &ssp1_lock}, +}; + +static struct mmp_param_gate_clk apbcp_gate_clks[] = { + {PXA910_CLK_TWSI1, "twsi1_clk", "pll1_13_1_5", CLK_SET_RATE_PARENT, APBCP_TWSI1, 0x3, 0x3, 0x0, 0, &reset_lock}, + /* The gate clocks has mux parent. */ + {PXA910_CLK_UART2, "uart2_clk", "uart2_mux", CLK_SET_RATE_PARENT, APBCP_UART2, 0x3, 0x3, 0x0, 0, &uart2_lock}, +}; + +static void pxa910_apb_periph_clk_init(struct pxa910_clk_unit *pxa_unit) +{ + struct mmp_clk_unit *unit = &pxa_unit->unit; + + mmp_register_mux_clks(unit, apbc_mux_clks, pxa_unit->apbc_base, + ARRAY_SIZE(apbc_mux_clks)); + + mmp_register_mux_clks(unit, apbcp_mux_clks, pxa_unit->apbcp_base, + ARRAY_SIZE(apbcp_mux_clks)); + + mmp_register_gate_clks(unit, apbc_gate_clks, pxa_unit->apbc_base, + ARRAY_SIZE(apbc_gate_clks)); + + mmp_register_gate_clks(unit, apbcp_gate_clks, pxa_unit->apbcp_base, + ARRAY_SIZE(apbcp_gate_clks)); +} + +static DEFINE_SPINLOCK(sdh0_lock); +static DEFINE_SPINLOCK(sdh1_lock); +static const char *sdh_parent_names[] = {"pll1_12", "pll1_13"}; + +static DEFINE_SPINLOCK(usb_lock); + +static DEFINE_SPINLOCK(disp0_lock); +static const char *disp_parent_names[] = {"pll1_2", "pll1_12"}; + +static DEFINE_SPINLOCK(ccic0_lock); +static const char *ccic_parent_names[] = {"pll1_2", "pll1_12"}; +static const char *ccic_phy_parent_names[] = {"pll1_6", "pll1_12"}; + +static struct mmp_param_mux_clk apmu_mux_clks[] = { + {0, "sdh0_mux", sdh_parent_names, ARRAY_SIZE(sdh_parent_names), CLK_SET_RATE_PARENT, APMU_SDH0, 6, 1, 0, &sdh0_lock}, + {0, "sdh1_mux", sdh_parent_names, ARRAY_SIZE(sdh_parent_names), CLK_SET_RATE_PARENT, APMU_SDH1, 6, 1, 0, &sdh1_lock}, + {0, "disp0_mux", disp_parent_names, ARRAY_SIZE(disp_parent_names), CLK_SET_RATE_PARENT, APMU_DISP0, 6, 1, 0, &disp0_lock}, + {0, "ccic0_mux", ccic_parent_names, ARRAY_SIZE(ccic_parent_names), CLK_SET_RATE_PARENT, APMU_CCIC0, 6, 1, 0, &ccic0_lock}, + {0, "ccic0_phy_mux", ccic_phy_parent_names, ARRAY_SIZE(ccic_phy_parent_names), CLK_SET_RATE_PARENT, APMU_CCIC0, 7, 1, 0, &ccic0_lock}, +}; + +static struct mmp_param_div_clk apmu_div_clks[] = { + {0, "ccic0_sphy_div", "ccic0_mux", CLK_SET_RATE_PARENT, APMU_CCIC0, 10, 5, 0, &ccic0_lock}, +}; + +static struct mmp_param_gate_clk apmu_gate_clks[] = { + {PXA910_CLK_DFC, "dfc_clk", "pll1_4", CLK_SET_RATE_PARENT, APMU_DFC, 0x19b, 0x19b, 0x0, 0, NULL}, + {PXA910_CLK_USB, "usb_clk", "usb_pll", 0, APMU_USB, 0x9, 0x9, 0x0, 0, &usb_lock}, + {PXA910_CLK_SPH, "sph_clk", "usb_pll", 0, APMU_USB, 0x12, 0x12, 0x0, 0, &usb_lock}, + /* The gate clocks has mux parent. */ + {PXA910_CLK_SDH0, "sdh0_clk", "sdh0_mux", CLK_SET_RATE_PARENT, APMU_SDH0, 0x1b, 0x1b, 0x0, 0, &sdh0_lock}, + {PXA910_CLK_SDH1, "sdh1_clk", "sdh1_mux", CLK_SET_RATE_PARENT, APMU_SDH1, 0x1b, 0x1b, 0x0, 0, &sdh1_lock}, + {PXA910_CLK_DISP0, "disp0_clk", "disp0_mux", CLK_SET_RATE_PARENT, APMU_DISP0, 0x1b, 0x1b, 0x0, 0, &disp0_lock}, + {PXA910_CLK_CCIC0, "ccic0_clk", "ccic0_mux", CLK_SET_RATE_PARENT, APMU_CCIC0, 0x1b, 0x1b, 0x0, 0, &ccic0_lock}, + {PXA910_CLK_CCIC0_PHY, "ccic0_phy_clk", "ccic0_phy_mux", CLK_SET_RATE_PARENT, APMU_CCIC0, 0x24, 0x24, 0x0, 0, &ccic0_lock}, + {PXA910_CLK_CCIC0_SPHY, "ccic0_sphy_clk", "ccic0_sphy_div", CLK_SET_RATE_PARENT, APMU_CCIC0, 0x300, 0x300, 0x0, 0, &ccic0_lock}, +}; + +static void pxa910_axi_periph_clk_init(struct pxa910_clk_unit *pxa_unit) +{ + struct mmp_clk_unit *unit = &pxa_unit->unit; + + mmp_register_mux_clks(unit, apmu_mux_clks, pxa_unit->apmu_base, + ARRAY_SIZE(apmu_mux_clks)); + + mmp_register_div_clks(unit, apmu_div_clks, pxa_unit->apmu_base, + ARRAY_SIZE(apmu_div_clks)); + + mmp_register_gate_clks(unit, apmu_gate_clks, pxa_unit->apmu_base, + ARRAY_SIZE(apmu_gate_clks)); +} + +static void pxa910_clk_reset_init(struct device_node *np, + struct pxa910_clk_unit *pxa_unit) +{ + struct mmp_clk_reset_cell *cells; + int i, base, nr_resets_apbc, nr_resets_apbcp, nr_resets; + + nr_resets_apbc = ARRAY_SIZE(apbc_gate_clks); + nr_resets_apbcp = ARRAY_SIZE(apbcp_gate_clks); + nr_resets = nr_resets_apbc + nr_resets_apbcp; + cells = kcalloc(nr_resets, sizeof(*cells), GFP_KERNEL); + if (!cells) + return; + + base = 0; + for (i = 0; i < nr_resets_apbc; i++) { + cells[base + i].clk_id = apbc_gate_clks[i].id; + cells[base + i].reg = + pxa_unit->apbc_base + apbc_gate_clks[i].offset; + cells[base + i].flags = 0; + cells[base + i].lock = apbc_gate_clks[i].lock; + cells[base + i].bits = 0x4; + } + + base = nr_resets_apbc; + for (i = 0; i < nr_resets_apbcp; i++) { + cells[base + i].clk_id = apbcp_gate_clks[i].id; + cells[base + i].reg = + pxa_unit->apbc_base + apbc_gate_clks[i].offset; + cells[base + i].flags = 0; + cells[base + i].lock = apbc_gate_clks[i].lock; + cells[base + i].bits = 0x4; + } + + mmp_clk_reset_register(np, cells, nr_resets); +} + +static void __init pxa910_clk_init(struct device_node *np) +{ + struct pxa910_clk_unit *pxa_unit; + + pxa_unit = kzalloc(sizeof(*pxa_unit), GFP_KERNEL); + if (!pxa_unit) + return; + + pxa_unit->mpmu_base = of_iomap(np, 0); + if (!pxa_unit->mpmu_base) { + pr_err("failed to map mpmu registers\n"); + return; + } + + pxa_unit->apmu_base = of_iomap(np, 1); + if (!pxa_unit->mpmu_base) { + pr_err("failed to map apmu registers\n"); + return; + } + + pxa_unit->apbc_base = of_iomap(np, 2); + if (!pxa_unit->apbc_base) { + pr_err("failed to map apbc registers\n"); + return; + } + + pxa_unit->apbcp_base = of_iomap(np, 3); + if (!pxa_unit->mpmu_base) { + pr_err("failed to map apbcp registers\n"); + return; + } + + mmp_clk_init(np, &pxa_unit->unit, PXA910_NR_CLKS); + + pxa910_pll_init(pxa_unit); + + pxa910_apb_periph_clk_init(pxa_unit); + + pxa910_axi_periph_clk_init(pxa_unit); + + pxa910_clk_reset_init(np, pxa_unit); +} + +CLK_OF_DECLARE(pxa910_clk, "marvell,pxa910-clock", pxa910_clk_init); diff --git a/drivers/clk/mmp/clk-pxa168.c b/drivers/clk/mmp/clk-pxa168.c index 014396b..93e967c 100644 --- a/drivers/clk/mmp/clk-pxa168.c +++ b/drivers/clk/mmp/clk-pxa168.c @@ -47,7 +47,7 @@ static DEFINE_SPINLOCK(clk_lock); -static struct clk_factor_masks uart_factor_masks = { +static struct mmp_clk_factor_masks uart_factor_masks = { .factor = 2, .num_mask = 0x1fff, .den_mask = 0x1fff, @@ -55,7 +55,7 @@ static struct clk_factor_masks uart_factor_masks = { .den_shift = 0, }; -static struct clk_factor_tbl uart_factor_tbl[] = { +static struct mmp_clk_factor_tbl uart_factor_tbl[] = { {.num = 8125, .den = 1536}, /*14.745MHZ */ }; @@ -158,7 +158,7 @@ void __init pxa168_clk_init(void) uart_pll = mmp_clk_register_factor("uart_pll", "pll1_4", 0, mpmu_base + MPMU_UART_PLL, &uart_factor_masks, uart_factor_tbl, - ARRAY_SIZE(uart_factor_tbl)); + ARRAY_SIZE(uart_factor_tbl), &clk_lock); clk_set_rate(uart_pll, 14745600); clk_register_clkdev(uart_pll, "uart_pll", NULL); diff --git a/drivers/clk/mmp/clk-pxa910.c b/drivers/clk/mmp/clk-pxa910.c index 9efc6a4..993abcd 100644 --- a/drivers/clk/mmp/clk-pxa910.c +++ b/drivers/clk/mmp/clk-pxa910.c @@ -45,7 +45,7 @@ static DEFINE_SPINLOCK(clk_lock); -static struct clk_factor_masks uart_factor_masks = { +static struct mmp_clk_factor_masks uart_factor_masks = { .factor = 2, .num_mask = 0x1fff, .den_mask = 0x1fff, @@ -53,7 +53,7 @@ static struct clk_factor_masks uart_factor_masks = { .den_shift = 0, }; -static struct clk_factor_tbl uart_factor_tbl[] = { +static struct mmp_clk_factor_tbl uart_factor_tbl[] = { {.num = 8125, .den = 1536}, /*14.745MHZ */ }; @@ -163,7 +163,7 @@ void __init pxa910_clk_init(void) uart_pll = mmp_clk_register_factor("uart_pll", "pll1_4", 0, mpmu_base + MPMU_UART_PLL, &uart_factor_masks, uart_factor_tbl, - ARRAY_SIZE(uart_factor_tbl)); + ARRAY_SIZE(uart_factor_tbl), &clk_lock); clk_set_rate(uart_pll, 14745600); clk_register_clkdev(uart_pll, "uart_pll", NULL); diff --git a/drivers/clk/mmp/clk.c b/drivers/clk/mmp/clk.c new file mode 100644 index 0000000..cf038ef --- /dev/null +++ b/drivers/clk/mmp/clk.c @@ -0,0 +1,192 @@ +#include <linux/io.h> +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/clkdev.h> +#include <linux/of.h> +#include <linux/of_address.h> + +#include "clk.h" + +void mmp_clk_init(struct device_node *np, struct mmp_clk_unit *unit, + int nr_clks) +{ + static struct clk **clk_table; + + clk_table = kcalloc(nr_clks, sizeof(struct clk *), GFP_KERNEL); + if (!clk_table) + return; + + unit->clk_table = clk_table; + unit->nr_clks = nr_clks; + unit->clk_data.clks = clk_table; + unit->clk_data.clk_num = nr_clks; + of_clk_add_provider(np, of_clk_src_onecell_get, &unit->clk_data); +} + +void mmp_register_fixed_rate_clks(struct mmp_clk_unit *unit, + struct mmp_param_fixed_rate_clk *clks, + int size) +{ + int i; + struct clk *clk; + + for (i = 0; i < size; i++) { + clk = clk_register_fixed_rate(NULL, clks[i].name, + clks[i].parent_name, + clks[i].flags, + clks[i].fixed_rate); + if (IS_ERR(clk)) { + pr_err("%s: failed to register clock %s\n", + __func__, clks[i].name); + continue; + } + if (clks[i].id) + unit->clk_table[clks[i].id] = clk; + } +} + +void mmp_register_fixed_factor_clks(struct mmp_clk_unit *unit, + struct mmp_param_fixed_factor_clk *clks, + int size) +{ + struct clk *clk; + int i; + + for (i = 0; i < size; i++) { + clk = clk_register_fixed_factor(NULL, clks[i].name, + clks[i].parent_name, + clks[i].flags, clks[i].mult, + clks[i].div); + if (IS_ERR(clk)) { + pr_err("%s: failed to register clock %s\n", + __func__, clks[i].name); + continue; + } + if (clks[i].id) + unit->clk_table[clks[i].id] = clk; + } +} + +void mmp_register_general_gate_clks(struct mmp_clk_unit *unit, + struct mmp_param_general_gate_clk *clks, + void __iomem *base, int size) +{ + struct clk *clk; + int i; + + for (i = 0; i < size; i++) { + clk = clk_register_gate(NULL, clks[i].name, + clks[i].parent_name, + clks[i].flags, + base + clks[i].offset, + clks[i].bit_idx, + clks[i].gate_flags, + clks[i].lock); + + if (IS_ERR(clk)) { + pr_err("%s: failed to register clock %s\n", + __func__, clks[i].name); + continue; + } + if (clks[i].id) + unit->clk_table[clks[i].id] = clk; + } +} + +void mmp_register_gate_clks(struct mmp_clk_unit *unit, + struct mmp_param_gate_clk *clks, + void __iomem *base, int size) +{ + struct clk *clk; + int i; + + for (i = 0; i < size; i++) { + clk = mmp_clk_register_gate(NULL, clks[i].name, + clks[i].parent_name, + clks[i].flags, + base + clks[i].offset, + clks[i].mask, + clks[i].val_enable, + clks[i].val_disable, + clks[i].gate_flags, + clks[i].lock); + + if (IS_ERR(clk)) { + pr_err("%s: failed to register clock %s\n", + __func__, clks[i].name); + continue; + } + if (clks[i].id) + unit->clk_table[clks[i].id] = clk; + } +} + +void mmp_register_mux_clks(struct mmp_clk_unit *unit, + struct mmp_param_mux_clk *clks, + void __iomem *base, int size) +{ + struct clk *clk; + int i; + + for (i = 0; i < size; i++) { + clk = clk_register_mux(NULL, clks[i].name, + clks[i].parent_name, + clks[i].num_parents, + clks[i].flags, + base + clks[i].offset, + clks[i].shift, + clks[i].width, + clks[i].mux_flags, + clks[i].lock); + + if (IS_ERR(clk)) { + pr_err("%s: failed to register clock %s\n", + __func__, clks[i].name); + continue; + } + if (clks[i].id) + unit->clk_table[clks[i].id] = clk; + } +} + +void mmp_register_div_clks(struct mmp_clk_unit *unit, + struct mmp_param_div_clk *clks, + void __iomem *base, int size) +{ + struct clk *clk; + int i; + + for (i = 0; i < size; i++) { + clk = clk_register_divider(NULL, clks[i].name, + clks[i].parent_name, + clks[i].flags, + base + clks[i].offset, + clks[i].shift, + clks[i].width, + clks[i].div_flags, + clks[i].lock); + + if (IS_ERR(clk)) { + pr_err("%s: failed to register clock %s\n", + __func__, clks[i].name); + continue; + } + if (clks[i].id) + unit->clk_table[clks[i].id] = clk; + } +} + +void mmp_clk_add(struct mmp_clk_unit *unit, unsigned int id, + struct clk *clk) +{ + if (IS_ERR_OR_NULL(clk)) { + pr_err("CLK %d has invalid pointer %p\n", id, clk); + return; + } + if (id > unit->nr_clks) { + pr_err("CLK %d is invalid\n", id); + return; + } + + unit->clk_table[id] = clk; +} diff --git a/drivers/clk/mmp/clk.h b/drivers/clk/mmp/clk.h index ab86dd4..adf9b71 100644 --- a/drivers/clk/mmp/clk.h +++ b/drivers/clk/mmp/clk.h @@ -7,19 +7,123 @@ #define APBC_NO_BUS_CTRL BIT(0) #define APBC_POWER_CTRL BIT(1) -struct clk_factor_masks { - unsigned int factor; - unsigned int num_mask; - unsigned int den_mask; - unsigned int num_shift; - unsigned int den_shift; + +/* Clock type "factor" */ +struct mmp_clk_factor_masks { + unsigned int factor; + unsigned int num_mask; + unsigned int den_mask; + unsigned int num_shift; + unsigned int den_shift; }; -struct clk_factor_tbl { +struct mmp_clk_factor_tbl { unsigned int num; unsigned int den; }; +struct mmp_clk_factor { + struct clk_hw hw; + void __iomem *base; + struct mmp_clk_factor_masks *masks; + struct mmp_clk_factor_tbl *ftbl; + unsigned int ftbl_cnt; + spinlock_t *lock; +}; + +extern struct clk *mmp_clk_register_factor(const char *name, + const char *parent_name, unsigned long flags, + void __iomem *base, struct mmp_clk_factor_masks *masks, + struct mmp_clk_factor_tbl *ftbl, unsigned int ftbl_cnt, + spinlock_t *lock); + +/* Clock type "mix" */ +#define MMP_CLK_BITS_MASK(width, shift) \ + (((1 << (width)) - 1) << (shift)) +#define MMP_CLK_BITS_GET_VAL(data, width, shift) \ + ((data & MMP_CLK_BITS_MASK(width, shift)) >> (shift)) +#define MMP_CLK_BITS_SET_VAL(val, width, shift) \ + (((val) << (shift)) & MMP_CLK_BITS_MASK(width, shift)) + +enum { + MMP_CLK_MIX_TYPE_V1, + MMP_CLK_MIX_TYPE_V2, + MMP_CLK_MIX_TYPE_V3, +}; + +/* The register layout */ +struct mmp_clk_mix_reg_info { + void __iomem *reg_clk_ctrl; + void __iomem *reg_clk_sel; + u8 width_div; + u8 shift_div; + u8 width_mux; + u8 shift_mux; + u8 bit_fc; +}; + +/* The suggested clock table from user. */ +struct mmp_clk_mix_clk_table { + unsigned long rate; + u8 parent_index; + unsigned int divisor; + unsigned int valid; +}; + +struct mmp_clk_mix_config { + struct mmp_clk_mix_reg_info reg_info; + struct mmp_clk_mix_clk_table *table; + unsigned int table_size; + u32 *mux_table; + struct clk_div_table *div_table; + u8 div_flags; + u8 mux_flags; +}; + +struct mmp_clk_mix { + struct clk_hw hw; + struct mmp_clk_mix_reg_info reg_info; + struct mmp_clk_mix_clk_table *table; + u32 *mux_table; + struct clk_div_table *div_table; + unsigned int table_size; + u8 div_flags; + u8 mux_flags; + unsigned int type; + spinlock_t *lock; +}; + +extern const struct clk_ops mmp_clk_mix_ops; +extern struct clk *mmp_clk_register_mix(struct device *dev, + const char *name, + const char **parent_names, + u8 num_parents, + unsigned long flags, + struct mmp_clk_mix_config *config, + spinlock_t *lock); + + +/* Clock type "gate". MMP private gate */ +#define MMP_CLK_GATE_NEED_DELAY BIT(0) + +struct mmp_clk_gate { + struct clk_hw hw; + void __iomem *reg; + u32 mask; + u32 val_enable; + u32 val_disable; + unsigned int flags; + spinlock_t *lock; +}; + +extern const struct clk_ops mmp_clk_gate_ops; +extern struct clk *mmp_clk_register_gate(struct device *dev, const char *name, + const char *parent_name, unsigned long flags, + void __iomem *reg, u32 mask, u32 val_enable, + u32 val_disable, unsigned int gate_flags, + spinlock_t *lock); + + extern struct clk *mmp_clk_register_pll2(const char *name, const char *parent_name, unsigned long flags); extern struct clk *mmp_clk_register_apbc(const char *name, @@ -28,8 +132,108 @@ extern struct clk *mmp_clk_register_apbc(const char *name, extern struct clk *mmp_clk_register_apmu(const char *name, const char *parent_name, void __iomem *base, u32 enable_mask, spinlock_t *lock); -extern struct clk *mmp_clk_register_factor(const char *name, - const char *parent_name, unsigned long flags, - void __iomem *base, struct clk_factor_masks *masks, - struct clk_factor_tbl *ftbl, unsigned int ftbl_cnt); + +struct mmp_clk_unit { + unsigned int nr_clks; + struct clk **clk_table; + struct clk_onecell_data clk_data; +}; + +struct mmp_param_fixed_rate_clk { + unsigned int id; + char *name; + const char *parent_name; + unsigned long flags; + unsigned long fixed_rate; +}; +void mmp_register_fixed_rate_clks(struct mmp_clk_unit *unit, + struct mmp_param_fixed_rate_clk *clks, + int size); + +struct mmp_param_fixed_factor_clk { + unsigned int id; + char *name; + const char *parent_name; + unsigned long mult; + unsigned long div; + unsigned long flags; +}; +void mmp_register_fixed_factor_clks(struct mmp_clk_unit *unit, + struct mmp_param_fixed_factor_clk *clks, + int size); + +struct mmp_param_general_gate_clk { + unsigned int id; + const char *name; + const char *parent_name; + unsigned long flags; + unsigned long offset; + u8 bit_idx; + u8 gate_flags; + spinlock_t *lock; +}; +void mmp_register_general_gate_clks(struct mmp_clk_unit *unit, + struct mmp_param_general_gate_clk *clks, + void __iomem *base, int size); + +struct mmp_param_gate_clk { + unsigned int id; + char *name; + const char *parent_name; + unsigned long flags; + unsigned long offset; + u32 mask; + u32 val_enable; + u32 val_disable; + unsigned int gate_flags; + spinlock_t *lock; +}; +void mmp_register_gate_clks(struct mmp_clk_unit *unit, + struct mmp_param_gate_clk *clks, + void __iomem *base, int size); + +struct mmp_param_mux_clk { + unsigned int id; + char *name; + const char **parent_name; + u8 num_parents; + unsigned long flags; + unsigned long offset; + u8 shift; + u8 width; + u8 mux_flags; + spinlock_t *lock; +}; +void mmp_register_mux_clks(struct mmp_clk_unit *unit, + struct mmp_param_mux_clk *clks, + void __iomem *base, int size); + +struct mmp_param_div_clk { + unsigned int id; + char *name; + const char *parent_name; + unsigned long flags; + unsigned long offset; + u8 shift; + u8 width; + u8 div_flags; + spinlock_t *lock; +}; +void mmp_register_div_clks(struct mmp_clk_unit *unit, + struct mmp_param_div_clk *clks, + void __iomem *base, int size); + +#define DEFINE_MIX_REG_INFO(w_d, s_d, w_m, s_m, fc) \ +{ \ + .width_div = (w_d), \ + .shift_div = (s_d), \ + .width_mux = (w_m), \ + .shift_mux = (s_m), \ + .bit_fc = (fc), \ +} + +void mmp_clk_init(struct device_node *np, struct mmp_clk_unit *unit, + int nr_clks); +void mmp_clk_add(struct mmp_clk_unit *unit, unsigned int id, + struct clk *clk); #endif diff --git a/drivers/clk/mmp/reset.c b/drivers/clk/mmp/reset.c new file mode 100644 index 0000000..b54da1f --- /dev/null +++ b/drivers/clk/mmp/reset.c @@ -0,0 +1,99 @@ +#include <linux/slab.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/reset-controller.h> + +#include "reset.h" + +#define rcdev_to_unit(rcdev) container_of(rcdev, struct mmp_clk_reset_unit, rcdev) + +static int mmp_of_reset_xlate(struct reset_controller_dev *rcdev, + const struct of_phandle_args *reset_spec) +{ + struct mmp_clk_reset_unit *unit = rcdev_to_unit(rcdev); + struct mmp_clk_reset_cell *cell; + int i; + + if (WARN_ON(reset_spec->args_count != rcdev->of_reset_n_cells)) + return -EINVAL; + + for (i = 0; i < rcdev->nr_resets; i++) { + cell = &unit->cells[i]; + if (cell->clk_id == reset_spec->args[0]) + break; + } + + if (i == rcdev->nr_resets) + return -EINVAL; + + return i; +} + +static int mmp_clk_reset_assert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct mmp_clk_reset_unit *unit = rcdev_to_unit(rcdev); + struct mmp_clk_reset_cell *cell; + unsigned long flags = 0; + u32 val; + + cell = &unit->cells[id]; + if (cell->lock) + spin_lock_irqsave(cell->lock, flags); + + val = readl(cell->reg); + val |= cell->bits; + writel(val, cell->reg); + + if (cell->lock) + spin_unlock_irqrestore(cell->lock, flags); + + return 0; +} + +static int mmp_clk_reset_deassert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct mmp_clk_reset_unit *unit = rcdev_to_unit(rcdev); + struct mmp_clk_reset_cell *cell; + unsigned long flags = 0; + u32 val; + + cell = &unit->cells[id]; + if (cell->lock) + spin_lock_irqsave(cell->lock, flags); + + val = readl(cell->reg); + val &= ~cell->bits; + writel(val, cell->reg); + + if (cell->lock) + spin_unlock_irqrestore(cell->lock, flags); + + return 0; +} + +static struct reset_control_ops mmp_clk_reset_ops = { + .assert = mmp_clk_reset_assert, + .deassert = mmp_clk_reset_deassert, +}; + +void mmp_clk_reset_register(struct device_node *np, + struct mmp_clk_reset_cell *cells, int nr_resets) +{ + struct mmp_clk_reset_unit *unit; + + unit = kzalloc(sizeof(*unit), GFP_KERNEL); + if (!unit) + return; + + unit->cells = cells; + unit->rcdev.of_reset_n_cells = 1; + unit->rcdev.nr_resets = nr_resets; + unit->rcdev.ops = &mmp_clk_reset_ops; + unit->rcdev.of_node = np; + unit->rcdev.of_xlate = mmp_of_reset_xlate; + + reset_controller_register(&unit->rcdev); +} diff --git a/drivers/clk/mmp/reset.h b/drivers/clk/mmp/reset.h new file mode 100644 index 0000000..be8b1a7 --- /dev/null +++ b/drivers/clk/mmp/reset.h @@ -0,0 +1,31 @@ +#ifndef __MACH_MMP_CLK_RESET_H +#define __MACH_MMP_CLK_RESET_H + +#include <linux/reset-controller.h> + +#define MMP_RESET_INVERT 1 + +struct mmp_clk_reset_cell { + unsigned int clk_id; + void __iomem *reg; + u32 bits; + unsigned int flags; + spinlock_t *lock; +}; + +struct mmp_clk_reset_unit { + struct reset_controller_dev rcdev; + struct mmp_clk_reset_cell *cells; +}; + +#ifdef CONFIG_RESET_CONTROLLER +void mmp_clk_reset_register(struct device_node *np, + struct mmp_clk_reset_cell *cells, int nr_resets); +#else +static inline void mmp_clk_reset_register(struct device_node *np, + struct mmp_clk_reset_cell *cells, int nr_resets) +{ +} +#endif + +#endif |