7
7
8
8
#include <linux/clk-provider.h>
9
9
#include <linux/device.h>
10
+ #include <linux/io.h>
10
11
#include <linux/mod_devicetable.h>
11
12
#include <linux/module.h>
12
13
#include <linux/of.h>
13
14
#include <linux/platform_device.h>
15
+ #include <linux/pm_runtime.h>
14
16
15
17
#include <dt-bindings/clock/imx8mp-clock.h>
16
18
17
19
#include "clk.h"
18
20
19
21
#define CLKEN0 0x000
20
22
#define CLKEN1 0x004
23
+ #define EARC 0x200
21
24
#define SAI1_MCLK_SEL 0x300
22
25
#define SAI2_MCLK_SEL 0x304
23
26
#define SAI3_MCLK_SEL 0x308
26
29
#define SAI7_MCLK_SEL 0x314
27
30
#define PDM_SEL 0x318
28
31
#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
29
37
30
38
#define SAIn_MCLK1_PARENT (n ) \
31
39
static const struct clk_parent_data \
@@ -182,26 +190,82 @@ static struct clk_imx8mp_audiomix_sel sels[] = {
182
190
CLK_SAIn (7 )
183
191
};
184
192
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
+
185
235
static int clk_imx8mp_audiomix_probe (struct platform_device * pdev )
186
236
{
187
- struct clk_hw_onecell_data * priv ;
237
+ struct clk_imx8mp_audiomix_priv * priv ;
238
+ struct clk_hw_onecell_data * clk_hw_data ;
188
239
struct device * dev = & pdev -> dev ;
189
240
void __iomem * base ;
190
241
struct clk_hw * hw ;
191
- int i ;
242
+ int i , ret ;
192
243
193
244
priv = devm_kzalloc (dev ,
194
- struct_size (priv , hws , IMX8MP_CLK_AUDIOMIX_END ),
245
+ struct_size (priv , clk_data . hws , IMX8MP_CLK_AUDIOMIX_END ),
195
246
GFP_KERNEL );
196
247
if (!priv )
197
248
return - ENOMEM ;
198
249
199
- priv -> num = IMX8MP_CLK_AUDIOMIX_END ;
250
+ clk_hw_data = & priv -> clk_data ;
251
+ clk_hw_data -> num = IMX8MP_CLK_AUDIOMIX_END ;
200
252
201
253
base = devm_platform_ioremap_resource (pdev , 0 );
202
254
if (IS_ERR (base ))
203
255
return PTR_ERR (base );
204
256
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
+
205
269
for (i = 0 ; i < ARRAY_SIZE (sels ); i ++ ) {
206
270
if (sels [i ].num_parents == 1 ) {
207
271
hw = devm_clk_hw_register_gate_parent_data (dev ,
@@ -216,10 +280,12 @@ static int clk_imx8mp_audiomix_probe(struct platform_device *pdev)
216
280
0 , NULL , NULL );
217
281
}
218
282
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
+ }
221
287
222
- priv -> hws [sels [i ].clkid ] = hw ;
288
+ clk_hw_data -> hws [sels [i ].clkid ] = hw ;
223
289
}
224
290
225
291
/* SAI PLL */
@@ -228,39 +294,86 @@ static int clk_imx8mp_audiomix_probe(struct platform_device *pdev)
228
294
ARRAY_SIZE (clk_imx8mp_audiomix_pll_parents ),
229
295
CLK_SET_RATE_NO_REPARENT , base + SAI_PLL_GNRL_CTL ,
230
296
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 ;
232
298
233
299
hw = imx_dev_clk_hw_pll14xx (dev , "sai_pll" , "sai_pll_ref_sel" ,
234
300
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 ;
238
306
239
307
hw = devm_clk_hw_register_mux_parent_data_table (dev ,
240
308
"sai_pll_bypass" , clk_imx8mp_audiomix_pll_bypass_sels ,
241
309
ARRAY_SIZE (clk_imx8mp_audiomix_pll_bypass_sels ),
242
310
CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT ,
243
311
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 ;
247
318
248
319
hw = devm_clk_hw_register_gate (dev , "sai_pll_out" , "sai_pll_bypass" ,
249
320
0 , base + SAI_PLL_GNRL_CTL , 13 ,
250
321
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 ;
254
327
255
328
hw = devm_clk_hw_register_fixed_factor (dev , "sai_pll_out_div2" ,
256
329
"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);
259
359
260
- return devm_of_clk_add_hw_provider (& pdev -> dev , of_clk_hw_onecell_get ,
261
- priv );
360
+ return 0 ;
262
361
}
263
362
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
+
264
377
static const struct of_device_id clk_imx8mp_audiomix_of_match [] = {
265
378
{ .compatible = "fsl,imx8mp-audio-blk-ctrl" },
266
379
{ /* sentinel */ }
@@ -269,9 +382,11 @@ MODULE_DEVICE_TABLE(of, clk_imx8mp_audiomix_of_match);
269
382
270
383
static struct platform_driver clk_imx8mp_audiomix_driver = {
271
384
.probe = clk_imx8mp_audiomix_probe ,
385
+ .remove = clk_imx8mp_audiomix_remove ,
272
386
.driver = {
273
387
.name = "imx8mp-audio-blk-ctrl" ,
274
388
.of_match_table = clk_imx8mp_audiomix_of_match ,
389
+ .pm = & clk_imx8mp_audiomix_pm_ops ,
275
390
},
276
391
};
277
392
0 commit comments