linux/drivers/clk/at91/sam9x60.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2#include <linux/clk-provider.h>
   3#include <linux/mfd/syscon.h>
   4#include <linux/slab.h>
   5
   6#include <dt-bindings/clock/at91.h>
   7
   8#include "pmc.h"
   9
  10static DEFINE_SPINLOCK(pmc_pll_lock);
  11static DEFINE_SPINLOCK(mck_lock);
  12
  13static const struct clk_master_characteristics mck_characteristics = {
  14        .output = { .min = 140000000, .max = 200000000 },
  15        .divisors = { 1, 2, 4, 3 },
  16        .have_div3_pres = 1,
  17};
  18
  19static const struct clk_master_layout sam9x60_master_layout = {
  20        .mask = 0x373,
  21        .pres_shift = 4,
  22        .offset = 0x28,
  23};
  24
  25static const struct clk_range plla_outputs[] = {
  26        { .min = 2343750, .max = 1200000000 },
  27};
  28
  29static const struct clk_pll_characteristics plla_characteristics = {
  30        .input = { .min = 12000000, .max = 48000000 },
  31        .num_output = ARRAY_SIZE(plla_outputs),
  32        .output = plla_outputs,
  33};
  34
  35static const struct clk_range upll_outputs[] = {
  36        { .min = 300000000, .max = 500000000 },
  37};
  38
  39static const struct clk_pll_characteristics upll_characteristics = {
  40        .input = { .min = 12000000, .max = 48000000 },
  41        .num_output = ARRAY_SIZE(upll_outputs),
  42        .output = upll_outputs,
  43        .upll = true,
  44};
  45
  46static const struct clk_pll_layout pll_frac_layout = {
  47        .mul_mask = GENMASK(31, 24),
  48        .frac_mask = GENMASK(21, 0),
  49        .mul_shift = 24,
  50        .frac_shift = 0,
  51};
  52
  53static const struct clk_pll_layout pll_div_layout = {
  54        .div_mask = GENMASK(7, 0),
  55        .endiv_mask = BIT(29),
  56        .div_shift = 0,
  57        .endiv_shift = 29,
  58};
  59
  60static const struct clk_programmable_layout sam9x60_programmable_layout = {
  61        .pres_mask = 0xff,
  62        .pres_shift = 8,
  63        .css_mask = 0x1f,
  64        .have_slck_mck = 0,
  65        .is_pres_direct = 1,
  66};
  67
  68static const struct clk_pcr_layout sam9x60_pcr_layout = {
  69        .offset = 0x88,
  70        .cmd = BIT(31),
  71        .gckcss_mask = GENMASK(12, 8),
  72        .pid_mask = GENMASK(6, 0),
  73};
  74
  75static const struct {
  76        char *n;
  77        char *p;
  78        u8 id;
  79} sam9x60_systemck[] = {
  80        { .n = "ddrck",  .p = "masterck_div", .id = 2 },
  81        { .n = "uhpck",  .p = "usbck",    .id = 6 },
  82        { .n = "pck0",   .p = "prog0",    .id = 8 },
  83        { .n = "pck1",   .p = "prog1",    .id = 9 },
  84        { .n = "qspick", .p = "masterck_div", .id = 19 },
  85};
  86
  87static const struct {
  88        char *n;
  89        u8 id;
  90} sam9x60_periphck[] = {
  91        { .n = "pioA_clk",   .id = 2, },
  92        { .n = "pioB_clk",   .id = 3, },
  93        { .n = "pioC_clk",   .id = 4, },
  94        { .n = "flex0_clk",  .id = 5, },
  95        { .n = "flex1_clk",  .id = 6, },
  96        { .n = "flex2_clk",  .id = 7, },
  97        { .n = "flex3_clk",  .id = 8, },
  98        { .n = "flex6_clk",  .id = 9, },
  99        { .n = "flex7_clk",  .id = 10, },
 100        { .n = "flex8_clk",  .id = 11, },
 101        { .n = "sdmmc0_clk", .id = 12, },
 102        { .n = "flex4_clk",  .id = 13, },
 103        { .n = "flex5_clk",  .id = 14, },
 104        { .n = "flex9_clk",  .id = 15, },
 105        { .n = "flex10_clk", .id = 16, },
 106        { .n = "tcb0_clk",   .id = 17, },
 107        { .n = "pwm_clk",    .id = 18, },
 108        { .n = "adc_clk",    .id = 19, },
 109        { .n = "dma0_clk",   .id = 20, },
 110        { .n = "matrix_clk", .id = 21, },
 111        { .n = "uhphs_clk",  .id = 22, },
 112        { .n = "udphs_clk",  .id = 23, },
 113        { .n = "macb0_clk",  .id = 24, },
 114        { .n = "lcd_clk",    .id = 25, },
 115        { .n = "sdmmc1_clk", .id = 26, },
 116        { .n = "macb1_clk",  .id = 27, },
 117        { .n = "ssc_clk",    .id = 28, },
 118        { .n = "can0_clk",   .id = 29, },
 119        { .n = "can1_clk",   .id = 30, },
 120        { .n = "flex11_clk", .id = 32, },
 121        { .n = "flex12_clk", .id = 33, },
 122        { .n = "i2s_clk",    .id = 34, },
 123        { .n = "qspi_clk",   .id = 35, },
 124        { .n = "gfx2d_clk",  .id = 36, },
 125        { .n = "pit64b_clk", .id = 37, },
 126        { .n = "trng_clk",   .id = 38, },
 127        { .n = "aes_clk",    .id = 39, },
 128        { .n = "tdes_clk",   .id = 40, },
 129        { .n = "sha_clk",    .id = 41, },
 130        { .n = "classd_clk", .id = 42, },
 131        { .n = "isi_clk",    .id = 43, },
 132        { .n = "pioD_clk",   .id = 44, },
 133        { .n = "tcb1_clk",   .id = 45, },
 134        { .n = "dbgu_clk",   .id = 47, },
 135        { .n = "mpddr_clk",  .id = 49, },
 136};
 137
 138static const struct {
 139        char *n;
 140        u8 id;
 141        struct clk_range r;
 142} sam9x60_gck[] = {
 143        { .n = "flex0_gclk",  .id = 5, },
 144        { .n = "flex1_gclk",  .id = 6, },
 145        { .n = "flex2_gclk",  .id = 7, },
 146        { .n = "flex3_gclk",  .id = 8, },
 147        { .n = "flex6_gclk",  .id = 9, },
 148        { .n = "flex7_gclk",  .id = 10, },
 149        { .n = "flex8_gclk",  .id = 11, },
 150        { .n = "sdmmc0_gclk", .id = 12, .r = { .min = 0, .max = 105000000 }, },
 151        { .n = "flex4_gclk",  .id = 13, },
 152        { .n = "flex5_gclk",  .id = 14, },
 153        { .n = "flex9_gclk",  .id = 15, },
 154        { .n = "flex10_gclk", .id = 16, },
 155        { .n = "tcb0_gclk",   .id = 17, },
 156        { .n = "adc_gclk",    .id = 19, },
 157        { .n = "lcd_gclk",    .id = 25, .r = { .min = 0, .max = 140000000 }, },
 158        { .n = "sdmmc1_gclk", .id = 26, .r = { .min = 0, .max = 105000000 }, },
 159        { .n = "flex11_gclk", .id = 32, },
 160        { .n = "flex12_gclk", .id = 33, },
 161        { .n = "i2s_gclk",    .id = 34, .r = { .min = 0, .max = 105000000 }, },
 162        { .n = "pit64b_gclk", .id = 37, },
 163        { .n = "classd_gclk", .id = 42, .r = { .min = 0, .max = 100000000 }, },
 164        { .n = "tcb1_gclk",   .id = 45, },
 165        { .n = "dbgu_gclk",   .id = 47, },
 166};
 167
 168static void __init sam9x60_pmc_setup(struct device_node *np)
 169{
 170        struct clk_range range = CLK_RANGE(0, 0);
 171        const char *td_slck_name, *md_slck_name, *mainxtal_name;
 172        struct pmc_data *sam9x60_pmc;
 173        const char *parent_names[6];
 174        struct clk_hw *main_osc_hw;
 175        struct regmap *regmap;
 176        struct clk_hw *hw;
 177        int i;
 178
 179        i = of_property_match_string(np, "clock-names", "td_slck");
 180        if (i < 0)
 181                return;
 182
 183        td_slck_name = of_clk_get_parent_name(np, i);
 184
 185        i = of_property_match_string(np, "clock-names", "md_slck");
 186        if (i < 0)
 187                return;
 188
 189        md_slck_name = of_clk_get_parent_name(np, i);
 190
 191        i = of_property_match_string(np, "clock-names", "main_xtal");
 192        if (i < 0)
 193                return;
 194        mainxtal_name = of_clk_get_parent_name(np, i);
 195
 196        regmap = device_node_to_regmap(np);
 197        if (IS_ERR(regmap))
 198                return;
 199
 200        sam9x60_pmc = pmc_data_allocate(PMC_PLLACK + 1,
 201                                        nck(sam9x60_systemck),
 202                                        nck(sam9x60_periphck),
 203                                        nck(sam9x60_gck), 8);
 204        if (!sam9x60_pmc)
 205                return;
 206
 207        hw = at91_clk_register_main_rc_osc(regmap, "main_rc_osc", 12000000,
 208                                           50000000);
 209        if (IS_ERR(hw))
 210                goto err_free;
 211
 212        hw = at91_clk_register_main_osc(regmap, "main_osc", mainxtal_name, 0);
 213        if (IS_ERR(hw))
 214                goto err_free;
 215        main_osc_hw = hw;
 216
 217        parent_names[0] = "main_rc_osc";
 218        parent_names[1] = "main_osc";
 219        hw = at91_clk_register_sam9x5_main(regmap, "mainck", parent_names, 2);
 220        if (IS_ERR(hw))
 221                goto err_free;
 222
 223        sam9x60_pmc->chws[PMC_MAIN] = hw;
 224
 225        hw = sam9x60_clk_register_frac_pll(regmap, &pmc_pll_lock, "pllack_fracck",
 226                                           "mainck", sam9x60_pmc->chws[PMC_MAIN],
 227                                           0, &plla_characteristics,
 228                                           &pll_frac_layout,
 229                                           /*
 230                                            * This feeds pllack_divck which
 231                                            * feeds CPU. It should not be
 232                                            * disabled.
 233                                            */
 234                                           CLK_IS_CRITICAL | CLK_SET_RATE_GATE);
 235        if (IS_ERR(hw))
 236                goto err_free;
 237
 238        hw = sam9x60_clk_register_div_pll(regmap, &pmc_pll_lock, "pllack_divck",
 239                                          "pllack_fracck", 0, &plla_characteristics,
 240                                          &pll_div_layout,
 241                                           /*
 242                                            * This feeds CPU. It should not
 243                                            * be disabled.
 244                                            */
 245                                          CLK_IS_CRITICAL | CLK_SET_RATE_GATE);
 246        if (IS_ERR(hw))
 247                goto err_free;
 248
 249        sam9x60_pmc->chws[PMC_PLLACK] = hw;
 250
 251        hw = sam9x60_clk_register_frac_pll(regmap, &pmc_pll_lock, "upllck_fracck",
 252                                           "main_osc", main_osc_hw, 1,
 253                                           &upll_characteristics,
 254                                           &pll_frac_layout, CLK_SET_RATE_GATE);
 255        if (IS_ERR(hw))
 256                goto err_free;
 257
 258        hw = sam9x60_clk_register_div_pll(regmap, &pmc_pll_lock, "upllck_divck",
 259                                          "upllck_fracck", 1, &upll_characteristics,
 260                                          &pll_div_layout,
 261                                          CLK_SET_RATE_GATE |
 262                                          CLK_SET_PARENT_GATE |
 263                                          CLK_SET_RATE_PARENT);
 264        if (IS_ERR(hw))
 265                goto err_free;
 266
 267        sam9x60_pmc->chws[PMC_UTMI] = hw;
 268
 269        parent_names[0] = md_slck_name;
 270        parent_names[1] = "mainck";
 271        parent_names[2] = "pllack_divck";
 272        hw = at91_clk_register_master_pres(regmap, "masterck_pres", 3,
 273                                           parent_names, &sam9x60_master_layout,
 274                                           &mck_characteristics, &mck_lock,
 275                                           CLK_SET_RATE_GATE, INT_MIN);
 276        if (IS_ERR(hw))
 277                goto err_free;
 278
 279        hw = at91_clk_register_master_div(regmap, "masterck_div",
 280                                          "masterck_pres", &sam9x60_master_layout,
 281                                          &mck_characteristics, &mck_lock,
 282                                          CLK_SET_RATE_GATE);
 283        if (IS_ERR(hw))
 284                goto err_free;
 285
 286        sam9x60_pmc->chws[PMC_MCK] = hw;
 287
 288        parent_names[0] = "pllack_divck";
 289        parent_names[1] = "upllck_divck";
 290        parent_names[2] = "main_osc";
 291        hw = sam9x60_clk_register_usb(regmap, "usbck", parent_names, 3);
 292        if (IS_ERR(hw))
 293                goto err_free;
 294
 295        parent_names[0] = md_slck_name;
 296        parent_names[1] = td_slck_name;
 297        parent_names[2] = "mainck";
 298        parent_names[3] = "masterck_div";
 299        parent_names[4] = "pllack_divck";
 300        parent_names[5] = "upllck_divck";
 301        for (i = 0; i < 2; i++) {
 302                char name[6];
 303
 304                snprintf(name, sizeof(name), "prog%d", i);
 305
 306                hw = at91_clk_register_programmable(regmap, name,
 307                                                    parent_names, 6, i,
 308                                                    &sam9x60_programmable_layout,
 309                                                    NULL);
 310                if (IS_ERR(hw))
 311                        goto err_free;
 312
 313                sam9x60_pmc->pchws[i] = hw;
 314        }
 315
 316        for (i = 0; i < ARRAY_SIZE(sam9x60_systemck); i++) {
 317                hw = at91_clk_register_system(regmap, sam9x60_systemck[i].n,
 318                                              sam9x60_systemck[i].p,
 319                                              sam9x60_systemck[i].id);
 320                if (IS_ERR(hw))
 321                        goto err_free;
 322
 323                sam9x60_pmc->shws[sam9x60_systemck[i].id] = hw;
 324        }
 325
 326        for (i = 0; i < ARRAY_SIZE(sam9x60_periphck); i++) {
 327                hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock,
 328                                                         &sam9x60_pcr_layout,
 329                                                         sam9x60_periphck[i].n,
 330                                                         "masterck_div",
 331                                                         sam9x60_periphck[i].id,
 332                                                         &range, INT_MIN);
 333                if (IS_ERR(hw))
 334                        goto err_free;
 335
 336                sam9x60_pmc->phws[sam9x60_periphck[i].id] = hw;
 337        }
 338
 339        for (i = 0; i < ARRAY_SIZE(sam9x60_gck); i++) {
 340                hw = at91_clk_register_generated(regmap, &pmc_pcr_lock,
 341                                                 &sam9x60_pcr_layout,
 342                                                 sam9x60_gck[i].n,
 343                                                 parent_names, NULL, 6,
 344                                                 sam9x60_gck[i].id,
 345                                                 &sam9x60_gck[i].r, INT_MIN);
 346                if (IS_ERR(hw))
 347                        goto err_free;
 348
 349                sam9x60_pmc->ghws[sam9x60_gck[i].id] = hw;
 350        }
 351
 352        of_clk_add_hw_provider(np, of_clk_hw_pmc_get, sam9x60_pmc);
 353
 354        return;
 355
 356err_free:
 357        kfree(sam9x60_pmc);
 358}
 359/* Some clks are used for a clocksource */
 360CLK_OF_DECLARE(sam9x60_pmc, "microchip,sam9x60-pmc", sam9x60_pmc_setup);
 361