14d7dc77bSStephen Boyd // SPDX-License-Identifier: GPL-2.0
24d7dc77bSStephen Boyd // Copyright (c) 2018, The Linux Foundation. All rights reserved.
34d7dc77bSStephen Boyd
44d7dc77bSStephen Boyd #include <linux/kernel.h>
54d7dc77bSStephen Boyd #include <linux/module.h>
64d7dc77bSStephen Boyd #include <linux/init.h>
74d7dc77bSStephen Boyd #include <linux/io.h>
84d7dc77bSStephen Boyd #include <linux/delay.h>
94d7dc77bSStephen Boyd #include <linux/err.h>
104d7dc77bSStephen Boyd #include <linux/clk-provider.h>
114d7dc77bSStephen Boyd #include <linux/spinlock.h>
124d7dc77bSStephen Boyd
134d7dc77bSStephen Boyd #include <asm/krait-l2-accessors.h>
144d7dc77bSStephen Boyd
154d7dc77bSStephen Boyd #include "clk-krait.h"
164d7dc77bSStephen Boyd
174d7dc77bSStephen Boyd /* Secondary and primary muxes share the same cp15 register */
184d7dc77bSStephen Boyd static DEFINE_SPINLOCK(krait_clock_reg_lock);
194d7dc77bSStephen Boyd
204d7dc77bSStephen Boyd #define LPL_SHIFT 8
21898d0d64SAnsuel Smith #define SECCLKAGD BIT(4)
22898d0d64SAnsuel Smith
__krait_mux_set_sel(struct krait_mux_clk * mux,int sel)234d7dc77bSStephen Boyd static void __krait_mux_set_sel(struct krait_mux_clk *mux, int sel)
244d7dc77bSStephen Boyd {
254d7dc77bSStephen Boyd unsigned long flags;
264d7dc77bSStephen Boyd u32 regval;
274d7dc77bSStephen Boyd
284d7dc77bSStephen Boyd spin_lock_irqsave(&krait_clock_reg_lock, flags);
29898d0d64SAnsuel Smith
304d7dc77bSStephen Boyd regval = krait_get_l2_indirect_reg(mux->offset);
31898d0d64SAnsuel Smith
32898d0d64SAnsuel Smith /* apq/ipq8064 Errata: disable sec_src clock gating during switch. */
33898d0d64SAnsuel Smith if (mux->disable_sec_src_gating) {
34898d0d64SAnsuel Smith regval |= SECCLKAGD;
35898d0d64SAnsuel Smith krait_set_l2_indirect_reg(mux->offset, regval);
36898d0d64SAnsuel Smith }
37898d0d64SAnsuel Smith
384d7dc77bSStephen Boyd regval &= ~(mux->mask << mux->shift);
394d7dc77bSStephen Boyd regval |= (sel & mux->mask) << mux->shift;
404d7dc77bSStephen Boyd if (mux->lpl) {
414d7dc77bSStephen Boyd regval &= ~(mux->mask << (mux->shift + LPL_SHIFT));
424d7dc77bSStephen Boyd regval |= (sel & mux->mask) << (mux->shift + LPL_SHIFT);
434d7dc77bSStephen Boyd }
444d7dc77bSStephen Boyd krait_set_l2_indirect_reg(mux->offset, regval);
454d7dc77bSStephen Boyd
46898d0d64SAnsuel Smith /* apq/ipq8064 Errata: re-enabled sec_src clock gating. */
47898d0d64SAnsuel Smith if (mux->disable_sec_src_gating) {
48898d0d64SAnsuel Smith regval &= ~SECCLKAGD;
49898d0d64SAnsuel Smith krait_set_l2_indirect_reg(mux->offset, regval);
50898d0d64SAnsuel Smith }
51898d0d64SAnsuel Smith
524d7dc77bSStephen Boyd /* Wait for switch to complete. */
534d7dc77bSStephen Boyd mb();
544d7dc77bSStephen Boyd udelay(1);
55df83d2c9SAnsuel Smith
56df83d2c9SAnsuel Smith /*
57df83d2c9SAnsuel Smith * Unlock now to make sure the mux register is not
58df83d2c9SAnsuel Smith * modified while switching to the new parent.
59df83d2c9SAnsuel Smith */
60df83d2c9SAnsuel Smith spin_unlock_irqrestore(&krait_clock_reg_lock, flags);
614d7dc77bSStephen Boyd }
624d7dc77bSStephen Boyd
krait_mux_set_parent(struct clk_hw * hw,u8 index)634d7dc77bSStephen Boyd static int krait_mux_set_parent(struct clk_hw *hw, u8 index)
644d7dc77bSStephen Boyd {
654d7dc77bSStephen Boyd struct krait_mux_clk *mux = to_krait_mux_clk(hw);
664d7dc77bSStephen Boyd u32 sel;
674d7dc77bSStephen Boyd
68bb5c4a85SStephen Boyd sel = clk_mux_index_to_val(mux->parent_map, 0, index);
694d7dc77bSStephen Boyd mux->en_mask = sel;
704d7dc77bSStephen Boyd /* Don't touch mux if CPU is off as it won't work */
714d7dc77bSStephen Boyd if (__clk_is_enabled(hw->clk))
724d7dc77bSStephen Boyd __krait_mux_set_sel(mux, sel);
734d7dc77bSStephen Boyd
7477612720SSricharan R mux->reparent = true;
7577612720SSricharan R
764d7dc77bSStephen Boyd return 0;
774d7dc77bSStephen Boyd }
784d7dc77bSStephen Boyd
krait_mux_get_parent(struct clk_hw * hw)794d7dc77bSStephen Boyd static u8 krait_mux_get_parent(struct clk_hw *hw)
804d7dc77bSStephen Boyd {
814d7dc77bSStephen Boyd struct krait_mux_clk *mux = to_krait_mux_clk(hw);
824d7dc77bSStephen Boyd u32 sel;
834d7dc77bSStephen Boyd
844d7dc77bSStephen Boyd sel = krait_get_l2_indirect_reg(mux->offset);
854d7dc77bSStephen Boyd sel >>= mux->shift;
864d7dc77bSStephen Boyd sel &= mux->mask;
874d7dc77bSStephen Boyd mux->en_mask = sel;
884d7dc77bSStephen Boyd
89bb5c4a85SStephen Boyd return clk_mux_val_to_index(hw, mux->parent_map, 0, sel);
904d7dc77bSStephen Boyd }
914d7dc77bSStephen Boyd
924d7dc77bSStephen Boyd const struct clk_ops krait_mux_clk_ops = {
934d7dc77bSStephen Boyd .set_parent = krait_mux_set_parent,
944d7dc77bSStephen Boyd .get_parent = krait_mux_get_parent,
954d7dc77bSStephen Boyd .determine_rate = __clk_mux_determine_rate_closest,
964d7dc77bSStephen Boyd };
974d7dc77bSStephen Boyd EXPORT_SYMBOL_GPL(krait_mux_clk_ops);
984d7dc77bSStephen Boyd
994d7dc77bSStephen Boyd /* The divider can divide by 2, 4, 6 and 8. But we only really need div-2. */
krait_div2_determine_rate(struct clk_hw * hw,struct clk_rate_request * req)100*a7074c3eSLuca Weiss static int krait_div2_determine_rate(struct clk_hw *hw, struct clk_rate_request *req)
1014d7dc77bSStephen Boyd {
102*a7074c3eSLuca Weiss req->best_parent_rate = clk_hw_round_rate(clk_hw_get_parent(hw), req->rate * 2);
103*a7074c3eSLuca Weiss req->rate = DIV_ROUND_UP(req->best_parent_rate, 2);
104*a7074c3eSLuca Weiss return 0;
1054d7dc77bSStephen Boyd }
1064d7dc77bSStephen Boyd
krait_div2_set_rate(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate)1074d7dc77bSStephen Boyd static int krait_div2_set_rate(struct clk_hw *hw, unsigned long rate,
1084d7dc77bSStephen Boyd unsigned long parent_rate)
1094d7dc77bSStephen Boyd {
1104d7dc77bSStephen Boyd struct krait_div2_clk *d = to_krait_div2_clk(hw);
1114d7dc77bSStephen Boyd unsigned long flags;
1124d7dc77bSStephen Boyd u32 val;
1134d7dc77bSStephen Boyd u32 mask = BIT(d->width) - 1;
1144d7dc77bSStephen Boyd
1154d7dc77bSStephen Boyd if (d->lpl)
1164d7dc77bSStephen Boyd mask = mask << (d->shift + LPL_SHIFT) | mask << d->shift;
117d676d3a3SChristian Marangi else
118d676d3a3SChristian Marangi mask <<= d->shift;
1194d7dc77bSStephen Boyd
1204d7dc77bSStephen Boyd spin_lock_irqsave(&krait_clock_reg_lock, flags);
1214d7dc77bSStephen Boyd val = krait_get_l2_indirect_reg(d->offset);
1224d7dc77bSStephen Boyd val &= ~mask;
1234d7dc77bSStephen Boyd krait_set_l2_indirect_reg(d->offset, val);
1244d7dc77bSStephen Boyd spin_unlock_irqrestore(&krait_clock_reg_lock, flags);
1254d7dc77bSStephen Boyd
1264d7dc77bSStephen Boyd return 0;
1274d7dc77bSStephen Boyd }
1284d7dc77bSStephen Boyd
1294d7dc77bSStephen Boyd static unsigned long
krait_div2_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)1304d7dc77bSStephen Boyd krait_div2_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
1314d7dc77bSStephen Boyd {
1324d7dc77bSStephen Boyd struct krait_div2_clk *d = to_krait_div2_clk(hw);
1334d7dc77bSStephen Boyd u32 mask = BIT(d->width) - 1;
1344d7dc77bSStephen Boyd u32 div;
1354d7dc77bSStephen Boyd
1364d7dc77bSStephen Boyd div = krait_get_l2_indirect_reg(d->offset);
1374d7dc77bSStephen Boyd div >>= d->shift;
1384d7dc77bSStephen Boyd div &= mask;
1394d7dc77bSStephen Boyd div = (div + 1) * 2;
1404d7dc77bSStephen Boyd
1414d7dc77bSStephen Boyd return DIV_ROUND_UP(parent_rate, div);
1424d7dc77bSStephen Boyd }
1434d7dc77bSStephen Boyd
1444d7dc77bSStephen Boyd const struct clk_ops krait_div2_clk_ops = {
145*a7074c3eSLuca Weiss .determine_rate = krait_div2_determine_rate,
1464d7dc77bSStephen Boyd .set_rate = krait_div2_set_rate,
1474d7dc77bSStephen Boyd .recalc_rate = krait_div2_recalc_rate,
1484d7dc77bSStephen Boyd };
1494d7dc77bSStephen Boyd EXPORT_SYMBOL_GPL(krait_div2_clk_ops);
150