1 // SPDX-License-Identifier: GPL-2.0+
4 * Dong Aisheng <aisheng.dong@nxp.com>
7 #include <linux/bits.h>
8 #include <linux/clk-provider.h>
11 #include <linux/slab.h>
12 #include <linux/spinlock.h>
16 static DEFINE_SPINLOCK(imx_lpcg_scu_lock
);
18 #define CLK_GATE_SCU_LPCG_MASK 0x3
19 #define CLK_GATE_SCU_LPCG_HW_SEL BIT(0)
20 #define CLK_GATE_SCU_LPCG_SW_SEL BIT(1)
23 * struct clk_lpcg_scu - Description of LPCG clock
25 * @hw: clk_hw of this LPCG
26 * @reg: register of this LPCG clock
27 * @bit_idx: bit index of this LPCG clock
28 * @hw_gate: HW auto gate enable
30 * This structure describes one LPCG clock
38 /* for state save&restore */
42 #define to_clk_lpcg_scu(_hw) container_of(_hw, struct clk_lpcg_scu, hw)
44 static int clk_lpcg_scu_enable(struct clk_hw
*hw
)
46 struct clk_lpcg_scu
*clk
= to_clk_lpcg_scu(hw
);
50 spin_lock_irqsave(&imx_lpcg_scu_lock
, flags
);
52 reg
= readl_relaxed(clk
->reg
);
53 reg
&= ~(CLK_GATE_SCU_LPCG_MASK
<< clk
->bit_idx
);
55 val
= CLK_GATE_SCU_LPCG_SW_SEL
;
57 val
|= CLK_GATE_SCU_LPCG_HW_SEL
;
59 reg
|= val
<< clk
->bit_idx
;
60 writel(reg
, clk
->reg
);
62 spin_unlock_irqrestore(&imx_lpcg_scu_lock
, flags
);
67 static void clk_lpcg_scu_disable(struct clk_hw
*hw
)
69 struct clk_lpcg_scu
*clk
= to_clk_lpcg_scu(hw
);
73 spin_lock_irqsave(&imx_lpcg_scu_lock
, flags
);
75 reg
= readl_relaxed(clk
->reg
);
76 reg
&= ~(CLK_GATE_SCU_LPCG_MASK
<< clk
->bit_idx
);
77 writel(reg
, clk
->reg
);
79 spin_unlock_irqrestore(&imx_lpcg_scu_lock
, flags
);
82 static const struct clk_ops clk_lpcg_scu_ops
= {
83 .enable
= clk_lpcg_scu_enable
,
84 .disable
= clk_lpcg_scu_disable
,
87 struct clk_hw
*__imx_clk_lpcg_scu(struct device
*dev
, const char *name
,
88 const char *parent_name
, unsigned long flags
,
89 void __iomem
*reg
, u8 bit_idx
, bool hw_gate
)
91 struct clk_lpcg_scu
*clk
;
92 struct clk_init_data init
;
96 clk
= kzalloc(sizeof(*clk
), GFP_KERNEL
);
98 return ERR_PTR(-ENOMEM
);
101 clk
->bit_idx
= bit_idx
;
102 clk
->hw_gate
= hw_gate
;
105 init
.ops
= &clk_lpcg_scu_ops
;
106 init
.flags
= CLK_SET_RATE_PARENT
| flags
;
107 init
.parent_names
= parent_name
? &parent_name
: NULL
;
108 init
.num_parents
= parent_name
? 1 : 0;
110 clk
->hw
.init
= &init
;
113 ret
= clk_hw_register(dev
, hw
);
120 dev_set_drvdata(dev
, clk
);
125 void imx_clk_lpcg_scu_unregister(struct clk_hw
*hw
)
127 struct clk_lpcg_scu
*clk
= to_clk_lpcg_scu(hw
);
129 clk_hw_unregister(&clk
->hw
);
133 static int __maybe_unused
imx_clk_lpcg_scu_suspend(struct device
*dev
)
135 struct clk_lpcg_scu
*clk
= dev_get_drvdata(dev
);
137 clk
->state
= readl_relaxed(clk
->reg
);
138 dev_dbg(dev
, "save lpcg state 0x%x\n", clk
->state
);
143 static int __maybe_unused
imx_clk_lpcg_scu_resume(struct device
*dev
)
145 struct clk_lpcg_scu
*clk
= dev_get_drvdata(dev
);
148 * FIXME: Sometimes writes don't work unless the CPU issues
152 writel(clk
->state
, clk
->reg
);
153 writel(clk
->state
, clk
->reg
);
154 dev_dbg(dev
, "restore lpcg state 0x%x\n", clk
->state
);
159 const struct dev_pm_ops imx_clk_lpcg_scu_pm_ops
= {
160 SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(imx_clk_lpcg_scu_suspend
,
161 imx_clk_lpcg_scu_resume
)