clk: at91: clk-master: split master clock in pres and divider
authorClaudiu Beznea <claudiu.beznea@microchip.com>
Fri, 16 Jul 2021 05:43:48 +0000 (08:43 +0300)
committerEugen Hristev <eugen.hristev@microchip.com>
Tue, 21 Sep 2021 07:05:37 +0000 (10:05 +0300)
Split master clock in 2 controlling block: one for prescaler one for
divider. This will allow referencing correctly the CPU clock and
master clock in device trees.

Reported-by: Eugen Hristev <eugen.hristev@microchip.com>
Fixes: a64862284f65 ("clk: at91: sam9x60: add support compatible with
CCF")
Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
drivers/clk/at91/clk-master.c
drivers/clk/at91/pmc.h
drivers/clk/at91/sam9x60.c
drivers/clk/at91/sama7g5.c

index 5d93e6a..aec0bca 100644 (file)
 #include <asm/processor.h>
 #include <clk-uclass.h>
 #include <common.h>
+#include <div64.h>
 #include <dm.h>
 #include <linux/clk-provider.h>
 #include <linux/clk/at91_pmc.h>
 
 #include "pmc.h"
 
-#define UBOOT_DM_CLK_AT91_MASTER               "at91-master-clk"
+#define UBOOT_DM_CLK_AT91_MASTER_PRES          "at91-master-clk-pres"
+#define UBOOT_DM_CLK_AT91_MASTER_DIV           "at91-master-clk-div"
 #define UBOOT_DM_CLK_AT91_SAMA7G5_MASTER       "at91-sama7g5-master-clk"
 
 #define MASTER_PRES_MASK       0x7
@@ -73,7 +75,7 @@ static int clk_master_enable(struct clk *clk)
        return 0;
 }
 
-static ulong clk_master_get_rate(struct clk *clk)
+static ulong clk_master_pres_get_rate(struct clk *clk)
 {
        struct clk_master *master = to_clk_master(clk);
        const struct clk_master_layout *layout = master->layout;
@@ -81,7 +83,7 @@ static ulong clk_master_get_rate(struct clk *clk)
                                                master->characteristics;
        ulong rate = clk_get_parent_rate(clk);
        unsigned int mckr;
-       u8 pres, div;
+       u8 pres;
 
        if (!rate)
                return 0;
@@ -90,29 +92,21 @@ static ulong clk_master_get_rate(struct clk *clk)
        mckr &= layout->mask;
 
        pres = (mckr >> layout->pres_shift) & MASTER_PRES_MASK;
-       div = (mckr >> MASTER_DIV_SHIFT) & MASTER_DIV_MASK;
 
        if (characteristics->have_div3_pres && pres == MASTER_PRES_MAX)
-               rate /= 3;
+               pres = 3;
        else
-               rate >>= pres;
-
-       rate /= characteristics->divisors[div];
-
-       if (rate < characteristics->output.min)
-               pr_warn("master clk is underclocked");
-       else if (rate > characteristics->output.max)
-               pr_warn("master clk is overclocked");
+               pres = (1 << pres);
 
-       return rate;
+       return DIV_ROUND_CLOSEST_ULL(rate, pres);
 }
 
-static const struct clk_ops master_ops = {
+static const struct clk_ops master_pres_ops = {
        .enable = clk_master_enable,
-       .get_rate = clk_master_get_rate,
+       .get_rate = clk_master_pres_get_rate,
 };
 
-struct clk *at91_clk_register_master(void __iomem *base,
+struct clk *at91_clk_register_master_pres(void __iomem *base,
                const char *name, const char * const *parent_names,
                int num_parents, const struct clk_master_layout *layout,
                const struct clk_master_characteristics *characteristics,
@@ -140,7 +134,7 @@ struct clk *at91_clk_register_master(void __iomem *base,
        pmc_read(master->base, master->layout->offset, &val);
        clk = &master->clk;
        clk->flags = CLK_GET_RATE_NOCACHE | CLK_IS_CRITICAL;
-       ret = clk_register(clk, UBOOT_DM_CLK_AT91_MASTER, name,
+       ret = clk_register(clk, UBOOT_DM_CLK_AT91_MASTER_PRES, name,
                           parent_names[val & AT91_PMC_CSS]);
        if (ret) {
                kfree(master);
@@ -150,10 +144,81 @@ struct clk *at91_clk_register_master(void __iomem *base,
        return clk;
 }
 
-U_BOOT_DRIVER(at91_master_clk) = {
-       .name = UBOOT_DM_CLK_AT91_MASTER,
+U_BOOT_DRIVER(at91_master_pres_clk) = {
+       .name = UBOOT_DM_CLK_AT91_MASTER_PRES,
+       .id = UCLASS_CLK,
+       .ops = &master_pres_ops,
+       .flags = DM_FLAG_PRE_RELOC,
+};
+
+static ulong clk_master_div_get_rate(struct clk *clk)
+{
+       struct clk_master *master = to_clk_master(clk);
+       const struct clk_master_layout *layout = master->layout;
+       const struct clk_master_characteristics *characteristics =
+                                               master->characteristics;
+       ulong rate = clk_get_parent_rate(clk);
+       unsigned int mckr;
+       u8 div;
+
+       if (!rate)
+               return 0;
+
+       pmc_read(master->base, master->layout->offset, &mckr);
+       mckr &= layout->mask;
+       div = (mckr >> MASTER_DIV_SHIFT) & MASTER_DIV_MASK;
+
+       rate = DIV_ROUND_CLOSEST_ULL(rate, characteristics->divisors[div]);
+       if (rate < characteristics->output.min)
+               pr_warn("master clk is underclocked");
+       else if (rate > characteristics->output.max)
+               pr_warn("master clk is overclocked");
+
+       return rate;
+}
+
+static const struct clk_ops master_div_ops = {
+       .enable = clk_master_enable,
+       .get_rate = clk_master_div_get_rate,
+};
+
+struct clk *at91_clk_register_master_div(void __iomem *base,
+               const char *name, const char *parent_name,
+               const struct clk_master_layout *layout,
+               const struct clk_master_characteristics *characteristics)
+{
+       struct clk_master *master;
+       struct clk *clk;
+       int ret;
+
+       if (!base || !name || !parent_name || !layout || !characteristics)
+               return ERR_PTR(-EINVAL);
+
+       master = kzalloc(sizeof(*master), GFP_KERNEL);
+       if (!master)
+               return ERR_PTR(-ENOMEM);
+
+       master->layout = layout;
+       master->characteristics = characteristics;
+       master->base = base;
+       master->num_parents = 1;
+
+       clk = &master->clk;
+       clk->flags = CLK_GET_RATE_NOCACHE | CLK_IS_CRITICAL;
+       ret = clk_register(clk, UBOOT_DM_CLK_AT91_MASTER_DIV, name,
+                          parent_name);
+       if (ret) {
+               kfree(master);
+               clk = ERR_PTR(ret);
+       }
+
+       return clk;
+}
+
+U_BOOT_DRIVER(at91_master_div_clk) = {
+       .name = UBOOT_DM_CLK_AT91_MASTER_DIV,
        .id = UCLASS_CLK,
-       .ops = &master_ops,
+       .ops = &master_div_ops,
        .flags = DM_FLAG_PRE_RELOC,
 };
 
index f07f535..2b4dd9a 100644 (file)
@@ -97,12 +97,17 @@ sam9x60_clk_register_frac_pll(void __iomem *base, const char *name,
                        const struct clk_pll_characteristics *characteristics,
                        const struct clk_pll_layout *layout, bool critical);
 struct clk *
-at91_clk_register_master(void __iomem *base, const char *name,
+at91_clk_register_master_pres(void __iomem *base, const char *name,
                        const char * const *parent_names, int num_parents,
                        const struct clk_master_layout *layout,
                        const struct clk_master_characteristics *characteristics,
                        const u32 *mux_table);
 struct clk *
+at91_clk_register_master_div(void __iomem *base,
+                       const char *name, const char *parent_name,
+                       const struct clk_master_layout *layout,
+                       const struct clk_master_characteristics *characteristics);
+struct clk *
 at91_clk_sama7g5_register_master(void __iomem *base, const char *name,
                        const char * const *parent_names, int num_parents,
                        const u32 *mux_table, const u32 *clk_mux_table,
index 9e9a643..4d00ee2 100644 (file)
@@ -31,7 +31,7 @@
  * @ID_PLL_A_FRAC:             APLL fractional clock identifier
  * @ID_PLL_A_DIV:              APLL divider clock identifier
 
- * @ID_MCK:                    MCK clock identifier
+ * @ID_MCK_DIV:                        MCK DIV clock identifier
 
  * @ID_UTMI:                   UTMI clock identifier
 
@@ -43,6 +43,8 @@
  * @ID_DDR:                    DDR system clock identifier
  * @ID_QSPI:                   QSPI system clock identifier
  *
+ * @ID_MCK_PRES:               MCK PRES clock identifier
+ *
  * Note: if changing the values of this enums please sync them with
  *       device tree
  */
@@ -60,7 +62,7 @@ enum pmc_clk_ids {
        ID_PLL_A_FRAC           = 9,
        ID_PLL_A_DIV            = 10,
 
-       ID_MCK                  = 11,
+       ID_MCK_DIV              = 11,
 
        ID_UTMI                 = 12,
 
@@ -73,6 +75,8 @@ enum pmc_clk_ids {
        ID_DDR                  = 17,
        ID_QSPI                 = 18,
 
+       ID_MCK_PRES             = 19,
+
        ID_MAX,
 };
 
@@ -93,7 +97,8 @@ static const char *clk_names[] = {
        [ID_MAINCK]             = "mainck",
        [ID_PLL_U_DIV]          = "upll_divpmcck",
        [ID_PLL_A_DIV]          = "plla_divpmcck",
-       [ID_MCK]                = "mck",
+       [ID_MCK_PRES]           = "mck_pres",
+       [ID_MCK_DIV]            = "mck_div",
 };
 
 /* Fractional PLL output range. */
@@ -260,10 +265,10 @@ static const struct {
        u8 id;
        u8 cid;
 } sam9x60_systemck[] = {
-       { .n = "ddrck",         .p = "mck", .id = 2, .cid = ID_DDR, },
+       { .n = "ddrck",         .p = "mck_pres", .id = 2, .cid = ID_DDR, },
        { .n = "pck0",          .p = "prog0",    .id = 8, .cid = ID_PCK0, },
        { .n = "pck1",          .p = "prog1",    .id = 9, .cid = ID_PCK1, },
-       { .n = "qspick",        .p = "mck", .id = 19, .cid = ID_QSPI, },
+       { .n = "qspick",        .p = "mck_pres", .id = 19, .cid = ID_QSPI, },
 };
 
 /**
@@ -508,7 +513,7 @@ static int sam9x60_clk_probe(struct udevice *dev)
                clk_dm(AT91_TO_CLK_ID(PMC_TYPE_CORE, sam9x60_plls[i].cid), c);
        }
 
-       /* Register MCK clock. */
+       /* Register MCK pres clock. */
        p[0] = clk_names[ID_MD_SLCK];
        p[1] = clk_names[ID_MAINCK];
        p[2] = clk_names[ID_PLL_A_DIV];
@@ -519,25 +524,36 @@ static int sam9x60_clk_probe(struct udevice *dev)
        cm[3] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_PLL_U_DIV);
        prepare_mux_table(clkmuxallocs, clkmuxallocindex, tmpclkmux, cm, 4,
                          fail);
-       c = at91_clk_register_master(base, clk_names[ID_MCK], p, 4, &mck_layout,
-                                    &mck_characteristics, tmpclkmux);
+       c = at91_clk_register_master_pres(base, clk_names[ID_MCK_PRES], p, 4,
+                                         &mck_layout, &mck_characteristics,
+                                         tmpclkmux);
+       if (IS_ERR(c)) {
+               ret = PTR_ERR(c);
+               goto fail;
+       }
+       clk_dm(AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MCK_PRES), c);
+
+       /* Register MCK div clock. */
+       c = at91_clk_register_master_div(base, clk_names[ID_MCK_DIV],
+                                        clk_names[ID_MCK_PRES],
+                                        &mck_layout, &mck_characteristics);
        if (IS_ERR(c)) {
                ret = PTR_ERR(c);
                goto fail;
        }
-       clk_dm(AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MCK), c);
+       clk_dm(AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MCK_DIV), c);
 
        /* Register programmable clocks. */
        p[0] = clk_names[ID_MD_SLCK];
        p[1] = clk_names[ID_TD_SLCK];
        p[2] = clk_names[ID_MAINCK];
-       p[3] = clk_names[ID_MCK];
+       p[3] = clk_names[ID_MCK_DIV];
        p[4] = clk_names[ID_PLL_A_DIV];
        p[5] = clk_names[ID_PLL_U_DIV];
        cm[0] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MD_SLCK);
        cm[1] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_TD_SLCK);
        cm[2] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MAINCK);
-       cm[3] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MCK);
+       cm[3] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MCK_DIV);
        cm[4] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_PLL_A_DIV);
        cm[5] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_PLL_U_DIV);
        for (i = 0; i < ARRAY_SIZE(sam9x60_prog); i++) {
@@ -572,7 +588,7 @@ static int sam9x60_clk_probe(struct udevice *dev)
        for (i = 0; i < ARRAY_SIZE(sam9x60_periphck); i++) {
                c = at91_clk_register_sam9x5_peripheral(base, &pcr_layout,
                                                        sam9x60_periphck[i].n,
-                                                       clk_names[ID_MCK],
+                                                       clk_names[ID_MCK_DIV],
                                                        sam9x60_periphck[i].id,
                                                        &r);
                if (IS_ERR(c)) {
@@ -587,7 +603,7 @@ static int sam9x60_clk_probe(struct udevice *dev)
        p[0] = clk_names[ID_MD_SLCK];
        p[1] = clk_names[ID_TD_SLCK];
        p[2] = clk_names[ID_MAINCK];
-       p[3] = clk_names[ID_MCK];
+       p[3] = clk_names[ID_MCK_DIV];
        p[4] = clk_names[ID_PLL_A_DIV];
        p[5] = clk_names[ID_PLL_U_DIV];
        m[0] = 0;
@@ -599,7 +615,7 @@ static int sam9x60_clk_probe(struct udevice *dev)
        cm[0] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MD_SLCK);
        cm[1] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_TD_SLCK);
        cm[2] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MAINCK);
-       cm[3] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MCK);
+       cm[3] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MCK_DIV);
        cm[4] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_PLL_A_DIV);
        cm[5] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_PLL_U_DIV);
        for (i = 0; i < ARRAY_SIZE(sam9x60_gck); i++) {
index c0d9271..d1ec3c8 100644 (file)
@@ -44,7 +44,8 @@
  * @ID_PLL_ETH_FRAC:           Ethernet PLL fractional clock identifier
  * @ID_PLL_ETH_DIV:            Ethernet PLL divider clock identifier
 
- * @ID_MCK0:                   MCK0 clock identifier
+ * @ID_MCK0_PRES:              MCK0 PRES clock identifier
+ * @ID_MCK0_DIV:               MCK0 DIV clock identifier
  * @ID_MCK1:                   MCK1 clock identifier
  * @ID_MCK2:                   MCK2 clock identifier
  * @ID_MCK3:                   MCK3 clock identifier
@@ -95,7 +96,7 @@ enum pmc_clk_ids {
        ID_PLL_ETH_FRAC         = 20,
        ID_PLL_ETH_DIV          = 21,
 
-       ID_MCK0                 = 22,
+       ID_MCK0_DIV             = 22,
        ID_MCK1                 = 23,
        ID_MCK2                 = 24,
        ID_MCK3                 = 25,
@@ -121,6 +122,8 @@ enum pmc_clk_ids {
        ID_PCK6                 = 42,
        ID_PCK7                 = 43,
 
+       ID_MCK0_PRES            = 44,
+
        ID_MAX,
 };
 
@@ -147,7 +150,8 @@ static const char *clk_names[] = {
        [ID_PLL_AUDIO_DIVPMC]   = "audiopll_divpmcck",
        [ID_PLL_AUDIO_DIVIO]    = "audiopll_diviock",
        [ID_PLL_ETH_DIV]        = "ethpll_divpmcck",
-       [ID_MCK0]               = "mck0",
+       [ID_MCK0_DIV]           = "mck0_div",
+       [ID_MCK0_PRES]          = "mck0_pres",
 };
 
 /* Fractional PLL output range. */
@@ -504,7 +508,7 @@ static const struct {
        struct clk_range r;
        u8 id;
 } sama7g5_periphck[] = {
-       { .n = "pioA_clk",      .p = "mck0", .id = 11, },
+       { .n = "pioA_clk",      .p = "mck0_div", .id = 11, },
        { .n = "sfr_clk",       .p = "mck1", .id = 19, },
        { .n = "hsmc_clk",      .p = "mck1", .id = 21, },
        { .n = "xdmac0_clk",    .p = "mck1", .id = 22, },
@@ -514,7 +518,7 @@ static const struct {
        { .n = "aes_clk",       .p = "mck1", .id = 27, },
        { .n = "tzaesbasc_clk", .p = "mck1", .id = 28, },
        { .n = "asrc_clk",      .p = "mck1", .id = 30, .r = { .max = 200000000, }, },
-       { .n = "cpkcc_clk",     .p = "mck0", .id = 32, },
+       { .n = "cpkcc_clk",     .p = "mck0_div", .id = 32, },
        { .n = "csi_clk",       .p = "mck3", .id = 33, .r = { .max = 266000000, }, },
        { .n = "csi2dc_clk",    .p = "mck3", .id = 34, .r = { .max = 266000000, }, },
        { .n = "eic_clk",       .p = "mck1", .id = 37, },
@@ -1210,7 +1214,7 @@ static int sama7g5_clk_probe(struct udevice *dev)
                        sama7g5_plls[i].c));
        }
 
-       /* Register MCK0 clock. */
+       /* Register MCK0_PRES clock. */
        p[0] = clk_names[ID_MD_SLCK];
        p[1] = clk_names[ID_MAINCK];
        p[2] = clk_names[ID_PLL_CPU_DIV];
@@ -1221,15 +1225,19 @@ static int sama7g5_clk_probe(struct udevice *dev)
        cm[3] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_PLL_SYS_DIV);
        prepare_mux_table(clkmuxallocs, clkmuxallocindex, tmpclkmux, cm, 2,
                          fail);
-       clk_dm(AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MCK0),
-               at91_clk_register_master(base, clk_names[ID_MCK0], p,
+       clk_dm(AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MCK0_PRES),
+               at91_clk_register_master_pres(base, clk_names[ID_MCK0_PRES], p,
                4, &mck0_layout, &mck0_characteristics, tmpclkmux));
 
+       clk_dm(AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MCK0_DIV),
+               at91_clk_register_master_div(base, clk_names[ID_MCK0_DIV],
+               clk_names[ID_MCK0_PRES], &mck0_layout, &mck0_characteristics));
+
        /* Register MCK1-4 clocks. */
        p[0] = clk_names[ID_MD_SLCK];
        p[1] = clk_names[ID_TD_SLCK];
        p[2] = clk_names[ID_MAINCK];
-       p[3] = clk_names[ID_MCK0];
+       p[3] = clk_names[ID_MCK0_DIV];
        m[0] = 0;
        m[1] = 1;
        m[2] = 2;
@@ -1237,7 +1245,7 @@ static int sama7g5_clk_probe(struct udevice *dev)
        cm[0] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MD_SLCK);
        cm[1] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_TD_SLCK);
        cm[2] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MAINCK);
-       cm[3] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MCK0);
+       cm[3] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MCK0_DIV);
        for (i = 0; i < ARRAY_SIZE(sama7g5_mckx); i++) {
                for (j = 0; j < sama7g5_mckx[i].ep_count; j++) {
                        p[4 + j] = sama7g5_mckx[i].ep[j];
@@ -1267,7 +1275,7 @@ static int sama7g5_clk_probe(struct udevice *dev)
        p[0] = clk_names[ID_MD_SLCK];
        p[1] = clk_names[ID_TD_SLCK];
        p[2] = clk_names[ID_MAINCK];
-       p[3] = clk_names[ID_MCK0];
+       p[3] = clk_names[ID_MCK0_DIV];
        p[4] = clk_names[ID_PLL_SYS_DIV];
        p[5] = clk_names[ID_PLL_DDR_DIV];
        p[6] = clk_names[ID_PLL_IMG_DIV];
@@ -1277,7 +1285,7 @@ static int sama7g5_clk_probe(struct udevice *dev)
        cm[0] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MD_SLCK);
        cm[1] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_TD_SLCK);
        cm[2] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MAINCK);
-       cm[3] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MCK0);
+       cm[3] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MCK0_DIV);
        cm[4] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_PLL_SYS_DIV);
        cm[5] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_PLL_DDR_DIV);
        cm[6] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_PLL_IMG_DIV);
@@ -1315,7 +1323,7 @@ static int sama7g5_clk_probe(struct udevice *dev)
        p[0] = clk_names[ID_MD_SLCK];
        p[1] = clk_names[ID_TD_SLCK];
        p[2] = clk_names[ID_MAINCK];
-       p[3] = clk_names[ID_MCK0];
+       p[3] = clk_names[ID_MCK0_DIV];
        m[0] = 0;
        m[1] = 1;
        m[2] = 2;
@@ -1323,7 +1331,7 @@ static int sama7g5_clk_probe(struct udevice *dev)
        cm[0] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MD_SLCK);
        cm[1] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_TD_SLCK);
        cm[2] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MAINCK);
-       cm[3] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MCK0);
+       cm[3] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MCK0_DIV);
        for (i = 0; i < ARRAY_SIZE(sama7g5_gck); i++) {
                for (j = 0; j < sama7g5_gck[i].ep_count; j++) {
                        p[4 + j] = sama7g5_gck[i].ep[j];