Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 #include <linux/clk-provider.h>
0003 #include <linux/mfd/syscon.h>
0004 #include <linux/slab.h>
0005 
0006 #include <dt-bindings/clock/at91.h>
0007 
0008 #include "pmc.h"
0009 
0010 static DEFINE_SPINLOCK(pmc_pll_lock);
0011 static DEFINE_SPINLOCK(mck_lock);
0012 
0013 static const struct clk_master_characteristics mck_characteristics = {
0014     .output = { .min = 140000000, .max = 200000000 },
0015     .divisors = { 1, 2, 4, 3 },
0016     .have_div3_pres = 1,
0017 };
0018 
0019 static const struct clk_master_layout sam9x60_master_layout = {
0020     .mask = 0x373,
0021     .pres_shift = 4,
0022     .offset = 0x28,
0023 };
0024 
0025 static const struct clk_range plla_outputs[] = {
0026     { .min = 2343750, .max = 1200000000 },
0027 };
0028 
0029 static const struct clk_pll_characteristics plla_characteristics = {
0030     .input = { .min = 12000000, .max = 48000000 },
0031     .num_output = ARRAY_SIZE(plla_outputs),
0032     .output = plla_outputs,
0033 };
0034 
0035 static const struct clk_range upll_outputs[] = {
0036     { .min = 300000000, .max = 500000000 },
0037 };
0038 
0039 static const struct clk_pll_characteristics upll_characteristics = {
0040     .input = { .min = 12000000, .max = 48000000 },
0041     .num_output = ARRAY_SIZE(upll_outputs),
0042     .output = upll_outputs,
0043     .upll = true,
0044 };
0045 
0046 static const struct clk_pll_layout pll_frac_layout = {
0047     .mul_mask = GENMASK(31, 24),
0048     .frac_mask = GENMASK(21, 0),
0049     .mul_shift = 24,
0050     .frac_shift = 0,
0051 };
0052 
0053 static const struct clk_pll_layout pll_div_layout = {
0054     .div_mask = GENMASK(7, 0),
0055     .endiv_mask = BIT(29),
0056     .div_shift = 0,
0057     .endiv_shift = 29,
0058 };
0059 
0060 static const struct clk_programmable_layout sam9x60_programmable_layout = {
0061     .pres_mask = 0xff,
0062     .pres_shift = 8,
0063     .css_mask = 0x1f,
0064     .have_slck_mck = 0,
0065     .is_pres_direct = 1,
0066 };
0067 
0068 static const struct clk_pcr_layout sam9x60_pcr_layout = {
0069     .offset = 0x88,
0070     .cmd = BIT(31),
0071     .gckcss_mask = GENMASK(12, 8),
0072     .pid_mask = GENMASK(6, 0),
0073 };
0074 
0075 static const struct {
0076     char *n;
0077     char *p;
0078     u8 id;
0079 } sam9x60_systemck[] = {
0080     { .n = "ddrck",  .p = "masterck_div", .id = 2 },
0081     { .n = "uhpck",  .p = "usbck",    .id = 6 },
0082     { .n = "pck0",   .p = "prog0",    .id = 8 },
0083     { .n = "pck1",   .p = "prog1",    .id = 9 },
0084     { .n = "qspick", .p = "masterck_div", .id = 19 },
0085 };
0086 
0087 static const struct {
0088     char *n;
0089     u8 id;
0090 } sam9x60_periphck[] = {
0091     { .n = "pioA_clk",   .id = 2, },
0092     { .n = "pioB_clk",   .id = 3, },
0093     { .n = "pioC_clk",   .id = 4, },
0094     { .n = "flex0_clk",  .id = 5, },
0095     { .n = "flex1_clk",  .id = 6, },
0096     { .n = "flex2_clk",  .id = 7, },
0097     { .n = "flex3_clk",  .id = 8, },
0098     { .n = "flex6_clk",  .id = 9, },
0099     { .n = "flex7_clk",  .id = 10, },
0100     { .n = "flex8_clk",  .id = 11, },
0101     { .n = "sdmmc0_clk", .id = 12, },
0102     { .n = "flex4_clk",  .id = 13, },
0103     { .n = "flex5_clk",  .id = 14, },
0104     { .n = "flex9_clk",  .id = 15, },
0105     { .n = "flex10_clk", .id = 16, },
0106     { .n = "tcb0_clk",   .id = 17, },
0107     { .n = "pwm_clk",    .id = 18, },
0108     { .n = "adc_clk",    .id = 19, },
0109     { .n = "dma0_clk",   .id = 20, },
0110     { .n = "matrix_clk", .id = 21, },
0111     { .n = "uhphs_clk",  .id = 22, },
0112     { .n = "udphs_clk",  .id = 23, },
0113     { .n = "macb0_clk",  .id = 24, },
0114     { .n = "lcd_clk",    .id = 25, },
0115     { .n = "sdmmc1_clk", .id = 26, },
0116     { .n = "macb1_clk",  .id = 27, },
0117     { .n = "ssc_clk",    .id = 28, },
0118     { .n = "can0_clk",   .id = 29, },
0119     { .n = "can1_clk",   .id = 30, },
0120     { .n = "flex11_clk", .id = 32, },
0121     { .n = "flex12_clk", .id = 33, },
0122     { .n = "i2s_clk",    .id = 34, },
0123     { .n = "qspi_clk",   .id = 35, },
0124     { .n = "gfx2d_clk",  .id = 36, },
0125     { .n = "pit64b_clk", .id = 37, },
0126     { .n = "trng_clk",   .id = 38, },
0127     { .n = "aes_clk",    .id = 39, },
0128     { .n = "tdes_clk",   .id = 40, },
0129     { .n = "sha_clk",    .id = 41, },
0130     { .n = "classd_clk", .id = 42, },
0131     { .n = "isi_clk",    .id = 43, },
0132     { .n = "pioD_clk",   .id = 44, },
0133     { .n = "tcb1_clk",   .id = 45, },
0134     { .n = "dbgu_clk",   .id = 47, },
0135     { .n = "mpddr_clk",  .id = 49, },
0136 };
0137 
0138 static const struct {
0139     char *n;
0140     u8 id;
0141     struct clk_range r;
0142 } sam9x60_gck[] = {
0143     { .n = "flex0_gclk",  .id = 5, },
0144     { .n = "flex1_gclk",  .id = 6, },
0145     { .n = "flex2_gclk",  .id = 7, },
0146     { .n = "flex3_gclk",  .id = 8, },
0147     { .n = "flex6_gclk",  .id = 9, },
0148     { .n = "flex7_gclk",  .id = 10, },
0149     { .n = "flex8_gclk",  .id = 11, },
0150     { .n = "sdmmc0_gclk", .id = 12, .r = { .min = 0, .max = 105000000 }, },
0151     { .n = "flex4_gclk",  .id = 13, },
0152     { .n = "flex5_gclk",  .id = 14, },
0153     { .n = "flex9_gclk",  .id = 15, },
0154     { .n = "flex10_gclk", .id = 16, },
0155     { .n = "tcb0_gclk",   .id = 17, },
0156     { .n = "adc_gclk",    .id = 19, },
0157     { .n = "lcd_gclk",    .id = 25, .r = { .min = 0, .max = 140000000 }, },
0158     { .n = "sdmmc1_gclk", .id = 26, .r = { .min = 0, .max = 105000000 }, },
0159     { .n = "flex11_gclk", .id = 32, },
0160     { .n = "flex12_gclk", .id = 33, },
0161     { .n = "i2s_gclk",    .id = 34, .r = { .min = 0, .max = 105000000 }, },
0162     { .n = "pit64b_gclk", .id = 37, },
0163     { .n = "classd_gclk", .id = 42, .r = { .min = 0, .max = 100000000 }, },
0164     { .n = "tcb1_gclk",   .id = 45, },
0165     { .n = "dbgu_gclk",   .id = 47, },
0166 };
0167 
0168 static void __init sam9x60_pmc_setup(struct device_node *np)
0169 {
0170     struct clk_range range = CLK_RANGE(0, 0);
0171     const char *td_slck_name, *md_slck_name, *mainxtal_name;
0172     struct pmc_data *sam9x60_pmc;
0173     const char *parent_names[6];
0174     struct clk_hw *main_osc_hw;
0175     struct regmap *regmap;
0176     struct clk_hw *hw;
0177     int i;
0178 
0179     i = of_property_match_string(np, "clock-names", "td_slck");
0180     if (i < 0)
0181         return;
0182 
0183     td_slck_name = of_clk_get_parent_name(np, i);
0184 
0185     i = of_property_match_string(np, "clock-names", "md_slck");
0186     if (i < 0)
0187         return;
0188 
0189     md_slck_name = of_clk_get_parent_name(np, i);
0190 
0191     i = of_property_match_string(np, "clock-names", "main_xtal");
0192     if (i < 0)
0193         return;
0194     mainxtal_name = of_clk_get_parent_name(np, i);
0195 
0196     regmap = device_node_to_regmap(np);
0197     if (IS_ERR(regmap))
0198         return;
0199 
0200     sam9x60_pmc = pmc_data_allocate(PMC_PLLACK + 1,
0201                     nck(sam9x60_systemck),
0202                     nck(sam9x60_periphck),
0203                     nck(sam9x60_gck), 8);
0204     if (!sam9x60_pmc)
0205         return;
0206 
0207     hw = at91_clk_register_main_rc_osc(regmap, "main_rc_osc", 12000000,
0208                        50000000);
0209     if (IS_ERR(hw))
0210         goto err_free;
0211 
0212     hw = at91_clk_register_main_osc(regmap, "main_osc", mainxtal_name, 0);
0213     if (IS_ERR(hw))
0214         goto err_free;
0215     main_osc_hw = hw;
0216 
0217     parent_names[0] = "main_rc_osc";
0218     parent_names[1] = "main_osc";
0219     hw = at91_clk_register_sam9x5_main(regmap, "mainck", parent_names, 2);
0220     if (IS_ERR(hw))
0221         goto err_free;
0222 
0223     sam9x60_pmc->chws[PMC_MAIN] = hw;
0224 
0225     hw = sam9x60_clk_register_frac_pll(regmap, &pmc_pll_lock, "pllack_fracck",
0226                        "mainck", sam9x60_pmc->chws[PMC_MAIN],
0227                        0, &plla_characteristics,
0228                        &pll_frac_layout,
0229                        /*
0230                         * This feeds pllack_divck which
0231                         * feeds CPU. It should not be
0232                         * disabled.
0233                         */
0234                        CLK_IS_CRITICAL | CLK_SET_RATE_GATE);
0235     if (IS_ERR(hw))
0236         goto err_free;
0237 
0238     hw = sam9x60_clk_register_div_pll(regmap, &pmc_pll_lock, "pllack_divck",
0239                       "pllack_fracck", 0, &plla_characteristics,
0240                       &pll_div_layout,
0241                        /*
0242                         * This feeds CPU. It should not
0243                         * be disabled.
0244                         */
0245                       CLK_IS_CRITICAL | CLK_SET_RATE_GATE, 0);
0246     if (IS_ERR(hw))
0247         goto err_free;
0248 
0249     sam9x60_pmc->chws[PMC_PLLACK] = hw;
0250 
0251     hw = sam9x60_clk_register_frac_pll(regmap, &pmc_pll_lock, "upllck_fracck",
0252                        "main_osc", main_osc_hw, 1,
0253                        &upll_characteristics,
0254                        &pll_frac_layout, CLK_SET_RATE_GATE);
0255     if (IS_ERR(hw))
0256         goto err_free;
0257 
0258     hw = sam9x60_clk_register_div_pll(regmap, &pmc_pll_lock, "upllck_divck",
0259                       "upllck_fracck", 1, &upll_characteristics,
0260                       &pll_div_layout,
0261                       CLK_SET_RATE_GATE |
0262                       CLK_SET_PARENT_GATE |
0263                       CLK_SET_RATE_PARENT, 0);
0264     if (IS_ERR(hw))
0265         goto err_free;
0266 
0267     sam9x60_pmc->chws[PMC_UTMI] = hw;
0268 
0269     parent_names[0] = md_slck_name;
0270     parent_names[1] = "mainck";
0271     parent_names[2] = "pllack_divck";
0272     hw = at91_clk_register_master_pres(regmap, "masterck_pres", 3,
0273                        parent_names, &sam9x60_master_layout,
0274                        &mck_characteristics, &mck_lock);
0275     if (IS_ERR(hw))
0276         goto err_free;
0277 
0278     hw = at91_clk_register_master_div(regmap, "masterck_div",
0279                       "masterck_pres", &sam9x60_master_layout,
0280                       &mck_characteristics, &mck_lock,
0281                       CLK_SET_RATE_GATE, 0);
0282     if (IS_ERR(hw))
0283         goto err_free;
0284 
0285     sam9x60_pmc->chws[PMC_MCK] = hw;
0286 
0287     parent_names[0] = "pllack_divck";
0288     parent_names[1] = "upllck_divck";
0289     parent_names[2] = "main_osc";
0290     hw = sam9x60_clk_register_usb(regmap, "usbck", parent_names, 3);
0291     if (IS_ERR(hw))
0292         goto err_free;
0293 
0294     parent_names[0] = md_slck_name;
0295     parent_names[1] = td_slck_name;
0296     parent_names[2] = "mainck";
0297     parent_names[3] = "masterck_div";
0298     parent_names[4] = "pllack_divck";
0299     parent_names[5] = "upllck_divck";
0300     for (i = 0; i < 2; i++) {
0301         char name[6];
0302 
0303         snprintf(name, sizeof(name), "prog%d", i);
0304 
0305         hw = at91_clk_register_programmable(regmap, name,
0306                             parent_names, 6, i,
0307                             &sam9x60_programmable_layout,
0308                             NULL);
0309         if (IS_ERR(hw))
0310             goto err_free;
0311 
0312         sam9x60_pmc->pchws[i] = hw;
0313     }
0314 
0315     for (i = 0; i < ARRAY_SIZE(sam9x60_systemck); i++) {
0316         hw = at91_clk_register_system(regmap, sam9x60_systemck[i].n,
0317                           sam9x60_systemck[i].p,
0318                           sam9x60_systemck[i].id);
0319         if (IS_ERR(hw))
0320             goto err_free;
0321 
0322         sam9x60_pmc->shws[sam9x60_systemck[i].id] = hw;
0323     }
0324 
0325     for (i = 0; i < ARRAY_SIZE(sam9x60_periphck); i++) {
0326         hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock,
0327                              &sam9x60_pcr_layout,
0328                              sam9x60_periphck[i].n,
0329                              "masterck_div",
0330                              sam9x60_periphck[i].id,
0331                              &range, INT_MIN);
0332         if (IS_ERR(hw))
0333             goto err_free;
0334 
0335         sam9x60_pmc->phws[sam9x60_periphck[i].id] = hw;
0336     }
0337 
0338     for (i = 0; i < ARRAY_SIZE(sam9x60_gck); i++) {
0339         hw = at91_clk_register_generated(regmap, &pmc_pcr_lock,
0340                          &sam9x60_pcr_layout,
0341                          sam9x60_gck[i].n,
0342                          parent_names, NULL, 6,
0343                          sam9x60_gck[i].id,
0344                          &sam9x60_gck[i].r, INT_MIN);
0345         if (IS_ERR(hw))
0346             goto err_free;
0347 
0348         sam9x60_pmc->ghws[sam9x60_gck[i].id] = hw;
0349     }
0350 
0351     of_clk_add_hw_provider(np, of_clk_hw_pmc_get, sam9x60_pmc);
0352 
0353     return;
0354 
0355 err_free:
0356     kfree(sam9x60_pmc);
0357 }
0358 /* Some clks are used for a clocksource */
0359 CLK_OF_DECLARE(sam9x60_pmc, "microchip,sam9x60-pmc", sam9x60_pmc_setup);