| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227 |
- /*
- * Copyright (c) 2015 MediaTek Inc.
- * Author: James Liao <jamesjj.liao@mediatek.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that 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.
- */
- #include <linux/of.h>
- #include <linux/of_address.h>
- #include <linux/slab.h>
- #include "clk-mtk.h"
- #include "clk-mux.h"
- #define WORKAROUND_318_WARNING 1
- struct mtk_mux_upd_data {
- struct clk_hw hw;
- void __iomem *base;
- u32 mux_ofs;
- u32 upd_ofs;
- s8 mux_shift;
- s8 mux_width;
- s8 gate_shift;
- s8 upd_shift;
- spinlock_t *lock;
- };
- static inline struct mtk_mux_upd_data *to_mtk_mux_upd_data(struct clk_hw *hw)
- {
- return container_of(hw, struct mtk_mux_upd_data, hw);
- }
- static int mtk_mux_upd_enable(struct clk_hw *hw)
- {
- struct mtk_mux_upd_data *mux = to_mtk_mux_upd_data(hw);
- u32 val, orig;
- unsigned long flags = 0;
- if (mux->lock)
- spin_lock_irqsave(mux->lock, flags);
- val = clk_readl(mux->base + mux->mux_ofs);
- orig = val;
- val &= ~BIT(mux->gate_shift);
- if (val != orig) {
- clk_writel(val, mux->base + mux->mux_ofs);
- if (mux->upd_shift > 0)
- clk_writel(BIT(mux->upd_shift), mux->base + mux->upd_ofs);
- }
- if (mux->lock)
- spin_unlock_irqrestore(mux->lock, flags);
- return 0;
- }
- static void mtk_mux_upd_disable(struct clk_hw *hw)
- {
- struct mtk_mux_upd_data *mux = to_mtk_mux_upd_data(hw);
- u32 val, orig;
- unsigned long flags = 0;
- if (mux->lock)
- spin_lock_irqsave(mux->lock, flags);
- val = clk_readl(mux->base + mux->mux_ofs);
- orig = val;
- val |= BIT(mux->gate_shift);
- if (val != orig) {
- clk_writel(val, mux->base + mux->mux_ofs);
- if (mux->upd_shift > 0)
- clk_writel(BIT(mux->upd_shift), mux->base + mux->upd_ofs);
- }
- if (mux->lock)
- spin_unlock_irqrestore(mux->lock, flags);
- }
- static int mtk_mux_upd_is_enabled(struct clk_hw *hw)
- {
- struct mtk_mux_upd_data *mux = to_mtk_mux_upd_data(hw);
- if (mux->gate_shift < 0)
- return true;
- return (clk_readl(mux->base + mux->mux_ofs) & BIT(mux->gate_shift)) == 0;
- }
- static u8 mtk_mux_upd_get_parent(struct clk_hw *hw)
- {
- struct mtk_mux_upd_data *mux = to_mtk_mux_upd_data(hw);
- int num_parents = __clk_get_num_parents(hw->clk);
- u32 mask = GENMASK(mux->mux_width - 1, 0);
- u32 val;
- val = clk_readl(mux->base + mux->mux_ofs) >> mux->mux_shift;
- val &= mask;
- if (val >= num_parents)
- return -EINVAL;
- return val;
- }
- static int mtk_mux_upd_set_parent(struct clk_hw *hw, u8 index)
- {
- struct mtk_mux_upd_data *mux = to_mtk_mux_upd_data(hw);
- u32 mask = GENMASK(mux->mux_width - 1, 0);
- u32 val, orig;
- unsigned long flags = 0;
- if (mux->lock)
- spin_lock_irqsave(mux->lock, flags);
- val = clk_readl(mux->base + mux->mux_ofs);
- orig = val;
- val &= ~(mask << mux->mux_shift);
- val |= index << mux->mux_shift;
- if (val != orig) {
- clk_writel(val, mux->base + mux->mux_ofs);
- if (mux->upd_shift > 0)
- clk_writel(BIT(mux->upd_shift), mux->base + mux->upd_ofs);
- }
- if (mux->lock)
- spin_unlock_irqrestore(mux->lock, flags);
- return 0;
- }
- const struct clk_ops mtk_mux_upd_ops = {
- .is_enabled = mtk_mux_upd_is_enabled,
- .get_parent = mtk_mux_upd_get_parent,
- .set_parent = mtk_mux_upd_set_parent,
- };
- const struct clk_ops mtk_mux_upd_gate_ops = {
- .enable = mtk_mux_upd_enable,
- .disable = mtk_mux_upd_disable,
- .is_enabled = mtk_mux_upd_is_enabled,
- .get_parent = mtk_mux_upd_get_parent,
- .set_parent = mtk_mux_upd_set_parent,
- };
- struct clk * __init mtk_clk_register_mux_upd(const struct mtk_mux_upd *mu,
- void __iomem *base, spinlock_t *lock)
- {
- struct clk *clk;
- struct mtk_mux_upd_data *mux;
- struct clk_init_data init;
- mux = kzalloc(sizeof(*mux), GFP_KERNEL);
- if (!mux)
- return ERR_PTR(-ENOMEM);
- init.name = mu->name;
- init.flags = CLK_SET_RATE_PARENT;
- #if WORKAROUND_318_WARNING
- init.parent_names = (const char **)mu->parent_names;
- #else
- init.parent_names = mu->parent_names;
- #endif
- init.num_parents = mu->num_parents;
- if (mu->gate_shift < 0)
- init.ops = &mtk_mux_upd_ops;
- else
- init.ops = &mtk_mux_upd_gate_ops;
- mux->base = base;
- mux->mux_ofs = mu->mux_ofs;
- mux->upd_ofs = mu->upd_ofs;
- mux->mux_shift = mu->mux_shift;
- mux->mux_width = mu->mux_width;
- mux->gate_shift = mu->gate_shift;
- mux->upd_shift = mu->upd_shift;
- mux->lock = lock;
- mux->hw.init = &init;
- clk = clk_register(NULL, &mux->hw);
- if (IS_ERR(clk))
- kfree(mux);
- return clk;
- }
- void __init mtk_clk_register_mux_upds(const struct mtk_mux_upd *mus,
- int num, void __iomem *base, spinlock_t *lock,
- struct clk_onecell_data *clk_data)
- {
- struct clk *clk;
- int i;
- for (i = 0; i < num; i++) {
- const struct mtk_mux_upd *mu = &mus[i];
- clk = mtk_clk_register_mux_upd(mu, base, lock);
- if (IS_ERR(clk)) {
- pr_err("Failed to register clk %s: %ld\n",
- mu->name, PTR_ERR(clk));
- continue;
- }
- if (clk_data)
- clk_data->clks[mu->id] = clk;
- }
- }
|