xref: /openbmc/linux/drivers/clk/imx/clk-lpcg-scu.c (revision ecc23d0a422a3118fcf6e4f0a46e17a6c2047b02)
12f77296dSAisheng Dong // SPDX-License-Identifier: GPL-2.0+
22f77296dSAisheng Dong /*
32f77296dSAisheng Dong  * Copyright 2018 NXP
42f77296dSAisheng Dong  *	Dong Aisheng <aisheng.dong@nxp.com>
52f77296dSAisheng Dong  */
62f77296dSAisheng Dong 
77d6b5e4fSAnson Huang #include <linux/bits.h>
82f77296dSAisheng Dong #include <linux/clk-provider.h>
9*791bf619SPeng Fan #include <linux/delay.h>
102f77296dSAisheng Dong #include <linux/err.h>
112f77296dSAisheng Dong #include <linux/io.h>
122f77296dSAisheng Dong #include <linux/slab.h>
132f77296dSAisheng Dong #include <linux/spinlock.h>
14*791bf619SPeng Fan #include <linux/units.h>
152f77296dSAisheng Dong 
162f77296dSAisheng Dong #include "clk-scu.h"
172f77296dSAisheng Dong 
182f77296dSAisheng Dong static DEFINE_SPINLOCK(imx_lpcg_scu_lock);
192f77296dSAisheng Dong 
202f77296dSAisheng Dong #define CLK_GATE_SCU_LPCG_MASK		0x3
212f77296dSAisheng Dong #define CLK_GATE_SCU_LPCG_HW_SEL	BIT(0)
222f77296dSAisheng Dong #define CLK_GATE_SCU_LPCG_SW_SEL	BIT(1)
232f77296dSAisheng Dong 
242f77296dSAisheng Dong /*
252f77296dSAisheng Dong  * struct clk_lpcg_scu - Description of LPCG clock
262f77296dSAisheng Dong  *
272f77296dSAisheng Dong  * @hw: clk_hw of this LPCG
282f77296dSAisheng Dong  * @reg: register of this LPCG clock
292f77296dSAisheng Dong  * @bit_idx: bit index of this LPCG clock
302f77296dSAisheng Dong  * @hw_gate: HW auto gate enable
312f77296dSAisheng Dong  *
322f77296dSAisheng Dong  * This structure describes one LPCG clock
332f77296dSAisheng Dong  */
342f77296dSAisheng Dong struct clk_lpcg_scu {
352f77296dSAisheng Dong 	struct clk_hw hw;
362f77296dSAisheng Dong 	void __iomem *reg;
372f77296dSAisheng Dong 	u8 bit_idx;
382f77296dSAisheng Dong 	bool hw_gate;
39ea0c5cbaSDong Aisheng 
40ea0c5cbaSDong Aisheng 	/* for state save&restore */
41ea0c5cbaSDong Aisheng 	u32 state;
422f77296dSAisheng Dong };
432f77296dSAisheng Dong 
442f77296dSAisheng Dong #define to_clk_lpcg_scu(_hw) container_of(_hw, struct clk_lpcg_scu, hw)
452f77296dSAisheng Dong 
46*791bf619SPeng Fan /* e10858 -LPCG clock gating register synchronization errata */
lpcg_e10858_writel(unsigned long rate,void __iomem * reg,u32 val)47*791bf619SPeng Fan static void lpcg_e10858_writel(unsigned long rate, void __iomem *reg, u32 val)
48*791bf619SPeng Fan {
49*791bf619SPeng Fan 	writel(val, reg);
50*791bf619SPeng Fan 
51*791bf619SPeng Fan 	if (rate >= 24 * HZ_PER_MHZ || rate == 0) {
52*791bf619SPeng Fan 		/*
53*791bf619SPeng Fan 		 * The time taken to access the LPCG registers from the AP core
54*791bf619SPeng Fan 		 * through the interconnect is longer than the minimum delay
55*791bf619SPeng Fan 		 * of 4 clock cycles required by the errata.
56*791bf619SPeng Fan 		 * Adding a readl will provide sufficient delay to prevent
57*791bf619SPeng Fan 		 * back-to-back writes.
58*791bf619SPeng Fan 		 */
59*791bf619SPeng Fan 		readl(reg);
60*791bf619SPeng Fan 	} else {
61*791bf619SPeng Fan 		/*
62*791bf619SPeng Fan 		 * For clocks running below 24MHz, wait a minimum of
63*791bf619SPeng Fan 		 * 4 clock cycles.
64*791bf619SPeng Fan 		 */
65*791bf619SPeng Fan 		ndelay(4 * (DIV_ROUND_UP(1000 * HZ_PER_MHZ, rate)));
66*791bf619SPeng Fan 	}
67*791bf619SPeng Fan }
68*791bf619SPeng Fan 
clk_lpcg_scu_enable(struct clk_hw * hw)692f77296dSAisheng Dong static int clk_lpcg_scu_enable(struct clk_hw *hw)
702f77296dSAisheng Dong {
712f77296dSAisheng Dong 	struct clk_lpcg_scu *clk = to_clk_lpcg_scu(hw);
722f77296dSAisheng Dong 	unsigned long flags;
732f77296dSAisheng Dong 	u32 reg, val;
742f77296dSAisheng Dong 
752f77296dSAisheng Dong 	spin_lock_irqsave(&imx_lpcg_scu_lock, flags);
762f77296dSAisheng Dong 
772f77296dSAisheng Dong 	reg = readl_relaxed(clk->reg);
782f77296dSAisheng Dong 	reg &= ~(CLK_GATE_SCU_LPCG_MASK << clk->bit_idx);
792f77296dSAisheng Dong 
802f77296dSAisheng Dong 	val = CLK_GATE_SCU_LPCG_SW_SEL;
812f77296dSAisheng Dong 	if (clk->hw_gate)
822f77296dSAisheng Dong 		val |= CLK_GATE_SCU_LPCG_HW_SEL;
832f77296dSAisheng Dong 
842f77296dSAisheng Dong 	reg |= val << clk->bit_idx;
85*791bf619SPeng Fan 
86*791bf619SPeng Fan 	lpcg_e10858_writel(clk_hw_get_rate(hw), clk->reg, reg);
872f77296dSAisheng Dong 
882f77296dSAisheng Dong 	spin_unlock_irqrestore(&imx_lpcg_scu_lock, flags);
892f77296dSAisheng Dong 
902f77296dSAisheng Dong 	return 0;
912f77296dSAisheng Dong }
922f77296dSAisheng Dong 
clk_lpcg_scu_disable(struct clk_hw * hw)932f77296dSAisheng Dong static void clk_lpcg_scu_disable(struct clk_hw *hw)
942f77296dSAisheng Dong {
952f77296dSAisheng Dong 	struct clk_lpcg_scu *clk = to_clk_lpcg_scu(hw);
962f77296dSAisheng Dong 	unsigned long flags;
972f77296dSAisheng Dong 	u32 reg;
982f77296dSAisheng Dong 
992f77296dSAisheng Dong 	spin_lock_irqsave(&imx_lpcg_scu_lock, flags);
1002f77296dSAisheng Dong 
1012f77296dSAisheng Dong 	reg = readl_relaxed(clk->reg);
1022f77296dSAisheng Dong 	reg &= ~(CLK_GATE_SCU_LPCG_MASK << clk->bit_idx);
103*791bf619SPeng Fan 	lpcg_e10858_writel(clk_hw_get_rate(hw), clk->reg, reg);
1042f77296dSAisheng Dong 
1052f77296dSAisheng Dong 	spin_unlock_irqrestore(&imx_lpcg_scu_lock, flags);
1062f77296dSAisheng Dong }
1072f77296dSAisheng Dong 
1082f77296dSAisheng Dong static const struct clk_ops clk_lpcg_scu_ops = {
1092f77296dSAisheng Dong 	.enable = clk_lpcg_scu_enable,
1102f77296dSAisheng Dong 	.disable = clk_lpcg_scu_disable,
1112f77296dSAisheng Dong };
1122f77296dSAisheng Dong 
__imx_clk_lpcg_scu(struct device * dev,const char * name,const char * parent_name,unsigned long flags,void __iomem * reg,u8 bit_idx,bool hw_gate)113a4bfc85cSDong Aisheng struct clk_hw *__imx_clk_lpcg_scu(struct device *dev, const char *name,
114a4bfc85cSDong Aisheng 				  const char *parent_name, unsigned long flags,
115a4bfc85cSDong Aisheng 				  void __iomem *reg, u8 bit_idx, bool hw_gate)
1162f77296dSAisheng Dong {
1172f77296dSAisheng Dong 	struct clk_lpcg_scu *clk;
1182f77296dSAisheng Dong 	struct clk_init_data init;
1192f77296dSAisheng Dong 	struct clk_hw *hw;
1202f77296dSAisheng Dong 	int ret;
1212f77296dSAisheng Dong 
1222f77296dSAisheng Dong 	clk = kzalloc(sizeof(*clk), GFP_KERNEL);
1232f77296dSAisheng Dong 	if (!clk)
1242f77296dSAisheng Dong 		return ERR_PTR(-ENOMEM);
1252f77296dSAisheng Dong 
1262f77296dSAisheng Dong 	clk->reg = reg;
1272f77296dSAisheng Dong 	clk->bit_idx = bit_idx;
1282f77296dSAisheng Dong 	clk->hw_gate = hw_gate;
1292f77296dSAisheng Dong 
1302f77296dSAisheng Dong 	init.name = name;
1312f77296dSAisheng Dong 	init.ops = &clk_lpcg_scu_ops;
1322f77296dSAisheng Dong 	init.flags = CLK_SET_RATE_PARENT | flags;
1332f77296dSAisheng Dong 	init.parent_names = parent_name ? &parent_name : NULL;
1342f77296dSAisheng Dong 	init.num_parents = parent_name ? 1 : 0;
1352f77296dSAisheng Dong 
1362f77296dSAisheng Dong 	clk->hw.init = &init;
1372f77296dSAisheng Dong 
1382f77296dSAisheng Dong 	hw = &clk->hw;
139a4bfc85cSDong Aisheng 	ret = clk_hw_register(dev, hw);
1402f77296dSAisheng Dong 	if (ret) {
1412f77296dSAisheng Dong 		kfree(clk);
1422f77296dSAisheng Dong 		hw = ERR_PTR(ret);
143054ef44eSJian Dong 		return hw;
1442f77296dSAisheng Dong 	}
1452f77296dSAisheng Dong 
146ea0c5cbaSDong Aisheng 	if (dev)
147ea0c5cbaSDong Aisheng 		dev_set_drvdata(dev, clk);
148ea0c5cbaSDong Aisheng 
1492f77296dSAisheng Dong 	return hw;
1502f77296dSAisheng Dong }
151d5f1e6a2SDong Aisheng 
imx_clk_lpcg_scu_unregister(struct clk_hw * hw)152d5f1e6a2SDong Aisheng void imx_clk_lpcg_scu_unregister(struct clk_hw *hw)
153d5f1e6a2SDong Aisheng {
154d5f1e6a2SDong Aisheng 	struct clk_lpcg_scu *clk = to_clk_lpcg_scu(hw);
155d5f1e6a2SDong Aisheng 
156d5f1e6a2SDong Aisheng 	clk_hw_unregister(&clk->hw);
157d5f1e6a2SDong Aisheng 	kfree(clk);
158d5f1e6a2SDong Aisheng }
159ea0c5cbaSDong Aisheng 
imx_clk_lpcg_scu_suspend(struct device * dev)160ea0c5cbaSDong Aisheng static int __maybe_unused imx_clk_lpcg_scu_suspend(struct device *dev)
161ea0c5cbaSDong Aisheng {
162ea0c5cbaSDong Aisheng 	struct clk_lpcg_scu *clk = dev_get_drvdata(dev);
163ea0c5cbaSDong Aisheng 
164ea0c5cbaSDong Aisheng 	clk->state = readl_relaxed(clk->reg);
165ea0c5cbaSDong Aisheng 	dev_dbg(dev, "save lpcg state 0x%x\n", clk->state);
166ea0c5cbaSDong Aisheng 
167ea0c5cbaSDong Aisheng 	return 0;
168ea0c5cbaSDong Aisheng }
169ea0c5cbaSDong Aisheng 
imx_clk_lpcg_scu_resume(struct device * dev)170ea0c5cbaSDong Aisheng static int __maybe_unused imx_clk_lpcg_scu_resume(struct device *dev)
171ea0c5cbaSDong Aisheng {
172ea0c5cbaSDong Aisheng 	struct clk_lpcg_scu *clk = dev_get_drvdata(dev);
173ea0c5cbaSDong Aisheng 
174ea0c5cbaSDong Aisheng 	writel(clk->state, clk->reg);
175*791bf619SPeng Fan 	lpcg_e10858_writel(0, clk->reg, clk->state);
176ea0c5cbaSDong Aisheng 	dev_dbg(dev, "restore lpcg state 0x%x\n", clk->state);
177ea0c5cbaSDong Aisheng 
178ea0c5cbaSDong Aisheng 	return 0;
179ea0c5cbaSDong Aisheng }
180ea0c5cbaSDong Aisheng 
181ea0c5cbaSDong Aisheng const struct dev_pm_ops imx_clk_lpcg_scu_pm_ops = {
182ea0c5cbaSDong Aisheng 	SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(imx_clk_lpcg_scu_suspend,
183ea0c5cbaSDong Aisheng 				      imx_clk_lpcg_scu_resume)
184ea0c5cbaSDong Aisheng };
185