Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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 <shengjiu.wang@nxp.com>
Reviewed-by: Peng Fan <peng.fan@nxp.com>
Reviewed-by: Marc Kleine-Budde <mkl@pengutronix.de>
Link: https://lore.kernel.org/r/1711026842-7268-1-git-send-email-shengjiu.wang@nxp.com
Signed-off-by: Abel Vesa <abel.vesa@linaro.org>

authored by

Shengjiu Wang and committed by
Abel Vesa
1496dd41 4cece764

+136 -21
+136 -21
drivers/clk/imx/clk-imx8mp-audiomix.c
··· 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 ··· 20 18 21 19 #define CLKEN0 0x000 22 20 #define CLKEN1 0x004 21 + #define EARC 0x200 23 22 #define SAI1_MCLK_SEL 0x300 24 23 #define SAI2_MCLK_SEL 0x304 25 24 #define SAI3_MCLK_SEL 0x308 ··· 29 26 #define SAI7_MCLK_SEL 0x314 30 27 #define PDM_SEL 0x318 31 28 #define SAI_PLL_GNRL_CTL 0x400 29 + #define SAI_PLL_FDIVL_CTL0 0x404 30 + #define SAI_PLL_FDIVL_CTL1 0x408 31 + #define SAI_PLL_SSCG_CTL 0x40C 32 + #define SAI_PLL_MNIT_CTL 0x410 33 + #define IPG_LP_CTRL 0x504 32 34 33 35 #define SAIn_MCLK1_PARENT(n) \ 34 36 static const struct clk_parent_data \ ··· 190 182 CLK_SAIn(7) 191 183 }; 192 184 185 + static const u16 audiomix_regs[] = { 186 + CLKEN0, 187 + CLKEN1, 188 + EARC, 189 + SAI1_MCLK_SEL, 190 + SAI2_MCLK_SEL, 191 + SAI3_MCLK_SEL, 192 + SAI5_MCLK_SEL, 193 + SAI6_MCLK_SEL, 194 + SAI7_MCLK_SEL, 195 + PDM_SEL, 196 + SAI_PLL_GNRL_CTL, 197 + SAI_PLL_FDIVL_CTL0, 198 + SAI_PLL_FDIVL_CTL1, 199 + SAI_PLL_SSCG_CTL, 200 + SAI_PLL_MNIT_CTL, 201 + IPG_LP_CTRL, 202 + }; 203 + 204 + struct clk_imx8mp_audiomix_priv { 205 + void __iomem *base; 206 + u32 regs_save[ARRAY_SIZE(audiomix_regs)]; 207 + 208 + /* Must be last */ 209 + struct clk_hw_onecell_data clk_data; 210 + }; 211 + 212 + static void clk_imx8mp_audiomix_save_restore(struct device *dev, bool save) 213 + { 214 + struct clk_imx8mp_audiomix_priv *priv = dev_get_drvdata(dev); 215 + void __iomem *base = priv->base; 216 + int i; 217 + 218 + if (save) { 219 + for (i = 0; i < ARRAY_SIZE(audiomix_regs); i++) 220 + priv->regs_save[i] = readl(base + audiomix_regs[i]); 221 + } else { 222 + for (i = 0; i < ARRAY_SIZE(audiomix_regs); i++) 223 + writel(priv->regs_save[i], base + audiomix_regs[i]); 224 + } 225 + } 226 + 193 227 static int clk_imx8mp_audiomix_probe(struct platform_device *pdev) 194 228 { 195 - struct clk_hw_onecell_data *priv; 229 + struct clk_imx8mp_audiomix_priv *priv; 230 + struct clk_hw_onecell_data *clk_hw_data; 196 231 struct device *dev = &pdev->dev; 197 232 void __iomem *base; 198 233 struct clk_hw *hw; 199 - int i; 234 + int i, ret; 200 235 201 236 priv = devm_kzalloc(dev, 202 - struct_size(priv, hws, IMX8MP_CLK_AUDIOMIX_END), 237 + struct_size(priv, clk_data.hws, IMX8MP_CLK_AUDIOMIX_END), 203 238 GFP_KERNEL); 204 239 if (!priv) 205 240 return -ENOMEM; 206 241 207 - priv->num = IMX8MP_CLK_AUDIOMIX_END; 242 + clk_hw_data = &priv->clk_data; 243 + clk_hw_data->num = IMX8MP_CLK_AUDIOMIX_END; 208 244 209 245 base = devm_platform_ioremap_resource(pdev, 0); 210 246 if (IS_ERR(base)) 211 247 return PTR_ERR(base); 248 + 249 + priv->base = base; 250 + dev_set_drvdata(dev, priv); 251 + 252 + /* 253 + * pm_runtime_enable needs to be called before clk register. 254 + * That is to make core->rpm_enabled to be true for clock 255 + * usage. 256 + */ 257 + pm_runtime_get_noresume(dev); 258 + pm_runtime_set_active(dev); 259 + pm_runtime_enable(dev); 212 260 213 261 for (i = 0; i < ARRAY_SIZE(sels); i++) { 214 262 if (sels[i].num_parents == 1) { ··· 280 216 0, NULL, NULL); 281 217 } 282 218 283 - if (IS_ERR(hw)) 284 - return PTR_ERR(hw); 219 + if (IS_ERR(hw)) { 220 + ret = PTR_ERR(hw); 221 + goto err_clk_register; 222 + } 285 223 286 - priv->hws[sels[i].clkid] = hw; 224 + clk_hw_data->hws[sels[i].clkid] = hw; 287 225 } 288 226 289 227 /* SAI PLL */ ··· 294 228 ARRAY_SIZE(clk_imx8mp_audiomix_pll_parents), 295 229 CLK_SET_RATE_NO_REPARENT, base + SAI_PLL_GNRL_CTL, 296 230 0, 2, 0, NULL, NULL); 297 - priv->hws[IMX8MP_CLK_AUDIOMIX_SAI_PLL_REF_SEL] = hw; 231 + clk_hw_data->hws[IMX8MP_CLK_AUDIOMIX_SAI_PLL_REF_SEL] = hw; 298 232 299 233 hw = imx_dev_clk_hw_pll14xx(dev, "sai_pll", "sai_pll_ref_sel", 300 234 base + 0x400, &imx_1443x_pll); 301 - if (IS_ERR(hw)) 302 - return PTR_ERR(hw); 303 - priv->hws[IMX8MP_CLK_AUDIOMIX_SAI_PLL] = hw; 235 + if (IS_ERR(hw)) { 236 + ret = PTR_ERR(hw); 237 + goto err_clk_register; 238 + } 239 + clk_hw_data->hws[IMX8MP_CLK_AUDIOMIX_SAI_PLL] = hw; 304 240 305 241 hw = devm_clk_hw_register_mux_parent_data_table(dev, 306 242 "sai_pll_bypass", clk_imx8mp_audiomix_pll_bypass_sels, 307 243 ARRAY_SIZE(clk_imx8mp_audiomix_pll_bypass_sels), 308 244 CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT, 309 245 base + SAI_PLL_GNRL_CTL, 16, 1, 0, NULL, NULL); 310 - if (IS_ERR(hw)) 311 - return PTR_ERR(hw); 312 - priv->hws[IMX8MP_CLK_AUDIOMIX_SAI_PLL_BYPASS] = hw; 246 + if (IS_ERR(hw)) { 247 + ret = PTR_ERR(hw); 248 + goto err_clk_register; 249 + } 250 + 251 + clk_hw_data->hws[IMX8MP_CLK_AUDIOMIX_SAI_PLL_BYPASS] = hw; 313 252 314 253 hw = devm_clk_hw_register_gate(dev, "sai_pll_out", "sai_pll_bypass", 315 254 0, base + SAI_PLL_GNRL_CTL, 13, 316 255 0, NULL); 317 - if (IS_ERR(hw)) 318 - return PTR_ERR(hw); 319 - priv->hws[IMX8MP_CLK_AUDIOMIX_SAI_PLL_OUT] = hw; 256 + if (IS_ERR(hw)) { 257 + ret = PTR_ERR(hw); 258 + goto err_clk_register; 259 + } 260 + clk_hw_data->hws[IMX8MP_CLK_AUDIOMIX_SAI_PLL_OUT] = hw; 320 261 321 262 hw = devm_clk_hw_register_fixed_factor(dev, "sai_pll_out_div2", 322 263 "sai_pll_out", 0, 1, 2); 323 - if (IS_ERR(hw)) 324 - return PTR_ERR(hw); 264 + if (IS_ERR(hw)) { 265 + ret = PTR_ERR(hw); 266 + goto err_clk_register; 267 + } 325 268 326 - return devm_of_clk_add_hw_provider(&pdev->dev, of_clk_hw_onecell_get, 327 - priv); 269 + ret = devm_of_clk_add_hw_provider(&pdev->dev, of_clk_hw_onecell_get, 270 + clk_hw_data); 271 + if (ret) 272 + goto err_clk_register; 273 + 274 + pm_runtime_put_sync(dev); 275 + return 0; 276 + 277 + err_clk_register: 278 + pm_runtime_put_sync(dev); 279 + pm_runtime_disable(dev); 280 + return ret; 328 281 } 282 + 283 + static int clk_imx8mp_audiomix_remove(struct platform_device *pdev) 284 + { 285 + pm_runtime_disable(&pdev->dev); 286 + 287 + return 0; 288 + } 289 + 290 + static int clk_imx8mp_audiomix_runtime_suspend(struct device *dev) 291 + { 292 + clk_imx8mp_audiomix_save_restore(dev, true); 293 + 294 + return 0; 295 + } 296 + 297 + static int clk_imx8mp_audiomix_runtime_resume(struct device *dev) 298 + { 299 + clk_imx8mp_audiomix_save_restore(dev, false); 300 + 301 + return 0; 302 + } 303 + 304 + static const struct dev_pm_ops clk_imx8mp_audiomix_pm_ops = { 305 + SET_RUNTIME_PM_OPS(clk_imx8mp_audiomix_runtime_suspend, 306 + clk_imx8mp_audiomix_runtime_resume, NULL) 307 + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, 308 + pm_runtime_force_resume) 309 + }; 329 310 330 311 static const struct of_device_id clk_imx8mp_audiomix_of_match[] = { 331 312 { .compatible = "fsl,imx8mp-audio-blk-ctrl" }, ··· 382 269 383 270 static struct platform_driver clk_imx8mp_audiomix_driver = { 384 271 .probe = clk_imx8mp_audiomix_probe, 272 + .remove = clk_imx8mp_audiomix_remove, 385 273 .driver = { 386 274 .name = "imx8mp-audio-blk-ctrl", 387 275 .of_match_table = clk_imx8mp_audiomix_of_match, 276 + .pm = &clk_imx8mp_audiomix_pm_ops, 388 277 }, 389 278 }; 390 279