xref: /openbmc/linux/drivers/clk/qcom/clk-krait.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
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