]> git.baikalelectronics.ru Git - kernel.git/commitdiff
clk: at91: clk-master: re-factor master clock
authorClaudiu Beznea <claudiu.beznea@microchip.com>
Thu, 19 Nov 2020 15:43:16 +0000 (17:43 +0200)
committerStephen Boyd <sboyd@kernel.org>
Sat, 19 Dec 2020 19:50:56 +0000 (11:50 -0800)
Re-factor master clock driver by splitting it into 2 clocks: prescaller
and divider clocks. Based on registered clock flags the prescaler's rate
could be changed at runtime. This is necessary for platforms supporting
DVFS (e.g. SAMA7G5) where master clock could be changed at run-time.

Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
Link: https://lore.kernel.org/r/1605800597-16720-11-git-send-email-claudiu.beznea@microchip.com
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
14 files changed:
drivers/clk/at91/at91rm9200.c
drivers/clk/at91/at91sam9260.c
drivers/clk/at91/at91sam9g45.c
drivers/clk/at91/at91sam9n12.c
drivers/clk/at91/at91sam9rl.c
drivers/clk/at91/at91sam9x5.c
drivers/clk/at91/clk-master.c
drivers/clk/at91/dt-compat.c
drivers/clk/at91/pmc.h
drivers/clk/at91/sam9x60.c
drivers/clk/at91/sama5d2.c
drivers/clk/at91/sama5d3.c
drivers/clk/at91/sama5d4.c
drivers/clk/at91/sama7g5.c

index 2c3d8e6ca63cacdfec37c8e9f468c2168af347ac..0fad1009f3152aec25a65df615242df1390e23cb 100644 (file)
@@ -7,6 +7,8 @@
 
 #include "pmc.h"
 
+static DEFINE_SPINLOCK(rm9200_mck_lock);
+
 struct sck {
        char *n;
        char *p;
@@ -137,9 +139,20 @@ static void __init at91rm9200_pmc_setup(struct device_node *np)
        parent_names[1] = "mainck";
        parent_names[2] = "pllack";
        parent_names[3] = "pllbck";
-       hw = at91_clk_register_master(regmap, "masterck", 4, parent_names,
-                                     &at91rm9200_master_layout,
-                                     &rm9200_mck_characteristics);
+       hw = at91_clk_register_master_pres(regmap, "masterck_pres", 4,
+                                          parent_names,
+                                          &at91rm9200_master_layout,
+                                          &rm9200_mck_characteristics,
+                                          &rm9200_mck_lock, CLK_SET_RATE_GATE,
+                                          INT_MIN);
+       if (IS_ERR(hw))
+               goto err_free;
+
+       hw = at91_clk_register_master_div(regmap, "masterck_div",
+                                         "masterck_pres",
+                                         &at91rm9200_master_layout,
+                                         &rm9200_mck_characteristics,
+                                         &rm9200_mck_lock, CLK_SET_RATE_GATE);
        if (IS_ERR(hw))
                goto err_free;
 
@@ -181,7 +194,7 @@ static void __init at91rm9200_pmc_setup(struct device_node *np)
        for (i = 0; i < ARRAY_SIZE(at91rm9200_periphck); i++) {
                hw = at91_clk_register_peripheral(regmap,
                                                  at91rm9200_periphck[i].n,
-                                                 "masterck",
+                                                 "masterck_div",
                                                  at91rm9200_periphck[i].id);
                if (IS_ERR(hw))
                        goto err_free;
index bb81ff731ad83009fb8cc7a66219107f65e44b29..ceb5495f723af8377da1954f79f85a679ee787a7 100644 (file)
@@ -32,6 +32,8 @@ struct at91sam926x_data {
        bool has_slck;
 };
 
+static DEFINE_SPINLOCK(at91sam9260_mck_lock);
+
 static const struct clk_master_characteristics sam9260_mck_characteristics = {
        .output = { .min = 0, .max = 105000000 },
        .divisors = { 1, 2, 4, 0 },
@@ -218,8 +220,8 @@ static const struct sck at91sam9261_systemck[] = {
        { .n = "pck1",  .p = "prog1",    .id = 9 },
        { .n = "pck2",  .p = "prog2",    .id = 10 },
        { .n = "pck3",  .p = "prog3",    .id = 11 },
-       { .n = "hclk0", .p = "masterck", .id = 16 },
-       { .n = "hclk1", .p = "masterck", .id = 17 },
+       { .n = "hclk0", .p = "masterck_div", .id = 16 },
+       { .n = "hclk1", .p = "masterck_div", .id = 17 },
 };
 
 static const struct pck at91sam9261_periphck[] = {
@@ -413,9 +415,21 @@ static void __init at91sam926x_pmc_setup(struct device_node *np,
        parent_names[1] = "mainck";
        parent_names[2] = "pllack";
        parent_names[3] = "pllbck";
-       hw = at91_clk_register_master(regmap, "masterck", 4, parent_names,
-                                     &at91rm9200_master_layout,
-                                     data->mck_characteristics);
+       hw = at91_clk_register_master_pres(regmap, "masterck_pres", 4,
+                                          parent_names,
+                                          &at91rm9200_master_layout,
+                                          data->mck_characteristics,
+                                          &at91sam9260_mck_lock,
+                                          CLK_SET_RATE_GATE, INT_MIN);
+       if (IS_ERR(hw))
+               goto err_free;
+
+       hw = at91_clk_register_master_div(regmap, "masterck_div",
+                                         "masterck_pres",
+                                         &at91rm9200_master_layout,
+                                         data->mck_characteristics,
+                                         &at91sam9260_mck_lock,
+                                         CLK_SET_RATE_GATE);
        if (IS_ERR(hw))
                goto err_free;
 
@@ -457,7 +471,7 @@ static void __init at91sam926x_pmc_setup(struct device_node *np,
        for (i = 0; i < data->num_pck; i++) {
                hw = at91_clk_register_peripheral(regmap,
                                                  data->pck[i].n,
-                                                 "masterck",
+                                                 "masterck_div",
                                                  data->pck[i].id);
                if (IS_ERR(hw))
                        goto err_free;
index cb4a406ed15dd0f5456061e7b77f248d67dadfe5..0214333dedd33f3f1cbf8776f55f59bfc3f0c862 100644 (file)
@@ -7,6 +7,8 @@
 
 #include "pmc.h"
 
+static DEFINE_SPINLOCK(at91sam9g45_mck_lock);
+
 static const struct clk_master_characteristics mck_characteristics = {
        .output = { .min = 0, .max = 133333333 },
        .divisors = { 1, 2, 4, 3 },
@@ -40,10 +42,10 @@ static const struct {
        char *p;
        u8 id;
 } at91sam9g45_systemck[] = {
-       { .n = "ddrck", .p = "masterck", .id = 2 },
-       { .n = "uhpck", .p = "usbck",    .id = 6 },
-       { .n = "pck0",  .p = "prog0",    .id = 8 },
-       { .n = "pck1",  .p = "prog1",    .id = 9 },
+       { .n = "ddrck", .p = "masterck_div", .id = 2 },
+       { .n = "uhpck", .p = "usbck",        .id = 6 },
+       { .n = "pck0",  .p = "prog0",        .id = 8 },
+       { .n = "pck1",  .p = "prog1",        .id = 9 },
 };
 
 struct pck {
@@ -148,9 +150,21 @@ static void __init at91sam9g45_pmc_setup(struct device_node *np)
        parent_names[1] = "mainck";
        parent_names[2] = "plladivck";
        parent_names[3] = "utmick";
-       hw = at91_clk_register_master(regmap, "masterck", 4, parent_names,
-                                     &at91rm9200_master_layout,
-                                     &mck_characteristics);
+       hw = at91_clk_register_master_pres(regmap, "masterck_pres", 4,
+                                          parent_names,
+                                          &at91rm9200_master_layout,
+                                          &mck_characteristics,
+                                          &at91sam9g45_mck_lock,
+                                          CLK_SET_RATE_GATE, INT_MIN);
+       if (IS_ERR(hw))
+               goto err_free;
+
+       hw = at91_clk_register_master_div(regmap, "masterck_div",
+                                         "masterck_pres",
+                                         &at91rm9200_master_layout,
+                                         &mck_characteristics,
+                                         &at91sam9g45_mck_lock,
+                                         CLK_SET_RATE_GATE);
        if (IS_ERR(hw))
                goto err_free;
 
@@ -166,7 +180,7 @@ static void __init at91sam9g45_pmc_setup(struct device_node *np)
        parent_names[1] = "mainck";
        parent_names[2] = "plladivck";
        parent_names[3] = "utmick";
-       parent_names[4] = "masterck";
+       parent_names[4] = "masterck_div";
        for (i = 0; i < 2; i++) {
                char name[6];
 
@@ -195,7 +209,7 @@ static void __init at91sam9g45_pmc_setup(struct device_node *np)
        for (i = 0; i < ARRAY_SIZE(at91sam9g45_periphck); i++) {
                hw = at91_clk_register_peripheral(regmap,
                                                  at91sam9g45_periphck[i].n,
-                                                 "masterck",
+                                                 "masterck_div",
                                                  at91sam9g45_periphck[i].id);
                if (IS_ERR(hw))
                        goto err_free;
index 93f7eb216122a4de1ec7176280a980aa672c8b83..f9db5316a7f1bad47c333956f36f57ed23cc20e5 100644 (file)
@@ -7,6 +7,8 @@
 
 #include "pmc.h"
 
+static DEFINE_SPINLOCK(at91sam9n12_mck_lock);
+
 static const struct clk_master_characteristics mck_characteristics = {
        .output = { .min = 0, .max = 133333333 },
        .divisors = { 1, 2, 4, 3 },
@@ -54,12 +56,12 @@ static const struct {
        char *p;
        u8 id;
 } at91sam9n12_systemck[] = {
-       { .n = "ddrck", .p = "masterck", .id = 2 },
-       { .n = "lcdck", .p = "masterck", .id = 3 },
-       { .n = "uhpck", .p = "usbck",    .id = 6 },
-       { .n = "udpck", .p = "usbck",    .id = 7 },
-       { .n = "pck0",  .p = "prog0",    .id = 8 },
-       { .n = "pck1",  .p = "prog1",    .id = 9 },
+       { .n = "ddrck", .p = "masterck_div", .id = 2 },
+       { .n = "lcdck", .p = "masterck_div", .id = 3 },
+       { .n = "uhpck", .p = "usbck",        .id = 6 },
+       { .n = "udpck", .p = "usbck",        .id = 7 },
+       { .n = "pck0",  .p = "prog0",        .id = 8 },
+       { .n = "pck1",  .p = "prog1",        .id = 9 },
 };
 
 static const struct clk_pcr_layout at91sam9n12_pcr_layout = {
@@ -175,9 +177,21 @@ static void __init at91sam9n12_pmc_setup(struct device_node *np)
        parent_names[1] = "mainck";
        parent_names[2] = "plladivck";
        parent_names[3] = "pllbck";
-       hw = at91_clk_register_master(regmap, "masterck", 4, parent_names,
-                                     &at91sam9x5_master_layout,
-                                     &mck_characteristics);
+       hw = at91_clk_register_master_pres(regmap, "masterck_pres", 4,
+                                          parent_names,
+                                          &at91sam9x5_master_layout,
+                                          &mck_characteristics,
+                                          &at91sam9n12_mck_lock,
+                                          CLK_SET_RATE_GATE, INT_MIN);
+       if (IS_ERR(hw))
+               goto err_free;
+
+       hw = at91_clk_register_master_div(regmap, "masterck_div",
+                                         "masterck_pres",
+                                         &at91sam9x5_master_layout,
+                                         &mck_characteristics,
+                                         &at91sam9n12_mck_lock,
+                                         CLK_SET_RATE_GATE);
        if (IS_ERR(hw))
                goto err_free;
 
@@ -191,7 +205,7 @@ static void __init at91sam9n12_pmc_setup(struct device_node *np)
        parent_names[1] = "mainck";
        parent_names[2] = "plladivck";
        parent_names[3] = "pllbck";
-       parent_names[4] = "masterck";
+       parent_names[4] = "masterck_div";
        for (i = 0; i < 2; i++) {
                char name[6];
 
@@ -221,7 +235,7 @@ static void __init at91sam9n12_pmc_setup(struct device_node *np)
                hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock,
                                                         &at91sam9n12_pcr_layout,
                                                         at91sam9n12_periphck[i].n,
-                                                        "masterck",
+                                                        "masterck_div",
                                                         at91sam9n12_periphck[i].id,
                                                         &range, INT_MIN);
                if (IS_ERR(hw))
index a343eb69bb353cd3049a3f42365dbfa5b345a741..66736e03cfef49539fc79059b43a1dbec7d797a7 100644 (file)
@@ -7,6 +7,8 @@
 
 #include "pmc.h"
 
+static DEFINE_SPINLOCK(sam9rl_mck_lock);
+
 static const struct clk_master_characteristics sam9rl_mck_characteristics = {
        .output = { .min = 0, .max = 94000000 },
        .divisors = { 1, 2, 4, 0 },
@@ -117,9 +119,20 @@ static void __init at91sam9rl_pmc_setup(struct device_node *np)
        parent_names[1] = "mainck";
        parent_names[2] = "pllack";
        parent_names[3] = "utmick";
-       hw = at91_clk_register_master(regmap, "masterck", 4, parent_names,
-                                     &at91rm9200_master_layout,
-                                     &sam9rl_mck_characteristics);
+       hw = at91_clk_register_master_pres(regmap, "masterck_pres", 4,
+                                          parent_names,
+                                          &at91rm9200_master_layout,
+                                          &sam9rl_mck_characteristics,
+                                          &sam9rl_mck_lock, CLK_SET_RATE_GATE,
+                                          INT_MIN);
+       if (IS_ERR(hw))
+               goto err_free;
+
+       hw = at91_clk_register_master_div(regmap, "masterck_div",
+                                         "masterck_pres",
+                                         &at91rm9200_master_layout,
+                                         &sam9rl_mck_characteristics,
+                                         &sam9rl_mck_lock, CLK_SET_RATE_GATE);
        if (IS_ERR(hw))
                goto err_free;
 
@@ -129,7 +142,7 @@ static void __init at91sam9rl_pmc_setup(struct device_node *np)
        parent_names[1] = "mainck";
        parent_names[2] = "pllack";
        parent_names[3] = "utmick";
-       parent_names[4] = "masterck";
+       parent_names[4] = "masterck_div";
        for (i = 0; i < 2; i++) {
                char name[6];
 
@@ -158,7 +171,7 @@ static void __init at91sam9rl_pmc_setup(struct device_node *np)
        for (i = 0; i < ARRAY_SIZE(at91sam9rl_periphck); i++) {
                hw = at91_clk_register_peripheral(regmap,
                                                  at91sam9rl_periphck[i].n,
-                                                 "masterck",
+                                                 "masterck_div",
                                                  at91sam9rl_periphck[i].id);
                if (IS_ERR(hw))
                        goto err_free;
index 22b9aad9efb8dbb5d3780eb299e8be8e2ca12f35..79b9d3667228dc51a5fc4ec49a5bb1f16487212e 100644 (file)
@@ -7,6 +7,8 @@
 
 #include "pmc.h"
 
+static DEFINE_SPINLOCK(mck_lock);
+
 static const struct clk_master_characteristics mck_characteristics = {
        .output = { .min = 0, .max = 133333333 },
        .divisors = { 1, 2, 4, 3 },
@@ -41,7 +43,7 @@ static const struct {
        char *p;
        u8 id;
 } at91sam9x5_systemck[] = {
-       { .n = "ddrck", .p = "masterck", .id = 2 },
+       { .n = "ddrck", .p = "masterck_div", .id = 2 },
        { .n = "smdck", .p = "smdclk",   .id = 4 },
        { .n = "uhpck", .p = "usbck",    .id = 6 },
        { .n = "udpck", .p = "usbck",    .id = 7 },
@@ -196,9 +198,19 @@ static void __init at91sam9x5_pmc_setup(struct device_node *np,
        parent_names[1] = "mainck";
        parent_names[2] = "plladivck";
        parent_names[3] = "utmick";
-       hw = at91_clk_register_master(regmap, "masterck", 4, parent_names,
-                                     &at91sam9x5_master_layout,
-                                     &mck_characteristics);
+       hw = at91_clk_register_master_pres(regmap, "masterck_pres", 4,
+                                          parent_names,
+                                          &at91sam9x5_master_layout,
+                                          &mck_characteristics, &mck_lock,
+                                          CLK_SET_RATE_GATE, INT_MIN);
+       if (IS_ERR(hw))
+               goto err_free;
+
+       hw = at91_clk_register_master_div(regmap, "masterck_div",
+                                         "masterck_pres",
+                                         &at91sam9x5_master_layout,
+                                         &mck_characteristics, &mck_lock,
+                                         CLK_SET_RATE_GATE);
        if (IS_ERR(hw))
                goto err_free;
 
@@ -218,7 +230,7 @@ static void __init at91sam9x5_pmc_setup(struct device_node *np,
        parent_names[1] = "mainck";
        parent_names[2] = "plladivck";
        parent_names[3] = "utmick";
-       parent_names[4] = "masterck";
+       parent_names[4] = "masterck_div";
        for (i = 0; i < 2; i++) {
                char name[6];
 
@@ -245,7 +257,7 @@ static void __init at91sam9x5_pmc_setup(struct device_node *np,
        }
 
        if (has_lcdck) {
-               hw = at91_clk_register_system(regmap, "lcdck", "masterck", 3);
+               hw = at91_clk_register_system(regmap, "lcdck", "masterck_div", 3);
                if (IS_ERR(hw))
                        goto err_free;
 
@@ -256,7 +268,7 @@ static void __init at91sam9x5_pmc_setup(struct device_node *np,
                hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock,
                                                         &at91sam9x5_pcr_layout,
                                                         at91sam9x5_periphck[i].n,
-                                                        "masterck",
+                                                        "masterck_div",
                                                         at91sam9x5_periphck[i].id,
                                                         &range, INT_MIN);
                if (IS_ERR(hw))
@@ -269,7 +281,7 @@ static void __init at91sam9x5_pmc_setup(struct device_node *np,
                hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock,
                                                         &at91sam9x5_pcr_layout,
                                                         extra_pcks[i].n,
-                                                        "masterck",
+                                                        "masterck_div",
                                                         extra_pcks[i].id,
                                                         &range, INT_MIN);
                if (IS_ERR(hw))
index aafd003b30cfbd69df6105d6e0a65b4a0a67ea13..a80427980bf73b1f48bda5bc9ec2aea6b360282c 100644 (file)
@@ -58,83 +58,309 @@ static inline bool clk_master_ready(struct clk_master *master)
 static int clk_master_prepare(struct clk_hw *hw)
 {
        struct clk_master *master = to_clk_master(hw);
+       unsigned long flags;
+
+       spin_lock_irqsave(master->lock, flags);
 
        while (!clk_master_ready(master))
                cpu_relax();
 
+       spin_unlock_irqrestore(master->lock, flags);
+
        return 0;
 }
 
 static int clk_master_is_prepared(struct clk_hw *hw)
 {
        struct clk_master *master = to_clk_master(hw);
+       unsigned long flags;
+       bool status;
 
-       return clk_master_ready(master);
+       spin_lock_irqsave(master->lock, flags);
+       status = clk_master_ready(master);
+       spin_unlock_irqrestore(master->lock, flags);
+
+       return status;
 }
 
-static unsigned long clk_master_recalc_rate(struct clk_hw *hw,
-                                           unsigned long parent_rate)
+static unsigned long clk_master_div_recalc_rate(struct clk_hw *hw,
+                                               unsigned long parent_rate)
 {
-       u8 pres;
        u8 div;
-       unsigned long rate = parent_rate;
+       unsigned long flags, rate = parent_rate;
        struct clk_master *master = to_clk_master(hw);
        const struct clk_master_layout *layout = master->layout;
        const struct clk_master_characteristics *characteristics =
                                                master->characteristics;
        unsigned int mckr;
 
+       spin_lock_irqsave(master->lock, flags);
        regmap_read(master->regmap, master->layout->offset, &mckr);
+       spin_unlock_irqrestore(master->lock, flags);
+
        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;
-       else
-               rate >>= pres;
-
        rate /= characteristics->divisors[div];
 
        if (rate < characteristics->output.min)
-               pr_warn("master clk is underclocked");
+               pr_warn("master clk div is underclocked");
        else if (rate > characteristics->output.max)
-               pr_warn("master clk is overclocked");
+               pr_warn("master clk div is overclocked");
 
        return rate;
 }
 
-static u8 clk_master_get_parent(struct clk_hw *hw)
+static const struct clk_ops master_div_ops = {
+       .prepare = clk_master_prepare,
+       .is_prepared = clk_master_is_prepared,
+       .recalc_rate = clk_master_div_recalc_rate,
+};
+
+static int clk_master_div_set_rate(struct clk_hw *hw, unsigned long rate,
+                                  unsigned long parent_rate)
+{
+       struct clk_master *master = to_clk_master(hw);
+       const struct clk_master_characteristics *characteristics =
+                                               master->characteristics;
+       unsigned long flags;
+       int div, i;
+
+       div = DIV_ROUND_CLOSEST(parent_rate, rate);
+       if (div > ARRAY_SIZE(characteristics->divisors))
+               return -EINVAL;
+
+       for (i = 0; i < ARRAY_SIZE(characteristics->divisors); i++) {
+               if (!characteristics->divisors[i])
+                       break;
+
+               if (div == characteristics->divisors[i]) {
+                       div = i;
+                       break;
+               }
+       }
+
+       if (i == ARRAY_SIZE(characteristics->divisors))
+               return -EINVAL;
+
+       spin_lock_irqsave(master->lock, flags);
+       regmap_update_bits(master->regmap, master->layout->offset,
+                          (MASTER_DIV_MASK << MASTER_DIV_SHIFT),
+                          (div << MASTER_DIV_SHIFT));
+       while (!clk_master_ready(master))
+               cpu_relax();
+       spin_unlock_irqrestore(master->lock, flags);
+
+       return 0;
+}
+
+static int clk_master_div_determine_rate(struct clk_hw *hw,
+                                        struct clk_rate_request *req)
+{
+       struct clk_master *master = to_clk_master(hw);
+       const struct clk_master_characteristics *characteristics =
+                                               master->characteristics;
+       struct clk_hw *parent;
+       unsigned long parent_rate, tmp_rate, best_rate = 0;
+       int i, best_diff = INT_MIN, tmp_diff;
+
+       parent = clk_hw_get_parent(hw);
+       if (!parent)
+               return -EINVAL;
+
+       parent_rate = clk_hw_get_rate(parent);
+       if (!parent_rate)
+               return -EINVAL;
+
+       for (i = 0; i < ARRAY_SIZE(characteristics->divisors); i++) {
+               if (!characteristics->divisors[i])
+                       break;
+
+               tmp_rate = DIV_ROUND_CLOSEST_ULL(parent_rate,
+                                                characteristics->divisors[i]);
+               tmp_diff = abs(tmp_rate - req->rate);
+
+               if (!best_rate || best_diff > tmp_diff) {
+                       best_diff = tmp_diff;
+                       best_rate = tmp_rate;
+               }
+
+               if (!best_diff)
+                       break;
+       }
+
+       req->best_parent_rate = best_rate;
+       req->best_parent_hw = parent;
+       req->rate = best_rate;
+
+       return 0;
+}
+
+static const struct clk_ops master_div_ops_chg = {
+       .prepare = clk_master_prepare,
+       .is_prepared = clk_master_is_prepared,
+       .recalc_rate = clk_master_div_recalc_rate,
+       .determine_rate = clk_master_div_determine_rate,
+       .set_rate = clk_master_div_set_rate,
+};
+
+static void clk_sama7g5_master_best_diff(struct clk_rate_request *req,
+                                        struct clk_hw *parent,
+                                        unsigned long parent_rate,
+                                        long *best_rate,
+                                        long *best_diff,
+                                        u32 div)
+{
+       unsigned long tmp_rate, tmp_diff;
+
+       if (div == MASTER_PRES_MAX)
+               tmp_rate = parent_rate / 3;
+       else
+               tmp_rate = parent_rate >> div;
+
+       tmp_diff = abs(req->rate - tmp_rate);
+
+       if (*best_diff < 0 || *best_diff >= tmp_diff) {
+               *best_rate = tmp_rate;
+               *best_diff = tmp_diff;
+               req->best_parent_rate = parent_rate;
+               req->best_parent_hw = parent;
+       }
+}
+
+static int clk_master_pres_determine_rate(struct clk_hw *hw,
+                                         struct clk_rate_request *req)
 {
        struct clk_master *master = to_clk_master(hw);
+       struct clk_rate_request req_parent = *req;
+       const struct clk_master_characteristics *characteristics =
+                                                       master->characteristics;
+       struct clk_hw *parent;
+       long best_rate = LONG_MIN, best_diff = LONG_MIN;
+       u32 pres;
+       int i;
+
+       if (master->chg_pid < 0)
+               return -EOPNOTSUPP;
+
+       parent = clk_hw_get_parent_by_index(hw, master->chg_pid);
+       if (!parent)
+               return -EOPNOTSUPP;
+
+       for (i = 0; i <= MASTER_PRES_MAX; i++) {
+               if (characteristics->have_div3_pres && i == MASTER_PRES_MAX)
+                       pres = 3;
+               else
+                       pres = 1 << i;
+
+               req_parent.rate = req->rate * pres;
+               if (__clk_determine_rate(parent, &req_parent))
+                       continue;
+
+               clk_sama7g5_master_best_diff(req, parent, req_parent.rate,
+                                            &best_diff, &best_rate, pres);
+               if (!best_diff)
+                       break;
+       }
+
+       return 0;
+}
+
+static int clk_master_pres_set_rate(struct clk_hw *hw, unsigned long rate,
+                                   unsigned long parent_rate)
+{
+       struct clk_master *master = to_clk_master(hw);
+       unsigned long flags;
+       unsigned int pres;
+
+       pres = DIV_ROUND_CLOSEST(parent_rate, rate);
+       if (pres > MASTER_PRES_MAX)
+               return -EINVAL;
+
+       else if (pres == 3)
+               pres = MASTER_PRES_MAX;
+       else
+               pres = ffs(pres) - 1;
+
+       spin_lock_irqsave(master->lock, flags);
+       regmap_update_bits(master->regmap, master->layout->offset,
+                          (MASTER_PRES_MASK << master->layout->pres_shift),
+                          (pres << master->layout->pres_shift));
+
+       while (!clk_master_ready(master))
+               cpu_relax();
+       spin_unlock_irqrestore(master->lock, flags);
+
+       return 0;
+}
+
+static unsigned long clk_master_pres_recalc_rate(struct clk_hw *hw,
+                                                unsigned long parent_rate)
+{
+       struct clk_master *master = to_clk_master(hw);
+       const struct clk_master_characteristics *characteristics =
+                                               master->characteristics;
+       unsigned long flags;
+       unsigned int val, pres;
+
+       spin_lock_irqsave(master->lock, flags);
+       regmap_read(master->regmap, master->layout->offset, &val);
+       spin_unlock_irqrestore(master->lock, flags);
+
+       pres = (val >> master->layout->pres_shift) & MASTER_PRES_MASK;
+       if (pres == 3 && characteristics->have_div3_pres)
+               pres = 3;
+       else
+               pres = (1 << pres);
+
+       return DIV_ROUND_CLOSEST_ULL(parent_rate, pres);
+}
+
+static u8 clk_master_pres_get_parent(struct clk_hw *hw)
+{
+       struct clk_master *master = to_clk_master(hw);
+       unsigned long flags;
        unsigned int mckr;
 
+       spin_lock_irqsave(master->lock, flags);
        regmap_read(master->regmap, master->layout->offset, &mckr);
+       spin_unlock_irqrestore(master->lock, flags);
 
        return mckr & AT91_PMC_CSS;
 }
 
-static const struct clk_ops master_ops = {
+static const struct clk_ops master_pres_ops = {
        .prepare = clk_master_prepare,
        .is_prepared = clk_master_is_prepared,
-       .recalc_rate = clk_master_recalc_rate,
-       .get_parent = clk_master_get_parent,
+       .recalc_rate = clk_master_pres_recalc_rate,
+       .get_parent = clk_master_pres_get_parent,
 };
 
-struct clk_hw * __init
-at91_clk_register_master(struct regmap *regmap,
+static const struct clk_ops master_pres_ops_chg = {
+       .prepare = clk_master_prepare,
+       .is_prepared = clk_master_is_prepared,
+       .determine_rate = clk_master_pres_determine_rate,
+       .recalc_rate = clk_master_pres_recalc_rate,
+       .get_parent = clk_master_pres_get_parent,
+       .set_rate = clk_master_pres_set_rate,
+};
+
+static struct clk_hw * __init
+at91_clk_register_master_internal(struct regmap *regmap,
                const char *name, int num_parents,
                const char **parent_names,
                const struct clk_master_layout *layout,
-               const struct clk_master_characteristics *characteristics)
+               const struct clk_master_characteristics *characteristics,
+               const struct clk_ops *ops, spinlock_t *lock, u32 flags,
+               int chg_pid)
 {
        struct clk_master *master;
        struct clk_init_data init;
        struct clk_hw *hw;
        int ret;
 
-       if (!name || !num_parents || !parent_names)
+       if (!name || !num_parents || !parent_names || !lock)
                return ERR_PTR(-EINVAL);
 
        master = kzalloc(sizeof(*master), GFP_KERNEL);
@@ -142,15 +368,17 @@ at91_clk_register_master(struct regmap *regmap,
                return ERR_PTR(-ENOMEM);
 
        init.name = name;
-       init.ops = &master_ops;
+       init.ops = ops;
        init.parent_names = parent_names;
        init.num_parents = num_parents;
-       init.flags = 0;
+       init.flags = flags;
 
        master->hw.init = &init;
        master->layout = layout;
        master->characteristics = characteristics;
        master->regmap = regmap;
+       master->chg_pid = chg_pid;
+       master->lock = lock;
 
        hw = &master->hw;
        ret = clk_hw_register(NULL, &master->hw);
@@ -162,37 +390,54 @@ at91_clk_register_master(struct regmap *regmap,
        return hw;
 }
 
-static unsigned long
-clk_sama7g5_master_recalc_rate(struct clk_hw *hw,
-                              unsigned long parent_rate)
+struct clk_hw * __init
+at91_clk_register_master_pres(struct regmap *regmap,
+               const char *name, int num_parents,
+               const char **parent_names,
+               const struct clk_master_layout *layout,
+               const struct clk_master_characteristics *characteristics,
+               spinlock_t *lock, u32 flags, int chg_pid)
 {
-       struct clk_master *master = to_clk_master(hw);
+       const struct clk_ops *ops;
 
-       return DIV_ROUND_CLOSEST_ULL(parent_rate, (1 << master->div));
+       if (flags & CLK_SET_RATE_GATE)
+               ops = &master_pres_ops;
+       else
+               ops = &master_pres_ops_chg;
+
+       return at91_clk_register_master_internal(regmap, name, num_parents,
+                                                parent_names, layout,
+                                                characteristics, ops,
+                                                lock, flags, chg_pid);
 }
 
-static void clk_sama7g5_master_best_diff(struct clk_rate_request *req,
-                                        struct clk_hw *parent,
-                                        unsigned long parent_rate,
-                                        long *best_rate,
-                                        long *best_diff,
-                                        u32 div)
+struct clk_hw * __init
+at91_clk_register_master_div(struct regmap *regmap,
+               const char *name, const char *parent_name,
+               const struct clk_master_layout *layout,
+               const struct clk_master_characteristics *characteristics,
+               spinlock_t *lock, u32 flags)
 {
-       unsigned long tmp_rate, tmp_diff;
+       const struct clk_ops *ops;
 
-       if (div == MASTER_PRES_MAX)
-               tmp_rate = parent_rate / 3;
+       if (flags & CLK_SET_RATE_GATE)
+               ops = &master_div_ops;
        else
-               tmp_rate = parent_rate >> div;
+               ops = &master_div_ops_chg;
 
-       tmp_diff = abs(req->rate - tmp_rate);
+       return at91_clk_register_master_internal(regmap, name, 1,
+                                                &parent_name, layout,
+                                                characteristics, ops,
+                                                lock, flags, -EINVAL);
+}
 
-       if (*best_diff < 0 || *best_diff >= tmp_diff) {
-               *best_rate = tmp_rate;
-               *best_diff = tmp_diff;
-               req->best_parent_rate = parent_rate;
-               req->best_parent_hw = parent;
-       }
+static unsigned long
+clk_sama7g5_master_recalc_rate(struct clk_hw *hw,
+                              unsigned long parent_rate)
+{
+       struct clk_master *master = to_clk_master(hw);
+
+       return DIV_ROUND_CLOSEST_ULL(parent_rate, (1 << master->div));
 }
 
 static int clk_sama7g5_master_determine_rate(struct clk_hw *hw,
index a50084de97d41b7919d0ad3f2af08a5c41d8cb57..a97b99c2dc127fd98636a707a81a8a20deb8ca7b 100644 (file)
@@ -24,6 +24,8 @@
 
 #define GCK_INDEX_DT_AUDIO_PLL 5
 
+static DEFINE_SPINLOCK(mck_lock);
+
 #ifdef CONFIG_HAVE_AT91_AUDIO_PLL
 static void __init of_sama5d2_clk_audio_pll_frac_setup(struct device_node *np)
 {
@@ -388,9 +390,16 @@ of_at91_clk_master_setup(struct device_node *np,
        if (IS_ERR(regmap))
                return;
 
-       hw = at91_clk_register_master(regmap, name, num_parents,
-                                     parent_names, layout,
-                                     characteristics);
+       hw = at91_clk_register_master_pres(regmap, "masterck_pres", num_parents,
+                                          parent_names, layout,
+                                          characteristics, &mck_lock,
+                                          CLK_SET_RATE_GATE, INT_MIN);
+       if (IS_ERR(hw))
+               goto out_free_characteristics;
+
+       hw = at91_clk_register_master_div(regmap, name, "masterck_pres",
+                                         layout, characteristics,
+                                         &mck_lock, CLK_SET_RATE_GATE);
        if (IS_ERR(hw))
                goto out_free_characteristics;
 
index bedcd85ad7509d6518bb305b5aa6619f4e16cded..a49076c804a9a724e7cfcee7759babf7fa1c7541 100644 (file)
@@ -155,10 +155,18 @@ at91_clk_register_sam9x5_main(struct regmap *regmap, const char *name,
                              const char **parent_names, int num_parents);
 
 struct clk_hw * __init
-at91_clk_register_master(struct regmap *regmap, const char *name,
-                        int num_parents, const char **parent_names,
-                        const struct clk_master_layout *layout,
-                        const struct clk_master_characteristics *characteristics);
+at91_clk_register_master_pres(struct regmap *regmap, const char *name,
+                             int num_parents, const char **parent_names,
+                             const struct clk_master_layout *layout,
+                             const struct clk_master_characteristics *characteristics,
+                             spinlock_t *lock, u32 flags, int chg_pid);
+
+struct clk_hw * __init
+at91_clk_register_master_div(struct regmap *regmap, const char *name,
+                            const char *parent_names,
+                            const struct clk_master_layout *layout,
+                            const struct clk_master_characteristics *characteristics,
+                            spinlock_t *lock, u32 flags);
 
 struct clk_hw * __init
 at91_clk_sama7g5_register_master(struct regmap *regmap,
index dd62bb2880cf09f5c03a65a43ceeafe69f601678..240e48149bcdcb4413986e017603ea393dced175 100644 (file)
@@ -8,6 +8,7 @@
 #include "pmc.h"
 
 static DEFINE_SPINLOCK(pmc_pll_lock);
+static DEFINE_SPINLOCK(mck_lock);
 
 static const struct clk_master_characteristics mck_characteristics = {
        .output = { .min = 140000000, .max = 200000000 },
@@ -76,11 +77,11 @@ static const struct {
        char *p;
        u8 id;
 } sam9x60_systemck[] = {
-       { .n = "ddrck",  .p = "masterck", .id = 2 },
+       { .n = "ddrck",  .p = "masterck_div", .id = 2 },
        { .n = "uhpck",  .p = "usbck",    .id = 6 },
        { .n = "pck0",   .p = "prog0",    .id = 8 },
        { .n = "pck1",   .p = "prog1",    .id = 9 },
-       { .n = "qspick", .p = "masterck", .id = 19 },
+       { .n = "qspick", .p = "masterck_div", .id = 19 },
 };
 
 static const struct {
@@ -272,9 +273,17 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
        parent_names[0] = md_slck_name;
        parent_names[1] = "mainck";
        parent_names[2] = "pllack_divck";
-       hw = at91_clk_register_master(regmap, "masterck", 3, parent_names,
-                                     &sam9x60_master_layout,
-                                     &mck_characteristics);
+       hw = at91_clk_register_master_pres(regmap, "masterck_pres", 3,
+                                          parent_names, &sam9x60_master_layout,
+                                          &mck_characteristics, &mck_lock,
+                                          CLK_SET_RATE_GATE, INT_MIN);
+       if (IS_ERR(hw))
+               goto err_free;
+
+       hw = at91_clk_register_master_div(regmap, "masterck_div",
+                                         "masterck_pres", &sam9x60_master_layout,
+                                         &mck_characteristics, &mck_lock,
+                                         CLK_SET_RATE_GATE);
        if (IS_ERR(hw))
                goto err_free;
 
@@ -290,7 +299,7 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
        parent_names[0] = md_slck_name;
        parent_names[1] = td_slck_name;
        parent_names[2] = "mainck";
-       parent_names[3] = "masterck";
+       parent_names[3] = "masterck_div";
        parent_names[4] = "pllack_divck";
        parent_names[5] = "upllck_divck";
        for (i = 0; i < 2; i++) {
@@ -322,7 +331,7 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
                hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock,
                                                         &sam9x60_pcr_layout,
                                                         sam9x60_periphck[i].n,
-                                                        "masterck",
+                                                        "masterck_div",
                                                         sam9x60_periphck[i].id,
                                                         &range, INT_MIN);
                if (IS_ERR(hw))
index 8b220762941aba7d125fa23afdeb16435ea1f1d2..9a5cbc7cd55a4f8ced9aa5d39a6104d6cc8e3369 100644 (file)
@@ -7,6 +7,8 @@
 
 #include "pmc.h"
 
+static DEFINE_SPINLOCK(mck_lock);
+
 static const struct clk_master_characteristics mck_characteristics = {
        .output = { .min = 124000000, .max = 166000000 },
        .divisors = { 1, 2, 4, 3 },
@@ -40,14 +42,14 @@ static const struct {
        char *p;
        u8 id;
 } sama5d2_systemck[] = {
-       { .n = "ddrck", .p = "masterck", .id = 2 },
-       { .n = "lcdck", .p = "masterck", .id = 3 },
-       { .n = "uhpck", .p = "usbck",    .id = 6 },
-       { .n = "udpck", .p = "usbck",    .id = 7 },
-       { .n = "pck0",  .p = "prog0",    .id = 8 },
-       { .n = "pck1",  .p = "prog1",    .id = 9 },
-       { .n = "pck2",  .p = "prog2",    .id = 10 },
-       { .n = "iscck", .p = "masterck", .id = 18 },
+       { .n = "ddrck", .p = "masterck_div", .id = 2 },
+       { .n = "lcdck", .p = "masterck_div", .id = 3 },
+       { .n = "uhpck", .p = "usbck",        .id = 6 },
+       { .n = "udpck", .p = "usbck",        .id = 7 },
+       { .n = "pck0",  .p = "prog0",        .id = 8 },
+       { .n = "pck1",  .p = "prog1",        .id = 9 },
+       { .n = "pck2",  .p = "prog2",        .id = 10 },
+       { .n = "iscck", .p = "masterck_div", .id = 18 },
 };
 
 static const struct {
@@ -235,15 +237,25 @@ static void __init sama5d2_pmc_setup(struct device_node *np)
        parent_names[1] = "mainck";
        parent_names[2] = "plladivck";
        parent_names[3] = "utmick";
-       hw = at91_clk_register_master(regmap, "masterck", 4, parent_names,
-                                     &at91sam9x5_master_layout,
-                                     &mck_characteristics);
+       hw = at91_clk_register_master_pres(regmap, "masterck_pres", 4,
+                                          parent_names,
+                                          &at91sam9x5_master_layout,
+                                          &mck_characteristics, &mck_lock,
+                                          CLK_SET_RATE_GATE, INT_MIN);
+       if (IS_ERR(hw))
+               goto err_free;
+
+       hw = at91_clk_register_master_div(regmap, "masterck_div",
+                                         "masterck_pres",
+                                         &at91sam9x5_master_layout,
+                                         &mck_characteristics, &mck_lock,
+                                         CLK_SET_RATE_GATE);
        if (IS_ERR(hw))
                goto err_free;
 
        sama5d2_pmc->chws[PMC_MCK] = hw;
 
-       hw = at91_clk_register_h32mx(regmap, "h32mxck", "masterck");
+       hw = at91_clk_register_h32mx(regmap, "h32mxck", "masterck_div");
        if (IS_ERR(hw))
                goto err_free;
 
@@ -259,7 +271,7 @@ static void __init sama5d2_pmc_setup(struct device_node *np)
        parent_names[1] = "mainck";
        parent_names[2] = "plladivck";
        parent_names[3] = "utmick";
-       parent_names[4] = "masterck";
+       parent_names[4] = "masterck_div";
        parent_names[5] = "audiopll_pmcck";
        for (i = 0; i < 3; i++) {
                char name[6];
@@ -290,7 +302,7 @@ static void __init sama5d2_pmc_setup(struct device_node *np)
                hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock,
                                                         &sama5d2_pcr_layout,
                                                         sama5d2_periphck[i].n,
-                                                        "masterck",
+                                                        "masterck_div",
                                                         sama5d2_periphck[i].id,
                                                         &range, INT_MIN);
                if (IS_ERR(hw))
@@ -317,7 +329,7 @@ static void __init sama5d2_pmc_setup(struct device_node *np)
        parent_names[1] = "mainck";
        parent_names[2] = "plladivck";
        parent_names[3] = "utmick";
-       parent_names[4] = "masterck";
+       parent_names[4] = "masterck_div";
        parent_names[5] = "audiopll_pmcck";
        for (i = 0; i < ARRAY_SIZE(sama5d2_gck); i++) {
                hw = at91_clk_register_generated(regmap, &pmc_pcr_lock,
index 7c6e0a5b9dc87088caa2427a4b9197b87dc50a69..87009ee8effc991c06f9a348c6cbb969c2acef06 100644 (file)
@@ -7,6 +7,8 @@
 
 #include "pmc.h"
 
+static DEFINE_SPINLOCK(mck_lock);
+
 static const struct clk_master_characteristics mck_characteristics = {
        .output = { .min = 0, .max = 166000000 },
        .divisors = { 1, 2, 4, 3 },
@@ -40,14 +42,14 @@ static const struct {
        char *p;
        u8 id;
 } sama5d3_systemck[] = {
-       { .n = "ddrck", .p = "masterck", .id = 2 },
-       { .n = "lcdck", .p = "masterck", .id = 3 },
-       { .n = "smdck", .p = "smdclk",   .id = 4 },
-       { .n = "uhpck", .p = "usbck",    .id = 6 },
-       { .n = "udpck", .p = "usbck",    .id = 7 },
-       { .n = "pck0",  .p = "prog0",    .id = 8 },
-       { .n = "pck1",  .p = "prog1",    .id = 9 },
-       { .n = "pck2",  .p = "prog2",    .id = 10 },
+       { .n = "ddrck", .p = "masterck_div", .id = 2 },
+       { .n = "lcdck", .p = "masterck_div", .id = 3 },
+       { .n = "smdck", .p = "smdclk",       .id = 4 },
+       { .n = "uhpck", .p = "usbck",        .id = 6 },
+       { .n = "udpck", .p = "usbck",        .id = 7 },
+       { .n = "pck0",  .p = "prog0",        .id = 8 },
+       { .n = "pck1",  .p = "prog1",        .id = 9 },
+       { .n = "pck2",  .p = "prog2",        .id = 10 },
 };
 
 static const struct {
@@ -170,9 +172,19 @@ static void __init sama5d3_pmc_setup(struct device_node *np)
        parent_names[1] = "mainck";
        parent_names[2] = "plladivck";
        parent_names[3] = "utmick";
-       hw = at91_clk_register_master(regmap, "masterck", 4, parent_names,
-                                     &at91sam9x5_master_layout,
-                                     &mck_characteristics);
+       hw = at91_clk_register_master_pres(regmap, "masterck_pres", 4,
+                                          parent_names,
+                                          &at91sam9x5_master_layout,
+                                          &mck_characteristics, &mck_lock,
+                                          CLK_SET_RATE_GATE, INT_MIN);
+       if (IS_ERR(hw))
+               goto err_free;
+
+       hw = at91_clk_register_master_div(regmap, "masterck_div",
+                                         "masterck_pres",
+                                         &at91sam9x5_master_layout,
+                                         &mck_characteristics, &mck_lock,
+                                         CLK_SET_RATE_GATE);
        if (IS_ERR(hw))
                goto err_free;
 
@@ -192,7 +204,7 @@ static void __init sama5d3_pmc_setup(struct device_node *np)
        parent_names[1] = "mainck";
        parent_names[2] = "plladivck";
        parent_names[3] = "utmick";
-       parent_names[4] = "masterck";
+       parent_names[4] = "masterck_div";
        for (i = 0; i < 3; i++) {
                char name[6];
 
@@ -222,7 +234,7 @@ static void __init sama5d3_pmc_setup(struct device_node *np)
                hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock,
                                                         &sama5d3_pcr_layout,
                                                         sama5d3_periphck[i].n,
-                                                        "masterck",
+                                                        "masterck_div",
                                                         sama5d3_periphck[i].id,
                                                         &sama5d3_periphck[i].r,
                                                         INT_MIN);
index 92d8d4141b4335b1aa39dba170cc8f3f8ff47d48..57fff790188b6aa8a5e25aee173692ecd7885660 100644 (file)
@@ -7,6 +7,8 @@
 
 #include "pmc.h"
 
+static DEFINE_SPINLOCK(mck_lock);
+
 static const struct clk_master_characteristics mck_characteristics = {
        .output = { .min = 125000000, .max = 200000000 },
        .divisors = { 1, 2, 4, 3 },
@@ -39,14 +41,14 @@ static const struct {
        char *p;
        u8 id;
 } sama5d4_systemck[] = {
-       { .n = "ddrck", .p = "masterck", .id = 2 },
-       { .n = "lcdck", .p = "masterck", .id = 3 },
-       { .n = "smdck", .p = "smdclk",   .id = 4 },
-       { .n = "uhpck", .p = "usbck",    .id = 6 },
-       { .n = "udpck", .p = "usbck",    .id = 7 },
-       { .n = "pck0",  .p = "prog0",    .id = 8 },
-       { .n = "pck1",  .p = "prog1",    .id = 9 },
-       { .n = "pck2",  .p = "prog2",    .id = 10 },
+       { .n = "ddrck", .p = "masterck_div", .id = 2 },
+       { .n = "lcdck", .p = "masterck_div", .id = 3 },
+       { .n = "smdck", .p = "smdclk",       .id = 4 },
+       { .n = "uhpck", .p = "usbck",        .id = 6 },
+       { .n = "udpck", .p = "usbck",        .id = 7 },
+       { .n = "pck0",  .p = "prog0",        .id = 8 },
+       { .n = "pck1",  .p = "prog1",        .id = 9 },
+       { .n = "pck2",  .p = "prog2",        .id = 10 },
 };
 
 static const struct {
@@ -185,15 +187,25 @@ static void __init sama5d4_pmc_setup(struct device_node *np)
        parent_names[1] = "mainck";
        parent_names[2] = "plladivck";
        parent_names[3] = "utmick";
-       hw = at91_clk_register_master(regmap, "masterck", 4, parent_names,
-                                     &at91sam9x5_master_layout,
-                                     &mck_characteristics);
+       hw = at91_clk_register_master_pres(regmap, "masterck_pres", 4,
+                                          parent_names,
+                                          &at91sam9x5_master_layout,
+                                          &mck_characteristics, &mck_lock,
+                                          CLK_SET_RATE_GATE, INT_MIN);
+       if (IS_ERR(hw))
+               goto err_free;
+
+       hw = at91_clk_register_master_div(regmap, "masterck_div",
+                                         "masterck_pres",
+                                         &at91sam9x5_master_layout,
+                                         &mck_characteristics, &mck_lock,
+                                         CLK_SET_RATE_GATE);
        if (IS_ERR(hw))
                goto err_free;
 
        sama5d4_pmc->chws[PMC_MCK] = hw;
 
-       hw = at91_clk_register_h32mx(regmap, "h32mxck", "masterck");
+       hw = at91_clk_register_h32mx(regmap, "h32mxck", "masterck_div");
        if (IS_ERR(hw))
                goto err_free;
 
@@ -215,7 +227,7 @@ static void __init sama5d4_pmc_setup(struct device_node *np)
        parent_names[1] = "mainck";
        parent_names[2] = "plladivck";
        parent_names[3] = "utmick";
-       parent_names[4] = "masterck";
+       parent_names[4] = "masterck_div";
        for (i = 0; i < 3; i++) {
                char name[6];
 
@@ -245,7 +257,7 @@ static void __init sama5d4_pmc_setup(struct device_node *np)
                hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock,
                                                         &sama5d4_pcr_layout,
                                                         sama5d4_periphck[i].n,
-                                                        "masterck",
+                                                        "masterck_div",
                                                         sama5d4_periphck[i].id,
                                                         &range, INT_MIN);
                if (IS_ERR(hw))
index e0c4d2eb9f597faa3d0488da2a2b6d5ecbb5af61..927eb3b2b126d36e2a6898df2df6d59990fd13bb 100644 (file)
@@ -32,6 +32,7 @@
        } while (0)
 
 static DEFINE_SPINLOCK(pmc_pll_lock);
+static DEFINE_SPINLOCK(pmc_mck0_lock);
 static DEFINE_SPINLOCK(pmc_mckX_lock);
 
 /**
@@ -984,8 +985,16 @@ static void __init sama7g5_pmc_setup(struct device_node *np)
        parent_names[1] = "mainck";
        parent_names[2] = "cpupll_divpmcck";
        parent_names[3] = "syspll_divpmcck";
-       hw = at91_clk_register_master(regmap, "mck0", 4, parent_names,
-                                     &mck0_layout, &mck0_characteristics);
+       hw = at91_clk_register_master_pres(regmap, "mck0_pres", 4, parent_names,
+                                          &mck0_layout, &mck0_characteristics,
+                                          &pmc_mck0_lock,
+                                          CLK_SET_RATE_PARENT, 0);
+       if (IS_ERR(hw))
+               goto err_free;
+
+       hw = at91_clk_register_master_div(regmap, "mck0_div", "mck0_pres",
+                                         &mck0_layout, &mck0_characteristics,
+                                         &pmc_mck0_lock, 0);
        if (IS_ERR(hw))
                goto err_free;