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
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 ) \
3139static 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+
185235static 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+
264377static 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
270383static 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