clk-audio.c (13082B)
1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * MMP Audio Clock Controller driver 4 * 5 * Copyright (C) 2020 Lubomir Rintel <lkundrak@v3.sk> 6 */ 7 8#include <linux/clk-provider.h> 9#include <linux/io.h> 10#include <linux/module.h> 11#include <linux/platform_device.h> 12#include <linux/pm_clock.h> 13#include <linux/pm_runtime.h> 14#include <linux/slab.h> 15#include <dt-bindings/clock/marvell,mmp2-audio.h> 16 17/* Audio Controller Registers */ 18#define SSPA_AUD_CTRL 0x04 19#define SSPA_AUD_PLL_CTRL0 0x08 20#define SSPA_AUD_PLL_CTRL1 0x0c 21 22/* SSPA Audio Control Register */ 23#define SSPA_AUD_CTRL_SYSCLK_SHIFT 0 24#define SSPA_AUD_CTRL_SYSCLK_DIV_SHIFT 1 25#define SSPA_AUD_CTRL_SSPA0_MUX_SHIFT 7 26#define SSPA_AUD_CTRL_SSPA0_SHIFT 8 27#define SSPA_AUD_CTRL_SSPA0_DIV_SHIFT 9 28#define SSPA_AUD_CTRL_SSPA1_SHIFT 16 29#define SSPA_AUD_CTRL_SSPA1_DIV_SHIFT 17 30#define SSPA_AUD_CTRL_SSPA1_MUX_SHIFT 23 31#define SSPA_AUD_CTRL_DIV_MASK 0x7e 32 33/* SSPA Audio PLL Control 0 Register */ 34#define SSPA_AUD_PLL_CTRL0_DIV_OCLK_MODULO_MASK (0x7 << 28) 35#define SSPA_AUD_PLL_CTRL0_DIV_OCLK_MODULO(x) ((x) << 28) 36#define SSPA_AUD_PLL_CTRL0_FRACT_MASK (0xfffff << 8) 37#define SSPA_AUD_PLL_CTRL0_FRACT(x) ((x) << 8) 38#define SSPA_AUD_PLL_CTRL0_ENA_DITHER (1 << 7) 39#define SSPA_AUD_PLL_CTRL0_ICP_2UA (0 << 5) 40#define SSPA_AUD_PLL_CTRL0_ICP_5UA (1 << 5) 41#define SSPA_AUD_PLL_CTRL0_ICP_7UA (2 << 5) 42#define SSPA_AUD_PLL_CTRL0_ICP_10UA (3 << 5) 43#define SSPA_AUD_PLL_CTRL0_DIV_FBCCLK_MASK (0x3 << 3) 44#define SSPA_AUD_PLL_CTRL0_DIV_FBCCLK(x) ((x) << 3) 45#define SSPA_AUD_PLL_CTRL0_DIV_MCLK_MASK (0x1 << 2) 46#define SSPA_AUD_PLL_CTRL0_DIV_MCLK(x) ((x) << 2) 47#define SSPA_AUD_PLL_CTRL0_PD_OVPROT_DIS (1 << 1) 48#define SSPA_AUD_PLL_CTRL0_PU (1 << 0) 49 50/* SSPA Audio PLL Control 1 Register */ 51#define SSPA_AUD_PLL_CTRL1_SEL_FAST_CLK (1 << 24) 52#define SSPA_AUD_PLL_CTRL1_CLK_SEL_MASK (1 << 11) 53#define SSPA_AUD_PLL_CTRL1_CLK_SEL_AUDIO_PLL (1 << 11) 54#define SSPA_AUD_PLL_CTRL1_CLK_SEL_VCXO (0 << 11) 55#define SSPA_AUD_PLL_CTRL1_DIV_OCLK_PATTERN_MASK (0x7ff << 0) 56#define SSPA_AUD_PLL_CTRL1_DIV_OCLK_PATTERN(x) ((x) << 0) 57 58struct mmp2_audio_clk { 59 void __iomem *mmio_base; 60 61 struct clk_hw audio_pll_hw; 62 struct clk_mux sspa_mux; 63 struct clk_mux sspa1_mux; 64 struct clk_divider sysclk_div; 65 struct clk_divider sspa0_div; 66 struct clk_divider sspa1_div; 67 struct clk_gate sysclk_gate; 68 struct clk_gate sspa0_gate; 69 struct clk_gate sspa1_gate; 70 71 u32 aud_ctrl; 72 u32 aud_pll_ctrl0; 73 u32 aud_pll_ctrl1; 74 75 spinlock_t lock; 76 77 /* Must be last */ 78 struct clk_hw_onecell_data clk_data; 79}; 80 81static const struct { 82 unsigned long parent_rate; 83 unsigned long freq_vco; 84 unsigned char mclk; 85 unsigned char fbcclk; 86 unsigned short fract; 87} predivs[] = { 88 { 26000000, 135475200, 0, 0, 0x8a18 }, 89 { 26000000, 147456000, 0, 1, 0x0da1 }, 90 { 38400000, 135475200, 1, 2, 0x8208 }, 91 { 38400000, 147456000, 1, 3, 0xaaaa }, 92}; 93 94static const struct { 95 unsigned char divisor; 96 unsigned char modulo; 97 unsigned char pattern; 98} postdivs[] = { 99 { 1, 3, 0, }, 100 { 2, 5, 0, }, 101 { 4, 0, 0, }, 102 { 6, 1, 1, }, 103 { 8, 1, 0, }, 104 { 9, 1, 2, }, 105 { 12, 2, 1, }, 106 { 16, 2, 0, }, 107 { 18, 2, 2, }, 108 { 24, 4, 1, }, 109 { 36, 4, 2, }, 110 { 48, 6, 1, }, 111 { 72, 6, 2, }, 112}; 113 114static unsigned long audio_pll_recalc_rate(struct clk_hw *hw, 115 unsigned long parent_rate) 116{ 117 struct mmp2_audio_clk *priv = container_of(hw, struct mmp2_audio_clk, audio_pll_hw); 118 unsigned int prediv; 119 unsigned int postdiv; 120 u32 aud_pll_ctrl0; 121 u32 aud_pll_ctrl1; 122 123 aud_pll_ctrl0 = readl(priv->mmio_base + SSPA_AUD_PLL_CTRL0); 124 aud_pll_ctrl0 &= SSPA_AUD_PLL_CTRL0_DIV_OCLK_MODULO_MASK | 125 SSPA_AUD_PLL_CTRL0_FRACT_MASK | 126 SSPA_AUD_PLL_CTRL0_ENA_DITHER | 127 SSPA_AUD_PLL_CTRL0_DIV_FBCCLK_MASK | 128 SSPA_AUD_PLL_CTRL0_DIV_MCLK_MASK | 129 SSPA_AUD_PLL_CTRL0_PU; 130 131 aud_pll_ctrl1 = readl(priv->mmio_base + SSPA_AUD_PLL_CTRL1); 132 aud_pll_ctrl1 &= SSPA_AUD_PLL_CTRL1_CLK_SEL_MASK | 133 SSPA_AUD_PLL_CTRL1_DIV_OCLK_PATTERN_MASK; 134 135 for (prediv = 0; prediv < ARRAY_SIZE(predivs); prediv++) { 136 if (predivs[prediv].parent_rate != parent_rate) 137 continue; 138 for (postdiv = 0; postdiv < ARRAY_SIZE(postdivs); postdiv++) { 139 unsigned long freq; 140 u32 val; 141 142 val = SSPA_AUD_PLL_CTRL0_ENA_DITHER; 143 val |= SSPA_AUD_PLL_CTRL0_PU; 144 val |= SSPA_AUD_PLL_CTRL0_DIV_OCLK_MODULO(postdivs[postdiv].modulo); 145 val |= SSPA_AUD_PLL_CTRL0_FRACT(predivs[prediv].fract); 146 val |= SSPA_AUD_PLL_CTRL0_DIV_FBCCLK(predivs[prediv].fbcclk); 147 val |= SSPA_AUD_PLL_CTRL0_DIV_MCLK(predivs[prediv].mclk); 148 if (val != aud_pll_ctrl0) 149 continue; 150 151 val = SSPA_AUD_PLL_CTRL1_CLK_SEL_AUDIO_PLL; 152 val |= SSPA_AUD_PLL_CTRL1_DIV_OCLK_PATTERN(postdivs[postdiv].pattern); 153 if (val != aud_pll_ctrl1) 154 continue; 155 156 freq = predivs[prediv].freq_vco; 157 freq /= postdivs[postdiv].divisor; 158 return freq; 159 } 160 } 161 162 return 0; 163} 164 165static long audio_pll_round_rate(struct clk_hw *hw, unsigned long rate, 166 unsigned long *parent_rate) 167{ 168 unsigned int prediv; 169 unsigned int postdiv; 170 long rounded = 0; 171 172 for (prediv = 0; prediv < ARRAY_SIZE(predivs); prediv++) { 173 if (predivs[prediv].parent_rate != *parent_rate) 174 continue; 175 for (postdiv = 0; postdiv < ARRAY_SIZE(postdivs); postdiv++) { 176 long freq = predivs[prediv].freq_vco; 177 178 freq /= postdivs[postdiv].divisor; 179 if (freq == rate) 180 return rate; 181 if (freq < rate) 182 continue; 183 if (rounded && freq > rounded) 184 continue; 185 rounded = freq; 186 } 187 } 188 189 return rounded; 190} 191 192static int audio_pll_set_rate(struct clk_hw *hw, unsigned long rate, 193 unsigned long parent_rate) 194{ 195 struct mmp2_audio_clk *priv = container_of(hw, struct mmp2_audio_clk, audio_pll_hw); 196 unsigned int prediv; 197 unsigned int postdiv; 198 unsigned long val; 199 200 for (prediv = 0; prediv < ARRAY_SIZE(predivs); prediv++) { 201 if (predivs[prediv].parent_rate != parent_rate) 202 continue; 203 204 for (postdiv = 0; postdiv < ARRAY_SIZE(postdivs); postdiv++) { 205 if (rate * postdivs[postdiv].divisor != predivs[prediv].freq_vco) 206 continue; 207 208 val = SSPA_AUD_PLL_CTRL0_ENA_DITHER; 209 val |= SSPA_AUD_PLL_CTRL0_PU; 210 val |= SSPA_AUD_PLL_CTRL0_DIV_OCLK_MODULO(postdivs[postdiv].modulo); 211 val |= SSPA_AUD_PLL_CTRL0_FRACT(predivs[prediv].fract); 212 val |= SSPA_AUD_PLL_CTRL0_DIV_FBCCLK(predivs[prediv].fbcclk); 213 val |= SSPA_AUD_PLL_CTRL0_DIV_MCLK(predivs[prediv].mclk); 214 writel(val, priv->mmio_base + SSPA_AUD_PLL_CTRL0); 215 216 val = SSPA_AUD_PLL_CTRL1_CLK_SEL_AUDIO_PLL; 217 val |= SSPA_AUD_PLL_CTRL1_DIV_OCLK_PATTERN(postdivs[postdiv].pattern); 218 writel(val, priv->mmio_base + SSPA_AUD_PLL_CTRL1); 219 220 return 0; 221 } 222 } 223 224 return -ERANGE; 225} 226 227static const struct clk_ops audio_pll_ops = { 228 .recalc_rate = audio_pll_recalc_rate, 229 .round_rate = audio_pll_round_rate, 230 .set_rate = audio_pll_set_rate, 231}; 232 233static int register_clocks(struct mmp2_audio_clk *priv, struct device *dev) 234{ 235 const struct clk_parent_data sspa_mux_parents[] = { 236 { .hw = &priv->audio_pll_hw }, 237 { .fw_name = "i2s0" }, 238 }; 239 const struct clk_parent_data sspa1_mux_parents[] = { 240 { .hw = &priv->audio_pll_hw }, 241 { .fw_name = "i2s1" }, 242 }; 243 int ret; 244 245 priv->audio_pll_hw.init = CLK_HW_INIT_FW_NAME("audio_pll", 246 "vctcxo", &audio_pll_ops, 247 CLK_SET_RATE_PARENT); 248 ret = devm_clk_hw_register(dev, &priv->audio_pll_hw); 249 if (ret) 250 return ret; 251 252 priv->sspa_mux.hw.init = CLK_HW_INIT_PARENTS_DATA("sspa_mux", 253 sspa_mux_parents, &clk_mux_ops, 254 CLK_SET_RATE_PARENT); 255 priv->sspa_mux.reg = priv->mmio_base + SSPA_AUD_CTRL; 256 priv->sspa_mux.mask = 1; 257 priv->sspa_mux.shift = SSPA_AUD_CTRL_SSPA0_MUX_SHIFT; 258 ret = devm_clk_hw_register(dev, &priv->sspa_mux.hw); 259 if (ret) 260 return ret; 261 262 priv->sysclk_div.hw.init = CLK_HW_INIT_HW("sys_div", 263 &priv->sspa_mux.hw, &clk_divider_ops, 264 CLK_SET_RATE_PARENT); 265 priv->sysclk_div.reg = priv->mmio_base + SSPA_AUD_CTRL; 266 priv->sysclk_div.shift = SSPA_AUD_CTRL_SYSCLK_DIV_SHIFT; 267 priv->sysclk_div.width = 6; 268 priv->sysclk_div.flags = CLK_DIVIDER_ONE_BASED; 269 priv->sysclk_div.flags |= CLK_DIVIDER_ROUND_CLOSEST; 270 priv->sysclk_div.flags |= CLK_DIVIDER_ALLOW_ZERO; 271 ret = devm_clk_hw_register(dev, &priv->sysclk_div.hw); 272 if (ret) 273 return ret; 274 275 priv->sysclk_gate.hw.init = CLK_HW_INIT_HW("sys_clk", 276 &priv->sysclk_div.hw, &clk_gate_ops, 277 CLK_SET_RATE_PARENT); 278 priv->sysclk_gate.reg = priv->mmio_base + SSPA_AUD_CTRL; 279 priv->sysclk_gate.bit_idx = SSPA_AUD_CTRL_SYSCLK_SHIFT; 280 ret = devm_clk_hw_register(dev, &priv->sysclk_gate.hw); 281 if (ret) 282 return ret; 283 284 priv->sspa0_div.hw.init = CLK_HW_INIT_HW("sspa0_div", 285 &priv->sspa_mux.hw, &clk_divider_ops, 0); 286 priv->sspa0_div.reg = priv->mmio_base + SSPA_AUD_CTRL; 287 priv->sspa0_div.shift = SSPA_AUD_CTRL_SSPA0_DIV_SHIFT; 288 priv->sspa0_div.width = 6; 289 priv->sspa0_div.flags = CLK_DIVIDER_ONE_BASED; 290 priv->sspa0_div.flags |= CLK_DIVIDER_ROUND_CLOSEST; 291 priv->sspa0_div.flags |= CLK_DIVIDER_ALLOW_ZERO; 292 ret = devm_clk_hw_register(dev, &priv->sspa0_div.hw); 293 if (ret) 294 return ret; 295 296 priv->sspa0_gate.hw.init = CLK_HW_INIT_HW("sspa0_clk", 297 &priv->sspa0_div.hw, &clk_gate_ops, 298 CLK_SET_RATE_PARENT); 299 priv->sspa0_gate.reg = priv->mmio_base + SSPA_AUD_CTRL; 300 priv->sspa0_gate.bit_idx = SSPA_AUD_CTRL_SSPA0_SHIFT; 301 ret = devm_clk_hw_register(dev, &priv->sspa0_gate.hw); 302 if (ret) 303 return ret; 304 305 priv->sspa1_mux.hw.init = CLK_HW_INIT_PARENTS_DATA("sspa1_mux", 306 sspa1_mux_parents, &clk_mux_ops, 307 CLK_SET_RATE_PARENT); 308 priv->sspa1_mux.reg = priv->mmio_base + SSPA_AUD_CTRL; 309 priv->sspa1_mux.mask = 1; 310 priv->sspa1_mux.shift = SSPA_AUD_CTRL_SSPA1_MUX_SHIFT; 311 ret = devm_clk_hw_register(dev, &priv->sspa1_mux.hw); 312 if (ret) 313 return ret; 314 315 priv->sspa1_div.hw.init = CLK_HW_INIT_HW("sspa1_div", 316 &priv->sspa1_mux.hw, &clk_divider_ops, 0); 317 priv->sspa1_div.reg = priv->mmio_base + SSPA_AUD_CTRL; 318 priv->sspa1_div.shift = SSPA_AUD_CTRL_SSPA1_DIV_SHIFT; 319 priv->sspa1_div.width = 6; 320 priv->sspa1_div.flags = CLK_DIVIDER_ONE_BASED; 321 priv->sspa1_div.flags |= CLK_DIVIDER_ROUND_CLOSEST; 322 priv->sspa1_div.flags |= CLK_DIVIDER_ALLOW_ZERO; 323 ret = devm_clk_hw_register(dev, &priv->sspa1_div.hw); 324 if (ret) 325 return ret; 326 327 priv->sspa1_gate.hw.init = CLK_HW_INIT_HW("sspa1_clk", 328 &priv->sspa1_div.hw, &clk_gate_ops, 329 CLK_SET_RATE_PARENT); 330 priv->sspa1_gate.reg = priv->mmio_base + SSPA_AUD_CTRL; 331 priv->sspa1_gate.bit_idx = SSPA_AUD_CTRL_SSPA1_SHIFT; 332 ret = devm_clk_hw_register(dev, &priv->sspa1_gate.hw); 333 if (ret) 334 return ret; 335 336 priv->clk_data.hws[MMP2_CLK_AUDIO_SYSCLK] = &priv->sysclk_gate.hw; 337 priv->clk_data.hws[MMP2_CLK_AUDIO_SSPA0] = &priv->sspa0_gate.hw; 338 priv->clk_data.hws[MMP2_CLK_AUDIO_SSPA1] = &priv->sspa1_gate.hw; 339 priv->clk_data.num = MMP2_CLK_AUDIO_NR_CLKS; 340 341 return of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get, 342 &priv->clk_data); 343} 344 345static int mmp2_audio_clk_probe(struct platform_device *pdev) 346{ 347 struct mmp2_audio_clk *priv; 348 int ret; 349 350 priv = devm_kzalloc(&pdev->dev, 351 struct_size(priv, clk_data.hws, 352 MMP2_CLK_AUDIO_NR_CLKS), 353 GFP_KERNEL); 354 if (!priv) 355 return -ENOMEM; 356 357 spin_lock_init(&priv->lock); 358 platform_set_drvdata(pdev, priv); 359 360 priv->mmio_base = devm_platform_ioremap_resource(pdev, 0); 361 if (IS_ERR(priv->mmio_base)) 362 return PTR_ERR(priv->mmio_base); 363 364 pm_runtime_enable(&pdev->dev); 365 ret = pm_clk_create(&pdev->dev); 366 if (ret) 367 goto disable_pm_runtime; 368 369 ret = pm_clk_add(&pdev->dev, "audio"); 370 if (ret) 371 goto destroy_pm_clk; 372 373 ret = register_clocks(priv, &pdev->dev); 374 if (ret) 375 goto destroy_pm_clk; 376 377 return 0; 378 379destroy_pm_clk: 380 pm_clk_destroy(&pdev->dev); 381disable_pm_runtime: 382 pm_runtime_disable(&pdev->dev); 383 384 return ret; 385} 386 387static int mmp2_audio_clk_remove(struct platform_device *pdev) 388{ 389 pm_clk_destroy(&pdev->dev); 390 pm_runtime_disable(&pdev->dev); 391 392 return 0; 393} 394 395#ifdef CONFIG_PM 396static int mmp2_audio_clk_suspend(struct device *dev) 397{ 398 struct mmp2_audio_clk *priv = dev_get_drvdata(dev); 399 400 priv->aud_ctrl = readl(priv->mmio_base + SSPA_AUD_CTRL); 401 priv->aud_pll_ctrl0 = readl(priv->mmio_base + SSPA_AUD_PLL_CTRL0); 402 priv->aud_pll_ctrl1 = readl(priv->mmio_base + SSPA_AUD_PLL_CTRL1); 403 pm_clk_suspend(dev); 404 405 return 0; 406} 407 408static int mmp2_audio_clk_resume(struct device *dev) 409{ 410 struct mmp2_audio_clk *priv = dev_get_drvdata(dev); 411 412 pm_clk_resume(dev); 413 writel(priv->aud_ctrl, priv->mmio_base + SSPA_AUD_CTRL); 414 writel(priv->aud_pll_ctrl0, priv->mmio_base + SSPA_AUD_PLL_CTRL0); 415 writel(priv->aud_pll_ctrl1, priv->mmio_base + SSPA_AUD_PLL_CTRL1); 416 417 return 0; 418} 419#endif 420 421static const struct dev_pm_ops mmp2_audio_clk_pm_ops = { 422 SET_RUNTIME_PM_OPS(mmp2_audio_clk_suspend, mmp2_audio_clk_resume, NULL) 423}; 424 425static const struct of_device_id mmp2_audio_clk_of_match[] = { 426 { .compatible = "marvell,mmp2-audio-clock" }, 427 {} 428}; 429 430MODULE_DEVICE_TABLE(of, mmp2_audio_clk_of_match); 431 432static struct platform_driver mmp2_audio_clk_driver = { 433 .driver = { 434 .name = "mmp2-audio-clock", 435 .of_match_table = of_match_ptr(mmp2_audio_clk_of_match), 436 .pm = &mmp2_audio_clk_pm_ops, 437 }, 438 .probe = mmp2_audio_clk_probe, 439 .remove = mmp2_audio_clk_remove, 440}; 441module_platform_driver(mmp2_audio_clk_driver); 442 443MODULE_AUTHOR("Lubomir Rintel <lkundrak@v3.sk>"); 444MODULE_DESCRIPTION("Clock driver for MMP2 Audio subsystem"); 445MODULE_LICENSE("GPL");