123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266 |
- /*
- * Copyright (c) 2014 Marvell Technology Group Ltd.
- *
- * Alexandre Belloni <alexandre.belloni@free-electrons.com>
- * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program. If not, see <http://www.gnu.org/licenses/>.
- */
- #include <linux/bitops.h>
- #include <linux/clk-provider.h>
- #include <linux/of.h>
- #include <linux/of_address.h>
- #include <linux/slab.h>
- #include <linux/spinlock.h>
- #include "berlin2-div.h"
- /*
- * Clock dividers in Berlin2 SoCs comprise a complex cell to select
- * input pll and divider. The virtual structure as it is used in Marvell
- * BSP code can be seen as:
- *
- * +---+
- * pll0 --------------->| 0 | +---+
- * +---+ |(B)|--+--------------->| 0 | +---+
- * pll1.0 -->| 0 | +-->| 1 | | +--------+ |(E)|----->| 0 | +---+
- * pll1.1 -->| 1 | | +---+ +-->|(C) 1:M |-->| 1 | |(F)|-->|(G)|->
- * ... -->|(A)|--+ | +--------+ +---+ +-->| 1 | +---+
- * ... -->| | +-->|(D) 1:3 |----------+ +---+
- * pll1.N -->| N | +---------
- * +---+
- *
- * (A) input pll clock mux controlled by <PllSelect[1:n]>
- * (B) input pll bypass mux controlled by <PllSwitch>
- * (C) programmable clock divider controlled by <Select[1:n]>
- * (D) constant div-by-3 clock divider
- * (E) programmable clock divider bypass controlled by <Switch>
- * (F) constant div-by-3 clock mux controlled by <D3Switch>
- * (G) clock gate controlled by <Enable>
- *
- * For whatever reason, above control signals come in two flavors:
- * - single register dividers with all bits in one register
- * - shared register dividers with bits spread over multiple registers
- * (including signals for the same cell spread over consecutive registers)
- *
- * Also, clock gate and pll mux is not available on every div cell, so
- * we have to deal with those, too. We reuse common clock composite driver
- * for it.
- */
- #define PLL_SELECT_MASK 0x7
- #define DIV_SELECT_MASK 0x7
- struct berlin2_div {
- struct clk_hw hw;
- void __iomem *base;
- struct berlin2_div_map map;
- spinlock_t *lock;
- };
- #define to_berlin2_div(hw) container_of(hw, struct berlin2_div, hw)
- static u8 clk_div[] = { 1, 2, 4, 6, 8, 12, 1, 1 };
- static int berlin2_div_is_enabled(struct clk_hw *hw)
- {
- struct berlin2_div *div = to_berlin2_div(hw);
- struct berlin2_div_map *map = &div->map;
- u32 reg;
- if (div->lock)
- spin_lock(div->lock);
- reg = readl_relaxed(div->base + map->gate_offs);
- reg >>= map->gate_shift;
- if (div->lock)
- spin_unlock(div->lock);
- return (reg & 0x1);
- }
- static int berlin2_div_enable(struct clk_hw *hw)
- {
- struct berlin2_div *div = to_berlin2_div(hw);
- struct berlin2_div_map *map = &div->map;
- u32 reg;
- if (div->lock)
- spin_lock(div->lock);
- reg = readl_relaxed(div->base + map->gate_offs);
- reg |= BIT(map->gate_shift);
- writel_relaxed(reg, div->base + map->gate_offs);
- if (div->lock)
- spin_unlock(div->lock);
- return 0;
- }
- static void berlin2_div_disable(struct clk_hw *hw)
- {
- struct berlin2_div *div = to_berlin2_div(hw);
- struct berlin2_div_map *map = &div->map;
- u32 reg;
- if (div->lock)
- spin_lock(div->lock);
- reg = readl_relaxed(div->base + map->gate_offs);
- reg &= ~BIT(map->gate_shift);
- writel_relaxed(reg, div->base + map->gate_offs);
- if (div->lock)
- spin_unlock(div->lock);
- }
- static int berlin2_div_set_parent(struct clk_hw *hw, u8 index)
- {
- struct berlin2_div *div = to_berlin2_div(hw);
- struct berlin2_div_map *map = &div->map;
- u32 reg;
- if (div->lock)
- spin_lock(div->lock);
- /* index == 0 is PLL_SWITCH */
- reg = readl_relaxed(div->base + map->pll_switch_offs);
- if (index == 0)
- reg &= ~BIT(map->pll_switch_shift);
- else
- reg |= BIT(map->pll_switch_shift);
- writel_relaxed(reg, div->base + map->pll_switch_offs);
- /* index > 0 is PLL_SELECT */
- if (index > 0) {
- reg = readl_relaxed(div->base + map->pll_select_offs);
- reg &= ~(PLL_SELECT_MASK << map->pll_select_shift);
- reg |= (index - 1) << map->pll_select_shift;
- writel_relaxed(reg, div->base + map->pll_select_offs);
- }
- if (div->lock)
- spin_unlock(div->lock);
- return 0;
- }
- static u8 berlin2_div_get_parent(struct clk_hw *hw)
- {
- struct berlin2_div *div = to_berlin2_div(hw);
- struct berlin2_div_map *map = &div->map;
- u32 reg;
- u8 index = 0;
- if (div->lock)
- spin_lock(div->lock);
- /* PLL_SWITCH == 0 is index 0 */
- reg = readl_relaxed(div->base + map->pll_switch_offs);
- reg &= BIT(map->pll_switch_shift);
- if (reg) {
- reg = readl_relaxed(div->base + map->pll_select_offs);
- reg >>= map->pll_select_shift;
- reg &= PLL_SELECT_MASK;
- index = 1 + reg;
- }
- if (div->lock)
- spin_unlock(div->lock);
- return index;
- }
- static unsigned long berlin2_div_recalc_rate(struct clk_hw *hw,
- unsigned long parent_rate)
- {
- struct berlin2_div *div = to_berlin2_div(hw);
- struct berlin2_div_map *map = &div->map;
- u32 divsw, div3sw, divider = 1;
- if (div->lock)
- spin_lock(div->lock);
- divsw = readl_relaxed(div->base + map->div_switch_offs) &
- (1 << map->div_switch_shift);
- div3sw = readl_relaxed(div->base + map->div3_switch_offs) &
- (1 << map->div3_switch_shift);
- /* constant divide-by-3 (dominant) */
- if (div3sw != 0) {
- divider = 3;
- /* divider can be bypassed with DIV_SWITCH == 0 */
- } else if (divsw == 0) {
- divider = 1;
- /* clock divider determined by DIV_SELECT */
- } else {
- u32 reg;
- reg = readl_relaxed(div->base + map->div_select_offs);
- reg >>= map->div_select_shift;
- reg &= DIV_SELECT_MASK;
- divider = clk_div[reg];
- }
- if (div->lock)
- spin_unlock(div->lock);
- return parent_rate / divider;
- }
- static const struct clk_ops berlin2_div_rate_ops = {
- .recalc_rate = berlin2_div_recalc_rate,
- };
- static const struct clk_ops berlin2_div_gate_ops = {
- .is_enabled = berlin2_div_is_enabled,
- .enable = berlin2_div_enable,
- .disable = berlin2_div_disable,
- };
- static const struct clk_ops berlin2_div_mux_ops = {
- .set_parent = berlin2_div_set_parent,
- .get_parent = berlin2_div_get_parent,
- };
- struct clk_hw * __init
- berlin2_div_register(const struct berlin2_div_map *map,
- void __iomem *base, const char *name, u8 div_flags,
- const char **parent_names, int num_parents,
- unsigned long flags, spinlock_t *lock)
- {
- const struct clk_ops *mux_ops = &berlin2_div_mux_ops;
- const struct clk_ops *rate_ops = &berlin2_div_rate_ops;
- const struct clk_ops *gate_ops = &berlin2_div_gate_ops;
- struct berlin2_div *div;
- div = kzalloc(sizeof(*div), GFP_KERNEL);
- if (!div)
- return ERR_PTR(-ENOMEM);
- /* copy div_map to allow __initconst */
- memcpy(&div->map, map, sizeof(*map));
- div->base = base;
- div->lock = lock;
- if ((div_flags & BERLIN2_DIV_HAS_GATE) == 0)
- gate_ops = NULL;
- if ((div_flags & BERLIN2_DIV_HAS_MUX) == 0)
- mux_ops = NULL;
- return clk_hw_register_composite(NULL, name, parent_names, num_parents,
- &div->hw, mux_ops, &div->hw, rate_ops,
- &div->hw, gate_ops, flags);
- }
|