1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3 * Copyright 2018 NXP
4 * Dong Aisheng <aisheng.dong@nxp.com>
5 */
6
7 #include <linux/bits.h>
8 #include <linux/clk-provider.h>
9 #include <linux/delay.h>
10 #include <linux/err.h>
11 #include <linux/io.h>
12 #include <linux/slab.h>
13 #include <linux/spinlock.h>
14 #include <linux/units.h>
15
16 #include "clk-scu.h"
17
18 static DEFINE_SPINLOCK(imx_lpcg_scu_lock);
19
20 #define CLK_GATE_SCU_LPCG_MASK 0x3
21 #define CLK_GATE_SCU_LPCG_HW_SEL BIT(0)
22 #define CLK_GATE_SCU_LPCG_SW_SEL BIT(1)
23
24 /*
25 * struct clk_lpcg_scu - Description of LPCG clock
26 *
27 * @hw: clk_hw of this LPCG
28 * @reg: register of this LPCG clock
29 * @bit_idx: bit index of this LPCG clock
30 * @hw_gate: HW auto gate enable
31 *
32 * This structure describes one LPCG clock
33 */
34 struct clk_lpcg_scu {
35 struct clk_hw hw;
36 void __iomem *reg;
37 u8 bit_idx;
38 bool hw_gate;
39
40 /* for state save&restore */
41 u32 state;
42 };
43
44 #define to_clk_lpcg_scu(_hw) container_of(_hw, struct clk_lpcg_scu, hw)
45
46 /* e10858 -LPCG clock gating register synchronization errata */
lpcg_e10858_writel(unsigned long rate,void __iomem * reg,u32 val)47 static void lpcg_e10858_writel(unsigned long rate, void __iomem *reg, u32 val)
48 {
49 writel(val, reg);
50
51 if (rate >= 24 * HZ_PER_MHZ || rate == 0) {
52 /*
53 * The time taken to access the LPCG registers from the AP core
54 * through the interconnect is longer than the minimum delay
55 * of 4 clock cycles required by the errata.
56 * Adding a readl will provide sufficient delay to prevent
57 * back-to-back writes.
58 */
59 readl(reg);
60 } else {
61 /*
62 * For clocks running below 24MHz, wait a minimum of
63 * 4 clock cycles.
64 */
65 ndelay(4 * (DIV_ROUND_UP(1000 * HZ_PER_MHZ, rate)));
66 }
67 }
68
clk_lpcg_scu_enable(struct clk_hw * hw)69 static int clk_lpcg_scu_enable(struct clk_hw *hw)
70 {
71 struct clk_lpcg_scu *clk = to_clk_lpcg_scu(hw);
72 unsigned long flags;
73 u32 reg, val;
74
75 spin_lock_irqsave(&imx_lpcg_scu_lock, flags);
76
77 reg = readl_relaxed(clk->reg);
78 reg &= ~(CLK_GATE_SCU_LPCG_MASK << clk->bit_idx);
79
80 val = CLK_GATE_SCU_LPCG_SW_SEL;
81 if (clk->hw_gate)
82 val |= CLK_GATE_SCU_LPCG_HW_SEL;
83
84 reg |= val << clk->bit_idx;
85
86 lpcg_e10858_writel(clk_hw_get_rate(hw), clk->reg, reg);
87
88 spin_unlock_irqrestore(&imx_lpcg_scu_lock, flags);
89
90 return 0;
91 }
92
clk_lpcg_scu_disable(struct clk_hw * hw)93 static void clk_lpcg_scu_disable(struct clk_hw *hw)
94 {
95 struct clk_lpcg_scu *clk = to_clk_lpcg_scu(hw);
96 unsigned long flags;
97 u32 reg;
98
99 spin_lock_irqsave(&imx_lpcg_scu_lock, flags);
100
101 reg = readl_relaxed(clk->reg);
102 reg &= ~(CLK_GATE_SCU_LPCG_MASK << clk->bit_idx);
103 lpcg_e10858_writel(clk_hw_get_rate(hw), clk->reg, reg);
104
105 spin_unlock_irqrestore(&imx_lpcg_scu_lock, flags);
106 }
107
108 static const struct clk_ops clk_lpcg_scu_ops = {
109 .enable = clk_lpcg_scu_enable,
110 .disable = clk_lpcg_scu_disable,
111 };
112
__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)113 struct clk_hw *__imx_clk_lpcg_scu(struct device *dev, const char *name,
114 const char *parent_name, unsigned long flags,
115 void __iomem *reg, u8 bit_idx, bool hw_gate)
116 {
117 struct clk_lpcg_scu *clk;
118 struct clk_init_data init;
119 struct clk_hw *hw;
120 int ret;
121
122 clk = kzalloc(sizeof(*clk), GFP_KERNEL);
123 if (!clk)
124 return ERR_PTR(-ENOMEM);
125
126 clk->reg = reg;
127 clk->bit_idx = bit_idx;
128 clk->hw_gate = hw_gate;
129
130 init.name = name;
131 init.ops = &clk_lpcg_scu_ops;
132 init.flags = CLK_SET_RATE_PARENT | flags;
133 init.parent_names = parent_name ? &parent_name : NULL;
134 init.num_parents = parent_name ? 1 : 0;
135
136 clk->hw.init = &init;
137
138 hw = &clk->hw;
139 ret = clk_hw_register(dev, hw);
140 if (ret) {
141 kfree(clk);
142 hw = ERR_PTR(ret);
143 return hw;
144 }
145
146 if (dev)
147 dev_set_drvdata(dev, clk);
148
149 return hw;
150 }
151
imx_clk_lpcg_scu_unregister(struct clk_hw * hw)152 void imx_clk_lpcg_scu_unregister(struct clk_hw *hw)
153 {
154 struct clk_lpcg_scu *clk = to_clk_lpcg_scu(hw);
155
156 clk_hw_unregister(&clk->hw);
157 kfree(clk);
158 }
159
imx_clk_lpcg_scu_suspend(struct device * dev)160 static int __maybe_unused imx_clk_lpcg_scu_suspend(struct device *dev)
161 {
162 struct clk_lpcg_scu *clk = dev_get_drvdata(dev);
163
164 if (!strncmp("hdmi_lpcg", clk_hw_get_name(&clk->hw), strlen("hdmi_lpcg")))
165 return 0;
166
167 clk->state = readl_relaxed(clk->reg);
168 dev_dbg(dev, "save lpcg state 0x%x\n", clk->state);
169
170 return 0;
171 }
172
imx_clk_lpcg_scu_resume(struct device * dev)173 static int __maybe_unused imx_clk_lpcg_scu_resume(struct device *dev)
174 {
175 struct clk_lpcg_scu *clk = dev_get_drvdata(dev);
176
177 if (!strncmp("hdmi_lpcg", clk_hw_get_name(&clk->hw), strlen("hdmi_lpcg")))
178 return 0;
179
180 writel(clk->state, clk->reg);
181 lpcg_e10858_writel(0, clk->reg, clk->state);
182 dev_dbg(dev, "restore lpcg state 0x%x\n", clk->state);
183
184 return 0;
185 }
186
187 const struct dev_pm_ops imx_clk_lpcg_scu_pm_ops = {
188 SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(imx_clk_lpcg_scu_suspend,
189 imx_clk_lpcg_scu_resume)
190 };
191