linux/drivers/clk/st/clk-flexgen.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * clk-flexgen.c
   4 *
   5 * Copyright (C) ST-Microelectronics SA 2013
   6 * Author:  Maxime Coquelin <maxime.coquelin@st.com> for ST-Microelectronics.
   7 */
   8
   9#include <linux/clk.h>
  10#include <linux/clk-provider.h>
  11#include <linux/module.h>
  12#include <linux/slab.h>
  13#include <linux/io.h>
  14#include <linux/err.h>
  15#include <linux/string.h>
  16#include <linux/of.h>
  17#include <linux/of_address.h>
  18
  19struct clkgen_clk_out {
  20        const char *name;
  21        unsigned long flags;
  22};
  23
  24struct clkgen_data {
  25        unsigned long flags;
  26        bool mode;
  27        const struct clkgen_clk_out *outputs;
  28        const unsigned int outputs_nb;
  29};
  30
  31struct flexgen {
  32        struct clk_hw hw;
  33
  34        /* Crossbar */
  35        struct clk_mux mux;
  36        /* Pre-divisor's gate */
  37        struct clk_gate pgate;
  38        /* Pre-divisor */
  39        struct clk_divider pdiv;
  40        /* Final divisor's gate */
  41        struct clk_gate fgate;
  42        /* Final divisor */
  43        struct clk_divider fdiv;
  44        /* Asynchronous mode control */
  45        struct clk_gate sync;
  46        /* hw control flags */
  47        bool control_mode;
  48};
  49
  50#define to_flexgen(_hw) container_of(_hw, struct flexgen, hw)
  51#define to_clk_gate(_hw) container_of(_hw, struct clk_gate, hw)
  52
  53static int flexgen_enable(struct clk_hw *hw)
  54{
  55        struct flexgen *flexgen = to_flexgen(hw);
  56        struct clk_hw *pgate_hw = &flexgen->pgate.hw;
  57        struct clk_hw *fgate_hw = &flexgen->fgate.hw;
  58
  59        __clk_hw_set_clk(pgate_hw, hw);
  60        __clk_hw_set_clk(fgate_hw, hw);
  61
  62        clk_gate_ops.enable(pgate_hw);
  63
  64        clk_gate_ops.enable(fgate_hw);
  65
  66        pr_debug("%s: flexgen output enabled\n", clk_hw_get_name(hw));
  67        return 0;
  68}
  69
  70static void flexgen_disable(struct clk_hw *hw)
  71{
  72        struct flexgen *flexgen = to_flexgen(hw);
  73        struct clk_hw *fgate_hw = &flexgen->fgate.hw;
  74
  75        /* disable only the final gate */
  76        __clk_hw_set_clk(fgate_hw, hw);
  77
  78        clk_gate_ops.disable(fgate_hw);
  79
  80        pr_debug("%s: flexgen output disabled\n", clk_hw_get_name(hw));
  81}
  82
  83static int flexgen_is_enabled(struct clk_hw *hw)
  84{
  85        struct flexgen *flexgen = to_flexgen(hw);
  86        struct clk_hw *fgate_hw = &flexgen->fgate.hw;
  87
  88        __clk_hw_set_clk(fgate_hw, hw);
  89
  90        if (!clk_gate_ops.is_enabled(fgate_hw))
  91                return 0;
  92
  93        return 1;
  94}
  95
  96static u8 flexgen_get_parent(struct clk_hw *hw)
  97{
  98        struct flexgen *flexgen = to_flexgen(hw);
  99        struct clk_hw *mux_hw = &flexgen->mux.hw;
 100
 101        __clk_hw_set_clk(mux_hw, hw);
 102
 103        return clk_mux_ops.get_parent(mux_hw);
 104}
 105
 106static int flexgen_set_parent(struct clk_hw *hw, u8 index)
 107{
 108        struct flexgen *flexgen = to_flexgen(hw);
 109        struct clk_hw *mux_hw = &flexgen->mux.hw;
 110
 111        __clk_hw_set_clk(mux_hw, hw);
 112
 113        return clk_mux_ops.set_parent(mux_hw, index);
 114}
 115
 116static inline unsigned long
 117clk_best_div(unsigned long parent_rate, unsigned long rate)
 118{
 119        return parent_rate / rate + ((rate > (2*(parent_rate % rate))) ? 0 : 1);
 120}
 121
 122static long flexgen_round_rate(struct clk_hw *hw, unsigned long rate,
 123                                   unsigned long *prate)
 124{
 125        unsigned long div;
 126
 127        /* Round div according to exact prate and wished rate */
 128        div = clk_best_div(*prate, rate);
 129
 130        if (clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT) {
 131                *prate = rate * div;
 132                return rate;
 133        }
 134
 135        return *prate / div;
 136}
 137
 138static unsigned long flexgen_recalc_rate(struct clk_hw *hw,
 139                unsigned long parent_rate)
 140{
 141        struct flexgen *flexgen = to_flexgen(hw);
 142        struct clk_hw *pdiv_hw = &flexgen->pdiv.hw;
 143        struct clk_hw *fdiv_hw = &flexgen->fdiv.hw;
 144        unsigned long mid_rate;
 145
 146        __clk_hw_set_clk(pdiv_hw, hw);
 147        __clk_hw_set_clk(fdiv_hw, hw);
 148
 149        mid_rate = clk_divider_ops.recalc_rate(pdiv_hw, parent_rate);
 150
 151        return clk_divider_ops.recalc_rate(fdiv_hw, mid_rate);
 152}
 153
 154static int flexgen_set_rate(struct clk_hw *hw, unsigned long rate,
 155                                unsigned long parent_rate)
 156{
 157        struct flexgen *flexgen = to_flexgen(hw);
 158        struct clk_hw *pdiv_hw = &flexgen->pdiv.hw;
 159        struct clk_hw *fdiv_hw = &flexgen->fdiv.hw;
 160        struct clk_hw *sync_hw = &flexgen->sync.hw;
 161        struct clk_gate *config = to_clk_gate(sync_hw);
 162        unsigned long div = 0;
 163        int ret = 0;
 164        u32 reg;
 165
 166        __clk_hw_set_clk(pdiv_hw, hw);
 167        __clk_hw_set_clk(fdiv_hw, hw);
 168
 169        if (flexgen->control_mode) {
 170                reg = readl(config->reg);
 171                reg &= ~BIT(config->bit_idx);
 172                writel(reg, config->reg);
 173        }
 174
 175        div = clk_best_div(parent_rate, rate);
 176
 177        /*
 178        * pdiv is mainly targeted for low freq results, while fdiv
 179        * should be used for div <= 64. The other way round can
 180        * lead to 'duty cycle' issues.
 181        */
 182
 183        if (div <= 64) {
 184                clk_divider_ops.set_rate(pdiv_hw, parent_rate, parent_rate);
 185                ret = clk_divider_ops.set_rate(fdiv_hw, rate, rate * div);
 186        } else {
 187                clk_divider_ops.set_rate(fdiv_hw, parent_rate, parent_rate);
 188                ret = clk_divider_ops.set_rate(pdiv_hw, rate, rate * div);
 189        }
 190
 191        return ret;
 192}
 193
 194static const struct clk_ops flexgen_ops = {
 195        .enable = flexgen_enable,
 196        .disable = flexgen_disable,
 197        .is_enabled = flexgen_is_enabled,
 198        .get_parent = flexgen_get_parent,
 199        .set_parent = flexgen_set_parent,
 200        .round_rate = flexgen_round_rate,
 201        .recalc_rate = flexgen_recalc_rate,
 202        .set_rate = flexgen_set_rate,
 203};
 204
 205static struct clk *clk_register_flexgen(const char *name,
 206                                const char **parent_names, u8 num_parents,
 207                                void __iomem *reg, spinlock_t *lock, u32 idx,
 208                                unsigned long flexgen_flags, bool mode) {
 209        struct flexgen *fgxbar;
 210        struct clk *clk;
 211        struct clk_init_data init;
 212        u32  xbar_shift;
 213        void __iomem *xbar_reg, *fdiv_reg;
 214
 215        fgxbar = kzalloc(sizeof(struct flexgen), GFP_KERNEL);
 216        if (!fgxbar)
 217                return ERR_PTR(-ENOMEM);
 218
 219        init.name = name;
 220        init.ops = &flexgen_ops;
 221        init.flags = CLK_GET_RATE_NOCACHE | flexgen_flags;
 222        init.parent_names = parent_names;
 223        init.num_parents = num_parents;
 224
 225        xbar_reg = reg + 0x18 + (idx & ~0x3);
 226        xbar_shift = (idx % 4) * 0x8;
 227        fdiv_reg = reg + 0x164 + idx * 4;
 228
 229        /* Crossbar element config */
 230        fgxbar->mux.lock = lock;
 231        fgxbar->mux.mask = BIT(6) - 1;
 232        fgxbar->mux.reg = xbar_reg;
 233        fgxbar->mux.shift = xbar_shift;
 234        fgxbar->mux.table = NULL;
 235
 236
 237        /* Pre-divider's gate config (in xbar register)*/
 238        fgxbar->pgate.lock = lock;
 239        fgxbar->pgate.reg = xbar_reg;
 240        fgxbar->pgate.bit_idx = xbar_shift + 6;
 241
 242        /* Pre-divider config */
 243        fgxbar->pdiv.lock = lock;
 244        fgxbar->pdiv.reg = reg + 0x58 + idx * 4;
 245        fgxbar->pdiv.width = 10;
 246
 247        /* Final divider's gate config */
 248        fgxbar->fgate.lock = lock;
 249        fgxbar->fgate.reg = fdiv_reg;
 250        fgxbar->fgate.bit_idx = 6;
 251
 252        /* Final divider config */
 253        fgxbar->fdiv.lock = lock;
 254        fgxbar->fdiv.reg = fdiv_reg;
 255        fgxbar->fdiv.width = 6;
 256
 257        /* Final divider sync config */
 258        fgxbar->sync.lock = lock;
 259        fgxbar->sync.reg = fdiv_reg;
 260        fgxbar->sync.bit_idx = 7;
 261
 262        fgxbar->control_mode = mode;
 263
 264        fgxbar->hw.init = &init;
 265
 266        clk = clk_register(NULL, &fgxbar->hw);
 267        if (IS_ERR(clk))
 268                kfree(fgxbar);
 269        else
 270                pr_debug("%s: parent %s rate %u\n",
 271                        __clk_get_name(clk),
 272                        __clk_get_name(clk_get_parent(clk)),
 273                        (unsigned int)clk_get_rate(clk));
 274        return clk;
 275}
 276
 277static const char ** __init flexgen_get_parents(struct device_node *np,
 278                                                       int *num_parents)
 279{
 280        const char **parents;
 281        unsigned int nparents;
 282
 283        nparents = of_clk_get_parent_count(np);
 284        if (WARN_ON(!nparents))
 285                return NULL;
 286
 287        parents = kcalloc(nparents, sizeof(const char *), GFP_KERNEL);
 288        if (!parents)
 289                return NULL;
 290
 291        *num_parents = of_clk_parent_fill(np, parents, nparents);
 292
 293        return parents;
 294}
 295
 296static const struct clkgen_data clkgen_audio = {
 297        .flags = CLK_SET_RATE_PARENT,
 298};
 299
 300static const struct clkgen_data clkgen_video = {
 301        .flags = CLK_SET_RATE_PARENT,
 302        .mode = 1,
 303};
 304
 305static const struct clkgen_clk_out clkgen_stih407_a0_clk_out[] = {
 306        /* This clk needs to be on so that memory interface is accessible */
 307        { .name = "clk-ic-lmi0", .flags = CLK_IS_CRITICAL },
 308};
 309
 310static const struct clkgen_data clkgen_stih407_a0 = {
 311        .outputs = clkgen_stih407_a0_clk_out,
 312        .outputs_nb = ARRAY_SIZE(clkgen_stih407_a0_clk_out),
 313};
 314
 315static const struct clkgen_clk_out clkgen_stih410_a0_clk_out[] = {
 316        /* Those clks need to be on so that memory interface is accessible */
 317        { .name = "clk-ic-lmi0", .flags = CLK_IS_CRITICAL },
 318        { .name = "clk-ic-lmi1", .flags = CLK_IS_CRITICAL },
 319};
 320
 321static const struct clkgen_data clkgen_stih410_a0 = {
 322        .outputs = clkgen_stih410_a0_clk_out,
 323        .outputs_nb = ARRAY_SIZE(clkgen_stih410_a0_clk_out),
 324};
 325
 326static const struct clkgen_clk_out clkgen_stih407_c0_clk_out[] = {
 327        { .name = "clk-icn-gpu", },
 328        { .name = "clk-fdma", },
 329        { .name = "clk-nand", },
 330        { .name = "clk-hva", },
 331        { .name = "clk-proc-stfe", },
 332        { .name = "clk-proc-tp", },
 333        { .name = "clk-rx-icn-dmu", },
 334        { .name = "clk-rx-icn-hva", },
 335        /* This clk needs to be on to keep bus interconnect alive */
 336        { .name = "clk-icn-cpu", .flags = CLK_IS_CRITICAL },
 337        /* This clk needs to be on to keep bus interconnect alive */
 338        { .name = "clk-tx-icn-dmu", .flags = CLK_IS_CRITICAL },
 339        { .name = "clk-mmc-0", },
 340        { .name = "clk-mmc-1", },
 341        { .name = "clk-jpegdec", },
 342        /* This clk needs to be on to keep A9 running */
 343        { .name = "clk-ext2fa9", .flags = CLK_IS_CRITICAL },
 344        { .name = "clk-ic-bdisp-0", },
 345        { .name = "clk-ic-bdisp-1", },
 346        { .name = "clk-pp-dmu", },
 347        { .name = "clk-vid-dmu", },
 348        { .name = "clk-dss-lpc", },
 349        { .name = "clk-st231-aud-0", },
 350        { .name = "clk-st231-gp-1", },
 351        { .name = "clk-st231-dmu", },
 352        /* This clk needs to be on to keep bus interconnect alive */
 353        { .name = "clk-icn-lmi", .flags = CLK_IS_CRITICAL },
 354        { .name = "clk-tx-icn-disp-1", },
 355        /* This clk needs to be on to keep bus interconnect alive */
 356        { .name = "clk-icn-sbc", .flags = CLK_IS_CRITICAL },
 357        { .name = "clk-stfe-frc2", },
 358        { .name = "clk-eth-phy", },
 359        { .name = "clk-eth-ref-phyclk", },
 360        { .name = "clk-flash-promip", },
 361        { .name = "clk-main-disp", },
 362        { .name = "clk-aux-disp", },
 363        { .name = "clk-compo-dvp", },
 364};
 365
 366static const struct clkgen_data clkgen_stih407_c0 = {
 367        .outputs = clkgen_stih407_c0_clk_out,
 368        .outputs_nb = ARRAY_SIZE(clkgen_stih407_c0_clk_out),
 369};
 370
 371static const struct clkgen_clk_out clkgen_stih410_c0_clk_out[] = {
 372        { .name = "clk-icn-gpu", },
 373        { .name = "clk-fdma", },
 374        { .name = "clk-nand", },
 375        { .name = "clk-hva", },
 376        { .name = "clk-proc-stfe", },
 377        { .name = "clk-proc-tp", },
 378        { .name = "clk-rx-icn-dmu", },
 379        { .name = "clk-rx-icn-hva", },
 380        /* This clk needs to be on to keep bus interconnect alive */
 381        { .name = "clk-icn-cpu", .flags = CLK_IS_CRITICAL },
 382        /* This clk needs to be on to keep bus interconnect alive */
 383        { .name = "clk-tx-icn-dmu", .flags = CLK_IS_CRITICAL },
 384        { .name = "clk-mmc-0", },
 385        { .name = "clk-mmc-1", },
 386        { .name = "clk-jpegdec", },
 387        /* This clk needs to be on to keep A9 running */
 388        { .name = "clk-ext2fa9", .flags = CLK_IS_CRITICAL },
 389        { .name = "clk-ic-bdisp-0", },
 390        { .name = "clk-ic-bdisp-1", },
 391        { .name = "clk-pp-dmu", },
 392        { .name = "clk-vid-dmu", },
 393        { .name = "clk-dss-lpc", },
 394        { .name = "clk-st231-aud-0", },
 395        { .name = "clk-st231-gp-1", },
 396        { .name = "clk-st231-dmu", },
 397        /* This clk needs to be on to keep bus interconnect alive */
 398        { .name = "clk-icn-lmi", .flags = CLK_IS_CRITICAL },
 399        { .name = "clk-tx-icn-disp-1", },
 400        /* This clk needs to be on to keep bus interconnect alive */
 401        { .name = "clk-icn-sbc", .flags = CLK_IS_CRITICAL },
 402        { .name = "clk-stfe-frc2", },
 403        { .name = "clk-eth-phy", },
 404        { .name = "clk-eth-ref-phyclk", },
 405        { .name = "clk-flash-promip", },
 406        { .name = "clk-main-disp", },
 407        { .name = "clk-aux-disp", },
 408        { .name = "clk-compo-dvp", },
 409        { .name = "clk-tx-icn-hades", },
 410        { .name = "clk-rx-icn-hades", },
 411        /* This clk needs to be on to keep bus interconnect alive */
 412        { .name = "clk-icn-reg-16", .flags = CLK_IS_CRITICAL },
 413        { .name = "clk-pp-hades", },
 414        { .name = "clk-clust-hades", },
 415        { .name = "clk-hwpe-hades", },
 416        { .name = "clk-fc-hades", },
 417};
 418
 419static const struct clkgen_data clkgen_stih410_c0 = {
 420        .outputs = clkgen_stih410_c0_clk_out,
 421        .outputs_nb = ARRAY_SIZE(clkgen_stih410_c0_clk_out),
 422};
 423
 424static const struct clkgen_clk_out clkgen_stih418_c0_clk_out[] = {
 425        { .name = "clk-icn-gpu", },
 426        { .name = "clk-fdma", },
 427        { .name = "clk-nand", },
 428        { .name = "clk-hva", },
 429        { .name = "clk-proc-stfe", },
 430        { .name = "clk-tp", },
 431        /* This clk needs to be on to keep bus interconnect alive */
 432        { .name = "clk-rx-icn-dmu", .flags = CLK_IS_CRITICAL },
 433        /* This clk needs to be on to keep bus interconnect alive */
 434        { .name = "clk-rx-icn-hva", .flags = CLK_IS_CRITICAL },
 435        { .name = "clk-icn-cpu", .flags = CLK_IS_CRITICAL },
 436        /* This clk needs to be on to keep bus interconnect alive */
 437        { .name = "clk-tx-icn-dmu", .flags = CLK_IS_CRITICAL },
 438        { .name = "clk-mmc-0", },
 439        { .name = "clk-mmc-1", },
 440        { .name = "clk-jpegdec", },
 441        /* This clk needs to be on to keep bus interconnect alive */
 442        { .name = "clk-icn-reg", .flags = CLK_IS_CRITICAL },
 443        { .name = "clk-proc-bdisp-0", },
 444        { .name = "clk-proc-bdisp-1", },
 445        { .name = "clk-pp-dmu", },
 446        { .name = "clk-vid-dmu", },
 447        { .name = "clk-dss-lpc", },
 448        { .name = "clk-st231-aud-0", },
 449        { .name = "clk-st231-gp-1", },
 450        { .name = "clk-st231-dmu", },
 451        /* This clk needs to be on to keep bus interconnect alive */
 452        { .name = "clk-icn-lmi", .flags = CLK_IS_CRITICAL },
 453        /* This clk needs to be on to keep bus interconnect alive */
 454        { .name = "clk-tx-icn-1", .flags = CLK_IS_CRITICAL },
 455        /* This clk needs to be on to keep bus interconnect alive */
 456        { .name = "clk-icn-sbc", .flags = CLK_IS_CRITICAL },
 457        { .name = "clk-stfe-frc2", },
 458        { .name = "clk-eth-phyref", },
 459        { .name = "clk-eth-ref-phyclk", },
 460        { .name = "clk-flash-promip", },
 461        { .name = "clk-main-disp", },
 462        { .name = "clk-aux-disp", },
 463        { .name = "clk-compo-dvp", },
 464        /* This clk needs to be on to keep bus interconnect alive */
 465        { .name = "clk-tx-icn-hades", .flags = CLK_IS_CRITICAL },
 466        /* This clk needs to be on to keep bus interconnect alive */
 467        { .name = "clk-rx-icn-hades", .flags = CLK_IS_CRITICAL },
 468        /* This clk needs to be on to keep bus interconnect alive */
 469        { .name = "clk-icn-reg-16", .flags = CLK_IS_CRITICAL },
 470        { .name = "clk-pp-hevc", },
 471        { .name = "clk-clust-hevc", },
 472        { .name = "clk-hwpe-hevc", },
 473        { .name = "clk-fc-hevc", },
 474        { .name = "clk-proc-mixer", },
 475        { .name = "clk-proc-sc", },
 476        { .name = "clk-avsp-hevc", },
 477};
 478
 479static const struct clkgen_data clkgen_stih418_c0 = {
 480        .outputs = clkgen_stih418_c0_clk_out,
 481        .outputs_nb = ARRAY_SIZE(clkgen_stih418_c0_clk_out),
 482};
 483
 484static const struct clkgen_clk_out clkgen_stih407_d0_clk_out[] = {
 485        { .name = "clk-pcm-0", },
 486        { .name = "clk-pcm-1", },
 487        { .name = "clk-pcm-2", },
 488        { .name = "clk-spdiff", },
 489};
 490
 491static const struct clkgen_data clkgen_stih407_d0 = {
 492        .flags = CLK_SET_RATE_PARENT,
 493        .outputs = clkgen_stih407_d0_clk_out,
 494        .outputs_nb = ARRAY_SIZE(clkgen_stih407_d0_clk_out),
 495};
 496
 497static const struct clkgen_clk_out clkgen_stih410_d0_clk_out[] = {
 498        { .name = "clk-pcm-0", },
 499        { .name = "clk-pcm-1", },
 500        { .name = "clk-pcm-2", },
 501        { .name = "clk-spdiff", },
 502        { .name = "clk-pcmr10-master", },
 503        { .name = "clk-usb2-phy", },
 504};
 505
 506static const struct clkgen_data clkgen_stih410_d0 = {
 507        .flags = CLK_SET_RATE_PARENT,
 508        .outputs = clkgen_stih410_d0_clk_out,
 509        .outputs_nb = ARRAY_SIZE(clkgen_stih410_d0_clk_out),
 510};
 511
 512static const struct clkgen_clk_out clkgen_stih407_d2_clk_out[] = {
 513        { .name = "clk-pix-main-disp", },
 514        { .name = "clk-pix-pip", },
 515        { .name = "clk-pix-gdp1", },
 516        { .name = "clk-pix-gdp2", },
 517        { .name = "clk-pix-gdp3", },
 518        { .name = "clk-pix-gdp4", },
 519        { .name = "clk-pix-aux-disp", },
 520        { .name = "clk-denc", },
 521        { .name = "clk-pix-hddac", },
 522        { .name = "clk-hddac", },
 523        { .name = "clk-sddac", },
 524        { .name = "clk-pix-dvo", },
 525        { .name = "clk-dvo", },
 526        { .name = "clk-pix-hdmi", },
 527        { .name = "clk-tmds-hdmi", },
 528        { .name = "clk-ref-hdmiphy", },
 529};
 530
 531static const struct clkgen_data clkgen_stih407_d2 = {
 532        .outputs = clkgen_stih407_d2_clk_out,
 533        .outputs_nb = ARRAY_SIZE(clkgen_stih407_d2_clk_out),
 534        .flags = CLK_SET_RATE_PARENT,
 535        .mode = 1,
 536};
 537
 538static const struct clkgen_clk_out clkgen_stih418_d2_clk_out[] = {
 539        { .name = "clk-pix-main-disp", },
 540        { .name = "", },
 541        { .name = "", },
 542        { .name = "", },
 543        { .name = "", },
 544        { .name = "clk-tmds-hdmi-div2", },
 545        { .name = "clk-pix-aux-disp", },
 546        { .name = "clk-denc", },
 547        { .name = "clk-pix-hddac", },
 548        { .name = "clk-hddac", },
 549        { .name = "clk-sddac", },
 550        { .name = "clk-pix-dvo", },
 551        { .name = "clk-dvo", },
 552        { .name = "clk-pix-hdmi", },
 553        { .name = "clk-tmds-hdmi", },
 554        { .name = "clk-ref-hdmiphy", },
 555        { .name = "", }, { .name = "", }, { .name = "", }, { .name = "", },
 556        { .name = "", }, { .name = "", }, { .name = "", }, { .name = "", },
 557        { .name = "", }, { .name = "", }, { .name = "", }, { .name = "", },
 558        { .name = "", }, { .name = "", }, { .name = "", }, { .name = "", },
 559        { .name = "", }, { .name = "", }, { .name = "", }, { .name = "", },
 560        { .name = "", }, { .name = "", }, { .name = "", }, { .name = "", },
 561        { .name = "", }, { .name = "", }, { .name = "", }, { .name = "", },
 562        { .name = "", }, { .name = "", }, { .name = "", },
 563        { .name = "clk-vp9", },
 564};
 565
 566static const struct clkgen_data clkgen_stih418_d2 = {
 567        .outputs = clkgen_stih418_d2_clk_out,
 568        .outputs_nb = ARRAY_SIZE(clkgen_stih418_d2_clk_out),
 569        .flags = CLK_SET_RATE_PARENT,
 570        .mode = 1,
 571};
 572
 573static const struct clkgen_clk_out clkgen_stih407_d3_clk_out[] = {
 574        { .name = "clk-stfe-frc1", },
 575        { .name = "clk-tsout-0", },
 576        { .name = "clk-tsout-1", },
 577        { .name = "clk-mchi", },
 578        { .name = "clk-vsens-compo", },
 579        { .name = "clk-frc1-remote", },
 580        { .name = "clk-lpc-0", },
 581        { .name = "clk-lpc-1", },
 582};
 583
 584static const struct clkgen_data clkgen_stih407_d3 = {
 585        .outputs = clkgen_stih407_d3_clk_out,
 586        .outputs_nb = ARRAY_SIZE(clkgen_stih407_d3_clk_out),
 587};
 588
 589static const struct of_device_id flexgen_of_match[] = {
 590        {
 591                .compatible = "st,flexgen-audio",
 592                .data = &clkgen_audio,
 593        },
 594        {
 595                .compatible = "st,flexgen-video",
 596                .data = &clkgen_video,
 597        },
 598        {
 599                .compatible = "st,flexgen-stih407-a0",
 600                .data = &clkgen_stih407_a0,
 601        },
 602        {
 603                .compatible = "st,flexgen-stih410-a0",
 604                .data = &clkgen_stih410_a0,
 605        },
 606        {
 607                .compatible = "st,flexgen-stih407-c0",
 608                .data = &clkgen_stih407_c0,
 609        },
 610        {
 611                .compatible = "st,flexgen-stih410-c0",
 612                .data = &clkgen_stih410_c0,
 613        },
 614        {
 615                .compatible = "st,flexgen-stih418-c0",
 616                .data = &clkgen_stih418_c0,
 617        },
 618        {
 619                .compatible = "st,flexgen-stih407-d0",
 620                .data = &clkgen_stih407_d0,
 621        },
 622        {
 623                .compatible = "st,flexgen-stih410-d0",
 624                .data = &clkgen_stih410_d0,
 625        },
 626        {
 627                .compatible = "st,flexgen-stih407-d2",
 628                .data = &clkgen_stih407_d2,
 629        },
 630        {
 631                .compatible = "st,flexgen-stih418-d2",
 632                .data = &clkgen_stih418_d2,
 633        },
 634        {
 635                .compatible = "st,flexgen-stih407-d3",
 636                .data = &clkgen_stih407_d3,
 637        },
 638        {}
 639};
 640
 641static void __init st_of_flexgen_setup(struct device_node *np)
 642{
 643        struct device_node *pnode;
 644        void __iomem *reg;
 645        struct clk_onecell_data *clk_data;
 646        const char **parents;
 647        int num_parents, i;
 648        spinlock_t *rlock = NULL;
 649        const struct of_device_id *match;
 650        struct clkgen_data *data = NULL;
 651        unsigned long flex_flags = 0;
 652        int ret;
 653        bool clk_mode = 0;
 654        const char *clk_name;
 655
 656        pnode = of_get_parent(np);
 657        if (!pnode)
 658                return;
 659
 660        reg = of_iomap(pnode, 0);
 661        of_node_put(pnode);
 662        if (!reg)
 663                return;
 664
 665        parents = flexgen_get_parents(np, &num_parents);
 666        if (!parents) {
 667                iounmap(reg);
 668                return;
 669        }
 670
 671        match = of_match_node(flexgen_of_match, np);
 672        if (match) {
 673                data = (struct clkgen_data *)match->data;
 674                flex_flags = data->flags;
 675                clk_mode = data->mode;
 676        }
 677
 678        clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL);
 679        if (!clk_data)
 680                goto err;
 681
 682        /* First try to get output information from the compatible data */
 683        if (!data || !data->outputs_nb || !data->outputs) {
 684                ret = of_property_count_strings(np, "clock-output-names");
 685                if (ret <= 0) {
 686                        pr_err("%s: Failed to get number of output clocks (%d)",
 687                                        __func__, clk_data->clk_num);
 688                        goto err;
 689                }
 690                clk_data->clk_num = ret;
 691        } else
 692                clk_data->clk_num = data->outputs_nb;
 693
 694        clk_data->clks = kcalloc(clk_data->clk_num, sizeof(struct clk *),
 695                        GFP_KERNEL);
 696        if (!clk_data->clks)
 697                goto err;
 698
 699        rlock = kzalloc(sizeof(spinlock_t), GFP_KERNEL);
 700        if (!rlock)
 701                goto err;
 702
 703        spin_lock_init(rlock);
 704
 705        for (i = 0; i < clk_data->clk_num; i++) {
 706                struct clk *clk;
 707
 708                if (!data || !data->outputs_nb || !data->outputs) {
 709                        if (of_property_read_string_index(np,
 710                                                          "clock-output-names",
 711                                                          i, &clk_name))
 712                                break;
 713                        flex_flags &= ~CLK_IS_CRITICAL;
 714                        of_clk_detect_critical(np, i, &flex_flags);
 715                } else {
 716                        clk_name = data->outputs[i].name;
 717                        flex_flags = data->flags | data->outputs[i].flags;
 718                }
 719
 720                /*
 721                 * If we read an empty clock name then the output is unused
 722                 */
 723                if (*clk_name == '\0')
 724                        continue;
 725
 726                clk = clk_register_flexgen(clk_name, parents, num_parents,
 727                                           reg, rlock, i, flex_flags, clk_mode);
 728
 729                if (IS_ERR(clk))
 730                        goto err;
 731
 732                clk_data->clks[i] = clk;
 733        }
 734
 735        kfree(parents);
 736        of_clk_add_provider(np, of_clk_src_onecell_get, clk_data);
 737
 738        return;
 739
 740err:
 741        iounmap(reg);
 742        if (clk_data)
 743                kfree(clk_data->clks);
 744        kfree(clk_data);
 745        kfree(parents);
 746        kfree(rlock);
 747}
 748CLK_OF_DECLARE(flexgen, "st,flexgen", st_of_flexgen_setup);
 749