linux/drivers/clk/mmp/clk-audio.c
<<
>>
Prefs
   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");
 446