Skip to content

Commit 1496dd4

Browse files
TE-N-ShengjiuWangabelvesa
authored andcommitted
clk: imx: imx8mp: Add pm_runtime support for power saving
Add pm_runtime support for power saving. In pm runtime suspend state the registers will be reseted, so add registers save in pm runtime suspend and restore them in pm runtime resume. Signed-off-by: Shengjiu Wang <[email protected]> Reviewed-by: Peng Fan <[email protected]> Reviewed-by: Marc Kleine-Budde <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Abel Vesa <[email protected]>
1 parent 4cece76 commit 1496dd4

File tree

1 file changed

+136
-21
lines changed

1 file changed

+136
-21
lines changed

drivers/clk/imx/clk-imx8mp-audiomix.c

+136-21
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,20 @@
77

88
#include <linux/clk-provider.h>
99
#include <linux/device.h>
10+
#include <linux/io.h>
1011
#include <linux/mod_devicetable.h>
1112
#include <linux/module.h>
1213
#include <linux/of.h>
1314
#include <linux/platform_device.h>
15+
#include <linux/pm_runtime.h>
1416

1517
#include <dt-bindings/clock/imx8mp-clock.h>
1618

1719
#include "clk.h"
1820

1921
#define CLKEN0 0x000
2022
#define CLKEN1 0x004
23+
#define EARC 0x200
2124
#define SAI1_MCLK_SEL 0x300
2225
#define SAI2_MCLK_SEL 0x304
2326
#define SAI3_MCLK_SEL 0x308
@@ -26,6 +29,11 @@
2629
#define SAI7_MCLK_SEL 0x314
2730
#define PDM_SEL 0x318
2831
#define SAI_PLL_GNRL_CTL 0x400
32+
#define SAI_PLL_FDIVL_CTL0 0x404
33+
#define SAI_PLL_FDIVL_CTL1 0x408
34+
#define SAI_PLL_SSCG_CTL 0x40C
35+
#define SAI_PLL_MNIT_CTL 0x410
36+
#define IPG_LP_CTRL 0x504
2937

3038
#define SAIn_MCLK1_PARENT(n) \
3139
static const struct clk_parent_data \
@@ -182,26 +190,82 @@ static struct clk_imx8mp_audiomix_sel sels[] = {
182190
CLK_SAIn(7)
183191
};
184192

193+
static const u16 audiomix_regs[] = {
194+
CLKEN0,
195+
CLKEN1,
196+
EARC,
197+
SAI1_MCLK_SEL,
198+
SAI2_MCLK_SEL,
199+
SAI3_MCLK_SEL,
200+
SAI5_MCLK_SEL,
201+
SAI6_MCLK_SEL,
202+
SAI7_MCLK_SEL,
203+
PDM_SEL,
204+
SAI_PLL_GNRL_CTL,
205+
SAI_PLL_FDIVL_CTL0,
206+
SAI_PLL_FDIVL_CTL1,
207+
SAI_PLL_SSCG_CTL,
208+
SAI_PLL_MNIT_CTL,
209+
IPG_LP_CTRL,
210+
};
211+
212+
struct clk_imx8mp_audiomix_priv {
213+
void __iomem *base;
214+
u32 regs_save[ARRAY_SIZE(audiomix_regs)];
215+
216+
/* Must be last */
217+
struct clk_hw_onecell_data clk_data;
218+
};
219+
220+
static void clk_imx8mp_audiomix_save_restore(struct device *dev, bool save)
221+
{
222+
struct clk_imx8mp_audiomix_priv *priv = dev_get_drvdata(dev);
223+
void __iomem *base = priv->base;
224+
int i;
225+
226+
if (save) {
227+
for (i = 0; i < ARRAY_SIZE(audiomix_regs); i++)
228+
priv->regs_save[i] = readl(base + audiomix_regs[i]);
229+
} else {
230+
for (i = 0; i < ARRAY_SIZE(audiomix_regs); i++)
231+
writel(priv->regs_save[i], base + audiomix_regs[i]);
232+
}
233+
}
234+
185235
static int clk_imx8mp_audiomix_probe(struct platform_device *pdev)
186236
{
187-
struct clk_hw_onecell_data *priv;
237+
struct clk_imx8mp_audiomix_priv *priv;
238+
struct clk_hw_onecell_data *clk_hw_data;
188239
struct device *dev = &pdev->dev;
189240
void __iomem *base;
190241
struct clk_hw *hw;
191-
int i;
242+
int i, ret;
192243

193244
priv = devm_kzalloc(dev,
194-
struct_size(priv, hws, IMX8MP_CLK_AUDIOMIX_END),
245+
struct_size(priv, clk_data.hws, IMX8MP_CLK_AUDIOMIX_END),
195246
GFP_KERNEL);
196247
if (!priv)
197248
return -ENOMEM;
198249

199-
priv->num = IMX8MP_CLK_AUDIOMIX_END;
250+
clk_hw_data = &priv->clk_data;
251+
clk_hw_data->num = IMX8MP_CLK_AUDIOMIX_END;
200252

201253
base = devm_platform_ioremap_resource(pdev, 0);
202254
if (IS_ERR(base))
203255
return PTR_ERR(base);
204256

257+
priv->base = base;
258+
dev_set_drvdata(dev, priv);
259+
260+
/*
261+
* pm_runtime_enable needs to be called before clk register.
262+
* That is to make core->rpm_enabled to be true for clock
263+
* usage.
264+
*/
265+
pm_runtime_get_noresume(dev);
266+
pm_runtime_set_active(dev);
267+
pm_runtime_enable(dev);
268+
205269
for (i = 0; i < ARRAY_SIZE(sels); i++) {
206270
if (sels[i].num_parents == 1) {
207271
hw = devm_clk_hw_register_gate_parent_data(dev,
@@ -216,10 +280,12 @@ static int clk_imx8mp_audiomix_probe(struct platform_device *pdev)
216280
0, NULL, NULL);
217281
}
218282

219-
if (IS_ERR(hw))
220-
return PTR_ERR(hw);
283+
if (IS_ERR(hw)) {
284+
ret = PTR_ERR(hw);
285+
goto err_clk_register;
286+
}
221287

222-
priv->hws[sels[i].clkid] = hw;
288+
clk_hw_data->hws[sels[i].clkid] = hw;
223289
}
224290

225291
/* SAI PLL */
@@ -228,39 +294,86 @@ static int clk_imx8mp_audiomix_probe(struct platform_device *pdev)
228294
ARRAY_SIZE(clk_imx8mp_audiomix_pll_parents),
229295
CLK_SET_RATE_NO_REPARENT, base + SAI_PLL_GNRL_CTL,
230296
0, 2, 0, NULL, NULL);
231-
priv->hws[IMX8MP_CLK_AUDIOMIX_SAI_PLL_REF_SEL] = hw;
297+
clk_hw_data->hws[IMX8MP_CLK_AUDIOMIX_SAI_PLL_REF_SEL] = hw;
232298

233299
hw = imx_dev_clk_hw_pll14xx(dev, "sai_pll", "sai_pll_ref_sel",
234300
base + 0x400, &imx_1443x_pll);
235-
if (IS_ERR(hw))
236-
return PTR_ERR(hw);
237-
priv->hws[IMX8MP_CLK_AUDIOMIX_SAI_PLL] = hw;
301+
if (IS_ERR(hw)) {
302+
ret = PTR_ERR(hw);
303+
goto err_clk_register;
304+
}
305+
clk_hw_data->hws[IMX8MP_CLK_AUDIOMIX_SAI_PLL] = hw;
238306

239307
hw = devm_clk_hw_register_mux_parent_data_table(dev,
240308
"sai_pll_bypass", clk_imx8mp_audiomix_pll_bypass_sels,
241309
ARRAY_SIZE(clk_imx8mp_audiomix_pll_bypass_sels),
242310
CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT,
243311
base + SAI_PLL_GNRL_CTL, 16, 1, 0, NULL, NULL);
244-
if (IS_ERR(hw))
245-
return PTR_ERR(hw);
246-
priv->hws[IMX8MP_CLK_AUDIOMIX_SAI_PLL_BYPASS] = hw;
312+
if (IS_ERR(hw)) {
313+
ret = PTR_ERR(hw);
314+
goto err_clk_register;
315+
}
316+
317+
clk_hw_data->hws[IMX8MP_CLK_AUDIOMIX_SAI_PLL_BYPASS] = hw;
247318

248319
hw = devm_clk_hw_register_gate(dev, "sai_pll_out", "sai_pll_bypass",
249320
0, base + SAI_PLL_GNRL_CTL, 13,
250321
0, NULL);
251-
if (IS_ERR(hw))
252-
return PTR_ERR(hw);
253-
priv->hws[IMX8MP_CLK_AUDIOMIX_SAI_PLL_OUT] = hw;
322+
if (IS_ERR(hw)) {
323+
ret = PTR_ERR(hw);
324+
goto err_clk_register;
325+
}
326+
clk_hw_data->hws[IMX8MP_CLK_AUDIOMIX_SAI_PLL_OUT] = hw;
254327

255328
hw = devm_clk_hw_register_fixed_factor(dev, "sai_pll_out_div2",
256329
"sai_pll_out", 0, 1, 2);
257-
if (IS_ERR(hw))
258-
return PTR_ERR(hw);
330+
if (IS_ERR(hw)) {
331+
ret = PTR_ERR(hw);
332+
goto err_clk_register;
333+
}
334+
335+
ret = devm_of_clk_add_hw_provider(&pdev->dev, of_clk_hw_onecell_get,
336+
clk_hw_data);
337+
if (ret)
338+
goto err_clk_register;
339+
340+
pm_runtime_put_sync(dev);
341+
return 0;
342+
343+
err_clk_register:
344+
pm_runtime_put_sync(dev);
345+
pm_runtime_disable(dev);
346+
return ret;
347+
}
348+
349+
static int clk_imx8mp_audiomix_remove(struct platform_device *pdev)
350+
{
351+
pm_runtime_disable(&pdev->dev);
352+
353+
return 0;
354+
}
355+
356+
static int clk_imx8mp_audiomix_runtime_suspend(struct device *dev)
357+
{
358+
clk_imx8mp_audiomix_save_restore(dev, true);
259359

260-
return devm_of_clk_add_hw_provider(&pdev->dev, of_clk_hw_onecell_get,
261-
priv);
360+
return 0;
262361
}
263362

363+
static int clk_imx8mp_audiomix_runtime_resume(struct device *dev)
364+
{
365+
clk_imx8mp_audiomix_save_restore(dev, false);
366+
367+
return 0;
368+
}
369+
370+
static const struct dev_pm_ops clk_imx8mp_audiomix_pm_ops = {
371+
SET_RUNTIME_PM_OPS(clk_imx8mp_audiomix_runtime_suspend,
372+
clk_imx8mp_audiomix_runtime_resume, NULL)
373+
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
374+
pm_runtime_force_resume)
375+
};
376+
264377
static const struct of_device_id clk_imx8mp_audiomix_of_match[] = {
265378
{ .compatible = "fsl,imx8mp-audio-blk-ctrl" },
266379
{ /* sentinel */ }
@@ -269,9 +382,11 @@ MODULE_DEVICE_TABLE(of, clk_imx8mp_audiomix_of_match);
269382

270383
static struct platform_driver clk_imx8mp_audiomix_driver = {
271384
.probe = clk_imx8mp_audiomix_probe,
385+
.remove = clk_imx8mp_audiomix_remove,
272386
.driver = {
273387
.name = "imx8mp-audio-blk-ctrl",
274388
.of_match_table = clk_imx8mp_audiomix_of_match,
389+
.pm = &clk_imx8mp_audiomix_pm_ops,
275390
},
276391
};
277392

0 commit comments

Comments
 (0)